成绩通知:仅在某门课程成绩正式发布后自动发送,按成绩单学生名单通知,不展示具体分数;发布状态与通知同一次提交。
手动发信:移除“成绩通知”选项,只能发送普通通知,原有角色与范围权限保持不变。 选课通知:管理员关闭一轮选课时,自动向每位参与学生发送一条汇总通知,包含最终选中课程及候补未成功课程;候补失效、轮次关闭和通知生成保持原子性。 关闭选课界面会明确提示发送通知,并显示实际通知人数。
This commit is contained in:
@@ -206,7 +206,9 @@ public sealed class ApprovalsController(AppDbContext db, ICurrentUserDataScope s
|
||||
.Select(x => new { StudentName = x.GradeRecord!.Student!.Name, CourseName = x.GradeRecord.GradeSheet!.TeachingTask!.Course!.Name, x.CurrentScore, x.RequestedScore })
|
||||
.FirstAsync(ct);
|
||||
await NotificationService.SendToRoleAsync(db, SystemRoles.CollegeAdmin, "成绩修改待审核",
|
||||
$"{gmInfo.StudentName} — 《{gmInfo.CourseName}》{gmInfo.CurrentScore}→{gmInfo.RequestedScore}", cancellationToken: ct);
|
||||
$"{gmInfo.StudentName} — 《{gmInfo.CourseName}》{gmInfo.CurrentScore}→{gmInfo.RequestedScore}",
|
||||
cancellationToken: ct,
|
||||
category: NotificationCategory.Grade);
|
||||
return Created("", new { gm.Id });
|
||||
}
|
||||
|
||||
@@ -219,7 +221,14 @@ public sealed class ApprovalsController(AppDbContext db, ICurrentUserDataScope s
|
||||
if (gm.Status != GradeModificationStatus.TeacherSubmitted) return ConflictProblem("状态不正确。");
|
||||
gm.Status = GradeModificationStatus.CollegeApproved; gm.CollegeReviewedAt = DateTime.UtcNow; gm.CollegeReviewedByUserId = scope.Current.UserId;
|
||||
await db.SaveChangesAsync(ct);
|
||||
await NotifyRole(SystemRoles.AcademicAdmin, "成绩修改待校级审核", $"{gm.GradeRecord!.Student!.Name} — 《{gm.GradeRecord.GradeSheet!.TeachingTask!.Course!.Name}》", ct);
|
||||
await NotificationService.SendToRoleAsync(
|
||||
db,
|
||||
SystemRoles.AcademicAdmin,
|
||||
"成绩修改待校级审核",
|
||||
$"{gm.GradeRecord!.Student!.Name} — 《{gm.GradeRecord.GradeSheet!.TeachingTask!.Course!.Name}》",
|
||||
linkUrl: "/approvals",
|
||||
cancellationToken: ct,
|
||||
category: NotificationCategory.Grade);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
@@ -227,7 +236,14 @@ public sealed class ApprovalsController(AppDbContext db, ICurrentUserDataScope s
|
||||
[Authorize(Roles = SystemRoles.SuperAdmin + "," + SystemRoles.AcademicAdmin)]
|
||||
public async Task<ActionResult> FinalApproveGradeMod(Guid id, CancellationToken ct)
|
||||
{
|
||||
var gm = await db.GradeModifications.Include(x => x.GradeRecord).FirstOrDefaultAsync(x => x.Id == id, ct);
|
||||
var gm = await db.GradeModifications
|
||||
.Include(x => x.GradeRecord)
|
||||
.ThenInclude(x => x!.Student)
|
||||
.Include(x => x.GradeRecord)
|
||||
.ThenInclude(x => x!.GradeSheet)
|
||||
.ThenInclude(x => x!.TeachingTask)
|
||||
.ThenInclude(x => x!.Course)
|
||||
.FirstOrDefaultAsync(x => x.Id == id, ct);
|
||||
if (gm is null) return NotFound();
|
||||
if (gm.Status != GradeModificationStatus.CollegeApproved) return ConflictProblem("需先通过学院审核。");
|
||||
gm.Status = GradeModificationStatus.Approved; gm.FinalReviewedAt = DateTime.UtcNow; gm.FinalReviewedByUserId = scope.Current.UserId;
|
||||
@@ -235,7 +251,27 @@ public sealed class ApprovalsController(AppDbContext db, ICurrentUserDataScope s
|
||||
gm.GradeRecord!.TotalScore = gm.RequestedScore;
|
||||
gm.GradeRecord.GradePoint = GradeCalculator.CalculateGradePoint(gm.RequestedScore);
|
||||
await db.SaveChangesAsync(ct);
|
||||
await NotificationService.SendAsync(db, gm.ApplicantUserId, "成绩修改已通过", "您的成绩修改申请已通过三级审批并生效。", cancellationToken: ct);
|
||||
await NotificationService.SendAsync(
|
||||
db,
|
||||
gm.ApplicantUserId,
|
||||
"成绩修改已通过",
|
||||
"您的成绩修改申请已通过三级审批并生效。",
|
||||
"/approvals",
|
||||
ct,
|
||||
NotificationCategory.Grade);
|
||||
var studentUserId = gm.GradeRecord.Student?.UserId;
|
||||
if (studentUserId.HasValue &&
|
||||
studentUserId.Value != gm.ApplicantUserId)
|
||||
{
|
||||
await NotificationService.SendAsync(
|
||||
db,
|
||||
studentUserId.Value,
|
||||
"成绩已更新",
|
||||
$"《{gm.GradeRecord.GradeSheet!.TeachingTask!.Course!.Name}》成绩已由 {gm.CurrentScore} 调整为 {gm.RequestedScore}。",
|
||||
"/grades",
|
||||
ct,
|
||||
NotificationCategory.Grade);
|
||||
}
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
@@ -249,7 +285,14 @@ public sealed class ApprovalsController(AppDbContext db, ICurrentUserDataScope s
|
||||
if (gm.Status == GradeModificationStatus.TeacherSubmitted) { gm.CollegeReviewedAt = DateTime.UtcNow; gm.CollegeReviewedByUserId = scope.Current.UserId; }
|
||||
else { gm.FinalReviewedAt = DateTime.UtcNow; gm.FinalReviewedByUserId = scope.Current.UserId; }
|
||||
await db.SaveChangesAsync(ct);
|
||||
await NotificationService.SendAsync(db, gm.ApplicantUserId, "成绩修改已驳回", gm.ReviewComment ?? "审核未通过。", cancellationToken: ct);
|
||||
await NotificationService.SendAsync(
|
||||
db,
|
||||
gm.ApplicantUserId,
|
||||
"成绩修改已驳回",
|
||||
gm.ReviewComment ?? "审核未通过。",
|
||||
"/approvals",
|
||||
ct,
|
||||
NotificationCategory.Grade);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
@@ -513,10 +556,10 @@ public sealed class ApprovalsController(AppDbContext db, ICurrentUserDataScope s
|
||||
ct);
|
||||
private ActionResult StudentNotFound() => Conflict(new ProblemDetails { Title = "未关联学生档案", Detail = "当前账号未关联有效学生档案。", Status = 409 });
|
||||
|
||||
private async Task NotifyManagers(string title, string content, CancellationToken ct) { await NotificationService.SendToRoleAsync(db, SystemRoles.CollegeAdmin, title, content, cancellationToken: ct); await NotificationService.SendToRoleAsync(db, SystemRoles.AcademicAdmin, title, content, cancellationToken: ct); }
|
||||
private async Task NotifyStudent(Guid sid, string title, string content, CancellationToken ct) { var uid = await db.Students.Where(s => s.Id == sid).Select(s => s.UserId).FirstOrDefaultAsync(ct); if (uid.HasValue) await NotificationService.SendAsync(db, uid.Value, title, content, cancellationToken: ct); }
|
||||
private async Task NotifyCollege(GradeModification gm, CancellationToken ct) { await NotificationService.SendToRoleAsync(db, SystemRoles.CollegeAdmin, "成绩修改待审核", $"{gm.GradeRecord!.Student!.Name} — 《{gm.GradeRecord.GradeSheet!.TeachingTask!.Course!.Name}》{gm.CurrentScore}→{gm.RequestedScore}", cancellationToken: ct); }
|
||||
private async Task NotifyRole(string role, string title, string content, CancellationToken ct) { await NotificationService.SendToRoleAsync(db, role, title, content, cancellationToken: ct); }
|
||||
private async Task NotifyManagers(string title, string content, CancellationToken ct) { await NotificationService.SendToRoleAsync(db, SystemRoles.CollegeAdmin, title, content, cancellationToken: ct, category: NotificationCategory.Approval); await NotificationService.SendToRoleAsync(db, SystemRoles.AcademicAdmin, title, content, cancellationToken: ct, category: NotificationCategory.Approval); }
|
||||
private async Task NotifyStudent(Guid sid, string title, string content, CancellationToken ct) { var uid = await db.Students.Where(s => s.Id == sid).Select(s => s.UserId).FirstOrDefaultAsync(ct); if (uid.HasValue) await NotificationService.SendAsync(db, uid.Value, title, content, "/approvals", ct, NotificationCategory.Approval); }
|
||||
private async Task NotifyCollege(GradeModification gm, CancellationToken ct) { await NotificationService.SendToRoleAsync(db, SystemRoles.CollegeAdmin, "成绩修改待审核", $"{gm.GradeRecord!.Student!.Name} — 《{gm.GradeRecord.GradeSheet!.TeachingTask!.Course!.Name}》{gm.CurrentScore}→{gm.RequestedScore}", cancellationToken: ct, category: NotificationCategory.Grade); }
|
||||
private async Task NotifyRole(string role, string title, string content, CancellationToken ct) { await NotificationService.SendToRoleAsync(db, role, title, content, cancellationToken: ct, category: NotificationCategory.Approval); }
|
||||
|
||||
private static string SSCLabel(StudentStatusChangeType t) => t switch { StudentStatusChangeType.Suspension => "休学", StudentStatusChangeType.Resumption => "复学", StudentStatusChangeType.Withdrawal => "退学", _ => "异动" };
|
||||
private static string CALabel(CourseAdjustmentType t) => t switch { CourseAdjustmentType.Reschedule => "调课", CourseAdjustmentType.Cancel => "停课", CourseAdjustmentType.Makeup => "补课", CourseAdjustmentType.Substitute => "代课", _ => "调停课" };
|
||||
|
||||
Reference in New Issue
Block a user