springboot推送微信公众号消息,java推送微信公众号消息

文章目录

    • 1. 公众号申请和配置
      • 首先!!记得设置白名单
    • 2. 依赖
      • 2.1 父子工程聚合
      • 2.2 独立的单独工程
    • 3. 工具类
      • 3.1 HttpUtil http请求工具类
      • 3.2 RestTemplateConfig 配置类
      • 3.3 WeChatTemplate 短信模板实体类
      • 3.4 WxConstant 微信相关参数封装类
      • 3.5 WxGetTokenUtil 生成 access_token
      • 3.7 WxMpConfiguration 微信server的配置类,不写这个server就没法用
      • 3.8 WxResult 微信返回错误码封装类
      • 3.9 WxSendUtill 发送消息工具类
    • 4. 调用(controller)

1. 公众号申请和配置

首先!!记得设置白名单

2. 依赖

2.1 父子工程聚合

   
	<properties>
			<weixin-java-mp.version>3.0.0weixin-java-mp.version>
    properties>

	<dependencyManagement>
        <dependencies>
            
			
            <dependency>
                <groupId>com.github.binarywanggroupId>
                <artifactId>weixin-java-mpartifactId>
                <version>${weixin-java-mp.version}version>
            dependency>
            
            <dependency>
                <groupId>com.alibabagroupId>
                <artifactId>fastjsonartifactId>
                <version>${fastjson.version}version>
            dependency>

        dependencies>
	dependencyManagement>



			
            <dependency>
                <groupId>com.github.binarywanggroupId>
                <artifactId>weixin-java-mpartifactId>
            dependency>
            
            <dependency>
                <groupId>com.alibabagroupId>
                <artifactId>fastjsonartifactId>
            dependency>

2.2 独立的单独工程

		   	
			<dependency>
                <groupId>com.github.binarywanggroupId>
                <artifactId>weixin-java-mpartifactId>
                <version>3.0.0version>
            dependency>
		   <dependency>
                <groupId>com.alibabagroupId>
                <artifactId>fastjsonartifactId>
                <version>1.2.47version>
            dependency>




3. 工具类

目录结构:

  • util.wx文件夹
    • HttpUtil.java 类
    • RestTemplateConfig.java 类
    • WeChatTemplate 类
    • WxConstant.java 类
    • WxGetTokenUtil.java 类
    • WxResult.java 类
    • WxSendUtil.java 类

3.1 HttpUtil http请求工具类

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;

import javax.net.ssl.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;

@Slf4j
public class HttpUtil {
 
    protected static final String POST_METHOD = "POST";
    private static final String GET_METHOD = "GET";

    static {
        TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                log.debug("ClientTrusted");
            }

