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.

437 lines
21 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 邮箱项目(邮件自动预报)
> 给接手同事看的入口文档。细则以 `docx/` 下 PRD / 技术设计 / 韧性为准;冲突时以 PRD 为准。
> **更新日期**:2026-08-04(补充开发注意点 + 上市/生产安全清单)
---
## 1. 项目信息
| 项 | 说明 |
|---|---|
| 名称 | 邮件自动预报系统(repo: `email-forecast`) |
| 目标 | 从绑定邮箱 IMAP(多箱/IDLE/OAuth|授权码)拉取 → 解析/分类 → **NEW 人工确认 SaveContainer**;DO/转仓/工单等走对应确认或指令写能力 |
| **不做**(一期) | 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 上市注意点](#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.72 | `docx/接口/carriercentral客户端通用接口V1.72.md` |
| 实施计划(Cursor) | `.cursor/rules/implementation-plan.mdc` |
| 快速 README | `README.md` |
---
## 2. 常用命令
### 2.1 日常开发
```bash
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 一键三件套
```bash
pnpm compose:up # mysql + web + worker
pnpm compose:ps
pnpm compose:logs
```
**Windows 一键启停(先杀旧进程/端口再启动,防地址占用):**
```powershell
# 默认 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 数据库
```bash
pnpm db:generate
pnpm db:push
pnpm db:migrate:dev # 开发迁移
pnpm db:migrate # 部署 migrate deploy
pnpm db:seed
```
### 2.4 IMAP / CC / 运维
```bash
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 测试与构建
```bash
pnpm test # vitest(含安全路径 tests/unit/security-hardening.test.ts)
pnpm test:watch
pnpm test:e2e # playwright(需先起服务)
pnpm lint
pnpm build
```
### 2.6 默认环境要点
```env
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.72 有正式接口;确认导入路径 |
| `do_upload` | live | `SaveFieldValue` + `annexes/upload`;list 验真接口文档未单列,为客户端同源 |
| `transfer` / `batch_transfer` / `hold_split` / `label` / `customer_message` | **mock** | 多数 **不在 V1.72 正式表**;切 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 导入(开关开启时)、忽略邮件 |
**一期验收要点(摘要)**
- 列表有 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.72 为契约底** | `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 测试建议(改完要跑)
```bash
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 两账号模型;**不是多租户**;泄露一账号即全站业务数据 |
| 审计 | 导入成功/失败、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.72.md` 为准;缺接口时先 mock + 书面确认。