XIJIAN DOCS

消息台 API 盘点

111 个业务 API 清单/发送安全机制/wcid 链路 · ← 返回文档中心

目录
1. 概览2. HTTP API 全量清单3. 发送安全机制4. 绑定与回流5. 可复用资产6. 给 P0-6.2 契约的候选接口7. 坑清单(照录,标出处)

消息工作台 API 全量盘点(P0-6.1)

调研对象:xijian-legacy/message/(Flask + SQLite,独立部署,115 机 /opt/message/,systemd 服务名 message用户定案不动不管)。 视角:犀见新 server 作为消费方,通过 HTTP 集成;不重写已验证的 QiWe 接入层。 方法:本地代码通读(app.py 1530 行 + api_ext.py 2091 行为主,13 个辅助模块),未 SSH,部署事实以代码/配置文件为准,标记处已注明「未核实」。


1. 概览

部署事实(从代码/配置推断,未 ssh 核实)

出处
技术栈 Python 3 + Flask + gunicorn(-w 1 --threads 4单进程)+ requests + pymysql message.serviceqiwe.pylynoxi.py
部署目录 /opt/message/(systemd WorkingDirectory message.service
systemd 服务名 message不是 README 里写的 xijianxiaoxi —— README 明显滞后于实际重命名,代码目录名、message.serviceXIJIAN_DB=/opt/message/xijian.db 三处互相印证) message.service vs README.md
监听地址 127.0.0.1:5040172.17.0.1:5040(docker 网桥地址,供跑在容器里的 nginx-ui 反代访问,见 app.py _gzip() 注释) message.service ExecStart
数据库 SQLite,XIJIAN_DB 环境变量指定路径,实际 /opt/message/xijian.dbstore.py 里的默认值 /opt/xijianxiaoxi/xijian.db 是旧路径,被 systemd 的 EnvironmentFile/Environment= 覆盖) message.servicestore.py:16
对外域名(当前,推断) message.lynoxi.com(独立域名,根路径服务)——nginx-lynoxi-api.conf 明确写着「工作台界面已迁到独立域名」,/message/message/*api.lynoxi.com 上做 301 跳转过去 nginx-lynoxi-api.conf:39-45nginx-message-domain.conf
对外域名(旧,推断已废弃) y1.wcc.cn/jingyinghuan/xijianxiaoxi/(README 首行仍写这个,nginx-message.conf 的路径前缀 + auth_request /_fa_verify 是这一代的产物)——与当前 feishu.py 登录闸、message.lynoxi.com 域名配置不一致,判断是历史遗留,未核实是否已下线 README.md:4nginx-message.conf
公网免鉴权端点固定在 api.lynoxi.com 上,不能迁 /qiwe/callback(企微回调→/ingest)、/qiwe/wcid/redeem(小程序回传→/api/wcid/redeem)、/qiwe/share.png(小程序卡片封面图)、/qiwe/up/*(待发送媒体公开目录,7 天清理) nginx-lynoxi-api.conf:26-76
登录鉴权 飞书 OAuth(feishu.py,复用犀见 admin 端同一飞书应用)签发 HttpOnly Cookie;FEISHU_APP_ID 未配置时鉴权整体旁路(见 §6 认证缺口);独立域名版靠 nginx Basic Auth(.htpasswd_message)兜底并透传 X-Auth-User app.py:54-72nginx-message-domain.conf:61-67
当前接入企微号数量 5 个(devices.json,2 个仅有 guid 靠自动发现补身份,3 个手工填了显示名) devices.json
前端 原生 JS 单页 web/index.html,服务端直接 serve,无独立构建 app.py:1233,1490

数据源拓扑(对理解「犀见该拿什么」很关键)

消息工作台自己接了 4 个外部数据源,犀见规划契约时必须分清楚"数据在谁的库里":

  1. lynoxi 生产库(MySQL,只读账号 msgboard_ro,7 张表)——lynoxi.py。〔口径更正 2026-08-17:犀见已定案独立新库 + 跨库只读,达人域数据一次性映射导入新表;对旧库仅跨库只读(xijian_app 只读授权),不经消息工作台转一手这一点不变〕。
  2. 消息工作台自己的 SQLite(xijian.db——会话、消息、绑定关系(peer_binding/wcid_token)、定价、群发/加人任务、指令记录……这些数据只存在这里,MySQL 里没有,犀见必须走 HTTP 才能拿到。
  3. 大千中台 open APIzhongtai.pyhttps://ai.iphome.cn/api/open/projects,Bearer token)——"项目"清单,priority>0 判定"正在执行"。
  4. 经营环本地文件jyh.py,读 /opt/jingyinghuan/cards/*.json/opt/jingyinghuan/web/daren-exec-gen-*.html)——项目卡/达人执行计划,代码注释明确写着这台机器上 /opt/jingyinghuan/cards 不存在PROJECTS 硬编码只有 ["shzoo","sz-xiaomeisha"] 两个项目,这条数据链路目前处于半失效状态(详见 §7)。
  5. (历史遗留、当前主流程已不用)大千数仓 dy_influencer——daqian.py,README 记录的"单位混用"坑就在这个源,app.py_lynoxi_profile() 的注释说明已经改读 lynoxi 而非数仓,但 daqian.py/cmdbus.py 仍 import 着它,未确认是否还有路径在用。

结论:项目/需求这个概念在系统里同时有 lynoxi.taskproject_id 全库恒为 1,靠标题关键词 zhongtai.match_title 归项目)、中台 projectzhongtai.py,另一套 id)、经营环 project cardjyh.py,第三套、半失效)三份,互不是同一个主键体系。犀见如果要复用"项目"概念,必须先决定以哪个为准,不能假设消息工作台传回来的 projectId 能直接跟 lynoxi 的什么字段对上。


2. HTTP API 全量清单

统计口径app.py 34 个业务路由 + 2 个静态兜底(//<path:fn>,不计入业务 API);api_ext.py(Blueprint ext)77 个;feishu.py(Blueprint feishu)4 个认证路由。业务 API 合计 111 个,另有 4 个认证路由、2 个静态路由。

2.1 消息收发

方法 路径 用途 关键参数 返回形状 备注/坑
POST /api/conv/<cid>/send 发文本 body {text} {ok,message,err,rateLimited?} 走进程内限流闸(§3);app.py:890
POST /api/conv/<cid>/reply 引用回复 body {text, quoteUid} {ok,degraded,note} 拼不出 reply 结构会降级成普通发送(degraded:true);api_ext.py:1181
POST /api/msg/<uid>/recall 撤回一条自己发的消息 - {ok,err} 认的是 msgServerId 不是 uid,历史/补录消息可能撤不了;auto.py 实现
POST /api/msg/<uid>/forward 转发到多个会话(≤20) body {targets:[convId]} {ok,sent,failed} 每个目标各过一次限流闸;api_ext.py:393
POST /api/conv/<cid>/send-card 发联系人名片 body {sharedId} {ok,name} api_ext.py:334
POST /api/conv/<cid>/send-location 发定位 body {title,address,lat,lng} {ok} 探店场景用;api_ext.py:362
POST /api/conv/<cid>/send-media 发图片/视频/文件/语音 body {kind,url,name} {ok} /api/upload 拿公网 URL 再调它,QiWe 上传端点只吃 JSON 不吃 multipart;api_ext.py:493
POST /api/upload 前端文件上传中转 multipart file {ok,url,name,size} 落盘到 nginx 公开只读目录 /var/www/qiwe-up,7 天清理
POST /api/conv/<cid>/send-miniprogram 发小程序卡片(wcid 关联机制) body {pageKey,title,desc,taskId} {ok,wcid,path} 犀见入驻绑定的核心接口,详见 §4;api_ext.py:683
POST /api/miniprogram/cover/init 初始化小程序卡片封面(企微 CDN fileId) query guid,url {ok,cover} 一次性/低频操作
GET /api/miniprogram/pages 可发小程序页面清单 - {appId,pages} appId 默认 wx120205414255fb5b,与犀见 miniapp 同一个小程序

2.2 会话

方法 路径 用途 关键参数 返回形状 备注/坑
GET /api/state 左栏全量:账号+会话列表+项目+usage+AI模式 - 大对象 面向整页刷新设计,非增量,几百会话时响应体较大(有 gzip)
GET /api/conv/<cid> 会话详情(消息+项目面板+对方情况+草稿) - 大对象 打开即标已读
POST /api/conv/<cid>/read 标已读 - {ok} 顺带同步企微已读状态
POST /api/conv/<cid>/handling 显式改会话四态 auto/escalated/taken/done body {state} {ok,handling,was,closed} 唯一允许改回 auto(交还)的入口,铁律见 §3
POST /api/conv/<cid>/takeover 人工接管(taken) - {ok,handling}
POST /api/conv/<cid>/done 标处理完但不交还 - {ok,handling}
POST /api/conv/<cid>/handback 交还给 AI - {ok,handling,resolved} 顺带关闭该会话未处理的转人工事项
POST /api/conv/<cid>/bind 绑定所属经营环项目(注意与 §4 的达人绑定是两回事 body {projectId} {ok} projectId 必须在 jyh.PROJECTS(当前只有 2 个硬编码项目)
POST /api/project/<pid>/brief 项目简介人工覆盖版 body {text} {ok}
GET /api/conv/<cid>/history 往上翻历史(游标分页) query before,limit≤200 {ok,messages,hasMore}
GET /api/search 全局搜索会话名+聊天内容 query kw {ok,convs,messages}
GET /api/conv-of 按 guid+userId 查会话 id query guid,userId {ok,convId} 通讯录"单发"用
POST /api/conv/new 主动发起会话(还没聊过的联系人) body {userId,guid} {ok,convId,name}
GET /api/conv/<cid>/alt-accounts 同一个人在别的企微号下的会话 - {ok,current,alternatives} 一个 peer 可能挂在多台设备下
GET/POST /api/conv-flags/api/conv/<cid>/flag 置顶/免打扰/标记 body {pin,mute,mark} {ok,flags} 存 kv,不动会话表
GET/POST /api/conv/<cid>/tags 会话(联系人)标签,走企微标签体系 body {tagIds,add} {ok,tags}
GET/POST /api/conv/<cid>/price 会话定价(人工标记,AI 不参与) body {priceYuan\|priceFen,evidenceUid,note} {ok,ts}/{ok,latest,history} 不选证据不能提交;金额以分入库、追加不覆盖;operator=unknown 时 403
GET /api/send-quota 各账号剩余发送额度 - {ok,quota} 给前端"先看到余量再决定"用

2.3 AI 自动回复

方法 路径 用途 关键参数 返回形状 备注/坑
POST /api/aimode 三态开关 off/shadow/live body {mode} {ok,mode} 切换即清空防抖队列
GET /api/aimode/queue 待转人工队列 - {items}
GET /api/aimode/activity 自动回复活动流水 query limit≤100 {items}
GET /api/aimode/watch 观察窗:AI 正在看的会话 query limit≤200 {items,stale,truncated,limit}
POST /api/conv/<cid>/suggest 生成/取回 AI 起草建议 body {trigger,model} 建议对象 有缓存命中即返回缓存
POST /api/escalation/<eid>/resolve 处理一条转人工事项 body {note} {ok}
GET /api/usage 今日 AI token/调用量 + 最近 20 条 - {todayTokens,todayCalls,recent} 不报分母(无日额度)

2.4 联系人 / 达人档案 / 绑定

方法 路径 用途 关键参数 返回形状 备注/坑
GET /api/projects 中台"正在执行"项目清单 - {ok,projects} 数据源见 §1 拓扑第 3 条
GET /api/requirements 达人需求(lynoxi.task)清单 query projectId {ok,total,requirements} 直读 lynoxi,犀见可跳过此接口直连库
GET /api/requirement/<tid> 需求详情 - {ok,requirement} 同上
GET /api/influencers/search 达人搜索(昵称/手机/抖音号) query kw {ok,influencers} 直读 lynoxi,犀见可跳过
GET /api/influencer/<iid> 达人档案大视图 - {ok,influencer} 同上
GET/POST/DELETE /api/conv/<cid>/binding 企微联系人↔犀见达人绑定的 CRUD body {influencerId,taskId,note} {ok,...,pendingCards} 只存在消息工作台 SQLite,犀见必须走这里拿api_ext.py:131
GET /api/binding/stats 绑定统计(总数+来源分布) - {ok,bound,bySource}
POST /api/wcid/redeem 小程序回传兑现绑定(无鉴权,HMAC 签名防伪) body {wcid,openId,unionId,influencerId?} {ok,bound,influencerId} wcid 机制核心落点,详见 §4
GET /api/contacts 通讯录检索(群发选人用) query kw,guid,tagId,bound,limit≤5000 {ok,total,contacts}
GET /api/contacts/enrich 批量补达人属性(等级/地区/类目) query influencerIds(逗号分隔,≤200) {ok,profiles}
GET /api/roster 名册(会话+通讯录合并视图) query scope=active\|all {ok,total,activeTotal,rows} 单接口全量返回,非分页
POST /api/roster/tag 批量/单行改标签 body {convIds,tag}{convIds:[1],tags:[]} {ok,changed}/{ok,tags}
POST /api/contact/<uid>/tags 不经会话直接给联系人打标签 body {guid,tagIds,add} {ok,changed,tags}
GET /api/tags 企微标签目录 query guid,force {ok,groups,tags}
POST /api/backfill/sender-names | /api/backfill/sender-avatars 回刷历史消息发送者昵称/头像 - {ok,fixed,...} 运维工具类接口

2.5 好友申请(addfriend,批量加人)

方法 路径 用途 关键参数 返回形状 备注/坑
GET /api/addfriend 任务列表+各号今日余量 - {ok,items,quota,config}
POST /api/addfriend/parse 只解析手机号不落库 body {text} {ok,phones,count}
POST /api/addfriend/upload Excel/CSV 解析手机号 multipart file {ok,phones,count} 纯标准库解析 xlsx,不依赖 openpyxl
POST /api/addfriend 建草稿任务 body {phones\|text,guids,mode,verifyText,note} {ok,id,job} mode: single\|balance
GET /api/addfriend/<jid> 任务详情(含逐条明细) - {ok,job}
POST /api/addfriend/<jid>/confirm 二次确认(数量原样输入) body {count} {ok}
POST /api/addfriend/<jid>/start | /cancel 启动/取消 - {ok,job} 后台线程异步跑
POST /api/addfriend/unpause 人工解除某号的风控挂起 body {guid} {ok,quota} 正常不该用
GET /api/addfriend/stats 实测速率/错误分布 query days {ok,byDay,errors,config} 上限没有官方文档,靠这个接口"测出真实上限"(§7 坑清单)

2.6 群发助手(broadcast)

方法 路径 用途 关键参数 返回形状 备注/坑
GET /api/broadcast 草稿/任务列表 - {ok,items}
GET /api/broadcast/rule 查企微自身群发规则 query guid {ok,rule} 查不到不等于不允许
POST /api/broadcast 建草稿(不直接发 body {guid,targets,msgs,sendType,note} {ok,id,draft} ≤500 人/次
GET /api/broadcast/<bid> 草稿详情 - {ok,draft}
POST /api/broadcast/<bid>/confirm 二次确认(收件人数量原样输入) body {count} {ok}
POST /api/broadcast/<bid>/send 真发(必须已 confirmed) - {ok,msg} 异步分批(20人/批,间隔3秒),走企微 /msg/sendGroupMsg不经过 §3 的单聊限流闸,是独立节流
POST /api/broadcast/<bid>/cancel 取消 - {ok,draft}

2.7 群管理(rooms)

方法 路径 用途 关键参数 返回形状 备注/坑
GET /api/rooms 所有在线设备的群合并列表 query guid? {ok,rooms,note} 只问在线设备
GET /api/room/<rid> 群详情(含成员) query guid {ok,room}
GET /api/room/<rid>/members 群成员(挨个试在线设备) query guid? {ok,members,guid,notice}
POST /api/room/<rid>/<action> 群操作:rename/notice/invite(安全)、kick/transfer/dismiss(危险,必须 confirm=群名原文 body 依 action {ok} 全程写 room_audit 审计表
GET /api/room/<rid>/qrcode 群二维码 query guid {ok,qrcode}
GET /api/room/<rid>/tops | POST /api/msg/<uid>/top 群置顶消息 - -
GET /api/rooms/audit 群操作审计日志 - {ok,log}

2.8 一键拉群(挂在需求单上)

方法 路径 用途 关键参数 返回形状 备注/坑
GET /api/requirement/<tid>/room-plan 建群前计划卡:谁能拉/谁连不上 - {ok,pullable,unreachable,guid,...} 按抖音号匹配企微身份,命中率取决于 peer_identity
POST /api/requirement/<tid>/room-create 确认后真建群 body {roomName,senderIds} {ok,roomId,pulled,missed,dropped,unreachable} 确认名单是冻结的:只在"确认集合∩此刻仍可拉"里动手;回写后台走 DEMAND_ROOM_WRITEBACK 环境变量,默认关(无接口契约)

2.9 指挥台(cmdbus,自然语言→计划→确认→执行)

方法 路径 用途 关键参数 返回形状 备注/坑
POST /api/cmd/plan 一句话解析成执行计划 body {text,convId} {ok,plan,err,guesses} AI 解析(走 cligw),read 档直接把结果一起带回来
POST /api/cmd/run 执行一条计划 body {planId,targetIds,draft,confirmWord} {ok,result,jobId,err} write 档需确认,mass 档异步返回 jobId
GET /api/cmd/job/<jobId> | POST /stop 群发类异步任务查询/中止 - -
GET /api/cmd/history 指令历史(留 30 天) query limit {items}
GET /api/cmd/examples 示例话术分组 - {groups}

支持的动作(ACTIONS 注册表,cmdbus.py:62525附近):读档 contact.search / daren.profile / conv.history / room.members / stat.ask;写档 contact.add / contact.accept / msg.send / room.create / room.invite / contact.remark / contact.tag;批量档仅 msg.mass删好友/踢群/解散群/发朋友圈这类不可逆动作,代码里根本没有实现这条路(不是开关关着)。支持 XIJIAN_CMD_DRYRUN=1 环境变量整体 dry-run(写类动作断在调企微接口前一步)。

2.10 设备 / 账号

方法 路径 用途 关键参数 返回形状 备注/坑
GET /api/devices/refresh 强制刷新设备状态 - {ok,accounts,stats} 平时 10 分钟缓存
POST /api/device/<guid>/remove 移出设备清单(不删历史) body {confirm=设备名} {ok,name,keptMessages,accounts}
POST /api/devices/add 手动并入新设备 body {guid} {ok,name,state,accounts} 必须过企微核验
POST /api/device/<guid>/login/start | GET /status | POST /verify 页面内扫码登录三部曲 - - 掉线设备必须人工扫码manualLogin 免扫码接口实测无效

2.11 其他

方法 路径 用途 关键参数 返回形状 备注/坑
GET /health 探活 - {ok} 免鉴权
POST /ingest 企微回调入口(QiWe → 这里) 企微 webhook payload {ok:true} 免鉴权,犀见不需要调它
GET /api/media/<uid>/raw | /voice | /urls | /download 媒体取用四件套 - - 企微 CDN 直链不能裸访问,需服务端代理
POST /api/media/<uid>/voice-to-text 语音转文字 - {ok,text,cached} 企微不给语音下载直链,转文字是唯一读法
GET/POST/DELETE /api/requirement/<tid>/docs*/api/doc/<...> 挂在需求单上的参考文档 - - 同需求下所有会话共用
GET/POST /api/phrases 快捷回复短语库 - {ok,phrases}

