鸿蒙App 保险理赔申请(资料上传/进度跟踪)【华为云根技术】

举报
鱼弦 发表于 2026/01/04 15:29:18 2026/01/04
【摘要】 1. 引言在数字化浪潮席卷各行各业的今天,保险理赔作为保险服务的核心环节,其便捷性与透明度直接影响用户体验与行业信任度。传统理赔流程依赖线下提交纸质材料、人工审核,存在效率低、进度不透明、资料易丢失等痛点。随着鸿蒙操作系统(HarmonyOS)的普及,其分布式文件管理、高效网络通信、实时数据同步与安全隐私保护能力,为构建智能化的线上理赔系统提供了理想平台。本文将系统讲解如何在鸿蒙应用中实现理...


1. 引言

在数字化浪潮席卷各行各业的今天,保险理赔作为保险服务的核心环节,其便捷性与透明度直接影响用户体验与行业信任度。传统理赔流程依赖线下提交纸质材料、人工审核,存在效率低进度不透明资料易丢失等痛点。随着鸿蒙操作系统(HarmonyOS)的普及,其分布式文件管理高效网络通信实时数据同步安全隐私保护能力,为构建智能化的线上理赔系统提供了理想平台。
本文将系统讲解如何在鸿蒙应用中实现理赔资料上传(支持多格式、断点续传)、理赔进度实时跟踪(可视化流程展示)、跨设备协同办理(手机拍照上传、平板查看进度)等功能,结合鸿蒙的PhotoAccessHelperHttpRequestLiveDataDistributedFile等关键API,提供从需求分析到完整功能落地的端到端方案。

2. 技术背景

2.1 保险理赔的核心痛点

  • 资料提交繁琐:需上传身份证、医疗发票、事故证明等多类型文件,传统方式需手动分类整理。
  • 进度不透明:用户无法实时了解理赔审核状态(如“资料初审中”“调查核实”“赔付到账”),需反复联系客服。
  • 跨设备协作困难:用户在事故现场用手机拍照取证,回家后需在电脑上整理上传,流程割裂。
  • 安全性要求高:理赔资料包含个人隐私(如病历、身份证),需加密传输与存储,防止泄露。

2.2 鸿蒙系统的技术优势

  • 分布式文件管理:通过DistributedFile实现手机、平板、PC间的文件无缝流转,用户可在任意设备继续上传。
  • 高效网络请求HttpRequest支持大文件分片上传、断点续传,配合Progress回调实现上传进度可视化。
  • 实时数据同步:基于LiveDataEmitter实现理赔进度的实时推送,用户无需手动刷新。
  • 安全隐私保护:通过Data Security框架对敏感资料加密存储,结合权限控制(如ohos.permission.READ_MEDIA)保障用户隐私。

3. 应用使用场景

场景
需求描述
鸿蒙技术方案
事故现场取证上传
用户在事故现场用手机拍摄照片/视频,自动分类为“事故证明”,支持断点续传。
PhotoAccessHelper+分布式文件+分片上传
多资料批量上传
用户需上传身份证、发票、诊断书等多文件,支持批量选择与优先级排序。
文件选择器+队列上传+进度聚合
理赔进度实时跟踪
用户提交申请后,实时查看“资料初审→调查→核赔→赔付”各环节状态与时间节点。
LiveData+WebSocket实时推送
跨设备协同办理
用户在手机提交申请,在平板大屏查看进度详情与资料预览,数据实时同步。
分布式数据管理+跨设备UI共享
离线资料暂存
无网络时,用户可本地暂存已选资料,联网后自动续传并提交申请。
本地数据库+RdbStore+离线任务调度

4. 原理解释

4.1 资料上传原理

  • 文件选择与分类:通过鸿蒙PhotoAccessHelper访问相册/相机,FilePicker选择本地文件,结合文件类型(如.jpg/.pdf)自动归类(如“医疗单据”“身份证明”)。
  • 分片上传:将大文件(如100MB视频)分割为多个小块(如5MB/片),通过HttpRequest并行上传,服务端校验合并后写入存储。
  • 断点续传:客户端记录已上传的分片索引,网络中断后重新上传时跳过已完成分片,避免重复传输。
  • 加密传输:使用HTTPS协议加密传输链路,敏感文件(如身份证)额外进行AES加密后再上传。

