|
|
@@ -1,4 +1,5 @@
|
|
|
"""公共接口(Web 登录、刷新 Token、登出;用户模型对齐 docs/sql/zkxw.sql 基线)"""
|
|
|
+import base64
|
|
|
import os
|
|
|
from datetime import datetime
|
|
|
from typing import Optional
|
|
|
@@ -39,6 +40,8 @@ from app.services.web_auth_roles import (
|
|
|
from app.schemas.auth import ProfileUpdateRequest
|
|
|
from app.crud.user import update_user, change_password
|
|
|
from app.services.issue_user_tokens import build_user_info_payload
|
|
|
+from app.services import wx_qr_session
|
|
|
+from app.services.wechat_mini_auth import get_unlimited_wxacode, WechatMiniAuthError
|
|
|
|
|
|
router = APIRouter(prefix="", tags=["公共接口"])
|
|
|
|
|
|
@@ -155,6 +158,49 @@ async def web_login(
|
|
|
)
|
|
|
|
|
|
|
|
|
+@router.post("/web/auth/wx-qr/create", response_model=dict)
|
|
|
+async def wx_qr_create():
|
|
|
+ session_id, short = wx_qr_session.create_session()
|
|
|
+ page = (settings.WX_QR_PAGE or "pages/login/web-qr/index").strip()
|
|
|
+ try:
|
|
|
+ raw = get_unlimited_wxacode(
|
|
|
+ scene=short,
|
|
|
+ page=page,
|
|
|
+ env_version=settings.WX_QR_ENV_VERSION,
|
|
|
+ )
|
|
|
+ except WechatMiniAuthError as exc:
|
|
|
+ wx_qr_session.invalidate(session_id)
|
|
|
+ return error_response(400, str(exc))
|
|
|
+ b64 = base64.b64encode(raw).decode("ascii")
|
|
|
+ mime = "image/jpeg" if raw[:2] == b"\xff\xd8" else "image/png"
|
|
|
+ return success_response(
|
|
|
+ {
|
|
|
+ "sessionId": session_id,
|
|
|
+ "expiresIn": settings.WX_QR_SESSION_TTL_SEC,
|
|
|
+ "qrImageBase64": f"data:{mime};base64,{b64}",
|
|
|
+ }
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+@router.get("/web/auth/wx-qr/status", response_model=dict)
|
|
|
+async def wx_qr_status(sessionId: str = ""):
|
|
|
+ sid = (sessionId or "").strip()
|
|
|
+ data = wx_qr_session.get_session(sid)
|
|
|
+ if not data:
|
|
|
+ return success_response({"status": "expired", "message": "二维码已过期,请刷新"})
|
|
|
+ status = data.get("status") or "expired"
|
|
|
+ if status == "success":
|
|
|
+ tokens = wx_qr_session.consume_success(sid)
|
|
|
+ if not tokens:
|
|
|
+ return success_response({"status": "expired", "message": "登录凭证已失效,请刷新二维码"})
|
|
|
+ return success_response({"status": "success", **tokens})
|
|
|
+ if status == "failed":
|
|
|
+ return success_response({"status": "failed", "message": data.get("message") or "登录失败"})
|
|
|
+ if status in ("expired", "consumed"):
|
|
|
+ return success_response({"status": "expired", "message": "二维码已过期,请刷新"})
|
|
|
+ return success_response({"status": status})
|
|
|
+
|
|
|
+
|
|
|
@router.post("/web/auth/refresh-token", response_model=dict)
|
|
|
async def refresh_token(body: dict, db: Session = Depends(get_db)):
|
|
|
"""刷新 Token"""
|