2.12 认证(feishu.py,独立 Blueprint)

方法 路径 用途
GET /auth/feishu/login 跳转飞书授权
GET /auth/feishu/callback 换 token、签 Cookie
GET /auth/me 当前登录人
GET /auth/logout 登出

3. 发送安全机制

三态门(AI 自动回复,auto.py

发送限流(单聊路径)

批量加人限流(addfriend.py

危险操作二次确认模式(跨模块统一套路,可直接复用到犀见的确认机制设计)

统一套路:前端先出计划/预览 → 要求把关键信息原样输入一遍才放行 → 全程留痕。四处独立实现,模式一致:

场景 确认要素 出处
群发 原样输入收件人数量 api_ext.py:869-878
批量加人 原样输入手机号数量 api_ext.py:979-988
移除设备 原样输入设备显示名 app.py:1122-1131
群踢人/转让/解散 原样输入群名 rooms.pyDANGEROUS = {"kick","dismiss","transfer"}
指挥台写类动作 confirmWord + XIJIAN_CMD_DRYRUN 环境变量整体断路 cmdbus.py

幂等 / 去重


4. 绑定与回流

企微不给外部联系人手机号/微信号/unionidcontactType 2057 实测全空;企微 unionid 与犀见小程序不在同一开放平台,884 个 unionid 对 6474 个达人 0 命中)——这是整个绑定机制存在的前提。现有两条路,全部存在消息工作台自己的 SQLite(peer_binding/wcid_token 表),MySQL 里没有这份数据

路径 A:wcid 回流(自动、管增量)

  1. 运营(或系统)触发 POST /api/conv/<cid>/send-miniprogram(或新好友自动欢迎卡片,见 _welcome_card() / XIJIAN_WELCOME_CARD=1 开关)。
  2. 服务端 bind.make_wcid(guid, peer_id, conv_id, task_id, page) 生成一次性 token(<随机串>.<HMAC-SHA256前16位>,密钥 WCID_SECRET),写入 wcid_token 表,拼进小程序 pagePath 的 query(如 pages/onboarding/onboarding.html?wcid=xxx.yyy)。
  3. 达人在小程序里打开卡片 → 小程序拿到 wcid + 微信 openId/unionId → 回传 POST /api/wcid/redeem无鉴权,靠 wcid 自身 HMAC 签名 + 30 天 TTL 防伪造,走 api.lynoxi.com/qiwe/wcid/redeem 固定域名)。
  4. 服务端校验签名后,优先用微信身份openId/unionId)反查 lynoxi 库拿 influencer_idlynoxi.by_open_id/by_union_id),拿不到才信小程序传上来的 influencerId 兜底——这是一条修复过的坑:曾经反过来信任小程序传的 id,导致小程序端 localStorage 残留的陈旧账号把聊天记录错绑到别人档案上。
  5. 写入/更新 peer_binding(已有人工绑定的不覆盖,人工优先级更高)。

