"""应用宝充值任务服务。""" from __future__ import annotations from datetime import datetime, timezone from typing import Any from sqlalchemy.orm import Session from ..models import YybRechargeTask from .yyb_worker_client import YybWorkerClient def _utcnow(): return datetime.now(timezone.utc) def _from_ts(value) -> datetime | None: try: return datetime.fromtimestamp(int(value), tz=timezone.utc) if value else None except (TypeError, ValueError, OSError): return None def _as_utc(value: datetime | None) -> datetime | None: """DB 时间列为 naive UTC;输出时补 UTC 时区标记,前端才能按本地时区正确显示。""" if value is None: return None if value.tzinfo is None: return value.replace(tzinfo=timezone.utc) return value.astimezone(timezone.utc) def cleanup_orphan_yyb_tasks(db: Session, message: str) -> int: rows = db.query(YybRechargeTask).filter( YybRechargeTask.status.in_(["created", "waiting_login", "ready", "running", "ordering", "waiting_payment"]) ).all() for task in rows: if task.status == "ordering": task.status = "waiting_payment" task.message = "服务重启,支付流程中断,可重新检测到账" elif task.status == "waiting_payment": task.message = "服务重启,到账检测中断,可重新检测到账" else: task.status = "failed" task.message = message task.finished_at = _utcnow() if rows: db.commit() return len(rows) def sync_task(db: Session, task: YybRechargeTask, worker: YybWorkerClient) -> YybRechargeTask: data = worker.get_job(task.worker_job_id) task.status = str(data.get("status", task.status)) task.phase = str(data.get("phase", task.phase)) task.message = str(data.get("message", task.message)) if data.get("provider"): task.provider = str(data["provider"]) if data.get("qr_data"): task.login_qr_data = str(data["qr_data"]) task.result = {**(task.result or {}), "login_qr_mime_type": data.get("qr_mime_type", "image/jpeg")} if data.get("payment_qr_data"): task.payment_qr_data = str(data["payment_qr_data"]) task.result = {**(task.result or {}), "payment_qr_mime_type": data.get("payment_qr_mime_type", "image/png")} task.result = { **(task.result or {}), "logs": data.get("logs", []), "payment_status": data.get("payment_status"), } qr_created_at = _from_ts(data.get("payment_qr_created_at")) if qr_created_at: task.payment_qr_created_at = qr_created_at last_checked_at = _from_ts(data.get("payment_last_checked_at")) if last_checked_at: task.payment_last_checked_at = last_checked_at if task.status in {"success", "failed"} and task.finished_at is None: task.finished_at = _utcnow() if task.status not in {"success", "failed", "stopped"} and task.finished_at is not None: task.finished_at = None db.commit() db.refresh(task) return task def public_task(task: YybRechargeTask, include_qr: bool = True, include_payment_qr: bool | None = None, creator_username: str = "") -> dict[str, Any]: result: dict[str, Any] = { "id": task.id, "task_id": task.task_id, "provider": task.provider, "platform": task.platform, "points": task.points, "price_fen": task.price_fen, "product_id": task.product_id, "zone_id": task.zone_id, "zone_name": task.zone_name, "role_id": task.role_id, "role_name": task.role_name, "status": task.status, "phase": task.phase, "message": task.message, "result": task.result, "created_by": task.created_by, "created_by_username": creator_username, "created_at": _as_utc(task.created_at), "finished_at": _as_utc(task.finished_at), "payment_started_at": _as_utc(task.payment_started_at), "payment_qr_created_at": _as_utc(task.payment_qr_created_at), "payment_last_checked_at": _as_utc(task.payment_last_checked_at), } if task.result: result["login_qr_mime_type"] = task.result.get("login_qr_mime_type", "image/jpeg") result["payment_qr_mime_type"] = task.result.get("payment_qr_mime_type", "image/png") if include_payment_qr is None: include_payment_qr = include_qr if include_qr: result["login_qr_data"] = task.login_qr_data or "" if include_payment_qr: result["payment_qr_data"] = task.payment_qr_data or "" return result