成绩通知:仅在某门课程成绩正式发布后自动发送,按成绩单学生名单通知,不展示具体分数;发布状态与通知同一次提交。

手动发信:移除“成绩通知”选项,只能发送普通通知,原有角色与范围权限保持不变。
选课通知:管理员关闭一轮选课时,自动向每位参与学生发送一条汇总通知,包含最终选中课程及候补未成功课程;候补失效、轮次关闭和通知生成保持原子性。
关闭选课界面会明确提示发送通知,并显示实际通知人数。
This commit is contained in:
2026-07-26 19:12:42 +08:00 Unverified
parent ecc6546f3a
commit b0679a253d
20 changed files with 6804 additions and 210 deletions
+14 -12
View File
@@ -411,7 +411,8 @@ public sealed class GradesController(
$"《{courseName}》成绩已提交,请及时审核。",
collegeId,
"/grades",
cancellationToken);
cancellationToken,
NotificationCategory.Grade);
return NoContent();
}
@@ -452,7 +453,7 @@ public sealed class GradesController(
await NotificationService.SendToUserIdsAsync(db, teacherUserIds,
"成绩审核通过",
$"《{sheet.TeachingTask!.Course!.Name}》成绩已通过学院审核,等待校级发布。",
"/grades", cancellationToken);
"/grades", cancellationToken, NotificationCategory.Grade);
return NoContent();
}
@@ -497,7 +498,7 @@ public sealed class GradesController(
await NotificationService.SendToUserIdsAsync(db, teacherUserIds,
"成绩被退回",
$"《{sheet.TeachingTask!.Course!.Name}》成绩被退回修改:{sheet.ReviewComment}",
"/grades", cancellationToken);
"/grades", cancellationToken, NotificationCategory.Grade);
return NoContent();
}
@@ -505,7 +506,10 @@ public sealed class GradesController(
[Authorize(Roles = Publishers)]
public async Task<ActionResult> Publish(Guid id, CancellationToken cancellationToken)
{
var sheet = await AccessibleSheets().FirstOrDefaultAsync(
var sheet = await AccessibleSheets()
.Include(x => x.TeachingTask)
.ThenInclude(x => x!.Course)
.FirstOrDefaultAsync(
x => x.Id == id,
cancellationToken);
if (sheet is null) return NotFound();
@@ -514,13 +518,11 @@ public sealed class GradesController(
return ConflictProblem("只有审核通过的成绩单可以发布。");
sheet.Status = GradeSheetStatus.Published;
sheet.PublishedAt = DateTime.UtcNow;
await db.SaveChangesAsync(cancellationToken);
// Notify enrolled students
var studentUserIds = await db.CourseEnrollments
.Where(x =>
x.CourseSelectionOffering!.TeachingTaskId == sheet.TeachingTaskId &&
x.Status == CourseEnrollmentStatus.Enrolled)
// The grade sheet roster is authoritative at publication time. This also
// covers students added through approved roster corrections.
var studentUserIds = await db.GradeRecords
.Where(x => x.GradeSheetId == sheet.Id)
.Select(x => x.Student!.UserId)
.Where(id => id != null)
.Select(id => id!.Value)
@@ -528,8 +530,8 @@ public sealed class GradesController(
.ToListAsync(cancellationToken);
await NotificationService.SendToUserIdsAsync(db, studentUserIds,
"成绩已发布",
$"《{sheet.TeachingTask!.Course!.Name}》成绩已发布,请查看。",
"/grades", cancellationToken);
$"《{sheet.TeachingTask!.Course!.Name}》成绩已正式发布,请前往成绩单查看。",
"/grades", cancellationToken, NotificationCategory.Grade);
return NoContent();
}