# 接口 1 整批校验与落表。不跑选品。 # # 入参 camelCase,落表 snake_case。长度跟 VARCHAR2。 # # 先在内存里标 REJECTED / 同批 CONFLICT,再一次查重、一次插入。 # 单条结果: # - ACCEPTED:入库,deal_flag=0,等选品作业捞 # - DUPLICATE:库里已有同一 omsId,或并发撞上 uk_rpa_sgmd_oms_id # - CONFLICT:同一请求里 omsId 出现过(前一条哪怕被拒,后一条也算冲突) # - REJECTED:字段不过,不入库 # # 混批 HTTP 仍 200。omsId / 数量必须是 JSON 整数,不把 "10" 当 10。 from __future__ import annotations from . import messages from .parse import parse_date, parse_decimal, parse_int, parse_oms_id, parse_str _STRING_FIELDS = ( ("barCode", "bar_code", 40), ("goodsId", "goods_id", 20), ("goodsName", "goods_name", 200), ("goodsSpec", "goods_spec", 100), ("manufacturer", "manufacturer", 100), ("approvalNo", "approval_no", 100), ("unit", "unit", 20), ("customerId", "customer_id", 20), ("customerName", "customer_name", 200), ("orgCode", "org_code", 10), ("goodsMdmCode", "goods_mdm_code", 100), ("customerMdmCode", "customer_mdm_code", 100), ) _INT_FIELDS = ( ("saleQty", "sale_qty"), ("planPurchaseQty", "plan_purchase_qty"), ) # 整批接收销售商品:先校验再落库,返回与入参下标对齐的结果。 def receive_sales_goods(rows: list, repo) -> list[dict]: results: list[dict | None] = [None] * len(rows) pending: list[tuple[int, dict]] = [] seen: set[int] = set() for index, row in enumerate(rows): decided = _classify(row, seen) if "resultCode" in decided: results[index] = decided else: pending.append((index, decided)) if pending: _persist(results, pending, repo) return [item for item in results if item is not None] # 内存判定:对象/字段/同批冲突当场出结果,通过则返回待插入记录。 def _classify(row: object, seen: set[int]) -> dict: if not isinstance(row, dict): return _result(None, "REJECTED", messages.ROW_NOT_OBJECT) oms_id, oms_error = parse_oms_id(row.get("omsId")) if oms_id is not None and oms_id in seen: return _result(oms_id, "CONFLICT", messages.SALES_CONFLICT) if oms_id is not None: seen.add(oms_id) parsed, error = _parse_row(row, oms_id, oms_error) if error: _camel, message = error return _result(row.get("omsId"), "REJECTED", message) return parsed # 对通过校验的行一次查重、一次插入,把 DUPLICATE / ACCEPTED 填回 results。 def _persist(results: list[dict | None], pending: list[tuple[int, dict]], repo) -> None: existing = repo.existing_oms_ids([record["oms_id"] for _index, record in pending]) to_insert: list[dict] = [] insert_at: list[int] = [] for index, record in pending: if record["oms_id"] in existing: results[index] = _result( record["oms_id"], "DUPLICATE", messages.SALES_DUPLICATE ) else: to_insert.append(record) insert_at.append(index) collided = repo.insert_many(to_insert) if to_insert else set() for index, record in zip(insert_at, to_insert): if record["oms_id"] in collided: results[index] = _result( record["oms_id"], "DUPLICATE", messages.SALES_DUPLICATE ) else: results[index] = _result( record["oms_id"], "ACCEPTED", messages.SALES_ACCEPTED ) # 把一条明细收成表 1 行。失败返回 (None, (字段, 说明))。 def _parse_row(row: dict, oms_id: int | None, oms_error: str | None): if oms_error: return None, ("omsId", oms_error) record: dict = {"oms_id": oms_id, "deal_flag": 0} for camel, snake, max_len in _STRING_FIELDS: value, error = parse_str(row.get(camel), camel, max_len) if error: return None, (camel, error) record[snake] = value order_date, error = parse_date(row.get("orderDate"), "orderDate") if error: return None, ("orderDate", error) record["order_date"] = order_date sale_price, error = parse_decimal(row.get("salePrice"), "salePrice") if error: return None, ("salePrice", error) record["sale_price"] = sale_price for camel, snake in _INT_FIELDS: value, error = parse_int(row.get(camel), camel) if error: return None, (camel, error) record[snake] = value return record, None # 拼一条接口 1 的 results[] 项。 def _result(oms_id, result_code: str, message: str) -> dict: return {"omsId": oms_id, "resultCode": result_code, "message": message}