微信开发相关知识点

由于个人公众中竟然需要用到微信相关开发,故整理下,以便后期查阅。

个人开发过程中积累的工具类Utilities 

package com.project.wx.util;


import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by zhu on 2017/4/19.
 */
public class Utilities {
    public static void main(String[] args) {
        
    }

    public static ResponseEntity returnResponseEntity(String result){
        return new ResponseEntity(result,Utilities.setAccessControl(), HttpStatus.OK);
    }

  public  static  String filter(String str) {

        if(str.trim().isEmpty()){
            return str;
        }
        String pattern="[\ud83c\udc00-\ud83c\udfff]|[\ud83d\udc00-\ud83d\udfff]|[\u2600-\u27ff]";
        String reStr="";
        Pattern emoji=Pattern.compile(pattern);
        Matcher emojiMatcher=emoji.matcher(str);
        str=emojiMatcher.replaceAll(reStr);

        return str;
    }

    public static long getCurrentTimeStamp(){
        long time = System.currentTimeMillis();

        return time;
    }
    public static long date2TimeStamp(String date){
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            return sdf.parse(date).getTime();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }
    public static boolean timeCompare(String beginTime,String endTime){
        boolean isBefore=false;
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        try {
            Date bt = sdf.parse(beginTime);
            Date et=sdf.parse(endTime);
            if (bt.before(et)){
                //表示bt小于et
                isBefore=true;
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
       return isBefore;
    }
    public static String getTimeTransform(String time){
        long create_time=date2TimeStamp(time);
        long currentTimeStamp=getCurrentTimeStamp();
        //得到距离当前时间
        long show_hour=(currentTimeStamp-create_time)/1000/60/60;
        String show_time=show_hour+"小时前";
        if(show_hour==0){
            show_time="刚刚";
        }
        if(show_hour>=24&&show_hour<24*7){
            //大于一天,小于一周
            show_hour=show_hour/24;
            show_time=show_hour+"天前";
        }
        if(show_hour>=24*7&&show_hour<24*30){
            //大于一周,小于一月
            show_hour=show_hour/24/7;
            show_time=show_hour+"周前";
        }
        if(show_hour>=24*30&&show_hour<24*30*12){
            //大于一月,小于一年
            show_hour=show_hour/24/30;
            show_time=show_hour+"月前";
        }
        if(show_hour>=24*30*12){
            //大于一年
            show_hour=show_hour/24/30/12;
            show_time=show_hour+"年前";
        }
        return show_time;
    }
    public static String timeStamp2Date(String timestamp){
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date date=new Date(Long.parseLong(timestamp));
            return sdf.format(date);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }
    public static String getCurrentTime() {
        Date inputDate = new Date();
        SimpleDateFormat outputFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String datetime = outputFormat.format(inputDate);
        return datetime;
    }
    public static String checkNull(String input){
        String result="";
        if(input==null||input.trim().equals("")||input.trim().equalsIgnoreCase("null")){
            result="";
        }else {
            result=input.trim();
        }
        return result;
    }
    public static boolean checkEmpty(String input){
        if(input==null||input.trim().equals("")||input.trim().equalsIgnoreCase("null")){
            return true;
        }else {
            return false;
        }
    }
   public static String getCurrentTimeSubOrAddHour(int hour) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        Calendar nowTime = Calendar.getInstance();
        nowTime.add(Calendar.HOUR, hour);
        String datetime = sdf.format(nowTime.getTime());

        return datetime;
    }

    public static int getDefaultValue(HttpServletRequest request, String paraName, int defaultValue){
        int result;
        try {
            result=Integer.valueOf(request.getParameter(paraName));
        } catch (NumberFormatException e) {
            result=defaultValue;
        }
        return result;
    }
    public static String getTimestamp() {
        Long timestamp = System.currentTimeMillis();
        return timestamp.toString();
    }
    /**
     * 加密算法
     */
    public static String encode( String str) {
        String ALGORITHM = "MD5";

        char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5',
                '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
        if (str == null) {
            return null;
        }
        try {
            MessageDigest messageDigest = MessageDigest.getInstance(ALGORITHM);
            messageDigest.update(str.getBytes("utf-8"));
            return getFormattedText(messageDigest.digest());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static String getFormattedText(byte[] bytes) {
        int len = bytes.length;
        StringBuilder buf = new StringBuilder(len * 2);
        char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5',
                '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
        // 把密文转换成十六进制的字符串形式
        for (int j = 0; j < len; j++) {
            buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);
            buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
        }
        return buf.toString();
    }
   /**
     * 产生8位随机数
     * @return
     */
    public static String random8(){

        int[] i=new int[8];
        int count=0;
        String randomNum="";
        while(count<8){
            //抽取的数值小于char类型的“z”(122)
            int t=(int)(Math.random()*122);
            if((t>=0&t<=9)|(t>=65&t<=90)|(t>=97&t<=122)){
                i[count]=t;
                count++;
            }
        }for(int k=0;k<8;k++){
            if(i[k]>=0&i[k]<=9){
                randomNum=randomNum+i[k];
            }
            else{
                randomNum=randomNum+(char)i[k];
            }
        }
        return randomNum;
    }

    public static HttpHeaders setAccessControl(){
        HttpHeaders httpHeaders=new HttpHeaders();
        httpHeaders.set("Content-Type", "application/json;charset=UTF-8");
        httpHeaders.set("Access-Control-Allow-Origin","*");
        return httpHeaders;
    }
    public static String getIpAddr(HttpServletRequest request){
        String ipAddress = request.getHeader("x-forwarded-for");
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
            if(ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")){
                //根据网卡取本机配置的IP
                InetAddress inet=null;
                try {
                    inet = InetAddress.getLocalHost();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }
                ipAddress= inet.getHostAddress();
            }
        }
        //对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
        if(ipAddress!=null && ipAddress.length()>15){
            //"***.***.***.***".length() = 15
            if(ipAddress.indexOf(",")>0){
                ipAddress = ipAddress.substring(0,ipAddress.indexOf(","));
            }
        }
        return ipAddress;
    }
    public static void getTimeSub(){
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        format.setLenient(false);
        Date date1 = null;
        try {
            date1 = format.parse("2018-04-16 10:00:00");
        } catch (ParseException e) {
            e.printStackTrace();
        }
        Date date2 = new Date();
        //计算差值,分钟数
        long minutes=(date2.getTime()-date1.getTime())/(1000*60);
        System.out.println(minutes);
        //计算差值,天数
        long days=(date2.getTime()-date1.getTime())/(1000*60*60*24);
        System.out.println(days);
    }
    /**
     * 比较两个字符串的大小,按字母的ASCII码比较
     * @param pre
     * @param next
     * @return
     * */
    public static boolean isMoreThan(String pre, String next){
        if(null == pre || null == next || "".equals(pre) || "".equals(next)){
            System.out.println("字符串比较数据不能为空!");
            return false;
        }

        char[] c_pre = pre.toCharArray();
        char[] c_next = next.toCharArray();

        int minSize = Math.min(c_pre.length, c_next.length);

        for (int i = 0; i < minSize; i++) {
            if((int)c_pre[i] > (int)c_next[i]){
                return true;
            }else if((int)c_pre[i] < (int)c_next[i]){
                return false;
            }
        }
        if(c_pre.length > c_next.length){
            return true;
        }

        return false;
    }
    public static String deciMal(int top, int below) {
        double result = new BigDecimal((float)top / below).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
        //获取格式化对象
        NumberFormat nt = NumberFormat.getPercentInstance();
        //设置百分数精确度2即保留两位小数
        nt.setMinimumFractionDigits(0);
        return nt.format(result);
    }
   public static Set<Integer> getRandomInt(int limit,int size){
       Set<Integer> resultSet=new HashSet<>();
       Random random = new Random();
       while (resultSet.size()<size){
           resultSet.add(random.nextInt(limit));
       }
       /**
        * set直接转array
        * Integer [] arr=resultSet.toArray(new Integer[resultSet.size()]);
        */

      /**
       * set遍历
       *   Iterator it = resultSet.iterator();
       while(it.hasNext()){
       System.out.println(it.next());
       }
       */


       return resultSet;
   }
    /**
     * 生成指定范围的随机数
     */
    public static void getLimitRandom() {
        int max=20;
        int min=10;
        Random random = new Random();

        int s = random.nextInt(max)%(max-min+1) + min;
        System.out.println(s);
    }

    public static JSONArray treeMenuList(JSONArray menuList, int parentId) {
        JSONArray childMenu = new JSONArray();
        for (Object object : menuList) {
            JSONObject jsonMenu = (JSONObject)object;
            int menuId = jsonMenu.getIntValue("thr_gz_id");
            int pid = jsonMenu.getIntValue("thr_p_id");
            if (parentId == pid) {
                JSONArray c_node = treeMenuList(menuList, menuId);
                jsonMenu.put("childNode", c_node);
                childMenu.add(jsonMenu);
            }
        }
        return childMenu;
    }
    //求两个数组的交集
    public static String[] intersect(String[] arr1, String[] arr2) {
        Map<String, Boolean> map = new HashMap<>();
        LinkedList<String> list = new LinkedList<String>();
        for (String str : arr1) {
            if (!map.containsKey(str)) {
                map.put(str, Boolean.FALSE);
            }
        }
        for (String str : arr2) {
            if (map.containsKey(str)) {
                map.put(str, Boolean.TRUE);
            }
        }

        for (Map.Entry<String, Boolean> e : map.entrySet()) {
            if (e.getValue().equals(Boolean.TRUE)) {
                list.add(e.getKey());
            }
        }

        String[] result = {};
        return list.toArray(result);
    }

    //求两个数组的差集
    public static String[] minus(String[] arr1, String[] arr2) {
        LinkedList<String> list = new LinkedList<String>();
        LinkedList<String> history = new LinkedList<String>();
        String[] longerArr = arr1;
        String[] shorterArr = arr2;
        //找出较长的数组来减较短的数组
        if (arr1.length > arr2.length) {
            longerArr = arr2;
            shorterArr = arr1;
        }
        for (String str : longerArr) {
            if (!list.contains(str)) {
                list.add(str);
            }
        }
        for (String str : shorterArr) {
            if (list.contains(str)) {
                history.add(str);
                list.remove(str);
            } else {
                if (!history.contains(str)) {
                    list.add(str);
                }
            }
        }

        String[] result = {};
        return list.toArray(result);
    }

    //求两个数组的差集
    public static List<String> getDelValue(String[] targetArray, String[] currentArray) {
        List<String> result = new ArrayList<>();

        for (String t : targetArray) {
            boolean isOK=true;
            for (String c : currentArray) {
               if(t.equals(c)){
                   isOK=false;
                   break;
               }
            }
            if(isOK){
                result.add(t);
            }
        }

        return result;
    }
}

接口返回值封装类Result

package com.ddyg.wx.domain;

public class Result {
    private String message="操作成功";
    private Object data;
    private int count;
    private boolean status=true;
    private int code=0;