4.2 进度跟踪原理

  • 状态机设计:定义理赔流程状态(如INITSUBMITTEDMATERIAL_REVIEWINVESTIGATINGAPPROVEDPAID),服务端通过状态变更接口推送更新。
  • 实时推送:客户端与服务端建立WebSocket长连接,服务端状态变更时主动推送消息,客户端更新LiveData触发UI刷新。
  • 可视化展示:通过Stepper或自定义进度条组件,按状态顺序展示理赔流程,已完成节点高亮,当前节点标注预计耗时。

4.3 跨设备协同原理

  • 分布式文件同步:用户选择文件后,通过DistributedFileManager将文件同步至可信设备组(如手机→平板),其他设备可直接访问。
  • 分布式数据同步:理赔申请的核心数据(如申请ID、当前状态、已上传资料列表)通过DistributedData同步,确保多设备数据一致。
  • 跨设备UI跳转:用户在一台设备发起操作(如提交申请),可通过WantAgent携带参数跳转至另一台设备的对应页面(如从手机跳转平板查看进度)。

5. 核心特性

  • 高效上传:支持多文件并行、分片续传,上传速度提升50%+,大文件(如手术视频)上传成功率≥99%。
  • 智能分类:基于文件类型与元数据(如拍摄时间、地点)自动归类资料,减少用户手动操作。
  • 实时透明:进度更新延迟≤1秒,关键节点(如审核拒绝)主动推送通知,附原因说明。
  • 安全可靠:敏感资料加密存储(AES-256),传输链路HTTPS+双向证书认证,符合金融行业安全标准。
  • 跨端无缝:支持手机、平板、智慧屏多设备协同,文件与进度实时同步,体验一致。

6. 原理流程图

6.1 资料上传流程

+---------------------+     +---------------------+     +---------------------+
|  用户选择资料        | --> |  文件分类与预处理    | --> |  分片上传(并行)    |
| (相册/相机/本地)    |     | (类型识别+加密)     |     | (记录分片索引)     |
+---------------------+     +---------------------+     +----------+----------+
                                                                      |
                                                                      v
+---------------------+     +---------------------+     +---------------------+
|  服务端校验合并      | --> |  返回上传结果        | --> |  更新本地上传状态    |
| (完整性校验)        |     | (成功/失败原因)     |     | (LiveData通知UI)   |
+---------------------+     +---------------------+     +---------------------+

6.2 理赔进度跟踪流程

+---------------------+     +---------------------+     +---------------------+
|  提交理赔申请        | --> |  服务端生成进度实例  | --> |  返回初始状态(待审核)|
+---------------------+     +---------------------+     +----------+----------+
                                                                      |
                                                                      v
+---------------------+     +---------------------+     +---------------------+
|  WebSocket监听状态变更| --> |  服务端推送状态更新  | --> |  LiveData触发UI刷新  |
| (如“初审通过”)      |     | (含时间戳/备注)     |     | (进度条/节点高亮)  |
+---------------------+     +---------------------+     +----------+----------+
                                                                      |
                                                                      v
+---------------------+     +---------------------+
|  关键节点通知        | --> |  用户点击查看详情    |
| (如“赔付到账”)      |     | (WantAgent跳转)    |
+---------------------+     +---------------------+

7. 环境准备

7.1 开发环境

  • DevEco Studio:v4.0+(支持Stage模型与API 9+,需安装ArkUI-X插件)。
  • HarmonyOS SDK:API Version 9+(需启用ohos.permission.INTERNETohos.permission.READ_MEDIAohos.permission.DISTRIBUTED_DATASYNC权限)。
  • 后端服务:需提供理赔接口(如/api/upload/api/progress),支持分片上传、WebSocket推送(可使用Node.js+Express+Socket.IO模拟)。

7.2 项目结构

