2026山东大学软件学院创新实训——IntelliHealth(六)
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;
}
整体关系大致如下:
二、提醒创建与管理接口
核心要点
提醒管理部分采用 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 秒误差结果。
- 完成前后端联调和真机测试。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)