HarmonyOS ArkTS 实战:实现一个校园卡充值与消费记录应用

📅 2026/7/21 6:04:53 👁️ 阅读次数 📝 编程学习
HarmonyOS ArkTS 实战:实现一个校园卡充值与消费记录应用

HarmonyOS ArkTS 实战:实现一个校园卡充值与消费记录应用

HarmonyOS ArkTS 实战:校园卡充值与消费记录应用完整实现

摘要

本文基于HarmonyOS Next + ArkTS技术栈,实现一个功能完整的校园卡应用,涵盖余额展示、在线充值、消费记录、挂失解挂、消费统计、限额设置等核心功能。项目完整覆盖金融类应用的状态管理、金额计算、交易流水排序等关键开发要点。

项目难度:⭐⭐⭐ 中级实战
适用人群:有 ArkTS 基础、希望学习金融类应用开发的 HarmonyOS 开发者
开发环境:DevEco Studio Next / API 24+
代码规模:单页面约 500 行,可直接运行

通过本项目,你将掌握:

  • 金融类应用的数据模型设计与金额精度处理
  • 交易流水的增删查改与分类筛选
  • 卡片挂失/解挂的状态机管理
  • 消费数据的统计聚合计算
  • 砖红色主题的金融类 UI 视觉设计

目录

  • 一、项目概述与功能架构
    • 1.1 应用场景与项目价值
    • 1.2 完整功能清单
    • 1.3 技术选型说明
  • 二、数据结构设计
    • 2.1 交易记录模型(Transaction)
    • 2.2 设计思路与字段说明
    • 2.3 为什么用整数存储金额
  • 三、核心功能实现详解
    • 3.1 页面状态初始化
    • 3.2 在线充值功能实现
    • 3.3 消费扣款逻辑
    • 3.4 挂失与解挂状态管理
    • 3.5 消费记录筛选与查询
  • 四、统计功能实现
    • 4.1 本月消费/充值统计
    • 4.2 消费分类聚合
    • 4.3 限额设置与校验
  • 五、UI 设计与交互体验
    • 5.1 整体布局结构
    • 5.2 主题色与视觉规范
    • 5.3 交易记录的视觉区分
  • 六、关键技术点解析
    • 6.1 金额精度问题与解决方案
    • 6.2 交易流水的时间倒序排列
    • 6.3 挂失状态的全局影响
  • 七、常见问题与优化建议
  • 八、项目总结与扩展方向
  • 附录:运行方式

一、项目概述与功能架构

1.1 应用场景与项目价值

校园卡是每个大学生日常生活中最常用的支付工具,食堂吃饭、超市购物、图书馆打印、宿舍缴电费都离不开它。本项目模拟真实校园卡应用的核心功能,是学习金融类应用开发非常好的实战案例。

金融类应用的开发难点不在于 UI 复杂度,而在于数据准确性和状态一致性:余额计算不能出错、交易记录不能重复、挂失状态要全局生效。这些细节处理能力是区分初级和中级开发者的重要标志。

1.2 完整功能清单

功能模块具体功能实现难度
余额展示大卡片展示当前余额、卡状态、今日消费
充值功能固定金额选择、自定义金额、充值确认⭐⭐
交易记录消费/充值混合流水、时间倒序、分类筛选⭐⭐⭐
挂失解挂一键挂失、解挂验证、挂失后功能禁用⭐⭐
数据统计本月消费总额、充值总额、消费笔数、分类占比⭐⭐⭐
辅助功能付款码展示、限额设置、账单明细导出⭐⭐

1.3 技术选型说明

本项目采用纯 ArkTS 声明式开发:

  • ArkUI 声明式范式@Component+@State构建响应式界面
  • 本地数据存储:交易记录使用内存数组模拟,可扩展为关系型数据库
  • 状态管理:单页面场景下@State完全满足需求
  • 金额计算:使用浮点数简化 Demo 实现,生产环境建议改用整数分

为什么选择单页面实现?校园卡的核心功能都围绕余额和交易记录展开,单页面集中展示可以让用户操作路径最短。真实项目中可以将充值、记录、设置拆分为 Tab 页面。

二、数据结构设计

2.1 交易记录模型(Transaction)

统一的数据模型是整个应用的核心。无论是充值还是消费,都用同一张交易流水表记录:

