"""M5 货品视图只读查询。账号密码来自环境变量或 connection.local.json。""" from __future__ import annotations import json import os from pathlib import Path from typing import Any from .config import settings FIXTURE_DIR = Path(__file__).resolve().parents[1] / "fixtures" M5_DIR = FIXTURE_DIR / "m5" ORDER_PATH = FIXTURE_DIR / "orders" / "yyc-fujian-20260804.json" FUJIAN_PATH = M5_DIR / "fujian.json" LOCAL_CONN_PATH = M5_DIR / "connection.local.json" def _load_json(path: Path) -> dict[str, Any]: return json.loads(path.read_text(encoding="utf-8")) def load_connection() -> dict[str, Any]: local: dict[str, Any] = {} if LOCAL_CONN_PATH.is_file(): local = _load_json(LOCAL_CONN_PATH) host = os.getenv("M5_HOST") or local.get("host") or settings.M5_HOST port = int(os.getenv("M5_PORT") or local.get("port") or settings.M5_PORT) service = os.getenv("M5_SERVICE") or local.get("serviceName") or settings.M5_SERVICE user = os.getenv("M5_USER") or local.get("user") or settings.M5_USER password = os.getenv("M5_PASSWORD") or local.get("password") or settings.M5_PASSWORD schema = os.getenv("M5_SCHEMA") or local.get("schema") or settings.M5_SCHEMA return { "host": host, "port": port, "serviceName": service, "user": user, "password": password, "schema": schema, } def recorded_source_rows(goods_id: str) -> list[dict[str, Any]] | None: """离线夹具:哈药牌这条 SQL 的 P1 结果,不连 M5。""" params = lookup_params() if str(goods_id) != params["expectedGoodsId"]: return None return [ { "ENTRYID": params["entryId"], "PRIORITY": 1, "GOODSTYPE": params["spec"], "FACTORYNAME": params["manufacturer"], } ] def evaluate_priority_source( rows: list[dict[str, Any]], *, entry_id: int, spec: str, manufacturer: str, ) -> dict[str, Any]: """PRIORITY=1 且货主/规格/厂家对上,才算货源齐全。""" priority_rows = [row for row in rows if str(row.get("PRIORITY") or "") == "1"] hit = next( ( row for row in priority_rows if str(row.get("ENTRYID") or "") == str(entry_id) and str(row.get("GOODSTYPE") or "").strip() == spec.strip() and str(row.get("FACTORYNAME") or "").strip() == manufacturer.strip() ), None, ) return { "source_add_needed": hit is None, "hit": hit, "priorityRows": priority_rows, } def lookup_params(order: dict[str, Any] | None = None, fujian: dict[str, Any] | None = None) -> dict[str, Any]: order = order or _load_json(ORDER_PATH) fujian = fujian or _load_json(FUJIAN_PATH) row = order["row"] return { "goodsNameLike": f"%{row['goodsNameLike']}%", "approvalNo": row["approvalNo"], "spec": row["spec"], "manufacturer": row["manufacturer"], "entryId": int(order["m5"]["entryId"] or fujian["entryId"]), "expectedGoodsId": str(row["expectedGoodsId"]), "sql": fujian["sql"], "sourceSql": fujian["sourceSql"], "expectedSourceAddNeeded": bool(row.get("expectedSourceAddNeeded", True)), } def connect(conn_cfg: dict[str, Any] | None = None): cfg = conn_cfg or load_connection() missing = [key for key in ("user", "password") if not cfg.get(key)] if missing: raise RuntimeError( "M5 连接缺少 " + ", ".join(missing) + "。设 M5_USER/M5_PASSWORD,或写 fixtures/m5/connection.local.json" ) import oracledb dsn = f"{cfg['host']}:{cfg['port']}/{cfg['serviceName']}" return oracledb.connect(user=cfg["user"], password=cfg["password"], dsn=dsn) def lookup_one(conn_cfg: dict[str, Any] | None = None) -> dict[str, Any]: params = lookup_params() conn = connect(conn_cfg) try: cur = conn.cursor() cur.execute( params["sql"], goodsNameLike=params["goodsNameLike"], approvalNo=params["approvalNo"], spec=params["spec"], manufacturer=params["manufacturer"], entryId=params["entryId"], ) rows = _rows_from_cursor(cur) finally: conn.close() goods_ids = [str(row.get("GOODSID") or "") for row in rows] return { "ok": len(rows) == 1 and goods_ids[0] == params["expectedGoodsId"], "expectedGoodsId": params["expectedGoodsId"], "hits": len(rows), "goodsIds": goods_ids, "rows": rows, } def _rows_from_cursor(cur) -> list[dict[str, Any]]: columns = [col[0] for col in cur.description] return [dict(zip(columns, row)) for row in cur.fetchall()] def query_priority_source(goods_id: str, conn_cfg: dict[str, Any] | None = None) -> list[dict[str, Any]]: fujian = _load_json(FUJIAN_PATH) conn = connect(conn_cfg) try: cur = conn.cursor() cur.execute(fujian["sourceSql"], goodsId=int(goods_id)) return _rows_from_cursor(cur) finally: conn.close() def check_priority_source(conn_cfg: dict[str, Any] | None = None) -> dict[str, Any]: params = lookup_params() rows = query_priority_source(params["expectedGoodsId"], conn_cfg) judged = evaluate_priority_source( rows, entry_id=params["entryId"], spec=params["spec"], manufacturer=params["manufacturer"], ) judged["ok"] = judged["source_add_needed"] == params["expectedSourceAddNeeded"] judged["expectedSourceAddNeeded"] = params["expectedSourceAddNeeded"] judged["rows"] = rows return judged if __name__ == "__main__": match = lookup_one() source = check_priority_source() if match.get("ok") else {"skipped": True, "reason": "对码未命中,不查货源"} print(json.dumps({"match": match, "source": source}, ensure_ascii=False, default=str, indent=2))