From ee832e327e48db895ef0aa1b9130b92ba6c9d1d5 Mon Sep 17 00:00:00 2001 From: zjf <1799650030@qq.com> Date: Fri, 11 Apr 2025 15:10:51 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=9F=AD=E4=BF=A1=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/VisitorsController.java | 2 +- .../thirdparty/dto/sms/SmsRequest.java | 18 ++++ .../thirdparty/dto/sms/SmsResponse.java | 10 ++ .../pjcloud/thirdparty/config/SmsConfig.java | 86 +++++++++++++++ .../thirdparty/config/SmsTemplateConfig.java | 9 ++ .../thirdparty/controller/SmsController.java | 33 ++++++ .../thirdparty/exception/SmsException.java | 11 ++ .../thirdparty/service/SmsHttpClient.java | 44 ++++++++ .../thirdparty/service/SmsService.java | 102 ++++++++++++++++++ .../src/main/resources/application.yml | 19 ++++ 10 files changed, 333 insertions(+), 1 deletion(-) create mode 100644 pjcloud-thirdparty/pjcloud-thirdparty-api/src/main/java/com/bsd/pjcloud/thirdparty/dto/sms/SmsRequest.java create mode 100644 pjcloud-thirdparty/pjcloud-thirdparty-api/src/main/java/com/bsd/pjcloud/thirdparty/dto/sms/SmsResponse.java create mode 100644 pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/config/SmsConfig.java create mode 100644 pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/config/SmsTemplateConfig.java create mode 100644 pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/controller/SmsController.java create mode 100644 pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/exception/SmsException.java create mode 100644 pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/SmsHttpClient.java create mode 100644 pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/SmsService.java diff --git a/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/controller/VisitorsController.java b/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/controller/VisitorsController.java index 7f04c0ce..4d151181 100644 --- a/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/controller/VisitorsController.java +++ b/pjcloud-integrated/pjcloud-integrated-biz/src/main/java/com/bsd/pjcloud/integrated/controller/VisitorsController.java @@ -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) { diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-api/src/main/java/com/bsd/pjcloud/thirdparty/dto/sms/SmsRequest.java b/pjcloud-thirdparty/pjcloud-thirdparty-api/src/main/java/com/bsd/pjcloud/thirdparty/dto/sms/SmsRequest.java new file mode 100644 index 00000000..22938b42 --- /dev/null +++ b/pjcloud-thirdparty/pjcloud-thirdparty-api/src/main/java/com/bsd/pjcloud/thirdparty/dto/sms/SmsRequest.java @@ -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; +} \ No newline at end of file diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-api/src/main/java/com/bsd/pjcloud/thirdparty/dto/sms/SmsResponse.java b/pjcloud-thirdparty/pjcloud-thirdparty-api/src/main/java/com/bsd/pjcloud/thirdparty/dto/sms/SmsResponse.java new file mode 100644 index 00000000..7bf18c52 --- /dev/null +++ b/pjcloud-thirdparty/pjcloud-thirdparty-api/src/main/java/com/bsd/pjcloud/thirdparty/dto/sms/SmsResponse.java @@ -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; +} \ No newline at end of file diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/config/SmsConfig.java b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/config/SmsConfig.java new file mode 100644 index 00000000..c5a2e66e --- /dev/null +++ b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/config/SmsConfig.java @@ -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 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 params; + } + + // 通用设置 + @Getter + @Setter + public static class Settings { + @NotBlank + private String defaultExtCode; + + @Min(60) + private int timeoutSeconds; + } +} \ No newline at end of file diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/config/SmsTemplateConfig.java b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/config/SmsTemplateConfig.java new file mode 100644 index 00000000..1456149d --- /dev/null +++ b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/config/SmsTemplateConfig.java @@ -0,0 +1,9 @@ +package com.bsd.pjcloud.thirdparty.config; + +import lombok.Data; + +@Data +public class SmsTemplateConfig { + private String content; + private String signName; +} \ No newline at end of file diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/controller/SmsController.java b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/controller/SmsController.java new file mode 100644 index 00000000..e6bff3e8 --- /dev/null +++ b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/controller/SmsController.java @@ -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 params + ) { + smsService.sendSms(mobile, "20657", params); + } + +} \ No newline at end of file diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/exception/SmsException.java b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/exception/SmsException.java new file mode 100644 index 00000000..4238747a --- /dev/null +++ b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/exception/SmsException.java @@ -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); + } +} \ No newline at end of file diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/SmsHttpClient.java b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/SmsHttpClient.java new file mode 100644 index 00000000..5d5c459b --- /dev/null +++ b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/SmsHttpClient.java @@ -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 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); +// } +} \ No newline at end of file diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/SmsService.java b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/SmsService.java new file mode 100644 index 00000000..e82f0ac2 --- /dev/null +++ b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/service/SmsService.java @@ -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 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 params, List requiredParams) { + for (String param : requiredParams) { + if (!params.containsKey(param)) { + throw new SmsException("缺少必要参数: " + param); + } + } + + String result = template; + for (Map.Entry 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); + } + } +} \ No newline at end of file diff --git a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/resources/application.yml b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/resources/application.yml index 0dca5d90..b07af19d 100644 --- a/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/resources/application.yml +++ b/pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/resources/application.yml @@ -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 + +