1
0
forked from erp-dev/erp
Files
erpnew/docs/settlement/Service.md

187 lines
4.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Settlement Service 设计文档
## 概述
本文档描述 settlement 模块的 service 层设计,遵循职责分离原则,每个函数职责单一、可测试。
## 函数列表
### 1. `get_plate_order_summary_by_customer`
获取按客户分组的开版订单统计。
**参数**:
- `merchant_id: int` - 商户ID
- `settlement_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` - 商户ID
- `user: 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数据)
```
## 过滤规则
1. **商户隔离**: 只查询指定商户的订单
2. **plate_type 过滤**: `plate_type` 为空的订单不纳入统计
3. **production_method 过滤**: `production_method` 为空的订单不纳入统计
4. **客户可见性**: 非超级用户只能看到自己创建的客户或被授权可见的客户
5. **全0数据过滤**: today 和 current_month 都为 0 的类型组合不返回
6. **客户分组稳健性**: 以 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`: 集成测试
## 性能优化
1. 使用 `select_related` 减少查询次数
2. 使用 `annotate` 和聚合函数避免 N+1 查询
3. 使用条件聚合Case/When一次查询获取 today 和 current_month
## PostgreSQL 优化评估记录
1. 已评估数据库层优化(索引、视图、物化视图)
2. 当前决策:暂不实施结构性优化,优先保持线上稳定性
3. 后续若优化,优先级为:索引 > 表达式索引 > 物化视图
4. 生产约束:索引变更需使用并发建索引方式并在低峰执行