399 lines
11 KiB
TypeScript

import { NextResponse } from 'next/server'
import { prisma } from '@/libs/prisma'
import { convertToSnakeCase, ERROR_MESSAGES } from '@/utils/common-utils'
import { loggerWrapper } from '@/libs/api-wrapper'
import { HttpStatusCode } from 'axios'
/**
* @description 검색 파라미터 타입
*/
type SearchParams = {
keyword?: string | null
searchOption?: string | null
isMySurvey?: string | null
sort?: string | null
offset?: string | null
role?: string | null
storeId?: string | null
builderId?: string | null
}
type WhereCondition = {
AND: any[]
OR?: any[]
[key: string]: any
}
/** 검색 가능한 필드 옵션 */
const SEARCH_OPTIONS = [
'BUILDING_NAME',
'REPRESENTATIVE',
'STORE',
'STORE_ID',
'CONSTRUCTION_POINT',
'CONSTRUCTION_POINT_ID',
'CUSTOMER_NAME',
'POST_CODE',
'ADDRESS',
'ADDRESS_DETAIL',
'SRL_NO',
] as const
/** 페이지당 기본 항목 수 */
const ITEMS_PER_PAGE = 10
/**
* @description 키워드 검색 조건 생성 함수
* @param {string} keyword 검색 키워드
* @param {string} searchOption 검색 옵션
* @returns {WhereCondition} 검색 조건 객체
*/
const createKeywordSearchCondition = (keyword: string, searchOption: string): WhereCondition => {
const where: WhereCondition = { AND: [] }
if (searchOption === 'all') {
/** 모든 필드 검색 시 OR 조건 사용 */
where.OR = []
where.OR.push(
...SEARCH_OPTIONS.map((field) => ({
[field]: { contains: keyword },
})),
)
} else if (SEARCH_OPTIONS.includes(searchOption.toUpperCase() as any)) {
/** 특정 필드 검색 */
where[searchOption.toUpperCase()] = { contains: keyword }
}
return where
}
/**
* @description 회원 역할별 검색 조건 생성 함수
* @param {SearchParams} params 검색 파라미터
* @returns {WhereCondition} 검색 조건 객체
*/
const createMemberRoleCondition = (params: SearchParams): WhereCondition => {
const where: WhereCondition = { AND: [] }
switch (params.role) {
case 'Admin':
where.OR = [
{
AND: [{ STORE_ID: { equals: params.storeId } }],
},
{
AND: [{ SUBMISSION_TARGET_ID: { equals: params.storeId } }, { SUBMISSION_STATUS: { equals: true } }],
},
]
break
case 'Admin_Sub':
where.OR = [
{
AND: [{ STORE_ID: { equals: params.storeId } }, { CONSTRUCTION_POINT_ID: { equals: params.builderId } }],
},
{
AND: [
{ SUBMISSION_TARGET_ID: { equals: params.storeId } },
{ CONSTRUCTION_POINT_ID: { not: null } },
{ CONSTRUCTION_POINT_ID: { not: '' } },
{ SUBMISSION_STATUS: { equals: true } },
],
},
]
break
case 'Builder':
case 'Partner':
where.AND?.push({
CONSTRUCTION_POINT_ID: { equals: params.builderId },
})
break
case 'T01':
where.OR = [
{
NOT: {
SRL_NO: {
startsWith: '一時保存',
},
},
},
{
STORE_ID: {
equals: params.storeId,
},
},
]
break
case 'User':
break
}
return where
}
/**
* @description 권한 별 필수 값 존재 여부 확인, 없을 시 빈 데이터 반환
* @param {SearchParams} params 검색 파라미터
* @returns {NextResponse} 세션 체크 결과
*/
const checkSession = (params: SearchParams) => {
if (params.role === null) {
return NextResponse.json({ data: [], count: 0 })
}
if (params.role === 'Builder' || params.role === 'Partner') {
if (params.builderId === null) {
return NextResponse.json({ data: [], count: 0 })
}
} else {
if (params.storeId === null) {
return NextResponse.json({ data: [], count: 0 })
}
}
return null
}
/**
* @api {GET} /api/survey-sales 설문 목록 조회 API
* @apiName GET /api/survey-sales
* @apiGroup SurveySales
* @apiDescription 설문 목록 조회 API
*
* @apiParam {String} keyword 검색어 (optional)
* @apiParam {String} searchOption 검색 옵션 (optional)
* @apiParam {String} isMySurvey 내가 작성한 매물 (optional)
* @apiParam {String} sort 정렬 방식 (optional)
* @apiParam {String} offset 페이지 오프셋 (optional)
* @apiParam {String} role 회원권한 (optional)
* @apiParam {String} storeId 판매점ID (optional)
* @apiParam {String} builderId 시공점ID (optional)
*
* @apiSuccess {Object[]} data 설문 목록 데이터
* @apiSuccess {Number} data.count 설문 목록 개수
*
* @apiExample {curl} Example usage:
* curl -X GET \
* -G "keyword=test&searchOption=all&isMySurvey=true&sort=created&offset=0&role=Admin&storeId=1234567890&builderId=1234567890" \
* http://localhost:3000/api/survey-sales
*
* @apiSuccessExample {json} Success-Response:
* {
* "data": [
* {
* "id": 1,
* "srlNo": "1234567890",
* "storeId": "1234567890",
* "detailInfo": {
* "id": 1,
* "memo": "1234567890",
* ...
* }
* ...
* }
* ],
* "count": 1
* }
*
*/
async function getSurveySales(request: Request) {
try {
/** URL 파라미터 파싱 */
const { searchParams } = new URL(request.url)
const params: SearchParams = {
keyword: searchParams.get('keyword'),
searchOption: searchParams.get('searchOption'),
isMySurvey: searchParams.get('isMySurvey'),
sort: searchParams.get('sort'),
offset: searchParams.get('offset'),
role: searchParams.get('role'),
storeId: searchParams.get('storeId'),
builderId: searchParams.get('builderId'),
}
/** 세션 체크 결과 처리 */
const sessionCheckResult = checkSession(params)
if (sessionCheckResult) {
return sessionCheckResult
}
/** 검색 조건 구성 */
const where: WhereCondition = { AND: [] }
/** 내가 작성한 매물 조건 적용 */
if (params.isMySurvey) {
where.AND.push({ REPRESENTATIVE_ID: params.isMySurvey })
}
/** 키워드 검색 조건 적용 */
if (params.keyword && params.searchOption) {
where.AND.push(createKeywordSearchCondition(params.keyword, params.searchOption))
}
/** 회원 유형 조건 적용 */
const roleCondition = createMemberRoleCondition(params)
if (Object.keys(roleCondition).length > 0) {
where.AND.push(roleCondition)
}
/** 페이지네이션 데이터 조회 */
//@ts-ignore
const surveys = await prisma.SD_SURVEY_SALES_BASIC_INFO.findMany({
where,
orderBy: params.sort === 'created' ? { REG_DT: 'desc' } : { UPT_DT: 'desc' },
skip: Number(params.offset),
take: ITEMS_PER_PAGE,
})
/** 전체 개수만 조회 */
//@ts-ignore
const count = await prisma.SD_SURVEY_SALES_BASIC_INFO.count({ where })
return NextResponse.json({ data: { data: surveys, count: count } })
} catch (error) {
console.error('❌ API ROUTE ERROR:', error)
return NextResponse.json({ error: ERROR_MESSAGES.FETCH_ERROR }, { status: HttpStatusCode.InternalServerError })
}
}
/**
* @api {PUT} /api/survey-sales 설문 상세 정보 추가 API
* @apiName PUT /api/survey-sales
* @apiGroup SurveySales
* @apiDescription 설문 상세 정보 추가 API
*
* @apiParam {Number} id 설문 목록 ID (required)
* @apiBody {Object} detail_info 상세 정보 (required)
*
* @apiSuccess {String} message 성공 메시지
*
* @apiExample {curl} Example usage:
* curl -X PUT \
* -H "Content-Type: application/json" \
* -d '{"id": 1, "detail_info": {"memo": "1234567890"}}' \
* http://localhost:3000/api/survey-sales
*
* @apiSuccessExample {json} Success-Response:
* {
* "message": "Success Update Survey"
* }
*
* @apiError {Number} 500 서버 오류
*/
async function updateSurveySales(request: Request) {
try {
/** 요청 바디 파싱 */
const body = await request.json()
/** 상세 정보 생성을 위한 데이터 구성 */
const detailInfo = {
...body.detail_info,
BASIC_INFO_ID: body.id,
}
/** 상세 정보 생성 */
//@ts-ignore
await prisma.SD_SURVEY_SALES_DETAIL_INFO.create({
data: detailInfo,
})
return NextResponse.json({ status: HttpStatusCode.Ok })
} catch (error) {
console.error('❌ API ROUTE ERROR:', error)
return NextResponse.json({ error: ERROR_MESSAGES.FETCH_ERROR }, { status: HttpStatusCode.InternalServerError })
}
}
/**
* @api {POST} /api/survey-sales 설문 상세 정보 추가 API
* @apiName POST /api/survey-sales
* @apiGroup SurveySales
* @apiDescription 설문 상세 정보 추가 API
*
* @apiParam {Object} survey 설문 목록 데이터 (required)
* @apiParam {String} role 회원권한 (required)
* @apiParam {String} storeId 판매점ID (required)
* @returns
*
* @apiSuccess {Object} data 설문 목록 데이터
*
* @apiExample {curl} Example usage:
* curl -X POST \
* -H "Content-Type: application/json" \
* -d '{"survey": {"srlNo": "1234567890", "storeId": "1234567890", "role": "T01", "detail_info": {"memo": "1234567890"}}}' \
* http://localhost:3000/api/survey-sales
*
* @apiSuccessExample {json} Success-Response:
* {
* "data": {
* "id": 1,
* "srlNo": "1234567890",
* "storeId": "1234567890",
* "detailInfo": {
* "id": 1,
* "memo": "1234567890",
* ...
* }
* ...
* }
* }
*
* @apiError {Number} 500 서버 오류
*/
async function createSurveySales(request: Request) {
try {
const body = await request.json()
const role =
body.role === 'T01' || body.role === 'Admin'
? 'HO'
: body.role === 'Admin_Sub' || body.role === 'Builder'
? 'HM'
: body.role === 'Partner'
? ''
: null
/** 임시 저장 시 임시저장으로 저장 */
/** 기본 저장 시 (HO/HM) + 판매점ID + yyMMdd + 000 으로 저장 */
const baseSrlNo =
body.survey.srlNo ??
role +
body.storeId +
new Date().getFullYear().toString().slice(-2) +
(new Date().getMonth() + 1).toString().padStart(2, '0') +
new Date().getDate().toString().padStart(2, '0')
// @ts-ignore
const lastSurvey = await prisma.SD_SURVEY_SALES_BASIC_INFO.findFirst({
where: {
SRL_NO: {
startsWith: role + body.storeId,
},
},
orderBy: {
SRL_NO: 'desc',
},
})
/** 마지막 번호 추출 */
const lastNumber = lastSurvey ? parseInt(lastSurvey.SRL_NO.slice(-3)) : 0
/** 새로운 srlNo 생성 - 임시저장일 경우 '임시저장' 으로 저장 */
const newSrlNo = baseSrlNo.startsWith('一時保存') ? baseSrlNo : baseSrlNo + (lastNumber + 1).toString().padStart(3, '0')
const { detailInfo, ...basicInfo } = body.survey
// @ts-ignore
const result = await prisma.SD_SURVEY_SALES_BASIC_INFO.create({
data: {
...convertToSnakeCase(basicInfo),
SRL_NO: newSrlNo,
DETAIL_INFO: {
create: convertToSnakeCase(detailInfo),
},
},
})
return NextResponse.json(result)
} catch (error) {
console.error('❌ API ROUTE ERROR:', error)
return NextResponse.json({ error: ERROR_MESSAGES.FETCH_ERROR }, { status: HttpStatusCode.InternalServerError })
}
}
export const GET = loggerWrapper(getSurveySales)
export const PUT = loggerWrapper(updateSurveySales)
export const POST = loggerWrapper(createSurveySales)