Spring Cloud Admin添加微信通知

发布时间 2023-04-09 13:56:17作者: shigp1

SpringCloud发送微信消息推送参考https://blog.csdn.net/qq_44697754/article/details/128035736

 
 

SpringCloud Admin要增加微信通知,需要继承AbstractStatusChangeNotifier类,在doNotify方法按照模板发送消息。
 
AdminServe添加依赖:

<dependency>
        <groupId>commons-lang</groupId>
        <artifactId>commons-lang</artifactId>
        <version>2.3</version>
        <scope>compile</scope>
    </dependency>

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>2.0.21.graal</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>

 
文件application.properties增加配置:

wechat.originalId=gh_dca23axxx
wechat.appId=wx360b2xxxxx
wechat.appsecret=1a7876a7bxxxxxxx
wechat.templateId=-Z6u1ASSqsstKrzxxxxxxxxxxxxx
wechat.accessTokenUrl=https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential
wechat.userGetUrl=https://api.weixin.qq.com/cgi-bin/user/get
wechat.messageSendUrl=https://api.weixin.qq.com/cgi-bin/message/template/send
wechat.templateMassage={\"touser\":\"%s\",\"template_id\":\"%s\",\
             \"data\":{\"serviceName\": {\"value\":\"%s\",\"color\":\"#00BFFF\"},\"serviceUrl\":{\"value\":\"%s\",\"color\":\"#00FFFF\"},\
             \"status\": {\"value\":\"%s\",\"color\":\"#173177\"},\"details\": {\"value\":\"%s\",\"color\":\"#EE212D\"}}}

 
 

微信接口配置类WechatServiceProperties:

@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "wechat")
public class WechatServiceProperties {


   /** 获取access_token的接口地址(GET) 限2000(次/天) */
    private  String  accessTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential";

    /** 获取关注用户id */
    private  String userGetUrl  = "https://api.weixin.qq.com/cgi-bin/user/get";

    /**
     * 发送消息url
     */
    private String messageSendUrl = "https://api.weixin.qq.com/cgi-bin/message/template/send";

    /**
     * 模板id
     */
    private String  templateId = "91921dL1CxrEUms2rnIlKi9_rfmwmiICaiaXxAbhsc8";

    /**
     * 模板消息
     */
    private String templateMassage;

    /**
     * appId
     */
    private String appId;

    private String appsecret;

    private String originalId;


    public String getAccessTokenUrl() {
        if (StringUtils.isBlank(accessTokenUrl)) {
            throw new IllegalArgumentException("获取access_token的接口地址不能为空");
        }

        if (StringUtils.isBlank(appId)) {
            throw new IllegalArgumentException("appId不能为空");
        }

        if (StringUtils.isBlank(appsecret)) {
            throw new IllegalArgumentException("appsecret不能为空");
        }
        return String.format(accessTokenUrl+"&appid=%s&secret=%s",appId, appsecret);
    }




    public String formatGetUserUrl(String accessToken) {
        if (StringUtils.isBlank(accessToken)) {
            throw  new IllegalArgumentException("accessToken不能为空");
        }

        return String.format(userGetUrl+"?access_token=%s", accessToken);
    }

    public String formatMessageSendUrl(String accessToken) {
        if (StringUtils.isBlank(messageSendUrl)) {
            throw  new IllegalArgumentException("messageSendUrl不能为空");
        }

        if (StringUtils.isBlank(accessToken)) {
            throw  new IllegalArgumentException("accessToken不能为空");
        }
        return  String.format(messageSendUrl+"?access_token=%s", accessToken);
    }


    public String formatTemplateMassage(TemplateMassageParam param) {
        if (StringUtils.isBlank(messageSendUrl)) {
            throw  new IllegalArgumentException("messageSendUrl不能为空");
        }

        if (param == null) {
            throw  new IllegalArgumentException("要发送的消息不能为空");
        }
       return String.format(templateMassage,param.getTouser(), param.getTemplateId(), param.getServiceName(), param.getServiceUrl(), param.getStatus(), param.getDetails());
    }
}

 

 

AccessToken

public class AccessToken implements java.io.Serializable{

  
	private static final long serialVersionUID = -4240357901925120079L;

	/** 获取到的凭证 */ 
	private String token;

	/** 凭证有效时间,单位:秒 */ 
	private int expiresIn;

	public AccessToken() {
	}

	public AccessToken(String token, int expiresIn) {
		this.token = token;
		this.expiresIn = expiresIn;
	}

	public String getToken() {
		return token;
	}

	public void setToken(String token) {
		this.token = token;
	}

	public int getExpiresIn() {
		return expiresIn;
	}

	public void setExpiresIn(int expiresIn) {
		this.expiresIn = expiresIn;
	}
}

 

HttpUtils用于发送http请求:

Slf4j
public class HttpUtils {

