推荐

`member_id` 指向的是 `auth_subject.id`

y yasepix · 2026-09-19 21:25 · 21 浏览
你问到点子上了,而且这里有个**关键细节需要先纠正**:`member_id` 指向的是 `auth_subject.id`,不永远是 `sys_user.id`。让我先把"它到底怎么落到用户"的代码读准,再讲 `member_type` / `is_direct` 的用途——尤其 `is_direct` 大概率是个"冗余闭包"设计,我得确认下代码里谁在维护它。 ServiceImpl 是空的(只是个 CRUD 壳)。那 `is_direct` 的"冗余闭包"逻辑应该在别处维护。让我全局搜一下 `isDirect` 和 `setIsDirect` 的写入点。 已经定位到关键信息了。先纠正你理解里的一个**精度问题**,再讲 `member_type` / `is_direct` 各自的用途——`is_direct` 的注释 "0继承冗余" 已经说明它是个"闭包冗余"设计。我再看一眼种子数据,给你一个真实例子证明 `member_type` 不总是 1。 读完种子数据,可以给你一个**带实证的精确回答**了。 ## 先纠正一个精度问题 你的理解**只对了一半**。`member_id` 指向的是 **`auth_subject.id`,不是 `sys_user.id`**。它只在这个成员是"用户类型"时才最终落到用户表。完整的落地链是: ``` auth_subject_member.member_id → auth_subject.id ├─ 若 subject_type = 1(用户) → auth_subject.subject_id = sys_user.id ✅ 终点是用户 └─ 若 subject_type = 2/3/6/8(科室/组/角色/医疗组) → member_id 指向另一个"组"主体,继续向下展开 ``` 所以准确说法是:**只有叶子节点 `member_type=1` 时,它背后的 `auth_subject.subject_id` 才是 `sys_user.id`;中间层的 `member_type` 可能是 3/6/8,指向的是"组/角色/医疗组"这种非用户主体。** ## 实证:种子数据里就有 `member_type≠1` 的行 `seed_demo.sql` 第 481 行: ```sql -- group_subject_id=...4028 (演示角色-含安全组, 角色) member_type=3 member_id=...4017 (9000000000005011, ..., 9000000000001003, 9000000000004028, 3, 9000000000004017, ...); ``` 而 `...4017` 本身又是一个"安全组"主体,它的成员是 `member_type=1` 的用户(第 471–473 行)。所以真实链条是: **角色 R5 → 含安全组(4017) → 含用户(4001/4002/4003) → sys_user** 这正好证明:`member_id` 不永远连 `sys_user`,中间层是"组套组"的递归嵌套。 ## `member_type` 有什么用 它是**被引用主体的类型**的冗余副本(跟 `auth_subject.subject_type` 镜像),本表里所有 `member_type` 都和它所指向的 subject 的类型一致。冗余出来的原因: 1. **查询/展示免 join**:成员列表要显示"成员类型:用户/科室/角色/组…",直接读这一列即可,不用每次 JOIN `auth_subject`。 2. **约束"谁能被加为成员"**:配合 `auth_subject_type.member_allowed`——机构(5)、岗位(7) 不允许作为成员,只有 user/dept/group/campus/role/medical_group 能进组。这一列就是校验闸门。 ## `is_direct` 有什么用(关键:它是"闭包冗余"标记) 注释写得很明确:`1直接 0继承冗余`。这是一个**闭包表(closure table)式的冗余设计**: - `is_direct=1`:**你手动显式加的边**("我把张三直接加进这个安全组")。 - `is_direct=0`:**继承来的冗余记录**("张三是因为属于子组 A,而 A 属于本组,所以被冗余记一笔")。 它的价值: - **查询区分**:"只看直接成员" vs "看全部生效成员(含继承下来的)"——一条 `WHERE is_direct=1` 就能切。 - **删除语义清晰**:删一个直接边即可,继承边不需要、也不应该手工维护。 - **UI 标注**:前端 `SelectDict` 里直接显示"直接 / 继承冗余",让用户知道某人是"直接加进来的"还是"跟着子组带进来的"。 ⚠️ **一个现状提醒**:当前 `seed_demo` 里 23 行 `auth_subject_member` 的 `is_direct` **全部是 1**(没有 0 的继承冗余行),而且后端 `AuthSubjectMemberServiceImpl` 是空的 CRUD 壳,**没有自动计算并写入 `is_direct=0` 闭包的代码**。也就是说:这个字段的语义和 UI/筛选都预留好了,但"自动展开闭包并落冗余行"这一步目前要么没实现、要么靠外部脚本/触发器维护。真正做成员递归展开的地方是 `SnapshotBuilder.resolveToUsers`(构建期实时递归,不依赖 `is_direct`)。 ## 顺带其它几个列(你可能也会问) | 列 | 作用 | |----|------| | `is_default` | 是否默认成员(种子里管理员指派行是 1,demo 行是 0) | | `effective_time` / `expire_time` | **成员级时间窗**:某人临时借调进组 3 个月,到期自动失效 | | `status` | 0停用 1启用(单条成员关系可单独停用) | | `org_id` | 机构隔离,多院区/多机构共用模型 | **一句话结论**:`member_id` 不是直接连 `sys_user`,而是连 `auth_subject`;`member_type` 是成员类型的冗余副本(用于免 join 展示 + 成员资格约束),`is_direct` 是用来区分"我亲手加的"和"跟着子组继承来的"的闭包冗余标记。只有在成员树叶子(`member_type=1`)处,才真正关联到 `sys_user.id`。
↗ 分享

全部回复(0)

登录 后发表评论
还没有回复,来说两句~
相关推荐