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} 通道,通用短信网关)。 *

* 对接特征:HTTP POST 表单 {@code action/account/password/mobile/content/extno/rt},无签名。 *

* ⚠️ 现有三个入口对同一个网关的调用方式并不一致,且其中两个是坏的。为满足「按站点原样封装、 * 不改线上行为」的要求,这里把三种模式都保留为独立方法,由调用方按原站点选择: *

* 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 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,必然失败)。 *

* 保留此方法是为了让 {@code RyTask} 的改造可以做到「行为一字不差」;确认网关格式后应删除。 */ public SendResult sendJsonLegacy(SmsSendRequest request) { XhsmsConfig cfg = properties.getSms().getXhsms(); //原实现:Map 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 buildParams(SmsSendRequest request, XhsmsConfig cfg) { Map 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 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; } } }