diff --git a/.gitignore b/.gitignore index 55e3fff..e86ab8b 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,4 @@ app/assets/ app/new_web_source app/user_data/ OPS_REDIS_MYSQL_OPTIMIZATION_NOTES.md +.omx/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..65ac965 --- /dev/null +++ b/README.md @@ -0,0 +1,382 @@ +# crawler-plugin + +## 这个仓库是什么 + +这个仓库不是一个“单体项目”,而是一个混合工作目录。当前更像是以下三层共同组成的一套系统: + +- `app/`:Python 桌面宿主、Flask 页面承载层、`pywebview` 本地桥接层、自动化任务入口 +- `backend-java/`:Java 业务后端,负责任务模型、文件处理、进度缓存、结果组装、落库、下载 +- `frontend-vue/`:Vue 多页面前端,负责工具页交互和任务发起 + +如果你是第一次接手这个仓库,不要先把它理解成: + +- 纯 Java 后端项目 +- 纯 Python 自动化项目 +- 纯前后端分离 Web 项目 + +它当前的真实形态更接近: + +`Vue 页面 -> Python 桌面桥接/Flask -> Java 任务系统 -> Redis / DB / OSS` + +同时,Python 自动化还会再去驱动本地紫鸟客户端和浏览器。 + +## 先看哪里 + +如果你的目标是理解当前主线,请按这个顺序读: + +1. `app/` +2. `backend-java/` +3. `frontend-vue/` + +原因很简单: + +- `app/` 决定了桌面端怎么承载页面、怎么暴露本地能力、怎么把任务推给 Python 自动化 +- `backend-java/` 决定了任务怎么创建、状态怎么缓存、结果怎么回传、文件怎么生成和下载 +- `frontend-vue/` 决定了用户是怎么触发这些链路的 + +以下目录不要默认当成当前主线: + +- `source_code/` +- `backend/` + +它们和 `app/` 有明显重叠,更像历史版本、迁移残留或中间态副本。阅读时要先带着“可能不是当前生效版本”的假设。 + +## 主线架构怎么分工 + +### Python 层:桌面壳 + 本地桥接 + 自动化执行 + +`app/` 不是单纯的业务后端。它更像一个桌面宿主,负责三件事: + +- 承载 Flask 页面和登录态 +- 通过 `pywebview` 暴露本地文件选择、保存文件、上传文件、任务入队等能力 +- 启动和调度 Python 自动化任务 + +这一层解决的是“本地能力”和“桌面壳”的问题,不是最终任务结果的权威存储。 + +### Java 层:任务系统 + 结果系统 + +`backend-java/` 是当前业务核心后端,负责: + +- 文件上传与临时文件管理 +- 任务创建、历史查询、批量轮询 +- Redis 进度缓存 +- 分片结果接收与聚合 +- 结果文件生成 +- OSS 上传 +- 下载接口输出 + +从模块名看,Java 端已经承接了大部分工具型任务,例如: + +- `brand` +- `dedupe` +- `split` +- `convert` +- `deletebrand` +- `productrisk` +- `shopmatch` +- `pricetrack` + +### Vue 层:多页面工具前端 + +`frontend-vue/` 不是单页应用,而是多入口页面工程。它的作用主要是: + +- 让用户选择文件或参数 +- 调用 Java API 创建任务或查询任务 +- 调用 `pywebview` 桥接拿本地能力 +- 在任务执行过程中轮询状态和下载结果 + +它构建后的产物输出到 `new_web_source/`,再由 Python/Flask 暴露出来供桌面端使用。 + +## 如何理解目录 + +### 当前最值得关注的目录 + +#### `app/` + +这是当前阅读优先级最高的 Python 目录,重点看这些角色: + +- `main.py`:创建 `pywebview` 窗口,暴露本地桥接 API +- `app.py`:Flask 应用入口 +- `blueprints/`:页面、登录、品牌、通信等 HTTP 层 +- `amazon/`:删除品牌、商品风险、匹配、跟价等自动化任务实现 + +#### `backend-java/` + +这是任务系统核心,重点关注: + +- controller:对外 API +- service:任务创建、进度、结果回传、组装 +- model / mapper:任务和结果模型 +- `application.yml`:后端依赖配置 + +#### `frontend-vue/` + +这是当前工具页前端源码,重点关注: + +- `src/shared/bridges/pywebview.ts`:前端如何调 Python 本地桥接 +- `src/shared/api/java-modules.ts`:前端如何调 Java API +- `src/pages/brand/components/`:各个具体工具页 + +### 不要误判成源码主线的目录 + +#### `new_web_source/` + +更像前端构建产物目录,不是首选手改源码位置。 + +#### `source_code/` + +和 `app/` 结构高度相似,但从当前仓库关系看,更像旧线或中间迁移副本。 + +#### `backend/` + +另一套 Python 后端实现,和当前主线职责不完全一致,更像旧后台线。 + +## 用“删除品牌自动化”看懂整个系统 + +“删除品牌”是这个仓库里很有代表性的一条链路,因为它同时经过: + +- 前端 +- Python 桌面桥接 +- Python 自动化执行 +- Java 任务系统 + +下面按真实职责来拆。 + +### 1. 用户在前端选择文件 + +用户打开删除品牌页面后,前端并不会直接访问浏览器文件系统,而是通过 `pywebview` 调 Python 暴露的方法: + +- 选择 Excel 文件 +- 或选择一个文件夹并展开其中的 Excel + +这一阶段文件还在用户本地磁盘上。 + +### 2. Python 先把本地文件上传给 Java + +前端拿到本地路径后,会继续通过 `pywebview` 调 Python 的文件上传桥接。 + +这一步的实际执行者是 Python,不是前端浏览器。Python 会: + +1. 按本地路径读取文件 +2. 调用 Java 的 `/api/files/upload` +3. 以 `multipart/form-data` 上传 +4. 从 Java 换回一个 `fileKey` + +这里要注意: + +- 这是 `Python -> Java` +- 传的是源文件 +- 目的是让 Java 后续能基于 `fileKey` 找到文件 + +### 3. 前端要求 Java 创建“删除品牌任务” + +文件上传完成后,前端调用 Java 的删除品牌运行接口。 + +Java 在这一阶段做的事情不是“真的去删店铺里的 SKU”,而是: + +1. 根据 `fileKey` 找到上传文件 +2. 解析删除品牌 Excel +3. 按国家和 ASIN 组织数据 +4. 尝试匹配对应店铺 +5. 创建 `taskId` 和结果记录 +6. 把可执行项返回给前端 + +这一阶段可以理解成: + +- Java 负责建模和建任务 +- Python 还没有开始自动化执行 + +### 4. 前端把“可执行项”推入 Python 本地队列 + +删除品牌不是由 Java 主动发起 Python 执行的,而是前端把某个任务项再推给 Python。 + +这一步通过 `pywebview` 暴露的 `enqueue_json()` 完成,前端会构造一个类似下面的 payload: + +- `type: delete-brand-run` +- `taskId` +- `items` + +然后交给 Python。 + +重要区分: + +- 这一步不是 Java 队列 +- 这是 Python 本地进程内队列 +- 当前仓库里对应的是 `JSON_TASK_QUEUE` + +### 5. Python 的任务监听器开始消费队列 + +Python 侧有一个任务监听器持续盯着 `JSON_TASK_QUEUE`。 + +当它收到 `delete-brand-run` 后,会把任务交给线程池,然后按下面的层次处理: + +1. 任务 +2. 店铺 +3. 国家 +4. ASIN + +同时,Python 会在本地维护当前任务状态,例如: + +- 当前店铺 +- 当前国家 +- 当前 ASIN +- 已处理数量 +- 成功/失败计数 + +这些状态主要服务于 Python 本地执行期,不是前端最终查询任务状态的权威来源。 + +### 6. Python 自动化驱动紫鸟客户端和浏览器 + +删除品牌的“真正执行动作”发生在这里。 + +Python 不会直接操作普通浏览器,而是先和紫鸟客户端通信,再接管浏览器调试口。大致过程是: + +1. 连接或启动本机紫鸟客户端 +2. 打开指定店铺 +3. 切换国家 +4. 进入库存/目标页面 +5. 搜索 ASIN +6. 删除对应 SKU + +所以这一段实际上又分两层: + +- `Python -> 紫鸟客户端`:本地 HTTP IPC +- `Python -> 浏览器`:通过调试口/自动化驱动执行页面操作 + +### 7. Python 一边执行,一边把结果分片回传给 Java + +这条链路的关键点在这里: + +- Python 不是等整个任务做完再一次性提交结果 +- 而是每处理完一部分,就立刻回传给 Java + +删除品牌这里通常是按 ASIN 逐步回传。 + +回传方式: + +- `Python -> Java` +- `Content-Type: application/json` +- 接口:`/api/delete-brand/tasks/{taskId}/result` + +回传的数据里会包含: + +- 当前文件标识 +- `chunkIndex` +- `chunkTotal` +- 当前国家 +- 当前 ASIN +- 当前累计进度 +- 本次处理结果 + +因此,删除品牌这条线里 Python 到 Java 有两种传输: + +1. 源文件上传:`multipart/form-data` +2. 执行结果回传:`application/json` + +### 8. Java 负责接收分片、缓存进度、判断是否可以完结 + +Java 收到 Python 的结果分片后,不会简单地“收一条写一条最终结果”,而是会先做任务系统层面的处理: + +1. 校验 `taskId` +2. 校验分片对应的文件身份 +3. 过滤重复分片 +4. 将结果分片缓存起来 +5. 更新实时进度 +6. 判断某个文件的分片是否收齐 +7. 判断整个任务是否达到 finalize 条件 + +这一步说明 Java 才是“任务状态和最终结果”的权威系统。 + +### 9. Java 在分片收齐后生成最终结果 + +当 Java 发现所有需要的结果分片都已收齐时,会执行最终组装: + +1. 合并分片 +2. 重建最终结果数据 +3. 生成结果 Excel +4. 上传 OSS +5. 更新任务状态 +6. 写入结果记录 + +如果这一步成功,任务会变成 `SUCCESS`;否则会进入 `FAILED`。 + +### 10. 前端轮询 Java,不轮询 Python + +前端展示任务状态时,查询对象是 Java,而不是 Python 本地队列。 + +前端主要关心的是: + +- 任务详情 +- 批量进度 +- 下载地址 + +也就是说: + +- Python 负责执行 +- Java 负责对前端提供任务状态与结果 + +### 11. 前端最终通过 Java 下载结果,再交给 Python 保存到本地 + +任务成功后,前端拿到 Java 下载地址,然后再通过 `pywebview` 调 Python 的保存能力,把文件落回用户本地磁盘。 + +所以最后一步仍然是混合协作: + +- 下载来源是 Java +- 本地保存能力来自 Python 桌面桥接 + +## 这条例子可以类比到哪些模块 + +删除品牌不是孤例,它更像当前仓库自动化任务的代表模式。 + +同类模式至少还能看到这些任务类型: + +- `delete-brand-run` +- `product-risk-resolve-run` +- `shop-match-run` +- `price-track` + +它们的共性通常是: + +1. 前端先建任务 +2. Python 负责本地自动化执行 +3. Java 负责任务状态、结果缓存和最终输出 + +因此,理解删除品牌这条线之后,再看商品风险、店铺匹配、跟价等模块会容易很多。 + +## 接手时最容易踩的坑 + +### 不要把 `enqueue_json()` 当成 Java 队列 + +它是前端调 Python 的本地桥接,进入的是 Python 进程内队列,不是 Java 消息系统。 + +### 不要把 `new_web_source/` 当成首选源码目录 + +它更像构建产物输出,首选还是看 `frontend-vue/`。 + +### 不要默认 `source_code/` 和 `backend/` 仍是当前主线 + +这两个目录有参考价值,但从当前结构看,不应先于 `app/`、`backend-java/`、`frontend-vue/` 阅读。 + +### 不要把 Python 当成最终任务状态来源 + +Python 负责执行期状态;前端面向用户看到的任务状态、历史、下载结果,当前主权在 Java。 + +## 当前文档的边界 + +这份 README 基于当前仓库的静态分析整理,有几个边界需要明确: + +- 没有运行仓库程序做启动验证 +- 没有确认最终发布时到底启用哪一个入口 +- 对 `source_code/`、`backend/` 的判断是“更像旧线/残留”,不是运行态绝对结论 + +所以这份文档的目标不是提供启动手册,而是帮助接手开发者先建立正确的系统地图。 + +## 一句话结论 + +理解这个仓库最有效的方式不是按语言分开看,而是按职责看: + +- Python 负责桌面壳、本地桥接、自动化执行 +- Java 负责任务系统、结果系统、下载系统 +- Vue 负责工具页交互和任务发起 + +而“删除品牌自动化”正好把这三层的协作关系完整串了起来。