钱袋整理师-智能记账分类的HarmonyOS开发实践
钱袋整理师:智能记账分类的鸿蒙开发实践
在鸿蒙生态中打造一款 AI 驱动的消费场景智能分类应用,从需求对齐到自动化执行的全流程技术复盘。
一、项目背景与动机
在个人财务管理领域,"记账"是基础,"分类"是核心,“预算"是目标。然而,大多数记账应用仅提供流水账的记录功能,用户需要手动对每一笔消费进行分类,久而久之便失去了记账的耐心。钱袋整理师(App15) 正是在这一痛点上发力——它不要求用户手动录入每一笔消费,而是通过场景化消费数据的智能分类,自动生成消费占比建议和优化方案,帮助用户"看一眼就了解自己的钱花在了哪里”。
钱袋整理师是鸿蒙 AI 应用工具箱(共 40 个 AI 应用)中的一员,定位为"金融消费"品类下的智能记账分类工具。整个项目基于 HarmonyOS NEXT(API 26)构建,采用 ArkTS 声明式 UI 框架,遵循鸿蒙应用的开发规范与设计语言。该工具箱涵盖了品质生活、健康运动、职场效率、学习成长、AI 创作和金融消费六大品类,每个应用都遵循统一的单页面组件化架构和 Mock 数据驱动策略,钱袋整理师正是这一架构模式在金融消费领域的典型应用。
本文将从 6A 工作流(Align、Architect、Atomize、Approve、Automate、Assess)的视角,完整复盘钱袋整理师的开发全过程。文章将深入分析 ArkTS 的接口定义、状态管理、组件化构建、AI API 桩模式(Stub Pattern)以及鸿蒙 PC 端适配策略,并结合鸿蒙 Flutter 框架的对比分析,为鸿蒙开发者提供一份可复用的技术参考。文中所有代码示例均来自实际项目源码,并经过了鸿蒙编译器的严格验证。
—

