179 lines
14 KiB
Markdown
179 lines
14 KiB
Markdown
# Banban 与 4G 小程序流量关联、联合解绑联调说明
|
||
|
||
当前契约以 [4G 后端接口文档 (2)](./设备直接绑定接口联调文档(2).md) 为准。本文描述 Banban 后端、Banban 小程序及 `/Users/stu2not/4g-miniprogram` 的实现;第 10 节记录服务器部署版本及验证范围,小程序发布状态需单独确认。
|
||
|
||
## 1. 业务规则
|
||
|
||
- Banban NFC 绑定成功即可正常使用设备;4G 关联允许失败、取消或稍后处理。
|
||
- 管理页查询原 4G 关系,只有后端确认关联有效才显示查询、充值入口。
|
||
- 用户确认联合解绑后,持久化解绑操作,阻止此绑定实例继续申请 handoff。
|
||
- Banban 必须调用 4G 关系关闭接口,并取得 `canUnbind=true` 才能解除本地设备绑定。
|
||
- 4G 已在其他入口解绑时,可直接确认后完成 Banban 解绑,不必再次跳转小程序。
|
||
- `unknown`、403、404、网络失败和不存在记录均不能作为已解绑依据。
|
||
- 不把客户端回报或当前用户设备列表中存在同一 ICCID 当作原关系有效的证明。
|
||
|
||
## 2. 身份与绑定实例
|
||
|
||
| 小程序 | AppID |
|
||
| --- | --- |
|
||
| Banban | `wx29ce99ce96b56cd9` |
|
||
| 4G 设备管理 | `wx56e1cfc52aab630f` |
|
||
|
||
两个小程序分别登录。服务密钥仅供后端使用,不进入小程序、页面参数或日志。开发版/体验版跳转目标体验版,正式版跳转目标正式版,两侧需协调发布。
|
||
|
||
设备每次重新建立 Banban 绑定时生成新的 `device_bindings.binding_instance_id`。加卡、关联宝贝保持原实例。原有 `sourceBindingId=banban:{binding_instance_id}` 保持稳定,查询/关闭路径对整个 ID 做 URL 编码,包括冒号;不能为旧关系换 ID 绕过关闭状态。
|
||
|
||
## 3. ICCID 来源与保存
|
||
|
||
设备成功的 `006` 回复:
|
||
|
||
```json
|
||
{
|
||
"msg_id": "006",
|
||
"status": "success",
|
||
"data": {
|
||
"uuid": "53D22B6DA20001",
|
||
"iccid": "898604E91123D1334901"
|
||
}
|
||
}
|
||
```
|
||
|
||
ICCID 为 18~22 位字母或数字,归一化为大写,持久化到 `device_settings.iccid`。准备操作时保存到操作表作为快照。不能从 IMEI 推导;设备换卡或其他设备占用同一 ICCID 时不覆盖原记录。
|
||
|
||
保存最多等待 2 秒;缺失、非法或保存失败不影响 NFC 绑定。日志记录 `006 ICCID present=..., valid=...`,保存成功记录 `006 ICCID persisted`,不打印完整 ICCID。当前仅接入 `006`,没有替固件实现上报或为历史设备自动回填。
|
||
|
||
## 4. Banban 接口与状态
|
||
|
||
所有接口使用 Banban 用户登录态,并校验设备归属账号。家庭成员不能执行关联、确认或解绑。
|
||
|
||
| 方法与路径 | 用途 |
|
||
| --- | --- |
|
||
| `GET /banban/joint-bindings/devices/{device_id}` | 本地当前绑定实例的操作概况 |
|
||
| `POST /banban/joint-bindings` | 创建/复用 `{device_id,action:bind/unbind}` 操作 |
|
||
| `GET /banban/joint-bindings/{operation_id}` | 读取本地操作 |
|
||
| `POST /banban/joint-bindings/{operation_id}/check` | 向 4G 查询原关系并更新本地确认状态 |
|
||
| `POST /banban/joint-bindings/{operation_id}/prepare` | 绑定申请 handoff;解绑先关闭关系,必要时返回 4G 跳转信息 |
|
||
| `POST /banban/joint-bindings/{operation_id}/result` | 接收客户端结果;成功仍需服务端查询确认 |
|
||
| `POST /banban/joint-bindings/{operation_id}/finish-unbind` | 重新确认关闭结果后,事务性完成 Banban 解绑 |
|
||
| `GET /banban/joint-bindings/devices/{device_id}/portal` | 查询原关系后返回查询/充值入口 |
|
||
|
||
原 `DELETE /banban/bindings/{device_id}` 返回 409,引导联合解绑。旧客户端需要更新才能使用新解绑流程。
|
||
|
||
操作的 `status=pending/partial_failed/completed` 不代表 Banban 是否已绑定。新增 `traffic_status=server_confirmed` 表示 4G 服务端关系查询/关闭已确认;旧的 `client_confirmed` 只代表客户端回报,不能授权解绑。字段已为 VARCHAR,无需为本次新增状态再加列。
|
||
|
||
绑定确认要求原关系为 `bound + open`。解绑确认要求 4G 明确返回 `canUnbind=true`,同时校验 `closing`、无待处理凭证、不再接受 handoff、状态为 `unbound/never_bound`。响应必须 HTTP 200、业务码 OK、sourceBindingId 匹配,布尔字段必须是布尔值,checkedAt 必须是合法 UTC 时间。
|
||
|
||
## 5. 流量关联流程
|
||
|
||
1. Banban NFC 绑定独立完成,用户自愿继续关联。
|
||
2. 后端使用服务凭证向 `POST /internal/v1/handoff-tokens` 申请凭证;sourceBindingId 始终对应同一 ICCID。
|
||
3. 4G 小程序使用自身登录态调用 `POST /api/v1/program/bindings/handoff`;原消费请求重试必须沿用 token 与业务 requestId。
|
||
4. 客户端返回经过字段匹配的结果,Banban 再使用 GET 关系接口确认原绑定,确认后显示查询/充值入口。
|
||
5. 申请返回 `HANDOFF_TOKEN_USED` 时,Banban 直接查询原关系;只有原绑定仍有效才恢复成功,不再跳转后按当前 ICCID 设备列表猜测。
|
||
6. 4G 小程序即使保存过成功结果,再次提交也让 4G 后端校验原 token,防止旧回执关联到后来的其他绑定。旧 recoveryOnly 入口提示返回 Banban 确认。
|
||
|
||
关联失败不回滚 Banban 绑定。管理页刷新和打开查询/充值入口都会检查原关系,4G 侧独立解绑后不会继续把旧缓存当作有效关联。
|
||
|
||
## 6. 联合解绑流程
|
||
|
||
1. 用户确认后创建持久化 `action=unbind` 操作;此记录阻止同实例再申请 handoff。
|
||
2. 用户点击继续,调用 `POST /internal/v1/binding-relations/{sourceBindingId}/close`,业务请求号为 `bb-close-{operation_id}`。
|
||
3. 返回 `canUnbind=true`:无需跳转,直接执行本地解绑。此判断仅依赖原关系,ICCID 缺失也不会挡住已经得到的服务端确认。
|
||
4. 返回 `bound`:保留 Banban 绑定,携带原 ICCID 跳转 4G 小程序,用户用自己的 4G 登录态调用现有 unbind 接口。
|
||
5. 用户返回、重新打开页面或在 4G 其他入口完成解绑后,可以主动查询/继续处理;最终提交前再次调用幂等 close,只有确认放行才执行本地事务。
|
||
6. 本地设备解绑、家庭关联停用、操作完成在同一事务内提交。失败保留已确认状态,重试仍检查原关系;已完成操作可幂等返回。
|
||
7. 未知关系、鉴权错误、限流、网络异常均保留绑定,不执行自动猜测或无限重试。
|
||
|
||
| 4G 状态 | 处理 |
|
||
| --- | --- |
|
||
| 原绑定仍有效 | 保留 Banban,前往 4G 解绑 |
|
||
| 原绑定已解除 | 关闭后确认 canUnbind=true,再解绑 Banban |
|
||
| 有记录证明从未完成绑定 | 撤销待消费凭证并确认放行 |
|
||
| 没有关系记录或历史不明确 | unknown,不自动放行,需核查 |
|
||
|
||
关闭不会自动解除 4G 用户绑定或删除套餐。它会持续禁止该 sourceBindingId 签发/消费 handoff,文档没有取消关闭接口。页面明确提示继续后不能恢复本次关联;“稍后处理”只是离开页面,不恢复已关闭关系。
|
||
|
||
4G 设备详情页对本机留有 Banban 标记的设备引导回 Banban;清缓存/换手机无法依靠该本地标记拦截所有独立解绑,服务端关系查询负责后续确认。
|
||
|
||
## 7. 有限等待与并发
|
||
|
||
| 环节 | 上限 |
|
||
| --- | --- |
|
||
| 006 保存 ICCID | 2 秒,失败继续 NFC 绑定 |
|
||
| Banban 新业务接口 | 8 秒总期限 |
|
||
| Banban 调用 4G | 默认 6 秒网络超时,外层受 8 秒期限约束 |
|
||
| Banban 小程序 HTTP | 10 秒,平台超时及独立计时器 |
|
||
| 4G 微信登录 | 10 秒 |
|
||
| 4G 每次 HTTP | 15 秒,超时终止请求 |
|
||
| 4G 单次操作 | 45 秒 |
|
||
| 页面及跨小程序跳转 | 15 秒 |
|
||
|
||
无自动无限循环或长期轮询,失败后用户主动重试。关闭按 sourceBindingId 幂等;超时不换绑定实例或业务请求号。
|
||
|
||
Banban 不跨网络持有数据库行锁:先读取、关闭事务,再请求 4G,返回后重新校验当前设备所有者和绑定实例。最终事务再次校验操作。4G 端关闭与签发/消费的互斥依赖新版后端的关系锁;本地模拟测试不能替代这部分 MySQL 并发验收。
|
||
|
||
## 8. 配置与数据库
|
||
|
||
```dotenv
|
||
TRAFFIC_4G_API_BASE_URL=https://role.api.talkingq.com/traffic-4g
|
||
BANBAN_HANDOFF_SERVICE_TOKEN=<两侧一致的服务端密钥>
|
||
TRAFFIC_4G_TARGET_APP_ID=wx56e1cfc52aab630f
|
||
TRAFFIC_4G_TIMEOUT_SECONDS=6
|
||
```
|
||
|
||
查询和关闭沿用现有密钥。必须先部署 4G 新 JAR、V13 迁移及 Nginx 新路由;仅替换 JAR 不保证公网接口可用。历史关系无法准确回填时保持 unknown。
|
||
|
||
Banban 首次安装本功能:已有库执行一次 `talkingq-url/mysql/migrations/20260923_joint_binding.sql`;新库使用 `mysql/init/02-init.sql` 与 `03-joint-binding.sql`。已执行迁移的生产库不要重跑 ADD COLUMN。本次查询/关闭接入不需要新增 Banban 数据库迁移。
|
||
|
||
## 9. 本地验证与发布
|
||
|
||
```bash
|
||
# talkingq-url(既有告警测试断言此测试地址,不需要真实 .env)
|
||
SERVER_HOST=192.168.101.78 .venv/bin/python -m pytest tests -q
|
||
# banban-mini
|
||
npm run test:ui
|
||
npm run build:weapp
|
||
# 4g-miniprogram
|
||
node --test tests/*.test.cjs
|
||
```
|
||
|
||
覆盖:原关系解绑、从未绑定、未知关系、旧客户端成功回执、原关系与后来 ICCID 绑定区别、关闭失败重试、事务回滚、重绑后的旧响应、格式/鉴权/限流/超时、页面恢复及跳转计时。
|
||
|
||
状态事务测试使用 SQLite 适配,HTTP 使用模拟传输;没有访问生产 4G 接口或验证真实 MySQL 锁竞争。微信登录、真机跨小程序返回、V13 历史回填和真实关闭/消费并发需部署到联调环境后验收。伴伴构建可执行,全量 TypeScript 检查有既有类型错误,不将构建通过等同于全量类型检查通过。
|
||
|
||
2026-09-23 首次提交内容在独立目录验证:后端 142 项通过,Banban 小程序 30 项通过且微信小程序构建成功,4G 小程序 16 项通过且全部页面脚本/JSON 校验通过。全量 TypeScript 检查的 6 项错误与修改前 HEAD 一致。后续服务器部署及兼容修复验证见第 10 节。
|
||
|
||
## 10. 2026-09-23 服务器更新记录
|
||
|
||
按用户授权,于北京时间 14:21 完成 Banban 后端更新,范围为本功能相关的 8 个 Python 文件和已有库迁移脚本。未上传两个小程序,未配置 4G 服务密钥。
|
||
|
||
服务器:`101.35.224.118`,应用目录:`/home/ubuntu/banban-backend`。
|
||
|
||
备份目录:`/home/ubuntu/banban-backups/joint-binding-20260923-1411`,仅 root 可读:
|
||
|
||
- `code-and-config.tar.gz`:更新前代码与配置,排除虚拟环境、资源、运行数据和日志。
|
||
- `binding-tables.sql.gz`:`device_settings`、`device_bindings`、`device_family_members`、`device_bind_history` 的一致性快照。
|
||
- `replaced-files/`、`manifest-before.json`、`manifest-after.json`:被替换文件和更新前后 SHA-256 清单。
|
||
|
||
迁移后四表记录数仍为 63、22、33、144;22 条绑定均生成互不重复的绑定实例 UUID。新操作表为空,未为了验证而创建或解除真实绑定。
|
||
|
||
更新后验证:进程 `511137` 启动成功,MQTT 重新连接;7 个新接口路径已注册;设备所有者状态查询与原绑定查询均为 HTTP 200;未关联设备的查询/充值跳转接口返回预期 409;未登录访问返回 401;公网 HTTPS OpenAPI 和绑定提示音均为 200。
|
||
|
||
截至 14:22,`TalkingQ_ZHWK00003111` 的 ICCID 仍为空,尚无更新后的 `006 ICCID` 检测日志,需要设备重新上报。`BANBAN_HANDOFF_SERVICE_TOKEN` 仍未配置,申请 4G 凭证暂不可用。
|
||
|
||
14:25 按用户提供的密钥配置 `BANBAN_HANDOFF_SERVICE_TOKEN`。更新前 `.env` 备份于 `/home/ubuntu/banban-backups/handoff-token-20260923-1424/env.before`;其他配置项保持原值,现有 `.env` 权限收紧为 600。独立进程校验有效配置与提供值一致,重启后进程为 `512483`,HTTPS OpenAPI 为 200,MQTT 连接正常。密钥值不记录到文档或仓库。
|
||
|
||
4G 申请接口空请求分别在无密钥、有密钥时均返回 400 `INVALID_REQUEST`,说明该检查无法证明密钥鉴权成功;未提交有效 ICCID 或创建交接凭证,实际关联仍待正常业务请求验证。
|
||
|
||
### 17:58 新版关系查询与关闭流程部署
|
||
|
||
按用户后续授权部署 `515567f` 的关系校验流程,并补充 `9cb7195` 的 Python 3.10 时间戳兼容修复。与下午旧版本相比,仅替换 `banban/routers/joint_binding.py`、`banban/service/joint_binding.py`、`banban/service/traffic_handoff.py` 三个文件;已有数据库结构满足要求,未重复执行迁移,`.env` 及服务密钥保持不变。
|
||
|
||
更新前备份位于 `/home/ubuntu/banban-backups/relation-check-20260923-1755`,包含代码与配置归档、五张绑定相关表的一致性 SQL 快照、替换文件及哈希清单;兼容修复前的二次备份和最终 `verification.json` 位于其 `compat-fix/` 子目录。目录仅 root 可访问。
|
||
|
||
首次线上验证发现 4G Java 服务的 `checkedAt` 使用纳秒精度,服务器 Python 3.10 的 `datetime.fromisoformat` 无法解析;修复为分别校验 UTC 时间格式和日历值,保留原始精度。修复后本地后端 151 项测试通过,服务器 Python 3.10 的 9 个时间戳有效/无效用例通过。
|
||
|
||
最终进程为 `563609`。本机及公网 HTTPS OpenAPI 均为 200,8 条联合绑定路由已注册;未登录校验请求为 401,已登录不存在操作的校验请求为预期 404 `OPERATION_NOT_FOUND`;现有绑定查询、联合绑定概览及绑定提示音均为 200;启动和 MQTT 连接正常。部署文件哈希与提交一致,配置哈希保持不变。
|
||
|
||
使用既有服务密钥执行真实 4G 关系 GET,返回 200 `OK`,新版适配器成功解析:3111 当时为 `bound / open / canUnbind=false`。另一个历史关系只读抽查为 `unknown`,不能据此允许伴伴解绑。最终验证前后五表记录数一致,未创建测试绑定、关闭真实关系或执行实际解绑;4G `/close` 的真实关闭效果、消费并发及微信真机往返仍需业务联调验证。本次未部署 4G JAR/V13,也未上传或发布两个小程序。
|