# 同步表约定（M0）

> 依据：`松离线-dbsync方案-最终版.md`  
> 默认建表模板**仍为自增**；仅「同步表」使用下列模板。

## 同步表模板（UUID 主键）

SQLite 示例：

```sql
CREATE TABLE IF NOT EXISTS orders (
  id TEXT PRIMARY KEY NOT NULL,          -- UUID，小写带连字符
  -- ... 业务列 ...
  updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
```

Postgres 示例：

```sql
CREATE TABLE IF NOT EXISTS orders (
  id UUID PRIMARY KEY,                   -- 或 TEXT
  -- ... 业务列 ...
  updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
```

## 入通道校验（平台已启用）

保存 / 启用同步通道时：

1. 白名单非空  
2. 可达端（通常为 **remote 线上库**）上，各表主键列须为 **TEXT/VARCHAR/UUID** 类（拒绝 INTEGER/SERIAL）  
3. **外键闭包**：白名单内表若引用名单外表（或反之一侧在名单），保存失败  

形态 B 下本机 SQLite 可能不可达：至少 **remote** 须能完成校验。

### 线上库 A DSN（生产统一 Postgres）

```text
postgres://user:pass@127.0.0.1:5432/app_online?sslmode=disable
```

联调临时可用 SQLite（不宜多 writer）：

```text
file:E:/data/ajz_remote.db?_pragma=foreign_keys(1)
```

使用正斜杠；平台会规范化反斜杠并补 `busy_timeout`。

单服务器一条通道即可；多服务器为**每台线上库各建一条通道**，客户端选用对应 `channel_id`。

## 宇恒开通（opt-in）

```bash
YXD_SYNC_MODE=local_dbsync
YXD_SYNC_DBSYNC_TABLES=orders,order_items
# 或写入 cache/db_sync/whitelist.json: {"tables":["orders","order_items"]}
```

未设置 `local_dbsync` 时行为与现网一致。

## M2 本机 agent（B→A）— 智建已提供的接口

宇恒侧自行实现 agent；智建只提供下列 API（需「数据同步」权限）：

- `GET /api/v1/agent/sync/channels/:id/whitelist`
- `POST /api/v1/agent/sync/channels/:id/push` — body: `{table,op,row_pk,row,version,client_outbox_id}`
- `POST /api/v1/agent/sync/channels/:id/push/batch`

推送落到通道 **remote**（线上 A），幂等认客户端 version；平台不连用户本机 SQLite。

**幂等**：同 version 再 push → `{ success:true, result:{ skipped:true, ... } }`；重复 HTTP 安全，线上不双行。  
**可重试**：remote IO 失败 → `503` + `retryable:true`（勿换 UUID）。

宇恒建议环境变量（由对方配置，不在智建仓改）：

```bash
YXD_SYNC_MODE=local_dbsync
YXD_SYNC_AGENT=1
YXD_ONLINE_API_BASE=https://aisite.example.com
YXD_SYNC_CHANNEL_ID=<通道ID>
YXD_SYNC_ACCESS_TOKEN=<含「数据同步」权限的 JWT 或 agent token>
```

## M3 超管 LWW 审计（智建）

- 存储：`data/dbsync/lww_overrides.json`（与租户 conflicts 分离）
- API：`GET /api/v1/platform/dbsync/lww-overrides`（**仅平台超级管理员**）
- 公司侧：`/api/v1/admin/sync/conflicts*` → **403**
- 双入口写入：worker `drain` + agent `push` 在 LWW 覆盖/保留时记审计
- TTL：默认 90 天（`DBSync.LwwAuditTTLDays`）
- 对账限流：手动默认 300 秒（`DBSync.ReconcileMinSec`）
- 超管回滚：`POST /api/v1/platform/dbsync/lww-overrides/:id/rollback`（仅 `applied_source`；按 `loser_payload` 写回线上 A）
