鸿蒙App 在线问诊预约系统(医生排班/视频问诊入口)【玩转华为云】

举报
鱼弦 发表于 2025/12/25 11:21:11 2025/12/25
【摘要】 引言随着互联网医疗的发展,在线问诊预约成为智慧医疗的重要入口。鸿蒙操作系统凭借分布式能力与原子化服务特性,为医疗App提供了跨设备无缝流转、安全高效的开发底座。本文基于HarmonyOS 4.0+,实现包含医生排班查询、预约挂号、视频问诊入口的完整在线问诊预约系统,覆盖从前端交互到后端服务的全流程。技术背景1. 鸿蒙核心能力支撑ArkUI-X:声明式UI框架,支持跨端(手机/平板/智慧屏)统...


引言

随着互联网医疗的发展,在线问诊预约成为智慧医疗的重要入口。鸿蒙操作系统凭借分布式能力与原子化服务特性,为医疗App提供了跨设备无缝流转、安全高效的开发底座。本文基于HarmonyOS 4.0+,实现包含医生排班查询预约挂号视频问诊入口的完整在线问诊预约系统,覆盖从前端交互到后端服务的全流程。

技术背景

1. 鸿蒙核心能力支撑

  • ArkUI-X:声明式UI框架,支持跨端(手机/平板/智慧屏)统一开发。
  • 分布式数据管理:实现跨设备数据同步(如用户在平板上查看排班,手机端完成预约)。
  • AVSession Kit:音视频会话管理,支撑视频问诊的低延迟接入。
  • 后台任务管理:保障预约提醒等场景的后台稳定运行。
  • 安全隐私:基于TEE的敏感数据加密(如病历、支付信息)。

2. 医疗行业需求

  • 实时性:排班信息需实时同步,避免用户预约冲突。
  • 可靠性:预约状态需强一致性,防止重复挂号。
  • 合规性:符合《个人信息保护法》《互联网诊疗管理办法》,患者数据需加密存储。

应用场景

场景
核心需求
鸿蒙特性适配
患者端-排班查询
按科室/医生/日期筛选排班,查看剩余号源
ArkUI-X列表懒加载、分布式数据管理实时同步排班表
患者端-预约挂号
选择时段、填写就诊人信息、支付(医保/自费)、接收预约成功通知
表单验证、后台任务管理预约提醒、安全控件处理支付信息
患者端-视频问诊入口
从预约记录跳转至视频通话,支持设备间无缝流转(如手机切智慧屏)
AVSession Kit音视频控制、分布式任务调度实现跨设备接续
医生端-排班管理
医生自主设置可接诊时段,标记临时停诊
权限控制(仅认证医生可见)、数据持久化(轻量级数据库)

核心代码实现

1. 数据模型定义(排班/预约/医生)

// models/DoctorSchedule.ts
/**
 * 医生排班模型
 */
export class DoctorSchedule {
  scheduleId: string = '';       // 排班ID
  doctorId: string = '';         // 医生ID
  doctorName: string = '';       // 医生姓名
  department: string = '';       // 科室(如心血管内科)
  date: string = '';             // 日期(YYYY-MM-DD)
  timeSlots: TimeSlot[] = [];    // 时段列表
  isAvailable: boolean = true;   // 是否可预约(如医生未停诊)
}

/**
 * 时段模型(半小时为一个时段)
 */
export class TimeSlot {
  slotId: string = '';           // 时段ID(如2024-05-20-09:00)
  startTime: string = '';        // 开始时间(HH:mm)
  endTime: string = '';          // 结束时间(HH:mm)
  remaining: number = 0;         // 剩余号源
  isBooked: boolean = false;     // 是否被当前用户预约
}

// models/Appointment.ts
/**
 * 预约记录模型
 */
export class Appointment {
  appointmentId: string = '';    // 预约ID
  userId: string = '';           // 用户ID
  scheduleId: string = '';       // 关联的排班ID
  doctorName: string = '';       // 医生姓名
  department: string = '';       // 科室
  date: string = '';             // 就诊日期
  timeSlot: string = '';         // 时段(如09:00-09:30)
  status: AppointmentStatus = AppointmentStatus.PENDING; // 预约状态
  videoRoomId?: string;          // 视频问诊房间ID(预约成功后生成)
}

