update 租赁系统1.1.0 钱包地址参数加密,gpu矿机新增到订单流程测试修改等

This commit is contained in:
yyb
2025-12-22 09:42:57 +08:00
parent 6250442c77
commit f1896bc1ae
38 changed files with 1031 additions and 230 deletions

View File

@@ -0,0 +1,14 @@
package com.m2pool.lease.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 标记在方法上,表示需要对请求体进行解密
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Decrypt {
}

View File

@@ -0,0 +1,14 @@
package com.m2pool.lease.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 标记在字段上,表示该字段需要解密
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptedField {
}

View File

@@ -0,0 +1,127 @@
package com.m2pool.lease.aspect;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.json.JSONUtil;
import com.m2pool.lease.annotation.EncryptedField;
import com.m2pool.lease.exception.RSAException;
import com.m2pool.lease.utils.RsaUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
import java.lang.reflect.Field;
import java.lang.reflect.Parameter;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Map;
@Aspect
@Component
public class DecryptAspect {
// 定义切点,拦截所有带 @Decrypt 注解的方法
@Pointcut("@annotation(com.m2pool.lease.annotation.Decrypt)")
public void decryptPointcut() {}
@Around("decryptPointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] args = joinPoint.getArgs();
Parameter[] parameters = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameters();
for (int i = 0; i < parameters.length; i++) {
// 找到被 @RequestBody 标记的参数
if (parameters[i].isAnnotationPresent(RequestBody.class)) {
Object requestBody = args[i];
// 递归解密对象中的加密字段
decryptObjectFields(requestBody);
}
}
// 继续执行原方法
return joinPoint.proceed(args);
}
private void decryptObjectFields(Object obj) {
if (obj == null) {
return;
}
// 处理集合或数组类型
if (obj instanceof Collection) {
for (Object item : (Collection<?>) obj) {
decryptObjectFields(item);
}
return;
}
// 处理Map类型
if (obj instanceof Map) {
for (Object value : ((Map<?, ?>) obj).values()) {
decryptObjectFields(value);
}
return;
}
// 处理普通对象
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(EncryptedField.class)) {
handleEncryptedField(obj, field);
} else {
//这里可能是普通对象或者集合或其他非EncryptedField标记的普通属性字段
handleNestedField(obj, field);
}
}
}
private void handleEncryptedField(Object obj, Field field) {
field.setAccessible(true);
try {
Object value = field.get(obj);
if (value != null) {
String encryptedValue = value.toString();
System.out.println("加密前地址:"+value +"类型"+(value instanceof String));
String decryptedValue = RsaUtils.decrypt(encryptedValue);
System.out.println("解密后地址:"+decryptedValue);
field.set(obj, decryptedValue);
}
} catch (Exception e) {
throw new RSAException("解密失败: " + e.getMessage());
} finally {
field.setAccessible(false);
}
}
private void handleNestedField(Object obj, Field field) {
field.setAccessible(true);
try {
Object fieldValue = field.get(obj);
if (fieldValue != null && !isPrimitiveOrWrapper(fieldValue)) {
decryptObjectFields(fieldValue);
}
} catch (IllegalAccessException e) {
// 忽略访问异常
} finally {
field.setAccessible(false);
}
}
private boolean isPrimitiveOrWrapper(Object obj) {
Class<?> clazz = obj.getClass();
return clazz.isPrimitive() ||
clazz == String.class ||
clazz == Integer.class ||
clazz == Long.class ||
clazz == Double.class ||
clazz == Float.class ||
clazz == Boolean.class ||
clazz == Character.class ||
clazz == Byte.class ||
clazz == Short.class ||
clazz == BigDecimal.class;
}
}

View File

