This commit is contained in:
2025-12-09 17:02:27 +09:00
parent 26f8e1dab2
commit 83127da569
275 changed files with 139682 additions and 1 deletions

View File

@@ -0,0 +1,58 @@
import { IsNotEmpty, IsString, IsOptional, IsInt, MaxLength, IsIn } from 'class-validator';
/**
* 도움말 생성 DTO
*
* @export
* @class CreateHelpDto
*/
export class CreateHelpDto {
@IsNotEmpty()
@IsString()
@IsIn(['SNP', 'GENOME', 'MPT'])
@MaxLength(20)
helpCtgry: string;
@IsNotEmpty()
@IsString()
@MaxLength(100)
targetNm: string;
@IsOptional()
@IsString()
@MaxLength(200)
helpTitle?: string;
@IsOptional()
@IsString()
helpShort?: string;
@IsOptional()
@IsString()
helpFull?: string;
@IsOptional()
@IsString()
@MaxLength(500)
helpImageUrl?: string;
@IsOptional()
@IsString()
@MaxLength(500)
helpVideoUrl?: string;
@IsOptional()
@IsString()
@MaxLength(500)
helpLinkUrl?: string;
@IsOptional()
@IsInt()
displayOrder?: number;
@IsOptional()
@IsString()
@IsIn(['Y', 'N'])
@MaxLength(1)
useYn?: string;
}

View File

@@ -0,0 +1,26 @@
import { IsOptional, IsString, IsIn, MaxLength } from 'class-validator';
/**
* 도움말 필터링 DTO
*
* @export
* @class FilterHelpDto
*/
export class FilterHelpDto {
@IsOptional()
@IsString()
@IsIn(['SNP', 'GENOME', 'MPT'])
@MaxLength(20)
helpCtgry?: string;
@IsOptional()
@IsString()
@MaxLength(100)
targetNm?: string;
@IsOptional()
@IsString()
@IsIn(['Y', 'N'])
@MaxLength(1)
useYn?: string;
}

View File

@@ -0,0 +1,11 @@
import { PartialType } from '@nestjs/mapped-types';
import { CreateHelpDto } from './create-help.dto';
/**
* 도움말 수정 DTO
*
* @export
* @class UpdateHelpDto
* @extends {PartialType(CreateHelpDto)}
*/
export class UpdateHelpDto extends PartialType(CreateHelpDto) {}

View File

@@ -0,0 +1,108 @@
import { BaseModel } from "src/common/entities/base.entity";
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
@Entity({ name: "tb_help" })
export class HelpModel extends BaseModel {
@PrimaryGeneratedColumn({
name: "pk_help_no",
type: "int",
comment: "도움말 번호",
})
pkHelpNo: number;
@Column({
name: "help_ctgry",
type: "varchar",
length: 20,
nullable: false,
comment: "분류 (SNP/GENOME/MPT)",
})
helpCtgry: string;
@Column({
name: "target_nm",
type: "varchar",
length: 100,
nullable: false,
comment: "대상명 (PLAG1, 도체중, 혈당 등)",
})
targetNm: string;
@Column({
name: "help_title",
type: "varchar",
length: 200,
nullable: true,
comment: "제목",
})
helpTitle: string;
@Column({
name: "help_short",
type: "text",
nullable: true,
comment: "짧은 설명 (툴팁용)",
})
helpShort: string;
@Column({
name: "help_full",
type: "text",
nullable: true,
comment: "상세 설명 (사이드패널용)",
})
helpFull: string;
@Column({
name: "help_image_url",
type: "varchar",
length: 500,
nullable: true,
comment: "이미지 URL",
})
helpImageUrl: string;
@Column({
name: "help_video_url",
type: "varchar",
length: 500,
nullable: true,
comment: "영상 URL",
})
helpVideoUrl: string;
@Column({
name: "help_link_url",
type: "varchar",
length: 500,
nullable: true,
comment: "참고 링크 URL",
})
helpLinkUrl: string;
@Column({
name: "display_order",
type: "int",
nullable: true,
comment: "표시 순서",
})
displayOrder: number;
@Column({
name: "use_yn",
type: "char",
length: 1,
nullable: false,
default: "Y",
comment: "사용 여부 (Y/N)",
})
useYn: string;
// BaseModel에서 상속받는 컬럼들:
// - regDt: 등록일시
// - updtDt: 수정일시
// - regIp: 등록 IP
// - updtIp: 수정 IP
// - regUserId: 등록자 ID
// - updtUserId: 수정자 ID
}

