"use server"; import { revalidateTag, unstable_noStore } from "next/cache"; import db from "@/db/db"; import { unstable_cache } from "@/lib/unstable-cache"; import { filterColumns } from "@/lib/filter-columns"; import { tagTypeClassFormMappings } from "@/db/schema/vendorData"; import { asc, desc, ilike, inArray, and, gte, lte, not, or, eq } from "drizzle-orm"; import { countProjectLists, selectProjectLists } from "./repository"; import { projects, projectCoverTemplates, generatedCoverPages } from "@/db/schema"; import { GetProjectListsSchema } from "./validation"; export async function getProjectListsForCover(input: GetProjectListsSchema) { try { const offset = (input.page - 1) * input.perPage; const advancedTable = true; const advancedWhere = filterColumns({ table: projects, filters: input.filters, joinOperator: input.joinOperator, }); let globalWhere if (input.search) { const s = `%${input.search}%` globalWhere = or( ilike(projects.name, s), ilike(projects.code, s), ilike(projects.type, s), ) } const finalWhere = and( eq(projects.type, "plant"), advancedWhere, globalWhere ) const where = finalWhere const orderBy = input.sort.length > 0 ? input.sort.map((item) => item.desc ? desc(projects[item.id]) : asc(projects[item.id]) ) : [asc(projects.createdAt)]; // 트랜잭션 내부에서 Repository 호출 const { data, total } = await db.transaction(async (tx) => { const projectData = await selectProjectLists(tx, { where, orderBy, offset, limit: input.perPage, }); // 프로젝트 ID 목록 추출 const projectIds = projectData.map(p => p.id); // 활성 템플릿 정보 조회 const templates = projectIds.length > 0 ? await tx .select() .from(projectCoverTemplates) .where( and( inArray(projectCoverTemplates.projectId, projectIds), eq(projectCoverTemplates.isActive, true) ) ) : []; // 템플릿 맵 생성 const templateMap = new Map( templates.map(t => [t.projectId, t]) ); // 생성된 커버 페이지 조회 (각 템플릿의 최신 것만) const templateIds = templates.map(t => t.id); const generatedCovers = templateIds.length > 0 ? await tx .select() .from(generatedCoverPages) .where(inArray(generatedCoverPages.templateId, templateIds)) .orderBy(desc(generatedCoverPages.generatedAt)) : []; // 각 템플릿별 최신 생성 커버 맵 생성 const latestCoverMap = new Map(); for (const cover of generatedCovers) { if (!latestCoverMap.has(cover.templateId)) { latestCoverMap.set(cover.templateId, cover); } } // 프로젝트에 템플릿 및 생성된 커버 정보 병합 const data = projectData.map(project => { const template = templateMap.get(project.id); const latestCover = template ? latestCoverMap.get(template.id) : null; return { ...project, coverTemplatePath: template?.filePath || null, templateVariables: template?.variables || null, template: template || null, generatedCover: latestCover || null, generatedCoverPath: latestCover?.filePath || null, }; }); const total = await countProjectLists(tx, where); return { data, total }; }); const pageCount = Math.ceil(total / input.perPage); return { data, pageCount }; } catch (err) { console.error("❌ getProjectListsForCover 오류:", err); return { data: [], pageCount: 0 }; } }