Skip to content

Commit

Permalink
Feature/#22 주식 차트 데이터 조회 기능 추가 (#163)
Browse files Browse the repository at this point in the history
  • Loading branch information
demian-m00n authored Nov 14, 2024
2 parents 225309b + e84dbb7 commit 28a1a3a
Show file tree
Hide file tree
Showing 8 changed files with 970 additions and 2 deletions.
35 changes: 35 additions & 0 deletions packages/backend/src/stock/decorator/stockData.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* eslint-disable @typescript-eslint/naming-convention */
/* eslint-disable max-lines-per-function */

import { applyDecorators } from '@nestjs/common';
import { ApiOperation, ApiParam, ApiQuery, ApiResponse } from '@nestjs/swagger';
import { StockDataResponse } from '../dto/stockData.response';

export function ApiGetStockData(summary: string, type: string) {
return applyDecorators(
ApiOperation({ summary }),
ApiParam({
name: 'stockId',
type: String,
description: '주식 ID',
example: 'A005930',
}),
ApiQuery({
name: 'lastStartTime',
required: false,
description: '마지막 시작 시간 (ISO 8601 형식)',
example: '2024-04-01T00:00:00.000Z',
type: String,
format: 'date-time',
}),
ApiResponse({
status: 200,
description: `주식의 ${type} 단위 데이터 성공적으로 조회`,
type: StockDataResponse,
}),
ApiResponse({
status: 404,
description: '주식 데이터가 존재하지 않음',
}),
);
}
51 changes: 51 additions & 0 deletions packages/backend/src/stock/domain/stockData.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
JoinColumn,
ManyToOne,
} from 'typeorm';
import { Stock } from './stock.entity';

abstract class StockData {
@PrimaryGeneratedColumn()
id: number;

@Column({ type: 'decimal', precision: 15, scale: 2 })
close: number;

@Column({ type: 'decimal', precision: 15, scale: 2 })
low: number;

@Column({ type: 'decimal', precision: 15, scale: 2 })
high: number;

@Column({ type: 'decimal', precision: 15, scale: 2 })
open: number;

@Column({ type: 'bigint' })
volume: number;

@Column({ type: 'timestamp', name: 'start_time' })
startTime: Date;

@ManyToOne(() => Stock)
@JoinColumn({ name: 'stock_id' })
stock: Stock;

@CreateDateColumn({ name: 'created_at' })
createdAt: Date;
}

@Entity('stock_minutely')
export class StockMinutely extends StockData {}

@Entity('stock_daily')
export class StockDaily extends StockData {}
@Entity('stock_weekly')
export class StockWeekly extends StockData {}
@Entity('stock_monthly')
export class StockMonthly extends StockData {}
@Entity('stock_yearly')
export class StockYearly extends StockData {}
74 changes: 74 additions & 0 deletions packages/backend/src/stock/dto/stockData.response.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer';

export class PriceDto {
@ApiProperty({
description: '시작 시간',
type: String,
format: 'date-time',
example: '2024-04-01T00:00:00.000Z',
})
startTime: Date;

@ApiProperty({
description: '시가',
example: '121.00',
})
open: number;

@ApiProperty({
description: '고가',
example: '125.00',
})
high: number;

@ApiProperty({
description: '저가',
example: '120.00',
})
low: number;

@ApiProperty({
description: '종가',
example: '123.45',
})
close: number;
}

export class VolumeDto {
@ApiProperty({
description: '시작 시간',
type: String,
format: 'date-time',
example: '2024-04-01T00:00:00.000Z',
})
startTime: Date;

@ApiProperty({
description: '거래량',
example: 1000,
})
volume: number;
}

