From decfc2ff7244cec365d3f32a5090edf5faeae861 Mon Sep 17 00:00:00 2001
From: zj <1772600164@qq.com>
Date: Wed, 23 Apr 2025 19:08:18 +0800
Subject: [PATCH] 1

---
 src/main/java/com/nq/controller/StockIndexApiController.java   |   64 +++++
 src/main/java/com/nq/service/impl/UserAssetsServices.java      |  145 +++++++++++
 src/main/java/com/nq/service/impl/StockServiceImpl.java        |  201 +++++++++++++---
 src/main/java/com/nq/controller/StockApiController.java        |   15 +
 src/main/java/com/nq/controller/protol/UserController.java     |   26 ++
 src/main/java/com/nq/utils/task/stock/StockTask.java           |   80 ++++++
 src/main/java/com/nq/service/impl/UserPositionServiceImpl.java |   85 +++++++
 src/main/java/com/nq/service/IUserPositionService.java         |    2 
 src/main/java/com/nq/utils/redis/RedisKeyConstant.java         |    9 
 src/main/java/com/nq/utils/translate/GoogleTranslateUtil.java  |    2 
 src/main/java/com/nq/service/impl/UserServiceImpl.java         |    9 
 src/main/resources/application.properties                      |    2 
 src/main/java/com/nq/service/IUserAssetsServices.java          |    6 
 src/main/java/com/nq/controller/backend/AdminController.java   |   10 
 src/main/java/com/nq/service/impl/StockIndexServiceImpl.java   |   33 +-
 src/main/java/com/nq/vo/stockindex/StockIndexVO.java           |    1 
 16 files changed, 627 insertions(+), 63 deletions(-)

diff --git a/src/main/java/com/nq/controller/StockApiController.java b/src/main/java/com/nq/controller/StockApiController.java
index d265e7a..33fe10f 100644
--- a/src/main/java/com/nq/controller/StockApiController.java
+++ b/src/main/java/com/nq/controller/StockApiController.java
@@ -4,6 +4,9 @@
 import com.nq.pojo.SiteSetting;
 import com.nq.service.IStockService;
 import com.nq.service.StockDzService;
+import com.nq.utils.redis.RedisKeyConstant;
+import com.nq.utils.redis.RedisShardedPoolUtils;
+import com.nq.utils.translate.GoogleTranslateUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -14,6 +17,7 @@
 import org.springframework.web.bind.annotation.ResponseBody;
 
 import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
 
 @Controller
 @RequestMapping({"/api/stock/"})
@@ -52,6 +56,17 @@
         return this.iStockService.getStockByType(pageNum, pageSize, orderBy,keyWords , stockType, request);
     }
 
+    @RequestMapping("getGoldCrudeOil.do")
+    @ResponseBody
+    public ServerResponse getGoldCrudeOil(HttpServletRequest request)   {
+        String gold = RedisShardedPoolUtils.get(RedisKeyConstant.gold);
+        String crudeOil = RedisShardedPoolUtils.get(RedisKeyConstant.crude_oil);
+        HashMap<String,String> map = new HashMap<>();
+        map.put(new GoogleTranslateUtil().translate("GOLD",request.getHeader("lang")),gold);
+        map.put(new GoogleTranslateUtil().translate("CRUDE OIL",request.getHeader("lang")),crudeOil);
+        return ServerResponse.createBySuccess(map);
+    }
+
 
 
     @RequestMapping("getOptionStock.do")
diff --git a/src/main/java/com/nq/controller/StockIndexApiController.java b/src/main/java/com/nq/controller/StockIndexApiController.java
new file mode 100644
index 0000000..81c35ae
--- /dev/null
+++ b/src/main/java/com/nq/controller/StockIndexApiController.java
@@ -0,0 +1,64 @@
+package com.nq.controller;
+
+
+import com.nq.common.ServerResponse;
+import com.nq.service.IStockIndexService;
+import com.nq.vo.stock.MarketVO;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+
+
+@Controller
+@RequestMapping({"/api/index/"})
+public class StockIndexApiController {
+    private static final Logger log = LoggerFactory.getLogger(StockIndexApiController.class);
+
+    @Autowired
+    IStockIndexService iStockIndexService;
+
+
+    @RequestMapping({"queryHomeIndex.do"})
+    @ResponseBody
+    public ServerResponse queryHomeIndex() {
+        return this.iStockIndexService.queryHomeIndex();
+    }
+    //查询指数信息
+    @RequestMapping({"queryListIndex.do"})
+    @ResponseBody
+    public ServerResponse queryListIndex(HttpServletRequest request) {
+        return this.iStockIndexService.queryListIndex(request);
+    }
+    //查询固定指数信息
+    @RequestMapping({"queryListIndexByCode.do"})
+    @ResponseBody
+    public ServerResponse queryListIndexByCode(@RequestParam List<String> code) {
+        return this.iStockIndexService.queryListIndexByCode(code);
+    }
+
+    @RequestMapping({"queryTransIndex.do"})
+    @ResponseBody
+    public ServerResponse queryTransIndex(@RequestParam("indexId") Integer indexId) {
+        return this.iStockIndexService.queryTransIndex(indexId);
+    }
+
+    @RequestMapping({"querySingleIndex.do"})
+    @ResponseBody
+    public ServerResponse querySingleIndex(@RequestParam("indexCode") String indexCode) {
+        MarketVO marketVO = this.iStockIndexService.querySingleIndex(indexCode);
+        return ServerResponse.createBySuccess(marketVO);
+    }
+    //指数新闻
+    @RequestMapping({"queryIndexNews.do"})
+    @ResponseBody
+    public ServerResponse queryIndexNews() {
+        return this.iStockIndexService.queryIndexNews();
+    }
+}
diff --git a/src/main/java/com/nq/controller/backend/AdminController.java b/src/main/java/com/nq/controller/backend/AdminController.java
index aecd92a..49dd07e 100644
--- a/src/main/java/com/nq/controller/backend/AdminController.java
+++ b/src/main/java/com/nq/controller/backend/AdminController.java
@@ -384,4 +384,14 @@
         return  iUserAssetsServices.updateUserAssets(id,amt,type);
     }
 
