鸿蒙个人记账本:分类占比为何放云端算——一次浏览、Star 与 AI Shell 追问
今天在鸿蒙开发实践中心看的是“个人记账本”这个端云一体案例。端侧用 ArkTS 写了一个三 Tab 的记账 App——记账、明细、统计;云端没有用托管 Serverless,而是在一台华为云 ECS 上自托管 Appwrite 1.9,由它提供数据库、认证和云函数。案例的完成形态很具体:6 个云函数、19 项端到端验收(verify-api.mjs 19/19)、联调验收 6 项全过,仓库在 GitCode 的 harmony-practice-center/account_book。今天我把案例详情页从头读完,又在 AI 开发者空间的 AI Shell 里就统计设计追问了一轮,顺手给仓库点了 Star——Star 数从 520 变成了 521,之前没有 Star 过这个仓。这篇文章只写今天实际看到、实际问到的东西。
一、一条记账请求走过哪些环节
先把链路说清楚,后面的取舍才有落点。
端侧只带公开的项目 ID 调云函数。一次“记一笔账”,端侧把业务参数 JSON 放进 POST /v1/functions/{fnId}/executions 的请求体,带上 X-Appwrite-Project 头交给云函数;云函数先校验金额、分类和这笔账的归属,校验通过才写入 transactions 集合。登录注册、增删查也一样,都是云函数收口。
统计页的流程反过来:切换月份时,端侧不自己算,而是调 get-stats,让云函数在云端拉取该用户当月全部记录,聚合出 7 类(餐饮、交通、购物、娱乐、居住、医疗、其他)的占比,返回给 App 渲染环形图。验收里有一条很实在:get-stats 返回的 categories 占比和是 100%。
数据层是 Appwrite 里的 account_book_db,两个集合:users(账号)和 transactions(账目)。端侧的登录态用 Preferences 存,登录后自动进入主页;配置只有两处:harmony_cloud/.env 里的 APPWRITE_ENDPOINT、APPWRITE_PROJECT_ID 和 API key,端侧 rawfile/app.config.json 里只同步 endpoint 和 projectId。
二、分类占比为什么放云端算
这是我今天问 AI Shell 的第一个问题。结合案例 README 和 AI 的回答,理由有四条,按重要程度排:
-
数据在哪,全量就在哪。端侧即使有缓存,缓存也是子集——多设备场景下,手机上记的账,平板本地可能还没同步完。端侧拿本地缓存算,算出来的是“这台设备当前已同步的数据”的占比;云函数直接查服务端,拿到的是权威全量,所有设备看到同一个数字。
-
传回来的只是聚合结果,不是原始流水。几千条记录拉到端侧再算,流量和耗时都大;云函数算完只回几个分类的数字,传输量从 KB 级降到百字节级。
-
逻辑改一处,全端生效。分类规则会变,比如把“外卖”从“餐饮”里拆出来。逻辑在端侧就得发版更新 App,老版本用户还停在旧分类;放在云函数里,改一次函数、重新部署,所有端立即生效,不用走应用市场审核。
-
权限收口。原始流水含金额,是敏感数据。云函数在服务端确认请求者只能看自己的数据,再返回聚合结果;端侧如果直接拉全量流水,等于端侧持有全部敏感明细,日志误写、缓存被读都会扩大泄露面。
反过来说,端侧算也不是一无是处:离线时想看统计,只能拿本地缓存算一个近似值,联网后再用云函数结果覆盖;数据量只有几十条时,拉全量算也无所谓;刚记完一笔就想立即看到统计刷新,端侧算没有网络延迟。所以更成熟的做法是“端侧算即时近似 + 云函数算权威精确”,案例只放云函数,是初期数据量不大、优先保证一致性和逻辑集中的合理简化。
三、密码怎么做,才算没有“裸奔”
这个案例的认证实现很值得单独写一段。它没有直接用 Appwrite 原生 Auth,而是在 users 集合里自定义实现——README 说得很坦白,原因是自托管 1.9 + node-16 的环境下原生 Auth 联调复杂,这是教学案例的一个取舍,不是推荐做法。
自定义归自定义,密码处理没有马虎:用 node:crypto 的 scrypt 做加盐哈希,再用 timingSafeEqual 做常量时间比较,防通过响应耗时猜测密码;密码不落明文,存储格式为 scrypt 加盐哈希的三段式(参数 N、盐值 salt、哈希值 hash)。密钥只存在本机 harmony_cloud/.env 和云函数环境变量,端侧不带 API key,.env 已进 .gitignore。
四、演示配置和生产之间,差的那几步
案例 README 给了“从演示到生产的最小改造清单”,我照着自己的理解排了个优先级,最要紧的第一条是收紧集合权限:当前演示配置下,任何人只要知道公开的项目 ID 就能读取全部数据,包括密码哈希,必须优先关闭。后面几条按顺序是:
- 把云函数 execute 从 [‘any’] 改为 [‘users’](仅登录用户可执行)或 [‘user:<uid>’],让未登录的调用在网关就被拦截;
- 接 Appwrite 原生 Auth / JWT,业务函数从认证上下文派生 user_id,不再信任客户端传入的 user_id;
- 上 HTTPS 和自有域名:现在密码是明文经 HTTP 传输,生产必须加密;
- API key 按最小权限授 scope,定期轮换;
- 在反代层限流,开启访问日志审计。
还有几个容易踩的坑,README 里都标出来了:冷启动实测约 1.5 秒,函数超时设 30 秒;时区按北京时间(UTC+8)硬编码处理,非东八区部署会错位;executions 返回 201,响应包一层 responseBody,端侧要按 201 判成功再解包;JSON 查询要用新格式,分页注意 limit 100。
五、AI Shell 追问的收获,和一次“纠错”
在 AI 开发者空间里,我把今天的核心问题原样抛给了 AI Shell:“为什么分类占比统计要放在云函数 get-stats 里做,而不是端侧直接算?如果要在统计页加一个近 6 个月月度趋势图,云侧和端侧分别要注意什么?”它给的答案里有三条可以直接用:按月 GROUP BY,不要把全量拉到函数内存里再循环算;“近 6 个月”按自然月算,从当月往前推 5 个月取 1 号;没有数据的月份也要补零返回,否则折线会断。
但它也犯了一个错,值得写出来:它先上网搜了这个案例,没搜到确切的源码文档,就按“鸿蒙 + AGC 云函数 / CloudDB”的通用架构来答。实际上这个案例的云端是 ECS 自托管 Appwrite,不是 AGC CloudDB。它给出的聚合原则(全量一致性、只传聚合结果、逻辑集中、权限收口)是对的,迁移到 Appwrite 架构上依然成立;但具体到查询写法、鉴权方式,就不能照抄它给的 CloudDB 伪代码。AI 回答要对照案例 README 和仓库源码核对,这是今天最实在的一条经验。
六、小结
今天这一圈下来,我做完了三件实事:把个人记账本案例的端云链路读了一遍,重点是“业务校验 + 统计聚合收口云端”这个职责划分;在 GitCode 上给 HarmonyOS-APP-account-book 点了 Star(从 520 到 521);在 AI 开发者空间里用 AI Shell 把“为什么统计放云端”和“月度趋势怎么做”问透了,顺带发现 AI 会把后端认错、需要人工核对。
下一步想做的,是顺着今天的问题,把 get-stats 扩展成按月汇总的接口:云侧按月聚合收支,空月补零,端侧只负责渲染。案例仓库:https://gitcode.com/harmony-practice-center/account_book ;案例详情页在鸿蒙开发实践中心,版本 v1.0.0,最后验证于 2026-08-19,compile API 24、兼容 6.1.0(23),Appwrite 1.9.0、node-16.0。
- 点赞
- 收藏
- 关注作者
评论(0)