fix: RIIN 商品状态补 COMPLETE/PAUSE 映射,修复 Goods.status 大面积 NULL
CI / test (pull_request) Successful in 1m35s
CI / test (pull_request) Successful in 1m35s
问题:RIIN 商品级 goodsStatus 复用平台状态枚举,COMPLETE/PAUSE 无映射, .get() 返回 None 落库为 NULL,累计 206 万行(占 RIIN 商品 33.75%), 终态订单不再被 refresh_pending 刷新而长期滞留。该缺口继承自 inkreach-bi-auth-mng TS 版,且 TS 的 warnUnknown 告警在移植时被省略, 导致从未暴露。 修复: - RIIN_GOODS_STATUS_MAP 补 COMPLETE=已完成、PAUSE=暂停 - 对齐 TS 版 warnUnknown:四个状态映射函数对未知值告警(同值只报一次) - docs/sql:Goods.status 约束新增允许"暂停"(原约束拒绝,已在库执行) - scripts/backfill_riin_goods_status.py:存量 NULL 按"已发货"订单终态 近似回填(幂等、分批);"已关闭"与非终态订单走 by-orders 重拉真值 - tests/test_mappers.py:9 个单测全过;实单 GSU1S555400MFLL e2e 验证 4 行 NULL → 已完成/暂停
This commit is contained in:
+49
-8
@@ -33,6 +33,8 @@ RIIN_EXPRESS_SOURCE_MAP: dict[int, str] = {
|
||||
RIIN_GOODS_STATUS_MAP: dict[str, str] = {
|
||||
"NOT_SHIPPED": "待发货", "SHIPPED": "已发货", "CANCEL": "已取消",
|
||||
"CANCELLED": "已取消", "REFUNDING": "退款中",
|
||||
# 商品级状态复用平台状态枚举,COMPLETE/PAUSE 缺失曾导致 1/3 商品行 status 为 NULL
|
||||
"COMPLETE": "已完成", "PAUSE": "暂停",
|
||||
}
|
||||
|
||||
RIIN_PLATFORM_STATUS_MAP: dict[str, str] = {
|
||||
@@ -55,6 +57,17 @@ SDS_CHANNEL_TYPE_LABEL_MAP: dict[str, str] = {
|
||||
|
||||
# ---------- 日期解析 ----------
|
||||
|
||||
# 未知枚举值告警:同值只报一次,避免批量同步刷屏(对齐 TS 版 warnUnknown)
|
||||
_warned_values: set[str] = set()
|
||||
|
||||
|
||||
def _warn_unknown(map_name: str, value: object) -> None:
|
||||
key = f"{map_name}:{value!r}"
|
||||
if key not in _warned_values:
|
||||
_warned_values.add(key)
|
||||
print(f"[mapper] unknown {map_name} value: {value!r}")
|
||||
|
||||
|
||||
def safe_date(value: Any, context: str = "") -> datetime:
|
||||
"""解析日期字符串(无时区视为 +08:00),返回 tz-aware datetime。"""
|
||||
if not value or not isinstance(value, str):
|
||||
@@ -188,15 +201,20 @@ class GoodsUpsertInput:
|
||||
|
||||
# ---------- RIIN 映射 ----------
|
||||
|
||||
def map_riin_system_status(order_status: int) -> str | None:
|
||||
return RIIN_SYSTEM_STATUS_MAP.get(order_status)
|
||||
def map_riin_system_status(order_status: int | None) -> str | None:
|
||||
if order_status is None:
|
||||
return None
|
||||
v = RIIN_SYSTEM_STATUS_MAP.get(order_status)
|
||||
if v is None:
|
||||
_warn_unknown("RIIN systemStatus", order_status)
|
||||
return v
|
||||
|
||||
|
||||
def map_riin_order(raw: Any) -> OrderUpsertInput:
|
||||
platform_oid = raw.platform_oid
|
||||
ordered_at = _parse(raw.push_time, f"RIIN order {platform_oid} pushTime")
|
||||
confirmed_at = _parse(raw.distributor_push_time, f"RIIN order {platform_oid} distributorPushTime")
|
||||
system_status = map_riin_system_status(raw.order_status) if raw.order_status is not None else None
|
||||
system_status = map_riin_system_status(raw.order_status)
|
||||
is_terminal = system_status in ("已发货", "已关闭", "已取消")
|
||||
completed_at: datetime | None = None
|
||||
if is_terminal:
|
||||
@@ -236,6 +254,15 @@ def map_riin_order(raw: Any) -> OrderUpsertInput:
|
||||
)
|
||||
|
||||
|
||||
def map_riin_goods_status(goods_status: str | None) -> str | None:
|
||||
if not goods_status:
|
||||
return None
|
||||
v = RIIN_GOODS_STATUS_MAP.get(goods_status)
|
||||
if v is None:
|
||||
_warn_unknown("RIIN goodsStatus", goods_status)
|
||||
return v
|
||||
|
||||
|
||||
def map_riin_goods(raw: Any, parent_order_no: str, parent_created_at: datetime) -> GoodsUpsertInput:
|
||||
product_code = raw.product_code
|
||||
item_name = product_code.split("-")[0] if product_code else None
|
||||
@@ -250,7 +277,7 @@ def map_riin_goods(raw: Any, parent_order_no: str, parent_created_at: datetime)
|
||||
itemSize=raw.size_code,
|
||||
itemStyleNo=style_no,
|
||||
quantity=raw.num if raw.num is not None else 1,
|
||||
status=RIIN_GOODS_STATUS_MAP.get(raw.goods_status) if raw.goods_status else None,
|
||||
status=map_riin_goods_status(raw.goods_status),
|
||||
)
|
||||
|
||||
|
||||
@@ -262,13 +289,27 @@ def map_sds_channel_type(channel_type: Any) -> str | None:
|
||||
return SDS_CHANNEL_TYPE_LABEL_MAP.get(channel_type)
|
||||
|
||||
|
||||
def map_sds_system_status(status: int) -> str | None:
|
||||
return SDS_SYSTEM_STATUS_MAP.get(status)
|
||||
def map_sds_system_status(status: int | None) -> str | None:
|
||||
if status is None:
|
||||
return None
|
||||
v = SDS_SYSTEM_STATUS_MAP.get(status)
|
||||
if v is None:
|
||||
_warn_unknown("SDS systemStatus", status)
|
||||
return v
|
||||
|
||||
|
||||
def map_sds_goods_status(status: int | None) -> str | None:
|
||||
if status is None:
|
||||
return None
|
||||
v = SDS_GOODS_STATUS_MAP.get(status)
|
||||
if v is None:
|
||||
_warn_unknown("SDS goodsStatus", status)
|
||||
return v
|
||||
|
||||
|
||||
def map_sds_order(raw: Any, channel_type_map: dict[int, str | None], pre_courier: str | None = None) -> OrderUpsertInput:
|
||||
ordered_at = _parse(raw.gmt_pay, f"SDS order {raw.no} gmtPay")
|
||||
system_status = map_sds_system_status(raw.status) if raw.status is not None else None
|
||||
system_status = map_sds_system_status(raw.status)
|
||||
is_terminal = system_status in ("已完成", "已取消")
|
||||
completed_at: datetime | None = None
|
||||
if is_terminal and raw.finish_time and raw.finish_time > 0:
|
||||
@@ -369,7 +410,7 @@ def map_sds_goods(raw: Any, parent_order_no: str, parent_created_at: datetime) -
|
||||
itemSize=raw.product_size,
|
||||
itemStyleNo=_parse_sds_style_no(product_name),
|
||||
quantity=raw.num if raw.num is not None else 1,
|
||||
status=SDS_GOODS_STATUS_MAP.get(raw.status) if raw.status is not None else None,
|
||||
status=map_sds_goods_status(raw.status),
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user