MP_APPID 默认值 wx120205414255fb5b,与 CLAUDE.md 记录的犀见 miniapp AppID 完全一致——说明消息工作台的 wcid 机制本来就是绑定犀见小程序设计的,这条链路是犀见新 miniapp 大概率需要直接对接(或复刻)的现成协议。

路径 B:人工绑定(管存量)

GET/POST/DELETE /api/conv/<cid>/binding,运营在界面上手动搜索/选择达人,source=manual

犀见入驻绑定要对接的点


5. 可复用资产

资产 位置 说明
QiWe 最小客户端 qiwe.py 全文件(404行) 统一 call(method, params) 入口、login_info 三态判定(在线/离线/未登录,坑很细,见文件头注释)、掉线自动告警去重、设备自动发现/注销(内存+devices.json双写,原子替换)。若犀见将来需要直连 QiWe(而不是永远经消息工作台转发),这个文件是最省事的移植起点,但当前铁律是"不重造",优先级低。
进程内限流器模式 api_ext.py:1494-1521 简单有效的滑动窗口限流实现(无需 Redis),犀见若有类似"防外呼被封"场景可参考同一模式,但注意它是单进程内存态,多进程/多实例部署需要换成集中式存储。
三档危险操作确认模式 见 §3 表格 "计划预览 → 原样输入关键信息确认 → 执行 → 留痕" 是这几处独立实现但高度一致的模式,与 CLAUDE.md 铁律 4(危险外呼先 dry-run/确认机制)直接对应,犀见新 server 做危险外呼时可直接照搬这个交互契约。
XIJIAN_CMD_DRYRUN 环境变量整体断路模式 cmdbus.py:_call() 一个环境变量统一拦截所有写类企微调用,写类动作断在"调用前一步"而非事后补偿,是 dry-run 实现的一个干净范式。
批量加人的"风控自动熔断+双通道告警"模式 addfriend.py:_halt()notify.py 命中风控关键词立即挂起当天配额 + 飞书/企微双通道告警,犀见做 RPA/自动化外呼时的安全闸可参照。
"写库前判值是否真变" 先例 store.py:set_handling() (line ~1943) 犀见 CLAUDE.md 铁律 2 在这个仓库已有实践先例,不是新发明。
wcid HMAC 签名 token 生成/校验 bind.py:make_wcid/verify_wcid 短小(约30行),若犀见需要自己签发同类一次性追踪串,可直接照抄逻辑(token 格式、签名截断长度、TTL 判定)。
Excel/CSV 免第三方库解析手机号 api_ext.py:api_add_upload()(921-954行) 用标准库 zipfile 读 xlsx 的 sharedStrings.xml/sheet*.xml,不依赖 openpyxl,犀见若有类似批量导入场景可以直接借用思路。
Feishu OAuth 登录 + HttpOnly Cookie 自签会话 feishu.py 全文件(143行) 与犀见现有测试 token 登录、未来飞书 OAuth 整合(CLAUDE.md 技术选型记录里提到"飞书 OAuth 后补")思路一致,可作为实现参考(自签 token 格式:hex(payload).hmac前32位)。

