1
0
forked from erp-dev/erp

feat: improve for report

This commit is contained in:
2026-01-16 17:25:21 +08:00
parent 0c24c7e7e9
commit 2e0b7dc335
6 changed files with 417 additions and 73 deletions

View File

@@ -0,0 +1,51 @@
# 2026-01-16 工作日志
## 背景
需要在 `stateflow` 模块提供一种通用查询能力按“已完成的状态节点state_id+ 工艺参数 key/value”反查相关的 `BusinessObject` 列表。
典型场景:查找所有 **已完成**“画图中”节点且工艺参数“设计师名称”为“AAA”的业务对象并可通过 `content_type/object_id` 追溯到实际业务对象,如 `printing.plateorder`)。
---
## 今日任务
-`GET /api/v1/stateflow/business-objects/` 增加过滤条件:`state_id` + `param_key` + `param_value`
- 增加 PostgreSQL JSONB 查询性能优化:为 `StateLogParameterRecord.parameters` 添加 GIN`jsonb_path_ops`)索引
- 补齐/更新测试用例,验证过滤行为与撤销记录处理
---
## 今日完成
### 1) BusinessObject 查询能力增强(按已完成节点 + 工艺参数过滤)
`api_v1/views/stateflow/business_object.py``BusinessObjectFilterSet` 中新增过滤参数:
- `state_id`:状态节点 ID表示“已完成该节点”仅匹配 `StateFlowRecord.is_cancelled=False`
- `param_key`:工艺参数 key
- `param_value`:工艺参数 value
典型用法(示例:查 `printing.plateorder` 类型):
- `GET /api/v1/stateflow/business-objects/?content_type_str=printing.plateorder&state_id=<id>&param_key=设计师名称&param_value=AAA&limit=500&offset=0`
实现方式使用 `Exists(OuterRef)` 子查询,避免 join + distinct 的重复行问题,性能更稳定。
### 2) PostgreSQL JSONB 索引优化
新增迁移:`stateflow/migrations/0022_statelogparameterrecord_parameters_gin_index.py`
-`StateLogParameterRecord.parameters` 增加 `GIN(jsonb_path_ops)` 索引
- 主要加速 `parameters__contains={key: value}`(即 `jsonb @> ...`)这类查询
### 3) 测试补齐
`stateflow/tests/test_business_object_crud_and_filters_api.py` 增加用例,覆盖:
- 命中:已完成节点 + 参数匹配
- 不命中:参数值不一致
- 排除:已撤销的 `StateFlowRecord` 不参与匹配
另外,为保证 `stateflow` 测试集可持续运行,恢复了 `stateflow.services.clone_business_object()` 的实现(此前曾临时禁用导致测试失败)。

View File

