【聚水潭平台对接】

1. 简介

聚水潭是什么?首先来看下聚水潭的官方介绍:
聚水潭成立于2014年,创始人兼CEO骆海东拥有近三十年传统及电商ERP的研发和实施部署经验。聚水潭创建之初,以电商SaaS ERP切入市场,凭借出色的产品和服务,快速获得市场的肯定。随着客户需求的不断变化,如今聚水潭已经发展成为以SaaS ERP为核心,集多种商家服务为一体的SaaS协同平台。
em…就是提供ERP服务的SaaS平台。
提供的核心功能:
在这里插入图片描述
但是在我的实际使用过程中,更为看重的是聚水潭的电商ERP,能够对接抖音和淘宝店铺中的数据。这也是我接触聚水潭的初衷。
随便提一下:聚水潭拥有自己的开放平台,而且在2021年年底的时候进行了一次大的更新,使用起来更加的方便一些。
聚水潭官网:https://www.jushuitan.com
聚水潭开放平台:https://openweb.jushuitan.com

下面介绍一下聚水潭的对接流程以及自己在对接过程中使用的工具类。

2. 平台入驻

平台入驻的目的是为了新建应用,获取应用对应的APP Key和Secret。拥有了APP Key和Secret后续的接口调用才能顺利进行。随便说一下,聚水潭提供了沙箱的appkey和secret,可以直接进行接口调用,方便开发者的开发。
在这里插入图片描述
入驻流程的资质只需要提供公司的营业执照和企业法人的证件信息就可以了。然后等待平台审核,审核通过就可以创建应用。

3. 第三方授权

官方概述:
在这里插入图片描述
授权流程:
在这里插入图片描述
第三方授权的作用或者说目的就是获取一个access_token。access_token的作用是在调用相关API接口的时候使用的。所以在调用平台的接口之前我们需要先获取到access_token。
而获取access_token的流程(授权流程)也在上面说的很清楚了,我们也就是开发者需要先提供一个授权的URL给商家,商家通过URL所展示的页面进行登录授权,授权之后聚水潭会通过回调接口来返回code,然后开发者再通过code来换取access_token。
回调接口地址是在聚水潭平台的应用管理中进行配置的。
授权的URL拼装:
业务授权URL: https://openweb.jushuitan.com/auth
在URL后加上下表参数并且计算签名:

参数类型名称示例值是否必填
app_keystring开发者应用Key0ecde8631431a5ed6b3e7368afbabdadss必填
timestampstring当前请求的时间戳【单位是秒】1577771730必填
statestringISV自定义字段,授权完成时,此值会返回给ISVABC非必填
charsetstring交互数据的编码【utf-8】目前只能传utf-8,不能不传!utf-8必填
signstring请求的数字签名,是通过所有请求参数通过摘要生成的,保证请求参数没有被篡改。0ecde8631431a5ed6b3e7368afbabdaoas必填

业务授权URL拼装示例:https://openweb.jushuitan.com/auth?app_key=0ecde8631431a5ed6b3e7368afbabdadss&timestamp=1577772770&charset=UTF-8&sign=acce9f196f609952b3194bd4e164c340247c39ee&state=test

注意:授权URL有效时间为15分钟,生成链接之后,请尽快让商家登录授权。

获取授权URL的代码示例:

  1. 聚水潭配置文件的配置
################## 聚水潭 ##################
#serverUrl正式环境需要设置为 https://openapi.jushuitan.com
#serverUrl测试环境为        https://dev-api.jushuitan.com
jushuitan.serverUrl=https://dev-api.jushuitan.com
jushuitan.appKey=b0b7d1db226d4216a3d58df9ffa2dde5
jushuitan.appSecret=99c4cef262f34ca882975a7064de0b87
#聚水潭开放平台地址
jushuitan.openWebUrl=https://openweb.jushuitan.com
#聚水潭回调验证是否开启
jushuitan.callBack.signature=true
  1. 配置文件对应的bean
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * @description: 聚水潭配置
 * @author: WangYX
 * @create: 2021-12-31 10:55
 * @Version: 1.0.0
 **/
