최초커밋
This commit is contained in:
360
backend-node/src/services/adminService.ts
Normal file
360
backend-node/src/services/adminService.ts
Normal file
@@ -0,0 +1,360 @@
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import { logger } from "../utils/logger";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export class AdminService {
|
||||
/**
|
||||
* 관리자 메뉴 목록 조회
|
||||
*/
|
||||
static async getAdminMenuList(paramMap: any): Promise<any[]> {
|
||||
try {
|
||||
logger.info("AdminService.getAdminMenuList 시작 - 파라미터:", paramMap);
|
||||
|
||||
const { userLang = "ko", SYSTEM_NAME = "PLM" } = paramMap;
|
||||
|
||||
// 기존 Java의 selectAdminMenuList 쿼리를 Prisma로 포팅
|
||||
// WITH RECURSIVE 쿼리를 Prisma의 $queryRaw로 구현
|
||||
const menuList = await prisma.$queryRaw<any[]>`
|
||||
WITH RECURSIVE v_menu(
|
||||
LEVEL,
|
||||
MENU_TYPE,
|
||||
OBJID,
|
||||
PARENT_OBJ_ID,
|
||||
MENU_NAME_KOR,
|
||||
MENU_URL,
|
||||
MENU_DESC,
|
||||
SEQ,
|
||||
WRITER,
|
||||
REGDATE,
|
||||
STATUS,
|
||||
SYSTEM_NAME,
|
||||
COMPANY_CODE,
|
||||
LANG_KEY,
|
||||
LANG_KEY_DESC,
|
||||
PATH,
|
||||
CYCLE,
|
||||
TRANSLATED_NAME,
|
||||
TRANSLATED_DESC
|
||||
) AS (
|
||||
SELECT
|
||||
1 AS LEVEL,
|
||||
MENU.MENU_TYPE,
|
||||
MENU.OBJID::numeric,
|
||||
MENU.PARENT_OBJ_ID,
|
||||
MENU.MENU_NAME_KOR,
|
||||
MENU.MENU_URL,
|
||||
MENU.MENU_DESC,
|
||||
MENU.SEQ,
|
||||
MENU.WRITER,
|
||||
MENU.REGDATE,
|
||||
MENU.STATUS,
|
||||
MENU.SYSTEM_NAME,
|
||||
MENU.COMPANY_CODE,
|
||||
MENU.LANG_KEY,
|
||||
MENU.LANG_KEY_DESC,
|
||||
ARRAY [MENU.OBJID],
|
||||
FALSE,
|
||||
-- 번역된 메뉴명 (우선순위: 회사별 번역 > 공통 번역 > 기본명)
|
||||
COALESCE(
|
||||
(SELECT MLT.lang_text
|
||||
FROM MULTI_LANG_KEY_MASTER MLKM
|
||||
JOIN MULTI_LANG_TEXT MLT ON MLKM.key_id = MLT.key_id
|
||||
WHERE MLKM.lang_key = MENU.LANG_KEY
|
||||
AND (MLKM.company_code = MENU.COMPANY_CODE OR (MENU.COMPANY_CODE IS NULL AND MLKM.company_code = '*'))
|
||||
AND MLT.lang_code = ${userLang}
|
||||
LIMIT 1),
|
||||
(SELECT MLT.lang_text
|
||||
FROM MULTI_LANG_KEY_MASTER MLKM
|
||||
JOIN MULTI_LANG_TEXT MLT ON MLKM.key_id = MLT.key_id
|
||||
WHERE MLKM.lang_key = MENU.LANG_KEY
|
||||
AND MLKM.company_code = '*'
|
||||
AND MLT.lang_code = ${userLang}
|
||||
LIMIT 1),
|
||||
MENU.MENU_NAME_KOR
|
||||
),
|
||||
-- 번역된 설명 (우선순위: 회사별 번역 > 공통 번역 > 기본명)
|
||||
COALESCE(
|
||||
(SELECT MLT.lang_text
|
||||
FROM MULTI_LANG_KEY_MASTER MLKM
|
||||
JOIN MULTI_LANG_TEXT MLT ON MLKM.key_id = MLT.key_id
|
||||
WHERE MLKM.lang_key = MENU.LANG_KEY_DESC
|
||||
AND (MLKM.company_code = MENU.COMPANY_CODE OR (MENU.COMPANY_CODE IS NULL AND MLKM.company_code = '*'))
|
||||
AND MLT.lang_code = ${userLang}
|
||||
LIMIT 1),
|
||||
(SELECT MLT.lang_text
|
||||
FROM MULTI_LANG_KEY_MASTER MLKM
|
||||
JOIN MULTI_LANG_TEXT MLT ON MLKM.key_id = MLT.key_id
|
||||
WHERE MLKM.lang_key = MENU.LANG_KEY_DESC
|
||||
AND MLKM.company_code = '*'
|
||||
AND MLT.lang_code = ${userLang}
|
||||
LIMIT 1),
|
||||
MENU.MENU_DESC
|
||||
)
|
||||
FROM MENU_INFO MENU
|
||||
WHERE PARENT_OBJ_ID = 0
|
||||
AND MENU_TYPE = 0
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
V_MENU.LEVEL + 1,
|
||||
MENU_SUB.MENU_TYPE,
|
||||
MENU_SUB.OBJID,
|
||||
MENU_SUB.PARENT_OBJ_ID,
|
||||
MENU_SUB.MENU_NAME_KOR,
|
||||
MENU_SUB.MENU_URL,
|
||||
MENU_SUB.MENU_DESC,
|
||||
MENU_SUB.SEQ,
|
||||
MENU_SUB.WRITER,
|
||||
MENU_SUB.REGDATE,
|
||||
MENU_SUB.STATUS,
|
||||
MENU_SUB.SYSTEM_NAME,
|
||||
MENU_SUB.COMPANY_CODE,
|
||||
MENU_SUB.LANG_KEY,
|
||||
MENU_SUB.LANG_KEY_DESC,
|
||||
PATH || MENU_SUB.SEQ::numeric,
|
||||
MENU_SUB.OBJID = ANY(PATH),
|
||||
-- 번역된 메뉴명 (우선순위: 회사별 번역 > 공통 번역 > 기본명)
|
||||
COALESCE(
|
||||
(SELECT MLT.lang_text
|
||||
FROM MULTI_LANG_KEY_MASTER MLKM
|
||||
JOIN MULTI_LANG_TEXT MLT ON MLKM.key_id = MLT.key_id
|
||||
WHERE MLKM.lang_key = MENU_SUB.LANG_KEY
|
||||
AND (MLKM.company_code = MENU_SUB.COMPANY_CODE OR (MENU_SUB.COMPANY_CODE IS NULL AND MLKM.company_code = '*'))
|
||||
AND MLT.lang_code = ${userLang}
|
||||
LIMIT 1),
|
||||
(SELECT MLT.lang_text
|
||||
FROM MULTI_LANG_KEY_MASTER MLKM
|
||||
JOIN MULTI_LANG_TEXT MLT ON MLKM.key_id = MLT.key_id
|
||||
WHERE MLKM.lang_key = MENU_SUB.LANG_KEY
|
||||
AND MLKM.company_code = '*'
|
||||
AND MLT.lang_code = ${userLang}
|
||||
LIMIT 1),
|
||||
MENU_SUB.MENU_NAME_KOR
|
||||
),
|
||||
-- 번역된 설명 (우선순위: 회사별 번역 > 공통 번역 > 기본명)
|
||||
COALESCE(
|
||||
(SELECT MLT.lang_text
|
||||
FROM MULTI_LANG_KEY_MASTER MLKM
|
||||
JOIN MULTI_LANG_TEXT MLT ON MLKM.key_id = MLT.key_id
|
||||
WHERE MLKM.lang_key = MENU_SUB.LANG_KEY_DESC
|
||||
AND (MLKM.company_code = MENU_SUB.COMPANY_CODE OR (MENU_SUB.COMPANY_CODE IS NULL AND MLKM.company_code = '*'))
|
||||
AND MLT.lang_code = ${userLang}
|
||||
LIMIT 1),
|
||||
(SELECT MLT.lang_text
|
||||
FROM MULTI_LANG_KEY_MASTER MLKM
|
||||
JOIN MULTI_LANG_TEXT MLT ON MLKM.key_id = MLT.key_id
|
||||
WHERE MLKM.lang_key = MENU_SUB.LANG_KEY_DESC
|
||||
AND MLKM.company_code = '*'
|
||||
AND MLT.lang_code = ${userLang}
|
||||
LIMIT 1),
|
||||
MENU_SUB.MENU_DESC
|
||||
)
|
||||
FROM MENU_INFO MENU_SUB
|
||||
JOIN V_MENU ON MENU_SUB.PARENT_OBJ_ID = V_MENU.OBJID
|
||||
WHERE MENU_SUB.OBJID != ANY(V_MENU.PATH)
|
||||
)
|
||||
SELECT
|
||||
LEVEL AS LEV,
|
||||
CASE MENU_TYPE
|
||||
WHEN '0' THEN 'admin'
|
||||
WHEN '1' THEN 'user'
|
||||
ELSE ''
|
||||
END AS MENU_TYPE,
|
||||
A.OBJID,
|
||||
A.PARENT_OBJ_ID,
|
||||
A.MENU_NAME_KOR,
|
||||
LPAD(' ', 3 * (LEVEL - 1)) || A.MENU_NAME_KOR AS LPAD_MENU_NAME_KOR,
|
||||
A.MENU_URL,
|
||||
A.MENU_DESC,
|
||||
A.SEQ,
|
||||
A.WRITER,
|
||||
TO_CHAR(A.REGDATE, 'YYYY-MM-DD') AS REGDATE,
|
||||
A.STATUS,
|
||||
A.COMPANY_CODE,
|
||||
A.LANG_KEY,
|
||||
A.LANG_KEY_DESC,
|
||||
COALESCE(CM.COMPANY_NAME, '미지정') AS COMPANY_NAME,
|
||||
A.TRANSLATED_NAME,
|
||||
A.TRANSLATED_DESC,
|
||||
CASE UPPER(A.STATUS)
|
||||
WHEN 'ACTIVE' THEN '활성화'
|
||||
WHEN 'INACTIVE' THEN '비활성화'
|
||||
ELSE ''
|
||||
END AS STATUS_TITLE
|
||||
FROM v_menu A
|
||||
LEFT JOIN COMPANY_MNG CM ON A.COMPANY_CODE = CM.COMPANY_CODE
|
||||
WHERE 1 = 1
|
||||
AND (A.SEQ > 1 OR (A.SEQ = 0 AND LEVEL = 1))
|
||||
ORDER BY PATH, SEQ
|
||||
`;
|
||||
|
||||
logger.info(`관리자 메뉴 목록 조회 결과: ${menuList.length}개`);
|
||||
if (menuList.length > 0) {
|
||||
logger.info("첫 번째 메뉴:", menuList[0]);
|
||||
}
|
||||
|
||||
return menuList;
|
||||
} catch (error) {
|
||||
logger.error("AdminService.getAdminMenuList 오류:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 사용자 메뉴 목록 조회
|
||||
*/
|
||||
static async getUserMenuList(paramMap: any): Promise<any[]> {
|
||||
try {
|
||||
logger.info("AdminService.getUserMenuList 시작 - 파라미터:", paramMap);
|
||||
|
||||
const { userLang = "ko", SYSTEM_NAME = "PLM" } = paramMap;
|
||||
|
||||
// 기존 Java의 selectUserMenuList 쿼리를 Prisma로 포팅
|
||||
const menuList = await prisma.$queryRaw<any[]>`
|
||||
WITH RECURSIVE v_menu(
|
||||
LEVEL,
|
||||
MENU_TYPE,
|
||||
OBJID,
|
||||
PARENT_OBJ_ID,
|
||||
MENU_NAME_KOR,
|
||||
MENU_URL,
|
||||
MENU_DESC,
|
||||
SEQ,
|
||||
WRITER,
|
||||
REGDATE,
|
||||
STATUS,
|
||||
COMPANY_CODE,
|
||||
LANG_KEY,
|
||||
LANG_KEY_DESC,
|
||||
PATH,
|
||||
CYCLE
|
||||
) AS (
|
||||
SELECT
|
||||
1 AS LEVEL,
|
||||
MENU_TYPE,
|
||||
OBJID::numeric,
|
||||
PARENT_OBJ_ID,
|
||||
MENU_NAME_KOR,
|
||||
MENU_URL,
|
||||
MENU_DESC,
|
||||
SEQ,
|
||||
WRITER,
|
||||
REGDATE,
|
||||
STATUS,
|
||||
COMPANY_CODE,
|
||||
LANG_KEY,
|
||||
LANG_KEY_DESC,
|
||||
ARRAY [MENU.OBJID],
|
||||
FALSE
|
||||
FROM MENU_INFO MENU
|
||||
WHERE PARENT_OBJ_ID = 0
|
||||
AND MENU_TYPE = 1
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
V_MENU.LEVEL + 1,
|
||||
MENU_SUB.MENU_TYPE,
|
||||
MENU_SUB.OBJID,
|
||||
MENU_SUB.PARENT_OBJ_ID,
|
||||
MENU_SUB.MENU_NAME_KOR,
|
||||
MENU_SUB.MENU_URL,
|
||||
MENU_SUB.MENU_DESC,
|
||||
MENU_SUB.SEQ,
|
||||
MENU_SUB.WRITER,
|
||||
MENU_SUB.REGDATE,
|
||||
MENU_SUB.STATUS,
|
||||
MENU_SUB.COMPANY_CODE,
|
||||
MENU_SUB.LANG_KEY,
|
||||
MENU_SUB.LANG_KEY_DESC,
|
||||
PATH || MENU_SUB.SEQ::numeric,
|
||||
MENU_SUB.OBJID = ANY(PATH)
|
||||
FROM MENU_INFO MENU_SUB
|
||||
JOIN V_MENU ON MENU_SUB.PARENT_OBJ_ID = V_MENU.OBJID
|
||||
WHERE 1 = 1
|
||||
)
|
||||
SELECT
|
||||
LEVEL AS LEV,
|
||||
CASE MENU_TYPE
|
||||
WHEN '0' THEN 'admin'
|
||||
WHEN '1' THEN 'user'
|
||||
ELSE ''
|
||||
END AS MENU_TYPE,
|
||||
A.OBJID,
|
||||
A.PARENT_OBJ_ID,
|
||||
A.MENU_NAME_KOR,
|
||||
LPAD(' ', 3 * (LEVEL - 1)) || A.MENU_NAME_KOR AS LPAD_MENU_NAME_KOR,
|
||||
A.MENU_URL,
|
||||
A.MENU_DESC,
|
||||
A.SEQ,
|
||||
A.WRITER,
|
||||
TO_CHAR(A.REGDATE, 'YYYY-MM-DD') AS REGDATE,
|
||||
A.STATUS,
|
||||
A.COMPANY_CODE,
|
||||
A.LANG_KEY,
|
||||
A.LANG_KEY_DESC,
|
||||
COALESCE(CM.COMPANY_NAME, '미지정') AS COMPANY_NAME,
|
||||
-- 번역된 메뉴명 (우선순위: 번역 > 기본명)
|
||||
COALESCE(MLT_NAME.lang_text, A.MENU_NAME_KOR) AS TRANSLATED_NAME,
|
||||
-- 번역된 설명 (우선순위: 번역 > 기본명)
|
||||
COALESCE(MLT_DESC.lang_text, A.MENU_DESC) AS TRANSLATED_DESC,
|
||||
CASE UPPER(A.STATUS)
|
||||
WHEN 'ACTIVE' THEN '활성화'
|
||||
WHEN 'INACTIVE' THEN '비활성화'
|
||||
ELSE ''
|
||||
END AS STATUS_TITLE
|
||||
FROM v_menu A
|
||||
LEFT JOIN COMPANY_MNG CM ON A.COMPANY_CODE = CM.COMPANY_CODE
|
||||
LEFT JOIN MULTI_LANG_KEY_MASTER MLKM_NAME ON A.LANG_KEY = MLKM_NAME.lang_key
|
||||
LEFT JOIN MULTI_LANG_TEXT MLT_NAME ON MLKM_NAME.key_id = MLT_NAME.key_id AND MLT_NAME.lang_code = ${userLang}
|
||||
LEFT JOIN MULTI_LANG_KEY_MASTER MLKM_DESC ON A.LANG_KEY_DESC = MLKM_DESC.lang_key
|
||||
LEFT JOIN MULTI_LANG_TEXT MLT_DESC ON MLKM_DESC.key_id = MLT_DESC.key_id AND MLT_DESC.lang_code = ${userLang}
|
||||
WHERE 1 = 1
|
||||
AND (A.SEQ > 1 OR (A.SEQ = 0 AND LEVEL = 1))
|
||||
ORDER BY PATH, SEQ
|
||||
`;
|
||||
|
||||
logger.info(`사용자 메뉴 목록 조회 결과: ${menuList.length}개`);
|
||||
if (menuList.length > 0) {
|
||||
logger.info("첫 번째 메뉴:", menuList[0]);
|
||||
}
|
||||
|
||||
return menuList;
|
||||
} catch (error) {
|
||||
logger.error("AdminService.getUserMenuList 오류:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 메뉴 정보 조회
|
||||
*/
|
||||
static async getMenuInfo(menuId: string): Promise<any> {
|
||||
try {
|
||||
logger.info(`AdminService.getMenuInfo 시작 - menuId: ${menuId}`);
|
||||
|
||||
// menu_info 모델이 @@ignore로 설정되어 있으므로 $queryRaw 사용
|
||||
const menuInfo = await prisma.$queryRaw<any[]>`
|
||||
SELECT
|
||||
MI.*,
|
||||
COALESCE(CM.COMPANY_NAME, '미지정') AS COMPANY_NAME
|
||||
FROM MENU_INFO MI
|
||||
LEFT JOIN COMPANY_MNG CM ON MI.COMPANY_CODE = CM.COMPANY_CODE
|
||||
WHERE MI.OBJID = ${parseInt(menuId)}::numeric
|
||||
LIMIT 1
|
||||
`;
|
||||
|
||||
if (!menuInfo || menuInfo.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.info("메뉴 정보 조회 결과:", menuInfo[0]);
|
||||
return menuInfo[0];
|
||||
} catch (error) {
|
||||
logger.error("AdminService.getMenuInfo 오류:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
309
backend-node/src/services/authService.ts
Normal file
309
backend-node/src/services/authService.ts
Normal file
@@ -0,0 +1,309 @@
|
||||
// 인증 서비스
|
||||
// 기존 Java LoginService를 Node.js로 포팅
|
||||
|
||||
import prisma from "../config/database";
|
||||
import { PasswordUtils } from "../utils/passwordUtils";
|
||||
import { JwtUtils } from "../utils/jwtUtils";
|
||||
import { PersonBean, LoginResult, LoginLogData } from "../types/auth";
|
||||
import { logger } from "../utils/logger";
|
||||
|
||||
export class AuthService {
|
||||
/**
|
||||
* 기존 Java LoginService.loginPwdCheck() 메서드 포팅
|
||||
* 로그인을 시도하여 결과를 return 한다.
|
||||
*/
|
||||
static async loginPwdCheck(
|
||||
userId: string,
|
||||
password: string
|
||||
): Promise<LoginResult> {
|
||||
try {
|
||||
// 사용자 비밀번호 조회 (기존 login.getUserPassword 쿼리 포팅)
|
||||
const userInfo = await prisma.user_info.findUnique({
|
||||
where: {
|
||||
user_id: userId,
|
||||
},
|
||||
select: {
|
||||
user_password: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (userInfo && userInfo.user_password) {
|
||||
const dbPassword = userInfo.user_password;
|
||||
|
||||
logger.info(`로그인 시도: ${userId}`);
|
||||
logger.debug(`DB 비밀번호: ${dbPassword}, 입력 비밀번호: ${password}`);
|
||||
|
||||
// 마스터 패스워드 체크 (기존 Java 로직과 동일)
|
||||
if (password === "qlalfqjsgh11") {
|
||||
logger.info(`마스터 패스워드로 로그인 성공: ${userId}`);
|
||||
return {
|
||||
loginResult: true,
|
||||
};
|
||||
}
|
||||
|
||||
// 비밀번호 검증 (기존 EncryptUtil 로직 사용)
|
||||
if (PasswordUtils.matches(password, dbPassword)) {
|
||||
logger.info(`비밀번호 일치로 로그인 성공: ${userId}`);
|
||||
return {
|
||||
loginResult: true,
|
||||
};
|
||||
} else {
|
||||
logger.warn(`비밀번호 불일치로 로그인 실패: ${userId}`);
|
||||
return {
|
||||
loginResult: false,
|
||||
errorReason: "패스워드가 일치하지 않습니다.",
|
||||
};
|
||||
}
|
||||
} else {
|
||||
logger.warn(`사용자가 존재하지 않음: ${userId}`);
|
||||
return {
|
||||
loginResult: false,
|
||||
errorReason: "사용자가 존재하지 않습니다.",
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`로그인 검증 중 오류 발생: ${error instanceof Error ? error.message : error}`
|
||||
);
|
||||
return {
|
||||
loginResult: false,
|
||||
errorReason: "로그인 처리 중 오류가 발생했습니다.",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 기존 Java LoginService.insertLoginAccessLog() 메서드 포팅
|
||||
* 로그인 로그를 기록한다.
|
||||
*/
|
||||
static async insertLoginAccessLog(logData: LoginLogData): Promise<void> {
|
||||
try {
|
||||
// 기존 login.insertLoginAccessLog 쿼리 포팅
|
||||
await prisma.$executeRaw`
|
||||
INSERT INTO LOGIN_ACCESS_LOG(
|
||||
LOG_TIME,
|
||||
SYSTEM_NAME,
|
||||
USER_ID,
|
||||
LOGIN_RESULT,
|
||||
ERROR_MESSAGE,
|
||||
REMOTE_ADDR,
|
||||
RECPTN_DT,
|
||||
RECPTN_RSLT_DTL,
|
||||
RECPTN_RSLT,
|
||||
RECPTN_RSLT_CD
|
||||
) VALUES (
|
||||
now(),
|
||||
${logData.systemName},
|
||||
UPPER(${logData.userId}),
|
||||
${logData.loginResult},
|
||||
${logData.errorMessage || null},
|
||||
${logData.remoteAddr},
|
||||
${logData.recptnDt || null},
|
||||
${logData.recptnRsltDtl || null},
|
||||
${logData.recptnRslt || null},
|
||||
${logData.recptnRsltCd || null}
|
||||
)
|
||||
`;
|
||||
|
||||
logger.info(
|
||||
`로그인 로그 기록 완료: ${logData.userId} (${logData.loginResult ? "성공" : "실패"})`
|
||||
);
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`로그인 로그 기록 중 오류 발생: ${error instanceof Error ? error.message : error}`
|
||||
);
|
||||
// 로그 기록 실패는 로그인 프로세스를 중단하지 않음
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 기존 Java SessionManager.setSessionManage() 메서드 포팅
|
||||
* 로그인 성공 시 사용자 정보를 조회하여 PersonBean 형태로 반환
|
||||
*/
|
||||
static async getUserInfo(userId: string): Promise<PersonBean | null> {
|
||||
try {
|
||||
// 기존 login.getUserInfo 쿼리 포팅
|
||||
const userInfo = await prisma.user_info.findUnique({
|
||||
where: {
|
||||
user_id: userId,
|
||||
},
|
||||
select: {
|
||||
sabun: true,
|
||||
user_id: true,
|
||||
user_name: true,
|
||||
user_name_eng: true,
|
||||
user_name_cn: true,
|
||||
dept_code: true,
|
||||
dept_name: true,
|
||||
position_code: true,
|
||||
position_name: true,
|
||||
email: true,
|
||||
tel: true,
|
||||
cell_phone: true,
|
||||
user_type: true,
|
||||
user_type_name: true,
|
||||
partner_objid: true,
|
||||
company_code: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!userInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 권한 정보 조회 (기존 Java 로직과 동일)
|
||||
const authInfo = await prisma.$queryRaw<Array<{ auth_name: string }>>`
|
||||
SELECT ARRAY_TO_STRING(ARRAY_AGG(AM.AUTH_NAME), ',') AS AUTH_NAME
|
||||
FROM AUTHORITY_MASTER AM, AUTHORITY_SUB_USER ASU
|
||||
WHERE AM.OBJID = ASU.MASTER_OBJID
|
||||
AND ASU.USER_ID = ${userId}
|
||||
GROUP BY ASU.USER_ID
|
||||
`;
|
||||
|
||||
// 회사 정보 조회 (기존 Java 로직과 동일)
|
||||
const companyInfo = await prisma.$queryRaw<
|
||||
Array<{ company_name: string }>
|
||||
>`
|
||||
SELECT COALESCE(CM.COMPANY_NAME, '미지정') AS COMPANY_NAME
|
||||
FROM COMPANY_MNG CM
|
||||
WHERE CM.COMPANY_CODE = ${userInfo.company_code || "ILSHIN"}
|
||||
`;
|
||||
|
||||
// PersonBean 형태로 변환 (null 값을 undefined로 변환)
|
||||
const personBean: PersonBean = {
|
||||
userId: userInfo.user_id,
|
||||
userName: userInfo.user_name || "",
|
||||
userNameEng: userInfo.user_name_eng || undefined,
|
||||
userNameCn: userInfo.user_name_cn || undefined,
|
||||
deptCode: userInfo.dept_code || undefined,
|
||||
deptName: userInfo.dept_name || undefined,
|
||||
positionCode: userInfo.position_code || undefined,
|
||||
positionName: userInfo.position_name || undefined,
|
||||
email: userInfo.email || undefined,
|
||||
tel: userInfo.tel || undefined,
|
||||
cellPhone: userInfo.cell_phone || undefined,
|
||||
userType: userInfo.user_type || undefined,
|
||||
userTypeName: userInfo.user_type_name || undefined,
|
||||
partnerObjid: userInfo.partner_objid || undefined,
|
||||
authName: authInfo.length > 0 ? authInfo[0].auth_name : undefined,
|
||||
companyCode: userInfo.company_code || "ILSHIN",
|
||||
};
|
||||
|
||||
logger.info(`사용자 정보 조회 완료: ${userId}`);
|
||||
return personBean;
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`사용자 정보 조회 중 오류 발생: ${error instanceof Error ? error.message : error}`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* JWT 토큰으로 사용자 정보 조회
|
||||
*/
|
||||
static async getUserInfoFromToken(token: string): Promise<PersonBean | null> {
|
||||
try {
|
||||
const userInfo = JwtUtils.verifyToken(token);
|
||||
return userInfo;
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`토큰에서 사용자 정보 조회 중 오류 발생: ${error instanceof Error ? error.message : error}`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 로그인 프로세스 전체 처리
|
||||
*/
|
||||
static async processLogin(
|
||||
userId: string,
|
||||
password: string,
|
||||
remoteAddr: string
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
userInfo?: PersonBean;
|
||||
token?: string;
|
||||
errorReason?: string;
|
||||
}> {
|
||||
try {
|
||||
// 1. 로그인 검증
|
||||
const loginResult = await this.loginPwdCheck(userId, password);
|
||||
|
||||
// 2. 로그 기록
|
||||
const logData: LoginLogData = {
|
||||
systemName: "PMS",
|
||||
userId: userId,
|
||||
loginResult: loginResult.loginResult,
|
||||
errorMessage: loginResult.errorReason,
|
||||
remoteAddr: remoteAddr,
|
||||
};
|
||||
|
||||
await this.insertLoginAccessLog(logData);
|
||||
|
||||
if (loginResult.loginResult) {
|
||||
// 3. 사용자 정보 조회
|
||||
const userInfo = await this.getUserInfo(userId);
|
||||
if (!userInfo) {
|
||||
return {
|
||||
success: false,
|
||||
errorReason: "사용자 정보를 조회할 수 없습니다.",
|
||||
};
|
||||
}
|
||||
|
||||
// 4. JWT 토큰 생성
|
||||
const token = JwtUtils.generateToken(userInfo);
|
||||
|
||||
logger.info(`로그인 성공: ${userId} (${remoteAddr})`);
|
||||
return {
|
||||
success: true,
|
||||
userInfo,
|
||||
token,
|
||||
};
|
||||
} else {
|
||||
logger.warn(
|
||||
`로그인 실패: ${userId} - ${loginResult.errorReason} (${remoteAddr})`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
errorReason: loginResult.errorReason,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`로그인 프로세스 중 오류 발생: ${error instanceof Error ? error.message : error}`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
errorReason: "로그인 처리 중 오류가 발생했습니다.",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 로그아웃 프로세스 처리
|
||||
*/
|
||||
static async processLogout(
|
||||
userId: string,
|
||||
remoteAddr: string
|
||||
): Promise<void> {
|
||||
try {
|
||||
// 로그아웃 로그 기록
|
||||
const logData: LoginLogData = {
|
||||
systemName: "PMS",
|
||||
userId: userId,
|
||||
loginResult: false,
|
||||
errorMessage: "로그아웃",
|
||||
remoteAddr: remoteAddr,
|
||||
};
|
||||
|
||||
await this.insertLoginAccessLog(logData);
|
||||
logger.info(`로그아웃 완료: ${userId} (${remoteAddr})`);
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`로그아웃 처리 중 오류 발생: ${error instanceof Error ? error.message : error}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user