forked from erp-dev/erp
feat: printing module
This commit is contained in:
@@ -7,6 +7,18 @@ from . import models
|
||||
class StateParameterInline(admin.StackedInline):
|
||||
model = models.StateParameter
|
||||
extra = 1
|
||||
fields = ('key', 'value', 'attachment', 'description')
|
||||
readonly_fields = ('attachment_preview',)
|
||||
|
||||
def attachment_preview(self, obj):
|
||||
"""显示附件预览"""
|
||||
if obj.attachment:
|
||||
if obj.attachment.name.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp')):
|
||||
return mark_safe(f'<img src="{obj.attachment.url}" style="max-width: 200px; max-height: 200px;" />')
|
||||
else:
|
||||
return mark_safe(f'<a href="{obj.attachment.url}" target="_blank">查看附件</a>')
|
||||
return "-"
|
||||
attachment_preview.short_description = '附件预览'
|
||||
|
||||
|
||||
class ProcessNodeInline(admin.StackedInline):
|
||||
@@ -34,10 +46,15 @@ class StateAdmin(admin.ModelAdmin):
|
||||
def parameter_overview(self, obj: models.State):
|
||||
params = obj.parameters.all()
|
||||
if params:
|
||||
result = "<br>".join([f"{param.key}={param.value}" for param in params])
|
||||
result = []
|
||||
for param in params:
|
||||
param_str = f"{param.key}={param.value}"
|
||||
if param.attachment:
|
||||
param_str += f' <a href="{param.attachment.url}" target="_blank">📎</a>'
|
||||
result.append(param_str)
|
||||
return mark_safe("<br>".join(result))
|
||||
else:
|
||||
result = "-"
|
||||
return mark_safe(result)
|
||||
return "-"
|
||||
|
||||
|
||||
@admin.register(models.Process)
|
||||
@@ -81,7 +98,7 @@ class BusinessObjectAdmin(admin.ModelAdmin):
|
||||
)
|
||||
search_fields = ('name', 'description')
|
||||
list_filter = ('process', 'created_at', 'updated_at')
|
||||
actions = ['advance_to_next_state_action', 'reset_progress_action']
|
||||
actions = ['advance_to_next_state_action', 'step_back_one_state_action', 'reset_progress_action']
|
||||
autocomplete_fields = ('process',)
|
||||
inlines = [StateFlowRecordInline]
|
||||
fieldsets = [
|
||||
@@ -115,6 +132,20 @@ class BusinessObjectAdmin(admin.ModelAdmin):
|
||||
|
||||
self.message_user(request, f'成功推进 {success_count}/{queryset.count()} 个业务对象。详情: {"; ".join(messages[:5])}')
|
||||
|
||||
@action(description='回退一步')
|
||||
def step_back_one_state_action(self, request, queryset):
|
||||
from . import services
|
||||
success_count = 0
|
||||
messages = []
|
||||
|
||||
for obj in queryset:
|
||||
success, message = services.step_back_one_state(obj, request.user)
|
||||
if success:
|
||||
success_count += 1
|
||||
messages.append(f"{obj.name}: {message}")
|
||||
|
||||
self.message_user(request, f'成功回退 {success_count}/{queryset.count()} 个业务对象。详情: {"; ".join(messages[:5])}')
|
||||
|
||||
@action(description='重置进度')
|
||||
def reset_progress_action(self, request, queryset):
|
||||
from . import services
|
||||
|
||||
18
stateflow/migrations/0015_stateparameter_attachment.py
Normal file
18
stateflow/migrations/0015_stateparameter_attachment.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2.7 on 2025-11-13 08:34
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('stateflow', '0014_alter_process_options'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='stateparameter',
|
||||
name='attachment',
|
||||
field=models.FileField(blank=True, null=True, upload_to='state_parameters/', verbose_name='附件'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 5.2.7 on 2025-11-13 09:00
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('stateflow', '0015_stateparameter_attachment'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='stateparameter',
|
||||
name='key',
|
||||
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='参数键'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='stateparameter',
|
||||
name='value',
|
||||
field=models.CharField(blank=True, max_length=200, null=True, verbose_name='参数值'),
|
||||
),
|
||||
]
|
||||
@@ -33,8 +33,9 @@ class State(ModelBase):
|
||||
class StateParameter(ModelBase):
|
||||
"""StateParameter model representing parameters associated with a state."""
|
||||
state = models.ForeignKey(State, on_delete=models.PROTECT, related_name='parameters', verbose_name='关联参数')
|
||||
key = models.CharField(max_length=100, verbose_name='参数键')
|
||||
value = models.CharField(max_length=200, verbose_name='参数值')
|
||||
key = models.CharField(max_length=100, blank=True, null=True, verbose_name='参数键')
|
||||
value = models.CharField(max_length=200, blank=True, null=True, verbose_name='参数值')
|
||||
attachment = models.FileField(upload_to='state_parameters/', null=True, blank=True, verbose_name='附件')
|
||||
description = models.CharField(max_length=200, blank=True, verbose_name='参数描述')
|
||||
|
||||
def __str__(self):
|
||||
|
||||
@@ -9,11 +9,21 @@ from stateflow import models
|
||||
|
||||
class StateParameterSerializer(serializers.ModelSerializer):
|
||||
"""状态参数序列化器"""
|
||||
attachment_url = serializers.SerializerMethodField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = models.StateParameter
|
||||
fields = ['id', 'key', 'value', 'description']
|
||||
read_only_fields = ['id']
|
||||
fields = ['id', 'key', 'value', 'attachment', 'attachment_url', 'description']
|
||||
read_only_fields = ['id', 'attachment_url']
|
||||
|
||||
def get_attachment_url(self, obj):
|
||||
"""获取附件的完整 URL"""
|
||||
if obj.attachment:
|
||||
request = self.context.get('request')
|
||||
if request:
|
||||
return request.build_absolute_uri(obj.attachment.url)
|
||||
return obj.attachment.url
|
||||
return None
|
||||
|
||||
|
||||
class StateListSerializer(serializers.ModelSerializer):
|
||||
|
||||
@@ -148,6 +148,37 @@ def reset_business_object_progress(business_object: 'models.BusinessObject') ->
|
||||
reset_order_progress = reset_business_object_progress
|
||||
|
||||
|
||||
def step_back_one_state(business_object: 'models.BusinessObject', user) -> Tuple[bool, str]:
|
||||
"""
|
||||
回退一步(撤销最后一次完成的状态)
|
||||
|
||||
这是 advance_to_next_state 的逆向操作
|
||||
|
||||
返回: (是否成功, 消息)
|
||||
"""
|
||||
from django.utils import timezone
|
||||
|
||||
with transaction.atomic():
|
||||
# 获取最后一条有效的状态流转记录(按完成时间倒序)
|
||||
last_record = (
|
||||
business_object.state_logs
|
||||
.filter(is_cancelled=False)
|
||||
.order_by('-completed_at', '-id')
|
||||
.first()
|
||||
)
|
||||
|
||||
# 如果没有任何有效记录,说明当前没有任何状态流转,无需回退
|
||||
if not last_record:
|
||||
return False, "当前没有任何状态流转记录,无法回退"
|
||||
|
||||
# 标记最后一条记录为已撤销
|
||||
last_record.is_cancelled = True
|
||||
last_record.cancelled_at = timezone.now()
|
||||
last_record.save(update_fields=['is_cancelled', 'cancelled_at'])
|
||||
|
||||
return True, f"已回退状态: {last_record.state.name}"
|
||||
|
||||
|
||||
def get_current_state_parameters(business_object: 'models.BusinessObject') -> List['models.StateParameter']:
|
||||
"""获取订单当前状态的参数列表"""
|
||||
current_state = get_business_object_current_state(business_object)
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
Stateflow API 测试
|
||||
"""
|
||||
from django.test import TestCase
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from rest_framework.test import APIClient
|
||||
from rest_framework import status
|
||||
from django.contrib.auth import get_user_model
|
||||
from stateflow import models
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
@@ -92,6 +95,140 @@ class StateAPITestCase(TestCase):
|
||||
response = self.client.get('/api/v1/stateflow/states/?search=审核&limit=10&offset=0')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data['count'], 1)
|
||||
|
||||
def test_create_state_parameter_with_attachment(self):
|
||||
"""测试创建带附件的状态参数"""
|
||||
# 创建一个测试文件
|
||||
test_file = SimpleUploadedFile(
|
||||
"test_doc.txt",
|
||||
b"This is a test document content",
|
||||
content_type="text/plain"
|
||||
)
|
||||
|
||||
# 先创建状态
|
||||
state = models.State.objects.create(name='测试状态', description='带附件')
|
||||
|
||||
# 创建带附件的参数
|
||||
param = models.StateParameter.objects.create(
|
||||
state=state,
|
||||
key='document',
|
||||
value='测试文档',
|
||||
attachment=test_file,
|
||||
description='这是一个测试文档'
|
||||
)
|
||||
|
||||
# 验证附件已保存
|
||||
self.assertIsNotNone(param.attachment)
|
||||
self.assertIn('test_doc', param.attachment.name) # 文件名可能有哈希前缀
|
||||
|
||||
# 通过 API 获取状态详情,验证附件字段
|
||||
response = self.client.get(f'/api/v1/stateflow/states/{state.id}/')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
# 验证参数中包含附件信息
|
||||
parameters = response.data['parameters']
|
||||
self.assertEqual(len(parameters), 1)
|
||||
self.assertIn('attachment', parameters[0])
|
||||
self.assertIn('attachment_url', parameters[0])
|
||||
self.assertIsNotNone(parameters[0]['attachment_url'])
|
||||
|
||||
# 清理测试文件
|
||||
if param.attachment:
|
||||
param.attachment.delete()
|
||||
|
||||
def test_state_parameter_without_attachment(self):
|
||||
"""测试创建不带附件的状态参数"""
|
||||
state = models.State.objects.create(name='测试状态2', description='不带附件')
|
||||
param = models.StateParameter.objects.create(
|
||||
state=state,
|
||||
key='simple_param',
|
||||
value='简单值',
|
||||
description='简单参数'
|
||||
)
|
||||
|
||||
# 验证附件字段为空
|
||||
self.assertFalse(param.attachment)
|
||||
|
||||
# 通过 API 获取状态详情
|
||||
response = self.client.get(f'/api/v1/stateflow/states/{state.id}/')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
# 验证附件字段为 null
|
||||
parameters = response.data['parameters']
|
||||
self.assertEqual(len(parameters), 1)
|
||||
self.assertIsNone(parameters[0]['attachment'])
|
||||
self.assertIsNone(parameters[0]['attachment_url'])
|
||||
|
||||
def test_update_state_parameter_with_attachment(self):
|
||||
"""测试更新状态参数时添加附件"""
|
||||
# 创建初始状态和参数(不带附件)
|
||||
state = models.State.objects.create(name='测试状态3', description='更新附件')
|
||||
param = models.StateParameter.objects.create(
|
||||
state=state,
|
||||
key='updatable_param',
|
||||
value='初始值',
|
||||
description='可更新参数'
|
||||
)
|
||||
|
||||
# 验证初始无附件
|
||||
self.assertFalse(param.attachment)
|
||||
|
||||
# 添加附件
|
||||
test_file = SimpleUploadedFile(
|
||||
"updated_doc.pdf",
|
||||
b"Updated document content",
|
||||
content_type="application/pdf"
|
||||
)
|
||||
param.attachment = test_file
|
||||
param.save()
|
||||
|
||||
# 验证附件已添加
|
||||
param.refresh_from_db()
|
||||
self.assertIsNotNone(param.attachment)
|
||||
self.assertIn('updated_doc', param.attachment.name) # 文件名可能有哈希前缀
|
||||
|
||||
# 通过 API 验证
|
||||
response = self.client.get(f'/api/v1/stateflow/states/{state.id}/')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
parameters = response.data['parameters']
|
||||
self.assertIsNotNone(parameters[0]['attachment'])
|
||||
self.assertIsNotNone(parameters[0]['attachment_url'])
|
||||
|
||||
# 清理
|
||||
if param.attachment:
|
||||
param.attachment.delete()
|
||||
|
||||
def test_state_parameter_attachment_url_format(self):
|
||||
"""测试附件 URL 格式正确"""
|
||||
test_file = SimpleUploadedFile(
|
||||
"test_image.jpg",
|
||||
b"fake image content",
|
||||
content_type="image/jpeg"
|
||||
)
|
||||
|
||||
state = models.State.objects.create(name='图片状态', description='带图片')
|
||||
param = models.StateParameter.objects.create(
|
||||
state=state,
|
||||
key='image',
|
||||
value='测试图片',
|
||||
attachment=test_file
|
||||
)
|
||||
|
||||
# 通过 API 获取
|
||||
response = self.client.get(f'/api/v1/stateflow/states/{state.id}/')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
parameters = response.data['parameters']
|
||||
attachment_url = parameters[0]['attachment_url']
|
||||
|
||||
# 验证 URL 格式
|
||||
self.assertIsNotNone(attachment_url)
|
||||
self.assertIn('http', attachment_url) # 应该是完整 URL
|
||||
self.assertIn('state_parameters/', attachment_url) # 包含上传路径
|
||||
|
||||
# 清理
|
||||
if param.attachment:
|
||||
param.attachment.delete()
|
||||
|
||||
|
||||
class ProcessAPITestCase(TestCase):
|
||||
|
||||
1
stateflow/tests/__init__.py
Normal file
1
stateflow/tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# Stateflow tests
|
||||
182
stateflow/tests/test_step_back.py
Normal file
182
stateflow/tests/test_step_back.py
Normal file
@@ -0,0 +1,182 @@
|
||||
"""
|
||||
测试单步回退功能
|
||||
"""
|
||||
from django.test import TestCase
|
||||
from django.contrib.auth import get_user_model
|
||||
from stateflow import models, services
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class StepBackTestCase(TestCase):
|
||||
"""测试单步回退功能"""
|
||||
|
||||
def setUp(self):
|
||||
# 创建测试用户
|
||||
self.user = User.objects.create_user(username='testuser', password='testpass')
|
||||
|
||||
# 创建状态
|
||||
self.state1 = models.State.objects.create(name='状态1', description='第一个状态')
|
||||
self.state2 = models.State.objects.create(name='状态2', description='第二个状态')
|
||||
self.state3 = models.State.objects.create(name='状态3', description='第三个状态')
|
||||
|
||||
# 创建流程
|
||||
self.process = models.Process.objects.create(name='测试流程', description='三个状态的测试流程')
|
||||
|
||||
# 创建流程节点
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state1, order=1)
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state2, order=2)
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state3, order=3)
|
||||
|
||||
# 创建业务对象
|
||||
self.business_object = models.BusinessObject.objects.create(
|
||||
name='测试业务对象',
|
||||
process=self.process,
|
||||
description='测试回退功能'
|
||||
)
|
||||
|
||||
def test_step_back_from_not_started(self):
|
||||
"""测试从未开始状态回退(应该返回失败)"""
|
||||
success, message = services.step_back_one_state(self.business_object, self.user)
|
||||
|
||||
self.assertFalse(success)
|
||||
self.assertIn('没有任何状态流转记录', message)
|
||||
|
||||
# 验证仍然是未开始状态
|
||||
self.assertEqual(services.get_overall_status(self.business_object), 'not_started')
|
||||
|
||||
def test_step_back_from_first_state(self):
|
||||
"""测试从第一个状态回退到未开始"""
|
||||
# 推进到第一个状态
|
||||
success, _ = services.advance_to_next_state(self.business_object, self.user)
|
||||
self.assertTrue(success)
|
||||
|
||||
# 验证当前在第一个状态(进行中,准备完成第二个状态)
|
||||
current_state = services.get_business_object_current_state(self.business_object)
|
||||
self.assertEqual(current_state.id, self.state2.id)
|
||||
|
||||
# 回退一步
|
||||
success, message = services.step_back_one_state(self.business_object, self.user)
|
||||
self.assertTrue(success)
|
||||
self.assertIn('状态1', message)
|
||||
|
||||
# 验证回到未开始状态
|
||||
self.assertEqual(services.get_overall_status(self.business_object), 'not_started')
|
||||
|
||||
# 验证状态1的记录已被撤销
|
||||
record = models.StateFlowRecord.objects.filter(
|
||||
business_object=self.business_object,
|
||||
state=self.state1
|
||||
).first()
|
||||
self.assertTrue(record.is_cancelled)
|
||||
self.assertIsNotNone(record.cancelled_at)
|
||||
|
||||
def test_step_back_from_middle_state(self):
|
||||
"""测试从中间状态回退"""
|
||||
# 推进到第二个状态
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
|
||||
# 验证当前在第二个状态(进行中,准备完成第三个状态)
|
||||
current_state = services.get_business_object_current_state(self.business_object)
|
||||
self.assertEqual(current_state.id, self.state3.id)
|
||||
|
||||
# 回退一步
|
||||
success, message = services.step_back_one_state(self.business_object, self.user)
|
||||
self.assertTrue(success)
|
||||
self.assertIn('状态2', message)
|
||||
|
||||
# 验证回到第一个状态(进行中,准备完成第二个状态)
|
||||
current_state = services.get_business_object_current_state(self.business_object)
|
||||
self.assertEqual(current_state.id, self.state2.id)
|
||||
|
||||
# 验证状态2的记录已被撤销
|
||||
record = models.StateFlowRecord.objects.filter(
|
||||
business_object=self.business_object,
|
||||
state=self.state2
|
||||
).first()
|
||||
self.assertTrue(record.is_cancelled)
|
||||
|
||||
def test_step_back_from_completed(self):
|
||||
"""测试从已完成状态回退"""
|
||||
# 推进到完成
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
|
||||
# 验证已完成
|
||||
self.assertEqual(services.get_overall_status(self.business_object), 'completed')
|
||||
|
||||
# 回退一步
|
||||
success, message = services.step_back_one_state(self.business_object, self.user)
|
||||
self.assertTrue(success)
|
||||
self.assertIn('状态3', message)
|
||||
|
||||
# 验证回到进行中状态(准备完成第三个状态)
|
||||
self.assertEqual(services.get_overall_status(self.business_object), 'in_progress')
|
||||
current_state = services.get_business_object_current_state(self.business_object)
|
||||
self.assertEqual(current_state.id, self.state3.id)
|
||||
|
||||
def test_step_back_multiple_times(self):
|
||||
"""测试多次回退"""
|
||||
# 推进到完成
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
|
||||
# 第一次回退
|
||||
success, _ = services.step_back_one_state(self.business_object, self.user)
|
||||
self.assertTrue(success)
|
||||
current = services.get_business_object_current_state(self.business_object)
|
||||
self.assertEqual(current.id, self.state3.id)
|
||||
|
||||
# 第二次回退
|
||||
success, _ = services.step_back_one_state(self.business_object, self.user)
|
||||
self.assertTrue(success)
|
||||
current = services.get_business_object_current_state(self.business_object)
|
||||
self.assertEqual(current.id, self.state2.id)
|
||||
|
||||
# 第三次回退
|
||||
success, _ = services.step_back_one_state(self.business_object, self.user)
|
||||
self.assertTrue(success)
|
||||
self.assertEqual(services.get_overall_status(self.business_object), 'not_started')
|
||||
|
||||
# 第四次回退(应该失败)
|
||||
success, message = services.step_back_one_state(self.business_object, self.user)
|
||||
self.assertFalse(success)
|
||||
self.assertIn('没有任何状态流转记录', message)
|
||||
|
||||
def test_advance_after_step_back(self):
|
||||
"""测试回退后再前进"""
|
||||
# 推进两步
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
|
||||
# 回退一步
|
||||
services.step_back_one_state(self.business_object, self.user)
|
||||
|
||||
# 再前进
|
||||
success, _ = services.advance_to_next_state(self.business_object, self.user)
|
||||
self.assertTrue(success)
|
||||
|
||||
# 验证当前在第二个状态
|
||||
current_state = services.get_business_object_current_state(self.business_object)
|
||||
self.assertEqual(current_state.id, self.state3.id)
|
||||
|
||||
def test_step_back_preserves_history(self):
|
||||
"""测试回退操作保留历史记录"""
|
||||
# 推进两步
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
|
||||
# 回退一步
|
||||
services.step_back_one_state(self.business_object, self.user)
|
||||
|
||||
# 验证记录仍然存在,只是被标记为已撤销
|
||||
all_records = models.StateFlowRecord.objects.filter(
|
||||
business_object=self.business_object
|
||||
).order_by('completed_at')
|
||||
|
||||
self.assertEqual(all_records.count(), 2)
|
||||
self.assertFalse(all_records[0].is_cancelled) # 状态1未撤销
|
||||
self.assertTrue(all_records[1].is_cancelled) # 状态2已撤销
|
||||
81
stateflow/tests/test_step_back_api.py
Normal file
81
stateflow/tests/test_step_back_api.py
Normal file
@@ -0,0 +1,81 @@
|
||||
"""
|
||||
测试 step_back API 端点
|
||||
"""
|
||||
from django.test import TestCase
|
||||
from rest_framework.test import APIClient
|
||||
from rest_framework import status
|
||||
from django.contrib.auth import get_user_model
|
||||
from stateflow import models
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class StepBackAPITestCase(TestCase):
|
||||
"""测试单步回退 API"""
|
||||
|
||||
def setUp(self):
|
||||
self.client = APIClient()
|
||||
|
||||
# 创建用户
|
||||
self.user = User.objects.create_user(username='testuser', password='testpass')
|
||||
self.client.force_authenticate(user=self.user)
|
||||
|
||||
# 创建状态
|
||||
self.state1 = models.State.objects.create(name='状态1')
|
||||
self.state2 = models.State.objects.create(name='状态2')
|
||||
self.state3 = models.State.objects.create(name='状态3')
|
||||
|
||||
# 创建流程
|
||||
self.process = models.Process.objects.create(name='测试流程')
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state1, order=1)
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state2, order=2)
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state3, order=3)
|
||||
|
||||
# 创建业务对象
|
||||
self.business_object = models.BusinessObject.objects.create(
|
||||
name='测试对象',
|
||||
process=self.process
|
||||
)
|
||||
|
||||
def test_step_back_api_success(self):
|
||||
"""测试回退 API 成功"""
|
||||
# 先推进两步
|
||||
self.client.post(f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/')
|
||||
self.client.post(f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/')
|
||||
|
||||
# 回退一步
|
||||
response = self.client.post(f'/api/v1/stateflow/business-objects/{self.business_object.id}/step_back/')
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertTrue(response.data['success'])
|
||||
self.assertIn('状态2', response.data['message'])
|
||||
self.assertIn('business_object', response.data)
|
||||
|
||||
def test_step_back_api_not_started(self):
|
||||
"""测试未开始状态回退失败"""
|
||||
response = self.client.post(f'/api/v1/stateflow/business-objects/{self.business_object.id}/step_back/')
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertFalse(response.data['success'])
|
||||
self.assertIn('没有任何状态流转记录', response.data['message'])
|
||||
|
||||
def test_advance_step_back_advance_cycle(self):
|
||||
"""测试前进-回退-前进循环"""
|
||||
# 前进
|
||||
response1 = self.client.post(f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/')
|
||||
self.assertEqual(response1.status_code, status.HTTP_200_OK)
|
||||
|
||||
# 回退
|
||||
response2 = self.client.post(f'/api/v1/stateflow/business-objects/{self.business_object.id}/step_back/')
|
||||
self.assertEqual(response2.status_code, status.HTTP_200_OK)
|
||||
|
||||
# 再前进
|
||||
response3 = self.client.post(f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/')
|
||||
self.assertEqual(response3.status_code, status.HTTP_200_OK)
|
||||
|
||||
# 验证数据库中有已撤销的记录
|
||||
cancelled_count = models.StateFlowRecord.objects.filter(
|
||||
business_object=self.business_object,
|
||||
is_cancelled=True
|
||||
).count()
|
||||
self.assertEqual(cancelled_count, 1)
|
||||
Reference in New Issue
Block a user