# WeChatOfficialAccountCenter **Repository Path**: leafCore/we-chat-official-account-center ## Basic Information - **Project Name**: WeChatOfficialAccountCenter - **Description**: 微信公众号关注中心 (WeChatAttentionHub): 基于 .NET 8+ 盛派 Senparc SDK搭建的微信公众号关注事件分发中心。实现【一个公众号服务多个小程序/下游系统】,通过场景路由将关注来源精准分发,并以 API Key/Secret 验签方式安全对接各下级系统。 - **Primary Language**: C# - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-06-23 - **Last Updated**: 2026-06-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 微信公众号关注中心 (WeChatAttentionHub) 基于 **.NET 8** + **盛派 Senparc SDK** 搭建的微信公众号关注事件分发中心。实现**一个公众号服务多个小程序/下游系统**,通过场景路由将关注来源精准分发,并以 API Key/Secret 验签方式安全对接各下级系统。 --- ## 项目简介 ### 解决什么问题? 当一个公司有多个小程序(或下游业务系统),但只有一个公众号时,如何让不同渠道扫码关注的用户**自动归属**到对应的小程序? **WeChatAttentionHub** 充当公众号与下游系统之间的**智能路由层**: - 接收微信公众号的关注/取关/扫码事件 - 根据二维码场景值(scene)匹配所属下游系统 - 实时回调通知对应下游系统 - 下游系统通过 API Key + Secret 验签安全接入 ### 核心特性 | 特性 | 说明 | |------|------| | 🧭 **场景路由** | 支持精确/前缀/后缀/包含/全匹配通配符 | | 🔐 **API 验签** | HMAC-SHA256 签名 + 时间戳防重放 | | 📢 **实时回调** | 关注/取关事件即时推送到下游系统 | | 🗄️ **事件追溯** | 全量记录关注/取关/扫码事件,支持分页查询 | | 🔑 **密钥轮换** | 下游系统 API Secret 支持在线轮换 | | 🧩 **可扩展** | 下游系统零耦合接入,只需提供 CallbackUrl | --- ## 架构概览 ``` ┌──────────────────────────────────────────────────────────────┐ │ 微信服务器 │ │ (推送关注/取关/扫码事件) │ └─────────────────────┬────────────────────────────────────────┘ │ POST /api/wechat ▼ ┌──────────────────────────────────────────────────────────────┐ │ WeChatAttentionHub │ │ │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ WeChatController │───▶│ MessageHandler │ │ │ │ (签名验证/接收) │ │ (Senparc SDK) │ │ │ └──────────────────┘ └────────┬─────────┘ │ │ │ │ │ ┌──────────────▼──────────────┐ │ │ │ SceneRouter │ │ │ │ 场景值 → 下游系统匹配 │ │ │ └──────────────┬──────────────┘ │ │ │ │ │ ┌──────────────▼──────────────┐ │ │ │ DownstreamNotifier │ │ │ │ HTTP回调 + API签名 │ │ │ └──────────────┬──────────────┘ │ │ │ │ │ ┌──────────────▼──────────────┐ │ │ │ FollowRecord (SQLite) │ │ │ │ 事件持久化存储 │ │ │ └─────────────────────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ ManagementController + ApiKeyAuthMiddleware │ │ │ │ 下游管理 / 路由配置 / 记录查询 (需验签) │ │ │ └──────────────────────────────────────────────────────┘ │ └─────────────────────┬────────────────────────────────────────┘ │ HTTP POST (X-Api-Key + X-Signature) ┌───────────┼───────────┬───────────┐ ▼ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ 小程序A │ │ 小程序B │ │ 业务系统C│ │ ... │ │scene=100│ │scene=200│ │scene=300│ │ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ ``` --- ## 路由流程图 ### 关注事件处理流程 ``` 用户扫码 │ ┌────────▼────────┐ │ 微信服务器 │ │ 推送事件到公众号 │ └────────┬────────┘ │ ┌────────▼────────┐ │ WeChatController│ │ GET: Token验证 │ │ POST: 消息接收 │ └────────┬────────┘ │ ┌────────▼────────┐ │ MessageHandler │ │ (Senparc SDK) │ │ │ │ subscribe事件? │ │ scan事件? │ │ unsubscribe? │ └────────┬────────┘ │ ┌────────▼────────┐ │ 提取场景值(scene) │ │ EventKey处理: │ │ qrscene_123→123 │ │ 直接扫码→原值 │ └────────┬────────┘ │ ┌────────▼────────┐ │ SceneRouter │ │ │ │ 匹配规则: │ │ ┌─────────────┐ │ │ │ "100" → 精确 │ │ │ │ "200*"→ 前缀 │ │ │ │ "*300"→ 后缀 │ │ │ │ "*vip*"→包含 │ │ │ │ "*" → 全匹配│ │ │ └─────────────┘ │ └────────┬────────┘ │ ┌─────────▼─────────┐ │ 匹配到下游系统? │ └────┬─────────┬────┘ 是 │ │ 否 │ │ ┌─────────▼──┐ ┌──▼──────────┐ │ 记录事件 │ │ 记录事件 │ │ (含下游ID) │ │ (下游ID=null)│ └─────────┬──┘ └──────────────┘ │ ┌─────────▼──────────┐ │ DownstreamNotifier │ │ │ │ POST CallbackUrl │ │ Headers: │ │ X-Api-Key │ │ X-Timestamp │ │ X-Nonce │ │ X-Signature │ │ │ │ Body: │ │ { │ │ "event_type": │ │ "subscribe", │ │ "open_id":"...", │ │ "scene_value": │ │ "100", │ │ "record_id":"..." │ │ } │ └────────┬───────────┘ │ ┌────────▼────────┐ │ 下游系统处理 │ │ 返回 200 OK │ └────────┬────────┘ │ ┌────────▼────────┐ │ 更新 FollowRecord│ │ CallbackSuccess │ │ = true │ └─────────────────┘ ``` ### 下游系统接入流程 ``` ┌─────────────────────────────────────────────────┐ │ 下游系统接入步骤 │ ├─────────────────────────────────────────────────┤ │ │ │ ① 注册下游系统 │ │ POST /api/management/systems │ │ { "name":"小程序A", "callbackUrl":"..." } │ │ │ │ │ ▼ │ │ 返回: { id, apiKey, apiSecret } │ │ (apiSecret 仅返回一次!) │ │ │ │ │ ▼ │ │ ② 配置场景路由 │ │ POST /api/management/scene-routes │ │ { "sceneValue":"100", │ │ "downstreamSystemId":"...", │ │ "priority":10 } │ │ │ │ │ ▼ │ │ ③ 下游系统实现回调接口 │ │ 接收 POST /your-callback-url │ │ 验证 X-Signature (HMAC-SHA256) │ │ 处理 subscribe / unsubscribe 事件 │ │ │ │ │ ▼ │ │ ④ 创建带场景值的公众号二维码 │ │ 微信后台 → 工具 → 生成带参数二维码 │ │ 场景值 = "100" │ │ │ │ │ ▼ │ │ ⑤ 用户扫码关注 ──→ Hub自动路由 ──→ 小程序A收到通知 │ │ │ └─────────────────────────────────────────────────┘ ``` ### API 验签流程 ``` 下游系统请求管理接口 │ ┌────────────────▼────────────────┐ │ 构造签名字符串 │ │ signingString = │ │ timestamp\n │ │ nonce\n │ │ requestBody │ │ │ │ signature = HMAC-SHA256( │ │ apiSecret, signingString) │ └────────────────┬────────────────┘ │ ┌────────────────▼────────────────┐ │ HTTP Request Headers: │ │ X-Api-Key: sk_xxx... │ │ X-Timestamp: 1719123456 │ │ X-Nonce: a1b2c3d4e5f6 │ │ X-Signature: ab12cd34... │ └────────────────┬────────────────┘ │ ▼ ┌────────────────────────────────┐ │ ApiKeyAuthMiddleware │ │ │ │ ① 检查四个 Header 是否存在 │ │ ② 验证 Timestamp (±5分钟) │ │ ③ 读取 Request Body │ │ ④ 查库: ApiKey → ApiSecret │ │ ⑤ HMAC-SHA256 重新计算签名 │ │ ⑥ 固定时间比较 (防时序攻击) │ │ │ │ ✓ 通过 → 放行到 Controller │ │ ✗ 失败 → 401 Unauthorized │ └────────────────────────────────┘ ``` --- ## 数据模型 ``` ┌──────────────────────┐ │ DownstreamSystem │ ├──────────────────────┤ │ Id GUID │ │ Name string │ │ ApiKey string │ ← 唯一索引 │ ApiSecret string │ │ CallbackUrl string? │ │ IsEnabled bool │ │ CreatedAt DateTime│ │ UpdatedAt DateTime│ └──────┬───────────────┘ │ 1 │ │ N ┌──────┴───────────────┐ ┌──────────────────────┐ │ SceneRouteRule │ │ FollowRecord │ ├──────────────────────┤ ├──────────────────────┤ │ Id GUID │ │ Id GUID │ │ SceneValue string │ │ OpenId string │ │ DownstreamSystemId │───▶│ EventType enum │ │ MiniProgramAppId? │ │ SceneValue string? │ │ Priority int │ │ EventKey string? │ │ IsEnabled bool │ │ DownstreamSystemId? │ │ CreatedAt DateTime│ │ CallbackSuccess bool? │ │ UpdatedAt DateTime│ │ CallbackError string? │ └──────────────────────┘ │ ProcessedAt DateTime? │ │ CreatedAt DateTime │ └──────────────────────┘ ``` --- ## 快速开始 ### 1. 配置微信参数 编辑 `src/WeChatAttentionHub.Api/appsettings.json`: ```json { "WeChat": { "AppId": "wx_your_mp_appid", "AppSecret": "your_mp_appsecret", "Token": "your_server_token", "EncodingAesKey": "" } } ``` ### 2. 启动服务 ```bash cd src/WeChatAttentionHub.Api dotnet run ``` SQLite 数据库自动创建在 `Data/app.db`,Swagger UI 访问 `http://localhost:5000/swagger`。 ### 3. 注册第一个下游系统 ```bash curl -X POST http://localhost:5000/api/management/systems \ -H "Content-Type: application/json" \ -d '{"name":"小程序A","callbackUrl":"https://miniapp-a.example.com/wechat/callback"}' ``` 返回的 `apiKey` 和 `apiSecret`(在响应头 `X-Api-Secret` 中)**请妥善保存**。 ### 4. 配置场景路由 ```bash # 使用刚获得的 apiKey/apiSecret 签名请求 curl -X POST http://localhost:5000/api/management/scene-routes \ -H "Content-Type: application/json" \ -H "X-Api-Key: " \ -H "X-Timestamp: $(date +%s)" \ -H "X-Nonce: $(openssl rand -hex 8)" \ -H "X-Signature: " \ -d '{"sceneValue":"100","downstreamSystemId":"","priority":10}' ``` ### 5. 公众号后台配置 在微信公众平台 → 开发 → 基本配置中,将服务器地址指向 `https://your-domain/api/wechat`。 --- ## API 接口一览 | 方法 | 路径 | 说明 | 验签 | |------|------|------|:----:| | `GET` | `/api/wechat` | 微信服务器 Token 验证 | - | | `POST` | `/api/wechat` | 接收微信消息/事件 | - | | `POST` | `/api/management/systems` | 注册下游系统 | ✓ | | `GET` | `/api/management/systems` | 下游系统列表 | ✓ | | `PUT` | `/api/management/systems/{id}` | 更新下游系统 | ✓ | | `DELETE` | `/api/management/systems/{id}` | 删除下游系统 | ✓ | | `POST` | `/api/management/systems/{id}/rotate-secret` | 轮换 Secret | ✓ | | `POST` | `/api/management/scene-routes` | 创建路由规则 | ✓ | | `GET` | `/api/management/scene-routes` | 路由规则列表 | ✓ | | `PUT` | `/api/management/scene-routes/{id}` | 更新路由规则 | ✓ | | `DELETE` | `/api/management/scene-routes/{id}` | 删除路由规则 | ✓ | | `GET` | `/api/management/follow-records` | 查询关注记录(分页) | ✓ | --- ## 回调负载格式 Hub 向下游系统 `CallbackUrl` 发送的 JSON: ```json { "event_type": "subscribe", "open_id": "oABC123xyz...", "scene_value": "100", "event_key": "qrscene_100", "timestamp": 1719123456, "record_id": "a1b2c3d4-..." } ``` 下游系统验证签名方式与 Hub 验签逻辑一致(`{timestamp}\n{nonce}\n{body}` → HMAC-SHA256)。 --- ## 技术栈 | 组件 | 版本 | |------|------| | .NET | 8.0 | | Senparc.Weixin.MP | 16.21.3 | | EF Core + SQLite | 8.0.0 | | Swashbuckle | 6.6.2 |