import type { PoolConnection, RowDataPacket } from "mysql2/promise"; import { pool } from "../../db/pool"; type DbExecutor = typeof pool | PoolConnection; export interface RolePermissionRecord { roleId: number; roleCode: string; roleName: string; roleDescription: string | null; isSystem: boolean; permissions: string[]; } interface RolePermissionRow extends RowDataPacket { role_id: number; role_code: string; role_name: string; role_description: string | null; is_system: number; permission_code: string | null; } interface PermissionCodeRow extends RowDataPacket { permission_code: string; } function toRolePermissionRecords( rows: RolePermissionRow[], ): RolePermissionRecord[] { const records = new Map(); for (const row of rows) { const record = records.get(row.role_id) ?? { roleId: row.role_id, roleCode: row.role_code, roleName: row.role_name, roleDescription: row.role_description, isSystem: row.is_system === 1, permissions: [], }; if (row.permission_code) { record.permissions.push(row.permission_code); } records.set(row.role_id, record); } return [...records.values()]; } export const permissionRepository = { async withTransaction( handler: (connection: PoolConnection) => Promise, ): Promise { const connection = await pool.getConnection(); try { await connection.beginTransaction(); const result = await handler(connection); await connection.commit(); return result; } catch (error) { await connection.rollback(); throw error; } finally { connection.release(); } }, async listRolePermissions( db: DbExecutor = pool, ): Promise { const [rows] = await db.execute( ` SELECT r.id AS role_id, r.code AS role_code, r.name AS role_name, r.description AS role_description, r.is_system, rp.permission_code FROM roles r LEFT JOIN role_permissions rp ON rp.role_id = r.id AND rp.deleted_at IS NULL WHERE r.deleted_at IS NULL ORDER BY r.id ASC, rp.permission_code ASC `, ); return toRolePermissionRecords(rows); }, async findRolePermissionsByRoleId( roleId: number, db: DbExecutor = pool, ): Promise { const [rows] = await db.execute( ` SELECT r.id AS role_id, r.code AS role_code, r.name AS role_name, r.description AS role_description, r.is_system, rp.permission_code FROM roles r LEFT JOIN role_permissions rp ON rp.role_id = r.id AND rp.deleted_at IS NULL WHERE r.id = ? AND r.deleted_at IS NULL ORDER BY rp.permission_code ASC `, [roleId], ); return toRolePermissionRecords(rows)[0] ?? null; }, async findPermissionCodesByRoleCodes(roleCodes: string[]): Promise { if (roleCodes.length === 0) { return []; } const placeholders = roleCodes.map(() => "?").join(", "); const [rows] = await pool.execute( ` SELECT DISTINCT rp.permission_code FROM role_permissions rp INNER JOIN roles r ON r.id = rp.role_id WHERE r.code IN (${placeholders}) AND r.deleted_at IS NULL AND rp.deleted_at IS NULL ORDER BY rp.permission_code ASC `, roleCodes, ); return rows.map((row) => row.permission_code); }, async replaceRolePermissions( roleId: number, permissionCodes: string[], db: DbExecutor = pool, ): Promise { await db.execute( ` UPDATE role_permissions SET deleted_at = CURRENT_TIMESTAMP(3) WHERE role_id = ? AND deleted_at IS NULL `, [roleId], ); if (permissionCodes.length === 0) { return; } await db.execute( ` INSERT INTO role_permissions (role_id, permission_code) VALUES ${permissionCodes.map(() => "(?, ?)").join(", ")} ON DUPLICATE KEY UPDATE deleted_at = NULL, created_at = CURRENT_TIMESTAMP(3) `, permissionCodes.flatMap((permissionCode) => [roleId, permissionCode]), ); }, };