Archived
47 lines
3.6 KiB
Markdown
47 lines
3.6 KiB
Markdown
# module: org
|
|
|
|
职责: 员工与岗位(ADR-012):验签后的人是谁(名册即时更新)、他能进哪几个端(角色→端)、
|
|
岗位指派与在岗人员查询(负责人四策略解析的候选集来源)、名册同步(Admin API)、
|
|
在途工作(只读)、工作组(ADR-011)、种子(岗位 + 缺省角色→端)
|
|
不负责: 验签之外的 HTTP 语义(401/403 由 app 层中间件按路由归属判)、
|
|
负责人解析算法本身(见 process)、任务与卡的在途判定(经 InFlightWorkProbe 由 app 注入)
|
|
|
|
公开出口: index.ts
|
|
- 类型:`OrgService` `StaffWithPosition` `Identity` 与 @contracts 的 `Staff` / `Position` / `Principal` /
|
|
`RoleAccess` / `RosterSyncOutcome` / `InFlightWork` / `Surface` / `WorkGroup` / `WorkGroupBinding` 转出
|
|
- 错误:`TokenRejectedError`(→401)`AccountDisabledError`(→403)`AccessLockoutError`(→409)
|
|
`StaffNotFoundError` `PositionNotFoundError`
|
|
- 端口类型:`TokenVerifier` `VerifiedClaims` `RosterSource` `RosterUser` `IdentityPatch`
|
|
`PositionRepository` `StaffRepository` `StaffRecord` `RoleAccessRepository`
|
|
`WorkGroupRepository` `InFlightWorkProbe` `Clock` `IdGenerator`
|
|
- 用例工厂:`createOrgService(deps: OrgServiceDeps): OrgService`
|
|
- 纯函数:`identityOf` `isNoiseRole` `normalizeRoles` `endsOf` `assertNotLockedOut` `normalizeEnds` `buildInFlightWork`
|
|
- 常量:`POSITION_SEED` `NONE_POSITION_CODE` `TEST_POSITION_CODE` `DEFAULT_ROLE_ACCESS`
|
|
- adapter 工厂(供 app 装配):`createKeycloakTokenVerifier`(jose,配置型 `KeycloakVerifierOptions`)
|
|
`createKeycloakRoster`(Admin API,配置型 `KeycloakRosterOptions`)
|
|
`createPositionRepository` `createStaffRepository` `createRoleAccessRepository` `createWorkGroupRepository`
|
|
- 错误译文(供 app 拼 401 message):`explainJoseError`(jose 错误 → 人话,手册 4.4)
|
|
- Drizzle 表:`positionTable` `staffTable` `roleAccessTable` `workGroupTable`
|
|
|
|
层级: L1(编码规范 §4)
|
|
依赖: @contracts;@shared/db(Drizzle 句柄与 sys schema);jose(仅 adapter)
|
|
port:`InFlightWorkProbe`(app 注入 task / human-tool 的只读用例)、`IdGenerator`、
|
|
`TokenVerifier`(app 给 jose 远程 JWKS;测试给本地密钥对)、`RosterSource`(可缺省)
|
|
被依赖: process(负责人解析的候选集)、message(治理台收件人 = staffWithEnd('ops'))、
|
|
app(鉴权中间件与 /api/gov/staff、/api/gov/access 路由组装)
|
|
|
|
不变量:
|
|
- `Staff.id` 就是 Keycloak `sub`;`account` 只作显示,永不当键
|
|
- `state` 只由名册同步置 `disabled`;`authenticate` / `principalOf` 对 disabled 一律抛 AccountDisabledError,
|
|
有效 token 不能把它翻回 active(撤权在同步之后即时生效)
|
|
- 名册即时更新只写变了的列;`last_seen_at` 60 秒内不重写
|
|
- 角色→端:`setRoleAccess` / `deleteRoleAccess` 先算新表再验锁死不变量(名册为空不算锁死)
|
|
- `staffOfPosition` 只回 active;哨兵岗 NONE 恒回 []
|
|
- `ensureSeeded` 幂等:岗位按 code 判存在;缺省角色→端只在表空时播;测试账号策略只补不覆盖
|
|
- 工作组是绑定不是策略(ADR-011):`bindingOf` 只出成员表快照,在岗与否由调用方核
|
|
|
|
影响半径(2026-08-25,ADR-012):contract / port / index 全面重写 —— 退役 `login` `logout` `resolveSession`
|
|
`setPassword` `authorize` `loadSession` `listGrants` `setGrants` `createStaff` `checkDisable`
|
|
`disableStaff` `findByAccount` 与 `SessionRepository` `PasswordHasher` `GrantRepository`;
|
|
下游 app(中间件 / 装配 / 路由)与 message 装配同批改,depcruise 已反查确认。
|