forked from erp-dev/erp
278 lines
6.8 KiB
Markdown
278 lines
6.8 KiB
Markdown
# Stateflow current_state 重构记录
|
||
|
||
**日期**: 2025-11-19
|
||
**目标**: 消除 `current_state` 语义歧义,使其可配置
|
||
|
||
---
|
||
|
||
## 重构目标
|
||
|
||
### 问题
|
||
原有的 `current_state` 始终表示"下一个待执行的节点",但在某些业务场景下,需要显示"最后完成的节点",语义不够清晰,容易混淆。
|
||
|
||
### 解决方案
|
||
1. 添加两个明确语义的方法:
|
||
- `get_last_completed_state()` - 返回最后完成的状态
|
||
- `get_next_pending_state()` - 返回下一个待执行的状态
|
||
|
||
2. `current_state` 变为可配置模式:
|
||
- 通过 `settings.STATEFLOW_CURRENT_STATE_MODE` 控制行为
|
||
- `'NEXT'` (默认): 返回下一个待执行的节点(原有行为)
|
||
- `'LAST'`: 返回最后完成的节点(新增模式)
|
||
|
||
3. 保持所有API接口不变,确保向后兼容
|
||
|
||
---
|
||
|
||
## 修改文件清单
|
||
|
||
### 1. `stateflow/services.py`
|
||
|
||
**新增函数**:
|
||
- `get_last_completed_state(business_object)` - 获取最后完成的状态
|
||
- `get_next_pending_state_simple(business_object)` - 获取下一个待执行的状态(简化版)
|
||
|
||
**修改函数**:
|
||
- `get_business_object_current_state(business_object)` - 根据配置返回不同结果
|
||
|
||
```python
|
||
def get_business_object_current_state(business_object):
|
||
"""根据 settings.STATEFLOW_CURRENT_STATE_MODE 决定返回内容"""
|
||
from django.conf import settings
|
||
mode = getattr(settings, 'STATEFLOW_CURRENT_STATE_MODE', 'NEXT')
|
||
|
||
if mode == 'LAST':
|
||
return get_last_completed_state(business_object)
|
||
else: # 默认 'NEXT'
|
||
return get_next_pending_state_simple(business_object)
|
||
```
|
||
|
||
### 2. `stateflow/models.py` (BusinessObject)
|
||
|
||
**新增方法**:
|
||
- `get_last_completed_state()` - 获取最后完成的状态
|
||
- `get_next_pending_state()` - 获取下一个待执行的状态
|
||
|
||
**修改方法**:
|
||
- `get_current_state()` - 更新文档说明,明确其行为可配置
|
||
|
||
### 3. `stateflow/admin.py`
|
||
|
||
**修改**:
|
||
- `current_state_display()` - 根据配置模式显示不同文案
|
||
- NEXT模式: "进行中 (下一步: xxx)"
|
||
- LAST模式: "进行中 (已完成: xxx)"
|
||
- `params()` - 更新文档说明
|
||
|
||
### 4. `flower/settings.py`
|
||
|
||
**新增配置**:
|
||
```python
|
||
# Stateflow 配置
|
||
STATEFLOW_CURRENT_STATE_MODE = 'NEXT' # 或 'LAST'
|
||
```
|
||
|
||
添加详细的配置说明文档。
|
||
|
||
---
|
||
|
||
## 兼容性保证
|
||
|
||
### ✅ API层面完全兼容
|
||
- 所有API端点无需修改
|
||
- 所有序列化器无需修改
|
||
- 返回结构保持一致
|
||
|
||
### ✅ 模型层面保持兼容
|
||
- `PlateOrder.status` 属性无需修改
|
||
- `PrintingJob.status` 属性无需修改
|
||
- 方法签名完全一致
|
||
|
||
### ✅ 测试验证
|
||
- ✅ stateflow.tests.test_services - 10个测试全部通过
|
||
- ✅ printing.test_plate_order - 11个测试全部通过
|
||
- ✅ printing.test_plate_order_advance - 3个测试全部通过
|
||
- ✅ **总计95个测试全部通过**
|
||
|
||
---
|
||
|
||
## 使用指南
|
||
|
||
### 默认行为 (NEXT模式)
|
||
```python
|
||
# settings.py
|
||
STATEFLOW_CURRENT_STATE_MODE = 'NEXT' # 默认
|
||
|
||
# 业务代码
|
||
current = business_object.get_current_state()
|
||
# 返回:下一个待执行的节点
|
||
|
||
# 示例:流程 [质检] → [包装] → [发货]
|
||
# 完成质检后:
|
||
current_state.name # "包装"(下一步要做的)
|
||
status # "包装"
|
||
progress # 33.3%
|
||
```
|
||
|
||
### LAST模式
|
||
```python
|
||
# settings.py
|
||
STATEFLOW_CURRENT_STATE_MODE = 'LAST'
|
||
|
||
# 业务代码
|
||
current = business_object.get_current_state()
|
||
# 返回:最后完成的节点
|
||
|
||
# 示例:流程 [质检] → [包装] → [发货]
|
||
# 完成质检后:
|
||
current_state.name # "质检"(刚完成的)
|
||
status # "质检"
|
||
progress # 33.3%
|
||
```
|
||
|
||
### 推荐做法:使用明确命名的方法
|
||
```python
|
||
# ✅ 推荐:语义清晰
|
||
last_done = business_object.get_last_completed_state()
|
||
next_todo = business_object.get_next_pending_state()
|
||
|
||
# ⚠️ 可用但需注意:语义取决于配置
|
||
current = business_object.get_current_state()
|
||
```
|
||
|
||
---
|
||
|
||
## status 和 current_state 的关系
|
||
|
||
### status 属性逻辑(不变)
|
||
```python
|
||
@property
|
||
def status(self) -> str:
|
||
if not self.business_object:
|
||
return '未开始'
|
||
|
||
current_state = self.business_object.get_current_state()
|
||
|
||
if current_state is None:
|
||
return '已完成'
|
||
|
||
progress = self.business_object.get_progress_percentage()
|
||
if progress == 0:
|
||
return '未开始'
|
||
|
||
return current_state.name
|
||
```
|
||
|
||
### 不同模式下的表现
|
||
|
||
| 场景 | progress | NEXT模式 | LAST模式 |
|
||
|------|----------|----------|----------|
|
||
| 未开始 | 0% | `status="未开始"`<br>`current_state="质检"` | `status="未开始"`<br>`current_state=None` |
|
||
| 完成质检 | 33% | `status="包装"`<br>`current_state="包装"` | `status="质检"`<br>`current_state="质检"` |
|
||
| 全部完成 | 100% | `status="已完成"`<br>`current_state=None` | `status="已完成"`<br>`current_state="发货"` |
|
||
|
||
---
|
||
|
||
## Admin 显示变化
|
||
|
||
### NEXT模式(默认)
|
||
```
|
||
当前状态: 进行中 (下一步: 包装)
|
||
```
|
||
|
||
### LAST模式
|
||
```
|
||
当前状态: 进行中 (已完成: 质检)
|
||
```
|
||
|
||
---
|
||
|
||
## 注意事项
|
||
|
||
### ⚠️ 语义变化
|
||
- 切换配置会改变 `status` 和 `status_id` 的含义
|
||
- 前端显示文案可能需要相应调整
|
||
|
||
### ⚠️ 环境一致性
|
||
- 建议在所有环境(开发/测试/生产)使用相同配置
|
||
- 避免因配置不同导致行为差异
|
||
|
||
### ⚠️ 测试注意
|
||
- 如需切换模式,确保充分测试
|
||
- 可以使用 `@override_settings` 装饰器测试不同模式
|
||
|
||
```python
|
||
from django.test import override_settings
|
||
|
||
@override_settings(STATEFLOW_CURRENT_STATE_MODE='LAST')
|
||
def test_with_last_mode():
|
||
# 测试 LAST 模式
|
||
pass
|
||
```
|
||
|
||
---
|
||
|
||
## 设计优势
|
||
|
||
### ✅ 消除歧义
|
||
- 通过命名明确区分"最后完成"和"下一个待执行"
|
||
- 减少开发者理解成本
|
||
|
||
### ✅ 灵活可配置
|
||
- 可以根据业务需求选择不同模式
|
||
- 无需修改代码即可切换行为
|
||
|
||
### ✅ 向后兼容
|
||
- API完全不变
|
||
- 测试全部通过
|
||
- 现有集成无影响
|
||
|
||
### ✅ 代码清晰
|
||
- 新增方法语义明确
|
||
- 文档完善
|
||
- 易于维护
|
||
|
||
---
|
||
|
||
## 未来优化建议
|
||
|
||
### 方案A:逐步迁移到明确命名的方法
|
||
```python
|
||
# 在新代码中推荐使用
|
||
business_object.get_last_completed_state()
|
||
business_object.get_next_pending_state()
|
||
|
||
# 而非
|
||
business_object.get_current_state()
|
||
```
|
||
|
||
### 方案B:考虑废弃 current_state
|
||
```python
|
||
# 未来可能
|
||
@deprecated("请使用 get_last_completed_state() 或 get_next_pending_state()")
|
||
def get_current_state(self):
|
||
pass
|
||
```
|
||
|
||
---
|
||
|
||
## 总结
|
||
|
||
此次重构成功实现了以下目标:
|
||
|
||
1. ✅ 添加了语义明确的 `get_last_completed_state()` 和 `get_next_pending_state()` 方法
|
||
2. ✅ 使 `current_state` 可配置,默认保持原有行为
|
||
3. ✅ 所有API接口保持不变
|
||
4. ✅ Admin仅作轻微文案调整
|
||
5. ✅ 95个测试全部通过
|
||
|
||
**重构风险**: 低
|
||
**兼容性**: 完全兼容
|
||
**测试覆盖**: 完整
|
||
|
||
---
|
||
|
||
**重构完成时间**: 2025-11-19
|
||
**测试通过率**: 100% (95/95)
|
||
|