from rest_framework import serializers, status, permissions from rest_framework.response import Response from rest_framework.views import APIView from django.core.exceptions import FieldDoesNotExist from django.db import transaction from stateflow import models as stateflow_models from stateflow import services as stateflow_services class BusinessObjectCloneRequestSerializer(serializers.Serializer): business_object_id = serializers.IntegerField(min_value=1) # 调用方必须显式传入 content_type 与 object_id: # - content_type:仅用于校验与源对象一致(不做额外校验) # - object_id:克隆后的新业务对象 ID(必须与源对象不同) content_type = serializers.IntegerField(min_value=1, allow_null=True) object_id = serializers.IntegerField(min_value=1, allow_null=True) class BusinessObjectCloneView(APIView): """ 克隆 BusinessObject(含所有状态流转记录与工艺参数记录) POST /api/v2/stateflow/business-objects/clone/ 请求体: { "business_object_id": 123, "content_type": 9, "object_id": 456 } 返回: { "business_object_id": 456 } """ permission_classes = [permissions.IsAuthenticated] def post(self, request): srz = BusinessObjectCloneRequestSerializer(data=request.data) srz.is_valid(raise_exception=True) bo_id = srz.validated_data['business_object_id'] expected_content_type_id = srz.validated_data.get('content_type') new_object_id = srz.validated_data.get('object_id') try: bo = stateflow_models.BusinessObject.objects.get(id=bo_id) except stateflow_models.BusinessObject.DoesNotExist: return Response({'detail': 'business_object 不存在'}, status=status.HTTP_404_NOT_FOUND) try: with transaction.atomic(): cloned = stateflow_services.clone_business_object( bo, new_object_id=new_object_id, expected_content_type_id=expected_content_type_id, ) # 兼容 printing 的“克隆=新建后复制流程进度”工作流: # 目标业务对象(PlateOrder/PrintingJob)本身持有 business_object 字段, # 需要把克隆后的 BusinessObject 回写到目标对象上,否则前端看不到节点/参数历史。 try: target_obj = cloned.content_type.get_object_for_this_type(pk=cloned.object_id) except Exception: target_obj = None if target_obj is not None: try: bo_field = target_obj._meta.get_field('business_object') except FieldDoesNotExist: bo_field = None if bo_field is not None and bo_field.related_model is stateflow_models.BusinessObject: old_bo_id = getattr(target_obj, 'business_object_id', None) target_obj.business_object = cloned target_obj.save(update_fields=['business_object']) # 如果目标对象在创建时自动生成了一个“空 business_object”,这里做安全清理, # 避免同一个业务对象存在多个 BusinessObject 绑定导致后续审计/排查混乱。 if old_bo_id and old_bo_id != cloned.id: old_bo = ( stateflow_models.BusinessObject.objects .filter(id=old_bo_id) .select_related('content_type') .first() ) if ( old_bo and old_bo.state_logs.count() == 0 and old_bo.content_type_id == cloned.content_type_id and old_bo.object_id == cloned.object_id ): old_bo.delete() except ValueError as e: return Response({'detail': str(e)}, status=status.HTTP_400_BAD_REQUEST) return Response({'business_object_id': cloned.id})