修复学期功能

This commit is contained in:
2026-04-22 02:37:27 +08:00
parent 4121e9624f
commit 8f77251910
8 changed files with 388 additions and 37 deletions

View File

@@ -9,6 +9,7 @@
# 版权所有 © Sea Network Technology Studio
# ===========================================
import datetime
from typing import Dict, Any, List, Optional
from models.semester import SemesterModel, SemesterArchiveModel
@@ -49,20 +50,41 @@ class SemesterService:
return {"success": False, "message": "学期名称不能为空"}
try:
# 自动将之前的活跃学期设为非活跃
await SemesterModel.deactivate_all()
# 创建新学期并自动设为活跃
# 创建学期(不预先 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)
# 判断新学期的日期范围是否包含今天,决定是否自动激活
should_activate = False
if start_date is not None:
try:
today = datetime.date.today()
s_date = datetime.datetime.strptime(start_date, "%Y-%m-%d").date()
e_date = (
datetime.datetime.strptime(end_date, "%Y-%m-%d").date()
if end_date is not None
else None
)
if s_date <= today and (e_date is None or e_date >= today):
should_activate = True
except (ValueError, TypeError):
should_activate = False
logger.info(f"用户[{operator_id}] 创建了新学期: {semester_name}")
if should_activate:
# 日期范围包含今天,自动激活
await SemesterModel.deactivate_all()
await SemesterModel.activate(semester_id)
logger.info(
f"用户[{operator_id}] 创建并激活新学期: {semester_name}"
)
else:
# 补录历史学期或未来学期,不激活
logger.info(
f"用户[{operator_id}] 创建补录学期(未激活): {semester_name}"
)
return {
"success": True,
@@ -120,6 +142,11 @@ class SemesterService:
if semester['is_archived']:
return {"success": False, "message": "该学期已归档"}
# 校验开始日期:无 start_date 时作业统计会全部归零
start_date = semester.get('start_date')
if not start_date:
return {"success": False, "message": "学期未设置开始日期,无法进行归档"}
# 获取所有活跃学生及其当前分数
students = await StudentModel.get_all(include_disabled=False)
if not students:
@@ -127,12 +154,60 @@ class SemesterService:
total_students = len(students)
# 获取学期的日期范围,用于查询考勤和作业统计
end_date = semester.get('end_date') or datetime.date.today().isoformat()
# 批量查询考勤和作业统计
attendance_stats = await SemesterModel.get_attendance_stats_by_semester(
semester_id, start_date, end_date
)
homework_stats = await SemesterModel.get_homework_stats_by_date_range(
start_date, end_date
)
# 构建 attendance_map: {student_id: {status_field: cnt, ...}}
attendance_map = {}
for stat in attendance_stats:
sid = stat['student_id']
if sid not in attendance_map:
attendance_map[sid] = {
'attendance_present': 0, 'attendance_absent': 0,
'attendance_late': 0, 'attendance_leave': 0
}
status_key = {
'present': 'attendance_present',
'absent': 'attendance_absent',
'late': 'attendance_late',
'leave': 'attendance_leave'
}.get(stat['status'])
if status_key:
attendance_map[sid][status_key] = stat['cnt']
# 构建 homework_map: {student_id: {status_field: cnt, ...}}
homework_map = {}
for stat in homework_stats:
sid = stat['student_id']
if sid not in homework_map:
homework_map[sid] = {
'homework_submitted': 0, 'homework_not_submitted': 0,
'homework_late': 0
}
status_key = {
'submitted': 'homework_submitted',
'not_submitted': 'homework_not_submitted',
'late': 'homework_late'
}.get(stat['status'])
if status_key:
homework_map[sid][status_key] = stat['cnt']
# 按分数降序排列以计算排名
sorted_students = sorted(students, key=lambda s: s['total_points'], reverse=True)
# 构建归档快照数据
archives_data = []
for rank, student in enumerate(sorted_students, 1):
att = attendance_map.get(student['student_id'], {})
hw = homework_map.get(student['student_id'], {})
archives_data.append({
'semester_id': semester_id,
'student_id': student['student_id'],
@@ -140,10 +215,17 @@ class SemesterService:
'student_name': student['name'],
'final_points': student['total_points'],
'rank_position': rank,
'total_students': total_students
'total_students': total_students,
'attendance_present': att.get('attendance_present', 0),
'attendance_absent': att.get('attendance_absent', 0),
'attendance_late': att.get('attendance_late', 0),
'attendance_leave': att.get('attendance_leave', 0),
'homework_submitted': hw.get('homework_submitted', 0),
'homework_not_submitted': hw.get('homework_not_submitted', 0),
'homework_late': hw.get('homework_late', 0),
})
# 保存归档快照
# 删除已有的归档数据以保证幂等性,再保存归档快照
await SemesterArchiveModel.delete_by_semester(semester_id)
await SemesterArchiveModel.batch_create(archives_data)
# 标记学期为已归档