鸿蒙个人记账本实战:端云一体架构下如何做数据同步与聚合统计

举报
浩轩丶 发表于 2026/11/15 01:33:08 2026/11/15
【摘要】 记账类应用的核心难点不在界面,而在数据一致性:本地先写还是云端先写、断网怎么办、多设备如何合并、统计口径怎么统一。本文结合鸿蒙实践案例的记账本,梳理端云一体架构下的同步策略与聚合统计实现。

为什么记账应用值得研究

记账本看起来是最简单的一类应用:记一笔、查一下、算个总和。但真正做起来,问题全在数据层:

  • 用户在电梯里没信号,还能不能记账?
  • 同一笔支出在手机和平板上各记了一次,怎么去重?
  • 删掉一笔记录,云端要不要跟着删?
  • 月度统计的口径是按账单日期还是按记账时间?

这些问题在单机应用里都不存在,一旦上了端云一体架构就全部冒出来。鸿蒙实践中心的记账本案例(account_book)把这些问题做了一个示范性的解答。

一、数据模型设计

1.1 账单记录

核心实体是账单,字段大致是:

interface BillRecord {
  id: string;           // 全局唯一 ID
  amount: number;       // 金额,单位分
  category: string;     // 分类:餐饮、交通、购物...
  type: number;         // 0 支出 1 收入
  billDate: number;     // 账单发生时间
  createdAt: number;    // 记录创建时间
  updatedAt: number;    // 最后修改时间
  deleted: boolean;     // 软删除标记
  synced: boolean;      // 是否已同步到云端
}

几个设计要点值得说:

  1. 金额用整数分而不是浮点数。0.1 + 0.2 在浮点下不等于 0.3,账目上出现一分钱的误差会引起用户质疑。全部按分存储,显示时除以 100。
  2. billDate 和 createdAt 分开。用户可能今天补记上周的账,账单日期是上周,但记录是今天创建的。统计时按 billDate 算,同步冲突判断时用 updatedAt。
  3. 软删除。直接物理删除会导致同步时无法区分「云端有、本地没有是因为删了」还是「因为还没拉下来」。加 deleted 标记,删除变成一次普通更新。

1.2 本地存储选型

鸿蒙提供了几种本地存储方案:

方案 适用场景
Preferences 键值对配置,不适合大量结构化数据
关系型数据库 (RDB) 结构化账单数据,支持 SQL 查询和聚合
分布式数据对象 多设备实时同步的轻量场景

记账本用 RDB 最合适,因为统计需要 GROUP BY、SUM 这类聚合操作,用 SQL 一行就能算出来。如果自己用数组遍历,数据量一大就卡。

二、端云同步策略

2.1 本地优先写入

记账这个场景,用户的期望是「点下保存就该立刻看到」。所以写入顺序一定是本地优先:

  1. 写入本地数据库,标记 synced = false
  2. 界面立即刷新,用户看到记录出现
  3. 后台异步推送到云端
  4. 推送成功后把 synced 置 true

如果反过来先等云端响应,弱网环境下用户要盯着加载圈好几秒,体验很差。而且一旦超时,用户会重复点击,产生重复记录。

2.2 冲突解决:最后写入胜出

多设备场景下,同一条记录可能在两端被修改。常见的解决方案有三种:

策略 说明 适用性
Last Write Wins 比较 updatedAt,后改的覆盖先改的 记账场景够用
版本号乐观锁 每次更新 version+1,不一致则拒绝 更严格,但需要用户处理冲突
CRDT 数学上保证最终一致 过重,记账不需要

记账本用 LWW 就够了。因为账单的修改频率很低,同一笔账在两端同时修改的概率微乎其微。真发生了,用户发现金额不对重新改一次即可,成本很低。

2.3 增量同步

每次同步全量拉取数据是不现实的。做法是记录一个 lastSyncTime,每次只拉取 updatedAt > lastSyncTime 的记录:

GET /bills?since=1700000000000

服务端返回这段时间内新增和修改的所有记录,客户端按 id 合并。注意要包含已删除的记录(deleted=true),否则删除操作同步不过去。

三、聚合统计的实现

3.1 月度汇总

最常用的统计是「这个月花了多少」。用 SQL 一条搞定:

SELECT type, category, SUM(amount) as total, COUNT(*) as cnt
FROM bills
WHERE deleted = 0
  AND billDate >= ? AND billDate < ?
GROUP BY type, category

把起止时间按自然月算好传进去,就能拿到按分类拆分的收支汇总。同比环比只需要换两次时间参数再查一遍。

3.2 趋势图数据

折线图需要按天聚合:

SELECT strftime('%Y-%m-%d', billDate / 1000, 'unixepoch', 'localtime') as day,
       SUM(CASE WHEN type = 0 THEN amount ELSE 0 END) as expense,
       SUM(CASE WHEN type = 1 THEN amount ELSE 0 END) as income
FROM bills
WHERE deleted = 0 AND billDate >= ?
GROUP BY day
ORDER BY day

注意时区处理。billDate 存的是毫秒时间戳,直接按 UTC 格式化会让凌晨的账单算到前一天。加 localtime 修正。

3.3 统计口径要一致

一个容易被忽略的坑:列表页显示的条数和统计页算出的笔数经常对不上。原因通常是两处用的过滤条件不同——列表可能默认只显示本月的,但统计按全部算;或者列表把软删除的记录过滤了,统计没有。

解决办法是把过滤条件抽成一个公共方法,两处都调用它。这类一致性问题在数据量小的时候看不出来,用户数据积累半年后就会暴露,那时候排查很痛苦。

四、几个实战细节

4.1 分类的层级设计

餐饮下面有正餐、外卖、咖啡,交通下面有地铁、打车、加油。用两级分类比单级更实用,但要注意统计时既要能看小类,也要能汇总到大类。数据库里存子类 id,同时冗余一个父类 id,查询时就不用做连表。

4.2 金额输入的防错

输入框要做几件事:限制只能输数字和一个小数点、小数点后最多两位、不允许以小数点开头。提交前再校验一次金额大于 0。这些校验放在一个统一的函数里,收入支出两个入口都调用。

4.3 同步状态的可见性

列表项上可以加一个小标记,表示「未同步」。用户看到就知道这条还没上云,心里有底。全部同步完成后标记消失。这个小细节能大幅降低用户对数据安全的焦虑。

五、小结

记账应用的技术含量集中在数据层,界面反而是最简单的部分。这套端云一体的思路可以迁移到很多场景:

  1. 本地优先写入,云端异步同步
  2. 软删除代替物理删除,保证同步可追溯
  3. 用 updatedAt 做增量拉取和冲突判断
  4. 聚合统计全部放在数据库层,不在内存里算
  5. 过滤条件统一封装,避免口径不一致

把这几点做好,一个记账本的数据层就相当可靠了。剩下的工作主要是界面打磨和报表可视化,那些更多是审美问题,不是工程问题。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。