Files
wecom_it_smart_desk/docs/04-运维文档/部署运维/DEPLOY-REQ-用户-006-智能推荐重构-v1.0.md
T
Simon facc04aa65 chore: docs 结构整改 + compose 双目录对齐(合并重建提交)
本提交为 .git 对象库损坏后的重建提交,内容等价于原先三个本地提交
(5e2fd4c2 / 57a53c98 / 5d7e1873)的累积结果,未做任何额外改动。

一、docs 结构整改(整改 #14)
根因:重构时新结构为 untracked 文件,执行 git stash(未带 -u)未纳入,
随后 git reset 拉回 HEAD 旧 tracked 树,导致旧树复活、新旧两棵目录
树并存于 docs/,共 791 文件、双分类体系冲突。

修复动作:
- b2 同名异主题文件改名迁移保全 9 个
- C 类 39 个孤立文件按主题正确归类
- A/B1 类 222 个重复文件删除(新结构已有内容副本)
- 9 个旧独有空目录删除
- 270 处内部引用按 verified 映射改写
- 整改记录 #14 登记于 04-运维文档/部署运维

结果:docs 791 → 569 文件,顶层仅规范 8 类 + 治理文件,单树恢复。
残留:约 20 处指向从未存在文件的陈旧死链,归入独立文档卫生任务。

二、compose 双目录对齐(消除踩坑 A)
- docker-compose.yml:nginx 前端挂载全部由根目录 frontend-*/dist
  改为 src/frontend-*/dist(h5 / agent / admin / terminal)
- docker-compose.dev.yml:dev 服务 build context 与卷同步改 src/
- 效果:本地 docker compose up 不再把根目录 stale dist 挂回,
  与线上一致,分叉隐患消除(已 docker compose config 校验通过)

防复发铁律:
- 重构须提交;仓库修复须 git stash -u 或先 commit
- 新结构须 git add 并提交,避免再次 untracked 复活
- H5 改动只动 src/frontend-h5/,禁改根目录遗留 frontend-*/
2026-08-07 22:31:32 +08:00

16 KiB
Raw Blame History

部署运维说明 — H5 智能推荐重构(v1.0)

REQ 编号: REQ-用户-006 部署日期: 2026-07-28(首次部署) 版本: v1.0 关联文档:

  • PRD(冻结)docs/01-产品文档/05-用户端H5/PRD-REQ-用户-006-智能推荐重构-v1.0-Frozen.md
  • 技术方案docs/02-技术文档/技术架构/技术方案-REQ-用户-006-智能推荐重构-v1.0.md
  • 任务说明书docs/07-项目管理/任务说明书/任务说明书-REQ-用户-006-智能推荐重构.md
  • 原型图docs/01-产品文档/05-用户端H5/原型-REQ-用户-006-智能推荐重构-v1.0.html
  • 测试用例docs/03-测试文档/03-功能测试用例/TC-用户-006-智能推荐重构.md
  • 部署总手册docs/04-运维文档/部署运维/DEPLOY-GUIDE.md

一、变更摘要

维度 变更
后端新增 app/services/recommend_progress_service.pyT2 webhook + 60s 轮询)
后端新增 app/services/topic_detector.pyJaccard 相似度)
后端新增 app/api/recommend.pyREST API:进度查询 + webhook 接收)
后端重构 app/services/asset_recommend_service.pymerge_recommends + 同源抑制 + 中文匹配)
后端重构 app/config/assets.yaml(中文 role key + 同义词表 + 排除关键词)
后端扩展 app/services/employee_profile_service.pyDB 缓存画像降级)
后端集成 app/tasks/h5_ai_task.py::_step_assets3s 超时 + 降级链路)
后端集成 app/tasks/h5_ai_task.py::_step_persistsource/trigger_timing/layer 标识)
数据库 新增 recommend_progress + recommend_event 双表 + 索引
前端新增 src/frontend-h5/src/stores/recommendStore.tsPinia 状态机 + localStorage
前端新增 src/frontend-h5/src/composables/useRecommendWs.tsWS 接收处理)
前端重构 src/frontend-h5/src/components/assistant/DynamicRecommend.vue(无标题 + FIFO + 4 类卡片)
前端适配 src/frontend-h5/src/components/assistant/RightPanel.vue(引用 store
nginx 无改动

二、部署前 Checklist

2.1 文档就位(5 件套)

  • PRD v1.0-Frozen(含 §4.7 冻结声明 + §13 扩展计划)
  • 技术方案 v1.019 章节)
  • 任务说明书 v1.0(8 阶段 WBS)
  • 原型图 v1.07 场景)
  • 测试用例 v1.076 条用例)

