""" 业务员工作台路由模块 提供业务员个人工作台的概览数据接口,包括: - 订单概览(最近订单列表) - 提醒概览(待处理提醒列表) - 汇总统计(订单数量、提醒数量) - 销售统计(日/周/月销售额统计) URL 前缀:/api/salesman 权限要求:salesman/admin 角色 """ from datetime import datetime, timedelta from fastapi import APIRouter, Depends, Query from sqlalchemy import func from sqlalchemy.orm import Session from backend.app.api.deps import require_permissions, require_roles from backend.app.db import get_db_session from backend.app.core.cache import cache_delete_pattern, cache_get, cache_set from backend.app.core.error_codes import ErrorCode from backend.app.core.exceptions import AppException from backend.app.models.business import SalesOrder from backend.app.schemas.common import success_payload from backend.app.services.reminder_service import reminder_service from backend.app.services.order_service import order_service router = APIRouter(prefix="/api/salesman", tags=["salesman"]) @router.get("/dashboard") def salesman_dashboard( session: Session = Depends(get_db_session), # 数据库会话 current_user: dict = Depends(require_roles("salesman", "admin")), # 当前登录用户,仅salesman和admin可访问 _permission_user: dict = Depends(require_permissions("order:list")), # 权限校验:订单列表查看权限 ) -> dict: """ 获取业务员工作台概览数据 返回当前业务员的订单列表(最近20条)、提醒列表及汇总统计信息。 业务员只能查看自己的数据,管理员可查看所有。 返回值: 包含以下字段的字典: - orders: 最近20条订单列表 - reminders: 提醒列表 - summary: 汇总数据(order_count 订单总数、reminder_count 提醒总数) 权限要求:salesman、admin 角色,需 order:list 权限 """ user_id = current_user.get("user_id") cache_key = f"dashboard:{user_id}" cached = cache_get(cache_key) if cached is not None: return success_payload(cached) orders_result = order_service.list_orders(session, {"salesman_id": user_id}) orders = orders_result.get("list", []) reminders = reminder_service.list_reminders(session, {"receiver_user_id": user_id}) result = { "orders": orders[:20], "reminders": reminders.get("list", []) if isinstance(reminders, dict) else [], "summary": { "order_count": orders_result.get("total", 0), "reminder_count": len(reminders.get("list", [])) if isinstance(reminders, dict) else 0, }, } cache_set(cache_key, result, ttl=30) return success_payload(result) @router.get("/statistics") def salesman_statistics( period: str = Query(default="week", regex="^(day|week|month)$"), session: Session = Depends(get_db_session), current_user: dict = Depends(require_roles("salesman", "admin")), _permission_user: dict = Depends(require_permissions("order:list")), ) -> dict: """ 获取业务员销售统计数据 根据指定的时间周期(日/周/月)统计销售额、订单数、客单价等数据。 参数: period: 统计周期,可选值:day(今日)、week(本周)、month(本月) 返回值: 包含以下字段的字典: - total_sales: 总销售额 - order_count: 订单数 - avg_order_amount: 客单价 - daily_breakdown: 每日销售明细 - top_customers: TOP客户排名 权限要求:salesman、admin 角色,需 order:list 权限 """ user_id = current_user.get("user_id") cache_key = f"salesman:statistics:{user_id}:{period}" cached = cache_get(cache_key) if cached is not None: return success_payload(cached) # 计算时间范围 now = datetime.now() if period == "day": start_date = now.replace(hour=0, minute=0, second=0, microsecond=0) elif period == "week": start_date = now - timedelta(days=now.weekday()) start_date = start_date.replace(hour=0, minute=0, second=0, microsecond=0) else: # month start_date = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0) # 查询订单数据 base_query = session.query(SalesOrder).filter( SalesOrder.salesman_id == user_id, SalesOrder.order_status.notin_(["canceled", "draft"]), SalesOrder.created_at >= start_date, ) # 汇总统计 total_sales = base_query.with_entities(func.sum(SalesOrder.sale_price_total)).scalar() or 0 order_count = base_query.count() avg_order_amount = total_sales / order_count if order_count > 0 else 0 # 每日明细 daily_query = session.query( func.date(SalesOrder.created_at).label("date"), func.sum(SalesOrder.sale_price_total).label("sales"), func.count(SalesOrder.id).label("count"), ).filter( SalesOrder.salesman_id == user_id, SalesOrder.order_status.notin_(["canceled", "draft"]), SalesOrder.created_at >= start_date, ).group_by(func.date(SalesOrder.created_at)).order_by(func.date(SalesOrder.created_at)) daily_breakdown = [ {"date": str(row.date), "sales": float(row.sales or 0), "count": row.count} for row in daily_query.all() ] # TOP客户 top_customers_query = session.query( SalesOrder.customer_name, func.sum(SalesOrder.sale_price_total).label("sales"), func.count(SalesOrder.id).label("count"), ).filter( SalesOrder.salesman_id == user_id, SalesOrder.order_status.notin_(["canceled", "draft"]), SalesOrder.created_at >= start_date, ).group_by(SalesOrder.customer_name).order_by(func.sum(SalesOrder.sale_price_total).desc()).limit(10) top_customers = [ {"customer_name": row.customer_name, "sales": float(row.sales or 0), "count": row.count} for row in top_customers_query.all() ] result = { "period": period, "total_sales": round(float(total_sales), 2), "order_count": order_count, "avg_order_amount": round(float(avg_order_amount), 2), "daily_breakdown": daily_breakdown, "top_customers": top_customers, } cache_set(cache_key, result, ttl=60) return success_payload(result)