You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
mail-yubao/docx/需求规格-邮件自动预报-v0.2.md

31 KiB

邮件自动预报系统 — 需求规格 v0.2

状态:待评审确认(2026-07-23 现行口径回写;2026-08-03 指令拆分规则见独立产品规则页)
相对 v0.1:补齐状态机/已读策略、导入柜·行语义、金样例、冲突检测、UI 主路径、测试 DoD、运营 SOP
相对审查补强:新增 §14 异常场景与系统韧性设计(全文亦独立于 docx/异常场景与系统韧性设计.md)
相对实现回写:邮件来源仅 IMAP(无开发种子进库);不做操作审计页/audit_log;IMAP 为近 N 天已读+未读(默认 3 天)+ 幂等;轮询默认 30min(设置页优先);路由含 /settings、/logs(导入+拉取);详见 docx/现行口径-修订说明-v1.md
指令识别/拆分锁定口径:docx/产品规则-邮件指令识别与拆分.md(与 §4 类型打分并用;冲突以产品规则页 + instruction-lexicon.ts 为准)
依据:v0.1、四方审查结论、推荐开发方案、docx/邮件、ccnew TEST、接口 PDF V1.72


0. 已确认决策

# 决策 结论
1 验收主路径 主题规则识别「新增预报」+ 清单映射以「邮件2 卡派资料.xlsx」为第一模板
2 TransMode / OperationType 解析给推荐默认;确认页必选可改
3 CarrierCentral TEST https://test.saas.carriercentral.vip/api + Header Saas: TEST;SaveContainer
4 邮件线程 自动拆单:按 In-Reply-To/References 拆成独立 Message;解析/执行只看当前条,不把历史主题动作合并进当前
5 导入触发 NEW_CONTAINER 必须人工确认;TRANSFER / 留仓拆分 / 贴标解析成功后 自动调 CC 写接口(mock/live 门禁)
6 可导入类型 NEW_CONTAINER 人工确认;指令类自动执行(须 CC 能力 live,mock 不可作 TEST 验收)
10 IMAP 接入 多邮箱 + IDLE(失败回退轮询)+ OAuth 与授权码并存;可配间隔(默认 30min) + 黑/白名单与关键词过滤 + 拉取日志(≤1000)
11 OCR PDF/图片 OCR 作为权威正文辅助(参与分类/预筛);置信度低于阈值不覆盖人工可改内容
7 一期验收口径(消解矛盾) 正式口径:绑定真实 IMAP → 拉近 N 天邮件 → 列表/详情/确认导入闭环;Admin 改类型保留为运维能力。上线门禁:至少 1 封「新增预报+卡派清单」真邮件可进 PENDING_CONFIRM(金样例 JSON 仅对照,不种子进业务库)(见 §3.2)
8 已读策略 先落库再标已读;解析失败仍标已读但可「重新解析」(见 §6.2)
9 导入粒度 一柜一请求;勾选过滤货件行后组包;多柜才柜级部分成功(见 §5.7)
12 操作审计 一期不做 Admin 审计查询与 audit_log 落库;以应用日志 / 拉取日志 / 导入日志为准
13 指令拆分 见 docx/产品规则-邮件指令识别与拆分.md:主题+正文+附件分源;仅最新指令可确认;软词→客户指令;解析无 LLM

1. 背景与目标

客户不愿走客户端手工预报,改为向客服邮箱发邮件。系统:拉信 → 解析 → 结构化入库 → 人工确认 → SaveContainer 完成新增柜预报。

一期目标: Next.js + MySQL + docker-compose;邮件列表/详情/确认导入;对接 TEST SaveContainer。

一期量化成功标准:

指标 目标
M2 卡派资料字段映射 核心列(柜号/仓库ID/渠道/件数)映射正确率 100%(金样例对照)
人工确认路径 从打开详情到提交确认 ≤ 3 分钟(327 行场景含筛选勾选)
TEST 导入 烟雾 1 柜成功 code=200 且返回柜 id;勾选子集导入货件数与请求一致
类型规则 M1–M4 金样例 mail_type 命中率 4/4

与现有客户端关系: 本系统是预报入口之一;写入同一 CarrierCentral 账套。一期不做双向同步;冲突以 CC 已有柜为准阻断(§5.8)。


2. 范围

