252 lines
8.4 KiB
Python
252 lines
8.4 KiB
Python
"""加购班与 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()
|