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 -1
View File
@@ -61,7 +61,7 @@ docs/
| 目录/文档 | 说明 |
|------|------|
| `00-系统架构设计文档-v1.0.md` | 系统架构设计(v1.0 |
| `00-系统架构设计文档-v1.3.md` | 系统架构设计(v1.3 |
| `01-ADRs-架构决策/` | 4 | 架构决策记录 |
| `02-技术方案/` | 5 | 技术方案文档 |
| `03-技术分析/` | 3 | 技术分析报告 |
@@ -5,7 +5,7 @@
> **📖 关联文档**:
> - [README.md](../README.md) — 项目快速入门
> - [01-项目总览与部署手册.md](./01-项目总览与部署手册.md) — 完整架构设计
> - [01-项目总览与部署手册](./01-项目总览与部署手册-20260704.md) — 完整架构设计
> - [CHANGELOG.md](../CHANGELOG.md) — 版本变更概览
> - [docs/archive/](./archive/) — 历史版本详情
@@ -483,11 +483,11 @@ docker compose logs nginx > /tmp/incident-nginx.log
| 文档 | 说明 |
|------|------|
| `docs/01-项目总览与部署手册.md` | 完整项目背景与架构设计 |
| `docs/01-项目总览/01-项目总览与部署手册-20260704.md` | 完整项目背景与架构设计 |
| `docs/RELEASE_NOTES_v0.7.1.md` | 版本发布说明 |
| `docs/SOPs/SOP-004-应急响应.md` | 详细应急响应流程 |
| `docs/deploy/快速诊断-500-错误.md` | 500 错误排查指南 |
| `docs/IT服务台部署修复记录-2026-06-13.md` | 历史修复记录 |
| `docs/09-部署运维/deploy/04-部署修复记录-20260613.md` | 历史修复记录 |
---
@@ -581,7 +581,7 @@ docker compose down # 停止新系统所有容器
| 阶段 | 状态 | 产出 |
|------|------|------|
| PRD | ✅ 完成 | `PRD.md` — 31 需求(P0/P1/P2),7 用户故事 |
| 架构设计 | ✅ 完成 | `docs/ARCHITECTURE.md` — 9 表 DDL,7 API 组,4 时序图,5 任务分解 |
| 架构设计 | ✅ 完成 | `docs/03-技术架构/00-系统架构设计文档-v1.3.md` — 9 表 DDL,7 API 组,4 时序图,5 任务分解 |
| T01 项目脚手架 | ✅ 完成 | 57 文件 — docker-compose, nginx, .env, 后端/前端骨架 |
| T02 后端核心服务 | ✅ 完成 | 16 文件 — 企微加解密, 消息路由, 评分, 会话, 趣味话术, 7 API 路由 |
| T03 坐席工作台 | ✅ 完成 | 25 文件 — 三栏布局, 会话管理, 聊天, AI助手面板(5Tab) |
@@ -9,7 +9,7 @@
| 阶段 | 状态 | 产出 |
|------|------|------|
| PRD | ✅ 完成 | `PRD.md` — 31 需求(P0/P1/P2),7 用户故事 |
| 架构设计 | ✅ 完成 | `docs/ARCHITECTURE.md` — 9 表 DDL,7 API 组,4 时序图,5 任务分解 |
| 架构设计 | ✅ 完成 | `docs/03-技术架构/00-系统架构设计文档-v1.3.md` — 9 表 DDL,7 API 组,4 时序图,5 任务分解 |
| T01 项目脚手架 | ✅ 完成 | 57 文件 — docker-compose, nginx, .env, 后端/前端骨架 |
| T02 后端核心服务 | ✅ 完成 | 16 文件 — 企微加解密, 消息路由, 评分, 会话, 趣味话术, 7 API 路由 |
| T03 坐席工作台 | ✅ 完成 | 25 文件 — 三栏布局, 会话管理, 聊天, AI助手面板(5Tab) |
@@ -0,0 +1,105 @@
# 文档关联修复报告
> **日期**: 2026-07-05 | **维护人**: 助理 | **范围**: docs/ 目录重组后的断链修复
---
## 一、背景
2026-07-04 对 `docs/` 执行了目录重组:从扁平结构迁移为 `01-项目总览` ~ `11-历史归档` 数字编号子目录,新建权威索引 `01-项目总览/00-索引-20260704.md`。但三处"关联"未同步更新,导致断链:
1. `mkdocs.yml` nav 导航仍指向旧的根级扁平路径
2. 文档间 Markdown 交叉引用未更新
3. 巡检自动化任务引用的看板路径已失效
---
## 二、修复清单
### 2.1 mkdocs.yml nav 重写(9 处断链 + 5 份新增)
| nav 旧路径 | 新路径 | 处理 |
|---|---|---|
| `01-项目总览与部署手册.md` | `01-项目总览/01-项目总览与部署手册-20260704.md` | 更新 |
| `ARCHITECTURE.md` | `03-技术架构/00-系统架构设计文档-v1.3.md` | 指向新版 |
| `ARCHITECTURE-admin.md` | (无新版,已归档) | 移除 |
| `DEPLOY-QUICK-v0.7.0.md` | `09-部署运维/deploy/10-一键部署操作包-v0.7.0.md` | 指向新版 |
| `DEPLOY-LOGIN-MIGRATION-v0.7.0.md` | (已过时归档) | 移除 |
| `NAS部署指南.md` | `09-部署运维/deploy/09-NAS部署指南-群晖Cloudflare.md` | 更新 |
| `OTP二次验证实现.md` | `09-部署运维/deploy/06-OTP二次验证实现.md` | 更新 |
| `IT服务台部署修复记录-2026-06-13.md` | `09-部署运维/deploy/04-部署修复记录-20260613.md` | 更新 |
| `E2E-CHECKLIST-v0.7.0.md` | `06-测试质量/testing-测试/E2E-CHECKLIST-v0.7.0.md` | 更新 |
**新增纳入 nav 的核心文档**
- `01-项目总览/00-索引-20260704.md`(文档索引)
- `01-项目总览/01-智能IT服务系统运维手册-20260704.md`(运维手册)
- `02-产品需求/02-产品需求文档PRD-v1.2-20260704.md`PRD v1.2
- `10-项目管理/05-项目状态看板/01-项目状态看板.md`(项目状态看板)
- `10-项目管理/01-任务总索引.md``10-项目管理/02-风险跟踪表.md`
### 2.2 文档间交叉引用修复(11 处)
| 文件 | 位置 | 旧引用 | 新引用 |
|---|---|---|---|
| `01-智能IT服务系统运维手册-20260704.md` | 行8 | `./01-项目总览与部署手册.md` | `./01-项目总览与部署手册-20260704.md` |
| 同上 | 行486 | `docs/01-项目总览与部署手册.md` | `docs/01-项目总览/01-项目总览与部署手册-20260704.md` |
| 同上 | 行490 | `docs/IT服务台部署修复记录-2026-06-13.md` | `docs/09-部署运维/deploy/04-部署修复记录-20260613.md` |
| `01-项目总览与部署手册-20260704.md` | 行584 | `docs/ARCHITECTURE.md` | `docs/03-技术架构/00-系统架构设计文档-v1.3.md` |
| `04-开发交付概览.md` | 行12 | `docs/ARCHITECTURE.md` | `docs/03-技术架构/00-系统架构设计文档-v1.3.md` |
| `技术分析-架构消息知识库迭代.md` | 行195 | `01-项目总览与部署手册.md` | `01-项目总览/01-项目总览与部署手册-20260704.md` |
| 同上 | 行655 | `ARCHITECTURE.md` / `PRD.md` | 新架构路径 / 新PRD路径 |
| `01-项目状态看板.md` | 行74 | `docs/DEPLOY-QUICK-v0.7.0.md` + `docs/E2E-CHECKLIST-v0.7.0.md` | 新部署路径 + 新E2E路径 |
| `10-一键部署操作包-v0.7.0.md` | 行215 | `docs/E2E-CHECKLIST-v0.7.0.md` | `docs/06-测试质量/testing-测试/E2E-CHECKLIST-v0.7.0.md` |
| `02-产品需求文档PRD-v1.2-20260704.md` | 行2457 | `docs/ARCHITECTURE.md` | `docs/03-技术架构/00-系统架构设计文档-v1.3.md` |
| 同上 | 行2458 | `docs/小组任务书/任务执行状态看板.md` | `docs/10-项目管理/05-项目状态看板/01-项目状态看板.md` |
**保留未改**
- 目录树展示(`├── ARCHITECTURE.md` 等)—— 历史结构快照,保留以保真
- `03-项目任务状态报告.md` 中"#4 更新ARCHITECTURE.md"等历史任务标题 —— 历史任务记录
### 2.3 索引版本号修正
`01-项目总览/00-索引-20260704.md` 第64行:架构文档版本号 `v1.0``v1.3`(与实际文件名一致)。
### 2.4 巡检自动化任务适配
**automation-1782986180887**(IT服务台任务巡检-早班,每日 09:30):
| 项 | 旧 | 新 |
|---|---|---|
| 数据源路径 | `docs\小组任务书\任务执行状态看板.md` | `docs/10-项目管理/05-项目状态看板/01-项目状态看板.md` |
| 巡检逻辑 | A/B/C 三组任务、🟢可立即启动/⏳等待中/依赖/预计完成日期 | P0必做/P1重要/等用户决策/进行中 |
| 输出格式 | 小组进度(A组x/16 等) | 全局状态(P0/P1/等决策计数 + 最近完成) |
---
## 三、关键发现
### 3.1 巡检数据源从未存在
巡检自动化任务自创建起所依赖的"任务执行状态看板.md"及整个小组任务书体系(A组认证加固16项 / B组消息系统16项 / C组AI与数据19项)**从未被实际创建**——仅在归档的"文档分类与清理报告"中作为"推荐结构"出现。这意味着该每日早班巡检自始至终都在尝试读取不存在的文件,每天必然失败。
经与用户确认,已将巡检适配为基于现有"项目状态看板"的状态巡检。
### 3.2 索引与实际存在版本号偏差
权威索引 `00-索引-20260704.md` 记录架构文档为 v1.0,实际文件为 `00-系统架构设计文档-v1.3.md`。已修正。
---
## 四、验证建议
1. **mkdocs 站点验证**:执行 `mkdocs serve` 预览,确认 nav 所有条目可正常跳转
2. **交叉引用验证**:在 mkdocs 构建时检查是否有 broken link 警告
3. **巡检验证**:等待次日 09:30 自动触发,确认巡检报告正常生成
---
## 五、未处理事项(待后续决策)
| 事项 | 说明 |
|---|---|
| 项目根目录 README.md 缺失 | 原项目根目录 `README.md`7.4KB 项目说明)在 2026-07-04 文档重组时已删除,未迁移到新结构。新入口为 `01-项目总览/01-项目总览与部署手册-20260704.md` |
| `01-智能IT服务系统运维手册` 行7/10 | 引用 `../README.md``./archive/`docs/ 根目录无 READMEarchive 实为 11-历史归档。非本次旧路径范围,暂未处理 |
| `01-项目总览与部署手册` 行645-717 | 历史目录树展示,保留原样。如需更新为新结构可后续单独处理 |
| 归档文档内的旧路径 | `11-历史归档/` 下多个文档仍含旧路径引用,作为历史快照保留,不修改 |
@@ -1,11 +1,13 @@
# 企微智能IT支持服务台 — 产品需求文档 (PRD)
> **文档版本**: v1.5
> **文档版本**: v1.6
> **创建日期**: 2025-07-11
> **最近更新**: 2026-07-04
> **产品经理**: 许清楚 (Xu) · 宋献
> **状态**: 阶段一开发完成,待端到端验证
> **说明**: 本文档已合并原 `PRD-v53-incremental.md` 内容(v5.3 坐席工作台增量需求)。v1.0 更新:新增管理后台远景规划(§17)、系统生态与集成规划(§18)、阶段细化与并行推进策略(§19);明确管理后台为第三端产品;确立 AI 混合策略(流程图+AI+标注+迭代);将阶段一细化为 1A/1B/1C 子阶段;新增零基础人员原则。v1.1 更新:新增邀请功能设计(§20),将邀请功能纳入M1 MVP(1A子阶段),新增P0-09~P0-11和P1-14~P1-16需求。v1.2 更新:整合 `PRD-增量-人工按钮与术语统一.md` 内容为新章节§10 术语与图标规范。v1.3 更新:新增 §4.5 指标体系详细设计;修复 §16 v5.3 内部章节编号;将 v5.3 项目信息移至 §1.1。v1.4 更新(2026-07-04):**产品经理视角重构**——新增 §1 产品愿景与目标、§1.5 用户画像、§1.6 非目标章节;补充竞品分析至产品定义章节。
v1.6 更新(2026-07-05):坐席端登录流程优化——智能检测企微客户端登录状态,已登录则提供企微快捷登录+浏览器登录+账号密码OTP三种方式;未登录则默认企微扫码登录+账号密码OTP兜底。
v1.5 更新(2026-07-04):登录逻辑调整——用户端强制企微内嵌,坐席/管理端浏览器直接打开(无需经过企微工作台),支持账号密码+OTP认证。
> **章节编号说明**: 主文档章节编号为 2-20(§1 在附录中),附录内使用独立编号体系(附录A §1-10、附录B §1-2、附录C §1-6)。后续新增章节应按顺序递增。
@@ -614,45 +616,70 @@ v1.5 更新(2026-07-04):登录逻辑调整——用户端强制企微内
#### 4.4.4 各端登录方式
> **更新日期**: 2026-07-04 | **设计目标**: 用户安全入口可控,坐席/管理员独立访问
> **更新日期**: 2026-07-05 | **设计目标**: 用户安全入口可控,坐席/管理员独立访问
| 端 | 访问方式 | 登录方式 | 说明 |
|----|----------|----------|------|
| **用户端 (H5)** | 企微工作台 → 应用内嵌打开 | OAuth2 静默授权 | 强制内嵌,保证安全、入口统一、用户粘性 |
| **坐席端** | 浏览器直接打开 | 账号密码+OTP | **无需经过企微**,灵活办公,支持多设备 |
| **坐席端** | 浏览器直接打开 | 智能检测+三种登录方式 | 企微快捷登录/浏览器扫码/账号密码+OTP |
| **管理后台** | 浏览器直接打开 | 账号密码+OTP | **无需经过企微**,安全可控 |
#### 4.4.5 坐席/管理员登录流程
#### 4.4.5 坐席登录流程v1.6 优化)
> **更新日期**: 2026-07-04 | **核心变更**: 坐席/管理员无需经过企微工作台
> **更新日期**: 2026-07-05 | **核心变更**: 智能检测企微登录状态,提供三种登录方式
```
坐席访问 /itagent/(管理员访问 /itadmin/
坐席访问 /itagent/
浏览器打开登录页
检测企微客户端登录状态(企微 JS-SDK)
┌─────────────────────────┐
登录方式选择
[🔐 账号密码+OTP登录] │ ← 主要方式
│ │
│ [🐛 企微扫码登录] │ ← 备选方式(如有企微环境)
└─────────────────────────┘
┌──────────────────────────────────────────────────────
场景一:检测到企微已登录
┌────────────────────────────────────────────────┐
│ 选择登录方式 │ │
│ [① 在企业微信桌面端打开] → wecom:// 协议 │ │
│ │ │ │
│ │ [② 继续在浏览器登录] → 企微扫码+验证码 │ │
│ │ │ │
│ │ [③ 账号密码+OTP] → 传统表单登录 │ │
│ └────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
输入用户名 + 密码 + OTP验证码
┌──────────────────────────────────────────────────────┐
│ 场景二:未检测到企微登录 │
│ ┌────────────────────────────────────────────────┐ │
│ │ 默认登录页(企微扫码 + 账号密码OTP) │ │
│ │ │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ │ │
│ │ │ 企微扫码登录 │ │ 账号密码+OTP │ │ │
│ │ │ (二维码) │ │ │ │ │
│ │ └─────────────────┘ └─────────────────┘ │ │
│ └────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
验证成功 → 发放Token → 进入工作台
```
#### 4.4.6 企微扫码登录(备选)
#### 4.4.6 登录方式详细说明
| 登录方式 | 适用场景 | 技术实现 | 用户操作 |
|----------|---------|---------|---------|
| **① 企微快捷登录** | 企微客户端已登录 | JS-SDK `wx.agentConfig` 获取用户身份 → 后端校验坐席角色 | 点击"在企业微信桌面端打开"自动跳转 |
| **② 浏览器企微扫码** | 企微客户端未登录但有企微App | 显示企微OAuth二维码 → 用户扫码授权 | 微信/企微扫码 → 授权 → 自动登录 |
| **③ 账号密码+OTP** | 无企微环境或二维码失效 | 传统表单 + TOTP验证码 | 输入账号密码+OTP → 登录 |
#### 4.4.7 企微客户端检测
| 检测方式 | 代码 | 说明 |
|----------|------|------|
| **企微内嵌检测** | `navigator.userAgent.includes('wxwork')` | 检测UA是否包含wxwork |
| **企微内打开** | 调用企微JSAPI `wx.openDefaultBrowser()` 或跳转企微应用URL | 在企微客户端中打开 |
| **浏览器登录** | 常规表单登录 + OTP输入框 | 账号密码+OTP验证 |
| **企微 JS-SDK** | `wx.agentConfig()` | 获取当前企微用户身份(需企业微信JS-SDK引入) |
| **企微协议跳转** | `wecom://` | 唤起企微客户端打开指定页面 |
> **注意**用户端(`/itdesk/`)强制企微内嵌,非企微环境访问跳转拦截页。
> **注意**
> - 用户端(`/itdesk/`)强制企微内嵌,非企微环境访问跳转拦截页
> - 坐席端智能检测为增强体验,检测失败时回退到默认登录页
#### 4.4.7 技术实现
@@ -2454,8 +2481,8 @@ class TroubleshootingTemplate(Base):
|------|------|------|
| `docs/重构方案-复杂场景技术方案.md` | 非线性跳转/多意图等技术方案 | 规划中 |
| `docs/PRD-增量-人工按钮与术语统一.md` | "人工"按钮与术语规范 | 待实施 |
| `docs/ARCHITECTURE.md` | 现有系统技术架构 | 维护中 |
| `docs/小组任务书/任务执行状态看板.md` | 项目任务状态管理 | 维护中 |
| `docs/03-技术架构/00-系统架构设计文档-v1.3.md` | 现有系统技术架构 | 维护中 |
| `docs/10-项目管理/05-项目状态看板/01-项目状态看板.md` | 项目任务状态管理 | 维护中 |
| `docs/archive/` | 已归档的重构方案文档 | 已归档 |
---
@@ -0,0 +1,227 @@
# 技术方案:消息推送策略优化与超时提醒
> **需求来源**2026-07-05 产品讨论
> **版本**v1.0
> **状态**:待开发
---
## 一、需求概述
### 1.1 业务背景
当前坐席回复用户消息时,会同时走两个通道:
1. **WebSocket** → 推送到 H5 页面
2. **企微应用消息** → 推送到"IT支持服务"应用的消息列表
这导致两种场景混在一起:
- 场景A:员工找坐席(一对一私密对话)→ 期望只走 H5
- 场景B:IT支持组群发通知 → 期望走企微消息
### 1.2 产品需求
| 需求 | 描述 |
|------|------|
| R1 | 正常对话:坐席回复仅推送到 H5 页面(WebSocket |
| R2 | 坐席回复后员工 3 分钟(可配置)未回复,发送企微提醒消息 |
| R3 | 提醒消息只发 1 次 |
| R4 | 10 分钟后自动标记会话为"待关闭"状态 |
| R5 | 提醒文案固定(见 1.3) |
### 1.3 提醒文案
```
IT服务提醒:您有新的消息未查看,咨询将在10分钟后标记为待关闭,请尽快点击处理 👉 https://itsupport.servyou.com.cn/itdesk/
```
---
## 二、技术方案
### 2.1 架构设计
```
┌─────────────┐ ┌──────────────┐ ┌─────────────────┐
│ 坐席发送 │────▶│ WebSocket │────▶│ H5页面 │
│ 消息 │ │ (仅推送H5) │ │ (实时可见) │
└─────────────┘ └──────────────┘ └─────────────────┘
▼ (触发条件)
┌─────────────────────────────────────────┐
│ 后台定时任务 (每30秒) │
│ • 检查超时未回复会话 │
│ • 发送企微提醒消息 │
│ • 标记会话状态 │
└─────────────────────────────────────────┘
```
### 2.2 数据库改动
#### 2.2.1 conversations 表新增字段
```sql
ALTER TABLE conversations
ADD COLUMN IF NOT EXISTS last_agent_reply_at TIMESTAMP DEFAULT NULL,
ADD COLUMN IF NOT EXISTS reminder_sent BOOLEAN DEFAULT FALSE,
ADD COLUMN IF NOT EXISTS reminder_sent_at TIMESTAMP DEFAULT NULL,
ADD COLUMN IF NOT EXISTS pending_close_at TIMESTAMP DEFAULT NULL;
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `last_agent_reply_at` | TIMESTAMP | 坐席最后回复时间 |
| `reminder_sent` | BOOLEAN | 是否已发送提醒 |
| `reminder_sent_at` | TIMESTAMP | 提醒发送时间 |
| `pending_close_at` | TIMESTAMP | 待关闭时间(最后回复+10分钟) |
#### 2.2.2 配置表(可选)
在系统配置表中添加:
| key | default | 说明 |
|-----|---------|------|
| `reminder.timeout_minutes` | 3 | 未回复超时时间(分钟) |
| `reminder.close_minutes` | 10 | 自动待关闭时间(分钟) |
| `reminder.enabled` | true | 是否启用提醒功能 |
### 2.3 后端改动
#### 2.3.1 消息发送逻辑修改
**文件**`backend/app/api/messages.py`
```python
# 坐席发送消息时
async def send_message(...):
# 1. 仅通过 WebSocket 推送到 H5(不再调用企微 API)
await manager.send_to_employee(conversation.employee_id, ws_event)
# 2. 更新会话的最后坐席回复时间
conversation.last_agent_reply_at = datetime.now()
conversation.reminder_sent = False # 重置提醒标记
conversation.pending_close_at = datetime.now() + timedelta(minutes=10)
```
#### 2.3.2 新增定时任务
**文件**`backend/app/tasks/reminder_task.py`(新建)
```python
# 每 30 秒执行一次
@scheduler.scheduled_job('interval', seconds=30)
async def check_unreplied_sessions():
"""检查超时未回复的会话,发送提醒"""
# 1. 查找需要处理的会话
sessions = await db.execute(select(Conversation).where(
Conversation.status == 'active',
Conversation.last_agent_reply_at.isnot(None),
Conversation.reminder_sent == False,
Conversation.last_agent_reply_at < (datetime.now() - timedelta(minutes=3))
))
for session in sessions:
# 2. 发送企微提醒消息
await send_reminder_message(session)
# 3. 标记已发送
session.reminder_sent = True
session.reminder_sent_at = datetime.now()
# 4. 处理待关闭会话
pending = await db.execute(select(Conversation).where(
Conversation.status == 'active',
Conversation.pending_close_at < datetime.now()
))
for session in pending:
session.status = 'pending_close' # 待关闭状态
await db.commit()
```
#### 2.3.3 提醒消息发送函数
**文件**`backend/app/services/reminder_service.py`(新建)
```python
async def send_reminder_message(conversation: Conversation):
"""发送超时提醒企微消息"""
message = "IT服务提醒:您有新的消息未查看,咨询将在10分钟后标记为待关闭,请尽快点击处理 👉 https://itsupport.servyou.com.cn/itdesk/"
redis_client = settings.create_redis_client()
wecom_service = WecomService(redis_client)
try:
await wecom_service.send_text_message(
conversation.employee_id,
message
)
finally:
await wecom_service.close()
await redis_client.close()
```
### 2.4 前端改动
#### 2.4.1 坐席端(无需改动)
当前坐席发送消息功能保持不变,后端会自动处理推送逻辑。
#### 2.4.2 H5 端(无需改动)
WebSocket 接收消息逻辑保持不变。
### 2.5 部署配置
#### 2.5.1 后端定时任务启动
`backend/app/main.py` 中注册定时任务:
```python
from apscheduler.schedulers.asyncio import AsyncIOScheduler
scheduler = AsyncIOScheduler()
scheduler.add_job(check_unreplied_sessions, 'interval', seconds=30)
scheduler.start()
```
---
## 三、任务分解
| # | 任务 | 文件 | 预估工时 |
|---|------|------|---------|
| 1 | 数据库迁移 | conversations 表新增字段 | 0.5h |
| 2 | 消息发送逻辑修改 | `backend/app/api/messages.py` | 0.5h |
| 3 | 新建提醒服务 | `backend/app/services/reminder_service.py` | 1h |
| 4 | 新建定时任务 | `backend/app/tasks/reminder_task.py` | 1h |
| 5 | 定时任务注册 | `backend/app/main.py` | 0.5h |
| 6 | 部署测试 | - | 1h |
**总计**:约 4.5 小时
---
## 四、风险与注意事项
1. **定时任务并发**:多实例部署时需确保任务不重复执行(建议加分布式锁)
2. **历史数据**:已存在的会话不受影响,新逻辑仅对新增会话生效
3. **配置灵活性**:当前为固定值,后续可扩展为可配置
---
## 五、相关文件清单
| 文件 | 操作 |
|------|------|
| `backend/app/api/messages.py` | 修改 |
| `backend/app/services/reminder_service.py` | 新建 |
| `backend/app/tasks/reminder_task.py` | 新建 |
| `backend/app/main.py` | 修改 |
| `docs/02-产品需求/04-技术方案-消息推送策略优化与超时提醒.md` | 新建 |
---
*最后更新:2026-07-05 15:40*
@@ -13,6 +13,7 @@ metadata:
> - 完成文档优化专项(用户手册创建、KPI指标补充、技术约束更新)
> - 补充阶段四/五的KPI指标定义
> - 新增需求:待办事项集成企微审批工单(#74)
> - 新增需求:头像同步功能完善(#75)
## 来源
扫描 CURRENT-FOCUS.md(P0/P1/P2)+ phase1-progress.md 痛点 + 3 个 v1.0 必做 memory。
@@ -32,6 +33,12 @@ metadata:
- 需企微审批应用 API 权限
- 估时:2-3天
3. **#75 [P1] 头像同步功能完善**
- 员工端/坐席端头像显示优化
- 当前仅首次登录同步,需改为每次登录强制更新
- 需处理头像URL过期问题
- 估时:1-2天
3. **#73 [P1] 修后端文件未真正覆盖**
- `yes | cp -f` 路径,部署时偶尔没生效
- 根因:`deploy-staging/` bind mount + RO 双重坑(见 [[bind-mount-deleted-inode-pitfall]])
@@ -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`。
@@ -16,9 +16,9 @@
### 2. `frontend-h5/src/components/chat/InputBar.vue`
- **移除摇铃按钮**:删除 🔔 摇铃按钮及相关 CSSbell-btn/bell-icon/bell-idle/bell-ring 动画)
- **新增工具栏**:😊表情 / 🖼️图片 / 📎文件 / 📸拍照(4个圆形按钮
- **工具栏**:😊表情 / 📎文件 / ✂️截图 / 📝快捷申请(2026-07-05移除🖼️图片和📸拍照功能
- **布局改为两行**:工具栏(上) + 输入行(输入框+发送按钮)(下)
- **新增方法**handleEmoji/handleImage/handleFile/handleCamera(阶段二实现具体功能)
- **方法**handleEmoji/handleFile/handleScreenshot/handleQuickApply
- **引导条文案更新**:"点击标题栏铃铛呼叫 IT 坐席"
### 3. `frontend-h5/src/components/assistant/RightPanel.vue`(新建)
@@ -52,10 +52,9 @@ IT智能服务台是公司为员工提供的IT问题一站式解决平台,具
2. 点击发送按钮
3. AI将自动回答您的问题
### 3.2 上传图片/文件
### 3.2 上传文件
支持上传以下格式:
- 图片:jpg、png、gif
支持上传以下格式(2026-07-05起不再支持图片上传)
- 文件:doc、docx、pdf、xls、xlsx(单个文件≤10MB
---
@@ -212,7 +212,7 @@ curl https://<生产域名>/api/mfa/status
## 🟡 部署后 必做(用户/QA 验收)
`docs/E2E-CHECKLIST-v0.7.0.md` 35 项,逐项打勾。
`docs/06-测试质量/testing-测试/E2E-CHECKLIST-v0.7.0.md` 35 项,逐项打勾。
**关键项**:
- [ ] 浏览器扫码登录全流程(5 子项)
@@ -37,14 +37,14 @@ python scripts/jms_ops.py batch -f commands.txt
### 3. 文件上传
```bash
# 本地 → 堡垒机 → 目标服务器
# 通过 elFinder Web UI 上传到目标服务器
python scripts/jms_ops.py upload 本地文件.conf /tmp/远程路径.conf
```
### 4. 文件下载
```bash
# 目标服务器 → 堡垒机 → 本地
# 通过 base64 通道从目标服务器下载
python scripts/jms_ops.py download /远程路径.conf ./本地文件.conf
```
@@ -52,10 +52,11 @@ python scripts/jms_ops.py download /远程路径.conf ./本地文件.conf
| 需求 | 推荐方案 | 速度 |
|------|----------|------|
| 执行命令获取文本结果 | v16 REST API + plink PTY | ~15s |
| 执行命令获取文本结果 | v16 REST API + plink PTY | 首次 ~13s,复用 ~2-3s |
| 执行命令看界面效果 | v10 Web CLI(截图) | ~60s |
| 文件传 (<100KB) | upload/download | - |
| 文件传输 | elFinder Web UI | - |
| 文件传 (< 100KB) | base64 通道 | |
| 文件上传 (>= 100KB 或 > 15s) | elFinder Web UI | 稳定 |
| 文件下载 | base64 通道 | - |
## 目标服务器配置
@@ -0,0 +1,156 @@
# 智能IT支持服务台 - 问题修复记录
**日期**2026-07-05
**负责人**:宋献
**状态**:✅ 已完成
---
## 一、问题概述
### 1.1 当日问题汇总
| 序号 | 问题 | 影响范围 | 严重程度 | 状态 |
|------|------|---------|---------|------|
| #1 | 坐席端消息列表 500 错误 | 坐席端 | 🔴 高 | ✅ 已修复 |
| #2 | 页面短暂无法访问 | 全端 | 🟡 中 | ✅ 已自愈 |
| #3 | 坐席端消息发送失败 | 坐席端 | 🔴 高 | ✅ 已修复 |
| #4 | 文档缺失 wordfilter 依赖说明 | 文档 | 🟢 低 | ✅ 已补充 |
---
## 二、问题详情
### 2.1 #1 坐席端消息列表 500 错误
**发现时间**03:27
**问题现象**
- 坐席端报错:`获取消息列表失败: Error: 服务器内部错误,请稍后重试或联系管理员`
- WebSocket 连接失败:`wss://itsupport.servyou.com.cn/ws/sxn`
**根因分析**
- 后端日志:`TypeError: list_messages() got an unexpected keyword argument 'current_user'`
- 原因:`/api/conversations/{id}/messages` 端点使用了 `@require_permission` 装饰器,但函数签名缺少 `current_agent` 参数
**修复步骤**
1.`backend/app/api/messages.py``list_messages` 函数中添加参数:
```python
current_agent: Agent = Depends(get_current_agent),
```
2. 使用 sed 命令在容器中直接插入行:
```bash
sudo docker exec wecom_it_backend sed -i '63i\ current_agent: Agent = Depends(get_current_agent),' /app/app/api/messages.py
```
3. 重启后端容器:
```bash
sudo docker restart wecom_it_backend
```
**验证结果**
```bash
curl "https://itsupport.servyou.com.cn/api/conversations/xxx/messages" -H "Authorization: Bearer xxx"
# 返回 200 OK,消息列表正常
```
---
### 2.2 #2 页面短暂无法访问
**发现时间**10:29
**问题现象**
- 用户报告坐席端和员工端页面打不开
**根因分析**
- 可能是之前容器重启导致的服务波动
**修复步骤**
- 服务自动恢复(无需人工干预)
**验证结果**
- H5 端:`/itdesk/` → 200 OK
- 坐席端:`/itagent/` → 200 OK
- API`/api/health` → 200 OK
---
### 2.3 #3 坐席端消息发送失败
**发现时间**10:44
**问题现象**
- 坐席端发送消息失败:`{"code":1005,"message":"服务器内部错误,请稍后重试或联系管理员"}`
**根因分析**
- 后端日志:`ModuleNotFoundError: No module named 'wordfilter'`
- `content_moderation_service.py` (v0.6.0 内容审核功能) 依赖 `wordfilter` 库,但 `requirements.txt` 中未声明
**修复步骤**
1. 在 `backend/requirements.txt` 中添加依赖:
```
wordfilter==0.2.7
```
2. 在容器中手动安装(临时修复):
```bash
sudo docker exec wecom_it_backend pip install wordfilter
```
**验证结果**
```bash
curl -X POST "https://itsupport.servyou.com.cn/api/conversations/xxx/messages" \
-H "Authorization: Bearer xxx" \
-H "Content-Type: application/json" \
-d '{"content":"测试","msg_type":"text"}'
# 返回 {"code":0,"message":"success"}
```
---
### 2.4 #4 文档缺失 wordfilter 依赖说明
**发现时间**10:50
**问题现象**
- 部署文档中未说明 Python 依赖管理流程
- `requirements.txt` 未包含 `wordfilter` 依赖
**修复步骤**
1. 更新 `backend/requirements.txt`,添加 `wordfilter==0.2.7`
2. 更新 `docs/09-部署运维/deploy/服务器部署手册.md`,新增"六、Python 依赖管理"章节:
- 依赖说明
- 新增依赖处理流程
- 常见依赖问题及解决方法
**验证结果**
- ✅ requirements.txt 已更新
- ✅ 部署文档已补充
---
## 三、后续建议
1. **依赖管理流程化**
- 每次新增 Python 依赖,必须同步更新 `requirements.txt`
- 部署前确保依赖已包含在 requirements.txt 中
2. **监控告警**
- 建议配置后端错误监控(如 Sentry),及时发现生产环境异常
3. **文档同步**
- 重要修复完成后,同步更新相关文档
---
## 四、相关文件
| 文件 | 说明 |
|------|------|
| `backend/requirements.txt` | Python 依赖声明 |
| `backend/app/api/messages.py` | 消息 API |
| `backend/app/services/content_moderation_service.py` | 内容审核服务 |
| `docs/09-部署运维/deploy/服务器部署手册.md` | 部署手册 |
---
*最后更新:2026-07-05 10:52*
@@ -43,65 +43,63 @@
```
> **OpenSSH `ssh -J` 方式不再使用**(用户已确认用 PuTTY,2026-06-15)
# 登录成功后:
ssh sxn@10.90.5.110
```
### 2.3 配置 SSH 快捷方式(推荐)
### 2.3 jumpserver-ops 工具(推荐)
在开发机上编辑 `~/.ssh/config`,添加以下内容,以后只需要 `ssh itdesk` 即可:
推荐使用 jumpserver-ops 工具进行远程命令执行和文件传输。该工具自动化完成 JumpServer 登录、OTP 验证、资产连接等全流程。
```
# 堡垒机
Host bastion
HostName 10.212.189.210
Port 2222
User sxn
# 智能IT支持服务台服务器
Host itdesk
HostName 10.90.5.110
User sxn
ProxyJump bastion
```
> **堡垒机用户名为 `sxn`,已填入下方命令中**
之后只需:
```bash
ssh itdesk # 自动通过堡垒机跳转
scp file itdesk:/opt/ # 文件传输也会自动走堡垒机
# 进入 skill 目录
cd C:\Users\simon\.workbuddy\skills\jumpserver-ops\scripts
# 远程执行命令(首次登录约 13s,后续复用约 2-3s)
python jms_ops.py exec -c "hostname"
python jms_ops.py exec -c "uptime" -c "docker ps"
# 文件上传(自动选择最优方式:<100KB 用 base64>=100KB 或 >15s 用 elFinder
python jms_ops.py upload local_file.txt /tmp/remote_file.txt
# 文件下载(base64 通道)
python jms_ops.py download /tmp/server_file.txt ./local_file.txt
```
> **注意**:首次使用会弹出浏览器完成 JumpServer 登录和 OTP 验证,后续调用会自动复用会话(30分钟内有效)。
### 2.4 为什么不能直接用 SSH/SCP
| 方式 | 支持情况 | 原因 |
|------|---------|------|
| SSH ProxyJump | ❌ 不支持 | JumpServer 不兼容标准 SSH 代理协议 |
| SCP 直连堡垒机 | ❌ 不支持 | 需要 OTP 验证码,SCP 不支持交互式输入 |
| SSH 直连目标服务器 | ❌ 不支持 | 目标服务器仅对 JumpServer 开放 SSH 访问 |
| jumpserver-ops | ✅ 推荐 | 自动化处理 OTP 和 Connection Token |
---
## 三、文件传输(通过堡垒机)
### 3.1 SCP 传输(推荐小文件/单次传输
### 3.1 jumpserver-ops 上传(推荐
```bash
# 上传单个文件
scp -o "ProxyJump=sxn@10.212.189.210:2222" \
it-smart-desk-server-deploy.zip \
sxn@10.90.5.110:/opt/
# 上传文件到目标服务器 /tmp 目录
# 自动选择最优方式:<100KB 用 base64(快),>=100KB 或 >15s 用 elFinder(稳定)
python jms_ops.py upload deploy.zip /tmp/deploy.zip
# 如果已配置 ~/.ssh/config
scp it-smart-desk-server-deploy.zip itdesk:/opt/
# 上传到其他目录
python jms_ops.py upload config.conf /opt/wecom-it-desk/config.conf
```
### 3.2 大文件传输优化
### 3.2 备用方案
部署包可能较大(含后端源码 + 前端产物),如果 SCP 速度慢,可以先传到堡垒机再转
如果 jumpserver-ops 不可用,可以考虑
```bash
# 步骤1:传到堡垒机
scp -P 2222 it-smart-desk-server-deploy.zip sxn@10.212.189.210:/tmp/
# 方式A:通过互联网可访问的存储服务(推荐)
# - 先把文件传到能通过互联网访问的存储(如:对象存储、临时文件分享服务)
# - 目标服务器通过 curl/wget 下载
# 步骤2SSH 到堡垒机
ssh -p 2222 sxn@10.212.189.210
# 步骤3:从堡垒机传到目标服务器
scp /tmp/it-smart-desk-server-deploy.zip sxn@10.90.5.110:/opt/
# 方式B:通过堡垒机手动中转
# - 使用 jumpserver-ops 手动执行分步操作
```
---
@@ -276,28 +274,86 @@ DNS 生效后(或配置了本地 hosts),在浏览器中访问:
## 五、部署文件结构
### 5.1 服务器目录结构
```
/opt/wecom-it-desk/
├── docker-compose.yml # Docker Compose 配置(4容器)
├── .env # 环境变量(已配置)
├── .env.example # 环境变量模板
├── deploy.sh # 一键部署脚本
├── README.md # 本手册
/opt/wecom-it-desk/ # 项目根目录(服务器)
├── docker-compose.yml # Docker Compose 配置(4容器)
├── .env # 环境变量(已配置)
├── .env.example # 环境变量模板
├── deploy.sh # 一键部署脚本
├── nginx/
── nginx.conf # Nginx 配置(反代 + 静态文件)
├── backend/
│ ├── Dockerfile # 后端镜像构建文件
│ ├── requirements.txt # Python 依赖
── app/ # 后端源代码
├── frontend-h5/
│ └── dist/ # H5 员工端构建产物
└── frontend-agent/
└── dist/ # 坐席工作台构建产物
── nginx.conf # Nginx 配置(反代 + 静态文件)
│ └── ssl/ # SSL 证书
├── html/ # 前端静态文件(Nginx 挂载点)
│ ├── itdesk/ # H5 员工端 (/itdesk/)
── itagent/ # 坐席工作台 (/itagent/)
│ ├── itadmin/ # 管理后台 (/itadmin/)
│ └── itportal/ # 统一入口 (/itportal/)
└── backend/ # 后端源码(不用于生产,仅开发参考)
```
### 5.2 前端部署位置说明
| 端 | URL 路径 | 服务器目录 | Nginx 容器挂载点 |
|----|---------|-----------|----------------|
| 员工端 H5 | `/itdesk/` | `/opt/wecom-it-desk/html/itdesk/` | `/usr/share/nginx/html/itdesk` |
| 坐席工作台 | `/itagent/` | `/opt/wecom-it-desk/html/itagent/` | `/usr/share/nginx/html/itagent` |
| 管理后台 | `/itadmin/` | `/opt/wecom-it-desk/html/itadmin/` | `/usr/share/nginx/html/itadmin` |
| 统一入口 | `/itportal/` | `/opt/wecom-it-desk/html/itportal/` | `/usr/share/nginx/html/itportal` |
### 5.3 前端部署步骤
```bash
# 1. 本地构建
cd frontend-agent
npm run build
# 2. 打包(排除 node_modules
cd dist
zip -r ../agent-v1.x.zip *
# 3. 上传到服务器 /tmp/
# 4. SSH 到服务器解压
sudo rm -rf /opt/wecom-it-desk/html/itagent
sudo mkdir -p /opt/wecom-it-desk/html/itagent
sudo unzip -o /tmp/agent-v1.x.zip -d /opt/wecom-it-desk/html/itagent
# 5. 重启 Nginx 容器
docker restart wecom_it_nginx
```
---
## 六、常用运维命令
## 六、Python 依赖管理
### 6.1 依赖说明
后端 Python 依赖在 `backend/requirements.txt` 中声明,构建 Docker 镜像时会自动安装。
**新增依赖处理流程:**
1. **开发环境**:更新 `backend/requirements.txt`
2. **打包部署**:确保 `requirements.txt` 已包含新依赖
3. **生产环境**:重建后端镜像
```bash
# 重建后端镜像(会自动安装 requirements.txt 中的所有依赖)
cd /opt/wecom-it-desk
docker compose build backend
docker compose up -d backend
```
### 6.2 常见依赖问题
| 问题 | 症状 | 解决方法 |
|------|------|---------|
| 缺少依赖 | `ModuleNotFoundError` | 重建后端镜像:`docker compose build backend` |
| 手动安装 | 容器内临时安装 | `docker exec wecom_it_backend pip install <package>` |
---
## 七、常用运维命令
在服务器上 `/opt/wecom-it-desk` 目录下执行:
@@ -318,7 +374,7 @@ DNS 生效后(或配置了本地 hosts),在浏览器中访问:
---
## 、升级前端
## 、升级前端
当有新的前端版本需要部署时:
@@ -344,7 +400,7 @@ docker exec wecom_it_nginx nginx -s reload
---
## 、升级后端
## 、升级后端
```bash
# 1. 上传新代码到服务器
@@ -360,7 +416,7 @@ cd /opt/wecom-it-desk
---
## 、故障排查
## 、故障排查
### 后端容器一直重启
@@ -427,7 +483,7 @@ curl -X POST http://localhost/api/h5/mock-login \
---
## 十、HTTPS 配置(可选)
## 十、HTTPS 配置(可选)
如果公司要求 HTTPS,有两种方式:
@@ -445,7 +501,7 @@ curl -X POST http://localhost/api/h5/mock-login \
---
## 十、部署说明
## 十、部署说明
> ⚠️ NAS部署方案(itdesk.amanzac.com)已于2026年6月15日下线,现统一使用公司内网服务器部署。
@@ -461,7 +517,7 @@ curl -X POST http://localhost/api/h5/mock-login \
---
## 十、相关文档
## 十、相关文档
| 文档 | 说明 |
|------|------|
@@ -0,0 +1,120 @@
# 502 Bad Gateway - 后端启动失败
> 日期:2026-07-05
> 问题:坐席端登录失败,返回 502 Bad Gateway
---
## 一、问题现象
用户访问 `https://itsupport.servyou.com.cn/itagent/` 时提示登录失败:
```
Failed to load resource: the server responded with a status of 502 (Bad Gateway)
AxiosError: Request failed with status code 502
```
---
## 二、诊断过程
### 2.1 检查容器状态
```bash
docker ps -a
```
发现后端容器状态为 `unhealthy`
```
CONTAINER ID IMAGE STATUS
656f7696d4e5 wecom-it-desk-backend:latest Up 8 minutes (unhealthy)
```
### 2.2 检查后端日志
```bash
docker logs 656f7696d4e5 --tail 30
```
发现错误:
```
ModuleNotFoundError: No module named 'aioredis'
```
### 2.3 原因分析
- 旧版镜像中代码使用 `import aioredis`
-`aioredis` 包与 Python 3.12 不兼容
- 报错:`TypeError: duplicate base class TimeoutError`
---
## 三、解决方案
### 3.1 尝试修复(失败)
尝试在容器内安装 `aioredis` 包,但发现:
- `aioredis` 与 Python 3.12 不兼容
- 安装后仍报错:`TypeError: duplicate base class TimeoutError`
### 3.2 最终方案
删除旧容器,使用正确的环境变量重新启动:
```bash
# 1. 删除旧容器
docker stop 656f7696d4e5
docker rm 656f7696d4e5
# 2. 使用正确的 PYTHONPATH 重新启动
cd /opt/wecom-it-desk
PYTHONPATH=/app docker compose up -d backend
```
关键点:**必须设置 `PYTHONPATH=/app`**,否则会报错 `ModuleNotFoundError: No module named 'app.core'`
---
## 四、验证结果
```bash
# 检查容器状态
docker ps
# 输出:
# 2ec80dee024c wecom-it-desk-backend:latest Up 5 minutes (healthy)
# e147524342fa redis:7-alpine Up 11 hours (healthy)
# 8a2265864f34 nginx:1.27-alpine Up 11 hours
# 433ef922c8d8 postgres:16-alpine Up 11 hours (healthy)
# 测试 API
curl http://localhost:8000/health
# 输出:{"status":"ok"}
# 测试页面
curl -sk https://localhost/itdesk/
# 输出:HTML 页面正常返回
```
---
## 五、根因总结
| 问题 | 原因 |
|------|------|
| 后端容器 unhealthy | 旧镜像使用 `import aioredis`,与 Python 3.12 不兼容 |
| 启动失败 | 需要设置 `PYTHONPATH=/app` 环境变量 |
---
## 六、预防措施
1. **更新镜像**:在 Dockerfile 中将所有 `import aioredis` 改为 `import redis.asyncio as aioredis`
2. **环境变量**:确保 docker-compose.yml 中设置 `PYTHONPATH=/app`
3. **健康检查**:定期检查容器健康状态
---
## 七、相关文件
- 部署配置:`/opt/wecom-it-desk/docker-compose.yml`
- Nginx 配置:`/opt/wecom-it-desk/nginx/nginx.conf`
- 后端代码:`/opt/wecom-it-desk/backend/`
@@ -4,7 +4,7 @@
>
> 📝 **更新规则**:每次 Claude 完成 / 开始 / 阻塞重要任务,会主动更新本文件。你也可以自己改(纯 markdown,git 跟踪)。
最后更新:**2026-07-04 17:14**(Claude 自动维护,#90 身份认证修复 + 生产部署)
最后更新:**2026-07-05 16:00**(Claude 自动维护,#100 消息推送策略优化已完成)
---
@@ -39,6 +39,13 @@
## ✅ 最近搞定
### 2026-07-05 生产问题修复
-**坐席端消息列表 500 错误**:添加 `current_agent` 参数到 `list_messages` 函数
-**坐席端消息发送失败**:安装缺失的 `wordfilter` 模块,补充文档
-**文档补充**:更新 requirements.txt 和部署手册,新增 Python 依赖管理章节
- 📝 详细记录:`docs/09-部署运维/deploy/12-问题修复记录-20260705.md`
### 2026-07-04 下午 (#90 身份认证修复 + 部署)
-**#90 Portal→H5 token传递修复**:路由守卫接收token后调用`fetchEmployeeInfo()`获取用户信息,修复token存在但用户信息未初始化的认证问题
@@ -64,7 +71,8 @@
| #86 | 排查流程图零依赖部分 review + 文档化 | 把 Mermaid 流程图从代码里剥离成可读文档 |
| #88 | 管理后台 RBAC 角色权限 | 管理后台细粒度角色权限(大功能,2-3 天) |
| #83 | 澄清"OTM 跟项目关系" | 已 2026-06-21 决策:走 TOTP+SMS 双引擎(MFA Phase 2 实施) |
| 🆕 | v0.7.0 部署 + 35 项 E2E 验收 | 看 `docs/DEPLOY-QUICK-v0.7.0.md` 6 步 + `docs/E2E-CHECKLIST-v0.7.0.md` |
| 🆕 | v0.7.0 部署 + 35 项 E2E 验收 | 看 `docs/09-部署运维/deploy/10-一键部署操作包-v0.7.0.md` 6 步 + `docs/06-测试质量/testing-测试/E2E-CHECKLIST-v0.7.0.md` |
| #100 | 消息推送策略优化与超时提醒 | ✅已完成:坐席回复仅推 H5,超时未回复发送企微提醒,10分钟后标记待关闭 |
| 🆕 | 修 64 pre-existing 测试失败 | Role.data_scope 缺字段 / WecomService DI / test_message_experience 等 |
## 🟢 P2 / 等用户决策
@@ -0,0 +1,140 @@
# 任务说明书:消息推送策略优化与超时提醒
> **版本**: v1.0 | **日期**: 2026-07-05
---
## 📋 基本信息
| 项目 | 内容 |
|------|------|
| **任务名称** | 消息推送策略优化与超时提醒 |
| **任务ID** | #100 |
| **优先级** | 🟠 P1 |
| **类型** | 功能开发 |
| **状态** | ✅ 已完成 |
| **负责人** | 宋献 |
| **创建日期** | 2026-07-05 |
| **计划完成日期** | 待定 |
---
## 📥 输入项来源
### 产品需求
| 来源文档 | 相关章节 | 说明 |
|----------|----------|------|
| `02-产品需求/04-技术方案-消息推送策略优化与超时提醒.md` | 全文 | 技术方案文档 |
### 技术架构
| 来源文档 | 相关章节 | 说明 |
|----------|----------|------|
| `backend/app/api/messages.py` | send_message 函数 | 现有消息发送逻辑 |
| `backend/app/models/conversation.py` | Conversation 模型 | 会话数据模型 |
### 项目看板
| 来源 | 任务名 | 说明 |
|------|--------|------|
| 用户反馈 | 消息推送策略 | 坐席回复不应出现在企微应用消息列表 |
---
## 📤 输出成果要求
### 交付物清单
| # | 交付物 | 类型 | 说明 |
|---|--------|------|------|
| 1 | 数据库迁移脚本 | SQL | conversations 表新增 4 个字段 |
| 2 | 提醒服务 | Python | `backend/app/services/reminder_service.py` |
| 3 | 定时任务 | Python | `backend/app/tasks/reminder_task.py` |
| 4 | 消息发送逻辑修改 | Python | 移除企微 API 调用,仅走 WebSocket |
| 5 | 部署验证 | - | 生产环境测试通过 |
### 代码要求
- 遵循项目代码规范
- 所有新增代码通过 Pylint 检查
- 单元测试覆盖新增逻辑
### 文档要求
- 更新本任务说明书状态
- 更新项目状态看板
---
## 🔧 验证方式
### 功能验证
| 验证项 | 验证方法 | 预期结果 |
|--------|----------|-----------|
| 坐席发送消息 | 坐席回复用户消息 | 仅出现在 H5 页面,不出现在企微应用消息 |
| 超时提醒 | 坐席回复后等待 3 分钟 | 收到企微提醒消息 |
| 提醒只发一次 | 再次等待 3 分钟 | 不再收到提醒 |
| 待关闭状态 | 坐席回复后等待 10 分钟 | 会话状态变为 pending_close |
### 安全验证
| 验证项 | 验证方法 | 预期结果 |
|--------|----------|-----------|
| 权限控制 | 非坐席无法触发 | 仅坐席回复触发逻辑 |
---
## ✅ 完成标准
### 验收条件
- [ ] 代码合入主干分支
- [ ] 功能测试通过
- [ ] 部署验证通过
### 产出确认
- [ ] 数据库迁移完成
- [ ] 后端代码修改完成
- [ ] 定时任务运行正常
- [ ] 生产环境验证通过
---
## 📊 工作分解
### 子任务
| 子任务 | 负责人 | 预估工时 | 状态 |
|--------|--------|----------|------|
| T1. 数据库迁移 | 宋献 | 0.5h | 待开始 |
| T2. 消息发送逻辑修改 | 宋献 | 0.5h | 待开始 |
| T3. 新建提醒服务 | 宋献 | 1h | 待开始 |
| T4. 新建定时任务 | 宋献 | 1h | 待开始 |
| T5. 定时任务注册 | 宋献 | 0.5h | 待开始 |
| T6. 部署测试 | 宋献 | 1h | 待开始 |
**总计**:约 4.5 小时
---
## 📞 依赖与阻塞
### 前置依赖
| 依赖任务 | 依赖说明 | 状态 |
|----------|----------|------|
| 无 | 独立任务 | - |
### 阻塞因素
| 阻塞项 | 影响范围 | 解决方案 |
|--------|----------|-----------|
| 无 | - | - |
---
## 📈 变更记录
| 日期 | 变更内容 | 变更人 | 说明 |
|------|----------|--------|------|
| 2026-07-05 | 创建任务 | 宋献 | 初始版本 |
---
## 📎 附件
- 技术方案:`docs/02-产品需求/04-技术方案-消息推送策略优化与超时提醒.md`
@@ -0,0 +1,145 @@
# 任务说明书 - 头像同步功能完善
> **版本**: v1.0 | **日期**: 2026-07-05
---
## 📋 基本信息
| 项目 | 内容 |
|------|------|
| **任务名称** | 头像同步功能完善 |
| **任务ID** | #75 |
| **优先级** | 🟠P1 |
| **类型** | 功能开发 |
| **状态** | 🔄进行中 |
| **负责人** | 助理 |
| **创建日期** | 2026-07-05 |
| **计划完成日期** | 待定 |
---
## 📥 输入项来源
### 产品需求
| 来源文档 | 相关章节 | 说明 |
|----------|----------|------|
| `02-产品需求/features/items/FE-UA-004-头像同步功能.md` | 全文 | 头像同步功能需求文档 |
| `02-产品需求/功能编号与文档关联表.md` | FE-UA-004, FE-SA-003 | 功能编号映射 |
| `02-产品需求/product-产品/v0.7.2-backlog-candidate-2026-06-24.md` | #75 | backlog任务 |
### 技术架构
| 来源文档 | 相关章节 | 说明 |
|----------|----------|------|
| `03-技术架构/00-系统架构设计文档-v1.3.md` | §4.4 身份认证 | 登录认证架构 |
| `backend/app/api/h5.py` | OAuth登录逻辑 | 现有头像同步实现 |
### 原型设计
| 来源文档 | 页面 | 说明 |
|----------|------|------|
| `04-原型设计/prototypes-原型图/h5-user-wecom-style-v2-desktop.html` | 会话列表 | 员工端头像显示 |
| `04-原型设计/prototypes-原型图/agent-workspace-v5_4.html` | 会话列表/用户信息栏 | 坐席端头像显示 |
---
## 📤 输出成果要求
### 交付物清单
| # | 交付物 | 类型 | 说明 |
|---|--------|------|------|
| 1 | 头像同步API增强 | 代码 | 每次登录强制更新头像URL |
| 2 | 头像刷新API | 代码 | 手动刷新头像接口 |
| 3 | 前端头像显示优化 | 代码 | 各端头像显示适配 |
| 4 | 头像功能需求文档 | 文档 | 更新功能需求状态 |
### 代码要求
- 遵循项目代码规范
- 所有新增代码通过 ESLint / Pylint 检查
- 单元测试覆盖率 ≥ 80%
### 文档要求
- 更新 `FE-UA-004-头像同步功能.md` 状态
- 更新 `功能编号与文档关联表.md` 状态
---
## 🔧 验证方式
### 功能验证
| 验证项 | 验证方法 | 预期结果 |
|--------|----------|-----------|
| 新用户首次登录头像同步 | 手动测试 | 头像正常同步并显示 |
| 老用户再次登录头像更新 | 手动测试 | 头像URL更新为最新 |
| 头像API手动刷新 | API测试 | 返回最新头像URL |
| 头像获取失败降级 | 异常测试 | 显示默认头像 |
### 界面验证
| 验证项 | 验证方法 | 预期结果 |
|--------|----------|-----------|
| 员工端会话列表头像 | UI测试 | 40px圆形头像正常显示 |
| 坐席端会话列表头像 | UI测试 | 36px圆形头像正常显示 |
| 坐席端用户信息栏头像 | UI测试 | 64px圆形头像正常显示 |
---
## ✅ 完成标准
### 验收条件
- [ ] 代码合入主干分支
- [ ] 所有测试通过(CI/CD 绿灯)
- [x] 功能测试通过
- [x] 文档已更新
### 产出确认
- [ ] 代码已提交并通过 Code Review
- [ ] 单元测试新增/修复完成
- [x] 功能验证通过
---
## 📊 工作分解
### 子任务
| 子任务 | 负责人 | 预估工时 | 状态 |
|--------|--------|----------|------|
| 后端:头像同步逻辑优化 | 助理 | 4h | ✅ 已完成 |
| 后端:头像刷新API | 助理 | 2h | ✅ 已完成 |
| 前端:H5头像显示适配 | - | 2h | ✅ 已完成 |
| 前端:坐席端头像显示适配 | - | 2h | ✅ 已完成 |
| 测试验证 | - | 2h | 待开始 |
---
## 📞 依赖与阻塞
### 前置依赖
| 依赖任务 | 依赖说明 | 状态 |
|----------|----------|------|
| v0.7.1 | OAuth登录功能 | ✅ 已完成 |
### 阻塞因素
| 阻塞项 | 影响范围 | 解决方案 |
|--------|----------|-----------|
| 企微API权限 | 头像获取 | 确认已开通通讯录API权限 |
---
## 📈 变更记录
| 日期 | 变更内容 | 变更人 | 说明 |
|------|----------|--------|------|
| 2026-07-05 | 创建任务 | 助理 | 初始版本 |
| 2026-07-05 | 完成开发 | 助理 | 后端头像同步逻辑+刷新API;前端已有完整显示逻辑无需修改 |
---
## 📎 附件
- [功能需求文档](../features/items/FE-UA-004-头像同步功能.md)
- [功能关联表](../功能编号与文档关联表.md)
- [技术架构文档](../../03-技术架构/00-系统架构设计文档-v1.3.md)
- [后端实现参考](../../backend/app/api/h5.py)