欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

手写一个生成带参数 微信二维码的工具类

程序员文章站 2022-04-23 23:40:14
...

1.先定义接口

package com.example.mongodemo.framework.wechat;

import com.example.mongodemo.framework.wechat.entity.AccessToken;
import com.example.mongodemo.framework.wechat.entity.ImageTicket;
import com.example.mongodemo.framework.wechat.entity.LoginUser;
import org.checkerframework.checker.signature.qual.FieldDescriptorForPrimitive;

/**
 * @Author lyr
 * @create 2020/4/19 15:54
 */
public interface WechatTemplate {
    /**
     * 获取token的 url,没什么用
     * @return 生成获取 token的 url
     */
    @Deprecated
    String generateTokenUrl();

    /**
     * 请求微信接口,获取 accessToken
     * @return 返回微信的 ACCESS_TOKEN
     */
    AccessToken getAccessTokenObject();

    /**
     * 查看用户的登录状态
     * @param sceneId 场景id
     * @return 返回登录状态的包装类
     */
    LoginUser isUserLogin(String sceneId);

    /**
     * 查看用户是否登录,后台确认登录了,返回true,否则返回 false
     * @param sceneId 用户场景id
     * @return
     */
    boolean userIsLogin(String sceneId);

    /**
     * 设置用户已经登录
     * @param sceneId 用户场景id => 表示唯一的用户
     */
    void setUserIsLogin(String sceneId);

    /**
     * 获取扫描二维码的图片门票
     * @return 获取扫码二维码的图片门票
     */
    ImageTicket getImageTicket();

    /**
     * 生成访问二维码的 url
     * @param ticket 门票对象
     * @return
     */
    String generateQrCodeUrl(ImageTicket ticket);
}

下面是 AccesToken 的 pojo 等等

package com.example.mongodemo.framework.wechat.entity;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

/**
 * @Author lyr
 * @create 2020/4/19 13:50
 */
@Data
public class AccessToken {
    /**
     * redis前缀
     */
    public static final String REDIS_KEY = "ac_tk::actk";

    @JsonProperty("access_token")
    private String accessTokenContent;
    @JsonProperty("expires_in")
    private Long expiresIn;
}

// 生成二维码的门票

package com.example.mongodemo.framework.wechat.entity;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.experimental.Accessors;

/**
 * @Author lyr
 * @create 2020/4/19 16:00
 * 获取微信二维码的门票
 *
 */
@Data
@Accessors(chain = true)
public class ImageTicket {

    /**
     * 获取微信二维码门票的链接
     */
    public static final String
    GET_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/qrcode/create?" +
            "access_token=" +
            "%s";



    /**
     * 获取微信二维码的门票
     */
    @JsonProperty("ticket")
    private String ticket;

    /**
     * 过期时间
     */
    @JsonProperty("expires_seconds")
    private Integer expireSeconds;
    /**
     * 返回给前端,让前端用这个 sceneId 轮询后端接口,确认是否用户已经登录
     */
    private String sceneId;

}

用户登录状态实体(这里其实不用看的,你要什么业务,定义什么接口)

package com.example.mongodemo.framework.wechat.entity;

import com.example.mongodemo.pojo.WeixinUserInfo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.experimental.Accessors;

/**
 * @Author lyr
 * @create 2020/4/19 15:29
 */
@Data
@Accessors(chain = true)
@AllArgsConstructor
public class LoginUser {
    public static final String REDIS_KEY=
            "loginuser::";

    private String sceneId;


    /**
     * 登录状态
     */
    private Boolean loginStatus = false;

    public LoginUser(String sceneId) {
        this.sceneId = sceneId;
    }
}

请求接口,获取门票的配置

package com.example.mongodemo.framework.wechat.entity;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.experimental.Accessors;

/**
 * @Author lyr
 * @create 2020/4/19 16:20
 */
@Data
@Accessors(chain = true)
public class TicketRequest {

    public static final
    String REQUEST_TICKET_URL
            ="https://api.weixin.qq.com/cgi-bin/qrcode/create?" +
            "access_token=" +
            "%s"
    ;
    public static final String REQUEST_TEMPLATE
            ="{\n" +
            "    \"expire_seconds\": 1800,\n" +
            "    \"action_name\": \"QR_SCENE\",\n" +
            "    \"action_info\": {\n" +
            "        \"scene\": {\n" +
            "            \"scene_str\": \"%s\"\n" +
            "        }\n" +
            "    }\n" +
            "}";



}

