This commit is contained in:
efren
2025-06-25 10:15:43 +08:00
parent d4a0d179ea
commit 983c4886ad
8 changed files with 450 additions and 0 deletions

View File

@ -0,0 +1,71 @@
package com.chinaunicom.mall.ebtp.extend.sms.client;
import com.chinaunicom.mall.ebtp.extend.sms.config.SmsFeignConfig;
import com.chinaunicom.mall.ebtp.extend.sms.entity.SmsBaseResponseDTO;
import com.chinaunicom.mall.ebtp.extend.sms.entity.SmsSendResponseDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.Map;
@FeignClient(name = "sms-service", url = "${spring.cloopen.url}", configuration = SmsFeignConfig.class)
public interface SmsClient {
/**
* 查询短信模板
* 返回:
{
"statusCode":"000000",
"totalCount":"2",
"TemplateSMS":[
{
"id":"1180212",
"title":"通知2",
"content":"【中远海运】{1}",
"status":"1",
"type":"1",
"dateCreated":"2022-03-27 12:34:28.0",
"dateUpdated":"2022-03-27 12:40:13.0",
"appendcode":"317806"
},{
"id":"142717",
"title":"集团航运管理平台短信通知",
"content":"【中远海运航标平台】{1}",
"status":"1",
"type":"1",
"dateCreated":"2016-12-16 12:04:36.0",
"dateUpdated":"2020-12-10 12:58:25.0",
"appendcode":"319026"
}
]
}
* @param request 请求参数
* @return 模板查询结果
*/
@PostMapping("/${spring.cloopen.softVersion}/Accounts/${spring.cloopen.accountSid}/SMS/QuerySMSTemplate")
SmsBaseResponseDTO querySmsTemplate(@RequestBody Map<String, String> request);
/**
* 发送模板短信
参数:
{
"datas":["测试内容"],// 模板替换参数
"appId":"8aaf070857f4daef0157fe76a316072b",
"to":"18686879363",// 发送给哪些手机号,多个用逗号分隔
"templateId":"1180212"/ 短信模板
}
返回:
{
"statusCode":"000000",
"templateSMS":{
"smsMessageSid":"b21c72a8c9844843b8d87ecdb10304fb",
"dateCreated":"20250620143237"
}
}
* @param request 请求参数
* @return 发送结果
*/
@PostMapping("/${spring.cloopen.softVersion}/Accounts/${spring.cloopen.accountSid}/SMS/TemplateSMS")
SmsSendResponseDTO sendTemplateSms(@RequestBody Map<String, Object> request);
}

View File

@ -0,0 +1,55 @@
package com.chinaunicom.mall.ebtp.extend.sms.config;
import com.chinaunicom.mall.ebtp.extend.sms.client.SmsClient;
import feign.RequestInterceptor;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
/**
* 短信服务Feign配置类
* SigParameter是REST API 验证参数
* URL后必须带有sig参数例如sig=ABCDEFG。
* 使用MD5加密账户ACCOUNT SID+ 账户授权令牌AUTH TOKEN + 时间戳。其中账户Id和账户授权令牌根据url的验证级别对应主账户或子账户短信业务接口对应主账户及主账号授权令牌。
* 时间戳是当前系统时间,格式"yyyyMMddHHmmss"。
* SigParameter参数需要大写
*/
@Configuration
public class SmsFeignConfig {
@Value("${spring.cloopen.accountSid}")
private String accountSid;
@Value("${spring.cloopen.authToken}")
private String authToken;
@Bean
@ConditionalOnBean(SmsClient.class)
public RequestInterceptor requestInterceptor() {
return template -> {
// 生成时间戳
String timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
// 计算Authorization
String auth = accountSid + ":" + timestamp;
String authorization = Base64.getEncoder().encodeToString(auth.getBytes());
// 计算SigParameter
String sig = DigestUtils.md5Hex(accountSid + authToken + timestamp).toUpperCase();
// 设置请求头
template.header("Accept", "application/json");
template.header("Content-Type", "application/json;charset=utf-8");
template.header("Authorization", authorization);
// 添加sig参数
template.query("sig", sig);
};
}
}

View File

