diff options
Diffstat (limited to 'lib/vendors/service.ts')
| -rw-r--r-- | lib/vendors/service.ts | 160 |
1 files changed, 158 insertions, 2 deletions
diff --git a/lib/vendors/service.ts b/lib/vendors/service.ts index 6132832f..de88ae72 100644 --- a/lib/vendors/service.ts +++ b/lib/vendors/service.ts @@ -1818,12 +1818,168 @@ export async function approveVendors(input: ApproveVendorsInput & { userId: numb } /** + * 선택된 벤더의 상태를 REJECTED로 변경하고 이메일 알림을 발송하는 서버 액션 + */ +export async function rejectVendors(input: ApproveVendorsInput & { userId: number }) { + unstable_noStore(); + + try { + // 트랜잭션 내에서 협력업체 상태 업데이트 및 이메일 발송 + const result = await db.transaction(async (tx) => { + // 0. 업데이트 전 협력업체 상태 조회 + const vendorsBeforeUpdate = await tx + .select({ + id: vendors.id, + status: vendors.status, + }) + .from(vendors) + .where(inArray(vendors.id, input.ids)); + + // 1. 협력업체 상태 업데이트 + const [updated] = await tx + .update(vendors) + .set({ + status: "REJECTED", + updatedAt: new Date() + }) + .where(inArray(vendors.id, input.ids)) + .returning(); + + // 2. 업데이트된 협력업체 정보 조회 (국가 정보 포함) + const updatedVendors = await tx + .select({ + id: vendors.id, + vendorName: vendors.vendorName, + email: vendors.email, + country: vendors.country, // 언어 설정용 국가 정보 + }) + .from(vendors) + .where(inArray(vendors.id, input.ids)); + + // 3. 각 벤더에 대한 유저 계정 비활성화 처리 + await Promise.all( + updatedVendors.map(async (vendor) => { + if (!vendor.email) return; // 이메일이 없으면 스킵 + + // 기존 유저 확인 + const existingUser = await tx + .select({ + id: users.id, + isActive: users.isActive, + language: users.language, + }) + .from(users) + .where(eq(users.email, vendor.email)) + .limit(1); + + if (existingUser.length > 0) { + // 기존 사용자 존재 시 - 비활성화 + const user = existingUser[0]; + console.log(`👤 기존 사용자 발견: ${vendor.email} (활성상태: ${user.isActive})`); + + if (user.isActive) { + // 활성 사용자 비활성화 + await tx + .update(users) + .set({ + isActive: false, + updatedAt: new Date(), + }) + .where(eq(users.id, user.id)); + + console.log(`❌ 사용자 비활성화 완료: ${vendor.email} (ID: ${user.id})`); + } else { + console.log(`ℹ️ 사용자가 이미 비활성 상태: ${vendor.email}`); + } + } + }) + ); + + // 4. 로그 기록 + await Promise.all( + vendorsBeforeUpdate.map(async (vendorBefore) => { + await tx.insert(vendorsLogs).values({ + vendorId: vendorBefore.id, + userId: input.userId, + action: "status_change", + oldStatus: vendorBefore.status, + newStatus: "REJECTED", + comment: "Vendor rejected", + }); + }) + ); + + // 5. 각 벤더에게 거절 이메일 발송 + await Promise.all( + updatedVendors.map(async (vendor) => { + if (!vendor.email) return; // 이메일이 없으면 스킵 + + try { + // 사용자 언어 확인 + const userInfo = await tx + .select({ + id: users.id, + language: users.language + }) + .from(users) + .where(eq(users.email, vendor.email)) + .limit(1); + + const userLang = userInfo.length > 0 ? userInfo[0].language : + (vendor.country === 'KR' ? 'ko' : 'en'); + + const subject = userLang === 'ko' + ? "[eVCP] 업체 등록 거절 안내" + : "[eVCP] Vendor Registration Rejected"; + + const headersList = await headers(); + const host = headersList.get('host') || 'localhost:3000'; + const protocol = headersList.get('x-forwarded-proto') || 'http'; + const baseUrl = `${protocol}://${host}`; + const loginUrl = `${baseUrl}/${userLang}/login`; + + await sendEmail({ + to: vendor.email, + subject, + template: "vendor-rejected", // 거절 템플릿 + context: { + vendorName: vendor.vendorName, + loginUrl, + language: userLang, + }, + }); + + console.log(`📧 거절 이메일 발송: ${vendor.email}`); + } catch (emailError) { + console.error(`이메일 발송 실패 - 업체 ${vendor.id}:`, emailError); + // 이메일 전송 실패는 전체 트랜잭션을 실패시키지 않음 + } + }) + ); + + console.log(`❌ 협력업체 거절 완료: ${updatedVendors.length}개 업체`); + return updated; + }); + + // 캐시 무효화 + revalidateTag("vendors"); + revalidateTag("vendor-status-counts"); + revalidateTag("users"); // 유저 캐시도 무효화 + + return { data: result, error: null }; + } catch (err) { + console.error("협력업체 거절 처리 오류:", err); + return { data: null, error: getErrorMessage(err) }; + } +} + +/** * 유니크한 PQ 번호 생성 함수 - * + * * 형식: PQ-YYMMDD-XXXXX * YYMMDD: 연도(YY), 월(MM), 일(DD) * XXXXX: 시퀀스 번호 (00001부터 시작) - * + * * 예: PQ-240520-00001, PQ-240520-00002, ... */ export async function generatePQNumber(isProject: boolean = false) { |
