style: 统一 Ruff 代码格式
This commit is contained in:
@@ -5,6 +5,7 @@ The worker owns per-job sessions and invokes the already verified protocol
|
||||
scripts. It intentionally exposes QR images and state only; cookies and raw
|
||||
payment links never leave the worker API.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
@@ -79,15 +80,23 @@ def _safe_log(job: dict, line: str) -> None:
|
||||
clean = re.sub(r"weixin://\S+", "[付款链接已隐藏]", line)
|
||||
clean = re.sub(r"HOLD_[A-Za-z0-9_-]+", "[订单已隐藏]", clean)
|
||||
clean = re.sub(r"(订单\s*[::]\s*)\S+", r"\1[订单已隐藏]", clean)
|
||||
clean = re.sub(r"(?:token|openid|openkey|cookie)=\S+", "[敏感字段已隐藏]", clean, flags=re.I)
|
||||
clean = re.sub(r"(?:pay_token|web_token|anti_token|session_id|sessionid)\s*[:= ]\s*\S+",
|
||||
"[敏感字段已隐藏]", clean, flags=re.I)
|
||||
clean = re.sub(
|
||||
r"(?:token|openid|openkey|cookie)=\S+", "[敏感字段已隐藏]", clean, flags=re.I
|
||||
)
|
||||
clean = re.sub(
|
||||
r"(?:pay_token|web_token|anti_token|session_id|sessionid)\s*[:= ]\s*\S+",
|
||||
"[敏感字段已隐藏]",
|
||||
clean,
|
||||
flags=re.I,
|
||||
)
|
||||
timestamp = time.strftime("%H:%M:%S")
|
||||
with _lock:
|
||||
job["logs"] = (job.get("logs", []) + [f"[{timestamp}] {clean.strip()}"])[-100:]
|
||||
|
||||
|
||||
def _run_process(job_id: str, command: list[str], phase: str, mark_success: bool = True) -> int:
|
||||
def _run_process(
|
||||
job_id: str, command: list[str], phase: str, mark_success: bool = True
|
||||
) -> int:
|
||||
"""Run one stage process and return its exit code.
|
||||
|
||||
When ``mark_success`` is False the caller owns the post-success state
|
||||
@@ -104,16 +113,24 @@ def _run_process(job_id: str, command: list[str], phase: str, mark_success: bool
|
||||
stage_started = time.monotonic()
|
||||
_safe_log(job, f"[{phase}] 开始执行")
|
||||
try:
|
||||
process = subprocess.Popen(command, cwd=ROOT, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT, text=True,
|
||||
bufsize=1, env=environment)
|
||||
process = subprocess.Popen(
|
||||
command,
|
||||
cwd=ROOT,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
bufsize=1,
|
||||
env=environment,
|
||||
)
|
||||
with _lock:
|
||||
job["process_pid"] = process.pid
|
||||
assert process.stdout is not None
|
||||
for line in process.stdout:
|
||||
_safe_log(job, line)
|
||||
code = process.wait()
|
||||
_safe_log(job, f"[{phase}] 执行结束,耗时 {time.monotonic() - stage_started:.1f} 秒")
|
||||
_safe_log(
|
||||
job, f"[{phase}] 执行结束,耗时 {time.monotonic() - stage_started:.1f} 秒"
|
||||
)
|
||||
with _lock:
|
||||
job["process_pid"] = None
|
||||
if code != 0:
|
||||
@@ -134,7 +151,9 @@ def _run_process(job_id: str, command: list[str], phase: str, mark_success: bool
|
||||
job["message"] = "付款流程已完成"
|
||||
return code
|
||||
except Exception as exc: # noqa: BLE001
|
||||
_safe_log(job, f"[{phase}] 执行异常,耗时 {time.monotonic() - stage_started:.1f} 秒")
|
||||
_safe_log(
|
||||
job, f"[{phase}] 执行异常,耗时 {time.monotonic() - stage_started:.1f} 秒"
|
||||
)
|
||||
with _lock:
|
||||
job["status"] = "failed"
|
||||
job["message"] = str(exc)
|
||||
@@ -146,42 +165,74 @@ def _start_login(job_id: str, provider: str, timeout: int) -> None:
|
||||
directory = _job_dir(job_id)
|
||||
session = directory / "mall-session.json"
|
||||
qr = directory / ("qq-login.jpg" if provider == "qq" else "wechat-login.jpg")
|
||||
command = [sys.executable, f"scripts/login-{provider}.py", "--session", str(session),
|
||||
"--qr", str(qr), "--timeout", str(timeout)]
|
||||
command = [
|
||||
sys.executable,
|
||||
f"scripts/login-{provider}.py",
|
||||
"--session",
|
||||
str(session),
|
||||
"--qr",
|
||||
str(qr),
|
||||
"--timeout",
|
||||
str(timeout),
|
||||
]
|
||||
with _lock:
|
||||
job["provider"] = provider
|
||||
job["qr_path"] = str(qr)
|
||||
job["session_path"] = str(session)
|
||||
job["status"] = "waiting_login"
|
||||
job["phase"] = "login"
|
||||
threading.Thread(target=_run_process, args=(job_id, command, "login"), daemon=True).start()
|
||||
threading.Thread(
|
||||
target=_run_process, args=(job_id, command, "login"), daemon=True
|
||||
).start()
|
||||
|
||||
|
||||
def _selection_options(job_id: str, platform: str, points: int | None, zone_id: str | None = None) -> dict:
|
||||
def _selection_options(
|
||||
job_id: str, platform: str, points: int | None, zone_id: str | None = None
|
||||
) -> dict:
|
||||
if job_id not in _jobs:
|
||||
raise ValueError("任务不存在")
|
||||
selector = _load_selector()
|
||||
session = json.loads((_job_dir(job_id) / "mall-session.json").read_text(encoding="utf-8"))
|
||||
session = json.loads(
|
||||
(_job_dir(job_id) / "mall-session.json").read_text(encoding="utf-8")
|
||||
)
|
||||
cookies = session.get("cookies", {})
|
||||
products = selector.product_options(cookies, platform)
|
||||
if points is not None and not any(int(item["points"]) == points for item in products):
|
||||
if points is not None and not any(
|
||||
int(item["points"]) == points for item in products
|
||||
):
|
||||
raise ValueError("当前登录态不支持该点券档位")
|
||||
product = next((item for item in products if int(item["points"]) == points), None) if points else None
|
||||
product = (
|
||||
next((item for item in products if int(item["points"]) == points), None)
|
||||
if points
|
||||
else None
|
||||
)
|
||||
if product is None:
|
||||
product = products[0]
|
||||
cmall = selector.Cmall(cookies, str(product["offer_id"]), platform)
|
||||
zones = cmall.zones()
|
||||
selected_zone = next((zone for zone in zones if str(zone["zone_id"]) == str(zone_id)), None)
|
||||
selected_zone = next(
|
||||
(zone for zone in zones if str(zone["zone_id"]) == str(zone_id)), None
|
||||
)
|
||||
if zone_id and selected_zone is None:
|
||||
raise ValueError("区服不存在")
|
||||
selected_zone = selected_zone or (zones[0] if zones else None)
|
||||
roles = cmall.roles(selected_zone["zone_id"]) if selected_zone else []
|
||||
return {"products": products, "zones": zones, "roles": roles,
|
||||
"default_product": product, "default_zone": selected_zone}
|
||||
return {
|
||||
"products": products,
|
||||
"zones": zones,
|
||||
"roles": roles,
|
||||
"default_product": product,
|
||||
"default_zone": selected_zone,
|
||||
}
|
||||
|
||||
|
||||
def _payment_stage(job_id: str, command: list[str], phase: str, running_message: str,
|
||||
failed_message: str) -> bool:
|
||||
def _payment_stage(
|
||||
job_id: str,
|
||||
command: list[str],
|
||||
phase: str,
|
||||
running_message: str,
|
||||
failed_message: str,
|
||||
) -> bool:
|
||||
"""Run one payment stage; return True on success without touching final state."""
|
||||
job = _jobs[job_id]
|
||||
with _lock:
|
||||
@@ -210,18 +261,32 @@ def _check_payment_once(job_id: str) -> int:
|
||||
"""Read-only completion check; returns 0=confirmed, 1=not yet, 2=check failed."""
|
||||
job = _jobs[job_id]
|
||||
directory = _job_dir(job_id)
|
||||
command = [sys.executable, "scripts/jsdom-pay.py", "--check-only",
|
||||
"--session", str(directory / "mall-session.json"),
|
||||
"--out-dir", str(directory / "jsdom-order")]
|
||||
command = [
|
||||
sys.executable,
|
||||
"scripts/jsdom-pay.py",
|
||||
"--check-only",
|
||||
"--session",
|
||||
str(directory / "mall-session.json"),
|
||||
"--out-dir",
|
||||
str(directory / "jsdom-order"),
|
||||
]
|
||||
environment = os.environ.copy()
|
||||
environment.pop("NODE_OPTIONS", None)
|
||||
check_started = time.monotonic()
|
||||
_safe_log(job, "[到账检测] 开始执行")
|
||||
try:
|
||||
result = subprocess.run(command, cwd=ROOT, capture_output=True, text=True,
|
||||
env=environment, timeout=90)
|
||||
result = subprocess.run(
|
||||
command,
|
||||
cwd=ROOT,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=environment,
|
||||
timeout=90,
|
||||
)
|
||||
except subprocess.TimeoutExpired:
|
||||
_safe_log(job, f"[到账检测] 执行超时,耗时 {time.monotonic() - check_started:.1f} 秒")
|
||||
_safe_log(
|
||||
job, f"[到账检测] 执行超时,耗时 {time.monotonic() - check_started:.1f} 秒"
|
||||
)
|
||||
with _lock:
|
||||
job["payment_last_checked_at"] = int(time.time())
|
||||
return 2
|
||||
@@ -229,7 +294,9 @@ def _check_payment_once(job_id: str) -> int:
|
||||
_safe_log(job, line)
|
||||
for line in (result.stderr or "").splitlines():
|
||||
_safe_log(job, line)
|
||||
_safe_log(job, f"[到账检测] 执行结束,耗时 {time.monotonic() - check_started:.1f} 秒")
|
||||
_safe_log(
|
||||
job, f"[到账检测] 执行结束,耗时 {time.monotonic() - check_started:.1f} 秒"
|
||||
)
|
||||
with _lock:
|
||||
job["payment_last_checked_at"] = int(time.time())
|
||||
return result.returncode
|
||||
@@ -275,28 +342,62 @@ def _payment_flow(job_id: str, selection: dict) -> None:
|
||||
job["status"] = "ordering"
|
||||
job["phase"] = "payment"
|
||||
job["message"] = "正在创建商城订单"
|
||||
order_cmd = [sys.executable, "main.py", "mall", "auto", "--session", str(session),
|
||||
"--order-template", str(ROOT / "config" / "mall-order-template.json"),
|
||||
"--quantity", "1", "--product-id", str(selection["product_id"]),
|
||||
"--offer-id", str(selection["offer_id"]),
|
||||
"--role-id", str(selection["role_id"]), "--role-name", str(selection["role_name"]),
|
||||
"--zone-id", str(selection["zone_id"]), "--zone-name", str(selection["zone_name"]),
|
||||
"--output", str(response)]
|
||||
order_cmd = [
|
||||
sys.executable,
|
||||
"main.py",
|
||||
"mall",
|
||||
"auto",
|
||||
"--session",
|
||||
str(session),
|
||||
"--order-template",
|
||||
str(ROOT / "config" / "mall-order-template.json"),
|
||||
"--quantity",
|
||||
"1",
|
||||
"--product-id",
|
||||
str(selection["product_id"]),
|
||||
"--offer-id",
|
||||
str(selection["offer_id"]),
|
||||
"--role-id",
|
||||
str(selection["role_id"]),
|
||||
"--role-name",
|
||||
str(selection["role_name"]),
|
||||
"--zone-id",
|
||||
str(selection["zone_id"]),
|
||||
"--zone-name",
|
||||
str(selection["zone_name"]),
|
||||
"--output",
|
||||
str(response),
|
||||
]
|
||||
if selection.get("area"):
|
||||
order_cmd.extend(["--area", str(selection["area"])])
|
||||
if selection.get("partition"):
|
||||
order_cmd.extend(["--partition", str(selection["partition"])])
|
||||
if selection.get("order_pf"):
|
||||
order_cmd.extend(["--pf", str(selection["order_pf"])])
|
||||
if not _payment_stage(job_id, order_cmd, "order", "正在创建商城订单", "创建商城订单失败"):
|
||||
if not _payment_stage(
|
||||
job_id, order_cmd, "order", "正在创建商城订单", "创建商城订单失败"
|
||||
):
|
||||
return
|
||||
pay_cmd = [sys.executable, "scripts/jsdom-pay.py", "--session", str(session),
|
||||
"--mall-response", str(response), "--out-dir", str(output),
|
||||
"--zone-id", str(selection["zone_id"]),
|
||||
"--pf", str(selection.get("order_pf", "")),
|
||||
"--amount-fen", str(selection["price_fen"]),
|
||||
"--skip-payment-check"]
|
||||
if not _payment_stage(job_id, pay_cmd, "payment", "正在生成微信付款码", "生成付款码失败"):
|
||||
pay_cmd = [
|
||||
sys.executable,
|
||||
"scripts/jsdom-pay.py",
|
||||
"--session",
|
||||
str(session),
|
||||
"--mall-response",
|
||||
str(response),
|
||||
"--out-dir",
|
||||
str(output),
|
||||
"--zone-id",
|
||||
str(selection["zone_id"]),
|
||||
"--pf",
|
||||
str(selection.get("order_pf", "")),
|
||||
"--amount-fen",
|
||||
str(selection["price_fen"]),
|
||||
"--skip-payment-check",
|
||||
]
|
||||
if not _payment_stage(
|
||||
job_id, pay_cmd, "payment", "正在生成微信付款码", "生成付款码失败"
|
||||
):
|
||||
return
|
||||
meta_path = output / "payment-meta.json"
|
||||
try:
|
||||
@@ -318,7 +419,9 @@ def _start_payment(job_id: str, selection: dict) -> None:
|
||||
_jobs[job_id]["status"] = "ordering"
|
||||
_jobs[job_id]["phase"] = "payment"
|
||||
_jobs[job_id]["message"] = "正在创建商城订单"
|
||||
threading.Thread(target=_payment_flow, args=(job_id, selection), daemon=True).start()
|
||||
threading.Thread(
|
||||
target=_payment_flow, args=(job_id, selection), daemon=True
|
||||
).start()
|
||||
|
||||
|
||||
def _stop_job(job_id: str) -> None:
|
||||
@@ -344,13 +447,18 @@ def _stop_job(job_id: str) -> None:
|
||||
|
||||
def _public_job(job_id: str) -> dict:
|
||||
job = _jobs[job_id]
|
||||
result = {key: value for key, value in job.items()
|
||||
if key not in {"directory", "session_path", "process_pid"}}
|
||||
result = {
|
||||
key: value
|
||||
for key, value in job.items()
|
||||
if key not in {"directory", "session_path", "process_pid"}
|
||||
}
|
||||
qr_path = job.get("qr_path", "")
|
||||
if qr_path and Path(qr_path).exists():
|
||||
qr_bytes = Path(qr_path).read_bytes()
|
||||
result["qr_data"] = base64.b64encode(qr_bytes).decode("ascii")
|
||||
result["qr_mime_type"] = "image/jpeg" if qr_bytes.startswith(b"\xff\xd8\xff") else "image/png"
|
||||
result["qr_mime_type"] = (
|
||||
"image/jpeg" if qr_bytes.startswith(b"\xff\xd8\xff") else "image/png"
|
||||
)
|
||||
output = Path(job["directory"]) / "jsdom-order"
|
||||
for name in ("wechat-pay.png", "payment-status.json"):
|
||||
path = None
|
||||
@@ -359,18 +467,32 @@ def _public_job(job_id: str) -> dict:
|
||||
path = direct if direct.exists() else next(output.glob(f"*/{name}"), None)
|
||||
if path and name.endswith(".png"):
|
||||
payment_qr_bytes = path.read_bytes()
|
||||
result["payment_qr_data"] = base64.b64encode(payment_qr_bytes).decode("ascii")
|
||||
result["payment_qr_mime_type"] = "image/jpeg" if payment_qr_bytes.startswith(b"\xff\xd8\xff") else "image/png"
|
||||
result["payment_qr_data"] = base64.b64encode(payment_qr_bytes).decode(
|
||||
"ascii"
|
||||
)
|
||||
result["payment_qr_mime_type"] = (
|
||||
"image/jpeg"
|
||||
if payment_qr_bytes.startswith(b"\xff\xd8\xff")
|
||||
else "image/png"
|
||||
)
|
||||
elif path:
|
||||
try:
|
||||
status = json.loads(path.read_text(encoding="utf-8"))
|
||||
matched = status.get("matched_completion") if isinstance(status, dict) else None
|
||||
matched = (
|
||||
status.get("matched_completion")
|
||||
if isinstance(status, dict)
|
||||
else None
|
||||
)
|
||||
result["payment_status"] = {
|
||||
"checked_at": status.get("checked_at") if isinstance(status, dict) else None,
|
||||
"checked_at": status.get("checked_at")
|
||||
if isinstance(status, dict)
|
||||
else None,
|
||||
"matched_completion": {
|
||||
"is_finished": matched.get("is_finished"),
|
||||
"status": matched.get("status"),
|
||||
} if isinstance(matched, dict) else None,
|
||||
}
|
||||
if isinstance(matched, dict)
|
||||
else None,
|
||||
}
|
||||
except (OSError, json.JSONDecodeError):
|
||||
pass
|
||||
@@ -411,8 +533,14 @@ class Handler(BaseHTTPRequestHandler):
|
||||
directory = DEFAULT_DATA / job_id
|
||||
directory.mkdir(parents=True, exist_ok=True)
|
||||
os.chmod(directory, 0o700)
|
||||
_jobs[job_id] = {"job_id": job_id, "status": "created", "phase": "login",
|
||||
"logs": [], "directory": str(directory), "created_at": int(time.time())}
|
||||
_jobs[job_id] = {
|
||||
"job_id": job_id,
|
||||
"status": "created",
|
||||
"phase": "login",
|
||||
"logs": [],
|
||||
"directory": str(directory),
|
||||
"created_at": int(time.time()),
|
||||
}
|
||||
return self._json(201, _public_job(job_id))
|
||||
if len(path) == 4 and path[:2] == ["v1", "jobs"] and path[3] == "login":
|
||||
job_id = path[2]
|
||||
@@ -421,44 +549,87 @@ class Handler(BaseHTTPRequestHandler):
|
||||
return self._json(400, {"detail": "无效任务或登录方式"})
|
||||
_start_login(job_id, body["provider"], int(body.get("timeout", 600)))
|
||||
return self._json(202, _public_job(job_id))
|
||||
if len(path) == 4 and path[:2] == ["v1", "jobs"] and path[3] == "selection-options":
|
||||
if (
|
||||
len(path) == 4
|
||||
and path[:2] == ["v1", "jobs"]
|
||||
and path[3] == "selection-options"
|
||||
):
|
||||
job_id = path[2]
|
||||
body = self._body()
|
||||
options = _selection_options(job_id, str(body.get("platform", "android")), body.get("points"), body.get("zone_id"))
|
||||
options = _selection_options(
|
||||
job_id,
|
||||
str(body.get("platform", "android")),
|
||||
body.get("points"),
|
||||
body.get("zone_id"),
|
||||
)
|
||||
_jobs[job_id]["selection_options"] = options
|
||||
return self._json(200, options)
|
||||
if len(path) == 4 and path[:2] == ["v1", "jobs"] and path[3] == "selection":
|
||||
job_id = path[2]
|
||||
body = self._body()
|
||||
required = ("platform", "points", "product_id", "role_id", "role_name", "zone_id")
|
||||
required = (
|
||||
"platform",
|
||||
"points",
|
||||
"product_id",
|
||||
"role_id",
|
||||
"role_name",
|
||||
"zone_id",
|
||||
)
|
||||
if job_id not in _jobs or any(not body.get(key) for key in required):
|
||||
return self._json(400, {"detail": "选择参数不完整"})
|
||||
selector = _load_selector()
|
||||
if body["platform"] not in selector.PLATFORMS:
|
||||
return self._json(400, {"detail": "不支持的平台"})
|
||||
session_path = _job_dir(job_id) / "mall-session.json"
|
||||
cookies = json.loads(session_path.read_text(encoding="utf-8")).get("cookies", {})
|
||||
product = next((item for item in selector.product_options(cookies, body["platform"])
|
||||
if str(item["product_id"]) == str(body["product_id"])
|
||||
and int(item["points"]) == int(body["points"])), None)
|
||||
cookies = json.loads(session_path.read_text(encoding="utf-8")).get(
|
||||
"cookies", {}
|
||||
)
|
||||
product = next(
|
||||
(
|
||||
item
|
||||
for item in selector.product_options(cookies, body["platform"])
|
||||
if str(item["product_id"]) == str(body["product_id"])
|
||||
and int(item["points"]) == int(body["points"])
|
||||
),
|
||||
None,
|
||||
)
|
||||
if product is None:
|
||||
return self._json(400, {"detail": "商品已失效,请重新选择"})
|
||||
cmall = selector.Cmall(cookies, str(product["offer_id"]), body["platform"])
|
||||
zone = next((item for item in cmall.zones()
|
||||
if str(item["zone_id"]) == str(body["zone_id"])), None)
|
||||
cmall = selector.Cmall(
|
||||
cookies, str(product["offer_id"]), body["platform"]
|
||||
)
|
||||
zone = next(
|
||||
(
|
||||
item
|
||||
for item in cmall.zones()
|
||||
if str(item["zone_id"]) == str(body["zone_id"])
|
||||
),
|
||||
None,
|
||||
)
|
||||
if zone is None:
|
||||
return self._json(400, {"detail": "区服已失效,请重新选择"})
|
||||
role = next((item for item in cmall.roles(zone["zone_id"])
|
||||
if str(item["role_id"]) == str(body["role_id"])), None)
|
||||
role = next(
|
||||
(
|
||||
item
|
||||
for item in cmall.roles(zone["zone_id"])
|
||||
if str(item["role_id"]) == str(body["role_id"])
|
||||
),
|
||||
None,
|
||||
)
|
||||
if role is None or role.get("ban_status") == "1":
|
||||
return self._json(400, {"detail": "角色不可充值,请重新选择"})
|
||||
_jobs[job_id]["selection"] = {
|
||||
"platform": body["platform"], "points": product["points"],
|
||||
"platform": body["platform"],
|
||||
"points": product["points"],
|
||||
"price_fen": product["price_fen"],
|
||||
"product_id": product["product_id"], "offer_id": product["offer_id"],
|
||||
"zone_id": zone["zone_id"], "zone_name": zone["name"],
|
||||
"role_id": role["role_id"], "role_name": role["name"],
|
||||
"area": role.get("area", ""), "partition": role.get("partition", ""),
|
||||
"product_id": product["product_id"],
|
||||
"offer_id": product["offer_id"],
|
||||
"zone_id": zone["zone_id"],
|
||||
"zone_name": zone["name"],
|
||||
"role_id": role["role_id"],
|
||||
"role_name": role["name"],
|
||||
"area": role.get("area", ""),
|
||||
"partition": role.get("partition", ""),
|
||||
"order_pf": selector.PLATFORMS[body["platform"]]["order_pf"],
|
||||
}
|
||||
_jobs[job_id]["phase"] = "payment"
|
||||
@@ -473,14 +644,23 @@ class Handler(BaseHTTPRequestHandler):
|
||||
with _lock:
|
||||
if job.get("status") == "stopped":
|
||||
return self._json(400, {"detail": "任务已停止,不能生成付款码"})
|
||||
if job.get("status") in {"ordering", "waiting_payment", "payment_timeout"} \
|
||||
or job.get("process_pid"):
|
||||
return self._json(400, {"detail": "已有进行中的支付流程,请勿重复操作"})
|
||||
if job.get("status") in {
|
||||
"ordering",
|
||||
"waiting_payment",
|
||||
"payment_timeout",
|
||||
} or job.get("process_pid"):
|
||||
return self._json(
|
||||
400, {"detail": "已有进行中的支付流程,请勿重复操作"}
|
||||
)
|
||||
if not job.get("selection"):
|
||||
return self._json(400, {"detail": "请先完成角色选择"})
|
||||
_start_payment(job_id, job["selection"])
|
||||
return self._json(202, _public_job(job_id))
|
||||
if len(path) == 5 and path[:2] == ["v1", "jobs"] and path[3:5] == ["payment", "check"]:
|
||||
if (
|
||||
len(path) == 5
|
||||
and path[:2] == ["v1", "jobs"]
|
||||
and path[3:5] == ["payment", "check"]
|
||||
):
|
||||
job_id = path[2]
|
||||
if job_id not in _jobs:
|
||||
return self._json(400, {"detail": "任务不存在"})
|
||||
@@ -541,7 +721,9 @@ def _restore_jobs(data_dir: Path) -> int:
|
||||
continue
|
||||
if not meta_path.exists():
|
||||
_jobs[job_id] = {
|
||||
"job_id": job_id, "status": "ready", "phase": "selection",
|
||||
"job_id": job_id,
|
||||
"status": "ready",
|
||||
"phase": "selection",
|
||||
"logs": ["服务重启,已从任务目录恢复登录会话"],
|
||||
"directory": str(directory),
|
||||
"created_at": int(time.time()),
|
||||
@@ -569,7 +751,9 @@ def _restore_jobs(data_dir: Path) -> int:
|
||||
except (OSError, json.JSONDecodeError):
|
||||
pass
|
||||
_jobs[job_id] = {
|
||||
"job_id": job_id, "status": status, "phase": phase,
|
||||
"job_id": job_id,
|
||||
"status": status,
|
||||
"phase": phase,
|
||||
"logs": ["服务重启,已从任务目录恢复本任务"],
|
||||
"directory": str(directory),
|
||||
"created_at": int(time.time()),
|
||||
@@ -587,9 +771,15 @@ def main() -> int:
|
||||
parser.add_argument("--host", default="127.0.0.1")
|
||||
parser.add_argument("--port", type=int, default=8810)
|
||||
parser.add_argument("--data-dir", type=Path, default=DEFAULT_DATA)
|
||||
parser.add_argument("--evidence-ttl-hours", type=int, default=DEFAULT_EVIDENCE_TTL_HOURS,
|
||||
help="任务原始证据保留时长;0 表示不自动清理")
|
||||
parser.add_argument("--key", default=None, help="HTTP Bearer 鉴权密钥;默认读取 YYB_WORKER_KEY")
|
||||
parser.add_argument(
|
||||
"--evidence-ttl-hours",
|
||||
type=int,
|
||||
default=DEFAULT_EVIDENCE_TTL_HOURS,
|
||||
help="任务原始证据保留时长;0 表示不自动清理",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--key", default=None, help="HTTP Bearer 鉴权密钥;默认读取 YYB_WORKER_KEY"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
# 强制绝对路径:子进程以 ROOT 为 cwd,相对路径会让任务文件写到错误位置。
|
||||
DEFAULT_DATA = Path(args.data_dir).resolve()
|
||||
@@ -603,7 +793,10 @@ def main() -> int:
|
||||
os.chmod(DEFAULT_DATA, 0o700)
|
||||
removed = _cleanup_expired_jobs(DEFAULT_DATA, args.evidence_ttl_hours)
|
||||
if removed:
|
||||
print(f"已清理 {removed} 个过期任务证据(保留期 {args.evidence_ttl_hours} 小时)", flush=True)
|
||||
print(
|
||||
f"已清理 {removed} 个过期任务证据(保留期 {args.evidence_ttl_hours} 小时)",
|
||||
flush=True,
|
||||
)
|
||||
restored = _restore_jobs(DEFAULT_DATA)
|
||||
if restored:
|
||||
print(f"已从任务目录恢复 {restored} 个支付任务", flush=True)
|
||||
|
||||
Reference in New Issue
Block a user