CGL SCRM  /  api

REST API

对外开放 API + MCP 端点

这是什么

一句话:让程序(而不是人)来跟这套系统打交道。

老系统完全没有这个能力——想拿数据只能在后台页面上人肉翻页、点导出。 我们新开了一组接口,把线索、活动报名、问卷、二维码、统计 都变成机器能直接读写的格式(JSON)。

它能用来干什么:

  • 让公司内部的 AI 助手(Aily 平台)自己去取数、做二次加工;
  • 跑定时任务,比如每天早上把线索同步到另一个系统;
  • 将来做小程序、大屏看板时,直接拿这组接口当数据源。
这块是「额外新增」,不碰原来的东西。 所有接口都收在 /api/v1/ 这个前缀下面, 和后台页面(/manager/)、客户看到的页面(/video/) 互不干扰。所以哪怕这些接口将来全改了,老页面也不受影响。

怎么证明「你是谁」

一共支持三种方式。每一种都是给不同的人用的,不要混。

API 钥匙
给程序和 AI 用的。放在请求头里:
X-API-Key: cgl_…
或者 Authorization: Bearer cgl_…。
每把钥匙带一组权限,只能干被允许的事。
后台登录会话
同事在浏览器里登录后自动带的凭证。
后台首页那块「实时看板」走的就是这条路, 这样数据和页面不会出现两个来源。
不用登录
少数几个刻意开放的:客户报名的页面、二维码图片、微信回调、体检地址。 完整清单在「安全模型」那一页,逐条列出来并说明为什么。

权限分得有多细(11 档)

钥匙不是「要么全能、要么不能」,而是按业务动作切开的。 给市场部同事发钥匙时,通常只给「看」的那几档。

权限允许做什么用在多少个接口上
event:read 看活动和报名名单 3 个
event:write 办活动、改活动、审报名、签到 3 个
lead:read 看线索(手机号、邮箱会打码) 4 个
lead:read_pii 看线索的真实手机号和邮箱(不打码)—— 单独一个权限,不轻易给 0 个
lead:write 改线索:加备注、跟进行为、改状态、打标签、合并重复 5 个
qrcode:read 看二维码台账 2 个
qrcode:write 生成新二维码 2 个
stats:read 看统计报表 8 个
survey:read 看问卷和答卷 3 个
survey:write 建问卷、改题目、提交答卷 1 个
token:admin 管理 API 钥匙本身(签发 / 吊销)—— 只给系统管理员 0 个
手机号默认打码:接口返回的线索里,手机号是 138****9999 这种形式。 要看真实号码,钥匙必须额外带 lead:read_pii 这一档。 这是故意加的护栏——老系统的导出表格里手机号全是明文,转发一次就外泄一次。

全部 36 个接口

下表「需要什么」那一列是从代码里现场读出来的,不是照着代码抄的。 空着的那几条说明它们不限定具体权限,只要身份有效就行——下面单独解释。

