ArkTS+AGC构建鸿蒙记账应用:从架构设计到云函数集成的实战笔记
2026/8/31 12:19:11 网站建设 项目流程

简介:本资源是一套基于HarmonyOS生态的ArkTS语言实战项目——网吧会员终端应用,面向鸿蒙应用开发者、高校移动开发学习者及转岗工程师,解决从零构建完整商用级鸿蒙应用的核心能力训练问题。项目覆盖引导页、登录注册、密码管理、网吧浏览、会员充值、数据查询等12个核心功能模块,所有业务数据统一落库至关系型数据库(SQLite)并辅以首选项存储用户轻量配置,体现鸿蒙平台典型的数据持久化实践路径。压缩包含343个文件,主体为24个ArkTS源码(.ets)、77个JS逻辑脚本、37个JSON配置、40张JPG/PNG资源图及36个protobin协议文件,总大小36.58MB,结构清晰、模块解耦,便于逐层理解UI组件、状态管理与数据库交互逻辑。已有227人下载学习,可直接导入DevEco Studio运行调试,获取完整可执行HAP包、分层代码结构、真实业务场景下的ArkTS语法应用范例及跨模块通信实现方案。 最近几个月我一直在折腾 HarmonyOS 应用开发,主力语言就是 ArkTS。手上的项目是一个完整的个人记账账单服务系统:前端用 ArkTS 在 DevEco Studio 里从零搭起来,后端直接挂在 AGC(AppGallery Connect)上,包含用户认证、账单记录、分类管理、统计分析、预算管理五个核心模块。这篇文章把从技术选型到每个功能落地的完整过程整理一遍,适合刚接触 HarmonyOS 开发、想用 ArkTS 独立完成一个含前后端真实项目的开发者参考。

这篇文章不是贴一堆官网文档,而是把我实际开发中踩过的坑、验证过的写法、取舍时考虑的理由一次性讲清楚。你把它当一份"带评论的实操笔记"来看就行,照着思路走,能少走不少弯路。

1. 项目定位与整体设计:为什么选 HarmonyOS + ArkTS 做记账应用

1.1 ArkTS 到底适合做什么样的项目

先说结论:ArkTS 是 HarmonyOS 原生应用的首选开发语言,基于 TypeScript 语法做过裁剪和强化,配合 ArkUI 声明式 UI 框架,写页面比传统命令式 UI 要快很多。它和 JS/Java 那套老开发方式最大的区别是:状态驱动视图——你把数据声明成状态,数据一变,界面自动跟着刷新,不需要手动操作 DOM 或组件节点。

我在设计这个项目时,第一反应不是"能不能用 ArkTS",而是"这个项目的复杂度和 ArkTS 的定位匹不匹配"。记账类应用有几个特点:页面数量多(列表、表单、报表、设置)、数据状态频繁变化(账单增删改、预算进度更新)、还需要和云侧数据实时同步。这些场景刚好是声明式 UI 的强项。反过来,如果你只是做个静态展示页,用 ArkTS 反而有点杀鸡用牛刀。

另一个现实因素是生态。现在 HarmonyOS 的第三方组件库虽然没有 Android 那么丰富,但核心场景基本都覆盖了。记账系统里最需要的表格、图表、下拉刷新、日期选择器,在 OpenHarmony 三方库中心都能找到可用版本。而且项目里用的是 AGC 后端,ArkTS 前端调用 AGC 云函数、云数据库的 SDK 也是官方维护的,链路是通的。

1.2 个人记账系统的业务需求拆解

这个系统,本质上就是"个人的轻量级财务管家"。我在整理需求时,把它拆成了五个模块,每个模块的边界要非常清晰:

  • 用户认证:注册、登录、退出登录,以及登录态保持。后端用 AGC 的认证服务,支持手机号、邮箱、匿名登录,我最后选了邮箱 + 密码的方案,便于测试和后期扩展。
  • 账单记录:核心功能,支持收入和支出两种类型,每条账单包含金额、分类、备注、日期四个必要字段。支持按时间范围筛选、按分类筛选。
  • 分类管理:预置常用分类(餐饮、交通、购物、工资等),同时允许用户自定义分类。分类是账单分析的基础,设计时必须保证每个账单只能归属一个分类。
  • 统计分析:按月份、按分类汇总收支金额,用饼图展示支出占比,用折线图展示近六个月的收支趋势。
  • 预算管理:用户可以给某个分类设置月度预算,或者设置总体月度预算,系统实时计算剩余额度,超支时给出提醒。

