新增短信功能
This commit is contained in:
1 parent
5924d8d389
commit
ee832e327e
10 files changed
+333
-1
No files matched your search
+1
-1
@@ -134,7 +134,7 @@ public class VisitorsController {
|
||||
@Operation(summary = "app新增访客表" , description = "app新增访客表" )
|
||||
@SysLog("新增访客表" )
|
||||
@PostMapping("/app/save")
|
||||
@Idempotent(key = "#key", expireTime = 10, info = "请勿重复提交")
|
||||
@Idempotent(key = "#", expireTime = 10, info = "请勿重复提交")
|
||||
@Inner(value = false)
|
||||
public R save(@RequestBody VisitorsAppDTO visitors) {
|
||||
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.bsd.pjcloud.thirdparty.dto.sms;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class SmsRequest {
|
||||
private String secretName;
|
||||
private String secretKey;
|
||||
private String mobile;
|
||||
private String content;
|
||||
private String extCode;
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package com.bsd.pjcloud.thirdparty.dto.sms;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class SmsResponse {
|
||||
private String code;
|
||||
private String message;
|
||||
private String taskId;
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package com.bsd.pjcloud.thirdparty.config;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "sms")
|
||||
@Getter
|
||||
@Setter
|
||||
@Validated
|
||||
public class SmsConfig {
|
||||
@NotNull
|
||||
private ApiConfig api;
|
||||
|
||||
@NotEmpty
|
||||
private Map<String, TemplateConfig> templates;
|
||||
|
||||
@NotNull
|
||||
private Settings settings;
|
||||
|
||||
// API配置
|
||||
@Getter
|
||||
@Setter
|
||||
public static class ApiConfig {
|
||||
@NotBlank
|
||||
private String baseUrl;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
private Secret secret;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
private Endpoints endpoints;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class Secret {
|
||||
@NotBlank
|
||||
private String name;
|
||||
|
||||
@NotBlank
|
||||
private String key;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class Endpoints {
|
||||
@NotBlank
|
||||
private String send;
|
||||
}
|
||||
}
|
||||
|
||||
// 模板配置
|
||||
@Getter
|
||||
@Setter
|
||||
public static class TemplateConfig {
|
||||
@NotBlank
|
||||
private String content;
|
||||
|
||||
@NotEmpty
|
||||
private List<String> params;
|
||||
}
|
||||
|
||||
// 通用设置
|
||||
@Getter
|
||||
@Setter
|
||||
public static class Settings {
|
||||
@NotBlank
|
||||
private String defaultExtCode;
|
||||
|
||||
@Min(60)
|
||||
private int timeoutSeconds;
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package com.bsd.pjcloud.thirdparty.config;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class SmsTemplateConfig {
|
||||
private String content;
|
||||
private String signName;
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.bsd.pjcloud.thirdparty.controller;
|
||||
|
||||
import com.bsd.pjcloud.common.security.annotation.Inner;
|
||||
import com.bsd.pjcloud.thirdparty.service.SmsService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/sms")
|
||||
@RequiredArgsConstructor
|
||||
public class SmsController {
|
||||
|
||||
private final SmsService smsService;
|
||||
|
||||
// 简单发送
|
||||
@PostMapping("/verify-code")
|
||||
public void sendVerifyCode(@RequestParam String mobile) {
|
||||
smsService.sendSms(mobile, "20657");
|
||||
}
|
||||
|
||||
// 带参数发送
|
||||
@PostMapping("/order-notify")
|
||||
@Inner(value = false)
|
||||
public void sendOrderNotify(
|
||||
@RequestParam String mobile,
|
||||
@RequestBody Map<String, String> params
|
||||
) {
|
||||
smsService.sendSms(mobile, "20657", params);
|
||||
}
|
||||
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package com.bsd.pjcloud.thirdparty.exception;
|
||||
|
||||
public class SmsException extends RuntimeException {
|
||||
public SmsException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public SmsException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.bsd.pjcloud.thirdparty.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class SmsHttpClient {
|
||||
|
||||
private static final RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
@Value("${sms.settings.timeout-seconds}")
|
||||
private int timeoutSeconds;
|
||||
|
||||
public String post(String url, Object body) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
|
||||
HttpEntity<Object> entity = new HttpEntity<>(body, headers);
|
||||
|
||||
return restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.POST,
|
||||
entity,
|
||||
String.class
|
||||
).getBody();
|
||||
}
|
||||
|
||||
// @Bean
|
||||
// public RestTemplate restTemplate() {
|
||||
// SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
|
||||
// factory.setConnectTimeout(timeoutSeconds * 1000);
|
||||
// factory.setReadTimeout(timeoutSeconds * 1000);
|
||||
// return new RestTemplate(factory);
|
||||
// }
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
package com.bsd.pjcloud.thirdparty.service;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.bsd.pjcloud.thirdparty.config.SmsConfig;
|
||||
import com.bsd.pjcloud.thirdparty.dto.sms.SmsRequest;
|
||||
import com.bsd.pjcloud.thirdparty.dto.sms.SmsResponse;
|
||||
import com.bsd.pjcloud.thirdparty.exception.SmsException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class SmsService {
|
||||
private final SmsConfig config;
|
||||
private final SmsHttpClient httpClient;
|
||||
|
||||
/**
|
||||
* 发送短信(最简版)
|
||||
* @param mobile 手机号
|
||||
* @param templateId 模板ID
|
||||
*/
|
||||
public void sendSms(String mobile, String templateId) {
|
||||
validateMobile(mobile);
|
||||
SmsConfig.TemplateConfig template = getTemplate(templateId);
|
||||
|
||||
if (!template.getParams().isEmpty()) {
|
||||
throw new SmsException("模板需要参数,请使用sendSmsWithParams方法");
|
||||
}
|
||||
|
||||
sendRequest(buildRequest(mobile, template.getContent()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送含参数的短信
|
||||
* @param mobile 手机号
|
||||
* @param templateId 模板ID
|
||||
* @param params 参数Map
|
||||
*/
|
||||
public void sendSms(String mobile, String templateId, Map<String, String> params) {
|
||||
validateMobile(mobile);
|
||||
SmsConfig.TemplateConfig template = getTemplate(templateId);
|
||||
|
||||
String content = replacePlaceholders(template.getContent(), params, template.getParams());
|
||||
sendRequest(buildRequest(mobile, content));
|
||||
}
|
||||
|
||||
private SmsRequest buildRequest(String mobile, String content) {
|
||||
return SmsRequest.builder()
|
||||
.secretName(config.getApi().getSecret().getName())
|
||||
.secretKey(config.getApi().getSecret().getKey())
|
||||
.mobile(mobile)
|
||||
.content(content)
|
||||
.extCode(config.getSettings().getDefaultExtCode())
|
||||
.build();
|
||||
}
|
||||
|
||||
private void sendRequest(SmsRequest request) {
|
||||
String url = config.getApi().getBaseUrl() + config.getApi().getEndpoints().getSend();
|
||||
String response = httpClient.post(url, request);
|
||||
handleResponse(response);
|
||||
}
|
||||
|
||||
private void handleResponse(String response) {
|
||||
SmsResponse resp = JSON.parseObject(response, SmsResponse.class);
|
||||
if (!"0".equals(resp.getCode())) {
|
||||
throw new SmsException("短信发送失败: " + resp.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private SmsConfig.TemplateConfig getTemplate(String templateId) {
|
||||
SmsConfig.TemplateConfig template = config.getTemplates().get(templateId);
|
||||
if (template == null) {
|
||||
throw new SmsException("无效的模板ID: " + templateId);
|
||||
}
|
||||
return template;
|
||||
}
|
||||
|
||||
private String replacePlaceholders(String template, Map<String, String> params, List<String> requiredParams) {
|
||||
for (String param : requiredParams) {
|
||||
if (!params.containsKey(param)) {
|
||||
throw new SmsException("缺少必要参数: " + param);
|
||||
}
|
||||
}
|
||||
|
||||
String result = template;
|
||||
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||
result = result.replace("{" + entry.getKey() + "}", entry.getValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void validateMobile(String mobile) {
|
||||
if (!mobile.matches("^1[3-9]\\d{9}$")) {
|
||||
throw new SmsException("无效的手机号码: " + mobile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,3 +86,22 @@ ks-discern:
|
||||
|
||||
|
||||
|
||||
sms:
|
||||
api:
|
||||
base-url: "https://api.028lk.com/Sms/Api/"
|
||||
secret:
|
||||
name: "${SMS_SECRET_NAME:ZJSK00022796}" # 从环境变量读取,带默认值
|
||||
key: "${SMS_SECRET_KEY:zxy@1234}"
|
||||
endpoints:
|
||||
send: "Send"
|
||||
|
||||
templates:
|
||||
20657: # 验证码模板
|
||||
content: "您好,您的验证码是{code},有效时间{s1}分钟,请勿转发!"
|
||||
params: ["code", "date"] # 需要的参数
|
||||
|
||||
settings:
|
||||
default-ext-code: "123"
|
||||
timeout-seconds: 300
|
||||
|
||||
|
||||
Reference in new issue
Block a user