각 회사별 데이터 분리

This commit is contained in:
kjs
2025-10-27 16:40:59 +09:00
parent 783ce5594e
commit 29c49d7f07
59 changed files with 8698 additions and 585 deletions

View File

@@ -195,6 +195,8 @@ export const getUserList = async (req: AuthenticatedRequest, res: Response) => {
search_email,
deptCode,
status,
companyCode, // 회사 코드 필터 추가
size, // countPerPage 대신 사용 가능
} = req.query;
// Raw Query를 사용한 사용자 목록 조회
@@ -203,6 +205,14 @@ export const getUserList = async (req: AuthenticatedRequest, res: Response) => {
let queryParams: any[] = [];
let paramIndex = 1;
// 회사 코드 필터 (권한 그룹 멤버 관리 시 사용)
if (companyCode && typeof companyCode === "string" && companyCode.trim()) {
whereConditions.push(`company_code = $${paramIndex}`);
queryParams.push(companyCode.trim());
paramIndex++;
logger.info("회사 코드 필터 적용", { companyCode });
}
// 검색 조건 처리
if (search && typeof search === "string" && search.trim()) {
// 통합 검색
@@ -303,6 +313,16 @@ export const getUserList = async (req: AuthenticatedRequest, res: Response) => {
}
}
// 현재 로그인한 사용자의 회사 코드 필터 (슈퍼관리자가 아닌 경우)
if (req.user && req.user.companyCode !== "*" && !companyCode) {
whereConditions.push(`company_code = $${paramIndex}`);
queryParams.push(req.user.companyCode);
paramIndex++;
logger.info("사용자 회사 코드 필터 적용", {
companyCode: req.user.companyCode,
});
}
// 기존 필터들
if (deptCode) {
whereConditions.push(`dept_code = $${paramIndex}`);
@@ -331,7 +351,8 @@ export const getUserList = async (req: AuthenticatedRequest, res: Response) => {
const totalCount = parseInt(countResult[0]?.total || "0", 10);
// 사용자 목록 조회
const offset = (Number(page) - 1) * Number(countPerPage);
const limit = size ? Number(size) : Number(countPerPage);
const offset = (Number(page) - 1) * limit;
const usersQuery = `
SELECT
sabun,
@@ -357,11 +378,7 @@ export const getUserList = async (req: AuthenticatedRequest, res: Response) => {
LIMIT $${paramIndex} OFFSET $${paramIndex + 1}
`;
const users = await query<any>(usersQuery, [
...queryParams,
Number(countPerPage),
offset,
]);
const users = await query<any>(usersQuery, [...queryParams, limit, offset]);
// 응답 데이터 가공
const processedUsers = users.map((user) => ({
@@ -393,8 +410,8 @@ export const getUserList = async (req: AuthenticatedRequest, res: Response) => {
searchType,
pagination: {
page: Number(page),
limit: Number(countPerPage),
totalPages: Math.ceil(totalCount / Number(countPerPage)),
limit: limit,
totalPages: Math.ceil(totalCount / limit),
},
message: "사용자 목록 조회 성공",
};
@@ -404,7 +421,8 @@ export const getUserList = async (req: AuthenticatedRequest, res: Response) => {
returnedCount: processedUsers.length,
searchType,
currentPage: Number(page),
countPerPage: Number(countPerPage),
limit: limit,
companyCode: companyCode || "all",
});
res.status(200).json(response);
@@ -1379,7 +1397,7 @@ export const getDepartmentList = async (
// 회사 코드 필터
if (companyCode) {
whereConditions.push(`company_name = $${paramIndex}`);
whereConditions.push(`company_code = $${paramIndex}`);
queryParams.push(companyCode);
paramIndex++;
}
@@ -1420,6 +1438,7 @@ export const getDepartmentList = async (
data_type,
status,
sales_yn,
company_code,
company_name
FROM dept_info
${whereClause}
@@ -1445,6 +1464,7 @@ export const getDepartmentList = async (
dataType: dept.data_type,
status: dept.status || "active",
salesYn: dept.sales_yn,
companyCode: dept.company_code,
companyName: dept.company_name,
children: [],
});
@@ -1480,6 +1500,7 @@ export const getDepartmentList = async (
dataType: dept.data_type,
status: dept.status || "active",
salesYn: dept.sales_yn,
companyCode: dept.company_code,
companyName: dept.company_name,
})),
},
@@ -1947,10 +1968,23 @@ export const changeUserStatus = async (
export const saveUser = async (req: AuthenticatedRequest, res: Response) => {
try {
const userData = req.body;
logger.info("사용자 저장 요청", { userData, user: req.user });
const isUpdate = req.method === "PUT"; // PUT 요청이면 수정
logger.info("사용자 저장 요청", {
userData,
user: req.user,
isUpdate,
method: req.method,
});
// 필수 필드 검증
const requiredFields = ["userId", "userName", "userPassword"];
let requiredFields = ["userId", "userName"];
// 신규 등록 시에만 비밀번호 필수
if (!isUpdate) {
requiredFields.push("userPassword");
}
for (const field of requiredFields) {
if (!userData[field] || userData[field].trim() === "") {
res.status(400).json({
@@ -1965,10 +1999,15 @@ export const saveUser = async (req: AuthenticatedRequest, res: Response) => {
}
}
// 비밀번호 암호화
const encryptedPassword = await EncryptUtil.encrypt(userData.userPassword);
// 비밀번호 암호화 (비밀번호가 제공된 경우에만)
let encryptedPassword = null;
if (userData.userPassword) {
encryptedPassword = await EncryptUtil.encrypt(userData.userPassword);
}
// Raw Query를 사용한 사용자 저장 (upsert with ON CONFLICT)
const updatePasswordClause = encryptedPassword ? "user_password = $4," : "";
const [savedUser] = await query<any>(
`INSERT INTO user_info (
user_id, user_name, user_name_eng, user_password,
@@ -1979,7 +2018,7 @@ export const saveUser = async (req: AuthenticatedRequest, res: Response) => {
ON CONFLICT (user_id) DO UPDATE SET
user_name = $2,
user_name_eng = $3,
user_password = $4,
${updatePasswordClause}
dept_code = $5,
dept_name = $6,
position_code = $7,
@@ -1998,7 +2037,7 @@ export const saveUser = async (req: AuthenticatedRequest, res: Response) => {
userData.userId,
userData.userName,
userData.userNameEng || null,
encryptedPassword,
encryptedPassword || "", // 빈 문자열로 넣되, UPDATE에서는 조건부로 제외
userData.deptCode || null,
userData.deptName || null,
userData.positionCode || null,
@@ -2017,23 +2056,26 @@ export const saveUser = async (req: AuthenticatedRequest, res: Response) => {
);
// 기존 사용자인지 새 사용자인지 확인 (regdate로 판단)
const isUpdate =
const isExistingUser =
savedUser.regdate &&
new Date(savedUser.regdate).getTime() < Date.now() - 1000;
logger.info(isUpdate ? "사용자 정보 수정 완료" : "새 사용자 등록 완료", {
userId: userData.userId,
});
logger.info(
isExistingUser ? "사용자 정보 수정 완료" : "새 사용자 등록 완료",
{
userId: userData.userId,
}
);
const response = {
success: true,
result: true,
message: isUpdate
message: isExistingUser
? "사용자 정보가 수정되었습니다."
: "사용자가 등록되었습니다.",
data: {
userId: userData.userId,
isUpdate,
isUpdate: isExistingUser,
},
};