refactor: 移除 AI 词云、AI 时间轴和博客模块

暂停开发以下功能,将设计文档移至未完成计划文件夹:

- AI 词云 (Keyword Cloud): 前端、API、数据库模型、n8n 工作流
- AI 时间轴 (AI Timeline): 前端、API、数据库模型
- 博客 (Blog): 导航链接占位

变更内容:
- 删除词云和时间轴的前端页面及组件
- 删除 /api/keyword-cloud/* 和 /api/events/* API 端点
- 从 prisma/schema.prisma 移除 Keyword, Quarter, VisualStyleRule, KeywordCloudErrorLog, AIEvent 模型
- 从 validations.ts 移除相关 Zod Schema
- 从国际化消息中移除 keywordCloud/timeline 命名空间
- 从导航菜单移除词云、时间轴、博客链接
- 更新 CLAUDE.md 移除词云系统文档

设计文档已移至 .omc/plans/postponed-features/ 供后续恢复开发参考
This commit is contained in:
2026-02-21 12:35:54 +08:00
parent c2eabfea97
commit 79c25eb1b3
42 changed files with 708 additions and 3124 deletions
+23 -115
View File
@@ -145,121 +145,6 @@ export type UpdateDiscoveryTask = z.infer<typeof UpdateDiscoveryTaskSchema>;
export type GetDiscoveryTasksQuery = z.infer<typeof GetDiscoveryTasksQuerySchema>;
export type BatchResetTasks = z.infer<typeof BatchResetTasksSchema>;
// ================================
// Keyword Cloud Schemas
// ================================
// 视觉配置 Schema
const VisualConfigSchema = z.object({
color: z.enum(["primary", "secondary", "accent", "gray"]),
size: z.enum(["text-5xl", "text-4xl", "text-3xl", "text-2xl", "text-xl", "text-lg", "text-base"]),
border: z.enum(["border-4", "border-2"]),
rotation: z
.string()
.regex(/^-?rotate-\d+$/)
.nullable()
.optional(),
});
// 关键词输入 Schema
export const KeywordInputSchema = z.object({
word: z.string().min(1).max(100),
trendScore: z.number().int().min(0).max(100),
description: z.string().min(10).max(500),
descriptionEn: z.string().max(500).optional(),
detailPoints: z.array(z.string().min(5).max(100)).min(1).max(5),
detailPointsEn: z.array(z.string().max(100)).max(5).optional(),
visualConfig: VisualConfigSchema,
});
// 批量写入关键词请求 Schema
export const BatchKeywordsRequestSchema = z.object({
quarter: z.string().regex(/^\d{4}-Q[1-4]$/, "格式应为 YYYY-QN"),
keywords: z.array(KeywordInputSchema).min(1).max(50),
});
// 季度 Schema
export const QuarterSchema = z.object({
quarter: z.string().regex(/^\d{4}-Q[1-4]$/),
title: z.string().min(1).max(200),
titleEn: z.string().max(200).optional(),
subtitle: z.string().max(500).optional(),
subtitleEn: z.string().max(500).optional(),
displayOrder: z.number().int().min(0).default(0),
isActive: z.boolean().default(true),
});
// 视觉规则 Schema
export const VisualStyleRuleSchema = z
.object({
name: z.string().min(1).max(100),
minScore: z.number().int().min(0).max(100),
maxScore: z.number().int().min(0).max(100),
color: z.enum(["primary", "secondary", "accent", "gray"]),
size: z.enum([
"text-5xl",
"text-4xl",
"text-3xl",
"text-2xl",
"text-xl",
"text-lg",
"text-base",
]),
border: z.enum(["border-4", "border-2"]),
rotation: z
.string()
.regex(/^-?rotate-\d+$/)
.nullable()
.optional(),
priority: z.number().int().min(0).default(0),
enabled: z.boolean().default(true),
})
.refine((data) => data.minScore < data.maxScore, {
message: "minScore 必须小于 maxScore",
});
// API 响应 Schema
export const KeywordCloudResponseSchema = z.object({
success: z.boolean(),
data: z.any().optional(),
error: z.string().optional(),
});
// ================================
// AI Timeline Schemas
// ================================
export const AIEventInputSchema = z.object({
title: z.string().min(1).max(200),
titleEn: z.string().max(200).optional(),
eventDate: z.string().datetime(),
description: z.string().min(10).max(500),
descriptionEn: z.string().max(500).optional(),
imageUrl: z.string().url(),
sourceUrl: z.string().url().optional(),
});
export const AIEventQuerySchema = z.object({
year: z
.string()
.regex(/^\d{4}$/)
.optional(),
limit: z.string().regex(/^\d+$/).transform(Number).optional(),
offset: z.string().regex(/^\d+$/).transform(Number).optional(),
});
// ================================
// Types
// ================================
export type KeywordInput = z.infer<typeof KeywordInputSchema>;
export type BatchKeywordsRequest = z.infer<typeof BatchKeywordsRequestSchema>;
export type Quarter = z.infer<typeof QuarterSchema>;
export type VisualStyleRule = z.infer<typeof VisualStyleRuleSchema>;
export type AIEventInput = z.infer<typeof AIEventInputSchema>;
export type AIEventQuery = z.infer<typeof AIEventQuerySchema>;
export type KeywordCloudResponse = z.infer<typeof KeywordCloudResponseSchema>;
// ================================
// Tags API Schemas
// ================================
@@ -344,6 +229,26 @@ export const TagMaintenanceRequestSchema = z.object({
});
});
export const TagMatchCandidateSchema = z.object({
name: z.string().min(1, "Tag name is required").max(100),
nameEn: z.string().max(100).optional(),
});
export const TagMatchAvailableTagSchema = z.object({
id: z.string().min(1),
name: z.string().min(1).max(100),
nameEn: z.string().max(100).nullable().optional(),
slug: z.string().max(150).optional(),
projectCount: z.number().int().nonnegative().optional(),
});
export const TagMatchRequestSchema = z.object({
apiKey: z.string().min(32, "Invalid API key format"),
candidates: z.array(TagMatchCandidateSchema).min(1).max(30),
availableTags: z.array(TagMatchAvailableTagSchema).max(1000).optional(),
limit: z.number().int().min(1).max(10).default(5),
});
// ================================
// Types
// ================================
@@ -352,3 +257,6 @@ export type TagUpdate = z.infer<typeof TagUpdateSchema>;
export type MergeTarget = z.infer<typeof MergeTargetSchema>;
export type TagMerge = z.infer<typeof TagMergeSchema>;
export type TagMaintenanceRequest = z.infer<typeof TagMaintenanceRequestSchema>;
export type TagMatchCandidate = z.infer<typeof TagMatchCandidateSchema>;
export type TagMatchAvailableTag = z.infer<typeof TagMatchAvailableTagSchema>;
export type TagMatchRequest = z.infer<typeof TagMatchRequestSchema>;