Pu Zhibing
7 天以前 9a03e46771f68bae27b172c561b6b6f49b5f0505
对接部分抖音接口
7个文件已添加
1个文件已修改
421 ■■■■■ 已修改文件
ruoyi-modules/ruoyi-order/pom.xml 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/ClientTokenUtil.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/DYWebHookController.java 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/DouyinConfig.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/OrderUtil.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/model/Order.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/model/OrderWebHook.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/model/WebHook.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-order/pom.xml
@@ -161,6 +161,12 @@
            <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>
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/ClientTokenUtil.java
New file
@@ -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());
        }
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/DYWebHookController.java
New file
@@ -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);
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/DouyinConfig.java
New file
@@ -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";
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/OrderUtil.java
New file
@@ -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;
    }
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/model/Order.java
New file
@@ -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;
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/model/OrderWebHook.java
New file
@@ -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;
}
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/douyin/model/WebHook.java
New file
@@ -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;
}