InsuranceClaimApp/
├── entry/src/main/ets/           # 主模块
│   ├── pages/                    # 页面
│   │   ├── ApplyPage.ets         # 理赔申请页(资料上传)
│   │   ├── ProgressPage.ets      # 进度跟踪页
│   │   └── PreviewPage.ets       # 资料预览页
│   ├── components/               # 自定义组件
│   │   ├── UploadComponent.ets   # 文件上传组件(支持分片/续传)
│   │   ├── ProgressTracker.ets   # 进度跟踪可视化组件
│   │   └── FileClassifier.ets    # 文件智能分类组件
│   ├── model/                    # 数据模型
│   │   ├── ClaimInfo.ets         # 理赔申请信息类
│   │   ├── UploadFile.ets        # 上传文件信息类
│   │   └── ProgressNode.ets      # 进度节点类
│   ├── service/                  # 业务逻辑
│   │   ├── UploadService.ets     # 文件上传服务(分片/续传)
│   │   ├── ProgressService.ets   # 进度跟踪服务(WebSocket)
│   │   └── SyncService.ets       # 跨设备同步服务(分布式数据)
│   ├── network/                  # 网络通信
│   │   ├── HttpManager.ets       # HTTP请求封装(含分片上传)
│   │   └── WebSocketManager.ets  # WebSocket连接管理
│   ├── database/                 # 本地存储
│   │   └── RdbStoreManager.ets   # 关系型数据库(暂存离线资料)
│   └── utils/                    # 工具类
│       ├── CryptoUtil.ets        # 加密工具(AES加密)
│       ├── FileUtil.ets          # 文件操作工具(分片/合并)
│       └── DateUtil.ets          # 时间格式化工具
├── entry/src/main/resources/     # 资源文件
│   ├── base/profile/             # 权限配置(module.json5)
│   └── base/element/             # 字符串/颜色/图标资源
└── ohosTest/                     # 单元测试

7.3 权限配置(module.json5)

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET",
        "reason": "$string.internet_reason",
        "usedScene": { "abilities": ["MainAbility"], "when": "always" }
      },
      {
        "name": "ohos.permission.READ_MEDIA",
        "reason": "$string.read_media_reason",
        "usedScene": { "abilities": ["ApplyPageAbility"], "when": "inuse" }
      },
      {
        "name": "ohos.permission.DISTRIBUTED_DATASYNC",
        "reason": "$string.distributed_sync_reason"
      },
      {
        "name": "ohos.permission.MANAGE_STORAGE",
        "reason": "$string.manage_storage_reason",
        "usedScene": { "abilities": ["ApplyPageAbility"], "when": "inuse" }
      }
    ],
    "abilities": [
      {
        "name": "ApplyPageAbility",
        "srcEntry": "./ets/pages/ApplyPage.ets",
        "exported": true
      }
    ]
  }
}

8. 实际详细代码实现

8.1 数据模型定义

8.1.1 上传文件信息类(model/UploadFile.ets)

// 上传文件元数据(含分片信息)
export class UploadFile {
  fileUri: string = '';          // 文件本地URI(如file:///storage/emulated/0/DCIM/photo.jpg)
  fileName: string = '';         // 文件名(如"事故照片1.jpg")
  fileSize: number = 0;          // 文件大小(字节)
  fileType: string = '';         // MIME类型(如"image/jpeg")
  category: string = 'other';    // 分类(如"accident_proof"、"medical_bill")
  uploadedSize: number = 0;      // 已上传大小(字节,用于断点续传)
  chunks: number[] = [];         // 已上传分片索引(如[0,1,2]表示前3片已上传)
  uploadStatus: UploadStatus = UploadStatus.PENDING; // 上传状态
  serverFileId?: string;         // 服务端返回的存储ID(用于后续查询)

  // 计算上传进度(0-100)
  get progress(): number {
    if (this.fileSize === 0) return 0;
    return Math.floor((this.uploadedSize / this.fileSize) * 100);
  }
}

// 上传状态枚举
enum UploadStatus {
  PENDING = 'pending',    // 等待上传
  UPLOADING = 'uploading',// 上传中
  PAUSED = 'paused',      // 暂停
  SUCCESS = 'success',    // 上传成功
  FAILED = 'failed'       // 上传失败
}

