微信小程序消息推送功能开发(java实现)

18 篇文章 0 订阅
2 篇文章 0 订阅

先好好把官方文档看一看,链接https://developers.weixin.qq.com/miniprogram/dev/framework/server-ability/message-push.html#option-url


主要用到的maven依赖:


        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.2</version>
        </dependency>

        <dependency>
            <groupId>net.sf.json-lib</groupId>
            <artifactId>json-lib</artifactId>
            <version>2.2.3</version>
            <classifier>jdk15</classifier>
        </dependency>

        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.2</version>
        </dependency>
  • Controller代码(把主要代码都写在这个类里了,读者可以自己做代码功能分离)
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.SerializedName;
import com.**.constant.WeChatConstants;
import com.**.util.HttpUtil;
import com.**.util.WeChatUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @description: 要把controller的echo方法对应的url配置到消息推送的URL(服务器地址)配置信息中,我们采用的是json格式,明文模式
 * @author: pilaf
 * @create: 2019-07-29 11:47
 */
@Slf4j
@RestController
@RequestMapping("/wechat")
public class WeChatController {

    /**
     * 要设置disableHtmlEscaping,否则会在转换成json的时候自作多情地转义一些特殊字符,如"="
     */
    private final Gson GSON = new GsonBuilder().disableHtmlEscaping().create();

    /**
     * 文本消息
     */
    private final String MSG_TYPE_TEXT = "text";

    /**
     * 苹果安卓通用的app下载链接
     */
    private final String APP_DOWNLOAD_URL = "做了脱敏处理啦。。。";

    @Autowired
    private WeChatUtil weChatUtil;
    @Autowired
    private HttpUtil httpUtil;

    @RequestMapping(value = "/chat/echo", method = {RequestMethod.GET, RequestMethod.POST})
    public String echo(HttpServletRequest request, HttpServletResponse response) {
        log.info("echo method invoked");
        boolean isGetRequest = request.getMethod().toLowerCase().equals("get");

        if (isGetRequest) {
        	//用于给微信服务器调用,以开通消息推送功能
            doGet(request, response);
        } else {
            //用于接收用户输入信息,其实也是给微信服务器调用的,它会将用户输入转发到这儿来
            doPost(request, response);
        }

        return null;
    }

    /**
     * 处理来自用户的信息
     *
     * @param request
     * @param response
     */
    private void doPost(HttpServletRequest request, HttpServletResponse response) {
        try {
            BufferedReader streamReader = new BufferedReader(new InputStreamReader(request.getInputStream(), "UTF-8"));
            StringBuilder requestStr = new StringBuilder();
            String line;
            while ((line = streamReader.readLine()) != null) {
                requestStr.append(line);
            }

            log.info("doPost receive user post message:{}", requestStr);

            UserTextRequest userTextRequest = GSON.fromJson(requestStr.toString(), UserTextRequest.class);

            if (MSG_TYPE_TEXT.equals(userTextRequest.getMsgType())) {

                // 用户输入1,返回app下载链接
                if ("1".equals(userTextRequest.getContent())) {
                    log.info("用户输入1");
                    UserTextResponse userTextResponse = new UserTextResponse();
                    userTextResponse.setToUser(userTextRequest.getFromUserName());
                    userTextResponse.setMsgType(MSG_TYPE_TEXT);
                    Map<String, String> textContent = new HashMap<>();
                    textContent.put("content", APP_DOWNLOAD_URL);
                    userTextResponse.setText(textContent);
                    String accessToken = weChatUtil.getAccessToken();
                    String url = MessageFormat.format(WeChatConstants.CUSTOM_SEND_URL, accessToken);
                    JSONObject result = httpUtil.sendJsonPost(url, GSON.toJson(userTextResponse));

                    log.info("result:{}", result);
                    if (result.getInt("errcode") == 0) {
                        ResponseWriterTemplate.execute(response, (writer) -> {
                            writer.print("success");
                            writer.flush();
                        });
                    }
                }
            }
        } catch (IOException e) {
            log.error("doPost failed", e);
        }

    }

