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.

21 KiB

邮箱项目(邮件自动预报)

给接手同事看的入口文档。细则以 docx/ 下 PRD / 技术设计 / 韧性为准;冲突时以 PRD 为准。
更新日期:2026-08-04(补充开发注意点 + 上市/生产安全清单)


1. 项目信息

项 说明
名称 邮件自动预报系统(repo: email-forecast)
目标 从绑定邮箱 IMAP(多箱/IDLE/OAuth
不做(一期) AI 客服、SMTP 自动回信客户、与 CC 双向全量同步、真实大模型识别(指令拆分为词表/规则)
栈 Next.js 15 App Router · Prisma 5 · MySQL 8 · Ant Design 5 · TypeScript · pnpm
进程 web(UI+API+ConfirmImport)· worker(IMAP+解析+reaper+补偿+retention)· mysql
本地入口 http://localhost:3100
默认账号(仅本地开发) admin / admin123(Admin);ops / ops123(运营)
MySQL 映射 宿主机 7023 → 容器 3306(避让本机 3306)
包管理 仅 pnpm(不要 npm/yarn)

⚠️ 生产环境 禁止 使用默认口令与默认 SESSION_SECRET;NODE_ENV=production 时弱口令会 拒绝启动。详见 §10 上市注意点。

1.1 路由

路径 说明
/login 登录
/mails 邮件列表
/mails/[id] 详情(重新解析 / Admin 改类型)
/mails/[id]/confirm 确认导入(预报类:有效货件 + 可确认状态;含部分 DO 路径)
/logs 导入日志;拉取记录
/settings Admin:自动拉取间隔/过滤、邮箱绑定、CC、OAuth

1.2 设计文档索引

文档 路径
需求规格(PRD) docx/需求规格-邮件自动预报-v0.2.md
技术设计 docx/技术设计方案-邮件自动预报-v1.0.md
UI/UX docx/产品UI-UX设计方案-邮件自动预报-v1.0.md
异常与韧性 docx/异常场景与系统韧性设计.md
运营 SOP docx/运营SOP-邮件自动预报.md
产品规则(指令拆分) docx/产品规则-邮件指令识别与拆分.md
指令提取规则(对照代码,含覆盖缺口) docs/邮件指令提取规则.md
CC 接口 V1.73 docx/接口/carriercentral客户端通用接口V1.73.md
实施计划(Cursor) .cursor/rules/implementation-plan.mdc
快速 README README.md

2. 常用命令

2.1 日常开发

pnpm install
cp .env.example .env   # 首次;DATABASE_URL 指向 localhost:7023;无 CC 账号时 CC_MOCK=true

docker compose up -d mysql
pnpm exec prisma db push
pnpm db:seed           # 仅账号

pnpm dev               # Web :3100 + Worker(IMAP 自动拉取)一起启
pnpm dev:web           # 仅 Web(不拉信)
pnpm worker            # 仅 Worker(IMAP / 解析 / 回收 / 补偿)

2.2 一键三件套

pnpm compose:up        # mysql + web + worker
pnpm compose:ps
pnpm compose:logs

Windows 一键启停(先杀旧进程/端口再启动,防地址占用):

# 默认 local:只起 mysql 容器 + 本机 pnpm dev/worker(不拉 node 镜像,避开 Docker Hub)
.\docs\start-system.ps1
.\docs\start-system.cmd

# 全量 compose(需能访问 registry-1.docker.io)
.\docs\start-system.ps1 -Mode compose
.\docs\start-system.ps1 -Mode compose -NoBuild

# compose 失败默认会自动 fallback 到 local;禁止回退加 -NoFallback

Docker Hub 超时(registry-1.docker.io / node:*-slim)时用默认 local 即可;Dockerfile 基底已改为本地常见的 node:20-bookworm。

2.3 数据库

pnpm db:generate
pnpm db:push
pnpm db:migrate:dev    # 开发迁移
pnpm db:migrate        # 部署 migrate deploy
pnpm db:seed

2.4 IMAP / CC / 运维

pnpm imap:smoke        # 连通性(.env 回退凭证)
pnpm imap:poll         # 拉一轮(优先设置页 mailbox_account)
pnpm exec tsx scripts/clear-imap-lock.ts   # 清残留 imap_poll 锁(lock_busy)

pnpm cc:smoke                    # 跟随当前配置(默认 mock ≠ TEST 验收)
pnpm cc:smoke -- --mode=mock
pnpm cc:smoke -- --mode=live     # 真烟雾:关 Mock + CC 账号

pnpm retention:cleanup
pnpm retention:cleanup -- --dry-run

2.5 测试与构建

pnpm test              # vitest(含安全路径 tests/unit/security-hardening.test.ts)
pnpm test:watch
pnpm test:e2e          # playwright(需先起服务)
pnpm lint
pnpm build

2.6 默认环境要点

DATABASE_URL=mysql://app:app@localhost:7023/email_forecast
CC_MOCK=true
POLL_INTERVAL_MS=1800000
IMAP_LOOKBACK_DAYS=3
ENABLE_TYPE_OVERRIDE=true
ENABLE_FORCE_IMPORT=false
SESSION_SECRET=change-me-to-a-long-random-string-at-least-32-chars   # 生产必须换强随机 ≥32

IMAP / CC 凭证:优先设置页 DB 配置,无则回退 .env。
拉取间隔:设置 → 自动拉取(imap_settings,默认 30min,3min~7d)优先于 POLL_INTERVAL_MS。


3. 技术方案(摘要)

3.1 职责拆分

imap.qq.com ──► worker(ImapPoller → Snapshot → ParsePipeline) ──► MySQL
                                                                    ▲
运营浏览器 ──► web(/api + ConfirmImport → CC SaveContainer) ────────┘
进程 允许 禁止
web UI、API、确认导入、调 CC、设置 长期 IMAP 轮询主循环(可 Admin 手动拉一轮)
worker IMAP 拉取、解析、stale reaper、补偿 due、retention、受限自动写(见能力表) 业务 SaveContainer(新增预报)必须在 web 人工确认后调用

3.2 邮件主路径

  1. Worker 按 可配间隔(默认 30min,设置页 3min~7d)轮询;Admin「立即拉取」可手动
  2. Lookback:SINCE IMAP_LOOKBACK_DAYS(默认 3 天,已读+未读)
  3. 过滤:黑名单拒绝 → 发件人白名单必拉 → 关键词命中才拉;三列表皆空则回退业务相关兜底
  4. 幂等:message_id / folder+uid / raw_hash;入库后标 \Seen;每封写 imap_pull_log(最多 1000)
  5. ParsePipeline:分类 → 解 xlsx/csv/zip → 柜头/货件
  6. 预报类有效货件 ≥1 → PENDING_CONFIRM → 确认页 → CC SaveContainer

3.3 状态机(邮件)

常用:FETCHED → PARSING → PARSED | PENDING_CONFIRM | PARSE_FAILED | REJECTED_VALIDATION
导入:PENDING_CONFIRM → IMPORTING → SUCCESS | PARTIAL_SUCCESS | FAILED
重新解析禁止:IMPORTING | SUCCESS | PARTIAL_SUCCESS

非法迁移抛 IllegalTransition;改状态必须走 assertTransition。

3.4 命名锁

  • imap_poll / cc_login:MySQL GET_LOCK
  • 必须经 src/services/db-lock.ts 独立连接 acquire/release(Prisma 连接池会导致锁泄漏 → 永久 lock_busy)
  • Worker 启动会清残留 imap_poll 锁

3.5 关键目录

src/app/                 # Next App Router(页面 + API)
src/components/          # UI
src/services/imap/       # 拉取 / lookback / snapshot
src/services/parse/      # 分类 / 卡派 / pipeline
src/services/import/     # 确认导入 / 补偿
src/services/cc/         # CC HTTP / auth / mock / 写能力
src/lib/                 # env / session / safe-path / rate-limit / cc-api-base
src/worker/              # 常驻 worker 入口
prisma/                  # schema + seed
docx/                    # 需求与设计(权威)
docs/                    # 协作入口文档(本文)

3.6 CC 写能力(易踩坑)

表 cc_write_capability(启动 ensureCcWriteCapabilities):

id 默认 mode 说明
save_container live V1.73 有正式接口;确认导入路径
do_upload live SaveFieldValue + annexes/upload;list 验真接口文档未单列,为客户端同源
transfer / batch_transfer / hold_split / label / customer_message mock 多数 不在 V1.73 正式表;切 live 前必须对方确认 endpoint + 联调

CC_MOCK=true 或设置页 Mock:所有写视为 mock,不能当 TEST/上线验收。


4. 编码规范

  1. 语言:TypeScript strict;API 入参用 zod;日志用 pino。
  2. 改动范围:只改任务相关文件;禁止顺手大重构、无关格式化。
  3. 兼容:保持现有状态机、API 契约、Prisma 字段语义;破坏性变更先改 docx 再改代码。
  4. UI:运营后台沿用 Antd 5;确认页大表用 TanStack Virtual;中文文案集中 src/constants/ui-copy.ts。
  5. Diff 优先:补丁级修改;删除代码要确认无引用。
  6. 测试:状态机 / 分类 / 解析 / 锁 / 安全路径变更需补或更新 tests/unit;关键路径跑 pnpm test。
  7. 密钥:不提交真实 IMAP 授权码、CC 密码;用设置页或本地 .env(已 gitignore)。
  8. 包管理:只用 pnpm;compose worker 用 tsx 跑源码(避免 dist 里 @/ 别名未重写)。
  9. 提交:不擅自 git commit / push;用户明确要求再建提交。
  10. 文档:改行为后同步本文件或对应 docx;Cursor 进度记在 implementation-plan.mdc(短摘要 + Done)。
  11. 读附件/快照路径:统一 src/lib/safe-path.ts(resolveDataFile / assertUnderRoot),禁止 path.startsWith(dataRoot) 自行拼装。
  12. 导入柜头覆盖:客户端字段须进 pickContainerHeaderPatch 白名单(confirm.ts),禁止 z.record 原样 merge 进 CC。

5. 红线(绝对不能违反)

# 红线
1 Worker 禁止承担新增预报 SaveContainer(导入只在 web ConfirmImport)
2 禁止绕过状态机非法迁移(含直接改库「修好」状态)
3 禁止用 Prisma 连接池直接 GET_LOCK/RELEASE_LOCK(必须用 db-lock 同源连接)
4 mock ≠ TEST 验收:CC_MOCK=true / 设置页 Mock 开着时的「成功」不能当上线门禁
5 预报导入走确认页门禁;指令类/转仓等不可冒充「新增柜」乱走 SaveContainer
6 幂等不可丢:同一邮件不得因重拉产生重复业务柜(依赖 message_id / uid / raw_hash)
7 zip 只解一层;路径穿越条目丢弃;解压后体积受 ATTACHMENT_MAX_BYTES 约束
8 冲突柜默认阻断;强制跳过仅 Admin + ENABLE_FORCE_IMPORT,且必须写审计
9 先落库再 \Seen;解析失败仍可「重新解析」,但不得在 IMPORTING/SUCCESS/PARTIAL_SUCCESS 重解析
10 不擅自改端口约定:本地 MySQL 对外 7023;Web 3100
11 不把密钥写进仓库;不在日志打印完整密码/授权码/Session/CC token
12 SMTP/AI 客服仍不做;OCR/IDLE/OAuth/多邮箱等变更须书面确认后改 PRD
13 生产禁止默认口令与默认 SESSION_SECRET(启动校验)
14 附件落盘路径必须在 data/ 下;API 下载禁止任意绝对路径读盘

违反以上任一条视为事故级改动,须回滚或补审计与文档后再合入。


6. 排障速查

现象 处理
跳过拉取 lock_busy pnpm exec tsx scripts/clear-imap-lock.ts;重启 worker;确认无多实例互抢
拉取成功列表无信 看 Toast「新入库 / 候选」;候选 0 = lookback 内无新信或已入库;点刷新;确认 pnpm worker 在跑
IMAP 未配置 Admin → /settings 绑定邮箱 + 测试连接
解析失败 详情「重新解析」;看 last_error;对照韧性文档错误码
不能确认导入 状态/类型门禁不满足(见门禁逻辑 forecast-confirm-gate);有效货件 ≥1
CC 导入失败 /logs;补偿 ≤3;审计 /logs?tab=audit
compose worker 起不来 看日志是否 @/ MODULE_NOT_FOUND;应用 tsx src/worker/index.ts
production 起不来 Invalid environment (production security) 换强 SESSION_SECRET + 非弱 APP_*_PASS,admin/ops 口令不可相同
登录 429 同 IP+用户名 1 分钟过多失败;稍后再试或换源 IP
设置 CC 提示 HOST_NOT_ALLOWED api_base 仅允许 *.carriercentral.vip / localhost / 与 env CC_API_BASE 同主机(防 SSRF)
OAuth 回调 bad_state state 15 分钟有效,须从本站 Admin 点「绑定」发起;勿复用旧链接

7. 角色与验收提示

角色 能力
ops 列表/详情/确认导入/重新解析/附件下载;日志可看全站导入(含租户列)
admin 上述 + 改类型、设置(公共邮箱)、立即拉取、审计、FORCE 导入、忽略邮件、租户账号管理
租户(customer) 登录后配置自己的 CC 客户档案与邮箱;仅见本账号邮件与导入;无租户管理页

一期验收要点(摘要)

  • 列表有 IMAP 真信;详情可确认导入
  • 新增预报 + 清单可进确认导入;勾选行与请求体一致
  • CC live 烟雾(非 mock)customerLogin → SaveContainer
  • 冲突阻断;失败可重试/补偿;审计可查
  • 生产环境可启动(密钥已换)、登录与导入有限流、附件只能在 data 根下

8. 开发注意点(日常易踩)

8.1 起环境

注意 说明
必须起 worker pnpm dev 已默认连带 worker;若只用 pnpm dev:web 则不拉信、不解析
MySQL 端口 7023 DATABASE_URL 写错成 3306 会连到别的本机实例
CC 无账号先 Mock CC_MOCK=true 可联调 UI;有 demovip/test 账号再关 Mock
多开 worker 同库多实例会抢 imap_poll(一得锁、一 lock_busy);一般只保留一个 worker
Windows 路径 附件 path 入库用 /;读盘一律走 resolveDataFile,不要自己 join absolute 逃逸

8.2 业务逻辑

注意 说明
确认门禁 以 canEnterForecastConfirm / 状态机为准,不是「邮件存在即可点导入」
version / 幂等 导入带 version + idempotency_key;并发第二请求可能 VERSION_CONFLICT / 直接返回已成功
渠道未映射 有 CHANNEL_UNMAPPED 须 ack_unmapped_channels,否则拒绝
强制跳冲突 须 Admin + ENABLE_FORCE_IMPORT=true,默认关
指令识别无 LLM instruction-lexicon / split-instructions;改规则先对齐 docx/产品规则-*.md
OCR OCR_PROVIDER=local 为启发式兜底,不是商用识别;aliyun 现为 pending SDK,不能写「已接阿里云 OCR」上线文案
自动执行 新增预报 / DO / 转仓主路径 人工确认 优先;ENABLE_AUTO_EXEC_* 与 cc_write_capability 双重约束;默认 mock endpoint 不等于 CC 真有该接口

8.3 安全相关(开发也要守)

注意 说明
登录限流 10 次/分钟/IP+用户名(/api/auth/login)
导入限流 10 次/分钟/登录用户(/api/mails/:id/import)
限流实现 进程内 Map,多副本不共享;要严格限流需前置网关
健康检查 /api/health 无鉴权(docker healthcheck 依赖);勿往 response 里塞密钥
会话 iron-session + Cookie HttpOnly;改 SESSION_SECRET 会使所有旧会话失效
邮箱密码 DB 中 AES-GCM,密钥派生自 SESSION_SECRET——换 SESSION_SECRET 后旧邮箱密文无法解密,需重新保存密码
OAuth Admin 发起;state 含 actor + exp;回调无登录 Cookie 也可完成绑定(依赖 state 签名)

8.4 改 CC / 接口

注意 说明
以 V1.73 为契约底 customerLogin / GetShippingLineList(GET) / SaveContainer / SaveFieldValue / annexes/upload
文档外 endpoint 转仓/工单等默认 mock;live 前写清路径与验收入库
token 可出现在 query GET 船司列表等;日志/反向代理勿明文长期存 query log
CC 密码 协议要求 MD5;设置页传明文则服务端 md5 后存用
改 api_base 受主机白名单约束;自定义域名须 .env CC_API_BASE 同主机才能保存

8.5 测试建议(改完要跑)

pnpm test                              # 全量单测
pnpm exec vitest run tests/unit/security-hardening.test.ts
pnpm cc:smoke -- --mode=mock           # UI 联调
pnpm cc:smoke -- --mode=live           # 有真账号时

9. 上市 / 生产注意点

上线前把下表当 检查清单 勾选;未勾完不算可对客。

9.1 必改环境变量

变量 要求
NODE_ENV production
SESSION_SECRET ≥32 位高强度随机;禁止 example 占位串
APP_ADMIN_PASS / APP_OPS_PASS 强密码;互不相同;禁止 admin123/ops123/password/123456
DATABASE_URL 生产 MySQL;账号最小权限;勿把开发 7023 写进生产
CC_MOCK 正式业务 false;且设置页 Mock 关闭
CC_API_BASE / CC_SAAS_HEADER / CC_LOGIN_MARK 生产租户正式地址与 Saas 头(非随意 test 残留)
CC_* 账号 生产只读/业务约定账号;轮换策略由运维定
OAUTH_PUBLIC_BASE_URL 公网 HTTPS 根,与 OAuth 控制台回调一致
OAUTH_*_CLIENT_* 仅在开启 Gmail/MS 绑定时配置;密钥不入库明文日志
ENABLE_FORCE_IMPORT 生产默认 false;仅紧急支持场景临时开
ENABLE_AUTO_EXEC_* 生产按业务决定;无 live endpoint 前保持关或 capability mock
ATTACHMENT_MAX_BYTES / shipment 上下限 按磁盘与 CC 限制校准

9.2 基础设施

项 建议
进程 web + worker 都要存活;worker 挂了只读历史、不进新信
实例数 IMAP 锁全局一把:worker 建议单副本(或明确主备切换)
数据盘 ./data(或挂载卷)持久化:eml、附件、imap-runtime.json;勿用无状态容器丢盘
备份 MySQL + data/mails 同步备份策略
HTTPS 生产强制 HTTPS;Cookie secure 在 production 已开启
反向代理 若记 access log,注意 CC token 可能在 query;脱敏或关闭详细 query 日志
健康检查 用 /api/health 即可;监控 mysql / imap_sync_alert
对外暴露 运营后台勿裸奔公网无 VPN;登录限流不替代 WAF/零信任

9.3 安全与合规

项 说明
启动硬校验 生产弱密钥 直接拒绝启动(见 src/lib/env.ts)
路径安全 下载/解析只读 data/ 下相对路径;防 ../ 与 data 前缀绕过
登录/导入限流 单机有效;多副本需 LB 层限流
角色 admin / ops / 租户(AppUser.role=customer)。租户仅见自有邮箱下的邮件与导入;admin 可跨租户查看导入。员工账号泄露仍可看全站业务数据
审计 导入成功/失败、FORCE、邮箱绑定、CC 设置、OAuth 绑定须可查
保留期 DATA_RETENTION_DAYS + worker retention;合规要求则加大或关闭清理并外备
第三方邮件 授权码/OAuth 属客户邮箱权限;SOP 告知运营勿绑个人箱当生产总线

9.4 业务上线闸门(建议顺序)

  1. 配置:生产 env + 强密钥 + CC_MOCK=false
  2. 库:pnpm db:migrate(或等价 migrate deploy);不要用生产库跑 seed 默认弱口令覆盖
  3. IMAP:设置页绑定业务邮箱并「测试连接」;确认 lookback/filter 符合运营预期
  4. CC live:pnpm cc:smoke -- --mode=live 或设置页「测试连接」
  5. 抽样邮件:真拉一封预报类 → 确认导入一柜(低风险测试数据)→ 查 CC 柜与审计
  6. 补偿与冲突:人为失败/冲突场景各看一次 /logs
  7. 监控:worker 连续失败告警、imap_sync_alert、磁盘水位、MySQL 连接
  8. 回滚预案:保留上一镜像 + DB 备份;知悉换 SESSION_SECRET 会登出全员并可能需重配邮箱密码

9.5 明确不要当作上市完成的事项

  • 仅 Mock 绿色通过
  • 仅 customerLogin 成功、未做过 SaveContainer
  • OCR local 启发式「识别准确」宣传
  • 文档外写接口未 live 联调就打开自动执行
  • 开发默认口令仍在生产 .env
  • 只部署 web 未部署 worker

10. 安全加固摘要(实现侧,便于 Code Review)

下列已在代码侧落地,改相关模块时请保持不退化:

能力 位置(参考)
数据区路径约束 src/lib/safe-path.ts
CC api_base 主机约束 src/lib/cc-api-base.ts + api/settings/cc
生产弱密钥拒绝启动 src/lib/env.ts
登录/滑动窗口限流 src/lib/rate-limit.ts + api/auth/login
OAuth state HMAC+TTL+actor src/services/oauth/providers.ts
DO 上传 410 最多 1 次重试 src/services/cc/do-upload.ts
柜头字段白名单 merge src/services/import/confirm.ts pickContainerHeaderPatch
Zip 解压体积上限 src/services/parse/zip-extract.ts
单测 tests/unit/security-hardening.test.ts

11. 联系与变更

  • 需求/行为变更:先改 docx/需求规格-*.md 与韧性文档,再改代码。
  • 协作入口文档:即本文件 docs/邮箱项目.md。
  • 开发进度:.cursor/rules/implementation-plan.mdc。
  • CC 路径/字段:以 docx/接口/carriercentral客户端通用接口V1.73.md 为准;缺接口时先 mock + 书面确认。