feat(role,scope)!: 角色与授权可配 + 需求收敛(4 角色 / 10 菜单 / 删两模块)

角色与授权(交接摘要 §一·三十五):
- 权限判定合并两个来源:内置角色按代码(BuiltinRole)、自定义角色按库
  (sys_role_permission)。实现放 module 层的 DbRoleGrantSource,security 只依赖
  接口 RoleGrantSource —— 否则角色服务要调 permissionChecker,两个包迟早成环
- 权限判定必须加缓存:此前是零 IO,而压测基线是"SQL 条数恒定"。缓存放 Redis,
  空结果也缓存(否则已删角色的 token 会把库打穿)、Redis 挂了退化成直读库、TTL 只兜底
- V18 建 sys_role.user_type(账号的 user_type 一直从角色反推,自定义角色不在代码映射里)
  与 sys_role_menu;8 个接口 + 错误码 40045;契约 119 -> 129
- 内置角色只读(权限码是前后端契约,做成可改 = 一次误操作就能把管理员锁在系统外);
  平台级能力两道防线(界面禁用 + 服务端 40301);防自锁;改完立即生效(写授权清缓存)
- RoleProvisioner 从"已存在就 continue"改成幂等回填 + 每日 03:20 定时任务 ——
  修的是"代码改了权限、已开通租户永远停在发放那一刻",这种漂移不报错也没测试会红
- scripts/e2e-m10-role.py 77/77

需求收敛(交接摘要 §一·三十六,本节的数字为准):
- 删副班主任 / 审核员两个内置角色(6 -> 4)、历史整理器(module/cleanup)、
  加入申请(module/join + 学生端 pages/join)、collect_task_org.deputy_user_ids 整列;
  审核范围三级回退 -> 两级,连带 6 个 mapper 的 LIKE '%,id,%' 分支
- V19 迁移处理存量:先解 sys_user_role 绑定再软删角色;账号只在"除这两个角色外没有
  别的角色"时才软删。必须迁而不能只删代码 —— 角色码删掉后 mergePermissions 返回空集,
  存量账号会"能登录、处处 40301",不抛异常不打日志
- 契约 129 -> 119、错误码 59 -> 54、权限码 30 -> 28、PC 菜单 12 -> 10、
  小程序页 10 -> 9、原型 32 -> 29 屏、E2E 39 -> 37 个(断言 1695 条)
- UserService.list 对不认识的角色码由"静默丢掉过滤条件"改成失败关闭
  (之前传个不存在的 roleCode 会返回全校账号,且看起来完全正常)
- 品牌元信息同步改名「深学在线」:pom 名称、/doc.html 标题、配置与启动类注释、
  存储自检探针内容;学生端 app 名与全局导航栏标题
This commit is contained in:
2026-10-10 16:48:54 +08:00
parent 5b877451eb
commit 1f3c485ceb
84 changed files with 2845 additions and 4904 deletions
+6 -25
View File
@@ -33,18 +33,13 @@
<view v-if="error" class="verify__error">{{ error }}</view>
<!--
核验失败时给一条**受控通道**(FR-509 / 客户确认后新增)。
注意两件事:
1) 这条通道**不绕过门禁** —— 申请期间仍然不能填,去申请页会说清这一点;
2) 它只在真失败过之后才出现(`error` 非空),不是一进来就摆个"申请"按钮。
否则会有学生绕过核验直接去申请,把管理员的待办当成人工客服。
核验失败时**只给"重新填写"这一条路**(2026-10 需求收敛:删掉「申请加入名单」)。
刻意不提任何联系方式:名单外的人怎么进名单是老师那边的事,
系统内不展示班主任/管理员的电话(客户定过的规矩)。
-->
<button v-if="error" class="verify__btn verify__btn--ghost" @click="gotoJoin">
申请加入名单
</button>
<view class="verify__foot">
核对不上?先确认姓名和证件号没有打错。仍然不行可点上面的「申请加入名单」。
<view v-if="error" class="verify__foot">
核对不上?请先确认姓名和身份证号没有打错。
如果确实已经报名,请直接联系老师,让老师把你加进名单后再来核对。
</view>
</view>
@@ -225,20 +220,6 @@ function next() {
})
}
/** 去申请加入名单(FR-509 的入口)—— 带上已填的姓名与证件号,省他再输一遍。 */
function gotoJoin() {
const s = sceneFromUrl()
const params = new URLSearchParams({
scene: s,
taskId: String(ctx.value.taskId || 0),
// 只把**已经打过的字**带过去。这不是"预填答案":门禁刚判定它不匹配,
// 带过去是为了让他少打一遍,而不是暗示它是对的。
name: realName.value.trim(),
idCardNo: idCardNo.value.trim()
})
uni.navigateTo({ url: `/pages/join/join?${params.toString()}` })
}
onLoad((options) => {
loadContext(options?.scene || '')
})