AutoRpa/state_center/app/m5_client.py

175 lines
5.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""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))