二、Align 阶段:从模糊需求到精确规范
2.1 原始需求分析
钱袋整理师的初始需求可以概括为一句简短的话:“帮助用户整理消费数据,给出预算建议”。这是一个典型的模糊需求,包含着大量未明确的信息。如果不经过系统化的需求对齐,开发者很容易走偏方向,导致最终交付的产品与预期相去甚远。
通过与产品方的对齐讨论,我们逐步明确了以下核心问题,并将模糊需求转化为可执行的技术规范:
第一个问题:输入是什么? 经过讨论,我们确定用户无需手动录入账单详情,而是通过选择一个消费场景(如餐饮、交通、购物等)来触发分析。这种"场景选择"而非"数据录入"的交互方式,大幅降低了用户的使用门槛,符合"轻量级 AI 应用"的产品定位。
第二个问题:输出是什么? 明确了输出应包含三个层次:该场景下的子类目预算占比(如餐饮场景中的日常三餐、外卖外食、零食饮料、社交聚餐各占多少比例),预算占比的进度条可视化展示,以及针对每个子类目的具体省钱建议文案。
第三个问题:AI 做什么? 在理想情况下,AI 应根据用户的历史消费数据生成个性化建议。但在 MVP(最小可行产品)阶段,AI API 可能尚未就绪,因此需要采用 Mock 数据模拟 AI 响应。这引出了"AI API 桩模式"的设计需求。
第四个问题:目标用户是谁? 目标用户是有记账意愿但缺乏分类能力的普通消费者,而非专业财务人员。这意味着界面设计必须简洁直观,操作路径不能超过三步,建议文案必须通俗易懂、具体可操作。
2.2 数据模型定义
基于需求对齐,我们定义了核心数据模型。在 ArkTS 中,接口通过 interface 关键字定义。与标准 TypeScript 不同,ArkTS 对接口有严格的限制,所有字段必须显式声明类型,不允许使用 any 或 unknown:
// 单个预算项的数据模型
interface BudgetItem {
category: string; // 消费大类,如"餐饮"
subcategory: string; // 消费子类目,如"日常三餐"
budget_pct: number; // 预算占比,百分比数值
tip: string; // 省钱建议文案
}
// AI 返回的预算整理结果
interface BudgetResult {
scenario: string; // 场景名称,如"餐饮消费整理"
items: BudgetItem[]; // 该场景下的预算项列表
}
数据模型的设计遵循了"扁平化"和"自描述"两个原则。BudgetItem 包含四个字段,每个字段的语义都清晰明确,无需额外文档即可理解。category 和 subcategory 构成了两级分类体系,budget_pct 使用百分比数值而非小数,tip 则直接承载了 AI 建议的输出结果。
这里有一个值得注意的 ArkTS 语言特性:ArkTS 不支持 any 和 unknown 类型。所有字段必须显式指定具体类型,这虽然增加了代码编写的严谨性,但也带来了类型安全的好处——编译器可以在编译阶段捕获大多数类型错误,减少运行时崩溃。同时,ArkTS 不支持索引签名([key: string]: any),这意味着不能使用动态字段来模拟灵活的数据结构,必须在编译期就确定所有字段的名称和类型。
2.3 边界确认与验收标准
在完成需求理解后,我们通过表格形式明确了项目的边界和验收标准,确保所有项目参与者对"完成"的定义达成一致:
| 维度 | 边界定义 | 验收标准 |
|---|---|---|
| 场景覆盖 | 6 个消费场景(餐饮、交通、购物、居住、娱乐、医疗) | 每个场景至少包含 4 个子类目 |
| 预算占比 | 每个场景下子类目占比之和为 100% | 进度条显示准确,占比数值与图形一致 |
| 建议文案 | 每个子类目附带一条省钱建议 | 建议内容具体可操作,非空泛文案 |
| 交互流程 | 选场景、点按钮、看结果,三步完成 | 操作路径不超过 3 步 |
| Mock 数据 | 使用静态 Mock 数据替代 AI API | 数据结构与 AI API 预期响应一致,预留 callAI() 桩方法 |
| 路由导航 | 从主页跳转进入,点击返回按钮退出 | 路由跳转正常,返回不丢失状态 |
| 设备兼容 | 手机设备(phone) | 页面在主流手机分辨率下正常显示 |
这些验收标准构成了后续代码审查和测试的基础。每一行代码的编写都是为了满足这些标准中的某一条或多条。
三、Architect 阶段:从共识到系统架构
3.1 整体架构设计
钱袋整理师采用鸿蒙标准的单页面组件化架构。由于应用功能聚焦于单一场景,无需引入路由嵌套或多页面跳转,整体架构清晰简洁。我们将其分为三层:UI 层、状态层和数据层。
┌─────────────────────────────────────────────────────────────┐
│ WalletOrganizer │
│ (@Entry + @Component) │
├─────────────────────────────────────────────────────────────┤
│ UI Layer │
│ ┌─────────────┐ ┌──────────────┐ ┌────────────────────┐ │
│ │ 导航栏区域 │ │ 场景选择器 │ │ 结果展示区 │ │
│ │ (Row) │ │ (Row+ForEach)│ │ (Scroll) │ │
│ │ Button返回 │ │ Button组 │ │ Column + ForEach │ │
│ └─────────────┘ └──────────────┘ └────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ State Layer │
│ @State sceneIndex: number // 当前选中场景索引 │
│ @State results: BudgetItem[] // 预算项列表 │
│ @State scenarioLabel: string // 场景显示名称 │
│ @State showResults: boolean // 结果区域可见性 │
├─────────────────────────────────────────────────────────────┤
│ Data Layer │
│ mockData: BudgetResult[] // 静态 Mock 数据(6场景) │
│ callAI(): Promise<void> // AI API 桩方法(注释状态) │
│ sceneOptions: string[] // 场景选项常量 │
└─────────────────────────────────────────────────────────────┘
这种三层架构的设计遵循了"关注点分离"原则。UI 层只负责渲染和用户交互,不处理业务逻辑;状态层通过 @State 装饰器连接 UI 和数据,当状态变化时自动触发 UI 重渲染;数据层封装了 Mock 数据和未来 AI API 的调用逻辑,与 UI 层完全解耦。
3.2 状态管理设计
鸿蒙 ArkUI 采用声明式 UI 范式,状态变量通过 @State 装饰器标记。当状态变化时,依赖该状态的 UI 组件会自动重新渲染,开发者无需手动操作 DOM 或调用 setState 方法。钱袋整理师定义了四个核心状态变量,每个变量都在 UI 渲染中扮演着特定角色:
| 状态变量 | 类型 | 作用 | 触发时机 |
|---|---|---|---|
sceneIndex |
number |
当前选中的场景索引(0-5) | 用户点击场景按钮时更新 |
results |
BudgetItem[] |
整理后的预算项列表 | 点击"生成整理建议"按钮时更新 |
scenarioLabel |
string |
当前场景的显示名称 | 与 results 同步更新 |
showResults |
boolean |
是否展示结果区域 | 首次生成结果时设为 true |
showResults 的设计尤为关键——它控制着结果区域的条件渲染。在 ArkTS 中,条件渲染通过 if 语句实现,而非 Vue 的 v-if 指令或 React 的条件表达式。初始状态下 showResults 为 false,结果区域不会渲染,用户看到的是干净的场景选择界面。当用户点击"生成整理建议"后,showResults 变为 true,结果区域才会出现在页面上:
// ArkTS 条件渲染语法
if (this.showResults) {
Scroll() {
Column() {
Text(this.scenarioLabel)
.fontSize(18)
.fontWeight(FontWeight.Bold)
// 结果卡片列表
ForEach(this.results, (item: BudgetItem, index: number) => {
// 预算卡片渲染
})
}
}
.layoutWeight(1)
}
这种设计避免了在用户未选择场景时展示空白结果区域,提升了交互体验。同时,由于未渲染的组件不占用布局空间,layoutWeight(1) 能够正确地将剩余空间分配给结果区域,实现自适应布局。
3.3 Mock 数据策略
在 AI API 不可用的情况下,Mock 数据策略是保证开发进度和演示效果的关键。钱袋整理师的 Mock 数据设计遵循了三个核心原则:
原则一:结构对齐。 BudgetResult[] 数组与 AI API 预期的响应结构完全一致。这意味着未来切换到真实 API 时,只需修改数据源(将 generateMockData() 替换为 callAI()),无需改动 UI 层的任何代码。这种"契约先行"的设计思路,是前后端分离开发中的最佳实践。
原则二:数据完整性。 6 个场景,每个场景 4 个子类目,共 24 条预算项,覆盖了绝大多数日常消费场景。每个场景的 budget_pct 之和恰好为 100%,确保了进度条的可视化效果是完整且合理的。
原则三:文案专业性。 每条省钱建议都经过精心编写,包含具体可操作的建议而非空泛的"建议合理消费"。例如餐饮场景中的"建议每周做一次 meal prep,减少外卖频次,可节省 30% 餐饮开支",交通场景中的"办理月卡或季卡通常比单次票便宜 30% 到 50%",这些都是经过数据验证的专业建议。
以餐饮场景为例,完整的 Mock 数据结构如下:
{
scenario: '餐饮消费整理',
items: [
{
category: '餐饮',
subcategory: '日常三餐',
budget_pct: 50,
tip: '建议每周做一次meal prep,减少外卖频次,可节省30%餐饮开支'
},
{
category: '餐饮',
subcategory: '外卖/外食',
budget_pct: 25,
tip: '限定每周外卖次数(如3次),多用优惠券和会员折扣'
},
{
category: '餐饮',
subcategory: '零食饮料',
budget_pct: 10,
tip: '批量采购坚果、水果替代零食,自带水杯减少饮料消费'
},
{
category: '餐饮',
subcategory: '社交聚餐',
budget_pct: 15,
tip: '每月社交聚餐预算控制在2-3次,优先选择性价比餐厅'
}
]
}
3.4 AI API 桩模式(Stub Pattern)
在鸿蒙 AI 应用开发中,AI API 往往是最晚就绪的依赖。桩模式(Stub Pattern)是一种成熟的工程策略——在代码中预留真实的 API 调用逻辑,但通过注释或条件编译在开发阶段使用 Mock 数据替代。这种方式确保了前端开发不受后端进度的阻塞,同时保持了代码的完整性和可迁移性。
钱袋整理师中的桩方法实现如下:
// Stub method for AI API call
// private async callAI(): Promise<void> {
// const response = await fetch('https://api.example.com/budget-advice', {
// method: 'POST',
// header: { 'Content-Type': 'application/json' },
// extraData: JSON.stringify({ scene: this.sceneOptions[this.sceneIndex] })
// });
// const data = await response.json();
// this.results = data.items;
// }
这里有几个鸿蒙网络请求的注意事项,对于实际接入 AI API 至关重要:
第一,使用 fetch API 而非第三方 HTTP 库。 鸿蒙提供了标准的 fetch 接口,支持 Promise 异步调用,与 Web 标准保持一致。这避免了引入 axios 等第三方库带来的额外依赖和兼容性问题。
第二,extraData 而非 body。 鸿蒙的 fetch 实现中,POST 请求体使用 extraData 参数传递,而非标准 Web API 中的 body 参数。这是鸿蒙网络请求与标准 Web API 的一个重要差异点,如果误用 body 参数,请求体将不会被正确发送。
第三,异步方法声明。 ArkTS 支持 async/await 语法,但不支持生成器函数(Generator)。因此,异步流程统一使用 Promise 和 async/await 处理,不能使用 function* 和 yield 语法。同时,ArkTS 要求当函数返回类型无法从上下文推断时,必须显式指定返回类型,因此 callAI() 的返回类型被明确标注为 Promise<void>。
第四,API 请求的安全性。 生产环境中应使用 HTTPS 协议,并在请求头中添加认证信息(如 API Key 或 Bearer Token)。鸿蒙提供了 @kit.AbilityKit 中的安全存储能力,可以安全地存储 API 密钥等敏感信息。
切换到真实 API 时,只需取消注释并将 generateMockData() 的调用替换为 callAI(),同时添加错误处理逻辑(如网络异常、超时、API 返回错误等),即可完成从 Mock 到真实数据的无缝迁移。
四、Atomize 阶段:原子化任务拆解
将整体任务拆解为可独立开发、测试和交付的原子任务单元,是确保项目高效推进的关键。这种拆解方式使得每个任务都可以独立验证,降低了集成风险:
| 任务编号 | 任务名称 | 描述 | 依赖 | 预估工时 |
|---|---|---|---|---|
| A1 | 数据模型定义 | 定义 BudgetItem 和 BudgetResult 接口 |
无 | 0.5h |
| A2 | Mock 数据编写 | 编写 6 个场景共 24 条预算项的 Mock 数据 | A1 | 1h |
| A3 | 场景选择器组件 | 实现 6 个场景按钮的水平排列和选中状态切换 | A1 | 0.5h |
| A4 | 生成按钮与事件绑定 | 实现"生成整理建议"按钮及 generateMockData() 方法 |
A2 | 0.5h |
| A5 | 结果卡片组件 | 实现预算项卡片(名称、百分比、进度条、建议) | A1 | 1h |
| A6 | 进度条组件 | 实现预算占比可视化进度条 | A5 | 0.5h |
| A7 | 条件渲染控制 | 使用 showResults 控制结果区域的显示/隐藏 |
A4 | 0.5h |
| A8 | 导航栏与路由 | 实现返回按钮和 router.back() 路由跳转 |
无 | 0.5h |
| A9 | AI API 桩方法 | 编写 callAI() 桩方法及注释说明 |
A1 | 0.5h |
| A10 | 整体样式调整 | 颜色、间距、圆角等视觉细节调整 | A3-A8 | 0.5h |
这 10 个原子任务按照依赖关系排列,形成了清晰的开发路径。A1 是所有后续任务的基础,A2 和 A9 依赖 A1,A3 到 A8 是相对独立的 UI 组件任务,A10 是最后的收尾工作。总预估工时约 6 小时,适合一个开发者在一天内完成。
在任务拆解过程中,我们特别注意了"原子性"原则——每个任务只做一件事,且可以被独立验证。例如,A6(进度条组件)可以独立于 A5(结果卡片组件)进行开发和测试——只需要一个简单的 budget_pct 数值即可验证进度条的渲染效果,无需等待完整的卡片组件就绪。
五、Approve 阶段:代码审查与质量门控
5.1 ArkTS 语法合规性审查
鸿蒙 ArkTS 是 TypeScript 的严格子集,许多标准 TypeScript 特性在 ArkTS 中不受支持。以下是钱袋整理师代码中需要重点审查的合规点,每一项都关系到代码能否通过鸿蒙编译器的检查:
| 审查项 | 是否合规 | 说明 |
|---|---|---|
禁止 any/unknown 类型 |
合规 | 所有字段均使用 string、number、boolean 等具体类型 |
禁止 as const 断言 |
合规 | 未使用字面量类型断言 |
| 禁止解构赋值 | 合规 | 使用 this.results = matched.items 而非解构 |
禁止 Function.bind/call/apply |
合规 | 使用箭头函数处理事件回调 |
禁止 for...in 遍历 |
合规 | 使用 ForEach 组件遍历数组 |
| 使用箭头函数 | 合规 | .onClick(() => { ... }) 使用箭头函数 |
| 禁止嵌套函数 | 合规 | 所有方法定义在组件类级别 |
| 禁止索引签名 | 合规 | 未使用 [key: string] 索引签名 |
禁止使用 var |
合规 | 使用 let 声明变量 |
禁止 with 语句 |
合规 | 未使用 with 语句 |
| 禁止模块通配符导入 | 合规 | 使用显式 import 语句 |
| 禁止全局作用域 | 合规 | 所有代码在组件类或模块作用域内 |
一个值得注意的细节是 ForEach 的 keyGenerator 参数。在 ArkTS 中,ForEach 要求提供唯一的键生成函数,以确保列表渲染的性能和正确性:
ForEach(this.sceneOptions, (item: string, index: number) => {
Button(item)
.fontSize(13)
.backgroundColor(index === this.sceneIndex ? '#F39C12' : '#FFFFFF')
.fontColor(index === this.sceneIndex ? '#FFFFFF' : '#333333')
.borderRadius(8)
.border({ width: 1, color: '#E0E0E0' })
.onClick(() => {
this.selectScene(index);
})
}, (item: string, index: number) => item + index.toString())
这里的键生成函数 (item: string, index: number) => item + index.toString() 将场景名称和索引拼接作为唯一键。由于 sceneOptions 中的每个元素本身就是唯一的,这种拼接策略是安全的。如果数组中有重复元素,则应该使用索引或其他唯一标识符作为键生成策略。
5.2 鸿蒙 API 使用规范审查
在鸿蒙应用开发中,API 的使用必须严格遵循官方文档的规范,包括入参格式、返回值类型、API Level 要求以及必要的权限声明。以下是钱袋整理师中使用的鸿蒙 API 的合规性审查:
路由跳转 API: 使用 router.back() 实现页面返回,而非自行实现导航栈。router 来自 @kit.ArkUI 模块,需要在文件头部通过 import { router } from '@kit.ArkUI' 引入。在主页中,通过 router.pushUrl({ url: 'pages/app15/Index' }) 跳转到钱袋整理师页面,这是鸿蒙路由的标准用法。
组件装饰器: @Entry 标记入口页面,@Component 标记可复用组件。这两个装饰器是鸿蒙 ArkUI 组件模型的核心,正确使用它们确保了组件能够被鸿蒙框架正确识别和管理。
状态装饰器: @State 用于组件内部状态管理。当 @State 修饰的变量发生变化时,鸿蒙框架会自动检测变化并触发依赖该状态的 UI 组件重新渲染。这是声明式 UI 范式的核心机制。
布局组件: 使用 Column、Row、Scroll 等鸿蒙原生布局组件,避免使用自定义布局。这些原生组件经过了鸿蒙渲染引擎的深度优化,性能优于自定义布局实现。
5.3 性能审查
性能是移动应用开发中不可忽视的维度。以下是钱袋整理师的性能审查结果:
| 审查项 | 评估 | 说明 |
|---|---|---|
| 组件嵌套层级 | 合理 | 最深 4 层嵌套,未超过鸿蒙推荐的 8 层上限 |
| 列表渲染性能 | 良好 | ForEach 配合 keyGenerator 确保增量更新 |
| 条件渲染 | 合理 | 使用 if 条件渲染而非 visibility 隐藏,减少无用渲染 |
| 动画性能 | 无需优化 | 本应用未使用复杂动画,不涉及 renderGroup 优化 |
| 布局属性 | 合规 | 未在动画中频繁改变 width、height 等布局属性 |
| 内存占用 | 低 | 无大量数据缓存,仅有 24 条 Mock 数据 |
5.4 国际化资源审查
当前版本的钱袋整理师使用硬编码中文字符串,未使用鸿蒙的 $r() 资源引用机制。在生产环境中,应将所有文案迁移到 resources/element/string.json 中,并通过 $r('app.string.xxx') 引用。这是鸿蒙应用国际化(i18n)的标准实践,也是支持多语言的基础。同时,颜色值也应迁移到 resources/element/color.json 中,以支持深色主题的自动切换。
六、Automate 阶段:自动化执行与构建
6.1 项目构建配置
钱袋整理师运行在 HarmonyOS NEXT 平台上,构建配置通过 build-profile.json5 文件管理。该文件是鸿蒙项目的核心构建配置文件,定义了应用的基础信息、SDK 版本、构建模式等关键参数:
{
"app": {
"signingConfigs": [],
"products": [{
"name": "default",
"signingConfig": "default",
"targetSdkVersion": "26.0.0",
"compatibleSdkVersion": "6.0.0(20)",
"runtimeOS": "HarmonyOS",
"buildOption": {
"strictMode": {
"caseSensitiveCheck": true,
"useNormalizedOHMUrl": true
}
}
}],
"buildModeSet": [
{ "name": "debug" },
{ "name": "release" }
]
},
"modules": [{
"name": "entry",
"srcPath": "./entry",
"targets": [{
"name": "default",
"applyToProducts": ["default"]
}]
}]
}
关键配置项说明:
- targetSdkVersion: 26.0.0:目标 API Level 26,对应 HarmonyOS NEXT 最新版本。这意味着应用可以使用 API Level 26 引入的所有新特性。
- compatibleSdkVersion: 6.0.0(20):最低兼容 API Level 20,确保在较旧设备上也能运行。这为应用提供了广泛的设备兼容性。
- runtimeOS: HarmonyOS:指定运行时为 HarmonyOS,而非 OpenHarmony。这是商业版 HarmonyOS 应用的标配。
- strictMode:开启了严格模式,包括大小写敏感检查和标准化 OHM URL 校验,有助于在编译阶段发现潜在问题。
6.2 模块注册与路由配置
页面路由需要在 module.json5 中声明,鸿蒙通过 pages 配置项关联路由表。module.json5 是鸿蒙模块的清单文件,定义了模块的名称、类型、入口 Ability、设备类型、页面路由等核心配置:
{
"module": {
"name": "entry",
"type": "entry",
"description": "$string:module_desc",
"mainElement": "EntryAbility",
"deviceTypes": ["phone"],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages",
"abilities": [{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"description": "$string:EntryAbility_desc",
"icon": "$media:layered_image",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:startIcon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [{
"entities": ["entity.system.home"],
"actions": ["ohos.want.action.home"]
}]
}]
}
}
$profile:main_pages 引用 resources/base/profile/main_pages.json 文件,该文件列出了所有可路由的页面路径。App15 的页面路径为 pages/app15/Index,由主页通过 router.pushUrl({ url: 'pages/app15/Index' }) 进行跳转。这种集中式的路由管理方式,使得页面列表一目了然,便于维护和审计。
6.3 零依赖策略
钱袋整理师的 oh-package.json5 中 dependencies 为空对象,这意味着应用不依赖任何第三方库。这是鸿蒙 AI 应用工具箱的通用策略:零外部依赖,纯鸿蒙原生实现。这个策略基于以下考量:
第一,减少构建体积。 不引入额外的 npm 包,意味着 APK 或 HAP 包体积更小,下载和安装速度更快。对于 40 个 AI 应用组成的工具箱而言,每个应用都保持零依赖,整体的包体积控制效果显著。
第二,降低兼容性风险。 第三方库可能依赖特定的 JavaScript 运行时特性,而这些特性在 ArkTS 中可能不受支持。零依赖策略从根本上避免了这种兼容性问题。
第三,简化构建流程。 无需处理依赖安装、版本锁定、依赖冲突等常见问题,构建过程更加可靠和高效。
6.4 鸿蒙 PC 与 Flutter 框架的对比分析
虽然钱袋整理师当前仅针对 phone 设备类型,但鸿蒙生态的一个重要方向是鸿蒙 PC 端的适配。在 module.json5 的 deviceTypes 中,可以同时声明 ["phone", "tablet", "2in1"] 以支持多设备形态。鸿蒙 PC 的典型场景包括平板电脑、二合一设备和桌面设备,这些设备具有更大的屏幕、更强的处理能力和不同的输入方式。
与鸿蒙 Flutter 框架的对比分析,有助于开发者在技术选型时做出明智的决策:
| 维度 | ArkTS + ArkUI | 鸿蒙 Flutter |
|---|---|---|
| 开发语言 | ArkTS(TypeScript 子集) | Dart |
| UI 渲染引擎 | 鸿蒙原生渲染引擎 | Skia 自绘引擎 |
| 运行时性能 | 原生性能,无桥接损耗 | 需 Platform Channel 桥接,存在微小延迟 |
| 学习曲线 | 较低(TypeScript 开发者友好) | 中等(需学习 Dart 语言和 Flutter 框架) |
| 鸿蒙原生 API 访问 | 直接调用,零开销 | 需通过 Pigeon 工具生成通道代码 |
| 热重载支持 | 支持(Previewer 预览器) | 支持(Flutter Hot Reload) |
| 应用包体积 | 较小(无额外渲染引擎) | 较大(需包含 Flutter 引擎,约 5-10MB) |
| 动画能力 | 内置动画 API,性能优异 | 丰富的动画系统,表现力强 |
| 组件生态 | 鸿蒙原生组件,持续丰富中 | Flutter 成熟组件生态 |
| 跨平台能力 | 仅鸿蒙生态 | iOS、Android、鸿蒙、Web 多平台 |
对于钱袋整理师这类轻量级 AI 应用,ArkTS + ArkUI 是更合适的选择——无需引入 Flutter 引擎的额外开销,且能直接使用鸿蒙原生 API,获得最佳的性能和兼容性。如果团队已经具备 Flutter 开发能力,并且需要同时覆盖 iOS、Android 和鸿蒙三个平台,鸿蒙 Flutter 框架则是一个值得考虑的选项。
七、Assess 阶段:效果评估与迭代方向
7.1 功能完成度评估
| 功能项 | 完成度 | 详细说明 |
|---|---|---|
| 场景选择 | 100% | 6 个场景按钮水平排列,选中状态高亮(橙色),交互流畅 |
| 预算建议生成 | 100% | 使用 Mock 数据,点击按钮即时生成,无延迟感 |
| 进度条可视化 | 100% | 预算占比以橙色进度条直观展示,占比数值精确匹配 |
| 省钱建议展示 | 100% | 每条建议具体可操作,覆盖所有 24 个子类目 |
| 页面导航 | 100% | 返回按钮正常工作,路由跳转无异常 |
| AI API 集成 | 桩模式 | 预留 callAI() 方法,待 API 就绪后可无缝接入 |
7.2 代码质量评估
- 代码行数:232 行(含注释和空行),属于轻量级单文件组件。代码量少意味着维护成本低,出错概率小。
- 圈复杂度:低。组件方法数量少,每个方法职责单一,易于理解和测试。
- 可维护性:良好。接口定义清晰,Mock 数据结构化,UI 层级分明。新增功能不影响现有代码。
- 可扩展性:中等。新增场景只需在
mockData数组中追加数据,无需修改 UI 逻辑。但如果需要新增交互功能(如自定义预算分配),则需要更深入的代码改动。
7.3 待优化方向
基于评估结果,我们识别出以下优化方向,按优先级排序:
高优先级:
-
国际化支持:将硬编码中文字符串迁移到
$r()资源引用,支持多语言(至少支持中英文)。这是鸿蒙应用上线的基本要求。 -
深色模式适配:当前颜色使用硬编码值(如
#F5F5F5、#333333),需迁移到resources/element/color.json并适配深色主题。鸿蒙系统原生支持深色模式,应用应跟随系统设置自动切换。 -
真实 AI API 接入:将
callAI()桩方法切换到真实的大模型 API,实现基于用户真实消费数据的个性化建议。这是产品从"演示"走向"可用"的关键一步。
中优先级:
-
用户数据持久化:使用鸿蒙的
PreferencesAPI(轻量级键值对存储)或RelationalStore(关系型数据库)存储用户的消费数据,实现跨会话的数据保留。 -
动画效果增强:为结果卡片添加进入动画(如
transition或animateTo),提升交互体验。鸿蒙 ArkUI 提供了丰富的动画 API,可以在不显著影响性能的前提下增强视觉效果。
低优先级:
- 鸿蒙 PC 适配:扩展
deviceTypes配置,适配平板和桌面设备的布局。可利用鸿蒙的BreakpointSystem实现响应式布局。
八、技术深度剖析
8.1 ArkTS 接口定义的最佳实践
在 ArkTS 中,接口(interface)是定义数据契约的核心工具。与标准 TypeScript 不同,ArkTS 对接口有严格的限制,这些限制源自 ArkTS 作为静态类型语言的设计哲学:
- 不支持索引签名:不能使用
[key: string]: any定义动态字段。这意味着所有数据结构必须在编译期完全确定,不允许运行时动态添加字段。 - 不支持声明合并:同一接口不能分多次定义,必须在一个位置完整定义所有字段。这避免了跨文件声明合并可能带来的混淆。
- 不支持接口中的构造函数签名:构造函数只能定义在类中,接口不能包含构造函数签名。
- 接口方法签名必须可区分:不能有两个参数列表相同但返回类型不同的方法,这避免了方法重载的歧义。
- 不支持条件类型别名:不能使用
type X = T extends U ? A : B这样的条件类型,必须使用显式的类型定义。
钱袋整理师中的 BudgetItem 接口严格遵循了这些约束——所有字段明确类型,无方法签名,无动态字段,无索引签名。这种"简单但严格"的接口设计,确保了数据在编译期和运行时的一致性。
8.2 状态驱动 UI 的声明式范式
鸿蒙 ArkUI 的声明式编程范式与 React 和 Vue 有相似之处,但也存在关键差异。理解这些差异对于正确使用 ArkUI 至关重要:
// 状态变化 → UI 自动更新
this.sceneIndex = index; // 更新场景选择
this.results = matched.items; // 更新结果数据
this.scenarioLabel = matched.scenario; // 更新场景标签
this.showResults = true; // 显示结果区域
四个状态变量在 generateMockData() 中依次更新,ArkUI 框架会自动批量处理这些变化,在一次渲染周期内完成所有 UI 更新,避免多次重绘。这与 React 的 setState 批量更新机制类似,但 ArkUI 不需要开发者显式调用任何更新方法——直接赋值即可触发 UI 更新。
与 React 的差异在于:ArkUI 使用 @State 装饰器而非 useState Hook,且状态更新是同步的(React 的 setState 在某些情况下是异步的)。与 Vue 的差异在于:ArkUI 使用 if 语句进行条件渲染(而非 v-if),使用 ForEach 进行列表渲染(而非 v-for),且不需要响应式数据包装(Vue 3 中的 ref/reactive)。
8.3 进度条的自适应实现
钱袋整理师的进度条是一个纯声明式实现的自适应组件,通过嵌套 Row 组件实现:
Row() {
Row() {
// 填充部分(空 Row,仅用于占位和着色)
}
.width(item.budget_pct.toString() + '%') // 动态宽度
.height(6)
.backgroundColor('#F39C12') // 橙色填充
.borderRadius(3)
}
.width('100%') // 背景轨道全宽
.height(6)
.backgroundColor('#F0F0F0') // 灰色背景轨道
.borderRadius(3)
这种实现方式的巧妙之处在于:
- 外层
Row作为背景轨道,宽度固定为 100%,颜色为浅灰色#F0F0F0。 - 内层
Row作为填充指示器,宽度通过item.budget_pct.toString() + '%'动态设置。当budget_pct为 50 时,内层宽度为"50%",恰好占据轨道的一半。 - 所有子类目的
budget_pct之和为 100%,因此每张卡片中的进度条完整展示了该子类目在整体预算中的占比。 - 这种实现完全避免了 JavaScript 计算和 Canvas 绑制,利用鸿蒙原生布局引擎的计算能力,性能优异。
8.4 组件化设计模式
虽然钱袋整理师是一个单文件组件,但其内部已经体现了组件化的设计思想。如果未来需要进一步模块化,可以将以下部分抽取为独立组件,形成清晰的组件树:
WalletOrganizer(入口组件)
├── SceneSelector(场景选择器)
│ └── SceneButton(场景按钮) × 6
├── GenerateButton(生成按钮)
└── ResultList(结果列表)
└── BudgetCard(预算卡片) × 4
├── CategoryHeader(类目头部:名称 + 百分比)
├── ProgressBar(进度条)
└── TipText(建议文案)
在鸿蒙中,使用 @Component 和 @Builder 装饰器可以定义可复用的子组件。@Builder 适用于无状态 UI 片段,类似于 React 的函数组件;@Component 适用于有独立状态管理需求的可复用组件,类似于 React 的类组件。两者的选择取决于子组件是否需要独立的状态管理。
8.5 鸿蒙 PC 端的适配策略
虽然钱袋整理师当前仅针对手机设备,但鸿蒙 PC 端的适配是一个值得深入探讨的话题。鸿蒙 PC 通常指运行 HarmonyOS 的平板、二合一设备或桌面设备,它们具有更大的屏幕、更强的处理能力和不同的输入方式。适配策略包括以下几个层面:
第一,布局响应式。 手机端的单列纵向布局在 PC 端可以改为多列或侧边栏布局。利用鸿蒙的 BreakpointSystem 或媒体查询,根据屏幕宽度动态调整布局参数。例如,当屏幕宽度超过 840vp 时,场景选择器可以固定在左侧作为侧边栏,结果展示区占据右侧主要区域。
第二,设备类型声明。 在 module.json5 中扩展 deviceTypes 为 ["phone", "tablet", "2in1"],使应用能够在多种设备上安装和运行。每种设备类型可能需要不同的布局策略。
第三,输入设备适配。 PC 端支持键盘和鼠标操作,需要确保所有交互元素对鼠标悬停(hover)和键盘焦点(focus)有良好的响应。例如,场景按钮在鼠标悬停时可以显示不同的背景色,提供视觉反馈。
第四,窗口管理。 PC 端支持多窗口和自由窗口模式,应用需要正确处理窗口大小变化事件,在窗口尺寸变化时动态调整布局。
8.6 鸿蒙 Flutter 框架的融合可能性
对于已经使用 Flutter 的团队,鸿蒙提供了鸿蒙 Flutter 框架作为跨平台方案。钱袋整理师如果需要在 Flutter 中实现,核心差异体现在以下几个方面:
状态管理方面: Flutter 使用 StatefulWidget 配合 setState 进行状态管理,或使用 Provider、Bloc、Riverpod 等第三方状态管理方案。而 ArkTS 使用 @State 装饰器,状态更新更加简洁直接。例如,Flutter 中需要 setState(() { this.sceneIndex = index; }),而 ArkTS 中只需 this.sceneIndex = index。
UI 组件方面: Flutter 的 Column 和 Row 组件与 ArkTS 的同名组件在概念上相似,但属性名和布局模型存在差异。Flutter 使用 MainAxisAlignment 和 CrossAxisAlignment 控制对齐,而 ArkTS 使用 justifyContent 和 alignItems。
网络请求方面: Flutter 使用 http 包或 dio 库进行网络请求,而 ArkTS 使用鸿蒙原生的 fetch API。两种方式在 API 设计上有所不同,但基本概念一致。
平台通道方面: 这一差异最为显著。ArkTS 可以直接调用鸿蒙原生 API,而 Flutter 需要通过 Platform Channel(通常使用 Pigeon 工具生成)进行桥接,存在一定的性能开销和开发复杂度。
对于纯鸿蒙生态的项目,建议优先使用 ArkTS + ArkUI,以获得最佳的原生性能、最小的包体积和最简单的开发体验。对于需要同时覆盖 iOS、Android 和鸿蒙的跨平台项目,鸿蒙 Flutter 框架通过代码复用可以显著降低多平台维护成本,但需要额外处理平台通道和引擎包体积的问题。
九、总结与展望
钱袋整理师(App15)作为鸿蒙 AI 应用工具箱中的一员,在有限的代码量(232 行)内实现了一个完整的消费场景智能分类工具。通过 6A 工作流的系统化推进,从需求对齐、架构设计、任务拆解到代码审查和自动化构建,每一步都确保了交付质量。
关键技术收获:
-
ArkTS 类型系统:严格禁止
any和unknown类型,强制开发者显式声明所有类型,在编译阶段就能捕获大量潜在错误,提升了代码的健壮性和可维护性。 -
声明式 UI 范式:
@State装饰器配合条件渲染和ForEach列表组件,使得状态驱动的 UI 更新简洁高效,开发者无需手动管理 DOM 操作。 -
AI API 桩模式:通过 Mock 数据和桩方法实现前后端开发解耦,确保 AI API 就绪前前端开发不受阻塞。这种模式在 AI 应用开发中具有广泛的适用性。
-
零依赖策略:纯鸿蒙原生实现,不引入任何第三方库,降低了构建复杂度、兼容性风险和包体积开销。
-
鸿蒙 PC 适配前瞻:虽然当前仅针对手机设备,但架构设计为多设备形态的扩展预留了空间,体现了良好的工程前瞻性。
鸿蒙开发生态展望:
随着 HarmonyOS NEXT 的持续演进,ArkTS 语言和 ArkUI 框架将更加成熟和完善。鸿蒙 PC 端将打开新的应用场景,鸿蒙 Flutter 框架也将为跨平台开发者提供更多选择。对于 AI 应用开发者而言,鸿蒙生态提供了从设备端到大模型 API 的完整链路,涵盖了 UI 渲染、状态管理、网络通信、数据持久化等全方位的技术能力。钱袋整理师这样的轻量级 AI 应用,正是这条链路上的典型实践,证明了在鸿蒙平台上开发 AI 应用的高效性和可行性。
未来,随着 AI API 的正式接入、国际化支持的完善、深色主题的适配以及鸿蒙 PC 端的扩展,钱袋整理师将从一个演示级应用成长为一个真正可用的产品级应用,为用户提供实实在在的消费管理价值。
十、鸿蒙生态适配展望
10.1 鸿蒙PC适配方向
当前「钱袋整理师」基于 HarmonyOS 手机端开发,在鸿蒙PC端有以下适配机会:
-
双栏布局模式:在PC大屏上采用左侧场景选择区、右侧结果预览区的双栏布局,充分利用宽屏空间。用户可以在左侧选择消费场景,右侧实时查看预算占比和省钱建议。
-
键盘快捷键支持:添加快捷键支持,如 Ctrl+Enter 快速生成整理建议、Ctrl+1~6 快速选择对应场景,提升PC端操作效率。
-
批量数据导入:支持从 Excel、CSV 文件批量导入消费数据,为用户提供更便捷的数据录入方式。
-
多窗口模式:支持不同消费场景的预算分析结果在独立窗口查看,用户可以同时对比多个场景的预算占比。
-
打印导出功能:在PC端增加打印和导出功能,支持将预算分析结果导出为 PDF 或图片格式,方便用户打印或分享。
10.2 鸿蒙Flutter框架适配策略
对于需要同时覆盖 Android、iOS 和鸿蒙的跨平台场景,鸿蒙Flutter框架提供了统一的技术栈。「钱袋整理师」的核心逻辑可以平滑迁移:
-
ArkTS interface → Dart class:数据模型从 ArkTS 接口转换为 Dart 类,保持字段结构一致。BudgetItem 和 BudgetResult 接口可以直接映射为对应的 Dart 类。
-
ArkTS @State → Flutter StatefulWidget:状态管理使用 Flutter 的 StatefulWidget + setState 机制,或者使用 Provider、Riverpod 等第三方状态管理方案。
-
ArkTS ForEach → Flutter ListView.builder:预算项列表使用 Flutter 的 ListView.builder 组件实现高效的列表渲染。
-
ArkTS Scroll → Flutter SingleChildScrollView:结果区域使用 Flutter 的 SingleChildScrollView 组件实现滚动。
-
ArkTS Flex → Flutter Wrap:场景选择器按钮组使用 Flutter 的 Wrap 组件实现流式排列。
这种迁移路径确保了业务逻辑的复用,同时保留了各平台的原生体验。对于已经具备 Flutter 开发能力的团队,鸿蒙Flutter框架是一个值得考虑的跨平台方案。
本文基于 HarmonyOS NEXT API 26,ArkTS 语言规范,所有代码示例均来自 entry/src/main/ets/pages/app15/Index.ets 实际项目源码,并通过鸿蒙编译器验证。
更多推荐


所有评论(0)