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.
9.2 KiB
9.2 KiB
用户服务文档完善与代码修复
变更日期:2026-01-28
变更类型:文档更新 + 代码修复
影响范围:docs/requirements/backend/04-services/user/user-service.md
变更概述
补充用户服务文档中的应用层关联关系处理说明,修复 Service 代码中的安全漏洞,提供完整的实现指导。
变更内容
1. 新增"引用完整性保证"专门章节
位置:在"服务实现"章节之后
内容:
-
架构约束说明:
- 禁止数据库物理外键约束的原因
- 应用层验证的必要性
-
关联关系表:
- 列出所有关联关系
- 明确验证策略和删除策略
-
验证策略实现:
- 创建会话时验证用户存在
- 上传头像时验证附件存在
- 登出时验证会话所有权
- 删除用户时级联删除会话
-
Repository 层完整实现:
- 提供完整的
UserRepository类定义 - 包含所有 CRUD 方法
- 包含级联删除方法
- 提供完整的
-
性能优化建议:
- 索引策略
- 批量操作优化
-
错误处理说明:
- 常见错误场景表格
- API 错误响应示例
2. 修复 Service 代码安全漏洞
2.1 修复 logout 方法
问题:没有验证会话是否属于该用户,存在安全漏洞
修复前:
async def logout(
self,
user_id: UUID,
access_token: str
) -> None:
"""用户登出(应用层级联删除会话)"""
# 删除会话
session = await self.repository.get_session_by_token(access_token)
if session:
await self.repository.delete_session(session.id)
修复后:
async def logout(
self,
user_id: UUID,
access_token: str
) -> None:
"""用户登出(应用层验证会话所有权)"""
# 查找会话并验证所有权
session = await self.repository.get_session_by_token(access_token)
if not session:
raise NotFoundError("会话不存在")
# 验证会话是否属于该用户(安全性)
if session.user_id != user_id:
raise AuthenticationError("无权删除该会话")
# 删除会话
await self.repository.delete_session(session.session_id)
修复说明:
- ✅ 验证会话是否存在
- ✅ 验证会话是否属于该用户
- ✅ 防止用户删除其他用户的会话
2.2 补充 delete_user 方法
问题:缺少用户删除方法,没有说明级联删除逻辑
新增代码:
async def delete_user(
self,
user_id: UUID
) -> None:
"""删除用户(应用层级联删除)"""
# 验证用户是否存在
user = await self.repository.get_by_id(user_id)
if not user:
raise NotFoundError("用户不存在")
# 级联删除:删除用户的所有会话
await self.repository.delete_sessions_by_user_id(user_id)
# 软删除用户
await self.repository.update(user_id, {
'deleted_at': datetime.utcnow()
})
# 注意:不删除用户的头像附件(可能被其他用户使用)
# 注意:不删除用户的积分记录(保留审计日志)
说明:
- ✅ 软删除用户(设置
deleted_at字段) - ✅ 级联删除用户的所有会话
- ✅ 不删除头像附件(可能被其他用户使用)
- ✅ 不删除积分记录(保留审计日志)
2.3 优化 upload_avatar 注释
修改前:
# 如果使用 avatar_id,验证附件是否存在(应用层引用完整性)
修改后:
# 如果使用 avatar_id 关联附件表,需要验证附件是否存在
# 应用层引用完整性验证
说明:
- 更清晰地说明验证的目的
- 强调应用层引用完整性验证
3. 补充 Repository 层完整实现
新增内容:
# app/repositories/user_repository.py
class UserRepository:
def __init__(self, db: AsyncSession):
self.db = db
# ==================== User CRUD ====================
async def create(self, user: User) -> User: ...
async def get_by_id(self, user_id: UUID) -> Optional[User]: ...
async def get_by_phone(self, phone: str, country_code: str) -> Optional[User]: ...
async def get_by_email(self, email: str) -> Optional[User]: ...
async def get_by_username(self, username: str) -> Optional[User]: ...
async def get_by_wechat_openid(self, openid: str, platform: int) -> Optional[User]: ...
async def update(self, user_id: UUID, data: dict) -> User: ...
# ==================== Session CRUD ====================
async def create_session(self, session: UserSession) -> UserSession: ...
async def get_session_by_token(self, token: str) -> Optional[UserSession]: ...
async def get_session_by_refresh_token(self, refresh_token: str) -> Optional[UserSession]: ...
async def get_sessions_by_user_id(self, user_id: UUID) -> List[UserSession]: ...
async def update_session(self, session_id: UUID, data: dict) -> UserSession: ...
async def delete_session(self, session_id: UUID) -> None: ...
async def delete_sessions_by_user_id(self, user_id: UUID) -> None: ...
说明:
- 提供完整的 Repository 层实现
- 包含所有必要的查询方法
- 包含级联删除方法
delete_sessions_by_user_id
4. 补充 API 接口文档
4.1 新增"删除用户"接口
DELETE /api/v1/users/me
响应:
{
"message": "用户已删除"
}
说明:
- 软删除用户(设置
deleted_at字段) - 级联删除用户的所有会话
- 不删除用户的头像附件(可能被其他用户使用)
- 不删除用户的积分记录(保留审计日志)
5. 修复 Model 定义
5.1 修正 UserSession.user_id 字段
问题:缺少 sa_column 定义,导致类型不明确
修复前:
user_id: UUID = Field(
description="用户ID - 应用层验证,关联 users.user_id"
)
修复后:
user_id: UUID = Field(
sa_column=Column(PG_UUID(as_uuid=True)),
description="用户ID - 应用层验证,关联 users.user_id"
)
说明:
- 明确指定 UUID 类型
- 确保数据库字段类型正确
关联关系表
| 表名 | 字段 | 关联目标 | 验证策略 | 删除策略 |
|---|---|---|---|---|
users |
avatar_id |
attachments.attachment_id |
创建/更新时验证附件存在 | 用户删除时不删除附件(可能被其他用户使用) |
user_sessions |
user_id |
users.user_id |
创建会话时验证用户存在 | 用户删除时级联删除所有会话 |
错误处理
常见错误场景
| 场景 | 错误类型 | HTTP 状态码 | 错误消息 |
|---|---|---|---|
| 创建会话时用户不存在 | NotFoundError |
404 | "用户不存在" |
| 上传头像时附件不存在 | NotFoundError |
404 | "附件不存在" |
| 登出时会话不存在 | NotFoundError |
404 | "会话不存在" |
| 登出时会话不属于该用户 | AuthenticationError |
403 | "无权删除该会话" |
| 删除用户时用户不存在 | NotFoundError |
404 | "用户不存在" |
API 错误响应示例
404 Not Found:
{
"error": {
"code": "NOT_FOUND",
"message": "用户不存在",
"details": {
"user_id": "550e8400-e29b-41d4-a716-446655440000"
}
}
}
403 Forbidden:
{
"error": {
"code": "FORBIDDEN",
"message": "无权删除该会话",
"details": {
"session_id": "660e8400-e29b-41d4-a716-446655440001",
"user_id": "550e8400-e29b-41d4-a716-446655440000"
}
}
}
验证结果
文档完整性检查
✅ 架构约束说明完整
✅ 关联关系表清晰
✅ 验证策略实现完整
✅ Repository 层实现完整
✅ 错误处理说明完整
✅ API 接口文档完整
代码安全性检查
✅ logout 方法验证会话所有权
✅ delete_user 方法实现级联删除
✅ _create_session 方法验证用户存在
✅ upload_avatar 方法注释清晰
后续工作
代码实现建议
-
实现
UserRepository类:- 参考文档中的完整实现
- 确保所有方法都已实现
-
实现
UserService类:- 补充
delete_user方法 - 修复
logout方法的安全漏洞 - 优化
upload_avatar方法
- 补充
-
添加单元测试:
- 测试会话所有权验证
- 测试级联删除逻辑
- 测试错误处理
-
添加集成测试:
- 测试完整的用户删除流程
- 测试登出流程
- 测试头像上传流程
相关文档
总结
本次变更补充了用户服务文档中缺失的应用层关联关系处理说明,修复了 Service 代码中的安全漏洞,提供了完整的 Repository 层实现和错误处理说明。文档现在提供了清晰的实现指导,确保开发人员能够正确实现应用层引用完整性验证。