# 云端最小回调壳部署方案 ## 一、目标 **阶段目标**:在备案域名上部署最小可用回调壳,使企业微信能完成 URL 校验与回调联调。 **最小功能范围**: - ✅ `/api/wecom/callback` GET 校验(兼容 `signature`/`msg_signature`) - ✅ `/api/wecom/callback` POST 密文消息回调(验签、解密、echo 回复) - ✅ 结构化日志 + trace_id - ✅ Nginx 反代 + HTTPS(Let's Encrypt) - ⏸️ 数据库(可先不启用,但接口与配置要预留) - ⏸️ Admin 后台(可先占位) --- ## 二、架构 ``` 企业微信 → HTTPS (443) → Nginx → Backend (8000) ↓ PostgreSQL (可选) ``` **服务清单**: - `backend`: Python 3.12 + FastAPI + Uvicorn(最小回调壳) - `nginx`: 反代 + HTTPS(Let's Encrypt) - `db`: PostgreSQL 16(可选,先不启用) --- ## 三、环境变量配置 ### 3.1 必需变量(`.env`) ```bash # ============ Backend ============ API_HOST=0.0.0.0 API_PORT=8000 # Database(可选,先不启用) DATABASE_URL=postgresql+asyncpg://wecom:wecom_secret@db:5432/wecom_ai DATABASE_URL_SYNC=postgresql://wecom:wecom_secret@db:5432/wecom_ai # JWT(admin 登录,可选) JWT_SECRET=your-jwt-secret-change-in-production JWT_ALGORITHM=HS256 JWT_EXPIRE_MINUTES=60 # WeCom Callback(必须,从企业微信管理后台获取) WECOM_CORP_ID=你的企业ID WECOM_AGENT_ID=你的应用AgentId WECOM_SECRET=你的应用Secret(可选,用于主动发送消息) WECOM_TOKEN=你的Token(必须与企微后台一致) WECOM_ENCODING_AES_KEY=你的43位密钥(必须与企微后台一致) # WeCom API WECOM_API_BASE=https://qyapi.weixin.qq.com WECOM_API_TIMEOUT=10 WECOM_API_RETRIES=2 # Log LOG_LEVEL=INFO LOG_JSON=true # ============ Nginx ============ # 域名(必须,备案域名) DOMAIN=your-domain.com # SSL(Let's Encrypt) SSL_EMAIL=your-email@example.com ``` ### 3.2 关键变量说明 | 变量 | 说明 | 来源 | |------|------|------| | `WECOM_TOKEN` | 企业微信回调 Token | 企微后台 → 应用 → 接收消息 → Token | | `WECOM_ENCODING_AES_KEY` | 43 位 Base64 编码密钥 | 企微后台 → 应用 → 接收消息 → EncodingAESKey | | `WECOM_CORP_ID` | 企业 ID | 企微后台 → 我的企业 → 企业信息 | | `WECOM_AGENT_ID` | 应用 AgentId | 企微后台 → 应用管理 → 自建应用 → 应用详情 | | `DOMAIN` | 备案域名 | 你的域名服务商 | --- ## 四、部署步骤 ### 4.1 前置条件 1. **备案域名**:已备案且主体关联的域名(例如:`api.yourdomain.com`) 2. **服务器**:Linux(Ubuntu 20.04+ / CentOS 7+),公网 IP,开放 80/443 端口 3. **Docker**:已安装 Docker 和 docker-compose 4. **GitHub**:代码已推送到 GitHub(用于 CI/CD) ### 4.2 服务器初始化 ```bash # 1. 安装 Docker 和 docker-compose curl -fsSL https://get.docker.com | sh sudo usermod -aG docker $USER sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose # 2. 克隆项目(或通过 CI/CD 部署) git clone https://github.com/your-org/wecom-ai-assistant.git cd wecom-ai-assistant # 3. 创建 .env 文件 cp .env.example .env # 编辑 .env,填入上述必需变量 ``` ### 4.3 配置 Nginx + HTTPS #### 方案 A:使用 Certbot(Let's Encrypt) ```bash # 1. 安装 Certbot sudo apt-get update sudo apt-get install certbot python3-certbot-nginx # 2. 先启动 HTTP 服务(用于验证) docker-compose up -d backend # 3. 配置 Nginx(临时 HTTP 配置) # 编辑 deploy/nginx.conf,添加 server_name # 然后运行:docker-compose up -d nginx # 4. 获取 SSL 证书 sudo certbot --nginx -d your-domain.com -d www.your-domain.com --email your-email@example.com --agree-tos --non-interactive # 5. 更新 nginx.conf,使用 Certbot 生成的配置 # Certbot 会自动修改 /etc/nginx/sites-available/default # 将配置复制到 deploy/nginx.conf,或使用 volume 挂载 ``` #### 方案 B:手动配置 Nginx + Let's Encrypt 创建 `deploy/nginx-ssl.conf`: ```nginx events { worker_connections 1024; } http { upstream backend { server backend:8000; } # HTTP → HTTPS 重定向 server { listen 80; server_name your-domain.com www.your-domain.com; return 301 https://$server_name$request_uri; } # HTTPS server { listen 443 ssl http2; server_name your-domain.com www.your-domain.com; # SSL 证书(Let's Encrypt) ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; # /api -> backend location /api/ { proxy_pass http://backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 30s; } # 健康检查 location /health { proxy_pass http://backend/health; access_log off; } } } ``` 更新 `docker-compose.yml`: ```yaml nginx: image: nginx:alpine ports: - "80:80" - "443:443" volumes: - ./deploy/nginx-ssl.conf:/etc/nginx/nginx.conf:ro - /etc/letsencrypt:/etc/letsencrypt:ro # SSL 证书 depends_on: - backend ``` ### 4.4 启动服务 ```bash # 1. 构建镜像 docker-compose build backend # 2. 启动服务(最小回调壳:只启动 backend + nginx) docker-compose up -d backend nginx # 3. 检查日志 docker-compose logs -f backend ``` ### 4.5 验证服务 ```bash # 1. 检查服务状态 docker-compose ps # 2. 检查健康检查 curl https://your-domain.com/health # 3. 检查回调接口(应返回 400,因为缺少参数) curl https://your-domain.com/api/wecom/callback ``` --- ## 五、本地验证 ### 5.1 本地启动最小回调壳 ```bash # 1. 启动后端(不启动 db/admin) docker-compose up -d backend # 2. 检查日志 docker-compose logs backend # 3. 测试 GET 校验(模拟企业微信) # 注意:需要正确的 signature/timestamp/nonce/echostr curl "http://localhost:8000/api/wecom/callback?signature=xxx×tamp=123&nonce=abc&echostr=xxx" ``` ### 5.2 本地测试 POST 回调 ```bash # 使用企业微信官方测试工具生成测试请求 # 或使用 curl 模拟(需要正确的签名和加密) curl -X POST "http://localhost:8000/api/wecom/callback?msg_signature=xxx×tamp=123&nonce=abc" \ -H "Content-Type: application/xml" \ -d '' ``` ### 5.3 验证日志格式 检查日志输出是否符合结构化日志格式: ```json { "timestamp": "2025-02-05T10:00:00Z", "level": "INFO", "message": "wecom verify success", "trace_id": "abc123", "echostr_length": 43 } ``` --- ## 六、线上验证 ### 6.1 企业微信后台配置 1. **登录企业微信管理后台**:https://work.weixin.qq.com 2. **进入应用设置**:应用管理 → 自建应用 → 选择你的应用 3. **配置回调 URL**: - 接收消息服务器 URL:`https://your-domain.com/api/wecom/callback` - Token:与 `.env` 中的 `WECOM_TOKEN` **完全一致** - EncodingAESKey:与 `.env` 中的 `WECOM_ENCODING_AES_KEY` **完全一致** - 消息加解密方式:**安全模式** 4. **点击保存** ### 6.2 验证 GET 校验 保存后,企业微信会立即发送 GET 请求验证。观察后端日志: ```bash docker-compose logs -f backend ``` **成功日志**: ``` INFO: wecom verify success {"trace_id": "...", "echostr_length": 43} ``` **失败日志**: ``` WARNING: wecom verify failed {"trace_id": "...", "timestamp": "...", "nonce": "..."} ``` 如果验证失败,检查: - Token 是否一致 - EncodingAESKey 是否一致 - 域名是否可访问(`curl https://your-domain.com/api/wecom/callback`) ### 6.3 验证 POST 回调 1. **在企业微信中发送测试消息**: - 打开企业微信客户端 - 找到你配置的应用 - 发送文本消息:`你好,测试一下` 2. **观察后端日志**: ```bash docker-compose logs -f backend ``` **成功日志**: ```json { "timestamp": "2025-02-05T10:00:00Z", "level": "INFO", "message": "wecom message received", "trace_id": "abc123", "external_userid": "external_userid_xxx", "msgid": "123456", "msg_type": "text", "content_summary": "你好,测试一下" } ``` ```json { "timestamp": "2025-02-05T10:00:01Z", "level": "INFO", "message": "wecom reply sent", "trace_id": "abc123", "external_userid": "external_userid_xxx", "msgid": "123456", "reply_summary": "已收到:你好,测试一下" } ``` 3. **检查企业微信客户端**:应收到回复:`已收到:你好,测试一下` --- ## 七、常见问题 ### 7.1 GET 校验失败 **原因**: - Token 不一致 - EncodingAESKey 不一致 - 签名算法错误 **解决**: 1. 检查 `.env` 中的 `WECOM_TOKEN` 和 `WECOM_ENCODING_AES_KEY` 2. 确保与企微后台配置**完全一致**(包括大小写、空格) 3. 重启后端:`docker-compose restart backend` ### 7.2 POST 回调失败 **原因**: - 签名验证失败 - 解密失败 - XML 解析失败 **解决**: 1. 检查日志中的错误信息 2. 确认 EncodingAESKey 正确 3. 确认消息加解密方式为**安全模式** ### 7.3 HTTPS 证书问题 **原因**: - Let's Encrypt 证书未正确配置 - 证书过期 **解决**: 1. 检查证书:`sudo certbot certificates` 2. 续期证书:`sudo certbot renew` 3. 重启 Nginx:`docker-compose restart nginx` ### 7.4 域名无法访问 **原因**: - DNS 未解析 - 防火墙未开放 80/443 端口 - Nginx 配置错误 **解决**: 1. 检查 DNS:`nslookup your-domain.com` 2. 检查端口:`netstat -tlnp | grep -E '80|443'` 3. 检查 Nginx 日志:`docker-compose logs nginx` --- ## 八、下一步 完成最小回调壳部署后,按以下顺序逐步接入: 1. ✅ **最小回调壳**(当前阶段) 2. ⏭️ **数据库接入**(会话与消息入库) 3. ⏭️ **Admin 后台**(会话列表、工单、知识库) 4. ⏭️ **FAQ/RAG**(智能回复) --- ## 九、参考文档 - [企业微信回调配置](./wecom.md) - [企业微信测试指南](./wecom-test-guide.md) - [GitHub Actions CI/CD](../deploy/ci/github-actions.yml)