2025-12-2genxin
This commit is contained in:
BIN
database/__pycache__/user_database.cpython-312.pyc
Normal file
BIN
database/__pycache__/user_database.cpython-312.pyc
Normal file
Binary file not shown.
94
database/test_user_db.py
Normal file
94
database/test_user_db.py
Normal file
@@ -0,0 +1,94 @@
|
||||
import os
|
||||
import sys
|
||||
from user_database import UserDatabase
|
||||
|
||||
def test_user_database():
|
||||
# 获取数据库路径
|
||||
db_path = os.path.join(os.path.dirname(__file__), 'users.db')
|
||||
print(f"测试数据库路径: {db_path}")
|
||||
|
||||
try:
|
||||
# 初始化数据库
|
||||
db = UserDatabase(db_path)
|
||||
print("✅ 数据库初始化成功")
|
||||
|
||||
# 测试用户创建
|
||||
test_username = "testuser"
|
||||
test_email = "test@example.com"
|
||||
test_password = "Test123456"
|
||||
|
||||
# 先尝试删除测试用户(如果存在)
|
||||
try:
|
||||
user = db.get_user_by_username(test_username)
|
||||
if user:
|
||||
print(f"⚠️ 测试用户 '{test_username}' 已存在,尝试创建新的测试用户")
|
||||
test_username = "testuser_new"
|
||||
test_email = "test_new@example.com"
|
||||
except Exception as e:
|
||||
print(f"ℹ️ 检查用户时发生错误: {e}")
|
||||
|
||||
# 创建新用户
|
||||
print(f"\n🔄 创建测试用户: {test_username}")
|
||||
user_id = db.create_user(test_username, test_password, test_email)
|
||||
print(f"✅ 用户创建成功! 用户ID: {user_id}")
|
||||
|
||||
# 测试获取用户信息
|
||||
print(f"\n🔄 测试获取用户信息")
|
||||
user = db.get_user_by_username(test_username)
|
||||
if user:
|
||||
print(f"✅ 用户信息获取成功!")
|
||||
print(f" - 用户ID: {user['user_id']}")
|
||||
print(f" - 用户名: {user['username']}")
|
||||
print(f" - 邮箱: {user['email']}")
|
||||
print(f" - 创建时间: {user['created_at']}")
|
||||
else:
|
||||
print(f"❌ 无法获取用户信息")
|
||||
|
||||
# 测试密码验证
|
||||
print(f"\n🔄 测试密码验证")
|
||||
# 正确密码
|
||||
valid_user = db.verify_password(test_username, test_password)
|
||||
if valid_user:
|
||||
print(f"✅ 正确密码验证成功")
|
||||
else:
|
||||
print(f"❌ 正确密码验证失败")
|
||||
|
||||
# 错误密码
|
||||
invalid_user = db.verify_password(test_username, "WrongPassword")
|
||||
if not invalid_user:
|
||||
print(f"✅ 错误密码验证正确(返回None)")
|
||||
else:
|
||||
print(f"❌ 错误密码验证失败(应返回None)")
|
||||
|
||||
# 测试更新登录时间
|
||||
print(f"\n🔄 测试更新登录时间")
|
||||
if db.update_login_time(user_id):
|
||||
print(f"✅ 登录时间更新成功")
|
||||
# 验证更新是否成功
|
||||
updated_user = db.get_user_by_id(user_id)
|
||||
print(f" - 更新后的最后登录时间: {updated_user['last_login_at']}")
|
||||
else:
|
||||
print(f"❌ 登录时间更新失败")
|
||||
|
||||
print("\n🎉 所有测试完成!")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 测试失败: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
finally:
|
||||
if 'db' in locals():
|
||||
try:
|
||||
db.close()
|
||||
print("ℹ️ 数据库连接已关闭")
|
||||
except:
|
||||
pass
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("====================================")
|
||||
print("用户数据库功能测试")
|
||||
print("====================================")
|
||||
success = test_user_database()
|
||||
sys.exit(0 if success else 1)
|
||||
145
database/user_cli.py
Normal file
145
database/user_cli.py
Normal file
@@ -0,0 +1,145 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
用户数据库命令行接口
|
||||
用于Go后端调用执行用户操作
|
||||
"""
|
||||
|
||||
import sys
|
||||
import json
|
||||
from user_database import UserDatabase
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print(json.dumps({"error": "缺少命令参数"}))
|
||||
sys.exit(1)
|
||||
|
||||
command = sys.argv[1]
|
||||
db = UserDatabase()
|
||||
|
||||
try:
|
||||
if command == "create":
|
||||
# 创建用户: python user_cli.py create <username> <password> <email>
|
||||
if len(sys.argv) < 5:
|
||||
print(json.dumps({"error": "参数不足: 需要用户名、密码和邮箱"}))
|
||||
sys.exit(1)
|
||||
|
||||
username = sys.argv[2]
|
||||
password = sys.argv[3]
|
||||
email = sys.argv[4]
|
||||
|
||||
user_id = db.create_user(username, password, email)
|
||||
print(json.dumps({
|
||||
"success": True,
|
||||
"user_id": user_id,
|
||||
"username": username
|
||||
}))
|
||||
|
||||
elif command == "verify":
|
||||
# 验证密码: python user_cli.py verify <username> <password>
|
||||
if len(sys.argv) < 4:
|
||||
print(json.dumps({"error": "参数不足: 需要用户名和密码"}))
|
||||
sys.exit(1)
|
||||
|
||||
username = sys.argv[2]
|
||||
password = sys.argv[3]
|
||||
|
||||
user = db.verify_password(username, password)
|
||||
if user:
|
||||
# 删除密码哈希
|
||||
user_data = {k: v for k, v in user.items() if k != 'password_hash'}
|
||||
# 转换datetime为字符串
|
||||
if 'created_at' in user_data and user_data['created_at']:
|
||||
user_data['created_at'] = str(user_data['created_at'])
|
||||
if 'last_login_at' in user_data and user_data['last_login_at']:
|
||||
user_data['last_login_at'] = str(user_data['last_login_at'])
|
||||
|
||||
# 更新登录时间
|
||||
db.update_login_time(user['user_id'])
|
||||
|
||||
print(json.dumps({
|
||||
"success": True,
|
||||
**user_data
|
||||
}))
|
||||
else:
|
||||
print(json.dumps({"error": "用户名或密码错误"}))
|
||||
sys.exit(1)
|
||||
|
||||
elif command == "get":
|
||||
# 获取用户信息: python user_cli.py get <user_id>
|
||||
if len(sys.argv) < 3:
|
||||
print(json.dumps({"error": "参数不足: 需要用户ID"}))
|
||||
sys.exit(1)
|
||||
|
||||
user_id = int(sys.argv[2])
|
||||
user = db.get_user_by_id(user_id)
|
||||
|
||||
if user:
|
||||
# 删除密码哈希
|
||||
user_data = {k: v for k, v in user.items() if k != 'password_hash'}
|
||||
# 转换datetime为字符串
|
||||
if 'created_at' in user_data and user_data['created_at']:
|
||||
user_data['created_at'] = str(user_data['created_at'])
|
||||
if 'last_login_at' in user_data and user_data['last_login_at']:
|
||||
user_data['last_login_at'] = str(user_data['last_login_at'])
|
||||
|
||||
print(json.dumps(user_data))
|
||||
else:
|
||||
print(json.dumps({"error": "用户不存在"}))
|
||||
sys.exit(1)
|
||||
|
||||
elif command == "update":
|
||||
# 更新用户信息: python user_cli.py update <user_id> [--email <email>] [--bio <bio>]
|
||||
if len(sys.argv) < 3:
|
||||
print(json.dumps({"error": "参数不足: 需要用户ID"}))
|
||||
sys.exit(1)
|
||||
|
||||
user_id = int(sys.argv[2])
|
||||
kwargs = {}
|
||||
|
||||
# 解析命令行参数
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
if sys.argv[i] == "--email" and i + 1 < len(sys.argv):
|
||||
kwargs['email'] = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif sys.argv[i] == "--bio" and i + 1 < len(sys.argv):
|
||||
kwargs['bio'] = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif sys.argv[i] == "--password" and i + 1 < len(sys.argv):
|
||||
kwargs['password'] = sys.argv[i + 1]
|
||||
i += 2
|
||||
else:
|
||||
i += 1
|
||||
|
||||
if not kwargs:
|
||||
print(json.dumps({"error": "没有提供需要更新的字段"}))
|
||||
sys.exit(1)
|
||||
|
||||
success = db.update_user(user_id, **kwargs)
|
||||
|
||||
if success:
|
||||
print(json.dumps({"success": True, "message": "更新成功"}))
|
||||
else:
|
||||
print(json.dumps({"error": "更新失败"}))
|
||||
sys.exit(1)
|
||||
|
||||
else:
|
||||
print(json.dumps({"error": f"未知命令: {command}"}))
|
||||
sys.exit(1)
|
||||
|
||||
except db.UserExistsError as e:
|
||||
print(json.dumps({"error": str(e), "type": "UserExistsError"}))
|
||||
sys.exit(1)
|
||||
except db.ValidationError as e:
|
||||
print(json.dumps({"error": str(e), "type": "ValidationError"}))
|
||||
sys.exit(1)
|
||||
except db.UserNotFoundError as e:
|
||||
print(json.dumps({"error": str(e), "type": "UserNotFoundError"}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e), "type": "Exception"}))
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
556
database/user_database.py
Normal file
556
database/user_database.py
Normal file
@@ -0,0 +1,556 @@
|
||||
import sqlite3
|
||||
import datetime
|
||||
import os
|
||||
import bcrypt
|
||||
import re
|
||||
import logging
|
||||
|
||||
# 配置日志
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
filename=os.path.join(os.path.dirname(__file__), 'user_db.log')
|
||||
)
|
||||
logger = logging.getLogger('UserDatabase')
|
||||
|
||||
class UserDatabase:
|
||||
def __init__(self, db_path='users.db'):
|
||||
"""
|
||||
初始化数据库配置
|
||||
:param db_path: 数据库文件路径
|
||||
"""
|
||||
self.db_path = os.path.join(os.path.dirname(__file__), db_path)
|
||||
# 初始化时只创建表,不保持连接
|
||||
self._setup_adapters() # 设置全局适配器
|
||||
self._create_tables_at_init() # 初始化时创建表
|
||||
|
||||
def _setup_adapters(self):
|
||||
"""设置SQLite适配器和转换器(全局设置)"""
|
||||
# 添加自定义datetime适配器以解决Python 3.12弃用警告
|
||||
def adapt_datetime(dt):
|
||||
return dt.isoformat()
|
||||
|
||||
# 注册适配器
|
||||
sqlite3.register_adapter(datetime.datetime, adapt_datetime)
|
||||
|
||||
# 添加转换器(从字符串转换回datetime)
|
||||
def convert_datetime(val):
|
||||
try:
|
||||
return datetime.datetime.fromisoformat(val.decode())
|
||||
except (ValueError, AttributeError):
|
||||
return val
|
||||
|
||||
# 注册转换器
|
||||
sqlite3.register_converter('TIMESTAMP', convert_datetime)
|
||||
|
||||
def _get_connection(self):
|
||||
"""
|
||||
获取新的数据库连接(线程安全)
|
||||
:return: 数据库连接对象
|
||||
"""
|
||||
try:
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
conn.row_factory = sqlite3.Row # 允许通过列名访问结果
|
||||
return conn
|
||||
except sqlite3.Error as e:
|
||||
print(f"数据库连接错误: {e}")
|
||||
raise
|
||||
|
||||
def _create_tables_at_init(self):
|
||||
"""
|
||||
初始化时创建用户表
|
||||
"""
|
||||
conn = self._get_connection()
|
||||
try:
|
||||
cursor = conn.cursor()
|
||||
# 创建用户表,添加适当的主键和索引
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
user_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
email TEXT NOT NULL UNIQUE,
|
||||
password_hash TEXT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
last_login_at TIMESTAMP,
|
||||
status INTEGER DEFAULT 1,
|
||||
profile_image TEXT,
|
||||
bio TEXT,
|
||||
UNIQUE(username),
|
||||
UNIQUE(email)
|
||||
)
|
||||
''')
|
||||
|
||||
# 创建索引以提高查询效率
|
||||
cursor.execute('CREATE INDEX IF NOT EXISTS idx_username ON users(username)')
|
||||
cursor.execute('CREATE INDEX IF NOT EXISTS idx_email ON users(email)')
|
||||
cursor.execute('CREATE INDEX IF NOT EXISTS idx_status ON users(status)')
|
||||
|
||||
conn.commit()
|
||||
except sqlite3.Error as e:
|
||||
conn.rollback()
|
||||
print(f"创建表错误: {e}")
|
||||
raise
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
def _hash_password(self, password):
|
||||
"""
|
||||
对密码进行哈希加密
|
||||
:param password: 原始密码
|
||||
:return: 加密后的密码哈希
|
||||
"""
|
||||
# 使用bcrypt进行哈希,更安全的密码存储方式
|
||||
salt = bcrypt.gensalt()
|
||||
return bcrypt.hashpw(password.encode(), salt).decode()
|
||||
|
||||
# 定义自定义异常类
|
||||
class DatabaseError(Exception):
|
||||
"""数据库操作异常基类"""
|
||||
pass
|
||||
|
||||
class UserExistsError(DatabaseError):
|
||||
"""用户已存在异常"""
|
||||
pass
|
||||
|
||||
class UserNotFoundError(DatabaseError):
|
||||
"""用户不存在异常"""
|
||||
pass
|
||||
|
||||
class ValidationError(DatabaseError):
|
||||
"""数据验证异常"""
|
||||
pass
|
||||
|
||||
def _validate_input(self, username, email, password=None):
|
||||
"""
|
||||
验证输入数据的有效性
|
||||
:param username: 用户名
|
||||
:param email: 邮箱
|
||||
:param password: 密码(可选)
|
||||
:return: 验证通过返回True,否则抛出异常
|
||||
"""
|
||||
# 验证用户名
|
||||
if not username or not isinstance(username, str):
|
||||
raise self.ValidationError("用户名不能为空")
|
||||
if len(username) < 3:
|
||||
raise self.ValidationError("用户名必须至少包含3个字符")
|
||||
if len(username) > 50:
|
||||
raise self.ValidationError("用户名不能超过50个字符")
|
||||
if not re.match(r'^[a-zA-Z0-9_]+$', username):
|
||||
raise self.ValidationError("用户名只能包含字母、数字和下划线")
|
||||
|
||||
# 验证邮箱 - 使用更严格的正则表达式
|
||||
if not email or not isinstance(email, str):
|
||||
raise self.ValidationError("邮箱不能为空")
|
||||
email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
|
||||
if not re.match(email_pattern, email):
|
||||
raise self.ValidationError("邮箱格式不正确")
|
||||
if len(email) > 255:
|
||||
raise self.ValidationError("邮箱不能超过255个字符")
|
||||
|
||||
# 验证密码
|
||||
if password is not None:
|
||||
if not password:
|
||||
raise self.ValidationError("密码不能为空")
|
||||
if len(password) < 6:
|
||||
raise self.ValidationError("密码必须至少包含6个字符")
|
||||
if len(password) > 128:
|
||||
raise self.ValidationError("密码不能超过128个字符")
|
||||
|
||||
return True
|
||||
|
||||
def create_user(self, username, password, email, nickname=''):
|
||||
"""
|
||||
创建新用户
|
||||
:param username: 用户名
|
||||
:param password: 密码
|
||||
:param email: 邮箱
|
||||
:param nickname: 昵称(可选)
|
||||
:return: 创建成功返回用户ID,否则抛出异常
|
||||
"""
|
||||
conn = self._get_connection()
|
||||
try:
|
||||
cursor = conn.cursor()
|
||||
# 验证输入
|
||||
self._validate_input(username, email, password)
|
||||
|
||||
# 检查用户名是否已存在
|
||||
cursor.execute("SELECT user_id FROM users WHERE username = ?", (username,))
|
||||
if cursor.fetchone():
|
||||
raise self.UserExistsError("用户名已存在")
|
||||
|
||||
# 检查邮箱是否已存在
|
||||
cursor.execute("SELECT user_id FROM users WHERE email = ?", (email,))
|
||||
if cursor.fetchone():
|
||||
raise self.UserExistsError("邮箱已被注册")
|
||||
|
||||
# 哈希密码
|
||||
password_hash = self._hash_password(password)
|
||||
|
||||
# 插入用户记录
|
||||
cursor.execute('''
|
||||
INSERT INTO users (username, email, password_hash, created_at)
|
||||
VALUES (?, ?, ?, ?)
|
||||
''', (username, email, password_hash, datetime.datetime.now()))
|
||||
|
||||
conn.commit()
|
||||
user_id = cursor.lastrowid
|
||||
logger.info(f"创建用户成功,ID: {user_id}, 用户名: {username}")
|
||||
return user_id
|
||||
except self.DatabaseError:
|
||||
raise
|
||||
except sqlite3.Error as e:
|
||||
conn.rollback()
|
||||
error_msg = f"创建用户失败: {str(e)}"
|
||||
logger.error(f"创建用户错误: {e}, 用户名: {username}, 邮箱: {email}")
|
||||
raise self.DatabaseError(error_msg)
|
||||
|
||||
def get_user_by_id(self, user_id):
|
||||
"""
|
||||
根据用户ID获取用户信息
|
||||
:param user_id: 用户ID
|
||||
:return: 用户信息字典,如果不存在返回None
|
||||
"""
|
||||
conn = self._get_connection()
|
||||
try:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT * FROM users WHERE user_id = ?", (user_id,))
|
||||
row = cursor.fetchone()
|
||||
if row:
|
||||
return dict(row)
|
||||
return None
|
||||
except sqlite3.Error as e:
|
||||
print(f"查询用户错误: {e}")
|
||||
return None
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
def get_user_by_username(self, username):
|
||||
"""
|
||||
根据用户名获取用户信息
|
||||
:param username: 用户名
|
||||
:return: 用户信息字典,如果不存在返回None
|
||||
"""
|
||||
conn = self._get_connection()
|
||||
try:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT * FROM users WHERE username = ?", (username,))
|
||||
row = cursor.fetchone()
|
||||
if row:
|
||||
return dict(row)
|
||||
return None
|
||||
except sqlite3.Error as e:
|
||||
print(f"查询用户错误: {e}")
|
||||
return None
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
def get_user_by_email(self, email):
|
||||
"""
|
||||
根据邮箱获取用户信息
|
||||
:param email: 邮箱
|
||||
:return: 用户信息字典,如果不存在返回None
|
||||
"""
|
||||
conn = self._get_connection()
|
||||
try:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT * FROM users WHERE email = ?", (email,))
|
||||
row = cursor.fetchone()
|
||||
if row:
|
||||
return dict(row)
|
||||
return None
|
||||
except sqlite3.Error as e:
|
||||
print(f"查询用户错误: {e}")
|
||||
return None
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
def verify_password(self, username, password):
|
||||
"""
|
||||
验证用户密码
|
||||
:param username: 用户名
|
||||
:param password: 密码
|
||||
:return: 验证成功返回用户信息字典,失败返回None
|
||||
"""
|
||||
try:
|
||||
# 获取用户信息(get_user_by_username已经使用独立连接)
|
||||
user = self.get_user_by_username(username)
|
||||
if not user:
|
||||
logger.warning(f"密码验证失败: 用户不存在 - {username}")
|
||||
return None
|
||||
|
||||
# 检查账号状态
|
||||
if user['status'] != 1:
|
||||
logger.warning(f"密码验证失败: 账号状态异常 - 用户名: {username}, 状态: {user['status']}")
|
||||
return None
|
||||
|
||||
# 验证密码 - 使用bcrypt的checkpw方法
|
||||
is_valid = bcrypt.checkpw(password.encode('utf-8'), user['password_hash'].encode('utf-8'))
|
||||
|
||||
if is_valid:
|
||||
logger.info(f"密码验证成功: {username}")
|
||||
return user # 验证成功返回用户信息
|
||||
else:
|
||||
logger.warning(f"密码验证失败: 密码错误 - {username}")
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"密码验证错误: {e}, 用户名: {username}")
|
||||
return None
|
||||
|
||||
def update_login_time(self, user_id):
|
||||
"""
|
||||
更新用户最后登录时间
|
||||
:param user_id: 用户ID
|
||||
:return: 更新成功返回True,否则返回False
|
||||
"""
|
||||
conn = self._get_connection()
|
||||
try:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(
|
||||
"UPDATE users SET last_login_at = ? WHERE user_id = ?",
|
||||
(datetime.datetime.now(), user_id)
|
||||
)
|
||||
conn.commit()
|
||||
return cursor.rowcount > 0
|
||||
except sqlite3.Error as e:
|
||||
conn.rollback()
|
||||
print(f"更新登录时间错误: {e}")
|
||||
return False
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
def update_user(self, user_id, **kwargs):
|
||||
"""
|
||||
更新用户信息
|
||||
:param user_id: 用户ID
|
||||
:param kwargs: 要更新的字段
|
||||
:return: 更新成功返回True,否则返回False
|
||||
"""
|
||||
conn = self._get_connection()
|
||||
try:
|
||||
cursor = conn.cursor()
|
||||
# 检查用户是否存在
|
||||
if not self.get_user_by_id(user_id):
|
||||
raise self.UserNotFoundError("用户不存在")
|
||||
|
||||
# 准备更新字段
|
||||
update_fields = []
|
||||
update_values = []
|
||||
|
||||
# 记录要更新的字段用于日志
|
||||
updated_fields_log = []
|
||||
|
||||
if 'username' in kwargs:
|
||||
update_fields.append("username = ?")
|
||||
update_values.append(kwargs['username'])
|
||||
updated_fields_log.append('username')
|
||||
# 验证用户名
|
||||
self._validate_input(kwargs['username'], "dummy@example.com")
|
||||
# 检查用户名是否被其他用户使用
|
||||
cursor.execute(
|
||||
"SELECT user_id FROM users WHERE username = ? AND user_id != ?",
|
||||
(kwargs['username'], user_id)
|
||||
)
|
||||
if cursor.fetchone():
|
||||
raise self.UserExistsError("用户名已存在")
|
||||
|
||||
if 'email' in kwargs:
|
||||
update_fields.append("email = ?")
|
||||
update_values.append(kwargs['email'])
|
||||
updated_fields_log.append('email')
|
||||
# 验证邮箱
|
||||
self._validate_input("dummy", kwargs['email'])
|
||||
# 检查邮箱是否被其他用户使用
|
||||
cursor.execute(
|
||||
"SELECT user_id FROM users WHERE email = ? AND user_id != ?",
|
||||
(kwargs['email'], user_id)
|
||||
)
|
||||
if cursor.fetchone():
|
||||
raise self.UserExistsError("邮箱已被注册")
|
||||
|
||||
if 'password' in kwargs:
|
||||
# 验证密码
|
||||
self._validate_input("dummy", "dummy@example.com", kwargs['password'])
|
||||
# 哈希密码
|
||||
password_hash = self._hash_password(kwargs['password'])
|
||||
update_fields.append("password_hash = ?")
|
||||
update_values.append(password_hash)
|
||||
updated_fields_log.append('password')
|
||||
|
||||
if 'status' in kwargs:
|
||||
# 验证状态值
|
||||
if not isinstance(kwargs['status'], int) or kwargs['status'] not in (0, 1, 2):
|
||||
raise self.ValidationError("无效的用户状态值")
|
||||
update_fields.append("status = ?")
|
||||
update_values.append(kwargs['status'])
|
||||
updated_fields_log.append('status')
|
||||
|
||||
if 'profile_image' in kwargs:
|
||||
# 验证头像路径
|
||||
if kwargs['profile_image'] and len(kwargs['profile_image']) > 255:
|
||||
raise self.ValidationError("头像路径过长")
|
||||
update_fields.append("profile_image = ?")
|
||||
update_values.append(kwargs['profile_image'])
|
||||
updated_fields_log.append('profile_image')
|
||||
|
||||
if 'bio' in kwargs:
|
||||
# 验证个人简介
|
||||
if kwargs['bio'] and len(kwargs['bio']) > 500:
|
||||
raise self.ValidationError("个人简介不能超过500个字符")
|
||||
update_fields.append("bio = ?")
|
||||
update_values.append(kwargs['bio'])
|
||||
updated_fields_log.append('bio')
|
||||
|
||||
# 执行更新
|
||||
if update_fields:
|
||||
update_values.append(user_id)
|
||||
update_sql = f"UPDATE users SET {', '.join(update_fields)} WHERE user_id = ?"
|
||||
cursor.execute(update_sql, update_values)
|
||||
conn.commit()
|
||||
logger.info(f"更新用户信息成功,ID: {user_id}, 更新字段: {', '.join(updated_fields_log)}")
|
||||
return True
|
||||
else:
|
||||
# 没有要更新的字段
|
||||
logger.warning(f"没有更新字段提供,用户ID: {user_id}")
|
||||
return True
|
||||
except self.DatabaseError:
|
||||
raise
|
||||
except sqlite3.Error as e:
|
||||
conn.rollback()
|
||||
logger.error(f"更新用户信息错误: {e}, 用户ID: {user_id}")
|
||||
raise self.DatabaseError(f"更新用户信息失败: {str(e)}")
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
|
||||
def delete_user(self, user_id):
|
||||
"""
|
||||
删除用户
|
||||
:param user_id: 用户ID
|
||||
:return: 删除成功返回True,否则返回False
|
||||
"""
|
||||
try:
|
||||
# 检查用户是否存在
|
||||
user = self.get_user_by_id(user_id)
|
||||
if not user:
|
||||
raise self.UserNotFoundError("用户不存在")
|
||||
|
||||
# 开始事务
|
||||
self.cursor.execute("DELETE FROM users WHERE user_id = ?", (user_id,))
|
||||
self.conn.commit()
|
||||
|
||||
if self.cursor.rowcount > 0:
|
||||
logger.info(f"删除用户成功,ID: {user_id}, 用户名: {user['username']}")
|
||||
return True
|
||||
return False
|
||||
except self.DatabaseError:
|
||||
raise
|
||||
except sqlite3.Error as e:
|
||||
self.conn.rollback()
|
||||
error_msg = f"删除用户失败: {str(e)}"
|
||||
logger.error(f"删除用户错误: {e}, 用户ID: {user_id}")
|
||||
raise self.DatabaseError(error_msg)
|
||||
|
||||
def list_users(self, limit=100, offset=0):
|
||||
"""
|
||||
列出用户(支持分页)
|
||||
:param limit: 每页数量
|
||||
:param offset: 偏移量
|
||||
:return: 用户列表
|
||||
"""
|
||||
conn = self._get_connection()
|
||||
try:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(
|
||||
"SELECT * FROM users ORDER BY created_at DESC LIMIT ? OFFSET ?",
|
||||
(limit, offset)
|
||||
)
|
||||
users = []
|
||||
for row in cursor.fetchall():
|
||||
users.append(dict(row))
|
||||
return users
|
||||
except sqlite3.Error as e:
|
||||
print(f"列出用户错误: {e}")
|
||||
return []
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
关闭数据库连接(保持兼容性)
|
||||
"""
|
||||
# 现在每个操作都使用独立连接,不需要关闭持久连接
|
||||
print("数据库连接管理已更新,每个操作使用独立连接")
|
||||
|
||||
# 示例用法
|
||||
if __name__ == "__main__":
|
||||
print("=== 用户数据库示例程序 ===")
|
||||
# 创建数据库实例
|
||||
db = UserDatabase()
|
||||
|
||||
try:
|
||||
print("\n1. 创建测试用户")
|
||||
# 尝试创建已存在的用户
|
||||
try:
|
||||
user_id = db.create_user(
|
||||
username="demo_user",
|
||||
email="demo@example.com",
|
||||
password="SecurePass123"
|
||||
)
|
||||
print(f"✅ 创建用户成功,ID: {user_id}")
|
||||
except db.UserExistsError as e:
|
||||
print(f"ℹ️ {e},继续使用现有用户")
|
||||
user = db.get_user_by_username("demo_user")
|
||||
if user:
|
||||
user_id = user['user_id']
|
||||
|
||||
print("\n2. 查询用户信息")
|
||||
user = db.get_user_by_id(user_id)
|
||||
if user:
|
||||
# 不打印密码哈希
|
||||
safe_user = {k: v for k, v in user.items() if k != 'password_hash'}
|
||||
print(f"✅ 查询用户成功: {safe_user}")
|
||||
|
||||
print("\n3. 验证密码")
|
||||
is_valid = db.verify_password("demo_user", "SecurePass123")
|
||||
print(f"✅ 密码验证: {'通过' if is_valid else '失败'}")
|
||||
|
||||
# 测试密码验证失败的情况
|
||||
is_valid_wrong = db.verify_password("demo_user", "WrongPassword")
|
||||
print(f"✅ 错误密码验证: {'通过' if is_valid_wrong else '失败'}")
|
||||
|
||||
print("\n4. 更新用户信息")
|
||||
success = db.update_user(user_id,
|
||||
bio="这是一个测试用户账号",
|
||||
status=1)
|
||||
if success:
|
||||
updated_user = db.get_user_by_id(user_id)
|
||||
safe_updated = {k: v for k, v in updated_user.items() if k != 'password_hash'}
|
||||
print(f"✅ 更新用户成功: {safe_updated}")
|
||||
|
||||
print("\n5. 列出用户")
|
||||
users = db.list_users(limit=10)
|
||||
safe_users = [{k: v for k, v in u.items() if k != 'password_hash'} for u in users]
|
||||
print(f"✅ 共找到 {len(safe_users)} 个用户")
|
||||
for u in safe_users:
|
||||
print(f" - {u['username']} ({u['email']})")
|
||||
|
||||
print("\n6. 测试数据验证")
|
||||
try:
|
||||
db.create_user(username="ab", email="invalid-email", password="123")
|
||||
except db.ValidationError as e:
|
||||
print(f"✅ 验证错误捕获成功: {e}")
|
||||
|
||||
print("\n7. 更新登录时间")
|
||||
if db.update_login_time(user_id):
|
||||
print("✅ 登录时间更新成功")
|
||||
|
||||
print("\n=== 测试完成 ===")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 发生错误: {e}")
|
||||
finally:
|
||||
# 关闭数据库连接
|
||||
print("\n关闭数据库连接...")
|
||||
db.close()
|
||||
print("数据库连接已关闭")
|
||||
20
database/user_db.log
Normal file
20
database/user_db.log
Normal file
@@ -0,0 +1,20 @@
|
||||
2025-12-02 13:50:34,469 - UserDatabase - INFO - <20><><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><C3BB>ɹ<EFBFBD><C9B9><EFBFBD>ID: 6, <20>û<EFBFBD><C3BB><EFBFBD>: testuser123
|
||||
2025-12-02 13:50:40,670 - UserDatabase - INFO - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD>ɹ<EFBFBD>: testuser123
|
||||
2025-12-02 13:52:24,597 - UserDatabase - INFO - <20><><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><C3BB>ɹ<EFBFBD><C9B9><EFBFBD>ID: 7, <20>û<EFBFBD><C3BB><EFBFBD>: testuser3
|
||||
2025-12-02 13:52:31,767 - UserDatabase - INFO - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD>ɹ<EFBFBD>: testuser3
|
||||
2025-12-02 13:54:04,070 - UserDatabase - INFO - <20><><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><C3BB>ɹ<EFBFBD><C9B9><EFBFBD>ID: 8, <20>û<EFBFBD><C3BB><EFBFBD>: test
|
||||
2025-12-02 13:54:55,089 - UserDatabase - INFO - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD>ɹ<EFBFBD>: testuser3
|
||||
2025-12-02 13:56:44,653 - UserDatabase - INFO - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD>ɹ<EFBFBD>: testuser3
|
||||
2025-12-02 13:56:51,923 - UserDatabase - INFO - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD>ɹ<EFBFBD>: testuser3
|
||||
2025-12-02 13:57:52,850 - UserDatabase - INFO - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD>ɹ<EFBFBD>: testuser3
|
||||
2025-12-02 13:58:03,952 - UserDatabase - INFO - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD>ɹ<EFBFBD>: testuser3
|
||||
2025-12-02 13:58:30,809 - UserDatabase - INFO - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD>ɹ<EFBFBD>: testuser3
|
||||
2025-12-02 13:59:00,866 - UserDatabase - INFO - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD>ɹ<EFBFBD>: test
|
||||
2025-12-02 14:02:56,791 - UserDatabase - INFO - <20><><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><C3BB><EFBFBD>Ϣ<EFBFBD>ɹ<EFBFBD><C9B9><EFBFBD>ID: 7, <20><><EFBFBD><EFBFBD><EFBFBD>ֶ<EFBFBD>: email, bio
|
||||
2025-12-02 14:06:03,849 - UserDatabase - INFO - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD>ɹ<EFBFBD>: test
|
||||
2025-12-02 14:12:21,407 - UserDatabase - INFO - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD>ɹ<EFBFBD>: test
|
||||
2025-12-02 14:14:03,743 - UserDatabase - INFO - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD>ɹ<EFBFBD>: test
|
||||
2025-12-02 14:16:30,941 - UserDatabase - INFO - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD>ɹ<EFBFBD>: test
|
||||
2025-12-02 14:18:21,143 - UserDatabase - INFO - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD>ɹ<EFBFBD>: test
|
||||
2025-12-02 14:23:19,324 - UserDatabase - INFO - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD>ɹ<EFBFBD>: test
|
||||
2025-12-02 14:51:35,109 - UserDatabase - INFO - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD>ɹ<EFBFBD>: test
|
||||
BIN
database/users.db
Normal file
BIN
database/users.db
Normal file
Binary file not shown.
67
database/view_all_users.py
Normal file
67
database/view_all_users.py
Normal file
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
查看所有用户信息脚本
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
# 添加项目根目录到Python路径
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from database.user_database import UserDatabase
|
||||
|
||||
def main():
|
||||
"""
|
||||
主函数:查看所有用户信息
|
||||
"""
|
||||
print("=== 查看所有用户信息 ===")
|
||||
|
||||
try:
|
||||
# 创建数据库实例 - 指定正确的数据库路径
|
||||
db_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'backend', 'users.db')
|
||||
print(f"使用数据库路径: {db_path}")
|
||||
db = UserDatabase(db_path=db_path)
|
||||
|
||||
# 获取所有用户
|
||||
print("\n正在查询用户信息...")
|
||||
users = db.list_users(limit=1000)
|
||||
|
||||
if not users:
|
||||
print("\n❌ 未找到任何用户")
|
||||
return
|
||||
|
||||
print(f"\n✅ 共找到 {len(users)} 个用户:")
|
||||
print("-" * 100)
|
||||
print(f"{'用户ID':<10} | {'用户名':<20} | {'邮箱':<30} | {'状态':<10} | {'创建时间':<20}")
|
||||
print("-" * 100)
|
||||
|
||||
# 显示用户信息(不包含密码哈希)
|
||||
for user in users:
|
||||
# 创建不包含密码哈希的安全用户信息
|
||||
safe_user = {k: v for k, v in user.items() if k != 'password_hash'}
|
||||
|
||||
# 格式化输出
|
||||
user_id = safe_user.get('user_id', '')
|
||||
username = safe_user.get('username', '')
|
||||
email = safe_user.get('email', '')
|
||||
status = '正常' if safe_user.get('status') == 1 else '禁用' if safe_user.get('status') == 0 else '异常'
|
||||
created_at = safe_user.get('created_at', '')
|
||||
|
||||
print(f"{user_id:<10} | {username:<20} | {email:<30} | {status:<10} | {created_at:<20}")
|
||||
|
||||
print("-" * 100)
|
||||
print("\n✅ 用户信息查看完成")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ 发生错误: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
finally:
|
||||
# 关闭数据库连接
|
||||
if 'db' in locals():
|
||||
print("\n关闭数据库连接...")
|
||||
db.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user