feat(iit-manager): Add WeChat Official Account integration for patient notifications
Features: - PatientWechatCallbackController for URL verification and message handling - PatientWechatService for template and customer messages - Support for secure mode (message encryption/decryption) - Simplified route /wechat/patient/callback for WeChat config - Event handlers for subscribe/unsubscribe/text messages - Template message for visit reminders Technical details: - Reuse @wecom/crypto for encryption (compatible with Official Account) - Relaxed Fastify schema validation to prevent early request blocking - Access token caching (7000s with 5min pre-refresh) - Comprehensive logging for debugging Testing: Local URL verification passed, ready for SAE deployment Status: Code complete, waiting for WeChat platform configuration
This commit is contained in:
532
backend/src/modules/iit-manager/docs/微信服务号接入指南.md
Normal file
532
backend/src/modules/iit-manager/docs/微信服务号接入指南.md
Normal file
@@ -0,0 +1,532 @@
|
||||
# 微信服务号接入指南(患者端)
|
||||
|
||||
> **版本**: v1.0
|
||||
> **创建日期**: 2026-01-04
|
||||
> **目标**: 为患者端接入微信服务号,实现访视提醒和消息推送
|
||||
> **预估工作量**: 3天
|
||||
|
||||
---
|
||||
|
||||
## 📋 一、准备工作清单
|
||||
|
||||
### 1.1 微信服务号信息
|
||||
|
||||
✅ **已完成**:
|
||||
- 服务号名称:`AI for 临床研究`
|
||||
- AppID:`wx062568ff49e4570c`
|
||||
- AppSecret:`c0d19435d1a1e948939c16d767ec0faf`
|
||||
- 认证状态:✅ 已认证(企业认证)
|
||||
- 主体名称:`北京壹证循科技有限公司`
|
||||
|
||||
### 1.2 需要配置的参数
|
||||
|
||||
🔧 **待配置**:
|
||||
1. **Token**(3-32位字符串)
|
||||
2. **EncodingAESKey**(43位字符串)
|
||||
3. **服务器URL**(回调地址)
|
||||
|
||||
---
|
||||
|
||||
## 🔐 二、生成Token和EncodingAESKey
|
||||
|
||||
### 2.1 Token生成(推荐使用随机字符串)
|
||||
|
||||
```bash
|
||||
# 方法1:使用OpenSSL(推荐)
|
||||
openssl rand -base64 24 | tr -d '/+=' | cut -c1-32
|
||||
|
||||
# 方法2:使用Node.js
|
||||
node -e "console.log(require('crypto').randomBytes(24).toString('base64').replace(/[\/\+=]/g, '').substring(0, 32))"
|
||||
|
||||
# 方法3:在线生成器
|
||||
# https://suijimimashengcheng.51240.com/
|
||||
```
|
||||
|
||||
**推荐Token**(示例,请重新生成):
|
||||
```
|
||||
IitPatientWechat2026Jan04Abc
|
||||
```
|
||||
|
||||
### 2.2 EncodingAESKey生成(必须43位)
|
||||
|
||||
```bash
|
||||
# 方法1:使用OpenSSL(推荐)
|
||||
openssl rand -base64 43 | tr -d '/+=' | head -c 43
|
||||
|
||||
# 方法2:使用Node.js
|
||||
node -e "console.log(require('crypto').randomBytes(32).toString('base64').replace(/[\/\+=]/g, '').substring(0, 43))"
|
||||
|
||||
# 方法3:微信公众平台随机生成(最简单)
|
||||
# 登录微信公众平台 → 基本配置 → 消息加密密钥 → 点击"随机生成"
|
||||
```
|
||||
|
||||
**推荐EncodingAESKey**(示例,请重新生成):
|
||||
```
|
||||
abcdefghijklmnopqrstuvwxyz0123456789ABC
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ 三、配置步骤(详细)
|
||||
|
||||
### 3.1 更新环境变量
|
||||
|
||||
编辑 `backend/.env` 文件,添加以下配置:
|
||||
|
||||
```env
|
||||
# ==========================================
|
||||
# 微信服务号配置(患者端)
|
||||
# ==========================================
|
||||
|
||||
# 微信服务号基础配置
|
||||
WECHAT_MP_APP_ID=wx062568ff49e4570c
|
||||
WECHAT_MP_APP_SECRET=c0d19435d1a1e948939c16d767ec0faf
|
||||
|
||||
# 微信服务号回调配置(消息加解密,安全模式)
|
||||
WECHAT_MP_TOKEN=IitPatientWechat2026Jan04Abc
|
||||
WECHAT_MP_ENCODING_AES_KEY=abcdefghijklmnopqrstuvwxyz0123456789ABC
|
||||
|
||||
# 微信小程序配置(可选,后续开发)
|
||||
WECHAT_MINI_APP_ID=
|
||||
```
|
||||
|
||||
⚠️ **注意**:
|
||||
1. Token和EncodingAESKey必须与微信公众平台配置的**完全一致**
|
||||
2. Token长度:3-32位,建议使用英文字母和数字
|
||||
3. EncodingAESKey长度:**必须43位**,大小写敏感
|
||||
|
||||
### 3.2 配置微信公众平台
|
||||
|
||||
#### Step 1: 登录微信公众平台
|
||||
|
||||
访问:https://mp.weixin.qq.com/
|
||||
|
||||
使用管理员微信扫码登录(账号:`zhi***ng`)
|
||||
|
||||
#### Step 2: 进入基本配置页面
|
||||
|
||||
```
|
||||
左侧菜单 → 设置与开发 → 基本配置
|
||||
```
|
||||
|
||||
#### Step 3: 配置服务器地址
|
||||
|
||||
找到 **"服务器配置"** 部分,点击 **"修改配置"**
|
||||
|
||||
填写以下信息:
|
||||
|
||||
| 配置项 | 值 | 说明 |
|
||||
|--------|-----|------|
|
||||
| **URL** | `https://iit.xunzhengyixue.com/api/v1/iit/patient-wechat/callback` | 生产环境回调URL |
|
||||
| **Token** | `IitPatientWechat2026Jan04Abc` | 与.env中的WECHAT_MP_TOKEN一致 |
|
||||
| **EncodingAESKey** | `abcdefghijklmnopqrstuvwxyz0123456789ABC` | 与.env中的WECHAT_MP_ENCODING_AES_KEY一致 |
|
||||
| **消息加解密方式** | ✅ **安全模式(推荐)** | 选择"安全模式" |
|
||||
| **数据格式** | ✅ **XML** | 默认选择 |
|
||||
|
||||
**本地开发环境**(使用natapp内网穿透):
|
||||
```
|
||||
URL: https://devlocal.xunzhengyixue.com/api/v1/iit/patient-wechat/callback
|
||||
```
|
||||
|
||||
#### Step 4: 点击"提交"并验证
|
||||
|
||||
微信会发送GET请求到您的服务器进行验证:
|
||||
|
||||
```
|
||||
GET https://iit.xunzhengyixue.com/api/v1/iit/patient-wechat/callback?signature=xxx×tamp=xxx&nonce=xxx&echostr=xxx
|
||||
```
|
||||
|
||||
**验证成功标志**:
|
||||
- ✅ 页面显示"配置成功"
|
||||
- ✅ "服务器配置"状态为"已启用"
|
||||
|
||||
**验证失败原因**:
|
||||
- ❌ Token不一致
|
||||
- ❌ 服务器无法访问(防火墙、未部署)
|
||||
- ❌ 代码逻辑错误(签名验证失败)
|
||||
|
||||
#### Step 5: 启用服务器配置
|
||||
|
||||
验证成功后,点击 **"启用"** 按钮。
|
||||
|
||||
⚠️ **注意**:启用后,公众号的消息和事件会推送到您的服务器,不会显示在公众平台后台。
|
||||
|
||||
---
|
||||
|
||||
## 🧪 四、测试验证
|
||||
|
||||
### 4.1 测试URL验证(手动测试)
|
||||
|
||||
在配置微信公众平台时,点击"提交"按钮会自动触发URL验证。
|
||||
|
||||
查看后端日志:
|
||||
|
||||
```bash
|
||||
# 本地开发
|
||||
cd D:\MyCursor\AIclinicalresearch\backend
|
||||
npm run dev
|
||||
|
||||
# 查看日志
|
||||
# 应该看到类似以下日志:
|
||||
# ✅ 微信服务号回调控制器初始化成功
|
||||
# 📥 收到微信服务号 URL 验证请求
|
||||
# ✅ URL 验证成功,返回 echostr
|
||||
```
|
||||
|
||||
### 4.2 测试脚本1:验证Token和AESKey配置
|
||||
|
||||
创建测试脚本 `backend/src/modules/iit-manager/test-patient-wechat-config.ts`:
|
||||
|
||||
```typescript
|
||||
import dotenv from 'dotenv';
|
||||
import crypto from 'crypto';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
console.log('🔧 微信服务号配置检查\n');
|
||||
|
||||
// 1. 检查必需的环境变量
|
||||
const requiredEnvs = [
|
||||
'WECHAT_MP_APP_ID',
|
||||
'WECHAT_MP_APP_SECRET',
|
||||
'WECHAT_MP_TOKEN',
|
||||
'WECHAT_MP_ENCODING_AES_KEY',
|
||||
];
|
||||
|
||||
let hasError = false;
|
||||
|
||||
requiredEnvs.forEach((key) => {
|
||||
const value = process.env[key];
|
||||
if (!value) {
|
||||
console.error(`❌ 缺少环境变量: ${key}`);
|
||||
hasError = true;
|
||||
} else {
|
||||
console.log(`✅ ${key}: ${value.substring(0, 10)}... (长度: ${value.length})`);
|
||||
}
|
||||
});
|
||||
|
||||
if (hasError) {
|
||||
console.error('\n❌ 配置不完整,请检查 .env 文件');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// 2. 验证Token长度
|
||||
const token = process.env.WECHAT_MP_TOKEN!;
|
||||
if (token.length < 3 || token.length > 32) {
|
||||
console.error(`\n❌ Token长度不正确: ${token.length}位(应为3-32位)`);
|
||||
hasError = true;
|
||||
} else {
|
||||
console.log(`\n✅ Token长度正确: ${token.length}位`);
|
||||
}
|
||||
|
||||
// 3. 验证EncodingAESKey长度
|
||||
const aesKey = process.env.WECHAT_MP_ENCODING_AES_KEY!;
|
||||
if (aesKey.length !== 43) {
|
||||
console.error(`❌ EncodingAESKey长度不正确: ${aesKey.length}位(必须43位)`);
|
||||
hasError = true;
|
||||
} else {
|
||||
console.log(`✅ EncodingAESKey长度正确: 43位`);
|
||||
}
|
||||
|
||||
// 4. 测试签名生成
|
||||
console.log('\n🔐 测试签名生成...');
|
||||
const timestamp = Date.now().toString();
|
||||
const nonce = Math.random().toString(36).substring(2);
|
||||
const arr = [token, timestamp, nonce].sort();
|
||||
const str = arr.join('');
|
||||
const signature = crypto.createHash('sha1').update(str).digest('hex');
|
||||
|
||||
console.log(`生成的签名: ${signature}`);
|
||||
console.log(`✅ 签名生成功能正常`);
|
||||
|
||||
// 5. 总结
|
||||
if (hasError) {
|
||||
console.error('\n❌ 配置检查失败,请修复错误后重试');
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log('\n✅ 配置检查通过!可以开始配置微信公众平台');
|
||||
console.log('\n📋 配置信息(用于微信公众平台):');
|
||||
console.log(`URL: https://iit.xunzhengyixue.com/api/v1/iit/patient-wechat/callback`);
|
||||
console.log(`Token: ${token}`);
|
||||
console.log(`EncodingAESKey: ${aesKey}`);
|
||||
console.log(`消息加解密方式: 安全模式(推荐)`);
|
||||
}
|
||||
```
|
||||
|
||||
**运行测试**:
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
npx tsx src/modules/iit-manager/test-patient-wechat-config.ts
|
||||
```
|
||||
|
||||
### 4.3 测试脚本2:模拟微信URL验证请求
|
||||
|
||||
创建测试脚本 `backend/src/modules/iit-manager/test-patient-wechat-url-verify.ts`:
|
||||
|
||||
```typescript
|
||||
import axios from 'axios';
|
||||
import crypto from 'crypto';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const BASE_URL = 'http://localhost:3001';
|
||||
const TOKEN = process.env.WECHAT_MP_TOKEN || '';
|
||||
|
||||
async function testUrlVerification() {
|
||||
console.log('🧪 测试微信服务号URL验证\n');
|
||||
|
||||
// 1. 准备参数
|
||||
const timestamp = Date.now().toString();
|
||||
const nonce = Math.random().toString(36).substring(2, 12);
|
||||
const echostr = 'test_echo_' + Math.random().toString(36).substring(2);
|
||||
|
||||
// 2. 生成签名
|
||||
const arr = [TOKEN, timestamp, nonce].sort();
|
||||
const str = arr.join('');
|
||||
const signature = crypto.createHash('sha1').update(str).digest('hex');
|
||||
|
||||
console.log('📝 请求参数:');
|
||||
console.log(` timestamp: ${timestamp}`);
|
||||
console.log(` nonce: ${nonce}`);
|
||||
console.log(` echostr: ${echostr}`);
|
||||
console.log(` signature: ${signature}\n`);
|
||||
|
||||
// 3. 发送GET请求
|
||||
try {
|
||||
const url = `${BASE_URL}/api/v1/iit/patient-wechat/callback`;
|
||||
const response = await axios.get(url, {
|
||||
params: {
|
||||
signature,
|
||||
timestamp,
|
||||
nonce,
|
||||
echostr,
|
||||
},
|
||||
});
|
||||
|
||||
console.log('✅ URL验证成功!');
|
||||
console.log(`返回内容: ${response.data}`);
|
||||
|
||||
if (response.data === echostr) {
|
||||
console.log('✅ 返回的echostr正确');
|
||||
} else {
|
||||
console.error('❌ 返回的echostr不正确');
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('❌ URL验证失败:', error.message);
|
||||
if (error.response) {
|
||||
console.error('响应状态:', error.response.status);
|
||||
console.error('响应内容:', error.response.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testUrlVerification();
|
||||
```
|
||||
|
||||
**运行测试**:
|
||||
|
||||
```bash
|
||||
# 先启动后端服务
|
||||
npm run dev
|
||||
|
||||
# 新开一个终端,运行测试
|
||||
npx tsx src/modules/iit-manager/test-patient-wechat-url-verify.ts
|
||||
```
|
||||
|
||||
### 4.4 测试关注事件
|
||||
|
||||
1. 用测试微信号关注公众号:`AI for 临床研究`
|
||||
2. 查看后端日志,应该看到:
|
||||
|
||||
```
|
||||
📥 收到微信服务号回调消息
|
||||
🔐 检测到加密消息,开始解密...
|
||||
✅ 消息解密成功
|
||||
📬 提取用户消息成功
|
||||
🎯 处理事件消息: subscribe
|
||||
👤 用户关注公众号: oXXXXXXXXXXXXXXXXX
|
||||
```
|
||||
|
||||
### 4.5 测试文本消息
|
||||
|
||||
1. 在公众号对话框发送文本消息:`你好`
|
||||
2. 查看后端日志,应该看到:
|
||||
|
||||
```
|
||||
📥 收到微信服务号回调消息
|
||||
🔐 检测到加密消息,开始解密...
|
||||
✅ 消息解密成功
|
||||
💬 处理文本消息: 你好
|
||||
📝 文本消息已记录
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 五、部署上线
|
||||
|
||||
### 5.1 本地开发环境(natapp内网穿透)
|
||||
|
||||
**1. 启动natapp**:
|
||||
|
||||
```bash
|
||||
# Windows
|
||||
cd D:\tools\natapp
|
||||
natapp.exe -authtoken=YOUR_TOKEN -subdomain=devlocal
|
||||
```
|
||||
|
||||
**2. 验证映射**:
|
||||
|
||||
访问:`https://devlocal.xunzhengyixue.com/api/v1/iit/health`
|
||||
|
||||
应该返回:
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"module": "iit-manager",
|
||||
"version": "1.1.0"
|
||||
}
|
||||
```
|
||||
|
||||
**3. 配置微信公众平台**:
|
||||
|
||||
```
|
||||
URL: https://devlocal.xunzhengyixue.com/api/v1/iit/patient-wechat/callback
|
||||
```
|
||||
|
||||
### 5.2 生产环境(SAE)
|
||||
|
||||
**1. 更新SAE环境变量**:
|
||||
|
||||
登录阿里云SAE控制台 → 应用管理 → 环境变量配置
|
||||
|
||||
添加以下环境变量:
|
||||
```
|
||||
WECHAT_MP_APP_ID=wx062568ff49e4570c
|
||||
WECHAT_MP_APP_SECRET=c0d19435d1a1e948939c16d767ec0faf
|
||||
WECHAT_MP_TOKEN=IitPatientWechat2026Jan04Abc
|
||||
WECHAT_MP_ENCODING_AES_KEY=abcdefghijklmnopqrstuvwxyz0123456789ABC
|
||||
```
|
||||
|
||||
**2. 部署代码**:
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
./deploy-to-sae.ps1
|
||||
```
|
||||
|
||||
**3. 验证部署**:
|
||||
|
||||
访问:`https://iit.xunzhengyixue.com/api/v1/iit/health`
|
||||
|
||||
**4. 配置微信公众平台**:
|
||||
|
||||
```
|
||||
URL: https://iit.xunzhengyixue.com/api/v1/iit/patient-wechat/callback
|
||||
```
|
||||
|
||||
**5. 配置IP白名单**(重要):
|
||||
|
||||
登录微信公众平台 → 基本配置 → IP白名单
|
||||
|
||||
添加SAE应用的出口IP(可以从SAE控制台查看)
|
||||
|
||||
---
|
||||
|
||||
## 📋 六、常见问题排查
|
||||
|
||||
### Q1: URL验证失败,提示"Token验证失败"
|
||||
|
||||
**原因**:
|
||||
- Token配置不一致(大小写、多余空格)
|
||||
- 签名计算错误
|
||||
|
||||
**解决方法**:
|
||||
1. 检查 `.env` 文件中的 `WECHAT_MP_TOKEN` 是否与微信公众平台配置一致
|
||||
2. 运行配置检查脚本:`npx tsx src/modules/iit-manager/test-patient-wechat-config.ts`
|
||||
3. 查看后端日志,确认签名计算过程
|
||||
|
||||
### Q2: URL验证失败,提示"请求URL超时"
|
||||
|
||||
**原因**:
|
||||
- 服务器未启动
|
||||
- 防火墙阻止
|
||||
- URL配置错误
|
||||
|
||||
**解决方法**:
|
||||
1. 确认后端服务已启动:`npm run dev`
|
||||
2. 本地开发确认natapp已启动
|
||||
3. 生产环境确认SAE应用状态正常
|
||||
4. 使用浏览器直接访问健康检查接口测试连通性
|
||||
|
||||
### Q3: 消息解密失败
|
||||
|
||||
**原因**:
|
||||
- EncodingAESKey配置不一致
|
||||
- EncodingAESKey长度不正确(必须43位)
|
||||
|
||||
**解决方法**:
|
||||
1. 检查 `.env` 文件中的 `WECHAT_MP_ENCODING_AES_KEY` 长度是否为43位
|
||||
2. 确认与微信公众平台配置完全一致(包括大小写)
|
||||
3. 重新生成EncodingAESKey并同步更新
|
||||
|
||||
### Q4: 收不到用户消息
|
||||
|
||||
**原因**:
|
||||
- 服务器配置未启用
|
||||
- 回调URL配置错误
|
||||
- 服务端代码异常
|
||||
|
||||
**解决方法**:
|
||||
1. 登录微信公众平台,确认"服务器配置"状态为"已启用"
|
||||
2. 查看后端日志,确认是否收到POST请求
|
||||
3. 检查是否有异常日志
|
||||
|
||||
---
|
||||
|
||||
## 📝 七、后续开发计划
|
||||
|
||||
### Phase 1: 基础消息推送(当前)
|
||||
|
||||
- [x] 创建PatientWechatCallbackController
|
||||
- [x] 创建PatientWechatService
|
||||
- [x] 配置路由和环境变量
|
||||
- [ ] 测试URL验证
|
||||
- [ ] 测试消息接收
|
||||
|
||||
### Phase 2: 患者绑定功能
|
||||
|
||||
- [ ] 创建患者绑定数据表
|
||||
- [ ] 开发患者绑定H5页面
|
||||
- [ ] 实现手机号验证码功能
|
||||
- [ ] 实现患者身份验证逻辑
|
||||
|
||||
### Phase 3: 模板消息推送
|
||||
|
||||
- [ ] 申请模板消息权限
|
||||
- [ ] 设计访视提醒模板
|
||||
- [ ] 开发定时任务检测到期访视
|
||||
- [ ] 实现模板消息推送
|
||||
|
||||
### Phase 4: 微信小程序
|
||||
|
||||
- [ ] 注册微信小程序
|
||||
- [ ] 搭建小程序框架
|
||||
- [ ] 开发核心页面
|
||||
- [ ] 前后端联调
|
||||
|
||||
---
|
||||
|
||||
## 📞 联系方式
|
||||
|
||||
如有问题,请联系:
|
||||
- 技术负责人:冯志博
|
||||
- 邮箱:gofeng117@163.com
|
||||
- 微信:aiforresearch
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:2026-01-04
|
||||
**文档版本**:v1.0
|
||||
|
||||
Reference in New Issue
Block a user