@@ -0,0 +1,169 @@
# Stateflow按“已完成状态节点 + 工艺参数”查询 BusinessObjectV1
## 用途/背景
`stateflow` 中,业务流程实例由 `BusinessObject` 表示;每次完成某个状态节点会生成一条 `StateFlowRecord`(状态流转记录),并可在该记录下写入一条或多条 `StateLogParameterRecord.parameters`JSON 工艺参数记录)。
本接口用于在 **不依赖具体业务模块**(如 printing/shipment的情况下按以下条件反查 `BusinessObject` 列表:
- 已完成某个状态节点(`state_id`
- 且在该次完成该节点的日志下,存在工艺参数 `param_key=param_value`
典型场景:
- 找到所有 **已完成**“画图中”节点且“设计师名称”为“AAA”的 `BusinessObject`
- 再通过 `content_type/object_id` 追溯到实际业务对象(如 `printing.plateorder`
---
## 接口定义
- **Method**: `GET`
- **Path**: `/api/v1/stateflow/business-objects/`
- **Auth**: `IsAuthenticated`
- **Pagination**: `LimitOffsetPagination`(字段:`count / next / previous / results`
---
## 查询参数Query Params
### 必填组合(本能力的核心)
当你希望启用“按已完成节点 + 参数”过滤时,下面三个参数必须 **同时提供**
- **state_id**: `int`
- 语义:已完成该状态节点(仅匹配 `StateFlowRecord.is_cancelled=False`
- **param_key**: `string`
- 语义:工艺参数键(来自 `StateLogParameterRecord.parameters` 的 key
- **param_value**: `string`
- 语义:工艺参数值(来自 `StateLogParameterRecord.parameters` 的 value
**重要约束**
- 如果提供了 `state_id`,但缺少 `param_key``param_value`,出于避免“误放宽查询范围”的考虑,接口会 **返回空结果**`count=0`)。
### 常用配套过滤(推荐一起使用)
- **content_type_str**: `app_label.model`
- 示例:`printing.plateorder``auth.user`
- 用途:将查询限定到某类业务对象,避免在全表上做参数查询
### 其他已有过滤/排序(原有能力)
以下能力来自 `BusinessObjectFilterSet` 与 DRF 内置 backends
- **name**: 名称模糊搜索icontains
- **process**: 按流程 ID 过滤
- **process_name**: 流程名称模糊搜索icontains
- **overall_status**: `in_progress` / `completed`
- **has_content_object**: `true/false`
- **search**: 全文搜索(`name/description`
- **ordering**: 排序字段(`id/name/created_at/updated_at`),默认 `-created_at`
- **limit / offset**: 分页参数
---
## 过滤语义说明(非常重要)
### 1) “处于某状态”的含义
本接口中的“处于某状态”指:
- **已经完成**该状态节点(存在 `StateFlowRecord(state_id=..., is_cancelled=False)`
而不是“当前正在该节点”(当前节点在系统中是通过流程推导得到,不是直接落库字段)。
### 2) 参数匹配的绑定范围
参数匹配是 **绑定在同一次完成该节点的 state_log 下**
- `StateFlowRecord(state_id=画图中)` 这条记录下
- 存在某条 `StateLogParameterRecord.parameters` 包含 `{param_key: param_value}`
换句话说:不会使用 “整个 BusinessObject 的所有参数汇总” 来做匹配。
### 3) 已撤销记录的处理
用于过滤的状态完成记录会排除撤销状态:
- 仅匹配 `StateFlowRecord.is_cancelled=False`
- 如果某条记录被 `step_back` 撤销,则不会参与命中
---
## 返回结构BusinessObject list
成功响应为分页结构:
- **count**: 总数
- **next/previous**: 翻页链接
- **results**: 列表项(每项为 `BusinessObjectListSerializer` 输出)
`results[*]` 字段(与当前实现一致):
- **id**: BO ID
- **name**: BO 名称
- **process**: 流程 ID
- **process_name**: 流程名称
- **current_state_name**: 当前状态名称(按系统 current_state 策略推导)
- **overall_status**: `in_progress` / `completed`
- **progress_percentage**: 进度百分比(浮点数,已 round
- **content_type**: ContentType ID可空
- **object_id**: 关联对象 ID可空
- **content_type_name**: `app_label.model` 字符串(可空)
- **description**: 描述
- **created_at / updated_at**: 时间戳
---
## 示例
### 示例 1查找“画图中 + 设计师=AAA”的开版单plateorder
请求:
```http
GET /api/v1/stateflow/business-objects/?content_type_str=printing.plateorder&state_id=123&param_key=&param_value=AAA&limit=50&offset=0
Authorization: Bearer <token>
```
### 示例 2只按类型过滤不启用 state+param
请求:
```http
GET /api/v1/stateflow/business-objects/?content_type_str=printing.plateorder&limit=50&offset=0
Authorization: Bearer <token>
```
### 示例 3错误用法只给 state_id
由于缺少 `param_key/param_value`,会返回空结果(`count=0`
```http
GET /api/v1/stateflow/business-objects/?state_id=123&limit=50&offset=0
Authorization: Bearer <token>
```
---
## 错误响应
- **401 Unauthorized**:未登录
- 其它错误码沿用现有 `BusinessObjectViewSet` 行为(本过滤能力本身不额外引入新的 400 约束;缺参会返回空列表)
---
## 性能/索引建议JSONB
本查询的关键条件是:
- `StateLogParameterRecord.parameters` 的 JSON 包含匹配Django`parameters__contains={key: value}`
- PostgreSQL 对应:`parameters @> '{"key":"value"}'::jsonb`
为提升性能,项目已为 `StateLogParameterRecord.parameters` 增加:
- **GIN(jsonb_path_ops)** 索引(迁移:`stateflow/migrations/0022_statelogparameterrecord_parameters_gin_index.py`
建议:
- 线上启用前确保已执行迁移
- 数据量较大时可用 `EXPLAIN (ANALYZE, BUFFERS)` 验证查询是否命中 GIN 索引