鸿蒙App 在线问诊预约系统(医生排班/视频问诊入口)【玩转华为云】
【摘要】 引言随着互联网医疗的发展,在线问诊预约成为智慧医疗的重要入口。鸿蒙操作系统凭借分布式能力与原子化服务特性,为医疗App提供了跨设备无缝流转、安全高效的开发底座。本文基于HarmonyOS 4.0+,实现包含医生排班查询、预约挂号、视频问诊入口的完整在线问诊预约系统,覆盖从前端交互到后端服务的全流程。技术背景1. 鸿蒙核心能力支撑ArkUI-X:声明式UI框架,支持跨端(手机/平板/智慧屏)统...
引言
技术背景
1. 鸿蒙核心能力支撑
-
ArkUI-X:声明式UI框架,支持跨端(手机/平板/智慧屏)统一开发。 -
分布式数据管理:实现跨设备数据同步(如用户在平板上查看排班,手机端完成预约)。 -
AVSession Kit:音视频会话管理,支撑视频问诊的低延迟接入。 -
后台任务管理:保障预约提醒等场景的后台稳定运行。 -
安全隐私:基于TEE的敏感数据加密(如病历、支付信息)。
2. 医疗行业需求
-
实时性:排班信息需实时同步,避免用户预约冲突。 -
可靠性:预约状态需强一致性,防止重复挂号。 -
合规性:符合《个人信息保护法》《互联网诊疗管理办法》,患者数据需加密存储。
应用场景
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
核心代码实现
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、设备状态)传递至目标设备(如智慧屏)→目标设备自动加入同一房间,实现无缝切换。
核心特性
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
原理流程图
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适配需兼顾易用性与功能性。
总结
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)