2.2 代码就位

  • 中文路径 D:\资料\03-项目开发\wecom_it_smart_desk\src\ 已改
  • ASCII 路径 D:\dev\wecom\src\ 已同步改(多路径同步铁律
  • 后端 AST 静态校验通过(python -c "import ast; ast.parse(open(f).read())"
  • 前端 npm run build 通过(无 TS 报错)
  • 单元测试通过:pytest src/backend/tests/services/test_asset_recommend_v2.py -v
  • 单元测试通过:pytest src/backend/tests/services/test_recommend_progress.py -v
  • 单元测试通过:pytest src/backend/tests/services/test_topic_detector.py -v
  • 前端单元测试通过:vitest run recommendStore.test.ts

2.3 数据库迁移准备

  • Alembic 迁移脚本就位:alembic/versions/{revision}_add_recommend_progress.py
  • Alembic 迁移脚本就位:alembic/versions/{revision}_add_recommend_event.py
  • Init SQL 应急脚本就位(容器内 alembic 失败时备用):
    • scripts/init_recommend_tables.sql(含 ON CONFLICT DO NOTHING 幂等)
  • 迁移在 staging 环境 dry-run 通过

2.4 部署包就位

  • 后端部署包:backend-recommend-v1.0.zip(含 17 项代码 + Init SQL
  • 前端部署包:frontend-h5-recommend-v1.0.zip(含 dist 完整产物)
  • MD5 校验通过

2.5 灰度策略已对齐

  • 运维确认灰度名单(10 → 100 → 500 人)
  • 数据监控大盘已配置(点击率、自助解决率、降级次数)
  • 应急沟通群已建(产品 + 工程 + 运维)

三、部署顺序

铁律:后端 → 前端 → nginx(如有改动)→ DB 迁移 → 端到端验收

3.1 后端部署

3.1.1 压缩后端代码

# 在中文路径下打包
Compress-Archive -Path "D:\资料\03-项目开发\wecom_it_smart_desk\src\backend\app\*" -DestinationPath "D:\资料\03-项目开发\wecom_it_smart_desk\backend-recommend-v1.0.zip" -Force
# ASCII 副本也打包(防止源路径含中文导致的 GBK 误读)
Compress-Archive -Path "D:\dev\wecom\src\backend\app\*" -DestinationPath "D:\dev\wecom\backend-recommend-v1.0.zip" -Force

3.1.2 通过 jumpserver-V2 上传到 /tmp

# 使用 v2_ops.pyPowerShell 工具)
& "C:\Users\simon\.workbuddy\binaries\python\versions\3.13.12\python.exe" "C:\Users\simon\.workbuddy\skills\jumpserver-V2\scripts\v2_ops.py" upload "D:\dev\wecom\backend-recommend-v1.0.zip" "backend-recommend-v1.0.zip"

3.1.3 服务器解压到挂载源路径

# ⚠️ 必须用绝对路径(batch 模式 cd 不生效)
unzip -o /tmp/backend-recommend-v1.0.zip -d /tmp/backend-recommend-extract
# 备份旧代码
cp -r /opt/wecom-it-desk/app/app /tmp/app_bak_$(date +%Y%m%d_%H%M%S)
# 拷贝新代码(保留其他文件,只覆盖改动部分)
cp -rf /tmp/backend-recommend-extract/app/* /opt/wecom-it-desk/app/app/

3.1.4 数据库迁移(Alembic 双轨)

主路径Alembic 升级

# 容器内执行 alembic
docker exec -it wecom_it_backend alembic upgrade head

应急路径:手动执行 Init SQL(如果 alembic 失败)

# 上传 Init SQL
& "C:\Users\simon\.workbuddy\binaries\python\versions\3.13.12\python.exe" "C:\Users\simon\.workbuddy\skills\jumpserver-V2\scripts\v2_ops.py" upload "scripts\init_recommend_tables.sql" "init_recommend_tables.sql"

# 服务器执行
docker exec -i wecom_it_postgres psql -U postgres -d wecom_it_desk < /tmp/init_recommend_tables.sql

3.1.5 验证后端表创建

docker exec -i wecom_it_postgres psql -U postgres -d wecom_it_desk -c "\d recommend_progress"
docker exec -i wecom_it_postgres psql -U postgres -d wecom_it_desk -c "\d recommend_event"

3.1.6 重启后端(必须 --workers 1

# ⚠️ ws_manager 是进程内单例,多 worker 会导致 WS 消息丢失(~50%)
docker compose restart backend
# 验证 healthy
docker ps | grep wecom_it_backend

3.1.7 后端日志验证

# 应该看到资产推荐 + recommend_progress 启动日志
docker logs wecom_it_backend --tail 100 | grep -E "AssetRecommend|recommend_progress|Loaded.*recommend"

3.2 前端部署

3.2.1 本地构建(必须在 ASCII 路径,pnpm 不卡死)

cd D:\dev\wecom\src\frontend-h5
npm run build
# 验证 dist 产物含特征字符串
grep -r "recommendStore" dist/assets/ | head -5
grep -r "layer-progress" dist/assets/ | head -5

3.2.2 压缩 dist

Compress-Archive -Path "D:\dev\wecom\src\frontend-h5\dist\*" -DestinationPath "D:\dev\wecom\frontend-h5-recommend-v1.0.zip" -Force

3.2.3 上传 + 解压

& "C:\Users\simon\.workbuddy\binaries\python\versions\3.13.12\python.exe" "C:\Users\simon\.workbuddy\skills\jumpserver-V2\scripts\v2_ops.py" upload "D:\dev\wecom\frontend-h5-recommend-v1.0.zip" "frontend-h5-recommend-v1.0.zip"
# 服务器解压
unzip -o /tmp/frontend-h5-recommend-v1.0.zip -d /tmp/frontend-h5-extract
rm -rf /opt/wecom-it-desk/frontend-h5/dist
mv /tmp/frontend-h5-extract /opt/wecom-it-desk/frontend-h5/dist

3.2.4 重启 nginx(必须!bind mount 不会自动刷新新文件)

docker restart wecom_it_nginx

3.3 nginx 配置(无改动)

本次部署 无 nginx 配置变更


四、端到端验证

4.1 API 验证

# 1. 后端健康检查
curl -I http://localhost:8000/health

# 2. 推荐进度 API(如果有进行中的审批)
curl -H 'X-Forwarded-For: 10.240.1.100' http://localhost:8000/recommend/progress/test_approval_id

# 3. WS 端口可达
curl -I http://localhost:8000/ws/employee/{employee_id}

4.2 数据库验证

# 1. 表存在
docker exec -i wecom_it_postgres psql -U postgres -d wecom_it_desk -c "\dt recommend_*"

# 2. 索引存在
docker exec -i wecom_it_postgres psql -U postgres -d wecom_it_desk -c "\di idx_recommend_*"

# 3. recommend_progress 表能 INSERT
docker exec -i wecom_it_postgres psql -U postgres -d wecom_it_desk -c "INSERT INTO recommend_progress (recommend_id, employee_id, approval_id, approval_type, status) VALUES ('rec_test', 'test_emp', 'test_appr', 'vpn_access', 'pending') RETURNING id;"

4.3 前端验证(agent-browser 自动 + 用户人工)

# agent-browser 自动验证
# 1. 打开 H5 员工端
# 2. 进会话但不发言 → 验证右侧栏完全空白(决策 ① A)
# 3. 发"VPN 申请" → 验证左侧气泡 + 右侧栏操作卡
# 4. 切换话题 → 验证 L1 清空
# 5. 关闭浏览器 → 重新打开 → 验证 L2/L3/progress 持久化

4.4 用户人工验证(必做,企微扫码限制)

  • 登录 H5 员工端(需企微扫码)
  • 进入新会话,不发言,观察右侧栏空白
  • 发"VPN 怎么连",观察左侧气泡 + 右侧栏操作卡
  • 切换话题到"会议室",观察 L1 清空
  • 触发审批,观察进度卡回流
  • 关闭浏览器重新打开,观察持久化卡

五、灰度策略

5.1 4 阶段灰度

阶段 规模 持续时间 通过条件 不达标处理
1% 10 人(运维 + 产品 + 工程) 1 天 无 P0/P1 错误 立即回滚
10% 100 人(早期种子用户) 2 天 点击率 > 5%(无 P0/P1 错误) 暂停灰度排查
50% 500 人(半个部门) 3 天 点击率 > 10% 暂停灰度排查
100% 全量(约 7000 人) 长期 点击率 > 15%,自助解决率 > 10% 长期监控优化

5.2 数据埋点验证(关键指标)

通过 recommend_event 表统计:

-- 各 layer 推荐曝光数
SELECT layer, source, COUNT(*) AS exposure_count
FROM recommend_event
WHERE event_type = 'shown'
  AND created_at > NOW() - INTERVAL '24 hours'
GROUP BY layer, source;

-- 各 layer 推荐点击数
SELECT layer, source, COUNT(*) AS click_count
FROM recommend_event
WHERE event_type = 'clicked'
  AND created_at > NOW() - INTERVAL '24 hours'
GROUP BY layer, source;

-- 点击率
SELECT
  shown.layer,
  shown.source,
  shown.exposure_count,
  COALESCE(clicked.click_count, 0) AS click_count,
  ROUND(100.0 * COALESCE(clicked.click_count, 0) / shown.exposure_count, 2) AS click_rate
FROM (
  SELECT layer, source, COUNT(*) AS exposure_count
  FROM recommend_event
  WHERE event_type = 'shown'
  GROUP BY layer, source
) shown
LEFT JOIN (
  SELECT layer, source, COUNT(*) AS click_count
  FROM recommend_event
  WHERE event_type = 'clicked'
  GROUP BY layer, source
) clicked ON shown.layer = clicked.layer AND shown.source = clicked.source;

5.3 降级监控

-- 降级触发次数(C → D / 全部失败)
SELECT
  DATE(created_at) AS date,
  extra->>'degradation_path' AS degradation,
  COUNT(*) AS count
FROM recommend_event
WHERE event_type = 'degraded'
  AND created_at > NOW() - INTERVAL '7 days'
GROUP BY date, degradation
ORDER BY date DESC;

六、回滚预案

6.1 触发条件(任一)

  • P0/P1 错误率 > 5%
  • 推荐卡片渲染失败 > 2%
  • WS 推送延迟 > 5s
  • 后端 healthy 状态丢失
  • 推荐卡数量异常(同一用户 > 5 张)

6.2 回滚步骤(< 30 分钟)

Step 1: 前端回滚(5 分钟)

# 备份当前 dist
mv /opt/wecom-it-desk/frontend-h5/dist /opt/wecom-it-desk/frontend-h5/dist_bak_$(date +%Y%m%d_%H%M%S)
# 恢复备份的 dist
mv /opt/wecom-it-desk/frontend-h5/dist_rollback /opt/wecom-it-desk/frontend-h5/dist
# 重启 nginx
docker restart wecom_it_nginx
# 验证
curl -I http://localhost/itdesk/

Step 2: 后端回滚(10 分钟)

# 停止后端
docker compose stop backend
# 恢复代码
rm -rf /opt/wecom-it-desk/app/app
mv /opt/wecom-it-desk/app_bak_$(ls -t /opt/wecom-it-desk/app_bak_* | head -1 | sed 's/.*app_bak_/app_bak_/') /opt/wecom-it-desk/app/app
# 重启后端
docker compose up -d backend
# 验证
curl -I http://localhost:8000/health

Step 3: 数据库回滚(5 分钟,极端情况)

# Alembic 降级
docker exec -it wecom_it_backend alembic downgrade -1
# 或手动 DROP 新增双表
docker exec -i wecom_it_postgres psql -U postgres -d wecom_it_desk -c "DROP TABLE IF EXISTS recommend_progress CASCADE; DROP TABLE IF EXISTS recommend_event CASCADE;"

Step 4: 通知(5 分钟)

  • 群发"已回滚至 v2.3 现状"通知到产品 + 工程 + 客服群
  • 收集失败原因,写入 BUG 单
  • 排查修复后重新走灰度流程

6.3 回滚时间预算

阶段 时间
Step 1 前端 5 分钟
Step 2 后端 10 分钟
Step 3 DB 5 分钟
Step 4 通知 5 分钟
合计 < 30 分钟

七、风险与边界

7.1 部署期风险

风险 缓解措施
Alembic 迁移失败 Init SQL 应急脚本
models/__init__.py 整体覆盖丢失其他模型 字符串 replace 比 sed/awk 稳(详见任务说明书 §1.4 关键决策点)
前端 dist build 触发 safe-delete hook Rename-Item + .NET Delete 绕路
后端 --workers 不为 1 docker-compose.yml 校验,强制 1 worker
中文字符乱码 容器内 locale 检查 + UTF-8 编码
蓝绿共用 PG/Redis 干扰 仅升级不降级,DB migration 谨慎执行

7.2 运行期风险

风险 等级 缓解措施
Dify 升级 action 结构变化 兼容旧字段解析
画像 API 长时间不可用 DB 缓存画像降级(来源 C 改造)
localStorage 配额超限(5MB 仅持久 L2/L3/progressLRU 淘汰
企微 webhook 推送失败 60s 轮询兜底
灰度期间指标不达标 每阶段不达标暂停

八、常见问题(FAQ

Q1: Alembic 容器内不可用怎么办?

A: 使用 Init SQL 应急脚本(详见 §3.1.4)。

Q2: ws_manager 多 worker 导致 WS 消息丢失怎么办?

A: 后端必须 --workers 1docker-compose.yml 已强制。

Q3: 中文路径下 pnpm install 卡死怎么办?

A: 在 ASCII 路径 D:\dev\wecom\ 下执行 npm run build(详见多路径同步铁律)。

Q4: 前端 build 触发 safe-delete hook 怎么办?

A: 使用 Rename-Item dist __dist_movetmp(同目录)→ vite 跳过 fs.rmSync → build 完 .NET Delete(详见 memory 跨项目铁律)。

Q5: 模型注册丢失怎么办?

A: 不能整体覆盖 models/__init__.py,需用 Python 脚本字符串 replace(详见任务说明书 §1.4)。

Q6: 容器内 alembic.ini 路径不对怎么办?

A: 使用绝对路径 docker exec -it wecom_it_backend alembic -c /app/alembic.ini upgrade head

Q7: psftp 上传大于 100KB 文件慢怎么办?

A: 已优化,使用 v2_ops.py upload 子命令,自动 md5 校验 + 断点续传。

Q8: 灰度名单怎么选?

A: 1% 选运维 + 产品 + 工程核心成员;10% 选早期种子用户(高频用户);50% 选半个部门(覆盖不同角色);100% 全量。


九、变更日志

版本 日期 变更内容 作者
v1.0 2026-07-28 19:36 初版:基于 PRD v1.0-Frozen + 技术方案 v1.0 + 任务说明书 v1.0,给出完整部署 SOP(5 件套齐全):后端 7 步 + 前端 4 步 + DB 迁移双轨 + 端到端验证 + 4 阶段灰度 + 30 分钟回滚 + 8 FAQ Duckula + 宋献