# 商户查找系统 - 实现计划 > 基于 design-spec.md,按依赖顺序拆分为 12 个步骤 > 每个步骤产出可编译/可运行的代码 --- ## Step 1: 项目初始化 + 基础骨架 **目标:** Go module 初始化、目录结构、配置加载、数据库连接 **具体任务:** 1. `go mod init spider` 并安装核心依赖: - gin, gorm, gorm/driver/mysql, go-redis/v9, asynq - gotd/td, colly, chromedp, go-openai 2. 创建 `configs/config.yaml` (参考 design-spec 第十二节) 3. 实现 `internal/config/config.go` — 用 viper 加载 YAML 配置 4. 实现 `internal/model/` 下所有表结构 (GORM model),参考 design-spec 第四节: - seed.go, keyword.go, setting.go, channel.go, nav_site.go - merchant_raw.go, merchant_clean.go, task.go, config_revision.go 5. 实现 `cmd/server/main.go` — 启动时连接 MySQL + Redis,AutoMigrate 建表 6. 创建 `deploy/docker-compose.yml`, `deploy/Dockerfile.api` **验收:** `go run cmd/server/main.go` 启动成功,MySQL 中建好所有表 --- ## Step 2: HTTP API 基础框架 + CRUD 接口 **目标:** Gin 路由注册,实现管理类 CRUD **具体任务:** 1. 实现 `internal/handler/seed.go` — 种子 CRUD (GET/POST/PUT/DELETE /api/v1/seeds) 2. 实现 `internal/handler/keyword.go` — 关键词 CRUD (支持批量添加) 3. 实现 `internal/handler/config.go` — 配置读取/修改 (写审计日志到 config_revisions) 4. 实现 `internal/handler/dashboard.go` — 仪表盘统计 5. 实现 `internal/handler/merchant.go` — 商户列表 (raw + clean, 分页/过滤/排序) 6. 实现 `internal/handler/channel.go` — 频道列表 + 统计 7. 实现 `internal/handler/nav_site.go` — 导航网页列表 8. 在 main.go 注册所有路由,统一错误处理和分页中间件 **验收:** 用 curl 测试所有 CRUD 接口正常 --- ## Step 3: 任务系统 (asynq Worker) **目标:** 实现任务创建、调度、进度上报、停止机制 **具体任务:** 1. 实现 `internal/worker/worker.go`: - 初始化 asynq server + mux - 注册 7 种 task handler 2. 实现 `internal/service/task_service.go`: - StartTask: 创建 task 记录 → 推入 Redis 队列 - StopTask: 通过 context cancel 停止 - GetProgress: 从 Redis 读实时进度 3. 实现 `internal/handler/task.go`: - POST /tasks/start, POST /tasks/:id/stop - GET /tasks, GET /tasks/:id 4. 实现 Redis 分布式锁 (同类型任务互斥, full 全局互斥) 5. 实现进度上报: 各 phase 循环体内定期写 Redis hash 6. 在 main.go 里同时启动 Gin server 和 asynq worker **验收:** 能启动一个 mock 任务,看到状态从 pending→running→completed,进度实时更新 --- ## Step 4: Pipeline 调度器 **目标:** 实现 pipeline 主逻辑和 7 阶段框架 **具体任务:** 1. 实现 `internal/pipeline/pipeline.go`: - Run(ctx, taskID, taskType, params) — 根据 taskType 调度对应 phase - full 类型按顺序串行 Phase 1→7,skip_phases 跳过指定阶段 - 每个 phase 执行前后更新 task progress - context cancel 检查 (停止机制) 2. 每个 phase 文件先实现空框架 (interface + stub): ```go type Phase interface { Name() string Run(ctx context.Context, task *model.Task) error } ``` 3. 实现 managed_settings 热加载: - 启动时从 DB 加载到 Redis 缓存 - runtime 级别参数从缓存读取 - 修改参数时更新缓存 **验收:** 启动 full pipeline 任务,能看到 7 个 phase 依次执行 (stub 直接 pass) --- ## Step 5: LLM 统一接口 + 联系方式提取器 **目标:** 实现 LLM 调用和正则/LLM 联系方式提取 **具体任务:** 1. 实现 `internal/llm/client.go`: - 封装 go-openai client,支持配置 baseURL/model 切换 - EvalChannelRelevance(name, about, memberCount) → score - ParseMerchant(message) → MerchantInfo - ClassifyIndustry(name, about) → industry string - IsNavSite(url) → (bool, confidence) - 统一 timeout 和错误处理 2. 实现 `internal/extractor/regex.go`: - TG 用户名正则: @[a-zA-Z][a-zA-Z0-9_]{4,31} - TG 链接: t\.me/[a-zA-Z0-9_]{5,32} - 变体: t点me, t . me, tg: - 邮箱、电话、网址标准正则 - 微信变体: 加V/vx/wx/微信 3. 实现 `internal/extractor/llm_extractor.go`: - 正则无结果时 fallback 到 LLM 提取 4. 中文检测函数: ContainsChinese(text, threshold) **验收:** 单元测试覆盖各种格式的联系方式提取 --- ## Step 6: TG 客户端封装 + 账号管理 **目标:** 封装 gotd/td,实现多账号管理和 FloodWait 处理 **具体任务:** 1. 实现 `internal/telegram/client.go`: - 封装 gotd/td 连接/认证 - GetEntity(username) — 解析频道/用户 - GetChannelMessages(channel, limit, offsetID) — 读历史消息 - GetChannelInfo(channel) — 读简介+成员数 - GetPinnedMessages(channel, limit) — 读置顶消息 2. 实现 `internal/telegram/account_manager.go`: - Acquire(ctx) → 获取可用账号 - Release(acc, floodWait) → 归还+标记冷却 - 冷却状态存 Redis `spider:tg:floodwait:{phone}` - FloodWait 策略: ≤60s 等待重试, >60s 切换, >300s 整轮 break 3. 配置文件读取 TG 账号列表 **验收:** 能连接 TG,获取一个公开频道的信息和消息 --- ## Step 7: Phase 1 (discover) + Phase 4 (scrape) **目标:** 实现 TG 频道裂变和消息采集 **具体任务:** 1. 实现 `internal/pipeline/phase1_discover.go`: - 从 managed_seeds 拿 active 种子 - BFS 裂变: 读消息 → 提取 forward_from + t.me 链接 - max_depth=3, 每层 max_channels_per_layer, 总数 max_channels_total - 写入 channels 表 (去重: username UNIQUE) - 频道间 sleep, context cancel 检查 2. 实现 `internal/pipeline/phase4_scrape.go`: - 取 channels status=pending - LLM 相关性评估 → 不相关标 skipped - 读简介 + 置顶 + 历史消息 - 正则快速判 → 有联系方式则 LLM 精准解析 - 写入 merchants_raw (Redis dedup 去重) - 断点续传: 更新 channels.last_message_id **验收:** 从一个种子裂变出频道,并从频道采集到商户 --- ## Step 8: Phase 2 (search) + Phase 3 (github) **目标:** 实现搜索引擎和 GitHub 采集 **具体任务:** 1. 实现 `internal/search/serper.go`: - 调 Serper API,支持翻页 - 返回结构化结果 (url, title, snippet) 2. 实现 `internal/pipeline/phase2_search.go`: - 从 managed_keywords 拿 active 关键词 - 调 Serper 搜索 - 分拣: t.me → channels, 导航站 → nav_sites - 关键词间 sleep 3. 实现 `internal/pipeline/phase3_github.go`: - GitHub Search API 搜 repo (支持 token) - 下载 README,过滤非中文 - 正则提取 t.me 链接 (前后 200 字含中文) - 写入 channels 表 **验收:** 用几个关键词搜索到频道和导航站,从 GitHub 挖到 TG 链接 --- ## Step 9: Phase 5 (crawl) — 网页爬取 **目标:** 实现导航站爬取和商户提取 **具体任务:** 1. 实现 `internal/crawler/static.go` — colly 静态爬取 2. 实现 `internal/crawler/dynamic.go` — chromedp JS 渲染 fallback 3. 实现预过滤规则引擎: - 黑名单域名/扩展名/路径 - 正向信号检测 - LLM 二次过滤 4. 实现 t.me 死号预检 (HTTP 抓 t.me 网页检查头像) 5. 实现 `internal/pipeline/phase5_crawl.go`: - 取 nav_sites status=pending - 预过滤 → 爬取 → 解析商户链接 - 死号预检 → 写入 merchants_raw - 商户官网子页爬取 (/contact, /about) **验收:** 从导航站提取出商户信息 --- ## Step 10: Phase 6 (clean) + Phase 7 (score) **目标:** 实现清洗三关和评分 **具体任务:** 1. 实现 `internal/pipeline/phase6_clean.go`: - 第一关: 黑名单过滤 (bot 名单 + 邀请链接 + 非中文) - 第二关: 去重 (同 username 按丰富度保留最优, 合并 source) - 第三关: TG 验证 (get_entity, 有独立 rate limiter) - 结果写入 merchants_clean 2. 实现 `internal/pipeline/phase7_score.go`: - 6 维度加权打分 - 更新 merchants_clean.quality_score **验收:** raw 商户经过清洗后进入 clean 表,valid 商户有评分 --- ## Step 11: React 前端 **目标:** 实现完整管理后台 **具体任务:** 1. `npm create vite@latest web -- --template react-ts` 2. 安装 antd, zustand, axios, react-router-dom 3. 实现 Layout (侧边栏导航 + 顶栏) 4. 实现页面: - Dashboard — 各表计数卡片 + 运行中任务 + 最近任务列表 - Tasks — 7阶段独立启动按钮 + full pipeline 按钮 + 任务列表 + 进度条 + 停止按钮 - MerchantsRaw — Ant Design Table (分页/过滤/默认 created_at desc) - MerchantsClean — Table (分页/过滤/按 quality_score 排序) - Channels — Table (分页/状态过滤/来源过滤) + 统计 - NavSites — Table (分页/状态过滤) - Seeds — CRUD Table (新增/编辑/删除) - Keywords — CRUD Table (支持批量添加) - Settings — 三个 Tab (种子/关键词/流水线参数) - Logs — WebSocket 实时日志展示 5. API 层 (`web/src/api/index.ts`) — axios 封装所有后端接口 6. 状态管理 (`web/src/store/`) — zustand **验收:** 前端能展示所有数据,能启动/停止任务,能管理种子和关键词 --- ## Step 12: Docker 部署 + 联调 **目标:** Docker Compose 完整部署,前后端联调 **具体任务:** 1. `deploy/Dockerfile.api` — 多阶段构建 Go 二进制 2. `deploy/Dockerfile.web` — 构建 React + Nginx 静态文件 3. `deploy/nginx.conf` — 前端静态 + /api 反代到 Go 服务 4. `deploy/docker-compose.yml` — api + web 两个服务,连接外部 MySQL/Redis 网络 5. 补充 managed_settings 初始数据 (参考 design-spec 第十四节) 6. 完整流程联调: 添加种子 → 启动 full pipeline → 查看商户结果 **验收:** `docker-compose up` 一键启动,浏览器访问管理后台,全流程可跑通 --- ## 依赖关系 ``` Step 1 (骨架) ↓ Step 2 (API CRUD) ↓ Step 3 (任务系统) → Step 4 (Pipeline 调度) ↓ ↓ Step 5 (LLM+提取器) Step 6 (TG 封装) ↓ ↓ Step 7 (Phase 1+4, TG 采集) ↓ Step 8 (Phase 2+3, 搜索+GitHub) ↓ Step 9 (Phase 5, 网页爬取) ↓ Step 10 (Phase 6+7, 清洗+评分) ↓ Step 11 (React 前端) ↓ Step 12 (Docker 部署+联调) ``` **注意:** Step 5 和 Step 6 相互独立,可以并行实现。Step 11 (前端) 只依赖 Step 2 的 API,可以在 Step 3 之后就开始并行开发。