|
|
@@ -0,0 +1,130 @@
|
|
|
+"""小程序鉴权:微信登录 / 绑定 openid。"""
|
|
|
+from __future__ import annotations
|
|
|
+
|
|
|
+from datetime import datetime
|
|
|
+
|
|
|
+from fastapi import APIRouter, Depends, HTTPException, Request
|
|
|
+from sqlalchemy.orm import Session
|
|
|
+
|
|
|
+from app.core.auth import get_current_user_by_token
|
|
|
+from app.core.database import get_db
|
|
|
+from app.core.response import error_response, success_response
|
|
|
+from app.crud.user import user_crud
|
|
|
+from app.services.issue_user_tokens import issue_tokens_for_user
|
|
|
+from app.services.wechat_mini_auth import (
|
|
|
+ WechatMiniAuthError,
|
|
|
+ code2session,
|
|
|
+ get_phone_number,
|
|
|
+)
|
|
|
+
|
|
|
+router = APIRouter(prefix="/mini/auth", tags=["小程序鉴权"])
|
|
|
+
|
|
|
+
|
|
|
+async def _resolve_user_id(request: Request, db: Session) -> str:
|
|
|
+ authorization = request.headers.get("Authorization", "")
|
|
|
+ if not authorization.startswith("Bearer "):
|
|
|
+ raise HTTPException(status_code=401, detail="未授权")
|
|
|
+ token = authorization.replace("Bearer ", "")
|
|
|
+ current = await get_current_user_by_token(token=token, db=db)
|
|
|
+ user_id = current.get("user_id") or current.get("sub")
|
|
|
+ if not user_id:
|
|
|
+ raise HTTPException(status_code=401, detail="未授权")
|
|
|
+ return user_id
|
|
|
+
|
|
|
+
|
|
|
+@router.post("/wechat-login", response_model=dict)
|
|
|
+async def wechat_login(body: dict, db: Session = Depends(get_db)):
|
|
|
+ """微信授权登录:优先 openid;未绑定则用手机号匹配员工并绑定。"""
|
|
|
+ login_code = (body.get("loginCode") or "").strip()
|
|
|
+ phone_code = (body.get("phoneCode") or "").strip() or None
|
|
|
+
|
|
|
+ try:
|
|
|
+ sess = code2session(login_code)
|
|
|
+ except WechatMiniAuthError as exc:
|
|
|
+ return error_response(400, str(exc))
|
|
|
+
|
|
|
+ openid = sess["openid"]
|
|
|
+ user = user_crud.get_by_wx_openid(db, openid)
|
|
|
+ if user:
|
|
|
+ try:
|
|
|
+ data = await issue_tokens_for_user(db, user)
|
|
|
+ except ValueError as exc:
|
|
|
+ return error_response(403, str(exc))
|
|
|
+ return success_response(data)
|
|
|
+
|
|
|
+ if not phone_code:
|
|
|
+ return error_response(400, "请授权手机号登录,或使用账号密码登录")
|
|
|
+
|
|
|
+ try:
|
|
|
+ phone = get_phone_number(phone_code)
|
|
|
+ except WechatMiniAuthError as exc:
|
|
|
+ return error_response(400, str(exc))
|
|
|
+
|
|
|
+ users = user_crud.list_active_by_phone_no(db, phone)
|
|
|
+ if not users:
|
|
|
+ return error_response(
|
|
|
+ 401, "未找到与该手机号匹配的员工账号,请使用账号密码登录"
|
|
|
+ )
|
|
|
+ if len(users) > 1:
|
|
|
+ return error_response(400, "手机号对应多个账号,请使用账号密码登录")
|
|
|
+
|
|
|
+ user = users[0]
|
|
|
+ other = user_crud.get_by_wx_openid(db, openid)
|
|
|
+ if other and other.id != user.id:
|
|
|
+ return error_response(409, "该微信已绑定其他账号")
|
|
|
+ existing_oid = (getattr(user, "wx_openid", None) or "").strip()
|
|
|
+ if existing_oid and existing_oid != openid:
|
|
|
+ return error_response(409, "该账号已绑定其他微信,请联系管理员")
|
|
|
+
|
|
|
+ user.wx_openid = openid
|
|
|
+ user.update_time = datetime.now()
|
|
|
+ db.add(user)
|
|
|
+ db.commit()
|
|
|
+ db.refresh(user)
|
|
|
+
|
|
|
+ try:
|
|
|
+ data = await issue_tokens_for_user(db, user)
|
|
|
+ except ValueError as exc:
|
|
|
+ return error_response(403, str(exc))
|
|
|
+ return success_response(data)
|
|
|
+
|
|
|
+
|
|
|
+@router.post("/wechat-bind", response_model=dict)
|
|
|
+async def wechat_bind(
|
|
|
+ request: Request,
|
|
|
+ body: dict,
|
|
|
+ db: Session = Depends(get_db),
|
|
|
+):
|
|
|
+ """密码登录后绑定当前微信 openid(需 Bearer)。"""
|
|
|
+ try:
|
|
|
+ user_id = await _resolve_user_id(request, db)
|
|
|
+ except HTTPException:
|
|
|
+ return error_response(401, "未授权")
|
|
|
+
|
|
|
+ login_code = (body.get("loginCode") or "").strip()
|
|
|
+ try:
|
|
|
+ sess = code2session(login_code)
|
|
|
+ except WechatMiniAuthError as exc:
|
|
|
+ return error_response(400, str(exc))
|
|
|
+
|
|
|
+ openid = sess["openid"]
|
|
|
+ user = user_crud.get(db, user_id)
|
|
|
+ if not user or user.del_flag != 0:
|
|
|
+ return error_response(401, "用户不存在")
|
|
|
+
|
|
|
+ other = user_crud.get_by_wx_openid(db, openid)
|
|
|
+ if other and other.id != user.id:
|
|
|
+ return error_response(409, "该微信已绑定其他账号")
|
|
|
+
|
|
|
+ existing_oid = (getattr(user, "wx_openid", None) or "").strip()
|
|
|
+ if existing_oid == openid:
|
|
|
+ return success_response({"bound": True, "message": "已绑定"})
|
|
|
+
|
|
|
+ if existing_oid and existing_oid != openid:
|
|
|
+ return error_response(409, "该账号已绑定其他微信,请联系管理员")
|
|
|
+
|
|
|
+ user.wx_openid = openid
|
|
|
+ user.update_time = datetime.now()
|
|
|
+ db.add(user)
|
|
|
+ db.commit()
|
|
|
+ return success_response({"bound": True, "message": "绑定成功"})
|