265 lines
8.6 KiB
Python
265 lines
8.6 KiB
Python
# 加购班与 1 号药城支付落表。默认支付;`cart` 子命令加购。
|
||
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
from datetime import date, datetime
|
||
from pathlib import Path
|
||
|
||
import requests
|
||
|
||
from rpa_oracle import RepoUnavailable
|
||
from job import run_yaoex_pay
|
||
from parse_xls import parse_order_xls
|
||
from repo import 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
|
||
EXPORT_DIR = PROJECT_ROOT / "exports" / "yaoex"
|
||
YAOEX_PLATFORM_TYPE = 66
|
||
YBM_PLATFORM_TYPE = 48
|
||
DEFAULT_CART_LIMIT = 20
|
||
|
||
|
||
# 导出文件按订单日放进固定目录。同一秒再导一次加序号,不盖掉上一份。
|
||
def pay_export_path(
|
||
begin: str,
|
||
end: str,
|
||
exported_at: datetime,
|
||
root: Path = EXPORT_DIR,
|
||
) -> Path:
|
||
start_day = begin[:10]
|
||
end_day = end[:10]
|
||
folder_name = start_day if start_day == end_day else f"{start_day}_{end_day}"
|
||
directory = root / folder_name
|
||
stamp = exported_at.strftime("%H%M%S")
|
||
path = directory / f"{stamp}.xls"
|
||
suffix = 2
|
||
while path.exists():
|
||
path = directory / f"{stamp}_{suffix}.xls"
|
||
suffix += 1
|
||
return path
|
||
|
||
|
||
# 区分支付落表和加购。没写子命令时走支付。
|
||
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
|
||
|
||
|
||
# 支付落表没有额外参数:登录、导出当天、写表 2。
|
||
def _parse_pay_args(argv: list[str]) -> argparse.Namespace:
|
||
parser = argparse.ArgumentParser(description="1 号药城支付落表:登录、导出当天、解析、写库")
|
||
return parser.parse_args(argv)
|
||
|
||
|
||
def _parse_cart_args(argv: list[str]) -> argparse.Namespace:
|
||
"""解析加购参数:领取条数、平台、是否忽略登录缓存。
|
||
|
||
不传 --limit 时用 20,不再把空值当成全部领走。
|
||
"""
|
||
parser = argparse.ArgumentParser(description="加购班:领表 2 选品成功行,按仓加进购物车")
|
||
parser.add_argument(
|
||
"--limit",
|
||
type=int,
|
||
default=DEFAULT_CART_LIMIT,
|
||
help="本班最多领取 N 行,默认 20",
|
||
)
|
||
parser.add_argument(
|
||
"--platform",
|
||
choices=["all", "yaoex", "ybm"],
|
||
default="all",
|
||
help="all=两平台都跑",
|
||
)
|
||
parser.add_argument("--fresh-login", action="store_true", help="忽略登录缓存")
|
||
parser.add_argument("--yaoex-auth", type=Path, default=None)
|
||
parser.add_argument("--ybm-cookies", type=Path, default=None)
|
||
return parser.parse_args(argv)
|
||
|
||
|
||
# 入口。cart 子命令加购,否则跑 1 号药城支付落表。
|
||
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(repo=repo)
|
||
|
||
|
||
# 登录 1 号药城,导出当天订单,已支付行写入表 2。不对加购行。
|
||
def main_pay(repo=None) -> None:
|
||
load_cart_dotenv()
|
||
try:
|
||
auth = resolve_yaoex_auth()
|
||
token = auth.token
|
||
print("1号药城认证就绪。")
|
||
except SystemExit:
|
||
raise
|
||
except Exception as exc:
|
||
print(f"1号药城登录失败:{exc}")
|
||
raise SystemExit(2) from exc
|
||
end = date.today()
|
||
begin, end_ts = day_range(end, 0)
|
||
print(f"导出 {begin} ~ {end_ts}")
|
||
content = export_orders(token, begin=begin, end=end_ts)
|
||
xls_path = pay_export_path(begin, end_ts, datetime.now())
|
||
xls_path.parent.mkdir(parents=True, exist_ok=True)
|
||
xls_path.write_bytes(content)
|
||
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:
|
||
print(f"数据库不可用:{exc}")
|
||
raise SystemExit(2) from exc
|
||
|
||
summary = run_yaoex_pay(repo, lines)
|
||
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}"
|
||
)
|
||
|
||
|
||
# 领表 2 选品成功行,按条形码再搜并对上商家后加车。一行失败只改那一行。
|
||
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(
|
||
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(
|
||
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:
|
||
|
||
# 用本班已登录的 1 号药城会话按条形码再搜。
|
||
def search_yaoex(query_key: str):
|
||
return search_yaoex_offers(
|
||
yaoex_auth, query_key, session=yaoex_session, cache=yaoex_cache
|
||
)
|
||
|
||
# 把对上的仓按数量加入 1 号药城购物车。
|
||
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()
|