2.1 In Scope

  • 多邮箱 IMAP:授权码与 OAuth(Gmail/Microsoft XOAUTH2)并存;QQ 等无 OAuth 的主机仅授权码
  • IMAP IDLE(账号可关);失败/不支持时回退轮询(POLL_INTERVAL_MS)
  • 邮件快照、附件落盘、幂等键(§8.2);业务预筛后入库
  • 线程历史自动拆单(In-Reply-To / References → thread_id)
  • PDF/图片 OCR 权威解析(local/aliyun;置信度门限)
  • 类型识别(4 类 + UNKNOWN)+ 金样例
  • 「卡派资料」xlsx/csv + 贴标指令单解析 → 柜头 + 货件行
  • 前端:列表、详情、确认导入、导入日志/补偿、拉取记录;Admin 改类型;设置页(多邮箱/OAuth/CC/拉取间隔与过滤)
  • CC:customerLogin + GetContainerList + GetShippingLineList + SaveContainer
  • CC 写:转仓 / 留仓拆分 / 贴标自动执行(无接口时 mock+门禁;live 真接通才算完成)
  • docker-compose:web + worker + mysql
  • 不做:开发种子邮件入库、操作审计页(见决策 #7/#12)

2.2 Out of Scope

  • AI 客服
  • SMTP 自动回信客户(失败亦不自动通知客户)
  • 与 CC 双向全量同步(冲突仍以 CC 已有柜阻断)
  • 变更须书面确认后方可再扩范围

3. 样例邮件与验收口径

3.1 现网样例(原样展示)

路径:docx/邮件/(QQ 截图 PDF + 附件,无 .eml)。

ID 目录 期望 mail_type 一期动作
M1 邮件1/ UNKNOWN 展示
M2 邮件2/ + 卡派资料.xlsx WORK_ORDER(转仓关键词覆盖;历史 TRANSFER) 标准记录表;不导入
M3 邮件3/ INSTRUCTION_HOLD_SPLIT(贴标信号为证据,主类型按最高分;同分见 §4) 展示
M4 邮件4/ 当前 Message WORK_ORDER(贴标/拍照覆盖;历史 INSTRUCTION_LABEL) 标准记录;不拆根主题 NEW
M_BL_TEMPLATE 邮件/模板/ NEW_CONTAINER + mail_record.kind=BL_FORECAST 模块化柜头 + 数据模版表

3.2 验收口径(推荐方案,已采纳)

阶段 口径
开发/一期验收 ① 设置页绑定 IMAP 并连通测试;② 拉取近 N 天邮件可见列表;③ 命中「新增预报+清单」或 Admin 改类型后走确认导入(CC mock 可演示;TEST 须 live)
上线门禁 至少 1 封真邮件「主题含新增预报 + 卡派/装箱清单」→ NEW_CONTAINER/PENDING_CONFIRM;金样例 docx/金样例/*.json 仅作期望对照,禁止再依赖种子邮件验收

4. 邮件类型判定

4.1 枚举

NEW_CONTAINER | TRANSFER | INSTRUCTION_HOLD_SPLIT | INSTRUCTION_LABEL | WORK_ORDER | UNKNOWN

4.2 规则(加权 + 词边界)

信号源:主题 + 正文纯文本 + 附件名。
取最高分;同分优先级: INSTRUCTION_LABEL = INSTRUCTION_HOLD_SPLIT > TRANSFER > NEW_CONTAINER > UNKNOWN(指令内部:同时命中贴标与拆分时,贴标分 ≥ 拆分则 LABEL,否则 HOLD_SPLIT)。

信号 匹配方式 分 类型
新增预报 主题优先;词完整匹配 +50 NEW_CONTAINER
新增转仓 / 转仓 词边界;排除「不转仓」 +50 TRANSFER(可被工单覆盖)
换标 / 覆盖贴 / 贴标 / 贴好拍照 正文或附件名 +40 INSTRUCTION_LABEL(可被工单覆盖)
拆柜清单 / 卡转海 / 拦截 / 改自提 / 留仓 同上 +40 INSTRUCTION_HOLD_SPLIT(可被工单覆盖)
附件名含换标、贴标指令 文件名 +30 INSTRUCTION_LABEL
附件名含卡派资料 文件名 +15 只加分到当前领先类型,不单独定类型
ISO 柜号 ^[A-Z]{4}\d{7}$ +5 辅证到领先类型
ETA / 船名航次 / 柜型 主题或正文 +5 辅证到领先类型

最低可判定分:40,否则 UNKNOWN。
只依据当前 Message。证据写入 type_evidence(各信号命中列表 + 总分)。

4.3 工单覆盖(现行优先)

正文/主题/附件名命中任一:贴标 / 拦截 / 拍照 / 转仓 / 快递单号(「不转仓」除外)→ 最终 mail_type=WORK_ORDER,覆盖上表得分结果。
本期:工单只做标准记录落库与详情展示,不自动写 CC、不进确认导入。转仓一律先记工单,到仓分流自动转仓后置。

4.4 混乱正文柜号启发式

正文较乱时:优先扫正文前两行,取首个 ^[A-Z]{4}\d{7}(ISO 柜号)写入 mail_record.modules.container_no。

4.5 提单「+」标准模板 → mail_record

样例(docx/邮件/模板):主题/正文含
客户+提单号+柜号+港口+柜型+EDT… ETA…船名航次…+服务描述。
解析为 kind=BL_FORECAST 模块化柜头;货件行对齐 数据模版.xlsx 列写入 mail_record.table。详情按模块 + 表格标准化展示。

4.6 标准记录(本期交付重心)

解析产出 parse_result.mail_record(kind / summary / modules / table / work_order_actions)。
本期不做导入闭环变更;NEW_CONTAINER 仍可进 PENDING_CONFIRM,工单类一律 PARSED。


5. CarrierCentral 对接

5.1 环境

环境 API Base Header
一期默认 TEST https://test.saas.carriercentral.vip/api Saas: TEST
Demo 备选 https://api.saas.carriercentral.vip/api-demo 按租户
本机 ccnew http://host.docker.internal:31173 Saas: TEST

以 ccnew TEST.json 为准,不用 PDF 的 demovip。

5.2 请求信封

{
  "token": "<customerLogin>",
  "loginMark": "<进程级固定 UUID v4>",
  "data": "<多数接口为 JSON 字符串>"
}

Header:Saas: <CC_SAAS_HEADER>。

5.3 登录

  • POST /learun/adms/user/customerLogin
  • password = MD5(明文) 32 位小写
  • token 缓存于 web/worker 进程内存 + DB 表 cc_token_cache(多实例以 DB 为准);410 → 清缓存重登 → 重放原请求 1 次
  • 谁调用 SaveContainer:仅 Web API(用户点击确认);worker 不做导入

5.4 接口选择

接口 一期
/Container/Import 不用(ccnew Insert 可能被注释)
/Container/SaveContainer 采用
/Container/GetContainerList 导入前冲突检测
/ConfigModule/GetShippingLineList 船司匹配(可 M5)

5.5 字段映射

柜头 entity:

字段 规则
F_TransMode 确认页必选:0/1/3;默认推荐 0
F_OperationType 确认页必选:0/2/4;默认推荐 0;主题含「直送」推荐 2;「提拆派」仍推荐 0(开放项可改)
F_ContainerNo 必填;ISO 6346(可配置关闭)
F_CabinetType 可解析可改
F_BLCopyCode / F_ETD / F_ETA / F_LoadPort / F_Dock 有则填
F_ShippingLineId 模糊匹配;失败留空人工选
F_Classis 不带托架→0;带车架→1;默认 0
F_MemoRemark / F_Instruction 正文摘要
keyValue 新建恒 null

货件 ← 卡派资料:

列 字段 必填
仓库ID F_FBACode 是
渠道 F_Transporter(卡派→TRUCK;UPS/FEDEX/DHL/USPS/自提/留仓/扣货) 是
件数 F_CTNS 是
总体积/毛重 F_CBM / F_Weight 否
FBA ID F_FBAID 否
Amazon reference ID F_ReferenceId 否
分货标识/箱唛 F_ShipmentID 否
派送地址 F_Address 否
最早/最晚送仓 F_Expected_DeliveryDateB/E 否
备注 F_Remark 否

有效行:F_FBACode + F_Transporter + F_CTNS 齐全。无效行 row_status=INVALID,默认不勾选。

5.6 渠道映射表(黄金)

原文(含) F_Transporter
卡派 / TRUCK / truck TRUCK
UPS UPS
FEDEX / FedEx FEDEX
DHL DHL
USPS USPS
自提 自提
留仓 留仓
扣货 / 拦截 扣货
其他 原样上限 30 字符;标 CHANNEL_UNMAPPED 警告

5.7 导入语义(柜 / 行)— 推荐方案已采纳

一封邮件解析结果通常 = 1 个柜号 + N 条货件行
确认页:勾选货件行(过滤 INVALID)
提交:按柜号分组
  → 每个柜号 1 次 SaveContainer
  → entity = 柜头;sR_Shipments = 该柜勾选行
多柜(少见):柜 A 成功、柜 B 失败 → PARTIAL_SUCCESS;B 入补偿队列
单柜:要么 SUCCESS 要么 FAILED(行已在提交前过滤,不再「半柜成功」)

禁止: 同一柜拆多次 SaveContainer 做「行级部分成功」(CC 侧是整柜货件列表)。

重试:补偿队列按柜重试;已成功柜 external_container_id 非空则跳过。

5.8 冲突检测

导入前(确认提交时):

  1. GetContainerList,queryJson.F_ContainerNo = 柜号,近 30 天(StartTime/EndTime)
  2. 若存在非归档/有效记录 → 本柜 CONFLICT,不调用 SaveContainer,状态保持可编辑
  3. 本库 container_import 同柜 SUCCESS 且同 Message-ID → 幂等跳过
  4. 本库他邮件已 SUCCESS 同柜 → 阻断并提示原邮件

5.9 SaveContainer 黄金请求样例

{
  "loginMark": "11111111-2222-3333-4444-555555555555",
  "token": "<from customerLogin>",
  "data": "{\"keyValue\":null,\"entity\":{\"F_TransMode\":0,\"F_OperationType\":0,\"F_ContainerNo\":\"MATU2745683\",\"F_CabinetType\":\"40HQ\",\"F_BLCopyCode\":\"\",\"F_ETD\":null,\"F_ETA\":\"2026-07-13\",\"F_LoadPort\":\"\",\"F_Dock\":\"\",\"F_ShippingLineId\":\"\",\"F_Classis\":0,\"F_MemoRemark\":\"邮件预报导入\",\"F_Instruction\":\"\"},\"sR_Shipments\":[{\"F_FBACode\":\"ABQ2\",\"F_Transporter\":\"TRUCK\",\"F_ShipmentID\":\"BAZUS001799251\",\"F_Remark\":\"转POC2\",\"F_FBAID\":\"FBA19G5XJLV4\",\"F_ReferenceId\":\"3NHUDF7E\",\"F_Address\":\"\",\"F_CTNS\":1,\"F_CBM\":0.09,\"F_Weight\":10.69},{\"F_FBACode\":\"FTW1\",\"F_Transporter\":\"TRUCK\",\"F_ShipmentID\":\"\",\"F_FBAID\":\"FBA19GLHTHQ2\",\"F_ReferenceId\":\"5ELVT8ED\",\"F_CTNS\":4,\"F_CBM\":0.21,\"F_Weight\":85.08}]}"
}

成功:code=200,data = 集装箱 id 字符串。
失败:400/500 记入 container_import.last_error;410 触发重登重放一次。

5.10 错误码 → 文案

code 文案
200 导入成功
400 业务校验失败:{info}
410 登录失效,已自动重试;仍失败请检查 CC 账号
500 CC 异常:{info}
网络/超时 连接 CC 超时,已入补偿队列

6. 邮件接入与状态机

6.1 IMAP

项 值
服务器 imap.qq.com:993 SSL(多主机可配)
鉴权 授权码;Gmail/MS 可 OAuth
轮询 默认 30min(设置页 imap_settings 优先;.env POLL_INTERVAL_MS 回退);可 IDLE
拉取 近 N 天已读+未读(IMAP_LOOKBACK_DAYS 默认 3);按 UID 去重后再 FETCH;单连接 + GET_LOCK
幂等 见 §8.2
SMTP 不做

6.2 已读策略(推荐已采纳)

SEARCH SINCE (今天起往前 N-1 天)   ← 含已读+未读
  → 过滤 DB 已有 (mailbox, folder, uid) / message_id / raw_hash
  → FETCH 新 UID → 落库 FETCHED(raw.eml + 附件)
  → 立即 STORE \Seen          ← 避免重复消费;以 DB 状态为准
  → 同进程 PARSING
  → 成功 PARSED / PENDING_CONFIRM /(指令类可 AUTO_EXECUTING)
  → 失败 PARSE_FAILED(可点「重新解析」,不依赖邮箱未读)

若落库前进程崩溃:邮件可能仍未入库,下轮 lookback 仍会命中;靠幂等键去重。 (历史口径「仅 UNSEEN」已废止。)

6.3 状态机

状态 含义 可执行操作
FETCHED 已落库 系统自动解析
PARSING 解析中 无
PARSED 已解析;类型非 NEW 或未达确认条件 查看;Admin 改类型
PENDING_CONFIRM 类型=NEW 且 ≥1 有效货件 编辑字段;确认导入
IMPORTING 调用 CC 中 禁止重复提交
SUCCESS 全部柜成功 查看日志
PARTIAL_SUCCESS 多柜部分成功 对失败柜重试
FAILED 单柜失败或全部失败 重试;改数据再确认
PARSE_FAILED 解析失败 重新解析
REJECTED_VALIDATION 本地校验拒绝(无柜号等) 编辑/补附件后重解析
IGNORED 人工忽略 仅 Admin 从详情触发

迁移规则:

从 条件 到
FETCHED 开始解析 PARSING
PARSING 成功且 type=NEW 且有效行≥1 PENDING_CONFIRM
PARSING 成功且其他类型 PARSED
PARSING 异常/无模板 PARSE_FAILED
PARSING 无柜号等核心缺失 REJECTED_VALIDATION
PENDING_CONFIRM 用户确认 IMPORTING
IMPORTING 全成功 SUCCESS
IMPORTING 多柜部分成功 PARTIAL_SUCCESS
IMPORTING 失败 FAILED
PARSE_FAILED 重新解析 PARSING
PARSED Admin 改类型为 NEW 且有效行≥1 PENDING_CONFIRM
* Admin 忽略 IGNORED

7. 前端(含交互细则)

7.1 信息架构 / 主路径

登录 → 邮件列表 → 邮件详情(摘要/附件/证据/解析表)
                      ├─ 类型≠NEW:仅查看(按钮禁用+原因)
                      └─ 类型=NEW 或 Admin 改类型后 → 确认导入页
                            → 提交 → 结果 Toast → 导入日志

详情与确认:两步(详情只读+轻编辑柜头;确认页负责 TransMode/OperationType/行勾选/提交)。

7.2 页面

  1. 邮件列表 — 主题、发件人、时间、类型色标、状态角标;默认按时间倒序;筛类型/状态
  2. 邮件详情 — 正文摘要、附件下载、type_evidence、货件表预览(虚拟列表)
  3. 确认导入 — TransMode/OperationType 必选;柜头可编辑;货件表:
    • 默认勾选所有 VALID 行;INVALID 灰显不可选
    • 顶栏:全选有效 / 反选;按 FBACode/ShipmentID 搜索过滤
    • 327 行:虚拟滚动;底部显示「已选 n / 有效 m」
    • 二次确认对话框:「将向 CC 导入柜 {no},货件 {n} 行」
    • 提交后按钮 loading,状态 IMPORTING 防重复
  4. 导入日志/补偿 — 按柜成功失败、错误文案、重试按钮

空态:无邮件「等待 IMAP 拉取」;错态见 §5.10。

7.3 Admin「改类型」

  • 环境变量 ENABLE_TYPE_OVERRIDE=true 或角色=admin 时显示
  • 正式运营账号默认隐藏
  • Admin 改类型:详情 Modal;ENABLE_TYPE_OVERRIDE(前端构建侧须同步暴露开关,见 UI 方案);一期不写 audit_log

7.4 线框要点(一期低保真即可)

  • 列表:左类型色点,右状态 pill
  • 确认页:上柜头表单两列;下表格 + 固定底栏「确认导入」
  • 不强制品牌规范;清晰密度优先(客服桌面端为主,手机不一期优化)

8. 数据模型

8.1 表(核心字段)

mail_message

列 类型 说明
id BIGINT PK
message_id VARCHAR(998) UNIQUE NULL RFC Message-ID
imap_uid BIGINT 与 folder 组合唯一
folder VARCHAR(64) 默认 INBOX
subject, from_addr, received_at
mail_type VARCHAR(32)
status VARCHAR(32)
type_evidence JSON
snapshot_path VARCHAR(512)
raw_hash CHAR(64) 正文+关键指纹
created_at / updated_at

唯一: UNIQUE(message_id)(NULL 不冲突时用下条);UNIQUE(folder, imap_uid);UNIQUE(raw_hash) 兜底。

mail_attachment — mail_id, filename, sha256, path, size, template_id

parse_result — mail_id UNIQUE, container_header JSON, shipments JSON, lineage JSON

container_import — mail_id, container_no, external_id, request_body, response_body, status, last_error

import_compensation — import_id, retry_count, next_retry_at, max 3

cc_token_cache — login_mark, token, expire_at

audit_log — actor, action, payload JSON, created_at

sender_customer_map — 可空,一期不阻断

8.2 幂等键(无 Message-ID 时)

优先级:Message-ID → folder+imap_uid → sha256(Date+From+Subject+附件名列表)。

8.3 附件处理

  • xlsx/csv 直接解析;xls 转读;zip 解压一层取其中 xlsx/csv(邮件3)
  • 多 sheet:优先名含「卡派」否则第一 sheet
  • 表头别名失败 → PARSE_FAILED + 缺失列列表
  • 单附件 ≤20MB;超限拒收记 REJECTED_VALIDATION

8.4 存储

./data volume;保留 30 天(可配 DATA_RETENTION_DAYS)。


9. 技术架构

Next.js Web ──确认导入──▶ CC Adapter ──▶ test.saas.../SaveContainer
     │                        ▲
     ▼                        │ token
   MySQL 8 ◀──解析入库── worker (IMAP only)
  • Worker:只负责 IMAP + 解析;不调 SaveContainer
  • Web:确认导入、补偿重试、Admin
  • compose:web / worker / mysql
  • 多 worker:用 MySQL GET_LOCK('imap_poll') 互斥,保证单连接语义

9.1 环境变量(统一命名)

DATABASE_URL=mysql://app:***@mysql:3306/email_forecast
POLL_INTERVAL_MS=1800000
IMAP_LOOKBACK_DAYS=3
DATA_RETENTION_DAYS=30
ENABLE_TYPE_OVERRIDE=true

IMAP_HOST=imap.qq.com
IMAP_PORT=993
IMAP_USER=
IMAP_PASS=

CC_API_BASE=https://test.saas.carriercentral.vip/api
CC_SAAS_HEADER=TEST
CC_LOGIN_MARK=
CC_USERNAME=
CC_PASSWORD_PLAIN=    # 服务端 MD5;或直接 CC_PASSWORD_MD5=

APP_ADMIN_USER=
APP_ADMIN_PASS=

10. 开发路线与 DoD

阶段 交付 DoD(可测)
M0 脚手架 compose docker compose up 打开登录页
M1 IMAP 绑定 + 列表详情 绑定邮箱后可拉取;列表/详情可用
M2 卡派解析 + 规则 M2 shipments 行数=327;核心列金样例通过
M3 CC 客户端 mock/TEST 烟雾 1 柜 code=200
M4 确认导入 勾选 2 行 → 请求仅 2 货件;冲突柜阻断;失败可重试
M5 船司匹配 + /logs + README 演示全流程 + 运维文档(无审计页)

原则:独立 Next 栈;只复用 CC HTTP 契约;样例驱动。


11. 测试策略

层 内容
单元 类型规则(含「不转仓」负例)、渠道映射、ISO 柜号、状态迁移
契约 SaveContainer 请求 snapshot 与黄金样例 diff;410 重登重放
组件/集成 xlsx 解析 327 行;zip 解压
E2E IMAP 拉取 →(可选 Admin 改类型)→ 确认导入(CC mock)
手工 真实 IMAP(可选);TEST 烟雾

金样例文件(实现时落地): docx/金样例/M1.json … M4.json(期望 type + header 摘要 + shipments 前 2 行)。本期文档附录见 §14。


12. 运营 SOP(一期)

场景 动作 责任
邮件未进系统 查 worker 日志 / IMAP 授权码;看是否已读但 PARSE_FAILED 研发值班
解析失败 详情点「重新解析」;仍失败则人工走客户端预报 运营
类型不对但要导入 Admin 改类型(需授权)后确认 运营主管
导入失败 日志看文案;改字段重试或补偿重试 ≤3 运营
与客户端重复预报 冲突阻断后,以 CC 已有单为准,忽略邮件或改柜号 运营
客户追问结果 一期无回信 → 运营自行邮件/IM 回复 运营

失败不自动通知客户(Out of Scope)。


13. 非功能

  • 日邮件 <100;附件 ≤20MB;IMAP 单连接(锁互斥)
  • 导入串行;确认页防重复提交
  • 拉取/导入日志保留:随 DATA_RETENTION_DAYS(默认 30)
  • 准确率:金样例 4/4;不承诺 99% 直至标注集扩大

14. 异常场景与系统韧性设计

实现与 QA 以全文为准: docx/异常场景与系统韧性设计.md(含每条:异常描述 / 系统行为 / 降级 / 告警 / 用户结果)。
下文为强制覆盖维度与关键决策摘要,与 §5–§8、§12 SOP 对齐。

14.0 韧性常量

常量 值
IMPORT_LOCK_TTL 120s
IMPORTING_TIMEOUT 180s
PARSING_STALE 10min
COMPENSATION_MAX_RETRY 3
IMAP_CONNECT_TIMEOUT / READ 30s / 60s
CC_HTTP_TIMEOUT 60s
CC_REPLAY_ON_410 1
SHIPMENT_ROW_SOFT_LIMIT / HARD 1000 / 5000

14.1 输入层异常

场景 落点状态/字段 系统行为
无主题 subject="(无主题)" 继续 PARSING
无正文且无附件 REJECTED_VALIDATION / NO_BODY_NO_ATTACHMENT 阻断导入
无支持表格(非 xlsx/csv,zip 无有效表) PARSE_FAILED / NO_SUPPORTED_SPREADSHEET 可「重新解析」
缺仓库ID/渠道/件数列 PARSE_FAILED + missing_columns 阻断
柜号非 ISO 6346 REJECTED_VALIDATION;container_no_valid=false 可手改再校验;不自动猜号
行半空(CTNS≤0 / 仓空) row_status=INVALID 跳过行,不挡其他 VALID
附件 >20MB REJECTED_VALIDATION / ATTACHMENT_TOO_LARGE 不落全量附件;仍 \Seen
渠道未映射 CHANNEL_UNMAPPED 可 VALID;提交需 ack_unmapped_channels=true
体积/重量非数字 字段 null 行仍可 VALID
日期/地址错误 日期 null;地址截断 500 确认页可改;时区 Asia/Shanghai

14.2 用户行为与并发

  • 重复/并发确认:前端 disabled + Idempotency-Key;DB UPDATE status PENDING_CONFIRM→IMPORTING;失败者 409。
  • 刷新:以 DB status 为准;不自动重放 SaveContainer。
  • 回退/前进:非 PENDING_CONFIRM 禁止提交并重定向详情。
  • 多标签:version 乐观锁 → 409 VERSION_CONFLICT。
  • Admin 改类型:二次确认 + audit_log;SUCCESS 后禁止改;PARSING 中改类型 → 409 MAIL_BUSY。

14.3 网络与通信

  • IMAP 超时/断开:释放 GET_LOCK('imap_poll'),下轮重试;连续失败 ≥3 → 顶栏红条 + imap.poll_fail。
  • CC 超时或响应丢失:禁止盲重放 SaveContainer;TIMEOUT_UNKNOWN 入补偿;用 GetContainerList 核对后收敛 SUCCESS 或允许重试。
  • 弱网重复 POST:靠幂等键 + 条件更新吞掉。
  • 静态资源失败:Error boundary;不影响 worker。

14.4 CarrierCentral API

  • customerLogin 5xx/失败:清 cc_token_cache+内存;重试登录 ≤2;仍失败整单 FAILED。
  • SaveContainer 中途 410:重登并重放 1 次(CC_REPLAY_ON_410)。
  • SaveContainer 400:该柜 FAILED,不自动重试;多柜可 PARTIAL_SUCCESS。
  • GetContainerList 失败:默认阻断导入;ENABLE_FORCE_IMPORT=true 才可强跳(一期无 audit_log)。
  • 响应 schema 漂移:code=200 但无柜 id → 走超时不确定核对流程。
  • 浏览器永不持有 CC token。

14.5 解析与附件

  • MIME 损坏:快照保留 → PARSE_FAILED / MIME_PARSE_ERROR。
  • zip:只解 一层;嵌套 zip 忽略并 warn;损坏 → ZIP_EXTRACT_FAILED。
  • 无「卡派」sheet → 回退第一 sheet。
  • 行数 >5000 → REJECTED_VALIDATION;>1000 → 落库但默认不全选 + 警告。
  • 正文/附件柜号冲突 → 确认页强制人选,默认阻断提交。
  • 类型同分:严格执行 §4.2;type_evidence 落库。
  • 「重新解析」:禁止在 IMPORTING|SUCCESS|PARTIAL_SUCCESS。

14.6 状态机与生命周期

  • 落库成功但 \Seen 失败:幂等跳过新建,仅重试 Seen。
  • PARSING 超过 PARSING_STALE:自动回收(现行可置 PARSE_FAILED 后可再解析;目标语义回 FETCHED)。
  • IMPORTING 超过 IMPORTING_TIMEOUT:转失败/待核查 + 补偿,禁止永久 IMPORTING。
  • 补偿 retry_count≥3 → COMPENSATION_EXHAUSTED,停自动重试,人工重置。
  • 无通用 status PATCH;非法迁移拒绝。

14.7 缓存与幂等

  • Token:DB cc_token_cache 为权威;内存跟随;多实例 410 时抢 GET_LOCK('cc_login') 重登。
  • 幂等序:Message-ID → folder+imap_uid → raw_hash。
  • raw_hash 碰撞且 Message-ID 不同:分叉新建 + error 告警。
  • 同 Message-ID 重复拉取:唯一约束吞掉。

14.8 并发与锁

  • Worker:GET_LOCK('imap_poll');拿不到锁本轮退出(正常)。
  • 跨邮件同柜并发确认:活跃柜号占位唯一 + §5.8;后提交阻断。
  • 补偿调度:已 SUCCESS/IMPORTING 跳过;retry_token 幂等。

14.9 数据计算

  • UNMAPPED 渠道:无 ack_unmapped_channels 则提交 API 400。
  • CBM/Weight:DECIMAL(18,4),提交前 round 4 位。
  • 船司匹配失败:F_ShippingLineId 留空,不阻断。
  • 勾选数以服务端 accepted_count 为准,不一致 warn。

14.10 权限与安全

  • 未登录 API → 401。
  • 改类型:服务端校验 role=admin && ENABLE_TYPE_OVERRIDE → 否则 403。
  • 导入限流:10 次/用户/分钟 → 429。
  • 改类型 / 确认导入 / 强制跳过冲突 / 补偿重试 / 忽略 → 全写 audit_log。

14.11 监控与 QA

全文 §11 指标阈值、§12 用例索引 A-1.x…A-10.x(见独立文件)。

15. 附录:金样例期望(摘要)

M1

{
  "id": "M1",
  "subject_contains": "TIIU8073522-90022",
  "mail_type": "UNKNOWN",
  "shipments_expected": 0,
  "importable": false
}

M2

{
  "id": "M2",
  "subject_contains": "MATU2745683",
  "mail_type": "TRANSFER",
  "container_no": "MATU2745683",
  "shipments_expected": 327,
  "sample_rows": [
    { "F_FBACode": "ABQ2", "F_Transporter": "TRUCK", "F_CTNS": 1, "F_FBAID": "FBA19G5XJLV4" },
    { "F_FBACode": "FTW1", "F_Transporter": "TRUCK", "F_CTNS": 4, "F_FBAID": "FBA19GLHTHQ2" }
  ],
  "importable_default": false,
  "importable_after_admin_type_override_to_NEW": true
}

M3

{
  "id": "M3",
  "subject_contains": "WHSU5574991",
  "mail_type": "INSTRUCTION_HOLD_SPLIT",
  "notes": "卡转海/贴标为证据分;主类型按 §4 同分规则",
  "importable": false
}

M4

{
  "id": "M4",
  "subject_contains": "新增预报",
  "mail_type": "INSTRUCTION_LABEL",
  "notes": "当前 Message 为换标指令;不拆历史新增预报",
  "importable": false
}

16. 开放项

  • TEST 客户账号密码(谁提供)
  • 「提拆派组合柜」默认 OperationType 是否维持 0
  • 生产 API Base / Header
  • 上线门禁:补「新增预报+清单」真邮件
  • 运营登录:简单账号 vs SSO

17. 索引

资料 路径
本文 v0.2 docx/需求规格-邮件自动预报-v0.2.md
异常与韧性(全文) docx/异常场景与系统韧性设计.md
v0.1 docx/需求规格-邮件自动预报-v0.1.md
接口 PDF docx/接口/carriercentral客户端通用接口V1.72docx(4).pdf
样例 docx/邮件/
ccnew TEST X:\work\ccnew\...\App_Data\Saas\TEST.json
实施计划 .cursor/rules/implementation-plan.mdc