forked from erp-dev/erp
feat: new api for plate order (get_plate_order_by_state_status)
This commit is contained in:
233
api_v2/tests.py
233
api_v2/tests.py
@@ -960,6 +960,239 @@ class PlateOrderByProcessV2APITest(TestCase):
|
||||
self.assertEqual(resp.status_code, 400)
|
||||
|
||||
|
||||
class PlateOrderByStateStatusV2APITest(TestCase):
|
||||
def setUp(self):
|
||||
self.client = APIClient()
|
||||
|
||||
self.merchant = basic_models.Merchant.objects.create(
|
||||
name='开版工厂3',
|
||||
type=basic_models.MerchantTypeEnum.FACTORY,
|
||||
)
|
||||
self.user = get_user_model().objects.create_user(username='state_status_user', password='pass12345')
|
||||
basic_models.Employee.objects.create(
|
||||
merchant=self.merchant,
|
||||
sys_user=self.user,
|
||||
name='开版员工3',
|
||||
)
|
||||
self.client.force_authenticate(user=self.user)
|
||||
|
||||
self.customer = basic_models.Customer.objects.create(
|
||||
merchant=self.merchant,
|
||||
name='客户SS',
|
||||
created_by=None,
|
||||
)
|
||||
|
||||
self.state_prepare = stateflow_models.State.objects.create(name='画图')
|
||||
self.state_target = stateflow_models.State.objects.create(name='调色')
|
||||
self.state_unlinked = stateflow_models.State.objects.create(name='未绑定')
|
||||
self.param_temp = stateflow_models.StateParameter.objects.create(key='temperature', value='25')
|
||||
self.state_target.parameters.add(self.param_temp)
|
||||
|
||||
self.process = stateflow_models.Process.objects.create(name='主流程')
|
||||
self.node_prepare = stateflow_models.ProcessNode.objects.create(process=self.process, state=self.state_prepare, order=0)
|
||||
self.node_target = stateflow_models.ProcessNode.objects.create(process=self.process, state=self.state_target, order=1)
|
||||
|
||||
self.other_process = stateflow_models.Process.objects.create(name='其他流程')
|
||||
stateflow_models.ProcessNode.objects.create(process=self.other_process, state=self.state_target, order=0)
|
||||
|
||||
self.po_not_started = printing_models.PlateOrder.objects.create(
|
||||
customer=self.customer,
|
||||
process=self.process.id,
|
||||
design_code='PO-NOT',
|
||||
style_name='款式N',
|
||||
)
|
||||
self.po_no_logs = printing_models.PlateOrder.objects.create(
|
||||
customer=self.customer,
|
||||
process=self.process.id,
|
||||
design_code='PO-ZERO',
|
||||
style_name='款式Z',
|
||||
)
|
||||
self.po_completed_old = printing_models.PlateOrder.objects.create(
|
||||
customer=self.customer,
|
||||
process=self.process.id,
|
||||
design_code='PO-C-OLD',
|
||||
style_name='款式CO',
|
||||
)
|
||||
self.po_completed_new = printing_models.PlateOrder.objects.create(
|
||||
customer=self.customer,
|
||||
process=self.process.id,
|
||||
design_code='PO-C-NEW',
|
||||
style_name='款式CN',
|
||||
)
|
||||
self.po_cancelled = printing_models.PlateOrder.objects.create(
|
||||
customer=self.customer,
|
||||
process=self.process.id,
|
||||
design_code='PO-CAN',
|
||||
style_name='款式CA',
|
||||
)
|
||||
self.po_other_process = printing_models.PlateOrder.objects.create(
|
||||
customer=self.customer,
|
||||
process=self.other_process.id,
|
||||
design_code='PO-OTHER',
|
||||
style_name='款式OP',
|
||||
)
|
||||
|
||||
self._complete_state(self.po_not_started, self.state_prepare)
|
||||
self._complete_state(self.po_completed_old, self.state_prepare)
|
||||
self._complete_state(self.po_completed_new, self.state_prepare)
|
||||
self._complete_state(self.po_cancelled, self.state_prepare)
|
||||
|
||||
self._complete_state(self.po_completed_old, self.state_target, parameters={'temperature': '28'})
|
||||
self._complete_state(self.po_completed_new, self.state_target, parameters={'temperature': '32'})
|
||||
self._complete_state(self.po_cancelled, self.state_target, is_cancelled=True, parameters={'temperature': '99'})
|
||||
self._complete_state(self.po_other_process, self.state_target, parameters={'temperature': '88'})
|
||||
|
||||
tz = timezone.get_default_timezone()
|
||||
t_old = timezone.make_aware(datetime.datetime(2025, 12, 1, 9, 0, 0), tz)
|
||||
t_new = timezone.make_aware(datetime.datetime(2025, 12, 3, 9, 0, 0), tz)
|
||||
printing_models.PlateOrder.objects.filter(id=self.po_completed_old.id).update(created_at=t_old)
|
||||
printing_models.PlateOrder.objects.filter(id=self.po_completed_new.id).update(created_at=t_new)
|
||||
|
||||
self.url = '/api/v2/plate-orders/by-state-status/'
|
||||
|
||||
def _complete_state(self, plate_order, state, *, is_cancelled=False, parameters=None):
|
||||
log = stateflow_models.StateFlowRecord.objects.create(
|
||||
business_object=plate_order.business_object,
|
||||
state=state,
|
||||
completed_by=self.user,
|
||||
is_cancelled=is_cancelled,
|
||||
)
|
||||
if parameters is not None:
|
||||
stateflow_models.StateLogParameterRecord.objects.create(
|
||||
state_log=log,
|
||||
parameters=parameters,
|
||||
)
|
||||
return log
|
||||
|
||||
def test_completed_status_returns_sorted_plate_orders(self):
|
||||
resp = self.client.get(
|
||||
self.url,
|
||||
{
|
||||
'process_id': self.process.id,
|
||||
'state_id': self.state_target.id,
|
||||
'status': 'completed',
|
||||
}
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertEqual(resp.data['state']['id'], self.state_target.id)
|
||||
self.assertEqual(resp.data['status'], 'completed')
|
||||
ids = [item['id'] for item in resp.data['results']]
|
||||
self.assertEqual(ids, [self.po_completed_new.id, self.po_completed_old.id])
|
||||
|
||||
template_keys = [p['key'] for p in resp.data['state_parameters']]
|
||||
self.assertEqual(template_keys, ['temperature'])
|
||||
|
||||
by_id = {item['id']: item for item in resp.data['results']}
|
||||
self.assertEqual(by_id[self.po_completed_new.id]['state_parameters'][0]['value'], '32')
|
||||
self.assertEqual(by_id[self.po_completed_old.id]['state_parameters'][0]['value'], '28')
|
||||
self.assertIsNotNone(by_id[self.po_completed_new.id]['state_log'])
|
||||
self.assertFalse(by_id[self.po_completed_new.id]['state_log']['is_cancelled'])
|
||||
|
||||
def test_default_status_is_completed(self):
|
||||
resp = self.client.get(
|
||||
self.url,
|
||||
{
|
||||
'process_id': self.process.id,
|
||||
'state_id': self.state_target.id,
|
||||
}
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
ids = [item['id'] for item in resp.data['results']]
|
||||
self.assertEqual(set(ids), {self.po_completed_new.id, self.po_completed_old.id})
|
||||
|
||||
def test_state_id_with_not_started_status_is_invalid(self):
|
||||
resp = self.client.get(
|
||||
self.url,
|
||||
{
|
||||
'process_id': self.process.id,
|
||||
'state_id': self.state_target.id,
|
||||
'status': 'not_started',
|
||||
}
|
||||
)
|
||||
self.assertEqual(resp.status_code, 400)
|
||||
|
||||
def test_cancelled_status_returns_orders_with_cancelled_logs(self):
|
||||
resp = self.client.get(
|
||||
self.url,
|
||||
{
|
||||
'process_id': self.process.id,
|
||||
'state_id': self.state_target.id,
|
||||
'status': 'cancelled',
|
||||
}
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertEqual(resp.data['results'][0]['id'], self.po_cancelled.id)
|
||||
self.assertTrue(resp.data['results'][0]['state_log']['is_cancelled'])
|
||||
|
||||
def test_invalid_process_state_pair_returns_400(self):
|
||||
resp = self.client.get(
|
||||
self.url,
|
||||
{
|
||||
'process_id': self.process.id,
|
||||
'state_id': self.state_unlinked.id,
|
||||
'status': 'completed',
|
||||
}
|
||||
)
|
||||
self.assertEqual(resp.status_code, 400)
|
||||
self.assertIn('state 不属于该 process', resp.data['detail'])
|
||||
|
||||
def test_ordering_created_at_and_pagination(self):
|
||||
resp = self.client.get(
|
||||
self.url,
|
||||
{
|
||||
'process_id': self.process.id,
|
||||
'state_id': self.state_target.id,
|
||||
'status': 'completed',
|
||||
'ordering': 'created_at',
|
||||
'limit': 1,
|
||||
'offset': 1,
|
||||
}
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertEqual(resp.data['count'], 2)
|
||||
self.assertEqual(len(resp.data['results']), 1)
|
||||
self.assertEqual(resp.data['results'][0]['id'], self.po_completed_new.id)
|
||||
|
||||
def test_other_process_records_are_excluded(self):
|
||||
resp = self.client.get(
|
||||
self.url,
|
||||
{
|
||||
'process_id': self.process.id,
|
||||
'state_id': self.state_target.id,
|
||||
'status': 'completed',
|
||||
}
|
||||
)
|
||||
ids = [item['id'] for item in resp.data['results']]
|
||||
self.assertNotIn(self.po_other_process.id, ids)
|
||||
|
||||
def test_without_state_id_returns_orders_without_any_logs(self):
|
||||
resp = self.client.get(
|
||||
self.url,
|
||||
{
|
||||
'process_id': self.process.id,
|
||||
}
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertEqual(resp.data['status'], 'not_started')
|
||||
self.assertIsNone(resp.data['state'])
|
||||
self.assertEqual(resp.data['state_parameters'], [])
|
||||
ids = [item['id'] for item in resp.data['results']]
|
||||
self.assertEqual(ids, [self.po_no_logs.id])
|
||||
self.assertEqual(resp.data['results'][0]['state_parameters'], [])
|
||||
self.assertIsNone(resp.data['results'][0]['state_log'])
|
||||
|
||||
def test_without_state_id_only_allows_not_started_status(self):
|
||||
resp = self.client.get(
|
||||
self.url,
|
||||
{
|
||||
'process_id': self.process.id,
|
||||
'status': 'completed',
|
||||
}
|
||||
)
|
||||
self.assertEqual(resp.status_code, 400)
|
||||
self.assertIn('state_id 为空时仅支持 status=not_started', resp.data['detail'])
|
||||
|
||||
|
||||
class PlateOrderBatchUpdateV2APITest(TestCase):
|
||||
def setUp(self):
|
||||
self.client = APIClient()
|
||||
|
||||
@@ -7,6 +7,7 @@ from api_v2.views import (
|
||||
PrintingJobBatchAdvanceSubmitView,
|
||||
PlateOrderByProcessNodeView,
|
||||
PlateOrderByProcessView,
|
||||
PlateOrderByStateStatusView,
|
||||
PlateOrderBatchUpdateView,
|
||||
BusinessObjectCloneView,
|
||||
)
|
||||
@@ -18,6 +19,7 @@ urlpatterns = [
|
||||
path('printing-jobs/batch-advance/', PrintingJobBatchAdvanceSubmitView.as_view(), name='api_v2_printing_job_batch_advance_submit'),
|
||||
path('plate-orders/by-process-node/', PlateOrderByProcessNodeView.as_view(), name='api_v2_plate_order_by_process_node'),
|
||||
path('plate-orders/by-process/', PlateOrderByProcessView.as_view(), name='api_v2_plate_order_by_process'),
|
||||
path('plate-orders/by-state-status/', PlateOrderByStateStatusView.as_view(), name='api_v2_plate_order_by_state_status'),
|
||||
path('plate-orders/batch-update/', PlateOrderBatchUpdateView.as_view(), name='api_v2_plate_order_batch_update'),
|
||||
path('stateflow/business-objects/clone/', BusinessObjectCloneView.as_view(), name='api_v2_stateflow_business_object_clone'),
|
||||
]
|
||||
|
||||
@@ -10,6 +10,7 @@ from .printing import (
|
||||
PrintingJobBatchAdvanceSubmitView,
|
||||
PlateOrderByProcessNodeView,
|
||||
PlateOrderByProcessView,
|
||||
PlateOrderByStateStatusView,
|
||||
PlateOrderBatchUpdateView,
|
||||
)
|
||||
from .stateflow import BusinessObjectCloneView
|
||||
@@ -22,6 +23,7 @@ __all__ = [
|
||||
'PrintingJobBatchAdvanceSubmitView',
|
||||
'PlateOrderByProcessNodeView',
|
||||
'PlateOrderByProcessView',
|
||||
'PlateOrderByStateStatusView',
|
||||
'PlateOrderBatchUpdateView',
|
||||
'BusinessObjectCloneView',
|
||||
]
|
||||
|
||||
@@ -3,7 +3,7 @@ import datetime
|
||||
from django.utils import timezone
|
||||
from django.db import transaction
|
||||
from django.db import models as django_models
|
||||
from django.db.models import Q, Count, CharField, Prefetch
|
||||
from django.db.models import Q, Count, CharField, Prefetch, Exists, OuterRef
|
||||
from django.db.models.functions import Cast, Coalesce
|
||||
from rest_framework import serializers, status, permissions
|
||||
from rest_framework.pagination import LimitOffsetPagination
|
||||
@@ -14,6 +14,7 @@ from basic_info import models as basic_models
|
||||
from printing import models as printing_models
|
||||
from api_man.serializers import ProductSerializer
|
||||
from stateflow import models as stateflow_models
|
||||
from stateflow import services as stateflow_services
|
||||
|
||||
|
||||
class IsPrintingFactory(permissions.BasePermission):
|
||||
@@ -721,6 +722,102 @@ class PlateOrderByProcessSerializer(serializers.ModelSerializer):
|
||||
return result
|
||||
|
||||
|
||||
class PlateOrderByStateStatusSerializer(serializers.ModelSerializer):
|
||||
"""按单个节点状态过滤 PlateOrder 的轻量序列化器"""
|
||||
|
||||
design_code = serializers.SerializerMethodField()
|
||||
customer_name = serializers.CharField(source='customer.name', read_only=True)
|
||||
business_object_id = serializers.SerializerMethodField()
|
||||
created_by = serializers.IntegerField(source='created_by_id', read_only=True)
|
||||
state_parameters = serializers.SerializerMethodField()
|
||||
state_log = serializers.SerializerMethodField()
|
||||
state_status = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = printing_models.PlateOrder
|
||||
fields = [
|
||||
'id',
|
||||
'design_code',
|
||||
'customer',
|
||||
'customer_name',
|
||||
'style_name',
|
||||
'urgency_level',
|
||||
'is_invalid',
|
||||
'business_object_id',
|
||||
'created_by',
|
||||
'state_status',
|
||||
'state_parameters',
|
||||
'state_log',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
]
|
||||
read_only_fields = fields
|
||||
|
||||
def get_design_code(self, obj: printing_models.PlateOrder) -> str | None:
|
||||
return obj.design_code or (str(obj.id) if obj.id else None)
|
||||
|
||||
def get_business_object_id(self, obj: printing_models.PlateOrder) -> int | None:
|
||||
return obj.business_object_id
|
||||
|
||||
def get_state_status(self, obj: printing_models.PlateOrder) -> str:
|
||||
return self.context.get('requested_status', '')
|
||||
|
||||
def _get_latest_state_log(self, obj: printing_models.PlateOrder):
|
||||
bo = getattr(obj, 'business_object', None)
|
||||
if not bo:
|
||||
return None
|
||||
logs = getattr(bo, '_prefetched_target_state_logs', None)
|
||||
if logs:
|
||||
return logs[0]
|
||||
target_state_id = self.context.get('target_state_id')
|
||||
if not target_state_id:
|
||||
return None
|
||||
return (
|
||||
bo.state_logs
|
||||
.filter(state_id=target_state_id)
|
||||
.order_by('-completed_at', '-id')
|
||||
.select_related('completed_by')
|
||||
.first()
|
||||
)
|
||||
|
||||
def get_state_parameters(self, obj: printing_models.PlateOrder) -> list[dict]:
|
||||
keys: list[str] = self.context.get('target_parameter_keys') or []
|
||||
if not keys:
|
||||
return []
|
||||
|
||||
bo = getattr(obj, 'business_object', None)
|
||||
if not bo:
|
||||
return [{'key': k, 'value': None} for k in keys]
|
||||
|
||||
latest_log = self._get_latest_state_log(obj)
|
||||
if latest_log is None:
|
||||
return [{'key': k, 'value': None} for k in keys]
|
||||
|
||||
param_records = getattr(latest_log, '_prefetched_parameter_records', None)
|
||||
if param_records is None:
|
||||
param_records = list(latest_log.parameter_records.all().order_by('created_at', 'id'))
|
||||
|
||||
summary: dict = {}
|
||||
for rec in param_records:
|
||||
summary.update(rec.parameters or {})
|
||||
|
||||
return [{'key': k, 'value': summary.get(k)} for k in keys]
|
||||
|
||||
def get_state_log(self, obj: printing_models.PlateOrder) -> dict | None:
|
||||
log = self._get_latest_state_log(obj)
|
||||
if log is None:
|
||||
return None
|
||||
completed_by = log.completed_by
|
||||
return {
|
||||
'id': log.id,
|
||||
'state_id': log.state_id,
|
||||
'completed_at': log.completed_at,
|
||||
'completed_by': completed_by.id if completed_by else None,
|
||||
'completed_by_username': completed_by.username if completed_by else None,
|
||||
'is_cancelled': log.is_cancelled,
|
||||
}
|
||||
|
||||
|
||||
class PlateOrderByProcessView(APIView):
|
||||
"""
|
||||
按 process_id 查询 PlateOrder 列表,并在每条 PlateOrder 中返回 process 的所有节点参数结构。
|
||||
@@ -892,6 +989,176 @@ class PlateOrderByProcessView(APIView):
|
||||
})
|
||||
|
||||
|
||||
class PlateOrderByStateStatusView(APIView):
|
||||
"""按 process_id + state_id + status 过滤 PlateOrder。"""
|
||||
|
||||
permission_classes = [permissions.IsAuthenticated, IsPrintingFactory]
|
||||
|
||||
_ORDERING_FIELDS = {'id', 'created_at', 'updated_at', 'design_code'}
|
||||
_SUPPORTED_STATUS = {'completed', 'not_started', 'cancelled', 'in_progress'}
|
||||
|
||||
def get(self, request):
|
||||
qp = request.query_params
|
||||
|
||||
process_id = qp.get('process_id')
|
||||
if not process_id:
|
||||
return Response({'detail': 'process_id 为必填参数'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
try:
|
||||
process_id_int = int(process_id)
|
||||
except (TypeError, ValueError):
|
||||
return Response({'detail': 'process_id 必须为数字'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
state_id_raw = qp.get('state_id')
|
||||
state_id_int: int | None = None
|
||||
if state_id_raw not in (None, ''):
|
||||
try:
|
||||
state_id_int = int(state_id_raw)
|
||||
except (TypeError, ValueError):
|
||||
return Response({'detail': 'state_id 必须为数字'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
status_value = (qp.get('status') or 'completed').strip().lower()
|
||||
if state_id_int is None:
|
||||
if qp.get('status') and status_value != 'not_started':
|
||||
return Response({'detail': 'state_id 为空时仅支持 status=not_started'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
status_value = 'not_started'
|
||||
elif status_value == 'not_started':
|
||||
return Response({'detail': 'state_id 不为空时不支持 status=not_started'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
elif status_value not in self._SUPPORTED_STATUS:
|
||||
options = ', '.join(sorted(self._SUPPORTED_STATUS))
|
||||
return Response({'detail': f'status 不支持: {status_value}(可选: {options})'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
try:
|
||||
process = stateflow_models.Process.objects.get(id=process_id_int)
|
||||
except stateflow_models.Process.DoesNotExist:
|
||||
return Response({'detail': 'process 不存在'}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
state = None
|
||||
state_parameters_template: list[dict] = []
|
||||
target_keys: list[str] = []
|
||||
representative_order: int | None = None
|
||||
process_node_ids: list[int] = []
|
||||
|
||||
base_bo = stateflow_models.BusinessObject.objects.filter(
|
||||
process_id=process_id_int,
|
||||
plate_order__isnull=False,
|
||||
)
|
||||
|
||||
if state_id_int is None:
|
||||
filtered_bo = base_bo.annotate(
|
||||
has_any_log=Exists(
|
||||
stateflow_models.StateFlowRecord.objects.filter(business_object_id=OuterRef('pk'))
|
||||
)
|
||||
).filter(has_any_log=False)
|
||||
queryset = (
|
||||
printing_models.PlateOrder.objects
|
||||
.select_related('customer', 'business_object', 'created_by')
|
||||
.filter(process=process_id_int, business_object_id__in=filtered_bo.values('id'))
|
||||
)
|
||||
else:
|
||||
try:
|
||||
state = stateflow_models.State.objects.get(id=state_id_int)
|
||||
except stateflow_models.State.DoesNotExist:
|
||||
return Response({'detail': 'state 不存在'}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
process_nodes = list(
|
||||
stateflow_models.ProcessNode.objects
|
||||
.filter(process_id=process_id_int, state_id=state_id_int)
|
||||
.order_by('order', 'id')
|
||||
)
|
||||
if not process_nodes:
|
||||
return Response({'detail': 'state 不属于该 process'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
state_parameters_template = [
|
||||
{'key': param.key, 'value': param.value}
|
||||
for param in state.parameters.order_by('id')
|
||||
]
|
||||
target_keys = [item['key'] for item in state_parameters_template]
|
||||
representative_order = process_nodes[0].order
|
||||
process_node_ids = [node.id for node in process_nodes]
|
||||
|
||||
filtered_bo = stateflow_services.query_business_objects_by_state_status(
|
||||
state_ids=[state_id_int],
|
||||
status=status_value,
|
||||
process_id=process_id_int,
|
||||
base_queryset=base_bo,
|
||||
)
|
||||
|
||||
queryset = (
|
||||
printing_models.PlateOrder.objects
|
||||
.select_related('customer', 'business_object', 'created_by')
|
||||
.filter(process=process_id_int, business_object_id__in=filtered_bo.values('id'))
|
||||
)
|
||||
|
||||
param_records_prefetch = Prefetch(
|
||||
'parameter_records',
|
||||
queryset=stateflow_models.StateLogParameterRecord.objects.order_by('created_at', 'id'),
|
||||
to_attr='_prefetched_parameter_records',
|
||||
)
|
||||
target_logs_qs = (
|
||||
stateflow_models.StateFlowRecord.objects
|
||||
.filter(state_id=state_id_int)
|
||||
.order_by('-completed_at', '-id')
|
||||
.select_related('completed_by')
|
||||
.prefetch_related(param_records_prefetch)
|
||||
)
|
||||
queryset = queryset.prefetch_related(
|
||||
Prefetch(
|
||||
'business_object__state_logs',
|
||||
queryset=target_logs_qs,
|
||||
to_attr='_prefetched_target_state_logs',
|
||||
)
|
||||
)
|
||||
|
||||
ordering = (qp.get('ordering') or '-created_at').strip() or '-created_at'
|
||||
direction = '-' if ordering.startswith('-') else ''
|
||||
field = ordering[1:] if ordering.startswith('-') else ordering
|
||||
if field not in self._ORDERING_FIELDS:
|
||||
options = ', '.join(sorted(self._ORDERING_FIELDS))
|
||||
return Response({'detail': f'ordering 不支持: {ordering}(可选: {options})'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
if field == 'design_code':
|
||||
queryset = queryset.annotate(
|
||||
design_code_normalized=Coalesce('design_code', Cast('id', output_field=CharField()))
|
||||
).order_by(f'{direction}design_code_normalized', 'id')
|
||||
else:
|
||||
queryset = queryset.order_by(f'{direction}{field}', 'id')
|
||||
|
||||
paginator = LimitOffsetPagination()
|
||||
paginator.default_limit = 20
|
||||
page = paginator.paginate_queryset(queryset, request, view=self)
|
||||
results = page if page is not None else list(queryset)
|
||||
|
||||
srz = PlateOrderByStateStatusSerializer(
|
||||
results,
|
||||
many=True,
|
||||
context={
|
||||
'request': request,
|
||||
'target_state_id': state_id_int,
|
||||
'target_parameter_keys': target_keys,
|
||||
'requested_status': status_value,
|
||||
},
|
||||
)
|
||||
|
||||
return Response({
|
||||
'process': {
|
||||
'id': process.id,
|
||||
'name': process.name,
|
||||
},
|
||||
'state': {
|
||||
'id': state.id if state else None,
|
||||
'name': state.name if state else None,
|
||||
'order': representative_order,
|
||||
'process_node_ids': process_node_ids,
|
||||
} if state else None,
|
||||
'status': status_value,
|
||||
'state_parameters': state_parameters_template,
|
||||
'count': getattr(paginator, 'count', len(results)),
|
||||
'next': paginator.get_next_link() if page is not None else None,
|
||||
'previous': paginator.get_previous_link() if page is not None else None,
|
||||
'results': srz.data,
|
||||
})
|
||||
|
||||
|
||||
# 允许批量更新的字段白名单:只改这里即可增减
|
||||
PLATE_ORDER_BATCH_UPDATE_ALLOWED_FIELDS = [
|
||||
"original_id",
|
||||
|
||||
Reference in New Issue
Block a user