
鸿蒙跨端家庭药盒助手开发指南 原创
鸿蒙跨端家庭药盒助手开发指南
一、系统架构设计
基于HarmonyOS的AI能力和分布式技术,构建智能药品管理与提醒系统:
药品识别层:通过OCR和图像识别技术识别药品信息
数据管理层:存储药品信息和服药计划
提醒服务层:定时提醒用户服药
跨端同步层:多设备间同步药品数据和提醒状态
!https://example.com/harmony-medicine-assistant-arch.png
二、核心代码实现
药品识别服务
// MedicineService.ets
import ocr from ‘@ohos.ai.ocr’;
import ai from ‘@ohos.ai’;
import distributedData from ‘@ohos.distributedData’;
import { MedicineInfo, MedicineReminder } from ‘./MedicineTypes’;
class MedicineService {
private static instance: MedicineService = null;
private ocrEngine: ocr.OCREngine;
private modelManager: ai.ModelManager;
private dataManager: distributedData.DataManager;
private listeners: MedicineListener[] = [];
private constructor() {
this.initOCREngine();
this.initModelManager();
this.initDataManager();
public static getInstance(): MedicineService {
if (!MedicineService.instance) {
MedicineService.instance = new MedicineService();
return MedicineService.instance;
private initOCREngine(): void {
try {
const context = getContext() as common.Context;
this.ocrEngine = ocr.createOCREngine(context);
catch (err) {
console.error('初始化OCR引擎失败:', JSON.stringify(err));
}
private initModelManager(): void {
try {
this.modelManager = ai.createModelManager(getContext());
// 加载药品识别模型
this.modelManager.loadModel({
modelName: 'medicine_recognition',
modelPath: 'resources/rawfile/medicine.model',
callback: (err, data) => {
if (err) {
console.error('加载药品模型失败:', JSON.stringify(err));
}
});
catch (err) {
console.error('初始化模型管理器失败:', JSON.stringify(err));
}
private initDataManager(): void {
this.dataManager = distributedData.createDataManager({
bundleName: ‘com.example.medicine’,
area: distributedData.Area.GLOBAL,
isEncrypted: true
});
this.dataManager.registerDataListener('medicine_sync', (data) => {
this.handleSyncData(data);
});
public async recognizeMedicine(imageData: ArrayBuffer): Promise<MedicineInfo> {
try {
// 使用OCR识别药品文字信息
const ocrResult = await this.ocrEngine.recognize({
image: imageData,
language: 'zh',
options: {
detectDirection: true,
detectLanguage: true
});
// 使用AI模型识别药品类型
const modelInput = {
data: imageData,
width: 224,
height: 224,
format: 'RGB'
};
const modelOutput = await this.modelManager.runModel({
modelName: 'medicine_recognition',
input: modelInput
});
const medicineInfo: MedicineInfo = {
id: Date.now().toString(),
name: modelOutput.result.name || '未知药品',
type: modelOutput.result.type || '其他',
dosage: this.extractDosage(ocrResult.text),
description: ocrResult.text,
imageData: imageData,
timestamp: Date.now()
};
// 同步药品信息
this.syncMedicineInfo(medicineInfo);
return medicineInfo;
catch (err) {
console.error('药品识别失败:', JSON.stringify(err));
throw err;
}
private extractDosage(text: string): string {
// 简单提取剂量信息(实际应用中需要更复杂的逻辑)
const dosageRegex = /(\d+.?\d)\s(mgg ml 片 粒
丸)/g;
const matches = dosageRegex.exec(text);
return matches ? matches[0] : ‘请咨询医生’;
public async addReminder(reminder: MedicineReminder): Promise<void> {
try {
// 存储到本地数据库
const database = relationalStore.getRdbStore(getContext(), {
name: 'medicine_reminder.db',
securityLevel: relationalStore.SecurityLevel.S1
});
await database.insert('reminders', reminder);
// 同步提醒信息
this.syncReminder(reminder);
// 设置提醒通知
this.scheduleNotification(reminder);
catch (err) {
console.error('添加提醒失败:', JSON.stringify(err));
throw err;
}
private syncMedicineInfo(info: MedicineInfo): void {
this.dataManager.syncData(‘medicine_sync’, {
type: ‘medicine_info’,
data: info,
timestamp: Date.now()
});
private syncReminder(reminder: MedicineReminder): void {
this.dataManager.syncData('medicine_sync', {
type: 'medicine_reminder',
data: reminder,
timestamp: Date.now()
});
private handleSyncData(data: any): void {
if (!data) return;
switch (data.type) {
case 'medicine_info':
this.notifyMedicineAdded(data.data);
break;
case 'medicine_reminder':
this.notifyReminderAdded(data.data);
break;
}
private notifyMedicineAdded(info: MedicineInfo): void {
this.listeners.forEach(listener => {
listener.onMedicineAdded?.(info);
});
private notifyReminderAdded(reminder: MedicineReminder): void {
this.listeners.forEach(listener => {
listener.onReminderAdded?.(reminder);
});
private scheduleNotification(reminder: MedicineReminder): void {
const notificationRequest: notification.NotificationRequest = {
content: {
title: '服药提醒',
text: 该服用 ${reminder.medicineName} 了,
additionalText: 剂量: ${reminder.dosage},
tapAction: {
bundleName: 'com.example.medicine',
abilityName: 'MainAbility'
},
id: parseInt(reminder.id),
deliveryTime: reminder.nextRemindTime
};
notification.publish(notificationRequest).then(() => {
console.log('提醒设置成功');
}).catch(err => {
console.error('设置提醒失败:', JSON.stringify(err));
});
public addListener(listener: MedicineListener): void {
if (!this.listeners.includes(listener)) {
this.listeners.push(listener);
}
public removeListener(listener: MedicineListener): void {
this.listeners = this.listeners.filter(l => l !== listener);
}
interface MedicineListener {
onMedicineAdded?(info: MedicineInfo): void;
onReminderAdded?(reminder: MedicineReminder): void;
export const medicineService = MedicineService.getInstance();
主界面实现
// MainScreen.ets
import { medicineService } from ‘./MedicineService’;
import { cameraService } from ‘./CameraService’;
import { MedicineInfo, MedicineReminder } from ‘./MedicineTypes’;
@Component
export struct MainScreen {
@State hasPermission: boolean = false;
@State isProcessing: boolean = false;
@State currentMedicine: MedicineInfo | null = null;
@State reminders: MedicineReminder[] = [];
@State showReminderDialog: boolean = false;
@State newReminder: MedicineReminder = this.createEmptyReminder();
aboutToAppear() {
this.checkPermissions();
medicineService.addListener({
onMedicineAdded: (info) => {
this.handleMedicineAdded(info);
},
onReminderAdded: (reminder) => {
this.handleReminderAdded(reminder);
});
this.loadReminders();
aboutToDisappear() {
medicineService.removeListener({
onMedicineAdded: (info) => {
this.handleMedicineAdded(info);
},
onReminderAdded: (reminder) => {
this.handleReminderAdded(reminder);
});
build() {
Column() {
// 标题栏
Row() {
Text('家庭药盒助手')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
Button(this.hasPermission ? '添加药品' : '授权')
.width(100)
.onClick(() => {
if (this.hasPermission) {
this.showActionMenu();
else {
this.requestPermissions();
})
.padding(10)
.width('100%')
// 当前药品信息
if (this.currentMedicine) {
Column() {
Row() {
Image(this.currentMedicine.imageData)
.width(80)
.height(80)
.objectFit(ImageFit.Contain)
.margin({ right: 15 })
Column() {
Text(this.currentMedicine.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 5 })
Text(类型: ${this.currentMedicine.type})
.fontSize(14)
.fontColor('#666666')
.margin({ bottom: 2 })
Text(剂量: ${this.currentMedicine.dosage})
.fontSize(14)
.fontColor('#666666')
.alignItems(HorizontalAlign.Start)
.width(‘100%’)
.padding(10)
.backgroundColor('#F5F5F5')
.borderRadius(8)
.margin({ bottom: 20 })
Button('设置提醒')
.width(200)
.height(50)
.fontSize(18)
.onClick(() => {
this.newReminder.medicineName = this.currentMedicine.name;
this.newReminder.dosage = this.currentMedicine.dosage;
this.showReminderDialog = true;
})
.width(‘100%’)
.margin({ bottom: 20 })
// 提醒列表
if (this.reminders.length > 0) {
Column() {
Text('服药提醒')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 10 })
List({ space: 10 }) {
ForEach(this.reminders, (reminder) => {
ListItem() {
Column() {
Row() {
Text(reminder.medicineName)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
Text(reminder.dosage)
.fontSize(14)
.fontColor('#666666')
.margin({ bottom: 5 })
Row() {
Text('下次提醒:')
.fontSize(14)
.fontColor('#666666')
Text(this.formatTime(reminder.nextRemindTime))
.fontSize(14)
.fontColor('#409EFF')
.margin({ left: 5 })
Row() {
Text(频率: ${reminder.frequency}次/天)
.fontSize(14)
.fontColor('#666666')
.layoutWeight(1)
Button('完成')
.width(60)
.height(30)
.fontSize(12)
.onClick(() => {
this.markAsTaken(reminder);
})
.margin({ top: 5 })
.padding(10)
.width('100%')
.backgroundColor('#FFFFFF')
.border({ width: 1, color: '#E0E0E0' })
.borderRadius(8)
})
.height(300)
.width(‘100%’)
else if (!this.currentMedicine) {
Column() {
Text('暂无药品信息')
.fontSize(18)
.margin({ bottom: 10 })
Text('点击"添加药品"按钮开始识别药品')
.fontSize(16)
.fontColor('#666666')
.padding(20)
.width('90%')
.backgroundColor('#F5F5F5')
.borderRadius(8)
.margin({ top: 50 })
}
.width('100%')
.height('100%')
.padding(20)
// 设置提醒对话框
if (this.showReminderDialog) {
DialogComponent({
title: '设置服药提醒',
content: this.buildReminderDialogContent(),
confirm: {
value: '保存',
action: () => this.saveReminder()
},
cancel: {
value: '取消',
action: () => this.showReminderDialog = false
})
}
private buildReminderDialogContent(): void {
Column() {
Row() {
Text(‘药品名称:’)
.fontSize(16)
.width(80)
Text(this.newReminder.medicineName)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 15 })
Row() {
Text('剂量:')
.fontSize(16)
.width(80)
Text(this.newReminder.dosage)
.fontSize(16)
.margin({ bottom: 15 })
Row() {
Text('开始时间:')
.fontSize(16)
.width(80)
DatePicker({
start: new Date(),
selected: new Date(this.newReminder.startTime),
onDateChange: (value) => {
this.newReminder.startTime = value.getTime();
})
.margin({ bottom: 15 })
Row() {
Text('每日次数:')
.fontSize(16)
.width(80)
TextPicker({
range: ['1', '2', '3', '4'],
selected: parseInt(this.newReminder.frequency) - 1,
onValueChange: (value) => {
this.newReminder.frequency = value;
})
.margin({ bottom: 15 })
Row() {
Text('提醒方式:')
.fontSize(16)
.width(80)
Toggle({ type: ToggleType.Checkbox, isOn: this.newReminder.notification })
.onChange((isOn) => {
this.newReminder.notification = isOn;
})
Text('通知提醒')
.fontSize(16)
.margin({ left: 10 })
}
.padding(20)
private createEmptyReminder(): MedicineReminder {
const now = new Date();
now.setMinutes(0);
now.setSeconds(0);
return {
id: Date.now().toString(),
medicineName: '',
dosage: '',
startTime: now.getTime(),
nextRemindTime: now.getTime(),
frequency: '1',
notification: true,
taken: false
};
private async checkPermissions(): Promise<void> {
try {
const permissions = [
'ohos.permission.USE_AI',
'ohos.permission.CAMERA',
'ohos.permission.DISTRIBUTED_DATASYNC',
'ohos.permission.NOTIFICATION'
];
const result = await abilityAccessCtrl.verifyPermissions(
getContext(),
permissions
);
this.hasPermission = result.every(perm => perm.granted);
catch (err) {
console.error('检查权限失败:', JSON.stringify(err));
this.hasPermission = false;
}
private async requestPermissions(): Promise<void> {
this.hasPermission = await medicineService.requestPermissions();
if (!this.hasPermission) {
prompt.showToast({ message: '授权失败,无法使用药品识别功能' });
}
private showActionMenu(): void {
ActionMenu.show({
title: ‘添加药品方式’,
buttons: [
value: ‘camera’,
action: () => this.recognizeFromCamera()
},
value: ‘manual’,
action: () => this.addMedicineManually()
]
});
private async recognizeFromCamera(): Promise<void> {
try {
this.isProcessing = true;
const imageData = await cameraService.captureFrame();
const medicineInfo = await medicineService.recognizeMedicine(imageData);
this.currentMedicine = medicineInfo;
catch (err) {
console.error('药品识别失败:', JSON.stringify(err));
prompt.showToast({ message: '药品识别失败,请重试' });
finally {
this.isProcessing = false;
}
private addMedicineManually(): void {
this.currentMedicine = {
id: Date.now().toString(),
name: ‘手动添加药品’,
type: ‘其他’,
dosage: ‘请咨询医生’,
description: ‘’,
imageData: new ArrayBuffer(0),
timestamp: Date.now()
};
private async loadReminders(): Promise<void> {
try {
const database = relationalStore.getRdbStore(getContext(), {
name: 'medicine_reminder.db',
securityLevel: relationalStore.SecurityLevel.S1
});
const predicates = new relationalStore.RdbPredicates('reminders');
const result = await database.query(predicates, ['*']);
this.reminders = result.map(row => ({
id: row.id.toString(),
medicineName: row.medicineName,
dosage: row.dosage,
startTime: row.startTime,
nextRemindTime: row.nextRemindTime,
frequency: row.frequency,
notification: row.notification === 1,
taken: row.taken === 1
}));
catch (err) {
console.error('加载提醒失败:', JSON.stringify(err));
}
private async saveReminder(): Promise<void> {
try {
// 计算下次提醒时间
const frequency = parseInt(this.newReminder.frequency);
const interval = 24 / frequency 60 60 * 1000;
this.newReminder.nextRemindTime = this.newReminder.startTime;
await medicineService.addReminder(this.newReminder);
this.showReminderDialog = false;
this.newReminder = this.createEmptyReminder();
catch (err) {
console.error('保存提醒失败:', JSON.stringify(err));
prompt.showToast({ message: '保存提醒失败,请重试' });
}
private async markAsTaken(reminder: MedicineReminder): Promise<void> {
try {
const database = relationalStore.getRdbStore(getContext(), {
name: ‘medicine_reminder.db’,
securityLevel: relationalStore.SecurityLevel.S1
});
// 更新为已服用
const predicates = new relationalStore.RdbPredicates('reminders');
predicates.equalTo('id', reminder.id);
const values = {
taken: 1
};
await database.update(values, predicates);
// 计算下次提醒时间
const frequency = parseInt(reminder.frequency);
const interval = 24 / frequency 60 60 * 1000;
const nextTime = Date.now() + interval;
// 更新下次提醒时间
const newValues = {
taken: 0,
nextRemindTime: nextTime
};
await database.update(newValues, predicates);
// 重新加载提醒
this.loadReminders();
prompt.showToast({ message: '已记录服药' });
catch (err) {
console.error('更新提醒状态失败:', JSON.stringify(err));
prompt.showToast({ message: '操作失败,请重试' });
}
private handleMedicineAdded(info: MedicineInfo): void {
this.currentMedicine = info;
this.isProcessing = false;
private handleReminderAdded(reminder: MedicineReminder): void {
this.loadReminders();
private formatTime(timestamp: number): string {
const date = new Date(timestamp);
return {date.getHours()}:{date.getMinutes().toString().padStart(2, '0')};
}
类型定义
// MedicineTypes.ets
export interface MedicineInfo {
id: string;
name: string;
type: string;
dosage: string;
description: string;
imageData: ArrayBuffer;
timestamp: number;
export interface MedicineReminder {
id: string;
medicineName: string;
dosage: string;
startTime: number; // 时间戳
nextRemindTime: number; // 时间戳
frequency: string; // 每日次数
notification: boolean;
taken: boolean;
三、项目配置与权限
权限配置
// module.json5
“module”: {
"requestPermissions": [
“name”: “ohos.permission.USE_AI”,
"reason": "使用AI模型识别药品"
},
“name”: “ohos.permission.CAMERA”,
"reason": "拍摄药品照片"
},
“name”: “ohos.permission.DISTRIBUTED_DATASYNC”,
"reason": "同步药品数据"
},
“name”: “ohos.permission.NOTIFICATION”,
"reason": "发送服药提醒"
],
"abilities": [
“name”: “MainAbility”,
"type": "page",
"visible": true
},
“name”: “CameraAbility”,
"type": "service",
"backgroundModes": ["camera"]
},
“name”: “ReminderAbility”,
"type": "service",
"backgroundModes": ["notification"]
]
}
四、总结与扩展
本家庭药盒助手系统实现了以下核心功能:
智能药品识别:通过图像识别和OCR技术自动识别药品信息
服药计划管理:灵活设置服药时间和频率
智能提醒服务:准时提醒用户服药
跨设备同步:家庭成员间共享药品数据和服药记录
扩展方向:
药品数据库集成:对接权威药品数据库获取更详细信息
用药记录分析:生成用药报告和统计图表
药品库存管理:跟踪药品剩余量并提醒补充
家庭成员共享:多用户协同管理家庭用药
健康数据整合:与健康监测设备数据关联分析
紧急情况处理:漏服提醒和紧急联系人通知
通过HarmonyOS的AI能力和分布式技术,我们构建了一个智能化的家庭药盒管理系统,帮助用户科学管理服药计划,提高用药安全性,并在家庭成员间实现用药信息的无缝同步。
