package com.ruoyi.message.wechat.provider.xh;
|
|
import com.google.gson.Gson;
|
import com.google.gson.GsonBuilder;
|
import com.ruoyi.common.utils.http.HttpUtils;
|
import com.ruoyi.message.wechat.WechatMpConfig;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.stereotype.Component;
|
|
import java.nio.charset.StandardCharsets;
|
import java.security.MessageDigest;
|
import java.util.HashMap;
|
import java.util.Map;
|
|
/**
|
* 新华医院公众号 API。
|
* <p>
|
* 对接特征:POST JSON 报文,签名 {@code sign = MD5(appSecret + body)} 放在请求头,
|
* 网关地址来自机构级配置({@code WxGZHEnum})。
|
* <p>
|
* 迁移说明:原逻辑分散在 {@code RyTask.getWXCode}、{@code RedisMqReceiver.getWXCode}、
|
* {@code ServiceSubtaskServiceImpl.getWXCode} 三份拷贝里,此处合并为一份(行为保持不变)。
|
*/
|
@Slf4j
|
@Component
|
public class XinHuaMpApi {
|
|
private final com.ruoyi.message.config.MessageProperties properties;
|
|
public XinHuaMpApi(com.ruoyi.message.config.MessageProperties properties) {
|
this.properties = properties;
|
}
|
|
/**
|
* 发送公众号消息(默认不关闭 HTML 转义,等价 {@code RyTask.getWXCode})
|
*/
|
public String sendMessage(String idcard, String url, String taskName, String taskDesc,
|
String phone, String patientName, String patientId, WechatMpConfig config) {
|
return sendMessage(idcard, url, taskName, taskDesc, phone, patientName, patientId, config, false);
|
}
|
|
/**
|
* 发送公众号消息
|
*
|
* @param idcard 身份证号
|
* @param url 外链地址
|
* @param taskName 标题
|
* @param taskDesc 内容
|
* @param phone 手机号
|
* @param patientName 患者姓名
|
* @param patientId HIS 侧患者ID
|
* @param config 机构级公众号配置
|
* @param disableHtmlEscaping 是否关闭 Gson 的 HTML 转义
|
* <p>
|
* ⚠️ 站点差异(必须区分):{@code RedisMqReceiver.getWXCode} 用的是
|
* {@code new GsonBuilder().disableHtmlEscaping()},而
|
* {@code RyTask.getWXCode} / {@code ServiceSubtaskServiceImpl.getWXCode}
|
* 用的是 {@code new Gson()}(会把 {@code < > & = '} 转成 unicode 转义)。
|
* 两者产出的报文体不同,进而影响 sign 签名。此处按调用方区分,保持线上行为。
|
* @return 第三方原始响应
|
*/
|
public String sendMessage(String idcard, String url, String taskName, String taskDesc,
|
String phone, String patientName, String patientId, WechatMpConfig config,
|
boolean disableHtmlEscaping) {
|
XinHuaWXReq req = new XinHuaWXReq();
|
req.setService(properties.getWechat().getXinhua().getService());
|
req.setIdcard(idcard);
|
req.setUrl(url);
|
req.setPatientid(patientId);
|
req.setPatientname(patientName);
|
req.setMobile(phone);
|
req.setMedcardno(null);
|
req.setTitlename(taskName);
|
req.setContent(taskDesc);
|
req.setAppid(config.getAppid());
|
|
log.info("【XinHuaMpApi】微信公众号请求参数:{}(disableHtmlEscaping={})", req, disableHtmlEscaping);
|
String body;
|
if (disableHtmlEscaping) {
|
GsonBuilder gb = new GsonBuilder();
|
gb.disableHtmlEscaping();
|
body = gb.create().toJson(req);
|
} else {
|
body = new Gson().toJson(req);
|
}
|
String sign = md5(config.getAppSecret().concat(body));
|
Map<String, String> headerMap = new HashMap<>();
|
headerMap.put("sign", sign);
|
String result = HttpUtils.sendPostByHeader(config.getAddress(), body, headerMap);
|
log.info("【XinHuaMpApi】微信公众号返回参数:{}", result);
|
return result;
|
}
|
|
/**
|
* MD5 转十六进制小写(等价原 {@code getWXCode} 里的 {@code encode})
|
*/
|
private String md5(String arg) {
|
if (arg == null) {
|
arg = "";
|
}
|
try {
|
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
md5.update(arg.getBytes(StandardCharsets.UTF_8));
|
return toHex(md5.digest());
|
} catch (Exception e) {
|
log.error("【XinHuaMpApi】MD5 签名失败:{}", e.getMessage(), e);
|
return "";
|
}
|
}
|
|
private String toHex(byte[] hash) {
|
StringBuilder hexString = new StringBuilder();
|
for (byte b : hash) {
|
String hex = Integer.toHexString(0xff & b);
|
if (hex.length() == 1) {
|
hexString.append('0');
|
}
|
hexString.append(hex);
|
}
|
return hexString.toString();
|
}
|
}
|