1
0
forked from erp-dev/erp

fix: [stateflow] query cancelled state with parameters failed

This commit is contained in:
2025-11-24 08:46:28 +08:00
parent 2a838199fa
commit 0e77496d9a
6 changed files with 149 additions and 4 deletions

View File

@@ -0,0 +1,56 @@
# Bug 修复报告
## 基本信息
- 日期2025-11-22
- 模块stateflow流程状态管理
- 修复者Cline
---
## 问题描述
API `GET /api/v1/stateflow/business-objects/xxx/state-logs/` 无法获取已撤销状态的工艺参数。
**具体表现**当一个状态流转记录被撤销后通过该API获取状态日志列表时已撤销状态记录的工艺参数会被过滤掉导致参数丢失。
**影响范围**
1. 使用 `GET /api/v1/stateflow/business-objects/xxx/state-logs/` API 获取包含已撤销状态的日志列表
2. 依赖这些日志进行审计、回溯或数据分析的功能
---
## 根本原因
`StateFlowRecordWithParametersSerializer` 中调用 `get_all_parameters_summary()` 时未传递 `include_cancelled=True` 参数,导致已撤销状态的参数被 `get_all_parameters_summary()` 方法内部逻辑过滤掉。
---
## 修复方案
修改 `/home/f/coding/flower/stateflow/serializers.py` 中的 `get_parameters_summary` 方法:
**修改前**
```python
def get_parameters_summary(self, obj):
"""获取参数摘要"""
return obj.get_all_parameters_summary()
```
**修改后**
```python
def get_parameters_summary(self, obj):
"""获取参数摘要,包含已撤销状态的参数"""
return obj.get_all_parameters_summary(include_cancelled=True)
```
---
## 测试验证
1. **创建测试用例**:在 `/home/f/coding/flower/stateflow/tests/test_business_object_api.py` 中创建 `test_get_state_logs_api_cancelled_state_with_parameters` 测试用例用于复现和验证bug。
2. **验证修复**
- 运行测试用例 `test_get_state_logs_api_cancelled_state_with_parameters`
- 修复前测试失败,修复后测试通过
- 验证已撤销状态的工艺参数能够正确返回
---
## 总结
该修复确保了已撤销状态的工艺参数不会丢失与未撤销状态的处理方式保持一致符合业务需求。修复仅涉及一行代码的修改但确保了数据完整性和API的一致性。

View File

@@ -54,7 +54,7 @@ class PlateOrder(ModelBase):
basic_models.Customer,
on_delete=models.PROTECT,
related_name='plate_orders',
verbose_name='客户'
verbose_name='客户',
)
area = models.CharField(max_length=255, blank=True, null=True, verbose_name='区域')
default_address = models.CharField(max_length=500, blank=True, null=True, verbose_name='默认地址')

View File

