forked from erp-dev/erp
feat: new modul (stateflow)
This commit is contained in:
284
stateflow/README.md
Normal file
284
stateflow/README.md
Normal file
@@ -0,0 +1,284 @@
|
||||
# Stateflow 状态流转系统
|
||||
|
||||
## 架构概述
|
||||
|
||||
Stateflow 是一个基于日志的订单状态流转管理系统,采用服务层模式实现业务逻辑与数据访问的分离。
|
||||
|
||||
### 核心概念
|
||||
|
||||
- **State (状态)**: 流程中的一个节点,可以附带参数
|
||||
- **Process (流程模板)**: 由多个有序状态组成的流程定义
|
||||
- **Order (订单实例)**: 流程的具体执行实例
|
||||
- **OrderStateLog (完成日志)**: 记录订单完成每个状态的时间点和操作人
|
||||
|
||||
## 数据模型
|
||||
|
||||
### State
|
||||
```python
|
||||
State(name, parameters=[StateParameter(...)])
|
||||
```
|
||||
- 状态节点定义
|
||||
- 支持附加参数(键值对)
|
||||
|
||||
### Process
|
||||
```python
|
||||
Process(name, state_nodes=ManyToMany[ProcessNode])
|
||||
```
|
||||
- 流程模板,定义状态流转顺序
|
||||
- 通过 `ProcessNode` 中间表关联 `State`,支持排序
|
||||
|
||||
### ProcessNode
|
||||
```python
|
||||
ProcessNode(process, state, order)
|
||||
```
|
||||
- Process 和 State 的多对多关联表
|
||||
- `order` 字段定义状态在流程中的执行顺序
|
||||
|
||||
### Order
|
||||
```python
|
||||
Order(process, metadata={})
|
||||
```
|
||||
- 流程的具体实例
|
||||
- **不存储** `current_state` 字段
|
||||
- 当前状态通过查询 `OrderStateLog` 推导
|
||||
|
||||
### OrderStateLog
|
||||
```python
|
||||
OrderStateLog(order, state, completed_at, completed_by, is_cancelled, cancelled_at)
|
||||
```
|
||||
- 记录订单完成某个状态的日志
|
||||
- `completed_at`: 完成时间
|
||||
- `completed_by`: 操作人
|
||||
- `is_cancelled`: 是否已撤销
|
||||
- `cancelled_at`: 撤销时间
|
||||
- 唯一约束: `(order, state)` - 每个状态只能有一条记录
|
||||
- **不允许删除**,重置进度时只标记为已撤销
|
||||
|
||||
## 核心逻辑
|
||||
|
||||
### 状态推导规则
|
||||
|
||||
当前状态的推导遵循以下规则:
|
||||
|
||||
1. **未开始**: 没有任何有效完成日志(或所有记录都被撤销) → 返回 `None`
|
||||
2. **进行中**: 有部分完成日志 → 返回下一个未完成的状态
|
||||
3. **已完成**: 所有状态都已完成 → 返回 `None`
|
||||
|
||||
**重要**: 初始状态是 `None`(未开始),而不是第一个节点。
|
||||
|
||||
示例:
|
||||
```python
|
||||
from stateflow.services import get_order_current_state, get_overall_status
|
||||
|
||||
# 获取当前状态(None = 未开始或已完成)
|
||||
current_state = get_order_current_state(order)
|
||||
|
||||
# 获取整体状态类型
|
||||
status = get_overall_status(order) # 'not_started', 'in_progress', 'completed'
|
||||
```
|
||||
|
||||
### 状态推进
|
||||
|
||||
```python
|
||||
from stateflow.services import advance_to_next_state
|
||||
|
||||
# 推进到下一个状态(需要传入操作人)
|
||||
success = advance_to_next_state(order, completed_by=request.user)
|
||||
```
|
||||
|
||||
推进规则:
|
||||
- 如果是初始状态(`None`),推进到第一个节点
|
||||
- 自动查找当前状态
|
||||
- 验证是否可以推进(是否已完成)
|
||||
- 创建 `OrderStateLog` 记录完成时间和操作人
|
||||
- 返回 `(True, message)` 表示成功,`(False, reason)` 表示失败
|
||||
|
||||
### 完整时间线
|
||||
|
||||
```python
|
||||
from stateflow.services import get_order_state_timeline
|
||||
|
||||
# 获取完整的状态时间线
|
||||
timeline = get_order_state_timeline(order)
|
||||
# [
|
||||
# {'state': State1, 'completed_at': datetime, 'completed_by': User, 'is_completed': True},
|
||||
# {'state': State2, 'completed_at': None, 'completed_by': None, 'is_completed': False},
|
||||
# ...
|
||||
# ]
|
||||
```
|
||||
|
||||
## 服务层 API
|
||||
|
||||
所有业务逻辑封装在 `stateflow/services.py` 中,可在 Admin、API、任务队列等场景复用。
|
||||
|
||||
### 主要函数
|
||||
|
||||
#### `get_order_current_state(order) -> State | None`
|
||||
获取订单当前应处理的状态。
|
||||
|
||||
#### `get_overall_status(order) -> str`
|
||||
返回订单整体状态:
|
||||
- `'not_started'`: 未开始
|
||||
- `'in_progress'`: 进行中
|
||||
- `'completed'`: 已完成
|
||||
|
||||
#### `get_order_state_status(order, state) -> str`
|
||||
返回订单中某个状态的状态:
|
||||
- `'not_started'`: 未开始
|
||||
- `'in_progress'`: 进行中
|
||||
- `'completed'`: 已完成
|
||||
|
||||
#### `advance_to_next_state(order, completed_by) -> Tuple[bool, str]`
|
||||
推进到下一个状态,创建完成日志。返回 (是否成功, 消息)。
|
||||
|
||||
#### `get_order_state_timeline(order) -> List[dict]`
|
||||
获取完整时间线,包含所有状态的完成情况(包括已撤销的记录)。
|
||||
|
||||
#### `get_progress_percentage(order) -> float`
|
||||
计算完成进度百分比(0.0 - 100.0)。
|
||||
|
||||
#### `reset_order_progress(order)`
|
||||
重置订单进度。注意:**不删除日志记录**,而是标记为已撤销并记录撤销时间,保留完整的操作历史。
|
||||
|
||||
#### `can_advance_to_next_state(order) -> Tuple[bool, str]`
|
||||
检查是否可以继续推进。返回 (是否可以推进, 原因)。
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 1. 创建流程模板
|
||||
|
||||
```python
|
||||
from stateflow.models import State, Process, ProcessNode
|
||||
|
||||
# 创建状态
|
||||
pending = State.objects.create(name="待审核")
|
||||
approved = State.objects.create(name="已审核")
|
||||
shipped = State.objects.create(name="已发货")
|
||||
|
||||
# 创建流程
|
||||
process = Process.objects.create(name="订单流程")
|
||||
|
||||
# 关联状态(设置顺序)
|
||||
ProcessNode.objects.create(process=process, state=pending, order=1)
|
||||
ProcessNode.objects.create(process=process, state=approved, order=2)
|
||||
ProcessNode.objects.create(process=process, state=shipped, order=3)
|
||||
```
|
||||
|
||||
### 2. 创建订单并推进
|
||||
|
||||
```python
|
||||
from stateflow.models import Order
|
||||
from stateflow.services import get_order_current_state, advance_to_next_state, get_overall_status
|
||||
|
||||
# 创建订单实例
|
||||
order = Order.objects.create(process=process, metadata={'order_no': 'SO-001'})
|
||||
|
||||
# 查看当前状态(初始状态是 None)
|
||||
current = get_order_current_state(order) # None
|
||||
status = get_overall_status(order) # 'not_started'
|
||||
|
||||
# 推进到第一个状态
|
||||
success, msg = advance_to_next_state(order, completed_by=request.user)
|
||||
|
||||
current = get_order_current_state(order) # approved
|
||||
|
||||
# 推进到下一个状态
|
||||
success, msg = advance_to_next_state(order, completed_by=request.user)
|
||||
|
||||
current = get_order_current_state(order) # shipped
|
||||
```
|
||||
|
||||
### 3. 在 API 中使用
|
||||
|
||||
```python
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
from stateflow.services import advance_to_next_state, get_order_state_timeline
|
||||
|
||||
class OrderViewSet(viewsets.ModelViewSet):
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def advance(self, request, pk=None):
|
||||
order = self.get_object()
|
||||
success, message = advance_to_next_state(order, completed_by=request.user)
|
||||
|
||||
if success:
|
||||
return Response({'status': 'advanced', 'message': message})
|
||||
return Response({'status': 'failed', 'message': message}, status=400)
|
||||
|
||||
@action(detail=True, methods=['get'])
|
||||
def timeline(self, request, pk=None):
|
||||
order = self.get_object()
|
||||
timeline = get_order_state_timeline(order)
|
||||
return Response(timeline)
|
||||
```
|
||||
|
||||
## Admin 配置
|
||||
|
||||
Admin 界面提供以下功能:
|
||||
|
||||
1. **State 管理**: 创建和编辑状态,支持内联编辑参数
|
||||
2. **Process 管理**: 定义流程,通过内联表添加有序状态节点
|
||||
3. **Order 管理**:
|
||||
- 查看完成日志(只读内联)
|
||||
- 批量操作:推进到下一状态、重置进度
|
||||
- 显示当前状态、完成百分比
|
||||
4. **OrderStateLog 管理**: 只读日志查看,用于审计
|
||||
|
||||
## 迁移历史
|
||||
|
||||
- `0001_initial.py`: 初始模型(State, Process with CharField state_nodes)
|
||||
- `0002-0007`: 其他应用迁移
|
||||
- `0008_process_state_nodes_remove_process_current_state...`: 将 state_nodes 改为 ManyToMany,创建 ProcessNode 和 Order
|
||||
- `0009_remove_order_current_state_orderstatelog`: 移除 Order.current_state,创建 OrderStateLog
|
||||
|
||||
## 测试
|
||||
|
||||
运行测试:
|
||||
```bash
|
||||
python manage.py test stateflow
|
||||
```
|
||||
|
||||
测试覆盖:
|
||||
- ✅ 初始状态推导
|
||||
- ✅ 状态推进逻辑
|
||||
- ✅ 状态类型判断
|
||||
- ✅ 完整时间线生成
|
||||
- ✅ 进度重置
|
||||
|
||||
## 设计优势
|
||||
|
||||
1. **审计完整**: 每次状态变更都有时间和操作人记录
|
||||
2. **数据一致**: 状态从日志推导,无冗余存储
|
||||
3. **逻辑复用**: 服务层可在 Admin/API/Task 中共享
|
||||
4. **灵活扩展**: 可轻松添加状态前置条件、权限检查等
|
||||
5. **可追溯**: 通过 OrderStateLog 实现完整的操作历史
|
||||
6. **历史保留**: 重置进度时不删除记录,保留完整的操作痕迹
|
||||
7. **撤销追踪**: 记录撤销时间,方便审计和问题追溯
|
||||
|
||||
## ProcessNode vs OrderStateLog
|
||||
|
||||
很多人会混淆这两个表,这里做详细说明:
|
||||
|
||||
### ProcessNode (流程模板)
|
||||
- **作用**: 定义流程包含哪些状态,以及执行顺序
|
||||
- **关系**: Process ↔ State 的多对多关联
|
||||
- **层级**: 模板层(一对多)
|
||||
- **类比**: "菜谱",定义做菜的步骤
|
||||
- **示例**: 订单流程 = [待审核(1) → 已审核(2) → 已发货(3)]
|
||||
|
||||
### OrderStateLog (订单实例)
|
||||
- **作用**: 记录某个订单完成了哪些状态,以及完成时间、操作人
|
||||
- **关系**: Order → State 的完成记录
|
||||
- **层级**: 实例层(每个订单独立)
|
||||
- **类比**: "做菜记录",记录实际做了什么、何时做的
|
||||
- **示例**: 订单#001 在 2025-01-10 由张三完成了"待审核"状态
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **唯一约束**: 每个 `(order, state)` 只能有一条记录
|
||||
2. **顺序性**: 必须按 ProcessNode.order 顺序完成,不能跳过
|
||||
3. **操作人必填**: `advance_to_next_state()` 必须传入 `completed_by` 参数
|
||||
4. **不可删除**: OrderStateLog 不允许删除,只能标记为已撤销
|
||||
5. **保留历史**: 重置进度时保留所有历史记录,并记录撤销时间
|
||||
6. **初始状态**: 新订单的初始状态是 `None`(未开始),而不是第一个节点
|
||||
Reference in New Issue
Block a user