@ -0,0 +1,93 @@
package com.chinaunicom.mall.ebtp.extend.sms.constant;
public enum SmsErrorEnum {
ACCOUNT_URL_FORMAT_ERROR(111100, "【账号】请求URL中账号格式不正确"),
ACCOUNT_AUTHORIZATION_HEADER_EMPTY(111101, "【账号】请求包头Authorization参数为空"),
ACCOUNT_AUTHORIZATION_BASE64_DECODE_FAIL(111102, "【账号】请求包头Authorization参数Base64解码失败"),
ACCOUNT_AUTHORIZATION_DECODE_FORMAT_ERROR(111103, "【账号】请求包头Authorization参数解码后格式有误"),
ACCOUNT_AUTHORIZATION_DECODE_ID_EMPTY(111104, "【账号】请求包头Authorization参数解码后账户ID为空"),
ACCOUNT_AUTHORIZATION_DECODE_TIMESTAMP_EMPTY(111105, "【账号】请求包头Authorization参数解码后时间戳为空"),
ACCOUNT_AUTHORIZATION_DECODE_TIMESTAMP_EXPIRED(111106, "【账号】请求包头Authorization参数解码后时间戳过期"),
ACCOUNT_AUTHORIZATION_ID_NOT_MATCH(111107, "【账号】请求包头Authorization参数中账户ID跟请求地址中的账户ID不一致"),
ACCOUNT_SIG_PARAM_EMPTY(111108, "【账号】请求地址Sig参数为空"),
ACCOUNT_SIG_VERIFY_FAIL(111109, "【账号】请求地址Sig校验失败"),
ACCOUNT_SOFTVERSION_ERROR(111110, "【账号】请求地址SoftVersion参数有误"),
ACCOUNT_CONCURRENT_LIMIT(111111, "【账号】接口请求超过规定的并发数"),
ACCOUNT_AUTHORIZATION_TIMESTAMP_FORMAT_ERROR(111113, "【账号】请求包头Authorization参数中时间戳格式有误请参考yyyyMMddHHmmss"),
ACCOUNT_PAUSED(111139, "【账号】主账户已暂停"),
ACCOUNT_CLOSED(111140, "【账号】主账户已关闭"),
ACCOUNT_NOT_EXIST(111141, "【账号】主账户不存在"),
ACCOUNT_BALANCE_NOT_ENOUGH(121002, "【账号】余额不足"),
SMS_TEST_TEMPLATE_NOT_BIND(113302, "【短信】正在使用云通讯测试模板且短信接收者不是绑定的测试号码"),
SMS_PARAM_PARSE_FAIL(160031, "【短信】参数解析失败"),
SMS_TEMPLATE_INVALID(160032, "【短信】短信模板无效"),
SMS_BLACK_WORD(160033, "【短信】短信存在黑词"),
SMS_NUMBER_BLACKLIST(160034, "【短信】号码黑名单"),
SMS_CONTENT_EMPTY(160035, "【短信】短信下发内容为空"),
SMS_TEMPLATE_TYPE_UNKNOWN(160036, "【短信】短信模板类型未知"),
SMS_CONTENT_LENGTH_LIMIT(160037, "【短信】短信内容长度限制"),
SMS_CODE_SEND_TOO_FREQUENT(160038, "【短信】短信验证码发送过频繁"),
SMS_TEMPLATE_NUMBER_DAY_LIMIT(160039, "【短信】超出同模板同号天发送次数上限"),
SMS_CODE_NUMBER_DAY_LIMIT(160040, "【短信】验证码超出同模板同号码天发送上限"),
SMS_NOTIFY_NUMBER_DAY_LIMIT(160041, "【短信】通知超出同模板同号码天发送上限"),
SMS_NUMBER_FORMAT_ERROR(160042, "【短信】号码格式有误"),
SMS_APP_TEMPLATE_ID_NOT_MATCH(160043, "【短信】应用与模板id不匹配"),
SMS_SEND_NUMBER_EMPTY(160044, "【短信】发送号码为空"),
SMS_BATCH_NUMBER_DUPLICATE(160045, "【短信】群发号码重复"),
SMS_MARKETING_CONTENT_AUDIT_FAIL(160046, "【短信】营销短信发送内容审核未通过"),
SMS_STATUS_REPORT_PARSE_FAIL(160047, "【短信】查询状态报告包体解析失败"),
SMS_BATCH_NUMBER_LIMIT(160048, "【短信】群发号码个数超限制"),
SMS_CONTENT_SENSITIVE_WORD(160049, "【短信】短信内容含敏感词"),
SMS_SEND_FAIL(160050, "【短信】短信发送失败"),
SMS_MARKETING_UNSUBSCRIBE_NUMBER(160051, "【短信】营销退订号码"),
SMS_TEMPLATE_VARIABLE_FORMAT_ERROR(160052, "【短信】模板变量格式有误"),
SMS_IP_AUTH_FAIL(160053, "【短信】IP鉴权失败"),
SMS_REQUEST_REPEAT(160054, "【短信】请求重复"),
SMS_REQID_TOO_LONG(160055, "【短信】请求reqId超长"),
SMS_SAME_NUMBER_CONTENT_REPEAT(160056, "【短信】同号码请求内容重复"),
SMS_TEMPLATE_ID_MUST_NUMBER(160057, "【短信】短信模板ID要求为数字"),
SMS_INTL_NO_PERMISSION(160058, "【国际短信】账户无国际短信权限"),
SMS_INTL_BATCH_NOT_SUPPORT(160059, "【国际短信】国际短信暂不支持群发"),
SMS_INTL_MARKETING_NO_PERMISSION(160060, "【国际短信】国际短信账户无营销短信权限"),
SMS_INTL_UNSUPPORTED_COUNTRY_CODE(160061, "【国际短信】暂不支持的国家码号"),
SMS_INTL_COUNTRY_CODE_NOT_OPEN(160062, "【国际短信】未开通此国家码号"),
SMS_SEND_FAIL_2(160063, "【短信】短信发送失败"),
SMS_SEND_FAIL_3(160064, "【短信】短信发送失败"),
SMS_SUB_EXT_INVALID(160065, "【短信】子扩展不符合要求"),
SMS_SCHEDULE_TIME_INVALID(160066, "【短信】定时发送时间不符合平台规则"),
SMS_SCHEDULE_TIME_FORMAT_ERROR(160067, "【短信】定时发送时间格式有误"),
SMS_ALL_UNSUBSCRIBE_BLACKLIST(160068, "【短信】平台全部退订号码黑名单"),
SMS_TEST_TEMPLATE_VAR_NOT_NUMBER(160069, "【短信】测试模板变量非数字"),
SMS_CUSTOM_TEMPLATE_NOT_SUPPORT_INTL(160070, "【短信】自定义短信模板不支持国际短信"),
SMS_MARKETING_TIME_NOT_ALLOWED(160071, "【短信】营销短信不在允许发送时间段"),
SMS_KEY_NUMBER_BLACKLIST(160072, "【短信】关键号码黑名单"),
SMS_OPERATOR_COMPLAINT_BLACKLIST(160073, "【短信】运营商投诉号码黑名单"),
SMS_CONTENT_BLACK_LINK(160074, "【短信】内容含黑链接"),
SMS_TEMPLATE_VAR_TOO_LONG(160078, "【短信】模板变量超长"),
SMS_NUMBER_BLACKLIST_2(160079, "【短信】号码黑名单"),
SMS_DAY_LIMIT(160080, "【短信】日发送量超出限额"),
SMS_MONTH_LIMIT(160081, "【短信】月发送量超出限额"),
SMS_NOT_WHITE_TEMPLATE(160082, "【短信】非报备白模板内容"),
SMS_CUSTOM_APP_NOT_EXIST(160083, "【短信】自定义应用不存在"),
SMS_NOT_CUSTOM_APP(160084, "【短信】非自定义应用"),
SMS_CUSTOM_APP_DISABLED(160085, "【短信】自定义应用已停用"),
SMS_NUMBER_BLIND_ZONE(160086, "【短信】号码盲区"),
SMS_SIGNATURE_BLOCKED(160087, "【短信】系统屏蔽签名"),
SMS_INTL_NUMBER_FORMAT_INVALID(160088, "【国际短信】号码格式无效");
private final int code;
private final String message;
SmsErrorEnum(int code, String message) {
this.code = code;
this.message = message;
}
public int code() {
return code;
}
public String message() {
return message;
}
}