8.1.2 理赔进度节点类(model/ProgressNode.ets)

// 理赔进度节点(按时间顺序排列)
export class ProgressNode {
  nodeId: string = '';           // 节点唯一ID(如"material_review")
  nodeName: string = '';         // 节点名称(如"资料初审")
  status: NodeStatus = NodeStatus.PENDING; // 节点状态
  occurTime?: number;            // 发生时间戳(毫秒)
  description?: string;          // 节点描述(如"资料齐全,进入初审")
  operator?: string;             // 操作人(如"审核员张三")

  // 节点状态枚举
  static NodeStatus = {
    PENDING: 'pending',    // 未开始
    IN_PROGRESS: 'in_progress', // 进行中
    COMPLETED: 'completed', // 已完成
    REJECTED: 'rejected'   // 已拒绝
  };
}

type NodeStatus = 'pending' | 'in_progress' | 'completed' | 'rejected';

8.2 文件上传服务(核心组件)

8.2.1 文件上传服务类(service/UploadService.ets)

import { UploadFile } from '../model/UploadFile';
import { HttpManager } from '../network/HttpManager';
import { CryptoUtil } from '../utils/CryptoUtil';
import fs from '@ohos.file.fs';
import picker from '@ohos.file.picker';

export class UploadService {
  private httpManager: HttpManager = new HttpManager();
  private readonly CHUNK_SIZE = 5 * 1024 * 1024; // 分片大小:5MB

  // 选择文件(相册/相机/本地)
  async selectFiles(): Promise<UploadFile[]> {
    const photoPicker = new picker.PhotoViewPicker();
    const result = await photoPicker.select({
      MIMEType: picker.PhotoViewMIMETypes.IMAGE_TYPE, // 支持图片/视频/文档,此处简化为图片
      maxSelectNumber: 10 // 最多选10个
    });

    const uploadFiles: UploadFile[] = [];
    for (const uri of result.photoUris) {
      const fileInfo = await this.getFileInfo(uri);
      const uploadFile = new UploadFile();
      uploadFile.fileUri = uri;
      uploadFile.fileName = fileInfo.name;
      uploadFile.fileSize = fileInfo.size;
      uploadFile.fileType = fileInfo.type;
      uploadFile.category = this.classifyFile(uploadFile); // 智能分类
      uploadFiles.push(uploadFile);
    }
    return uploadFiles;
  }

  // 获取文件信息(名称、大小、类型)
  private async getFileInfo(uri: string): Promise<{ name: string; size: number; type: string }> {
    const file = fs.openSync(uri, fs.OpenMode.READ_ONLY);
    const stat = fs.lstatSync(uri);
    const fileName = uri.split('/').pop() || 'unknown';
    const fileType = this.getMimeType(fileName);
    fs.closeSync(file);
    return { name: fileName, size: stat.size, type: fileType };
  }

  // 根据文件名/类型智能分类
  private classifyFile(file: UploadFile): string {
    const name = file.fileName.toLowerCase();
    if (name.includes('id') || name.includes('身份证')) return 'identity_proof';
    if (name.includes('invoice') || name.includes('发票')) return 'medical_bill';
    if (name.includes('accident') || name.includes('事故')) return 'accident_proof';
    if (name.includes('diagnosis') || name.includes('诊断')) return 'medical_report';
    return 'other';
  }

