鸿蒙掌上银行应用开发指南 (HarmonyOS 5.0)
一、项目架构设计
1.1 整体架构
BankApp/
├── entry/
│ ├── src/
│ │ ├── main/
│ │ │ ├── ets/
│ │ │ │ ├── entryability/
│ │ │ │ ├── pages/
│ │ │ │ ├── components/
│ │ │ │ ├── model/
│ │ │ │ ├── utils/
│ │ │ │ └── service/
│ │ │ ├── resources/
│ │ │ └── config.json
1.2 技术栈选型
- UI框架:ArkUI 3.0
- 状态管理:@ohos.app.ability.UIAbility + AppStorage
- 网络请求:@ohos.net.http
- 数据存储:@ohos.data.relationalStore
- 安全认证:@ohos.userIAM.userAuth
- 生物识别:@ohos.userIAM.faceAuth
- 分布式能力:@ohos.distributedDeviceManager
二、核心模块实现
2.1 安全登录模块
import userAuth from '@ohos.userIAM.userAuth';
import faceAuth from '@ohos.userIAM.faceAuth';
@Entry
@Component
struct LoginPage {
@State loginType: 'password' | 'fingerprint' | 'face' = 'password'
@State username: string = ''
@State password: string = ''
async faceLogin() {
try {
const result = await faceAuth.auth({
challenge: 'bank_auth_2023',
authType: faceAuth.FaceAuthType.DEFAULT
});
if (result.code === 0) {
router.replaceUrl({ url: 'pages/Home' });
}
} catch (error) {
promptAction.showToast({ message: '人脸识别失败' });
}
}
async passwordLogin() {
const httpRequest = http.createHttp();
const response = await httpRequest.request(
'https://api.bank.com/login',
{
method: 'POST',
header: { 'Content-Type': 'application/json' },
extraData: JSON.stringify({ username, password })
}
);
if (response.code === 200) {
AppStorage.SetOrCreate('token', response.result.token);
router.replaceUrl({ url: 'pages/Home' });
}
}
build() {
Column() {
Tabs({ barPosition: BarPosition.Start }) {
TabContent() {
TextInput({ placeholder: '请输入账号' })
.onChange((value: string) => { this.username = value })
TextInput({ placeholder: '请输入密码', type: InputType.Password })
.onChange((value: string) => { this.password = value })
Button('登录', { type: ButtonType.Capsule })
.onClick(() => this.passwordLogin())
}.tabBar('密码登录')
TabContent() {
Button('开始人脸识别', { type: ButtonType.Capsule })
.onClick(() => this.faceLogin())
}.tabBar('人脸识别')
}
}
}
}
2.2 账户总览模块
import { AccountService } from '../service/AccountService';
@Entry
@Component
struct AccountOverview {
@State accountList: Array<Account> = []
@State totalAssets: number = 0
async onPageShow() {
this.accountList = await AccountService.getAccountList();
this.totalAssets = this.accountList.reduce((sum, acc) => sum + acc.balance, 0);
}
build() {
Column() {
Card() {
Text('总资产').fontSize(16)
Text(`¥${this.totalAssets.toLocaleString()}`)
.fontSize(24)
.fontColor('#FF0000')
}.margin(10)
List({ space: 10 }) {
ForEach(this.accountList, (account: Account) => {
ListItem() {
AccountItem({ account: account })
}
})
}
.layoutWeight(1)
.divider({ strokeWidth: 1, color: '#F1F1F1' })
}
}
}
@Component
struct AccountItem {
@Prop account: Account
build() {
Row() {
Image(this.account.type === '储蓄卡' ? $r('app.media.ic_debit') : $r('app.media.ic_credit'))
.width(40)
.height(40)
Column() {
Text(this.account.name).fontSize(18)
Text(`**** **** **** ${this.account.number.slice(-4)}`)
.fontSize(14)
.fontColor('#999999')
}.layoutWeight(1)
Text(`¥${account.balance.toLocaleString()}`)
.fontSize(18)
}
.padding(10)
.width('100%')
}
}
2.3 转账汇款模块
import { TransferService } from '../service/TransferService';
import promptAction from '@ohos.promptAction';
@Entry
@Component
struct TransferPage {
@State fromAccount: string = ''
@State toAccount: string = ''
@State amount: string = ''
@State remark: string = ''
async submitTransfer() {
if (!this.validate()) {
return;
}
try {
const result = await TransferService.transfer({
from: this.fromAccount,
to: this.toAccount,
amount: parseFloat(this.amount),
remark: this.remark
});
if (result.success) {
router.pushUrl({
url: 'pages/TransferResult',
params: { result: JSON.stringify(result) }
});
}
} catch (error) {
promptAction.showToast({ message: '转账失败: ' + error.message });
}
}
validate(): boolean {
return true;
}
build() {
Column() {
AccountSelector({
label: '转出账户',
accounts: AccountService.getCurrentUserAccounts(),
onSelect: (acc) => this.fromAccount = acc.number
})
TextInput({ placeholder: '请输入对方账号' })
.onChange((value: string) => { this.toAccount = value })
TextInput({ placeholder: '请输入金额', type: InputType.Number })
.onChange((value: string) => { this.amount = value })
TextInput({ placeholder: '备注(选填)' })
.onChange((value: string) => { this.remark = value })
Button('确认转账', { type: ButtonType.Capsule })
.onClick(() => this.submitTransfer())
}
}
}
三、高级功能实现
3.1 分布式跨设备转账
import deviceManager from '@ohos.distributedDeviceManager';
import distributedData from '@ohos.data.distributedData';
export class DistributedTransferService {
static async startCrossDeviceTransfer(transferInfo: TransferInfo) {
const devices = await deviceManager.getTrustedDeviceListSync();
const kvManager = distributedData.createKVManager({
context: getContext(),
bundleName: 'com.example.bankapp'
});
const kvStore = await kvManager.getKVStore('transferStore', {
createIfMissing: true,
encrypt: true,
backup: false,
autoSync: true
});
await kvStore.put('pendingTransfer', JSON.stringify(transferInfo));
router.pushUrl({
url: 'pages/DeviceSelector',
params: { transferId: transferInfo.id }
});
}
static async receiveTransferFromDevice(deviceId: string) {
const kvManager = ;
const kvStore = await kvManager.getKVStore('transferStore');
const transferData = await kvStore.get('pendingTransfer');
if (transferData) {
return JSON.parse(transferData) as TransferInfo;
}
return null;
}
}
3.2 智能语音助手
import audio from '@ohos.multimedia.audio';
import recorder from '@ohos.multimedia.audio';
export class VoiceAssistant {
private static audioCapturer: audio.AudioCapturer;
private static audioRenderer: audio.AudioRenderer;
static async startVoiceCommand() {
const audioStreamInfo = {
samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_16000,
channels: audio.AudioChannel.CHANNEL_1,
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW
};
this.audioCapturer = await audio.createAudioCapturer({
streamInfo: audioStreamInfo,
capturerInfo: {
source: audio.SourceType.SOURCE_TYPE_MIC,
capturerFlags: 0
}
});
this.startSpeechRecognition();
}
private static async startSpeechRecognition() {
this.audioCapturer.on('data', (buffer: ArrayBuffer) => {
this.processVoiceCommand(buffer);
});
await this.audioCapturer.start();
}
private static async processVoiceCommand(buffer: ArrayBuffer) {
const httpRequest = http.createHttp();
const response = await httpRequest.request(
'https://api.ai-service.com/speech',
{
method: 'POST',
extraData: buffer
}
);
const command = this.parseCommand(response.result);
this.executeCommand(command);
}
private static executeCommand(command: VoiceCommand) {
switch(command.type) {
case 'transfer':
router.pushUrl({
url: 'pages/Transfer',
params: {
toAccount: command.params.account,
amount: command.params.amount
}
});
break;
case 'query':
break;
}
}
}
四、安全防护实现
4.1 安全键盘组件
@Component
export struct SecureKeypad {
@State private inputValue: string = ''
@State private showValue: string = ''
private maxLength: number = 6
@Provide('secureInput') inputValue: string = ''
build() {
Column() {
Row() {
ForEach(Array.from({ length: this.showValue.length }), (_, index) => {
Text('•').fontSize(24)
})
}
Grid() {
ForEach([1, 2, 3, 4, 5, 6, 7, 8, 9, '', 0, 'X'], (num) => {
GridItem() {
if (num === 'X') {
Image($r('app.media.ic_backspace'))
.onClick(() => this.handleBackspace())
} else if (num !== '') {
Text(num.toString())
.onClick(() => this.handleInput(num))
}
}
})
}
}
}
private handleInput(num: number) {
if (this.inputValue.length < this.maxLength) {
this.inputValue += num.toString();
this.showValue += '•';
this.dispatchInputEvent();
}
}
private handleBackspace() {
if (this.inputValue.length > 0) {
this.inputValue = this.inputValue.slice(0, -1);
this.showValue = this.showValue.slice(0, -1);
this.dispatchInputEvent();
}
}
private dispatchInputEvent() {
}
}
4.2 交易风控系统
import geolocation from '@ohos.geolocation';
export class RiskControlService {
static async checkTransferRisk(transfer: TransferInfo): Promise<RiskAssessment> {
const location = await geolocation.getCurrentLocation();
const deviceInfo = await deviceManager.getLocalDeviceInfo();
const riskScore = this.calculateRiskScore({
amount: transfer.amount,
location,
device: deviceInfo,
time: new Date(),
recipient: transfer.toAccount
});
return {
score: riskScore,
suggestion: riskScore > 70 ? 'require_2fa' : 'allow',
reasons: this.getRiskReasons(riskScore)
};
}
private static calculateRiskScore(factors: RiskFactors): number {
let score = 0;
if (factors.amount > 50000) score += 30;
if (!this.isCommonLocation(factors.location)) score += 25;
if (this.isNewDevice(factors.device)) score += 20;
if (this.isUnusualTime(factors.time)) score += 15;
if (this.isHighRiskRecipient(factors.recipient)) score += 10;
return Math.min(score, 100);
}
}
五、应用配置
// entry/src/main/config.json
{
"app": {
"bundleName": "com.example.bankapp",
"vendor": "example",
"version": {
"code": 100,
"name": "1.0.0"
}
},
"deviceConfig": {
"default": {
"network": {
"cleartextTraffic": false,
"securityConfig": {
"domainSettings": {
"domains": [
{
"name": "api.bank.com",
"subnames": ["secure"]
}
]
}
}
}
}
},
"module": {
"name": "entry",
"type": "entry",
"abilities": [
{
"name": "MainAbility",
"icon": "$media:icon",
"label": "掌上银行",
"launchType": "standard",
"backgroundModes": ["dataTransfer", "location", "audio"]
}
],
"reqPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "用于网络请求"
},
{
"name": "ohos.permission.ACCESS_BIOMETRIC",
"reason": "生物识别登录"
},
{
"name": "ohos.permission.LOCATION",
"reason": "风控系统位置验证"
},
{
"name": "ohos.permission.MICROPHONE",
"reason": "语音助手功能"
},
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC",
"reason": "跨设备转账"
}
]
}
}