@Component
public class JuShuiTanProperties {

    /**
     * 请求地址
     */
    @Value("${jushuitan.serverUrl}")
    private String serverUrl;

    /**
     * 开放平台地址
     */
    @Value("${jushuitan.openWebUrl}")
    private String openWebUrl;

    /**
     * 应用的appKey
     */
    @Value("${jushuitan.appKey}")
    private String appKey;

    /**
     * 应用的appSecret
     */
    @Value("${jushuitan.appSecret}")
    private String appSecret;

    /**
     * 回调验证是否开启
     */
    @Value("${jushuitan.callBack.signature}")
    private Boolean signature;


    public String getServerUrl() {
        return serverUrl;
    }

    public void setServerUrl(String serverUrl) {
        this.serverUrl = serverUrl;
    }

    public String getOpenWebUrl() {
        return openWebUrl;
    }

    public void setOpenWebUrl(String openWebUrl) {
        this.openWebUrl = openWebUrl;
    }

    public String getAppKey() {
        return appKey;
    }

    public void setAppKey(String appKey) {
        this.appKey = appKey;
    }

    public String getAppSecret() {
        return appSecret;
    }

    public void setAppSecret(String appSecret) {
        this.appSecret = appSecret;
    }

    public Boolean getSignature() {
        return signature;
    }

    public void setSignature(Boolean signature) {
        this.signature = signature;
    }
}

  1. 聚水潭的工具类
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sendbox.repository.domain.AccessTokenDO;
import com.sendbox.serviceapi.config.JuShuiTanProperties;
import com.sendbox.serviceapi.util.OkHttp3Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @description: 聚水潭工具类
 * @author: WangYX
 * @create: 2021-12-30 17:11
 * @Version: 1.0.0
 **/
@Component
public class JuShuiTanUtil {

    public static final String SIGN_METHOD_MD5 = "md5";
    public static final String CHARSET_UTF8 = "utf-8";
    public static final String CONTENT_ENCODING_GZIP = "gzip";

    /**
     * 请求地址
     */
    public static String serverUrl;

    /**
     * 开放平台地址
     */
    public static String openWebUrl;

    /**
     * 应用的appKey
     */
    public static String appKey;

    /**
     * 应用的appSecret
     */
    public static String appSecret;

    @Autowired
    private JuShuiTanProperties juShuiTanProperties;

    @PostConstruct
    public void init() {
        serverUrl = juShuiTanProperties.getServerUrl();
        openWebUrl = juShuiTanProperties.getOpenWebUrl();
        appKey = juShuiTanProperties.getAppKey();
        appSecret = juShuiTanProperties.getAppSecret();
    }

    /**
     * 构建表单请求的body
     *
     * @param params
     * @param charset
     * @return
     * @throws IOException
     */
    public static String buildQuery(Map<String, String> params, String charset) throws IOException {
        if (params == null || params.isEmpty()) {
            return null;
        }

        StringBuilder query = new StringBuilder();
        Set<Map.Entry<String, String>> entries = params.entrySet();
        boolean hasParam = false;

        for (Map.Entry<String, String> entry : entries) {
            String name = entry.getKey();
            String value = entry.getValue();
            // 忽略参数名或参数值为空的参数
            if (isNotEmpty(name) && isNotEmpty(value)) {
                if (hasParam) {
                    query.append("&");
                } else {
                    hasParam = true;
                }
                query.append(name).append("=").append(URLEncoder.encode(value, charset));
            }
        }

        return query.toString();
    }


    /**
     * 对TOP请求进行签名。
     */
    public static String signTopRequest(Map<String, String> params, String secret, String signMethod) throws IOException {
        // 第一步:检查参数是否已经排序
        String[] keys = params.keySet().toArray(new String[0]);
        Arrays.sort(keys);

        // 第二步:把所有参数名和参数值串在一起
        StringBuilder query = new StringBuilder();
        if (SIGN_METHOD_MD5.equals(signMethod)) {
            query.append(secret);
        }
        for (String key : keys) {
            String value = params.get(key);
            if (isNotEmpty(key) && isNotEmpty(value)) {
                query.append(key).append(value);
            }
        }
        return createSign(query.toString());
    }

