
灯牌草稿弱网同步重试不能多出三份作品也不能盖掉平板的新版本地铁里手机修改灯牌文案点保存时网络断了一下。按钮转圈后提示失败用户连点三次回到家用平板打开列表里多了三份同名作品最新颜色还被旧文案覆盖。根因不是简单的“网络不稳定”而是应用没有明确“本地已保存、云端待同步、云端已确认”的状态也没有为重试定义幂等与冲突规则。华为Network Kit 文档入口说明网络数据传输与连接管理能力网络状态判断 FAQ提示连接状态可用于界面反馈。本文讨论跨平台应援灯牌控制应用的业务同步协议而不是把某个 Android Network Kit SDK 误当作 HarmonyOS 原生 API。参考 Flutter OpenHarmony API 20 工程未提供已验收的跨端云同步代码是前后端契约示意。1. 先分清本地保存与云端确认用户点保存最先能保证的是本机草稿落盘。云端同步是第二步必须独立展示。若本地成功、云端超时按钮不能继续写“保存失败”让用户误以为内容丢了若本地落盘失败即使网络正常也不能显示“已保存”。平板只能看到云端确认过的版本手机应显示“本机已保存等待同步”。状态手机可见结果平板可见结果编辑中未保存旧版本本地已保存、排队同步内容安全在本机旧版本服务端已确认已同步可读取新版本冲突待处理需要选择版本保持原版本这张状态表约束 UI 用语。网络指示灯变绿只说明有网络不说明服务器已经接受了某份草稿。2. 每次修改生成稳定的操作 ID重试时要重发同一个操作而不是重新生成一条“创建草稿”请求。客户端给草稿固定draftId给这次修改固定operationId传输失败后重试沿用原 ID。服务端记录已处理操作并返回相同结果避免重复创建。新一次用户编辑才获得新的操作 ID。interfaceDraftSyncOperation{operationId:string;draftId:string;baseRevision:number;newRevision:number;changedAt:number;patch:{text?:string;color?:string;speed?:number};}functionretrySameOperation(op:DraftSyncOperation):DraftSyncOperation{return{...op,operationId:op.operationId};}这个函数刻意“什么也不变”因为重试要表达同一业务意图。真实实现要把操作和本地草稿一起持久化进程退出后还能找到原 ID只存在按钮闭包里的 UUID重启后仍会重复创建。3. 本地队列先保证顺序手机连续改文字、颜色、速度三个操作若并发发送后一个可能先到服务端。最简单的可靠方式是按每份草稿串行同步同一draftId的下一条操作等待上一条确认。不同草稿可并行避免一份卡住拖住全部。队列要能在断网时暂停、重连后继续也要能让用户取消某个尚未上传的编辑。interfacePendingQueue{draftId:string;operations:DraftSyncOperation[];inFlightOperationId?:string;}functionnextToSend(queue:PendingQueue):DraftSyncOperation|undefined{if(queue.inFlightOperationId)returnundefined;returnqueue.operations[0];}队首策略能守住单草稿顺序但不自动解决跨设备冲突。服务端仍需检查baseRevision是否是当前最新版本。队列中的后续操作若基底已失效应暂停并让用户处理而不是盲目向前发送。4. 服务端用修订号做条件写入手机基于修订 5 修改文字平板已经把颜色更新到修订 6。若服务端只按最后写入时间覆盖就会吞掉平板颜色。请求必须声明baseRevision5服务端比较当前版本只有相等才提交否则返回当前修订和可用于比较的状态。客户端进入“冲突待处理”不悄悄丢弃其中一边。typeSyncReply|{kind:applied;operationId:string;revision:number}|{kind:already_applied;operationId:string;revision:number}|{kind:conflict;currentRevision:number}|{kind:rejected;reason:string};functionmarkCloudSynced(reply:SyncReply):boolean{returnreply.kindapplied||reply.kindalready_applied;}already_applied是超时重试的重要结果第一次可能已经提交只是响应丢了。服务端对同一operationId返回稳定的旧结果客户端才能正确清队列而不是把它误判为重复冲突。5. 网络可用不等于服务端可写设备连上 Wi-Fi却可能处于需认证的热点、DNS 故障、服务端超时或账号过期。Network Kit 的连接状态可以帮助调整 UI 和重试时机但每次同步请求仍要看 HTTP 结果与业务响应。对401应先处理登录态对冲突不能自动重试对连接超时可退避重试对明确的数据校验失败要停队列并提示修复。typeSyncFailure|offline|timeout|unauthorized|conflict|invalid_payload|server_error;functionshouldAutoRetry(reason:SyncFailure):boolean{returnreasonoffline||reasontimeout||reasonserver_error;}functionretryDelayMs(attempt:number):number{returnMath.min(60_000,1000*Math.pow(2,Math.min(attempt,6)));}指数退避是业务策略需叠加随机抖动避免大量客户端同时重连。后台运行和周期任务要服从当前系统资源限制不能靠死循环每秒探测服务器“保证实时”。6. 冲突界面给用户可理解的选择“版本冲突 409”对普通用户没有意义。展示“手机的未同步修改”和“平板已经保存的版本”标出时间与主要差异允许保留平板版、覆盖为手机版或另存为副本。覆盖也要产生新的基底修订而不是把原本baseRevision5的旧请求再发一遍。typeConflictChoicekeep_remote|overwrite_remote|save_copy;functionchooseNextOperation(choice:ConflictChoice,currentRevision:number,local:DraftSyncOperation):DraftSyncOperation|undefined{if(choicekeep_remote)returnundefined;return{...local,operationId:new-${local.operationId},baseRevision:currentRevision,newRevision:currentRevision1};}new-仅表示生成新操作意图不应在生产中用字符串拼接充当全局唯一 ID。另存为副本还需要新draftId示例函数故意只表现“不能复用旧操作”实际选择分支应由草稿仓库完成。7. 登录退出与隐私清理也属于同步链路多设备同步通常绑定账号。用户退出账号后尚未上传的本地草稿是留在设备、加密保留还是删除必须有明确策略换账号时绝不能把前一个账号的队列发到新账号。请求中ownerId应与当前登录会话匹配服务端还要再次验证资源归属。日志只记操作 ID、修订号与错误阶段不记录应援文案或私有路径。functioncanFlushQueue(queueOwnerId:string,currentOwnerId?:string):boolean{return!!currentOwnerIdqueueOwnerIdcurrentOwnerId;}这是一条最基本的隔离边界。跨账号切换、游客模式和账号注销都应有独立测试不应靠“退出时清空页面”来当数据清理。8. 用可控断网验证重复与覆盖准备手机、平板、测试账号和可控制的代理/测试服务。手机提交一条操作后让服务端成功但丢弃响应客户端重试应得到already_applied云端只有一份作品手机离线改文字、平板在线改颜色手机恢复网络后应进入冲突界面不覆盖平板。最后强制结束手机应用再启动待同步队列仍能恢复。故障注入应见结果服务端成功但响应丢失同操作重试不重复创建同草稿三次快速修改按顺序提交不出现旧状态回写两台设备同时编辑条件写入返回冲突用户可选离线后重启应用本地草稿与操作队列仍在退出账号再登录另一账号旧队列不发送给新账号弱网同步的核心不是“失败多试几次”而是给每次编辑可追踪的身份、可比较的版本和可解释的状态。本文代码是前后端契约设计没有后端事务、目标设备和真实断网演练时不能说参考工程已完成云同步。