            @Override
            public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                log.debug("ServerTrusted");
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[]{};
            }
        }};

        HostnameVerifier doNotVerify = (s, sslSession) -> true;

        try {
            SSLContext sc = SSLContext.getInstance("SSL", "SunJSSE");
            sc.init(null, trustAllCerts, new SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
            HttpsURLConnection.setDefaultHostnameVerifier(doNotVerify);
        } catch (Exception e) {
            log.error("Initialization https impl occur exception : {}", e);
        }
    }


    /**
     * 默认的http请求执行方法
     *
     * @param url  url 路径
     * @param method 请求的方法 POST/GET
     * @param map  请求参数集合
     * @param data  输入的数据 允许为空
     * @return result
     */
    private static String HttpDefaultExecute(String url, String method, Map<String, String> map, String data) {
        String result = "";
        try {
            url = setParmas(url, map, null);
            result = defaultConnection(url, method, data);
        } catch (Exception e) {
            log.error("出错参数 {}", map);
        }
        return result;
    }

    public static String httpGet(String url, Map<String, String> map) {
        return HttpDefaultExecute(url, GET_METHOD, map, null);
    }

    public static String httpPost(String url, Map<String, String> map, String data) {
        return HttpDefaultExecute(url, POST_METHOD, map, data);
    }

    /**
     * 默认的https执行方法,返回
     *
     * @param url  url 路径
     * @param method 请求的方法 POST/GET
     * @param map  请求参数集合
     * @param data  输入的数据 允许为空
     * @return result
     */
    private static String HttpsDefaultExecute(String url, String method, Map<String, String> map, String data) {
        try {
            url = setParmas(url, map, null);
            log.info(data);
            return defaultConnection(url, method, data);
        } catch (Exception e) {
            log.error("出错参数 {}", map);
        }
        return "";
    }

    public static String doGet(String url, Map<String, String> map) {
        return HttpsDefaultExecute(url, GET_METHOD, map, null);
    }

    public static String doPost(String url, Map<String, String> map, String data) {
        return HttpsDefaultExecute(url, POST_METHOD, map, data);
    }

    /**
     * @param path  请求路径
     * @param method 方法
     * @param data  输入的数据 允许为空
     * @return
     * @throws Exception
     */
    private static String defaultConnection(String path, String method, String data) throws Exception {
        if (StringUtils.isBlank(path)) {
            throw new IOException("url can not be null");
        }
        String result = null;
        URL url = new URL(path);
        HttpURLConnection conn = getConnection(url, method);
        if (StringUtils.isNotEmpty(data)) {
            OutputStream output = conn.getOutputStream();
            output.write(data.getBytes(StandardCharsets.UTF_8));
            output.flush();
            output.close();
        }
        if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
            InputStream input = conn.getInputStream();
            result = IOUtils.toString(input, StandardCharsets.UTF_8);
            input.close();
            conn.disconnect();
        }
        return result;
    }

    /**
     * 根据url的协议选择对应的请求方式
     * @param url  请求路径
     * @param method 方法
     * @return conn
     * @throws IOException 异常
     */
    //待改进
    protected static HttpURLConnection getConnection(URL url, String method) throws IOException {
        HttpURLConnection conn;
        if (StringUtils.equals("https", url.getProtocol())) {
            conn = (HttpsURLConnection) url.openConnection();
        } else {
            conn = (HttpURLConnection) url.openConnection();
        }
        if (conn == null) {
            throw new IOException("connection can not be null");
        }
        conn.setRequestProperty("Pragma", "no-cache");// 设置不适用缓存
        conn.setRequestProperty("Cache-Control", "no-cache");
        conn.setRequestProperty("Connection", "Close");// 不支持Keep-Alive
        conn.setUseCaches(false);
        conn.setDoOutput(true);
        conn.setDoInput(true);
        conn.setInstanceFollowRedirects(true);
        conn.setRequestMethod(method);
        conn.setConnectTimeout(8000);
        conn.setReadTimeout(8000);
        return conn;
    }

    /**
     * 根据url
     * @param url 请求路径
     * @return isFile
     * @throws IOException 异常
     */
    //待改进
    protected static HttpURLConnection getConnection(URL url, boolean isFile) throws IOException {
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        if (conn == null) {
            throw new IOException("connection can not be null");
        }
        //设置从httpUrlConnection读入
        conn.setDoInput(true);
        conn.setDoOutput(true);
        conn.setUseCaches(false);
        //如果是上传文件,则设为POST
        if (isFile) {
            conn.setRequestMethod(POST_METHOD); //GET和 POST都可以 文件略大改成POST
        }
        // 设置请求头信息
        conn.setRequestProperty("Connection", "Keep-Alive");
        conn.setRequestProperty("Charset", String.valueOf(StandardCharsets.UTF_8));
        conn.setConnectTimeout(8000);
        conn.setReadTimeout(8000);
        return conn;
    }

    /**
     * 拼接参数
     * @param url   需要拼接参数的url
     * @param map   参数
     * @param charset 编码格式
     * @return 拼接完成后的url
     */
    public static String setParmas(String url, Map<String, String> map, String charset) throws Exception {
        String result = StringUtils.EMPTY;
        boolean hasParams = false;
        if (StringUtils.isNotEmpty(url) && map != null && !map.isEmpty()) {
            StringBuilder builder = new StringBuilder();
            for (Map.Entry<String, String> entry : map.entrySet()) {
                String key = entry.getKey().trim();
                String value = entry.getValue().trim();
                if (hasParams) {
                    builder.append("&");
                } else {
                    hasParams = true;
                }
                if (StringUtils.isNotEmpty(charset)) {
                    builder.append(key).append("=").append(URLEncoder.encode(value, charset));
                } else {
                    builder.append(key).append("=").append(value);
                }
            }
            result = builder.toString();
        }
        URL u = new URL(url);
        if (StringUtils.isEmpty(u.getQuery())) {
            if (url.endsWith("?")) {
                url += result;
            } else {
                url = url + "?" + result;
            }
        } else {
            if (url.endsWith("&")) {
                url += result;
            } else {
                url = url + "&" + result;
            }
        }
        log.debug("request url is {}", url);
        return url;
    }
}

