feat(medication): 实现用药提醒短信和电话提醒功能

- 新增 intc-common-message 消息服务模块
- 阿里云短信服务 SmsService(用药提醒、漏服提醒、家属通知)
- 阿里云语音服务 VoiceService(用药提醒、漏服提醒电话)
- HealthPerson 添加 phone 手机号字段
- MedicationPlan 添加 remind_type 提醒方式字段(1-短信 2-电话 3-短信+电话)
- MedicationRemindJob 完善提醒逻辑:
  - 定时扫描待服药任务发送提醒
  - 超时30分钟未服药发送加强提醒
  - 配置家属通知功能
- 新增数据库更新SQL和Nacos配置示例文件
This commit is contained in:
bot5
2026-03-29 18:26:53 +08:00
parent f0a61a5314
commit 846cd3141e
19 changed files with 662 additions and 12 deletions

View File

@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.intc</groupId>
<artifactId>intc-common</artifactId>
<version>3.6.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>intc-common-message</artifactId>
<description>
intc-common-message 消息通知服务(短信、语音等)
</description>
<dependencies>
<!-- 阿里云短信 SDK -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
<version>2.0.24</version>
</dependency>
<!-- 阿里云语音 SDK -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dyvmsapi20170525</artifactId>
<version>2.0.2</version>
</dependency>
<!-- 阿里云核心 SDK -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>tea-openapi</artifactId>
<version>0.3.4</version>
</dependency>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- Fastjson -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
</dependency>
<!-- intc-common-core -->
<dependency>
<groupId>com.intc</groupId>
<artifactId>intc-common-core</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -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 {
}

View File

@@ -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;
}
}

View File

@@ -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<String, String> 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<String, String> 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<String, String> 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<String, String> 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;
}
}
}

View File

@@ -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<String, String> 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<String, String> 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<String, String> 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;
}
}
}

View File

@@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
com.intc.common.message.config.AliyunMessageAutoConfiguration

View File

@@ -17,6 +17,7 @@
<module>intc-common-security</module> <module>intc-common-security</module>
<module>intc-common-datascope</module> <module>intc-common-datascope</module>
<module>intc-common-datasource</module> <module>intc-common-datasource</module>
<module>intc-common-message</module>
</modules> </modules>
<artifactId>intc-common</artifactId> <artifactId>intc-common</artifactId>

View File

@@ -100,6 +100,13 @@
<groupId>com.intc</groupId> <groupId>com.intc</groupId>
<artifactId>intc-common-swagger</artifactId> <artifactId>intc-common-swagger</artifactId>
</dependency> </dependency>
<!-- intc-common-message 消息服务 -->
<dependency>
<groupId>com.intc</groupId>
<artifactId>intc-common-message</artifactId>
</dependency>
<dependency> <dependency>
<groupId>ws.schild</groupId> <groupId>ws.schild</groupId>
<artifactId>jave-core</artifactId> <artifactId>jave-core</artifactId>

View File

@@ -81,6 +81,11 @@ public class HealthPerson extends BaseEntity
@Excel(name = "身份证") @Excel(name = "身份证")
private String identityCard; private String identityCard;
/** 手机号 */
@ApiModelProperty(value="手机号")
@Excel(name = "手机号")
private String phone;
@Override @Override
public String toString() { public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
@@ -100,6 +105,7 @@ public class HealthPerson extends BaseEntity
.append("ranking", getRanking()) .append("ranking", getRanking())
.append("sex", getSex()) .append("sex", getSex())
.append("identityCard", getIdentityCard()) .append("identityCard", getIdentityCard())
.append("phone", getPhone())
.toString(); .toString();
} }
} }

View File

