# HANDOFF — 离线模式双界面 + 文档上传管理

> 最后更新：2026-08-03 ｜ 分支：troubleshoot-ai-assistant ｜ 负责人：czj

---

## 1. 我们在做什么

### 1.1 问题排查双模式界面升级

在已有在线 AI 分析模式基础上，升级离线模式（`OFFLINE_MODE=true`）为全新产品树界面：

- **在线模式**（`OFFLINE_MODE=false`）：保持原有 AI 分析界面不变
- **离线模式**（`OFFLINE_MODE=true`）：左侧产品树 + 右侧资料下载/常见问题面板

前端入口 `Index.vue` 根据 `/api/health` 的 `offline_mode` 自动切换两套界面。

### 1.2 管理员文档上传/删除功能

因 Windows ACL 权限问题导致本地维护手册文件无法通过 git/tools 上传到服务器，在页面上增加了管理员专属的上传/删除文档功能，通过浏览器直接上传到服务器。

---

## 2. 已经完成了什么

### 2.1 后端产品 API ✅

| 子任务 | 说明 | 验证 |
|--------|------|------|
| `products.json` | 9 个产品配置（门口屏/无纸化/智屏集控/蓝牙基站/双屏互动/会议助手/语音助手/IDEATOP-安卓/IDEATOP-中控），28 份文档映射 | `curl /api/troubleshoot/products` 返回正确 |
| `product_service.py` | 产品服务模块：get_products/get_product/get_documents/get_document_path/add_document/delete_document | Python 模块加载正常 |
| `search_engine.py` | 新增 `search_by_product()` 方法（按 apk_product + tags 筛选知识库记录） | 单元测试全绿 |
| API 端点 | `/api/troubleshoot/products`、`/products/{id}`、`/products/{id}/documents`、`/documents/{id}/download`、`/documents/upload`、`/documents/{id}/delete`、`/products/{id}/faqs` | 全链路 HTTP 验证通过 |

### 2.2 前端双模式界面 ✅

| 子任务 | 文件 | 说明 |
|--------|------|------|
| 模式切换入口 | `frontend/src/views/troubleshoot/Index.vue` | 调用 `/api/health` 判断模式，动态加载 OnlineIndex 或 OfflineIndex |
| 在线模式 | `frontend/src/views/troubleshoot/OnlineIndex.vue` | 原 Index.vue 内容，AI 分析界面保持不变 |
| 离线模式主布局 | `frontend/src/views/troubleshoot/OfflineIndex.vue` | 左右分栏：左侧产品树 + 右侧内容区 |
| 产品树组件 | `frontend/src/components/troubleshoot/ProductTree.vue` | 搜索框 + 展开折叠 + 产品资料/常见问题子菜单 |
| 产品资料面板 | `frontend/src/components/troubleshoot/DocumentsPanel.vue` | 文档列表 + 下载 + **上传/删除（管理员）** |
| 常见问题面板 | `frontend/src/components/troubleshoot/FAQsPanel.vue` | 已置空，显示「常见问题整理中」 |
| API 封装 | `frontend/src/api/troubleshoot.ts` | 新增 getProducts/getProduct/getProductDocuments/downloadDocument/uploadDocument/deleteDocument |
| 类型定义 | `frontend/src/types/troubleshoot.ts` | 新增 Product/ProductDocument/FAQ 等类型 |

### 2.3 文档上传/删除功能 ✅

| 功能 | API | 验证 |
|------|-----|------|
| 上传 | `POST /api/troubleshoot/documents/upload` (multipart/form-data) | curl 测试上传 4395 bytes → 下载成功 |
| 删除 | `DELETE /api/troubleshoot/documents/{id}/delete` | curl 测试删除成功 |
| 权限 | 仅 admin 可上传/删除 | 非 admin 返回 403 |

### 2.4 部署 ✅

| 子任务 | 说明 |
|--------|------|
| 后端代码 | `product_service.py`、`routes/troubleshoot.py` 已部署到 5.60 容器内 |
| 前端构建 | `npm run build` 产物已部署到 5.60 前端目录 |
| Nginx | 已配置 `client_max_body_size 100m` 支持大文件上传 |
| 容器重启 | 已重启，服务正常运行 |

---

## 3. 当前卡在哪

**卡点：`Docs/维护手册/` 下的 28 个 docx/pdf 文件因 Windows ACL 权限拒绝访问，无法通过 git、Python、PowerShell 等任何方式读取或上传。**

- 错误：所有文件读取操作返回 `PermissionError: [Errno 13] Permission denied`
- 影响：服务器上的对应文件全部为 0 字节空文件
- 根本原因：文件 ACL 中存在 Deny 规则（`S-1-22-1-1106 Deny Modify`）

**解决方案**：已实现页面上传功能，管理员可通过浏览器直接上传文件到对应产品目录。用户可通过此功能重新上传那 28 份文档。

---

## 4. 下一步计划

| 优先级 | 任务 | 说明 |
|--------|------|------|
| **P1** | 通过页面上传 28 份维护手册文档 | 用 admin 账号登录，在产品资料面板中逐产品上传文件 |
| **P2** | 提供常见问题（排查指引）内容 | 用户整理各产品常见问题的排查步骤后，解除 FAQsPanel 的空状态 |
| **P3** | 清理 deploy/ 下的临时脚本 | 删除本次调试产生的 upload_docs_v*.py、encode_docs.ps1 等临时文件 |
| **P4** | 提交未提交的代码变更 | `git status` 显示还有前端组件和后端 service 的修改待提交 |
| **P5** | 启用 product_service.py 的 faq 加载逻辑 | 移除 `loadData()` 中的 `return` 占位语句 |