    /**
     * 处理来自微信服务端的消息
     *
     * @param request
     * @param response
     */
    private void doGet(HttpServletRequest request, HttpServletResponse response) {
        log.info("doGet receive WeChat server request parameters:{}", request.getParameterMap());
        String signature = request.getParameter("signature");
        String timestamp = request.getParameter("timestamp");
        String nonce = request.getParameter("nonce");
        String echoStr = request.getParameter("echostr");

        //必须与在小程序后台->开发->开发配置->消息推送中填写的token一致,
        String token = "mytoken996";

        String[] arr = new String[] {token, timestamp, nonce};

        String result = weChatUtil.encodeUsingSHA1(arr);

        ResponseWriterTemplate.execute(response, (writer) -> {
            if (result.equalsIgnoreCase(signature)) {
                writer.print(echoStr);
                writer.flush();

                log.info("echo {} back to WeChat Server", echoStr);
            } else {
                writer.print("");
            }
        });

    }

    /**
     * 对PrintWriter输出内容做一个模版类,避免每次调用都要写一边try catch finally
     */
    private static class ResponseWriterTemplate {
        public static void execute(HttpServletResponse response, PrintCallback callback) {
            PrintWriter writer = null;
            try {
                writer = response.getWriter();
                log.info("ResponseWriterTemplate doPrint");
                callback.doPrint(writer);
            } catch (IOException e) {
                log.error("getWriter failed", e);
                throw new RuntimeException("getWriter failed");
            } finally {
                if (writer != null) {
                    writer.close();
                }
            }
        }
    }

    private interface PrintCallback {
        void doPrint(PrintWriter writer);
    }

    /**
     * 用户通过小程序发送的消息经过微信服务器转发到后端之后的结构
     * 微信发送的json字段名字都是大写字母开头
     */
    @Data
    private static class UserTextRequest {
        /**
         * 小程序的原始id
         */
        @SerializedName("ToUserName")
        private String toUserName;
        /**
         * 用户openid
         */
        @SerializedName("FromUserName")
        private String fromUserName;
        /**
         * 事件创建时间
         */
        @SerializedName("CreateTime")
        private Long createTime;
        @SerializedName("MsgType")
        private String msgType;
        /**
         * 用户发送的文本字段
         */
        @SerializedName("Content")
        private String content;
        @SerializedName("MsgId")
        private String msgId;
    }

    /**
     * 响应用户的文本信息
     */
    @Data
    private static class UserTextResponse {
        @SerializedName("touser")
        private String toUser;

        @SerializedName("msgtype")
        private String msgType;

        @SerializedName("text")
        private Map<String, String> text;
    }

}

  • WeChatUtil工具类
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
import com.**.WeChatProperties;
import com.**.WeChatConstants;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;


import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.text.MessageFormat;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

/**
 * @description: 微信工具类
 * @author: pilaf
 * @create: 2019-04-22 11:05
 */
@Slf4j
@Component
public class WeChatUtil {

    private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    private final String ACCESS_TOKEN_KEY = "wechat:access_token";
    /**
     * 日期格式化格式
     */
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");

    private static final Random RANDOM = new SecureRandom();
    /**
     * 要设置disableHtmlEscaping,否则会在转换成json的时候自作多情地转义一些特殊字符,如"="
     */
    private final Gson GSON = new GsonBuilder().disableHtmlEscaping().create();

    @Autowired
    private WeChatProperties weChatProperties;
    @Autowired
    private HttpUtil httpUtil;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;



