鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个掌上银行

138 阅读2分钟

鸿蒙掌上银行应用开发指南 (HarmonyOS 5.0)

一、项目架构设计

1.1 整体架构

BankApp/
├── entry/
│   ├── src/
│   │   ├── main/
│   │   │   ├── ets/
│   │   │   │   ├── entryability/    # 入口Ability
│   │   │   │   ├── 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 安全登录模块

// ets/pages/Login.ets
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 账户总览模块

// ets/pages/AccountOverview.ets
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 转账汇款模块

// ets/pages/Transfer.ets
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 分布式跨设备转账

// ets/service/DistributedTransferService.ets
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 智能语音助手

// ets/service/VoiceAssistant.ets
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) {
    // 调用AI语音识别API
    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 安全键盘组件

// ets/components/SecureKeypad.ets
@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 交易风控系统

// ets/service/RiskControlService.ets
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": "跨设备转账"
      }
    ]
  }
}