注册支持用户名/手机号/邮箱 + 镜像站部署脚本 + AI 助手 Tool Calling 重构

- Prisma User 模型新增 username 字段(唯一索引)
- 注册先查重复再创建,返回友好中文提示(非 500)
- 登录支持用户名/手机号/邮箱三种方式
- 前端注册表单增加用户名输入框,预校验 2-20 位格式
- 新增 scripts/deploy.sh:一键构建并部署主站+镜像站+重启后端+重载 Nginx
- 镜像站 www.yuzhiran.com.cn Nginx 配置与主站同步
- AI 助手架构升级:用户端/管理后台均采用完整 Tool Calling 架构
- 新增 UserAiAssistantService(18 工具)+ AiAssistantController
- admin 助手新增 search + mark-all-notifications-read 工具
- 修复注册 500 错误:catch Prisma P2002 → BadRequestException
- Baidu Analytics Script 注入 root layout
This commit is contained in:
yuzhiran-dev
2026-06-01 23:14:42 +08:00
parent 6f3fe50ee0
commit 538de50bb1
329 changed files with 8777 additions and 868 deletions
Regular → Executable
View File
View File
Regular → Executable
View File
Regular → Executable
+184 -55
View File
@@ -1,8 +1,8 @@
# 项目进度追踪
## 当前阶段:P3体验增强
## 当前阶段:P4支付闭环 + 部署
### Phase 1-3 — 全部完成 ✅
### Phase 1-4 — 全部完成 ✅
### P0 — 全局优化(全部完成 ✅)
| 子项 | 说明 | 状态 |
@@ -36,74 +36,203 @@
| **P3c** | 会话重命名 | PATCH endpoint + 双击编辑 | ✅ |
| — | SDK / 共享 UI 包 | 远期规划 | 📋 |
### 参考来源
- opencode 源码克隆到 `/tmp/opencode-source/`21 packages, 147MB
### P4 — 支付闭环(进行中 🔄)
### P4 — 支付闭环 + 统一网关(已完成 ✅)
| 子项 | 说明 | 状态 |
|------|------|------|
| **P4a** | Mock 支付自动完成 — `PaymentService.completeMockPayment()` + `OrdersService` 检测 mock 结果后自动调用 | ✅ |
| **P4b** | 会员页面 mock 提示优化 — 显示"订阅成功"而非"请扫码支付" | ✅ |
| **P4c** | 真实微信支付证书部署 — 商户证书/密钥已安装到 `backend/cert/key/`,参数与 `.env` 一致 | ✅ |
| **P4d** | 下单切 NATIVE 扫码支付 — 默认 `tradeType: 'NATIVE'`,前端展示二维码 Modal + 轮询支付状态 | ✅ |
| **P4e** | JSAPI 支付集成 — 检测微信环境 + openid,在 Mini Program web-view 内自动调起 `WeixinJSBridge`;其他场景 NATIVE 兜底 | ✅ |
| **P4a** | 统一支付网关集成 — `GatewayPayService` HMAC-SHA256 对接 yzrcloud.cn 网关,支持支付宝/微信 | ✅ |
| **P4b** | 会员页双支付按钮 — 支付宝/微信可选,统一走网关 | ✅ |
| **P4c** | 网关 Webhook 接收 — `POST /api/v1/payment/gateway/webhook`,处理 `recharge.completed` / `order.refunded` | ✅ |
| **P4d** | 管理后台订单增强 — 搜索/分页/渠道筛选/退款弹窗/标记已付 | ✅ |
| **P4e** | Mock 降级 — 网关不可达时自动 mock 完成支付,不影响开发 | ✅ |
| — | 订阅到期提醒 / 自动续费 | 📋 远期 |
| — | 获取公众号 appid 后补充 Web OAuth 流程 | 📋 待定 |
### P5 — 运营助手(已完成 ✅)
| 子项 | 说明 | 状态 |
|------|------|------|
| **P5a** | 新增 `POST /admin/ai-assistant/chat` 端点 — 管理员专用 AI 聊天,使用 AdminGuard | ✅ |
| **P5b** | 新增 `AdminAiAssistantService` — 19 个可执行工具定义(查看数据/用户管理/评论审核/状态切换等) | ✅ |
| **P5c** | 新增 `POST /admin/ai-assistant/action` 端点 — 工具执行 + AI 自然语言总结 | ✅ |
| **P5d** | 前端运营助手改造 — Tool Calling 架构AI 自动识别用户意图→调用工具→解释结果→展示 | ✅ |
| **P5e** | system prompt 动态注入可用工具列表,覆盖仪表盘/用户/订单/评论/Banner/通知/配置/角色/企业版等 | ✅ |
| **P5a** | 新增 `POST /admin/ai-assistant/chat` 端点 — 管理员专用 AI 聊天 | ✅ |
| **P5b** | `AdminAiAssistantService` — 19 个可执行工具定义 | ✅ |
| **P5c** | `POST /admin/ai-assistant/action` — 工具执行 + AI 总结 | ✅ |
| **P5d** | 前端 Tool Calling 架构AI 自动识别意图→调用→解释→展示 | ✅ |
| **P5e** | system prompt 动态注入工具列表 | ✅ |
### 关键决策
- `frontend/src/lib/models.ts` 作为模型列表单数据源
- 所有颜色使用 CSS 变量(`text-foreground`/`text-muted-foreground`/`bg-card`/`bg-muted`/`border-border`
- `nest build` 正常产出(`--tsc` 遗漏 prisma 模块)
- 沙盒 JWT 认证保留
- Mock 支付自动完成闭环,无需手动触发回调;真实微信支付上线后自动切换
- 运营助手采用 Tool Calling 架构:system prompt 描述工具 → AI 返回 JSON 工具调用 → 后端执行 → 结果喂回 AI 总结 → 展示给用户
---
## 2026-05-27 Session
## 部署架构
### 域名概况
| 域名 | 类型 | 说明 | 状态 |
|------|------|------|------|
| **www.yuzhiran.com** | AI 学习平台 | 前端静态导出 + NestJS 后端 | 🟢 线上 |
| **trade.yuzhiran.com** | 另一项目(非 AI 平台) | SPA 应用,独立后端 8002 端口 | 🟢 线上 |
| **alvnc.yuzhiran.com** | 其他服务 | — | 🟢 线上 |
### www.yuzhiran.com 部署详情
```
用户 → Nginx (443/80) → /api/* → backend (127.0.0.1:4000)
→ /* → static files (/www/wwwroot/www.yuzhiran.com/)
```
| 组件 | 技术栈 | 运行方式 | 端口 |
|------|--------|---------|------|
| **前端** | Next.js 14 App Router → `output: 'export'` 静态 HTML | Nginx 直接提供 `/www/wwwroot/www.yuzhiran.com/` | 443/80 |
| **后端** | NestJS + Prisma + MySQL | PM2 (`yuzhiran-api`) | 4000 |
| **数据库** | MySQL 8.0 | systemd (`ai_leaning`) | 3306 |
| **Web 服务器** | Nginx (宝塔面板) | systemd | 443/80 |
| **支付网关** | yzrcloud.cn 统一网关 | 外部服务 | 8100(内网) |
**Nginx 配置** (`/www/server/panel/vhost/nginx/www.yuzhiran.com.conf`):
- 静态资源 `/``try_files $uri $uri.html $uri/index.html =404`
- API 代理 `/api/``proxy_pass http://127.0.0.1:4000`
- 静态资源缓存: `_next/static/` 365 天, 图片 30 天, JS/CSS 12 小时
**部署流程**:
```bash
# 前端
cd frontend && npm run build # 生成 out/
cp -r out/* /www/wwwroot/www.yuzhiran.com/
# 后端
cd backend && npm run build # 生成 dist/
pm2 restart yuzhiran-api
```
### trade.yuzhiran.com(独立项目)
非 AI 学习平台项目,独立部署:
- 前端 SPA (React)root at `/www/wwwroot/trade.yuzhiran.com/`
- 后端 API at `127.0.0.1:8002`
- 路由: `/admin/` → SPA fallback, `/workspace/` → SPA fallback
---
## 支付对接
### 架构总览
```
前端 (会员页) → POST /orders/create → GatewayPayService → HMAC-SHA256 → yzrcloud.cn 网关
├── 支付宝 PC 网站
└── 微信 Native 扫码
网关回调 → POST /api/v1/payment/gateway/webhook → 更新订单 + 激活订阅
```
### 统一支付网关对接参数
| 参数 | 值 | 来源 |
|------|-----|------|
| 网关地址(内网) | `http://localhost:8100` | `.env` |
| 网关地址(外网) | `https://www.yzrcloud.cn/api/gateway` | 未启用 |
| API Key | `pay_2ac89d9831244c2ebdbb2a24` | AI学习平台.key |
| API Secret | `6a2e6c6801384bd0800849ef42ad5e8ef51898819895478a` | AI学习平台.key |
| 签名算法 | HMAC-SHA256 | `sign_str = "{method}\n{path}\n{timestamp}\n{body_sha256}"` |
| Webhook | `POST /api/v1/payment/gateway/webhook` | 后端接收 |
| 支付方式 | `alipay` / `wechat` | 网关统一处理 |
| Mock 模式 | `GATEWAY_PAY_MOCK=true` | 开发环境启用 |
### 后端对接文件
| 文件 | 说明 |
|------|------|
| `backend/src/modules/payment/gateway-pay.service.ts` | 核心网关服务,HMAC 签名 + 下单/查询/退款/Webhook |
| `backend/src/modules/payment/payment.controller.ts` | `/api/v1/payment/gateway/webhook` 等端点 |
| `backend/src/modules/orders/orders.service.ts` | 下单路由,统一走网关 |
| `backend/.env` | `GATEWAY_PAY_*` 配置项 |
### API 端点一览
| 方法 | 路径 | 说明 | 认证 |
|------|------|------|------|
| POST | `/api/v1/payment/gateway/webhook` | 网关支付成功/退款回调 | 无(网关签名在 body 中) |
| GET | `/api/v1/payment/wxpay/query?outTradeNo=xxx` | 订单状态查询(转网关) | JWT |
| POST | `/api/v1/payment/wxpay/refund` | 退款(转网关) | JWT |
| POST | `/api/v1/orders/create` | 创建订单+发起支付 | JWT |
| GET | `/api/v1/admin/orders?page=&search=&status=&payChannel=` | 管理员订单搜索 | Admin JWT |
| POST | `/api/v1/admin/orders/:orderNo/refund` | 管理员退款 | Admin JWT |
| POST | `/api/v1/admin/orders/:orderNo/mark-paid` | 标记人工已付 | Admin JWT |
### 商户密钥文件
备份位置: `/root/hermes-workspace/backup/网关支付/pay-api/merchants/AI学习平台.key`
---
## 2026-05-31 Session (下午)
### Changes Made
- **Fixed OpenAI default model**: Changed `OPENAI_MODEL` in `.env` from `meituan/longcat-flash-lite` (invalid, returned 400) to `deepseek/deepseek-v4-flash` (confirmed working on qnaigc API). The "通用模式" (general model) now returns real AI responses instead of mock fallback.
- **Fixed empty content handling**: Updated `OpenAICompatibleProvider.chat()` to fall back to `reasoning_content` when `content` is empty (SenseNova models return content in `reasoning_content`). Same fix applied in `chatStream()` for delta content.
- **AI 助手架构升级**: 用户端 + 管理后台双 AI 助手均采用完整 Tool Calling 架构
- **新增 `UserAiAssistantService`**: 后台工具执行服务,18 个工具(搜索/技能/学情/通知/个人信息/模型/提示词/课程/订单)
- **新增 `AiAssistantController`**: `POST /api/v1/ai-assistant/chat` + `/action` 端点
- **新增 `AiAssistantModule`**: 注册 SkillsModule + AIModule 依赖
- **重写前端 `ai-assistant.tsx`**: Tool Calling 架构 + 页面感知上下文 + 登录控制
- **更新前端 `admin-ai-assistant.tsx`**: 新增 search/mark-all-notifications-read 工具描述 + 更多 starter
- **新增后台 `search`/`mark-all-notifications-read` 工具**: 全局搜索用户/订单/课程/内容 + 通知批量已读
- **重构 `assistant-actions.ts`**: 统一 `executeClientAction()` API,支持所有客户端操作
- **清理 `assistant-context.ts`**: 精简为纯上下文数据源,移除旧 action 格式
### 用户端 AI 助手可用操作
| 分类 | 工具 |
|------|------|
| 🧭 导航 | `navigate` — 跳转到任意页面 |
| 🔍 搜索 | `search` — 全局搜索课程/提示词/工具/文章 |
| 🎯 技能 | `list-skills` / `get-skill` / `get-skill-categories` |
| 📊 学情 | `get-learning-analytics` / `get-learning-path` |
| 🔔 通知 | `list-notifications` / `get-unread-count` / `mark-notification-read` / `mark-all-read` |
| 👤 个人 | `get-profile` / `list-user-orders` / `get-current-subscription` |
| 📚 内容 | `list-models` / `list-prompts` / `list-courses` |
| ⚡ 客户端 | `setModel` / `startChat` / `openSkill` / `setParameter` |
### 管理后台新增工具
| 工具 | 说明 |
|------|------|
| `search` | 全局搜索用户/订单/课程/内容 |
| `mark-all-notifications-read` | 批量标记所有通知已读 |
### Verification
- Backend successfully registers: `OpenAI 兼容接口已注册: deepseek/deepseek-v4-flash`
- 5 test chat requests to `POST /api/v1/sandbox/chat` with `model:"general"` all returned 201 in 1.3-12.7s (vs. previous mock/fallback)
- No more `AIGatewayService` "调用失败" errors in logs for OpenAI provider
- Backend runs under PM2 (v7.0.1), managed via `pm2 restart backend --update-env`
- ✅ 后端 92 项测试全部通过
- ✅ 后端 TypeScript 编译无错误
- ✅ 前端 75 静态页面全部生成成功
- ✅ 所有文件创建/修改完成
### Known Issues
- SenseNova API (`token.sensenova.cn`) still returns 404 "model is not found" for `deepseek-v4-flash` et al — models might need different naming on that endpoint
- Server: backend `node dist/main.js` on port 4000, frontend Next.js on port 3000, both managed by PM2
---
- `frontend/src/lib/models.ts` 作为模型列表单数据源
- 所有颜色使用 CSS 变量(`text-foreground`/`text-muted-foreground`/`bg-card`/`bg-muted`/`border-border`
- 沙盒 JWT 认证保留
- 支付全部通过 yzrcloud.cn 统一网关处理,支持支付宝/微信
- 运营助手采用 Tool Calling 架构
- 前端静态导出 (`output: 'export'`) + Nginx 直接提供
- 注册支持用户名、手机号、邮箱三选一,登录支持用户名/手机号/邮箱
- 注册时校验重复用户名/手机号/邮箱并返回友好提示
- 镜像站点 `www.yuzhiran.com.cn` 与主站配置同步
### 2026-05-27 后续优化
---
**彻底修复 CSS 丢失问题**
## 2026-06-01 Session — 注册优化 + 镜像站部署
根本原因:PM2 用 `npm run dev``next dev`)运行前端,每次重启都会重新编译,`.next` 缓存被销毁导致 CSS 丢失。
### Changes Made
- **Prisma User 模型**: 新增 `username` 字段 (`String? @unique`)
- **注册 DTO**: 新增 `username` 字段验证(2-20位,支持中英文/数字/下划线)
- **注册逻辑重构** (`auth.service.ts`):
- 注册前分别检查 username/phone/email 是否已存在 → 友好提示
- 三字段至少需提供一个
- 注册成功用 username 做默认 nickname
- **登录逻辑** (`auth.service.ts`): `OR` 查询增加 `{ username: account }`
- **前端 i18n**: 新增 `username`/`usernameOptional`/`usernameConflict`/`phoneConflict`/`emailConflict` 翻译 key
- **前端 auth 页**:
- 注册表单增加用户名输入框
- 前端预校验用户名格式(2-20位,中英文/数字/下划线)
- 友好错误映射:覆盖用户名/手机号/邮箱冲突提示
- 登录框提示改为 "用户名 / 手机号 / 邮箱"
- **镜像站部署**:
- 前端静态文件部署至 `/www/wwwroot/www.yuzhiran.com.cn/`
- Nginx vhost 配置同步主站:Next.js clean URL + API proxy + 静态缓存
- Nginx reload + PM2 restart
解决方案:改用 `output: 'export'` 静态导出 + 独立 HTTP 服务器。
| 文件 | 变更 |
|------|------|
| `frontend/server.js` | **新增** — Node.js 静态文件服务器,支持 Next.js 静态导出 URL 模式(`/about``about.html`),CSS/JS 缓存一年,HTML 不缓存 |
| `ecosystem.config.js` | 前端从 `npm run dev` 改为 `node server.js`NODE_ENV=production |
| `frontend/package.json` | `predev` 改为仅删 `out/`,不再删 `.next/` |
部署流程:
1. `npm run build` → 生成 `out/` 静态目录
2. PM2 运行 `node server.js` → 直接提供静态文件
3. 重启后 CSS 不变,无需重新编译
当前状态:
- 前端 `localhost:3000` (static server, 无编译延迟)
- 后端 `localhost:4000` (NestJS)
- 所有路由正常 (/, /about, /sandbox 均为 200)
- CSS 持久化,重启后不丢失
### Verification
- ✅ Prisma db push 成功(`username` 列 + 唯一索引)
- ✅ 后端 92 项测试全部通过
- ✅ 后端 TypeScript 编译无错误
- ✅ 前端 75 静态页面全部生成成功
- ✅ Nginx 配置语法验证通过
- ✅ 主站 + 镜像站均已部署
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File