1
0
forked from erp-dev/erp

feat: log formatted

This commit is contained in:
2026-06-26 17:03:54 +08:00
parent e8bf4b5e49
commit cb6589c884
26 changed files with 1410 additions and 50 deletions

View File

@@ -0,0 +1,23 @@
# Generated by Django 5.2.8 on 2026-06-25 05:47
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api_v1', '0014_alter_printingexternalsyncfailure_created_at'),
]
operations = [
migrations.AlterField(
model_name='apiauditlog',
name='method',
field=models.CharField(help_text='HTTP方法如POST、PUT、PATCH', max_length=10, verbose_name='请求方法'),
),
migrations.AlterField(
model_name='apiauditlog',
name='request_data',
field=models.JSONField(default=dict, help_text='请求的body数据JSON格式', verbose_name='请求体数据'),
),
]

View File

@@ -255,9 +255,9 @@ class PrintingExternalSyncFailure(ModelBase):
class ApiAuditLog(models.Model):
"""API审计日志 - 记录创建操作的历史现场
用于保存特定模块的POST请求记录"创建"操作的完整请求信息。
"""API审计日志 - 记录创建/更新操作的历史现场
用于保存特定模块的POST/PUT/PATCH请求,记录"创建/更新"操作的完整请求信息。
通过Celery异步写入避免影响API响应性能。
"""
url = models.CharField(
@@ -268,12 +268,12 @@ class ApiAuditLog(models.Model):
method = models.CharField(
max_length=10,
verbose_name='请求方法',
help_text='HTTP方法如POST'
help_text='HTTP方法如POST、PUT、PATCH'
)
request_data = models.JSONField(
default=dict,
verbose_name='请求体数据',
help_text='POST请求的body数据JSON格式'
help_text='请求的body数据JSON格式'
)
query_params = models.JSONField(
default=dict,

View File

@@ -448,6 +448,7 @@ def _build_printing_order_before_snapshot(printing_order):
{
'id': job.id,
'original_id': job.original_id,
'sub_id': job.sub_id,
'product_id': job.product_id,
'product_name': getattr(job.product, 'name', None),
'quantity': job.quantity,
@@ -939,6 +940,7 @@ def _upsert_external_printing_job(*, merchant, printing_order, record: dict, syn
'pieces': _extract_pieces(record.get('BeiZhuC')),
'description': None,
'original_id': record_id,
'sub_id': _extract_positive_int(record.get('SubID'), field_name='SubID'),
'created_by': created_by_user,
'external_product_name': None,
'external_raw': record,

View File

@@ -85,6 +85,7 @@ class ExternalPrintingRecordsSyncTaskTest(TestCase):
'SeHao': '1.51',
'ShuLiang': '10.00',
'ShuLiangZ': '2.68',
'SubID': 1,
'YanSe': product_name,
'area': '周边',
'customer': {
@@ -116,6 +117,7 @@ class ExternalPrintingRecordsSyncTaskTest(TestCase):
job = printing_models.PrintingJob.objects.get(original_id=1000000)
self.assertEqual(job.product, self.existing_product)
self.assertEqual(job.sub_id, 1)
self.assertIsNone(job.external_product_name)
@patch('api_v1.tasks._advance_external_printing_cursor')
@@ -336,6 +338,7 @@ class ExternalPrintingRecordsSyncTaskTest(TestCase):
updated_record['ShuLiang'] = '20.00'
updated_record['ShuLiangZ'] = '3.15'
updated_record['BeiZhuC'] = '20件'
updated_record['SubID'] = 2
mock_fetch_image.return_value = {'bytes': b'', 'content_type': 'image/jpeg', 'name': 'unused.jpg'}
mock_advance_cursor.return_value = {'updated': True}
@@ -354,6 +357,7 @@ class ExternalPrintingRecordsSyncTaskTest(TestCase):
self.assertEqual(jobs.count(), 1)
job = jobs.get()
self.assertEqual(job.original_id, 1000001)
self.assertEqual(job.sub_id, 2)
self.assertEqual(job.quantity, 20)
self.assertEqual(job.size, '3.15')
self.assertEqual(job.pieces, 20)

View File

@@ -369,7 +369,7 @@ class PrintingJobListSerializer(serializers.ModelSerializer):
class Meta:
model = models.PrintingJob
fields = [
'id', 'original_id', 'merchant_id', 'printing_order', 'printing_order_id', 'external_order_id', 'product', 'product_name',
'id', 'original_id', 'sub_id', 'merchant_id', 'printing_order', 'printing_order_id', 'external_order_id', 'product', 'product_name',
'product_image_url', 'has_started',
'quantity', 'billed_quantity', 'unit', 'size', 'pieces', 'description',
'work_state', 'work_state_display',
@@ -480,7 +480,7 @@ class PrintingJobDetailSerializer(serializers.ModelSerializer):
class Meta:
model = models.PrintingJob
fields = [
'id', 'original_id', 'merchant_id', 'printing_order', 'printing_order_id', 'external_order_id', 'product', 'product_name', 'product_code',
'id', 'original_id', 'sub_id', 'merchant_id', 'printing_order', 'printing_order_id', 'external_order_id', 'product', 'product_name', 'product_code',
'quantity', 'billed_quantity', 'unit', 'size', 'pieces', 'description',
'work_state', 'work_state_display',
'status', 'status_id', 'is_completed', 'is_production_completed', 'has_started',
@@ -543,7 +543,7 @@ class PrintingJobCreateUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = models.PrintingJob
fields = [
'id', 'original_id', 'printing_order', 'external_order_id', 'product', 'quantity', 'unit', 'size', 'pieces', 'description',
'id', 'original_id', 'sub_id', 'printing_order', 'external_order_id', 'product', 'quantity', 'unit', 'size', 'pieces', 'description',
'work_state',
'batch_advance_records',
]

View File

@@ -248,6 +248,22 @@ class PrintingOrderAPITestCase(TestCase):
self.assertEqual(response.data['craft'], '活性印花')
self.assertIn('customer_name', response.data)
self.assertEqual(response.data['customer_name'], self.customer.name)
def test_get_printing_order_by_external_order_id(self):
"""测试通过 external_order_id 获取订单详情"""
order = printing_models.PrintingOrder.objects.create(
merchant=self.merchant,
customer=self.customer,
fabric='通过外部编号查询',
width='150cm',
external_order_id='KD20410611',
)
response = self.client.get('/api/v1/printing-orders/by-external-order-id/KD20410611/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['id'], order.id)
self.assertEqual(response.data['external_order_id'], 'KD20410611')
self.assertEqual(response.data['fabric'], '通过外部编号查询')
def test_update_printing_order(self):
"""测试更新订单"""

View File

@@ -114,12 +114,14 @@ class PrintingJobAPITestCase(TestCase):
'unit': '',
'size': '50*60',
'pieces': 10,
'sub_id': 7,
'description': '测试备注'
}
response = self.client.post('/api/v1/printing-jobs/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data['external_order_id'], 'KD20410611')
self.assertEqual(response.data['sub_id'], 7)
# 新增字段:批量推进记录(稳定输出 key默认空数组
self.assertIn('batch_advance_records', response.data)
@@ -135,6 +137,7 @@ class PrintingJobAPITestCase(TestCase):
self.assertEqual(job.quantity, 100)
self.assertEqual(job.unit, '')
self.assertEqual(job.pieces, 10)
self.assertEqual(job.sub_id, 7)
self.assertFalse(job.is_production_completed)
def test_create_printing_job_ignores_is_production_completed(self):
@@ -162,7 +165,8 @@ class PrintingJobAPITestCase(TestCase):
quantity=100,
unit='',
size='50*60',
pieces=10
pieces=10,
sub_id=101
)
job2 = printing_models.PrintingJob.objects.create(
printing_order=self.printing_order,
@@ -170,12 +174,16 @@ class PrintingJobAPITestCase(TestCase):
quantity=200,
unit='',
size='60*70',
pieces=20
pieces=20,
sub_id=202
)
response = self.client.get('/api/v1/printing-jobs/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['count'], 2)
sub_ids_by_id = {item['id']: item['sub_id'] for item in response.data['results']}
self.assertEqual(sub_ids_by_id[job1.id], 101)
self.assertEqual(sub_ids_by_id[job2.id], 202)
# 新增字段:批量推进记录(稳定输出 key
for item in response.data['results']:
@@ -640,6 +648,7 @@ class PrintingJobAPITestCase(TestCase):
'unit': '',
'size': '60*70',
'pieces': 20,
'sub_id': 8,
'description': '更新后的备注'
}
@@ -650,11 +659,13 @@ class PrintingJobAPITestCase(TestCase):
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['external_order_id'], 'KD20410611')
self.assertEqual(response.data['sub_id'], 8)
job.refresh_from_db()
self.assertEqual(job.quantity, 200)
self.assertEqual(job.unit, '')
self.assertEqual(job.pieces, 20)
self.assertEqual(job.sub_id, 8)
self.assertFalse(job.is_production_completed)
def test_update_printing_job_ignores_is_production_completed(self):
@@ -724,7 +735,8 @@ class PrintingJobAPITestCase(TestCase):
patch_data = {
'quantity': 150,
'pieces': 15
'pieces': 15,
'sub_id': None
}
response = self.client.patch(
@@ -734,10 +746,12 @@ class PrintingJobAPITestCase(TestCase):
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['external_order_id'], 'KD20410611')
self.assertIsNone(response.data['sub_id'])
job.refresh_from_db()
self.assertEqual(job.quantity, 150)
self.assertEqual(job.pieces, 15)
self.assertIsNone(job.sub_id)
self.assertEqual(job.unit, '') # 未修改字段保持不变
def test_delete_printing_job_forbidden(self):

View File

@@ -301,7 +301,7 @@ class PrintingOrderViewSet(CustomerVisibilityFilterMixin, LimitedModelViewSet):
):
queryset = queryset.filter(is_invalid=False)
if self.action in ["list", "retrieve"]:
if self.action in ["list", "retrieve", "by_external_order_id"]:
queryset = queryset.select_related("customer")
# 预取 printing_jobs 及其 business_object用于状态汇总统计
# 同时预取 state_logs 和 process 相关数据以减少 job.status 属性调用时的 N+1 查询
@@ -327,6 +327,29 @@ class PrintingOrderViewSet(CustomerVisibilityFilterMixin, LimitedModelViewSet):
status=status.HTTP_405_METHOD_NOT_ALLOWED,
)
@action(
detail=False,
methods=["get"],
url_path=r"by-external-order-id/(?P<external_order_id>[^/.]+)",
)
def by_external_order_id(self, request, external_order_id=None):
"""通过 external_order_id 获取印染订单详情"""
normalized_external_order_id = str(external_order_id or "").strip()
printing_order = (
self.get_queryset()
.filter(external_order_id=normalized_external_order_id)
.order_by("id")
.first()
)
if not printing_order:
return Response(
{"detail": "未找到该 external_order_id 对应的印染订单"},
status=status.HTTP_404_NOT_FOUND,
)
serializer = self.get_serializer(printing_order)
return Response(serializer.data)
@action(detail=True, methods=["post"])
def invalidate(self, request, pk=None):
"""