133 lines
4.7 KiB
Python
133 lines
4.7 KiB
Python
# 接口 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}
|