跳转至

听云 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 分钟内回传回执 —