1
0
forked from erp-dev/erp
Files
erpnew/stateflow/serializers.py

419 lines
15 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Stateflow 序列化器
"""
from rest_framework import serializers
from django.db import transaction
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from stateflow import models
class StateParameterSerializer(serializers.ModelSerializer):
"""状态参数序列化器"""
attachment_url = serializers.SerializerMethodField(read_only=True)
class Meta:
model = models.StateParameter
fields = ['id', 'key', 'value', 'attachment', 'attachment_url', 'description', 'is_required', 'is_image_path']
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):
"""状态列表序列化器(不包含参数)"""
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):
"""状态创建/更新序列化器"""
parameter_ids = serializers.ListField(
child=serializers.IntegerField(),
required=False,
write_only=True,
help_text="参数ID列表"
)
parameters = StateParameterSerializer(many=True, read_only=True)
class Meta:
model = models.State
fields = ['name', 'description', 'parameter_ids', 'parameters']
def validate_parameter_ids(self, value):
"""验证参数ID是否存在"""
if value:
existing_ids = set(models.StateParameter.objects.filter(id__in=value).values_list('id', flat=True))
invalid_ids = set(value) - existing_ids
if invalid_ids:
raise serializers.ValidationError(f"参数ID不存在: {invalid_ids}")
return value
@transaction.atomic
def create(self, validated_data):
parameter_ids = validated_data.pop('parameter_ids', [])
state = models.State.objects.create(**validated_data)
# 关联参数(多对多)
if parameter_ids:
state.parameters.set(parameter_ids)
return state
@transaction.atomic
def update(self, instance, validated_data):
parameter_ids = validated_data.pop('parameter_ids', None)
# 更新基本字段
instance.name = validated_data.get('name', instance.name)
instance.description = validated_data.get('description', instance.description)
instance.save()
# 如果提供了参数ID列表更新关联
if parameter_ids is not None:
instance.parameters.set(parameter_ids)
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 StateLogParameterRecordSerializer(serializers.ModelSerializer):
"""状态流转参数记录序列化器"""
class Meta:
model = models.StateLogParameterRecord
fields = ['id', 'state_log', 'parameters', 'created_at', 'remark']
read_only_fields = ['id', 'created_at']
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 StateFlowRecordWithParametersSerializer(serializers.ModelSerializer):
"""状态流转记录序列化器(包含参数摘要)"""
state_name = serializers.CharField(source='state.name', read_only=True)
completed_by_username = serializers.CharField(source='completed_by.username', read_only=True)
parameters_summary = serializers.SerializerMethodField(read_only=True)
class Meta:
model = models.StateFlowRecord
fields = [
'id', 'state', 'state_name', 'completed_at',
'completed_by', 'completed_by_username',
'is_cancelled', 'cancelled_at',
'parameters_summary'
]
read_only_fields = ['id', 'completed_at', 'is_cancelled', 'cancelled_at']
def get_parameters_summary(self, obj):
"""获取参数摘要,包含已撤销状态的参数"""
return obj.get_all_parameters_summary(include_cancelled=True)
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):
"""
业务约束BusinessObject 必须绑定到一个“真实业务对象”。
背景:
- DB 允许 content_type/object_id 为空仅为历史兼容;
- 但业务逻辑要求 BusinessObject 可通过 (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}'
})
# 以“最终值”做校验partial_update 未传字段时要以 instance 的现有值为准
if self.instance is not None:
if 'content_type' not in attrs:
content_type = self.instance.content_type
if 'object_id' not in attrs:
object_id = self.instance.object_id
# 强制绑定content_type/object_id 均不可为空
if content_type is None or object_id is None:
raise serializers.ValidationError({
'detail': 'BusinessObject 必须绑定关联对象content_type 与 object_id 均为必填且不可为空)'
})
# 强制要求绑定对象存在(避免产生“不可追溯”的悬空绑定)
try:
content_type.get_object_for_this_type(pk=object_id)
except ObjectDoesNotExist:
raise serializers.ValidationError({
'object_id': f'关联对象不存在:{content_type.app_label}.{content_type.model} #{object_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