- 创建药品通知服务模块,统一管理药品提醒通知的调度和取消 - 新增独立的通知设置页面,支持总开关和药品提醒开关分离控制 - 重构药品详情页面,移除频率编辑功能到独立页面 - 优化药品添加流程,支持拍照和相册选择图片 - 改进通知权限检查和错误处理机制 - 更新用户偏好设置,添加药品提醒开关配置
197 lines
6.4 KiB
TypeScript
197 lines
6.4 KiB
TypeScript
import type { Medication } from '@/types/medication';
|
||
import { getMedicationReminderEnabled, getNotificationEnabled } from '@/utils/userPreferences';
|
||
import * as Notifications from 'expo-notifications';
|
||
import { notificationService, NotificationTypes } from './notifications';
|
||
|
||
/**
|
||
* 药品通知服务
|
||
* 负责管理药品提醒通知的调度和取消
|
||
*/
|
||
export class MedicationNotificationService {
|
||
private static instance: MedicationNotificationService;
|
||
private notificationPrefix = 'medication_';
|
||
|
||
private constructor() {}
|
||
|
||
public static getInstance(): MedicationNotificationService {
|
||
if (!MedicationNotificationService.instance) {
|
||
MedicationNotificationService.instance = new MedicationNotificationService();
|
||
}
|
||
return MedicationNotificationService.instance;
|
||
}
|
||
|
||
/**
|
||
* 检查是否可以发送药品通知
|
||
*/
|
||
private async canSendMedicationNotifications(): Promise<boolean> {
|
||
try {
|
||
// 检查总通知开关
|
||
const notificationEnabled = await getNotificationEnabled();
|
||
if (!notificationEnabled) {
|
||
console.log('总通知开关已关闭,跳过药品通知');
|
||
return false;
|
||
}
|
||
|
||
// 检查药品通知开关
|
||
const medicationReminderEnabled = await getMedicationReminderEnabled();
|
||
if (!medicationReminderEnabled) {
|
||
console.log('药品通知开关已关闭,跳过药品通知');
|
||
return false;
|
||
}
|
||
|
||
// 检查系统权限
|
||
const permissionStatus = await notificationService.getPermissionStatus();
|
||
if (permissionStatus !== 'granted') {
|
||
console.log('系统通知权限未授予,跳过药品通知');
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
} catch (error) {
|
||
console.error('检查药品通知权限失败:', error);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 为药品安排通知
|
||
*/
|
||
async scheduleMedicationNotifications(medication: Medication): Promise<void> {
|
||
try {
|
||
const canSend = await this.canSendMedicationNotifications();
|
||
if (!canSend) {
|
||
console.log('药品通知权限不足,跳过安排通知');
|
||
return;
|
||
}
|
||
|
||
// 先取消该药品的现有通知
|
||
await this.cancelMedicationNotifications(medication.id);
|
||
|
||
// 为每个用药时间安排通知
|
||
for (const time of medication.medicationTimes) {
|
||
const [hour, minute] = time.split(':').map(Number);
|
||
|
||
// 创建通知内容
|
||
const notificationContent = {
|
||
title: '用药提醒',
|
||
body: `该服用 ${medication.name} 了 (${medication.dosageValue}${medication.dosageUnit})`,
|
||
data: {
|
||
type: NotificationTypes.MEDICATION_REMINDER,
|
||
medicationId: medication.id,
|
||
medicationName: medication.name,
|
||
dosage: `${medication.dosageValue}${medication.dosageUnit}`,
|
||
},
|
||
sound: true,
|
||
priority: 'high' as const,
|
||
};
|
||
|
||
// 安排每日重复通知
|
||
const notificationId = await notificationService.scheduleCalendarRepeatingNotification(
|
||
notificationContent,
|
||
{
|
||
type: Notifications.SchedulableTriggerInputTypes.DAILY,
|
||
hour,
|
||
minute,
|
||
}
|
||
);
|
||
|
||
console.log(`已为药品 ${medication.name} 安排通知,时间: ${time},通知ID: ${notificationId}`);
|
||
}
|
||
} catch (error) {
|
||
console.error('安排药品通知失败:', error);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 取消药品的所有通知
|
||
*/
|
||
async cancelMedicationNotifications(medicationId: string): Promise<void> {
|
||
try {
|
||
// 获取所有已安排的通知
|
||
const allNotifications = await notificationService.getAllScheduledNotifications();
|
||
|
||
// 过滤出该药品的通知并取消
|
||
for (const notification of allNotifications) {
|
||
const data = notification.content.data as any;
|
||
if (data?.type === NotificationTypes.MEDICATION_REMINDER &&
|
||
data?.medicationId === medicationId) {
|
||
await notificationService.cancelNotification(notification.identifier);
|
||
console.log(`已取消药品通知,ID: ${notification.identifier}`);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('取消药品通知失败:', error);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 重新安排所有激活药品的通知
|
||
*/
|
||
async rescheduleAllMedicationNotifications(medications: Medication[]): Promise<void> {
|
||
try {
|
||
// 先取消所有药品通知
|
||
for (const medication of medications) {
|
||
await this.cancelMedicationNotifications(medication.id);
|
||
}
|
||
|
||
// 重新安排激活药品的通知
|
||
const activeMedications = medications.filter(m => m.isActive);
|
||
for (const medication of activeMedications) {
|
||
await this.scheduleMedicationNotifications(medication);
|
||
}
|
||
|
||
console.log(`已重新安排 ${activeMedications.length} 个激活药品的通知`);
|
||
} catch (error) {
|
||
console.error('重新安排药品通知失败:', error);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 发送立即的药品通知(用于测试)
|
||
*/
|
||
async sendTestMedicationNotification(medication: Medication): Promise<string> {
|
||
try {
|
||
const canSend = await this.canSendMedicationNotifications();
|
||
if (!canSend) {
|
||
throw new Error('药品通知权限不足');
|
||
}
|
||
|
||
return await notificationService.sendImmediateNotification({
|
||
title: '用药提醒测试',
|
||
body: `这是 ${medication.name} 的测试通知 (${medication.dosageValue}${medication.dosageUnit})`,
|
||
data: {
|
||
type: NotificationTypes.MEDICATION_REMINDER,
|
||
medicationId: medication.id,
|
||
medicationName: medication.name,
|
||
dosage: `${medication.dosageValue}${medication.dosageUnit}`,
|
||
},
|
||
sound: true,
|
||
priority: 'high',
|
||
});
|
||
} catch (error) {
|
||
console.error('发送测试药品通知失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取所有已安排的药品通知
|
||
*/
|
||
async getMedicationNotifications(): Promise<Notifications.NotificationRequest[]> {
|
||
try {
|
||
const allNotifications = await notificationService.getAllScheduledNotifications();
|
||
|
||
// 过滤出药品相关的通知
|
||
return allNotifications.filter(notification =>
|
||
notification.content.data?.type === NotificationTypes.MEDICATION_REMINDER
|
||
);
|
||
} catch (error) {
|
||
console.error('获取药品通知失败:', error);
|
||
return [];
|
||
}
|
||
}
|
||
}
|
||
|
||
// 导出单例实例
|
||
export const medicationNotificationService = MedicationNotificationService.getInstance();
|