2026山东大学软件学院创新实训——IntelliHealth(六)

概要

本周主要完成了智能提醒系统的后端开发部分。相比之前的健康档案和用户画像模块,提醒系统更偏向“交互”和“任务管理”,所以这周重点放在提醒任务模型、提醒创建管理、触发时间计算、触发日志记录和状态联动上。

目前后端已经支持三类核心提醒:疲劳提醒、服药提醒、药物过期提醒。同时也实现了不少于 20 条并发提醒任务的管理能力,为下周前端页面开发和 Android 本地定时通知做准备。

一、提醒任务模型设计

核心要点

提醒任务需要支持不同类型、不同周期和不同触发来源,所以我没有把它简单设计成一个“时间 + 内容”的表,而是拆成了提醒类型、重复类型、触发时间、状态和扩展信息几个部分。

提醒类型如下:

public enum ReminderType {
    FATIGUE,          // 疲劳提醒
    MEDICATION,       // 服药提醒
    MEDICINE_EXPIRY,  // 药物过期提醒
    HEALTH_CHECK      // 通用健康提醒
}

周期类型如下:

public enum ReminderRepeatType {
    ONCE,             // 仅一次
    DAILY,            // 每天
    WEEKLY,           // 每周
    INTERVAL_HOURS,   // 每隔几小时
    REALTIME          // 实时联动
}

提醒任务实体主要字段如下:

@Document(collection = "reminder_tasks")
public class ReminderTask {
    @Id
    private String id;

    private String userId;
    private ReminderType type;
    private String title;
    private String message;

    private String medicationName;
    private String reminderTime;
    private ReminderRepeatType repeatType;

    private Integer intervalHours;
    private List<Integer> daysOfWeek;

    private LocalDate expiryDate;
    private Integer advanceDays;

    private Boolean enabled;
    private Instant nextTriggerAt;
    private Instant lastTriggeredAt;

    private Integer triggerToleranceSeconds;
    private String source;
    private Map<String, Object> metadata;
}

整体关系大致如下:

用户

提醒任务 ReminderTask

疲劳提醒

服药提醒

药物过期提醒

触发日志 ReminderTriggerLog

二、提醒创建与管理接口

核心要点

提醒管理部分采用 RESTful 风格接口,前端只需要调用统一的 /api/reminders 路径即可完成提醒任务管理。

主要接口如下:

功能 方法 路径
创建提醒 POST /api/reminders
修改提醒 PUT /api/reminders/{id}
启用/停用提醒 PATCH /api/reminders/{id}/enabled
删除提醒 DELETE /api/reminders/{id}
查询提醒列表 GET /api/reminders?userId=u1001
查询提醒总览 GET /api/reminders/overview?userId=u1001

创建服药提醒的请求示例:

{
  "userId": "u1001",
  "type": "MEDICATION",
  "title": "服药提醒",
  "message": "请按计划服药",
  "medicationName": "阿司匹林",
  "reminderTime": "08:00",
  "repeatType": "DAILY",
  "enabled": true
}

后端创建提醒时会自动计算下一次触发时间:

public ReminderTaskResponse createReminder(ReminderTaskRequest request) {
    ReminderTask task = ReminderTask.builder()
        .userId(request.getUserId())
        .type(request.getType())
        .title(defaultTitle(request))
        .message(defaultMessage(request))
        .reminderTime(defaultTime(request.getReminderTime()))
        .repeatType(request.getRepeatType())
        .enabled(true)
        .triggerToleranceSeconds(5)
        .source("USER")
        .build();

    task.setNextTriggerAt(computeNextTrigger(task, Instant.now()));
    return toTaskResponse(reminderTaskRepository.save(task));
}

三、提醒触发时间计算

核心要点

提醒系统最重要的一点是:后端要能明确告诉前端或 Android 端“下一次应该什么时候触发”。

这部分主要由 computeNextTrigger 完成。

不同类型的处理方式略有不同:

  • ONCE:只触发一次。
  • DAILY:每天固定时间触发。
  • WEEKLY:指定星期触发。
  • INTERVAL_HOURS:按间隔小时触发。
  • REALTIME:用于状态联动,不固定时间。

简化后的逻辑如下:

private Instant computeNextTrigger(ReminderTask task, Instant after) {
    if (!Boolean.TRUE.equals(task.getEnabled())) {
        return null;
    }

    if (task.getType() == ReminderType.FATIGUE
            && task.getRepeatType() == ReminderRepeatType.REALTIME) {
        return null;
    }

    if (task.getType() == ReminderType.MEDICINE_EXPIRY
            && task.getExpiryDate() != null) {
        LocalDate triggerDate = task.getExpiryDate().minusDays(task.getAdvanceDays());
        return LocalDateTime.of(triggerDate, parseTime(task.getReminderTime()))
                .atZone(ZoneId.of("Asia/Shanghai"))
                .toInstant();
    }

    // 其他周期继续按每日、每周、间隔小时计算
}

