# Day 3 - 企业微信集成开发完成记录 **开发日期**:2026-01-02 **开发者**:AI + 用户协作 **版本**:v1.3 **状态**:✅ 企业微信URL验证成功,基础集成完成 --- ## 📊 开发概览 ### 目标 实现企业微信集成,包括消息推送和回调处理,为 IIT Manager Agent 建立与 PI 的沟通渠道。 ### 成果 - ✅ 企业微信消息推送服务(WechatService) - ✅ 企业微信回调处理(WechatCallbackController) - ✅ URL验证测试通过 - ✅ 完善质控Worker,支持企业微信推送 - ✅ natapp内网穿透配置成功 ### 进度 - 模块整体完成度:**35% → 50%** - 企业微信集成:**0% → 80%**(URL验证完成,待端到端测试) --- ## 🏗️ 架构设计 ### 核心组件 ``` ┌─────────────────────────────────────────────────────────────┐ │ 企业微信集成架构 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ REDCap DET ──→ WebhookController ──→ JobQueue │ │ ↓ ↓ │ │ Audit Logs iit_quality_check │ │ Worker │ │ ↓ │ │ WechatService │ │ ↓ │ │ 企业微信 API │ │ ↓ │ │ PI 手机 │ │ │ │ 企业微信消息 ──→ WechatCallbackController ──→ AI处理 │ │ ↓ │ │ WechatService ──→ 主动推送回复 │ │ │ └─────────────────────────────────────────────────────────────┘ ``` ### 技术选型 | 组件 | 技术方案 | 原因 | |------|----------|------| | 消息加解密 | @wecom/crypto | 企业微信官方推荐库 | | XML解析 | xml2js | 成熟稳定的XML解析库 | | 内网穿透 | natapp | 本地开发调试 | | 异步处理 | setImmediate | 规避5秒超时限制 | --- ## 💻 代码实现 ### 1. WechatService.ts(企业微信推送服务) **文件路径**:`backend/src/modules/iit-manager/services/WechatService.ts` **代码行数**:314行 **核心功能**: ```typescript class WechatService { // 1. Access Token 管理(缓存 + 自动刷新) async getAccessToken(): Promise // 2. 发送文本消息 async sendTextMessage(userId: string, content: string): Promise // 3. 发送 Markdown 消息 async sendMarkdownMessage(userId: string, content: string): Promise // 4. 审计日志记录 private async recordAuditLog(data): Promise } ``` **技术亮点**: - ✅ Access Token 缓存机制(7200秒,提前5分钟刷新) - ✅ 完整的错误处理和重试机制 - ✅ 详细的日志记录(corpId、agentId等) - ✅ 审计日志自动记录 **环境变量**: ```env WECHAT_CORP_ID=ww6ab493470ab4f377 WECHAT_AGENT_ID=1000002 WECHAT_CORP_SECRET=AZIVxMtoLb0rEszXS81e4dBRl-I9kgTjygIS0cFfENU ``` --- ### 2. WechatCallbackController.ts(企业微信回调处理) **文件路径**:`backend/src/modules/iit-manager/controllers/WechatCallbackController.ts` **代码行数**:501行 **核心功能**: ```typescript class WechatCallbackController { // 1. URL 验证(GET 请求) async handleVerification(request, reply): Promise // 2. 消息接收(POST 请求 + 异步处理) async handleCallback(request, reply): Promise // 3. 异步消息处理 private async processMessageAsync(...): Promise // 4. 用户消息处理(关键词匹配 + 业务逻辑) private async processUserMessage(message): Promise // 5. 签名验证 private verifySignature(...): boolean } ``` **技术亮点**: - ✅ **异步回复模式**:立即返回`"success"`,后台异步处理(规避5秒超时) - ✅ **消息解密**:使用 `@wecom/crypto` 的 `decrypt(encodingAESKey, encrypt)` 函数 - ✅ **签名验证**:使用 `@wecom/crypto` 的 `getSignature(token, timestamp, nonce, data)` 函数 - ✅ **意图识别**:支持"汇总"、"帮助"、"新患者"等关键词 - ✅ **主动推送**:处理完成后主动调用 WechatService 推送回复 **环境变量**: ```env WECHAT_TOKEN=oXlRBm1YnvMy2SbDLbvAdDd5Gq3oBGq WECHAT_ENCODING_AES_KEY=v88eT3O9bMW897h4btr7v7qvQImlMf31edTQCmuhOhO ``` --- ### 3. 路由配置(routes/index.ts) **新增路由**: ```typescript // GET: URL验证(企业微信配置回调URL时使用) fastify.get('/api/v1/iit/wechat/callback', wechatCallbackController.handleVerification.bind(wechatCallbackController) ); // POST: 接收企业微信消息 fastify.post('/api/v1/iit/wechat/callback', wechatCallbackController.handleCallback.bind(wechatCallbackController) ); ``` --- ### 4. 完善质控Worker(index.ts) **文件路径**:`backend/src/modules/iit-manager/index.ts` **新增功能**:质控完成后自动推送企业微信通知 **代码逻辑**: ```typescript jobQueue.process('iit_quality_check', async (job) => { // 1. 获取项目配置 const project = await prisma.$queryRaw`...`; const piUserId = project.notification_config.wechat_user_id; // 2. 执行质控检查 const qualityCheckResult = await performSimpleQualityCheck(...); // 3. 构建企业微信通知消息 const message = buildWechatNotification(...); // 4. 推送到企业微信 await wechatService.sendTextMessage(piUserId, message); }); ``` **通知消息格式**: ``` 📊 IIT Manager 数据录入通知 项目:test0102 记录ID:xxx 表单:xxx 时间:2026-01-02 23:55:00 💡 质控建议 (3项): 1. ✅ 数据录入及时(5分钟内) 2. ✅ 记录ID有效 3. ✅ 表单:demographics ✅ 数据质量良好,无明显问题 💬 如有疑问,请回复"帮助"查看更多功能 ``` --- ## 🔧 开发过程与问题解决 ### 问题1:环境变量名称不一致 ⚠️ **现象**: ``` hasSecret: false Error: 企业微信配置不完整,请检查环境变量 ``` **原因**: - 环境变量:`WECHAT_CORP_SECRET` - 代码读取:`WECHAT_AGENT_SECRET` **解决方案**: ```typescript // 修改前 agentSecret: process.env.WECHAT_AGENT_SECRET // 修改后 agentSecret: process.env.WECHAT_CORP_SECRET ``` --- ### 问题2:@wecom/crypto 导入方式错误 ❌ **现象**: ``` TypeError: WXBizMsgCrypt is not a constructor ``` **原因**: `@wecom/crypto` 不是一个类,而是导出了4个独立的函数: ```javascript { decrypt: [Function: decrypt], encrypt: [Function: encrypt], getJsApiSignature: [Function: getJsApiSignature], getSignature: [Function: getSignature] } ``` **解决方案**: ```typescript // 修改前(错误) import WXBizMsgCrypt from '@wecom/crypto'; this.wxcrypt = new WXBizMsgCrypt(token, aesKey, corpId); // 修改后(正确) const require = createRequire(import.meta.url); const { decrypt, encrypt, getSignature } = require('@wecom/crypto'); ``` --- ### 问题3:decrypt 函数参数错误 ❌ **现象**: ``` Error: invalid encodingAESKey ``` **原因**: 通过测试脚本发现,`decrypt` 函数只需要 **2个参数**: ```javascript function decrypt(encodingAESKey, encrypt) { ... } ``` **解决方案**: ```typescript // 修改前(错误 - 4个参数) const result = decrypt(this.token, this.encodingAESKey, this.corpId, echostr); // 修改后(正确 - 2个参数) const result = decrypt(this.encodingAESKey, echostr); ``` --- ### 问题4:Token字符识别错误 ⚠️ **现象**: ``` ⚠️ 签名验证失败 expected: 0b7cf05d6cb23ab9ce2efca6fdc659f32051eabe calculated: 6f79cabd3e9eea5eb10f55abdcf087ce6393d51d ``` **原因**: Token的第3个字符容易混淆: - `oX1R...`(数字1) - `oXlR...`(小写字母l) 后端日志显示的是 `oXlR...`(小写l),而调试工具中可能输入了数字1。 **解决方案**: - 直接从 `.env` 文件复制粘贴,避免手动输入 - 确认 Token 为:`oXlRBm1YnvMy2SbDLbvAdDd5Gq3oBGq` --- ### 问题5:EncodingAESKey 更新 🔄 **现象**: 旧的 EncodingAESKey 可能格式有问题导致解密失败。 **解决方案**: 在企业微信管理后台重新生成: ``` 旧值:zE4tcdBeekCHPUV015jCh9RVUydnCITINqSmCzg9xtO 新值:v88eT3O9bMW897h4btr7v7qvQImlMf31edTQCmuhOhO(43位,格式正确) ``` --- ### 问题6:natapp 内网穿透配置 🌐 **需求**: 本地开发环境需要公网 HTTPS URL 用于企业微信回调。 **解决方案**: 1. 使用 natapp 服务 2. 配置隧道:`http://iit.nat100.top` → `127.0.0.1:3001` 3. natapp 自动提供 HTTPS 支持 **验证**: ```bash curl https://iit.nat100.top/api/v1/iit/health # 返回:{"status":"ok","module":"iit-manager",...} ``` --- ## 🧪 测试验证 ### 测试1:企业微信开发者调试工具验证 ✅ **工具**:企业微信管理后台 → 开发者工具 → 测试回调模式 **配置**: ``` URL: https://iit.nat100.top/api/v1/iit/wechat/callback Token: oXlRBm1YnvMy2SbDLbvAdDd5Gq3oBGq EncodingAESKey: v88eT3O9bMW897h4btr7v7qvQImlMf31edTQCmuhOhO EchoStr: test12345678901234567890123 ToUserName: ww6ab493470ab4f377 ``` **测试结果**: ``` ✅ 返回状态:request: 成功 ✅ 返回结果:123456789012345678901 25(解密后的23位字符) ✅ HTTP状态码:200 ``` **后端日志**: ``` 📥 收到企业微信 URL 验证请求 nonce: "95zbplrrko5" echostrLength: 88 ✅ URL 验证成功 decryptedLength: 23 statusCode: 200 ``` --- ### 测试2:natapp 隧道连通性测试 ✅ **测试命令**: ```bash curl https://iit.nat100.top/api/v1/iit/health ``` **返回结果**: ```json { "status": "ok", "module": "iit-manager", "version": "1.1.0", "timestamp": "2026-01-02T15:53:06.000Z" } ``` --- ## 📋 配置清单 ### 后端环境变量(backend/.env) ```env # ========================================== # 企业微信配置 # ========================================== # 企业微信基础配置(应用信息) WECHAT_CORP_ID=ww6ab493470ab4f377 WECHAT_AGENT_ID=1000002 WECHAT_CORP_SECRET=AZIVxMtoLb0rEszXS81e4dBRl-I9kgTjygIS0cFfENU # 企业微信回调配置(消息加解密) WECHAT_TOKEN=oXlRBm1YnvMy2SbDLbvAdDd5Gq3oBGq WECHAT_ENCODING_AES_KEY=v88eT3O9bMW897h4btr7v7qvQImlMf31edTQCmuhOhO ``` ### 企业微信应用配置 **应用信息**: - 企业ID:`ww6ab493470ab4f377` - 应用名称:`IIT Manager Agent` - AgentID:`1000002` **回调URL配置**(待正式保存): ``` URL: https://iit.nat100.top/api/v1/iit/wechat/callback Token: oXlRBm1YnvMy2SbDLbvAdDd5Gq3oBGq EncodingAESKey: v88eT3O9bMW897h4btr7v7qvQImlMf31edTQCmuhOhO ``` **可信域名**: ``` iit.xunzhengyixue.com(SAE生产环境) ``` ### natapp 配置 ``` 隧道状态:Online 公网URL:http://iit.nat100.top 本地端口:127.0.0.1:3001 HTTPS:自动支持 ``` --- ## 📊 代码统计 | 文件 | 代码行数 | 主要功能 | |------|---------|---------| | WechatService.ts | 314行 | 企业微信消息推送 | | WechatCallbackController.ts | 501行 | 企业微信回调处理 | | index.ts(质控Worker) | +80行 | 质控完成后推送通知 | | routes/index.ts | +48行 | 企业微信路由注册 | | **总计** | **~943行** | 企业微信集成核心代码 | --- ## ✅ 已完成的功能 - [x] 企业微信 Access Token 管理(缓存+刷新) - [x] 发送文本消息到企业微信 - [x] 发送 Markdown 消息到企业微信 - [x] 企业微信 URL 验证(GET请求处理) - [x] 企业微信消息接收(POST请求处理) - [x] 消息解密(使用 @wecom/crypto) - [x] 签名验证(使用 @wecom/crypto) - [x] 异步回复模式(规避5秒超时) - [x] 关键词意图识别(汇总、帮助、新患者) - [x] 质控Worker推送企业微信通知 - [x] 审计日志记录 - [x] natapp 内网穿透配置 --- ## ⏳ 待完成的功能 - [ ] 保存正式的企业微信回调URL配置 - [ ] 配置数据库中的 `wechat_user_id`(PI的企业微信UserID) - [ ] 端到端测试(REDCap → 企微推送) - [ ] LLM意图识别(升级关键词匹配) - [ ] 对话功能完善(更多业务场景) - [ ] IP白名单配置(部署到SAE时) --- ## 🚀 下一步计划 ### Day 3 下午/晚上(可选) 1. **保存企业微信正式配置**(5分钟) - 在企业微信管理后台保存回调URL配置 - 勾选需要接收的消息类型 2. **配置项目通知**(10分钟) - 获取 PI 的企业微信 UserID - 更新数据库 `projects` 表的 `notification_config` 字段 3. **端到端测试**(30分钟) - 在 REDCap 中录入测试数据 - 验证企业微信收到实时通知 - 测试对话功能(发送"帮助"、"汇总"等关键词) ### Day 4(后续优化) 1. **LLM意图识别** - 接入 DeepSeek 或其他 LLM - 实现真正的 AI Agent 对话 2. **功能完善** - 更多对话场景(数据查询、统计分析) - 错误处理优化 - 性能监控 3. **文档编写** - 使用手册 - API 文档 - 部署指南 --- ## 📖 参考文档 - [企业微信API文档](https://developer.work.weixin.qq.com/document/path/90664) - [企业微信消息加解密说明](https://developer.work.weixin.qq.com/document/path/90968) - [@wecom/crypto GitHub](https://github.com/wecomteam/crypto) - [最小MVP闭环开发计划](../04-开发计划/最小MVP闭环开发计划.md) --- ## 🎉 总结 Day 3 的开发工作虽然遇到了多个技术问题,但最终成功完成了企业微信集成的核心功能。通过调试工具的验证,证明了: 1. ✅ **技术方案可行**:@wecom/crypto 库正常工作 2. ✅ **架构设计合理**:异步回复模式有效规避超时问题 3. ✅ **代码质量良好**:详细的日志和错误处理 4. ✅ **开发流程完善**:问题排查→测试验证→文档记录 **距离完整的 MVP 闭环只差最后的端到端测试了!** --- **记录人**:AI Assistant **审核人**:开发团队 **文档版本**:v1.0 **最后更新**:2026-01-02 23:55:00