12 KiB
阿里国际站询盘数据采集与管理系统 · 项目介绍
基于 2026-08-27 代码现状整理(扩展
d:\alibb+ 服务端d:\alibb-server)。 部署细节见 DEPLOY-ALIBB.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 | — | MV3 清单:权限 storage/activeTab/alarms/unlimitedStorage/scripting;host 覆盖 message/alicrm/onetalk.alibaba.com;optional_host_permissions 运行时授权任意后端地址 |
| content.js | ~1300 行 | 核心:注入 message.alibaba.com 的抓取状态机 |
| background.js | 145 行 | Service Worker:推送队列调度 + 本地数据汇聚(双写) |
| push/queue.js | — | IndexedDB 持久化队列(库 inquiry_push) |
| push/worker.js | — | 队列消费:POST /api/ingest,指数退避,8 次后标 failed |
| push/payload.js | — | 页面数据 → 后端 zod schema 的字段适配层 |
| push/config.js | — | 推送配置(chrome.storage.local) |
| popup.html / popup.js | — | 操作界面:抓取控制 + 后端推送配置 |
| tests/ | 5 个文件 | vitest 单测(config/queue/payload/sync/worker,fake-indexeddb) |
| scripts/ | — | e2e-push.mjs(推送端到端)、verify-chat-range.mjs、verify-risk-detect.mjs |
| 使用手册.html | — | 面向操作者的图文五步指南 |
2.2 抓取状态机与断点续抓
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)。
- API 直拉主路径:
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 幂等核心(亮点)
- 同客户合并:同一 customerId 只保留一条"代表询盘"(最早创建),后续询盘追加进
allInquiriesJSON 数组,消息统一归入代表 —— 不产生冗余行; - 消息级幂等:按
message_id唯一约束createMany skipDuplicates,500 条/批,单批失败只计数不回滚(has_failed_batches标记,下次推送强制重插); - 早返回优化:"消息数 + 最大 sendTime + 无失败批次" 三项指纹未变 → 跳过批量插入直接返回,避免重复重写几千条消息;
- 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):
- 阿里云 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/。
6. 已知限制
- 客户资料折叠面板字段抓不到(阿里校验
event.isTrusted只认真实鼠标点击),好在多数买家本就是 "-"; - 公网明文 HTTP(23010),内网/测试可接受,长期公网建议套域名 + HTTPS;
- 数据时效 = 上次手动抓取快照,需浏览器保持阿里页打开;
- 同 messageId 内容被上游修订时早返回会跳过更新(MVP 接受,重推主场景是新增消息)。