feat(cos): 接入 COS 直传预签名 + 词云结果图转存

This commit is contained in:
2026-08-12 19:30:30 +08:00
parent 8925e83b8f
commit 910da1c369
8 changed files with 506 additions and 18 deletions
+19 -5
View File
@@ -3,6 +3,7 @@ import { ConfigService } from '@nestjs/config';
import axios from 'axios';
import ExcelJS from 'exceljs';
import { PrismaService } from '../prisma/prisma.service';
import { CosService } from '../cos/cos.service';
type RemoteStatus = 'queued' | 'running' | 'success' | 'failed';
@@ -25,6 +26,7 @@ export class WordCloudService {
constructor(
private readonly config: ConfigService,
private readonly prisma: PrismaService,
private readonly cos: CosService,
) {}
/** 词云平台是否已配置(WORDCLOUD_API_URL 非空) */
@@ -184,10 +186,22 @@ export class WordCloudService {
};
}
/** 下载 wordcloud 结果 PNG 并返回小程序可用地址(COS 转存前置,P1 实现) */
private async resolveResultImage(_remoteJobId: string): Promise<string | null> {
// TODO(P1 COS)GET /api/jobs/{id}/result → 下载 png → 写入 COS → 返回公网 URL。
// 当前 COS 写入尚未实现,结果图地址留待 P1 接入。
return null;
/** 下载 wordcloud 结果 PNG 转存 COS,返回小程序可访问的 COS 公网 URL;COS 未配置时返回空 */
private async resolveResultImage(remoteJobId: string): Promise<string | null> {
if (!this.cos.configured) {
return null;
}
try {
const res = await axios.get(`${this.apiUrl}/api/jobs/${remoteJobId}/files/png`, {
responseType: 'arraybuffer',
timeout: this.timeoutMs,
});
const key = `uploads/wordcloud/${remoteJobId}.png`;
await this.cos.putObject(key, Buffer.from(res.data), 'image/png');
return this.cos.publicUrl(key);
} catch (e) {
this.logger.warn(`转存词云结果失败: ${(e as Error).message}`);
return null;
}
}
}