方式 接口地址 处理函数 需要什么 干什么
GET /api/v1/events api_events event:read 活动列表
GET /api/v1/events/{aid} api_event_detail event:read 活动详情(含统计/表单字段/分享码)
GET /api/v1/events/{aid}/signups api_event_signups event:read 报名名单
POST /api/v1/events/{aid}/signups api_event_signup event:write 代客报名(写线索 + 评分 + 归因)
POST /api/v1/events/{aid}/signups/{sid}/audit api_audit_signup event:write 报名审核
POST /api/v1/events/{aid}/signups/{sid}/checkin api_checkin event:write 签到
GET /api/v1/health api_health 无需登录 供 Aily / 监控探活。
GET /api/v1/leads api_leads lead:read 线索列表(分页 + 筛选)
GET /api/v1/leads/duplicates api_find_duplicates lead:read 查重(手机/邮箱/openid 三键)
GET /api/v1/leads/export.csv api_export_leads lead:read 导出线索 CSV。
POST /api/v1/leads/merge api_merge_leads lead:write 合并重复线索(新功能)
GET /api/v1/leads/{uid:int} api_lead_detail lead:read 线索详情(含标签/行为/备注/归因链)
POST /api/v1/leads/{uid:int}/behaviors api_add_behavior lead:write 写入线索行为(自动参与评分)
POST /api/v1/leads/{uid:int}/remark api_add_remark lead:write 追加跟进备注
POST /api/v1/leads/{uid:int}/status api_set_status lead:write 变更线索状态
POST /api/v1/leads/{uid:int}/tags api_set_tags lead:write 打标签 / 取消标签
GET /api/v1/meta/scopes api_scopes 有效钥匙即可 列出可用 scope
GET /api/v1/qrcodes api_qrcodes qrcode:read 二维码台账(新功能:原站不落档、不可统计)
POST /api/v1/qrcodes/event api_qrcode_event qrcode:write 生成活动带参二维码
GET /api/v1/qrcodes/preview.png api_qrcode_preview qrcode:read 内网演示 / 联调用:本地生成 PNG 直出,不产生微信归因。
POST /api/v1/qrcodes/survey api_qrcode_survey qrcode:write 生成问卷带参二维码
GET /api/v1/stats/behaviors api_stats_behaviors stats:read 行为分布
GET /api/v1/stats/channels api_stats_channels stats:read 渠道分布
GET /api/v1/stats/funnel api_stats_funnel stats:read 状态漏斗
GET /api/v1/stats/leaderboard api_stats_leaderboard stats:read 顾问业绩榜 + 活动/问卷排行
GET /api/v1/stats/overview api_stats_overview stats:read 总览指标(含环比)
GET /api/v1/stats/recent-leads api_stats_recent stats:read 最新线索(供大屏轮播)
GET /api/v1/stats/today api_stats_today stats:read 今日快照
GET /api/v1/stats/trend api_stats_trend stats:read 新增线索 / 报名 / 答卷 趋势
GET /api/v1/surveys api_surveys survey:read 问卷列表
GET /api/v1/surveys/{qid}/answers api_survey_answers survey:read 答卷列表
POST /api/v1/surveys/{qid}/answers api_survey_answer survey:write 提交答卷(自动评分并落线索)
GET /api/v1/surveys/{qid}/schema api_survey_schema survey:read 问卷结构(供 H5 / 外部表单渲染)
GET /api/v1/tokens api_tokens 后台登录 · 管理员 Token 列表(含归属人与签发人)
POST /api/v1/tokens api_token_create 后台登录 · 管理员 按人签发。
DELETE /api/v1/tokens/{tid} api_token_revoke 后台登录 · 管理员 停用 Token(软停,记录保留)

那几条「不需要具体权限」的

接口为什么可以只要身份、不要具体权限
GET /api/v1/health 体检用的:返回服务活着、连得上数据库、现在有多少条线索。 设计上就是公开的(代码摘要里写明了「无需鉴权」)。 唯一值得斟酌的是它顺带把线索总数也报了出来; 收窄这一点会改动对外约定,属于业务决定,已经记进缺陷台账。
GET /api/v1/meta/scopes 「我手里这把钥匙有哪些权限」——拿来自己查自己。必须先通过钥匙校验,但不需要额外权限。
GET/POST /api/v1/tokens
DELETE /api/v1/tokens/{id}
管理钥匙本身。走的是后台登录会话这条路——必须是登录的管理员,不能用钥匙来签发钥匙。

调用约定(写代码的人看)

列表返回什么一律是 {total, page, size, pages, items}——分页信息就在同一个信封里,不用另外去猜。
写操作返回什么{"ok": true, …};失败则是 HTTP 状态码 + {"detail": "…"}。
时间格式ISO-8601 带时区,按北京时间给出。数据库里存的仍是世界时——全局统一,避免「差 8 小时」这类问题。
参数填错直接拒绝并说清哪个参数不对。不会替你猜一个默认值然后继续跑。
字段名以 docs/MODEL_REFERENCE.md 为准。比如线索里是 firstname/lastname,不是直觉上的 name。

一个真实例子

# 问一句:最近有哪些线索?(只看前 5 条)
curl -H "X-API-Key: cgl_你的钥匙" \
     "https://scrm.jxccwx.com/api/v1/leads?page=1&size=5"

# 返回(手机号是打码的,因为这把钥匙没有 lead:read_pii)
{
  "total": 11348,
  "page": 1, "size": 5, "pages": 2270,
  "items": [ { "firstname": "…", "mobile": "138****9999" } ]
}

调试时可以打开 /api/docs 看自动生成的接口说明页—— 不过线上是正式环境,这个页面被刻意关掉了,只有开发和测试环境能看到。