chore: 更新.gitignore并添加新文件

在.gitignore中添加对*.png、*.log、*-lock.json、*.woff2文件的忽略规则,并新增OFL.txt文件。同时,添加vue.svg图标文件以支持前端展示。更新多个TypeScript文件以优化代码格式和增强可读性。
This commit is contained in:
dmy
2026-01-14 22:26:32 +08:00
parent 10565af001
commit 82f5a81887
47 changed files with 1513 additions and 814 deletions

View File

@@ -1,9 +1,36 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, LessThan, MoreThanOrEqual } from 'typeorm';
import { Repository, LessThan } from 'typeorm';
import { BidItem } from '../entities/bid-item.entity';
import { CrawlInfoAdd } from '../../crawler/entities/crawl-info-add.entity';
interface FindAllQuery {
page?: number;
limit?: number;
source?: string;
keyword?: string;
}
interface SourceResult {
source: string;
}
interface CrawlInfoAddStats {
source: string;
count: number;
latestUpdate: Date | string;
latestPublishDate: Date | string | null;
error: string | null;
}
interface CrawlInfoAddRawResult {
source: string;
count: number;
latestPublishDate: Date | string | null;
error: string | null;
latestUpdate: Date | string;
}
@Injectable()
export class BidsService {
constructor(
@@ -13,7 +40,7 @@ export class BidsService {
private crawlInfoRepository: Repository<CrawlInfoAdd>,
) {}
async findAll(query?: any) {
async findAll(query?: FindAllQuery) {
const { page = 1, limit = 10, source, keyword } = query || {};
const qb = this.bidRepository.createQueryBuilder('bid');
@@ -26,8 +53,8 @@ export class BidsService {
}
qb.orderBy('bid.publishDate', 'DESC')
.skip((page - 1) * limit)
.take(limit);
.skip((Number(page) - 1) * Number(limit))
.take(Number(limit));
const [items, total] = await qb.getManyAndCount();
return { items, total };
@@ -35,7 +62,9 @@ export class BidsService {
async createOrUpdate(data: Partial<BidItem>) {
// Use title or a hash of title to check for duplicates
let item = await this.bidRepository.findOne({ where: { title: data.title } });
const item = await this.bidRepository.findOne({
where: { title: data.title },
});
if (item) {
Object.assign(item, data);
return this.bidRepository.save(item);
@@ -51,21 +80,21 @@ export class BidsService {
});
}
async getSources() {
async getSources(): Promise<string[]> {
const result = await this.bidRepository
.createQueryBuilder('bid')
.select('DISTINCT bid.source')
.select('DISTINCT bid.source', 'source')
.where('bid.source IS NOT NULL')
.orderBy('bid.source', 'ASC')
.getRawMany();
return result.map((item: any) => item.source);
.getRawMany<SourceResult>();
return result.map((item) => item.source);
}
async getRecentBids() {
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
thirtyDaysAgo.setHours(0, 0, 0, 0);
return this.bidRepository
.createQueryBuilder('bid')
.where('bid.publishDate >= :thirtyDaysAgo', { thirtyDaysAgo })
@@ -81,7 +110,11 @@ export class BidsService {
.getMany();
}
async getBidsByDateRange(startDate?: string, endDate?: string, keywords?: string[]) {
async getBidsByDateRange(
startDate?: string,
endDate?: string,
keywords?: string[],
) {
const qb = this.bidRepository.createQueryBuilder('bid');
if (startDate) {
@@ -97,13 +130,18 @@ export class BidsService {
}
if (keywords && keywords.length > 0) {
const keywordConditions = keywords.map((keyword, index) => {
return `bid.title LIKE :keyword${index}`;
}).join(' OR ');
qb.andWhere(`(${keywordConditions})`, keywords.reduce((params, keyword, index) => {
params[`keyword${index}`] = `%${keyword}%`;
return params;
}, {}));
const keywordConditions = keywords
.map((keyword, index) => {
return `bid.title LIKE :keyword${index}`;
})
.join(' OR ');
qb.andWhere(
`(${keywordConditions})`,
keywords.reduce((params, keyword, index) => {
params[`keyword${index}`] = `%${keyword}%`;
return params;
}, {}),
);
}
return qb.orderBy('bid.publishDate', 'DESC').getMany();
@@ -118,7 +156,7 @@ export class BidsService {
return this.bidRepository.save(item);
}
async getCrawlInfoAddStats() {
async getCrawlInfoAddStats(): Promise<CrawlInfoAddStats[]> {
// 获取每个来源的最新一次爬虫记录(按 createdAt 降序)
const query = `
SELECT
@@ -136,15 +174,19 @@ export class BidsService {
ORDER BY source ASC
`;
const results = await this.crawlInfoRepository.query(query);
const results =
await this.crawlInfoRepository.query<CrawlInfoAddRawResult[]>(query);
return results.map((item: any) => ({
source: item.source,
count: item.count,
return results.map((item) => ({
source: String(item.source),
count: Number(item.count),
latestUpdate: item.latestUpdate,
latestPublishDate: item.latestPublishDate,
// 确保 error 字段正确处理null 或空字符串都转换为 null非空字符串保留
error: item.error && item.error.trim() !== '' ? item.error : null,
error:
item.error && String(item.error).trim() !== ''
? String(item.error)
: null,
}));
}
}