From 5024d2c5025bc55eaca91c1c9fd41f20f15c2042 Mon Sep 17 00:00:00 2001 From: dmy Date: Tue, 13 Jan 2026 00:39:43 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=E6=8A=95=E6=A0=87=E9=A1=B9=E7=9B=AE=E6=B8=85=E7=90=86=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=E5=B9=B6=E6=9B=B4=E6=96=B0=E7=9B=B8=E5=85=B3=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更新依赖版本,移除调试日志,修改静态文件排除规则,将投标项目查重逻辑从URL改为标题,并添加清理重复投标项目的脚本 --- package.json | 2 +- src/ai/ai.service.ts | 2 +- src/app.module.ts | 2 +- src/bids/services/bid.service.ts | 4 +- src/scripts/remove-duplicates.ts | 77 ++++++++++++++++++++++++++++++++ 5 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 src/scripts/remove-duplicates.ts diff --git a/package.json b/package.json index e761fe2..380cd62 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "devDependencies": { "@eslint/eslintrc": "^3.2.0", "@eslint/js": "^9.18.0", - "@nestjs/cli": "^11.0.0", + "@nestjs/cli": "^11.0.14", "@nestjs/schematics": "^11.0.0", "@nestjs/testing": "^11.0.1", "@types/express": "^5.0.0", diff --git a/src/ai/ai.service.ts b/src/ai/ai.service.ts index 5b670ee..18ba0a4 100644 --- a/src/ai/ai.service.ts +++ b/src/ai/ai.service.ts @@ -59,7 +59,7 @@ export class AiService { 投标项目标题列表: ${JSON.stringify(bids.map(b => b.title), null, 2)}`; - this.logger.log('发给AI的内容',prompt); + // this.logger.log('发给AI的内容',prompt); const completion = await this.openai.chat.completions.create({ model: 'mimo-v2-flash-free', // max_tokens: 32768, diff --git a/src/app.module.ts b/src/app.module.ts index d2ae4d6..e22e81b 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -17,7 +17,7 @@ import { AiModule } from './ai/ai.module'; ScheduleModule.forRoot(), ServeStaticModule.forRoot({ rootPath: join(__dirname, '..', 'frontend', 'dist'), - exclude: ['/api/:path(*)'], + exclude: ['/api'], }), LoggerModule, DatabaseModule, diff --git a/src/bids/services/bid.service.ts b/src/bids/services/bid.service.ts index 1a81ae5..0967098 100644 --- a/src/bids/services/bid.service.ts +++ b/src/bids/services/bid.service.ts @@ -49,8 +49,8 @@ export class BidsService { } async createOrUpdate(data: Partial) { - // Use URL or a hash of URL to check for duplicates - let item = await this.bidRepository.findOne({ where: { url: data.url } }); + // Use title or a hash of title to check for duplicates + let item = await this.bidRepository.findOne({ where: { title: data.title } }); if (item) { Object.assign(item, data); return this.bidRepository.save(item); diff --git a/src/scripts/remove-duplicates.ts b/src/scripts/remove-duplicates.ts new file mode 100644 index 0000000..0a9cec0 --- /dev/null +++ b/src/scripts/remove-duplicates.ts @@ -0,0 +1,77 @@ +import { NestFactory } from '@nestjs/core'; +import { AppModule } from '../app.module'; +import { getRepositoryToken } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { BidItem } from '../bids/entities/bid-item.entity'; +import { CustomLogger } from '../common/logger/logger.service'; + +async function removeDuplicates() { + const app = await NestFactory.createApplicationContext(AppModule); + + // 设置自定义 logger + const logger = await app.resolve(CustomLogger); + app.useLogger(logger); + logger.setContext('RemoveDuplicatesScript'); + + try { + // 获取 BidItem 的 repository + const bidItemRepository = app.get>(getRepositoryToken(BidItem)); + + logger.log('开始查找重复的title...'); + + // 查找所有重复的title + const duplicates = await bidItemRepository + .createQueryBuilder('bid') + .select('bid.title', 'title') + .addSelect('COUNT(*)', 'count') + .groupBy('bid.title') + .having('COUNT(*) > 1') + .getRawMany(); + + if (duplicates.length === 0) { + logger.log('没有发现重复的title'); + await app.close(); + process.exit(0); + } + + logger.log(`发现 ${duplicates.length} 个重复的title`); + + let totalDeleted = 0; + + // 对每个重复的title,只保留最晚创建的一条记录 + for (const duplicate of duplicates) { + const title = duplicate.title; + const count = duplicate.count; + + logger.log(`处理重复title: "${title}" (共 ${count} 条)`); + + // 获取该title的所有记录,按创建时间降序排列 + const items = await bidItemRepository + .createQueryBuilder('bid') + .where('bid.title = :title', { title }) + .orderBy('bid.createdAt', 'DESC') + .getMany(); + + // 保留第一条(最晚创建的),删除其余的 + const itemsToDelete = items.slice(1); + + if (itemsToDelete.length > 0) { + const idsToDelete = itemsToDelete.map(item => item.id); + const deleteResult = await bidItemRepository.delete(idsToDelete); + totalDeleted += deleteResult.affected || 0; + logger.log(` 删除了 ${deleteResult.affected} 条重复记录,保留ID: ${items[0].id} (最晚创建)`); + } + } + + logger.log(`总共删除了 ${totalDeleted} 条重复记录`); + + await app.close(); + process.exit(0); + } catch (error) { + logger.error('删除重复记录失败:', error); + await app.close(); + process.exit(1); + } +} + +removeDuplicates();