Files
ai_site/宇恒-松离线数据同步使用文档.md
whm 2a7da53769 docs: align sync catalog and checklists after Z14c
Document schema/fingerprint APIs, quote SMS Provider off, and refresh the联调意见 commit trail.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-05 18:33:13 +08:00

640 lines
22 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 宇恒 × 智建 · 松离线数据同步使用文档
> 面向:**宇恒一号客户端**对接同学
> 平台侧仓库智建ai建站
> 依据:`松离线-dbsync方案-最终版.md`(冻结)、`联调后修改意见-宇恒松离线.md`Z12/Z13 产品路径)
> 本地测试基址示例:`http://127.0.0.1:8180`;生产示例:`https://aisite.yuxindazhineng.com`
> **生产·宇信达联调登录**:手机号 `13531041944`(公司侧专用;勿用超管号 `13531041945` / 演示号 `13800000001`
---
## 1. 你要做什么(一句话)
未开通用户**零改动**。仅当显式 `YXD_SYNC_MODE=local_dbsync`(或库级选「同步」)且走白名单/Binding 时:业务写进本机正式库 BUUID→ 写 outbox → **本机 agent** 调智建 push API → 落到线上库 A。agent 停了也**不能挡保存**。
```text
业务保存 ──► 本机 B最终 UUID立刻可见
outbox积压可接受
▼ 本机 agent可选装
智建 POST .../push ──► 线上 A
```
**生产开通推荐Z12/Z13**:不要引导用户手抄 `channel_id`
后台启用智能体 / 发绑定码 / 同号确认 → 换票带 `sync_bound` + 落点 → 本机库选「同步」即可用。
手建通道 + `YXD_SYNC_CHANNEL_ID` 仅作**运维高级 / 联调过渡**。
---
## 2. 硬约束(违反即不合入)
| # | 要求 |
|---|------|
| H1 | 未开通:保存 / 自增表 / 插件与现网 **行为 diff = 0** |
| H2 | **禁止**自动升为 `local_dbsync`(仅显式配置或用户库级选「同步」) |
| H3 | agent 停运 / 推送失败 → **保存仍成功**;心跳只驱动 UI |
| H4 | 默认建表仍自增;仅「同步表」用 UUID TEXT PK |
| H5 | 旧 `online_primary` 客户禁止静默关双写 |
| H6 | 未开通用户:**零同步文案**(无强制状态条、无「已自动合并」) |
开通判定(写路径):
```text
mode == local_dbsync AND (表白名单缓存命中 或 Binding 整库策略)
→ 走松离线旁路
否则 → 原写路径
```
同表互斥:某表不可同时 HTTP 双写 + dbsync 白名单。
---
## 3. 模式三分(环境变量)
| `YXD_SYNC_MODE` | 含义 | 说明 |
|-----------------|------|------|
| `local_only` | 仅本地 | 默认之一 |
| `online_primary` | HTTP 双写 / 离线 pending | **旧客保留**;仅配了 `YXD_ONLINE_API_BASE` 且未写 MODE 时可兼容升为此模式 |
| `local_dbsync` | 松离线 + 白名单 + agent | **必须显式写出**,永不因「配了线上地址」自动升 |
建议本地测试:
```bash
# 默认无感:不要设 local_dbsync或显式
YXD_SYNC_MODE=local_only
```
开通增值(**过渡 / 运维**仍可用 env生产优先换票落点
```bash
YXD_SYNC_MODE=local_dbsync
YXD_SYNC_AGENT=1
YXD_ONLINE_API_BASE=http://127.0.0.1:8180
# 过渡:可手填;生产应优先用换票/agents/me 的 channel_id
YXD_SYNC_CHANNEL_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_LOGIN_PHONE=13531041944
```
调试兜底表白名单(通道拉取失败前):
```bash
YXD_SYNC_WHITELIST=orders,order_items
# 或
YXD_SYNC_DBSYNC_TABLES=orders,order_items
```
---
## 3.1 生产绑定与落点Z12 / Z13 · 必读)
### 产品约定
1. **一个登录账号 / 一个智能体 ↔ 本公司同步落点**;个人库默认隔离,禁止 A 数据进 B 库。
2. **禁止**把「手抄通道 ID / 先去控制台新建通道」当作普通用户开通主路径。
3. 公司默认同步通道由智建在**启用智能体**时自动创建(`is_system_default`);控制台「数据同步」留给运维改 DSN。
4. 库选「同步」→ 自动 Binding + drain宇恒 Z13e全程零手填 DSN/通道。
### 换票带回绑定Z12a
`POST /api/v1/auth/token`client_credentials或登录响应可含
| 字段 | 说明 |
|------|------|
| `channel_id` | 已绑则非空 |
| `online_db_id` | 已绑则非空 |
| `database_name` | 可读落库名 |
| `sync_bound` | `true` = 已绑落点;`false` = 须走绑定流程 |
通道解析顺序建议:`env 过渡缓存` → 换票/`agents/me` → Binding → 租户仅 1 条通道自动选用。
### 智能体自查Z12b
```http
GET /api/v1/agents/me
Authorization: Bearer < Token>
```
返回 `channel_id` / `online_db_id` / `database_name` / `sync_bound` / `status`(无需「管理智能体」权限)。
### 绑定码Z13a/b
管理员(需「数据同步」):
```http
POST /api/v1/admin/bind-codes
Authorization: Bearer < JWT>
Content-Type: application/json
{ "max_uses": 1, "expires_hours": 168, "note": "" }
```
```http
GET /api/v1/admin/bind-codes
DELETE /api/v1/admin/bind-codes/{code}
```
终端兑换(公开):
```http
POST /api/v1/auth/bind-code/redeem
Content-Type: application/json
{ "code": "A1B2C3D4", "host_key": "<宿 key>", "name": "" }
```
成功 → 智能体挂到该公司 + 默认同步落点,`sync_bound=true`
### 同号探测 / 确认Z13c / Z13c-1
**禁止静默绑定**。有手机号时必须先 lookup命中后**弹窗确认**再 confirm。
**推荐(仅宇恒 · 安全凭票免登录)**:用户已登录宇恒时,由**宇恒后端**用共享 Secret 签发短时凭票,客户端调:
```http
POST /api/v1/auth/yuheng/ticket-exchange
Content-Type: application/json
{
"ticket": "YHT1.<payload>.<hmac>",
"host_key": "<宿 key>",
"local_database_id": ""
}
```
成功直接返回智能体 `access_token` + `sync_bound` / `channel_id` / `online_db_id`**无需**智建账号密码,也**不要**再传 `attested_same_phone`)。
凭票 payloadJSONHMAC-SHA256前缀 `YHT1`)字段:
| 字段 | 说明 |
|------|------|
| `iss` | 固定 `yuheng` |
| `aud` | 固定 `aijianzhan` |
| `phone` | 宇恒当前已绑手机(须已是智建公司成员) |
| `host_key` | 与请求一致 |
| `exp` | Unix 秒;建议 60120s智建拒 >now+10m |
| `jti` | 一次性随机串(防重放) |
| `scope` | `sync_bind` |
| `name` / `local_database_id` | 可选 |
配置(智建 `platform.yaml``Agent.YuhengTicket``Enabled` + 与宇恒共享的 `Secret`。生产必换 Secret**其它客户端不得使用**。
启动前可拉策略(公开,无需登录):
```http
GET /api/v1/auth/bind/policy
```
| 字段 | 说明 |
|------|------|
| `trial_mode` | `true` = 试运行(不强制绑定短信) |
| `require_for_bind` | 是否强制短信(与 `SMS.RequireForBind` + Provider≠off 一致) |
| `yuheng_ticket_enabled` | 是否启用凭票换票 |
| `message` | 给人看的提示文案 |
---
仍可用手机号 API无凭票或试运行
```http
POST /api/v1/auth/bind/phone-lookup
Content-Type: application/json
{ "phone": "13531041944" }
```
响应要点:`exists``tenant_name``masked_name``need_confirm``sms_required_unless_attested``message`**不**执行绑定)。
```http
POST /api/v1/auth/bind/phone-confirm
Content-Type: application/json
{
"phone": "13531041944",
"host_key": "< host_key>",
"confirm": true,
"local_database_id": " ID",
"attested_same_phone": true,
"sms_code": ""
}
```
| 字段 | 说明 |
|------|------|
| `attested_same_phone` | **试运行**可传;**正式且已启凭票**时禁用,须改用 ticket-exchange |
| `sms_code` | **试运行**可不传;正式异号必填 |
> **试运行**(当前):`SMS.RequireForBind=false` 或 `Provider=off` → 绑定不校验短信,仍须 `confirm=true`。正式:`RequireForBind: true` 且 Provider 非 off。
| 场景 | 行为 |
|------|------|
| 同号命中公司成员 | **必须询问**「已有账号是否绑定」;确认才 `phone-confirm` / 或凭票换票 |
| 用户取消 | 不绑定;可改走绑定码 / 换号 |
| 无此成员 | 提示用绑定码;**勿**用超管号 `13531041945` 测 |
| 生产联调样例 | 智建与宇恒均为 **`13531041944`** → 凭票或弹确认 |
### 未绑定时客户端流程Z13d · 宇恒已接 2026-08-05
> 宇恒落地:宿主 `ensure_sync_bound_interactive`;本机 `/database/sync/bind/*`;选「同步」走 `POST /database/sync/mode`Z13e。生产仍须智建部署含 `ticket-exchange` / `bind/policy` 的 gateway并对齐凭票 Secret。
```text
换票 / agents/me → sync_bound=false
├─ 有手机号 → phone-lookup
│ ├─ need_confirm → 【弹窗】确认?→ phone-confirm / 取消
│ └─ 未命中 → 绑定码或换号表单
└─ 无手机号 → 绑定码表单
绑定成功 → 库选「同步」→ Binding + drain零通道配置
```
---
## 4. 智建侧前置(公司管理员)
### 生产推荐Z12/Z13
1. 公司级配置默认同步 DSN`DBSync.DefaultRemoteDSN`,生产 Postgres
2. **启用智能体**(勿要求用户先「新建通道」)→ 平台自动建默认同步通道并写回智能体落点。
3. (可选)生成**绑定码**发给终端;或引导用户用公司成员手机号做同号确认(须弹窗)。
4. 运维需要时再在「数据同步」改 remote DSN / 查看通道 ID可复制
### 运维高级 / 联调过渡(手建通道)
1. 登录智建控制台 → **数据同步** → 新建通道。
2. **生产**`remote.driver=postgres`DSN 例:`postgres://user:pass@host:5432/db?sslmode=disable`(须可达)。
3. 表白名单可空(整库 Binding 策略);若填表须 **UUID TEXT/UUID PK** + **外键闭包**
4. 方向推荐 **本地 → 线上**;冲突策略推荐 **源端覆盖lww_source**
5. 保存通过校验后记下 **通道 ID**(过渡写入 `YXD_SYNC_CHANNEL_ID`)。
6. **二选一鉴权**
- 管理路径:发带「数据同步」的智能体 Token / 管理员 JWT
- **用户自助**:终端用登录用户 JWT先登记 Bindingpush 带本人 `online_db_id`
**单服务器**:一条默认同步通道即可(自动或手建)。
**多服务器**:每台线上 Postgres **各建一条通道**;按本机库选择对应 `channel_id`(建议登记 Binding
Binding多库 / 用户自助时建议登记;登录 JWT 即可,不必管理员权):
```http
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「本地库名 ↔ 线上库名」。
```http
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_status``pending` / `local_only_table` 等仅开通用户可见。
文案:`需 agent 才上云`(不得暗示已上云)。
### 5.2 Outbox
建议路径(等价即可):
```text
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=1``mode=local_dbsync`。默认可不启。
循环建议:
1. TTL拉白名单 → 写本地缓存。
2. `list_pending` → 逐条 push → 成功 `mark_done` / 失败 `mark_error` 并 break。
3. 写心跳文件(供 `/sync/status` 展示 `agent.running` / `last_beat`)。
**拉白名单**
```http
GET /api/v1/agent/sync/channels/{channel_id}/whitelist
Authorization: Bearer <token>
```
响应要点:`tables``pk_columns``conflict_policy`
**推单条**
```http
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`
**下行(仅线上 / 灌库 / 补齐)**
```http
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 .../pull``mode` 取:
| mode | 作用 |
|------|------|
| `bootstrap` | 分页全量(循环至 `has_more=false` |
| `pks` | 只取 A 主键,本机 diff |
| `rows` | 按 `row_pks` 取行 |
客户端把 `result.items` upsert 进本机 B 时务必 **WithApplying**(或等价),避免回声进 outbox。
空表:`result.columns` 仍会返回;本机应用其 `CREATE TABLE IF NOT EXISTS`(见下「表结构同步」)。
成功响应示例:
```json
{
"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/batch`body `{ "items": [ ... ] }`,最多 100保序遇错即停。
也可设完整 URL`YXD_SYNC_PUSH_URL=...`(覆盖默认拼装)。
**表结构同步(空表也要建)**
仅靠 outbox 行 push 时,**空表不会出现在线上**(无变更事件)。同步周期应额外:
1. **本机 → 线上**:对本机每张业务表(含 0 行)调用 ensure
```http
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已存在则跳过。
2. **线上 → 本机**:先拉结构,本机缺表则建空表,再 bootstrap 行:
```http
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`/`bootstrap``result.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 同步表示例:
```sql
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
- [ ] 空表:`schema/ensure` 后线上出现空表Z10c
- [ ] 缺列ensure 后 push 含新列不再 503Z10d
- [ ] 指纹:`schema/fingerprint` 返回 content_hash智能体带挂载 `online_db_id` 不 403Z14/Z14c
### 生产绑定Z12/Z13
- [ ] 启用智能体后换票 / `agents/me` 已有 `sync_bound=true`(无需手抄通道)
- [ ] 绑定码 redeem 成功
- [ ] 同号 `13531041944`lookup → **弹窗** → confirm取消不绑
- [ ] 未用超管号 `13531041945` 做绑定联调
- [ ] 库选「同步」后可 drain零手填 `CHANNEL_ID`
### 冲突
- [ ] 终端**无**冲突处理台
- [ ] 公司管理员打 conflicts API → 403
- [ ] 超管可在平台工作台看 LWW 日志 / 回滚
---
## 9. 智建 API 速查
基址:`{YXD_ONLINE_API_BASE}`,鉴权:`Authorization: Bearer ...`
- 管理路径:需「数据同步」
- **用户自助**:登录用户 JWT + 本人 Bindingpush 须带 `online_db_id`
- LWW 仅超管
| 方法 | 路径 | 谁用 |
|------|------|------|
| POST | `/api/v1/auth/token` | 换票;响应可含 `channel_id`/`online_db_id`/`sync_bound`Z12a |
| GET | `/api/v1/agents/me` | 智能体自查落点Z12b |
| POST | `/api/v1/auth/yuheng/ticket-exchange` | 宇恒凭票免登录换票(仅宇恒 HMAC |
| GET | `/api/v1/auth/bind/policy` | 绑定策略:`require_for_bind` / `trial_mode` / `yuheng_ticket_enabled` |
| POST | `/api/v1/auth/bind-code/redeem` | 绑定码兑换公开Z13b |
| POST | `/api/v1/auth/bind/phone-lookup` | 同号探测不绑定Z13c |
| POST | `/api/v1/auth/bind/phone-confirm` | 同号确认后绑定Z13c-1 |
| GET/POST/DELETE | `/api/v1/admin/bind-codes` | 管理员生成/列表/撤销绑定码Z13a |
| 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` | 全量灌库 |
| POST | `/api/v1/agent/sync/channels/{id}/schema` | 拉线上表结构(含空表) |
| POST | `/api/v1/agent/sync/channels/{id}/schema/ensure` | 本机空表建到线上已存在可补列Z10/Z10d |
| POST | `/api/v1/agent/sync/channels/{id}/schema/fingerprint` | 线上表指纹 `row_count`+`content_hash`Z14智能体可用挂载 online_db_id |
| GET/POST | `/api/v1/admin/sync/bindings` | 管理员或用户自助 |
| GET | `/api/v1/platform/dbsync/lww-overrides` | 仅超管 |
| POST | `/api/v1/platform/dbsync/lww-overrides/{id}/rollback` | 仅超管 |
OpenAPI`GET /api/v1/meta/openapi.yaml`
---
## 10. 本地联调最小步骤
### 生产路径(推荐)
1. 智建起栈;公司配置 `DefaultRemoteDSN`(或联调用空 DSN → sqlite 文件)。
2. 启用智能体 → 自动绑默认同步通道;或管理员 `POST .../admin/bind-codes` 发码。
3. 宇恒:`host_key` 换票 / `agents/me``sync_bound`;未绑则 lookup→确认 或 redeem。
4. 本机库选「同步」→ Binding + agent drain**不必**手填 `YXD_SYNC_CHANNEL_ID`
5. 同号联调用 **`13531041944`**,禁止超管号。
### 过渡 / 手建通道(仍可用)
1. 智建本机起栈(`.env` 已是本地:`AIJZ_PUBLIC_BASE_URL=http://127.0.0.1:8180`)。
2. 控制台建通道:`remote` 优先本机 **Postgres**;拷贝通道 ID。
3. 拿 token管理员登录或智能体 client_credentials
4. 宇恒设:
```bash
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=...
```
5. 对白名单表插一行 → 看本机 B + outbox → agent 推上 A → 比对 UUID。
6. 多服务器演练:再建第二条通道指向另一 Postgres`CHANNEL_ID` 验证不串库。
7. **空表**sync 周期调 `schema/ensure` + `schema`**Z10c 宇恒已接** `sync_schema.ensure_before_drain`),否则空表不会出现在线上。
---
## 11. 相关文档(智建仓)
| 文档 | 内容 |
|------|------|
| `松离线-dbsync方案-最终版.md` | 双方冻结方案 |
| `联调后修改意见-宇恒松离线.md` | 联调结论 + Z12/Z13 产品路径 |
| `docs/同步表约定.md` | UUID / 接口约定 |
| `docs/数据同步-开通说明.md` | 管理员开通 |
| `docs/数据同步-迁移手册.md` | 旧客迁移 |
| `docs/数据同步-中间件.md` | 平台能力与 API |
| `docs/发版说明-数据同步.md` | 默认无感发版说明 |
---
**分工提醒**智建只提供平台通道、校验、push/whitelist/Binding/LWW/绑定 API**写网关、outbox、agent、未开通回归、Z13 弹窗与选同步即用**由宇恒在己方仓库实现,勿改智建仓业务代码。