# 智建修改意见(松离线 + UUID 主键一致性) > 配套文档:同目录 `database_fastapi修改意见.md`(宇恒客户端写网关 / 双轨写) > 目标:离线写入不破坏联网后的**数据一致 + 主键一致**;对账可兜底。 > 对照能力:本仓库 `docs/数据同步-中间件.md`(dbsync outbox / ingest / reconcile / 冲突队列)。 --- ## 一、结论摘要 | 项 | 结论 | |----|------| | 对账引擎 | **不必从零重做**;dbsync 已有按主键 upsert、ingest、reconcile、冲突队列 | | 真正要改 | **表主键约定(UUID)** + **写入口语义(认客户端 id / upsert)** + **与宇恒绑定对齐** | | 推荐模型 | **松 + UUID**:离线本地先按最终主键落库;联网同主键幂等 upsert 到线上 | | 禁止 | 同步键依赖两端各自 `INTEGER AUTOINCREMENT` | 宇恒侧(客户端)负责:补 UUID、松离线本地落库 + pending、回放幂等。 智建侧负责:线上按 UUID upsert、通道/对账/冲突、表结构与发号规则。 --- ## 二、与宇恒文档的分工 | 端 | 文档 | 职责 | |----|------|------| | 宇恒客户端 | `database_fastapi修改意见.md` | `apply_write`、pending、binding、`online_primary` | | 智建平台 | **本文** | 线上主库语义、dbsync、表约定、对账 API 对业务可用 | 接法二选一(长期建议 ①): | 接法 | 含义 | 智建改动量 | |------|------|------------| | **① 走 dbsync** | 宇恒本地 = B;松离线写 B(带 UUID);通道 B↔A | 表结构 + 配置为主,代码改少 | | **② HTTP 双写** | 宇恒直打 `/database/permanent/...` | 线上 insert **必须**变 upsert,且强制客户端 `id` | --- ## 三、表结构约定(A / B 两端) 1. 参与同步的业务表主键为 **`id TEXT`(UUID 字符串)**,不用自增整数当同步键。 2. 建议列:`updated_at` / `version`(与 dbsync 版本跳过、冲突策略对齐)。 3. 通道 `pk_columns`:非默认 `id` 时显式配置。 4. 存量自增表:迁移生成 UUID,或暂不纳入同步白名单,仅新表用 UUID。 5. dbsync 触发器已 `CAST(pk AS TEXT)`,字符串 UUID **原生兼容**;改的是建表习惯,不是中间件内核。 --- ## 四、接法 ①:走 dbsync(推荐) ### 已有、可直接用 - 本地变更 → `_ajz_sync_outbox` → worker 推线上 - `POST .../ingest`:按主键 upsert(示例已是 `"id": "c-001"`) - `POST .../reconcile`:主键集合补缺 - 冲突队列 + LWW / queue 策略 - 双向通道约每分钟自动对账 ### 建议补强 | 优先级 | 项 | 说明 | |--------|----|------| | P0 | 文档/控制台规范 | 写死:同步表白名单必须 UUID 主键;松离线以客户端已带 `id` 为准 | | P0 | 写入口拒无 id | ingest / 业务写:无 `id` → 400,或服务端生成 UUID **并回写响应**(松离线仍优先客户端生成) | | P1 | 库绑定对齐 | 宇恒 `online_db_id` 与智建租户库 / 通道 remote 可查询、可登记;避免仅本地 hash 对不上真实库 | | P1 | 业务可调对账 | 现 sync API 偏管理员;提供租户内用户/agent 代理调用 ingest/reconcile,或网关代调 | | P2 | 同 PK 内容对账 | 当前 reconcile 偏主键集合;可选 version/hash 不一致 → 冲突队列 | ### 松离线数据流(①) ```text 离线:宇恒本地 B 按最终 UUID 落库(可标未同步)+ 可选 pending 联网:触发器/回放 → 同 PK upsert → A 兜底:reconcile 补主键集合差;冲突进 conflicts ``` --- ## 五、接法 ②:HTTP 双写(短期兼容宇恒网关) 宇恒当前:`online_primary` 下同 body 先写线上再写本地,**不会**把线上自增 `inserted_id` 回填本地。 智建线上 permanent 写接口必须: 1. **`insert` = upsert by `id`**(有则更新,无则插入);请求**必须带 `id`**。 2. 响应带回 **`id` + `version`(可选)**,便于客户端校验。 3. `update` / `delete` 统一按同一 `id`;禁止「无 id 靠自增」。 4. 建表默认主键模板改为 UUID,不再默认 `INTEGER AUTOINCREMENT`。 5. (可选)按 `online_db_id` + 表暴露 reconcile,或复用 dbsync reconcile。 未改以上语义时,即使客户端用 UUID,线上若仍自增,双轨主键仍会漂。 --- ## 六、落地顺序(智建) 1. **规范 + 迁移**:同步表白名单,主键 UUID。 2. **选定接法**:优先 ①;若短期 ②,先改 permanent 写为 upsert。 3. **写入口强制带 `id`**(缺则 400 或服务端生成并返回)。 4. **绑定**:租户/用户 ↔ 线上库 ID 可查,与宇恒 `cache/db_bindings` 对齐。 5. **对账**:业务可读 API 或网关代调;冲突走现有 conflicts。 6. **文档**:离线写入 = 最终主键;联网只做同 PK 幂等,不重新发号。 --- ## 七、验收标准 - 离线插入一行(客户端 UUID)→ 联网后线上与本地 **同一 `id`**,无重复行。 - 同一 UUID 重复提交 / 回放 → **幂等**,不多行。 - 断网窗口结束回放后,主键集合对账无持续缺口(或缺口可一键 reconcile 清掉)。 - 并发改同 PK → 进冲突队列或按约定 LWW,不静默改主键。 - 未纳入白名单的自增旧表:不同步或明确排除,避免误配通道。 --- ## 八、勿做事项 - 不要用「两端自增 + 事后对账」充当主路径。 - 不要只靠触发器异步 outbox 充当客户端主写路径却仍让 HTTP insert 自增发号(两套语义打架)。 - 不要在回放失败时换新主键重试。 - 不要让管理员专用 sync API 成为唯一对账入口却要求终端用户自愈(需代理或网关)。 --- ## 九、相关路径 | 项 | 路径 | |----|------| | 本文 | `E:\project\ai建站\智建修改意见.md` | | 宇恒写网关意见 | `E:\project\ai建站\database_fastapi修改意见.md` | | dbsync 说明 | `E:\project\ai建站\docs\数据同步-中间件.md` | | dbsync 实现 | `platform/internal/dbsync/` | | 宇恒网关实现 | `yuhengyihao_client/yxd/app_fastapi/db_write_gateway.py` 等 | --- ## 十一、模块导入默认能力(方案 A) > **正式条目已写入联调意见,请以此为准:** > `E:\project\ai建站\联调后修改意见-宇恒松离线.md` → **§5.6 Z9(方案 A)** > (§0 结论表已挂「Z9 模块导入默认能力」) 摘要:生成/发布时业务 resource **默认**带 `import`(及 `export`),列表 `actions` 对齐;publish/merge 可兜底。绑定权限解决「谁能调」,方案 A 解决「表是否允许导入」。 --- ## 十二、一句话(同步 + 导入) 松离线要对齐的是 **UUID 主键 + upsert**;模块灌数要对齐的是 **绑定权限 + 蓝图默认开放 import(方案 A / Z9)**——细则见联调意见 §5.6。