import { and, eq, inArray, isNull, or } from "drizzle-orm"; import { getDatabase } from "@/modules/core/server/db"; import { permissions, rolePermissions, roles } from "@/modules/core/server/schema"; import type { Permission, RoleDefinition, RoleId } from "@/modules/core/types"; import { PERMISSIONS, ROLE_LABELS } from "@/modules/core/types"; type PermissionDefinition = { id: Permission; label: string; category: string; description: string; }; export const PERMISSION_DEFINITIONS: PermissionDefinition[] = [ { id: "platform:manage", label: "平台管理", category: "平台", description: "管理平台级配置和跨机构操作。" }, { id: "organization:read", label: "查看机构", category: "机构", description: "查看机构、院区和租户信息。" }, { id: "organization:manage", label: "管理机构", category: "机构", description: "创建、编辑和停用机构。" }, { id: "account:read", label: "查看用户", category: "用户", description: "查看账号、成员和申请。" }, { id: "account:manage", label: "管理用户", category: "用户", description: "创建、审批、启停和分配用户。" }, { id: "role:read", label: "查看角色", category: "权限", description: "查看角色和权限矩阵。" }, { id: "role:manage", label: "管理角色", category: "权限", description: "创建自定义角色并配置权限组合。" }, { id: "permission:read", label: "查看权限", category: "权限", description: "查看系统注册权限点。" }, { id: "audit:read", label: "查看审计", category: "审计", description: "查看关键操作审计日志。" }, { id: "incident:read", label: "查看故障", category: "运维", description: "查看系统健康和故障中心。" }, { id: "incident:manage", label: "处理故障", category: "运维", description: "确认、处理和关闭故障事件。" }, { id: "facility:read", label: "查看房间床位", category: "床位", description: "查看房间、床位和占用状态。" }, { id: "facility:manage", label: "管理房间床位", category: "床位", description: "维护院区、房间、床位和床位状态。" }, { id: "admission:read", label: "查看入住", category: "入住", description: "查看入住、换床、退住历史。" }, { id: "admission:manage", label: "管理入住", category: "入住", description: "办理入住、换床和退住。" }, { id: "elder:read", label: "查看老人档案", category: "老人", description: "查看老人基础档案。" }, { id: "elder:create", label: "新增老人档案", category: "老人", description: "创建老人档案。" }, { id: "elder:update", label: "更新老人档案", category: "老人", description: "更新老人档案和照护信息。" }, { id: "elder:delete", label: "删除老人档案", category: "老人", description: "删除老人档案。" }, ]; type SeedRole = { key: RoleId; scope: "platform" | "organization"; description: string; permissions: Permission[]; }; export const SYSTEM_ROLE_DEFINITIONS: SeedRole[] = [ { key: "platform_admin", scope: "platform", description: "平台最高权限,管理所有机构、角色、审计和故障中心。", permissions: [...PERMISSIONS], }, { key: "platform_operator", scope: "platform", description: "平台运营角色,管理机构和跨机构用户配置。", permissions: [ "organization:read", "organization:manage", "account:read", "account:manage", "role:read", "permission:read", "audit:read", "incident:read", ], }, { key: "platform_auditor", scope: "platform", description: "平台审计角色,只读查看机构、审计和故障。", permissions: ["organization:read", "account:read", "role:read", "permission:read", "audit:read", "incident:read"], }, { key: "platform_ops", scope: "platform", description: "平台运维角色,处理系统故障和运行状态。", permissions: ["organization:read", "audit:read", "incident:read", "incident:manage"], }, { key: "org_admin", scope: "organization", description: "机构管理员,管理本机构用户、角色、房间床位、入住和审计。", permissions: [ "organization:read", "account:read", "account:manage", "role:read", "role:manage", "permission:read", "audit:read", "incident:read", "facility:read", "facility:manage", "admission:read", "admission:manage", "elder:read", "elder:create", "elder:update", "elder:delete", ], }, { key: "manager", scope: "organization", description: "运营主管,处理老人档案、床位入住和审计查看。", permissions: [ "account:read", "role:read", "audit:read", "facility:read", "facility:manage", "admission:read", "admission:manage", "elder:read", "elder:create", "elder:update", "elder:delete", ], }, { key: "caregiver", scope: "organization", description: "照护人员,查看床位和维护老人照护信息。", permissions: ["facility:read", "admission:read", "elder:read", "elder:update"], }, { key: "viewer", scope: "organization", description: "只读访客,查看老人、床位和入住信息。", permissions: ["facility:read", "admission:read", "elder:read"], }, ]; export async function ensureSystemDefaults(): Promise { const database = getDatabase(); await database .insert(permissions) .values(PERMISSION_DEFINITIONS) .onConflictDoUpdate({ target: permissions.id, set: { label: permissions.label, category: permissions.category, description: permissions.description, isEnabled: true, }, }); const existingRoles = await database.select().from(roles).where(isNull(roles.organizationId)); const existingRoleKeys = new Set(existingRoles.map((role) => role.key)); const seedRoles = SYSTEM_ROLE_DEFINITIONS.filter((role) => role.scope === "platform" && !existingRoleKeys.has(role.key)); if (seedRoles.length > 0) { await database.insert(roles).values( seedRoles.map((role) => ({ key: role.key, scope: role.scope, label: ROLE_LABELS[role.key], description: role.description, isSystem: true, isEnabled: true, })), ); } const platformRoles = await database.select().from(roles).where(isNull(roles.organizationId)); const inserts = platformRoles.flatMap((role) => { const definition = SYSTEM_ROLE_DEFINITIONS.find((item) => item.key === role.key); if (!definition) { return []; } return definition.permissions.map((permissionId) => ({ roleId: role.id, permissionId, })); }); if (inserts.length > 0) { await database.insert(rolePermissions).values(inserts).onConflictDoNothing(); } } export async function seedOrganizationRoles(organizationId: string): Promise { const database = getDatabase(); const organizationRoles = SYSTEM_ROLE_DEFINITIONS.filter((role) => role.scope === "organization"); await database .insert(roles) .values( organizationRoles.map((role) => ({ key: role.key, organizationId, scope: role.scope, label: ROLE_LABELS[role.key], description: role.description, isSystem: true, isEnabled: true, })), ) .onConflictDoNothing(); const seededRoles = await database .select() .from(roles) .where(and(eq(roles.organizationId, organizationId), inArray(roles.key, organizationRoles.map((role) => role.key)))); const inserts = seededRoles.flatMap((role) => { const definition = SYSTEM_ROLE_DEFINITIONS.find((item) => item.key === role.key); if (!definition) { return []; } return definition.permissions.map((permissionId) => ({ roleId: role.id, permissionId })); }); if (inserts.length > 0) { await database.insert(rolePermissions).values(inserts).onConflictDoNothing(); } } export async function getRoleDefinitions(organizationId?: string): Promise { const database = getDatabase(); const rows = await database .select() .from(roles) .where(organizationId ? or(isNull(roles.organizationId), eq(roles.organizationId, organizationId)) : isNull(roles.organizationId)); const roleIds = rows.map((role) => role.id); const permissionRows = roleIds.length > 0 ? await database.select().from(rolePermissions).where(inArray(rolePermissions.roleId, roleIds)) : []; return rows.map((role) => ({ id: role.id, key: role.key, label: role.label, description: role.description, scope: role.scope, organizationId: role.organizationId ?? undefined, isSystem: role.isSystem, isEnabled: role.isEnabled, permissions: permissionRows .filter((permission) => permission.roleId === role.id) .map((permission) => permission.permissionId as Permission), })); } export async function getPermissionsForRole(roleId: string): Promise { const database = getDatabase(); const rows = await database.select().from(rolePermissions).where(eq(rolePermissions.roleId, roleId)); return rows.map((row) => row.permissionId as Permission); } export function hasPermission(permissionsList: readonly Permission[], permission: Permission): boolean { return permissionsList.includes(permission); }