+    @RequestMapping("transfer.do")
+    @ResponseBody
+    public  ServerResponse transfer(
+            @RequestParam("userId") Integer userId,
+            @RequestParam("disbursementAccount") String disbursementAccount,
+            @RequestParam("depositAccount") String depositAccount,
+            @RequestParam("amt") String amt, HttpServletRequest request
+    ){
+        return  iUserAssetsServices.transfer(userId,disbursementAccount,depositAccount,amt,request);
+    }
 }
diff --git a/src/main/java/com/nq/controller/protol/UserController.java b/src/main/java/com/nq/controller/protol/UserController.java
index 0b720f5..e49f34a 100644
--- a/src/main/java/com/nq/controller/protol/UserController.java
+++ b/src/main/java/com/nq/controller/protol/UserController.java
@@ -130,6 +130,32 @@
             buyOrderCreated.set(false);
         }
     }
+
+    //用户下单买入股票
+    @RequestMapping({"goldCrudeOilbuy.do"})
+    @ResponseBody
+    public ServerResponse goldCrudeOilbuy(@RequestParam("name") String name,
+                              @RequestParam("buyNum") Integer buyNum,
+                              @RequestParam("buyType") Integer buyType,
+                              @RequestParam("lever") Integer lever,
+                              @RequestParam(value = "profitTarget",required = false)
+                              BigDecimal profitTarget,@RequestParam(value = "stopLoss",required = false) BigDecimal stopLoss, HttpServletRequest request) {
+        buyLock.lock();
+        try {
+            if (buyOrderCreated.get()) {
+                return ServerResponse.createByErrorMsg("当前下单人数过多,请稍后重试", request);
+            }
+            buyOrderCreated.set(true);
+            return this.iUserPositionService.goldCrudeOilbuy(name, buyNum,lever,profitTarget,stopLoss, request);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return ServerResponse.createByErrorMsg("订单异常,请稍后重试", request);
+        }  finally{
+            buyLock.unlock();
+            buyOrderCreated.set(false);
+        }
+    }
+
     //修改涨跌板
     @RequestMapping({"updateProfitTarget.do"})
     @ResponseBody
diff --git a/src/main/java/com/nq/service/IUserAssetsServices.java b/src/main/java/com/nq/service/IUserAssetsServices.java
index ae7475a..98a3ebe 100644
--- a/src/main/java/com/nq/service/IUserAssetsServices.java
+++ b/src/main/java/com/nq/service/IUserAssetsServices.java
@@ -1,14 +1,15 @@
 package com.nq.service;
 
+import com.baomidou.mybatisplus.extension.service.IService;
 import com.nq.common.ServerResponse;
 import com.nq.enums.EUserAssets;
-import com.nq.pojo.Bank;
 import com.nq.pojo.UserAssets;
 
+import javax.servlet.http.HttpServletRequest;
 import java.math.BigDecimal;
 import java.util.List;
 
-public interface IUserAssetsServices {
+public interface IUserAssetsServices extends IService<UserAssets> {
 
 
 
@@ -48,4 +49,5 @@
     Boolean availablebalanceChange(String accetType, Integer userId, EUserAssets eUserAssets, BigDecimal amount, String desc, String descType);
 
 
+    ServerResponse transfer(Integer userId, String disbursementAccount, String depositAccount, String amt, HttpServletRequest request);
 }
diff --git a/src/main/java/com/nq/service/IUserPositionService.java b/src/main/java/com/nq/service/IUserPositionService.java
index 34ddad4..936134d 100644
--- a/src/main/java/com/nq/service/IUserPositionService.java
+++ b/src/main/java/com/nq/service/IUserPositionService.java
@@ -85,4 +85,6 @@
   ServerResponse buyStockDzList(HttpServletRequest request);
 
   void stockConstraint(List<UserPosition> userPositions);
+
+  ServerResponse goldCrudeOilbuy(String name, Integer buyNum, Integer lever, BigDecimal profitTarget, BigDecimal stopTarget, HttpServletRequest requestrequest);
 }
diff --git a/src/main/java/com/nq/service/impl/StockIndexServiceImpl.java b/src/main/java/com/nq/service/impl/StockIndexServiceImpl.java
index 75b0b29..763b801 100644
--- a/src/main/java/com/nq/service/impl/StockIndexServiceImpl.java
+++ b/src/main/java/com/nq/service/impl/StockIndexServiceImpl.java
@@ -81,11 +81,13 @@
 
 
         MarketVO marketVO = querySingleIndex(stockIndex.getIndexGid());
+        if(null != marketVO){
+            stockIndexVO.setCurrentPoint(marketVO.getNowPrice());
+            stockIndexVO.setFloatPoint(marketVO.getIncrease());
+            stockIndexVO.setFloatRate(marketVO.getIncreaseRate());
+            stockIndexVO.setType(marketVO.getType());
+        }
 
-        stockIndexVO.setCurrentPoint(marketVO.getNowPrice());
-        stockIndexVO.setFloatPoint(marketVO.getIncrease());
-        stockIndexVO.setFloatRate(marketVO.getIncreaseRate());
-        stockIndexVO.setType(marketVO.getType());
         return stockIndexVO;
     }
 
