Files
go-sip/cmd/saas-server/README.md
T

6.6 KiB
Raw Blame History

SaaS Server:最小外呼测试工具

使用 Fiber/v3,代替浏览器操作:提供 Dispatcher 所需配置,手动向测试队列发送一条外呼,查看回执和最终结果。

不修改 Dispatcher/Agent,不绕过它们的业务检查。启动不自动发消息。仅支持专用 saas-mock-* vhost,不能和真实 SaaS 共用反馈队列。 本工具不授权真实拨号或真实 AI/OSS 调用。

启动

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/saas-mock-example",
  "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),不能与输入配置目录交叠。
  • MQ 用户必须能够在这个专用 vhost 声明、绑定和使用现行拓扑。已有拓扑不匹配或已有独占反馈消费者时明确失败;不会清队列或自动恢复连接。

快照目录:

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":"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/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":"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}'

任务/额度按现行 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。通过不代表真实服务或通话验收。