@@ -23,4 +23,5 @@ dependencies = [
[dependency-groups]
dev = [
"psycopg[binary]>=3.2.12",
"pytest>=9.0.1",
]

View File

@@ -232,8 +232,8 @@ class StateFlowRecordWithParametersSerializer(serializers.ModelSerializer):
read_only_fields = ['id', 'completed_at', 'is_cancelled', 'cancelled_at']
def get_parameters_summary(self, obj):
"""获取参数摘要"""
return obj.get_all_parameters_summary()
"""获取参数摘要,包含已撤销状态的参数"""
return obj.get_all_parameters_summary(include_cancelled=True)
class BusinessObjectListSerializer(serializers.ModelSerializer):

View File

@@ -597,6 +597,38 @@ class BusinessObjectAPITestCase(TestCase):
self.assertLessEqual(time1, time2)
self.assertLessEqual(time2, time3)
def test_get_state_logs_api_cancelled_state_with_parameters(self):
"""测试获取已撤销状态的工艺参数bug修复验证"""
# 推进并提供参数
self.client.post(
f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/',
{'parameters': {'temperature': '25.5', 'humidity': '60%'}},
format='json'
)
# 回退(撤销该状态)
self.client.post(f'/api/v1/stateflow/business-objects/{self.business_object.id}/step_back/')
# 使用 include_cancelled=true 和 include_parameters=true 获取状态记录
response = self.client.get(
f'/api/v1/stateflow/business-objects/{self.business_object.id}/state-logs/?include_cancelled=true&include_parameters=true'
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
# 验证返回2条记录撤销后原来的记录也在只是标记为已撤销
self.assertEqual(response.data['count'], 1)
# 验证记录包含工艺参数
logs = response.data['state_logs']
self.assertEqual(len(logs), 1)
state_log = logs[0]
self.assertTrue(state_log['is_cancelled'])
self.assertIn('parameters_summary', state_log)
self.assertEqual(state_log['parameters_summary']['temperature'], '25.5')
self.assertEqual(state_log['parameters_summary']['humidity'], '60%')
def test_advance_when_already_completed(self):
"""测试流程已完成时尝试推进(预期错误)"""
# 完成所有状态

58
uv.lock generated
View File

@@ -231,6 +231,7 @@ dependencies = [
[package.dev-dependencies]
dev = [
{ name = "psycopg", extra = ["binary"] },
{ name = "pytest" },
]
[package.metadata]
@@ -251,7 +252,10 @@ requires-dist = [
]
[package.metadata.requires-dev]
dev = [{ name = "psycopg", extras = ["binary"], specifier = ">=3.2.12" }]
dev = [
{ name = "psycopg", extras = ["binary"], specifier = ">=3.2.12" },
{ name = "pytest", specifier = ">=9.0.1" },
]
[[package]]
name = "h11"
@@ -280,6 +284,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/59/91/aa6bde563e0085a02a435aa99b49ef75b0a4b062635e606dab23ce18d720/inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2", size = 9454, upload-time = "2020-08-22T08:16:27.816Z" },
]
[[package]]
name = "iniconfig"
version = "2.3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
]
[[package]]
name = "jsonschema"
version = "4.25.1"
@@ -316,6 +329,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/70/81/54e3ce63502cd085a0c556652a4e1b919c45a446bd1e5300e10c44c8c521/markdown-3.10-py3-none-any.whl", hash = "sha256:b5b99d6951e2e4948d939255596523444c0e677c669700b1d17aa4a8a464cb7c", size = 107678, upload-time = "2025-11-03T19:51:13.887Z" },
]
[[package]]
name = "packaging"
version = "25.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
]
[[package]]
name = "pillow"
version = "12.0.0"
@@ -349,6 +371,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/c1/70/6b41bdcddf541b437bbb9f47f94d2db5d9ddef6c37ccab8c9107743748a4/pillow-12.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:99353a06902c2e43b43e8ff74ee65a7d90307d82370604746738a1e0661ccca7", size = 2525630, upload-time = "2025-10-15T18:23:57.149Z" },
]
[[package]]
name = "pluggy"
version = "1.6.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
]
[[package]]
name = "psycopg"
version = "3.2.12"
@@ -427,6 +458,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/8a/ac/9fc61b4f9d079482a290afe8d206b8f490e9fd32d4fc03ed4fc698214e01/pydantic_core-2.41.4-cp314-cp314t-win_arm64.whl", hash = "sha256:d34f950ae05a83e0ede899c595f312ca976023ea1db100cd5aa188f7005e3ab0", size = 1973897, upload-time = "2025-10-14T10:22:13.444Z" },
]
[[package]]
name = "pygments"
version = "2.19.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
]
[[package]]
name = "pyjwt"
version = "2.10.1"
@@ -436,6 +476,22 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload-time = "2024-11-28T03:43:27.893Z" },
]
[[package]]
name = "pytest"
version = "9.0.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "iniconfig" },
{ name = "packaging" },
{ name = "pluggy" },
{ name = "pygments" },
]
sdist = { url = "https://files.pythonhosted.org/packages/07/56/f013048ac4bc4c1d9be45afd4ab209ea62822fb1598f40687e6bf45dcea4/pytest-9.0.1.tar.gz", hash = "sha256:3e9c069ea73583e255c3b21cf46b8d3c56f6e3a1a8f6da94ccb0fcf57b9d73c8", size = 1564125, upload-time = "2025-11-12T13:05:09.333Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0b/8b/6300fb80f858cda1c51ffa17075df5d846757081d11ab4aa35cef9e6258b/pytest-9.0.1-py3-none-any.whl", hash = "sha256:67be0030d194df2dfa7b556f2e56fb3c3315bd5c8822c6951162b92b32ce7dad", size = 373668, upload-time = "2025-11-12T13:05:07.379Z" },
]
[[package]]
name = "pyyaml"
version = "6.0.3"