fix(auth): 当前身份优先按 roleName 判定,免疫角色号配置漂移
build / build-push (push) Successful in 51s

- matchCurrent 改为「先 roleName、再 roleId」:平台下发的 roleName(老师/校领导/学生/
  家长/局领导/工作号/管理员)是平台权威标签,先认名称可避免部署环境里 agent.roles.*
  被旧角色号覆盖时把身份判错(现象:老师账号显示成「学生」)
- ADMIN_WORDS 去掉「主任」(与「班主任」冲突会误判为管理员),补「教导」「职工」等
- 构造时 INFO 打印本次生效的角色配置,配置漂移一眼可见
- 测试 48/48 通过
This commit is contained in:
lijing
2026-10-12 16:41:09 +08:00
parent 67e55f1875
commit 61a3000bee
@@ -36,24 +36,32 @@ import java.util.concurrent.ConcurrentHashMap;
*
* <h3>判定顺序</h3>
* <ol>
* <li><b>当前身份</b>:先按 {@code roleId} 命中 {@code agent.roles.*};
* 角色号没配到,再按 {@code roleName} 关键词兜底(老师/教师/班主任 → 教师,
* 管理员/校长/主任/教务… → 管理员,家长 → 家长,学生 → 学生);命中即<b>只</b>返回该身份;</li>
* <li><b>当前身份</b>:<b>先按 {@code roleName} 关键词判定</b>(老师/教师/班主任 → 教师,
* 管理员/校长/局领导/教务… → 管理员,家长 → 家长,学生 → 学生);
* 名称看不出身份的(如「工作号」)再按 {@code roleId} 命中 {@code agent.roles.*};
* 命中即<b>只</b>返回该身份;</li>
* <li>当前身份是管理员时,按 {@code admin-also-teacher=true} 追加教师能力;</li>
* <li>无当前身份(或当前身份识别不出)→ 回退按 {@code roleIds} 逐个匹配配置;</li>
* <li>仍然什么都没识别出 → 按 {@code BoaUser.type} 兜底(0=学生、2=教职工、1=超管);</li>
* <li>什么都识别不出 → 访客(仅公共只读能力)。</li>
* </ol>
*
* <p>★ 为什么 {@code roleName} 优先:它是平台下发、与门户顶栏一致的权威标签,
* 先认名称可避免部署环境里 {@code agent.roles.*} 配置漂移(被旧角色号覆盖)把身份判错。
*/
@Slf4j
@Component
public class RoleResolver {
/** 角色名关键词:命中即判定(角色号未配置时的兜底,跨学校更稳) */
/** 角色名关键词:命中即判定(角色号未配置时的兜底,跨学校更稳)
*
* <p>★ 不含「主任」:它与「班主任」冲突,会把普通教师误判成管理员。
* 教务主任/教导主任等仍可经「教务」「教导」… 命中,或由角色号配置兜底。
*/
private static final String[] ADMIN_WORDS = {
"管理", "校长", "园长", "局长", "领导", "主任", "教务", "督导", "书记", "党委"
"管理", "校长", "园长", "局长", "领导", "教务", "教导", "督导", "书记", "党委"
};
private static final String[] TEACHER_WORDS = {"教师", "老师", "班主任", "教工"};
private static final String[] TEACHER_WORDS = {"教师", "老师", "班主任", "教工", "职工"};
private final AgentSettings settings;
@@ -62,6 +70,13 @@ public class RoleResolver {
public RoleResolver(AgentSettings settings) {
this.settings = settings;
// ★ 启动时把「本次真正生效」的角色配置打出来:一旦部署环境存在配置漂移
// (外部 application-prod.yml / 环境变量覆盖了 agent.roles),据此一眼可见。
AgentSettings.Roles c = settings.getRoles();
log.info("[角色配置] student={} teacher={} parent={} admin={} | type: student={} teacher={} admin={} parent={} | admin-also-teacher={}",
c.getStudent(), c.getTeacher(), c.getParent(), c.getAdmin(),
c.getStudentUserTypes(), c.getTeacherUserTypes(), c.getAdminUserTypes(), c.getParentUserTypes(),
c.isAdminAlsoTeacher());
}
/** 解析用户角色集合(永不为空,至少含 GUEST) */
@@ -131,13 +146,23 @@ public class RoleResolver {
return sb.toString();
}
/** 当前身份 → 智能体角色:角色号配置优先,角色名关键词兜底 */
/**
* 当前身份 → 智能体角色。
*
* <p>★ <b>先按 {@code roleName} 判定,再退回 {@code roleId}</b>:
* 平台下发的 {@code roleName}(老师 / 校领导 / 学生 / 家长 / 局领导 / 工作号 / 管理员)
* 是<b>平台自己的权威标签</b>,与用户在门户里看到的一致;先认它,
* 可以免疫 {@code agent.roles.*} 配置漂移(部署环境里被旧的角色号配置覆盖)导致的身份误判。
* 名称里看不出身份的(如「工作号」)再按角色号 {@code roleId} 映射。
*/
private Set<AgentRole> matchCurrent(BoaRoleInfo current, AgentSettings.Roles cfg) {
Set<AgentRole> hit = match(current.getRoleId(), cfg);
if (hit.isEmpty() && StringUtils.isNotBlank(current.getRoleName())) {
hit = matchByRoleName(current.getRoleName().trim());
if (StringUtils.isNotBlank(current.getRoleName())) {
Set<AgentRole> byName = matchByRoleName(current.getRoleName().trim());
if (!byName.isEmpty()) {
return byName;
}
}
return hit;
return match(current.getRoleId(), cfg);
}
/** 单个角色号 → 智能体角色(一个号理论上可命中多个分组,全部保留) */