    /**
     * 生成新sign
     *
     * @param str 字符串
     * @return String
     */
    public static String createSign(String str) {
        if (str == null || str.length() == 0) {
            return null;
        }
        char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
        try {
            MessageDigest mdTemp = MessageDigest.getInstance(SIGN_METHOD_MD5);
            mdTemp.update(str.getBytes("UTF-8"));

            byte[] md = mdTemp.digest();
            int j = md.length;
            char[] buf = new char[j * 2];
            int k = 0;
            int i = 0;
            while (i < j) {
                byte byte0 = md[i];
                buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
                buf[k++] = hexDigits[byte0 & 0xf];
                i++;
            }
            return new String(buf);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 店铺授权聚水潭的URL
     *
     * @return
     */
    public static String spliceAuthorizationUrl(String state) throws IOException {
        Map<String, String> params = new HashMap<String, String>();
        // 公共参数
        params.put("app_key", appKey);
        params.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
        params.put("state", state);
        params.put("charset", "utf-8");
        // 签名参数
        params.put("sign", signTopRequest(params, appSecret, SIGN_METHOD_MD5));
        String url = buildQuery(params, "utf-8");
        url = openWebUrl + "/auth?" + url;
        return url;
    }

    /**
     * Function:验证签名
     *
     * @author Wangyx
     * @date 2021/12/31 12:28
     * @version 1.0.0
     */
    public static Boolean signatureVerification(Map<String, String> params, String sign) throws IOException {
        String signTopRequest = signTopRequest(params, appSecret, SIGN_METHOD_MD5);
        return sign.equals(signTopRequest);
    }

    /**
     * Function:获取accessToken
     *
     * @author Wangyx
     * @date 2021/12/31 14:23
     * @version 1.0.0
     */
    public static String getAccessToken(String code) throws IOException {
        Map<String, String> params = new HashMap<>();
        params.put("app_key", appKey);
        params.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
        params.put("grant_type", "authorization_code");
        params.put("charset", "utf-8");
        params.put("code", code);
        params.put("sign", signTopRequest(params, appSecret, SIGN_METHOD_MD5));

        //获取accessToken的地址
        String url = serverUrl + "/openWeb/auth/accessToken";
        return OkHttp3Util.sendByPostMap(url, params);
    }

    /**
     * Function:刷新AccessToken
     *
     * @author Wangyx
     * @date 2022/01/04 9:18
     * @version 1.0.0
     */
    public static String refreshToken(String refreshToken) throws IOException {
        Map<String, String> params = new HashMap<>();
        params.put("app_key", appKey);
        params.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
        params.put("grant_type", "refresh_token");
        params.put("charset", "utf-8");
        params.put("refresh_token", refreshToken);
        params.put("scope", "all");
        params.put("sign", signTopRequest(params, appSecret, SIGN_METHOD_MD5));

        //获取accessToken的地址
        String url = openWebUrl + "/openWeb/auth/refreshToken";
        return OkHttp3Util.sendByPostMap(url, params);
    }


    public static boolean isNotEmpty(String value) {
        int strLen;
        if (value == null || (strLen = value.length()) == 0) {
            return false;
        }
        for (int i = 0; i < strLen; i++) {
            if ((Character.isWhitespace(value.charAt(i)) == false)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 发送请求
     *
     * @param accessToken
     * @param biz
     * @param requestUrl
     * @return
     * @throws IOException
     */
    public static String sendRequest(String accessToken, String biz, String requestUrl) throws IOException {
        // 公共请求参数
        Map<String, String> params = new HashMap<String, String>();
        params.put("app_key", appKey);
        params.put("access_token", accessToken);
        params.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
        params.put("version", "2");
        params.put("charset", "utf-8");
        // 业务参数
        params.put("biz", biz);
        // 签名参数
        params.put("sign", signTopRequest(params, appSecret, SIGN_METHOD_MD5));
        String url = serverUrl + requestUrl;
        return OkHttp3Util.sendByPostMap(url, params);
    }
}
  1. access_token对应的POJO

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.util.Date;
import java.io.Serializable;

/**
 * <p>
 * access_token
 * </p>
 *
 * @author wyx
 * @since 2021-12-31
 */
@Data
@TableName("tbl_access_token")
public class AccessTokenDO implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 店铺id
     */
    @TableId(value = "id", type = IdType.INPUT)
    private String id;

    /**
     * 标志
     */
    @TableField("mark")
    private String mark;


    /**
     * access_token有效期【单位是秒】 2592000  30天
     */
    @TableField("expires_in")
    private Long expiresIn;
    /**
     * 访问令牌
     */
    @TableField("access_token")
    private String accessToken;
    /**
     * 固定值:all
     */
    private String scope;
    /**
     * 更新令牌
     */
    @TableField("refresh_token")
    private String refreshToken;
    /**
     * access_token 过期时间
     */
    @TableField("expiration_time")
    private Date expirationTime;
}
  1. 获取授权的URL

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

/**
 * @description:聚水潭
 * @author: WangYX
 * @create: 2021-12-31 10:41
 * @Version: 1.0.0
 **/
@Data
public class JuShuiTanQuery {

    @ApiModelProperty(value = "自定义字段", example = "xxxxx")
    private String state;
}

    /**
     * 授权URL有效时间为15分钟,生成链接之后,请尽快让商家登录授权
     *
     * @param juShuiTanQuery
     * @return
     */
    @ApiOperation("获取授权的URL")
    @ResponseBody
    @RequestMapping(value = "/auth/url", method = RequestMethod.POST)
    public ResponseResult getAuthorizationUrl(@RequestBody JuShuiTanQuery juShuiTanQuery) {
        String url = null;
        try {
            url = JuShuiTanUtil.spliceAuthorizationUrl(juShuiTanQuery.getState());
        } catch (IOException e) {
            logger.error("IO异常:{}", e.getMessage());
            e.printStackTrace();
        }
        return new SuccessResponseResult(url);
    }

通过上面的方式获取到授权的URL之后,商家便可以打开授权的页面。
在这里插入图片描述
商家登录成功后,可查看待授权的公司名称及其下应用名称信息确认并同意授权,完成业务授权操作。
在这里插入图片描述
商家点击确认授权之后,聚水潭平台便会通过回调地址返回code。
授权回调的接口如下:GET的请求方式。
是否验签可以由开发者自己决定。

  /**
     * 授权码有效期仅有15分钟,超时则需要重新生成授权链接,由商家重新授权。
     *
     * @return
     */
    @ApiIgnore
    @ApiOperation("授权回调")
    @ResponseBody
    @RequestMapping(value = "/auth/callBack", method = RequestMethod.GET)
    public Object callBackAuthorization() {

        HttpServletRequest httpServletRequest = getHttpServletRequest();

        String app_key = httpServletRequest.getParameter("app_key");
        String code = httpServletRequest.getParameter("code");
        String state = httpServletRequest.getParameter("state");
        String sign = httpServletRequest.getParameter("sign");

        logger.info("聚水潭授权回调:appKey-{},code-{},state-{},sign-{}", app_key, code, state, sign);

        //验签
        Map<String, String> params = new HashMap<>();
        params.put("app_key", app_key);
        params.put("code", code);
        params.put("state", state);
        try {
            //验签结果
            Boolean signatureVerification = true;

            if (juShuiTanProperties.getSignature()) {
                //验证签名
                signatureVerification = JuShuiTanUtil.signatureVerification(params, sign);
            }

            if (signatureVerification) {
                //获取AccessToken
                getAccessToken(code);
            } else {
                logger.error("验签失败!!!");
                throw new CommonExceptionOptimize(SendboxExceptionEnum.SIGNATURE_EXCEPTION, getSdkLanguage());
            }
        } catch (IOException e) {
            logger.error("IO异常:{}", e.getMessage());
            e.printStackTrace();
        }
        Map<String, Object> result = new HashMap<>();
        result.put("code", 0);
        result.put("msg", "success");
        return JSON.toJSON(result);
    }

获取到code之后便可以通过code换取access_token。
使用code换取access_token接口地址为:https://openapi.jushuitan.com/openWeb/auth/accessToken,请求方式为:POST。
接口参数说明如下:

参数类型名称示例值是否必填
app_keystring开发者应用Key0ecde8631431a5ed6b3e7368afbabdadss必填
timestampstring当前请求的时间戳【单位是秒】1577771730必填
grant_typestring固定值:authorization_codeauthorization_code必填
charsetstring交互数据的编码【utf-8】目前只能传utf-8,不能不传!utf-8必填
codestring授权码4xFIOC必填
signstring请求的数字签名,是通过所有请求参数通过摘要生成的,保证请求参数没有被篡改。0ecde8631431a5ed6b3e7368afbabdaoas必填

响应参数说明如下:

参数类型名称示例值是否必填
access_tokenstring访问令牌0ecde8631431a5ed6b3e7368afbabdadss必填
expires_instringaccess_token有效期【单位是秒】2592000必填
refresh_tokenstring更新令牌eb1964a9d142423a9f0de88b97bb38fc必填
scopestring固定值:allall必填

代码示例:

 /**
     * Function:获取AccessToken
     *
     * @author Wangyx
     * @date 2021/12/31 15:33
     * @version 1.0.0
     */
    private void getAccessToken(String code) throws IOException {
        //获取AccessToken
        String accessTokenJson = JuShuiTanUtil.getAccessToken(code);
        JSONObject jsonObject = JSON.parseObject(accessTokenJson);
        String statusCode = jsonObject.getString("code");
        if ("0".equals(statusCode)) {
            String data = jsonObject.getString("data");

            QueryWrapper<AccessTokenDO> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().eq(AccessTokenDO::getMark, Const.POST_TO);
            accessTokenService.remove(queryWrapper);

            AccessTokenDO accessToken = JSON.parseObject(data, AccessTokenDO.class);
            //1.保存到数据库,
            accessToken.setMark(Const.POST_TO);
            Long expirationTime = System.currentTimeMillis() / 1000 + Long.valueOf(accessToken.getExpiresIn()) - 3600;
            //过期时间设置提前设置1小时
            Date date = new Date(expirationTime * 1000);
            accessToken.setExpirationTime(date);
            accessTokenService.save(accessToken);

            //2.同步到redis
            //过期时间设置提前设置1小时
            redisClient.set(String.format(RedisKeyPreConst.JU_SHUI_TAN_ACCESS_TOKEN, Const.POST_TO), accessToken.getAccessToken(), accessToken.getExpiresIn() - 3600);

        } else {
            logger.error("获取聚水潭AccessToken异常:{}", accessTokenJson);
            throw new CommonExceptionOptimize(SendboxExceptionEnum.SERVER_ERROR, getSdkLanguage());
        }
    }

access_token的有效时间是30天。我这里的处理方式为在数据库和redis的缓存中都存储了一份。
在代码中使用到access_token的流程为先从缓存中获取,如果缓存中不存在从数据库中获取,然后判断数据库中的数据是否过了有效时间。如果过了就使用refresh_token来刷新access_token。
同时也设置一个定时任务在判断access_token是否到了过期时间,过期了则使用refresh_token来刷新access_token。

更新令牌接口地址:https://openapi.jushuitan.com/openWeb/auth/refreshToken, 请求方式:Post。

接口请求参数:

参数类型名称示例值是否必填
app_keystring开发者应用Key0ecde8631431a5ed6b3e7368afbabdadss必填
timestampstring当前请求的时间戳【单位是秒】1577771730必填
grant_typestring固定值:refresh_tokenrefresh_token必填
charsetstring交互数据的编码【utf-8】目前只能传utf-8,不能不传!utf-8必填
refresh_tokenstring更新令牌eb1964a9d142423a9f0de88b97bb38fc必填
scopestring固定值:allall必填
signstring请求的数字签名,是通过所有请求参数通过摘要生成的,保证请求参数没有被篡改。0ecde8631431a5ed6b3e7368afbabdaoas必填

响应参数说明如下:

参数类型名称示例值是否必填
access_tokenstring访问令牌0ecde8631431a5ed6b3e7368afbabdadss必填
expires_instringaccess_token有效期【单位是秒】2592000必填
refresh_tokenstring更新令牌eb1964a9d142423a9f0de88b97bb38fc必填
scopestring固定值:allall必填

代码示例如下:

 /**
     * Function:更新授权令牌
     *
     * @author Wangyx
     * @date 2022/01/04 10:16
     * @version 1.0.0
     */
    public void refreshToken() {
        QueryWrapper<AccessTokenDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(AccessTokenDO::getMark, Const.POST_TO);
        AccessTokenDO one = accessTokenService.getOne(queryWrapper);
        if (one != null) {
            String refreshToken = one.getRefreshToken();
            String result = null;
            try {
                result = JuShuiTanUtil.refreshToken(refreshToken);
            } catch (IOException e) {
                e.printStackTrace();
            }
            JSONObject jsonObject = JSON.parseObject(result);
            String statusCode = jsonObject.getString("code");
            if ("0".equals(statusCode)) {
                String data = jsonObject.getString("data");
                AccessTokenDO accessToken = JSON.parseObject(data, AccessTokenDO.class);
                //1.保存到数据库,
                accessToken.setMark(Const.POST_TO);
                Long expirationTime = System.currentTimeMillis() / 1000 + Long.valueOf(accessToken.getExpiresIn()) - 3600;
                //过期时间设置提前设置1小时
                Date date = new Date(expirationTime);
                accessToken.setExpirationTime(date);
                accessTokenService.save(accessToken);

                //2.同步到redis
                //过期时间设置提前设置1小时
                redisClient.set(String.format(RedisKeyPreConst.JU_SHUI_TAN_ACCESS_TOKEN, Const.POST_TO), accessToken.getAccessToken(), accessToken.getExpiresIn() - 3600);
            } else if ("100".equals(statusCode)) {
                logger.info("access_token超时");
                throw new CommonExceptionOptimize(SendboxExceptionEnum.AUTHORIZATION_EXPIRED);
            }
        } else {
            logger.info("聚水潭未授权");
            throw new CommonExceptionOptimize(SendboxExceptionEnum.UNAUTHORIZED);
        }
    }

定时任务:

 //每天凌晨一点执行
    @Scheduled(cron = "0 0 1 * * ?")
    public void refreshToken() {
        logger.info("更新聚水潭授权令牌任务启动,执行时间为:{}", new Date());
        QueryWrapper<AccessTokenDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(AccessTokenDO::getMark, Const.POST_TO);
        AccessTokenDO one = accessTokenService.getOne(queryWrapper);
        if (one != null) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            if (!sdf.format(new Date()).equals(sdf.format(one.getExpirationTime()))) {
                logger.info("更新聚水潭授权令牌,更新时间未到,当前时间为:{},更新时间为:{}", sdf.format(new Date()), sdf.format(one.getExpirationTime()));
                return;
            }

            String refreshToken = one.getRefreshToken();
            String result = null;
            try {
                result = JuShuiTanUtil.refreshToken(refreshToken);
            } catch (IOException e) {
                e.printStackTrace();
            }
            JSONObject jsonObject = JSON.parseObject(result);
            String statusCode = jsonObject.getString("code");
            if ("0".equals(statusCode)) {
                String data = jsonObject.getString("data");

                QueryWrapper<AccessTokenDO> wrapper = new QueryWrapper<>();
                wrapper.lambda().eq(AccessTokenDO::getMark, Const.POST_TO);
                accessTokenService.remove(wrapper);

                AccessTokenDO accessToken = JSON.parseObject(data, AccessTokenDO.class);
                //1.保存到数据库,
                accessToken.setMark(Const.POST_TO);
                Long expirationTime = System.currentTimeMillis() / 1000 + Long.valueOf(accessToken.getExpiresIn()) - 3600;
                //过期时间设置提前设置1小时
                Date date = new Date(expirationTime);
                accessToken.setExpirationTime(date);
                accessTokenService.save(accessToken);

                //2.同步到redis
                //过期时间设置提前设置1小时
                redisClient.set(String.format(RedisKeyPreConst.JU_SHUI_TAN_ACCESS_TOKEN, Const.POST_TO), accessToken.getAccessToken(), accessToken.getExpiresIn() - 3600);
            } else if ("100".equals(statusCode)) {
                logger.info("access_token超时");
                throw new CommonExceptionOptimize(SendboxExceptionEnum.AUTHORIZATION_EXPIRED);
            }
        } else {
            logger.info("更新聚水潭授权令牌任务失败");
        }
    }

至此,第三方授权流程基本结束。

4. 调用示例

    /**
     * Function:获取授权令牌
     *
     * @author Wangyx
     * @date 2022/01/04 10:16
     * @version 1.0.0
     */
    public String getAccessToken() {
        String accessToken = redisClient.get(String.format(RedisKeyPreConst.JU_SHUI_TAN_ACCESS_TOKEN, Const.POST_TO));
        if (StringUtils.isEmpty(accessToken)) {
            //更新授权令牌
            refreshToken();
        }
        accessToken = redisClient.get(String.format(RedisKeyPreConst.JU_SHUI_TAN_ACCESS_TOKEN, Const.POST_TO));
        return accessToken;
    }
    /**
     * 店铺查询
     *
     * @param index 第几页,默认第一页
     * @param size  每页多少条;默认100条,最大100条
     * @param nicks 店铺主账号,不支持模糊查询,非必填项,默认查所有
     * @return
     * @throws IOException
     */
    public String shopQuery(Integer index, Integer size, String nicks) throws IOException {
        String accessToken = accessTokenManager.getAccessToken();
        String url = "/open/shops/query";
        //请求参数
        Map<String, Object> params = new HashMap<>();
        params.put("page_index", index);
        params.put("page_size", size);
        if (!StringUtils.isEmpty(nicks)) {
            String[] split = nicks.split(",");
            params.put("nicks", split);
        }
        String biz = JSON.toJSONString(params);
        String result = JuShuiTanUtil.sendRequest(accessToken, biz, url);
        return result;
    }

至于接口的调用规则以及签名规则均已封装在了JuShuiTanUtil的工具类中。
详细的情况可以参考开放平台的地址:https://openweb.jushuitan.com

电信AEP平台对接工程是指将电信AEP平台与其他系统进行连接和集成的工程项目。 首先,电信AEP平台是指电信运营商的应用扩展平台(Application Extension Platform),它提供了丰富的接口和功能,可以与其他系统进行数据交互和业务集成。对接工程的目标就是将AEP平台与其他系统无缝连接,实现数据共享和业务协同。 对接工程的步骤主要包括需求分析、接口设计、开发实现、测试验证和部署上线等。首先需要与相关系统的业务人员和开发团队进行沟通,了解业务需求和系统架构,明确对接的目标和需求。然后进行接口设计,确定需要对接的接口和数据格式,并与相关人员进行确认。 接下来,开发团队根据接口设计文档进行开发工作,编写代码实现数据的传输和转换,确保数据在不同系统之间的正确传递。开发过程中需要进行测试,保证对接的稳定性和数据的准确性。 在测试阶段,需要进行功能测试、性能测试和兼容性测试等,验证对接的结果是否符合预期。同时还需要运行一些模拟场景和异常情况,确保对接的稳定性和可靠性。 最后,对接工程完成后需要部署上线,即将开发好的对接程序安装到相应的服务器上,并与其他系统进行配置和联调,确保整个系统能够正常运行。 总结来说,电信AEP平台对接工程是一项复杂的工作,需要涉及到需求分析、接口设计、开发实现、测试验证和部署上线等多个环节。只有完成了这些步骤,才能实现不同系统之间的数据共享和业务协同。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值