""" 企业管理员代理路由 把订单、消费、发票、审核、日志、模型等模块代理到企业管理员认证, 并在 service 层加 tenant_id 过滤。 """ from typing import Optional from datetime import date from fastapi import APIRouter, Depends, HTTPException, Query, Request, status from sqlalchemy.orm import Session from app.database import get_db from app.dependencies.enterprise_auth import get_current_enterprise_admin from app.models.tenant import EnterpriseAdmin from app.models.user import User router = APIRouter(prefix="/api/enterprise-admin", tags=["企业管理员-代理"]) def _tenant_user_ids(db: Session, tenant_id: int): return db.query(User.id).filter(User.tenant_id == tenant_id).all() # ==================== 订单管理 ==================== @router.get("/orders") def list_orders( keyword: str = Query(None), status_filter: str = Query(None, alias="status"), payment_method: str = Query(None), start_date: str = Query(None), end_date: str = Query(None), amount_min: float = Query(None), amount_max: float = Query(None), sort_by: str = Query("created_at"), sort_order: str = Query("desc"), page: int = Query(1, ge=1), size: int = Query(20, ge=1, le=100), current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.admin_order_service import AdminOrderService, AdminOrderException from app.schemas.admin_order_schema import OrderListParams params = OrderListParams( keyword=keyword, status=status_filter, payment_method=payment_method, start_date=start_date, end_date=end_date, amount_min=amount_min, amount_max=amount_max, sort_by=sort_by, sort_order=sort_order, page=page, size=size ) service = AdminOrderService(db) orders, total = service.list_orders(params, tenant_id=current_admin.tenant_id) return {"code": 0, "message": "success", "data": { "items": [o.model_dump() for o in orders], "total": total, "page": page, "size": size, "pages": (total + size - 1) // size }} @router.get("/orders/export") def export_orders( keyword: str = Query(None), status_filter: str = Query(None, alias="status"), start_date: str = Query(None), end_date: str = Query(None), current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.admin_order_service import AdminOrderService from app.schemas.admin_order_schema import OrderListParams params = OrderListParams(keyword=keyword, status=status_filter, start_date=start_date, end_date=end_date, page=1, size=10000) service = AdminOrderService(db) url = service.export_orders(params) return {"code": 0, "data": {"download_url": url}} @router.get("/orders/{order_id}") def get_order_detail( order_id: int, current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.admin_order_service import AdminOrderService, AdminOrderException service = AdminOrderService(db) order = service.get_order_detail(order_id) # 验证订单属于本企业 user = db.query(User).filter(User.id == order.user_id, User.tenant_id == current_admin.tenant_id).first() if not user: raise HTTPException(status_code=404, detail={"code": "NOT_FOUND", "message": "订单不存在"}) return {"code": 0, "data": order.model_dump()} @router.post("/orders/{order_id}/confirm") def confirm_order( order_id: int, data: dict, request: Request, current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.admin_order_service import AdminOrderService service = AdminOrderService(db) service.confirm_order(order_id, current_admin.id, data.get("reason", ""), request.client.host if request.client else "") return {"code": 0, "message": "确认成功"} @router.post("/orders/{order_id}/refund") def refund_order( order_id: int, data: dict, request: Request, current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.admin_order_service import AdminOrderService service = AdminOrderService(db) service.refund_order(order_id, current_admin.id, data.get("reason", ""), request.client.host if request.client else "") return {"code": 0, "message": "退款成功"} # ==================== 账单管理 ==================== @router.get("/bills") def list_bills( keyword: str = Query(None), biz_type: str = Query(None), module: str = Query(None), start_date: str = Query(None), end_date: str = Query(None), sort_by: str = Query("created_at"), sort_order: str = Query("desc"), page: int = Query(1, ge=1), size: int = Query(20, ge=1, le=100), current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.admin_bill_service import AdminBillService from app.schemas.admin_order_schema import BillListParams params = BillListParams(keyword=keyword, biz_type=biz_type, module=module, start_date=start_date, end_date=end_date, sort_by=sort_by, sort_order=sort_order, page=page, size=size) service = AdminBillService(db) bills, total = service.list_bills(params, tenant_id=current_admin.tenant_id) return {"code": 0, "data": { "items": [b.model_dump() for b in bills], "total": total, "page": page, "size": size, "pages": (total + size - 1) // size }} # ==================== 消费记录 ==================== @router.get("/consumptions") def list_consumptions( user_id: str = Query(None), order_no: str = Query(None), model_name: str = Query(None), start_date: date = Query(None), end_date: date = Query(None), invoiced: bool = Query(None), page: int = Query(1, ge=1), size: int = Query(20, ge=1, le=100), current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.admin_consumption_service import AdminConsumptionService from app.schemas.admin_consumption_invoice_schema import ConsumptionListParams params = ConsumptionListParams(user_id=user_id, order_no=order_no, model_name=model_name, start_date=start_date, end_date=end_date, invoiced=invoiced, page=page, size=size) service = AdminConsumptionService(db) items, total = service.list_consumptions(params, tenant_id=current_admin.tenant_id) return {"code": 0, "data": { "items": [i.model_dump() for i in items], "total": total, "page": page, "size": size, "pages": (total + size - 1) // size }} # ==================== 发票管理 ==================== @router.get("/invoices") def list_invoices( user_id: str = Query(None), inv_status: str = Query(None, alias="status"), invoice_type: str = Query(None), start_date: date = Query(None), end_date: date = Query(None), page: int = Query(1, ge=1), size: int = Query(20, ge=1, le=100), current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.admin_invoice_service import AdminInvoiceService from app.schemas.admin_consumption_invoice_schema import InvoiceListParams params = InvoiceListParams(user_id=user_id, status=inv_status, invoice_type=invoice_type, start_date=start_date, end_date=end_date, page=page, size=size) service = AdminInvoiceService(db) items, total = service.list_invoices(params, tenant_id=current_admin.tenant_id) return {"code": 0, "data": { "items": [i.model_dump() for i in items], "total": total, "page": page, "size": size, "pages": (total + size - 1) // size }} @router.get("/invoices/{invoice_id}") def get_invoice_detail( invoice_id: int, current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.admin_invoice_service import AdminInvoiceService service = AdminInvoiceService(db) detail = service.get_invoice_detail(invoice_id) return {"code": 0, "data": detail.model_dump()} @router.post("/invoices/{invoice_id}/approve") def approve_invoice( invoice_id: int, request: Request, current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.admin_invoice_service import AdminInvoiceService service = AdminInvoiceService(db) service.approve_invoice(invoice_id, current_admin.id, request.client.host if request.client else "") return {"code": 0, "message": "审核通过"} @router.post("/invoices/{invoice_id}/reject") def reject_invoice( invoice_id: int, data: dict, request: Request, current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.admin_invoice_service import AdminInvoiceService service = AdminInvoiceService(db) service.reject_invoice(invoice_id, current_admin.id, data.get("reason", ""), request.client.host if request.client else "") return {"code": 0, "message": "驳回成功"} # ==================== 内容审核 ==================== @router.get("/review/pictures") def get_pictures( rev_status: str = Query(None, alias="status"), page: int = Query(1, ge=1), size: int = Query(20, ge=1, le=100), current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.review_service import ReviewService service = ReviewService(db) result = service.get_picture_review_list(rev_status, None, None, None, page, size, tenant_id=current_admin.tenant_id) return {"code": 0, "data": result} @router.get("/review/videos") def get_videos( rev_status: str = Query(None, alias="status"), page: int = Query(1, ge=1), size: int = Query(20, ge=1, le=100), current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.review_service import ReviewService service = ReviewService(db) result = service.get_video_review_list(rev_status, None, None, None, page, size, tenant_id=current_admin.tenant_id) return {"code": 0, "data": result} @router.get("/review/audios") def get_audios( rev_status: str = Query(None, alias="status"), page: int = Query(1, ge=1), size: int = Query(20, ge=1, le=100), current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.review_service import ReviewService service = ReviewService(db) result = service.get_audio_review_list(rev_status, None, None, None, page, size, tenant_id=current_admin.tenant_id) return {"code": 0, "data": result} @router.post("/review/{content_type}/{content_id}/approve") def approve_content( content_type: str, content_id: int, action: dict = {}, current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.review_service import ReviewService service = ReviewService(db) service.approve_content(content_type, content_id, current_admin.id, action.get("remark")) return {"code": 0, "message": "审核通过"} @router.post("/review/{content_type}/{content_id}/reject") def reject_content( content_type: str, content_id: int, action: dict, current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.review_service import ReviewService service = ReviewService(db) service.reject_content(content_type, content_id, current_admin.id, action.get("reason", ""), action.get("delete_content", False), action.get("ban_user", False)) return {"code": 0, "message": "审核拒绝"} # ==================== 日志审计 ==================== @router.get("/logs/api") def list_api_logs( user_id: str = Query(None), username: str = Query(None), phone: str = Query(None), module: str = Query(None), api_path: str = Query(None), start_date: str = Query(None), end_date: str = Query(None), page: int = Query(1, ge=1), size: int = Query(20, ge=1, le=100), current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.log_service import LogService service = LogService(db) result = service.get_api_logs(start_date=start_date, end_date=end_date, user_id=user_id, username=username, phone=phone, module=module, api_path=api_path, page=page, size=size, tenant_id=current_admin.tenant_id) return {"code": 0, "data": result} @router.get("/logs/logins") def list_login_logs( user_type: str = Query(None), login_result: str = Query(None), start_date: str = Query(None), end_date: str = Query(None), page: int = Query(1, ge=1), size: int = Query(20, ge=1, le=100), current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.log_service import LogService service = LogService(db) result = service.get_login_logs(start_date=start_date, end_date=end_date, user_type=user_type, login_result=login_result, page=page, size=size, tenant_id=current_admin.tenant_id) return {"code": 0, "data": result} # ==================== 模型管理(只读,不隔离) ==================== @router.get("/models") def list_models( keyword: str = Query(None), category: str = Query(None), is_show_enabled: bool = Query(None), page: int = Query(1, ge=1), size: int = Query(20, ge=1, le=100), current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.admin_model_service import AdminModelService from app.schemas.admin_schema import ModelListParams params = ModelListParams(keyword=keyword, category=category, is_show_enabled=is_show_enabled, page=page, size=size) service = AdminModelService(db) models, total = service.list_models(params) return {"code": 0, "data": { "items": [m.model_dump() for m in models], "total": total, "page": page, "size": size }} @router.get("/models/local") def list_local_models( keyword: str = Query(None), page: int = Query(1, ge=1), size: int = Query(20, ge=1, le=100), current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.models.model import ModelNew from app.models.tenant import Tenant from sqlalchemy import or_, desc # 检查企业是否开启了私域模型功能 tenant = db.query(Tenant).filter(Tenant.id == current_admin.tenant_id).first() if tenant and not getattr(tenant, "enable_local_model", True): raise HTTPException(status_code=403, detail="该企业未开启私域模型功能") query = db.query(ModelNew).filter( ModelNew.is_local == True, ModelNew.tenant_id == current_admin.tenant_id ) if keyword: kw = f"%{keyword}%" query = query.filter(or_(ModelNew.model_code.ilike(kw), ModelNew.display_name.ilike(kw))) total = query.count() models = query.order_by(desc(ModelNew.created_at)).offset((page - 1) * size).limit(size).all() items = [{"id": m.id, "model_code": m.model_code, "title": m.display_name, "visibility": m.visibility, "user_id": m.user_id, "tenant_id": m.tenant_id, "created_at": m.created_at.isoformat() if m.created_at else None} for m in models] return {"code": 0, "data": {"items": items, "total": total, "page": page, "size": size}} # ==================== 系统配置 ==================== @router.get("/config/branding") def get_branding_config( current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): """获取品牌配置,优先返回企业自己设置的值,没有则 fallback 到全局默认""" from app.models.config import SystemConfig import json as _json KEYS = ["system_name", "system_logo", "icp_number"] DEFAULTS = {"system_name": "智创空间", "system_logo": "", "icp_number": ""} result = {} for key in KEYS: # 先查企业自己的配置 row = db.query(SystemConfig).filter( SystemConfig.tenant_id == current_admin.tenant_id, SystemConfig.config_key == key ).first() if row: try: result[key] = _json.loads(row.config_value) except Exception: result[key] = row.config_value continue # fallback 到全局 row = db.query(SystemConfig).filter( SystemConfig.tenant_id == None, SystemConfig.config_key == key ).first() if row: try: result[key] = _json.loads(row.config_value) except Exception: result[key] = row.config_value else: result[key] = DEFAULTS[key] return {"code": 0, "data": result} @router.get("/config") def get_config( current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.config_service import ConfigService service = ConfigService(db, tenant_id=current_admin.tenant_id) return service.get_all_configs() @router.put("/config") def update_config( update_data: dict, current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.config_service import ConfigService from app.schemas.config_schema import ConfigUpdate service = ConfigService(db, tenant_id=current_admin.tenant_id) configs = [ConfigUpdate(**c) for c in update_data.get("configs", [])] service.update_configs(configs, current_admin.id) return {"message": "配置更新成功", "data": None} @router.get("/config/{config_key}/history") def get_config_history( config_key: str, page: int = Query(1, ge=1), size: int = Query(20, ge=1, le=100), current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.config_service import ConfigService service = ConfigService(db, tenant_id=current_admin.tenant_id) history = service.get_config_history(config_key, page, size) return {"data": history} @router.post("/config/{config_key}/reset") def reset_config( config_key: str, current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.config_service import ConfigService service = ConfigService(db, tenant_id=current_admin.tenant_id) service.reset_config(config_key, current_admin.id) return {"message": "配置重置成功", "data": None} @router.get("/local-config") def get_local_config( current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), ): from app.services import local_config_service from app.schemas.local_config_schema import LocalConfigResponse result = local_config_service.get_all() return LocalConfigResponse(data=result) # ==================== 模型写操作 ==================== @router.post("/models") def create_model( data: dict, request: Request, current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.admin_model_service import AdminModelService from app.schemas.admin_schema import ModelCreateRequest service = AdminModelService(db) req = ModelCreateRequest(**data) model_id = service.create_model(req) return {"message": "模型创建成功", "model_id": model_id} @router.put("/models/{model_id}") def update_model( model_id: int, data: dict, request: Request, current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.admin_model_service import AdminModelService from app.schemas.admin_schema import ModelUpdateRequest service = AdminModelService(db) req = ModelUpdateRequest(**data) service.update_model(model_id, req) return {"message": "模型更新成功", "model_id": model_id} @router.put("/models/{model_id}/price") def update_model_price( model_id: int, data: dict, request: Request, current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.admin_model_service import AdminModelService from app.schemas.admin_schema import ModelPriceRequest service = AdminModelService(db) req = ModelPriceRequest(**data) service.update_model_price(model_id, req) return {"message": "价格更新成功", "model_id": model_id} @router.put("/models/{model_id}/status") def update_model_status( model_id: int, data: dict, request: Request, current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.admin_model_service import AdminModelService from app.schemas.admin_schema import ModelStatusRequest service = AdminModelService(db) req = ModelStatusRequest(**data) service.update_model_status(model_id, req.field, req.value) return {"message": "状态更新成功", "model_id": model_id, req.field: req.value} # ==================== 企业私域模型创建/删除/测试 ==================== @router.post("/models/local/test") async def test_local_model_connection( data: dict, current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): """企业管理员测试私域模型连接""" from app.services.local_model_service import LocalModelService from app.schemas.local_model import ConnectionTestRequest service = LocalModelService(db) categories = data.get('categories') or [0] result = await service.test_connection( data.get('base_url', ''), data.get('api_key'), data.get('model_name'), categories[0] if categories else 0, ) return result @router.put("/models/local/{model_id}") async def update_local_model( model_id: int, data: dict, current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): """企业管理员更新本企业私域模型""" from app.models.model import ModelNew from app.services.crypto_utils import encrypt_api_key model = db.query(ModelNew).filter( ModelNew.id == model_id, ModelNew.is_local == True, ModelNew.tenant_id == current_admin.tenant_id, ).first() if not model: raise HTTPException(status_code=404, detail="模型不存在") if 'name' in data: model.display_name = data['name'] if 'supplier' in data: model.supplier = data['supplier'] if 'base_url' in data: model.base_url = data['base_url'] if 'api_key' in data and data['api_key']: model.local_api_key = encrypt_api_key(data['api_key']) if 'categories' in data: model.categories = data['categories'] if 'visibility' in data: model.visibility = data['visibility'] db.commit() return {"code": 0, "message": "更新成功"} @router.post("/models/local") async def create_local_model( data: dict, current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): """企业管理员创建私域模型,自动绑定 tenant_id""" from app.services.local_model_service import LocalModelService import time from app.models.model import ModelNew from app.models.tenant import Tenant from app.services.crypto_utils import encrypt_api_key # 检查企业是否开启了私域模型功能 tenant = db.query(Tenant).filter(Tenant.id == current_admin.tenant_id).first() if tenant and not getattr(tenant, "enable_local_model", True): raise HTTPException(status_code=403, detail="该企业未开启私域模型功能") base_url = data.get('base_url', '') if not base_url: raise HTTPException(status_code=400, detail="base_url 不能为空") service = LocalModelService(db) is_valid, err = service.validate_base_url(base_url) if not is_valid: raise HTTPException(status_code=400, detail=err) api_key = data.get('api_key') model_code = f"local_tenant_{current_admin.tenant_id}_{int(time.time() * 1000)}" model = ModelNew( model_code=model_code, display_name=data.get('name', model_code), supplier=data.get('supplier', 'Custom'), img="", categories=data.get('categories', [0]), is_local=True, user_id=None, tenant_id=current_admin.tenant_id, base_url=base_url, local_api_key=encrypt_api_key(api_key) if api_key else None, is_show_enabled=False, is_api_enabled=True, visibility=data.get('visibility', 'global'), ) db.add(model) db.commit() db.refresh(model) return {"code": 0, "message": "创建成功", "data": {"id": model.id, "model_code": model.model_code}} @router.delete("/models/local/{model_id}") def delete_enterprise_local_model( model_id: int, current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): """企业管理员删除本企业的私域模型""" from app.models.model import ModelNew model = db.query(ModelNew).filter( ModelNew.id == model_id, ModelNew.is_local == True, ModelNew.tenant_id == current_admin.tenant_id ).first() if not model: raise HTTPException(status_code=404, detail="模型不存在或无权限") db.delete(model) db.commit() return {"code": 0, "message": "删除成功"} # ==================== 本地模型写操作 ==================== @router.put("/local-models/{model_id}/visibility") def update_local_model_visibility( model_id: int, data: dict, request: Request, current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.models.model import ModelNew model = db.query(ModelNew).filter(ModelNew.id == model_id, ModelNew.is_local == True).first() if not model: raise HTTPException(status_code=404, detail="本地模型不存在") model.visibility = data.get("visibility", model.visibility) db.commit() return {"message": "可见性更新成功", "model_id": model_id, "visibility": model.visibility} @router.delete("/local-models/{model_id}") def delete_local_model( model_id: int, request: Request, current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.models.model import ModelNew model = db.query(ModelNew).filter(ModelNew.id == model_id, ModelNew.is_local == True).first() if not model: raise HTTPException(status_code=404, detail="本地模型不存在") db.delete(model) db.commit() return {"message": "本地模型已删除", "model_id": model_id} # ==================== 用户本地模型权限 ==================== @router.get("/users/{user_id}/local-model-permissions") def get_user_local_model_permissions( user_id: str, current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.user_local_model_permission_service import UserLocalModelPermissionService service = UserLocalModelPermissionService(db) return service.get_user_model_permissions(user_id) @router.put("/users/{user_id}/local-model-permissions/{model_id}") async def update_user_local_model_permission( user_id: str, model_id: int, data: dict, current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.user_local_model_permission_service import UserLocalModelPermissionService service = UserLocalModelPermissionService(db) success = await service.update_user_model_permission( user_id=user_id, model_id=model_id, has_access=data.get("has_access", False) ) if not success: raise HTTPException(status_code=400, detail="更新权限失败") return {"message": "权限更新成功"} @router.put("/users/{user_id}/local-model-permissions") async def update_user_all_local_model_permissions( user_id: str, data: dict, current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.user_local_model_permission_service import UserLocalModelPermissionService service = UserLocalModelPermissionService(db) success = await service.update_user_all_model_permissions( user_id=user_id, has_access=data.get("has_access", False) ) if not success: raise HTTPException(status_code=400, detail="更新权限失败") return {"message": "权限更新成功"} # ==================== 统计导出 ==================== @router.get("/stats/users/export") async def export_user_stats( start_date: date = Query(...), end_date: date = Query(...), current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.admin_stats_service import AdminStatsService service = AdminStatsService(db) file_url = await service.export_user_stats(start_date, end_date) return {"file_url": file_url} @router.get("/stats/revenue/export") async def export_revenue_stats( start_date: date = Query(...), end_date: date = Query(...), current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.admin_stats_service import AdminStatsService service = AdminStatsService(db) file_url = await service.export_revenue_stats(start_date, end_date) return {"file_url": file_url} @router.get("/stats/business/export") async def export_business_stats( start_date: date = Query(...), end_date: date = Query(...), current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db) ): from app.services.admin_stats_service import AdminStatsService service = AdminStatsService(db) file_url = await service.export_business_stats(start_date, end_date) return {"file_url": file_url} # ── 企业流水(租户隔离)──────────────────────────────────────────────────────── from app.models.tenant import TenantBalanceLog from sqlalchemy import desc as _desc_tbl from datetime import datetime, time as _time @router.get("/balance-logs") def list_tenant_balance_logs( biz_type: str = Query(None, description="recharge/consume/adjust"), start_date: date = Query(None), end_date: date = Query(None), page: int = Query(1, ge=1), size: int = Query(20, ge=1, le=100), current_admin: EnterpriseAdmin = Depends(get_current_enterprise_admin), db: Session = Depends(get_db), ): """查询本企业余额流水(租户隔离)""" tenant_id = current_admin.tenant_id q = db.query(TenantBalanceLog).filter(TenantBalanceLog.tenant_id == tenant_id) if biz_type: q = q.filter(TenantBalanceLog.biz_type == biz_type) if start_date: q = q.filter(TenantBalanceLog.created_at >= datetime.combine(start_date, _time.min)) if end_date: q = q.filter(TenantBalanceLog.created_at <= datetime.combine(end_date, _time.max)) total = q.count() logs = q.order_by(_desc_tbl(TenantBalanceLog.created_at)).offset((page - 1) * size).limit(size).all() items = [ { "id": l.id, "change_amount": float(l.change_amount), "balance_after": float(l.balance_after), "biz_type": l.biz_type, "biz_order_no": l.biz_order_no, "remark": l.remark, "created_at": l.created_at.isoformat() if l.created_at else None, } for l in logs ] # 累计统计(不受分页影响,基于当前筛选条件) from sqlalchemy import func as _func stats_q = db.query(TenantBalanceLog).filter(TenantBalanceLog.tenant_id == tenant_id) if biz_type: stats_q = stats_q.filter(TenantBalanceLog.biz_type == biz_type) if start_date: stats_q = stats_q.filter(TenantBalanceLog.created_at >= datetime.combine(start_date, _time.min)) if end_date: stats_q = stats_q.filter(TenantBalanceLog.created_at <= datetime.combine(end_date, _time.max)) total_recharge = float(stats_q.filter(TenantBalanceLog.change_amount > 0) .with_entities(_func.sum(TenantBalanceLog.change_amount)).scalar() or 0) total_consume = float(stats_q.filter(TenantBalanceLog.change_amount < 0) .with_entities(_func.sum(TenantBalanceLog.change_amount)).scalar() or 0) return { "items": items, "total": total, "page": page, "size": size, "total_recharge": total_recharge, "total_consume": total_consume, }