guzhi/app/api/v1/app_users/app_users.py
邹方成 cc352d3184 feat: 重构后端服务并添加新功能
refactor: 优化API路由和响应模型
feat(admin): 添加App用户管理接口
feat(sms): 实现阿里云短信服务集成
feat(email): 添加SMTP邮件发送功能
feat(upload): 支持文件上传接口
feat(rate-limiter): 实现手机号限流器
fix: 修复计算步骤入库问题
docs: 更新API文档和测试计划
chore: 更新依赖和配置
2025-11-19 19:36:03 +08:00

183 lines
5.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from fastapi import APIRouter, Depends, HTTPException, status
from app.controllers.app_user import app_user_controller
from app.schemas.app_user import (
AppUserRegisterSchema,
AppUserLoginSchema,
AppUserJWTOut,
AppUserInfoOut,
AppUserUpdateSchema,
AppUserChangePasswordSchema,
AppUserDashboardOut,
AppUserQuotaOut,
)
from app.schemas.app_user import AppUserRegisterOut, TokenValidateOut
from app.schemas.base import BasicResponse, MessageOut
from app.utils.app_user_jwt import (
create_app_user_access_token,
get_current_app_user,
ACCESS_TOKEN_EXPIRE_MINUTES
)
from app.models.user import AppUser
from app.controllers.user_valuation import user_valuation_controller
from app.controllers.invoice import invoice_controller
router = APIRouter()
@router.post("/register", response_model=BasicResponse[AppUserRegisterOut], summary="用户注册")
async def register(
register_data: AppUserRegisterSchema
):
"""
用户注册 - 只需要手机号
默认密码为手机号后六位
"""
try:
user = await app_user_controller.register(register_data)
return {
"code": 200,
"msg": "注册成功",
"data": {
"user_id": user.id,
"phone": user.phone,
"default_password": register_data.phone[-6:]
}
}
except Exception as e:
raise HTTPException(status_code=200, detail=str(e))
@router.post("/login", response_model=AppUserJWTOut, summary="用户登录")
async def login(
login_data: AppUserLoginSchema
):
"""
用户登录
"""
user = await app_user_controller.authenticate(login_data)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="手机号或密码错误"
)
# 更新最后登录时间
await app_user_controller.update_last_login(user.id)
# 生成访问令牌
access_token = create_app_user_access_token(user.id, user.phone)
return AppUserJWTOut(
access_token=access_token,
token_type="bearer",
expires_in=ACCESS_TOKEN_EXPIRE_MINUTES * 60
)
@router.post("/logout", summary="用户登出", response_model=BasicResponse[MessageOut])
async def logout(current_user: AppUser = Depends(get_current_app_user)):
"""
用户登出客户端需要删除本地token
"""
return {"code": 200, "msg": "OK", "data": {"message": "登出成功"}}
@router.get("/profile", response_model=AppUserInfoOut, summary="获取用户信息")
async def get_profile(current_user: AppUser = Depends(get_current_app_user)):
"""
获取当前用户信息
"""
return current_user
@router.get("/dashboard", response_model=AppUserDashboardOut, summary="用户首页摘要")
async def get_dashboard(current_user: AppUser = Depends(get_current_app_user)):
"""
用户首页摘要
功能:
- 返回剩余估值次数(暂以 0 占位,后续可接入配额系统)
- 返回最近一条估值评估记录(若有)
- 返回待处理发票数量
"""
# 最近估值记录
latest = await user_valuation_controller.model.filter(user_id=current_user.id).order_by("-created_at").first()
latest_out = None
if latest:
latest_out = {
"id": latest.id,
"asset_name": latest.asset_name,
"valuation_result": latest.final_value_ab,
"status": latest.status,
"created_at": latest.created_at.isoformat() if latest.created_at else "",
}
# 待处理发票数量
try:
pending_invoices = await invoice_controller.count_pending_for_user(current_user.id)
except Exception:
pending_invoices = 0
# 剩余估值次数(占位,可从用户扩展字段或配额表获取)
remaining_quota = 0
return AppUserDashboardOut(remaining_quota=remaining_quota, latest_valuation=latest_out, pending_invoices=pending_invoices)
@router.get("/quota", response_model=AppUserQuotaOut, summary="剩余估值次数")
async def get_quota(current_user: AppUser = Depends(get_current_app_user)):
"""
剩余估值次数查询
说明:
- 当前实现返回默认 0 次与用户类型占位
- 若后续接入配额系统,可从数据库中读取真实值
"""
remaining_count = 0
user_type = "体验用户"
return AppUserQuotaOut(remaining_count=remaining_count, user_type=user_type)
@router.put("/profile", response_model=AppUserInfoOut, summary="更新用户信息")
async def update_profile(
update_data: AppUserUpdateSchema,
current_user: AppUser = Depends(get_current_app_user)
):
"""
更新用户信息
"""
updated_user = await app_user_controller.update_user_info(current_user.id, update_data)
if not updated_user:
raise HTTPException(status_code=404, detail="用户不存在")
return updated_user
@router.post("/change-password", summary="修改密码", response_model=BasicResponse[MessageOut])
async def change_password(
password_data: AppUserChangePasswordSchema,
current_user: AppUser = Depends(get_current_app_user)
):
"""
修改密码
"""
success = await app_user_controller.change_password(
current_user.id,
password_data.old_password,
password_data.new_password
)
if not success:
raise HTTPException(status_code=400, detail="原密码错误")
return {"code": 200, "msg": "OK", "data": {"message": "密码修改成功"}}
@router.get("/validate-token", summary="验证token", response_model=BasicResponse[TokenValidateOut])
async def validate_token(current_user: AppUser = Depends(get_current_app_user)):
"""
验证token是否有效
"""
return {
"code": 200,
"msg": "token有效",
"data": {
"user_id": current_user.id,
"phone": current_user.phone
}
}