1
0
forked from erp-dev/erp

feat: new modul (stateflow)

This commit is contained in:
2025-11-11 18:05:10 +08:00
parent 2aafb93aad
commit 9898d71a7e
30 changed files with 3723 additions and 7 deletions

350
stateflow/serializers.py Normal file
View File

@@ -0,0 +1,350 @@
"""
Stateflow 序列化器
"""
from rest_framework import serializers
from django.db import transaction
from django.contrib.contenttypes.models import ContentType
from stateflow import models
class StateParameterSerializer(serializers.ModelSerializer):
"""状态参数序列化器"""
class Meta:
model = models.StateParameter
fields = ['id', 'key', 'value', 'description']
read_only_fields = ['id']
class StateListSerializer(serializers.ModelSerializer):
"""状态列表序列化器(不包含参数)"""
class Meta:
model = models.State
fields = ['id', 'name', 'description', 'created_at', 'updated_at']
read_only_fields = ['id', 'created_at', 'updated_at']
class StateDetailSerializer(serializers.ModelSerializer):
"""状态详情序列化器(包含参数)"""
parameters = StateParameterSerializer(many=True, read_only=True)
class Meta:
model = models.State
fields = ['id', 'name', 'description', 'parameters', 'created_at', 'updated_at']
read_only_fields = ['id', 'created_at', 'updated_at']
class StateCreateUpdateSerializer(serializers.ModelSerializer):
"""状态创建/更新序列化器"""
parameters = StateParameterSerializer(many=True, required=False)
class Meta:
model = models.State
fields = ['name', 'description', 'parameters']
@transaction.atomic
def create(self, validated_data):
parameters_data = validated_data.pop('parameters', [])
state = models.State.objects.create(**validated_data)
# 创建参数
for param_data in parameters_data:
models.StateParameter.objects.create(state=state, **param_data)
return state
@transaction.atomic
def update(self, instance, validated_data):
parameters_data = validated_data.pop('parameters', None)
# 更新基本字段
instance.name = validated_data.get('name', instance.name)
instance.description = validated_data.get('description', instance.description)
instance.save()
# 如果提供了参数数据,更新参数
if parameters_data is not None:
# 删除旧参数
instance.parameters.all().delete()
# 创建新参数
for param_data in parameters_data:
models.StateParameter.objects.create(state=instance, **param_data)
return instance
# Process 序列化器
class ProcessNodeSerializer(serializers.ModelSerializer):
"""流程节点序列化器"""
state_id = serializers.IntegerField(source='state.id', read_only=True)
state_name = serializers.CharField(source='state.name', read_only=True)
class Meta:
model = models.ProcessNode
fields = ['id', 'state_id', 'state_name', 'order']
read_only_fields = ['id']
class ProcessNodeCreateSerializer(serializers.Serializer):
"""流程节点创建序列化器"""
state_id = serializers.IntegerField(help_text="状态ID")
order = serializers.IntegerField(help_text="排序号", min_value=0)
def validate_state_id(self, value):
"""验证状态是否存在"""
try:
models.State.objects.get(id=value)
except models.State.DoesNotExist:
raise serializers.ValidationError(f"状态ID {value} 不存在")
return value
class ProcessListSerializer(serializers.ModelSerializer):
"""流程列表序列化器(不包含节点)"""
node_count = serializers.IntegerField(read_only=True, help_text="节点数量")
class Meta:
model = models.Process
fields = ['id', 'name', 'description', 'node_count', 'created_at', 'updated_at']
read_only_fields = ['id', 'created_at', 'updated_at']
class ProcessDetailSerializer(serializers.ModelSerializer):
"""流程详情序列化器(包含节点)"""
nodes = ProcessNodeSerializer(source='process_nodes', many=True, read_only=True)
class Meta:
model = models.Process
fields = ['id', 'name', 'description', 'nodes', 'created_at', 'updated_at']
read_only_fields = ['id', 'created_at', 'updated_at']
class ProcessCreateUpdateSerializer(serializers.ModelSerializer):
"""流程创建/更新序列化器"""
nodes = ProcessNodeCreateSerializer(many=True, required=False, help_text="流程节点列表")
class Meta:
model = models.Process
fields = ['name', 'description', 'nodes']
@transaction.atomic
def create(self, validated_data):
nodes_data = validated_data.pop('nodes', [])
process = models.Process.objects.create(**validated_data)
# 创建节点
for node_data in nodes_data:
models.ProcessNode.objects.create(
process=process,
state_id=node_data['state_id'],
order=node_data['order']
)
return process
@transaction.atomic
def update(self, instance, validated_data):
nodes_data = validated_data.pop('nodes', None)
# 更新基本字段
instance.name = validated_data.get('name', instance.name)
instance.description = validated_data.get('description', instance.description)
instance.save()
# 如果提供了节点数据,更新节点
if nodes_data is not None:
# 删除旧节点
instance.process_nodes.all().delete()
# 创建新节点
for node_data in nodes_data:
models.ProcessNode.objects.create(
process=instance,
state_id=node_data['state_id'],
order=node_data['order']
)
return instance
# BusinessObject 序列化器
class StateFlowRecordSerializer(serializers.ModelSerializer):
"""状态流转记录序列化器(只读)"""
state_name = serializers.CharField(source='state.name', read_only=True)
completed_by_username = serializers.CharField(source='completed_by.username', read_only=True)
class Meta:
model = models.StateFlowRecord
fields = [
'id', 'state', 'state_name', 'completed_at',
'completed_by', 'completed_by_username',
'is_cancelled', 'cancelled_at'
]
read_only_fields = ['id', 'completed_at', 'is_cancelled', 'cancelled_at']
class BusinessObjectListSerializer(serializers.ModelSerializer):
"""业务对象列表序列化器"""
process_name = serializers.CharField(source='process.name', read_only=True)
current_state_name = serializers.SerializerMethodField()
overall_status = serializers.SerializerMethodField()
progress_percentage = serializers.SerializerMethodField()
# 关联对象信息
content_type_name = serializers.SerializerMethodField()
class Meta:
model = models.BusinessObject
fields = [
'id', 'name', 'process', 'process_name',
'current_state_name', 'overall_status', 'progress_percentage',
'content_type', 'object_id', 'content_type_name',
'description', 'created_at', 'updated_at'
]
def get_current_state_name(self, obj):
from . import services
state = services.get_business_object_current_state(obj)
return state.name if state else None
def get_overall_status(self, obj):
from . import services
return services.get_overall_status(obj)
def get_progress_percentage(self, obj):
from . import services
return round(services.get_progress_percentage(obj), 2)
def get_content_type_name(self, obj):
if obj.content_type:
return f"{obj.content_type.app_label}.{obj.content_type.model}"
return None
class BusinessObjectDetailSerializer(serializers.ModelSerializer):
"""业务对象详情序列化器"""
process_detail = ProcessDetailSerializer(source='process', read_only=True)
current_state = serializers.SerializerMethodField()
overall_status = serializers.SerializerMethodField()
progress_percentage = serializers.SerializerMethodField()
timeline = serializers.SerializerMethodField()
state_logs = StateFlowRecordSerializer(many=True, read_only=True)
# 关联对象信息
content_type_name = serializers.SerializerMethodField()
class Meta:
model = models.BusinessObject
fields = [
'id', 'name', 'process', 'process_detail',
'current_state', 'overall_status', 'progress_percentage',
'timeline', 'state_logs',
'content_type', 'object_id', 'content_type_name',
'description', 'created_at', 'updated_at'
]
def get_current_state(self, obj):
from . import services
state = services.get_business_object_current_state(obj)
if state:
return StateListSerializer(state).data
return None
def get_overall_status(self, obj):
from . import services
return services.get_overall_status(obj)
def get_progress_percentage(self, obj):
from . import services
return round(services.get_progress_percentage(obj), 2)
def get_timeline(self, obj):
from . import services
timeline = services.get_business_object_state_timeline(obj)
return [
{
'state': StateListSerializer(item['state']).data,
'status': item['status'],
'order': item['order'],
'completed_at': item['completed_at'],
'completed_by': item['completed_by'].username if item['completed_by'] else None,
'cancelled_at': item['cancelled_at'],
'is_cancelled': item['is_cancelled'],
}
for item in timeline
]
def get_content_type_name(self, obj):
if obj.content_type:
return f"{obj.content_type.app_label}.{obj.content_type.model}"
return None
class BusinessObjectCreateUpdateSerializer(serializers.ModelSerializer):
"""业务对象创建/更新序列化器"""
# 支持通过 app_label.model 字符串指定 content_type
content_type_str = serializers.CharField(
write_only=True,
required=False,
allow_null=True,
help_text="格式: app_label.model, 例如: basic_info.order"
)
class Meta:
model = models.BusinessObject
fields = [
'name', 'process', 'description',
'content_type', 'object_id', 'content_type_str'
]
extra_kwargs = {
'content_type': {'required': False, 'allow_null': True},
'object_id': {'required': False, 'allow_null': True},
}
def validate(self, attrs):
"""验证 content_type 和 object_id 的一致性"""
content_type = attrs.get('content_type')
content_type_str = attrs.get('content_type_str')
object_id = attrs.get('object_id')
# 如果提供了 content_type_str解析并设置 content_type
if content_type_str:
try:
app_label, model = content_type_str.split('.')
content_type = ContentType.objects.get(app_label=app_label, model=model)
attrs['content_type'] = content_type
except (ValueError, ContentType.DoesNotExist):
raise serializers.ValidationError({
'content_type_str': f'无效的 content_type 格式: {content_type_str}'
})
# 验证:如果提供了 content_type必须提供 object_id
if content_type and not object_id:
raise serializers.ValidationError({
'object_id': '提供了关联对象类型必须同时提供对象ID'
})
# 验证:如果提供了 object_id必须提供 content_type
if object_id and not content_type:
raise serializers.ValidationError({
'content_type': '提供了对象ID必须同时提供关联对象类型'
})
# 移除临时字段
attrs.pop('content_type_str', None)
return attrs
@transaction.atomic
def create(self, validated_data):
return models.BusinessObject.objects.create(**validated_data)
@transaction.atomic
def update(self, instance, validated_data):
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.save()
return instance