Files
access-manage/src/modules/auth/auth.repository.ts
T
2026-05-26 12:14:33 +08:00

225 lines
5.3 KiB
TypeScript

import type { RowDataPacket } from "mysql2/promise";
import { pool } from "../../db/pool";
import type {
EmployeeLoginAccount,
SuperAdminStatus,
SuperAdminWithPassword,
} from "./auth.types";
interface SuperAdminRow extends RowDataPacket {
id: number;
username: string;
password_hash: string;
display_name: string;
status: SuperAdminStatus;
last_login_at: Date | null;
created_at: Date;
updated_at: Date;
}
interface EmployeeLoginRow extends RowDataPacket {
id: number;
username: string;
password_hash: string;
display_name: string;
store_id: number;
store_name: string;
}
interface EmployeeRoleRow extends RowDataPacket {
employee_id: number;
id: number;
code: string;
name: string;
}
function toIso(value: Date | null): string | null {
return value ? value.toISOString() : null;
}
function toSuperAdmin(row: SuperAdminRow): SuperAdminWithPassword {
return {
id: row.id,
username: row.username,
passwordHash: row.password_hash,
displayName: row.display_name,
status: row.status,
lastLoginAt: toIso(row.last_login_at),
createdAt: toIso(row.created_at) ?? "",
updatedAt: toIso(row.updated_at) ?? "",
};
}
export const authRepository = {
async findActiveByUsername(
username: string,
): Promise<SuperAdminWithPassword | null> {
const [rows] = await pool.execute<SuperAdminRow[]>(
`
SELECT id, username, password_hash, display_name, status, last_login_at, created_at, updated_at
FROM super_admins
WHERE username = ? AND status = 'ACTIVE'
LIMIT 1
`,
[username],
);
return rows[0] ? toSuperAdmin(rows[0]) : null;
},
async findActiveById(id: number): Promise<SuperAdminWithPassword | null> {
const [rows] = await pool.execute<SuperAdminRow[]>(
`
SELECT id, username, password_hash, display_name, status, last_login_at, created_at, updated_at
FROM super_admins
WHERE id = ? AND status = 'ACTIVE'
LIMIT 1
`,
[id],
);
return rows[0] ? toSuperAdmin(rows[0]) : null;
},
async updateLastLoginAt(id: number): Promise<void> {
await pool.execute(
`
UPDATE super_admins
SET last_login_at = CURRENT_TIMESTAMP(3)
WHERE id = ?
`,
[id],
);
},
async findActiveEmployeeByPhone(
phone: string,
): Promise<EmployeeLoginAccount | null> {
const [rows] = await pool.execute<EmployeeLoginRow[]>(
`
SELECT
e.id,
e.phone AS username,
e.password_hash,
e.name AS display_name,
e.store_id,
s.name AS store_name
FROM employees e
INNER JOIN stores s ON s.id = e.store_id
WHERE e.phone = ?
AND e.status = 'ACTIVE'
AND e.deleted_at IS NULL
AND s.status = 'ACTIVE'
AND s.deleted_at IS NULL
LIMIT 1
`,
[phone],
);
if (!rows[0]) {
return null;
}
const rolesByEmployeeId = await this.findRolesByEmployeeIds([rows[0].id]);
return toEmployeeLoginAccount(
rows[0],
rolesByEmployeeId.get(rows[0].id) ?? [],
);
},
async findActiveEmployeeById(
id: number,
): Promise<EmployeeLoginAccount | null> {
const [rows] = await pool.execute<EmployeeLoginRow[]>(
`
SELECT
e.id,
e.phone AS username,
e.password_hash,
e.name AS display_name,
e.store_id,
s.name AS store_name
FROM employees e
INNER JOIN stores s ON s.id = e.store_id
WHERE e.id = ?
AND e.status = 'ACTIVE'
AND e.deleted_at IS NULL
AND s.status = 'ACTIVE'
AND s.deleted_at IS NULL
LIMIT 1
`,
[id],
);
if (!rows[0]) {
return null;
}
const rolesByEmployeeId = await this.findRolesByEmployeeIds([rows[0].id]);
return toEmployeeLoginAccount(
rows[0],
rolesByEmployeeId.get(rows[0].id) ?? [],
);
},
async updateEmployeeLastLoginAt(id: number): Promise<void> {
await pool.execute(
`
UPDATE employees
SET last_login_at = CURRENT_TIMESTAMP(3)
WHERE id = ?
`,
[id],
);
},
async findRolesByEmployeeIds(
employeeIds: number[],
): Promise<Map<number, EmployeeLoginAccount["roles"]>> {
const result = new Map<number, EmployeeLoginAccount["roles"]>();
if (employeeIds.length === 0) {
return result;
}
const placeholders = employeeIds.map(() => "?").join(", ");
const [rows] = await pool.execute<EmployeeRoleRow[]>(
`
SELECT er.employee_id, r.id, r.code, r.name
FROM employee_roles er
INNER JOIN roles r ON r.id = er.role_id
WHERE er.employee_id IN (${placeholders})
ORDER BY r.id ASC
`,
employeeIds,
);
for (const row of rows) {
const roles = result.get(row.employee_id) ?? [];
roles.push({
id: row.id,
code: row.code,
name: row.name,
});
result.set(row.employee_id, roles);
}
return result;
},
};
function toEmployeeLoginAccount(
row: EmployeeLoginRow,
roles: EmployeeLoginAccount["roles"],
): EmployeeLoginAccount {
return {
id: row.id,
username: row.username,
passwordHash: row.password_hash,
displayName: row.display_name,
storeId: row.store_id,
storeName: row.store_name,
roles,
};
}