Agent tokens use agent_id as user_id while Binding is under the human member; authorize schema/push/ensure against the agent's mounted OnlineDBID. Co-authored-by: Cursor <cursoragent@cursor.com>
684 lines
46 KiB
Markdown
684 lines
46 KiB
Markdown
# 联调后修改意见 · 宇恒松离线(形态 B)
|
||
|
||
> 初稿:2026-08-01 · 持续修订至 **2026-08-05 晚**
|
||
> 本次整理:生产 platform 已恢复;**§0.3 双方配合**;默认同步通道**默认运行中**;Z10d/Z14 合入待生产 pull;凭票/SMS 引号踩坑回写
|
||
> 来源:宇恒客户端 `yuhengyihao_client` ↔ 智建 gateway/platform(本机 `8180`/`8888` 或生产 `aisite.yuxindazhineng.com`)
|
||
> 依据:`松离线-dbsync方案-最终版.md`、`宇恒-松离线数据同步使用文档.md`
|
||
|
||
---
|
||
|
||
## 0. 当前结论
|
||
|
||
| 级别 | 状态 | 说明 |
|
||
|------|------|------|
|
||
| **硬改(阻塞接口上线)** | **无** | Z1–Z14 代码侧大体齐;剩运维配置 + Z14c ACL |
|
||
| **生产栈** | **已恢复(2026-08-05)** | `SMS.Provider: "off"` 须加引号;health `platform/ai=true`;部署脚本 `chmod +x` |
|
||
| **用户自助 Z1–Z7** | **已落实** | Z7:宇恒 ensure 宜带 `database_name`(§0.2 A′) |
|
||
| **Z8–Z9** | **已落实** | 见 §5.5 / §5.6 |
|
||
| **Z10 / Z10d** | **双方已接 / 智建已合入** | ensure + 空表;**Z10d** 已存在表 `ADD COLUMN`(生产须 pull) |
|
||
| **Z11** | **智建已落实** | 发布自动补列 |
|
||
| **Z12** | **双方已接** | 默认同步通道;**默认 Enabled=运行中**(`cb76824`) |
|
||
| **Z13** | **双方代码已接** | 绑定码/手机/凭票/policy;生产配号+Secret 见 §0.2 B |
|
||
| **Z14** | **双方已接;生产 pull** | 指纹 API + Z10d;**Z14c** 智能体挂载 online 已放行;见 §5.11 |
|
||
| **仍建议关注** | 性能 | SQLite remote 串行 drain;高并发易锁 |
|
||
|
||
### 0.1 绑定产品冻结摘要(Z13 · 试运行)
|
||
|
||
| 约定 | 内容 |
|
||
|------|------|
|
||
| 联调号 | 生产「宇信达」用 **`13531041944`**;**禁止**超管号 `13531041945` |
|
||
| 换票 | **凭票免登录**:`ticket-exchange`(`YXD_YUHENG_TICKET_SECRET` + `YXD_HOST_KEY` + 手机);**不要**手写登录密码 |
|
||
| 短信 | **试运行关闭**:`RequireForBind=false`;`Provider: "off"`(**必须加引号**,否则 platform 起不来) |
|
||
| 同号 / 确认 | 命中成员必须弹窗;`confirm=true` 才绑 |
|
||
| 通道状态 | 默认同步通道 **默认运行中**;勿依赖用户先点「启动」 |
|
||
|
||
### 0.2 待改清单(按负责方)
|
||
|
||
> 智建近期提交:`d195aa4` 凭票… → `4222667` SMS 引号 → `cb76824` 默认启动 → (本批)Z10d + fingerprint。生产:`bash ./restart.sh --pull`。
|
||
|
||
#### A. 宇恒 · 已完成(2026-08-05)
|
||
|
||
| 编号 | 内容 | 落地 |
|
||
|------|------|------|
|
||
| **Z10c** | 空表 ensure | `sync_schema.ensure_before_drain` |
|
||
| **Z13b–e** | 绑定码 / 弹窗 / policy / 凭票 / 选同步 | 技能 + `/database/sync/bind/*` + `sync/mode` |
|
||
| **Z14a** | 表级指纹慢心跳 | `sync_fingerprint.py`;不一致 ensure+full_push |
|
||
|
||
#### A′. 宇恒 · 仍建议(非阻塞) / **配合注意**
|
||
|
||
| 优先级 | 编号 | 内容 |
|
||
|--------|------|------|
|
||
| **P0 配合** | **数据怎么上云** | 见 **§0.3**:绑定 → 库选「同步」→ agent drain;智建「查看线上表」只反映已 push/ensure |
|
||
| **P0 配合** | **online_db_id** | 智能体 JWT 可带 `agents/me` / ticket 返回的 `{channel}_uN`(Z14c 已修);人类自助 JWT 仍须本人 Binding |
|
||
| **P1** | **Z12e / Z7 / §2.6** | env 过渡;Binding 可读名;按库串行 drain |
|
||
| **P1** | **指纹** | 优先调智建 `schema/fingerprint`;未部署则回退 `schema.row_count` |
|
||
|
||
#### B. 智建 · 已完成(生产已验证 / 代码已合入)
|
||
|
||
| 项 | 说明 |
|
||
|----|------|
|
||
| gateway 公开 bind/ticket | 生产 health 已通 |
|
||
| `SMS.Provider: "off"` | YAML 布尔踩坑已修 |
|
||
| 默认同步通道默认启动 | `IsSystemDefault` 创建即 Enabled;重启自动 Start |
|
||
| Podman DNS 别名 | compose hostname/aliases |
|
||
| 部署脚本可执行位 | `restart.sh` 等 100755 |
|
||
|
||
#### B′. 智建 · 仍待办
|
||
|
||
| 优先级 | 项 | 说明 |
|
||
|--------|----|------|
|
||
| **P0** | 生产再 pull 本批 | Z10d ADD COLUMN + `schema/fingerprint` + 默认启动;`bash ./restart.sh --pull` |
|
||
| **P0** | 成员手机 | 「宇信达」绑 **`13531041944`**;勿超管 `13531041945` |
|
||
| **P0** | 通道表白名单 | **勿**「填入测试默认」;形态 B 可空 |
|
||
| **P0** | **Z14c** `online_db_id` ACL | ~~ticket/`agents/me` 的 `{channel}_uN` 403~~ → **已修**:智能体 JWT 放行其挂载的 `online_db_id`(`4fae7ae` 后续) |
|
||
| **P1** | 凭票 Secret | `YuhengTicket.Secret` ↔ `YXD_YUHENG_TICKET_SECRET` 同值 |
|
||
| **P1** | `DefaultRemoteDSN` | 生产 Postgres;空则 sqlite 文件(联调) |
|
||
| **P2** | 正式短信 | Provider≠`"off"` 且 `RequireForBind=true` |
|
||
|
||
#### C. 双方已否决 / 不改
|
||
|
||
| 项 | 说明 |
|
||
|----|------|
|
||
| 终端冲突台 | 仅平台超管 |
|
||
| 智建代改宇恒仓 | 分工冻结 |
|
||
| 平台 push 租约 | version 幂等 + 宇恒串行 drain |
|
||
|
||
### 0.3 双方怎么配合(开通 → 看到线上表)
|
||
|
||
```text
|
||
【智建运维】
|
||
1. bash ./restart.sh --pull(平台 Up,health platform=true)
|
||
2. 成员管理绑 13531041944;通道勿测试表白名单;配凭票 Secret
|
||
3. 启用智能体 → 自动默认同步通道(应显示「运行中」)
|
||
4.(可选)生成绑定码发给终端
|
||
|
||
【宇恒终端】
|
||
5. sync_bound?否 → policy → lookup/confirm 或 redeem 或 ticket-exchange
|
||
6. 本机库选「同步」→ Binding + ensure + agent drain/full_push(Z13e)
|
||
7. 慢心跳指纹对账(Z14);缺列依赖智建 Z10d
|
||
|
||
【智建验收】
|
||
8. 「查看线上表」刷新 → 应出现业务表(含曾 0 行空表)
|
||
9. 仍空 = 终端未 push,不是「点启动」就能灌表
|
||
```
|
||
|
||
| 角色 | 负责 | 不负责 |
|
||
|------|------|--------|
|
||
| **智建** | 落点/通道/鉴权/ensure/fingerprint API;控制台查看线上表 | 不替宇恒本机灌历史数据 |
|
||
| **宇恒** | 绑定 UX、选同步、outbox、drain、指纹修复、full-push | 不手填 DSN/通道当产品终态 |
|
||
| **共同** | 同号 `13531041944`;试运行免短信仍须确认;Secret 对齐 | 不用超管号联调 |
|
||
|
||
宇恒对照脚本:
|
||
|
||
- `yxd/app_fastapi/smoke_zhijian_e2e.py`
|
||
- `yxd/app_fastapi/smoke_offline_concurrent.py`
|
||
- `yxd/app_fastapi/smoke_zhijian_gateway_burst.py`(连续 push / stats)
|
||
- `yxd/app_fastapi/smoke_stress_sync.py`(高并发 + gateway 压测)
|
||
|
||
复测摘要(改后):gateway 串行 60/60、`eof502=0`;同 version → `skipped`;`pushed_applied` / `pushed_skipped` 有值;并发 push 加重试可压满(锁冲突靠重试消化)。
|
||
|
||
---
|
||
|
||
## 1. 已验证可用(请保持兼容)
|
||
|
||
请**勿破坏**下列契约(宇恒 agent 已按此对接):
|
||
|
||
1. `GET /api/v1/agent/sync/channels/{id}/whitelist`
|
||
- 返回含 `tables`、`pk_columns`、`conflict_policy`
|
||
2. `POST /api/v1/agent/sync/channels/{id}/push`
|
||
- Body:`table, op, row_pk, row/rows, version, client_outbox_id`(用户自助另加 `online_db_id`)
|
||
- `op`:`insert|update|update_by_id|delete`(平台侧 insert/update 归一为 upsert 可接受)
|
||
- 成功:`{ success: true, result: { ok, applied, skipped, conflict, applied_version, message } }`
|
||
- **同 `(table,row_pk,version)` 已落地 → `skipped=true` 且 HTTP 2xx**
|
||
3. `POST .../pull`、`POST .../bootstrap`
|
||
- 下行灌库;响应含 `columns`(空表也返回列,便于本机建表)
|
||
4. `POST .../schema`、`POST .../schema/ensure`(**Z10**)
|
||
- 拉线上表结构 / 本机空表结构推到线上(`CREATE IF NOT EXISTS`;**Z10d** 部署后已存在表亦 `ADD COLUMN`)
|
||
5. `POST .../schema/fingerprint`(**Z14** · 建议部署)
|
||
- Body:`{ "online_db_id": "..." }`
|
||
- 返回各表 `name/row_count/content_hash/pk_column`;与宇恒本机指纹算法对齐(见 §5.11)
|
||
- **未部署时**宇恒回退只用 `schema.row_count` 对账(可发现行数差,不能发现「同 count 不同内容」)
|
||
6. 鉴权:管理员 JWT(含「数据同步」)、智能体 Token,或**登录用户 JWT**(须本人 Binding + `online_db_id`)均可调 agent API
|
||
7. 错误体优先带可读 `message`;忙/上游失败宜带 `retryable: true`(502/503)
|
||
|
||
**联调账号(写入约定,勿再用错租户 / 勿用超管号)**
|
||
|
||
| 环境 | 用途 | 登录 | 说明 |
|
||
|------|------|------|------|
|
||
| 本机 / 演示公司 | 租户 1 演示 | `13800000001` / `demo123` | 仅本机联调;**不要**拿去推生产「宇信达」通道 |
|
||
| **生产 · 宇信达** | 公司管理 / 同号绑定联调 | **`13531041944`**(密码由开通时设定) | **专用联调号**;智建须在「宇信达」成员/管理员中写入此手机;宇恒 `YXD_SYNC_LOGIN_PHONE` 与 Z13 同号确认均以此号为准 |
|
||
| 平台超管(禁止联调) | 控制台「填入超管手机」 | `13531041945` / `ljk_admin` | **最顶级管理员**;seed `ensureDevPhone(ljk_admin)`;**禁止**用作同步登录 / 绑定联调,会与公司账号冲突 |
|
||
|
||
> **智建待办(运维)**:见 **§0.2 B**(成员手机 `13531041944`、`./restart.sh --pull`、凭票 Secret、通道勿用测试表白名单)。
|
||
|
||
---
|
||
|
||
## 2. 原建议项落实对照(2026-08-03)
|
||
|
||
| 编号 | 原建议 | 智建现状 | 是否还要改 |
|
||
|------|--------|----------|------------|
|
||
| **2.1 P1** | gateway 偶发 502 EOF;超时与可重试 JSON | `ProxyTimeoutSec` 已生效;sync 路径加长超时;502 体含 `retryable`;SQLite DSN 规范化 + `busy_timeout`;remote IO → 503+retryable(见方案 changelog 2026-08-01) | **否(已落实)**。若生产仍偶发 EOF,按运维排障,不升格为接口变更 |
|
||
| **2.2 P2** | 文档写明多 agent / version 幂等 | `docs/数据同步-中间件.md` 已写「允许重复 push / 客户端 drain 锁优化」 | **否** |
|
||
| **2.3 P3** | `pushed_applied` / `pushed_skipped` | `ChannelStats` 已有;burst 联调可见 | **否**。OpenAPI 已标注 applied/skipped,示例可再补一句即可(非必须) |
|
||
| **2.4 P4** | Windows `file:` DSN 示例 | 开通/同步表约定已补正斜杠示例;平台规范化反斜杠 | **否** |
|
||
| **2.5 P5** | 503/502 + retryable | 已按表落实 | **否** |
|
||
|
||
### 2.6 【新增·知会】高并发 push 与 SQLite remote
|
||
|
||
**现象**(宇恒 `smoke_stress_sync`):对同一通道 remote(本机 SQLite)**并行** push 时,大量 `database is locked` / 失败,串行则稳定;加重试后可最终成功,但 p95 延迟明显升高。
|
||
|
||
**边界**:
|
||
|
||
- 正确性仍靠 **version 幂等**,不要求平台做 push 租约。
|
||
- **推荐用法**:宇恒 agent **按库串行 drain**(勿多线程齐推同一 SQLite A)。
|
||
- 若线上 A 为 MySQL/Postgres,并发能力通常好于 SQLite;文档可一句带过「SQLite remote 不适合高并发多 writer」。
|
||
|
||
**智建是否必须改**:**否**。可选更低优:remote 打开时统一更长 `busy_timeout`、或文档强调 SQLite 并发限制(已有 busy_timeout 则足够)。
|
||
|
||
---
|
||
|
||
## 3. 明确不需要智建改的部分
|
||
|
||
| 项 | 负责方 | 说明 |
|
||
|----|--------|------|
|
||
| 本机 drain 互斥锁 / 串行 drain | 宇恒 | 减重复 HTTP 与 SQLite 锁风暴 |
|
||
| 写网关 / outbox / UUID 补齐 | 宇恒 | 未开通零感;仅白名单表同步 |
|
||
| 「agent 停仍可保存」 | 宇恒 | H3 |
|
||
| 终端冲突处理台 | 双方已否决 | 冲突只在平台超管侧 |
|
||
| 改宇恒仓库代码 | 禁止由智建代改 | 分工已冻结 |
|
||
|
||
---
|
||
|
||
## 4. 联调回归清单(改后复测)
|
||
|
||
| 项 | 宇恒侧结果 |
|
||
|----|------------|
|
||
| whitelist 与通道一致 | ✅ |
|
||
| push insert → A 同 UUID | ✅ |
|
||
| push update / delete | ✅ |
|
||
| 同 version → skipped,A 不增行 | ✅ |
|
||
| gateway 连续 push ≥20 无 EOF | ✅(burst 25、stress 串行 60) |
|
||
| stats `pushed_applied` / `pushed_skipped` | ✅ |
|
||
| 并发 push(SQLite) | ⚠️ 需重试;建议客户端串行 |
|
||
|
||
未在每次脚本覆盖:无「数据同步」权限 → 403;旧 version + `lww_source` 全矩阵——以平台单测/文档为准即可。
|
||
|
||
---
|
||
|
||
## 5. 宇恒侧说明(知会)
|
||
|
||
- 测试已收紧:mock 对齐 skipped、双 drain 断言 applied==N、update/delete 保序、压测脚本带锁重试。
|
||
- **开通仍是可选**:`YXD_SYNC_MODE=local_dbsync`;用户自助以 Binding 为准(**Z4**:不按通道表白名单拒收)。
|
||
- **空表**:sync 周期须调 **Z10** `schema/ensure`(见 §5.7),否则本机空表不会出现在线上。
|
||
- 原「若采纳 2.1 则稳定性提升」:**已验证提升**;本意见可归档为「历史建议 + 落实对照」,无需再开一轮同名改造。
|
||
|
||
---
|
||
|
||
## 5.1 【新增·2026-08-03】用户自助库级三态(宇恒已开做)
|
||
|
||
产品目标:用户登录智能体后,用**自己的全部权限**管理名下数据库/网页;可对每个库选择:
|
||
|
||
| 产品态 | 含义 | 宇恒引擎映射 |
|
||
|--------|------|--------------|
|
||
| **仅本地** `local_only` | 只写本机 | `local_only` |
|
||
| **仅线上** `online_only` | 以线上为主 | `online_primary` |
|
||
| **同步** `sync` | 本机落库 + outbox 上云 | `local_dbsync` |
|
||
|
||
宇恒已落地(本机):
|
||
|
||
- `POST/GET /database/sync/mode`、`GET /database/sync/policies`
|
||
- 策略键:`(tenant_id, user_id, local_database_id, database_name)`
|
||
- 写网关按命名库策略覆盖全局 `YXD_SYNC_MODE`(默认仍不静默升 sync)
|
||
- 数据库区卡片「同步设置」可视化三态;鉴权优先**登录用户 JWT**(`app.client.access_token`)
|
||
- 选「同步」后可拉起本机 agent;push 带头里的用户 Bearer
|
||
|
||
---
|
||
|
||
## 5.2 【请智建配合改 / 兼容】用户态同步
|
||
|
||
> 不破坏现有管理员通道 + agent Token 联调路径;以下为**用户自助开通**所需平台能力。
|
||
|
||
| 编号 | 优先级 | 诉求 | 说明 | 智建现状(2026-08-04) |
|
||
|------|--------|------|------|------------------------|
|
||
| **Z1** | **P0** | **登录用户 JWT 可调 agent sync API** | whitelist / push / batch 允许已登录普通用户(本人资源范围)。 | **已落实** |
|
||
| **Z2** | **P0** | **权限范围 = 用户自己的库** | 带 `online_db_id` 时一律校验本人 Binding;异库 → 403(含有「数据同步」的人类)。 | **已落实** |
|
||
| **Z3** | **P1** | **Binding 支持用户自助登记** | `POST/GET .../bindings` 认用户 JWT;强制本人 `user_id`。 | **已落实** |
|
||
| **Z4** | **P1** | **用户选 sync 时白名单策略** | **冻结②**:不按通道表白名单拒收;Binding 库内任意表可 push,可自动建表。 | **已落实** |
|
||
| **Z5** | **P2** | **仅线上 / 下行补齐** | A→B / pull / bootstrap。 | **已落实** |
|
||
| **Z6** | **P2** | **控制台文案** | Binding 即权限;管理员不负责维护表白名单。 | **已落实** |
|
||
| **Z7** | **P1** | **SyncPage / Binding 展示「可读库名 + 映射」** | 通道「线上」列优先可读名;副文案 online_db_id / 落库路径;统计标注为 push 次数;Binding 列表「本地库名 ↔ 线上库名」。 | **已落实(2026-08-04)**:Binding 增 `database_name`/`display_name`;SyncPage 主标题优先本地 `database_name`,↑ 标注「推送次数 / 非表数」。宇恒 ensure 须带可读名,否则仍会回退到通道名或 driver |
|
||
| **Z8** | **P0** | **模块数据:本机库可管理 + 按智能体上云** | 见 §5.5:本机 B 管理模块表;push 到智能体绑定的 A;废弃默认双轨 | **已落实(含 Z8g 展示)** |
|
||
| **Z9** | **P0** | **模块默认 import + 存量扫库** | 见 §5.6 | **已落实 Z9a–f** |
|
||
| **Z10** | **P0** | **空表两侧建齐** | 见 §5.7:仅靠 outbox 不会建空表 | **双方已接**:智建 API + 宇恒 Z10c(`sync_schema`) |
|
||
| **Z11** | **P1** | **蓝图增字段迁库** | 见 §5.8 | **智建已落实** ALTER + 导入提示 |
|
||
|
||
### 建议验收(智建改后)
|
||
|
||
1. 演示账号登录拿用户 JWT(**无**单独「数据同步」管理员权)→ `GET .../channels/{id}/whitelist` → 2xx。
|
||
2. 同 JWT → `POST .../push` 写入本人 Binding 库 → A 出现同 UUID。
|
||
3. 同 JWT 推他人 `online_db_id` → 403。
|
||
4. 原管理员 / 智能体 Token 路径回归仍通过(不破坏联调结论)。
|
||
|
||
### 宇恒 ↔ 智建分工(本项)
|
||
|
||
| 方 | 负责 |
|
||
|----|------|
|
||
| 宇恒 | 库级三态 UI/API、写路径分流、outbox、用登录态调 push;**默认不按通道表白名单过滤**;**Z10c 已接** schema/ensure |
|
||
| 智建 | 用户 JWT 鉴权、Binding 校验、任意表 push;**SyncPage/Binding 按可读名展示(Z7)**;**Z10a/b** 空表 API |
|
||
|
||
---
|
||
|
||
## 5.3 【复测·2026-08-03 下午】智建改后 × 宇恒库级策略
|
||
|
||
> 环境:`127.0.0.1:8180` + `:8888`;演示账号 `13800000001` / `demo123`
|
||
> 脚本:`yuhengyihao_client/yxd/app_fastapi/smoke_zhijian_e2e.py`、`smoke_user_jwt_policy.py`
|
||
> 说明:直连智建验收(当时本机 8080 未起);push 遇 gateway **502 EOF / 503** 时按 `retryable` 重试后成功。
|
||
|
||
### 对照表
|
||
|
||
| 编号 | 项 | 结果 | 备注 |
|
||
|------|----|------|------|
|
||
| — | 登录拿用户 JWT | ✅ | `token_len=225`;`/api/v1/auth/me` 可读 |
|
||
| — | 经典 e2e(B→outbox→push→A 同 UUID) | ✅ **PASSED** | 首次 push 常 502/503,约 2–4 次重试后成功;二次 insert 同行数≥2 |
|
||
| **Z1** | 用户 JWT 拉 whitelist | ✅ | HTTP 200,`tables=['orders']` |
|
||
| **Z1** | 用户 JWT 直推本人库 | ✅ | 带 `online_db_id`;A 可见同 UUID(重试后 200,或同 version → `skipped`) |
|
||
| **Z3** | 用户 JWT 自助 Binding | ✅ | `POST .../admin/sync/bindings` → 200,返回 `user_id/local/online/channel_id` |
|
||
| — | 库级策略 `sync` → 写网关 `local_dbsync` | ✅ | 全局未强制 `YXD_SYNC_MODE=local_dbsync` 时,命名库策略仍进 outbox |
|
||
| — | 登录态 agent drain 上云 | ✅ | drain 重试后 `pushed=1`,A 行 title=`policy-sync` |
|
||
| **Z2** | 推他人 `online_db_id` → 403 | ✅ **宇恒复测通过(2026-08-04)** | `403 无权访问该 online_db_id(非本人 Binding)`;`smoke_user_jwt_policy.py` PASSED |
|
||
| **Z4–Z6** | 白名单 / 下行 / 文案 | ✅ | Z4 冻结②已落实(任意表+自动建表);Z5 pull;Z6 SyncPage |
|
||
|
||
### 现象与建议(运维)
|
||
|
||
1. **Gateway 偶发 502 EOF / 503 Request Timeout**,体带 `retryable: true`;正确性仍靠 version 幂等(重试后常见 `skipped=already applied`)。
|
||
2. **推荐**:宇恒 agent / 联调脚本对 push **串行 + 按 retryable 重试**(已在上述 smoke 加重试与 error→pending 恢复)。
|
||
3. **Z2**:**2026-08-04 宇恒复测通过**——异库返回 `403 无权访问该 online_db_id(非本人 Binding)`。
|
||
4. 前端「数据库区 → 同步设置」需本机服务起来后人工点验;本次后端契约已通。
|
||
|
||
### 再测摘要(2026-08-03 17:53)
|
||
|
||
| 脚本 | 结果 |
|
||
|------|------|
|
||
| `smoke_zhijian_e2e.py` | ✅ PASSED(首推即成功,无 502) |
|
||
| `smoke_user_jwt_policy.py` | ✅ 主路径 PASSED;**当时 Z2 WARN** 异库仍 applied |
|
||
|
||
### 再测摘要(2026-08-04 · 智建 Z2/Z4/Z6 重启后 · 宇恒确认)
|
||
|
||
| 脚本 | 结果 |
|
||
|------|------|
|
||
| `smoke_user_jwt_policy.py` | ✅ **PASSED**;**Z2 OK**(异库 403);直推 + 库级 sync drain 上云均通过 |
|
||
|
||
### 结论
|
||
|
||
- **用户自助主路径(Z1 + Z2 + Z3 + 宇恒库级 sync)已打通**。
|
||
- **Z2(2026-08-04)**:异库 `online_db_id` → **403**(智建已修,宇恒已确认)。
|
||
- **Z4 已冻结并落地**:push 不按通道表白名单拒收;Binding + 用户 JWT 即权限;可自动建表。
|
||
|
||
### 宇恒澄清(2026-08-04 · 对照截图)
|
||
|
||
| 现象 | 说明 | 责任方 |
|
||
|------|------|--------|
|
||
| SyncPage `↑37` | **不是表数**,是通道 `pushed_ok`(push 成功次数);与线上 `orders` 行数巧合接近 | 智建 Z7 已改列名为「推送次数」并加副文案 |
|
||
| 显示 `ai_site_….db` 而非「AI建站智能体API」 | SyncPage 曾用落库 DSN 当主展示;应对齐本地库名,id/路径作副文案 | **智建 Z7**(主标题优先 Binding.`database_name`);宇恒 ensure 带 `database_name`/`display_name` |
|
||
| Binding `local_*` ↔ `online_*`「名字对不上」 | **id 本就可不同**(映射);烟雾联调造的占位 id。真实库 `6a3df5…` 已补可读名「AI建站智能体API」;默认隐藏 smoke Binding | 智建已清烟雾数据 + UI 过滤;宇恒正式 ensure 带可读名 |
|
||
| 本地多表 vs 线上曾只有 `orders` | **当时** remote 仅 `orders`;`accounts` / `填土高度` 尚未 push,**不是**映射错库 | 后续 `填土高度` 已 push 对齐;空表仍需 **Z10 ensure**(见 §5.7) |
|
||
| 弹窗 4 表「0 条记录 / 0 字段」 | 卡片统计不准;库内仍有数据 | **宇恒前端**(智建 SyncPage 无此卡片);可用数据预览或打开 remote db |
|
||
| **要看线上表内容验同步** | SyncPage 原先只显示通道名/推送次数 | **智建已加**:「查看线上表」→ 表名/行数/字段 + 行预览;**可「删表」**(仅当前侧,需确认;不同步 DDL) |
|
||
| **图1「线上表」≠ 图2 宇恒「数据表」** | 见 §5.4:一边是线上 A,一边是本机 B | **不是串库**;未 push / 未 ensure 的表不会出现在线上 |
|
||
| **模块数据按智能体进库** | 模块与 dbsync 原无关联 | **§5.5 Z8 已通**;空表 **Z10c 宇恒已接** |
|
||
|
||
同步链路本身可用;展示层勿把「通道文件名 + 推送次数」当成「本地库名 + 表行数」。
|
||
|
||
### 5.4 图1 vs 图2:表名「对不上」说明(2026-08-04 · 现状已回写)
|
||
|
||
| | 图1 智建「查看线上表」 | 图2 宇恒「数据库管理 · 数据表」 |
|
||
|--|------------------------|--------------------------------|
|
||
| **看的是哪边** | 通道 **线上 A**(例:`ai_site_1785779755.db`) | 本机正式库 **B**(「AI建站智能体API」) |
|
||
| **联调当时(早)** | 仅 `orders`(约 37 行)+ `_ajz_*` | `accounts`、`orders`、`填土高度…` 等 |
|
||
| **回写后(已 push)** | `orders`(1)+ **`填土高度(6标一工区)`(96)**;outbox 全 `done` | 同行数对齐;`accounts` 仍 0 行 |
|
||
| **仍可能不一致** | 空表(如 `accounts`)未 ensure → 线上无此表 | 本机有空表;须走 **Z10** `schema/ensure`,单靠 outbox 不会建空表 |
|
||
| **如何验同步** | 「查看线上表」预览行;或对照 `_ajz_sync_meta` | 宇恒「数据预览」看本机真实行 |
|
||
|
||
**结论**:表名集合不同 = **本机有、线上尚未 push/ensure**,不是 Binding/通道指错库。有行的表走 drain push;**空表必须走 Z10 ensure**。
|
||
|
||
`_ajz_sync_*` 是中间件系统表(版本/outbox),**不是业务模块表**;控制台「查看线上表」默认隐藏(Z8e)。
|
||
|
||
### 5.5 【进展 Z8】模块数据 ↔ 本机库 + 智能体库
|
||
|
||
> **产品原则(2026-08-04 确认)**
|
||
> 1. **模块数据也要能在本机库 B 里管理**(宇恒数据库区:建表/增删改查/导入),与普通业务表同一套体验,不是只能在聊天「模块操作」或线上 Postgres 里改。
|
||
> 2. 本机改完后,经松离线 **push → 该智能体绑定的线上库 A**;换机/仅线上可用 **pull/bootstrap** 灌回本机。
|
||
> 3. **哪个智能体的模块 → 进哪个智能体的库**(通道/`online_db_id` 绑定)。
|
||
> **进展**:Z8a–g 已通(本机镜像、绑库、SyncPage 模块展示);空表 **Z10c 宇恒已接**(`sync_schema`)。
|
||
|
||
**目标数据流**
|
||
|
||
```text
|
||
宇恒本机库 B(含模块表,可本地管理)
|
||
│ sync / agent drain push
|
||
▼
|
||
智能体绑定的线上库 A(与通道 remote 一致)
|
||
│ 智能体 / 控制台「查看线上表」核对
|
||
▼
|
||
(可选)模块页 CRUD 读同一 A,或本机 B 为唯一写入口 + 同步
|
||
```
|
||
|
||
| 编号 | 优先级 | 诉求 | 说明 | 现状 |
|
||
|------|--------|------|------|------|
|
||
| **Z8f** | **P0** | **模块表可在本机库管理** | 模块实体表出现在宇恒「AI建站智能体API」类本机库中;数据预览/录入/导入与业务表一致;开通 sync 后进 outbox | **宇恒已通(2026-08-04)**:`local_module_db` 镜像;表名 **`模块·{模块名}·{资源}`**(无下划线);列含「所属模块」「模块slug」;写网关 → outbox → push |
|
||
| **Z8a** | **P0** | **统一「一份表」叙事** | 废弃「模块一套库、松离线又一套」双轨并存为默认;默认:**本机 B 为模块数据管理面**,A 为同步副本(或同库) | **已补开通说明**(`docs/数据同步-开通说明.md`) |
|
||
| **Z8b** | **P0** | **智能体选库 / 选通道** | 该智能体绑定 `channel_id` + `online_db_id`;宇恒可选「同步到哪个智能体库」 | **智建已落实**:用户管理可绑通道/线上库/落库名;SyncPage 按智能体筛通道。宇恒开通 sync 时会 `PUT` 通道 `agent_id`/`app_slug`(读 `YXD_SYNC_*`) |
|
||
| **Z8c** | **P1** | **线上与本机对齐** | 智能体读模块数据时优先本机 B(离线)或已同步的 A;发布蓝图在本机建表(UUID PK)再 sync,避免只在平台 schema 建一份 | **部分**:智能体新建发布若设 `database_name` → `database_per_app` 落该库;本机建表靠宇恒 Z8f |
|
||
| **Z8d** | **P1** | **本地上传 / 编辑 → 上云** | 本机模块表变更经 Binding + push 进该智能体 A;「查看线上表」可核对 | **路径已通**:本机改 `模块·…` 表 → drain → 「查看线上表」应见同表;若仍缺行 = 未 push / 未 drain |
|
||
| **Z8e** | **P2** | **查看线上表默认藏 `_ajz_*`** | 减少「表名不对」误解 | **智建已做默认隐藏**(2026-08-04) |
|
||
| **Z8g** | **P1** | **本机可见 = 线上可见(展示闭环)** | SyncPage「智能体/模块」勿长期「未绑模块」;「查看线上表」列出本机已有业务表(含 `模块·…`);模块列优先展示名 | **智建已落实(2026-08-05)**:模块展示名 + slug 副文案;策略表标「参考/整库同步」 |
|
||
|
||
**分工建议**
|
||
|
||
| 方 | 负责 |
|
||
|----|------|
|
||
| 智建 | Z8a 文案;Z8b 智能体↔通道/库绑定;**Z8g SyncPage 模块展示名**;Z10a/b API;开通说明 |
|
||
| 宇恒 | **本机库管理模块表(Z8f 已通)**;开通/全量推送时挂通道 `agent_id`/`app_slug`;**Z10c 已接** sync 周期 schema/ensure |
|
||
|
||
**验收(Z8 完成后)**
|
||
|
||
1. ✅ 在宇恒本机库能看到并编辑模块相关表(Z8f)。
|
||
2. ✅ 本机改模块行 → sync 后智建「查看线上表」出现同表同行(有数据路径;空表见 Z10)。
|
||
3. ✅ 智能体 X 只落到绑定库;管理员可按智能体筛通道验数(Z8b)。
|
||
4. ✅ 文档写明:本机有、线上无 = 尚未 push/ensure,不是串库。
|
||
5. ✅ SyncPage「智能体/模块」显示智能体名 + 模块**展示名**(Z8g;slug 作副文案)。
|
||
|
||
### 5.5.1 【联调补丁·2026-08-04】编辑发布撞 page id
|
||
|
||
**现象**:编辑模块 `mode=add_pages` 时报
|
||
`page id already exists: record_list`(模型复用了已有页 id,实为要改 API `import`)。
|
||
|
||
**智建已改**:`platform/internal/blueprint/merge.go` — 同 page id **合并 actions**(不再 400);同 path resource **并集 operations**;仅有 API/按钮更新也视为可发布(`UpdatedResources`/`UpdatedPages`)。需**重启 platform** 后生效。
|
||
|
||
**与 Z9**:Z9 默认带 import 后,此类「仅为开导入而编辑」会减少;本补丁仍保留给「改已有页能力」用。
|
||
|
||
### 5.5.2 【已落实 Z8g】本机可见 ↔ 线上可见(展示增强 · 2026-08-05)
|
||
|
||
> **联调现象(对照两图)**
|
||
> - 宇恒「数据表」已有:`orders`、`填土高度(6标一工区)`、**`模块·测·Sheet1`**(及业务表)。
|
||
> - 智建 SyncPage:有 `app_slug` 时解析已发布模块**展示名**,slug 作副文案;仅缺绑定才「未绑模块」。
|
||
> - 通道表白名单 UI 标为「策略表(参考)」/「整库同步中」,避免误读为只能 sync 名单内表。
|
||
|
||
| 编号 | 优先级 | 诉求 | 说明 | 负责 |
|
||
|------|--------|------|------|------|
|
||
| **Z8g-1** | **P1** | **SyncPage 模块列可读** | 展示名 + slug 副文案 | **智建已落实** |
|
||
| **Z8g-2** | **P1** | **通道绑定可被宇恒写入** | `PUT` 可写 `agent_id`/`app_slug` | **智建保持契约**;宇恒已接 |
|
||
| **Z8g-3** | **P1** | **查看线上表 = 本机业务表子集** | inspect + 默认藏 `_ajz_*`;缺表文案 | **智建已具备** |
|
||
| **Z8g-4** | **P2** | **白名单列勿误导** | 「策略表(参考)」/「整库同步中」 | **智建已落实** |
|
||
|
||
**验收**
|
||
|
||
1. 通道已挂 `agent_id=1`、`app_slug=mismatch_test2` → SyncPage 不再显示「未绑模块」,模块列可见「测」或至少 slug。
|
||
2. 本机 `模块·测·Sheet1` 全量 push 后,「查看线上表」出现同名表且行数对齐(幂等 upsert)。
|
||
3. 本机 `填土高度(6标一工区)` / `orders` 与线上一致。
|
||
|
||
---
|
||
|
||
### 5.6 【已落实 Z9】模块导入默认能力(方案 A · 2026-08-04)
|
||
|
||
> **联调现象**
|
||
> 宇恒绑定账号已具备 `row.import`(导入数据),智能体调用
|
||
> `POST /api/v1/apps/{slug}/{resource}/import` 仍返回
|
||
> `HTTP 400: operation import not allowed`。
|
||
> 根因(已修):平台在角色权限之外,还校验蓝图 `apis.resources[].operations` 是否含 `import`;
|
||
> 旧生成默认只有 `list/get/create/update/delete`。现已默认带 import,且 publish 兜底补全(Z9)。
|
||
> 编辑补丁时模型仍可能只改部分表;**再发布**或依赖 Z9d/`EnsureDefaultImportExport` 即可。
|
||
> 另:同 page id 编辑曾 400,见 §5.5.1(merge 已改)。
|
||
|
||
**产品结论:采用方案 A**
|
||
|
||
绑定宇恒账号解决「谁可以调导入接口」;方案 A 解决「业务表是否声明允许导入」,避免「有权限仍 400」。
|
||
|
||
不采用「仅有 `row.import` 就跳过 `hasOp`」(方案 B)作主路径,以免列表页无导入入口、前后端能力不一致。
|
||
|
||
| 编号 | 优先级 | 诉求 | 说明 | 现状 |
|
||
|------|--------|------|------|------|
|
||
| **Z9a** | **P0** | **生成默认带 import** | 业务 `apis.resources[].operations` 默认含 `list,get,create,update,delete,**import**,**export**`(除非需求明确禁止) | **已落实**:`generation_rules` + `prompt-contract.md` |
|
||
| **Z9b** | **P0** | **列表 actions 对齐** | list 页 `actions` 默认含 **`import`**(及可选 `export`) | **已落实**:发布兜底补 list actions |
|
||
| **Z9c** | **P0** | **改 prompt / 文档** | `prompt-contract.md`:由「提到导入再加」改为「默认加;明确不要再去掉」 | **已落实** |
|
||
| **Z9d** | **P0** | **publish/merge 兜底** | 发布或编辑合并时:业务 resource 缺 `import` 则自动补上(可配置,默认开) | **已落实**:`EnsureDefaultImportExport`;但**只在 publish 路径触发** |
|
||
| **Z9e** | **P0(升格)** | **存量模块一次性扫库补齐** | 平台启动扫库 + 管理接口;不依赖用户再发布 | **已落实(2026-08-05)**:启动 `RunBackfill…`;`POST /api/v1/admin/apps/ensure-import`;模块管理「一键开启导入」;超管 `…/platform/apps/ensure-import` |
|
||
| **Z9f** | **P1** | **读路径也兜底(双保险)** | Import/Export 入口内存补齐后再校验;可读错误文案 | **已落实**:`ensureImportOpInMemory` + 区分蓝图缺 import vs 权限 |
|
||
|
||
**存量复现(必须消灭)**
|
||
|
||
```text
|
||
POST /api/v1/apps/coerce_fields/items/import
|
||
→ HTTP 400: operation import not allowed
|
||
蓝图 ops 实为:list, create, get, update, delete ← 无 import
|
||
(Z9d 已合入后新建模块正常;未再发布的旧模块仍坏)
|
||
```
|
||
|
||
**智建交付标准(Z9e 验收)**
|
||
|
||
1. 不手工编辑、不触发「编辑模块」的情况下,对租户内**全部**已发布可写业务 resource 执行扫库后:`hasOp("import")==true`。
|
||
2. 回归:任取 3 个「合入 Z9 之前发布」的旧 slug,直接 `POST .../import` **不再**出现 `operation import not allowed`。
|
||
3. 控制台可选:「数据同步 / 模块管理 → 一键开启全部业务表导入」按钮,调用同一扫库逻辑。
|
||
4. 文档写明:Z9d≠存量修复;上线 Z9 后**必须跑一次 Z9e**,否则用户会反复以为「绑定/权限坏了」。
|
||
|
||
**分工**
|
||
|
||
| 方 | 负责 |
|
||
|----|------|
|
||
| **智建** | **Z9e/Z9f 已落实**;保持 Z9a–d;错误文案区分「蓝图未开 import」vs「账号无 row.import」 |
|
||
| 宇恒 / 智能体 | 导入前尽力自动补齐(已做);表单选对 slug/resource;**重启 platform 或点「一键开启导入」后**旧模块即可 import |
|
||
|
||
**验收(含存量)**
|
||
|
||
1. 新建模块需求**不提**「导入」→ 发布后 `POST .../import` 不再因 `operation import not allowed` 失败。
|
||
2. **旧模块**(Z9 合入前发布、之后未再发布)在跑完 Z9e 后同样不再 400。
|
||
3. 用户明确写「禁止导入」的表可不含 `import`(`disable_default_import` 或表级标记)。
|
||
4. 400 文案可读:指出是蓝图 operations 缺 import,而不是笼统「操作失败」。
|
||
|
||
### 5.7 【双方已接 Z10】空表也要两侧建齐(2026-08-04 / 宇恒接 2026-08-05)
|
||
|
||
> **现象**
|
||
> 本机有空表(如 `accounts` 0 行)时,线上「查看线上表」没有该表;
|
||
> 仅靠 outbox 行 push 时,**没有行就没有建表事件**。同理,线上空表下行时本机若缺表,旧 pull 只给空 `items`、不给列,本机无法建表。
|
||
|
||
| 编号 | 优先级 | 诉求 | 现状 |
|
||
|------|--------|------|------|
|
||
| **Z10a** | **P0** | 本机空表 → 线上建空表 | **智建已落实** `POST /api/v1/agent/sync/channels/{id}/schema/ensure` |
|
||
| **Z10b** | **P0** | 线上空表 → 本机建空表 | **智建已落实** `POST .../schema`;`pull`/`bootstrap` 响应带 `columns` |
|
||
| **Z10c** | **P0** | 宇恒 sync 周期调用上述 API | **宇恒已接(2026-08-05)**:`sync_schema.ensure_before_drain`;选「同步」force ensure;drain 前 TTL 节流 |
|
||
|
||
**契约摘要**
|
||
|
||
```http
|
||
POST .../schema/ensure
|
||
{ "online_db_id": "...", "tables": [{ "table": "accounts", "pk_column": "id", "columns": ["id","username",...] }] }
|
||
|
||
POST .../schema
|
||
{ "online_db_id": "..." }
|
||
→ result.tables[].name / columns / row_count
|
||
```
|
||
|
||
列一律按 TEXT + 指定 PK 建空表。**Z10d(2026-08-05 晚)**:已存在表不再整表跳过,缺列则 `ALTER TABLE … ADD COLUMN`(`platform/internal/dbsync/ensure_table.go`)。**合入后生产 `./restart.sh --pull` 生效**;未 pull 时对「先按 `_row_id` 建表、后 push 注入 `id`」会持续 `has no column named id`。
|
||
|
||
**验收**
|
||
|
||
1. 本机空表 ensure 后,智建「查看线上表」可见同名 **0 行**表。
|
||
2. 换机 / 仅线上:schema → 本机建空表 → bootstrap 灌行。
|
||
3. 有数据的表仍走原 push;ensure **不替代** outbox 行同步。
|
||
4. **Z10d**:ensure 带新增列名时,已存在表补列成功,随后 push 含新列不再 503。
|
||
|
||
### 5.8 【已落实 Z11】蓝图增字段须迁 Postgres(2026-08-05)
|
||
|
||
> **联调现象**
|
||
> 模块 `record` 蓝图后来带了 `title/content/status`,但 Postgres 表仍只有 `col_0/col_1/col_2`。
|
||
> 导入 Excel 若含「标题/内容/状态」→ `pq: 关系 "record" 的 "title" 字段不存在 (42703)`,整批 skipped。
|
||
> 反过来:蓝图把这些标成非空、Excel 又没有时,会先报 `missing required field: title`。
|
||
|
||
| 编号 | 优先级 | 诉求 | 负责 |
|
||
|------|--------|------|------|
|
||
| **Z11a** | **P1** | 编辑/发布增字段后 **自动 ALTER** 业务表补列 | **智建已落实**:`BuildPostgresAlterDDL` + publish 合并执行;字段-only merge 可发布 |
|
||
| **Z11b** | **P2** | 导入时:Excel 列映射到蓝图有、库无的字段 → 明确提示迁库 | **智建已落实**:42703 行错误附加「请重新发布以自动补列」 |
|
||
|
||
**说明**:存量表补列一律可空,避免 NOT NULL 迁库失败。已有库需**再发布一次**(或编辑发布增字段)才会 ALTER;之后新字段随发布自动补。
|
||
|
||
### 5.9 【智建已落实 Z12】生产:账号绑定即落点,禁止手填通道 / 手建通道(2026-08-05)
|
||
|
||
> **产品约定(生产 · 冻结意向)**
|
||
> 1. **一个登录账号 / 一个智能体 ↔ 本公司同步落点**:绑定后,该账号的本机库只进自己的(或公司共享的)线上库,**不会**接到别人账号的库。
|
||
> 2. **用户不应**:手抄 `channel_id`、也不应先去「数据同步」里点「新建通道」才能用。
|
||
> 3. **通道对用户隐藏或自动生成**:公司开通 / 智能体启用时,平台**自动**准备本公司默认同步通道(Postgres 线上 A)并写到智能体/账号绑定上;控制台「数据同步」留给运维改 DSN,不作为开通必经 UI。
|
||
> 4. **共享库**:仅「公司共享数据库」可被多名成员同步到同一 `online_db_id`;个人库默认隔离。
|
||
> 5. 方向默认 **`local_to_remote`**(松离线 agent push);「双向」不是用户必选项。
|
||
> 6. SyncPage 列表须展示可复制 **通道 ID**(运维用;普通开通流程不依赖)。
|
||
|
||
| 编号 | 优先级 | 诉求 | 说明 | 负责 |
|
||
|------|--------|------|------|------|
|
||
| **Z12a** | **P0** | **换票带回绑定** | `POST /api/v1/auth/token`(及登录)响应含 `channel_id` / `online_db_id` / `database_name` / `sync_bound` | **智建已落实** |
|
||
| **Z12b** | **P0** | **智能体自查绑定** | `GET /api/v1/agents/me`:智能体 Bearer 可读自己的通道/线上库/状态 | **智建已落实** |
|
||
| **Z12c** | **P0** | **启用即自动绑通道** | 创建/启用智能体 → `EnsureAndStartSystemDefaultChannel`(`IsSystemDefault`,表白名单可空,**默认运行中**);写回 `channel_id`+`online_db_id`;DSN 见 `DBSync.DefaultRemoteDSN` | **智建已落实**(含默认启动 `cb76824`) |
|
||
| **Z12c-1** | **P0** | **账号级隔离** | Binding / push 校验:用户 JWT 只能写本人 `online_db_id` | **智建已有**(自助 Binding);**Z14c** 仍有 `_uN` 与 JWT 不一致问题 |
|
||
| **Z12c-2** | **P1** | **公司共享库** | Binding `shared=true`:同租户成员可访问该 `online_db_id`;仅管理员可设 | **智建已落实** |
|
||
| **Z12d** | **P1** | **SyncPage 展示通道 ID** | 运维可见 + copyable | **智建已落实** |
|
||
| **Z12e** | **P1** | **唯一默认通道** | 每公司至多 1 条 `is_system_default`;复用已有默认 | **智建已落实** + 宇恒「仅 1 条则写入 env」 |
|
||
| **Z12f** | **P1** | **开通 UX** | 启用智能体即可;DSN 用公司级默认 | **智建已落实** |
|
||
| **Z12g** | **P1** | **默认同步通道默认启动** | 创建 `Enabled=true`;重启对 `IsSystemDefault` 自动 Start;控制台保存后自动启动 | **智建已落实**(`cb76824`) |
|
||
|
||
**宇恒已做(2026-08-05)**
|
||
|
||
1. 当前生产通道 `691575c0-…` 已写入 env(过渡缓存;Z12c 落地后应由换票/唯一通道自动获得)。
|
||
2. `resolve_sync_channel_id()`:env → Binding → 租户仅 1 通道自动选用。
|
||
3. 不把「手建通道 + 手抄 ID」当作产品终态。
|
||
|
||
**验收**
|
||
|
||
1. 新公司:只「启用智能体」,**不**点新建通道,换票已带 `channel_id`,宇恒可 drain。
|
||
2. 账号 A 的本机库 push 不会出现在账号 B 的线上库;共享库仅在显式共享时可见。
|
||
3. 用户全程无需 F12、无需手填 `YXD_SYNC_CHANNEL_ID`。
|
||
|
||
### 5.10 【双方代码已接 Z13】绑定流程简化:绑定码 / 手机号(2026-08-05)
|
||
|
||
> **端到端体验(生产目标)**
|
||
> 1. 后台已绑定(或用户用绑定码/手机号完成绑定)→ 宇恒本机库选 **「同步」** → **直接可用**(自动通道、自动 Binding、agent drain),无第二套配置。
|
||
> 2. 绑定流程必须比「登记智能体 + 抄通道 ID + 填 env」简单。
|
||
> 3. **生产·宇信达联调账号**:`13531041944`(公司侧专用;**禁止**用超管号 `13531041945`)。
|
||
> 4. **试运行**:未接短信平台 → **关闭短信验证**;正式接入后再开(见下)。
|
||
|
||
**推荐交互**
|
||
|
||
```text
|
||
公司管理员(智建后台)
|
||
→ 生成「绑定码」→ 发给终端用户
|
||
|
||
宇恒 · AI建站智能体
|
||
→ 检测绑定状态
|
||
├─ 已绑定 → 静默换票;本机库选「同步」即可用
|
||
└─ 未绑定
|
||
├─ 宇恒已有手机,且智建同号有成员
|
||
│ → 弹「已有账号是否绑定?」→ 确认 / 取消
|
||
├─ 表单:【绑定码】或【手机号】
|
||
│ · 宇恒未绑手机 → 仍可直接填手机号
|
||
│ · 试运行:不校验短信,仅确认绑定
|
||
│ · 正式:同号可免短信/凭票;异号须短信
|
||
└─ 无成员 / 无码 → 提示联系管理员
|
||
→ 绑定成功 → 选「同步」→ drain,零通道配置
|
||
```
|
||
|
||
**试运行 vs 正式**
|
||
|
||
| 项 | 试运行(当前) | 正式(接短信后) |
|
||
|----|----------------|------------------|
|
||
| 配置 | `SMS.RequireForBind=false`(或 `Provider=off`) | `RequireForBind=true` |
|
||
| 探测 | `GET /api/v1/auth/bind/policy` → `trial_mode=true`,`sms_required_unless_attested=false` | `trial_mode=false` |
|
||
| 填手机号 | 可绑;**不校验短信** | 异号 / 宇恒未绑号须 `sms_code` |
|
||
| 同号免验 | 可不传 `sms_code`;确认即可 | 同号 attested 或走 `ticket-exchange` |
|
||
| 必保留 | **「已有账号是否绑定」确认**;`confirm=true` | 同左 |
|
||
|
||
**同号 / 验证规则**
|
||
|
||
| 场景 | 行为 |
|
||
|------|------|
|
||
| 试运行 / 短信未接入 | **不校验短信**;填号 + 确认即可 |
|
||
| 宇恒未绑手机 | **仍可直接输入手机号**;正式环境须短信 |
|
||
| 宇恒已绑且输入 = 已绑号 | 正式可免短信 / 凭票;命中成员仍弹确认 |
|
||
| 宇恒已绑且输入 ≠ 已绑号 | 正式**必须短信** |
|
||
| 命中公司成员 | **必须询问**「已有账号是否绑定」;不得静默 |
|
||
| 用户确认 | 绑定该账号 + 公司默认同步落点 |
|
||
| 用户取消 | 不绑;可改绑定码 / 换号 |
|
||
| 公司无此号 | 提示用绑定码 |
|
||
| 联调样例 | **`13531041944`**;勿用 `13531041945` |
|
||
|
||
| 编号 | 优先级 | 诉求 | 说明 | 负责 |
|
||
|------|--------|------|------|------|
|
||
| **Z13a** | **P0** | **后台生成绑定码** | `POST/GET/DELETE /api/v1/admin/bind-codes`;SyncPage「绑定码」区 | **智建已落实** |
|
||
| **Z13b** | **P0** | **绑定码兑换** | `POST /api/v1/auth/bind-code/redeem`(`host_key` + `code`) | **双方已接**(智建 API + 宇恒表单) |
|
||
| **Z13c** | **P0** | **手机号探测** | `POST /api/v1/auth/bind/phone-lookup`(不直接绑定) | **智建已落实** |
|
||
| **Z13c-1** | **P0** | **确认绑定** | `POST /api/v1/auth/bind/phone-confirm`(`confirm=true`) | **双方已接**(宇恒弹窗 + 本机 API) |
|
||
| **Z13c-2** | **P0** | **验证策略 / 凭票 / policy** | 试运行关短信;`GET /auth/bind/policy`;正式异号短信;凭票 `ticket-exchange` | **双方已接**(生产须对齐 Secret + 部署 gateway) |
|
||
| **Z13d** | **P0** | **未绑定检测与提示** | `sync_bound=false` → lookup / 表单 | **宇恒已接** `ensure_sync_bound_interactive` |
|
||
| **Z13e** | **P0** | **选同步即用** | 库选「同步」→ Binding + drain;零手填通道 | **宇恒已接** `POST /database/sync/mode` |
|
||
| **Z13f** | **P1** | **安全** | 试运行关短信;正式强制异号短信;审计 | **智建开关已落实** |
|
||
|
||
**验收**
|
||
|
||
1. 绑定码 → 填码 → 绑定成功 → 库选「同步」→ 数据进公司线上库。
|
||
2. 同号 `13531041944` → 弹「已有账号是否绑定」;确认绑、取消不绑;禁用 `13531041945`。
|
||
3. 宇恒未绑手机 → 仍可直接填号;试运行免短信,正式须短信。
|
||
4. 试运行:无 `sms_code` 也可 confirm;`GET /auth/bind/policy` → `trial_mode=true`。
|
||
5. 未绑定用户只见绑定码/手机表单,**不见** DSN/通道 ID。
|
||
6. 未确认 / 他人绑定码,不能绑进别的公司或别人账号。
|
||
7. 正式且启凭票:同号走 `ticket-exchange`,不再依赖明文 `attested_same_phone`。
|
||
|
||
### 5.11 【新增 Z14 · 2026-08-05 晚】表级指纹对账(慢心跳)+ 联调踩坑
|
||
|
||
> **诉求**
|
||
> 仅靠 agent「推送心跳」(pending/pushed)发现不了「本机 96 行、线上 0 行」或「行数相同但内容不同」。
|
||
> 需要**慢周期表级指纹**:`row_count` +(可选)`content_hash`;不一致则 ensure + 对该表 full-push。
|
||
|
||
| 编号 | 优先级 | 诉求 | 现状 |
|
||
|------|--------|------|------|
|
||
| **Z14a** | **P0** | 宇恒本机指纹 + 对比线上 row_count + 自动修复 | **宇恒已接**:`yxd/app_fastapi/sync_fingerprint.py`;agent 默认 TTL **600s**;`POST /database/sync/fingerprint` |
|
||
| **Z14b** | **P1** | 智建返回 `content_hash` | **智建已合入待生产 pull**:`POST /api/v1/agent/sync/channels/{id}/schema/fingerprint` |
|
||
| **Z10d** | **P0** | ensure 对已存在表补列 | **智建已合入待生产 pull**;否则 `填土高度` 等缺 `id` 会 503 |
|
||
| **Z14c** | **P1** | ticket/`agents/me` 的 `online_db_id` 与 gateway ACL 一致 | **智建已落实**:智能体 JWT 校验挂载落点(`agentOwnsOnlineDB`);Binding 仍按人类 user_id |
|
||
|
||
**契约摘要(Z14b)**
|
||
|
||
```http
|
||
POST /api/v1/agent/sync/channels/{id}/schema/fingerprint
|
||
{ "online_db_id": "..." }
|
||
→ result.tables[]: { name, pk_column, row_count, content_hash }
|
||
```
|
||
|
||
哈希约定(双方对齐):`sha256( table+"|"+pk+"|" + Σ json(row, sort_keys=True)+"\n" )` 取前 32 hex。
|
||
|
||
**分工**
|
||
|
||
| 方 | 做什么 |
|
||
|----|--------|
|
||
| 宇恒 | 本机算指纹;优先调 fingerprint API;404 则回退 `schema.row_count`;不一致自动 ensure+`enqueue_full_push`;heartbeat 写入 `fingerprint_*` |
|
||
| 智建 | 部署 fingerprint + Z10d;**Z14c 已修**(智能体挂载 online) |
|
||
|
||
**联调踩坑(2026-08-05 · 宇信达生产)**
|
||
|
||
1. **数据量对不上**:历史行不会因开通同步自动上云 → 须 full-push;指纹对账可检出差表。
|
||
2. **`填土高度(6标一工区)`**:本机列 `_row_id`,线上曾无 `id`;push 默认 pk=`id` 并注入该列 → `has no column named id`;整队曾因 `mark_error` 未改 status 卡住(宇恒已修)。修复路径:删表重建 / 部署 Z10d 后 ensure 补 `id` 再推。
|
||
3. **`online_db_id=_uN` 403(已修)**:智能体 JWT 的 `user_id=agent_id`,Binding 记在人类成员上;现 `authorizeUserOnlineDB` 额外放行智能体挂载的 `OnlineDBID`/`ChannelID`。
|
||
|
||
**验收**
|
||
|
||
1. 本机改行数后 ≤TTL 内指纹检出 mismatch,并排队 full-push。
|
||
2. 未部署 fingerprint 时:仅 count 对账仍可用;`zhijian_fingerprint_api=false`。
|
||
3. 部署后:同 count 不同内容 → `hash_mismatch` → 修复。
|
||
4. Z10d 部署后:缺列 ensure 成功,`填土高度` 可推满。
|
||
|
||
---
|
||
|
||
## 6. 联系与附件
|
||
|
||
- **待改清单(优先看)**:§0.2;**双方配合**见 **§0.3**
|
||
- 方案:`松离线-dbsync方案-最终版.md`(含 2026-08-01 联调建议落地记录)
|
||
- 宇恒使用说明:`宇恒-松离线数据同步使用文档.md`(含 Z10 schema/ensure、Z13 绑定)
|
||
- 开通说明:`docs/数据同步-开通说明.md`
|
||
- 绑定策略探测:`GET /api/v1/auth/bind/policy`(`trial_mode` / `require_for_bind`)
|
||
- 本意见如与冻结方案冲突,**以冻结方案为准**;§5.1–5.11 为产品增量与复测记录,不推翻 H1–H6 默认无感约束。
|