forked from erp-dev/erp
feat: batch update for plate order
This commit is contained in:
5
api_v1/views/mingdaoyun/__init__.py
Normal file
5
api_v1/views/mingdaoyun/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
"""MingdaoYun related API views."""
|
||||
|
||||
from .plate_order_staging import MDYPlateOrderStagingViewSet
|
||||
|
||||
__all__ = ["MDYPlateOrderStagingViewSet"]
|
||||
102
api_v1/views/mingdaoyun/plate_order_staging.py
Normal file
102
api_v1/views/mingdaoyun/plate_order_staging.py
Normal file
@@ -0,0 +1,102 @@
|
||||
"""开版暂存(明道云)查询 API。
|
||||
|
||||
需求:
|
||||
- 默认按 -ctime 排序
|
||||
- 支持 raw__62d52f4b8d2972284492dd0e(设计编号)过滤
|
||||
- 支持 LimitOffset 分页
|
||||
- 返回时把 raw / related 两个 JSONField 做“可读化/碾平”
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from django.db.models import F
|
||||
from rest_framework import serializers, viewsets
|
||||
from rest_framework.pagination import LimitOffsetPagination
|
||||
from rest_framework.permissions import DjangoModelPermissions
|
||||
|
||||
from api_v1.models import MDYPlateOrderStaging
|
||||
from flower.utils.mingdaoyun.mappings import plate_order_field_definitions
|
||||
from flower.utils.mingdaoyun.relations import flatten_row_by_field_definitions, normalize_mdy_value
|
||||
|
||||
|
||||
_PLATE_ORDER_DESIGN_NO_CONTROL_ID = "62d52f4b8d2972284492dd0e"
|
||||
|
||||
|
||||
class MDYPlateOrderStagingSerializer(serializers.ModelSerializer):
|
||||
"""把 raw/related 输出为扁平的可读结构。"""
|
||||
|
||||
design_no = serializers.SerializerMethodField()
|
||||
raw = serializers.SerializerMethodField()
|
||||
related = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = MDYPlateOrderStaging
|
||||
fields = [
|
||||
"id",
|
||||
"mdy_rowid",
|
||||
"ctime",
|
||||
"utime",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"design_no",
|
||||
"raw",
|
||||
"related",
|
||||
]
|
||||
|
||||
def get_design_no(self, obj: MDYPlateOrderStaging) -> Any:
|
||||
if isinstance(obj.raw, dict):
|
||||
return obj.raw.get(_PLATE_ORDER_DESIGN_NO_CONTROL_ID)
|
||||
return None
|
||||
|
||||
def get_raw(self, obj: MDYPlateOrderStaging) -> dict[str, Any]:
|
||||
if not isinstance(obj.raw, dict):
|
||||
return {}
|
||||
# 1) 已知字段:按映射转换为中文 key(更可读)
|
||||
out = flatten_row_by_field_definitions(obj.raw, plate_order_field_definitions, include_system_keys=True)
|
||||
|
||||
# 2) 未映射字段:为了排查方便,不静默丢弃,保留 controlId 原样
|
||||
known_control_ids = set(plate_order_field_definitions.keys())
|
||||
for k, v in obj.raw.items():
|
||||
if k in {"rowid", "ctime", "utime"}:
|
||||
continue
|
||||
if k in known_control_ids:
|
||||
continue
|
||||
out[str(k)] = normalize_mdy_value(v)
|
||||
|
||||
return out
|
||||
|
||||
def get_related(self, obj: MDYPlateOrderStaging) -> list[dict[str, Any]]:
|
||||
if not obj.related:
|
||||
return []
|
||||
if not isinstance(obj.related, list):
|
||||
return []
|
||||
|
||||
out: list[dict[str, Any]] = []
|
||||
for item in obj.related:
|
||||
if isinstance(item, dict):
|
||||
out.append({k: normalize_mdy_value(v) for k, v in item.items()})
|
||||
else:
|
||||
# 兜底:若数据被写成非 dict 元素,仍然返回原值,便于排查
|
||||
out.append({"value": item})
|
||||
return out
|
||||
|
||||
|
||||
class MDYPlateOrderStagingViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
"""开版暂存数据查询(只读)。"""
|
||||
|
||||
serializer_class = MDYPlateOrderStagingSerializer
|
||||
# permission_classes = [DjangoModelPermissions]
|
||||
pagination_class = LimitOffsetPagination
|
||||
|
||||
def get_queryset(self):
|
||||
qs = MDYPlateOrderStaging.objects.all()
|
||||
|
||||
# 支持按 raw 中的“设计编号/订单id”过滤(按你要求的 query param 名)
|
||||
design_no = self.request.query_params.get(f"raw__{_PLATE_ORDER_DESIGN_NO_CONTROL_ID}")
|
||||
if design_no:
|
||||
qs = qs.filter(**{f"raw__{_PLATE_ORDER_DESIGN_NO_CONTROL_ID}": design_no})
|
||||
|
||||
# 默认 -ctime;ctime 为空的放最后,避免老数据(未回填)跑到前面
|
||||
return qs.order_by(F("ctime").desc(nulls_last=True), F("id").desc())
|
||||
101
api_v1/views/mingdaoyun/test_plate_order_staging_api.py
Normal file
101
api_v1/views/mingdaoyun/test_plate_order_staging_api.py
Normal file
@@ -0,0 +1,101 @@
|
||||
"""MDYPlateOrderStaging 查询 API 测试"""
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.test import TestCase
|
||||
from django.utils import timezone
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from api_v1.models import MDYPlateOrderStaging
|
||||
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class MDYPlateOrderStagingAPITestCase(TestCase):
|
||||
def setUp(self):
|
||||
self.client = APIClient()
|
||||
|
||||
self.user = User.objects.create_user(
|
||||
username="testuser",
|
||||
password="testpass123",
|
||||
email="test@example.com",
|
||||
)
|
||||
self.client.force_authenticate(user=self.user)
|
||||
|
||||
view_perm = Permission.objects.get(codename="view_mdyplateorderstaging")
|
||||
self.user.user_permissions.add(view_perm)
|
||||
|
||||
now = timezone.now()
|
||||
|
||||
# older
|
||||
self.row1 = MDYPlateOrderStaging.objects.create(
|
||||
mdy_rowid="row1",
|
||||
ctime=now - timedelta(days=1),
|
||||
utime=now - timedelta(days=1, minutes=1),
|
||||
raw={
|
||||
"rowid": "row1",
|
||||
"ctime": "2025-01-01 00:00:00",
|
||||
"utime": "2025-01-01 00:01:00",
|
||||
"62d52f4b8d2972284492dd0e": "A001", # 设计编号
|
||||
"62d52f4b8d2972284492dd2c": "款号A", # 款号名称
|
||||
},
|
||||
related=[
|
||||
{
|
||||
"rowid": "rel1",
|
||||
"source": "drawing",
|
||||
"source_name": "画图",
|
||||
"设计师名字": "张三",
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
# newer
|
||||
self.row2 = MDYPlateOrderStaging.objects.create(
|
||||
mdy_rowid="row2",
|
||||
ctime=now,
|
||||
utime=now,
|
||||
raw={
|
||||
"rowid": "row2",
|
||||
"ctime": "2025-01-02 00:00:00",
|
||||
"utime": "2025-01-02 00:01:00",
|
||||
"62d52f4b8d2972284492dd0e": "A002", # 设计编号
|
||||
"62d52f4b8d2972284492dd2c": "款号B", # 款号名称
|
||||
},
|
||||
related=[],
|
||||
)
|
||||
|
||||
def test_list_default_order_and_flatten(self):
|
||||
resp = self.client.get("/api/v1/mdy-plate-order-staging/?limit=10&offset=0")
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
payload = resp.json()
|
||||
self.assertEqual(payload["count"], 2)
|
||||
|
||||
first = payload["results"][0]
|
||||
self.assertEqual(first["mdy_rowid"], "row2") # -ctime 排序
|
||||
|
||||
# raw 已按字段定义“可读化”
|
||||
self.assertIn("raw", first)
|
||||
self.assertIn("设计编号", first["raw"])
|
||||
self.assertEqual(first["raw"]["设计编号"], "A002")
|
||||
self.assertEqual(first["raw"]["款号名称"], "款号B")
|
||||
|
||||
# related 以 list[dict] 返回
|
||||
self.assertIn("related", first)
|
||||
self.assertEqual(first["related"], [])
|
||||
|
||||
def test_filter_by_raw_design_no(self):
|
||||
resp = self.client.get(
|
||||
"/api/v1/mdy-plate-order-staging/?limit=10&offset=0&raw__62d52f4b8d2972284492dd0e=A001"
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
payload = resp.json()
|
||||
self.assertEqual(payload["count"], 1)
|
||||
self.assertEqual(payload["results"][0]["mdy_rowid"], "row1")
|
||||
|
||||
self.assertEqual(payload["results"][0]["raw"]["设计编号"], "A001")
|
||||
self.assertEqual(payload["results"][0]["related"][0]["source"], "drawing")
|
||||
Reference in New Issue
Block a user