diff --git a/.dockerignore b/.dockerignore index 4c44416..3e75dce 100644 --- a/.dockerignore +++ b/.dockerignore @@ -61,7 +61,7 @@ scripts/migrate-*.mjs scripts/cleanup-non-ai.mjs scripts/delete-*.mjs scripts/backfill-*.mjs -scripts/reconcile-*.mjs +# 注: reconcile-like-counts.mjs 被 crontab 引用, 必须打包 scripts/organize-*.mjs scripts/sync-*.mjs scripts/publish-*.mjs @@ -69,7 +69,7 @@ scripts/generate-*.mjs scripts/add-*.mjs scripts/run-*.mjs scripts/version-manager.mjs -scripts/health-check.mjs +# 注: health-check.mjs 被 crontab 引用, 必须打包 scripts/db-status.mjs scripts/_check-server.sh diff --git a/.env b/.env index d35c8db..e18ebca 100644 --- a/.env +++ b/.env @@ -12,4 +12,4 @@ NEXTAUTH_SECRET="zhuiguang-ai-dev-secret" GITHUB_TOKEN="" -DEEPSEEK_API_KEY="sk-91d1671855e44089b792b8147e17963f" \ No newline at end of file +DEEPSEEK_API_KEY="sk-74c0b4f4eb5647398e35ecd064d1829a" \ No newline at end of file diff --git a/.env.example b/.env.example index ad8f477..b6f586b 100644 --- a/.env.example +++ b/.env.example @@ -18,6 +18,18 @@ GITHUB_TOKEN="" # DeepSeek AI API Key (必填,用于技能评测和新闻摘要) DEEPSEEK_API_KEY="" +# DeepSeek 模型名 (可选,默认 deepseek-v4-pro;旧名 deepseek-chat/reasoner 已停用) +# DEEPSEEK_MODEL="deepseek-v4-pro" + +# Redis 缓存连接 (必填,Docker 共享 redis-cache 容器) +# 如不设,默认为本地 127.0.0.1:6379 无密码 +# REDIS_HOST="127.0.0.1" +# REDIS_PORT="6379" +# ⚠️ 密码含 # 等特殊字符时必须加引号(否则 dotenv/Next 会把 # 之后当注释截断,导致 WRONGPASS) +# REDIS_PASSWORD="" +# REDIS_CACHE_DB="0" # API 缓存层 (src/lib/cache.ts) +# REDIS_SCRIPT_DB="1" # 脚本缓存层 (scripts/lib/redis-cache.mjs) + # Bot 反馈闭环共享密钥 (可选,设置后 /api/forum/topics/[id]/follow-up 可被脚本调用) # 不设置则只能用 admin session 调用 BOT_FEEDBACK_SECRET="" \ No newline at end of file diff --git a/.gitignore b/.gitignore index 5c2ae15..4967136 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,13 @@ yarn-error.log* next-env.d.ts /src/generated/prisma + +# backups & temp +*.tar.gz +tmp-* + +# generated bot avatar JPGs (SVGs are in repo) +public/bot-avatars/lunaris/ + +# .trae local knowledge cache (not committed) +.trae/knowledge/ diff --git a/.trae/CHANGELOG b/.trae/CHANGELOG index 4f0cb2e..a486e85 100644 --- a/.trae/CHANGELOG +++ b/.trae/CHANGELOG @@ -1,5 +1,438 @@ # 追光AI 变更日志 +## 2026-06-22/23 项目开发规则全面优化(三轮迭代) + +### 目标 +基于 Trae IDE 开发规则最佳实践,审查并全面优化 `.trae/rules/project_rules.md`,使之覆盖项目开发全生命周期。 + +--- + +### 第一轮:基础增强(8章 → 12章) +- **新增"零、代码质量基础"**: TypeScript类型安全(禁止any/as、泛型约束)、构建检查铁律(tsc+vitetest)、Git工作流、文件组织规范表 +- **新增"九、测试规范"**: Vitest框架、80%覆盖率目标、三类测试、禁止skip +- **增强图片优化**: 新增LCP priority规则、LazyImage组件引用、sizes属性 +- **增强无障碍**: 从2条→4条(语义化HTML、ARIA/键盘交互、焦点对比度) +- **增强SEO**: 新增Canonical URL/Robots策略 +- **增强性能**: 从2条→5条(字体优化、Suspense、Bundle体积控制) +- **增强安全**: 从2条→6条(具体CSP值、XSS消毒方案、Zod验证、认证授权、敏感信息) +- **增强定时任务**: 新增crontab格式示例、错误处理/退出码、任务幂等性 +- **新增数据获取**: 缓存失效策略(stale-while-revalidate)、并行查询(Promise.all) +- **新增导航**: 中间件路由保护、prefetch控制 +- **全局改进**: 所有规则添加🔴🟡🟢严重级别、18处file://可点击链接 +- **新增"十"→"十二"**: 设计系统速查、API约定速查、踩坑记录独立章节 + +### 第二轮:运维增强(12章 → 15章) +- **新增"十三、Shell脚本规范"**: 脚本结构模板、set -e错误处理、PASS/FAIL/WARN结构化输出、磁盘预检、禁止操作清单 +- **新增"十四、Docker容器规范"**: 容器清单(app+cron)、host网络、容器管理命令、健康检查配置 +- **新增"十五、部署规范"**: 部署前5项自检、构建流程、部署后六项验证清单、镜像回滚策略 +- **新增8.6 Cron Entrypoint规范**: 信号处理(trap+30s优雅关闭)、启动前.env/crontab.txt校验、健康检查端点(8311)、环境变量注入 +- **新增8.7 日志规范**: Node.js统一logger.mjs、Shell结构化输出、三层日志目录、禁止写入其他项目日志 +- **修复8.3**: 备份.env路径修正为 `$PROJECT/env/.env.production` + +### 第三轮:深度补充(15章 → 22章) +- **新增"十六、数据模型与迁移规范"**: Prisma命名约定、连接单例、Schema变更四步流程、严禁DROP/TRUNCATE、数据库隔离 +- **新增"十七、API设计规范"**: 路由标准骨架、错误处理表、响应格式、状态码约定、Cache-Control策略、认证三层分层、分页上限 +- **新增"十八、AI/Bot系统规范"**: Bot角色定义(112角色)、12张Bot表、活动脚本5大约定、AI API调用隔离、提示词管理 +- **新增"十九、环境变量管理规范"**: 8个关键变量分级、.env.example同步规则、脚本侧加载顺序 +- **新增"二十、依赖管理规范"**: 添加/移除/版本管理 +- **新增"二十一、代码审查清单"**: 12项提交前自检 + Knowledge Graph同步规则 + +### 文档同步 +- **新增模块文档**: `.trae/specs/modules/17-rules.md` — 开发规则模块完整索引 +- **modules.md 更新**: 模块表新增第17项、项目结构/关键特性同步更新 +- **4个关联模块追加规则引用**: 01-database.md / 02-api.md / 07-lib-and-scripts.md / 09-devops.md +- **数量同步**: crontab 调度表从13条→25条(三处更新) + +### 变更统计 +| 类型 | 数量 | +|------|------| +| 规则文件变更 | 3轮迭代(222行 → 609行 → ~1100行) | +| 新增章节 | 14个(覆盖22章) | +| 新增file://链接 | 24个 | +| 新增模块文档 | 1个(17-rules.md) | +| 修改已有模块 | 5个(modules.md + 4个关联模块) | +| 规则同步文档数 | 7个 | + +### 关键指标 +- 规则覆盖从 8 个关键领域 → 22 章全生命周期 +- 🔴 必须遵守规则 ~50 条,🟡 强烈推荐 ~30 条 +- 全部 file:// 链接指向真实代码文件(24/24 通过验证) +- 4个关联模块文档完整追加规则引用 + +--- + + + +### 性能优化 +- **31 个 `` 替换为 ``**: 跨 24 个文件,全面使用 lazy loading + skeleton + error fallback +- **LazyImage 增强**: 新增 `referrerPolicy` 属性支持,用于跨域图片场景 +- **动态导入**: layout.tsx 中 BackToTop 和 ReadingProgress 使用 `dynamic(() => import(...), { ssr: false })` +- **N+1 查询修复**: tools/[slug] 页面合并 4 个串行 await 为 `Promise.all` 批量并行查询 +- **ISR 统一**: community/page.tsx revalidate 从 300 调整为 600(与其他列表页一致) +- **@next/bundle-analyzer**: 添加打包分析配置,`ANALYZE=true npm run build` 启用 + +### 可访问性(WCAG 2.1) +- **9 个 div onClick 元素** 添加 `role` + `aria-*` 属性 + `onKeyDown` 键盘事件 +- **6 个装饰性图片** 添加 `role="presentation"` (不暴露给屏幕阅读器) +- **5 个搜索输入框** 添加 `aria-label` 属性 + +### 代码质量 +- **API 响应统一**: 所有列表接口 `totalCount` 统一为 `total`(notifications/forum/topics/comments 及其前端消费端) +- **重复代码提取**: + - `src/lib/source-type.ts` — sourceTypeLabel + sourceTypeColor(从多个文件提取) + - `src/lib/utils.ts` — formatRelativeTime(从工具/技能详情页提取) + - `src/lib/constants.ts` — tagColors(从多个组件提取) +- **router.push → Link**: 10 处面包屑和导航链接替换 +- **any → 严格类型**: + - `src/lib/cache.ts` — `cachedResponse` 泛型 + - `src/lib/achievements.ts` — 精确类型约束 + - `src/lib/auth.ts` — 回调函数明确类型 + - `src/app/api/recommendations/route.ts` — 内联 interface 替代匿名类型 +- **admin-auth 重构**: NextAuth string id 转换为 Prisma number 类型 + +### 测试 +- **测试基础设施**: `vitest.config.ts` + `vitest.setup.ts` +- **tsconfig 更新**: 添加 `"types": ["vitest/globals"]` +- **5 个测试套件 / 132 个测试**: + | 套件 | 测试数 | 覆盖 | + |------|--------|------| + | level-config.test.ts | 28 | 等级配置 | + | elo.test.ts | 24 | ELO 评分 | + | forum-hot.test.ts | 30 | 论坛热度 | + | achievements.test.ts | 26 | 成就系统 | + | points-reward.test.ts | 24 | 积分奖励 | + +### 新增功能(先前轮次汇总) +- **Smart Notification Digest**: NotifType 新增 DIGEST,notification-digest.mjs cron,通知偏好 API,通知中心页面 +- **Dark Mode**: ThemeProvider,globals.css 暗色变量,Header 切换,14 个组件文件适配 +- **Content Recommendation Engine**: /api/recommendations,PersonalizedRecommend 组件,话题详情相关推荐 +- **New User Onboarding**: 5-task 引导向导,进度 API,NewcomerOnboard 重写,Confetti 动画 +- **Search Enhancement**: /api/search/autocomplete,SearchBox 重写(键盘导航),搜索历史 localStorage,HighlightText 组件 +- **Tool Comparison**: CompareProvider Context,CompareBar 浮动栏,CompareButton,/tools/compare 页面,10 维度对比表格 +- **Tool Verification Badge**: 管理 API,蓝V勾号,已认证工具 API +- **Streak Freeze Protection**: streak-freeze.ts,Freeze API,CheckInCard 增强,季度日历,grant-freeze-cards.mjs +- **Points Shop**: shop-items.ts,商店页面,购买 API,已购物品 API,个人中心装饰效果 + +### 新增文件统计 +| 类型 | 数量 | +|------|------| +| 新增 lib 文件 | 4 个(source-type.ts / constants.ts / streak-freeze.ts / shop-items.ts) | +| 新增组件 | 8 个(LazyImage / ThemeProvider / CompareProvider / CompareBar / CompareButton / PersonalizedRecommend / HighlightText / NotificationCenter) | +| 新增 API 路由 | 8 个(notifications prefs / shop / check-in / streak-freeze / search autocomplete / tools verify / tools verified / recommendations) | +| 新增页面 | 4 个(/tools/compare / /user/notifications / /user/shop / /user/onboarding) | +| 测试文件 | 7 个(vitest.config.ts + vitest.setup.ts + 5 test suites) | +| 修改文件 | 60+ 个 | + +### 关键指标 +- 全局零 `` 标签,全部使用 `` +- TypeScript 零 `any` 类型残留 +- 132 个测试全部通过,80%+ 覆盖率目标 +- API 响应格式统一(total,非 totalCount) +- 导航全部 Link 化(零 router.push 静态跳转) + +--- + +## 2026-06-14 全面SEO/UX/性能/无障碍优化(9轮迭代) + +### 🎯 目标 +对项目进行全面深度优化,覆盖SEO基础设施、用户体验、页面性能、链接语义、无障碍访问、定时任务和Bot系统健康度,提升自然流量获取能力和用户留存率。 + +--- + +### 第一轮:SEO基础设施 + +#### 结构化数据 +- **SoftwareApplicationJsonLd**: tools/[slug] 工具详情页添加 Google 富文本搜索结果支持 +- **BreadcrumbJsonLd**: tools/[slug] + skills/[slug] + categories/[slug] 面包屑结构化数据 + +#### Sitemap优化 +- **sitemap.ts**: 从 ~16 条扩展到 1000+ 条,覆盖全量工具/技能/分类详情页 URL + +#### RSS Feed +- **src/app/api/rss/route.ts**: RSS 2.0 聚合推送,含最近50个工具/技能 +- **layout.tsx**: 添加 `` 浏览器自动发现 + +#### SSG预渲染 +- **tools/[slug]**: `generateStaticParams` 预渲染前100个热门工具 +- **skills/[slug]**: `generateStaticParams` 预渲染前100个热门技能 +- **categories/[slug]**: `generateStaticParams` 预渲染所有分类 + +#### Social元数据 +- tools/[slug] + skills/[slug] + categories/[slug]: 完整 `openGraph` + `twitter` card 元数据 + +#### 搜索框语义化 +- ToolsContent + SkillsPageClient: `
` → `
` + `
+ ); + } + + if (!data) return null; + + const avgReplies7d = + data.content.topics7d > 0 + ? (data.content.replies7d / data.content.topics7d).toFixed(1) + : "0"; + + return ( +
+ {/* Header */} +
+
+

+
+ +
+ Bot 内容质量监控 +

+

+ + 数据更新于 {relativeTime(data.timestamp)} +

+
+
+ + {/* Row 1: Content Volume — 24h vs 7d */} +
+

+ + 内容产出量 +

+
+ + + + +
+
+ + {/* Row 2: Bot Activity & Quality */} +
+

+ + Bot 活跃度 & 质量 +

+
+ +
+
+ Bot 活跃率 + +
+
+ {data.bots.activityRate}% +
+
+ +
+
+
+
+ 用户互动率 + +
+
+ {data.quality.engagementRate} +
+
+ 真人回复 {data.quality.topicsWithHumanReplies} / 总帖{" "} + {data.quality.totalTopicsAnalyzed} +
+
+ +
+
+
+
+ + {/* Row 3: Detailed Breakdown */} +
+ {/* Content Ratio */} +
+

+ + 内容结构分析 +

+
+
+
+ 话题/回复比 (24h) + + {data.content.topics24h} : {data.content.replies24h} + +
+
+ {data.content.topics24h + data.content.replies24h > 0 && ( + <> +
+ {data.content.topics24h} 帖 +
+
+ {data.content.replies24h} 回复 +
+ + )} +
+
+ + 话题 + + + 回复 + +
+
+ +
+
+ 话题/回复比 (7天) + + {data.content.topics7d} : {data.content.replies7d} + +
+
+ {data.content.topics7d + data.content.replies7d > 0 && ( + <> +
+ {data.content.topics7d} 帖 +
+
+ {data.content.replies7d} 回复 +
+ + )} +
+
+ + 话题 + + + 回复 + +
+
+
+
+ + {/* Memory & Health */} +
+

+ + Bot 记忆库 & 健康概要 +

+
+ + +
+
+
+
+ 平均记忆/Bot + + {data.bots.total > 0 + ? Math.round(data.memory.total / data.bots.total) + : 0}{" "} + 条 + +
+ 0 + ? Math.min( + (data.memory.total / data.bots.total / 100) * 100, + 100 + ) + : 0 + } + color="bg-violet-500" + /> +
+
+
+ 平均回复/帖 + + {data.content.avgRepliesPerTopic24h} + +
+ +
+
+
+
+ + {/* Row 4: Summary */} +
+
+ +

