微信公衆號java後端開發記錄(二):後端配置及請求第三方封裝

·後端配置詳解

微信公衆平臺文檔地址:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277

注:本博文使用的的框架結構是 SpringCloud+Springboot+Mybatis-Plus

1.yml常量配置

#公衆號
wxpublic:
  appid: x  #公衆號appid
  secret: x  #公衆號secret
  template_id:
    remind: x  #公衆號模板一id(名字見名知意即可)
    reply: x   #公衆號模板二id
  #rediskey名字
  rediskey:
      access_token: WechatPublic:access_token  #access_token 存儲在redis的key名字

2.1  獲取access_token

若是不同業務場景每次拿accesstoken都去請求微信,重複獲取將導致上次獲取的access_token失效,需定時刷新,保證一致性。access_token默認有7200秒(倆小時)過期時間,所以採取redis將其緩存起來,並設置少於7200秒的時間。每次取用access_token之前都先從redis中獲取,若已過期再重新獲取。

//============公衆號配置參數
@Resource
private RedisTemplate<String, Object> stringRedisTemplate;
@Value("${wxpublic.appid}")
private String wxpublicAppId;

@Value("${wxpublic.secret}")
private String wxpublicAppSecret;

@Value("${wxpublic.rediskey.access_token}")
private String wxpublicAccessTokenRediskey;

//通知模板id
@Value("${wxpublic.template_id.remind}")
private String wxpublicRemindTemplateId;

//回覆模板id
@Value("${wxpublic.template_id.reply}")
private String wxpublicReplyTemplateId;

public ResultVO<?> getWxPublicAccessToken() {
        //redis中獲取access_token
        String tokenRedis = (String) stringRedisTemplate.opsForValue().get(wxpublicAccessTokenRediskey);
        if (tokenRedis == null) {
            //若沒有(從未,或者已過期),請求微信獲取
            //請求微信
            JSONObject myAccessToken = WechatApiUtil.getMyAccessToken(wxpublicAppId, wxpublicAppSecret);
            if (myAccessToken.containsKey("access_token")) {
                String newAccessToken = myAccessToken.getString("access_token");
                stringRedisTemplate.opsForValue().set(wxpublicAccessTokenRediskey, newAccessToken, 7000, TimeUnit.SECONDS);
                log.info("公衆號請求wechat request獲取access_token");
                //請求成功將記錄保存到數據庫中
                WxApplet entity = new WxApplet();
                entity.setNo(CodeNoEnum.WX_APPLET.getTableNO() + CommentUtil.createNo());
                entity.setCategory(WeChatRequestTypeEnum.GET_WX_PUBLIC_ACCESS_TOKEN.getCode());
                entity.setAccessToken(newAccessToken);
                entity.setReturnResult(myAccessToken.toJSONString());
                wxAppletMapper.insert(entity);
                return ResultVOUtil.returnSuccess(newAccessToken);
            } else {
                //請求有誤,返回錯誤信息
                //請求有無也將記錄保存到數據庫中
                log.info("獲取access_token失敗");
                WxApplet entity = new WxApplet();
                entity.setNo(CodeNoEnum.WX_APPLET.getTableNO() + CommentUtil.createNo());
                entity.setCategory(WeChatRequestTypeEnum.GET_WX_PUBLIC_ACCESS_TOKEN.getCode());
                entity.setReturnResult(myAccessToken.toJSONString());
                wxAppletMapper.insert(entity);
                return ResultVOUtil.returnFail(myAccessToken.getString("errmsg"));
            }
        } else {
            return ResultVOUtil.returnSuccess(tokenRedis);
        }
    }

封裝的請求微信方法(使用hutool工具包):

工具類方法:

//獲取AccessToken
   public static JSONObject getMyAccessToken(String appId, String appSecret) {
       String apiUrl = StrUtil.format(
               "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={}&secret={}",
               appId, appSecret
       );
       String body = HttpRequest.get(apiUrl).execute().body();
       JSONObject jsonObject = myThrowErrorMessageIfExists(body);
       return jsonObject;
   }

2.2根據網頁授權code 換取公衆號用戶openid及相關信息

微信公衆號消息推送以及涉及到用戶的操作均需要用戶openid,需要請求微信獲取。

爲保持session_key的。最新時效性,也將獲取到的用戶openid存入redis,但是每次取用之前進行判斷,若不存在就存入redis,若存在,先刪除然後重新獲取對其更新。

