新版仿ok交易所-后端
1
zj
2025-09-30 d4be4cc69f18b01cc39bd3f9dc9497a828848ca8
1
17 files modified
5 files added
937 ■■■■ changed files
trading-order-admin/src/main/java/com/yami/trading/admin/task/InitHandle.java 5 ●●●●● patch | view | raw | blame | history
trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiCapitaltWalletWalletController.java 8 ●●●● patch | view | raw | blame | history
trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiWalletController.java 8 ●●●● patch | view | raw | blame | history
trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiWithdrawController.java 15 ●●●● patch | view | raw | blame | history
trading-order-admin/src/main/java/com/yami/trading/api/controller/exchange/ApiExchangeApplyOrderController.java 19 ●●●●● patch | view | raw | blame | history
trading-order-admin/src/main/resources/application-prod.yml 2 ●●● patch | view | raw | blame | history
trading-order-admin/src/main/resources/redisson/redisson-dev.yml 2 ●●● patch | view | raw | blame | history
trading-order-bean/src/main/java/com/yami/trading/bean/item/domain/Item.java 2 ●●● patch | view | raw | blame | history
trading-order-huobi/src/main/java/com.yami.trading.huobi/data/job/ETFDataSynchronizationJob.java 272 ●●●●● patch | view | raw | blame | history
trading-order-huobi/src/main/java/com.yami.trading.huobi/data/job/StockGetDataJob.java 25 ●●●●● patch | view | raw | blame | history
trading-order-huobi/src/main/java/com.yami.trading.huobi/data/job/StockGetMarketJob.java 6 ●●●● patch | view | raw | blame | history
trading-order-huobi/src/main/java/com.yami.trading.huobi/data/model/ETFData.java 30 ●●●●● patch | view | raw | blame | history
trading-order-huobi/src/main/java/com.yami.trading.huobi/data/model/ETFResponse.java 19 ●●●●● patch | view | raw | blame | history
trading-order-huobi/src/main/java/com.yami.trading.huobi/data/model/RealTimeData.java 35 ●●●●● patch | view | raw | blame | history
trading-order-huobi/src/main/java/com.yami.trading.huobi/data/model/RealTimeResponse.java 19 ●●●●● patch | view | raw | blame | history
trading-order-huobi/src/main/java/com.yami.trading.huobi/hobi/internal/XinLangDataServiceImpl.java 20 ●●●● patch | view | raw | blame | history
trading-order-huobi/src/main/java/com.yami.trading.huobi/hobi/internal/XueQiuDataServiceImpl.java 371 ●●●● patch | view | raw | blame | history
trading-order-service/src/main/java/com/yami/trading/service/exchange/impl/ExchangeApplyOrderServiceImpl.java 20 ●●●● patch | view | raw | blame | history
trading-order-service/src/main/java/com/yami/trading/service/impl/RechargeBlockchainOrderServiceImpl.java 13 ●●●●● patch | view | raw | blame | history
trading-order-service/src/main/java/com/yami/trading/service/impl/WithdrawServiceImpl.java 38 ●●●●● patch | view | raw | blame | history
trading-order-service/src/main/java/com/yami/trading/service/item/ItemService.java 2 ●●● patch | view | raw | blame | history
trading-order-service/src/main/java/com/yami/trading/service/user/impl/UserStatisticsServiceImpl.java 6 ●●●●● patch | view | raw | blame | history
trading-order-admin/src/main/java/com/yami/trading/admin/task/InitHandle.java
@@ -112,6 +112,7 @@
        // todo 先注释观察报错