View File

@ -0,0 +1,68 @@
package com.chinaunicom.mall.ebtp.extend.sms.controller;
import com.chinaunicom.mall.ebtp.common.base.entity.BaseResponse;
import com.chinaunicom.mall.ebtp.extend.sms.entity.SmsBaseResponseDTO;
import com.chinaunicom.mall.ebtp.extend.sms.entity.SmsSendResponseDTO;
import com.chinaunicom.mall.ebtp.extend.sms.entity.SmsTemplateDTO;
import com.chinaunicom.mall.ebtp.extend.sms.service.SmsService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/sms")
public class SmsController {
@Resource
private SmsService smsService;
/**
* 查询短信模板
* @param templateId
* @return
*/
@GetMapping("/queryTemplate")
public BaseResponse<List<SmsTemplateDTO>> queryTemplate(@RequestParam(value = "templateId", required = false) String templateId) {
// public SmsBaseResponseDTO queryTemplate(@RequestParam(value = "templateId", required = false) String templateId) {
SmsBaseResponseDTO smsBaseResponseDTO = smsService.querySmsTemplate(templateId);// 空(null和'')查询所有模板
return BaseResponse.success(smsBaseResponseDTO.getTemplateSMS());
}
/**
* 发送短信
* @return
* 返回:
{
"statusCode": "000000",
"templateSMS": {
"smsMessageSid": "b9fb1cc5acc3428c8d40c87428380b72",
"dateCreated": "20250618100527"
}
}
*/
@GetMapping("/send")
public SmsSendResponseDTO sendSms(@RequestParam String[] mobiles, @RequestParam String templateId, @RequestParam String[] datas) {
// String[] datas = new String[]{"测试内容"}; // 模板参数
// return smsService.sendTemplateSms(
// "18686879363", // 接收手机号
// "1180212", // 模板ID
// datas, // 模板参数
// null, // 子扩展码
// null // 自定义消息ID
// );
// 字符串数组转逗号拼接字符串
String mobileStr = String.join(",", mobiles);
return smsService.sendTemplateSms(
mobileStr, // 接收手机号
templateId, // 模板ID
datas, // 模板参数
null, // 子扩展码
null // 自定义消息ID
);
}
}