View File

@@ -0,0 +1,185 @@
import { Controller, Get, Post, Put, Delete, Body, Param, Query, Req } from '@nestjs/common';
import { HelpService } from './help.service';
import { CreateHelpDto } from './dto/create-help.dto';
import { UpdateHelpDto } from './dto/update-help.dto';
import { FilterHelpDto } from './dto/filter-help.dto';
import { Request } from 'express';
/**
* Help Controller
*
* @description
* 도움말/툴팁 시스템 API 엔드포인트를 제공합니다.
*
* 주요 기능:
* - 도움말 CRUD (생성, 조회, 수정, 삭제)
* - 카테고리별 조회 (SNP/GENOME/MPT)
* - 대상명별 조회 (PLAG1, 도체중 등)
* - 툴팁 데이터 제공
*
* @export
* @class HelpController
*/
@Controller('help')
export class HelpController {
constructor(private readonly helpService: HelpService) {}
/**
* POST /help - 도움말 생성 (관리자)
*
* @description
* 새로운 도움말을 생성합니다.
*
* @example
* // POST /help
* {
* "helpCtgry": "SNP",
* "targetNm": "PLAG1",
* "helpTitle": "PLAG1 유전자란?",
* "helpShort": "체고 및 성장 관련 유전자",
* "helpFull": "PLAG1은 소의 체고와 성장에 영향을 미치는 주요 유전자입니다...",
* "displayOrder": 1,
* "useYn": "Y"
* }
*
* @param {CreateHelpDto} createHelpDto - 생성할 도움말 데이터
* @param {Request} req - Express Request 객체
* @returns {Promise<HelpModel>}
*/
@Post()
async create(@Body() createHelpDto: CreateHelpDto, @Req() req: Request) {
const userId = (req as any).user?.userId || 'system';
const ip = req.ip || req.socket.remoteAddress || 'unknown';
return await this.helpService.create(createHelpDto, userId, ip);
}
/**
* GET /help - 전체 도움말 목록 조회
*
* @description
* 전체 도움말 목록을 조회합니다. 필터 조건을 통해 검색 가능합니다.
*
* @example
* // GET /help
* // GET /help?helpCtgry=SNP
* // GET /help?useYn=Y
* // GET /help?targetNm=PLAG1
*
* @param {FilterHelpDto} filterDto - 필터 조건 (선택)
* @returns {Promise<HelpModel[]>}
*/
@Get()
async findAll(@Query() filterDto: FilterHelpDto) {
return await this.helpService.findAll(filterDto);
}
/**
* GET /help/category/:category - 카테고리별 도움말 조회
*
* @description
* 특정 카테고리(SNP/GENOME/MPT)의 모든 도움말을 조회합니다.
*
* @example
* // GET /help/category/SNP
* // GET /help/category/GENOME
* // GET /help/category/MPT
*
* @param {string} category - 카테고리 (SNP/GENOME/MPT)
* @returns {Promise<HelpModel[]>}
*/
@Get('category/:category')
async findByCategory(@Param('category') category: string) {
return await this.helpService.findByCategory(category);
}
/**
* GET /help/:category/:targetNm - 특정 대상의 도움말 조회
*
* @description
* 특정 카테고리와 대상명에 해당하는 도움말을 조회합니다.
* 툴팁이나 사이드패널에서 사용됩니다.
*
* @example
* // GET /help/SNP/PLAG1
* // GET /help/GENOME/도체중
* // GET /help/MPT/혈당
*
* @param {string} category - 카테고리 (SNP/GENOME/MPT)
* @param {string} targetNm - 대상명 (PLAG1, 도체중 등)
* @returns {Promise<HelpModel>}
*/
@Get(':category/:targetNm')
async findByTarget(
@Param('category') category: string,
@Param('targetNm') targetNm: string,
) {
return await this.helpService.findByTarget(category, targetNm);
}
/**
* GET /help/id/:id - 도움말 단건 조회
*
* @description
* 도움말 번호로 단건을 조회합니다.
*
* @example
* // GET /help/id/1
*
* @param {number} id - 도움말 번호
* @returns {Promise<HelpModel>}
*/
@Get('id/:id')
async findOne(@Param('id') id: number) {
return await this.helpService.findOne(id);
}
/**
* PUT /help/:id - 도움말 수정 (관리자)
*
* @description
* 기존 도움말을 수정합니다.
*
* @example
* // PUT /help/1
* {
* "helpTitle": "수정된 제목",
* "helpShort": "수정된 짧은 설명",
* "displayOrder": 2
* }
*
* @param {number} id - 도움말 번호
* @param {UpdateHelpDto} updateHelpDto - 수정할 데이터
* @param {Request} req - Express Request 객체
* @returns {Promise<HelpModel>}
*/
@Put(':id')
async update(
@Param('id') id: number,
@Body() updateHelpDto: UpdateHelpDto,
@Req() req: Request,
) {
const userId = (req as any).user?.userId || 'system';
const ip = req.ip || req.socket.remoteAddress || 'unknown';
return await this.helpService.update(id, updateHelpDto, userId, ip);
}
/**
* DELETE /help/:id - 도움말 삭제 (관리자)
*
* @description
* 도움말을 삭제합니다 (soft delete - useYn = 'N').
*
* @example
* // DELETE /help/1
*
* @param {number} id - 도움말 번호
* @param {Request} req - Express Request 객체
* @returns {Promise<void>}
*/
@Delete(':id')
async remove(@Param('id') id: number, @Req() req: Request) {
const userId = (req as any).user?.userId || 'system';
const ip = req.ip || req.socket.remoteAddress || 'unknown';
return await this.helpService.remove(id, userId, ip);
}
}

