feat(admin): R176-R180 公示表与新闻撤回 + 管理端多管理员权限(C 层)+ 交卷冻结

- R176 成绩公示表引擎(lib/publicitySheet.js + lib/assets/publicity-template.xlsx 官方模板基底)
  与指导教师回填(lib/teacherBackfill.js);赛项名称/编号改为配置项
- R177 新闻「发布 ↔ 撤回」互斥:撤回只置 is_draft=1,不删除数据
- R178 C 层 RBAC:lib/adminPermissions.js(13 权限点 / 70 条路由规则,唯一 SoT)
  + middleware/auth.js 三段判定(默认拒绝 · is_super 单点 · token_version)
  + controller/service/adminUser.js + lib/auditLog.js + model/adminAuditLog.js
- R178 B1 交卷冻结:service.exam.assertNotSubmitted(bizCode 4006 单一判据源)
- R178 B④ 提交口径拆分:formalSubmitted / submitted / answeredCount 三字段
- R178 成绩总览补小组编号:resolveTeamLabels(与选手管理同源,含兜底与三态)
- R181 生产迁移配套:admins +7 列、admin_audit_logs(scripts/migrate_r178_admin_rbac.py)
This commit is contained in:
2026-10-20 11:06:29 +08:00
parent 5392345078
commit 459f1adf6d
21 changed files with 2886 additions and 76 deletions

View File

@@ -74,6 +74,16 @@ module.exports = app => {
word_score: decimalScore('word_score', 'Word题/综合大题得分(可含 2 位小数)'),
// PPT分数(本科/教师组管理员手动打分)
ppt_score: decimalScore('ppt_score', 'PPT得分(本科/教师组,可含 2 位小数)'),
// 指导教师姓名(R176)。来源 = 报名表里 identity 为「指导教师」的行,
// 按 (group, 队伍编号) 归到同组选手身上;同组多人用「、」连接后冗余存一份。
// ⚠️ 语义上是**组级属性**,冗余到每位组员是为了让成绩公示表能直接按行取用,
// 不必每次再回查 aggregation。回填入口见 service/admin.js#backfillTeachers。
teacher_name: {
type: STRING(200),
allowNull: true,
defaultValue: null,
comment: '指导教师姓名(同组多人用、分隔)',
},
// 所属团队 ID(外键关联 teams 表)
team_id: {
type: INTEGER,