forked from erp-dev/erp
4.7 KiB
4.7 KiB
Settlement Service 设计文档
概述
本文档描述 settlement 模块的 service 层设计,遵循职责分离原则,每个函数职责单一、可测试。
函数列表
1. get_plate_order_summary_by_customer
获取按客户分组的开版订单统计。
参数:
merchant_id: int- 商户IDsettlement_date: datetime.date- 统计日期user: User | None- 当前用户(用于客户可见性过滤)
返回:
list[dict]- 客户统计列表
职责:
- 参数验证
- 调用底层数据查询函数
- 应用客户可见性过滤
- 数据格式化
参数校验规则:
merchant_id必须是正整数settlement_date必须是date或datetime类型(datetime会自动转换为date)- 参数非法时抛出
ValueError
2. _get_plate_order_queryset
获取开版订单的基础查询集。
参数:
merchant_id: int- 商户IDuser: User | None- 当前用户(用于客户可见性过滤)
返回:
QuerySet[PlateOrder]- 过滤后的查询集
职责:
- 应用商户隔离
- 应用客户可见性过滤
- 过滤
plate_type不为空的订单 - 过滤
production_method不为空的订单 - 优化查询(select_related)
3. _get_month_date_range
获取从月初到指定日期的日期范围。
参数:
settlement_date: datetime.date- 统计日期
返回:
tuple[date, date]- (月初日期, 统计日期)
职责:
- 计算当月第一天
- 返回日期范围元组
4. _aggregate_plate_orders_by_customer_and_type
按客户和类型分组聚合订单数据。
参数:
queryset: QuerySet[PlateOrder]- 基础查询集settlement_date: datetime.date- 统计日期
返回:
QuerySet[PlateOrder]- 添加了聚合标注的查询集
职责:
- 按 customer_id、customer_name、plate_type、production_method 分组
- 计算今日数量(条件聚合)
- 计算本月累计数量(条件聚合)
- 生成 type 字段(plate_type + '-' + production_method)
5. _format_plate_order_summary
格式化聚合结果为 API 返回格式。
参数:
aggregated_data: QuerySet[PlateOrder]- 聚合后的查询集
返回:
list[dict]- 格式化后的数据
职责:
- 遍历聚合结果
- 按客户分组
- 返回客户ID(
client_id)和客户名称(client_name) - 过滤全0数据
- 生成最终的 API 返回格式
6. _filter_zero_data
过滤全0数据。
参数:
plate_order_counts: list[dict]- 订单统计列表
返回:
list[dict]- 过滤后的列表
职责:
- 移除 today 和 current_month 都为 0 的数据
数据流程
get_plate_order_summary_by_customer
↓
_get_plate_order_queryset (获取基础查询集)
↓
_aggregate_plate_orders_by_customer_and_type (分组聚合)
↓
_get_month_date_range (获取日期范围)
↓
_format_plate_order_summary (格式化结果)
↓
_filter_zero_data (过滤全0数据)
过滤规则
- 商户隔离: 只查询指定商户的订单
- plate_type 过滤:
plate_type为空的订单不纳入统计 - production_method 过滤:
production_method为空的订单不纳入统计 - 客户可见性: 非超级用户只能看到自己创建的客户或被授权可见的客户
- 全0数据过滤: today 和 current_month 都为 0 的类型组合不返回
- 客户分组稳健性: 以 customer_id 分组,避免同名客户被合并
type 组合规则
格式: {plate_type}-{production_method}
plate_type 可能的值:
首版修改
production_method 可能的值:
定位匹布
组合示例:
首版-定位首版-匹布修改-定位修改-匹布
测试策略
每个函数都应该有独立的单元测试:
_get_plate_order_queryset: 测试商户隔离、客户可见性过滤、plate_type 和 production_method 过滤_get_month_date_range: 测试日期范围计算_aggregate_plate_orders_by_customer_and_type: 测试分组聚合逻辑_format_plate_order_summary: 测试数据格式化_filter_zero_data: 测试全0数据过滤get_plate_order_summary_by_customer: 集成测试
性能优化
- 使用
select_related减少查询次数 - 使用
annotate和聚合函数避免 N+1 查询 - 使用条件聚合(Case/When)一次查询获取 today 和 current_month
PostgreSQL 优化评估记录
- 已评估数据库层优化(索引、视图、物化视图)
- 当前决策:暂不实施结构性优化,优先保持线上稳定性
- 后续若优化,优先级为:索引 > 表达式索引 > 物化视图
- 生产约束:索引变更需使用并发建索引方式并在低峰执行