Commit Graph
50 Commits
  • 已按“实验成绩独立管理”完成修改。
    每个实验项目独立建成绩单,支持评分项、参与状态、补做次数、安全违规、报告地址和教师评语。
    流程为:录入 → 学院审核 → 教务发布;学生只能查看已发布成绩。
    自主预约实验支持同步有效预约名单,已评分记录不会被误删。
    课程成绩可将已发布实验成绩汇总为快照导入指定分项;导入后锁定,不能手工篡改。
    实验项目成绩不完整时,对应课程分项保持空白,不按 0 分处理。
    一级“实验管理”菜单下增加“项目与场次”“实验成绩”;学生端增加“实验安排”“实验成绩”。
    已生成 MySQL 正式迁移及 SQLite 开发迁移,尚未操作生产数据库。
    界面采用“实验项目 → 独立评分 → 审核发布”工作台设计。
  • 参照现有的 ExamArrangementJob / SchedulePublishJob 后台任务模式,将发布改为通过 RabbitMQ
    队列异步执行,拆分查询消除笛卡尔积。
    
      修改的文件(共 13 个)
    
      ┌───────────────────────────────────────────────────────────────┬──────────────────────────────────────────────────┐
      │                             文件                              │                       变更                       │
      ├───────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────┤
      │ Domain/Academic/ExamEntities.cs                               │ 新增 ExamPublishJob 实体 + ExamPublishJobStatus  │
      │                                                               │ / ExamPublishJobKind 枚举                        │
      ├───────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────┤
      │ Domain/System/BackgroundJobOutboxMessage.cs                   │ BackgroundJobKind 新增 ExamPublish = 6           │
      ├───────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────┤
      │ Infrastructure/BackgroundJobs/BackgroundJobOptions.cs         │ 新增 ExamPublishConcurrency 配置项               │
      ├───────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────┤
      │ Infrastructure/BackgroundJobs/BackgroundJobRunner.cs          │ RunAsync 和 MarkJobRetryLimitExceeded 添加       │
      │                                                               │ ExamPublish 分支                                 │
      ├───────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────┤
      │ Infrastructure/BackgroundJobs/RabbitMqBackgroundJobs.cs       │ JobKinds 数组和 RoutingKey 添加 ExamPublish →    │
      │                                                               │ "exam.publish"                                   │
      ├───────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────┤
      │ Infrastructure/BackgroundJobs/BackgroundJobOutboxPublisher.cs │ 启动恢复逻辑添加 ExamPublishJobs                 │
      ├───────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────┤
      │ Infrastructure/Persistence/AppDbContext.cs                    │ 新增 ExamPublishJobs DbSet                       │
      ├───────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────┤
      │ Controllers/OperationsController.cs                           │ CountFailedJobsAsync / GetFailedJobs             │
      │                                                               │ 添加考试发布失败统计和筛选                       │
      ├───────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────┤
      │ Program.cs                                                    │ 校验 ExamPublishConcurrency + 注册               │
      │                                                               │ ExamPublishJobProcessor                          │
      ├───────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────┤
      │ Infrastructure/Exams/ExamPublishJobs.cs                       │ 新文件 —                                         │
      │                                                               │ ExamPublishJobProcessor,拆分查询校验后发布      │
      ├───────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────┤
      │ Controllers/ExamsController.cs                                │ Publish 改为创建后台任务 + 202 返回;新增 GET    │
      │                                                               │ publish-jobs/{id} / GET plans/{id}/publish-job   │
      ├───────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────┤
      │ Controllers/MakeupExamsController.cs                          │ 同上改造                                         │
      ├───────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────┤
      │ tests/.../TeachingWorkflowRosterTests.cs                      │ 更新测试适配新的异步发布模式                     │
      └───────────────────────────────────────────────────────────────┴──────────────────────────────────────────────────┘
    
      笛卡尔积消除
    
      之前:一个 Include 链拉全部 → EF Core 生成 Sessions × Invigilators × RoomLinks × Seats 笛卡尔积
    
      之后:
      - 场次计数:db.ExamSessions.CountAsync(无 JOIN)
      - 场次摘要:Select new { Id, ClassroomId, InvigilatorCount, RoomLinkCount }(只查所需列)
      - 容量超限:db.ExamRooms.Select(r => new { SeatCount = r.Seats.Count, Capacity })(单表 JOIN)
      - 课程冲突:db.ExamRoomSessions.Where(link => ...CourseId != link.ExamRoom!.CourseId)(独立查询)
      - 每个查询只做自己需要的 JOIN,互不干扰
    
      测试结果
    
      213 通过,0 失败,0 跳过
    
      配置方式
    
      - BackgroundJobs__Transport=RabbitMq → 走 RabbitMQ 队列 jiaowu.background-jobs.exam.publish
      - BackgroundJobs__Transport=InMemory(默认) → 走内存 Channel
      - BackgroundJobs__ExamPublishConcurrency=1(默认,可调 1-16)
  • 1. 教室容量按 1/2 计算
    - ExamArrangementService.cs: SelectRooms() 使用 Capacity / 2 选择房间、计算剩余座位和总容量
      - MakeupExamArrangementService.cs: 数据库查询用 x.Capacity >= enrolledCount * 2(等价于 Capacity/2 >=
      enrolledCount),消息显示有效座位数
    
      2. 教学楼限制多选
    
      - Domain: ExamSession 和 MakeupExamSession 新增 RequiredBuildingIds (JSON string),保留旧 RequiredBuildingId 向后兼容
      - Service: RoomGroupKey 改为字符串键确保值相等;GroupKey() 合并新旧字段
      - Controller: 请求 DTO 增加 RequiredBuildingIds (Guid 数组),响应包含该字段
      - DB: MySQL 迁移 + SQLite migrator 添加新列
      - Frontend: <el-select> 改为 multiple,新增 parseBuildingIds() 解析服务器返回的 JSON
    
      3. 导出签名单后台任务
    
      - 新增: ExamSignInExportJob 实体、ExamSignInExportJobProcessor、ExamSignInExportJobStatus 枚举
      - BackgroundJobKind: 新增 ExamSignInExport = 5
      - RabbitMQ: routing key exam.sign-in-export,队列 jiaowu.background-jobs.exam.sign-in-export
      - API: POST /sign-in-export 创建任务返回 202;GET /sign-in-exports/{jobId} 查询状态;GET
      /sign-in-exports/{jobId}/download 下载文件
      - Frontend: 导出改为异步任务 + 轮询 + 自动下载,显示进度条
      - 恢复: OutboxPublisher 启动时恢复未完成的任务,重试超限自动标记失败
  • 自动编排接口现在立即返回 202 + jobId,不会再等待 15 秒导致 Axios 超时。
    任务参数、状态和结果持久化到 MySQL。
    接入现有 outbox;配置 BackgroundJobs__Transport=RabbitMq 时使用 RabbitMQ 队列 exam.arrangement,否则使用 InMemory worker。
    服务重启后可恢复未完成任务。
    前端显示排队/执行/完成/失败状态,刷新页面可恢复正在执行的任务。
    编排期间禁止修改、删除或发布对应计划。
    补考原有“一键生成”保留,并与编排任务互斥。
    运维后台增加“考试与补考编排”失败任务筛选。
  • 发信改为事务内每 300 人分批写入,避免全校群发一次插入过大;数据库异常现在返回明确的 503,不再只显示模糊 500。
    校级、学院级管理员支持按学院、身份、行政班、教学班、姓名/学号/工号筛选,也可指定最多 500 名收件人。所有结果均由服务端重新校验权限。
    接入 CKEditor 5,支持标题、列表、引用和链接;正文扩展为 MySQL longtext。
    富文本在收件箱、详情弹窗和已发送记录中统一经 DOMPurify 净化后展示。
    页面改用系统现有的冷白、靛蓝、青绿色令牌,新增“选择收件人 → 编辑内容”工作台和移动端适配。
  • 第二阶段优化已完成,重点是提高后台任务吞吐并降低 Outbox 数据库开销。
    RabbitMQ 默认从单一回调通道提升为三类任务独立并行,跨类型并发能力由 1 提升到 3;每类还能独立配置 1–16 个消费者。[RabbitMqBackgroundJobs.cs (line 143)](E:/jiaowu/src/Jiaowu.Api/Infrastructure/BackgroundJobs/RabbitMqBackgroundJobs.cs:143)
    InMemory 开发模式同步改为按任务类型隔离队列,避免某类长任务堵塞其他任务。
    租约恢复检查从“每发布一条执行一次”改为默认每 60 秒维护一次。
    启动恢复改为数据库 NOT EXISTS 查询,不再把全部历史 Outbox 加载进内存。[BackgroundJobOutboxPublisher.cs (line 9)](E:/jiaowu/src/Jiaowu.Api/Infrastructure/BackgroundJobs/BackgroundJobOutboxPublisher.cs:9)
    完成消息默认保留 14 天,之后按每批 500 条清理,并增加对应组合索引。
    /health/messaging 现在返回各状态积压量、过期租约和最老任务等待时间。[BackgroundJobMonitoringService.cs (line 16)](E:/jiaowu/src/Jiaowu.Api/Infrastructure/BackgroundJobs/BackgroundJobMonitoringService.cs:16)
    新增 Jiaowu.BackgroundJobs 运行时指标,覆盖发布量、处理量、发布耗时、处理耗时和清理量。[BackgroundJobTelemetry.cs (line 9)](E:/jiaowu/src/Jiaowu.Api/Infrastructure/BackgroundJobs/BackgroundJobTelemetry.cs:9)
    配置、Compose 和调优建议已更新。[README.md (line 257)](E:/jiaowu/README.md:257)
  • 排课计算、课表发布、补考自动编排三类任务统一接入消息系统。
    业务任务与 Outbox 消息同一事务落库,避免“数据库成功但消息没发出去”。
    RabbitMQ 使用持久化消息、发布确认、手动 ACK、Quorum Queue、死信队列。
    增加处理租约、心跳、异常重试、最大重试次数和幂等状态控制。
    保留 InMemory 模式,开发环境无需安装 RabbitMQ。
    支持多实例竞争消费,后续可以横向扩容。
    新增 /health/messaging 消息系统健康检查。
  • 成绩通知:仅在某门课程成绩正式发布后自动发送,按成绩单学生名单通知,不展示具体分数;发布状态与通知同一次提交。
    手动发信:移除“成绩通知”选项,只能发送普通通知,原有角色与范围权限保持不变。
    选课通知:管理员关闭一轮选课时,自动向每位参与学生发送一条汇总通知,包含最终选中课程及候补未成功课程;候补失效、轮次关闭和通知生成保持原子性。
    关闭选课界面会明确提示发送通知,并显示实际通知人数。
  • 学生可在“学籍管理 → 电子成绩单与证明”申请成绩单或学籍证明。
    服务端根据登录账号绑定本人档案,不接受学生 ID,无法代他人申请。
    成绩单仅包含正式发布成绩;无已发布成绩时明确提示。
    同类型、同用途 5 分钟内重复申请复用已有有效凭证。
    申请后即时生成 PDF,可在本人凭证列表下载、二维码验真。
    管理员的下载记录、失效、重签能力保持不变。
  • 已完善个人教学日历订阅。
    主要能力:
    教师、学生可在“我的课表”启用订阅、复制 URL、唤起日历客户端或下载一次性 ICS。
    自动覆盖固定课程、调停课后的最新安排、考试/监考、补考,以及灵活课程提醒。
    采用独立随机订阅标识和 HMAC 签名;重置或停用后旧地址立即失效。
    支持 ETag 条件请求和一小时刷新提示,减少日历客户端重复同步。
    订阅地址按实际前端 API 地址生成,兼容独立部署域名。
    已增加窄屏弹窗最大宽度和纵向操作布局。
  • 将索引缩短为 IX_CE_Offering_Status_WaitlistedAt。
    将候补迁移改为可重入:先检查列和索引是否存在,再执行 DDL。
    附件中已经成功添加 WaitlistedAt、但索引创建失败的数据库可直接重试,不会报重复列。
    运行时和 dotnet ef 连接统一开启 AllowUserVariables;Connector/NET 默认关闭该选项。Connector/NET 官方说明
    增加所有 MySQL 索引名不得超过 64 字符的回归测试。
  • 已把选课的“年级划分”和限制规则补成完整闭环:
    选课批次可指定多个适用年级;留空兼容原有“全部年级”。
    新增可选的“最多课程门数”,与原有学分上限并行。
    年级、门数限制统一作用于学生可见批次、自主选课、候补、管理员代选和自动递补。
    普通候选名单遵守年级及行政班范围;强制选课可绕过业务限制,但仍严格遵守管理员学院权限。
    管理页面会展示“2026 级 · 最多 6 门 · 最多 30 学分”,名单和候补列表也增加年级列。
    新增 MySQL 迁移:[CourseSelectionGradeLimits.cs](E:/jiaowu/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260726180000_CourseSelectionGradeLimits.cs)。
  • 已完成“选课候补与自动递补”完整流程。
    学生端:满员后可加入/取消候补,展示候补顺位和当前候补人数。
    自动递补:退课释放名额后,按候补时间顺序重新校验学籍、范围、学分及时间冲突;不合格者自动失效并继续下一位。
    管理端:教学班显示候补人数,名单抽屉提供候补队列及移出操作。
    状态通知:递补成功、资格失效、批次关闭、管理员移出均会通知学生。
    并发安全:退课和递补放在 Serializable 事务中执行。
    数据库:已补充 SQLite 开发迁移和 MySQL 正式迁移。
  • 修复500
    TimeOnly ↔ TimeSpan
  • 学年学期页面新增“设为当前”“归档”“撤销归档”,当前学期不能归档、删除或直接取消;已归档学期需先撤销才能重新设为当前。[BaseDataController.cs (line 286)](/E:/jiaowu/src/Jiaowu.Api/Controllers/BaseDataController.cs:286)
    切换后,其他学期自动变为历史态;已归档学期进一步弱化。所有主要学期选择器统一默认当前,并显示“当前 / 历史 / 已归档”。[academicTerms.ts (line 1)](/E:/jiaowu/web/src/utils/academicTerms.ts:1)
    桌面使用分层表格,390px 手机端改为独立学期卡片,操作不会再挤压表格。[BaseDataView.vue (line 211)](/E:/jiaowu/web/src/views/BaseDataView.vue:211)
    归档定义为“历史显示状态”,不是成绩冻结:补考成绩仍可录入并回写,已发布成绩仍可走成绩更正审批。
    已加入 SQLite 开发迁移、MySQL 正式迁移和切换/归档/撤销测试。[20260726143000_AcademicTermArchiving.cs (line 1)](/E:/jiaowu/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260726143000_AcademicTermArchiving.cs:1)
  • 已完成,学生端考勤记录现在按课程展示。
    每门课程会独立显示:
    课程名称、代码、教学班和任课教师。
    课程总体出勤率。
    出勤、迟到、缺勤、请假、免修次数。
    按日期排列的历次点名记录。
    缺勤或迟到记录仍可单独申诉。
  • 全部完成。定时检测系统概览:
    新增组件
    
      ┌────────────────────────────────┬─────────────────────────────────────────────────────────┐
      │              组件              │                          说明                           │
      ├────────────────────────────────┼─────────────────────────────────────────────────────────┤
      │ WarningSchedule 实体           │ 定时配置:学期、星期几、时、分、启用/禁用、上次执行时间 │
      ├────────────────────────────────┼─────────────────────────────────────────────────────────┤
      │ WarningCheckWorker             │ BackgroundService,每分钟轮询一次,匹配到时间就执行检测 │
      ├────────────────────────────────┼─────────────────────────────────────────────────────────┤
      │ GET/PUT /api/warnings/schedule │ 读取/保存定时配置                                       │
      └────────────────────────────────┴─────────────────────────────────────────────────────────┘
    
      页面配置
    
      在学业预警页面顶部新增「定时检测」区域:
      - 启用开关:一键开/关自动检测
      - 星期选择:周一至周日
      - 时分选择:精确到分钟
      - 上次执行时间:自动显示
    
      工作流程
    
      每分钟 Worker 轮询
        → 查询 WarningSchedules 表,匹配当前星期+时+分
        → 检查是否已在本分钟执行过(防止重复)
        → 加载该学期全部启用规则 + 在籍学生
        → 逐条规则、逐个学生检测
        → 已存在的同型预警跳过,新预警写入 + 通知学生/辅导员
        → 更新 LastRunAt
    
      管理员配置"每周一 08:00 自动检测",系统就会在每周一早上 8 点整自动执行一次完整的学业预警检测。
  • 已改成后台“检查并发布”任务,原来的超时主要就是同步逐条校验导致的。
    发布接口立即返回 202 Accepted,页面轮询显示检查进度。
    校验改为批量读取,减少重复数据库查询。
    检查失败保留草稿并显示具体原因;成功后原子完成旧课表归档和新课表发布。
    同一学期禁止两个发布任务并发,服务重启后未完成任务会自动恢复。
    发布期间锁定编辑、自动排课等冲突操作。
    已补齐 SQLite 开发迁移及 MySQL 生产迁移。
  • 主要改动:
    自动排课接口立即返回 202 + jobId
    后台服务独立执行,不受浏览器关闭或前端超时影响
    进度、成功、失败状态持久化到数据库
    服务重启后自动恢复排队中或中断的任务
    同一草稿禁止重复提交后台任务
    运行期间禁止编辑、发布或删除该课表
    排课结果和任务成功状态在同一事务中提交
    前端每秒轮询进度,显示已处理教学班和已规划安排数
  • 自动排课:保留手工安排,按教师、行政班、周次、节次、容量和场地约束补排。
    课程约束:可限定校区、教学楼、指定教室、允许上课日、最早/最晚节次。
    无教室课程:教室可为空,但仍校验教师和班级冲突。
    作息维护:按学期维护节次、上下课时间和启用状态。
    发布保护:发布前重新检查全部约束,并阻止学时未排满的课表发布。
    工作台升级:增加“排课规则与作息”“自动排课”入口,自动生成后仍支持手工微调。
    同时兼容 SQLite 和 MySQL,已生成正式迁移。
  • 课程库 Excel 导入导出已完成:
    支持下载标准导入模板。
    导出严格沿用当前关键词、学院、分类、课程性质和状态筛选。
    以课程编码为唯一键:存在则更新,不存在则新增。
    校验学院编码、课程分类、课程性质、学分学时及考核方式。
    整批原子导入,任一行错误则全部不写入,并提示具体行号。
    学院管理员仍只能维护本学院的专业课和实践课,无法借 Excel 越权。
    界面入口沿用现有课程库工具栏样式。
  • 学籍异动:休学、复学、退学申请及辅导员→学院→教务处分级审批。
    毕业审核:批次计算、缺失课程检查、人工复核、结果发布。
    学位授予:毕业资格与 GPA 计算、人工调整、发布授予结果。
    毕业离校:离校事项配置、责任角色分工、逐项办理及批次关闭。
  • 已继续完成“学籍异动”后端基础闭环:
    学生可申请休学、复学或退学。
    自动根据当前状态推导目标学籍状态。
    同一学生不能同时提交多项在审申请。
    强制执行辅导员 → 学院 → 校级三级审核。
    驳回不会修改学生档案。
    只有校级最终批准才更新学籍状态。
    数据范围分别限制到本人、所带班级、所属学院或全校。
    SQLite 增量升级和 MySQL 迁移已生成。
    当前 28 项测试继续全部通过。
  • 已完成“考试安排与考场管理”模块:
    考试计划草稿、场次配置与正式发布。
    教学班、考试时间、考场、监考教师关联。
    自动校验考场容量。
    阻止考场、监考教师和学生考试时间冲突。
    管理员查看考生名单。
    教师查看个人监考安排。
    学生查看已发布考试日程。
    SQLite 增量升级和 MySQL 正式迁移。
  • 已完成“成绩管理与学业档案”模块:
    教师批量录入平时、期中、期末成绩。
    自动计算总评、绩点和及格状态。
    支持缺考、缓考、免修。
    教师提交 → 学院审核/退回 → 校级发布。
    学生只能查看正式发布成绩。
    自动统计平均分、完成度、及格率和加权 GPA。
    SQLite 本地增量升级和 MySQL 正式迁移均已完成。
    分级权限在前后端同时校验。
  • 选课批次:时间窗口、退课截止、学分上限、开放/关闭。
    教学班投放:容量、班级范围、全校开放。
    学生选退课:容量、重复课程、学分上限、课表冲突校验。
    教学班实时名单。
    校级、学院级、学生角色分级操作。
    SQLite 本地增量升级及演示数据。
    MySQL 正式 EF Core 迁移。
    Vue 已编译进 wwwroot,单服务运行无需 npm run dev。
    学生端和管理端均完成桌面、手机响应式检查。
  • 多角色账号按 All > College > Class > Self 自动取最高权限。
    学院管理员限制在所属学院。
    辅导员通过稳定账号 ID 绑定行政班,避免重名串班。
    教师只能访问本人档案、授课课程和所授课学生。
    学生只能访问本人档案及所在班级课程。
    教师/学生角色会自动校验并绑定工号或学号档案。
    超级管理员可在用户页面调整角色、学院、工号/学号,并预览生效后的数据范围。
  • 排课版本新建、编辑、复制、发布、归档和删除。
    周课表视图,支持12节课、单双周和起止周。
    自动校验教师、行政班、教室、时间段冲突。
    校验教室容量、教学任务状态和学期范围。
    SQLite 开发环境自动升级,MySQL 提供正式 EF Core 迁移。
    Vue 已编译为静态文件并随 ASP.NET Core 发布,无需 npm run dev。
    桌面端和390px移动端页面均已验收。
  • 培养方案:专业/年级版本、课程模块、学分校验、复制版本、发布锁定、旧版本归档。
    教学任务:学期开课、授课教师、唯一主讲、合班、容量与周次校验、发布及结课。
    SQLite 开发库已自动升级,无需删除原数据库。
    新增对应 MySQL EF Core 迁移。
    Vue 已编译进 wwwroot,仍可只运行 ASP.NET Core 单服务。
    桌面端及 390px 窄屏页面验证通过,无前端控制台错误。
  • 3
  • 1