docs: 移动蓝绿部署指南到 troubleshooting 目录

This commit is contained in:
Simon
2026-07-05 17:03:36 +08:00
parent ab90db3d3d
commit ca7c6d937a
91 changed files with 4841 additions and 406 deletions
@@ -1,6 +1,6 @@
# IT智能服务台 — 系统架构设计文档
> **文档版本**: v1.4
> **文档版本**: v1.5
> **创建日期**: 2025-07-11
> **最近更新**: 2026-07-04
> **架构师**: 高见远 (Bob)
@@ -356,6 +356,43 @@ Wingman 是坐席工作台的 AI 辅助系统:
| **数据映射** | 审批标题 → TodoItem.title / 审批状态 → TodoItem.status |
| **依赖** | 需企微管理后台创建审批应用并授权 API |
### 9.3 WebSocket 实时通讯技术方案
> **新增日期**: 2026-07-05 | **状态**: 已实现
#### 9.3.1 技术选型
| 方案 | 优点 | 缺点 | 结论 |
|------|------|------|------|
| WebSocket | 双向实时、低延迟 | 需心跳维护 | ✅ 推荐 |
| SSE | 简单单向 | 仅服务器→客户端 | ❌ 不适合 |
| 轮询 | 实现简单 | 延迟高、资源浪费 | ❌ 不推荐 |
#### 9.3.2 消息类型
| 消息类型 | 方向 | 说明 |
|----------|------|------|
| `message_new` | Server→Client | 新消息推送 |
| `message_recall` | Server→Client | 消息撤回 |
| `typing` | Bidirectional | 对方正在输入 |
| `presence` | Bidirectional | 在线状态 |
| `ping/pong` | Bidirectional | 心跳保活 |
#### 9.3.3 心跳机制
- 客户端每 30 秒发送一次 ping
- 服务端 60 秒内未收到 ping 断开连接
#### 9.3.4 断线重连
- 前端 WebSocket 断开后自动重连
- 最大重连次数: 5
- 重连间隔: 2s, 4s, 8s, 16s, 32s
#### 9.3.5 降级策略
WebSocket 连接失败或断开时,自动降级为轮询(每3-5秒)。
---
## 10. 安全设计
@@ -381,6 +418,56 @@ Wingman 是坐席工作台的 AI 辅助系统:
| 角色来源追溯 | user_roles 表记录 source 和 assigned_by |
| 管理端 IP 白名单 | 仅内网/VPN 可访问 |
### 10.3 OTP 双因素认证技术方案
> **新增日期**: 2026-07-05 | **状态**: 规划中
#### 10.3.1 技术选型
| 方案 | 优点 | 缺点 | 结论 |
|------|------|------|------|
| TOTP (Google Authenticator) | 开源成熟无需服务器 | 需手动绑定 | ✅ 推荐 |
| 短信OTP | 用户无需安装App | 有成本,有延迟 | ❌ 不推荐 |
| 邮箱OTP | 无需安装App | 有延迟,不实时 | ❌ 不推荐 |
#### 10.3.2 认证流程
```
用户输入账号密码
验证账号密码成功
返回要求OTP验证
用户输入OTP验证码
验证OTP → 返回结果
```
#### 10.3.3 绑定流程
```
用户首次登录 → 系统检测未绑定OTP → 显示绑定页面 → 用户扫描二维码 → 输入验证码确认 → 绑定成功
```
#### 10.3.4 API 设计
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/api/auth/otp-bind` | 绑定OTP |
| POST | `/api/auth/otp-verify` | 验证OTP |
| POST | `/api/auth/otp-unbind` | 解绑OTP(管理员) |
| GET | `/api/auth/otp-status` | 查询OTP绑定状态 |
#### 10.3.5 数据库设计
```sql
-- 扩展 agents 表新增字段
ALTER TABLE agents ADD COLUMN otp_secret VARCHAR(32) DEFAULT NULL;
ALTER TABLE agents ADD COLUMN otp_bound BOOLEAN DEFAULT FALSE;
ALTER TABLE agents ADD COLUMN otp_bound_at TIMESTAMP DEFAULT NULL;
```
---
## 11. 复杂对话场景设计
@@ -0,0 +1,24 @@
#!/bin/bash
# 尝试多个可能的接口路径
# IT服务台Secret
curl -s 'https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=wwa8c87970b2011f41&corpsecret=EOtQslW7WD8Rna8Nm9WnwCW-ozHP3tustL4mFnet6O8' > /tmp/token.json
TOKEN=$(grep -o '"access_token":"[^"]*' /tmp/token.json | cut -d'"' -f4)
echo "Token: $TOKEN"
echo ""
# 尝试不同的接口路径
echo "=== 1. oa/get_template_list ==="
curl -s "https://qyapi.weixin.qq.com/cgi-bin/oa/get_template_list?access_token=$TOKEN"
echo ""
echo ""
echo "=== 2. oa/template/list ==="
curl -s "https://qyapi.weixin.qq.com/cgi-bin/oa/template/list?access_token=$TOKEN"
echo ""
echo ""
echo "=== 3. oa/approval/list ==="
curl -s "https://qyapi.weixin.qq.com/cgi-bin/oa/approval/list?access_token=$TOKEN&starttime=1767187200&endtime=1783094400"
echo ""
@@ -0,0 +1,77 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""企微审批API权限测试脚本 - 服务器版本(使用 urllib)"""
import urllib.request
import urllib.parse
import urllib.error
import json
import sys
CORP_ID = "wwa8c87970b2011f41"
CORP_SECRET = "EOtQslW7WD8Rna8Nm9WnwCW-ozHP3tustL4mFnet6O8"
print("=" * 60)
print("企微审批API权限测试")
print("=" * 60)
# 1. 获取 access_token
print("\n[1/2] 获取 access_token...")
url = f"https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={CORP_ID}&corpsecret={CORP_SECRET}"
try:
with urllib.request.urlopen(url, timeout=10) as response:
result = json.loads(response.read().decode('utf-8'))
except Exception as e:
print(f"❌ 请求失败: {e}")
sys.exit(1)
print(f" 返回: {result}")
if result.get("errcode") != 0:
print(f"❌ access_token 获取失败: {result.get('errmsg')}")
sys.exit(1)
token = result.get("access_token")
print(f"✅ access_token: {token[:20]}...")
# 2. 测试审批API - 企微正确的API路径
print("\n[2/2] 测试审批API...")
# 企微OA审批接口正确的调用方式
# 首先尝试 approvallist 接口
test_endpoints = [
("/cgi-bin/oa/approvallist", {"start_time": 0, "end_time": 9999999999, "cursor": 0, "size": 1}),
("/cgi-bin/oa/approvalinfo", {"spno": "test"}), # 用一个测试单号
]
success = False
for endpoint, params in test_endpoints:
full_url = f"https://qyapi.weixin.qq.com{endpoint}?access_token={token}&{urllib.parse.urlencode(params)}"
print(f" 测试: {endpoint}...")
try:
with urllib.request.urlopen(full_url, timeout=10) as response:
result = json.loads(response.read().decode('utf-8'))
except urllib.error.HTTPError as e:
result = {"errcode": e.code, "errmsg": f"HTTP {e.code}"}
except Exception as e:
result = {"errcode": -1, "errmsg": str(e)}
print(f" errcode: {result.get('errcode')}, errmsg: {result.get('errmsg')}")
if result.get("errcode") == 0:
success = True
print(f"{endpoint} 接口可用!")
break
elif result.get("errcode") == 48001:
print(f" ❌ 没有权限: {result.get('errmsg')}")
elif result.get("errcode") == 301012:
print(f" ⚠️ 接口可用但审批单不存在(正常)")
success = True
break
print("\n" + "=" * 60)
if success:
print("✅ 测试结果: 企微审批API权限已开通")
else:
print("❌ 测试结果: 未开通审批API权限 (错误码 48001)")
print("=" * 60)
@@ -192,7 +192,7 @@ M1 已升级为 **WebSocket 实时推送**2026-06-03 完成),坐席浏览
- 需跨平台/跨主体 → 方案A 不可替代
- **推荐混合策略**:原生1对1做日常入口,H5 保留为扩展层
**运维应急**(详见 `01-项目总览与部署手册.md` §7.5):
**运维应急**(详见 `01-项目总览/01-项目总览与部署手册-20260704.md` §7.5):
- H5 不可用时,零代码切换至方案B
- 需外援协作时,按需启用 appchat 群聊
@@ -652,4 +652,4 @@ C:\Users\simon\wecom_it_smart_desk\
---
> 本文档面向运维/架构/开发三团队沟通使用。详细技术规格见 `ARCHITECTURE.md`,产品需求见 `PRD.md`。
> 本文档面向运维/架构/开发三团队沟通使用。详细技术规格见 `03-技术架构/00-系统架构设计文档-v1.3.md`,产品需求见 `02-产品需求/02-产品需求文档PRD-v1.2-20260704.md`。