AutoRpa/rpa_cart/main.py

265 lines
8.6 KiB
Python
Raw Permalink 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.

# 加购班与 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()