wechatTemplate 的实现

package com.example.mongodemo.framework.wechat;

import com.example.mongodemo.framework.wechat.entity.AccessToken;
import com.example.mongodemo.framework.wechat.entity.ImageTicket;
import com.example.mongodemo.framework.wechat.entity.LoginUser;
import com.example.mongodemo.framework.wechat.entity.TicketRequest;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;


import javax.annotation.Resource;
import java.time.Duration;

/**
 * @Author lyr
 * @create 2020/4/19 11:31
 */
@Component
@Slf4j
public class RestWechatTemplate implements WechatTemplate{
    /**
     * 获取 调用接口权限 的 ac token
     */
    @Value("${wechatTemplate.getTicket.appid}")
    private String appId;
    /**
     * **
     */
    @Value("${wechatTemplate.getTicket.secret}")
    private String secret;
    @Value("${wechatTemplate.getTicket.grant_type}")
    private String grantType;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    /**
     * 远程调用 微信接口
     */
    @Resource
    private RestTemplate restTemplate;

    /**
     * @return 生成请求微信接口的 url => 请求 微信token的 url
     */
    @Override
    public String generateTokenUrl() {
        String getBasicAccessToken = "https://api.weixin.qq.com/cgi-bin/" +
                "token?" +
                "grant_type=%s" +
                "&" +
                "appid=%s" +
                "&" +
                "secret=%s";
        return String.format(getBasicAccessToken,grantType,appId,secret);

        // //log.info("微信返回结果 {}",responseString);
        // return responseString;
    }





    /**
     * 公众号每日 10000 次调用机会
     * @return 返回 AccessToken
     */
    @Deprecated
    public String getAccessToken() {
        String responseString
                = restTemplate
                .getForObject(generateTokenUrl(),String.class);
        return responseString;
    }

    /**
     * @return 请求微信接口,获得 AccessToken =》将 json传转对象
     */
    @Override
    public AccessToken getAccessTokenObject() {
        AccessToken token = (AccessToken) redisTemplate.opsForValue()
                .get(AccessToken.REDIS_KEY);
        if(token==null) {
            //请求微信
            token = restTemplate.getForObject(generateTokenUrl(),AccessToken.class);

            if(token!=null) {
                //缓存 token 至有效时间
                redisTemplate.opsForValue()
                        .set(AccessToken.REDIS_KEY,token, Duration.ofSeconds(token.getExpiresIn()-10));
            }

        }
        return token;
    }


    /**
     * 用户是否已经登录
     * @param sceneId 场景id
     * @return
     */
    @Override
    public LoginUser isUserLogin(String sceneId) {
        LoginUser loginUser = (LoginUser) redisTemplate
                .opsForValue()
                .get(LoginUser.REDIS_KEY);
        return loginUser==null ? new LoginUser(sceneId):loginUser;
    }

    /**
     * 用户是否已经登录
     * @param sceneId 场景id
     * @return
     */
    @Override
    public boolean userIsLogin(String sceneId) {
        return isUserLogin(sceneId).getLoginStatus();
    }

    @Override
    public void setUserIsLogin(String sceneId) {
        redisTemplate.opsForValue()
                .set(LoginUser.REDIS_KEY+sceneId,new LoginUser(sceneId).setLoginStatus(true));
    }

    @Override
    public ImageTicket getImageTicket() {
        String getTicketUrl = String
                .format(ImageTicket.GET_TICKET_URL,
                        getAccessTokenObject()
                                .getAccessTokenContent());
        String sceneId
                = String.valueOf(System.nanoTime());
        ResponseEntity<ImageTicket> responseEntity = restTemplate
                .postForEntity(
                        //url
                        getTicketUrl,
                        //请求参数
                        String.format(TicketRequest.REQUEST_TEMPLATE, sceneId),
                        ImageTicket.class
                );

        ImageTicket ticket = responseEntity.getBody();

        return ticket!=null?ticket.setSceneId(sceneId):null;
    }
    @Override
    public String generateQrCodeUrl(ImageTicket ticket) {
        String qrCodeUrl = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=%s";
        return String.format(qrCodeUrl, ticket.getTicket());
    }







}

相关标签: CRUD