/** * 交易流水数据模型 * 充值和消费统一使用该结构,通过 type 字段区分 */interfaceTransaction{id:number;// 交易流水号type:'充值'|'消费';// 交易类型amount:number;// 交易金额(正数)location:string;// 交易地点time:string;// 交易时间category:string;// 交易分类(餐饮/超市/充值等)balance:number;// 交易后余额}

2.2 设计思路与字段说明

统一交易模型的好处:

  • 列表展示简单,不需要合并两个数组
  • 统计计算方便,按 type 过滤即可分别统计
  • 扩展性好,后续增加"退款""转账"等类型只需新增枚举值

关键字段作用:

  • balance:记录交易后余额,便于回溯对账,不需要反向计算
  • category:消费分类字段,支持后续做分类统计和图表展示
  • id:自增流水号,唯一标识每一笔交易

2.3 为什么用整数存储金额

⚠️ 重要提示:本 Demo 为了简化代码使用了浮点数,但真实金融应用绝对不能用浮点数存金额

浮点数精度问题示例:

console.log(0.1+0.2);// 输出 0.30000000000000004,不是 0.3

生产环境推荐方案:

  • 方案一:金额以为单位用整数存储(如 12850 分 = 128.50 元)
  • 方案二:使用专门的高精度计算库
  • 方案三:所有计算后统一toFixed(2)保留两位小数

本项目作为教学 Demo 简化处理,但实际开发中务必注意金额精度。

三、核心功能实现详解

3.1 页面状态初始化

@Componentexportstruct Index{// 当前校园卡余额@Stateprivatebalance:number=128.5;// 卡片是否已挂失@StateprivateisLost:boolean=false;// 选中的充值金额@StateprivaterechargeAmount:number=50;// 下一条交易记录ID@StateprivatenextTransId:number=20;// 交易记录列表(最新在前)@Stateprivatetransactions:Transaction[]=[{id:19,type:'消费',amount:12.5,location:'第一食堂',time:'2024/5/20 12:30',category:'餐饮',balance:128.5},{id:18,type:'充值',amount:100,location:'线上充值',time:'2024/5/20 09:00',category:'充值',balance:141},{id:17,type:'消费',amount:8.5,location:'校园超市',time:'2024/5/19 18:20',category:'超市',balance:41},];// 单日消费限额(0表示不限制)@StateprivatedailyLimit:number=0;}

状态设计原则:

  • balance作为唯一真值来源,所有交易都更新这个值
  • isLost是全局状态,挂失后所有消费、充值按钮都要禁用
  • 交易记录只追加、不修改,保证流水可追溯

3.2 在线充值功能实现

/** * 充值操作 * @param amount 充值金额 */privaterecharge(amount:number):void{// 1. 状态校验:挂失状态不能充值if(this.isLost){console.warn('卡片已挂失,无法充值');return;}// 2. 金额校验if(amount<=0){console.warn('充值金额必须大于0');return;}// 3. 创建充值交易记录consttrans:Transaction={id:this.nextTransId,type:'充值',amount:amount,location:'线上充值',time:newDate().toLocaleString(),category:'充值',balance:this.balance+amount};// 4. 更新余额和交易列表this.transactions=[trans,...this.transactions];this.balance+=amount;this.nextTransId++;console.log(`充值成功,金额:${amount}元,当前余额:${this.balance}`);}

充值功能的防御性校验:

  • 挂失状态拦截
  • 金额合法性校验(大于0、不超过上限)
  • 实际项目还需要增加支付密码、短信验证等安全环节

3.3 消费扣款逻辑

/** * 消费扣款 * @param amount 消费金额 * @param location 消费地点 * @param category 消费分类 */privateconsume(amount:number,location:string,category:string):boolean{// 1. 挂失状态禁止消费if(this.isLost){console.warn('卡片已挂失,禁止消费');returnfalse;}// 2. 余额校验if(this.balance<amount){console.warn('余额不足');returnfalse;}// 3. 单日限额校验(如果设置了限额)if(this.dailyLimit>0){consttodayConsume=this.getTodayTotalConsume();if(todayConsume+amount>this.dailyLimit){console.warn('超出单日消费限额');returnfalse;}}// 4. 创建消费记录consttrans:Transaction={id:this.nextTransId,type:'消费',amount:amount,location:location,time:newDate().toLocaleString(),category:category,balance:this.balance-amount};// 5. 更新数据this.transactions=[trans,...this.transactions];this.balance-=amount;this.nextTransId++;returntrue;}

消费比充值多了两层校验:

  • 余额是否足够
  • 是否超出单日消费限额

这是金融类应用的典型特点——扣款路径的校验永远比充值路径严格

3.4 挂失与解挂状态管理

/** * 挂失校园卡 */privatereportLost():void{if(this.isLost)return;this.isLost=true;console.log('校园卡已挂失,所有消费功能已冻结');}/** * 解挂校园卡 */privatereactivate():void{if(!this.isLost)return;this.isLost=false;console.log('校园卡已解挂,恢复正常使用');}

挂失状态的全局影响:

  • 所有消费按钮禁用
  • 充值功能通常也暂停(避免资金风险)
  • 付款码页面显示"已挂失"遮罩
  • 交易记录仍可查看,但不能进行任何资金操作

真实场景说明:实际项目中挂失需要验证身份(密码/短信/人脸),且挂失状态要与服务端同步,不能仅本地修改。

3.5 消费记录筛选与查询

/** * 获取指定类型的交易记录 * @param type '全部' | '消费' | '充值' */privategetFilteredTransactions(type:string):Transaction[]{if(type==='全部'){returnthis.transactions;}returnthis.transactions.filter(t=>t.type===type);}/** * 按分类筛选消费记录 */privategetTransactionsByCategory(category:string):Transaction[]{returnthis.transactions.filter(t=>t.type==='消费'&&t.category===category);}

筛选功能是交易列表的常用交互,一般会提供:全部/消费/充值三个 Tab 切换,以及按时间范围、分类的高级筛选。

四、统计功能实现

4.1 本月消费/充值统计

/** * 计算本月总消费 */privategetMonthlyConsume():number{constnow=newDate();constcurrentMonth=now.getMonth();constcurrentYear=now.getFullYear();returnthis.transactions.filter(t=>{consttransDate=newDate(t.time);returnt.type==='消费'&&transDate.getMonth()===currentMonth&&transDate.getFullYear()===currentYear;}).reduce((sum,t)=>sum+t.amount,0);}/** * 计算本月总充值 */privategetMonthlyRecharge():number{// 逻辑同上,过滤 type === '充值'// ...}

统计功能的核心是filter + reduce的组合:先按条件筛选出目标记录,再累加金额。这是数据处理中非常经典的模式。

4.2 消费分类聚合

/** * 获取各分类消费金额 * 返回 {餐饮: 85.5, 超市: 32, ...} */privategetCategoryStats():Record<string,number>{conststats:Record<string,number>={};this.transactions.filter(t=>t.type==='消费').forEach(t=>{if(stats[t.category]){stats[t.category]+=t.amount;}else{stats[t.category]=t.amount;}});returnstats;}

分类统计数据可以直接用来绘制饼图、柱状图等可视化图表,是消费分析功能的基础。

4.3 限额设置与校验

/** * 设置单日消费限额 * @param limit 限额金额,0表示不限 */privatesetDailyLimit(limit:number):void{if(limit<0)return;this.dailyLimit=limit;}/** * 计算今日已消费金额 */privategetTodayTotalConsume():number{consttoday=newDate().toDateString();returnthis.transactions.filter(t=>t.type==='消费'&&newDate(t.time).toDateString()===today).reduce((sum,t)=>sum+t.amount,0);}

限额功能是校园卡的常见安全措施,防止卡片丢失后被盗刷造成大额损失。配合挂失功能形成双重保障。

五、UI 设计与交互体验

5.1 整体布局结构

页面采用经典的卡片式布局,从上到下依次为:

  1. 顶部余额大卡片(砖红色渐变背景):突出显示当前余额、卡状态、今日消费
  2. 快捷功能区:充值、付款码、挂失、账单四个圆形入口
  3. 充值金额选择区:20/50/100/200 四个固定金额 + 自定义输入
  4. 统计概览区:本月消费、本月充值、消费笔数三个数据卡片
  5. 交易记录列表:支持 Tab 切换(全部/消费/充值),可滚动

5.2 主题色与视觉规范

主色调:砖红色#B91C1C

选择砖红色作为校园卡主题色的原因:

  • 契合金融、支付、校园卡的产品属性
  • 红色代表"金额减少"的语义,与消费场景匹配
  • 视觉上有稳重、可信的感觉,符合金融类产品的安全感

辅助色体系:

  • 消费金额:红色#DC2626(减号前缀)
  • 充值金额:绿色#16A34A(加号前缀)
  • 挂失状态:灰色#6B7280遮罩
  • 警告提示:橙色#F59E0B

5.3 交易记录的视觉区分

列表中的每一条交易记录,通过颜色和图标快速区分类型:

交易类型金额颜色前缀符号图标视觉感受
消费红色-购物/餐具图标支出
充值绿色+钱包/箭头图标收入
退款绿色+退回箭头图标返还

这种设计符合用户的心理模型——红色=花钱,绿色=进钱,不需要额外的学习成本。

六、关键技术点解析

6.1 金额精度问题与解决方案

这是所有金融类应用的第一课:永远不要相信浮点数计算

常见的精度坑:

0.1+0.2=0.300000000000000041.0-0.9=0.09999999999999998

三种解决方案对比:

方案实现方式优点缺点
整数分所有金额以分为单位存储精度绝对准确,计算快展示时需要格式化
toFixed每次计算后保留两位小数实现简单仍有微小精度风险
高精度库使用 decimal.js 等第三方库专业可靠增加包体积

校园卡场景推荐:金额不大的场景用"整数分"方案最稳妥。

6.2 交易流水的时间倒序排列

// 新记录永远插入数组头部this.transactions=[newTrans,...this.transactions];

为什么不使用 unshift?

  • unshift是原地修改,@State可能检测不到变化
  • 展开运算符创建新数组,触发完整的状态更新
  • 符合不可变数据的最佳实践

更严谨的方案:按时间戳排序,而不是依赖插入顺序。当数据来自服务端时,必须以时间排序为准。

6.3 挂失状态的全局影响

挂失是一个典型的"全局状态影响多个功能"的场景。正确的做法是:

  • 状态源唯一(只有一个isLost变量)
  • 所有按钮、功能入口都绑定同一个状态做禁用判断
  • 不要每个功能各自维护挂失状态,容易出现不一致

七、常见问题与优化建议

开发中容易踩的坑

  1. 金额浮点数精度:直接加减导致余额出现多位小数,展示异常
  2. 交易记录顺序错乱:误用push而不是头部插入,最新记录不在最前面
  3. 挂失状态不同步:某个功能入口忘记加挂失判断,出现逻辑漏洞
  4. 余额负数:消费校验和扣款之间有竞态条件,极端情况余额变负

可优化方向

  • 数据持久化:交易记录存入关系型数据库,重启不丢失
  • 数据加密:敏感金额数据本地加密存储
  • 图表可视化:接入图表组件展示消费趋势、分类占比
  • 支付对接:接入真实支付 SDK 完成充值流程
  • 多卡管理:支持绑定多张校园卡、公交卡

八、项目总结与扩展方向

核心收获

通过这个校园卡项目,你应该掌握了:

  1. 金融类应用的数据模型设计:统一交易流水、余额唯一真值
  2. 防御性编程思维:每一步操作前的状态、金额、权限校验
  3. 状态一致性管理:挂失等全局状态对所有功能的统一影响
  4. 数据统计计算:filter + reduce 处理聚合统计的经典模式

后续功能扩展建议

如果想继续深化这个项目,可以增加:

  • 💳NFC 刷卡:调用 HarmonyOS NFC 能力模拟刷卡消费
  • 📊消费分析:月度账单、消费趋势图、分类占比饼图
  • 🔄转账功能:校园卡之间的余额转账
  • 📱校园码合一:付款码、门禁码、借书码一码通用
  • 💡生活缴费:电费、水费、网费在线缴纳
  • 🔐生物识别:指纹/人脸验证充值、大额消费确认

项目效果

本文使用 HarmonyOS 和 ArkTS 实现一个校园卡充值与消费记录应用。

应用可以查看校园卡余额,在线充值,查看消费记录,挂失补卡,并提供消费统计、充值记录、限额设置等功能。

项目使用 DevEco Studio 开发,适配 API 23 及以上版本。

运行效果