feat: 添加关键词词云数据访问层函数
This commit is contained in:
@@ -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,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user