export class StockDataResponse {
@ApiProperty({
description: '가격 데이터 배열 (날짜 오름차순)',
type: [PriceDto],
})
@Type(() => PriceDto)
priceDtoList: PriceDto[];

@ApiProperty({
description: '거래량 데이터 배열 (날짜 오름차순, 색 포함)',
type: [VolumeDto],
})
@Type(() => VolumeDto)
volumeDtoList: VolumeDto[];

@ApiProperty({
description: '스크롤해서 불러올 수 있는 데이터가 더 존재하는지',
example: true,
})
hasMore: boolean;
}
75 changes: 75 additions & 0 deletions packages/backend/src/stock/stock.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,20 @@ import {
HttpCode,
Param,
Post,
Query,
} from '@nestjs/common';
import { ApiOkResponse, ApiOperation, ApiParam } from '@nestjs/swagger';
import { ApiGetStockData } from './decorator/stockData.decorator';
import { StockDetailResponse } from './dto/stockDetail.response';
import { StockService } from './stock.service';
import {
StockDataDailyService,
StockDataMinutelyService,
StockDataMonthlyService,
StockDataWeeklyService,
StockDataYearlyService,
} from './stockData.service';
import { ApiOkResponse, ApiOperation, ApiParam } from '@nestjs/swagger';
import { StockDetailResponse } from './dto/stockDetail.response';
import { StockService } from './stock.service';
import { StockDetailService } from './stockDetail.service';
Expand All @@ -23,6 +35,11 @@ import { UserStockResponse } from '@/stock/dto/userStock.response';
export class StockController {
constructor(
private readonly stockService: StockService,
private readonly stockDataMinutelyService: StockDataMinutelyService,
private readonly stockDataDailyService: StockDataDailyService,
private readonly stockDataWeeklyService: StockDataWeeklyService,
private readonly stockDataMonthlyService: StockDataMonthlyService,
private readonly stockDataYearlyService: StockDataYearlyService,
private readonly stockDetailService: StockDetailService,
) {}

Expand Down Expand Up @@ -94,6 +111,64 @@ export class StockController {
);
}

@Get(':stockId/minutely')
@ApiGetStockData('주식 분 단위 데이터 조회 API', '분')
async getStockDataMinutely(
@Param('stockId') stockId: string,
@Query('lastStartTime') lastStartTime?: string,
) {
return this.stockDataMinutelyService.getStockDataMinutely(
stockId,
lastStartTime,
);
}

@Get(':stockId/daily')
@ApiGetStockData('주식 일 단위 데이터 조회 API', '일')
async getStockDataDaily(
@Param('stockId') stockId: string,
@Query('lastStartTime') lastStartTime?: string,
) {
return this.stockDataDailyService.getStockDataDaily(stockId, lastStartTime);
}

@Get(':stockId/weekly')
@ApiGetStockData('주식 주 단위 데이터 조회 API', '주')
async getStockDataWeekly(
@Param('stockId') stockId: string,
@Query('lastStartTime') lastStartTime?: string,
) {
return this.stockDataWeeklyService.getStockDataWeekly(
stockId,
lastStartTime,
);
}

@Get(':stockId/mothly')
@ApiGetStockData('주식 월 단위 데이터 조회 API', '월')
async getStockDataMonthly(
@Param('stockId') stockId: string,
@Query('lastStartTime') lastStartTime?: string,
) {
return this.stockDataMonthlyService.getStockDataMonthly(
stockId,
lastStartTime,
);
}

@Get(':stockId/yearly')
@ApiGetStockData('주식 연 단위 데이터 조회 API', '연')
async getStockDataYearly(
@Param('stockId') stockId: string,
@Query('lastStartTime') lastStartTime?: string,
) {
return this.stockDataYearlyService.getStockDataYearly(
stockId,
lastStartTime,
);
}


@ApiOperation({
summary: '주식 상세 정보 조회 API',
description: '시가 총액, EPS, PER, 52주 최고가, 52주 최저가를 조회합니다',
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/stock/stock.gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
import { Server, Socket } from 'socket.io';

@WebSocketGateway({
path: '/stock',
namespace: '/stock/realtime',
})
export class StockGateway {
@WebSocketServer()
Expand Down
36 changes: 35 additions & 1 deletion packages/backend/src/stock/stock.module.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,53 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Stock } from './domain/stock.entity';
import {
StockDaily,
StockMinutely,
StockMonthly,
StockWeekly,
StockYearly,
} from './domain/stockData.entity';
import { StockDetail } from './domain/stockDetail.entity';
import { StockLiveData } from './domain/stockLiveData.entity';
import { StockController } from './stock.controller';
import { StockGateway } from './stock.gateway';
import { StockService } from './stock.service';
import {
StockDataDailyService,
StockDataMinutelyService,
StockDataMonthlyService,
StockDataService,
StockDataWeeklyService,
StockDataYearlyService,
} from './stockData.service';
import { StockDetailService } from './stockDetail.service';
import { StockLiveDataSubscriber } from './stockLiveData.subscriber';

@Module({
imports: [TypeOrmModule.forFeature([Stock])],
imports: [
TypeOrmModule.forFeature([
Stock,
StockMinutely,
StockDaily,
StockWeekly,
StockMonthly,
StockYearly,
StockLiveData,
StockDetail,
]),
],
controllers: [StockController],
providers: [
StockService,
StockGateway,
StockLiveDataSubscriber,
StockDataService,
StockDataDailyService,
StockDataMinutelyService,
StockDataWeeklyService,
StockDataYearlyService,
StockDataMonthlyService,
StockDetailService,
],
})
Expand Down
Loading

0 comments on commit 28a1a3a

Please sign in to comment.