# TG 商户采集系统 — 需求方案书(v2) > 本文档描述系统要实现的功能和业务逻辑,供开发者从零设计和实现。 > 版本: v2(精简版,砍掉低 ROI 模块,强调模块化隔离) --- ## 一、系统目标 **一句话:用关键词去 Google 搜,把搜到的网页里的商户联系方式扒下来,清洗后输出一张可以直接联系的客户表。** ### 什么是"商户" 系统要找的是**在 TG 上提供产品或服务的人或组织**。 判定标准(满足任意一条即算商户): - 有 TG 联系方式(@xxx 或 t.me/xxx)**并且**有商业意图(接单、代理、价格、购买、咨询、客服、官网、订阅) - 被导航站收录(导航站本身就是商户目录) **不算商户的**:聊天用户、新闻频道、系统 bot。 ### 目标行业(当前) 当前只做**机场 / VPN / 科学上网**。行业规则可配置,以后可扩展。 ### 输入 - 一组**关键词**(比如"机场推荐"、"VPN 订阅"、"科学上网") ### 最终输出 | 字段 | 说明 | |------|------| | 商户名 | 显示名称 | | TG 用户名 | @xxx | | TG 链接 | https://t.me/xxx | | 网站 | 商户官网 | | 邮箱 | 联系邮箱 | | 电话 | 联系电话 | | 来源 | 从哪个网页/渠道发现的 | | 行业标签 | 机场 / VPN 等 | | 等级 | Hot / Warm / Cold | --- ## 二、核心架构:插件式采集 + 统一清洗 ### 设计理念 系统分两大部分:**采集端**和**处理端**。 - **采集端**:负责从各种渠道找商户,每个渠道是一个**独立插件** - **处理端**:负责清洗、去重、验证、打标签,**所有插件共用同一套** ``` ┌─────────────────────────────────────────────────────────────┐ │ 采集端(插件式) │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ 插件 A │ │ 插件 B │ │ 插件 C │ ← 互相 │ │ │ 网页采集 │ │ TG 频道采集 │ │ 未来新增... │ 不影响 │ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 统一入口:商户表 raw │ │ │ │ 所有插件的产出格式一样,往同一张表写 │ │ │ └──────────────────────┬─────────────────────────────┘ │ └─────────────────────────┼─────────────────────────────────────┘ │ ┌─────────────────────────┼─────────────────────────────────────┐ │ 处理端(固定流程) │ │ ▼ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ 死号预检 │ → │ 黑名单 │ → │ 去重 │ → │ 打标签 │ │ │ │ (t.me) │ │ 过滤 │ │ 合并 │ │ 分等级 │ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ │ │ │ ▼ │ │ 商户表 clean │ │ (Hot / Warm / Cold) │ └───────────────────────────────────────────────────────────────┘ ``` ### 为什么这样设计 **核心问题:后期加新的采集渠道,不能把前面的东西弄坏。** 解决办法:**插件隔离**。 ``` 规则 1: 每个采集插件是独立的代码模块,有自己的目录/文件 规则 2: 插件之间零依赖,A 插件的代码不能 import B 插件的任何东西 规则 3: 所有插件的产出格式统一(见下方"标准产出格式") 规则 4: 插件只管采集,不管清洗/去重/打分 — 那是处理端的事 规则 5: 新增插件 = 新建一个目录 + 实现标准接口,不改任何已有代码 ``` ### 标准产出格式(所有插件必须遵守) 每个插件采集到商户后,必须按这个格式写入 `merchants_raw` 表: ``` { "merchant_name": "商户名(选填)", "tg_username": "xxx(必填,没有就不入库)", "tg_link": "https://t.me/xxx", "website": "官网地址(选填)", "email": "邮箱(选填)", "phone": "电话(选填)", "source_type": "web / tg_channel / github / ...", "source_name": "具体来源(哪个网页/频道)", "source_url": "来源 URL", "original_text": "原始文本(留底)", "industry_tag": "行业标签(选填)" } ``` **关键约束**:没有 `tg_username` 的商户不入库。这是核心数据,其他都是锦上添花。 ### 插件的标准接口 每个插件需要实现以下接口(伪代码): ``` class CollectorPlugin: name: str # 插件名,比如 "web_collector" async def run(config, callback): """ config: 该插件的配置(关键词、URL 列表等) callback(merchant_data): 每找到一个商户就调一次,由框架写入 raw 表 """ async def stop(): """外部可以随时叫停""" ``` 框架负责:调度插件、写数据库、记日志、控制并发。 插件负责:采集逻辑,只管找商户,找到就 callback。 --- ## 三、采集插件 A:网页采集(优先开发) **这是最高优先级的插件,也是系统的核心价值。** ### 为什么优先 - 一个导航站几秒出 50 个商户,效率最高 - 没有限速问题,想跑多快跑多快 - 导航站上的商户是别人已经整理好的,质量高 ### 流程 ``` 关键词 → Google 搜索 → 拿到 URL 列表 │ ┌─────────┴─────────┐ ↓ ↓ URL 是 t.me/xxx URL 是网页 直接提取 username 打开网页读 HTML │ │ │ ┌─────┴──────┐ │ ↓ ↓ │ 找 t.me 链接 找联系方式 │ 提取 username (邮箱/电话/网址) │ │ │ └──────┬──────┘ │ ↓ │ 写入 merchants_raw ←────────┘ ``` ### 详细逻辑 **第一步:关键词搜索** 1. 从关键词表拿关键词(比如"机场推荐 telegram") 2. 调搜索 API(Serper 或 Brave Search),拿搜索结果 3. 每个关键词搜 3-5 页,每页 10 条 4. 关键词之间等几秒,避免被封 **第二步:URL 分拣** 拿到的 URL 分三种: | URL 类型 | 怎么判断 | 怎么处理 | |----------|---------|---------| | `t.me/xxx` | URL 以 t.me/ 开头 | 直接提取 username,写 raw 表 | | 导航站/有用网页 | 不在黑名单里的网页 | 打开网页,进入第三步 | | 垃圾 | 在黑名单里(twitter/google/youtube 等 80 个域名) | 丢弃 | **第三步:网页解析** 1. 用 HTTP 请求抓网页 HTML 2. HTML 前 3000 字不含中文 → 跳过(不是中文站) 3. 解析 HTML,找所有外链: - `t.me/xxx` 链接 → 提取 username - `mailto:xxx` → 提取邮箱 - 电话号码正则 → 提取电话 4. 如果页面上有很多 t.me 链接(>5 个),说明这是个导航站,每个链接都是一个商户 5. 每个提取到的商户按标准格式写入 raw 表 **HTTP 请求失败时的 fallback**(按顺序尝试): 1. 标准 `net/http`(或 colly 爬虫框架) 2. 带自定义 TLS 指纹的 HTTP 客户端(绕反爬,如 utls 库) 3. chromedp / rod(Go 原生浏览器引擎,处理 JS 渲染页面) ### 搜索 API 选择 | 方案 | 免费额度 | 付费 | 推荐 | |------|---------|------|------| | **Brave Search API** | 5000 次/月 | $5/1000 次 | 先用这个测试 | | **Serper.dev** | 2500 次(一次性) | $50/50000 次 | Google 结果最准 | | **DuckDuckGo** | 无限 | 免费 | 开源库,稳定性差 | 建议:**先用 Brave 免费额度测试**,结果不够好再切 Serper。代码层面做成可配置,换 API 只改配置不改代码。 --- ## 四、采集插件 B:TG 频道采集(第二优先级) **等插件 A 跑稳了再开发这个。** 这是锦上添花,不是核心。 ### 为什么第二优先级 - TG 限速严,一天最多出几十个商户 - 需要手机号注册账号,被封就废 - 开发和维护成本比网页采集高得多 ### 流程 ``` 种子频道列表 → 进频道 → 读历史消息(最近 500 条) │ 每条消息看有没有联系方式 正则快扫 → 有 → AI 精确提取 │ 写入 merchants_raw(标准格式) ``` ### 详细逻辑 1. 从种子列表拿频道(比如 @bbs3000),种子由用户手动添加 2. 用 TG 客户端库(Go: gotd/td)登录 TG 账号,进入频道 3. 读最近 500 条消息(支持断点续传,记住上次读到哪条) 4. 每条消息: - 系统消息 / 非中文 → 跳过 - 正则快速扫描有没有 `@xxx`、`t.me/xxx`、邮箱、网址 - 有联系方式 → 调 AI 精确提取商户信息 - AI 超时(>5 秒)或失败 → 用正则兜底 5. 提取到的商户按标准格式写入 raw 表 ### TG 账号管理(重要) TG 账号是稀缺资源,需要专门的调度器: **entity ID 缓存(必须做)**: - 第一次 `ResolveUsername` 拿到频道的数字 ID 后存到本地 - 以后直接用数字 ID 访问,不再调 `ResolveUsername` - 这样同一个频道只消耗 1 次 resolve 额度,之后无限次不限速 **限速处理**: - 全局请求频率控制(所有模块共享,不超过 30 次/分钟) - FloodWait < 60 秒 → 等完继续 - FloodWait > 60 秒 → 切账号 - FloodWait > 300 秒 → 停止,下次再来 - 没有可用账号 → 排队等待,不崩溃 **每个 TG 账号需要的信息**: | 字段 | 说明 | |------|------| | 手机号 | 注册 Telegram 用的 | | api_id | 在 https://my.telegram.org 申请 | | api_hash | 同上 | | session 文件 | 首次登录后生成 | --- ## 五、采集插件 C/D/E...:未来扩展 以下是以后可能加的插件,**现在不做,但架构要能支持**: | 插件 | 数据源 | 什么时候加 | |------|--------|-----------| | GitHub 搜索 | GitHub README 里的 t.me 链接 | 网页+TG 都稳定后 | | TG 频道裂变 | 从种子频道滚雪球发现新频道 | TG 采集稳定后 | | 百度搜索 | 百度搜索结果 | 如果 Google 覆盖不够 | | Twitter/X | 推文里的 t.me 链接 | 如果有需求 | **加新插件的步骤**(这是模块化的价值): 1. 新建一个目录/文件 2. 实现 `run()` 和 `stop()` 接口 3. 按标准格式 callback 产出商户 4. 在配置里注册插件名 5. **不改任何已有代码** --- ## 六、处理端:清洗流程 所有插件的产出都进 `merchants_raw` 表,然后统一过清洗流程。 ### 清洗流水线(4 步,按顺序执行) ``` merchants_raw → [死号预检] → [黑名单过滤] → [去重合并] → [打标签分等级] → merchants_clean ``` ### 第一步:t.me 死号预检(免费,无限速) - 用 HTTP 请求访问 `https://t.me/{username}` - 看返回 HTML 里有没有 `tgme_page_photo_image` 标记 - 有头像 = 活号 → 继续 - 没头像 = 死号 → 标记 invalid,不进后面的步骤 - **准确率 100%,不花钱,不限速** - 建议并发 10 个,每分钟能检 600 个 ### 第二步:黑名单过滤(本地,秒级) | 规则 | 处理 | |------|------| | TG 用户名是系统 bot(@BotFather、@SpamBot、以 bot 结尾) | 标记 bot | | TG 用户名像邀请链接哈希(16-24 位随机字符串) | 标记 invalid | | 原始文本不含中文(如果有原始文本的话) | 标记 invalid | ### 第三步:去重合并(本地,秒级) - 同一个 tg_username 可能被多个插件多次发现 - 按信息丰富度保留最好的一条(有网站 > 没网站,有邮箱 > 没邮箱) - 其他标记为 duplicate - 合并所有来源信息到保留的那条 ### 第四步:打标签 + 分等级 **行业标签**:用关键词匹配(商户名/原始文本里包含"机场""节点""VPN"→ 打标签)。只做机场/VPN 一个行业时,关键词匹配完全够用,不需要 AI。 **等级划分**(3 个桶,不打分): | 等级 | 条件 | 含义 | |------|------|------| | **Hot** | 行业匹配 + 有网站或邮箱 | 优先联系,信息最全 | | **Warm** | 行业匹配 + 只有 TG 号 | 可以联系,但信息少 | | **Cold** | 行业不匹配 / 信息太少 | 暂不联系 | **为什么不用 0-100 打分**: - 100 分制需要成员数、Premium、活跃度等数据,但这些要调 TG API 才能拿到 - TG API 是最大的瓶颈,为了打分去调 API 得不偿失 - 3 个桶简单直观,销售拿到手就能用 ### 可选增强:TG 验证(需要 TG 账号) 如果有 TG 账号且未被限速,可以在第三步和第四步之间加一步: - 调 `ResolveUsername` 验证账号真实性 - 拿到:显示名、是否 Premium、最后在线时间 - 有了这些数据可以更精准地分等级 **但这不是必须的。** 没有 TG 账号,系统照样能跑(靠 t.me 预检 + 黑名单就能过滤大部分垃圾)。 --- ## 七、数据模型(5 张表) ### 表 1:关键词表 (keywords) | 字段 | 类型 | 说明 | |------|------|------| | id | int | 主键 | | keyword | string | 搜索关键词 | | industry_tag | string | 行业标签(机场/VPN) | | enabled | bool | 是否启用 | | created_at | datetime | 创建时间 | 种子频道也放这个表(`industry_tag = 'seed'`),不单独建表。 ### 表 2:商户表 — 原始 (merchants_raw) 所有插件的产出统一写这张表。 | 字段 | 类型 | 说明 | |------|------|------| | id | int | 主键 | | tg_username | string | **必填**,TG 用户名 | | tg_link | string | t.me 链接 | | merchant_name | string | 商户名 | | website | string | 官网 | | email | string | 邮箱 | | phone | string | 电话 | | source_type | string | 来源类型(web / tg_channel / github) | | source_name | string | 具体来源(哪个网页/频道) | | source_url | string | 来源 URL | | original_text | text | 原始文本 | | industry_tag | string | 行业标签 | | status | string | raw / processing / done | | created_at | datetime | 入库时间 | **入库去重规则**:同 `tg_username` + 同 `source_url` 不重复插入。不同来源发现同一个 username 允许多条(去重在清洗阶段做)。 ### 表 3:商户表 — 已清洗 (merchants_clean) 清洗通过的商户。 | 字段 | 类型 | 说明 | |------|------|------| | id | int | 主键 | | tg_username | string | TG 用户名 | | tg_link | string | t.me 链接 | | merchant_name | string | 商户名 | | website | string | 官网 | | email | string | 邮箱 | | phone | string | 电话 | | source_count | int | 被多少个来源发现 | | all_sources | text | 所有来源列表(JSON) | | industry_tag | string | 行业标签 | | level | string | **Hot / Warm / Cold** | | status | string | valid / invalid / bot / duplicate | | is_alive | bool | t.me 预检结果 | | last_checked_at | datetime | 最近一次验证时间 | | created_at | datetime | 首次发现时间 | ### 表 4:频道表 (channels) 只有启用了 TG 采集插件才需要这张表。 | 字段 | 类型 | 说明 | |------|------|------| | id | int | 主键 | | username | string | 频道用户名(唯一) | | channel_id | bigint | TG 数字 ID(缓存,避免重复 resolve) | | access_hash | bigint | TG access_hash(缓存) | | status | string | pending / scraped / skipped | | last_message_id | int | 上次采集到哪条(断点续传) | | merchants_found | int | 发现了多少商户 | | source | string | seed / discovered | | created_at | datetime | 入库时间 | ### 表 5:任务日志表 (task_logs) | 字段 | 类型 | 说明 | |------|------|------| | id | int | 主键 | | task_type | string | web_collect / tg_collect / clean / ... | | plugin_name | string | 哪个插件 | | status | string | running / success / failed / stopped | | items_processed | int | 处理了多少条 | | merchants_added | int | 新增了多少商户 | | errors_count | int | 错误数 | | started_at | datetime | 开始时间 | | finished_at | datetime | 结束时间 | | detail | text | 详细日志/错误信息 | --- ## 八、前端需求(只做 2 个页面) 早期只需要 2 个页面,其他的等有需求了再加。 ### 页面 1:商户列表 - 显示 `merchants_clean` 表的数据 - 按等级筛选(Hot / Warm / Cold) - 按行业筛选 - 按来源筛选 - 搜索(按商户名、TG 用户名) - 排序(按发现时间、来源数) - 导出 CSV / Excel - 点击 TG 链接可以直接跳转 ### 页面 2:任务管理 - 选择插件启动任务(网页采集 / TG 采集 / 清洗) - 显示当前运行中的任务 - 停止任务 - 查看历史任务和结果 ### 不做的(延后) | 功能 | 为什么不做 | |------|-----------| | ~~仪表盘~~ | 数据量小时看列表就够了 | | ~~配置管理~~ | 改配置文件比写前端快 | | ~~实时日志流~~ | SSH 看日志就行 | | ~~种子频道管理~~ | 初期手动维护,量不大 | --- ## 九、外部依赖 ### 技术栈 | 层 | 选型 | 说明 | |---|---|---| | **后端语言** | **Go** | 高并发、编译型、单二进制部署 | | **Web 框架** | Gin 或 Echo | 轻量 HTTP 框架 | | **ORM** | GORM | Go 主流 ORM | | **数据库** | SQLite(初期)/ PostgreSQL(后期) | 初期单机够用 | | **TG 客户端** | gotd/td 或 gotdlib | Go 原生 Telegram MTProto 库 | | **HTML 解析** | goquery | 类似 jQuery 的 HTML 解析 | | **HTTP 客户端** | net/http + colly | 标准库 + 爬虫框架 | | **浏览器引擎** | chromedp 或 rod | Go 原生 Chrome DevTools Protocol(替代 Playwright) | | **前端** | Vue 3 + Vite + TypeScript | 不变 | | **配置** | YAML(viper 库) | Go 生态标准 | | **日志** | zerolog 或 zap | 结构化日志 | ### 必须的外部服务 | 服务 | 用途 | 备注 | |------|------|------| | **搜索 API** | 关键词搜索 | Brave(免费 5000 次/月)或 Serper($50/50000 次) | | **HTTP 客户端** | 抓网页、t.me 预检 | net/http + colly + chromedp 三层 fallback | ### 可选的(TG 采集插件启用后才需要) | 服务 | 用途 | 备注 | |------|------|------| | **gotd/td** | TG 频道采集 | Go 原生 MTProto 库,替代 Python Telethon | | **AI 大模型 API** | 联系方式提取 | 智谱 GLM 或 DeepSeek,仅 TG 采集时用,HTTP 调用即可 | ### AI 使用策略 **规则优先,AI 只在一个地方用。** | 环节 | 方法 | 说明 | |------|------|------| | 网页联系方式提取 | **纯正则** | 网页上的 t.me 链接、邮箱、电话,正则就能 100% 提取 | | TG 消息联系方式提取 | **正则 + AI** | 非标准格式("加V:xxx")需要 AI | | 行业分类 | **纯关键词匹配** | 只做机场/VPN,关键词够用 | | 导航站识别 | **纯规则**(黑名单 + 正向关键词) | 不需要 AI | AI 只在 TG 采集插件的联系方式提取环节使用。网页采集完全不需要 AI。 --- ## 十、运行方式 ### 单插件运行 每个插件可以独立跑: - 只跑网页采集 → 看搜到了什么 - 只跑清洗 → 处理已有的脏数据 - 只跑 TG 采集 → 从指定频道挖商户 ### 全链路运行 也可以串起来:`网页采集 → 清洗`(两步就够了) ### 任务控制 - 每个任务有状态:运行中 / 完成 / 失败 / 已停止 - 支持手动停止 - 同类型任务不能同时跑两个 - 支持测试模式(只跑少量数据) --- ## 十一、踩过的坑(供参考) ### 1. TG 限速是最大坑 单账号一天最多几百次 `ResolveUsername`,之后被限速 10-24 小时。 **根治方案**:缓存 `channel_id + access_hash`,同一个频道只调一次 `ResolveUsername`,之后用数字 ID 直接访问。 ### 2. t.me 网页可以免费检测死号 访问 `https://t.me/{username}`,HTML 里有 `tgme_page_photo_image` = 活号,没有 = 死号。准确率 100%,不限速,不花钱。**在调 TG API 之前先做这一步能省 90% 的 API 调用。** ### 3. AI 会编造联系方式 AI 提取后必须用正则回原文二次验证。原文里找不到的,丢弃 AI 的结果。 ### 4. 清洗后数据和原始数据分开存 用两张表(raw 和 clean),清洗通过的搬到 clean 表。raw 表保留原始数据,可以反复清洗。 ### 5. 非中文内容直接跳过 系统只做中文商户,非中文的网页/消息直接跳过,节省大量处理时间。 ### 6. 网页抓取要有 fallback 有些网页有反爬(Cloudflare),有些是 JS 渲染。按顺序试:net/http → utls(自定义 TLS 指纹)→ chromedp/rod(浏览器引擎)。 ### 7. 不要一上来就做全链路 先把一个插件(网页采集)做稳做透,再加第二个(TG)。一上来就做 7 阶段 pipeline,结果哪个都不稳。 --- ## 附录 A:模块化目录结构建议(Go) ``` tg-lead-scraper/ ├── cmd/ │ └── server/ │ └── main.go # 程序入口 │ ├── internal/ │ ├── plugin/ # 插件框架 │ │ ├── interface.go # 插件接口定义(Collector interface) │ │ └── registry.go # 插件注册中心 │ │ │ ├── plugins/ # 采集插件目录(每个插件一个包) │ │ ├── webcollector/ # 插件 A:网页采集 │ │ │ ├── collector.go # 实现 Collector 接口 │ │ │ ├── searcher.go # 调搜索 API │ │ │ └── parser.go # 解析网页 HTML │ │ ├── tgcollector/ # 插件 B:TG 频道采集 │ │ │ ├── collector.go # 实现 Collector 接口 │ │ │ ├── scraper.go # TG 消息采集 │ │ │ └── account.go # TG 账号调度 │ │ └── githubcollector/ # 插件 C:未来新增 │ │ └── ... │ │ │ ├── processor/ # 处理端(清洗流程) │ │ ├── pipeline.go # 清洗流水线调度 │ │ ├── tmechecker.go # t.me 死号预检 │ │ ├── blacklist.go # 黑名单过滤 │ │ ├── dedup.go # 去重合并 │ │ └── tagger.go # 打标签 + 分等级 │ │ │ ├── model/ # 数据模型 │ │ ├── merchant.go # 商户结构体 + GORM model │ │ ├── channel.go # 频道 │ │ ├── keyword.go # 关键词 │ │ └── tasklog.go # 任务日志 │ │ │ ├── store/ # 数据访问层 │ │ ├── db.go # 数据库连接 + 初始化 │ │ ├── merchant_repo.go # 商户 CRUD │ │ └── keyword_repo.go # 关键词 CRUD │ │ │ ├── extractor/ # 联系方式提取器 │ │ ├── regex.go # 正则提取 │ │ └── ai.go # AI 提取(调大模型 API) │ │ │ └── task/ # 任务调度 │ └── manager.go # 任务启停、并发控制 │ ├── api/ # HTTP API │ ├── server.go # Gin/Echo 初始化 │ ├── handler/ │ │ ├── merchant.go # 商户列表 API │ │ └── task.go # 任务管理 API │ └── middleware/ │ └── auth.go # 认证中间件 │ ├── frontend/ # 前端(Vue 3) │ └── ... │ ├── config/ │ └── config.yaml # 全局配置 │ ├── go.mod ├── go.sum └── Makefile ``` ### 插件接口定义(Go interface) ```go // internal/plugin/interface.go package plugin import "context" // MerchantData 是所有插件的标准产出格式 type MerchantData struct { TgUsername string `json:"tg_username"` TgLink string `json:"tg_link"` MerchantName string `json:"merchant_name"` Website string `json:"website"` Email string `json:"email"` Phone string `json:"phone"` SourceType string `json:"source_type"` SourceName string `json:"source_name"` SourceURL string `json:"source_url"` OriginalText string `json:"original_text"` IndustryTag string `json:"industry_tag"` } // Collector 是所有采集插件必须实现的接口 type Collector interface { // Name 返回插件名,比如 "web_collector" Name() string // Run 启动采集,每找到一个商户就调 callback,ctx 取消时优雅退出 Run(ctx context.Context, cfg map[string]any, callback func(MerchantData)) error // Stop 外部可以随时叫停 Stop() error } ``` ### 加新插件的步骤 1. 在 `internal/plugins/` 下新建包(比如 `baiducollector/`) 2. 实现 `Collector` 接口的 3 个方法 3. 在 `registry.go` 注册插件名 4. 在 `config.yaml` 加插件配置 5. **不改 internal/plugins/ 外的任何代码** --- ## 附录 B:完整数据流图 ``` ┌─────────── 采集端 ───────────┐ │ │ │ 关键词 → [网页采集插件] │ │ │ │ │ ├→ t.me 链接 │ │ └→ 网页 → 解析 │ ┌─────── 处理端 ──────┐ │ │ │ │ │ │ ↓ │ │ [死号预检] │ │ merchants_raw ←────┼──→ │ ↓ │ │ ↑ │ │ [黑名单过滤] │ │ 种子频道 → [TG 采集插件] │ │ ↓ │ │ │ │ │ [去重合并] │ │ └→ 消息 → AI提取 │ │ ↓ │ │ │ │ [打标签分等级] │ │ (未来) → [GitHub 插件] │ │ ↓ │ │ (未来) → [百度插件] │ │ merchants_clean │ │ (未来) → [Twitter 插件] │ │ (Hot/Warm/Cold) │ │ │ │ │ └────────────────────────────────┘ └───────────────────────┘ │ ↓ 前端:商户列表 + 导出 ```