v2.3修复
This commit is contained in:
@@ -56,14 +56,18 @@ class StudentModel:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
async def get_dormitory_list() -> List[str]:
|
async def get_dormitory_list() -> List[str]:
|
||||||
"""获取所有不重复的宿舍号列表"""
|
"""获取所有不重复的宿舍号列表"""
|
||||||
sql = """
|
try:
|
||||||
SELECT DISTINCT dormitory_number
|
sql = """
|
||||||
FROM students
|
SELECT DISTINCT dormitory_number
|
||||||
WHERE status = 1 AND dormitory_number IS NOT NULL AND dormitory_number != ''
|
FROM students
|
||||||
ORDER BY dormitory_number
|
WHERE status = 1 AND dormitory_number IS NOT NULL AND dormitory_number != ''
|
||||||
"""
|
ORDER BY dormitory_number
|
||||||
rows = await execute_query(sql)
|
"""
|
||||||
return [row["dormitory_number"] for row in rows]
|
rows = await execute_query(sql)
|
||||||
|
return [row["dormitory_number"] for row in rows]
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"dormitory_number 列不存在,返回空列表: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def create(
|
async def create(
|
||||||
@@ -74,17 +78,25 @@ class StudentModel:
|
|||||||
initial_points: int = 60
|
initial_points: int = 60
|
||||||
) -> int:
|
) -> int:
|
||||||
"""创建学生(初始操行分默认60分)"""
|
"""创建学生(初始操行分默认60分)"""
|
||||||
sql = """
|
if dormitory_number is not None:
|
||||||
INSERT INTO students (student_no, name, parent_phone, dormitory_number, total_points)
|
sql = """
|
||||||
VALUES (%s, %s, %s, %s, %s)
|
INSERT INTO students (student_no, name, parent_phone, dormitory_number, total_points)
|
||||||
"""
|
VALUES (%s, %s, %s, %s, %s)
|
||||||
return await execute_insert(sql, (student_no, name, parent_phone, dormitory_number, initial_points))
|
"""
|
||||||
|
return await execute_insert(sql, (student_no, name, parent_phone, dormitory_number, initial_points))
|
||||||
|
else:
|
||||||
|
sql = """
|
||||||
|
INSERT INTO students (student_no, name, parent_phone, total_points)
|
||||||
|
VALUES (%s, %s, %s, %s)
|
||||||
|
"""
|
||||||
|
return await execute_insert(sql, (student_no, name, parent_phone, initial_points))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def update(student_id: int, name: str = None, parent_phone: str = None, dormitory_number: str = None, status: int = None) -> bool:
|
async def update(student_id: int, name: str = None, parent_phone: str = None, dormitory_number: str = None, status: int = None) -> bool:
|
||||||
"""更新学生信息"""
|
"""更新学生信息"""
|
||||||
updates = []
|
updates = []
|
||||||
params = []
|
params = []
|
||||||
|
has_dormitory = False
|
||||||
|
|
||||||
if name is not None:
|
if name is not None:
|
||||||
updates.append("name = %s")
|
updates.append("name = %s")
|
||||||
@@ -95,6 +107,7 @@ class StudentModel:
|
|||||||
if dormitory_number is not None:
|
if dormitory_number is not None:
|
||||||
updates.append("dormitory_number = %s")
|
updates.append("dormitory_number = %s")
|
||||||
params.append(dormitory_number)
|
params.append(dormitory_number)
|
||||||
|
has_dormitory = True
|
||||||
if status is not None:
|
if status is not None:
|
||||||
updates.append("status = %s")
|
updates.append("status = %s")
|
||||||
params.append(status)
|
params.append(status)
|
||||||
@@ -104,8 +117,30 @@ class StudentModel:
|
|||||||
|
|
||||||
params.append(student_id)
|
params.append(student_id)
|
||||||
sql = f"UPDATE students SET {', '.join(updates)} WHERE student_id = %s"
|
sql = f"UPDATE students SET {', '.join(updates)} WHERE student_id = %s"
|
||||||
result = await execute_update(sql, tuple(params))
|
try:
|
||||||
return result > 0
|
result = await execute_update(sql, tuple(params))
|
||||||
|
return result > 0
|
||||||
|
except Exception as e:
|
||||||
|
if has_dormitory:
|
||||||
|
logger.warning(f"dormitory_number 列不存在,尝试不含该字段重试: {e}")
|
||||||
|
retry_updates = []
|
||||||
|
retry_params = []
|
||||||
|
if name is not None:
|
||||||
|
retry_updates.append("name = %s")
|
||||||
|
retry_params.append(name)
|
||||||
|
if parent_phone is not None:
|
||||||
|
retry_updates.append("parent_phone = %s")
|
||||||
|
retry_params.append(parent_phone)
|
||||||
|
if status is not None:
|
||||||
|
retry_updates.append("status = %s")
|
||||||
|
retry_params.append(status)
|
||||||
|
if not retry_updates:
|
||||||
|
return True
|
||||||
|
retry_params.append(student_id)
|
||||||
|
sql = f"UPDATE students SET {', '.join(retry_updates)} WHERE student_id = %s"
|
||||||
|
result = await execute_update(sql, tuple(retry_params))
|
||||||
|
return result > 0
|
||||||
|
raise
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def delete(student_id: int) -> bool:
|
async def delete(student_id: int) -> bool:
|
||||||
@@ -117,7 +152,7 @@ class StudentModel:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
async def update_total_points(student_id: int, points_change: int) -> bool:
|
async def update_total_points(student_id: int, points_change: int) -> bool:
|
||||||
"""更新学生总分"""
|
"""更新学生总分"""
|
||||||
sql = "UPDATE students SET total_points = total_points + %s, points_updated_at = CURRENT_TIMESTAMP WHERE student_id = %s"
|
sql = "UPDATE students SET total_points = total_points + %s WHERE student_id = %s"
|
||||||
result = await execute_update(sql, (points_change, student_id))
|
result = await execute_update(sql, (points_change, student_id))
|
||||||
return result > 0
|
return result > 0
|
||||||
|
|
||||||
@@ -125,10 +160,10 @@ class StudentModel:
|
|||||||
async def get_ranking(limit: int = 50) -> List[Dict[str, Any]]:
|
async def get_ranking(limit: int = 50) -> List[Dict[str, Any]]:
|
||||||
"""获取学生排行(单班级)"""
|
"""获取学生排行(单班级)"""
|
||||||
sql = """
|
sql = """
|
||||||
SELECT student_id, student_no, name, total_points, points_updated_at
|
SELECT student_id, student_no, name, total_points
|
||||||
FROM students
|
FROM students
|
||||||
WHERE status = 1
|
WHERE status = 1
|
||||||
ORDER BY total_points DESC, points_updated_at ASC
|
ORDER BY total_points DESC, student_id ASC
|
||||||
LIMIT %s
|
LIMIT %s
|
||||||
"""
|
"""
|
||||||
results = await execute_query(sql, (limit,))
|
results = await execute_query(sql, (limit,))
|
||||||
|
|||||||
@@ -37,6 +37,64 @@ ALL_VERSIONS = {
|
|||||||
'2.3': 'v2.3.sql',
|
'2.3': 'v2.3.sql',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 版本特征标记(按优先级从高到低)
|
||||||
|
VERSION_MARKERS = [
|
||||||
|
('2.0', 'students', 'dormitory_number'),
|
||||||
|
('1.8', 'conduct_records', 'related_type'),
|
||||||
|
('1.7', 'subjects', 'sort_order'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
async def _detect_current_version() -> str:
|
||||||
|
"""检测当前数据库版本,优先从 system_settings 读取,否则通过列特征推断"""
|
||||||
|
# 1. 尝试从 system_settings 读取 db_version
|
||||||
|
try:
|
||||||
|
row = await execute_query(
|
||||||
|
"SELECT setting_value FROM system_settings WHERE setting_key = 'db_version'"
|
||||||
|
)
|
||||||
|
if row:
|
||||||
|
return row[0]['setting_value']
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"查询 system_settings 表失败,将通过列特征推断版本: {e}")
|
||||||
|
|
||||||
|
# 2. 通过列特征推断版本
|
||||||
|
inferred_version = '1.0'
|
||||||
|
for version, table, column in VERSION_MARKERS:
|
||||||
|
try:
|
||||||
|
result = await execute_query(
|
||||||
|
"SELECT COUNT(*) as cnt FROM INFORMATION_SCHEMA.COLUMNS "
|
||||||
|
"WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s AND COLUMN_NAME = %s",
|
||||||
|
(table, column)
|
||||||
|
)
|
||||||
|
if result and result[0]['cnt'] > 0:
|
||||||
|
inferred_version = version
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"检查列特征失败 ({table}.{column}): {e}")
|
||||||
|
|
||||||
|
logger.info(f"通过列特征推断数据库版本为: {inferred_version}")
|
||||||
|
|
||||||
|
# 3. 确保 system_settings 表存在并写入推断版本
|
||||||
|
try:
|
||||||
|
await execute_update(
|
||||||
|
"CREATE TABLE IF NOT EXISTS `system_settings` ("
|
||||||
|
"`setting_key` VARCHAR(50) PRIMARY KEY,"
|
||||||
|
"`setting_value` VARCHAR(255) NOT NULL,"
|
||||||
|
"`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"
|
||||||
|
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
|
||||||
|
)
|
||||||
|
await execute_update(
|
||||||
|
"INSERT INTO system_settings (setting_key, setting_value) VALUES ('db_version', %s) "
|
||||||
|
"ON DUPLICATE KEY UPDATE setting_value = %s",
|
||||||
|
(inferred_version, inferred_version)
|
||||||
|
)
|
||||||
|
logger.info(f"已将推断版本 {inferred_version} 写入 system_settings")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"写入推断版本失败: {e}")
|
||||||
|
|
||||||
|
return inferred_version
|
||||||
|
|
||||||
|
|
||||||
@router.get("/check")
|
@router.get("/check")
|
||||||
async def check_upgrade(request: Request):
|
async def check_upgrade(request: Request):
|
||||||
"""检查数据库版本是否需要升级"""
|
"""检查数据库版本是否需要升级"""
|
||||||
@@ -51,16 +109,8 @@ async def check_upgrade(request: Request):
|
|||||||
if not is_teacher:
|
if not is_teacher:
|
||||||
return error_response(message="仅班主任可执行升级操作", code=403)
|
return error_response(message="仅班主任可执行升级操作", code=403)
|
||||||
|
|
||||||
# 检测当前数据库版本
|
# 检测当前数据库版本(支持自动推断)
|
||||||
current_version = '0.0.0'
|
current_version = await _detect_current_version()
|
||||||
try:
|
|
||||||
row = await execute_query(
|
|
||||||
"SELECT setting_value FROM system_settings WHERE setting_key = 'db_version'"
|
|
||||||
)
|
|
||||||
if row:
|
|
||||||
current_version = row[0]['setting_value']
|
|
||||||
except Exception:
|
|
||||||
pass # 表不存在时使用默认值
|
|
||||||
|
|
||||||
# 读取目标版本(从 VERSION 文件)
|
# 读取目标版本(从 VERSION 文件)
|
||||||
version_file = os.path.join(os.path.dirname(os.path.dirname(__file__)), '..', 'VERSION')
|
version_file = os.path.join(os.path.dirname(os.path.dirname(__file__)), '..', 'VERSION')
|
||||||
|
|||||||
@@ -33,25 +33,45 @@ class AdminService:
|
|||||||
"""获取所有学生列表"""
|
"""获取所有学生列表"""
|
||||||
offset = (page - 1) * page_size
|
offset = (page - 1) * page_size
|
||||||
|
|
||||||
sql = """
|
try:
|
||||||
SELECT student_id, student_no, name, total_points, parent_phone, dormitory_number, status
|
sql = """
|
||||||
FROM students
|
SELECT student_id, student_no, name, total_points, parent_phone, dormitory_number, status
|
||||||
WHERE status = 1
|
FROM students
|
||||||
"""
|
WHERE status = 1
|
||||||
params = []
|
"""
|
||||||
|
params = []
|
||||||
if search:
|
|
||||||
sql += " AND (student_no LIKE %s OR name LIKE %s)"
|
if search:
|
||||||
params.extend([f"%{search}%", f"%{search}%"])
|
sql += " AND (student_no LIKE %s OR name LIKE %s)"
|
||||||
|
params.extend([f"%{search}%", f"%{search}%"])
|
||||||
if dormitory_number:
|
|
||||||
sql += " AND dormitory_number = %s"
|
if dormitory_number:
|
||||||
params.append(dormitory_number)
|
sql += " AND dormitory_number = %s"
|
||||||
|
params.append(dormitory_number)
|
||||||
sql += " ORDER BY student_no LIMIT %s OFFSET %s"
|
|
||||||
params.extend([page_size, offset])
|
sql += " ORDER BY student_no LIMIT %s OFFSET %s"
|
||||||
|
params.extend([page_size, offset])
|
||||||
students = await execute_query(sql, tuple(params))
|
|
||||||
|
students = await execute_query(sql, tuple(params))
|
||||||
|
has_dormitory = True
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"dormitory_number 列不存在,使用不含该字段的查询: {e}")
|
||||||
|
sql = """
|
||||||
|
SELECT student_id, student_no, name, total_points, parent_phone, status
|
||||||
|
FROM students
|
||||||
|
WHERE status = 1
|
||||||
|
"""
|
||||||
|
params = []
|
||||||
|
|
||||||
|
if search:
|
||||||
|
sql += " AND (student_no LIKE %s OR name LIKE %s)"
|
||||||
|
params.extend([f"%{search}%", f"%{search}%"])
|
||||||
|
|
||||||
|
sql += " ORDER BY student_no LIMIT %s OFFSET %s"
|
||||||
|
params.extend([page_size, offset])
|
||||||
|
|
||||||
|
students = await execute_query(sql, tuple(params))
|
||||||
|
has_dormitory = False
|
||||||
|
|
||||||
# 获取总数
|
# 获取总数
|
||||||
count_sql = "SELECT COUNT(*) as total FROM students WHERE status = 1"
|
count_sql = "SELECT COUNT(*) as total FROM students WHERE status = 1"
|
||||||
@@ -59,7 +79,7 @@ class AdminService:
|
|||||||
if search:
|
if search:
|
||||||
count_sql += " AND (student_no LIKE %s OR name LIKE %s)"
|
count_sql += " AND (student_no LIKE %s OR name LIKE %s)"
|
||||||
count_params.extend([f"%{search}%", f"%{search}%"])
|
count_params.extend([f"%{search}%", f"%{search}%"])
|
||||||
if dormitory_number:
|
if dormitory_number and has_dormitory:
|
||||||
count_sql += " AND dormitory_number = %s"
|
count_sql += " AND dormitory_number = %s"
|
||||||
count_params.append(dormitory_number)
|
count_params.append(dormitory_number)
|
||||||
if count_params:
|
if count_params:
|
||||||
|
|||||||
Reference in New Issue
Block a user