提交
This commit is contained in:
1 parent
80138c6388
commit
693e8a678e
682 files changed
+211375
-5306
No files matched your search
@@ -11,12 +11,14 @@ 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_login_service import resolve_wechat_login_user
|
||||
from app.services.wechat_mini_auth import (
|
||||
WechatMiniAuthError,
|
||||
code2session,
|
||||
get_phone_number,
|
||||
)
|
||||
|
||||
|
||||
router = APIRouter(prefix="/mini/auth", tags=["小程序鉴权"])
|
||||
|
||||
|
||||
@@ -34,7 +36,7 @@ async def _resolve_user_id(request: Request, db: Session) -> str:
|
||||
|
||||
@router.post("/wechat-login", response_model=dict)
|
||||
async def wechat_login(body: dict, db: Session = Depends(get_db)):
|
||||
"""微信授权登录:优先 openid;未绑定则用手机号匹配员工并绑定。"""
|
||||
"""微信授权登录:须授权手机号;openid 已绑定也须与账号 phone_no 一致。"""
|
||||
login_code = (body.get("loginCode") or "").strip()
|
||||
phone_code = (body.get("phoneCode") or "").strip() or None
|
||||
|
||||
@@ -43,15 +45,6 @@ async def wechat_login(body: dict, db: Session = Depends(get_db)):
|
||||
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, "请授权手机号登录,或使用账号密码登录")
|
||||
|
||||
@@ -60,27 +53,11 @@ async def wechat_login(body: dict, db: Session = Depends(get_db)):
|
||||
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)
|
||||
user, err_msg, err_code = resolve_wechat_login_user(
|
||||
db, openid=sess["openid"], phone=phone
|
||||
)
|
||||
if err_msg:
|
||||
return error_response(err_code or 400, err_msg)
|
||||
|
||||
try:
|
||||
data = await issue_tokens_for_user(db, user)
|
||||
|
||||
+28
-37
@@ -1,8 +1,9 @@
|
||||
"""公共接口(Web 登录、刷新 Token、登出;用户模型对齐 docs/sql/zkxw.sql 基线)"""
|
||||
import os
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, UploadFile, File
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Request, UploadFile, File
|
||||
from sqlalchemy.exc import OperationalError
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
@@ -37,43 +38,11 @@ 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
|
||||
|
||||
router = APIRouter(prefix="", tags=["公共接口"])
|
||||
|
||||
|
||||
def _avatar_url_field(user: SysUser) -> Optional[str]:
|
||||
"""返回库中原始头像路径,由前端按 VITE_MINIO_BASE_URL 拼展示 URL。"""
|
||||
raw = getattr(user, "avatar", None)
|
||||
if not raw:
|
||||
return None
|
||||
s = str(raw).strip()
|
||||
return s if s else None
|
||||
|
||||
|
||||
def _build_user_info_payload(user: SysUser, web_role: str, role_names: list = None) -> dict:
|
||||
"""与 web-admin `userInfo` 字段约定一致。"""
|
||||
phone = (getattr(user, "phone_no", None) or "").strip() or None
|
||||
return {
|
||||
"id": user.id,
|
||||
"username": user.user_no,
|
||||
"realName": user.nick_name,
|
||||
"phone": phone,
|
||||
"email": (getattr(user, "email", None) or "").strip() or None,
|
||||
"hireDate": user.hire_date.strftime("%Y-%m-%d") if getattr(user, "hire_date", None) else None,
|
||||
"probationMonths": getattr(user, "probation_months", None),
|
||||
"lastWorkDay": user.last_work_day.strftime("%Y-%m-%d")
|
||||
if getattr(user, "last_work_day", None)
|
||||
else None,
|
||||
"sex": getattr(user, "sex", None) or "0",
|
||||
"remark": (getattr(user, "remark", None) or "").strip() or None,
|
||||
"role": web_role,
|
||||
"roleNames": role_names or [],
|
||||
"regionId": user.dept_id,
|
||||
"regionName": None,
|
||||
"avatarUrl": _avatar_url_field(user),
|
||||
}
|
||||
|
||||
|
||||
def _build_current_user_payload(current_user: dict) -> dict:
|
||||
"""组装 current-user 接口响应。"""
|
||||
au = current_user.get("avatar_url")
|
||||
@@ -90,7 +59,9 @@ def _build_current_user_payload(current_user: dict) -> dict:
|
||||
"roleNames": current_user.get("role_names") or current_user.get("roleNames") or [],
|
||||
"isSuperAdmin": current_user.get("role") == "super_admin",
|
||||
"regionId": current_user.get("region_id"),
|
||||
"regionName": None,
|
||||
"regionName": current_user.get("region_name") or current_user.get("dept_name"),
|
||||
"deptName": current_user.get("dept_name") or current_user.get("region_name"),
|
||||
"postNames": current_user.get("post_names") or [],
|
||||
"regionIds": [],
|
||||
"isActive": current_user.get("is_active"),
|
||||
"avatarUrl": avatar_url if avatar_url else None,
|
||||
@@ -179,7 +150,7 @@ async def web_login(
|
||||
"token": access_token,
|
||||
"refreshToken": refresh_token,
|
||||
"expiresIn": settings.ACCESS_TOKEN_EXPIRE_MINUTES * 60,
|
||||
"userInfo": _build_user_info_payload(user, web_role, role_names),
|
||||
"userInfo": build_user_info_payload(user, web_role, role_names, db=db),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -305,7 +276,7 @@ async def update_current_user_profile(
|
||||
return error_response(404, "用户不存在")
|
||||
web_role = resolve_web_role_for_user(db, user.id)
|
||||
role_names = get_user_role_names(db, user.id)
|
||||
return success_response(_build_user_info_payload(user, web_role, role_names))
|
||||
return success_response(build_user_info_payload(user, web_role, role_names, db=db))
|
||||
except HTTPException:
|
||||
return error_response(401, "Token无效或已过期")
|
||||
except ValueError as exc:
|
||||
@@ -416,3 +387,23 @@ async def get_web_auth_menu_codes(request: Request, db: Session = Depends(get_db
|
||||
)
|
||||
except HTTPException:
|
||||
return error_response(401, "Token无效或已过期")
|
||||
|
||||
|
||||
@router.get("/cn-holiday/range", response_model=dict)
|
||||
def cn_holiday_range(
|
||||
start: str = Query(..., description="YYYY-MM-DD"),
|
||||
end: str = Query(..., description="YYYY-MM-DD"),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""查询闭区间内每日节假日/休息日标志(只读本地缓存表)。"""
|
||||
try:
|
||||
start_d = datetime.strptime(start.strip(), "%Y-%m-%d").date()
|
||||
end_d = datetime.strptime(end.strip(), "%Y-%m-%d").date()
|
||||
except ValueError:
|
||||
raise HTTPException(status_code=400, detail="start/end 须为 YYYY-MM-DD")
|
||||
if end_d < start_d:
|
||||
raise HTTPException(status_code=400, detail="end 不能早于 start")
|
||||
from app.services.cn_holiday import list_day_flags
|
||||
|
||||
days = list_day_flags(db, start_d, end_d)
|
||||
return success_response({"days": days})
|
||||
Reference in new issue
Block a user