diff --git a/intc-common/intc-common-message/pom.xml b/intc-common/intc-common-message/pom.xml
new file mode 100644
index 0000000..834a6a4
--- /dev/null
+++ b/intc-common/intc-common-message/pom.xml
@@ -0,0 +1,65 @@
+
+
+
+ com.intc
+ intc-common
+ 3.6.3
+
+ 4.0.0
+
+ intc-common-message
+
+
+ intc-common-message 消息通知服务(短信、语音等)
+
+
+
+
+
+ com.aliyun
+ dysmsapi20170525
+ 2.0.24
+
+
+
+
+ com.aliyun
+ dyvmsapi20170525
+ 2.0.2
+
+
+
+
+ com.aliyun
+ tea-openapi
+ 0.3.4
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+ com.alibaba.fastjson2
+ fastjson2
+
+
+
+
+ com.intc
+ intc-common-core
+
+
+
+
\ No newline at end of file
diff --git a/intc-common/intc-common-message/src/main/java/com/intc/common/message/config/AliyunMessageAutoConfiguration.java b/intc-common/intc-common-message/src/main/java/com/intc/common/message/config/AliyunMessageAutoConfiguration.java
new file mode 100644
index 0000000..fdf286b
--- /dev/null
+++ b/intc-common/intc-common-message/src/main/java/com/intc/common/message/config/AliyunMessageAutoConfiguration.java
@@ -0,0 +1,17 @@
+package com.intc.common.message.config;
+
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 阿里云消息服务自动配置
+ *
+ * @author bot5
+ * @date 2026-03-29
+ */
+@Configuration
+@EnableConfigurationProperties(AliyunMessageProperties.class)
+@ComponentScan(basePackages = "com.intc.common.message")
+public class AliyunMessageAutoConfiguration {
+}
\ No newline at end of file
diff --git a/intc-common/intc-common-message/src/main/java/com/intc/common/message/config/AliyunMessageProperties.java b/intc-common/intc-common-message/src/main/java/com/intc/common/message/config/AliyunMessageProperties.java
new file mode 100644
index 0000000..45e2c2a
--- /dev/null
+++ b/intc-common/intc-common-message/src/main/java/com/intc/common/message/config/AliyunMessageProperties.java
@@ -0,0 +1,78 @@
+package com.intc.common.message.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * 阿里云消息服务配置
+ *
+ * @author bot5
+ * @date 2026-03-29
+ */
+@Data
+@Component
+@ConfigurationProperties(prefix = "aliyun.message")
+public class AliyunMessageProperties {
+
+ /**
+ * 阿里云 AccessKey ID
+ */
+ private String accessKeyId;
+
+ /**
+ * 阿里云 AccessKey Secret
+ */
+ private String accessKeySecret;
+
+ /**
+ * 短信服务配置
+ */
+ private SmsConfig sms = new SmsConfig();
+
+ /**
+ * 语音服务配置
+ */
+ private VoiceConfig voice = new VoiceConfig();
+
+ @Data
+ public static class SmsConfig {
+ /**
+ * 短信签名名称
+ */
+ private String signName;
+
+ /**
+ * 短信模板CODE(用药提醒)
+ */
+ private String medicationRemindTemplateCode;
+
+ /**
+ * 短信模板CODE(漏服提醒)
+ */
+ private String missedRemindTemplateCode;
+
+ /**
+ * 短信模板CODE(家属通知)
+ */
+ private String familyNotifyTemplateCode;
+ }
+
+ @Data
+ public static class VoiceConfig {
+ /**
+ * 语音通知模板CODE(用药提醒)
+ */
+ private String medicationRemindTtsCode;
+
+ /**
+ * 语音通知模板CODE(漏服提醒)
+ */
+ private String missedRemindTtsCode;
+
+ /**
+ * 被叫号码显示的呼入号码
+ */
+ private String calledShowNumber;
+ }
+}
\ No newline at end of file
diff --git a/intc-common/intc-common-message/src/main/java/com/intc/common/message/service/SmsService.java b/intc-common/intc-common-message/src/main/java/com/intc/common/message/service/SmsService.java
new file mode 100644
index 0000000..434fd14
--- /dev/null
+++ b/intc-common/intc-common-message/src/main/java/com/intc/common/message/service/SmsService.java
@@ -0,0 +1,129 @@
+package com.intc.common.message.service;
+
+import com.alibaba.fastjson2.JSON;
+import com.aliyun.dysmsapi20170525.Client;
+import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
+import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
+import com.aliyun.teaopenapi.models.Config;
+import com.intc.common.message.config.AliyunMessageProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 阿里云短信服务
+ *
+ * @author bot5
+ * @date 2026-03-29
+ */
+@Service
+public class SmsService {
+
+ private static final Logger log = LoggerFactory.getLogger(SmsService.class);
+
+ @Resource
+ private AliyunMessageProperties properties;
+
+ private Client smsClient;
+
+ @PostConstruct
+ public void init() throws Exception {
+ Config config = new Config()
+ .setAccessKeyId(properties.getAccessKeyId())
+ .setAccessKeySecret(properties.getAccessKeySecret())
+ .setEndpoint("dysmsapi.aliyuncs.com");
+ this.smsClient = new Client(config);
+ }
+
+ /**
+ * 发送用药提醒短信
+ *
+ * @param phoneNumber 手机号
+ * @param personName 成员姓名
+ * @param medicineName 药品名称
+ * @param plannedTime 计划服药时间
+ * @return 是否成功
+ */
+ public boolean sendMedicationRemind(String phoneNumber, String personName, String medicineName, String plannedTime) {
+ Map templateParams = new HashMap<>();
+ templateParams.put("name", personName);
+ templateParams.put("medicine", medicineName);
+ templateParams.put("time", plannedTime);
+
+ return sendSms(phoneNumber, properties.getSms().getMedicationRemindTemplateCode(), templateParams);
+ }
+
+ /**
+ * 发送漏服提醒短信
+ *
+ * @param phoneNumber 手机号
+ * @param personName 成员姓名
+ * @param medicineName 药品名称
+ * @param plannedTime 计划服药时间
+ * @return 是否成功
+ */
+ public boolean sendMissedRemind(String phoneNumber, String personName, String medicineName, String plannedTime) {
+ Map templateParams = new HashMap<>();
+ templateParams.put("name", personName);
+ templateParams.put("medicine", medicineName);
+ templateParams.put("time", plannedTime);
+
+ return sendSms(phoneNumber, properties.getSms().getMissedRemindTemplateCode(), templateParams);
+ }
+
+ /**
+ * 发送家属通知短信
+ *
+ * @param phoneNumber 手机号
+ * @param personName 成员姓名
+ * @param medicineName 药品名称
+ * @param plannedTime 计划服药时间
+ * @return 是否成功
+ */
+ public boolean sendFamilyNotify(String phoneNumber, String personName, String medicineName, String plannedTime) {
+ Map templateParams = new HashMap<>();
+ templateParams.put("name", personName);
+ templateParams.put("medicine", medicineName);
+ templateParams.put("time", plannedTime);
+
+ return sendSms(phoneNumber, properties.getSms().getFamilyNotifyTemplateCode(), templateParams);
+ }
+
+ /**
+ * 发送短信
+ *
+ * @param phoneNumber 手机号
+ * @param templateCode 模板CODE
+ * @param templateParams 模板参数
+ * @return 是否成功
+ */
+ public boolean sendSms(String phoneNumber, String templateCode, Map templateParams) {
+ try {
+ SendSmsRequest request = new SendSmsRequest()
+ .setPhoneNumbers(phoneNumber)
+ .setSignName(properties.getSms().getSignName())
+ .setTemplateCode(templateCode)
+ .setTemplateParam(JSON.toJSONString(templateParams));
+
+ SendSmsResponse response = smsClient.sendSms(request);
+
+ if ("OK".equals(response.getBody().getCode())) {
+ log.info("短信发送成功: phone={}, templateCode={}, bizId={}",
+ phoneNumber, templateCode, response.getBody().getBizId());
+ return true;
+ } else {
+ log.error("短信发送失败: phone={}, code={}, message={}",
+ phoneNumber, response.getBody().getCode(), response.getBody().getMessage());
+ return false;
+ }
+ } catch (Exception e) {
+ log.error("短信发送异常: phone={}, templateCode={}", phoneNumber, templateCode, e);
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/intc-common/intc-common-message/src/main/java/com/intc/common/message/service/VoiceService.java b/intc-common/intc-common-message/src/main/java/com/intc/common/message/service/VoiceService.java
new file mode 100644
index 0000000..be30e55
--- /dev/null
+++ b/intc-common/intc-common-message/src/main/java/com/intc/common/message/service/VoiceService.java
@@ -0,0 +1,111 @@
+package com.intc.common.message.service;
+
+import com.alibaba.fastjson2.JSON;
+import com.aliyun.dyvmsapi20170525.Client;
+import com.aliyun.dyvmsapi20170525.models.SingleCallByTtsRequest;
+import com.aliyun.dyvmsapi20170525.models.SingleCallByTtsResponse;
+import com.aliyun.teaopenapi.models.Config;
+import com.intc.common.message.config.AliyunMessageProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 阿里云语音通知服务
+ *
+ * @author bot5
+ * @date 2026-03-29
+ */
+@Service
+public class VoiceService {
+
+ private static final Logger log = LoggerFactory.getLogger(VoiceService.class);
+
+ @Resource
+ private AliyunMessageProperties properties;
+
+ private Client voiceClient;
+
+ @PostConstruct
+ public void init() throws Exception {
+ Config config = new Config()
+ .setAccessKeyId(properties.getAccessKeyId())
+ .setAccessKeySecret(properties.getAccessKeySecret())
+ .setEndpoint("dyvmsapi.aliyuncs.com");
+ this.voiceClient = new Client(config);
+ }
+
+ /**
+ * 发送用药提醒语音电话
+ *
+ * @param phoneNumber 手机号
+ * @param personName 成员姓名
+ * @param medicineName 药品名称
+ * @param plannedTime 计划服药时间
+ * @return 是否成功
+ */
+ public boolean callMedicationRemind(String phoneNumber, String personName, String medicineName, String plannedTime) {
+ Map ttsParams = new HashMap<>();
+ ttsParams.put("name", personName);
+ ttsParams.put("medicine", medicineName);
+ ttsParams.put("time", plannedTime);
+
+ return callByTts(phoneNumber, properties.getVoice().getMedicationRemindTtsCode(), ttsParams);
+ }
+
+ /**
+ * 发送漏服提醒语音电话
+ *
+ * @param phoneNumber 手机号
+ * @param personName 成员姓名
+ * @param medicineName 药品名称
+ * @param plannedTime 计划服药时间
+ * @return 是否成功
+ */
+ public boolean callMissedRemind(String phoneNumber, String personName, String medicineName, String plannedTime) {
+ Map ttsParams = new HashMap<>();
+ ttsParams.put("name", personName);
+ ttsParams.put("medicine", medicineName);
+ ttsParams.put("time", plannedTime);
+
+ return callByTts(phoneNumber, properties.getVoice().getMissedRemindTtsCode(), ttsParams);
+ }
+
+ /**
+ * 发送语音通知电话
+ *
+ * @param phoneNumber 手机号
+ * @param ttsCode 语音模板CODE
+ * @param ttsParams 模板参数
+ * @return 是否成功
+ */
+ public boolean callByTts(String phoneNumber, String ttsCode, Map ttsParams) {
+ try {
+ SingleCallByTtsRequest request = new SingleCallByTtsRequest()
+ .setCalledNumber(phoneNumber)
+ .setCalledShowNumber(properties.getVoice().getCalledShowNumber())
+ .setTtsCode(ttsCode)
+ .setTtsParam(JSON.toJSONString(ttsParams));
+
+ SingleCallByTtsResponse response = voiceClient.singleCallByTts(request);
+
+ if ("OK".equals(response.getBody().getCode())) {
+ log.info("语音电话发送成功: phone={}, ttsCode={}, callId={}",
+ phoneNumber, ttsCode, response.getBody().getCallId());
+ return true;
+ } else {
+ log.error("语音电话发送失败: phone={}, code={}, message={}",
+ phoneNumber, response.getBody().getCode(), response.getBody().getMessage());
+ return false;
+ }
+ } catch (Exception e) {
+ log.error("语音电话发送异常: phone={}, ttsCode={}", phoneNumber, ttsCode, e);
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/intc-common/intc-common-message/src/main/resources/META-INF/spring.factories b/intc-common/intc-common-message/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..df8c80c
--- /dev/null
+++ b/intc-common/intc-common-message/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=
+com.intc.common.message.config.AliyunMessageAutoConfiguration
\ No newline at end of file
diff --git a/intc-common/pom.xml b/intc-common/pom.xml
index 6a17cb9..34ef084 100644
--- a/intc-common/pom.xml
+++ b/intc-common/pom.xml
@@ -17,6 +17,7 @@
intc-common-security
intc-common-datascope
intc-common-datasource
+ intc-common-message
intc-common
diff --git a/intc-modules/intc-health/pom.xml b/intc-modules/intc-health/pom.xml
index 47f349a..1e014e7 100644
--- a/intc-modules/intc-health/pom.xml
+++ b/intc-modules/intc-health/pom.xml
@@ -100,6 +100,13 @@
com.intc
intc-common-swagger
+
+
+
+ com.intc
+ intc-common-message
+
+
ws.schild
jave-core
diff --git a/intc-modules/intc-health/src/main/java/com/intc/health/domain/HealthPerson.java b/intc-modules/intc-health/src/main/java/com/intc/health/domain/HealthPerson.java
index 95e8f13..a5049c2 100644
--- a/intc-modules/intc-health/src/main/java/com/intc/health/domain/HealthPerson.java
+++ b/intc-modules/intc-health/src/main/java/com/intc/health/domain/HealthPerson.java
@@ -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();
}
}
diff --git a/intc-modules/intc-health/src/main/java/com/intc/health/domain/MedicationPlan.java b/intc-modules/intc-health/src/main/java/com/intc/health/domain/MedicationPlan.java
index 76460b3..7ac5583 100644
--- a/intc-modules/intc-health/src/main/java/com/intc/health/domain/MedicationPlan.java
+++ b/intc-modules/intc-health/src/main/java/com/intc/health/domain/MedicationPlan.java
@@ -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;
diff --git a/intc-modules/intc-health/src/main/java/com/intc/health/domain/vo/MedicationPlanVo.java b/intc-modules/intc-health/src/main/java/com/intc/health/domain/vo/MedicationPlanVo.java
index 64bc0c2..3636190 100644
--- a/intc-modules/intc-health/src/main/java/com/intc/health/domain/vo/MedicationPlanVo.java
+++ b/intc-modules/intc-health/src/main/java/com/intc/health/domain/vo/MedicationPlanVo.java
@@ -72,6 +72,9 @@ public class MedicationPlanVo {
@ApiModelProperty("是否开启提醒:1-是 0-否")
private Integer remindEnabled;
+ @ApiModelProperty("提醒方式:1-短信 2-电话 3-短信+电话")
+ private Integer remindType;
+
@ApiModelProperty("提前提醒分钟数")
private Integer remindAdvanceMin;
diff --git a/intc-modules/intc-health/src/main/java/com/intc/health/domain/vo/MedicationTaskVo.java b/intc-modules/intc-health/src/main/java/com/intc/health/domain/vo/MedicationTaskVo.java
index 9cb2312..81189c9 100644
--- a/intc-modules/intc-health/src/main/java/com/intc/health/domain/vo/MedicationTaskVo.java
+++ b/intc-modules/intc-health/src/main/java/com/intc/health/domain/vo/MedicationTaskVo.java
@@ -33,6 +33,9 @@ public class MedicationTaskVo {
@ApiModelProperty("成员名称")
private String personName;
+ @ApiModelProperty("成员手机号")
+ private String personPhone;
+
@ApiModelProperty("药品名称")
private String medicineName;
diff --git a/intc-modules/intc-health/src/main/java/com/intc/health/task/MedicationRemindJob.java b/intc-modules/intc-health/src/main/java/com/intc/health/task/MedicationRemindJob.java
index cd8f689..77111db 100644
--- a/intc-modules/intc-health/src/main/java/com/intc/health/task/MedicationRemindJob.java
+++ b/intc-modules/intc-health/src/main/java/com/intc/health/task/MedicationRemindJob.java
@@ -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 "未知时间";
}
}
\ No newline at end of file
diff --git a/intc-modules/intc-health/src/main/resources/mapper/health/HealthPersonMapper.xml b/intc-modules/intc-health/src/main/resources/mapper/health/HealthPersonMapper.xml
index d536383..1c737f4 100644
--- a/intc-modules/intc-health/src/main/resources/mapper/health/HealthPersonMapper.xml
+++ b/intc-modules/intc-health/src/main/resources/mapper/health/HealthPersonMapper.xml
@@ -21,10 +21,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+
- 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
+
+
+
+ com.intc
+ intc-common-message
+ ${intc.version}
+
diff --git a/sql/aliyun-message-config-example.yml b/sql/aliyun-message-config-example.yml
new file mode 100644
index 0000000..07c48aa
--- /dev/null
+++ b/sql/aliyun-message-config-example.yml
@@ -0,0 +1,32 @@
+# 阿里云消息服务配置示例(在Nacos配置中心添加)
+# 配置项:intc-health-dev.yml 或独立配置文件
+
+aliyun:
+ message:
+ # 阿里云 AccessKey(请替换为实际值)
+ accessKeyId: ${ALIYUN_ACCESS_KEY_ID:your-access-key-id}
+ accessKeySecret: ${ALIYUN_ACCESS_KEY_SECRET:your-access-key-secret}
+
+ # 短信配置
+ sms:
+ signName: 智聪健康
+ # 用药提醒短信模板CODE(需要在阿里云短信服务中申请)
+ medicationRemindTemplateCode: SMS_123456789
+ # 漏服提醒短信模板CODE
+ missedRemindTemplateCode: SMS_123456790
+ # 家属通知短信模板CODE
+ familyNotifyTemplateCode: SMS_123456791
+
+ # 语音配置
+ voice:
+ # 用药提醒语音模板CODE(需要在阿里云语音服务中申请)
+ medicationRemindTtsCode: TTS_123456789
+ # 漏服提醒语音模板CODE
+ missedRemindTtsCode: TTS_123456790
+ # 被叫号码显示的呼入号码(需要在阿里云购买号码)
+ calledShowNumber: 4001234567
+
+# 短信模板示例(在阿里云申请时使用):
+# 用药提醒模板:您好,${name}请在${time}服用${medicine},请按时服药保持健康。
+# 漏服提醒模板:您好,${name}未在${time}服用${medicine},请尽快补服或咨询医生。
+# 家属通知模板:您好,${name}超时未服用${medicine}(计划时间${time}),请关注提醒。
\ No newline at end of file
diff --git a/sql/update_medication_remind.sql b/sql/update_medication_remind.sql
new file mode 100644
index 0000000..f8daba6
--- /dev/null
+++ b/sql/update_medication_remind.sql
@@ -0,0 +1,16 @@
+-- 用药提醒功能更新SQL
+-- 1. 给health_person添加phone字段
+-- 2. 给medication_plan添加remind_type字段
+
+-- 1. health_person表添加phone字段
+ALTER TABLE health_person ADD COLUMN IF NOT EXISTS phone VARCHAR(20);
+
+COMMENT ON COLUMN health_person.phone IS '手机号';
+
+-- 2. medication_plan表添加remind_type字段
+ALTER TABLE medication_plan ADD COLUMN IF NOT EXISTS remind_type INTEGER DEFAULT 1;
+
+COMMENT ON COLUMN medication_plan.remind_type IS '提醒方式:1-短信 2-电话 3-短信+电话';
+
+-- 更新已有数据,默认提醒方式为短信
+UPDATE medication_plan SET remind_type = 1 WHERE remind_type IS NULL AND remind_enabled = 1;
\ No newline at end of file