---

## 5. 踩过的坑（绝对不要再踩）

### 坑 1：Windows 中文路径文件 ACL 拒绝访问

**坑**：`Docs/维护手册/` 下的所有 docx/pdf 文件，Python（`open()`、`pathlib.read_bytes()`、`os.open()`）、Git（`git add`）、PowerShell（`Copy-Item`）、SFTP 均返回 `Permission denied`。

**原因**：文件 ACL 中存在 Deny 规则 `S-1-22-1-1106 Deny DeleteSubdirectoriesAndFiles, Modify, ChangePermissions, TakeOwnership`。可能是文件从其他路径复制时带入的权限，或 OneDrive/杀毒软件锁定。

**避免方法**：
1. 上传附件前先检查文件属性 → 安全 → 确认当前用户有读取权限
2. 如果文件被锁定，先复制到新文件夹（会重置 ACL），再操作
3. **终极方案**：通过浏览器上传，完全绕过本地文件系统权限

### 坑 2：Git Bash 中 `$_` 变量被展开

**坑**：在 Git Bash 内联运行 PowerShell 命令时，`$_`、`$files` 等 PowerShell 变量被 Bash 解析为环境变量展开。

**避免方法**：始终将 PowerShell 脚本放在 `.ps1` 文件中执行，不要内联在 Bash 命令行中。

### 坑 3：SFTP `sftp.file(path, 'wb')` 写入中文路径文件为 0 字节

**坑**：SFTP 的 `sftp.file()` 对含中文路径的文件写入后，`stat()` 确认写入成功但实际文件大小为 0。

**原因**：paramiko SFTP 客户端编码问题，中文路径在创建文件时成功（0 字节），但写入操作静默失败。

**避免方法**：使用 `sftp.putfo(io.BytesIO(data), path)` 替代 `sftp.file()` 进行上传。

### 坑 4：`docker cp` 中文路径复制也为 0 字节

**坑**：`sudo docker cp /data/.../门口屏/01-文档.docx troubleshoot:/app/...` 同样产生 0 字节文件。

**原因**：Docker cp 对中文路径文件也有编码问题。

**避免方法**：使用 `tar cf - ... | docker exec -i ... tar xf -` 管道方式。

### 坑 5：`echo "{base64}" | base64 -d` 对大文件失败

**坑**：将 base64 编码的文件内容通过 `echo` 管道给 `base64 -d`，对大文件（>10MB）会截断。

**原因**：shell 命令行参数长度限制 + echo 对大字符串处理有问题。

**避免方法**：先把 base64 内容写入临时文件，再 `base64 -d file.b64 > output`。

---

## 6. 关键文件与命令速查

### 本次新增/修改文件

| 文件 | 说明 |
|------|------|
| `skill/code/web/products.json` | 产品配置文件（9 产品 / 28 文档映射） |
| `skill/code/web/services/product_service.py` | 产品服务模块（含上传/删除方法） |
| `skill/code/web/routes/troubleshoot.py` | 新增 upload/delete API + 产品 API |
| `skill/code/web/search_engine.py` | 新增 `search_by_product()` |
| `frontend/src/views/troubleshoot/Index.vue` | 模式切换入口 |
| `frontend/src/views/troubleshoot/OnlineIndex.vue` | 在线模式界面（原内容） |
| `frontend/src/views/troubleshoot/OfflineIndex.vue` | 离线模式主布局 |
| `frontend/src/components/troubleshoot/ProductTree.vue` | 产品树组件 |
| `frontend/src/components/troubleshoot/DocumentsPanel.vue` | 产品资料面板（含上传/删除） |
| `frontend/src/components/troubleshoot/FAQsPanel.vue` | 常见问题面板（已置空） |
| `frontend/src/types/troubleshoot.ts` | 新增离线模式类型 |
| `frontend/src/api/troubleshoot.ts` | 新增产品/上传/删除 API |
| `frontend/src/router/index.ts` | /troubleshoot 路由统一入口 |
| `nginx/nginx.conf` | 新增 `client_max_body_size 100m` |
| `Dockerfile` | 新增维护手册目录 COPY |
| `deploy/deploy_docker.py` | 新增维护手册目录上传路径 |

### 常用命令

```bash
# 前端构建
cd frontend && npm run build

# 后端单元测试（218 用例）
cd skill/code && python -m pytest -v

# 部署前端到 5.60
python deploy/deploy_frontend.py

# 部署后端代码到 5.60
python deploy/upload_backend.py

# 验证服务
curl http://192.168.5.60:8088/api/health
curl http://192.168.5.60:8088/api/troubleshoot/products

# 测试上传（需 admin session）
curl -c /tmp/c.txt -X POST http://192.168.5.60:8088/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"Ubains@1357"}'
curl -b /tmp/c.txt -X POST http://192.168.5.60:8088/api/troubleshoot/documents/upload \
  -F "file=@test.pdf" -F "product_id=door-screen"

# 产品与文档目录映射
# 服务器：/data/third_party/monitor-platform/Docs/维护手册/{产品名}/{文件名}
# 容器内：/app/Docs/维护手册/{产品名}/{文件名}
```

### 服务器信息

- **SSH**：`ubains@192.168.5.60`，密码：`Ubains@123`
- **部署目录**：`/data/third_party/monitor-platform`
- **服务地址**：`http://192.168.5.60:8088`
- **admin 密码**：`Ubains@1357`
- **离线模式**：`OFFLINE_MODE=true`（`.env` 配置）