    /**
     * XML格式字符串转换为Map
     *
     * @param strXML XML字符串
     * @return XML数据转换后的Map
     * @throws Exception
     */
    public static Map<String, String> xmlToMap(String strXML) throws Exception {
        try {
            Map<String, String> data = new HashMap<String, String>();
            DocumentBuilder documentBuilder = newDocumentBuilder();
            InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
            org.w3c.dom.Document doc = documentBuilder.parse(stream);
            doc.getDocumentElement().normalize();
            NodeList nodeList = doc.getDocumentElement().getChildNodes();
            for (int idx = 0; idx < nodeList.getLength(); ++idx) {
                Node node = nodeList.item(idx);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                    data.put(element.getNodeName(), element.getTextContent());
                }
            }
            try {
                stream.close();
            } catch (Exception ex) {
                // do nothing
            }
            return data;
        } catch (Exception ex) {
            log.warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
            throw ex;
        }

    }

    /**
     * 将Map转换为XML格式的字符串
     *
     * @param data Map类型数据
     * @return XML格式的字符串
     * @throws Exception
     */
    public static String mapToXml(Map<String, String> data) throws Exception {
        org.w3c.dom.Document document = newDocument();
        org.w3c.dom.Element root = document.createElement("xml");
        document.appendChild(root);
        for (String key : data.keySet()) {
            String value = data.get(key);
            if (value == null) {
                value = "";
            }
            value = value.trim();
            org.w3c.dom.Element filed = document.createElement(key);
            filed.appendChild(document.createTextNode(value));
            root.appendChild(filed);
        }
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        DOMSource source = new DOMSource(document);
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        StringWriter writer = new StringWriter();
        StreamResult result = new StreamResult(writer);
        transformer.transform(source, result);
        String output = writer.getBuffer().toString();
        try {
            writer.close();
        } catch (Exception ex) {
            log.error("mapToXml failed", ex);
        }
        return output;
    }

    /**
     * 将对象转换成xml,对象属性为xml节点,xml根结点是"xml"
     *
     * @param obj
     * @return
     */
    public static String objToXml(Object obj) {
        if (obj == null) {
            throw new IllegalArgumentException("objToXml obj is null");
        }
        //要把两个下划线换成一个
        XStream xStream = new XStream(new DomDriver(StandardCharsets.UTF_8.name(), new XmlFriendlyNameCoder("-_", "_")));
        xStream.alias("xml", obj.getClass());
        String xml = xStream.toXML(obj);

        return xml;
    }

    public static <T> T xmlToObject(String xml, Class<T> clazz) {
        if (StringUtils.isEmpty(xml)) {
            throw new IllegalArgumentException("xmlToObject xml is empty");
        }
        XStream xStream = new XStream();
        xStream.alias("xml", clazz);
        T result = (T) xStream.fromXML(xml);

        return result;
    }

    /**
     * 生成签名
     *
     * @param data 待签名数据
     * @param key API密钥
     * @return 签名
     */
    public static String generateSignature(final Map<String, String> data, String key) throws Exception {
        return generateSignature(data, key, WeChatConstants.MD5);
    }

    /**
     * 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
     *
     * @param data 待签名数据
     * @param key API密钥
     * @param signType 签名方式
     * @return 签名
     */
    public static String generateSignature(final Map<String, String> data, String key, String signType) {
        Set<String> keySet = data.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (k.equals(WeChatConstants.FIELD_SIGN)) {
                continue;
            }
            // 参数值为空,则不参与签名
            if (data.get(k).trim().length() > 0) {
                sb.append(k).append("=").append(data.get(k).trim()).append("&");
            }
        }
        sb.append("key=").append(key);

        return generateBySignType(sb.toString(), key, signType);
    }

    /**
     * 生成签名
     *
     * @param obj 要参与签名的数据对象
     * @return
     * @throws IllegalAccessException
     */
    public static String generateSignature(Object obj, String key, String signType) {
        ArrayList<String> list = new ArrayList<>();
        Class clazz = obj.getClass();
        Field[] fields = clazz.getDeclaredFields();
        try {
            for (Field field : fields) {
                field.setAccessible(true);
                //只有非空字段才参与签名计算
                if (field.get(obj) != null && field.get(obj) != "") {
                    list.add(field.getName() + "=" + field.get(obj));
                }
            }
        } catch (IllegalAccessException e) {
            log.error("生成签名失败", e);
            throw ServerException.create("生成签名失败");
        }

        int size = list.size();
        String[] arrayToSort = list.toArray(new String[size]);
        //对参与签名计算的参数进行升序排序
        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < size; i++) {
            sb.append(arrayToSort[i]);
            //非最后一个参数项要加&符号
            if (i != size - 1) {
                sb.append("&");
            }
        }

        String result = sb.toString();
        result += "&key=" + key;

        return generateBySignType(result, key, signType);
    }

    /**
     * 获取随机字符串 Nonce Str
     *
     * @return String 随机字符串
     */
    public static String generateNonceStr() {
        char[] nonceChars = new char[32];
        for (int index = 0; index < nonceChars.length; ++index) {
            nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
        }
        return new String(nonceChars);
    }

    private static String generateBySignType(String str, String key, String signType) {
        if (WeChatConstants.MD5.equals(signType)) {
            return MD5(str).toUpperCase();
        } else if (WeChatConstants.HMACSHA256.equals(signType)) {
            return HMACSHA256(str, key);
        } else {
            throw ServerException.create(String.format("Invalid sign_type: %s", signType));
        }
    }

    /**
     * 生成 MD5
     *
     * @param data 待处理数据
     * @return MD5结果
     */
    public static String MD5(String data) {
        byte[] array = new byte[0];
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            array = md.digest(data.getBytes("UTF-8"));
        } catch (Exception e) {
            log.error("MD5 Exception", e);
            throw ServerException.create("MD5加密失败");
        }

        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }

    /**
     * 生成 HMACSHA256
     *
     * @param data 待处理数据
     * @param key 密钥
     * @return 加密结果
     * @throws Exception
     */
    public static String HMACSHA256(String data, String key) {
        byte[] array = new byte[0];
        try {
            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
            sha256_HMAC.init(secret_key);
            array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
        } catch (Exception e) {
            log.error("HMACSHA256 Exception", e);
            throw ServerException.create("HMACSHA256加密失败");
        }

        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }

    /**
     * 获取当前时间戳,单位秒
     *
     * @return
     */
    public static long getCurrentTimestamp() {
        return System.currentTimeMillis() / 1000;
    }

    /**
     * 获取当前时间戳,单位毫秒
     *
     * @return
     */
    public static long getCurrentTimestampMs() {
        return System.currentTimeMillis();
    }

    /**
     * 获取交易单号前面是日期后面是随机字符串
     *
     * @return
     */
    public static String generateTradeNo() {
        return getRandomStringByLength(32);
    }

    /**
     * 获取一定长度的随机字符串
     *
     * @param length 指定字符串长度
     * @return 一定长度的字符串
     */
    private static String getRandomStringByLength(int length) {

        StringBuffer orderSNBuffer = new StringBuffer();
        //14是上面日期格式化的14位长度
        orderSNBuffer.append(getRandomString(length - 14));

        String result = LocalDateTime.now().format(FORMATTER) + orderSNBuffer.toString();

        return result;
    }

    private static String getRandomString(int len) {
        StringBuilder sb = new StringBuilder(len);
        int length = SYMBOLS.length();
        for (int i = 0; i < length; i++) {
            Random random = new Random();
            sb.append(SYMBOLS.charAt(random.nextInt(length)));
        }
        //14+18=32
        return sb.toString().substring(0, 18);
    }

    /**
     * 获取小程序access_token
     *
     * @return
     */
    public String getAccessToken() {
        //先从redis缓存中取,如果取不到再去微信服务端获取
        String accessToken = stringRedisTemplate.opsForValue().get(ACCESS_TOKEN_KEY);
        if (!StringUtils.isEmpty(accessToken)) {
            return accessToken;
        }

        log.info("access token 过期了,将重新获取");
        String url = MessageFormat.format(WeChatConstants.TOKEN_URL, weChatProperties.getAppId(), weChatProperties.getSecret());
        JSONObject jsonObject = httpUtil.sendGet(url);

        String token = jsonObject.getString(WeChatConstants.ACCESS_TOKEN);
        Integer expireSeconds = jsonObject.getInt("expires_in");

        if (StringUtils.isEmpty(token)) {
            log.error("未获取到access token:{}", jsonObject);
            throw ServerException.create("cannot get access token.");
        }
        log.info("获取到access token:{}", token);

        stringRedisTemplate.opsForValue().set(ACCESS_TOKEN_KEY, token, Duration.ofSeconds(expireSeconds));
        return token;
    }

    /**
     * 如果通过别的途径获取了access_token,将导致redis上的access_token invalid,此时就要把invalid的token删掉
     */
    private void deleteInvalidAccessToken() {
        stringRedisTemplate.opsForValue().getOperations().delete(ACCESS_TOKEN_KEY);
    }


    /**
     * 使用SHA1算法对字符串数组进行加密
     *
     * @param strList
     * @return
     */
    public String encodeUsingSHA1(String... strList) {
        //将strList的值进行字典排序
        Arrays.sort(strList);
        StringBuilder content = new StringBuilder();
        for (int i = 0; i < strList.length; i++) {
            content.append(strList[i]);
        }

        String tmpStr  = doEncodeUsingSHA1(content.toString());
        return tmpStr;
    }


    /**
     * SHA1实现
     *
     * @return sha1加密后的字符串
     */
    private static String doEncodeUsingSHA1(String inStr)  {
        byte[] byteArray ;

        try {
            MessageDigest sha = MessageDigest.getInstance("SHA-1");
            byteArray = sha.digest(inStr.getBytes("UTF-8"));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("no sha-1 algorithm");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("unsupported utf-8 encoding");
        }

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < byteArray.length; i++) {
            sb.append(Integer.toString((byteArray[i] & 0xff) + 0x100, 16).substring(1));
        }

        return sb.toString();
    }


}

  • HttpUtil工具类
