94 lines
11 KiB
Markdown
94 lines
11 KiB
Markdown
# 企业微信推广自动化部署与验收
|
||
|
||
此实现覆盖推广渠道欢迎语、企业标签、客户备注/描述及原客户同步补偿。企微获客链接接口不提供欢迎语和标签配置字段,因此这些设置不会显示在企微链接详情的“配置”入口,而是在客户添加回调后通过客户联系接口执行;它不会覆盖企业微信后台欢迎语设置,也不意味着企业已开通接口权限。官方能力及限制见 [核验报告](research/wecom-promotion-api-capabilities.md)。
|
||
|
||
## 部署顺序
|
||
|
||
1. 新环境先执行 `server/sql/1.9.20260831/add_wecom_promotion_automation.sql`;已建四张自动化表的环境再执行 `server/sql/1.9.20260901/upgrade_qywx_promotion_automation_runtime.sql`,补登记分钟补偿和素材刷新任务。默认表前缀为 `zyt_`,实际前缀不同须由部署人员调整。本开发任务没有执行业务数据库迁移。
|
||
2. 部署 PHP 代码。为 PHP-FPM、CLI worker 使用同一项目目录 `server/runtime/qywx_promotion_private/`,授予应用运行用户读写权限。目录必须位于 Web 根目录之外,禁止静态文件映射;保留源文件,不能随意清理该目录。
|
||
3. 配置客户联系“可调用应用”,优先使用创建获客链接的同一自建应用。代码默认使用 `qywx_customer_acquisition.secret`,无该值才使用 `pay.wechat_work.customer_contact_secret`。需要明确覆盖时设置 `WECHAT_WORK_PROMOTION_CONTACT_SECRET`。不会使用 `external_pay_secret`。新接入不要依赖已受官方限制的客户联系系统应用 Secret。检查应用可见范围、可信 IP、外部联系人变更事件订阅;欢迎语回调必须来自可调用应用的相应配置。
|
||
4. 建议设置至少32字符的随机 `WECHAT_WORK_PROMOTION_ENCRYPTION_KEY`,所有 API/CLI 节点保持一致。未设置时会自动在项目私有目录生成 `welcome.key`(0600)。多节点使用共享源文件目录和一致密钥;更换密钥前先处理/过期并清空待处理欢迎码。
|
||
5. 在启用渠道欢迎语之前,启动并监控下面的常驻 worker,并确保系统 `crontab` 调度进程正常运行。回调会立即尝试欢迎语和标签;常驻 worker 负责欢迎码窗口内的快速重试,分钟任务负责其余补偿和素材预热。
|
||
|
||
若旧版脚本在 `welcome_cipher` 字段的 `COMMENT=` 处报 MySQL 1064,请重新打开已修正的 SQL 文件并完整重跑。字段注释必须使用 `COMMENT '内容'`(不带等号);表级的 `COMMENT='内容'` 是合法语法,无须修改。脚本中的四张表都使用 `CREATE TABLE IF NOT EXISTS`,已成功创建的表会保留,不需要删表。四张表全部创建成功后再启动 worker。
|
||
|
||
## 必须运行的进程
|
||
|
||
欢迎码只有20秒有效。验签回调入库后会立即尝试欢迎语,并为正式客户立即添加标签;普通客户同步、备注、范围更新和素材上传等慢动作不进入回调。独立常驻进程继续秒级消费明确可重试的欢迎语任务。
|
||
|
||
```sh
|
||
cd /path/to/server
|
||
php think qywx:work-promotion-automation
|
||
```
|
||
|
||
使用 Supervisor 或 systemd 保持常驻、自动重启。建议同一数据库启动2个欢迎语worker,以免单个HTTP请求阻塞其他事件;任务租约保证同一任务不重复领取。高并发须按实际20秒延迟指标增加进程。`--once` 仅用于受控诊断/部署测试,不能替代守护进程。
|
||
|
||
例如 Supervisor 配置(路径和用户替换为实际值):
|
||
|
||
```ini
|
||
[program:qywx-promotion-welcome]
|
||
command=/usr/bin/php /path/to/server/think qywx:work-promotion-automation
|
||
directory=/path/to/server
|
||
numprocs=2
|
||
process_name=%(program_name)s_%(process_num)02d
|
||
user=www-data
|
||
autostart=true
|
||
autorestart=true
|
||
stopasgroup=true
|
||
killasgroup=true
|
||
stopwaitsecs=35
|
||
stdout_logfile=/var/log/qywx-promotion-welcome.log
|
||
stderr_logfile=/var/log/qywx-promotion-welcome-error.log
|
||
```
|
||
|
||
迁移会把每分钟补偿和每5分钟素材预热登记到 `zyt_dev_crontab`;部署仍须保证 `php think crontab` 被系统每分钟触发。若部署不使用内置任务表,可改用以下系统 cron:
|
||
|
||
```cron
|
||
* * * * * cd /path/to/server && /usr/bin/flock -n /tmp/qywx-promotion-retry.lock /usr/bin/php think qywx:retry-promotion-automation
|
||
*/5 * * * * cd /path/to/server && /usr/bin/flock -n /tmp/qywx-promotion-media.lock /usr/bin/php think qywx:refresh-promotion-media
|
||
```
|
||
|
||
现有 `qywx:sync-promotion-ranges`、`qywx:retry-customer-acquisition-events` 也须保留。新推广配置事件优先在回调中添加标签;失败标签以及备注、描述、成员记账、范围更新、客户同步由补偿任务执行,处理延迟通常不超过一分钟。不要把这些慢速动作塞进欢迎语worker。方案日上限是回调后的统计控制,不是企微并发建联的硬性上限。
|
||
|
||
## 配置与素材契约
|
||
|
||
- `QywxPromotionContactApiService::tagOptions(): array` → `{tag_groups:[{group_id,group_name,tag:[{id,name}]}]}`。
|
||
- 方案客户标签为单选;接口仍使用 `tag_ids: []/[id]`,服务端拒绝多个标签。旧多选配置会显示重新选择提示,不自动截断。自定义标签通过 `POST firstvisit.wecomPromotion/createTag`(`{name}`)调用企微 `externalcontact/add_corp_tag`,固定保存到“推广渠道”分组,名称最多30字符;同组同名复用,创建成功后自动选中。创建立即写入企微标签库,取消方案编辑不会删除该标签;本次调整不新增数据库迁移。
|
||
- `QywxPromotionMediaService::upload($file, string $type, int $adminId): array` → `{asset_id,name,type}`。仅接受 ThinkPHP 已验证的 `UploadedFile`;不接受服务器路径或网络下载地址。上传时立即预热临时素材。
|
||
- `validateConfig(array $config, int $adminId, array $existingConfig = []): array` → 保留其他配置字段、规范化附件后的完整配置。调用方必须先校验方案编辑权;第三参数只传数据库读取的旧配置,不能传用户提供的“白名单”。其他管理员只能保留已获授权的旧方案资产,不能新引入别人的资产。
|
||
- `validateAttachments(array $attachments, int $adminId, array $allowedAssetIds = []): array` 为底层契约;不要直接向HTTP客户端暴露第三参数。
|
||
- 素材 ID 是随机48位十六进制字符串。图片 JPG/PNG≤10MB、视频MP4≤10MB、文件≤20MB,且大于5字节。普通文件限定PDF、Office、文本、CSV、ZIP、JPG/PNG、MP4;拒绝可执行文件、HTML/SVG等格式。后端使用实际MIME和扩展名,不信任浏览器Content-Type。
|
||
- 附件支持 `image/video/file.{asset_id}`、`miniprogram.{title,appid,page,pic_asset_id}`、`link.{title,url,desc,picurl?}`。不接受前端任意 `media_id`。本实现不开放 `image.pic_url`:官方只支持uploadimg生成的URL,普通CDN地址不能替代。
|
||
- 欢迎语文本最多4000 UTF-8字节(产品配置还可额外限制1200字符),最多9附件;链接标题128字节、描述512字节,小程序标题64字节。小程序需已关联企业;本地无法代替企微验证关联与页面可达性。
|
||
- 源文件与媒体记录持久保留;缓存media_id有效3天,在到期前1小时进入刷新候选。发送只使用剩余至少5分钟的缓存。过期素材不会在欢迎语关键路径上传,不会静默丢掉附件只发文字;会记录准备失败,直至20秒期限结束。
|
||
- 临时素材与凭证指纹绑定。更换企业/应用Secret后,先运行 `qywx:refresh-promotion-media` 并检查失败数,再恢复渠道曝光。
|
||
|
||
## 回调、幂等与失败策略
|
||
|
||
- 入口仅在 EasyWeChat 已验签/解密的 `change_external_contact` listener 中调用 `enqueueVerifiedEvent($event, true)`;不得把此服务直接作为未验签HTTP接口。
|
||
- 优先用 `State=zyt_pool:{id}`,无State时才使用事件实际存在的 `LinkId/LinkID`。必须存在正常的推广方案、非删除成员关系、官方有效链接、新配置记录。仅有可猜测的State不是授权。普通客户事件、无新配置旧方案、迁移尚未安装时保留旧同步路径;不会让所有客户突然依赖新worker。
|
||
- 正式客户add可执行全部动作;半客户add_half只处理欢迎语,不提前打标签、改备注或落正式客户表。事件重复通过唯一事件键去重;同一WelcomeCode在half/add之间通过唯一摘要索引只分配一次消费权。
|
||
- 队列持久化失败会抛专用异常,listener不吞掉,HTTP返回500供企微重投。即时发送结果按分动作持久化;明确可重试的失败由持久任务补偿,事件重投不会重复发送已完成动作。
|
||
- 每条任务保存配置快照;分时欢迎语按事件时间和Asia/Shanghai选择,未命中用基础欢迎语。`default`和`none`均不发送本系统欢迎语,不能抑制企业微信管理端或其他应用发送。
|
||
- 欢迎码以AES-256-GCM短存;动作进入sent/skipped/expired/uncertain/failed后清除密文。既有客户事件raw字段现在移除WelcomeCode;审计中无明文code/token、上游请求或Guzzle异常链。历史已有raw数据需要另外评估清理,本次未改历史记录。
|
||
- 发送前先持久化running。网络超时、无法解析响应、HTTP失败,或进程在发送后记录成功前崩溃,均记为uncertain,清理密文并停止自动重发,避免重复推送;需通过企微实际聊天结果人工核对。
|
||
- 只有明确的企微拒绝响应允许在剩余窗口重试;41096可重试,41051记为已使用并停止。token明确失效可刷新一次。过期或缺少code有独立原因,不会当成成功发送。
|
||
- 标签仅增添配置标签,不删除人工标签;备注与描述只写明确启用的字段。变量替换只支持白名单,名字接口失败使用本地员工名或“客户顾问”、客户用“您”。备注截断到20字符;欢迎语字节截断有审计原因。
|
||
- 元数据每个动作单独记录状态。失败最多10次、指数间隔后终态failed;成功动作不重放。最终failed不是成功,应安排告警/人工修复。范围动作仅触发现有范围任务,其最终应用状态仍以原范围同步表为准。
|
||
|
||
## 监控与验收
|
||
|
||
`zyt_qywx_promotion_automation_task.actions_json` 保存分动作状态、次数、错误码、原因和重试时间;`zyt_qywx_promotion_automation_action_log` 保存每次状态转移。监控欢迎语入队延迟、`expired/uncertain/failed` 数量、待处理最早事件时间、素材 `last_error` 和worker存活。一次没有异常输出不等于客户端已收到消息。
|
||
|
||
安全离线验证(不会初始化现有数据库、HTTP全部Mock):
|
||
|
||
```sh
|
||
cd server
|
||
php tests/QywxPromotionContactApiServiceTest.php
|
||
php tests/QywxPromotionMediaServiceTest.php
|
||
php tests/QywxPromotionAutomationServiceTest.php
|
||
php tests/QywxPromotionCodeCipherTest.php
|
||
```
|
||
|
||
生产验收仍需在授权的企业测试客户/员工上验证真实权限、半客户/正式客户回调、每类附件实际接收、昵称模板、默认欢迎语互斥、worker故障和跨节点存储。此开发没有发出任何真实企业微信写请求。
|