# 追光AI 变更日志 ## 2026-06-11 容器化生产切换 + 旧部署清理 ### 🎯 目标 容器测试验证通过后,直接切换生产环境,完全替换 PM2 + 主机 crontab。 ### 🚀 生产切换 1. **停止 PM2**:`pm2 delete zhuiguang-ai` + `pm2 kill`,删除 `.pm2` 目录 2. **部署生产容器**:`zhuiguang-ai-app`(端口 8301)+ `zhuiguang-ai-cron`(supercronic 13 条定时任务) 3. **清空主机 crontab**:`crontab -r`,全部由容器 supercronic 接管 4. **Nginx 零改动**:一直代理 `127.0.0.1:8301`,只是后端从 PM2 换成了 Docker ### 🧹 旧部署清理 | 删除项 | 说明 | |--------|------| | `node_modules/`、`.next/` | 旧构建产物 | | `logs/`、`data/` | 旧 PM2 日志/数据(已由 Docker volume 接管) | | `daily-discover.mjs`、`daily-news.mjs`、`task1~task6*.mjs`、`temp_clear.mjs` | 旧 cron 脚本(已由 supercronic 容器调度) | | `cron-wrapper.sh`、`install-cron.sh` | 旧 crontab 管理脚本 | | `restart.ps1` | Windows 本地重启脚本(不再适用) | | `nginx.conf` | 旧 nginx 配置(服务器使用独立 sites-enabled) | | `05-ai-discovery.md` 等 8 个旧文档 | 已由 `.trae/specs/` 统一管理 | | `.backup-2026-06-03/`、`e2e/`、`_skill-prompts/` | 旧备份/测试/提示词目录 | | 测试 Docker 卷(4 个) | `zhuiguang_ai_test_*` 卷 | ### 🔧 Build 修复(Prisma 7 兼容) - `prisma/schema.prisma`:移除 `datasource.url`(Prisma 7 要求 URL 在 `prisma.config.ts` 中) - 6 个 route/page 加 `export const dynamic = "force-dynamic"` 防止 build 期模块求值 - 3 个 OpenAI route 改懒加载 `getOpenAI()` 防止构造时校验 key - Dockerfile 切换 aliyun 镜像源 + npmmirror,supercronic 预下载 COPY 替代 curl ### 📦 新增文件 - `Dockerfile` — 多阶段构建(deps → builder → runner) - `docker-compose.yml` — 2 服务(app + cron)+ 4 命名卷 - `docker-compose.test.yml` — 测试部署(端口 8302) - `crontab.txt` — supercronic 调度表,13 条定时任务 - `scripts/entrypoint-app.sh` — APP 容器入口(prisma migrate + next start) - `scripts/entrypoint-cron.sh` — CRON 容器入口(supercronic) - `scripts/backup-volumes.sh` — 卷备份脚本 - `scripts/install-cron-backup.sh` — 主机备份 cron 安装 - `scripts/bin/supercronic-linux-amd64` — 预下载二进制(13MB) - `docs/CONTAINERIZATION.md` — 容器化部署文档 - `src/app/api/health/route.ts` — 健康检查 API(Docker HEALTHCHECK) ### 🏗️ 最终架构 ``` Nginx (443) → 127.0.0.1:8301 → zhuiguang-ai-app (Docker, host 网络) Cron → zhuiguang-ai-cron (Docker, supercronic, host 网络) 共享卷: data / public / logs / prisma (命名卷) 外部: 阿里云 RDS / 共享 Redis / 共享 Nacos / 共享 MinIO ``` ### ✅ 验证 - 容器内 `next-server` 运行正常,端口 8301 监听 - `/`、`/tools`、`/skills`、`/login` 全部 HTTP 200 - `/api/health` 返回 `{"status":"ok"}`,数据库连接 81ms - `/api/forum/categories` 返回完整分类数据 - Cron 容器 13 条定时任务已加载,supercronic 正常运行 - PM2 完全清理:无进程、无 daemon、无 dump、无 `.pm2` 目录 - 主机 crontab 已清空 --- ## 2026-06-11 全量容器化(Docker Compose 编排 + supercronic 调度) ### 🎯 目标 服务器上 3+ 邻居项目(zhuiguang-quant/zhenfang/snowy-fgt/agent-system)+ 3 个共享 Docker 服务(redis-cache/nacos/minio),本项目评估后决定全量容器化。 ### 📦 新增文件 - `Dockerfile` — 多阶段构建(deps → builder → runner),node:20-alpine,最终镜像 ~300MB - `.dockerignore` — 排除 node_modules / .next / logs / data / 调试脚本 - `docker-compose.yml` — 2 个服务(app + cron)+ 4 个命名卷 - `crontab.txt` — supercronic 调度表,13 个定时任务 - `scripts/entrypoint-app.sh` — APP 启动入口(prisma migrate + next start) - `scripts/entrypoint-cron.sh` — CRON 启动入口(supercronic) - `scripts/backup-volumes.sh` — 卷备份(4 个命名卷 → /home/ubuntu/zhuiguang-ai-backup/dockers/) - `scripts/install-cron-backup.sh` — host 备份 cron 安装 - `docs/CONTAINERIZATION.md` — 部署文档(架构 / 部署步骤 / 运维命令 / 回滚方案) ### 🏗️ 架构 ``` zhuiguang-ai-app (Next.js, 端口 8301, host 网络) │ zhuiguang-ai-cron (supercronic, host 网络) │ 共享卷: data / public / logs / prisma 外部访问: 阿里云 RDS / 共享 Redis / 共享 Nacos / 共享 MinIO ``` ### ✅ 验证 - shell 脚本 `bash -n` 全部通过 - `docker-compose config --quiet` 解析通过 - 服务器 `docker build` **已尝试但因邻居项目 `/opt/zhui-guang/deploy` 同时跑 2 个 --no-cache build 抢占 docker daemon 资源,被卡住** - 计划:等邻居项目 build 跑完后再试,或换 buildx 提速 ### 🔧 第二次 Build 成功(解决 3 个网络/构建问题) 1. **apk 源极慢**(`dl-cdn.alpinelinux.org` 1m23s vs `mirrors.aliyun.com` 0.134s) - Dockerfile 加 `sed -i 's|dl-cdn.alpinelinux.org|mirrors.aliyun.com|g' /etc/apk/repositories` - npm 也切到 `registry.npmmirror.com` 2. **GitHub release 下载超时**(supercronic 二进制从 release-assets.githubusercontent.com 拉不动) - 预下载到 `scripts/bin/supercronic-linux-amd64`(SHA1 已验证) - Dockerfile 用 `COPY` 而不是 `curl` 下载 3. **Next.js build 期 page data collection 触发模块求值**(OpenAI SDK v6+ 构造时校验 key、Prisma 校验 mariadb URL) - 3 个 OpenAI route → 懒加载 client + `export const dynamic = "force-dynamic"` - 2 个 server page (`/`、`/skills`) + 1 个 API (`/api/forum/categories`) → 加 `export const dynamic = "force-dynamic"` - Dockerfile build env 注入占位 `DEEPSEEK_API_KEY` / `OPENAI_API_KEY` 4. **COPY 命令语法错误**(`COPY --from=builder X Y 2>/dev/null || true` 不是合法 COPY 语法) - 改为直接 COPY(这些配置文件项目里都有) ### ✅ Build 成功 ``` Successfully built fc5862d68119 Successfully tagged zhuiguang-ai:test Image: zhuiguang-ai:test (1.27GB) - /usr/local/bin/supercronic-linux-amd64 ✓ - /app/.next/BUILD_ID ✓ - /app/.next/app-build-manifest.json ✓ Entrypoint: /sbin/tini -- /usr/local/bin/entrypoint-app.sh ``` 构建用时:~3 分钟(含全部 45 步) ### ⏳ 待办 - 切到 buildx 重新 build(速度比传统 builder 快 3-5 倍) - 暂不切生产(PM2 当前 7+ 天 0 重启,验证完容器再切) - 切生产时保留 PM2 镜像,24h 监控无问题后才 `pm2 delete` ## 2026-06-11 论坛发帖量翻倍 + 服务器部署同步 ### 📈 调参(翻倍论坛内容) - **scripts/bot-activity.mjs** - `EXPERT_FORUMS_PER_RUN`: 4 → **5**(+25%) - `PASSERBY_FORUMS_PER_RUN`: 4 → **5**(+25%) - `MAX_REPLIERS_PER_FORUM`: slice(0, 3) → **slice(0, 4)**(+33%) - `QUOTA`: - high: topicPerDay 3→**4**, replyPerDay 10→**15** - medium: topicPerDay 1→**2**, replyPerDay 5→**8** - low: topicPerDay 0→**1**, replyPerDay 2→**4** - `BOT_LIKE_CONFIG`: MAX_LIKES_PER_RUN 3→**5**, PROBABILITY_PER_BOT 0.6→0.65 - 跑批节奏不变:9-23 每整点 15 轮/天 - **理论产出**:原 5 项/轮 → 现 8-10 项/轮,约 1.7-2x ### 🛠️ 部署同步(修复 Prisma 模型未注册) - 服务器原本是 v1 schema,跑了简单版 bot-activity.mjs(无 BotDailyStat 等新模型) - 部署本次需要: 1. 推送新 `scripts/bot-activity.mjs`(带 v2 逻辑) 2. 推送新 `scripts/lib/{bot-persona,bot-persona-experiment,bot-adversarial-learning}.mjs`(旧服务器没这几个 lib) 3. 推送新 `prisma/schema.prisma`(v2) 4. 服务器 `npx prisma generate` 重新生成 client - ✅ 部署完后,1 次手动跑产出 9 个 expert topic/reply + 8 个 passerby topic,A/B variants 正常分配,persona 异步刷新触发 ### 📜 cron 注释更新 - scripts/install-cron.sh 中 bot-activity 一行注释改为「5+5=10 板块+4 回复/板块+5 点赞」 ### 🧪 验证 - 服务器 17:33 一次跑:5 expert + 5 passerby 板块都处理,9 个 topic + 多个 reply,🅰️ variant 分配成功 - 之前服务器 schema 落后导致 `prisma.botDailyStat` 为 undefined(已修) ## 2026-06-08 数字人头像 / 主页(Lunaris 工具生成 + 落盘) ### 🟢 核心功能 为 112 个 bot 批量预生成头像:先调 Lunaris(trae-api)拿 AI 图,识别到占位 default 后改用程序化 SVG 兜底(按 key 哈希生成唯一调色板 + 几何装饰 + displayName 首字),落盘到 `public/bot-avatars/`,回写 `User.avatarUrl`。 ### 📦 核心库 - **scripts/lib/bot-avatar-generator.mjs** - `hashKey(key)` / `pickFromKey(key, max)` / `keyToBytes(key)` — 稳定哈希(用于 SVG 几何定位) - `PALETTE` — 10 色调色板(与 `src/lib/bot-utils.ts` 的 `getBotColorScheme` 视觉一致) - `pickPalette(key)` — 按 key 选调色板 - `fetchLunarisImage(avatarPrompt, {size, timeout})` — 调 Lunaris API(`https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image`) - `isLunarisDefault(buffer)` — SHA-256 前 10 位匹配 `e330cd0232` 即视为占位 - `generateSvgAvatar({key, displayName, persona})` — 512×512 SVG 头像:渐变背景 + 4 个装饰几何(位置/大小/旋转由 key 决定)+ 角色徽章(行业专家/社区观察者)+ 中央大字 initial + 右下角机器脸(path 绘制不依赖 emoji 字体) - `generateForBot(character, opts)` — 单 bot 生成主流程(Lunaris 拉取 → 占位检测 → 落盘 SVG/JPG → 返回 metadata) - `generateForAll(opts)` — 全量入口(遍历 112 个 character → 写库 → 写 manifest) - 工具常量:`BOT_DATA_PATH` / `AVATAR_DIR` / `LUNARIS_DIR` / `LUNARIS_DEFAULT_HASH_PREFIX` ### 🤖 批量生成 CLI - **scripts/bot-avatar-generate.mjs** — 调度入口 - `node scripts/bot-avatar-generate.mjs` — 全部 112 个 bot 一次性生成 - `--bot=` — 单个 bot - `--force` — 覆盖已存在 - `--use-lunaris-only` — 禁用 SVG 兜底(当前 Lunaris 全是 default,不推荐) - `--no-update-db` — 只落盘不写 User.avatarUrl - `--dry-run` — 试运行 - `--delay=` — bot 间隔(默认 200ms) ### 🗂️ 落盘结构 ``` public/bot-avatars/ ├── {key}.svg # 唯一程序化头像(112 个,~2.5KB/个,总 278KB) ├── manifest.json # 生成清单(generatedAt / total / succeeded / failed / avatars[]) └── lunaris/ └── {key}.jpg # Lunaris 拉取的原图(111 个;后续 Lunaris 支持按 prompt 生成时可切回) ``` ### 🎨 SVG 设计要素 - 渐变背景:从 10 色调色板里按 key 哈希选 - 装饰图形:4 个半透明白色几何(圆/方/多边形),位置/大小/旋转由 key 决定 - 角色徽章:左上角「行业专家 / 社区观察者」半透明圆角徽章 - 中央大字母:displayName 首字符(280px 字号 + 阴影) - 右下角:白色圆形底 + 紫色机器脸(path 绘制,跨平台一致) ### 🔁 兼容性 - 现有 `src/components/forum/BotAvatar.tsx` 优先用 `user.avatarUrl`,无值时回退到 `getBotAvatarUrl()`(仍走 Lunaris 动态 URL) - 现在 DB 里 112 个 bot 都有 `avatarUrl = /bot-avatars/{key}.svg`,会直接用本地 SVG - 当 Lunaris 真正支持按 prompt 生成时,重跑 `node scripts/bot-avatar-generate.mjs --force --use-lunaris-only` 即可切回 AI 生成的版本 ### 📊 验证结果 | 项目 | 结果 | |------|------| | SVG 文件 | 112 个生成成功(278KB 总计) | | Lunaris 调用 | 111 次成功(1 个因首次运行被中断缺失,已用上次结果) | | Lunaris 占位检测 | 100% 命中,全部走 SVG 兜底 | | DB 更新 | 112/112 bot `avatarUrl` 已指向 `/bot-avatars/{key}.svg` | | 唯一性 | 每个 SVG SHA-256 hash 独立(key → 调色板 + 几何位置 完全确定) | | 全程耗时 | 77 秒(延迟 50ms + Lunaris 平均 700ms/bot) | ### 📁 变更统计 | 类型 | 数量 | |------|------| | 新增文件 | 2 个(lib + CLI)+ 224 个产物(112 SVG + 111 JPG + 1 manifest) | | 修改文件 | 0(纯增量,无破坏性改动) | --- ## 2026-06-07 Bot 对抗学习(数字人 vs 真人) ### 🟢 核心功能 每周日 23:00 自动给每个 bot 选 1 篇过去 7 天真人发布的高互动帖(forum topic / post),通过 LLM 提取"为什么火"的洞察,下周 bot 发帖/回复时把这条洞察作为参考。下次生成内容时会优先选 status=active、且 learnedAt 最近的学习作为"灵感"。 ### 🗄️ 数据模型(1 张新表) - **prisma/schema.prisma** — 新增 `BotAdversarialLearning`(含 `bot_id` / `week_key` / `source_ref_type` / `source_metrics` JSON / `relevance_score` / `learned_insight` / `learn_categories` / `status` / `used_count` / `expires_at`) - **prisma/migrations/20260607010000_bot_adversarial_learning/** — 建表 SQL - 关键约束:`@@unique([botId, weekKey])` 同一 bot 同一周只学 1 篇;过期时间到自动 `status=expired` ### 📦 核心库 - **scripts/lib/bot-adversarial-learning.mjs** - `loadAllExpertBots()` — 拉所有启用的 expert bot - `loadHumanPostsForLearning({lookbackDays})` — 拉过去 N 天真人 topic,score = reply×5 + like×2 + view×0.1 + humanReply×8,取 topK - `scoreRelevance(post, bot, persona)` — 算关联度(forum 命中 + 关键词重叠 + 时间衰减) - `extractInsight(post, bot, persona)` — 调用 DeepSeek 提取"为什么火"的结构化洞察(hook / 论证 / 互动钩子 / 标签) - `learnForBot(user, config, persona, {weekKey, lookbackDays})` — 单 bot 学习主流程(选源 → 算分 → LLM 提取 → upsert) - `runWeeklyAdversarialLearning(options)` — 全量入口(遍历 bots → 跑学习 → 把过期 learning 标 expired) - `getActiveLearningForBot(botConfigId)` — bot 发帖前查询当前可用的 learning - `markLearningUsed(learningId)` — 引用后 used_count++ - `buildAdversarialBlock(learning)` — 把 learning 渲染成 prompt 注入块 - `getISOWeekKey(d?)` — 计算 "2026-W23" 形式的周键 ### 🤖 Bot 活动引擎集成 - **scripts/bot-activity.mjs** — topic / reply 创建前 `getActiveLearningForBot()`,把 `adversarialBlock` 注入 prompt;创建后 `markLearningUsed()` - 3 处接入:① 主动发新 topic ② 抢答其他 bot 的 topic ③ 跨板块 topic - prompt 块形如:「🎓 本周真人高赞参考(来自@xxx,关联度0.78):hook: ... 论证: ... 互动钩子: ... 标签: [...]」 ### ⏰ 调度任务 - **scripts/bot-adversarial-learning-run.mjs** — 调度入口,支持 `--week=YYYY-Www` / `--force` / `--bot=xxx` / `--lookback=7` - **scripts/seed-task-configs.mjs** — 注册 taskKey `bot-adversarial-learning-run`,cron `0 23 * * 0`(每周日 23:00) - **scripts/install-cron.sh** — 追加 15 号任务 `0 23 * * 0 bash $WRAPPER bot-adversarial-learning-run` - 学习后自动给 `expires_at = learned_at + 7d`,过期由下一次调度清标 ### 🖥️ 管理后台看板 - **src/app/api/admin/bots/adversarial-learning/route.ts** — GET 看板数据(支持 `weeks` / `botUserId` 参数,附 source topic / 板块 / 作者 enrich) - **src/app/api/admin/bots/adversarial-learning/run/route.ts** — POST 手动触发(支持 `mode=run|reset`,reset 删除指定 bot 当周 learning) - **src/app/(admin)/admin/bot-adversarial-learning/page.tsx** + **BotAdversarialLearningClient.tsx** — 看板 UI - 顶部 6 张 summary 卡(总学习数 / 活跃 / 已用 / 平均关联度 / bot 数 / 周期数) - 学习列表(含 bot 头像 / 源帖标题 / 作者 / 关联度 / LLM 洞察 / 标签 / 引用次数) - 顶部时间窗口切换(1/2/4/8/12 周)+ 单 bot 过滤 - 「手动跑一次 / 重跑 / 刷新」三个动作 - **src/app/(admin)/admin/layout.tsx** — 新增菜单 "Bot 对抗学习" 入口(紧邻 A/B 看板) ### 🎯 选源策略 - 仅取非 bot 用户(`author.isBot = false`) - 真实互动分 = `replyCount×5 + likeCount×2 + viewCount×0.1 + humanReplies×8`(真人回复权重最高) - 关联度 = forum 命中(0.4)+ 关键词 jaccard(0.4)+ 24h 衰减(0.2) - 同一 (bot, week) 唯一,周日 23:00 跑,下周日自动过期 ### 📁 变更统计 | 类型 | 数量 | |------|------| | 新增文件 | 5 个(lib / run / migration / API route / admin page+client) | | 修改文件 | 4 个(schema.prisma / bot-activity.mjs / seed-task-configs.mjs / install-cron.sh / admin/layout.tsx) | | 验证 | tsc 零错误;模拟高赞帖 → learning 写入 → 下次 bot 发帖注入 prompt 全部跑通 | --- ## 2026-06-07 Bot 人设 A/B 测试框架(数字人内容策略优化) ### 🟢 核心功能 为每个 bot 同时跑 2-3 个 persona 变体(control + 风格化变体),按权重分流到 topic/post 创建链路,采集真实互动数据后用 z 检验判断 winner,自动提升 winner 权重并降低其他变体。 ### 🗄️ 数据模型(4 张新表) - **prisma/schema.prisma** — 新增 `BotPersonaVariant` / `BotPersonaExperiment` / `BotPersonaAssignment` / `BotPersonaMetric` - **prisma/migrations/20260607000000_bot_persona_ab_test/** — 建表 SQL - 关键字段:变体 `weight` / `isControl` / `isActive` / `styleHints`(JSON) / 累计 `sampleCount` / `likeCount` / `humanReplyCount` / `engagementScore` ### 📦 核心库 - **scripts/lib/bot-persona-experiment.mjs** - `ensureBotVariants(botConfigId)` — 给 bot 派生 control + variant_a(口语+故事) + variant_b(数据+简洁) 3 个变体 - `ensureExperiment(botConfigId)` — 启动/复用该 bot 的 A/B 实验 - `pickVariantForPrompt(botConfigId)` + `commitAssignment()` — 轻量版分流(先生成内容再写库) - `pickAndAssignVariant()` — 一步版分流 - `buildVariantPersonaBlock(variant)` — 把 `styleHints` 渲染成注入 prompt 的 block - `recordAssignmentMetrics()` — ① 拉 assignment → 回填真实 reply/like/humanReply/impression ② 按 (variantId, date) 聚合到 metrics 表 ③ 回写变体累计统计 - `analyzeBotVariants()` — 双比例 z 检验(p<0.1, sample≥20) - `analyzeAndSwitchAllBots()` — 遍历所有 bot,发现 winner 后把权重提升到 0.85、其他降到 0.075,experiment → completed ### 🤖 Bot 活动引擎集成 - **scripts/bot-activity.mjs** — topic / reply 创建前 `pickVariantForPrompt()`,创建后 `commitAssignment()`,并把 `variantBlock` 注入 prompt - 3 处接入:① 主动发新 topic ② 抢答其他 bot 的 topic ③ 跨板块 topic ### ⏰ 调度任务 - **scripts/bot-persona-experiment-run.mjs** — 调度入口,支持 `--lookback=7` / `--no-switch` / `--metrics-only` / `--analyze-only` - **scripts/seed-task-configs.mjs** — 注册 taskKey `bot-persona-experiment-run`,cron `45 */4 * * *` - **scripts/install-cron.sh** — 追加 14 号任务 `45 */4 * * * bash $WRAPPER bot-persona-experiment-run ... --lookback=7` - **scripts/seed-persona-variants.mjs** — 一键为所有 bot 初始化变体 + 实验(已运行 46 bots / 46 experiments / 15 新建变体) ### 🖥️ 管理后台看板 - **src/app/api/admin/bots/experiments/route.ts** — GET 看板数据(变体 / 实验 / 显著性分析) - **src/app/api/admin/bots/experiments/run/route.ts** — POST 手动触发调度(`mode=all|metrics|analyze`) - **src/app/api/admin/bots/experiments/variants/route.ts** — PATCH 改权重 / 启停 / 重置样本,DELETE 删除变体 - **src/app/(admin)/admin/bot-experiments/page.tsx** + **BotExperimentsClient.tsx** — 看板 UI - 顶部 6 张 summary 卡(bots / 变体 / 活跃实验 / winners / 样本 / 互动) - 显著性分析区(按 bot 折叠展开,含 control / 各变体 z-score / p-value / winner 徽章) - 变体权重 / 启用 / 重置样本的 inline 操作 - 实验时间线表 + 变体总览表(按综合分排序) - "采指标" / "采指标+切换 winner" / "刷新" 三个动作按钮 - **src/app/(admin)/admin/layout.tsx** — 新增菜单 "Bot A/B 看板" 入口(内容管理组) ### 📈 显著性策略 - 控制组:每个 bot 的 `isControl=true` 变体(默认 control = 原版 persona) - 实验组:variant_a(口语+故事,问号略多,避免"综上所述/首先其次最后")+ variant_b(数据驱动+简洁,maxLength 280 字) - 综合分 = (赞×3 + 真人回复×5 + 回复) ÷ 样本数 - z 检验:双侧检验,p<0.1 且样本≥20 视为显著 - 切换:winner weight=0.85,其他活跃变体 weight=0.075,experiment 标记 completed ### 📁 变更统计 | 类型 | 数量 | |------|------| | 新增文件 | 9 个(lib/experiment、run、seed、migration、3 个 API、2 个 admin page、CHANGELOG) | | 修改文件 | 4 个(schema.prisma / bot-activity.mjs / seed-task-configs.mjs / install-cron.sh / admin/layout.tsx) | | 验证 | tsc 零错误;seed 跑通 46 bots / 15 新建变体;scheduler 调度器触发无错 | --- ## 2026-05-28 UI优化 + 社区页面修复 ### 🟡 HeroBanner 新增 orange 配色方案 - **问题**: Skills 页面深红色(crimson `#881337→#e11d48`)视觉过深,用户反馈体验不佳 - **修复**: 新增 `orange` 配色方案 `#c2410c → #ea580c → #f97316 → #fb923c` - **文件**: `src/components/common/HeroBanner.tsx` — 新增 `orange` scheme,STAR_COUNTS 添加 `orange: 80`,SVG 圆环渲染加入 orange - **页面**: `src/app/skills/SkillsPageClient.tsx` — colorScheme 从 `crimson` 改为 `orange` ### 🟡 社区话题详情页 Server Component 错误修复 - **问题**: `/community/topic/[id]` 页面报错 `Event handlers cannot be passed to Client Component props` - **根因**: 页面是 Server Component,但内联了 `onSubmit={async (e) => {...}}` 事件处理函数 - **修复**: 提取回复表单为独立 Client Component `ReplyForm.tsx`,页面仅渲染 `` - **文件**: `src/app/community/topic/[id]/ReplyForm.tsx`(新增)、`src/app/community/topic/[id]/page.tsx`(修改) - **同时**: ReplyForm 增加 `disabled` 状态防止重复提交 ### 📁 变更统计 | 类型 | 数量 | |------|------| | 修改文件 | 3个(HeroBanner.tsx + SkillsPageClient.tsx + community/topic/[id]/page.tsx) | | 新增文件 | 1个(ReplyForm.tsx) | | 新增配色 | 1个(orange) | | TypeScript检查 | 零错误通过 | --- ## 2026-05-28 全盘安全扫描 + 系统优化(6项修复) ### 🔴 P0 安全修复:任意用户可创建工具 - **admin/tools/route.ts POST**: 此前无任何认证,任意登录用户可直接创建工具 - **修复**: 添加 `requireAdmin()` 认证检查,输入参数校验(name/websiteUrl/categoryId非空),categoryId有效性校验(存在性检查),slug自动去重 - **注**: 扫描全部25个后台写操作API,其余24个已有requireAdmin ### 🔴 P0 FK回退硬编码修复(4处) - **问题**: `categoryId || 1` — id=1 在生产数据库不存在(分类从165开始),导致FK约束失败 - **修复方式**: 动态取数据库第一个有效分类 `category.findFirst({ select: { id: true } })` 替代硬编码 `1` - **修复文件**: | 文件 | 修复 | |------|------| | `scripts/task3-check-tools.mjs` | 自动发布时 FK 回退 | | `src/app/api/admin/tools/route.ts` | 创建工具 POST | | `src/app/api/admin/pending/batch-publish/route.ts` | 批量发布待审核工具 | | `src/app/api/admin/pending-skills/batch-publish/route.ts` | 批量发布待审核技能 | | `src/app/api/admin/pending-skills/publish-all/route.ts` | 一键发布所有待审核技能 | ### 🟡 P1 批量操作安全加固 - **validation.ts**: `toolImportSchema.max(100)`、`skillImportSchema.max(100)`、`newsImportSchema.max(50)`、`checkTypeSchema.max(50)` - **批量发布API**: `pending/batch-publish` 和 `pending-skills/batch-publish` 添加 `ids.length > 500` 上限检查 - **问题**: 原无上限,恶意请求可一次处理任意多条记录 ### 🟡 定时任务全盘扫描结果 - **lib库**: prisma/deepseek/github/logo-fetcher/rate-limit 全部符合规范 ✅ - **脚本**: 全部使用 `mariadb://` 协议 + `PrismaMariaDb` adapter ✅ - **API**: 25个后台POST中24个已有requireAdmin(1个漏洞已修复) ✅ - **TypeScript**: `tsc --noEmit` 零错误通过 ✅ ### 📊 定时任务执行情况(05-27 → 05-28) | 任务 | 状态 | 说明 | |------|------|------| | Task5 更新星值 | ✅ 正常 | 更新37个技能星值 | | Task3 工具巡检 | ⚠️ 服务器未更新 | 本地已修复FK回退,需部署脚本到服务器 | | Task6 评测热门 | ✅ 正常 | 无待评测技能,reviewed:0(正常) | | Task4 发现技能 | ✅ 正常 | 发现0,跳过42(GitHub新项目少) | | Task1 发现工具 | ✅ 正常 | 发现6个,导入2个 | | AI日报 | ✅ 正常 | 生成37号日报,6条内容 | | Task7 新闻推社区 | ✅ 正常 | 创建6条帖子 | ### 📁 变更统计 | 类型 | 数量 | |------|------| | 修改文件 | 8个 | | 修复问题 | 6项(1P0安全 + 4P0 FK + 1P1批量上限) | | TypeScript检查 | 零错误通过 | --- ## 2026-05-27 定时任务根因修复 + 新闻推社区功能上线 ### 🔴 P0 根因修复:定时任务全部不运行(5月25日故障) **诊断过程:** - SSH 调查发现 `logs/` 目录不存在,crontab 环境不加载 `.env` 变量 - 首次修复(wrapper + install-cron)后测试 task5 报错:`error parsing connection string, format must be 'mariadb://'` - 进一步发现 `.env` 文件值带单引号(`DATABASE_URL='mysql://...'`),`xargs` 导出方式无法正确处理 **三层根因 + 修复:** | 层次 | 问题 | 修复 | |------|------|------| | 环境变量加载 | crontab 不加载 `.env` → 创建 `cron-wrapper.sh` 统一入口,使用 `source` + `set -a`/`set +a` | | .env 值格式 | 带单引号导致 `SET NAMES utf8mb4'` SQL语法错误 → 去除所有值的引号 | | 数据库协议 | `mysql://` 与 PrismaMariaDb 不兼容 → 21个脚本添加 `.replace('mysql://', 'mariadb://')` | ### 🔴 P0 脚本语法修复 - **task1-discover-tools.mjs**: 移除 TypeScript 类型注解(`const errors: string[]` / `const categoryMap: Record`)— `.mjs` 文件不支持 TS 语法 - **task6-review-hot.mjs**: 移除 `prisma.skillReview.create()` 中的 `tags` 字段 — SkillReview 模型无此字段,Prisma 报 `Unknown argument 'tags'` - **task7-news-to-community.mjs**: CATEGORY_MAP slug 映射修复 — `ai-products/tech-discussion/open-source/industry-trends` → `ai-tools/ai-news/tech/discuss`(匹配 DB 实际 slug) ### 🟢 新功能上线 - **Task7 新闻推社区**: `scripts/task7-news-to-community.mjs` — 每天 08:00 将日报新闻分发到社区 4 个板块 - 映射规则:product→AI工具推荐 / business→AI资讯 / model→技术探讨 / opensource→技术探讨 / policy→AI资讯 / insight→观点讨论 - 首次运行测试:9条日报全部成功分发([AI资讯]×6, [技术探讨]×2, [观点讨论]×1) ### 🛠 新增工具脚本 - `scripts/fix-db-url.cjs` — 批量修复 21 个脚本的 `mysql://` → `mariadb://` 协议 - `scripts/fix-ts-types.cjs` — 批量移除 `.mjs` 文件中的 TypeScript 类型注解 - `scripts/check-categories.cjs` — 检查数据库社区板块 ### 📊 测试验证结果(7个任务全部通过) | 任务 | 测试结果 | |------|----------| | Task5 星值更新 | ✅ 更新37⭐,0失败 | | Task3 工具巡检 | ✅ 巡检463个,网站失效32个 | | Task6 评测热门 | ✅ 评测2个(Snailclimb/JavaGuide 4.4⭐, system-prompts 3.4⭐) | | Task4 技能发现 | ✅ 正常执行 | | Task1 工具发现 | ✅ 发现10个AI工具,入库6个 | | 日报生成 | ✅ 生成9条AI日报 | | Task7 新闻推社区 | ✅ 9条全部分发到社区板块 | ### 📁 变更统计 | 类型 | 数量 | |------|------| | 修改的服务器脚本 | 21个(协议修复)+ 3个(bug修复) | | 新增脚本 | 3个(fix-db-url.cjs, fix-ts-types.cjs, check-categories.cjs) | | 修复的根因 | 3层(env加载 + 引号 + 协议) + 4个脚本bug | | 服务器验证 | 7/7 任务全部测试通过 | --- ## 2026-05-25 全面审查优化(三轮扫描 + 43项修复) ### 🔴 P0 安全修复 - **.env入.gitignore**: `.env` 和 `.env.production` 加入忽略列表,创建 `.env.example` 模板文件 - **CSP/HSTS安全头**: next.config.mjs 添加 Content-Security-Policy、Strict-Transport-Security、Permissions-Policy - **Logo安全加固**: 5MB大小限制 + SVG script/iframe/on-event 标签消毒 + MD5→SHA256 - **middleware权限收紧**: VIP用户不再能访问 /admin/* 和 /api/admin/*,仅admin角色可访问 - **单条发布去重**: pending-skills/[id]/publish 添加 name/sourceUrl/slug 三重去重检查,冲突返回409 - **Web更新星级GitHub认证**: update-stars API 添加 GITHUB_TOKEN 环境变量认证(限流60→5000次/小时) - **删除危险API**: pending-skills/route.ts 的 DELETE 方法(原可无条件清空全表)已移除 - **skill-discoverer绕过审核**: Web自动发现统一改为写 pendingSkill 表(status:pending),不再跳过审核 - **prisma默认连接修复**: DATABASE_URL未设置时抛出明确错误,移除硬编码默认连接串 ### 🔴 P0 可靠性修复 - **deepseek.ts超时重试**: OpenAI客户端添加 timeout:120000 + maxRetries:2,模型名改为 DEEPSEEK_MODEL 环境变量 - **github.ts超时重试**: Octokit 添加 timeout:30000,新增 withRetry 指数退避重试,403/404/422 不重试 - **脚本层DeepSeek保护**: 4个定时脚本(daily-discover/daily-news/task1/task6)添加 AbortSignal.timeout(120000) + withRetry 重试 - **脚本启动验证**: 定时脚本启动时验证 DEEPSEEK_API_KEY + DATABASE_URL 存在性 - **auth.ts OAuth竞态修复**: GitHub OAuth 并发创建用户时 P2002 唯一约束冲突自动回退 findFirst ### 🔴 P0 性能修复 - **skill-discoverer N+1优化**: 批量预加载 sourceUrl/slug/分类到内存 Set,DB查询从 ~360次 降至 ~22次(↓94%) - **后台API分页限制**: 6个管理后台 API findMany 添加 take:500/1000 防全表扫描 - **getUserStats优化**: 改用 _count 聚合替代加载全量关联记录取 .length ### 🟡 P1 修复 - **分类动态加载**: discover-tools 页面分类列表从硬编码改为 /api/categories 动态加载 - **星级字段统一**: update-stars API 同时写入 rating + githubStars,统一提取 starsToRating() 函数 - **fetch-logos并发Bug修复**: processWithConcurrency 改用 Set + then(cleanup) 模式 - **批量发布冲突不删除**: 冲突工具改为跳过标记,不再静默物理删除 - **check-tools API重试**: 添加指数退避重试(最多2次) - **前端空指针修复**: 13处 email?.split / overallAvg?.toFixed / createdAt?.toLocaleDateString 可选链 - **logo-fetcher日志**: 9处空白 catch 块添加 console.warn 日志 - **rate-limit清理**: 每5分钟自动清理过期IP记录,防止内存无限增长 - **公开API缓存**: 5个公开API添加 Cache-Control 响应头(30s~300s分级缓存) ### 🔴 已修复的前台Bug - **/reviews 评测列表不显示**: ReviewsList.tsx 添加 useEffect 在挂载时触发初始数据加载 ### 📁 变更统计 | 类型 | 数量 | |------|------| | 修改文件 | ~55个 | | 新增文件 | 1个(.env.example) | | 修复问题 | 43项 | | TypeScript检查 | 零错误通过 | --- ## 2026-05-25 页面重构 + HeroBanner五色方案 ### 🎨 页面重构为三栏布局 - **统一布局**: `grid-cols-[208px_1fr_280px]` (左侧栏208px + 内容区 + 右侧栏280px) - **/reviews**: HeroBanner + 左侧(外部热度排行+评测分值排行) + 右侧(五维模型+评测说明) - **/community**: HeroBanner + 左侧(本月活跃用户+积分排行) + 右侧(社区指南) - **/daily**: 保持原有三栏结构 ### 🌈 HeroBanner 五色方案 - **重构组件**: 从三元表达式改为 `SCHEMES` 配置表,代码量从560行减至410行 - **5种配色**: | 页面 | 配色 | 背景渐变色 | 高亮色 | |------|------|-----------|--------| | 首页 `/` | 🔵 cosmos 星空蓝 | `#1e3a5f → #1d4ed8` | `#93c5fd` | | 技能 `/skills` | 🩷 crimson 玫红 | `#881337 → #e11d48` | `#fda4af` | | 日报 `/daily` | 🟠 dawn 黎明金 | `#78350f → #a16207` | `#fcd34d` | | 评测 `/reviews` | 🟣 aurora 极光紫 | `#4c1d95 → #6d28d9` | `#c4b5fd` | | 社区 `/community` | 🟢 forest 森林绿 | `#064e3b → #047857` | `#6ee7b7` | - **接口变更**: 旧 `highlightColor + starStyle` → 新 `colorScheme`,一个参数搞定 ### 📁 新增/修改 | 文件 | 操作 | 说明 | |------|------|------| | `src/components/common/HeroBanner.tsx` | 重写 | 5色方案+配置表 | | `src/app/reviews/page.tsx` | 重写 | 三栏布局+左侧排行 | | `src/app/community/page.tsx` | 重写 | 三栏布局+左侧排行 | | `src/app/community/CommunitySidebar.tsx` | 新增 | 活跃用户+积分排行 | | `src/app/api/user/rankings/route.ts` | 新增 | 用户排行API | | `src/components/layout/Header.tsx` | 修改 | 社区导航图标更换 | --- ## 2026-05-25 等级体系升级 + 论坛二级分类 + 会员空间 ### 🔥 20级成长体系 (类似QQ) - **从4级扩展到20级**: LV01(0分) → LV20(50000分) - **等级图标**: ⭐(初级) → ⭐⭐⭐(中级) → 🌟(高级) → 🌟🌟(精英) → 🌟🌟🌟(大师) - **旧等级迁移**: BRONZE→LV01, SILVER→LV03, GOLD→LV06, PLATINUM→LV10 - `src/lib/level-config.ts` — 20级完整配置(含每日限额表) - **迁移脚本**: `scripts/run-migration-manual.mjs` (绕过shadow DB问题) ### 🔥 论坛二级分类结构 - **ForumCategory自引用关系**: parent/children (CategoryHierarchy) - **6个父分类 + 24个子分类**: - 综合讨论区 → 新手入门/AI资讯/观点讨论 - AI工具 → 聊天写作/图像创作/视频音频/代码编程/效率办公/数据分析/教育学习 - 开源技能 → LLM框架/Agent框架/RAG技术/模型训练/部署推理/多模态/数据工程 - 技术深度 → 算法原理/模型评测/安全对齐/硬件加速 - 教程与分享 → 入门教程/实战项目/最佳实践 - 社区互动 → 求助问答/求职招聘/闲聊灌水/反馈建议 - **修复脚本**: `scripts/fix-forum-categories.mjs` (修正孤立子分类parentId) - `/community` 首页更新为显示二级分类结构 ### 🆕 会员个人空间 - `/user/[id]` — 会员空间页面(资料/统计/话题/评论) - **分享功能**: `ShareButton` 组件(原生API/Twitter/QQ/复制链接) - **bio字段**: 用户个人简介,支持编辑(最多500字符) - `/user` 页面新增bio编辑区域 ### 🔧 技术修复 - 修复所有旧等级引用 (BRONZE → LV01) - 修复 ShareButton 构建错误 (Twitter图标 → Link图标) - 修复 LevelProgress 动态颜色逻辑 (基于等级数字计算) - 数据库迁移标记为已应用: `20260525000000_expand_user_levels` --- ## 2026-05-24 会员功能优化 + 追光会论坛上线 ### 🆕 积分体系 - **PointsHistory** 数据模型 — 记录所有积分变动,含原因/详情/关联信息 - **4级会员系统**: 青铜🥉(0分) → 白银🥈(100分) → 黄金🥇(500分) → 铂金💎(2000分) - **积分规则**: 评论+5/被赞+2/发帖+10/回复+5/评价+8,含每日上限防刷分 - `src/lib/level-config.ts` — 客户端安全等级配置(无 Prisma 依赖) - `src/lib/points-reward.ts` — 积分奖励引擎(rewardPoints / getUserStats / getUserPointsHistory) - `/api/user/points` — 积分统计 + 历史查询 API ### 🆕 追光会(论坛) - **3个新页面**: /community(首页)、/community/[category](板块)、/community/topic/[id](话题详情) - **8个论坛板块**: 综合讨论/AI工具推荐/技能分享/技术探讨/教程指南/反馈建议/求职招聘/闲聊灌水 - **5个论坛API**: 分类/话题(CRUD)/回复(CRUD) 共10个端点 - **4个论坛组件**: ForumBoard / ForumTopicCard / ForumEditor / NotificationBell - **种子脚本**: `scripts/seed-forum-categories.mjs` ### 🆕 评论系统(全新实现) - `/api/comments` — GET获取(含用户等级/积分)+ POST创建(自动积分奖励+通知) - `/api/comments/[id]/like` — 点赞/取消点赞(切换模式,+2分+通知) - `CommentCard` — 评论卡片(等级徽章/评分/点赞/嵌套回复) - `CommentThread` — 评论列表(分页/排序/回复面板) ### 🆕 通知系统 - `/api/notifications` — GET获取(含未读数)+ PUT标记已读 - `NotificationBell` — 导航栏通知铃铛(红点/下拉面板/自动已读) - 自动触发: 评论回复/评论点赞/话题回复 ### 🆕 会员UI组件 - `MemberBadge` — 等级徽章(3种尺寸,支持显示/隐藏积分) - `LevelProgress` — 等级进度条(当前等级/下一级/进度百分比) ### 🔧 技术改进 - **NextAuth 类型扩展** (`src/types/next-auth.d.ts`) — Session/User/JWT 包含 level/points - **auth.ts 增强** — JWT/Session回调包含 level/points - **客户端安全架构** — level-config.ts 与 points-reward.ts 分离,避免客户端打包Node.js原生模块 - **Header 增强** — 新增社区导航链接 + 通知铃铛 + 会员等级徽章 - **/user 页面增强** — 积分进度条/积分明细/统计卡片 ### 修复 - ForumPost 新增 `isAnswer` 字段(标记最佳回答) - Prisma schema 日期默认值修正 `@default(now)` → `@default(now())` - 迁移兼容问题处理(`prisma db push` 同步) ### 统计数据(本次新增) - **新增文件**: 15个 - **新增API端点**: 15个 - **新增页面**: 3个 - **新增组件**: 8个 - **新增数据模型**: 1个(PointsHistory) --- ## 2026-05-24 全面技术文档生成 (详见历史 CHANGELOG 记录)