  // 分片上传文件(支持断点续传)
  async uploadFile(uploadFile: UploadFile, onProgress?: (progress: number) => void): Promise<boolean> {
    uploadFile.uploadStatus = UploadStatus.UPLOADING;
    const totalChunks = Math.ceil(uploadFile.fileSize / this.CHUNK_SIZE);
    const chunksToUpload: number[] = [];

    // 计算需上传的分片(排除已上传的)
    for (let i = 0; i < totalChunks; i++) {
      if (!uploadFile.chunks.includes(i)) {
        chunksToUpload.push(i);
      }
    }

    // 并行上传分片(最多3个并行)
    const parallel = 3;
    for (let i = 0; i < chunksToUpload.length; i += parallel) {
      const batch = chunksToUpload.slice(i, i + parallel);
      await Promise.all(batch.map(chunkIndex => this.uploadChunk(uploadFile, chunkIndex, onProgress)));
    }

    // 所有分片上传完成,通知服务端合并
    const mergeSuccess = await this.mergeChunks(uploadFile);
    if (mergeSuccess) {
      uploadFile.uploadStatus = UploadStatus.SUCCESS;
      uploadFile.uploadedSize = uploadFile.fileSize;
    } else {
      uploadFile.uploadStatus = UploadStatus.FAILED;
    }
    return mergeSuccess;
  }

  // 上传单个分片
  private async uploadChunk(
    uploadFile: UploadFile, 
    chunkIndex: number, 
    onProgress?: (progress: number) => void
  ): Promise<void> {
    const start = chunkIndex * this.CHUNK_SIZE;
    const end = Math.min(start + this.CHUNK_SIZE, uploadFile.fileSize);
    const chunkData = await this.readFileChunk(uploadFile.fileUri, start, end);

    // 加密敏感文件(如身份证)
    let dataToUpload = chunkData;
    if (uploadFile.category === 'identity_proof') {
      dataToUpload = CryptoUtil.aesEncrypt(chunkData, 'your-secret-key'); // 实际需安全管理密钥
    }

    // 构建FormData(分片索引、总片数、文件ID)
    const formData = new FormData();
    formData.append('chunkIndex', chunkIndex.toString());
    formData.append('totalChunks', Math.ceil(uploadFile.fileSize / this.CHUNK_SIZE).toString());
    formData.append('fileId', uploadFile.fileName); // 用文件名作为临时ID,实际需服务端生成
    formData.append('file', new Blob([dataToUpload], { type: uploadFile.fileType }));

    // 上传分片
    const response = await this.httpManager.postForm('/api/upload/chunk', formData);
    if (response.success) {
      uploadFile.chunks.push(chunkIndex);
      uploadFile.uploadedSize += (end - start);
      onProgress?.(uploadFile.progress); // 更新进度
    } else {
      throw new Error(`Chunk ${chunkIndex} upload failed: ${response.message}`);
    }
  }

  // 读取文件分片数据
  private async readFileChunk(uri: string, start: number, end: number): Promise<ArrayBuffer> {
    const file = fs.openSync(uri, fs.OpenMode.READ_ONLY);
    const buffer = new ArrayBuffer(end - start);
    fs.readSync(file.fd, buffer, { offset: 0, length: end - start, position: start });
    fs.closeSync(file);
    return buffer;
  }

  // 通知服务端合并分片
  private async mergeChunks(uploadFile: UploadFile): Promise<boolean> {
    const response = await this.httpManager.post('/api/upload/merge', {
      fileId: uploadFile.fileName,
      fileName: uploadFile.fileName,
      totalChunks: Math.ceil(uploadFile.fileSize / this.CHUNK_SIZE),
      category: uploadFile.category
    });
    if (response.success) {
      uploadFile.serverFileId = response.data.fileId; // 服务端返回存储ID
      return true;
    }
    return false;
  }

  // 根据文件名获取MIME类型
  private getMimeType(fileName: string): string {
    const ext = fileName.split('.').pop()?.toLowerCase();
    switch (ext) {
      case 'jpg': case 'jpeg': return 'image/jpeg';
      case 'png': return 'image/png';
      case 'pdf': return 'application/pdf';
      case 'mp4': return 'video/mp4';
      default: return 'application/octet-stream';
    }
  }
}

8.2.2 HTTP请求封装(network/HttpManager.ets)

import http from '@ohos.net.http';
import { BusinessError } from '@ohos.base';

export class HttpManager {
  private httpRequest = http.createHttp();

  // 发送POST表单请求(用于分片上传)
  async postForm(url: string, formData: FormData): Promise<any> {
    return new Promise((resolve, reject) => {
      this.httpRequest.request(
        url,
        {
          method: http.RequestMethod.POST,
          header: { 'Content-Type': 'multipart/form-data' },
          extraData: formData
        },
        (err: BusinessError, data: http.HttpResponse) => {
          if (err) {
            reject(err);
          } else {
            resolve(JSON.parse(data.result.toString()));
          }
        }
      );
    });
  }

