luotaiqian 2026-07-06 17:05:24 +08:00
parent 8ca81101ab
commit 2829d082ae
4 changed files with 291 additions and 28 deletions

View File

@ -1,7 +1,6 @@
package org.qinan.safetyeval.adapter.web;
import cn.hutool.json.JSONUtil;
import com.jjb.saas.message.client.message.facede.MessageFacade;
import com.jjb.saas.message.client.message.request.MessageSendCmd;
import com.jjb.saas.message.client.message.request.MessageTargetCmd;
import io.swagger.annotations.Api;
@ -9,11 +8,11 @@ import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.qinan.safetyeval.client.api.AccountApi;
import org.qinan.safetyeval.client.co.AccountCO;
import org.qinan.safetyeval.client.dto.*;
import org.qinan.safetyeval.domain.exception.BizException;
import org.qinan.safetyeval.infrastructure.remote.SmsRemote;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@ -39,11 +38,9 @@ public class AccountController {
@Resource
private AccountApi accountApi;
@DubboReference
private MessageFacade messageFacade;
@Resource
private SmsRemote smsRemote;
@Value("${sms.sign.sourceCode:}")
private String sourceCode;
@ApiOperation("新增用户")
@PostMapping("/save")
@ -78,27 +75,7 @@ public class AccountController {
@ApiOperation("发送短信")
@PostMapping("/sendMessage")
public com.alibaba.cola.dto.SingleResponse<Boolean> sendMessage(@Validated @RequestBody SendMessageCmd cmd) {
if (StringUtils.isBlank(sourceCode)) {
throw new BizException(SMS_SOURCE_CODE_NOT_EXIST);
}
MessageSendCmd messageSendCmd = new MessageSendCmd();
messageSendCmd.setBusinessId(UUID.randomUUID().toString().replace("-", ""));
MessageTargetCmd messageTargetCmd = new MessageTargetCmd();
messageTargetCmd.setMobile(cmd.getMobile());
messageSendCmd.setTargetCmd(messageTargetCmd);
messageSendCmd.setNeedTokenEnum(false);
messageSendCmd.setSourceCode(sourceCode);
Map<String, Object> codeMap = new HashMap<>();
codeMap.put("valid_code", "123456");
messageSendCmd.setParams(codeMap);
log.info("发送短信参数: messageSendCmd: {}", JSONUtil.toJsonStr(messageSendCmd));
com.alibaba.cola.dto.SingleResponse<Boolean> result = messageFacade.send(messageSendCmd);
log.info("发送短信结果: result: {}", JSONUtil.toJsonStr(result));
return result;
return smsRemote.sign(cmd.getMobile());
}
@ApiOperation("用户同步gbs")

View File

@ -0,0 +1,46 @@
package org.qinan.safetyeval.infrastructure.remote;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* v2
*
* @author safety-eval
*/
@Data
@Component
@ConfigurationProperties(prefix = "zt.sms")
public class SmsProperties {
/**
*
*/
private String username = "qhdzyhy";
/**
* md5
*/
private String password = "Zcloud@88888";
/**
* v2
*/
private String apiUrl = "https://api-shss.zthysms.com/v2/sendSms";
/**
* 使
*/
private String signature = "【秦安安全】";
/**
*
*/
private int connectTimeout = 10000;
/**
*
*/
private int readTimeout = 30000;
}

View File

@ -0,0 +1,240 @@
package org.qinan.safetyeval.infrastructure.remote;
import cn.hutool.json.JSONUtil;
import com.alibaba.cola.dto.SingleResponse;
import com.jjb.saas.message.client.message.facede.MessageFacade;
import com.jjb.saas.message.client.message.request.MessageSendCmd;
import com.jjb.saas.message.client.message.request.MessageTargetCmd;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.qinan.safetyeval.client.dto.SendMessageCmd;
import org.qinan.safetyeval.domain.exception.BizException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.client.RestTemplate;
import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import static org.qinan.safetyeval.domain.exception.ErrorCode.SMS_SOURCE_CODE_NOT_EXIST;
/**
* v2
* <p>
*
* - https://api-shss.zthysms.com/v2/sendSms
* - POSTContent-Type: application/json
* - tKey = Unix password = md5(md5() + tKey)
* - 200 4023 4001 4009
*
* @author safety-eval
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class SmsRemote {
private final SmsProperties smsProperties;
private RestTemplate restTemplate;
@DubboReference
private MessageFacade messageFacade;
@Value("${sms.sign.sourceCode:}")
private String sourceCode;
@PostConstruct
public void init() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(smsProperties.getConnectTimeout());
factory.setReadTimeout(smsProperties.getReadTimeout());
this.restTemplate = new RestTemplate(factory);
}
/**
* ID 793059
*
* @param mobile
* @param validCode
* @return
*/
public SingleResponse<String> sendRegisterVerificationCode(String mobile, String validCode) {
// 模板内容:[秦安安全]您正在注册本系统,验证码:{valid_code}5分钟内有效请勿泄露给他人如非本人操作请忽略本条短信。
String content = smsProperties.getSignature()
+ "您正在注册本系统,验证码:" + validCode
+ "5分钟内有效请勿泄露给他人如非本人操作请忽略本条短信。";
return sendSms(mobile, content);
}
public SingleResponse<Boolean> sign(String mobile) {
String validCode = generateVerificationCode();
String content = "【秦安安全】您正在注册本系统,验证码:" + validCode + "5分钟内有效请勿泄露给他人如非本人操作请忽略本条短信。";
sendSms(mobile, content);
return SingleResponse.of(true);
}
/**
* v2
*
* @param mobile
* @param content
* @return
*/
public SingleResponse<String> sendSms(String mobile, String content) {
log.info("助通短信发送请求 - mobile={}, content={}", mobile, content);
SingleResponse<String> response;
try {
String jsonBody = buildRequestBody(mobile, content);
log.info("助通短信发送请求体: {}", jsonBody);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAcceptCharset(Collections.singletonList(StandardCharsets.UTF_8));
HttpEntity<String> requestEntity = new HttpEntity<>(jsonBody, headers);
ResponseEntity<String> resp =
restTemplate.postForEntity(smsProperties.getApiUrl(), requestEntity, String.class);
String body = resp.getBody();
log.info("助通短信发送响应: status={}, body={}", resp.getStatusCodeValue(), body);
// 融合云成功返回 HTTP 200且响应 JSON 中 code=200
if (resp.getStatusCodeValue() == 200) {
response = SingleResponse.of(body);
} else {
log.warn("助通短信发送失败 - mobile={}, body={}", mobile, body);
throw new BizException("SMS_SEND_FAIL", "短信发送失败: " + body);
}
} catch (BizException e) {
throw e;
} catch (Exception e) {
log.error("助通短信发送异常 - mobile={}", mobile, e);
throw new BizException("SMS_SEND_ERROR", "短信发送异常: " + e.getMessage());
}
return response;
}
/**
* 6
*
* @return 6
*/
public String generateVerificationCode() {
return String.format("%06d", ThreadLocalRandom.current().nextInt(1000000));
}
// ==================== 内部方法 ====================
/**
* v2 JSON
*/
private String buildRequestBody(String mobile, String content) {
String tKey = String.valueOf(System.currentTimeMillis() / 1000);
String finalPassword = md5(md5(smsProperties.getPassword()) + tKey);
return "{"
+ "\"username\":\"" + smsProperties.getUsername() + "\","
+ "\"password\":\"" + finalPassword + "\","
+ "\"tKey\":\"" + tKey + "\","
+ "\"mobile\":\"" + mobile + "\","
+ "\"content\":\"" + escapeJson(content) + "\""
+ "}";
}
/**
* MD5
*/
private String md5(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
sb.append(String.format("%02x", b & 0xff));
}
return sb.toString();
} catch (Exception e) {
throw new BizException("SMS_MD5_ERROR", "MD5加密失败: " + e.getMessage());
}
}
/**
* JSON
*/
private String escapeJson(String input) {
if (input == null) {
return "";
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < input.length(); i++) {
char c = input.charAt(i);
switch (c) {
case '"':
sb.append("\\\"");
break;
case '\\':
sb.append("\\\\");
break;
case '\n':
sb.append("\\n");
break;
case '\r':
sb.append("\\r");
break;
case '\t':
sb.append("\\t");
break;
default:
sb.append(c);
}
}
return sb.toString();
}
@ApiOperation("发送短信")
@PostMapping("/sendMessage")
public com.alibaba.cola.dto.SingleResponse<Boolean> sendMessage(@Validated @RequestBody SendMessageCmd cmd) {
if (StringUtils.isBlank(sourceCode)) {
throw new BizException(SMS_SOURCE_CODE_NOT_EXIST);
}
MessageSendCmd messageSendCmd = new MessageSendCmd();
messageSendCmd.setBusinessId(UUID.randomUUID().toString().replace("-", ""));
MessageTargetCmd messageTargetCmd = new MessageTargetCmd();
messageTargetCmd.setMobile(cmd.getMobile());
messageSendCmd.setTargetCmd(messageTargetCmd);
messageSendCmd.setNeedTokenEnum(false);
messageSendCmd.setSourceCode(sourceCode);
Map<String, Object> codeMap = new HashMap<>();
codeMap.put("valid_code", "123456");
messageSendCmd.setParams(codeMap);
log.info("发送短信参数: messageSendCmd: {}", JSONUtil.toJsonStr(messageSendCmd));
com.alibaba.cola.dto.SingleResponse<Boolean> result = messageFacade.send(messageSendCmd);
log.info("发送短信结果: result: {}", JSONUtil.toJsonStr(result));
return result;
}
}

View File

@ -1,4 +1,4 @@
package org.qinan.safetyeval.start.sms;
package org.qinan.safetyeval.infrastructure.remote;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;