3.2 RestTemplateConfig 配置类

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
        return new RestTemplate(factory);
    }
    @Bean
    public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setConnectTimeout(15*1000);
        factory.setReadTimeout(60*60*1000);
        return factory;
    }
}

3.3 WeChatTemplate 短信模板实体类

import lombok.Data;
import java.util.TreeMap;
@Data
public class WeChatTemplate {
    private String touser; //接收者openid
    private String template_id; //模板ID
    private String url; //URL置空,则在发送后,点击模板消息会进入一个空白页面(ios),或无法点击(android)
    private TreeMap<String, TreeMap<String, String>> data; //data数据
    public static TreeMap<String, String> item(String value, String color) {
        TreeMap<String, String> params = new TreeMap<String, String>();
        params.put("value", value);
        params.put("color", color);
        return params;
    }
    @Override
    public String toString() {
        return "WechatTemplate" +
                "{" +
                "openId='" + touser + "'," +
                "template_id='" + template_id + "'," +
                "url='" + url + "'," +
                "data=" + data +
                '}';
    }
}

3.4 WxConstant 微信相关参数封装类

public class WxConstant {
    /**
     * 开发者appId
     */
    public static final String appId="xxxxxxxxxxxxxxxxx";
    /**
     * 开发者密码 appSecret
     */
    public static final String appSecret="xxxxxxxxxxxxxxxxxxxxxxxx";
    /**
     * templateId:微信公众号模板id
     */
    public static final String templateId="xxxxxxxxxxxxxxxxxxxx";
}

3.5 WxGetTokenUtil 生成 access_token

import com.alibaba.fastjson.JSONObject;
import java.util.HashMap;
import java.util.Map;
public class WxGetTokenUtil {
    public static String getAccessToken() {
        String appSecret = WxConstant.appSecret;
        String appId = WxConstant.appId;
        String tokenUrl = "https://api.weixin.qq.com/cgi-bin/token";
        Map<String, String> params = new HashMap<>();
        params.put("appid", appId);
        params.put("secret", appSecret);
        params.put("grant_type", "client_credential");
        String response = HttpUtil.doGet(tokenUrl, params);
        System.out.println("WxGetTokenUtil.getAccessToken()返回值:" + response);//此处可注释,调试时解开,防止code出报错找不到原因
        JSONObject accessTokenObject = JSONObject.parseObject(response);
        return accessTokenObject.getString("access_token");
    }
}

3.7 WxMpConfiguration 微信server的配置类,不写这个server就没法用

import me.chanjar.weixin.mp.api.WxMpConfigStorage;
import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
import me.chanjar.weixin.mp.api.WxMpService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * wechat mp configuration
 */
