Forráskód Böngészése

update:优化gateio订单列表同步逻辑v2

lvzhiqiang 3 hete
szülő
commit
5760f30f37

+ 32 - 0
src/main/java/top/lvzhiqiang/entity/CoinSpotOrders.java

@@ -0,0 +1,32 @@
+package top.lvzhiqiang.entity;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * 精简版现货订单表
+ *
+ * @author lvzhiqiang
+ * 2026/3/13 15:33
+ */
+@Data
+public class CoinSpotOrders implements Serializable {
+
+    private String id;
+    private String currencyPair;
+    private String side;
+    private String type;
+    private String amount;
+    private String price;
+    private String filledAmount;
+    private String avgDealPrice;
+    private String filledTotal;
+    private String fee;
+    private String feeCurrency;
+    private String status;
+    private String finishAs;
+    private String createTimeMs;
+    private String updateTimeMs;
+}

+ 14 - 0
src/main/java/top/lvzhiqiang/mapper/CoinGateMapper.java

@@ -1,11 +1,13 @@
 package top.lvzhiqiang.mapper;
 
 import java.util.List;
+import java.util.Map;
 
 import org.apache.ibatis.annotations.Insert;
 import org.apache.ibatis.annotations.Param;
 
 import me.chanjar.weixin.cp.bean.oa.WxCpApprovalInfoQueryFilter.KEY;
