import { describe, expect, it } from "vitest"; import { classifyMail } from "@/services/parse/classify"; import { extractOpsSubjectFromMail, parseOpsSubjectTemplate, } from "@/services/parse/ops-subject-template"; import { buildMailRecord } from "@/services/parse/work-order-record"; /** 邮件3 真实主题(3邮箱.pdf) */ const MAIL3_SUBJECT = "Fw: 转发:派送要求更新: 拆柜清单更新: DO请查收+拆柜清单更新: WHSU5574991+WHL063G550810+船名航次:OOCL SINGAPORE / 065W+ETA:6/28+FedEx-29件+UPS-102件+亚马逊卡派-289件+私人地址-166件+拦截-209件+拆柜清单"; const PAYLOAD = "WHSU5574991+WHL063G550810+船名航次:OOCL SINGAPORE / 065W+ETA:6/28+FedEx-29件+UPS-102件+亚马逊卡派-289件+私人地址-166件+拦截-209件+拆柜清单"; describe("ops-subject-template mail3", () => { it("parses subject payload fields", () => { const r = parseOpsSubjectTemplate(PAYLOAD, { defaultYear: 2026 }); expect(r).not.toBeNull(); expect(r!.modules.container_no).toBe("WHSU5574991"); expect(r!.modules.bl_no).toBe("WHL063G550810"); expect(r!.modules.shipping_line_hint).toBe("WHL"); expect(r!.modules.vessel_voyage).toMatch(/OOCL SINGAPORE/); expect(r!.modules.vessel_voyage).toMatch(/065W/); expect(r!.modules.eta).toBe("2026-06-28"); expect(r!.channels).toEqual( expect.arrayContaining([ { channel: "FedEx", ctns: 29 }, { channel: "UPS", ctns: 102 }, { channel: "亚马逊卡派", ctns: 289 }, { channel: "私人地址", ctns: 166 }, { channel: "拦截", ctns: 209 }, ]), ); expect(r!.modules.ops_hints).toEqual( expect.arrayContaining(["拆柜清单"]), ); expect(r!.modules.ops_hints).not.toContain("拦截"); }); it("extracts from full forwarded subject", () => { const r = extractOpsSubjectFromMail(MAIL3_SUBJECT, { defaultYear: 2026 }); expect(r).not.toBeNull(); expect(r!.modules.container_no).toBe("WHSU5574991"); expect(r!.modules.bl_no).toBe("WHL063G550810"); expect(r!.modules.eta).toBe("2026-06-28"); }); it("拆柜清单 ops_hints → mapBlModulesToHeader OperationType 0", async () => { const { mapBlModulesToHeader } = await import( "@/services/parse/bl-plus-template" ); const r = extractOpsSubjectFromMail(MAIL3_SUBJECT, { defaultYear: 2026 })!; const header = mapBlModulesToHeader( { F_ContainerNo: r.modules.container_no || "", container_no_valid: true, }, r.modules, ); expect(r.modules.ops_hints).toEqual( expect.arrayContaining(["拆柜清单"]), ); expect(header.F_OperationType).toBe(0); }); it("classifies as WORK_ORDER and builds subject-only mail_record", () => { const evidence = classifyMail({ subject: MAIL3_SUBJECT, body: "", filenames: [], }); expect(evidence.mail_type).toBe("WORK_ORDER"); const ops = extractOpsSubjectFromMail(MAIL3_SUBJECT, { defaultYear: 2026, })!; const record = buildMailRecord({ mailType: "WORK_ORDER", subject: MAIL3_SUBJECT, body: "这票需要开箱拍照……不应进入记录", filenames: ["WL103276-SBD1(1).pdf"], opsSubject: { modules: ops.modules, channels: ops.channels, }, }); expect(record.kind).toBe("WORK_ORDER"); expect(record.lineage.subject_only).toBe(true); expect(record.modules.container_no).toBe("WHSU5574991"); expect(record.modules.bl_no).toBe("WHL063G550810"); expect(record.modules.eta).toBe("2026-06-28"); expect(record.table.rows.length).toBe(5); expect(record.table.rows.every((r) => r.动作 === "拆柜清单")).toBe(true); expect(record.table.rows.some((r) => String(r.备注).includes("FedEx"))).toBe( true, ); expect(record.work_order_actions || []).not.toContain("拦截"); expect(record.summary).toMatch(/拆柜清单/); expect(record.summary).not.toMatch(/主题):拦截/); // 正文/附件未混入 expect(record.summary).not.toMatch(/开箱拍照/); }); });