微信公众号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;
    }
}

 

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