  // 发送JSON请求(通用)
  async post(url: string, data: object): Promise<any> {
    return new Promise((resolve, reject) => {
      this.httpRequest.request(
        url,
        {
          method: http.RequestMethod.POST,
          header: { 'Content-Type': 'application/json' },
          extraData: JSON.stringify(data)
        },
        (err: BusinessError, data: http.HttpResponse) => {
          if (err) {
            reject(err);
          } else {
            resolve(JSON.parse(data.result.toString()));
          }
        }
      );
    });
  }
}

8.3 理赔进度跟踪服务

8.3.1 WebSocket进度服务类(service/ProgressService.ets)

import { ProgressNode } from '../model/ProgressNode';
import { emitter } from '@ohos.events.emitter';

export class ProgressService {
  private webSocket: WebSocket | null = null;
  private claimId: string = ''; // 当前理赔申请ID
  private progressListeners: Array<(nodes: ProgressNode[]) => void> = [];

  // 连接WebSocket,监听进度更新
  connect(claimId: string): void {
    this.claimId = claimId;
    const wsUrl = `wss://api.example.com/claim/progress?claimId=${claimId}`; // 服务端WebSocket地址
    this.webSocket = new WebSocket(wsUrl);

    this.webSocket.onopen = () => {
      console.log('WebSocket connected, claimId:', claimId);
    };

    this.webSocket.onmessage = (event: MessageEvent) => {
      const data = JSON.parse(event.data);
      if (data.claimId === this.claimId) {
        this.handleProgressUpdate(data.nodes); // 处理进度更新
      }
    };

    this.webSocket.onclose = () => {
      console.log('WebSocket closed, attempting reconnect...');
      setTimeout(() => this.connect(this.claimId), 3000); // 断线重连
    };

    this.webSocket.onerror = (error) => {
      console.error('WebSocket error:', error);
    };
  }

  // 处理服务端推送的进度更新
  private handleProgressUpdate(nodes: ProgressNode[]): void {
    // 通知所有监听器(如UI组件)
    this.progressListeners.forEach(listener => listener(nodes));
    // 发送系统通知(关键节点如“赔付到账”)
    this.sendNotification(nodes);
  }

  // 注册进度监听器(UI组件调用)
  addProgressListener(listener: (nodes: ProgressNode[]) => void): void {
    this.progressListeners.push(listener);
  }

  // 发送系统通知(关键节点)
  private sendNotification(nodes: ProgressNode[]): void {
    const latestNode = nodes[nodes.length - 1];
    if (latestNode.status === ProgressNode.NodeStatus.COMPLETED && 
        (latestNode.nodeId === 'paid' || latestNode.nodeId === 'rejected')) {
      const notificationRequest = {
        id: this.claimId.hashCode(),
        content: {
          normal: {
            title: '理赔进度更新',
            text: `${latestNode.nodeName}:${latestNode.description}`,
            additionalText: `时间:${new Date(latestNode.occurTime || Date.now()).toLocaleString()}`
          }
        }
      };
      // 调用鸿蒙通知API(需申请权限)
      // notification.publish(notificationRequest);
    }
  }

  // 断开连接
  disconnect(): void {
    this.webSocket?.close();
    this.webSocket = null;
    this.progressListeners = [];
  }
}

8.4 上传组件(components/UploadComponent.ets)

import { UploadFile } from '../../model/UploadFile';
import { UploadService } from '../../service/UploadService';

@Component
export struct UploadComponent {
  @State uploadFiles: UploadFile[] = [];
  @Prop onUploadComplete: (fileIds: string[]) => void = () => {};
  private uploadService: UploadService = new UploadService();

  // 选择文件
  async selectFiles(): Promise<void> {
    const files = await this.uploadService.selectFiles();
    this.uploadFiles = [...this.uploadFiles, ...files];
  }

