企业身份与归属模型
平台上同时存在两套用户体系:平台运营,和客户企业。 企业是实体(可以独立存在),Agent 是业务单元(服务于某家企业,也可以不属于任何企业)。 这两件事分开回答,是这一页的全部内容。
四类主体
平台运营(system.user/role/dept) 客户侧(user_client)
│ │
│ 管平台参数 ├─ 企业组织 organization(实体)
│ │ ├─ ownerUserIds(管理员名单)
▼ │ ├─ reviewStatus(审核状态)
/sys/* 后台接口 │ └─ approval-policy(版本化策略)
│
└─ Agent(业务单元)
├─ ownerId(主所有者)
├─ ownerIds(共同维护人)
└─ organizationId(归属企业,可选)| 主体 | 存在哪 | 回答什么问题 |
|---|---|---|
| 平台运营 | system 的 user / role / dept(MySQL) | 谁能改平台参数、看全局 |
| 终端用户 | user_client(MySQL) | 谁在平台上注册、用服务 |
| 企业组织 | /master/organization/{orgId}(主数据,版本化) | 这是哪家企业、谁在管它、审核到哪一步 |
| 业务单元(Agent) | agent-profiles(MongoDB) | 这个 Agent 提供什么能力、属于谁、服务哪家企业 |
双轨身份:两套判定,绝不混用
| 平台运营 | 客户企业 | |
|---|---|---|
| 用户表 | system 的 user / role / dept | user_client(没有角色字段) |
| 判定依据 | @RequiresRoles(['admin','system']) + 角色守卫 | 组织管理员名单 ∪ Agent owner |
| 管什么 | 平台参数、全局运营、排障 | 自己的企业、自己的 Agent |
不能用平台角色管客户:那会出现「平台运营能改客户审批链」或「客户 owner 需要平台发角色」的双重错位。 也不能用客户身份管平台:客户侧没有角色表,只有 owner 关系。
客户侧判定:两条来源取并集
能管这家企业 ⇔ userId ∈ organization.ownerUserIds ① 组织自己的管理员名单
∪ userId 是「归属该企业的某个 Agent」的
ownerId / ownerIds ② 业务单元的维护人- ① 建组织的人自动进入
ownerUserIds—— 所以没有 Agent 也能建企业并管理它 - ② Agent 挂进企业后,它的
ownerId/ownerIds(共同维护人)同样算这家企业的管理员 - 两条都不命中 ⇒ 拒绝;
platform/system视同所有者(排障与系统任务);外部 API 客户端拒绝 - fail closed:空
userId、查不到记录、脏数据(ownerId: ''/ownerIds: [''])一律不放行
三层归属:每个数据只回答一个问题
| 层 | 数据 | 归属字段 | 性质 |
|---|---|---|---|
| 组织 | /master/organization/{orgId} | ownerUserIds、reviewStatus | 主数据(唯一 + 版本留痕) |
| 业务单元 | agent-profiles.organizationId | 指向组织;可选 | 业务归属,不填即与任何企业无关 |
| 运行时事实 | /orders/{orderId}/approvals/{orgId} | /master/approval-policy/{orgId} | 事件(仅追加)+ 版本化配置 |
规则:Agent 归属企业是可选的。未归属的 Agent 仍能上架、接单、被派单,只是不会出现在 /organization/mine 与企业管理页里。这正是「平台预置 + 用户认领」与「用户自建」两条 Agent 来源 都不受企业约束的原因。
组织生命周期
① 创建 任何登录用户都能建(不要求先有 Agent)
└─ 产出 reviewStatus = 'pending'(未审核;审核流程后期做)
ownerUserIds = [创建者]
② 接入 Agent 可选。两种顺序都成立:
· 建组织 → 之后在「我的 Agent」里选归属企业
· 先有 Agent → 建组织时顺手填 agentId(校验该 Agent 属于调用者)
③ 配审批链 /master/approval-policy/{orgId}
每次实质变更追加一版(actor + at + note + diff),决策可反查「按哪版批的」
④ 跑决策 /orders/{orderId}/approvals/{orgId}
结果带 rule_version(策略内容版本)与 policy_version(库里的版本号)为什么这样设计
为什么企业不挂在 Agent 上
因为「谁是这家企业的管理员」和「这个 Agent 服务哪家企业」是两个问题。 早期把前者也交给 Agent owner 回答,代价是:一个人没有 Agent 就建不了企业。 现在的取舍是承认两条来源,允许它们出现分歧(Agent 转走、组织管理员仍在)—— 这个分歧是刻意的,也是现实里企业该有的样子。
为什么企业创建先开放、审核只留字段
企业资质审核需要真实运营流程(谁批、批什么材料、驳回后能不能重提)。 先把状态字段和读取入口做出来(reviewStatus),审核动作留到有运营能力时再加, 避免为了一个没有执行者的流程挡住所有企业注册。
为什么组织与策略都放主数据域
它们都是结构(唯一、低频写、高频读),不是事实(事件流)。 主数据域已经解决了「唯一约束 + 版本历史 + 乐观并发」,再写一套只会让审计出现两个口径。 运行时的审批决策才是事实,落 state 事件。
与既有架构的关系
| 既有能力 | 与本模型的关系 |
|---|---|
主数据域(master-data) | 组织与审批策略的存储与版本化都复用它 |
状态层(state) | 审批决策是仅追加事件,带 actor / 因果链 |
一致性阶梯 L4(multi-org-approval) | 审批链的判定逻辑仍是一份纯函数 + 配置;企业数据只是配置的来源 |
门面 Agent 双面隔离(resolveAudience) | 客户侧身份判定的纯函数收口,本页的「来源 ②」直接复用 |
边界(当前明确不做)
- 多准则并行 / 组织层级合并:只做维度隔离(谁能看到哪条记录)
- 币种换算、重估、汇兑损益:只做记账与显示精度
- 审批链的代理人、加签、会签:只做金额阈值 + 角色顺序
- 企业资质审核流程:只有
reviewStatus字段与展示,没有审批动作 - 组织成员的两级角色(管理员 / 普通成员):当前只有「是不是管理员」