完成五人团队正式内用最小收口(07-18-small-team-internal-readiness)

这个提交包含在:
2026-07-18 15:46:14 +08:00
父节点 58c8decef9
当前提交 5d0a5f9a38
共修改 134 个文件,包含 10790 行新增和 1413 行删除
+76 -1
查看文件
@@ -30,7 +30,7 @@ async function buildApp({ routeOptions = {}, employee } = {}) {
{
id: "actor-1",
department_id: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
permissionCodes: ["system.users"],
permissionCodes: ["system.accounts.manage"],
};
app.decorate("prisma", {
@@ -53,6 +53,13 @@ async function buildApp({ routeOptions = {}, employee } = {}) {
const permissionCodes = requestEmployee.permissionCodes ?? [];
return permissionCodes.includes("*") || permissionCodes.includes(permissionCode);
});
app.decorate("assertPermission", (requestEmployee, permissionCode) => {
const permissionCodes = requestEmployee.permissionCodes ?? [];
if (!permissionCodes.includes("*") && !permissionCodes.includes(permissionCode)) {
throw new AppError({ code: "PERM_001", message: "无权限", statusCode: 403 });
}
return permissionCode;
});
app.decorate("config", {});
await app.register(usersRoutes, { prefix: "/api", ...routeOptions });
@@ -193,4 +200,72 @@ describe("usersRoutes list query", () => {
await app.close();
});
it("returns a minimal directory DTO and does not select or search contact fields", async () => {
const { app, findMany } = await buildApp({
routeOptions: {
basePath: "/employees",
allowDepartmentFilter: true,
directoryOnly: true,
managementPermissionCode: "system.users",
listPermissionCodes: ["expense.view_own"],
},
employee: {
id: "viewer-1",
department_id: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
permissionCodes: ["expense.view_own"],
},
});
const response = await app.inject({ method: "GET", url: "/api/employees?q=alice" });
expect(response.statusCode).toBe(200);
expect(response.json().data.items[0]).toEqual({
id: "11111111-1111-4111-8111-111111111111",
employeeNo: "E001",
name: "Alice",
title: "员工",
status: "active",
department: null,
avatarUrl: null,
});
const query = findMany.mock.calls[0][0];
expect(query.select).toEqual({
id: true,
employee_no: true,
name: true,
title: true,
status: true,
avatar_path: true,
department: { select: { id: true, name: true } },
});
const searchFields = query.where.AND[0].OR.map((item) => Object.keys(item)[0]);
expect(searchFields).toEqual(["employee_no", "name"]);
await app.close();
});
it("rejects management scope before directory queries without the personnel permission", async () => {
const { app, findMany, count } = await buildApp({
routeOptions: {
basePath: "/employees",
allowDepartmentFilter: true,
directoryOnly: true,
managementPermissionCode: "system.users",
listPermissionCodes: ["expense.view_own"],
},
employee: {
id: "system-admin-1",
department_id: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
permissionCodes: ["expense.view_own", "system.accounts.manage"],
},
});
const response = await app.inject({ method: "GET", url: "/api/employees?scope=management" });
expect(response.statusCode).toBe(403);
expect(findMany).not.toHaveBeenCalled();
expect(count).not.toHaveBeenCalled();
await app.close();
});
});