鸿蒙端云一体待办清单实战手记:ArkTS 端侧 + Serverless 云侧的任务同步设计
一、为什么选待办清单练端云一体
待办清单看起来简单,却是练习端云一体的好题目:它的状态模型很小,一条任务只有标题、完成状态、优先级、截止日期和时间戳几个字段;但它的同步语义一点也不简单——同一条任务可能在手机上被改了优先级,又在另一台设备上被标记完成,云端必须给出一个确定的结果。把这个小题目做扎实,端云协同的基本功就练到了:端侧负责交互与离线体验,云侧负责收口数据与裁决冲突,两边各司其职,接口就是契约。
本文记录一次鸿蒙端云一体待办清单的实战过程:端侧用 ArkTS 与 ArkUI,云侧用 8 个 FunctionGraph 云函数加 API 网关收口,注册登录、任务增删改、优先级与截止日期全部收口云端,支持多端同步。代码思路与验证方法都可以在自己的环境里复现。
二、整体架构:端侧轻、云侧收口
架构分三层。端侧是 HarmonyOS 应用,用 ArkTS 写业务逻辑,ArkUI 写声明式界面;网络层只做一件事:把请求发给 API 网关,把响应转成页面状态。云侧入口是 API 网关,对外只暴露一个域名,负责鉴权、限流和路由;网关后面是 8 个 FunctionGraph 云函数,每个函数只做一件事;数据层用云数据库保存任务,函数本身不保存任何状态,扩容与发布都不影响数据。
这条链路的好处是职责清晰:界面改版不用动云函数,云函数升级不用重新发版端侧;坏处也同样明显——链路一长,排障就要分段:先看端侧日志确认请求是否发出,再看网关日志确认是否路由,最后看函数日志确认是否执行。实战中我给每个环节都留了可观测的点:端侧打印请求编号,网关按请求编号查日志,函数入口先打一条包含请求编号的日志,三段日志用同一个编号串起来。
三、数据模型:字段越少,同步越稳
任务表只保留必要的字段:任务编号、归属用户、标题、是否完成、优先级、截止日期、创建时间与更新时间。优先级只取高、中、低三档;截止日期存 UTC 时间戳,不存本地格式化的字符串——字符串一旦混进时区,排序与提醒都会错。更新时间是同步裁决的依据:每次写操作都由云侧生成新的更新时间,端侧不参与生成,避免了设备时钟不一致带来的乱序。
注册登录也收口云端:端侧只保存令牌,不保存密码;令牌过期后用刷新接口换新令牌,刷新失败再回到登录页。这个顺序不能反过来——先刷新、再重试原请求,而不是直接把用户踢回登录页,前者用户几乎无感,后者每次过期都要重新登录。
四、八个云函数怎么分工
八个云函数按读写分工:注册与登录两个函数管账号;创建任务、查询任务列表、更新任务、删除任务、切换完成状态五个函数管任务的增删改查;再加一个按优先级与截止日期筛选的查询函数,列表排序与筛选全部放云端做,端侧只负责展示。
分工时我坚持了两条原则。第一,函数无状态:函数不缓存用户数据,每次调用都从数据库读最新值,函数实例随时可以被回收与新建。第二,一个函数只做一件事:切换完成状态就是单独的函数,不复用更新任务接口——复用看似省事,实际上会把“改标题”和“改状态”两种语义混在一起,后续加审计、加权限校验都会互相牵制。函数之间不直接调用,只通过数据库与网关协作,这样某个函数出问题时,影响面就是一个接口,而不是整条链路。
五、API 网关:端侧只认一个域名
端侧不直接调云函数,所有请求都走 API 网关。网关做三件事:鉴权——校验令牌,拒绝未登录的请求;路由——按路径把请求分发到对应的云函数;限流——给高频的列表查询接口单独设限额,避免一次误操作把函数打满。
网关的返回体我定了统一格式:成功时返回业务数据,失败时返回错误码与可读的提示。端侧据此做分支:鉴权失败去刷新令牌;参数错误直接提示用户;服务端错误显示“稍后重试”并记录请求编号。统一格式的价值在排障时最明显:不用逐个接口猜返回结构,看到错误码就知道该查哪一段。
六、端侧实现:页面、逻辑与数据三层分开
端侧分三层。页面层只有 ArkUI 组件与 @State、@Prop 状态,负责渲染列表、展示优先级颜色、弹出日期选择器;逻辑层是纯函数与视图模型,负责把任务列表按优先级与截止日期排序、把用户的点击翻译成网络请求;数据层封装网络请求与本地缓存,负责发请求、解析响应、写本地文件。
网络请求单独封装成一个模块:每个请求带上令牌与请求编号,响应先校验统一格式再交给逻辑层。列表页打开时先读本地缓存立即渲染,再发请求拉最新数据覆盖——用户看到的永远不是白屏,而是“先有旧数据、再刷新成新数据”。
七、同步策略:乐观更新与后写胜出
同步用两条规则。第一是乐观更新:用户点“完成”后,界面先把任务标成完成,再把请求发往云端;请求失败时再把界面恢复成之前的状态并提示重试。乐观更新的代价是失败时要回滚,所以每一次乐观更新都要先把旧状态保存下来,回滚时有据可依。
第二是后写胜出:云端以更新时间裁决冲突,更新时间更大的写操作胜出;离线期间的修改先记在本地队列里,联网后按队列顺序依次提交,提交时带上本地的更新时间。删除用软删除:删除操作只把任务标记为已删除并更新时间,不真正删行——否则一台离线的设备在联网后把旧数据重新推上来,已删除的任务会“复活”。软删除的记录定期清理,清理前多端都已同步过删除标记。
八、容易踩的坑
第一个坑是时区。截止日期在端侧用本地时间展示没有问题,但入库必须转成 UTC 时间戳;如果把本地格式化的字符串直接存进数据库,换一台时区的设备登录,同一条任务的截止日期就会差几个小时。
第二个坑是重复提交。用户在网络差的时候多点一次“保存”,云端可能收到两次创建请求。解决办法是给创建请求带一个幂等键:端侧在新建任务时生成一个唯一键并随请求发送,云端用这个键去重,重复的请求直接返回第一次的结果。
第三个坑是排序放错地方。任务列表的排序必须在云端做,用数据库的排序一次返回有序结果;如果把全量数据拉回端侧再排序,数据量一大,端侧内存与流量都会涨,而且分页会错乱——先分页再排序与先排序再分页,结果完全不同。
第四个坑是令牌过期时机。令牌不要等到请求返回 401 才处理:端侧在发起请求前先检查令牌剩余有效期,快过期就先刷新,再发业务请求;已经返回 401 的请求,刷新后重试一次,重试仍然失败才回到登录页。
九、验证清单
做完之后,我按这份清单逐项验证:同一账号在两台设备登录,改一台的优先级,另一台刷新后同步;断网时新建三条任务,联网后全部出现在云端且不重复;断网时删除一条任务,联网后另一台设备上这条任务消失而不是复活;截止日期按从近到远排序,跨时区展示一致;连续点两次保存,只产生一条任务。
十、小结
待办清单的端云一体,核心不是写了多少代码,而是把三件事定下来:数据以云端为准,端侧只做展示与乐观更新;冲突用更新时间裁决,删除用软删除防复活;接口格式统一,排障能按请求编号分段定位。这三件事定下来之后,再加提醒、再加共享、再加统计,都是在同一套规则上扩展。下一步我打算给这个待办清单加上截止日期提醒:云侧按截止日期扫描即将到期的任务并推送提醒,端侧只负责展示提醒——提醒的触发条件与去重逻辑,仍然放在云侧收口。
- 点赞
- 收藏
- 关注作者
评论(0)