forked from erp-dev/erp
436 lines
17 KiB
Python
436 lines
17 KiB
Python
"""
|
||
BusinessObject API ViewSet
|
||
"""
|
||
from rest_framework import viewsets, filters, status
|
||
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 django_filters import rest_framework as django_filters
|
||
from stateflow import models, services
|
||
from stateflow.serializers import (
|
||
BusinessObjectListSerializer,
|
||
BusinessObjectDetailSerializer,
|
||
BusinessObjectCreateUpdateSerializer,
|
||
StateListSerializer,
|
||
)
|
||
|
||
|
||
class BusinessObjectFilterSet(django_filters.FilterSet):
|
||
"""业务对象过滤器"""
|
||
name = django_filters.CharFilter(lookup_expr='icontains')
|
||
process_name = django_filters.CharFilter(field_name='process__name', lookup_expr='icontains')
|
||
overall_status = django_filters.ChoiceFilter(
|
||
choices=[
|
||
('not_started', '未开始'),
|
||
('in_progress', '进行中'),
|
||
('completed', '已完成')
|
||
],
|
||
method='filter_overall_status'
|
||
)
|
||
content_type_str = django_filters.CharFilter(method='filter_content_type')
|
||
has_content_object = django_filters.BooleanFilter(method='filter_has_content_object')
|
||
|
||
def filter_overall_status(self, queryset, name, value):
|
||
"""过滤整体状态"""
|
||
business_object_ids = []
|
||
for obj in queryset:
|
||
if services.get_overall_status(obj) == value:
|
||
business_object_ids.append(obj.id)
|
||
return queryset.filter(id__in=business_object_ids)
|
||
|
||
def filter_content_type(self, queryset, name, value):
|
||
"""通过 app_label.model 字符串过滤"""
|
||
try:
|
||
from django.contrib.contenttypes.models import ContentType
|
||
app_label, model = value.split('.')
|
||
content_type = ContentType.objects.get(app_label=app_label, model=model)
|
||
return queryset.filter(content_type=content_type)
|
||
except:
|
||
return queryset.none()
|
||
|
||
def filter_has_content_object(self, queryset, name, value):
|
||
"""过滤是否有关联对象"""
|
||
if value:
|
||
return queryset.exclude(content_type__isnull=True)
|
||
else:
|
||
return queryset.filter(content_type__isnull=True)
|
||
|
||
class Meta:
|
||
model = models.BusinessObject
|
||
fields = ['name', 'process', 'process_name', 'overall_status',
|
||
'content_type_str', 'has_content_object']
|
||
|
||
|
||
class BusinessObjectViewSet(viewsets.ModelViewSet):
|
||
"""
|
||
业务对象 CRUD 接口
|
||
|
||
业务对象是流程的实例,可以选择性地关联到实际的业务模型(如订单、工单等)
|
||
|
||
list: 获取业务对象列表
|
||
retrieve: 获取业务对象详情(包含时间线和日志)
|
||
create: 创建业务对象
|
||
update: 更新业务对象
|
||
partial_update: 部分更新业务对象
|
||
destroy: 删除业务对象
|
||
|
||
查询参数:
|
||
- name: 按名称模糊查询
|
||
- process: 按流程ID过滤
|
||
- process_name: 按流程名称模糊查询
|
||
- overall_status: 按整体状态过滤 (not_started/in_progress/completed)
|
||
- content_type_str: 按关联对象类型过滤,格式: app_label.model
|
||
- has_content_object: 是否有关联对象 (true/false)
|
||
- search: 全文搜索(名称和描述)
|
||
- ordering: 排序字段
|
||
"""
|
||
queryset = models.BusinessObject.objects.all()
|
||
pagination_class = LimitOffsetPagination
|
||
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
|
||
filterset_class = BusinessObjectFilterSet
|
||
search_fields = ['name', 'description']
|
||
ordering_fields = ['id', 'name', 'created_at', 'updated_at']
|
||
ordering = ['-created_at']
|
||
|
||
def get_serializer_class(self):
|
||
"""根据动作选择序列化器"""
|
||
if self.action == 'list':
|
||
return BusinessObjectListSerializer
|
||
elif self.action in ['create', 'update', 'partial_update']:
|
||
return BusinessObjectCreateUpdateSerializer
|
||
else: # retrieve
|
||
return BusinessObjectDetailSerializer
|
||
|
||
def get_queryset(self):
|
||
"""优化查询"""
|
||
queryset = super().get_queryset()
|
||
if self.action == 'list':
|
||
queryset = queryset.select_related('process', 'content_type')
|
||
elif self.action == 'retrieve':
|
||
queryset = queryset.select_related('process', 'content_type').prefetch_related(
|
||
'state_logs__state',
|
||
'state_logs__completed_by',
|
||
'process__process_nodes__state'
|
||
)
|
||
return queryset
|
||
|
||
@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, 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:
|
||
return Response({
|
||
'success': False,
|
||
'message': message
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
@action(detail=True, methods=['post'])
|
||
def step_back(self, request, pk=None):
|
||
"""回退一步(撤销最后一次完成的状态)"""
|
||
business_object = self.get_object()
|
||
user = request.user
|
||
|
||
success, message = services.step_back_one_state(business_object, user)
|
||
|
||
if success:
|
||
return Response({
|
||
'success': True,
|
||
'message': message,
|
||
'business_object': BusinessObjectDetailSerializer(business_object).data
|
||
})
|
||
else:
|
||
return Response({
|
||
'success': False,
|
||
'message': message
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
@action(detail=True, methods=['post'])
|
||
def reset(self, request, pk=None):
|
||
"""重置进度"""
|
||
business_object = self.get_object()
|
||
services.reset_business_object_progress(business_object)
|
||
|
||
return Response({
|
||
'success': True,
|
||
'message': '进度已重置',
|
||
'business_object': BusinessObjectDetailSerializer(business_object).data
|
||
})
|
||
|
||
@action(detail=True, methods=['get'])
|
||
def timeline(self, request, pk=None):
|
||
"""获取状态时间线"""
|
||
business_object = self.get_object()
|
||
timeline = services.get_business_object_state_timeline(business_object)
|
||
|
||
timeline_data = [
|
||
{
|
||
'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
|
||
]
|
||
|
||
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=['get'], url_path='process-nodes')
|
||
def process_nodes(self, request, pk=None):
|
||
"""
|
||
获取业务对象所属流程的所有状态节点
|
||
|
||
返回流程中所有节点的列表,按顺序排列
|
||
"""
|
||
business_object = self.get_object()
|
||
nodes = services.get_process_nodes(business_object)
|
||
|
||
return Response({
|
||
'count': len(nodes),
|
||
'nodes': [
|
||
{
|
||
'id': node['id'],
|
||
'state_id': node['state_id'],
|
||
'state_name': node['state_name'],
|
||
'order': node['order']
|
||
}
|
||
for node in nodes
|
||
]
|
||
})
|
||
|
||
@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')
|
||
def get_state_logs(self, request, pk=None):
|
||
"""
|
||
获取业务对象的所有状态流转记录列表
|
||
|
||
GET /api/v1/stateflow/business-objects/{id}/state-logs/
|
||
GET /api/v1/stateflow/business-objects/{id}/state-logs/?include_parameters=false
|
||
GET /api/v1/stateflow/business-objects/{id}/state-logs/?include_cancelled=true
|
||
|
||
查询参数:
|
||
- include_parameters: 是否包含工艺参数,默认 true(可选值: true, false)
|
||
- include_cancelled: 是否包含已撤销的记录,默认 true(可选值: true, false)
|
||
"""
|
||
business_object = self.get_object()
|
||
include_parameters = request.query_params.get('include_parameters', 'true').lower() == 'true'
|
||
include_cancelled = request.query_params.get('include_cancelled', 'true').lower() == 'true'
|
||
|
||
# 获取状态流转记录
|
||
state_logs = business_object.state_logs.select_related('state', 'completed_by').order_by('completed_at')
|
||
|
||
if not include_cancelled:
|
||
state_logs = state_logs.filter(is_cancelled=False)
|
||
|
||
# 序列化
|
||
if include_parameters:
|
||
from stateflow.serializers import StateFlowRecordWithParametersSerializer
|
||
serializer = StateFlowRecordWithParametersSerializer(state_logs, many=True)
|
||
else:
|
||
from stateflow.serializers import StateFlowRecordSerializer
|
||
serializer = StateFlowRecordSerializer(state_logs, many=True)
|
||
|
||
return Response({
|
||
'count': state_logs.count(),
|
||
'state_logs': serializer.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 []
|
||
})
|