diff options
| author | rlaks5757 <rlaks5757@gmail.com> | 2025-03-27 17:48:36 +0900 |
|---|---|---|
| committer | rlaks5757 <rlaks5757@gmail.com> | 2025-03-27 17:48:36 +0900 |
| commit | 773918229ccb14c0d00798fbbf2b2be0130a8251 (patch) | |
| tree | ab9e200e65cc471ec139cd8482bde70a3b0a105f /lib/rfqs/service.ts | |
| parent | 92ddb4f13d48cbf344dc2bf63df4457b3c713608 (diff) | |
| parent | 34bbeb86c1a8d24b5f526710889b5e54d699cfd0 (diff) | |
merge complete
Diffstat (limited to 'lib/rfqs/service.ts')
| -rw-r--r-- | lib/rfqs/service.ts | 68 |
1 files changed, 50 insertions, 18 deletions
diff --git a/lib/rfqs/service.ts b/lib/rfqs/service.ts index b1e02cd0..6b8b4738 100644 --- a/lib/rfqs/service.ts +++ b/lib/rfqs/service.ts @@ -24,6 +24,7 @@ import { sendEmail } from "../mail/sendEmail"; import { projects } from "@/db/schema/projects"; import { items } from "@/db/schema/items"; import * as z from "zod" +import { users } from "@/db/schema/users"; interface InviteVendorsInput { @@ -116,7 +117,7 @@ export async function getRfqs(input: GetRfqsSchema) { [JSON.stringify(input)], { revalidate: 3600, - tags: [`rfqs-${input.rfqType}`], + tags: [`rfqs-${input.rfqType}`], } )(); } @@ -597,8 +598,6 @@ export async function getMatchedVendors(input: GetMatchedVendorsSchema, rfqId: n return { data: [], pageCount: 0 } } - console.log(vendorIdList,"vendorIdList") - // ───────────────────────────────────────────────────── // 3) 필터/검색/정렬 // ───────────────────────────────────────────────────── @@ -631,7 +630,9 @@ export async function getMatchedVendors(input: GetMatchedVendorsSchema, rfqId: n // 특정 rfqId(뷰에 담긴 값)도 일치해야 함. const finalWhere = and( inArray(vendorRfqView.vendorId, vendorIdList), - eq(vendorRfqView.rfqId, rfqId), + // 아래 라인은 rfq에 초대된 벤더만 필터링하는 조건으로 추정되지만 + // rfq 를 진행하기 전에도 벤더를 보여줘야 하므로 주석처리하겠습니다 + // eq(vendorRfqView.rfqId, rfqId), advancedWhere, globalWhere ) @@ -670,18 +671,21 @@ export async function getMatchedVendors(input: GetMatchedVendorsSchema, rfqId: n .offset(offset) .limit(limit) - // 총 개수 + // 중복 제거된 데이터 생성 + const distinctData = Array.from( + new Map(data.map(row => [row.id, row])).values() + ) + + // 중복 제거된 총 개수 계산 const [{ count }] = await tx - .select({ count: sql<number>`count(*)`.as("count") }) + .select({ count: sql<number>`count(DISTINCT ${vendorRfqView.vendorId})`.as("count") }) .from(vendorRfqView) .where(finalWhere) - return [data, Number(count)] + return [distinctData, Number(count)] }) - console.log(rows) - console.log(total) // ───────────────────────────────────────────────────── // 4-1) 정확한 rfqVendorStatus와 rfqVendorUpdated 조회 // ───────────────────────────────────────────────────── @@ -732,11 +736,36 @@ export async function getMatchedVendors(input: GetMatchedVendorsSchema, rfqId: n ) const commByVendorId = new Map<number, any[]>() + // 먼저 모든 사용자 ID를 수집 + const userIds = new Set(commAll.map(c => c.commentedBy)); + const userIdsArray = Array.from(userIds); + + // Drizzle의 select 메서드를 사용하여 사용자 정보를 가져옴 + const usersData = await db + .select({ + id: users.id, + email: users.email, + }) + .from(users) + .where(inArray(users.id, userIdsArray)); + + // 사용자 ID를 키로 하는 맵 생성 + const userMap = new Map(); + for (const user of usersData) { + userMap.set(user.id, user); + } + + // 댓글 정보를 벤더 ID별로 그룹화하고, 사용자 이메일 추가 for (const c of commAll) { const vid = c.vendorId! if (!commByVendorId.has(vid)) { commByVendorId.set(vid, []) } + + // 사용자 정보 가져오기 + const user = userMap.get(c.commentedBy); + const userEmail = user ? user.email : 'unknown@example.com'; // 사용자를 찾지 못한 경우 기본값 설정 + commByVendorId.get(vid)!.push({ id: c.id, commentText: c.commentText, @@ -744,9 +773,9 @@ export async function getMatchedVendors(input: GetMatchedVendorsSchema, rfqId: n evaluationId: c.evaluationId, createdAt: c.createdAt, commentedBy: c.commentedBy, + commentedByEmail: userEmail, // 이메일 추가 }) } - // ───────────────────────────────────────────────────── // 6) rows에 comments 병합 // ───────────────────────────────────────────────────── @@ -1623,9 +1652,10 @@ export async function createRfqCommentWithAttachments(params: { commentText: string commentedBy: number evaluationId?: number | null + cbeId?: number | null files?: File[] }) { - const { rfqId, vendorId, commentText, commentedBy, evaluationId, files } = params + const { rfqId, vendorId, commentText, commentedBy, evaluationId,cbeId, files } = params // 1) 새로운 코멘트 생성 @@ -1637,6 +1667,7 @@ export async function createRfqCommentWithAttachments(params: { commentText, commentedBy, evaluationId: evaluationId || null, + cbeId: cbeId || null, }) .returning({ id: rfqComments.id, createdAt: rfqComments.createdAt }) // id만 반환하도록 @@ -1644,7 +1675,7 @@ export async function createRfqCommentWithAttachments(params: { throw new Error("Failed to create comment") } - // 2) 첨부파일 처리 (S3 업로드 등은 프로젝트 상황에 따라) + // 2) 첨부파일 처리 if (files && files.length > 0) { const rfqDir = path.join(process.cwd(), "public", "rfq", String(rfqId)); @@ -1669,6 +1700,7 @@ export async function createRfqCommentWithAttachments(params: { rfqId, vendorId: vendorId || null, evaluationId: evaluationId || null, + cbeId: cbeId || null, commentId: insertedComment.id, // 새 코멘트와 연결 fileName: file.name, filePath: "/" + relativePath.replace(/\\/g, "/"), @@ -2552,10 +2584,10 @@ export async function getCBE(input: GetCBESchema, rfqId: number) { // [6] 정렬 const orderBy = input.sort?.length ? input.sort.map((s) => { - // vendor_cbe_view 컬럼 중 정렬 대상이 되는 것만 매핑 - const col = (vendorCbeView as any)[s.id]; - return s.desc ? desc(col) : asc(col); - }) + // vendor_cbe_view 컬럼 중 정렬 대상이 되는 것만 매핑 + const col = (vendorCbeView as any)[s.id]; + return s.desc ? desc(col) : asc(col); + }) : [asc(vendorCbeView.vendorId)]; // [7] 메인 SELECT @@ -2690,7 +2722,7 @@ export async function getCBE(input: GetCBESchema, rfqId: number) { // Step 2: responseIds const allResponseIds = responsesAll.map((r) => r.id); - + const commercialResponsesAll = await db .select({ id: vendorCommercialResponses.id, @@ -2708,7 +2740,7 @@ export async function getCBE(input: GetCBESchema, rfqId: number) { } const allCommercialResponseIds = commercialResponsesAll.map((cr) => cr.id); - + // 여기서는 예시로 TBE와 마찬가지로 vendorResponseAttachments를 // 직접 responseId로 관리한다고 가정(혹은 commercialResponseId로 연결) |
