Files
ai_site/宇恒-松离线数据同步使用文档.md
whm b04b180d30 feat: harden loose-offline sync for user JWT, schema, and console ops
Enable Binding-scoped agent push/pull, empty-table schema ensure, SyncPage inspect/drop-table, default module import, and agent-bound publish docs from the 宇恒联调意见.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-05 09:47:35 +08:00

14 KiB
Raw Blame History

宇恒 × 智建 · 松离线数据同步使用文档

面向:宇恒一号客户端对接同学
平台侧仓库智建ai建站
依据:松离线-dbsync方案-最终版.md(冻结)
本地测试基址示例:http://127.0.0.1:8180;生产示例:https://aisite.yuxindazhineng.com


1. 你要做什么(一句话)

未开通用户零改动。仅当显式 YXD_SYNC_MODE=local_dbsync 且表在白名单时:业务写进本机正式库 BUUID→ 写 outbox → 本机 agent 调智建 push API → 落到线上库 A。agent 停了也不能挡保存

业务保存 ──► 本机 B最终 UUID立刻可见
              │
              ▼
           outbox积压可接受
              │
              ▼ 本机 agent可选装
         智建 POST .../push  ──► 线上 A

2. 硬约束(违反即不合入)

# 要求
H1 未开通:保存 / 自增表 / 插件与现网 行为 diff = 0
H2 禁止自动升为 local_dbsync(仅显式配置)
H3 agent 停运 / 推送失败 → 保存仍成功;心跳只驱动 UI
H4 默认建表仍自增;仅「同步表」用 UUID TEXT PK
H5 online_primary 客户禁止静默关双写
H6 未开通用户:零同步文案(无强制状态条、无「已自动合并」)

开通判定(写路径):

mode == local_dbsync  AND  表白名单缓存命中该表
→ 走松离线旁路
否则 → 原写路径

同表互斥:某表不可同时 HTTP 双写 + dbsync 白名单。


3. 模式三分(环境变量)

YXD_SYNC_MODE 含义 说明
local_only 仅本地 默认之一
online_primary HTTP 双写 / 离线 pending 旧客保留;仅配了 YXD_ONLINE_API_BASE 且未写 MODE 时可兼容升为此模式
local_dbsync 松离线 + 白名单 + agent 必须显式写出,永不因「配了线上地址」自动升

建议本地测试:

# 默认无感:不要设 local_dbsync或显式
YXD_SYNC_MODE=local_only

开通增值:

YXD_SYNC_MODE=local_dbsync
YXD_SYNC_AGENT=1
YXD_ONLINE_API_BASE=http://127.0.0.1:8180
YXD_SYNC_CHANNEL_ID=<智建控制台通道 ID>
YXD_SYNC_ACCESS_TOKEN=<Bearer管理员/智能体「数据同步」,或登录用户 JWT自助须先 Binding>
# 可选
YXD_SYNC_WHITELIST_TTL_SEC=600
YXD_SYNC_AGENT_INTERVAL_SEC=5
YXD_SYNC_WHITELIST_CACHE=cache/db_sync/whitelist.json

调试兜底表白名单(通道拉取失败前):

YXD_SYNC_WHITELIST=orders,order_items
# 或
YXD_SYNC_DBSYNC_TABLES=orders,order_items

4. 智建侧前置(公司管理员)

  1. 登录智建控制台 → 数据同步 → 新建通道。
  2. 生产remote.driver=postgresDSN 例:postgres://user:pass@host:5432/db?sslmode=disable(须可达)。
  3. 表白名单须 UUID TEXT/UUID PK + 外键闭包
  4. 方向推荐 本地 → 线上;冲突策略推荐 源端覆盖lww_source
  5. 保存通过校验后记下 通道 ID
  6. 二选一鉴权
    • 管理路径:发带「数据同步」的智能体 Token / 管理员 JWT
    • 用户自助:终端用登录用户 JWT先登记 Bindingpush 带本人 online_db_id

单服务器:一条通道 + 一个 YXD_SYNC_CHANNEL_ID 即可。
多服务器:每台线上 Postgres 各建一条通道;按本机库选择对应 channel_id(建议登记 Binding

Binding多库 / 用户自助时建议登记;登录 JWT 即可,不必管理员权):

POST /api/v1/admin/sync/bindings
Authorization: Bearer <token>
Content-Type: application/json

{
  "local_database_id": "用户本机库 ID",
  "online_db_id": "线上库 ID",
  "channel_id": "<通道ID>",
  "database_name": "可选,本机库可读名(控制台优先展示)",
  "display_name": "可选,线上库可读名(控制台优先展示)",
  "note": "可选"
}

不传可读名时控制台仍显示 id宇恒有命名库名时建议一并写入便于 SyncPage「本地库名 ↔ 线上库名」。