    public static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) {
        log.debug("[发起https请求]requestUrl=" + requestUrl);
        log.debug("[发起https请求]requestMethod=" + requestMethod);
        log.debug("[发起https请求]outputStr=" + outputStr);
        JSONObject jsonObject = null;
        StringBuffer buffer = new StringBuffer();
        try {
            // 创建SSLContext对象,并使用我们指定的信任管理器初始化
            TrustManager[] tm = {new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {

                }

                @Override
                public void checkServerTrusted(X509Certificate[] chain, String authType) {

                }

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }
            }};
            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
            sslContext.init(null, tm, new java.security.SecureRandom());
            // 从上述SSLContext对象中得到SSLSocketFactory对象
            SSLSocketFactory ssf = sslContext.getSocketFactory();

            URL url = new URL(requestUrl);
            HttpsURLConnection httpUrlConn = (HttpsURLConnection) url
                    .openConnection();
            httpUrlConn.setSSLSocketFactory(ssf);
            httpUrlConn.setDoOutput(true);
            httpUrlConn.setDoInput(true);
            httpUrlConn.setUseCaches(false);
            // 设置请求方式(GET/POST)
            httpUrlConn.setRequestMethod(requestMethod);

            //if ("GET".equalsIgnoreCase(requestMethod))
            httpUrlConn.connect();

            // 当有数据需要提交时
            if (null != outputStr) {
                OutputStream outputStream = httpUrlConn.getOutputStream();
                // 注意编码格式,防止中文乱码

                outputStream.write(outputStr.getBytes("UTF-8"));
                outputStream.close();

            }

            // 将返回的输入流转换成字符串
            InputStream inputStream = httpUrlConn.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(
                    inputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(
                    inputStreamReader);

            String str = null;
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }
            bufferedReader.close();
            inputStreamReader.close();
            // 释放资源
            inputStream.close();
            httpUrlConn.disconnect();
            jsonObject = JSONObject.parseObject(buffer.toString());
        } catch (ConnectException ce) {
            log.error("Weixin server connection timed out.");
        } catch (Exception e) {
            log.error("https request error:{}", e);
        }
        return jsonObject;
    }

}

 
 

模板消息参数TemplateMassageParam:

@Setter
@Getter
@Builder
public class TemplateMassageParam {

    /**
     * 服务名
     */
    private String serviceName;

    /**
     * 服务url
     */
    private String serviceUrl;

    /**
     * 服务状态
     */
    private String status;

    /**
     * 详情
     */
    private Map<String, Object> details;

    /**
     * 发送到的用户
     */
    private String touser;

    /**
     * 消息模板id
     */
    private String templateId;
}

 
 

通知发送类WechatNotifier:

@Component
@Slf4j
public class WechatNotifier extends AbstractStatusChangeNotifier {

    private Map<String, AccessToken> map = new HashMap<>();

    @Autowired
    private WechatServiceProperties wechatServiceProperties;

    @Autowired
    public WechatNotifier(InstanceRepository repository) {
        super(repository);
    }

    @Override
    protected Mono<Void> doNotify(InstanceEvent event, Instance instance) {
        Registration registration = instance.getRegistration();
        // 获取关注用户
        List<String> users = this.getUsers();
        if (users != null && users.size() > 0) {
            TemplateMassageParam param = TemplateMassageParam.builder().serviceName(registration.getName())
                    .serviceUrl(registration.getServiceUrl())
                    .status(instance.getStatusInfo().getStatus())
                    .details(instance.getStatusInfo().getDetails())
                    .templateId(wechatServiceProperties.getTemplateId())
                    .build();

            String sendUrl = wechatServiceProperties.formatMessageSendUrl(map.get("token").getToken());
            // 并行遍历推送
            users.stream().parallel().forEach(x->{
                if (x != null) {
                    param.setTouser(x);
                    String templateMassage = wechatServiceProperties.formatTemplateMassage(param);
                    log.info("[发送模板信息]sendTemplateMessage:"+templateMassage);
                    JSONObject jsonObject = null;
                    jsonObject = HttpUtils.httpsRequest(sendUrl, "POST", templateMassage);
                    log.info("[发送模板信息] sendTemplateMessage result:"+jsonObject);
                }
            });
        }
        return Mono.empty();
    }


    /**
     * 获取token
     * @return
     */
    public AccessToken getAccessToken() {
        AccessToken accessToken = null;
        String requestUrl = wechatServiceProperties.getAccessTokenUrl();
        JSONObject jsonObject=null;
        jsonObject = HttpUtils.httpsRequest(requestUrl, "GET", null);
        log.info("[(刷新)获取access_token]jsonObject="+jsonObject);
        jsonObject = JSONObject.parseObject((String) jsonObject.toString());

        if (null != jsonObject) {
            try {
                accessToken = new AccessToken(jsonObject.getString("access_token"), jsonObject.getInteger("expires_in"));
            } catch (JSONException e) {
                log.error("获取token失败 errcode:{} errmsg:{}"+jsonObject.getInteger("errcode")+"errmsg:"+jsonObject.getString("errmsg"),e);
            }
        }
        log.info("[获取access_token]jsonObject="+jsonObject);
        log.info("[获取access_token]accessToken="+accessToken.getToken());
        return accessToken;
    }

    /**
     * 获取关注用户
     * @return
     */
    public List<String> getUsers() {
        try {
            log.info("------------ [获取用户信息] ---------------");
            String token = null;
            AccessToken accessToken  = map.get("token");
            if (accessToken == null) {
                accessToken = this.getAccessToken();
                map.put("token",accessToken);
            }

            if (accessToken != null) {
                token = accessToken.getToken();
            }
            if (token != null) {
                String requestUrl = wechatServiceProperties.formatGetUserUrl(token);
                JSONObject jsonObject = HttpUtils.httpsRequest(requestUrl, "GET", null);
                String dataJson = jsonObject.getString("data");
                JSONObject data = JSONObject.parseObject(dataJson);
                JSONArray openid = data.getJSONArray("openid");
                List<String> list = new ArrayList<>();
                if (openid != null && openid.size() > 0) {
                    for (int i = 0; i < openid.size(); i++) {
                        Object obj = openid.get(i);
                        if (obj != null) {
                            list.add(obj.toString());
                        }
                    }
                }
                return list;
            }
        } catch (Exception e) {
            log.error("[获取关注用户信息]数据异常:",e);
        }
        return null;
    }
}

思路是从微信获取token,在获取关注公众号的用户,按照模板构造消息,向微信接口发送消息。如果发送消息出现data format error错误,应该是模板消息不是正确的JSON格式。修改模板消息成正确的JSON格式。