import com.**.config.WeChatProperties;
import com.**.constant.WeChatConstants;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

/**
 * @description:
 * @author: pilaf
 * @create: 2019-04-23 15:10
 */
@Slf4j
@Component
public class HttpUtil {

    @Autowired
    private CertUtil certUtil;

    @Autowired
    private WeChatProperties weChatProperties;
    /**
     * Json类型响应处理器
     */
    private ResponseHandler<JSONObject> jsonResponseHandler = response -> {
        int statusCode = response.getStatusLine().getStatusCode();
        //正常响应
        if (statusCode >= 200 && statusCode < 300) {
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                return getJsonObject(entity);
            } else {
                return null;
            }
        } else {
            throw new ClientProtocolException("Unexpected response status:" + statusCode);
        }
    };

    /**
     * String类型响应处理器
     */
    private ResponseHandler<String> stringResponseHandler = response -> {
        int statusCode = response.getStatusLine().getStatusCode();
        //正常响应
        if (statusCode >= 200 && statusCode < 300) {
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                return EntityUtils.toString(entity, WeChatConstants.UTF8);
            } else {
                return null;
            }
        } else {
            throw new ClientProtocolException("Unexpected response status:" + statusCode);
        }
    };

    /**
     * 发送get请求
     *
     * @param url 请求url
     * @return 响应JSON
     */
    public JSONObject sendGet(String url) {
        //创建连接
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            //创建HttpGet实例
            HttpGet httpGet = new HttpGet(url);
            JSONObject responseBody = httpClient.execute(httpGet, jsonResponseHandler);
            return responseBody;
        } catch (IOException e) {
            log.error("sendGet failed, url:{}", url, e);
            return null;
        }
    }

    /**
     * 发送get请求
     *
     * @param url 请求url
     * @param responseClazz 响应体对应的类
     * @param <T>
     * @return
     */
    public <T> T sendGet(String url, Class<T> responseClazz) {
        //创建连接
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            //创建HttpGet实例
            HttpGet httpGet = new HttpGet(url);
            JSONObject responseBody = httpClient.execute(httpGet, jsonResponseHandler);

            T resultObj = (T) JSONObject.toBean(responseBody, responseClazz);
            return resultObj;
        } catch (IOException e) {
            log.error("sendGet failed, url:{},responseClazz:{}", url, responseClazz, e);
            return null;
        }
    }

    /**
     * 发送http post请求,请求体是json
     *
     * @param url 请求url
     * @param obj 请求体对象
     * @return
     */
    public JSONObject sendJsonPost(String url, Object obj) {
        String jsonStr = JSONObject.fromObject(obj).toString();
        return sendJsonPost(url, jsonStr);
    }

    /**
     * 发送http post请求,请求体是json
     *
     * @param url 请求url
     * @param jsonContext 请求体json
     * @return
     */
    public JSONObject sendJsonPost(String url, String jsonContext) {
        log.info("sendJsonPost,url:{},json:{}", url, jsonContext);
        HttpPost httpPost = new HttpPost(url);

        StringEntity stringEntity = new StringEntity(jsonContext, ContentType.create("text/plain", WeChatConstants.UTF8));

        //设置需要传递的数据
        httpPost.setEntity(stringEntity);

        //创建一个HttpClient连接
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            JSONObject responseBody = httpClient.execute(httpPost, jsonResponseHandler);
            return responseBody;
        } catch (IOException e) {
            log.error("sendJsonPost failed", e);
            return null;
        }

    }

    private JSONObject getJsonObject(HttpEntity entity) throws IOException {
        String result = EntityUtils.toString(entity);
        //根据字符串生成JSONObject对象
        JSONObject resultObj = JSONObject.fromObject(result);
        return resultObj;
    }

    /**
     * 发送post请求,请求内容是xml
     *
     * @param url 请求url
     * @param xmlStr 请求的xml字符串
     * @param isLoadCert 是否使用证书(企业付款到个人需要使用证书)
     * @return 响应字符串
     * @throws Exception
     */
    public String sendXmlPost(String url, String xmlStr, boolean isLoadCert) {
        HttpPost httpPost = new HttpPost(url);
        // 得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
        StringEntity stringEntity = new StringEntity(xmlStr, ContentType.create("text/xml", WeChatConstants.UTF8));

        httpPost.setEntity(stringEntity);

        if (isLoadCert) {
            //加载含有证书的http请求,创建一个HttpClient连接
            try (CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(certUtil.initCert()).build()) {
                String responseBody = httpClient.execute(httpPost, stringResponseHandler);
                return responseBody;
            } catch (IOException e) {
                log.error("sendXmlPost with cert file failed", e);
                return null;
            }

        } else {
            try (CloseableHttpClient httpClient = HttpClients.custom().build()) {
                String responseBody = httpClient.execute(httpPost, stringResponseHandler);
                return responseBody;
            } catch (IOException e) {
                log.error("sendXmlPost without cert file failed", e);
                return null;
            }

        }

    }
}

  • WechatConstants常量类


