Files
zyt/server/docs/qywx-im-deploy.md
T
2026-05-11 17:49:38 +08:00

99 lines
5.6 KiB
Markdown
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 企业微信 员工↔客户 IM 功能部署说明
本文档对应「管理后台 IM」功能(`admin/src/views/chat`),负责在后端/后台视角下的**收/发**一对一消息。
- 发送:企业群发 `add_msg_template`(员工本人身份 + 手机端确认)
- 接收:会话内容存档(官方 C SDK + RSA 解密 + 计划任务拉取)
- 老回调接口 `QywxExternalContactCallbackController` 仍用于 **添加/编辑/删除外部联系人** 事件,不承载消息内容。
---
## 1. 数据表
执行一次:
```bash
mysql -u<user> -p<pass> <db> < server/database/migrations/2026_04_20_create_qywx_msg_tables.sql
```
新增 5 张:`qywx_msg_session``qywx_msg_archive``qywx_msg_archive_media``qywx_msg_send_task``qywx_msg_archive_cursor`
## 2. 配置(server/config/pay.php → wechat_work
```php
'customer_contact_secret' => '客户联系 Secret', // 已有:发消息 / 客户列表
// 会话内容存档
'msgaudit_enabled' => true, // 开关
'msgaudit_secret' => '会话存档 Secret', // 独立应用 Secret
'msgaudit_public_key_ver' => 1, // 管理后台上传公钥返回的版本号
'msgaudit_private_key_path'=> '/data/qywx/priv.pem', // RSA 私钥文件路径(建议)
'msgaudit_sdk_lib_path' => '/data/qywx/libWeWorkFinanceSdk_C.so', // SDK 动态库
'msgaudit_timeout' => 60,
'msgaudit_limit' => 500,
```
> `msgaudit_enabled = false` 或 `msgaudit_sdk_lib_path` 留空时,后端不会崩溃——拉取命令会以 `noop` 方式返回,方便「先上线、再接 SDK」。
## 3. C SDK & 私钥准备
1. 从企微管理后台 → 安全与合规 → 会话内容存档 → SDK 下载区,下载官方 C SDK。
2. Linux 部署 `libWeWorkFinanceSdk_C.so``/data/qywx/`,并确保 PHP-FPM 账户有可执行权限。
3. **安装 PHP FFI**`php.ini` 开启 `ffi.enable=true`preload 场景用 `preload`CLI + FPM 用 `true`)。
4. 生成 RSA 密钥对:`openssl genrsa -out priv.pem 2048 && openssl rsa -in priv.pem -pubout -out pub.pem`
5. 企微后台上传 `pub.pem` → 记录返回的 `public_key_ver` → 回填到配置。
6. 私钥 `priv.pem` 放到 `msgaudit_private_key_path` 指向的位置,权限 600。
> 轮换公钥时,**保留历史 `public_key_ver → private_key` 映射**(SDK 会用旧消息的版本号去找旧私钥)。如需多版本,代码已预留:在 `WeComFinanceSdkClient::loadPrivateKeys()` 同一配置下,可额外传入 `msgaudit_private_keys` 数组形式支持多版本(自行扩展)。
## 4. 计划任务
推荐 30 秒一次(企微限制:每分钟不超过 600 次)。
Linux crontab(每 30s 跑一次的 2 行写法):
```
* * * * * cd /path/to/server && /usr/bin/php think qywx:sync-msg-archive --download >/dev/null 2>&1
* * * * * cd /path/to/server && sleep 30 && /usr/bin/php think qywx:sync-msg-archive --download >/dev/null 2>&1
```
可选:把媒体下载拆开、并发运行:
```
* * * * * cd /path/to/server && /usr/bin/php think qywx:sync-msg-archive >/dev/null 2>&1
*/2 * * * * cd /path/to/server && /usr/bin/php think qywx:sync-msg-archive --only-media --max-media=500 >/dev/null 2>&1
```
命令已做 `flock` 互斥(`runtime/qywx_msg_archive.lock`),并发多跑也不会重复写。
## 5. 权限与菜单
后台菜单需新增一条,路径 `/chat`(或自定义),component 指向 `views/chat/index.vue`
建议开启菜单权限控制:仅运营/客服组可见。API 路由前缀为 `qywx.message/*`,请在 `adminapi` 权限中心相应配置。
## 6. 使用流程
1. 打开「IM」页面,系统自动拉取会话列表。
2. 左侧可按员工筛选;选中会话后中间展示消息流,8 秒轮询新消息。
3. 在底部输入框输入文本/添加附件(图片/视频/文件/链接/小程序)→ 点击「发送」。
4. 后端创建企微 `add_msg_template` 任务,**员工手机**会收到确认弹窗,确认后客户即可收到。
5. 已发送任务可在右侧「发送任务」Tab 查看送达详情。
## 7. 故障排查
| 症状 | 排查 |
| --- | --- |
| 前端顶部提示「会话存档 SDK 不可用」| 查 `config/pay.php` msgaudit_* 配置 + `libWeWorkFinanceSdk_C.so` 路径 + `ffi.enable` + 日志 `runtime/log/`。调 GET `/adminapi/qywx.message/archive_status` 看诊断。 |
| 消息拉取无数据 | `php think qywx:sync-msg-archive -vvv`;看 `qywx_msg_archive_cursor` 表 seq 是否推进;确认企微已开通会话存档 License 并勾选目标员工。 |
| 媒体文件一直下载中 | `qywx_msg_archive_media.status` / `retry_count` / `last_error`;路径 `runtime/qywx_msg_media/` 写权限;SDK `GetMediaData` 超时调大 `msgaudit_timeout`。 |
| 发送「消息内容不能为空」 | 文本 + 附件都为空;或附件上传失败导致没有 media_id,面板会红色报错。 |
| 发送成功但客户未收到 | 员工手机端漏点确认;或该员工未启用企业群发能力;查 `qywx_msg_send_task.fail_list` & 右侧「送达详情」。 |
| 需要撤回消息 | 企微规则:群发任务已提交后无法撤回单条;会话存档里会记录 `recall` 动作并在前端打「已撤回」标记。 |
## 8. 未来扩展点
- 多版本 RSA 私钥轮换:`WeComFinanceSdkClient::loadPrivateKeys()` 改造为读取 `msgaudit_private_keys` 数组,key 为 `public_key_ver`
- WebSocket 推送新消息:在 `QywxMsgArchiveService::persistBatch()` 写入后发布到 Workerman / Swoole,前端改成订阅(当前是 8s 轮询)。
- 群聊发送:企微「客户群 SOP」接口,当前 UI 仅展示群会话消息不允许发送(`canSendMessage` 为 false)。