GET /api/v1/admin/sync/bindings?local_database_id=...

5. 宇恒侧实现清单

5.1 写网关旁路M1可无 agent

insert / update / delete / 写 SQL

  1. 读模式与白名单。
  2. 未命中:原路径,响应形状与现网一致(可多字段,不可少成功语义)。
  3. 命中
    • insert缺 id 或非法 id → 自动补小写带连字符 UUID
    • 写入本机正式库 B
    • append outbox失败只打日志不挡保存
    • sync_statuspending / local_only_table 等仅开通用户可见。

文案:需 agent 才上云(不得暗示已上云)。

5.2 Outbox

建议路径(等价即可):

cache/db_sync/outbox/<local_database_id>/pending.jsonl

每条建议字段:

字段 说明
id outbox 记录 UUID
seq 单调版本(如 time.time_ns()),作 push 的 version
op insert / update / update_by_id / delete
table_name 表名
row_pk 行主键 UUID
payload data / update_data / where_*
status pending

规则:按 seq 保序消费;失败标记 error 并停该库后续,恢复后重试;禁止失败后换新 UUID。

5.3 本机 AgentM2

启用:YXD_SYNC_AGENT=1mode=local_dbsync。默认可不启。

循环建议:

  1. TTL拉白名单 → 写本地缓存。
  2. list_pending → 逐条 push → 成功 mark_done / 失败 mark_error 并 break。
  3. 写心跳文件(供 /sync/status 展示 agent.running / last_beat)。

拉白名单

GET /api/v1/agent/sync/channels/{channel_id}/whitelist
Authorization: Bearer <token>

响应要点:tablespk_columnsconflict_policy

推单条

POST /api/v1/agent/sync/channels/{channel_id}/push
Authorization: Bearer <token>
Content-Type: application/json

{
  "table": "orders",
  "op": "insert",
  "row_pk": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "row": {
    "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
    "title": "demo"
  },
  "version": 1710000000000000000,
  "client_outbox_id": "outbox-record-uuid",
  "online_db_id": "用户自助必填(本人 Binding管理员/智能体可选"
}
op 平台行为
insert / update / update_by_id upsert 到 remote A
delete 按 pk 删 A

幂等:同 version 已落地 → 跳过成功(skipped=true)。
LWW目标更新 → 按通道策略;源端覆盖会记超管审计(公司管理员不可见)。
重复 push双 drain正确性由 version 保证;客户端可用 drain 锁减流量,非平台必做。
remote 暂不可达:503 + retryable:true

下行(仅线上 / 灌库 / 补齐)

POST /api/v1/agent/sync/channels/{channel_id}/bootstrap
Authorization: Bearer <token>
Content-Type: application/json

{
  "table": "orders",
  "after_pk": "",
  "limit": 200,
  "online_db_id": "用户自助必填"
}

POST .../pullmode 取:

