# 阿里国际站询盘数据采集与管理系统 · 项目介绍 > 基于 2026-08-27 代码现状整理(扩展 `d:\alibb` + 服务端 `d:\alibb-server`)。 > 部署细节见 [DEPLOY-ALIBB.md](DEPLOY-ALIBB.md),后端设计见 [superpowers/specs/2026-07-13-alibaba-inquiry-backend-design.md](superpowers/specs/2026-07-13-alibaba-inquiry-backend-design.md)。 ## 1. 项目概述 本项目是一套**阿里国际站(alibaba.com)询盘数据的采集与管理系统**,由三个部分组成: | 组成部分 | 位置 | 技术 | 角色 | |---|---|---|---| | **采集端** Chrome 扩展 | `d:\alibb`(本仓库) | Chrome MV3、原生 JS | 抓取询盘列表、客户资料、行为数据、聊天记录 | | **服务端** 后端 API | `d:\alibb-server\backend` | Node.js + Express + TypeScript + Prisma + MySQL | 接收推送、幂等入库、提供查询/统计/导出 API | | **服务端** 管理后台 | `d:\alibb-server\frontend` | Vue3 + Element Plus + Pinia + ECharts | 询盘列表/详情/聊天查看/统计报表 | 核心链路: ``` 浏览器(登录阿里主账号) Chrome 扩展 content.js ├─ 列表抓取(DOM 翻页)→ 详情抓取(DOM)→ 聊天抓取(阿里内部 API 直拉) ├─ 双写 A(本地):chrome.storage 汇聚 + customerId 去重 → CSV 导出 └─ 双写 B(推送):IndexedDB 队列 → alarm 每分钟消费 → POST /api/ingest(Bearer Token) ↓ 失败指数退避(≤8 次),成功出队 服务器(阿里云 ECS,1Panel + OpenResty + MySQL 8.4,:23010) 后端 zod 校验 → 幂等入库(同客户合并 / 消息级去重 / 早返回) ↓ 管理员浏览器 → Vue3 后台(JWT 登录):列表筛选 / 聊天详情 / 统计 / CSV 导出 ``` 使用模式:主管理员账号一人操作扩展手动触发抓取(非实时同步),后台数据 = 最近一次抓取的快照;询盘记录的 `manager` 字段天然区分业务员,无需多人登录。 --- ## 2. 采集端:Chrome MV3 扩展(本仓库) ### 2.1 文件结构 | 文件 | 行数/大小 | 职责 | |---|---|---| | [manifest.json](../manifest.json) | — | MV3 清单:权限 `storage/activeTab/alarms/unlimitedStorage/scripting`;host 覆盖 `message/alicrm/onetalk.alibaba.com`;`optional_host_permissions` 运行时授权任意后端地址 | | [content.js](../content.js) | ~1300 行 | 核心:注入 `message.alibaba.com` 的抓取状态机 | | [background.js](../background.js) | 145 行 | Service Worker:推送队列调度 + 本地数据汇聚(双写) | | [push/queue.js](../push/queue.js) | — | IndexedDB 持久化队列(库 `inquiry_push`) | | [push/worker.js](../push/worker.js) | — | 队列消费:POST `/api/ingest`,指数退避,8 次后标 failed | | [push/payload.js](../push/payload.js) | — | 页面数据 → 后端 zod schema 的字段适配层 | | [push/config.js](../push/config.js) | — | 推送配置(`chrome.storage.local`) | | [popup.html](../popup.html) / [popup.js](../popup.js) | — | 操作界面:抓取控制 + 后端推送配置 | | [tests/](../tests/) | 5 个文件 | vitest 单测(config/queue/payload/sync/worker,fake-indexeddb) | | [scripts/](../scripts/) | — | `e2e-push.mjs`(推送端到端)、`verify-chat-range.mjs`、`verify-risk-detect.mjs` | | [使用手册.html](../使用手册.html) | — | 面向操作者的图文五步指南 | ### 2.2 抓取状态机与断点续抓 [content.js](../content.js) 的核心是**跨页面持久化的状态机**:`state.phase ∈ idle | list | detail | paused | done`,存储于 `chrome.storage.local`(键 `alibaba_scraper_state`)。每跳转一个详情页 content script 就被销毁重建,靠恢复 `queueIndex` 继续队列 —— 整轮抓取可跨导航、可暂停恢复、可扩展重载续跑。 抓取编排由 `processCurrentDetailPage()` 完成:等待渲染 → 聊天抓取(API 优先)→ 详情提取 → **双写**(本地 `storeDetail` + 推送 `enqueue`)→ 跳下一条。 ### 2.3 数据抓取能力 - **列表页**:DOM 解析 `a.inquiry-item`(询价单号/时间/买家/国家/消息预览/负责人/状态等 13 字段),100 条/页,自动翻页; - **详情页**:客户资料(邮箱/电话/公司/注册时间等)、买家标签(8 词表)、买家特征(12 词表)、近 90 天行为数据(产品浏览/有效询盘/RFQ/登录天数/垃圾询盘/黑名单数)、店内行为与最近询盘产品; - **聊天记录**(最复杂模块): - **API 直拉主路径**:`querySummary.htm` 拿会话加密参数 → `getOpMessages.htm` 游标循环翻页(复用页面 cookie 凭据),单询盘实测 8000+ 条;实测全量 4576 询盘 + 68058 条消息零风控; - **DOM 滚动回退**:API 失败时模拟 wheel 事件滚动 `.common-load-more` 懒加载; - **同客户去重**:同买家多询盘共享同一会话,只抓首个(`seenChatCustomer`); - **消息归一化**(`apiMsgToChat`):方向(incoming/outgoing/system)、富媒体(`img:`/`file:`/`video:` 前缀 + clouddisk 直链构造)、翻译(嵌套 JSON 双重 parse)。 ### 2.4 反风控设计(贯穿全文件) | 机制 | 说明 | |---|---| | 随机时延 | 翻页 1.2–3s、聊天每轮 2–5s、每 15 轮插入 30–90s"模拟走神" | | 作息时段 | 默认 8–12 / 14–18 点工作,会话级 ±30 分钟抖动,非工作时段自动挂起 | | 风控检测 | `RISK_KEYWORDS`(验证码/captcha/blocked 等)命中 → 暂停 + popup 红色横幅报警;只浅扫 API 响应顶层字段,不扫消息正文防误判 | | 跳过策略 | 注销/管控账号(`SKIP_KEYWORDS`)直接跳过不暂停 | | 增量水位 | 按 inquiryId 记录已抓最新消息(`lastTopMsg_*`),重抓只拉增量,只前进不倒退 | | 时间范围 | 近 1/2/3 月或自定义起止;`until` 跳游标直接从截止时间往回翻,大幅减少请求量 —— 同时是风控减压手段 | ### 2.5 推送链路(可靠性) ``` content.js 抓完一条 → sendMessage 'enqueue' → SW 写 IndexedDB(不丢) → 立即 + alarm 'push-tick'(每 1 分钟)触发 processQueue() → 每批 3 条,POST {serverUrl}/api/ingest(Bearer Token) → 成功标 sent;失败指数退避(2^n 秒,封顶 1h),8 次后标 failed(popup 红字提示) ``` 关键设计:worker **每次自己 `loadConfig()`**(无状态),因为 MV3 SW 休眠重启会清空模块级变量 —— 缓存配置会导致永不推送。 ### 2.6 popup 界面 抓取模式(仅列表/列表+详情)、条数(全部/前10/前50)、customerId 去重开关、聊天上限、**聊天时间范围**(全部/近1-3月/自定义日期)、后端推送设置(服务器 URL + Token + 授权域名 + 启用开关,默认指向生产 `http://219.157.255.213:23010`)、实时推送状态条(已推/待推/失败,2s 刷新)、开始/暂停/恢复/导出 CSV/清空数据。 --- ## 3. 服务端后端(d:\alibb-server\backend) ### 3.1 技术栈与结构 Express 4 + TypeScript + Prisma 5 + MySQL 8,zod 校验,Docker 两阶段构建(node:20-slim),容器启动自动 `prisma migrate deploy`。目录:`src/{routes,services,middleware,utils,config,lib}` + `prisma/` + `tests/`(vitest + testcontainers 真库集成测试)。 ### 3.2 API 清单 | 方法 + 路径 | 鉴权 | 功能 | |---|---|---| | `GET /api/health` | 无 | 探活(DB 连通性) | | `POST /api/ingest/` | Ingest Token + 60次/min 限流 | 扩展推送入口,幂等入库 | | `POST /api/auth/login` / `refresh` | 无 / JWT | 管理员登录(bcrypt)、续签 | | `GET /api/inquiries` | JWT | 列表:分页 + 国家/业务员/状态/时间筛选 + 关键词搜索 + 排序 | | `GET /api/inquiries/:id` | JWT | 详情 + 客户 + 行为 + 消息数 | | `GET /api/inquiries/:id/messages` | JWT | 聊天游标分页(复合键 `(sendTime, messageId)`,微信式向上翻页) | | `GET /api/inquiries/:id/messages/export` | JWT | 单询盘聊天 CSV 流式导出(预留) | | `GET /api/stats/overview` | JWT | 总数 / 国家 Top10 / 业务员分布 / 近 12 月趋势 | | `GET /api/export` | JWT | 询盘列表 CSV 导出(21 列中文表头,与列表筛选一致) | ### 3.3 ingest 幂等核心(亮点) 1. **同客户合并**:同一 customerId 只保留一条"代表询盘"(最早创建),后续询盘追加进 `allInquiries` JSON 数组,消息统一归入代表 —— 不产生冗余行; 2. **消息级幂等**:按 `message_id` 唯一约束 `createMany skipDuplicates`,500 条/批,单批失败只计数不回滚(`has_failed_batches` 标记,下次推送强制重插); 3. **早返回优化**:"消息数 + 最大 sendTime + 无失败批次" 三项指纹未变 → 跳过批量插入直接返回,避免重复重写几千条消息; 4. customer upsert 用 `skipNulls`,本次没抓到的字段不覆盖已落库值。 ### 3.4 数据模型(4 张表) - **customers**:`customer_id` 唯一;资料 + 行为七字段(可空,区分"无数据"与"为 0")+ 店内行为 JSON 列; - **inquiries**:`inquiry_id` 唯一;`all_inquiries`(同客户合并)、`messages_count`/`last_send_time`(幂等指纹)、`has_failed_batches`; - **chat_messages**:`message_id` 唯一(幂等键);`content MEDIUMTEXT`;复合索引 `(inquiry_id, send_time, message_id)` 恰好覆盖游标分页; - **users**:管理员(bcrypt,空表时按环境变量播种)。 ### 3.5 鉴权 两套并行:扩展 → 静态 `INGEST_TOKEN`(`timingSafeEqual` 常量时间比较防时序攻击);后台 → JWT(HS256,7 天,登录签发)。 --- ## 4. 管理后台前端(d:\alibb-server\frontend) ### 4.1 页面 | 路由 | 页面 | 功能要点 | |---|---|---| | `/login` | 登录 | JWT + open-redirect 防护 | | `/inquiries` | 询盘列表 | 筛选栏(国家/业务员/状态/时间范围/搜索/排序)、表格分页、**CSV 导出复用当前筛选**;列表状态存 sessionStorage,进详情返回后恢复 | | `/inquiries/:id` | 询盘详情 | 客户信息卡 + 行为数据卡(复刻阿里详情页行为区)+ **同会话多询盘列表** + 完整聊天 | | `/stats` | 统计 | 数字卡(总数/本月新增)+ ECharts:国家 Top10 环形图、业务员柱状图、12 月趋势折线 | ### 4.2 聊天组件(核心体验) - **虚拟滚动**(vue-virtual-scroller):几千条消息流畅渲染,可变高度气泡自动测量; - **游标分页**:首屏拉最新一页自动滚到底,顶部按钮向上加载历史,prepend 后保持视觉位置不跳动; - **微信式气泡**:incoming 左 / outgoing 右 / system 居中;消息类型协议(`img:`/`file:`/`video:`/纯链接/纯文本); - **XSS 防护**:文本一律插值不用 v-html;富媒体 URL 走 alibaba 域名白名单,纯 URL 只许 http/https。 ### 4.3 工程细节 axios 封装(401 全局跳登录、错误脱敏不含 token)、Pinia auth store(JWT 存 localStorage,MVP 可接受)、路由守卫、全套 vitest 单测、双 lint(oxlint + eslint)。 --- ## 5. 部署与运维 生产环境(详见 [DEPLOY-ALIBB.md](DEPLOY-ALIBB.md)): - 阿里云 ECS `219.157.255.213`(Ubuntu 26.04),对外端口 **23010**; - 1Panel 面板管理:OpenResty(host 网络,站点 `alibaba`)→ SPA 静态 + `/api/*` 反代 → `alibb-backend` 容器(127.0.0.1:8090)→ 1Panel MySQL 8.4(库 `alibaba`,utf8mb4,仅本机可达); - `client_max_body_size 50m` 配合大聊天 payload;备份:mysqldump → 本机 `D:\alibb-server\backups\`(权威)+ 服务器副本; - 三份代码位置:扩展 `d:\alibb`(master)、后端 `d:\alibb-server\backend`(master)、前端 `d:\alibb-server\frontend`(main);服务器重装恢复 runbook 见 [superpowers/plans/](superpowers/plans/)。 ## 6. 已知限制 - 客户资料**折叠面板字段抓不到**(阿里校验 `event.isTrusted` 只认真实鼠标点击),好在多数买家本就是 "-"; - 公网明文 HTTP(23010),内网/测试可接受,长期公网建议套域名 + HTTPS; - 数据时效 = 上次手动抓取快照,需浏览器保持阿里页打开; - 同 messageId 内容被上游修订时早返回会跳过更新(MVP 接受,重推主场景是新增消息)。