1
0
forked from erp-dev/erp

feat: stateflow v2 (params required)

This commit is contained in:
2025-11-15 19:39:15 +08:00
parent 9ad404a365
commit 9c38e2ac09
24 changed files with 3670 additions and 547 deletions

View File

@@ -257,11 +257,13 @@ class PrintingJobViewSet(viewsets.ModelViewSet):
create: 创建款式明细
update: 更新款式明细
partial_update: 部分更新款式明细
advance_to_next_state: 推进到下一个状态
step_back_one_state: 回退一步
completed_states: 查询已完成的流程列表
timeline: 获取流程时间线
注意:状态流转功能已迁移至 stateflow 模块的业务对象接口
- 推进到下一步: POST /api/v1/stateflow/business-objects/{business_object_id}/advance/
- 回退一步: POST /api/v1/stateflow/business-objects/{business_object_id}/step_back/
查询参数:
- printing_order: 印染订单ID
- product: 产品ID
@@ -306,76 +308,6 @@ class PrintingJobViewSet(viewsets.ModelViewSet):
status=status.HTTP_405_METHOD_NOT_ALLOWED
)
@action(detail=True, methods=['post'], url_path='advance-to-next-state')
def advance_to_next_state(self, request, pk=None):
"""
推进到下一个状态
将印染任务推进到下一个流程状态
"""
job = self.get_object()
if not job.business_object:
return Response(
{'detail': '该任务没有关联的流程实例'},
status=status.HTTP_400_BAD_REQUEST
)
from stateflow import services as stateflow_services
success, message = stateflow_services.advance_to_next_state(
job.business_object, request.user
)
if not success:
return Response(
{'detail': message},
status=status.HTTP_400_BAD_REQUEST
)
# 重新获取 job 以刷新状态
job.refresh_from_db()
serializer = PrintingJobDetailSerializer(job)
return Response({
'detail': message,
'data': serializer.data
})
@action(detail=True, methods=['post'], url_path='step-back-one-state')
def step_back_one_state(self, request, pk=None):
"""
回退一步
将印染任务回退到上一个流程状态
"""
job = self.get_object()
if not job.business_object:
return Response(
{'detail': '该任务没有关联的流程实例'},
status=status.HTTP_400_BAD_REQUEST
)
from stateflow import services as stateflow_services
success, message = stateflow_services.step_back_one_state(
job.business_object, request.user
)
if not success:
return Response(
{'detail': message},
status=status.HTTP_400_BAD_REQUEST
)
# 重新获取 job 以刷新状态
job.refresh_from_db()
serializer = PrintingJobDetailSerializer(job)
return Response({
'detail': message,
'data': serializer.data
})
@action(detail=True, methods=['get'], url_path='completed-states')
def completed_states(self, request, pk=None):
"""
@@ -520,11 +452,13 @@ class PlateOrderViewSet(viewsets.ModelViewSet):
partial_update: 部分更新开版订单
invalidate: 作废开版订单
activate: 恢复开版订单
advance_to_next_state: 推进到下一个状态
step_back_one_state: 回退一步
completed_states: 查询已完成的流程列表
timeline: 获取流程时间线
注意:状态流转功能已迁移至 stateflow 模块的业务对象接口
- 推进到下一步: POST /api/v1/stateflow/business-objects/{business_object_id}/advance/
- 回退一步: POST /api/v1/stateflow/business-objects/{business_object_id}/step_back/
查询参数:
- customer: 客户ID
- customer_name: 客户名称(模糊查询)
@@ -653,78 +587,6 @@ class PlateOrderViewSet(viewsets.ModelViewSet):
'data': serializer.data
})
@action(detail=True, methods=['post'], url_path='advance-to-next-state')
def advance_to_next_state(self, request, pk=None):
"""
推进到下一个状态
将开版订单推进到下一个流程状态
"""
plate_order = self.get_object()
if not plate_order.business_object:
return Response(
{'detail': '该开版订单没有关联的流程实例'},
status=status.HTTP_400_BAD_REQUEST
)
from stateflow import services as stateflow_services
success, message = stateflow_services.advance_to_next_state(
plate_order.business_object, request.user
)
if not success:
return Response(
{'detail': message},
status=status.HTTP_400_BAD_REQUEST
)
# 重新获取 plate_order 以刷新状态
plate_order.refresh_from_db()
from .serializers import PlateOrderDetailSerializer
serializer = PlateOrderDetailSerializer(plate_order, context={'request': request})
return Response({
'detail': message,
'data': serializer.data
})
@action(detail=True, methods=['post'], url_path='step-back-one-state')
def step_back_one_state(self, request, pk=None):
"""
回退一步
将开版订单回退到上一个流程状态
"""
plate_order = self.get_object()
if not plate_order.business_object:
return Response(
{'detail': '该开版订单没有关联的流程实例'},
status=status.HTTP_400_BAD_REQUEST
)
from stateflow import services as stateflow_services
success, message = stateflow_services.step_back_one_state(
plate_order.business_object, request.user
)
if not success:
return Response(
{'detail': message},
status=status.HTTP_400_BAD_REQUEST
)
# 重新获取 plate_order 以刷新状态
plate_order.refresh_from_db()
from .serializers import PlateOrderDetailSerializer
serializer = PlateOrderDetailSerializer(plate_order, context={'request': request})
return Response({
'detail': message,
'data': serializer.data
})
@action(detail=True, methods=['get'], url_path='completed-states')
def completed_states(self, request, pk=None):
"""