/**
 * @description: 微信相关常量类
 * @author: pilaf
 * @create: 2019-04-22 10:06
 */
public interface WeChatConstants {

    /**
     * 企业付款的微信url
     */
    String COMPANY_PAY_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";

    /**
     * 获取企业付款结果的url
     */
    String GET_TRANSFER_INFO_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo";

    /**
     * 小程序登录校验
     */
    String WX_MINI_LOGIN = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code";
    /**
     * 获取小程序access_token的url
     */
    String TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}";

    /**
     * 用于向微信服务端申请二维码的url
     */
    String URL_QR_CODE = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token={0}";

    /**
     * 用于聊天时向用户发送消息的url
     */
    String CUSTOM_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token={0}";

    /**
     * 企业付款到个人时,不校验真实姓名
     */
    String NO_CHECK = "NO_CHECK";

    /**
     * 企业付款到个人时,强校验真实姓名
     */
    String FORCE_CHECK = "FORCE_CHECK";

    /**
     * 成功
     */
    String SUCCESS = "SUCCESS";

    /**
     * 微信系统错误
     */
    String SYSTEMERROR = "SYSTEMERROR";

    /**
     * 失败 (注意:微信有的接口返回的失败用FAIL字符串表示,有的接口用FAILED表示)
     */
    String FAIL = "FAIL";

    /**
     * 微信企业付款到个人失败 (注意:微信有的接口返回的失败用FAIL字符串表示,有的接口用FAILED表示)
     */
    String FAILED = "FAILED";

    /**
     * 微信企业付款到个人正在处理中
     */
    String PROCESSING = "PROCESSING";

    String HMACSHA256 = "HMAC-SHA256";

    String MD5 = "MD5";

    String UTF8 = "UTF-8";

    String FIELD_SIGN = "sign";

    String FIELD_SIGN_TYPE = "sign_type";

    String RESULT_CODE = "result_code";

    /**
     * 通信标识,非交易标识,交易是否成功需要查看result_code来判断
     */
    String RETURN_CODE = "return_code";

    String OPENID = "openid";

    String STATUS = "status";

    String REASON = "reason";

    String ACCESS_TOKEN = "access_token";
}

  • WeChatProperties配置类(用于映射application.yml中的配置)
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * @description: 微信相关的配置信息
 * @author: pilaf
 * @create: 2019-04-22 19:49
 */
