"""加购班与 1 号药城支付落表。默认支付;`cart` 子命令加购。""" from __future__ import annotations import argparse import tempfile from datetime import date from pathlib import Path import requests from db import RepoUnavailable from job import run_yaoex_pay from parse_xls import parse_order_xls from repo import EmptyPayRepo, OraclePayRepo from yaoex_client import day_range, export_orders from yaoex_login import ( BrowserRuntime, load_cart_dotenv, resolve_yaoex_auth, resolve_ybm_cookies, ) PROJECT_ROOT = Path(__file__).resolve().parent YAOEX_PLATFORM_TYPE = 66 YBM_PLATFORM_TYPE = 48 def parse_args(argv: list[str] | None = None) -> argparse.Namespace: argv = list(argv) if argv is not None else None if argv is None: import sys argv = sys.argv[1:] command = "pay" if argv and argv[0] in {"pay", "cart"}: command = argv.pop(0) if command == "cart": args = _parse_cart_args(argv) args.command = "cart" return args args = _parse_pay_args(argv) args.command = "pay" return args def _parse_pay_args(argv: list[str]) -> argparse.Namespace: parser = argparse.ArgumentParser(description="1 号药城支付落表") parser.add_argument("--xls", type=Path, default=None, help="已导出的订单明细 xls,跳过商城导出") parser.add_argument( "--days", type=int, default=0, help="导出日期跨度,0=仅当天。与 --xls 互斥时仍用于无 xls 的导出", ) parser.add_argument("--day", type=str, default=None, help="结束日期 YYYY-MM-DD,默认今天") parser.add_argument("--dry-run", action="store_true", help="解析但不写库") parser.add_argument("--fresh-login", action="store_true", help="忽略登录缓存") parser.add_argument("--secrets", type=Path, default=None) parser.add_argument("--yaoex-auth", type=Path, default=None) return parser.parse_args(argv) def _parse_cart_args(argv: list[str]) -> argparse.Namespace: parser = argparse.ArgumentParser(description="加购班:领表 2 选品成功行,按仓加进购物车") parser.add_argument("--limit", type=int, default=None, help="本班最多领取 N 行") parser.add_argument( "--platform", choices=["all", "yaoex", "ybm"], default="all", help="all=两平台都跑", ) parser.add_argument("--fresh-login", action="store_true", help="忽略登录缓存") parser.add_argument("--secrets", type=Path, default=None) parser.add_argument("--yaoex-auth", type=Path, default=None) parser.add_argument("--ybm-cookies", type=Path, default=None) return parser.parse_args(argv) def main(argv: list[str] | None = None, repo=None) -> None: args = parse_args(argv) if args.command == "cart": main_cart(args, repo=repo) return main_pay(args, repo=repo) def main_pay(args: argparse.Namespace, repo=None) -> None: load_cart_dotenv() xls_path = args.xls tmp_xls: Path | None = None if xls_path is None: try: auth = resolve_yaoex_auth( secrets_path=args.secrets, auth_path=args.yaoex_auth, fresh=args.fresh_login, ) token = auth.token print("1号药城认证就绪。") except SystemExit: raise except Exception as exc: print(f"1号药城登录失败:{exc}") raise SystemExit(2) from exc end = date.fromisoformat(args.day) if args.day else date.today() begin, end_ts = day_range(end, args.days) print(f"导出 {begin} ~ {end_ts}") content = export_orders(token, begin=begin, end=end_ts) tmp_dir = Path(tempfile.mkdtemp(prefix="yaoex_pay_")) tmp_xls = tmp_dir / "orders.xls" tmp_xls.write_bytes(content) xls_path = tmp_xls print(f"导出文件 {xls_path} ({len(content)} bytes)") lines = parse_order_xls(xls_path) print(f"导出行 {len(lines)}") if repo is None: try: repo = OraclePayRepo.from_env() except RepoUnavailable as exc: if not args.dry_run: print(f"数据库不可用:{exc}") raise SystemExit(2) from exc print(f"无库,dry-run 只解析:{exc}") repo = EmptyPayRepo() summary = run_yaoex_pay(repo, lines, dry_run=args.dry_run) print( "落表 " f"inserted={summary.inserted} " f"skipped_status={summary.skipped_status} " f"skipped_dup={summary.skipped_dup} " f"unmatched={summary.unmatched} " f"reasons={summary.unmatched_reasons}" ) def main_cart(args: argparse.Namespace, repo=None) -> None: from cart_job import run_cart_batch from cart_repo import OracleCartRepo from cart_search import search_yaoex_offers, search_ybm_offers from cart_yaoex import add_yaoex_cart from cart_ybm import add_ybm_cart load_cart_dotenv() if repo is None: try: repo = OracleCartRepo.from_env() except RepoUnavailable as exc: print(f"数据库不可用:{exc}") raise SystemExit(2) from exc want_yaoex = args.platform in {"all", "yaoex"} want_ybm = args.platform in {"all", "ybm"} login_errors: dict[int, str] = {} searchers = {} adders = {} yaoex_auth = None ybm_cookies = None runtime = BrowserRuntime(keep=False) yaoex_session = requests.Session() ybm_session = requests.Session() yaoex_cache: dict = {} ybm_cache: dict = {} try: if want_yaoex: try: yaoex_auth = resolve_yaoex_auth( secrets_path=args.secrets, auth_path=args.yaoex_auth, fresh=args.fresh_login, runtime=runtime, ) print("1号药城认证就绪。") except SystemExit: raise except Exception as exc: login_errors[YAOEX_PLATFORM_TYPE] = str(exc) print(f"1号药城登录失败,本平台中止:{exc}") if want_ybm: try: ybm_cookies = resolve_ybm_cookies( secrets_path=args.secrets, cookie_path=args.ybm_cookies, fresh=args.fresh_login, runtime=runtime, ) print("药帮忙认证就绪。") except SystemExit: raise except Exception as exc: login_errors[YBM_PLATFORM_TYPE] = str(exc) print(f"药帮忙登录失败,本平台中止:{exc}") if ybm_cookies: for name, value in ybm_cookies.items(): ybm_session.cookies.set(name, value) if yaoex_auth is not None: def search_yaoex(query_key: str): return search_yaoex_offers( yaoex_auth, query_key, session=yaoex_session, cache=yaoex_cache ) def add_yaoex(offer, qty: int): return add_yaoex_cart(yaoex_auth, offer, qty, session=yaoex_session) searchers[YAOEX_PLATFORM_TYPE] = search_yaoex adders[YAOEX_PLATFORM_TYPE] = add_yaoex if ybm_cookies is not None: def search_ybm(query_key: str): return search_ybm_offers( ybm_cookies, query_key, session=ybm_session, cache=ybm_cache ) def add_ybm(offer, qty: int): return add_ybm_cart(ybm_cookies, offer, qty, session=ybm_session) searchers[YBM_PLATFORM_TYPE] = search_ybm adders[YBM_PLATFORM_TYPE] = add_ybm platform_types: list[int] = [] if want_yaoex: platform_types.append(YAOEX_PLATFORM_TYPE) if want_ybm: platform_types.append(YBM_PLATFORM_TYPE) summary = run_cart_batch( repo, searchers=searchers, adders=adders, login_errors=login_errors, limit=args.limit, platform_types=platform_types, ) print( f"领取={summary.claimed} 加购成功={summary.added} 失败={summary.failed}" ) print("未推订单中心,未清空购物车。") finally: yaoex_session.close() ybm_session.close() runtime.close() if __name__ == "__main__": main()