//        futuresRecomConsumeServer.start();
        log.info("开始Data初始化........");
        stockGetDataJob.start();
        List<Item> items = itemService.list();
        for (Item item : items) {
            AdjustmentValueCache.getCurrentValue().put(item.getSymbol(), item.getAdjustmentValue());
@@ -135,9 +136,9 @@
         klineInitService.klineInit(symbols);
        // 高低修正
        highLowHandleJob.start();
        stockGetMarketJob.start();
//        stockGetMarketJob.start();
//        // 获取realtime实时数据
        stockGetDataJob.start();
//        forexGetDataJob.start(); 外汇
        cryptosGetDataJob.start();
        fakeSymbolGetDataJob.start();
trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiCapitaltWalletWalletController.java
@@ -92,8 +92,8 @@
        // 获取合约账户(contract)
        Wallet wallet = walletService.saveWalletByPartyId(partyId);
        // 获取资金账户(capital)
        CapitaltWallet capitaltWallet = capitaltWalletService.getOne(new LambdaQueryWrapper<>(CapitaltWallet.class)
                .eq(CapitaltWallet::getUserId, partyId).last(" limit 1 "));
//        CapitaltWallet capitaltWallet = capitaltWalletService.getOne(new LambdaQueryWrapper<>(CapitaltWallet.class)
//                .eq(CapitaltWallet::getUserId, partyId).last(" limit 1 "));
        List<WalletExtend> walletExtends = walletExtendService.findByUserId(partyId);
        AtomicReference<BigDecimal> walletExtendMoneyRef = new AtomicReference<>(BigDecimal.ZERO);
        walletExtends.forEach(f -> {
@@ -107,8 +107,8 @@
        Map<String, BigDecimal> assets =  new HashMap<>();
        assets.put("contract",wallet.getMoney().setScale(2,RoundingMode.DOWN));
        assets.put("capital",capitaltWallet.getMoney().add(walletExtendMoney).setScale(2,RoundingMode.DOWN));
        assets.put("capitalUSDT",capitaltWallet.getMoney());//划转专用
//        assets.put("capital",capitaltWallet.getMoney().add(walletExtendMoney).setScale(2,RoundingMode.DOWN));
//        assets.put("capitalUSDT",capitaltWallet.getMoney());//划转专用
        return Result.succeed(assets);
    }
}
trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiWalletController.java
@@ -215,13 +215,13 @@
        df2.setRoundingMode(RoundingMode.FLOOR);
        // String partyId ="dcc0dd35a49c383dadabc4dc030afe70";
        String partyId = SecurityUtils.getCurrentUserId();
        CapitaltWallet usdt = null;
        Wallet usdt = null;
        if (StringUtils.isNotEmpty(partyId)) {
//            usdt = this.walletService.saveWalletByPartyId(partyId);
            usdt = capitaltWalletService.getUserIdWallet(partyId);
            usdt = this.walletService.saveWalletByPartyId(partyId);
//            usdt = capitaltWalletService.getUserIdWallet(partyId);
        }
        if (null == usdt) {
            usdt = new CapitaltWallet();
            usdt = new Wallet();
            usdt.setMoney(new BigDecimal(0));
            usdt.setLockMoney(new BigDecimal(0));
            usdt.setFreezeMoney(new BigDecimal(0));
trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiWithdrawController.java
@@ -3,6 +3,7 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yami.trading.bean.model.CapitaltWallet;
import com.yami.trading.bean.model.User;
import com.yami.trading.bean.model.Wallet;
import com.yami.trading.bean.model.Withdraw;
import com.yami.trading.common.constants.Constants;
import com.yami.trading.common.domain.Result;
@@ -13,6 +14,7 @@
import com.yami.trading.security.common.util.SecurityUtils;
import com.yami.trading.service.CapitaltWalletService;
import com.yami.trading.service.SessionTokenService;
import com.yami.trading.service.WalletService;
import com.yami.trading.service.WithdrawService;
import com.yami.trading.service.syspara.SysparaService;
import com.yami.trading.service.user.UserService;
@@ -50,6 +52,8 @@
    protected WalletLogService walletLogService;
    @Autowired
    CapitaltWalletService capitaltWalletService;
    @Autowired
    WalletService walletService;
    /**
     * 首次进入页面,传递session_token
@@ -111,9 +115,14 @@
        // 获取资金账户(capital)
        if(channel.contains("USDT")){
            CapitaltWallet capitaltWallet = capitaltWalletService.getOne(new LambdaQueryWrapper<>(CapitaltWallet.class)
                    .eq(CapitaltWallet::getUserId, partyId).last(" limit 1 "));
            if(capitaltWallet.getMoney().compareTo(new BigDecimal(amount)) < 0){
//            CapitaltWallet capitaltWallet = capitaltWalletService.getOne(new LambdaQueryWrapper<>(CapitaltWallet.class)
//                    .eq(CapitaltWallet::getUserId, partyId).last(" limit 1 "));
//            if(capitaltWallet.getMoney().compareTo(new BigDecimal(amount)) < 0){
//                throw new YamiShopBindException("Insufficient available balance for withdrawal!");
//            }
            Wallet wallet = walletService.getOne(new LambdaQueryWrapper<>(Wallet.class)
                    .eq(Wallet::getUserId, partyId).last(" limit 1 "));
            if(wallet.getMoney().compareTo(new BigDecimal(amount)) < 0){
                throw new YamiShopBindException("Insufficient available balance for withdrawal!");
            }
        }
trading-order-admin/src/main/java/com/yami/trading/api/controller/exchange/ApiExchangeApplyOrderController.java
@@ -109,13 +109,14 @@
        Map<String, Object> parities = parities(symbol, symbol_to, volume_temp);
        Object getVolume = parities.get("get_volume");
        if(symbol.equals("usdt")){
            CapitaltWallet userIdWallet = capitaltWalletService.getUserIdWallet(partyId);
            Wallet userIdWallet = walletService.saveWalletByPartyId(partyId);
//            CapitaltWallet userIdWallet = capitaltWalletService.getUserIdWallet(partyId);
            if(userIdWallet.getMoney().compareTo(new BigDecimal(volume_temp)) < 0){
                throw new YamiShopBindException("not sufficient funds");
            }
            Double value = Double.valueOf(volume_temp);
            capitaltWalletService.update(userIdWallet,  -value);
//            capitaltWalletService.update(userIdWallet,  -value);
            walletService.update(userIdWallet.getUserId(),  -value);
            WalletExtend walletExtend = walletExtendService.getOne(new LambdaQueryWrapper<>(WalletExtend.class)
                    .eq(WalletExtend::getWallettype, symbol_to)
                            .eq(WalletExtend::getPartyId,partyId)
@@ -147,8 +148,10 @@
            walletExtendService.update(walletExtend,new LambdaUpdateWrapper<WalletExtend>().eq(UUIDEntity::getUuid,walletExtend.getUuid()));
            CapitaltWallet userIdWallet = capitaltWalletService.getUserIdWallet(partyId);
            capitaltWalletService.update(userIdWallet,Double.valueOf(getVolume.toString()));
//            CapitaltWallet userIdWallet = capitaltWalletService.getUserIdWallet(partyId);
//            capitaltWalletService.update(userIdWallet,Double.valueOf(getVolume.toString()));
            Wallet userIdWallet = walletService.saveWalletByPartyId(partyId);
            walletService.update(userIdWallet.getUserId(),Double.valueOf(getVolume.toString()));
        }else{
            WalletExtend walletExtend = walletExtendService.getOne(new LambdaQueryWrapper<>(WalletExtend.class)
                    .eq(WalletExtend::getWallettype, symbol)
@@ -374,12 +377,12 @@
    public Result openview() {
        Map<String, Object> data = new HashMap<String, Object>();
        String partyId = SecurityUtils.getUser().getUserId();
//        Wallet wallet = walletService.saveWalletByPartyId(partyId);
        CapitaltWallet userIdWallet = capitaltWalletService.getUserIdWallet(partyId);
        Wallet wallet = walletService.saveWalletByPartyId(partyId);
//        CapitaltWallet userIdWallet = capitaltWalletService.getUserIdWallet(partyId);
        // 账户剩余资金
        DecimalFormat df = new DecimalFormat("#.##");
        df.setRoundingMode(RoundingMode.FLOOR);// 向下取整
        data.put("volume", df.format(userIdWallet.getMoney()));
        data.put("volume", df.format(wallet.getMoney()));
        String session_token = sessionTokenService.savePut(partyId);
        data.put("session_token", session_token);
        data.put("fee", sysparaService.find("exchange_apply_order_buy_fee").getSvalue());
trading-order-admin/src/main/resources/application-prod.yml
@@ -49,4 +49,4 @@
        maxIdle: 20
        maxTotal: 50
      host: 127.0.0.1
      port: 6379
      port: 6380
trading-order-admin/src/main/resources/redisson/redisson-dev.yml
@@ -1,6 +1,6 @@
# 单节点设置
singleServerConfig:
  address: redis://127.0.0.1:6379
  address: redis://127.0.0.1:6380
  database: 0
  password:
  idleConnectionTimeout: 10000
trading-order-bean/src/main/java/com/yami/trading/bean/item/domain/Item.java
@@ -30,7 +30,7 @@
     */
    public final static String forex = "forex";
    /**
     * 指数
     * etf
     */
    public final static String indices = "indices";
trading-order-huobi/src/main/java/com.yami.trading.huobi/data/job/ETFDataSynchronizationJob.java
New file
@@ -0,0 +1,272 @@
package com.yami.trading.huobi.data.job;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yami.trading.bean.item.domain.Item;
import com.yami.trading.huobi.data.model.ETFData;
import com.yami.trading.huobi.data.model.ETFResponse;
import com.yami.trading.huobi.data.model.RealTimeData;
import com.yami.trading.huobi.data.model.RealTimeResponse;
import com.yami.trading.service.item.ItemService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
/**
 * @program: trading-order-master
 * @description:
 * @create: 2025-09-28 11:20
 **/
@Slf4j
@Component
public class ETFDataSynchronizationJob {
    @Autowired
    private ItemService itemService;
    @Autowired
    private RestTemplate restTemplate;
    private static final String LIST_API_URL = "https://www.tsanghi.com/api/fin/etf/XNAS/list?token=9668db3503214cd19a831a9f866923b9";
    private static final String REALTIME_API_URL = "https://www.tsanghi.com/api/fin/etf/XNAS/realtime?token=9668db3503214cd19a831a9f866923b9&ticker=";
    /**
     * 同步ETF数据定时任务
     */
//    @Scheduled(cron = "0 * * * * ?")
    public void syncETFData() {
        try {
            log.info("开始同步ETF数据...");
            // 获取ETF列表
            List<ETFData> etfList = getETFList();
            if (etfList == null || etfList.isEmpty()) {
                log.warn("未获取到ETF列表数据");
                return;
            }
            log.info("共获取到{}个ETF数据", etfList.size());
            int successCount = 0;
            int errorCount = 0;
            // 处理每个ETF
            for (ETFData etf : etfList) {
                try {
                    if (processETFItem(etf)) {
                        successCount++;
                    } else {
                        errorCount++;
                    }
                } catch (Exception e) {
                    log.error("处理ETF {} 时发生错误: {}", etf.getTicker(), e.getMessage());
                    errorCount++;
                }
            }
            log.info("ETF数据同步完成: 成功 {}, 失败 {}", successCount, errorCount);
        } catch (Exception e) {
            log.error("同步ETF数据时发生错误: {}", e.getMessage(), e);
        }
    }
    /**
     * 获取ETF列表
     */
    private List<ETFData> getETFList() {
        try {
            ResponseEntity<ETFResponse> response = restTemplate.exchange(
                    LIST_API_URL,
                    HttpMethod.GET,
                    null,
                    ETFResponse.class
            );
            if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
                ETFResponse apiResponse = response.getBody();
                if (apiResponse.getCode() == 200) {
                    return apiResponse.getData();
                } else {
                    log.error("API返回错误: {} - {}", apiResponse.getCode(), apiResponse.getMsg());
                }
            }
        } catch (Exception e) {
            log.error("获取ETF列表失败: {}", e.getMessage(), e);
        }
        return null;
    }
    /**
     * 处理单个ETF项目
     */
    private boolean processETFItem(ETFData etf) {
        // 只处理活跃的ETF
        if (etf.getIsActive() == null || etf.getIsActive() != 1) {
            log.info("跳过非活跃ETF: {}", etf.getTicker());
            return false;
        }
        // 检查是否已存在
        Item existingItem = itemService.getOne(new LambdaQueryWrapper<Item>()
                .eq(Item::getSymbol,etf.getTicker())
        );
        if (existingItem != null) {
            log.info("ETF {} 已存在,跳过", etf.getTicker());
            return true;
        }
        // 获取实时数据计算小数位
        RealTimeData realtimeData = getRealtimeData(etf.getTicker());
        if (realtimeData == null) {
            log.warn("无法获取ETF {} 的实时数据", etf.getTicker());
            return false;
        }
        // 创建Item对象
        Item item = createItemFromETFData(etf, realtimeData);
        // 保存到数据库
            return itemService.save(item);
    }
    /**
     * 获取实时数据
     */
    private RealTimeData getRealtimeData(String ticker) {
        try {
            String url = REALTIME_API_URL + ticker;
            ResponseEntity<RealTimeResponse> response = restTemplate.exchange(
                    url,
                    HttpMethod.GET,
                    null,
                    RealTimeResponse.class
            );
            if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
                RealTimeResponse apiResponse = response.getBody();
                if (apiResponse.getCode() == 200) {
                    return apiResponse.getData().get(0);
                }
            }
        } catch (Exception e) {
            log.error("获取ETF {} 实时数据失败: {}", ticker, e.getMessage());
        }
        return null;
    }
    /**
     * 根据收盘价计算小数位数和PIPS
     */
    private ETFDecimalInfo calculateDecimalInfo(BigDecimal closePrice) {
        ETFDecimalInfo info = new ETFDecimalInfo();
        if (closePrice == null) {
            // 默认值
            info.setDecimals(2);
            info.setPips(new BigDecimal("0.01"));
            info.setPipsAmount(new BigDecimal("0.01"));
            return info;
        }
        try {
            String priceStr = closePrice.stripTrailingZeros().toPlainString();
            int decimalPlaces = 0;
            // 查找小数点位置
            int dotIndex = priceStr.indexOf('.');
            if (dotIndex != -1) {
                // 计算实际的小数位数(去除末尾的0)
                decimalPlaces = priceStr.length() - dotIndex - 1;
            }
            // 设置小数位数,至少为2位
            int decimals = Math.max(decimalPlaces, 2);
            info.setDecimals(decimals);
            // 计算PIPS(最小价格单位)
            BigDecimal pips = BigDecimal.ONE.divide(
                    BigDecimal.TEN.pow(decimals),
                    decimals,
                    RoundingMode.HALF_UP
            );
            info.setPips(pips);
            info.setPipsAmount(pips);
        } catch (Exception e) {
            log.error("计算小数位信息失败,使用默认值", e);
            info.setDecimals(2);
            info.setPips(new BigDecimal("0.01"));
            info.setPipsAmount(new BigDecimal("0.01"));
        }
        return info;
    }
    /**
     * 创建Item对象
     */
    private Item createItemFromETFData(ETFData etf, RealTimeData realtimeData) {
        Item item = new Item();
        // 生成UUID
        String uuid = UUID.randomUUID().toString().replace("-", "");
        item.setUuid(uuid);
        // 基础信息
        item.setName(etf.getName());
        item.setEnName(etf.getName());
        item.setSymbol(etf.getTicker());
        item.setSymbolData(etf.getTicker());
        item.setSymbolFullName(etf.getName());
        // 计算小数位信息
        item.setDecimals(2);
        item.setPips(new BigDecimal("0.01"));
        item.setPipsAmount(new BigDecimal("0.01"));
        // 固定值设置
        item.setAdjustmentValue(BigDecimal.ZERO);
        item.setUnitAmount(new BigDecimal("1000"));
        item.setUnitFee(new BigDecimal("30"));
        item.setMarket("indices");
        item.setMultiple(BigDecimal.ZERO);
        item.setBorrowingRate(BigDecimal.ZERO);
        item.setType("indices");
        item.setCategory("Miscellaneous");
        item.setSorted("100");
        item.setOpenCloseType("indices");
        item.setFake("0");
        item.setShowStatus("1");
        item.setTradeStatus("1");
        item.setQuoteCurrency("USDT");
        item.setFaceValue(0.01);
        // 创建时间和更新时间
        item.setCreateTime(new Date());
        item.setUpdateTime(new Date());
        return item;
    }
    /**
     * 小数位信息辅助类
     */
    @Data
    private static class ETFDecimalInfo {
        private Integer decimals;
        private BigDecimal pips;
        private BigDecimal pipsAmount;
    }
}
trading-order-huobi/src/main/java/com.yami.trading.huobi/data/job/StockGetDataJob.java
@@ -2,9 +2,11 @@
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yami.trading.bean.data.domain.Realtime;
import com.yami.trading.bean.data.domain.StockMarket;
import com.yami.trading.bean.item.domain.Item;
import com.yami.trading.common.domain.BaseEntity;
import com.yami.trading.common.util.*;
import com.yami.trading.huobi.data.AdjustmentValueCache;
import com.yami.trading.huobi.data.DataCache;
@@ -64,23 +66,24 @@
        }
        while (true) {
            try {
                List<Item> list = itemService.list().stream().filter(i->"0".equalsIgnoreCase(i.getFake())).collect(Collectors.toList());
                List<Item> list = itemService.list(new LambdaQueryWrapper<>(Item.class).eq(BaseEntity::getDelFlag,0)).stream().filter(i->"0".equalsIgnoreCase(i.getFake())).collect(Collectors.toList());
                // etf 和A股开盘时间是一样的
                String aStocSymbols = list.stream().filter(item ->item.getOpenCloseType() != null &&  item.getOpenCloseType().equalsIgnoreCase(Item.A_STOCKS))
                        .map(Item::getSymbol).collect(Collectors.joining(","));
                String hkStocSymbols = list.stream().filter(item -> item.getOpenCloseType() != null &&item.getOpenCloseType().equalsIgnoreCase(Item.HK_STOCKS)).map(Item::getSymbol).collect(Collectors.joining(","));
//                String aStocSymbols = list.stream().filter(item ->item.getOpenCloseType() != null &&  item.getOpenCloseType().equalsIgnoreCase(Item.A_STOCKS))
//                        .map(Item::getSymbol).collect(Collectors.joining(","));
//                String hkStocSymbols = list.stream().filter(item -> item.getOpenCloseType() != null &&item.getOpenCloseType().equalsIgnoreCase(Item.HK_STOCKS)).map(Item::getSymbol).collect(Collectors.joining(","));
                String usStocSymbols = list.stream().filter(item -> item.getOpenCloseType() != null &&item.getOpenCloseType().equalsIgnoreCase(Item.US_STOCKS)).map(Item::getSymbol).collect(Collectors.joining(","));
                String indicesymbols = list.stream().filter(item -> item.getOpenCloseType() != null &&item.getOpenCloseType().equalsIgnoreCase(Item.indices)).map(Item::getSymbol).collect(Collectors.joining(","));
                if(stockFirstFetch){
                    this.realtimeHandle(aStocSymbols);
                    this.realtimeHandle(hkStocSymbols);
//                    this.realtimeHandle(aStocSymbols);
                    this.realtimeHandle(indicesymbols);
                    this.realtimeHandle(usStocSymbols);
                    stockFirstFetch = false;
                }
                if(MarketOpenChecker.isMarketOpen(Item.A_STOCKS)){
                    this.realtimeHandle(aStocSymbols);
                }
                if(MarketOpenChecker.isMarketOpen(Item.HK_STOCKS)){
                    this.realtimeHandle(hkStocSymbols);
//                if(MarketOpenChecker.isMarketOpen(Item.A_STOCKS)){
//                    this.realtimeHandle(aStocSymbols);
//                }
                if(MarketOpenChecker.isMarketOpen(Item.indices)){
                    this.realtimeHandle(indicesymbols);
                }
                if(MarketOpenChecker.isMarketOpen(Item.US_STOCKS)){
                    this.realtimeHandle(usStocSymbols);
trading-order-huobi/src/main/java/com.yami.trading.huobi/data/job/StockGetMarketJob.java
@@ -58,10 +58,10 @@
            try {
                List<Item> list = itemService.list().stream().filter(i -> "0".equalsIgnoreCase(i.getFake())).collect(Collectors.toList());
                // etf 和A股开盘时间是一样的
                String aStocSymbols = list.stream().filter(item -> item.getOpenCloseType() != null && item.getOpenCloseType().equalsIgnoreCase(Item.A_STOCKS)).map(Item::getSymbol).collect(Collectors.joining(","));
                String hkStocSymbols = list.stream().filter(item -> item.getOpenCloseType() != null && item.getOpenCloseType().equalsIgnoreCase(Item.HK_STOCKS)).map(Item::getSymbol).collect(Collectors.joining(","));
//                String aStocSymbols = list.stream().filter(item -> item.getOpenCloseType() != null && item.getOpenCloseType().equalsIgnoreCase(Item.A_STOCKS)).map(Item::getSymbol).collect(Collectors.joining(","));
//                String hkStocSymbols = list.stream().filter(item -> item.getOpenCloseType() != null && item.getOpenCloseType().equalsIgnoreCase(Item.HK_STOCKS)).map(Item::getSymbol).collect(Collectors.joining(","));
                String usStocSymbols = list.stream().filter(item -> item.getOpenCloseType() != null && item.getOpenCloseType().equalsIgnoreCase(Item.US_STOCKS)).map(Item::getSymbol).collect(Collectors.joining(","));
                String symbols = aStocSymbols + "," + hkStocSymbols + "," + usStocSymbols;
                String symbols = usStocSymbols;
                List<StockMarket> markets = xueQiuDataService.getMarkets(symbols);
                markets.forEach(m -> DataCache.putMarket(m.getSymbol(), m));
trading-order-huobi/src/main/java/com.yami.trading.huobi/data/model/ETFData.java
New file
@@ -0,0 +1,30 @@
package com.yami.trading.huobi.data.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
 * @program: trading-order-master
 * @description:
 * @create: 2025-09-28 14:16
 **/
@Data
public class ETFData {
    @JsonProperty("ticker")
    private String ticker;
    @JsonProperty("name")
    private String name;
    @JsonProperty("is_active")
    private Integer isActive;
    @JsonProperty("exchange_code")
    private String exchangeCode;
    @JsonProperty("country_code")
    private String countryCode;
    @JsonProperty("currency_code")
    private String currencyCode;
}
trading-order-huobi/src/main/java/com.yami.trading.huobi/data/model/ETFResponse.java
New file
@@ -0,0 +1,19 @@
package com.yami.trading.huobi.data.model;
import lombok.Data;
import java.util.List;
/**
 * @program: trading-order-master
 * @description:
 * @create: 2025-09-28 14:18
 **/
@Data
public class ETFResponse {
    private String msg;
    private Integer code;
    private List<ETFData> data;
}
trading-order-huobi/src/main/java/com.yami.trading.huobi/data/model/RealTimeData.java
New file
@@ -0,0 +1,35 @@
package com.yami.trading.huobi.data.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
 * @program: trading-order-master
 * @description:
 * @create: 2025-09-28 14:18
 **/
@Data
public class RealTimeData {
    @JsonProperty("ticker")
    private String ticker;
    @JsonProperty("close")
    private Double close;
    @JsonProperty("date")
    private String date;
    @JsonProperty("open")
    private Double open;
    @JsonProperty("high")
    private Double high;
    @JsonProperty("low")
    private Double low;
    @JsonProperty("volume")
    private Double volume;
}
trading-order-huobi/src/main/java/com.yami.trading.huobi/data/model/RealTimeResponse.java
New file
@@ -0,0 +1,19 @@
package com.yami.trading.huobi.data.model;
import lombok.Data;
import java.util.List;
/**
 * @program: trading-order-master
 * @description:
 * @create: 2025-09-28 14:19
 **/
@Data
public class RealTimeResponse {
    private String msg;
    private Integer code;
    private List<RealTimeData> data;
}
trading-order-huobi/src/main/java/com.yami.trading.huobi/hobi/internal/XinLangDataServiceImpl.java
@@ -65,17 +65,7 @@
    private ItemService itemService;
    public static void main(String[] args) {
        XinLangDataServiceImpl service = new XinLangDataServiceImpl();
        List<Realtime> usdsgd = service.realtimeSingle("USDSGD");
        for (Realtime re : usdsgd) {
            System.out.println(JSONObject.toJSONString(re));
        }
//        List<Kline> sz300750 = service.buildOneYearPeriod("AAPL");
//        System.out.println(sz300750.size());
//        List<Kline> eurusd = service.getTimeseriesByPeriodOneDay("EURUSD");
//        System.out.println(JSONObject.toJSONString(eurusd));
    }
    /**
@@ -142,10 +132,10 @@
        return list;
    }
    public List<Realtime> realtime(String symbols) {
        List<Realtime> realtimeList = realtimeSingle(symbols);
        return realtimeList;
    }
//    public List<Realtime> realtime(String symbols) {
//        List<Realtime> realtimeList = realtimeSingle(symbols);
//        return realtimeList;
//    }
    /**
     * 1day       历史数据  : 周期 1年
trading-order-huobi/src/main/java/com.yami.trading.huobi/hobi/internal/XueQiuDataServiceImpl.java
@@ -7,6 +7,7 @@
import com.google.common.collect.Lists;
import com.yami.trading.bean.cms.Infomation;
import com.yami.trading.bean.data.domain.*;
import com.yami.trading.bean.item.domain.Item;
import com.yami.trading.common.util.RedisUtil;
import com.yami.trading.huobi.data.DataCache;
import com.yami.trading.huobi.data.internal.DepthTimeObject;
@@ -16,6 +17,7 @@
import com.yami.trading.huobi.hobi.http.HttpMethodType;
import com.yami.trading.service.cms.InfomationService;
import com.yami.trading.service.item.ItemService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
@@ -27,6 +29,7 @@
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -36,6 +39,7 @@
 * 完成需求k线图采集
 */
@Component
@Slf4j
public class XueQiuDataServiceImpl {
    //   https://stock.xueqiu.com/v5/stock/chart/kline.json?symbol=TSLA&begin=1682695800000&period=120m&type=before&count=-500&indicator=kline";
    public final static String klineUrl = "https://stock.xueqiu.com/v5/stock/chart/kline.json?symbol={}&begin={}&period={}&type=before&count=-500&indicator=kline";
@@ -45,7 +49,7 @@
    /**
     * live
     */
    public final static String live = "https://stock.xueqiu.com/v5/stock/quote.json";
    public final static String live = "https://www.tsanghi.com/api/fin/";
    public final static String markets = "https://stock.xueqiu.com/v5/stock/quote.json";
    public final static String pankou = "https://stock.xueqiu.com/v5/stock/realtime/pankou.json";
@@ -322,55 +326,164 @@
    }
    public List<Realtime> realtimeSingle(String symbols) {
        List<Realtime> list = new ArrayList<Realtime>();
        List<Realtime> list = new ArrayList<>();
        try {
            List<String> strings = Arrays.asList(symbols.split(","));
            String cookie = HttpHelper.getCookie("https://xueqiu.com/");
            for (String symbol: strings) {
                String result = HttpHelper.sendGetHttp(live, "symbol=" + symbol, cookie);
                JSONObject resultJson = JSON.parseObject(result);
                String code = resultJson.getString("error_code");
                if ("0".equals(code)) {
                    JSONObject jsonObject = resultJson.getJSONObject("data").getJSONObject("quote");
                    Realtime realtime = new Realtime();
                    String currency;
                    currency = symbol;
                    int decimal = itemService.getDecimal(currency);
                    realtime.setSymbol(currency);
                    realtime.setName(currency);
                    Long timestamp = jsonObject.getLong("timestamp");
                    if (timestamp.toString().length() > 13) {
                        timestamp = timestamp / 1000;
            List<String> symbolList = Arrays.asList(symbols.split(","));
            for (String symbol : symbolList) {
                try {
                    Item item = itemService.findBySymbol(symbol);
                    if (item == null) {
                        log.warn("未找到对应的item: {}", symbol);
                        continue;
                    }
                    realtime.setTs(timestamp);
                    realtime.setOpen(jsonObject.getBigDecimal("open").setScale(decimal, RoundingMode.HALF_UP));
                    realtime.setClose(jsonObject.getBigDecimal("current").setScale(decimal, RoundingMode.HALF_UP));
                    realtime.setHigh(jsonObject.getBigDecimal("high").setScale(decimal, RoundingMode.HALF_UP));
                    realtime.setLow(jsonObject.getBigDecimal("low").setScale(decimal, RoundingMode.HALF_UP));
                    realtime.setMarketCapital(jsonObject.getLong("market_capital"));
                    realtime.setFloatMarketCapital(jsonObject.getLong("float_market_capital"));
                    realtime.setPeForecast(jsonObject.getBigDecimal("pe_forecast"));
                    realtime.setVolumeRatio(jsonObject.getBigDecimal("volume_ratio"));
                    realtime.setTurnoverRate(jsonObject.getBigDecimal("turnover_rate"));
                    BigDecimal amount = jsonObject.getBigDecimal("amount");
                    if (amount == null) {
                        amount = BigDecimal.ZERO;
                    String type = "";
                    String exchange = "";
                    // 根据类型确定API路径和交易所
                    if ("US-stocks".equals(item.getType())) {
                        type = "stock";
                        exchange = "XNAS";  // 美股纳斯达克
                    } else if ("indices".equals(item.getType()) || "ETF".equals(item.getType())) {
                        type = "etf";
                        exchange = "XNAS";  // ETF也在纳斯达克
                    } else {
                        // 其他类型,默认为股票和上交所
                        type = "stock";
                        exchange = "XSHG";
                    }
                    realtime.setAmount(amount.setScale(decimal, RoundingMode.HALF_UP));
                    BigDecimal volume = jsonObject.getBigDecimal("volume");
                    if (volume == null) {
                        volume = BigDecimal.ZERO;
                    // 构建API URL
                    String url = String.format("https://www.tsanghi.com/api/fin/%s/%s/realtime?token=9668db3503214cd19a831a9f866923b9&ticker=%s",
                            type, exchange, symbol);
                    String result = HttpHelper.sendGetHttp(url, null, null);
                    JSONObject resultJson = JSON.parseObject(result);
                    String code = resultJson.getString("code");
                    if ("200".equals(code)) {
                        JSONArray dataArray = resultJson.getJSONArray("data");
                        // 检查数据是否为空
                        if (dataArray == null || dataArray.isEmpty()) {
                            log.warn("股票 {} 的实时数据为空", symbol);
                            continue;
                        }
                        // 取第一个数据对象
                        JSONObject dataObject = dataArray.getJSONObject(0);
                        Realtime realtime = new Realtime();
                        int decimal = itemService.getDecimal(symbol);
                        realtime.setSymbol(symbol);
                        realtime.setName(item.getName() != null ? item.getName() : symbol);
                        // 处理时间戳
                        String dateStr = dataObject.getString("date");
                        long timestamp = parseDateTimeToTimestamp(dateStr);
                        if (Long.toString(timestamp).length() > 13) {
                            timestamp = timestamp / 1000;
                        }
                        realtime.setTs(timestamp);
                        // 设置价格数据
                        realtime.setOpen(getBigDecimalValue(dataObject, "open", decimal));
                        realtime.setClose(getBigDecimalValue(dataObject, "close", decimal));
                        realtime.setHigh(getBigDecimalValue(dataObject, "high", decimal));
                        realtime.setLow(getBigDecimalValue(dataObject, "low", decimal));
                        // 设置成交量和成交额
                        realtime.setVolume(getBigDecimalValue(dataObject, "volume", decimal));
                        realtime.setAmount(getBigDecimalValue(dataObject, "amount", decimal));
                        // 设置昨收价
                        BigDecimal preClose = getBigDecimalValue(dataObject, "pre_close", decimal);
                        if (preClose != null) {
                            // 如果Realtime类有preClose字段,取消注释
                            // realtime.setPreClose(preClose);
                        }
                        // 处理盘口数据(如果存在)
                        JSONArray buyPriceArray = dataObject.getJSONArray("buy_price");
                        JSONArray sellPriceArray = dataObject.getJSONArray("sell_price");
                        JSONArray buyVolumeArray = dataObject.getJSONArray("buy_volume");
                        JSONArray sellVolumeArray = dataObject.getJSONArray("sell_volume");
                        if (buyPriceArray != null && !buyPriceArray.isEmpty()) {
                            realtime.setBid(getBigDecimalFromArray(buyPriceArray, 0, decimal));
                        }
                        if (sellPriceArray != null && !sellPriceArray.isEmpty()) {
                            realtime.setAsk(getBigDecimalFromArray(sellPriceArray, 0, decimal));
                        }
                        list.add(realtime);
                    } else {
                        log.warn("API返回错误代码: {}, 股票: {}", code, symbol);
                    }
                    realtime.setVolume(volume.setScale(decimal, RoundingMode.HALF_UP));
//                    realtime.setAsk(realtimeJson.getBigDecimal("ask").setScale(decimal, RoundingMode.HALF_UP));
//                    realtime.setBid(realtimeJson.getBigDecimal("pb").setScale(decimal, RoundingMode.HALF_UP));
                    list.add(realtime);
                } catch (Exception e) {
                    log.error("处理股票 {} 时发生错误", symbol, e);
                }
            }
        } catch (Exception e) {
            logger.error("error", e);
            log.error("获取实时数据失败", e);
        }
        return list;
    }
    /**
     * 将日期时间字符串转换为时间戳
     * 格式: "yyyy-mm-dd hh:mm:ss"
     */
    private long parseDateTimeToTimestamp(String dateTimeStr) {
        try {
            if (dateTimeStr == null || dateTimeStr.isEmpty()) {
                return System.currentTimeMillis();
            }
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date date = sdf.parse(dateTimeStr);
            return date.getTime();
        } catch (Exception e) {
            log.error("日期时间解析失败: {}", dateTimeStr, e);
            return System.currentTimeMillis();
        }
    }
    /**
     * 安全获取BigDecimal值
     */
    private BigDecimal getBigDecimalValue(JSONObject jsonObject, String key, int decimal) {
        try {
            BigDecimal value = jsonObject.getBigDecimal(key);
            if (value == null) {
                return BigDecimal.ZERO;
            }
            return value.setScale(decimal, RoundingMode.HALF_UP);
        } catch (Exception e) {
            log.warn("获取字段 {} 失败", key, e);
            return BigDecimal.ZERO;
        }
    }
    /**
     * 从JSONArray中获取BigDecimal值
     */
    private BigDecimal getBigDecimalFromArray(JSONArray jsonArray, int index, int decimal) {
        try {
            if (jsonArray == null || jsonArray.size() <= index) {
                return BigDecimal.ZERO;
            }
            BigDecimal value = jsonArray.getBigDecimal(index);
            if (value == null) {
                return BigDecimal.ZERO;
            }
            return value.setScale(decimal, RoundingMode.HALF_UP);
        } catch (Exception e) {
            log.warn("从数组获取数据失败", e);
            return BigDecimal.ZERO;
        }
    }
    public List<Realtime> realtime(String symbols) {
@@ -389,25 +502,25 @@
        map.put(Kline.PERIOD_1MON, buildOneMonthPeriod(symbol));
        map.put(Kline.PERIOD_1DAY, buildOneDayPeriod(symbol));
        map.put(Kline.PERIOD_5DAY, buildFiveDayPeriod(symbol));
        map.put(Kline.PERIOD_QUARTER, buildOneQuarterPeriod(symbol));
//        map.put(Kline.PERIOD_QUARTER, buildOneQuarterPeriod(symbol));
        map.put(Kline.PERIOD_YEAR, buildOneYearPeriod(symbol));
        return map;
    }
    public List<Kline> buildOneDayPeriod(String currency) {
        return getTimeseriesByPeriod(currency, "day", Kline.PERIOD_1DAY, 365);
        return getTimeseriesByPeriod(currency, "daily", Kline.PERIOD_1DAY, 365);
    }
    public List<Kline> buildOneWeekPeriod(String currency) {
        return getTimeseriesByPeriod(currency, "week", Kline.PERIOD_1WEEK, 365 * 5);
        return getTimeseriesByPeriod(currency, "weekly", Kline.PERIOD_1WEEK, 365 * 5);
    }
    public List<Kline> buildOneMonthPeriod(String currency) {
        return getTimeseriesByPeriod(currency, "month", Kline.PERIOD_1MON, 365 * 5);
        return getTimeseriesByPeriod(currency, "monthly", Kline.PERIOD_1MON, 365 * 5);
    }
    public List<Kline> buildOneQuarterPeriod(String currency) {
@@ -415,7 +528,7 @@
    }
    public List<Kline> buildOneYearPeriod(String currency) {
        return getTimeseriesByPeriod(currency, "year", Kline.PERIOD_YEAR, 365 * 100);
        return getTimeseriesByPeriod(currency, "yearly", Kline.PERIOD_YEAR, 365 * 100);
    }
    /**
@@ -588,64 +701,144 @@
    public List<Kline> getTimeseriesByPeriod(String currency, String periodXieQiu, String sysPeriod, long limitDays) {
        List<Kline> resList = new ArrayList<>();
        long nowTs = System.currentTimeMillis();
        long startTs = System.currentTimeMillis() - limitDays * 24 * 60 * 60 * 1000;
        long begin = nowTs;
        String cookie = HttpHelper.getCookie("https://xueqiu.com/");
        Set<Long> tsSet = new HashSet<>();
        while (begin > startTs) {
            String url = StrUtil.format(klineUrl, currency, begin, periodXieQiu);
            String json = HttpHelper.sendGetHttp(url, null, cookie);
        try {
            // 获取商品信息判断类型
            Item itemData = itemService.findBySymbol(currency);
            String type = "stock"; // 默认股票
            String exchange = "XNAS"; // 默认纳斯达克
            if (itemData != null) {
                if ("ETF".equals(itemData.getType()) || "etf".equals(itemData.getType()) ||
                        "indices".equals(itemData.getType()) || currency.startsWith("51") ||
                        currency.startsWith("15") || currency.startsWith("16")) {
                    type = "etf";
                    // ETF可能在不同的交易所,根据实际情况调整
                    exchange = "XNAS"; // 或者可能是XSHG等其他交易所
                } else if ("US-stocks".equals(itemData.getType())) {
                    type = "stock";
                    exchange = "XNAS";
                } else {
                    // 其他类型默认为A股股票
                    type = "stock";
                    exchange = "XSHG";
                }
            } else {
                // 如果没有找到item信息,根据symbol特征猜测类型
                if (currency.startsWith("51") || currency.startsWith("15") || currency.startsWith("16")) {
                    type = "etf";
                    exchange = "XSHG"; // A股ETF
                } else if (currency.matches("[A-Z]+")) {
                    type = "stock";
                    exchange = "XNAS"; // 美股
                } else {
                    type = "stock";
                    exchange = "XSHG"; // 默认A股
                }
            }
            log.debug("获取K线数据,symbol: {}, 类型: {}, 交易所: {}, 周期: {}",
                    currency, type, exchange, periodXieQiu);
            // 构建API URL
            String url = StrUtil.format("https://www.tsanghi.com/api/fin/{}/{}/{}?token=9668db3503214cd19a831a9f866923b9&ticker={}&order=2",
                    type, exchange, periodXieQiu, currency);
            // 发送HTTP请求
            String json = HttpHelper.sendGetHttp(url, null, null);
            JSONObject resultJson = JSON.parseObject(json);
            JSONArray dataArray = resultJson.getJSONObject("data").getJSONArray("item");
            List<List> lists = dataArray.toJavaList(List.class);
            long minTS = begin;
            for (List result : lists) {
            // 检查API响应是否成功
            if (resultJson == null || !resultJson.containsKey("data")) {
                log.error("API响应数据格式异常: {}", json);
                return resList;
            }
            JSONArray dataArray = resultJson.getJSONArray("data");
            if (dataArray == null || dataArray.isEmpty()) {
                log.warn("未获取到K线数据");
                return resList;
            }
            // 计算时间范围
            long endTime = System.currentTimeMillis();
            long startTime = endTime - limitDays * 24 * 60 * 60 * 1000L;
            Set<String> dateSet = new HashSet<>();
            // 解析数据
            for (int i = 0; i < dataArray.size(); i++) {
                JSONObject item = dataArray.getJSONObject(i);
                // 解析日期
                String dateStr = item.getString("date");
                if (dateSet.contains(dateStr)) {
                    continue;
                }
                dateSet.add(dateStr);
                // 将日期转换为时间戳
                long ts = parseDateToTimestamp(dateStr);
                // 检查时间范围
                if (ts < startTime) {
                    continue;
                }
                Kline kline = new Kline();
                kline.setSymbol(currency);
                kline.setPeriod(sysPeriod);
                // 毫秒
                long ts = Long.parseLong(result.get(0).toString());
                if (Long.toString(ts).length() > 13) {
                    ts = ts / 1000;
                }
                if (tsSet.contains(ts)) {
                    continue;
                } else {
                    tsSet.add(ts);
                }
                kline.setTs(ts);
                kline.setOpen(new BigDecimal(result.get(2).toString()));
                kline.setClose(new BigDecimal(result.get(5).toString()));
                kline.setHigh(new BigDecimal(result.get(3).toString()));
                kline.setLow(new BigDecimal(result.get(4).toString()));
                kline.setVolume(new BigDecimal(result.get(1).toString()));
                kline.setAmount(new BigDecimal(result.get(9).toString()));
                kline.setOpen(item.getBigDecimal("open"));
                kline.setClose(item.getBigDecimal("close"));
                kline.setHigh(item.getBigDecimal("high"));
                kline.setLow(item.getBigDecimal("low"));
                kline.setVolume(item.getBigDecimal("volume"));
                // 如果有amount字段就设置,没有就设为0
                if (item.containsKey("amount")) {
                    kline.setAmount(item.getBigDecimal("amount"));
                } else {
                    kline.setAmount(BigDecimal.ZERO);
                }
                // 修复K线数据(如果有修复逻辑)
                if (klineService != null) {
                    klineService.repairKline(kline);
                }
                resList.add(kline);
                if (ts < minTS) {
                    minTS = ts;
                }
            }
            // 按时间戳升序排序
            Collections.sort(resList);
            // 如果需要设置开盘价为前一根的收盘价
            int len = resList.size();
            for (int i = 1; i < len; i++) {
                resList.get(i).setOpen(resList.get(i - 1).getClose());
            }
            if (minTS == begin) {
                break;
            }
            begin = minTS;
            if (begin < startTs) {
                break;
            }
        } catch (Exception e) {
            log.error("获取K线数据失败: {}", currency, e);
        }
        Collections.sort(resList);
        int len = resList.size();
        for (int i = 1; i < len; i++) {
            resList.get(i).setOpen(resList.get(i - 1).getClose());
        }
        return resList;
    }
    /**
     * 将日期字符串转换为时间戳
     * 格式: "yyyy-mm-dd"
     */
    private long parseDateToTimestamp(String dateStr) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            Date date = sdf.parse(dateStr);
            return date.getTime();
        } catch (Exception e) {
            log.error("日期解析失败: {}", dateStr, e);
            return System.currentTimeMillis();
        }
    }
    public List<Kline> getTimeseriesByMinute(String currency, int minute, long limitDays) {
trading-order-service/src/main/java/com/yami/trading/service/exchange/impl/ExchangeApplyOrderServiceImpl.java
@@ -111,11 +111,11 @@
        double amount = Arith.mul(sub, realtime.getClose().doubleValue());
        order.setCloseTime(new Date());
        order.setClosePrice(realtime.getClose().doubleValue());
//        Wallet wallet = this.walletService.saveWalletByPartyId(order.getPartyId());
        CapitaltWallet capitaltWallet = capitaltWalletService.getUserIdWallet(order.getPartyId());
        double amount_before = capitaltWallet.getMoney().doubleValue();
//        this.walletService.update(userIdWallet.getUserId().toString(), amount);
        this.capitaltWalletService.update(capitaltWallet, amount);
        Wallet wallet = this.walletService.saveWalletByPartyId(order.getPartyId());
//        CapitaltWallet capitaltWallet = capitaltWalletService.getUserIdWallet(order.getPartyId());
        double amount_before = wallet.getMoney().doubleValue();
        this.walletService.update(wallet.getUserId().toString(), amount);
//        this.capitaltWalletService.update(capitaltWallet, amount);
        /*
         * 保存资金日志
@@ -124,7 +124,7 @@
        moneylog_deposit.setCategory(Constants.MONEYLOG_CATEGORY_EXCHANGE);
        moneylog_deposit.setAmountBefore(new BigDecimal(amount_before));
        moneylog_deposit.setAmount(new BigDecimal(amount));
        moneylog_deposit.setAmountAfter(capitaltWallet.getMoney());
        moneylog_deposit.setAmountAfter(wallet.getMoney());
        moneylog_deposit.setLog("委托单,订单号[" + order.getOrderNo() + "]");
        moneylog_deposit.setUserId(order.getPartyId());
        moneylog_deposit.setWalletType(Constants.WALLET);
@@ -615,8 +615,8 @@
        // 可以买的数量
        double amount = Arith.div(sub, order.getClosePrice(), 8);
        order.setSymbolValue(amount);
//        Wallet wallet = this.walletService.saveWalletByPartyId(order.getPartyId());
        CapitaltWallet userIdWallet = capitaltWalletService.getUserIdWallet(order.getPartyId());
        Wallet userIdWallet = this.walletService.saveWalletByPartyId(order.getPartyId());
//        CapitaltWallet userIdWallet = capitaltWalletService.getUserIdWallet(order.getPartyId());
        double amount_before = userIdWallet.getMoney().doubleValue();
        // 如果是计划委托,则先不扣钱
        if (order.isTriggerOrder()) {
@@ -643,8 +643,8 @@
            if (userIdWallet.getMoney().doubleValue() < order.getVolume().doubleValue()) {
                throw new YamiShopBindException("余额不足");
            }
//            this.walletService.update(userIdWallet.getUserId().toString(), Arith.sub(0, order.getVolume()));
            capitaltWalletService.update(userIdWallet, Arith.sub(0, order.getVolume()));
            this.walletService.update(userIdWallet.getUserId().toString(), Arith.sub(0, order.getVolume()));
//            capitaltWalletService.update(userIdWallet, Arith.sub(0, order.getVolume()));
            /*
             * 保存资金日志
             */
trading-order-service/src/main/java/com/yami/trading/service/impl/RechargeBlockchainOrderServiceImpl.java
@@ -135,10 +135,15 @@
//
//            walletService.update(wallet.getUserId(), amount1);
            CapitaltWallet capitaltWallet = capitaltWalletService.getOne(new LambdaQueryWrapper<>(CapitaltWallet.class)
                    .eq(CapitaltWallet::getUserId, recharge.getPartyId()).last(" limit 1 "));
            double amount_before = capitaltWallet.getMoney().doubleValue();
            capitaltWalletService.update(capitaltWallet,amount1);
//            CapitaltWallet capitaltWallet = capitaltWalletService.getOne(new LambdaQueryWrapper<>(CapitaltWallet.class)
//                    .eq(CapitaltWallet::getUserId, recharge.getPartyId()).last(" limit 1 "));
//            double amount_before = capitaltWallet.getMoney().doubleValue();
//            capitaltWalletService.update(capitaltWallet,amount1);
            Wallet wallet = walletService.getOne(new LambdaQueryWrapper<>(Wallet.class)
                    .eq(Wallet::getUserId, recharge.getPartyId()).last(" limit 1 "));
            double amount_before = wallet.getMoney().doubleValue();
            walletService.update(wallet.getUserId(),amount1);
            // 保存资金日志
            MoneyLog moneyLog = new MoneyLog();
trading-order-service/src/main/java/com/yami/trading/service/impl/WithdrawServiceImpl.java
@@ -151,14 +151,24 @@
//            walletService.update(wallet.getUserId().toString(),
//                    Arith.add(withdraw.getAmount(), withdraw.getAmountFee()));
            CapitaltWallet capitaltWallet = capitaltWalletService.getOne(new LambdaQueryWrapper<>(CapitaltWallet.class)
                    .eq(CapitaltWallet::getUserId, wallet.getUserId().toString()).last(" limit 1 "));
            if(ObjectUtil.isEmpty(capitaltWallet)){
//            CapitaltWallet capitaltWallet = capitaltWalletService.getOne(new LambdaQueryWrapper<>(CapitaltWallet.class)
//                    .eq(CapitaltWallet::getUserId, wallet.getUserId().toString()).last(" limit 1 "));
//            if(ObjectUtil.isEmpty(capitaltWallet)){
//                throw new YamiShopBindException("用户资金账户不存在!");
//            }
//            capitaltWalletService.update(new LambdaUpdateWrapper<CapitaltWallet>()
//                    .set(CapitaltWallet::getMoney,new BigDecimal(Arith.add(capitaltWallet.getMoney(), withdraw.getVolume())))
//                    .eq(CapitaltWallet::getUserId,wallet.getUserId()));
            Wallet wallet1 = walletService.getOne(new LambdaQueryWrapper<>(Wallet.class)
                    .eq(Wallet::getUserId, wallet.getUserId().toString()).last(" limit 1 "));
            if(ObjectUtil.isEmpty(wallet1)){
                throw new YamiShopBindException("用户资金账户不存在!");
            }
            capitaltWalletService.update(new LambdaUpdateWrapper<CapitaltWallet>()
                    .set(CapitaltWallet::getMoney,new BigDecimal(Arith.add(capitaltWallet.getMoney(), withdraw.getVolume())))
                    .eq(CapitaltWallet::getUserId,wallet.getUserId()));
            walletService.update(new LambdaUpdateWrapper<Wallet>()
                    .set(Wallet::getMoney,new BigDecimal(Arith.add(wallet1.getMoney(), withdraw.getVolume())))
                    .eq(Wallet::getUserId,wallet.getUserId()));
            /*
@@ -482,11 +492,11 @@
            throw new YamiShopBindException("Your account has been frozen");
        }
//        Wallet wallet = walletService.saveWalletByPartyId(withdraw.getUserId());
        Wallet wallet = walletService.saveWalletByPartyId(withdraw.getUserId());
        CapitaltWallet capitaltWallet = capitaltWalletService.getOne(new LambdaQueryWrapper<>(CapitaltWallet.class)
                .eq(CapitaltWallet::getUserId, withdraw.getUserId()).last(" limit 1 "));
        if (capitaltWallet.getMoney().doubleValue() < withdraw.getVolume().doubleValue()) {
//        CapitaltWallet capitaltWallet = capitaltWalletService.getOne(new LambdaQueryWrapper<>(CapitaltWallet.class)
//                .eq(CapitaltWallet::getUserId, withdraw.getUserId()).last(" limit 1 "));
        if (wallet.getMoney().doubleValue() < withdraw.getVolume().doubleValue()) {
            throw new YamiShopBindException("not sufficient funds");
        }
        // 手续费(USDT)
@@ -671,17 +681,17 @@
         */
        String withdraw_qr = qRGenerateService.generateWithdraw(withdraw.getOrderNo(), withdraw.getAddress());
        withdraw.setQdcode(withdraw_qr);
        double amount_before = capitaltWallet.getMoney().doubleValue();
        double amount_before = wallet.getMoney().doubleValue();
//        walletService.update(wallet.getUserId().toString(), Arith.sub(0, withdraw.getVolume().doubleValue()));
        if(ObjectUtil.isEmpty(capitaltWallet)){
        if(ObjectUtil.isEmpty(wallet)){
            throw new YamiShopBindException("The user's funds account does not exist!");
        }
        capitaltWalletService.update(capitaltWallet,-withdraw.getVolume().doubleValue());
        walletService.update(wallet.getUserId(),-withdraw.getVolume().doubleValue());
        save(withdraw);
@@ -692,7 +702,7 @@
        moneyLog.setCategory(Constants.MONEYLOG_CATEGORY_COIN);
        moneyLog.setAmountBefore(new BigDecimal(amount_before));
        moneyLog.setAmount(new BigDecimal(Arith.sub(0, withdraw.getVolume().doubleValue())));
        moneyLog.setAmountAfter(capitaltWallet.getMoney());
        moneyLog.setAmountAfter(wallet.getMoney());
        moneyLog.setLog("提现订单[" + withdraw.getOrderNo() + "]");
        // moneyLog.setExtra(withdraw.getOrder_no());
        moneyLog.setUserId(withdraw.getUserId());
trading-order-service/src/main/java/com/yami/trading/service/item/ItemService.java
@@ -162,7 +162,7 @@
    @Cached(name = ITEM_CACHE, key = "'all'", expire = 3600)
    @Override
    public List<Item> list() {
        List<Item> list = super.list(new LambdaQueryWrapper<>(Item.class).eq(Item::getType,Item.cryptos));
        List<Item> list = super.list(new LambdaQueryWrapper<>(Item.class).eq(Item::getDelFlag,0));
        symbolDecimal = list.stream()
                .collect(Collectors.toMap(Item::getSymbol, Item::getDecimals, (s1, s2) -> s2));
        return list;
trading-order-service/src/main/java/com/yami/trading/service/user/impl/UserStatisticsServiceImpl.java
@@ -118,8 +118,10 @@
        result.add(0,data);
        Map<String,Object> capitaldata = new HashMap<String,Object>();
        CapitaltWallet capitaltWallet = capitaltWalletService.getOne(new LambdaQueryWrapper<>(CapitaltWallet.class)
                .eq(CapitaltWallet::getUserId, targetPartyId).last(" limit 1 "));
//        CapitaltWallet capitaltWallet = capitaltWalletService.getOne(new LambdaQueryWrapper<>(CapitaltWallet.class)
//                .eq(CapitaltWallet::getUserId, targetPartyId).last(" limit 1 "));
        Wallet capitaltWallet = walletService.getOne(new LambdaQueryWrapper<>(Wallet.class)
                .eq(Wallet::getUserId, targetPartyId).last(" limit 1 "));
        capitaldata.put("wallettype", "capitalusdt");
        capitaldata.put("amount",null==capitaltWallet?0:new BigDecimal(capitaltWallet.getMoney().doubleValue()).setScale(8, RoundingMode.FLOOR).toPlainString() );
        capitaldata.put("lock_amount",null==capitaltWallet?0:capitaltWallet.getLockMoney().setScale(8, RoundingMode.FLOOR).toPlainString() );