import { query, withTransaction } from '../../db/client.js' import { createHttpError } from '../../utils/http.js' export const ADMIN_PERMISSION_KEYS = [ 'worker_order.view', 'worker_order.edit', 'worker_order.configure_share', 'worker_order.assign', 'worker_order.return_to_hall', 'worker_order.cancel', 'worker_order.cancel_share', 'worker_order.delete', 'worker_order.publish', 'worker_order.accept', 'worker_order.reopen', 'worker_order.update_material', 'worker_order.manage_problem', 'worker_order.deduct_deposit', 'worker_cancel_request.review', ] as const export type AdminPermissionKey = (typeof ADMIN_PERMISSION_KEYS)[number] type PermissionRow = { permission_key: string permission_name: string permission_group: string description: string } export async function listAdminPermissionKeysForRole(role: string): Promise { const result = await query<{ permission_key: string }>( ` SELECT arp.permission_key FROM admin_role_permissions arp WHERE arp.role_key = $1 ORDER BY arp.permission_key `, [ String(role || '') .trim() .toLowerCase(), ], ) return result.rows.map((row) => String(row.permission_key || '').trim()).filter(Boolean) } export async function listAdminRolesWithPermissions() { const result = await query< PermissionRow & { role_key: string; role_name: string; role_description: string } >( ` SELECT ar.role_key, ar.role_name, ar.description AS role_description, ap.permission_key, ap.permission_name, ap.permission_group, ap.description FROM admin_roles ar CROSS JOIN admin_permissions ap ORDER BY ar.role_key, ap.permission_group, ap.permission_key `, ) const roleMap = new Map< string, { roleKey: string roleName: string description: string permissions: Array<{ permissionKey: string permissionName: string permissionGroup: string description: string enabled: boolean }> } >() const enabled = await query<{ role_key: string; permission_key: string }>( 'SELECT role_key, permission_key FROM admin_role_permissions', ) const enabledSet = new Set(enabled.rows.map((row) => `${row.role_key}:${row.permission_key}`)) for (const row of result.rows) { let role = roleMap.get(row.role_key) if (!role) { role = { roleKey: row.role_key, roleName: row.role_name, description: row.role_description, permissions: [], } roleMap.set(row.role_key, role) } role.permissions.push({ permissionKey: row.permission_key, permissionName: row.permission_name, permissionGroup: row.permission_group, description: row.description, enabled: enabledSet.has(`${row.role_key}:${row.permission_key}`), }) } return { roles: Array.from(roleMap.values()) } } export async function updateAdminRolePermissions(roleKey: unknown, permissionKeys: unknown) { const normalizedRole = String(roleKey || '') .trim() .toLowerCase() if (!['admin', 'operator', 'support'].includes(normalizedRole)) { throw createHttpError('角色不存在或不可修改', { statusCode: 400, errorCode: 'admin_role_invalid', }) } if (!Array.isArray(permissionKeys)) { throw createHttpError('权限列表格式无效', { statusCode: 400, errorCode: 'admin_permissions_invalid', }) } const requested = Array.from( new Set( permissionKeys .map((item) => String(item || '').trim()) .filter((item): item is AdminPermissionKey => (ADMIN_PERMISSION_KEYS as readonly string[]).includes(item), ), ), ) if (normalizedRole === 'admin' && requested.length !== ADMIN_PERMISSION_KEYS.length) { throw createHttpError('管理员角色必须保留全部权限', { statusCode: 409, errorCode: 'admin_role_full_permissions_required', }) } await withTransaction(async (client) => { await client.query('DELETE FROM admin_role_permissions WHERE role_key = $1', [normalizedRole]) if (requested.length > 0) { await client.query( ` INSERT INTO admin_role_permissions (role_key, permission_key) SELECT $1, permission_key FROM admin_permissions WHERE permission_key = ANY($2::text[]) ON CONFLICT DO NOTHING `, [normalizedRole, requested], ) } }) return (await listAdminRolesWithPermissions()).roles.find( (role) => role.roleKey === normalizedRole, ) }