数据流设计上,前端所有的写操作都通过 AGC 云函数完成,云函数负责校验参数和写云数据库;读操作优先走云数据库的查询接口,配合本地缓存做加速。用户身份通过 AGC 认证服务统一管理,每个账单记录带 userId 字段,保证用户之间的数据完全隔离。

2. 开发环境搭建与工程架构

2.1 DevEco Studio 环境配置要点

开发这个项目,我用的 DevEco Studio 是 5.x 版本,API 级别按最新稳定版来。环境配置有几个容易出问题的地方,先说清楚:

  • SDK 下载:首次创建工程时,IDE 会提示下载 HarmonyOS SDK,网络不好的话容易失败。我建议在设置里先配置好 SDK 镜像源,再创建工程。
  • 签名配置:真机调试必须配置签名,否则装不上设备。项目刚创建时用的自动签名,第一次连真机时,需要在 Project Structure 里检查签名证书是否已生成,并且让手机开启开发者模式、连接电脑后授权调试。
  • 模拟器 vs 真机:纯 UI 阶段我用模拟器跑,但涉及 AGC 认证、云数据库时,模拟器偶尔会有网络或服务异常,这时候直接换真机。

DevEco Studio 创建工程时语言选 ArkTS,模板选 Empty Ability 就行。生成的工程默认带 entry 模块,所有页面代码都在 entry/src/main/ets 下面。这个工程结构后面可以自己扩展成多模块,但对于记账系统这种规模的 App,单 entry 足够,别过度设计。

2.2 工程目录与代码分层

我最终的代码结构是这样的,按"UI 层 / 业务层 / 数据层"三层来组织:

entry/src/main/ets/ ├── entryability/ // 应用入口 │ └── EntryAbility.ets ├── pages/ // 页面层 │ ├── LoginPage.ets │ ├── BillListPage.ets │ ├── BillEditPage.ets │ ├── CategoryPage.ets │ ├── StatisticsPage.ets │ └── BudgetPage.ets ├── components/ // 可复用的 UI 组件 │ ├── BillCard.ets │ ├── SummaryBar.ets │ └── CategoryIcon.ets ├── model/ // 数据模型 │ ├── BillModel.ets │ ├── CategoryModel.ets │ └── BudgetModel.ets ├── service/ // 云服务调用封装 │ ├── AuthService.ets │ ├── BillService.ets │ └── StatsService.ets └── common/ // 常量、工具类 ├── Constants.ets └── DateUtils.ets

分层的好处是:页面里只负责 UI 渲染和用户交互,不直接碰云函数调用;所有跟 AGC 打交道的逻辑集中在 service 层,后面如果换后端实现,只改 service 层就行。我当时踩过的坑是,第一版把云函数调用写在页面里,结果多个页面都要查账单时,代码复制粘贴越来越乱,后来才统一抽到 service 层。

2.3 接入 AGC 后台服务

AGC 接入的流程,很多人觉得繁琐,其实核心就三步:

第一步,在 AppGallery Connect 控制台创建应用。登录 AGC 后台,新建项目,然后添加应用,包名要和 DevEco Studio 工程里的包名完全一致,否则后面 SDK 初始化会失败。包名建议用反域名格式,例如 com.example.mycashbook。

第二步,开通需要的服务。记账系统里我开通了认证服务、云数据库、云函数三个。认证服务用来处理用户登录;云数据库存账单、分类、预算数据;云函数放统计聚合等复杂业务逻辑。在控制台里都是一键开通,但要注意后续的权限配置和集合创建。

第三步,把 agconnect-services.json 文件放到工程指定位置。这个文件是 AGC 服务的"身份证",包含应用 ID、密钥等信息。下载后放到 entry/src/main/resources/rawfile 目录下,然后代码里就能用 AGC 的 SDK 了。第一次配置时容易漏掉这个文件,导致运行时报"AGC SDK not initialized"之类的错误,别问我怎么知道的。

3. 核心业务模块的实现思路与关键代码

3.1 用户认证模块:用 AGC Auth 做登录和登录态保持

AGC 认证服务支持多种认证方式,我选的是邮箱注册登录。之所以不选手机号,是因为测试阶段邮箱不需要真实走短信验证,流程更顺。

在 ArkTS 里调用认证服务,第一步是初始化,然后调用注册接口。这里直接贴核心代码:

import { auth } from '@kit.AGCKit'; export class AuthService { static async register(email: string, password: string): Promise<boolean> { try { const result = await auth.AuthClient.getInstance() .createUser({ email: email, password: password }); return result.user != null; } catch (error) { console.error('register failed: ' + JSON.stringify(error)); return false; } } static async login(email: string, password: string): Promise<boolean> { try { const result = await auth.AuthClient.getInstance() .signInWithPassword({ email: email, password: password }); return result.user != null; } catch (error) { console.error('login failed: ' + JSON.stringify(error)); return false; } } }

这里有个经验:注册和登录接口都要做错误捕获,而且要把错误码映射成用户能看懂的中文提示。比如"用户已存在""密码错误""网络异常",不能直接弹一坨英文错误对象给用户看。

登录态保持也要单独处理。AGC Auth 默认有本地会话保持,App 杀掉再重启后,理论上用户还是登录状态。但我发现,偶尔会出现会话失效但本地没感知的情况。所以我在 EntryAbility 的 onWindowStageCreate 里加了一个令牌校验逻辑:启动时调用 getUser 接口,如果取不到用户,就强制跳转登录页。

3.2 账单记录与分类管理:数据模型与增删改查

账单记录是整个系统的心脏。我先定义 ArkTS 数据模型:

export class BillItem { id: string; amount: number; categoryId: string; type: 'expense' | 'income'; note: string; date: string; // 格式 yyyy-MM-dd HH:mm:ss userId: string; constructor() { this.id = ''; this.amount = 0; this.categoryId = ''; this.type = 'expense'; this.note = ''; this.date = ''; this.userId = ''; } }

注意,ArkTS 语法上对类字段声明有要求:所有字段必须显式初始化,不能在构造函数里才赋值。我第一次用 TypeScript 的习惯直接写id: string,编译直接报错,必须给初始值。这一点是 ArkTS 和标准 TS 的一个明显差异,后面会专门讲。

云数据库里,我建了一个叫 bills 的集合,每条记录的字段和 BillItem 类一一对应。新增账单时,前端把数据传给云函数,云函数校验通过后写入数据库,然后返回给前端新纪录的 id。前端拿到 id 后,直接把这条记录追加到本地的账单列表状态里,不需要重新拉全量数据,这样列表刷新会非常快。

分类管理相对简单。预置分类放在一个常量表里,用户自定义分类则写入 categories 集合。分类数据在 App 启动时加载一次,缓存在全局单例里。因为分类数量少,不需要做分页,一次查全。

3.3 统计分析模块:云函数聚合 + 图表展示

统计是记账系统里最有技术含量的模块。要实现"按月汇总各分类支出""近六个月收支趋势",最简单的做法是前端把所有账单拉下来,在本地算。但如果账单数据量到几千条,这种方案会越来越卡。我在初期就决定把聚合逻辑放到云函数里,数据库算完只返回汇总结果,前端只负责展示。

这里贴一个云函数里统计月度分类支出的核心逻辑(云函数运行环境是 Node.js,语法是 JavaScript/TypeScript):

exports.myHandler = async function (event, context, callback) { const { cloud } = require('@agconnect/cloud'); const cloudDb = cloud.database(); const { userId, month } = event; const startTime = new Date(`${month}-01T00:00:00`); const endTime = new Date(startTime); endTime.setMonth(startTime.getMonth() + 1); try { const res = await cloudDb.collection('bills') .where({ userId: userId, type: 'expense', date: { $gte: startTime, $lt: endTime } }) .limit(1000) .get(); // 在云函数里做聚合 const stats = {}; res.data.forEach(bill => { const catId = bill.categoryId; if (stats[catId]) { stats[catId] += bill.amount; } else { stats[catId] = bill.amount; } }); callback(null, stats); } catch (err) { callback(err); } };

前端 ArkTS 调用云函数时,我用的是@kit.AGCKit里的 cloud 调用接口,把 userId 和 month 作为参数传进去,拿到返回的 stats 对象,再映射到图表组件上。

图表组件方面,我用了三方库里的图形组件来做饼图和折线图。饼图的每个扇区对应一个分类,颜色从分类配置里取。一开始我用了默认配色的饼图,视觉效果一般。后来在统计页顶部加了一个渐变背景,用 ArkUI 的 linearGradient 接口,从黑色 #000000 透明度 80% 渐变到完全透明,背景色和饼图形成对比后,整体高级感提升了不少。具体写法:

LinearGradient({ angle: 180, colors: [['#000000', 0.8], ['#00000000', 0.0]] })

这个写法里,0.8 表示颜色在渐变起始位置的透明度比例,0.0 是终点位置完全透明。ArkUI 的 linearGradient 颜色数组用的是[color, position]二元组,position 表示该颜色在渐变中的位置。如果你想做从上到下的渐变,angle 用 180 度就可以。

3.4 预算管理模块:实时计算剩余额度和超支提醒

预算管理模块的核心是数据结构设计和进度计算逻辑。我在 budgets 集合里存了三种预算:

  • 总体月度预算:一个用户一条记录,字段包含 budgetAmount、month、userId。
  • 分类月度预算:每个分类一条记录,包含 categoryId、budgetAmount、month、userId。
  • 单笔限额提醒:这个属于附加功能,比如"单笔超过 1000 元时提醒",我用一个统一字段用于标记。

预算进度计算,也是放到云函数里做。逻辑是:输入 userId 和 month,云函数先取该月账单汇总金额,再取预算总额,然后算出已用比例和剩余金额。如果用户只想看某个分类的预算进度,就传 categoryId 参数,云函数按分类统计。

前端展示上,预算页面用进度条组件展示已用百分比,并用颜色区分状态:正常状态绿色、接近 90% 黄色、超支红色。超支提醒我用了两种方式:一种是页面内弹窗,另一种是系统通知。系统通知需要申请通知权限,并且要处理用户拒绝授权的情况。第一版我直接弹窗提醒,用户反应不够友好,后来加了通知中心的通知,体验才好起来。

4. 开发过程中踩过的坑与排查技巧

4.1 ArkTS 语法限制:和 TypeScript 的差异对照

ArkTS 是基于 TS 的,但不是所有 TS 特性都支持。这个项目的开发过程中,我整理了一份"ArkTS 踩坑对照表",非常有用:

场景标准 TS 写法ArkTS 里的正确写法
变量类型let data: any = {...}let data: Record<string, Object> = {...}
类字段id: string;id: string = '';必须给初始值
对象字面量const obj = { a: 1 }需显式声明类型const obj: MyType = { a: 1 }
解构赋值const { name } = data部分场景不支持,建议逐字段赋值
联合类型type A = string | number基本支持,但对象类型尽量用接口
方法重载function f(a: string): void不支持,用可选参数代替

第一版代码里,我大量使用了any类型和对象解构,结果编译时报了几十个错误。后来学乖了,所有数据模型都用 class 或 interface 定义好,后端返回的数据通过一个 mapper 函数逐字段复制到前端模型里,虽然代码多写了几行,但类型安全让后续维护省心太多。

还有一个细节:ArkTS 里Record<string, Object>用起来比any安全,但取值时要先做类型转换,否则拿到的类型不确定。比如:

let obj: Record<string, Object> = { value: 100 }; let v: number = obj['value'] as number;

4.2 node-gyp 与鸿蒙原生依赖的兼容问题

有段时间我在项目里想引入一个带原生代码的三方库,结果编译时一直报 node-gyp 相关错误。node-gyp 通常用于 Node.js 原生模块编译,在 HarmonyOS 开发里出现,主要嫌疑是某个依赖在安装阶段尝试编译原生命令行工具,但环境里缺少对应构建链。

排查思路是这样的:先看报错栈里有没有 node-gyp rebuild 字样;有的话,确认电脑上是否装了 Python、C++ 构建工具和对应版本的 Node.js。我按官方文档装齐之后,错误就消失了。如果你也遇到这个坑,先不要动工程配置,先补环境:

  • 安装 Python 3.x,并加入系统 PATH
  • 安装 Visual Studio Build Tools,勾选 C++ 开发组件
  • 安装 Node.js LTS 版本,和 DevEco Studio 要求的版本匹配

如果补完环境依然报错,那就得考虑放弃这个带原生代码的库,换一个纯 JS/TS 实现的库。HarmonyOS 生态里不是所有库都做过原生适配,这一点在选型时要提前查清楚。

4.3 第三方组件库选型:下拉刷新和图表库的取舍

第三方库方面,我试过下拉刷新库 pulltorefreshv2。这个库的交互效果很流畅,但接入时要注意版本和 HarmonyOS API 的匹配。我第一版引入的版本已经和 API 12 兼容,后来升级到 API 13 后,发现刷新动画有点异常,查了版本记录才知道要同步升级到 v2 的更高版本才能兼容。

图表库也踩过坑。我试过用@ohos生态里一个 Canvas 图表库,功能是挺全,但打包体积太大,而且冷启动时图表初始化有明显的卡顿感。后来我换了一个体积更小的图表组件,虽然配置项少一些,但针对记账系统的饼图和折线图,已经完全够用。这里给个经验:第三方库不是越大越全就越好,要自己的页面复杂度来选。

4.4 AGC 云函数冷启动与数据同步问题

AGC 云函数是 Serverless 架构,存在冷启动的问题。所谓冷启动,就是函数长时间没被调用时,下一次请求会有一个初始化耗时,有时候可能要 1~3 秒。记账系统在打开统计页时,如果正好赶上冷启动,那个转圈动画会明显卡顿。

我的解决办法有两个:一是启动 App 后,在后台预加载当月账单数据,提前触发云函数调用,等于人为"预热";二是前端做好加载状态,在云函数返回前先展示本地缓存的统计数据,返回后无缝刷新。实测下来,用户感知到的等待时间大幅降低。

数据同步方面,AGC 云数据库有实时数据推送的能力,但我没有选择实时监听整个 bills 集合,而是采用"增删改后手动刷新列表"的策略。理由很简单:记账场景下,数据变化频率低,而且只影响当前用户,手动刷新足够,还能省掉监听带来的复杂逻辑和数据流量。

4.5 几个高频问题的排查速查表

开发到后期,我把团队里同事们常遇到的问题整理成一个速查表,特别适合新手:

问题现象可能原因解决办法
编译报错Property 'xxx' does not exist对象被隐式推断成窄类型给对象变量显式加类型注解
真机调试时提示签名失效调试证书过期或设备未信任在 DevEco Studio 中重新生成证书,并到手机设置里信任开发者证书
AGC 云数据库写入失败集合权限未开放到 AGC 控制台配置数据库安全规则
调用云函数超时函数默认超时时间太短在云函数配置里调大超时时间,例如从默认值调至 30s
页面刷新后数据消失没有做本地持久化用 Preferences 或关系型数据库做本地缓存
渐变背景不生效linearGradient 位置系数范围错误确认 position 值在 0.0~1.0 之间

这里特别说一下数据库安全规则。AGC 云数据库在不同业务场景下访问权限需要人工配置。我的规则是:所有数据库读写都通过云函数代理,所以数据库本身对客户端关闭了直接读写权限,只允许云函数访问。这个规则在 AGC 控制台的数据库模块里配置,语法类似 JSON,配置一次后,所有客户端的直连请求都会被拒绝。

5. 项目最后的优化方向与个人经验总结

整个项目做到这里,功能已经完整,应用也能在真机上稳定跑起来。但我还留了几个优化方向,给后续迭代做准备:一是多设备适配,目前主要布局针对手机,后续要适配平板折叠屏;二是数据导出功能,用户可以把账单导成 CSV 文件,方便自己做备份和深度分析;三是桌面卡片,HarmonyOS 的特征之一就是服务卡片,做一个"当月预算进度"的卡片放在桌面上,比打开 App 看进度要方便得多。

最后说点个人体会。如果你之前只写过 Web 前端或只写过 Android,ArkTS 的上手门槛其实没有想象中那么高。它保留了声明式 UI 的思想,只要你理解"状态驱动视图"这个核心概念,大部分页面开发都非常顺。真正花时间的地方反而是跟 AGC 服务集成、处理类型约束、排查原生依赖兼容这类"坑"。

我自己在开发这个项目的过程中,最大的收获不是会调用了多少 API,而是建立了一套"先定义好模型和接口,再写页面"的工程习惯。以前写小项目喜欢边写边改,到了 ArkTS 里,因为类型系统严格、前后端数据契约必须清晰,这种习惯逼着我把所有数据模型、云函数出入参提前定义好,反而让整个开发过程变得很顺畅。

如果你也要用 ArkTS 做一个类似的业务系统,我给的建议是:第一步,先把数据模型定义清楚,包括本地实体和云数据库字段结构;第二步,把 AGC 服务接入和调试跑通,再做具体页面;第三步,页面开发时严格分层,不要图省事把业务逻辑全堆在组件里。做到这三点,你的项目大概率不会在中途陷入"改一处坏一处"的泥潭。

个人记账应用只是一个样板,ArkTS 能做的事情远不止于此。把这个流程玩熟以后,你会发现,用 HarmonyOS 原生技术栈做全栈应用开发,比想象中更顺手。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询