View File

@@ -0,0 +1,28 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { HelpController } from './help.controller';
import { HelpService } from './help.service';
import { HelpModel } from './entities/help.entity';
/**
* Help Module
*
* @description
* 도움말/툴팁 시스템 모듈입니다.
* SNP, GENOME, MPT 등의 용어에 대한 설명을 제공합니다.
*
* 주요 기능:
* - 도움말 CRUD
* - 카테고리별 조회
* - 툴팁/사이드패널 데이터 제공
*
* @export
* @class HelpModule
*/
@Module({
imports: [TypeOrmModule.forFeature([HelpModel])],
controllers: [HelpController],
providers: [HelpService],
exports: [HelpService],
})
export class HelpModule {}

View File

@@ -0,0 +1,179 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { HelpModel } from './entities/help.entity';
import { CreateHelpDto } from './dto/create-help.dto';
import { UpdateHelpDto } from './dto/update-help.dto';
import { FilterHelpDto } from './dto/filter-help.dto';
/**
* Help Service
*
* @description
* 도움말/툴팁 시스템 서비스입니다.
* SNP, GENOME, MPT 등의 용어에 대한 도움말을 제공합니다.
*
* @export
* @class HelpService
*/
@Injectable()
export class HelpService {
constructor(
@InjectRepository(HelpModel)
private readonly helpRepository: Repository<HelpModel>,
) {}
/**
* 도움말 생성
*
* @param {CreateHelpDto} createHelpDto - 생성할 도움말 데이터
* @param {string} userId - 생성자 ID
* @param {string} ip - 생성자 IP
* @returns {Promise<HelpModel>}
*/
async create(createHelpDto: CreateHelpDto, userId: string, ip: string): Promise<HelpModel> {
const help = this.helpRepository.create({
...createHelpDto,
regUserId: userId,
regIp: ip,
useYn: createHelpDto.useYn || 'Y',
});
return await this.helpRepository.save(help);
}
/**
* 전체 도움말 목록 조회
*
* @param {FilterHelpDto} filterDto - 필터 조건 (선택)
* @returns {Promise<HelpModel[]>}
*/
async findAll(filterDto?: FilterHelpDto): Promise<HelpModel[]> {
const queryBuilder = this.helpRepository.createQueryBuilder('help');
if (filterDto?.helpCtgry) {
queryBuilder.andWhere('help.helpCtgry = :helpCtgry', { helpCtgry: filterDto.helpCtgry });
}
if (filterDto?.targetNm) {
queryBuilder.andWhere('help.targetNm LIKE :targetNm', { targetNm: `%${filterDto.targetNm}%` });
}
if (filterDto?.useYn) {
queryBuilder.andWhere('help.useYn = :useYn', { useYn: filterDto.useYn });
}
return await queryBuilder
.orderBy('help.displayOrder', 'ASC')
.addOrderBy('help.pkHelpNo', 'DESC')
.getMany();
}
/**
* 카테고리별 도움말 조회
*
* @param {string} category - 카테고리 (SNP/GENOME/MPT)
* @returns {Promise<HelpModel[]>}
*/
async findByCategory(category: string): Promise<HelpModel[]> {
return await this.helpRepository.find({
where: {
helpCtgry: category,
useYn: 'Y',
},
order: {
displayOrder: 'ASC',
pkHelpNo: 'DESC',
},
});
}
/**
* 특정 대상명의 도움말 조회
*
* @param {string} category - 카테고리 (SNP/GENOME/MPT)
* @param {string} targetNm - 대상명 (예: PLAG1, 도체중 등)
* @returns {Promise<HelpModel>}
*/
async findByTarget(category: string, targetNm: string): Promise<HelpModel> {
const help = await this.helpRepository.findOne({
where: {
helpCtgry: category,
targetNm: targetNm,
useYn: 'Y',
},
});
if (!help) {
throw new NotFoundException(`도움말을 찾을 수 없습니다. (카테고리: ${category}, 대상: ${targetNm})`);
}
return help;
}
/**
* 도움말 번호로 단건 조회
*
* @param {number} id - 도움말 번호
* @returns {Promise<HelpModel>}
*/
async findOne(id: number): Promise<HelpModel> {
const help = await this.helpRepository.findOne({
where: { pkHelpNo: id },
});
if (!help) {
throw new NotFoundException(`도움말을 찾을 수 없습니다. (ID: ${id})`);
}
return help;
}
/**
* 도움말 수정
*
* @param {number} id - 도움말 번호
* @param {UpdateHelpDto} updateHelpDto - 수정할 데이터
* @param {string} userId - 수정자 ID
* @param {string} ip - 수정자 IP
* @returns {Promise<HelpModel>}
*/
async update(id: number, updateHelpDto: UpdateHelpDto, userId: string, ip: string): Promise<HelpModel> {
const help = await this.findOne(id);
Object.assign(help, updateHelpDto);
help.updtUserId = userId;
help.updtIp = ip;
return await this.helpRepository.save(help);
}
/**
* 도움말 삭제 (soft delete - useYn = 'N')
*
* @param {number} id - 도움말 번호
* @param {string} userId - 삭제자 ID
* @param {string} ip - 삭제자 IP
* @returns {Promise<void>}
*/
async remove(id: number, userId: string, ip: string): Promise<void> {
const help = await this.findOne(id);
help.useYn = 'N';
help.updtUserId = userId;
help.updtIp = ip;
await this.helpRepository.save(help);
}
/**
* 도움말 영구 삭제 (hard delete)
*
* @param {number} id - 도움말 번호
* @returns {Promise<void>}
*/
async hardRemove(id: number): Promise<void> {
const help = await this.findOne(id);
await this.helpRepository.remove(help);
}
}