View File

@ -0,0 +1,45 @@
package com.chinaunicom.mall.ebtp.extend.sms.entity;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
*
{
"statusCode": "000000",
"totalCount": "2",
"TemplateSMS": [
{
"id": "1180212",
"title": "通知2",
"content": "【中远海运】{1}",
"status": "1",
"type": "1",
"dateCreated": "2022-03-27 12:34:28.0",
"dateUpdated": "2022-03-27 12:40:13.0",
"appendcode": "317806"
},
{
"id": "142717",
"title": "集团航运管理平台短信通知",
"content": "【中远海运航标平台】{1}",
"status": "1",
"type": "1",
"dateCreated": "2016-12-16 12:04:36.0",
"dateUpdated": "2020-12-10 12:58:25.0",
"appendcode": "319026"
}
]
}
*/
@Data
public class SmsBaseResponseDTO {
private String statusCode;
private String totalCount;
@JsonProperty("TemplateSMS")
private List<SmsTemplateDTO> templateSMS;
}

View File

@ -0,0 +1,27 @@
package com.chinaunicom.mall.ebtp.extend.sms.entity;
import lombok.Data;
/**
*
{
"statusCode":"000000",
"templateSMS":{
"smsMessageSid":"b21c72a8c9844843b8d87ecdb10304fb",
"dateCreated":"20250620143237"
}
}
*/
@Data
public class SmsSendResponseDTO {
private String statusCode;
private TemplateSMS templateSMS;
@Data
public static class TemplateSMS {
private String smsMessageSid;
private String dateCreated;
}
}

View File

@ -0,0 +1,26 @@
package com.chinaunicom.mall.ebtp.extend.sms.entity;
import lombok.Data;
/**
*
"id": "142717",
"title": "集团航运管理平台短信通知",
"content": "【中远海运航标平台】{1}",
"status": "1",
"type": "1",
"dateCreated": "2016-12-16 12:04:36.0",
"dateUpdated": "2020-12-10 12:58:25.0",
"appendcode": "319026"
*/
@Data
public class SmsTemplateDTO {
private String id;
private String title;
private String content;
private String status;
private String type;
private String dateCreated;
private String dateUpdated;
private String appendcode;
}

View File

@ -0,0 +1,65 @@
package com.chinaunicom.mall.ebtp.extend.sms.service;
import cn.hutool.core.util.ObjectUtil;
import com.chinaunicom.mall.ebtp.extend.sms.client.SmsClient;
import com.chinaunicom.mall.ebtp.extend.sms.entity.SmsBaseResponseDTO;
import com.chinaunicom.mall.ebtp.extend.sms.entity.SmsSendResponseDTO;
import com.chinaunicom.mall.ebtp.extend.sms.entity.SmsTemplateDTO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class SmsService {
@Autowired
private SmsClient yunSmsClient;
@Value("${spring.cloopen.appId}")
private String appId;
/**
* 查询短信模板
* @param templateId 模板id
* @return 模板查询结果
*/
public SmsBaseResponseDTO querySmsTemplate(String templateId) {
Map<String, String> request = new HashMap<>();
request.put("appId", appId);
if (StringUtils.isNotEmpty(templateId)) {
request.put("templateId", templateId);
}
return yunSmsClient.querySmsTemplate(request);
}
/**
* 发送模板短信
* @param to 接收手机号,多个用逗号分隔
* @param templateId 模板ID
* @param datas 模板参数
* @param subAppend 子扩展码,可选
* @param reqId 自定义消息ID可选
* @return 发送结果
*/
public SmsSendResponseDTO sendTemplateSms(String to, String templateId, String[] datas,
String subAppend, String reqId) {
Map<String, Object> request = new HashMap<>();
request.put("to", to);
request.put("appId", appId);
request.put("templateId", templateId);
request.put("datas", datas);
if (subAppend != null && !subAppend.isEmpty()) {
request.put("subAppend", subAppend);
}
if (reqId != null && !reqId.isEmpty()) {
request.put("reqId", reqId);
}
return yunSmsClient.sendTemplateSms(request);
}
}