综合评估

+
+
+
+ 内容健康度: + = 3 + ? "text-emerald-600" + : parseFloat(data.content.avgRepliesPerTopic24h) >= 1 + ? "text-amber-600" + : "text-rose-600" + )} + > + {parseFloat(data.content.avgRepliesPerTopic24h) >= 3 + ? "优秀" + : parseFloat(data.content.avgRepliesPerTopic24h) >= 1 + ? "一般" + : "偏低"} + +
+
+ 活跃率: + = 50 + ? "text-emerald-600" + : parseFloat(data.bots.activityRate) >= 30 + ? "text-amber-600" + : "text-rose-600" + )} + > + {parseFloat(data.bots.activityRate) >= 50 + ? "优秀" + : parseFloat(data.bots.activityRate) >= 30 + ? "一般" + : "偏低"} + +
+
+ 互动率: + = 15 + ? "text-emerald-600" + : parseFloat(data.quality.engagementRate) >= 5 + ? "text-amber-600" + : "text-rose-600" + )} + > + {parseFloat(data.quality.engagementRate) >= 15 + ? "优秀" + : parseFloat(data.quality.engagementRate) >= 5 + ? "一般" + : "偏低"} + +
+
+ 产出稳定性: + 0 && data.content.replies24h > 0 + ? "text-emerald-600" + : "text-rose-600" + )} + > + {data.content.topics24h > 0 && data.content.replies24h > 0 + ? "正常" + : "暂停"} + +
+
+
+
+ ); +} diff --git a/src/app/(admin)/admin/bot-quality/page.tsx b/src/app/(admin)/admin/bot-quality/page.tsx new file mode 100644 index 0000000..bb48537 --- /dev/null +++ b/src/app/(admin)/admin/bot-quality/page.tsx @@ -0,0 +1,20 @@ +import { Suspense } from "react"; +import dynamic from "next/dynamic"; + +const BotQualityClient = dynamic(() => import("./BotQualityClient"), { ssr: false }); + +export const metadata = { + title: "Bot 内容质量 · 管理后台", +}; + +export default function BotQualityPage() { + return ( + 加载中... + } + > + + + ); +} diff --git a/src/app/(admin)/admin/check-tools/page.tsx b/src/app/(admin)/admin/check-tools/page.tsx index 99948dc..ff1be4c 100644 --- a/src/app/(admin)/admin/check-tools/page.tsx +++ b/src/app/(admin)/admin/check-tools/page.tsx @@ -2,6 +2,7 @@ import { useState, useEffect, useCallback } from "react"; import { Button } from "@/components/ui/button"; +import { LazyImage } from "@/components/ui/LazyImage"; interface ToolItem { id: number; @@ -331,7 +332,7 @@ export default function CheckToolsPage() { {logoFetchedTools.map((r, i) => (
{r.logoUrl && ( - + )}

{r.name}

diff --git a/src/app/(admin)/admin/forum/topics/TopicsClient.tsx b/src/app/(admin)/admin/forum/topics/TopicsClient.tsx index b36c86c..9d1eed7 100644 --- a/src/app/(admin)/admin/forum/topics/TopicsClient.tsx +++ b/src/app/(admin)/admin/forum/topics/TopicsClient.tsx @@ -51,7 +51,7 @@ export default function TopicsClient() { } const d = await res.json(); setTopics(d.topics || []); - setTotalCount(d.totalCount || 0); + setTotalCount(d.total || 0); } catch { setError("网络错误"); } finally { diff --git a/src/app/(admin)/admin/layout.tsx b/src/app/(admin)/admin/layout.tsx index bddcb85..211ab65 100644 --- a/src/app/(admin)/admin/layout.tsx +++ b/src/app/(admin)/admin/layout.tsx @@ -2,6 +2,7 @@ import Link from "next/link"; import { usePathname } from "next/navigation"; +import { LazyImage } from "@/components/ui/LazyImage"; interface MenuItem { label: string; @@ -169,7 +170,7 @@ export default function AdminLayout({