听云 OCM 部署任务单 V2(聚焦版)· 生产仅 MFR 开放报价¶
| 字段 | 值 |
|---|---|
| 任务编号 | OCL-DEPLOY-20260827-02(V2 · 取代此前 V1 的 B1+B2 外派/催办/跟进) |
| 签发方 | 听写 DT |
| 执行方 | 听云 OCM(阿里云 ECS 8.218.165.94 root SSH) |
| 签发时间 | 2026-08-27 |
| 签发依据 | 用户指令:①「听云的工作阻塞,请了解」②「我现在只需要听云在阿里云部署 MFR 开放报价这个单一的功能,在本地则需要全部功能,请向听云正确安排任务」 |
| 三 AI 边界 | 听写仅签发本任务单,不 SSH ECS;听云只执行本文列出的 2 件事,不做其他任何改动 |
一、范围边界(必须先读本条!避免越权)¶
✅ 生产阿里云本次部署「只做这 1 个功能」¶
MFR 开放报价 = 2 个组件(本文任务覆盖):
1. MFR-Portal 前端:Next.js standalone,监听 127.0.0.1:3000,Caddy 暴露 /mfr/*
2. MFR Open Quote API:后端无需部署(已在运行 ✅),Caddy /api/* 反代已存在,端点 POST /api/v2/qsv/quote/mfr-open
🚫 生产阿里云本次「明确不做」的功能(全部保持现状)¶
- 不升级后端 API 版本(当前 v2.0.0,SCAT V1.1 v2.1 不部署,见 D03 声明)
- 不修改 OPR-Admin / WKR / CST / Download 四端的 systemd 或 Caddy 配置
- 不重建数据库、不跑数据迁移
- 不部署 SCAT/RNV 等新设计的业务功能
- 不修改其他任何未在本任务单列出的 ECS 本地文件
💻 本地开发环境(dip1/)范围说明¶
- 听云无需关心本地(本地=全功能,由听码 WDE 维护),听云只操作生产 ECS
- 本文档"本地"字样仅作范围声明对照,听云任务只涉及生产阿里云 ECS
二、生产现状基线(听云执行前先核对,避免重复劳动)¶
执行前 5 秒公网核对(在你自己的电脑或 ECS 本地 curl 均可):
# 基线-1:后端 API 已正常运行(应该 200 + version=2.0.0 + db=ok)
curl -s http://8.218.165.94/health
# 期望:{"status":"ok","service":"weavely-dip1-api","version":"2.0.0","env":"prod","db":"ok"}
# 基线-2:Caddy /api/* 反代已存在(应该 422 或 405,说明 /api/* 能通到后端,不是 404/502)
curl -s -o /dev/null -w "api=%{http_code}\n" -X POST http://8.218.165.94/api/v2/qsv/quote/mfr-open
# 期望:400~499 之间(表示请求到了后端,只是没带 token/参数,不是 404/502)
# 基线-3:MFR 目前 404(本次任务就是要把这个从 404 → 200)
curl -s -o /dev/null -w "mfr=%{http_code}\n" http://8.218.165.94/mfr/
# 期望当前=404(若已经是 200 → 无需执行!直接回执"已完成"即可)
👉 如果基线-1 或基线-2 异常(不是期望输出),请先把 curl 实际输出回传,再决定是否继续执行 B1/B2。不要先动 Caddy/systemd!
三、任务分解(只有 2 件事,做完即止)¶
🎯 任务 1 / 2:启动 mfr-portal systemd 服务(B1 · 最简 3 条命令)¶
前置:
/etc/systemd/system/mfr-portal.service已就位(WorkingDirectory=/opt/dip1/dip1/frontend/apps/mfr-portal,PORT=3000)
ECS 本地执行(整块复制粘贴):
# 1-1 启用并启动(enable + start 合并)
systemctl enable --now mfr-portal.service 2>&1
echo "--- 1-2 active检查 ---"
systemctl is-active mfr-portal.service
echo "--- 1-3 :3000监听检查 ---"
ss -tlnp | grep ':3000'
echo "--- 1-4 HTTP探活 ---"
curl -s -o /dev/null -w "localhost:3000=%{http_code}\n" --max-time 5 http://127.0.0.1:3000/
任务 1 通过标准(全部满足才算过):
| 检查点 | 期望输出 | 不满足时怎么查(3 选 1 取 20 行贴回) |
|-------|---------|-----------------------------------|
| is-active | active | journalctl -u mfr-portal.service -n 30 --no-pager |
| ss :3000 | 含 LISTEN + node | cat /var/log/mfr-portal.err | tail -20 |
| curl :3000 | 200 | ls -la /opt/dip1/dip1/frontend/apps/mfr-portal/.next/standalone/apps/mfr-portal/server.js |
🎯 任务 2 / 2:Caddy 加一条 /mfr/* 反代(B2 · 极简 sed 一键)¶
关键原则:只加 1 个 handle_path 块,不改其他任何行;加在
/admin/*之前;先 validate 再 reload。
ECS 本地执行(整块复制粘贴,无需打开 nano/vim):
# 2-0 锚点检查(先跑这条,输出一行=OK,0行=模板和预期不一致→立即停手并回传 grep 输出)
echo "--- 锚点 ---" && grep -n "OPR-Admin Next.js" /etc/caddy/Caddyfile
# 2-1 备份(必须,留一键回滚抓手)
cp /etc/caddy/Caddyfile /etc/caddy/Caddyfile.bak.$(date +%Y%m%d%H%M%S)
echo "--- 备份文件 ---" && ls /etc/caddy/Caddyfile.bak.* | tail -1
# 2-2 插入 MFR 块(用 awk 在锚点注释行上方插入,比 sed 更稳兼容换行)
gawk -i inplace '
/OPR-Admin Next\.js/ {
print " # ========== MFR-Portal Next.js(handle_path 剥离 /mfr 前缀) =========="
print " handle_path /mfr/* {"
print " reverse_proxy http://127.0.0.1:3000"
print " }"
print ""
}
{print}
' /etc/caddy/Caddyfile
echo "--- 插入后顺序检查(/dl/→/mfr/→/admin/ 必须按此顺序从上到下)---"
grep -n "handle_path /mfr/\|handle /admin/\|handle_path /dl/" /etc/caddy/Caddyfile
# 2-3 validate(必须过,否则别 reload!)
echo "--- validate ---" && caddy validate --config /etc/caddy/Caddyfile 2>&1 | tail -3
# 2-4 reload(热重载,不停服)
systemctl reload caddy 2>&1 && echo "--- reload OK ---"
# 2-5 3 条验收断言(A4~A6,从 ECS 本地打 127.0.0.1)
echo "--- A4 /mfr/ root ---" && curl -s -o /dev/null -w "%{http_code}\n" --max-time 5 http://127.0.0.1/mfr/
echo "--- A5 /mfr/login ---" && curl -s -o /dev/null -w "%{http_code}\n" --max-time 5 http://127.0.0.1/mfr/login
echo "--- A6 不含/admin/_next/(期望=0)---" && curl -s --max-time 5 http://127.0.0.1/mfr/ | grep -c '/admin/_next/'
任务 2 通过标准(全部满足才算过):
| 检查点 | 期望 | 不满足时怎么处理 |
|-------|------|----------------|
| 顺序检查输出 | 从上到下依次出现 /dl/ → /mfr/ → /admin/(/mfr/ 必须在 /admin/ 上面) | 顺序不对 → cp Caddyfile.bak.<时间戳> Caddyfile 回滚,再跑一遍 gawk 插入命令 |
| caddy validate | 输出含 valid configuration | 语法错 → 回滚 bak 到 Caddyfile,再 validate 确认基线,然后把错误原文贴回 |
| systemctl reload | 无报错,输出 reload OK | reload 抛错 → systemctl status caddy -n 30 取 30 行贴回 |
| A4 /mfr/ | 200 | 502 → 回查任务 1 的 :3000 是否仍在 LISTEN;404 → 回查顺序检查(是不是还在 /admin/ 下面) |
| A5 /mfr/login | 200 | 同上 |
| A6 grep 计数 | 0(一次都没出现 /admin/_next/) | ≥1 → 说明顺序错了,被兜底抢走,回滚 bak 后重新插入 |
💡 最常见失败 = 插入顺序不对。如果 gawk 插入后顺序检查
/mfr/不在/dl/和/admin/之间,直接回滚 bak 然后用 nano/vim 打开,找到# ========== OPR-Admin Next.js这行注释,在它上面手动粘贴 5 行 MFR 块(见 §三任务 2-2 gawk 里 print 的 5 行),保存后再 validate + reload。
四、完成回执(最简 10 行,复制粘贴填值 ≤ 30 秒)¶
无论是否完成、是否阻塞,执行完毕后 5 分钟内必须回传本表。阻塞就填"阻塞原文"到最后一行,不用先排查完再回传。
[听云执行回执 OCL-DEPLOY-20260827-02]
基线 /health = ___(期望 {"status":"ok"...2.0.0...db:"ok"})
基线 API 状态码 = ___(期望 4xx,不是 404/502)
基线 MFR 原状态 = ___(原 404 或原 200)
---
任务1 A1 active = ___(active/inactive/failed)
任务1 A2 :3000 = ___(LISTEN 或 NO)
任务1 A3 :3000码 = ___(200/其他)
---
任务2 A4 /mfr/码 = ___(200/其他)
任务2 A5 /mfr/login= ___(200/其他)
任务2 A6 admin_next= ___(0/1+/N/A)
---
OCL 日志编号 = L-YYYYMMDD-NN
完成时间戳 = YYYY-MM-DD HH:MM
阻塞/备注 = ___(无=N/A;有=贴错误原文至少 20 行)
五、阻塞升级通道¶
听云执行中遇到任何阻塞,不要硬扛超过 10 分钟,直接把错误原文(至少 20 行输出)贴到协作通道,听写 10 分钟内响应:
| 阻塞类型 | 听云做什么 | 听写做什么 |
|---|---|---|
基线-1 /health 非期望 |
停止后续操作,回传 curl 原文 | 判定是否后端挂了,是否需要先单独处理 API |
| 任务 1 A1=failed | 回传 journalctl + err log 各 20 行 | 判定是否 standalone 缺失 → 需要则协调听码重打包 MFR |
| 任务 1 A3 非 200(node 崩溃/超时) | 回传 journalctl -n 30 + cat .err | tail -30 |
给出具体修复命令或代码层面建议 |
| 任务 2 validate 失败反复不通过 | 回滚 bak → 回传 validate 报错原文 + 顺序检查输出 | 直接发完整 Caddyfile 目标片段给听云整块替换 |
| 任务 2 reload 后 A4/A5 仍 404 | 回传顺序检查输出 + A6 结果 | 定位是否顺序问题或 Caddy 未真正 reload;必要时听写发逐行操作指引 |
| 其他任何未知问题 | 回传至少 20 行错误原文 + 已尝试的操作 | 诊断后给针对性方案,或升级用户协调 |
— 任务单 V2 签发完毕(仅 MFR 开放报价),听云执行后 5 分钟内回传回执 —