사용자 검색 기능 구현
This commit is contained in:
@@ -174,13 +174,30 @@ export const getUserList = async (req: AuthenticatedRequest, res: Response) => {
|
||||
user: req.user,
|
||||
});
|
||||
|
||||
const { page = 1, countPerPage = 20, search, deptCode, status } = req.query;
|
||||
const {
|
||||
page = 1,
|
||||
countPerPage = 20,
|
||||
// 통합 검색 (전체 필드 대상)
|
||||
search,
|
||||
// 고급 검색 (개별 필드별)
|
||||
searchField,
|
||||
searchValue,
|
||||
search_sabun,
|
||||
search_companyName,
|
||||
search_deptName,
|
||||
search_positionName,
|
||||
search_userId,
|
||||
search_userName,
|
||||
search_tel,
|
||||
search_email,
|
||||
// 기존 필터
|
||||
deptCode,
|
||||
status,
|
||||
} = req.query;
|
||||
|
||||
// PostgreSQL 클라이언트 생성
|
||||
const client = new Client({
|
||||
connectionString:
|
||||
process.env.DATABASE_URL ||
|
||||
"postgresql://postgres:postgres@localhost:5432/ilshin",
|
||||
connectionString: config.databaseUrl,
|
||||
});
|
||||
|
||||
await client.connect();
|
||||
@@ -214,27 +231,109 @@ export const getUserList = async (req: AuthenticatedRequest, res: Response) => {
|
||||
|
||||
const queryParams: any[] = [];
|
||||
let paramIndex = 1;
|
||||
let searchType = "none";
|
||||
|
||||
// 검색 조건 처리
|
||||
if (search && typeof search === "string" && search.trim()) {
|
||||
// 통합 검색 (우선순위: 모든 주요 필드에서 검색)
|
||||
searchType = "unified";
|
||||
const searchTerm = search.trim();
|
||||
|
||||
// 검색 조건 추가
|
||||
if (search) {
|
||||
query += ` AND (
|
||||
u.user_name ILIKE $${paramIndex} OR
|
||||
u.user_id ILIKE $${paramIndex} OR
|
||||
u.sabun ILIKE $${paramIndex} OR
|
||||
u.email ILIKE $${paramIndex}
|
||||
UPPER(COALESCE(u.sabun, '')) LIKE UPPER($${paramIndex}) OR
|
||||
UPPER(COALESCE(u.user_type_name, '')) LIKE UPPER($${paramIndex}) OR
|
||||
UPPER(COALESCE(u.dept_name, '')) LIKE UPPER($${paramIndex}) OR
|
||||
UPPER(COALESCE(u.position_name, '')) LIKE UPPER($${paramIndex}) OR
|
||||
UPPER(COALESCE(u.user_id, '')) LIKE UPPER($${paramIndex}) OR
|
||||
UPPER(COALESCE(u.user_name, '')) LIKE UPPER($${paramIndex}) OR
|
||||
UPPER(COALESCE(u.tel, '')) LIKE UPPER($${paramIndex}) OR
|
||||
UPPER(COALESCE(u.cell_phone, '')) LIKE UPPER($${paramIndex}) OR
|
||||
UPPER(COALESCE(u.email, '')) LIKE UPPER($${paramIndex})
|
||||
)`;
|
||||
queryParams.push(`%${search}%`);
|
||||
queryParams.push(`%${searchTerm}%`);
|
||||
paramIndex++;
|
||||
|
||||
logger.info("통합 검색 실행", { searchTerm });
|
||||
} else if (searchField && searchValue) {
|
||||
// 단일 필드 검색
|
||||
searchType = "single";
|
||||
const fieldMap: { [key: string]: string } = {
|
||||
sabun: "u.sabun",
|
||||
companyName: "u.user_type_name",
|
||||
deptName: "u.dept_name",
|
||||
positionName: "u.position_name",
|
||||
userId: "u.user_id",
|
||||
userName: "u.user_name",
|
||||
tel: "u.tel",
|
||||
cellPhone: "u.cell_phone",
|
||||
email: "u.email",
|
||||
};
|
||||
|
||||
if (fieldMap[searchField as string]) {
|
||||
if (searchField === "tel") {
|
||||
// 전화번호는 TEL과 CELL_PHONE 모두 검색
|
||||
query += ` AND (UPPER(u.tel) LIKE UPPER($${paramIndex}) OR UPPER(u.cell_phone) LIKE UPPER($${paramIndex}))`;
|
||||
} else {
|
||||
query += ` AND UPPER(${fieldMap[searchField as string]}) LIKE UPPER($${paramIndex})`;
|
||||
}
|
||||
queryParams.push(`%${searchValue}%`);
|
||||
paramIndex++;
|
||||
|
||||
logger.info("단일 필드 검색 실행", { searchField, searchValue });
|
||||
}
|
||||
} else {
|
||||
// 고급 검색 (개별 필드별 AND 조건)
|
||||
const advancedSearchFields = [
|
||||
{ param: search_sabun, field: "u.sabun" },
|
||||
{ param: search_companyName, field: "u.user_type_name" },
|
||||
{ param: search_deptName, field: "u.dept_name" },
|
||||
{ param: search_positionName, field: "u.position_name" },
|
||||
{ param: search_userId, field: "u.user_id" },
|
||||
{ param: search_userName, field: "u.user_name" },
|
||||
{ param: search_email, field: "u.email" },
|
||||
];
|
||||
|
||||
let hasAdvancedSearch = false;
|
||||
|
||||
for (const { param, field } of advancedSearchFields) {
|
||||
if (param && typeof param === "string" && param.trim()) {
|
||||
query += ` AND UPPER(${field}) LIKE UPPER($${paramIndex})`;
|
||||
queryParams.push(`%${param.trim()}%`);
|
||||
paramIndex++;
|
||||
hasAdvancedSearch = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 전화번호 검색 (TEL 또는 CELL_PHONE)
|
||||
if (search_tel && typeof search_tel === "string" && search_tel.trim()) {
|
||||
query += ` AND (UPPER(u.tel) LIKE UPPER($${paramIndex}) OR UPPER(u.cell_phone) LIKE UPPER($${paramIndex}))`;
|
||||
queryParams.push(`%${search_tel.trim()}%`);
|
||||
paramIndex++;
|
||||
hasAdvancedSearch = true;
|
||||
}
|
||||
|
||||
if (hasAdvancedSearch) {
|
||||
searchType = "advanced";
|
||||
logger.info("고급 검색 실행", {
|
||||
search_sabun,
|
||||
search_companyName,
|
||||
search_deptName,
|
||||
search_positionName,
|
||||
search_userId,
|
||||
search_userName,
|
||||
search_tel,
|
||||
search_email,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 부서 코드 필터
|
||||
// 기존 필터들
|
||||
if (deptCode) {
|
||||
query += ` AND u.dept_code = $${paramIndex}`;
|
||||
queryParams.push(deptCode);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
// 상태 필터
|
||||
if (status) {
|
||||
query += ` AND u.status = $${paramIndex}`;
|
||||
queryParams.push(status);
|
||||
@@ -244,26 +343,15 @@ export const getUserList = async (req: AuthenticatedRequest, res: Response) => {
|
||||
// 정렬
|
||||
query += ` ORDER BY u.regdate DESC, u.user_name`;
|
||||
|
||||
// 총 개수 조회
|
||||
const countQuery = `
|
||||
SELECT COUNT(*) as total
|
||||
FROM user_info u
|
||||
WHERE 1=1
|
||||
${
|
||||
search
|
||||
? `AND (
|
||||
u.user_name ILIKE $1 OR
|
||||
u.user_id ILIKE $1 OR
|
||||
u.sabun ILIKE $1 OR
|
||||
u.email ILIKE $1
|
||||
)`
|
||||
: ""
|
||||
}
|
||||
${deptCode ? `AND u.dept_code = $${search ? 2 : 1}` : ""}
|
||||
${status ? `AND u.status = $${search ? (deptCode ? 3 : 2) : deptCode ? 2 : 1}` : ""}
|
||||
`;
|
||||
// 페이징 파라미터 제외한 카운트용 파라미터
|
||||
const countParams = [...queryParams];
|
||||
|
||||
// 총 개수 조회를 위해 기존 쿼리를 COUNT로 변환
|
||||
const countQuery = query.replace(
|
||||
/SELECT[\s\S]*?FROM/i,
|
||||
"SELECT COUNT(*) as total FROM"
|
||||
).replace(/ORDER BY.*$/i, "");
|
||||
|
||||
const countParams = queryParams.slice(0, -2); // 페이징 파라미터 제외
|
||||
const countResult = await client.query(countQuery, countParams);
|
||||
const totalCount = parseInt(countResult.rows[0].total);
|
||||
|
||||
@@ -360,14 +448,13 @@ export const getUserList = async (req: AuthenticatedRequest, res: Response) => {
|
||||
|
||||
const response = {
|
||||
success: true,
|
||||
data: {
|
||||
users: processedUsers,
|
||||
pagination: {
|
||||
currentPage: Number(page),
|
||||
countPerPage: Number(countPerPage),
|
||||
totalCount: totalCount,
|
||||
totalPages: Math.ceil(totalCount / Number(countPerPage)),
|
||||
},
|
||||
data: processedUsers,
|
||||
total: totalCount,
|
||||
searchType, // 검색 타입 정보 (unified, single, advanced, none)
|
||||
pagination: {
|
||||
page: Number(page),
|
||||
limit: Number(countPerPage),
|
||||
totalPages: Math.ceil(totalCount / Number(countPerPage)),
|
||||
},
|
||||
message: "사용자 목록 조회 성공",
|
||||
};
|
||||
@@ -375,6 +462,7 @@ export const getUserList = async (req: AuthenticatedRequest, res: Response) => {
|
||||
logger.info("사용자 목록 조회 성공", {
|
||||
totalCount,
|
||||
returnedCount: processedUsers.length,
|
||||
searchType,
|
||||
currentPage: Number(page),
|
||||
countPerPage: Number(countPerPage),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user