feat: 添加投标信息按日期范围更新功能及AI推荐持久化

添加按日期范围更新投标信息的功能,支持日期范围选择和数据更新
实现AI推荐结果的持久化存储和加载功能
优化日期范围选择器的本地存储功能
This commit is contained in:
dmy
2026-01-12 19:50:51 +08:00
parent 2b21ddb990
commit af58d770b6
8 changed files with 227 additions and 13 deletions

View File

@@ -1,5 +1,5 @@
import { Controller, Post, Body } from '@nestjs/common';
import { AiService } from './ai.service';
import { Controller, Post, Body, Get } from '@nestjs/common';
import { AiService, AIRecommendation } from './ai.service';
export class BidDataDto {
title: string;
@@ -9,6 +9,10 @@ export class BidsRequestDto {
bids: BidDataDto[];
}
export class SaveRecommendationsDto {
recommendations: AIRecommendation[];
}
@Controller('api/ai')
export class AiController {
constructor(private readonly aiService: AiService) {}
@@ -17,4 +21,15 @@ export class AiController {
async getRecommendations(@Body() request: BidsRequestDto) {
return this.aiService.getRecommendations(request.bids);
}
@Post('save-recommendations')
async saveRecommendations(@Body() request: SaveRecommendationsDto) {
await this.aiService.saveRecommendations(request.recommendations);
return { success: true };
}
@Get('latest-recommendations')
async getLatestRecommendations() {
return this.aiService.getLatestRecommendations();
}
}

View File

@@ -1,10 +1,16 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AiController } from './ai.controller';
import { AiService } from './ai.service';
import { AiRecommendation } from './entities/ai-recommendation.entity';
import { BidItem } from '../bids/entities/bid-item.entity';
@Module({
imports: [ConfigModule],
imports: [
ConfigModule,
TypeOrmModule.forFeature([AiRecommendation, BidItem]),
],
controllers: [AiController],
providers: [AiService],
exports: [AiService],

View File

@@ -1,7 +1,11 @@
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import OpenAI from 'openai';
import { PromptString } from './Prompt';
import { AiRecommendation as AiRecommendationEntity } from './entities/ai-recommendation.entity';
import { BidItem } from '../bids/entities/bid-item.entity';
export interface BidDataDto {
title: string;
@@ -19,7 +23,13 @@ export class AiService {
private readonly logger = new Logger(AiService.name);
private openai: OpenAI;
constructor(private readonly configService: ConfigService) {
constructor(
private readonly configService: ConfigService,
@InjectRepository(AiRecommendationEntity)
private readonly aiRecommendationRepository: Repository<AiRecommendationEntity>,
@InjectRepository(BidItem)
private readonly bidItemRepository: Repository<BidItem>,
) {
const apiKey = this.configService.get<string>('ARK_API_KEY');
this.openai = new OpenAI({
apiKey: apiKey || '',
@@ -80,4 +90,57 @@ ${JSON.stringify(bids.map(b => b.title), null, 2)}`;
throw error;
}
}
async saveRecommendations(recommendations: AIRecommendation[]): Promise<void> {
this.logger.log('开始保存 AI 推荐结果');
try {
// 删除所有旧的推荐
await this.aiRecommendationRepository.clear();
// 保存新的推荐结果(只保存 title 和 confidence
const entities = recommendations.map(rec => {
const entity = new AiRecommendationEntity();
entity.title = rec.title;
entity.confidence = rec.confidence;
return entity;
});
await this.aiRecommendationRepository.save(entities);
this.logger.log(`成功保存 ${entities.length} 条 AI 推荐结果`);
} catch (error) {
this.logger.error('保存 AI 推荐失败:', error);
throw error;
}
}
async getLatestRecommendations(): Promise<AIRecommendation[]> {
this.logger.log('获取最新的 AI 推荐结果');
try {
const entities = await this.aiRecommendationRepository.find({
order: { confidence: 'DESC' }
});
// 从 bid-items 表获取 url 和 source
const result: AIRecommendation[] = [];
for (const entity of entities) {
const bidItem = await this.bidItemRepository.findOne({
where: { title: entity.title }
});
result.push({
title: entity.title,
url: bidItem?.url || '',
source: bidItem?.source || '',
confidence: entity.confidence
});
}
return result;
} catch (error) {
this.logger.error('获取最新 AI 推荐失败:', error);
throw error;
}
}
}

View File

@@ -0,0 +1,16 @@
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn } from 'typeorm';
@Entity('ai_recommendations')
export class AiRecommendation {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
title: string;
@Column({ type: 'int' })
confidence: number;
@CreateDateColumn()
createdAt: Date;
}