feat: 添加关键词词云数据访问层函数

This commit is contained in:
2026-01-27 19:34:33 +08:00
parent 76a1646cb7
commit 7c9f7ad018
+191
View File
@@ -0,0 +1,191 @@
import { prisma } from '@/lib/prisma';
import type { Quarter, Keyword, VisualStyleRule } from '@prisma/client';
// 类型定义
export type KeywordWithVisual = Keyword & {
visualConfig: {
color: string;
size: string;
border: string;
rotation?: string;
};
};
export type QuarterWithKeywords = Quarter & {
keywords: KeywordWithVisual[];
_count?: { keywords: number };
};
export type QuarterWithCount = Quarter & {
_count?: { keywords: number };
};
/**
* 获取所有季度列表
*/
export async function getAllQuarters(
options?: { isActive?: boolean }
): Promise<Quarter[]> {
const where = options?.isActive !== undefined
? { isActive: options.isActive }
: {};
return prisma.quarter.findMany({
where,
orderBy: { displayOrder: 'asc' },
});
}
/**
* 获取单个季度的详情(包含关键词计数)
*/
export async function getQuarterByQuarter(
quarter: string
): Promise<QuarterWithCount | null> {
const quarterData = await prisma.quarter.findUnique({
where: { quarter },
include: {
_count: {
select: { keywords: true },
},
},
});
return quarterData as QuarterWithCount | null;
}
/**
* 获取指定季度的所有关键词
*/
export async function getKeywordsByQuarter(
quarter: string
): Promise<QuarterWithKeywords | null> {
const quarterData = await prisma.quarter.findUnique({
where: { quarter },
include: {
keywords: {
orderBy: { trendScore: 'desc' },
},
},
});
if (!quarterData) {
return null;
}
// 转换 visualConfig 从 JSON 到对象
const keywords: KeywordWithVisual[] = quarterData.keywords.map(kw => ({
...kw,
visualConfig: (typeof kw.visualConfig === 'string'
? JSON.parse(kw.visualConfig)
: kw.visualConfig) as KeywordWithVisual['visualConfig'],
}));
return {
...quarterData,
keywords,
} as QuarterWithKeywords;
}
/**
* 获取所有启用的视觉规则
*/
export async function getVisualStyleRules(
options?: { enabled?: boolean }
): Promise<VisualStyleRule[]> {
const where = options?.enabled !== undefined
? { enabled: options.enabled }
: {};
return prisma.visualStyleRule.findMany({
where,
orderBy: [
{ priority: 'asc' },
{ minScore: 'desc' },
],
});
}
/**
* 创建或更新季度
*/
export async function upsertQuarter(
quarter: string,
data: {
title: string;
titleEn?: string;
subtitle?: string;
subtitleEn?: string;
displayOrder?: number;
}
): Promise<Quarter> {
return prisma.quarter.upsert({
where: { quarter },
update: data,
create: {
quarter,
...data,
},
});
}
/**
* 批量创建关键词
*/
export async function createKeywords(
quarterId: number,
keywords: Array<{
word: string;
trendScore: number;
description: string;
descriptionEn?: string;
detailPoints: string[];
detailPointsEn?: string[];
visualConfig: Record<string, any>;
}>
): Promise<{ created: number; failed: number; errors: Array<{ word: string; error: string }> }> {
const errors: Array<{ word: string; error: string }> = [];
let created = 0;
for (const kw of keywords) {
try {
await prisma.keyword.create({
data: {
quarterId,
word: kw.word,
trendScore: kw.trendScore,
description: kw.description,
descriptionEn: kw.descriptionEn,
detailPoints: kw.detailPoints as any, // Prisma Json 类型
detailPointsEn: kw.detailPointsEn as any,
visualConfig: kw.visualConfig as any,
},
});
created++;
} catch (error) {
errors.push({
word: kw.word,
error: error instanceof Error ? error.message : 'Unknown error',
});
}
}
return { created, failed: errors.length, errors };
}
/**
* 记录错误日志
*/
export async function logKeywordCloudError(
data: {
quarter: string;
keyword?: string;
errorType: string;
errorMessage: string;
rawData?: any;
}
): Promise<void> {
await prisma.keywordCloudErrorLog.create({
data,
});
}