summaryrefslogtreecommitdiff
path: root/lib/evaluation-submit/service.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/evaluation-submit/service.ts')
-rw-r--r--lib/evaluation-submit/service.ts213
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