import { SessionData } from '@/types/Auth' import { CommonCode, InquiryRequest } from '@/types/Inquiry' import { ERROR_MESSAGE } from '@/hooks/useAlertMsg' import { HttpStatusCode } from 'axios' import { ApiError } from 'next/dist/server/api-utils' import { prisma } from '@/libs/prisma' import { sendEmail } from '@/libs/mailer' export class QnaService { private session?: SessionData constructor(session?: SessionData) { this.session = session } /** * @description API ROUTE 에러 처리 * @param {any} error 에러 객체 * @returns {ApiError} 에러 객체 */ private handleRouteError(error: any): ApiError { if (error === undefined) return new ApiError(HttpStatusCode.InternalServerError, ERROR_MESSAGE.SERVER_ERROR) console.error('❌ API ROUTE ERROR : ', error) return new ApiError(error.response.status ?? HttpStatusCode.InternalServerError, error.response.data.result.message ?? ERROR_MESSAGE.FETCH_ERROR) } /** * @description 비동기 함수 try-catch 처리 함수 * @param {() => Promise} func 비동기 함수 * @returns {Promise} 에러 객체 또는 함수 결과 */ async tryFunction(func: () => Promise, shouldThrowResult?: boolean): Promise { if (this.session !== undefined && !this.session?.isLoggedIn) { return new ApiError(HttpStatusCode.Unauthorized, ERROR_MESSAGE.UNAUTHORIZED) } try { const response = await func() if (shouldThrowResult) return response return this.handleResult(response) } catch (error) { return this.handleRouteError(error) } } /** * @description 함수 결과 처리 함수 * @param {any} result 함수 결과 * @returns {ApiError | any} 에러 객체 또는 함수 결과 */ private handleResult(response: any): ApiError | any { if (response.status === HttpStatusCode.Ok) { if (response.data.data !== null) return response.data return new ApiError(HttpStatusCode.NotFound, ERROR_MESSAGE.NOT_FOUND) } return new ApiError(response.result.code, response.result.message) } /** * @description 문의 유형 타입 목록 조회 * @param {string[]} responseList 문의 유형 타입 목록 * @returns {CommonCode[]} 문의 유형 타입 목록 */ getInquiryTypeList(responseList: string[]): CommonCode[] { const codeList: CommonCode[] = [] responseList.forEach((item: any) => { if (item.headCd === '204200' || item.headCd === '204300' || item.headCd === '204400') { codeList.push({ headCd: item.headCd, code: item.code, name: item.codeJp, refChar1: item.refChr1, }) } }) return codeList } /** * @description 문의 목록 조회 파라미터 처리 * @param {URLSearchParams} searchParams URLSearchParams 객체 * @returns {Record} 문의 목록 조회 파라미터 */ getSearchParams(searchParams: URLSearchParams): Record { const params: Record = {} searchParams.forEach((value, key) => { const match = key.match(/inquiryListRequest\[(.*)\]/) if (match) { params[match[1]] = value } else { params[key] = value } }) return params } /** * @description 문의 메일 발송 * @param {FormData} formData 문의 메일 발송 요청 파라미터 - qnaClsLrgCd, title, contents, files * @returns {ApiError | null} 에러 객체 또는 null */ async sendMail(formData: FormData): Promise { const receivers: string[] = await this.getReceiver(formData.get('qnaClsLrgCd') as string) if (receivers.length === 0) { return new ApiError(HttpStatusCode.InternalServerError, ERROR_MESSAGE.EMAIL_SEND_ERROR) } const files = formData.getAll('files') as File[] const fileBuffers = await Promise.all(files.map((file) => file.arrayBuffer())) const attachments = files.map((file, index) => ({ filename: file.name, content: Buffer.from(fileBuffers[index]), contentType: file.type || 'application/octet-stream', })) return this.tryFunction(() => { return sendEmail({ from: 'test@test.com', to: receivers, subject: `[HANASYS お問い合わせ] ${formData.get('title')}`, content: this.generateEmailContent(formData), attachments: attachments, }) }, true) } /** * @description 문의 수신자 조회 * @param {string} qnaClsLrgCd 문의 유형 대분류 코드 * @returns {string[]} 문의 수신자 목록 */ async getReceiver(qnaClsLrgCd: string): Promise { const query = ` OPEN SYMMETRIC KEY SYMMETRICKEY DECRYPTION BY CERTIFICATE CERTI_QSPJP; SELECT CONVERT(NVARCHAR(100), DecryptByKey(bu.e_mail)) AS email FROM BC_USER bu WHERE bu.user_id IN ( SELECT user_id FROM SY_POLICY_U spu WHERE policy_cd = ( SELECT bcl.ref_chr2 FROM BC_COMM_L bcl WHERE bcl.head_cd = (SELECT head_cd FROM BC_COMM_H bch WHERE head_id = 'QNA_CLS_MID_CD') AND bcl.ref_chr1 = '${qnaClsLrgCd}' GROUP BY bcl.ref_chr2 ) ) CLOSE SYMMETRIC KEY SYMMETRICKEY;` const receivers: { email: string }[] = await prisma.$queryRawUnsafe(query) return receivers.map((receiver) => receiver.email) } /** * @description 문의 이메일 내용 생성 * @param {FormData} formData 문의 메일 발송 요청 파라미터 - qnaClsLrgCd, title, contents, files * @returns {string} 문의 이메일 내용 */ generateEmailContent = (formData: FormData) => `

お問い合わせが登録されました。
{ QSP > System mgt. > お問い合わせ } でお問い合わせ内容を確認してください。

-登録者: ${formData.get('regUserNm')}

-販売店ID: ${formData.get('storeId')}

-販売店名: ${this.session?.storeNm ?? ' - '}

-お問い合わせ内容:

${formData.get('contents')}

` }