chore: 整理项目结构,清理归档文件,更新部署配置

This commit is contained in:
Simon
2026-07-04 21:01:39 +08:00
parent 8bd4ab0366
commit 64ff1bf7d5
508 changed files with 43575 additions and 14129 deletions
+172
View File
@@ -0,0 +1,172 @@
# 文档索引
> **版本**: v2.0 | **日期**: 2026-07-04 | **维护人**: 助理
> **说明**: 本索引提供 docs/ 目录下所有文档的快速导航
---
## 📚 文档目录结构
```
docs/
├── 01-项目总览/ ← 核心文档(必读)
├── 02-产品需求/ ← PRD、功能需求
├── 03-技术架构/ ← 架构设计、ADR、图表
├── 04-原型设计/ ← UI/UX原型
├── 05-用户手册/ ← 用户指南
├── 06-测试质量/ ← 测试文档
├── 07-代码评审/ ← Code Review
├── 08-安全审计/ ← 安全、集成分析
├── 09-部署运维/ ← 部署、运维、故障排查
├── 10-项目管理/ ← SOP、项目管理
└── 11-历史归档/ ← 历史归档
```
---
## 📋 核心文档(必读)
| 文档 | 路径 | 说明 |
|------|------|------|
| 项目总览与部署手册 | `01-项目总览/01-项目总览与部署手册-20260704.md` | 项目背景、架构、三阶段演进 |
| 智能IT服务系统运维手册 | `01-项目总览/01-智能IT服务系统运维手册-20260704.md` | 部署、运维、故障处理 |
| 项目知识库 | `01-项目总览/01-项目知识库-20260704.md` | 全量知识库汇总 |
| 资源申请清单 | `01-项目总览/01-资源申请清单-20260704.md` | IT 资源申请记录 |
---
## 📂 各目录文档清单
### 01-项目总览/
| 文档 | 说明 |
|------|------|
| `00-索引-20260704.md` | 本索引文档 |
| `01-项目总览与部署手册-20260704.md` | v2.2,项目完整概览 |
| `01-智能IT服务系统运维手册-20260704.md` | v1.0,统一运维手册 |
| `01-资源申请清单-20260704.md` | IT 资源申请记录 |
| `01-项目知识库-20260704.md` | 项目全量知识库 |
| `01-IT智能服务台_项目汇报-20260704.html` | 项目汇报演示 |
| `02-项目迁移文档-20260606.md` | 项目迁移文档 |
| `03-项目全面评估报告-20260625.md` | 项目全面评估报告 |
### 02-产品需求/
| 文档 | 说明 |
|------|------|
| `02-产品需求文档PRD-v1.2-20260704.md` | 主 PRD 文档(v1.22026-07-04更新) |
| `product-产品/v0.7.2-backlog-candidate-2026-06-24.md` | 需求候选池 |
### 03-技术架构/
| 目录/文档 | 说明 |
|------|------|
| `00-系统架构设计文档-v1.0.md` | 系统架构设计(v1.0 |
| `01-ADRs-架构决策/` | 4 | 架构决策记录 |
| `02-技术方案/` | 5 | 技术方案文档 |
| `03-技术分析/` | 3 | 技术分析报告 |
| `04-数据库设计/` | 1 | 数据库设计 |
| `05-架构图/` | 9 | Mermaid 图表 |
### 04-原型设计/
| 子目录 | 文档数 | 说明 |
|--------|--------|------|
| `prototypes-原型图/` | 12 | 活跃原型图 |
| `prototypes-原型图/archive/` | 17 | 历史版本 |
### 05-用户手册/
> 待添加用户手册
### 06-测试质量/
| 文档 | 说明 |
|------|------|
| `03-调试验证指南-20260613.md` | 调试验证指南 |
| `testing-测试/E2E-CHECKLIST-v0.7.0.md` | E2E 验收清单 |
| `testing-测试/TESTING_CALL_AGENT.md` | 呼叫坐席测试 |
| `testing-测试/QA_COMPREHENSIVE_REPORT.md` | QA 综合报告 |
### 07-代码评审/
| 文档 | 说明 |
|------|------|
| `评审报告-代码评审/workbuddy-2026-06-14-P0安全.md` | P0 安全评审 |
| `评审报告-代码评审/workbuddy-2026-06-14-消息优化.md` | 消息优化评审 |
| `评审报告-代码评审/workbuddy-2026-06-14-消息优化-P1二次评审.md` | 消息优化二次评审 |
| `评审报告-代码评审/workbuddy-2026-06-14-Gitea重建.md` | Gitea 重建评审 |
| `评审报告-代码评审/workbuddy-2026-06-14-预检验证.md` | 预检验证评审 |
| `评审报告-代码评审/workbuddy-2026-06-15-T组A组.md` | T组A组评审 |
| `评审报告-代码评审/REVIEW_B_T10-消息可靠性增强.md` | B-T10 消息可靠性评审 |
### 08-安全审计/
| 子目录 | 文档数 | 说明 |
|--------|--------|------|
| `审计报告-安全审计/` | 3 | 安全审计报告 |
| `集成分析-外部系统/` | 3 | 外部系统集成分析 |
### 09-部署运维/
| 文档 | 说明 |
|------|------|
| `deploy/03-RELEASE-NOTES-v0.7.1-20260623.md` | v0.7.1 发布说明 |
| `deploy/04-部署修复记录-20260613.md` | 部署修复记录 |
| `deploy/05-版本更新说明-v1.1.0-20260614.md` | 版本更新说明 v1.1.0 |
| `deploy/06-OTP二次验证实现.md` | OTP 二次验证实现 |
| `deploy/07-扫码登录OTP部署指南-v0.7.0.md` | 扫码登录OTP部署指南 |
| `deploy/08-NAS部署指南-预生产.md` | NAS部署指南(预生产) |
| `deploy/09-NAS部署指南-群晖Cloudflare.md` | NAS部署指南(群晖) |
| `deploy/10-一键部署操作包-v0.7.0.md` | 一键部署操作包 |
| `deploy/` | 7 | 部署文档 |
| `troubleshooting-故障排查/` | 1 | 故障排查 |
| `guides-用户指南/` | 1 | 用户指南 |
### 10-项目管理/
| 编号 | 文档 | 说明 |
|------|------|------|
| 01 | `01-任务总索引.md` | 任务管理文档体系总览 |
| 02 | `02-风险跟踪表.md` | 风险登记册(22项) |
| 03 | `03-项目任务状态报告.md` | 历史全量任务(152个) |
| 04 | `04-项目开发任务调整建议.md` | 任务调整建议 |
| 05 | `05-项目状态看板/01-项目状态看板.md` | 驾驶舱仪表盘 |
| SOPs | `SOPs-标准流程/` | 4项标准操作流程 |
### 11-历史归档/
> 历史归档文档,包含 `-archived-日期` 后缀
---
## 🔗 快速链接
| 场景 | 推荐文档 |
|------|---------|
| 新人入职 | `01-项目总览/01-项目总览与部署手册-20260704.md` |
| 部署上线 | `01-项目总览/01-智能IT服务系统运维手册-20260704.md` |
| 故障排查 | `09-部署运维/troubleshooting-故障排查/` |
| 代码评审 | `07-代码评审/评审报告-代码评审/` |
| 安全合规 | `08-安全审计/` |
| 运维操作 | `10-项目管理/SOPs-标准流程/` |
---
## 📝 文档更新规则
| 更新类型 | 同步要求 |
|---------|---------|
| 新增文档 | 添加到对应目录 + 本索引 |
| 删除文档 | 从本索引移除 |
| 移动文档 | 更新本索引路径 |
| 重大变更 | 同步更新 `CHANGELOG.md` |
---
## 📅 版本历史
| 版本 | 日期 | 变更 |
|------|------|------|
| v2.0 | 2026-07-04 | 目录结构重组,完成重命名 |
| v1.0 | 2026-07-04 | 初始版本,索引创建 |
@@ -703,9 +703,8 @@
<h3>🖥️ 基础设施</h3>
<table>
<tr><th>资源</th><th>用途</th><th>状态</th></tr>
<tr><td>NAS群晖 DS923plus</td><td>生产环境Docker容器</td><td><span class="tag tag-green">已有</span></td></tr>
<tr><td>Cloudflare Tunnel</td><td>外网访问(免公网IP</td><td><span class="tag tag-green">已配置</span></td></tr>
<tr><td>itdesk.amanzac.com</td><td>阶段一测试域名</td><td><span class="tag tag-green">已配置</span></td></tr>
<tr><td>公司内网服务器</td><td>生产环境Docker容器</td><td><span class="tag tag-green">已有</span></td></tr>
<tr><td>WAF/域名</td><td>外网访问</td><td><span class="tag tag-green">已配置</span></td></tr>
<tr><td>itsupport.servyou.com.cn</td><td>正式生产域名</td><td><span class="tag tag-amber">待申请</span></td></tr>
<tr><td>G端服务器 10.80.0.129</td><td>预生产环境</td><td><span class="tag tag-green">已有</span></td></tr>
</table>
@@ -732,7 +731,7 @@
</div>
<div class="callout success" style="margin-top:16px;">
<strong>资源优势</strong>阶段一无需额外服务器采购(NAS已有)、无需额外AI基础设施投入(复用现有)、无需新增编制(1人全栈+AI辅助),启动成本极低。
<strong>资源优势</strong>利用现有服务器资源、无需额外AI基础设施投入(复用现有)、无需新增编制(1人全栈+AI辅助),启动成本极低。
</div>
</div>
@@ -758,12 +757,12 @@
<div class="risk-item">
<div class="risk-level risk-mid"></div>
<div>
<strong>NAS部署稳定性</strong><br>
<span style="color:var(--text-dim);font-size:0.9em;">NAS为非标准服务器,Docker资源有限,高并发可能性能不足</span>
<strong>服务器资源限制</strong><br>
<span style="color:var(--text-dim);font-size:0.9em;">内网服务器资源有限,需关注高并发场景下的性能表现</span>
</div>
<div>
<strong style="color:var(--green);">应对</strong><br>
<span style="color:var(--text-dim);font-size:0.9em;">① 阶段一仅支持少量坐席(2-5人),NAS足够 ② 预留G端服务器作为备选 ③ 监控资源使用率</span>
<span style="color:var(--text-dim);font-size:0.9em;">① 阶段一仅支持少量坐席(2-5人)② 预留扩展方案 ③ 监控资源使用率</span>
</div>
</div>
<div class="risk-item">
@@ -873,7 +872,7 @@
<td><strong>数据自主</strong></td>
<td style="color:var(--red);">❌ 数据在SaaS平台</td>
<td style="color:var(--red);">❌ 数据在SaaS平台</td>
<td style="color:var(--green);">✅ 全部本地NAS</td>
<td style="color:var(--green);">✅ 全部本地部署</td>
</tr>
<tr>
<td><strong>定制灵活性</strong></td>
@@ -0,0 +1,495 @@
# 智能IT服务系统运维手册
> **版本**: v1.0 | **日期**: 2026-07-04 | **维护人**: 助理(小米)
> **目标读者**: 运维工程师 / IT支持组
> **📖 关联文档**:
> - [README.md](../README.md) — 项目快速入门
> - [01-项目总览与部署手册.md](./01-项目总览与部署手册.md) — 完整架构设计
> - [CHANGELOG.md](../CHANGELOG.md) — 版本变更概览
> - [docs/archive/](./archive/) — 历史版本详情
---
## 目录
1. [系统概述](#一系统概述)
2. [环境信息](#二环境信息)
3. [部署操作](#三部署操作)
4. [日常运维](#四日常运维)
5. [故障排查](#五故障排查)
6. [回滚方案](#六回滚方案)
7. [备份恢复](#七备份恢复)
8. [应急响应](#八应急响应)
---
## 一、系统概述
### 1.1 系统架构
```
浏览器 ──→ itsupport.servyou.com.cn:443
┌─── nginx (容器) ───────────────┐
│ │
│ /itdesk/* → H5 员工端 SPA │
│ /itagent/* → 坐席工作台 SPA │
│ /itadmin/* → 管理后台 SPA │
│ /itportal/* → Portal 选择页 │
│ /api/* → backend:8000 │
│ /ws/* → backend:8000 (WS)│
│ │
└──────────────┬───────────────────┘
│ 本机 Docker 网络
┌─────────────┼─────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ backend │ │ postgres │ │ redis │
│ :8000 │ │ :5432 │ │ :6379 │
└──────────┘ └──────────┘ └──────────┘
```
### 1.2 组件清单
| 服务 | 镜像 | 端口 | 说明 |
|------|------|------|------|
| nginx | nginx:alpine | 443→80 (对外) | 反向代理 + SSL |
| backend | 自构建 | 8000 (内部) | FastAPI 后端 |
| postgres | postgres:16 | 5432 (内部) | 数据库 |
| redis | redis:7 | 6379 (内部) | 缓存 + Session |
### 1.3 访问端点
| 端点 | 说明 |
|------|------|
| `https://itsupport.servyou.com.cn/itdesk/` | H5 员工端 |
| `https://itsupport.servyou.com.cn/itagent/` | 坐席工作台 |
| `https://itsupport.servyou.com.cn/itadmin/` | 管理后台 |
| `https://itsupport.servyou.com.cn/itportal/` | Portal 角色选择 |
| `https://itsupport.servyou.com.cn/api/docs` | API Swagger 文档 |
---
## 二、环境信息
### 2.1 服务器信息
| 环境 | IP | 域名 | 用途 |
|------|-----|------|------|
| 生产 | 10.90.5.110 (内网) | itsupport.servyou.com.cn | 正式环境 |
| 运维入口 | 10.212.189.210:2222 | - | 堡垒机 SSH |
### 2.2 关键配置
| 配置项 | 值 |
|--------|-----|
| 企微 CorpID | `ww...` (见 .env) |
| 企微 AgentID | `1000xxx` |
| 数据库 | PostgreSQL 16 |
| 缓存 | Redis 7 |
| 域名证书 | `*.servyou.com.cn` (GeoTrust/DigiCert) |
### 2.3 部署路径
```
/opt/wecom-it-desk/
├── docker-compose.yml
├── .env # 环境变量(不提交 Git)
├── backend/ # 后端代码
├── frontend-h5/dist/ # H5 前端构建产物
├── frontend-agent/dist/ # 坐席前端构建产物
├── frontend-admin/dist/ # 管理后台构建产物
├── frontend-portal/dist/ # Portal 构建产物
├── nginx/ # Nginx 配置
└── logs/ # 日志目录
```
---
## 三、部署操作
### 3.1 部署流程概览
```
1. 打包代码 → 2. 上传服务器 → 3. 配置环境变量 → 4. 启动容器 → 5. 验证
```
### 3.2 打包命令(本地)
```bash
# 在项目根目录执行
cd D:\资\03-项目开发\wecom_it_smart_desk
# 使用部署脚本打包
powershell -File deploy-server\build-package.ps1
# 或手动打包
tar czf deploy.tar.gz \
backend/ frontend-h5/dist/ frontend-agent/dist/ \
frontend-admin/dist/ frontend-portal/dist/ \
nginx/ docker-compose.yml .env.production scripts/
```
### 3.3 上传到服务器
> **注意**: 公司服务器只能通过堡垒机上传,无法直接从本地 scp
1. **通过堡垒机上传到 `/tmp/`**
- 使用 SFTP 或 Web 界面上传到堡垒机
2. **SSH 登录服务器**
```bash
# 堡垒机: sxn@10.212.189.210:2222
ssh sxn@10.90.5.110 # 跳转目标服务器
```
3. **移动到目标目录**
```bash
mv /tmp/deploy.tar.gz /opt/wecom-it-desk/
cd /opt/wecom-it-desk/
tar xzf deploy.tar.gz
```
### 3.4 配置环境变量
```bash
# 创建环境配置
cp .env.production .env
vim .env # 编辑真实配置
# 必填项:
# - WECOM_CORP_ID
# - WECOM_AGENT_ID
# - WECOM_SECRET
# - WECOM_TOKEN
# - WECOM_ENCODING_AES_KEY
# - POSTGRES_PASSWORD
# - REDIS_PASSWORD
```
### 3.5 启动服务
```bash
# 启动所有容器
docker compose up -d --build
# 或分步启动
docker compose up -d postgres redis # 先启动基础服务
docker compose up -d backend # 再启动后端
docker compose up -d nginx # 最后启动前端
```
### 3.6 验证部署
```bash
# 1. 检查容器状态
docker compose ps
# 预期:4 个容器全部 Up/healthy
# 2. 健康检查
curl -ksI https://itsupport.servyou.com.cn/api/health
# 3. 各端点验证
curl -ksI https://itsupport.servyou.com.cn/itdesk/
curl -ksI https://itsupport.servyou.com.cn/itagent/
curl -ksI https://itsupport.servyou.com.cn/itadmin/
curl -ksI https://itsupport.servyou.com.cn/itportal/
```
---
## 四、日常运维
### 4.1 日常检查
```bash
# 每日必做检查
docker compose ps # 容器状态
docker compose logs --tail=50 backend # 后端日志
docker compose logs --tail=50 nginx # 前端日志
df -h # 磁盘空间
```
### 4.2 常用操作
| 操作 | 命令 |
|------|------|
| 重启后端 | `docker compose restart backend` |
| 重启 nginx | `docker compose restart nginx` |
| 查看实时日志 | `docker compose logs -f backend` |
| 进入后端容器 | `docker compose exec backend bash` |
| 查看容器资源 | `docker stats` |
### 4.3 监控指标
| 指标 | 阈值 | 说明 |
|------|------|------|
| CPU 使用率 | < 80% | 主机层面 |
| 内存使用率 | < 80% | 主机层面 |
| 磁盘使用率 | < 70% | 主机层面 |
| 容器状态 | 全部 Up | docker compose ps |
| API 响应时间 | P95 < 500ms | 业务层面 |
### 4.4 日志位置
| 服务 | 日志命令 |
|------|----------|
| 后端 | `docker compose logs backend` |
| Nginx | `docker compose logs nginx` |
| PostgreSQL | `docker compose logs postgres` |
| Redis | `docker compose logs redis` |
---
## 五、故障排查
### 5.1 快速诊断流程
```
1. 检查容器状态 → 2. 检查端口连通 → 3. 检查日志 → 4. 定位根因
```
### 5.2 常见问题
#### 问题 1: 访问返回 500 错误
```bash
# 1. 检查容器状态
docker compose ps
# 2. 检查后端日志
docker compose logs --tail=100 backend
# 3. 检查 nginx 日志
docker compose logs --tail=100 nginx
# 4. 检查前端 dist 是否存在
ls /opt/wecom-it-desk/frontend-h5/dist/
docker compose exec nginx ls /usr/share/nginx/html/itdesk/
```
#### 问题 2: API 返回连接错误
```bash
# 1. 检查后端是否启动
docker compose ps backend
# 2. 检查后端健康端点
curl http://localhost:8000/health
# 3. 检查数据库连接
docker compose exec backend python -c "from app.database import get_db; print('OK')"
```
#### 问题 3: WebSocket 连接失败
```bash
# 1. 检查 nginx WebSocket 配置
docker compose exec nginx cat /etc/nginx/nginx.conf | grep -A10 ws
# 2. 检查 WS 端点
curl -I http://localhost:8000/ws/test
# 3. 检查 RedisWS 依赖)
docker compose exec redis redis-cli ping
```
#### 问题 4: 企微工作台打开页面显示"加载失败"或无限加载(2026-07-04
**现象**:员工通过企业微信-工作台-IT支持服务访问,显示加载失败或一直转圈
**排查步骤**
```bash
# 1. 检查容器状态
docker ps
# 2. 检查 nginx 是否正确加载配置
docker exec wecom_it_nginx nginx -t
# 3. 检查前端页面访问
curl -I http://localhost/itdesk/
# 4. 检查 API 代理
curl -I http://localhost/api/h5/health
# 5. 查看后端日志(查找 NameError)
docker logs --tail=50 wecom_it_backend | grep -i error
```
**根因 1**nginx 容器未正确挂载 nginx.conf 配置文件
- 表现:API 请求返回 404,nginx 错误日志显示 `open() "/usr/share/nginx/html/api/xxx" failed`
- 解决:重建 nginx 容器,确保正确挂载配置
```bash
# 重建 nginx 容器
docker rm -f wecom_it_nginx
docker run -d --name wecom_it_nginx \
--network wecom-it-desk_it-desk-internal \
-p 80:80 -p 443:443 \
-v /opt/wecom-it-desk/html:/usr/share/nginx/html:ro \
-v /opt/wecom-it-desk/nginx/nginx.conf:/etc/nginx/nginx.conf:ro \
-v /opt/wecom-it-desk/nginx/ssl:/etc/nginx/ssl:rw \
--restart unless-stopped nginx:1.27-alpine
# 重新加载配置
docker exec wecom_it_nginx nginx -s reload
```
**根因 2**:后端 h5.py 代码存在 NameError
- 表现:后端日志显示 `NameError: name '_require_wework_ua' is not defined`
- 原因:生产服务器代码未同步最新版本
- 解决:复制最新代码并重启后端
```bash
# 复制最新代码
docker cp /opt/wecom-it-desk/backend/app/api/h5.py wecom_it_backend:/app/app/api/h5.py
# 重启后端
docker restart wecom_it_backend
```
### 5.3 完整诊断脚本
详细诊断脚本见:`docs/deploy/服务器端跑诊断.md`
```bash
# 一键诊断
bash /opt/wecom-it-desk/diagnose-500.sh
```
---
## 六、回滚方案
### 6.1 快速回滚
```bash
# 停止当前版本
docker compose down
# 恢复上一个版本(需提前备份)
# 方法1: 从 Git 拉取上一个 commit
git checkout {上一个commit-hash}
# 重新构建部署
# 方法2: 保留上一个版本的部署包
cd /opt/wecom-it-desk-backup
tar xzf deploy-v0.x.x.tar.gz
docker compose up -d
```
### 6.2 回滚检查清单
- [ ] 确认上一个版本可用
- [ ] 通知相关人员
- [ ] 记录当前版本问题
- [ ] 执行回滚
- [ ] 验证回滚后功能正常
- [ ] 发送回滚通知
---
## 七、备份恢复
### 7.1 备份策略
| 备份对象 | 方法 | 频率 | 保留 |
|---------|------|------|------|
| PostgreSQL | pg_dump | 每日凌晨 | 7 天 |
| Redis | redis-cli SAVE | 每日凌晨 | 7 天 |
| 配置文件 | tar 归档 | 每次部署 | 4 个版本 |
| 日志文件 | logrotate | 每周 | 4 周 |
### 7.2 备份命令
```bash
# 备份数据库
docker compose exec postgres pg_dump -U postgres wecom_it > /tmp/backup_$(date +%Y%m%d).sql
# 备份 Redis
docker compose exec redis redis-cli SAVE
cp /var/lib/docker/volumes/wecom-it-desk_redis_data/_data/dump.rdb /tmp/redis_$(date +%Y%m%d).rdb
# 备份配置
tar czf /tmp/config_$(date +%Y%m%d).tar.gz /opt/wecom-it-desk/.env /opt/wecom-it-desk/nginx/
```
### 7.3 恢复命令
```bash
# 恢复数据库
docker compose exec -T postgres psql -U postgres wecom_it < backup_20260701.sql
# 恢复 Redis
docker compose exec -T redis redis-cli FLUSHALL
# 停止服务后复制 dump.rdb 到数据目录
```
---
## 八、应急响应
### 8.1 事件分级
| 等级 | 场景 | 响应时间 |
|------|------|----------|
| 🔴 P0 | 鉴权漏洞 / 数据泄露 / 服务全停 | 5 min |
| 🟠 P1 | 功能故障 / 单服务降级 | 30 min |
| 🟡 P2 | 性能问题 / UI 异常 | 4 h |
| 🟢 P3 | 体验优化 | 1 周 |
### 8.2 P0 应急流程
#### 立即止血
```bash
# 1. 关闭外网访问
sudo iptables -A INPUT -p tcp --dport 443 -j DROP
# 2. 停可疑服务
docker compose stop backend
# 3. 保留现场(不删文件)
docker compose logs backend > /tmp/incident-backend.log
docker compose logs nginx > /tmp/incident-nginx.log
```
#### 通知
- 微信/电话通知项目负责人
- 邮件通知:`wecom-it-desk-incident@servyou-it.com`
### 8.3 应急联系
| 角色 | 联系人 |
|------|--------|
| 项目负责人 | 宋献 |
| 运维 | IT 支持组 |
| 企微技术支持 | 企微客服 |
---
## 附录
### 版本历史
| 版本 | 日期 | 更新内容 |
|------|------|----------|
| v1.0 | 2026-07-04 | 初始版本,整合部署/运维/故障排查文档 |
### 相关文档
| 文档 | 说明 |
|------|------|
| `docs/01-项目总览与部署手册.md` | 完整项目背景与架构设计 |
| `docs/RELEASE_NOTES_v0.7.1.md` | 版本发布说明 |
| `docs/SOPs/SOP-004-应急响应.md` | 详细应急响应流程 |
| `docs/deploy/快速诊断-500-错误.md` | 500 错误排查指南 |
| `docs/IT服务台部署修复记录-2026-06-13.md` | 历史修复记录 |
---
> **维护说明**: 本文档由助理(小米)维护,随每次发布更新。
> 如有更新,请同步更新本文档的版本号和日期。
@@ -23,7 +23,7 @@
| 环境 | 用途 | 部署位置 | 访问方式 |
|------|------|---------|---------|
| **预生产** | 内网功能验证 | G端服务器 `10.80.0.129` | 公司内网域名 |
| **生产NAS** | 企微端实际使用 | 群晖 NAS `192.168.3.200` | Cloudflare Tunnel 公网域名 |
| **生产** | 企微端实际使用 | 公司内网服务器 `10.90.5.110` | WAF/域名直连 |
---
@@ -41,16 +41,16 @@
| **现有域名** | `it-dataquery.dc.servyou-it.com`(与数据查询平台共用) |
| **状态** | ✅ 已部署运行 |
### 2.2 生产环境 — 群晖 NAS
### 2.2 生产环境 — 公司内网服务器
| 项目 | 信息 |
|------|------|
| **NAS 内网 IP** | `192.168.3.200` |
| **服务器 IP** | `10.90.5.110` |
| **用途** | 生产环境(企微端实际使用) |
| **部署方式** | Docker Compose 5容器(cloudflared + nginx + backend + postgres + redis |
| **部署路径** | `/volume1/docker/wecom-it-desk` |
| **外网访问** | Cloudflare Tunnel(无需公网 IP、无需端口映射) |
| **状态** | 🔄 部署包已准备,待上线 |
| **部署方式** | Docker Compose 4容器(nginx + backend + postgres + redis |
| **部署路径** | `/opt/wecom-it-desk` |
| **外网访问** | WAF/域名直连 |
| **状态** | ✅ 已上线运行 |
---
@@ -59,13 +59,13 @@
| 域名 | 环境 | 用途 | 类型 | 状态 |
|------|------|------|------|------|
| `it-dataquery.dc.servyou-it.com` | 预生产 | G端服务器反代入口(共用现有域名) | 公司内网域名 | ✅ 已有 |
| `itdesk.amanzac.com` | 生产(阶段一测试) | NAS Cloudflare Tunnel 入口 | Cloudflare 托管域名 | 配置 |
| `itsupport.servyou.com.cn` | 生产(正式) | 公司备案域名,后续正式环境使用 | 公司备案域名 | 🔄 待申请/配置 |
| `itdesk.amanzac.com` | ~~生产(已下线)~~ | ~~NAS Cloudflare Tunnel 入口~~ | ~~已下线~~ | 下线 |
| `itsupport.servyou.com.cn` | 生产(正式) | 公司内网服务器入口 | WAF/域名 | ✅ 已上线 |
> **说明**
> - 预生产环境复用数据查询平台现有域名,新增 `/itdesk/``/itagent/``/api/``/ws/` 路径即可
> - 阶段一测试使用 Cloudflare Tunnel + `itdesk.amanzac.com`,已配置可用
> - 正式上线后将切换至 `itsupport.servyou.com.cn`(公司备案域名),需走域名申请流程
> - ~~阶段一测试使用 Cloudflare Tunnel + `itdesk.amanzac.com`,已配置可用~~(已下线)
> - 生产环境已切换至 `itsupport.servyou.com.cn`(公司备案域名)
---
@@ -131,9 +131,9 @@ location /itagent/ {
}
```
### 4.2 生产 — NAS NginxDocker 内部)
### 4.2 生产环境 — NginxDocker 内部)
NAS 环境的 Nginx 已内置于 Docker Compose,**无需运维额外配置**。路由规则如下:
生产环境的 Nginx 已内置于 Docker Compose,**无需运维额外配置**。路由规则如下:
| 路径前缀 | 用途 | 目标 |
|----------|------|------|
@@ -142,7 +142,7 @@ NAS 环境的 Nginx 已内置于 Docker Compose**无需运维额外配置**
| `/api/` | 后端 API | `http://backend:8000/` |
| `/ws/` | WebSocket | `http://backend:8000`upgrade |
> 详细配置见 `nginx/nginx-nas.conf`
> 详细配置见 `nginx/nginx.conf`
---
@@ -167,16 +167,15 @@ NAS 环境的 Nginx 已内置于 Docker Compose**无需运维额外配置**
目标: yw-dify.dc.servyou-it.com:443 TCP 允许 # Dify AI
```
### 5.2 生产环境NAS
### 5.2 生产环境
| 源(发起方) | 目标 | 端口 | 协议 | 用途 |
|-------------|------|------|------|------|
| NAS `192.168.3.200` | Cloudflare Edge | `443` | TCP/HTTPS(出站) | Tunnel 连接 |
| NAS `192.168.3.200` | 企微 API `qyapi.weixin.qq.com` | `443` | TCP/HTTPS(出站) | 企微回调/消息推送 |
| NAS `192.168.3.200` | Dify AI 服务 | `443` | TCP/HTTPS(出站) | AI 对话调用(预留 |
| 办公网络 | NAS `192.168.3.200` | `18080` | TCP/HTTP | 内网调试访问(可选) |
| 服务器 `10.90.5.110` | 企微 API `qyapi.weixin.qq.com` | `443` | TCP/HTTPS(出站) | 企微回调/消息推送 |
| 服务器 `10.90.5.110` | Dify AI 服务 | `443` | TCP/HTTPS(出站) | AI 对话调用(预留) |
| 办公网络 | 服务器 `10.90.5.110` | `18080` | TCP/HTTP | 内网调试访问(可选 |
> **说明**Cloudflare Tunnel 为**出站连接**,NAS 无需开放任何入站端口,安全性最优
> **说明**生产环境通过 WAF/域名直连访问,无需 Tunnel
---
@@ -185,7 +184,7 @@ NAS 环境的 Nginx 已内置于 Docker Compose**无需运维额外配置**
| 环境 | 方案 | 状态 |
|------|------|------|
| 预生产 | 复用 `it-dataquery.dc.servyou-it.com` 现有证书 | ✅ 无需额外申请 |
| 生产 | Cloudflare Tunnel 自动 HTTPS 终止 | ✅ 无需申请证书 |
| 生产 | 公司统一 SSL 证书(*.servyou.com.cn | ✅ 已配置 |
---
@@ -204,10 +203,10 @@ NAS 环境的 Nginx 已内置于 Docker Compose**无需运维额外配置**
| 验证地址 | 预期结果 |
|----------|----------|
| `https://itdesk.amanzac.com/itdesk/` | 显示 H5 员工端页面 |
| `https://itdesk.amanzac.com/itagent/` | 显示坐席工作台页面 |
| `https://itdesk.amanzac.com/api/health` | 返回 `{"status":"healthy"}` |
| `http://192.168.3.200:18080/itdesk/` | 内网直连访问(调试用) |
| `https://itsupport.servyou.com.cn/itdesk/` | 显示 H5 员工端页面 |
| `https://itsupport.servyou.com.cn/itagent/` | 显示坐席工作台页面 |
| `https://itsupport.servyou.com.cn/api/health` | 返回 `{"status":"healthy"}` |
| `http://10.90.5.110:18080/itdesk/` | 内网直连访问(调试用) |
---
@@ -308,7 +307,7 @@ NAS 环境的 Nginx 已内置于 Docker Compose**无需运维额外配置**
- **申请人**:宋献,IT支持组(税友集团),负责终端安全
- **火绒/联软对接人**:宋献(超管权限,自行创建API账户,受安全团队管理)
- **项目**:智能IT支持服务台(IT Smart Desk
- **紧急程度**:预生产反代配置建议 1-2 个工作日内完成;生产环境 NAS 自建,无需运维介入
- **紧急程度**:预生产反代配置建议 1-2 个工作日内完成;生产环境已部署运行
- **组织架构说明**
- IT支持组 = 终端安全负责团队(非独立"终端安全团队")
- 火绒企业版、联软LV7000 均以IT支持组名义申请/配置
@@ -1,8 +1,12 @@
# 企微智能IT支持服务台 — 项目总览与部署手册
> **版本**: v2.1 | **日期**: 2026-06-03 | **编制**: 宋献(IT支持组组长)
> **版本**: v2.2 | **日期**: 2026-07-04 | **编制**: 宋献(IT支持组组长)
> **目标读者**: **管理者 / 架构师 / 运维** — 了解项目全貌、架构决策、部署与运维操作
> **📖 与 README.md 的关系**: 本文是 [README.md](../README.md) 的**详细版本**,侧重完整的架构设计和部署运维。README 适合新人快速入门,本文适合深入了解。
> **⚠️ 运维手册更新**: 部署与运维操作已整合到独立文档 [智能IT服务系统运维手册](./智能IT服务系统运维手册.md),该文档由助理(小米)维护,随每次发布更新。本文档保留架构设计与背景信息。
---
## 目录
@@ -0,0 +1,52 @@
# 企微智能 IT 支持服务台 — 项目全量知识库
> **项目代号**:`wecom_it_smart_desk`
> **状态**:v0.5.0-beta 内测(2026-06-15),v0.4.x 仍为生产稳定版
> **目标用户**:公司 6000 员工(企微 IM)
> **核心痛点**:员工绕过 AI 直接找人工 / AI 转人工需另开窗口 / 无法跨主体共享
> **核心方案**:自研 IT 服务坐席系统,基于企微自建应用消息 API
> **演进路径**:M1 消息接管 → M2 AI 接入 → M3 知识库闭环
> **本文档用途**:Claude 全面接管项目后的"项目大脑" — 完整功能/架构/集成/部署/安全/历史决策清单
> **最后更新**:2026-06-15(项目从 WB 移交到 Claude 接管)
---
## 0. 项目移交状态(2026-06-15)
| 项目 | 状态 |
|---|---|
| 项目控制方 | **Claude 全面接管**(原由 Workbuddy CN 推进,2026-06-15 起停用) |
| WB 项目目录 | `D:\资料\03-项目开发\wecom_it_smart_desk`(待用户授权后归档封存) |
| Claude 工作目录 | `D:\资料\03-项目开发\wecom_it_smart_desk-claude`(唯一 active 副本) |
| 代码托管 | Gitea(自托管 NAS 8418 端口),推送前需重新获 token |
| 移交决策原因 | WB 1009 上下文冲突 + token 误吊销 + 大量功能信息丢失 |
| 关联 memory | `project-handover-to-claude` / `gitea-push-permission-revoked-2026-06-15` |
---
## 1. 业务背景
### 1.1 公司规模与用户
- 6000 员工,全国分子机构
- 内部 IM:企业微信(企微)
- 现有痛点:
- 员工绕过 AI 直接找人工,AI 筛选率极低
- AI 转人工需另开窗口,体验割裂
- 企微"员工服务"不支持跨企业应用共享
### 1.2 核心设计理念
- **并行协作**(非传统串行排队):AI 全程在线 + 人工随时介入 + 员工同一窗口无缝切换
- **统一入口**(v0.5):企微工作台 → 唯一 OAuth2 认证点 → 角色检测 → 路由到用户端/坐席端/管理端
- **AI Wingman**(v0.6+):AI 不仅是服务员工的,更是解放坐席的
### 1.3 三步演进路径
| 里程碑 | 周期 | 核心交付 | 状态 |
|---|---|---|---|
| **M1** 消息接管 + 极简坐席 | 6-8 周 | 企微 API 链路 · 三栏工作台 · H5 双栏 · 邀请功能 | ✅ 代码完成,部署中 |
| **M2** AI 机器人接入 | M1 后 4-6 周 | 千问/Dify/RAGFlow · AI 前置筛选 · 排队系统 | 📋 计划中 |
| **M3** 知识库闭环 | M2 后 4-6 周 | 坐席标注系统 · 千问自动分析 · 知识库自优化 | 📋 计划中 |
---
*注:完整内容已移至 docs/ 目录统一管理*
@@ -0,0 +1,349 @@
# 企微IT智能服务台 — 项目全面评估报告
> **评估日期**: 2026-06-25
> **评估范围**: 全量代码库(后端 178 个 .py 文件 + 4 个前端 + 部署配置 + 文档体系)
> **项目周期**: 2025-12 至今(约 7 个月)
> **当前版本**: v0.7.1
> **目标用户**: 公司 6000+ 员工
---
## 一、评估总览
| 评估维度 | 评分 | 说明 |
|---------|------|------|
| 架构设计 | ⚠️ 7/10 | 层次清晰,但单体瓶颈已显现 |
| 代码质量 | ⚠️ 6.5/10 | 后端良好,前端可优化空间大 |
| 测试覆盖 | ⚠️ 5/10 | 后端有测试但 64 条失败,前端零测试 |
| 安全性 | ⚠️ 6.5/10 | 多次 P0 修复,但仍有遗留风险 |
| 文档完整性 | ✅ 9/10 | 50+ 份文档,远超同类项目 |
| 部署运维 | ⚠️ 5.5/10 | 手动部署,单点风险,无 CI/CD |
| 项目管理 | ⚠️ 6/10 | 任务跟踪良好但用户是唯一瓶颈 |
| **综合健康度** | **🟡 6.5/10 — 可用但有风险** |
---
## 二、架构评估
### 2.1 优势
1. **清晰的层次分离** — API 层 / Service 层 / Model 层职责分明,28 个 router 文件 + 20 个 service 文件组织合理
2. **合理的抽象设计**`ExternalSystemAdapter` 抽象层统一外部系统集成接口,支持降级和缓存
3. **WebSocket 双模式** — 实时推送 + 轮询降级,含指数退避重连
4. **统一响应格式**`{code, data, message}` 统一错误码体系
5. **4 前端独立工程** — 坐席/H5/管理后台/Portal 各司其职
### 2.2 关键问题
| # | 问题 | 严重度 | 影响 |
|---|------|--------|------|
| A1 | **单体后端无拆分** — 20 个 service 全部在一个 Python 进程 | 🟠 中 | 不可独立扩缩,故障影响面大;`message_router.py` 671 行,`session_service.py` 1234 行已经过大 |
| A2 | **无消息队列** — 所有业务同步处理,未引入 RabbitMQ/Kafka/Redis Stream | 🟠 中 | AI 回复耗时会阻塞请求链路;高并发下后端的请求处理能力受限于最慢的同步操作 |
| A3 | **WebSocket 单点** — 一个 ws_manager 进程管理所有连接 | 🔴 高 | 容器重启导致所有连接断开,生产环境已有坑记录;无跨进程 WS 广播方案 |
| A4 | **AI 层未集成** — Dify/RAGFlow 仅基础占位,路由已就绪但未启用 | 🔴 高 | README 自述"AI 自助解决率 70.2%",但当前新会话直接进人工队列 — 核心能力缺失 |
| A5 | **单数据库实例** — 无读写分离,无连接池监控 | 🟢 低 | 6000 用户并发访问压力下,单 PG 实例是潜在瓶颈 |
### 2.3 改进建议
1. **引入消息队列**:用 Redis Stream(已有 Redis 基础设施)解耦 AI 回复、消息路由等耗时操作
2. **WebSocket 水平扩展**:改用 Redis Pub/Sub 实现跨进程 WS 广播(ws_manager 框架化)
3. **后端模块化**:将 AI 处理、消息路由、外部系统集成拆分为独立微服务或异步 Worker
4. **AI 集成优先**Dify 凭证已在 docker-compose 中预留,应优先补齐 AI 自动回复能力
---
## 三、代码质量评估
### 3.1 后端
- **28 个 API 文件**, **20 个 service 文件**, **18 个模型文件** — 组织合理
- 文件注解完备(每个 API 文件注释齐全),参数命名规范
- 部分文件过大:`admin_service.py` (1,728 行), `session_service.py` (1,234 行), `message_router.py` (671 行)
### 3.2 前端(4 端)
- 坐席工作台 `Workspace.vue` 复杂度高(三栏动态切换,多个 v-if/v-else-if
- 前端类型定义与后端存在字段映射问题(已知 `MessageResponse.id` vs `message_id` 的 CRITICAL 映射)
- 缺少前端单元测试和组件测试
- 部分组件缺少 loading/error/empty 三态处理
### 3.3 依赖管理
- `requirements.txt` 版本锁定清晰,含完整注释说明每个依赖的作用
- 已知历史问题:`pydantic==2.7.5` 被 PyPI yank,需锁定 `2.7.4`
- **无前端依赖 lock 文件** — `package-lock.json``yarn.lock` 未归档
- **无依赖安全审计** — dependabot 配置了 `.gitea/dependabot.yml` 但 Gitea 是否支持不确定
### 3.4 改进建议
1. **大文件拆分**`admin_service.py` 按模块拆分,`session_service.py` 按生命周期阶段拆分
2. **前端测试**:核心组件(ChatArea/ConversationList)引入 Vitest + Vue Test Utils
3. **前端依赖锁**`npm run build` 前验证 package-lock.json 存在
4. **类型对齐**:后端 OpenAPI → 前端 TypeScript 代码生成(使用 openapi-typescript 工具)
---
## 四、测试评估
### 4.1 现状
| 测试类型 | 数据 | 状态 |
|---------|------|------|
| 后端 pytest 文件 | 25 个测试文件 | ✅ 存在 |
| 总测试用例 | ~538 条 | ⚠️ |
| 通过 | 470 passed + 4 xfailed | ✅ |
| 失败 | **64 条 pre-existing 失败** | 🔴 |
| 前端测试 | **0** | 🔴 完全缺失 |
| E2E 测试 | 176 行检查清单 | ✅ 文档化但未自动化 |
| 集成测试 | 62 tests collectedWindows 环境卡 conftest | ⚠️ 已知问题 |
### 4.2 关键问题
| # | 问题 | 严重度 |
|---|------|--------|
| T1 | **64 条测试持续失败** — 被标记为 pre-existing,无人修复 | 🔴 高 |
| T2 | **conftest SQLite+StaticPool 在 Windows 卡死** — 本地无法跑全量测试 | 🔴 高 |
| T3 | **前端零测试** — 4 个前端项目没有任何单元/组件/E2E 测试 | 🔴 高 |
| T4 | **无 CI 门禁** — 没有"测试通过才能合入"的流水线 | 🟠 中 |
| T5 | **Mock 依赖强耦合** — conftest 的 patch 路径耦合代码结构 | 🟠 中 |
### 4.3 改进建议
1. **根治 64 条失败** — 每个 sprint 分配 10% 时间专门修复,按模块优先级推进
2. **conftest 重写** — 用 `PostgreSQL testcontainers` 替代 SQLite,彻底解决兼容性问题
3. **前端测试筑基** — 从最核心的消息列表和发送组件开始,逐步扩展到 30% 覆盖率
4. **Git Hooks**`pre-commit-check.sh` 已存在,但应加入"新增 API 端点必加 Depends"的静态检查
---
## 五、安全评估
### 5.1 已完成的安全工作(值得肯定)
- ✅ 5 个 P0 鉴权漏洞已修复
- ✅ WS token 改走 `Sec-WebSocket-Protocol`
- ✅ 坐席密码 bcrypt 迁移
- ✅ nginx access_log 脱敏
- ✅ MFA/TOTP 二次验证(Google Authenticator 兼容)
- ✅ 高危操作守卫(HighRiskGuard
- ✅ CSP/HSTS/X-Frame-Options 等安全头
- ✅ 异常信息脱敏
### 5.2 遗留风险
| # | 风险项 | 状态 | 严重度 | 存在时间 |
|---|--------|------|--------|----------|
| S1 | **nginx IP 白名单开 0.0.0.0/0** — 临时方案,攻击面大 | ⚠️ 待处理 | 🔴 高 | 11 天 |
| S2 | **Token 未绑定 IP/设备** — 任何获取 token 者可冒用 | ⚠️ 待处理 | 🟠 高 | 11 天 |
| S3 | **未实现全端速率限制** — 仅登录端点有限制 | ⚠️ 待处理 | 🟠 中 | 11 天 |
| S4 | **Upload 路径在容器本地** — 容器重建后文件丢失 | ⚠️ 待处理 | 🟠 高 | 11 天 |
| S5 | **Gitea 无异地备份** — 仅 NAS 本地 | ⚠️ 待处理 | 🟠 中 | 11 天 |
| S6 | **Nginx client_max_body_size 未按路径细分** | ⚠️ 待处理 | 🟢 低 | 11 天 |
| S7 | **前端硬编码配置** — 配置未通过运行时注入 | ⚠️ 待处理 | 🟢 低 | 11 天 |
### 5.3 改进建议
1. **立即收窄 IP 白名单**:联系网络组确认 WAF/堡垒机/CDN 出口 IP,替换 0.0.0.0/0
2. **Token 绑定增强**Redis 中存储 token 时绑定用户 IP 段(可选 /24)或浏览器指纹
3. **全端速率限制**:用 slowapi 为所有 API 端点分级限流(登录 10/min,通用 60/min,管理 30/min
4. **Upload 持久化**:加 docker volume mount 解决重建丢文件问题
5. **Gitea 异地备份**:备份到 NAS 另一存储池 + 定期下载到本地(或公司备份系统)
---
## 六、部署与运维评估
### 6.1 现状
| 维度 | 当前状态 | 目标状态 |
|------|---------|---------|
| 编排方式 | Docker Compose 单机 | K8s 集群(规划中) |
| 部署方式 | 手动打包 → 堡垒机上传 → 解压部署 | CI/CD 自动化 |
| HTTPS | 已配置(443 + 301 跳转) | — |
| 健康检查 | 已配置但有 curl 已知坑 | 稳定可靠 |
| 容器镜像管理 | 本地构建,无 registry | Harbor/阿里云镜像仓库 |
| 数据库备份 | 无自动化备份 | cron 定期备份 |
| 日志管理 | docker logs + json-file driver | 集中式日志(ELK/Loki |
| 监控 | /health 端点 + dashboard.py | Prometheus + Grafana |
### 6.2 关键问题
| # | 问题 | 严重度 |
|---|------|--------|
| D1 | **手动部署高风险** — 依赖堡垒机手动上传,无回滚按钮 | 🔴 高 |
| D2 | **无容器镜像 Registry** — 每台机器本地构建镜像,版本不可追溯 | 🟠 中 |
| D3 | **单点故障** — 后端/PG/Redis 全部在同一 Docker 主机 | 🔴 高 |
| D4 | **无自动化备份策略** — 数据库无定期 dumpRedis 仅 AOF | 🔴 高 |
| D5 | **Docker Compose env 密码明文风险** — .env 文件存密码 | 🟠 中 |
| D6 | **部署包打包工具 package.py 曾缺少 Portal 和 Admin** — 2026-06-15 已修复 | ✅ 已修 |
### 6.3 改进建议
1. **短期:加固部署流程**
- `scripts/deploy.sh` 增加回滚模式(保留前 3 个版本的部署包)
- 部署前自动备份数据库(`docker compose exec postgres pg_dump`
- 部署后自动触发健康检查 + 5 分钟观察期
2. **中期:镜像管理与 CI/CD**
- 搭建 Harbor(NAS 上即可)作为私有镜像仓库
- GitHub Actions 或 Gitea Actions 实现自动构建 + push
- 服务器 pull 新镜像实现零停机部署
3. **长期:高可用架构**
- PostgreSQL 主从 + PgPool-II
- Redis Sentinel 集群
- 后端多副本 + Nginx 负载均衡
- 多 Docker 主机(Swarm/K8s
---
## 七、文档与项目管理评估
### 7.1 文档优势
- ✅ 50+ 份文档覆盖架构/PRD/部署/审计/风险/SOP/ADR
- ✅ README 有分角色阅读指南(新人/开发/运维/测试)
- ✅ 风险跟踪表 22 项(含处理计划和责任人)
- ✅ CHANGELOG 严格按 Keep a Changelog 格式
- ✅ CURRENT-FOCUS.md 作为驾驶舱仪表盘
- ✅ 知识库 KNOWLEDGE.md 23 章 1000+ 行
### 7.2 问题
| # | 问题 | 严重度 |
|---|------|--------|
| P1 | **工作记忆文件过大被截断**`MEMORY.md` 超过 3000 字符限制 | 🔴 高 |
| P2 | **风险跟踪表 11 天未更新**(上次 2026-06-14 | 🟠 中 |
| P3 | **用户是唯一决策者** — 所有 P2 任务标的"等用户决策",无自主推进机制 | 🟠 中 |
| P4 | **CURRENT-FOCUS.md 状态混乱** — 同时存在多个不同时间的 P2 等决策表 | 🟠 中 |
| P5 | **无 Sprint 回顾记录** — 无经验教训的结构化沉淀 | 🟢 低 |
### 7.3 改进建议
1. **立即整理 MEMORY.md**:合并重复项,删除过期记录,控制在 3000 字符内
2. **风险跟踪表定期更新**:每周一自动检查,标注处理进度
3. **设立"自动推进"机制**:对重复出现的 P2 项(如镜像仓库、HTTPS),设定 next-review-date 到期自动提醒
4. **文档审计周期**:每两周审查一次关键文档(风险跟踪表 + CURRENT-FOCUS.md
---
## 八、风险汇总矩阵
| 级别 | 基础架构 | 安全 | 功能交付 | 运维 | 项目管理 |
|------|---------|------|---------|------|---------|
| 🔴 高 | WS 单点 / 单体后端瓶颈 | IP 白名单全开 / Token 无绑定 | AI 集成未完成 | 无备份 / 手动部署高风险 | MEMORY.md 截断 |
| 🟠 中 | 无消息队列 / 单 DB | 速率限制不全 / Upload 丢文件 | 外部系统集成不全(aTrust/eHR) | 无镜像 Registry / 单点故障 | 用户单一瓶颈 / 风险表过期 |
| 🟢 低 | 前端组件未拆分 | 前端硬编码 / Nginx 文件限制 | 满意度评价未开始 | .env 明文密码 | 无 Sprint 回顾 |
### 当前风险热力图
```
高危区(P0,须即刻处理):
┌─────────────────────────────────────────────┐
│ ■ AI 集成未完成(核心功能缺失) │
│ ■ nginx IP 白名单 0.0.0.0/0(攻击面大) │
│ ■ MEMORY.md 截断(项目记忆丢失风险) │
│ ■ 无自动化数据库备份(数据丢失即永远丢失) │
└─────────────────────────────────────────────┘
中危区(P12 周内处理):
┌─────────────────────────────────────────────┐
│ ■ 64 条测试失败(质量信号失真) │
│ ■ 前端零测试(回归风险不可控) │
│ ■ 上传文件容器重建丢失(数据不持久) │
│ ■ 单体后端超负荷(session_service 1234行) │
└─────────────────────────────────────────────┘
低危区(P2,纳入 Sprint):
┌─────────────────────────────────────────────┐
│ ■ WebSocket 单点(断连重建体验差) │
│ ■ 无镜像 Registry(版本不可追溯) │
│ ■ 风险跟踪表过期(11 天未更新) │
│ ■ 用户单一决策瓶颈(P2 任务堆积) │
└─────────────────────────────────────────────┘
```
---
## 九、推进计划
### 9.1 Phase 1 — 立即止血(1-3 天)
| 优先级 | 任务 | 关联问题 | 预计耗时 |
|--------|------|---------|---------|
| P0 | **收窄 nginx IP 白名单** — 联系网络组获取 WAF/CDN 出口 IP | S1 | 1 天(等回复) |
| P0 | **清理 MEMORY.md** — 合并去重,控制 3000 字符 | P1 | 1 小时 |
| P0 | **配置数据库自动备份** — 写入 cron,每天凌晨全量 dump | D4 | 2 小时 |
| P1 | **Upload 加 volume mount** — 解决容器重建丢文件 | S4, D6 | 30 分钟 |
| P1 | **修复 conftest SQLite+StaticPool** — 让本地测试可用 | T2 | 2 小时 |
### 9.2 Phase 2 — 核心功能补齐(1-2 周)
| 优先级 | 任务 | 关联问题 | 预计耗时 |
|--------|------|---------|---------|
| P0 | **AI 集成** — Dify 接入(凭证已有,路由就绪,缺工作流对接) | A4 | 3-5 天 |
| P1 | **修复 64 条测试** — 按模块逐个排查修复 | T1 | 3 天 |
| P1 | **前端测试筑基** — ChatArea/ConversationList 核心组件测试 | T3 | 2 天 |
| P1 | **全端速率限制** — slowapi 覆盖所有 API 端点 | S3 | 1 天 |
| P2 | **Token 绑定 IP** — Redis 存储时绑定客户端 IP | S2 | 1 天 |
### 9.3 Phase 3 — 基础设施加固(2-4 周)
| 优先级 | 任务 | 关联问题 | 预计耗时 |
|--------|------|---------|---------|
| P1 | **搭建私有镜像 Registry**NAS Harbor | D2 | 2 天 |
| P1 | **Gitea Actions CI/CD** — 自动构建 + push + 部署 | D1, T4 | 3 天 |
| P1 | **大文件服务拆分** — admin_service / session_service 按模块拆分 | A1 | 2 天 |
| P2 | **WebSocket 改用 Redis Pub/Sub** — 支持多进程广播 | A3 | 3 天 |
| P2 | **消息队列引入(Redis Stream** — AI 回复异步化 | A2 | 3 天 |
### 9.4 Phase 4 — 架构演进(1-2 月)
| 优先级 | 任务 | 关联问题 | 预计耗时 |
|--------|------|---------|---------|
| P1 | **PostgreSQL 主从** — 配置流复制 | A5 | 3 天 |
| P2 | **前端组件全面重构** — 按功能模块拆分"巨石组件" | — | 1 周 |
| P2 | **E2E 自动化** — Playwright/Cypress 端到端测试 | — | 1 周 |
| P3 | **外部系统集成补齐** — aTrust + eHR 对接 | — | 1 周/系统 |
| P3 | **K8s 迁移评估** — 可行性分析 + 资源估算 | — | 3 天 |
### 9.5 时间线总览
```
Week 1 Week 2 Week 3-4 Month 2
┌─────────┐ ┌─────────┐ ┌──────────┐ ┌──────────────┐
│ Phase 1 │ → │ Phase 2 │ → │ Phase 3 │ → │ Phase 4 │
│ 止血 │ │ 补功能 │ │ 加固 │ │ 演进 │
├─────────┤ ├─────────┤ ├──────────┤ ├──────────────┤
│ IP白名单│ │ AI集成 │ │ 镜像Registry│ │ PG主从 │
│ MEMORY │ │ 修测试 │ │ CI/CD │ │ 前端重构 │
│ 数据库备份 │ │ 前端测试 │ │ 服务拆分 │ │ E2E自动化 │
│ Upload修复│ │ 限流 │ │ WS优化 │ │ 外部系统补齐 │
│ conftest│ │ Token绑定│ │ 消息队列 │ │ K8s评估 │
└─────────┘ └─────────┘ └──────────┘ └──────────────┘
```
---
## 十、总结
### 10.1 值得肯定的成就
- **7 个月从零到 v0.7.1**,4 个前端 + 完整后端,产品功能全面
- **安全意识高** — 多次 P0 安全修复,已实施 MFA、高危守卫、安全头等
- **文档体系远超同类项目** — 50+ 份文档,ADR、SOP、风险跟踪表一应俱全
- **工程规范良好** — CHANGELOG 标准格式、提交约定、分支模型
### 10.2 最需要关注的 3 件事
1. **🔴 AI 集成** — 这是项目的核心价值主张,"AI 驱动"写在项目名称里,不能只有人肉坐席
2. **🔴 测试体系** — 64 条失败 + 前端零测试,质量门禁缺失,这是长期积累的债务
3. **🔴 部署与运维** — 无自动化备份、手动部署、单点故障,一旦出问题恢复时间不可控
### 10.3 一句话评估
> **项目功能完整、文档优秀、安全根基扎实,但核心 AI 能力缺失和测试体系薄弱是当前最大的风险敞口。建议在推进新功能前,集中 1-2 周补齐这两块短板。**
---
*报告由 软件开发团队·主理人 齐活林 编制 | 2026-06-25*
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,115 @@
---
name: v0.7.2-backlog-candidate-2026-07-04
description: v0.7.1 开发中,基于文档优化专项后的最新状态更新
metadata:
type: project
last_updated: 2026-07-04
---
# v0.7.2 backlog 候选(2026-07-04 更新)
> **更新说明 (2026-07-04)**:
> - v0.7.1 正在开发中(企微SSO、RBAC权限)
> - 完成文档优化专项(用户手册创建、KPI指标补充、技术约束更新)
> - 补充阶段四/五的KPI指标定义
> - 新增需求:待办事项集成企微审批工单(#74)
## 来源
扫描 CURRENT-FOCUS.md(P0/P1/P2)+ phase1-progress.md 痛点 + 3 个 v1.0 必做 memory。
## 🟡 候选清单
### P1(可放 v0.7.2)
1. **#48 [P0→P1] 收窄 /api/admin/ + /itadmin/ IP 白名单**
- 当前 `allow 0.0.0.0/0` 临时全开
- 阻塞:**需网络组确认真实代理 IP 段**(WAF/堡垒机/CDN 出口 IP)
- 不能 Claude 单方面定,需用户提交工单
- 估时:1h(改 nginx + reload + 验证)
2. **#74 [P1] 待办事项集成企微审批工单**
- 将企微审批工单同步到坐席待办事项
- 需企微审批应用 API 权限
- 估时:2-3天
3. **#73 [P1] 修后端文件未真正覆盖**
- `yes | cp -f` 路径,部署时偶尔没生效
- 根因:`deploy-staging/` bind mount + RO 双重坑(见 [[bind-mount-deleted-inode-pitfall]])
- 估时:2h(改 deploy 脚本用 rsync --checksum)
3. **#86 [P1] 排查流程图零依赖部分 review + 文档化**
- 把 Mermaid 流程图从代码里剥离成可读文档
- 不阻塞生产,可顺手做
- 估时:3h
4. **#92 [P1] 修 v0.7.1-dev 引入的 pytest 失败(0 引入,33 pre-existing)**
- 实际还有 64 个 pre-existing 失败(conftest 卡死环境问题)
- v0.7.1-dev 引入 0 个
- 估时:4h(可能是 conftest.py SQLite StaticPool 性能 + Windows + utf-8 + asyncio loop 顺序问题)
### P2(可放 v0.7.3+ 或 v1.0)
5. **#31 docker 镜像推生产 registry**(等用户决策 Harbor/阿里云/其他)
6. **#43 HTTPS 证书**(等域名备案完成)
7. **#108 Gitea push v0.7.1-dev**(等 token 重授权)
8. **#53 企微 App 实扫码验证**(等用户手动)
## 🔧 内部清理(Claude 可独立做)
9. **pytest conftest 性能优化**
- 当前每个测试文件都要跑整套 alembic migration(从 0001 到 027)
- 优化:用 `alembic upgrade head` 缓存 db 到 tmpfs,或 sqlite-in-memory 复用
- 估时:2h
10. **CURRENT-FOCUS.md 刷新到 v0.7.1 release 完成态**
- 当前停在 2026-06-22 19:55,看板过期 36 小时
- 更新 in_progress / P0 / 最近搞定段
11. **pytest conftest 卡死问题根因分析**
- 跑单个 test_high_risk_guard.py 都 hang
- 怀疑 alembic migration 在 Windows + StaticPool 下死锁
- 估时:1h(可加 print + trace)
## 📚 文档优化专项(已完成 v0.7.2)
12. **✅ 05-用户手册目录创建**
- 已创建 3 本手册框架:员工使用指南、坐席操作手册、管理员手册
- 状态:已完成
13. **✅ PRD KPI 指标体系补充**
- 新增 §4.5 指标体系详细设计
- 包含北极星/驱动/健康指标框架、测量方法、行业基准对比
- 状态:已完成
14. **✅ PRD 章节编号说明**
- 添加版本号 v1.3 更新说明
- 添加章节编号解释
- 状态:已完成
15. **✅ 技术约束状态更新**
- 更新约束项当前状态
- 补充企微设备管理付费状态
- 状态:已完成
## 推荐 v0.7.2 范围
**必含(用户能直接拍板的)**:
- #48 IP 白名单收窄(前提:网络组确认)
- #73 修后端文件覆盖
- #92/9 pytest 性能 + 33 失败修复
**可选(顺手)**:
- #86 排查流程图文档化
- #10 看板刷新
**暂缓(等用户/外部)**:
- #108 Gitea push
- #31 docker registry
- #43 HTTPS 证书
- #53 企微验证
## Why
v0.7.1 已 release,P0 全清;剩余都是 P1/P2,用户应有选择权决定下一版本范围。
## How to apply
下次用户问"接下来做什么"或"v0.7.2 规划",直接给这份清单让用户选。
@@ -0,0 +1,499 @@
# IT智能服务台 — 系统架构设计文档
> **文档版本**: v1.4
> **创建日期**: 2025-07-11
> **最近更新**: 2026-07-04
> **架构师**: 高见远 (Bob)
> **状态**: 正式版
---
## 目录
1. [技术文档索引](#1-技术文档索引)
2. [系统概述](#2-系统概述)
3. [整体架构](#3-整体架构)
4. [技术选型](#4-技术选型)
5. [部署架构](#5-部署架构)
6. [统一入口设计](#6-统一入口设计)
7. [模块架构](#7-模块架构)
8. [AI Wingman 设计](#8-ai-wingman-设计)
9. [外部系统集成](#9-外部系统集成)
10. [安全设计](#10-安全设计)
11. [复杂对话场景设计](#11-复杂对话场景设计)
12. [知识图谱数据模型设计](#12-知识图谱数据模型设计)
13. [数据库设计](#13-数据库设计)
14. [API设计规范](#14-api设计规范)
---
## 1. 技术文档索引
本文档为技术架构主文档,相关详细设计文档如下:
| 文档 | 说明 | 状态 |
|------|------|------|
| **本文档** | 系统架构总览 v1.3 | ✅ 正式 |
| **01-ADRs-架构决策/** | 4项架构决策 | ✅ 已完成 |
| **02-技术方案/** | 具体功能技术方案 | |
| ├── [技术方案-消息功能详细设计.md](./02-技术方案/技术方案-消息功能详细设计.md) | 消息功能详细设计 | ✅ 已实现 |
| ├── [技术方案-摇人协作.md](./02-技术方案/技术方案-摇人协作.md) | 多坐席协作方案 | ✅ 已实现 |
| ├── [技术方案-邀请功能.md](./02-技术方案/技术方案-邀请功能.md) | 邀请功能方案 | ✅ 已实现 |
| ├── [技术方案-复杂场景重构.md](./02-技术方案/技术方案-复杂场景重构.md) | 复杂对话场景技术方案 | ✅ 设计完成 |
| └── [技术方案-ExternalSystemAdapter抽象层.md](./02-技术方案/技术方案-ExternalSystemAdapter抽象层.md) | 外部系统适配层设计 | ✅ 设计完成 |
| **03-技术分析/** | 技术研究分析 | |
| ├── [技术分析-H5右侧栏动态推送评估.md](./03-技术分析/技术分析-H5右侧栏动态推送评估.md) | H5右侧栏动态推送评估 | ✅ 已完成 |
| └── [技术分析-架构消息知识库迭代.md](./03-技术分析/技术分析-架构消息知识库迭代.md) | 架构消息知识库迭代 | ✅ 已完成 |
| **04-数据库设计/** | 数据库设计 | |
| └── [数据库设计-ER图与环境变量清点.md](./04-数据库设计/数据库设计-ER图与环境变量清点.md) | 数据库ER图与环境变量 | ✅ 已完成 |
| **05-架构图/** | Mermaid图表 | |
| └── [架构图集](./05-架构图/) | 各类时序图/类图 | ✅ 已完成 |
| [D-T19-知识图谱数据模型设计.md](../02-产品需求/小组任务书/D-T19-知识图谱数据模型设计.md) | 知识图谱数据模型设计 | ✅ 设计完成 |
---
## 2. 系统概述
### 2.1 项目背景
IT智能服务台是为企业提供 IT support 的智能化服务平台,核心目标:
- **员工侧**:通过 H5 页面提交 IT 问题、AI 自助解答、人工坐席服务
- **坐席侧**:通过自研工作台处理会话、AI 辅助( Wingman )、知识推荐
- **管理侧**:通过管理后台配置系统、管理坐席、查看数据
### 2.2 核心能力
| 能力 | 说明 |
|------|------|
| 消息路由 | AI 与人工无缝切换 |
| 实时会话 | 坐席工作台实时消息 |
| AI 辅助 | Wingman 草稿/摘要/知识推荐 |
| 外部集成 | 联软/火绒/aTrust/eHR 终端数据 |
| 角色管理 | 统一入口 + RBAC 权限 |
| 知识图谱 | Neo4j 图数据库支撑智能对话 |
---
## 3. 整体架构
### 3.1 系统架构图
```
┌─────────────────────────────────────────────────────────────────────┐
│ Linux 服务器 Docker │
│ │
│ ┌──────────┐ ┌──────────────┐ ┌──────────────┐ ┌────────────┐ │
│ │ Nginx │ │ Frontend │ │ Frontend │ │ Frontend │ │
│ │ (反代) │──│ Agent │ │ H5 User │ │ Portal │ │
│ │ :80/:443│ │ (Vue3+EP) │ │ (Vue3+Vant4)│ │ (Vue3) │ │
│ └────┬─────┘ └──────────────┘ └──────────────┘ └────────────┘ │
│ │ :5173 :5174 :5176 │
│ ▼ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ FastAPI │ │ Redis │ │PostgreSQL│ │ Neo4j │ │
│ │ Backend │──│ (缓存/Session)│──│ (持久化) │──│ (知识图谱) │ │
│ │ :8000 │ │ :6379 │ │ :5432 │ │ :7687 │ │
│ └────┬─────┘ └──────────┘ └──────────┘ └──────┬───────┘ │
│ │ │ │
│ │ ▼ │
│ │ ┌──────────────┐ │
│ └──────────────────────────────────────▶│ Dify AI │ │
│ │ (外部依赖) │ │
│ └──────────────┘ │
└───────────────────────────────────────────────────────────────────────┘
│ HTTPS
┌───────────────┐
│ 企微服务器 │
│ (消息回调API) │
└───────────────┘
```
### 3.2 前端架构
| 端 | 路径 | 技术栈 | 端口 |
|----|------|--------|------|
| Portal | /itportal/ | Vue3 + Element Plus | 5176 |
| 坐席端 | /itagent/ | Vue3 + Element Plus | 5173 |
| H5用户端 | /itdesk/ | Vue3 + Vant 4 | 5174 |
| 管理后台 | /itadmin/ | Vue3 + Element Plus + Tailwind | 5175 |
---
## 4. 技术选型
### 4.1 核心技术栈
| 层级 | 技术 | 版本 | 说明 |
|------|------|------|------|
| 前端框架 | Vue 3 | ^3.4.0 | Composition API |
| 前端路由 | Vue Router | ^4.3.0 | SPA 路由 |
| 状态管理 | Pinia | ^2.1.0 | 轻量级状态管理 |
| UI 组件 | Element Plus | ^2.7.0 | 坐席端/Portal |
| UI 组件 | Vant 4 | ^4.0 | H5 移动端 |
| 样式 | Tailwind CSS | ^3.4.0 | 管理后台 |
| 构建工具 | Vite | ^5.3.0 | 快速构建 |
| 后端框架 | FastAPI | 0.110+ | 异步高性能 |
| ORM | SQLAlchemy | 2.0+ | async ORM |
| 数据库 | PostgreSQL | 16 | 主数据存储 |
| 图数据库 | Neo4j Community | 5.x | 知识图谱存储 |
| 缓存 | Redis | 7 | Session/Token/缓存 |
| AI 引擎 | Dify | — | 外部依赖 |
---
## 5. 部署架构
### 5.1 部署拓扑
```
┌────────────────────────────┐
│ 企微服务器(外部) │
│ qyapi.weixin.qq.com │
└──────────┬─────────────────┘
│ HTTPS :443
┌──────────────── 办公网络 ────────────────────────────────┐
│ │
│ ┌──────────┐ ┌──────────────────────────┐ │
│ │ 坐席浏览器 │────────▶│ https://itsupport. │ │
│ │ (内网) │ HTTPS │ servyou.com.cn │ │
│ └──────────┘ └──────────┬───────────────┘ │
│ │ │
├────────────────── OA 服务器网络 ──┼───────────────────────┤
│ │ │
│ ┌───────▼──────────────┐ │
│ │ 服务器 (Docker) │ │
│ │ ┌────────────────┐ │ │
│ │ │ Nginx :80/443 │ │ │
│ │ └───────┬────────┘ │ │
│ │ │ │ │
│ │ ┌───────▼────────┐ │ │
│ │ │ FastAPI :8000 │ │ │
│ │ └───────┬────────┘ │ │
│ │ │ │ │
│ │ ┌───────▼────────┐ │ │
│ │ │ PostgreSQL │ │ │
│ │ └───────┬────────┘ │ │
│ │ │ │ │
│ │ ┌───────▼────────┐ │ │
│ │ │ Redis │ │ │
│ │ └───────┬────────┘ │ │
│ └──────────┴───────────┘ │
│ │ │
│ ┌────────▼──────────────┐ │
│ │ 现有 AI 服务(外部依赖)│ │
│ └───────────────────────┘ │
└──────────────────────────────────────────────────────────┘
```
---
## 6. 统一入口设计
### 6.1 概述
统一入口(Portal)是系统的认证入口,按角色分发到对应端。
**核心功能**
- 企微 OAuth2 静默授权(用户端)
- 账号密码+OTP 认证(坐席/管理端)
- 角色检测与路由选择
- 统一 Token 管理
### 6.2 登录方式(v1.5 变更)
> **更新日期**: 2026-07-04 | **核心变更**: 坐席/管理端浏览器直接打开,**无需经过企微工作台**
| 端 | 访问方式 | 登录方式 | 说明 |
|----|----------|----------|------|
| **用户端 (H5)** | 企微工作台 → 应用内嵌打开 | OAuth2 静默授权 | 强制内嵌,保证安全、入口统一、用户粘性 |
| **坐席端** | 浏览器直接打开 | 账号密码+OTP | **无需经过企微**,灵活办公,支持多设备 |
| **管理后台** | 浏览器直接打开 | 账号密码+OTP | **无需经过企微**,安全可控 |
### 6.3 登录流程
```
用户端 (H5) 坐席端 / 管理后台
│ │
▼ ▼
企微工作台打开 浏览器直接访问
│ │
▼ ▼
OAuth2 静默授权 账号密码+OTP验证
(用户无感知) │
│ ▼
▼ 登录成功
跳转 /itdesk/ 跳转 /itagent/ 或 /itadmin/
```
### 6.4 角色路由逻辑
```
OAuth2 授权完成(用户端) / 登录成功(坐席/管理端)
查询角色列表: GET /api/portal/roles
├── 仅 user 角色 → 直接跳转 /itdesk/
├── user + agent → 显示选择页(2张卡片)
├── user + admin → 显示选择页(2张卡片)
└── user + agent + admin → 显示选择页(3张卡片)
```
### 6.5 URL 路径规划
| 端 | 路径 | 说明 |
|---|------|------|
| 统一入口 | /itportal/ | 路由选择页 |
| 用户端 | /itdesk/ | 员工提交工单 |
| 坐席端 | /itagent/ | IT坐席处理会话 |
| 管理端 | /itadmin/ | 系统配置 |
| API | /api/ | 后端接口 |
### 6.6 企微环境检测
| 组件 | 说明 |
|------|------|
| **用户端检测** | `navigator.userAgent.includes('wxwork')`,非企微跳转拦截页 |
| **坐席/管理端** | 无需检测,浏览器直接访问 |
### 6.7 OTP 双因素认证
| 组件 | 说明 |
|------|------|
| **OTP 绑定** | 首次登录引导绑定,支持 TOTPGoogle Authenticator/微信扫码) |
| **OTP API** | `/api/agents/otp-bind``/api/agents/otp-verify` |
| **验证场景** | 坐席/管理员登录时需 OTP 验证 |
| **绑定入口** | 坐席端 TopBar 下拉菜单"OTP二次验证"选项 |
### 6.8 测试账号
| 角色 | 用户名 | 初始密码 | OTP | 说明 |
|------|--------|----------|-----|------|
| 坐席 | `sxn` | `admin123` | 需绑定 | IT 支持组组长 |
| 管理 | `sxn` | `admin123` | 需绑定 | 同上,具有 admin 权限 |
> **注意**:生产环境需修改默认密码;坐席/管理员需先绑定OTP才能登录
---
## 7. 模块架构
### 7.1 管理后台模块
**技术栈**Vue 3 + TypeScript + Element Plus + Tailwind CSS + Pinia
**核心功能**
- 运营仪表盘
- 功能开关配置
- 坐席管理(角色/技能标签)
- 外部系统集成配置
- 快速回复审核
- 会话监控
- 知识图谱管理
### 7.2 坐席工作台模块
**技术栈**Vue 3 + TypeScript + Element Plus + Pinia
**核心功能**
- 会话列表(排队/进行中/已解决)
- 实时聊天
- 快速回复
- AI Wingman 右侧栏
- 消息标记(VIP/招手/情绪)
### 7.3 H5 用户端模块
**技术栈**Vue 3 + Vant 4 + TypeScript
**核心功能**
- 消息发送/接收
- 排查步骤引导
- 会话状态查看
- 满意度评价
---
## 8. AI Wingman 设计
详见 [02-技术方案/技术方案-Wingman设计.md](./02-技术方案/技术方案-Wingman设计.md)
### 8.1 什么是 Wingman
Wingman 是坐席工作台的 AI 辅助系统:
- **草稿回复**:坐席打字 → AI 实时生成 3 条草稿
- **自动摘要**:会话结束 → AI 200 字摘要
- **知识推荐**:对话中识别关键字 → 推 FAQ
- **排查步骤**:员工描述问题 → AI 给 step-by-step
---
## 9. 外部系统集成
### 9.1 系统角色与优先级
| 系统 | 角色 | 核心能力 | 认证方式 |
|------|------|---------|---------|
| 联软LV7000 | 主映射源(P0) | 终端查询、硬件详情、在线状态 | IP白名单+账号密码 |
| 火绒企业版 | 安全源(P0) | 终端列表、漏洞/病毒事件 | HMAC-SHA1 AccessKey |
| aTrust | VPN源(P1) | 在线用户+VPN IP、终端查询 | HMAC-SHA256签名 |
| 北森eHR | 辅助静态数据(P2) | 员工基础信息、任职信息 | OAuth2.0 |
| 企微审批 | 待办同步(P1) | 审批工单同步到坐席待办 | 企微审批应用API |
### 9.2 企微审批工单同步
> **新增日期**: 2026-07-04
| 项目 | 说明 |
|------|------|
| **功能** | 将企微审批工单同步到坐席待办事项面板 |
| **企微API** | `GET /cgi-bin/oa/approvallist` |
| **同步频率** | 每5分钟定时拉取,或 Webhook 实时推送 |
| **数据映射** | 审批标题 → TodoItem.title / 审批状态 → TodoItem.status |
| **依赖** | 需企微管理后台创建审批应用并授权 API |
---
## 10. 安全设计
### 10.1 认证安全
| 安全措施 | 说明 |
|----------|------|
| OAuth2 静默授权 | scope=snsapi_base,用户无感知(用户端) |
| 账号密码+OTP | 坐席/管理端认证方式 |
| state 参数防 CSRF | 随机 state,回调时验证 |
| Token 密码学安全 | secrets.token_urlsafe(32) |
| Token TTL 8小时 | Redis 自动过期 |
| redirect_uri 白名单 | 生产环境仅允许正式域名 |
| 企微 UA 检测 | 用户端非企微环境跳转拦截页 |
| OTP 双因素认证 | 坐席/管理员登录需 OTP 验证码 |
### 10.2 角色安全
| 安全措施 | 说明 |
|----------|------|
| 角色最小权限 | 默认仅 user 角色 |
| 角色来源追溯 | user_roles 表记录 source 和 assigned_by |
| 管理端 IP 白名单 | 仅内网/VPN 可访问 |
---
## 11. 复杂对话场景设计
### 11.1 设计理念:TeliChat 三重约束
| 约束 | 作用 | 实现方式 |
|------|------|---------|
| 拓扑结构限制 | 限制对话可以走到哪里 | Neo4j DAG 边定义 |
| 信息状态约束 | 决定当前已经知道什么 | 信息项组合状态 |
| Python 代码约束 | 负责真正的业务判断 | FastAPI 业务逻辑 |
### 11.2 全局意图类型
| 意图 | 用户表达示例 | 处理策略 |
|------|-------------|---------|
| SKIP | "这个问题先不管了" | 跳过当前节点 |
| INSERT | "对了,我的打印机也有问题" | 插入新任务到队列 |
| RESUME | "还是说回刚才那个网络问题" | 恢复之前话题 |
| ESCALATE | "叫个人工来" | 转接坐席 |
---
## 12. 知识图谱数据模型设计
详见 [../02-产品需求/小组任务书/D-T19-知识图谱数据模型设计.md](../02-产品需求/小组任务书/D-T19-知识图谱数据模型设计.md)
### 12.1 实体类型
| 实体类型 | 说明 | 示例 |
|----------|------|------|
| Domain | 业务域 | 网络域、安全域、设备域 |
| Issue | 问题 | VPN连不上、打印机故障 |
| Solution | 解决方案 | 密码重置、重启服务 |
| FAQ | 常见问题 | 如何连接VPN |
### 12.2 关系类型
| 关系类型 | 方向 | 含义 | 核心属性 |
|----------|------|------|----------|
| BELONGS_TO | Issue→Domain | 属于 | weight |
| RECOMMENDS | Issue→Solution | 推荐 | priority, confidence |
| CAN_RESOLVE | Solution→Issue | 解决 | success_rate |
---
## 13. 数据库设计
### 13.1 核心表结构
| 表名 | 说明 |
|------|------|
| agents | 坐席信息 |
| conversations | 会话表 |
| messages | 消息表 |
| quick_reply_templates | 快速回复模板 |
| system_configs | 系统配置 |
| roles | 角色表 |
| user_roles | 用户角色关联 |
---
## 14. API设计规范
### 14.1 响应格式
```json
// 成功
{"code": 0, "data": {...}, "message": "success"}
// 失败
{"code": 1001, "data": null, "message": "参数错误"}
```
### 14.2 认证方式
| 端 | localStorage 键 | 说明 |
|----|-----------------|------|
| 坐席端 | `agent_token` | 坐席工作台 |
| 管理后台 | `admin_token` | 管理后台 |
| 统一入口 | `user_token` | Portal |
---
## 附录
### A. 项目阶段规划
| 阶段 | 内容 |
|------|------|
| 阶段一 | 转人工改H5+坐席MVP+邀请+管理后台 |
| 阶段二 | H5全流程+WS+排队+满意度+OAuth2 |
| 阶段三 | AI Wingman+排查流程图+标注+知识图谱 |
| 阶段四 | 迭代闭环+数据看板+知识库 |
| 阶段五 | 自动/辅助审核、开单、结单 |
### B. 部署信息
| 环境 | 域名 | IP |
|------|------|-----|
| 生产 | itsupport.servyou.com.cn | 10.90.5.110 |
| 测试 | itdesk.amanzac.com | NAS |
### C. 技术文档更新日志
| 版本 | 日期 | 修改内容 |
|------|------|---------|
| v1.0 | 2026-07-04 | 整合技术架构文档 |
| v1.1 | 2026-07-04 | 新增知识图谱章节 |
| v1.2 | 2026-07-04 | 新增登录设计章节(用户端强制企微内嵌) |
| v1.3 | 2026-07-04 | 坐席/管理端无需经过企微,浏览器直接登录 |
| v1.4 | 2026-07-04 | 新增企微审批工单同步到待办事项 |
---
> **文档结束** — 本文档为 IT 智能服务台系统架构设计主文档
@@ -0,0 +1,199 @@
# 技术方案-企微审批工单同步
> **文档编号**: 02-技术方案-企微审批工单同步
> **版本**: v1.1
> **创建日期**: 2026-07-04
> **状态**: 待评审
---
## 1. 需求概述
### 1.1 背景
将企微审批工单同步到坐席待办事项,实现IT服务台与企微审批系统的集成。
### 1.2 需求描述
| 需求项 | 说明 |
|--------|------|
| 需求来源 | PRD v0.7.2 Backlog #74 |
| 需求类型 | P1(待办事项集成) |
| 目标 | 将企微审批工单同步到坐席待办事项 |
---
## 2. 技术方案
### 2.1 整体架构
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 企微审批系统 │ ───▶ │ IT服务台后端 │ ───▶ │ 坐席待办事项 │
│ (外部API) │ │ (同步服务) │ │ (todo_items) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
▼ ▼ ▼
审批工单数据 定时轮询/回调 待办面板展示
(get_approval_list) 转换+存储
```
### 2.2 数据同步方式
| 方式 | 说明 | 优点 | 缺点 |
|------|------|------|------|
| **定时轮询** | 每5分钟调用企微API拉取审批列表 | 实现简单,稳定可靠 | 有延迟(最大5分钟) |
| **企微回调** | 审批通过时企微主动推送 | 实时性高 | 需要企微开通回调权限 |
| **混合模式** | 回调为主 + 定时兜底 | 实时+兜底 | 实现复杂 |
**推荐方案**:定时轮询(简单可靠) + 回调模式(实时性)
### 2.3 企微API接口
使用企微 OA 审批接口:
| 接口 | 说明 |
|------|------|
| `GET /cgi-bin/oa/approvallist` | 获取审批列表 |
| `GET /cgi-bin/oa/approvalinfo` | 获取审批详情 |
### 2.4 数据映射
| 企微审批字段 | todo_item 字段 | 说明 |
|-------------|----------------|------|
| `sp_no` | `id` | 审批单号 |
| `sp_name` | `title` | 审批名称 |
| `apply_name` | `description.applicant` | 申请人 |
| `apply_time` | `created_at` | 申请时间 |
| `status` | `status` | 审批状态 |
| `sp_status` | `priority` | 审批类型 |
### 2.5 待办事项字段扩展
```python
class TodoItem(Base):
# 现有字段...
type: Mapped[str] = "approval" # 固定为 approval
# 新增字段(审批专用)
approval_id: Mapped[str] = mapped_column(String(64)) # 企微审批单ID
approval_type: Mapped[str] = mapped_column(String(64)) # 审批模板名称
applicant: Mapped[str] = mapped_column(String(100)) # 申请人
applicant_dept: Mapped[str] = mapped_column(String(100)) # 申请人部门
apply_time: Mapped[datetime] # 申请时间
approval_url: Mapped[str] = mapped_column(String(512)) # 审批详情链接
```
---
## 3. 审批模板(IT相关)
根据需求,明确需要同步的审批模板:
| 序号 | 审批模板名称 | 说明 |
|------|-------------|------|
| 1 | IT资产升级申请 | 硬件升级审批 |
| 2 | IT资产报废申请 | 资产报废审批 |
| 3 | 商业软件服务申请 | 软件采购审批 |
| 4 | 企微外联权限申请 | 外网权限审批 |
| 5 | 离职人员企微微盘&文档空间异常移交 | 离职资产移交 |
| 6 | 会议室故障报修 | 设备报修审批 |
---
## 4. 企微API权限确认
### 4.1 如何确认是否已开通权限
**方法一:企微管理后台查看**
1. 登录企微管理后台:https://work.weixin.qq.com/
2. 进入「应用管理」→「自建应用」→ 选择IT服务台应用
3. 查看「API权限」中是否包含:
- 通讯录同步
- 审批相关接口
**方法二:调用接口测试**
使用已有 access_token 调用以下接口测试:
```bash
curl "https://qyapi.weixin.qq.com/cgi-bin/oa/approvallist?access_token=xxx&start_time=0&end_time=9999999999"
```
如果返回 `{"errcode":0, ...}` 表示已开通权限。
**方法三:联系企微管理员**
确认是否在「审批」应用中开通了API调用权限。
### 4.2 回调模式说明
| 对比项 | 定时轮询 | 企微回调 |
|--------|----------|----------|
| 实时性 | 5分钟延迟 | 秒级实时 |
| 实现复杂度 | 简单 | 稍复杂 |
| 可靠性 | 稳定 | 依赖回调通道 |
| 资源消耗 | 每次全量/增量拉取 | 按需推送 |
**回调模式优势**
1. 实时性高 - 审批提交/通过后立即同步
2. 节省资源 - 只需处理变更,无需轮询
3. 体验更好 - 坐席几乎实时看到新待办
**回调模式限制**
1. 需要企微开通审批回调权限
2. 回调地址需公网可访问(可通过nginx反向代理)
3. 需要处理回调签名验证
---
## 5. 实施计划
### 5.1 任务拆分
| 任务 | 说明 | 优先级 |
|------|------|--------|
| T1 | 扩展 todo_item 模型,新增审批专用字段 | P0 |
| T2 | 创建企微审批同步服务 `ApprovalSyncService` | P0 |
| T3 | 实现定时轮询逻辑(每5分钟) | P0 |
| T4 | 坐席端待办面板接入审批数据 | P1 |
| T5 | 审批状态变更同步 | P1 |
| T6 | 企微审批回调接入(可选) | P2 |
### 5.2 数据库变更
```sql
ALTER TABLE todo_items
ADD COLUMN approval_id VARCHAR(64),
ADD COLUMN approval_type VARCHAR(64),
ADD COLUMN applicant VARCHAR(100),
ADD COLUMN applicant_dept VARCHAR(100),
ADD COLUMN apply_time TIMESTAMP,
ADD COLUMN approval_url VARCHAR(512);
```
---
## 6. 待确认事项
| 事项 | 状态 | 备注 |
|------|------|------|
| 企微API权限 | 待确认 | 需按4.1方法确认 |
| 审批模板 | ✅ 已明确 | 6个IT相关模板 |
| 同步频率 | ✅ 已确认 | 5分钟可接受 |
| 回调模式 | ✅ 确认开通 | 实时性优先 |
---
## 7. 相关文档
| 文档 | 说明 |
|------|------|
| [PRD v0.7.2 Backlog](./02-产品需求/product-产品/v0.7.2-backlog-candidate-2026-06-24.md) | 需求来源 |
| [todo_item 模型](../../backend/app/models/todo_item.py) | 现有模型定义 |
| [企微API文档](https://developer.work.weixin.qq.com/document/14567) | 审批接口文档 |
---
> **文档状态**: 待评审
@@ -0,0 +1,900 @@
# 重构方案 - 复杂场景技术实现方案
> 文档版本:v1.1
> 日期:2026-07-03
> **设计理念**:借鉴 TeliChat "让代码负责业务逻辑,让模型负责语言理解"
---
## 零、设计理念:TeliChat 三重约束
> **核心理念**:借鉴 TeliChat 白盒架构,确保复杂对话场景的可靠性
### 0.1 三重约束机制
| 约束 | 作用 | 实现方式 |
|------|------|---------|
| **拓扑结构限制** | 限制对话可以走到哪里 | Neo4j DAG 边定义 |
| **信息状态约束** | 决定当前已经知道什么 | 信息项组合状态 |
| **Python 代码约束** | 负责真正的业务判断 | FastAPI 业务逻辑 |
### 0.2 信息项修饰机制
| 修饰 | 含义 | 在复杂场景中的应用 |
|------|------|------------------|
| `固定` | 用户回答后不再重复询问 | 已通过系统获取的信息(操作系统、用户名) |
| `增量` | 允许用户补充新信息 | 故障描述、错误信息 — **非线性跳转核心** |
| `明确` | 必须明确回答 | 紧急程度确认 — **信息更正核心** |
| `隐含` | 可以从上下文推断 | AI 推断的问题类型 |
| `复述` | 要求用户确认信息正确性 | 重要操作确认 — **信息更正核心** |
| `必需` | 必须填写才能进入下一节点 | 必填字段 — **任务中断恢复核心** |
### 0.3 全局意图类型
| 意图 | 用户表达示例 | 处理策略 | 对应场景 |
|------|-------------|---------|---------|
| `SKIP` | "这个问题先不管了" | 跳过当前节点,记录未完成 | 非线性跳转 |
| `INSERT` | "对了,我的打印机也有问题" | 插入新任务到队列 | 多意图并行 |
| `RESUME` | "还是说回刚才那个网络问题" | 恢复之前话题 | 任务中断恢复 |
| `SWITCH` | "先帮我看看VPN吧" | 切换到指定话题 | 非线性跳转 |
| `CORRECT` | "刚才说错了,是win10" | 更新信息项值 | 信息更正 |
| `SUPPLEMENT` | "再补充一下,是财务部的电脑" | 增量补充信息 | 信息更正 |
| `PAUSE` | "我先去开会,等会继续" | 保存状态,等待恢复 | 任务中断恢复 |
| `RESUME_TASK` | "好了,继续吧" | 恢复中断的任务 | 任务中断恢复 |
| `ESCALATE` | "叫个人工来" | 转接坐席 | 所有场景 |
### 0.4 状态驱动流程
```python
def determine_next_node(topology, information_items, user_intent):
"""
根据三重因素确定下一个节点
"""
# 1. 拓扑约束:检查意图是否在允许的路径上
allowed_paths = topology.get_allowed_paths(current_node)
if user_intent not in allowed_paths:
return handle_off_path_intent(user_intent)
# 2. 信息项检查:是否满足必填信息要求
required_items = topology.get_required_items(next_node)
for item in required_items:
if not information_items[item].is_filled:
return PromptForItem(item)
# 3. 业务逻辑:Python 代码执行判断
if should_escalate(information_items):
return TransferToAgent()
return ExecuteNode(next_node)
```
---
## 一、非线性跳转
### 1.1 场景描述
用户在对话过程中不按线性路径跳转,而是随时切换话题或返回上一步。
**示例**
```
用户:我想开VPN
AI:请问是个人用途还是团队用途?
用户:先说说团队 VPN 是什么(跳转到知识了解)
AI:(介绍团队VPN
用户:算了,我还是开个人的吧(返回原话题)
AI:好的,个人VPN开通需要...
```
### 1.2 技术架构
```
┌─────────────────────────────────────────────────────────────────┐
│ 用户对话 │
└─────────────────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Dify LLM 推理层 │
│ ┌─────────────────┐ ┌─────────────────┐ ┌───────────────┐ │
│ │ 意图理解 │ │ 上下文追踪 │ │ 路径规划 │ │
│ │ Intent Parser │ │ Context Track │ │ Path Planner │ │
│ └─────────────────┘ └─────────────────┘ └───────────────┘ │
└─────────────────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Neo4j 知识图谱 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ IT_SUPPORT_GRAPH │ │
│ │ │ │
│ │ [VPN问题] ──[可选]──> [个人VPN] │ │
│ │ │ │ │
│ │ [可选] │ │
│ │ │ │ │
│ │ └───[可选]──> [团队VPN] ──[子节点]──> [使用场景] │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
### 1.3 知识图谱设计
```cypher
// 节点设计
CREATE (vpn:Issue {name: "VPN开通", category: "网络"})
CREATE (personal:Action {name: "个人VPN开通"})
CREATE (team:Action {name: "团队VPN开通"})
CREATE (usage:Info {name: "使用场景说明"})
// 关系设计 - 支持非线性跳转
CREATE (vpn)-[:LEADS_TO {type: "可选", order: 1}]->(personal)
CREATE (vpn)-[:LEADS_TO {type: "可选", order: 2}]->(team)
CREATE (team)-[:LINKS_TO {type: "子节点"}]->(usage)
// 跳转关系 - 支持任意跳转
CREATE (personal)-[:CAN_JUMP_TO {type: "跳转"}]->(team)
CREATE (team)-[:CAN_JUMP_TO {type: "返回"}]->(vpn)
CREATE (usage)-[:CAN_JUMP_TO {type: "返回"}]->(team)
```
### 1.4 信息项修饰机制(借鉴 TeliChat)
**核心设计**:使用信息项的 `增量` 修饰符支持非线性跳转
```python
# 信息项定义
class InformationItem:
name: str # 信息项名称,如 "{故障描述}"
value: Any # 当前值
modifiers: List[str] # 修饰符: ["增量"]
# 状态追踪
is_filled: bool # 是否已填写
is_incremental: bool # 是否允许增量(补充)
# 非线性跳转示例
用户我想开VPN
AI请问是个人用途还是团队用途
用户先说说团队 VPN 是什么用户切换到"了解"意图
# 信息项状态变化
information_items = {
"VPN类型": {"value": None, "modifiers": ["增量"], "is_filled": False},
}
# 用户切换话题时,信息项"VPN类型"保留(因为是增量修饰)
# 用户返回时,可以继续之前的流程
```
### 1.5 全局意图识别支持
```python
# 非线性跳转意图识别
def detect_jump_intent(user_input: str) -> JumpIntent:
"""检测跳转意图"""
# RESUME - 返回之前话题
if any(kw in user_input for kw in ["还是说回", "继续刚才", "回到"]):
return JumpIntent.RESUME
# SWITCH - 切换到新话题
if any(kw in user_input for kw in ["先看", "先帮我看看", "算了"]):
return JumpIntent.SWITCH
# SKIP - 跳过当前问题
if any(kw in user_input for kw in ["先不管", "跳过", "算了"]):
return JumpIntent.SKIP
return JumpIntent.NONE
```
### 1.6 关键设计点
| 设计点 | 方案 | 说明 |
|--------|------|------|
| 上下文栈 | 使用栈结构维护对话路径 | 支持"返回上一步" |
| 节点状态 | 每个节点记录 visited/focused 状态 | 区分已访问和当前节点 |
| 跳转权限 | 边设计 CAN_JUMP_TO 关系 | 控制哪些节点可以互相跳转 |
| **信息项修饰** | 使用"增量"修饰符 | **借鉴 TeliChat,支持乱序输入** |
| **全局意图** | 识别 RESUME/SWITCH/SKIP | **借鉴 TeliChat,控制跳转** |
---
## 二、多意图并行
### 2.1 场景描述
用户一次输入包含多个意图,系统需要并行处理后再合并结果。
**示例**
```
用户:我电脑开不了机,VPN也连不上
→ 同时处理2个问题:
1. 电脑开机问题 → 引导检查电源/硬件
2. VPN连接问题 → 引导检查网络/账号
→ 合并输出:两个问题的处理指引
```
### 2.2 技术架构
```
用户输入: "我电脑开不了机,VPN也连不上"
┌─────────────────────────────────────────────────────────────────┐
│ Dify 多意图识别节点 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Input: "我电脑开不了机,VPN也连不上" │ │
│ │ Output: │ │
│ │ [ │ │
│ │ {intent: "电脑开机故障", entities: []}, │ │
│ │ {intent: "VPN连接失败", entities: []} │ │
│ │ ] │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────┬───────────────────────────────────┘
┌───────────────────┼───────────────────┐
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│ 意图1分支 │ │ 意图2分支 │ │ 意图N分支 │
│ 电脑开机 │ │ VPN连接 │ │ ... │
│ 路径推理 │ │ 路径推理 │ │ │
└─────┬─────┘ └─────┬─────┘ └─────┬─────┘
│ │ │
└───────────────────┼───────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 结果合并节点 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 将多个分支的结果合并为统一回复 │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
### 2.3 Dify 工作流设计
```yaml
# Dify 工作流配置(简化版)
workflow:
nodes:
- id: multi_intent_parser
type: LLM
prompt: |
用户输入: {{input}}
识别所有意图,以JSON数组返回:
[{"intent": "意图1", "entities": [...]}, {"intent": "意图2", "entities": [...]}]
- id: parallel_branches
type: parallel
branches:
- target: intent_1_handler
- target: intent_2_handler
- id: result_merger
type: LLM
prompt: |
合并以下处理结果为统一回复:
{{intent_1_result}}
{{intent_2_result}}
```
### 2.4 知识图谱辅助
```cypher
// 为多意图场景设计聚合节点
CREATE (multi:IntentGroup {name: "多问题聚合", type: "parallel"})
// 并行意图关系
CREATE (multi)-[:CONTAINS {parallel: true}]->(vpn_issue)
CREATE (multi)-[:CONTAINS {parallel: true}]->(hardware_issue)
// 并行度标记
MATCH (n)-[r:LEADS_TO]->(m)
SET r.is_parallel = false // 默认串行
```
### 2.5 信息项聚合管理(借鉴 TeliChat)
**核心设计**:多意图对应多个独立的信息项集合
```python
# 多意图场景的信息项设计
class MultiIntentSession:
"""多意图会话管理"""
# 每个意图对应独立的信息项集合
intent_items: Dict[str, List[InformationItem]] = {
"电脑开机": [
{"name": "故障现象", "modifiers": ["增量", "必需"]},
{"name": "错误信息", "modifiers": ["增量"]},
],
"VPN连接": [
{"name": "错误代码", "modifiers": ["明确"]},
{"name": "网络环境", "modifiers": ["隐含"]},
]
}
def add_intent(self, intent: str):
"""添加新意图,创建独立信息项集合"""
if intent not in self.intent_items:
self.intent_items[intent] = []
def get_all_items(self) -> List[InformationItem]:
"""获取所有意图的信息项"""
items = []
for intent_items in self.intent_items.values():
items.extend(intent_items)
return items
```
### 2.6 全局意图 INSERT 支持
```python
# INSERT 意图处理
def handle_insert_intent(user_input: str, session: MultiIntentSession):
"""处理插入新意图"""
# 检测 INSERT 意图
insert_keywords = ["对了", "还有", "另外", "顺便"]
if any(kw in user_input for kw in insert_keywords):
# 识别新意图
new_intent = llm_recognize_intent(user_input)
session.add_intent(new_intent)
# 并行处理新旧意图
return process_parallel_intents(session)
return None
```
### 2.7 关键设计点
| 设计点 | 方案 | 说明 |
|--------|------|------|
| 意图识别 | Dify LLM 并行识别 | 使用 Few-shot 提示词模板 |
| 分支并行 | Dify Parallel Branch | 同时触发多个处理分支 |
| 结果合并 | Dify LLM 合并 | 智能合并多分支输出 |
| 冲突检测 | 边设计 CONFLICTS_WITH | 检测意图间冲突 |
| **信息项聚合** | 每个意图独立信息项集合 | **借鉴 TeliChat,管理多意图状态** |
| **INSERT 意图** | 检测"对了/还有"等插入语 | **借鉴 TeliChat 全局意图** |
---
## 三、信息更正
### 3.1 场景描述
用户在对话过程中更正之前提供的信息,系统需要理解更正并更新上下文。
**示例**
```
用户:帮我重置密码,用户名是 zhangsan
AI:好的,正在为 zhangsan 重置密码...
用户:不好意思,用户名是 lisi,不是 zhangsan
AI:好的,已更正,为 lisi 重置密码
```
### 3.2 技术架构
```
用户输入: "不好意思,用户名是 lisi,不是 zhangsan"
┌─────────────────────────────────────────────────────────────────┐
│ Dify 意图理解层 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 识别更正意图: │ │
│ │ { │ │
│ │ "type": "correction", │ │
│ │ "field": "username", │ │
│ │ "old_value": "zhangsan", │ │
│ │ "new_value": "lisi" │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Neo4j 会话状态图谱 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Session(id: xxx) │ │
│ │ │ │ │
│ │ ├── [:PROVIDED]─> Field(name: "username", value: "zhangsan") │ │
│ │ │ │ │
│ │ └── [:CORRECTED]─> (标记旧值为已更正) │ │
│ │ │ │ │
│ │ └──> Field(name: "username", value: "lisi") │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
### 3.3 知识图谱设计
```cypher
// 会话状态节点
CREATE (session:Session {
id: "session_123",
user_id: "user_001",
created_at: datetime(),
current_node: "password_reset"
})
// 用户提供的字段(可更正)
CREATE (session)-[:PROVIDED]->(field1:Field {
name: "username",
value: "zhangsan",
timestamp: datetime(),
status: "corrected" // 标记为已更正
})
// 更正后的字段
CREATE (session)-[:PROVIDED]->(field2:Field {
name: "username",
value: "lisi",
timestamp: datetime(),
status: "active" // 当前有效值
})
// 更正历史关系
CREATE (field1)-[:CORRECTED_TO {new_value: "lisi", timestamp: datetime()}]->(field2)
```
### 3.4 Dify 工作流设计
```yaml
# 更正处理节点
nodes:
- id: correction_detector
type: LLM
prompt: |
检测用户输入是否为信息更正:
用户输入: {{input}}
当前已知信息: {{known_fields}}
输出JSON:
{
"is_correction": true/false,
"corrected_field": "字段名",
"old_value": "旧值",
"new_value": "新值",
"confidence": 0.0-1.0
}
- id: field_updater
type: code
action: |
# 更新 Neo4j 中的字段状态
# 1. 标记旧值为 corrected
# 2. 创建新值节点
# 3. 建立更正关系
```
### 3.5 信息项修饰机制(借鉴 TeliChat)
**核心设计**:使用"增量"+"复述"双修饰实现智能信息更正
```python
# 信息项修饰与更正策略
class InformationItem:
modifiers: List[str] # 修饰符组合
def handle_update(self, new_value: str, is_correction: bool = False):
"""处理信息更新"""
if "增量" in self.modifiers and not is_correction:
# 增量模式:追加新值,不覆盖旧值
self.value = f"{self.value}; {new_value}"
elif "复述" in self.modifiers:
# 复述模式:要求用户确认
self.pending_confirmation = new_value
return ConfirmationRequest(new_value)
else:
# 默认模式:直接覆盖
self.value = new_value
self.is_filled = True
self.last_updated = datetime.now()
return None
# 更正示例
# 用户:不好意思,用户名是 lisi,不是 zhangsan
# 系统识别 CORRECT 意图,更新信息项
information_items["用户名"] = {
"value": "lisi",
"modifiers": ["明确"], # 原来是"明确"修饰
"is_filled": True,
"update_history": ["zhangsan"] # 保留更正历史
}
```
### 3.6 全局意图 CORRECT/SUPPLEMENT 支持
```python
# 更正意图识别
def detect_correction_intent(user_input: str) -> CorrectionInfo:
"""检测更正意图"""
correction_patterns = [
(r"不是(.+),是(.+)", "swap"), # 不是A,是B
(r"应该是(.+)", "replace"), # 应该是A
(r"更正.*?为(.+)", "replace"), # 更正为A
(r"说错了.*?是(.+)", "replace"), # 说错了是A
]
for pattern, correction_type in correction_patterns:
match = re.search(pattern, user_input)
if match:
return CorrectionInfo(
type=correction_type,
old_value=match.group(1) if match.lastindex >= 1 else None,
new_value=match.group(2) if match.lastindex >= 2 else match.group(1),
is_correction=True
)
return None
```
### 3.7 关键设计点
| 设计点 | 方案 | 说明 |
|--------|------|------|
| 更正识别 | Dify LLM | 检测"不是/应该是/更正为"等模式 |
| 字段版本 | Neo4j 节点版本 | 维护字段历史,支持回溯 |
| 状态同步 | WS 实时推送 | 更正后立即更新前端状态 |
| **增量修饰** | 追加而非覆盖 | **借鉴 TeliChat,支持补充** |
| **复述修饰** | 要求用户确认 | **借鉴 TeliChat,关键信息确认** |
| **CORRECT 意图** | 识别更正表达 | **借鉴 TeliChat 全局意图** |
---
## 四、任务中断与恢复
### 4.1 场景描述
用户在任务进行过程中中断(离开/超时),后续可以恢复继续。
**示例**
```
用户:我要开VPN
AI:请问是个人还是团队用途?
用户:(离开/超时/未回复)
--- 2小时后 ---
用户:继续刚才的VPN申请
AI:好的,您刚才选择的是VPN开通,请问是个人还是团队用途?
(恢复上下文,继续流程)
```
### 4.2 技术架构
```
┌─────────────────────────────────────────────────────────────────┐
│ 任务状态机设计 │
│ │
│ ┌─────────┐ 用户输入 ┌─────────┐ 选择个人 ┌─────┐ │
│ │ START │ ───────────> │ ASK_TYPE│ ──────────> │INPUT│ │
│ └─────────┘ └─────────┘ └──┬──┘ │
│ ^ │ │ │
│ │ │ 恢复 │ │
│ │ ▼ ▼ │
│ │ ┌─────────┐ ┌────────┐ │
│ └─────────────── │ PAUSED │ <──────────── │ RESUME │ │
│ 恢复 └─────────┘ 用户恢复 └────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### 4.3 知识图谱设计
```cypher
// 任务节点
CREATE (task:Task {
id: "task_vpn_001",
type: "VPN开通",
status: "paused", // paused / active / completed / cancelled
created_at: datetime(),
updated_at: datetime(),
current_node: "ASK_TYPE",
user_id: "user_001"
})
// 任务路径历史
CREATE (task)-[:HAS_HISTORY]->(step1:TaskStep {
node: "START",
status: "completed",
timestamp: datetime()
})
CREATE (task)-[:HAS_HISTORY]->(step2:TaskStep {
node: "ASK_TYPE",
status: "active",
timestamp: datetime()
})
// 恢复点
CREATE (task)-[:CAN_RESUME_FROM {node: "ASK_TYPE"}]->(resume_point:ResumePoint {
prompt: "请问是个人还是团队用途?",
options: ["个人", "团队"],
timestamp: datetime()
})
```
### 4.4 状态管理
```python
# 任务状态机
class TaskState:
STATES = {
"created": ["active", "cancelled"],
"active": ["paused", "completed", "cancelled"],
"paused": ["active", "cancelled", "expired"],
"completed": [],
"cancelled": [],
"expired": ["active"]
}
def pause(self):
"""任务中断"""
self.status = "paused"
self.paused_at = datetime.now()
self._save_to_neo4j()
def resume(self):
"""任务恢复"""
if self.status != "paused":
raise InvalidStateError("只有暂停的任务可以恢复")
self.status = "active"
self.resumed_at = datetime.now()
self._save_to_neo4j()
```
### 4.5 恢复触发
| 触发方式 | 说明 |
|----------|------|
| 关键字恢复 | 用户输入"继续/恢复/接着刚才" |
| 菜单恢复 | 提供"我的任务"入口 |
| 超时恢复 | 定时任务检测暂停任务,恢复后通知用户 |
| 坐席恢复 | 坐席手动恢复用户任务 |
```cypher
// 恢复点查询
MATCH (task:Task {user_id: $user_id, status: "paused"})
MATCH (task)-[:CAN_RESUME_FROM]->(rp)
RETURN task, rp.prompt as resume_prompt
ORDER BY rp.timestamp DESC
LIMIT 1
```
### 4.6 信息项与任务状态(借鉴 TeliChat)
**核心设计**:任务状态 = 信息项组合,使用结构化状态空间
```python
# 任务状态 - 结构化信息项组合
class TaskState:
"""借鉴 TeliChat 的结构化状态空间"""
# 任务元信息
task_id: str
status: str # created/active/paused/completed/cancelled/expired
# 信息项组合 - 决定任务能否继续
information_items: Dict[str, InformationItem] = {}
# 当前节点
current_node: str
visited_nodes: List[str] = []
def can_proceed_to(self, next_node: str) -> bool:
"""检查是否可以进入下一节点"""
# 检查必需信息项是否已填写
required_items = get_required_items(next_node)
for item_name in required_items:
if item_name not in self.information_items:
return False
if not self.information_items[item_name].is_filled:
return False
return True
def get_pending_items(self) -> List[str]:
"""获取未完成的必需信息项"""
pending = []
# 检查所有节点的必需信息项
all_required = get_all_required_items(self.current_node)
for item_name in all_required:
if item_name not in self.information_items:
pending.append(item_name)
elif not self.information_items[item_name].is_filled:
pending.append(item_name)
return pending
```
### 4.7 全局意图 PAUSE/RESUME 支持
```python
# 任务中断与恢复意图
class TaskIntent(Enum):
PAUSE = "暂停" # 用户主动暂停
RESUME_TASK = "继续" # 用户恢复任务
EXPIRED = "过期" # 任务超时过期
def handle_task_intent(user_input: str, task_state: TaskState) -> Action:
"""处理任务控制意图"""
# PAUSE - 用户离开
pause_keywords = ["先去开会", "等会继续", "先处理别的"]
if any(kw in user_input for kw in pause_keywords):
task_state.status = "paused"
task_state.paused_at = datetime.now()
save_to_redis(task_state) # 持久化
return Action(message="好的,您先忙,需要时 say一声继续")
# RESUME_TASK - 用户返回
resume_keywords = ["继续", "好了", "继续刚才", "接着来"]
if any(kw in user_input for kw in resume_keywords):
task_state = load_from_redis(task_state.task_id)
task_state.status = "active"
pending = task_state.get_pending_items()
if pending:
return Action(message=f"好的,您刚才说到{pending[0]},请继续")
else:
return Action(message="继续刚才的流程...")
return None
```
### 4.8 关键设计点
| 设计点 | 方案 | 说明 |
|--------|------|------|
| 状态持久化 | Neo4j 节点 | 保存任务完整上下文 |
| 恢复点 | ResumePoint 节点 | 保存每个步骤的恢复信息 |
| 超时处理 | 定时任务 | 24小时未恢复则标记 expired |
| 坐席可见 | 状态同步 | 坐席工作台可查看用户任务状态 |
| **结构化状态** | 信息项组合决定状态 | **借鉴 TeliChat,可靠的状态管理** |
| **必需修饰** | 缺失必填项则阻塞 | **借鉴 TeliChat,保证任务完整性** |
| **PAUSE/RESUME 意图** | 任务控制意图 | **借鉴 TeliChat 全局意图** |
---
## 五、综合架构
### 5.1 完整技术栈
```
┌─────────────────────────────────────────────────────────────────┐
│ 用户层 (H5端) │
│ 用户发起对话,接收AI/坐席回复 │
└─────────────────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Dify AI 推理层 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ 意图理解 │ │ 多意图并行 │ │ 信息更正检测 │ │
│ │ Intent │ │ Parallel │ │ Correction Detector │ │
│ └──────────────┘ └──────────────┘ └──────────────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ 结果合并 │ │ 路径规划 │ │ 任务状态机 │ │
│ │ Merger │ │ Path Plan │ │ Task FSM │ │
│ └──────────────┘ └──────────────┘ └──────────────────────┘ │
└─────────────────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Neo4j 知识图谱层 │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ [Issue] ──[LEADS_TO]──> [Action] │ │
│ │ │ │ │
│ │ [:CAN_JUMP_TO] ←──→ [:CAN_JUMP_TO] │ │
│ │ │ │ │
│ │ [Session] ──[PROVIDED]──> [Field] │ │
│ │ │ │ │
│ │ [Task] ──[HAS_HISTORY]──> [TaskStep] │ │
│ │ │ │ │
│ │ [:CAN_RESUME_FROM] ──> [ResumePoint] │ │
│ │ │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
### 5.2 TeliChat 风格架构
```
┌─────────────────────────────────────────────────────────────────┐
│ TeliChat 风格架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 信息项状态管理层 │ │
│ │ (InformationItem: name/value/modifiers/is_filled) │ │
│ └─────────────────────────────┬───────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────▼───────────────────────────┐ │
│ │ 全局意图识别层 │ │
│ │ (SKIP/INSERT/RESUME/SWITCH/CORRECT/SUPPLEMENT/ │ │
│ │ PAUSE/RESUME_TASK/CANCEL/ESCALATE) │ │
│ └─────────────────────────────┬───────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────▼───────────────────────────┐ │
│ │ 状态驱动引擎 │ │
│ │ f(拓扑结构, 信息项组合, 用户意图) = 下一节点 │ │
│ └─────────────────────────────┬───────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────▼───────────────────────────┐ │
│ │ Dify AI 执行层 │ │
│ │ (意图理解 + 路径推理 + 结果生成) │ │
│ └─────────────────────────────┬───────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────▼───────────────────────────┐ │
│ │ Neo4j 图数据库层 │ │
│ │ (知识图谱 + 会话状态 + 任务状态) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### 5.3 核心能力矩阵
| 场景 | TeliChat 设计 | Dify 能力 | Neo4j 能力 | 综合支持 |
|------|--------------|-----------|-------------|---------|
| 非线性跳转 | 增量修饰 + SWITCH/SKIP/RESUME 意图 | 路径规划 | 图谱遍历 + 跳转关系 | ✅ 完全支持 |
| 多意图并行 | INSERT 意图 + 信息项聚合 | 并行分支 + 结果合并 | 聚合节点 | ✅ 完全支持 |
| 信息更正 | 增量+复述修饰 + CORRECT/SUPPLEMENT 意图 | 更正检测 | 字段版本管理 | ✅ 完全支持 |
| 中断恢复 | 必需修饰 + PAUSE/RESUME_TASK 意图 | 状态触发 | 任务状态机 + 恢复点 | ✅ 完全支持 |
---
## 六、实施建议
### 6.1 TeliChat 架构落地计划
#### 短期(1-2周)
1. **信息项数据模型设计**
- 设计 InformationItem 数据结构
- 定义 6 种修饰符的交互策略
- 开发 CRUD 接口
2. **全局意图识别 Agent**
- 在 Dify 中创建意图识别工作流
- 支持 10 种全局意图类型
#### 中期(1个月)
3. **状态驱动引擎**
- 开发对话状态管理服务
- 实现"拓扑+信息项+意图"三因素路由
4. **Neo4j 融合**
- 图谱节点携带信息项定义
- 支持信息项状态查询
### 6.2 实施优先级
| 优先级 | 场景 | TeliChat 核心 | 工作量 | 建议 |
|--------|------|--------------|--------|------|
| P0 | 任务中断恢复 | 必需修饰 + PAUSE/RESUME | 中 | 核心场景,优先实现 |
| P1 | 信息更正 | 增量+复述 + CORRECT | 小 | 用户体验关键 |
| P1 | 非线性跳转 | 增量修饰 + SWITCH/SKIP | 大 | 知识图谱扩展 |
| P2 | 多意图并行 | INSERT + 信息项聚合 | 中 | 高级场景,后续迭代 |
### 6.3 技术债务
| 项 | 说明 | 规避方案 |
|----|------|----------|
| 图谱复杂度 | 跳转关系过多导致图谱复杂 | 设计跳转权限控制 |
| 状态一致性 | 中断恢复可能产生状态不一致 | 使用事务保证 |
| 性能 | 多意图并行增加响应时间 | 添加缓存层 |
| **LLM 幻觉** | **TeliChat 解决的核心问题** | **代码约束 + 拓扑限制** |
---
*本文档为技术实现方案详细设计 v1.1*
*新增 TeliChat 风格设计理念*
@@ -0,0 +1,312 @@
# JP-webcli 自动化部署工具 — 能力、限制与优化空间分析
> 分析时间:2026-06-25 | 基于 v7/v8/v8b/v9_cdp/v10_persistent 全版本代码审查
---
## 一、现有能力全景
### 1.1 核心自动化能力(已验证可用)
| 能力 | 实现方式 | 状态 | 版本 |
|------|---------|------|------|
| 自动登录 | Playwright 填写用户名/密码 | ✅ 稳定 | v6+ |
| OTP 双因素认证 | pyotp 本地生成 TOTP | ✅ 自动填充 | v4+ |
| 资产导航 | DOM 选择器定位目标行 | ✅ 支持模糊匹配 | v6+ |
| Web CLI 连接 | 点击连接按钮 → 处理 Luna dialog | ✅ 含轮询重试 | v6+ |
| 命令执行 | `keyboard.type()` 逐字输入 | ✅ 含 delay 防丢字 | v6+ |
| SFTP 文件上传 | `set_input_files()` 文件管理器 | ✅ 分块(8 chunk | v7+ |
| base64 大文件传输 | 分段 echo + base64 -d 还原 | ✅ 250 字符/段 | v8b+ |
| 截图留存 | `page.screenshot()` | ✅ PNG 格式 | v3+ |
| 会话复用 | `persistent_context` / CDP connect | ✅ v10 | v10 |
| 轮询等待(替代死等) | 检测 prompt 字符/dialog/terminal | ✅ 40s → 待命 | v10.15 |
| 分阶段部署 | Part A(传输) + B(执行) + C(验证) | ✅ 脚本编排 | v5+ |
### 1.2 已验证的部署场景
| 场景 | 日期 | 成果 |
|------|------|------|
| Hotfix #116 扫码获取 | 06-22 | `auth_qrcode.py` 部署成功 |
| Hotfix #120 扫码自动确认 | 06-23 | 29KB `qrcode_service.py` + 8 chunk SFTP |
| Hotfix #48 Nginx Upstream | 06-24 | 配置修复 + HTTPS 验证 |
| Hotfix SSO 单点登录 | 06-25 | config.py + 环境变量注入 |
---
## 二、当前限制(按严重程度排序)
### 🔴 P0 — 阻塞性问题
#### 2.1 xterm.js 输出无法结构化捕获(**当前最紧急**)
**现状**:所有版本(v6v10)均依赖 **截图** 作为命令执行结果的唯一验证手段。
**根因**xterm.js 将终端内容渲染到 `<canvas>` 元素,终端缓冲区存储在 JavaScript 内部对象中:
```
xterm.js 架构:
┌─────────────────────────────┐
│ DOM: <div class="xterm"> │
│ ├── .xterm-viewport │ ← 可滚动视口
│ ├── .xterm-screen │ ← 屏幕容器
│ │ └── <canvas> │ ← ★ 内容渲染到这里
│ └── .xterm-accessibility │ ← 无障碍层(可能为空)
└─────────────────────────────┘
terminal.buffer.active ← ★ 真正的文本在 JS 内存中
├── .baseY / .viewportY ← 滚动位置
├── .length ← 总行数
└── .getLine(y) ← 按行获取
└── .translateToString() ← 转为可见文本
```
当前所有版本的 "输出检测" 方法:
| 方法 | 代码 | 捕获到的是什么 |
|------|------|-------------|
| `page.evaluate("document.body.innerText")` | v10.15 | ⚠️ DOM 文本(不含 canvas 内容) |
| `page.screenshot()` | 所有版本 | ⚠️ 二进制图片,不可解析 |
| prompt 正则匹配 | v10.15 | ⚠️ 只能判定"命令结束了",不能拿到输出 |
**导致的失败模式**
1. **Hotfix #120 的 18 个脚本分裂**`part1 → part2 → diag → verify → sub → tail → view` 中,大量 "查看日志" 脚本 (diag/log/tail/view) 的唯一目的是在终端执行 `cat`/`tail`/`curl`,然后**人工查看截图**确认结果。如果能结构化捕获输出,这些脚本可以合并为带条件判断的单一流程。
2. **无法自动判断部署成功与否**`run_v7_v5.py``proc.returncode` 只能判断 Playwright 进程是否正常退出,无法判断远程服务器上的 `docker restart` 是否成功、`curl` 是否返回预期响应。
3. **无退出码捕获**:执行 `docker ps && echo $?` 的结果在 canvas 里,脚本只能"相信"它成功了。
4. **日志无法结构化存储**:每次部署的输出是截图(PNG),而非文本。无法 grep、无法 diff、无法自动对比两次部署的差异。
5. **截图驱动的不稳定性**
- 命令输出超过一屏时,截图只看到最后一段
- 颜色/样式变化可能被误读为错误
- 网络延迟导致截图时机窗口不可控
#### 2.2 无 headless 模式(依赖 Chrome 可见窗口)
**现状**v10 明文注释"默认非 headless 模式(JumpServer 可能检测 headless 并拒绝)"。
**影响**
- 无法在无 GUI 的服务器上运行
- 必须保持 Chrome 窗口可见(占用桌面空间)
- 无法与用户其他 Chrome 使用并行
#### 2.3 单实例限制
- 同一时间只能运行一个 Playwright 实例
- 多个部署任务必须串行
- `persistent_context` 和 CDP connect 互斥
### 🟡 P1 — 重要限制
| 限制 | 详情 | 影响 |
|------|------|------|
| **长命令输入易截断** | 单次 `keyboard.type` ~20KB 安全上限,超过需分块 | 29KB 文件需 8 chunk |
| **依赖 Windows Chrome** | Playwright `channel="chrome"` | 无法在 Linux 服务器运行 |
| **磁盘占用大** | Playwright + Chromium ~500MB | 多实例部署开销大 |
| **无命令队列机制** | 没有批量执行 + 结果收集 | 每次部署需人工编排 |
| **OTP 需本地密钥文件** | 依赖 `otp_secret.key` | 换机器需重新配置 |
| **错误恢复依赖人工** | 失败后无自动重试策略 | 夜间部署需人工值守 |
| **硬编码路径** | v7 中 V7/V7_DIR 路径硬编码 | 换环境需修改代码 |
### 🟢 P2 — 改进空间
| 限制 | 详情 |
|------|------|
| **部署包 >500MB 未迁移** | `deploy-server/` 目录 |
| **无企微通知集成** | 部署结果需手动确认 |
| **无 CLI 参数标准** | v7/v8/v9/v10 参数不统一 |
| **无部署历史数据库** | 每次部署成功/失败无结构化记录 |
---
## 三、优化空间(按 ROI 排序)
### 🔴 P0 — 紧急优化
#### 3.1 xterm.js 结构化输出捕获 ✅ 最高优先级
这是从 **"截图验证" → "程序化验证"** 的关键跃迁。
**实现方案**:通过 `page.evaluate()` 直接访问 xterm.js 的内部 buffer
```python
# 方案 A:读取 xterm.js 内部 buffer(推荐)
def capture_terminal_output(page):
"""从 xterm.js 的 terminal.buffer 中提取全部可见文本"""
return page.evaluate("""() => {
// JumpServer Luna 将 xterm 实例挂载在全局或 DOM 属性上
// 方式1: 通过 xterm 的全局引用
if (window.term) {
const term = window.term;
const buffer = term.buffer.active;
const lines = [];
for (let i = 0; i < buffer.length; i++) {
const line = buffer.getLine(i);
if (line) {
lines.push(line.translateToString());
}
}
return lines.join('\\n');
}
// 方式2: 通过 xterm 的 DOM 属性(如 data-xterm 或 __vue__
// 方式3: 遍历 window 对象找 xterm 实例
for (const key of Object.keys(window)) {
try {
const obj = window[key];
if (obj && obj.buffer && obj.buffer.active) {
const buffer = obj.buffer.active;
const lines = [];
for (let i = 0; i < buffer.length; i++) {
const line = buffer.getLine(i);
if (line) lines.push(line.translateToString());
}
return lines.join('\\n');
}
} catch(e) {}
}
return null;
}""")
# 方案 B:利用 xterm.js 的无障碍 DOM 层(如果开启)
def capture_via_a11y(page):
"""xterm.js 可选地把内容同步到 .xterm-accessibility 层"""
return page.evaluate("""() => {
const a11y = document.querySelector('.xterm-accessibility');
if (a11y) {
const children = a11y.querySelectorAll('[role="presentation"]');
return Array.from(children).map(el => el.textContent).join('\\n');
}
return null;
}""")
```
**预期收益**
| 改进项 | 当前状态 | 优化后 |
|--------|---------|--------|
| 命令输出获取 | 截图(PNG 二进制) | 结构化文本 |
| 部署成功率判断 | 人工看截图 | 自动 grep exit code / 关键字 |
| 日志存储 | PNG 图片 | 文本(可 diff、可搜索) |
| 脚本数量 | Hotfix #120 需 18 个 | 可合并为 3-5 个带条件分支 |
| 错误诊断速度 | 人工翻截图 | 自动提取错误行 |
| CI/CD 集成 | 不可行 | 可行(输出可解析) |
**实施步骤**
1. **探测阶段**:写一个 `probe_xterm_buffer.py`,在连上终端后执行 `page.evaluate()`,尝试多种方式找到 xterm 实例引用
2. **验证阶段**:对比 `capture_terminal_output()` 的输出和截图内容是否一致
3. **集成阶段**:修改 `send_terminal_command()``send_and_capture()`,返回结构化文本
4. **高级阶段**:利用 buffer 实现增量读取(只读新增行),避免重复传输全部内容
**风险**JumpServer Luna 可能对 xterm 实例做了封装/私有化。需要先探测具体引用路径。
#### 3.2 命令执行结果自动判定
依赖 3.1 的输出,实现:
```python
def execute_and_verify(terminal_page, command, expected_pattern=None, timeout=60):
"""执行命令并自动判定结果"""
send_command(terminal_page, command)
output = wait_for_prompt_and_capture(terminal_page, timeout)
# 自动提取退出码
exit_code = extract_exit_code(output) # 从 "$?=0" 或显式 echo $?
# 自动匹配预期模式
if expected_pattern:
matched = re.search(expected_pattern, output)
return CommandResult(
output=output,
exit_code=exit_code,
matched=matched,
screenshot=screenshot(terminal_page)
)
```
---
### 🟡 P1 — 高价值优化
#### 3.3 部署流水线引擎
将当前的手动编排的 Phase 1/2/3 流程抽象为通用引擎:
```python
class DeployPipeline:
stages = [
Stage("prepare", commands=[...], verify="docker ps | grep wecom"),
Stage("upload", sftp_files=[...], verify="ls -la /tmp/"),
Stage("deploy", commands=[...], verify="curl -s localhost/api/health"),
Stage("verify", commands=[...], verify="grep 'started' /var/log/app.log"),
]
def run(self):
for stage in self.stages:
result = self.execute_stage(stage)
if not result.ok:
self.rollback()
self.notify_failure(stage, result)
return
self.notify_success()
```
#### 3.4 headless 模式适配
探索 JumpServer 反 headless 检测的具体方式,针对性绕过:
- 添加 `--disable-blink-features=AutomationControlled`
- 注入 JS 覆盖 `navigator.webdriver`
- 使用 `stealth` 模式的 Playwright 配置
#### 3.5 企微通知集成
部署完成后自动发送企微消息:
```
✅ Hotfix #XXX 部署成功
目标: 10.90.5.110
耗时: 8m 23s
验证: curl 返回 200 OK
日志: webcli_output/v10_result.txt
```
---
### 🟢 P2 — 长期优化
| 优化项 | 说明 | 依赖 |
|--------|------|------|
| **SSH Key 认证(v17** | 绕过 OTP 流程,实现无人值守 | JumpServer 管理员配置 |
| **API Token 方式** | 直接通过 Koko WebSocket 通信 | JumpServer API 权限 |
| **部署包 pip 化** | `pip install jumpserver-webcli` | 路径去硬编码 |
| **Docker 化执行环境** | Chrome + Playwright 容器化 | headless 模式 |
| **部署历史数据库** | SQLite 记录每次部署 | 输出结构化捕获 |
| **回滚自动化** | 失败自动回滚到上一个版本 | 部署流水线引擎 |
---
## 四、技术路线优先级矩阵
```
紧急度
高 ●●● │ 低 ●○○
┌──────────┼──────────┐
高 ●●●│ xterm.js │ 部署流水 │
│ 输出捕获 │ 线引擎 │
影响面 ├──────────┼──────────┤
│ headless │ SSH Key │
低 ●○○│ 适配 │ 认证 │
└──────────┴──────────┘
```
## 五、总结
JP-webcli 自动化工具已经通过了 4 次生产部署验证,核心链路(登录 → 导航 → 命令执行 → 文件传输)稳定可用。当前最大的瓶颈是 **xterm.js 输出无法结构化捕获**,导致:
1. 所有验证依赖截图(不可解析)
2. 部署成功/失败无法自动判定
3. 脚本过多(单个 hotfix 分裂为 18 个)
4. 无法集成 CI/CD
**建议立即启动 xterm.js buffer 探测**(估计 1-2 小时),确认 JumpServer Luna 中 xterm 实例的引用路径后,一周内完成 `send_and_capture()` 改造。这将使自动化覆盖率从当前的 ~40% 提升到 ~90%。
@@ -442,9 +442,10 @@ bash scripts/security-audit.sh --secrets
### 5.4 长期方案(下季度)
1. **NAS Vault**:用 Synology 的「密码保险箱」存关键 secret
2. **Server Keyring**:用 systemd-creds / HashiCorp Vault
3. **环境变量注入**:容器启动时从 vault 拉,不入镜像
> ⚠️ NAS 部署方案已下线
1. **Server Keyring**:用 systemd-creds / HashiCorp Vault(当前推荐)
2. **环境变量注入**:容器启动时从 vault 拉,不入镜像
---
+64
View File
@@ -0,0 +1,64 @@
# 03-技术架构 目录说明
> 本目录存放 IT 智能服务台项目的技术架构相关文档。
## 目录结构
```
03-技术架构/
├── 00-系统架构设计文档-v1.3.md # 主文档:系统架构总览(v1.3)
├── 01-ADRs-架构决策/ # 架构决策记录 (ADR)
├── 02-技术方案/ # 技术方案文档
├── 03-技术分析/ # 技术分析报告
├── 04-数据库设计/ # 数据库设计
├── 05-架构图/ # Mermaid 图表
└── archive/ # 归档目录:已废弃/旧版文档
```
## 文档清单
### 01-ADRs-架构决策/
| 文档 | 说明 |
|------|------|
| ADR-001-Gitea自托管-Funnel暴露.md | 架构决策 #001 |
| ADR-002-WS-Token-Subprotocol鉴权.md | 架构决策 #002 |
| ADR-003-nginx-access_log关闭.md | 架构决策 #003 |
| ADR-004-Token不入文件-走wincred.md | 架构决策 #004 |
### 02-技术方案/
| 文档 | 说明 |
|------|------|
| 技术方案-ExternalSystemAdapter抽象层.md | 外部系统适配器设计 |
| 技术方案-消息功能详细设计.md | 消息功能详细设计 |
| 技术方案-摇人协作.md | 摇人功能技术方案 |
| 技术方案-邀请功能.md | 邀请功能技术方案 |
| 技术方案-复杂场景重构.md | 复杂场景重构方案 |
### 03-技术分析/
| 文档 | 说明 |
|------|------|
| 技术分析-架构消息知识库迭代.md | 架构消息知识库迭代分析 |
| 技术分析-H5右侧栏动态推送评估.md | H5右侧栏动态推送评估 |
| 技术分析-jp-webcli自动化部署能力分析.md | JumpServer自动化部署分析 |
### 04-数据库设计/
| 文档 | 说明 |
|------|------|
| 数据库设计-ER图与环境变量清点.md | 数据库ER图与环境变量 |
### 05-架构图/
| 文档 | 说明 |
|------|------|
| class-diagram.mermaid | 核心类图 |
| sequence-diagram.mermaid | 核心序列图 |
| sequence-scoring.mermaid | 评分序列图 |
| sequence-polling.mermaid | 轮询序列图 |
| sequence-shake.mermaid | 摇人序列图 |
| admin-class-diagram.mermaid | 管理端类图 |
| admin-login-sequence.mermaid | 管理端登录序列图 |
| admin-config-change-sequence.mermaid | 配置变更序列图 |
| admin-quick-reply-review-sequence.mermaid | 快速回复审核序列图 |
---
*最后更新: 2026-07-04*
@@ -0,0 +1,43 @@
# H5用户端原型图 → Vue3代码实现概览
## 完成时间
2026-06-09
## 变更摘要
根据已锁定的原型图 v1.1 修复版,将 H5 用户端设计实现为 Vue3 代码。
## 修改文件清单
### 1. `frontend-h5/src/components/chat/ChatPanel.vue`
- **标题栏重构**:左侧(标题 + 坐席在线/离线状态胶囊) + 右侧(🔔呼叫按钮 + 主题切换)
- **🔔摇铃按钮**:从输入栏移至标题栏(桌面端+手机端统一)
- **排查步骤固定顶部**:从消息列表内移出,固定在标题栏下方、所有消息之上,不随滚动消失
- **移除 InputBar 事件**:不再需要 @call-agent 事件(摇铃直接在 ChatPanel 内控制)
### 2. `frontend-h5/src/components/chat/InputBar.vue`
- **移除摇铃按钮**:删除 🔔 摇铃按钮及相关 CSSbell-btn/bell-icon/bell-idle/bell-ring 动画)
- **新增工具栏**:😊表情 / 🖼️图片 / 📎文件 / 📸拍照(4个圆形按钮)
- **布局改为两行**:工具栏(上) + 输入行(输入框+发送按钮)(下)
- **新增方法**handleEmoji/handleImage/handleFile/handleCamera(阶段二实现具体功能)
- **引导条文案更新**:"点击标题栏铃铛呼叫 IT 坐席"
### 3. `frontend-h5/src/components/assistant/RightPanel.vue`(新建)
- **三段式面板**:AI推送区 / 常用资源标签页 / 趣味问答
- **AI推送区**3种卡片类型(guide/process/download) + 动态图标+颜色
- **常用资源**:2个Tab(申请流程/必装软件) + 资源列表
- **趣味问答**:题目+4选项+积分+答题结果反馈
- **阶段一静态数据**,阶段二接入 Dify 动态推送
### 4. `frontend-h5/src/views/ChatView.vue`
- **替换右侧面板**AiHelperPanel → RightPanel(三段式面板)
- **响应式断点**:从768px改为500px(与原型图对齐)
- **移动端**<500px 不显示右侧面板
- **拖拽逻辑修复**:只固定左侧宽度,右侧 flex:1 自动填满(消除拖拽后空白)
- **移除浮动按钮**:不再需要移动端AI助手浮动按钮
### 5. `frontend-h5/src/stores/conversation.ts`
- **新增 agentOnline 状态**:默认true,阶段一简化处理
- **暴露到 return 语句**:使组件可以访问
## 构建验证
`npx vite build` 构建成功,无编译错误
@@ -0,0 +1,236 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IT智能服务台 - 管理员登录</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/font-awesome@6.4.0/css/all.min.css" rel="stylesheet">
<style>
:root {
--primary: #07C160;
--primary-dark: #05964a;
--bg: #f5f6f7;
--card-bg: #ffffff;
--text: #171a1d;
--text-secondary: #858e99;
--border: #e7e8eb;
--danger: #e74c3c;
}
body {
background: var(--bg);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.login-card {
width: 100%;
max-width: 420px;
background: var(--card-bg);
border-radius: 12px;
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
padding: 40px;
}
.logo {
text-align: center;
margin-bottom: 32px;
}
.logo-icon {
width: 56px;
height: 56px;
background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
border-radius: 12px;
display: inline-flex;
align-items: center;
justify-content: center;
color: white;
font-size: 24px;
font-weight: bold;
margin-bottom: 12px;
}
.logo-title {
font-size: 20px;
font-weight: 600;
color: var(--text);
margin-bottom: 4px;
}
.logo-subtitle {
font-size: 13px;
color: var(--text-secondary);
}
.form-label {
font-size: 14px;
font-weight: 500;
color: var(--text);
margin-bottom: 8px;
}
.form-control {
height: 44px;
border: 1px solid var(--border);
border-radius: 8px;
font-size: 14px;
padding: 0 14px;
}
.form-control:focus {
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(7,193,96,0.1);
}
.input-group-text {
background: transparent;
border-left: none;
color: var(--text-secondary);
}
.btn-primary {
background: var(--primary);
border: none;
height: 44px;
border-radius: 8px;
font-size: 15px;
font-weight: 500;
}
.btn-primary:hover {
background: var(--primary-dark);
}
.divider {
display: flex;
align-items: center;
margin: 24px 0;
color: var(--text-secondary);
font-size: 13px;
}
.divider::before, .divider::after {
content: '';
flex: 1;
height: 1px;
background: var(--border);
}
.divider span {
padding: 0 16px;
}
.wecom-btn {
width: 100%;
height: 44px;
border: 1px solid var(--border);
border-radius: 8px;
background: white;
font-size: 14px;
font-weight: 500;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
cursor: pointer;
transition: all 0.2s;
}
.wecom-btn:hover {
border-color: var(--primary);
background: rgba(7,193,96,0.05);
}
.wecom-btn i {
color: var(--primary);
font-size: 18px;
}
.otp-hint {
font-size: 12px;
color: var(--text-secondary);
margin-top: 8px;
}
.otp-hint a {
color: var(--primary);
text-decoration: none;
}
.warning-box {
background: #fef3e8;
border: 1px solid #f5c6a5;
border-radius: 8px;
padding: 12px 16px;
margin-bottom: 20px;
display: flex;
align-items: flex-start;
gap: 10px;
}
.warning-box i {
color: #e67e22;
margin-top: 2px;
}
.warning-box-text {
font-size: 13px;
color: #d35400;
line-height: 1.5;
}
.footer {
text-align: center;
margin-top: 24px;
font-size: 12px;
color: var(--text-secondary);
}
</style>
</head>
<body>
<div class="login-card">
<div class="logo">
<div class="logo-icon">IT</div>
<div class="logo-title">IT智能服务台</div>
<div class="logo-subtitle">管理后台 · 登录</div>
</div>
<div class="warning-box">
<i class="fas fa-shield-alt"></i>
<div class="warning-box-text">
管理员账号需要二次验证,请确保您已绑定OTP
</div>
</div>
<form>
<div class="mb-3">
<label class="form-label">用户名</label>
<input type="text" class="form-control" placeholder="请输入用户名" id="username">
</div>
<div class="mb-3">
<label class="form-label">密码</label>
<div class="input-group">
<input type="password" class="form-control" placeholder="请输入密码" id="password" style="border-right: none;">
<span class="input-group-text"><i class="fas fa-eye-slash"></i></span>
</div>
</div>
<div class="mb-3">
<label class="form-label">OTP 验证码 <span style="color: var(--danger);">*</span></label>
<div class="row">
<div class="col-7">
<input type="text" class="form-control" placeholder="6位数字" maxlength="6" id="otp" required>
</div>
<div class="col-5">
<div class="otp-image" style="height: 44px; background: #f5f6f7; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; font-family: monospace; font-size: 18px; letter-spacing: 4px;">
●● ●●
</div>
</div>
</div>
<div class="otp-hint">
打开 Google Authenticator 获取验证码
</div>
</div>
<button type="submit" class="btn btn-primary w-100">登录</button>
</form>
<div class="divider">
<span></span>
</div>
<button class="wecom-btn">
<i class="fab fa-weixin"></i>
使用企业微信扫码登录
</button>
<div class="footer">
<a href="#" style="color: var(--text-secondary); text-decoration: none;">遇到问题?联系管理员</a>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
@@ -0,0 +1,164 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IT智能服务台 - 选择登录方式</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/font-awesome@6.4.0/css/all.min.css" rel="stylesheet">
<style>
:root {
--primary: #07C160;
--primary-dark: #05964a;
--bg: #f5f6f7;
--card-bg: #ffffff;
--text: #171a1d;
--text-secondary: #858e99;
--border: #e7e8eb;
--accent: #667eea;
}
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.choice-card {
width: 100%;
max-width: 440px;
background: var(--card-bg);
border-radius: 16px;
box-shadow: 0 8px 32px rgba(0,0,0,0.15);
padding: 40px;
text-align: center;
}
.choice-icon {
width: 72px;
height: 72px;
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
color: white;
font-size: 32px;
margin-bottom: 20px;
}
.choice-title {
font-size: 20px;
font-weight: 600;
color: var(--text);
margin-bottom: 8px;
}
.choice-desc {
font-size: 14px;
color: var(--text-secondary);
margin-bottom: 28px;
line-height: 1.6;
}
.choice-btn {
width: 100%;
height: 52px;
border-radius: 10px;
font-size: 15px;
font-weight: 500;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
cursor: pointer;
transition: all 0.2s;
margin-bottom: 12px;
}
.choice-btn-wechat {
background: linear-gradient(135deg, #07C160 0%, #05964a 100%);
border: none;
color: white;
}
.choice-btn-wechat:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(7,193,96,0.3);
}
.choice-btn-browser {
background: white;
border: 1px solid var(--border);
color: var(--text);
}
.choice-btn-browser:hover {
border-color: var(--primary);
background: rgba(7,193,96,0.05);
}
.choice-btn i {
font-size: 18px;
}
.choice-divider {
display: flex;
align-items: center;
margin: 20px 0;
color: var(--text-secondary);
font-size: 12px;
}
.choice-divider::before, .choice-divider::after {
content: '';
flex: 1;
height: 1px;
background: var(--border);
}
.choice-divider span {
padding: 0 12px;
}
.footer {
font-size: 12px;
color: var(--text-secondary);
margin-top: 24px;
}
.footer a {
color: var(--primary);
text-decoration: none;
}
</style>
</head>
<body>
<div class="choice-card">
<div class="choice-icon">
<i class="fas fa-check"></i>
</div>
<div class="choice-title">检测到您正在企业微信中</div>
<div class="choice-desc">
请选择您偏好的登录方式
</div>
<button class="choice-btn choice-btn-wechat">
<i class="fab fa-weixin"></i>
在企业微信中打开
</button>
<div style="font-size: 12px; color: var(--text-secondary); margin-bottom: 12px;">
在企业微信客户端中打开,无需再次验证
</div>
<div class="choice-divider">
<span></span>
</div>
<button class="choice-btn choice-btn-browser">
<i class="fas fa-globe"></i>
继续在浏览器中登录
</button>
<div style="font-size: 12px; color: var(--text-secondary);">
使用账号密码 + OTP 验证登录
</div>
<div class="footer">
<a href="#">什么是 OTP</a> · <a href="#">遇到问题?</a>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
@@ -0,0 +1,209 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IT智能服务台 - 坐席登录</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/font-awesome@6.4.0/css/all.min.css" rel="stylesheet">
<style>
:root {
--primary: #07C160;
--primary-dark: #05964a;
--bg: #f5f6f7;
--card-bg: #ffffff;
--text: #171a1d;
--text-secondary: #858e99;
--border: #e7e8eb;
}
body {
background: var(--bg);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.login-card {
width: 100%;
max-width: 420px;
background: var(--card-bg);
border-radius: 12px;
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
padding: 40px;
}
.logo {
text-align: center;
margin-bottom: 32px;
}
.logo-icon {
width: 56px;
height: 56px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 12px;
display: inline-flex;
align-items: center;
justify-content: center;
color: white;
font-size: 24px;
font-weight: bold;
margin-bottom: 12px;
}
.logo-title {
font-size: 20px;
font-weight: 600;
color: var(--text);
margin-bottom: 4px;
}
.logo-subtitle {
font-size: 13px;
color: var(--text-secondary);
}
.form-label {
font-size: 14px;
font-weight: 500;
color: var(--text);
margin-bottom: 8px;
}
.form-control {
height: 44px;
border: 1px solid var(--border);
border-radius: 8px;
font-size: 14px;
padding: 0 14px;
}
.form-control:focus {
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(7,193,96,0.1);
}
.input-group-text {
background: transparent;
border-left: none;
color: var(--text-secondary);
}
.btn-primary {
background: var(--primary);
border: none;
height: 44px;
border-radius: 8px;
font-size: 15px;
font-weight: 500;
}
.btn-primary:hover {
background: var(--primary-dark);
}
.divider {
display: flex;
align-items: center;
margin: 24px 0;
color: var(--text-secondary);
font-size: 13px;
}
.divider::before, .divider::after {
content: '';
flex: 1;
height: 1px;
background: var(--border);
}
.divider span {
padding: 0 16px;
}
.wecom-btn {
width: 100%;
height: 44px;
border: 1px solid var(--border);
border-radius: 8px;
background: white;
font-size: 14px;
font-weight: 500;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
cursor: pointer;
transition: all 0.2s;
}
.wecom-btn:hover {
border-color: var(--primary);
background: rgba(7,193,96,0.05);
}
.wecom-btn i {
color: var(--primary);
font-size: 18px;
}
.otp-hint {
font-size: 12px;
color: var(--text-secondary);
margin-top: 8px;
}
.otp-hint a {
color: var(--primary);
text-decoration: none;
}
.footer {
text-align: center;
margin-top: 24px;
font-size: 12px;
color: var(--text-secondary);
}
</style>
</head>
<body>
<div class="login-card">
<div class="logo">
<div class="logo-icon">IT</div>
<div class="logo-title">IT智能服务台</div>
<div class="logo-subtitle">坐席工作台 · 登录</div>
</div>
<form>
<div class="mb-3">
<label class="form-label">用户名</label>
<input type="text" class="form-control" placeholder="请输入用户名" id="username">
</div>
<div class="mb-3">
<label class="form-label">密码</label>
<div class="input-group">
<input type="password" class="form-control" placeholder="请输入密码" id="password" style="border-right: none;">
<span class="input-group-text"><i class="fas fa-eye-slash"></i></span>
</div>
</div>
<div class="mb-3">
<label class="form-label">OTP 验证码</label>
<div class="row">
<div class="col-7">
<input type="text" class="form-control" placeholder="6位数字" maxlength="6" id="otp">
</div>
<div class="col-5">
<div class="otp-image" style="height: 44px; background: #f5f6f7; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; font-family: monospace; font-size: 18px; letter-spacing: 4px;">
●● ●●
</div>
</div>
</div>
<div class="otp-hint">
打开 Google Authenticator / 微信扫码 获取验证码
</div>
</div>
<button type="submit" class="btn btn-primary w-100">登录</button>
</form>
<div class="divider">
<span></span>
</div>
<button class="wecom-btn">
<i class="fab fa-weixin"></i>
使用企业微信扫码登录
</button>
<div class="footer">
<a href="#" style="color: var(--text-secondary); text-decoration: none;">遇到问题?联系管理员</a>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
@@ -0,0 +1,31 @@
# 用户手册索引
> **版本**: v1.0 | **日期**: 2026-07-04
---
## 手册清单
| 手册 | 文件 | 说明 |
|------|------|------|
| 员工使用指南 | `01-员工使用指南.md` | 普通员工使用IT智能服务台的操作指南 |
| 坐席操作手册 | `02-坐席操作手册.md` | IT运维人员处理会话的操作指南 |
| 管理员手册 | `03-管理员手册.md` | 系统管理员配置管理的操作指南 |
---
## 快速导航
### 员工 → 查看 `01-员工使用指南.md`
### 坐席 → 查看 `02-坐席操作手册.md`
### 管理员 → 查看 `03-管理员手册.md`
---
## 更新日志
| 版本 | 日期 | 变更 |
|------|------|------|
| v1.0 | 2026-07-04 | 初始版本 |
@@ -0,0 +1,110 @@
# IT智能服务台 — 员工使用指南
> **版本**: v1.0 | **日期**: 2026-07-04 | **维护人**: 助理
---
## 目录
1. [系统简介](#1-系统简介)
2. [访问入口](#2-访问入口)
3. [发起咨询](#3-发起咨询)
4. [转人工服务](#4-转人工服务)
5. [查看历史记录](#5-查看历史记录)
6. [满意度评价](#6-满意度评价)
7. [常见问题](#7-常见问题)
---
## 1. 系统简介
IT智能服务台是公司为员工提供的IT问题一站式解决平台,具备以下能力:
| 功能 | 说明 |
|------|------|
| **智能问答** | AI自动回答常见IT问题 |
| **转人工服务** | 一键呼叫IT坐席获得人工帮助 |
| **问题跟踪** | 实时查看处理进度 |
| **满意度评价** | 服务结束后评价服务质量 |
---
## 2. 访问入口
### 2.1 企业微信入口
1. 打开企业微信
2. 点击底部「工作台」
3. 找到「IT智能服务台」应用
4. 点击进入
### 2.2 直接访问
如已获得授权,也可直接访问:https://itsupport.servyou.com.cn/itdesk/
---
## 3. 发起咨询
### 3.1 发送消息
1. 在对话框中输入您的问题
2. 点击发送按钮
3. AI将自动回答您的问题
### 3.2 上传图片/文件
支持上传以下格式:
- 图片:jpg、png、gif
- 文件:doc、docx、pdf、xls、xlsx(单个文件≤10MB
---
## 4. 转人工服务
当AI无法解决您的问题时,您可以:
### 4.1 摇人按钮
- 点击输入框左侧的「摇人」按钮
- 系统将为您转接IT坐席
### 4.2 多次对话后自动转接
- 当与AI对话超过3轮仍未解决问题时
- 系统会自动提示是否需要转人工
---
## 5. 查看历史记录
- 在会话列表中查看历史咨询记录
- 点击任意历史会话可查看详情
---
## 6. 满意度评价
- 每次会话结束后
- 系统会弹出评分窗口
- 请根据实际体验给予1-5星评价
---
## 7. 常见问题
### Q1: 为什么会提示"转人工"
A: 当AI连续回复3次仍未能解决您的问题时,系统会自动提示转人工。您也可以随时点击"摇人"按钮主动转接人工服务。
### Q2: 上班时间没有坐席响应怎么办?
A: 请耐心等待,当前没有空闲坐席时,系统会为您排队。一旦有坐席接单,您将收到通知。
### Q3: 如何查看我的IT问题处理进度?
A: 在IT智能服务台首页,点击「我的工单」即可查看当前处理中的问题和历史记录。
---
*如有其他问题,请联系IT部门。*
@@ -0,0 +1,169 @@
# IT智能服务台 — 坐席操作手册
> **版本**: v1.0 | **日期**: 2026-07-04 | **维护人**: 助理
---
## 目录
1. [系统简介](#1-系统简介)
2. [访问入口](#2-访问入口)
3. [会话管理](#3-会话管理)
4. [回复用户](#4-回复用户)
5. [快速回复](#5-快速回复)
6. [转接与协作](#6-转接与协作)
7. [会话标记](#7-会话标记)
8. [结单处理](#8-结单处理)
9. [常见问题](#9-常见问题)
---
## 1. 系统简介
IT智能服务台坐席端是IT运维人员处理员工IT问题的工作台,具备以下能力:
| 功能 | 说明 |
|------|------|
| **会话列表** | 按紧急度排序的待处理会话 |
| **AI辅助** | AI推荐回复内容供参考 |
| **快速回复** | 常用语一键发送 |
| **摇人协作** | 邀请其他坐席协助处理 |
---
## 2. 访问入口
### 2.1 企业微信入口
1. 打开企业微信
2. 点击底部「工作台」
3. 找到「IT智能服务台」应用
4. 点击进入(系统自动识别坐席身份)
### 2.2 直接访问
如已获得授权,也可直接访问:https://itsupport.servyou.com.cn/itagent/
---
## 3. 会话管理
### 3.1 会话列表
会话列表按以下优先级排序:
| 优先级 | 说明 |
|--------|------|
| 🔴 紧急 | 需要立即处理 |
| 🟠 举手 | 用户主动呼叫坐席 |
| 🟡 需介入 | AI无法处理,等待人工 |
| 🟢 活跃 | 正在对话中 |
| 🔵 AI处理中 | AI正在回答 |
| ⚪ 已结单 | 已完成处理 |
### 3.2 接单
- 点击会话列表中的任意会话
- 即可开始处理
---
## 4. 回复用户
### 4.1 手动输入
1. 在底部输入框输入回复内容
2. 点击发送按钮
### 4.2 使用AI建议
- 右侧面板显示AI推荐回复
- 点击「采纳」直接使用
- 点击「编辑」修改后使用
- 点击「忽略」不使用
---
## 5. 快速回复
### 5.1 使用快速回复
1. 点击输入框上方的「快捷回复」按钮
2. 在弹出的列表中选择常用语
3. 点击后自动填充到输入框
### 5.2 管理快速回复
- 点击「管理」可添加/编辑/删除常用语
- 快速回复支持分类管理
---
## 6. 转接与协作
### 6.1 摇人(邀请协作)
当需要其他坐席协助时:
1. 点击会话详情中的「摇人」按钮
2. 选择要邀请的坐席
3. 发送邀请请求
### 6.2 接受邀请
- 收到邀请后,会话列表会显示提醒
- 点击即可加入协作会话
---
## 7. 会话标记
### 7.1 添加标签
- 在会话详情中点击「添加标签」
- 选择预设标签或自定义标签
### 7.2 预设标签
| 标签 | 说明 |
|------|------|
| 网络问题 | 网络连接相关 |
| 软件问题 | 软件使用相关 |
| 硬件问题 | 设备故障相关 |
| 账号问题 | 账号权限相关 |
| 其他 | 其他问题类型 |
---
## 8. 结单处理
### 8.1 结单
- 确认用户问题已解决后
- 点击「结单」按钮
- 系统将发送满意度评价邀请
### 8.2 结单注意事项
- 结单前确保用户问题已完全解决
- 如需后续跟进,可创建工单
---
## 9. 常见问题
### Q1: 会话列表不显示新会话?
A: 请检查网络连接,或点击刷新按钮手动刷新。
### Q2: AI建议不准确怎么办?
A: 可以点击「忽略」不使用AI建议,手动输入回复。您的反馈将帮助AI学习改进。
### Q3: 如何查看历史会话统计?
A: 在管理后台的「数据看板」中查看会话量、响应时间等统计信息。
---
*如有其他问题,请联系系统管理员。*
+215
View File
@@ -0,0 +1,215 @@
# IT智能服务台 — 管理员手册
> **版本**: v1.0 | **日期**: 2026-07-04 | **维护人**: 助理
---
## 目录
1. [系统简介](#1-系统简介)
2. [访问入口](#2-访问入口)
3. [坐席管理](#3-坐席管理)
4. [角色权限](#4-角色权限)
5. [系统配置](#5-系统配置)
6. [功能开关](#6-功能开关)
7. [数据看板](#7-数据看板)
8. [集成管理](#8-集成管理)
9. [安全设置](#9-安全设置)
10. [运维监控](#10-运维监控)
---
## 1. 系统简介
IT智能服务台管理后台是系统管理员的配置管理中心,主要功能包括:
| 功能 | 说明 |
|------|------|
| **坐席管理** | 添加、编辑、删除坐席账户 |
| **角色权限** | 配置RBAC细粒度权限 |
| **系统配置** | 业务参数设置 |
| **功能开关** | 控制功能上线/下线 |
| **数据看板** | 查看运营数据统计 |
| **集成管理** | 第三方系统对接配置 |
| **安全设置** | 认证、授权、审计配置 |
---
## 2. 访问入口
### 2.1 访问地址
管理后台访问地址:https://itsupport.servyou.com.cn/itadmin/
### 2.2 登录要求
- 仅限管理员角色访问
- 需要OTP二次验证(已绑定OTP的管理员)
---
## 3. 坐席管理
### 3.1 查看坐席列表
在「坐席管理」页面查看所有坐席账户:
| 列 | 说明 |
|----|------|
| 姓名 | 坐席显示名称 |
| 用户ID | 企业微信用户ID |
| 状态 | 在线/离线/忙碌 |
| OTP | 是否已绑定二次验证 |
| 添加时间 | 账户创建时间 |
### 3.2 添加坐席
1. 点击「添加坐席」按钮
2. 输入坐席信息(姓名、用户ID等)
3. 设置角色权限
4. 点击保存
### 3.3 编辑/删除坐席
- 点击坐席行的「编辑」可修改信息
- 点击「删除」可移除坐席(需确认)
---
## 4. 角色权限
### 4.1 角色类型
| 角色 | 说明 | 数据范围 |
|------|------|---------|
| 超级管理员 | 拥有全部权限 | 全部 |
| 坐席组长 | 管理本组坐席 | 本组数据 |
| 普通坐席 | 处理会话 | 负责的会话 |
| 审计员 | 仅查看数据 | 全部(只读) |
| 用户 | 普通员工 | 自己的会话 |
### 4.2 权限配置
每个角色可配置以下资源权限:
| 资源 | 操作 |
|------|------|
| 会话 | 查看、回复、结单、转接 |
| 用户 | 查看、编辑 |
| 坐席 | 查看、添加、编辑、删除 |
| 配置 | 查看、修改 |
| 数据 | 导出、统计 |
---
## 5. 系统配置
### 5.1 业务参数
| 参数 | 说明 | 默认值 |
|------|------|-------|
| AI转人工轮数 | AI回复多少轮后允许转人工 | 3轮 |
| 响应超时 | 坐席未响应超时时间 | 5分钟 |
| 会话保持时间 | 会话未活动自动结单时间 | 24小时 |
| 满意度评分 | 是否开启评分功能 | 开启 |
---
## 6. 功能开关
### 6.1 可控制的功能
| 功能组 | 功能 | 说明 |
|--------|------|------|
| queue_ | 排队系统 | 启用/禁用排队机制 |
| satisfaction_ | 满意度评价 | 启用/禁用评分功能 |
| invite_ | 邀请协作 | 启用/禁用摇人功能 |
| notification_ | 消息通知 | 启用/禁用企微推送 |
| security_ | 安全验证 | 启用/禁用OTP验证 |
### 6.2 操作
- 点击开关即可启用/禁用对应功能
- 状态变更即时生效
---
## 7. 数据看板
### 7.1 核心指标
| 指标 | 说明 |
|------|------|
| 会话总量 | 统计周期内的总会话数 |
| AI解决率 | AI直接解决问题的比例 |
| 平均响应时间 | 从会话创建到首次响应的平均时间 |
| 满意度评分 | 用户评价的平均分 |
### 7.2 趋势图
- 支持按日/周/月查看数据趋势
- 支持导出为Excel
---
## 8. 集成管理
### 8.1 已集成系统
| 系统 | 集成方式 | 状态 |
|------|---------|------|
| RAGFlow | API Key | 已配置 |
| Dify | API Key | 已配置 |
| 火绒安全 | AccessKey | 已配置 |
| 联软 | 账号密码 | 已配置 |
| aTrust | 待配置 | 待配置 |
### 8.2 配置集成
1. 选择要配置的系统
2. 输入对应的凭据信息
3. 点击「测试连接」验证
4. 保存配置
---
## 9. 安全设置
### 9.1 OTP管理
- 查看坐席OTP绑定状态
- 强制解绑OTP(管理员操作)
- 设置强制OTP策略
### 9.2 IP白名单
- 配置管理后台访问IP白名单
- 支持CIDR格式
### 9.3 操作审计
- 查看所有管理操作日志
- 支持按时间、操作类型筛选
---
## 10. 运维监控
### 10.1 系统健康
| 指标 | 状态 |
|------|------|
| 后端服务 | 正常/异常 |
| 数据库 | 正常/异常 |
| Redis | 正常/异常 |
| WebSocket | 正常/异常 |
### 10.2 日志查看
- 访问「日志查看」页面
- 按时间、级别筛选日志
- 支持下载日志文件
---
*如有其他问题,请联系系统开发团队。*
@@ -0,0 +1,110 @@
# B-T10 代码评审报告
## 评审概述
- **任务**: B-T10 代码评审 + PR 创建
- **评审范围**: 消息可靠性增强功能
- **评审日期**: 2026-07-03
- **评审人**: 软件工程师
## 评审范围
### 1. WebSocket 重连机制
- **文件**: `frontend-h5/src/composables/useH5WebSocket.ts`
- **文件**: `frontend-agent/src/composables/useWebSocket.ts`
**评审结果**: ✅ 通过
**优点**:
- 使用指数退避算法(1s → 2s → 4s → 8s → 16s → 30s 上限)
- 实现心跳保活机制(30 秒间隔)
- 自动降级到 HTTP 轮询
- 连接断开时自动重连
### 2. 离线消息补偿
- **文件**: `backend/app/api/messages.py` (poll 接口)
- **文件**: `frontend-h5/src/stores/conversation.ts`
**评审结果**: ✅ 通过
**实现**:
- 后端提供 `/api/conversations/{id}/messages/poll` 接口
- 支持 `after_message_id` 参数获取增量消息
- 前端维护 `lastMessageId` 实现断线重连后的消息补偿
### 3. 消息时序保证
- **文件**: `backend/app/api/messages.py`
- **字段**: `server_timestamp` (毫秒级)
**评审结果**: ✅ 通过
**实现**:
- 消息入库时记录 `server_timestamp`
- 前端按 `server_timestamp` 排序
- 基于 `message_id` 去重
### 4. 已读同步机制
- **文件**: `backend/app/api/messages.py` (/mark-read)
- **文件**: `frontend-h5/src/stores/conversation.ts`
**评审结果**: ✅ 通过
**实现**:
- 后端提供 `POST /api/conversations/{id}/mark-read`
- WebSocket 广播已读状态变更
- 字段 `is_read` 标记消息已读状态
### 5. 文件上传重试
- **文件**: `frontend-h5/src/api/upload.ts`
**评审结果**: ⚠️ 需要改进
**当前实现**:
- 单次上传,无重试机制
- 60 秒超时配置
**建议**:
- 添加自动重试机制(最多 3 次)
- 实现指数退避(1s → 2s → 4s)
### 6. Nginx 配置优化
- **文件**: `nginx/nginx-root.conf`
**评审结果**: ✅ 通过
**配置**:
```nginx
proxy_read_timeout 3600s; # 1 小时
proxy_send_timeout 3600s; # 1 小时
```
## 代码质量评估
### 命名规范 ✅
- 所有函数和变量命名清晰,符合 TypeScript/Python 命名规范
- 注释完整,说明了实现原理
### 安全性 ✅
- WebSocket 认证使用 Sec-WebSocket-Protocol 传递 token
- 后端 API 有权限控制(RBAC)
### 性能 ✅
- 心跳间隔 30 秒合理
- 轮询降级机制减少资源消耗
### 可维护性 ✅
- 模块化设计清晰
- WebSocket 逻辑封装在 composable 中
## 发现的问题
### 问题 1: 文件上传缺少重试机制
- **严重程度**: 中
- **位置**: `frontend-h5/src/api/upload.ts`
- **描述**: 上传失败时没有重试机制
- **建议**: 添加 3 次重试,指数退避
## 总体结论
**IS_PASS: YES**
除文件上传重试机制需要完善外,其他功能均已正确实现并通过评审。
@@ -28,24 +28,23 @@
| 方案 | 复杂度 | 安全性 | 适用场景 |
|------|--------|--------|----------|
| **NAS Vault** | 低 | 中 | 有 NAS设备 |
| **Server Keyring** | 低 | 中 | Linux 服务器 |
| **Server Keyring** | 低 | 中 | Linux 服务器(当前使用) |
| **Docker Secrets** | 中 | 高 | K8s/ Swarm |
| **HashiCorp Vault** | 高 | 高 | 企业级 |
### 推荐:NAS Vault + Server Keyring
### 推荐:Server Keyring
#### 方案1NAS Vault当前用)
#### 方案1Server KeyringLinux当前使用)
> ⚠️ NAS 部署方案已下线,当前使用公司内网服务器部署
```bash
# 在 NAS 上创建加密文件
/volume1/docker/wecom-it-desk/secrets/.env.encrypted
# 启动时解密
docker run --env-file <(gpg -d /volume1/docker/wecom-it-desk/secrets/.env.encrypted) ...
# 使用 keyring 工具
keyring set wecom-it-desk WECOM_SECRET
keyring get wecom-it-desk WECOM_SECRET
```
#### 方案2Server KeyringLinux
#### 方案2Docker Secrets(备选
```bash
# 使用 keyring 工具
@@ -71,7 +70,7 @@ keyring get wecom-it-desk WECOM_SECRET
| 阶段 | 内容 | 优先级 |
|------|------|--------|
| MVP | 当前方案 + 限制文件权限 | P0 |
| V1 | 迁移到 NAS Vault | P2 |
| V1 | 迁移到 Server Keyring | P2 |
| V2 | 迁移到 HashiCorp Vault | P3 |
---
@@ -594,7 +594,7 @@ async def request_id_middleware(request: Request, call_next):
| **CloudWatch / 阿里云 SLS** | 公有云 | 🟡 看云 |
**短期**: 走 stdout,Docker 收集到 `/var/log/wecom-it-desk/*.log`,脚本 + grep 查
**中期**: Loki + Grafana(本地 NAS 部署)
**中期**: Loki + Grafana(本地服务器部署)
**长期**: ELK / 云原生日志
---
@@ -0,0 +1,185 @@
# Release Notes v0.7.1
> 📅 发布日期:2026-06-23 | 类型:🔐 安全 + ✨ 功能 | 紧急度:🟡 重要
> 🔄 补充:2026-06-24 hotfix #116/#118/#119/#120 已部署(详见文末"补充 hotfix"小节)
## 🎯 本版本重点
1. **修复扫码登录 iOS NSURLErrorCannotFindHost** — 企微 App 用户扫码不再报错
2. **完整 MFA + RBAC 权限体系** — 5 角色细粒度权限 + 高危操作二次认证
3. **H5 员工端企微 OAuth 登录** — 员工不再需要输账号密码
4. **🆕 扫码登录去掉手机 confirm 步骤** — 企微 App 无确认 UI,扫码成功 = 直接登录
## 🔐 安全修复
### #109 P0:扫码登录 NSURLError 修复
**问题**:用户企微 App 访问 `https://itsupport.servyou.com.cn/itportal/`,扫描二维码后跳转报 `NSURLErrorCannotFindHost`(iOS WebView DNS 解析失败)
**根因**:`backend/app/config.py` 新字段 `qrcode_oauth_callback` 用了 pydantic 默认行为,**不剥环境变量前缀**
- 字段名:`qrcode_oauth_callback`
- pydantic-settings 期望 env:`QRCODE_OAUTH_CALLBACK`
- 实际 env:`WECOM_QRCODE_CALLBACK`(带前缀,匹配不上)
- 结果:`settings.qrcode_oauth_callback = ""`,qrcode_service 走兜底返回**相对路径** `/api/auth_qrcode/scan`
- iOS WebView 拼接相对路径失败 → DNS 解析错误
**修复**:
```python
from pydantic import Field
class Settings(BaseSettings):
qrcode_oauth_callback: str = Field(
default="",
validation_alias="WECOM_QRCODE_CALLBACK",
)
```
**部署**:docker commit + restart + 验证,详见 [[phase1-progress#109-hotfix-二轮修复]]
### RBAC 5 角色 × 4 资源 × 4 操作 × 3 范围
- **5 角色**:super_admin / admin / agent / user / guest
- **4 资源**:user / conversation / config / audit_log
- **4 操作**:read / create / update / delete
- **3 范围**:all / department / self
### 高危路由白名单 + 中间件
5 类高危操作需 OTP 二次认证:
- `role_change`(角色变更)
- `config_change`(配置变更)
- `data_export`(数据导出)
- `account_disable`(账户禁用)
- `account_create_reset`(账户创建/重置)
## ✨ 新功能
### 后端扫码登录(端点 4 个)
| 端点 | 方法 | 说明 |
|---|---|---|
| `/api/auth_qrcode/create` | POST | 生成二维码(含 PNG base64) |
| `/api/auth_qrcode/poll/{ticket}` | POST | 扫码端轮询 |
| `/api/auth_qrcode/scan` | POST | 企微回调 |
| `/api/auth_qrcode/confirm` | POST | 用户确认登录 |
### 后端 MFA + pyotp(端点 5 个)
| 端点 | 方法 | 说明 |
|---|---|---|
| `/api/mfa/status` | GET | 查询绑定状态 |
| `/api/mfa/bind/start` | POST | 开始绑定(返回 secret + QR) |
| `/api/mfa/bind/confirm` | POST | 确认绑定 |
| `/api/mfa/verify` | POST | 验证 OTP |
| `/api/mfa/disable` | POST | 禁用 MFA |
### 前端 MFA UI
- `MfaBind.vue` — 绑定弹窗
- `useHighRiskOtp.ts` composable — 高危操作 OTP 弹窗
- `Login.vue` / `TopBar.vue` — 集成 MFA 状态显示
- 路由 + store 全链路集成
### H5 员工端 OAuth 登录
- 部署路径:`https://itsupport.servyou.com.cn/itdesk/`
- 自动 OAuth 静默授权(`snsapi_base`)
- 会话列表 + 消息收发
- 已完成 E2E 验证(#104)
## 🛠️ 部署要点
### 必备环境变量
```bash
# /opt/wecom-it-desk/.env
WECOM_QRCODE_CALLBACK=https://itsupport.servyou.com.cn/api/auth_qrcode/scan
WECOM_SSO_ENABLED=true
WECOM_SSO_CALLBACK_BASE=https://itsupport.servyou.com.cn
```
### 部署清单
- [x] Backend commit `78f60c6`(v0.7.1-dev)
- [x] docker 镜像 `wecom-it-desk-backend:latest` 已 commit 新 config
- [x] 容器已重启 + /api/ready 200
- [x] H5 dist 已部署到 `/opt/wecom-it-desk/nginx/html/itdesk/`
- [x] nginx 已 reload
- [x] /api/auth_qrcode/create 返回绝对 URL
- [ ] Gitea push(等用户重授权 token)
- [ ] 企微 App 实际扫码验证
## ⚠️ 已知问题
1. **Gitea push 阻塞**:`workbuddy-claude` token 2026-06-15 被吊销,需去 Gitea Web 重授权
2. **#108 still pending**:v0.7.1-dev 78f60c6 未推到 origin
3. **/api/admin/ IP 白名单**仍是 0.0.0.0/0(临时方案),v1.0 前必须收窄
## 📚 相关文档
- [[H5-DEPLOY-RUNBOOK-v0.7.1]] — H5 部署 runbook
- [[DEPLOY-LOGIN-MIGRATION-v0.7.0]] — 旧版扫码登录
- [[E2E-CHECKLIST-v0.7.0]] — 端到端验收
- [[NGINX-DOMAIN-ROUTING]] — nginx 域名分发
- [[USER-GUIDE-QRCODE-MFA]] — 用户手册
- [[phase1-progress]] — 进展跟踪
## 📊 改动统计
- **代码**:22 文件 / +4705 行
- **测试**:78 passed + 4 xfail + 33 pre-existing failures(已分类)
- **文档**:5 新增 / 3 更新
- **Agent**:4 并行 worktree 合并
---
## 🔄 补充 hotfix(2026-06-24 部署)
### #116 P0 + #118 P0:扫码端点 405 + ticket≠state
**问题**:
- 坐席扫码登录报 `405 Method Not Allowed`
- 企微 OAuth 回调走 GET(`?code=xxx&state=<ticket>`),旧代码只支持 POST
- 即使改双方法,`Optional[str] = None``Query()` 装饰器,FastAPI 当 body 参数处理
- 函数参数叫 `ticket`,企微用 `state`,参数名不匹配
**修复**:`backend/app/api/auth_qrcode.py` scan 端点
```python
@router.api_route("/scan", methods=["GET", "POST"], response_model=None)
async def scan_qrcode(
body: Optional[QrcodeScanRequest] = None,
ticket: Optional[str] = Query(None, description="兼容旧参数名"),
state: Optional[str] = Query(None, description="企微 OAuth state 标准参数名"),
code: Optional[str] = Query(None, description="企微 OAuth 授权码"),
redis_client = Depends(dep_redis),
):
if body is not None:
final_ticket, final_code = body.ticket, body.code
else:
final_ticket = state or ticket # 优先 state(企微标准),回退 ticket
final_code = code
```
**部署**:`deploy-staging/hotfix-116-qrcode-scan-get/` — webcli v7 一键,20/20 pytest 过
### #119:webcli v7 全自动部署链路
复用 jumpserver Playwright + Luna webcli + OTP 自动,实现 18-31KB 命令脚本单次输入,2-3 分钟跑完。
### #120 P0:扫码成功 = 自动登录(去掉 confirm)
**问题**:企微 App 端没有"确认登录" UI,旧 confirm 步骤在生产永远走不通(用户报告"扫码成功后,手机端没有出现确认登录按钮")
**用户决策**:方案 A — 扫码成功 = 自动登录(推荐),2026-06-24 拍板
**修复**:`backend/app/services/qrcode_service.py` `process_scan` 默认 `auto_confirm=True`
- 扫码成功直接写 `qrcode:confirm:{ticket}`(含 token)
- 前端 poll 立即拿到 status=confirmed + token
- 跳过手机 confirm 步骤
- 默认 roles=["user"],admin/agent 走 portal 端角色选择
**测试**:20/20 pytest 过(`test_scan_then_poll_returns_confirmed` + `test_scan_auto_confirm_writes_confirm_key` 等)
**部署**:`deploy-staging/hotfix-120-qrcode-auto-confirm/` — webcli v7 一键,容器内 `/api/ready` 200 OK
**残留**:外部域名 `https://itsupport.servyou.com.cn/api/ready` 不通(nginx `/api/` upstream 配错 + 8000 端口未暴露),**等 #48 修复**。容器内 backend 完全健康。
@@ -0,0 +1,87 @@
# 堡垒机运维工具 (jumpserver-ops)
## 概述
通过 JumpServer 堡垒机自动化执行远程命令、文件上传下载。统一入口为 `jms_ops.py`,支持 4 种操作模式。
## 连接方式决策
| 场景 | 连接方式 |
|------|----------|
| **默认** | 通过堡垒机跳转(大多数内网服务器) |
| **例外** | 直连(NAS、开发机等)需单独配置 |
> **规则**:默认都需要通过堡垒机,除非明确告知某台服务器是直连。
## 使用方法
### 1. 远程命令执行(推荐)
```bash
# 单命令(纯文本输出)
python scripts/jms_ops.py exec -c "docker ps"
# 多命令串行(一次登录,5x 提速)
python scripts/jms_ops.py exec -c "hostname" -c "uptime" -c "docker ps"
# 并行模式(不冲突的长命令)
python scripts/jms_ops.py exec -c "docker logs nginx --tail 100" -c "df -h" --parallel
```
### 2. 批量命令
```bash
python scripts/jms_ops.py batch -f commands.txt
```
### 3. 文件上传
```bash
# 本地 → 堡垒机 → 目标服务器
python scripts/jms_ops.py upload 本地文件.conf /tmp/远程路径.conf
```
### 4. 文件下载
```bash
# 目标服务器 → 堡垒机 → 本地
python scripts/jms_ops.py download /远程路径.conf ./本地文件.conf
```
## 方案选择依据
| 需求 | 推荐方案 | 速度 |
|------|----------|------|
| 执行命令获取文本结果 | v16 REST API + plink PTY | ~15s |
| 执行命令看界面效果 | v10 Web CLI(截图) | ~60s |
| 小文件传输 (<100KB) | upload/download | - |
| 大文件传输 | elFinder Web UI | - |
## 目标服务器配置
当前预设目标:`hz-oa-ai-g-dataquery-90-5-110` (10.90.5.110)
新增直连服务器时,需提供:
- IP 地址
- 端口(默认 22
- 用户名
- 认证方式(密码或 SSH 密钥)
## 故障排除
| 问题 | 解决方法 |
|------|----------|
| 登录失败 | 检查 `config/jumpserver_config.json` 密码是否正确(Base64 编码) |
| MFA 失败 | 确认 `scripts/otp_secret.key` 存在且系统时间准确 |
| 资产未找到 | 确认目标名称与 JumpServer 中显示一致 |
## 脚本位置
```
C:\Users\simon\.workbuddy\skills\jumpserver-ops\scripts\jms_ops.py
```
## 相关文档
- [服务器部署手册](./服务器部署手册.md)
- [版本更新说明](./05-版本更新说明-v1.1.0-20260614.md)

Some files were not shown because too many files have changed in this diff Show More