Merge branch 'main' of http://39.117.244.52:3000/kjs/ERP-node into feat/rest-api
This commit is contained in:
commit
2c30d40623
|
|
@ -1,554 +0,0 @@
|
|||
import { query } from "../database/db";
|
||||
import { logger } from "../utils/logger";
|
||||
|
||||
/**
|
||||
* 권한 그룹 인터페이스
|
||||
*/
|
||||
export interface RoleGroup {
|
||||
objid: number;
|
||||
authName: string;
|
||||
authCode: string;
|
||||
companyCode: string;
|
||||
status: string;
|
||||
writer: string;
|
||||
regdate: Date;
|
||||
memberCount?: number;
|
||||
menuCount?: number;
|
||||
memberNames?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 권한 그룹 멤버 인터페이스
|
||||
*/
|
||||
export interface RoleMember {
|
||||
objid: number;
|
||||
masterObjid: number;
|
||||
userId: string;
|
||||
userName?: string;
|
||||
deptName?: string;
|
||||
positionName?: string;
|
||||
writer: string;
|
||||
regdate: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* 메뉴 권한 인터페이스
|
||||
*/
|
||||
export interface MenuPermission {
|
||||
objid: number;
|
||||
menuObjid: number;
|
||||
authObjid: number;
|
||||
menuName?: string;
|
||||
createYn: string;
|
||||
readYn: string;
|
||||
updateYn: string;
|
||||
deleteYn: string;
|
||||
writer: string;
|
||||
regdate: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* 권한 그룹 서비스
|
||||
*/
|
||||
export class RoleService {
|
||||
/**
|
||||
* 회사별 권한 그룹 목록 조회
|
||||
* @param companyCode - 회사 코드 (undefined 시 전체 조회)
|
||||
* @param search - 검색어
|
||||
*/
|
||||
static async getRoleGroups(
|
||||
companyCode?: string,
|
||||
search?: string
|
||||
): Promise<RoleGroup[]> {
|
||||
try {
|
||||
let sql = `
|
||||
SELECT
|
||||
objid,
|
||||
auth_name AS "authName",
|
||||
auth_code AS "authCode",
|
||||
company_code AS "companyCode",
|
||||
status,
|
||||
writer,
|
||||
regdate,
|
||||
(SELECT COUNT(*) FROM authority_sub_user asu WHERE asu.master_objid = am.objid) AS "memberCount",
|
||||
(SELECT COUNT(*) FROM rel_menu_auth rma WHERE rma.auth_objid = am.objid) AS "menuCount",
|
||||
(SELECT STRING_AGG(ui.user_name, ', ' ORDER BY ui.user_name)
|
||||
FROM authority_sub_user asu
|
||||
JOIN user_info ui ON asu.user_id = ui.user_id
|
||||
WHERE asu.master_objid = am.objid) AS "memberNames"
|
||||
FROM authority_master am
|
||||
WHERE 1=1
|
||||
`;
|
||||
|
||||
const params: any[] = [];
|
||||
let paramIndex = 1;
|
||||
|
||||
// 회사 코드 필터 (companyCode가 undefined면 전체 조회)
|
||||
if (companyCode) {
|
||||
sql += ` AND company_code = $${paramIndex}`;
|
||||
params.push(companyCode);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
// 검색어 필터
|
||||
if (search && search.trim()) {
|
||||
sql += ` AND (auth_name ILIKE $${paramIndex} OR auth_code ILIKE $${paramIndex})`;
|
||||
params.push(`%${search.trim()}%`);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
sql += ` ORDER BY regdate DESC`;
|
||||
|
||||
logger.info("권한 그룹 조회 SQL", { sql, params });
|
||||
const result = await query<RoleGroup>(sql, params);
|
||||
logger.info("권한 그룹 조회 결과", { count: result.length });
|
||||
return result;
|
||||
} catch (error) {
|
||||
logger.error("권한 그룹 목록 조회 실패", { error, companyCode, search });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 권한 그룹 상세 조회
|
||||
*/
|
||||
static async getRoleGroupById(objid: number): Promise<RoleGroup | null> {
|
||||
try {
|
||||
const sql = `
|
||||
SELECT
|
||||
objid,
|
||||
auth_name AS "authName",
|
||||
auth_code AS "authCode",
|
||||
company_code AS "companyCode",
|
||||
status,
|
||||
writer,
|
||||
regdate
|
||||
FROM authority_master
|
||||
WHERE objid = $1
|
||||
`;
|
||||
|
||||
const result = await query<RoleGroup>(sql, [objid]);
|
||||
return result.length > 0 ? result[0] : null;
|
||||
} catch (error) {
|
||||
logger.error("권한 그룹 상세 조회 실패", { error, objid });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 권한 그룹 생성
|
||||
*/
|
||||
static async createRoleGroup(data: {
|
||||
authName: string;
|
||||
authCode: string;
|
||||
companyCode: string;
|
||||
writer: string;
|
||||
}): Promise<RoleGroup> {
|
||||
try {
|
||||
const sql = `
|
||||
INSERT INTO authority_master (objid, auth_name, auth_code, company_code, status, writer, regdate)
|
||||
VALUES (nextval('seq_authority_master'), $1, $2, $3, 'active', $4, NOW())
|
||||
RETURNING objid, auth_name AS "authName", auth_code AS "authCode",
|
||||
company_code AS "companyCode", status, writer, regdate
|
||||
`;
|
||||
|
||||
const result = await query<RoleGroup>(sql, [
|
||||
data.authName,
|
||||
data.authCode,
|
||||
data.companyCode,
|
||||
data.writer,
|
||||
]);
|
||||
|
||||
logger.info("권한 그룹 생성 성공", {
|
||||
objid: result[0].objid,
|
||||
authName: data.authName,
|
||||
});
|
||||
return result[0];
|
||||
} catch (error) {
|
||||
logger.error("권한 그룹 생성 실패", { error, data });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 권한 그룹 수정
|
||||
*/
|
||||
static async updateRoleGroup(
|
||||
objid: number,
|
||||
data: {
|
||||
authName?: string;
|
||||
authCode?: string;
|
||||
status?: string;
|
||||
}
|
||||
): Promise<RoleGroup> {
|
||||
try {
|
||||
const updates: string[] = [];
|
||||
const params: any[] = [];
|
||||
let paramIndex = 1;
|
||||
|
||||
if (data.authName !== undefined) {
|
||||
updates.push(`auth_name = $${paramIndex}`);
|
||||
params.push(data.authName);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
if (data.authCode !== undefined) {
|
||||
updates.push(`auth_code = $${paramIndex}`);
|
||||
params.push(data.authCode);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
if (data.status !== undefined) {
|
||||
updates.push(`status = $${paramIndex}`);
|
||||
params.push(data.status);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
if (updates.length === 0) {
|
||||
throw new Error("수정할 데이터가 없습니다");
|
||||
}
|
||||
|
||||
params.push(objid);
|
||||
|
||||
const sql = `
|
||||
UPDATE authority_master
|
||||
SET ${updates.join(", ")}
|
||||
WHERE objid = $${paramIndex}
|
||||
RETURNING objid, auth_name AS "authName", auth_code AS "authCode",
|
||||
company_code AS "companyCode", status, writer, regdate
|
||||
`;
|
||||
|
||||
const result = await query<RoleGroup>(sql, params);
|
||||
|
||||
if (result.length === 0) {
|
||||
throw new Error("권한 그룹을 찾을 수 없습니다");
|
||||
}
|
||||
|
||||
logger.info("권한 그룹 수정 성공", { objid, updates });
|
||||
return result[0];
|
||||
} catch (error) {
|
||||
logger.error("권한 그룹 수정 실패", { error, objid, data });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 권한 그룹 삭제
|
||||
*/
|
||||
static async deleteRoleGroup(objid: number): Promise<void> {
|
||||
try {
|
||||
// CASCADE로 연결된 데이터도 함께 삭제됨 (authority_sub_user, rel_menu_auth)
|
||||
await query("DELETE FROM authority_master WHERE objid = $1", [objid]);
|
||||
logger.info("권한 그룹 삭제 성공", { objid });
|
||||
} catch (error) {
|
||||
logger.error("권한 그룹 삭제 실패", { error, objid });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 권한 그룹 멤버 목록 조회
|
||||
*/
|
||||
static async getRoleMembers(masterObjid: number): Promise<RoleMember[]> {
|
||||
try {
|
||||
const sql = `
|
||||
SELECT
|
||||
asu.objid,
|
||||
asu.master_objid AS "masterObjid",
|
||||
asu.user_id AS "userId",
|
||||
ui.user_name AS "userName",
|
||||
ui.dept_name AS "deptName",
|
||||
ui.position_name AS "positionName",
|
||||
asu.writer,
|
||||
asu.regdate
|
||||
FROM authority_sub_user asu
|
||||
JOIN user_info ui ON asu.user_id = ui.user_id
|
||||
WHERE asu.master_objid = $1
|
||||
ORDER BY ui.user_name
|
||||
`;
|
||||
|
||||
const result = await query<RoleMember>(sql, [masterObjid]);
|
||||
return result;
|
||||
} catch (error) {
|
||||
logger.error("권한 그룹 멤버 조회 실패", { error, masterObjid });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 권한 그룹 멤버 추가 (여러 명)
|
||||
*/
|
||||
static async addRoleMembers(
|
||||
masterObjid: number,
|
||||
userIds: string[],
|
||||
writer: string
|
||||
): Promise<void> {
|
||||
try {
|
||||
// 이미 존재하는 멤버 제외
|
||||
const existingSql = `
|
||||
SELECT user_id
|
||||
FROM authority_sub_user
|
||||
WHERE master_objid = $1 AND user_id = ANY($2)
|
||||
`;
|
||||
const existing = await query<{ user_id: string }>(existingSql, [
|
||||
masterObjid,
|
||||
userIds,
|
||||
]);
|
||||
const existingIds = new Set(
|
||||
existing.map((row: { user_id: string }) => row.user_id)
|
||||
);
|
||||
|
||||
const newUserIds = userIds.filter((userId) => !existingIds.has(userId));
|
||||
|
||||
if (newUserIds.length === 0) {
|
||||
logger.info("추가할 신규 멤버가 없습니다", { masterObjid, userIds });
|
||||
return;
|
||||
}
|
||||
|
||||
// 배치 삽입
|
||||
const values = newUserIds
|
||||
.map(
|
||||
(_, index) =>
|
||||
`(nextval('seq_authority_sub_user'), $1, $${index + 2}, $${newUserIds.length + 2}, NOW())`
|
||||
)
|
||||
.join(", ");
|
||||
|
||||
const sql = `
|
||||
INSERT INTO authority_sub_user (objid, master_objid, user_id, writer, regdate)
|
||||
VALUES ${values}
|
||||
`;
|
||||
|
||||
await query(sql, [masterObjid, ...newUserIds, writer]);
|
||||
|
||||
// 히스토리 기록
|
||||
for (const userId of newUserIds) {
|
||||
await this.insertAuthorityHistory(masterObjid, userId, "ADD", writer);
|
||||
}
|
||||
|
||||
logger.info("권한 그룹 멤버 추가 성공", {
|
||||
masterObjid,
|
||||
count: newUserIds.length,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("권한 그룹 멤버 추가 실패", { error, masterObjid, userIds });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 권한 그룹 멤버 제거 (여러 명)
|
||||
*/
|
||||
static async removeRoleMembers(
|
||||
masterObjid: number,
|
||||
userIds: string[],
|
||||
writer: string
|
||||
): Promise<void> {
|
||||
try {
|
||||
await query(
|
||||
"DELETE FROM authority_sub_user WHERE master_objid = $1 AND user_id = ANY($2)",
|
||||
[masterObjid, userIds]
|
||||
);
|
||||
|
||||
// 히스토리 기록
|
||||
for (const userId of userIds) {
|
||||
await this.insertAuthorityHistory(
|
||||
masterObjid,
|
||||
userId,
|
||||
"REMOVE",
|
||||
writer
|
||||
);
|
||||
}
|
||||
|
||||
logger.info("권한 그룹 멤버 제거 성공", {
|
||||
masterObjid,
|
||||
count: userIds.length,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("권한 그룹 멤버 제거 실패", { error, masterObjid, userIds });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 권한 히스토리 기록
|
||||
*/
|
||||
private static async insertAuthorityHistory(
|
||||
masterObjid: number,
|
||||
userId: string,
|
||||
historyType: "ADD" | "REMOVE",
|
||||
writer: string
|
||||
): Promise<void> {
|
||||
try {
|
||||
const sql = `
|
||||
INSERT INTO authority_master_history
|
||||
(objid, parent_objid, parent_name, parent_code, user_id, active, history_type, writer, reg_date)
|
||||
SELECT
|
||||
nextval('seq_authority_master'),
|
||||
$1,
|
||||
am.auth_name,
|
||||
am.auth_code,
|
||||
$2,
|
||||
am.status,
|
||||
$3,
|
||||
$4,
|
||||
NOW()
|
||||
FROM authority_master am
|
||||
WHERE am.objid = $1
|
||||
`;
|
||||
|
||||
await query(sql, [masterObjid, userId, historyType, writer]);
|
||||
} catch (error) {
|
||||
logger.error("권한 히스토리 기록 실패", {
|
||||
error,
|
||||
masterObjid,
|
||||
userId,
|
||||
historyType,
|
||||
});
|
||||
// 히스토리 기록 실패는 메인 작업을 중단하지 않음
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 메뉴 권한 목록 조회
|
||||
*/
|
||||
static async getMenuPermissions(
|
||||
authObjid: number
|
||||
): Promise<MenuPermission[]> {
|
||||
try {
|
||||
const sql = `
|
||||
SELECT
|
||||
rma.objid,
|
||||
rma.menu_objid AS "menuObjid",
|
||||
rma.auth_objid AS "authObjid",
|
||||
mi.menu_name_kor AS "menuName",
|
||||
mi.menu_code AS "menuCode",
|
||||
mi.menu_url AS "menuUrl",
|
||||
rma.create_yn AS "createYn",
|
||||
rma.read_yn AS "readYn",
|
||||
rma.update_yn AS "updateYn",
|
||||
rma.delete_yn AS "deleteYn",
|
||||
rma.execute_yn AS "executeYn",
|
||||
rma.export_yn AS "exportYn",
|
||||
rma.writer,
|
||||
rma.regdate
|
||||
FROM rel_menu_auth rma
|
||||
LEFT JOIN menu_info mi ON rma.menu_objid = mi.objid
|
||||
WHERE rma.auth_objid = $1
|
||||
ORDER BY mi.menu_name_kor
|
||||
`;
|
||||
|
||||
const result = await query<MenuPermission>(sql, [authObjid]);
|
||||
return result;
|
||||
} catch (error) {
|
||||
logger.error("메뉴 권한 조회 실패", { error, authObjid });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 메뉴 권한 설정 (여러 메뉴)
|
||||
*/
|
||||
static async setMenuPermissions(
|
||||
authObjid: number,
|
||||
permissions: Array<{
|
||||
menuObjid: number;
|
||||
createYn: string;
|
||||
readYn: string;
|
||||
updateYn: string;
|
||||
deleteYn: string;
|
||||
}>,
|
||||
writer: string
|
||||
): Promise<void> {
|
||||
try {
|
||||
// 기존 권한 삭제
|
||||
await query("DELETE FROM rel_menu_auth WHERE auth_objid = $1", [
|
||||
authObjid,
|
||||
]);
|
||||
|
||||
// 새로운 권한 삽입
|
||||
if (permissions.length > 0) {
|
||||
const values = permissions
|
||||
.map(
|
||||
(_, index) =>
|
||||
`(nextval('seq_rel_menu_auth'), $${index * 5 + 2}, $1, $${index * 5 + 3}, $${index * 5 + 4}, $${index * 5 + 5}, $${index * 5 + 6}, $${permissions.length * 5 + 2}, NOW())`
|
||||
)
|
||||
.join(", ");
|
||||
|
||||
const params = permissions.flatMap((p) => [
|
||||
p.menuObjid,
|
||||
p.createYn,
|
||||
p.readYn,
|
||||
p.updateYn,
|
||||
p.deleteYn,
|
||||
]);
|
||||
|
||||
const sql = `
|
||||
INSERT INTO rel_menu_auth (objid, menu_objid, auth_objid, create_yn, read_yn, update_yn, delete_yn, writer, regdate)
|
||||
VALUES ${values}
|
||||
`;
|
||||
|
||||
await query(sql, [authObjid, ...params, writer]);
|
||||
}
|
||||
|
||||
logger.info("메뉴 권한 설정 성공", {
|
||||
authObjid,
|
||||
count: permissions.length,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("메뉴 권한 설정 실패", { error, authObjid, permissions });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 사용자가 속한 권한 그룹 목록 조회
|
||||
*/
|
||||
static async getUserRoleGroups(
|
||||
userId: string,
|
||||
companyCode: string
|
||||
): Promise<RoleGroup[]> {
|
||||
try {
|
||||
const sql = `
|
||||
SELECT
|
||||
am.objid,
|
||||
am.auth_name AS "authName",
|
||||
am.auth_code AS "authCode",
|
||||
am.company_code AS "companyCode",
|
||||
am.status,
|
||||
am.writer,
|
||||
am.regdate
|
||||
FROM authority_master am
|
||||
JOIN authority_sub_user asu ON am.objid = asu.master_objid
|
||||
WHERE asu.user_id = $1
|
||||
AND am.company_code = $2
|
||||
AND am.status = 'active'
|
||||
ORDER BY am.auth_name
|
||||
`;
|
||||
|
||||
const result = await query<RoleGroup>(sql, [userId, companyCode]);
|
||||
return result;
|
||||
} catch (error) {
|
||||
logger.error("사용자 권한 그룹 조회 실패", {
|
||||
error,
|
||||
userId,
|
||||
companyCode,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 전체 메뉴 목록 조회 (권한 설정용)
|
||||
*/
|
||||
/**
|
||||
* 전체 메뉴 목록 조회 (권한 설정용)
|
||||
*/
|
||||
static async getAllMenus(companyCode?: string): Promise<any[]> {
|
||||
try {
|
||||
logger.info("🔍 전체 메뉴 목록 조회 시작", { companyCode });
|
||||
|
||||
let whereConditions: string[] = ["status = 'active'"];
|
||||
const params: any[] = [];
|
||||
let paramIndex = 1;
|
||||
|
||||
// 회사 코드 필터 (선택적)
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
/**
|
||||
* 전체 메뉴 목록 조회 (권한 설정용)
|
||||
*/
|
||||
static async getAllMenus(companyCode?: string): Promise<any[]> {
|
||||
try {
|
||||
logger.info("🔍 전체 메뉴 목록 조회 시작", { companyCode });
|
||||
|
||||
let whereConditions: string[] = ["status = 'active'"];
|
||||
const params: any[] = [];
|
||||
let paramIndex = 1;
|
||||
|
||||
// 회사 코드 필터 (선택적)
|
||||
// 공통 메뉴(*)와 특정 회사 메뉴를 모두 조회
|
||||
if (companyCode) {
|
||||
whereConditions.push(`(company_code = \$${paramIndex} OR company_code = '*')`);
|
||||
params.push(companyCode);
|
||||
paramIndex++;
|
||||
logger.info("📋 회사 코드 필터 적용", { companyCode });
|
||||
} else {
|
||||
logger.info("📋 회사 코드 필터 없음 (전체 조회)");
|
||||
}
|
||||
|
||||
const whereClause = whereConditions.join(" AND ");
|
||||
|
||||
const sql = `
|
||||
SELECT
|
||||
objid,
|
||||
menu_name_kor AS "menuName",
|
||||
menu_name_eng AS "menuNameEng",
|
||||
menu_code AS "menuCode",
|
||||
menu_url AS "menuUrl",
|
||||
menu_type AS "menuType",
|
||||
parent_obj_id AS "parentObjid",
|
||||
seq AS "sortOrder",
|
||||
company_code AS "companyCode"
|
||||
FROM menu_info
|
||||
WHERE ${whereClause}
|
||||
ORDER BY seq, menu_name_kor
|
||||
`;
|
||||
|
||||
logger.info("🔍 SQL 쿼리 실행", {
|
||||
whereClause,
|
||||
params,
|
||||
sql: sql.substring(0, 200) + "...",
|
||||
});
|
||||
|
||||
const result = await query<any>(sql, params);
|
||||
|
||||
logger.info("✅ 메뉴 목록 조회 성공", {
|
||||
count: result.length,
|
||||
companyCode,
|
||||
menus: result.map((m) => ({
|
||||
objid: m.objid,
|
||||
name: m.menuName,
|
||||
code: m.menuCode,
|
||||
companyCode: m.companyCode,
|
||||
})),
|
||||
});
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
logger.error("❌ 메뉴 목록 조회 실패", { error, companyCode });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue