|
|
# 项目全貌与开发指南
|
|
|
|
|
|
> **文档定位**:`X:\work\yx`(包名 `email-forecast`)后续开发的唯一权威参考(系统核心知识库)。
|
|
|
> **产品名**:邮件自动预报系统
|
|
|
> **形态**:Next.js 运营 Web + IMAP Worker + MySQL,**不是**桌面助手、不是客服渠道。
|
|
|
> **代码根目录**:`X:\work\yx`
|
|
|
> **文档版本**:v1.0
|
|
|
> **编写日期**:2026-08-18
|
|
|
> **对照材料**:`README.md`、`docs/邮箱项目.md`、`docx/` PRD/技术设计、当前源码。
|
|
|
> **密钥纪律**:只写配置**键名与路径**,不写 IMAP 授权码、CC 密码、OAuth secret 的具体值。
|
|
|
|
|
|
与 `carriercentral-ai-assistant` 的关系:yx 是**运营后台**(拉邮件 → 解析 → 人工确认 → 写 CarrierCentral SaaS);助手仓是**桌面查询 Demo**(读仓)。两边打同一套海外仓 HTTP,但进程、UI、状态机完全独立。把 yx 能力迁到助手仓时,以本文模块名为地图,**禁止**把 Worker 的 SaveContainer 禁令或 IMAP 轮询直接塞进 Tkinter。
|
|
|
|
|
|
---
|
|
|
|
|
|
## 0. 阅读路径与权威优先级
|
|
|
|
|
|
材料冲突时,优先级从高到低:
|
|
|
|
|
|
1. **正在运行的代码**:`src/app/**`、`src/services/**`、`src/worker/index.ts`、`prisma/schema.prisma`
|
|
|
2. **行为契约**:`docx/需求规格-邮件自动预报-v0.2.md`、`docx/接口/carriercentral客户端通用接口V1.73.md`
|
|
|
3. **协作入口**:`docs/邮箱项目.md`(红线、排障、上市清单)
|
|
|
4. **指令规则**:`docx/产品规则-邮件指令识别与拆分.md`、`docs/邮件指令提取规则.md`
|
|
|
5. **速查**:`README.md`
|
|
|
|
|
|
改识别规则先改 `docx/产品规则-*` 再改 `src/services/parse/`。改 CC 路径以 V1.73 为准;文档外 endpoint 默认 mock。
|
|
|
|
|
|
---
|
|
|
|
|
|
## 1. 技术栈与全局依赖
|
|
|
|
|
|
### 1.1 系统形态
|
|
|
|
|
|
三进程,无独立消息队列、无 Redis、无 GraphQL。
|
|
|
|
|
|
| 组件 | 本仓库状态 |
|
|
|
|------|------------|
|
|
|
| 前端 | **Next.js 15 App Router** + **React 18** + **Ant Design 5** |
|
|
|
| 后端 | 同进程:`src/app/api/**/route.ts`(Route Handlers) |
|
|
|
| Worker | 独立进程 `src/worker/index.ts`(`tsx` 跑源码) |
|
|
|
| ORM | **Prisma 5** + **mysql2** |
|
|
|
| 数据库 | **MySQL 8**(库名 `email_forecast`,宿主机端口 **7023**) |
|
|
|
| 缓存 | **无 Redis**。CC token 在表 `cc_token_cache`;会话在 Cookie |
|
|
|
| 消息队列 | **无**。补偿/自动执行靠 Worker 轮询 `import_compensation.next_retry_at` |
|
|
|
| 状态管理 | **无 Redux/Pinia**。客户端 `useState` + hooks(`useMails` / `useMailDetail`)+ `AuthSessionProvider` |
|
|
|
| 包管理 | **仅 pnpm**(不要 npm/yarn) |
|
|
|
|
|
|
### 1.2 关键三方库(`package.json`)
|
|
|
|
|
|
| 包 | 用途 |
|
|
|
|----|------|
|
|
|
| `next` ^15.1.3 | App Router、API、生产 `next start -p 3100` |
|
|
|
| `react` / `react-dom` 18 | UI |
|
|
|
| `antd` ^5.22 + `@ant-design/icons` + `cssinjs` + React19 patch | 运营后台组件库 |
|
|
|
| `@prisma/client` / `prisma` | schema → MySQL |
|
|
|
| `mysql2` | Prisma 驱动;`db-lock.ts` 独立连接做 `GET_LOCK` |
|
|
|
| `iron-session` | Cookie 会话 `email_forecast_session` |
|
|
|
| `imapflow` | IMAP 拉取 / IDLE |
|
|
|
| `mailparser` | 解析 eml |
|
|
|
| `exceljs` | 装箱清单 / 转仓表 xlsx |
|
|
|
| `adm-zip` | zip 解一层 |
|
|
|
| `tesseract.js` | `OCR_PROVIDER=local` 启发式 OCR |
|
|
|
| `undici` | CC HTTP(`src/services/cc/http.ts`) |
|
|
|
| `zod` | env 与 API 入参校验 |
|
|
|
| `pino` | 结构化日志 `src/lib/logger.ts` |
|
|
|
| `uuid` | 幂等 key、补偿 `retry_token`、trace |
|
|
|
| `@tanstack/react-virtual` | 确认页大表 `ShipmentVirtualTable` |
|
|
|
|
|
|
开发:`vitest`、`@playwright/test`、`tsx`、`typescript`。Lint:`next lint`(无独立 ESLint/Prettier 配置文件)。
|
|
|
|
|
|
### 1.3 本仓库明确没有的技术
|
|
|
|
|
|
- Vue / 桌面 Tkinter / LangChain Agent
|
|
|
- Redis / Kafka / RabbitMQ
|
|
|
- 独立 Tenant 表 / 一租户多登录用户(当前为一租户一 `AppUser.role=customer` 账号)
|
|
|
- SMTP 自动回信、真实大模型指令识别(词表/规则)
|
|
|
|
|
|
---
|
|
|
|
|
|
## 2. 目录结构详解
|
|
|
|
|
|
组织逻辑:**按运行时分层 + 按业务域分子目录**。
|
|
|
|
|
|
```
|
|
|
页面/API(src/app)
|
|
|
→ hooks / components(UI)
|
|
|
→ services(imap / parse / import / cc)
|
|
|
→ lib(env / session / prisma 辅助)
|
|
|
→ prisma + MySQL
|
|
|
Worker(src/worker)直接调同一套 services,禁止走 ConfirmImport 写新增预报。
|
|
|
```
|
|
|
|
|
|
### 2.1 仓库根
|
|
|
|
|
|
| 路径 | 职责 |
|
|
|
|------|------|
|
|
|
| `package.json` | 脚本与依赖;`name: email-forecast` |
|
|
|
| `pnpm-lock.yaml` | 锁文件 |
|
|
|
| `docker-compose.yml` | `mysql` + `web` + `worker` |
|
|
|
| `Dockerfile` | 多阶段 Node 20 bookworm;web 入口 `docker-entrypoint-web.sh` |
|
|
|
| `next.config.ts` | Antd transpile、关 dev 指示标、client 禁 `async_hooks` |
|
|
|
| `tsconfig.json` | `@/*` → `./src/*`,strict |
|
|
|
| `tsconfig.worker.json` | Worker 编译(compose 实际用 tsx 跑源码) |
|
|
|
| `vitest.config.ts` | 单测 + coverage |
|
|
|
| `playwright.config.ts` | e2e,baseURL `3100` |
|
|
|
| `playwright.test-mode.config.ts` | TEST_MODE 端口 3110 |
|
|
|
| `.env.example` | 环境变量模板 |
|
|
|
| `prisma/schema.prisma` | 全表 |
|
|
|
| `prisma/seed.ts` | 仅账号 + 部分 write capability |
|
|
|
| `scripts/` | 启动栈、IMAP/CC 烟雾、retention、样例导入 |
|
|
|
| `fixtures/emails/` | 01–16 金样例(spec + expected) |
|
|
|
| `docx/` | PRD / 技术设计 / UI / 韧性 / 金样例 JSON / CC 接口 |
|
|
|
| `docs/` | 协作文档 + 本知识库 + Windows 一键脚本 |
|
|
|
| `data/` | 运行时 eml/附件/日志(gitignore);compose 挂载 `./data` |
|
|
|
| `tests/unit` `tests/integration` `tests/e2e` | 测试 |
|
|
|
|
|
|
### 2.2 `src/app/` — 页面与 API
|
|
|
|
|
|
| 路径 | 职责 |
|
|
|
|------|------|
|
|
|
| `layout.tsx` | 根:`lang=zh-CN`、`AntdProvider`、标题「邮件自动预报」 |
|
|
|
| `globals.css` | 全局样式 |
|
|
|
| `error.tsx` | 错误页 |
|
|
|
| `page.tsx` | `/` → `redirect("/mails")` |
|
|
|
| `(auth)/login/page.tsx` | 登录 |
|
|
|
| `(ops)/layout.tsx` | `AuthGuard` + `TopNav` + `main.ops-main` |
|
|
|
| `(ops)/mails/page.tsx` | 邮件列表 |
|
|
|
| `(ops)/mails/[id]/page.tsx` | 详情 |
|
|
|
| `(ops)/mails/[id]/confirm/page.tsx` | 确认导入(按 instruction 分发视图) |
|
|
|
| `(ops)/logs/page.tsx` | 导入日志 / 拉取记录 |
|
|
|
| `(ops)/settings/page.tsx` | Admin 设置 |
|
|
|
| `(ops)/test-workbench/page.tsx` | `TEST_MODE=1` 测试台 |
|
|
|
| `api/**/route.ts` | 42 个 Route Handler(见第 6 节) |
|
|
|
|
|
|
**无** `middleware.ts`。鉴权在各 route 的 `requireSession` / `requireAdmin`,页面靠客户端 `AuthSessionProvider`。
|
|
|
|
|
|
### 2.3 `src/services/` — 业务核
|
|
|
|
|
|
| 目录 | 职责 |
|
|
|
|------|------|
|
|
|
| `imap/` | `poller.ts` 轮询;`idle-supervisor.ts` IDLE;`snapshot.ts` 落 eml;`pull-filter.ts` 过滤;`mailbox-config.ts` 多箱;`stale-reaper.ts` 超时回收;`runtime-status.ts` 连续失败 |
|
|
|
| `parse/` | `pipeline.ts` 主解析;`classify.ts` 类型;`mail-intent.ts` 意图文案;`packing-list.ts` 装箱表;`split-instructions.ts` 指令拆分;`extract-container-header.ts` 柜头 |
|
|
|
| `import/` | `confirm.ts` **ConfirmImport**(唯一新增预报 SaveContainer);`compensation.ts` 补偿;`auto-exec.ts` 指令自动写;`confirm-do-upload.ts` / `confirm-batch-transfer.ts` / `confirm-work-order.ts` |
|
|
|
| `cc/` | `http.ts` `auth.ts` `save-container.ts` `do-upload.ts` `conflict.ts` `shipping-line.ts` `mock-*` `write-capability.ts` `settings-config.ts` |
|
|
|
| `ocr/` | `index.ts` local/aliyun/off;`sanitize.ts` |
|
|
|
| `oauth/` | `providers.ts` Google/MS;state HMAC |
|
|
|
| `customer/` | `resolve-sender.ts` 发件人→客户编码 |
|
|
|
| `alert/` | `webhook.ts` 企微/飞书 JSON POST |
|
|
|
| `retention/` | `cleanup.ts` |
|
|
|
| `test/` | TEST_MODE 工作台、fixture 评测 |
|
|
|
| `db.ts` | Prisma 单例 |
|
|
|
| `db-lock.ts` | **独立 mysql2 连接** `GET_LOCK` / `RELEASE_LOCK` |
|
|
|
| `state-machine.ts` | `assertTransition` / `resolveAfterParse` |
|
|
|
| `audit.ts` | 审计日志 |
|
|
|
|
|
|
### 2.4 `src/lib/` `src/components/` `src/hooks/` `src/constants/` `src/types/`
|
|
|
|
|
|
| 路径 | 职责 |
|
|
|
|------|------|
|
|
|
| `lib/env.ts` | Zod 校验环境;生产弱密钥拒绝启动 |
|
|
|
| `lib/session.ts` | iron-session;`verifyCredentials` **读 .env 账号,不读 `app_user` 表** |
|
|
|
| `lib/api.ts` | `ok`/`fail`、`requireSession`、`requireAdmin`、BigInt JSON |
|
|
|
| `lib/safe-path.ts` | 附件/快照必须在 `data/` 下 |
|
|
|
| `lib/cc-api-base.ts` | CC `api_base` 主机白名单(防 SSRF) |
|
|
|
| `lib/rate-limit.ts` | 进程内滑动窗口 |
|
|
|
| `lib/secret-crypto.ts` | AES-GCM,密钥派生自 `SESSION_SECRET` |
|
|
|
| `lib/logger.ts` | pino |
|
|
|
| `lib/mail-trace.ts` | 单封 `traceId` |
|
|
|
| `lib/test-mode.ts` / `test-mode-guard.ts` | TEST_MODE;测试 API 非测试环境返回 404 |
|
|
|
| `components/TopNav.tsx` | 顶栏:邮件 / 日志 / 设置(admin) / 测试台 |
|
|
|
| `components/MailTable.tsx` | 列表 |
|
|
|
| `components/confirm/*` | 确认页:预报 / DO / 转仓 / 工单 |
|
|
|
| `components/Cc*.tsx` | 对照 CC 表单的运营控件 |
|
|
|
| `hooks/useMails.ts` `useMailDetail.ts` `useImport.ts` `useCcUiMode.ts` | 客户端数据 |
|
|
|
| `constants/ui-copy.ts` `error-copy.ts` | 中文文案 |
|
|
|
| `types/mail.ts` | `MailType` / `MailStatus` / 柜头货件字段 |
|
|
|
|
|
|
### 2.5 `src/worker/index.ts`
|
|
|
|
|
|
常驻循环 `scheduleLoop`:
|
|
|
|
|
|
1. `ImapPoller.tick()`
|
|
|
2. `staleReaper.run()`(`PARSING`/`IMPORTING` 超时打回)
|
|
|
3. `drainFetchedMails(10)` 消化 `FETCHED`
|
|
|
4. `processDueAutoExec(10)`
|
|
|
5. `processDueCompensations(10)`
|
|
|
|
|
|
启动:`ImapPoller.clearStaleLock()`、`ensureCcWriteCapabilities()`、`IdleSupervisor.start()`。
|
|
|
吞掉 IMAP `ETIMEOUT` 的 `uncaughtException`,避免整进程退出。
|
|
|
|
|
|
---
|
|
|
|
|
|
## 3. 入口与启动流程
|
|
|
|
|
|
### 3.1 入口一览
|
|
|
|
|
|
| 入口 | 实质 |
|
|
|
|------|------|
|
|
|
| `pnpm dev` / `dev:stack` | `scripts/dev-stack.mjs`:同时 `next dev -p 3100` + `tsx src/worker/index.ts` |
|
|
|
| `pnpm dev:web` | 仅 Web,**不拉信** |
|
|
|
| `pnpm worker` | 仅 Worker |
|
|
|
| `pnpm start` | 生产 Next `:3100` |
|
|
|
| `.\docs\start-system.ps1` | Windows:mysql 容器 + 本机 web/worker(默认不拉 node 镜像) |
|
|
|
| `pnpm compose:up` | docker 三件套 |
|
|
|
| `prisma/seed.ts` | 账号种子 |
|
|
|
|
|
|
无 FastAPI `app.include_router`。Next 按文件系统挂载路由。
|
|
|
|
|
|
### 3.2 Web 启动全过程
|
|
|
|
|
|
```
|
|
|
pnpm dev:web
|
|
|
→ Next 加载 next.config.ts
|
|
|
→ 首次请求触发 getEnv()(读 process.env,Zod)
|
|
|
→ prisma 单例连 DATABASE_URL
|
|
|
→ App Router:
|
|
|
/ → redirect /mails
|
|
|
(ops)/* → 客户端 AuthSessionProvider 调 GET /api/auth/me
|
|
|
未登录 → /login
|
|
|
→ API:各 route 内 requireSession / requireAdmin
|
|
|
```
|
|
|
|
|
|
**没有**全局中间件链。登录:`POST /api/auth/login` → `verifyCredentials`(env)→ `session.save()`。
|
|
|
|
|
|
Docker web:`scripts/docker-entrypoint-web.sh`(`SEED_ON_START` 时可 db push + seed)→ `pnpm start`。compose 把 `DATABASE_URL` 改成 `mysql://app:app@mysql:3306/email_forecast`。
|
|
|
|
|
|
注意:`Dockerfile` `EXPOSE 3000`,实际监听 **3100**(`package.json` `start`)。
|
|
|
|
|
|
### 3.3 Worker 启动全过程
|
|
|
|
|
|
```
|
|
|
tsx src/worker/index.ts
|
|
|
→ getEnv()
|
|
|
→ clearStaleLock(imap_poll)
|
|
|
→ ensureCcWriteCapabilities() upsert 写能力行
|
|
|
→ IdleSupervisor.start() 各箱 IDLE(IMAP_IDLE_ENABLED 且账号 idle_enabled)
|
|
|
→ scheduleLoop:
|
|
|
tick → 按 imap_settings.pollIntervalMs(否则 POLL_INTERVAL_MS)+ 连续失败退避
|
|
|
```
|
|
|
|
|
|
数据库:compose `mysql` healthcheck 通过后 web/worker 才起。本地需先 `docker compose up -d mysql` + `pnpm exec prisma db push`。
|
|
|
|
|
|
---
|
|
|
|
|
|
## 4. 核心模块与依赖关系
|
|
|
|
|
|
### 4.1 模块划分
|
|
|
|
|
|
| 模块 | 关键文件 | 职责 |
|
|
|
|------|----------|------|
|
|
|
| 认证 | `lib/session.ts` `api/auth/*` | admin/ops Cookie |
|
|
|
| 邮箱绑定 | `imap/mailbox-config.ts` `api/settings/mailboxes*` `oauth/*` | 多箱、授权码/OAuth |
|
|
|
| IMAP 拉取 | `imap/poller.ts` `pull-filter.ts` `snapshot.ts` | 入库 `mail_message` |
|
|
|
| 解析 | `parse/pipeline.ts` `classify.ts` | 类型 + 柜头 + 货件 + 指令 |
|
|
|
| 状态机 | `state-machine.ts` | 合法迁移 |
|
|
|
| 确认导入 | `import/confirm.ts` | 预报 SaveContainer(仅 web) |
|
|
|
| 指令写 CC | `confirm-do-upload` / `confirm-batch-transfer` / `confirm-work-order` / `auto-exec` | DO/转仓/工单 |
|
|
|
| CC HTTP | `cc/http.ts` `auth.ts` | customerLogin + 写接口 |
|
|
|
| 冲突锁 | `cc/conflict.ts` `container_active_lock` | 同柜互斥 |
|
|
|
| 补偿 | `import/compensation.ts` | TIMEOUT_UNKNOWN 重试 ≤3 |
|
|
|
| 设置 | `api/settings/*` | IMAP 间隔、过滤、CC、OAuth |
|
|
|
| 可观测 | `api/health` `alert/webhook.ts` `imap-pull-log` | 健康与告警 |
|
|
|
| TEST | `api/test/*` `TestWorkbench` | 仅 TEST_MODE |
|
|
|
|
|
|
调用方向(禁止反向):
|
|
|
|
|
|
```
|
|
|
页面 → hooks/client-api → Route Handler → services.* → prisma / CC / IMAP
|
|
|
Worker → services.imap|parse|import.compensation|auto-exec
|
|
|
Worker ──X── ConfirmImport.execute(新增预报)
|
|
|
```
|
|
|
|
|
|
### 4.2 公共层
|
|
|
|
|
|
| 能力 | 文件 |
|
|
|
|------|------|
|
|
|
| API 信封 `{ok, data}` / `{ok:false, error}` | `lib/api.ts` |
|
|
|
| 限流 | `lib/rate-limit.ts` |
|
|
|
| 命名锁 | `db-lock.ts`(禁止 Prisma 池直接 GET_LOCK) |
|
|
|
| 路径安全 | `safe-path.ts` |
|
|
|
| 审计 | `audit.ts` |
|
|
|
| 中文错误 | `constants/error-copy.ts` `formatOperatorError` |
|
|
|
|
|
|
### 4.3 页面层级与组件树
|
|
|
|
|
|
```
|
|
|
RootLayout (AntdProvider)
|
|
|
/login 无 TopNav
|
|
|
OpsLayout (AuthGuard + TopNav)
|
|
|
/mails MailTable + 筛选
|
|
|
/mails/[id] 详情:摘要、附件、重新解析、改类型(admin)
|
|
|
/mails/[id]/confirm 按 instruction_id:
|
|
|
ForecastConfirmView
|
|
|
DoUploadConfirmView
|
|
|
TransferConfirmView
|
|
|
WorkOrderConfirmView
|
|
|
InstructionConfirmPicker
|
|
|
/logs 导入 + ImapPullLogsPanel
|
|
|
/settings Admin:ImapPullSettingsCard、邮箱、CcSettingsCard、OauthSettingsCard
|
|
|
/test-workbench TestWorkbench(TEST_MODE)
|
|
|
```
|
|
|
|
|
|
顶栏 `TopNav`:`ImapStatusBanner` + 菜单 + 角色 Tag + 登出。
|
|
|
|
|
|
确认页 `confirm/page.tsx`:`extractInstructionsFromMail` → `resolveConfirmInstruction`;无 `instruction_id` 时 `router.replace(confirmHref(...))`。
|
|
|
|
|
|
---
|
|
|
|
|
|
## 5. 数据模型与数据流
|
|
|
|
|
|
### 5.1 表结构(`prisma/schema.prisma` → MySQL)
|
|
|
|
|
|
```
|
|
|
mailbox_account 1──* mail_message 1──* mail_attachment
|
|
|
│
|
|
|
├──1 parse_result(柜头/货件 JSON)
|
|
|
└──* container_import 1──* import_compensation
|
|
|
|
|
|
container_active_lock(柜号主键,跨邮件互斥)
|
|
|
cc_token_cache / cc_settings / cc_write_capability / oauth_settings
|
|
|
imap_settings / imap_filter_rule / imap_pull_log
|
|
|
sender_customer_map
|
|
|
app_user ← seed 写入;登录当前不查此表
|
|
|
```
|
|
|
|
|
|
**MailboxAccount** `mailbox_account`:IMAP 主机/端口/用户;`password_enc` AES-GCM;`auth_type` `PASSWORD|OAUTH`;OAuth 令牌密文与过期;`idle_enabled`;`username` 唯一。
|
|
|
|
|
|
**MailMessage** `mail_message`:幂等 `message_id`、`(mailbox, folder, imap_uid)`、`raw_hash`;`mail_type`;`status`;`type_evidence` JSON;`snapshot_path`;`trace_id`;`version`(乐观锁,导入用);正文/OCR/线程字段。
|
|
|
|
|
|
**MailAttachment**:`sha256`、`path`(相对 `data/`)、`rejected`、`template_id`。
|
|
|
|
|
|
**ParseResult**:`container_header` / `shipments` / `lineage` JSON,与 `types/mail.ts` 的 `ContainerHeader`、`ShipmentRow` 对齐。
|
|
|
|
|
|
**ContainerImport**:每次向 CC 提交一柜;`shipments_hash` 参与唯一键 `uk_mail_container_shiphash`;`external_id` 为 CC 返回 id。
|
|
|
|
|
|
**ImportCompensation**:`reason`、`retry_count`/`max_retry`、`next_retry_at`、`retry_token` UUID。
|
|
|
|
|
|
**CcSettings** id=1:mock、api_base、saas、login_mark、username、`password_enc`。优先于 `.env`。
|
|
|
|
|
|
**CcWriteCapability**:`save_container` / `do_upload` / `transfer` / `batch_transfer` / `hold_split` / `label` / `customer_message`,mode `live|mock|off`。
|
|
|
|
|
|
**ImapSettings** id=1:`poll_interval_ms` 默认 1800000(30min),合法 3min~7d。
|
|
|
|
|
|
**ImapFilterRule**:`kind` + `value`(关键词/白名单/黑名单等)。
|
|
|
|
|
|
**ImapPullLog**:每封拉取结果,最多约 1000 条。
|
|
|
|
|
|
**AppUser**:username + sha256 `password_hash` + role。**现行登录走 env,此表不参与鉴权**(隐式债务)。
|
|
|
|
|
|
### 5.2 邮件状态机(`state-machine.ts`)
|
|
|
|
|
|
| from | 允许 to |
|
|
|
|------|---------|
|
|
|
| FETCHED | PARSING, IGNORED |
|
|
|
| PARSING | PENDING_CONFIRM, PARSED, PARSE_FAILED, REJECTED_VALIDATION, AUTO_EXECUTING, FETCHED |
|
|
|
| PARSED | PARSING, PENDING_CONFIRM, AUTO_EXECUTING, IGNORED |
|
|
|
| PENDING_CONFIRM | IMPORTING, PARSING, IGNORED, PENDING_CONFIRM |
|
|
|
| IMPORTING | SUCCESS, PARTIAL_SUCCESS, FAILED |
|
|
|
| AUTO_EXECUTING | SUCCESS, FAILED, PARSED |
|
|
|
| SUCCESS | (终态) |
|
|
|
| PARTIAL_SUCCESS | IMPORTING |
|
|
|
| FAILED | IMPORTING, PENDING_CONFIRM, PARSING, AUTO_EXECUTING, IGNORED |
|
|
|
| PARSE_FAILED | PARSING, IGNORED |
|
|
|
| REJECTED_VALIDATION | PARSING, IGNORED, PENDING_CONFIRM |
|
|
|
| IGNORED | (终态) |
|
|
|
|
|
|
非法迁移抛 `IllegalTransition`。`IMPORTING`/`SUCCESS`/`PARTIAL_SUCCESS` 禁止重新解析。
|
|
|
|
|
|
`resolveAfterParse`:`NEW_CONTAINER` 且有效货件≥1 → `PENDING_CONFIRM`;`WORK_ORDER`/`DO_UPLOAD`/`TRANSFER` → `PENDING_CONFIRM`;`autoExecEligible` → `AUTO_EXECUTING`;否则 `PARSED`。
|
|
|
|
|
|
### 5.3 邮件类型 `MailType`(`types/mail.ts`)
|
|
|
|
|
|
`NEW_CONTAINER` | `TRANSFER` | `DO_UPLOAD` | `INSTRUCTION_HOLD_SPLIT` | `INSTRUCTION_LABEL` | `WORK_ORDER` | `UNKNOWN`。
|
|
|
|
|
|
分类在 `classify.ts`(主题/正文/附件名打分),不是 LLM。
|
|
|
|
|
|
### 5.4 主数据流
|
|
|
|
|
|
```
|
|
|
IMAP UID
|
|
|
→ pull-filter(黑名单拒绝 → 白名单必拉 → 关键词;皆空则 isBusinessRelevantMail)
|
|
|
→ snapshot 落 data/ + Prisma upsert(幂等)
|
|
|
→ 成功后 IMAP \Seen(失败且未入库则不标已读)
|
|
|
→ status FETCHED
|
|
|
→ ParsePipeline(Worker drain 或拉取后立即 parse)
|
|
|
→ parse_result + PENDING_CONFIRM / PARSED / ...
|
|
|
→ 运营打开 /mails/[id]/confirm
|
|
|
→ POST /api/mails/:id/import
|
|
|
ConfirmImport.execute
|
|
|
version 校验 → 柜冲突检查 → 状态 IMPORTING
|
|
|
→ saveContainer(CC 或 mock)
|
|
|
→ SUCCESS / 补偿 TIMEOUT_UNKNOWN
|
|
|
→ 浏览器列表刷新
|
|
|
```
|
|
|
|
|
|
前端:`useMails` → `GET /api/mails`;详情 `useMailDetail` → `GET /api/mails/:id`。无全局 store。
|
|
|
|
|
|
导入状态 `ImportStatus`:`PENDING|IMPORTING|SUCCESS|FAILED|CONFLICT|TIMEOUT_UNKNOWN`。
|
|
|
|
|
|
---
|
|
|
|
|
|
## 6. API 接口与页面路由
|
|
|
|
|
|
统一响应:`{ ok: true, data }` 或 `{ ok: false, error: { code, message, details? } }`。BigInt 转字符串。
|
|
|
|
|
|
权限:`public` / `session`(admin|ops)/ `admin`。`/api/test/*` 另需 `TEST_MODE` 否则 404。
|
|
|
|
|
|
### 6.1 认证与健康
|
|
|
|
|
|
| 方法 | 路径 | 权限 | 说明 |
|
|
|
|------|------|------|------|
|
|
|
| POST | `/api/auth/login` | public | 限流 10/分/IP+用户名;写 Cookie |
|
|
|
| POST | `/api/auth/logout` | Cookie | 销毁会话 |
|
|
|
| GET | `/api/auth/me` | session | 当前用户 |
|
|
|
| GET | `/api/health` | **public** | mysql、IMAP 告警、`cc_mode`/`cc_live_ready`、`test_mode`;勿塞密钥 |
|
|
|
|
|
|
### 6.2 邮件
|
|
|
|
|
|
| 方法 | 路径 | 权限 | 说明 |
|
|
|
|------|------|------|------|
|
|
|
| GET | `/api/mails` | session | 分页筛选 `mail_type`/`status`/`content_kind`/`q`/`sort` |
|
|
|
| DELETE | `/api/mails` | session | `ids` 或 `oldest` 批量删 |
|
|
|
| GET | `/api/mails/[id]` | session | 详情+解析+附件 |
|
|
|
| POST | `/api/mails/[id]/import` | session | ConfirmImport;10/分/用户;`force_skip_conflict` 仅 admin |
|
|
|
| POST | `/api/mails/[id]/reparse` | session | 重新解析 |
|
|
|
| POST | `/api/mails/[id]/type` | admin | `ENABLE_TYPE_OVERRIDE`;改 `mail_type` |
|
|
|
| POST | `/api/mails/[id]/ignore` | admin | → IGNORED |
|
|
|
| GET | `/api/mails/[id]/attachments/[attId]/download` | session | `resolveDataFile` 读盘 |
|
|
|
| POST | `/api/mails/[id]/ocr-attachments` | session | 触发附件 OCR |
|
|
|
| POST | `/api/mails/[id]/do/confirm` | session | DO 确认上传 |
|
|
|
| POST | `/api/mails/[id]/transfer/confirm` | session | 转仓确认 |
|
|
|
| GET | `/api/mails/[id]/transfer/gate` | session | 转仓门禁 |
|
|
|
| POST | `/api/mails/[id]/work-order/confirm` | session | 工单确认 |
|
|
|
|
|
|
### 6.3 导入 / 补偿 / 船司 / 拉取日志
|
|
|
|
|
|
| 方法 | 路径 | 权限 | 说明 |
|
|
|
|------|------|------|------|
|
|
|
| GET | `/api/imports` | session | 导入日志 |
|
|
|
| POST | `/api/imports/[importId]/retry` | session;部分 admin | 失败重试 |
|
|
|
| POST | `/api/compensations/[id]/retry` | session / admin 分支 | 补偿重试 |
|
|
|
| GET | `/api/shipping-lines` | session | CC `GetShippingLineList` |
|
|
|
| GET | `/api/imap-pull-logs` | session | 拉取记录 |
|
|
|
| POST | `/api/admin/imap/poll` | admin | 手动拉一轮,锁等待 20s |
|
|
|
|
|
|
### 6.4 设置(均 admin)
|
|
|
|
|
|
| 方法 | 路径 | 说明 |
|
|
|
|------|------|------|
|
|
|
| GET/PUT | `/api/settings/imap` | 拉取间隔 |
|
|
|
| GET/POST | `/api/settings/imap/filters` | 过滤规则 |
|
|
|
| PATCH/DELETE | `/api/settings/imap/filters/[id]` | 单条规则 |
|
|
|
| GET/POST | `/api/settings/mailboxes` | 邮箱列表/新增 |
|
|
|
| PATCH/DELETE | `/api/settings/mailboxes/[id]` | 改/删 |
|
|
|
| POST | `/api/settings/mailboxes/[id]/test` | 测连通 |
|
|
|
| POST | `/api/settings/mailboxes/probe` | 探测 |
|
|
|
| GET/PUT | `/api/settings/cc` | CC 连接(api_base 白名单) |
|
|
|
| POST | `/api/settings/cc/test` | CC 测试 |
|
|
|
| GET/PUT | `/api/settings/oauth` | OAuth 应用凭证 |
|
|
|
|
|
|
### 6.5 OAuth
|
|
|
|
|
|
| 方法 | 路径 | 权限 | 说明 |
|
|
|
|------|------|------|------|
|
|
|
| GET | `/api/oauth/providers` | (见源码) | 提供商是否配置 |
|
|
|
| GET/POST | `/api/oauth/start` | admin | 发起绑定,带 HMAC state |
|
|
|
| GET | `/api/oauth/callback/[provider]` | **无登录 Cookie** | 靠 state.actor;写 mailbox 后重定向 `/settings?oauth=` |
|
|
|
|
|
|
### 6.6 TEST_MODE(`requireTestMode`)
|
|
|
|
|
|
`/api/test/faults` `inject` `pipeline` `fixtures` `reset` `run-all` `target-requests`:注入样例、切故障、跑流水线。生产必须 `TEST_MODE=false`。
|
|
|
|
|
|
### 6.7 页面路由
|
|
|
|
|
|
| 路径 | 文件 | 说明 |
|
|
|
|------|------|------|
|
|
|
| `/` | `app/page.tsx` | 跳转 `/mails` |
|
|
|
| `/login` | `(auth)/login/page.tsx` | 登录 |
|
|
|
| `/mails` | `(ops)/mails/page.tsx` | 列表 |
|
|
|
| `/mails/[id]` | `(ops)/mails/[id]/page.tsx` | 详情 |
|
|
|
| `/mails/[id]/confirm` | `confirm/page.tsx` | 确认;query `instruction_id` |
|
|
|
| `/logs` | `(ops)/logs/page.tsx` | 日志 |
|
|
|
| `/settings` | `(ops)/settings/page.tsx` | Admin |
|
|
|
| `/test-workbench` | `(ops)/test-workbench/page.tsx` | 测试台 |
|
|
|
|
|
|
---
|
|
|
|
|
|
## 7. 关键业务流程
|
|
|
|
|
|
### 7.1 登录
|
|
|
|
|
|
1. `POST /api/auth/login` `{username, password}`
|
|
|
2. `checkSlidingWindow` → `verifyCredentials` 比对 `APP_ADMIN_*` / `APP_OPS_*`
|
|
|
3. iron-session 写入 `username`+`role`
|
|
|
4. 客户端进 `(ops)`,`AuthSessionProvider` 调 `/api/auth/me`
|
|
|
|
|
|
状态:未登录只能 `/login`;Cookie `HttpOnly`,生产 `secure`。
|
|
|
|
|
|
### 7.2 IMAP 拉取 → 解析(Worker)
|
|
|
|
|
|
```
|
|
|
Idle 或 定时 tick
|
|
|
→ withMysqlNamedLock("imap_poll")
|
|
|
→ resolveEnabledMailboxes(DB 优先,否则 .env IMAP_*)
|
|
|
→ SEARCH SINCE lookback(默认 3 天,已读+未读)
|
|
|
→ 每 UID:decidePull → 跳过则 stub + pull_log
|
|
|
→ FETCH + parseMailSource + saveMailSnapshot
|
|
|
→ upsert mail_message(P2002 幂等)
|
|
|
→ ParsePipeline / 或 FETCHED 待 drain
|
|
|
→ markSeen
|
|
|
```
|
|
|
|
|
|
关键函数:`ImapPoller.tick`、`pollMailbox`、`ParsePipeline`(`pipeline.ts`)、`drainFetchedMails`。
|
|
|
|
|
|
### 7.3 新增预报确认导入(核心交易)
|
|
|
|
|
|
```
|
|
|
运营:有效货件 + PENDING_CONFIRM
|
|
|
→ /mails/[id]/confirm → ForecastConfirmView
|
|
|
→ POST /api/mails/:id/import
|
|
|
version + idempotency_key + F_TransMode + F_OperationType
|
|
|
+ container_header 白名单 + selected_row_indexes
|
|
|
→ ConfirmImport.execute(import/confirm.ts)
|
|
|
assertTransition → IMPORTING
|
|
|
checkContainerConflict / acquireContainerActiveLock
|
|
|
buildSaveContainerEntity → saveContainer
|
|
|
live: ccAuth + POST /Container/SaveContainer
|
|
|
mock: mockRegisterContainer
|
|
|
成功 SUCCESS + external_id
|
|
|
超时 TIMEOUT_UNKNOWN → import_compensation
|
|
|
```
|
|
|
|
|
|
Worker **禁止**走这条 SaveContainer。门禁:`forecast-confirm-gate.ts` / `canEnterForecastConfirm`。
|
|
|
|
|
|
渠道未映射须 `ack_unmapped_channels`。强制跳冲突:Admin + `ENABLE_FORCE_IMPORT` + 审计。
|
|
|
|
|
|
### 7.4 DO / 转仓 / 工单
|
|
|
|
|
|
确认页按 `mail_type` 与 `instruction_id` 分发:
|
|
|
|
|
|
- DO → `confirm-do-upload.ts` → `cc/do-upload.ts`(`SaveFieldValue` + `/learun/adms/annexes/upload`)
|
|
|
- 转仓 → `confirm-batch-transfer.ts` / `batch-transfer.ts`(默认 capability **mock**)
|
|
|
- 工单 → `confirm-work-order.ts`
|
|
|
|
|
|
自动执行:`auto-exec.ts` + `ENABLE_AUTO_EXEC_*`;新增预报仍以人工确认为准。
|
|
|
|
|
|
### 7.5 补偿与超时回收
|
|
|
|
|
|
- `IMPORTING` 超过 `IMPORTING_TIMEOUT_MS`(默认 180s)→ reaper 打回
|
|
|
- `PARSING` 超过 `PARSING_STALE_MS`(默认 600s)→ 回 FETCHED
|
|
|
- 补偿 due:`processDueCompensations` 最多 `COMPENSATION_MAX_RETRY=3`
|
|
|
- IMAP 连续失败 ≥3 且配置了 `ALERT_WEBHOOK_URL` → POST JSON(15 分钟去重)
|
|
|
|
|
|
---
|
|
|
|
|
|
## 8. 配置与环境管理
|
|
|
|
|
|
### 8.1 切换方式
|
|
|
|
|
|
无独立 `config/dev.yml`。环境 = `.env` + 设置页 DB(IMAP/CC/OAuth **DB 优先于 env**)。
|
|
|
|
|
|
| 场景 | 做法 |
|
|
|
|------|------|
|
|
|
| 本地 | `.env` 指向 `localhost:7023`;`CC_MOCK=true` |
|
|
|
| Docker | compose 覆盖 `DATABASE_URL=@mysql:3306` |
|
|
|
| 生产 | `NODE_ENV=production` + 强密钥;弱口令 **拒绝启动**(`env.ts` `enforceProductionSecrets`) |
|
|
|
| TEST | `TEST_MODE=1`;与 `CC_MOCK` 正交 |
|
|
|
|
|
|
换 `SESSION_SECRET`:全员登出,且 **邮箱密码密文无法解密,须重新保存**。
|
|
|
|
|
|
### 8.2 环境变量(`getEnv` / `.env.example`)
|
|
|
|
|
|
| 键 | 含义 |
|
|
|
|----|------|
|
|
|
| `DATABASE_URL` | Prisma MySQL |
|
|
|
| `POLL_INTERVAL_MS` | Worker 间隔回退;设置页优先 |
|
|
|
| `DATA_RETENTION_DAYS` | 保留天数;Worker 不再自动删,用 `pnpm retention:cleanup` |
|
|
|
| `ENABLE_TYPE_OVERRIDE` | Admin 改邮件类型 |
|
|
|
| `ENABLE_FORCE_IMPORT` | 强制跳过柜冲突 |
|
|
|
| `ENABLE_ISO_CHECK` | 柜号 ISO6346 |
|
|
|
| `IMAP_*` | 主机/端口/用户/超时/lookback/每轮上限/IDLE/代理 |
|
|
|
| `CC_API_BASE` `CC_SAAS_HEADER` `CC_LOGIN_MARK` | SaaS |
|
|
|
| `CC_USERNAME` `CC_PASSWORD_PLAIN` `CC_PASSWORD_MD5` | 回退凭证 |
|
|
|
| `CC_MOCK` | 全局写 mock |
|
|
|
| `CC_HTTP_TIMEOUT_MS` `CC_READ_TIMEOUT_MS` | 写/读超时 |
|
|
|
| `APP_ADMIN_*` `APP_OPS_*` | **实际登录账号** |
|
|
|
| `SESSION_SECRET` | ≥32;会话 + AES 派生 |
|
|
|
| `TEST_MODE` | Mock IMAP/CC 故障注入 |
|
|
|
| `ALERT_WEBHOOK_URL` | 告警 |
|
|
|
| `OCR_PROVIDER` | `off\|local\|aliyun` |
|
|
|
| `OAUTH_*` | Gmail/MS 应用 |
|
|
|
| `ENABLE_AUTO_EXEC_*` `AUTO_EXEC_REQUIRE_CONFIRM` | 自动执行开关 |
|
|
|
| `SHIPMENT_ROW_SOFT/HARD_LIMIT` `ATTACHMENT_MAX_BYTES` | 清单/附件上限 |
|
|
|
|
|
|
### 8.3 端口约定(红线)
|
|
|
|
|
|
Web **3100**;MySQL 对外 **7023**。不要改成 3306/3000 当本地默认。
|
|
|
|
|
|
---
|
|
|
|
|
|
## 9. 构建、测试与部署
|
|
|
|
|
|
### 9.1 脚本(`package.json`)
|
|
|
|
|
|
| 脚本 | 作用 |
|
|
|
|------|------|
|
|
|
| `dev` / `dev:stack` | web+worker |
|
|
|
| `dev:web` / `dev:turbo` | 仅 Next |
|
|
|
| `build` | `next build` + `tsc -p tsconfig.worker.json` |
|
|
|
| `start` | `next start -p 3100` |
|
|
|
| `worker` | `tsx src/worker/index.ts` |
|
|
|
| `lint` | `next lint` |
|
|
|
| `test` / `test:coverage` / `test:all` | vitest |
|
|
|
| `test:e2e` / `test:e2e:test-mode` | Playwright |
|
|
|
| `db:*` | generate / migrate / seed / push |
|
|
|
| `cc:smoke` `imap:smoke` `imap:poll` | 联调 |
|
|
|
| `compose:*` | docker |
|
|
|
| `retention:cleanup` | 清旧数据 |
|
|
|
| `postinstall` | `prisma generate` |
|
|
|
|
|
|
### 9.2 日常命令
|
|
|
|
|
|
```bash
|
|
|
pnpm install
|
|
|
cp .env.example .env
|
|
|
docker compose up -d mysql
|
|
|
pnpm exec prisma db push
|
|
|
pnpm db:seed
|
|
|
pnpm dev # http://localhost:3100 admin/admin123
|
|
|
pnpm test
|
|
|
pnpm test:e2e # 需已起服务或交给 webServer 拉 pnpm dev
|
|
|
pnpm build && pnpm start
|
|
|
```
|
|
|
|
|
|
Windows:`.\docs\start-system.ps1`(`-Mode compose` 需 Docker Hub)。
|
|
|
|
|
|
### 9.3 部署要点
|
|
|
|
|
|
- 必须 **web + worker** 都活;worker 建议**单副本**(全局 `imap_poll` 锁)
|
|
|
- 持久化:MySQL 卷 + `./data`(eml、附件、`imap-runtime.json`)
|
|
|
- 生产:`pnpm db:migrate`(`migrate deploy`),不要用弱口令 seed 覆盖生产
|
|
|
- 健康检查:`GET /api/health`
|
|
|
- compose worker 命令必须是 `tsx src/worker/index.ts`(dist 里 `@/` 别名会 MODULE_NOT_FOUND)
|
|
|
|
|
|
测试目录:`src/**/*.test.ts`、`tests/unit`、`tests/integration/pipeline-fixtures.test.ts`、`tests/e2e`。覆盖率排除 `src/app` 与 `src/components`。
|
|
|
|
|
|
---
|
|
|
|
|
|
## 10. 代码规范与约定
|
|
|
|
|
|
来源:`docs/邮箱项目.md` §4–5、源码实践。
|
|
|
|
|
|
| 项 | 约定 |
|
|
|
|----|------|
|
|
|
| 语言 | TypeScript strict;API 入参 Zod |
|
|
|
| 命名 | 文件 kebab-case;函数 camelCase;Prisma model PascalCase;表 snake_case |
|
|
|
| 路径别名 | `@/` = `src/` |
|
|
|
| API | `ok`/`fail`;错误码大写下划线(`UNAUTHORIZED`、`VERSION_CONFLICT`) |
|
|
|
| 日志 | pino 对象字段 + 短消息(`worker.imap_tick`);禁止打完整密码/token |
|
|
|
| UI 文案 | `src/constants/ui-copy.ts`;中文 |
|
|
|
| CC 柜头覆盖 | 仅 `pickContainerHeaderPatch` 白名单 |
|
|
|
| 读盘 | 只走 `resolveDataFile` / `assertUnderRoot` |
|
|
|
| 锁 | 只走 `db-lock.ts` |
|
|
|
| 状态 | 只走 `assertTransition` |
|
|
|
| 包管理 | 仅 pnpm |
|
|
|
| 提交 | 用户明确要求才 git commit |
|
|
|
| Lint | `next lint`;无 Prettier/Ruff 工程配置 |
|
|
|
| 破坏性变更 | 先改 `docx` 再改代码 |
|
|
|
|
|
|
角色:ops 列表/详情/确认/重解析/下载;admin 另含改类型、设置、立即拉取、忽略、FORCE 导入。
|
|
|
|
|
|
---
|
|
|
|
|
|
## 11. 现有问题与注意事项
|
|
|
|
|
|
源码几乎无 `TODO/FIXME/HACK` 标记。已知项来自 README、协作文档与代码对照。
|
|
|
|
|
|
| ID | 说明 |
|
|
|
|----|------|
|
|
|
| AUTH-TABLE | `app_user` 被 seed,登录却只认 env。改密码易误改库表无效 |
|
|
|
| MOCK≠TEST | `CC_MOCK` 或设置页 Mock 的成功 **不算** 上线验收 |
|
|
|
| CAP-MOCK | `transfer`/`hold_split`/`label`/`customer_message` 默认 mock,V1.73 无正式表 |
|
|
|
| OCR-LOCAL | `OCR_PROVIDER=local` 是启发式,不是商用 OCR;aliyun 路径未当正式 SDK 宣传 |
|
|
|
| RATE-LIMIT | 限流是进程内 Map,多副本不共享 |
|
|
|
| LOCK | Prisma 池 GET_LOCK 会泄漏 → 永久 `lock_busy`;用 `scripts/clear-imap-lock.ts` |
|
|
|
| UNIT-DRIFT | README:`mail-intent` / `extract-header` / `four-business` / `work-order-dedupe` 与 expected 不完全一致 |
|
|
|
| COMP-SCOPE | 工单/DO/转仓 5xx 未统一进预报补偿表,靠详情一键重试 |
|
|
|
| DOCKER-PORT | Dockerfile EXPOSE 3000 vs 应用 3100 |
|
|
|
| HEALTH-PUBLIC | `/api/health` 无鉴权(docker 依赖) |
|
|
|
| FILTER | 关键字未命中 `IGNORED`/`SKIP_FILTER` 不进列表;`UNKNOWN` 仍可能 `PARSED` 供人工处理 |
|
|
|
| PARTIAL-CC | CC 对象有 data 但仍可能 `TIMEOUT_UNKNOWN` 进补偿(缺 external id) |
|
|
|
|
|
|
高耦合:`ParsePipeline` 体量大(分类+xlsx+zip+OCR+船司匹配+指令拆分);`ConfirmImport` 与 CC 字段、状态机、锁强绑定。
|
|
|
|
|
|
风险区:`SESSION_SECRET` 兼会话与邮箱密文;worker 多开抢锁;附件路径穿越;CC `api_base` SSRF(已有白名单)。
|
|
|
|
|
|
红线(事故级,见 `docs/邮箱项目.md` §5):Worker 禁止新增预报 SaveContainer;不绕过状态机;不用 Prisma 池 GET_LOCK;mock 不当验收;zip 只解一层;冲突默认阻断;先落库再 `\Seen`;端口 7023/3100;密钥不入库。
|
|
|
|
|
|
---
|
|
|
|
|
|
## 12. 外部集成
|
|
|
|
|
|
### 12.1 CarrierCentral SaaS(核心)
|
|
|
|
|
|
| 项 | 位置 |
|
|
|
|----|------|
|
|
|
| 基址 | `CC_API_BASE` 或 `cc_settings.api_base`(默认 `https://test.saas.carriercentral.vip/api`) |
|
|
|
| 头 | `Saas`、登录 token(`cc/auth.ts` `customerLogin`) |
|
|
|
| 契约 | `docx/接口/carriercentral客户端通用接口V1.73.md` |
|
|
|
| 登录 | `customerLogin`;token 缓存 `cc_token_cache` |
|
|
|
| 预报写入 | `POST /Container/SaveContainer`(`save-container.ts`) |
|
|
|
| 船司 | `GET GetShippingLineList`(`shipping-line.ts`) |
|
|
|
| DO | `SaveFieldValue` + `/learun/adms/annexes/upload` |
|
|
|
| Mock | `cc/mock-target-api.ts` `mock-state.ts`;设置页或 `CC_MOCK` |
|
|
|
| 主机约束 | `lib/cc-api-base.ts`:`*.carriercentral.vip` / localhost / 与 env 同主机 |
|
|
|
|
|
|
CC 密码协议为 MD5;设置页明文则服务端哈希。Token 可能出现在 GET query,日志须脱敏。
|
|
|
|
|
|
### 12.2 IMAP 邮箱
|
|
|
|
|
|
| 项 | 位置 |
|
|
|
|----|------|
|
|
|
| 协议 | IMAPS 993(预设 `lib/imap-presets.ts`:QQ/163/Gmail/Outlook) |
|
|
|
| 客户端 | `imapflow`(`imap/client.ts`) |
|
|
|
| 凭证 | 设置页 AES 密文,或 `.env IMAP_USER/PASS` |
|
|
|
| OAuth | Google / Microsoft(`oauth/providers.ts`);回调写 `mailbox_account` |
|
|
|
| 代理 | `IMAP_PROXY`;默认仅 Gmail/Outlook 主机走代理 |
|
|
|
|
|
|
### 12.3 OCR
|
|
|
|
|
|
`OCR_PROVIDER`:`off` | `local`(tesseract.js)| `aliyun`(endpoint/ak 环境变量)。解析辅助,不是主识别。
|
|
|
|
|
|
### 12.4 告警 Webhook
|
|
|
|
|
|
`ALERT_WEBHOOK_URL`:IMAP 连续失败或 CC 鉴权失败 POST JSON。空则只打日志。实现 `services/alert/webhook.ts`。
|
|
|
|
|
|
### 12.5 未集成
|
|
|
|
|
|
支付、短信、邮件 SMTP 发送、对象存储 SDK、LangChain、微信客服。附件只落本地 `data/`。
|
|
|
|
|
|
---
|
|
|
|
|
|
## 附录 A. 限制常量
|
|
|
|
|
|
| 常量 | 默认 | 位置 |
|
|
|
|------|------|------|
|
|
|
| 拉取间隔 | 30min(设置页) | `imap_settings` / `POLL_INTERVAL_MS` |
|
|
|
| lookback | 3 天 | `IMAP_LOOKBACK_DAYS` |
|
|
|
| 每箱每轮最多新信 | 100 | `IMAP_MAX_FETCH_PER_TICK` |
|
|
|
| 解析过期 | 600s | `PARSING_STALE_MS` |
|
|
|
| 导入超时 | 180s | `IMPORTING_TIMEOUT_MS` |
|
|
|
| 补偿次数 | 3 | `COMPENSATION_MAX_RETRY` |
|
|
|
| 货件行软/硬上限 | 1000 / 5000 | env |
|
|
|
| 附件 | 20MiB | `ATTACHMENT_MAX_BYTES` |
|
|
|
| 登录/导入限流 | 10/分钟 | login / import route |
|
|
|
| pull_log | ~1000 | `imap/pull-log.ts` |
|
|
|
| 间隔合法范围 | 3min~7d | 设置页 IMAP |
|
|
|
|
|
|
---
|
|
|
|
|
|
## 附录 B. 与助手仓对照(迁移功能时用)
|
|
|
|
|
|
| 维度 | yx(本文) | carriercentral-ai-assistant |
|
|
|
|------|------------|------------------------------|
|
|
|
| 形态 | Web 运营后台 | Windows Tkinter Demo |
|
|
|
| 主路径 | IMAP → 解析 → 确认 → SaveContainer | 自然语言 → ReAct → GET intelligent |
|
|
|
| CC 写 | 有(确认后) | 查询为主,附件下载 |
|
|
|
| 状态机 | 邮件 12 态 | 无邮件态 |
|
|
|
| 鉴权 | admin/ops Cookie | 无登录 |
|
|
|
| 禁止 | Worker 写新增预报 | 改 kf-ai、伪装客服 |
|
|
|
|
|
|
迁功能原则:复用 **CC 契约与字段名**(`F_ContainerNo`、`SaveContainer`),不要把 Next/Prisma/IMAP 整包塞进助手;UI 保持助手现有 `ui_main.py` 分层,除非用户确认换栈。
|
|
|
|
|
|
---
|
|
|
|
|
|
## 附录 C. 后续开发引用协议
|
|
|
|
|
|
之后任何需求(含「把 yx 功能加到助手仓」):
|
|
|
|
|
|
1. 先引用本文的**模块路径与函数名**,再给修改方案。
|
|
|
2. 遵循现有分层、Zod 入参、状态机、pnpm、中文 `ui-copy`。
|
|
|
3. 未经明确允许,不引入 Redis/新 ORM/第二套前端框架。
|
|
|
4. Worker 与 ConfirmImport 职责拆分不破。
|
|
|
5. 行为变更同步 `docx` 与本文对应章节;协作入口 `docs/邮箱项目.md` 红线保持。
|