"""选品读写:表 1 领取、表 2 落结果。 claim 时 deal_flag 0 → 3;save 时写成 1 或 2。 中途崩溃会停在 3,需人工改回 0 再跑。 """ from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass from typing import Any from db import DEAL_USER_NAME, RepoUnavailable, connect_from_env, oracle_dsn _SELECT_PENDING = """ SELECT fid, oms_id, bar_code, sale_price, plan_purchase_qty, NVL(approval_no, '') AS approval_no, NVL(goods_name, '') AS goods_name, NVL(goods_spec, '') AS goods_spec, NVL(manufacturer, '') AS manufacturer, NVL(org_code, '') AS org_code, deal_flag FROM rpa_sales_goods_mapping_data WHERE deal_flag = 0 ORDER BY fid """ _SELECT_PENDING_LIMIT = f""" SELECT * FROM ( {_SELECT_PENDING} ) WHERE ROWNUM <= :limit """ _CLAIM_SQL = """ UPDATE rpa_sales_goods_mapping_data SET deal_flag = 3, deal_time = SYSDATE, update_time = SYSDATE, deal_user_name = :deal_user_name WHERE fid = :fid AND deal_flag = 0 """ # 选品无登录用户:创建人/更新人固定 0 / 系统管理员,时间用库时钟。 _INSERT_CART_SQL = """ INSERT INTO rpa_cart_settlement_goods_data ( operate_type, oms_id, platform_type, supplier_name, bar_code, purchase_price, purchase_qty, purchase_total, status, fail_reason, push_status, org_code, multi_platform_flag, create_time, create_user_id, create_user_name, update_time, update_user_id, update_user_name ) VALUES ( :operate_type, :oms_id, :platform_type, :supplier_name, :bar_code, :purchase_price, :purchase_qty, :purchase_total, :status, :fail_reason, :push_status, :org_code, :multi_platform_flag, SYSDATE, 0, '系统管理员', SYSDATE, 0, '系统管理员' ) """ _COMPLETE_SQL = """ UPDATE rpa_sales_goods_mapping_data SET deal_flag = :deal_flag, deal_result = :deal_result, deal_time = SYSDATE, update_time = SYSDATE, deal_user_name = :deal_user_name WHERE oms_id = :oms_id """ _CART_BIND_KEYS = ( "operate_type", "oms_id", "platform_type", "supplier_name", "bar_code", "purchase_price", "purchase_qty", "purchase_total", "status", "fail_reason", "push_status", "org_code", "multi_platform_flag", ) @dataclass class MappingRow: fid: int oms_id: int bar_code: str sale_price: float | None plan_purchase_qty: float | None approval_no: str goods_name: str goods_spec: str manufacturer: str org_code: str deal_flag: int deal_result: str = "" class MemorySelectRepo: def __init__(self, rows: list[MappingRow]): self.mapping: dict[int, MappingRow] = {row.oms_id: row for row in rows} self.cart: list[dict[str, Any]] = [] def claim_pending(self, limit: int | None = None) -> list[MappingRow]: pending = sorted( (row for row in self.mapping.values() if row.deal_flag == 0), key=lambda row: row.fid, ) if limit is not None: pending = pending[:limit] for row in pending: row.deal_flag = 3 return pending def save_result( self, oms_id: int, deal_flag: int, deal_result: str, cart_rows: list[dict[str, Any]], ) -> None: row = self.mapping[oms_id] row.deal_flag = deal_flag row.deal_result = deal_result self.cart.extend(dict(item) for item in cart_rows) class OracleSelectRepo: def __init__(self, connect: Callable): self._connect = connect @classmethod def from_env(cls) -> "OracleSelectRepo": oracle_dsn() return cls(connect=connect_from_env) def claim_pending(self, limit: int | None = None) -> list[MappingRow]: conn = self._connect() try: with conn.cursor() as cur: if limit is None: cur.execute(_SELECT_PENDING) else: cur.execute(_SELECT_PENDING_LIMIT, {"limit": int(limit)}) fetched = [_mapping_from_cursor(cur, rec) for rec in cur.fetchall()] claimed: list[MappingRow] = [] for row in fetched: cur.execute( _CLAIM_SQL, {"fid": row.fid, "deal_user_name": DEAL_USER_NAME}, ) if cur.rowcount == 1: row.deal_flag = 3 claimed.append(row) conn.commit() return claimed except Exception as exc: try: conn.rollback() except Exception: pass raise RepoUnavailable(str(exc)) from exc finally: conn.close() def save_result( self, oms_id: int, deal_flag: int, deal_result: str, cart_rows: list[dict[str, Any]], ) -> None: conn = self._connect() try: with conn.cursor() as cur: for row in cart_rows: payload = {key: row.get(key) for key in _CART_BIND_KEYS} cur.execute(_INSERT_CART_SQL, payload) cur.execute( _COMPLETE_SQL, { "deal_flag": deal_flag, "deal_result": deal_result, "deal_user_name": DEAL_USER_NAME, "oms_id": oms_id, }, ) conn.commit() except Exception as exc: try: conn.rollback() except Exception: pass raise RepoUnavailable(str(exc)) from exc finally: conn.close() def _mapping_from_cursor(cur, rec) -> MappingRow: cols = [item[0].lower() for item in cur.description] data = dict(zip(cols, rec)) return MappingRow( fid=int(data["fid"]), oms_id=int(data["oms_id"]), bar_code=str(data.get("bar_code") or ""), sale_price=_float_or_none(data.get("sale_price")), plan_purchase_qty=_float_or_none(data.get("plan_purchase_qty")), approval_no=str(data.get("approval_no") or ""), goods_name=str(data.get("goods_name") or ""), goods_spec=str(data.get("goods_spec") or ""), manufacturer=str(data.get("manufacturer") or ""), org_code=str(data.get("org_code") or ""), deal_flag=int(data.get("deal_flag") or 0), ) def _float_or_none(value: object) -> float | None: if value is None or value == "": return None return float(value)