From 9a03e46771f68bae27b172c561b6b6f49b5f0505 Mon Sep 17 00:00:00 2001
From: Pu Zhibing <393733352@qq.com>
Date: 星期三, 11 六月 2025 20:22:59 +0800
Subject: [PATCH] 对接部分抖音接口
---
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/ClientTokenUtil.java | 70 ++++++++
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/DouyinConfig.java | 23 ++
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/OrderUtil.java | 106 +++++++++++++
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/model/WebHook.java | 29 +++
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/model/OrderWebHook.java | 21 ++
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/model/Order.java | 35 ++++
ruoyi-modules/ruoyi-order/pom.xml | 28 ++-
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/DYWebHookController.java | 123 +++++++++++++++
8 files changed, 427 insertions(+), 8 deletions(-)
diff --git a/ruoyi-modules/ruoyi-order/pom.xml b/ruoyi-modules/ruoyi-order/pom.xml
index 9db90d8..a6e1c7c 100644
--- a/ruoyi-modules/ruoyi-order/pom.xml
+++ b/ruoyi-modules/ruoyi-order/pom.xml
@@ -149,18 +149,24 @@
<version>4.13.1</version>
<scope>test</scope>
</dependency>
-
+
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.22</version>
</dependency>
- <dependency>
- <groupId>com.ruoyi</groupId>
- <artifactId>ruoyi-api-goods</artifactId>
- <version>3.6.2</version>
- <scope>compile</scope>
- </dependency>
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-api-goods</artifactId>
+ <version>3.6.2</version>
+ <scope>compile</scope>
+ </dependency>
+ <!--抖音sdk-->
+ <dependency>
+ <groupId>com.douyin.openapi</groupId>
+ <artifactId>sdk</artifactId>
+ <version>1.0.0</version>
+ </dependency>
</dependencies>
<build>
@@ -179,5 +185,11 @@
</plugin>
</plugins>
</build>
-
+ <repositories>
+ <!--抖音sdk-->
+ <repository>
+ <id>douyin-openapi-repo</id>
+ <url>https://artifacts-cn-beijing.volces.com/repository/douyin-openapi/</url>
+ </repository>
+ </repositories>
</project>
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/ClientTokenUtil.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/ClientTokenUtil.java
new file mode 100644
index 0000000..98618ef
--- /dev/null
+++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/ClientTokenUtil.java
@@ -0,0 +1,70 @@
+package com.ruoyi.order.util.douyin;
+
+import com.aliyun.tea.TeaException;
+import com.douyin.openapi.client.Client;
+import com.douyin.openapi.client.models.OauthClientTokenRequest;
+import com.douyin.openapi.client.models.OauthClientTokenResponse;
+import com.douyin.openapi.client.models.OauthClientTokenResponseData;
+import com.douyin.openapi.credential.models.Config;
+import lombok.extern.slf4j.Slf4j;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+
+/**
+ * 抖音获取client_token工具类
+ * @author zhibing.pu
+ * @Date 2025/6/11 18:46
+ */
+@Slf4j
+public class ClientTokenUtil {
+
+ public static String token = "";
+
+ public static Long expiration_time = 0L;
+
+
+ /**
+ * 获取client_token
+ */
+ public static void getClientToken() {
+ try {
+ Config config = new Config().setClientKey(DouyinConfig.CLIENT_KEY).setClientSecret(DouyinConfig.CLIENT_SECRET); // 改成自己的app_id跟secret
+ Client client = new Client(config);
+ /* 构建请求参数,该代码示例中只给出部分参数,请用户根据需要自行构建参数值
+ token:
+ 1.若用户自行维护token,将用户维护的token赋值给该参数即可
+ 2.SDK包中有获取token的函数,请根据接口path在《OpenAPI SDK 总览》文档中查找获取token函数的名字
+ 在使用过程中,请注意token互刷问题
+ header:
+ sdk中默认填充content-type请求头,若不需要填充除content-type之外的请求头,删除该参数即可
+ */
+ OauthClientTokenRequest sdkRequest = new OauthClientTokenRequest();
+ sdkRequest.setClientKey(DouyinConfig.CLIENT_KEY);
+ sdkRequest.setClientSecret(DouyinConfig.CLIENT_SECRET);
+ sdkRequest.setGrantType("client_credential");
+ OauthClientTokenResponse sdkResponse = client.OauthClientToken(sdkRequest);
+ String message = sdkResponse.getMessage();
+ if("success".equals(message)){
+ OauthClientTokenResponseData data = sdkResponse.getData();
+ if(data.getErrorCode() != 0){
+ log.error("【抖音】获取client_token失败:{}", data.getDescription());
+ throw new RuntimeException("【抖音】获取client_token失败:" + data.getDescription());
+ }
+ token = data.getAccessToken();
+ long second = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
+ expiration_time = second + data.getExpiresIn() - 30;
+ }else{
+ OauthClientTokenResponseData data = sdkResponse.getData();
+ if(data.getErrorCode() != 0){
+ log.error("【抖音】获取client_token失败:{}", data.getDescription());
+ throw new RuntimeException("【抖音】获取client_token失败:" + data.getDescription());
+ }
+ }
+ } catch (TeaException e) {
+ System.out.println(e.getMessage());
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ }
+ }
+}
diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/DYWebHookController.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/DYWebHookController.java
new file mode 100644
index 0000000..9a93645
--- /dev/null
+++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/DYWebHookController.java
@@ -0,0 +1,123 @@
+package com.ruoyi.order.util.douyin;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.ruoyi.common.redis.service.RedisService;
+import com.ruoyi.order.util.douyin.model.OrderWebHook;
+import com.ruoyi.order.util.douyin.model.WebHook;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.BufferedReader;
+import java.io.PrintWriter;
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * @author zhibing.pu
+ * @Date 2025/6/11 19:36
+ */
+@Slf4j
+@RestController
+@RequestMapping("/douyin")
+public class DYWebHookController {
+
+ @Resource
+ private RedisService redisService;
+
+
+ /**
+ * 抖音webhook
+ * @param request
+ * @param response
+ */
+ @ResponseBody
+ @PostMapping("/webhook")
+ public void orderWebHook(HttpServletRequest request, HttpServletResponse response) {
+ // 获取消息中body
+ String str, wholeStr = "";
+ try{
+ BufferedReader br = request.getReader();
+ while((str = br.readLine()) != null){
+ wholeStr += str;
+ }
+ log.info("【抖音】webhook获取请求内容:{}", wholeStr);
+ } catch (Exception e){
+ log.error("【抖音】webhook获取请求内容失败");
+ return;
+ }
+ // 获取请求头中的加签信息
+ String msgId = request.getHeader("Msg-Id");
+ String signature = request.getHeader("X-Douyin-Signature");
+ String data = DouyinConfig.CLIENT_SECRET + wholeStr;
+ String sign = DigestUtils.sha1Hex(data);
+ if(!sign.equals(signature)){
+ log.error("【抖音】webhook验签失败");
+ return;
+ }
+ if(redisService.hasKey(msgId)){
+ return;
+ }
+ redisService.setCacheObject(msgId, "", 60L, TimeUnit.SECONDS);
+ WebHook webHook = JSON.parseObject(wholeStr, WebHook.class);
+ String event = webHook.getEvent();
+ switch (event){
+ // 验证
+ case "verify_webhook":
+ verifyWebhook(webHook, response);
+ break;
+ //订单消息通知
+ case "life_trade_order_notify":
+ lifeTradeOrderNotify(webHook, response);
+ break;
+ //券消息通知
+ case "life_trade_certificate_notify":
+ break;
+ default:
+ break;
+ }
+
+ }
+
+
+ /**
+ * 校验webhook
+ * @param webHook
+ * @param response
+ */
+ public void verifyWebhook(WebHook webHook, HttpServletResponse response){
+ JSONObject jsonObject = JSON.parseObject(webHook.getContent());
+ //响应
+ PrintWriter out = null;
+ try {
+ out = response.getWriter();
+ out.print(jsonObject);
+ out.flush();
+ out.close();
+ }catch (Exception e){
+ e.printStackTrace();
+ }finally {
+ if(null != out){
+ out.close();
+ }
+ }
+ }
+
+
+ /**
+ * 生活服务订单webhook
+ * @param webHook
+ * @param response
+ */
+ public void lifeTradeOrderNotify(WebHook webHook, HttpServletResponse response){
+ OrderWebHook orderWebHook = JSON.parseObject(webHook.getContent(), OrderWebHook.class);
+
+ }
+}
diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/DouyinConfig.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/DouyinConfig.java
new file mode 100644
index 0000000..f6da792
--- /dev/null
+++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/DouyinConfig.java
@@ -0,0 +1,23 @@
+package com.ruoyi.order.util.douyin;
+
+/**
+ * 抖音配置文件
+ * @author zhibing.pu
+ * @Date 2025/6/11 18:41
+ */
+public interface DouyinConfig {
+
+ String APP_ID = "your app id";
+ /**
+ * 应用唯一标识
+ */
+ String CLIENT_KEY = "your app secret";
+ /**
+ * 应用的密钥
+ */
+ String CLIENT_SECRET = "your redirect uri";
+ /**
+ * 来客商户根账户ID
+ */
+ String ACCOUNT_ID = "your redirect uri";
+}
diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/OrderUtil.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/OrderUtil.java
new file mode 100644
index 0000000..8a5d454
--- /dev/null
+++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/OrderUtil.java
@@ -0,0 +1,106 @@
+package com.ruoyi.order.util.douyin;
+
+import com.douyin.openapi.client.Client;
+import com.douyin.openapi.client.models.*;
+import com.douyin.openapi.credential.models.Config;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+
+/**
+ * 抖音订单工具类
+ * @author zhibing.pu
+ * @Date 2025/6/11 18:58
+ */
+public class OrderUtil {
+
+
+ /**
+ * 查询订单列表
+ * @param page
+ * @param pageSize
+ * @param startTime
+ * @param endTime
+ * @return
+ */
+ public static OrderQueryResponse queryOrderList(Integer page, Integer pageSize, LocalDateTime startTime, LocalDateTime endTime) {
+ //判断token是否过期
+ long now = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
+ if(ClientTokenUtil.expiration_time < now){
+ ClientTokenUtil.getClientToken();
+ }
+ try {
+ Config config = new Config().setClientKey(DouyinConfig.CLIENT_KEY).setClientSecret(DouyinConfig.CLIENT_SECRET); // 改成自己的app_id跟secret
+ Client client = new Client(config);
+ OrderQueryRequest queryRequest = new OrderQueryRequest();
+ queryRequest.setAccessToken(ClientTokenUtil.token);
+ queryRequest.setAccountId(DouyinConfig.ACCOUNT_ID);
+ queryRequest.setPage(page);
+ queryRequest.setSize(pageSize);
+ if(null != startTime){
+ queryRequest.setStartTime(startTime.toEpochSecond(ZoneOffset.of("+8")));
+ }
+ if(null != endTime){
+ queryRequest.setEndTime(endTime.toEpochSecond(ZoneOffset.of("+8")));
+ }
+ OrderQueryResponse queryResponse = client.OrderQuery(queryRequest);
+ return queryResponse;
+ }catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+
+ /**
+ * 根据订单id查询订单详情
+ * @param orderId
+ * @return
+ */
+ public static OrderGetResponse getOrder(String orderId) {
+ //判断token是否过期
+ long now = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
+ if(ClientTokenUtil.expiration_time < now){
+ ClientTokenUtil.getClientToken();
+ }
+ try {
+ Config config = new Config().setClientKey(DouyinConfig.CLIENT_KEY).setClientSecret(DouyinConfig.CLIENT_SECRET); // 改成自己的app_id跟secret
+ Client client = new Client(config);
+ OrderGetRequest getRequest = new OrderGetRequest();
+ getRequest.setAccessToken(ClientTokenUtil.token);
+ getRequest.setOrderId(orderId);
+ OrderGetResponse orderGetResponse = client.OrderGet(getRequest);
+ return orderGetResponse;
+ }catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+
+ /**
+ * 根据订单id获取详细的订单信息
+ * @param orderId
+ * @return
+ */
+ public static OrderDetailGetResponse getOrderDetail(String orderId) {
+ //判断token是否过期
+ long now = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
+ if(ClientTokenUtil.expiration_time < now){
+ ClientTokenUtil.getClientToken();
+ }
+ try {
+ Config config = new Config().setClientKey(DouyinConfig.CLIENT_KEY).setClientSecret(DouyinConfig.CLIENT_SECRET); // 改成自己的app_id跟secret
+ Client client = new Client(config);
+ OrderDetailGetRequest request = new OrderDetailGetRequest();
+ request.setAccessToken(ClientTokenUtil.token);
+ request.setOrderId(orderId);
+ OrderDetailGetResponse orderDetailGetResponse = client.OrderDetailGet(request);
+ return orderDetailGetResponse;
+ }catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+}
diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/model/Order.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/model/Order.java
new file mode 100644
index 0000000..c450f23
--- /dev/null
+++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/model/Order.java
@@ -0,0 +1,35 @@
+package com.ruoyi.order.util.douyin.model;
+
+import lombok.Data;
+
+/**
+ * @author zhibing.pu
+ * @Date 2025/6/11 19:34
+ */
+@Data
+public class Order {
+ /**
+ * 商家账号id
+ */
+ private String account_id;
+ /**
+ * 创单时间,秒级时间戳
+ */
+ private Long create_time;
+ /**
+ * 订单id
+ */
+ private String order_id;
+ /**
+ * 售卖价格(分)
+ */
+ private Long original_amount;
+ /**
+ * 用户支付价格(分)
+ */
+ private Long pay_amount;
+ /**
+ * 支付时间,秒级时间戳
+ */
+ private Long pay_time;
+}
diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/model/OrderWebHook.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/model/OrderWebHook.java
new file mode 100644
index 0000000..36cd569
--- /dev/null
+++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/model/OrderWebHook.java
@@ -0,0 +1,21 @@
+package com.ruoyi.order.util.douyin.model;
+
+import lombok.Data; /**
+ * @author zhibing.pu
+ * @Date 2025/6/11 19:32
+ */
+@Data
+public class OrderWebHook {
+ /**
+ * 事件类型
+ */
+ private String action;
+ /**
+ * 消息发送时间
+ */
+ private Long msg_time;
+ /**
+ * 订单信息
+ */
+ private Order order;
+}
diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/model/WebHook.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/model/WebHook.java
new file mode 100644
index 0000000..fc77f23
--- /dev/null
+++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/model/WebHook.java
@@ -0,0 +1,29 @@
+package com.ruoyi.order.util.douyin.model;
+
+import lombok.Data; /**
+ * @author zhibing.pu
+ * @Date 2025/6/11 19:42
+ */
+@Data
+public class WebHook {
+ /**
+ * 消息类型,用于区分各类消息
+ */
+ private String event;
+ /**
+ * 对应服务商平台或开发者平台中的APPID,应用ID
+ */
+ private String client_key;
+ /**
+ * 标识用户身份的openId,同一用户在不同的APPID中openId不相同
+ */
+ private String from_user_id;
+ /**
+ * 消息内容,根据需要解析消息内容,不同类型的消息内容不同
+ */
+ private String content;
+ /**
+ * 抖音内部日志id,可提供给抖音方便排查问题
+ */
+ private String log_id;
+}
--
Gitblit v1.7.1