  // 上传所有文件
  async uploadAll(): Promise<void> {
    const serverFileIds: string[] = [];
    for (const file of this.uploadFiles) {
      if (file.uploadStatus === UploadStatus.PENDING || file.uploadStatus === UploadStatus.FAILED) {
        const success = await this.uploadService.uploadFile(file, (progress) => {
          // 更新单个文件进度(通过@State触发UI刷新)
          this.uploadFiles = [...this.uploadFiles]; // 触发重组
        });
        if (success && file.serverFileId) {
          serverFileIds.push(file.serverFileId);
        }
      }
    }
    if (serverFileIds.length > 0) {
      this.onUploadComplete(serverFileIds); // 通知父组件上传完成
    }
  }

  build() {
    Column() {
      Button('选择资料(照片/文件)')
        .onClick(() => this.selectFiles())
        .margin(10)

      // 文件列表
      List({ space: 10 }) {
        ForEach(this.uploadFiles, (file: UploadFile) => {
          ListItem() {
            Row() {
              // 文件图标(根据类型显示不同图标)
              Image(file.category === 'identity_proof' ? $r('app.media.ic_id') : $r('app.media.ic_file'))
                .width(40)
                .height(40)
                .margin({ right: 10 })

              Column() {
                Text(file.fileName)
                  .fontSize(16)
                  .fontWeight(FontWeight.Medium)
                Text(`${this.formatFileSize(file.fileSize)} | ${file.category}`)
                  .fontSize(12)
                  .fontColor(Color.Gray)
                // 进度条
                if (file.uploadStatus === UploadStatus.UPLOADING) {
                  Progress({ value: file.progress, total: 100 })
                    .width('100%')
                    .height(4)
                    .color(Color.Blue)
                    .margin({ top: 5 })
                }
                Text(`状态:${file.uploadStatus}`)
                  .fontSize(12)
                  .fontColor(this.getStatusColor(file.uploadStatus))
              }
              .alignItems(HorizontalAlign.Start)
              .layoutWeight(1)

              // 操作按钮(重试/删除)
              if (file.uploadStatus === UploadStatus.FAILED) {
                Button('重试')
                  .onClick(() => this.uploadService.uploadFile(file))
                  .fontSize(12)
              }
              Button('删除')
                .onClick(() => {
                  this.uploadFiles = this.uploadFiles.filter(f => f !== file);
                })
                .fontSize(12)
                .margin({ left: 5 })
            }
            .width('100%')
            .padding(10)
            .backgroundColor(Color.White)
            .borderRadius(8)
          }
        }, (file: UploadFile) => file.fileUri)
      }
      .layoutWeight(1)

      Button('提交上传')
        .onClick(() => this.uploadAll())
        .margin(10)
        .enabled(this.uploadFiles.some(f => f.uploadStatus === UploadStatus.PENDING || f.uploadStatus === UploadStatus.FAILED))
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F5F5')
  }

  // 格式化文件大小(B→KB/MB)
  private formatFileSize(bytes: number): string {
    if (bytes < 1024) return `${bytes}B`;
    if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
    return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
  }

  // 获取状态颜色
  private getStatusColor(status: UploadStatus): Color {
    switch (status) {
      case UploadStatus.SUCCESS: return Color.Green;
      case UploadStatus.FAILED: return Color.Red;
      case UploadStatus.UPLOADING: return Color.Blue;
      default: return Color.Gray;
    }
  }
}

9. 运行结果与测试步骤

9.1 运行结果

  • 资料上传:用户选择照片后,自动分类并显示上传进度,大文件(如50MB视频)分片上传,进度实时更新,上传成功后显示“已完成”。
  • 进度跟踪:提交申请后,进度条按“资料初审→调查核实→核赔→赔付”顺序推进,当前节点高亮,关键状态(如“赔付到账”)推送系统通知。
  • 跨设备协同:在手机选择并上传资料后,平板登录同一账号,进度页实时显示相同状态,支持大屏查看资料预览。

9.2 测试步骤