View File

@@ -117,16 +117,32 @@ class BusinessObjectViewSet(viewsets.ModelViewSet):
@action(detail=True, methods=['post'])
def advance(self, request, pk=None):
"""推进到下一个状态"""
"""
推进到下一个状态
请求体:
{
"parameters": {
"temperature": "25.5",
"humidity": "60%",
"operator": "张三"
}
}
参数可选,但如果下一个状态有必填参数,则必须提供
"""
business_object = self.get_object()
user = request.user
parameters = request.data.get('parameters', {})
success, message = services.advance_to_next_state(business_object, user)
success, message, state_log = services.advance_to_next_state(business_object, user, **parameters)
if success:
from stateflow.serializers import StateFlowRecordWithParametersSerializer
return Response({
'success': True,
'message': message,
'state_log': StateFlowRecordWithParametersSerializer(state_log).data if state_log else None,
'business_object': BusinessObjectDetailSerializer(business_object).data
})
else:
@@ -187,3 +203,174 @@ class BusinessObjectViewSet(viewsets.ModelViewSet):
]
return Response(timeline_data)
@action(detail=True, methods=['get'])
def next_pending_state(self, request, pk=None):
"""
获取最近一个待执行节点
查询参数:
- include_parameters: 是否包含参数列表,默认 true (可选值: true, false)
"""
business_object = self.get_object()
include_parameters = request.query_params.get('include_parameters', 'true').lower() == 'true'
next_state = services.get_next_pending_state(business_object, include_parameters=include_parameters)
if next_state is None:
return Response({
'next_state': None,
'message': '没有待执行节点(未开始或已完成)'
})
from stateflow.serializers import StateParameterSerializer
result = {
'state': StateListSerializer(next_state['state']).data,
'order': next_state['order'],
}
if 'parameters' in next_state:
result['parameters'] = StateParameterSerializer(next_state['parameters'], many=True).data
return Response(result)
@action(detail=True, methods=['get'])
def pending_states(self, request, pk=None):
"""获取所有待执行节点列表(不包含参数)"""
business_object = self.get_object()
pending_states = services.get_all_pending_states(business_object)
result = [
{
'state': StateListSerializer(item['state']).data,
'order': item['order'],
}
for item in pending_states
]
return Response({
'count': len(result),
'pending_states': result
})
@action(detail=True, methods=['get'], url_path='current_state_parameters')
def current_state_parameters(self, request, pk=None):
"""
获取当前状态(最后完成的状态)的参数列表
查询参数:
- required_only: 是否只返回必填参数,默认 false (可选值: true, false)
注意current_state 表示最后完成的状态,如果未开始则返回空
"""
business_object = self.get_object()
required_only = request.query_params.get('required_only', 'false').lower() == 'true'
current_state = services.get_business_object_current_state(business_object)
if current_state is None:
return Response({
'parameters': [],
'message': '尚未完成任何状态'
})
from stateflow.serializers import StateParameterSerializer
parameters = services.get_state_parameters(current_state, required_only=required_only)
return Response({
'state': StateListSerializer(current_state).data,
'parameters': StateParameterSerializer(parameters, many=True).data,
'count': len(parameters)
})
@action(detail=True, methods=['post'], url_path='state-logs/(?P<log_id>[^/.]+)/add-parameters')
def add_parameters_to_log(self, request, pk=None, log_id=None):
"""
为状态流转记录补充参数
POST /api/v1/stateflow/business-objects/{id}/state-logs/{log_id}/add-parameters/
请求体:
{
"parameters": {
"additional_note": "发现轻微瑕疵",
"temperature": "26.0"
},
"remark": "质检员补充"
}
"""
business_object = self.get_object()
try:
state_log = business_object.state_logs.get(id=log_id)
except models.StateFlowRecord.DoesNotExist:
return Response({
'success': False,
'message': '状态流转记录不存在'
}, status=status.HTTP_404_NOT_FOUND)
parameters = request.data.get('parameters', {})
remark = request.data.get('remark', '')
if not parameters:
return Response({
'success': False,
'message': '参数不能为空'
}, status=status.HTTP_400_BAD_REQUEST)
from stateflow.serializers import StateLogParameterRecordSerializer
record = services.add_parameters_to_state_log(state_log, remark=remark, **parameters)
return Response({
'success': True,
'parameter_record': StateLogParameterRecordSerializer(record).data
})
@action(detail=True, methods=['get'], url_path='state-logs/(?P<log_id>[^/.]+)/parameters')
def get_log_parameters(self, request, pk=None, log_id=None):
"""
获取状态流转记录的所有参数记录
GET /api/v1/stateflow/business-objects/{id}/state-logs/{log_id}/parameters/
GET /api/v1/stateflow/business-objects/{id}/state-logs/{log_id}/parameters/?key=temperature
GET /api/v1/stateflow/business-objects/{id}/state-logs/{log_id}/parameters/?include_cancelled=true
查询参数:
- key: 指定参数键,返回该参数的历史(可选)
- include_cancelled: 是否包含已撤销状态的参数,默认 false可选
"""
business_object = self.get_object()
try:
state_log = business_object.state_logs.get(id=log_id)
except models.StateFlowRecord.DoesNotExist:
return Response({
'success': False,
'message': '状态流转记录不存在'
}, status=status.HTTP_404_NOT_FOUND)
key = request.query_params.get('key')
include_cancelled = request.query_params.get('include_cancelled', 'false').lower() == 'true'
if key:
# 返回指定参数的历史
history = state_log.get_parameter_history(key, include_cancelled=include_cancelled)
return Response({
'state_log_id': state_log.id,
'key': key,
'is_cancelled': state_log.is_cancelled,
'history': history
})
else:
# 返回所有参数记录
from stateflow.serializers import StateLogParameterRecordSerializer
records = state_log.parameter_records.all()
return Response({
'state_log_id': state_log.id,
'is_cancelled': state_log.is_cancelled,
'count': records.count(),
'summary': state_log.get_all_parameters_summary(include_cancelled=include_cancelled),
'records': StateLogParameterRecordSerializer(records, many=True).data if include_cancelled or not state_log.is_cancelled else []
})

View File

@@ -2,13 +2,16 @@
State API ViewSet
"""
from rest_framework import viewsets, filters
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.pagination import LimitOffsetPagination
from django_filters.rest_framework import DjangoFilterBackend
from stateflow import models
from stateflow import models, services
from stateflow.serializers import (
StateListSerializer,
StateDetailSerializer,
StateCreateUpdateSerializer,
StateParameterSerializer,
)
@@ -51,3 +54,23 @@ class StateViewSet(viewsets.ModelViewSet):
if self.action == 'retrieve':
queryset = queryset.prefetch_related('parameters')
return queryset
@action(detail=True, methods=['get'])
def parameters(self, request, pk=None):
"""
获取状态的参数列表
查询参数:
- required_only: 是否只返回必填参数,默认 false (可选值: true, false)
"""
state = self.get_object()
required_only = request.query_params.get('required_only', 'false').lower() == 'true'
parameters = services.get_state_parameters(state, required_only=required_only)
return Response({
'state_id': state.id,
'state_name': state.name,
'parameters': StateParameterSerializer(parameters, many=True).data,
'count': len(parameters)
})