You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
124 lines
3.3 KiB
124 lines
3.3 KiB
|
3 months ago
|
import { Request, Response } from 'express';
|
||
|
|
import { stockService } from '../services/stockService';
|
||
|
|
import { asyncHandler, NotFoundError, BadRequestError } from '../middleware/errorHandler';
|
||
|
|
import { ApiResponse } from '../types';
|
||
|
|
import { searchKeywordSchema, periodSchema } from '../utils/validator';
|
||
|
|
|
||
|
|
export const stockController = {
|
||
|
|
// 搜索股票
|
||
|
|
search: asyncHandler(async (req: Request, res: Response) => {
|
||
|
|
const { keyword, type } = req.query;
|
||
|
|
|
||
|
|
if (!keyword || typeof keyword !== 'string') {
|
||
|
|
throw new BadRequestError('搜索关键词不能为空');
|
||
|
|
}
|
||
|
|
|
||
|
|
const validatedKeyword = searchKeywordSchema.parse(keyword);
|
||
|
|
|
||
|
|
let sectors: any[] = [];
|
||
|
|
let stocks: any[] = [];
|
||
|
|
|
||
|
|
if (!type || type === 'all' || type === 'sector') {
|
||
|
|
sectors = await stockService.searchSectors(validatedKeyword);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!type || type === 'all' || type === 'stock') {
|
||
|
|
stocks = await stockService.searchStocks(validatedKeyword);
|
||
|
|
}
|
||
|
|
|
||
|
|
const response: ApiResponse = {
|
||
|
|
code: 200,
|
||
|
|
message: 'success',
|
||
|
|
data: {
|
||
|
|
sectors,
|
||
|
|
stocks,
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
res.json(response);
|
||
|
|
}),
|
||
|
|
|
||
|
|
// 获取股票详情
|
||
|
|
getStockDetail: asyncHandler(async (req: Request, res: Response) => {
|
||
|
|
const { stock_code } = req.params;
|
||
|
|
|
||
|
|
const stock = await stockService.getStockDetail(stock_code);
|
||
|
|
|
||
|
|
if (!stock) {
|
||
|
|
throw new NotFoundError('股票不存在');
|
||
|
|
}
|
||
|
|
|
||
|
|
const response: ApiResponse = {
|
||
|
|
code: 200,
|
||
|
|
message: 'success',
|
||
|
|
data: stock,
|
||
|
|
};
|
||
|
|
|
||
|
|
res.json(response);
|
||
|
|
}),
|
||
|
|
|
||
|
|
// 获取股票K线数据
|
||
|
|
getStockKLine: asyncHandler(async (req: Request, res: Response) => {
|
||
|
|
const { stock_code } = req.params;
|
||
|
|
const period = periodSchema.parse(req.query.period || 'day');
|
||
|
|
const days = parseInt(req.query.days as string) || 60;
|
||
|
|
|
||
|
|
const klines = await stockService.getKLineData(stock_code, period, days);
|
||
|
|
|
||
|
|
const response: ApiResponse = {
|
||
|
|
code: 200,
|
||
|
|
message: 'success',
|
||
|
|
data: klines,
|
||
|
|
};
|
||
|
|
|
||
|
|
res.json(response);
|
||
|
|
}),
|
||
|
|
|
||
|
|
// 获取新高股票
|
||
|
|
getNewHighStocks: asyncHandler(async (req: Request, res: Response) => {
|
||
|
|
const days = parseInt(req.query.days as string) || 20;
|
||
|
|
const limit = parseInt(req.query.limit as string) || 20;
|
||
|
|
|
||
|
|
const stocks = await stockService.getNewHighStocks(days, limit);
|
||
|
|
|
||
|
|
const response: ApiResponse = {
|
||
|
|
code: 200,
|
||
|
|
message: 'success',
|
||
|
|
data: stocks,
|
||
|
|
};
|
||
|
|
|
||
|
|
res.json(response);
|
||
|
|
}),
|
||
|
|
|
||
|
|
// 获取新低股票
|
||
|
|
getNewLowStocks: asyncHandler(async (req: Request, res: Response) => {
|
||
|
|
const days = parseInt(req.query.days as string) || 20;
|
||
|
|
const limit = parseInt(req.query.limit as string) || 20;
|
||
|
|
|
||
|
|
const stocks = await stockService.getNewLowStocks(days, limit);
|
||
|
|
|
||
|
|
const response: ApiResponse = {
|
||
|
|
code: 200,
|
||
|
|
message: 'success',
|
||
|
|
data: stocks,
|
||
|
|
};
|
||
|
|
|
||
|
|
res.json(response);
|
||
|
|
}),
|
||
|
|
|
||
|
|
// 获取动量股推荐
|
||
|
|
getMomentumRecommendation: asyncHandler(async (req: Request, res: Response) => {
|
||
|
|
const limit = parseInt(req.query.limit as string) || 15;
|
||
|
|
|
||
|
|
const stocks = await stockService.getMomentumStocks(limit);
|
||
|
|
|
||
|
|
const response: ApiResponse = {
|
||
|
|
code: 200,
|
||
|
|
message: 'success',
|
||
|
|
data: stocks,
|
||
|
|
};
|
||
|
|
|
||
|
|
res.json(response);
|
||
|
|
}),
|
||
|
|
};
|