forked from ChaiHao/BPOS-MVP
三面拆分⑩:工作组管理落员工区 —— ADR-011 欠账清账
建组/改组:逐岗位选人,留空即回落四策略。绑定先于策略,起单时快照进工作流; 实测改绑定后新起单的 work_group 快照即含新成员表,在途不受影响。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
@@ -11,8 +11,10 @@ import {
|
||||
listGrantsRoute,
|
||||
listLlmInstancesRoute,
|
||||
listStaffRoute,
|
||||
listWorkGroupsRoute,
|
||||
positionIdSchema,
|
||||
probeLlmInstanceRoute,
|
||||
saveWorkGroupRoute,
|
||||
setGrantsRoute,
|
||||
staffIdSchema,
|
||||
upsertLlmInstanceRoute,
|
||||
@@ -97,6 +99,48 @@ export function registerGovernanceRoutes(app: Hono<AppEnv>, container: Container
|
||||
return c.json({ grants: await container.org.setGrants(positionId, parsed.data.grants) });
|
||||
});
|
||||
|
||||
/* ── 员工管理 · 工作组(ADR-011) ──────────────────────────────────── */
|
||||
|
||||
/**
|
||||
* 全部工作组 —— 一人至多一组,按员工遍历取。
|
||||
* `ownerName` 一并带出:前端要显示的是「柴浩的组」,不是一串 id。
|
||||
*/
|
||||
app.get(listWorkGroupsRoute.path, async (c) => {
|
||||
const denied = await guardStaff(c.get('account'));
|
||||
if (denied) return c.json(denied, denied.code === 'forbidden' ? 403 : 401);
|
||||
const { staff } = await container.org.listStaff();
|
||||
const found = await Promise.all(
|
||||
staff.map(async (s) => {
|
||||
const group = await container.org.workGroupOf(s.id);
|
||||
return group === undefined ? undefined : { ...group, ownerName: s.name };
|
||||
}),
|
||||
);
|
||||
return c.json({ groups: found.filter((g) => g !== undefined) });
|
||||
});
|
||||
|
||||
/**
|
||||
* 建组 / 改组 —— 按 `ownerId` 落:已有即更新(复用 id),没有即新建。
|
||||
* 与 `workGroupOf` 的取组口径一致(一人一组),不给同一个人建出两个组来。
|
||||
* 改组**不影响在途**:装进工作流的是起单时的快照。
|
||||
*/
|
||||
app.put(saveWorkGroupRoute.path, async (c) => {
|
||||
const denied = await guardStaff(c.get('account'));
|
||||
if (denied) return c.json(denied, denied.code === 'forbidden' ? 403 : 401);
|
||||
const parsed = saveWorkGroupRoute.body.safeParse(await c.req.json());
|
||||
if (!parsed.success) {
|
||||
const body: ApiError = { code: 'validation-failed', message: '工作组不合 schema', detail: parsed.error.issues };
|
||||
return c.json(body, 400);
|
||||
}
|
||||
const existing = await container.org.workGroupOf(parsed.data.ownerId);
|
||||
const group = await container.org.saveWorkGroup({
|
||||
...(existing === undefined ? {} : { id: existing.id }),
|
||||
name: parsed.data.name,
|
||||
ownerId: parsed.data.ownerId,
|
||||
members: parsed.data.members,
|
||||
});
|
||||
return c.json({ group });
|
||||
});
|
||||
|
||||
/* ── 系统管理 · LLM 实例 ──────────────────────────────────────────── */
|
||||
|
||||
app.get(listLlmInstancesRoute.path, async (c) => {
|
||||
|
||||
@@ -45,3 +45,7 @@
|
||||
影响半径: contract 增 `login` / `logout` / `resolveSession` / `setPassword` 与 `IssuedSession`;
|
||||
port 增 `SessionRepository` / `PasswordHasher`,`StaffRepository` 增两个口令读写方法;
|
||||
下游只有 app(装配 + 鉴权中间件),depcruise 已反查确认
|
||||
|
||||
近变(2026-07-27):模块面**未变**。app 层新增工作组读写路由
|
||||
(`GET|PUT /api/gov/work-groups`),组装既有的 `workGroupOf` / `saveWorkGroup`
|
||||
—— ADR-011 落地时欠的那个治理界面就此清账,一人一组的口径由 app 层按 ownerId 保证。
|
||||
|
||||
Reference in New Issue
Block a user