feat: 完成 R13/R14/R15/R16 小团队管理核心能力

这个提交包含在:
2026-03-21 20:31:34 +08:00
父节点 0826693a8f
当前提交 1e52f1c225
共修改 47 个文件,包含 4298 行新增和 306 行删除
+80
查看文件
@@ -0,0 +1,80 @@
const { z } = require("zod");
const { AppError } = require("../utils/AppError");
const { sendData } = require("../utils/reply");
const { buildAuditLogData } = require("../utils/audit");
const {
DEFAULT_MODULE_VISIBILITY,
getWorkspaceSetting,
getSystemNavigation,
normalizeModuleVisibilityJson,
upsertWorkspaceSetting,
} = require("../services/workspaceSettings");
const MODULE_VISIBILITY_SCHEMA = z.record(z.string(), z.enum(["main", "admin", "hidden"])).optional();
const PATCH_SCHEMA = z
.object({
moduleVisibility: MODULE_VISIBILITY_SCHEMA,
})
.strict()
.refine((value) => Object.keys(value).length > 0, { message: "empty_patch" });
function buildValidationError(details, statusCode = 400) {
return new AppError({ code: "VALID_001", message: "参数校验失败", statusCode, details });
}
function parseBody(schema, body) {
const parsed = schema.safeParse(body ?? {});
if (!parsed.success) {
throw buildValidationError(parsed.error.flatten());
}
return parsed.data;
}
async function writeWorkspaceAuditLog(fastify, request, workspace) {
try {
await fastify.prisma.auditLog.create({
data: buildAuditLogData(request, {
actorId: request.employee.id,
action: "WORKSPACE_SETTING_UPDATED",
targetType: "workspace_setting",
targetId: workspace.id,
extra: { moduleVisibility: workspace.moduleVisibility },
}),
});
} catch (error) {
request.log.error({ err: error, workspaceId: workspace.id }, "workspace setting audit failed");
}
}
async function systemWorkspaceRoutes(fastify) {
fastify.get("/system/workspace", { preHandler: fastify.authenticate }, async (request, reply) => {
const workspace = await getWorkspaceSetting(fastify.prisma);
return sendData(reply, request, {
...workspace,
defaults: DEFAULT_MODULE_VISIBILITY,
});
});
fastify.patch("/system/workspace", { preHandler: fastify.requirePermission("system.settings") }, async (request, reply) => {
const body = parseBody(PATCH_SCHEMA, request.body);
const workspace = await upsertWorkspaceSetting(fastify.prisma, request.employee.id, {
moduleVisibility: normalizeModuleVisibilityJson(body.moduleVisibility ?? null),
});
await writeWorkspaceAuditLog(fastify, request, workspace);
return sendData(reply, request, { item: workspace });
});
fastify.get("/system/navigation", { preHandler: fastify.authenticate }, async (request, reply) => {
const data = await getSystemNavigation({
prisma: fastify.prisma,
config: fastify.config,
employee: request.employee,
hasPermission: fastify.hasPermission,
});
return sendData(reply, request, data);
});
}
module.exports = systemWorkspaceRoutes;