@@ -195,14 +197,21 @@
     public MarketVO querySingleIndex(String indexCode) {
         MarketVO marketVO = null;
         if (indexCode.contains("hk")||indexCode.contains("us")){
-            String index = RedisShardedPoolUtils.get(indexCode, 3);
-            JSONObject jsonObject = JSONObject.parseObject(index);
-            marketVO = new MarketVO();
-            marketVO.setName(jsonObject.getString("f14"));
-            marketVO.setNowPrice(jsonObject.getString("f2"));
-            marketVO.setIncrease(jsonObject.getString("f4"));
-            marketVO.setIncreaseRate(jsonObject.getString("f3"));
-            marketVO.setType(jsonObject.getString("f13"));
+//            String index = RedisShardedPoolUtils.get(indexCode, 3);
+//            JSONObject jsonObject = JSONObject.parseObject(index);
+//            marketVO = new MarketVO();
+//            if(jsonObject.containsKey("f14") &&
+//                    jsonObject.containsKey("f2") &&
+//                    jsonObject.containsKey("f4") &&
+//                    jsonObject.containsKey("f3") &&
+//                    jsonObject.containsKey("f13")){
+//                marketVO.setName(jsonObject.getString("f14"));
+//                marketVO.setNowPrice(jsonObject.getString("f2"));
+//                marketVO.setIncrease(jsonObject.getString("f4"));
+//                marketVO.setIncreaseRate(jsonObject.getString("f3"));
+//                marketVO.setType(jsonObject.getString("f13"));
+//            }
+
         }else {
             String market_url = PropertiesUtil.getProperty("sina.single.market.url");
 
diff --git a/src/main/java/com/nq/service/impl/StockServiceImpl.java b/src/main/java/com/nq/service/impl/StockServiceImpl.java
index 7b896d8..d6f7d9e 100644
--- a/src/main/java/com/nq/service/impl/StockServiceImpl.java
+++ b/src/main/java/com/nq/service/impl/StockServiceImpl.java
@@ -21,6 +21,7 @@
 import com.nq.service.*;
 import com.nq.utils.http.HttpClientRequest;
 import com.nq.utils.PropertiesUtil;
+import com.nq.utils.redis.RedisKeyConstant;
 import com.nq.utils.redis.RedisKeyUtil;
 import com.nq.utils.redis.RedisShardedPoolUtils;
 import com.nq.utils.stock.pinyin.GetPyByChinese;
@@ -38,6 +39,11 @@
 import java.net.HttpURLConnection;
 import java.net.URL;
 import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
 import java.util.*;
 import java.util.concurrent.CompletableFuture;
 import javax.annotation.Resource;
@@ -53,6 +59,7 @@
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
 
 import static com.nq.utils.timeutil.DateTimeUtil.getWeekDay;
 
@@ -190,33 +197,52 @@
 
 
     public ServerResponse getSingleStock(String code, HttpServletRequest request) {
-        if (StringUtils.isBlank(code))
-            return ServerResponse.createByErrorMsg("");
-        Stock stock = stockMapper.findStockByCode(code);
-        DataStockBean cacheBaseStock = RedisKeyUtil.getCacheBaseStock(stock);
-        Integer depositAmt = 0;
-        String introduction = null;
-        StockVO   stockVO = StockApi.assembleInStockVO(stock);
-        stockVO.setDepositAmt(depositAmt);
-        stockVO.setNowPrice(iPriceServices.getNowPrice(stock.getStockCode()).toString());
-        stockVO.setType(stock.getStockType());
-        stockVO.setId(stock.getId().intValue());
-        stockVO.setCode(stock.getStockCode());
-        stockVO.setName(stock.getStockName());
-        stockVO.setSpell(stock.getStockSpell());
-        stockVO.setGid(stock.getStockGid().toUpperCase());
-        Map<String, Object> newStock = iPriceServices.getNewStock(stock.getStockCode());
-        stockVO.setHcrate(newStock.get("ChgPct").toString().replaceAll("%", ""));
-        stockVO.setToday_max(newStock.get("High").toString());
-        stockVO.setToday_min(newStock.get("Low").toString());
-        if(null != cacheBaseStock){
-            stockVO.setOpen_px(cacheBaseStock.getOpen());
-            stockVO.setPreclose_px(cacheBaseStock.getPrevClose());
+
+        if(code.equals("GOLD")){
+            String price =  RedisShardedPoolUtils.get(RedisKeyConstant.gold);
+            StockVO stockVO = new StockVO();
+            stockVO.setNowPrice(price);
+            stockVO.setName(code);
+            Map map = Maps.newHashMap();
+            map.put("stock", stockVO);
+            return ServerResponse.createBySuccess(map);
+        }else if(code.equals("CRUDE OIL")){
+            String price = RedisShardedPoolUtils.get(RedisKeyConstant.crude_oil);
+            StockVO stockVO = new StockVO();
+            stockVO.setNowPrice(price);
+            stockVO.setName(code);
+            Map map = Maps.newHashMap();
+            map.put("stock", stockVO);
+            return ServerResponse.createBySuccess(map);
+        }else{
+            if (StringUtils.isBlank(code))
+                return ServerResponse.createByErrorMsg("");
+            Stock stock = stockMapper.findStockByCode(code);
+            DataStockBean cacheBaseStock = RedisKeyUtil.getCacheBaseStock(stock);
+            Integer depositAmt = 0;
+            String introduction = null;
+            StockVO   stockVO = StockApi.assembleInStockVO(stock);
+            stockVO.setDepositAmt(depositAmt);
+            stockVO.setNowPrice(iPriceServices.getNowPrice(stock.getStockCode()).toString());
+            stockVO.setType(stock.getStockType());
+            stockVO.setId(stock.getId().intValue());
+            stockVO.setCode(stock.getStockCode());
+            stockVO.setName(stock.getStockName());
+            stockVO.setSpell(stock.getStockSpell());
+            stockVO.setGid(stock.getStockGid().toUpperCase());
+            Map<String, Object> newStock = iPriceServices.getNewStock(stock.getStockCode());
+            stockVO.setHcrate(newStock.get("ChgPct").toString().replaceAll("%", ""));
+            stockVO.setToday_max(newStock.get("High").toString());
+            stockVO.setToday_min(newStock.get("Low").toString());
+            if(null != cacheBaseStock){
+                stockVO.setOpen_px(cacheBaseStock.getOpen());
+                stockVO.setPreclose_px(cacheBaseStock.getPrevClose());
+            }
+            Map map = Maps.newHashMap();
+            map.put("introduction", introduction);
+            map.put("stock", stockVO);
+            return ServerResponse.createBySuccess(map);
         }
-        Map map = Maps.newHashMap();
-        map.put("introduction", introduction);
-        map.put("stock", stockVO);
-        return ServerResponse.createBySuccess(map);
     }
 
     public Map getSingleStock(String code) {
@@ -432,25 +458,118 @@
     }
 
 
+    //黄金
+    private static final String gold_API_URL = "http://139.196.211.109/ldMetal_k.action?username=Qq112233&password=3ce25a66d5b3a8cd661024fea6c79388&id=AUUSDO&num=-100&period=";
+
+    //原油
+    private static final String crude_oil_API_URL = "http://47.112.169.122/fOption_k.action?username=Qq112233&password=3ce25a66d5b3a8cd661024fea6c79388&id=@CL0W&num=-100&period=";
+
         /*股票日线-K线*/
     @Override
     public Object getKData(String pid, String interval, String stockType) {
-        EStockType eStockType = EStockType.getEStockTypeByCode(stockType);
-        Object object = HttpUtil.get(eStockType.stockUrl + "kline?pid=" + pid + "&interval=" + interval + "&key=" + eStockType.stockKey);
-        Gson gson = new Gson();
-        List<kData> dataList = gson.fromJson(object.toString(), new TypeToken<List<kData>>(){}.getType());
+        if(stockType.equals("GOLD")){
+            // 使用RestTemplate发起HTTP请求
+            String response = RedisShardedPoolUtils.get("k_gold_"+interval.toLowerCase());
+            return parseData(interval,response, stockType);
+        }else if(stockType.equals("CRUDE OIL")){
+            // 使用RestTemplate发起HTTP请求
+            RestTemplate restTemplate = new RestTemplate();
+            String response = RedisShardedPoolUtils.get("k_crude_oil_"+interval.toLowerCase());
+            return parseData(interval,response, stockType);
+        }else{
+            EStockType eStockType = EStockType.getEStockTypeByCode(stockType);
+            Object object = HttpUtil.get(eStockType.stockUrl + "kline?pid=" + pid + "&interval=" + interval + "&key=" + eStockType.stockKey);
+            Gson gson = new Gson();
+            List<kData> dataList = gson.fromJson(object.toString(), new TypeToken<List<kData>>(){}.getType());
 
-        Stock stock = stockMapper.selectOne(new LambdaQueryWrapper<Stock>().eq(Stock::getStockCode, pid).eq(Stock::getStockType, EStockType.ST.getCode()));
-        BigDecimal nowPrice = iPriceServices.getNowPrice(stock.getStockCode());
-        Map singleStock = getSingleStock(stock.getStockCode());
-        StockVO stockVO = (StockVO)singleStock.get("stock");
-        // 修改 List 中的最后一条数据
-        kData lastData = dataList.get(dataList.size() - 1);
-        lastData.setC(nowPrice.toString());
-        lastData.setO(stockVO.getOpen_px());
-        lastData.setH(stockVO.getToday_max());
-        lastData.setL(stockVO.getToday_min());
-        return gson.toJson(dataList);
+            Stock stock = stockMapper.selectOne(new LambdaQueryWrapper<Stock>().eq(Stock::getStockCode, pid).eq(Stock::getStockType, EStockType.ST.getCode()));
+            BigDecimal nowPrice = iPriceServices.getNowPrice(stock.getStockCode());
+            Map singleStock = getSingleStock(stock.getStockCode());
+            StockVO stockVO = (StockVO)singleStock.get("stock");
+            // 修改 List 中的最后一条数据
+            kData lastData = dataList.get(dataList.size() - 1);
+            lastData.setC(nowPrice.toString());
+            lastData.setO(stockVO.getOpen_px());
+            lastData.setH(stockVO.getToday_max());
+            lastData.setL(stockVO.getToday_min());
+            return gson.toJson(dataList);
+        }
+    }
+
+    public List<kData> parseData(String interval,String data,String key) {
+        String price = null;
+        if(key.equals("GOLD")){
+            price =  RedisShardedPoolUtils.get(RedisKeyConstant.gold);
+        }else if(key.equals("CRUDE OIL")){
+            price = RedisShardedPoolUtils.get(RedisKeyConstant.crude_oil);
+        }
+        List<kData> kDataList = new ArrayList<>();
+
+        // 将数据按行分割
+        String[] lines = data.split("\n");
+
+        // 跳过第一行(列名)
+        for (int i = 1; i < lines.length; i++) {
+            String line = lines[i].trim();
+            // 确保行不为空
+            if (!line.isEmpty()) {
+                String[] fields = line.split(",");
+
+                // 确保每行有至少7个字段:日期、开盘价、最高价、最低价、收盘价、成交量、持仓量
+                if ((key.equals("CRUDE OIL") && fields.length == 7) || (key.equals("GOLD") && fields.length == 5)) {
+                    kData kData = new kData();
+                    kData.setT(convDate(fields[0],interval));
+                    kData.setC(price);
+                    kData.setO(fields[1]);
+                    kData.setH(fields[2]);
+                    kData.setL(fields[3]);
+                    if(key.equals("CRUDE OIL")){
+                        kData.setV(fields[4]);
+                        kData.setVo(fields[5]);
+                    }else{
+                        kData.setV("0");
+                        kData.setVo("0");
+                    }
+                    // 将每一条 KData 对象添加到列表中
+                    kDataList.add(kData);
+                }
+            }
+        }
+        return kDataList;
+    }
+
+
+    // 常量,定义日期格式
+    private static final String DATE_PATTERN = "yyyyMMdd";
+    private static final String DATE_TIME_PATTERN = "yyyyMMdd HH:mm";
+
+    public long convDate(String dateStr, String interval) {
+        // 输入校验,确保 dateStr 和 interval 不为 null 或空
+        if (dateStr == null || dateStr.trim().isEmpty() || interval == null || interval.trim().isEmpty()) {
+            throw new IllegalArgumentException("日期字符串或时间间隔不能为空。");
+        }
+
+        // 根据 interval 的值,选择日期格式
+        String pattern = (interval.equalsIgnoreCase("D") || interval.equalsIgnoreCase("W") || interval.equalsIgnoreCase("M"))
+                ? DATE_PATTERN : DATE_TIME_PATTERN;
+
+        // 创建 DateTimeFormatter,使用合适的格式
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
+
+        try {
+            // 根据是否包含时间部分,选择 LocalDate 或 LocalDateTime 进行解析
+            if (pattern.equals(DATE_PATTERN)) {
+                LocalDate localDate = LocalDate.parse(dateStr, formatter);
+                Instant timestamp = localDate.atStartOfDay(ZoneOffset.UTC).toInstant();
+                return timestamp.getEpochSecond();
+            } else {
+                LocalDateTime localDateTime = LocalDateTime.parse(dateStr, formatter);
+                Instant timestamp = localDateTime.toInstant(ZoneOffset.UTC);
+                return timestamp.getEpochSecond();
+            }
+        } catch (Exception e) {
+            throw new IllegalArgumentException("无效的日期格式: " + dateStr, e);
+        }
     }
 
     @Override
diff --git a/src/main/java/com/nq/service/impl/UserAssetsServices.java b/src/main/java/com/nq/service/impl/UserAssetsServices.java
index e6fb068..88ec26c 100644
--- a/src/main/java/com/nq/service/impl/UserAssetsServices.java
+++ b/src/main/java/com/nq/service/impl/UserAssetsServices.java
@@ -1,8 +1,9 @@
 package com.nq.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.nq.common.ServerResponse;
 import com.nq.dao.*;
 import com.nq.enums.EStockType;
@@ -13,13 +14,13 @@
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
-import javax.validation.constraints.Email;
+import javax.servlet.http.HttpServletRequest;
 import java.math.BigDecimal;
-import java.util.Date;
-import java.util.List;
-import java.util.Objects;
+import java.util.*;
+import java.util.function.Function;
 
 /**
  * 用户资产
@@ -259,6 +260,95 @@
         return userAssetsMapper.updateById(userAssets)>1;
     }
 
+    @Override
+    @Transactional
+    public ServerResponse transfer(Integer userId, String disbursementAccount, String depositAccount, String amt, HttpServletRequest request) {
+        // 参数校验
+        ServerResponse paramCheckResponse = checkParameters(userId, disbursementAccount, depositAccount, amt, request);
+        if (paramCheckResponse != null) {
+            return paramCheckResponse;
+        }
+
+        // 转账金额合法性检查
+        BigDecimal transferAmount = validateTransferAmount(amt, request);
+        if (transferAmount == null) {
+            return ServerResponse.createByErrorMsg("无效的转账金额", request);
+        }
+
+        // 查询转出账户和转入账户的资产信息
+        UserAssets disbursement = getUserAssets(userId, disbursementAccount);
+        UserAssets deposit = getUserAssets(userId, depositAccount);
+
+        // 账户检查
+        if (disbursement == null) {
+            return ServerResponse.createByErrorMsg("转出账户不存在", request);
+        }
+        if (deposit == null) {
+            return ServerResponse.createByErrorMsg("转入账户不存在", request);
+        }
+
+        // 检查转出账户余额是否足够
+        if (isBalanceSufficient(disbursement.getAvailableBalance(), transferAmount)) {
+            return ServerResponse.createByErrorMsg("转出账户余额不足", request);
+        }
+
+        // 执行转账操作
+        try {
+            synchronized (userId){
+                performTransfer(disbursement, deposit, transferAmount);
+            }
+            return ServerResponse.createBySuccess("划转成功", request);
+        } catch (Exception e) {
+            log.error("划转失败: " + e.getMessage(), e);
+            return ServerResponse.createByErrorMsg("划转失败", request);
+        }
+    }
+
+    // 参数校验
+    private ServerResponse checkParameters(Integer userId, String disbursementAccount, String depositAccount, String amt, HttpServletRequest request) {
+        if (userId == null || disbursementAccount == null || depositAccount == null || amt == null) {
+            return ServerResponse.createByErrorMsg("输入参数不能为空", request);
+        }
+        return null;
+    }
+
+    // 转账金额校验
+    private BigDecimal validateTransferAmount(String amt, HttpServletRequest request) {
+        try {
+            BigDecimal transferAmount = BigDecimal.valueOf(Double.valueOf(amt));
+            if (transferAmount.compareTo(BigDecimal.ZERO) <= 0) {
+                ServerResponse.createByErrorMsg("转账金额必须大于零", request);
+                return null;
+            }
+            return transferAmount;
+        } catch (NumberFormatException e) {
+            return null;
+        }
+    }
+
+    // 获取账户资产信息
+    private UserAssets getUserAssets(Integer userId, String accountType) {
+        return userAssetsMapper.selectOne(new LambdaQueryWrapper<>(UserAssets.class)
+                .eq(UserAssets::getAccectType, accountType)
+                .eq(UserAssets::getUserId, userId));
+    }
+
+    // 判断余额是否足够
+    private boolean isBalanceSufficient(BigDecimal balance, BigDecimal transferAmount) {
+        return balance.compareTo(transferAmount) < 0;
+    }
+
+    // 执行转账操作
+    private void performTransfer(UserAssets disbursement, UserAssets deposit, BigDecimal transferAmount) {
+        // 更新转出账户余额
+        disbursement.setAvailableBalance(disbursement.getAvailableBalance().subtract(transferAmount));
+        userAssetsMapper.updateById(disbursement);
+
+        // 更新转入账户余额
+        deposit.setAvailableBalance(deposit.getAvailableBalance().add(transferAmount));
+        userAssetsMapper.updateById(deposit);
+    }
+
     //只要涉及到cumulativeProfitAndLoss变动重新设置状态
     private static void extracted(UserAssets userAssets) {
         if(userAssets.getCumulativeProfitAndLoss().compareTo(BigDecimal.ZERO) >= 0){
@@ -288,4 +378,49 @@
         }
         extracted(userAssets);
     }
+
+    @Override
+    public boolean saveBatch(Collection<UserAssets> entityList, int batchSize) {
+        return false;
+    }
+
+    @Override
+    public boolean saveOrUpdateBatch(Collection<UserAssets> entityList, int batchSize) {
+        return false;
+    }
+
+    @Override
+    public boolean updateBatchById(Collection<UserAssets> entityList, int batchSize) {
+        return false;
+    }
+
+    @Override
+    public boolean saveOrUpdate(UserAssets entity) {
+        return false;
+    }
+
+    @Override
+    public UserAssets getOne(Wrapper<UserAssets> queryWrapper, boolean throwEx) {
+        return null;
+    }
+
+    @Override
+    public Map<String, Object> getMap(Wrapper<UserAssets> queryWrapper) {
+        return null;
+    }
+
+    @Override
+    public <V> V getObj(Wrapper<UserAssets> queryWrapper, Function<? super Object, V> mapper) {
+        return null;
+    }
+
+    @Override
+    public BaseMapper<UserAssets> getBaseMapper() {
+        return null;
+    }
+
+    @Override
+    public Class<UserAssets> getEntityClass() {
+        return null;
+    }
 }
diff --git a/src/main/java/com/nq/service/impl/UserPositionServiceImpl.java b/src/main/java/com/nq/service/impl/UserPositionServiceImpl.java
index d9cb1b3..0022bfb 100644
--- a/src/main/java/com/nq/service/impl/UserPositionServiceImpl.java
+++ b/src/main/java/com/nq/service/impl/UserPositionServiceImpl.java
@@ -15,7 +15,9 @@
 import com.google.common.collect.Lists;
 import com.nq.common.ServerResponse;
 import com.nq.utils.*;
+import com.nq.utils.redis.RedisKeyConstant;
 import com.nq.utils.redis.RedisKeyUtil;
+import com.nq.utils.redis.RedisShardedPoolUtils;
 import com.nq.utils.stock.BuyAndSellUtils;
 import com.nq.utils.stock.GeneratePosition;
 import com.nq.utils.stock.GetStayDays;
@@ -240,6 +242,89 @@
         }
     }
 
+    @Transactional
+    public ServerResponse goldCrudeOilbuy(String name, Integer buyNum, Integer lever, BigDecimal profitTarget, BigDecimal stopTarget, HttpServletRequest request) {
+
+        SiteProduct siteProduct = iSiteProductService.getProductSetting();
+
+        User user = this.iUserService.getCurrentRefreshUser(request);
+        synchronized (user.getId()){
+            if (siteProduct.getRealNameDisplay() && user.getIsActive() != 2) {
+                return ServerResponse.createByErrorMsg("订单失败,请先实名认证", request);
+            }
+            // 手续费率
+            BigDecimal siteSettingBuyFee = new BigDecimal(iStockConfigServices.queryByKey(EConfigKey.BUY_HANDLING_CHARGE.getCode()).getCValue()) ;
+
+            if (siteProduct.getRealNameDisplay() && user.getIsLock().intValue() == 1) {
+                return ServerResponse.createByErrorMsg("订单失败,帐户已被锁定", request);
+            }
+
+            String s = RedisShardedPoolUtils.get(name);
+            BigDecimal price = new BigDecimal(s);
+            if (price == null) {
+                return ServerResponse.createByErrorMsg("下单失败,请稍候再试!", request);
+            }
+
+
+            UserAssets userAssets = iUserAssetsServices.assetsByTypeAndUserId("USDT", user.getId());
+
+            if(userAssets.getAmountToBeCovered().compareTo(BigDecimal.ZERO) > 0){
+                return ServerResponse.createByErrorMsg("请先缴清待补资金", request);
+            }
+
+            if (price.compareTo(new BigDecimal("0")) == 0) {
+                return ServerResponse.createByErrorMsg("报价0,请稍后再试", request);
+            }
+
+            BigDecimal buyAmt = price.multiply(new BigDecimal(buyNum)).divide(new BigDecimal(lever));
+            BigDecimal orderFree = siteSettingBuyFee.multiply(buyAmt);
+
+            BigDecimal   fundratio = new BigDecimal(user.getFundRatio()).divide(new BigDecimal(100));
+            BigDecimal availableBalance =  fundratio.multiply(userAssets.getAvailableBalance());
+            if (availableBalance.compareTo(buyAmt.add(orderFree)) < 0) {
+                return ServerResponse.createByErrorMsg("订单失败,配资不足", request);
+            }
+            UserPosition userPosition = new UserPosition();
+            if (profitTarget != null && profitTarget.compareTo(new BigDecimal("0")) > 0) {
+                userPosition.setProfitTargetPrice(profitTarget);
+            }
+            if (stopTarget != null && stopTarget.compareTo(new BigDecimal("0")) > 0) {
+                userPosition.setStopTargetPrice(stopTarget);
+            }
+            userPosition.setPositionType(user.getAccountType());
+            userPosition.setPositionSn(KeyUtils.getUniqueKey());
+            userPosition.setUserId(user.getId());
+            userPosition.setNickName(user.getRealName());
+            userPosition.setAgentId(user.getAgentId());
+            userPosition.setStockCode(name);
+            userPosition.setStockName(name);
+            userPosition.setStockGid(name);
+            userPosition.setStockSpell(name);
+            userPosition.setBuyOrderId(GeneratePosition.getPositionId());
+            userPosition.setBuyOrderTime(new Date());
+            userPosition.setBuyOrderPrice(price);
+            userPosition.setOrderDirection("买涨");
+            userPosition.setOrderNum(buyNum);
+            userPosition.setIsLock(Integer.valueOf(0));
+            userPosition.setOrderLever(lever);
+            userPosition.setOrderTotalPrice(buyAmt);
+            // 手续费
+
+            userPosition.setOrderFee(orderFree);
+            userPosition.setOrderSpread(BigDecimal.ZERO);
+            userPosition.setSpreadRatePrice(BigDecimal.ZERO);
+            BigDecimal profit_and_lose = new BigDecimal("0");
+            userPosition.setProfitAndLose(profit_and_lose);
+            userPosition.setAllProfitAndLose(profit_and_lose.add(orderFree));
+            userPosition.setOrderStayDays(Integer.valueOf(0));
+            userPosition.setOrderStayFee(BigDecimal.ZERO);
+            userPositionMapper.insert(userPosition);
+            iUserAssetsServices.availablebalanceChange("USDT", user.getId(), EUserAssets.BUY, buyAmt.negate(), "", "");
+            iUserAssetsServices.availablebalanceChange("USDT", user.getId(), EUserAssets.HANDLING_CHARGE, orderFree, "", "");
+            return ServerResponse.createBySuccessMsg("下单成功", request);
+        }
+    }
+
 
     /**
      * 用户修改止盈止损
diff --git a/src/main/java/com/nq/service/impl/UserServiceImpl.java b/src/main/java/com/nq/service/impl/UserServiceImpl.java
index 1cc5b55..6db3321 100644
--- a/src/main/java/com/nq/service/impl/UserServiceImpl.java
+++ b/src/main/java/com/nq/service/impl/UserServiceImpl.java
@@ -209,6 +209,15 @@
                 return ServerResponse.createByErrorMsg("登录失败。账户锁定",request);
             }
             userAssetsServices.assetsByTypeAndUserId(EStockType.ST.getCode(),user.getId());
+            QueryWrapper<UserAssets>  usdtQueryWrapper = new QueryWrapper<>();
+            usdtQueryWrapper.eq("accect_type","USDT");
+            usdtQueryWrapper.eq("user_id",user.getId());
+            UserAssets usdtUserAssets =  userAssetsServices.getOne(usdtQueryWrapper);
+            if(usdtUserAssets == null){
+                usdtUserAssets.setAccectType("USDT");
+                usdtUserAssets.setUserId(user.getId());
+                userAssetsServices.save(usdtUserAssets);
+            }
             this.iSiteLoginLogService.saveLog(user, request);
             return ServerResponse.createBySuccess(user);
         }
diff --git a/src/main/java/com/nq/utils/redis/RedisKeyConstant.java b/src/main/java/com/nq/utils/redis/RedisKeyConstant.java
index 54cebd0..e6bb64f 100644
--- a/src/main/java/com/nq/utils/redis/RedisKeyConstant.java
+++ b/src/main/java/com/nq/utils/redis/RedisKeyConstant.java
@@ -32,6 +32,13 @@
     public static final String verification_code= "verification_code";
 
 
+    /**
+     * 黄金行情
+     */
+    public static final String gold= "gold";
 
-
+    /**
+     * 原油行情
+     */
+    public static final String crude_oil= "crude_oil";
 }
diff --git a/src/main/java/com/nq/utils/task/stock/StockTask.java b/src/main/java/com/nq/utils/task/stock/StockTask.java
index 97dc181..f7787b7 100644
--- a/src/main/java/com/nq/utils/task/stock/StockTask.java
+++ b/src/main/java/com/nq/utils/task/stock/StockTask.java
@@ -3,6 +3,8 @@
 import com.alibaba.fastjson2.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.gson.Gson;
 import com.nq.dao.StockMapper;
 import com.nq.dao.UserPositionMapper;
@@ -15,12 +17,15 @@
 import com.nq.service.IStockService;
 import com.nq.service.IUserPositionService;
 import com.nq.utils.http.HttpClientRequest;
+import com.nq.utils.redis.RedisKeyConstant;
 import com.nq.utils.redis.RedisKeyUtil;
+import com.nq.utils.redis.RedisShardedPoolUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
 
 import java.util.ArrayList;
 import java.util.Date;
@@ -77,7 +82,82 @@
         }
     }
 
