feat(medication): 实现用药提醒短信和电话提醒功能
- 新增 intc-common-message 消息服务模块 - 阿里云短信服务 SmsService(用药提醒、漏服提醒、家属通知) - 阿里云语音服务 VoiceService(用药提醒、漏服提醒电话) - HealthPerson 添加 phone 手机号字段 - MedicationPlan 添加 remind_type 提醒方式字段(1-短信 2-电话 3-短信+电话) - MedicationRemindJob 完善提醒逻辑: - 定时扫描待服药任务发送提醒 - 超时30分钟未服药发送加强提醒 - 配置家属通知功能 - 新增数据库更新SQL和Nacos配置示例文件
This commit is contained in:
65
intc-common/intc-common-message/pom.xml
Normal file
65
intc-common/intc-common-message/pom.xml
Normal 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>
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
|
||||
com.intc.common.message.config.AliyunMessageAutoConfiguration
|
||||
@@ -17,6 +17,7 @@
|
||||
<module>intc-common-security</module>
|
||||
<module>intc-common-datascope</module>
|
||||
<module>intc-common-datasource</module>
|
||||
<module>intc-common-message</module>
|
||||
</modules>
|
||||
|
||||
<artifactId>intc-common</artifactId>
|
||||
|
||||
Reference in New Issue
Block a user