@@ -1,12 +1,10 @@
package com.m2pool.lease.controller;
import com.m2pool.lease.annotation.Decrypt;
import com.m2pool.lease.dto.PageResult;
import com.m2pool.lease.dto.Result;
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.dto.v2.*;
import com.m2pool.lease.service.LeaseOrderInfoService;
import com.m2pool.lease.vo.BaseVo;
import com.m2pool.lease.vo.OrderAndCodeVo;
@@ -52,6 +50,7 @@ public class LeaseOrderInfoV2Controller {
@ApiOperation("创建订单及订单详情 + 支付订单(返回二维码内容)")
@PostMapping("/addOrdersV2")
@Decrypt
public Result<String> addOrdersV2(@RequestBody OrderAndCodeVo orderAndCodeVo) {
return leaseOrderInfoService.addOrdersV2(orderAndCodeVo);
}
@@ -70,7 +69,7 @@ public class LeaseOrderInfoV2Controller {
@ApiOperation("买家:根据id查询已购矿机详情")
@PostMapping("/getPurchasedInfo")
public Result<PurchasedMachineDto> getPurchasedInfo(@RequestBody BaseVo baseVo) {
public Result<MiningConfigInfoDto> getPurchasedInfo(@RequestBody BaseVo baseVo) {
return leaseOrderInfoService.getPurchasedInfo(baseVo);
}
}

View File

@@ -1,6 +1,7 @@
package com.m2pool.lease.controller;
import com.m2pool.lease.annotation.Decrypt;
import com.m2pool.lease.dto.*;
import com.m2pool.lease.service.LeaseProductService;
import com.m2pool.lease.service.LeaseUserOwnedProductService;
@@ -14,6 +15,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
/**
@@ -121,6 +123,7 @@ public class LeaseProductController {
@ApiOperation("新增绑定店铺钱包并设置店铺下面每个矿机该钱包币种的售价 + 钱包绑定")
@PostMapping("/updateProductListForShopWalletConfig")
@Decrypt
public Result<String> updateProductListForShopWalletConfig(@RequestBody ProductMachineForWalletConfigVo productMachineForWalletConfigVo) {
return leaseProductService.updateProductListForShopWalletConfig(productMachineForWalletConfigVo);
}

View File

@@ -1,5 +1,6 @@
package com.m2pool.lease.dto;
import com.m2pool.lease.annotation.EncryptedField;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
@@ -7,7 +8,6 @@ import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.util.List;
/**
@@ -26,6 +26,7 @@ import java.util.List;
public class ProductMachineForWalletConfigVo {
@ApiModelProperty(value = "钱包地址")
@EncryptedField
private String payAddress;
@ApiModelProperty(value = "支付链")

View File

@@ -1,15 +1,12 @@
package com.m2pool.lease.dto.v2;
import com.m2pool.lease.dto.PageResult;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
import java.math.BigDecimal;
@Data
@Builder
@@ -37,4 +34,29 @@ public class CoinAndAlgorithmDto {
*/
private Boolean isEffect;
/**
* gpu名称
*/
private String model;
/**
* gpu 功耗
*/
private BigDecimal powerDissipation;
/**
* gpu 理论算力
*/
private BigDecimal hashRate;
/**
* 月收入
*/
private BigDecimal monthIncome;
/**
* 是否新增 gpu 配置
*/
private Boolean isAdd;
}

View File

@@ -0,0 +1,27 @@
package com.m2pool.lease.dto.v2;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
* <p>
* 商品列表分页请求对象
* </p>
*
* @author yyb
* @since 2025-07-23
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MachineHashRateMapDto {
private Long machineId;
private BigDecimal hashRate;
}

View File

@@ -0,0 +1,56 @@
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;
import java.util.List;
/**
* <p>
* 挖矿中订单
* </p>
*
* @author yyb
* @since 2025-12-02
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "已购商品挖矿配置详情返回对象",value = "MiningConfigInfoDto" )
public class MiningConfigInfoDto {
@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 walletAddress;
@ApiModelProperty(value = "挖矿信息页面地址")
private String watchUrl;
@ApiModelProperty(value = "已购矿机信息")
private List<PurchasedMachineDto> purchasedMachines;
}

View File

@@ -6,8 +6,8 @@ import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import scala.math.BigDecimal;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@@ -25,9 +25,12 @@ import java.time.LocalDateTime;
@AllArgsConstructor
@ApiModel(description = "矿机对应实时算力放回对象",value = "MiningHashrateInfoDto" )
public class MiningHashrateInfoDto {
@ApiModelProperty(value = "算力")
@ApiModelProperty(value = "实时算力")
private BigDecimal power;
@ApiModelProperty(value = "记录时间")
@ApiModelProperty(value = "最近实时算力记录时间")
private LocalDateTime recordTime;
@ApiModelProperty(value = "矿工号")
private String miner;
}

View File

@@ -7,6 +7,7 @@ import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
@@ -30,37 +31,12 @@ public class PurchasedMachineDto {
//@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;
private Integer status;
@ApiModelProperty(value = "挖矿开始时间")
@@ -70,7 +46,11 @@ public class PurchasedMachineDto {
@ApiModelProperty(value = "挖矿结束时间")
private LocalDateTime endTime;
@ApiModelProperty(value = "算力信息")
private MiningHashrateInfoDto miningHashrateInfo;
@ApiModelProperty(value = "实时算力")
private BigDecimal power;
@ApiModelProperty(value = "最近实时算力记录时间")
private LocalDateTime recordTime;
}

View File

@@ -50,6 +50,38 @@ public class LeaseGpuConfig implements Serializable {
*/
private Integer memory;
/**
* 币种
*/
private String coin;
/**
* 算力 单位MH/s
*/
private BigDecimal hashrate;
/**
* 算法
*/
private String algorithm;
/**
* 图标
*/
private String icon;
/**
* 单位 默认MH/S
*/
private String unit;
/**
* 月收入
*/
private BigDecimal monthIncome;
/**
* 功耗 单位kw/h
*/

View File

@@ -41,6 +41,11 @@ public class GlobalExceptionHandler {
return Result.fail(e.getMessage());
}
@ExceptionHandler(RSAException.class)
public Result<String> handleRSAException(RSAException e) {
return Result.fail(e.getMessage());
}
/**
* 处理其他未明确捕获的异常,返回统一的错误结果。
*

View File

@@ -0,0 +1,10 @@
package com.m2pool.lease.exception;
/**
* 非对称加密异常
*/
public class RSAException extends RuntimeException {
public RSAException(String message) {
super(message);
}
}

View File

@@ -1,8 +1,10 @@
package com.m2pool.lease.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.m2pool.lease.dto.v2.CoinAndAlgorithmDto;
import com.m2pool.lease.entity.LeaseGpuConfig;
import com.m2pool.lease.netty.message.GpuMessage;
import com.m2pool.lease.task.info.BlockInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -24,5 +26,13 @@ public interface LeaseGpuConfigMapper extends BaseMapper<LeaseGpuConfig> {
* @param gpuMessages
* @return
*/
int insertOrUpdate(@Param("list") Collection<GpuMessage> gpuMessages);
int insertOrUpdateBatchByGpuInfo(@Param("list") List<CoinAndAlgorithmDto> gpuMessages);
/**
* 通过实体类批量插入或更新
* @param leaseGpuConfigs
* @return
*/
int insertOrUpdateBatchByEntity(@Param("list") List<LeaseGpuConfig> leaseGpuConfigs);
}

View File

@@ -1,16 +1,16 @@
package com.m2pool.lease.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.m2pool.lease.dto.v2.CoinAndAlgorithmDto;
import com.m2pool.lease.dto.v2.CoinAndAlgorithmListDto;
import com.m2pool.lease.dto.v2.MiningConfigSelectDto;
import com.m2pool.lease.dto.v2.*;
import com.m2pool.lease.entity.LeaseMachineConfig;
import com.m2pool.lease.vo.BaseVo;
import com.m2pool.lease.vo.v2.CoinAndAlgorithmVo;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* <p>
@@ -38,4 +38,10 @@ public interface LeaseMachineConfigMapper extends BaseMapper<LeaseMachineConfig>
*/
List<CoinAndAlgorithmDto> getMachineSupportCoinAndAlgorithm(@Param("machineIds") List<BaseVo> machineIds);
/**
* 获取理论算力
* @return
*/
List<MachineHashRateMapDto> getTheoryHashRate(@Param("list") List<OrderMiningInfoDto> orderMiningInfoDtoList);
}

View File

@@ -23,5 +23,5 @@ public interface LeaseMiningSoftwareConfigMapper extends BaseMapper<LeaseMiningS
* @param miningsofts
* @return
*/
List<CoinAndAlgorithmDto> selectSupportAlgorithm(@Param("miningsofts") List<String> miningsofts);
List<CoinAndAlgorithmDto> selectSupportAlgorithm(@Param("miningsofts") List<String> miningsofts,@Param("modelList") List<String> modelList);
}

View File

@@ -5,11 +5,13 @@ 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.MapKey;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* <p>
@@ -50,22 +52,17 @@ public interface LeaseOrderMiningMapper extends BaseMapper<LeaseOrderMining> {
* @param id
* @return
*/
PurchasedMachineDto getPurchasedInfo(@Param("id") Long id);
MiningConfigInfoDto getPurchasedInfo(@Param("id") Long id);
/**
* 获取最近一次的算力
* @param pool
* @param wallet
* @param coin
* @param miner
* @param list
* @return
*/
@MiningDB
MiningHashrateInfoDto getRecentlyHashrate(@Param("pool") String pool,
@Param("wallet") String wallet,
@Param("coin") String coin,
@Param("miner") String miner);
@MapKey("miner")
Map<String,MiningHashrateInfoDto> getRecentlyHashrate(@Param("pool") String pool,@Param("wallet") String wallet,@Param("coin") String coin,@Param("list") List<PurchasedMachineDto> list);
/**
@@ -90,4 +87,12 @@ public interface LeaseOrderMiningMapper extends BaseMapper<LeaseOrderMining> {
* @return
*/
List<OrderMiningInfoDto> checkMiningMinersExist(@Param("list") List<OrderMiningInfoDto> list);
/**
* 获取用户购买的矿机信息
* @param info
* @return
*/
List<PurchasedMachineDto> getPurchasedMachineInfo(@Param("info") MiningConfigInfoDto info);
}

View File

