抖音 - 抖店开放平台:https://op.jinritemai.com/
该 SDK 已实现 API 接口调用和消息推送验证解析
github:
gitee:
API 文档:https://op.jinritemai.com/docs/guide-docs/10/814
注: 该 SDK 目前只适用于自用型应用,实现了自用型应用 client;后续补充工具型应用;两者的差别仅存在于获取授权的方式不一样
在配置文件中配置
doudian:
serverUrl: https://openapi-fxg.jinritemai.com
appKey:
appSecret:
signMethod: md5
readTimeout: 30000
version: 2
Bean 注入 DoudianClient
@Value("${spring.profiles.active}")
private String env;
@Autowired
private DoudianProperties doudianProperties;
/**
* 描述:默认的抖店开放平台 client(默认为自用型应用)
*
* @methodName defaultDoudianClient
* @param
* @return {@link DoudianClient}
* @date 2021/8/20 10:42
* @author chengqb
*/
@Bean
public DoudianClient defaultDoudianClient() {
DefaultDoudianClient defaultDoudianClient = new DefaultDoudianClient();
defaultDoudianClient.setServerUrl(doudianProperties.getServerUrl());
defaultDoudianClient.setAppKey(doudianProperties.getAppKey());
defaultDoudianClient.setAppSecret(doudianProperties.getAppSecret());
defaultDoudianClient.setSignMethod(doudianProperties.getSignMethod());
defaultDoudianClient.setReadTimeout(doudianProperties.getReadTimeout());
defaultDoudianClient.setVersion(doudianProperties.getVersion());
defaultDoudianClient.setEnv(env);
return defaultDoudianClient;
}
具体代码如下:
/**
* 描述:抖店平台 api 调用工具
*
* @author chengqb
* @className DoudianClient
* @date 2021/8/19 10:30
*/
public interface DoudianClient extends Cloneable {
/**
* 描述:创建并获取授权
*
* @methodName getAccessToken
* @param
* @return {@link DoudianAccessToken}
* @date 2021/8/19 10:38
* @author chengqb
*/
DoudianAccessToken getAccessToken();
/**
* 描述:刷新并获取授权
*
* @methodName getAccessToken
* @param refreshToken
* @return {@link DoudianAccessToken}
* @date 2021/8/19 10:38
* @author chengqb
*/
DoudianAccessToken getAccessToken(String refreshToken);
/**
* 描述:抖店接口调用
*
* @methodName execute
* @param request
* @param accessToken
* @return {@link T}
* @date 2021/8/19 10:38
* @author chengqb
*/
<T extends DoudianResponse> T execute(DoudianRequest<T> request, String accessToken);
}
请求类(Request)
基类(DoudianRequest)
该基类为一个接口,含有两个方法:
getMethod():用于请求时,构建请求路径,从而指定请求接口
getResponseClass():用于请求结果返回时,构建对应的 Response;必须被实现
分页基类(BasePage)
主要有两个参数:page(当前页码,从 0 开始)、size(每页条数)
具体接口请求类(Request)
必须实现 DoudianRequest 接口
需要分页请求的接口,具体接口请求类需继承 BasePage 类
必须定义 private final String method = "";
具体值,根据抖店开放平台 API 文档中的接口而定
注: method 由 “.” 分隔,具体可查看抖店开放平台 API 文档
如:
必须实现 getResponseClass() ,并指定具体的返回基类(Response)
再补充定义具体接口所需请求参数,如:shopOrderId(店铺订单号)
如:
/**
* 描述:获取抖店订单详情请求类
*
* @author chengqb
* @className DoudianOrderDetailRequest
* @date 2021/8/25 17:59
*/
@Data
public class DoudianOrderDetailRequest implements DoudianRequest<DoudianOrderDetailResponse> {
private final String method = "order.orderDetail";
/**
* 店铺订单号
*/
private String shopOrderId;
@Override
public Class<DoudianOrderDetailResponse> getResponseClass() {
return DoudianOrderDetailResponse.class;
}
}
返回基类(Response)
基类(DoudianResponse)
该基类后跟上一个泛型,标识具体返回参数类
返回基础结构如下:
{
"err_no": 0,
"message": "success",
"logId": "20210831199610203011"
"data": {}
}
分页基类(DoudianPage)
该基类后跟上一个泛型,标识具体返回参数类,指明具体分页数据类型
具体接口返回类(Response)
必须继承 DoudianResponse 类,并指定泛型
若为分页请求,DoudianResponse 的泛型指定为 DoudianPage ;而 DoudianPage 的泛型再指定为具体参数类;
如:public class DoudianSettleBillResponse extends DoudianResponse
如:
/**
* 描述:获取抖店订单详情返回类
*
* @author chengqb
* @className DoudianOrderDetailResponse
* @date 2021/8/25 18:00
*/
@Data
public class DoudianOrderDetailResponse extends DoudianResponse<DoudianOrderDetail> {
}
返回参数类(Entity)
普通返回参数类
根据抖店开放平台接口具体返回参数进行定义
如:
/**
* 描述:获取抖店订单详情返回参数
*
* @author chengqb
* @className DoudianOrderDetail
* @date 2021/8/25 18:01
*/
@Data
public class DoudianOrderDetail {
/**
* 抖店订单详情信息
*/
private ShopOrderDetail shopOrderDetail;
}
抖店订阅消息推送:https://op.jinritemai.com/docs/guide-docs/10/99
注: 需定义个接口,专门接收抖店各类订阅消息;再将该接口路径拼接上请求协议及请求域名,再填写至抖店开放平台配置中
此处,要求在 5 s 内必须返回 {"code":0,"msg":"success"}
;故而,建议在接收到消息内容并解析完成后,立即返回指定格式数据给抖店,再通过 异步来处理业务 ;doudian-open-test 使用了 mq 消息 的方式来实现了异步处理
基础返回类(BaseResponse)
只含有 code、msg 两个参数字段
基础数据类(DoudianMessageData )
含有 tag、msgId、data 三个参数字段;其中 data 为字符串类型,存放具体数据;
含有 toObject(Class tClass) 方法,可将 data 字符串数据转换为具体的订阅消息数据
如: DoudianOrderPaySuccess 为抖店订单支付成功订阅消息数据类
DoudianMessage pushMessage = DoudianMessageUtil.getDoudianMessage(request);
List<DoudianMessageData> pushDataList = pushMessage.getData();
for (DoudianMessageData messageData : messageDataList) {
DoudianOrderPaySuccess orderPaySuccess = messageData.toObject(DoudianOrderPaySuccess.class);
}
基础消息类(DoudianMessage)
含有 appId、eventSign、body 三个参数字段;其中 body 为字符串类型,存放具体数据;
含有 getData() 方法,可将 body 字符串数据转换为 DoudianMessageData 列表
消息获取
该 SDK 定义了 DoudianMessageUtil 工具类
其中的 public static DoudianMessage getDoudianMessage(HttpServletRequest request)
方法,可解析获取抖店开放平台订阅消息
具体代码可参考:
/**
* 描述:抖店开放平台订阅消息推送接收
*
* @author chengqb
* @className DoudianMessageController
* @date 2021/8/20 17:05
*/
@Slf4j
@RestController
@Api(tags = "抖店消息")
@RequestMapping("/doudian/message")
public class DoudianMessageController {
@Autowired
private DoudianMessageDataHandler doudianMessageDataHandler;
/**
* 描述:抖店订阅消息接收
*
* @methodName receiveMessage
* @param request
* @return {@link BaseResponse}
* @date 2021/8/12 14:42
* @author chengqb
*/
@ApiOperation(value = "抖店订阅消息接收", notes = "接收")
@PostMapping("/receiveMessage")
public BaseResponse receiveMessage(HttpServletRequest request) {
// return BaseResponse.failed(1, "测试中");
DoudianMessage pushMessage = DoudianMessageUtil.getDoudianMessage(request);
if (CollectionUtil.isEmpty(pushMessage.getData())) {
log.info("接收到抖店平台订阅消息,但消息解析失败");
return BaseResponse.failed(ReponseEnum.DATA_BLANK.getCode(), ReponseEnum.DATA_BLANK.getMsg());
} else {
// 推送地址添加后,平台会立即 Post 一条 "[{"tag":"0","msg_id":"0","data":"2020-09-10T16:27:56.52842897+08:00"}]" 的测试消息,
// 必须返回 {"code":0,"msg":"success"} ,否则平台判定推送地址异常,将无法启用消息推送服务
// (注意:超时也会被认为是推送失败,超时时间是5s)
List<DoudianMessageData> pushDataList = pushMessage.getData();
// 接收到抖店平台推送消息后,必须返回{"code":0,"msg":"success"}
if (pushDataList.size() == 1 && "0".equals(pushDataList.get(0).getTag())) {
log.info("接收到抖店平台订阅消息,消息 tag 为:{}", pushDataList.get(0).getTag());
return BaseResponse.ok();
} else {
log.info("接收到抖店平台订阅消息,并解析成功");
// 处理抖店订阅消息数据
doudianMessageDataHandler.proccessPushData(pushDataList);
// return BaseResponse.failed(1, "测试中");
return BaseResponse.ok();
}
}
}
}
店铺授权:https://op.jinritemai.com/docs/guide-docs/9/20
access_token 获取后,有效期为 7 天,而 refresh_token 有效期为 14 天
可参考如下代码:
DoudianOauthHandler
/**
* 描述:抖店平台 access_token 相关业务处理
*
* @author chengqb
* @className DoudianOauthHandler
* @date 2021/8/20 10:13
*/
@Slf4j
@Service
public class DoudianOauthHandler {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private DoudianOpenOauthHandler doudianOpenOauthHandler;
private static final String DOUDIAN_ACCESS_TOKEN_KEY = "doudian:access_token";
private static final String DOUDIAN_REFRESH_TOKEN_KEY = "doudian:refresh_token";
/**
* 描述:获取抖店授权
* 文档地址:https://op.jinritemai.com/docs/guide-docs/9/21
*
* @methodName getOauthToken
* @param
* @return {@link String}
* @date 2021/8/16 16:08
* @author chengqb
*/
public String getOauthToken() {
Object accessTokenObject = redisTemplate.opsForValue().get(DOUDIAN_ACCESS_TOKEN_KEY);
if (ObjectUtil.isNull(accessTokenObject)) {
String refreshToken = (String) redisTemplate.opsForValue().get(DOUDIAN_REFRESH_TOKEN_KEY);
// 获取 access_token
DoudianAccessToken accessToken = doudianOpenOauthHandler.getAccessToken(refreshToken);
redisTemplate.opsForValue().set(DOUDIAN_ACCESS_TOKEN_KEY, accessToken, accessToken.getExpiresIn(), TimeUnit.SECONDS);
redisTemplate.opsForValue().set(DOUDIAN_REFRESH_TOKEN_KEY, accessToken.getRefreshToken(), 14, TimeUnit.DAYS);
return accessToken.getAccessToken();
}
DoudianAccessToken accessToken = JSONUtil.toBean(JSONUtil.toJsonStr(accessTokenObject), DoudianAccessToken.class);
return accessToken.getAccessToken();
}
}
DoudianOpenOauthHandler
/**
* 描述:抖店平台对接 access_token 相关业务处理
*
* @author chengqb
* @className DoudianOpenOauthHandler
* @date 2021/8/20 10:15
*/
@Slf4j
@Service
public class DoudianOpenOauthHandler {
@Autowired
private DoudianClient defaultDoudianClient;
/**
* 描述:获取 access_token
*
* @methodName getAccessToken
* @param refreshToken
* @return {@link DoudianAccessToken}
* @date 2021/8/20 10:39
* @author chengqb
*/
public DoudianAccessToken getAccessToken(String refreshToken) {
DoudianAccessToken accessToken = null;
if (StrUtil.isBlank(refreshToken)) {
// 如果刷新令牌也过期了,则调用获取 access_token 接口
accessToken = defaultDoudianClient.getAccessToken();
} else {
// 如果刷新令牌还未过期了,则调用刷新 access_token 接口
accessToken = defaultDoudianClient.getAccessToken(refreshToken);
}
return accessToken;
}
}
参考资料: https://github.com/cnJun/sdk4-jinritemai