# 桌面端后端数据交互通用模板(以车辆管理为例) ## 1. 目的与适用范围 本文将“车辆管理”已落地的同步能力抽象为可复用模板,适用于后续任意业务模块(如供应商、订单、库存、质检等)快速实现: - 桌面端 CRUD 与后端数据交互 - 正向同步(桌面端 -> 后端) - 反向同步(后端 -> 桌面端) - 断线续传(离线可操作,重连自动补偿) - 本地缓存(保证离线可查、可改) --- ## 2. 总体架构模板 建议统一采用“**信号触发 + REST 拉取 + Outbox 补偿**”架构: 1. 后端业务变更后,广播 STOMP 信号(只发变更事件,不强依赖完整数据包)。 2. 桌面端接收信号后,不直接写本地业务表,而是触发“拉取接口”同步最新数据。 3. 桌面端本地操作时,在线优先调用后端;失败或离线则先写本地缓存 + 记录待同步操作(Outbox)。 4. 网络恢复后自动回放 Outbox,成功后清理队列并全量回拉一次做一致性对齐。 **核心原则**: - 在线追求实时,离线保证可用,重连追求最终一致。 - 任何“回传动作”必须幂等。 - 同步链路必须有完整日志(请求、结果、回放、失败原因)。 --- ## 3. 后端接口模板(推荐样式) 以车辆管理为例,接口分两类:业务 CRUD 接口 + 同步辅助接口。 ### 3.1 业务 CRUD(给桌面端直接调用) 建议统一路径风格(示例): - `GET /{module}/{entity}/anon/list?pageNo=1&pageSize=10000&tenantId=1002` - `GET /{module}/{entity}/anon/queryById?id=xxx&tenantId=1002` - `POST /{module}/{entity}/anon/add?tenantId=1002` - `POST /{module}/{entity}/anon/edit?tenantId=1002` - `DELETE /{module}/{entity}/anon/delete?id=xxx&tenantId=1002` - `POST /{module}/{entity}/anon/updateStatus?id=xxx&status=1&tenantId=1002` 返回建议统一: ```json { "success": true, "code": 200, "message": "操作成功", "result": {} } ``` 分页 `result` 建议统一包含: - `records` - `total` - `current` - `size` ### 3.2 后端推送信号(反向同步触发) 后端每次成功增删改后,广播 STOMP: - Topic:`/topic/sync/{entity-topic}` - Payload 示例: ```json { "cmd": "MES_VEHICLE_CHANGED", "action": "add|edit|delete|status", "entityId": "xxx", "timestamp": 1710000000000 } ``` 说明: - `cmd` 必须唯一且稳定,桌面端按它路由处理。 - `action` 用于日志和增量策略判断。 - `entityId` 可选但建议提供。 ### 3.3 桌面端正向回传批量接口(可选增强) 若使用统一 Outbox 回传通道,建议后端提供: - `POST /sys/sync/batch` 请求体示例: ```json [ { "messageId": "outbox-id-1", "aggregateType": "VEHICLE", "aggregateId": "vehicle-id", "eventType": "CREATE|UPDATE|DELETE|TOGGLE_STATUS", "payload": "{...}", "occurredAt": "2026-01-01T10:00:00Z" } ] ``` 后端必须做 `messageId` 幂等去重。 --- ## 4. 桌面端数据层模板 ## 4.1 必备组件 每个模块建议固定四层: 1. `EntityService`(例:`VehicleService`) - CRUD 入口 - 本地缓存 + 离线队列 - 重连回放逻辑 2. `SyncCoordinator`(例:`VehicleSyncCoordinator`) - 监听 STOMP 信号 - 识别 `cmd` 并发布本地事件或触发同步 3. `OutboxProcessor`(全局复用) - 持久化消息 - 在线实时发、离线补发 - 失败重试 + 指数退避 4. `NetworkMonitor`(全局复用) - 网络状态检测 - 状态变化事件发布 ## 4.2 本地缓存与待同步队列 推荐本地文件结构(示例): - `%LocalAppData%/YY.Admin/sync-cache/{entity}-cache.json` - `%LocalAppData%/YY.Admin/sync-cache/{entity}-pending-ops.json` 建议数据结构: - `cache`: 最新本地快照(可直接查询) - `pendingOps`: 离线期间操作日志,按时间顺序回放 `pendingOps` 字段建议: - `id` - `opType`(Add/Edit/Delete/UpdateStatus) - `entityId` - `payload` - `createdAt` --- ## 5. 同步逻辑模板(标准流程) ### 5.1 查询流程(Page/List) 1. 在线:先拉远端数据 -> 刷新本地缓存。 2. 远端失败:回退本地缓存。 3. 将 `pendingOps` 叠加到查询结果(保证 UI 看到“离线后的最新本地状态”)。 ### 5.2 新增/编辑/删除/状态修改 1. 若在线,优先调用远端接口。 2. 远端成功:更新本地缓存。 3. 远端失败或离线:写入 `pendingOps`,并立即更新本地缓存(保证可用)。 **注意**:新增时若本地临时 ID(如 `local-xxx`)不符合后端主键规则,回放前需清空 ID 让后端生成。 ### 5.3 网络恢复(断线续传) 触发条件:`NetworkMonitor` 从离线 -> 在线。 流程: 1. 串行回放 `pendingOps`。 2. 回放中任意失败立即停止,等待下次重试。 3. 回放完成后再做一次全量拉取,覆盖本地缓存。 4. 发布 UI 刷新事件(避免用户手动点查询)。 ### 5.4 后端 -> 桌面反向同步 流程: 1. STOMP 收到 `cmd`。 2. `SyncCoordinator` 解析命令。 3. 触发本地“拉取并刷新缓存”。 4. UI 订阅变更事件刷新列表。 --- ## 6. 可复用方法模板 后续新模块可直接复用以下模式(名称替换即可): - `FetchRemoteListAsync()` - `RemoteAddAsync() / RemoteEditAsync() / RemoteDeleteAsync() / RemoteUpdateStatusAsync()` - `ApplyFilters()` - `ApplyPendingOpsSnapshotUnsafe()` - `EnqueuePendingOperation()` - `ReplayPendingOperationsAsync()` - `ExecutePendingOperationAsync()` - `LoadPendingOpsFromDisk() / SavePendingOpsToDiskUnsafe()` - `LoadCacheFromDisk() / SaveCacheToDiskUnsafe()` - `CloneEntity()` 建议抽一个通用基类(后续可做): - `OfflineSyncEntityServiceBase` - 负责缓存、队列、回放、网络事件订阅 - 业务子类只实现远端接口和过滤逻辑 --- ## 7. 日志模板(强制建议) 每个模块建议统一日志前缀,便于检索。 车辆管理示例前缀可复用为模块名替换: - `[车辆同步]` 服务初始化 - `[车辆列表]` 查询链路 - `[车辆新增]` `[车辆修改]` `[车辆删除]` `[车辆状态]` 本地/远端操作 - `[车辆远端]` 实际 HTTP 请求与结果 - `[车辆入队]` 离线入队 - `[车辆回放]` 重连回放 - `[车辆重连]` 全量对齐 - `[车辆推送]` STOMP 信号处理 - `[车辆网络]` 网络状态变化 每条日志建议最少包含: - 操作类型 - 业务主键 - 在线/离线状态 - 是否成功 - 失败原因(异常 message) - 队列长度(适用时) --- ## 8. 新模块落地清单(开发步骤) 按以下顺序复制模板最稳: 1. 后端先准备 `anon` CRUD 接口(或授权接口)+ 统一返回结构。 2. 后端在 CRUD 成功后广播 STOMP 变更信号。 3. 桌面端新增 `EntityService`(先把在线 CRUD 跑通)。 4. 增加本地缓存与 `pendingOps` 持久化。 5. 加入网络状态监听与重连回放。 6. 新增 `SyncCoordinator` 处理 STOMP -> 本地刷新。 7. 全链路日志补齐。 8. 按“联调测试矩阵”逐项验证。 --- ## 9. 联调测试矩阵(建议) 每个模块至少跑完以下用例: 1. 在线新增 -> 后端可见。 2. 在线编辑 -> 后端可见。 3. 在线删除 -> 后端可见。 4. 在线状态切换 -> 后端可见。 5. 离线新增/编辑/删除 -> 本地立即可见。 6. 重连后自动回放 -> 后端最终一致。 7. 后端直接改数据 -> 桌面端自动刷新。 8. 异常网络抖动 -> 不崩溃,回放可恢复。 --- ## 10. 车辆管理对应实现参考(当前项目) 桌面端: - `YY.Admin.Services/Service/Vehicle/VehicleService.cs` - `YY.Admin.Services/Service/Vehicle/VehicleSyncCoordinator.cs` - `YY.Admin.Core/Core/Services/IVehicleService.cs` 后端: - `jeecg-module-xslmes/.../MesXslVehicleController.java`(业务接口 + 变更推送) - `jeecg-module-device-sync/.../SyncController.java`(如启用统一回传通道) 基础设施: - `YY.Admin/Infrastructure/Sync/OutboxProcessor.cs` - `YY.Admin/Infrastructure/Network/NetworkMonitor.cs` - `YY.Admin/Infrastructure/Hubs/StompWebSocketService.cs` --- ## 11. 推荐统一规范(后续团队约定) 1. 所有新模块都按“在线优先 + 离线入队 + 重连回放 + 全量对齐”实现。 2. 所有模块都使用统一日志前缀和字段。 3. 后端变更推送统一 `cmd` 命名:`{SYSTEM}_{ENTITY}_CHANGED`。 4. 回传消息统一带 `messageId` 幂等键。 5. 同步失败不阻塞主流程,但必须可观测(日志 + 队列长度)。 6. 任意新模块上线前必须跑完“联调测试矩阵”。 --- > 结论: > 车辆管理已经提供了完整的可复制样板。后续新模块只需替换“实体、接口、过滤字段、命令字”,其余同步骨架可直接复用,从而快速实现稳定的桌面端/后端数据交互能力。