版本:v1.0 定稿 | 2026-08-16 | 作者:product-owner | 终验:主会话(裁决见 §6 表)
上游:口径基线 v2.1(唯一口径源)| 总纲领 v1.3 §5 | 盘点 docs/dev/message-api-notes.md(下称「盘点」,细节一律引其节号,本文不复述)
下游:P0-6.3(msgbridge 客户端)、P0-6.4(缺口处置)、P1-7/P1-8/P1-11 全部按本文开发。触达类功能的总闸:本文没写的触达场景不许开工。
定位澄清(用户确认 2026-08-16):消息台深度融入统一运营台——对用户是一个产品(主工作面板即消息台形态,总纲领 §5),不是两个独立运行的系统;「集成契约」指的是代码层面的服务间接口(消息台作为企微执行层服务化部署),产品层面无缝一体。
| 侧 | 职责 |
|---|---|
| 犀见 server(编排) | 圈人(业务条件→名单)、判发不发、文案组装、幂等与去重、审计留痕、批次归因、结果回执 |
| 消息工作台(执行) | 企微通道本身:发送、限流、设备在线、风控熔断、加人节流、群发分批 |
犀见 server 绝不自建企微发送通道(盘点 §3「推论对犀见的约束」):消息台的发送限流是单 gunicorn 进程的内存状态,任何绕过它的发送路径都会让两边各限各的、合起来突破企微真实上限,代价是封号(封号=整条触达链全断)。犀见所有主动触达一律经消息台 API。
http://127.0.0.1:5040(同机部署,115)。不走 message.lynoxi.com(公网入口由 nginx Basic Auth 挡,非服务间通道)。X-Auth-User: xijian-server、X-Auth-Oid: system-xijian。消息台 _operator() 直接信任这两个头;不带则 operator=unknown,定价等落库接口 403(盘点 §6 认证缺口)。.env 未配置 FEISHU_APP_ID,/api/* 在内网无需任何凭证(已核实)。因此本方案的全部安全性来自「服务只监听回环 + 同机」这一网络边界,不来自凭证。跨机部署即失效——届时必须升级为真凭证(mTLS 或消息台侧加 API Key)。升级钩子:server 侧 MSGBRIDGE_AUTH_MODE=header(v1 唯一实现值),换值即换鉴权实现,现在不做。| 通道 | 闸值 | 归属 |
|---|---|---|
| 单聊 send/reply/forward/card/location | 20 条/分、300 条/时(单账号) | 消息台 api_ext.py:1500,工程保守取值 |
| 群发 broadcast | 20 人/批、批间隔 3 秒、≤500 人/次,不过单聊闸 | 消息台 broadcast.py |
| 批量加人 addfriend | 20/号/天、间隔 45~90s、仅 9:00–21:00、单任务 ≤2000 | 消息台 addfriend.py |
犀见侧不设本地限流副本:额度一律实时读 GET /api/send-quota(发送)与 GET /api/addfriend 返回的 quota/config(加人)。理由——把 20/300 抄进犀见 cfg 会制造两份真相且必然漂移;真闸在消息台,犀见只做「发之前先看余量、余量不够就排队」。
msgbridge.mode(off/shadow/live,默认 off)+ 场景级 msgbridge.scene_mode.<scene>(默认继承全局;取二者中更保守的一档)。mode=shadow。{ok:false, reason:"msgbridge_off"}。invite.addfriend / invite.message / invite.card / onboard.card / urge.underperform / urge.sla / order.visitor_invite(报名确认→入园信息邀请,基线 §6.4)/ order.group_pull(报名确认→拉群,基线 §6.5)——后两个 v1.1 追加(P0-5.1 设计 S13 裁决,2026-08-17)。前置铁律:名单由人工反选拍板(基线 §5「反选永不自动化」);server 只执行已确认名单。名单在发起时快照落库(invite_batch_member),运行期不允许扩名单——对应 §6.9 判据「名单外不发」。
A1 未建联/未加好友者 → 批量好友申请(基线 §6.11)
POST /api/addfriend body {phones:[...], guids:[...], mode:"balance", verifyText, note:"xijian:<batch_no>"} → {ok,id,job}POST /api/addfriend/<jid>/confirm body {count: len(phones)}(数量原样确认)POST /api/addfriend/<jid>/start轮询 GET /api/addfriend/<jid> 取明细(策略见 §4.3)
幂等:note 写 xijian:<batch_no> 作业务幂等标记;建 job 前先 GET /api/addfriend 按该前缀查重,命中则复用不重建(消息台无幂等键,缺口 4)。
start 之后不重试。GET /api/addfriend 读余量为止。A2 已是好友者 → 邀约消息 + 入驻/任务卡片
(guid,userId)GET /api/conv-of?guid=&userId= → cid;无会话则 POST /api/conv/new {userId,guid}POST /api/conv/<cid>/send {text}(模板 cfg,见 §5)POST /api/conv/<cid>/send-miniprogram {pageKey,title,desc,taskId} → {ok,wcid,path}人数 > msgbridge.urge_batch_threshold 时改走群发:POST /api/broadcast {guid,targets,msgs,sendType,note:"xijian:<batch_no>"} → POST /api/broadcast/<bid>/confirm {count} → POST /api/broadcast/<bid>/send
批次归因(§6.9 判据 2「报名意向可关联回邀约批次」):用步骤 4 返回的 wcid 作归因键,server 存 wcid ↔ (batch_id, influencer_id);达人点卡片后 wcid 回流即完成漏斗闭环。不新增任何接口。
msgbridge.invite.*,记 scene/batch_id/influencer_id/peer/mode/wcid/消息台返回。口径红线(契约级断言,api-tester 必测):请求必带 trigger ∈ {manual, auto}。
- scene=urge.underperform(数据不达标催发,§6.10/§7.2)只接受 trigger=manual——「系统绝不自动催发不达标达人,着急的是达人」。trigger=auto + 该 scene → 422。
- scene=urge.sla(时效超时催发,§4)可 auto,对全量达人统一标准(§4 v2.0 口径)。
调用序列(单发):身份翻译(§2.5)→ GET /api/conv-of → POST /api/conv/<cid>/send {text};需要挂在某条消息下时用 POST /api/conv/<cid>/reply {text,quoteUid}(拼不出结构会 degraded:true 降级成普通发送,server 按成功记但审计标 degraded)。
批量(> 阈值):POST /api/broadcast → /confirm {count} → /send(≤500 人/次)。
touch_log(scene, influencer_id, biz_date) 唯一索引,先占后发(先 INSERT 抢占,再调发送);同一 scene+同一人在 msgbridge.dedupe_window_hours 内只发一次。msgbridge.retry_max 次指数退避;{ok:false, rateLimited:true}:不重试,转延后队列(msgbridge.rate_limited_backoff_minutes 后重排);unknown 转人工确认。理由:发送接口无幂等键,超时时「是否已发出」不可知,重发的代价(骚扰达人/触发风控)高于漏发。msgbridge.urge.*,必记 trigger、operator、文案摘要(≤200 字)。POST /api/conv/<cid>/send-miniprogram {pageKey,title,desc,taskId};pageKey 必须在 GET /api/miniprogram/pages 清单内(appId wx120205414255fb5b,与犀见 miniapp 同一小程序)。XIJIAN_WELCOME_CARD=1 已在生产开启,新好友会自动收到一张卡。server 发卡前查 GET /api/conv/<cid>/binding 的 pendingCards,若存在未点开卡片且在 msgbridge.dedupe_window_hours 内,跳过并记审计 skipped,避免同一人被连轰两张卡。taskId 语义见待裁决 D6;v1 期未确认前只传犀见已知可对齐的值或留空,批次归因一律靠 wcid,不依赖 taskId。可用接口:GET /api/state、GET /api/conv/<cid>、GET /api/conv/<cid>/history、GET /api/search——均为整页人工界面设计的重接口(盘点 §6)。
P0 决策:不做嵌入。 北极星判据:只读会话不减少任何达运人工动作(运营已有消息台界面,且总纲领 §5 定调「主工作面板就是消息台」),为它做裁剪接口属镀金(三不原则第 3 条)。P0 只用轻接口 GET /api/conv-of、GET /api/conv/<cid>/binding。P1(随 P2-16 前端融合)再议是否要裁剪版只读接口。
influencer_id →(达人域档案)→ 手机号/抖音号 →(绑定关系)→ peer(guid,userId) → GET /api/conv-of → cid。
关键:不要试图用手机号在企微侧搜人——企微不给外部联系人手机号/微信号(contactType 2057 实测全空,盘点 §7),GET /api/contacts?kw=<手机号> 对外部联系人基本搜不到。可靠的唯一桥是绑定关系(wcid 回流或人工绑定,盘点 §4)。因此:
binding_mirror(influencer_id, guid, peer_user_id, conv_id, source, synced_at);(实现表名定案 influencer_binding,归达人域、字段为本契约形状的超集——P0-2.3 设计 S6 裁决,2026-08-16)GET /api/contacts?bound=1&limit=5000(该接口带 bound 参数,返回含 influencerId);PO 推荐方案:沿用消息台协议签发与兑现,犀见 server 只做镜像。
- 犀见新 miniapp 拿到 wcid 后仍调 POST /api/wcid/redeem(公网固定入口 api.lynoxi.com/qiwe/wcid/redeem,无鉴权、HMAC 自证、30 天 TTL);
- redeem 成功后 miniapp 再调犀见 POST /api/miniapp/binding/redeemed {wcid, influencerId, openId},server 写 binding_mirror 并触发 P0-3.1 建联入档。
- 推荐理由:① WCID_SECRET 只在消息台,犀见自签需复制密钥跨服务,双签发一旦密钥不同步即全链断;② 绑定权威表 peer_binding 在消息台(人工绑定也写那里),双写会造出两份真相;③ 卡片由消息台发出,token 由发卡方签发天然内聚;④ 犀见真正需要的只是「绑定发生了」这一事实,镜像足够。
- 风险与缓解:miniapp 需连调两个后端 → 顺序固定「先 redeem(权威)后通知犀见」;通知犀见失败不阻塞达人流程,靠 §4.3 的绑定对账轮询兜底补齐。
- 备选(若主会话裁定犀见自签):需下发 WCID_SECRET、复刻 bind.make_wcid(约 30 行,盘点 §5),消息台 verify 侧不变——代价是密钥同步与两处生成逻辑必须永远一致。
GET/POST/DELETE /api/conv/<cid>/binding(单会话 CRUD,POST body {influencerId,taskId,note},source=manual 优先级高于 wcid 自动绑定,不被覆盖);GET /api/binding/stats(总数+来源分布,看板用);按 influencer_id 反查见 §2.5 镜像方案。
GET/POST /api/conv/<cid>/price(存自己 SQLite,POST 需身份头且不选证据不能提交,金额以分、追加不覆盖)。GET /api/conv/<cid>/price 读 {ok,latest,history}),不要求消息台改代码调犀见(用户定案:老代码不动不管)。轮询集合受控——只轮「活跃商单关联的 cid」,不全量扫(§4.3)。POST /api/msgbridge/price-quotes {convId, peerId, influencerId, orderId, priceFen, evidenceUid, note, operator, confirmed, confirmedAt}。红线:evidenceUid 缺失 → 422;confirmed != true → 零写入(§6.2 判据 3);写入后挂 §7.5 三数一致校验与 R3 价差告警。资金相关 = 全评审链。业务阶段(基线 §3 的 12+2)权威在犀见,消息台侧的 handling(auto/escalated/taken/done)是会话处理态,两者不是一回事,禁止互相映射。犀见对外只读接口形状:
GET /api/msgbridge/stage?douyin_id=|phone=|influencer_id= → 当前阶段 + 关联商单;GET /api/msgbridge/influencer?douyin_id=|phone= → 档案(§6.8 判据)。
建议:同机回环 + 犀见侧固定 X-Msgbridge-Token(放 server .env,非业务 cfg)。v1 不实现。
| # | 缺口 | 处置 | 理由 |
|---|---|---|---|
| 1 | 无服务间认证 | 契约内解决 | 固定身份头 + 127.0.0.1 回环边界(§1.2);改消息台加凭证 = 动老服务,风险与收益不匹配;跨机升级钩子已留 |
| 2 | 无按 influencer_id 反查绑定 | 犀见侧自解 | binding_mirror 本地镜像(§2.5):反查是犀见的读需求,缓存在犀见侧最自然,零改动老服务 |
| 3 | 无事件推送/webhook | 接受现状轮询(策略见 §4.3) | 加 webhook 需改老服务且要处理投递重试;受控轮询在本量级(5 个企微号、百级会话)足够 |
| 4 | 发送无客户端幂等键 | 犀见侧自解 | touch_log 先占后发 + 超时不自动重试(§2.2);note 写 xijian:<batch_no> 供批量类回查。「每人每场景每日至多一次」本就是业务口径层的事,放犀见侧比改协议更贴切 |
| 5 | 「项目」三源不一致 | 契约内解决(约束式) | v1 禁止跨系统传递 projectId 语义:犀见→消息台不传 projectId;归因一律用 wcid + 犀见自己的 batch_id。主键体系对齐留 P0-4(待裁决 D2) |
GET /api/state。(scene, entity_id, fingerprint) 比对,值未变零写入、零事件(消息台 store.set_handling() 是同一纪律的先例,盘点 §3)。latest.ts 水位;binding 用 synced_at 对账时间戳。A. 走犀见配置中心(cfg,判定/开关;键名规范 域.蛇形名,需补入基线 §12A —— 补入动作待主会话批准,本次未改基线)
| 键 | 默认值 | 出处 |
|---|---|---|
msgbridge.mode |
off |
总纲领 P0-6「shadow 先行」;三态语义对齐盘点 §3 |
msgbridge.scene_mode.<scene> |
空(继承全局,取更保守档) | 同上 |
msgbridge.dedupe_window_hours |
24 | 无基线出处,待业务确认(D3) |
msgbridge.urge_batch_threshold |
30 | 工程取值(20 条/分反推),非业务口径(D4) |
msgbridge.broadcast_max_targets |
500 | 消息台硬限(盘点 §2.6),只读镜像不得调大 |
msgbridge.retry_max |
2 | 工程取值 |
msgbridge.rate_limited_backoff_minutes |
5 | 工程取值 |
msgbridge.poll.addfriend_job_seconds |
30 | 工程取值(§4.3) |
msgbridge.poll.binding_reconcile_minutes |
60 | 工程取值(§4.3) |
msgbridge.poll.price_minutes |
5 | 工程取值(§4.3) |
msgbridge.tpl.invite_text |
待确认 | 基线 §6.9;话术由犀见/运营提供(基线 §3 五要素说明),不编默认文案 |
msgbridge.tpl.urge_underperform_text |
待确认 | 基线 §6.10,同上(D5) |
msgbridge.tpl.urge_sla_link_fill_text |
待确认 | 基线 §4/§6.10,同上(D5) |
msgbridge.tpl.card_onboarding_title / _desc |
待确认 | 基线 §6.1(欢迎话术已配企微后台,卡片文案待核) |
B. 走 server 环境配置(.env,部署事实非业务口径,不进配置中心)
MSGBRIDGE_BASE_URL=http://127.0.0.1:5040、MSGBRIDGE_AUTH_MODE=header、MSGBRIDGE_AUTH_USER=xijian-server、MSGBRIDGE_AUTH_OID=system-xijian、MSGBRIDGE_TIMEOUT_READ=10、MSGBRIDGE_TIMEOUT_WRITE=30。
C. 明确不设本地副本:发送限流 20/分·300/时、加人 20/号/天 —— 见 §1.3,实时读消息台接口,禁止抄进 cfg。
| # | 事项 | PO 建议倾向 | 裁决 |
|---|---|---|---|
| D1 | wcid 归属:沿用消息台签发 vs 犀见 server 自签 | 沿用消息台签发 + 犀见镜像(理由见 §3.1 四条) | ✅ 采纳——密钥不跨服务、权威表单一,是唯一低险解 |
| D2 | 「项目」主键三源(lynoxi.task / 中台 project / 经营环 card)对齐 | v1 契约内回避(不传 projectId);主键体系在 P0-4 项目与计划域定案 | ✅ 采纳 |
| D3 | 同场景同人去重窗口 24h | 基线无此口径,建议按 24h 先跑并交业务确认后补入 §12A | ✅ 用户定案(二批-1):每日至多 5 次(msgbridge.daily_touch_limit=5 可配),touch_log 改计数制;已入基线 §12A |
| D4 | 单发/群发切换阈值 30 人 | 工程取值,允许开发期按实测调;不属业务口径 | ✅ 采纳 |
| D5 | 邀约/催发/卡片文案模板 | 待运营出话术;不编默认文案 | ✅ 用户定案(二批-2):全部可配,系统生成默认文案(运营后台可改)——tpl 键已填默认值 |
| D6 | send-miniprogram 的 taskId 语义(消息台 make_wcid 存 task_id,疑为 lynoxi.task.id) |
P0-6.3 实测确认;确认前留空,归因靠 wcid | ✅ 采纳 |
| D7 | 反向接口(阶段/档案/议价)鉴权形式 | 同机回环 + 固定 X-Msgbridge-Token,P1 实现 |
✅ 采纳 |
| D8 | 议价落库:犀见轮询拉 vs 消息台推 | v1 轮询拉(老代码不动不管);若日后允许改消息台,推送更省资源 | ✅ 采纳——与用户定案「老代码不动不管」一致 |
| D9 | 会话查看嵌入是否做 | P0 不做(镀金,§2.4);随 P2-16 前端融合再议 | ✅ 采纳——与「主工作面板即消息台」定调一致,运营直接用消息台界面 |
msgbridge.mode=off 时任一触达接口返回 {ok:false, reason:"msgbridge_off"} 且抓包零出站请求msgbridge.mode=shadow 时触达接口返回 200、审计有 mode=shadow 记录、mock 服务端零写类请求(P0-6.5 断言)scene=urge.underperform + trigger=auto → 422(基线 §6.10/§7.2 红线)(scene, influencer_id, 当日) 第二次调用返回 deduped,不产生第二次发送unknown,不自动重发X-Auth-User: xijian-server 与 X-Auth-Oid: system-xijian| 日期 | 版本 | 变更 |
|---|---|---|
| 2026-08-16 | v1 草案 | 首版:总则/双向场景/五缺口处置/配置清单/9 项待裁决。基于盘点 v1 + 主会话核实的部署事实(无 FEISHU_APP_ID、回环监听、同机部署、WCID_SECRET 与 XIJIAN_WELCOME_CARD 已配置) |
| 2026-08-16 | v1.0 定稿 | 主会话终验:D1–D9 全部裁决(§6 表),D3/D5 转用户确认清单;契约生效,P0-6.3/6.4/6.5 按此开发 |
| 2026-08-17 | v1.1 | 追加 scene:order.visitor_invite / order.group_pull(商单触发器场景,P0-5.1 S13);§2.5 注实现表名(S6) |