+import org.apache.ibatis.annotations.Select;
 import top.lvzhiqiang.entity.CoinGateSpotOrders;
 
 /**
@@ -45,4 +47,16 @@ public interface CoinGateMapper {
             "    update_time_ms = VALUES(update_time_ms)" +
             " </script>" })
     int batchUpsert(@Param("orders") List<CoinGateSpotOrders> fundrates);
+
+    @Select({"<script>" +
+            "select *  from coin_gate_spot_orders WHERE  1 = 1" +
+            "<if test=\"keyword != null and keyword != ''\">" +
+            "   and currency_pair like concat('%',#{keyword},'%')" +
+            "</if>" +
+            "<if test=\"side != null and side != ''\">" +
+            "   and side = #{side}" +
+            "</if>" +
+            " order by ${sortField} ${sort}" +
+            "</script>"})
+    List<CoinGateSpotOrders> findHistorySpotOrderList(Map javaObject);
 }

+ 104 - 0
src/main/java/top/lvzhiqiang/service/impl/CoinServiceImpl.java

@@ -17,6 +17,7 @@ import org.apache.commons.lang3.time.DurationFormatUtils;
 import org.jsoup.Connection;
 import org.redisson.api.RLock;
 import org.redisson.api.RedissonClient;
+import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
@@ -123,6 +124,9 @@ public class CoinServiceImpl implements CoinService {
     private BookmarkMapper bookmarkMapper;
 
     @Resource
+    private CoinGateMapper coinGateMapper;
+
+    @Resource
     private RedissonClient redissonClient;
 
     @Resource
@@ -1784,6 +1788,19 @@ public class CoinServiceImpl implements CoinService {
             renderMainSearch4OrderHistoryProductType(historyOrderList);
             //result = (JSONArray) JSON.toJSON(historyOrderList);
             return historyOrderPageInfo;
+        } else if (params.getString("nameEn").equals("orderHistorySpotProductType")) {
+            PageHelper.startPage(params.getInteger("pageNo"), params.getInteger("pageSize"), true);
+
+            List<CoinSpotOrders> spotOrdersList = new ArrayList<>();
+            String exchangeCategory = params.getString("exchangeCategory");
+            if ("6".equals(exchangeCategory)){
+                List<CoinGateSpotOrders> gateSpotOrdersList = coinGateMapper.findHistorySpotOrderList(params.toJavaObject(Map.class));
+                renderMainSearch4OrderHistorySpotProductType(gateSpotOrdersList, spotOrdersList);
+            }
+
+            PageInfo<CoinSpotOrders> historyOrderPageInfo = new PageInfo<>(spotOrdersList);
+
+            return historyOrderPageInfo;
         } else if (params.getString("nameEn").equals("traderList")) {
             PageHelper.startPage(params.getInteger("pageNo"), params.getInteger("pageSize"), true);
             List<CoinTrader> mixTraderList = coinMapper.findMixTraderList(params.toJavaObject(Map.class));
@@ -2276,6 +2293,52 @@ public class CoinServiceImpl implements CoinService {
         }
     }
 
+    /**
+     * 动态格式化数字:小数点后,从第一个非0数字开始,保留指定的有效位数
+     * * @param value        原始数值
+     * @param keepFraction 需要保留的非0有效小数位数 (如你的需求是 3)
+     * @return 格式化后的清爽字符串
+     */
+    public static String formatDynamicScale(BigDecimal value, int keepFraction) {
+        // 1. 判空防雷
+        if (value == null || value.compareTo(BigDecimal.ZERO) == 0) {
+            return "0";
+        }
+
+        // 2. 取绝对值去零并转纯文本,方便字符串分析 (负数不影响我们找小数点)
+        String plainStr = value.abs().stripTrailingZeros().toPlainString();
+        int dotIndex = plainStr.indexOf('.');
+
+        // 3. 如果没有小数点(纯整数),直接返回去零后的原始字符串
+        if (dotIndex == -1) {
+            return value.stripTrailingZeros().toPlainString();
+        }
+
+        // 4. 计算小数点后连续的 0 的个数
+        int leadingZeros = 0;
+        String fractionPart = plainStr.substring(dotIndex + 1);
+        for (int i = 0; i < fractionPart.length(); i++) {
+            if (fractionPart.charAt(i) == '0') {
+                leadingZeros++;
+            } else {
+                break; // 遇到第一个非 0 数字,停止计数
+            }
+        }
+
+        // 5. 核心逻辑:计算目标标度 (Target Scale)
+        // 例如:0.00000098333 -> 连续6个0,保留3位 -> 目标标度为 6 + 3 = 9
+        int targetScale = leadingZeros + keepFraction;
+
+        // 6. 截断保留指定精度
+        // 注意:这里强烈建议使用 RoundingMode.DOWN (向下取整/直接截断)
+        // 在金融/币圈,数量和金额通常宁愿截断,也不能四舍五入(HALF_UP),防止出现"超卖/超买"的资金盘差异
+        BigDecimal scaledValue = value.setScale(targetScale, RoundingMode.DOWN);
+
+        // 7. 最后做一次兜底去零。
+        // 防止出现例如截断后变成 139.990,去零后变成更清爽的 139.99
+        return scaledValue.stripTrailingZeros().toPlainString();
+    }
+
     private void renderMainSearch4TraderList(List<CoinTrader> mixTraderList) {
         for (CoinTrader mixTrader : mixTraderList) {
             mixTrader.setLastTradeTime(DateUtils.longToString(Long.valueOf(mixTrader.getLastTradeTime())));
@@ -2487,6 +2550,47 @@ public class CoinServiceImpl implements CoinService {
     }
 
     /**
+     * 渲染获取全部历史委托-现货
+     *
+     * @param spotOrdersList
+     */
+    private void renderMainSearch4OrderHistorySpotProductType(List<CoinGateSpotOrders> gateSpotOrdersList, List<CoinSpotOrders> spotOrdersList) {
+        for (CoinGateSpotOrders coinGateSpotOrders : gateSpotOrdersList) {
+            CoinSpotOrders coinSpotOrders = new CoinSpotOrders();
+            BeanUtils.copyProperties(coinGateSpotOrders, coinSpotOrders);
+
+            // 币种名称
+            coinSpotOrders.setCurrencyPair(coinGateSpotOrders.getCurrencyPair().replace("_USDT", ""));
+            // 订单状态
+            coinSpotOrders.setStatus(InitRunner.publicParamsMap.get("gateStatus").getString(coinGateSpotOrders.getStatus()));
+            if ("cancelled".equals(coinGateSpotOrders.getStatus()) && coinGateSpotOrders.getFilledTotal().compareTo(BigDecimal.ZERO) > 0) {
+                coinSpotOrders.setStatus("已结束的订单");
+            }
+            // 结束状态
+            coinSpotOrders.setFinishAs(InitRunner.publicParamsMap.get("gatefinishAs").getString(coinGateSpotOrders.getFinishAs()));
+            if ("cancelled".equals(coinGateSpotOrders.getFinishAs()) && coinGateSpotOrders.getFilledTotal().compareTo(BigDecimal.ZERO) > 0) {
+                coinSpotOrders.setFinishAs("部分成交");
+            }
+            // 交易方向
+            coinSpotOrders.setSide(InitRunner.publicParamsMap.get("spotSide").getString(coinGateSpotOrders.getSide()));
+            // 订单类型
+            coinSpotOrders.setType(InitRunner.publicParamsMap.get("orderType").getString(coinGateSpotOrders.getType()));
+            // 委托数量,委托价格,已成交数量,成交均价,成交总金额,手续费
+            coinSpotOrders.setAmount(formatDynamicScale(coinGateSpotOrders.getAmount(), 3));
+            coinSpotOrders.setPrice(formatDynamicScale(coinGateSpotOrders.getPrice(), 3));
+            coinSpotOrders.setFilledAmount(formatDynamicScale(coinGateSpotOrders.getFilledAmount(), 3));
+            coinSpotOrders.setAvgDealPrice(formatDynamicScale(coinGateSpotOrders.getAvgDealPrice(), 3));
+            coinSpotOrders.setFilledTotal(formatDynamicScale(coinGateSpotOrders.getFilledTotal(), 3));
+            coinSpotOrders.setFee(formatDynamicScale(coinGateSpotOrders.getFee(), 3));
+
+            coinSpotOrders.setCreateTimeMs(DateUtils.longToString(coinGateSpotOrders.getCreateTimeMs()));
+            coinSpotOrders.setUpdateTimeMs(DateUtils.longToString(coinGateSpotOrders.getUpdateTimeMs()));
+
+            spotOrdersList.add(coinSpotOrders);
+        }
+    }
+
+    /**
      * 渲染获取全部当前委托
      *
      * @param result

+ 25 - 0
src/main/resources/static/coin.html

@@ -599,6 +599,31 @@
                 <option value="close_short">平空</option>
             </select>
         </div>
+        <div id="apis-quiet-div-orderHistorySpotProductType" style="display: none;">
+            <button class="apis-quiet-div-button3" slideDiv="apis-quiet-content" pageO="prev">上一页</button>
+            <button class="apis-quiet-div-button3" slideDiv="apis-quiet-content" pageO="next">下一页</button>
+            <input type="text" style="width: 100px;padding-top: 3px;" id="apis-quiet-div-orderHistorySpotProductType-pageNo" value="1">
+            <input type="text" style="width: 100px;padding-top: 3px;" id="apis-quiet-div-orderHistorySpotProductType-pageSize" disabled="disabled" value="20">
+            <input type="text" style="width: 100px;padding-top: 3px;" id="apis-quiet-div-orderHistorySpotProductType-pages" disabled="disabled" value="999999">
+            <input type="text" style="width: 100px;padding-top: 3px;" id="apis-quiet-div-orderHistorySpotProductType-keyword" placeholder="币对关键词">
+            <select id="apis-quiet-div-orderHistorySpotProductType-sortField" style="height: 24px;">
+                <option value="create_time_ms">创建时间</option>
+                <option value="filled_total">成交总金额</option>
+            </select>
+            <select id="apis-quiet-div-orderHistorySpotProductType-sort" style="height: 24px;">
+                <option value="desc">desc</option>
+                <option value="asc">asc</option>
+            </select>
+            <select id="apis-quiet-div-orderHistorySpotProductType-side" style="height: 24px;">
+                <option value="">交易方向</option>
+                <option value="buy">买入</option>
+                <option value="sell">卖出</option>
+            </select>
+            <select id="apis-quiet-div-orderHistorySpotProductType-exchangeCategory" style="height: 24px;">
+                <option value="6">Gate.Io</option>
+                <option value="7">Bitget</option>
+            </select>
+        </div>
     </div>
     <div id="apis-quiet-content" style="display: none;">
         total:<span class="contentSPAN">0</span><span class="contentSPAN2"></span>

+ 9 - 1
src/main/resources/static/js/my-coin.js

@@ -550,6 +550,14 @@ function mainSearch(url, nameEn, slideDiv, typetype, needCustomFlag) {
         jsonData.sortField = $("#apis-quiet-div-orderHistoryProductType-sortField").val();
         jsonData.sort = $("#apis-quiet-div-orderHistoryProductType-sort").val();
         jsonData.side = $("#apis-quiet-div-orderHistoryProductType-side").val();
+    } else if (nameEn === 'orderHistorySpotProductType') {
+        jsonData.pageNo = $("#apis-quiet-div-orderHistorySpotProductType-pageNo").val();
+        jsonData.pageSize = $("#apis-quiet-div-orderHistorySpotProductType-pageSize").val();
+        jsonData.keyword = $("#apis-quiet-div-orderHistorySpotProductType-keyword").val();
+        jsonData.sortField = $("#apis-quiet-div-orderHistorySpotProductType-sortField").val();
+        jsonData.sort = $("#apis-quiet-div-orderHistorySpotProductType-sort").val();
+        jsonData.side = $("#apis-quiet-div-orderHistorySpotProductType-side").val();
+        jsonData.exchangeCategory = $("#apis-quiet-div-orderHistorySpotProductType-exchangeCategory").val();
     } else if (nameEn === 'traderList') {
         jsonData.pageNo = $("#apis-quiet-div-traderList-pageNo").val();
         jsonData.pageSize = $("#apis-quiet-div-traderList-pageSize").val();
@@ -640,7 +648,7 @@ function mainSearch(url, nameEn, slideDiv, typetype, needCustomFlag) {
                     return;
                 }
 
-                if (nameEn === 'orderHistoryProductType' || nameEn === 'traderList' || nameEn === 'watchlist' || nameEn === 'image' || nameEn === 'cmcmap' || nameEn === 'music' || nameEn === 'currentHolding' || nameEn === 'bookmark' || nameEn === 'youtubeLive' || nameEn === 'goldenQuotes') {
+                if (nameEn === 'orderHistoryProductType' || nameEn === 'orderHistorySpotProductType' || nameEn === 'traderList' || nameEn === 'watchlist' || nameEn === 'image' || nameEn === 'cmcmap' || nameEn === 'music' || nameEn === 'currentHolding' || nameEn === 'bookmark' || nameEn === 'youtubeLive' || nameEn === 'goldenQuotes') {
                     $("#apis-quiet-div-" + nameEn).find("input[id$=pages]").val(data.data.pages);
                     $('#' + slideDiv).find("span.contentSPAN").html(data.data.total);
                     data = data.data.list;