| | |
| | | import com.yami.trading.common.domain.Result; |
| | | import com.yami.trading.common.exception.YamiShopBindException; |
| | | import com.yami.trading.common.util.Arith; |
| | | import com.yami.trading.common.util.DateUtils; |
| | | import com.yami.trading.common.util.RandomUtil; |
| | | import com.yami.trading.common.util.StringEscapeUtil; |
| | | import com.yami.trading.dao.dz.ExchangeApplyOrderDzMapper; |
| | | import com.yami.trading.dao.dz.StockDzMapper; |
| | | import com.yami.trading.service.MarketOpenChecker; |
| | | import com.yami.trading.service.MoneyLogService; |
| | | import com.yami.trading.service.UsStockTradingDayCalculator; |
| | | import com.yami.trading.service.WalletService; |
| | | import com.yami.trading.service.data.DataService; |
| | | import com.yami.trading.service.dz.StockDzService; |
| | |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import javax.annotation.Resource; |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import java.lang.reflect.Field; |
| | | import java.lang.reflect.Method; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.text.DecimalFormat; |
| | |
| | | Page<StockDzDto> page = new Page<>(pageNum, pageSize); |
| | | page = stockDzMapper.findStockTypeDz(page,orderBy,keyWords,formatDate, stockType); |
| | | page.getRecords().forEach(stockDzDto -> { |
| | | Realtime realtime = this.dataService.realtime(stockDzDto.getStockCode()).get(0); |
| | | if (realtime != null) { |
| | | stockDzDto.setCurrentPrice(realtime.getClose()); |
| | | List<Realtime> list = this.dataService.realtime(stockDzDto.getStockCode()); |
| | | if (!list.isEmpty() && list.get(0) != null) { |
| | | stockDzDto.setCurrentPrice(list.get(0).getClose()); |
| | | } |
| | | if (stockDzDto.getDayRate() != 0) { |
| | | double dayRate = BigDecimal.valueOf(stockDzDto.getDayRate()).multiply(BigDecimal.valueOf(100)).doubleValue(); |
| | | stockDzDto.setDayRate(dayRate); |
| | | } |
| | | if (!stockDzDto.getStockName().isEmpty()) { |
| | | stockDzDto.setStockName(StringEscapeUtil.unEscapeAmpersand(stockDzDto.getStockName())); |
| | | } |
| | | }); |
| | | return Result.succeed(page); |
| | |
| | | } |
| | | |
| | | @Override |
| | | public Result addByAdmin(String stockCode, String stockNum, String password, String startTime, String endTime, String discount,Integer period,String nowPrice,Integer switchType, String stockType, double dayRate) { |
| | | public Result addByAdmin(String stockCode, String stockName, String stockNum, String password, String startTime, String endTime, String discount,Integer period,String nowPrice,Integer switchType, String stockType, double dayRate) { |
| | | if (stockCode == null || stockCode.isEmpty() || startTime == null || startTime.isEmpty() ||endTime == null || endTime.isEmpty() || nowPrice == null){ |
| | | return Result.failed("参数不能为空"); |
| | | } |
| | | if (stockType.equalsIgnoreCase(Item.indices) && dayRate == 0) { |
| | | return Result.failed("请输入日收益率"); |
| | | if (stockType.equalsIgnoreCase(Item.indices)) { |
| | | if (dayRate == 0) { |
| | | return Result.failed("请输入日收益率"); |
| | | } |
| | | if (stockName == null || stockName.isEmpty()){ |
| | | return Result.failed("请输入股票名称"); |
| | | } |
| | | } |
| | | |
| | | /*if (!stockName.isEmpty()) { |
| | | stockName = StringEscapeUtil.escapeAmpersand(stockName); |
| | | }*/ |
| | | |
| | | //该类型是否已添加 |
| | | Long count = stockDzMapper.selectCount(new LambdaQueryWrapper<StockDz>() |
| | |
| | | return Result.failed("股票代码已存在"); |
| | | } |
| | | |
| | | Item item = itemService.findBySymbol(stockCode); |
| | | if(item == null){ |
| | | return Result.failed("不存在该股票"); |
| | | StockDz stockDz = new StockDz(); |
| | | if (stockType.equalsIgnoreCase(Item.indices)) { |
| | | stockDz.setStockName(stockName); |
| | | stockDz.setStockCode(stockCode); |
| | | stockDz.setStockSpell(stockCode); |
| | | } else { |
| | | Item item = itemService.findBySymbol(stockCode); |
| | | if(item == null){ |
| | | return Result.failed("不存在该股票"); |
| | | } |
| | | stockDz.setStockName(item.getSymbolFullName()); |
| | | stockDz.setStockCode(item.getSymbol()); |
| | | stockDz.setStockSpell(item.getSymbolData()); |
| | | } |
| | | |
| | | StockDz stockDz = new StockDz(); |
| | | stockDz.setStockName(item.getSymbolFullName()); |
| | | stockDz.setStockCode(item.getSymbol()); |
| | | stockDz.setStockType(stockType); |
| | | stockDz.setStockSpell(item.getSymbolData()); |
| | | stockDz.setIsLock(0); |
| | | stockDz.setIsShow(1); |
| | | stockDz.setAddTime(new Date()); |
| | |
| | | stockDz.setSwitchType(switchType); |
| | | stockDz.setNowPrice(new BigDecimal(nowPrice)); |
| | | stockDz.setDayRate(dayRate); |
| | | |
| | | if (save(stockDz)) { |
| | | return Result.succeed("添加成功"); |
| | | } |
| | |
| | | try { |
| | | Page page = new Page(pageNum, pageSize); |
| | | stockDzMapper.getDzListByAdmin(page, Keywords, stockType); |
| | | |
| | | page.getRecords().forEach(stockDzObj -> { |
| | | // 1. 判空 + 强转为HashMap(兼容所有Map子类,如LinkedHashMap) |
| | | if (stockDzObj == null || !(stockDzObj instanceof HashMap)) { |
| | | return; |
| | | } |
| | | |
| | | // 强转为HashMap<String, Object>(确保key是字符串类型) |
| | | HashMap<String, Object> stockDzMap = (HashMap<String, Object>) stockDzObj; |
| | | |
| | | // 2. 获取stock_name字段值(注意:key的拼写/大小写要和Map中完全一致!) |
| | | Object oldValue = stockDzMap.get("stock_name"); |
| | | |
| | | // 3. 空值防护 + 处理&转义 |
| | | if (oldValue instanceof String) { |
| | | String oldStr = (String) oldValue; |
| | | // 反转义&为& |
| | | String newStr = oldStr.replace("&", "&"); |
| | | // 4. 重新赋值回HashMap |
| | | stockDzMap.put("stock_name", newStr); |
| | | } else { |
| | | // 字段值不是字符串/为null时的提示(方便排查) |
| | | System.out.println("stock_name字段值异常:" + oldValue); |
| | | } |
| | | }); |
| | | return Result.succeed(page); |
| | | } catch (Exception e) { |
| | | log.error(e.getMessage()); |
| | |
| | | return Result.failed("股票代码已存在"); |
| | | } |
| | | |
| | | Item item = itemService.findBySymbol(model.getStockCode()); |
| | | if(item == null){ |
| | | return Result.failed("不存在该股票"); |
| | | } |
| | | |
| | | StockDz stockDz = stockDzMapper.selectById(model.getUuid()); |
| | | if(stockDz == null){ |
| | | return Result.failed("不存在该大宗信息"); |
| | | } |
| | | //model.setStockName(item.getSymbolFullName()); |
| | | model.setStockCode(item.getSymbol()); |
| | | |
| | | /*if (!model.getStockName().isEmpty()) { |
| | | System.out.println(model.getStockName()); |
| | | //model.setStockName(StringEscapeUtil.escapeAmpersand(model.getStockName())); |
| | | }*/ |
| | | |
| | | if (!stockDz.getStockType().equalsIgnoreCase(Item.indices)) { |
| | | Item item = itemService.findBySymbol(model.getStockCode()); |
| | | if(item == null){ |
| | | return Result.failed("不存在该股票"); |
| | | } else { |
| | | model.setStockCode(item.getSymbol()); |
| | | model.setStockSpell(item.getSymbolData()); |
| | | } |
| | | } else { |
| | | model.setStockSpell(model.getStockCode()); |
| | | } |
| | | model.setStockType(stockDz.getStockType()); |
| | | model.setStockSpell(item.getSymbolData()); |
| | | |
| | | return stockDzMapper.updateById(model) > 0 ? Result.succeed("修改成功") : Result.failed("修改失败"); |
| | | } |
| | | |
| | |
| | | if (!page.getRecords().isEmpty()) { |
| | | List<ExchangeApplyOrderDzDto> list = page.getRecords(); |
| | | list.forEach(dz -> { |
| | | double closePrice; |
| | | double closePrice = 0; |
| | | if (state != null) { //持仓 |
| | | Realtime realtime = this.dataService.realtime(dz.getSymbol()).get(0); |
| | | //获取现价 |
| | | closePrice = realtime.getClose(); |
| | | List<Realtime> realtimeList = this.dataService.realtime(dz.getSymbol()); |
| | | if (!realtimeList.isEmpty() && realtimeList.get(0) != null) { |
| | | //获取现价 |
| | | closePrice = realtimeList.get(0).getClose(); |
| | | } |
| | | } else { //已结算 |
| | | closePrice = dz.getClosePrice(); |
| | | } |
| | | if (dz.getDayRate() > 0) { |
| | | Date startTime = dz.getCreateTime(); |
| | | Date now = new Date(); |
| | | Calendar calendar = Calendar.getInstance(); |
| | | calendar.setTime(startTime); |
| | | calendar.add(Calendar.DATE, dz.getPeriod()); |
| | | // 锁仓截至时间 |
| | | Date resultTime = calendar.getTime(); |
| | | //显示收益不超过 锁仓截至时间 |
| | | if(now.getTime() > resultTime.getTime()){ |
| | | now = resultTime; |
| | | } |
| | | // 计算美股交易天数 |
| | | int num = UsStockTradingDayCalculator.countUsStockTradingDays(startTime, now); |
| | | // 计算相差天数 |
| | | int num = com.yami.trading.common.util.DateUtil.dateNum(startTime, new Date()); |
| | | int days = com.yami.trading.common.util.DateUtil.dateNum(startTime, now); |
| | | if (days >= dz.getPeriod()) { |
| | | num--; |
| | | } |
| | | num = Math.max(1, Math.min(num, dz.getPeriod())); |
| | | double dayEarnings = dz.getDayRate() * dz.getVolume(); |
| | | double profitLoss = dayEarnings * num; |
| | | |
| | | double dayEarnings = 0; //日收益 |
| | | double profitLoss = 0; //盈利 |
| | | double volume = dz.getVolume(); //本金 |
| | | for (int i = 0; i < num; i++) { |
| | | dayEarnings = dz.getDayRate() * volume; |
| | | profitLoss += dayEarnings; |
| | | volume += dayEarnings; |
| | | } |
| | | |
| | | DecimalFormat df = new DecimalFormat("#.##"); |
| | | String resultStr = df.format(profitLoss); |
| | | String resultStr2 = df.format(dayEarnings); |
| | |
| | | String resultPer = df.format(profitPercentage); |
| | | dz.setProfitLossPercentage(Double.parseDouble(resultPer)); |
| | | } |
| | | |
| | | if (!dz.getStockName().isEmpty()) { |
| | | dz.setStockName(StringEscapeUtil.unEscapeAmpersand(dz.getStockName())); |
| | | } |
| | | |
| | | }); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | @Override |
| | | public Result getDzCheckList(int pageNum, int pageSize, String state, String stockCode, String stockType, List<String> checkedList) { |
| | | public Result getDzCheckList(int pageNum, int pageSize, String state, String stockCode, String stockType, String userName, List<String> checkedList) { |
| | | try { |
| | | Page page = new Page(pageNum, pageSize); |
| | | |
| | | stockDzMapper.getDzCheckList(page ,state, stockCode, stockType, checkedList); |
| | | stockDzMapper.getDzCheckList(page ,state, stockCode, stockType, userName, checkedList); |
| | | |
| | | page.getRecords().forEach(stockDzObj -> { |
| | | if (stockDzObj == null) { |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | // ========== 第一步:获取字段值(以stockName为例) ========== |
| | | // 1. 获取getter方法(JavaBean规范:字段名stockName → 方法名getStockName) |
| | | Method getter = stockDzObj.getClass().getMethod("getStockName"); |
| | | // 2. 执行方法获取值 |
| | | Object oldValue = getter.invoke(stockDzObj); |
| | | |
| | | // ========== 第二步:处理字段值(仅当值不为null时) ========== |
| | | if (oldValue instanceof String) { // 确保字段类型是String(根据实际字段类型调整) |
| | | String newValue = ((String) oldValue).replace("&", "&"); // 自定义处理逻辑 |
| | | |
| | | // ========== 第三步:重新赋值 ========== |
| | | // 1. 获取setter方法(参数为字段类型,比如String) |
| | | Method setter = stockDzObj.getClass().getMethod("setStockName", String.class); |
| | | // 2. 执行setter方法赋值 |
| | | setter.invoke(stockDzObj, newValue); |
| | | } |
| | | } catch (Exception e) { |
| | | // 捕获反射异常(方法不存在、权限不足等),避免循环中断 |
| | | log.error(e.getMessage()); |
| | | } |
| | | }); |
| | | return Result.succeed(page); |
| | | } catch (Exception e) { |
| | | log.error(e.getMessage()); |
| | | log.error(e.getMessage(), e); |
| | | } |
| | | return Result.failed("获取失败"); |
| | | } |
| | |
| | | public Result buyDz(String dzId, String password, double num, String partyId, Boolean isAdmin) { |
| | | try { |
| | | if (num <= 0) { |
| | | throw new YamiShopBindException("请输入"); |
| | | return Result.failed("请购买最小数量"); |
| | | } |
| | | User party = userService.getById(partyId); |
| | | if (!party.isEnabled()) { |
| | | throw new YamiShopBindException("用户已禁用"); |
| | | return Result.failed("用户已禁用"); |
| | | } |
| | | Syspara syspara = sysparaService.find("stop_user_internet"); |
| | | /*Syspara syspara = sysparaService.find("stop_user_internet"); |
| | | String stopUserInternet = syspara.getSvalue(); |
| | | if (org.apache.commons.lang3.StringUtils.isNotEmpty(stopUserInternet)) { |
| | | String[] stopUsers = stopUserInternet.split(","); |
| | | if (Arrays.asList(stopUsers).contains(party.getUserName())) { |
| | | throw new YamiShopBindException("无网络"); |
| | | } |
| | | } |
| | | }*/ |
| | | |
| | | StockDz stockDz = this.stockDzMapper.selectOne(new QueryWrapper<StockDz>().eq("uuid", dzId)); |
| | | if (stockDz == null) { |
| | | throw new YamiShopBindException("大宗不存在"); |
| | | return Result.failed("大宗数据错误"); |
| | | } |
| | | if (StringUtils.isNotEmpty(stockDz.getPassword()) && !Objects.equals(stockDz.getPassword(), password)) { |
| | | throw new YamiShopBindException("密码错误"); |
| | | return Result.failed("密码错误"); |
| | | } |
| | | if (stockDz.getIsLock() != null && stockDz.getIsLock() != 0) { |
| | | throw new YamiShopBindException("股票被锁定,不能购买"); |
| | | return Result.failed("股票被锁定"); |
| | | } |
| | | |
| | | if(stockDz.getStartTime().getTime() > new Date().getTime() || stockDz.getEndTime().getTime() < new Date().getTime()){ |
| | |
| | | BigDecimal nowPrice = stockDz.getNowPrice(); |
| | | |
| | | if (nowPrice.compareTo(new BigDecimal("0")) == 0) { |
| | | throw new YamiShopBindException("股票价格0,请重试"); |
| | | return Result.failed("大宗数据错误"); |
| | | } |
| | | boolean isETF = stockDz.getStockType().equalsIgnoreCase(Item.indices); |
| | | if (isETF) { |
| | |
| | | } |
| | | |
| | | if (!isAdmin) { //后台买入不判断 |
| | | boolean isOpen = MarketOpenChecker.isMarketOpenBuyDz(Item.US_STOCKS); |
| | | if (!isOpen) { |
| | | return Result.failed("当前股市休市"); |
| | | if (!stockDz.getStockName().contains("测试")) { |
| | | boolean isOpen = MarketOpenChecker.isMarketOpenBuyDz(Item.US_STOCKS); |
| | | if (!isOpen) { |
| | | return Result.failed("当前股市休市"); |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | BigDecimal buyAmt; |
| | | if (isETF) { |
| | | buyAmt = new BigDecimal(num); |
| | | nowPrice = buyAmt; |
| | | } else { |
| | | buyAmt = nowPrice.multiply(new BigDecimal(num)); |
| | | } |
| | |
| | | if(stockDz.getSwitchType() == 1) { |
| | | order.setState(ExchangeApplyOrderDz.STATE_SUBMITTED); |
| | | exchangeApplyOrderDzMapper.insert(order); |
| | | return Result.succeed("购买成功,等待审核"); |
| | | return Result.succeed("购买成功"); |
| | | } |
| | | |
| | | order.setState(ExchangeApplyOrderDz.STATE_POSITION); |
| | |
| | | try { |
| | | ExchangeApplyOrderDz order = exchangeApplyOrderDzMapper.selectById(id); |
| | | if (order == null) { |
| | | throw new YamiShopBindException("订单不存在"); |
| | | return Result.failed("失败"); |
| | | } |
| | | if (!order.getState().equals(ExchangeApplyOrderDz.STATE_POSITION)) { |
| | | throw new YamiShopBindException("订单不能平仓"); |
| | | return Result.failed("订单不能平仓"); |
| | | } |
| | | if (num == null) { //不传默认平全部 |
| | | num = order.getSymbolValue(); |
| | | } |
| | | if (num < 0 || num > order.getSymbolValue()) { |
| | | throw new YamiShopBindException("数量错误"); |
| | | return Result.failed("数量错误"); |
| | | } |
| | | if (partyId == null) { |
| | | partyId = order.getPartyId(); |
| | |
| | | |
| | | StockDz stockDz = this.stockDzMapper.selectOne(new QueryWrapper<StockDz>().eq("uuid", order.getDzId())); |
| | | if (stockDz == null) { |
| | | throw new YamiShopBindException("大宗不存在"); |
| | | return Result.failed("大宗数据错误"); |
| | | } |
| | | |
| | | BigDecimal nowPrice; |
| | |
| | | nowPrice = stockDz.getNowPrice(); |
| | | } |
| | | if (nowPrice.compareTo(new BigDecimal("0")) == 0) { |
| | | throw new YamiShopBindException("股票价格0,请重试"); |
| | | return Result.failed("大宗数据错误"); |
| | | } |
| | | |
| | | Date now = new Date(); |
| | | if (!isAdmin) { |
| | | if (stockDz.getPeriod() != null && stockDz.getPeriod() > 0) { |
| | | Calendar calendar = Calendar.getInstance(); |
| | | calendar.setTime(order.getCreateTime()); |
| | | calendar.add(Calendar.DATE, stockDz.getPeriod()); |
| | | // 锁仓时间 |
| | | Date resultTime = calendar.getTime(); |
| | | |
| | | if(now.getTime() < resultTime.getTime()){ |
| | | Date now = new Date(); |
| | | Calendar calendar = Calendar.getInstance(); |
| | | calendar.setTime(order.getCreateTime()); |
| | | calendar.add(Calendar.DATE, stockDz.getPeriod()); |
| | | // 锁仓截至时间 |
| | | Date resultTime = calendar.getTime(); |
| | | if (!isAdmin) { |
| | | if (isETF && (order.getUnLock() == null || order.getUnLock() == 0)) { |
| | | return Result.failed("未到平仓时间"); |
| | | } |
| | | |
| | | if (stockDz.getPeriod() != null && stockDz.getPeriod() > 0) { |
| | | if (now.getTime() < resultTime.getTime()) { |
| | | return Result.failed("未到平仓时间"); |
| | | } |
| | | } |
| | | |
| | | if (!stockDz.getStockName().contains("测试")) { |
| | | boolean isOpen = MarketOpenChecker.isMarketOpenByItemCloseType(Item.US_STOCKS); |
| | | if (!isOpen) { |
| | |
| | | double closeAmt; |
| | | String symbol = stockDz.getStockCode(); |
| | | |
| | | double closePrice; |
| | | double closePrice = 0; |
| | | if (isETF) { |
| | | //按日收益率结算 |
| | | closeAmt = order.getPrice(); |
| | | closeAmt = closeAmt + stockDz.getDayRate() * closeAmt * stockDz.getPeriod(); |
| | | Date startTime = order.getCreateTime(); |
| | | // 计算美股交易天数 |
| | | int day = UsStockTradingDayCalculator.countUsStockTradingDays(startTime, resultTime) - 1; |
| | | day = Math.max(1, Math.min(day, stockDz.getPeriod())); |
| | | // 复利总金额 = 本金 × (1 + 日收益率) ^ 交易日数 |
| | | closeAmt = closeAmt * Math.pow(1 + stockDz.getDayRate(), day); |
| | | closeAmt = BigDecimal.valueOf(closeAmt).setScale(4, RoundingMode.DOWN).doubleValue(); |
| | | |
| | | closePrice = closeAmt; |
| | | closeAmt = closeAmt - orderFree.doubleValue(); |
| | | } else { |
| | | Realtime realtime = this.dataService.realtime(symbol).get(0); |
| | | closePrice = realtime.getClose(); |
| | | List<Realtime> realtimeList = this.dataService.realtime(symbol); |
| | | if (!realtimeList.isEmpty() && realtimeList.get(0) != null) { |
| | | //获取现价 |
| | | closePrice = realtimeList.get(0).getClose(); |
| | | } |
| | | closeAmt = closePrice * num - orderFree.doubleValue(); |
| | | } |
| | | if (closeAmt < 0) { |
| | |
| | | } |
| | | |
| | | |
| | | public static void main(String[] args) { |
| | | double dayEarnings = 0; //日收益 |
| | | double profitLoss = 0; //盈利 |
| | | double volume = 6000; //本金 |
| | | double price = volume; |
| | | int num = 11; |
| | | double dayRate = 0.12; |
| | | for (int i = 0; i < num; i++) { |
| | | dayEarnings = dayRate * volume; |
| | | profitLoss += dayEarnings; |
| | | volume += dayEarnings; |
| | | } |
| | | System.out.println("dayEarnings:" + dayEarnings); |
| | | System.out.println("profitLoss:" + profitLoss); |
| | | System.out.println("volume:" + volume); |
| | | |
| | | |
| | | // 复利总金额 = 本金 × (1 + 日收益率) ^ 交易日数 |
| | | double compoundAmount = price * Math.pow(1 + dayRate, num); |
| | | // 复利总盈亏 = 复利总金额 - 本金 |
| | | double profitLoss2 = compoundAmount - price; |
| | | System.out.println("compoundAmount:" + compoundAmount); |
| | | System.out.println("profitLoss2:" + profitLoss2); |
| | | } |
| | | } |