This commit is contained in:
Your Name
2026-08-31 15:17:34 +08:00
parent ed48f8be31
commit 456dd667df
439 changed files with 5720 additions and 422 deletions
@@ -0,0 +1,396 @@
# 企业微信推广链接能力核验
核验日期:2026-08-31。范围:企业自建应用的获客助手、客户联系、客户欢迎语。本文是只读研究交付,没有修改应用代码,也没有调用任何企业的写接口。
来源均为企业微信开发者中心官方文档。网页搜索工具无法打开部分官方页面,实际通过 HTTPS 读取同一官方 URL 的公开 HTML,提取正文核验;没有以 SDK、博客或第三方镜像作为结论依据。以下标为“实现建议”的内容是本项目的工程设计,不是官方 API 自带能力。
## 1. 结论与能力边界
| 功能 | 官方能力 | 本地需要实现的部分 |
| --- | --- | --- |
| 获客成员范围 | `create_link` / `update_link``range.user_list``range.department_list` | 成员开关、有效期、星期时段、当日上限计算后写入范围 |
| 老客户优先找原员工 | `priority_option`,且仅部分经营类目支持 | 校验经营类目能力;与排班、上限的冲突提示 |
| 按星期、时段自动上下线 | 获客链接接口没有排班字段 | 常驻任务/定时调度重算,调用 `update_link` 覆盖范围 |
| 备用员工 | 获客链接接口没有“主用/备用”字段 | 主用无人可接待时才把合格备用成员放入范围 |
| 客户标签 | 读取企业标签库、对指定员工的客户 `mark_tag` | 配置标签 ID、回调后应用、幂等和失败补偿 |
| 客户备注/描述 | `externalcontact/remark` | 模板变量展开、字符数校验、只更新明确配置的字段 |
| 欢迎语文本/附件 | `send_welcome_msg` | 默认/渠道/关闭/分时策略选择、变量展开、素材准备、20 秒内发送 |
| 通过 `LinkId` 定位添加客户渠道 | **普通 `add_external_contact` 文档不承诺有 `LinkId`** | 使用 `State` 映射本地推广方案;有 `LinkId` 的获客事件作补充 |
依据:[获客链接管理](https://developer.work.weixin.qq.com/document/path/97297)、[事件格式](https://developer.work.weixin.qq.com/document/path/92130)、[发送新客户欢迎语](https://developer.work.weixin.qq.com/document/path/92137)。
## 2. 获客链接 create / update
官方文档:[获客链接管理](https://developer.work.weixin.qq.com/document/path/97297),页面最后更新 2025-11-17。
### 2.1 请求
创建:`POST https://qyapi.weixin.qq.com/cgi-bin/externalcontact/customer_acquisition/create_link?access_token=ACCESS_TOKEN`
```json
{
"link_name": "门诊咨询推广",
"range": {
"user_list": ["assistant_a", "assistant_b"],
"department_list": [2]
},
"skip_verify": true,
"priority_option": {
"priority_type": 2,
"priority_userid_list": ["assistant_a", "assistant_b"]
}
}
```
更新:`POST https://qyapi.weixin.qq.com/cgi-bin/externalcontact/customer_acquisition/update_link?access_token=ACCESS_TOKEN`
```json
{
"link_id": "LINK_ID",
"range": {
"user_list": ["assistant_b"],
"department_list": []
},
"skip_verify": true
}
```
示例中的 `priority_option` 仅在企业确实具备该能力且配置了好友优先策略时提交;它不是排班、权重或备用配置。`skip_verify` 应始终使用本地已保存的值,不能在范围同步时意外改变免验证设置。
### 2.2 已核实限制
- 创建 `link_name` 必填,更新可选,最长 **30 个字符**
- `range.user_list` 最多 **500 人**;部门覆盖人数也有上限,最终 `range` 覆盖总人数不得超过 **500 人**
- 创建时 `user_list``department_list` **不可同时为空**
- 更新的 `range` 是**覆盖更新**,不是增量加入或删除。若目的是精确排班,应使用明确的 `user_list`,并清掉不受排班控制的部门范围。
- `skip_verify` 缺省值为 `true`
- `priority_type=1`:在全企业内优先分配给已有好友关系的成员。
- `priority_type=2`:在 `priority_userid_list` 中优先分配给已有好友关系的成员;创建时该列表必填,最多 **1000 人**
- `priority_option` 也是覆盖更新;仅支持“客户与成员关系绑定”的经营类目可用,需在管理端“高级功能 → 获客助手”确认。
- `range` / `priority_userid_list` 受应用可见范围或客户可建联成员范围约束。
- 还有 `mark_source`,缺省 `true`,但**只对“营销获客”应用生效**;本项目自建应用不要将其误当作通用渠道标记开关。
- 查询、更新、删除的 `link_id` 必须属于当前应用创建的链接。
### 2.3 权限和不确定点
官方明确要求使用配置到客户联系“可调用应用”列表中的自建应用 secret 获取的 token;获客链接 API **不支持客户联系系统应用调用**。不能因为客户详情接口过去可用,就推断同一 secret 一定支持获客链接。
文档没有明确以下行为,不能自行编造 payload:
1. `priority_type=0` 的含义以及取消已存在 `priority_option` 的正确方式。官方只列出 `1``2`;不要宣称传 `0`、空对象或省略字段能清除既有设置。
2. 更新链接时提交全空 `range` 是否有特殊停用语义。创建明确不允许空范围,本项目应继续把“至少一名可接待成员”作为有效配置约束。
3. `update_link` 的传播延迟以及对已经打开的成员页/已经发起的好友请求是否有追溯影响。
4. 好友优先列表与排班范围交叉时的完整路由细节。`priority_type=1` 涵盖全企业,不能承诺严格服从本地排班/上限;需提供冲突说明并做真实企业联调。
## 3. 星期排班、备用员工与上限
以下为根据官方范围更新能力提出的实现建议,并非独立的企微“上下线 API”。
1. 将星期、开始/结束时间、时区、是否启用、有效期、成员角色(主用/备用)存到本地;统一用 `Asia/Shanghai`,时间区间采用左闭右开 `[start, end)`,跨午夜时段拆成两天或显式处理前一日。
2. 先计算符合开关、有效期、班次、业务上限的主用成员;主用集合非空就只发送主用集合。主用全部不可用时才选择合格备用成员;备用成员不要日常混在同一 `range` 中,否则企微会把他们当普通候选成员。
3. 保存后立即同步;分钟任务持续重算;在时段边界可以额外立即同步。数据库事务只认领任务/保存状态,网络请求放在事务之外。
4. `range` 有变化才调用 `update_link`,保留版本号、租约、重试和最后成功应用范围;调用成功后用 `customer_acquisition/get` 回读核验。
5. 企微自动跳过“暂时无法添加客户”的异常账号;若整条链接所有成员异常,会推送 `customer_acquisition/link_unavailable`。这是账号异常路由能力,**不代表按本地班次自动启用备用员工**。可以据此触发告警或经过本地规则校验的备用范围切换。[获客助手事件通知](https://developer.work.weixin.qq.com/document/path/97299)
6. 若主用和备用都为空,不要把“本地已下线”展示成“官方链接已停用”。保留明确阻塞状态、错误提示,并由业务选择停止曝光/受控入口暂停;不能在保存排班时偷偷删除官方链接。
7. 回调记账后更新范围是事后控制;有网络延迟和并发好友申请,不能声称“日上限绝不超发”。UI 应说明本地统计上限与官方建联并发之间的边界。
现有 `QywxPromotionRangeSyncService` 已有任务租约、版本号、回读范围、分钟重算和空范围阻塞,适合作为扩展点;不必另造一套链路。现有调度代码通过 `State=zyt_pool:{id}` 记账,扩展渠道参数时应保持兼容。
## 4. 企业标签、客户备注、客户详情
### 4.1 获取企业客户标签
文档:[管理企业标签](https://developer.work.weixin.qq.com/document/path/92117),最后更新 2023-12-01。
`POST https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get_corp_tag_list?access_token=ACCESS_TOKEN`
```json
{}
```
`tag_id``group_id` 都不传即返回所有标签;如果需要按组获取:
```json
{"group_id":["GROUP_ID"]}
```
同时传两个筛选条件时以 `group_id` 为准,忽略 `tag_id`。返回 `tag_group[]`,组内为 `tag[]`,标签使用 `id``name`,有删除标记时应过滤。应用仅可编辑/删除自己创建的标签,但读取标签库和给客户打已有企业标签是另一个权限层次,不能据此把所有其他来源标签都从选择器隐藏。
自建应用需被列入客户联系可调用应用;企业标签库最多 10000 个标签。页面没有给 `get_corp_tag_list` 列出分页参数,不要自行增加 cursor 分页。
### 4.1.1 自定义企业客户标签(2026-08-31 补充核验)
官方接口:`POST https://qyapi.weixin.qq.com/cgi-bin/externalcontact/add_corp_tag?access_token=ACCESS_TOKEN`。来源仍为 [管理企业标签](https://developer.work.weixin.qq.com/document/path/92117) 中“添加企业客户标签”一节;本次通过 HTTPS 读取官方页面公开 HTML 核实,没有调用企业 API。
已有“推广渠道”分组时:
```json
{"group_id":"EXISTING_GROUP_ID","tag":[{"name":"直播推广"}]}
```
没有该分组时,一次请求创建分组及标签:
```json
{"group_name":"推广渠道","tag":[{"name":"直播推广"}]}
```
- `tag.name` 必填,最长30个字符;`group_name` 同样最长30个字符,均不是字节上限。
- 指定已有分组用 `group_id`。填写该字段后,`group_name` 和标签组 `order` 被忽略。
- 通过 `group_name` 创建分组时,如果分组名称已存在,会在已有分组下新增标签;不能创建空分组。
- 同组标签不能重名;单次传入多个同名标签只创建一个。官方没有承诺“名称已存在”的每种错误码及返回列表形态,因此不能靠猜测错误码返回本地假ID。
- 返回值包含 `tag_group.group_id/group_name/tag[]`,标签真实ID为 `tag[].id`;企业标签总数上限10000。
- `agentid` 仅旧第三方多应用套件需要,本项目自建应用不提交。
本项目新增 `POST firstvisit.wecomPromotion/createTag`,复用页面权限,只接受 `{name}` 并固定使用“推广渠道”分组。名称须非空、最多30字符,不得包含控制/不可见格式字符。先查询同组同名并复用,创建失败或响应无法确认时只读回确认;无法确认则提示刷新列表核对,不再次发送创建请求。每个推广方案保存的 `tag_ids` 仍是数组,但最多一项,开启时必须一项;读取旧多选数据不截断,重新保存时要求用户明确选一个。
### 4.2 给指定员工的客户打标签
文档:[编辑客户企业标签](https://developer.work.weixin.qq.com/document/path/92118),最后更新 2023-12-01。
`POST https://qyapi.weixin.qq.com/cgi-bin/externalcontact/mark_tag?access_token=ACCESS_TOKEN`
```json
{
"userid": "assistant_a",
"external_userid": "EXTERNAL_USER_ID",
"add_tag": ["ENTERPRISE_TAG_ID_A", "ENTERPRISE_TAG_ID_B"]
}
```
- 可选 `remove_tag` 用于明确移除;`add_tag``remove_tag` 不能同时为空。
- 客户必须已是该 `userid` 的外部联系人;操作面向**员工与客户的关系**,不是企业下无差别更新所有员工视角。
- 每个成员对同一客户最多 3000 个标签;同一标签组可以选多个标签。
- 应用只能操作可见范围内成员的客户标签;规则组标签要求同一应用创建该规则组,且成员在其管理范围。
- 渠道自动标签建议只增添已配置标签,不能为了“同步一致”删除员工手动添加的其他标签。
### 4.3 客户备注和描述
文档:[修改客户备注信息](https://developer.work.weixin.qq.com/document/path/92115),最后更新 2025-11-17。
`POST https://qyapi.weixin.qq.com/cgi-bin/externalcontact/remark?access_token=ACCESS_TOKEN`
```json
{
"userid": "assistant_a",
"external_userid": "EXTERNAL_USER_ID",
"remark": "渠道A-李女士",
"description": "来自门诊咨询推广"
}
```
- `remark` 最多 **20 个字符**`description` 最多 **150 个字符**,均是字符数,不是欢迎语的字节数。
- 可选 `remark_company`(最多20字符,仅微信客户有效)、`remark_mobiles``remark_pic_mediaid`
- 不可全部为空;仅写本次用户明确启用的字段,避免覆盖人工备注/电话。
- 电话数组会覆盖旧值;官方清除全部电话的特殊说明为给 `remark_mobiles` 填一个空字符串。当前推广需求无须触及这一功能。
- 修改权限限制在应用可见范围内成员添加的客户。
- 文档未对清空 `remark` / `description` 的空字符串语义作同样明确说明,不要将“未配置”自动转换成清空远端。
### 4.4 获取客户详情、名字与渠道
文档:[获取客户详情](https://developer.work.weixin.qq.com/document/path/92114),最后更新 2025-12-19。
`GET https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get?access_token=ACCESS_TOKEN&external_userid=EXTERNAL_USER_ID`
重要字段:
- `external_contact.name`:微信客户为微信昵称;企微联系人为其对外别名或实名。
- `follow_user[]`:每个跟进人的 `userid``remark``description``tags``state``add_way`
- `follow_user.add_way=16` 表示获客链接添加;`state` 是本地可自定义渠道,两者不能混用。
- 读取/应用关系级备注和标签时,要匹配回调实际 `UserID`,不能直接取第一个 `follow_user`
- 跟进人超过500时,使用返回的 `next_cursor` 分页;只保证获取应用有可见权限的成员信息。
- 官方注明自 2023-12-01 起不再支持新场景使用系统应用 secret,存量企业暂不受影响。项目应以已列入可调用列表的自建应用作为正式接入方式。
## 5. 获客渠道与回调字段
### 5.1 customer_channel 与 State
将渠道标识放在已创建的官方链接 URL 查询参数中,而不是写进 `create_link` 的自造 `state` 字段:
```text
https://work.weixin.qq.com/ca/LINK_PATH?customer_channel=zyt_pool%3A123
```
如果原链接已有查询串,应以 URL 解析器安全合并;不能重复叠加 `customer_channel`。自定义字符串最长 **64 字节**,超过会截断,因此应在保存/生成时拒绝超长值,避免两个渠道被截断后碰撞。返回的客户列表与客户详情 `state` 对应这个字符串。[获取由获客链接添加的客户信息](https://developer.work.weixin.qq.com/document/path/97298)
建议继续采用无个人信息的短、不透明标识;若扩展为独立渠道 ID,应新增明确映射并保持 `zyt_pool:{id}` 老链接兼容。
### 5.2 添加客户事件
官方文档:[事件格式](https://developer.work.weixin.qq.com/document/path/92130)。以下是接收 XML 解密后用于本地处理的字段示意(**不是 POST API 请求体**):
```json
{
"Event": "change_external_contact",
"ChangeType": "add_external_contact",
"UserID": "assistant_a",
"ExternalUserID": "EXTERNAL_USER_ID",
"State": "zyt_pool:123",
"WelcomeCode": "WELCOME_CODE",
"CreateTime": 1788141600
}
```
本事件的官方字段表**没有 `LinkId`**。应以 `State` 识别渠道;不能在本事件没有 `LinkId` 时放弃欢迎语/标签,也不能把后来的首次聊天事件当成欢迎语触发条件。
`WelcomeCode` 不是必然存在:客户与成员已开始聊天、已经在半客户事件中发过欢迎语等情况不会继续给 code;企业微信商务伙伴自动递名片,也不回调 code。
`add_half_external_contact` 同样可能带 `State``WelcomeCode`。若需要支持免验证添加全流程,应让欢迎语处理器在有效 code 出现时就处理,不应被“半客户不入客户表”的早返回吞掉;但打标签和修改备注可以等关系确认后做,不能为等客户详情而消耗欢迎语窗口。
### 5.3 LinkId 出现在哪些事件
获客助手专用事件为 `Event=customer_acquisition`。例如 `link_unavailable``delete_link``open_profile``friend_request``customer_start_chat``message_from_customer` 等有 `LinkId`;其中 `open_profile` / `friend_request``State`,但**没有可用于发送新客户欢迎语的 `WelcomeCode`**。[获客助手事件通知](https://developer.work.weixin.qq.com/document/path/97299),最后更新 2026-07-22。
`message_from_customer``UserID``ExternalUserID``ChatSeq` 自 2024-12-19 起不再保证回调,须使用 30 分钟内有效的 `ChatKey` 查询。当前项目已接入 ChatKey 处理,应保留这条补偿链路,不能用旧示例假定字段永远齐全。
### 5.4 接收要求
配置了客户联系可调用应用、API 接收消息,且勾选“外部联系人变更回调”,才能收到可见范围内成员客户事件。[回调通知概述](https://developer.work.weixin.qq.com/document/path/92129)
企业微信要求回调在 **5 秒内响应**;连接失败或超时时会重试,官方说明总共重试三次,并明确回调并非100%可靠。接收端应验签解密、快速持久化并应答,业务由立即运行的工作进程处理;需要额外对账。[回调配置](https://developer.work.weixin.qq.com/document/path/90930)
## 6. 发送新客户欢迎语及附件
官方文档:[发送新客户欢迎语](https://developer.work.weixin.qq.com/document/path/92137),最后更新 2025-11-17。
`POST https://qyapi.weixin.qq.com/cgi-bin/externalcontact/send_welcome_msg?access_token=ACCESS_TOKEN`
```json
{
"welcome_code": "WELCOME_CODE",
"text": {"content": "李女士您好,我是小张医助。"},
"attachments": [
{
"msgtype": "image",
"image": {"media_id": "IMAGE_MEDIA_ID"}
},
{
"msgtype": "link",
"link": {
"title": "就诊指南",
"picurl": "https://example.com/guide-cover.jpg",
"desc": "查看就诊须知",
"url": "https://example.com/guide"
}
},
{
"msgtype": "miniprogram",
"miniprogram": {
"title": "预约入口",
"pic_media_id": "COVER_MEDIA_ID",
"appid": "ASSOCIATED_MINIPROGRAM_APPID",
"page": "/pages/appointment/index"
}
},
{"msgtype": "video", "video": {"media_id": "VIDEO_MEDIA_ID"}},
{"msgtype": "file", "file": {"media_id": "FILE_MEDIA_ID"}}
]
}
```
以上五类均有官方示例/对应字段。官方参数表的 `attachments.msgtype` 行漏列了 `file`,但页面说明、完整示例和 `file.media_id` 行都明确支持文件;这是文档内部不一致,应记录而不是误删文件支持。
### 6.1 时效、互斥、错误处理
- 收到相关事件后 **20 秒内**调用,`welcome_code` 有效期20秒,只能成功使用一次;不能靠分钟级任务补发过期欢迎语。
- 管理端已为成员配置可用欢迎语时,不会返回 `welcome_code`。本地“关闭渠道欢迎语”只能控制**本应用是否发送**,无法压制企业微信管理端或其他应用自己发的欢迎语。
- 长期未登录企业微信的成员不能发送欢迎语。
- 已成功下发后再发返回 `41051`,无需重试。
- 多应用竞争发送时,后来的应用可能返回 `41096`,表示正在由其他应用分发,不等于已发成功;官方允许重试,但仍受20秒窗口限制。收到 `41051` 则停止。
- 自建应用须配置到可调用应用列表;成员须在其可见范围。获客链接可用不等于欢迎语权限和回调配置必然正确。
### 6.2 内容限制
| 字段 | 限制 |
| --- | --- |
| `text.content` | 最长4000字节(UTF-8 字节计算) |
| `attachments` | 最多9个;可以同时发文本和附件 |
| `text` / `attachments` | 不可同时为空 |
| `link.title` | 必填,最长128字节 |
| `link.desc` | 可选,最长512字节 |
| `link.url` | 必填 |
| `link.picurl` | 可选封面 URL;注意字段拼写不是 `pic_url` |
| `image.media_id` / `image.pic_url` | 至少一个;都传时 `media_id` 优先 |
| `image.pic_url` | 仅可用官方“上传图片”接口得到的 URL,不能直接塞任意本地/CDN图片地址 |
| `miniprogram.title` | 必填,最长64字节 |
| `miniprogram.pic_media_id` | 必填,封面建议520×416 |
| `miniprogram.appid` | 必须是关联到企业的小程序 |
| `miniprogram.page` | 必填的小程序页面路径 |
| `video.media_id` / `file.media_id` | 对应类型必填 |
`msgtype` 必须与同项内的内容对象一致。不能把多个附件拼成旧版顶层 `image` / `link` / `miniprogram` 字段。
### 6.3 素材必须提前准备
临时素材:`POST https://qyapi.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE`multipart 文件字段名为 `media``media_id` 有效 **3天**,同一企业内应用可共享。文件需大于5字节;图片 JPG/PNG ≤10MB,视频 MP4 ≤10MB,普通文件 ≤20MB。[上传临时素材](https://developer.work.weixin.qq.com/document/path/90253)
永久图片 URL`POST https://qyapi.weixin.qq.com/cgi-bin/media/uploadimg?access_token=ACCESS_TOKEN`,得到可用于欢迎语的 URL。图片大小5B~2MB,每企业每日最多1000张、每月最多3000张;返回 URL 永久有效,但用途受企微环境限制。[上传图片](https://developer.work.weixin.qq.com/document/path/90256)
实现建议:配置欢迎语时保存源文件和上传状态,提前转成企微素材并按 hash 去重;临时素材在过期前刷新。发送时不应临时下载大文件再上传,否则20秒窗口很容易失效。失效附件应有明确错误/降级记录,不能假装发送完整成功。
## 7. 模板变量及默认/渠道/关闭/分时策略
以下是服务端职责,官方 `send_welcome_msg` 不会替换 `{客户昵称}``{员工昵称}` 等模板内容,也没有这些模式参数。
### 7.1 变量
- `{客户昵称}`:取 `external_contact.name`,而非把某个员工的 `remark` 当客户原始昵称。缓存缺失时可做有严格超时预算的详情查询;无法取得时使用事先定义的“您”等兜底,不要把未展开的占位符发送出去。
- `{员工昵称}`:首先明确产品含义。可用本地配置的对外称呼,或企业成员 `name`;若要 `alias`,需明确优先级。`GET /cgi-bin/user/get?userid=USER_ID` 返回 `name`/`alias` 受应用类型和可见权限影响,第三方并不能普遍拿到姓名/别名。[读取成员](https://developer.work.weixin.qq.com/document/path/90196)
- 只支持白名单变量,不运行表达式、不执行任意模板代码;API 发出的最终文本必须已完成替换。
- 保存时校验模板结构,发送时在变量展开后再校验长度:欢迎语 UTF-8 字节上限,客户备注20字符、描述150字符。对模板变量导致的超限采用明确的截断/拒绝策略并记录,不能依赖企微静默截断。
### 7.2 建议的确定性策略
每条本地推广渠道保存 `welcome_mode = inherit | custom | disabled | scheduled`,并定义唯一优先级:
1. 能定位的渠道设为 `disabled` → 不发送本应用欢迎语,**不可回退默认欢迎语**。
2. 渠道为 `custom` → 使用渠道内容。
3. 渠道为 `scheduled` → 按事件发生时间、北京时间和星期选择规则;规则重叠要拒绝或使用明确排序。无匹配时使用该配置明确指定的兜底(默认/固定内容/不发),不能凭实现猜测。
4. 渠道为 `inherit`,或业务明确允许未知渠道走默认 → 使用默认欢迎语。
分时表示“添加客户时选哪一段内容”,不是“把欢迎语延迟到某个时段再发”;延迟通常会越过20秒有效期。配置应和事件一起保存版本或内容快照,避免工作进程稍后读到另一版规则。
### 7.3 处理链建议
```text
验签解密 → 最小事件幂等落库 → 立即工作进程领取
├→ 欢迎语:选策略/展开变量 → 20秒内send_welcome_msg
├→ 成员记账/范围同步(独立,可补偿)
└→ 关系确认后标签/备注/详情同步(独立,可补偿)
```
回调应答不能等全部远端请求完成;欢迎语必须使用立即消费的队列/工作进程,不能复用分钟任务。若部署没有立即消费能力,应明确补齐部署要求,不能只保存一条“待发送”记录就宣称欢迎语已经打通。
欢迎语状态建议包含 `pending/processing/sent/skipped/expired/failed`,并保存事件时间、首次接收时间、处理耗时、策略版本、结果码和跳过原因。幂等至少考虑企业、实际员工、客户、事件及 welcome_code;code 本身仅短期保留/加密,日志只记录摘要,不能泄露 token/code。`add_half` / `add` 以及回调重试不应造成重复发送。
标签、备注的结果单独记录,失败不能撤销已成功的欢迎语;人工重试仅重试失败动作,不重放全部新增客户流程。先读取现有关系可避免覆盖人工信息,但不得挡在欢迎语关键路径前。
## 8. 对当前代码的落地提示
只读查看了以下文件:
- `server/app/common/service/qywx/QywxCustomerAcquisitionApiService.php`
- `server/app/api/controller/QywxExternalContactCallbackController.php`
- `server/app/common/service/qywx/QywxPromotionRangeSyncService.php`
- `server/app/common/service/qywx/QywxPromotionMemberSchedulerService.php`
观察及建议:
1. API service 已有 create/update/get/list 和 token 无效单次刷新;它本身没有标签、备注、欢迎语或素材封装。扩展时应区分权限、超时预算和返回错误,而非把前端配置原样塞给 `create_link`
2. 回调已提取 `State``WelcomeCode`,但目前欢迎语只记为是否存在;没有发送欢迎语。新增服务要取得真实 code,而不是只接收布尔值。
3. `add_half_external_contact` 目前早返回;如果要支持其欢迎语,须在这个返回之前处理有效 code,客户落库的原有跳过行为可保留。
4. 目前 `add_external_contact` 会同步触发范围 API,再拉客户详情。欢迎语不应附加在这些操作之后;其超时窗口比范围/资料同步更严格。
5. `State` 当前正则为 `^zyt_pool:(\d+)$`。如果新前端生成别的 State 格式而不改兼容解析,现有统计和范围调度会失效。
6. 当前范围同步只发送 `link_id/link_name/range/skip_verify`;若新增好友优先策略,要确认创建、编辑、后台定时同步、远端回读都不会意外覆盖/遗失该设置。
7. 若排班/备用规则改变了本地“可用成员”判断,应只保留一个统一计算器供页面预览、保存校验、分钟重算和回调后同步共用,避免页面与官方实际范围不一致。
## 9. 联调时必须验证的项目
- 当前企业的自建应用已具备获客助手、客户详情、标签、备注、欢迎语权限及成员可见范围;新增这些功能不能仅沿用“获客链接列表成功”的权限检测结果。
- 管理端欢迎语是否已关闭/让位,本应用是否实际收到含 `WelcomeCode` 的回调。
- `priority_option` 取消语义、旧好友优先与排班范围的实际交互,官方未给明文保证的部分应以联调记录为准。
- `range` 更新到官方生效的实际时延;全员下线/备用不可用时既有链接仍可能维持旧范围,UI需如实显示同步阻塞。
- 20秒期限下“冷 token、冷客户缓存、素材过期、队列堆积”的处理;5秒回调应答要求。
- Unicode昵称展开后的UTF-8字节限制、备注字符限制、小程序关联/页面可用性、每类附件真实接收效果。
- 半客户转正式客户、重复回调、多应用竞争、人工欢迎语已发送、成员长期未登录等情况下,不把应跳过/过期误报成普通系统故障。
上述研究未通过真实企业写接口验收;所有不确定行为已显式列出,不能将研究示例当作企业权限或下发成功证明。