diff options
Diffstat (limited to 'lib/evaluation-submit/service.ts')
| -rw-r--r-- | lib/evaluation-submit/service.ts | 213 |
1 files changed, 152 insertions, 61 deletions
diff --git a/lib/evaluation-submit/service.ts b/lib/evaluation-submit/service.ts index 21ceb36f..023961de 100644 --- a/lib/evaluation-submit/service.ts +++ b/lib/evaluation-submit/service.ts @@ -94,39 +94,76 @@ function getCategoryFilterByDepartment(departmentCode: string): SQL<unknown> { */ export async function getEvaluationFormData(reviewerEvaluationId: number): Promise<EvaluationFormData | null> { try { + console.log(`[SERVER] getEvaluationFormData called with ID: ${reviewerEvaluationId}`); + + // reviewerEvaluationId 유효성 검사 + if (!reviewerEvaluationId || reviewerEvaluationId <= 0) { + console.error(`[SERVER] Invalid reviewerEvaluationId: ${reviewerEvaluationId}`); + return null; + } + // 1. 리뷰어 평가 정보 조회 (부서 정보 + 평가 대상 정보 포함) - const reviewerEvaluationInfo = await db - .select({ - id: reviewerEvaluations.id, - periodicEvaluationId: reviewerEvaluations.periodicEvaluationId, - evaluationTargetReviewerId: reviewerEvaluations.evaluationTargetReviewerId, - isCompleted: reviewerEvaluations.isCompleted, - // evaluationTargetReviewers 테이블에서 부서 정보 - departmentCode: evaluationTargetReviewers.departmentCode, - // evaluationTargets 테이블에서 division과 materialType 정보 - division: evaluationTargets.division, - materialType: evaluationTargets.materialType, - vendorName: evaluationTargets.vendorName, - vendorCode: evaluationTargets.vendorCode, - }) - .from(reviewerEvaluations) - .leftJoin( - evaluationTargetReviewers, - eq(reviewerEvaluations.evaluationTargetReviewerId, evaluationTargetReviewers.id) - ) - .leftJoin( - evaluationTargets, - eq(evaluationTargetReviewers.evaluationTargetId, evaluationTargets.id) - ) - .where(eq(reviewerEvaluations.id, reviewerEvaluationId)) - .limit(1); + let reviewerEvaluationInfo; + try { + reviewerEvaluationInfo = await db + .select({ + id: reviewerEvaluations.id, + periodicEvaluationId: reviewerEvaluations.periodicEvaluationId, + evaluationTargetReviewerId: reviewerEvaluations.evaluationTargetReviewerId, + isCompleted: reviewerEvaluations.isCompleted, + // evaluationTargetReviewers 테이블에서 부서 정보 + departmentCode: evaluationTargetReviewers.departmentCode, + // evaluationTargets 테이블에서 division과 materialType 정보 + division: evaluationTargets.division, + materialType: evaluationTargets.materialType, + vendorName: evaluationTargets.vendorName, + vendorCode: evaluationTargets.vendorCode, + }) + .from(reviewerEvaluations) + .leftJoin( + evaluationTargetReviewers, + eq(reviewerEvaluations.evaluationTargetReviewerId, evaluationTargetReviewers.id) + ) + .leftJoin( + evaluationTargets, + eq(evaluationTargetReviewers.evaluationTargetId, evaluationTargets.id) + ) + .where(eq(reviewerEvaluations.id, reviewerEvaluationId)) + .limit(1); + } catch (dbError) { + console.error(`[SERVER] Database query failed for ID ${reviewerEvaluationId}:`, dbError); + throw new Error(`데이터베이스 조회 중 오류가 발생했습니다: ${dbError instanceof Error ? dbError.message : 'Unknown database error'}`); + } if (reviewerEvaluationInfo.length === 0) { - throw new Error('Reviewer evaluation not found'); + console.warn(`[SERVER] Reviewer evaluation not found for ID: ${reviewerEvaluationId}`); + return null; } const evaluation = reviewerEvaluationInfo[0]; + // 필수 필드 검증 및 상세 로그 + console.log(`[SERVER] Found evaluation data:`, { + id: evaluation.id, + division: evaluation.division, + materialType: evaluation.materialType, + departmentCode: evaluation.departmentCode, + vendorName: evaluation.vendorName, + vendorCode: evaluation.vendorCode + }); + + if (!evaluation.division || !evaluation.materialType || !evaluation.departmentCode) { + console.error(`[SERVER] Missing required evaluation data for ID ${reviewerEvaluationId}:`, { + id: evaluation.id, + division: evaluation.division, + materialType: evaluation.materialType, + departmentCode: evaluation.departmentCode, + vendorName: evaluation.vendorName, + vendorCode: evaluation.vendorCode + }); + return null; + } + // 1-1. division과 materialType을 기반으로 reviewerType 계산 const reviewerType = calculateReviewerType(evaluation.division, evaluation.materialType); @@ -134,7 +171,9 @@ export async function getEvaluationFormData(reviewerEvaluationId: number): Promi const categoryFilter = getCategoryFilterByDepartment(evaluation.departmentCode); // 3. 해당 부서에 맞는 평가 기준들과 답변 옵션들 조회 - const criteriaWithDetails = await db + let criteriaWithDetails; + try { + criteriaWithDetails = await db .select({ // 질문 정보 (실제 스키마 기준) criteriaId: regEvalCriteria.id, @@ -168,23 +207,40 @@ export async function getEvaluationFormData(reviewerEvaluationId: number): Promi regEvalCriteria.id, regEvalCriteriaDetails.orderIndex ); + } catch (criteriaError) { + console.error(`[SERVER] Failed to fetch evaluation criteria for ID ${reviewerEvaluationId}:`, criteriaError); + throw new Error(`평가 기준 조회 중 오류가 발생했습니다: ${criteriaError instanceof Error ? criteriaError.message : 'Unknown criteria error'}`); + } + + if (!criteriaWithDetails || criteriaWithDetails.length === 0) { + console.warn(`[SERVER] No evaluation criteria found for ID ${reviewerEvaluationId} with department ${evaluation.departmentCode}`); + return null; + } // 4. 기존 응답 데이터 조회 (실제 답변만) - const existingResponses = await db - .select({ - id: reviewerEvaluationDetails.id, - reviewerEvaluationId: reviewerEvaluationDetails.reviewerEvaluationId, - regEvalCriteriaDetailsId: reviewerEvaluationDetails.regEvalCriteriaDetailsId, - score: reviewerEvaluationDetails.score, - comment: reviewerEvaluationDetails.comment, - createdAt: reviewerEvaluationDetails.createdAt, - updatedAt: reviewerEvaluationDetails.updatedAt, - }) - .from(reviewerEvaluationDetails) - .where(eq(reviewerEvaluationDetails.reviewerEvaluationId, reviewerEvaluationId)); + let existingResponses; + try { + existingResponses = await db + .select({ + id: reviewerEvaluationDetails.id, + reviewerEvaluationId: reviewerEvaluationDetails.reviewerEvaluationId, + regEvalCriteriaDetailsId: reviewerEvaluationDetails.regEvalCriteriaDetailsId, + score: reviewerEvaluationDetails.score, + comment: reviewerEvaluationDetails.comment, + createdAt: reviewerEvaluationDetails.createdAt, + updatedAt: reviewerEvaluationDetails.updatedAt, + }) + .from(reviewerEvaluationDetails) + .where(eq(reviewerEvaluationDetails.reviewerEvaluationId, reviewerEvaluationId)); + } catch (responseError) { + console.error(`[SERVER] Failed to fetch existing responses for ID ${reviewerEvaluationId}:`, responseError); + existingResponses = []; // 기본값 설정 + } // 📎 5. 첨부파일 정보 조회 - const attachmentsData = await db + let attachmentsData; + try { + attachmentsData = await db .select({ // 첨부파일 정보 attachmentId: reviewerEvaluationAttachments.id, @@ -229,6 +285,10 @@ export async function getEvaluationFormData(reviewerEvaluationId: number): Promi ) .where(eq(reviewerEvaluationDetails.reviewerEvaluationId, reviewerEvaluationId)) .orderBy(desc(reviewerEvaluationAttachments.createdAt)); + } catch (attachmentError) { + console.error(`[SERVER] Failed to fetch attachments for ID ${reviewerEvaluationId}:`, attachmentError); + attachmentsData = []; // 기본값 설정 + } // 📎 6. 첨부파일을 질문별로 그룹화 const attachmentsByQuestion = new Map<number, AttachmentInfo[]>(); @@ -286,7 +346,7 @@ export async function getEvaluationFormData(reviewerEvaluationId: number): Promi category2: record.category2, item: record.item, classification: record.classification, - range: record.range, + range: record.range || null, scoreType: record.scoreType, remarks: record.remarks, availableOptions: [], @@ -305,10 +365,10 @@ export async function getEvaluationFormData(reviewerEvaluationId: number): Promi // 답변 옵션 추가 const question = questionsMap.get(criteriaId)!; question.availableOptions.push({ - detailId: record.detailId, - detail: record.detail, + detailId: record.detailId || 0, + detail: record.detail || '', score: score, - orderIndex: record.orderIndex, + orderIndex: record.orderIndex || 0, }); }); @@ -392,7 +452,15 @@ export async function getEvaluationFormData(reviewerEvaluationId: number): Promi }; } catch (err) { - console.error('Error in getEvaluationFormData:', err); + console.error(`[SERVER] Error in getEvaluationFormData for ID ${reviewerEvaluationId}:`, err); + // 데이터베이스 연결 오류나 쿼리 실행 오류 등은 여기서 처리 + if (err instanceof Error) { + if (err.message.includes('Connection') || err.message.includes('timeout')) { + console.error(`[SERVER] Database connection error: ${err.message}`); + } else if (err.message.includes('syntax') || err.message.includes('column')) { + console.error(`[SERVER] Database query error: ${err.message}`); + } + } return null; } } @@ -546,12 +614,25 @@ export async function updateEvaluationResponse( selectedDetail = detailResult[0]; } - // 2. reviewerEvaluation 정보 조회 (periodicEvaluationId 포함) + // 2. reviewerEvaluation 정보 조회 (periodicEvaluationId, division, materialType 포함) const reviewerEvaluationInfo = await tx .select({ periodicEvaluationId: reviewerEvaluations.periodicEvaluationId, + // evaluationTargetReviewers 테이블에서 부서 정보 + departmentCode: evaluationTargetReviewers.departmentCode, + // evaluationTargets 테이블에서 division과 materialType 정보 + division: evaluationTargets.division, + materialType: evaluationTargets.materialType, }) .from(reviewerEvaluations) + .leftJoin( + evaluationTargetReviewers, + eq(reviewerEvaluations.evaluationTargetReviewerId, evaluationTargetReviewers.id) + ) + .leftJoin( + evaluationTargets, + eq(evaluationTargetReviewers.evaluationTargetId, evaluationTargets.id) + ) .where(eq(reviewerEvaluations.id, reviewerEvaluationId)) .limit(1); @@ -559,7 +640,14 @@ export async function updateEvaluationResponse( throw new Error('Reviewer evaluation not found'); } - const { periodicEvaluationId } = reviewerEvaluationInfo[0]; + const evaluation = reviewerEvaluationInfo[0]; + + // 필수 필드 검증 + if (!evaluation.division || !evaluation.materialType || !evaluation.departmentCode) { + throw new Error('Missing required evaluation data'); + } + + const { periodicEvaluationId } = evaluation; // 3. periodicEvaluation의 현재 상태 확인 및 업데이트 const currentStatus = await tx @@ -589,12 +677,13 @@ export async function updateEvaluationResponse( score = customScore; } else { // 일반 타입인 경우 리뷰어 타입에 맞는 점수 가져오기 - const evaluationInfo = await getEvaluationFormData(reviewerEvaluationId); - if (!evaluationInfo) { - throw new Error('Evaluation not found'); + if (!selectedDetail) { + throw new Error('Selected detail not found'); } - const calculatedScore = getScoreByReviewerType(selectedDetail!, evaluationInfo.evaluationInfo.reviewerType); + // reviewerType 계산 + const reviewerTypeForScore = calculateReviewerType(evaluation.division, evaluation.materialType); + const calculatedScore = getScoreByReviewerType(selectedDetail, reviewerTypeForScore); if (calculatedScore === null) { throw new Error('Score not found for this reviewer type'); } @@ -697,16 +786,18 @@ export async function updateVariableEvaluationResponse( } // 3. 해당 평가 기준에 대한 기존 응답들 삭제 - await tx - .delete(reviewerEvaluationDetails) - .where( - and( - eq(reviewerEvaluationDetails.reviewerEvaluationId, reviewerEvaluationId), - sql`${reviewerEvaluationDetails.regEvalCriteriaDetailsId} IN ( - SELECT id FROM reg_eval_criteria_details WHERE criteria_id = ${criteriaId} - )` - ) - ); + if (criteriaId) { + await tx + .delete(reviewerEvaluationDetails) + .where( + and( + eq(reviewerEvaluationDetails.reviewerEvaluationId, reviewerEvaluationId), + sql`${reviewerEvaluationDetails.regEvalCriteriaDetailsId} IN ( + SELECT id FROM reg_eval_criteria_details WHERE criteria_id = ${criteriaId} + )` + ) + ); + } // 4. 새로운 응답 생성 (variable 타입은 regEvalCriteriaDetailsId가 null) const [newDetail] = await tx |