四、触发日志与 ±5 秒误差记录

核心要点

任务书中要求提醒触发时间误差控制在 ±5 秒以内。后端本身不直接弹系统通知,但需要记录每次触发结果,判断实际触发时间和计划触发时间的误差。

因此新增了 ReminderTriggerLog

@Document(collection = "reminder_trigger_logs")
public class ReminderTriggerLog {
    @Id
    private String id;

    private String userId;
    private String reminderId;
    private ReminderType type;

    private String title;
    private String message;

    private Instant scheduledAt;
    private Instant triggeredAt;
    private Long latencyMs;
    private Boolean withinTolerance;

    private String triggerSource;
    private String status;
}

记录触发日志时会计算误差:

Long latencyMs = Math.abs(Duration
        .between(scheduledAt, triggeredAt)
        .toMillis());

Boolean withinTolerance = latencyMs <= 5 * 1000L;

对应接口:

POST /api/reminders/trigger-log

请求示例:

{
  "userId": "u1001",
  "reminderId": "reminder-001",
  "type": "MEDICATION",
  "title": "服药提醒",
  "message": "请按计划服药",
  "scheduledAt": "2026-06-10T00:00:00Z",
  "triggeredAt": "2026-06-10T00:00:03Z",
  "triggerSource": "ANDROID_ALARM"
}

这样后续前端可以直接展示“是否在 ±5 秒内触发”。

五、状态联动疲劳提醒

核心要点

除了普通的定时提醒,还需要实现“状态联动”。也就是当状态识别模块发现用户疲劳时,系统要尽快生成提醒。

目前疲劳判断主要参考三个因素:

  • fatigueLevel 疲劳等级
  • stressLevel 压力水平
  • mood / emotion / notes 中是否包含疲劳相关描述

判断逻辑大致如下:

boolean fatigueDetected =
        fatigue >= 4
        || stress >= 0.75
        || containsFatigueText(request.getMood())
        || containsFatigueText(request.getEmotion())
        || containsFatigueText(request.getNotes());

如果识别为疲劳,就会生成一个一次性疲劳提醒:

ReminderTask task = ReminderTask.builder()
    .userId(userId)
    .type(ReminderType.FATIGUE)
    .title("疲劳干预提醒")
    .message("检测到疲劳信号,请暂停连续用眼并安排 10 分钟休息。")
    .repeatType(ReminderRepeatType.ONCE)
    .enabled(true)
    .nextTriggerAt(now.plusSeconds(1))
    .source("STATUS_LINK")
    .build();

状态联动接口为:

POST /api/reminders/status-link?userId=u1001

返回中会包含:

  • 是否识别到疲劳
  • 是否创建提醒
  • 识别置信度
  • 生成耗时
  • 是否达到 5 秒内生成目标
{
  "userId": "u1001",
  "fatigueDetected": true,
  "reminderCreated": true,
  "effectiveRecognition": true,
  "confidence": 0.98,
  "generationLatencyMs": 3,
  "targetLatencyMs": 5000
}

状态快照接口也接入了这个逻辑,也就是说用户状态识别数据写入健康记录后,会自动尝试生成疲劳提醒。

状态识别结果

写入健康记录

判断疲劳信号

是否疲劳

生成疲劳提醒

不触发提醒

保存触发日志

六、20 条并发提醒任务验收

核心要点

为了验证系统可以稳定管理不少于 20 条提醒任务,我单独实现了一个补齐接口:

POST /api/reminders/capacity-demo?userId=u1001

如果当前用户提醒任务不足 20 条,后端会自动补齐:

public List<ReminderTaskResponse> ensureCapacityDemoReminders(String userId) {
    List<ReminderTask> existing =
        reminderTaskRepository.findByUserIdOrderByNextTriggerAtAsc(userId);

    int toCreate = 20 - existing.size();

    for (int i = 0; i < toCreate; i++) {
        ReminderTaskRequest request = new ReminderTaskRequest();
        request.setUserId(userId);
        request.setType(i % 3 == 0
            ? ReminderType.FATIGUE
            : i % 3 == 1
                ? ReminderType.MEDICATION
                : ReminderType.MEDICINE_EXPIRY);
        request.setRepeatType(ReminderRepeatType.DAILY);
        createReminder(request);
    }
}

总览接口会返回当前是否达到并发目标:

{
  "userId": "u1001",
  "totalCount": 20,
  "enabledCount": 20,
  "concurrentCapacityTarget": 20,
  "concurrentCapacityAchieved": true
}

下周计划

下周计划完成智能提醒系统的前端部分开发,主要包括:

  • 开发提醒任务列表页面。
  • 支持用户手动创建疲劳提醒、服药提醒和药物过期提醒。
  • 支持启用、停用和删除提醒。
  • 接入 Android 本地通知和定时任务。
  • 在前端展示提醒触发日志和 ±5 秒误差结果。
  • 完成前后端联调和真机测试。
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