/** * CC 烟雾验收 * * pnpm cc:smoke # 跟随 .env:mock �?live * pnpm cc:smoke -- --mode=mock # 强制 mock(不连网�? * pnpm cc:smoke -- --mode=live # 强制 live:需 CC_MOCK=false + 账号 * * Exit: 0 ok | 1 配置错误 | 2 CC 业务失败 | 3 模式不匹�? */ import { getEnv, resetEnvCache } from "../src/lib/env"; import { getCcModeStatus } from "../src/services/cc/mode"; import { saveContainer } from "../src/services/cc/save-container"; import { getShippingLineList } from "../src/services/cc/shipping-line"; import { getContainerListFromCc } from "../src/services/cc/conflict"; type ModeArg = "auto" | "mock" | "live"; function parseMode(argv: string[]): ModeArg { const raw = argv.find((a) => a.startsWith("--mode="))?.slice("--mode=".length); if (raw === "mock" || raw === "live" || raw === "auto") return raw; const idx = argv.indexOf("--mode"); if (idx >= 0) { const v = argv[idx + 1]; if (v === "mock" || v === "live" || v === "auto") return v; } return "auto"; } async function main() { resetEnvCache(); const env = getEnv(); const requested = parseMode(process.argv.slice(2)); const status = await getCcModeStatus(); console.log( JSON.stringify( { requested, runtime: status, api_base: env.CC_API_BASE, saas: env.CC_SAAS_HEADER, }, null, 2, ), ); if (requested === "live") { if (status.mode === "mock") { console.error( "FAIL: --mode=live �?CC_MOCK=true。设 CC_MOCK=false 并配�?CC_USERNAME + 密码后再跑�?, ); process.exit(3); } if (!status.live_ready) { console.error( "FAIL: live 未就�?�?, status.reason ?? "缺少 CC_USERNAME / CC_PASSWORD_PLAIN|MD5", ); process.exit(1); } } if (requested === "mock" && status.mode !== "mock") { console.error( "FAIL: --mode=mock 但当�?CC_MOCK=false。请临时�?CC_MOCK=true,或去掉 --mode�?, ); process.exit(3); } if (requested === "auto" && status.mode === "live" && !status.live_ready) { console.error("FAIL:", status.reason); process.exit(1); } const isLive = status.mode === "live" && status.live_ready; const containerNo = `SMK${Date.now().toString().slice(-8)}`; if (isLive) { const lines = await getShippingLineList(); console.log(`shipping_lines: ${lines.length}`); if (lines.length === 0) { console.warn("WARN: GetShippingLineList 空(不阻�?SaveContainer�?); } const list = await getContainerListFromCc(containerNo); if (!list.ok) { console.error("FAIL: GetContainerList", list.error); process.exit(2); } console.log(`conflict_probe items=${list.items.length}`); } else { console.log("mode=mock �?跳过真网 GetShippingLineList / GetContainerList"); } const res = await saveContainer({ entity: { F_TransMode: 0, F_OperationType: 0, F_ContainerNo: containerNo, F_CabinetType: "40HQ", F_ETA: "2026-07-13", F_Classis: 0, F_MemoRemark: `smoke-${status.mode}`, }, shipments: [ { row_index: 1, row_status: "VALID", F_FBACode: "ABQ2", F_Transporter: "TRUCK", F_CTNS: 1, F_FBAID: "FBA19G5XJLV4", }, ], }); console.log(JSON.stringify({ containerNo, result: res }, null, 2)); if (!res.ok) { process.exit(2); } // 指令写能力烟雾(默认 mock�? const { executeInstructionWrite } = await import( "../src/services/cc/instruction-write" ); const { ensureCcWriteCapabilities, listCcWriteCapabilities } = await import( "../src/services/cc/write-capability" ); await ensureCcWriteCapabilities(); const caps = await listCcWriteCapabilities(); console.log( "cc_write_capabilities", caps.map((c) => `${c.id}=${c.mode}`).join(", "), ); for (const kind of ["transfer", "hold_split", "label"] as const) { const r = await executeInstructionWrite(kind, { mailId: "0", containerNo: containerNo, subject: `smoke ${kind}`, shipments: [], remark: "cc-smoke", }); console.log(`instruction ${kind}:`, r.ok ? "PASS" : "FAIL", r.mode, r.error || ""); if (!r.ok) process.exit(2); } if (isLive) { console.log("PASS: TEST live SaveContainer code=200 + external id"); console.log( "NOTE: transfer/hold/label live 需 cc_write_capability.mode=live �?CC 侧接口可用才算该项完�?, ); } else { console.log( "PASS: mock SaveContainer + instruction writes �?非设�?TEST 烟雾验收。账号到位后:CC_MOCK=false + pnpm cc:smoke -- --mode=live", ); } } main().catch((e) => { console.error(e); process.exit(1); });