diff --git a/api_v1/views/printing/serializers.py b/api_v1/views/printing/serializers.py index bf6f80a..705c5f7 100644 --- a/api_v1/views/printing/serializers.py +++ b/api_v1/views/printing/serializers.py @@ -421,7 +421,7 @@ class PrintingJobCreateUpdateSerializer(serializers.ModelSerializer): class PrintingJobBatchAdvanceRecordSerializer(serializers.ModelSerializer): - """印染任务批量推进记录(用于嵌入 PrintingJob 的序列化结果)""" + """印染任务批量操作记录(用于嵌入 PrintingJob 的序列化结果)""" state_id = serializers.IntegerField(source='state.id', read_only=True) state_name = serializers.CharField(source='state.name', read_only=True) @@ -440,6 +440,7 @@ class PrintingJobBatchAdvanceRecordSerializer(serializers.ModelSerializer): 'created_by_username', 'created_by_name', 'parameters', + 'only_parameters', 'created_at', ] read_only_fields = fields diff --git a/api_v2/tests.py b/api_v2/tests.py index 47551a8..e9ebbc2 100644 --- a/api_v2/tests.py +++ b/api_v2/tests.py @@ -1654,3 +1654,226 @@ class PrintingOrderBatchAdvanceRecordsAPITest(TestCase): self.client.logout() response = self.client.get(f'/api/v2/printing-orders/{self.printing_order.id}/batch-advance-records/') self.assertEqual(response.status_code, 401) + + +class PrintingJobBatchAddParametersAPITest(TestCase): + """测试批量补充工艺参数 API""" + + def setUp(self): + self.client = APIClient() + self.merchant = basic_models.Merchant.objects.create( + name='测试工厂', + type=basic_models.MerchantTypeEnum.FACTORY, + ) + self.user = get_user_model().objects.create_user(username='factory_user', password='pass12345') + self.employee = basic_models.Employee.objects.create( + merchant=self.merchant, + name='工厂员工', + sys_user=self.user + ) + self.client.force_authenticate(user=self.user) + + # 创建客户 + self.customer = basic_models.Customer.objects.create( + merchant=self.merchant, + name='测试客户' + ) + + # 创建产品类别和产品 + self.category = basic_models.ProductCategory.objects.create( + merchant=self.merchant, + name='测试类别' + ) + self.product = basic_models.Product.objects.create( + merchant=self.merchant, + name='测试产品', + category=self.category, + unit=basic_models.ProductUnitEnum.METER + ) + + # 创建流程状态 + self.state1 = stateflow_models.State.objects.create(name='染色', description='染色工序') + self.state2 = stateflow_models.State.objects.create(name='整理', description='整理工序') + + # 创建流程 + self.process = stateflow_models.Process.objects.create(name='印染流程') + stateflow_models.ProcessNode.objects.create(process=self.process, state=self.state1, order=1) + stateflow_models.ProcessNode.objects.create(process=self.process, state=self.state2, order=2) + + # 创建印染订单 + self.printing_order = printing_models.PrintingOrder.objects.create( + customer=self.customer, + fabric='测试面料', + width='150cm', + process=self.process + ) + + # 创建印染明细并绑定流程实例 + self.job1 = printing_models.PrintingJob.objects.create( + printing_order=self.printing_order, + product=self.product, + quantity=100, + unit='米' + ) + self.job2 = printing_models.PrintingJob.objects.create( + printing_order=self.printing_order, + product=self.product, + quantity=200, + unit='米' + ) + + # 创建业务对象并推进到 state1 + from stateflow import services as stateflow_services + from django.contrib.contenttypes.models import ContentType + + ct = ContentType.objects.get_for_model(printing_models.PrintingJob) + + self.bo1 = stateflow_models.BusinessObject.objects.create( + name=f'PrintingJob-{self.job1.id}', + process=self.process, + content_type=ct, + object_id=self.job1.id + ) + self.job1.business_object = self.bo1 + self.job1.save() + + self.bo2 = stateflow_models.BusinessObject.objects.create( + name=f'PrintingJob-{self.job2.id}', + process=self.process, + content_type=ct, + object_id=self.job2.id + ) + self.job2.business_object = self.bo2 + self.job2.save() + + # 推进到 state1(创建 state_log) + stateflow_services.advance_to_next_state(self.bo1, self.user, temperature='25.0') + stateflow_services.advance_to_next_state(self.bo2, self.user, temperature='25.0') + + def test_batch_add_parameters_success(self): + """测试成功批量补充参数""" + response = self.client.post( + '/api/v2/printing-jobs/batch-add-parameters/', + { + 'printing_job_ids': [self.job1.id, self.job2.id], + 'state_id': self.state1.id, + 'parameters': {'temperature': '26.0', 'operator': '李四'}, + 'remark': '补测数据' + }, + format='json' + ) + + self.assertEqual(response.status_code, 200) + data = response.data + self.assertEqual(data['detail'], '批量补充参数成功') + self.assertEqual(data['printing_order_id'], self.printing_order.id) + self.assertEqual(set(data['printing_job_ids']), {self.job1.id, self.job2.id}) + self.assertEqual(data['state_id'], self.state1.id) + self.assertEqual(data['state_name'], '染色') + self.assertEqual(data['affected_count'], 2) + + # 验证创建了批量操作记录 + record = printing_models.PrintingJobBatchAdvanceRecord.objects.get(id=data['batch_record_id']) + self.assertTrue(record.only_parameters) + self.assertEqual(record.parameters, {'temperature': '26.0', 'operator': '李四'}) + self.assertEqual(record.printing_jobs.count(), 2) + + def test_batch_add_parameters_empty_parameters(self): + """测试参数为空时返回错误""" + response = self.client.post( + '/api/v2/printing-jobs/batch-add-parameters/', + { + 'printing_job_ids': [self.job1.id], + 'state_id': self.state1.id, + 'parameters': {}, + }, + format='json' + ) + + self.assertEqual(response.status_code, 400) + + def test_batch_add_parameters_job_not_found(self): + """测试 job 不存在时返回错误""" + response = self.client.post( + '/api/v2/printing-jobs/batch-add-parameters/', + { + 'printing_job_ids': [99999], + 'state_id': self.state1.id, + 'parameters': {'temperature': '26.0'}, + }, + format='json' + ) + + self.assertEqual(response.status_code, 400) + self.assertIn('missing_jobs', response.data) + + def test_batch_add_parameters_state_not_found(self): + """测试 state 不存在时返回错误""" + response = self.client.post( + '/api/v2/printing-jobs/batch-add-parameters/', + { + 'printing_job_ids': [self.job1.id], + 'state_id': 99999, + 'parameters': {'temperature': '26.0'}, + }, + format='json' + ) + + self.assertEqual(response.status_code, 400) + + def test_batch_add_parameters_missing_state_log(self): + """测试 job 没有对应 state_log 时返回错误""" + # state2 的 state_log 尚未创建(还没推进到那一步) + response = self.client.post( + '/api/v2/printing-jobs/batch-add-parameters/', + { + 'printing_job_ids': [self.job1.id], + 'state_id': self.state2.id, # 还没推进到这个状态 + 'parameters': {'temperature': '26.0'}, + }, + format='json' + ) + + self.assertEqual(response.status_code, 400) + self.assertIn('missing_state_log', response.data) + + def test_batch_add_parameters_different_orders(self): + """测试 jobs 属于不同订单时返回错误""" + # 创建另一个订单和 job + another_order = printing_models.PrintingOrder.objects.create( + customer=self.customer, + fabric='另一面料', + width='160cm' + ) + another_job = printing_models.PrintingJob.objects.create( + printing_order=another_order, + product=self.product, + quantity=300, + unit='米' + ) + + response = self.client.post( + '/api/v2/printing-jobs/batch-add-parameters/', + { + 'printing_job_ids': [self.job1.id, another_job.id], + 'state_id': self.state1.id, + 'parameters': {'temperature': '26.0'}, + }, + format='json' + ) + + self.assertEqual(response.status_code, 400) + + def test_batch_add_parameters_unauthenticated(self): + """测试未登录时返回 401""" + self.client.logout() + response = self.client.post( + '/api/v2/printing-jobs/batch-add-parameters/', + { + 'printing_job_ids': [self.job1.id], + 'state_id': self.state1.id, + 'parameters': {'temperature': '26.0'}, + }, + format='json' + ) + self.assertEqual(response.status_code, 401) diff --git a/api_v2/urls.py b/api_v2/urls.py index 9203364..63c33a2 100644 --- a/api_v2/urls.py +++ b/api_v2/urls.py @@ -7,6 +7,7 @@ from api_v2.views import ( PrintingJobByCustomerView, PrintingJobBatchAdvancePreviewView, PrintingJobBatchAdvanceSubmitView, + PrintingJobBatchAddParametersView, PlateOrderByProcessNodeView, PlateOrderByProcessView, PlateOrderByStateStatusView, @@ -25,6 +26,7 @@ urlpatterns = [ path('printing-jobs/by-customer/', PrintingJobByCustomerView.as_view(), name='api_v2_printing_job_by_customer'), path('printing-jobs/batch-advance/preview/', PrintingJobBatchAdvancePreviewView.as_view(), name='api_v2_printing_job_batch_advance_preview'), path('printing-jobs/batch-advance/', PrintingJobBatchAdvanceSubmitView.as_view(), name='api_v2_printing_job_batch_advance_submit'), + path('printing-jobs/batch-add-parameters/', PrintingJobBatchAddParametersView.as_view(), name='api_v2_printing_job_batch_add_parameters'), path('printing-orders//batch-advance-records/', PrintingOrderBatchAdvanceRecordsView.as_view(), name='api_v2_printing_order_batch_advance_records'), 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'), diff --git a/api_v2/views/__init__.py b/api_v2/views/__init__.py index d1292ea..1194264 100644 --- a/api_v2/views/__init__.py +++ b/api_v2/views/__init__.py @@ -9,6 +9,7 @@ from .printing import ( PrintingJobV2Serializer, PrintingJobBatchAdvancePreviewView, PrintingJobBatchAdvanceSubmitView, + PrintingJobBatchAddParametersView, PlateOrderByProcessNodeView, PlateOrderByProcessView, PlateOrderByStateStatusView, @@ -25,6 +26,7 @@ __all__ = [ 'PrintingJobV2Serializer', 'PrintingJobBatchAdvancePreviewView', 'PrintingJobBatchAdvanceSubmitView', + 'PrintingJobBatchAddParametersView', 'PlateOrderByProcessNodeView', 'PlateOrderByProcessView', 'PlateOrderByStateStatusView', diff --git a/api_v2/views/printing.py b/api_v2/views/printing.py index 1356b59..2b40141 100644 --- a/api_v2/views/printing.py +++ b/api_v2/views/printing.py @@ -1348,7 +1348,7 @@ class PlateOrderBatchUpdateView(APIView): class BatchAdvanceRecordSerializer(serializers.ModelSerializer): - """批量推进记录序列化器""" + """批量操作记录序列化器""" state_id = serializers.IntegerField(source='state.id', read_only=True) state_name = serializers.CharField(source='state.name', read_only=True) @@ -1369,6 +1369,7 @@ class BatchAdvanceRecordSerializer(serializers.ModelSerializer): 'created_by_username', 'created_by_name', 'parameters', + 'only_parameters', 'printing_job_ids', 'printing_job_count', 'created_at', @@ -1458,3 +1459,200 @@ class PrintingOrderBatchAdvanceRecordsView(APIView): 'records': serializer.data, 'total_count': records.count(), }) + + +class PrintingJobBatchAddParametersRequestSerializer(serializers.Serializer): + """批量补充参数请求序列化器""" + printing_job_ids = serializers.ListField( + child=serializers.IntegerField(), + min_length=1, + help_text='印染明细ID列表' + ) + state_id = serializers.IntegerField(help_text='目标流程节点ID') + parameters = serializers.DictField( + child=serializers.CharField(allow_blank=True), + help_text='要补充的工艺参数' + ) + remark = serializers.CharField(required=False, default='', allow_blank=True, help_text='备注说明') + + +def _validate_jobs_for_batch_add_parameters(printing_job_ids: list[int], state_id: int) -> dict: + """ + 验证批量补充参数的 jobs 并返回相关数据 + + 验证规则: + 1. 所有 job 必须存在 + 2. 所有 job 必须属于同一个 printing_order + 3. 所有 job 必须有 business_object + 4. 所有 job 的 business_object 必须有对应 state_id 的 state_log(未撤销) + + 返回: + { + 'jobs': QuerySet[PrintingJob], + 'printing_order': PrintingOrder, + 'printing_order_id': int, + 'state': State, + 'state_logs': dict[int, StateFlowRecord], # job_id -> state_log + } + """ + from stateflow import models as stateflow_models + + jobs = ( + printing_models.PrintingJob.objects + .select_related('printing_order', 'business_object') + .filter(id__in=printing_job_ids) + ) + + found_ids = set(jobs.values_list('id', flat=True)) + missing_ids = [i for i in printing_job_ids if i not in found_ids] + if missing_ids: + raise serializers.ValidationError({ + 'detail': f'以下 PrintingJob 不存在: {missing_ids}', + 'missing_jobs': missing_ids, + }) + + # 验证同一订单 + order_ids = set(jobs.values_list('printing_order_id', flat=True)) + if len(order_ids) != 1: + raise serializers.ValidationError({ + 'detail': '所有 PrintingJob 必须属于同一个 PrintingOrder', + }) + + printing_order_id = order_ids.pop() + printing_order = printing_models.PrintingOrder.objects.get(id=printing_order_id) + + # 验证 state 存在 + try: + state = stateflow_models.State.objects.get(id=state_id) + except stateflow_models.State.DoesNotExist: + raise serializers.ValidationError({ + 'detail': f'State ID {state_id} 不存在', + }) + + # 验证所有 job 有 business_object 和对应的 state_log + jobs_without_bo = [] + jobs_without_state_log = [] + state_logs = {} # job_id -> state_log + + for job in jobs: + if not job.business_object: + jobs_without_bo.append(job.id) + continue + + # 查找对应 state_id 的 state_log(未撤销) + state_log = job.business_object.state_logs.filter( + state_id=state_id, + is_cancelled=False + ).first() + + if not state_log: + jobs_without_state_log.append(job.id) + else: + state_logs[job.id] = state_log + + if jobs_without_bo: + raise serializers.ValidationError({ + 'detail': f'以下 PrintingJob 没有流程实例 (business_object): {jobs_without_bo}', + 'missing_business_object': jobs_without_bo, + }) + + if jobs_without_state_log: + raise serializers.ValidationError({ + 'detail': f'以下 PrintingJob 没有对应 state_id={state_id} 的状态流转记录(或已撤销): {jobs_without_state_log}', + 'missing_state_log': jobs_without_state_log, + }) + + return { + 'jobs': jobs, + 'printing_order': printing_order, + 'printing_order_id': printing_order_id, + 'state': state, + 'state_logs': state_logs, + } + + +class PrintingJobBatchAddParametersView(APIView): + """ + 批量补充工艺参数 + + 为多个 PrintingJob 的已完成状态流转记录批量补充工艺参数。 + + POST /api/v2/printing-jobs/batch-add-parameters/ + + 请求体: + { + "printing_job_ids": [101, 102, 103], + "state_id": 7, + "parameters": { + "temperature": "26.0", + "operator": "李四" + }, + "remark": "批量补充参数备注" + } + + 响应: + { + "detail": "批量补充参数成功", + "batch_record_id": 15, + "printing_order_id": 55, + "printing_job_ids": [101, 102, 103], + "state_id": 7, + "state_name": "染色", + "parameters": {...}, + "affected_count": 3 + } + """ + + permission_classes = [permissions.IsAuthenticated, IsPrintingFactory] + + def post(self, request): + srz = PrintingJobBatchAddParametersRequestSerializer(data=request.data) + srz.is_valid(raise_exception=True) + + payload = srz.validated_data + printing_job_ids = payload['printing_job_ids'] + state_id = payload['state_id'] + parameters = payload['parameters'] + remark = payload.get('remark', '') + + if not parameters: + raise serializers.ValidationError({ + 'detail': '参数不能为空', + }) + + # 验证数据 + data = _validate_jobs_for_batch_add_parameters(printing_job_ids, state_id) + + from stateflow import services as stateflow_services + + # 全成功/全失败 + with transaction.atomic(): + # 创建批量操作记录 + record = printing_models.PrintingJobBatchAdvanceRecord.objects.create( + printing_order=data['printing_order'], + state=data['state'], + created_by=request.user, + parameters=parameters, + only_parameters=True, # 标记为仅补充参数 + ) + record.printing_jobs.set(data['jobs']) + + # 逐个补充参数 + for job in data['jobs']: + state_log = data['state_logs'][job.id] + stateflow_services.add_parameters_to_state_log( + state_log, + remark=remark, + **parameters + ) + + return Response({ + 'detail': '批量补充参数成功', + 'batch_record_id': record.id, + 'printing_order_id': data['printing_order_id'], + 'printing_job_ids': [j.id for j in data['jobs']], + 'state_id': state_id, + 'state_name': data['state'].name, + 'parameters': parameters, + 'affected_count': len(data['jobs']), + }) diff --git a/docs/api_v2_batch_add_parameters.md b/docs/api_v2_batch_add_parameters.md new file mode 100644 index 0000000..323a900 --- /dev/null +++ b/docs/api_v2_batch_add_parameters.md @@ -0,0 +1,235 @@ +# 批量补充工艺参数 API + +## 概述 + +为多个 `PrintingJob` 的已完成状态流转记录批量补充工艺参数。 + +- **端点**: `POST /api/v2/printing-jobs/batch-add-parameters/` +- **认证**: 需要登录 +- **权限**: 印染工厂用户 + +## 与批量推进的区别 + +| 维度 | batch-advance | batch-add-parameters | +|------|---------------|----------------------| +| **目的** | 推进流程状态 | 补充已完成状态的参数 | +| **前置条件** | jobs 处于同一待推进状态 | jobs 已完成指定状态 | +| **核心操作** | `advance_to_next_state()` | `add_parameters_to_state_log()` | +| **审计记录** | `only_parameters=False` | `only_parameters=True` | + +## 请求 + +### 请求体 + +```json +{ + "printing_job_ids": [101, 102, 103], + "state_id": 7, + "parameters": { + "temperature": "26.0", + "operator": "李四", + "remark_field": "补测数据" + }, + "remark": "批量补充参数备注" +} +``` + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `printing_job_ids` | array[int] | 是 | 印染明细ID列表 | +| `state_id` | int | 是 | 目标流程节点ID(State.id) | +| `parameters` | object | 是 | 要补充的工艺参数(不能为空) | +| `remark` | string | 否 | 备注说明 | + +### 请求示例 + +```bash +curl -X POST "http://localhost:8000/api/v2/printing-jobs/batch-add-parameters/" \ + -H "Authorization: Token " \ + -H "Content-Type: application/json" \ + -d '{ + "printing_job_ids": [101, 102, 103], + "state_id": 7, + "parameters": {"temperature": "26.0", "operator": "李四"}, + "remark": "补测数据" + }' +``` + +## 响应 + +### 成功响应 (200 OK) + +```json +{ + "detail": "批量补充参数成功", + "batch_record_id": 15, + "printing_order_id": 55, + "printing_job_ids": [101, 102, 103], + "state_id": 7, + "state_name": "染色", + "parameters": { + "temperature": "26.0", + "operator": "李四" + }, + "affected_count": 3 +} +``` + +### 响应字段说明 + +| 字段 | 类型 | 说明 | +|------|------|------| +| `detail` | string | 操作结果消息 | +| `batch_record_id` | int | 批量操作记录ID | +| `printing_order_id` | int | 印染订单ID | +| `printing_job_ids` | array[int] | 受影响的印染明细ID列表 | +| `state_id` | int | 目标流程节点ID | +| `state_name` | string | 流程节点名称 | +| `parameters` | object | 补充的工艺参数 | +| `affected_count` | int | 受影响的明细数量 | + +### 错误响应 + +#### 参数为空 (400 Bad Request) + +```json +{ + "detail": "参数不能为空" +} +``` + +#### Job 不存在 (400 Bad Request) + +```json +{ + "detail": "以下 PrintingJob 不存在: [999]", + "missing_jobs": [999] +} +``` + +#### Jobs 属于不同订单 (400 Bad Request) + +```json +{ + "detail": "所有 PrintingJob 必须属于同一个 PrintingOrder" +} +``` + +#### Job 没有流程实例 (400 Bad Request) + +```json +{ + "detail": "以下 PrintingJob 没有流程实例 (business_object): [101]", + "missing_business_object": [101] +} +``` + +#### Job 没有对应状态记录 (400 Bad Request) + +```json +{ + "detail": "以下 PrintingJob 没有对应 state_id=7 的状态流转记录(或已撤销): [103]", + "missing_state_log": [103] +} +``` + +#### State 不存在 (400 Bad Request) + +```json +{ + "detail": "State ID 999 不存在" +} +``` + +#### 未认证 (401 Unauthorized) + +```json +{ + "detail": "Authentication credentials were not provided." +} +``` + +## 验证规则 + +| 规则 | 说明 | +|------|------| +| ① 所有 job 必须存在 | 返回 `missing_jobs` | +| ② 所有 job 必须属于同一个 `printing_order` | 与 batch_advance 一致 | +| ③ 所有 job 必须有 `business_object` | 返回 `missing_business_object` | +| ④ 所有 job 必须有对应 `state_id` 的未撤销状态记录 | 返回 `missing_state_log` | +| ⑤ `parameters` 不能为空 | 与单条 add-parameters 一致 | + +## 事务策略 + +- **全成功/全失败**:任意一个 job 验证失败 → 整体失败,不写入任何数据 +- 使用 `transaction.atomic()` 包裹 + +## 审计记录 + +操作成功后会创建 `PrintingJobBatchAdvanceRecord` 记录: + +| 字段 | 值 | +|------|-----| +| `printing_order` | 印染订单 | +| `state` | 操作的流程节点 | +| `parameters` | 补充的参数 | +| `printing_jobs` | 涉及的明细 | +| `created_by` | 操作人 | +| `only_parameters` | **`True`** | + +可通过 `GET /api/v2/printing-orders/{id}/batch-advance-records/` 查询所有批量操作记录。 + +## 前端使用示例 + +```javascript +// 批量补充参数 +async function batchAddParameters(printingJobIds, stateId, parameters, remark = '') { + const response = await fetch('/api/v2/printing-jobs/batch-add-parameters/', { + method: 'POST', + headers: { + 'Authorization': `Token ${token}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + printing_job_ids: printingJobIds, + state_id: stateId, + parameters: parameters, + remark: remark + }) + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.detail); + } + + return await response.json(); +} + +// 使用示例 +try { + const result = await batchAddParameters( + [101, 102, 103], + 7, + { temperature: '26.0', operator: '李四' }, + '补测数据' + ); + console.log(`成功补充 ${result.affected_count} 条明细的参数`); +} catch (error) { + console.error('补充参数失败:', error.message); +} +``` + +## 相关文档 + +- [批量推进 API](./api_v2_advance_printing_jobs_api.md) +- [批量操作记录查询](./api_v2_printing_order_batch_advance_records.md) +- [单条补充参数 API](./batch_submit_state_params.md) + +## 相关模型 + +- `printing.PrintingJobBatchAdvanceRecord` - 批量操作记录(`only_parameters=True`) +- `printing.PrintingJob` - 印染明细 +- `stateflow.StateFlowRecord` - 状态流转记录 +- `stateflow.StateLogParameterRecord` - 参数记录 + diff --git a/docs/batch_submit_state_params.md b/docs/batch_submit_state_params.md index 8ba19f5..6b94652 100644 --- a/docs/batch_submit_state_params.md +++ b/docs/batch_submit_state_params.md @@ -11,15 +11,18 @@ ### 返回字段结构:batch_advance_records 每条记录(PrintingJobBatchAdvanceRecord)包含: -- `id`: 批量推进记录 ID +- `id`: 批量操作记录 ID - `printing_order`: 主订单 ID -- `state`: 本次批量推进“完成”的流程节点 ID(即推进时的 next_pending_state) +- `state`: 本次批量操作的流程节点 ID - `state_id`: 同 `state`(便于前端直接取用) - `state_name`: 节点名称 - `created_by`: 操作人用户 ID(可能为 null) - `created_by_username`: 操作人用户名(可能为 null) - `created_by_name`: 操作人员工姓名(若用户有 employee,则返回 employee.name,否则 null) -- `parameters`: 本次批量推进提交的参数 JSON(与推进接口 `parameters` 完全一致) +- `parameters`: 本次批量操作提交的参数 JSON +- `only_parameters`: 操作类型标识 + - `false`(默认):批量推进状态 + - `true`:仅批量补充工艺参数(不推进状态) - `created_at`: 记录创建时间(ISO8601) ### 示例(无记录) @@ -56,6 +59,7 @@ "temperature": "25.5", "operator": "张三" }, + "only_parameters": false, "created_at": "2025-12-14T10:00:00+08:00" } ] diff --git a/printing/migrations/0028_batch_advance_record_only_parameters.py b/printing/migrations/0028_batch_advance_record_only_parameters.py new file mode 100644 index 0000000..e110565 --- /dev/null +++ b/printing/migrations/0028_batch_advance_record_only_parameters.py @@ -0,0 +1,34 @@ +# Generated by Django 5.2.8 on 2026-01-07 08:20 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('printing', '0027_add_created_by_to_printingorder_and_printingjob'), + ('stateflow', '0021_statelogparameterrecord'), + ] + + operations = [ + migrations.AlterModelOptions( + name='printingjobbatchadvancerecord', + options={'verbose_name': '印染任务批量操作记录', 'verbose_name_plural': '印染任务批量操作记录'}, + ), + migrations.AddField( + model_name='printingjobbatchadvancerecord', + name='only_parameters', + field=models.BooleanField(default=False, help_text='True=仅补充参数(不推进状态),False=批量推进状态', verbose_name='仅参数补充'), + ), + migrations.AlterField( + model_name='printingjobbatchadvancerecord', + name='parameters', + field=models.JSONField(blank=True, default=dict, help_text='批量操作时提交的参数', verbose_name='工艺参数'), + ), + migrations.AlterField( + model_name='printingjobbatchadvancerecord', + name='state', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='printing_job_batch_advance_records', to='stateflow.state', verbose_name='操作的流程节点'), + ), + ] diff --git a/printing/models.py b/printing/models.py index 90fa6dc..e02bac8 100644 --- a/printing/models.py +++ b/printing/models.py @@ -465,13 +465,16 @@ class PrintingJob(ModelBase): class PrintingJobBatchAdvanceRecord(ModelBase): """ - 印染任务批量推进记录(审计) + 印染任务批量操作记录(审计) - 记录一次“对多个 PrintingJob 用同一组参数推进同一步流程节点”的行为: - - created_by/created_at:谁在何时做了批量推进 - - parameters:本次推进提交的工艺参数(JSON) - - state:本次被“完成”的流程节点(即推进时的 next_pending_state) - - printing_jobs:本次批量推进涉及的所有明细 + 记录一次"对多个 PrintingJob 用同一组参数操作同一步流程节点"的行为: + - created_by/created_at:谁在何时做了批量操作 + - parameters:本次操作提交的工艺参数(JSON) + - state:本次操作的流程节点 + - printing_jobs:本次批量操作涉及的所有明细 + - only_parameters:操作类型 + - False(默认):批量推进状态 + - True:仅批量补充工艺参数(不推进状态) """ printing_order = models.ForeignKey( @@ -484,7 +487,7 @@ class PrintingJobBatchAdvanceRecord(ModelBase): stateflow_models.State, on_delete=models.PROTECT, related_name='printing_job_batch_advance_records', - verbose_name='完成的流程节点', + verbose_name='操作的流程节点', ) created_by = models.ForeignKey( settings.AUTH_USER_MODEL, @@ -498,7 +501,7 @@ class PrintingJobBatchAdvanceRecord(ModelBase): default=dict, blank=True, verbose_name='工艺参数', - help_text='批量推进时提交的参数(与单条推进一致)', + help_text='批量操作时提交的参数', ) printing_jobs = models.ManyToManyField( PrintingJob, @@ -506,7 +509,12 @@ class PrintingJobBatchAdvanceRecord(ModelBase): verbose_name='印染明细', blank=False, ) + only_parameters = models.BooleanField( + default=False, + verbose_name='仅参数补充', + help_text='True=仅补充参数(不推进状态),False=批量推进状态', + ) class Meta: - verbose_name = '印染任务批量推进记录' - verbose_name_plural = '印染任务批量推进记录' + verbose_name = '印染任务批量操作记录' + verbose_name_plural = '印染任务批量操作记录'