style(FEAT-DS-001): 统一超短行情代码格式

This commit is contained in:
2026-08-24 21:49:48 +08:00
parent 61283c5c38
commit 1398804a6f
10 changed files with 35 additions and 52 deletions
@@ -27,7 +27,9 @@ describe('AKTools HTTP 客户端', () => {
}); });
it('将 HTTP 错误转换为脱敏的客户端错误', async () => { it('将 HTTP 错误转换为脱敏的客户端错误', async () => {
const fetchImpl = vi.fn().mockResolvedValue(new Response('secret upstream detail', { status: 500 })); const fetchImpl = vi
.fn()
.mockResolvedValue(new Response('secret upstream detail', { status: 500 }));
const client = createAktoolsClient({ const client = createAktoolsClient({
enabled: true, enabled: true,
baseUrl: 'http://127.0.0.1:8080', baseUrl: 'http://127.0.0.1:8080',
@@ -44,7 +46,9 @@ describe('AKTools HTTP 客户端', () => {
}); });
it('将请求超时转换为稳定错误', async () => { it('将请求超时转换为稳定错误', async () => {
const fetchImpl = vi.fn().mockRejectedValue(Object.assign(new Error('aborted'), { name: 'AbortError' })); const fetchImpl = vi
.fn()
.mockRejectedValue(Object.assign(new Error('aborted'), { name: 'AbortError' }));
const client = createAktoolsClient({ const client = createAktoolsClient({
enabled: true, enabled: true,
baseUrl: 'http://127.0.0.1:8080', baseUrl: 'http://127.0.0.1:8080',
@@ -2,11 +2,7 @@ import type { AktoolsRuntimeConfig } from '../../../../../libs/config/src/runtim
import type { AktoolsDataClient, AktoolsRawRow } from './aktools.types'; import type { AktoolsDataClient, AktoolsRawRow } from './aktools.types';
export type AktoolsClientErrorKind = export type AktoolsClientErrorKind =
| 'disabled' 'disabled' | 'timeout' | 'network' | 'protocol' | 'unavailable';
| 'timeout'
| 'network'
| 'protocol'
| 'unavailable';
export class AktoolsClientError extends Error { export class AktoolsClientError extends Error {
constructor( constructor(
@@ -58,14 +54,11 @@ export function createAktoolsClient({
const controller = new AbortController(); const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), timeoutMs); const timeout = setTimeout(() => controller.abort(), timeoutMs);
try { try {
const response = await fetchImpl( const response = await fetchImpl(`${endpoint}${path}?date=${encodeURIComponent(tradeDate)}`, {
`${endpoint}${path}?date=${encodeURIComponent(tradeDate)}`,
{
method: 'GET', method: 'GET',
headers: { accept: 'application/json' }, headers: { accept: 'application/json' },
signal: controller.signal, signal: controller.signal,
}, });
);
const contentLength = response.headers.get('content-length'); const contentLength = response.headers.get('content-length');
if (contentLength && Number(contentLength) > maxResponseBytes) { if (contentLength && Number(contentLength) > maxResponseBytes) {
throw new AktoolsClientError('protocol', 'AKTools 响应体超过安全上限'); throw new AktoolsClientError('protocol', 'AKTools 响应体超过安全上限');
@@ -1,10 +1,6 @@
import { describe, expect, it } from 'vitest'; import { describe, expect, it } from 'vitest';
import { import { mapBrokenBoardPool, mapLimitUpPool, mapPreviousLimitUpPool } from './aktools.mapper';
mapBrokenBoardPool,
mapLimitUpPool,
mapPreviousLimitUpPool,
} from './aktools.mapper';
describe('AKTools 超短字段映射', () => { describe('AKTools 超短字段映射', () => {
it('过滤缺失连板数的记录并生成连板梯队', () => { it('过滤缺失连板数的记录并生成连板梯队', () => {
@@ -21,9 +17,7 @@ describe('AKTools 超短字段映射', () => {
{ board: 2, count: 1 }, { board: 2, count: 1 },
{ board: 3, count: 1 }, { board: 3, count: 1 },
], ],
industries: [ industries: [{ name: '软件服务', count: 2 }],
{ name: '软件服务', count: 2 },
],
sealFundsAmount: 1.5, sealFundsAmount: 1.5,
}); });
}); });
@@ -74,9 +74,7 @@ export function mapBrokenBoardPool(rows: readonly unknown[]): BrokenBoardSummary
return { count: rows.filter((row) => hasCode(row)).length }; return { count: rows.filter((row) => hasCode(row)).length };
} }
export function mapPreviousLimitUpPool( export function mapPreviousLimitUpPool(rows: readonly AktoolsRawRow[]): PreviousLimitUpSummary {
rows: readonly AktoolsRawRow[],
): PreviousLimitUpSummary {
const changes = rows.flatMap((row) => { const changes = rows.flatMap((row) => {
if (!hasCode(row)) return []; if (!hasCode(row)) return [];
const change = toNumber(row['涨跌幅']); const change = toNumber(row['涨跌幅']);
@@ -1,18 +1,13 @@
import { Controller, Get, Inject } from '@nestjs/common'; import { Controller, Get, Inject } from '@nestjs/common';
import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { import { ShortTermConclusionResponseDto, ShortTermEmotionResponseDto } from './short-term.dto';
ShortTermConclusionResponseDto,
ShortTermEmotionResponseDto,
} from './short-term.dto';
import { ShortTermMarketService } from './short-term.service'; import { ShortTermMarketService } from './short-term.service';
@ApiTags('market-overview-short-term') @ApiTags('market-overview-short-term')
@Controller('market-overview/short-term') @Controller('market-overview/short-term')
export class ShortTermMarketController { export class ShortTermMarketController {
constructor( constructor(@Inject(ShortTermMarketService) private readonly service: ShortTermMarketService) {}
@Inject(ShortTermMarketService) private readonly service: ShortTermMarketService,
) {}
@Get('emotion') @Get('emotion')
@ApiOkResponse({ type: ShortTermEmotionResponseDto }) @ApiOkResponse({ type: ShortTermEmotionResponseDto })
@@ -14,7 +14,9 @@ describe('短线行情服务', () => {
it('组合涨停、炸板和昨日涨停数据并返回新鲜情绪快照', async () => { it('组合涨停、炸板和昨日涨停数据并返回新鲜情绪快照', async () => {
const client = { const client = {
getLimitUpPool: vi.fn().mockResolvedValue([ getLimitUpPool: vi
.fn()
.mockResolvedValue([
{ 代码: '000001', 连板数: 4, 所属行业: '软件服务', 封板资金: 100_000_000 }, { 代码: '000001', 连板数: 4, 所属行业: '软件服务', 封板资金: 100_000_000 },
]), ]),
getBrokenBoardPool: vi.fn().mockResolvedValue([{ 代码: '000002' }]), getBrokenBoardPool: vi.fn().mockResolvedValue([{ 代码: '000002' }]),
@@ -98,9 +98,7 @@ function createConclusion(data: ShortTermEmotionDataDto): ShortTermConclusionDat
@Injectable() @Injectable()
export class ShortTermMarketService { export class ShortTermMarketService {
constructor( constructor(@Inject(AKTOOLS_CLIENT_PORT) private readonly aktoolsClient: AktoolsDataClient) {}
@Inject(AKTOOLS_CLIENT_PORT) private readonly aktoolsClient: AktoolsDataClient,
) {}
async getEmotion(): Promise<ShortTermEmotionResponseDto> { async getEmotion(): Promise<ShortTermEmotionResponseDto> {
const tradeDate = getShanghaiDate(new Date()); const tradeDate = getShanghaiDate(new Date());
+6 -9
View File
@@ -4,7 +4,10 @@ import request from 'supertest';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { ShortTermMarketController } from '../src/market-overview/short-term.controller'; import { ShortTermMarketController } from '../src/market-overview/short-term.controller';
import { AKTOOLS_CLIENT_PORT, ShortTermMarketService } from '../src/market-overview/short-term.service'; import {
AKTOOLS_CLIENT_PORT,
ShortTermMarketService,
} from '../src/market-overview/short-term.service';
describe('短线行情 HTTP API', () => { describe('短线行情 HTTP API', () => {
let app: INestApplication | undefined; let app: INestApplication | undefined;
@@ -31,10 +34,7 @@ describe('短线行情 HTTP API', () => {
}; };
const moduleRef = await Test.createTestingModule({ const moduleRef = await Test.createTestingModule({
controllers: [ShortTermMarketController], controllers: [ShortTermMarketController],
providers: [ providers: [ShortTermMarketService, { provide: AKTOOLS_CLIENT_PORT, useValue: client }],
ShortTermMarketService,
{ provide: AKTOOLS_CLIENT_PORT, useValue: client },
],
}).compile(); }).compile();
app = moduleRef.createNestApplication(); app = moduleRef.createNestApplication();
app.setGlobalPrefix('api/v1'); app.setGlobalPrefix('api/v1');
@@ -61,10 +61,7 @@ describe('短线行情 HTTP API', () => {
}; };
const moduleRef = await Test.createTestingModule({ const moduleRef = await Test.createTestingModule({
controllers: [ShortTermMarketController], controllers: [ShortTermMarketController],
providers: [ providers: [ShortTermMarketService, { provide: AKTOOLS_CLIENT_PORT, useValue: client }],
ShortTermMarketService,
{ provide: AKTOOLS_CLIENT_PORT, useValue: client },
],
}).compile(); }).compile();
app = moduleRef.createNestApplication(); app = moduleRef.createNestApplication();
app.setGlobalPrefix('api/v1'); app.setGlobalPrefix('api/v1');
+5 -1
View File
@@ -60,7 +60,11 @@ describe('NestJS 三进程入口', () => {
logLevel: 'debug', logLevel: 'debug',
api: Object.freeze({ host: '127.0.0.1', port: 3000 }), api: Object.freeze({ host: '127.0.0.1', port: 3000 }),
tdx: Object.freeze({ baseUrl: 'http://127.0.0.1:17710', timeoutMs: 5000 }), tdx: Object.freeze({ baseUrl: 'http://127.0.0.1:17710', timeoutMs: 5000 }),
aktools: Object.freeze({ baseUrl: 'http://127.0.0.1:8080', enabled: false, timeoutMs: 10000 }), aktools: Object.freeze({
baseUrl: 'http://127.0.0.1:8080',
enabled: false,
timeoutMs: 10000,
}),
redis: redisConfig, redis: redisConfig,
mysql: mysqlConfig, mysql: mysqlConfig,
ai: aiConfig, ai: aiConfig,
@@ -67,9 +67,7 @@ export function useMarketOverview(
funds.value, funds.value,
shortTermEmotion.value, shortTermEmotion.value,
shortTermConclusion.value, shortTermConclusion.value,
].some( ].some((module) => module.status === 'loading'),
(module) => module.status === 'loading',
),
); );
function setLoading() { function setLoading() {