@@ -66,6 +66,9 @@ public class MedicationPlan extends BaseEntity {
@ApiModelProperty("是否开启提醒1-是 0-否") @ApiModelProperty("是否开启提醒1-是 0-否")
private Integer remindEnabled; private Integer remindEnabled;
@ApiModelProperty("提醒方式1-短信 2-电话 3-短信+电话")
private Integer remindType;
@ApiModelProperty("提前提醒分钟数") @ApiModelProperty("提前提醒分钟数")
private Integer remindAdvanceMin; private Integer remindAdvanceMin;

View File

@@ -72,6 +72,9 @@ public class MedicationPlanVo {
@ApiModelProperty("是否开启提醒1-是 0-否") @ApiModelProperty("是否开启提醒1-是 0-否")
private Integer remindEnabled; private Integer remindEnabled;
@ApiModelProperty("提醒方式1-短信 2-电话 3-短信+电话")
private Integer remindType;
@ApiModelProperty("提前提醒分钟数") @ApiModelProperty("提前提醒分钟数")
private Integer remindAdvanceMin; private Integer remindAdvanceMin;

View File

@@ -33,6 +33,9 @@ public class MedicationTaskVo {
@ApiModelProperty("成员名称") @ApiModelProperty("成员名称")
private String personName; private String personName;
@ApiModelProperty("成员手机号")
private String personPhone;
@ApiModelProperty("药品名称") @ApiModelProperty("药品名称")
private String medicineName; private String medicineName;

View File

@@ -1,15 +1,24 @@
package com.intc.health.task; 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.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.mapper.MedicationTaskMapper;
import com.intc.health.service.IHealthPersonService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.LocalTime; import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.List; import java.util.List;
/** /**
@@ -26,6 +35,20 @@ public class MedicationRemindJob {
@Resource @Resource
private MedicationTaskMapper taskMapper; 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.getPersonName(), task.getMedicineName(),
task.getPlannedDate(), task.getPlannedTime()); task.getPlannedDate(), task.getPlannedTime());
// TODO: 实现消息推送 // 获取用药计划配置
// 1. 查询用户的提醒设置 MedicationPlanVo plan = planMapper.selectById(task.getPlanId());
// 2. 根据设置选择推送渠道 if (plan == null) {
// 3. 发送推送消息 log.warn("用药计划不存在: planId={}", task.getPlanId());
// 4. 记录提醒日志 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.getPersonName(), task.getMedicineName(),
task.getPlannedDate(), task.getPlannedTime()); task.getPlannedDate(), task.getPlannedTime());
// TODO: 实现超时提醒逻辑 // 获取用药计划配置
// 1. 发送二次提醒 MedicationPlanVo plan = planMapper.selectById(task.getPlanId());
// 2. 如果配置了家属通知,通知家属 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 "未知时间";
} }
} }

View File

@@ -21,10 +21,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="ranking" column="ranking" /> <result property="ranking" column="ranking" />
<result property="sex" column="sex" /> <result property="sex" column="sex" />
<result property="identityCard" column="identity_card" /> <result property="identityCard" column="identity_card" />
<result property="phone" column="phone" />
</resultMap> </resultMap>
<sql id="selectHealthPersonVo"> <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> </sql>
<select id="selectHealthPersonList" parameterType="HealthPersonDto" resultMap="HealthPersonResult"> <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="ranking != null">ranking,</if>
<if test="sex != null and sex != ''">sex,</if> <if test="sex != null and sex != ''">sex,</if>
<if test="identityCard != null and identityCard != ''">identity_card,</if> <if test="identityCard != null and identityCard != ''">identity_card,</if>
<if test="phone != null and phone != ''">phone,</if>
</trim> </trim>
<trim prefix="values (" suffix=")" suffixOverrides=","> <trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">#{id},</if> <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="ranking != null">#{ranking},</if>
<if test="sex != null and sex != ''">#{sex},</if> <if test="sex != null and sex != ''">#{sex},</if>
<if test="identityCard != null and identityCard != ''">#{identityCard},</if> <if test="identityCard != null and identityCard != ''">#{identityCard},</if>
<if test="phone != null and phone != ''">#{phone},</if>
</trim> </trim>
</insert> </insert>
@@ -103,6 +106,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="ranking != null">ranking = #{ranking},</if> <if test="ranking != null">ranking = #{ranking},</if>
<if test="sex != null and sex != ''">sex = #{sex},</if> <if test="sex != null and sex != ''">sex = #{sex},</if>
<if test="identityCard != null and identityCard != ''">identity_card = #{identityCard},</if> <if test="identityCard != null and identityCard != ''">identity_card = #{identityCard},</if>
<if test="phone != null and phone != ''">phone = #{phone},</if>
</trim> </trim>
where id = #{id} where id = #{id}
</update> </update>

View File

@@ -20,6 +20,7 @@
<result property="startDate" column="start_date"/> <result property="startDate" column="start_date"/>
<result property="endDate" column="end_date"/> <result property="endDate" column="end_date"/>
<result property="remindEnabled" column="remind_enabled"/> <result property="remindEnabled" column="remind_enabled"/>
<result property="remindType" column="remind_type"/>
<result property="remindAdvanceMin" column="remind_advance_min"/> <result property="remindAdvanceMin" column="remind_advance_min"/>
<result property="notifyFamily" column="notify_family"/> <result property="notifyFamily" column="notify_family"/>
<result property="familyMemberIds" column="family_member_ids" typeHandler="com.intc.common.core.handler.JsonNodeTypeHandler"/> <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, 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.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.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, 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) 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 (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="startDate != null">start_date,</if>
<if test="endDate != null">end_date,</if> <if test="endDate != null">end_date,</if>
<if test="remindEnabled != null">remind_enabled,</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="remindAdvanceMin != null">remind_advance_min,</if>
<if test="notifyFamily != null">notify_family,</if> <if test="notifyFamily != null">notify_family,</if>
<if test="familyMemberIds != null">family_member_ids,</if> <if test="familyMemberIds != null">family_member_ids,</if>
@@ -118,6 +120,7 @@
<if test="startDate != null">#{startDate},</if> <if test="startDate != null">#{startDate},</if>
<if test="endDate != null">#{endDate},</if> <if test="endDate != null">#{endDate},</if>
<if test="remindEnabled != null">#{remindEnabled},</if> <if test="remindEnabled != null">#{remindEnabled},</if>
<if test="remindType != null">#{remindType},</if>
<if test="remindAdvanceMin != null">#{remindAdvanceMin},</if> <if test="remindAdvanceMin != null">#{remindAdvanceMin},</if>
<if test="notifyFamily != null">#{notifyFamily},</if> <if test="notifyFamily != null">#{notifyFamily},</if>
<if test="familyMemberIds != null">#{familyMemberIds, typeHandler=com.intc.common.core.handler.JsonNodeTypeHandler},</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="startDate != null">start_date = #{startDate},</if>
<if test="endDate != null">end_date = #{endDate},</if> <if test="endDate != null">end_date = #{endDate},</if>
<if test="remindEnabled != null">remind_enabled = #{remindEnabled},</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="remindAdvanceMin != null">remind_advance_min = #{remindAdvanceMin},</if>
<if test="notifyFamily != null">notify_family = #{notifyFamily},</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> <if test="familyMemberIds != null">family_member_ids = #{familyMemberIds, typeHandler=com.intc.common.core.handler.JsonNodeTypeHandler},</if>

View File

@@ -8,6 +8,7 @@
<result property="planName" column="plan_name"/> <result property="planName" column="plan_name"/>
<result property="personId" column="person_id"/> <result property="personId" column="person_id"/>
<result property="personName" column="person_name"/> <result property="personName" column="person_name"/>
<result property="personPhone" column="person_phone"/>
<result property="medicineName" column="medicine_name"/> <result property="medicineName" column="medicine_name"/>
<result property="plannedDate" column="planned_date"/> <result property="plannedDate" column="planned_date"/>
<result property="plannedTime" column="planned_time"/> <result property="plannedTime" column="planned_time"/>
@@ -23,7 +24,7 @@
</resultMap> </resultMap>
<sql id="selectVo"> <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.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.actual_time, a.actual_dosage, a.status, a.confirm_type, a.notes,
a.remind_status, a.remind_time, a.create_time a.remind_status, a.remind_time, a.create_time

View File

@@ -216,6 +216,13 @@
<artifactId>intc-api-system</artifactId> <artifactId>intc-api-system</artifactId>
<version>${intc.version}</version> <version>${intc.version}</version>
</dependency> </dependency>
<!-- 消息服务 -->
<dependency>
<groupId>com.intc</groupId>
<artifactId>intc-common-message</artifactId>
<version>${intc.version}</version>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>

View File

@@ -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}),请关注提醒。

View File

@@ -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;