5 files modified
10 files added
| | |
| | | package com.nq.controller; |
| | | |
| | | import com.nq.common.ServerResponse; |
| | | import com.nq.pojo.SiteSetting; |
| | | import com.nq.service.IStockService; |
| | | import com.nq.service.StockDzService; |
| | | import org.slf4j.Logger; |
| | |
| | | @RequestParam(value = "stockType", defaultValue = "US") String stockType) { |
| | | return this.iStockService.getIndicesAndKData(pid, stockType); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.nq.controller; |
| | | |
| | | import com.nq.common.ServerResponse; |
| | | import com.nq.service.IStockAiService; |
| | | 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.math.BigDecimal; |
| | | import java.util.concurrent.locks.Lock; |
| | | import java.util.concurrent.locks.ReentrantLock; |
| | | |
| | | @Controller |
| | | @RequestMapping({"/api/lnk/"}) |
| | | public class StockInkApiController { |
| | | |
| | | @Autowired |
| | | IStockAiService stockAiService; |
| | | |
| | | private static final ThreadLocal<Boolean> buyOrderCreated = ThreadLocal.withInitial(() -> false); |
| | | private final Lock buyLock = new ReentrantLock(); |
| | | |
| | | /** |
| | | * 获取ai交易产品列表 |
| | | * @return |
| | | */ |
| | | @RequestMapping("getStockAiList.do") |
| | | @ResponseBody |
| | | public ServerResponse getStockAiList(@RequestParam(value = "pageNum", defaultValue = "1") int pageNum, |
| | | @RequestParam(value = "pageSize", defaultValue = "5") int pageSize) { |
| | | return stockAiService.getStockAiList(pageNum, pageSize); |
| | | } |
| | | |
| | | /** |
| | | * ai量化交易购买 |
| | | * @param id |
| | | * @param buyNum 买入金额 |
| | | * @return |
| | | */ |
| | | @RequestMapping("buyStockAi.do") |
| | | @ResponseBody |
| | | public ServerResponse buyStockAi(@RequestParam(value = "id") Long id, |
| | | @RequestParam(value = "buyNum") BigDecimal buyNum, HttpServletRequest request) { |
| | | if (buyNum.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return ServerResponse.createByErrorMsg("购买金额不能小于0"); |
| | | } |
| | | buyLock.lock(); |
| | | try { |
| | | if (buyOrderCreated.get()) { |
| | | return ServerResponse.createByErrorMsg("当前下单人数过多,请稍后重试"); |
| | | } |
| | | buyOrderCreated.set(true); |
| | | return stockAiService.buyStockAi(id, buyNum, request); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | return ServerResponse.createByErrorMsg("订单异常,请稍后重试"); |
| | | } finally{ |
| | | buyLock.unlock(); |
| | | buyOrderCreated.set(false); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取ai交易产品订单列表 |
| | | * @return |
| | | */ |
| | | @RequestMapping("getStockAiOrderList.do") |
| | | @ResponseBody |
| | | public ServerResponse getStockAiOrderList(@RequestParam(value = "pageNum", defaultValue = "1") int pageNum, |
| | | @RequestParam(value = "pageSize", defaultValue = "5") int pageSize, |
| | | @RequestParam(value = "status") String status, |
| | | HttpServletRequest request) { |
| | | return stockAiService.getStockAiOrderList(pageNum, pageSize, status ,request); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.nq.dao; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.nq.pojo.StockAI; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface StockAiMapper extends BaseMapper<StockAI> { |
| | | |
| | | } |
| New file |
| | |
| | | package com.nq.dao; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.nq.pojo.StockAIOrder; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface StockAiOrderMapper extends BaseMapper<StockAIOrder> { |
| | | |
| | | } |
| New file |
| | |
| | | package com.nq.enums; |
| | | |
| | | import lombok.Getter; |
| | | |
| | | @Getter |
| | | public enum EStockAIOrderStatus { |
| | | |
| | | //等待审核 |
| | | wait("wait"), |
| | | //已通过 |
| | | passed("passed"), |
| | | //不通过 |
| | | notPass("notPass"), |
| | | //已完成 |
| | | finished("finished"), |
| | | ; |
| | | |
| | | private String status; |
| | | |
| | | EStockAIOrderStatus(String status) { |
| | | this.status = status; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.nq.enums; |
| | | |
| | | import lombok.Getter; |
| | | |
| | | @Getter |
| | | public enum EStockAIStatus { |
| | | |
| | | //上线 |
| | | online("online"), |
| | | //下架 |
| | | out("out"), |
| | | ; |
| | | |
| | | private String value; |
| | | |
| | | EStockAIStatus(String value) { |
| | | this.value = value; |
| | | } |
| | | |
| | | } |
| | |
| | | // MAS("MAS","马来西亚股票","42",PropertiesUtil.getProperty("MAS_HTTP_API"),PropertiesUtil.getProperty("MAS_KEY"),"MYR","RM"), |
| | | // |
| | | // IN("IN","印度股票","14", PropertiesUtil.getProperty("JS_IN_HTTP_URL"),PropertiesUtil.getProperty("JS_IN_KEY"),"INR","₹"), |
| | | MX("MEX","墨西哥股票","7",PropertiesUtil.getProperty("MX_HTTP_API"),PropertiesUtil.getProperty("MX_KEY"),"MXN","MXN$"); |
| | | MX("MEX","墨西哥股票","7",PropertiesUtil.getProperty("MX_HTTP_API"),PropertiesUtil.getProperty("MX_KEY"),"MXN","MX$"); |
| | | |
| | | // TH("TH","泰国股票","41",PropertiesUtil.getProperty("TH_HTTP_API"),PropertiesUtil.getProperty("TH_KEY")), |
| | | // HG("HG","韩国股票","11",PropertiesUtil.getProperty("HG_HTTP_API"),PropertiesUtil.getProperty("HG_KEY")), |
| | |
| | | WITHDRAW("WITHDRAW","提现"), |
| | | |
| | | TRANSFER("TRANSFER","转换"), |
| | | TOP_UP("TOP_UP","充值"); |
| | | |
| | | TOP_UP("TOP_UP","充值"), |
| | | BUY_AI("BUY_AI","购买AI产品"), |
| | | ; |
| | | |
| | | private String code; |
| | | |
| New file |
| | |
| | | package com.nq.pojo; |
| | | |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import lombok.Data; |
| | | |
| | | import javax.persistence.Entity; |
| | | import javax.persistence.GeneratedValue; |
| | | import javax.persistence.GenerationType; |
| | | import javax.persistence.Id; |
| | | import java.math.BigDecimal; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * AI产品表 |
| | | */ |
| | | @Data |
| | | @TableName("stock_ai") |
| | | @Entity |
| | | public class StockAI { |
| | | |
| | | @Id |
| | | @GeneratedValue(strategy = GenerationType.IDENTITY) |
| | | private Long id; |
| | | |
| | | private String stockType; |
| | | |
| | | private String stockName; |
| | | |
| | | /** |
| | | * 最低买入金额 |
| | | */ |
| | | private BigDecimal minPrice; |
| | | |
| | | /** |
| | | * 交易成功率 |
| | | */ |
| | | private BigDecimal successRate; |
| | | |
| | | /** |
| | | * 预期收益率 |
| | | */ |
| | | private BigDecimal expectedEarning; |
| | | |
| | | /** |
| | | * 周期 天 |
| | | */ |
| | | private Integer cycle; |
| | | |
| | | /** |
| | | * 状态 上线 下架 |
| | | */ |
| | | private String status; |
| | | |
| | | //创建时间 |
| | | private Date createDate; |
| | | } |
| New file |
| | |
| | | package com.nq.pojo; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import javax.persistence.Entity; |
| | | import javax.persistence.GeneratedValue; |
| | | import javax.persistence.GenerationType; |
| | | import javax.persistence.Id; |
| | | import java.math.BigDecimal; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * AI产品订单表 |
| | | */ |
| | | @Data |
| | | @TableName("stock_ai_order") |
| | | @Entity |
| | | public class StockAIOrder { |
| | | |
| | | @Id |
| | | @GeneratedValue(strategy = GenerationType.IDENTITY) |
| | | private Long id; |
| | | |
| | | //用户id |
| | | private Integer userId; |
| | | |
| | | //ai交易产品id |
| | | private Long stockAiId; |
| | | |
| | | //买入时间 |
| | | private Date buyDate; |
| | | |
| | | /** |
| | | * 买入金额 |
| | | */ |
| | | private BigDecimal buyAmount; |
| | | |
| | | /** |
| | | * 剩余金额 |
| | | */ |
| | | private BigDecimal remainAmount; |
| | | |
| | | /** |
| | | * 实际收益 |
| | | */ |
| | | private BigDecimal realEarning; |
| | | |
| | | /** |
| | | * 状态 待审核 申请通过 申请通过 已完成 |
| | | */ |
| | | private String status; |
| | | |
| | | //审核时间 |
| | | private Date auditDate; |
| | | } |
| New file |
| | |
| | | package com.nq.pojo; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import javax.persistence.Entity; |
| | | import javax.persistence.GeneratedValue; |
| | | import javax.persistence.GenerationType; |
| | | import javax.persistence.Id; |
| | | import java.math.BigDecimal; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * AI产品订单建仓表 |
| | | */ |
| | | @Data |
| | | @TableName("stock_ai_order_position") |
| | | @Entity |
| | | public class StockAIOrderPosition { |
| | | |
| | | @Id |
| | | @GeneratedValue(strategy = GenerationType.IDENTITY) |
| | | private Long id; |
| | | |
| | | //ai交易产品订单id |
| | | private Long stockAiOrderId; |
| | | |
| | | //建仓股票id |
| | | private Integer stockId; |
| | | |
| | | /** |
| | | * 建仓股票数量 |
| | | */ |
| | | private Long stockNum; |
| | | |
| | | /** |
| | | * 建仓股票单价 |
| | | */ |
| | | private BigDecimal stockPrice; |
| | | |
| | | /** |
| | | * 建仓时间 |
| | | */ |
| | | private Date creatDate; |
| | | |
| | | /** |
| | | * 平仓单价 |
| | | */ |
| | | private BigDecimal coverPrice; |
| | | |
| | | /** |
| | | * 平仓时间 |
| | | */ |
| | | private Date coverDate; |
| | | |
| | | /** |
| | | * 收益 (平仓单价-建仓股票单价)*建仓股票数量 |
| | | */ |
| | | private BigDecimal earnings; |
| | | |
| | | |
| | | } |
| New file |
| | |
| | | package com.nq.service; |
| | | |
| | | import com.nq.common.ServerResponse; |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import java.math.BigDecimal; |
| | | |
| | | public interface IStockAiService { |
| | | |
| | | ServerResponse getStockAiList(Integer pageNum, Integer pageSize); |
| | | |
| | | ServerResponse buyStockAi(Long id, BigDecimal buyNum, HttpServletRequest request); |
| | | |
| | | ServerResponse getStockAiOrderList(Integer pageNum, Integer pageSize, String status, HttpServletRequest request); |
| | | |
| | | } |
| | |
| | | * */ |
| | | Boolean availablebalanceChange(String accetType, Integer userId, EUserAssets eUserAssets, BigDecimal amount, String desc, String descType); |
| | | |
| | | /** |
| | | * ai交易 |
| | | * @param userAssets |
| | | * @param eUserAssets |
| | | * @param amount |
| | | * @return |
| | | */ |
| | | Boolean aiAvailableBalanceChange(UserAssets userAssets, EUserAssets eUserAssets, BigDecimal amount); |
| | | |
| | | /** |
| | | * 根据汇率转换金额 |
| | | * @param amount 转换金额 |
| | | * @param rate 转换汇率 |
| | | * @return |
| | | */ |
| | | BigDecimal exchangeAmountByRate(BigDecimal amount, BigDecimal rate); |
| | | } |
| New file |
| | |
| | | package com.nq.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.github.pagehelper.PageHelper; |
| | | import com.github.pagehelper.PageInfo; |
| | | import com.nq.Repository.ExchangeRateRepository; |
| | | import com.nq.common.ServerResponse; |
| | | import com.nq.dao.StockAiMapper; |
| | | import com.nq.dao.StockAiOrderMapper; |
| | | import com.nq.enums.*; |
| | | import com.nq.pojo.*; |
| | | import com.nq.service.*; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import java.math.BigDecimal; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | |
| | | @Service("iStockAiService") |
| | | public class StockAiServiceImpl implements IStockAiService { |
| | | private static final Logger log = LoggerFactory.getLogger(StockAiServiceImpl.class); |
| | | |
| | | @Autowired |
| | | StockAiMapper stockAiMapper; |
| | | @Autowired |
| | | StockAiOrderMapper stockAiOrderMapper; |
| | | @Autowired |
| | | ISiteProductService iSiteProductService; |
| | | @Autowired |
| | | IUserService iUserService; |
| | | @Autowired |
| | | IUserAssetsServices iUserAssetsServices; |
| | | @Autowired |
| | | ExchangeRateRepository exchangeRateRepository; |
| | | |
| | | /** |
| | | * 获取上架ai产品 |
| | | * @param pageNum |
| | | * @param pageSize |
| | | * @return |
| | | */ |
| | | @Override |
| | | public ServerResponse getStockAiList(Integer pageNum, Integer pageSize) { |
| | | try { |
| | | PageHelper.startPage(pageNum, pageSize); |
| | | List<StockAI> stockAiList = stockAiMapper.selectList(new QueryWrapper<StockAI>().eq("status", EStockAIStatus.online.getValue())); |
| | | // 获取分页信息 |
| | | PageInfo<StockAI> pageInfo = new PageInfo<>(stockAiList); |
| | | return ServerResponse.createBySuccess(pageInfo); |
| | | } catch (Exception ex) { |
| | | log.error("StockAiService getStockAiList error", ex); |
| | | } |
| | | return ServerResponse.createByError(); |
| | | } |
| | | |
| | | /** |
| | | * 购买ai产品 |
| | | * @param id |
| | | * @param buyNum |
| | | * @return |
| | | */ |
| | | @Override |
| | | @Transactional |
| | | public ServerResponse buyStockAi(Long id, BigDecimal buyNum, HttpServletRequest request) { |
| | | try { |
| | | User user = iUserService.getCurrentUser(request); |
| | | if (user == null ){ |
| | | return ServerResponse.createBySuccessMsg("請先登錄"); |
| | | } |
| | | synchronized (user.getId()){ |
| | | SiteProduct siteProduct = iSiteProductService.getProductSetting(); |
| | | if (siteProduct.getRealNameDisplay() && user.getIsActive() != 2) { |
| | | return ServerResponse.createByErrorMsg("订单失败,请先实名认证", request); |
| | | } |
| | | |
| | | if (siteProduct.getRealNameDisplay() && user.getIsLock().intValue() == 1) { |
| | | return ServerResponse.createByErrorMsg("订单失败,帐户已被锁定", request); |
| | | } |
| | | |
| | | StockAI stockAI = stockAiMapper.selectById(id); |
| | | if (stockAI == null) { |
| | | return ServerResponse.createByErrorMsg("订单失败,AI股票不存在", request); |
| | | } |
| | | |
| | | if(buyNum.compareTo(stockAI.getMinPrice()) < 0){ |
| | | return ServerResponse.createByErrorMsg("最低购买数量" + stockAI.getMinPrice(), request); |
| | | } |
| | | //获取用户账户 |
| | | UserAssets userAssets = iUserAssetsServices.assetsByTypeAndUserId(EStockType.MX.getCode(), user.getId()); |
| | | BigDecimal finalBuyNum = buyNum; //购买金额 |
| | | //如果不是墨西哥币需要转换金额 |
| | | if (!stockAI.getStockType().equals(EStockType.US.getCode())) { |
| | | EStockType stockType = EStockType.getEStockTypeByCode(stockAI.getStockType()); |
| | | ExchangeRate exchangeRate = exchangeRateRepository.findExchangeRateByCurrencyAndConversionCurrency(stockType.getSymbol(), EStockType.MX.getSymbol()) |
| | | .orElse(null); |
| | | if (exchangeRate == null) { |
| | | return ServerResponse.createByErrorMsg("当前货币汇率无法转换"); |
| | | } |
| | | //转换为墨西哥币 |
| | | buyNum = iUserAssetsServices.exchangeAmountByRate(buyNum, exchangeRate.getRata()); |
| | | } |
| | | if(buyNum.compareTo(userAssets.getAvailableBalance()) > 0){ |
| | | return ServerResponse.createByErrorMsg("可用余额不足" + userAssets.getAvailableBalance(), request); |
| | | } |
| | | |
| | | if(userAssets.getAmountToBeCovered().compareTo(BigDecimal.ZERO) > 0){ |
| | | return ServerResponse.createByErrorMsg("请先缴清待补资金", request); |
| | | } |
| | | |
| | | StockAIOrder stockAIOrder = new StockAIOrder(); |
| | | stockAIOrder.setUserId(user.getId()); |
| | | stockAIOrder.setStockAiId(id); |
| | | stockAIOrder.setBuyDate(new Date()); |
| | | stockAIOrder.setBuyAmount(finalBuyNum); |
| | | stockAIOrder.setRemainAmount(finalBuyNum); |
| | | stockAIOrder.setRealEarning(BigDecimal.valueOf(0)); |
| | | stockAIOrder.setStatus(EStockAIOrderStatus.wait.getStatus()); //等待审核 |
| | | stockAiOrderMapper.insert(stockAIOrder); |
| | | iUserAssetsServices.aiAvailableBalanceChange(userAssets, EUserAssets.BUY_AI, buyNum); |
| | | return ServerResponse.createBySuccessMsg("下单成功", request); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("StockAiService buyStockAiList error", e); |
| | | } |
| | | return ServerResponse.createByError(); |
| | | } |
| | | |
| | | /** |
| | | * 获取ai产品订单 |
| | | * @param pageNum |
| | | * @param pageSize |
| | | * @param status |
| | | * @param request |
| | | * @return |
| | | */ |
| | | @Override |
| | | public ServerResponse getStockAiOrderList(Integer pageNum, Integer pageSize, String status, HttpServletRequest request) { |
| | | try { |
| | | User user = iUserService.getCurrentUser(request); |
| | | if (user == null ){ |
| | | return ServerResponse.createBySuccessMsg("請先登錄"); |
| | | } |
| | | |
| | | PageHelper.startPage(pageNum, pageSize); |
| | | List<StockAIOrder> stockAIOrders = stockAiOrderMapper.selectList(new QueryWrapper<StockAIOrder>() |
| | | .eq("user_id", user.getId()) |
| | | .eq(status != null && !status.isEmpty(), "status", status)); |
| | | // 获取分页信息 |
| | | PageInfo<StockAIOrder> pageInfo = new PageInfo<>(stockAIOrders); |
| | | return ServerResponse.createBySuccess(pageInfo); |
| | | } catch (Exception e) { |
| | | log.error("StockAiService getStockAiOrderList error", e); |
| | | } |
| | | return ServerResponse.createByError(); |
| | | } |
| | | } |
| | |
| | | } |
| | | extracted(userAssets); |
| | | } |
| | | |
| | | |
| | | public Boolean aiAvailableBalanceChange(UserAssets userAssets, EUserAssets eUserAssets, BigDecimal amount) { |
| | | String before = userAssets.getAvailableBalance().toString(); |
| | | if (eUserAssets.getCode().equals(EUserAssets.BUY_AI.getCode())) { |
| | | //冻结金额 |
| | | userAssets.setFreezeMoney(userAssets.getFreezeMoney().add(amount)); |
| | | //扣除可用金额 |
| | | userAssets.setAvailableBalance(userAssets.getAvailableBalance().add(amount.negate())); |
| | | } |
| | | String after = userAssets.getAvailableBalance().toString(); |
| | | MoneyLog moneyLog = new MoneyLog(); |
| | | moneyLog.setDescs(eUserAssets.getDesc()); |
| | | moneyLog.setBeFore(before); |
| | | moneyLog.setAfter(after); |
| | | moneyLog.setAmount(amount.toString()); |
| | | moneyLog.setAccectType(userAssets.getAccectType()); |
| | | moneyLog.setType(eUserAssets.getCode()); |
| | | moneyLog.setUserId(userAssets.getId()+""); |
| | | moneyLog.setSymbol(EStockType.getEStockTypeByCode(userAssets.getAccectType()).getSymbol()); |
| | | moneyLog.setCreateTime(new Date()); |
| | | moneyLogMapper.insert(moneyLog); |
| | | return userAssetsMapper.updateById(userAssets)>1; |
| | | } |
| | | |
| | | @Override |
| | | public BigDecimal exchangeAmountByRate(BigDecimal amount, BigDecimal rate) { |
| | | return amount.multiply(rate); |
| | | } |
| | | } |