INIT
This commit is contained in:
81
backend/src/farm/entities/farm.entity.ts
Normal file
81
backend/src/farm/entities/farm.entity.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { BaseModel } from 'src/common/entities/base.entity';
|
||||
import { UserModel } from 'src/user/entities/user.entity';
|
||||
import {
|
||||
Column,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
/**
|
||||
* 농장 정보 (tb_farm)
|
||||
* 1사용자 N농장 관계
|
||||
*/
|
||||
@Entity({ name: 'tb_farm' })
|
||||
export class FarmModel extends BaseModel {
|
||||
@PrimaryGeneratedColumn({
|
||||
name: 'pk_farm_no',
|
||||
type: 'int',
|
||||
comment: '내부 PK (자동증가)',
|
||||
})
|
||||
pkFarmNo: number;
|
||||
|
||||
@Column({
|
||||
name: 'trace_farm_no',
|
||||
type: 'varchar',
|
||||
length: 50,
|
||||
nullable: true,
|
||||
comment: '축평원 농장번호 (나중에 입력)',
|
||||
})
|
||||
traceFarmNo: string;
|
||||
|
||||
@Column({
|
||||
name: 'fk_user_no',
|
||||
type: 'int',
|
||||
nullable: true,
|
||||
comment: '사용자정보 FK',
|
||||
})
|
||||
fkUserNo: number;
|
||||
|
||||
@Column({
|
||||
name: 'farmer_name',
|
||||
type: 'varchar',
|
||||
length: 100,
|
||||
nullable: true,
|
||||
comment: '농장주명 (농가명을 농장주로 사용)',
|
||||
})
|
||||
farmerName: string;
|
||||
|
||||
@Column({
|
||||
name: 'region_si',
|
||||
type: 'varchar',
|
||||
length: 50,
|
||||
nullable: true,
|
||||
comment: '시군',
|
||||
})
|
||||
regionSi: string;
|
||||
|
||||
@Column({
|
||||
name: 'region_gu',
|
||||
type: 'varchar',
|
||||
length: 50,
|
||||
nullable: true,
|
||||
comment: '시/군/구 (지역)',
|
||||
})
|
||||
regionGu: string;
|
||||
|
||||
@Column({
|
||||
name: 'road_address',
|
||||
type: 'varchar',
|
||||
length: 500,
|
||||
nullable: true,
|
||||
comment: '도로명 주소',
|
||||
})
|
||||
roadAddress: string;
|
||||
|
||||
// Relations
|
||||
@ManyToOne(() => UserModel, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'fk_user_no' })
|
||||
user: UserModel;
|
||||
}
|
||||
52
backend/src/farm/farm.controller.ts
Normal file
52
backend/src/farm/farm.controller.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { Controller, Get, Post, Put, Delete, Body, Param, Query } from '@nestjs/common';
|
||||
import { FarmService } from './farm.service';
|
||||
import { FarmModel } from './entities/farm.entity';
|
||||
|
||||
@Controller('farm')
|
||||
export class FarmController {
|
||||
constructor(private readonly farmService: FarmService) {}
|
||||
|
||||
@Get()
|
||||
findAll(@Query('userId') userId?: string) {
|
||||
if (userId) {
|
||||
return this.farmService.findByUserId(+userId);
|
||||
}
|
||||
return this.farmService.findAll();
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.farmService.findOne(+id);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /farm/:farmNo/analysis-latest - 농장 최신 분석 의뢰 정보 조회
|
||||
*/
|
||||
@Get(':farmNo/analysis-latest')
|
||||
getLatestAnalysisRequest(@Param('farmNo') farmNo: string) {
|
||||
return this.farmService.getLatestAnalysisRequest(+farmNo);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /farm/:farmNo/analysis-all - 농장 전체 분석 의뢰 목록 조회
|
||||
*/
|
||||
@Get(':farmNo/analysis-all')
|
||||
getAllAnalysisRequests(@Param('farmNo') farmNo: string) {
|
||||
return this.farmService.getAllAnalysisRequests(+farmNo);
|
||||
}
|
||||
|
||||
@Post()
|
||||
create(@Body() data: Partial<FarmModel>) {
|
||||
return this.farmService.create(data);
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
update(@Param('id') id: string, @Body() data: Partial<FarmModel>) {
|
||||
return this.farmService.update(+id, data);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: string) {
|
||||
return this.farmService.remove(+id);
|
||||
}
|
||||
}
|
||||
21
backend/src/farm/farm.module.ts
Normal file
21
backend/src/farm/farm.module.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { FarmController } from './farm.controller';
|
||||
import { FarmService } from './farm.service';
|
||||
import { FarmModel } from './entities/farm.entity';
|
||||
import { GenomeRequestModel } from '../genome/entities/genome-request.entity';
|
||||
import { CowModel } from '../cow/entities/cow.entity';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([
|
||||
FarmModel,
|
||||
GenomeRequestModel,
|
||||
CowModel,
|
||||
]),
|
||||
],
|
||||
controllers: [FarmController],
|
||||
providers: [FarmService],
|
||||
exports: [FarmService],
|
||||
})
|
||||
export class FarmModule {}
|
||||
128
backend/src/farm/farm.service.ts
Normal file
128
backend/src/farm/farm.service.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository, IsNull } from 'typeorm';
|
||||
import { FarmModel } from './entities/farm.entity';
|
||||
import { GenomeRequestModel } from '../genome/entities/genome-request.entity';
|
||||
import { CowModel } from '../cow/entities/cow.entity';
|
||||
import { isValidGenomeAnalysis, VALID_CHIP_SIRE_NAME } from '../common/config/GenomeAnalysisConfig';
|
||||
|
||||
@Injectable()
|
||||
export class FarmService {
|
||||
constructor(
|
||||
@InjectRepository(FarmModel)
|
||||
private readonly farmRepository: Repository<FarmModel>,
|
||||
|
||||
@InjectRepository(GenomeRequestModel)
|
||||
private readonly genomeRequestRepository: Repository<GenomeRequestModel>,
|
||||
|
||||
@InjectRepository(CowModel)
|
||||
private readonly cowRepository: Repository<CowModel>,
|
||||
) { }
|
||||
|
||||
// 전체 농장 조회
|
||||
async findAll(): Promise<FarmModel[]> {
|
||||
return this.farmRepository.find({
|
||||
where: { delDt: IsNull() },
|
||||
relations: ['user'],
|
||||
order: { regDt: 'DESC' },
|
||||
});
|
||||
}
|
||||
|
||||
// 사용자별 농장 조회
|
||||
async findByUserId(userNo: number): Promise<FarmModel[]> {
|
||||
return this.farmRepository.find({
|
||||
where: { fkUserNo: userNo, delDt: IsNull() },
|
||||
relations: ['user'],
|
||||
order: { regDt: 'DESC' },
|
||||
});
|
||||
}
|
||||
|
||||
// 농장 단건 조회
|
||||
async findOne(id: number): Promise<FarmModel> {
|
||||
const farm = await this.farmRepository.findOne({
|
||||
where: { pkFarmNo: id, delDt: IsNull() },
|
||||
relations: ['user'],
|
||||
});
|
||||
if (!farm) {
|
||||
throw new NotFoundException('Farm #' + id + ' not found');
|
||||
}
|
||||
return farm;
|
||||
}
|
||||
|
||||
// 농장 생성
|
||||
async create(data: Partial<FarmModel>): Promise<FarmModel> {
|
||||
const farm = this.farmRepository.create(data);
|
||||
return this.farmRepository.save(farm);
|
||||
}
|
||||
|
||||
// 농장 수정
|
||||
async update(id: number, data: Partial<FarmModel>): Promise<FarmModel> {
|
||||
await this.findOne(id);
|
||||
await this.farmRepository.update(id, data);
|
||||
return this.findOne(id);
|
||||
}
|
||||
|
||||
// 농장 삭제
|
||||
async remove(id: number): Promise<void> {
|
||||
const farm = await this.findOne(id);
|
||||
await this.farmRepository.softRemove(farm);
|
||||
}
|
||||
|
||||
// 농장 최신 분석 의뢰 정보 조회
|
||||
async getLatestAnalysisRequest(farmNo: number): Promise<any> {
|
||||
const farm = await this.findOne(farmNo);
|
||||
|
||||
const requests = await this.genomeRequestRepository.find({
|
||||
where: { fkFarmNo: farmNo, delDt: IsNull() },
|
||||
relations: ['cow'],
|
||||
order: { requestDt: 'DESC' },
|
||||
});
|
||||
|
||||
const cows = await this.cowRepository.find({
|
||||
where: { fkFarmNo: farmNo, delDt: IsNull() },
|
||||
});
|
||||
|
||||
const farmAnlysCnt = requests.length;
|
||||
const matchCnt = requests.filter(r => isValidGenomeAnalysis(r.chipSireName, r.chipDamName, r.cow?.cowId)).length;
|
||||
const failCnt = requests.filter(r => r.chipSireName && r.chipSireName !== '일치').length;
|
||||
const noHistCnt = requests.filter(r => !r.chipSireName).length;
|
||||
|
||||
return {
|
||||
pkFarmAnlysNo: 1,
|
||||
fkFarmNo: farmNo,
|
||||
farmAnlysNm: farm.farmerName,
|
||||
anlysReqDt: requests[0]?.requestDt || new Date(),
|
||||
region: farm.regionSi,
|
||||
city: farm.regionGu,
|
||||
anlysReqCnt: cows.length,
|
||||
farmAnlysCnt,
|
||||
matchCnt,
|
||||
mismatchCnt: failCnt,
|
||||
failCnt,
|
||||
noHistCnt,
|
||||
matchRate: farmAnlysCnt > 0 ? Math.round((matchCnt / farmAnlysCnt) * 100) : 0,
|
||||
msAnlysCnt: 0,
|
||||
anlysRmrk: '',
|
||||
paternities: requests.map(r => ({
|
||||
pkFarmPaternityNo: r.pkRequestNo,
|
||||
fkFarmAnlysNo: 1,
|
||||
receiptDate: r.requestDt,
|
||||
farmOwnerName: farm.farmerName,
|
||||
individualNo: r.cow?.cowId || '',
|
||||
kpnNo: r.cow?.sireKpn || '',
|
||||
motherIndividualNo: r.cow?.damCowId || '',
|
||||
hairRootQuality: r.sampleAmount || '',
|
||||
remarks: r.cowRemarks || '',
|
||||
fatherMatch: r.chipSireName === '일치' ? '일치' : (r.chipSireName ? '불일치' : '미확인'),
|
||||
motherMatch: r.chipDamName || '미확인',
|
||||
reportDate: r.chipReportDt,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
// 농장 전체 분석 의뢰 목록 조회
|
||||
async getAllAnalysisRequests(farmNo: number): Promise<any[]> {
|
||||
const latestRequest = await this.getLatestAnalysisRequest(farmNo);
|
||||
return [latestRequest];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user