GoChat 商务通 Connector
该服务把多个商务通客服账号接入 GoChat 的 Channel::Shangwutong 收件箱。一个 Connector 进程统一管理全部账号,每个 Inbox 对应一个独立 supervisor;账号、session、cursor、入站队列和出站队列持久化在同一个 SQLite 文件中。
GoChat 商务通 Inbox 是唯一配置入口。Connector 不提供账号 CRUD 后台,也不接受启动参数中的账号密码。Inbox 创建或更新后,GoChat 发送签名 lifecycle webhook,Connector 再使用 account-scoped service token 拉取收件箱级配置。
完整协议、Schema、消息映射和验收边界见 开发计划,生产操作见 运行手册。
本地构建与测试
需要 Go 1.26.x。所有命令从本目录执行:
go tool sqlc generate
go test ./...
go vet ./...
go build -o /tmp/shangwutong-build-check ./cmd/shangwutong
# 500 账号等价协议长时间验证(非常规 CI)
SWT_SOAK_DURATION=1h go test ./internal/account \
-run TestManagerMaintainsOneSupervisorPer500Accounts -count=1 -timeout 70m
sqlc 生成代码提交在 db/generated/。修改 db/queries/*.sql 或 migration 后必须重新生成并确认工作区没有生成漂移。长时间测试使用 fake SWT 协议客户端验证 supervisor、心跳调度、SQLite 和退出清理;不代替真实商务通的单 IP/域名限流证据。
启动配置
SWT_CONNECTOR_LISTEN=:9100
SWT_CONNECTOR_DB_PATH=/data/connector.db
GOCHAT_BASE_URL=http://gochat:3000
GOCHAT_CONNECTOR_SERVICE_TOKEN=<account-scoped service token>
SWT_MAX_INFLIGHT_HEARTBEATS=64
SWT_INBOUND_WORKERS=8
SWT_OUTBOUND_WORKERS=8
SWT_SHUTDOWN_TIMEOUT=30s
只有前四项中的数据库路径、GoChat 地址和 service token 必填,监听地址有默认值。GOCHAT_CONNECTOR_SERVICE_TOKEN 只授予要接入的 GoChat Account,并仅允许 Connector 路由。不要增加 GOCHAT_SWT_* 全局配置;账号登录字段、presence 和 webhook URL 都属于 Inbox 配置。
SQLite 父目录必须已存在并且对运行用户可写。Connector 会把数据库、WAL/SHM 和在线备份权限收紧为 0600,镜像内 /data 和 /backup 为 0700;宿主机挂载目录仍需由部署层限制为 Connector 运行用户可访问。密码按内部系统约定明文保存在 GoChat 专用配置表和 Connector SQLite 中。
export SWT_CONNECTOR_DB_PATH="$PWD/tmp/connector.db"
export GOCHAT_BASE_URL="http://127.0.0.1:3000"
export GOCHAT_CONNECTOR_SERVICE_TOKEN="..."
mkdir -p "$(dirname "$SWT_CONNECTOR_DB_PATH")"
go run ./cmd/shangwutong serve
容器
镜像构建上下文必须是仓库根目录:
docker build -f channels/shangwutong/Dockerfile -t gochat/shangwutong:dev .
生产 Compose 已包含 shangwutong 服务、SQLite 数据卷和独立 /backup 备份卷。Quickstart 中该服务位于 shangwutong profile,避免未配置 service token 时影响普通 GoChat 启动:
cd deploy/quickstart
GOCHAT_CONNECTOR_SERVICE_TOKEN='...' docker compose --profile shangwutong up -d --build
在 GoChat 创建 Inbox 时 webhook URL 填写容器网络地址:
http://shangwutong:9100/webhooks/gochat/v1
运维端点与命令
GET /healthz
GET /readyz
GET /metrics
POST /internal/reconcile # 仅 loopback
shangwutong migrate up
shangwutong migrate status
shangwutong reconcile
shangwutong backup --output /backup/connector-$(date +%F).db
shangwutong doctor
healthz 只表示进程存活;readyz 使用轻量 SQLite quick check 并检查写锁。doctor 检查配置、migration、完整的 SQLite integrity check 和 GoChat 配置 API,但不会登录商务通。backup 使用 SQLite VACUUM INTO,不要直接复制活动中的 DB/WAL 文件。
可靠性边界
- GoChat webhook 的 2xx 只表示 Connector 已持久化,不表示商务通已发送。
- 商务通明确成功后回写
sent;永久失败回写failed;请求已经写出但结果未知时回写uncertain。 - uncertain 默认观察 5 分钟。同账号后续发送在此期间被顺序屏障阻塞;超时转 failed 后释放。晚到且唯一匹配的 kind=3 回显仍可纠正为 sent。
oc/send.aspx不返回消息 ID。kind=2/3 心跳中的原始seq_id才是商务通消息 ID;组合 source ID 只用于幂等,不能冒充外部消息 ID。- kind=52 没有真实版本 fixture 前保留 raw-only,不伪造 child 消息 ID。
- 启动时先从 SQLite 恢复 supervisor,再异步拉取 GoChat 全量配置;首次完整快照失败会从 1 秒指数退避到 5 分钟持续重试,不依赖 GoChat health 才启动进程。
- SIGTERM 会先停止 readiness 和 HTTP 接收,再取消 worker、等待 supervisor、checkpoint WAL;升级不会主动批量 logout。
自动化测试通过不等于真实商务通生产可用。本地 500 账号一小时 fake protocol soak 只证明调度、SQLite 和事件持久化;登录、收发、presence、密码更新、负 kind cursor、kind=52、真实媒体 URL、验证码流程和真实商务通限流必须按运行手册留存证据。