From b9117f231b4ee93574f96656c0deb743e6cd0f2c Mon Sep 17 00:00:00 2001 From: chu eun ju Date: Fri, 12 Dec 2025 17:03:30 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B0=9C=EC=B2=B4=20=EC=83=81=EC=84=B8?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/cow/cow.service.ts | 14 +++++++++-- backend/src/genome/genome.service.ts | 24 ++++++++++++++++--- .../_components/trait-distribution-charts.tsx | 20 +++++++++++----- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/backend/src/cow/cow.service.ts b/backend/src/cow/cow.service.ts index 3272e13..59b2e62 100644 --- a/backend/src/cow/cow.service.ts +++ b/backend/src/cow/cow.service.ts @@ -27,6 +27,13 @@ import { } from './dto/ranking-request.dto'; import { isValidGenomeAnalysis } from '../common/config/GenomeAnalysisConfig'; +/** + * 낮을수록 좋은 형질 목록 (부호 반전 필요) + * - 등지방두께: 지방이 얇을수록(EBV가 낮을수록) 좋은 형질 + * - 선발지수 계산 시 EBV 부호를 반전하여 적용 + */ +const NEGATIVE_TRAITS = ['등지방두께']; + /** * 개체(소) 관리 서비스 * @@ -291,13 +298,16 @@ export class CowService { if (trait && trait.traitEbv !== null) { // EBV 값이 있으면 가중치 적용하여 합산 const ebv = Number(trait.traitEbv); - weightedSum += ebv * weight; // EBV × 가중치 + // 등지방두께 등 낮을수록 좋은 형질은 부호 반전 + const isNegativeTrait = NEGATIVE_TRAITS.includes(condition.traitNm); + const adjustedEbv = isNegativeTrait ? -ebv : ebv; + weightedSum += adjustedEbv * weight; // EBV × 가중치 totalWeight += weight; // 가중치 누적 // 상세 내역 저장 (응답용) details.push({ code: condition.traitNm, // 형질명 - value: ebv, // EBV 값 + value: adjustedEbv, // EBV 값 (부호 반전 적용) weight, // 적용된 가중치 }); } else { diff --git a/backend/src/genome/genome.service.ts b/backend/src/genome/genome.service.ts index c18b585..743ef4f 100644 --- a/backend/src/genome/genome.service.ts +++ b/backend/src/genome/genome.service.ts @@ -10,6 +10,13 @@ import { FarmModel } from '../farm/entities/farm.entity'; import { GenomeRequestModel } from './entities/genome-request.entity'; import { GenomeTraitDetailModel } from './entities/genome-trait-detail.entity'; +/** + * 낮을수록 좋은 형질 목록 (부호 반전 필요) + * - 등지방두께: 지방이 얇을수록(EBV가 낮을수록) 좋은 형질 + * - 선발지수 계산 시 EBV 부호를 반전하여 적용 + */ +const NEGATIVE_TRAITS = ['등지방두께']; + /** * 형질명 → 카테고리 매핑 상수 * - 성장: 월령별 체중 관련 형질 @@ -1368,7 +1375,10 @@ export class GenomeService { if (trait && trait.traitEbv !== null) { const ebv = Number(trait.traitEbv); - const contribution = ebv * weight; // EBV × 가중치 + // 등지방두께 등 낮을수록 좋은 형질은 부호 반전 + const isNegativeTrait = NEGATIVE_TRAITS.includes(condition.traitNm); + const adjustedEbv = isNegativeTrait ? -ebv : ebv; + const contribution = adjustedEbv * weight; // EBV × 가중치 weightedSum += contribution; totalWeight += weight; @@ -1505,7 +1515,11 @@ export class GenomeService { const weight = condition.weight || 1; if (trait && trait.traitEbv !== null) { - weightedSum += Number(trait.traitEbv) * weight; + // 등지방두께 등 낮을수록 좋은 형질은 부호 반전 + const ebv = Number(trait.traitEbv); + const isNegativeTrait = NEGATIVE_TRAITS.includes(condition.traitNm); + const adjustedEbv = isNegativeTrait ? -ebv : ebv; + weightedSum += adjustedEbv * weight; totalWeight += weight; } else { hasAllTraits = false; @@ -1813,7 +1827,11 @@ export class GenomeService { const weight = condition.weight || 1; if (trait && trait.traitEbv !== null) { - weightedSum += Number(trait.traitEbv) * weight; + // 등지방두께 등 낮을수록 좋은 형질은 부호 반전 + const ebv = Number(trait.traitEbv); + const isNegativeTrait = NEGATIVE_TRAITS.includes(condition.traitNm); + const adjustedEbv = isNegativeTrait ? -ebv : ebv; + weightedSum += adjustedEbv * weight; totalWeight += weight; } else { hasAllTraits = false; diff --git a/frontend/src/app/cow/[cowNo]/genome/_components/trait-distribution-charts.tsx b/frontend/src/app/cow/[cowNo]/genome/_components/trait-distribution-charts.tsx index 4755704..10ddac4 100644 --- a/frontend/src/app/cow/[cowNo]/genome/_components/trait-distribution-charts.tsx +++ b/frontend/src/app/cow/[cowNo]/genome/_components/trait-distribution-charts.tsx @@ -6,6 +6,9 @@ import { Card, CardContent } from "@/components/ui/card" // 기본 7개 형질 const DEFAULT_TRAITS = ['도체중', '등심단면적', '등지방두께', '근내지방도', '체장', '체고', '등심weight'] +// 낮을수록 좋은 형질 (부호 반전 색상 적용) +const NEGATIVE_TRAITS = ['등지방두께'] + // 형질명 표시 (전체 이름) const TRAIT_SHORT_NAMES: Record = { '도체중': '도체중', @@ -82,12 +85,17 @@ function TraitListView({ traits, cowName }: { traits: Array<{ name: string; shor
- 0 - ? 'text-green-600' - : (trait.actualValue ?? 0) < 0 - ? 'text-red-600' - : 'text-muted-foreground' - }`}> + { + const value = trait.actualValue ?? 0 + const isNegativeTrait = NEGATIVE_TRAITS.includes(trait.name) + // 등지방두께: 음수가 좋음(녹색), 양수가 나쁨(빨간색) + // 나머지: 양수가 좋음(녹색), 음수가 나쁨(빨간색) + if (value === 0) return 'text-muted-foreground' + if (isNegativeTrait) { + return value < 0 ? 'text-green-600' : 'text-red-600' + } + return value > 0 ? 'text-green-600' : 'text-red-600' + })()}`}> {trait.actualValue !== undefined ? ( <>{trait.actualValue > 0 ? '+' : ''}{trait.actualValue.toFixed(1)} ) : '-'}