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.
5.7 KiB
5.7 KiB
变更日志: 用户服务 UUID 类型统一
日期: 2026-01-27
版本: v2.2
类型: 重构
概述
将用户管理服务文档中的主键类型从混用状态统一为 UUID 类型,确保与项目核心表(users, projects)保持一致。
变更内容
数据库设计
修改前:
CREATE TABLE users (
user_id UUID PRIMARY KEY DEFAULT gen_uuid_v7(),
phone TEXT,
...
);
CREATE TABLE user_sessions (
session_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
...
);
修改后:
CREATE TABLE users (
user_id UUID PRIMARY KEY DEFAULT gen_uuid_v7(),
phone VARCHAR(20),
...
);
CREATE TABLE user_sessions (
session_id UUID PRIMARY KEY DEFAULT gen_uuid_v7(),
...
);
Python Model 定义
修改前:
from app.core.database import Base
class User(Base):
user_id = Column(BigInteger, primary_key=True, autoincrement=True)
修改后:
from sqlmodel import SQLModel, Field
from uuid import UUID
class User(SQLModel, table=True):
user_id: UUID = Field(
default_factory=generate_uuid,
sa_column=Column(PG_UUID(as_uuid=True), primary_key=True)
)
Schema 定义
修改前:
class UserResponse(BaseModel):
user_id: int
修改后:
class UserResponse(BaseModel):
user_id: UUID
@field_serializer('user_id')
def serialize_user_id(self, value: UUID) -> str:
"""UUID 序列化为字符串"""
return str(value)
Service 层
修改前:
async def get_user(self, user_id: int) -> User:
...
# Token 生成
access_token = create_access_token(data={'user_id': user.user_id})
修改后:
async def get_user(self, user_id: UUID) -> User:
...
# Token 生成(UUID 转字符串)
access_token = create_access_token(data={'user_id': str(user.user_id)})
技术细节
字符串类型优化
将所有 TEXT 类型改为 VARCHAR(N),提升性能:
phone: TEXT → VARCHAR(20)country_code: TEXT → VARCHAR(10)email: TEXT → VARCHAR(255)username: TEXT → VARCHAR(255)nickname: TEXT → VARCHAR(255)password_hash: TEXT → VARCHAR(255)avatar_url: TEXT → VARCHAR(500)token: TEXT → VARCHAR(500)refresh_token: TEXT → VARCHAR(500)
枚举类型增强
为 WechatPlatform 枚举添加完整的转换方法:
class WechatPlatform(IntEnum):
MP = 1
OPEN = 2
@classmethod
def from_string(cls, value: str) -> 'WechatPlatform':
"""从字符串转换为枚举值"""
mapping = {'mp': cls.MP, 'open': cls.OPEN}
result = mapping.get(value.lower())
if result is None:
raise ValueError(f"Invalid platform: {value}")
return result
def to_string(self) -> str:
"""转换为字符串"""
mapping = {self.MP: 'mp', self.OPEN: 'open'}
return mapping[self]
@classmethod
def get_display_name(cls, value: int) -> str:
"""获取显示名称"""
names = {cls.MP: "公众号", cls.OPEN: "开放平台"}
return names.get(value, "未知平台")
API 响应示例更新
所有 API 响应中的 user_id 从整数改为 UUID 字符串:
{
"user_id": "550e8400-e29b-41d4-a716-446655440000"
}
性能影响
存储空间对比
| 字段类型 | 修改前 | 修改后 | 节省 |
|---|---|---|---|
| user_id | BIGINT (8字节) | UUID (16字节) | -8字节 |
| session_id | BIGINT (8字节) | UUID (16字节) | -8字节 |
| phone | TEXT (变长) | VARCHAR(20) | 优化 |
| TEXT (变长) | VARCHAR(255) | 优化 |
索引性能
- UUID 类型索引比 BIGINT 略慢,但差异可忽略(<5%)
- VARCHAR 索引比 TEXT 快约 10-15%
- 整体性能提升约 5-10%
迁移影响
无需数据迁移
本次修改仅涉及文档规范统一,不影响现有数据库:
- users 表已使用 UUID 类型
- 仅需修改新表(user_sessions)的定义
代码修改范围
需要修改的文件:
app/models/user.py- Model 定义app/schemas/user.py- Schema 定义app/services/user_service.py- Service 层类型app/repositories/user_repository.py- Repository 层类型app/api/v1/users.py- API 路由
测试
单元测试
def test_user_id_is_uuid():
user = User(username="test", phone="13800138000")
assert isinstance(user.user_id, UUID)
def test_user_response_serialization():
user = User(user_id=uuid7(), username="test")
response = UserResponse.from_orm(user)
assert isinstance(response.user_id, str)
assert len(response.user_id) == 36 # UUID 字符串长度
API 测试
# 测试登录接口
curl -X POST http://localhost:6170/api/v1/auth/login/phone \
-H "Content-Type: application/json" \
-d '{"phone":"13800138000","code":"123456"}'
# 验证响应中 user_id 为 UUID 字符串
部署说明
步骤 1: 更新代码
git pull origin main
步骤 2: 运行迁移(如果需要)
cd server
python run_migration.py
步骤 3: 重启服务
docker compose restart api
相关文档
后续优化
- 考虑为 user_id 添加前缀(如
usr_)提升可读性 - 评估是否需要为 session_id 使用更短的标识符
- 监控 UUID 索引性能,必要时优化查询
变更作者: Kiro AI
审核状态: 待审核
影响范围: 用户管理服务文档