Skip to content

Commit 4cc5236

Browse files
committed
feat(user): 实现用户ID和头像管理功能
1 parent 2a3e18b commit 4cc5236

File tree

12 files changed

+1396
-59
lines changed

12 files changed

+1396
-59
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ dependencies = [
5656
"Pillow>=10.5.0",
5757
"pymysql>=1.1.0",
5858
"tenacity>=8.0.0",
59+
"pypinyin>=0.55.0",
5960
]
6061
[tool.ruff]
6162
line-length = 120 # 代码最大行宽

scripts/migrate_user_fields.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
#!/usr/bin/env python3
2+
"""
3+
用户表字段迁移脚本
4+
为现有用户添加新字段:user_id, phone_number, avatar
5+
将现有的 username 作为 user_id 的默认值
6+
"""
7+
8+
import os
9+
import sys
10+
from pathlib import Path
11+
12+
# 添加项目根目录到Python路径
13+
PROJECT_ROOT = Path(__file__).parent
14+
sys.path.insert(0, str(PROJECT_ROOT))
15+
16+
from sqlalchemy import text
17+
from server.db_manager import db_manager
18+
from server.models.user_model import User
19+
20+
21+
def migrate_user_fields():
22+
"""执行用户字段迁移"""
23+
print("开始用户字段迁移...")
24+
25+
try:
26+
# 获取数据库会话
27+
db = db_manager.get_session()
28+
29+
# 1. 添加新字段(如果不存在)
30+
print("检查并添加新字段...")
31+
32+
# 检查字段是否存在的SQL
33+
check_columns_sql = """
34+
SELECT column_name FROM information_schema.columns
35+
WHERE table_name = 'users' AND table_schema = DATABASE()
36+
"""
37+
38+
try:
39+
result = db.execute(text(check_columns_sql))
40+
existing_columns = [row[0] for row in result.fetchall()]
41+
print(f"现有字段: {existing_columns}")
42+
43+
# 添加缺失的字段
44+
if 'user_id' not in existing_columns:
45+
print("添加 user_id 字段...")
46+
db.execute(text("ALTER TABLE users ADD COLUMN user_id VARCHAR(255)"))
47+
48+
if 'phone_number' not in existing_columns:
49+
print("添加 phone_number 字段...")
50+
db.execute(text("ALTER TABLE users ADD COLUMN phone_number VARCHAR(255)"))
51+
52+
if 'avatar' not in existing_columns:
53+
print("添加 avatar 字段...")
54+
db.execute(text("ALTER TABLE users ADD COLUMN avatar VARCHAR(500)"))
55+
56+
db.commit()
57+
print("字段添加完成")
58+
59+
except Exception as e:
60+
print(f"字段检查/添加失败: {e}")
61+
# 对于SQLite,尝试直接添加字段
62+
try:
63+
db.execute(text("ALTER TABLE users ADD COLUMN user_id TEXT"))
64+
db.execute(text("ALTER TABLE users ADD COLUMN phone_number TEXT"))
65+
db.execute(text("ALTER TABLE users ADD COLUMN avatar TEXT"))
66+
db.commit()
67+
print("字段添加完成(SQLite模式)")
68+
except Exception as sqlite_e:
69+
print(f"SQLite字段添加也失败: {sqlite_e}")
70+
print("字段可能已存在,继续执行...")
71+
72+
# 2. 为现有用户设置默认 user_id
73+
print("为现有用户设置默认 user_id...")
74+
75+
# 查询所有没有 user_id 的用户
76+
users_without_user_id = db.execute(
77+
text("SELECT id, username FROM users WHERE user_id IS NULL OR user_id = ''")
78+
).fetchall()
79+
80+
print(f"找到 {len(users_without_user_id)} 个需要设置 user_id 的用户")
81+
82+
for user_id, username in users_without_user_id:
83+
# 将 username 作为默认的 user_id
84+
print(f"为用户 {username} (ID: {user_id}) 设置 user_id: {username}")
85+
db.execute(
86+
text("UPDATE users SET user_id = :user_id WHERE id = :id"),
87+
{"user_id": username, "id": user_id}
88+
)
89+
90+
db.commit()
91+
92+
# 3. 添加唯一索引
93+
print("添加唯一索引...")
94+
try:
95+
# 先检查索引是否存在
96+
try:
97+
db.execute(text("CREATE UNIQUE INDEX idx_users_user_id ON users(user_id)"))
98+
print("创建 user_id 唯一索引")
99+
except:
100+
print("user_id 索引可能已存在")
101+
102+
try:
103+
db.execute(text("CREATE UNIQUE INDEX idx_users_phone_number ON users(phone_number) WHERE phone_number IS NOT NULL"))
104+
print("创建 phone_number 唯一索引")
105+
except:
106+
print("phone_number 索引可能已存在")
107+
108+
db.commit()
109+
110+
except Exception as e:
111+
print(f"索引创建失败: {e}")
112+
print("继续执行...")
113+
114+
# 4. 验证迁移结果
115+
print("验证迁移结果...")
116+
total_users = db.execute(text("SELECT COUNT(*) FROM users")).scalar()
117+
users_with_user_id = db.execute(text("SELECT COUNT(*) FROM users WHERE user_id IS NOT NULL AND user_id != ''")).scalar()
118+
119+
print(f"总用户数: {total_users}")
120+
print(f"已设置 user_id 的用户数: {users_with_user_id}")
121+
122+
if total_users == users_with_user_id:
123+
print("✅ 迁移成功完成!")
124+
else:
125+
print("❌ 迁移可能有问题,请检查数据库")
126+
127+
except Exception as e:
128+
print(f"迁移过程中发生错误: {e}")
129+
if 'db' in locals():
130+
db.rollback()
131+
raise
132+
finally:
133+
if 'db' in locals():
134+
db.close()
135+
136+
137+
if __name__ == "__main__":
138+
migrate_user_fields()

