forked from erp-dev/erp
402 lines
14 KiB
Python
402 lines
14 KiB
Python
"""
|
||
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):
|
||
"""状态参数序列化器"""
|
||
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):
|
||
"""验证 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
|