package com.ruoyi.message.sms.provider.xh;
|
|
import com.ruoyi.common.utils.HttpUtil;
|
import com.ruoyi.common.utils.http.HttpUtils;
|
import com.ruoyi.message.MessageChannel;
|
import com.ruoyi.message.SendResult;
|
import com.ruoyi.message.config.MessageProperties;
|
import com.ruoyi.message.config.XhsmsConfig;
|
import com.ruoyi.message.config.RootMessageProperties;
|
import com.ruoyi.message.sms.AbstractSmsSender;
|
import com.ruoyi.message.sms.SmsSendRequest;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.stereotype.Component;
|
|
import java.net.URLEncoder;
|
import java.util.LinkedHashMap;
|
import java.util.Map;
|
|
/**
|
* 新华短信发送方({@code xh} 通道,通用短信网关)。
|
* <p>
|
* 对接特征:HTTP POST 表单 {@code action/account/password/mobile/content/extno/rt},无签名。
|
* <p>
|
* ⚠️ 现有三个入口对同一个网关的调用方式并不一致,且其中两个是坏的。为满足「按站点原样封装、
|
* 不改线上行为」的要求,这里把三种模式都保留为独立方法,由调用方按原站点选择:
|
* <ul>
|
* <li>{@link #sendJsonLegacy} —— 等价 {@code RyTask}:拼好表单参数却发送 {@code new Gson().toJson(req)}
|
* ({@code req} 是空 map,实际发出 {@code {}}),且把 {@code Boolean} 当 {@code String} 返回值,
|
* 必然触发 NPE 被异常分支吞掉。结果:**新华站点长期任务短信实际发不出去**;</li>
|
* <li>{@link #sendForm} —— 等价 {@code RedisMqReceiver}:发送 URL 编码后的表单字符串(正确),
|
* 但原实现忽略了返回值,代码里固定按成功处理;</li>
|
* <li>{@link #sendJson} —— 等价 {@code ServiceSubtaskServiceImpl.smsSubTask} / {@code SmsController}:
|
* 发送 {@code Gson.toJson(map)} 的 JSON。</li>
|
* </ul>
|
* TODO 待确认:以上三种写法哪一种才是新华网关真正接受的报文格式(很可能是 {@link #sendForm}),
|
* 确认后应统一为一种并删除其余两种。
|
*/
|
@Slf4j
|
@Component
|
public class XinHuaSmsSender extends AbstractSmsSender {
|
|
/**
|
* 渠道 key,与 {@code spring.profiles.active=xh} 对应
|
*/
|
public static final String KEY = "xh";
|
|
private final MessageProperties properties;
|
|
public XinHuaSmsSender(MessageProperties properties) {
|
this.properties = properties;
|
}
|
|
@Override
|
public String key() {
|
return KEY;
|
}
|
|
@Override
|
public String description() {
|
return "新华短信(通用网关,表单/JSON 两种报文)";
|
}
|
|
@Override
|
protected SendResult doSend(SmsSendRequest request) {
|
//默认按 RedisMqReceiver 的(正确)表单模式发送
|
return sendForm(request);
|
}
|
|
/**
|
* 模式一:表单字符串(等价 {@code RedisMqReceiver},推荐)
|
*/
|
public SendResult sendForm(SmsSendRequest request) {
|
XhsmsConfig cfg = properties.getSms().getXhsms();
|
String encodedParams = buildEncodedParams(request, cfg);
|
log.info("【{}】新华短信发送(表单),地址:{},参数:{}", KEY, cfg.getPath(), encodedParams);
|
HttpUtils.sendPost(cfg.getPath(), encodedParams);
|
//与线上行为一致:原实现未使用返回值,固定按成功处理
|
return SendResult.success(KEY, MessageChannel.SMS, encodedParams);
|
}
|
|
/**
|
* 模式二:JSON 报文(等价 {@code smsSubTask} / {@code SmsController})
|
*/
|
public SendResult sendJson(SmsSendRequest request) {
|
XhsmsConfig cfg = properties.getSms().getXhsms();
|
Map<String, String> params = buildParams(request, cfg);
|
String json = com.alibaba.fastjson.JSON.toJSONString(params);
|
log.info("【{}】新华短信发送(JSON),地址:{},参数:{}", KEY, cfg.getPath(), json);
|
Boolean ok = HttpUtil.postJsonRequest(cfg.getPath(), json);
|
if (Boolean.TRUE.equals(ok)) {
|
return SendResult.success(KEY, MessageChannel.SMS, json);
|
}
|
return SendResult.fail(KEY, MessageChannel.SMS, "短信发送失败", json);
|
}
|
|
/**
|
* 模式三:兼容 {@code RyTask} 的旧实现(发送空 JSON,必然失败)。
|
* <p>
|
* 保留此方法是为了让 {@code RyTask} 的改造可以做到「行为一字不差」;确认网关格式后应删除。
|
*/
|
public SendResult sendJsonLegacy(SmsSendRequest request) {
|
XhsmsConfig cfg = properties.getSms().getXhsms();
|
//原实现:Map<String,String> req = new HashMap<>(); ...(HttpUtils.sendPost(xhsmsPath, new Gson().toJson(req));
|
// Gson 序列化空 map 的结果即 "{}"
|
String emptyJson = "{}";
|
log.info("【{}】新华短信发送(RyTask 旧实现,发送空报文),地址:{}", KEY, cfg.getPath());
|
Boolean result = HttpUtils.sendPost(cfg.getPath(), emptyJson);
|
if (Boolean.TRUE.equals(result)) {
|
return SendResult.success(KEY, MessageChannel.SMS, emptyJson);
|
}
|
return SendResult.fail(KEY, MessageChannel.SMS, "短信发送失败", emptyJson);
|
}
|
|
private Map<String, String> buildParams(SmsSendRequest request, XhsmsConfig cfg) {
|
Map<String, String> params = new LinkedHashMap<>();
|
params.put("action", "send");
|
params.put("account", cfg.getAccount());
|
params.put("password", cfg.getPwd());
|
params.put("mobile", request.getPhone());
|
params.put("content", request.getContent());
|
params.put("extno", cfg.getExtno());
|
params.put("rt", "json");
|
return params;
|
}
|
|
private String buildEncodedParams(SmsSendRequest request, XhsmsConfig cfg) {
|
StringBuilder encodedParams = new StringBuilder();
|
for (Map.Entry<String, String> entry : buildParams(request, cfg).entrySet()) {
|
if (encodedParams.length() > 0) {
|
encodedParams.append("&");
|
}
|
encodedParams.append(encode(entry.getKey())).append("=").append(encode(entry.getValue()));
|
}
|
return encodedParams.toString();
|
}
|
|
private String encode(String value) {
|
try {
|
return URLEncoder.encode(value == null ? "" : value, "UTF-8");
|
} catch (Exception e) {
|
return value;
|
}
|
}
|
}
|