From befbb28b2810ed108d2744bceee2bb9b3edaa9bc Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Sat, 13 Jun 2026 13:53:04 +0800
Subject: [PATCH] feat(mall): 添加USDT支付功能和相关服务
---
src/main/java/cc/mrbird/febs/mall/controller/dependentStation/utils/Trc20TokenviewContentModel.java | 24 +
src/main/java/cc/mrbird/febs/mall/controller/dependentStation/utils/OkHttpUtil2.java | 203 +++++++++++++++
src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiMallOrderController.java | 21 +
src/main/java/cc/mrbird/febs/mall/controller/dependentStation/utils/Trc20TokenviewModel.java | 17 +
src/main/java/cc/mrbird/febs/mall/quartz/ChatTrc20ChargeOkLinkTask.java | 185 ++++++++++++++
src/main/java/cc/mrbird/febs/mall/controller/dependentStation/constant/OrderConstants.java | 43 +++
src/main/java/cc/mrbird/febs/mall/service/impl/ApiChatPayServiceImpl.java | 123 +++++++++
src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallOrderInfoServiceImpl.java | 36 ++
src/main/java/cc/mrbird/febs/mall/dto/ApiOrderPayDto.java | 22 +
src/main/java/cc/mrbird/febs/mall/service/IApiMallMemberService.java | 2
src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallMemberServiceImpl.java | 20 +
src/main/java/cc/mrbird/febs/mall/controller/dependentStation/enums/SalesServiceEnums.java | 8
src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiMallShoppingCartController.java | 1
src/main/java/cc/mrbird/febs/mall/service/ApiChatPayService.java | 10
src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiLoginController.java | 12
src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiMallNewsController.java | 4
src/main/java/cc/mrbird/febs/mall/service/IApiMallOrderInfoService.java | 2
src/main/java/cc/mrbird/febs/mall/vo/ApiOrderPayVo.java | 25 +
src/main/java/cc/mrbird/febs/mall/entity/MallOrderInfo.java | 1
src/main/java/cc/mrbird/febs/mall/vo/MallMoneyChangeVo.java | 19 +
20 files changed, 770 insertions(+), 8 deletions(-)
diff --git a/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiLoginController.java b/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiLoginController.java
index 24218d8..7ecc950 100644
--- a/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiLoginController.java
+++ b/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiLoginController.java
@@ -6,6 +6,7 @@
import cc.mrbird.febs.common.utils.RedisUtils;
import cc.mrbird.febs.mall.dto.*;
import cc.mrbird.febs.mall.service.IApiMallMemberService;
+import cc.mrbird.febs.mall.vo.MallMoneyChangeVo;
import cc.mrbird.febs.mall.vo.MallSalesServiceVo;
import cc.mrbird.febs.pay.model.WxGenerateQrCodeDto;
import cc.mrbird.febs.pay.service.IXcxPayService;
@@ -71,4 +72,15 @@
return memberService.salesService();
}
+
+
+ @ApiOperation(value = "价格转换", notes = "价格转换")
+ @ApiResponses({
+ @ApiResponse(code = 200, message = "success", response = MallMoneyChangeVo.class)
+ })
+ @GetMapping(value = "/moneyChange")
+ public FebsResponse moneyChange() {
+ return memberService.moneyChange();
+ }
+
}
diff --git a/src/main/java/cc/mrbird/febs/mall/controller/ApiMallNewsController.java b/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiMallNewsController.java
similarity index 95%
rename from src/main/java/cc/mrbird/febs/mall/controller/ApiMallNewsController.java
rename to src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiMallNewsController.java
index cbdaa62..d2026c5 100644
--- a/src/main/java/cc/mrbird/febs/mall/controller/ApiMallNewsController.java
+++ b/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiMallNewsController.java
@@ -1,4 +1,4 @@
-package cc.mrbird.febs.mall.controller;
+package cc.mrbird.febs.mall.controller.dependentStation;
import cc.mrbird.febs.common.entity.FebsResponse;
import cc.mrbird.febs.mall.dto.NewsListDto;
@@ -23,7 +23,7 @@
@RestController
@RequestMapping(value = "/api/news")
@RequiredArgsConstructor
-@Api(value = "ApiMallNewsController", tags = "新闻接口类")
+@Api(value = "ApiMallNewsController", tags = "ds-新闻接口")
public class ApiMallNewsController {
private final IApiMallNewsService newsService;
diff --git a/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiMallOrderController.java b/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiMallOrderController.java
index 22cebeb..2417866 100644
--- a/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiMallOrderController.java
+++ b/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiMallOrderController.java
@@ -5,6 +5,7 @@
import cc.mrbird.febs.common.entity.LimitType;
import cc.mrbird.febs.mall.dto.*;
import cc.mrbird.febs.mall.service.IApiMallOrderInfoService;
+import cc.mrbird.febs.mall.vo.ApiOrderPayVo;
import cc.mrbird.febs.mall.vo.OrderDetailVo;
import cc.mrbird.febs.mall.vo.OrderListVo;
import cc.mrbird.febs.pay.service.IXcxPayService;
@@ -43,12 +44,17 @@
}
@ApiOperation(value = "创建订单", notes = "创建订单")
+ @ApiResponses({
+ @ApiResponse(code = 200, message = "success", response = ApiOrderPayVo.class)
+ })
@PostMapping(value = "/createOrder")
@Limit(key = "createOrder", period = 1, count = 1, name = "注册", prefix = "limit",limitType = LimitType.IP)
public FebsResponse createOrder(@RequestBody @Validated AddOrderDto addOrderDto) {
Long orderId = mallOrderInfoService.createOrder(addOrderDto);
-
- return new FebsResponse().success().data(orderId).message("Order successfully created");
+ ApiOrderPayDto apiOrderPayDto = new ApiOrderPayDto();
+ apiOrderPayDto.setOrderId(orderId);
+ apiOrderPayDto.setPayType(3);
+ return mallOrderInfoService.payOrderByCoin(apiOrderPayDto);
}
@ApiOperation(value = "取消订单", notes = "取消订单")
@@ -70,6 +76,17 @@
return new FebsResponse().success().data(map).message("支付成功");
}
+
+ @ApiOperation(value = "USDT支付订单", notes = "USDT支付订单")
+ @ApiResponses({
+ @ApiResponse(code = 200, message = "success", response = ApiOrderPayVo.class)
+ })
+ @PostMapping(value = "/payOrderByCoin", produces = "application/json")
+ public FebsResponse payOrder(@RequestBody @Validated ApiOrderPayDto payDto) {
+
+ return mallOrderInfoService.payOrderByCoin(payDto);
+ }
+
@ApiOperation(value = "订单列表", notes = "订单列表")
@ApiResponses({
@ApiResponse(code = 200, message = "success", response = OrderListVo.class)
diff --git a/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiMallShoppingCartController.java b/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiMallShoppingCartController.java
index 7dc5036..f5fba52 100644
--- a/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiMallShoppingCartController.java
+++ b/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiMallShoppingCartController.java
@@ -54,7 +54,6 @@
@ApiOperation(value = "从购物车中删除商品", notes = "从购物车中删除商品")
@PostMapping(value = "/delGoods")
- @Limit(key = "delGoods", period = 1, count = 1, name = "注册", prefix = "limit",limitType = LimitType.IP)
public FebsResponse delGoods(@RequestBody @Validated DelCartGoodsDto delCartGoodsDto) {
List<String> ids = StrUtil.split(delCartGoodsDto.getIds(), ',');
mallShoppingCartService.removeByIds(ids);
diff --git a/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/constant/OrderConstants.java b/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/constant/OrderConstants.java
new file mode 100644
index 0000000..39b8621
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/constant/OrderConstants.java
@@ -0,0 +1,43 @@
+package cc.mrbird.febs.mall.controller.dependentStation.constant;
+
+public class OrderConstants {
+
+ public static final String ORDER_EXPIRE_MINUTES = "chat.order.expire";
+
+ /**
+ * 充值类型 1-自助充值 2-后台充值
+ */
+ public static final Integer ORDER_CHARGE_TYPE_MEMBER = 1;
+ public static final Integer ORDER_CHARGE_TYPE_SYSTEM = 2;
+ public static final String ORDER_CHARGE_TYPE = "order_charge_type";
+
+
+ /**
+ * 订单生效状态 1-生效中 2-已失效
+ */
+ public static final Integer ORDER_WORK_SUCCESS = 1;
+ public static final Integer ORDER_WORK_FAIL = 2;
+ public static final String ORDER_WORK_STATE = "order_work_state";
+
+ /**
+ * 支付状态 1-待支付 2-支付中 3-支付成功 0-支付失败
+ */
+ public static final Integer ORDER_STATE_WAIT = 1;
+ public static final Integer ORDER_STATE_ING = 2;
+ public static final Integer ORDER_STATE_SUCCESS = 3;
+ public static final Integer ORDER_STATE_FAIL = 0;
+ public static final String ORDER_STATE = "order_state";
+
+ /**
+ * 支付方式 1-微信 2-支付宝 3-USDT
+ */
+ public static final Integer PAY_TYPE_WECHAT = 1;
+ public static final Integer PAY_TYPE_ALIPAY = 2;
+ public static final Integer PAY_TYPE_USDT = 3;
+ public static final Integer PAY_TYPE_SYSTEM = 4;
+ public static final String ORDER_PAY_TYPE = "order_pay_type";
+
+ public static final String TRC20_SYSTEM_ADDRESS = "pay.trc20.address";
+
+ public static final String TRC20_ORDER_KEY = "pay:trc20:order:";
+}
diff --git a/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/enums/SalesServiceEnums.java b/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/enums/SalesServiceEnums.java
index 3245649..a43405e 100644
--- a/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/enums/SalesServiceEnums.java
+++ b/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/enums/SalesServiceEnums.java
@@ -5,7 +5,13 @@
@Getter
public enum SalesServiceEnums {
- // 发货人
+ // 价格转换
+ USD("MONEY_CHANGE", "USD"),
+
+ // trc20地址
+ TRC_ADDRESS("SALES_SERVICE", "TRC_ADDRESS"),
+
+ // 售后方式
ADDRESS("SALES_SERVICE", "ADDRESS"),
WORKINGHOURS("SALES_SERVICE", "WORKINGHOURS"),
WHATSAPP("SALES_SERVICE", "WHATSAPP"),
diff --git a/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/utils/OkHttpUtil2.java b/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/utils/OkHttpUtil2.java
new file mode 100644
index 0000000..45dc0b0
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/utils/OkHttpUtil2.java
@@ -0,0 +1,203 @@
+package cc.mrbird.febs.mall.controller.dependentStation.utils;
+
+import okhttp3.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+import java.util.Objects;
+
+public class OkHttpUtil2 {
+ protected final static Logger logger = LoggerFactory.getLogger(OkHttpUtil2.class);
+ private static Dispatcher dispatcher = new Dispatcher();
+
+ static {
+ dispatcher.setMaxRequests(200);
+ dispatcher.setMaxRequestsPerHost(100);
+ }
+
+// private static OkHttpClient httpClient = new OkHttpClient.Builder().dispatcher(dispatcher).build();
+ private static OkHttpClient httpClient = createUnsafeOkHttpClient();
+
+ private static OkHttpClient createUnsafeOkHttpClient() {
+ try {
+ final TrustManager[] trustAllCerts = new TrustManager[]{
+ new X509TrustManager() {
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType) {
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String authType) {
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return new X509Certificate[0];
+ }
+ }
+ };
+
+ final SSLContext sslContext = SSLContext.getInstance("SSL");
+ sslContext.init(null, trustAllCerts, new SecureRandom());
+ final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
+
+ OkHttpClient.Builder builder = new OkHttpClient.Builder();
+ builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
+ builder.hostnameVerifier((hostname, session) -> true);
+ builder.dispatcher(dispatcher);
+
+ return builder.build();
+ } catch (NoSuchAlgorithmException | KeyManagementException e) {
+ throw new RuntimeException("Failed to create unsafe SSL client", e);
+ }
+ }
+ public static byte[] doGet(String url, Map<String, String[]> header, Map<String, String[]> params, String expectContentType) {
+ Request.Builder builder = new Request.Builder();
+ addHeader(builder, header);
+ addUrlParam(builder, url, params);
+ return requestExec(builder.build(), expectContentType);
+ }
+
+ public static byte[] doGetSingle(String url, Map<String, String> header, Map<String, String> params, String expectContentType) {
+ Request.Builder builder = new Request.Builder();
+ addHeaderSingle(builder, header);
+ addUrlParamSingle(builder, url, params);
+ return requestExec(builder.build(), expectContentType);
+ }
+
+ public static byte[] doPost(String url, Map<String, String[]> header, Map<String, String[]> body, String expectContentType) {
+ Request.Builder builder = new Request.Builder().url(url);
+ addHeader(builder, header);
+ addBodyParam(builder, body, "POST");
+ return requestExec(builder.build(), expectContentType);
+ }
+
+
+
+ private static void addHeaderSingle(Request.Builder builder, Map<String, String> header) {
+ if (header == null) {
+ return;
+ }
+ for (String key : header.keySet()) {
+ String value = header.get(key);
+ if (value != null) {
+ builder.addHeader(key, value);
+ }
+ }
+ }
+
+ private static void addHeader(Request.Builder builder, Map<String, String[]> header) {
+ if (header == null) {
+ return;
+ }
+ for (String key : header.keySet()) {
+ String[] values = header.get(key);
+ if (values != null) {
+ for (String value : values) {
+ builder.addHeader(key, value);
+ }
+ }
+ }
+ }
+
+ private static void addUrlParam(Request.Builder builder, String url, Map<String, String[]> params) {
+ if (params == null) {
+ return;
+ }
+ HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
+ for (String key : params.keySet()) {
+ String[] values = params.get(key);
+ if (values != null) {
+ for (String value : values) {
+ urlBuilder.addQueryParameter(key, value);
+ }
+ }
+ }
+ builder.url(urlBuilder.build());
+ }
+
+ private static void addUrlParamSingle(Request.Builder builder, String url, Map<String, String> params) {
+ if (params == null) {
+ return;
+ }
+ HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
+ for (String key : params.keySet()) {
+ String value = params.get(key);
+ if (value != null) {
+ urlBuilder.addQueryParameter(key, value);
+ }
+ }
+ builder.url(urlBuilder.build());
+ }
+
+ private static void addBodyParam(Request.Builder builder, Map<String, String[]> body, String method) {
+ if (body == null) {
+ return;
+ }
+ FormBody.Builder formBodyBuilder = new FormBody.Builder(StandardCharsets.UTF_8);
+ for (String key : body.keySet()) {
+ String[] values = body.get(key);
+ if (values != null) {
+ for (String value : values) {
+ formBodyBuilder.add(key, value);
+ }
+ }
+ }
+ builder.method(method, formBodyBuilder.build());
+ }
+
+ private static byte[] requestExec(Request request, String expectContentType) {
+ Objects.requireNonNull(request, "okHttp request is null");
+
+ try (Response response = httpClient.newCall(request).execute()) {
+ if (response.code() == 200) {
+ ResponseBody body = response.body();
+ if (body != null) {
+ byte[] bytes = body.bytes();
+ String contentType = response.header("Content-Type");
+ if (contentType != null && !contentType.contains(expectContentType)) {
+ String res = new String(bytes, StandardCharsets.UTF_8);
+ System.out.println(res);
+ return bytes;
+ }
+ return bytes;
+ }
+ logger.error("response body is null");
+ System.out.println("response body is null");
+ } else {
+ ResponseBody body = response.body();
+ String res = "";
+ byte[] bytes = null;
+ if (body != null) {
+ bytes = body.bytes();
+ String contentType = response.header("Content-Type");
+ if (contentType != null && !contentType.contains(expectContentType)) {
+ //res = new String(body.bytes(), StandardCharsets.UTF_8);
+ //return body.bytes();
+ }
+ res = new String(bytes, StandardCharsets.UTF_8);
+
+ }
+ logger.error("request failed, http code:{},responseBody:{} ", response.code(), res);
+ System.out.println("request failed, http code: " + response.code());
+
+ return bytes;
+ }
+ } catch (IOException ioException) {
+ logger.error("request exec error:", ioException);
+ System.out.println("request exec error: " + ioException.getMessage());
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/utils/Trc20TokenviewContentModel.java b/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/utils/Trc20TokenviewContentModel.java
new file mode 100644
index 0000000..1a9f766
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/utils/Trc20TokenviewContentModel.java
@@ -0,0 +1,24 @@
+package cc.mrbird.febs.mall.controller.dependentStation.utils;
+
+import lombok.Data;
+
+/**
+ * @author your name
+ * @description TODO
+ * @date 2025/5/30
+ */
+@Data
+public class Trc20TokenviewContentModel {
+ private Integer index;
+ private Integer confirmations;
+ private Integer block_no;
+ private String token;
+ private String tokenAddr;
+ private String tokenSymbol;
+ private String tokenDecimals;
+ private Long time;
+ private String txid;
+ private String from;
+ private String to;
+ private String value;
+}
diff --git a/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/utils/Trc20TokenviewModel.java b/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/utils/Trc20TokenviewModel.java
new file mode 100644
index 0000000..8f4d600
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/mall/controller/dependentStation/utils/Trc20TokenviewModel.java
@@ -0,0 +1,17 @@
+package cc.mrbird.febs.mall.controller.dependentStation.utils;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author your name
+ * @description TODO
+ * @date 2025/5/30
+ */
+@Data
+public class Trc20TokenviewModel {
+ private Integer code;
+ private String msg;
+ List<Trc20TokenviewContentModel> data;
+}
diff --git a/src/main/java/cc/mrbird/febs/mall/dto/ApiOrderPayDto.java b/src/main/java/cc/mrbird/febs/mall/dto/ApiOrderPayDto.java
new file mode 100644
index 0000000..0317ad9
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/mall/dto/ApiOrderPayDto.java
@@ -0,0 +1,22 @@
+package cc.mrbird.febs.mall.dto;
+
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+@ApiModel(value = "ApiOrderPayDto", description = "参数")
+public class ApiOrderPayDto {
+
+
+ @NotNull(message = "订单ID不能为空")
+ @ApiModelProperty(value = "订单ID", example = "you_ke_*****")
+ private Long orderId;
+
+ @NotNull(message = "支付方式不能为空")
+ @ApiModelProperty(value = "支付方式 1-微信 2-支付宝 3-USDT", example = "you_ke_*****")
+ private Integer payType;
+}
\ No newline at end of file
diff --git a/src/main/java/cc/mrbird/febs/mall/entity/MallOrderInfo.java b/src/main/java/cc/mrbird/febs/mall/entity/MallOrderInfo.java
index 5754d1c..e2350ea 100644
--- a/src/main/java/cc/mrbird/febs/mall/entity/MallOrderInfo.java
+++ b/src/main/java/cc/mrbird/febs/mall/entity/MallOrderInfo.java
@@ -26,6 +26,7 @@
private Date payTime;
+ private String tradeHash;
private BigDecimal amount;
private String payMethod;
diff --git a/src/main/java/cc/mrbird/febs/mall/quartz/ChatTrc20ChargeOkLinkTask.java b/src/main/java/cc/mrbird/febs/mall/quartz/ChatTrc20ChargeOkLinkTask.java
new file mode 100644
index 0000000..ace4816
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/mall/quartz/ChatTrc20ChargeOkLinkTask.java
@@ -0,0 +1,185 @@
+package cc.mrbird.febs.mall.quartz;
+
+import cc.mrbird.febs.common.enumerates.OrderStatusEnum;
+import cc.mrbird.febs.common.utils.RedisUtils;
+import cc.mrbird.febs.mall.controller.dependentStation.constant.OrderConstants;
+import cc.mrbird.febs.mall.controller.dependentStation.enums.SalesServiceEnums;
+import cc.mrbird.febs.mall.controller.dependentStation.utils.OkHttpUtil2;
+import cc.mrbird.febs.mall.controller.dependentStation.utils.Trc20TokenviewContentModel;
+import cc.mrbird.febs.mall.controller.dependentStation.utils.Trc20TokenviewModel;
+import cc.mrbird.febs.mall.entity.DataDictionaryCustom;
+import cc.mrbird.febs.mall.entity.MallOrderInfo;
+import cc.mrbird.febs.mall.mapper.DataDictionaryCustomMapper;
+import cc.mrbird.febs.mall.mapper.MallOrderInfoMapper;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+@Slf4j
+@Component
+@ConditionalOnProperty(prefix = "system", name = "job", havingValue = "true")
+public class ChatTrc20ChargeOkLinkTask {
+
+
+ @Resource
+ private RedisUtils redisUtils;
+ @Resource
+ private MallOrderInfoMapper mallOrderInfoMapper;
+ @Resource
+ private DataDictionaryCustomMapper dataDictionaryCustomMapper;
+ /**
+ * 五分钟 毫秒
+ */
+ private final static long TIME_INTERVAL = 300000*6;
+
+ private final static String TRC20_TRANSFER_API = "https://apilist.tronscanapi.com/api/token_trc20/transfers";
+
+ public final static String TRC20_CONTRACT_ADDRESS = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t";
+
+ private final static String TRON_API_KEY = "32c9841d-19ae-48cf-a535-cf56d9d25f1c";
+
+ private static Map<String, String> REQUEST_HEADER = new HashMap<>();
+ static {
+ REQUEST_HEADER.put("TRON-PRO-API-KEY", TRON_API_KEY);
+ }
+
+ @Scheduled(cron = "0 0/5 * * * ? ")
+ public void recharge() {
+ // 查询过去5分钟的记录
+
+ DataDictionaryCustom dataDictionaryCustom = dataDictionaryCustomMapper.selectDicDataByTypeAndCode(
+ SalesServiceEnums.TRC_ADDRESS.getType(),
+ SalesServiceEnums.TRC_ADDRESS.getCode()
+ );
+ String receiveAddress = dataDictionaryCustom.getValue();
+ if (receiveAddress == null) {
+ log.error("请先配置系统地址");
+ return;
+ }
+ String url = "https://services.tokenview.io/vipapi/trx/address/tokentrans/"+receiveAddress+"/TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t/1/10";
+ // 定时任务的执行时间和转账时间区间间隔5分钟
+
+ // 每次将上次的时间存入redis,第一次使用默认的时间
+ long endTime = System.currentTimeMillis();
+ long startTime = endTime-TIME_INTERVAL;
+
+ System.out.println(new Date()+" 自动充值定时任务fyTrc20RechargeTokenviewTask "+startTime+" "+endTime);
+ // 当前的充值地址 TRC20USDT_ADDRESS
+ Map<String, String> param = new HashMap<>();
+ param.put("timestampStart", startTime + "");
+ param.put("timestampEnd", endTime + "");
+ param.put("toAddress", receiveAddress);
+ param.put("apikey", TRON_API_KEY);
+ long l = System.currentTimeMillis();
+ byte[] bytes = OkHttpUtil2.doGetSingle(url, REQUEST_HEADER, param, "application/json");
+
+ if (ObjectUtil.isEmpty( bytes )) {
+ log.error("FyTrc20RechargeTask查询链上数据返回为空,传参:{}",param);
+ return;
+ }
+
+ String s = new String(bytes, StandardCharsets.UTF_8);
+ log.info("查询到的充值记录:{}", s);
+ Trc20TokenviewModel trc20TransfersModel = JSONObject.parseObject(s, Trc20TokenviewModel.class);
+ List<Trc20TokenviewContentModel> tokenTransfers = trc20TransfersModel.getData();
+ if (CollUtil.isEmpty(tokenTransfers)) {
+ //logger.error("FyTrc20RechargeTask查询链上数据返回没有token转账,返回结果:{}",s);
+ return;
+ }
+ long l1 = System.currentTimeMillis();
+ log.info("查询trc耗时:{}", (l1 - l));
+ log.info("查询到的充值记录:{}", JSONObject.toJSONString(trc20TransfersModel));
+ log.info("时间区间,start:{},end:{}", startTime, endTime);
+ // 有记录
+ for (Trc20TokenviewContentModel tokenTransfer : tokenTransfers) {
+ log.info("链上时间:{}", tokenTransfer.getTime());
+ // 从数据库
+ String transactionId = tokenTransfer.getTxid();
+ List<MallOrderInfo> chatOrders = mallOrderInfoMapper.selectList(
+ Wrappers.lambdaQuery(MallOrderInfo.class)
+ .eq(MallOrderInfo::getTradeHash, transactionId)
+ );
+ if(CollUtil.isNotEmpty(chatOrders)){
+ log.info("扫描到HASH已使用:{}",transactionId);
+ continue;
+ }
+ // 金额
+ String quant = tokenTransfer.getValue();
+ BigDecimal amount = new BigDecimal(quant).divide(new BigDecimal("1000000")).setScale(2, RoundingMode.DOWN);
+ String amountKey = OrderConstants.TRC20_ORDER_KEY + amount;
+ String orderCode = redisUtils.getString(amountKey);
+ if (StrUtil.isBlank(orderCode)) {
+ log.info("Redis未扫描到充值金额:{}",transactionId);
+ continue;
+ }
+ MallOrderInfo chatOrder = mallOrderInfoMapper.selectOne(
+ Wrappers.lambdaQuery(MallOrderInfo.class)
+ .eq(MallOrderInfo::getOrderNo, orderCode)
+ );
+ if(chatOrder==null){
+ log.error("未找到订单:{}",orderCode);
+ continue;
+ }
+ if(OrderStatusEnum.WAIT_PAY.getValue() != chatOrder.getStatus()){
+ log.error("订单不是待充值状态: {},订单编号:{}",transactionId,orderCode);
+ continue;
+ }
+
+ mallOrderInfoMapper.update(
+ null,
+ Wrappers.lambdaUpdate(MallOrderInfo.class)
+ .set(MallOrderInfo::getStatus, OrderStatusEnum.WAIT_SHIPPING.getValue())
+ .set(MallOrderInfo::getTradeHash, transactionId)
+ .set(MallOrderInfo::getPayTime, new Date())
+ .set(MallOrderInfo::getPayResult, "1")
+ .eq(MallOrderInfo::getId, chatOrder.getId())
+ );
+ redisUtils.del(amountKey);
+ log.info("Redis扫描到充值记录:{},订单编号:{}",transactionId,orderCode);
+ }
+ }
+
+
+ public static void main(String[] args) {
+ String receiveAddress = "TExto1UjtFcXKw5QdJDRqtx7wTQ15D37GD";
+ String url = "https://services.tokenview.io/vipapi/trx/address/tokentrans/"+receiveAddress+"/TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t/1/10";
+ // 定时任务的执行时间和转账时间区间间隔5分钟
+
+ // 每次将上次的时间存入redis,第一次使用默认的时间
+ long endTime = System.currentTimeMillis();
+ long startTime = endTime-TIME_INTERVAL;
+
+ System.out.println(new Date()+" 自动充值定时任务fyTrc20RechargeTokenviewTask "+startTime+" "+endTime);
+ // 当前的充值地址 TRC20USDT_ADDRESS
+ Map<String, String> param = new HashMap<>();
+ param.put("timestampStart", startTime + "");
+ param.put("timestampEnd", endTime + "");
+ param.put("toAddress", receiveAddress);
+ param.put("apikey", TRON_API_KEY);
+ long l = System.currentTimeMillis();
+ byte[] bytes = OkHttpUtil2.doGetSingle(url, REQUEST_HEADER, param, "application/json");
+
+ if (bytes == null) {
+ log.error("FyTrc20RechargeTask查询链上数据返回为空,传参:{}",param);
+ return;
+ }
+
+ String s = new String(bytes, StandardCharsets.UTF_8);
+ System.out.println(s);
+ System.exit(0);
+ }
+}
diff --git a/src/main/java/cc/mrbird/febs/mall/service/ApiChatPayService.java b/src/main/java/cc/mrbird/febs/mall/service/ApiChatPayService.java
new file mode 100644
index 0000000..5afd5ab
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/mall/service/ApiChatPayService.java
@@ -0,0 +1,10 @@
+package cc.mrbird.febs.mall.service;
+
+import cc.mrbird.febs.common.entity.FebsResponse;
+import cc.mrbird.febs.mall.entity.MallOrderInfo;
+
+public interface ApiChatPayService {
+
+
+ FebsResponse usPay(MallOrderInfo chatOrder);
+}
diff --git a/src/main/java/cc/mrbird/febs/mall/service/IApiMallMemberService.java b/src/main/java/cc/mrbird/febs/mall/service/IApiMallMemberService.java
index 2b2a227..d475298 100644
--- a/src/main/java/cc/mrbird/febs/mall/service/IApiMallMemberService.java
+++ b/src/main/java/cc/mrbird/febs/mall/service/IApiMallMemberService.java
@@ -49,4 +49,6 @@
FebsResponse setInvite(ApiSetInviteDto apiSetInviteDto);
FebsResponse salesService();
+
+ FebsResponse moneyChange();
}
diff --git a/src/main/java/cc/mrbird/febs/mall/service/IApiMallOrderInfoService.java b/src/main/java/cc/mrbird/febs/mall/service/IApiMallOrderInfoService.java
index 1036672..ca463e4 100644
--- a/src/main/java/cc/mrbird/febs/mall/service/IApiMallOrderInfoService.java
+++ b/src/main/java/cc/mrbird/febs/mall/service/IApiMallOrderInfoService.java
@@ -19,6 +19,8 @@
Map<String, Object> payOrder(PayOrderDto payOrderDto);
+ FebsResponse payOrderByCoin(ApiOrderPayDto payDto);
+
List<OrderListVo> findOrderList(OrderListDto orderListDto);
OrderDetailVo findOrderDetailsById(Long id);
diff --git a/src/main/java/cc/mrbird/febs/mall/service/impl/ApiChatPayServiceImpl.java b/src/main/java/cc/mrbird/febs/mall/service/impl/ApiChatPayServiceImpl.java
new file mode 100644
index 0000000..cefa3bb
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/mall/service/impl/ApiChatPayServiceImpl.java
@@ -0,0 +1,123 @@
+package cc.mrbird.febs.mall.service.impl;
+
+import cc.mrbird.febs.common.entity.FebsResponse;
+import cc.mrbird.febs.common.enumerates.OrderStatusEnum;
+import cc.mrbird.febs.common.exception.FebsException;
+import cc.mrbird.febs.common.utils.RedisUtils;
+import cc.mrbird.febs.mall.controller.dependentStation.constant.OrderConstants;
+import cc.mrbird.febs.mall.controller.dependentStation.enums.SalesServiceEnums;
+import cc.mrbird.febs.mall.entity.DataDictionaryCustom;
+import cc.mrbird.febs.mall.entity.MallOrderInfo;
+import cc.mrbird.febs.mall.mapper.DataDictionaryCustomMapper;
+import cc.mrbird.febs.mall.mapper.MallOrderInfoMapper;
+import cc.mrbird.febs.mall.service.ApiChatPayService;
+import cc.mrbird.febs.mall.vo.ApiOrderPayVo;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.HashMap;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class ApiChatPayServiceImpl implements ApiChatPayService {
+
+ @Resource
+ private RedisTemplate<String, String> redisTemplate;
+ private final MallOrderInfoMapper mallOrderInfoMapper;
+ private final DataDictionaryCustomMapper dataDictionaryCustomMapper;
+ @Override
+ public FebsResponse usPay(MallOrderInfo chatOrder) {
+
+ //判断是否redis已经缓存订单
+ BigDecimal amount = chatOrder.getAmount();
+ if (StrUtil.isEmpty(chatOrder.getTradeHash())) {
+ amount = generateUniqueNumber(chatOrder.getOrderNo(), amount);
+ }else{
+ amount = new BigDecimal(chatOrder.getTradeHash());
+ }
+ // 调用Mapper更新数据库中的订单信息
+ mallOrderInfoMapper.update(null,
+ Wrappers.lambdaUpdate(MallOrderInfo.class)
+ .set(MallOrderInfo::getPayMethod, OrderConstants.PAY_TYPE_USDT)
+ .set(MallOrderInfo::getTradeHash, null)
+ .eq(MallOrderInfo::getId, chatOrder.getId())
+ );
+
+ DataDictionaryCustom dataDictionaryCustom = dataDictionaryCustomMapper.selectDicDataByTypeAndCode(
+ SalesServiceEnums.TRC_ADDRESS.getType(),
+ SalesServiceEnums.TRC_ADDRESS.getCode()
+ );
+ String addressSystem = dataDictionaryCustom.getValue();
+ ApiOrderPayVo apiOrderPayVo = new ApiOrderPayVo();
+ apiOrderPayVo.setOrderCode(chatOrder.getOrderNo());
+ apiOrderPayVo.setAmount(amount.toString());
+ apiOrderPayVo.setAddress(addressSystem);
+ apiOrderPayVo.setCreatedTime(chatOrder.getCreatedTime());
+ return new FebsResponse().success().data(apiOrderPayVo);
+ }
+
+ /**
+ * 生成唯一数字
+ * 通过随机数和给定的金额计算生成一个唯一数字,并确保这个数字在Redis中是唯一的
+ *
+ * @param amount 金额
+ * @param orderCode 订单编码
+ * @return 生成的唯一数字
+ * @throws RuntimeException 如果在指定次数内无法生成唯一数字
+ */
+ private BigDecimal generateUniqueNumber(String orderCode,BigDecimal amount) {
+ int maxAttempts = 50;
+ int attempts = 0;
+ int num;
+ BigDecimal bigDecimal;
+
+ do {
+ if (attempts++ >= maxAttempts) {
+ throw new FebsException("Payment exception");
+ }
+ num = ThreadLocalRandom.current().nextInt(1, 51);
+
+ bigDecimal = calculateAmount(amount, num);
+
+ } while (!setRedisValue(bigDecimal, orderCode)); // 原子操作检查并写入
+
+ return bigDecimal;
+ }
+
+ /**
+ * 计算调整后的金额
+ * 根据给定的金额和一个随机数计算新的金额,用于生成唯一数字
+ *
+ * @param amount 原始金额
+ * @param num 随机生成的数字
+ * @return 调整后的金额
+ */
+ private BigDecimal calculateAmount(BigDecimal amount, int num) {
+ BigDecimal multiplier = new BigDecimal("0.01");
+ BigDecimal increment = new BigDecimal(num).multiply(multiplier);
+ return amount.add(increment).setScale(2, RoundingMode.DOWN);
+ }
+
+ /**
+ * 将生成的数字设置到Redis中
+ * 此方法确保生成的数字是唯一的,通过在Redis中设置值并使用NX参数来实现
+ *
+ * @param amountReal 生成的唯一数字
+ * @param orderCode 订单编码
+ * @return 如果设置成功则返回true,否则返回false
+ */
+ private boolean setRedisValue(BigDecimal amountReal, String orderCode) {
+ String key = OrderConstants.TRC20_ORDER_KEY + amountReal; // 添加前缀避免键冲突
+ return redisTemplate.opsForValue().setIfAbsent(key, orderCode, 60L * Integer.parseInt("30"), TimeUnit.SECONDS);
+ }
+}
diff --git a/src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallMemberServiceImpl.java b/src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallMemberServiceImpl.java
index 6f04506..4d3e4e5 100644
--- a/src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallMemberServiceImpl.java
+++ b/src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallMemberServiceImpl.java
@@ -422,6 +422,26 @@
return new FebsResponse().success().data(mallSalesServiceVo);
}
+ @Override
+ public FebsResponse moneyChange() {
+ List<MallMoneyChangeVo> vos = new ArrayList<>();
+ List<DataDictionaryCustom> dataDictionaryCustoms = dataDictionaryCustomMapper.selectDicByType(
+ SalesServiceEnums.USD.getType()
+ );
+ if (dataDictionaryCustoms.size() > 0){
+ for (DataDictionaryCustom dataDictionaryCustom : dataDictionaryCustoms){
+ MallMoneyChangeVo vo = new MallMoneyChangeVo();
+ vo.setMoneyChange(dataDictionaryCustom.getValue());
+ vo.setCode(dataDictionaryCustom.getCode());
+ vo.setMoneyCode(dataDictionaryCustom.getDescription());
+ vos.add(vo);
+ }
+
+ }
+ return new FebsResponse().success().data(vos);
+
+ }
+
public static void main(String[] args) {
Long userld = 173L;
String shopAccount = "luohu";
diff --git a/src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallOrderInfoServiceImpl.java b/src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallOrderInfoServiceImpl.java
index b181d54..b7d279d 100644
--- a/src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallOrderInfoServiceImpl.java
+++ b/src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallOrderInfoServiceImpl.java
@@ -5,6 +5,7 @@
import cc.mrbird.febs.common.exception.FebsException;
import cc.mrbird.febs.common.properties.XcxProperties;
import cc.mrbird.febs.common.utils.*;
+import cc.mrbird.febs.mall.controller.dependentStation.constant.OrderConstants;
import cc.mrbird.febs.mall.conversion.MallGoodsCommentConversion;
import cc.mrbird.febs.mall.conversion.MallOrderInfoConversion;
import cc.mrbird.febs.mall.conversion.MallOrderRefundConversion;
@@ -71,6 +72,7 @@
private final RedisUtils redisUtils;
private final AgentProducer agentProducer;
+ private final ApiChatPayService apiChatPayService;
private final IPayService payService;
private final IXcxPayService iXcxPayService;
private final IMallAchieveService mallAchieveService;
@@ -175,8 +177,8 @@
orderInfo.setLatitude(address.getLatitude());
orderInfo.setLongitude(address.getLongitude());
this.baseMapper.updateById(orderInfo);
- //过期时间修改成24小时
- agentProducer.sendOrderCancelDelayMsg(orderInfo.getId(), 24 * 60 * 60 * 1000L);
+ //过期时间修改成30分钟
+ agentProducer.sendOrderCancelDelayMsg(orderInfo.getId(), 30 * 60 * 1000L);
return orderInfo.getId();
}
@@ -477,6 +479,36 @@
return map;
}
+ /**
+ * 处理支付订单的请求
+ *
+ * @param payDto 包含支付订单所需信息的DTO
+ * @return 返回支付结果的AjaxResult对象
+ */
+ @Override
+ @Transactional
+ public FebsResponse payOrderByCoin(ApiOrderPayDto payDto) {
+ // 获取当前的用户
+ Long memberId = LoginUserUtil.getLoginUser().getId();
+ // 提取订单ID和支付类型
+ Long orderId = payDto.getOrderId();
+ Integer payType = payDto.getPayType();
+
+ // 验证订单是否存在
+
+ MallOrderInfo orderInfo =
+ ValidateEntityUtils.ensureColumnReturnEntity(orderId, MallOrderInfo::getId, this.baseMapper::selectOne, "Order does not exist");
+ ValidateEntityUtils.ensureEqual(memberId,orderInfo.getMemberId(),"Order does not exist");
+ ValidateEntityUtils.ensureEqual(OrderStatusEnum.WAIT_PAY.getValue(),orderInfo.getStatus(),"The order status is not pending payment");
+
+ // 根据支付类型调用相应的支付方法
+ if(OrderConstants.PAY_TYPE_USDT == payType){
+ return apiChatPayService.usPay(orderInfo);
+ }
+ // 如果支付类型不匹配或支付过程中出现异常,返回错误信息
+ return new FebsResponse().fail().message("Order exception, please contact us");
+ }
+
private String balancePay(MallOrderInfo orderInfo, String tradePwd, String field) {
if (StrUtil.isBlank(tradePwd)) {
throw new FebsException("支付密码错误");
diff --git a/src/main/java/cc/mrbird/febs/mall/vo/ApiOrderPayVo.java b/src/main/java/cc/mrbird/febs/mall/vo/ApiOrderPayVo.java
new file mode 100644
index 0000000..91d096a
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/mall/vo/ApiOrderPayVo.java
@@ -0,0 +1,25 @@
+package cc.mrbird.febs.mall.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@ApiModel(value = "ApiOrderPayVo", description = "信息返回类")
+public class ApiOrderPayVo {
+
+
+ @ApiModelProperty(value = "订单编号")
+ public String orderCode;
+ @ApiModelProperty(value = "支付金额")
+ public String amount;
+ @ApiModelProperty(value = "收款地址")
+ public String address;
+ @ApiModelProperty(value = "时间")
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ private Date createdTime;
+
+}
diff --git a/src/main/java/cc/mrbird/febs/mall/vo/MallMoneyChangeVo.java b/src/main/java/cc/mrbird/febs/mall/vo/MallMoneyChangeVo.java
new file mode 100644
index 0000000..a7bdbfa
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/mall/vo/MallMoneyChangeVo.java
@@ -0,0 +1,19 @@
+package cc.mrbird.febs.mall.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel(value = "MallMoneyChangeVo", description = "商城用户信息返回类")
+public class MallMoneyChangeVo {
+
+ @ApiModelProperty(value = "倍数")
+ private String moneyChange;
+
+ @ApiModelProperty(value = "编码")
+ private String code;
+
+ @ApiModelProperty(value = "小图标")
+ private String moneyCode;
+}
--
Gitblit v1.9.1