|
|
@@ -8,9 +8,12 @@ import type {
|
|
|
PlatformType,
|
|
|
} from '@media-manager/shared';
|
|
|
import { wsManager } from '../websocket/index.js';
|
|
|
-import { headlessBrowserService, type CookieData } from './HeadlessBrowserService.js';
|
|
|
+import { headlessBrowserService } from './HeadlessBrowserService.js';
|
|
|
+import type { CookieData } from '../utils/cookieParser.js';
|
|
|
+import { parseCookieString } from '../utils/cookieParser.js';
|
|
|
import { CookieManager } from '../automation/cookie.js';
|
|
|
import { logger } from '../utils/logger.js';
|
|
|
+import { IsNull } from 'typeorm';
|
|
|
|
|
|
interface GetCommentsParams {
|
|
|
page: number;
|
|
|
@@ -254,7 +257,7 @@ export class CommentService {
|
|
|
cookies = JSON.parse(decryptedCookies);
|
|
|
} catch {
|
|
|
// 如果 JSON 解析失败,尝试解析 "name=value; name2=value2" 格式
|
|
|
- cookies = this.parseCookieString(decryptedCookies, account.platform);
|
|
|
+ cookies = parseCookieString(decryptedCookies, account.platform);
|
|
|
if (cookies.length === 0) {
|
|
|
logger.error(`Invalid cookie format for account ${account.id}`);
|
|
|
continue;
|
|
|
@@ -430,6 +433,15 @@ export class CommentService {
|
|
|
|
|
|
logger.info(`Final work mapping: videoId="${commentVideoId}", title="${commentVideoTitle}", workId=${workId}`);
|
|
|
|
|
|
+ // Batch prepare existing comments for this work to avoid N+1 queries
|
|
|
+ const batchPairs = workComment.comments.map(c => ({ accountId: account.id, authorName: c.authorName, content: c.content }));
|
|
|
+ const existingList = await this.commentRepository.find({ where: batchPairs as any[] });
|
|
|
+ const existingMap = new Map<string, any>();
|
|
|
+ for (const ex of existingList) {
|
|
|
+ const key = `${ex.authorName}|||${ex.content}`;
|
|
|
+ existingMap.set(key, ex);
|
|
|
+ }
|
|
|
+
|
|
|
for (const comment of workComment.comments) {
|
|
|
try {
|
|
|
// 过滤无效评论内容 - 放宽限制,只过滤纯操作按钮文本
|
|
|
@@ -438,15 +450,8 @@ export class CommentService {
|
|
|
logger.debug(`Skipping invalid comment content: ${comment.content}`);
|
|
|
continue;
|
|
|
}
|
|
|
-
|
|
|
- // 检查评论是否已存在(基于内容+作者+账号的去重)
|
|
|
- const existing = await this.commentRepository
|
|
|
- .createQueryBuilder('comment')
|
|
|
- .where('comment.accountId = :accountId', { accountId: account.id })
|
|
|
- .andWhere('comment.authorName = :authorName', { authorName: comment.authorName })
|
|
|
- .andWhere('comment.content = :content', { content: comment.content })
|
|
|
- .getOne();
|
|
|
-
|
|
|
+ const key = `${comment.authorName}|||${comment.content}`;
|
|
|
+ const existing = existingMap.get(key);
|
|
|
if (!existing) {
|
|
|
const newComment = this.commentRepository.create({
|
|
|
userId,
|
|
|
@@ -518,7 +523,7 @@ export class CommentService {
|
|
|
|
|
|
// 获取所有没有 workId 的评论
|
|
|
const orphanedComments = await this.commentRepository.find({
|
|
|
- where: { userId, workId: undefined as unknown as number },
|
|
|
+ where: { userId, workId: IsNull() },
|
|
|
});
|
|
|
|
|
|
if (orphanedComments.length === 0) return;
|
|
|
@@ -610,51 +615,7 @@ export class CommentService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 将 cookie 字符串解析为 cookie 列表
|
|
|
- */
|
|
|
- private parseCookieString(cookieString: string, platform: string): CookieData[] {
|
|
|
- // 获取平台对应的域名
|
|
|
- const domainMap: Record<string, string> = {
|
|
|
- douyin: '.douyin.com',
|
|
|
- kuaishou: '.kuaishou.com',
|
|
|
- xiaohongshu: '.xiaohongshu.com',
|
|
|
- weixin_video: '.qq.com',
|
|
|
- bilibili: '.bilibili.com',
|
|
|
- toutiao: '.toutiao.com',
|
|
|
- baijiahao: '.baidu.com',
|
|
|
- qie: '.qq.com',
|
|
|
- dayuhao: '.alibaba.com',
|
|
|
- };
|
|
|
-
|
|
|
- const domain = domainMap[platform] || `.${platform}.com`;
|
|
|
-
|
|
|
- // 解析 "name=value; name2=value2" 格式的 cookie 字符串
|
|
|
- const cookies: CookieData[] = [];
|
|
|
-
|
|
|
- const pairs = cookieString.split(';');
|
|
|
- for (const pair of pairs) {
|
|
|
- const trimmed = pair.trim();
|
|
|
- if (!trimmed) continue;
|
|
|
-
|
|
|
- const eqIndex = trimmed.indexOf('=');
|
|
|
- if (eqIndex === -1) continue;
|
|
|
-
|
|
|
- const name = trimmed.substring(0, eqIndex).trim();
|
|
|
- const value = trimmed.substring(eqIndex + 1).trim();
|
|
|
-
|
|
|
- if (name && value) {
|
|
|
- cookies.push({
|
|
|
- name,
|
|
|
- value,
|
|
|
- domain,
|
|
|
- path: '/',
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return cookies;
|
|
|
- }
|
|
|
+
|
|
|
|
|
|
private formatComment(comment: Comment): CommentType {
|
|
|
return {
|