@Data
@Component
@ConfigurationProperties(prefix = "wxconfig")
public class WeChatProperties {

    /**
     * 微信小程序appid
     */
    private String appId;

    /**
     * 微信支付商户号
     */
    private String mchId;

    /**
     * api密钥,交易过程中生成签名的密钥,仅保留在商户系统和微信支付后台,不会在网络中传播
     */
    private String key;

    /**
     * Appsecret,是appid对应的接口密码,用于获取接口调用凭证access_token时使用
     */
    private String secret;

    /**
     * API证书路径
     */
    private String certPath

}

  • application.yml配置文件
wxconfig:
    #微信小程序appid
    appId: 你的小程序appid
    #微信支付商户号,如果没有企业付款的功能可以不用加这个配置
    mchId: 微信支付商户号
    #api密钥,交易过程中生成签名的密钥,仅保留在商户系统和微信支付后台,不会在网络中传播
    key: 你的api密钥
    #Appsecret,是appid对应的接口密码,用于获取接口调用凭证access_token时使用。
    secret: 你的appsecret
    #api证书路径,放在java/main/resources目录下打到jar包中,或者放到服务器指定路径
    certPath: cert/apiclient_cert.p12

参考文章:
1.https://developers.weixin.qq.com/miniprogram/dev/framework/server-ability/message-push.html#option-url
2.https://blog.csdn.net/weixin_42342572/article/details/80506303
3.https://developers.weixin.qq.com/community/develop/article/doc/00066a67324e70bdf0981381b5c813

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值