feat: 增加登录鉴权和固定角色权限
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user