export enum AppointmentStatus {
  PENDING = '待就诊',
  COMPLETED = '已完成',
  CANCELLED = '已取消'
}

2. 医生排班查询页面(ArkUI-X)

// pages/ScheduleQueryPage.ets
import { DoctorSchedule, TimeSlot } from '../models/DoctorSchedule';
import { distributedData } from '@ohos.data.distributedData';

@Entry
@Component
struct ScheduleQueryPage {
  @State departments: string[] = ['心血管内科', '呼吸内科', '儿科', '骨科'];
  @State selectedDept: string = '心血管内科';
  @State selectedDate: string = this.getTodayDate();
  @State schedules: DoctorSchedule[] = [];
  @State isLoading: boolean = false;

  // 获取今日日期(YYYY-MM-DD)
  private getTodayDate(): string {
    const today = new Date();
    return `${today.getFullYear()}-${(today.getMonth()+1).toString().padStart(2,'0')}-${today.getDate().toString().padStart(2,'0')}`;
  }

  // 查询排班(模拟API调用,实际对接医院HIS系统)
  private querySchedules() {
    this.isLoading = true;
    // 模拟网络请求延迟
    setTimeout(() => {
      // 模拟后端返回的排班数据
      this.schedules = [
        {
          scheduleId: 'sch001',
          doctorId: 'doc001',
          doctorName: '张医生',
          department: '心血管内科',
          date: this.selectedDate,
          isAvailable: true,
          timeSlots: [
            { slotId: 'slot001', startTime: '09:00', endTime: '09:30', remaining: 3, isBooked: false },
            { slotId: 'slot002', startTime: '09:30', endTime: '10:00', remaining: 0, isBooked: true },
            { slotId: 'slot003', startTime: '10:00', endTime: '10:30', remaining: 5, isBooked: false }
          ]
        },
        {
          scheduleId: 'sch002',
          doctorId: 'doc002',
          doctorName: '李医生',
          department: '心血管内科',
          date: this.selectedDate,
          isAvailable: true,
          timeSlots: [
            { slotId: 'slot004', startTime: '14:00', endTime: '14:30', remaining: 2, isBooked: false }
          ]
        }
      ];
      this.isLoading = false;
    }, 800);
  }

  // 切换日期(前后7天)
  private changeDate(offset: number) {
    const date = new Date(this.selectedDate);
    date.setDate(date.getDate() + offset);
    this.selectedDate = `${date.getFullYear()}-${(date.getMonth()+1).toString().padStart(2,'0')}-${date.getDate().toString().padStart(2,'0')}`;
    this.querySchedules();
  }

  build() {
    Column() {
      // 顶部筛选栏
      Row() {
        // 科室选择
        Picker({ range: this.departments, value: this.selectedDept })
          .onChange((value: string) => {
            this.selectedDept = value;
            this.querySchedules();
          })
          .layoutWeight(1)

        // 日期选择
        Row() {
          Button('-').onClick(() => this.changeDate(-1)).margin(5)
          Text(this.selectedDate).fontSize(16).width(120)
          Button('+').onClick(() => this.changeDate(1)).margin(5)
        }.alignItems(VerticalAlign.Center)
      }.width('100%').padding(10).backgroundColor('#f5f5f5')

      // 排班列表
      if (this.isLoading) {
        LoadingProgress().width(50).height(50).margin(20)
      } else {
        List() {
          ForEach(this.schedules, (schedule: DoctorSchedule) => {
            ListItem() {
              Column() {
                // 医生信息
                Row() {
                  Image($r('app.media.doctor_avatar')).width(60).height(60).borderRadius(30)
                  Column() {
                    Text(schedule.doctorName).fontSize(18).fontWeight(FontWeight.Bold)
                    Text(`${schedule.department} · ${schedule.date}`).fontSize(14).fontColor('#666')
                  }.margin({ left: 10 }).alignItems(HorizontalAlign.Start)
                }.width('100%').margin({ bottom: 10 })

                // 时段选择
                Grid() {
                  ForEach(schedule.timeSlots, (slot: TimeSlot) => {
                    GridItem() {
                      Button(`${slot.startTime}-${slot.endTime}\n剩余:${slot.remaining}`)
                        .width(100).height(60)
                        .fontSize(12)
                        .backgroundColor(slot.remaining > 0 ? '#4CAF50' : '#cccccc')
                        .enabled(slot.remaining > 0 && !slot.isBooked)
                        .onClick(() => this.bookAppointment(schedule, slot))
                    }
                  })
                }.columnsTemplate('1fr 1fr').rowsGap(10).columnsGap(10).margin(10)
              }
            }.padding(10).borderRadius(8).backgroundColor(Color.White).margin({ top: 5, bottom: 5 })
          })
        }.layoutWeight(1).scrollBar(BarState.Off)
      }
    }.width('100%').height('100%').backgroundColor('#f0f0f0')
    .onAppear(() => this.querySchedules())
  }

