Files
Hua.Todo/docs/manual/08-云同步规则.md
ShaoHua 65cee20006 docs: 重组 docs/manual/ 指南结构,区分普通用户与开发者双入口
- 新增 00-目录与导读.md 双入口导航

- 用户面(01-04):项目介绍、安装指南、版本记录、其他信息

- 开发者面(05-10):技术栈、构建、架构、云同步、代码规范、MCP

- 拆分旧01为 01(用户)+05(开发者);旧02为 02(用户)+06(开发者)

- 合并旧08+09 MCP文档为 10-MCP服务集成

- 同步更新 README.md 与 .trae/rules/项目/ 交叉引用
2026-06-16 01:46:46 +08:00

513 lines
24 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.
# Todo 待办项云同步规则
> 本文档面向开发者,汇总 Hua.Todo 项目的云同步完整规则,涵盖架构、API 契约、认证鉴权、同步工作流、安全策略与可控落盘等。
>
> 术语:本文中"任务 / Todo 待办项"均为业务实体(对应代码 `Task` / `SubTask` / `TaskEntity`),与编码侧的"研发工单"无关。
---
## 一、部署架构与职责边界
### 1.1 两种运行模式
| 模式 | 宿主 | 云同步端点 | 说明 |
|---|---|---|---|
| **嵌入式** | MAUI / Avalonia | 不暴露 | 嵌入 Kestrel + WebView,仅注册 `AddApplicationServices()`,托管本地 `/api/*`;云同步需连接外部 Host |
| **独立服务端** | Hua.Todo.Host | 完整暴露 | 同时注册 `AddApplicationServices()` + `AddCloudSyncServer()`,对外提供全部云同步端点 |
### 1.2 DI 注册边界(强制)
```
Hua.Todo.Application (共享层)
├── AddApplicationServices() ← Todo CRUD,两端均注册
│ ├── TodoDbContext / TaskRepository / TaskService
│ └── DynamicApi (本地 /api/*)
└── AddCloudSyncServer() ← 云同步能力,仅 Host 注册
├── CloudAuthService / CloudTaskSyncService
├── SecurityPolicyService / CloudAdminService / CloudProbeService
├── SessionAuthenticationHandler (Bearer Token 鉴权)
└── MapCloudSyncEndpoints() (13 个云同步端点)
```
- **MAUI / Avalonia**`MauiProgram.cs` / `AvaloniaProgram.cs` **只能**调用 `AddApplicationServices()`,禁止调用 `AddCloudSyncServer()`
- **Hua.Todo.Host**`Program.cs` 同时调用两者。
### 1.3 同源 Host 改造(研发工单 08)
前端云同步请求统一走当前 Host 同源,不再直连外部 `serverUrl`
- **Host 模式(Vite dev**Vite proxy 将 `/auth``/tasks``/sync``/security``/cloud-sync` 转发到 Host`:5173`)。
- **MAUI 模式(WebView**:Vue 静态部署到嵌入服务器同源,无需代理。
- `cloudClient.ts` 不设外部 baseURL,由浏览器自动同源。
- 用户配置的 `serverUrl` 仅用于服务端代理探测(`POST /cloud-sync/probe`),不再作为 API 基址。
---
## 二、数据隔离规则
### 2.1 UserId 隔离(强制)
- `Tasks` 表含 `UserId` 字段(外键),所有云同步 API 按当前登录用户隔离读写。
- 本地模式(嵌入式):`Tasks.UserId = TodoUserIds.LocalUserId = "local"`,与云端用户共存不冲突。
- 服务端查询/写入必须从会话上下文提取 `UserId`,不得接受客户端传入的 `UserId` 参数。
### 2.2 同步数据范围
- 每次同步操作仅涉及**当前登录用户**的 Todo 待办项。
- `GET /tasks` 返回该用户全量任务(`CloudTaskItem[]`)。
- `POST /sync` 的 upsert/deletes 均限定在当前用户数据范围内执行。
---
## 三、认证与会话管理
### 3.1 认证方式
- **Bearer Token**:所有云同步端点需携带 `Authorization: Bearer {accessToken}`
- Token 即 `UserSessionEntity.SessionId`GUID),存储于服务端 `UserSessions` 表,非纯无状态 JWT。
### 3.2 会话生命周期
| 操作 | 端点 | 说明 |
|---|---|---|
| 初始化管理员 | `POST /auth/bootstrap` | 仅当系统无云用户时可调用一次,自动生成随机密码 |
| 登录 | `POST /auth/login` | 验证用户名/密码(Argon2id 哈希),创建 `UserSessionEntity`,返回 AccessToken + 权限列表 |
| 登出 | `POST /auth/logout` | 删除当前会话记录,Token 立即失效 |
| 修改密码 | `POST /auth/change-password` | 需提供当前密码,更新后旧会话不失效 |
### 3.3 SessionAuthenticationHandler 鉴权流程
1.`Authorization` 头提取 Bearer TokenGUID 格式 SessionId
2.`UserSessions` 表:校验 SessionId 存在且 `ExpiresAtUtc > DateTime.UtcNow`
3. 加载用户角色,通过 `IRolePermissionMapper` 获取权限列表
4. 检查 `SecurityPolicies.AllowSync`:若为 `false`,从权限中移除 `sync:write`
5. 构造 `ClaimsIdentity`(含 `sub`=UserId、`role``perm` Claims),注入 `HttpContext.User`
---
## 四、RBAC 权限模型
### 4.1 权限点定义(6 个)
| 权限点 | 常量 | 说明 |
|---|---|---|
| `tasks:read` | CloudPermissions.TasksRead | 读取 Todo 待办项 |
| `tasks:write` | CloudPermissions.TasksWrite | 写入 Todo 待办项 |
| `sync:write` | CloudPermissions.SyncWrite | 执行同步操作(受 `AllowSync` 策略叠加限制) |
| `policy:read` | CloudPermissions.PolicyRead | 读取安全策略 |
| `policy:write` | CloudPermissions.PolicyWrite | 修改安全策略 |
| `users:manage` | CloudPermissions.UsersManage | 管理用户(Admin 专属) |
### 4.2 角色与权限映射
| 角色 | 权限 |
|---|---|
| `admin` | 全部 6 个权限 |
| `user`(默认) | `tasks:read``tasks:write``sync:write``policy:read` |
| `readonly` | `tasks:read``policy:read` |
| `nosync` | `tasks:read``tasks:write``policy:read` |
> `sync:write` 权限在鉴权时还会受 `SecurityPolicies.AllowSync` 二次过滤:若策略禁止同步,即使角色拥有 `sync:write` 也会被移除。
### 4.3 端点权限要求
| 端点 | 方法 | 所需权限 |
|---|---|---|
| `/tasks` | GET | `tasks:read` |
| `/sync` | POST | `sync:write` |
| `/security/policy` | GET | `policy:read` |
| `/security/policy` | PUT | `policy:write` |
| `/admin/*` | GET/POST/DELETE | `users:manage` |
| `/cloud-sync/probe` | POST | —(匿名访问) |
---
## 五、安全策略与可控落盘
### 5.1 SecurityPolicies 表
| 字段 | 类型 | 说明 |
|---|---|---|
| `AllowPersist` | bool | 是否允许客户端持久化 Todo 数据到本地存储 |
| `AllowSync` | bool | 是否允许该用户执行同步写入 |
| `IsTrustedDeviceOnly` | bool | 是否仅限受信任终端(预留) |
每个用户一条策略记录,与 `UserEntity` 一对一。
### 5.2 客户端落盘规则(强制)
| 服务端策略 | 客户端行为 |
|---|---|
| `allowPersist = true` | 正常落盘:Todo 数据 + 登录凭据(Token)写入 `localStorage` / SQLite |
| `allowPersist = false` | **内存模式**:Todo 数据与凭据仅保留在内存中,退出应用后全部清除 |
**策略切换时的处理**
- `true → false`:立即清空已落盘数据,提示用户"已切换为内存模式,退出后数据不保留"
- `false → true`:恢复持久化,将当前内存数据写入存储
### 5.3 落盘数据范围
"禁止落盘"时,以下数据均不得写入任何持久化介质:
- Todo 数据(列表/详情)
- 登录凭据(accessToken / 会话标识)
- 同步状态(上次同步时间、待同步队列)
- 安全策略缓存
---
## 六、同步工作流
### 6.1 完整流程
```
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 配置地址 │ ──→ │ 登录 │ ──→ │ 拉取全量 │
│ (探测可达) │ │ (获取Token) │ │ (GET /tasks) │
└──────────────┘ └──────────────┘ └──────┬───────┘
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 返回全量 │ ←── │ 增删改同步 │ ←── │ 本地编辑 │
│ (SyncResponse)│ │ (POST /sync) │ │ (upsert+del)│
└──────────────┘ └──────────────┘ └──────────────┘
```
### 6.2 同步策略
#### 6.2.1 核心原则
- **任务唯一标识是 `id`**`id` 是任务实体的唯一稳定标识,不可依赖时间戳做身份判断
- **`updatedAtUtc` 是冲突判断字段**:用于解决"同一任务被多端修改时哪个变更更新"的问题
- **服务端存储唯一真实源(Source of Truth)**:所有设备最终都收敛到服务端数据
- **字段级 Last-Write-WinsLWW)合并**:同一字段的多设备并发修改,以 `updatedAtUtc` 较新者为准
- **逻辑删除(Tombstone**:删除操作标记为"已删除",而非物理删除,保证多端删除语义一致
- **增量同步**:每次同步只上传本端变更(pendingUpserts + pendingDeletes),不上传全量
#### 6.2.2 变更追踪机制
每个 `CloudTaskItem` 包含以下时间戳字段:
| 字段 | 说明 |
|---|---|
| `createdAtUtc` | 创建时间(服务端分配,永不改变) |
| `updatedAtUtc` | 最后一次修改时间(每次字段变更时更新) |
| `deletedAtUtc` | 逻辑删除时间(为 `null` 表示未删除) |
> `updatedAtUtc` 由**请求发起方**(客户端或服务端)在发起变更时写入,存储到服务端后不再覆盖(除非有更新的变更)。
#### 6.2.3 冲突解决规则
当客户端提交的任务与服务端现有任务发生 `id` 冲突时,按以下规则处理:
| 场景 | 解决规则 |
|---|---|
| 同一 `id`,客户端 `updatedAtUtc` **更新** | 服务端接受客户端版本,覆盖对应字段 |
| 同一 `id`,服务端 `updatedAtUtc` **更新** | 服务端拒绝客户端版本,保留服务端版本 |
| 同一 `id`,两者 `updatedAtUtc` **相同** | 服务端优先(保守策略) |
**字段级合并示例**
- 设备 A 修改标题,设备 B 修改截止日期
- 服务端对两个字段分别取 `updatedAtUtc` 较新者,合并出最终结果
- 两个设备的修改都被保留,不会互相覆盖
#### 6.2.4 父子任务处理
- **删除**:删除父任务时,其子任务一并标记为已删除(递归)
- **创建**:子任务的 `parentTaskId` 在上传时若为 `null`(新创建任务),服务端分配 ID 后,第一遍返回临时 ID 映射;第二遍利用映射完成父子关系重映射
- **重建父子关系**:若原父任务被删除后重建,子任务的 `parentTaskId` 引用仍指向原父 ID,不会自动指向新父
#### 6.2.5 Tombstone 保留策略
- `deletedAtUtc``null` 的任务在服务端保留至少 **30 天**
- 超过 30 天后由服务端垃圾收集(物理删除)
- 客户端同步时拉取全量(含 Tombstone),本地过滤不展示 `deletedAtUtc != null` 的任务
#### 6.2.6 服务端处理流程
`POST /sync` 服务端执行顺序:
```
1. 解析 upserts 和 deletes
2. 对 deletes 中的每个 id
- 递归标记该任务及所有子任务 deletedAtUtc = now
3. 对 upserts 按 updatedAtUtc 降序排列(较新的先处理)
4. 对每个 upsert 任务:
a. 若 id 在服务端不存在 → 创建新记录(分配正向 id)
b. 若 id 存在但服务端 updatedAtUtc 更新 → 跳过(保留服务端)
c. 若 id 存在且客户端 updatedAtUtc >= 服务端 updatedAtUtc → 字段级合并更新
5. 返回当前用户全量任务(含 Tombstone)
```
> 排序处理的目的:确保最新的变更优先被采纳。
#### 6.2.7 关于"服务端为准"的正确理解
"服务端为准"并不意味着客户端会丢失数据。完整的同步流程是:
1. **客户端上传阶段**:将 `pendingUpserts` + `pendingDeletes` 提交到服务器
2. **服务器合并阶段**:按 LWW 规则合并客户端提交与服务器现有数据
3. **客户端覆盖阶段**:用服务器返回的全量数据覆盖本地
由于客户端在第 1 步已经把本地所有变更提交,服务器在第 2 步已经把客户端的变更合并进权威状态,第 3 步用权威状态覆盖本地是安全的——**不会丢失任何已提交的变更**。
真正会丢失的场景是:客户端在离线状态下修改了任务 A,但没有在联网后发起同步就直接断开连接。这种情况下,离线修改会保留在本地 `pendingUpserts` 中,下次联网同步时会正常提交。
### 6.3 SyncRequest / SyncResponse 结构
**请求**`POST /sync`):
```json
{
"upserts": [
{ "id": 1, "title": "A", "priority": 1, "isCompleted": false, "parentTaskId": null, "updatedAtUtc": "2026-04-06T17:00:00Z" },
{ "id": null, "title": "New", "priority": 1, "isCompleted": false, "parentTaskId": null, "updatedAtUtc": "2026-04-06T17:30:00Z" }
],
"deletes": [2, 3]
}
```
- `id` 为服务端已知 ID`id > 0`)时执行更新;`id``null` / 0 / 负数时创建新记录
- `updatedAtUtc` 必填,客户端在发起变更时写入本地时间(UTC)
- `deletes` 为待逻辑删除的任务 ID 数组
**响应**
```json
{
"serverTimeUtc": "2026-04-06T17:39:30.0281279Z",
"tasks": [
{ "id": 1, "title": "A", "priority": 1, "isCompleted": false, "parentTaskId": null, "createdAtUtc": "2026-04-01T00:00:00Z", "updatedAtUtc": "2026-04-06T17:00:00Z", "deletedAtUtc": null }
]
}
```
- `tasks` 返回当前用户**全量数据**(含 `deletedAtUtc != null` 的 Tombstone
- 客户端用响应全量覆盖本地数据,本地过滤不展示已删除任务
### 6.4 同步按钮调用规则
**触发入口**:主界面"同步"按钮(`TaskList.vue`),绑定 `syncNow()` 方法。
#### 6.4.1 前置检查
1. 检查 `isSyncing` 状态,防止并发重复调用
2. 检查登录状态(未登录则提示先登录)
#### 6.4.2 变更检测(增量同步核心)
本地维护 `pendingUpserts``pendingDeletes` 两个集合:
| 操作 | 记录时机 | 记录内容 |
|---|---|---|
| 新增任务 | 创建时 | `{ id: 临时负数, title, priority, ..., updatedAtUtc: 当前时间 }` |
| 修改任务 | 任何字段变更时 | `{ id, 全部字段, updatedAtUtc: 当前时间 }` |
| 删除任务 | 点击删除时 | `{ id }`(从 pendingUpserts 移除,加入 pendingDeletes|
> 每次用户操作后,更新本地 `updatedAtUtc`。使用 `seenIds` 集合去重,同一 `id` 只保留最新一条记录。
#### 6.4.3 数据准备
1. **合并 pendingUpserts**
- 调用 `flattenTasksToCloudUpserts(localTasks, pendingUpserts)` 生成 upsert 列表
- **ID 映射规则**
- `id > 0`:保留原 ID(服务端已知,执行更新)
- `id <= 0` 或未设置:置为 `null`(服务端分配新 ID,执行创建)
-`updatedAtUtc` 升序排列(较早的先发送,便于服务端 LWW 判断)
2. **生成 deletes 列表**
-`pendingDeletes` 提取待删除 ID
- 去重:`deletes = [...new Set(pendingDeletes.map(d => d.id))]`
#### 6.4.4 请求发送
- 端点:`POST /sync/`
- 请求体:`{ upserts: CloudTaskUpsert[], deletes: number[] }`
- 携带 `Authorization: Bearer {accessToken}`
#### 6.4.5 响应处理
1. **解析全量响应**
- 调用 `buildTaskTreeFromCloudItems(response.tasks)` 将扁平响应转为前端树结构
- 过滤 `deletedAtUtc == null` 的任务用于展示
2. **更新本地状态**
- `tasks.value = cloudTasks`(覆盖式更新,确保与服务端一致)
- `LocalStorageService.saveTasks(cloudTasks)`
- 清空 `pendingUpserts``pendingDeletes`
3. **更新同步状态**
- `lastSyncTime`:使用响应中的 `serverTimeUtc`
- `syncError = null`
#### 6.4.6 错误处理
| 错误类型 | 处理策略 |
|---|---|
| 401 未授权 | 清除本地会话,弹出重新登录提示 |
| 403 禁止 | Toast 提示权限不足 |
| 网络错误 | 重试(指数退避,最多 3 次),仍失败则保留 pending 数据下次同步 |
#### 6.4.7 完整流程图
```
┌─────────────────────────────────────────────────────────────┐
│ syncNow() 触发 │
└─────────────────────┬───────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 1. 前置检查:isSyncing? 已登录? │
└─────────────────────┬───────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 2. 变更检测:pendingUpserts + pendingDeletes │
│ - 遍历 tasks,收集 dirty 项(updatedAtUtc > lastSyncTime)│
│ - 新增/删除操作直接追加到 pending │
└─────────────────────┬───────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 3. 数据准备 │
│ - flattenTasksToCloudUpserts() │
│ - seenIds 去重 │
│ - 按 updatedAtUtc 排序 │
└─────────────────────┬───────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 4. POST /sync { upserts, deletes } │
└─────────────────────┬───────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 5. 响应处理 │
│ - buildTaskTreeFromCloudItems() │
│ - tasks.value = cloudTasks │
│ - saveTasks() │
│ - 清空 pendingUpserts / pendingDeletes │
│ - lastSyncTime = serverTimeUtc │
└─────────────────────────────────────────────────────────────┘
```
---
## 七、API 契约总览
### 7.1 通用约定
- Base URL:客户端同源请求,无需外部配置
- 认证:`Authorization: Bearer {accessToken}`
- 统一错误响应:
```json
{ "code": "ERROR_CODE", "message": "Human readable message." }
```
### 7.2 错误码
| 错误码 | HTTP 状态 | 含义 |
|---|---|---|
| `UNAUTHORIZED` | 401 | 未登录或会话失效 |
| `FORBIDDEN` | 403 | 权限不足(RBAC 拒绝 / 策略拒绝) |
| `BAD_REQUEST` | 400 | 请求参数不合法 |
| `NOT_FOUND` | 404 | 资源不存在 |
### 7.3 完整端点清单
| 路由 | 方法 | 权限 | 说明 |
|---|---|---|---|
| `/auth/bootstrap` | POST | 无(仅首次) | 初始化管理员账号 |
| `/auth/login` | POST | 匿名 | 登录,返回 AccessToken |
| `/auth/logout` | POST | 登录即可 | 登出,删除会话 |
| `/auth/change-password` | POST | 登录即可 | 修改当前用户密码 |
| `/tasks` | GET | `tasks:read` | 获取当前用户全量任务 |
| `/sync` | POST | `sync:write` | 上传 upsert + deletes,返回最新全量 |
| `/security/policy` | GET | `policy:read` | 获取当前用户安全策略 |
| `/security/policy` | PUT | `policy:write` | 修改当前用户安全策略 |
| `/cloud-sync/probe` | POST | 匿名 | 服务端代理探测目标 URL 可达性 |
| `/admin/users` | GET/POST/DELETE | `users:manage` | 用户管理 |
| `/admin/sessions` | GET/DELETE | `users:manage` | 会话管理 |
| `/admin/audit-logs` | GET | `users:manage` | 审计日志查询 |
---
## 八、前端实现规则
### 8.1 cloudClient.tsAxios 实例)
- 与通用 `client.ts`baseURL 含 `/api`)解耦,云同步专用。
- **请求拦截器**:MAUI 模式下从 `CloudSyncStorage` 读取 `serverUrl`Host 模式不设 baseURL 走同源;自动附加 `Bearer {accessToken}`
- **响应拦截器**
- 401 → 清除本地会话,弹出重新登录提示。
- 403 `FORBIDDEN` → Toast 提示权限不足。
- 其他错误 → 统一 Toast 通知。
### 8.2 cloudSync.tsAPI 封装)
提供 `cloudSyncApi` 对象,6 个方法:
| 方法 | 端点 | 核心逻辑 |
|---|---|---|
| `login()` | `/auth/login` | 登录后保存 Session 到 `CloudSyncStorage` |
| `logout()` | `/auth/logout` | 清除本地会话与缓存 |
| `getTasks()` | `/tasks` | 拉取全量 `CloudTaskItem[]`,通过 `buildTaskTreeFromCloudItems` 将扁平数据转为前端 Task 树 |
| `syncTasks()` | `/sync` | 通过 `flattenTasksToCloudUpserts` 将 Task 树扁平化为 upsert 列表,携带 deletes 提交 |
| `getSecurityPolicy()` | `/security/policy` | 获取策略,驱动落盘/内存模式切换 |
| `probeServerUrl()` | `/cloud-sync/probe` | 由服务端代理探测目标 URL |
### 8.3 CloudSyncSettingsDialog.vueUI 组件)
功能区块:
- **同步开关**:checkbox 控制,校验地址已保存方可开启。
- **服务端地址**:输入 + 规范化 + 保存并探测(`probeServerUrl`)。
- **登录区**:用户名/密码 → `login()` → 刷新策略与状态。
- **已登录区**:会话摘要、连接状态、安全策略展示、登出 + 立即同步。
- 通过 CustomEvent`cloudSyncStateChanged` / `cloudSyncTasksRequested`)与其他组件通信。
---
## 九、服务端代理探测规则
由研发工单 08 引入,`POST /cloud-sync/probe` 由 Host 代理探测目标 URL:
- 输入:`{ "targetUrl": "https://example.com" }`
- 输出:`{ "isReachable", "httpStatus", "isHttps", "title", "description", "type" }`type: `success` / `warn` / `error`
- 安全约束:
- 限制目标 URL 仅允许 HTTP/HTTPS 公网地址,禁止探测 localhost / 内网 IP(防 SSRF)。
- 建议加频率限制(如每分钟 3 次)。
---
## 十、审计日志
所有关键安全事件写入 `AuditLogs` 表:
- 登录成功 / 失败(含 IP、终端信息)
- 安全策略变更
- 用户管理操作(创建 / 删除 / 重置密码)
通过 `/admin/audit-logs` 端点查询,支持按时间、用户、事件类型过滤。
---
## 十一、约束与风险
| 约束 / 风险 | 缓解措施 |
|---|---|
| Token 吊销(会话非纯无状态) | `UserSessions` 表管理;登出即删记录,Token 立即失效 |
| HTTPS 强制 | 所有安全通信必须基于 TLS,防中间人攻击窃取 Token |
| 暴力破解 | `POST /auth/login` 建议加 Rate Limiting |
| MAUI 端不暴露云同步端点 | DI 注册边界严格执行:`AddCloudSyncServer()` 仅 Host 调用 |
| `allowPersist = false` 时数据丢失风险 | UI 强提示;退出时若未同步则二次确认 |
| 密码存储 | 使用 Argon2id 哈希(含 Salt),不存明文 |
| SSRF(探测端点) | `CloudProbeService` 限制目标为公网地址,禁止内网探测 |
---
> **相关文档**
> - PRD v1.2.0[产品需求文档-1.2.0.md](../project/产品需求文档-1.2.0.md)
> - 研发工单总览:[00-工单总览.md](../project/研发工单-v1.2.0/00-工单总览.md)
> - 服务端基础能力:[04-CloudSync-服务端基础能力.md](../project/研发工单-v1.2.0/04-CloudSync-服务端基础能力.md)
> - 客户端工作流:[05-CloudSync-客户端配置与工作流.md](../project/研发工单-v1.2.0/05-CloudSync-客户端配置与工作流.md)
> - 安全与可控落盘:[06-CloudSync-安全与可控落盘.md](../project/研发工单-v1.2.0/06-CloudSync-安全与可控落盘.md)
> - 安全设计方案:[06.1-CloudSync-服务端安全设计方案.md](../project/研发工单-v1.2.0/06.1-CloudSync-服务端安全设计方案.md)
> - 同源 Host 重构:[08-cloud_sync_refactor_plan.md](../project/研发工单-v1.2.0/08-cloud_sync_refactor_plan.md)
> - 技术架构设计:[07-技术架构设计](./07-技术架构设计.md)