You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
10 KiB
10 KiB
用户服务重构 - 符合架构规范
日期: 2026-01-27
类型: 重构
影响范围: 用户管理模块(Models, Schemas, Repositories, Services, API)
变更概述
根据项目架构规范和用户服务文档(docs/requirements/backend/04-services/user/user-service.md v2.3),对用户管理模块进行全面重构,主要包括:
- 移除数据库外键约束,改为应用层验证
- 添加微信登录相关字段和枚举
- 完善积分管理字段
- 使用 Pydantic v2 序列化器
- 实现完整的业务逻辑和 API 接口
详细变更
1. Model 层 (app/models/user.py)
新增 WechatPlatform 枚举
class WechatPlatform(IntEnum):
"""微信平台枚举"""
MP = 1 # 公众号
OPEN = 2 # 开放平台
@classmethod
def from_string(cls, value: str) -> 'WechatPlatform':
"""从字符串转换为枚举值"""
def to_string(self) -> str:
"""转换为字符串"""
@classmethod
def get_display_name(cls, value: int) -> str:
"""获取显示名称"""
User 模型变更
移除:
foreign_key约束Relationship关系定义sessions关系字段
新增字段:
wechat_openid: 微信 openidwechat_unionid: 微信 unionidwechat_platform: 微信平台(SMALLINT,1=mp, 2=open)password_hash: 密码哈希(可选)avatar_id: 头像附件 ID(应用层验证)total_recharged_amount: 累计充值金额(Numeric(10, 2))total_credits_earned: 累计获得积分total_credits_consumed: 累计消耗积分
字段调整:
phone: 改为可选(Optional[str])phone_verified: 默认值改为 False
UserSession 模型变更
移除:
foreign_key="users.user_id"约束user关系字段
新增字段:
refresh_token: 刷新令牌
字段调整:
user_id: 添加描述"应用层验证,关联 users.user_id"
2. Schema 层 (app/schemas/user.py)
新增枚举
class WechatPlatformEnum(str, Enum):
"""API 层微信平台枚举(字符串)"""
MP = "mp"
OPEN = "open"
新增 Schema
SendSmsRequest: 发送短信验证码请求UsernameUpdate: 用户名修改请求BindPhoneRequest: 绑定手机号请求BindWechatRequest: 绑定微信请求BindEmailRequest: 绑定邮箱请求RefreshTokenRequest: 刷新 Token 请求RefreshTokenResponse: 刷新 Token 响应CreditsInfoResponse: 积分信息响应WechatQrcodeResponse: 微信二维码响应WechatLoginResultResponse: 微信登录结果响应
使用 Pydantic v2 序列化器
UserResponse 变更:
@field_serializer('user_id')
def serialize_user_id(self, value: UUID) -> str:
"""UUID 序列化为字符串"""
return str(value)
@field_serializer('wechat_platform')
def serialize_platform(self, value: Optional[int]) -> Optional[str]:
"""API 输出:整数 → 字符串"""
if value is None:
return None
from app.models.user import WechatPlatform
return WechatPlatform(value).to_string()
BindWechatRequest 变更:
@field_validator('platform', mode='before')
@classmethod
def convert_platform_to_int(cls, v):
"""API 输入:字符串 → 整数"""
from app.models.user import WechatPlatform
if isinstance(v, str):
return WechatPlatform.from_string(v).value
return v
3. Repository 层 (app/repositories/user_repository.py)
新增方法
get_by_wechat_openid(openid, platform): 通过微信 openid 查询用户get_session_by_refresh_token(refresh_token): 通过 refresh_token 查询会话update_session(session_id, data): 更新会话
方法签名调整
所有涉及 UUID 的参数和返回值类型从 str 改为 UUID:
delete_session(session_id: UUID)delete_user_sessions(user_id: UUID)update_session_last_used(session_id: UUID)
添加应用层级联删除注释
async def delete_user_sessions(self, user_id: UUID) -> int:
"""删除用户的所有会话(应用层级联删除)"""
4. Service 层 (app/services/user_service.py)
新增方法
登录相关:
refresh_token(refresh_token): 刷新访问令牌
用户信息管理:
update_username(user_id, username): 修改用户名(仅允许一次)upload_avatar(user_id, file_content, filename): 上传头像
账号绑定:
bind_phone(user_id, phone, country_code, code): 绑定手机号bind_wechat(user_id, code, platform): 绑定微信(待实现)bind_email(user_id, email): 绑定邮箱
积分查询:
get_credits_info(user_id): 查询用户积分信息
私有方法:
_create_session(...): 创建会话(应用层验证用户存在)
方法增强
login_with_phone:
- 添加
refresh_token生成 - 初始化完整的积分字段(balance, total_earned, total_consumed, total_recharged_amount)
- 调用
_create_session进行应用层验证
logout:
- 参数类型从
str改为UUID - 添加应用层级联删除注释
update_user:
- 添加邮箱唯一性检查
- 邮箱自动转小写
5. API 层
app/api/v1/auth.py 新增接口
POST /auth/refresh: 刷新 TokenGET /auth/wechat/qrcode: 获取微信登录二维码(待实现)GET /auth/wechat/result: 轮询微信登录结果(待实现)
app/api/v1/users.py 新增接口
PUT /users/me/username: 修改用户名POST /users/me/avatar: 上传头像POST /users/me/bind/phone: 绑定手机号POST /users/me/bind/wechat: 绑定微信(待实现)POST /users/me/bind/email: 绑定邮箱GET /users/me/credits: 查询积分信息
架构改进
1. 移除数据库外键约束
原因:
- 提升性能和扩展性
- 为分库分表做准备
- 避免数据库层级联删除的复杂性
实现:
- 在 Service 层的
_create_session方法中验证user_id是否存在 - 在 Repository 层添加
delete_user_sessions方法实现应用层级联删除
2. 枚举类型使用 SMALLINT
原因:
- 更好的性能(2 字节 vs 可变长度字符串)
- 更容易扩展
- 与项目其他模块保持一致
实现:
- 数据库层:
wechat_platform SMALLINT - Model 层:
WechatPlatform(IntEnum) - Schema 层:
WechatPlatformEnum(str, Enum)+field_validator+field_serializer
3. Pydantic v2 序列化器
优势:
- 更好的性能
- 更灵活的序列化控制
- 类型安全
实现:
- 使用
@field_serializer替代@validator - 使用
@field_validator替代@validator - 支持
mode='before'进行输入转换
数据库迁移
需要执行以下迁移脚本(待创建):
-- 1. 移除外键约束
ALTER TABLE user_sessions DROP CONSTRAINT IF EXISTS user_sessions_user_id_fkey;
-- 2. 添加微信相关字段
ALTER TABLE users ADD COLUMN IF NOT EXISTS wechat_openid VARCHAR(100);
ALTER TABLE users ADD COLUMN IF NOT EXISTS wechat_unionid VARCHAR(100);
ALTER TABLE users ADD COLUMN IF NOT EXISTS wechat_platform SMALLINT CHECK (wechat_platform IN (1, 2));
-- 3. 添加积分字段
ALTER TABLE users ADD COLUMN IF NOT EXISTS total_recharged_amount NUMERIC(10, 2) DEFAULT 0.00;
ALTER TABLE users ADD COLUMN IF NOT EXISTS total_credits_earned INTEGER DEFAULT 100;
ALTER TABLE users ADD COLUMN IF NOT EXISTS total_credits_consumed INTEGER DEFAULT 0;
-- 4. 添加其他字段
ALTER TABLE users ADD COLUMN IF NOT EXISTS password_hash VARCHAR(255);
ALTER TABLE users ADD COLUMN IF NOT EXISTS avatar_id UUID;
ALTER TABLE user_sessions ADD COLUMN IF NOT EXISTS refresh_token VARCHAR(500);
-- 5. 修改字段约束
ALTER TABLE users ALTER COLUMN phone DROP NOT NULL;
ALTER TABLE users ALTER COLUMN phone_verified SET DEFAULT false;
-- 6. 创建索引
CREATE UNIQUE INDEX IF NOT EXISTS idx_users_wechat_openid
ON users (wechat_openid, wechat_platform)
WHERE deleted_at IS NULL AND wechat_openid IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_users_avatar_id
ON users (avatar_id)
WHERE avatar_id IS NOT NULL;
-- 7. 添加注释
COMMENT ON TABLE users IS '用户表 - 应用层保证引用完整性';
COMMENT ON COLUMN users.avatar_id IS '头像附件ID - 应用层验证,关联 attachments.attachment_id';
COMMENT ON TABLE user_sessions IS '用户会话表 - 应用层保证引用完整性';
COMMENT ON COLUMN user_sessions.user_id IS '用户ID - 应用层验证,关联 users.user_id';
待实现功能
以下功能已定义接口但尚未实现:
-
微信登录:
GET /auth/wechat/qrcodeGET /auth/wechat/resultbind_wechat方法
-
文件上传:
upload_avatar方法需要集成存储服务
-
短信验证码:
- 当前使用固定验证码 "6666",需要集成短信服务
测试建议
1. 单元测试
- 测试
WechatPlatform枚举转换方法 - 测试 Service 层的应用层验证逻辑
- 测试 Schema 序列化器
2. 集成测试
- 测试手机号登录流程
- 测试 Token 刷新流程
- 测试账号绑定流程
- 测试积分查询
3. 性能测试
- 测试无外键约束后的查询性能
- 测试应用层级联删除的性能
兼容性说明
Breaking Changes
-
API 响应格式变更:
UserResponse中user_id从 UUID 对象改为字符串- 新增
wechat_platform字段(字符串格式) - 新增
total_recharged_amount字段
-
数据库结构变更:
- 移除外键约束
- 新增多个字段
phone字段改为可选
向后兼容
- 所有新增字段均为可选或有默认值
- 现有 API 接口保持兼容
- 数据库迁移脚本使用
IF NOT EXISTS和IF EXISTS
相关文档
总结
本次重构全面对齐了用户服务文档规范,主要改进包括:
- ✅ 移除数据库外键,改为应用层验证
- ✅ 添加微信登录支持(字段和枚举)
- ✅ 完善积分管理字段
- ✅ 使用 Pydantic v2 序列化器
- ✅ 实现完整的业务逻辑和 API 接口
- ✅ 代码通过诊断检查,无错误
下一步需要:
- 创建数据库迁移脚本
- 实现微信登录功能
- 集成存储服务(头像上传)
- 集成短信服务(验证码)
- 编写单元测试和集成测试