Commit 56d736c3 authored by honghy's avatar honghy

短信功能实作

parent dea7eb34
This diff is collapsed.
...@@ -162,7 +162,7 @@ public class PhoneUtil { ...@@ -162,7 +162,7 @@ public class PhoneUtil {
try { try {
List<DictDataRespDTO> dtos = DictFrameworkUtils.listDictDatasFromCache("phone_number_rule"); List<DictDataRespDTO> dtos = DictFrameworkUtils.listDictDatasFromCache("phone_number_rule");
if (CollUtil.isNotEmpty(dtos)) { if (CollUtil.isNotEmpty(dtos)) {
DictDataRespDTO dto = dtos.stream().filter(d -> d.getValue().trim().equals(code.trim())).findFirst().orElseGet(null); DictDataRespDTO dto = dtos.stream().filter(d -> d.getValue().trim().equals(code.trim())).findFirst().orElse(null);
if (Objects.nonNull(dto)) { if (Objects.nonNull(dto)) {
// 优先获取中文 // 优先获取中文
rule = dto.getLabel(); rule = dto.getLabel();
......
...@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.http.core.Request; ...@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.http.core.Request;
import cn.iocoder.yudao.framework.http.core.Response; import cn.iocoder.yudao.framework.http.core.Response;
import cn.iocoder.yudao.framework.http.core.client.HttpClient; import cn.iocoder.yudao.framework.http.core.client.HttpClient;
import cn.iocoder.yudao.framework.http.core.client.HttpClientFactory; import cn.iocoder.yudao.framework.http.core.client.HttpClientFactory;
import lombok.SneakyThrows;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.HashMap; import java.util.HashMap;
...@@ -16,52 +17,70 @@ import java.util.Map; ...@@ -16,52 +17,70 @@ import java.util.Map;
**/ **/
public class SendchampHttp { public class SendchampHttp {
private static final String SMS_URL="https://api.sendchamp.com/api/v1/sms/send"; private static final String SMS_URL = "https://api.sendchamp.com/api/v1/sms/send";
public void postReq(Map<String,Object> param,Map<String, String> header){ private static final String RECEIVE_URL = "https://api.sendchamp.com/api/v1/sms/status/";
public Response postReq(Map<String, Object> param, Map<String, String> header) {
HttpClient client = HttpClientFactory.get(Request.Util.OkHttp); HttpClient client = HttpClientFactory.get(Request.Util.OkHttp);
Request req = Request.create(SMS_URL,Request.Method.POST,param); Request req = Request.create(SMS_URL, Request.Method.POST, param);
req.setParamFormat(Request.ParamFormat.JSON); req.setParamFormat(Request.ParamFormat.JSON);
try { try {
Response res = client.execute(req,Request.Option.create(0,0,header)); Response res = client.execute(req, Request.Option.create(0, 0, header));
System.out.println("response code ---------------------------->"+res.getCode()); System.out.println("response code ---------------------------->" + res.getCode());
System.out.println("response content is --------------------》"+res.getBody()); System.out.println("response content is --------------------》" + res.getBody());
return res;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@SneakyThrows
public Response getReceiveStatus(Map<String, String> header, String id) {
HttpClient client = HttpClientFactory.get(Request.Util.OkHttp);
Request req = Request.create(RECEIVE_URL + id, Request.Method.GET, "");
try {
Response res = client.execute(req, Request.Option.create(0, 0, header));
System.out.println("response code ---------------------------->" + res.getCode());
System.out.println("response content is --------------------》" + res.getBody());
return res;
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
@NotNull @NotNull
private static Map<String , String> setHeader(String apiKey){ public static Map<String, String> setHeader(String apiKey) {
Map<String,String> header = new HashMap<>(); Map<String, String> header = new HashMap<>();
header.put("Accept","application/json,text/plain,*/*"); header.put("Accept", "application/json,text/plain,*/*");
header.put("Content-Type", "application/json"); header.put("Content-Type", "application/json");
header.put("Authorization", apiKey); header.put("Authorization", apiKey);
return header; return header;
} }
@NotNull @NotNull
private static Map<String, Object> setParams(String to,String message,String senderName) { public static Map<String, Object> setParams(String to, String message, String senderName) {
Map<String,Object> param = new HashMap<>(); Map<String, Object> param = new HashMap<>();
param.put("to",to); param.put("to", to);
param.put("message",message); param.put("message", message);
param.put("sender_name",senderName); param.put("sender_name", senderName);
param.put("route","dnd"); param.put("route", "dnd");
return param; return param;
} }
public static void main(String[] args){ public static void main(String[] args) {
SendchampHttp test = new SendchampHttp(); SendchampHttp test = new SendchampHttp();
String to = "2348140352000"; // String to = "2348140352000";
String to = "8618926674857";
String senderName = "Sendchamp"; String senderName = "Sendchamp";
//信息如果超过200字符,需要拆成多条短信,多次发送 //信息如果超过200字符,需要拆成多条短信,多次发送
String message = "Good day!This is E&C logistics(ship from China to Nigeria),It has been a while since we last worked together, and we would like to reestablish our partnership. Our main service: GROUPAGE/FULL CONTAINE"; String message = "Good day!This is E&C logistics(ship from China to Nigeria),It has been a while since we last worked together, and we would like to reestablish our partnership. Our main service: GROUPAGE/FULL CONTAINE";
Map<String, Object> param = setParams(to,message,senderName); Map<String, Object> param = setParams(to, message, senderName);
String apiKey = "Bearer sendchamp_live_$2a$10$vQPdaDjl96Ybc5tzFmZYg.nqGirXuJBGDqJArthZnFR8P9mM5Z/JO"; String apiKey = "Bearer sendchamp_live_$2a$10$vQPdaDjl96Ybc5tzFmZYg.nqGirXuJBGDqJArthZnFR8P9mM5Z/JO";
Map<String,String> header = setHeader(apiKey); Map<String, String> header = setHeader(apiKey);
test.postReq(param,header); test.getReceiveStatus(header, "66e6e9df-b454-4df7-a968-af944a535757");
} }
......
package cn.iocoder.yudao.framework.http.example; package cn.iocoder.yudao.framework.http.example;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.http.core.Request; import cn.iocoder.yudao.framework.http.core.Request;
import cn.iocoder.yudao.framework.http.core.Response; import cn.iocoder.yudao.framework.http.core.Response;
import cn.iocoder.yudao.framework.http.core.client.HttpClient; import cn.iocoder.yudao.framework.http.core.client.HttpClient;
import cn.iocoder.yudao.framework.http.core.client.HttpClientFactory; import cn.iocoder.yudao.framework.http.core.client.HttpClientFactory;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import java.util.ArrayList; import java.time.ZoneId;
import java.util.HashMap; import java.time.ZonedDateTime;
import java.util.List; import java.time.format.DateTimeFormatter;
import java.util.Map; import java.util.*;
/** /**
* @author wuxian * @author wuxian
* @since 2024-10-30 * @since 2024-10-30
**/ **/
public class YCloudWhatsappHttp { public class YCloudWhatsappHttp {
private HttpClient client = null; private HttpClient client = null;
//请求地址 //请求地址
private static final String WA_URL="https://api.ycloud.com/v2/whatsapp/messages/sendDirectly"; private static final String WA_URL = "https://api.ycloud.com/v2/whatsapp/messages/sendDirectly";
private static final String RECEIVE_URL = "https://api.ycloud.com/v2/whatsapp/messages/";
public YCloudWhatsappHttp() { public YCloudWhatsappHttp() {
init(); init();
} }
private void init(){ private void init() {
client = HttpClientFactory.get(Request.Util.OkHttp); client = HttpClientFactory.get(Request.Util.OkHttp);
} }
@SneakyThrows @SneakyThrows
public void postWhatsapp(Map<String, String> header,Map<String,Object> param){ public Response postWhatsapp(Map<String, String> header, Map<String, Object> param) {
Request req = Request.create(WA_URL,Request.Method.POST,param); Request req = Request.create(WA_URL, Request.Method.POST, param);
req.setParamFormat(Request.ParamFormat.JSON); req.setParamFormat(Request.ParamFormat.JSON);
Response res = client.execute(req,Request.Option.create(0,0,header)); Response res = client.execute(req, Request.Option.create(0, 0, header));
System.out.println("response code ---------------------------->"+res.getCode()); System.out.println("response code ---------------------------->" + res.getCode());
System.out.println("response content is --------------------》"+res.getBody()); System.out.println("response content is --------------------》" + res.getBody());
return res;
} }
@SneakyThrows
public Response getReceiveStatus(Map<String, String> header, String id) {
Request req = Request.create(RECEIVE_URL + id, Request.Method.GET, "");
Response res = client.execute(req, Request.Option.create(0, 0, header));
System.out.println("response code ---------------------------->" + res.getCode());
System.out.println("response content is --------------------》" + res.getBody());
return res;
}
/** /**
* * @param code 验证码
* @param code 验证码
* @param templateName 模板名称 * @param templateName 模板名称
* @param lanCode 语言编号 * @param lanCode 语言编号
* @param to 接收手机号
* @return 参数 * @return 参数
*/ */
private Map<String,Object> setParams(String code,String templateName,String lanCode){ public Map<String, Object> setParams(String code, String templateName, String lanCode, String to) {
Map<String, Object> template = new HashMap<>(); Map<String, Object> template = new HashMap<>();
Map<String, String> language = new HashMap<>(); Map<String, String> language = new HashMap<>();
Components components = new Components(); Components components = new Components();
...@@ -69,44 +83,65 @@ public class YCloudWhatsappHttp { ...@@ -69,44 +83,65 @@ public class YCloudWhatsappHttp {
list2.add(components); list2.add(components);
list2.add(button); list2.add(button);
language.put("code",lanCode); language.put("code", lanCode);
language.put("policy","deterministic"); language.put("policy", "deterministic");
template.put("language",language); template.put("language", language);
template.put("name",templateName); template.put("name", templateName);
template.put("components",list2); template.put("components", list2);
//构建最终requestBody的json //构建最终requestBody的json
Map<String, Object> param = new HashMap<>(); Map<String, Object> param = new HashMap<>();
param.put("from","8618022485824"); param.put("from", "8618022485824");
param.put("to","8618102810628"); param.put("to", to);
param.put("type","template"); param.put("type", "template");
param.put("template",template); param.put("template", template);
return param; return param;
} }
//构建http请求的头 //构建http请求的头
private Map<String,String> setHeader(String apiKey){ public Map<String, String> setHeader(String apiKey) {
Map<String,String> header = new HashMap<>(); Map<String, String> header = new HashMap<>();
header.put("Accept","application/json"); header.put("Accept", "application/json");
header.put("Content-Type", "application/json"); header.put("Content-Type", "application/json");
header.put("X-API-Key", apiKey); header.put("X-API-Key", apiKey);
return header; return header;
} }
public static void main(String[] args){ public static void main(String[] args) {
YCloudWhatsappHttp test = new YCloudWhatsappHttp(); YCloudWhatsappHttp test = new YCloudWhatsappHttp();
String apiKey = "9dbd912f56c101e53b23cb7b758ffda8"; String apiKey = "9dbd912f56c101e53b23cb7b758ffda8";
String code = "8888"; // String code = "8888";
String templateName ="ec_verification"; // String templateName = "ec_verification";
String lanCode ="en"; // String lanCode = "en";
test.postWhatsapp(test.setHeader(apiKey),test.setParams(code,templateName,lanCode)); // test.postWhatsapp(test.setHeader(apiKey), test.setParams(code, templateName, lanCode, "8618926674857"));
Response response = test.getReceiveStatus(test.setHeader(apiKey), "67370e34334f011e030dfd7b");
Map<String, Object> responseObj = JsonUtils.parseObject(response.getBody(), Map.class);
String status = (String) responseObj.get("status");
if (status != null && ("delivered".equals(status) || "read".equals(status))) {
System.out.println(status);
String deliverTime = (String) responseObj.get("deliverTime");
// 定义 ISO 8601 格式的日期时间解析器
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX");
// 将字符串解析为 ZonedDateTime 对象
ZonedDateTime utcDateTime = ZonedDateTime.parse(deliverTime, formatter);
// 获取本地时区
ZoneId localZoneId = ZoneId.systemDefault();
// 将 UTC 时间转换为本地时间
ZonedDateTime localDateTime = utcDateTime.withZoneSameInstant(localZoneId);
// 将 ZonedDateTime 转换为 Date
Date localDate = Date.from(localDateTime.toInstant());
// 输出本地时间
System.out.println("本地时间: " + localDate);
} else {
System.out.println("null");
}
} }
} }
@lombok.Setter @lombok.Setter
@lombok.Getter @lombok.Getter
class Parameters{ class Parameters {
private String type; private String type;
private String text; private String text;
...@@ -122,10 +157,10 @@ class Components { ...@@ -122,10 +157,10 @@ class Components {
@lombok.Setter @lombok.Setter
@lombok.Getter @lombok.Getter
class Button { class Button {
private String type; private String type;
private String sub_type; private String sub_type;
private String index; private String index;
private List<Object> parameters= null; private List<Object> parameters = null;
} }
......
package cn.iocoder.yudao.framework.sms.core.client; package cn.iocoder.yudao.framework.sms.core.client;
import cn.iocoder.yudao.framework.common.util.json.core.KeyValue; import cn.iocoder.yudao.framework.common.util.json.core.KeyValue;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO; import cn.iocoder.yudao.framework.sms.core.client.dto.*;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
import java.util.List; import java.util.List;
...@@ -25,15 +23,27 @@ public interface SmsClient { ...@@ -25,15 +23,27 @@ public interface SmsClient {
/** /**
* 发送消息 * 发送消息
* *
* @param logId 日志编号 * @param logId 日志编号
* @param mobile 手机号 * @param mobile 手机号
* @param apiTemplateId 短信 API 的模板编号 * @param apiTemplateId 短信 API 的模板编号
* @param templateParams 短信模板参数。通过 List 数组,保证参数的顺序 * @param templateParams 短信模板参数。通过 List 数组,保证参数的顺序
* @return 短信发送结果 * @return 短信发送结果
*/ */
SmsCommonResult<SmsSendRespDTO> sendSms(Long logId, String mobile, String apiTemplateId, SmsCommonResult<SmsSendRespDTO> sendSms(Long logId, String mobile, String apiTemplateId,
List<KeyValue<String, Object>> templateParams); List<KeyValue<String, Object>> templateParams);
/**
* 发送消息
*
* @param logId 日志编号
* @param mobile 手机号
* @param smsTemplateDTO 模板
* @param templateParams 短信模板参数。通过 List 数组,保证参数的顺序
* @return 短信发送结果
*/
SmsCommonResult<SmsSendRespDTO> sendSmsV2(Long logId, String mobile, SmsTemplateDTO smsTemplateDTO,
List<KeyValue<String, Object>> templateParams);
/** /**
* 解析接收短信的接收结果 * 解析接收短信的接收结果
* *
...@@ -51,4 +61,11 @@ public interface SmsClient { ...@@ -51,4 +61,11 @@ public interface SmsClient {
*/ */
SmsCommonResult<SmsTemplateRespDTO> getSmsTemplate(String apiTemplateId); SmsCommonResult<SmsTemplateRespDTO> getSmsTemplate(String apiTemplateId);
/**
* 查询短信发送状态
*
* @param smsLogDO
* @return
*/
SmsLogDTO getReceiveStatus(SmsLogDTO smsLogDO);
} }
package cn.iocoder.yudao.framework.sms.core.client.dto;
import cn.iocoder.yudao.framework.common.util.spring.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.Data;
import lombok.ToString;
import java.util.Date;
import java.util.Map;
/**
* 日志dto
*
* @author Jayden
* @date 2024/11/6
*/
@Data
@ToString(callSuper = true)
public class SmsLogDTO {
/**
* 自增编号
*/
private Long id;
// ========= 渠道相关字段 =========
/**
* 短信渠道编号
* <p>
*/
private Long channelId;
/**
* 短信渠道编码
* <p>
*/
private String channelCode;
// ========= 模板相关字段 =========
/**
* 模板编号
* <p>
*/
private Long templateId;
/**
* 模板编码
* <p>
*/
private String templateCode;
/**
* 短信类型
* <p>
*/
private Integer templateType;
/**
*
*/
private String templateContent;
/**
*
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private Map<String, Object> templateParams;
/**
* 短信 API 的模板编号
* <p>
*/
private String apiTemplateId;
// ========= 手机相关字段 =========
/**
* 手机号
*/
private String mobile;
/**
* 用户编号
*/
private Long userId;
/**
* 用户类型
* <p>
* 枚举 {@link UserTypeEnum}
*/
private Integer userType;
// ========= 发送相关字段 =========
/**
* 发送状态
* <p>
*/
private Integer sendStatus;
/**
* 发送时间
*/
private Date sendTime;
/**
* 发送结果的编码
* <p>
* 枚举 {@link SmsFrameworkErrorCodeConstants}
*/
private Integer sendCode;
/**
* 发送结果的提示
* <p>
* 一般情况下,使用 {@link SmsFrameworkErrorCodeConstants}
* 异常情况下,通过格式化 Exception 的提示存储
*/
private String sendMsg;
/**
* 短信 API 发送结果的编码
* <p>
* 由于第三方的错误码可能是字符串,所以使用 String 类型
*/
private String apiSendCode;
/**
* 短信 API 发送失败的提示
*/
private String apiSendMsg;
/**
* 短信 API 发送返回的唯一请求 ID
* <p>
* 用于和短信 API 进行定位于排错
*/
private String apiRequestId;
/**
* 短信 API 发送返回的序号
* <p>
* 用于和短信 API 平台的发送记录关联
*/
private String apiSerialNo;
// ========= 接收相关字段 =========
/**
* 接收状态
* <p>
*/
private Integer receiveStatus;
/**
* 接收时间
*/
private Date receiveTime;
/**
* 短信 API 接收结果的编码
*/
private String apiReceiveCode;
/**
* 短信 API 接收结果的提示
*/
private String apiReceiveMsg;
/**
* 重发的短信日志id
*/
private Long smsLogId;
/**
* 重发次数
*/
private Integer num;
/**
* 短信节点表id
*/
private Long nodeId;
/**
* 节点模板序列号
*/
private Integer nodeTemplateSn;
/**
* 发送类型
*/
private Integer messageType;
}
package cn.iocoder.yudao.framework.sms.core.client.dto;
import lombok.Data;
import lombok.ToString;
/**
* 模板dto
*
* @author Jayden
* @date 2024/11/6
*/
@Data
@ToString(callSuper = true)
public class SmsTemplateDTO {
/**
* 自增编号
*/
private Long id;
// ========= 模板相关字段 =========
/**
* 短信类型
*/
private Integer type;
/**
* 启用状态
*/
private Integer status;
/**
* 模板编码,保证唯一
*/
private String code;
/**
* 模板名称
*/
private String name;
/**
* 模板内容
*/
private String content;
/**
* 备注
*/
private String remark;
/**
* 短信 API 的模板编号
*/
private String apiTemplateId;
// ========= 渠道相关字段 =========
/**
* 短信渠道编号
*/
private Long channelId;
/**
* 短信渠道编码
*/
private String channelCode;
/**
* 节点
*/
private String nodeValue;
/**
* 运输方式
*/
private Long transportId;
/**
* 发送类型
*/
private Integer messageType;
/**
* 语言
*/
private String language;
}
...@@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.sms.core.client.SmsClient; ...@@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory; import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
import cn.iocoder.yudao.framework.sms.core.client.impl.aliyun.AliyunSmsClient; import cn.iocoder.yudao.framework.sms.core.client.impl.aliyun.AliyunSmsClient;
import cn.iocoder.yudao.framework.sms.core.client.impl.debug.DebugDingTalkSmsClient; import cn.iocoder.yudao.framework.sms.core.client.impl.debug.DebugDingTalkSmsClient;
import cn.iocoder.yudao.framework.sms.core.client.impl.sendchamp.SendchampSmsClient;
import cn.iocoder.yudao.framework.sms.core.client.impl.yCloud.YCloudSmsClient;
import cn.iocoder.yudao.framework.sms.core.client.impl.yunpian.YunpianSmsClient; import cn.iocoder.yudao.framework.sms.core.client.impl.yunpian.YunpianSmsClient;
import cn.iocoder.yudao.framework.sms.core.enums.SmsChannelEnum; import cn.iocoder.yudao.framework.sms.core.enums.SmsChannelEnum;
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties; import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
...@@ -33,7 +35,7 @@ public class SmsClientFactoryImpl implements SmsClientFactory { ...@@ -33,7 +35,7 @@ public class SmsClientFactoryImpl implements SmsClientFactory {
/** /**
* 短信客户端 Map * 短信客户端 Map
* key:渠道编码,使用 {@link SmsChannelProperties#getCode()} ()} * key:渠道编码,使用 {@link SmsChannelProperties#getCode()} ()}
* * <p>
* 注意,一些场景下,需要获得某个渠道类型的客户端,所以需要使用它。 * 注意,一些场景下,需要获得某个渠道类型的客户端,所以需要使用它。
* 例如说,解析短信接收结果,是相对通用的,不需要使用某个渠道编号的 {@link #channelIdClients} * 例如说,解析短信接收结果,是相对通用的,不需要使用某个渠道编号的 {@link #channelIdClients}
*/ */
...@@ -78,9 +80,16 @@ public class SmsClientFactoryImpl implements SmsClientFactory { ...@@ -78,9 +80,16 @@ public class SmsClientFactoryImpl implements SmsClientFactory {
Assert.notNull(channelEnum, String.format("渠道类型(%s) 为空", channelEnum)); Assert.notNull(channelEnum, String.format("渠道类型(%s) 为空", channelEnum));
// 创建客户端 // 创建客户端
switch (channelEnum) { switch (channelEnum) {
case ALIYUN: return new AliyunSmsClient(properties); case ALIYUN:
case YUN_PIAN: return new YunpianSmsClient(properties); return new AliyunSmsClient(properties);
case DEBUG_DING_TALK: return new DebugDingTalkSmsClient(properties); case YUN_PIAN:
return new YunpianSmsClient(properties);
case DEBUG_DING_TALK:
return new DebugDingTalkSmsClient(properties);
case SENDCHAMP:
return new SendchampSmsClient(properties);
case YCLOUD:
return new YCloudSmsClient(properties);
} }
// 创建失败,错误日志 + 抛出异常 // 创建失败,错误日志 + 抛出异常
log.error("[createSmsClient][配置({}) 找不到合适的客户端实现]", properties); log.error("[createSmsClient][配置({}) 找不到合适的客户端实现]", properties);
......
...@@ -3,20 +3,21 @@ package cn.iocoder.yudao.framework.sms.core.client.impl.aliyun; ...@@ -3,20 +3,21 @@ package cn.iocoder.yudao.framework.sms.core.client.impl.aliyun;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.json.core.KeyValue; import cn.iocoder.yudao.framework.common.util.json.core.KeyValue;
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult; import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO; import cn.iocoder.yudao.framework.sms.core.client.dto.*;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.impl.AbstractSmsClient; import cn.iocoder.yudao.framework.sms.core.client.impl.AbstractSmsClient;
import cn.iocoder.yudao.framework.sms.core.enums.ReceiveStatusEnum;
import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum; import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties; import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import com.aliyuncs.AcsRequest; import com.aliyuncs.AcsRequest;
import com.aliyuncs.AcsResponse; import com.aliyuncs.AcsResponse;
import com.aliyuncs.DefaultAcsClient; import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient; import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsResponse;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateRequest; import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest; import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.exceptions.ClientException; import com.aliyuncs.exceptions.ClientException;
...@@ -25,9 +26,11 @@ import com.aliyuncs.profile.IClientProfile; ...@@ -25,9 +26,11 @@ import com.aliyuncs.profile.IClientProfile;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import io.undertow.util.DateUtils;
import lombok.Data; import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
...@@ -56,6 +59,8 @@ public class AliyunSmsClient extends AbstractSmsClient { ...@@ -56,6 +59,8 @@ public class AliyunSmsClient extends AbstractSmsClient {
*/ */
private volatile IAcsClient client; private volatile IAcsClient client;
private static final String DELIVERED_STATUS = "DELIVERED";
public AliyunSmsClient(SmsChannelProperties properties) { public AliyunSmsClient(SmsChannelProperties properties) {
super(properties, new AliyunSmsCodeMapping()); super(properties, new AliyunSmsCodeMapping());
Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空"); Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空");
...@@ -74,9 +79,9 @@ public class AliyunSmsClient extends AbstractSmsClient { ...@@ -74,9 +79,9 @@ public class AliyunSmsClient extends AbstractSmsClient {
// 构建参数 // 构建参数
SendSmsRequest request = new SendSmsRequest(); SendSmsRequest request = new SendSmsRequest();
request.setPhoneNumbers(mobile); request.setPhoneNumbers(mobile);
if(mobile.startsWith("0086")||mobile.startsWith("86")||mobile.startsWith("+86")||mobile.startsWith("+0086")) { if (mobile.startsWith("0086") || mobile.startsWith("86") || mobile.startsWith("+86") || mobile.startsWith("+0086")) {
request.setSignName(properties.getSignature()); request.setSignName(properties.getSignature());
}else{ } else {
request.setSignName(properties.getSignatureEn()); request.setSignName(properties.getSignatureEn());
} }
request.setTemplateCode(apiTemplateId); request.setTemplateCode(apiTemplateId);
...@@ -116,10 +121,14 @@ public class AliyunSmsClient extends AbstractSmsClient { ...@@ -116,10 +121,14 @@ public class AliyunSmsClient extends AbstractSmsClient {
@VisibleForTesting @VisibleForTesting
Integer convertSmsTemplateAuditStatus(Integer templateStatus) { Integer convertSmsTemplateAuditStatus(Integer templateStatus) {
switch (templateStatus) { switch (templateStatus) {
case 0: return SmsTemplateAuditStatusEnum.CHECKING.getStatus(); case 0:
case 1: return SmsTemplateAuditStatusEnum.SUCCESS.getStatus(); return SmsTemplateAuditStatusEnum.CHECKING.getStatus();
case 2: return SmsTemplateAuditStatusEnum.FAIL.getStatus(); case 1:
default: throw new IllegalArgumentException(String.format("未知审核状态(%d)", templateStatus)); return SmsTemplateAuditStatusEnum.SUCCESS.getStatus();
case 2:
return SmsTemplateAuditStatusEnum.FAIL.getStatus();
default:
throw new IllegalArgumentException(String.format("未知审核状态(%d)", templateStatus));
} }
} }
...@@ -150,9 +159,96 @@ public class AliyunSmsClient extends AbstractSmsClient { ...@@ -150,9 +159,96 @@ public class AliyunSmsClient extends AbstractSmsClient {
return ex.getErrMsg() + " => " + ex.getErrorDescription(); return ex.getErrMsg() + " => " + ex.getErrorDescription();
} }
@Override
public SmsCommonResult<SmsSendRespDTO> sendSmsV2(Long sendLogId, String mobile, SmsTemplateDTO smsTemplateDTO, List<KeyValue<String, Object>> templateParams) {
// 构建参数
SendSmsRequest request = new SendSmsRequest();
request.setPhoneNumbers(mobile);
String apiTemplateId = smsTemplateDTO.getApiTemplateId();
request.setSignName(properties.getSignature());
request.setTemplateCode(apiTemplateId);
request.setTemplateParam(JsonUtils.toJsonString(MapUtils.convertMap(templateParams)));
request.setOutId(String.valueOf(sendLogId));
// 执行请求
return invoke(request, response -> new SmsSendRespDTO().setSerialNo(response.getBizId()));
}
/** /**
* 短信接收状态 * 获取短信日志的接收状态
* 通过调用阿里云短信服务的查询接口,更新短信日志的接收状态
* *
* @param smsLogDO 短信日志实体,包含查询所需的参数,如手机号、发送时间等
* @return 更新了接收状态的短信日志实体
*/
@Override
public SmsLogDTO getReceiveStatus(SmsLogDTO smsLogDO) {
// 初始化短信日志DTO,并默认设置接收状态为未接收到
SmsLogDTO smsLogDTO = new SmsLogDTO();
smsLogDTO.setReceiveStatus(ReceiveStatusEnum.Receive_STATUS_20.getValue());
// 创建查询发送详情的请求
QuerySendDetailsRequest querySendDetailsRequest = new QuerySendDetailsRequest();
// 设置查询参数,包括手机号(去掉国际拨号)、页大小、当前页、发送日期和业务ID
querySendDetailsRequest.setPhoneNumber(removeCountryCode(smsLogDO.getMobile()));
querySendDetailsRequest.setPageSize(1L);
querySendDetailsRequest.setCurrentPage(1L);
querySendDetailsRequest.setSendDate(formatDate(smsLogDO.getSendTime()));
querySendDetailsRequest.setBizId(smsLogDO.getApiSerialNo());
// 初始化查询响应对象
QuerySendDetailsResponse acsResponse = null;
try {
// 发起查询请求并获取响应
acsResponse = client.getAcsResponse(querySendDetailsRequest);
} catch (ClientException e) {
// 如果查询出错,记录日志并返回初始化的短信日志DTO
log.error("Error querying send details: ", e);
return smsLogDTO;
}
// 如果查询成功且有查询结果,更新短信日志的接收状态
if (acsResponse != null && !acsResponse.getSmsSendDetailDTOs().isEmpty()) {
// 获取第一条查询结果
QuerySendDetailsResponse.SmsSendDetailDTO smsSendDetailDTO = acsResponse.getSmsSendDetailDTOs().get(0);
// 如果短信发送成功,更新接收状态为已接收到,并设置接收时间
if (DELIVERED_STATUS.equals(smsSendDetailDTO.getErrCode())) {
smsLogDTO.setReceiveStatus(ReceiveStatusEnum.Receive_STATUS_10.getValue());
String receiveDate = smsSendDetailDTO.getReceiveDate();
if (receiveDate != null) {
smsLogDTO.setReceiveTime(DateUtils.parseDate(receiveDate));
}
}
}
// 返回更新后的短信日志DTO
return smsLogDTO;
}
/**
* 去掉手机号开头的国家代码 "86"
*
* @param phoneNumber 包含国家代码的手机号
* @return 去掉国家代码后的手机号
*/
public static String removeCountryCode(String phoneNumber) {
// 使用 replaceFirst 方法去掉开头的 "86"
return phoneNumber.replaceFirst("^86", "");
}
/**
* 将 Date 对象格式化为指定格式的字符串
*
* @param date 需要格式化的 Date 对象
* @return 格式化后的字符串,格式为 "yyyyMMdd"
*/
public static String formatDate(Date date) {
SimpleDateFormat outputFormat = new SimpleDateFormat("yyyyMMdd");
return outputFormat.format(date);
}
/**
* 短信接收状态
* <p>
* 参见 https://help.aliyun.com/document_detail/101867.html 文档 * 参见 https://help.aliyun.com/document_detail/101867.html 文档
* *
* @author 捷道源码 * @author 捷道源码
...@@ -198,14 +294,14 @@ public class AliyunSmsClient extends AbstractSmsClient { ...@@ -198,14 +294,14 @@ public class AliyunSmsClient extends AbstractSmsClient {
private String bizId; private String bizId;
/** /**
* 用户序列号 * 用户序列号
* * <p>
* 这里我们传递的是 SysSmsLogDO 的日志编号 * 这里我们传递的是 SysSmsLogDO 的日志编号
*/ */
@JsonProperty("out_id") @JsonProperty("out_id")
private String outId; private String outId;
/** /**
* 短信长度,例如说 1、2、3 * 短信长度,例如说 1、2、3
* * <p>
* 140 字节算一条短信,短信长度超过 140 字节时会拆分成多条短信发送 * 140 字节算一条短信,短信长度超过 140 字节时会拆分成多条短信发送
*/ */
@JsonProperty("sms_size") @JsonProperty("sms_size")
......
...@@ -9,9 +9,7 @@ import cn.hutool.crypto.digest.HmacAlgorithm; ...@@ -9,9 +9,7 @@ import cn.hutool.crypto.digest.HmacAlgorithm;
import cn.hutool.http.HttpUtil; import cn.hutool.http.HttpUtil;
import cn.iocoder.yudao.framework.common.util.json.core.KeyValue; import cn.iocoder.yudao.framework.common.util.json.core.KeyValue;
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult; import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO; import cn.iocoder.yudao.framework.sms.core.client.dto.*;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.impl.AbstractSmsClient; import cn.iocoder.yudao.framework.sms.core.client.impl.AbstractSmsClient;
import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum; import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties; import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
...@@ -93,4 +91,13 @@ public class DebugDingTalkSmsClient extends AbstractSmsClient { ...@@ -93,4 +91,13 @@ public class DebugDingTalkSmsClient extends AbstractSmsClient {
return SmsCommonResult.build("0", "success", null, data, codeMapping); return SmsCommonResult.build("0", "success", null, data, codeMapping);
} }
@Override
public SmsCommonResult<SmsSendRespDTO> sendSmsV2(Long logId, String mobile, SmsTemplateDTO smsTemplateDTO, List<KeyValue<String, Object>> templateParams) {
return null;
}
@Override
public SmsLogDTO getReceiveStatus(SmsLogDTO smsLogDO) {
return null;
}
} }
package cn.iocoder.yudao.framework.sms.core.client.impl.sendchamp;
import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.json.core.KeyValue;
import cn.iocoder.yudao.framework.http.core.Response;
import cn.iocoder.yudao.framework.http.example.SendchampHttp;
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
import cn.iocoder.yudao.framework.sms.core.client.dto.*;
import cn.iocoder.yudao.framework.sms.core.client.impl.AbstractSmsClient;
import cn.iocoder.yudao.framework.sms.core.enums.ReceiveStatusEnum;
import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Sendchamp客户端
*/
@Slf4j
public class SendchampSmsClient extends AbstractSmsClient {
// 创建HTTP客户端实例,用于发送短信
private final SendchampHttp sendchampHttp = new SendchampHttp();
// 定义发送方名称,此处为固定值
private final String senderName = "Sendchamp";
public SendchampSmsClient(SmsChannelProperties properties) {
super(properties, new SendchampSmsCodeMapping());
Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空");
Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
}
@Override
protected void doInit() {
}
@Override
protected SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile, String apiTemplateId, List<KeyValue<String, Object>> templateParams) throws Throwable {
return null;
}
/**
* 发送短信服务(版本2)
* 该方法用于发送短信,会根据模板内容和参数动态生成短信内容,并处理发送过程
*
* @param logId 日志ID,用于追踪和记录
* @param mobile 手机号码,短信接收方
* @param smsTemplateDTO 短信模板信息,包括模板内容等
* @param templateParams 模板参数,用于替换模板中的占位符
* @return 返回短信发送结果,包括状态码、消息ID等信息
*/
@Override
public SmsCommonResult<SmsSendRespDTO> sendSmsV2(Long logId, String mobile, SmsTemplateDTO smsTemplateDTO, List<KeyValue<String, Object>> templateParams) {
SmsCommonResult<SmsSendRespDTO> smsCommonResult = SmsCommonResult.build("400", "Bad Request", null, null, codeMapping);
// 获取短信模板内容
String content = smsTemplateDTO.getContent();
// 如果有模板参数,则进行参数替换
if (templateParams != null) {
for (KeyValue<String, Object> entry : templateParams) {
// 对键和值进行转义,以确保安全性
String key = escapeKey(entry.getKey());
String value = escapeValue(entry.getValue());
// 构建正则表达式,用于匹配模板中的占位符
String regex = "\\$\\{" + key + "\\}";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(content);
// 将匹配到的占位符替换为实际值
content = matcher.replaceAll(value);
}
}
try {
// 如果信息超过200字符,需要拆分成多条短信发送
List<String> messages = splitContent(content, 200);
for (String message : messages) {
// 设置发送短信所需的参数
Map<String, Object> param = sendchampHttp.setParams(mobile, message, senderName);
// 设置请求头,包含认证信息
Map<String, String> header = sendchampHttp.setHeader("Bearer " + properties.getApiSecret());
// 发送POST请求
Response response = sendchampHttp.postReq(param, header);
// 获取响应体
String result = response.getBody();
// 解析响应结果
Map<?, ?> responseObj = JsonUtils.parseObject(result, Map.class);
Map<String, Object> data = (Map<String, Object>) responseObj.get("data");
// 构建发送结果
smsCommonResult = SmsCommonResult.build(String.valueOf(responseObj.get("status")), String.valueOf(responseObj.get("message")), String.valueOf(data.get("business_id")), null, codeMapping);
smsCommonResult.setData(new SmsSendRespDTO().setSerialNo(String.valueOf(data.get("business_id"))));
}
} catch (Exception e) {
// 记录发送短信时发生的错误
log.error("Error sending SMS: ", e);
// 返回内部服务器错误结果
return SmsCommonResult.build("500", "Internal Server Error", null, null, codeMapping);
}
// 返回请求结果
return smsCommonResult;
}
@Override
public SmsLogDTO getReceiveStatus(SmsLogDTO smsLogDO) {
// 初始化短信日志DTO,并默认设置接收状态为未接收到
SmsLogDTO smsLogDTO = new SmsLogDTO();
smsLogDTO.setReceiveStatus(ReceiveStatusEnum.Receive_STATUS_20.getValue());
return smsLogDTO;
}
/**
* 转义键
* 对键进行转义,以防止恶意代码注入
*
* @param key 待转义的键
* @return 转义后的键
*/
private String escapeKey(String key) {
return key.replaceAll("[^a-zA-Z0-9_]", "");
}
/**
* 转义值
* 对值进行转义,以防止恶意代码注入
*
* @param value 待转义的值
* @return 转义后的值
*/
private String escapeValue(Object value) {
if (value instanceof String) {
return ((String) value).replaceAll("[^a-zA-Z0-9_]", "");
}
return value.toString();
}
/**
* 拆分内容
* 将内容拆分成多个部分,每个部分不超过 maxLength 字符
*
* @param content 待拆分的内容
* @param maxLength 每个部分的最大长度
* @return 拆分后的内容列表
*/
private List<String> splitContent(String content, int maxLength) {
List<String> messages = new ArrayList<>();
int start = 0;
while (start < content.length()) {
int end = Math.min(start + maxLength, content.length());
messages.add(content.substring(start, end));
start = end;
}
return messages;
}
@Override
protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable {
return null;
}
@Override
protected SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) throws Throwable {
SmsTemplateRespDTO data = new SmsTemplateRespDTO().setId(apiTemplateId).setContent("")
.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason("");
return SmsCommonResult.build("accepted", "accepted", null, data, codeMapping);
}
/**
* 短信接收状态
* <p>
* @author 捷道源码
*/
@Data
public static class SmsReceiveStatus {
/**
* module
*/
@JsonProperty("module")
private String module;
/**
* reference
*/
@JsonProperty("reference")
private String reference;
/**
* 状态
*/
@JsonProperty("status")
private String status;
}
}
package cn.iocoder.yudao.framework.sms.core.client.impl.sendchamp;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.yudao.framework.sms.core.client.SmsCodeMapping;
import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
/**
* sendchamp SmsCodeMapping 实现类
*/
public class SendchampSmsCodeMapping implements SmsCodeMapping {
@Override
public ErrorCode apply(String apiCode) {
switch (apiCode) {
case "accepted": return GlobalErrorCodeConstants.SUCCESS;
}
return SmsFrameworkErrorCodeConstants.SMS_UNKNOWN;
}
}
package cn.iocoder.yudao.framework.sms.core.client.impl.yCloud;
import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.json.core.KeyValue;
import cn.iocoder.yudao.framework.http.core.Response;
import cn.iocoder.yudao.framework.http.example.YCloudWhatsappHttp;
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
import cn.iocoder.yudao.framework.sms.core.client.dto.*;
import cn.iocoder.yudao.framework.sms.core.client.impl.AbstractSmsClient;
import cn.iocoder.yudao.framework.sms.core.client.impl.sendchamp.SendchampSmsCodeMapping;
import cn.iocoder.yudao.framework.sms.core.enums.ReceiveStatusEnum;
import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
import lombok.extern.slf4j.Slf4j;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* ycloud客户端
*
* @author Jayden
* @date 2024/11/7
*/
@Slf4j
public class YCloudSmsClient extends AbstractSmsClient {
// 创建HTTP客户端实例,用于发送短信
private final YCloudWhatsappHttp yCloudWhatsappHttp = new YCloudWhatsappHttp();
public YCloudSmsClient(SmsChannelProperties properties) {
super(properties, new SendchampSmsCodeMapping());
Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空");
Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
}
@Override
protected void doInit() {
}
@Override
protected SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile, String apiTemplateId, List<KeyValue<String, Object>> templateParams) throws Throwable {
return null;
}
/**
* 发送短信V2版本
* 该方法用于发送短信,根据提供的日志ID、手机号码、短信模板和模板参数进行短信发送
* 主要逻辑包括处理模板参数、调用HTTP工具发送短信以及解析响应结果
*
* @param logId 日志ID,用于追踪和记录发送短信的日志
* @param mobile 手机号码,指定短信接收者的电话号码
* @param smsTemplateDTO 短信模板数据传输对象,包含短信模板的相关信息
* @param templateParams 模板参数列表,包含短信模板中的变量和对应的值
* @return 返回一个通用的结果对象,包含短信发送的相关信息和结果状态
*/
@Override
public SmsCommonResult<SmsSendRespDTO> sendSmsV2(Long logId, String mobile, SmsTemplateDTO smsTemplateDTO, List<KeyValue<String, Object>> templateParams) {
// 默认验证码为空字符串
String code = "";
// 如果模板参数不为空,则遍历模板参数以获取验证码
if (templateParams != null) {
for (KeyValue<String, Object> entry : templateParams) {
code = (String) entry.getValue();
}
}
// 发送短信并获取响应
Response response = yCloudWhatsappHttp.postWhatsapp(yCloudWhatsappHttp.setHeader(properties.getApiSecret()),
yCloudWhatsappHttp.setParams(code, smsTemplateDTO.getApiTemplateId(), smsTemplateDTO.getLanguage(), mobile));
// 解析结果
Map<String, Object> responseObj = JsonUtils.parseObject(response.getBody(), Map.class);
// 构建短信发送结果对象
SmsCommonResult<SmsSendRespDTO> smsCommonResult = SmsCommonResult.build(String.valueOf(responseObj.get("status")), String.valueOf(responseObj.get("status")), String.valueOf(responseObj.get("id")), null, codeMapping);
// 设置短信发送结果的详细信息
smsCommonResult.setData(new SmsSendRespDTO().setSerialNo(String.valueOf(responseObj.get("id"))));
// 返回短信发送结果
return smsCommonResult;
}
/**
* 获取短信接收状态
* 本方法通过调用第三方API来获取短信的实际接收状态,并更新短信日志DTO中的相关信息
*
* @param smsLogDO 短信日志DTO,包含需要查询接收状态的短信的相关信息
* @return 返回更新了接收状态和接收时间的短信日志DTO
*/
@Override
public SmsLogDTO getReceiveStatus(SmsLogDTO smsLogDO) {
// 初始化短信日志DTO,并默认设置接收状态为未接收到
SmsLogDTO smsLogDTO = new SmsLogDTO();
smsLogDTO.setReceiveStatus(ReceiveStatusEnum.Receive_STATUS_20.getValue());
try {
// 发送短信并获取响应
Response response = yCloudWhatsappHttp.getReceiveStatus(yCloudWhatsappHttp.setHeader(properties.getApiSecret()), smsLogDO.getApiSerialNo());
// 解析响应内容为Map对象,以便后续处理
Map<String, Object> responseObj = JsonUtils.parseObject(response.getBody(), Map.class);
// 获取短信状态
String status = (String) responseObj.get("status");
// 如果短信状态为已送达或已读取,则进一步处理
if (status != null && ("delivered".equals(status) || "read".equals(status))) {
// 获取送达时间
String deliverTime = (String) responseObj.get("deliverTime");
// 解析并转换送达时间为本地时间
Date localDate = parseAndConvertToLocalDate(deliverTime);
// 更新短信日志DTO的接收时间和接收状态
smsLogDTO.setReceiveTime(localDate);
smsLogDTO.setReceiveStatus(ReceiveStatusEnum.Receive_STATUS_10.getValue());
}
} catch (Exception e) {
// 记录异常信息
log.error("Error occurred while getting receive status: ", e);
// 保持默认的接收状态为未接收到
smsLogDTO.setReceiveStatus(ReceiveStatusEnum.Receive_STATUS_20.getValue());
}
// 返回更新后的短信日志DTO
return smsLogDTO;
}
/**
* 解析并转换时间为本地时间
* 本方法将给定的ISO 8601格式的日期时间字符串解析为日期对象,并转换为本地时间
*
* @param deliverTime ISO 8601格式的日期时间字符串
* @return 返回转换后的本地时间日期对象
*/
private Date parseAndConvertToLocalDate(String deliverTime) {
// 定义 ISO 8601 格式的日期时间解析器
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX");
// 将字符串解析为 ZonedDateTime 对象
ZonedDateTime utcDateTime = ZonedDateTime.parse(deliverTime, formatter);
// 获取本地时区
ZoneId localZoneId = ZoneId.systemDefault();
// 将 UTC 时间转换为本地时间
ZonedDateTime localDateTime = utcDateTime.withZoneSameInstant(localZoneId);
// 将 ZonedDateTime 转换为 Date
return Date.from(localDateTime.toInstant());
}
@Override
protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) {
throw new UnsupportedOperationException("无回调");
}
@Override
protected SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) {
SmsTemplateRespDTO data = new SmsTemplateRespDTO().setId(apiTemplateId).setContent("")
.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason("");
return SmsCommonResult.build("accepted", "accepted", null, data, codeMapping);
}
}
package cn.iocoder.yudao.framework.sms.core.client.impl.yCloud;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.yudao.framework.sms.core.client.SmsCodeMapping;
import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
/**
* ycloud SmsCodeMapping 实现类
*
* @author Jayden
* @date 2024/11/7
*/
public class YCloudSmsCodeMapping implements SmsCodeMapping {
@Override
public ErrorCode apply(String apiCode) {
switch (apiCode) {
case "success": return GlobalErrorCodeConstants.SUCCESS;
}
return SmsFrameworkErrorCodeConstants.SMS_UNKNOWN;
}
}
...@@ -6,9 +6,7 @@ import cn.hutool.core.util.StrUtil; ...@@ -6,9 +6,7 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil; import cn.hutool.core.util.URLUtil;
import cn.iocoder.yudao.framework.common.util.json.core.KeyValue; import cn.iocoder.yudao.framework.common.util.json.core.KeyValue;
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult; import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO; import cn.iocoder.yudao.framework.sms.core.client.dto.*;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.impl.AbstractSmsClient; import cn.iocoder.yudao.framework.sms.core.client.impl.AbstractSmsClient;
import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum; import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties; import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
...@@ -148,6 +146,16 @@ public class YunpianSmsClient extends AbstractSmsClient { ...@@ -148,6 +146,16 @@ public class YunpianSmsClient extends AbstractSmsClient {
return sendResult.getMsg() + " => " + sendResult.getDetail(); return sendResult.getMsg() + " => " + sendResult.getDetail();
} }
@Override
public SmsCommonResult<SmsSendRespDTO> sendSmsV2(Long logId, String mobile, SmsTemplateDTO smsTemplateDTO, List<KeyValue<String, Object>> templateParams) {
return null;
}
@Override
public SmsLogDTO getReceiveStatus(SmsLogDTO smsLogDO) {
return null;
}
/** /**
* 短信接收状态 * 短信接收状态
* *
......
package cn.iocoder.yudao.framework.sms.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 短信接收状态
*
* @author jayden
*/
@Getter
@AllArgsConstructor
public enum ReceiveStatusEnum {
RECEIVE_STATUS_0("等待结果", 0),
Receive_STATUS_10("接收成功", 10),
Receive_STATUS_20("接收失败", 20);
private String name;
private Integer value;
}
...@@ -17,6 +17,8 @@ public enum SmsChannelEnum { ...@@ -17,6 +17,8 @@ public enum SmsChannelEnum {
DEBUG_DING_TALK("DEBUG_DING_TALK", "调试(钉钉)"), DEBUG_DING_TALK("DEBUG_DING_TALK", "调试(钉钉)"),
YUN_PIAN("YUN_PIAN", "云片"), YUN_PIAN("YUN_PIAN", "云片"),
ALIYUN("ALIYUN", "阿里云"), ALIYUN("ALIYUN", "阿里云"),
SENDCHAMP("SENDCHAMP","Sendchamp"),
YCLOUD("YCLOUD","Ycloud")
// TENCENT("TENCENT", "腾讯云"), // TENCENT("TENCENT", "腾讯云"),
// HUA_WEI("HUA_WEI", "华为云"), // HUA_WEI("HUA_WEI", "华为云"),
; ;
......
package cn.iocoder.yudao.framework.apollo.core.constants;
/**
* 缓存的key 常量
*
* @author Jayden
* @date 2024/11/6
*/
public class CacheConstants {
public static final String COLON = ":";
/**
* 短信节点表缓存
*/
public static final String SYSTEM_SMS_NODE_KEY = "system_sms_node:";
/**
* 短信节点表缓存
*/
public static final String SYSTEM_SMS_TEMPLATE_KEY = "system_sms_template:";
}
...@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.member.enums; ...@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.member.enums;
import java.util.stream.Stream; import java.util.stream.Stream;
public enum TransportTypeEnum { public enum TransportTypeEnum {
OTHER(0, "其他"),
OCEAN_LCL(1, "海运拼柜"), OCEAN_LCL(1, "海运拼柜"),
SPECIAL_LINE_AIR_FREIGHT(3, "专线空运"); SPECIAL_LINE_AIR_FREIGHT(3, "专线空运");
...@@ -22,6 +23,7 @@ public enum TransportTypeEnum { ...@@ -22,6 +23,7 @@ public enum TransportTypeEnum {
public String getName() { public String getName() {
return name; return name;
} }
public static TransportTypeEnum parseByValue(int value) { public static TransportTypeEnum parseByValue(int value) {
return Stream.of(values()).filter(e -> e.getValue() == value).findAny().orElse(null); return Stream.of(values()).filter(e -> e.getValue() == value).findAny().orElse(null);
} }
......
package cn.iocoder.yudao.module.member.controller.app.auth.vo; package cn.iocoder.yudao.module.member.controller.app.auth.vo;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.framework.common.validation.Mobile;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
...@@ -36,9 +33,8 @@ public class AppAuthCheckCodeReqVO { ...@@ -36,9 +33,8 @@ public class AppAuthCheckCodeReqVO {
@Pattern(regexp = "^[0-9]+$", message = "{app.auth.captcha.pattern}") @Pattern(regexp = "^[0-9]+$", message = "{app.auth.captcha.pattern}")
private String code; private String code;
@ApiModelProperty(value = "发送场景", example = "1", notes = "对应 SmsSceneEnum 枚举") @ApiModelProperty(value = "节点")
@NotNull(message = "{app.sms.scene.not.blank}") @NotNull(message = "{app.sms.scene.not.blank}")
@InEnum(value = SmsSceneEnum.class, message = "{app.sms.scene.not.range}") private String nodeValue;
private Integer scene;
} }
package cn.iocoder.yudao.module.member.controller.app.auth.vo; package cn.iocoder.yudao.module.member.controller.app.auth.vo;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.framework.common.validation.Mobile;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
...@@ -25,12 +22,27 @@ public class AppAuthSendSmsReqVO { ...@@ -25,12 +22,27 @@ public class AppAuthSendSmsReqVO {
// @Mobile(message = "incorrect mobile number") // @Mobile(message = "incorrect mobile number")
private String mobile; private String mobile;
@ApiModelProperty(value = "发送场景(0注册1登录2修改手机3忘记密码)", example = "1", notes = "对应 SmsSceneEnum 枚举") @ApiModelProperty(value = "业务节点", required = true)
@NotNull(message = "{app.sms.scene.not.blank}") @NotNull(message = "节点不能为空")
@InEnum(value = SmsSceneEnum.class, message = "{app.sms.scene.not.range}") private String nodeValue;
private Integer scene;
@ApiModelProperty(value = "区号", required = true) @ApiModelProperty(value = "区号", required = true)
@NotNull(message = "区号不能为空") @NotNull(message = "区号不能为空")
private String areaCode; private String areaCode;
@ApiModelProperty(value = "是否匹配运输方式", required = true)
@NotNull(message = "是否匹配运输方式不能为空")
private Integer isTransport;
@ApiModelProperty(value = "运输方式", required = true)
@NotNull(message = "运输方式不能空")
private Integer transportId;
@ApiModelProperty(value = "是否多订单不能为空", required = true)
@NotNull(message = "是否多订单不能为空")
private Integer isOrders;
@ApiModelProperty(value = "发送类型不能为空", required = true)
@NotNull(message = "发送类型不能为空")
private Integer messageType;
} }
package cn.iocoder.yudao.module.member.controller.app.auth.vo; package cn.iocoder.yudao.module.member.controller.app.auth.vo;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.framework.common.validation.Mobile;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import javax.validation.constraints.Email; import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
/** /**
* @author Administrator * @author Administrator
......
...@@ -8,7 +8,7 @@ import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; ...@@ -8,7 +8,7 @@ import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO; import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; import cn.iocoder.yudao.module.system.enums.sms.SmsNodeEnum;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mapping; import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers; import org.mapstruct.factory.Mappers;
...@@ -32,8 +32,8 @@ public interface AuthConvert { ...@@ -32,8 +32,8 @@ public interface AuthConvert {
SocialUserUnbindReqDTO convert(Long userId, Integer userType, AppAuthSocialUnbindReqVO reqVO); SocialUserUnbindReqDTO convert(Long userId, Integer userType, AppAuthSocialUnbindReqVO reqVO);
SmsCodeSendReqDTO convert(AppAuthSendSmsReqVO reqVO); SmsCodeSendReqDTO convert(AppAuthSendSmsReqVO reqVO);
SmsCodeUseReqDTO convert(AppAuthResetPasswordReqVO reqVO, SmsSceneEnum scene, String usedIp); SmsCodeUseReqDTO convert(AppAuthResetPasswordReqVO reqVO, SmsNodeEnum scene, String usedIp);
SmsCodeUseReqDTO convert(AppAuthSmsLoginReqVO reqVO, Integer scene, String usedIp); SmsCodeUseReqDTO convert(AppAuthSmsLoginReqVO reqVO, String nodeValue, String usedIp);
SmsCodeUseReqDTO convert(AppAuthRegReqVO reqVO, Integer scene, String usedIp); SmsCodeUseReqDTO convert(AppAuthRegReqVO reqVO, String nodeValue, String usedIp);
} }
...@@ -2,11 +2,12 @@ package cn.iocoder.yudao.module.member.service.auth; ...@@ -2,11 +2,12 @@ package cn.iocoder.yudao.module.member.service.auth;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.spring.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.spring.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import cn.iocoder.yudao.framework.common.util.spring.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.spring.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.util.validation.PhoneUtil;
import cn.iocoder.yudao.framework.i18n.core.I18nMessage; import cn.iocoder.yudao.framework.i18n.core.I18nMessage;
import cn.iocoder.yudao.framework.security.core.LoginUser; import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.security.core.authentication.MultiUsernamePasswordAuthenticationToken; import cn.iocoder.yudao.framework.security.core.authentication.MultiUsernamePasswordAuthenticationToken;
...@@ -31,7 +32,7 @@ import cn.iocoder.yudao.module.system.api.social.SocialUserApi; ...@@ -31,7 +32,7 @@ import cn.iocoder.yudao.module.system.api.social.SocialUserApi;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateSendReqVO; import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateSendReqVO;
import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; import cn.iocoder.yudao.module.system.enums.sms.SmsNodeEnum;
import cn.iocoder.yudao.module.system.service.mail.MailSendService; import cn.iocoder.yudao.module.system.service.mail.MailSendService;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
...@@ -113,7 +114,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { ...@@ -113,7 +114,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
@Override @Override
public String reg(@Valid AppAuthRegReqVO reqVO, String userIp, String userAgent, Boolean isAutoLogin) { public String reg(@Valid AppAuthRegReqVO reqVO, String userIp, String userAgent, Boolean isAutoLogin) {
// 校验验证码 // 校验验证码
smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.MEMBER_REG.getScene(), userIp)); smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsNodeEnum.MEMBER_REG.getNodeValue(), userIp));
MemberUserDO userByMobile = userService.checkPhoneUnique(reqVO.getAreaCode(), reqVO.getMobile()); MemberUserDO userByMobile = userService.checkPhoneUnique(reqVO.getAreaCode(), reqVO.getMobile());
if (userByMobile != null) { if (userByMobile != null) {
...@@ -164,7 +165,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { ...@@ -164,7 +165,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
@Transactional @Transactional
public String smsLogin(AppAuthSmsLoginReqVO reqVO, String userIp, String userAgent) { public String smsLogin(AppAuthSmsLoginReqVO reqVO, String userIp, String userAgent) {
// 校验验证码 // 校验验证码
smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.MEMBER_LOGIN.getScene(), userIp)); smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsNodeEnum.MEMBER_LOGIN.getNodeValue(), userIp));
// 获得获得注册用户 // 获得获得注册用户
MemberUserDO user = userService.createUserIfAbsent(reqVO.getMobile(), reqVO.getAreaCode(), userIp, reqVO.getLoginPlatform()); MemberUserDO user = userService.createUserIfAbsent(reqVO.getMobile(), reqVO.getAreaCode(), userIp, reqVO.getLoginPlatform());
...@@ -388,7 +389,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { ...@@ -388,7 +389,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
MemberUserDO userDO = checkUserIfExists(reqVO.getMobile()); MemberUserDO userDO = checkUserIfExists(reqVO.getMobile());
// 使用验证码 // 使用验证码
smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.MEMBER_FORGET_PASSWORD, smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsNodeEnum.MEMBER_FORGET_PASSWORD,
getClientIP())); getClientIP()));
// 更新密码 // 更新密码
...@@ -430,7 +431,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { ...@@ -430,7 +431,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
MemberUserDO userDO = checkUserIfExists(reqVO.getMobile()); MemberUserDO userDO = checkUserIfExists(reqVO.getMobile());
// 使用验证码 // 使用验证码
smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.MEMBER_FORGET_PASSWORD, smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsNodeEnum.MEMBER_FORGET_PASSWORD,
getClientIP())); getClientIP()));
// 更新密码 // 更新密码
...@@ -451,10 +452,10 @@ public class MemberAuthServiceImpl implements MemberAuthService { ...@@ -451,10 +452,10 @@ public class MemberAuthServiceImpl implements MemberAuthService {
public void sendSmsCode(Long userId, AppAuthSendSmsReqVO reqVO) { public void sendSmsCode(Long userId, AppAuthSendSmsReqVO reqVO) {
String mobileCode = reqVO.getAreaCode() + StrUtil.COLON + reqVO.getMobile(); String mobileCode = reqVO.getAreaCode() + StrUtil.COLON + reqVO.getMobile();
paramValidatorApi.validatorMobile(mobileCode); paramValidatorApi.validatorMobile(mobileCode);
// String mobile = PhoneUtil.formatPhone(mobileCode); String mobile = PhoneUtil.formatPhone(mobileCode);
// if (StringUtils.isNotBlank(mobile)) { if (StringUtils.isNotBlank(mobile)) {
// reqVO.setMobile(mobile); reqVO.setMobile(mobile);
// } }
// TODO 要根据不同的场景,校验是否有用户 // TODO 要根据不同的场景,校验是否有用户
smsCodeApi.sendSmsCode(AuthConvert.INSTANCE.convert(reqVO).setCreateIp(getClientIP())); smsCodeApi.sendSmsCode(AuthConvert.INSTANCE.convert(reqVO).setCreateIp(getClientIP()));
} }
......
...@@ -40,7 +40,7 @@ import cn.iocoder.yudao.module.member.vo.userOperationLog.UserOperationLogCreate ...@@ -40,7 +40,7 @@ import cn.iocoder.yudao.module.member.vo.userOperationLog.UserOperationLogCreate
import cn.iocoder.yudao.module.product.service.coupon.CouponService; import cn.iocoder.yudao.module.product.service.coupon.CouponService;
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; import cn.iocoder.yudao.module.system.enums.sms.SmsNodeEnum;
import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.util.CollectionUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
...@@ -333,7 +333,7 @@ public class MemberUserServiceImpl implements MemberUserService { ...@@ -333,7 +333,7 @@ public class MemberUserServiceImpl implements MemberUserService {
.setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP()));*/ .setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP()));*/
// 使用新验证码 // 使用新验证码
smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(reqVO.getMobile()).setCode(reqVO.getCode()) smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(reqVO.getMobile()).setCode(reqVO.getCode())
.setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP())); .setNodeValue(SmsNodeEnum.MEMBER_UPDATE_MOBILE.getNodeValue()).setUsedIp(getClientIP()));
// 更新用户手机 // 更新用户手机
memberUserMapper.updateById(MemberUserDO.builder().id(userId).mobile(reqVO.getMobile()).areaCode(reqVO.getAreaCode()).build()); memberUserMapper.updateById(MemberUserDO.builder().id(userId).mobile(reqVO.getMobile()).areaCode(reqVO.getAreaCode()).build());
......
...@@ -42,7 +42,7 @@ import cn.iocoder.yudao.module.system.api.dict.DictDataApi; ...@@ -42,7 +42,7 @@ import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; import cn.iocoder.yudao.module.system.enums.sms.SmsNodeEnum;
import cn.iocoder.yudao.module.system.framework.ue.UeProperties; import cn.iocoder.yudao.module.system.framework.ue.UeProperties;
import com.alibaba.excel.EasyExcel; import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.ExcelWriter;
...@@ -154,7 +154,7 @@ public class OrderCargoControlServiceImpl extends AbstractService<OrderCargoCont ...@@ -154,7 +154,7 @@ public class OrderCargoControlServiceImpl extends AbstractService<OrderCargoCont
if (StringUtils.isNotBlank(createReqVO.getCode()) && (StringUtils.isBlank(createReqVO.getControlPassword()) || isPasswordError || isPasswordNull)) { if (StringUtils.isNotBlank(createReqVO.getCode()) && (StringUtils.isBlank(createReqVO.getControlPassword()) || isPasswordError || isPasswordNull)) {
SmsCodeUseReqDTO smsCodeSendReqDTO = new SmsCodeUseReqDTO(); SmsCodeUseReqDTO smsCodeSendReqDTO = new SmsCodeUseReqDTO();
smsCodeSendReqDTO.setUsedIp(getClientIP()); smsCodeSendReqDTO.setUsedIp(getClientIP());
smsCodeSendReqDTO.setScene(SmsSceneEnum.TRANSFER_CONTROL_GOODS.getScene()); smsCodeSendReqDTO.setNodeValue(SmsNodeEnum.TRANSFER_CONTROL_GOODS.getNodeValue());
smsCodeSendReqDTO.setCode(String.valueOf(createReqVO.getCode())); smsCodeSendReqDTO.setCode(String.valueOf(createReqVO.getCode()));
smsCodeSendReqDTO.setMobile(orgOrderCargoControlDO.getPhone()); smsCodeSendReqDTO.setMobile(orgOrderCargoControlDO.getPhone());
smsCodeApi.useSmsCode(smsCodeSendReqDTO); smsCodeApi.useSmsCode(smsCodeSendReqDTO);
...@@ -294,7 +294,12 @@ public class OrderCargoControlServiceImpl extends AbstractService<OrderCargoCont ...@@ -294,7 +294,12 @@ public class OrderCargoControlServiceImpl extends AbstractService<OrderCargoCont
public void sendSmsCode(Long loginUserId, OrderSendSmsReqVO reqVO, MemberUserDO memberUserDO) { public void sendSmsCode(Long loginUserId, OrderSendSmsReqVO reqVO, MemberUserDO memberUserDO) {
SmsCodeSendReqDTO smsCodeSendReqDTO = new SmsCodeSendReqDTO(); SmsCodeSendReqDTO smsCodeSendReqDTO = new SmsCodeSendReqDTO();
smsCodeSendReqDTO.setCreateIp(getClientIP()); smsCodeSendReqDTO.setCreateIp(getClientIP());
smsCodeSendReqDTO.setScene(reqVO.getScene()); smsCodeSendReqDTO.setNodeValue(reqVO.getNodeValue());
smsCodeSendReqDTO.setIsTransport(reqVO.getIsTransport());
smsCodeSendReqDTO.setTransportId(reqVO.getTransportId());
smsCodeSendReqDTO.setIsOrders(reqVO.getIsOrders());
smsCodeSendReqDTO.setMessageType(reqVO.getMessageType());
OrderCargoControlDO orderCargoControlDO = this.getOne(new LambdaQueryWrapper<OrderCargoControlDO>() OrderCargoControlDO orderCargoControlDO = this.getOne(new LambdaQueryWrapper<OrderCargoControlDO>()
.eq(OrderCargoControlDO::getOrderId, reqVO.getOrderId()) .eq(OrderCargoControlDO::getOrderId, reqVO.getOrderId())
.eq(OrderCargoControlDO::getIsActual, Boolean.TRUE) .eq(OrderCargoControlDO::getIsActual, Boolean.TRUE)
......
package cn.iocoder.yudao.module.order.vo.order; package cn.iocoder.yudao.module.order.vo.order;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
...@@ -16,9 +14,21 @@ public class OrderSendSmsReqVO { ...@@ -16,9 +14,21 @@ public class OrderSendSmsReqVO {
@NotNull(message = "{order.id.not.null}") @NotNull(message = "{order.id.not.null}")
private Long orderId; private Long orderId;
@ApiModelProperty(value = "发送场景(4 控货全转移 5 放货验证短信 6 放货成功通知)", example = "1", notes = "对应 SmsSceneEnum 枚举", required = true) /**
@NotNull(message = "{app.sms.scene.not.blank}") * 发送场景
@InEnum(value = SmsSceneEnum.class, message = "{app.sms.scene.not.range}") */
private Integer scene; @NotNull(message = "业务节点不能为空")
private String nodeValue;
@NotNull(message = "是否匹配运输方式不能为空")
private Integer isTransport;
@NotNull(message = "运输方式不能空")
private Integer transportId;
@NotNull(message = "是否多订单不能为空")
private Integer isOrders;
@NotNull(message = "发送类型不能为空")
private Integer messageType;
} }
...@@ -57,7 +57,6 @@ public interface RedeemRewardApi { ...@@ -57,7 +57,6 @@ public interface RedeemRewardApi {
/** /**
* 兑换礼品发送验证码 * 兑换礼品发送验证码
* @param memberId
*/ */
void sendSmsCode(Long memberId); void sendSmsCode(RedeemRewardRespDTO reqVO);
} }
...@@ -4,6 +4,8 @@ import io.swagger.annotations.ApiModel; ...@@ -4,6 +4,8 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import javax.validation.constraints.NotNull;
@Data @Data
@ApiModel("管理后台 - 礼品兑换 Response VO") @ApiModel("管理后台 - 礼品兑换 Response VO")
public class RedeemRewardRespDTO { public class RedeemRewardRespDTO {
...@@ -14,4 +16,23 @@ public class RedeemRewardRespDTO { ...@@ -14,4 +16,23 @@ public class RedeemRewardRespDTO {
private String msgZh; private String msgZh;
@ApiModelProperty(value = "兑换操作详情英文") @ApiModelProperty(value = "兑换操作详情英文")
private String msgEn; private String msgEn;
@ApiModelProperty(value = "会员id")
@NotNull(message = "会员id不能为空")
private Long memberId;
@NotNull(message = "业务节点不能为空")
private String nodeValue;
@NotNull(message = "是否匹配运输方式不能为空")
private Integer isTransport;
@NotNull(message = "运输方式不能空")
private Integer transportId;
@NotNull(message = "是否多订单不能为空")
private Integer isOrders;
@NotNull(message = "发送类型不能为空")
private Integer messageType;
} }
...@@ -25,7 +25,7 @@ import cn.iocoder.yudao.module.reward.util.RewardGenCodeUtils; ...@@ -25,7 +25,7 @@ import cn.iocoder.yudao.module.reward.util.RewardGenCodeUtils;
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; import cn.iocoder.yudao.module.system.enums.sms.SmsNodeEnum;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
...@@ -86,7 +86,7 @@ public class RedeemRewardApiImpl implements RedeemRewardApi { ...@@ -86,7 +86,7 @@ public class RedeemRewardApiImpl implements RedeemRewardApi {
smsCodeUseReqDTO.setMobile(memberUser.getMobile()); smsCodeUseReqDTO.setMobile(memberUser.getMobile());
smsCodeUseReqDTO.setCode(appRedeemRewardReqVO.getCode()); smsCodeUseReqDTO.setCode(appRedeemRewardReqVO.getCode());
smsCodeUseReqDTO.setUsedIp(getClientIP()); smsCodeUseReqDTO.setUsedIp(getClientIP());
smsCodeUseReqDTO.setScene(SmsSceneEnum.MEMBER_REDEEM_REWARD.getScene()); smsCodeUseReqDTO.setNodeValue(SmsNodeEnum.MEMBER_REDEEM_REWARD.getNodeValue());
smsCodeApi.useSmsCode(smsCodeUseReqDTO); smsCodeApi.useSmsCode(smsCodeUseReqDTO);
} }
// 校验收货人信息 // 校验收货人信息
...@@ -282,7 +282,8 @@ public class RedeemRewardApiImpl implements RedeemRewardApi { ...@@ -282,7 +282,8 @@ public class RedeemRewardApiImpl implements RedeemRewardApi {
} }
@Override @Override
public void sendSmsCode(Long memberId) { public void sendSmsCode(RedeemRewardRespDTO reqVO) {
Long memberId = reqVO.getMemberId();
UserRespDTO memberUserApiUser = memberUserApi.getUser(memberId); UserRespDTO memberUserApiUser = memberUserApi.getUser(memberId);
if (memberUserApiUser == null) { if (memberUserApiUser == null) {
throw exception(USER_NOT_EXISTS); throw exception(USER_NOT_EXISTS);
...@@ -291,8 +292,12 @@ public class RedeemRewardApiImpl implements RedeemRewardApi { ...@@ -291,8 +292,12 @@ public class RedeemRewardApiImpl implements RedeemRewardApi {
smsCodeSendReqDTO.setMobile(memberUserApiUser.getMobile()); smsCodeSendReqDTO.setMobile(memberUserApiUser.getMobile());
smsCodeSendReqDTO.setAreaCode(memberUserApiUser.getAreaCode()); smsCodeSendReqDTO.setAreaCode(memberUserApiUser.getAreaCode());
smsCodeSendReqDTO.setCreateIp(getClientIP()); smsCodeSendReqDTO.setCreateIp(getClientIP());
smsCodeSendReqDTO.setScene(SmsSceneEnum.MEMBER_REDEEM_REWARD.getScene());
smsCodeSendReqDTO.setNodeValue(reqVO.getNodeValue());
smsCodeSendReqDTO.setIsTransport(reqVO.getIsTransport());
smsCodeSendReqDTO.setTransportId(reqVO.getTransportId());
smsCodeSendReqDTO.setIsOrders(reqVO.getIsOrders());
smsCodeSendReqDTO.setMessageType(reqVO.getMessageType());
smsCodeApi.sendSmsCode(smsCodeSendReqDTO); smsCodeApi.sendSmsCode(smsCodeSendReqDTO);
} }
......
...@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils; ...@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
import cn.iocoder.yudao.framework.idempotent.core.annotation.Idempotent; import cn.iocoder.yudao.framework.idempotent.core.annotation.Idempotent;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.reward.api.reward.RedeemRewardApi; import cn.iocoder.yudao.module.reward.api.reward.RedeemRewardApi;
import cn.iocoder.yudao.module.reward.api.reward.dto.RedeemRewardRespDTO;
import cn.iocoder.yudao.module.reward.controller.app.redeem.vo.*; import cn.iocoder.yudao.module.reward.controller.app.redeem.vo.*;
import cn.iocoder.yudao.module.reward.service.redeem.RewardRedeemService; import cn.iocoder.yudao.module.reward.service.redeem.RewardRedeemService;
import cn.iocoder.yudao.module.reward.vo.reward.RewardRedeemPageRespVO; import cn.iocoder.yudao.module.reward.vo.reward.RewardRedeemPageRespVO;
...@@ -74,8 +75,8 @@ public class AppRedeemRewardController { ...@@ -74,8 +75,8 @@ public class AppRedeemRewardController {
@ApiOperation(value = "兑换礼品发送手机验证码") @ApiOperation(value = "兑换礼品发送手机验证码")
@OperateLog(enable = false) // 避免 Post 请求被记录操作日志 @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
@Idempotent(timeout = 5) @Idempotent(timeout = 5)
public CommonResult<Boolean> sendSmsCode(@RequestBody @Valid AppRewardRedeemReqVO reqVO) { public CommonResult<Boolean> sendSmsCode(@RequestBody @Valid RedeemRewardRespDTO reqVO) {
redeemRewardApi.sendSmsCode(reqVO.getMemberId()); redeemRewardApi.sendSmsCode(reqVO);
return success(true); return success(true);
} }
} }
package cn.iocoder.yudao.module.shipment.dal.dataobject; package cn.iocoder.yudao.module.shipment.dal.dataobject;
import lombok.*;
import java.util.*;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/** /**
* 出货短信记录 DO * 出货短信记录 DO
...@@ -20,7 +20,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; ...@@ -20,7 +20,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
public class BoxOrderSmsLogDO extends BaseDO { public class BoxOrderSmsLogDO extends BaseDO {
/** /**
* *
*/ */
@TableId @TableId
private Long id; private Long id;
...@@ -47,7 +47,7 @@ public class BoxOrderSmsLogDO extends BaseDO { ...@@ -47,7 +47,7 @@ public class BoxOrderSmsLogDO extends BaseDO {
/** /**
* 场景编码 * 场景编码
*/ */
private Integer scene; private String nodeValue;
/** /**
* 说明 * 说明
*/ */
......
package cn.iocoder.yudao.module.shipment.dal.mysql; package cn.iocoder.yudao.module.shipment.dal.mysql;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQuery;
import cn.iocoder.yudao.framework.mybatis.core.mapper.AbstractMapper; import cn.iocoder.yudao.framework.mybatis.core.mapper.AbstractMapper;
import cn.iocoder.yudao.module.shipment.dal.dataobject.BoxOrderSmsLogDO; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQuery;
import cn.iocoder.yudao.framework.mybatis.core.vo.PageVO; import cn.iocoder.yudao.framework.mybatis.core.vo.PageVO;
import cn.iocoder.yudao.module.shipment.dal.dataobject.BoxOrderSmsLogDO;
import cn.iocoder.yudao.module.shipment.vo.boxOrderSmsLog.BoxOrderSmsLogQueryVO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.shipment.vo.boxOrderSmsLog.*;
import java.util.List;
/** /**
* 出货短信记录 Mapper * 出货短信记录 Mapper
...@@ -25,7 +26,7 @@ public interface BoxOrderSmsLogMapper extends AbstractMapper<BoxOrderSmsLogDO> { ...@@ -25,7 +26,7 @@ public interface BoxOrderSmsLogMapper extends AbstractMapper<BoxOrderSmsLogDO> {
.eqIfPresent(BoxOrderSmsLogDO::getOrderNo, vo.getOrderNo()) .eqIfPresent(BoxOrderSmsLogDO::getOrderNo, vo.getOrderNo())
.eqIfPresent(BoxOrderSmsLogDO::getMarks, vo.getMarks()) .eqIfPresent(BoxOrderSmsLogDO::getMarks, vo.getMarks())
.eqIfPresent(BoxOrderSmsLogDO::getSelfNo, vo.getSelfNo()) .eqIfPresent(BoxOrderSmsLogDO::getSelfNo, vo.getSelfNo())
.eqIfPresent(BoxOrderSmsLogDO::getScene, vo.getScene()) .eqIfPresent(BoxOrderSmsLogDO::getNodeValue, vo.getNodeValue())
.eqIfPresent(BoxOrderSmsLogDO::getTemplateCode, vo.getTemplateCode()) .eqIfPresent(BoxOrderSmsLogDO::getTemplateCode, vo.getTemplateCode())
.eqIfPresent(BoxOrderSmsLogDO::getDescription, vo.getDescription()) .eqIfPresent(BoxOrderSmsLogDO::getDescription, vo.getDescription())
.betweenIfPresent(BoxOrderSmsLogDO::getCreateTime, vo.getBeginCreateTime(), vo.getEndCreateTime()) .betweenIfPresent(BoxOrderSmsLogDO::getCreateTime, vo.getBeginCreateTime(), vo.getEndCreateTime())
...@@ -45,7 +46,7 @@ public interface BoxOrderSmsLogMapper extends AbstractMapper<BoxOrderSmsLogDO> { ...@@ -45,7 +46,7 @@ public interface BoxOrderSmsLogMapper extends AbstractMapper<BoxOrderSmsLogDO> {
.eqIfPresent(BoxOrderSmsLogDO::getOrderNo, vo.getOrderNo()) .eqIfPresent(BoxOrderSmsLogDO::getOrderNo, vo.getOrderNo())
.eqIfPresent(BoxOrderSmsLogDO::getMarks, vo.getMarks()) .eqIfPresent(BoxOrderSmsLogDO::getMarks, vo.getMarks())
.eqIfPresent(BoxOrderSmsLogDO::getSelfNo, vo.getSelfNo()) .eqIfPresent(BoxOrderSmsLogDO::getSelfNo, vo.getSelfNo())
.eqIfPresent(BoxOrderSmsLogDO::getScene, vo.getScene()) .eqIfPresent(BoxOrderSmsLogDO::getNodeValue, vo.getNodeValue())
.eqIfPresent(BoxOrderSmsLogDO::getTemplateCode, vo.getTemplateCode()) .eqIfPresent(BoxOrderSmsLogDO::getTemplateCode, vo.getTemplateCode())
.eqIfPresent(BoxOrderSmsLogDO::getDescription, vo.getDescription()) .eqIfPresent(BoxOrderSmsLogDO::getDescription, vo.getDescription())
.betweenIfPresent(BoxOrderSmsLogDO::getCreateTime, vo.getBeginCreateTime(), vo.getEndCreateTime()) .betweenIfPresent(BoxOrderSmsLogDO::getCreateTime, vo.getBeginCreateTime(), vo.getEndCreateTime())
......
...@@ -35,7 +35,7 @@ import cn.iocoder.yudao.module.shipment.vo.boxPreloadSection.BoxLoadSectionBackV ...@@ -35,7 +35,7 @@ import cn.iocoder.yudao.module.shipment.vo.boxPreloadSection.BoxLoadSectionBackV
import cn.iocoder.yudao.module.shipment.vo.boxTally.BoxGuanlianOrderBackVO; import cn.iocoder.yudao.module.shipment.vo.boxTally.BoxGuanlianOrderBackVO;
import cn.iocoder.yudao.module.shipment.vo.boxTally.BoxOrderLocationUpdateReq; import cn.iocoder.yudao.module.shipment.vo.boxTally.BoxOrderLocationUpdateReq;
import cn.iocoder.yudao.module.shipment.vo.boxTally.BoxTallyBackVO; import cn.iocoder.yudao.module.shipment.vo.boxTally.BoxTallyBackVO;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; import cn.iocoder.yudao.module.system.enums.sms.SmsNodeEnum;
import org.springframework.context.annotation.Description; import org.springframework.context.annotation.Description;
import javax.validation.Valid; import javax.validation.Valid;
...@@ -141,11 +141,11 @@ public interface BoxService extends IService<BoxDO> { ...@@ -141,11 +141,11 @@ public interface BoxService extends IService<BoxDO> {
* 发送短信 * 发送短信
* *
* @param shipment 出货单 * @param shipment 出货单
* @param smsSceneEnum 场景 * @param smsNodeEnum 场景
*/ */
void sendSms(Long shipment, SmsSceneEnum smsSceneEnum); void sendSms(Long shipment, SmsNodeEnum smsNodeEnum);
void sendSms(Long shipmentId, Collection<Long> orderIdList, SmsSceneEnum smsSceneEnum); void sendSms(Long shipmentId, Collection<Long> orderIdList, SmsNodeEnum smsNodeEnum);
/** /**
* 新增销售日志 * 新增销售日志
...@@ -809,4 +809,4 @@ public interface BoxService extends IService<BoxDO> { ...@@ -809,4 +809,4 @@ public interface BoxService extends IService<BoxDO> {
* @param shipmentId 出货单ID * @param shipmentId 出货单ID
*/ */
void clearUnloadCabinet(Long shipmentId); void clearUnloadCabinet(Long shipmentId);
} }
\ No newline at end of file
...@@ -33,7 +33,7 @@ import cn.iocoder.yudao.module.shipment.vo.boxAirCheckout.BoxAirCheckoutCreateRe ...@@ -33,7 +33,7 @@ import cn.iocoder.yudao.module.shipment.vo.boxAirCheckout.BoxAirCheckoutCreateRe
import cn.iocoder.yudao.module.shipment.vo.boxAirCheckout.BoxAirCheckoutPDAVO; import cn.iocoder.yudao.module.shipment.vo.boxAirCheckout.BoxAirCheckoutPDAVO;
import cn.iocoder.yudao.module.shipment.vo.boxAirCheckout.BoxAirCheckoutQueryVO; import cn.iocoder.yudao.module.shipment.vo.boxAirCheckout.BoxAirCheckoutQueryVO;
import cn.iocoder.yudao.module.shipment.vo.boxAirCheckout.BoxAirCheckoutUpdateReqVO; import cn.iocoder.yudao.module.shipment.vo.boxAirCheckout.BoxAirCheckoutUpdateReqVO;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; import cn.iocoder.yudao.module.system.enums.sms.SmsNodeEnum;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
...@@ -74,7 +74,7 @@ public class BoxAirCheckoutServiceImpl extends AbstractService<BoxAirCheckoutMap ...@@ -74,7 +74,7 @@ public class BoxAirCheckoutServiceImpl extends AbstractService<BoxAirCheckoutMap
@Resource @Resource
private WarehouseService warehouseService; private WarehouseService warehouseService;
@Resource @Resource
@Lazy @Lazy
private BoxPreloadGoodsService boxPreloadGoodsService; private BoxPreloadGoodsService boxPreloadGoodsService;
...@@ -168,7 +168,7 @@ public class BoxAirCheckoutServiceImpl extends AbstractService<BoxAirCheckoutMap ...@@ -168,7 +168,7 @@ public class BoxAirCheckoutServiceImpl extends AbstractService<BoxAirCheckoutMap
//出仓添加业绩日志 //出仓添加业绩日志
boxService.addTargetLog(shipmentId, TargetLogEnum.LOADING,checkoutTime); boxService.addTargetLog(shipmentId, TargetLogEnum.LOADING,checkoutTime);
// 出仓短信通知 // 出仓短信通知
boxService.sendSms(shipmentId, SmsSceneEnum.AIR_SHIPMENT_WAREHOUSE); boxService.sendSms(shipmentId, SmsNodeEnum.AIR_SHIPMENT_WAREHOUSE);
//插入操作日志 //插入操作日志
boxOpLogService.createBoxOpLog(boxDO.getId(), checkout.getStatus(), checkout.getText(), checkout.getTextEn(), null); boxOpLogService.createBoxOpLog(boxDO.getId(), checkout.getStatus(), checkout.getText(), checkout.getTextEn(), null);
} }
......
package cn.iocoder.yudao.module.shipment.service.boxArrival; package cn.iocoder.yudao.module.shipment.service.boxArrival;
import java.util.*; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import javax.annotation.Resource; import cn.iocoder.yudao.framework.mybatis.core.service.AbstractService;
import cn.iocoder.yudao.framework.mybatis.core.vo.PageVO;
import cn.iocoder.yudao.module.order.enums.OrderStatusEnum; import cn.iocoder.yudao.module.order.enums.OrderStatusEnum;
import cn.iocoder.yudao.module.shipment.convert.BoxArrivalConvert; import cn.iocoder.yudao.module.shipment.convert.BoxArrivalConvert;
import cn.iocoder.yudao.module.shipment.dal.dataobject.BoxArrivalDO; import cn.iocoder.yudao.module.shipment.dal.dataobject.BoxArrivalDO;
...@@ -16,15 +16,17 @@ import cn.iocoder.yudao.module.shipment.service.boxOpLog.BoxOpLogService; ...@@ -16,15 +16,17 @@ import cn.iocoder.yudao.module.shipment.service.boxOpLog.BoxOpLogService;
import cn.iocoder.yudao.module.shipment.vo.boxArrival.BoxArrivalCreateReqVO; import cn.iocoder.yudao.module.shipment.vo.boxArrival.BoxArrivalCreateReqVO;
import cn.iocoder.yudao.module.shipment.vo.boxArrival.BoxArrivalQueryVO; import cn.iocoder.yudao.module.shipment.vo.boxArrival.BoxArrivalQueryVO;
import cn.iocoder.yudao.module.shipment.vo.boxArrival.BoxArrivalUpdateReqVO; import cn.iocoder.yudao.module.shipment.vo.boxArrival.BoxArrivalUpdateReqVO;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; import cn.iocoder.yudao.module.system.enums.sms.SmsNodeEnum;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import cn.iocoder.yudao.framework.mybatis.core.vo.PageVO;
import cn.iocoder.yudao.framework.mybatis.core.service.AbstractService; import javax.annotation.Resource;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import java.util.Collection;
import java.util.Date;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.shipment.enums.ErrorCodeConstants.BOX_NOT_EXISTS; import static cn.iocoder.yudao.module.shipment.enums.ErrorCodeConstants.BOX_NOT_EXISTS;
...@@ -101,11 +103,7 @@ public class BoxArrivalServiceImpl extends AbstractService<BoxArrivalMapper, Box ...@@ -101,11 +103,7 @@ public class BoxArrivalServiceImpl extends AbstractService<BoxArrivalMapper, Box
//确认到港 //确认到港
//发送短信通知 //发送短信通知
if (StringUtils.equals("3", boxDO.getTransportType())) { boxService.sendSms(shipmentId, SmsNodeEnum.SHIPMENT_ARRIVAL);
boxService.sendSms(shipmentId, SmsSceneEnum.AIR_SHIPMENT_ARRIVAL);
} else {
boxService.sendSms(shipmentId, SmsSceneEnum.SHIPMENT_ARRIVAL);
}
//修改订单状态为已到港 //修改订单状态为已到港
......
package cn.iocoder.yudao.module.shipment.service.boxArrivalAir; package cn.iocoder.yudao.module.shipment.service.boxArrivalAir;
import java.util.*;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.service.AbstractService;
import cn.iocoder.yudao.framework.mybatis.core.vo.PageVO;
import cn.iocoder.yudao.module.order.dal.dataobject.order.OrderDO; import cn.iocoder.yudao.module.order.dal.dataobject.order.OrderDO;
import cn.iocoder.yudao.module.order.enums.OrderStatusEnum; import cn.iocoder.yudao.module.order.enums.OrderStatusEnum;
import cn.iocoder.yudao.module.order.service.order.OrderQueryService; import cn.iocoder.yudao.module.order.service.order.OrderQueryService;
...@@ -13,7 +12,6 @@ import cn.iocoder.yudao.module.shipment.convert.BoxArrivalAirConvert; ...@@ -13,7 +12,6 @@ import cn.iocoder.yudao.module.shipment.convert.BoxArrivalAirConvert;
import cn.iocoder.yudao.module.shipment.dal.dataobject.BoxArrivalAirDO; import cn.iocoder.yudao.module.shipment.dal.dataobject.BoxArrivalAirDO;
import cn.iocoder.yudao.module.shipment.dal.dataobject.BoxDO; import cn.iocoder.yudao.module.shipment.dal.dataobject.BoxDO;
import cn.iocoder.yudao.module.shipment.dal.dataobject.boxArrivalOrder.BoxArrivalOrderDO; import cn.iocoder.yudao.module.shipment.dal.dataobject.boxArrivalOrder.BoxArrivalOrderDO;
import cn.iocoder.yudao.module.shipment.dal.dataobject.boxClearanceOrder.BoxClearanceOrderDO;
import cn.iocoder.yudao.module.shipment.dal.mysql.BoxArrivalAirMapper; import cn.iocoder.yudao.module.shipment.dal.mysql.BoxArrivalAirMapper;
import cn.iocoder.yudao.module.shipment.enums.*; import cn.iocoder.yudao.module.shipment.enums.*;
import cn.iocoder.yudao.module.shipment.service.box.BoxService; import cn.iocoder.yudao.module.shipment.service.box.BoxService;
...@@ -25,6 +23,7 @@ import cn.iocoder.yudao.module.shipment.vo.boxArrivalAir.BoxArrivalAirQueryVO; ...@@ -25,6 +23,7 @@ import cn.iocoder.yudao.module.shipment.vo.boxArrivalAir.BoxArrivalAirQueryVO;
import cn.iocoder.yudao.module.shipment.vo.boxArrivalAir.BoxArrivalAirUpdateReqVO; import cn.iocoder.yudao.module.shipment.vo.boxArrivalAir.BoxArrivalAirUpdateReqVO;
import cn.iocoder.yudao.module.shipment.vo.boxArrivalOrder.BoxArrivalOrderAllCreateVO; import cn.iocoder.yudao.module.shipment.vo.boxArrivalOrder.BoxArrivalOrderAllCreateVO;
import cn.iocoder.yudao.module.shipment.vo.boxArrivalOrder.BoxArrivalOrderCreateVO; import cn.iocoder.yudao.module.shipment.vo.boxArrivalOrder.BoxArrivalOrderCreateVO;
import cn.iocoder.yudao.module.system.enums.sms.SmsNodeEnum;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
...@@ -32,9 +31,10 @@ import org.springframework.context.annotation.Lazy; ...@@ -32,9 +31,10 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import cn.iocoder.yudao.framework.mybatis.core.vo.PageVO;
import cn.iocoder.yudao.framework.mybatis.core.service.AbstractService; import javax.annotation.Resource;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import java.util.*;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.shipment.enums.ErrorCodeConstants.BOX_NOT_EXISTS; import static cn.iocoder.yudao.module.shipment.enums.ErrorCodeConstants.BOX_NOT_EXISTS;
...@@ -152,6 +152,8 @@ public class BoxArrivalAirServiceImpl extends AbstractService<BoxArrivalAirMappe ...@@ -152,6 +152,8 @@ public class BoxArrivalAirServiceImpl extends AbstractService<BoxArrivalAirMappe
.eq(BoxArrivalOrderDO::getShipmentId, shipmentId) .eq(BoxArrivalOrderDO::getShipmentId, shipmentId)
); );
if (arrivalCount == arrivalOrders.size()) { if (arrivalCount == arrivalOrders.size()) {
//发送短信通知
boxService.sendSms(shipmentId, SmsNodeEnum.SHIPMENT_ARRIVAL);
SapStatueEnum sapStatueEnum = SapStatueEnum.HAS_ARRIVE; SapStatueEnum sapStatueEnum = SapStatueEnum.HAS_ARRIVE;
boxDO.setSapStatus(sapStatueEnum.getSapStatus()); boxDO.setSapStatus(sapStatueEnum.getSapStatus());
boxDO.setDgDate(actTime); boxDO.setDgDate(actTime);
......
...@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.shipment.service.boxCabinet; ...@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.shipment.service.boxCabinet;
import java.util.*; import java.util.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import cn.hutool.core.collection.CollectionUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.order.enums.TargetLogEnum; import cn.iocoder.yudao.module.order.enums.TargetLogEnum;
...@@ -11,7 +10,6 @@ import cn.iocoder.yudao.module.shipment.convert.BoxCabinetConvert; ...@@ -11,7 +10,6 @@ import cn.iocoder.yudao.module.shipment.convert.BoxCabinetConvert;
import cn.iocoder.yudao.module.shipment.dal.dataobject.BoxBookSeaDO; import cn.iocoder.yudao.module.shipment.dal.dataobject.BoxBookSeaDO;
import cn.iocoder.yudao.module.shipment.dal.dataobject.BoxCabinetDO; import cn.iocoder.yudao.module.shipment.dal.dataobject.BoxCabinetDO;
import cn.iocoder.yudao.module.shipment.dal.dataobject.BoxDO; import cn.iocoder.yudao.module.shipment.dal.dataobject.BoxDO;
import cn.iocoder.yudao.module.shipment.dal.dataobject.BoxPreloadGoodsDO;
import cn.iocoder.yudao.module.shipment.dal.mysql.BoxBookSeaMapper; import cn.iocoder.yudao.module.shipment.dal.mysql.BoxBookSeaMapper;
import cn.iocoder.yudao.module.shipment.dal.mysql.BoxCabinetMapper; import cn.iocoder.yudao.module.shipment.dal.mysql.BoxCabinetMapper;
import cn.iocoder.yudao.module.shipment.enums.*; import cn.iocoder.yudao.module.shipment.enums.*;
...@@ -20,8 +18,7 @@ import cn.iocoder.yudao.module.shipment.service.boxOpLog.BoxOpLogService; ...@@ -20,8 +18,7 @@ import cn.iocoder.yudao.module.shipment.service.boxOpLog.BoxOpLogService;
import cn.iocoder.yudao.module.shipment.vo.boxCabinet.BoxCabinetCreateReqVO; import cn.iocoder.yudao.module.shipment.vo.boxCabinet.BoxCabinetCreateReqVO;
import cn.iocoder.yudao.module.shipment.vo.boxCabinet.BoxCabinetQueryVO; import cn.iocoder.yudao.module.shipment.vo.boxCabinet.BoxCabinetQueryVO;
import cn.iocoder.yudao.module.shipment.vo.boxCabinet.BoxCabinetUpdateReqVO; import cn.iocoder.yudao.module.shipment.vo.boxCabinet.BoxCabinetUpdateReqVO;
import cn.iocoder.yudao.module.shipment.vo.boxPreloadGoods.BoxPreloadGoodsQueryVO; import cn.iocoder.yudao.module.system.enums.sms.SmsNodeEnum;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
...@@ -166,7 +163,7 @@ public class BoxCabinetServiceImpl extends AbstractService<BoxCabinetMapper, Box ...@@ -166,7 +163,7 @@ public class BoxCabinetServiceImpl extends AbstractService<BoxCabinetMapper, Box
boxService.addTargetLog(shipmentId, TargetLogEnum.LOADING, ldBoxTime); boxService.addTargetLog(shipmentId, TargetLogEnum.LOADING, ldBoxTime);
//发送短信 //发送短信
try { try {
boxService.sendSms(shipmentId, SmsSceneEnum.SHIPMENT_CABINET); boxService.sendSms(shipmentId, SmsNodeEnum.SHIPMENT_CABINET);
} catch (Exception e) { } catch (Exception e) {
log.error(e.getMessage()); log.error(e.getMessage());
} }
......
package cn.iocoder.yudao.module.shipment.service.boxClearance; package cn.iocoder.yudao.module.shipment.service.boxClearance;
import java.util.*;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.CollectionUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.service.AbstractService;
import cn.iocoder.yudao.framework.mybatis.core.vo.PageVO;
import cn.iocoder.yudao.module.bpm.enums.BpmProcessInstanceResultEnum; import cn.iocoder.yudao.module.bpm.enums.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.module.order.dal.dataobject.order.OrderDO; import cn.iocoder.yudao.module.order.dal.dataobject.order.OrderDO;
import cn.iocoder.yudao.module.order.enums.OrderStatusEnum; import cn.iocoder.yudao.module.order.enums.OrderStatusEnum;
...@@ -28,7 +27,7 @@ import cn.iocoder.yudao.module.shipment.vo.boxClearance.BoxClearanceQueryVO; ...@@ -28,7 +27,7 @@ import cn.iocoder.yudao.module.shipment.vo.boxClearance.BoxClearanceQueryVO;
import cn.iocoder.yudao.module.shipment.vo.boxClearance.BoxClearanceUpdateReqVO; import cn.iocoder.yudao.module.shipment.vo.boxClearance.BoxClearanceUpdateReqVO;
import cn.iocoder.yudao.module.shipment.vo.boxClearanceOrder.BoxClearanceOrderAllCreateVO; import cn.iocoder.yudao.module.shipment.vo.boxClearanceOrder.BoxClearanceOrderAllCreateVO;
import cn.iocoder.yudao.module.shipment.vo.boxClearanceOrder.BoxClearanceOrderCreateVO; import cn.iocoder.yudao.module.shipment.vo.boxClearanceOrder.BoxClearanceOrderCreateVO;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; import cn.iocoder.yudao.module.system.enums.sms.SmsNodeEnum;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
...@@ -36,9 +35,10 @@ import org.springframework.context.annotation.Lazy; ...@@ -36,9 +35,10 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import cn.iocoder.yudao.framework.mybatis.core.vo.PageVO;
import cn.iocoder.yudao.framework.mybatis.core.service.AbstractService; import javax.annotation.Resource;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import java.util.*;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.shipment.enums.ErrorCodeConstants.BOX_NOT_EXISTS; import static cn.iocoder.yudao.module.shipment.enums.ErrorCodeConstants.BOX_NOT_EXISTS;
...@@ -118,7 +118,7 @@ public class BoxClearanceServiceImpl extends AbstractService<BoxClearanceMapper, ...@@ -118,7 +118,7 @@ public class BoxClearanceServiceImpl extends AbstractService<BoxClearanceMapper,
if (clClearTime != null) { if (clClearTime != null) {
boxOpLogService.createBoxOpLog(shipmentId, ClStatueEnum.HAS_CLEARANCE.getClStatus(), "已清关,待到仓", "", null); boxOpLogService.createBoxOpLog(shipmentId, ClStatueEnum.HAS_CLEARANCE.getClStatus(), "已清关,待到仓", "", null);
//发送清关短信 //发送清关短信
boxService.sendSms(shipmentId, SmsSceneEnum.SHIPMENT_CUSTOMS_CLEARANCE); boxService.sendSms(shipmentId, SmsNodeEnum.SHIPMENT_CUSTOMS_CLEARANCE);
} }
//修改订单状态为已清关 //修改订单状态为已清关
...@@ -274,7 +274,7 @@ public class BoxClearanceServiceImpl extends AbstractService<BoxClearanceMapper, ...@@ -274,7 +274,7 @@ public class BoxClearanceServiceImpl extends AbstractService<BoxClearanceMapper,
if (clearTime != null) { if (clearTime != null) {
boxOpLogService.createBoxOpLog(shipmentId, ClStatueEnum.HAS_CLEARANCE.getClStatus(), "已清关,待到仓", "", null); boxOpLogService.createBoxOpLog(shipmentId, ClStatueEnum.HAS_CLEARANCE.getClStatus(), "已清关,待到仓", "", null);
//发送清关短信 //发送清关短信
boxService.sendSms(shipmentId, clearanceOrders, SmsSceneEnum.AIR_SHIPMENT_CUSTOMS_CLEARANCE); boxService.sendSms(shipmentId, clearanceOrders, SmsNodeEnum.SHIPMENT_CUSTOMS_CLEARANCE);
} }
//修改订单状态为已清关 //修改订单状态为已清关
......
package cn.iocoder.yudao.module.shipment.vo.boxOrderSmsLog; package cn.iocoder.yudao.module.shipment.vo.boxOrderSmsLog;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/** /**
...@@ -40,9 +42,9 @@ public class BoxOrderSmsLogBackVO { ...@@ -40,9 +42,9 @@ public class BoxOrderSmsLogBackVO {
@ApiModelProperty(value = "自编号") @ApiModelProperty(value = "自编号")
private String selfNo; private String selfNo;
@ExcelProperty("场景编码") @ExcelProperty("节点")
@ApiModelProperty(value = "场景编码") @ApiModelProperty(value = "节点")
private Integer scene; private String nodeValue;
@ExcelProperty("说明") @ExcelProperty("说明")
@ApiModelProperty(value = "说明") @ApiModelProperty(value = "说明")
......
package cn.iocoder.yudao.module.shipment.vo.boxOrderSmsLog; package cn.iocoder.yudao.module.shipment.vo.boxOrderSmsLog;
import lombok.*; import io.swagger.annotations.ApiModelProperty;
import java.util.*; import lombok.Data;
import io.swagger.annotations.*;
import javax.validation.constraints.*;
/** /**
* 出货短信记录 Base VO,提供给添加、修改、详细的子 VO 使用 * 出货短信记录 Base VO,提供给添加、修改、详细的子 VO 使用
...@@ -27,8 +25,8 @@ public class BoxOrderSmsLogBaseVO { ...@@ -27,8 +25,8 @@ public class BoxOrderSmsLogBaseVO {
@ApiModelProperty(value = "自编号") @ApiModelProperty(value = "自编号")
private String selfNo; private String selfNo;
@ApiModelProperty(value = "场景编码") @ApiModelProperty(value = "节点")
private Integer scene; private String nodeValue;
@ApiModelProperty(value = "说明") @ApiModelProperty(value = "说明")
private String templateCode; private String templateCode;
......
package cn.iocoder.yudao.module.shipment.vo.boxOrderSmsLog; package cn.iocoder.yudao.module.shipment.vo.boxOrderSmsLog;
import lombok.*; import io.swagger.annotations.ApiModel;
import java.util.*; import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.*; import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Data @Data
...@@ -26,8 +28,8 @@ public class BoxOrderSmsLogQueryVO { ...@@ -26,8 +28,8 @@ public class BoxOrderSmsLogQueryVO {
@ApiModelProperty(value = "自编号") @ApiModelProperty(value = "自编号")
private String selfNo; private String selfNo;
@ApiModelProperty(value = "场景编码") @ApiModelProperty(value = "节点")
private Integer scene; private String nodeValue;
@ApiModelProperty(value = "说明") @ApiModelProperty(value = "说明")
private String templateCode; private String templateCode;
......
package cn.iocoder.yudao.module.shipment.controller.admin; package cn.iocoder.yudao.module.shipment.controller.admin;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.excel.util.ExcelUtils;
import cn.iocoder.yudao.framework.i18n.core.I18nMessage; import cn.iocoder.yudao.framework.i18n.core.I18nMessage;
import cn.iocoder.yudao.framework.idempotent.core.annotation.Idempotent; import cn.iocoder.yudao.framework.idempotent.core.annotation.Idempotent;
import cn.iocoder.yudao.framework.limiter.dynamic.DynamicRateLimiter; import cn.iocoder.yudao.framework.limiter.dynamic.DynamicRateLimiter;
import cn.iocoder.yudao.framework.mybatis.core.vo.PageVO; import cn.iocoder.yudao.framework.mybatis.core.vo.PageVO;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.framework.redis.helper.RedisHelper; import cn.iocoder.yudao.framework.redis.helper.RedisHelper;
import cn.iocoder.yudao.module.order.vo.order.OrderBackPageVO; import cn.iocoder.yudao.module.order.vo.order.OrderBackPageVO;
import cn.iocoder.yudao.module.order.vo.orderSplit.OrderSplitBackVO; import cn.iocoder.yudao.module.order.vo.orderSplit.OrderSplitBackVO;
import cn.iocoder.yudao.module.shipment.convert.BoxConvert; import cn.iocoder.yudao.module.shipment.convert.BoxConvert;
import cn.iocoder.yudao.module.shipment.dal.dataobject.BoxDO; import cn.iocoder.yudao.module.shipment.dal.dataobject.BoxDO;
import cn.iocoder.yudao.module.shipment.service.box.BoxService;
import cn.iocoder.yudao.module.shipment.vo.box.*;
import cn.iocoder.yudao.module.shipment.vo.boxTally.BoxGuanlianOrderBackVO; import cn.iocoder.yudao.module.shipment.vo.boxTally.BoxGuanlianOrderBackVO;
import cn.iocoder.yudao.module.shipment.vo.boxTally.BoxOrderLocationUpdateReq; import cn.iocoder.yudao.module.shipment.vo.boxTally.BoxOrderLocationUpdateReq;
import cn.iocoder.yudao.module.shipment.vo.boxTally.BoxTallyBackVO; import cn.iocoder.yudao.module.shipment.vo.boxTally.BoxTallyBackVO;
import cn.iocoder.yudao.module.system.api.file.FileMakeApi; import cn.iocoder.yudao.module.system.api.file.FileMakeApi;
import cn.iocoder.yudao.module.system.api.file.dto.FileMakeReqDTO; import cn.iocoder.yudao.module.system.api.file.dto.FileMakeReqDTO;
import cn.iocoder.yudao.module.system.enums.download.DownloadTypeEnum; import cn.iocoder.yudao.module.system.enums.download.DownloadTypeEnum;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; import cn.iocoder.yudao.module.system.enums.sms.SmsNodeEnum;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import org.springframework.validation.annotation.Validated; import javax.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize; import java.io.IOException;
import io.swagger.annotations.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.io.*;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.*; import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
import cn.iocoder.yudao.framework.excel.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
import static cn.iocoder.yudao.module.shipment.controller.admin.constant.Constant.BOX_UPDATE_KEY; import static cn.iocoder.yudao.module.shipment.controller.admin.constant.Constant.BOX_UPDATE_KEY;
import static cn.iocoder.yudao.module.shipment.enums.ErrorCodeConstants.BOX_UPDATE_REPEAT_COMMIT; import static cn.iocoder.yudao.module.shipment.enums.ErrorCodeConstants.BOX_UPDATE_REPEAT_COMMIT;
import cn.iocoder.yudao.module.shipment.vo.box.*;
import cn.iocoder.yudao.module.shipment.service.box.BoxService;
@Validated @Validated
@RestController @RestController
@Api(tags = "管理后台 - 出货") @Api(tags = "管理后台 - 出货")
...@@ -323,8 +321,8 @@ public class BoxController { ...@@ -323,8 +321,8 @@ public class BoxController {
@ApiImplicitParam(name = "shipmentId", value = "出货单ID", required = true, example = "1024", dataTypeClass = Long.class), @ApiImplicitParam(name = "shipmentId", value = "出货单ID", required = true, example = "1024", dataTypeClass = Long.class),
@ApiImplicitParam(name = "scene", value = "场景编号", required = true, example = "20", dataTypeClass = String.class) @ApiImplicitParam(name = "scene", value = "场景编号", required = true, example = "20", dataTypeClass = String.class)
}) })
public CommonResult<Boolean> shipmentSmsSend(@RequestParam("shipmentId") Long shipmentId, @RequestParam("scene") Integer scene) { public CommonResult<Boolean> shipmentSmsSend(@RequestParam("shipmentId") Long shipmentId, @RequestParam("scene") String scene) {
boxService.sendSms(shipmentId, SmsSceneEnum.getCodeByScene(scene)); boxService.sendSms(shipmentId, SmsNodeEnum.getCodeByScene(scene));
return success(true); return success(true);
} }
......
package cn.iocoder.yudao.module.system.api.sms; package cn.iocoder.yudao.module.system.api.sms;
import cn.iocoder.yudao.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTO; import cn.iocoder.yudao.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTO;
import cn.iocoder.yudao.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTOV2;
import javax.validation.Valid; import javax.validation.Valid;
...@@ -21,6 +22,16 @@ public interface SmsSendApi { ...@@ -21,6 +22,16 @@ public interface SmsSendApi {
*/ */
Long sendSingleSmsToAdmin(@Valid SmsSendSingleToUserReqDTO reqDTO); Long sendSingleSmsToAdmin(@Valid SmsSendSingleToUserReqDTO reqDTO);
/**
* 发送单条短信给 Admin 用户
*
* 在 mobile 为空时,使用 userId 加载对应 Admin 的手机号
*
* @param reqDTO 发送请求
* @return 发送日志编号
*/
Long sendSingleSmsToAdminV2(@Valid SmsSendSingleToUserReqDTOV2 reqDTO);
/** /**
* 发送单条短信给 Member 用户 * 发送单条短信给 Member 用户
* *
......
package cn.iocoder.yudao.module.system.api.sms.dto.code; package cn.iocoder.yudao.module.system.api.sms.dto.code;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.framework.common.validation.Mobile; import cn.iocoder.yudao.framework.common.validation.Mobile;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
import lombok.Data; import lombok.Data;
import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotEmpty;
...@@ -26,8 +24,7 @@ public class SmsCodeCheckReqDTO { ...@@ -26,8 +24,7 @@ public class SmsCodeCheckReqDTO {
* 发送场景 * 发送场景
*/ */
@NotNull(message = "发送场景不能为空") @NotNull(message = "发送场景不能为空")
@InEnum(SmsSceneEnum.class) private String nodeValue;
private Integer scene;
/** /**
* 验证码 * 验证码
*/ */
......
package cn.iocoder.yudao.module.system.api.sms.dto.code; package cn.iocoder.yudao.module.system.api.sms.dto.code;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.framework.common.validation.Mobile; import cn.iocoder.yudao.framework.common.validation.Mobile;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
import lombok.Data; import lombok.Data;
import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotEmpty;
...@@ -25,9 +23,8 @@ public class SmsCodeSendReqDTO { ...@@ -25,9 +23,8 @@ public class SmsCodeSendReqDTO {
/** /**
* 发送场景 * 发送场景
*/ */
@NotNull(message = "发送场景不能为空") @NotNull(message = "业务节点不能为空")
@InEnum(SmsSceneEnum.class) private String nodeValue;
private Integer scene;
/** /**
* 发送 IP * 发送 IP
*/ */
...@@ -36,4 +33,16 @@ public class SmsCodeSendReqDTO { ...@@ -36,4 +33,16 @@ public class SmsCodeSendReqDTO {
@NotNull(message = "国家区号不能为空") @NotNull(message = "国家区号不能为空")
private String areaCode; private String areaCode;
@NotNull(message = "是否匹配运输方式不能为空")
private Integer isTransport;
@NotNull(message = "运输方式不能空")
private Integer transportId;
@NotNull(message = "是否多订单不能为空")
private Integer isOrders;
@NotNull(message = "发送类型不能为空")
private Integer messageType;
} }
package cn.iocoder.yudao.module.system.api.sms.dto.code; package cn.iocoder.yudao.module.system.api.sms.dto.code;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.framework.common.validation.Mobile; import cn.iocoder.yudao.framework.common.validation.Mobile;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
import lombok.Data; import lombok.Data;
import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotEmpty;
...@@ -26,8 +24,7 @@ public class SmsCodeUseReqDTO { ...@@ -26,8 +24,7 @@ public class SmsCodeUseReqDTO {
* 发送场景 * 发送场景
*/ */
@NotNull(message = "发送场景不能为空") @NotNull(message = "发送场景不能为空")
@InEnum(SmsSceneEnum.class) private String nodeValue;
private Integer scene;
/** /**
* 验证码 * 验证码
*/ */
......
package cn.iocoder.yudao.module.system.api.sms.dto.send;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.util.Map;
/**
* 短信发送给 Admin 或者 Member 用户
*
* @author 捷道源码
*/
@Data
public class SmsSendSingleToUserReqDTOV2 {
/**
* 用户编号
*/
@NotNull(message = "用户编号不能为空")
private Long userId;
@NotNull(message = "手机号不能为空")
private String mobile;
@NotNull(message = "节点不能为空")
private String nodeValue;
@NotNull(message = "区号不能为空")
private String areaCode;
@NotNull(message = "是否匹配运输方式不能为空")
private Integer isTransport;
@NotNull(message = "运输方式不能空")
private Integer transportId;
@NotNull(message = "是否多订单不能为空")
private Integer isOrders;
@NotNull(message = "发送类型不能为空")
private Integer messageType;
/**
* 短信模板参数
*/
private Map<String, Object> templateParams;
}
...@@ -89,6 +89,9 @@ public interface ErrorCodeConstants { ...@@ -89,6 +89,9 @@ public interface ErrorCodeConstants {
ErrorCode SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS = new ErrorCode(1002013001, "模板参数({})缺失"); ErrorCode SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS = new ErrorCode(1002013001, "模板参数({})缺失");
ErrorCode SMS_SEND_TEMPLATE_NOT_EXISTS = new ErrorCode(1002013002, "短信模板不存在"); ErrorCode SMS_SEND_TEMPLATE_NOT_EXISTS = new ErrorCode(1002013002, "短信模板不存在");
// ========== 短信节点 1002013100 ==========
ErrorCode SMS_NODE_NOT_EXISTS = new ErrorCode(1002013100, "短信节点不存在");
// ========== 短信验证码 1002014000 ========== // ========== 短信验证码 1002014000 ==========
ErrorCode SMS_CODE_NOT_FOUND = new ErrorCode(1002014000, "验证码不存在"); ErrorCode SMS_CODE_NOT_FOUND = new ErrorCode(1002014000, "验证码不存在");
ErrorCode SMS_CODE_EXPIRED = new ErrorCode(1002014001, "验证码已过期"); ErrorCode SMS_CODE_EXPIRED = new ErrorCode(1002014001, "验证码已过期");
......
package cn.iocoder.yudao.module.system.enums.sms;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.util.json.core.IntArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 国家编号枚举
*
* @author jayden
*/
@Getter
@AllArgsConstructor
public enum SmsCountryCodeEnum {
SMS_COUNTRY_CODE_0("全部", "0"),
SMS_COUNTRY_CODE_1("其他", "1");
private String name;
private String value;
}
package cn.iocoder.yudao.module.system.enums.sms;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 是否多订单枚举
*
* @author jayden
*/
@Getter
@AllArgsConstructor
public enum SmsIsOrdersEnum {
SMS_ORDERS_0("否", 0),
SMS_ORDERS_1("是", 1);
private String name;
private Integer value;
}
package cn.iocoder.yudao.module.system.enums.sms;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 是否匹配运输方式枚举
*
* @author jayden
*/
@Getter
@AllArgsConstructor
public enum SmsIsTransportEnum {
SMS_ORDERS_0("否", 0),
SMS_ORDERS_1("是", 1);
private String name;
private Integer value;
}
package cn.iocoder.yudao.module.system.enums.sms;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 发送类型枚举
*
* @author jayden
*/
@Getter
@AllArgsConstructor
public enum SmsMessageTypeEnum {
SMS_MESSAGE_TYPE_1("短信", 1),
SMS_MESSAGE_TYPE_2("whatsapp", 2),
SMS_MESSAGE_TYPE_3("email", 3);
private String name;
private Integer value;
}
package cn.iocoder.yudao.module.system.enums.sms;
import cn.hutool.core.util.ArrayUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 用户短信验证码发送场景的枚举
*
* @author 捷道源码
*/
@Getter
@AllArgsConstructor
public enum SmsNodeEnum {
MEMBER_REG("user-sms-reg", "user-sms-reg", "会员用户 - 手机号注册"),//测了
MEMBER_LOGIN("user-sms-login", "user-sms-login", "会员用户 - 手机号登陆"),//测了
MEMBER_UPDATE_MOBILE("user-sms-update-mobile", "user-sms-update-mobile", "会员用户 - 解绑手机"),//测了
MEMBER_FORGET_PASSWORD("user-sms-reset-password", "user-sms-reset-password", "会员用户 - 忘记密码"),//测了
TRANSFER_CONTROL_GOODS("transfer-control-goods", "transfer-control-goods", "订单 - 控货权转移"),//测了
DELIVERY_VERIFICATION_SMS("delivery-verification-sms", "delivery-verification-sms", "订单 - 放货验证短信"),//测了
NOTIFICATION_SUCCESS_DELIVERY("notification-successful-delivery", "notification-successful-delivery", "订单 - 放货成功通知"),//测了
NOTIFICATION_SUCCESS_CANCEL_DELIVERY("notification-successful-cancel-delivery", "notification-successful-cancel-delivery", "订单 - 取消放货通知"),//测了
// NOTIFICATION_SUCCESS_SEASONING_CONDIMENTS("notification-successful-seasoning-condiments", "notification-successful-seasoning-condiments", "订单 - 调货通知"),//暂时没发现调用
MEMBER_REDEEM_REWARD("user-sms-redeem-reward", "user-sms-redeem-reward", "会员用户 - 兑换礼品"),
// SHIPMENT_ARRIVALS("shipment-arrivals", "shipment-arrivals", "出货 - 到港通知(多订单)"),//
// SHIPMENT_CUSTOMS_CLEARANCES("shipment-customs-clearances", "shipment-customs-clearances", "出货 - 清关通知(多订单)"),//
// SHIPMENT_CABINETS("shipment-cabinets", "shipment-cabinets", "出货 - 装柜通知(多订单)"),//
SHIPMENT_ARRIVAL("shipment-arrival", "shipment-arrival", "出货 - 到港通知"),//h
SHIPMENT_CUSTOMS_CLEARANCE("shipment-customs-clearance", "shipment-customs-clearance", "出货 - 清关通知"),//h
SHIPMENT_CABINET("shipment-cabinet", "shipment-cabinet", "出货 - 装柜通知"),//h
SHIPMENT_CLOSE_CONTAINER("shipment-close-container", "shipment-close-container", "出货 - 封柜反审"),//好像没发信息
WAREHOUSE_IN_CONTROL("warehouse-in-control", "warehouse-in-control", "入仓控货通知"),//h
WAREHOUSE_IN("warehouse-in", "warehouse-in", "入仓通知"),//h
WAREHOUSE_IN_APPEND("warehouse-in-append", "warehouse-in-append", "追加通知"),//k
WAREHOUSE_IN_CONTROL_COLLECT_OF_GOODS("warehouse-in-control-collect-of-goods", "warehouse-in-control-collect-of-goods", "入仓-控货代收货款"),//无此功能
WAREHOUSE_IN_COLLECT_OF_GOODS("warehouse-in-collect-of-goods", "warehouse-in-collect-of-goods", "入仓-非控货代收货款"),//h
/** 空运新增模板 **/
// AIR_WAREHOUSE_IN_CONTROL("air-warehouse-in-control", "air-warehouse-in-control", "入仓控货通知"),//x
// AIR_WAREHOUSE_IN("air-warehouse-in", "air-warehouse-in", "入仓通知"),//x
// AIR_SHIPMENT_ARRIVAL("air-shipment-arrival", "air-shipment-arrival", "出货 - 到港通知"),//x
// AIR_SHIPMENT_ARRIVALS("air-shipment-arrivals", "air-shipment-arrivals", "出货 - 到港通知(多订单)"),//x
// AIR_SHIPMENT_CUSTOMS_CLEARANCE("air-shipment-customs-clearance", "air-shipment-customs-clearance", "出货 - 清关通知"),//
// AIR_SHIPMENT_CUSTOMS_CLEARANCES("air-shipment-customs-clearances", "air-shipment-customs-clearances", "出货 - 清关通知(多订单)"),//
AIR_SHIPMENT_WAREHOUSE("air-shipment-warehouse", "air-shipment-warehouse", "出货 - 出仓通知"),//
// AIR_SHIPMENT_WAREHOUSES("air-shipment-warehouses", "air-shipment-warehouses", "出货 - 出仓通知(多订单)"),//
// AIR_WAREHOUSE_IN_COLLECT_OF_GOODS("air-warehouse-in-collect-of-goods", "air-warehouse-in-collect-of-goods", "入仓-非控货代收货款"),//
;
/**
* 验证场景的编号
*/
private final String nodeValue;
/**
* 模版编码
*/
private final String templateCode;
/**
* 描述
*/
private final String description;
public static SmsNodeEnum getCodeByScene(String scene) {
return ArrayUtil.firstMatch(sceneEnum -> sceneEnum.getNodeValue().equals(scene),
values());
}
}
package cn.iocoder.yudao.module.system.enums.sms;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 系统状态枚举
*
* @author jayden
*/
@Getter
@AllArgsConstructor
public enum SystemStatusEnum {
SYSTEM_STATUS_0("开启", 0),
SYSTEM_STATUS_1("关闭", 1);
private String name;
private Integer value;
}
package cn.iocoder.yudao.module.system.api.sms; package cn.iocoder.yudao.module.system.api.sms;
import cn.iocoder.yudao.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTO; import cn.iocoder.yudao.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTO;
import cn.iocoder.yudao.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTOV2;
import cn.iocoder.yudao.module.system.service.sms.SmsSendService; import cn.iocoder.yudao.module.system.service.sms.SmsSendService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
...@@ -25,6 +26,13 @@ public class SmsSendApiImpl implements SmsSendApi { ...@@ -25,6 +26,13 @@ public class SmsSendApiImpl implements SmsSendApi {
reqDTO.getTemplateCode(), reqDTO.getTemplateParams(), reqDTO.getAreaCode()); reqDTO.getTemplateCode(), reqDTO.getTemplateParams(), reqDTO.getAreaCode());
} }
@Override
public Long sendSingleSmsToAdminV2(SmsSendSingleToUserReqDTOV2 reqDTO) {
return smsSendService.sendSingleSmsToAdminV2(reqDTO.getMobile(), reqDTO.getUserId(),
reqDTO.getNodeValue(), reqDTO.getAreaCode(), reqDTO.getIsOrders(), reqDTO.getIsTransport(),
reqDTO.getTransportId(), reqDTO.getMessageType(), reqDTO.getTemplateParams());
}
@Override @Override
public Long sendSingleSmsToMember(SmsSendSingleToUserReqDTO reqDTO) { public Long sendSingleSmsToMember(SmsSendSingleToUserReqDTO reqDTO) {
return smsSendService.sendSingleSmsToMember(reqDTO.getMobile(), reqDTO.getUserId(), return smsSendService.sendSingleSmsToMember(reqDTO.getMobile(), reqDTO.getUserId(),
......
...@@ -2,13 +2,17 @@ package cn.iocoder.yudao.module.system.controller.admin.sms; ...@@ -2,13 +2,17 @@ package cn.iocoder.yudao.module.system.controller.admin.sms;
import cn.hutool.core.util.URLUtil; import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.servlet.ServletUtil; import cn.hutool.extra.servlet.ServletUtil;
import cn.iocoder.yudao.module.system.service.sms.SmsSendService;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.framework.sms.core.client.impl.sendchamp.SendchampSmsClient;
import cn.iocoder.yudao.framework.sms.core.enums.SmsChannelEnum; import cn.iocoder.yudao.framework.sms.core.enums.SmsChannelEnum;
import cn.iocoder.yudao.module.system.service.sms.SmsLogService;
import cn.iocoder.yudao.module.system.service.sms.SmsSendService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
...@@ -27,6 +31,9 @@ public class SmsCallbackController { ...@@ -27,6 +31,9 @@ public class SmsCallbackController {
@Resource @Resource
private SmsSendService smsSendService; private SmsSendService smsSendService;
@Autowired
private SmsLogService smsLogService;
@PostMapping("/yunpian") @PostMapping("/yunpian")
@ApiOperation(value = "云片短信的回调", notes = "参见 https://www.yunpian.com/official/document/sms/zh_cn/domestic_push_report 文档") @ApiOperation(value = "云片短信的回调", notes = "参见 https://www.yunpian.com/official/document/sms/zh_cn/domestic_push_report 文档")
@ApiImplicitParam(name = "sms_status", value = "发送状态", required = true, example = "[{具体内容}]", dataTypeClass = String.class) @ApiImplicitParam(name = "sms_status", value = "发送状态", required = true, example = "[{具体内容}]", dataTypeClass = String.class)
...@@ -46,4 +53,13 @@ public class SmsCallbackController { ...@@ -46,4 +53,13 @@ public class SmsCallbackController {
return success(true); return success(true);
} }
@PostMapping("/sendchamp")
@ApiOperation(value = "sendchamp短信的回调", notes = "参见 https://help.aliyun.com/document_detail/120998.html 文档")
@OperateLog(enable = false)
public CommonResult<Boolean> receiveSendchampSmsStatus(HttpServletRequest request) throws Throwable {
String text = ServletUtil.getBody(request);
SendchampSmsClient.SmsReceiveStatus smsReceiveStatus = JsonUtils.parseObject(text, SendchampSmsClient.SmsReceiveStatus.class);
smsLogService.updateSendchampReceive(smsReceiveStatus);
return success(true);
}
} }
package cn.iocoder.yudao.module.system.controller.admin.sms;
import cn.iocoder.yudao.framework.mybatis.core.vo.PageVO;
import cn.iocoder.yudao.module.system.convert.sms.SmsNodeConvert;
import cn.iocoder.yudao.module.system.service.sms.SmsNodeService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.annotations.*;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.smsNode.*;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsNodeDO;
@Validated
@RestController
@Api(tags = "管理后台 - 短信节点")
@RequestMapping("/system/sms-node")
public class SmsNodeController {
@Resource
private SmsNodeService smsNodeService;
@PostMapping("/create")
@ApiOperation("创建短信节点")
@PreAuthorize("@ss.hasPermission('system:sms-node:create')")
public CommonResult<Long> createSmsNode(@Valid @RequestBody SmsNodeCreateReqVO createReqVO) {
return success(smsNodeService.createSmsNode(createReqVO));
}
@PutMapping("/update")
@ApiOperation("更新短信节点")
@PreAuthorize("@ss.hasPermission('system:sms-node:update')")
public CommonResult<Boolean> updateSmsNode(@Valid @RequestBody SmsNodeUpdateReqVO updateReqVO) {
smsNodeService.updateSmsNode(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@ApiOperation("删除短信节点")
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('system:sms-node:delete')")
public CommonResult<Boolean> deleteSmsNode(@RequestParam("id") Long id) {
smsNodeService.deleteSmsNode(id);
return success(true);
}
@GetMapping("/get")
@ApiOperation("获得短信节点")
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('system:sms-node:query')")
public CommonResult<SmsNodeBackVO> getSmsNode(@RequestParam("id") Long id) {
SmsNodeDO smsNode = smsNodeService.getSmsNode(id);
return success(SmsNodeConvert.INSTANCE.convert(smsNode));
}
@GetMapping("/list")
@ApiOperation("获得短信节点列表")
@ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class)
@PreAuthorize("@ss.hasPermission('system:sms-node:query')")
public CommonResult<List<SmsNodeBackVO>> getSmsNodeList(@RequestParam("ids") Collection<Long> ids) {
List<SmsNodeDO> list = smsNodeService.getSmsNodeList(ids);
return success(SmsNodeConvert.INSTANCE.convertList(list));
}
@GetMapping("/page")
@ApiOperation("获得短信节点分页")
@PreAuthorize("@ss.hasPermission('system:sms-node:query')")
public CommonResult<PageResult<SmsNodeBackVO>> getSmsNodePage(@Valid SmsNodeQueryVO query, PageVO page) {
PageResult<SmsNodeDO> pageResult = smsNodeService.getSmsNodePage(query, page);
return success(SmsNodeConvert.INSTANCE.convertPage(pageResult));
}
@GetMapping("/export-excel")
@ApiOperation("导出短信节点 Excel")
@PreAuthorize("@ss.hasPermission('system:sms-node:export')")
@OperateLog(type = EXPORT)
public void exportSmsNodeExcel(@Valid SmsNodeQueryVO query,
HttpServletResponse response) throws IOException {
List<SmsNodeDO> list = smsNodeService.getSmsNodeList(query);
// 导出 Excel
List<SmsNodeBackVO> datas = SmsNodeConvert.INSTANCE.convertList(list);
ExcelUtils.write(response, "短信节点.xls", "数据", SmsNodeBackVO.class, datas);
}
}
...@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.excel.convert.DictConvert; ...@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.excel.convert.DictConvert;
import cn.iocoder.yudao.framework.excel.convert.JsonConvert; import cn.iocoder.yudao.framework.excel.convert.JsonConvert;
import cn.iocoder.yudao.module.system.enums.DictTypeConstants; import cn.iocoder.yudao.module.system.enums.DictTypeConstants;
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import java.util.Date; import java.util.Date;
...@@ -107,4 +108,23 @@ public class SmsLogExcelVO { ...@@ -107,4 +108,23 @@ public class SmsLogExcelVO {
*/ */
@ExcelProperty(value = "重发次数") @ExcelProperty(value = "重发次数")
private Integer num; private Integer num;
/**
* 短信节点表id
*/
@ExcelProperty(value = "短信节点表id")
private Long nodeId;
/**
* 节点模板序列号
*/
@ExcelProperty(value = "节点模板序列号")
private Integer nodeTemplateSn;
/**
* 发送类型
*/
@ExcelProperty(value = "发送类型")
private Integer messageType;
} }
...@@ -55,4 +55,24 @@ public class SmsLogExportReqVO { ...@@ -55,4 +55,24 @@ public class SmsLogExportReqVO {
@ApiModelProperty(value = "重发次数") @ApiModelProperty(value = "重发次数")
private Integer num; private Integer num;
/**
* 短信节点表id
*/
@ApiModelProperty(value = "短信节点表id")
private Long nodeId;
/**
* 节点模板序列号
*/
@ApiModelProperty(value = "节点模板序列号")
private Integer nodeTemplateSn;
/**
* 发送类型
*/
@ApiModelProperty(value = "发送类型")
private Integer messageType;
@ApiModelProperty(value = "短信 API 发送返回的序号")
private String apiSerialNo;
} }
package cn.iocoder.yudao.module.system.controller.admin.sms.vo.log; package cn.iocoder.yudao.module.system.controller.admin.sms.vo.log;
import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageParam;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
...@@ -62,4 +63,21 @@ public class SmsLogPageReqVO extends PageParam { ...@@ -62,4 +63,21 @@ public class SmsLogPageReqVO extends PageParam {
private Integer num; private Integer num;
/**
* 短信节点表id
*/
@ApiModelProperty(value = "短信节点表id")
private Long nodeId;
/**
* 节点模板序列号
*/
@ApiModelProperty(value = "节点模板序列号")
private Integer nodeTemplateSn;
/**
* 发送类型
*/
@ApiModelProperty(value = "发送类型")
private Integer messageType;
} }
...@@ -97,4 +97,22 @@ public class SmsLogRespVO { ...@@ -97,4 +97,22 @@ public class SmsLogRespVO {
@ApiModelProperty(value = "重发次数") @ApiModelProperty(value = "重发次数")
private Integer num; private Integer num;
/**
* 短信节点表id
*/
@ApiModelProperty(value = "短信节点表id")
private Long nodeId;
/**
* 节点模板序列号
*/
@ApiModelProperty(value = "节点模板序列号")
private Integer nodeTemplateSn;
/**
* 发送类型
*/
@ApiModelProperty(value = "发送类型")
private Integer messageType;
} }
package cn.iocoder.yudao.module.system.controller.admin.sms.vo.smsNode;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import com.alibaba.excel.annotation.ExcelProperty;
import cn.iocoder.yudao.framework.excel.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.convert.DictConvert;
import org.springframework.format.annotation.DateTimeFormat;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* 短信节点 Response VO
*
* @author jayden
*/
@Data
@ApiModel("管理后台 - 短信节点 Response VO")
public class SmsNodeBackVO {
@ExcelProperty("编号")
@ApiModelProperty(value = "编号", required = true)
private Long id;
@ExcelProperty("节点")
@ApiModelProperty(value = "节点", required = true)
private String nodeValue;
@ExcelProperty(value = "运输方式", converter = DictConvert.class)
@DictFormat("transport_type") // TODO 代码优化:建议设置到对应的 XXXDictTypeConstants 枚举类中
@ApiModelProperty(value = "运输方式", required = true)
private Long transportId;
@ExcelProperty("国家区号")
@ApiModelProperty(value = "国家区号", required = true)
private Long countryId;
@ExcelProperty("国家区号")
@ApiModelProperty(value = "国家区号,和订单中国家区号保持一致", required = true)
private String countryCode;
@ExcelProperty("启用状态")
@ApiModelProperty(value = "启用状态(0:启用,1:关闭)", required = true)
private Integer status;
@ExcelProperty("模板1")
@ApiModelProperty(value = "模板1", required = true)
private Long templateIdOne;
@ExcelProperty("模板2")
@ApiModelProperty(value = "模板2")
private Long templateIdTwo;
@ExcelProperty("模板3")
@ApiModelProperty(value = "模板3")
private Long templateIdThree;
@ExcelProperty("模板4")
@ApiModelProperty(value = "模板4")
private Long templateIdFour;
@ExcelProperty("创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
@ExcelProperty("是否匹配运输方式")
@ApiModelProperty(value = "是否匹配运输方式", required = true)
private Integer isTransport;
@ExcelProperty("多订单")
@ApiModelProperty(value = "多订单", required = true)
private Integer isOrders;
}
package cn.iocoder.yudao.module.system.controller.admin.sms.vo.smsNode;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import javax.validation.constraints.*;
/**
* 短信节点 Base VO,提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class SmsNodeBaseVO {
@ApiModelProperty(value = "节点", required = true)
@NotNull(message = "节点不能为空")
private String nodeValue;
@ApiModelProperty(value = "运输方式ID", required = true)
@NotNull(message = "运输方式ID不能为空")
private Long transportId;
@ApiModelProperty(value = "国家区号id", required = true)
@NotNull(message = "国家区号id不能为空")
private Long countryId;
@ApiModelProperty(value = "国家区号,和订单中国家区号保持一致", required = true)
@NotNull(message = "国家区号,和订单中国家区号保持一致不能为空")
private String countryCode;
@ApiModelProperty(value = "启用状态(0:启用,1:关闭)", required = true)
@NotNull(message = "启用状态(0:启用,1:关闭)不能为空")
private Integer status;
@ApiModelProperty(value = "模板1", required = true)
@NotNull(message = "模板1不能为空")
private Long templateIdOne;
@ApiModelProperty(value = "模板2")
private Long templateIdTwo;
@ApiModelProperty(value = "模板3")
private Long templateIdThree;
@ApiModelProperty(value = "模板4")
private Long templateIdFour;
@ApiModelProperty(value = "是否匹配运输方式", required = true)
private Integer isTransport;
@ApiModelProperty(value = "多订单", required = true)
private Integer isOrders;
}
package cn.iocoder.yudao.module.system.controller.admin.sms.vo.smsNode;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import javax.validation.constraints.*;
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@ApiModel("管理后台 - 短信节点创建 Request VO")
public class SmsNodeCreateReqVO extends SmsNodeBaseVO {
}
package cn.iocoder.yudao.module.system.controller.admin.sms.vo.smsNode;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Data
@ApiModel("管理后台 - 短信节点查询 VO")
public class SmsNodeQueryVO {
@ApiModelProperty(value = "节点")
private String nodeValue;
@ApiModelProperty(value = "运输方式ID")
private Long transportId;
@ApiModelProperty(value = "国家区号id")
private Long countryId;
@ApiModelProperty(value = "国家区号,和订单中国家区号保持一致")
private String countryCode;
@ApiModelProperty(value = "启用状态(0:启用,1:关闭)")
private Integer status;
@ApiModelProperty(value = "模板1")
private Long templateIdOne;
@ApiModelProperty(value = "模板2")
private Long templateIdTwo;
@ApiModelProperty(value = "模板3")
private Long templateIdThree;
@ApiModelProperty(value = "模板4")
private Long templateIdFour;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ApiModelProperty(value = "开始创建时间")
private Date beginCreateTime;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ApiModelProperty(value = "结束创建时间")
private Date endCreateTime;
@ApiModelProperty(value = "是否匹配运输方式", required = true)
private Integer isTransport;
@ApiModelProperty(value = "多订单", required = true)
private Integer isOrders;
}
package cn.iocoder.yudao.module.system.controller.admin.sms.vo.smsNode;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
import javax.validation.constraints.*;
@ApiModel("管理后台 - 短信节点更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SmsNodeUpdateReqVO extends SmsNodeBaseVO {
@ApiModelProperty(value = "编号", required = true)
@NotNull(message = "编号不能为空")
private Long id;
}
...@@ -6,9 +6,9 @@ import lombok.Data; ...@@ -6,9 +6,9 @@ import lombok.Data;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
/** /**
* 短信模板 Base VO,提供给添加、修改、详细的子 VO 使用 * 短信模板 Base VO,提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/ */
@Data @Data
public class SmsTemplateBaseVO { public class SmsTemplateBaseVO {
...@@ -43,11 +43,25 @@ public class SmsTemplateBaseVO { ...@@ -43,11 +43,25 @@ public class SmsTemplateBaseVO {
@NotNull(message = "短信渠道编号不能为空") @NotNull(message = "短信渠道编号不能为空")
private Long channelId; private Long channelId;
@ApiModelProperty(value = "短信 API 的英文模板编号", required = true, example = "4383920") // @ApiModelProperty(value = "短信 API 的英文模板编号", required = true, example = "4383920")
@NotNull(message = "短信 API 的英文模板编号不能为空") // @NotNull(message = "短信 API 的英文模板编号不能为空")
private String apiTemplateIdEn; // private String apiTemplateIdEn;
//
// @ApiModelProperty(value = "英文模板内容", required = true, example = "你好,{name}。你长的太{like}啦!")
// @NotNull(message = "英文模板内容不能为空")
// private String contentEn;
@ApiModelProperty(value = "英文模板内容", required = true, example = "你好,{name}。你长的太{like}啦!") @ApiModelProperty(value = "节点", required = true)
@NotNull(message = "英文模板内容不能为空") @NotNull(message = "节点不能为空")
private String contentEn; private String nodeValue;
@ApiModelProperty(value = "运输方式", required = true)
@NotNull(message = "运输方式不能为空")
private Long transportId;
@ApiModelProperty(value = "发送类型", required = true)
private Integer messageType;
@ApiModelProperty(value = "语言")
private String language;
} }
...@@ -52,9 +52,21 @@ public class SmsTemplateExcelVO { ...@@ -52,9 +52,21 @@ public class SmsTemplateExcelVO {
@ExcelProperty("创建时间") @ExcelProperty("创建时间")
private Date createTime; private Date createTime;
@ExcelProperty("英文模板内容") // @ExcelProperty("英文模板内容")
private String contentEn; // private String contentEn;
//
// @ExcelProperty("短信 API 的英文模板编号")
// private String apiTemplateIdEn;
@ExcelProperty("短信 API 的英文模板编号") @ExcelProperty(value = "节点")
private String apiTemplateIdEn; private String nodeValue;
@ExcelProperty(value = "运输方式")
private Long transportId;
@ExcelProperty(value = "发送类型")
private Integer messageType;
@ExcelProperty(value = "语言")
private String language;
} }
...@@ -44,4 +44,16 @@ public class SmsTemplateExportReqVO { ...@@ -44,4 +44,16 @@ public class SmsTemplateExportReqVO {
@ApiModelProperty(value = "短信 API 的英文模板编号", example = "4383920", notes = "模糊匹配") @ApiModelProperty(value = "短信 API 的英文模板编号", example = "4383920", notes = "模糊匹配")
private String apiTemplateIdEn; private String apiTemplateIdEn;
@ApiModelProperty(value = "节点")
private String nodeValue;
@ApiModelProperty(value = "运输方式")
private Long transportId;
@ApiModelProperty(value = "发送类型")
private Integer messageType;
@ApiModelProperty(value = "语言")
private String language;
} }
...@@ -49,4 +49,16 @@ public class SmsTemplatePageReqVO extends PageParam { ...@@ -49,4 +49,16 @@ public class SmsTemplatePageReqVO extends PageParam {
@ApiModelProperty(value = "短信 API 的英文模板编号", example = "4383920", notes = "模糊匹配") @ApiModelProperty(value = "短信 API 的英文模板编号", example = "4383920", notes = "模糊匹配")
private String apiTemplateIdEn; private String apiTemplateIdEn;
@ApiModelProperty(value = "节点")
private String nodeValue;
@ApiModelProperty(value = "运输方式")
private Long transportId;
@ApiModelProperty(value = "发送类型")
private Integer messageType;
@ApiModelProperty(value = "语言")
private String language;
} }
package cn.iocoder.yudao.module.system.convert.sms; package cn.iocoder.yudao.module.system.convert.sms;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsLogDTO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogExcelVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogExcelVO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogRespVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogRespVO;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO; import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers; import org.mapstruct.factory.Mappers;
...@@ -21,6 +22,8 @@ public interface SmsLogConvert { ...@@ -21,6 +22,8 @@ public interface SmsLogConvert {
SmsLogRespVO convert(SmsLogDO bean); SmsLogRespVO convert(SmsLogDO bean);
SmsLogDTO toDto(SmsLogDO smsLogDO);
List<SmsLogRespVO> convertList(List<SmsLogDO> list); List<SmsLogRespVO> convertList(List<SmsLogDO> list);
PageResult<SmsLogRespVO> convertPage(PageResult<SmsLogDO> page); PageResult<SmsLogRespVO> convertPage(PageResult<SmsLogDO> page);
......
package cn.iocoder.yudao.module.system.convert.sms;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.smsNode.SmsNodeBackVO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.smsNode.SmsNodeCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.smsNode.SmsNodeUpdateReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsNodeDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* 短信节点 Convert
* @author jayden
*/
@Mapper
public interface SmsNodeConvert {
/*****转换MapStruct*****/
SmsNodeConvert INSTANCE = Mappers.getMapper(SmsNodeConvert.class);
/***
* 创建VO转实体
* @param bean
* @return
*/
SmsNodeDO convert(SmsNodeCreateReqVO bean);
/***
* 修改VO转实体
* @param bean
* @return
*/
SmsNodeDO convert(SmsNodeUpdateReqVO bean);
/***
* 实体转返回VO
* @param bean
* @return
*/
SmsNodeBackVO convert(SmsNodeDO bean);
/***
* 实体列表转返回VO列表
* @param list
* @return
*/
List<SmsNodeBackVO> convertList(List<SmsNodeDO> list);
/***
* 实体分页转返回分页
* @param page
* @return
*/
PageResult<SmsNodeBackVO> convertPage(PageResult<SmsNodeDO> page);
}
package cn.iocoder.yudao.module.system.convert.sms; package cn.iocoder.yudao.module.system.convert.sms;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateDTO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateCreateReqVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateExcelVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateExcelVO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateRespVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateRespVO;
...@@ -20,6 +21,8 @@ public interface SmsTemplateConvert { ...@@ -20,6 +21,8 @@ public interface SmsTemplateConvert {
SmsTemplateDO convert(SmsTemplateUpdateReqVO bean); SmsTemplateDO convert(SmsTemplateUpdateReqVO bean);
SmsTemplateDTO toDto(SmsTemplateDO smsTemplateDO);
SmsTemplateRespVO convert(SmsTemplateDO bean); SmsTemplateRespVO convert(SmsTemplateDO bean);
List<SmsTemplateRespVO> convertList(List<SmsTemplateDO> list); List<SmsTemplateRespVO> convertList(List<SmsTemplateDO> list);
......
...@@ -34,11 +34,10 @@ public class SmsCodeDO extends BaseDO { ...@@ -34,11 +34,10 @@ public class SmsCodeDO extends BaseDO {
*/ */
private String code; private String code;
/** /**
* 发送场景 * 业务节点
* *
* 枚举 {@link SmsCodeDO}
*/ */
private Integer scene; private String nodeValue;
/** /**
* 创建 IP * 创建 IP
*/ */
......
...@@ -8,6 +8,7 @@ import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants; ...@@ -8,6 +8,7 @@ import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import io.swagger.annotations.ApiModelProperty;
import lombok.*; import lombok.*;
import java.util.Date; import java.util.Date;
...@@ -37,13 +38,13 @@ public class SmsLogDO extends BaseDO { ...@@ -37,13 +38,13 @@ public class SmsLogDO extends BaseDO {
/** /**
* 短信渠道编号 * 短信渠道编号
* * <p>
* 关联 {@link SmsChannelDO#getId()} * 关联 {@link SmsChannelDO#getId()}
*/ */
private Long channelId; private Long channelId;
/** /**
* 短信渠道编码 * 短信渠道编码
* * <p>
* 冗余 {@link SmsChannelDO#getCode()} * 冗余 {@link SmsChannelDO#getCode()}
*/ */
private String channelCode; private String channelCode;
...@@ -52,19 +53,19 @@ public class SmsLogDO extends BaseDO { ...@@ -52,19 +53,19 @@ public class SmsLogDO extends BaseDO {
/** /**
* 模板编号 * 模板编号
* * <p>
* 关联 {@link SmsTemplateDO#getId()} * 关联 {@link SmsTemplateDO#getId()}
*/ */
private Long templateId; private Long templateId;
/** /**
* 模板编码 * 模板编码
* * <p>
* 冗余 {@link SmsTemplateDO#getCode()} * 冗余 {@link SmsTemplateDO#getCode()}
*/ */
private String templateCode; private String templateCode;
/** /**
* 短信类型 * 短信类型
* * <p>
* 冗余 {@link SmsTemplateDO#getType()} * 冗余 {@link SmsTemplateDO#getType()}
*/ */
private Integer templateType; private Integer templateType;
...@@ -79,7 +80,7 @@ public class SmsLogDO extends BaseDO { ...@@ -79,7 +80,7 @@ public class SmsLogDO extends BaseDO {
private Map<String, Object> templateParams; private Map<String, Object> templateParams;
/** /**
* 短信 API 的模板编号 * 短信 API 的模板编号
* * <p>
* 冗余 {@link SmsTemplateDO#getApiTemplateId()} * 冗余 {@link SmsTemplateDO#getApiTemplateId()}
*/ */
private String apiTemplateId; private String apiTemplateId;
...@@ -96,7 +97,7 @@ public class SmsLogDO extends BaseDO { ...@@ -96,7 +97,7 @@ public class SmsLogDO extends BaseDO {
private Long userId; private Long userId;
/** /**
* 用户类型 * 用户类型
* * <p>
* 枚举 {@link UserTypeEnum} * 枚举 {@link UserTypeEnum}
*/ */
private Integer userType; private Integer userType;
...@@ -105,7 +106,7 @@ public class SmsLogDO extends BaseDO { ...@@ -105,7 +106,7 @@ public class SmsLogDO extends BaseDO {
/** /**
* 发送状态 * 发送状态
* * <p>
* 枚举 {@link SmsSendStatusEnum} * 枚举 {@link SmsSendStatusEnum}
*/ */
private Integer sendStatus; private Integer sendStatus;
...@@ -115,20 +116,20 @@ public class SmsLogDO extends BaseDO { ...@@ -115,20 +116,20 @@ public class SmsLogDO extends BaseDO {
private Date sendTime; private Date sendTime;
/** /**
* 发送结果的编码 * 发送结果的编码
* * <p>
* 枚举 {@link SmsFrameworkErrorCodeConstants} * 枚举 {@link SmsFrameworkErrorCodeConstants}
*/ */
private Integer sendCode; private Integer sendCode;
/** /**
* 发送结果的提示 * 发送结果的提示
* * <p>
* 一般情况下,使用 {@link SmsFrameworkErrorCodeConstants} * 一般情况下,使用 {@link SmsFrameworkErrorCodeConstants}
* 异常情况下,通过格式化 Exception 的提示存储 * 异常情况下,通过格式化 Exception 的提示存储
*/ */
private String sendMsg; private String sendMsg;
/** /**
* 短信 API 发送结果的编码 * 短信 API 发送结果的编码
* * <p>
* 由于第三方的错误码可能是字符串,所以使用 String 类型 * 由于第三方的错误码可能是字符串,所以使用 String 类型
*/ */
private String apiSendCode; private String apiSendCode;
...@@ -138,13 +139,13 @@ public class SmsLogDO extends BaseDO { ...@@ -138,13 +139,13 @@ public class SmsLogDO extends BaseDO {
private String apiSendMsg; private String apiSendMsg;
/** /**
* 短信 API 发送返回的唯一请求 ID * 短信 API 发送返回的唯一请求 ID
* * <p>
* 用于和短信 API 进行定位于排错 * 用于和短信 API 进行定位于排错
*/ */
private String apiRequestId; private String apiRequestId;
/** /**
* 短信 API 发送返回的序号 * 短信 API 发送返回的序号
* * <p>
* 用于和短信 API 平台的发送记录关联 * 用于和短信 API 平台的发送记录关联
*/ */
private String apiSerialNo; private String apiSerialNo;
...@@ -153,7 +154,7 @@ public class SmsLogDO extends BaseDO { ...@@ -153,7 +154,7 @@ public class SmsLogDO extends BaseDO {
/** /**
* 接收状态 * 接收状态
* * <p>
* 枚举 {@link SmsReceiveStatusEnum} * 枚举 {@link SmsReceiveStatusEnum}
*/ */
private Integer receiveStatus; private Integer receiveStatus;
...@@ -179,5 +180,19 @@ public class SmsLogDO extends BaseDO { ...@@ -179,5 +180,19 @@ public class SmsLogDO extends BaseDO {
*/ */
private Integer num; private Integer num;
/**
* 短信节点表id
*/
private Long nodeId;
/**
* 节点模板序列号
*/
private Integer nodeTemplateSn;
/**
* 发送类型
*/
private Integer messageType;
} }
package cn.iocoder.yudao.module.system.dal.dataobject.sms;
import lombok.*;
import java.util.*;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 短信节点 DO
*
* @author jayden
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName("system_sms_node")
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SmsNodeDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 节点
*/
private String nodeValue;
/**
* 运输方式ID
*
*/
private Integer transportId;
/**
* 国家区号id
*/
private Long countryId;
/**
* 国家区号,和订单中国家区号保持一致
*/
private String countryCode;
/**
* 启用状态(0:启用,1:关闭)
*/
private Integer status;
/**
* 模板1
*/
private Long templateIdOne;
/**
* 模板2
*/
@TableField(updateStrategy = FieldStrategy.IGNORED)
private Long templateIdTwo;
/**
* 模板3
*/
@TableField(updateStrategy = FieldStrategy.IGNORED)
private Long templateIdThree;
/**
* 模板4
*/
@TableField(updateStrategy = FieldStrategy.IGNORED)
private Long templateIdFour;
/**
* 是否匹配运输方式(0:否,1:是)
*/
private Integer isTransport;
/**
* 多订单(0:否,1:是)
*/
private Integer isOrders;
}
...@@ -95,5 +95,20 @@ public class SmsTemplateDO extends BaseDO { ...@@ -95,5 +95,20 @@ public class SmsTemplateDO extends BaseDO {
* 短信 API 的英文模板编号 * 短信 API 的英文模板编号
*/ */
private String apiTemplateIdEn; private String apiTemplateIdEn;
/**
* 节点
*/
private String nodeValue;
/**
* 运输方式
*/
private Long transportId;
/**
* 发送类型
*/
private Integer messageType;
/**
* 语言
*/
private String language;
} }
...@@ -16,13 +16,13 @@ public interface SmsCodeMapper extends BaseMapperX<SmsCodeDO> { ...@@ -16,13 +16,13 @@ public interface SmsCodeMapper extends BaseMapperX<SmsCodeDO> {
* 获得手机号的最后一个手机验证码 * 获得手机号的最后一个手机验证码
* *
* @param mobile 手机号 * @param mobile 手机号
* @param scene 发送场景,选填 * @param nodeValue 发送场景,选填
* @param code 验证码 选填 * @param code 验证码 选填
* @return 手机验证码 * @return 手机验证码
*/ */
default SmsCodeDO selectLastByMobile(String mobile, String code, Integer scene) { default SmsCodeDO selectLastByMobile(String mobile, String code, String nodeValue) {
return selectOne(new QueryWrapperX<SmsCodeDO>() return selectOne(new QueryWrapperX<SmsCodeDO>()
.eq("mobile", mobile).eqIfPresent("scene", scene).eqIfPresent("code", code) .eq("mobile", mobile).eqIfPresent("node_value", nodeValue).eqIfPresent("code", code)
.between("create_time", DateUtil.beginOfDay(new Date()), DateUtil.endOfDay(new Date())) .between("create_time", DateUtil.beginOfDay(new Date()), DateUtil.endOfDay(new Date()))
.orderByDesc("id").last(SqlConstants.LIMIT1)); .orderByDesc("id").last(SqlConstants.LIMIT1));
} }
......
package cn.iocoder.yudao.module.system.dal.mysql.sms; package cn.iocoder.yudao.module.system.dal.mysql.sms;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
...@@ -18,6 +19,8 @@ public interface SmsLogMapper extends BaseMapperX<SmsLogDO> { ...@@ -18,6 +19,8 @@ public interface SmsLogMapper extends BaseMapperX<SmsLogDO> {
return selectPage(reqVO, new QueryWrapperX<SmsLogDO>() return selectPage(reqVO, new QueryWrapperX<SmsLogDO>()
.eqIfPresent("channel_id", reqVO.getChannelId()) .eqIfPresent("channel_id", reqVO.getChannelId())
.eqIfPresent("template_id", reqVO.getTemplateId()) .eqIfPresent("template_id", reqVO.getTemplateId())
.eqIfPresent("node_id", reqVO.getNodeId())
.eqIfPresent("node_template_sn", reqVO.getNodeTemplateSn())
.likeIfPresent("mobile", reqVO.getMobile()) .likeIfPresent("mobile", reqVO.getMobile())
.eqIfPresent("send_status", reqVO.getSendStatus()) .eqIfPresent("send_status", reqVO.getSendStatus())
.betweenIfPresent("send_time", reqVO.getBeginSendTime(), reqVO.getEndSendTime()) .betweenIfPresent("send_time", reqVO.getBeginSendTime(), reqVO.getEndSendTime())
...@@ -30,11 +33,14 @@ public interface SmsLogMapper extends BaseMapperX<SmsLogDO> { ...@@ -30,11 +33,14 @@ public interface SmsLogMapper extends BaseMapperX<SmsLogDO> {
return selectList(new QueryWrapperX<SmsLogDO>() return selectList(new QueryWrapperX<SmsLogDO>()
.eqIfPresent("channel_id", reqVO.getChannelId()) .eqIfPresent("channel_id", reqVO.getChannelId())
.eqIfPresent("template_id", reqVO.getTemplateId()) .eqIfPresent("template_id", reqVO.getTemplateId())
.eqIfPresent("node_id", reqVO.getNodeId())
.eqIfPresent("node_template_sn", reqVO.getNodeTemplateSn())
.likeIfPresent("mobile", reqVO.getMobile()) .likeIfPresent("mobile", reqVO.getMobile())
.eqIfPresent("send_status", reqVO.getSendStatus()) .eqIfPresent("send_status", reqVO.getSendStatus())
.betweenIfPresent("send_time", reqVO.getBeginSendTime(), reqVO.getEndSendTime()) .betweenIfPresent("send_time", reqVO.getBeginSendTime(), reqVO.getEndSendTime())
.eqIfPresent("receive_status", reqVO.getReceiveStatus()) .eqIfPresent("receive_status", reqVO.getReceiveStatus())
.betweenIfPresent("receive_time", reqVO.getBeginReceiveTime(), reqVO.getEndReceiveTime()) .betweenIfPresent("receive_time", reqVO.getBeginReceiveTime(), reqVO.getEndReceiveTime())
.eq(StringUtils.isNotBlank(reqVO.getApiSerialNo()),"api_serial_no",reqVO.getApiSerialNo())
.orderByDesc("id")); .orderByDesc("id"));
} }
...@@ -42,4 +48,11 @@ public interface SmsLogMapper extends BaseMapperX<SmsLogDO> { ...@@ -42,4 +48,11 @@ public interface SmsLogMapper extends BaseMapperX<SmsLogDO> {
"DELETE FROM system_sms_log WHERE date(create_time) <= date(date_sub(now(), interval 90 day))" "DELETE FROM system_sms_log WHERE date(create_time) <= date(date_sub(now(), interval 90 day))"
}) })
int clearLog(Integer day); int clearLog(Integer day);
default List<SmsLogDO> selectReceiveList() {
return selectList(new QueryWrapperX<SmsLogDO>()
.apply("create_time >= NOW() - INTERVAL 20 HOUR")
.eq("template_type", 2)
.lt("num", 1)
.ne("receive_status", 10));
}
} }
package cn.iocoder.yudao.module.system.dal.mysql.sms;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.AbstractMapper;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQuery;
import cn.iocoder.yudao.framework.mybatis.core.vo.PageVO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.smsNode.SmsNodeQueryVO;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsNodeDO;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 短信节点 Mapper
*
* @author jayden
*/
@Mapper
public interface SmsNodeMapper extends AbstractMapper<SmsNodeDO> {
@Override
default PageResult<SmsNodeDO> selectPage(PageVO page, Object object) {
if (object instanceof SmsNodeQueryVO) {
SmsNodeQueryVO vo = (SmsNodeQueryVO) object;
return selectPage(page, new LambdaQuery<SmsNodeDO>()
.eq(StringUtils.isNotBlank(vo.getNodeValue()), SmsNodeDO::getNodeValue, vo.getNodeValue())
.eqIfPresent(SmsNodeDO::getTransportId, vo.getTransportId())
.eqIfPresent(SmsNodeDO::getCountryId, vo.getCountryId())
.eqIfPresent(SmsNodeDO::getCountryCode, vo.getCountryCode())
.eqIfPresent(SmsNodeDO::getIsTransport, vo.getIsTransport())
.eqIfPresent(SmsNodeDO::getIsOrders, vo.getIsOrders())
.eqIfPresent(SmsNodeDO::getStatus, vo.getStatus())
.eqIfPresent(SmsNodeDO::getTemplateIdOne, vo.getTemplateIdOne())
.eqIfPresent(SmsNodeDO::getTemplateIdTwo, vo.getTemplateIdTwo())
.eqIfPresent(SmsNodeDO::getTemplateIdThree, vo.getTemplateIdThree())
.eqIfPresent(SmsNodeDO::getTemplateIdFour, vo.getTemplateIdFour())
.betweenIfPresent(SmsNodeDO::getCreateTime, vo.getBeginCreateTime(), vo.getEndCreateTime())
.orderByDesc(SmsNodeDO::getId));
}
return null;
}
@Override
default List<SmsNodeDO> selectList(Object object) {
if (object instanceof SmsNodeQueryVO) {
SmsNodeQueryVO vo = (SmsNodeQueryVO) object;
return selectList(new LambdaQuery<SmsNodeDO>()
.eq(StringUtils.isNotBlank(vo.getNodeValue()), SmsNodeDO::getNodeValue, vo.getNodeValue())
.eqIfPresent(SmsNodeDO::getTransportId, vo.getTransportId())
.eqIfPresent(SmsNodeDO::getCountryId, vo.getCountryId())
.eqIfPresent(SmsNodeDO::getCountryCode, vo.getCountryCode())
.eqIfPresent(SmsNodeDO::getIsTransport, vo.getIsTransport())
.eqIfPresent(SmsNodeDO::getIsOrders, vo.getIsOrders())
.eqIfPresent(SmsNodeDO::getStatus, vo.getStatus())
.eqIfPresent(SmsNodeDO::getTemplateIdOne, vo.getTemplateIdOne())
.eqIfPresent(SmsNodeDO::getTemplateIdTwo, vo.getTemplateIdTwo())
.eqIfPresent(SmsNodeDO::getTemplateIdThree, vo.getTemplateIdThree())
.eqIfPresent(SmsNodeDO::getTemplateIdFour, vo.getTemplateIdFour())
.betweenIfPresent(SmsNodeDO::getCreateTime, vo.getBeginCreateTime(), vo.getEndCreateTime())
.orderByDesc(SmsNodeDO::getId));
}
return null;
}
}
...@@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; ...@@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateExportReqVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Select;
...@@ -26,6 +27,10 @@ public interface SmsTemplateMapper extends BaseMapperX<SmsTemplateDO> { ...@@ -26,6 +27,10 @@ public interface SmsTemplateMapper extends BaseMapperX<SmsTemplateDO> {
return selectPage(reqVO, new LambdaQueryWrapperX<SmsTemplateDO>() return selectPage(reqVO, new LambdaQueryWrapperX<SmsTemplateDO>()
.eqIfPresent(SmsTemplateDO::getType, reqVO.getType()) .eqIfPresent(SmsTemplateDO::getType, reqVO.getType())
.eqIfPresent(SmsTemplateDO::getStatus, reqVO.getStatus()) .eqIfPresent(SmsTemplateDO::getStatus, reqVO.getStatus())
.eq(StringUtils.isNotBlank(reqVO.getNodeValue()), SmsTemplateDO::getNodeValue, reqVO.getNodeValue())
.eqIfPresent(SmsTemplateDO::getTransportId, reqVO.getTransportId())
.eqIfPresent(SmsTemplateDO::getMessageType, reqVO.getMessageType())
.eqIfPresent(SmsTemplateDO::getLanguage, reqVO.getLanguage())
.likeIfPresent(SmsTemplateDO::getCode, reqVO.getCode()) .likeIfPresent(SmsTemplateDO::getCode, reqVO.getCode())
.likeIfPresent(SmsTemplateDO::getContent, reqVO.getContent()) .likeIfPresent(SmsTemplateDO::getContent, reqVO.getContent())
.likeIfPresent(SmsTemplateDO::getContentEn, reqVO.getContentEn()) .likeIfPresent(SmsTemplateDO::getContentEn, reqVO.getContentEn())
...@@ -40,6 +45,10 @@ public interface SmsTemplateMapper extends BaseMapperX<SmsTemplateDO> { ...@@ -40,6 +45,10 @@ public interface SmsTemplateMapper extends BaseMapperX<SmsTemplateDO> {
return selectList(new LambdaQueryWrapperX<SmsTemplateDO>() return selectList(new LambdaQueryWrapperX<SmsTemplateDO>()
.eqIfPresent(SmsTemplateDO::getType, reqVO.getType()) .eqIfPresent(SmsTemplateDO::getType, reqVO.getType())
.eqIfPresent(SmsTemplateDO::getStatus, reqVO.getStatus()) .eqIfPresent(SmsTemplateDO::getStatus, reqVO.getStatus())
.eq(StringUtils.isNotBlank(reqVO.getNodeValue()), SmsTemplateDO::getNodeValue, reqVO.getNodeValue())
.eqIfPresent(SmsTemplateDO::getTransportId, reqVO.getTransportId())
.eqIfPresent(SmsTemplateDO::getMessageType, reqVO.getMessageType())
.eqIfPresent(SmsTemplateDO::getLanguage, reqVO.getLanguage())
.likeIfPresent(SmsTemplateDO::getCode, reqVO.getCode()) .likeIfPresent(SmsTemplateDO::getCode, reqVO.getCode())
.likeIfPresent(SmsTemplateDO::getContent, reqVO.getContent()) .likeIfPresent(SmsTemplateDO::getContent, reqVO.getContent())
.likeIfPresent(SmsTemplateDO::getApiTemplateId, reqVO.getApiTemplateId()) .likeIfPresent(SmsTemplateDO::getApiTemplateId, reqVO.getApiTemplateId())
......
package cn.iocoder.yudao.module.system.mq.consumer.sms; //package cn.iocoder.yudao.module.system.mq.consumer.sms;
//
import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage; //import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage;
import cn.iocoder.yudao.module.system.service.sms.SmsSendService; //import cn.iocoder.yudao.module.system.service.sms.SmsSendService;
import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener; //import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener;
import lombok.extern.slf4j.Slf4j; //import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; //import org.springframework.stereotype.Component;
//
import javax.annotation.Resource; //import javax.annotation.Resource;
//
/** ///**
* 针对 {@link SmsSendMessage} 的消费者 // * 针对 {@link SmsSendMessage} 的消费者
* // *
* @author zzf // * @author zzf
*/ // */
@Component //@Component
@Slf4j //@Slf4j
public class SmsSendConsumer extends AbstractStreamMessageListener<SmsSendMessage> { //public class SmsSendConsumer extends AbstractStreamMessageListener<SmsSendMessage> {
//
@Resource // @Resource
private SmsSendService smsSendService; // private SmsSendService smsSendService;
//
@Override // @Override
public void onMessage(SmsSendMessage message) { // public void onMessage(SmsSendMessage message) {
log.info("[onMessage][消息内容({})]", message); // log.info("[onMessage][消息内容({})]", message);
smsSendService.doSendSms(message); // smsSendService.doSendSms(message);
} // }
//
} //}
package cn.iocoder.yudao.module.system.mq.consumer.sms;
import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener;
import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage;
import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessageV2;
import cn.iocoder.yudao.module.system.service.sms.SmsSendService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 针对 {@link SmsSendMessage} 的消费者
*
* @author zzf
*/
@Component
@Slf4j
public class SmsSendConsumerV2 extends AbstractStreamMessageListener<SmsSendMessageV2> {
@Resource
private SmsSendService smsSendService;
@Override
public void onMessage(SmsSendMessageV2 message) {
log.info("[onMessageV2][消息内容({})]", message);
smsSendService.doSendSmsV2(message);
}
}
package cn.iocoder.yudao.module.system.mq.message.sms;
import cn.iocoder.yudao.framework.common.util.json.core.KeyValue;
import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessage;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateDTO;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* 短信发送消息
*
* @author 捷道源码
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class SmsSendMessageV2 extends AbstractStreamMessage {
/**
* 短信日志编号
*/
@NotNull(message = "短信日志编号不能为空")
private Long logId;
/**
* 手机号
*/
@NotNull(message = "手机号不能为空")
private String mobile;
/**
* 模板
*/
@NotNull(message = "模板不能为空")
private SmsTemplateDTO smsTemplateDTO;
/**
* 短信模板参数
*/
private List<KeyValue<String, Object>> templateParams;
@Override
public String getStreamKey() {
return "system.sms.send";
}
}
...@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.mq.producer.sms; ...@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.mq.producer.sms;
import cn.iocoder.yudao.framework.common.util.json.core.KeyValue; import cn.iocoder.yudao.framework.common.util.json.core.KeyValue;
import cn.iocoder.yudao.module.system.mq.message.sms.SmsChannelRefreshMessage; import cn.iocoder.yudao.module.system.mq.message.sms.SmsChannelRefreshMessage;
import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage; import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage;
import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessageV2;
import cn.iocoder.yudao.module.system.mq.message.sms.SmsTemplateRefreshMessage; import cn.iocoder.yudao.module.system.mq.message.sms.SmsTemplateRefreshMessage;
import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
...@@ -56,4 +57,7 @@ public class SmsProducer { ...@@ -56,4 +57,7 @@ public class SmsProducer {
redisMQTemplate.send(message); redisMQTemplate.send(message);
} }
public void sendSmsSendMessageV2(SmsSendMessageV2 message) {
redisMQTemplate.send(message);
}
} }
package cn.iocoder.yudao.module.system.service.mail.impl; //package cn.iocoder.yudao.module.system.service.mail.impl;
//
import cn.hutool.core.lang.Assert; //import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil; //import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; //import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeCheckReqDTO; //import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeCheckReqDTO;
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; //import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; //import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsCodeDO; //import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsCodeDO;
import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsCodeMapper; //import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsCodeMapper;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; //import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
import cn.iocoder.yudao.module.system.framework.sms.SmsCodeProperties; //import cn.iocoder.yudao.module.system.framework.sms.SmsCodeProperties;
import cn.iocoder.yudao.module.system.service.sms.SmsCodeService; //import cn.iocoder.yudao.module.system.service.sms.SmsCodeService;
import cn.iocoder.yudao.module.system.service.sms.SmsSendService; //import cn.iocoder.yudao.module.system.service.sms.SmsSendService;
import org.springframework.stereotype.Service; //import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; //import org.springframework.validation.annotation.Validated;
//
import javax.annotation.Resource; //import javax.annotation.Resource;
import java.util.Date; //import java.util.Date;
//
import static cn.hutool.core.util.RandomUtil.randomInt; //import static cn.hutool.core.util.RandomUtil.randomInt;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; //import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
//
/** ///**
* 短信验证码 Service 实现类 // * 短信验证码 Service 实现类
* // *
* @author 捷道源码 // * @author 捷道源码
*/ // */
@Service //@Service
@Validated //@Validated
public class EmailCodeServiceImpl implements EmailCodeService { //public class EmailCodeServiceImpl implements EmailCodeService {
//
@Resource // @Resource
private SmsCodeProperties smsCodeProperties; // private SmsCodeProperties smsCodeProperties;
//
@Resource // @Resource
private SmsCodeMapper smsCodeMapper; // private SmsCodeMapper smsCodeMapper;
//
@Resource // @Resource
private SmsSendService smsSendService; // private SmsSendService smsSendService;
//
@Override // @Override
public void sendEmailCode(SmsCodeSendReqDTO reqDTO) { // public void sendEmailCode(SmsCodeSendReqDTO reqDTO) {
SmsSceneEnum sceneEnum = SmsSceneEnum.getCodeByScene(reqDTO.getScene()); // SmsSceneEnum sceneEnum = SmsSceneEnum.getCodeByScene(reqDTO.getScene());
Assert.notNull(sceneEnum, "验证码场景({}) 查找不到配置", reqDTO.getScene()); // Assert.notNull(sceneEnum, "验证码场景({}) 查找不到配置", reqDTO.getScene());
// 创建验证码 // // 创建验证码
String code = createSmsCode(reqDTO.getMobile(), reqDTO.getScene(), reqDTO.getCreateIp()); // String code = createSmsCode(reqDTO.getMobile(), reqDTO.getScene(), reqDTO.getCreateIp());
// 发送验证码 // // 发送验证码
smsSendService.sendSingleSms(reqDTO.getAreaCode()+reqDTO.getMobile(), null, null, // smsSendService.sendSingleSms(reqDTO.getAreaCode()+reqDTO.getMobile(), null, null,
sceneEnum.getTemplateCode(), MapUtil.of("code", code)); // sceneEnum.getTemplateCode(), MapUtil.of("code", code));
} // }
//
private String createSmsCode(String mobile, Integer scene, String ip) { // private String createSmsCode(String mobile, Integer scene, String ip) {
// 校验是否可以发送验证码,不用筛选场景 // // 校验是否可以发送验证码,不用筛选场景
SmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, null,null); // SmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, null,null);
if (lastSmsCode != null) { // if (lastSmsCode != null) {
if (lastSmsCode.getTodayIndex() >= smsCodeProperties.getSendMaximumQuantityPerDay()) { // 超过当天发送的上限。 // if (lastSmsCode.getTodayIndex() >= smsCodeProperties.getSendMaximumQuantityPerDay()) { // 超过当天发送的上限。
throw ServiceExceptionUtil.exception(SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY); // throw ServiceExceptionUtil.exception(SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY);
} // }
if (System.currentTimeMillis() - lastSmsCode.getCreateTime().getTime() // if (System.currentTimeMillis() - lastSmsCode.getCreateTime().getTime()
< smsCodeProperties.getSendFrequency().toMillis()) { // 发送过于频繁 // < smsCodeProperties.getSendFrequency().toMillis()) { // 发送过于频繁
throw ServiceExceptionUtil.exception(SMS_CODE_SEND_TOO_FAST); // throw ServiceExceptionUtil.exception(SMS_CODE_SEND_TOO_FAST);
} // }
// TODO 芋艿:提升,每个 IP 每天可发送数量 // // TODO 芋艿:提升,每个 IP 每天可发送数量
// TODO 芋艿:提升,每个 IP 每小时可发送数量 // // TODO 芋艿:提升,每个 IP 每小时可发送数量
} // }
//
// 创建验证码记录 // // 创建验证码记录
String code = String.valueOf(randomInt(smsCodeProperties.getBeginCode(), smsCodeProperties.getEndCode() + 1)); // String code = String.valueOf(randomInt(smsCodeProperties.getBeginCode(), smsCodeProperties.getEndCode() + 1));
SmsCodeDO newSmsCode = SmsCodeDO.builder().mobile(mobile).code(code) // SmsCodeDO newSmsCode = SmsCodeDO.builder().mobile(mobile).code(code)
.scene(scene).todayIndex(lastSmsCode != null ? lastSmsCode.getTodayIndex() + 1 : 1) // .scene(scene).todayIndex(lastSmsCode != null ? lastSmsCode.getTodayIndex() + 1 : 1)
.createIp(ip).used(false).build(); // .createIp(ip).used(false).build();
smsCodeMapper.insert(newSmsCode); // smsCodeMapper.insert(newSmsCode);
return code; // return code;
} // }
//
@Override // @Override
public void useSmsCode(SmsCodeUseReqDTO reqDTO) { // public void useSmsCode(SmsCodeUseReqDTO reqDTO) {
// 检测验证码是否有效 // // 检测验证码是否有效
SmsCodeDO lastSmsCode = this.checkSmsCode0(reqDTO.getMobile(), reqDTO.getCode(), reqDTO.getScene()); // SmsCodeDO lastSmsCode = this.checkSmsCode0(reqDTO.getMobile(), reqDTO.getCode(), reqDTO.getScene());
// 使用验证码 // // 使用验证码
smsCodeMapper.updateById(SmsCodeDO.builder().id(lastSmsCode.getId()) // smsCodeMapper.updateById(SmsCodeDO.builder().id(lastSmsCode.getId())
.used(true).usedTime(new Date()).usedIp(reqDTO.getUsedIp()).build()); // .used(true).usedTime(new Date()).usedIp(reqDTO.getUsedIp()).build());
} // }
//
@Override // @Override
public void checkSmsCode(SmsCodeCheckReqDTO reqDTO) { // public void checkSmsCode(SmsCodeCheckReqDTO reqDTO) {
checkSmsCode0(reqDTO.getMobile(), reqDTO.getCode(), reqDTO.getScene()); // checkSmsCode0(reqDTO.getMobile(), reqDTO.getCode(), reqDTO.getScene());
} // }
//
public SmsCodeDO checkSmsCode0(String mobile, String code, Integer scene) { // public SmsCodeDO checkSmsCode0(String mobile, String code, Integer scene) {
// 校验验证码 // // 校验验证码
SmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile,code,scene); // SmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile,code,scene);
// 若验证码不存在,抛出异常 // // 若验证码不存在,抛出异常
if (lastSmsCode == null) { // if (lastSmsCode == null) {
throw ServiceExceptionUtil.exception(SMS_CODE_NOT_FOUND); // throw ServiceExceptionUtil.exception(SMS_CODE_NOT_FOUND);
} // }
// 超过时间 // // 超过时间
if (System.currentTimeMillis() - lastSmsCode.getCreateTime().getTime() // if (System.currentTimeMillis() - lastSmsCode.getCreateTime().getTime()
>= smsCodeProperties.getExpireTimes().toMillis()) { // 验证码已过期 // >= smsCodeProperties.getExpireTimes().toMillis()) { // 验证码已过期
throw ServiceExceptionUtil.exception(SMS_CODE_EXPIRED); // throw ServiceExceptionUtil.exception(SMS_CODE_EXPIRED);
} // }
// 判断验证码是否已被使用 // // 判断验证码是否已被使用
if (Boolean.TRUE.equals(lastSmsCode.getUsed())) { // if (Boolean.TRUE.equals(lastSmsCode.getUsed())) {
throw ServiceExceptionUtil.exception(SMS_CODE_USED); // throw ServiceExceptionUtil.exception(SMS_CODE_USED);
} // }
return lastSmsCode; // return lastSmsCode;
} // }
//
} //}
package cn.iocoder.yudao.module.system.service.sms;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsResponse;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
/**
* @author Jayden
* @date 2024/11/12
*/
public class QuerySendDetails {
/**
* <b>description</b> :
* <p>使用AK&amp;SK初始化账号Client</p>
*
* @return Client
* @throws Exception
*/
public static IAcsClient createClient() throws Exception {
// 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考。
// 建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378657.html。
String key = "LTAIh5ooNeMRzsuz";
String secret = "jdqgtCP2Z0nZllEcxMiX8pv8qUDOTq";
IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", key, secret);
IAcsClient defaultAcsClient = new DefaultAcsClient(profile);
return defaultAcsClient;
}
public static void main(String[] args_) throws Exception {
String key = "LTAIh5ooNeMRzsuz";
String secret = "jdqgtCP2Z0nZllEcxMiX8pv8qUDOTq";
IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", key, secret);
IAcsClient client = new DefaultAcsClient(profile);
QuerySendDetailsRequest querySendDetailsRequest = new QuerySendDetailsRequest();
querySendDetailsRequest.setPhoneNumber("233591972886");
querySendDetailsRequest.setPageSize(1L);
querySendDetailsRequest.setCurrentPage(1L);
querySendDetailsRequest.setSendDate("20240905");
querySendDetailsRequest.setBizId("373707825513736449^0");
QuerySendDetailsResponse acsResponse = client.getAcsResponse(querySendDetailsRequest);
List<QuerySendDetailsResponse.SmsSendDetailDTO> smsSendDetailDTOs = acsResponse.getSmsSendDetailDTOs();
String receiveDate = smsSendDetailDTOs.get(0).getReceiveDate();
System.out.println(convertDateFormat(receiveDate));
}
/**
* 将日期字符串从 "yyyy-MM-dd HH:mm:ss" 格式转换为 "yyyyMMdd" 格式
*
* @param inputDateStr 输入的日期字符串,格式为 "yyyy-MM-dd HH:mm:ss"
* @return 转换后的日期字符串,格式为 "yyyyMMdd"
*/
public static String convertDateFormat(String inputDateStr) {
SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat outputFormat = new SimpleDateFormat("yyyyMMdd");
try {
// 解析输入的日期字符串
Date date = inputFormat.parse(inputDateStr);
// 格式化日期为输出格式
return outputFormat.format(date);
} catch (ParseException e) {
// 处理解析异常
e.printStackTrace();
return null;
}
}
}
package cn.iocoder.yudao.module.system.service.sms; package cn.iocoder.yudao.module.system.service.sms;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.web.config.BusinessProperties; import cn.iocoder.yudao.framework.web.config.BusinessProperties;
...@@ -9,14 +8,12 @@ import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; ...@@ -9,14 +8,12 @@ import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsCodeDO; import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsCodeDO;
import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsCodeMapper; import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsCodeMapper;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
import cn.iocoder.yudao.module.system.framework.sms.SmsCodeProperties; import cn.iocoder.yudao.module.system.framework.sms.SmsCodeProperties;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Date; import java.util.Date;
import java.util.Objects;
import static cn.hutool.core.util.RandomUtil.randomInt; import static cn.hutool.core.util.RandomUtil.randomInt;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
...@@ -44,38 +41,38 @@ public class SmsCodeServiceImpl implements SmsCodeService { ...@@ -44,38 +41,38 @@ public class SmsCodeServiceImpl implements SmsCodeService {
@Override @Override
public void sendSmsCode(SmsCodeSendReqDTO reqDTO) { public void sendSmsCode(SmsCodeSendReqDTO reqDTO) {
SmsSceneEnum sceneEnum = SmsSceneEnum.getCodeByScene(reqDTO.getScene()); // SmsSceneEnum sceneEnum = SmsSceneEnum.getCodeByScene(reqDTO.getScene());
Assert.notNull(sceneEnum, "验证码场景({}) 查找不到配置", reqDTO.getScene()); // Assert.notNull(sceneEnum, "验证码场景({}) 查找不到配置", reqDTO.getScene());
// if (Objects.nonNull(businessProperties) && businessProperties.isDebug()){ // if (Objects.nonNull(businessProperties) && businessProperties.isDebug()) {
// // TODO debug业务模式下不需要生成发送验证码,固定使用9999 // // TODO debug业务模式下不需要生成发送验证码,固定使用9999
// return; // return;
// } // }
// 创建验证码 // 创建验证码
String code = createSmsCode(reqDTO.getMobile(), reqDTO.getScene(), reqDTO.getCreateIp()); String code = createSmsCode(reqDTO.getMobile(), reqDTO.getNodeValue(), reqDTO.getCreateIp());
// 发送验证码 // 发送验证码
smsSendService.sendSingleSms(reqDTO.getAreaCode() + reqDTO.getMobile(), null, null, smsSendService.sendSingleSmsV2(reqDTO.getAreaCode() + reqDTO.getMobile(), null, null, reqDTO.getNodeValue(), reqDTO.getAreaCode(), reqDTO.getIsOrders(), reqDTO.getIsTransport(),
sceneEnum.getTemplateCode(), MapUtil.of("code", code)); reqDTO.getTransportId(), reqDTO.getMessageType(), MapUtil.of("code", code), null, 1);
} }
private String createSmsCode(String mobile, Integer scene, String ip) { private String createSmsCode(String mobile, String nodeValue, String ip) {
// 校验是否可以发送验证码,不用筛选场景 // 校验是否可以发送验证码,不用筛选场景
SmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, null, null); SmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, null, null);
if (lastSmsCode != null) { // if (lastSmsCode != null) {
if (lastSmsCode.getTodayIndex() >= smsCodeProperties.getSendMaximumQuantityPerDay()) { // 超过当天发送的上限。 // if (lastSmsCode.getTodayIndex() >= smsCodeProperties.getSendMaximumQuantityPerDay()) { // 超过当天发送的上限。
throw ServiceExceptionUtil.exception(SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY); // throw ServiceExceptionUtil.exception(SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY);
} // }
if (System.currentTimeMillis() - lastSmsCode.getCreateTime().getTime() // if (System.currentTimeMillis() - lastSmsCode.getCreateTime().getTime()
< smsCodeProperties.getSendFrequency().toMillis()) { // 发送过于频繁 // < smsCodeProperties.getSendFrequency().toMillis()) { // 发送过于频繁
throw ServiceExceptionUtil.exception(SMS_CODE_SEND_TOO_FAST); // throw ServiceExceptionUtil.exception(SMS_CODE_SEND_TOO_FAST);
} // }
// TODO 芋艿:提升,每个 IP 每天可发送数量 // // TODO 芋艿:提升,每个 IP 每天可发送数量
// TODO 芋艿:提升,每个 IP 每小时可发送数量 // // TODO 芋艿:提升,每个 IP 每小时可发送数量
} // }
// 创建验证码记录 // 创建验证码记录
String code = String.valueOf(randomInt(smsCodeProperties.getBeginCode(), smsCodeProperties.getEndCode() + 1)); String code = String.valueOf(randomInt(smsCodeProperties.getBeginCode(), smsCodeProperties.getEndCode() + 1));
SmsCodeDO newSmsCode = SmsCodeDO.builder().mobile(mobile).code(code) SmsCodeDO newSmsCode = SmsCodeDO.builder().mobile(mobile).code(code)
.scene(scene).todayIndex(lastSmsCode != null ? lastSmsCode.getTodayIndex() + 1 : 1) .nodeValue(nodeValue).todayIndex(lastSmsCode != null ? lastSmsCode.getTodayIndex() + 1 : 1)
.createIp(ip).used(false).build(); .createIp(ip).used(false).build();
smsCodeMapper.insert(newSmsCode); smsCodeMapper.insert(newSmsCode);
return code; return code;
...@@ -84,20 +81,20 @@ public class SmsCodeServiceImpl implements SmsCodeService { ...@@ -84,20 +81,20 @@ public class SmsCodeServiceImpl implements SmsCodeService {
@Override @Override
public void useSmsCode(SmsCodeUseReqDTO reqDTO) { public void useSmsCode(SmsCodeUseReqDTO reqDTO) {
if (Objects.equals(reqDTO.getCode(), "9999")) { // if (Objects.equals(reqDTO.getCode(), "9999")) {
return; // return;
} // }
//
if (Objects.nonNull(businessProperties) && businessProperties.isDebug()) { // if (Objects.nonNull(businessProperties) && businessProperties.isDebug()) {
// TODO debug业务模式下不需要生成发送验证码,固定使用9999 // // TODO debug业务模式下不需要生成发送验证码,固定使用9999
if (Objects.equals(reqDTO.getCode(), "9999")) { // if (Objects.equals(reqDTO.getCode(), "9999")) {
return; // return;
} else { // } else {
throw ServiceExceptionUtil.exception(SMS_CODE_NOT_FOUND); // throw ServiceExceptionUtil.exception(SMS_CODE_NOT_FOUND);
} // }
} // }
// 检测验证码是否有效 // 检测验证码是否有效
SmsCodeDO lastSmsCode = this.checkSmsCode0(reqDTO.getMobile(), reqDTO.getCode(), reqDTO.getScene()); SmsCodeDO lastSmsCode = this.checkSmsCode0(reqDTO.getMobile(), reqDTO.getCode(), reqDTO.getNodeValue());
// 使用验证码 // 使用验证码
smsCodeMapper.updateById(SmsCodeDO.builder().id(lastSmsCode.getId()) smsCodeMapper.updateById(SmsCodeDO.builder().id(lastSmsCode.getId())
.used(true).usedTime(new Date()).usedIp(reqDTO.getUsedIp()).build()); .used(true).usedTime(new Date()).usedIp(reqDTO.getUsedIp()).build());
...@@ -105,12 +102,12 @@ public class SmsCodeServiceImpl implements SmsCodeService { ...@@ -105,12 +102,12 @@ public class SmsCodeServiceImpl implements SmsCodeService {
@Override @Override
public void checkSmsCode(SmsCodeCheckReqDTO reqDTO) { public void checkSmsCode(SmsCodeCheckReqDTO reqDTO) {
checkSmsCode0(reqDTO.getMobile(), reqDTO.getCode(), reqDTO.getScene()); checkSmsCode0(reqDTO.getMobile(), reqDTO.getCode(), reqDTO.getNodeValue());
} }
public SmsCodeDO checkSmsCode0(String mobile, String code, Integer scene) { public SmsCodeDO checkSmsCode0(String mobile, String code, String nodeValue) {
// 校验验证码 // 校验验证码
SmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, code, scene); SmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, code, nodeValue);
// 若验证码不存在,抛出异常 // 若验证码不存在,抛出异常
if (lastSmsCode == null) { if (lastSmsCode == null) {
throw ServiceExceptionUtil.exception(SMS_CODE_NOT_FOUND); throw ServiceExceptionUtil.exception(SMS_CODE_NOT_FOUND);
......
package cn.iocoder.yudao.module.system.service.sms; package cn.iocoder.yudao.module.system.service.sms;
import cn.iocoder.yudao.framework.sms.core.client.impl.sendchamp.SendchampSmsClient;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogExportReqVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO; import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO;
...@@ -36,6 +37,27 @@ public interface SmsLogService { ...@@ -36,6 +37,27 @@ public interface SmsLogService {
Long createSmsLog(String mobile, Long userId, Integer userType, Boolean isSend, Long createSmsLog(String mobile, Long userId, Integer userType, Boolean isSend,
SmsTemplateDO template, String templateContent, Map<String, Object> templateParams, String apiTemplateId, Long smsLogId); SmsTemplateDO template, String templateContent, Map<String, Object> templateParams, String apiTemplateId, Long smsLogId);
/**
* 创建短信日志
*
* @param mobile 手机号
* @param userId 用户编号
* @param userType 用户类型
* @param isSend 是否发送
* @param template 短信模板
* @param templateContent 短信内容
* @param templateParams 短信参数
* @param apiTemplateId 短信 API 的模板编号
* @param smsLogId 重发短信时的原短信日志id
* @param nodeId 节点id
* @param nodeTemplateSn 节点模板序列号
* @param messageType 发送类型
* @return 发送日志编号
*/
Long createSmsLogV2(String mobile, Long userId, Integer userType, Boolean isSend,
SmsTemplateDO template, String templateContent, Map<String, Object> templateParams, String apiTemplateId, Long smsLogId,
Long nodeId, Integer nodeTemplateSn, Integer messageType);
/** /**
* 更新日志的发送结果 * 更新日志的发送结果
* *
...@@ -90,4 +112,11 @@ public interface SmsLogService { ...@@ -90,4 +112,11 @@ public interface SmsLogService {
void updateBatchSmsLog(List<SmsLogDO> resendSmsLogList); void updateBatchSmsLog(List<SmsLogDO> resendSmsLogList);
List<SmsLogDO> selectReceiveList();
/**
* 更新sendchamp回调状态
* @param smsReceiveStatus
*/
void updateSendchampReceive(SendchampSmsClient.SmsReceiveStatus smsReceiveStatus);
} }
package cn.iocoder.yudao.module.system.service.sms; package cn.iocoder.yudao.module.system.service.sms;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.sms.core.client.impl.sendchamp.SendchampSmsClient;
import cn.iocoder.yudao.framework.sms.core.enums.ReceiveStatusEnum;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogExportReqVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO; import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO;
...@@ -55,6 +57,57 @@ public class SmsLogServiceImpl implements SmsLogService { ...@@ -55,6 +57,57 @@ public class SmsLogServiceImpl implements SmsLogService {
return logDO.getId(); return logDO.getId();
} }
/**
* 创建短信日志
*
* @param mobile 手机号
* @param userId 用户编号
* @param userType 用户类型
* @param isSend 是否发送
* @param template 短信模板
* @param templateContent 短信内容
* @param templateParams 短信参数
* @param apiTemplateId 短信 API 的模板编号
* @param smsLogId 重发短信时的原短信日志id
* @param nodeId 节点id
* @param nodeTemplateSn 节点模板序列号
* @param messageType 发送类型
* @return 发送日志编号
*/
@Override
public Long createSmsLogV2(String mobile, Long userId, Integer userType, Boolean isSend,
SmsTemplateDO template, String templateContent, Map<String, Object> templateParams, String apiTemplateId, Long smsLogId,
Long nodeId, Integer nodeTemplateSn, Integer messageType) {
SmsLogDO.SmsLogDOBuilder logBuilder = SmsLogDO.builder();
// 根据是否要发送,设置状态
logBuilder.sendStatus(Objects.equals(isSend, true) ? SmsSendStatusEnum.INIT.getStatus()
: SmsSendStatusEnum.IGNORE.getStatus());
// 设置手机相关字段
logBuilder.mobile(mobile).userId(userId).userType(userType);
// 设置模板相关字段
logBuilder.templateId(template.getId()).templateCode(template.getCode()).templateType(template.getType());
logBuilder.templateContent(templateContent).templateParams(templateParams)
.apiTemplateId(apiTemplateId);
// 设置渠道相关字段
logBuilder.channelId(template.getChannelId()).channelCode(template.getChannelCode());
// 设置接收相关字段
logBuilder.receiveStatus(SmsReceiveStatusEnum.INIT.getStatus());
// 重发短信的原短信日志id
logBuilder.smsLogId(smsLogId);
// 默认重发次数是0
logBuilder.num(0);
// 节点id
logBuilder.nodeId(nodeId);
// 节点模板序列号
logBuilder.nodeTemplateSn(nodeTemplateSn);
// 默认重发次数是0
logBuilder.messageType(messageType);
// 发送类型
SmsLogDO logDO = logBuilder.build();
smsLogMapper.insert(logDO);
return logDO.getId();
}
@Override @Override
public void updateSmsSendResult(Long id, Integer sendCode, String sendMsg, public void updateSmsSendResult(Long id, Integer sendCode, String sendMsg,
String apiSendCode, String apiSendMsg, String apiSendCode, String apiSendMsg,
...@@ -100,4 +153,22 @@ public class SmsLogServiceImpl implements SmsLogService { ...@@ -100,4 +153,22 @@ public class SmsLogServiceImpl implements SmsLogService {
public void updateBatchSmsLog(List<SmsLogDO> resendSmsLogList) { public void updateBatchSmsLog(List<SmsLogDO> resendSmsLogList) {
smsLogMapper.updateBatch(resendSmsLogList); smsLogMapper.updateBatch(resendSmsLogList);
} }
@Override
public List<SmsLogDO> selectReceiveList() {
return smsLogMapper.selectReceiveList();
}
@Override
public void updateSendchampReceive(SendchampSmsClient.SmsReceiveStatus smsReceiveStatus) {
if ("delivered".equals(smsReceiveStatus.getStatus())) {
List<SmsLogDO> smsLogDOS = smsLogMapper.selectList(new SmsLogExportReqVO().setApiSerialNo(smsReceiveStatus.getReference()));
if (smsLogDOS != null) {
SmsLogDO smsLogDO = smsLogDOS.get(0);
smsLogDO.setReceiveStatus(ReceiveStatusEnum.Receive_STATUS_10.getValue());
smsLogDO.setReceiveTime(new Date());
smsLogMapper.updateById(smsLogDO);
}
}
}
} }
package cn.iocoder.yudao.module.system.service.sms;
import java.util.*;
import javax.validation.*;
import cn.iocoder.yudao.framework.mybatis.core.vo.PageVO;
import cn.iocoder.yudao.framework.mybatis.core.service.IService;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.smsNode.*;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsNodeDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
/**
* 短信节点 Service 接口
*
* @author jayden
*/
public interface SmsNodeService extends IService<SmsNodeDO> {
/**
* 创建短信节点
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createSmsNode(@Valid SmsNodeCreateReqVO createReqVO);
/**
* 更新短信节点
*
* @param updateReqVO 更新信息
*/
void updateSmsNode(@Valid SmsNodeUpdateReqVO updateReqVO);
/**
* 删除短信节点
*
* @param id 编号
*/
void deleteSmsNode(Long id);
/**
* 获得短信节点
*
* @param id 编号
* @return 短信节点
*/
SmsNodeDO getSmsNode(Long id);
/**
* 获得短信节点列表
*
* @param ids 编号
* @return 短信节点列表
*/
List<SmsNodeDO> getSmsNodeList(Collection<Long> ids);
/**
* 获得短信节点分页
*
* @param page 分页查询
* @param query 查询
* @return 短信节点分页
*/
PageResult<SmsNodeDO> getSmsNodePage(SmsNodeQueryVO query, PageVO page);
/**
* 获得短信节点列表, 用于 Excel 导出
*
* @param query 查询
* @return 短信节点列表
*/
List<SmsNodeDO> getSmsNodeList(SmsNodeQueryVO query);
/**
* 构建缓存键
* 根据SmsNodeDO对象的属性生成一个唯一的缓存键
* 这是为了确保每个配置项在缓存中都有一个唯一的标识
*
* @param smsNodeDO SmsNodeDO 对象
* @return 缓存键
*/
String buildCacheKey(SmsNodeDO smsNodeDO);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment