Files
digital-pilates/services/medicationNotifications.ts
richarjiang f4ce3d9edf feat(medications): 重构药品通知系统并添加独立设置页面
- 创建药品通知服务模块,统一管理药品提醒通知的调度和取消
- 新增独立的通知设置页面,支持总开关和药品提醒开关分离控制
- 重构药品详情页面,移除频率编辑功能到独立页面
- 优化药品添加流程,支持拍照和相册选择图片
- 改进通知权限检查和错误处理机制
- 更新用户偏好设置,添加药品提醒开关配置
2025-11-11 16:43:27 +08:00

197 lines
6.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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();