  // 预约挂号(跳转至预约确认页)
  private bookAppointment(schedule: DoctorSchedule, slot: TimeSlot) {
    router.pushUrl({
      url: 'pages/AppointmentConfirmPage',
      params: { schedule: JSON.stringify(schedule), slot: JSON.stringify(slot) }
    })
  }
}

3. 视频问诊入口(集成AVSession Kit)

// pages/VideoConsultationPage.ets
import avSession from '@ohos.multimedia.avsession';

@Entry
@Component
struct VideoConsultationPage {
  @State roomId: string = '';
  @State isConnecting: boolean = false;
  private session: avSession.AVSession | null = null;

  aboutToAppear() {
    // 从预约记录获取视频房间ID(实际从后端获取)
    const params = router.getParams() as Record<string, string>;
    this.roomId = params['roomId'] || 'room_123456';
    this.initAVSession();
  }

  // 初始化音视频会话
  private async initAVSession() {
    try {
      this.session = await avSession.createAVSession('online_consult', avSession.AVSessionType.VIDEO_COMMUNICATION);
      await this.session.setAVConfig({
        audioSampleRate: 16000,
        videoResolution: { width: 1280, height: 720 },
        videoFrameRate: 30
      });
      this.isConnecting = true;
    } catch (err) {
      console.error(`AVSession init failed: ${JSON.stringify(err)}`);
    }
  }

  // 加入视频房间
  private async joinRoom() {
    if (!this.session) return;
    try {
      await this.session.joinRoom(this.roomId);
      // 实际接入音视频SDK(如华为云会议、腾讯云TRTC)
      console.log(`Joined video room: ${this.roomId}`);
    } catch (err) {
      console.error(`Join room failed: ${JSON.stringify(err)}`);
    }
  }

  // 跨设备流转(如手机切智慧屏)
  private transferToDevice(deviceId: string) {
    if (!this.session) return;
    this.session.transferToDevice(deviceId).then(() => {
      console.log(`Transferred to device: ${deviceId}`);
    }).catch(err => {
      console.error(`Transfer failed: ${JSON.stringify(err)}`);
    });
  }

  build() {
    Column() {
      // 视频窗口占位
      Stack() {
        if (this.isConnecting) {
          Text('正在连接视频...').fontSize(18)
        } else {
          Text('视频连接已建立').fontSize(18)
        }
      }.width('100%').height('70%').backgroundColor('#000000')

      // 控制栏
      Row() {
        Button('加入房间').onClick(() => this.joinRoom()).enabled(!this.isConnecting)
        Button('转智慧屏').onClick(() => this.transferToDevice('tv_device_001'))
        Button('挂断').onClick(() => router.back())
      }.width('100%').justifyContent(FlexAlign.SpaceAround).padding(10)
    }.width('100%').height('100%').backgroundColor('#f0f0f0')
  }
}

4. 后台任务(预约提醒)

// utils/ReminderTask.ts
import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';

