SaaS Server:最小外呼测试工具
使用 Fiber/v3,代替浏览器操作:提供 Dispatcher 所需配置,手动向测试队列发送一条外呼,查看回执和最终结果。
不修改 Dispatcher/Agent,不绕过它们的业务检查。启动不自动发消息。共用现有 vhost,仅在内存中将租户 ID 映射为 90000 + 原ID、任务 ID 映射为 test-原ID,Dispatcher 编号不变。消费独立测试反馈队列,不抢正式 SaaS 的反馈。 本工具不授权真实拨号或真实 AI/OSS 调用。
server.sip 已安装实例
- 二进制:
~/.local/bin/saas-server;启动配置:~/.saas/server.json。 - 用户服务:
go-sip-saas-server.service,监听端口8089,共用现行/waihuvhost,测试任务如test-1。 - 服务日志:
journalctl --user -u go-sip-saas-server.service。 - 现有 Dispatcher/Agent 未改动,没有 Dispatcher 消费这个测试 vhost,推送仅入队,不会自动执行或返回外呼结果;接入测试链路需要另外安排。
- 启动时源配置只读;例如原租户
1001映射为91001,任务1映射为test-1,原文件不回写。内存修改提交的完整资源须携带这些测试身份。 - 测试反馈队列收到的非测试租户副本仅在该私有队列中确认,不保存为测试结果;正式 SaaS 原队列不被本服务消费。
- 实际安装及只读验证见
../../docs/evidence/saas-server-server-sip-deployment.md。
启动
go build -o /tmp/saas-server ./cmd/saas-server
/tmp/saas-server --config /absolute/path/server.json
server.json 示例(全部为占位,须换成已批准的测试配置):
{
"listen": "0.0.0.0:8089",
"dispatcher_id": "c046b893-8628-4589-ae50-619d049248a6",
"dispatcher_secret": "TEST_DISPATCHER_SECRET",
"broker_url": "amqp://TEST_USER:TEST_PASSWORD@127.0.0.1:5672/%2Fwaihu",
"snapshots": "snapshots",
"feedback_dir": "feedback",
"tls_cert": "dispatcher-test.crt",
"tls_key": "dispatcher-test.key"
}
- 相对路径以
server.json所在目录为基准。 listen省略时为0.0.0.0:8089。- TLS 两项可同时省略,服务改为 HTTP;需要遵循现有 Dispatcher 客户端的连接要求,不能为测试放松客户端校验。
- 启动文件和各快照必须是私有普通文件(如
0600)。反馈目录独立且私有(如0700),不能与输入配置目录交叠。 broker_url的 vhost 与现有 Dispatcher 一致,连接凭据使用测试专用账号。只创建agent-call.d.<D>.task.test-<ID>.v1和agent-call.saas.test.d.<D>.v1;原控制队列不创建、绑定或消费。MQ 用户须能声明/绑定这些测试队列及使用既有交换机。已有拓扑不匹配或已有独占反馈消费者时明确失败;不会清队列或自动恢复连接。
快照目录:
snapshots/
sip.json
providers.json
quota.json
tasks/
<task_id>.json
JSON 内容直接采用 ../../contracts/schema/ 的现行配置响应格式,不使用旧 ENV 替代。所有资源必须属于启动文件中的 Dispatcher;首期仅一个租户,任务文件名与 task_id 一致。任务列表由详情生成,不另建列表文件。
AI 连接、模型、音色、参数、SIP 线路、额度和时段来自这些文件;合同示例只是结构参考,不保证线路具备执行参数,也不是拨号授权。
另行启动使用这套测试配置及专用 MQ 的 Dispatcher/Agent;本工具不启动、停止、重启或修改现有服务。五类正式配置接口校验现有 X-DISPATCHER-id 和 X-DISPATCHER-SECRET-KEY 请求头。
手动发送一条外呼
以下命令以本地 HTTP 为例;启用 TLS 时改用 HTTPS 并使用测试 CA 的 --cacert,不要关闭证书校验。
BASE=http://127.0.0.1:8089
# 任务、号码必须是本次获准的测试数据;123456 只是占位。
curl --fail-with-body "$BASE/test/calls" \
-H 'Content-Type: application/json' \
-d '{"event_id":"test-numberID-001","task_id":"test-1","callee":"123456"}'
event_id 由调用者明确指定,原样保留。相同身份、相同请求在本次进程中不再发送;同身份不同内容报错。重启会清除发送记录,不要通过重启、换 ID 或重复调用补拨;Dispatcher 的既有重复消息规则不变。
响应中的 status:
| 值 | 含义 |
|---|---|
confirmed |
队列已确认,不表示已拨号或已接通 |
unroutable |
消息被队列退回,没有对应路由 |
rejected |
队列明确拒绝 |
not_sent |
发送前失败 |
unknown |
是否发送成功不确定;禁止自动重发 |
非 confirmed 返回 HTTP 503,并保留本次状态。输入错误返回 HTTP 400。工具没有自动重试、批量、定时或换线功能。
查询和校验反馈
curl --fail-with-body --get "$BASE/test/events" \
--data-urlencode 'event_id=test-numberID-001'
返回本次进程内的 requests 与 feedback。派发回执和最终结果分别记录;validation 为 valid 或 invalid,错误原因会明确显示。等待不到反馈不等于通话失败,只有拒绝的未发起请求不一定有通话最终结果。
查询只返回身份、状态、结果及校验摘要,不显示完整对话/音频等正文。收到的原消息保存在独立反馈目录的随机 feedback-*.json 文件中,文件保存并同步成功后才 ACK。保存失败停止消费并退出,不丢弃消息。重复或错误消息也保留证据,冲突不覆盖原记录。
重启后不恢复历史查询;旧反馈文件仍保留,供人工检查。本工具不会回写配置,也不会保存内存修改。
内存修改配置
使用完整配置响应,不使用 PATCH 或字段合并:
curl --fail-with-body -X PUT "$BASE/test/config/sip" \
-H 'Content-Type: application/json' --data-binary @/path/candidate-sip.json
curl --fail-with-body -X PUT "$BASE/test/config/providers" \
-H 'Content-Type: application/json' --data-binary @/path/candidate-providers.json
curl --fail-with-body -X PUT "$BASE/test/config/quota" \
-H 'Content-Type: application/json' --data-binary @/path/candidate-quota.json
curl --fail-with-body -X PUT "$BASE/test/config/tasks/test-1" \
-H 'Content-Type: application/json' --data-binary @/path/candidate-task.json
校验成功后整体替换内存,失败不改变当前值。只能修改已加载资源,不能增删任务或更换租户/Dispatcher 身份。重启重新加载原文件。
修改配置不会自动通知 Dispatcher,需要显式操作:
# action 只支持 start、resume、edit;每条指令使用明确的源 event_id。
curl --fail-with-body "$BASE/test/task-controls" \
-H 'Content-Type: application/json' \
-d '{"event_id":"test-edit-001","task_id":"test-1","action":"edit","reason":"test configuration change"}'
# change_type 按现行合同:enabled、disabled、removed、created;revision 为正整数。
curl --fail-with-body "$BASE/test/sip-refresh" \
-H 'Content-Type: application/json' \
-d '{"event_id":"test-sip-001","trunk_id":"trunk-mock","change_type":"enabled","revision":8}'
这些接口只发送现行指令,不会自动切换 Dispatcher 的配置来源;当前 Dispatcher 仍读取正式 SaaS,测试任务不会自动被消费。未来明确接入后,任务/额度按现行 start/resume/edit 流程读取,edit 不解除暂停、不恢复已停止任务。SIP 通知触发现行加载流程;已经发出的通话不改变快照。AI providers 只在 Dispatcher 启动读取,修改后须另行安排重启;本工具不重启服务。
本地验证
go test -race ./cmd/saas-server
# 需 Docker 和已存在的 rabbitmq:4.1-management-alpine 镜像,不自动拉取。
bash cmd/saas-server/check-integration.sh
集成脚本只创建并清理自己的临时 RabbitMQ 容器,运行 Fiber HTTPS → RabbitMQ → 未修改的 Dispatcher → Mock Agent mTLS → 回执/最终结果测试。使用临时配置、数据库和合成音频,不拨号,不访问真实 AI/OSS。通过不代表真实服务或通话验收。