注释移动云 mas 短信相关的实现

This commit is contained in:
chenbowen
2025-12-12 10:13:58 +08:00
parent 1706a70499
commit 1991863573
2 changed files with 223 additions and 223 deletions

View File

@@ -1,222 +1,222 @@
package com.zt.plat.module.system.framework.sms.core.client.impl; //package com.zt.plat.module.system.framework.sms.core.client.impl;
//
import cn.hutool.core.codec.Base64; //import cn.hutool.core.codec.Base64;
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.hutool.crypto.digest.DigestUtil; //import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONObject; //import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil; //import cn.hutool.json.JSONUtil;
import com.zt.plat.framework.common.core.KeyValue; //import com.zt.plat.framework.common.core.KeyValue;
import com.zt.plat.framework.common.util.http.HttpUtils; //import com.zt.plat.framework.common.util.http.HttpUtils;
import com.zt.plat.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; //import com.zt.plat.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO;
import com.zt.plat.module.system.framework.sms.core.client.dto.SmsSendRespDTO; //import com.zt.plat.module.system.framework.sms.core.client.dto.SmsSendRespDTO;
import com.zt.plat.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; //import com.zt.plat.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO;
import com.zt.plat.module.system.framework.sms.core.property.SmsChannelProperties; //import com.zt.plat.module.system.framework.sms.core.property.SmsChannelProperties;
import lombok.extern.slf4j.Slf4j; //import lombok.extern.slf4j.Slf4j;
//
import java.util.Collections; //import java.util.Collections;
import java.util.HashMap; //import java.util.HashMap;
import java.util.List; //import java.util.List;
import java.util.Map; //import java.util.Map;
//
/** ///**
* 中国移动云MAS短信客户端实现类 // * 中国移动云MAS短信客户端实现类
* // *
* @author zt-team // * @author zt-team
* @since 2025-01-19 // * @since 2025-01-19
*/ // */
@Slf4j //@Slf4j
public class CmccMasSmsClient extends AbstractSmsClient { //public class CmccMasSmsClient extends AbstractSmsClient {
//
private static final String URL = "https://112.35.10.201:28888/sms/submit"; // private static final String URL = "https://112.35.10.201:28888/sms/submit";
private static final String RESPONSE_SUCCESS = "success"; // private static final String RESPONSE_SUCCESS = "success";
//
public CmccMasSmsClient(SmsChannelProperties properties) { // public CmccMasSmsClient(SmsChannelProperties properties) {
super(properties); // super(properties);
Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空"); // Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空");
Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); // Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
validateCmccMasConfig(properties); // validateCmccMasConfig(properties);
} // }
//
/** // /**
* 参数校验中国移动云MAS的配置 // * 参数校验中国移动云MAS的配置
* // *
* 原因是中国移动云MAS需要三个参数ecName、apId、secretKey // * 原因是中国移动云MAS需要三个参数ecName、apId、secretKey
* // *
* 解决方案:考虑到不破坏原有的 apiKey + apiSecret 的结构,所以将 ecName 和 apId 拼接到 apiKey 字段中,格式为 "ecName apId"。 // * 解决方案:考虑到不破坏原有的 apiKey + apiSecret 的结构,所以将 ecName 和 apId 拼接到 apiKey 字段中,格式为 "ecName apId"。
* secretKey 存储在 apiSecret 字段中。 // * secretKey 存储在 apiSecret 字段中。
* // *
* @param properties 配置 // * @param properties 配置
*/ // */
private static void validateCmccMasConfig(SmsChannelProperties properties) { // private static void validateCmccMasConfig(SmsChannelProperties properties) {
String combineKey = properties.getApiKey(); // String combineKey = properties.getApiKey();
Assert.notEmpty(combineKey, "apiKey 不能为空"); // Assert.notEmpty(combineKey, "apiKey 不能为空");
String[] keys = combineKey.trim().split(" "); // String[] keys = combineKey.trim().split(" ");
Assert.isTrue(keys.length == 2, "中国移动云MAS apiKey 配置格式错误,请配置为 [ecName apId]"); // Assert.isTrue(keys.length == 2, "中国移动云MAS apiKey 配置格式错误,请配置为 [ecName apId]");
} // }
//
/** // /**
* 获取 ecName企业名称 // * 获取 ecName企业名称
*/ // */
private String getEcName() { // private String getEcName() {
return StrUtil.subBefore(properties.getApiKey(), " ", true); // return StrUtil.subBefore(properties.getApiKey(), " ", true);
} // }
//
/** // /**
* 获取 apId应用ID // * 获取 apId应用ID
*/ // */
private String getApId() { // private String getApId() {
return StrUtil.subAfter(properties.getApiKey(), " ", true); // return StrUtil.subAfter(properties.getApiKey(), " ", true);
} // }
//
/** // /**
* 获取 secretKey密钥 // * 获取 secretKey密钥
*/ // */
private String getSecretKey() { // private String getSecretKey() {
return properties.getApiSecret(); // return properties.getApiSecret();
} // }
//
/** // /**
* 发送短信 // * 发送短信
* // *
* @param logId 日志ID // * @param logId 日志ID
* @param mobile 手机号 // * @param mobile 手机号
* @param apiTemplateId 模板ID本平台不使用模板传入内容 // * @param apiTemplateId 模板ID本平台不使用模板传入内容
* @param templateParams 模板参数 // * @param templateParams 模板参数
* @return 发送结果 // * @return 发送结果
*/ // */
@Override // @Override
public SmsSendRespDTO sendSms(Long logId, String mobile, String apiTemplateId, // public SmsSendRespDTO sendSms(Long logId, String mobile, String apiTemplateId,
List<KeyValue<String, Object>> templateParams) throws Throwable { // List<KeyValue<String, Object>> templateParams) throws Throwable {
//
// 1. 构建短信内容 // // 1. 构建短信内容
String content = buildContent(apiTemplateId, templateParams); // String content = buildContent(apiTemplateId, templateParams);
//
// 2. 计算MAC校验值 // // 2. 计算MAC校验值
String mac = calculateMac(mobile, content); // String mac = calculateMac(mobile, content);
//
// 3. 构建请求参数 // // 3. 构建请求参数
JSONObject requestBody = new JSONObject(); // JSONObject requestBody = new JSONObject();
requestBody.set("ecName", getEcName()); // 企业名称 // requestBody.set("ecName", getEcName()); // 企业名称
requestBody.set("apId", getApId()); // 应用ID // requestBody.set("apId", getApId()); // 应用ID
requestBody.set("secretKey", getSecretKey()); // 密钥 // requestBody.set("secretKey", getSecretKey()); // 密钥
requestBody.set("sign", properties.getSignature()); // 签名编码 // requestBody.set("sign", properties.getSignature()); // 签名编码
requestBody.set("mobiles", mobile); // requestBody.set("mobiles", mobile);
requestBody.set("content", content); // requestBody.set("content", content);
requestBody.set("addSerial", ""); // requestBody.set("addSerial", "");
requestBody.set("mac", mac); // requestBody.set("mac", mac);
//
log.info("[sendSms][发送短信 {}]", JSONUtil.toJsonStr(requestBody)); // log.info("[sendSms][发送短信 {}]", JSONUtil.toJsonStr(requestBody));
//
// 4. Base64编码请求体 // // 4. Base64编码请求体
String encodedBody = Base64.encode(requestBody.toString()); // String encodedBody = Base64.encode(requestBody.toString());
log.info("[sendSms][Base64编码后: {}]", encodedBody); // log.info("[sendSms][Base64编码后: {}]", encodedBody);
//
// 5. 构建请求头需要JWT Token // // 5. 构建请求头需要JWT Token
Map<String, String> headers = new HashMap<>(); // Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Bearer " + getJwtToken()); // headers.put("Authorization", "Bearer " + getJwtToken());
headers.put("Content-Type", "text/plain"); // headers.put("Content-Type", "text/plain");
//
// 6. 发起请求 // // 6. 发起请求
String responseBody = HttpUtils.post(URL, headers, encodedBody); // String responseBody = HttpUtils.post(URL, headers, encodedBody);
JSONObject response = JSONUtil.parseObj(responseBody); // JSONObject response = JSONUtil.parseObj(responseBody);
//
log.info("[sendSms][收到响应 - {}]", response); // log.info("[sendSms][收到响应 - {}]", response);
//
// 7. 解析响应 // // 7. 解析响应
return new SmsSendRespDTO() // return new SmsSendRespDTO()
.setSuccess(response.getBool("success", false)) // .setSuccess(response.getBool("success", false))
.setSerialNo(response.getStr("msgGroup")) // .setSerialNo(response.getStr("msgGroup"))
.setApiCode(response.getStr("rspcod")) // .setApiCode(response.getStr("rspcod"))
.setApiMsg(response.getStr("message", "未知错误")); // .setApiMsg(response.getStr("message", "未知错误"));
} // }
//
/** // /**
* 解析短信接收状态回调 // * 解析短信接收状态回调
* // *
* @param text 回调文本 // * @param text 回调文本
* @return 接收状态列表 // * @return 接收状态列表
*/ // */
@Override // @Override
public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) throws Throwable { // public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) throws Throwable {
// TODO: 根据移动云MAS回调格式实现 // // TODO: 根据移动云MAS回调格式实现
log.warn("[parseSmsReceiveStatus][暂未实现短信状态回调解析]"); // log.warn("[parseSmsReceiveStatus][暂未实现短信状态回调解析]");
return Collections.emptyList(); // return Collections.emptyList();
} // }
//
/** // /**
* 查询短信模板 // * 查询短信模板
* // *
* @param apiTemplateId 模板ID // * @param apiTemplateId 模板ID
* @return 模板信息 // * @return 模板信息
*/ // */
@Override // @Override
public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable { // public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable {
// 移动云MAS不使用模板机制直接发送内容 // // 移动云MAS不使用模板机制直接发送内容
log.debug("[getSmsTemplate][中国移动云MAS不支持模板查询]"); // log.debug("[getSmsTemplate][中国移动云MAS不支持模板查询]");
return null; // return null;
} // }
//
/** // /**
* 计算MAC校验值 // * 计算MAC校验值
* 算法MD5(ecName + apId + secretKey + mobiles + content + sign + addSerial) // * 算法MD5(ecName + apId + secretKey + mobiles + content + sign + addSerial)
* // *
* @param mobile 手机号 // * @param mobile 手机号
* @param content 短信内容 // * @param content 短信内容
* @return MAC校验值 // * @return MAC校验值
*/ // */
private String calculateMac(String mobile, String content) { // private String calculateMac(String mobile, String content) {
String rawString = getEcName() // ecName // String rawString = getEcName() // ecName
+ getApId() // apId // + getApId() // apId
+ getSecretKey() // secretKey // + getSecretKey() // secretKey
+ mobile // mobiles // + mobile // mobiles
+ content // content // + content // content
+ properties.getSignature() // sign // + properties.getSignature() // sign
+ ""; // addSerial // + ""; // addSerial
//
String mac = DigestUtil.md5Hex(rawString).toLowerCase(); // String mac = DigestUtil.md5Hex(rawString).toLowerCase();
log.debug("[calculateMac][原始字符串长度: {}, MAC: {}]", rawString.length(), mac); // log.debug("[calculateMac][原始字符串长度: {}, MAC: {}]", rawString.length(), mac);
return mac; // return mac;
} // }
//
/** // /**
* 构建短信内容 // * 构建短信内容
* // *
* @param apiTemplateId 模板ID // * @param apiTemplateId 模板ID
* @param templateParams 模板参数 // * @param templateParams 模板参数
* @return 短信内容 // * @return 短信内容
*/ // */
private String buildContent(String apiTemplateId, List<KeyValue<String, Object>> templateParams) { // private String buildContent(String apiTemplateId, List<KeyValue<String, Object>> templateParams) {
// 简单实现直接返回模板ID作为内容 // // 简单实现直接返回模板ID作为内容
// 实际使用时需要根据业务需求构建短信内容 // // 实际使用时需要根据业务需求构建短信内容
if (templateParams == null || templateParams.isEmpty()) { // if (templateParams == null || templateParams.isEmpty()) {
return apiTemplateId; // return apiTemplateId;
} // }
//
// 替换模板参数,支持 {{key}} 格式 // // 替换模板参数,支持 {{key}} 格式
String content = apiTemplateId; // String content = apiTemplateId;
for (KeyValue<String, Object> param : templateParams) { // for (KeyValue<String, Object> param : templateParams) {
String placeholder = "{{" + param.getKey() + "}}"; // String placeholder = "{{" + param.getKey() + "}}";
String value = String.valueOf(param.getValue()); // String value = String.valueOf(param.getValue());
content = content.replace(placeholder, value); // content = content.replace(placeholder, value);
} // }
return content; // return content;
} // }
//
/** // /**
* 获取JWT Token // * 获取JWT Token
* TODO: 实现Token获取逻辑可能需要 // * TODO: 实现Token获取逻辑可能需要
* 1. 调用认证接口获取Token // * 1. 调用认证接口获取Token
* 2. 缓存Token并在过期前自动刷新 // * 2. 缓存Token并在过期前自动刷新
* 3. 处理Token失效情况 // * 3. 处理Token失效情况
* // *
* @return JWT Token // * @return JWT Token
*/ // */
private String getJwtToken() { // private String getJwtToken() {
// 临时实现:从配置中读取或调用认证接口获取 // // 临时实现:从配置中读取或调用认证接口获取
// 实际生产环境需要实现完整的Token管理机制 // // 实际生产环境需要实现完整的Token管理机制
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.example.token"; // String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.example.token";
log.warn("[getJwtToken][使用临时Token生产环境需实现完整的Token获取机制]"); // log.warn("[getJwtToken][使用临时Token生产环境需实现完整的Token获取机制]");
return token; // return token;
} // }
} //}

View File

@@ -81,7 +81,7 @@ public class SmsClientFactoryImpl implements SmsClientFactory {
case TENCENT: return new TencentSmsClient(properties); case TENCENT: return new TencentSmsClient(properties);
case HUAWEI: return new HuaweiSmsClient(properties); case HUAWEI: return new HuaweiSmsClient(properties);
case QINIU: return new QiniuSmsClient(properties); case QINIU: return new QiniuSmsClient(properties);
case CMCC_MAS: return new CmccMasSmsClient(properties); // case CMCC_MAS: return new CmccMasSmsClient(properties);
case HL95: return new Hl95SmsClient(properties); case HL95: return new Hl95SmsClient(properties);
} }
// 创建失败,错误日志 + 抛出异常 // 创建失败,错误日志 + 抛出异常