首页问候

This commit is contained in:
2026-08-10 10:03:35 +08:00 Unverified
parent 64b67bb3ce
commit 40064cedda
3 changed files with 279 additions and 4 deletions
@@ -135,9 +135,143 @@ public sealed class DashboardController(
currentTerm,
counts,
pending,
await BuildGreetingAsync(scope, currentTermId, counts, pending, cancellationToken),
DateTime.UtcNow));
}
[HttpGet("greeting")]
public async Task<ActionResult<DashboardGreeting>> GetGreeting(
CancellationToken cancellationToken)
{
var currentTermId = await db.AcademicTerms.AsNoTracking()
.Where(x => x.IsCurrent)
.Select(x => (Guid?)x.Id)
.FirstOrDefaultAsync(cancellationToken);
return Ok(await BuildGreetingAsync(
currentUserDataScope.Current,
currentTermId,
null,
null,
cancellationToken));
}
private async Task<DashboardGreeting> BuildGreetingAsync(
CurrentUserScope scope,
Guid? currentTermId,
DashboardCounts? counts,
DashboardPending? pending,
CancellationToken cancellationToken)
{
var name = string.IsNullOrWhiteSpace(scope.DisplayName) ? "" : $"{scope.DisplayName}";
var greeting = GetTimeGreeting();
var isManager = scope.IsInRole(SystemRoles.SuperAdmin) ||
scope.IsInRole(SystemRoles.AcademicAdmin) ||
scope.IsInRole(SystemRoles.CollegeAdmin) ||
scope.IsInRole(SystemRoles.Leader) ||
scope.IsInRole(SystemRoles.Counselor);
if (!isManager && scope.IsInRole(SystemRoles.Student))
{
var studentId = await db.Students.AsNoTracking()
.Where(x => x.UserId == scope.UserId)
.Select(x => (Guid?)x.Id)
.FirstOrDefaultAsync(cancellationToken);
if (!studentId.HasValue)
return new DashboardGreeting("Student", $"{name}{greeting}", "绑定学籍后,将为你生成课程与成绩学习概览。", "学习节奏", []);
var enrollments = db.CourseEnrollments.AsNoTracking().Where(x =>
x.StudentId == studentId.Value &&
x.Status == CourseEnrollmentStatus.Enrolled &&
currentTermId.HasValue &&
x.CourseSelectionOffering!.CourseSelectionRound!.AcademicTermId == currentTermId.Value);
var selectedCourses = await enrollments.CountAsync(cancellationToken);
var selectedCredits = await enrollments.SumAsync(
x => (decimal?)x.CourseSelectionOffering!.TeachingTask!.Course!.Credits,
cancellationToken) ?? 0;
var publishedGrades = db.GradeRecords.AsNoTracking().Where(x =>
x.StudentId == studentId.Value &&
x.GradeSheet!.Status == GradeSheetStatus.Published);
var gradeCount = await publishedGrades.CountAsync(cancellationToken);
var average = await publishedGrades
.Where(x => x.ExamStatus == GradeExamStatus.Normal && x.TotalScore.HasValue)
.AverageAsync(x => (decimal?)x.TotalScore, cancellationToken);
var failed = await publishedGrades.CountAsync(x =>
x.ExamStatus == GradeExamStatus.Normal && x.TotalScore.HasValue && x.TotalScore < 60,
cancellationToken);
var subtitle = failed > 0
? $"已发布成绩中有 {failed} 门课程需要重点关注,建议优先查看课程反馈。"
: selectedCourses > 0
? $"本学期已选 {selectedCourses} 门课、{selectedCredits:0.#} 学分,保持现在的学习节奏。"
: "本学期暂未发现有效选课记录,可先查看选课安排。";
return new DashboardGreeting("Student", $"{name}{greeting}", subtitle, "学习节奏",
[
new DashboardGreetingInsight("本学期课程", $"{selectedCourses} 门", $"共 {selectedCredits:0.#} 学分", "calm"),
new DashboardGreetingInsight("已发布成绩", $"{gradeCount} 门", average.HasValue ? $"平均分 {average.Value:0.0}" : "等待成绩发布", "calm"),
new DashboardGreetingInsight("重点关注", $"{failed} 门", failed > 0 ? "建议尽早安排复习与答疑" : "当前无不及格记录", failed > 0 ? "attention" : "positive")
]);
}
if (!isManager && scope.IsInRole(SystemRoles.Teacher))
{
var teacherId = await db.Teachers.AsNoTracking()
.Where(x => x.UserId == scope.UserId && x.Status == TeacherStatus.Active)
.Select(x => (Guid?)x.Id)
.FirstOrDefaultAsync(cancellationToken);
if (!teacherId.HasValue)
return new DashboardGreeting("Teacher", $"{name}{greeting}", "绑定教师档案后,将为你生成本学期教学负荷概览。", "教学节奏", []);
var tasks = db.TeachingTasks.AsNoTracking().Where(x =>
currentTermId.HasValue && x.AcademicTermId == currentTermId.Value &&
x.Teachers.Any(t => t.TeacherId == teacherId.Value));
var teachingClasses = await tasks.CountAsync(cancellationToken);
var estimatedHours = await tasks.SumAsync(
x => (int?)(x.WeeklyHours * (x.EndWeek - x.StartWeek + 1)), cancellationToken) ?? 0;
var gradeSheets = db.GradeSheets.AsNoTracking().Where(x =>
x.TeachingTask!.Teachers.Any(t => t.TeacherId == teacherId.Value) &&
currentTermId.HasValue && x.TeachingTask.AcademicTermId == currentTermId.Value);
var pendingGrades = await gradeSheets.CountAsync(x =>
x.Status == GradeSheetStatus.Draft ||
x.Status == GradeSheetStatus.Returned, cancellationToken);
var submittedGrades = await gradeSheets.CountAsync(x => x.Status == GradeSheetStatus.Submitted, cancellationToken);
var subtitle = pendingGrades > 0
? $"有 {pendingGrades} 张成绩登记册尚待提交,完成后可进入审核流程。"
: teachingClasses > 0
? $"本学期承担 {teachingClasses} 个教学班,预计授课 {estimatedHours} 学时。"
: "本学期暂未分配教学班,请留意教学任务安排。";
return new DashboardGreeting("Teacher", $"{name}{greeting}", subtitle, "教学节奏",
[
new DashboardGreetingInsight("教学班", $"{teachingClasses} 个", $"预计 {estimatedHours} 学时", "calm"),
new DashboardGreetingInsight("待提交成绩", $"{pendingGrades} 张", pendingGrades > 0 ? "请在截止日前完成登记" : "当前无需提交", pendingGrades > 0 ? "attention" : "positive"),
new DashboardGreetingInsight("审核中成绩", $"{submittedGrades} 张", submittedGrades > 0 ? "等待审核结果" : "暂无审核中登记册", "calm")
]);
}
var actionable = pending is null ? 0 : pending.TeacherApplications + pending.GradeSheets +
pending.CourseAdjustments + pending.StudentStatusChanges + pending.GradeModifications +
pending.ClassroomReservations + pending.GeneralApprovals;
var taskCount = counts?.TeachingTasks ?? 0;
var scheduledCount = counts?.ScheduledTeachingTasks ?? 0;
var subtitleForManager = actionable > 0
? $"当前有 {actionable} 项待办需要跟进,优先处理时效性审核事项。"
: taskCount > 0
? $"本学期 {taskCount} 个教学班正在运行,当前没有积压待办。"
: "当前学期运行数据已就绪,可从教学任务开始推进。";
return new DashboardGreeting("Manager", $"{name}{greeting}", subtitleForManager, "运行态势",
[
new DashboardGreetingInsight("当前待办", $"{actionable} 项", actionable > 0 ? "优先处理可操作事项" : "暂无积压", actionable > 0 ? "attention" : "positive"),
new DashboardGreetingInsight("本学期教学班", $"{taskCount} 个", "教学运行规模", "calm"),
new DashboardGreetingInsight("已进入课表", $"{scheduledCount} 个", taskCount > 0 ? $"覆盖 {Math.Round(scheduledCount * 100d / taskCount)}% 教学班" : "等待教学任务发布", "calm")
]);
}
private static string GetTimeGreeting()
{
var hour = DateTime.UtcNow.AddHours(8).Hour;
return hour < 11 ? "早上好" : hour < 14 ? "中午好" : hour < 18 ? "下午好" : "晚上好";
}
private async Task<DashboardPending> LoadPendingAsync(
CurrentUserScope scope,
Guid? restrictedCollegeId,
@@ -276,8 +410,22 @@ public sealed record DashboardResponse(
DashboardTerm? CurrentTerm,
DashboardCounts Counts,
DashboardPending Pending,
DashboardGreeting Greeting,
DateTime GeneratedAt);
public sealed record DashboardGreeting(
string Role,
string Title,
string Subtitle,
string Label,
IReadOnlyList<DashboardGreetingInsight> Insights);
public sealed record DashboardGreetingInsight(
string Label,
string Value,
string Hint,
string Tone);
public sealed record DashboardAudience(
string Level,
string Title,