summaryrefslogtreecommitdiff
path: root/lib/evaluation/service.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/evaluation/service.ts')
-rw-r--r--lib/evaluation/service.ts124
1 files changed, 109 insertions, 15 deletions
diff --git a/lib/evaluation/service.ts b/lib/evaluation/service.ts
index 76811753..8e394f88 100644
--- a/lib/evaluation/service.ts
+++ b/lib/evaluation/service.ts
@@ -9,6 +9,7 @@ import {
periodicEvaluationsView,
regEvalCriteria,
regEvalCriteriaDetails,
+ reviewerEvaluationAttachments,
reviewerEvaluationDetails,
reviewerEvaluations,
roles,
@@ -32,6 +33,7 @@ import { revalidatePath } from "next/cache"
import { DEPARTMENT_CODE_LABELS } from "@/types/evaluation"
import { getServerSession } from "next-auth"
import { authOptions } from "@/app/api/auth/[...nextauth]/route"
+import { AttachmentDetail, EvaluationDetailResponse } from "@/types/evaluation-form"
export async function getPeriodicEvaluations(input: GetEvaluationTargetsSchema) {
try {
@@ -989,17 +991,7 @@ export interface EvaluationDetailData {
/**
* 특정 정기평가의 상세 정보를 조회합니다
*/
-export async function getEvaluationDetails(periodicEvaluationId: number): Promise<{
- evaluationInfo: {
- id: number
- vendorName: string
- vendorCode: string
- evaluationYear: number
- division: string
- status: string
- }
- reviewerDetails: EvaluationDetailData[]
-}> {
+export async function getEvaluationDetails(periodicEvaluationId: number): Promise<EvaluationDetailResponse> {
try {
// 1. 평가 기본 정보 조회
const evaluationInfo = await db
@@ -1060,11 +1052,90 @@ export async function getEvaluationDetails(periodicEvaluationId: number): Promis
.where(eq(reviewerEvaluations.periodicEvaluationId, periodicEvaluationId))
.orderBy(evaluationTargetReviewers.departmentCode, regEvalCriteria.category, regEvalCriteria.classification)
- // 3. 리뷰어별로 그룹화
+ // 📎 3. 첨부파일 정보 조회
+ const attachmentsData = await db
+ .select({
+ // 첨부파일 정보
+ attachmentId: reviewerEvaluationAttachments.id,
+ originalFileName: reviewerEvaluationAttachments.originalFileName,
+ storedFileName: reviewerEvaluationAttachments.storedFileName,
+ publicPath: reviewerEvaluationAttachments.publicPath,
+ fileSize: reviewerEvaluationAttachments.fileSize,
+ mimeType: reviewerEvaluationAttachments.mimeType,
+ fileExtension: reviewerEvaluationAttachments.fileExtension,
+ description: reviewerEvaluationAttachments.description,
+ uploadedBy: reviewerEvaluationAttachments.uploadedBy,
+ attachmentCreatedAt: reviewerEvaluationAttachments.createdAt,
+
+ // 업로드한 사용자 정보
+ uploadedByName: users.name,
+
+ // 평가 세부사항 정보
+ evaluationDetailId: reviewerEvaluationDetails.id,
+ reviewerEvaluationId: reviewerEvaluationDetails.reviewerEvaluationId,
+
+ // 평가 기준 정보 (질문 식별용)
+ criteriaId: regEvalCriteriaDetails.criteriaId,
+ })
+ .from(reviewerEvaluationAttachments)
+ .innerJoin(
+ reviewerEvaluationDetails,
+ eq(reviewerEvaluationAttachments.reviewerEvaluationDetailId, reviewerEvaluationDetails.id)
+ )
+ .innerJoin(
+ reviewerEvaluations,
+ eq(reviewerEvaluationDetails.reviewerEvaluationId, reviewerEvaluations.id)
+ )
+ .leftJoin(
+ regEvalCriteriaDetails,
+ eq(reviewerEvaluationDetails.regEvalCriteriaDetailsId, regEvalCriteriaDetails.id)
+ )
+ .leftJoin(
+ users,
+ eq(reviewerEvaluationAttachments.uploadedBy, users.id)
+ )
+ .where(eq(reviewerEvaluations.periodicEvaluationId, periodicEvaluationId))
+ .orderBy(desc(reviewerEvaluationAttachments.createdAt))
+
+ // 📎 4. 첨부파일을 평가 세부사항별로 그룹화
+ const attachmentsByDetailId = new Map<number, AttachmentDetail[]>()
+ const attachmentsByReviewerId = new Map<number, AttachmentDetail[]>()
+
+ attachmentsData.forEach(attachment => {
+ const attachmentInfo: AttachmentDetail = {
+ id: attachment.attachmentId,
+ originalFileName: attachment.originalFileName,
+ storedFileName: attachment.storedFileName,
+ publicPath: attachment.publicPath,
+ fileSize: attachment.fileSize,
+ mimeType: attachment.mimeType || undefined,
+ fileExtension: attachment.fileExtension || undefined,
+ description: attachment.description || undefined,
+ uploadedBy: attachment.uploadedBy,
+ uploadedByName: attachment.uploadedByName || undefined,
+ createdAt: new Date(attachment.attachmentCreatedAt),
+ }
+
+ // 평가 세부사항별 그룹화
+ if (!attachmentsByDetailId.has(attachment.evaluationDetailId)) {
+ attachmentsByDetailId.set(attachment.evaluationDetailId, [])
+ }
+ attachmentsByDetailId.get(attachment.evaluationDetailId)!.push(attachmentInfo)
+
+ // 리뷰어별 그룹화
+ if (!attachmentsByReviewerId.has(attachment.reviewerEvaluationId)) {
+ attachmentsByReviewerId.set(attachment.reviewerEvaluationId, [])
+ }
+ attachmentsByReviewerId.get(attachment.reviewerEvaluationId)!.push(attachmentInfo)
+ })
+
+ // 5. 리뷰어별로 그룹화하고 첨부파일 정보 포함
const reviewerDetailsMap = new Map<number, EvaluationDetailData>()
reviewerDetailsRaw.forEach(row => {
if (!reviewerDetailsMap.has(row.reviewerEvaluationId)) {
+ const reviewerAttachments = attachmentsByReviewerId.get(row.reviewerEvaluationId) || []
+
reviewerDetailsMap.set(row.reviewerEvaluationId, {
reviewerEvaluationId: row.reviewerEvaluationId,
reviewerName: row.reviewerName || "",
@@ -1074,13 +1145,22 @@ export async function getEvaluationDetails(periodicEvaluationId: number): Promis
isCompleted: row.isCompleted || false,
completedAt: row.completedAt,
reviewerComment: row.reviewerComment,
- evaluationItems: []
+ evaluationItems: [],
+
+ // 📎 리뷰어별 첨부파일 통계
+ totalAttachments: reviewerAttachments.length,
+ totalAttachmentSize: reviewerAttachments.reduce((sum, att) => sum + att.fileSize, 0),
+ questionsWithAttachments: new Set(reviewerAttachments.map(att =>
+ attachmentsData.find(a => a.attachmentId === att.id)?.criteriaId
+ ).filter(Boolean)).size,
})
}
// 평가 항목이 있는 경우에만 추가
if (row.criteriaId && row.detailId) {
const reviewer = reviewerDetailsMap.get(row.reviewerEvaluationId)!
+ const itemAttachments = attachmentsByDetailId.get(row.detailId) || []
+
reviewer.evaluationItems.push({
criteriaId: row.criteriaId,
category: row.category || "",
@@ -1093,14 +1173,28 @@ export async function getEvaluationDetails(periodicEvaluationId: number): Promis
selectedDetailId: row.selectedDetailId,
selectedDetail: row.selectedDetail,
score: row.score ? Number(row.score) : null,
- comment: row.comment
+ comment: row.comment,
+
+ // 📎 항목별 첨부파일 정보
+ attachments: itemAttachments,
+ attachmentCount: itemAttachments.length,
+ attachmentTotalSize: itemAttachments.reduce((sum, att) => sum + att.fileSize, 0),
})
}
})
+ // 📎 6. 전체 첨부파일 통계 계산
+ const attachmentStats = {
+ totalFiles: attachmentsData.length,
+ totalSize: attachmentsData.reduce((sum, att) => sum + att.fileSize, 0),
+ reviewersWithAttachments: attachmentsByReviewerId.size,
+ questionsWithAttachments: new Set(attachmentsData.map(att => att.criteriaId).filter(Boolean)).size,
+ }
+
return {
evaluationInfo: evaluationInfo[0],
- reviewerDetails: Array.from(reviewerDetailsMap.values())
+ reviewerDetails: Array.from(reviewerDetailsMap.values()),
+ attachmentStats,
}
} catch (error) {