    public Result(){

    }
    public Result(Object data, int count){
        this.data=data;
        this.count=count;
    }
    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public boolean isStatus() {
        return status;
    }

    public void setStatus(boolean status) {
        this.status = status;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }
}

 

 

一、获取accesstoken(微信开发最基础要求,几乎所有接口都要用到这个参数)

请求链接:https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

accesstoken每日获取总量有上限(2000次),所以这个accesstoken值应该存储起来,不能随意开火。个人一般把这个值存储在ServletContext(域)中,核心代码如下:

public static Result getAccessToken(HttpServletRequest request) {
       
        Result result = new Result();
        try {
            //先看一眼全局变量里有没有
            String token = (String) request.getServletContext().getAttribute("access_token");
            String expiresString = (String) request.getServletContext().getAttribute("expires_time");
            LocalDateTime nowTime = LocalDateTime.now();
            LocalDateTime expiresTime;

            //如果全局变量里没有的话,要从新取

            if (Utilities.checkNull(token).isEmpty()) {
                result = getNewWxToken(result, request);
            } else {
                //如果全局变量里面有的话,要判断过期了没
                expiresTime = LocalDateTime.parse(expiresString, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                if (nowTime.isBefore(expiresTime)) {
                    JSONObject resultJO = new JSONObject();
                    resultJO.put("access_token", token);
                    resultJO.put("expires_time", expiresString);
                    result.setData(resultJO);
                    result.setMessage("请求成功,直接从全局变量里取的值,没有请求微信API");
                } else {
                    result = getNewWxToken(result, request,type);
                }
            }
        } catch (Exception e) {
            result.setMessage(e.getMessage());
            result.setStatus(false);
        }
        return result;
    }

    private static Result getNewWxToken(Result result, HttpServletRequest request) {
        JSONObject jo = getWxTokenApi();
        String token = jo.getString("access_token");
        //获取成功了
        if (!Utilities.checkNull(token).isEmpty()) {
            long expiresIn = jo.getLong("expires_in");
            LocalDateTime expiresTime = LocalDateTime.now().plusSeconds(expiresIn - 60);
            String expiresTimeString = expiresTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            request.getServletContext().setAttribute("access_token"+type, token);
            request.getServletContext().setAttribute("expires_time"+type, expiresTimeString);
            JSONObject resultJO = new JSONObject();
            resultJO.put("access_token", token);
            resultJO.put("expires_time", expiresTimeString);
            result.setData(resultJO);
            result.setMessage("请求成功,从微信API新请求了一个access_token");
        } else {
            //获取没成功
            String errmsg = jo.getString("errmsg");
            //看上一步的网络请求成功了没
            if (!Utilities.checkNull(errmsg).isEmpty()) {
                result.setStatus(false);
                result.setMessage("errcode:" + jo.get("errcode") + "    errmsg:" + errmsg);
            } else {
                result.setStatus(false);
                result.setMessage("getWxTokenApi()接口返回数据为空,可能是向微信请求token时网络超时或请求次数达到上限");
            }
        }
        return result;
    }

    private static JSONObject getWxTokenApi() {
        JSONObject jo = new JSONObject();
        String appID = "";
        String appSecret ="";
        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appID + "&secret=" + appSecret;
        Document document= null;
        try {
            document = Jsoup.connect(url).ignoreContentType(true).get();
            String data=document.select("body").text();
            if (!Utilities.checkNull(data).isEmpty()) {
                jo = JSON.parseObject(data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return jo;
    }

 二、获取用户openid

该过程分为三种情况(微信客户端网页授权,PC端微信登陆,微信小程序授权),每种情况都是两步,首先获取code,其次获取openid

微信客户端网页授权

2.1 获取code(微信公众平台配置授权回调域,我这里配置的是http://wx.pinche.com/login?mode=wx,这里的scope值为snsapi_base和snsapi_userinfo)

用户访问链接:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=http%3A%2F%2Fwx.pinche.com%2Flogin?mode=wx&response_type=code&scope=snsapi_base&state=dac24d03f848ce899f28ad787eba74e2&connect_redirect=1#wechat_redirect

2.2 获取openid

后端代码发送get请求

https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

获取openid

      PC端微信授权登陆(注意,这里的appid来自于微信开放平台)

      2.1 获取code(配置域名回调域,我这里配置的是http://wx.pinche.com/login?mode=wx,这里的scope值为snsapi_base和snsapi_userinfo)

      用户访问链接:

      https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=http%3A%2F%2Fwx.pinche.com%2Flogin?                               mode=wx&response_type=code&scope=snsapi_login&state=dac24d03f848ce899f28ad787eba74e2&connect_redirect=1#wechat_redirect

      2.2 获取openid

       同上

微信小程序授权

2.1 获取code(配置域名回调域,我这里配置的是http://wx.pinche.com/login?mode=wx)

小程序调用方法

wx.login()获取code,发送后端

2.2 获取openid

后端代码发送get请求

https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=APPSECRET&js_code=CODE&grant_type=authorization_code

获取openid

三、根据openid获取用户基本信息,如果用户已经关注公众号,则返回数据包含用户unionid(这个很简单,不赘述了)

https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN

四、获取已关注公众号列表,默认一次最多返回1000条数据(第一次不需要next_openid参数),根据NEXT_OPENID可进行翻页

https://api.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&next_openid=NEXT_OPENID

五、生成带参数微信二维码(生成临时二维码时,有没有自定义参数sence_str无所谓,生成永久二维码时这个参数不能为空)

public  Result getNewWxQRTicket(HttpServletRequest request, int fixed,String code){

        Result finalResult=new Result();
//获取access_token Result result
= WxQRUtil.getAccessToken(request); JSONObject jo= (JSONObject) result.getData(); String access_token= null; try { access_token = jo.getString("access_token"); } catch (Exception e) { e.printStackTrace(); } try { //发送post请求获取二维码 String qrCode_url="https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token="+access_token; JSONObject postDataJson=new JSONObject(); JSONObject sceneJson=new JSONObject(); JSONObject sceneDataJson=new JSONObject(); //临时ticket有效期,单位为秒,最多为2592000s(即30天),当前默认为7天 postDataJson.put("expire_seconds",day*3600*24); //临时二维码 QR_SCENE (对应scene_id),QR_STR_SCENE(对应scene_str) //永久二维码 QR_LIMIT_SCENE (对应scene_id),QR_LIMIT_STR_SCENE(对应scene_str) String action_name="QR_STR_SCENE"; if(fixed==1){ //永久二维码 action_name="QR_LIMIT_STR_SCENE"; } postDataJson.put("action_name",action_name); //业务场景下属性id sceneDataJson.put("scene_str",code); sceneJson.put("scene",sceneDataJson); postDataJson.put("action_info",sceneJson); String postData=postDataJson.toJSONString(); HttpClient httpClient = new DefaultHttpClient(); HttpPost post = new HttpPost(qrCode_url); StringEntity postingString = new StringEntity(postData,"utf-8");// json传递 post.setEntity(postingString); HttpResponse response = httpClient.execute(post); String get_content = EntityUtils.toString(response.getEntity()); JSONObject resultData= JSONObject.parseObject(get_content); String ticket=resultData.getString("ticket"); String final_code_url="https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket="+ticket; finalResult.setData(final_code_url); } catch (Exception e) { finalResult.setStatus(false); finalResult.setMessage(e.getMessage()); e.printStackTrace(); } return finalResult; }

 六、微信模板消息推送(注:模板消息换行使用"\\n"

、获取用户基本信息的几种方式 

移动端网页授权链接,官方文档(https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419317851&token=&lang=zh_CN

https://open.weixin.qq.com/connect/oauth2/authorize?appid=&redirect_uri=http%3A%2F%2F" + redirect_uri + "%2Fapi%2Fitem%2Fuser%2Fwx_info?active_index=1" + para + "&response_type=code&scope=snsapi_userinfo&state=dac24d03f848ce899f28ad787eba74e2&connect_redirect=1#wechat_redirect

redirect_uri即授权回调域名,如www.baidu.com

para参数格式为(%26mode=wx%26key=value,即把'&'替换为'%26'),用于授权回调后携带多个参数

 

7.1 只获取用户openid (可采用网页静默授权,即scope=snsapi_base)

7.2 获取用户个人基本信息有两种方式

    7.2.1 常规方式:同7.1相近,只需把scope改为 snsapi_userinfo,然后用户允许授权后即可通过code获取到用户信息

    7.2.2 已关注公众号用户获取个人信息:这个就比较简单,可以无视用户是否允许授权,先通过7.1获取用户openid,再获取公众号access_token(非7.2.1,通过code获取access_token),及可以通过下面链接直接获取

      https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN

 

PC端生成二维码,用户扫码授权登陆,官方文档(https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=&lang=zh_CN

授权链接

https://open.weixin.qq.com/connect/qrconnect?appid=&redirect_uri=http%3A%2F%2F" + redirect_uri + "%2Fapi%2Fitem%2Fuser%2Fwx_info?active_index=1&response_type=code&scope=snsapi_login&state=dac24d03f848ce899f28ad787eba74e2#wechat_redirect 

redirect_uri即授权回调域名,如www.baidu.com

剩下操作同上

 

 

 

 

 

qrconnect
posted @ 2018-06-07 11:33  流年飞逝  阅读(394)  评论(0)    收藏  举报