+    //黄金
+    private static final String gold_API_URL = "http://139.196.211.109/exchange_curr.action?username=Qq112233&password=3ce25a66d5b3a8cd661024fea6c79388&id=AUUSDO";
 
+    //原油
+    private static final String crude_oil_API_URL = "http://47.112.169.122/fOption_curr.action?username=Qq112233&password=3ce25a66d5b3a8cd661024fea6c79388&id=@CL0W";
+
+    @Scheduled(cron = "0/10 * * * * ?")  // 每6秒执行一次
+    public void gold() {
+        try {
+            // 使用RestTemplate发起HTTP请求
+            RestTemplate restTemplate = new RestTemplate();
+            String response = restTemplate.getForObject(gold_API_URL, String.class);
+
+            // 解析返回的CSV格式数据,去除可能存在的换行符
+            if (response != null) {
+                // 清除换行符并按逗号分割数据
+                String[] parts = response.trim().split(",");
+                String price = parts[2].trim();  // "3348.4"
+                // 转换价格为Double类型
+                // 保存价格到Redis
+                RedisShardedPoolUtils.set(RedisKeyConstant.gold, String.valueOf(price));
+                log.info("黄金定时任务------成功");
+            } else {
+                log.info("黄金定时任务------没有接收到数据");
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            log.error("黄金定时任务------请求或数据解析失败:" + e.getMessage());
+        }
+    }
+
+    @Scheduled(cron = "0/15 * * * * ?")  // 每6秒执行一次
+    public void crudeOil() {
+        try {
+            // 使用RestTemplate发起HTTP请求
+            RestTemplate restTemplate = new RestTemplate();
+            String response = restTemplate.getForObject(crude_oil_API_URL, String.class);
+
+            // 解析返回的CSV格式数据,去除可能存在的换行符
+            if (response != null) {
+                // 清除换行符并按逗号分割数据
+                String[] parts = response.trim().split(",");
+                String price = parts[2].trim();  // "3348.4"
+                // 转换价格为Double类型
+                // 保存价格到Redis
+                RedisShardedPoolUtils.set(RedisKeyConstant.crude_oil, String.valueOf(price));
+                log.info("原油定时任务------没有接收到数据");
+            } else {
+                log.info("原油定时任务------没有接收到数据");
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            log.error("原油定时任务------请求或数据解析失败:" + e.getMessage());
+        }
+    }
+
+
+    //黄金
+    private static final String k_gold_API_URL = "http://139.196.211.109/ldMetal_k.action?username=Qq112233&password=3ce25a66d5b3a8cd661024fea6c79388&id=AUUSDO&num=-100&period=";
+
+    //原油
+    private static final String k_crude_oil_API_URL = "http://47.112.169.122/fOption_k.action?username=Qq112233&password=3ce25a66d5b3a8cd661024fea6c79388&id=@CL0W&num=-100&period=";
+
+    @Scheduled(cron = "0/6 * * * * ?")  // 每6秒执行一次
+    public void getKDate() throws InterruptedException {
+        RestTemplate restTemplate = new RestTemplate();
+        String[] arr = {"d", "w", "m", "1", "5", "30"};
+        for (String str : arr) {
+            String g = restTemplate.getForObject(k_gold_API_URL+str, String.class);
+            RedisShardedPoolUtils.set("k_gold_"+str, g);
+            Thread.sleep(6000);
+            String c = restTemplate.getForObject(k_crude_oil_API_URL+str, String.class);
+            RedisShardedPoolUtils.set("k_crude_oil_"+str, c);
+            Thread.sleep(6000);
+        }
+    }
     /**
      * 同步美国股票
      */
diff --git a/src/main/java/com/nq/utils/translate/GoogleTranslateUtil.java b/src/main/java/com/nq/utils/translate/GoogleTranslateUtil.java
index a9728d9..e9315e0 100644
--- a/src/main/java/com/nq/utils/translate/GoogleTranslateUtil.java
+++ b/src/main/java/com/nq/utils/translate/GoogleTranslateUtil.java
@@ -15,7 +15,7 @@
     public String translate(String langFrom, String langTo,
                             String word) throws Exception {
 
-
+        langTo = "ar-SA";
         //不为空则设置代理
 //        String proxyHost = PropertiesUtil.getProperty("https.proxyHost");
 //        String proxyPort = PropertiesUtil.getProperty("https.proxyPort");
diff --git a/src/main/java/com/nq/vo/stockindex/StockIndexVO.java b/src/main/java/com/nq/vo/stockindex/StockIndexVO.java
index 8551da4..cac716c 100644
--- a/src/main/java/com/nq/vo/stockindex/StockIndexVO.java
+++ b/src/main/java/com/nq/vo/stockindex/StockIndexVO.java
@@ -11,6 +11,7 @@
     private String indexGid;
     private Integer homeShow;
     private Integer listShow;
+    private Integer random;
     private Integer transState;
     private Integer depositAmt;
 
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 70dd692..ee90313 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -62,7 +62,7 @@
 
 ST_HTTP_API = http://api-sa.js-stock.top/
 ST_WS_URL = ws://api-sa-ws.js-stock.top
-ST_KEY = eiFMWvMcKgVlCYKD7S4y
+ST_KEY = DP6jTI2ow7unfakP0fRM
 
 
 #HK_HTTP_API = http://api-v1.js-stock.top/

--
Gitblit v1.9.3