import { NextResponse } from 'next/server' import { prisma } from '@/libs/prisma' import { convertToSnakeCase } from '@/utils/common-utils' /** * 검색 파라미터 */ type SearchParams = { keyword?: string | null // 검색어 searchOption?: string | null // 검색 옵션 (select 옵션) isMySurvey?: string | null // 내가 작성한 매물 sort?: string | null // 정렬 방식 offset?: string | null role?: string | null // 회원권한한 store?: string | null // 판매점ID builderNo?: string | null // 시공ID } type WhereCondition = { AND: any[] OR?: any[] [key: string]: any } // 검색 가능한 필드 옵션 const SEARCH_OPTIONS = [ 'BUILDING_NAME', // 건물명 'REPRESENTATIVE', // 담당자 'STORE', // 판매점 'CONSTRUCTION_POINT', // 시공점 'CUSTOMER_NAME', // 고객명 'POST_CODE', // 우편번호 'ADDRESS', // 주소 'ADDRESS_DETAIL', // 상세주소 'SRL_NO', // 등록번호 ] as const // 페이지당 항목 수 const ITEMS_PER_PAGE = 10 /** * 키워드 검색 조건 생성 함수 * @param keyword 검색 키워드 * @param searchOption 검색 옵션 * @returns 검색 조건 객체 */ 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 } /** * 회원 역할별 검색 조건 생성 함수 * @param params 검색 파라미터 * @returns 검색 조건 객체 */ const createMemberRoleCondition = (params: SearchParams): WhereCondition => { const where: WhereCondition = { AND: [] } switch (params.role) { case 'Admin': // 1차점 where.OR = [ { // 같은 판매점에서 작성한 제출/제출되지 않은 매물 AND: [{ STORE_ID: { equals: params.store } }], }, { // MUSUBI (시공권한 X) 가 ORDER 에 제출한 매물 AND: [{ SUBMISSION_TARGET_ID: { equals: params.store } }, { SUBMISSION_STATUS: { equals: true } }], }, ] break case 'Admin_Sub': // 2차점 where.OR = [ { // MUSUBI (시공권한 X) 같은 판매점에서 작성한 제출/제출되지 않은 매물 AND: [ { STORE_ID: { equals: params.store } }, // { // OR: [{ CONSTRUCTION_POINT: { equals: null } }, { CONSTRUCTION_POINT: { equals: '' } }], // }, ], }, { // MUSUBI (시공권한 O) 가 MUSUBI 에 제출한 매물 + PARTNER 가 제출한 매물 AND: [ { SUBMISSION_TARGET_ID: { equals: params.store } }, { CONSTRUCTION_POINT: { not: null } }, { CONSTRUCTION_POINT: { not: '' } }, { SUBMISSION_STATUS: { equals: true } }, ], }, ] break case 'Builder': // MUSUBI (시공권한 O) case 'Partner': // PARTNER // 시공점이 있고 STORE_ID 가 시공ID와 같은 매물 where.AND?.push({ // CONSTRUCTION_POINT: { not: null }, CONSTRUCTION_POINT: { equals: params.builderNo }, }) break case 'T01': where.OR = [ { NOT: { SRL_NO: { startsWith: '一時保存', }, }, }, { STORE_ID: { equals: params.store, }, }, ] break case 'User': // 모든 매물 조회 가능 (추가 조건 없음) break } return where } /** * GET 핸들러 - 설문 목록 조회 */ export async function GET(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'), //representativeId sort: searchParams.get('sort'), offset: searchParams.get('offset'), role: searchParams.get('role'), store: searchParams.get('store'), //storeId builderNo: searchParams.get('builderNo'), } // 검색 조건 구성 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(error) return NextResponse.json({ error: 'Fail Read Survey' }, { status: 500 }) } } /** * PUT 핸들러 - 상세 정보 추가 */ export async function PUT(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({ message: 'Success Update Survey', }) } catch (error) { console.error(error) return NextResponse.json({ error: 'Fail Update Survey' }, { status: 500 }) } } export async function POST(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(error) return NextResponse.json({ error: 'Fail Create Survey' }, { status: 500 }) } }