Files
competition-website/app/model/admin.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

102 lines
2.9 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';
/**
* 管理员模型 - Admin
*
* 存储后台管理员账号信息,管理员通过用户名和密码进行登录。
*/
module.exports = app => {
const { STRING, INTEGER, DATE, TEXT, TINYINT } = app.Sequelize;
const Admin = app.model.define('admin', {
// 主键 ID,自增
id: {
type: INTEGER,
primaryKey: true,
autoIncrement: true,
comment: '管理员唯一标识',
},
// 管理员用户名,唯一
username: {
type: STRING(50),
allowNull: false,
unique: true,
comment: '管理员用户名',
},
// 管理员密码(明文存储,生产环境应使用 bcrypt 加密)
password: {
type: STRING(100),
allowNull: false,
comment: '管理员密码',
},
// 🔴 R178 最高管理员标记:唯一、不可改权限 / 不可禁用 / 不可删除 / 不可重置口令。
// 判据只认本字段(不认 username,避免改名绕过;也不认 id,避免误伤后续自增行)。
is_super: {
type: TINYINT,
allowNull: false,
defaultValue: 0,
comment: '最高管理员:1=不可变动(唯一)',
},
// 权限点 JSON 数组(见 lib/adminPermissions.js 的 PERMISSIONS)。
// NULL / 空数组 = 无任何业务权限;is_super=1 的账号忽略本列(恒全权)。
permissions: {
type: TEXT,
allowNull: true,
comment: '权限点 JSON 数组',
},
// 1=启用 0=禁用。禁用后:禁止登录,且已签发的令牌在下一次请求即被拒。
status: {
type: TINYINT,
allowNull: false,
defaultValue: 1,
comment: '1=启用 0=禁用',
},
// 令牌版本:改密 / 禁用 / 改权限时自增 ⇒ 旧 JWT 中的 tv 与之不匹配即失效。
// 这是「改密后旧会话立即下线」的唯一判据(本项目无会话表)。
token_version: {
type: INTEGER,
allowNull: false,
defaultValue: 0,
comment: '令牌版本(改密/禁用时自增,使旧 JWT 失效)',
},
last_login_at: {
type: DATE,
allowNull: true,
comment: '最近登录时间',
},
last_login_ip: {
type: STRING(64),
allowNull: true,
comment: '最近登录 IP',
},
remark: {
type: STRING(200),
allowNull: true,
comment: '备注(姓名 / 岗位)',
},
// 记录创建时间
created_at: {
type: DATE,
allowNull: false,
defaultValue: app.Sequelize.literal('CURRENT_TIMESTAMP'),
comment: '创建时间',
},
// 记录更新时间
updated_at: {
type: DATE,
allowNull: false,
defaultValue: app.Sequelize.literal('CURRENT_TIMESTAMP'),
comment: '更新时间',
},
}, {
// 表名
tableName: 'admins',
// 时间戳字段
timestamps: false,
// 下划线命名
underscored: true,
});
return Admin;
};