|
|
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(/开箱拍照/);
|
|
|
});
|
|
|
});
|