@Configuration
@ConditionalOnClass(WxMpService.class)
public class WxMpConfiguration {
  @Bean
  @ConditionalOnMissingBean
  public WxMpConfigStorage configStorage() {
    WxMpInMemoryConfigStorage configStorage = new WxMpInMemoryConfigStorage();
    configStorage.setAppId(WxConstant.appId);
    configStorage.setSecret(WxConstant.appSecret);
//    configStorage.setToken(WxConstant.token);
//    configStorage.setAesKey(WxConstant.key);
    return configStorage;
  }
  @Bean
  @ConditionalOnMissingBean
  public WxMpService wxMpService(WxMpConfigStorage configStorage) {
    WxMpService wxMpService = new me.chanjar.weixin.mp.api.impl.WxMpServiceImpl();
    wxMpService.setWxMpConfigStorage(configStorage);
    return wxMpService;
  }
}

3.8 WxResult 微信返回错误码封装类

import lombok.Data;
@Data
public class WxResult {
    private String errcode;
    private String errmsg;
    private String msgid;
    @Override
    public String toString() {
        return "WxError{errcode='" + errcode + "',errmsg='" + errmsg + "',msgid='" + msgid + "'}";
    }
}

3.9 WxSendUtill 发送消息工具类

注意,参数是根据你自己的模板进行组装的,我的模板有以下参数

{first,keyword1,keyword2,keyword3,keyword4,keyword5,remark}

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TreeMap;
/**
 * 推送公众号消息
 */
@Slf4j
@Component //使用方式2,需要用到这个注解,并且需要用到WxMapConfiguration。
public class WxSendUtil {

    /**
     * 发送消息,方式1
     * @param openId 用户openId
     * @param data   数据
     * @param title  标题
     */
    public static WxResult send(String openId, String data, String title) {
        String url = "https://www.baidu.com";
        String templateUrl = "https://api.weixin.qq.com/cgi-bin/message/template/send";
        String time = parseDate3String(new Date());
        String accessToken = WxGetTokenUtil.getAccessToken();
        log.info("获取access_token,微信平台接口返回{}", accessToken);
        TreeMap<String, TreeMap<String, String>> params = new TreeMap<String, TreeMap<String, String>>();
        //根据具体模板参数的  添加数据
        params.put("first", WeChatTemplate.item(title, "#000000"));//一般是标题
        params.put("keyword1", WeChatTemplate.item("项目名称", "#ff0000"));//项目名称,字体颜色
        params.put("keyword2", WeChatTemplate.item(time, "#000000"));
        params.put("keyword3", WeChatTemplate.item(time, "#000000"));
        params.put("keyword4", WeChatTemplate.item(time, "#000000"));
        params.put("keyword5", WeChatTemplate.item(time, "#000000"));
        params.put("remark", WeChatTemplate.item(data, "#000000"));

        WeChatTemplate wechatTemplate = new WeChatTemplate();
        wechatTemplate.setTemplate_id(WxConstant.templateId);//模板id
        wechatTemplate.setTouser(openId);// 接收者openid
        wechatTemplate.setUrl(url); // 模板跳转链接
        wechatTemplate.setData(params);
        JSONObject json = JSONObject.fromObject(wechatTemplate);//将java对象转换为json对象
        String sendData = json.toString();//将json对象转换为字符串
        log.info("板参数组装{}", sendData);
        TreeMap<String, String> treeMap = new TreeMap<String, String>();
        treeMap.put("access_token", accessToken);
        String retInfo = HttpUtil.doPost(templateUrl, treeMap, sendData);
        if (!StringUtils.isBlank(retInfo)) {
            return JSON.parseObject(retInfo, WxResult.class);
        }
        log.info("消息模板返回{}", retInfo);
        return null;
    }
    