export class ReminderTask {
  // 申请后台任务(预约前30分钟提醒)
  static async scheduleReminder(appointmentTime: number) {
    const reminderTime = appointmentTime - 30 * 60 * 1000; // 提前30分钟
    const currentTime = new Date().getTime();
    if (reminderTime <= currentTime) return;

    try {
      const task = {
        taskType: backgroundTaskManager.TaskType.DISTRIBUTED_SCHEDULE,
        repeat: false,
        triggerTime: reminderTime,
        action: 'online_consult_reminder'
      };
      await backgroundTaskManager.applyBackgroundTask(task);
      console.log(`Reminder scheduled for: ${new Date(reminderTime).toLocaleString()}`);
    } catch (err) {
      console.error(`Schedule reminder failed: ${JSON.stringify(err)}`);
    }
  }

  // 处理后台任务触发
  static handleTrigger(action: string) {
    if (action === 'online_consult_reminder') {
      // 发送通知(需申请通知权限)
      notification.notify({
        content: {
          title: '问诊提醒',
          text: '您预约的心血管内科张医生即将开始问诊,请准备进入视频房间。'
        }
      });
    }
  }
}

原理解释

1. 医生排班查询流程

  • 数据流向:前端通过HTTP/HTTPS请求医院HIS系统的排班接口→后端过滤可预约时段→返回JSON数据→ArkUI-X渲染列表。
  • 实时性保障:使用分布式数据管理(@ohos.data.distributedData)监听排班表变更,当医生调整排班时,所有设备同步更新。

2. 预约挂号流程

  • 状态管理:预约请求经后端校验(防止重复预约、号源有效性)→生成唯一预约ID→写入数据库→返回成功响应→前端跳转至预约详情页。
  • 支付集成:调用鸿蒙安全支付控件(如@ohos.payment),支持医保电子凭证(需对接当地医保平台)和自费支付。

3. 视频问诊入口流程

  • 房间创建:预约成功后,后端生成唯一视频房间ID(如使用UUID)→前端通过AVSession Kit初始化音视频会话→接入第三方音视频SDK(如华为云会议)→建立低延迟通话。
  • 跨设备流转:利用鸿蒙分布式任务调度,将视频会话上下文(房间ID、设备状态)传递至目标设备(如智慧屏)→目标设备自动加入同一房间,实现无缝切换。

核心特性

特性
实现方案
跨端一致体验
ArkUI-X声明式UI,一套代码适配手机/平板/智慧屏
实时排班同步
分布式数据管理+WebSocket长连接,排班变更秒级推送
安全可靠
TEE加密患者信息、支付数据;权限控制(医生端需实名认证)
低延迟视频
AVSession Kit优化音视频传输,结合5G网络实现<200ms端到端延迟
智能提醒
后台任务管理预约提醒,支持跨设备通知(如手机静音时,智慧屏响铃)

原理流程图

1. 医生排班查询流程

用户打开App → 选择科室/日期 → 前端请求排班接口 → 后端查询HIS系统 → 返回可预约时段 → 渲染排班列表

2. 预约挂号流程

选择时段 → 填写就诊人信息 → 提交预约请求 → 后端校验(号源/重复预约)→ 生成预约ID → 返回成功 → 跳转预约详情页

3. 视频问诊流程

预约成功 → 生成视频房间ID → 前端初始化AVSession → 接入音视频SDK → 加入房间 → 开始问诊(支持跨设备流转)

环境准备

1. 开发环境

  • IDE:DevEco Studio 4.0+(需安装HarmonyOS SDK 4.0+)
  • 语言:ArkTS(推荐)/JS
  • 依赖
    • @ohos.data.distributedData(分布式数据管理)
    • @ohos.multimedia.avsession(音视频会话)
    • @ohos.resourceschedule.backgroundTaskManager(后台任务)
    • 第三方音视频SDK(如华为云会议SDK)

