import { verifySession } from '@/lib/auth/dal'; import { fetchCommonCodes } from '@/lib/data/repositories/common-code-repository'; import { fetchAllMerchants } from '@/lib/data/repositories/merchant-repository'; import { CODE_GROUP } from '@/lib/domain/common-code'; import { formatCategoryLabel, formatVisibilityLabel } from '@/lib/domain/merchant'; /** * 가맹점 목록 엑셀 다운로드(기획 MCH_001 ②) — 검색 조건과 무관하게 전체다. * * TODO(백엔드): 서버가 xlsx를 만들어 주면 학생 목록처럼 스트림 중계로 바꾼다. 지금은 백엔드 * 자체가 없어 여기서 CSV를 만든다. */ const HEADERS = [ '번호', '가맹점코드', '브랜드명', '가맹점명', '업종', '사업자등록번호', '대표자명', '전화번호', '주소', '상세주소', '우편번호', '사용여부', '메모', '등록일', ]; /** 쉼표·따옴표·줄바꿈이 든 값은 따옴표로 감싸고 내부 따옴표는 두 번 쓴다(RFC 4180). */ function toCsvCell(value: string): string { return /[",\n]/.test(value) ? `"${value.replace(/"/g, '""')}"` : value; } export async function GET() { await verifySession(); const [merchants, business, businessDetail] = await Promise.all([ fetchAllMerchants(), fetchCommonCodes(CODE_GROUP.merchantBusiness), fetchCommonCodes(CODE_GROUP.merchantBusinessDetail), ]); const businessCodes = [...business, ...businessDetail]; const rows = merchants.map((merchant, index) => [ String(index + 1), merchant.code, merchant.brandName, merchant.name, formatCategoryLabel(businessCodes, merchant.categoryCode), merchant.businessNumber, merchant.ownerName, merchant.phoneNumber, merchant.address, merchant.addressDetail, merchant.postalCode, formatVisibilityLabel(merchant.isVisible), merchant.memo, merchant.createdAt, ] .map(toCsvCell) .join(',') ); // 엑셀이 UTF-8을 알아보게 BOM을 앞에 붙인다 — 없으면 한글이 깨진다. const csv = `${[HEADERS.join(','), ...rows].join('\r\n')}`; return new Response(csv, { status: 200, headers: { 'Content-Type': 'text/csv; charset=utf-8', 'Content-Disposition': 'attachment; filename="merchants.csv"', }, }); }