From ceaf46cd523f2bc94bbb35429e5ec0708a242caf Mon Sep 17 00:00:00 2001 From: dujinkim Date: Thu, 6 Nov 2025 03:18:19 +0000 Subject: (최겸) 구매 실사 의뢰 성공 시 캐시 재검증 및 email 추가 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../investigation-request-notification.hbs | 92 ++++++++++++++ lib/pq/service.ts | 133 ++++++++++++++++++++- 2 files changed, 219 insertions(+), 6 deletions(-) create mode 100644 lib/mail/templates/investigation-request-notification.hbs diff --git a/lib/mail/templates/investigation-request-notification.hbs b/lib/mail/templates/investigation-request-notification.hbs new file mode 100644 index 00000000..2c0c916b --- /dev/null +++ b/lib/mail/templates/investigation-request-notification.hbs @@ -0,0 +1,92 @@ + + + + + + eVCP 메일 + + + +
+ + + + +
+ eVCP +
+ +

실사 의뢰 요청 알림

+ +

안녕하세요, {{recipientName}}님

+ +

새로운 실사 의뢰가 생성되었습니다.

+ +
+

의뢰 정보:

+ + {{#if vendorNames}} +

협력업체:

+
    + {{#each vendorNames}} +
  • {{this}}
  • + {{/each}} +
+ {{/if}} + + {{#if forecastedAt}} +

실사 예정일:

+

{{forecastedAt}}

+ {{/if}} + + {{#if investigationAddress}} +

실사 주소:

+

{{investigationAddress}}

+ {{/if}} + + {{#if investigationNotes}} +

메모:

+

{{investigationNotes}}

+ {{/if}} + + {{#if requesterName}} +

의뢰자:

+

{{requesterName}}

+ {{/if}} +
+ +

+ 실사 관리 페이지 바로가기 +

+ +

문의사항이 있으시면 시스템 관리자에게 연락해 주세요.

+ +

감사합니다.
eVCP 팀

+ + + + + +
+

© {{currentYear}} EVCP. {{t "email.vendor.invitation.copyright"}}

+

{{t "email.vendor.invitation.no_reply"}}

+
+
+ + + diff --git a/lib/pq/service.ts b/lib/pq/service.ts index fd751b0f..bc3f37a0 100644 --- a/lib/pq/service.ts +++ b/lib/pq/service.ts @@ -2920,13 +2920,134 @@ export async function requestInvestigationAction( return created; }); - // 캐시 무효화 (핸들러에서 호출 시에는 건너뛰기) - if (!options?.skipRevalidation) { - revalidateTag("vendor-investigations"); - revalidateTag("pq-submissions"); - revalidateTag("vendor-pq-submissions"); - revalidatePath("/evcp/pq_new"); + + // 이메일 발송 (트랜잭션 외부에서 실행) + try { + // 1. 협력업체 정보 조회 (이메일 포함) + const vendorIds = result.map(inv => inv.vendorId); + const uniqueVendorIds = [...new Set(vendorIds)]; + + const vendorInfos = await db + .select({ + id: vendors.id, + vendorName: vendors.vendorName, + email: vendors.email, + }) + .from(vendors) + .where(inArray(vendors.id, uniqueVendorIds)); + + // 2. QM 담당자 정보 조회 + const qmManager = await db + .select({ + id: users.id, + name: users.name, + email: users.email, + }) + .from(users) + .where(eq(users.id, data.qmManagerId)) + .limit(1) + .then(rows => rows[0]); + + // 3. 요청자(현재 사용자) 정보 조회 + const requester = await db + .select({ + id: users.id, + name: users.name, + email: users.email, + }) + .from(users) + .where(eq(users.id, currentUser.id)) + .limit(1) + .then(rows => rows[0]); + + const portalUrl = process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000"; + const currentYear = new Date().getFullYear(); + const forecastedAtFormatted = format(data.forecastedAt, "yyyy-MM-dd"); + + // 4. 협력업체별로 이메일 발송 (investigation-request.hbs 템플릿 사용) + const vendorEmailPromises = vendorInfos + .filter(vendor => vendor.email) // 이메일이 있는 경우만 + .map(async (vendor) => { + try { + await sendEmail({ + to: vendor.email!, + subject: "[eVCP] 협력업체 실사 요청", + template: "investigation-request", + context: { + language: "ko", + vendorIds: [vendor.id], + notes: data.investigationNotes || "실사가 예정되어 있습니다.", + portalUrl: `${portalUrl}/ko/partners/site-visit`, + currentYear: currentYear, + }, + }); + console.log(`협력업체 이메일 발송 완료: ${vendor.vendorName} (${vendor.email})`); + } catch (emailError) { + console.error(`협력업체 이메일 발송 실패: ${vendor.vendorName} (${vendor.email})`, emailError); + } + }); + + await Promise.all(vendorEmailPromises); + + // 5. QM 담당자에게 알림 이메일 발송 + if (qmManager?.email) { + try { + const vendorNames = vendorInfos.map(v => v.vendorName); + + await sendEmail({ + to: qmManager.email, + subject: "[eVCP] 실사 의뢰 요청 알림", + template: "investigation-request-notification", + context: { + language: "ko", + recipientName: qmManager.name, + vendorNames: vendorNames, + forecastedAt: forecastedAtFormatted, + investigationAddress: data.investigationAddress, + investigationNotes: data.investigationNotes || null, + requesterName: requester?.name || "알 수 없음", + portalUrl: `${portalUrl}/evcp/vendor-investigation`, + currentYear: currentYear, + }, + }); + console.log(`QM 담당자 이메일 발송 완료: ${qmManager.name} (${qmManager.email})`); + } catch (emailError) { + console.error(`QM 담당자 이메일 발송 실패: ${qmManager.name} (${qmManager.email})`, emailError); + } + } + + // 6. 요청자(현재 사용자)에게 알림 이메일 발송 (QM 담당자와 다른 경우만) + // if (requester?.email && requester.id !== data.qmManagerId) { + // try { + // const vendorNames = vendorInfos.map(v => v.vendorName); + + // await sendEmail({ + // to: requester.email, + // subject: "[eVCP] 실사 의뢰 요청 알림", + // template: "investigation-request-notification", + // context: { + // language: "ko", + // recipientName: requester.name, + // vendorNames: vendorNames, + // forecastedAt: forecastedAtFormatted, + // investigationAddress: data.investigationAddress, + // investigationNotes: data.investigationNotes || null, + // requesterName: requester.name, + // portalUrl: `${portalUrl}/evcp/vendor-investigation`, + // currentYear: currentYear, + // }, + // }); + // console.log(`요청자 이메일 발송 완료: ${requester.name} (${requester.email})`); + // } catch (emailError) { + // console.error(`요청자 이메일 발송 실패: ${requester.name} (${requester.email})`, emailError); + // } + // } + } catch (emailErr) { + // 이메일 발송 실패는 로그만 남기고 전체 프로세스는 성공으로 처리 + console.error("이메일 발송 중 오류 발생:", emailErr); } + revalidatePath("/evcp/pq_new"); + revalidatePath("/evcp/vendor-investigation"); return { success: true, -- cgit v1.2.3