mode 作用
bootstrap 分页全量(循环至 has_more=false
pks 只取 A 主键,本机 diff
rows row_pks 取行

客户端把 result.items upsert 进本机 B 时务必 WithApplying(或等价),避免回声进 outbox。
空表:result.columns 仍会返回;本机应用其 CREATE TABLE IF NOT EXISTS(见下「表结构同步」)。

成功响应示例:

{
  "success": true,
  "result": {
    "ok": true,
    "applied": false,
    "skipped": true,
    "conflict": false,
    "applied_version": 1710000000000000000,
    "message": "already applied (same version)",
    "client_outbox_id": "outbox-record-uuid"
  }
}

批量:POST .../push/batchbody { "items": [ ... ] },最多 100保序遇错即停。

也可设完整 URLYXD_SYNC_PUSH_URL=...(覆盖默认拼装)。

表结构同步(空表也要建)

仅靠 outbox 行 push 时,空表不会出现在线上(无变更事件)。同步周期应额外:

  1. 本机 → 线上:对本机每张业务表(含 0 行)调用 ensure
POST /api/v1/agent/sync/channels/{channel_id}/schema/ensure
Authorization: Bearer <token>
Content-Type: application/json

{
  "online_db_id": "用户自助必填",
  "tables": [
    {
      "table": "accounts",
      "pk_column": "id",
      "columns": ["id", "username", "password", "email", "nickname", "status", "created_at", "updated_at"]
    }
  ]
}

平台对每张表 CREATE TABLE IF NOT EXISTS(列一律 TEXT + 指定 PK已存在则跳过。

  1. 线上 → 本机:先拉结构,本机缺表则建空表,再 bootstrap 行:
POST /api/v1/agent/sync/channels/{channel_id}/schema
Authorization: Bearer <token>
Content-Type: application/json

{ "online_db_id": "用户自助必填" }

响应 result.tables[].columns + row_count;对本地缺失表执行 CREATE TABLE IF NOT EXISTS(建议 TEXT + PK=pk_column)。
pull/bootstrapresult.columns 同样可用于单表建空表。

5.4 状态接口(建议)

GET /database/sync/status(或你们现有等价路由)对开通用户返回:

  • mode
  • local_dbsync.whitelist_tables / outbox_pending
  • agent.enabled_flag / running / last_error
  • note:未开通勿强塞同步 UI

6. UUID 约定

  • 标准形式:小写 + 连字符 8-4-4-4-12
  • 入库前 normalize;无连字符 32 hex 可规范化
  • 默认建表模板不要改成 UUID只给同步表白名单表用

SQLite 同步表示例:

CREATE TABLE IF NOT EXISTS orders (
  id TEXT PRIMARY KEY NOT NULL,
  title TEXT,
  updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);

7. 从旧双写迁入O7摘要

  1. 该表先停 online_primary 双写。
  2. 观察无残留。
  3. 再进白名单并切 local_dbsync
  4. 装 agent看 outbox 清空。
  5. 禁止静默全员迁移;旧客默认不变。

完整步骤见智建仓:docs/数据同步-迁移手册.md


8. 联调检查表

未开通回归(必过)

  • 不设 local_dbsync:插入自增表与现网一致
  • 无同步强制文案、无强制装 agent
  • 保存不因同步模块报错失败

开通 + 无 agent

  • 白名单表本地立刻可见UUID
  • outbox 增长;响应提示需 agent 才上云
  • 非白名单表仍走原路径

开通 + agent

  • 拉白名单成功并缓存
  • push 后线上 A 出现同一 UUID(不双行)
  • 重复 push 同 version 幂等
  • agent 停:仍可本地保存;恢复后按序追上
  • delete 保序,不换新 UUID

冲突

  • 终端冲突处理台
  • 公司管理员打 conflicts API → 403
  • 超管可在平台工作台看 LWW 日志 / 回滚

9. 智建 API 速查

基址:{YXD_ONLINE_API_BASE},鉴权:Authorization: Bearer ...

  • 管理路径:需「数据同步」
  • 用户自助:登录用户 JWT + 本人 Bindingpush 须带 online_db_id
  • LWW 仅超管
方法 路径 谁用
GET /api/v1/agent/sync/channels/{id}/whitelist agent / 用户 JWT
POST /api/v1/agent/sync/channels/{id}/push agent / 用户 JWT
POST /api/v1/agent/sync/channels/{id}/push/batch agent / 用户 JWT
POST /api/v1/agent/sync/channels/{id}/pull agent / 用户 JWT
POST /api/v1/agent/sync/channels/{id}/bootstrap agent / 用户 JWT
POST /api/v1/agent/sync/channels/{id}/schema agent / 用户 JWT拉线上表结构含空表
POST /api/v1/agent/sync/channels/{id}/schema/ensure agent / 用户 JWT本机空表建到线上
POST /api/v1/agent/sync/channels/{id}/pull agent / 用户 JWT下行
POST /api/v1/agent/sync/channels/{id}/bootstrap 全量灌库别名
GET/POST /api/v1/admin/sync/bindings 管理员或用户自助
GET /api/v1/platform/dbsync/lww-overrides 仅超管
POST /api/v1/platform/dbsync/lww-overrides/{id}/rollback 仅超管

OpenAPIGET /api/v1/meta/openapi.yaml


10. 本地联调最小步骤

  1. 智建本机起栈(.env 已是本地:AIJZ_PUBLIC_BASE_URL=http://127.0.0.1:8180)。
  2. 控制台建通道:remote 优先本机 Postgres(与平台同实例或独立库均可);拷贝通道 ID。
  3. 拿 token管理员登录或智能体 client_credentials
  4. 宇恒设:
YXD_SYNC_MODE=local_dbsync
YXD_SYNC_AGENT=1
YXD_ONLINE_API_BASE=http://127.0.0.1:8180
YXD_SYNC_CHANNEL_ID=...
YXD_SYNC_ACCESS_TOKEN=...
  1. 对白名单表插一行 → 看本机 B + outbox → agent 推上 A → 比对 UUID。
  2. 多服务器演练:再建第二条通道指向另一 PostgresCHANNEL_ID 验证不串库。

11. 相关文档(智建仓)

文档 内容
松离线-dbsync方案-最终版.md 双方冻结方案
docs/同步表约定.md UUID / 接口约定
docs/数据同步-开通说明.md 管理员开通
docs/数据同步-迁移手册.md 旧客迁移
docs/数据同步-中间件.md 平台能力与 API
docs/发版说明-数据同步.md 默认无感发版说明

分工提醒智建只提供平台通道、校验、push/whitelist/Binding/LWW写网关、outbox、agent、未开通回归由宇恒在己方仓库实现,勿改智建仓业务代码。