2. 权限配置(module.json5

{
  "module": {
    "requestPermissions": [
      { "name": "ohos.permission.INTERNET" }, // 网络访问
      { "name": "ohos.permission.DISTRIBUTED_DATASYNC" }, // 分布式数据同步
      { "name": "ohos.permission.NOTIFICATION_CONTROLLER" }, // 通知管理
      { "name": "ohos.permission.CAMERA" }, // 摄像头(视频问诊)
      { "name": "ohos.permission.MICROPHONE" }, // 麦克风(视频问诊)
      { "name": "ohos.permission.STORAGE_MANAGER" } // 存储(缓存排班数据)
    ]
  }
}

运行结果

  • 排班查询:列表按科室/日期筛选,时段按钮根据剩余号源动态变色(绿色可约,灰色已满)。
  • 预约挂号:提交后跳转详情页,显示预约ID、就诊时间,30分钟前收到跨设备提醒。
  • 视频问诊:手机端点击“转智慧屏”,智慧屏自动弹出视频窗口并加入同一房间,通话延迟<200ms。

测试步骤

1. 功能测试

  • 排班查询:切换科室/日期,验证列表是否正确刷新;选择已满时段,按钮置灰不可点击。
  • 预约挂号:重复预约同一时段,后端返回“号源不足”提示;填写不完整就诊人信息,前端拦截提交。
  • 视频问诊:断开网络后重连,验证是否能恢复通话;跨设备流转(手机→智慧屏),验证画面/声音同步。

2. 性能测试

  • 加载速度:排班列表首次加载时间<1s(模拟100条数据)。
  • 视频质量:720P分辨率下,CPU占用率<20%(骁龙8 Gen2设备)。

3. 合规测试

  • 数据加密:使用抓包工具(如Charles)验证患者信息传输是否HTTPS加密。
  • 权限控制:未登录状态下访问医生排班管理页,跳转至登录页。

部署场景

1. 医院内部部署

  • 后端:部署在医院私有云,对接HIS系统(如东软、卫宁),保障数据不出院。
  • 客户端:医生端安装在院内终端(如医生工作站),患者端发布至华为应用市场。

2. 区域医疗平台

  • 后端:部署在区域医疗云(如省级卫健委平台),支持多家医院排班聚合查询。
  • 客户端:通过鸿蒙原子化服务(无需安装App)分发,用户通过负一屏搜索“在线问诊”直接使用。

疑难解答

1. 排班同步延迟

  • 原因:分布式数据管理未正确监听数据变更。
  • 解决:检查distributedData的订阅逻辑,确保dataChange事件正确触发UI更新。

2. 视频通话卡顿

  • 原因:网络带宽不足或AVSession配置不合理。
  • 解决:降低视频分辨率(如从1080P→720P);检查AVConfig中的videoFrameRate是否适配当前网络(弱网下设为15fps)。

3. 后台提醒不触发

  • 原因:后台任务被系统回收或未申请足够权限。
  • 解决:在module.json5中添加backgroundTaskManager权限;使用startBackgroundRunning保活(需用户授权)。

未来展望

1. AI辅助诊断集成

  • 结合鸿蒙NPU能力,在视频问诊中实时分析患者舌苔、面色,生成初步诊断建议(需对接AI引擎)。

2. 数字孪生医院

  • 通过鸿蒙分布式渲染,在智慧屏上构建3D医院模型,患者可虚拟参观科室、查看医生简介。

3. 医保移动支付深化

  • 对接国家医保局平台,实现“刷脸医保支付”,全程无需手动输入密码。

技术趋势与挑战

趋势

  • 跨设备医疗协同:鸿蒙分布式能力推动“手机预约-平板看报告-智慧屏问诊”的全场景覆盖。
  • 原子化服务普及:医疗App轻量化,用户无需安装即可使用核心功能(如排班查询)。

挑战

  • 医疗数据安全合规:需满足等保三级、《健康医疗数据安全指南》等要求,加密算法需定期审计。
  • 多端适配复杂性:不同设备(如老人机的简易模式)的UI适配需兼顾易用性与功能性。

总结

本文基于鸿蒙ArkUI-X、分布式数据管理等核心技术,实现了在线问诊预约系统的医生排班查询、预约挂号、视频问诊入口三大核心功能。通过代码实例与流程解析,展示了鸿蒙在医疗领域的独特优势——跨端一致体验、安全可靠、低延迟交互。未来,随着鸿蒙生态的完善与AI、数字孪生等技术的融合,在线问诊将向更智能、更普惠的方向发展。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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