update 租赁系统1.1.0新版本业务完成

This commit is contained in:
yyb
2025-12-12 15:13:53 +08:00
parent cdc0cc8212
commit 6983e8657b
55 changed files with 1947 additions and 399 deletions

View File

@@ -3,13 +3,14 @@ package com.m2pool.lease.controller;
import com.m2pool.lease.dto.PageResult;
import com.m2pool.lease.dto.Result;
import com.m2pool.lease.dto.UserOwnedProductDto;
import com.m2pool.lease.dto.v2.CoinAndAlgorithmListDto;
import com.m2pool.lease.dto.v2.MiningInfoDto;
import com.m2pool.lease.dto.v2.PurchasedMachineDto;
import com.m2pool.lease.dto.v2.PurchasedMachineListDto;
import com.m2pool.lease.service.LeaseOrderInfoService;
import com.m2pool.lease.vo.BaseVo;
import com.m2pool.lease.vo.OrderAndCodeVo;
import com.m2pool.lease.vo.UserOwnedProductVo;
import com.m2pool.lease.vo.PageVo;
import com.m2pool.lease.vo.v2.CoinAndAlgorithmVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -34,7 +35,6 @@ import java.util.List;
@RequestMapping("/v2/order/info")
public class LeaseOrderInfoV2Controller {
@Resource
private LeaseOrderInfoService leaseOrderInfoService;
@@ -57,24 +57,21 @@ public class LeaseOrderInfoV2Controller {
}
@ApiOperation("用户查询当前自身已购商品列表")
@PostMapping("/getOwnedList")
public PageResult<UserOwnedProductDto> getOwnedList(@RequestBody(required = false) UserOwnedProductVo userOwnedProductVo) {
if (userOwnedProductVo == null){
userOwnedProductVo = new UserOwnedProductVo();
@ApiOperation("买家:已购矿机列表")
@PostMapping("/getPurchasedItems")
public PageResult<PurchasedMachineListDto> getPurchasedItems(@RequestBody(required = false) PageVo pageVo) {
if (pageVo == null){
pageVo = new PageVo();
}
//return leaseOrderInfoService.getOwnedList(userOwnedProductVo);
return null;
return leaseOrderInfoService.getPurchasedItems(pageVo);
}
@ApiOperation("根据id查询当前自身已购商品详情")
@PostMapping("/getOwnedById")
public Result<UserOwnedProductDto> getOwnedById(@RequestBody BaseVo baseVo) {
//return leaseOrderInfoService.getOwnedById(baseVo);
return null;
@ApiOperation("买家:根据id查询已购矿机详情")
@PostMapping("/getPurchasedInfo")
public Result<PurchasedMachineDto> getPurchasedInfo(@RequestBody BaseVo baseVo) {
return leaseOrderInfoService.getPurchasedInfo(baseVo);
}
}

View File

@@ -88,6 +88,18 @@ public class LeaseProductMachineV2Controller {
return leaseMachineService.deleteMachine(baseVo);
}
@ApiOperation("获取租赁系统支持的币种")
@GetMapping("/getSupportCoin")
public Result<List<String>> getSupportCoin() {
return leaseMachineService.getSupportCoin();
}
@ApiOperation("根据币种获取租赁系统支持的算法")
@GetMapping("/getSupportAlgo")
public Result<List<String>> getSupportAlgo(@RequestParam String coin) {
return leaseMachineService.getSupportAlgo(coin);
}

View File

@@ -100,8 +100,8 @@ public class LeaseUserController {
@GetMapping("/downloadClient")
@ApiOperation(value = "卖家:下载客户端")
public void downloadClient(@RequestParam String userEmail,HttpServletRequest request, HttpServletResponse response){
leaseUserService.downloadClient(userEmail,request, response);
public void downloadClient(@RequestParam String userEmail,@RequestParam String type,HttpServletRequest request, HttpServletResponse response){
leaseUserService.downloadClient(userEmail,type,request, response);
}
@GetMapping("/getClientVersion")

View File

@@ -90,6 +90,9 @@ public class OrderInfoDto {
@ApiModelProperty(value = "未支付金额")
private Double noPayAmount;
@ApiModelProperty(value = "店铺名称")
private String shopName;
//---------------------------------------------- 一个普通订单对应一个支付订单多个封装下面四个字段为一个对象并返回list- ----------------------------------------------------------------------
/**
* 订单详情

View File

@@ -84,6 +84,9 @@ public class OrderItemDto {
@ApiModelProperty(value = "租赁矿机数量")
private Integer numbers;
@ApiModelProperty(value = "矿机类型 0 ASIC 1 GPU")
private Integer type;
}

View File

@@ -1,6 +1,5 @@
package com.m2pool.lease.dto;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
@@ -9,7 +8,6 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* <p>

View File

@@ -32,4 +32,9 @@ public class CoinAndAlgorithmDto {
*/
private String machineIds;
/**
* 是否生效
*/
private Boolean isEffect;
}

View File

@@ -0,0 +1,66 @@
package com.m2pool.lease.dto.v2;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.annotations.ApiModel;
import lombok.*;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* <p>
* GPU对应币种挖矿算力(只要gpu类型相同那么就使用这个配置)
* </p>
*
* @author yyb
* @since 2025-11-27
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MachineConfigDto {
private Long id;
/**
* 机器id
*/
private Long machineId;
/**
* gpu/asic品牌
*/
private String brand;
/**
* gpu 名称/型号
*/
private String name;
/**
* 内存单位M
*/
private Integer memory;
/**
* 支持的币种
*/
private String coin;
/**
* 算法
*/
private String algorithm;
/**
* 是否是临时信息
*/
private Boolean isTemp;
}

View File

@@ -0,0 +1,15 @@
package com.m2pool.lease.dto.v2;
import lombok.Data;
/**
* @Description 矿机是否能够上架
* @Date 2025/12/12 14:41
* @Author yyb
*/
@Data
public class MachineEffectDto {
private Long machineId;
private Boolean effect;
}

View File

@@ -17,6 +17,8 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
public class MiningConfigDto {
private Long id;
@ApiModelProperty(value = "挖矿币种")
private String coin;

View File

@@ -0,0 +1,33 @@
package com.m2pool.lease.dto.v2;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import scala.math.BigDecimal;
import java.time.LocalDateTime;
/**
* <p>
* GPU对应币种挖矿算力(只要gpu类型相同那么就使用这个配置)
* </p>
*
* @author yyb
* @since 2025-11-27
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "矿机对应实时算力放回对象",value = "MiningHashrateInfoDto" )
public class MiningHashrateInfoDto {
@ApiModelProperty(value = "算力")
private BigDecimal power;
@ApiModelProperty(value = "记录时间")
private LocalDateTime recordTime;
}

View File

@@ -29,8 +29,8 @@ public class OrderMiningInfoDto {
@ApiModelProperty(value = "是否钱包挖矿 0 不支持 1 支持")
private Boolean walletMining;
@ApiModelProperty(value = "矿机id列表")
private List<Long> machineIds;
@ApiModelProperty(value = "矿机id")
private Long machineId;
@ApiModelProperty(value = "挖矿钱包(用户自己输入)")
private String walletAddress;

View File

@@ -0,0 +1,39 @@
package com.m2pool.lease.dto.v2;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Date;
/**
* <p>
* 订单状态修改返回对象
* </p>
*
* @author yyb
* @since 2025-07-23
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OrderTimeInfoDto {
private Long id;
private Long orderId;
private Long orderItemId;
private Integer leaseTime;
private LocalDateTime startTime;
private BigDecimal price;
private Integer numbers;
}

View File

@@ -0,0 +1,76 @@
package com.m2pool.lease.dto.v2;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* <p>
* 挖矿中订单
* </p>
*
* @author yyb
* @since 2025-12-02
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "买家已购矿机返回对象",value = "PurchasedMachineDto" )
public class PurchasedMachineDto {
@ApiModelProperty(value = "id")
private Long id;
//@ApiModelProperty(value = "订单编号")
//private String orderNumbers;
@ApiModelProperty(value = "矿池所挖币种")
private String coin;
@ApiModelProperty(value = "币种对应算法")
private String algorithm;
@ApiModelProperty(value = "矿池名称")
private String pool;
@ApiModelProperty(value = "矿池挖矿地址")
private String poolUrl;
@ApiModelProperty(value = "矿池挖矿账户")
private String poolUser;
@ApiModelProperty(value = "矿工号")
private String workerId;
@ApiModelProperty(value = "收款钱包")
private String walletAddress;
@ApiModelProperty(value = "挖矿信息页面地址")
private String watchUrl;
@ApiModelProperty(value = "0 租约已到期 1挖矿中 2卖家矿机启动中")
private Boolean status;
@ApiModelProperty(value = "挖矿开始时间")
private LocalDateTime startTime;
@ApiModelProperty(value = "挖矿结束时间")
private LocalDateTime endTime;
@ApiModelProperty(value = "算力信息")
private MiningHashrateInfoDto miningHashrateInfo;
}

View File

@@ -0,0 +1,72 @@
package com.m2pool.lease.dto.v2;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;
import java.time.LocalDateTime;
/**
* <p>
* 挖矿中订单
* </p>
*
* @author yyb
* @since 2025-12-02
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "买家已购矿机返回对象",value = "PurchasedMachineListDto" )
public class PurchasedMachineListDto {
@ApiModelProperty(value = "id")
private Long id;
//@ApiModelProperty(value = "订单编号")
//private String orderNumbers;
@ApiModelProperty(value = "矿池所挖币种")
private String coin;
@ApiModelProperty(value = "币种对应算法")
private String algorithm;
@ApiModelProperty(value = "矿池名称")
private String pool;
@ApiModelProperty(value = "矿池挖矿地址")
private String poolUrl;
@ApiModelProperty(value = "矿池挖矿账户")
private String poolUser;
@ApiModelProperty(value = "矿工号")
private String workerId;
@ApiModelProperty(value = "收款钱包")
private String walletAddress;
@ApiModelProperty(value = "挖矿信息页面地址")
private String watchUrl;
@ApiModelProperty(value = "0 租约已到期 1挖矿中 2卖家矿机启动中")
private Integer status;
@ApiModelProperty(value = "挖矿开始时间")
private LocalDateTime startTime;
@ApiModelProperty(value = "挖矿结束时间")
private LocalDateTime endTime;
private Long orderItemId;
}

View File

@@ -0,0 +1,61 @@
package com.m2pool.lease.dto.v2;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
* <p>
* GPU对应币种挖矿算力(只要gpu类型相同那么就使用这个配置)
* </p>
*
* @author yyb
* @since 2025-11-27
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RealHashrateInfoDto {
/**
* 订单项ID
*/
private Long orderItemId;
/**
* 算力
*/
private BigDecimal power;
/**
* 矿池名称
*/
private String pool;
/**
* 收款钱包
*/
private String walletAddress;
/**
* 矿工名称
*/
private String miner;
/**
* 币种
*/
private String coin;
/**
* 币种对应算法
*/
private String algorithm;
}

View File

@@ -85,6 +85,9 @@ public class SellerMachineInfoDto {
@ApiModelProperty(value = "矿机客户端在离线情况 0 离线 1 在线")
private Boolean onlineStatus;
@ApiModelProperty(value = "矿机是否能够上架 0 不能 1 能")
private Boolean effect;
//@ApiModelProperty(value = "gpu和asic对应币种算力和月收益信息")
//private List<PowerIncomeInfoDto> powerIncomeInfoList;

View File

@@ -1,11 +1,14 @@
package com.m2pool.lease.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.*;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* <p>
@@ -95,5 +98,7 @@ public class LeaseMachineConfig implements Serializable {
*/
private Boolean del;
@TableField(exist = false)
private LocalDateTime effectTime;
}

View File

@@ -0,0 +1,105 @@
package com.m2pool.lease.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.*;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* <p>
* GPU对应币种挖矿算力临时表(主机gpu信息变更临时表)
* </p>
*
* @author yyb
* @since 2025-12-09
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class LeaseMachineTempConfig implements Serializable {
private static final long serialVersionUID=1L;
/**
* id
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 机器id
*/
private Long machineId;
/**
* gpu/asic品牌
*/
private String brand;
/**
* gpu/asic 名称/型号
*/
private String name;
/**
* 内存单位M
*/
private Integer memory;
/**
* 0 禁用 1 启用
*/
private Boolean status;
/**
* 支持的币种
*/
private String coin;
/**
* 算力
*/
private BigDecimal hashrate;
/**
* 算法
*/
private String algorithm;
/**
* 币种图片
*/
private String icon;
/**
* 算力单位 GH/s TH/s PH/s等
*/
private String unit;
/**
* 月收益usdt
*/
private BigDecimal monthIncome;
/**
* 功耗 单位kw/h
*/
private BigDecimal powerDissipation;
/**
* 逻辑删除字段
*/
private Boolean del;
/**
* 创建时间
*/
private LocalDateTime effectTime;
}

View File

@@ -71,6 +71,11 @@ public class LeaseOrderInfo implements Serializable {
*/
private BigDecimal fee;
/**
* 店铺名称
*/
private String shopName;
@TableField(exist = false)
private String chainAndCoinAndShopIdKey;

View File

@@ -170,11 +170,11 @@ public class LeaseOrderItem implements Serializable {
private Integer numbers;
/**
* 商品类型 0 矿机 1 算力
* 0 矿机 asic 1 矿池 gpu
*/
@TableField(exist = false)
private Integer type;
private Boolean type;
}

View File

@@ -30,11 +30,23 @@ public class LeaseOrderMining implements Serializable {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 机器 ID
*/
private Long machineId;
/**
* 用户 ID
*/
private String userId;
/**
* 订单 ID
*/
private Long orderId;
private Long orderItemId;
/**
* 币种
*/
@@ -55,6 +67,11 @@ public class LeaseOrderMining implements Serializable {
*/
private String poolUrl;
/**
* 挖矿账户
*/
private String poolUser;
/**
* 矿工号
*/

View File

@@ -6,6 +6,7 @@ import com.m2pool.lease.netty.message.GpuMessage;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.Collection;
import java.util.List;
/**
@@ -23,5 +24,5 @@ public interface LeaseGpuConfigMapper extends BaseMapper<LeaseGpuConfig> {
* @param gpuMessages
* @return
*/
int insertOrUpdate(@Param("list") List<GpuMessage> gpuMessages);
int insertOrUpdate(@Param("list") Collection<GpuMessage> gpuMessages);
}

View File

@@ -116,4 +116,34 @@ public interface LeaseMachineMapper extends BaseMapper<LeaseMachine> {
@MapKey("id")
Map<Long, MiningConfigDto> getCoinAndAlgoById(@Param("ids") List<Long> ids);
/**
* 获取矿机详情信息含临时信息
* @param machineId
* @return
*/
List<MachineConfigDto> getEffectMachineAndTempMachineList(@Param("machineId") Long machineId);
/**
* 获取租赁系统中支持的币种(挖矿软件支持 + 矿池支持的交集)
* @return
*/
List<String> getSupportCoin();
/**
* 获取租赁系统中支持的算力(挖矿软件支持 + 矿池支持的交集)
* @return
*/
List<String> getSupportAlgo(@Param("coin") String coin);
/**
* 检测矿机是否能够上架
* @param ids
* @return
*/
@MapKey("machineId")
Map<Long, MachineEffectDto> checkIsEffect(@Param("list") List<SellerMachineInfoDto> ids);
}

View File

@@ -35,7 +35,7 @@ public interface LeaseMachinePriceMapper extends BaseMapper<LeaseMachinePrice> {
* @param orderInfoVoList
* @return
*/
@MapKey("productMachineId")
@MapKey("machineId")
Map<Long, LeaseMachinePrice> getOrderTotalPriceGroupByChainAndCoin(@Param("list") List<OrderInfoVo> orderInfoVoList);
}

View File

@@ -0,0 +1,35 @@
package com.m2pool.lease.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.m2pool.lease.entity.LeaseMachineConfig;
import com.m2pool.lease.entity.LeaseMachineTempConfig;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* <p>
* GPU对应币种挖矿算力临时表(主机gpu信息变更临时表) Mapper 接口
* </p>
*
* @author yyb
* @since 2025-12-09
*/
@Mapper
public interface LeaseMachineTempConfigMapper extends BaseMapper<LeaseMachineTempConfig> {
/**
* 批量插入
* @param needUpdateInfo
* @return
*/
int insertBatch(@Param("list") List<LeaseMachineConfig> needUpdateInfo);
/**
* 批量插入
* @param needUpdateInfo
* @return
*/
int insertBatchByTempList(@Param("list") List<LeaseMachineTempConfig> needUpdateInfo);
}

View File

@@ -1,7 +1,12 @@
package com.m2pool.lease.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.m2pool.lease.dto.v2.CoinAndAlgorithmDto;
import com.m2pool.lease.entity.LeaseMiningSoftwareConfig;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* <p>
@@ -11,6 +16,12 @@ import com.m2pool.lease.entity.LeaseMiningSoftwareConfig;
* @author yyb
* @since 2025-11-27
*/
@Mapper
public interface LeaseMiningSoftwareConfigMapper extends BaseMapper<LeaseMiningSoftwareConfig> {
/**
* 查询支持的币种和算法
* @param miningsofts
* @return
*/
List<CoinAndAlgorithmDto> selectSupportAlgorithm(@Param("miningsofts") List<String> miningsofts);
}

View File

@@ -55,4 +55,7 @@ public interface LeaseOrderInfoMapper extends BaseMapper<LeaseOrderInfo> {
@Pool2DB
BigDecimal getCoinPrice(@Param("coin") String coin);
}

View File

@@ -2,6 +2,7 @@ package com.m2pool.lease.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.m2pool.lease.dto.OrderStatusDto;
import com.m2pool.lease.dto.v2.OrderTimeInfoDto;
import com.m2pool.lease.entity.LeaseOrderItem;
import com.m2pool.lease.entity.LeasePaymentRecord;
import com.m2pool.lease.mq.message.RabbitmqPayAutoReturnInfoMessage;
@@ -71,4 +72,12 @@ public interface LeaseOrderItemMapper extends BaseMapper<LeaseOrderItem> {
*/
List<LeaseOrderItem> getOrderItemByOrderIds(@Param("orderIds") Set<Long> orderIds);
/**
* 获取订单详情id
* @return
*/
List<OrderTimeInfoDto> getOneDayApartOrder();
}

View File

@@ -1,7 +1,15 @@
package com.m2pool.lease.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.m2pool.common.datasource.annotation.MiningDB;
import com.m2pool.lease.dto.v2.*;
import com.m2pool.lease.entity.LeaseOrderMining;
import com.m2pool.lease.netty.message.ClientConfigurationMining;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
import java.util.List;
/**
* <p>
@@ -11,6 +19,75 @@ import com.m2pool.lease.entity.LeaseOrderMining;
* @author yyb
* @since 2025-12-02
*/
@Mapper
public interface LeaseOrderMiningMapper extends BaseMapper<LeaseOrderMining> {
/**
* 批量插入
*
* @param orderMiningList
* @return
*/
int insertBatch(@Param("list") List<LeaseOrderMining> orderMiningList);
/**
* 获取矿池正在挖矿中的信息
*
* @param clientConfigurationMining
* @return
*/
int updateOrderMining(@Param("userEmail") String userEmail,@Param("clientConfigurationMining") ClientConfigurationMining clientConfigurationMining);
/**
* 获取用户购买的机器列表
* @param userEmail
* @return
*/
List<PurchasedMachineListDto> getPurchasedItems(@Param("userEmail") String userEmail);
/**
* 根据id获取用户购买的机器信息
* @param id
* @return
*/
PurchasedMachineDto getPurchasedInfo(@Param("id") Long id);
/**
* 获取最近一次的算力
* @param pool
* @param wallet
* @param coin
* @param miner
* @return
*/
@MiningDB
MiningHashrateInfoDto getRecentlyHashrate(@Param("pool") String pool,
@Param("wallet") String wallet,
@Param("coin") String coin,
@Param("miner") String miner);
/**
* 获取最近24小时的算力
* @return
*/
@MiningDB
List<RealHashrateInfoDto> getRecently24HourHashrate(@Param("list") List<PurchasedMachineListDto> list,@Param("tableName") String tableName,@Param("now") Date now);
/**
* 根据orderId集合获取挖矿中的信息
* @param orderIds
* @return
*/
List<PurchasedMachineListDto> getMiningInfoByOrderId(@Param("orderIds") List<Long> orderIds);
/**
* 检查是否存在挖矿中矿工
* @param list
* @return
*/
List<OrderMiningInfoDto> checkMiningMinersExist(@Param("list") List<OrderMiningInfoDto> list);
}

View File

@@ -69,8 +69,8 @@ public class ChannelManager {
}
return null;
}
public static boolean sendToClient(String mac, Object message) {
Channel channel = getChannelByMac(mac);
if (channel != null && channel.isActive()) {
channel.writeAndFlush(message).syncUninterruptibly();

View File

@@ -20,6 +20,8 @@ public class MessageToJsonEncoder extends MessageToByteEncoder<Object> {
}
// 明确使用 UTF-8 编码创建 ByteBuf
ByteBuf buffer = Unpooled.copiedBuffer(jsonStr, java.nio.charset.StandardCharsets.UTF_8);
//客户端粘包半包问题 按换行符分割
buffer.writeBytes("\n".getBytes());
try {
// 将生成的 ByteBuf 内容写入输出的 ByteBuf
out.writeBytes(buffer);

View File

@@ -1,11 +1,14 @@
package com.m2pool.lease.netty.handler;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.m2pool.common.core.utils.ip.IpUtils;
import com.m2pool.lease.entity.LeaseShop;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.m2pool.lease.dto.v2.CoinAndAlgorithmDto;
import com.m2pool.lease.dto.v2.MachineConfigDto;
import com.m2pool.lease.entity.*;
import com.m2pool.lease.mapper.*;
import com.m2pool.lease.netty.message.*;
import com.m2pool.lease.service.LeaseMachineConfigService;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
@@ -13,27 +16,30 @@ import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import static com.m2pool.lease.netty.message.MethodConstant.PING;
@Component
@ChannelHandler.Sharable
public class ServerChannelHandler extends SimpleChannelInboundHandler<ClientMessage<Object>> {
/**
* 心跳检测
*/
public static Map<Channel, Boolean> heartbeatMap = new ConcurrentHashMap<Channel, Boolean>();
@Resource
private TransactionTemplate transactionTemplate;
@Resource
private LeaseShopMapper leaseShopMapper;
@Resource
private LeaseMachineConfigMapper leaseMachineConfigMapper;
private LeaseMachineConfigService leaseMachineConfigService;
@Resource
private LeaseMachineMapper leaseMachineMapper;
@@ -44,6 +50,15 @@ public class ServerChannelHandler extends SimpleChannelInboundHandler<ClientMess
@Resource
private LeaseMiningSoftwareConfigMapper leaseMiningSoftwareConfigMapper;
@Resource
private LeaseMachineTempConfigMapper leaseMachineTempConfigMapper;
@Resource
private LeaseOrderItemMapper leaseOrderItemMapper;
@Resource
private LeaseOrderMiningMapper leaseOrderMiningMapper;
/**
* 接收到客户端的信息
*
@@ -53,7 +68,6 @@ public class ServerChannelHandler extends SimpleChannelInboundHandler<ClientMess
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, ClientMessage<Object> msg) throws Exception {
//处理
if (MethodConstant.AUTH_MACHINE_CODE.equals(msg.getMethod())){
System.out.println("Netty tcp 服务端收到客户端下载初始化GPU消息");
@@ -73,75 +87,212 @@ public class ServerChannelHandler extends SimpleChannelInboundHandler<ClientMess
* @param msg
*/
private void handlerDownloadClientMessage(ChannelHandlerContext ctx,ClientMessage<Object> msg){
boolean b = checkMessage( ctx,msg);
if (b){
String id = msg.getId();
String[] split = id.split("\\.");
if (split.length == 2 && checkMessage(ctx,id,split[0])){
Object params = msg.getParams();
//if(params instanceof Map){
// //map key value类型校验
// Map<?, ?> rawMap = (Map<?, ?>) params;
// Map<String, GpuMessage> gpuMessageMap = new HashMap<>();
// boolean b1 = mapCheck(rawMap, gpuMessageMap);
// if (b1){
// List<GpuMessage> gpuMessagesList = new ArrayList<>(gpuMessageMap.values());
// //GPU配置信息配置
// int insertOrUpdate = leaseGpuConfigMapper.insertOrUpdate(gpuMessagesList);
// //TODO 挖矿软件公共配置
// //leaseMiningSoftwareConfigMapper.insertOrUpdate();
// //TODO 新增主机信息
// }
//}
if (params instanceof GpuAndSoftMessage){
GpuAndSoftMessage gpuAndSoftMessage = (GpuAndSoftMessage) params;
Map<String, GpuMessage> gpus = gpuAndSoftMessage.getGpus();
List<String> miningsofts = gpuAndSoftMessage.getMiningsofts();
Collection<GpuMessage> gpuMessagesList = gpus.values();
//GPU配置信息配置
//int insertOrUpdate = leaseGpuConfigMapper.insertOrUpdate(gpuMessagesList);
//TODO 挖矿软件公共配置
//leaseMiningSoftwareConfigMapper.insertOrUpdate();
//TODO 新增主机信息
//挖矿软件公共配置
List<CoinAndAlgorithmDto> supportAlgorithmAndCoin = leaseMiningSoftwareConfigMapper.selectSupportAlgorithm(miningsofts);
// 新增主机相关信息
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
LeaseMachine leaseMachine = leaseMachineMapper.selectOne(new LambdaQueryWrapper<LeaseMachine>()
.eq(LeaseMachine::getHostMac, split[1]));
if (leaseMachine == null){
LeaseShop shop = getShop(split[0]);
leaseMachine = LeaseMachine.builder()
.shopId(shop.getId())
.hostMac(split[1])
.type(true)
.state(true)
.saleNumbers(0).build();
//GPU配置信息配置
leaseGpuConfigMapper.insertOrUpdate(gpuMessagesList);
List<LeaseMachineConfig> leaseMachineConfigs = packageInsert(leaseMachine.getId(),gpus, supportAlgorithmAndCoin);
leaseMachineMapper.insert(leaseMachine);
leaseMachineConfigService.saveBatch(leaseMachineConfigs);
}else {
//检测对照表
List<LeaseMachineConfig> needUpdateInfo = packageInsert(leaseMachine.getId(),gpus, supportAlgorithmAndCoin);
//带检测矿机
List<MachineConfigDto> needCheckInfo = leaseMachineMapper.getEffectMachineAndTempMachineList(leaseMachine.getId());
Map<Boolean, List<MachineConfigDto>> collect = needCheckInfo.stream().collect(Collectors.groupingBy(MachineConfigDto::getIsTemp));
checkMachineAndInsertOrUpdate(leaseMachine.getId(),needUpdateInfo, collect);
}
} catch (Exception e) {
// 回滚事务
status.setRollbackOnly();
throw e;
}
}
});
}
}
}
/**
* 封装矿机子表信息
* 矿机详情表新增数据
* @return
*/
public List<LeaseMachineConfig> packageInsert(Long machineId,Map<String, GpuMessage> gpus,List<CoinAndAlgorithmDto> supportAlgorithmAndCoin){
List<LeaseMachineConfig> list = new ArrayList<>();
for (GpuMessage value : gpus.values()) {
list.addAll(supportAlgorithmAndCoin.stream().map(item-> LeaseMachineConfig.builder()
.machineId(machineId)
.coin(item.getCoin())
.algorithm(item.getAlgorithm())
.brand(value.getBrand())
.name(value.getModel())
.memory(value.getMem())
.status(item.getIsEffect())
.unit("MH/S")
.status(true)
.del(false)
.build()).collect(Collectors.toList()));
}
return list;
}
/**
* 处理客户端发送的挖矿开始信息
* 检测主机上gpu是否修改 并且根据情况做出相应的增删改操作
* @param machineId 检测的主机id
* @param needUpdateInfo 检测对照组
* @param needCheckInfo 检测组
* @return
*/
public void checkMachineAndInsertOrUpdate(Long machineId,List<LeaseMachineConfig> needUpdateInfo,Map<Boolean, List<MachineConfigDto>> needCheckInfo){
//先检测该矿机是否存在合约
LeaseOrderItem leaseOrderItem = leaseOrderItemMapper.selectOne(new LambdaQueryWrapper<LeaseOrderItem>()
.eq(LeaseOrderItem::getProductMachineId, machineId)
.eq(LeaseOrderItem::getStatus, 1)
);
if (leaseOrderItem != null){
LocalDateTime effectTime = leaseOrderItem.getCreateTime().plusDays(leaseOrderItem.getLeaseTime());
needUpdateInfo.forEach(item-> item.setEffectTime(effectTime));
List<MachineConfigDto> realMachineList = needCheckInfo.get(false);
List<MachineConfigDto> tempMachineList = needCheckInfo.get(true);
boolean realCheck = compare(needUpdateInfo, realMachineList);
//主表信息和本次客户端发送消息不相同 + 并且临时表没有信息
if(!realCheck && tempMachineList.isEmpty()){
//直接向临时表中加入数据
leaseMachineTempConfigMapper.insertBatch(needUpdateInfo);
return;
}
boolean tempCheck = compare(needUpdateInfo, tempMachineList);
if (!realCheck && !tempCheck){
//删除临时表数据 + 新增临时表数据
leaseMachineTempConfigMapper.delete(new LambdaQueryWrapper<LeaseMachineTempConfig>()
.eq(LeaseMachineTempConfig::getMachineId, machineId));
leaseMachineTempConfigMapper.insertBatch(needUpdateInfo);
return;
}
//ABA
if (realCheck && !tempCheck){
//删除临时表数据
leaseMachineTempConfigMapper.delete(new LambdaQueryWrapper<LeaseMachineTempConfig>()
.eq(LeaseMachineTempConfig::getMachineId, machineId));
}
}else{
//删除主表信息 + 新增主表数据
leaseMachineConfigService.remove(new LambdaUpdateWrapper<LeaseMachineConfig>()
.eq(LeaseMachineConfig::getMachineId, machineId));
leaseMachineConfigService.saveBatch(needUpdateInfo);
}
}
public static boolean compare(List<LeaseMachineConfig> needUpdateInfo,
List<MachineConfigDto> needCheckInfo) {
// 如果数量不相等直接返回false
if (needUpdateInfo.size() != needCheckInfo.size()) {
return false;
}
// 创建两个列表的副本用于比较
List<LeaseMachineConfig> list1 = new ArrayList<>(needUpdateInfo);
List<MachineConfigDto> list2 = new ArrayList<>(needCheckInfo);
// 比较每个元素
Iterator<LeaseMachineConfig> it1 = list1.iterator();
while (it1.hasNext()) {
LeaseMachineConfig config1 = it1.next();
boolean foundMatch = false;
Iterator<MachineConfigDto> it2 = list2.iterator();
while (it2.hasNext()) {
MachineConfigDto config2 = it2.next();
// 比较指定字段
if (Objects.equals(config1.getBrand(), config2.getBrand()) &&
Objects.equals(config1.getName(), config2.getName()) &&
Objects.equals(config1.getMemory(), config2.getMemory()) &&
Objects.equals(config1.getCoin(), config2.getCoin()) &&
Objects.equals(config1.getAlgorithm(), config2.getAlgorithm())) {
it2.remove();
foundMatch = true;
break;
}
}
if (!foundMatch) {
return false;
}
it1.remove();
}
return list1.isEmpty() && list2.isEmpty();
}
/**
* 处理客户端发送的挖矿开始信息(暂时不做任何处理)
* @param msg
*/
private void handlerClientMiningMessage(ChannelHandlerContext ctx,ClientMessage<Object> msg){
Object params = msg.getParams();
boolean b = checkMessage(ctx,msg);
if (b){
String id = msg.getId();
String[] split = id.split("\\.");
if (split.length == 2 && checkMessage(ctx,id,split[0])){
Object params = msg.getParams();
if (params instanceof ClientConfigurationMining){
ClientConfigurationMining clientConfigurationMining = (ClientConfigurationMining) params;
}else{
//挖矿开始失败
//修改 当前邮箱账户 对应的该矿池的 一些基本信息 TODO 这里可以设置真实开始时间 和 状态改为1
leaseOrderMiningMapper.updateOrderMining(split[0],clientConfigurationMining);
}
}
}
/**
* 根据身份码获取店铺信息
* @param authCode
* @return
*/
public LeaseShop getShop(String authCode){
return leaseShopMapper.selectOne(new LambdaQueryWrapper<LeaseShop>()
.eq(LeaseShop::getUserEmail, authCode));
}
/**
* 检查消息的合法性
* @param msg
* @return
*/
private boolean checkMessage(ChannelHandlerContext ctx,ClientMessage<Object> msg){
String id = msg.getId();
String[] split = id.split(".");
if (split.length != 0){
LeaseShop leaseShop = leaseShopMapper.selectOne(new LambdaQueryWrapper<LeaseShop>().eq(LeaseShop::getIdentityCode, split[0]));
//不存在对应身份码的店铺 发送消息给客户端
if (leaseShop == null){
sendErrorResp(ctx,id);
return false;
}
private boolean checkMessage(ChannelHandlerContext ctx,String id,String authCode){
LeaseShop leaseShop = getShop(authCode);
//不存在对应身份码的店铺 发送消息给客户端
if (leaseShop == null){
sendErrorResp(ctx,id);
return false;
}
return true;
}
@@ -159,30 +310,6 @@ public class ServerChannelHandler extends SimpleChannelInboundHandler<ClientMess
}
/**
* Map 类型校验
* @param rawMap
* @param gpuMessageMap
*/
public boolean mapCheck(Map<?, ?> rawMap,Map<String, GpuMessage> gpuMessageMap){
for (Map.Entry<?, ?> entry : rawMap.entrySet()) {
// 校验键是否为String
if (!(entry.getKey() instanceof String)) {
return false;
}
// 校验值是否为GpuMessage
if (!(entry.getValue() instanceof GpuMessage)) {
return false;
}
String key = (String) entry.getKey();
GpuMessage value = (GpuMessage) entry.getValue();
// 校验通过放入目标Map
gpuMessageMap.put(key, value);
}
return true;
}
/**
* 活跃的、有效的通道
* 第一次连接成功后进入的方法
@@ -207,6 +334,7 @@ public class ServerChannelHandler extends SimpleChannelInboundHandler<ClientMess
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) {
System.out.println("ip:"+getIPString(ctx)+"断开连接");
ChannelManager.removeChannel(getIPString(ctx));
ctx.close();
}
@@ -228,13 +356,11 @@ public class ServerChannelHandler extends SimpleChannelInboundHandler<ClientMess
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if ("PONG".equals(msg.toString())) {
//收到客户端心跳
heartbeatMap.put(ctx.channel(), true);
} else {
// 处理其他消息
super.channelRead(ctx, msg);
ClientMessage bean = null;
if (msg instanceof String) {
bean = JSONUtil.toBean(msg.toString(), ClientMessage.class);
}
super.channelRead(ctx, bean);
}
/**
* 心跳机制,超时处理
@@ -250,20 +376,15 @@ public class ServerChannelHandler extends SimpleChannelInboundHandler<ClientMess
IdleStateEvent event = (IdleStateEvent) evt;
String ip = getIPString(ctx);
if (event.state() == IdleState.READER_IDLE) {
Boolean isActive = heartbeatMap.put(ctx.channel(), false);
//服务端发送心跳信息
if (isActive != null && !isActive){
heartbeatMap.remove(ctx.channel());
ChannelManager.removeChannel(ip);
ctx.disconnect();//断开
}else{
ctx.writeAndFlush("PING");
System.out.println("读超时发送ping到客户端");
}
} else if (event.state() == IdleState.WRITER_IDLE) {
System.out.println("客户端WRITER_IDLE 读超时: " + socketString + " ");
System.out.println("读超时,未收到任何客户都安消息,断开连接");
ChannelManager.removeChannel(ip);
ctx.disconnect();//断开
} else if (event.state() == IdleState.WRITER_IDLE) {
System.out.println("写超时发送ping到客户端");
ClientMessage ping = new ClientMessage();
ping.setMethod(PING);
ping.setId("test.03000200-0400-0500-0006-000700080009");
ctx.writeAndFlush(ping);
} else if (event.state() == IdleState.ALL_IDLE) {
System.out.println("客户端 ALL_IDLE 总超时: " + socketString);
ChannelManager.removeChannel(ip);

View File

@@ -7,6 +7,7 @@ import lombok.Data;
* @Date 2025/11/26 16:42
* @Author yyb
*/
@Data
public class GpuMessage {
/**
@@ -20,29 +21,6 @@ public class GpuMessage {
/**
* 显存大小
*/
private String mem;
private Integer mem;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public String getMem() {
return mem;
}
public void setMem(String mem) {
this.mem = mem;
}
}

View File

@@ -25,4 +25,26 @@ public class MethodConstant {
*/
public static final String AUTH_MACHINE_CODE = "auth.machineCode";
/**
* 心跳pong
*/
public static final String PONG = "pong";
/**
* 心跳ping
*/
public static final String PING = "ping";
/**
* 心跳ping间隔时间
*/
public static final Integer PING_TIME = 30;
/**
* 心跳pong间隔时间
*/
public static final Integer PONG_TIME = 60;
}

View File

@@ -7,12 +7,16 @@ import com.m2pool.lease.netty.handler.ServerChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.timeout.IdleStateHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
import static com.m2pool.lease.netty.message.MethodConstant.PING_TIME;
import static com.m2pool.lease.netty.message.MethodConstant.PONG_TIME;
@Component
public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
@@ -25,13 +29,10 @@ public class ServerChannelInitializer extends ChannelInitializer<SocketChannel>
ChannelPipeline pipeline = socketChannel.pipeline();
//IdleStateHandler心跳机制,如果超时触发Handle中userEventTrigger()方法
pipeline.addLast("idleStateHandler",
new IdleStateHandler(10, 0, 0, TimeUnit.MINUTES));
new IdleStateHandler(PONG_TIME, PING_TIME, 0, TimeUnit.MINUTES));
pipeline.addLast(new LineBasedFrameDecoder(1024));
//字符串编解码器
pipeline.addLast(new MessageToJsonDecoder(),new MessageToJsonEncoder());
//pipeline.addLast(
// new StringDecoder(),
// new StringEncoder()
//);
//自定义Handler
pipeline.addLast("serverChannelHandler", serverChannelHandler);
}

View File

@@ -11,6 +11,7 @@ import com.m2pool.lease.vo.BaseVo;
import com.m2pool.lease.vo.ProductPageVo;
import com.m2pool.lease.vo.v2.*;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@@ -77,4 +78,18 @@ public interface LeaseMachineService extends IService<LeaseMachine> {
* @return
*/
Result<String> deleteMachine(BaseVo baseVo);
/**
* 获取支持的币种
* @return
*/
Result<List<String>> getSupportCoin();
/**
* 获取支持的算法
* @return
*/
Result<List<String>> getSupportAlgo( String coin);
}

View File

@@ -1,12 +1,11 @@
package com.m2pool.lease.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.m2pool.lease.dto.OrderInfoDto;
import com.m2pool.lease.dto.PageResult;
import com.m2pool.lease.dto.PaymentRecordDto;
import com.m2pool.lease.dto.Result;
import com.m2pool.lease.dto.*;
import com.m2pool.lease.dto.v2.CoinAndAlgorithmListDto;
import com.m2pool.lease.dto.v2.MiningInfoDto;
import com.m2pool.lease.dto.v2.PurchasedMachineDto;
import com.m2pool.lease.dto.v2.PurchasedMachineListDto;
import com.m2pool.lease.entity.LeaseOrderInfo;
import com.m2pool.lease.vo.*;
import com.m2pool.lease.vo.v2.CoinAndAlgorithmVo;
@@ -93,32 +92,19 @@ public interface LeaseOrderInfoService extends IService<LeaseOrderInfo> {
*/
Result<String> addOrdersV2( OrderAndCodeVo orderAndCodeVo);
/**
* 根据订单状态查询订单列表-V2(买家)
* @param orderInfoStateVo
* 获取用户已购商品列表-V2
* @param pageVo
* @return
*/
PageResult<OrderInfoDto> getOrdersByStatusV2(OrderInfoStateVo orderInfoStateVo);
PageResult<PurchasedMachineListDto> getPurchasedItems(PageVo pageVo);
/**
* 根据订单状态查询订单列表-V2卖家
* @param orderInfoStateVo
* 根据id查询用户已购商品详情-V2
* @param baseVo
* @return
*/
PageResult<OrderInfoDto> getOrdersByStatusForSellerV2(OrderInfoStateVo orderInfoStateVo);
/**
* 根据订单id查询订单-V2
* @param orderVo
* @return
*/
Result<OrderInfoDto> getOrdersByIdsV2(OrderVo orderVo);
/**
* 获取用户选择的支付币种 获取实时币价-V2
* @param coinVo
* @return
*/
Result<BigDecimal> getCoinPriceV2(CoinVo coinVo);
Result<PurchasedMachineDto> getPurchasedInfo(BaseVo baseVo);
}

View File

@@ -98,7 +98,7 @@ public interface LeaseUserService extends IService<LeaseUser> {
* @param response
* @return
*/
void downloadClient(String userEmail,HttpServletRequest request, HttpServletResponse response);
void downloadClient(String userEmail,String type,HttpServletRequest request, HttpServletResponse response);
/**

View File

@@ -227,7 +227,12 @@ public class LeaseMachineServiceImpl extends ServiceImpl<LeaseMachineMapper, Lea
//价格配置信息
List<MachinePayTypeDto> machinePriceList = leaseMachineMapper.getMachinePriceList(success.getRows());
Map<Long, List<MachinePayTypeDto>> collect = machinePriceList.stream().collect(Collectors.groupingBy(MachinePayTypeDto::getProductMachineId));
//能否上架
Map<Long, MachineEffectDto> longMachineEffectDtoMap = leaseMachineMapper.checkIsEffect(success.getRows());
for (SellerMachineInfoDto row : success.getRows()) {
MachineEffectDto machineEffectDto = longMachineEffectDtoMap.get(row.getId());
row.setEffect(machineEffectDto != null ? machineEffectDto.getEffect() : false);
row.setPriceList(collect.get(row.getId()));
row.setCoinAndAlgoList(collect1.get(row.getId()));
}
@@ -390,7 +395,9 @@ public class LeaseMachineServiceImpl extends ServiceImpl<LeaseMachineMapper, Lea
@Override
@Transactional
public Result<String> updateGpuMachine(List<GpuMachineParamsVo> gpuMachineParamsVoList) {
//校验能否上架
for (GpuMachineParamsVo gpuMachineParamsVo : gpuMachineParamsVoList) {
//修改lease_machine主表信息
LeaseMachine machine = LeaseMachine.builder()
.id(gpuMachineParamsVo.getId())
@@ -398,7 +405,7 @@ public class LeaseMachineServiceImpl extends ServiceImpl<LeaseMachineMapper, Lea
.state(gpuMachineParamsVo.getState())
.build();
int machineUpdate = leaseMachineMapper.updateById(machine);
//修改lease_machine_price 矿机价格表信息 + gpu的配置信息是固定的不能修改
//修改lease_machine_price 矿机价格表信息
if (machineUpdate > 0){
List<MachinePayTypeVo> priceList = gpuMachineParamsVo.getPriceList();
for (MachinePayTypeVo machinePayTypeVo : priceList) {
@@ -441,4 +448,17 @@ public class LeaseMachineServiceImpl extends ServiceImpl<LeaseMachineMapper, Lea
return Result.success("删除成功");
}
@Override
public Result<List<String>> getSupportCoin() {
List<String> supportCoin = leaseMachineMapper.getSupportCoin();
return Result.success(supportCoin);
}
@Override
public Result<List<String>> getSupportAlgo(String coin) {
List<String> supportAlgo = leaseMachineMapper.getSupportAlgo(coin);
return Result.success(supportAlgo);
}
}

View File

@@ -8,6 +8,8 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.m2pool.common.core.utils.DateUtils;
import com.m2pool.common.core.utils.StringUtils;
import com.m2pool.common.redis.service.RedisService;
import com.m2pool.common.security.utils.SecurityUtils;
import com.m2pool.lease.constant.CoinCharge;
@@ -18,7 +20,6 @@ import com.m2pool.lease.entity.*;
import com.m2pool.lease.exception.OrderException;
import com.m2pool.lease.mapper.*;
import com.m2pool.lease.netty.handler.ChannelManager;
import com.m2pool.lease.netty.handler.ServerChannelHandler;
import com.m2pool.lease.service.LeaseOrderInfoService;
import com.m2pool.lease.service.LeaseOrderItemService;
import com.m2pool.lease.service.LeaseProductService;
@@ -26,14 +27,14 @@ import com.m2pool.lease.service.LeaseUserOwnedProductService;
import com.m2pool.lease.utils.UuidGeneratorUtil;
import com.m2pool.lease.vo.*;
import com.m2pool.lease.vo.v2.CoinAndAlgorithmVo;
import io.netty.channel.Channel;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -88,6 +89,9 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
@Resource
private LeaseProductMachinePriceMapper leaseProductMachinePriceMapper;
@Resource
private LeaseOrderMiningMapper leaseOrderMiningMapper;
@Resource
private RedisService redisService;
@@ -245,7 +249,7 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
.fromAddress(walletInfo.getFromAddress())
.fromChain(walletInfo.getFromChain())
.fromSymbol(walletInfo.getFromSymbol())
.type(leaseProduct.getType())
.type(leaseProduct.getType() == 1)
.build());
}
@@ -503,6 +507,7 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
.price(leaseOrderItem.getPrice())
.image(leaseOrderItem.getImage())
.numbers(leaseOrderItem.getNumbers())
.type(leaseOrderItem.getType() ? 1 : 0)
.build());
// 累加支付金额
hasPayAmountMap.merge(leaseOrderItem.getOrderId(), leaseOrderItem.getAlreadyPayAmount(), BigDecimal::add);
@@ -543,15 +548,18 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
.name(leaseOrderItem.getName())
.image(leaseOrderItem.getImage())
.price(leaseOrderItem.getPrice())
.type(leaseOrderItem.getType() ? 1 : 0)
.build()).collect(Collectors.toList());
OrderInfoDto orderInfoDto = OrderInfoDto.builder()
.id(leaseOrderInfo.getId())
.shopName(leaseOrderInfo.getShopName())
.orderNumber(leaseOrderInfo.getOrderNumber())
.userId(leaseOrderInfo.getUserId())
.totalPrice(leaseOrderInfo.getTotalPrice())
.status(leaseOrderInfo.getStatus())
.createTime(leaseOrderInfo.getCreateTime())
.endTime(leaseOrderInfo.getUpdateTime())
.orderItemDtoList(collect1)
.build();
@@ -676,7 +684,12 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
public Result<String> addOrdersV2(OrderAndCodeVo orderAndCodeVo) {
String userEmail = SecurityUtils.getUsername();
List<OrderInfoVo> orderInfoVoList = orderAndCodeVo.getOrderInfoVoList();
List<OrderMiningInfoDto> orderMiningInfoDtoList = orderAndCodeVo.getOrderMiningInfoDtoList();
String errors = checkMiningMinersExist(orderMiningInfoDtoList);
if (StringUtils.isNotEmpty(errors)){
return Result.fail(errors);
}
Map<Long, OrderMiningInfoDto> userAndMinerMap = orderMiningInfoDtoList.stream().collect(Collectors.toMap(OrderMiningInfoDto::getMachineId, Function.identity()));
GoogleInfo googleInfo = leaseUserMapper.getGoogleInfoByEmail(userEmail);
//TODO 开发环境
//if(googleInfo == null || StringUtils.isBlank(googleInfo.getSecret())){
@@ -698,9 +711,10 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
return Result.fail("购买的ASIC商品中,存在可售数量不足的商品!");
}
boolean isGpuPass = checkGpuMachine(gpuMachines);
if (!isGpuPass){
return Result.fail("购买的GPU商品中,存在客户端不在线的矿机!");
}
//TODO 开发环境
//if (!isGpuPass){
// return Result.fail("购买的GPU商品中,存在客户端不在线的矿机!");
//}
//存储相同链和币种的map集合
Map<String, Map<String, List<OrderInfoVo>>> chainAndCoinMap = new HashMap<>();
Map<Long, LeaseMachinePrice> orderTotalPriceGroupByChainAndCoin = leaseMachinePriceMapper.getOrderTotalPriceGroupByChainAndCoin(orderInfoVoList);
@@ -712,7 +726,7 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
//其他
Map<Long, OrderInfoVo> machineMap = new HashMap<>();
LocalDateTime now = LocalDateTime.now();
Long startTime = System.currentTimeMillis();
long startTime = System.currentTimeMillis();
List<Long> machineIds = new ArrayList<>();
Set<Long> shopIds = new HashSet<>();
for (OrderInfoVo vo : orderInfoVoList) {
@@ -732,15 +746,16 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
}
LeaseMachinePrice priceInfo = orderTotalPriceGroupByChainAndCoin.get(vo.getMachineId());
if (priceInfo != null) {
v = v.add(priceInfo.getPrice().multiply(BigDecimal.valueOf(vo.getLeaseTime())));
v = v.add(priceInfo.getPrice().multiply(BigDecimal.valueOf(vo.getNumbers())).multiply(BigDecimal.valueOf(vo.getLeaseTime())));
}
return v;
});
machineMap.put(vo.getMachineId(), vo);
machineIds.add(vo.getMachineId());
shopIds.add(vo.getShopId());
}
//店铺名
Map<Long, LeaseShop> shopNameMapByIds = leaseShopMapper.getShopNameMapByIds(new ArrayList<>(shopIds));
//获取买家需要进行支付的几个钱包
List<LeaseUserWalletData> walletDataList = leaseUserWalletDataMapper.selectWalletByChainAndCoinAndUsername(chainAndCoinSet, userEmail);
if (walletDataList.size() != chainAndCoinSet.size()){
@@ -760,6 +775,7 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
.chainAndCoinKey(chain+"-"+coin)
.orderNumber(UuidGeneratorUtil.generateUuidWithoutHyphen())
.userId(userEmail)
.shopName(shopNameMapByIds.get(Long.parseLong(split[2])).getName())
.fee(fee)
.totalPrice(totalPrice)
.createTime(now)
@@ -799,11 +815,10 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
//订单详情表业务
List<LeaseShop> shopList = leaseShopMapper.selectBatchIds(shopIds);
Map<Long, LeaseShop> shopMap = shopList.stream().collect(Collectors.toMap(LeaseShop::getId, Function.identity()));
List<LeaseOrderItem> leaseOrderItems = new ArrayList<>();
//挖矿结束时间
Map<Long,Long> endMiningMap = new HashMap<>();
Map<Long,Long> machineOrderIdMap = new HashMap<>();
Map<Long,Long> machineOrderItemIdMap = new HashMap<>();
for (LeaseMachine leaseMachine : machinesList) {
Long machineId = leaseMachine.getId();
OrderInfoVo orderInfoVo = machineMap.get(machineId);
@@ -822,13 +837,13 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
LeaseShop leaseShop = shopMap.get(leaseMachine.getShopId());
leaseShop.setSaleNumber(leaseShop.getSaleNumber() + orderInfoVo.getNumbers());
leaseOrderItems.add( LeaseOrderItem.builder()
LeaseOrderItem build = LeaseOrderItem.builder()
.userId(userEmail)
.orderId(leaseOrderInfo.getId())
.productMachineId(machineId)
.price(leaseMachinePrice.getPrice())
.user(leaseMachine.getUser())
.miner(leaseMachine.getMiner())
.user(userAndMinerMap.get(machineId).getPoolUser())
.miner(userAndMinerMap.get(machineId).getWorkerId())
//.theoryIncome(leaseMachine.getTheoryIncome().multiply(BigDecimal.valueOf(orderInfoVo.getLeaseTime())))
.leaseTime(orderInfoVo.getLeaseTime())
.payCoin(leaseShopConfig.getPayCoin())
@@ -841,28 +856,40 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
.fromChain(walletInfo.getFromChain())
.fromSymbol(walletInfo.getFromSymbol())
.numbers(orderInfoVo.getNumbers())
.build());
Long timestamp =startTime + orderInfoVo.getLeaseTime() * orderInfoVo.getNumbers() * 24 * 60 * 60 * 1000L;
.type(leaseMachine.getType())
.build();
Long timestamp = startTime + orderInfoVo.getLeaseTime() * 24 * 60 * 60 * 1000L;
endMiningMap.put(machineId,timestamp);
machineOrderIdMap.put(machineId,leaseOrderInfo.getId());
leaseOrderItemService.save(build);
//获取itemId
machineOrderItemIdMap.put(machineId,build.getId());
}
boolean b = leaseOrderItemService.saveBatch(leaseOrderItems);
//开始生成支付订单并返回
if (b){
checkBalanceAndSetBlockBalance(fromAddressMap,leaseOrderInfoList);
//修改GPU 矿机售出状态为 1 已售出
int gpuUpdate = leaseMachineMapper.updateLockState(asicMachines);
//修改ASIC 矿机出售数量
int asicUpdate = leaseMachineMapper.updateLockNumbers(gpuMachines);
int i = gpuUpdate + asicUpdate;
if (i != machineIds.size()){
throw new OrderException("订单中已有商品售出,请刷新购物车删除已售出商品,重新结算生成订单");
}
//发送消息
sendGpuTcpMessageToClient(gpuMachines,orderAndCodeVo.getOrderMiningInfoDtoList(),endMiningMap);
return Result.success("订单生成成功");
checkBalanceAndSetBlockBalance(fromAddressMap,leaseOrderInfoList);
int i = 0;
//修改GPU 矿机售出状态为 1 已售出
if (!gpuMachines.isEmpty()){
int gpuUpdate = leaseMachineMapper.updateLockState(gpuMachines);
i += gpuUpdate;
}
throw new OrderException("订单生成失败");
//修改ASIC 矿机出售数量
if (!asicMachines.isEmpty()){
int asicUpdate = leaseMachineMapper.updateLockNumbers(asicMachines);
i += asicUpdate;
}
if (i != machineIds.size()){
throw new OrderException("订单中已有商品售出,请刷新购物车删除已售出商品,重新结算生成订单");
}
//TODO 开发环境 不添加
//if(!checkGpuMachine(gpuMachines)){
// throw new OrderException("购买的GPU商品中,存在客户端不在线的矿机!");
//}
//发送消息
sendMessageToClientAndInsertPurchasedMachine(userEmail,gpuMachines,orderMiningInfoDtoList,endMiningMap,machineOrderIdMap,machineOrderItemIdMap);
return Result.success("订单生成成功");
}
/**
@@ -895,58 +922,135 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
return true;
}
/**
* 检测本次输入的钱包对应矿工号,是否已经存在并在挖矿中
* @param orderMiningInfoDtoList
*/
private String checkMiningMinersExist(List<OrderMiningInfoDto> orderMiningInfoDtoList){
List<OrderMiningInfoDto> orderMiningInfoDtos = leaseOrderMiningMapper.checkMiningMinersExist(orderMiningInfoDtoList);
StringBuilder errors = new StringBuilder();
if(!orderMiningInfoDtos.isEmpty()){
Map<String, List<OrderMiningInfoDto>> collect = orderMiningInfoDtos.stream().collect(Collectors.groupingBy(OrderMiningInfoDto::getWalletAddress));
collect.forEach((address,list)->{
errors.append("钱包地址: ").append(address).append("-已存在挖矿中矿工号");
for (OrderMiningInfoDto orderMiningInfoDto : list) {
errors.append(orderMiningInfoDto.getWorkerId()).append(" ");
}
errors.append("请重新输入矿工号\n");
});
}
return errors.toString();
}
/**
* GPU矿机的通过mac发送给相应的客户端
*/
public void sendGpuTcpMessageToClient(List<LeaseMachine> gpuMachines, List<OrderMiningInfoDto> orderMiningInfo,Map<Long,Long> endMiningMap){
//TODO 不添加
//if(!checkGpuMachine(gpuMachines)){
// throw new OrderException("购买的GPU商品中,存在客户端不在线的矿机!");
//}
public void sendMessageToClientAndInsertPurchasedMachine(String userEmail, List<LeaseMachine> gpuMachines, List<OrderMiningInfoDto> orderMiningInfo, Map<Long,Long> endMiningMap, Map<Long,Long> machineOrderIdMap,Map<Long,Long> machineOrderItemIdMap){
Map<Long, MiningConfigDto> coinAndAlgoMap = leaseMachineMapper.getCoinAndAlgoById(orderMiningInfo.stream().map(OrderMiningInfoDto::getCoinConfigId).collect(Collectors.toList()));
Map<Long, LeaseMachine> idAndMacMap = gpuMachines.stream().collect(Collectors.toMap(LeaseMachine::getId, Function.identity()));
List<Long> configIds = orderMiningInfo.stream()
.map(OrderMiningInfoDto::getCoinConfigId).distinct().collect(Collectors.toList());
Map<Long,MiningConfigDto> coinAndAlgoMap = leaseMachineMapper.getCoinAndAlgoById(configIds);
Map<Long, LeaseMachine> idAndMacMap = gpuMachines.stream().collect(Collectors
.toMap(LeaseMachine::getId, Function.identity()));
Map<String,MiningConfigDto> macMessageMap = new HashMap<>();
List<LeaseOrderMining> orderMiningList = new ArrayList<>();
for (OrderMiningInfoDto orderMiningInfoDto : orderMiningInfo) {
MiningConfigDto miningConfigDto = coinAndAlgoMap.get(orderMiningInfoDto.getCoinConfigId());
for (Long machineId : orderMiningInfoDto.getMachineIds()) {
String mac = idAndMacMap.get(machineId).getHostMac();
Long endTime = endMiningMap.get(machineId);
ChannelManager.sendToClient(mac,MiningConfigDto.builder()
.algo(miningConfigDto.getAlgo())
.coin(miningConfigDto.getCoin())
.pool(miningConfigDto.getPool())
.wallet_mining(orderMiningInfoDto.getWalletMining())
.pool_url(miningConfigDto.getPool_url())
.wallet_address(orderMiningInfoDto.getWalletAddress())
.pool_user(orderMiningInfoDto.getPoolUser())
.worker_id(orderMiningInfoDto.getWorkerId())
.end_timestamp(endTime).build());
Long machineId = orderMiningInfoDto.getMachineId();
LeaseMachine leaseMachine = idAndMacMap.get(machineId);
Long orderId = machineOrderIdMap.get(machineId);
Long endTime = endMiningMap.get(machineId);
Long orderItemId = machineOrderItemIdMap.get(machineId);
String poolName = orderMiningInfoDto.getPoolName();
String walletAddress = orderMiningInfoDto.getWalletAddress();
String algo = miningConfigDto.getAlgo();
String coin = miningConfigDto.getCoin();
String poolUrl = miningConfigDto.getPool();
String poolUser = orderMiningInfoDto.getPoolUser();
String poolWorkerId = orderMiningInfoDto.getWorkerId();
//这里因为orderMiningInfo 存在ASIC 的信息可能为空
if (leaseMachine != null){
String mac = leaseMachine.getHostMac();
//TCP 消息
macMessageMap.put(mac,MiningConfigDto.builder()
.algo(algo)
.coin(coin)
.pool(poolName)
.pool_url(poolUrl)
.wallet_mining(orderMiningInfoDto.getWalletMining())
.wallet_address(walletAddress)
.pool_user(poolUser)
.worker_id(poolWorkerId)
.end_timestamp(endTime/1000).build());
}
LeaseOrderMining build = LeaseOrderMining.builder()
.orderId(orderId)
.orderItemId(orderItemId)
.machineId(machineId)
.userId(userEmail)
.coin(coin)
.algorithm(algo)
.poolUrl(poolUrl)
.pool(poolName)
.poolUser(poolUser)
.workerId(poolWorkerId)
.walletAddress(walletAddress)
.startTime(LocalDateTime.now())
.endTime(DateUtils.toLocalDateTime(new Date(endTime)))
.build();
//已购商品信息
orderMiningList.add(build);
long timeout = ChronoUnit.HOURS.between(build.getStartTime(), build.getEndTime().plusHours(1));
redisService.setCacheObject7(combinationKey(poolName,walletAddress,coin,algo),"1",timeout, TimeUnit.HOURS);
}
leaseOrderMiningMapper.insertBatch(orderMiningList);
macMessageMap.forEach(this::sendMessage);
}
//TODO ASIC 不是发送给客户端是通过其他方式不过发送消息的结构和gpu相同
public String combinationKey(String pool,String walletAddress,String coin,String algo){
return pool +":"+ walletAddress +":"+ coin +":"+ algo;
}
/**
* 发送矿机配置信息给矿机客户端
* @param mac 矿机mac
* @param message 消息内容
*/
public void sendMessage(String mac,MiningConfigDto message){
ChannelManager.sendToClient(mac,message);
}
@Override
public PageResult<OrderInfoDto> getOrdersByStatusV2(OrderInfoStateVo orderInfoStateVo) {
return null;
public PageResult<PurchasedMachineListDto> getPurchasedItems(PageVo pageVo) {
String userEmail = SecurityUtils.getUsername();
PageHelper.startPage(pageVo.getPageNum(), pageVo.getPageSize());
List<PurchasedMachineListDto> purchasedItems = leaseOrderMiningMapper.getPurchasedItems(userEmail);
//获取订单信息
PageInfo<PurchasedMachineListDto> pageInfo = new PageInfo<>(purchasedItems);
PageResult<PurchasedMachineListDto> success = PageResult.success(purchasedItems);
success.setTotal(pageInfo.getTotal());
success.setTotalPage(pageInfo.getPages());
PageHelper.clearPage();
return success;
}
@Override
public PageResult<OrderInfoDto> getOrdersByStatusForSellerV2(OrderInfoStateVo orderInfoStateVo) {
return null;
@DSTransactional
public Result<PurchasedMachineDto> getPurchasedInfo(BaseVo baseVo) {
//找到miner 和钱包 ,pool 矿池名
PurchasedMachineDto info = leaseOrderMiningMapper.getPurchasedInfo(baseVo.getId());
//根据钱包 + 币种 + 矿工号 查询 pool 库 pool.kryptex 表 中对应 算力
MiningHashrateInfoDto recentlyHashrate = leaseOrderMiningMapper.getRecentlyHashrate(info.getPool(), info.getWalletAddress(), info.getCoin(), info.getWorkerId());
info.setMiningHashrateInfo(recentlyHashrate);
return Result.success(info);
}
@Override
public Result<OrderInfoDto> getOrdersByIdsV2(OrderVo orderVo) {
return null;
}
@Override
public Result<BigDecimal> getCoinPriceV2(CoinVo coinVo) {
return null;
}
}

View File

@@ -197,7 +197,7 @@ public class LeaseUserOwnedProductServiceImpl extends ServiceImpl<LeaseUserOwned
.estimatedEndUsdtIncome(price == null ? BigDecimal.ZERO : item.getTheoryIncome().multiply(price))
.coin(item.getCoin())
.startTime(startOfDay)
.type(item.getType())
.type(item.getType() ? 1 : 0)
.endTime(startOfDay.plusDays(item.getLeaseTime()))
.build();
}).collect(Collectors.toList());

View File

@@ -88,17 +88,18 @@ public class LeaseUserServiceImpl extends ServiceImpl<LeaseUserMapper, LeaseUser
private LeaseShopMapper leaseShopMapper;
@Value("${file.filepath}")
private String localFilePath;
@Value("${client.download.client}")
private String downloadClient;
@Value("${client.download.client.linux}")
private String downloadClientLinux;
@Value("${client.download.client.windows}")
private String downloadClientWindows;
@Value("${client.download.version}")
private String version;
@Value("${client.download.config}")
private String configName;
@Value("${client.download.path}")
private String downloadPath;
@Override
@@ -499,22 +500,23 @@ public class LeaseUserServiceImpl extends ServiceImpl<LeaseUserMapper, LeaseUser
System.out.println("获取下载权限码"+userEmail);
LeaseShop leaseShop = leaseShopMapper.selectOne(new LambdaQueryWrapper<LeaseShop>()
.select(LeaseShop::getIdentityCode)
.select(LeaseShop::getUserEmail)
.eq(LeaseShop::getUserEmail, userEmail));
if (leaseShop == null){
throw new RuntimeException("下载失败,下载客户端权限,请重新登录并创建店铺");
}
if (StringUtils.isEmpty(leaseShop.getIdentityCode())) {
leaseShop.setIdentityCode(UuidGeneratorUtil.generateUuidWithoutHyphen());
leaseShopMapper.updateById(leaseShop);
}
return leaseShop.getIdentityCode();
// 直接把邮箱作为身份码
//if (StringUtils.isEmpty(leaseShop.getIdentityCode())) {
// leaseShop.setIdentityCode(UuidGeneratorUtil.generateUuidWithoutHyphen());
// leaseShopMapper.updateById(leaseShop);
//}
return leaseShop.getUserEmail();
}
@Override
public void downloadClient(String userEmail,HttpServletRequest request, HttpServletResponse response) {
public void downloadClient(String userEmail,String type,HttpServletRequest request, HttpServletResponse response) {
String auth = getAuthCode(userEmail);
//设置响应头信息
response.reset();
@@ -540,8 +542,8 @@ public class LeaseUserServiceImpl extends ServiceImpl<LeaseUserMapper, LeaseUser
Path authFile = tempDir.resolve("auth");
Files.write(authFile, auth.getBytes(StandardCharsets.UTF_8));
//读取/home/ubuntu/web/client/bin 下所有文件和 auth文件一起压缩
Path sourceDir = Paths.get(downloadClient);
//读取/home/ubuntu/web/client/linux 或 /home/ubuntu/web/client/windows 下所有文件和 auth文件一起压缩
Path sourceDir = Paths.get(downloadClientLinux);
try {
Files.walk(sourceDir)
.filter(path -> !Files.isDirectory(path))
@@ -610,7 +612,7 @@ public class LeaseUserServiceImpl extends ServiceImpl<LeaseUserMapper, LeaseUser
@Override
public String getClientVersion() {
try {
return readTxtFromDir(downloadClient, configName);
return readTxtFromDir(downloadClientLinux, version);
}catch (Exception e){
return "获取配置信息失败"+e;
}

View File

@@ -3,14 +3,19 @@ package com.m2pool.lease.task;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.m2pool.common.core.utils.bean.BeanUtils;
import com.m2pool.common.core.utils.DateUtils;
import com.m2pool.lease.constant.PowerUnit;
import com.m2pool.lease.dto.*;
import com.m2pool.lease.dto.v2.PurchasedMachineListDto;
import com.m2pool.lease.dto.v2.RealHashrateInfoDto;
import com.m2pool.lease.entity.*;
import com.m2pool.lease.mapper.*;
import com.m2pool.lease.mq.message.RabbitmqDeleteWalletMessage;
import com.m2pool.lease.mq.message.RabbitmqPayAutoMessage;
import com.m2pool.lease.service.*;
import com.m2pool.lease.service.LeaseOrderItemService;
import com.m2pool.lease.service.LeasePayRecordMessageInfoService;
import com.m2pool.lease.service.LeasePayRecordMessageService;
import com.m2pool.lease.service.LeaseUserOwnedProductService;
import com.m2pool.lease.utils.HashUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Configuration;
@@ -21,7 +26,9 @@ import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -66,9 +73,14 @@ public class OrderAndPayTask {
private LeaseShopConfigMapper leaseShopConfigMapper;
@Resource
private LeaseOrderItemService leaseOrderItemService;
@Resource
private LeasePayRecordMessageInfoService leasePayRecordMessageInfoService;
@Resource
private LeaseMachineTempConfigMapper leaseMachineTempConfigMapper;
@Resource
private LeaseOrderMiningMapper leaseOrderMiningMapper;
@Resource
private LeaseMachineMapper leaseMachineMapper;
/**
@@ -118,12 +130,318 @@ public class OrderAndPayTask {
}
@Scheduled(cron = "0 0/1 * * * ? ")
@DSTransactional
public void paymentTaskV2(){
Date now = DateUtils.getPreviousHalfHourOrFullHour(new Date());
//1.查找在进行中的订单
List<LeaseOrderInfo> needPayOrderList = leaseOrderInfoMapper.getNeedPayOrderList();
if (needPayOrderList.isEmpty()){
System.out.println("没有进行中的订单");
return;
}
List<Long> infoIds = new ArrayList<>();
Map<Long, LeaseOrderInfo> queueIdMap = new HashMap<>();
for (LeaseOrderInfo leaseOrderInfo : needPayOrderList) {
queueIdMap.put(leaseOrderInfo.getId(), leaseOrderInfo);
infoIds.add(leaseOrderInfo.getId());
}
//2.根据订单号找到对应的订单详情(状态为1 租约生效中) ,并根据这些订单详情找到每个矿机的信息
List<LeaseOrderItem> leaseOrderItems = leaseOrderItemMapper.selectList(new LambdaQueryWrapper<LeaseOrderItem>()
.eq(LeaseOrderItem::getStatus,1)
.in(LeaseOrderItem::getOrderId, infoIds));
//本次全部完成的订单id 集合
List<Long> orderComplete = new ArrayList<>();
//本次部分已完成订单详情id + 机器id 集合
List<Long> itemCompleteIds = new ArrayList<>();
List<Long> completeMachineIds = new ArrayList<>();
//本此需支付订单详情id 集合
List<Long> needPayIds = new ArrayList<>();
//本次需支付订单详情 集合
List<LeaseOrderItem> needPayInfos = new ArrayList<>();
getCompleteOrderAndNeedPayOrder(orderComplete,completeMachineIds,itemCompleteIds,needPayIds,needPayInfos,leaseOrderItems);
//订单进行过程中,卖方可能修改收款地址。所以每天支付前,需要修改旧收款地址为新地址
needPayInfos = updateOrderItemSellerWalletAddress(needPayInfos);
//1.处理需要支付的订单
//根据 lease_order_item 中订单id 查找到 对应的lease_order_mining 信息
List<PurchasedMachineListDto> miningInfoByOrderId = leaseOrderMiningMapper.getMiningInfoByOrderId(needPayIds);
//根据lease_order_mining 中相关信息 钱包 + 币种 + 算法 + 矿工号 + 矿池名 找到对应的实时算力
if (!miningInfoByOrderId.isEmpty()){
Map<String, List<PurchasedMachineListDto>> collect = miningInfoByOrderId.stream().collect(Collectors.groupingBy(PurchasedMachineListDto::getPool));
//挖矿信息 与 订单详情id 映射
Map<String, PurchasedMachineListDto> miningAnditemIdMap = miningInfoByOrderId.stream()
.collect(Collectors.toMap(
dto -> dto.getPool() + "_" + dto.getWalletAddress() + "_" +
dto.getWorkerId() + "_" + dto.getCoin() + "_" + dto.getAlgorithm(),
Function.identity(),
(existing, replacement) -> replacement
));
List<RealHashrateInfoDto> realPowerList = new ArrayList<>();
collect.forEach((pool, list) -> {
realPowerList.addAll(leaseOrderMiningMapper.getRecently24HourHashrate(list, pool, now));
});
//挖矿信息 与 实时算力 映射
Map<String, List<RealHashrateInfoDto>> groupedData = realPowerList.stream()
.collect(Collectors.groupingBy(
dto -> dto.getPool() + "_" + dto.getWalletAddress() + "_" +
dto.getMiner() + "_" + dto.getCoin() + "_" + dto.getAlgorithm()
));
//订单id 与 实时算力 映射
Map<Long, List<RealHashrateInfoDto>> orderIdToHashrateMap = new HashMap<>();
getOrderItemIdToHashrateMap(orderIdToHashrateMap,miningAnditemIdMap,groupedData);
//根据实时算力波动情况 求得订单实际支付金额
Map<Long, LeaseOrderItem> orderIdToPriceMap = needPayInfos.stream().collect(Collectors.toMap(LeaseOrderItem::getId,Function.identity()));
Map<Long, BigDecimal> orderItemToPriceMap = orderPaymentWaveAlgorithm(orderIdToHashrateMap,orderIdToPriceMap);
Map<Long, List<LeaseOrderItem>> userMapItem = needPayInfos.stream()
.collect(Collectors.groupingBy(LeaseOrderItem::getOrderId));
List<RabbitmqPayAutoMessage> rabbitmqPayAutoMessages = new ArrayList<>();
List<LeaseOrderItem> saleIngList = new ArrayList<>();
// 按照订单分组
userMapItem.forEach((orderId, items) -> {
LeaseOrderItem leaseOrderItem = items.get(0);
long timestamp = System.currentTimeMillis()/1000;
//因为同一个订单的queueId相同所以这里直接使用订单id
LeaseOrderInfo orderInfo = queueIdMap.get(orderId);
//买方信息
RabbitmqPayAutoMessage build = RabbitmqPayAutoMessage.builder()
.queue_id(orderInfo.getOrderNumber())
.order_id(String.valueOf(orderId))
.shopId(leaseOrderItem.getShopId())
.chain(leaseOrderItem.getChain())
.symbol(leaseOrderItem.getSymbol())
.fee(orderInfo.getFee())
.userId(leaseOrderItem.getUserId())
.from_address(leaseOrderItem.getFromAddress())
.to_address(leaseOrderItem.getAddress())
.amount(BigDecimal.ZERO)
.blockAmount(BigDecimal.ZERO)
.needAmount(BigDecimal.ZERO)
.timestamp(timestamp)
.sign(HashUtils.sha256(timestamp))
.build();
for (LeaseOrderItem item : items) {
BigDecimal amount = orderItemToPriceMap.get(item.getId());
BigDecimal realPayAmount = amount== null ? BigDecimal.ZERO : amount;
//设置理论支付金额 和 冻结金额 和 已支付理论金额 (冻结金额 = 理论支付金额 = 已支付理论金额)
build.setNeedAmount(build.getNeedAmount().add(item.getPrice().multiply(BigDecimal.valueOf(item.getNumbers()))));
build.setBlockAmount(build.getBlockAmount().add(item.getPrice().multiply(BigDecimal.valueOf(item.getNumbers()))));
item.setAlreadyPayAmount(item.getAlreadyPayAmount().add(item.getPrice().multiply(BigDecimal.valueOf(item.getNumbers()))));
//设置待支付真实金额
item.setSettlePayRealAmount(item.getSettlePayRealAmount().add(realPayAmount));
// mq消息真实支付金额
build.setAmount(build.getAmount().add(realPayAmount));
saleIngList.add(item);
}
if (build.getAmount().compareTo(BigDecimal.ZERO) > 0){
rabbitmqPayAutoMessages.add(build);
}
});
//修改订单支付金额等相关信息
leaseOrderItemService.updateBatchById(saleIngList);
//保存支付信息到支付记录表中
saveMqMessageToDB(rabbitmqPayAutoMessages);
}
//2.处理完成的订单 (订单部分完成)
if(!itemCompleteIds.isEmpty()){
handlerOrderPartComplete(itemCompleteIds,completeMachineIds);
}
//3.处理完成的订单 + 发送mq消息去支付订单全部完成
if (!orderComplete.isEmpty()){
handlerOrderAllComplete(orderComplete);
}
}
/**
* 获取订单完成和需要支付的订单id
* @param orderComplete 订单是否完成(所有子订单完成)
* @param completeIds 本次完成子订单 id集合
* @param needPayIds 本次需要支付的订单 id集合
* @param needPayInfos 本次需要支付的订单信息(租赁数量,租赁天数,单价等)
* @param oneDayApartOrder
*/
public void getCompleteOrderAndNeedPayOrder(List<Long> orderComplete,List<Long> completeMachineIds ,List<Long> completeIds,List<Long> needPayIds,List<LeaseOrderItem> needPayInfos,List<LeaseOrderItem> oneDayApartOrder){
LocalDateTime now = LocalDateTime.now();
Map<Long,Boolean> orderCompleteMap = new HashMap<>();
for (LeaseOrderItem orderTimeInfoDto : oneDayApartOrder) {
orderCompleteMap.putIfAbsent(orderTimeInfoDto.getOrderId(), true);
// 计算订单结束时间startTime + leaseTime天
//LocalDateTime endTime = orderTimeInfoDto.getCreateTime().plusDays(orderTimeInfoDto.getLeaseTime());
//TODO 开发环境测试用1分钟当一天 差值改为差1分钟
LocalDateTime endTime = orderTimeInfoDto.getCreateTime().plusMinutes(orderTimeInfoDto.getLeaseTime());
// 比较 now 和 endTime 的差值是否小于30分
if (Duration.between(now, endTime).toMinutes() <= 1) {
completeMachineIds.add(orderTimeInfoDto.getProductMachineId());
completeIds.add(orderTimeInfoDto.getId());
needPayIds.add(orderTimeInfoDto.getId());
needPayInfos.add(orderTimeInfoDto);
continue;
}
// 比较当前时间的时分秒与startTime的时分秒差值是否小于30分钟
LocalTime nowTime = now.toLocalTime();
LocalTime startTime = orderTimeInfoDto.getCreateTime().toLocalTime();
if (Duration.between(startTime, nowTime).toMinutes() <= 1) {
needPayIds.add(orderTimeInfoDto.getId());
needPayInfos.add(orderTimeInfoDto);
}
//记录某个订单下所有子项目是否都完成
orderCompleteMap.put(orderTimeInfoDto.getOrderId(), false);
}
orderCompleteMap.forEach(
(orderId, complete) -> {
if (complete) {
orderComplete.add(orderId);
}
}
);
}
/**
* 获取各orderItemId 所需支付的金额
* @param orderIdToHashrateMap 订单id 与算力的映射
* @param orderIdToPriceMap 订单id 与价格映射
* @return
*/
public Map<Long,BigDecimal> orderPaymentWaveAlgorithm(Map<Long, List<RealHashrateInfoDto>> orderIdToHashrateMap, Map<Long, LeaseOrderItem> orderIdToPriceMap){
Map<Long,BigDecimal> orderItemIdToWaveMap = new HashMap<>();
orderIdToHashrateMap.forEach((orderItemId, realPowerList) -> {
// 计算平均值
BigDecimal average = realPowerList.stream()
.map(RealHashrateInfoDto::getPower)
.reduce(BigDecimal.ZERO, BigDecimal::add)
.divide(BigDecimal.valueOf(realPowerList.size()), 2, RoundingMode.HALF_UP);
// 计算每个数据点与平均值的比值
List<BigDecimal> ratios = realPowerList.stream()
.map(dto -> dto.getPower().divide(average, 4, RoundingMode.HALF_UP))
.collect(Collectors.toList());
// 补零到48个数据点
while (ratios.size() < 48) {
ratios.add(BigDecimal.ZERO);
}
// 计算总和并除以48
BigDecimal finalValue = ratios.stream()
.reduce(BigDecimal.ZERO, BigDecimal::add)
.divide(BigDecimal.valueOf(48), 2, RoundingMode.HALF_UP);
LeaseOrderItem itemInfo = orderIdToPriceMap.get(orderItemId);
BigDecimal payAmount = itemInfo.getPrice()
.multiply(BigDecimal.valueOf(itemInfo.getNumbers()))
.multiply(BigDecimal.valueOf(itemInfo.getLeaseTime()));
if (finalValue.compareTo(BigDecimal.valueOf(0.95)) <= 0){
payAmount = payAmount.multiply(finalValue);
}
orderItemIdToWaveMap.put(orderItemId, payAmount);
});
return orderItemIdToWaveMap;
}
/**
* 获取订单项id 与实时算力映射
**/
public void getOrderItemIdToHashrateMap(Map<Long, List<RealHashrateInfoDto>> orderIdToHashrateMap,Map<String, PurchasedMachineListDto> miningAnditemIdMap,Map<String, List<RealHashrateInfoDto>> groupedData){
for (Map.Entry<String, List<RealHashrateInfoDto>> entry : groupedData.entrySet()) {
String key = entry.getKey();
List<RealHashrateInfoDto> hashrateList = entry.getValue();
// 从第一个映射中获取对应的订单信息
PurchasedMachineListDto orderInfo = miningAnditemIdMap.get(key);
if (orderInfo != null) {
// 将订单ID与实时算力关联起来
orderIdToHashrateMap.put(orderInfo.getOrderItemId(), hashrateList);
}
}
}
/**
* 处理订单全部完成
* @param orderIds 订单子项全部完成的订单id
*/
public void handlerOrderAllComplete(List<Long> orderIds){
//修改状态为已完成
leaseOrderInfoMapper.update(LeaseOrderInfo.builder().status(8).build(), new LambdaQueryWrapper<LeaseOrderInfo>().in(LeaseOrderInfo::getId, orderIds));
//发送支付消息
sendMessageToMq(orderIds);
}
/**
* 保存支付消息到支付记录表中
* @param rabbitmqPayAutoMessages
*/
public void saveMqMessageToDB( List<RabbitmqPayAutoMessage> rabbitmqPayAutoMessages){
if (!rabbitmqPayAutoMessages.isEmpty()){
List<LeasePayRecordMessageInfo> collect = new ArrayList<>();
for (RabbitmqPayAutoMessage rabbitmqPayAutoMessage : rabbitmqPayAutoMessages) {
collect.add( LeasePayRecordMessageInfo.builder()
.orderId(rabbitmqPayAutoMessage.getOrder_id())
.orderNumber(rabbitmqPayAutoMessage.getQueue_id())
.queueId(rabbitmqPayAutoMessage.getQueue_id())
.fromAddress(rabbitmqPayAutoMessage.getFrom_address())
.toAddress(rabbitmqPayAutoMessage.getTo_address())
.amount(rabbitmqPayAutoMessage.getNeedAmount())
.realAmount(rabbitmqPayAutoMessage.getAmount())
.needAmount(rabbitmqPayAutoMessage.getAmount())
.fromChain(rabbitmqPayAutoMessage.getChain())
.fromSymbol(rabbitmqPayAutoMessage.getSymbol())
.blockAmount(rabbitmqPayAutoMessage.getBlockAmount())
.shopId(rabbitmqPayAutoMessage.getShopId())
.userId(rabbitmqPayAutoMessage.getUserId())
.toChain(rabbitmqPayAutoMessage.getChain())
.toSymbol(rabbitmqPayAutoMessage.getSymbol())
.build());
}
leasePayRecordMessageInfoService.saveBatch(collect);
}
}
/**
* 处理订单部分完成
* @param itemIds 已完成订单项id
* @param machineIds 已完成机器id
*/
public void handlerOrderPartComplete(List<Long> itemIds,List<Long> machineIds){
//修改订单详情表中状态为 0 租约已过期
leaseOrderItemMapper.update(LeaseOrderItem.builder().status(0).build(), new LambdaUpdateWrapper<LeaseOrderItem>()
.in(LeaseOrderItem::getId, itemIds));
//修改商品库存矿机表 矿机状态为 0 未售出
leaseMachineMapper.update(LeaseMachine.builder().saleState(false).build(), new LambdaUpdateWrapper<LeaseMachine>()
.in(LeaseMachine::getId, machineIds));
//挖矿订单信息 改为状态0 租约已过期
leaseOrderMiningMapper.update(LeaseOrderMining.builder().status(false).build(), new LambdaUpdateWrapper<LeaseOrderMining>()
.in(LeaseOrderMining::getOrderItemId, itemIds));
}
/**
* 支付 定时任务
*/
//@Scheduled(cron = "0 5 0 * * ? ")
@Scheduled(cron = "0 0/2 * * * ? ")
//@Scheduled(cron = "0 0/2 * * * ? ")
@DSTransactional
public void paymentTask(){
// 获取当天 0 点的 时间
@@ -166,7 +484,7 @@ public class OrderAndPayTask {
dto -> dto.getUser() + "-" + dto.getMiner(),
dto -> dto
));
//2.2 订单详情集合 按照 买方地址 + 链名称分组
//2.2 订单详情集合 按照 order_id 分组
Map<Long, List<LeaseOrderItem>> userMapItem = leaseOrderItems.stream()
.collect(Collectors.groupingBy(LeaseOrderItem::getOrderId));
//.collect(Collectors.groupingBy(
@@ -369,30 +687,8 @@ public class OrderAndPayTask {
//5.正在售出中矿机已支付金额
leaseOrderItemService.updateBatchById(saleIngList);
//6.根据这些订单发送今天的支付消息到mq
if (!rabbitmqPayAutoMessages.isEmpty()){
List<LeasePayRecordMessageInfo> collect = new ArrayList<>();
for (RabbitmqPayAutoMessage rabbitmqPayAutoMessage : rabbitmqPayAutoMessages) {
collect.add( LeasePayRecordMessageInfo.builder()
.orderId(rabbitmqPayAutoMessage.getOrder_id())
.orderNumber(rabbitmqPayAutoMessage.getQueue_id())
.queueId(rabbitmqPayAutoMessage.getQueue_id())
.fromAddress(rabbitmqPayAutoMessage.getFrom_address())
.toAddress(rabbitmqPayAutoMessage.getTo_address())
.amount(rabbitmqPayAutoMessage.getNeedAmount())
.realAmount(rabbitmqPayAutoMessage.getAmount())
.needAmount(rabbitmqPayAutoMessage.getAmount())
.fromChain(rabbitmqPayAutoMessage.getChain())
.fromSymbol(rabbitmqPayAutoMessage.getSymbol())
.blockAmount(rabbitmqPayAutoMessage.getBlockAmount())
.shopId(rabbitmqPayAutoMessage.getShopId())
.userId(rabbitmqPayAutoMessage.getUserId())
.toChain(rabbitmqPayAutoMessage.getChain())
.toSymbol(rabbitmqPayAutoMessage.getSymbol())
.build());
}
leasePayRecordMessageInfoService.saveBatch(collect);
}
//6.保存支付消息到支付记录表中
saveMqMessageToDB(rabbitmqPayAutoMessages);
}
/**
@@ -510,6 +806,69 @@ public class OrderAndPayTask {
.collect(Collectors.toMap(LeaseOrderInfo::getOrderNumber, LeaseOrderInfo::getFee));
}
/**
* 一些校验失败的支付消息需要重新发送
*/
@Scheduled(cron = "0 0 0/1 * * ? ")
@Transactional
public void checkPushFailPayMessage(){
//查找出状态为3 校验失败 4 发送消息失败 的消息需要重发
List<LeasePayRecordMessage> list = leasePayRecordMessageService.list(new LambdaQueryWrapper<LeasePayRecordMessage>()
.eq(LeasePayRecordMessage::getStatus, 3)
.ge(LeasePayRecordMessage::getCreateTime, LocalDateTime.now().minusHours(3))
.or().eq(LeasePayRecordMessage::getStatus, 4));
if (list.isEmpty()){
return;
}
List<Long> orderIds = list.stream().map(leasePayRecordMessage->Long.valueOf(leasePayRecordMessage.getOrderId())).collect(Collectors.toList());
Map<String, BigDecimal> feeMap = getFee(orderIds);
List<LeasePayRecordMessage> againSendList = new ArrayList<>();
for (LeasePayRecordMessage leasePayRecordMessage : list) {
Long timestamp = System.currentTimeMillis()/1000;
RabbitmqPayAutoMessage build = RabbitmqPayAutoMessage.builder()
.queue_id(leasePayRecordMessage.getQueueId())
.order_id(leasePayRecordMessage.getOrderId())
.chain(leasePayRecordMessage.getFromChain())
.symbol(leasePayRecordMessage.getFromSymbol())
.from_address(leasePayRecordMessage.getFromAddress())
.to_address(leasePayRecordMessage.getToAddress())
.fee(feeMap.get(leasePayRecordMessage.getOrderId()))
.amount(leasePayRecordMessage.getAmount())
.timestamp(timestamp)
.sign(HashUtils.sha256(timestamp))
.build();
try {
rabbitTemplate.convertAndSend(PAY_AUTO_QUEUE, build);
leasePayRecordMessage.setStatus(2);
againSendList.add(leasePayRecordMessage);
}catch (Exception e){
System.out.println("消息发送失败"+e.getMessage());
}
}
leasePayRecordMessageService.updateBatchById(againSendList);
}
/**
* 检测 临时表数据是否需要持久化到矿机表中
*/
@Scheduled(cron = "0 0 0/1 * * ? ")
@Transactional
public void checkMachineTempTable(){
List<LeaseMachineTempConfig> leaseMachineTempConfigs = leaseMachineTempConfigMapper.selectList(new LambdaQueryWrapper<LeaseMachineTempConfig>()
.le(LeaseMachineTempConfig::getEffectTime, LocalDateTime.now()));
if (!leaseMachineTempConfigs.isEmpty()){
leaseMachineTempConfigMapper.insertBatchByTempList(leaseMachineTempConfigs);
leaseMachineTempConfigMapper.deleteBatchIds(leaseMachineTempConfigs);
}
}
///**
// * 订单完成后---发送支付消息到mq 一个买家对应多个卖家
// * @param orderIds
@@ -561,51 +920,6 @@ public class OrderAndPayTask {
// });
//}
/**
* 一些校验失败的支付消息需要重新发送
*/
@Scheduled(cron = "0 0 0/1 * * ? ")
@Transactional
public void checkPushFailPayMessage(){
//查找出状态为3 校验失败 4 发送消息失败 的消息需要重发
List<LeasePayRecordMessage> list = leasePayRecordMessageService.list(new LambdaQueryWrapper<LeasePayRecordMessage>()
.eq(LeasePayRecordMessage::getStatus, 3)
.ge(LeasePayRecordMessage::getCreateTime, LocalDateTime.now().minusHours(3))
.or().eq(LeasePayRecordMessage::getStatus, 4));
if (list.isEmpty()){
return;
}
List<Long> orderIds = list.stream().map(leasePayRecordMessage->Long.valueOf(leasePayRecordMessage.getOrderId())).collect(Collectors.toList());
Map<String, BigDecimal> feeMap = getFee(orderIds);
List<LeasePayRecordMessage> againSendList = new ArrayList<>();
for (LeasePayRecordMessage leasePayRecordMessage : list) {
Long timestamp = System.currentTimeMillis()/1000;
RabbitmqPayAutoMessage build = RabbitmqPayAutoMessage.builder()
.queue_id(leasePayRecordMessage.getQueueId())
.order_id(leasePayRecordMessage.getOrderId())
.chain(leasePayRecordMessage.getFromChain())
.symbol(leasePayRecordMessage.getFromSymbol())
.from_address(leasePayRecordMessage.getFromAddress())
.to_address(leasePayRecordMessage.getToAddress())
.fee(feeMap.get(leasePayRecordMessage.getOrderId()))
.amount(leasePayRecordMessage.getAmount())
.timestamp(timestamp)
.sign(HashUtils.sha256(timestamp))
.build();
try {
rabbitTemplate.convertAndSend(PAY_AUTO_QUEUE, build);
leasePayRecordMessage.setStatus(2);
againSendList.add(leasePayRecordMessage);
}catch (Exception e){
System.out.println("消息发送失败"+e.getMessage());
}
}
leasePayRecordMessageService.updateBatchById(againSendList);
}
//@Scheduled(cron = "0 0/1 * * * ? ")
//@Transactional
//public void test(){

View File

@@ -2,6 +2,8 @@ package com.m2pool.lease.utils;
import pt.kcry.blake3.Blake3;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.UUID;
/**
@@ -31,7 +33,10 @@ public class UuidGeneratorUtil {
public static void main(String[] args) {
//System.out.println("标准 UUID: " + generateStandardUuid());
//System.out.println("无连字符 UUID: " + generateUuidWithoutHyphen());
long daysBetween = ChronoUnit.HOURS.between(
LocalDateTime.of(2025, 12, 9, 23, 43),
LocalDateTime.of(2025, 12, 12, 23, 42));
System.out.println(daysBetween);
}
}

View File

@@ -38,10 +38,6 @@ spring:
max-file-size: 2MB
max-request-size: 8MB
client:
download:
path: https://test.m2pool.com/client
config: version
netty:
tcp:

View File

@@ -47,10 +47,11 @@ file:
client:
# 客户端安装包
download:
path: /home/ubuntu/web/client
client: /home/ubuntu/web/client/bin
client:
windows: /home/ubuntu/web/client/windows
linux: /home/ubuntu/web/client/linux
# 客户端版本号文件名
config: version
version: version
netty:
tcp:

View File

@@ -144,41 +144,6 @@
<select id="getGpuConfigList" resultType="com.m2pool.lease.dto.v2.PowerIncomeInfoDto">
<!-- SELECT mc.coin,-->
<!-- mc.algorithm,-->
<!-- mc.hashrate as power,-->
<!-- mc.month_income as monthIncome,-->
<!-- mc.power_dissipation as powerDissipation,-->
<!-- mc.unit,-->
<!-- mc.machine_id as machineId-->
<!-- FROM (-->
<!-- SELECT-->
<!-- distinct-->
<!-- coin,-->
<!-- algorithm-->
<!-- FROM-->
<!-- lease_machine_config-->
<!-- WHERE-->
<!-- machine_id = #{ids[0]}-->
<!-- LIMIT 10 ) AS t-->
<!-- RIGHT JOIN (-->
<!-- SELECT-->
<!-- coin,-->
<!-- algorithm,-->
<!-- sum(hashrate) as hashrate,-->
<!-- sum(month_income) as month_income,-->
<!-- sum(power_dissipation) as power_dissipation,-->
<!-- unit,-->
<!-- machine_id-->
<!-- FROM-->
<!-- lease_machine_config-->
<!-- WHERE-->
<!-- machine_id in-->
<!-- <foreach collection="ids" item="id" open="(" separator="," close=")">-->
<!-- #{id}-->
<!-- </foreach>-->
<!-- group by machine_id,coin, algorithm-->
<!-- ) mc ON t.coin = mc.coin AND t.algorithm = mc.algorithm-->
SELECT
coin,
algorithm,
@@ -190,7 +155,7 @@
FROM
lease_machine_config
WHERE
machine_id in
status = true AND machine_id in
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
@@ -334,7 +299,7 @@
gpc.algorithm
FROM
lease_machine lm
JOIN lease_machine_config gpc ON lm.id = gpc.machine_id
JOIN lease_machine_config gpc ON lm.id = gpc.machine_id AND gpc.status = 1
GROUP BY
lm.id,
gpc.coin,
@@ -384,7 +349,7 @@
<foreach collection="list" item="item" separator=";">
UPDATE lease_machine
SET
sale_out_numbers = #{item.saleOutNumbers}
sale_out_numbers = #{item.saleOutNumbers},
sale_state = CASE WHEN #{item.saleOutNumbers} = sale_numbers THEN 1 ELSE sale_state END
WHERE
id = #{item.id}
@@ -392,19 +357,89 @@
</foreach>
</update>
<select id="getCoinAndAlgoById" resultType="java.util.Map">
<select id="getCoinAndAlgoById" resultType="com.m2pool.lease.dto.v2.MiningConfigDto">
select
id,
coin,
algorithm as algo,
id,
coin,
mining_tcp_gpu_url as pool,
algorithm as algo
from lease_pool_coin_config
where
id IN
<foreach collection="id" item="ids" open="(" separator="," close=")">
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
<select id="getEffectMachineAndTempMachineList" resultType="com.m2pool.lease.dto.v2.MachineConfigDto">
select
id,
machine_id as machineId,
brand,
name,
memory,
coin,
algorithm,
false as isTemp
from lease_machine_config
where
machine_id = #{machineId}
union all
select
id,
machine_id as machineId,
brand,
name,
memory,
coin,
algorithm,
true as isTemp
from lease_machine_temp_config
where
machine_id = #{machineId}
</select>
<select id="getSupportCoin" resultType="java.lang.String">
SELECT DISTINCT
sc.coin
FROM
lease_mining_software_config sc
JOIN lease_pool_coin_config cc ON sc.coin = cc.coin
AND cc.STATUS = 1
</select>
<select id="getSupportAlgo" resultType="java.lang.String">
SELECT DISTINCT
cc.`algorithm`
FROM
lease_mining_software_config sc
JOIN lease_pool_coin_config cc ON sc.`algorithm` = cc.`algorithm`
AND cc.STATUS = 1 AND sc.coin = #{coin} AND cc.coin = #{coin}
</select>
<select id="checkIsEffect" resultType="com.m2pool.lease.dto.v2.MachineEffectDto">
SELECT
DISTINCT
sub.machine_id as machineId,
true as effect
FROM
(
SELECT
mp.machine_id,
mp.coin,
mp.`algorithm`
FROM
lease_machine_config mp
JOIN lease_mining_software_config sc ON mp.coin = sc.coin
AND mp.`algorithm` = sc.`algorithm`
) sub
JOIN lease_pool_coin_config cc ON sub.coin = cc.coin
AND sub.`algorithm` = cc.`algorithm`
AND cc.STATUS = 1
WHERE
sub.machine_id IN
<foreach collection="list" item="item" open="(" separator="," close=")">
#{item.id}
</foreach>
</select>
</mapper>

View File

@@ -28,9 +28,9 @@
(`machine_id` = #{item} AND del = false)
</foreach>
</select>
<select id="getOrderTotalPriceGroupByChainAndCoin" resultType="java.util.Map">
<select id="getOrderTotalPriceGroupByChainAndCoin" resultType="com.m2pool.lease.entity.LeaseMachinePrice">
select
machine_id as productMachineId,
machine_id as machineId,
price as price
from lease_machine_price
where

View File

@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.m2pool.lease.mapper.LeaseMachineTempConfigMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.m2pool.lease.entity.LeaseMachineTempConfig">
<id column="id" property="id" />
<result column="machine_id" property="machineId" />
<result column="brand" property="brand" />
<result column="name" property="name" />
<result column="memory" property="memory" />
<result column="status" property="status" />
<result column="coin" property="coin" />
<result column="hashrate" property="hashrate" />
<result column="algorithm" property="algorithm" />
<result column="icon" property="icon" />
<result column="unit" property="unit" />
<result column="month_income" property="monthIncome" />
<result column="power_dissipation" property="powerDissipation" />
<result column="del" property="del" />
<result column="effect_time" property="effectTime" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, machine_id, brand, name, memory, status, coin, hashrate, algorithm, icon, unit, month_income, power_dissipation, del, effect_time
</sql>
<insert id="insertBatch">
INSERT INTO lease_machine_temp_config (
machine_id,
brand,
`name`,
memory,
status,
coin,
hashrate,
algorithm,
icon,
unit,
month_income,
power_dissipation,
effect_time
)
VALUES
<foreach collection="list" item="item" separator=",">
(#{item.machineId},
#{item.brand},
#{item.name},
#{item.memory},
#{item.status},
#{item.coin},
#{item.hashrate},
#{item.algorithm},
#{item.icon},
#{item.unit},
#{item.monthIncome},
#{item.powerDissipation},
#{item.effectTime})
</foreach>
</insert>
<insert id="insertBatchByTempList">
INSERT INTO lease_machine_config (
machine_id,
brand,
`name`,
memory,
status,
coin,
hashrate,
algorithm,
icon,
unit,
month_income,
power_dissipation
)
VALUES
<foreach collection="list" item="item" separator=",">
(#{item.machineId},
#{item.brand},
#{item.name},
#{item.memory},
#{item.status},
#{item.coin},
#{item.hashrate},
#{item.algorithm},
#{item.icon},
#{item.unit},
#{item.monthIncome},
#{item.powerDissipation})
</foreach>
</insert>
</mapper>

View File

@@ -16,5 +16,20 @@
<sql id="Base_Column_List">
id, name, coin, algorithm, icon, del
</sql>
<select id="selectSupportAlgorithm" resultType="com.m2pool.lease.dto.v2.CoinAndAlgorithmDto">
SELECT DISTINCT
sc.coin,
sc.algorithm,
IF(cc.id,true,false) as isEffect
FROM
lease_mining_software_config sc
LEFT JOIN lease_pool_coin_config cc ON sc.coin = cc.coin AND cc.`algorithm` = sc.`algorithm` AND cc.status = 1
WHERE
sc.del = 0
AND sc.name IN
<foreach item="item" index="index" collection="miningsofts" separator="," open="(" close=")">
#{item}
</foreach>
</select>
</mapper>

View File

@@ -27,7 +27,8 @@
loo.total_price as totalPrice,
loo.status,
loo.create_time as createTime,
loo.update_time as endTime
loo.update_time as endTime,
loo.shop_name as shopName
from lease_order_info loo
<where>
del = 0 and user_id = #{userId}
@@ -44,7 +45,9 @@
loo.user_id as userId,
loo.total_price as totalPrice,
loo.status,
loo.create_time as createTime
loo.create_time as createTime,
loo.update_time as endTime,
loo.shop_name as shopName
from lease_order_info loo
<where>
del = 0

View File

@@ -101,6 +101,21 @@
#{id}
</foreach>
</select>
<select id="getOneDayApartOrder" resultType="com.m2pool.lease.dto.v2.OrderTimeInfoDto">
SELECT
id,
machine_id AS machineId,
order_id AS orderId,
order_item_id as orderItemId,
lease_time AS leaseTime,
create_time AS startTime,
price,
numbers
FROM
lease_order_item
WHERE
STATUS = 1
</select>
</mapper>

View File

@@ -5,11 +5,13 @@
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.m2pool.lease.entity.LeaseOrderMining">
<id column="id" property="id" />
<result column="user_id" property="userId" />
<result column="order_id" property="orderId" />
<result column="coin" property="coin" />
<result column="algorithm" property="algorithm" />
<result column="pool" property="pool" />
<result column="pool_url" property="poolUrl" />
<result column="pool_user" property="poolUser" />
<result column="worker_id" property="workerId" />
<result column="wallet_address" property="walletAddress" />
<result column="watch_url" property="watchUrl" />
@@ -21,7 +23,142 @@
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, order_id, coin, algorithm, pool, pool_url, worker_id, wallet_address, watch_url, status, start_time, end_time, del
id,user_id, order_id, coin, algorithm, pool, pool_url, pool_user, worker_id, wallet_address, watch_url, status, start_time, end_time, del
</sql>
<insert id="insertBatch">
INSERT INTO lease_order_mining (
order_id,
machine_id,
order_item_id,
user_id,
coin,
algorithm,
pool,
pool_url,
pool_user,
worker_id,
wallet_address,
start_time,
end_time
)
VALUES
<foreach collection="list" item="item" separator=",">
(
#{item.orderId},
#{item.machineId},
#{item.orderItemId},
#{item.userId},
#{item.coin},
#{item.algorithm},
#{item.pool},
#{item.poolUrl},
#{item.poolUser},
#{item.workerId},
#{item.walletAddress},
#{item.startTime},
#{item.endTime})
</foreach>
</insert>
<update id="updateOrderMining">
UPDATE lease_order_mining
SET watch_url = #{clientConfigurationMining.watchUrl}
WHERE user_id = #{userEmail} AND pool = #{clientConfigurationMining.pool}
</update>
<select id="getPurchasedItems" resultType="com.m2pool.lease.dto.v2.PurchasedMachineListDto">
select
id ,
coin,
algorithm,
pool,
pool_url as poolUrl,
pool_user as poolUser,
worker_id as workerId,
wallet_address as walletAddress,
watch_url as watchUrl,
status,
start_time as startTime,
end_time as endTime
FROM lease_order_mining
WHERE user_id = #{userEmail}
ORDER BY status DESC,end_time DESC
</select>
<select id="getPurchasedInfo" resultType="com.m2pool.lease.dto.v2.PurchasedMachineDto">
select
id ,
coin,
algorithm,
pool,
pool_url as poolUrl,
pool_user as poolUser,
worker_id as workerId,
wallet_address as walletAddress,
watch_url as watchUrl,
status,
start_time as startTime,
end_time as endTime
FROM lease_order_mining
WHERE id = #{id}
</select>
<select id="getRecentlyHashrate" resultType="com.m2pool.lease.dto.v2.MiningHashrateInfoDto">
SELECT
date_time AS recordTime,
hashrate AS power
FROM
$ {pool}
WHERE
wallet = #{wallet} and coin = #{coin} and miner = #{miner} order by date_time desc limit 1
</select>
<select id="getRecently24HourHashrate" resultType="com.m2pool.lease.dto.v2.RealHashrateInfoDto">
SELECT
hashrate AS power,
pool_name AS pool,
wallet AS walletAddress,
miner AS miner,
algorithm,
coin
FROM
`${tableName}`
WHERE
datetime >= DATE_SUB(#{now}, INTERVAL 24 HOUR) AND (wallet, coin, miner, algorithm) IN (
<foreach collection="list" item="item" separator=",">
(#{item.walletAddress}, #{item.coin}, #{item.workerId}, #{item.algorithm})
</foreach>
)
</select>
<select id="getMiningInfoByOrderId" resultType="com.m2pool.lease.dto.v2.PurchasedMachineListDto">
select
id ,
order_item_id as orderItemId,
coin,
algorithm,
pool,
pool_url as poolUrl,
pool_user as poolUser,
worker_id as workerId,
wallet_address as walletAddress,
watch_url as watchUrl,
status,
start_time as startTime,
end_time as endTime
FROM lease_order_mining
WHERE status = 1 and order_item_id in
<foreach item="item" collection="orderIds" separator="," open="(" close=")">
#{item}
</foreach>
</select>
<select id="checkMiningMinersExist" resultType="com.m2pool.lease.dto.v2.OrderMiningInfoDto">
select
worker_id as workerId,
wallet_address as walletAddress
FROM lease_order_mining
WHERE status = 1 AND (
<foreach collection="list" item="item" separator="OR">
(`wallet_address` = #{item.walletAddress} AND worker_id = #{item.workerId} )
</foreach>
)
</select>
</mapper>

View File

@@ -27,7 +27,7 @@
</select>
<select id="getPriceByOrderItems" resultType="java.util.Map">
select
product_machine_id as productMachineId,
product_machine_id as machineId,
price as price
from lease_product_machine_price
where