@@ -87,59 +87,70 @@ public class ServerChannelHandler extends SimpleChannelInboundHandler<ClientMess
*/
private void handlerDownloadClientMessage(ChannelHandlerContext ctx,ClientMessage<Object> msg){
String id = msg.getId();
String[] split = id.split("::");
System.out.println("客户端消息"+JSONUtil.toJsonPrettyStr( split) + "长度"+split.length);
System.out.println("客户端消息 "+checkMessage(ctx,id,split[0]));
if (split.length == 2 && checkMessage(ctx,id,split[0])){
if (checkMessage(ctx,id)){
String[] split = id.split("::");
Object params = msg.getParams();
GpuAndSoftMessage gpuAndSoftMessage = JSONUtil.toBean(params.toString(), GpuAndSoftMessage.class);
Map<String, GpuMessage> gpus = gpuAndSoftMessage.getGpus();
List<String> miningsofts = gpuAndSoftMessage.getMiningsofts();
List<String> modelList = gpus.values().stream().map(GpuMessage::getModel).collect(Collectors.toList());
if (params instanceof GpuAndSoftMessage){
System.out.println("客户端消息"+JSONUtil.toJsonPrettyStr(params));
GpuAndSoftMessage gpuAndSoftMessage = (GpuAndSoftMessage) params;
Map<String, GpuMessage> gpus = gpuAndSoftMessage.getGpus();
List<String> miningsofts = gpuAndSoftMessage.getMiningsofts();
Collection<GpuMessage> gpuMessagesList = gpus.values();
//挖矿软件公共配置
List<CoinAndAlgorithmDto> supportAlgorithmAndCoin = leaseMiningSoftwareConfigMapper.selectSupportAlgorithm(miningsofts,modelList);
// 新增主机相关信息
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
LeaseMachine leaseMachine = leaseMachineMapper.selectOne(new LambdaQueryWrapper<LeaseMachine>()
.eq(LeaseMachine::getHostMac, id).eq(LeaseMachine::getDel, false));
if (leaseMachine == null){
LeaseShop shop = getShop(split[0]);
leaseMachine = LeaseMachine.builder()
.shopId(shop.getId())
.hostMac(id)
.type(true)
.state(true)
.saleNumbers(0).build();
List<CoinAndAlgorithmDto> needAddAlgo = supportAlgorithmAndCoin.stream()
.filter(CoinAndAlgorithmDto::getIsAdd).collect(Collectors.toList());
//挖矿软件公共配置
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();
if (!needAddAlgo.isEmpty()){
List<CoinAndAlgorithmDto> needAdd = new ArrayList<>();
for (String model : modelList) {
for (CoinAndAlgorithmDto coinAndAlgorithmDto : needAddAlgo) {
needAdd.add(CoinAndAlgorithmDto.builder()
.isEffect(coinAndAlgorithmDto.getIsEffect())
.coin(coinAndAlgorithmDto.getCoin())
.algorithm(coinAndAlgorithmDto.getAlgorithm())
.model(model)
.build()
);
}
}
//GPU配置信息配置
leaseGpuConfigMapper.insertOrUpdate(gpuMessagesList);
List<LeaseMachineConfig> leaseMachineConfigs = packageInsert(leaseMachine.getId(),gpus, supportAlgorithmAndCoin);
leaseMachineMapper.insert(leaseMachine);
leaseMachineConfigService.saveBatch(leaseMachineConfigs);
}else {
leaseMachine.setOnlineStatus(true);
leaseMachineMapper.updateById(leaseMachine);
//检测对照表
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);
leaseGpuConfigMapper.insertOrUpdateBatchByGpuInfo(needAdd);
}
} catch (Exception e) {
// 回滚事务
status.setRollbackOnly();
throw e;
leaseMachineMapper.insert(leaseMachine);
List<LeaseMachineConfig> leaseMachineConfigs = packageInsert(leaseMachine.getId(),gpus, supportAlgorithmAndCoin);
leaseMachineConfigService.saveBatch(leaseMachineConfigs);
}else {
leaseMachine.setOnlineStatus(true);
leaseMachineMapper.updateById(leaseMachine);
//检测对照表
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;
}
});
}
}
});
}
}
@@ -151,18 +162,25 @@ public class ServerChannelHandler extends SimpleChannelInboundHandler<ClientMess
*/
public List<LeaseMachineConfig> packageInsert(Long machineId,Map<String, GpuMessage> gpus,List<CoinAndAlgorithmDto> supportAlgorithmAndCoin){
List<LeaseMachineConfig> list = new ArrayList<>();
System.out.println("gpu信息"+ JSONUtil.toJsonStr(gpus.values()));
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")
.del(false)
.build()).collect(Collectors.toList()));
for (CoinAndAlgorithmDto coinAndAlgorithmDto : supportAlgorithmAndCoin) {
if (coinAndAlgorithmDto.getModel() != null && value.getModel().contains(coinAndAlgorithmDto.getModel())){
list.add(LeaseMachineConfig.builder()
.machineId(machineId)
.brand(value.getBrand())
.name(value.getModel())
.memory(value.getMem())
.coin(coinAndAlgorithmDto.getCoin())
.algorithm(coinAndAlgorithmDto.getAlgorithm())
.status(coinAndAlgorithmDto.getIsEffect())
.powerDissipation(coinAndAlgorithmDto.getPowerDissipation())
.hashrate(coinAndAlgorithmDto.getHashRate())
.monthIncome(coinAndAlgorithmDto.getMonthIncome())
.build());
}
}
}
return list;
}
@@ -263,15 +281,12 @@ public class ServerChannelHandler extends SimpleChannelInboundHandler<ClientMess
* @param msg
*/
private void handlerClientMiningMessage(ChannelHandlerContext ctx,ClientMessage<Object> msg){
String id = msg.getId();
String[] split = id.split("::");
if (split.length == 2 && checkMessage(ctx,id,split[0])){
if (checkMessage(ctx,msg.getId())){
String[] split = msg.getId().split("::");
Object params = msg.getParams();
if (params instanceof ClientConfigurationMining){
ClientConfigurationMining clientConfigurationMining = (ClientConfigurationMining) params;
//修改 当前邮箱账户 对应的该矿池的 一些基本信息 TODO 这里可以设置真实开始时间 和 状态改为1
leaseOrderMiningMapper.updateOrderMining(split[0],clientConfigurationMining);
}
ClientConfigurationMining clientConfigurationMining = JSONUtil.toBean(params.toString(), ClientConfigurationMining.class);
//修改 当前邮箱账户 对应的该矿池的 一些基本信息 TODO 这里可以设置真实开始时间 和 状态改为1
leaseOrderMiningMapper.updateOrderMining(split[0],clientConfigurationMining);
}
}
@@ -290,11 +305,15 @@ public class ServerChannelHandler extends SimpleChannelInboundHandler<ClientMess
* 检查消息的合法性
* @return
*/
private boolean checkMessage(ChannelHandlerContext ctx,String id,String authCode){
LeaseShop leaseShop = getShop(authCode);
private boolean checkMessage(ChannelHandlerContext ctx,String id){
System.out.println("客户端消息id"+ id);
String[] split = id.split("::");
if (split.length != 2){
return false;
}
LeaseShop leaseShop = getShop(split[0]);
Channel channel = ctx.channel();
ChannelManager.idMap.put(channel,id);
ChannelManager.macMap.put(channel,getIPString(ctx));
//不存在对应身份码的店铺 发送消息给客户端
if (leaseShop == null){
sendErrorResp(ctx,id);
@@ -341,10 +360,11 @@ public class ServerChannelHandler extends SimpleChannelInboundHandler<ClientMess
@Override
public void channelInactive(ChannelHandlerContext ctx) {
System.out.println("ip:"+getIPString(ctx)+"断开连接");
String mac = ChannelManager.macMap.get(ctx.channel());
String id = ChannelManager.idMap.get(ctx.channel());
//修改矿机离线状态为 离线
if (mac != null){
leaseMachineMapper.update(LeaseMachine.builder().onlineStatus(false).build(), new LambdaUpdateWrapper<LeaseMachine>().eq(LeaseMachine::getHostMac,mac));
if (id != null){
leaseMachineMapper.update(LeaseMachine.builder().onlineStatus(false).build(),
new LambdaUpdateWrapper<LeaseMachine>().eq(LeaseMachine::getHostMac,id));
}
ChannelManager.removeChannel(getIPString(ctx));
ctx.close();
@@ -382,28 +402,28 @@ public class ServerChannelHandler extends SimpleChannelInboundHandler<ClientMess
*/
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
String socketString = ctx.channel().remoteAddress().toString();
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
String ip = getIPString(ctx);
String id = ChannelManager.getIdByChannel(ip);
if (event.state() == IdleState.READER_IDLE) {
System.out.println("读超时,未收到任何客户端消息,断开连接");
System.out.println(id+"用户写超时,接受pong"+System.currentTimeMillis());
//System.out.println(id+"用户读超时,断开连接");
ChannelManager.removeChannel(ip);
ctx.disconnect();//断开
} else if (event.state() == IdleState.WRITER_IDLE ) {
System.out.println("写超时发送ping到客户端");
String id = ChannelManager.getIdByChannel(ip);
System.out.println(id+"用户写超时,发送ping"+System.currentTimeMillis());
if (id != null){
ClientMessage ping = new ClientMessage();
ping.setMethod(PING);
ping.setId(id);
ctx.writeAndFlush(ping);
}
} else if (event.state() == IdleState.ALL_IDLE) {
System.out.println("客户端 ALL_IDLE 总超时: " + socketString);
ChannelManager.removeChannel(ip);
ctx.disconnect();
}
//else if (event.state() == IdleState.ALL_IDLE) {
// ChannelManager.removeChannel(ip);
// ctx.disconnect();
//}
}
}

View File

@@ -29,7 +29,7 @@ public class ServerChannelInitializer extends ChannelInitializer<SocketChannel>
ChannelPipeline pipeline = socketChannel.pipeline();
//IdleStateHandler心跳机制,如果超时触发Handle中userEventTrigger()方法
pipeline.addLast("idleStateHandler",
new IdleStateHandler(PONG_TIME, PING_TIME, 0, TimeUnit.MINUTES));
new IdleStateHandler(PONG_TIME, PING_TIME, 0, TimeUnit.SECONDS));
pipeline.addLast(new LineBasedFrameDecoder(1024));
//字符串编解码器
pipeline.addLast(new MessageToJsonDecoder(),new MessageToJsonEncoder());

View File

@@ -2,10 +2,7 @@ package com.m2pool.lease.service;
import com.baomidou.mybatisplus.extension.service.IService;
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.dto.v2.*;
import com.m2pool.lease.entity.LeaseOrderInfo;
import com.m2pool.lease.vo.*;
import com.m2pool.lease.vo.v2.CoinAndAlgorithmVo;
@@ -106,5 +103,5 @@ public interface LeaseOrderInfoService extends IService<LeaseOrderInfo> {
* @param baseVo
* @return
*/
Result<PurchasedMachineDto> getPurchasedInfo(BaseVo baseVo);
Result<MiningConfigInfoDto> getPurchasedInfo(BaseVo baseVo);
}

View File

@@ -821,12 +821,19 @@ 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<MachineHashRateMapDto> theoryHashRate1 = leaseMachineConfigMapper.getTheoryHashRate(orderMiningInfoDtoList);
Map<Long, List<MachineHashRateMapDto>> theoryHashRateMap = theoryHashRate1.stream()
.collect(Collectors.groupingBy(MachineHashRateMapDto::getMachineId));
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);
List<MachineHashRateMapDto> machineHashRateMapDtos = theoryHashRateMap.get(machineId);
BigDecimal theoryHashRate = machineHashRateMapDtos.isEmpty() ? BigDecimal.ZERO : machineHashRateMapDtos.get(0).getHashRate();
String chain = orderInfoVo.getChain();
String coin = orderInfoVo.getCoin();
Long shopId = orderInfoVo.getShopId();
@@ -862,6 +869,7 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
.fromSymbol(walletInfo.getFromSymbol())
.numbers(orderInfoVo.getNumbers())
.type(leaseMachine.getType())
.theoryPower(theoryHashRate)
.build();
Long timestamp = startTime + orderInfoVo.getLeaseTime() * 24 * 60 * 60 * 1000L;
endMiningMap.put(machineId,timestamp);
@@ -903,7 +911,7 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
*/
private boolean checkGpuMachine(List<LeaseMachine> gpuMachines){
for (LeaseMachine gpuMachine : gpuMachines) {
if (ChannelManager.getChannelByMac(gpuMachine.getHostMac()) == null){
if (ChannelManager.getChannelById(gpuMachine.getHostMac()) == null){
return false;
}
}
@@ -1003,7 +1011,7 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
.pool_user(poolUser)
.worker_id(poolWorkerId)
.end_timestamp(endTime / 1000).build();
message.setId(userEmail+"::"+ mac);
message.setId(mac);
message.setMethod(MINING_RAG);
message.setParams( build);
//TCP 消息
@@ -1067,12 +1075,24 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
@Override
@DSTransactional
public Result<PurchasedMachineDto> getPurchasedInfo(BaseVo baseVo) {
public Result<MiningConfigInfoDto> getPurchasedInfo(BaseVo baseVo) {
//找到miner 和钱包 ,pool 矿池名
PurchasedMachineDto info = leaseOrderMiningMapper.getPurchasedInfo(baseVo.getId());
MiningConfigInfoDto info = leaseOrderMiningMapper.getPurchasedInfo(baseVo.getId());
List<PurchasedMachineDto> list = leaseOrderMiningMapper.getPurchasedMachineInfo(info);
//根据钱包 + 币种 + 矿工号 查询 pool 库 pool.kryptex 表 中对应 算力
MiningHashrateInfoDto recentlyHashrate = leaseOrderMiningMapper.getRecentlyHashrate(info.getPool(), info.getWalletAddress(), info.getCoin(), info.getWorkerId());
info.setMiningHashrateInfo(recentlyHashrate);
Map<String, MiningHashrateInfoDto> recentlyHashRateMap = leaseOrderMiningMapper
.getRecentlyHashrate(info.getPool(), info.getWalletAddress(), info.getCoin(), list);
for (PurchasedMachineDto purchasedMachineDto : list) {
MiningHashrateInfoDto miningHashrateInfoDto = recentlyHashRateMap.get(purchasedMachineDto.getWorkerId());
if (miningHashrateInfoDto != null){
purchasedMachineDto.setRecordTime(miningHashrateInfoDto.getRecordTime());
purchasedMachineDto.setPower(miningHashrateInfoDto.getPower());
}
}
info.setPurchasedMachines(list);
return Result.success(info);
}
}

View File

@@ -1,6 +1,7 @@
package com.m2pool.lease.service.impl;
import cn.hutool.json.JSONUtil;
import com.alibaba.druid.support.json.JSONUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -251,7 +252,9 @@ public class LeaseProductServiceImpl extends ServiceImpl<LeaseProductMapper, Lea
.select(LeaseShop::getId)
.eq(LeaseShop::getUserEmail, SecurityUtils.getUsername())
.eq(LeaseShop::getDel, false));
if (leaseShop == null){
return Result.fail("店铺不存在");
}
List<PayConfigDto> shopWalletInfo = leaseShopMapper.getShopWalletInfo(leaseShop.getId());
return Result.success(shopWalletInfo);
}

View File

@@ -7,10 +7,12 @@ import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.m2pool.common.core.utils.StringUtils;
import com.m2pool.common.security.utils.SecurityUtils;
import com.m2pool.lease.controller.LeaseShopController;
import com.m2pool.lease.dto.*;
import com.m2pool.lease.dto.v2.PayWithdrawSellerRecordDto;
import com.m2pool.lease.dto.v2.ShopWalletInfoDto;
import com.m2pool.lease.entity.*;
import com.m2pool.lease.exception.MachineException;
import com.m2pool.lease.exception.PaymentException;
import com.m2pool.lease.mapper.*;
import com.m2pool.lease.mq.message.RabbitmqPayWithdrawMessage;
@@ -66,8 +68,9 @@ public class LeaseShopServiceImpl extends ServiceImpl<LeaseShopMapper, LeaseShop
@Resource
private LeaseProductMachineMapper leaseProductMachineMapper;
@Resource
private LeaseProductMachinePriceMapper leaseProductMachinePriceMapper;
private LeaseMachinePriceMapper leaseMachinePriceMapper;
@Resource
private LeaseUserMapper leaseUserMapper;
@@ -81,6 +84,12 @@ public class LeaseShopServiceImpl extends ServiceImpl<LeaseShopMapper, LeaseShop
@Resource
private LeaseAutoAddressMapper leaseAutoAddressMapper;
@Resource
private LeaseMachineMapper leaseMachineMapper;
@Resource
private LeaseMachineConfigMapper leaseMachineConfigMapper;
@Override
public Result<String> addShop(ShopVo shopVo) {
LeaseShop leaseShop1 = leaseShopMapper.selectOne(new LambdaQueryWrapper<LeaseShop>()
@@ -143,9 +152,11 @@ public class LeaseShopServiceImpl extends ServiceImpl<LeaseShopMapper, LeaseShop
return Result.fail("店铺不存在");
}
//校验是否存在已售出的矿机
if (leaseProductMachineMapper.checkHasSaleMachineByShopId(baseVo.getId()) >= 1){
if (leaseProductMachineMapper.checkHasSaleMachineByShopId(baseVo.getId()) >= 1 || leaseMachineMapper.checkHasSaleMachineByShopId(baseVo.getId()) >= 1){
return Result.fail("关闭店铺失败,店铺存在租约中矿机");
}
if (leaseShop.getState() == 1){
leaseShop.setState(2);
boolean b = updateById(leaseShop);
@@ -202,16 +213,8 @@ public class LeaseShopServiceImpl extends ServiceImpl<LeaseShopMapper, LeaseShop
if (byId.getDel()){
return Result.fail("删除店铺失败,店铺已删除,请刷新页面");
}
//校验是否存在已售出的矿机
if (leaseProductMachineMapper.checkHasSaleMachineByShopId(baseVo.getId()) >= 1){
return Result.fail("删除店铺失败,店铺存在租约中矿机");
}
leaseProductMapper.update(LeaseProduct.builder().del(true).build(), new LambdaUpdateWrapper<LeaseProduct>()
.eq(LeaseProduct::getShopId, baseVo.getId()));
leaseProductMachineMapper.update(LeaseProductMachine.builder().del(true).build(), new LambdaUpdateWrapper<LeaseProductMachine>()
.eq(LeaseProductMachine::getShopId, baseVo.getId()));
updateMachineInfoV1(baseVo.getId());
updateMachineInfoV2(baseVo.getId());
boolean b = updateById(LeaseShop.builder().id(baseVo.getId()).del(true).build());
if (b){
return Result.success("删除成功");
@@ -220,6 +223,51 @@ public class LeaseShopServiceImpl extends ServiceImpl<LeaseShopMapper, LeaseShop
}
/**
* 删除店铺时,将店铺下所有矿机和对应钱包删除 V1版本
*
* */
public void updateMachineInfoV1(Long shopId){
//校验是否存在已售出的矿机
if (leaseProductMachineMapper.checkHasSaleMachineByShopId(shopId) >= 1){
throw new MachineException("删除店铺失败,店铺存在租约中矿机");
}
leaseProductMapper.update(LeaseProduct.builder().del(true).build(), new LambdaUpdateWrapper<LeaseProduct>()
.eq(LeaseProduct::getShopId, shopId));
leaseProductMachineMapper.update(LeaseProductMachine.builder().del(true).build(), new LambdaUpdateWrapper<LeaseProductMachine>()
.eq(LeaseProductMachine::getShopId, shopId));
}
/**
* 删除店铺时,将店铺下所有矿机和对应钱包删除 V2版本
*
* */
public void updateMachineInfoV2(Long shopId){
//校验是否存在已售出的矿机
if (leaseMachineMapper.checkHasSaleMachineByShopId(shopId) >= 1){
throw new MachineException("删除店铺失败,店铺存在租约中矿机");
}
//校验是否存在余额不为0的钱包
List<LeaseShopConfig> leaseShopConfigs = leaseShopConfigMapper.selectList(new LambdaQueryWrapper<LeaseShopConfig>()
.select(LeaseShopConfig::getId,LeaseShopConfig::getBalance)
.eq(LeaseShopConfig::getShopId, shopId).eq(LeaseShopConfig::getDel, false));
if (leaseShopConfigs.stream().anyMatch(leaseShopConfig -> leaseShopConfig.getBalance().compareTo(BigDecimal.ZERO) > 0)){
throw new MachineException("删除店铺失败,店铺存在余额不为0的钱包");
}
leaseMachineMapper.update(LeaseMachine.builder().del(true).build(), new LambdaUpdateWrapper<LeaseMachine>()
.eq(LeaseMachine::getShopId, shopId));
List<Long> ids = leaseMachineMapper.selectList(new LambdaQueryWrapper<LeaseMachine>()
.select(LeaseMachine::getId)
.eq(LeaseMachine::getShopId, shopId))
.stream().map(LeaseMachine::getId)
.collect(Collectors.toList());
leaseMachineConfigMapper.delete(new LambdaQueryWrapper<LeaseMachineConfig>().in(LeaseMachineConfig::getMachineId, ids));
leaseShopConfigMapper.update(LeaseShopConfig.builder().del(true).build(), new LambdaUpdateWrapper<LeaseShopConfig>()
.eq(LeaseShopConfig::getShopId, shopId));
}
@Override
@@ -444,10 +492,10 @@ public class LeaseShopServiceImpl extends ServiceImpl<LeaseShopMapper, LeaseShop
List<LeaseShopConfig> collect = leaseShopConfigs.stream().peek(leaseShopConfig -> leaseShopConfig.setDel(true)).collect(Collectors.toList());
boolean b = leaseShopConfigService.updateBatchById(collect);
if (b){
leaseProductMachinePriceMapper.update(LeaseProductMachinePrice.builder().del(true).build(),
new LambdaQueryWrapper<LeaseProductMachinePrice>()
.eq(LeaseProductMachinePrice::getChain, config.getChain())
.in(LeaseProductMachinePrice::getProductMachineId,ids)
leaseMachinePriceMapper.update(LeaseMachinePrice.builder().del(true).build(),
new LambdaQueryWrapper<LeaseMachinePrice>()
.eq(LeaseMachinePrice::getChain, config.getChain())
.in(LeaseMachinePrice::getMachineId,ids)
);
return Result.success("删除成功");
}

View File

@@ -0,0 +1,196 @@
package com.m2pool.lease.task;
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.m2pool.lease.constant.BlockInterval;
import com.m2pool.lease.entity.LeaseGpuConfig;
import com.m2pool.lease.entity.LeaseMiningSoftwareConfig;
import com.m2pool.lease.mapper.LeaseGpuConfigMapper;
import com.m2pool.lease.mapper.LeaseMiningSoftwareConfigMapper;
import com.m2pool.lease.task.info.*;
import io.lettuce.core.ScriptOutputType;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @Description 获取gpu 信息三方apiwhattomine.com 定时任务
* @Date 2025/12/16 15:38
* @Author yyb
*/
@Configuration
@EnableScheduling
public class GpuRequestApiTask {
@Resource
private LeaseGpuConfigMapper leaseGpuConfigMapper;
@Resource
private LeaseMiningSoftwareConfigMapper leaseMiningSoftwareConfigMapper;
@Scheduled(cron = "0 0 0/6 * * ? ")
//@Scheduled(cron = "0 0/5 * * * ? ")
public void gpuInfoTask(){
List<GpuInfo> gpuInfos = fetchGpuInfo();
List<LeaseGpuConfig> leaseGpuConfigs = convertGpuInfoToLeaseConfig(gpuInfos);
List<LeaseMiningSoftwareConfig> leaseMiningSoftwareConfigs = leaseMiningSoftwareConfigMapper.
selectList(new LambdaQueryWrapper<>());
//选出挖矿软件只支持的算法和币种
List<LeaseGpuConfig> leaseGpuConfigs1 = new ArrayList<>();
for (LeaseGpuConfig leaseGpuConfig : leaseGpuConfigs) {
for (LeaseMiningSoftwareConfig leaseMiningSoftwareConfig : leaseMiningSoftwareConfigs) {
if (leaseMiningSoftwareConfig.getAlgorithm().contains(leaseGpuConfig.getAlgorithm())){
leaseGpuConfig.setCoin(leaseMiningSoftwareConfig.getCoin());
leaseGpuConfigs1.add(leaseGpuConfig);
break;
}
}
}
List<AlgorithmInfo> coinNetPowers = fetchMiningPower();
if (!coinNetPowers.isEmpty()){
Map<String, AlgorithmInfo> coinPowerMap = coinNetPowers.stream()
.collect(Collectors.toMap(AlgorithmInfo-> AlgorithmInfo.getCoin()+"-"+AlgorithmInfo.getName(), Function.identity()));
for (LeaseGpuConfig leaseGpuConfig : leaseGpuConfigs1) {
AlgorithmInfo coinPower = coinPowerMap.get(leaseGpuConfig.getCoin()+"-"+leaseGpuConfig.getAlgorithm());
BigDecimal monthIncome = calculateMonthIncome(leaseGpuConfig, coinPower);
leaseGpuConfig.setMonthIncome(monthIncome);
}
}
leaseGpuConfigMapper.insertOrUpdateBatchByEntity(leaseGpuConfigs1);
}
/**
* 计算月收入
* @param leaseGpuConfig
* @param coinPower
* @return
*/
private static BigDecimal calculateMonthIncome(LeaseGpuConfig leaseGpuConfig, AlgorithmInfo coinPower) {
BigDecimal monthIncome = BigDecimal.ZERO;
if (coinPower != null){
monthIncome = leaseGpuConfig.getHashrate()
.divide(coinPower.getHashrate().divide(BigDecimal.valueOf(1000 * 1000), 8, RoundingMode.HALF_UP), 8, RoundingMode.HALF_UP)
.multiply(BigDecimal.valueOf(24 * 60 * 60)
.divide(coinPower.getBlockInterval(), 8, RoundingMode.HALF_UP))
.multiply(coinPower.getBlockReward()).multiply(BigDecimal.valueOf(30))
.multiply(coinPower.getPrice())
;
}
return monthIncome;
}
/**
* 三方api 获取gpu信息
* @return
*/
public List<GpuInfo> fetchGpuInfo() {
String url = "https://whattomine.com/api/v1/gpus?api_token=8ea12738081eaa70a37d62364ddfffecc0c96a6047f02b4cc728af7588fcdbd8";
String response = HttpRequest.get(url).execute().body();
// 将JSONUtil.parseArray改为JSON.parseArray
JSONArray gpusArray = JSON.parseArray(response);
List<GpuInfo> gpuInfoList = new ArrayList<>();
for (Object obj : gpusArray) {
// JSONObject转换方式
JSONObject gpuJson = (JSONObject) obj;
GpuInfo gpuInfo = new GpuInfo();
// 设置基本GPU信息
gpuInfo.setId(gpuJson.getLong("id"));
gpuInfo.setName(gpuJson.getString("name"));
gpuInfo.setRelease_date(gpuJson.getString("release_date"));
// 处理算法信息
JSONArray algorithmsArray = gpuJson.getJSONArray("algorithms");
List<AlgorithmInfo> algorithmInfos = new ArrayList<>();
for (Object algoObj : algorithmsArray) {
// JSONObject转换方式
JSONObject algoJson = (JSONObject) algoObj;
AlgorithmInfo algorithmInfo = new AlgorithmInfo();
algorithmInfo.setName(algoJson.getString("name"));
algorithmInfo.setHashrate(algoJson.getBigDecimal("hashrate"));
algorithmInfo.setPower(algoJson.getBigDecimal("power"));
algorithmInfos.add(algorithmInfo);
}
gpuInfo.setAlgorithms(algorithmInfos);
gpuInfoList.add(gpuInfo);
}
return gpuInfoList;
}
public List<LeaseGpuConfig> convertGpuInfoToLeaseConfig(List<GpuInfo> gpuInfos) {
List<LeaseGpuConfig> leaseGpuConfigs = new ArrayList<>();
for (GpuInfo gpuInfo : gpuInfos) {
if (gpuInfo.getAlgorithms() != null) {
for (AlgorithmInfo algorithm : gpuInfo.getAlgorithms()) {
LeaseGpuConfig config = buildGpuConfig(gpuInfo, algorithm);
leaseGpuConfigs.add(config);
}
}
}
return leaseGpuConfigs;
}
private static LeaseGpuConfig buildGpuConfig(GpuInfo gpuInfo, AlgorithmInfo algorithm) {
LeaseGpuConfig config = new LeaseGpuConfig();
config.setName(gpuInfo.getName());
config.setAlgorithm(algorithm.getName());
config.setHashrate(algorithm.getHashrate().divide(BigDecimal.valueOf(1000 * 1000),2, RoundingMode.HALF_UP));
config.setPowerDissipation(algorithm.getPower());
return config;
}
/**
* 三方api 获取gpu 对应算法的收益信息
* @return
*/
public static List<AlgorithmInfo> fetchMiningPower() {
String url = "https://whattomine.com/api/v1/coins?api_token=8ea12738081eaa70a37d62364ddfffecc0c96a6047f02b4cc728af7588fcdbd8";
String response = HttpRequest.get(url).execute().body();
JSONArray gpusArray = JSON.parseArray(response);
List<AlgorithmInfo> hashRateInfos = new ArrayList<>();
for (int i = 0; i < gpusArray.size(); i++) {
JSONObject item = gpusArray.getJSONObject(i);
AlgorithmInfo info = new AlgorithmInfo();
info.setCoin(item.getString("tag"));
info.setName(item.getString("algorithm"));
info.setHashrate(new BigDecimal(item.getLong("nethash")));
info.setBlockReward(BigDecimal.valueOf(item.getDouble("block_reward")));
info.setBlockInterval(BigDecimal.valueOf(item.getDouble("block_time")));
// 解析exchanges数组并获取第一个对象的price值
JSONArray exchanges = item.getJSONArray("exchanges");
if (!exchanges.isEmpty()) {
JSONObject firstExchange = exchanges.getJSONObject(0);
//30日平均币价
info.setPrice(BigDecimal.valueOf(firstExchange.getDouble("price30")).multiply(BigDecimal.valueOf(100000)));
}
hashRateInfos.add(info);
}
return hashRateInfos;
}
}

View File

@@ -0,0 +1,46 @@
package com.m2pool.lease.task.info;
import lombok.Data;
import java.math.BigDecimal;
/**
* @Description 算法信息类
* @Date 2025/12/16 15:43
* @Author yyb
*/
@Data
public class AlgorithmInfo {
/**
* 币种名称
*/
private String coin;
/**
* 算法名称
*/
private String name;
/**
* 挖矿算力
*/
private BigDecimal hashrate;
/**
* 功耗
*/
private BigDecimal power;
/**
* 报块间隔
*/
private BigDecimal blockInterval;
/**
* 报块奖励
*/
private BigDecimal blockReward;
private BigDecimal price;
}

View File

@@ -0,0 +1,34 @@
package com.m2pool.lease.task.info;
import lombok.Data;
import java.util.List;
/**
* @Description gpu 信息类
* @Date 2025/12/16 15:42
* @Author yyb
*/
@Data
public class GpuInfo {
/**
* gpu id
*/
private Long id;
/**
* gpu 名称
*/
private String name;
/**
* gpu 发布日期
*/
private String release_date;
/**
* gpu 算法信息
*/
private List<AlgorithmInfo> algorithms;
}

View File

@@ -0,0 +1,21 @@
package com.m2pool.lease.task.info;
import lombok.Data;
@Data
public class MiningRewardInfo {
private int id;
/**
* 币种名
*/
private String tag;
private String name;
private String estimated_rewards;
private String estimated_rewards24;
private String btc_revenue;
private String btc_revenue24;
private double revenue;
private double revenue24;
private double profit;
private double profit24;
}

View File

@@ -0,0 +1,31 @@
package com.m2pool.lease.task.info;
import lombok.Builder;
import lombok.Data;
import java.math.BigDecimal;
/**
* @Description 月收益信息
* @Date 2025/12/16 15:43
* @Author yyb
*/
@Builder
@Data
public class MonthIncomeInfo {
/**
* 算法名称
*/
private String algorithm;
/**
* 功耗
*/
private BigDecimal power;
/**
* 矿机算力 单位H/S
*/
private BigDecimal hashrate;
}

View File

@@ -1,12 +1,7 @@
package com.m2pool.lease.utils;
import com.m2pool.lease.entity.LeaseProduct;
import java.math.BigInteger;
import java.security.*;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.security.MessageDigest;
public class HashUtils {
@@ -58,25 +53,6 @@ public class HashUtils {
return decimalValue.toString(16) + key;
}
public static void main(String[] args) throws NoSuchAlgorithmException {
//Long inputStr = 1758610297L;
//System.out.println("加密结果" + sha256(inputStr));
// 生成RSA密钥对
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048); // 密钥长度设置为2048位
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
// 将公钥转换为Base64字符串格式
String publicKeyString = Base64.getEncoder().encodeToString(publicKey.getEncoded());
System.out.println("公钥 (Base64): " + publicKeyString);
// 将私钥转换为Base64字符串格式注意安全
String privateKeyString = Base64.getEncoder().encodeToString(privateKey.getEncoded());
System.out.println("私钥 (Base64): " + privateKeyString);
public static void main(String[] args) throws Exception {
}
}

View File

@@ -0,0 +1,31 @@
package com.m2pool.lease.utils;
import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
public class RsaUtils {
public static final String PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsQVIKYozXCfnXUw8+omYLdcdL1pTzmQh35YPsvn22wM4SQJKvMmXmcS6bI5Bu+5zCjL0F56DzfKz0BNZEwb46UshUOO+KFBUr8CxjYE8NOgIsoe5FUn57O6er9/KySaWlkpGZX49K+l3e90R+dFUEfRE/ijYpeZWkLRwcgWZ+2u6HGpl9h/eF6XD0aW9asDjdAbxUQ48TlaWgfP+OHC+Zy2GKGQG16EcDMczrN6a2HbFnwRIUKrFP67UqyRq11BTUziOhXLY8J0MFuwXUk2OY4VpqjrJjHHjlHYADjIL/5K4Io2AhRU9+QSsKFR2wGxi4e8vw2IXDzscrDuah/7YSwIDAQAB"; // 可公开
private static final String PRIVATE_KEY = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCxBUgpijNcJ+ddTDz6iZgt1x0vWlPOZCHflg+y+fbbAzhJAkq8yZeZxLpsjkG77nMKMvQXnoPN8rPQE1kTBvjpSyFQ474oUFSvwLGNgTw06Aiyh7kVSfns7p6v38rJJpaWSkZlfj0r6Xd73RH50VQR9ET+KNil5laQtHByBZn7a7ocamX2H94XpcPRpb1qwON0BvFRDjxOVpaB8/44cL5nLYYoZAbXoRwMxzOs3prYdsWfBEhQqsU/rtSrJGrXUFNTOI6FctjwnQwW7BdSTY5jhWmqOsmMceOUdgAOMgv/krgijYCFFT35BKwoVHbAbGLh7y/DYhcPOxysO5qH/thLAgMBAAECggEAYE00TczuVfMrbdkcz1ttW3rMWMd2EjDtMJqNXduYjNs6kpVZnUy6WZ/pn0bgweFd2i2QFw1YPQENC2SLL8u6PAhwTnYnK/1PpPFaZlXpMAsPODiX2toMyfWihKFly3pfr9EmbO55TttK8ip5LgPQDZhqtC36dn5Rl5ViGtWrSFgQOJm4Mu7px/6DbAu5c3u9yBqwA5Z90XDGIOivjjquQA4upexCvtBPYm/krCwsZcYHRBeihi+s4mt2Mz+a3u8NkJGxE7+QpXkWC0XR2KKiG4kdD6Sn+d9fG7UZUUBrb1p9Z5n3NV2oh3EKnrV3Pf/J0WEzrDZKBF4I9PMNzEkd2QKBgQDx9uW2HOqGnGv993L57+Twon7BxJHd0WQzdskTNEU7+cdNBYPbailjEOByGBPuupwOc2qTU6j+kyqSH5SoWDpnp1BnmFaJF6X52DJCQ6+2z5MfPApZbMDNSG/MOgPpHRjVcMfdPR/48W0S/1jlPh3ZIQ6bvL/011HYKqNF+NBp5QKBgQC7SfzSgqftZmLiZ4q4DfYGiWjSPQ+G4LwT+ehRNQ1IuJL8yO2tnuOxEN+Egxm5PEY55syeQ41alKWSeoRSFaopdCZZvPnuAEH8rhCgi0ymHIk+24TREZZMJhimBas77JCkRnWfJmfIPCXlv0fS4etzczx+Prntd5B7CKDKYPZWbwKBgQDw6Yblv/yycmQDxbCwse4cuW3Dt9gUJzvBEfA6Mr0MbjHFukwC1rzGajNO5jJSa3Kd4MSoU4/biOPGAbOdudrNAYXI36CpduZG0L+ZwpLdTeeKHdmgF+wPeKP3j3v9Sn9DZLtayFVhI+AaHiuAdEx7t7I31cK6IPjYZfuS252HEQKBgB6WK7r5aZFkyjtta4M5jHdu03aaUEHXy/97LcytXFkHA8JS0jYrn8XYGpjajEXrKRDlt38VSy2C3zN33MpHDX4toOHA8hRDaINqrf56IW9zc5qXYS5/r8sJ4bajcF6d+NVLSIRQUlyqLgpCXel7yecV58g6WEUG5MqjExdaFIDzAoGBAIlhI+jGPRn7lJVTZxik6/Kvj2KKpmpF+WMKer7hLuKd8Tu6iAldNjce9Y9uvD5lvyn+eSGG4OgEKKlPZ4vbY2c3AsYlsJu3ltaz203c61SVYa6tc3xeas9fiLUF7IXd6+6/1nDubG7mDwnMhmGV0pxVJKMUVqx+2LFDFqHaaTAN"; // 这里替换为你的实际私钥
public static String decrypt(String encryptedData) throws Exception {
// Base64解码加密数据
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);
// 加载私钥
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(PRIVATE_KEY));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
// 初始化Cipher进行解密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// 执行解密
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
}

View File

@@ -1,6 +1,6 @@
package com.m2pool.lease.vo;
import com.m2pool.common.core.decrpy.Decrypt;
import com.m2pool.lease.annotation.EncryptedField;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
@@ -8,8 +8,6 @@ import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* <p>
* 店铺商品配置请求对象
@@ -48,8 +46,8 @@ public class ShopConfigVo extends BaseVo {
/**
* 卖方对应收款钱包
*/
@Decrypt
@ApiModelProperty(value = "卖方对应收款钱包",example = "nexa:nqtsq5g50jkkmklvjyaflg46c4nwuy46z9gzswqe3l0csc7g")
private String payAddress;
}

View File

@@ -1,5 +1,6 @@
package com.m2pool.lease.vo.v2;
import com.m2pool.lease.annotation.EncryptedField;
import com.m2pool.lease.vo.BaseVo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@@ -31,6 +32,7 @@ public class ShopWalletInfoVo {
private String chain;
@ApiModelProperty(value = "卖方对应收款钱包",example = "nexa:nqtsq5g50jkkmklvjyaflg46c4nwuy46z9gzswqe3l0csc7g")
@EncryptedField
private String payAddress;
}

View File

@@ -17,17 +17,30 @@
<sql id="Base_Column_List">
id, brand, name, status, memory, power_dissipation, del
</sql>
<insert id="insertOrUpdate">
insert into lease_gpu_config (brand, name, memory)
<insert id="insertOrUpdateBatchByGpuInfo">
insert into lease_gpu_config ( name, algorithm,coin)
values
<foreach item="item" index="index" collection="list" separator=",">
(#{item.brand}, #{item.name}, #{item.mem})
(#{item.model}, #{item.algorithm},'')
</foreach>
ON DUPLICATE KEY UPDATE
brand = VALUES(brand),
`name` = VALUES(`name`),
memory = VALUES(memory)
name = VALUES(`name`),
`algorithm` = VALUES(`algorithm`),
coin = VALUES(coin)
</insert>
<insert id="insertOrUpdateBatchByEntity">
insert into lease_gpu_config ( name, algorithm, power_dissipation,hashrate,month_income)
values
<foreach item="item" index="index" collection="list" separator=",">
(#{item.name}, #{item.algorithm}, #{item.powerDissipation},#{item.hashrate},#{item.monthIncome})
</foreach>
ON DUPLICATE KEY UPDATE
name = VALUES(`name`),
`algorithm` = VALUES(`algorithm`),
power_dissipation = VALUES(power_dissipation),
hashrate = VALUES(hashrate),
month_income = VALUES(month_income)
</insert>
</mapper>

View File

@@ -66,5 +66,13 @@
GROUP BY
lpc.coin, lpc.algorithm
</select>
<select id="getTheoryHashRate" resultType="com.m2pool.lease.dto.v2.MachineHashRateMapDto">
SELECT machine_id as machineId,hashrate as hashRate FROM lease_machine_config
-- TODO 这里最好加个算法,加算法后一一对应
WHERE del = 0 AND (machine_id,coin) IN
<foreach collection="list" item="item" open="(" separator="," close=")">
(#{item.machineId},#{item.coin})
</foreach>
</select>
</mapper>

View File

@@ -17,19 +17,40 @@
id, name, coin, algorithm, icon, del
</sql>
<select id="selectSupportAlgorithm" resultType="com.m2pool.lease.dto.v2.CoinAndAlgorithmDto">
SELECT DISTINCT
SELECT
sub.coin,
sub.`algorithm`,
sub.isEffect,
lgc.`name` AS model,
lgc.hashrate AS hashRate,
lgc.power_dissipation AS powerDissipation,
lgc.month_income as monthIncome,
IF(lgc.id,FALSE,TRUE) AS isAdd
FROM
(
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>
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>
) sub
LEFT JOIN(
SELECT gc.`name`,gc.hashrate,gc.power_dissipation,gc.algorithm,gc.id,gc.month_income FROM
lease_gpu_config gc
WHERE gc.`name` IN
<foreach item="item" index="index" collection="modelList" separator="," open="(" close=")">
#{item}
</foreach>
) lgc ON sub.`algorithm` = lgc.algorithm
</select>
</mapper>

View File

@@ -68,22 +68,21 @@
<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
end_time
FROM lease_order_mining
WHERE user_id = #{userEmail}
ORDER BY status DESC,end_time DESC
GROUP BY wallet_address,pool_user,pool,pool_url,coin,`algorithm`
ORDER BY `status` DESC,end_time DESC
</select>
<select id="getPurchasedInfo" resultType="com.m2pool.lease.dto.v2.PurchasedMachineDto">
<select id="getPurchasedInfo" resultType="com.m2pool.lease.dto.v2.MiningConfigInfoDto">
select
id ,
coin,
@@ -91,12 +90,6 @@
pool,
pool_url as poolUrl,
pool_user as poolUser,
-- worker_id as workerId,
-- status,
-- start_time as startTime,
-- end_time as endTime,
wallet_address as walletAddress,
watch_url as watchUrl
FROM lease_order_mining
@@ -104,12 +97,25 @@
</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
t.datetime AS recordTime,
t.hashrate AS power,
t.miner
FROM (
SELECT
datetime,
hashrate,
miner,
ROW_NUMBER() OVER(PARTITION BY miner ORDER BY datetime DESC) as rn
FROM `${pool}`
WHERE wallet = #{wallet}
AND coin = #{coin}
AND miner IN (
<foreach collection="list" item="item" separator=",">
#{item.workerId}
</foreach>
)
) t
WHERE t.rn = 1
</select>
<select id="getRecently24HourHashrate" resultType="com.m2pool.lease.dto.v2.RealHashrateInfoDto">
SELECT
@@ -160,6 +166,22 @@
</foreach>
)
</select>
<select id="getPurchasedMachineInfo" resultType="com.m2pool.lease.dto.v2.PurchasedMachineDto">
select
id ,
worker_id as workerId,
status,
start_time as startTime,
end_time as endTime
FROM lease_order_mining
WHERE
wallet_address = #{info.walletAddress} AND
pool_user = #{info.poolUser} AND
pool = #{info.pool} AND
pool_url = #{info.poolUrl} AND
coin = #{info.coin} AND
`algorithm` = #{info.algorithm}
</select>
</mapper>

View File

@@ -295,7 +295,7 @@ WHERE
SELECT
id
FROM
lease_product_machine
lease_machine
WHERE
shop_id = #{shopId}
and `del` = 0