public ResultVO<?> wechatPbulicGetOpenIdByCode(WxAppletDTO dto) {
        //請求微信獲得openid和sessionkey
        JSONObject myOpenIdByCode = WechatApiUtil.wechatPbulicGetOpenIdByCode(wxpublicAppId, wxpublicAppSecret, dto.getCode());
        if (myOpenIdByCode.containsKey("errcode")) {
            //若請求微信失敗(無效code情況)
            //失敗也要每次請求記錄到數據庫中
            WxApplet entity = new WxApplet();
            entity.setNo(CodeNoEnum.WX_APPLET.getTableNO() + CommentUtil.createNo());
            entity.setWechatCode(dto.getCode());
            entity.setCategory(WeChatRequestTypeEnum.GET_WX_OPENID.getCode());
            entity.setReturnResult(myOpenIdByCode.toJSONString());
            wxAppletMapper.insert(entity);
            return ResultVOUtil.returnFail(-1, "fail", myOpenIdByCode);
        }
        //正常請求到了微信
        //用戶唯一標識
        String openid = myOpenIdByCode.getString("openid");
        //臨時會話祕鑰
        String access_token = myOpenIdByCode.getString("access_token");

        //每次成功請求記錄到數據庫中
        WxApplet entity = new WxApplet();
        entity.setNo(CodeNoEnum.WX_APPLET.getTableNO() + CommentUtil.createNo());
        entity.setWechatCode(dto.getCode());
        entity.setCategory(WeChatRequestTypeEnum.GET_WX_OPENID.getCode());
        entity.setOpenid(openid);
        entity.setAccessToken(access_token);
        entity.setReturnResult(myOpenIdByCode.toJSONString());
        wxAppletMapper.insert(entity);

        //根據openid爲key查詢skey_redis是否存在
        String skey_redis = (String) stringRedisTemplate.opsForValue().get(openid);
        if (!StringUtils.isEmpty(skey_redis)) {
            //存在,刪除此數據,重新獲取並返回(保持session_key的最新時效性)
            stringRedisTemplate.delete(skey_redis);
        }
        //緩存一份新的
        //uuid生成唯一key
        String skey = UUID.randomUUID().toString();
        JSONObject sessionObj = new JSONObject();
        sessionObj.put("openId", openid);
        sessionObj.put("access_token", access_token);
        //[更新]以openid爲key,唯一sky爲value,存redis
        stringRedisTemplate.opsForValue().set(openid, skey);
        //重新生成一份帶有openid和session_key的數據
        stringRedisTemplate.opsForValue().set(skey, sessionObj.toJSONString());
        return ResultVOUtil.returnSuccess(myOpenIdByCode);
    }

工具類方法:

//微信公衆號通過網頁授權code獲取openid和session_key
    public static JSONObject wechatPbulicGetOpenIdByCode(String appId, String appSecrct, String code) {
        String apiUrl = StrUtil.format(
                " https://api.weixin.qq.com/sns/oauth2/access_token?appid={}&secret={}&code={}&grant_type=authorization_code",
                appId, appSecrct, code
        );
        String body = HttpRequest.get(apiUrl).execute().body();
        return throwErrorMessageIfExists(body);
    }

工具類打印請求日誌方法:

//微信請求異常處理
    public static JSONObject throwErrorMessageIfExists(String body) {
        String callMethodName = (new Throwable()).getStackTrace()[1].getMethodName();
        log.info("#請求微信方法名:{},body={}", callMethodName, body);
        JSONObject jsonObject = JSON.parseObject(body);
        return jsonObject;
    }

附:通用vo類

/*
* @JsonInclude(JsonInclude.Include.NON_NULL)標記是jackson包提供的json序列化方法,
* 已經集成於Springboot2.0中,此方法的配置意在可以對實體json序列化的時候進行對應的數值處理,
//將該標記放在屬性上,如果該屬性爲NULL則不參與序列化 
//如果放在類上邊,那對這個類的全部屬性起作用 
//Include.Include.ALWAYS 默認 
//Include.NON_DEFAULT 屬性爲默認值不序列化 
//Include.NON_EMPTY 屬性爲 空(“”) 或者爲 NULL 都不序列化 
//Include.NON_NULL 屬性爲NULL 不序列化 
* */
@Data //lombook
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResultVO<T> implements Serializable {
    private static final long serialVersionUID = -3032060746893382446L;
    // 錯誤碼
    private Integer code;
    // 提示信息
    private String msg;
    // 具體內容
    private T data;
}

通用成功/失敗返回類:

public class ResultVOUtil {

    public static ResultVO<?> returnSuccess(Object object) {
        ResultVO<Object> resultVO = new ResultVO<Object>();
        resultVO.setCode(0);
        resultVO.setMsg("success");
        resultVO.setData(object);
        return resultVO;
    }

    public static ResultVO<?> returnSuccess(String key, Object object) {
        ResultVO resultVO = new ResultVO();
        resultVO.setCode(0);
        resultVO.setMsg("success");
        Map<String, Object> map = new HashMap<>();
        map.put(key, object);
        resultVO.setData(map);
        return resultVO;
    }

    public static ResultVO<?> returnSuccess() {
        return returnSuccess(null);
    }

    public static ResultVO<?> returnFail(Integer code, String msg) {
        ResultVO resultVO = new ResultVO();
        resultVO.setCode(code);
        resultVO.setMsg(msg);
        return resultVO;
    }
    public static ResultVO<?> returnFail(Integer code, String msg,Object object) {
        ResultVO resultVO = new ResultVO();
        resultVO.setCode(code);
        resultVO.setMsg(msg);
        resultVO.setData(object);
        return resultVO;
    }

    public static ResultVO<?> returnFail(String msg) {
        ResultVO resultVO = new ResultVO();
        resultVO.setCode(ResultEnum.FAIL.getCode());
        resultVO.setMsg(msg);
        return resultVO;
    }

    public static ResultVO<?> returnFail() {
        return returnFail(ResultEnum.FAIL);
    }

    public static ResultVO<?> returnFail(ResultEnum resultEnum) {
        ResultVO resultVO = new ResultVO();
        resultVO.setCode(resultEnum.getCode());
        resultVO.setMsg(resultEnum.getMessage());
        return resultVO;
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章