6. 给 P0-6.2 契约的候选接口

按任务描述的四个场景对应:

一键定向邀约

催发通知

入驻卡片 / 绑定回流

会话查看(嵌入运营台)

认证缺口(贯穿所有场景的头号问题

现有鉴权只有两条路,都不是为程序化服务间调用设计的: 1. 飞书 OAuth Cookie(浏览器会话,犀见 server 后端进程无法参与); 2. nginx Basic Auth 透传 X-Auth-Userapp.py:_operator()X-Auth-Oid/X-Auth-User 请求头,这两个头是直接信任、不做校验的——nginx-message-domain.conf:66-67 特意清空客户端自带的 X-Auth-Oid 防伪造,说明这套机制假设"过了 Basic Auth 就是可信内网调用方")。

FEISHU_APP_ID 环境变量未配置,app.py:_guard() 的登录守卫直接整体旁路if not feishu.APP_ID: return None),这种情况下所有 /api/* 无需任何凭证即可调用——这是本次调研中最需要在 P0-6.2 明确核实的一点:当前生产环境是否配置了 FEISHU_APP_ID(本地 .env 不在仓库里,未 ssh 核实)。

建议 P0-6.2 讨论:是否需要给犀见 server 单独开一个 API Key / 内网 mTLS 之类的服务间凭证,或者约定犀见固定用一个 Basic Auth 账号 + 固定 X-Auth-Oid 值代表"系统自动化"这个操作人身份(定价、绑定这类接口会把 operator 落库,需要一个稳定可辨识的系统身份,不能是 unknown)。

缺口清单(一句话各是什么)

  1. 无服务间认证机制——只有浏览器 Cookie 和 nginx Basic Auth 透传头,犀见 server 后端进程没有干净的凭证路径。
  2. 无按 influencer_id 反查绑定的接口——只能按 peer_id(企微侧 userId)查,犀见若要"这个达人绑了哪个企微身份"需要自己在本地缓存 /api/roster/api/contacts 的全量结果做反查。
  3. 无事件推送/webhook——犀见若想知道"某达人刚绑定成功""某会话进入某聊天阶段""收到新消息",只能轮询现有只读接口,且这些接口是为人工 UI 刷新设计的(全量而非增量),不天然支持"自上次轮询以来变化了什么"这种增量查询,容易撞上 CLAUDE.md 铁律 2 提到的轮询去重/binlog 教训。
  4. 发送侧无客户端幂等键——POST /api/conv/<cid>/send 等接口不接受调用方传入的请求 ID 做去重,犀见如果因为超时重试而重复调用,会真的重复发消息;需要犀见自己在业务层做"这条消息今天有没有发过"的判断,或推动消息工作台加一个幂等参数。
  5. "项目"概念三源不一致(见 §1 拓扑),P0-6.2 若涉及按项目圈人/催发,要先跟 product-owner/口径基线确认犀见自己的项目主键体系,不能假设消息工作台传回的 projectId 直接可用。

7. 坑清单(照录,标出处)