feat(medication): 实现用药提醒短信和电话提醒功能
- 新增 intc-common-message 消息服务模块 - 阿里云短信服务 SmsService(用药提醒、漏服提醒、家属通知) - 阿里云语音服务 VoiceService(用药提醒、漏服提醒电话) - HealthPerson 添加 phone 手机号字段 - MedicationPlan 添加 remind_type 提醒方式字段(1-短信 2-电话 3-短信+电话) - MedicationRemindJob 完善提醒逻辑: - 定时扫描待服药任务发送提醒 - 超时30分钟未服药发送加强提醒 - 配置家属通知功能 - 新增数据库更新SQL和Nacos配置示例文件
This commit is contained in:
@@ -100,6 +100,13 @@
|
||||
<groupId>com.intc</groupId>
|
||||
<artifactId>intc-common-swagger</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- intc-common-message 消息服务 -->
|
||||
<dependency>
|
||||
<groupId>com.intc</groupId>
|
||||
<artifactId>intc-common-message</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>ws.schild</groupId>
|
||||
<artifactId>jave-core</artifactId>
|
||||
|
||||
@@ -81,6 +81,11 @@ public class HealthPerson extends BaseEntity
|
||||
@Excel(name = "身份证")
|
||||
private String identityCard;
|
||||
|
||||
/** 手机号 */
|
||||
@ApiModelProperty(value="手机号")
|
||||
@Excel(name = "手机号")
|
||||
private String phone;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
@@ -100,6 +105,7 @@ public class HealthPerson extends BaseEntity
|
||||
.append("ranking", getRanking())
|
||||
.append("sex", getSex())
|
||||
.append("identityCard", getIdentityCard())
|
||||
.append("phone", getPhone())
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,9 @@ public class MedicationPlan extends BaseEntity {
|
||||
@ApiModelProperty("是否开启提醒:1-是 0-否")
|
||||
private Integer remindEnabled;
|
||||
|
||||
@ApiModelProperty("提醒方式:1-短信 2-电话 3-短信+电话")
|
||||
private Integer remindType;
|
||||
|
||||
@ApiModelProperty("提前提醒分钟数")
|
||||
private Integer remindAdvanceMin;
|
||||
|
||||
|
||||
@@ -72,6 +72,9 @@ public class MedicationPlanVo {
|
||||
@ApiModelProperty("是否开启提醒:1-是 0-否")
|
||||
private Integer remindEnabled;
|
||||
|
||||
@ApiModelProperty("提醒方式:1-短信 2-电话 3-短信+电话")
|
||||
private Integer remindType;
|
||||
|
||||
@ApiModelProperty("提前提醒分钟数")
|
||||
private Integer remindAdvanceMin;
|
||||
|
||||
|
||||
@@ -33,6 +33,9 @@ public class MedicationTaskVo {
|
||||
@ApiModelProperty("成员名称")
|
||||
private String personName;
|
||||
|
||||
@ApiModelProperty("成员手机号")
|
||||
private String personPhone;
|
||||
|
||||
@ApiModelProperty("药品名称")
|
||||
private String medicineName;
|
||||
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
package com.intc.health.task;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.intc.common.message.service.SmsService;
|
||||
import com.intc.common.message.service.VoiceService;
|
||||
import com.intc.health.domain.MedicationTask;
|
||||
import com.intc.health.domain.vo.HealthPersonVo;
|
||||
import com.intc.health.domain.vo.MedicationPlanVo;
|
||||
import com.intc.health.domain.vo.MedicationTaskVo;
|
||||
import com.intc.health.mapper.HealthPersonMapper;
|
||||
import com.intc.health.mapper.MedicationPlanMapper;
|
||||
import com.intc.health.mapper.MedicationTaskMapper;
|
||||
import com.intc.health.service.IHealthPersonService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -26,6 +35,20 @@ public class MedicationRemindJob {
|
||||
@Resource
|
||||
private MedicationTaskMapper taskMapper;
|
||||
|
||||
@Resource
|
||||
private MedicationPlanMapper planMapper;
|
||||
|
||||
@Resource
|
||||
private HealthPersonMapper personMapper;
|
||||
|
||||
@Resource
|
||||
private SmsService smsService;
|
||||
|
||||
@Resource
|
||||
private VoiceService voiceService;
|
||||
|
||||
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm");
|
||||
|
||||
/**
|
||||
* 扫描并发送提醒
|
||||
* 定时任务配置:每分钟执行
|
||||
@@ -63,11 +86,59 @@ public class MedicationRemindJob {
|
||||
task.getPersonName(), task.getMedicineName(),
|
||||
task.getPlannedDate(), task.getPlannedTime());
|
||||
|
||||
// TODO: 实现消息推送
|
||||
// 1. 查询用户的提醒设置
|
||||
// 2. 根据设置选择推送渠道
|
||||
// 3. 发送推送消息
|
||||
// 4. 记录提醒日志
|
||||
// 获取用药计划配置
|
||||
MedicationPlanVo plan = planMapper.selectById(task.getPlanId());
|
||||
if (plan == null) {
|
||||
log.warn("用药计划不存在: planId={}", task.getPlanId());
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否开启提醒
|
||||
if (plan.getRemindEnabled() == null || plan.getRemindEnabled() == 0) {
|
||||
log.debug("用药计划未开启提醒: planId={}", task.getPlanId());
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取成员手机号
|
||||
String phone = task.getPersonPhone();
|
||||
if (phone == null || phone.isEmpty()) {
|
||||
log.warn("成员手机号为空: personId={}", task.getPersonId());
|
||||
return;
|
||||
}
|
||||
|
||||
// 格式化计划时间
|
||||
String plannedTimeStr = formatPlannedTime(task);
|
||||
|
||||
// 提醒方式:1-短信 2-电话 3-短信+电话
|
||||
Integer remindType = plan.getRemindType();
|
||||
if (remindType == null) {
|
||||
remindType = 1; // 默认短信
|
||||
}
|
||||
|
||||
boolean smsSuccess = false;
|
||||
boolean voiceSuccess = false;
|
||||
|
||||
// 发送短信提醒
|
||||
if (remindType == 1 || remindType == 3) {
|
||||
smsSuccess = smsService.sendMedicationRemind(phone, task.getPersonName(), task.getMedicineName(), plannedTimeStr);
|
||||
log.info("短信提醒发送结果: phone={}, success={}", phone, smsSuccess);
|
||||
}
|
||||
|
||||
// 发送电话提醒
|
||||
if (remindType == 2 || remindType == 3) {
|
||||
voiceSuccess = voiceService.callMedicationRemind(phone, task.getPersonName(), task.getMedicineName(), plannedTimeStr);
|
||||
log.info("电话提醒发送结果: phone={}, success={}", phone, voiceSuccess);
|
||||
}
|
||||
|
||||
// 更新任务提醒状态
|
||||
MedicationTask updateTask = new MedicationTask();
|
||||
updateTask.setId(task.getId());
|
||||
updateTask.setRemindStatus(1); // 已提醒
|
||||
updateTask.setRemindTime(LocalDateTime.now());
|
||||
taskMapper.update(updateTask);
|
||||
|
||||
log.info("提醒发送完成: taskId={}, person={}, sms={}, voice={}",
|
||||
task.getId(), task.getPersonName(), smsSuccess, voiceSuccess);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,8 +180,98 @@ public class MedicationRemindJob {
|
||||
task.getPersonName(), task.getMedicineName(),
|
||||
task.getPlannedDate(), task.getPlannedTime());
|
||||
|
||||
// TODO: 实现超时提醒逻辑
|
||||
// 1. 发送二次提醒
|
||||
// 2. 如果配置了家属通知,通知家属
|
||||
// 获取用药计划配置
|
||||
MedicationPlanVo plan = planMapper.selectById(task.getPlanId());
|
||||
if (plan == null) {
|
||||
log.warn("用药计划不存在: planId={}", task.getPlanId());
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取成员手机号
|
||||
String phone = task.getPersonPhone();
|
||||
if (phone == null || phone.isEmpty()) {
|
||||
log.warn("成员手机号为空: personId={}", task.getPersonId());
|
||||
return;
|
||||
}
|
||||
|
||||
// 格式化计划时间
|
||||
String plannedTimeStr = formatPlannedTime(task);
|
||||
|
||||
// 提醒方式:超时提醒默认短信+电话
|
||||
Integer remindType = plan.getRemindType();
|
||||
if (remindType == null) {
|
||||
remindType = 3; // 默认短信+电话
|
||||
} else if (remindType == 1) {
|
||||
remindType = 3; // 短信改为短信+电话加强提醒
|
||||
}
|
||||
|
||||
// 发送超时短信提醒
|
||||
boolean smsSuccess = smsService.sendMissedRemind(phone, task.getPersonName(), task.getMedicineName(), plannedTimeStr);
|
||||
log.info("超时短信提醒发送结果: phone={}, success={}", phone, smsSuccess);
|
||||
|
||||
// 发送超时电话提醒
|
||||
if (remindType == 2 || remindType == 3) {
|
||||
boolean voiceSuccess = voiceService.callMissedRemind(phone, task.getPersonName(), task.getMedicineName(), plannedTimeStr);
|
||||
log.info("超时电话提醒发送结果: phone={}, success={}", phone, voiceSuccess);
|
||||
}
|
||||
|
||||
// 更新任务提醒状态为已超时提醒
|
||||
MedicationTask updateTask = new MedicationTask();
|
||||
updateTask.setId(task.getId());
|
||||
updateTask.setRemindStatus(2); // 已超时提醒
|
||||
updateTask.setRemindTime(LocalDateTime.now());
|
||||
taskMapper.update(updateTask);
|
||||
|
||||
// 如果配置了家属通知,通知家属
|
||||
if (plan.getNotifyFamily() != null && plan.getNotifyFamily() == 1) {
|
||||
notifyFamily(task, plan);
|
||||
}
|
||||
|
||||
log.info("超时提醒发送完成: taskId={}, person={}", task.getId(), task.getPersonName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知家属
|
||||
*/
|
||||
private void notifyFamily(MedicationTaskVo task, MedicationPlanVo plan) {
|
||||
JsonNode familyMemberIds = plan.getFamilyMemberIds();
|
||||
if (familyMemberIds == null || !familyMemberIds.isArray()) {
|
||||
log.debug("未配置家属成员");
|
||||
return;
|
||||
}
|
||||
|
||||
String plannedTimeStr = formatPlannedTime(task);
|
||||
|
||||
for (JsonNode memberIdNode : familyMemberIds) {
|
||||
Long memberId = memberIdNode.asLong();
|
||||
HealthPersonVo familyMember = personMapper.selectHealthPersonById(memberId);
|
||||
if (familyMember == null) {
|
||||
log.warn("家属成员不存在: memberId={}", memberId);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取家属手机号(需要在HealthPersonVo中添加phone字段)
|
||||
String familyPhone = familyMember.getPhone();
|
||||
if (familyPhone == null || familyPhone.isEmpty()) {
|
||||
log.warn("家属手机号为空: memberId={}", memberId);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 发送家属通知短信
|
||||
boolean success = smsService.sendFamilyNotify(familyPhone, task.getPersonName(), task.getMedicineName(), plannedTimeStr);
|
||||
log.info("家属通知发送结果: familyPhone={}, familyMember={}, success={}",
|
||||
familyPhone, familyMember.getName(), success);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化计划时间
|
||||
*/
|
||||
private String formatPlannedTime(MedicationTaskVo task) {
|
||||
if (task.getPlannedTime() != null) {
|
||||
// plannedTime是Date类型,需要转换
|
||||
return new java.text.SimpleDateFormat("HH:mm").format(task.getPlannedTime());
|
||||
}
|
||||
return "未知时间";
|
||||
}
|
||||
}
|
||||
@@ -21,10 +21,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="ranking" column="ranking" />
|
||||
<result property="sex" column="sex" />
|
||||
<result property="identityCard" column="identity_card" />
|
||||
<result property="phone" column="phone" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectHealthPersonVo">
|
||||
select a.id, a.name,a.sex,a.identity_card, a.type, a.create_by, a.create_time, a.update_by, a.update_time, a.del_flag, a.remark, a.birthday, a.nick_name, a.height, a.weight, a.ranking from health_person a
|
||||
select a.id, a.name,a.sex,a.identity_card, a.phone, a.type, a.create_by, a.create_time, a.update_by, a.update_time, a.del_flag, a.remark, a.birthday, a.nick_name, a.height, a.weight, a.ranking from health_person a
|
||||
</sql>
|
||||
|
||||
<select id="selectHealthPersonList" parameterType="HealthPersonDto" resultMap="HealthPersonResult">
|
||||
@@ -64,6 +65,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="ranking != null">ranking,</if>
|
||||
<if test="sex != null and sex != ''">sex,</if>
|
||||
<if test="identityCard != null and identityCard != ''">identity_card,</if>
|
||||
<if test="phone != null and phone != ''">phone,</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="id != null">#{id},</if>
|
||||
@@ -82,6 +84,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="ranking != null">#{ranking},</if>
|
||||
<if test="sex != null and sex != ''">#{sex},</if>
|
||||
<if test="identityCard != null and identityCard != ''">#{identityCard},</if>
|
||||
<if test="phone != null and phone != ''">#{phone},</if>
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
@@ -103,6 +106,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="ranking != null">ranking = #{ranking},</if>
|
||||
<if test="sex != null and sex != ''">sex = #{sex},</if>
|
||||
<if test="identityCard != null and identityCard != ''">identity_card = #{identityCard},</if>
|
||||
<if test="phone != null and phone != ''">phone = #{phone},</if>
|
||||
</trim>
|
||||
where id = #{id}
|
||||
</update>
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
<result property="startDate" column="start_date"/>
|
||||
<result property="endDate" column="end_date"/>
|
||||
<result property="remindEnabled" column="remind_enabled"/>
|
||||
<result property="remindType" column="remind_type"/>
|
||||
<result property="remindAdvanceMin" column="remind_advance_min"/>
|
||||
<result property="notifyFamily" column="notify_family"/>
|
||||
<result property="familyMemberIds" column="family_member_ids" typeHandler="com.intc.common.core.handler.JsonNodeTypeHandler"/>
|
||||
@@ -35,7 +36,7 @@
|
||||
c.disease_name as chronic_disease_name, a.plan_name, a.medicine_id,
|
||||
a.medicine_name, a.specification, a.dosage_per_time, a.dosage_unit,
|
||||
a.frequency_type, a.frequency_config, a.time_slots, a.start_date, a.end_date,
|
||||
a.remind_enabled, a.remind_advance_min, a.notify_family, a.family_member_ids,
|
||||
a.remind_enabled, a.remind_type, a.remind_advance_min, a.notify_family, a.family_member_ids,
|
||||
a.status, a.create_by, a.create_time,
|
||||
(select count(1) from medication_task t where t.plan_id = a.id and t.del_flag = 0 and t.planned_date = current_date) as today_total,
|
||||
(select count(1) from medication_task t where t.plan_id = a.id and t.del_flag = 0 and t.planned_date = current_date and t.status = 1) as today_completed
|
||||
@@ -94,6 +95,7 @@
|
||||
<if test="startDate != null">start_date,</if>
|
||||
<if test="endDate != null">end_date,</if>
|
||||
<if test="remindEnabled != null">remind_enabled,</if>
|
||||
<if test="remindType != null">remind_type,</if>
|
||||
<if test="remindAdvanceMin != null">remind_advance_min,</if>
|
||||
<if test="notifyFamily != null">notify_family,</if>
|
||||
<if test="familyMemberIds != null">family_member_ids,</if>
|
||||
@@ -118,6 +120,7 @@
|
||||
<if test="startDate != null">#{startDate},</if>
|
||||
<if test="endDate != null">#{endDate},</if>
|
||||
<if test="remindEnabled != null">#{remindEnabled},</if>
|
||||
<if test="remindType != null">#{remindType},</if>
|
||||
<if test="remindAdvanceMin != null">#{remindAdvanceMin},</if>
|
||||
<if test="notifyFamily != null">#{notifyFamily},</if>
|
||||
<if test="familyMemberIds != null">#{familyMemberIds, typeHandler=com.intc.common.core.handler.JsonNodeTypeHandler},</if>
|
||||
@@ -146,6 +149,7 @@
|
||||
<if test="startDate != null">start_date = #{startDate},</if>
|
||||
<if test="endDate != null">end_date = #{endDate},</if>
|
||||
<if test="remindEnabled != null">remind_enabled = #{remindEnabled},</if>
|
||||
<if test="remindType != null">remind_type = #{remindType},</if>
|
||||
<if test="remindAdvanceMin != null">remind_advance_min = #{remindAdvanceMin},</if>
|
||||
<if test="notifyFamily != null">notify_family = #{notifyFamily},</if>
|
||||
<if test="familyMemberIds != null">family_member_ids = #{familyMemberIds, typeHandler=com.intc.common.core.handler.JsonNodeTypeHandler},</if>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<result property="planName" column="plan_name"/>
|
||||
<result property="personId" column="person_id"/>
|
||||
<result property="personName" column="person_name"/>
|
||||
<result property="personPhone" column="person_phone"/>
|
||||
<result property="medicineName" column="medicine_name"/>
|
||||
<result property="plannedDate" column="planned_date"/>
|
||||
<result property="plannedTime" column="planned_time"/>
|
||||
@@ -23,7 +24,7 @@
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectVo">
|
||||
select a.id, a.plan_id, p.plan_name, a.person_id, m.name as person_name,
|
||||
select a.id, a.plan_id, p.plan_name, a.person_id, m.name as person_name, m.phone as person_phone,
|
||||
a.medicine_name, a.planned_date, a.planned_time, a.planned_dosage,
|
||||
a.actual_time, a.actual_dosage, a.status, a.confirm_type, a.notes,
|
||||
a.remind_status, a.remind_time, a.create_time
|
||||
|
||||
Reference in New Issue
Block a user