Files
zhuiguang-ai/.trae/CHANGELOG
T

642 lines
37 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 追光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=<key>` — 单个 bot
- `--force` — 覆盖已存在
- `--use-lunaris-only` — 禁用 SVG 兜底(当前 Lunaris 全是 default,不推荐)
- `--no-update-db` — 只落盘不写 User.avatarUrl
- `--dry-run` — 试运行
- `--delay=<ms>` — 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`,页面仅渲染 `<ReplyForm topicId={topicId} isLocked={topic.isLocked} />`
- **文件**: `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<string, number>`)— `.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<Promise> + 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 记录)