    @Autowired
    private WxMpService wxService;
    /**
     * 发送消息,方式2
     * 发送微信模板信息
     * @param openId openId
     * @param url    跳转地址
     * @param title  标题
     */
    public Boolean sendWxMsg(String openId, String url, String title) {
        String time = parseDate3String(new Date());
        // 发送模板消息接口
        WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder()
                // 接收者openid
                .toUser(openId)
                // 模板id
                .templateId(WxConstant.templateId)
                // 模板跳转链接
                .url(url)
                .build();
        // 添加模板数据
        templateMessage.addData(new WxMpTemplateData("first", title, "#FF00FF"))
                .addData(new WxMpTemplateData("keyword1", "紧急", "#A9A9A9"))
                .addData(new WxMpTemplateData("keyword2", time, "#A9A9A9"))
                .addData(new WxMpTemplateData("remark", url, "#FF00FF"));
        String msgId = null;
        try {
            // 发送模板消息
            msgId = this.wxService.getTemplateMsgService().sendTemplateMsg(templateMessage);
        } catch (WxErrorException e) {
            e.printStackTrace();
        }
        log.warn("·==++--·推送微信模板信息:{}·--++==·", msgId != null ? "成功" : "失败");
        return msgId != null;
    }
    
    
    //时间转化成字符串,格式  yyyy-MM-dd HH:mm:ss
    private static String parseDate3String(Date date) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sdf.format(date);
    }
    
}

4. 调用(controller)

发消息得有发送对象—用户,,要获取到用户的openId,然后才能进行发送

import com.alibaba.fastjson.JSONObject;
import com.sq.statistics.utils.wx.WxGetTokenUtil;
import com.sq.statistics.utils.wx.WxSendUtil;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/wx")
public class WXController {
    
    @Autowired
    private RestTemplate restTemplate;
    
    /**
     * 获取关注者的openid
     */
    @GetMapping("/open-id/list")
    public Map<String, Object> getOpenIdList(@RequestParam(required = false) String next_openid) {
        String url = "https://api.weixin.qq.com/cgi-bin/user/get?access_token={access_token}";
        Map<String, Object> param = new HashMap<>();
        param.put("next_openid", next_openid);
        param.put("access_token", WxGetTokenUtil.getAccessToken());
        HttpHeaders headers = new HttpHeaders();
        ResponseEntity<String> entity =
                restTemplate.exchange(
                        url,
                        HttpMethod.GET,
                        new HttpEntity<String>(headers),
                        String.class,
                        param);
        String result = entity.getBody();
        System.out.println("WXController.getOpenIdList()返回值:" + result);//此处可注释,调试时解开,防止code出报错
        if (result != null && result.contains("count")) {//返回值可能有两种,一种是报错的,一种是正常返回
            Map<String, Object> resultData = JSONObject.parseObject(result, Map.class);
//            return resultData;//可以直接返回,也可以自己抽取,,我只需要openId和next_openId
            String next_openId = resultData.get("next_openid").toString(); //这个是下一组数据需要的传参
            Map<String, Object> data = JSONObject.parseObject(resultData.get("data").toString(), Map.class);
            List<String> openidList = JSONObject.parseArray(data.get("openid").toString(), String.class);
            Map<String, Object> map = new HashMap<>();
            map.put("next_openId", next_openId);
            map.put("openidList", openidList);
            return map;
        }
        return null;//返回的是tokenList,如果想要下一组数据,最后传的

        /**
         * 正确返回
         * {"total":2,"count":2,"data":{"openid":["111111","222222"]},"next_openid":"222222"}
         *
         * 错误返回,下边这个是白名单没开
         * {"errcode":40164,"errmsg":"invalid ip 180.0.0.0 ipv6 ::ffff:180.0.0.0, not in whitelist rid: 6268e703-5bc5bc49-4d17363f"}
         */
    }

    /**
     * 发送消息
     */
    @PostMapping("/send/message")
    public void sendMessage(String message, String title, String openId) {
        WxSendUtil.send(openId, message, title);
    }
}

你可能感兴趣的:(Java,微信公众号,java)