HTTP 接口文档
外呼派发接口背后的领取、容量、originate 与终态闭环见 外呼任务生命周期。
callout-server 默认监听 0.0.0.0:9920。所有接口(除特别说明)都走会话鉴权,所有响应都用统一信封。
通用约定
响应信封
jsonc
// 成功
{ "code": "0000", "data": { /* ... */ }, "msg": "获取成功" }
// 失败
{ "code": "404", "data": null, "msg": "中文错误信息" }三个顶层字段始终存在,成功码固定为字符串 "0000",失败码通常与 HTTP status 一致。SSE 事件、二进制下载、音频流和 OPTIONS 预检不套用 JSON 信封;业务数据内部 原本名为 ok 的字段保持不变。 HTTP 状态码:200 成功;400 参数错误;401 未鉴权/凭证无效;403 权限不足; 404 资源不存在;409 冲突(重复编码/忙碌中/锁竞争/非空删除);410 资源已过期; 500 服务端错误;502/503 下游(FreeSWITCH/Redis)不可用。
鉴权
启用鉴权(auth.enabled=true;代码缺省为 false,仓库 config.toml 已置为 true)时:
POST /api/auth/login用用户名/密码换取token。- 后续请求带
Authorization: Bearer <token>。 GET /api/auth/me返回当前用户与其permissions列表(前端据此做菜单/按钮门控)。
本地联调可设 CALLOUT_AUTH_ENABLED=false 关闭鉴权。启用时自动创建引导管理员 (默认 admin / 见部署配置,可用 CALLOUT_AUTH_BOOTSTRAP_ADMIN_USERNAME/PASSWORD 覆盖)。
内部服务间通信分两类鉴权:
- 仅内部 token:
/api/call-results、/api/call-progress、/api/transfers/route、/api/transfers/end、/api/scripts/runtime只校验请求头X-Callout-Internal-Token与server.serviceApi.authToken相等(配置为空时放行)。 - 会话或内部 token:AI 一族接口(
/api/ai/nlu/analyze、/api/ai/chat/stream、/api/ai/chat/completions、GET /api/ai/conversation/capabilities、/api/ai/generate、/api/ai/simulate-user)走requireAuth():可携带管理端 Bearer 会话, 或请求头X-Callout-Internal-Token(须与已配置的server.serviceApi.authToken相等,配置为空时不放行)。
角色与权限矩阵
| 权限 | admin | supervisor | agent |
|---|---|---|---|
admin(用户/系统) | ✓ | ||
audit:read(审计) | ✓ | ||
line:write(线路) | ✓ | ||
campaign:write(任务) | ✓ | ✓ | |
campaign:dispatch(派发/会议监控/主管介入) | ✓ | ✓ | |
policy:write(策略) | ✓ | ✓ | |
compliance:write(DNC 合规) | ✓ | ✓ | |
compliance:update(编辑免打扰号码) | ✓ | ||
import:write(导入) | ✓ | ✓ | |
contact:write(联系人/结果) | ✓ | ✓ | ✓ |
agent:write(坐席花名册管理) | ✓ | ✓ | |
agent:operate(坐席自助:状态/呼出/回拨/转人工/踢会议成员) | ✓ | ✓ | ✓ |
ai:strategy(NLU / Chat 策略配置) | ✓ | ✓ | |
ai:usage(AI 用量与调用明细) | ✓ | ✓ | |
ai:generate(AI 生成流程脚本 / 策略) | ✓ | ✓ | |
ai:simulate(流程演示模拟用户) | ✓ | ✓ |
agent:write(管理坐席)与agent:operate(坐席本人操作)刻意分离:坐席能操作软电话工作流,但不能管理花名册。
上表是角色内置权限(services/auth/auth.ts 的 rolePermissions,随角色固定)。 compliance:update 是独立的编辑按钮权限,普通角色需要在系统管理页显式授权,不默认授予主管或坐席。 合规页面访问仍需 compliance:write,因此普通用户使用页面编辑功能需要同时具备这两个权限。 超级管理员及本地免登录模式可编辑免打扰号码。 /api/users/*、/api/roles/*、/api/menus/* 一族另用一套数据库按钮权限(system:user:* / system:role:* / system:menu:*,由 db/seed.ts 播种进 callout.role_buttons),判定顺序是: 超级角色 R_SUPER、role=admin、或 buttons 里含 admin 通配 → 直接放行;否则要求 buttons 精确包含该权限码。因此这组权限不能靠改角色获得,要在系统管理页给角色勾按钮。
路由风格、分页与搜索
- 纯静态扁平路径:严禁在 URL 中使用动态路径参数(如
/:id),无参或简单标量查询使用GET,复杂筛选分页与所有变更动作统一使用POST + JSON扁平请求体。 - 分页与搜索:分页列表接口统一使用
POST + JSON请求体(如{ "page": 1, "pageSize": 50, "q": "..." }),分页响应统一为{ rows, page, pageSize, total }。 - 批量/单条删除:统一走
POST /api/<resource>/delete,请求体接收{ "ids": [...] }(单删传单元素数组[id]),响应返回{ "ids": [...], "deleted": number }。
鉴权与权限
POST /api/auth/login
请求:{ "username": "admin", "password": "******" } 响应 data:
jsonc
{
"token": "<bearer-token>",
"expiresAt": "2026-06-16T09:00:00.000Z",
"user": { "id": 1, "username": "admin", "displayName": "管理员", "role": "admin" },
"permissions": ["admin", "campaign:write", ...]
}POST /api/auth/refresh
显式刷新当前登录会话有效期(顺延 auth.sessionTtlSeconds)。请求头携带当前 Authorization: Bearer <token>,响应 data 返回最新到期时间:
jsonc
{
"expiresAt": "2026-06-17T09:00:00.000Z"
}NOTE
系统同时内置会话自动滑动续期:普通请求到达时,若会话剩余生命周期不足总 sessionTtlSeconds 的 50%,服务端会在后台异步自动顺延,前端一般无需高频显式轮询刷新。
POST /api/auth/logout
注销当前会话(请求头带 Bearer Token)。data 返回已注销用户摘要。
GET /api/auth/me
返回当前用户与其 permissions 列表;未登录(或凭据无效)时直接返回 401。
GET /api/user/events
当前登录用户的全局实时事件流(text/event-stream,需登录)。由系统路由模块提供,连接建立时先发一帧 connected;同一账号在别处登录时,服务端向旧连接推 kickout ({ reason: "duplicate_login", message, timestamp })后主动断开,前端据此弹提示并跳登录页; 坐席状态发生流转时(预占、接听、释放、切状态)毫秒级下发 agent_status 事件驱动前端原地变色; 其余时间周期性下发 : ping\n\n 心跳注释维持连接。鉴权关闭时不做登录校验。
POST /api/auth/change-password
登录用户修改本人密码(请求体 { "oldPassword": "...", "newPassword": "..." })。
仪表盘、运行态与 AI 策略中心
GET /api/ai/nlu/capabilities
由 callout-server 原生提供 NLU 能力发现接口。响应 data.actions 包含 decision / extract,每项提供已启用的 profiles[{ key, labelZh, descriptionZh }](平台已彻底废除默认概念,不再隐式回退,调用执行接口必须显式传参)。
注意:NLU 执行接口
POST /api/ai/nlu/analyze同样由callout-server原生提供(默认端口9920),供callflow-esl直连调用。
GET /api/ai/chat/capabilities
同一模式的 Chat 能力发现接口。 流程脚本 chat 节点的 chat_profile 只能取自本接口返回的 profile key, 见 流程脚本 Chat 节点开发。
AI 策略、模型与运行时生成(/api/ai/* 与 /api/scripts/*)
| 方法 | 路径 | 权限 | 说明 |
|---|---|---|---|
| GET | /api/ai/llm-config | 会话/内部Token | 获取当前 LLM 全局推理配置、连接状态与场景模型映射 |
| POST | /api/ai/llm-config/update | ai:strategy | 更新 LLM 全局推理配置、API Key 与场景模型超时参数 |
| POST | /api/ai/llm-config/test | 会话/内部Token | 测试指定 OpenAI 连接参数连通性并实时探测拉取可用模型列表 |
| GET | /api/ai/models | 会话/内部Token | 查询当前已连接/已启用的推理模型列表 |
| POST | /api/ai/usage/list | ai:usage | 分页查询 AI 调用用量明细与耗时记录(请求体 page、pageSize、service、action、profile、outcome) |
| POST | /api/ai/usage/summary | ai:usage | 查询 AI 调用总量、成功率分布与趋势看板(请求体 { "days"?: 14 }) |
| POST | /api/ai/nlu/analyze | 会话/内部Token | 统一 NLU 决策判定(action=decision)或槽位抽取(action=extract),返回 debug 诊断信息 |
| POST | /api/ai/nlu/eval | ai:strategy | 运营控制台单步调试 NLU 提示词与意图识别效果(返回识别意图与槽位结果) |
| GET | /api/ai/nlu/capabilities | 会话/内部Token | 查询 NLU 支持的意图识别动作、模型以及可提取槽位元数据 |
| POST | /api/ai/chat/stream | 会话/内部Token | 大模型多轮流式 SSE 对话接口,流式下发 delta 与 complete |
| GET | /api/ai/chat/capabilities | 会话/内部Token | 查询大模型对话能力与支持的模型列表 |
| POST | /api/scripts/node-prompt-update | campaign:write | 原子局部更新指定流程节点的 Prompt 属性与微调超参(请求体含 scriptId、nodeId) |
| POST | /api/ai/chat/completions | 会话/内部Token | 执行独立问答与对话策略(支持 start 与 query 模式,供知识库业务直连) |
| GET | /api/ai/conversation/capabilities | 会话/内部Token | 查询已注册的独立对话策略列表与元数据 |
| POST | /api/ai/generate | ai:generate/内部Token | 按自然语言业务需求一键生成外呼流程图脚本(请求体 { "description", "last_error"? }) |
| POST | /api/ai/generate-profile | ai:generate/内部Token | 按流程节点属性与一句话指令生成对应 Prompt 与策略配置(请求体 { "action", "node", "variableCatalog"?, "instruction"? }) |
| POST | /api/ai/simulate-user | ai:simulate/内部Token | 流程演示:collect/router 判定与模拟客户回答(chat 节点改走 /api/ai/chat/stream,与运行时同源) |
AI 全局配置与用量数据持久化存储于 PostgreSQL callout schema(表 ai_llm_configs 与 ai_usage_records)。流程节点策略与 Prompt 直接内联持久化于 callout.scripts 表的 flowJson 结构中(通过 POST /api/scripts/node-prompt-update 原子维护,无需全量重写画布)。三栏式话术策略运营工作台(含大纲树、微拓扑、一句话 Prompt 生成与常驻即时拨测仿真)原生内置于 callout-webpage 的「AI 策略中心」导航中。
独立对话策略(/api/ai/chat/completions)
独立对话策略是一套确定性、轻量级的单/多轮问答机制,与大模型 Chat Profile 相互独立。它采用确定性 JSON 请求/响应,不调用模型推理,直接供 callflow-esl 侧的知识库录音对话业务(如 kb-audio-chat-business)或联调测试使用。
GET /api/ai/conversation/capabilities
查询当前已注册的对话策略能力元数据:
jsonc
{
"code": "0000",
"data": {
"strategies": [
{
"name": "kb-mock",
"description": "以确定性文本回显模拟知识库问答,用于本地联调。"
}
]
},
"msg": "请求成功"
}POST /api/ai/chat/completions
执行对话策略。请求头携带 X-Callout-Internal-Token。支持两种动作模式:
- 会话开启 (
action: "start"):- 请求:
{ "strategy": "kb-mock", "action": "start", "sessionId": "sess-123" } - 响应
data:jsonc{ "openingPrompt": "您好,请问有什么可以帮您?", "errorPrompt": "抱歉,系统暂时没有找到相关内容,请您换个说法再试试。" }
- 请求:
- 多轮问答 (
action: "query"):- 请求:
{ "strategy": "kb-mock", "action": "query", "sessionId": "sess-123", "text": "咨询套餐" } - 响应
data:jsonc{ "answerText": "知识库模拟回答:咨询套餐", "hangup": false }
- 请求:
内部流程生成与模拟(/api/ai/generate* 与 /api/ai/simulate-user)
同时支持管理端会话(ai:generate / ai:simulate)或内部服务 Token(X-Callout-Internal-Token)调用:
POST /api/ai/generate:请求{ "description": "业务需求描述", "last_error"?: "..." },返回由大模型推理生成的完整流程节点图(nodes、edges)及建议新策略。POST /api/ai/generate-profile:请求{ "action": "decision" | "extract" | "chat", "node": {...}, "variableCatalog"?: [...], "instruction"?: "..." },返回针对当前节点生成的system_prompt与微调超参。POST /api/ai/simulate-user:请求{ "mode": "generate" 或 "classify"(必填), "node_type"?, "speaker"?: "user"/"bot", "bot_prompt"?, "user_text"?, "dialogue_history"?, "branches"?, "fields"?, "variables"? };mode=generate(需bot_prompt)返回模拟客户回复(user_reply),mode=classify(需user_text)按node_type返回意图判定或字段抽取结果。
仪表盘与运行态接口
| 方法 | 路径 | 权限 | 说明 |
|---|---|---|---|
| GET | /health | 公开 | 服务与数据库连通性 |
| GET | /api/dashboard/overview | 会话 | 数量、比率、近期活动/呼叫尝试 |
| POST | /api/dashboard/trends | 会话 | 按天聚合的呼叫/结果趋势(请求体 { "days"?: 14 },范围 1–90) |
| POST | /api/dashboard/call-stats | 会话 | 呼叫统计分布聚合(请求体 { "campaignId"?: 1, "lineId"?: 1, "days"?: 7, "startDate"?: "...", "endDate"?: "..." },默认最近 7 天) |
| GET | /api/media/config | 会话 | 前端拼接录音和情绪复核音频播放地址所需的 baseUrl |
| POST | /api/media/list | 会话 | 分页查询指定类型的音频媒体元数据(请求体 { "page", "pageSize", "type"?: "recording" / "prompt" / "tts" }) |
| POST | /api/media/delete | campaign:write | 删除指定音频媒体文件(请求体 { "ids": [...] }) |
| GET | /api/runtime/overview | 会话 | 运行中并发、卡住联系人、各任务进度、各线路并发、近期运行事件 |
| GET | /api/dashboard/runtime/overview | 会话 | /api/runtime/overview 的等价别名 |
| GET | /api/runtime/events | 会话 | SSE 运行事件流(text/event-stream) |
| POST | /api/runtime/events-acknowledge | 会话 | 确认某条运行事件/告警(请求体 { "eventId": 1 }) |
| POST | /api/audit/list | admin | 敏感操作审计日志(请求体 { "page", "pageSize", "q", "action", "resourceType", "operatorId" }) |
| GET | /api/system/config | admin | 脱敏系统配置(网络/鉴权/日志/媒体/运行时版本) |
| GET | /api/system/stats | admin | 运行时长、内存/CPU、SSE 在线连接数与数据库连通性 |
| POST | /api/system/reload | admin | 手动热重载运行时组件(请求体 { "target"?: "config" / "cache" / "rate-limiter" / "all", "reason"? },默认 all) |
GET /api/runtime/overview 的 data(RuntimeOverviewResponse)含:activeContacts、stuckContacts、 runningBatches、scheduler.{submittedLastMinute,failedLastMinute,completedBatchesLastMinute}、 campaignProgress[]、lineConcurrency[]、cellConcurrency[](各 FS cell 的 { cellId, maxConcurrency, active },maxConcurrency 0 = 不限制)、stuckBatches[]、recentEvents[]。
业务编码(callflow 业务)
GET /api/businesses/list(或 POST /api/businesses/list)
列出 callflow 库中的 call_businesses,供创建任务时选择 businessCode(另提供 POST /api/businesses/detail、POST /api/businesses/create、POST /api/businesses/update、POST /api/businesses/delete 管理接口)。 响应统一包装在标准 ApiResponse({ code: "0000", data: { businesses: Business[] }, msg: "请求成功" })中,businesses 是按场景分类排序后的数组:
jsonc
{
"code": "0000",
"data": {
"businesses": [
{
"businessCode": "callout-flow-business",
"businessName": "通用外呼流程解释器",
"businessCategory": "outbound",
"isEnabled": 1,
"remark": null
}
]
},
"msg": "请求成功"
}businessCategory 是代码注册表同步的四值主场景分类(outbound / inbound / assist / demo),仅供展示/分组;创建和更新任务仍提交 businessCode,分类不进入 campaign 数据、派单判断或 FreeSWITCH 变量。
流程脚本 scripts
流程脚本是系统二的话术资产:前端可视化编排节点图(start / send / collect / router / var_router / chat / end / transfer),运行时由 callflow-esl 的 callout-flow-business 按 runtime 接口输出的 JSON 逐节点执行。NLU 与对话节点契约分别见 flow-script-nlu-development.md 和 flow-script-chat-development.md。
| 方法 | 路径 | 权限 | 说明 |
|---|---|---|---|
| GET | /api/scripts/list | 会话 | 列出流程脚本,供外呼活动选择与管理页展示 |
| POST | /api/scripts/detail | 会话 | 获取单个脚本详情(请求体 { "scriptId": 4 }) |
| POST | /api/scripts/save | campaign:write | 新建或更新流程脚本(更新时请求体传 scriptId;含名称、说明与 flowJson 节点图) |
| POST | /api/scripts/clone | campaign:write | 克隆指定流程脚本(请求体 { "scriptId": 4, "name"?: "..." }) |
| POST | /api/scripts/draft-publish | campaign:write | 发布前图结构校验并刷新脚本更新时间(请求体 { "scriptId": 4, "status": "draft" 或 "published" };脚本无发布态字段,运行时不做发布过滤) |
| POST | /api/scripts/node-prompt-update | campaign:write | 原子局部更新指定流程节点的 Prompt 与超参(请求体含 scriptId、nodeId) |
| POST | /api/scripts/delete | campaign:write | 软删除脚本,保留历史引用(请求体 { "ids": [1] }) |
| POST | /api/scripts/runtime | 内部 token | 输出 callout-flow-business 执行用的运行时 JSON(请求体 { "scriptId": 4 }) |
POST /api/scripts/runtime 不走会话鉴权:与 /api/call-results 一样比对 X-Callout-Internal-Token 与 server.serviceApi.authToken(配置为空时放行),供 callflow-esl 在通话内拉取。
外呼线路 lines
线路决定主叫号码与并发;任务必须绑定至少一条启用线路。
| 方法 | 路径 | 权限 |
|---|---|---|
| GET | /api/lines/list | 会话 |
| POST | /api/lines/create | line:write(仅 admin;numbers[] 差量维护号码池) |
| POST | /api/lines/update | line:write(请求体含 lineId;numbers[] 差量维护号码池) |
| POST | /api/lines/status | 会话(线路状态快照,请求体 { "lineId"?: 1 },仅查询不启停) |
| POST | /api/lines/delete | line:write(请求体 { "ids": [1] },仅软删未被引用的线路) |
| POST | /api/lines/test-dial | line:write(线路拨测,请求体 { "lineId": 1, "targetNumber": "...", "callerId"?: "..." }) |
POST /api/lines/create 请求体(CreateLineInput):
jsonc
{
"name": "默认外呼线路",
"code": "default-trunk",
"dialStringTemplate": "sofia/gateway/my-trunk/{{destinationNumber}}",
"callerIdNumber": "01088886666", // 可选,single_shared 模式默认主叫
"callerIdName": "示例企业", // 可选
"maxConcurrency": 10, // 该线路下所有任务共享
"allocationMode": "single_shared", // single_shared | number_pool | bound_numbers
"callerIdHeaderMode": "caller_id_only", // caller_id_only | p_preferred_identity | from_and_p_preferred_identity
"numbers": [
// number_pool / bound_numbers 模式的号码池
{
"number": "01088886601",
"weight": 2,
"maxConcurrency": 1,
"isEnabled": 1
}
],
"isEnabled": 1,
"description": "..."
}号码分配模式:single_shared(一号多并发)/ number_pool(多号轮换,按 weight)/ bound_numbers(一号一并发)。响应 data.line: CalloutLineRow(含 numbers[])。
外呼任务 campaigns
| 方法 | 路径 | 权限 | 说明 |
|---|---|---|---|
| POST | /api/campaigns/list | 会话 | 列表(请求体 { "page", "pageSize", "q", "status" }) |
| POST | /api/campaigns/create | campaign:write | 创建任务 |
| POST | /api/campaigns/detail | 会话 | 详情(请求体 { "campaignId": 1 }):任务 + 联系人总数 + 近期呼叫尝试 + 批次 |
| POST | /api/campaigns/update | campaign:write | 更新(请求体含 campaignId 及状态/并发/重试/工作日/绑定线路/业务配置等) |
| POST | /api/campaigns/status-update | campaign:write | 切换任务状态(请求体 { "campaignId": 1, "action": "start" / "pause" / "stop" / "archive" }) |
| POST | /api/campaigns/delete | campaign:write | 软删任务(请求体 { "ids": [1] },直接软删,无引用校验) |
| POST | /api/campaigns/preflight | 会话 | 启动前预检(请求体 { "campaignId": 1 }):状态、计划窗口、待呼名单、线路、策略 |
POST /api/campaigns/create 请求体(CreateCampaignInput):
jsonc
{
"name": "外呼平台演示任务",
"businessCode": "callout-flow-business", // 必须能在 call_businesses 查到启用记录
"cellId": null, // 可选;绑定 config.cells 声明的 FS cell,空 = 派发时自动分配
"lineIds": [1], // 至少一条启用线路
"maxConcurrency": 5,
"maxRetries": 2,
"retryIntervalSeconds": 600,
"callIntervalMs": 1000,
"workdayStart": "09:00", // 可选 HH:mm
"workdayEnd": "20:00",
"scheduledStartAt": null,
"scheduledEndAt": null,
"businessConfig": {
"variables": { "script_id": "4" } // 仅字符串值会注入通道变量
},
"description": "..."
}POST /api/campaigns/update(或 POST /api/campaigns/status-update)把 status 改为 running 即启动(被调度器扫描);改 paused 暂停自动派发。 启动校验仅发生在 POST /api/campaigns/status-update 的 action=start:若既无待呼/可重试联系人,也无活跃通话,返回 HTTP 409 且保留原状态;已归档任务同样不可启动。 允许提前启动并以 running 等待 scheduledStartAt;若设置了 scheduledEndAt,启动接口会重新取服务端时间, 当前时间大于或等于结束时间时返回 HTTP 409,状态保持不变。预检通过后到期或直接调用启动接口均受此限制。 POST /api/campaigns/update 直接落库、不做该校验。等待未来重试或仍有活跃通话时允许保持/切换为 running。
POST /api/campaigns/preflight 返回 data: { allowed, checks };每项检查包含 { code, ok, message }。 scheduled_window 只检查计划结束时间是否已到;未设置结束时间时该项通过。 未来排期通过时提示“允许启动,等待计划开始时间后派发”;allowed=true 表示当前任务允许启动, 立即派发仍须满足服务端计划窗口 [scheduledStartAt, scheduledEndAt)、策略、工作时段、间隔和容量约束。 各检查编码与请求、响应结构保持不变。详细等待与完成语义见外呼任务生命周期。
cellId 传字符串改绑 cell(须在 config.cells 中声明),传 null 或空串解绑。 businessConfig 使用整组替换语义,不接受旧的顶层 variables / intentKeywords。空的 variables 或 intentKeywords 会被省略。知识库实时盯屏任务可在业务专属配置中提交 { "businessCode": "kb-audio-chat-business", "businessConfig": { "intentKeywords": ["可以", "有兴趣"], "monitoringMode": "elastic", "monitoringPriority": 50 } }。 monitoringMode 支持 strict(无人时停止新派发)、elastic(允许 AI 无人值守)与 unattended;这两个监控属性只对 kb-audio-chat-business 生效,切换到其它业务时服务端会清理。
POST /api/campaigns/detail 的 data(CampaignDetail):{ campaign, contactTotal, attempts[], batches[] }。
联系人 contacts
| 方法 | 路径 | 权限 | 说明 |
|---|---|---|---|
| POST | /api/contacts/list | 会话 | 列表(请求体 { "campaignId", "page", "pageSize", "q", "status" }) |
| POST | /api/contacts/create | contact:write | 粘贴号码同步导入,单次最多 1,000 行(请求体含 campaignId) |
| POST | /api/contacts/update | contact:write | 更新单个联系人(请求体含 campaignId、contactId) |
| POST | /api/contacts/pause | contact:write | 批量暂停(请求体 { "campaignId", "contactIds" },避免运行中继续派发) |
| POST | /api/contacts/retry | contact:write | 重置指定联系人为 pending(请求体 { "campaignId", "contactIds" }) |
| POST | /api/contacts/delete | contact:write | 批量/单条软删(请求体 { "campaignId": 1, "ids": [...] },campaignId 必填) |
POST /api/contacts/create 请求体(CreateContactsInput):
jsonc
{
"campaignId": 1,
"contacts": [
{
"phoneNumber": "13800138000",
"customerName": "王先生",
"remark": "...",
"metadata": { "orderId": "A123", "level": "vip" }
} // 仅字符串值会作为通道变量注入
]
}响应与文件导入一致,包含 totalRows / createdRows / skippedRows / duplicateRows / failedRows / errors。 errors 每项只有 { rowNumber, phoneNumber, reason };计数恒满足 totalRows = createdRows + duplicateRows + failedRows、skippedRows = duplicateRows + failedRows。 手工新增与粘贴入口不限制号码格式,只要求号码非空,并由服务端唯一索引最终去重。 CSV/TSV 文件导入仍按提交的地区、运营商和固话选项执行号码校验。
派发、批次与重试
| 方法 | 路径 | 权限 | 说明 |
|---|---|---|---|
| POST | /api/campaigns/dispatch | campaign:dispatch | 手动派发,最多 100 个待呼联系人(请求体 { "campaignId": 1, "limit": 20 }) |
| POST | /api/campaigns/retry | campaign:write | 把匹配联系人重置为 pending(请求体 { "campaignId": 1, "status"?: "failed", "maxAttempts"?: 2 }) |
| POST | /api/campaigns/batches-list | 会话 | 派发批次历史(请求体 { "campaignId": 1, "page": 1, "pageSize": 20 }) |
| POST | /api/campaigns/batches-cancel | campaign:write | 取消仍在 running 的批次(请求体 { "campaignId": 1, "batchId": 42 }) |
| POST | /api/attempts/list | 会话 | 呼叫明细列表(请求体含 page、pageSize、campaignId、status、callStatus、emotionStatus) |
| POST | /api/attempts/detail | 会话 | 按 ID 返回完整、未软删除的呼叫明细(请求体 { "attemptId": 555 });不存在时返回 404 |
POST /api/campaigns/dispatch 请求体 { "campaignId": 1, "limit": 20 }(limit 校验范围 1–100,越界返回 400)。 响应(DispatchCampaignResponse)data.result(DispatchResult):
jsonc
{
"campaignId": 1, "batchId": 42,
"requestedLimit": 20, "effectiveLimit": 20, "requested": 8,
"submitted": 8, "failed": 0, "remainingPending": 12,
"campaignStatus": "running",
"attempts": [
{ "contactId": 100, "phoneNumber": "138...", "attemptId": 555,
"status": "accepted", "requestId": "<uuid>", "replyText": "+OK ...",
"callStatus": "answered", // 启用回铃检测时带
"ringbackDetection": { "status": "answered", "detectionSource": "answer", ... } }
]
}- 派发可能被外呼策略拦截 →
409,error以「当前外呼策略禁止派发」开头。 - 锁竞争 →
409「任务正在调度中」;Redis 锁不可用 →503。 - 无可派发联系人 / 无可用槽位 / 呼叫间隔未到时不落批次,
batchId返回null,requested/submitted/failed均为 0。
POST /api/campaigns/retry 请求体(RetryContactsInput):
jsonc
{ "campaignId": 1, "status": "failed" } // 可选 "maxAttempts": 2(当前不影响重置行为)把匹配联系人改回 pending。status 可取 failed/submitted/completed/paused。 按联系人 ID 定向重置请用 POST /api/contacts/retry(请求体 { "campaignId": 1, "contactIds": [1, 2] })。
小批量同步文件导入
文件解析和号码校验先完成,再进入事务锁定 campaign 并按 1,000 行分批写入。除 running 外的任务状态均可导入;运行中返回 409,且不写入联系人。数据库异常会整体回滚。
| 方法 | 路径 | 权限 | 说明 |
|---|---|---|---|
| POST | /api/imports/upload | import:write | 上传最大 5 MiB、最多 1,000 行的 CSV/TSV 并同步返回结果(FormData 字段 campaignId 指定任务) |
空文件、表头或列映射错误、未闭合 CSV 引号、超过 1,000 行返回 400;超过 5 MiB 返回 413。所有拒绝都发生在联系人事务写入前。
外呼策略 policies
全局/任务级禁呼、时段、号码规则、频控、重试与并发限制;执行语义更严格优先(全局与任务级都必须通过)。
| 方法 | 路径 | 权限 | 说明 |
|---|---|---|---|
| POST | /api/policies/list | 会话 | 查询策略列表(JSON 请求体,支持 campaignId、scope、type、q 与分页) |
| POST | /api/policies/create | policy:write | 新建外呼策略 |
| POST | /api/policies/update | policy:write | 更新外呼策略(请求体含 policyId) |
| POST | /api/policies/delete | policy:write | 删除外呼策略(请求体 { "ids": [1] }) |
| POST | /api/policies/evaluate | 会话 | 外呼策略试算 |
POST /api/policies/create 请求体(CreateCalloutPolicyInput):
jsonc
{
"name": "工作时段",
"scope": "global", // global | campaign(campaign 时需 campaignId)
"type": "daily_window",
"action": "allow_window", // block | allow_window | limit
"priority": 10,
"config": { "start": "09:00", "end": "20:00" },
"isEnabled": 1
}策略类型 type 与 config:
| type | config 示例 | 作用 |
|---|---|---|
blocked_dates | { "dates": ["2026-05-01"] } | 命中日期阻断 |
holiday_blocklist | { "dates":["2026-02-17"], "recurringDates":["01-01"], "workdayOverrides":["2026-02-14"], "region":"CN" } | 节假日阻断 + 补班放行 |
daily_window | { "start":"09:00", "end":"20:00" } | 仅允许每日时段 |
weekly_window | { "days":[1,2,3,4,5], "start":"09:00", "end":"18:00" } | 仅允许星期时段 |
phone_blacklist | { "numbers":["138...","139..."] } | 按号码阻断 |
phone_prefix_blocklist | { "prefixes":["170","171"] } | 按前缀阻断 |
contact_frequency_limit | { "days":7, "maxAttempts":2 } | N 天最大触达次数 |
rate_limit | { "callIntervalMs":2000 } | 收紧呼叫间隔 |
retry_rule | { "maxRetries":1, "retryIntervalSeconds":1800 } | 收紧重试 |
concurrency_limit | { "maxConcurrency":3 } | 收紧并发 |
POST /api/policies/evaluate 试算(PolicyEvaluationRequest → PolicyEvaluationResult):
jsonc
// 请求
{ "campaignId": 1, "phoneNumber": "13800138000", "at": "2026-06-15T03:00:00.000Z" }
// 响应 data
{ "allowed": false, "reason": "命中策略:工作时段(当前不在允许时段)",
"matchedPolicies": [ /* CalloutPolicyRow */ ],
"effectiveLimits": { "maxConcurrency": 3, "maxRetries": 1, "retryIntervalSeconds": 1800, "callIntervalMs": 2000 } }DNC 免打扰名单
派发前独立于策略表进行阻断;业务回写退订/拒呼时自动沉淀。
| 方法 | 路径 | 权限 | 说明 |
|---|---|---|---|
| POST | /api/dnc/list | 会话 | 分页查询(请求体 { "page", "pageSize", "q" }) |
| POST | /api/dnc/create | compliance:write | 新增免打扰号码 |
| POST | /api/dnc/update | compliance:update | 按 ID 编辑未删除的免打扰号码 |
| POST | /api/dnc/batch-import | compliance:write | 批量导入免打扰号码 |
| POST | /api/dnc/delete | compliance:write | 删除免打扰号码(请求体 { "ids": [1] }) |
POST /api/dnc/create:{ "phoneNumber": "138...", "reason": "客户要求", "expiresAt": null }(expiresAt 为空=永久)。 已有同号码记录仍按新增接口的覆盖逻辑更新,可恢复已删除记录。
POST /api/dnc/update 请求示例:
json
{
"dncId": 1,
"phoneNumber": "13800000000",
"source": "manual",
"reason": null,
"expiresAt": "2026-12-31T15:59:59.000Z"
}dncId为正整数;phoneNumber、source必填,去除首尾空白后不能为空,分别最多 32、64 个字符。reason最多 255 个字符,传null清空;expiresAt传null取消过期时间(永久有效)。两字段未传时保留原值。- 页面按浏览器本地时区显示与编辑时间,保存时转换为带时区的 ISO 时间;未修改过期时间时保留原始时间戳。已过期记录可通过修改过期时间续期。
- 成功响应为统一信封中的
{ "dnc": { /* 更新后的免打扰记录,含 isExpired */ } }(HTTP 200)。创建人和创建时间保持不变,更新修改人、修改时间,并在同一事务中记录修改前后审计快照。 - 记录不存在或已删除返回 404,不恢复删除记录;号码与其他未删除记录重复返回 409(含数据库唯一约束并发冲突);非法参数返回 400,未登录返回 401,缺少编辑权限返回 403。
权限按钮在 src/db/seed.ts 的「合规与数据」菜单中声明,由现有种子同步机制写入;无需修改数据库结构。
结果回写(内部)
POST /api/call-results
内部接口(token 鉴权,非会话)。由 callflow-esl 业务在通话结束时回写。 完整字段、定位回退顺序、时长推导、录音地址归一化、transcript 落库语义以及级联副作用(DNC/情绪分析/状态联动/重试与任务闭环)见 result-schemas.md;业务侧怎么组装见 outbound-business-example.md。最小示例:
http
POST /api/call-results
Content-Type: application/json
X-Callout-Internal-Token: <server.serviceApi.authToken>
{ "requestId": "<callout_request_id>", "callStatus": "completed",
"payload": { "result": { "recognizedText": "客户有意向" } } }实时盯屏与人工介入 live-calls
坐席「一人盯多路、有意向即插入」的实时监控链路。数据源是 callflow-esl 的 kb-audio-chat-business 在通话过程中逐轮上报的进度事件写入 Redis TTL 共享快照, 并通过 Pub/Sub 向每个实例的本机 SSE 客户端广播。Redis 是必选依赖,服务启动时连接失败会 直接退出,不提供内存降级。快照不写业务库(挂机后的整段回写 /api/call-results 仍是权威数据)。链路与配置 详见 transfer-to-human.md §盯屏介入 与业务文档 callflow-esl/docs/existing-businesses.md。
| 方法 | 路径 | 权限 | 说明 |
|---|---|---|---|
| POST | /api/call-progress | 内部 token | callflow-esl 逐轮上报通话进度(started/segment/intervention/ended),更新共享快照并广播 |
| POST | /api/live-calls/list | agent:operate | 当前用户实际获配及介入前保留只读通话的快照与全局容量摘要(请求体 { "campaignId"?: 1 }) |
| POST | /api/live-calls/detail | agent:operate | 获取单路实时通话快照详情(请求体 { "callKey": "..." }) |
| GET | /api/live-calls/events?campaignId | agent:operate | 多路复用 SSE:推送实际获配及保留只读通话的字幕/关键词/介入/归属变化事件 |
| POST | /api/live-monitor/heartbeat | agent:operate | 登记并续租盯屏 Presence,按坐席实时状态计算接管容量并触发重分配 |
| POST | /api/live-monitor/presence-leave | agent:operate | 主动离开盯屏并立即释放分配;异常断线由 TTL 回收 |
| POST | /api/live-calls/intervene | agent:operate | 坐席认领人工介入(请求体 { "callKey": "..." }),信号转发给 callflow-esl(原子抢占,重复返回 409) |
| POST | /api/live-calls/intervention/cancel | agent:operate | 仅介入发起人取消仍为 requested 的请求(请求体 { "callKey": "..." });已消费进入转接返回 409 |
| POST | /api/live-calls/release | agent:operate | 当前监控责任人主动让出(请求体 { "callKey": "..." });本次 Presence 生命周期内不再分回本人 |
| POST | /api/live-calls/hangup | campaign:dispatch | 主管/管理员以 NORMAL_CLEARING 结束整通客户业务(请求体 { "callKey": "..." }),写审计与运行事件 |
| POST | /api/live-monitor/barge | agent:operate | 主管强插指定实时通话(请求体 { "callKey", "supervisorExtension", "mode": "barge" }) |
| POST | /api/live-monitor/whisper | agent:operate | 主管耳语指导指定实时通话(请求体 { "callKey", "supervisorExtension", "mode": "whisper" }) |
| POST | /api/live-monitor/intercept | agent:operate | 主管拦截接管指定实时通话(请求体 { "callKey", "supervisorExtension", "mode": "intercept" }) |
callKey形如attempt:<attemptId>(或request:<requestId>)。/api/live-monitor/barge、whisper、intercept请求体统一为{ "callKey", "supervisorExtension", "mode" }(callId可替代callKey);supervisorExtension与mode为预留参数,三个路由当前都执行同一介入认领逻辑(等同/api/live-calls/intervene)。segment事件的speaker取值:bot(AI 播报)/customer(客户 ASR)/agent(人工介入后坐席的会议转写)/supervisor(主管发言);介入成功后坐席与客户的发言仍持续上报。- 意向关键词在 campaign 的
businessConfig.intentKeywords中配置;收到客户 ASR 片段命中 任一关键词时,SSE 推intent-alert事件,盯屏页高亮 + 提示音。 - 每路通话只分配一个主监控人;分配按
businessConfig.monitoringPriority、意向命中、等待时间、 坐席 cell 与页面任务过滤执行。同一登录用户的多个标签页合计仍受服务端容量限制。 - 介入期间只保留介入通话的实际责任,其余原获配通话以
monitorViewMode=retained继续只读展示;monitorAssignment可为 null(等待接管)或指向另一坐席。介入结束后保留视图自动清除。 strict任务会把活跃盯屏容量加入任务/线路/cell 的派发上限计算;无人时联系人保持待呼。- 介入生效有节点边界延迟(业务播完当前录音/收音后才转接),典型 3~10 秒。
- Redis 已启用但不可用时,快照、SSE 建连和介入返回
503,不会退化为各实例分裂的本地状态。
坐席与软电话 agents
| 方法 | 路径 | 权限 | 说明 |
|---|---|---|---|
| GET | /api/agents/list | 会话 | 坐席 + 坐席组列表(亦支持 POST /api/agents/list 与 GET|POST /api/agent-groups/list) |
| POST | /api/agents/detail | 会话 | 获取单个坐席详情(请求体 { "agentId": 1 }) |
| GET | /api/agents/extensions | agent:write | FreeSWITCH 可用分机(含 freeswitchReachable) |
| POST | /api/agents/extensions | agent:write | 同 GET(GET/POST 双注册) |
| GET | /api/agents/phone-statuses | agent:operate | 全部坐席的非敏感注册摘要:agentId / phoneExtension / registrationStatus;四态为 registered / unregistered / unconfigured / unknown |
| POST | /api/agents/phone-statuses | agent:operate | 同 GET(GET/POST 双注册) |
| POST | /api/agent-groups/create | agent:write | 创建坐席组 |
| POST | /api/agent-groups/update | agent:write | 更新坐席组(请求体含 id 或 groupId) |
| POST | /api/agent-groups/delete | agent:write | 删除坐席组(请求体 { "ids": [1] }) |
| POST | /api/agents/create | agent:write | 创建坐席 |
| POST | /api/agents/update | agent:write | 更新坐席(请求体含 id 或 agentId) |
| POST | /api/agents/delete | agent:write | 删除坐席(请求体 { "ids": [1] },忙碌中不可删) |
| GET | /api/agents/me/status | agent:operate | 当前用户绑定且启用的坐席状态及电话注册态摘要;未绑定时 agent: null |
| POST | /api/agents/me/status | agent:operate | 切换本人绑定坐席状态(请求体 { "status": "idle" }),仅接受 idle / offline;手动示闲需电话 registered(可用),忙碌期间禁止手动切换 |
| POST | /api/agents/status | agent:operate | 切换指定坐席状态(请求体 { "agentId": 1, "status": "idle" });仅接受 idle / offline,旧 online 兼容映射为 idle;普通坐席仅限本人,主管/管理员可修改全部,但均不能切换忙碌坐席 |
| POST | /api/agents/callbacks | agent:operate | 人工回拨(请求体含 agentId):originate 坐席分机后 bridge 客户 |
| POST | /api/agents/manual-calls | agent:operate | 人工呼出任意客户号码(请求体含 agentId) |
| POST | /api/agents/transfers | agent:operate | 工作台对客户号码发起坐席↔客户桥接呼叫(请求体含 agentId,≠AI会议转人工,见 transfer-to-human.md §7) |
| POST | /api/softphone-calls/list | agent:operate | 软电话呼出/回拨/转人工记录分页列表(请求体含 page、pageSize、agentId、campaignId) |
手动提交 busy 返回 400 参数错误,忙碌由系统控制。两个状态变更接口在坐席 busy 时均返回 409,提示“呼叫占用中,结束后自动示闲”;示闲还需电话注册态为 registered,未注册、未配置或不可判定时返回 409,非占用时允许离线。手动更新通过数据库条件排除 busy,与路由预占竞争时若条件不再满足,返回 409,不覆盖系统忙碌状态。状态成功变更后向所有在线客户端广播 agent_status,本人、主管操作以及系统预占和释放均通过该事件同步工作台与顶部菜单。
坐席公开结构只包含唯一终端标识 phoneExtension,不再返回 sipUsername。升级兼容期内, 创建/更新接口仍会把旧请求中单独出现的 sipUsername 映射到 phoneExtension;两个非空值 不一致时返回 400,禁止静默选择。
POST /api/agents/manual-calls 请求体:
jsonc
{
"agentId": 1,
"phoneNumber": "13800138000",
"campaignId": 1,
"contactId": 100,
"note": "..."
}
// 也可只传 agentId + contactId,号码从联系人取回拨/呼出会经 submitAgentBridgeCall 先 originate 坐席分机、接通后 &bridge 客户号码, 并落 agent_call_records 记录(direction=callback/manual_call)。
浏览器软电话会话 softphone-sessions
坐席浏览器 SIP 软电话链路:整体流程、坐席 SIP 字段、配置与 FreeSWITCH 侧要求见 softphone-sip.md。callout-server 在这组接口上做权限校验与 agent_call_records 落库,实时会话本体代理到 callflow-esl 的 browser-softphone-session-hub。
| 方法 | 路径 | 权限 | 说明 |
|---|---|---|---|
| GET | /api/agents/me/sip-config | agent:operate | 当前登录坐席的浏览器 SIP 注册配置(坐席与用户按 agents.userId 一对一绑定) |
| POST | /api/agents/me/sip-config | agent:operate | 同 GET(GET/POST 双注册) |
| GET | /api/softphone/ice-config | 会话 | 浏览器 SIP 注册前取 ICE / TURN 短期凭据;turn.enabled=false 时 iceServers 为空数组 |
| POST | /api/softphone/ice-config | 会话 | 同 GET(兼容 POST 调用) |
| POST | /api/softphone/ice-servers | 会话 | 同 GET /api/softphone/ice-config(别名) |
| GET | /api/agents/sip-configs | agent:operate | 当前用户可见的已启用 SIP 配置列表(admin / supervisor 可见全部) |
| POST | /api/softphone-sessions/create | agent:operate | 创建会话:先落 agent_call_records,再请求 callflow-esl 创建实时会话;返回 sessionId、serviceNumber 与浏览器拨号必须原样携带的 sipExtraHeaders |
| GET | /api/softphone-sessions/events?sessionId=... | agent:operate | SSE 实时事件流(session / message / tts / error);EventSource 场景支持 ?access_token= 查询参数鉴权 |
| POST | /api/softphone-sessions/messages | agent:operate | 坐席打字文本(请求体含 sessionId),经 TTS 注入客户腿播放 |
| POST | /api/softphone-sessions/controls | agent:operate | 控制命令(请求体含 sessionId):{"type":"hold"} / resume / {"type":"dtmf","digits":"..."} / {"type":"transfer","targetNumber":"..."} |
| POST | /api/softphone-sessions/notes | agent:operate | 更新会话备注(请求体含 sessionId、notes,另有可选 intent) |
| POST | /api/softphone-sessions/end | agent:operate | 结束会话(请求体含 sessionId,同时结束会议与录音收尾) |
| POST | /api/softphone-calls/detail | agent:operate | 坐席通话记录详情(请求体 { "id": 1 } 或 { "softphoneCallId": 1 }):消息时间线、TTS 事件、录音信息与分角色转写 |
| POST | /api/softphone-calls/delete | compliance:write | 软删除坐席通话记录(请求体:{"ids":[1,2]},响应:{"ids":[1,2],"deleted":2}) |
转人工会议 conferences
| 方法 | 路径 | 权限 | 说明 |
|---|---|---|---|
| POST | /api/transfers/route | 内部 token | AI 业务转人工路由:预占空闲坐席 + originate 入会(callflow-esl 调用) |
| POST | /api/transfers/end | 内部 token | 客户挂机幂等收尾:校验客户 UUID,结束会议全部坐席/主管腿并释放坐席 |
| GET | /api/conferences/list | campaign:dispatch | 进行中会话列表(附 FS 实时成员数) |
| POST | /api/conferences/detail | campaign:dispatch | 会话详情 + 成员腿(请求体 { "conferenceId": "..." }) |
| POST | /api/conferences/participants/add | agent: agent:operate / supervisor: campaign:dispatch | 加成员/就地切换模式(请求体含 conferenceId,盲转/协商转/主管监听/强插/耳语指导) |
| POST | /api/conferences/participants/remove | agent:operate | 踢出一名成员腿(请求体 { "conferenceId": "...", "softphoneCallId": 12 }) |
| POST | /api/conferences/supervisor-mode | agent:operate | 主管在会模式切换(monitor / whisper / barge) |
| POST | /api/conferences/mute | agent:operate | 静音/取消静音会议成员(请求体 { "conferenceId", "memberId", "mute" }) |
| POST | /api/conferences/kick | agent:operate | 按会议成员 ID 踢出成员(请求体 { "conferenceId", "memberId" }) |
| POST | /api/conferences/lock | agent:operate | 锁定/解锁会议室(请求体 { "conferenceId", "lock" }) |
录音与转写
| 方法 | 路径 | 权限 | 说明 |
|---|---|---|---|
| GET | /api/media/config | 会话 | 返回兼容默认值 freeswitchRecordingBaseUrl、按 cell 映射 freeswitchRecordingBaseUrlsByCell 与 emotionAudioBaseUrl |
| POST | /api/attempts/detail | 会话 | 返回单条完整呼叫明细(请求体 { "attemptId": 555 });记录不存在或已软删除时返回 404 |
| POST | /api/attempts/transcript-segments | 会话 | 按外呼尝试查询分角色转写片段(请求体 { "attemptId": 555 }) |
| POST | /api/attempts/delete | compliance:write | 软删通话记录(请求体:{"ids":[1,2]},响应:{"ids":[1,2],"deleted":2}),并清除 contacts.lastAttemptId |
外呼录音由业务回写到
call_attempts.recordingUrl,数据库和呼叫尝试 API 响应都只保留文件名短值;前端按attempt.cellId从freeswitchRecordingBaseUrlsByCell选择前缀,历史空 cell 使用兼容默认值,非空失效 cell 不生成错误 URL。情绪分析 worker 使用相同规则。情绪分析音频仍用emotionAudioBaseUrl和短值拼接。
用户管理与系统管理 RBAC(/api/routes/*、/api/users/*、/api/roles/*、/api/menus/*)
callout-webpage 系统管理页与动态菜单使用的一组接口,落库在 callout schema 的 users / roles / menus / user_roles / role_menus / role_buttons 上:
- 分页参数统一为
page(默认 1)/pageSize(默认 20)。 - 写操作全部走
POST + JSON扁平静态路径,删除统一走POST /api/<resource>/delete,请求体接收{ ids: T[] }(单删传单元素数组[id]),响应统一返回{ ids: T[], deleted: number }。 - 权限是
system:*按钮权限,不在角色内置权限表里,判定规则见 角色与权限矩阵。 - 用户与角色的写操作都会记一条审计日志(
POST /api/audit/list可查)。
| 方法 | 路径 | 权限 | 说明 |
|---|---|---|---|
| GET | /api/routes/user-routes | 会话 | 当前用户可见的动态路由树(按角色菜单授权生成) |
| GET | /api/routes/constant-routes | 会话 | 常量路由,当前固定返回空数组 |
| POST | /api/users/list | system:user:view | 用户分页列表(username / displayName / nickName / userPhone / userEmail / userGender / status 过滤) |
| POST | /api/users/detail | system:user:view | 用户详情(请求体 { id }) |
| POST | /api/users/create | system:user:create | 新建用户(含 userRoles[]、可绑定 agentId) |
| POST | /api/users/update | system:user:update | 按 body id 更新用户 |
| POST | /api/users/delete | system:user:delete | 删除用户(请求体 { ids: number[] },返回 { ids, deleted }) |
| POST | /api/users/password-reset | system:user:update | 重置指定用户密码(请求体 { id, password },返回 { id }) |
| POST | /api/roles/list | system:role:view | 角色分页列表(roleName / roleCode / status 过滤) |
| GET | /api/roles/all | system:role:view | 全部角色(下拉用) |
| GET | /api/roles/permissions | 会话 | 系统内置全量权限码清单(供权限矩阵配置) |
| POST | /api/roles/create | system:role:create | 新建角色 |
| POST | /api/roles/update | system:role:update | 更新角色 |
| POST | /api/roles/delete | system:role:delete | 删除角色(请求体 { ids: number[] },返回 { ids, deleted }) |
| POST | /api/roles/menu-auth | system:role:auth | 查询角色已授权菜单 ID |
| POST | /api/roles/menu-auth-update | system:role:auth | 覆盖角色菜单授权 |
| POST | /api/roles/button-auth | system:role:auth | 查询角色已授权按钮码 |
| POST | /api/roles/button-auth-update | system:role:auth | 覆盖角色按钮授权 |
| GET | /api/menus/list | system:menu:view | 菜单树 |
| GET | /api/menus/buttons | system:menu:view | 全部可授权按钮码 |
| POST | /api/menus/create | system:menu:create | 新建菜单 |
| POST | /api/menus/update | system:menu:update | 更新菜单 |
| POST | /api/menus/status-update | system:menu:update | 启停菜单 |
| POST | /api/menus/delete | system:menu:delete | 删除菜单(请求体 { ids: number[] },返回 { ids, deleted }) |
首次启动会播种超级角色 R_SUPER 与全部 system:* 按钮码 (db/seed.ts);持有 R_SUPER、role=admin 或 admin 按钮码的账号 无需逐个勾选即可访问本组接口。