v1.2版本更新发布

This commit is contained in:
2026-04-22 00:59:29 +08:00
parent 194c076456
commit 4121e9624f
26 changed files with 1323 additions and 61 deletions

View File

@@ -0,0 +1,244 @@
# ===========================================
# 班级操行分管理系统 - 学期服务
#
# 开发者: Canglan
# 联系方式: admin@sea-studio.top
# 版权归属: Sea Network Technology Studio
# 许可证: MIT License
#
# 版权所有 © Sea Network Technology Studio
# ===========================================
from typing import Dict, Any, List, Optional
from models.semester import SemesterModel, SemesterArchiveModel
from models.student import StudentModel
from middleware.permission import PermissionChecker
from config import settings
from utils.logger import get_logger
from utils.database import execute_query
logger = get_logger(__name__)
class SemesterService:
"""学期管理服务"""
@staticmethod
async def list_semesters() -> Dict[str, Any]:
"""获取学期列表"""
try:
semesters = await SemesterModel.get_all()
return {
"success": True,
"semesters": semesters
}
except Exception as e:
logger.error(f"获取学期列表失败: {e}")
return {"success": False, "message": f"获取学期列表失败: {str(e)}"}
@staticmethod
async def create_semester(
semester_name: str,
start_date: str = None,
end_date: str = None,
operator_id: int = None
) -> Dict[str, Any]:
"""创建新学期"""
if not semester_name or not semester_name.strip():
return {"success": False, "message": "学期名称不能为空"}
try:
# 自动将之前的活跃学期设为非活跃
await SemesterModel.deactivate_all()
# 创建新学期并自动设为活跃
semester_id = await SemesterModel.create(
semester_name=semester_name.strip(),
start_date=start_date,
end_date=end_date
)
# 设为活跃学期
await SemesterModel.activate(semester_id)
logger.info(f"用户[{operator_id}] 创建了新学期: {semester_name}")
return {
"success": True,
"message": "学期创建成功",
"semester_id": semester_id
}
except Exception as e:
logger.error(f"创建学期失败: {e}")
return {"success": False, "message": f"创建学期失败: {str(e)}"}
@staticmethod
async def activate_semester(
semester_id: int,
operator_id: int = None
) -> Dict[str, Any]:
"""设为当前活跃学期"""
try:
# 检查学期是否存在
semester = await SemesterModel.get_by_id(semester_id)
if not semester:
return {"success": False, "message": "学期不存在"}
# 已归档的学期不能激活
if semester['is_archived']:
return {"success": False, "message": "已归档的学期不能设为当前学期"}
# 自动将之前的活跃学期设为非活跃
await SemesterModel.deactivate_all()
# 设为活跃
result = await SemesterModel.activate(semester_id)
if result:
logger.info(f"用户[{operator_id}] 激活了学期: {semester['semester_name']}")
return {"success": True, "message": "已设为当前学期"}
else:
return {"success": False, "message": "激活失败"}
except Exception as e:
logger.error(f"激活学期失败: {e}")
return {"success": False, "message": f"激活学期失败: {str(e)}"}
@staticmethod
async def archive_semester(
semester_id: int,
operator_id: int = None
) -> Dict[str, Any]:
"""归档学期"""
try:
# 检查学期是否存在
semester = await SemesterModel.get_by_id(semester_id)
if not semester:
return {"success": False, "message": "学期不存在"}
# 已归档的不能重复归档
if semester['is_archived']:
return {"success": False, "message": "该学期已归档"}
# 获取所有活跃学生及其当前分数
students = await StudentModel.get_all(include_disabled=False)
if not students:
return {"success": False, "message": "没有可归档的学生数据"}
total_students = len(students)
# 按分数降序排列以计算排名
sorted_students = sorted(students, key=lambda s: s['total_points'], reverse=True)
# 构建归档快照数据
archives_data = []
for rank, student in enumerate(sorted_students, 1):
archives_data.append({
'semester_id': semester_id,
'student_id': student['student_id'],
'student_no': student['student_no'],
'student_name': student['name'],
'final_points': student['total_points'],
'rank_position': rank,
'total_students': total_students
})
# 保存归档快照
await SemesterArchiveModel.batch_create(archives_data)
# 标记学期为已归档
await SemesterModel.archive(semester_id)
logger.info(
f"用户[{operator_id}] 归档了学期: {semester['semester_name']}, "
f"{total_students} 名学生"
)
return {
"success": True,
"message": f"学期归档成功,共归档 {total_students} 名学生数据"
}
except Exception as e:
logger.error(f"归档学期失败: {e}")
return {"success": False, "message": f"归档学期失败: {str(e)}"}
@staticmethod
async def get_archive_records(
semester_id: int,
page: int = 1,
page_size: int = 50
) -> Dict[str, Any]:
"""获取归档数据(只读)"""
try:
# 检查学期是否存在
semester = await SemesterModel.get_by_id(semester_id)
if not semester:
return {"success": False, "message": "学期不存在"}
archives = await SemesterArchiveModel.get_by_semester(semester_id)
total = len(archives)
offset = (page - 1) * page_size
paged_archives = archives[offset:offset + page_size]
return {
"success": True,
"data": {
"semester": {
"semester_id": semester['semester_id'],
"semester_name": semester['semester_name'],
"start_date": semester.get('start_date'),
"end_date": semester.get('end_date'),
"is_archived": bool(semester['is_archived'])
},
"archives": paged_archives,
"total": total,
"page": page,
"page_size": page_size,
"total_pages": (total + page_size - 1) // page_size
}
}
except Exception as e:
logger.error(f"获取归档数据失败: {e}")
return {"success": False, "message": f"获取归档数据失败: {str(e)}"}
@staticmethod
async def reset_student_points(initial_points: int = None) -> Dict[str, Any]:
"""重置所有学生的操行分为初始分"""
if initial_points is None:
initial_points = settings.STUDENT_INITIAL_POINTS
try:
from utils.database import execute_update
sql = "UPDATE students SET total_points = %s WHERE status = 1"
affected = await execute_update(sql, (initial_points,))
logger.info(f"已重置 {affected} 名学生的操行分为 {initial_points}")
return {
"success": True,
"message": f"已重置 {affected} 名学生的操行分为 {initial_points}",
"affected": affected
}
except Exception as e:
logger.error(f"重置学生分数失败: {e}")
return {"success": False, "message": f"重置学生分数失败: {str(e)}"}
@staticmethod
async def get_active_semester() -> Dict[str, Any]:
"""获取当前活跃学期"""
try:
active = await SemesterModel.get_active()
if active:
return {
"success": True,
"semester": active
}
else:
return {
"success": True,
"semester": None,
"message": "当前没有活跃学期"
}
except Exception as e:
logger.error(f"获取活跃学期失败: {e}")
return {"success": False, "message": f"获取活跃学期失败: {str(e)}"}