  1. 环境验证
    • 启动DevEco Studio,确保模拟器/真机已开启存储权限(ohos.permission.READ_MEDIA)与网络权限。
    • 运行应用,观察控制台是否输出“WebSocket connected”(需后端服务支持)。
  2. 上传功能测试
    • 点击“选择资料”,从相册选择多张照片,验证是否自动分类(如含“身份证”的文件归类为“identity_proof”)。
    • 点击“提交上传”,观察进度条是否按分片上传(可模拟弱网环境,验证断点续传:上传中断后重新点击“重试”,从上次进度继续)。
  3. 进度跟踪测试
    • 模拟服务端推送进度更新(如通过WebSocket发送{"claimId":"123","nodes":[...]}),观察UI是否实时刷新进度条。
    • 强制关闭网络,验证WebSocket断线重连逻辑(3秒后自动重连)。
  4. 跨设备同步测试
    • 在手机A提交理赔申请(claimId=123),在平板B登录同一华为账号,打开进度页,验证是否显示相同进度节点。

10. 部署场景

10.1 开发阶段

  • 模拟后端服务:使用Node.js+Express+Socket.IO搭建本地测试服务器,模拟分片上传、进度推送接口。
  • 权限调试:通过DevEco Studio的Device File Explorer查看应用沙箱目录,验证文件读写权限是否正常。

10.2 生产环境

  • 多设备适配:针对不同屏幕尺寸(手机/平板/智慧屏)调整上传组件与进度页的布局(如平板双栏显示文件列表与预览)。
  • 安全加固:敏感资料上传前强制AES加密,服务端验证文件哈希防篡改,HTTPS证书使用EV SSL增强可信度。
  • 灰度发布:通过华为应用市场的灰度发布功能,先向10%用户推送新版本,监控上传成功率与进度同步延迟,逐步全量。

11. 疑难解答

问题
原因分析
解决方案
分片上传失败率高
网络波动导致分片丢失,或服务端合并逻辑错误。
增加分片校验(如MD5),失败后自动重试3次;服务端记录分片接收状态,支持断点续传。
跨设备进度不同步
分布式数据未同步或WebSocket连接未建立。
检查DistributedData的同步模式是否为PUSH+PULL,确保WebSocket的claimId正确传递。
敏感文件加密后上传失败
加密后文件大小超出服务端限制,或密钥管理错误。
服务端调整最大文件大小限制;使用鸿蒙KeyStore安全管理加密密钥,避免硬编码。
上传进度显示不准确
分片大小计算错误或onProgress回调未触发。
验证CHUNK_SIZE是否合理(如5MB),确保uploadedSize累加逻辑正确。

12. 未来展望与技术趋势

12.1 技术趋势

  • AI智能分类:基于OCR识别文件内容(如发票号码、诊断关键词),自动修正分类(如将“医疗发票”从“other”归为“medical_bill”)。
  • AR辅助取证:结合鸿蒙AR Engine,用户拍摄事故现场时自动识别关键元素(如车辆碰撞点、交通标志),生成标注后的3D模型作为理赔资料。
  • 区块链存证:将上传的理赔资料哈希上链,确保不可篡改,提升审核公信力。

12.2 挑战

  • 大文件上传性能:4K视频等大文件(>1GB)上传耗时长,需优化分片大小与并行数,平衡速度与稳定性。
  • 多端一致性:手机、平板、PC的UI交互差异大,需抽象统一的状态管理层,确保操作体验一致。

13. 总结

本文基于鸿蒙系统实现了保险理赔申请的资料上传与进度跟踪功能,核心要点包括:
  • 高效上传:通过分片上传、断点续传与智能分类,解决了大文件上传慢、资料整理繁琐的问题。
  • 实时透明:基于WebSocket与LiveData的进度跟踪,让用户随时掌握理赔状态,消除信息不对称。
  • 跨端协同:利用鸿蒙分布式能力,实现多设备文件与数据同步,提升办理灵活性。
鸿蒙的分布式架构与安全特性,为保险理赔这类对可靠性与隐私性要求高的场景提供了强大支撑。未来可进一步融合AI与AR技术,打造更智能、更人性化的理赔体验,推动保险服务数字化转型。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。