server/db_manager.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from server.models import Base
99
from server.models.user_model import User
10-
from server.utils.migrate import check_and_migrate
10+
from server.utils.migrate import check_and_migrate, validate_database_schema
1111
from src import config
1212
from src.utils import logger
1313

@@ -44,15 +44,20 @@ def create_tables(self):
4444

4545
def run_migrations(self):
4646
"""运行数据库迁移"""
47-
try:
48-
success = check_and_migrate(self.db_path)
49-
if success:
50-
logger.info("数据库迁移检查完成")
51-
else:
52-
logger.warning("数据库迁移过程中出现问题")
53-
except Exception as e:
54-
logger.error(f"数据库迁移失败: {e}")
55-
# 不抛出异常,以防止阻断应用启动
47+
# 在创建表之前先检查结构
48+
if os.path.exists(self.db_path):
49+
is_valid, issues = validate_database_schema(self.db_path)
50+
51+
if not is_valid:
52+
logger.warning("=" * 60)
53+
logger.warning("检测到数据库结构与当前模型不一致!")
54+
logger.warning("=" * 60)
55+
for issue in issues:
56+
logger.warning(f" ⚠️ {issue}")
57+
logger.warning("")
58+
logger.warning("请运行以下 scripts/migrate_user_fields.py 来修复数据库结构:")
59+
logger.warning("=" * 60)
60+
5661

5762
def get_session(self):
5863
"""获取数据库会话"""

server/models/user_model.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ class User(Base):
1111
__tablename__ = "users"
1212

1313
id = Column(Integer, primary_key=True, autoincrement=True)
14-
username = Column(String, nullable=False, unique=True, index=True)
14+
username = Column(String, nullable=False, unique=True, index=True) # 显示名称,2-20字符
15+
user_id = Column(String, nullable=False, unique=True, index=True) # 登录ID,根据用户名生成
16+
phone_number = Column(String, nullable=True, unique=True, index=True) # 手机号,可选登录方式
17+
avatar = Column(String, nullable=True) # 头像URL
1518
password_hash = Column(String, nullable=False)
1619
role = Column(String, nullable=False, default="user") # 角色: superadmin, admin, user
1720
created_at = Column(DateTime, default=func.now())
@@ -29,6 +32,9 @@ def to_dict(self, include_password=False):
2932
result = {
3033
"id": self.id,
3134
"username": self.username,
35+
"user_id": self.user_id,
36+
"phone_number": self.phone_number,
37+
"avatar": self.avatar,
3238
"role": self.role,
3339
"created_at": self.created_at.isoformat() if self.created_at else None,
3440
"last_login": self.last_login.isoformat() if self.last_login else None,

0 commit comments

Comments
 (0)