Files
competition-website/app/model/adminAuditLog.js
qubiaobiao 459f1adf6d 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)
2026-10-20 11:06:29 +08:00

73 lines
2.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict';
/**
* 管理员高危操作审计日志 - AdminAuditLog
*
* 只记录**高危写操作**(不是全量写操作):
* 发布/撤回成绩、重置测试数据、删除选手、撤回交卷、
* 管理员账号的增 / 改权限 / 重置口令 / 禁用 / 删除。
*
* 为什么不做全量审计:本项目日志无集中采集,全量写接口约 30 个,
* 日志量增长快且缺少清理策略;而「多管理员下谁改了成绩 / 谁动了账号」
* 才是真正需要回答的问题。故按「能改变比赛结果或权限边界的动作」划定范围。
*
* ⚠️ 写入必须 best-effort:审计失败不得阻断业务(否则日志表故障会连累发布成绩)。
* 统一走 service.admin.recordAudit(),⛔ 不要在控制器里直接 create。
*/
module.exports = app => {
const { STRING, INTEGER, DATE, TEXT, BIGINT } = app.Sequelize;
const AdminAuditLog = app.model.define('adminAuditLog', {
id: {
type: BIGINT,
primaryKey: true,
autoIncrement: true,
comment: '日志唯一标识',
},
admin_id: {
type: INTEGER,
allowNull: false,
comment: '操作人管理员 ID',
},
// 冗余存用户名:账号被删除后仍能回答「当时是谁操作的」
admin_username: {
type: STRING(50),
allowNull: false,
comment: '操作人用户名(冗余,防账号删除后不可追溯)',
},
action: {
type: STRING(64),
allowNull: false,
comment: '动作标识,如 score.publish / admin.create',
},
target: {
type: STRING(200),
allowNull: true,
comment: '操作对象(组别 / 账号名 / 选手 ID)',
},
detail: {
type: TEXT,
allowNull: true,
comment: '补充信息(JSON 文本)',
},
ip: {
type: STRING(64),
allowNull: true,
comment: '来源 IP',
},
created_at: {
type: DATE,
allowNull: false,
defaultValue: app.Sequelize.literal('CURRENT_TIMESTAMP'),
comment: '发生时间',
},
}, {
tableName: 'admin_audit_logs',
timestamps: false,
underscored: true,
});
return AdminAuditLog;
};