update 租赁系统订单相关接口,tcp客户端相关业务

This commit is contained in:
yyb
2025-12-05 17:46:29 +08:00
parent 7cd19a2338
commit cdc0cc8212
123 changed files with 6180 additions and 198 deletions

View File

@@ -3,19 +3,36 @@ package com.m2pool.lease;
import com.m2pool.common.security.annotation.EnableCustomConfig;
import com.m2pool.common.security.annotation.EnableM2PoolFeignClients;
import com.m2pool.common.swagger.annotation.EnableCustomSwagger2;
import com.m2pool.lease.netty.server.NettyTcpServer;
import io.netty.channel.ChannelFuture;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import javax.annotation.Resource;
@EnableCustomConfig
@EnableCustomSwagger2
@EnableM2PoolFeignClients
@SpringBootApplication
@MapperScan({"com.m2pool.lease.mapper"})
public class M2poolLeaseApplication {
public class M2poolLeaseApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(M2poolLeaseApplication.class, args);
}
@Resource
NettyTcpServer nettyTcpServer;
@Override
public void run(String... args) {
//启动服务端
ChannelFuture start = nettyTcpServer.start();
//服务端管道关闭的监听器并同步阻塞,直到channel关闭,线程才会往下执行,结束进程
start.channel().closeFuture().syncUninterruptibly();
}
}

View File

@@ -0,0 +1,80 @@
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.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.v2.CoinAndAlgorithmVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
/**
* <p>
* 订单表 前端控制器
* </p>
*
* @author yyb
* @since 2025-07-23
*/
@Api(tags = "v2版本--订单控制器")
@RestController
@RequestMapping("/v2/order/info")
public class LeaseOrderInfoV2Controller {
@Resource
private LeaseOrderInfoService leaseOrderInfoService;
@ApiOperation("算法币种选择框:购物车结算获取选中矿机支持的算法和币种列表")
@PostMapping("/getMachineSupportCoinAndAlgorithm")
public Result<List<CoinAndAlgorithmListDto>> getMachineSupportCoinAndAlgorithm(@RequestBody List<BaseVo> machineIds) {
return leaseOrderInfoService.getMachineSupportCoinAndAlgorithm(machineIds);
}
@ApiOperation("矿池选择框:根据矿机id 获取当前矿机支持的矿池币种信息")
@PostMapping("/getMachineSupportPool")
public Result<List<MiningInfoDto>> getMachineSupportPool(@RequestBody CoinAndAlgorithmVo coinAndAlgorithmVo) {
return leaseOrderInfoService.getMachineSupportPool(coinAndAlgorithmVo);
}
@ApiOperation("创建订单及订单详情 + 支付订单(返回二维码内容)")
@PostMapping("/addOrdersV2")
public Result<String> addOrdersV2(@RequestBody OrderAndCodeVo orderAndCodeVo) {
return leaseOrderInfoService.addOrdersV2(orderAndCodeVo);
}
@ApiOperation("用户查询当前自身已购商品列表")
@PostMapping("/getOwnedList")
public PageResult<UserOwnedProductDto> getOwnedList(@RequestBody(required = false) UserOwnedProductVo userOwnedProductVo) {
if (userOwnedProductVo == null){
userOwnedProductVo = new UserOwnedProductVo();
}
//return leaseOrderInfoService.getOwnedList(userOwnedProductVo);
return null;
}
@ApiOperation("根据id查询当前自身已购商品详情")
@PostMapping("/getOwnedById")
public Result<UserOwnedProductDto> getOwnedById(@RequestBody BaseVo baseVo) {
//return leaseOrderInfoService.getOwnedById(baseVo);
return null;
}
}

View File

@@ -104,18 +104,21 @@ public class LeaseProductController {
@ApiOperation("查询用户店铺支持的支付方式")
@PostMapping("/getSupportPayType")
@Deprecated
public Result<List<PayTypeDto>> getSupportPayType() {
return leaseProductService.getSupportPayType();
}
//TODO 2025-11-21 查询店铺的矿机列表,不存在商品了
@ApiOperation("获取店铺商品列表用于新增绑定店铺钱包")
@PostMapping("/getProductListForShopWalletConfig")
public Result<List<ProductForWalletConfigDto>> getProductListForShopWalletConfig() {
return leaseProductService.getProductListForShopWalletConfig();
}
//TODO 2025-11-21 修改这些矿机的价格,并且绑定新钱包
@ApiOperation("新增绑定店铺钱包并设置店铺下面每个矿机该钱包币种的售价 + 钱包绑定")
@PostMapping("/updateProductListForShopWalletConfig")
public Result<String> updateProductListForShopWalletConfig(@RequestBody ProductMachineForWalletConfigVo productMachineForWalletConfigVo) {

View File

@@ -35,6 +35,7 @@ public class LeaseProductMachineController {
@Resource
private LeaseProductMachineService leaseProductMachineService;
@ApiOperation("根据 登录账户 获取挖矿账户及挖矿币种集合----用于用户为商品添加实际出售机器库存")
@PostMapping("/getUserMinersList")
public Result<Map<String, List<UserMinerDto>>> getUserMinersList(@RequestBody UserMinerVo userMinerVo) {

View File

@@ -0,0 +1,96 @@
package com.m2pool.lease.controller;
import com.m2pool.lease.dto.*;
import com.m2pool.lease.dto.v2.MachineInfoDto;
import com.m2pool.lease.dto.v2.SellerMachineInfoDto;
import com.m2pool.lease.dto.v2.ShopInfoDto;
import com.m2pool.lease.service.LeaseMachineService;
import com.m2pool.lease.vo.*;
import com.m2pool.lease.vo.v2.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* <p>
* 商品表对应的物品机器表 前端控制器
* </p>
*
* @author yyb
* @since 2025-07-23
*/
@Api(tags = "v2版本--矿机控制器类")
@RestController
@RequestMapping("/v2/product/machine")
public class LeaseProductMachineV2Controller {
@Resource
private LeaseMachineService leaseMachineService;
@ApiOperation("商城首页---店铺列表")
@PostMapping("/getShopList")
public PageResult<ShopInfoDto> getShopList(@RequestBody(required = false) ProductPageVo productPageVo) {
if(productPageVo == null){
productPageVo = new ProductPageVo();
}
return leaseMachineService.getShopList(productPageVo);
}
@ApiOperation("商城首页---店铺对应矿机详情列表")
@PostMapping("/getShopMachineList")
public PageResult<MachineInfoDto> getShopMachineList(@RequestBody ShopMachineVo shopMachineVo) {
return leaseMachineService.getShopMachineList(shopMachineVo);
}
@ApiOperation("卖家页面---矿机列表")
@PostMapping("/getShopMachineListForSeller")
public PageResult<SellerMachineInfoDto> getShopMachineListForSeller(@RequestBody(required = false) SellerMachineVo sellerMachineVo) {
if (sellerMachineVo == null) {
sellerMachineVo = new SellerMachineVo();
}
return leaseMachineService.getShopMachineListForSeller(sellerMachineVo);
}
@ApiOperation("卖家页面---新增ASIC矿机")
@PostMapping("/addAsicMachine")
public Result<String> addAsicMachine(@RequestBody AsicMachineParamsVo asicMachineParamsVo) {
return leaseMachineService.addAsicMachine(asicMachineParamsVo);
}
@ApiOperation("卖家页面---修改ASIC矿机")
@PostMapping("/updateAsicMachine")
public Result<String> updateAsicMachine(@RequestBody AsicMachineParamsVo asicMachineParamsVo) {
return leaseMachineService.updateAsicMachine(asicMachineParamsVo);
}
@ApiOperation("卖家页面--- 批量修改GPU矿机 和 批量上下架")
@PostMapping("/updateGpuMachine")
public Result<String> updateGpuMachine(@RequestBody List<GpuMachineParamsVo> gpuMachineParamsVoList) {
return leaseMachineService.updateGpuMachine(gpuMachineParamsVoList);
}
@ApiOperation("卖家页面---GPU/ASIC矿机上下架")
@PostMapping("/updateMachineState")
public Result<String> updateMachineState(@RequestBody MachineStateVo machineStateVo) {
return leaseMachineService.updateMachineState(machineStateVo);
}
@ApiOperation("卖家页面---根据矿机id删除GPU/ASIC矿机")
@PostMapping("/deleteMachine")
public Result<String> deleteMachine(@RequestBody BaseVo baseVo) {
return leaseMachineService.deleteMachine(baseVo);
}
}

View File

@@ -0,0 +1,69 @@
package com.m2pool.lease.controller;
import com.m2pool.lease.dto.*;
import com.m2pool.lease.dto.v2.PayWithdrawSellerRecordDto;
import com.m2pool.lease.dto.v2.ShopWalletInfoDto;
import com.m2pool.lease.service.LeaseShopService;
import com.m2pool.lease.vo.BalancePageVo;
import com.m2pool.lease.vo.BalanceVo;
import com.m2pool.lease.vo.BaseVo;
import com.m2pool.lease.vo.PageVo;
import com.m2pool.lease.vo.v2.ShopWalletInfoVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* <p>
* 店铺表 前端控制器
* </p>
*
* @author yyb
* @since 2025-08-05
*/
@Api(tags = "v2版本--店铺控制器")
@RestController
@RequestMapping("/v2/shop")
public class LeaseShopV2Controller {
@Resource
private LeaseShopService leaseShopService;
/**
* 根据店铺id 查询配置信息列表
* @param baseVo
* @return 操作结果
*/
@ApiOperation("钱包配置----根据店铺id查询收款钱包绑定信息列表")
@PostMapping("/getShopConfigV2")
public Result<List<ShopWalletInfoDto>> getShopConfigV2(@RequestBody BaseVo baseVo) {
return leaseShopService.getShopConfigV2(baseVo);
}
@PostMapping("/withdrawBalanceForSeller")
@ApiOperation(value = "卖家店铺钱包余额提现")
public Result<String> withdrawBalanceForSeller(@RequestBody BalanceVo balanceVo){
return leaseShopService.withdrawBalanceForSeller(balanceVo);
}
@ApiOperation("钱包配置----根据配置id 修改商铺收款钱包配置")
@PostMapping("/updateShopConfigV2")
public Result<String> updateShopConfigV2(@RequestBody ShopWalletInfoVo shopWalletInfoVo) {
return leaseShopService.updateShopConfigV2(shopWalletInfoVo);
}
@PostMapping("/balanceWithdrawList")
@ApiOperation(value = "卖家:余额提现记录列表")
public PageResult<PayWithdrawSellerRecordDto> balanceWithdrawList(@RequestBody PageVo pageVo){
return leaseShopService.balanceWithdrawList(pageVo);
}
}

View File

@@ -51,6 +51,12 @@ public class LeaseShoppingCartController {
return leaseShoppingCartService.getGoodsList(pageVo);
}
@ApiOperation("批量删除购物车中商品")
@PostMapping("/deleteBatchGoods")
public Result<String> deleteBatchGoods(@RequestBody List<ProductAndMachineVo> baseVoList) {
return leaseShoppingCartService.deleteBatchGoods(baseVoList);
}
@ApiOperation("删除购物车中商品")
@PostMapping("/deleteGoods")
@Deprecated
@@ -58,11 +64,6 @@ public class LeaseShoppingCartController {
return leaseShoppingCartService.deleteGoods(baseVo);
}
@ApiOperation("批量删除购物车中商品")
@PostMapping("/deleteBatchGoods")
public Result<String> deleteBatchGoods(@RequestBody List<ProductAndMachineVo> baseVoList) {
return leaseShoppingCartService.deleteBatchGoods(baseVoList);
}
@ApiOperation("批量删除购物车中已下架商品")
@PostMapping("/deleteBatchGoodsForIsDelete")

View File

@@ -0,0 +1,73 @@
package com.m2pool.lease.controller;
import com.m2pool.lease.dto.PageResult;
import com.m2pool.lease.dto.Result;
import com.m2pool.lease.dto.ShopCartDto;
import com.m2pool.lease.dto.v2.ShopCartV2Dto;
import com.m2pool.lease.service.LeaseShoppingCartService;
import com.m2pool.lease.vo.BaseVo;
import com.m2pool.lease.vo.PageVo;
import com.m2pool.lease.vo.ProductAndMachineVo;
import com.m2pool.lease.vo.ShoppingCartInfoURDVo;
import com.m2pool.lease.vo.v2.AddGoodsVo;
import com.m2pool.lease.vo.v2.CartInfoVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
/**
* <p>
* 购物车表 前端控制器
* </p>
*
* @author yyb
* @since 2025-07-23
*/
@Api(tags = "v2版本---购物车表控制器")
@RestController
@RequestMapping("/v2/shopping/cart")
public class LeaseShoppingCartV2Controller {
@Resource
private LeaseShoppingCartService leaseShoppingCartService;
@ApiOperation("添加商品到购物车(批量+单个)")
@PostMapping("/addGoodsV2")
public Result<String> addGoodsV2(@RequestBody List<AddGoodsVo> addGoodsVoList) {
return leaseShoppingCartService.addGoodsV2(addGoodsVoList);
}
@ApiOperation("查询购物车中商品列表")
@PostMapping("/getGoodsListV2")
public PageResult<ShopCartV2Dto> getGoodsListV2(@RequestBody(required = false) PageVo pageVo) {
if (pageVo == null){
pageVo = new PageVo();
}
return leaseShoppingCartService.getGoodsListV2(pageVo);
}
@ApiOperation("批量删除购物车中商品")
@PostMapping("/deleteBatchGoodsV2")
public Result<String> deleteBatchGoodsV2(@RequestBody List<CartInfoVo> baseVoList) {
return leaseShoppingCartService.deleteBatchGoodsV2(baseVoList);
}
@ApiOperation("批量删除购物车中已下架商品")
@PostMapping("/deleteBatchGoodsForIsDeleteV2")
public Result<String> deleteBatchGoodsForIsDeleteV2() {
return leaseShoppingCartService.deleteBatchGoodsForIsDeleteV2();
}
}

View File

@@ -7,12 +7,11 @@ import com.m2pool.lease.service.LeaseUserService;
import com.m2pool.lease.vo.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
@@ -98,5 +97,18 @@ public class LeaseUserController {
public Result<List<ChargeDto>> getCharge(){
return leaseUserService.getCharge();
}
@GetMapping("/downloadClient")
@ApiOperation(value = "卖家:下载客户端")
public void downloadClient(@RequestParam String userEmail,HttpServletRequest request, HttpServletResponse response){
leaseUserService.downloadClient(userEmail,request, response);
}
@GetMapping("/getClientVersion")
@ApiOperation(value = "获取客户端版本号")
public String getClientVersion(){
return leaseUserService.getClientVersion();
}
}

View File

@@ -71,6 +71,10 @@ public class OrderInfoDto {
private LocalDateTime createTime;
@ApiModelProperty(value = "订单完成时间")
private LocalDateTime endTime;
// ---------------------------------------------- 一个普通订单对应一个支付订单多个封装下面四个字段为一个对象并返回list----------------------------------------------------------------------

View File

@@ -81,5 +81,9 @@ public class OrderItemDto {
private String payCoin;
@ApiModelProperty(value = "租赁矿机数量")
private Integer numbers;
}

View File

@@ -8,6 +8,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
@@ -49,11 +50,18 @@ public class PageResult<T> implements Serializable
@ApiModelProperty(value = "查询结果描述", example = "成功")
private String msg;
@ApiModelProperty(value = "表头信息")
private List<TableHeadersDto> columns;
public static <T> PageResult<T> success(List<T> data)
{
return setPageResult(data, SUCCESS, "成功");
}
public static <T> PageResult<T> successColumns(List<T> data) {
return setPageResultAndHeader(data, SUCCESS, "成功");
}
public static <T> PageResult<T> fail(List<T> data, String msg)
{
return setPageResult(data, FAIL, msg);
@@ -67,4 +75,43 @@ public class PageResult<T> implements Serializable
rspData.setMsg("查询成功");
return rspData;
}
private static <T> PageResult<T> setPageResultAndHeader(List<T> data, int code, String msg) {
List<TableHeadersDto> columns = new ArrayList<>();
columns.add(TableHeadersDto.builder()
.key("model")
.label("型号")
.type("text")
.fixed("left")
.width(100)
.build());
columns.add(TableHeadersDto.builder()
.key("price")
.label("价格")
.type("amount")
.width(100)
.build());
columns.add(TableHeadersDto.builder()
.key("monthIncome")
.label("最大月收益")
.type("amount")
.currency("USDT")
.period("MONTH")
.width(110)
.build());
columns.add(TableHeadersDto.builder()
.key("maxLeaseDays")
.label("最大租赁天数")
.type("days")
.width(80)
.build());
PageResult<T> rspData = new PageResult<T>();
rspData.setColumns(columns);
rspData.setCode(SUCCESS);
rspData.setRows(data);
rspData.setMsg("查询成功");
return rspData;
}
}

View File

@@ -29,65 +29,44 @@ public class ProductMachineDto {
@ApiModelProperty(value = "矿机ID")
private Long id;
@ApiModelProperty(value = "购物车详情id")
private Long cartInfoId;
@ApiModelProperty(value = "店铺id")
private Long shopId;
/**
* 商品 ID
*/
@ApiModelProperty(value = "商品 ID")
private Long productId;
/**
* 挖矿机器 对应的矿工账号
*/
@ApiModelProperty(value = "挖矿机器 对应的矿工账号")
private String user;
/**
* 挖矿机器型号
*/
@ApiModelProperty(value = "挖矿机器型号")
private String type;
/**
* 挖矿机器编号
*/
@ApiModelProperty(value = "挖矿机器编号")
private String miner;
/**
* 单价
*/
@ApiModelProperty(value = "单价")
private BigDecimal price;
/**
* 实际算力(计算得到,商家不能够自己添加和修改)
*/
@ApiModelProperty(value = "3天算力平均大小---实际实时算力(计算得到,商家不能够自己添加和修改)")
private BigDecimal computingPower;
/**
* 理论算力
*/
@ApiModelProperty(value = "理论算力(卖方手动填写)")
private BigDecimal theoryPower;
@ApiModelProperty(value = "算力单位")
private String unit;
/**
* 上下架状态0 上架1 下架
*/
@ApiModelProperty(value = "上下架状态0 上架1 下架")
private Integer state;
/**
* 售出状态 0未售出 1已售出 2售出中
*/
@ApiModelProperty(value = "售出状态 0未售出 1已售出 2售出中")
private Integer saleState;
@@ -97,9 +76,7 @@ public class ProductMachineDto {
@ApiModelProperty(value = "机器算力最近记录的一次时间")
private LocalDateTime endTime;
/**
* 单机理论收入
*/
@ApiModelProperty(value = "单机理论收入(每日) 单位币种")
private BigDecimal theoryIncome;

View File

@@ -8,6 +8,8 @@ import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
* <p>
* 店铺表
@@ -54,4 +56,8 @@ public class ShopDto {
*/
@ApiModelProperty(value = "逻辑删除字段")
private Boolean del;
@ApiModelProperty(value = "店铺手续费比例 范围0.01-0.1",example = "0.01")
private BigDecimal feeRate;
}

View File

@@ -0,0 +1,66 @@
package com.m2pool.lease.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Description 表头信息
* @Date 2025/11/28 13:31
* @Author yyb
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "表头信息",value = "TableHeadersDto" )
public class TableHeadersDto {
/**
* 动态列第几列
*/
@ApiModelProperty(value = "动态列第几列")
private String key;
/**
* 列名
*/
@ApiModelProperty(value = "列名")
private String label;
/**
* 类型 text/amount/hashrate/days
*/
@ApiModelProperty(value = "类型 text/amount/hashrate/days")
private String type;
/**
* 固定列 left/right/不传
*/
@ApiModelProperty(value = "固定列 left/right/不传")
private String fixed;
@ApiModelProperty(value = "价格单位")
private String currency;
@ApiModelProperty(value = "时间间隔")
private String period;
/**
* 算力单位
*/
@ApiModelProperty(value = "算力单位")
private String unit;
@ApiModelProperty(value = "图标")
private String icon;
/**
* 宽度
*/
@ApiModelProperty(value = "宽度")
private Integer width;
}

View File

@@ -0,0 +1,30 @@
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;
/**
* <p>
* 商品列表分页请求对象
* </p>
*
* @author yyb
* @since 2025-07-23
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "商品对应算法",value = "AlgorithmShopIdMapDto" )
public class AlgorithmShopIdMapDto {
@ApiModelProperty(value = "店铺id",example = "1")
private Long shopId;
@ApiModelProperty(value = "算法")
private String algorithm;
}

View File

@@ -0,0 +1,44 @@
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.math.BigDecimal;
/**
* <p>
* 机器参数请求对象
* </p>
*
* @author yyb
* @since 2025-07-23
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "asic 矿机新增 算力,币种返回对象",value = "AsicCoinAndAlgoDto" )
public class AsicCoinAndAlgoDto {
@ApiModelProperty(value = "币种算力配置id")
private Long coinAndPowerId;
@ApiModelProperty(value = "矿机id忽略")
private Long productMachineId;
@ApiModelProperty(value = "币种(多个逗号隔开)")
private String coin;
@ApiModelProperty(value = "算法(多个逗号隔开)")
private String algorithm;
@ApiModelProperty(value = "理论算力)",example = "0.01")
private BigDecimal theoryPower;
@ApiModelProperty(value = "商品机器单机算力单位",example = "TH/S")
private String unit;
}

View File

@@ -0,0 +1,108 @@
package com.m2pool.lease.dto.v2;
import com.m2pool.lease.dto.MachinePayTypeDto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.util.List;
/**
* <p>
* 商品对应实际商品返回对象
* </p>
*
* @author yyb
* @since 2025-07-23
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "购物车列表返回对象",value = "CartMachineInfoDto" )
public class CartMachineInfoDto{
@ApiModelProperty(value = "矿机ID")
private Long id;
@ApiModelProperty(value = "购物车详情id")
private Long cartInfoId;
@ApiModelProperty(value = "店铺id")
private Long shopId;
@ApiModelProperty(value = " gpu名称型号 或asic 名称型号")
private String name;
@ApiModelProperty(value = "矿工账号 (gpu)")
private String user;
@ApiModelProperty(value = "矿机型号 0 ASIC 1 GPU")
private Integer type;
@ApiModelProperty(value = "矿机编号(gpu)")
private String miner;
@ApiModelProperty(value = "上下架状态0 上架1 下架")
private Integer state;
@ApiModelProperty(value = "售出状态 0未售出 1已售出")
private Integer saleState;
@ApiModelProperty(value = "最大可租借天数(默认七天)",example = "7")
private Integer maxLeaseDays;
@ApiModelProperty(value = "是否删除 0否 1是")
private Integer del;
@ApiModelProperty(value = "最大月收益 usdt")
private BigDecimal monthIncome;
@ApiModelProperty(value = "最大收益币种功耗 单位kw/h",example = "10")
private BigDecimal powerDissipation;
@ApiModelProperty(value = "理论算力(卖方手动填写)")
private BigDecimal theoryPower;
@ApiModelProperty(value = "最大收益币种算力单位")
private String unit;
@ApiModelProperty(value = "最大收益币种算法")
private String algorithm;
@ApiModelProperty(value = "售价")
private BigDecimal price;
@ApiModelProperty(value = "最大收益对应币种")
private String coin;
@ApiModelProperty(value = "租赁天数")
private Integer leaseTime;
private Long configId;
@ApiModelProperty(value = "支付币种")
private String payCoin;
@ApiModelProperty(value = "asic 租赁数量")
private Integer numbers;
@ApiModelProperty(value = "矿机客户端在离线情况 0 离线 1 在线")
private Boolean onlineStatus;
@ApiModelProperty(value = "可售数量")
private Integer canSaleNumbers;
@ApiModelProperty(value = "gpu和asic对应币种算力和月收益信息")
private List<PowerIncomeInfoDto> powerIncomeInfoList;
@ApiModelProperty(value = "店铺售价集合")
private List<MachinePayTypeDto> priceList;
}

View File

@@ -0,0 +1,35 @@
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;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class CoinAndAlgorithmDto {
/**
* 币种名称
*/
private String coin;
/**
* 算法名称
*/
private String algorithm;
/**
* 矿机id,多个已逗号隔开
*/
private String machineIds;
}

View File

@@ -0,0 +1,39 @@
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.util.List;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(description = "币种算法返回对象",value = "CoinAndAlgorithmListDto")
public class CoinAndAlgorithmListDto {
@ApiModelProperty(value = "币种")
private String coin;
@ApiModelProperty(value = "币种对应算法返回对象")
private List<AlgorithmListDto> algorithmList;
@Data
@ApiModel(description = "币种对应算法返回对象",value = "AlgorithmListDto")
public static class AlgorithmListDto{
@ApiModelProperty(value = "算法")
private String algorithm;
@ApiModelProperty(value = "支持该算法的矿机id集合")
private List<Long> machineIds;
}
}

View File

@@ -0,0 +1,39 @@
package com.m2pool.lease.dto.v2;
import com.m2pool.lease.dto.MachinePayTypeDto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.util.List;
/**
* <p>
* 商品对应实际商品返回对象
* </p>
*
* @author yyb
* @since 2025-07-23
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "gpu公共配置返回对象",value = "GpuConfigDto" )
public class GpuConfigDto {
@ApiModelProperty(value = "gpu名称/型号")
private String name;
@ApiModelProperty(value = "gpu品牌")
private String brand;
private Integer memory;
}

View File

@@ -0,0 +1,101 @@
package com.m2pool.lease.dto.v2;
import com.m2pool.lease.dto.MachinePayTypeDto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;
import java.math.BigDecimal;
import java.util.List;
/**
* <p>
* 商品对应实际商品返回对象
* </p>
*
* @author yyb
* @since 2025-07-23
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "矿机返回对象",value = "MachineInfoDto" )
public class MachineInfoDto {
private Integer type;
private BigDecimal theoryPower;
@ApiModelProperty(value = "矿机ID")
private Long id;
@ApiModelProperty(value = "gpu名称 或asic 名称")
private String model;
@ApiModelProperty(value = "矿机mac地址")
private String hostMac;
@ApiModelProperty(value = "上下架状态0 上架1 下架")
private Integer state;
@ApiModelProperty(value = "售出状态 0未售出 1已售出")
private Integer saleState;
@ApiModelProperty(value = "最大可租借天数(默认七天)",example = "7")
private Integer maxLeaseDays;
@ApiModelProperty(value = "最大月收益 usdt")
private BigDecimal monthIncome;
@ApiModelProperty(value = "最大收益币种算法")
private String algorithm;
@ApiModelProperty(value = "售价")
private BigDecimal price;
@ApiModelProperty(value = "最大收益对应币种")
private String coin;
@ApiModelProperty(value = "矿机客户端在离线情况 0 离线 1 在线")
private Boolean onlineStatus;
@ApiModelProperty(value = "可出售机器数量")
private Integer saleNumbers;
@ApiModelProperty(value = "已售出数量")
private Integer saleOutNumbers;
//动态列名(方便前端显示)
private BigDecimal c1;
private BigDecimal c2;
private BigDecimal c3;
private BigDecimal c4;
private BigDecimal c5;
private BigDecimal c6;
private BigDecimal c7;
private BigDecimal c8;
private BigDecimal c9;
private BigDecimal c10;
//@ApiModelProperty(value = "币种动态列集合")
//private List<PowerIncomeInfoDto> powerIncomeInfoList;
@ApiModelProperty(value = "店铺售价集合")
private List<MachinePayTypeDto> priceList;
}

View File

@@ -0,0 +1,14 @@
package com.m2pool.lease.dto.v2;
import java.math.BigDecimal;
import java.util.List;
public interface MachineInfoInterface {
Integer getType();
Long getId();
String getCoin();
String getAlgorithm();
BigDecimal getTheoryPower();
BigDecimal getMonthIncome();
void setPowerIncomeInfoList(List<PowerIncomeInfoDto> powerIncomeInfoList);
}

View File

@@ -0,0 +1,43 @@
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.math.BigDecimal;
/**
* @Description 矿机对应的矿池配置信息
* @Date 2025/11/26 14:19
* @Author yyb
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "矿池对应币种返回对象",value = "MiningCoinInfoDto" )
public class MiningCoinInfoDto {
@ApiModelProperty(value = "矿池配置id")
private Long coinConfigId;
@ApiModelProperty(value = "挖矿币种")
private String coin;
@ApiModelProperty(value = "挖矿算法")
private String algorithm;
@ApiModelProperty(value = "挖矿地址 TCP GPU")
private String miningTcpGpuUrl;
@ApiModelProperty(value = "分配模式名")
private String modelName;
@ApiModelProperty(value = "分配模式手续费")
private BigDecimal modelFee;
@ApiModelProperty(value = "是否支持钱包支付0 不支持 1 支持")
private Boolean walletMining;
}

View File

@@ -0,0 +1,46 @@
package com.m2pool.lease.dto.v2;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Description 矿机对应的矿池配置信息
* @Date 2025/11/26 14:19
* @Author yyb
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MiningConfigDto {
@ApiModelProperty(value = "挖矿币种")
private String coin;
@ApiModelProperty(value = "挖矿算法")
private String algo;
@ApiModelProperty(value = "矿池名称")
private String pool;
@ApiModelProperty(value = "是否支持钱包挖矿 0 不支持 1支持")
private Boolean wallet_mining;
@ApiModelProperty(value = "挖矿地址")
private String pool_url;
@ApiModelProperty(value = "收款钱包")
private String wallet_address;
@ApiModelProperty(value = "挖矿账户")
private String pool_user;
@ApiModelProperty(value = "矿工号")
private String worker_id;
@ApiModelProperty(value = "挖矿结束时间")
private Long end_timestamp;
}

View File

@@ -0,0 +1,49 @@
package com.m2pool.lease.dto.v2;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
* @Description 矿机对应的矿池配置信息
* @Date 2025/11/26 14:19
* @Author yyb
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MiningConfigSelectDto {
@ApiModelProperty(value = "矿池配置id")
private Long coinConfigId;
@ApiModelProperty(value = "矿池id")
private Long poolId;
@ApiModelProperty(value = "挖矿币种")
private String coin;
@ApiModelProperty(value = "挖矿算法")
private String algorithm;
@ApiModelProperty(value = "矿池名称")
private String poolName;
@ApiModelProperty(value = "是否支持钱包支付0 不支持 1 支持")
private Boolean walletMining;
@ApiModelProperty(value = "挖矿地址 TCP GPU")
private String miningTcpGpuUrl;
@ApiModelProperty(value = "分配模式名")
private String modelName;
@ApiModelProperty(value = "分配模式手续费")
private BigDecimal modelFee;
}

View File

@@ -0,0 +1,31 @@
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.util.List;
/**
* @Description 矿机对应的矿池配置信息
* @Date 2025/11/26 14:19
* @Author yyb
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "矿池列表返回对象",value = "MiningInfoDto" )
public class MiningInfoDto {
@ApiModelProperty(value = "矿池id")
private Long poolId;
@ApiModelProperty(value = "矿池名称")
private String poolName;
@ApiModelProperty(value = "矿池支持对应币种信息")
private List<MiningCoinInfoDto> miningCoinInfoList;
}

View File

@@ -0,0 +1,43 @@
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.util.List;
/**
* @Description 矿机对应的矿池配置信息
* @Date 2025/11/26 14:19
* @Author yyb
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "矿池列表返回对象",value = "OrderMiningInfoDto" )
public class OrderMiningInfoDto {
@ApiModelProperty(value = "矿池名称")
private String poolName;
@ApiModelProperty(value = "矿池配置id")
private Long coinConfigId;
@ApiModelProperty(value = "是否钱包挖矿 0 不支持 1 支持")
private Boolean walletMining;
@ApiModelProperty(value = "矿机id列表")
private List<Long> machineIds;
@ApiModelProperty(value = "挖矿钱包(用户自己输入)")
private String walletAddress;
@ApiModelProperty(value = "挖矿账号(用户自己输入)")
private String poolUser;
@ApiModelProperty(value = "矿工号(用户自己输入)")
private String workerId;
}

View File

@@ -0,0 +1,77 @@
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.math.BigDecimal;
import java.time.LocalDateTime;
/**
* <p>
* 余额提现返回对象
* </p>
*
* @author yyb
* @since 2025-09-10
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "店铺钱包提现记录返回信息",value = "PayWithdrawSellerRecordDto" )
public class PayWithdrawSellerRecordDto {
private Long id;
@ApiModelProperty(value = "用户充值地址")
private String fromAddress;
@ApiModelProperty(value = "提现地址(用户自定义)")
private String toAddress;
@ApiModelProperty(value = "提现金额")
private BigDecimal amount;
/**
* 币种
*/
@ApiModelProperty(value = "币种")
private String symbol;
/**
* 链名称
*/
@ApiModelProperty(value = "链名称")
private String chain;
/**
* 充值时间
*/
@ApiModelProperty(value = "提现时间")
private LocalDateTime createTime;
/**
* 更新时间
*/
@ApiModelProperty(value = "提现完成时间")
private LocalDateTime updateTime;
/**
* 0 支付失败 1 支付成功 2 支付中
*/
@ApiModelProperty(value = "记录状态 提现业务: 0 提现失败 1 提现成功 2 提现中 3 校验失败")
private Integer status;
/**
* 交易hash
*/
@ApiModelProperty(value = "交易hash")
private String txHash;
}

View File

@@ -0,0 +1,50 @@
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.math.BigDecimal;
/**
* <p>
* 商品对应实际商品返回对象
* </p>
*
* @author yyb
* @since 2025-07-23
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "gpu配置信息返回对象",value = "PowerIncomeInfoDto" )
public class PowerIncomeInfoDto {
@ApiModelProperty(value = "币种")
private String coin;
//@ApiModelProperty(value = "算法")
//private String algorithm;
@ApiModelProperty(value = "算力")
private BigDecimal power;
@ApiModelProperty(value = "月收益")
private BigDecimal monthIncome;
//@ApiModelProperty(value = "功耗")
//private BigDecimal powerDissipation;
private Long machineId;
@ApiModelProperty(value = "算力单位")
private String unit;
@ApiModelProperty(value = "图标")
private String icon;
}

View File

@@ -0,0 +1,96 @@
package com.m2pool.lease.dto.v2;
import com.m2pool.lease.dto.MachinePayTypeDto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;
import java.math.BigDecimal;
import java.util.List;
/**
* <p>
* 商品对应实际商品返回对象
* </p>
*
* @author yyb
* @since 2025-07-23
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "卖家矿机列表返回对象",value = "SellerMachineInfoDto" )
public class SellerMachineInfoDto {
@ApiModelProperty(value = "矿机ID")
private Long id;
@ApiModelProperty(value = "店铺id")
private Long shopId;
@ApiModelProperty(value = "gpu名称 或asic 名称")
private String name;
@ApiModelProperty(value = "矿机型号 0 ASIC 1 GPU")
private Integer type;
@ApiModelProperty(value = "矿工账号 (gpu)")
private String user;
@ApiModelProperty(value = "矿机编号(gpu)")
private String miner;
@ApiModelProperty(value = "上下架状态0 上架1 下架")
private Integer state;
@ApiModelProperty(value = "售出状态 0未售出 1已售出")
private Integer saleState;
@ApiModelProperty(value = "最大可租借天数(默认七天)",example = "7")
private Integer maxLeaseDays;
@ApiModelProperty(value = "是否删除 0否 1是")
private Integer del;
@ApiModelProperty(value = "最大月收益 usdt")
private BigDecimal monthIncome;
@ApiModelProperty(value = "最大收益币种功耗 单位kw/h",example = "10")
private BigDecimal powerDissipation;
@ApiModelProperty(value = "最大收益币种算力(单机理论算力)")
private BigDecimal theoryPower;
@ApiModelProperty(value = "最大收益币种算力单位")
private String unit;
@ApiModelProperty(value = "最大收益币种算法")
private String algorithm;
@ApiModelProperty(value = "售价")
private BigDecimal price;
@ApiModelProperty(value = "最大收益对应币种")
private String coin;
@ApiModelProperty(value = "可出售机器数量")
private Integer saleNumbers;
@ApiModelProperty(value = "已售出数量")
private Integer saleOutNumbers;
@ApiModelProperty(value = "矿机客户端在离线情况 0 离线 1 在线")
private Boolean onlineStatus;
//@ApiModelProperty(value = "gpu和asic对应币种算力和月收益信息")
//private List<PowerIncomeInfoDto> powerIncomeInfoList;
@ApiModelProperty(value = "币种算法算力信息")
private List<AsicCoinAndAlgoDto> coinAndAlgoList;
@ApiModelProperty(value = "店铺售价集合")
private List<MachinePayTypeDto> priceList;
}

View File

@@ -0,0 +1,48 @@
package com.m2pool.lease.dto.v2;
import com.m2pool.lease.dto.MachineTotalPriceDto;
import com.m2pool.lease.dto.PayConfigDto;
import com.m2pool.lease.dto.ProductMachineDto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.util.List;
/**
* <p>
* 购物车一层列表
* </p>
*
* @author yyb
* @since 2025-08-05
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "购物车一层商铺列表返回对象V2",value = "ShopCartV2Dto")
public class ShopCartV2Dto {
@ApiModelProperty(value = "店铺id")
private Long id;
@ApiModelProperty(value = "店铺名称")
private String name;
@ApiModelProperty(value = "店铺下机器总价")
private BigDecimal totalPrice;
@ApiModelProperty(value = "店铺下机器总数")
private Integer totalMachine;
@ApiModelProperty(value = "总价集合")
private List<MachineTotalPriceDto> totalPriceList;
@ApiModelProperty(value = "商品支持的支付地址")
private List<PayConfigDto> payConfigList;
@ApiModelProperty(value = "商品机器列表")
List<CartMachineInfoDto> cartMachineInfoDtoList;
}

View File

@@ -0,0 +1,58 @@
package com.m2pool.lease.dto.v2;
import com.m2pool.lease.dto.PayTypeDto;
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;
/**
* <p>
* 商品对应实际商品返回对象
* </p>
*
* @author yyb
* @since 2025-07-23
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "商城首页矿机返回对象",value = "ShopInfoDto" )
public class ShopInfoDto {
@ApiModelProperty(value = "店铺id")
private Long shopId;
@ApiModelProperty(value = "店铺名称")
private String shopName;
@ApiModelProperty(value = "店铺图片")
private String image;
@ApiModelProperty(value = "店铺描述")
private String description;
@ApiModelProperty(value = "已售矿机数")
private Integer saleNumber;
@ApiModelProperty(value = "店铺售出矿机包含的类型 多个以逗号隔开",example = "gpu,asci")
private String type;
@ApiModelProperty(value = "支持的挖矿币种",example = "NEXA,RXD")
private String coin;
@ApiModelProperty(value = "算法 (多个算法已逗号隔开)",example = "sha256,sha512")
private String algorithm;
@ApiModelProperty(value = "店铺支持的支付方式")
private List<PayTypeDto> payTypes;
}

View File

@@ -0,0 +1,50 @@
package com.m2pool.lease.dto.v2;
import com.m2pool.lease.vo.BaseVo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.util.List;
/**
* <p>
* 店铺钱包配置返回对象
* </p>
*
* @author yyb
* @since 2025-08-05
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "店铺钱包配置返回对象",value = "ShopWalletInfoDto")
public class ShopWalletInfoDto extends BaseVo {
@ApiModelProperty(value = "商铺ID")
private Long shopId;
@ApiModelProperty(value = "卖方对应收款钱包")
private String payAddress;
@ApiModelProperty(value = "")
private String chain;
@ApiModelProperty(value = "商品支付方式及价格单位 取值 虚拟币( nexa rxd dgbo dgbq dgbs alph enx grs mona) 稳定币usdt usdc busd")
private String payCoin;
@ApiModelProperty(value = "币种图片")
private String image;
@ApiModelProperty(value = "余额")
private BigDecimal balance;
@ApiModelProperty(value = "手续费")
private BigDecimal serviceCharge;
}

View File

@@ -0,0 +1,64 @@
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;
/**
* <p>
* GPU对应币种挖矿算力(只要gpu类型相同那么就使用这个配置)
* </p>
*
* @author yyb
* @since 2025-11-27
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class LeaseGpuConfig implements Serializable {
private static final long serialVersionUID=1L;
/**
* id
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* gpu品牌
*/
private String brand;
/**
* gpu 名称/型号
*/
private String name;
/**
* 0 禁用 1 启用
*/
private Boolean status;
/**
* 内存单位M
*/
private Integer memory;
/**
* 功耗 单位kw/h
*/
private BigDecimal powerDissipation;
/**
* 逻辑删除字段
*/
private Boolean del;
}

View File

@@ -0,0 +1,120 @@
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>
* gpu类型出售矿机表
* </p>
*
* @author yyb
* @since 2025-11-21
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class LeaseMachine implements Serializable {
private static final long serialVersionUID=1L;
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 商铺ID
*/
private Long shopId;
/**
* gpu 挖矿主机mac矿机编号
*/
private String hostMac;
/**
* 0 ASIC 1 GPU
*/
private Boolean type;
/**
* 矿机最大租售天数
*/
private Integer maxLeaseDays;
/**
* 售出状态 0未售出 1已售出
*/
private Boolean saleState;
/**
* 上下架状态0 上架1 下架
*/
private Boolean state;
/**
* 逻辑删除字段
*/
private Boolean del;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
/**
* 矿机客户端在离线情况 0 离线 1 在线
*/
private Boolean onlineStatus;
/**
* 挖矿机器 对应的矿工账号(mac地址代替)
*/
private String user;
/**
* 挖矿机器编号(gpu编号)
*/
private String miner;
/**
* asci 类型矿机租售的台数
*/
private Integer saleNumbers;
/**
* asci 类型矿机已租售的台数
*/
private Integer saleOutNumbers;
/**
* asci 类型矿机可租售的台数
*/
private Integer canSaleNumbers;
/**
* asci 类型矿机已锁定的台数 乐观锁
*/
@TableField(exist = false)
private Integer lockNumbers;
}

View File

@@ -0,0 +1,99 @@
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;
/**
* <p>
* GPU对应币种挖矿算力(只要gpu类型相同那么就使用这个配置)
* </p>
*
* @author yyb
* @since 2025-11-27
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class LeaseMachineConfig 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 名称/型号
*/
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;
}

View File

@@ -0,0 +1,59 @@
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;
/**
* <p>
* 商品表对应的物品机器表
* </p>
*
* @author yyb
* @since 2025-11-25
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class LeaseMachinePrice implements Serializable {
private static final long serialVersionUID=1L;
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 矿机id
*/
private Long machineId;
/**
* 实际单价
*/
private BigDecimal price;
/**
* 价值单位
*/
private String coin;
/**
* 链
*/
private String chain;
/**
* 逻辑删除字段
*/
private Boolean del;
}

View File

@@ -0,0 +1,58 @@
package com.m2pool.lease.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.*;
import java.io.Serializable;
/**
* <p>
* 挖矿软件公共配置表
* </p>
*
* @author yyb
* @since 2025-11-27
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class LeaseMiningSoftwareConfig implements Serializable {
private static final long serialVersionUID=1L;
/**
* id
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 挖矿软件名
*/
private String name;
/**
* 挖矿软件支持的币种
*/
private String coin;
/**
* 算法
*/
private String algorithm;
/**
* 币种图片
*/
private String icon;
/**
* 逻辑删除字段
*/
private Boolean del;
}

View File

@@ -168,6 +168,8 @@ public class LeaseOrderItem implements Serializable {
*/
private BigDecimal settlePayRealAmount;
private Integer numbers;
/**
* 商品类型 0 矿机 1 算力
*/

View File

@@ -0,0 +1,94 @@
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.time.LocalDateTime;
/**
* <p>
* 挖矿中订单
* </p>
*
* @author yyb
* @since 2025-12-02
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class LeaseOrderMining implements Serializable {
private static final long serialVersionUID=1L;
/**
* id
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 订单 ID
*/
private Long orderId;
/**
* 币种
*/
private String coin;
/**
* 算法
*/
private String algorithm;
/**
* 矿池
*/
private String pool;
/**
* 挖矿地址
*/
private String poolUrl;
/**
* 矿工号
*/
private String workerId;
/**
* 收款钱包
*/
private String walletAddress;
/**
* 挖矿信息页面
*/
private String watchUrl;
/**
* 0 租约已到期 1挖矿中 2等待卖家矿机启动中
*/
private Boolean status;
/**
* 挖矿开始时间
*/
private LocalDateTime startTime;
/**
* 挖矿结束时间
*/
private LocalDateTime endTime;
/**
* 逻辑删除字段
*/
private Boolean del;
}

View File

@@ -31,6 +31,11 @@ public class LeasePayWithdrawMessage implements Serializable {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 店铺ID
*/
private Long shopId;
/**
* 消息ID
*/

View File

@@ -52,12 +52,6 @@ public class LeaseProduct implements Serializable {
*/
private Integer type;
/**
* 商品矿池类型 0 自营矿池 1 非自营矿池
*/
private Integer poolType;
/**
* 上下架状态0 上架1 下架
*/

View File

@@ -33,6 +33,11 @@ public class LeaseProductMachinePrice implements Serializable {
private Long productMachineId;
/**
* 0 自营矿池 1 非自营矿池(gpu 和asic)
*/
private Boolean type;
private BigDecimal price;
private String coin;

View File

@@ -2,9 +2,11 @@ package com.m2pool.lease.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
@@ -30,6 +32,11 @@ public class LeaseShop implements Serializable {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 店铺唯一标识码
*/
private String identityCode;
/**
* 店铺拥有者邮箱
*/
@@ -70,5 +77,11 @@ public class LeaseShop implements Serializable {
*/
private Boolean del;
private BigDecimal feeRate;
/**
* 售出矿机数
*/
private Integer saleNumber;
}

View File

@@ -72,6 +72,11 @@ public class LeaseShopConfig implements Serializable {
*/
private String qrcode;
/**
* 余额(这个余额是租赁系统的收款余额,而不是卖家钱包真实余额,该余额只能用于提现)
*/
private BigDecimal balance;
/**
* 创建时间
*/

View File

@@ -30,11 +30,15 @@ public class LeaseShoppingCartInfo implements Serializable {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 用户ID
*/
private String userId;
/**
* 购物车ID
* 商店ID
*/
private Long cartId;
private Long shopId;
/**
* 商品 ID
@@ -51,6 +55,11 @@ public class LeaseShoppingCartInfo implements Serializable {
*/
private Integer leaseTime;
/**
* ASIC 租售台数
*/
private Integer numbers;
/**
* 创建时间
*/
@@ -61,5 +70,10 @@ public class LeaseShoppingCartInfo implements Serializable {
*/
private LocalDateTime updateTime;
/**
* 版本 0 旧版本 1 新版本
*/
private Integer version;
}

View File

@@ -21,6 +21,11 @@ public class GlobalExceptionHandler {
return Result.fail(e.getMessage());
}
@ExceptionHandler(MachineException.class)
public Result<String> handleMachineException(MachineException e) {
return Result.fail(e.getMessage());
}
@ExceptionHandler(PaymentException.class)
public Result<String> handlePaymentException(PaymentException e) {
return Result.fail(e.getMessage());

View File

@@ -0,0 +1,10 @@
package com.m2pool.lease.exception;
/**
* 商品已出售异常类,用于表示在添加订单时商品已被出售的异常情况。
*/
public class MachineException extends RuntimeException {
public MachineException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,27 @@
package com.m2pool.lease.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.m2pool.lease.entity.LeaseGpuConfig;
import com.m2pool.lease.netty.message.GpuMessage;
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-11-27
*/
@Mapper
public interface LeaseGpuConfigMapper extends BaseMapper<LeaseGpuConfig> {
/**
* 批量插入或更新
* @param gpuMessages
* @return
*/
int insertOrUpdate(@Param("list") List<GpuMessage> gpuMessages);
}

View File

@@ -0,0 +1,41 @@
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.entity.LeaseMachineConfig;
import com.m2pool.lease.vo.BaseVo;
import com.m2pool.lease.vo.v2.CoinAndAlgorithmVo;
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-11-27
*/
@Mapper
public interface LeaseMachineConfigMapper extends BaseMapper<LeaseMachineConfig> {
/**
* 获取配置列表
* @param coin
* @param algorithm
* @return
*/
List<MiningConfigSelectDto> getConfigList(@Param("coin") String coin,@Param("algorithm") String algorithm);
/**
* 获取矿机支持的币种和算法
* @return
*/
List<CoinAndAlgorithmDto> getMachineSupportCoinAndAlgorithm(@Param("machineIds") List<BaseVo> machineIds);
}

View File

@@ -0,0 +1,119 @@
package com.m2pool.lease.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.m2pool.lease.dto.MachinePayTypeDto;
import com.m2pool.lease.dto.v2.*;
import com.m2pool.lease.entity.LeaseMachine;
import com.m2pool.lease.vo.v2.SellerMachineVo;
import com.m2pool.lease.vo.v2.ShopMachineVo;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* <p>
* gpu类型出售矿机表 Mapper 接口
* </p>
*
* @author yyb
* @since 2025-11-21
*/
public interface LeaseMachineMapper extends BaseMapper<LeaseMachine> {
/**
* 获取店铺ids
* @return
*/
Set<Long> getShopIds();
/**
* 根据id集合查询店铺信息
* @return
*/
List<ShopInfoDto> getExistsMachineShop(@Param("ids") Set<Long> ids, @Param("coin") String coin , @Param("algorithm") String algorithm);
/**
* 获取店铺对应GPU或ASIC矿机信息
* @param shopMachineVo
* @return
*/
List<MachineInfoDto> getShopMachineList(@Param("shopMachineVo") ShopMachineVo shopMachineVo);
/**
* 根据id集合获取gpu配置信息
* @param ids
* @return
*/
List<PowerIncomeInfoDto> getGpuConfigList(@Param("ids") List<Long> ids);
/**
* 获取店铺Gpu或Asic对应矿机信息
* @param sellerMachineVo
* @return
*/
List<SellerMachineInfoDto> getShopMachineListForSeller(@Param("sellerMachineVo") SellerMachineVo sellerMachineVo, @Param("shopId") Long shopId);
/**
* 根据矿机id获取对应的矿机价格列表
* @param ids
* @return
*/
List<MachinePayTypeDto> getMachinePriceList(@Param("list") List<SellerMachineInfoDto> ids);
/**
* 根据矿机id获取对应的矿机价格列表
* @param machineIds
* @return
*/
List<Long> getShopIdsByMachineIds(@Param("machineIds") List<Long> machineIds);
/**
* 获取购物车对应矿机信息
* @param machineIds
* @return
*/
List<CartMachineInfoDto> getMachinesByIds(@Param("machineIds") List<Long> machineIds);
/**
* 获取购物车对应矿机信息
* @param ids
* @return
*/
List<AsicCoinAndAlgoDto> getCoinAndAlgoList(@Param("list") List<SellerMachineInfoDto> ids);
/**
* 修改gpu矿机售出状态
* @param asicMachines
* @return
*/
int updateLockState(@Param("list") List<LeaseMachine> asicMachines);
/**
* 修改asic 矿机售出数量
* @param gpuMachines
* @return
*/
int updateLockNumbers(@Param("list") List<LeaseMachine> gpuMachines);
/**
* 根据id 获取配置算法和币种
* @param ids
* @return
*/
@MapKey("id")
Map<Long, MiningConfigDto> getCoinAndAlgoById(@Param("ids") List<Long> ids);
}

View File

@@ -0,0 +1,41 @@
package com.m2pool.lease.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.m2pool.lease.dto.MachinePayTypeDto;
import com.m2pool.lease.entity.LeaseMachinePrice;
import com.m2pool.lease.entity.LeaseProductMachinePrice;
import com.m2pool.lease.vo.OrderInfoVo;
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>
* 商品表对应的物品机器表 Mapper 接口
* </p>
*
* @author yyb
* @since 2025-11-25
*/
@Mapper
public interface LeaseMachinePriceMapper extends BaseMapper<LeaseMachinePrice> {
/**
* 获取机器价格通过id
* @param machineIds
* @return
*/
List<MachinePayTypeDto> getMachinePriceByMachineIds(@Param("list") List<Long> machineIds);
/**
* 获取订单总金额 按照chain 和 分组 coin -v2
* @param orderInfoVoList
* @return
*/
@MapKey("productMachineId")
Map<Long, LeaseMachinePrice> getOrderTotalPriceGroupByChainAndCoin(@Param("list") List<OrderInfoVo> orderInfoVoList);
}

View File

@@ -0,0 +1,16 @@
package com.m2pool.lease.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.m2pool.lease.entity.LeaseMiningSoftwareConfig;
/**
* <p>
* 挖矿软件公共配置表 Mapper 接口
* </p>
*
* @author yyb
* @since 2025-11-27
*/
public interface LeaseMiningSoftwareConfigMapper extends BaseMapper<LeaseMiningSoftwareConfig> {
}

View File

@@ -0,0 +1,16 @@
package com.m2pool.lease.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.m2pool.lease.entity.LeaseOrderMining;
/**
* <p>
* 挖矿中订单 Mapper 接口
* </p>
*
* @author yyb
* @since 2025-12-02
*/
public interface LeaseOrderMiningMapper extends BaseMapper<LeaseOrderMining> {
}

View File

@@ -2,6 +2,7 @@ package com.m2pool.lease.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.m2pool.lease.dto.*;
import com.m2pool.lease.dto.v2.PayWithdrawSellerRecordDto;
import com.m2pool.lease.entity.LeasePayWithdrawMessage;
import com.m2pool.lease.entity.LeaseUserWalletData;
import org.apache.ibatis.annotations.MapKey;
@@ -74,4 +75,13 @@ public interface LeasePayWithdrawMessageMapper extends BaseMapper<LeasePayWithdr
* @return
*/
Integer getRecentlyOneData(@Param("fromAddress") String fromAddress,@Param("chain")String chain);
/**
* 获取商户提现记录
*
* @param shopId
* @return
*/
List<PayWithdrawSellerRecordDto> getSellerWithdrawRecord(@Param("shopId") Long shopId);
}

View File

@@ -152,4 +152,5 @@ public interface LeaseProductMachineMapper extends BaseMapper<LeaseProductMachin
* @return 插入结果
*/
int batchInsertPowers(@Param("coin")String coin,@Param("list")List<ProductMachineDto> list);
}

View File

@@ -2,6 +2,7 @@ package com.m2pool.lease.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.m2pool.lease.dto.*;
import com.m2pool.lease.dto.v2.ShopWalletInfoDto;
import com.m2pool.lease.entity.LeaseShopConfig;
import org.apache.ibatis.annotations.Param;
@@ -46,4 +47,18 @@ public interface LeaseShopConfigMapper extends BaseMapper<LeaseShopConfig> {
*/
List<PayTypeDto> getPayType(@Param("list") List<Long> shopIds);
/**
* 获取店铺钱包信息
* @param shopId
* @return
*/
List<ShopWalletInfoDto> getShopWalletInfoList(@Param("shopId") Long shopId);
/**
* 获取店铺钱包信息
* @param shopId
* @return
*/
ShopWalletInfoDto getShopWalletInfo(@Param("shopId") Long shopId,@Param("address") String address,@Param("chain") String chain,@Param("coin") String coin);
}

View File

@@ -3,16 +3,12 @@ package com.m2pool.lease.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.m2pool.lease.dto.ChainAndCoinDto;
import com.m2pool.lease.dto.PayConfigDto;
import com.m2pool.lease.entity.LeaseProductMachine;
import com.m2pool.lease.entity.LeaseShop;
import com.m2pool.lease.entity.LeaseShopConfig;
import com.m2pool.lease.vo.OrderInfoVo;
import com.m2pool.lease.vo.OrderItemVo;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -54,10 +50,10 @@ public interface LeaseShopMapper extends BaseMapper<LeaseShop> {
/**
* 根据商品id集合获取店铺id集合
* @param productIds
* @param machineIds
* @return
*/
List<Long> getShopIdsByProductIds(@Param("productIds") List<Long> productIds);
List<Long> getShopIdsByMachineIds(@Param("machineIds") List<Long> machineIds);
/**
* 根据店铺id集合获取店铺名称

View File

@@ -24,15 +24,14 @@ public interface LeaseShoppingCartInfoMapper extends BaseMapper<LeaseShoppingCar
* 查询购物车详情列表
* @return
*/
List<ShoppingCartInfoDto> getProductAndMachineIds(@Param("cartId") Long cartId);
List<ShoppingCartInfoDto> getProductAndMachineIds(@Param("userId") String userId, @Param("version") Integer version);
/**
* 批量删除购物车详情数据
* @param cartId
* @param productAndMachineVos
* @param ids
* @return
*/
int deleteBatchData(@Param("cartId") Long cartId,@Param("productAndMachineVos") List<ProductAndMachineVo> productAndMachineVos);
int deleteBatchData(@Param("ids") List<Long> ids);
}

View File

@@ -44,6 +44,8 @@ public class MessageReceiver {
private LeaseOrderItemMapper leaseOrderItemMapper;
@Resource
private LeaseOrderItemService leaseOrderItemService;
@Resource
private LeaseShopConfigMapper leaseShopConfigMapper;
@Resource
private RabbitTemplate rabbitTemplate;
@@ -335,6 +337,34 @@ public class MessageReceiver {
if (leasePayWithdrawMessageList.isEmpty()){
return ;
}
LeasePayWithdrawMessage leasePayWithdrawMessage = handlerBuyerWithdraw(payWithdrawReturnMessage, leasePayWithdrawMessageList);
if (leasePayWithdrawMessage != null){
LeaseShopConfig leaseShopConfig = leaseShopConfigMapper.selectOne(new LambdaQueryWrapper<LeaseShopConfig>()
.eq(LeaseShopConfig::getPayAddress, leasePayWithdrawMessage.getFromAddress())
.eq(LeaseShopConfig::getChain, leasePayWithdrawMessage.getFromChain())
.eq(LeaseShopConfig::getPayCoin, leasePayWithdrawMessage.getFromSymbol()));
if (leaseShopConfig != null){
//要修改的钱包状态为1 且提现的状态为1 则不处理
if (leasePayWithdrawMessage.getStatus() == 1 && payWithdrawReturnMessage.getStatus() == 1){
return;
}
if (payWithdrawReturnMessage.getStatus() == 1){
leaseShopConfig.setBalance(leaseShopConfig.getBalance()
.add(leasePayWithdrawMessage.getAmount())
.add(leasePayWithdrawMessage.getServiceCharge()));
leaseShopConfigMapper.updateById(leaseShopConfig);
}
leasePayWithdrawMessageMapper.updateById(LeasePayWithdrawMessage.builder()
.id(leasePayWithdrawMessage.getId())
.status(payWithdrawReturnMessage.getStatus())
.build());
//TODO 修改leaseShopConfig 金额(暂时不修改,提现申请的时候就修改了)
}
}
}
public LeasePayWithdrawMessage handlerBuyerWithdraw(RabbitmqPayWithdrawReturnMessage payWithdrawReturnMessage,List<LeasePayWithdrawMessage> leasePayWithdrawMessageList){
LeasePayWithdrawMessage leasePayWithdrawMessage = leasePayWithdrawMessageList.get(0);
//获取对应的提现钱包
LeaseUserWalletData leaseUserWalletData = leaseUserWalletDataMapper.selectOne(new LambdaQueryWrapper<LeaseUserWalletData>()
@@ -342,7 +372,9 @@ public class MessageReceiver {
.eq(LeaseUserWalletData::getFromChain, leasePayWithdrawMessage.getFromChain())
.eq(LeaseUserWalletData::getFromSymbol, leasePayWithdrawMessage.getFromSymbol())
.eq(LeaseUserWalletData::getDel,false));
if (leaseUserWalletData == null){
return leasePayWithdrawMessage;
}
BigDecimal balance = leaseUserWalletData.getBalance()
.subtract(payWithdrawReturnMessage.getAmount())
.subtract(leasePayWithdrawMessage.getServiceCharge());
@@ -370,7 +402,7 @@ public class MessageReceiver {
}else{
//要修改的钱包状态为1 且提现的状态为1 则不处理
if (leasePayWithdrawMessage.getStatus() == 1 && payWithdrawReturnMessage.getStatus() == 1){
return;
return null;
}
leasePayWithdrawMessageMapper.updateById(LeasePayWithdrawMessage.builder()
.id(leasePayWithdrawMessage.getId())
@@ -415,13 +447,12 @@ public class MessageReceiver {
updateWalletInfo(payWithdrawReturnMessage.getStatus(), leaseUserWalletData, balance, blockBalance);
}
}
}
return null;
}
/**
* 更新钱包余额信息
* @param status

View File

@@ -0,0 +1,83 @@
package com.m2pool.lease.netty.handler;
import io.netty.channel.Channel;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @Description 频道管理类
* @Date 2025/11/25 15:07
* @Author yyb
*/
public class ChannelManager {
/**
* key ip value channel
*/
private static final Map<String, Channel> channelMap = new ConcurrentHashMap<>();
/**
* key mac value ip
*/
private static final Map<String, String> macMap = new ConcurrentHashMap<>();
/**
* 添加Channel
* @param ip
* @param channel
*/
public static void addChannel(String ip, Channel channel) {
Channel prev = channelMap.put(ip, channel);
if (prev != null && prev.isActive()) {
prev.close();
}
}
/**
* 移除Channel
* @param ip
*/
public static void removeChannel(String ip) {
// 需要先移除MAC映射
macMap.values().removeIf(value -> value.equals(ip));
channelMap.remove(ip);
}
/**
* 根据ip获取Channel
* @param ip
* @return
*/
public static Channel getChannelByIp(String ip) {
return channelMap.get(ip);
}
/**
* 根据mac地址获取Channel
* @param mac
* @return
*/
public static Channel getChannelByMac(String mac) {
String ip = macMap.get(mac);
if (ip != null) {
return channelMap.get(ip);
}
return null;
}
public static boolean sendToClient(String mac, Object message) {
Channel channel = getChannelByMac(mac);
if (channel != null && channel.isActive()) {
channel.writeAndFlush(message).syncUninterruptibly();
return false;
}else{
return true;
}
}
}

View File

@@ -0,0 +1,43 @@
package com.m2pool.lease.netty.handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.m2pool.lease.netty.parser.*;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import java.util.ArrayList;
import java.util.List;
public class MessageToJsonDecoder extends ByteToMessageDecoder {
private final ObjectMapper objectMapper = new ObjectMapper();
private final List<MessageParser<?>> parsers = new ArrayList<>();
public MessageToJsonDecoder() {
// 初始化解析器列表
parsers.add(new StringParser());
parsers.add(new ClientConfigMiningParser());
parsers.add(new GpuMessageMapParser());
parsers.add(new ServerConfigMiningParser());
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
if (in.readableBytes() > 0) {
byte[] bytes = new byte[in.readableBytes()];
in.readBytes(bytes);
String jsonStr = new String(bytes);
for (MessageParser<?> parser : parsers) {
try {
Object message = parser.parse(objectMapper, jsonStr);
out.add(message);
return;
} catch (Exception e) {
// 继续尝试下一个解析器
}
}
}
}
}

View File

@@ -0,0 +1,31 @@
package com.m2pool.lease.netty.handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
public class MessageToJsonEncoder extends MessageToByteEncoder<Object> {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
// 使用 ObjectMapper 将 msg 对象序列化为 JSON 字符串
String jsonStr = objectMapper.writeValueAsString(msg);
// 如果 msg 是 String 类型,则直接使用 msg 的值
if (msg instanceof String){
jsonStr = (String) msg;
}
// 明确使用 UTF-8 编码创建 ByteBuf
ByteBuf buffer = Unpooled.copiedBuffer(jsonStr, java.nio.charset.StandardCharsets.UTF_8);
try {
// 将生成的 ByteBuf 内容写入输出的 ByteBuf
out.writeBytes(buffer);
} finally {
// 释放临时创建的 ByteBuf避免内存泄漏
buffer.release();
}
}
}

View File

@@ -0,0 +1,301 @@
package com.m2pool.lease.netty.handler;
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.m2pool.lease.mapper.*;
import com.m2pool.lease.netty.message.*;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@Component
@ChannelHandler.Sharable
public class ServerChannelHandler extends SimpleChannelInboundHandler<ClientMessage<Object>> {
/**
* 心跳检测
*/
public static Map<Channel, Boolean> heartbeatMap = new ConcurrentHashMap<Channel, Boolean>();
@Resource
private LeaseShopMapper leaseShopMapper;
@Resource
private LeaseMachineConfigMapper leaseMachineConfigMapper;
@Resource
private LeaseMachineMapper leaseMachineMapper;
@Resource
private LeaseGpuConfigMapper leaseGpuConfigMapper;
@Resource
private LeaseMiningSoftwareConfigMapper leaseMiningSoftwareConfigMapper;
/**
* 接收到客户端的信息
*
* @param ctx
* @param msg
* @throws Exception
*/
@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消息");
handlerDownloadClientMessage(ctx,msg);
}else if (MethodConstant.MINING_REP.equals(msg.getMethod())){
System.out.println("Netty tcp 服务端收到客户端开始挖矿信息");
handlerClientMiningMessage(ctx,msg);
}else{
System.out.println("未知消息方法");
}
}
/**
* 处理客户端发送主机gpu信息消息业务
* @param msg
*/
private void handlerDownloadClientMessage(ChannelHandlerContext ctx,ClientMessage<Object> msg){
boolean b = checkMessage( ctx,msg);
if (b){
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();
//GPU配置信息配置
//int insertOrUpdate = leaseGpuConfigMapper.insertOrUpdate(gpuMessagesList);
//TODO 挖矿软件公共配置
//leaseMiningSoftwareConfigMapper.insertOrUpdate();
//TODO 新增主机信息
}
}
}
/**
* 处理客户端发送的挖矿开始信息
* @param msg
*/
private void handlerClientMiningMessage(ChannelHandlerContext ctx,ClientMessage<Object> msg){
Object params = msg.getParams();
boolean b = checkMessage(ctx,msg);
if (b){
if (params instanceof ClientConfigurationMining){
ClientConfigurationMining clientConfigurationMining = (ClientConfigurationMining) params;
}else{
//挖矿开始失败
}
}
}
/**
* 检查消息的合法性
* @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;
}
}
return true;
}
/**
* 身份码校验失败
* @param ctx
* @param id
*/
public void sendErrorResp(ChannelHandlerContext ctx,String id){
ServerMessage<String> errorMsg = new ServerMessage<>();
errorMsg.setId(id);
errorMsg.setResult(false);
ctx.channel().writeAndFlush(errorMsg).syncUninterruptibly();
}
/**
* 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;
}
/**
* 活跃的、有效的通道
* 第一次连接成功后进入的方法
*
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
String ip = getIPString(ctx);
System.out.println("ip:"+ip+"连接客户端");
ChannelManager.addChannel(ip, ctx.channel());
}
/**
* 不活动的通道
* 连接丢失后执行的方法client端可据此实现断线重连
*
* @param ctx
* @throws Exception
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) {
ChannelManager.removeChannel(getIPString(ctx));
ctx.close();
}
/**
* 异常处理
*
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
//发生异常,关闭连接
System.out.println("引擎 {} 的通道发生异常,即将断开连接"+ getRemoteAddress(ctx));
ctx.close();//再次建议close
}
@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);
}
}
/**
* 心跳机制,超时处理
*
* @param ctx
* @param evt
* @throws Exception
*/
@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);
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 + " ");
ChannelManager.removeChannel(ip);
ctx.disconnect();//断开
} else if (event.state() == IdleState.ALL_IDLE) {
System.out.println("客户端 ALL_IDLE 总超时: " + socketString);
ChannelManager.removeChannel(ip);
ctx.disconnect();
}
}
}
/**
* 获取client对象ip+port
*
* @param ctx
* @return
*/
public String getRemoteAddress(ChannelHandlerContext ctx) {
String socketString = "";
socketString = ctx.channel().remoteAddress().toString();
return socketString;
}
/**
* 获取client的ip
*
* @param ctx
* @return
*/
public String getIPString(ChannelHandlerContext ctx) {
String ipString = "";
String socketString = ctx.channel().remoteAddress().toString();
int colonAt = socketString.indexOf(":");
ipString = socketString.substring(1, colonAt);
return ipString;
}
}

View File

@@ -0,0 +1,52 @@
package com.m2pool.lease.netty.message;
import lombok.Data;
/**
* @Description 客户端响应给服务器的消息
* @Date 2025/11/26 14:19
* @Author yyb
*/
@Data
public class ClientConfigurationMining {
/**
* 币种
*/
private String coin;
/**
* 算法
*/
private String algo;
/**
* 矿池名称
*/
private String pool;
/**
* 挖矿地址
*/
private String pool_url;
/**
* 矿池挖矿号 仅支持不支持钱包挖矿的矿池 如 f2poolm2pool
*/
private String worker_id;
/**
* 收款钱包
*/
private String wallet_address;
/**
* 挖矿信息页面 只有支持钱包挖矿的矿池才有这个字段
*/
private String watch_url;
}

View File

@@ -0,0 +1,30 @@
package com.m2pool.lease.netty.message;
import lombok.Data;
/**
* @Description 客户端消息
* @Date 2025/11/26 11:13
* @Author yyb
*/
@Data
public class ClientMessage<T> {
/**
* 身份码.主机码
*/
private String id;
/**
* 方法名 auth.machineCode
*/
private String method;
/**
* 消息体
*/
private T params;
}

View File

@@ -0,0 +1,24 @@
package com.m2pool.lease.netty.message;
import lombok.Builder;
import lombok.Data;
import java.util.List;
import java.util.Map;
@Data
@Builder
public class GpuAndSoftMessage {
/**
* 挖矿软件集合
*/
private List<String> miningsofts;
/**
* gpu集合
*/
private Map<String, GpuMessage> gpus;
}

View File

@@ -0,0 +1,48 @@
package com.m2pool.lease.netty.message;
import lombok.Data;
/**
* @Description 客户端GPU消息
* @Date 2025/11/26 16:42
* @Author yyb
*/
@Data
public class GpuMessage {
/**
* 显卡品牌
*/
private String brand;
/**
* 显卡型号
*/
private String model;
/**
* 显存大小
*/
private String 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

@@ -0,0 +1,28 @@
package com.m2pool.lease.netty.message;
import lombok.Data;
/**
* @Description 消息 类型常量类
* @Date 2025/11/26 15:31
* @Author yyb
*/
@Data
public class MethodConstant {
/**
* 买家下单后,需要发送一个消息给卖家客户端 (客户端消息)
*/
public static final String MINING_RAG = "mining.req";
/**
* 客户端开始挖矿成功后,给服务端一个响应 (服务端消息)
*/
public static final String MINING_REP = "mining.resp";
/**
* 用户下载客户端后,客户端主动发送一个消息到服务端 (服务段消息)
*/
public static final String AUTH_MACHINE_CODE = "auth.machineCode";
}

View File

@@ -0,0 +1,58 @@
package com.m2pool.lease.netty.message;
import lombok.Data;
/**
* @Description 客户端响应给服务端的客户端主机的gpu信息
* @Date 2025/11/26 14:13
* @Author yyb
*/
@Data
public class ServerConfigurationMining {
/**
* 币种
*/
private String coin;
/**
* 算法
*/
private String algo;
/**
* 矿池名称
*/
private String pool;
/**
* 是否支持钱包挖矿 0 不支持 1 支持
*/
private Boolean wallet_mining;
/**
* 挖矿地址
*/
private String pool_url;
/**
* 收款钱包
*/
private String wallet_address;
/**
* 矿池挖矿账号 仅支持不支持钱包挖矿的矿池 如 f2poolm2pool
*/
private String pool_user;
/**
* 矿池挖矿号 仅支持不支持钱包挖矿的矿池 如 f2poolm2pool
*/
private String worker_id;
/**
* 合约结束时间
*/
private Long end_timestamp;
}

View File

@@ -0,0 +1,31 @@
package com.m2pool.lease.netty.message;
import lombok.Data;
/**
* @Description 服务端响应给客户端的信息
* @Date 2025/11/26 14:11
* @Author yyb
*/
@Data
public class ServerMessage<T> {
/**
* 身份码.主机码
*/
private String id;
/**
* 方法名 0 false 1 true
*/
private Boolean result;
/**
* 方法名 auth.machineCode 详情见
*/
private String method;
/**
* 消息体
*/
private T data;
}

View File

@@ -0,0 +1,19 @@
package com.m2pool.lease.netty.parser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.m2pool.lease.netty.message.ClientConfigurationMining;
import com.m2pool.lease.netty.message.ClientMessage;
public class ClientConfigMiningParser implements MessageParser<ClientMessage<ClientConfigurationMining>> {
@Override
public ClientMessage<ClientConfigurationMining> parse(ObjectMapper objectMapper, String jsonStr) throws Exception {
return objectMapper.readValue(jsonStr, getTypeReference());
}
@Override
public TypeReference<ClientMessage<ClientConfigurationMining>> getTypeReference() {
return new TypeReference<ClientMessage<ClientConfigurationMining>>() {};
}
}

View File

@@ -0,0 +1,21 @@
package com.m2pool.lease.netty.parser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.m2pool.lease.netty.message.ClientMessage;
import com.m2pool.lease.netty.message.GpuMessage;
import java.util.HashMap;
public class GpuMessageMapParser implements MessageParser<ClientMessage<HashMap<Integer, GpuMessage>>> {
@Override
public ClientMessage<HashMap<Integer, GpuMessage>> parse(ObjectMapper objectMapper, String jsonStr) throws Exception {
return objectMapper.readValue(jsonStr, getTypeReference());
}
@Override
public TypeReference<ClientMessage<HashMap<Integer, GpuMessage>>> getTypeReference() {
return new TypeReference<ClientMessage<HashMap<Integer, GpuMessage>>>() {};
}
}

View File

@@ -0,0 +1,21 @@
package com.m2pool.lease.netty.parser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
public interface MessageParser<T> {
/**
* 解析 JSON 字符串为指定类型的消息对象
* @param objectMapper Jackson 的 ObjectMapper 实例
* @param jsonStr 待解析的 JSON 字符串
* @return 解析后的消息对象
* @throws Exception 解析过程中可能抛出的异常
*/
T parse(ObjectMapper objectMapper, String jsonStr) throws Exception;
/**
* 获取该解析器对应的类型引用
* @return 类型引用
*/
TypeReference<T> getTypeReference();
}

View File

@@ -0,0 +1,19 @@
package com.m2pool.lease.netty.parser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.m2pool.lease.netty.message.ServerConfigurationMining;
import com.m2pool.lease.netty.message.ServerMessage;
public class ServerConfigMiningParser implements MessageParser<ServerMessage<ServerConfigurationMining>> {
@Override
public ServerMessage<ServerConfigurationMining> parse(ObjectMapper objectMapper, String jsonStr) throws Exception {
return objectMapper.readValue(jsonStr, getTypeReference());
}
@Override
public TypeReference<ServerMessage<ServerConfigurationMining>> getTypeReference() {
return new TypeReference<ServerMessage<ServerConfigurationMining>>() {};
}
}

View File

@@ -0,0 +1,19 @@
package com.m2pool.lease.netty.parser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.m2pool.lease.netty.message.ClientConfigurationMining;
import com.m2pool.lease.netty.message.ClientMessage;
public class StringParser implements MessageParser<String> {
@Override
public String parse(ObjectMapper objectMapper, String jsonStr) throws Exception {
return jsonStr;
}
@Override
public TypeReference<String> getTypeReference() {
return new TypeReference<String>() {};
}
}

View File

@@ -0,0 +1,90 @@
package com.m2pool.lease.netty.server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.Future;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class NettyTcpServer {
private static final Logger log = LoggerFactory.getLogger(NettyTcpServer.class);
//boss事件轮询线程组
//处理Accept连接事件的线程这里线程数设置为1即可netty处理链接事件默认为单线程过度设置反而浪费cpu资源
private EventLoopGroup boss = new NioEventLoopGroup(1);
//worker事件轮询线程组
//处理hadnler的工作线程其实也就是处理IO读写 。线程数据默认为 CPU 核心数乘以2
private EventLoopGroup worker = new NioEventLoopGroup();
@Autowired
ServerChannelInitializer serverChannelInitializer;
@Value("${netty.tcp.client.port}")
private Integer port;
//与客户端建立连接后得到的通道对象
private Channel channel;
/**
* 开启Netty tcp server服务
*
* @return
*/
public ChannelFuture start() {
//启动类
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(boss, worker)//组配置初始化ServerBootstrap的线程组
.channel(NioServerSocketChannel.class)///构造channel通道工厂//bossGroup的通道只是负责连接
.childHandler(serverChannelInitializer)//设置通道处理者ChannelHandler////workerGroup的处理器
.option(ChannelOption.SO_BACKLOG, 1024)//socket参数当服务器请求处理程全满时用于临时存放已完成三次握手请求的队列的最大长度。如果未设置或所设置的值小于1Java将使用默认值50。
.childOption(ChannelOption.SO_KEEPALIVE, true);//启用心跳保活机制tcp默认2小时发一次心跳
//Future异步任务的生命周期可用来获取任务结果
ChannelFuture channelFuture1 = serverBootstrap.bind(port).syncUninterruptibly();//绑定端口,开启监听,同步等待
if (channelFuture1 != null && channelFuture1.isSuccess()) {
channel = channelFuture1.channel();//获取通道
log.info("Netty tcp 服务端启动成功, 端口号 = {}", port);
} else {
log.error("Netty tcp 服务端启动失败");
}
return channelFuture1;
}
/**
* 停止Netty tcp server服务
*/
@PreDestroy
public void destroy() {
if (channel != null) {
channel.close();
}
try {
Future<?> future = worker.shutdownGracefully().await();
if (!future.isSuccess()) {
log.error("netty tcp workerGroup shutdown fail, {}", future.cause());
}
Future<?> future1 = boss.shutdownGracefully().await();
if (!future1.isSuccess()) {
log.error("netty tcp bossGroup shutdown fail, {}", future1.cause());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("Netty tcp server shutdown success");
}
}

View File

@@ -0,0 +1,38 @@
package com.m2pool.lease.netty.server;
import com.m2pool.lease.netty.handler.MessageToJsonDecoder;
import com.m2pool.lease.netty.handler.MessageToJsonEncoder;
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.timeout.IdleStateHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
@Autowired
ServerChannelHandler serverChannelHandler;
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
//IdleStateHandler心跳机制,如果超时触发Handle中userEventTrigger()方法
pipeline.addLast("idleStateHandler",
new IdleStateHandler(10, 0, 0, TimeUnit.MINUTES));
//字符串编解码器
pipeline.addLast(new MessageToJsonDecoder(),new MessageToJsonEncoder());
//pipeline.addLast(
// new StringDecoder(),
// new StringEncoder()
//);
//自定义Handler
pipeline.addLast("serverChannelHandler", serverChannelHandler);
}
}

View File

@@ -0,0 +1,16 @@
package com.m2pool.lease.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.m2pool.lease.entity.LeaseMachineConfig;
/**
* <p>
* GPU对应币种挖矿算力(只要gpu类型相同那么就使用这个配置) 服务类
* </p>
*
* @author yyb
* @since 2025-11-27
*/
public interface LeaseMachineConfigService extends IService<LeaseMachineConfig> {
}

View File

@@ -0,0 +1,16 @@
package com.m2pool.lease.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.m2pool.lease.entity.LeaseMachinePrice;
/**
* <p>
* 商品表对应的物品机器表 服务类
* </p>
*
* @author yyb
* @since 2025-11-25
*/
public interface LeaseMachinePriceService extends IService<LeaseMachinePrice> {
}

View File

@@ -0,0 +1,80 @@
package com.m2pool.lease.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.m2pool.lease.dto.PageResult;
import com.m2pool.lease.dto.Result;
import com.m2pool.lease.dto.v2.MachineInfoDto;
import com.m2pool.lease.dto.v2.SellerMachineInfoDto;
import com.m2pool.lease.dto.v2.ShopInfoDto;
import com.m2pool.lease.entity.LeaseMachine;
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 java.util.List;
/**
* <p>
* gpu类型出售矿机表 服务类
* </p>
*
* @author yyb
* @since 2025-11-21
*/
public interface LeaseMachineService extends IService<LeaseMachine> {
/**
* 获取商品对应的实际商品列表
* @return
*/
PageResult<ShopInfoDto> getShopList(ProductPageVo productPageVo);
/**
* 获取店铺矿机详情
* @return
*/
PageResult<MachineInfoDto> getShopMachineList(ShopMachineVo shopMachineVo);
/**
* 获取卖家页面矿机列表
* @return
*/
PageResult<SellerMachineInfoDto> getShopMachineListForSeller(SellerMachineVo sellerMachineVo);
/**
* 添加ASIC矿机
* @return
*/
Result<String> addAsicMachine( AsicMachineParamsVo asicMachineParamsVo);
/**
* 修改ASIC矿机
* @return
*/
Result<String> updateAsicMachine( AsicMachineParamsVo asicMachineParamsVo);
/**
* 修改GPU矿机
* @return
*/
Result<String> updateGpuMachine(List<GpuMachineParamsVo> gpuMachineParamsVoList);
/**
* 矿机上下架
* @return
*/
Result<String> updateMachineState(MachineStateVo machineStateVo);
/**
* 删除矿机
* @return
*/
Result<String> deleteMachine(BaseVo baseVo);
}

View File

@@ -5,9 +5,11 @@ 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.v2.CoinAndAlgorithmListDto;
import com.m2pool.lease.dto.v2.MiningInfoDto;
import com.m2pool.lease.entity.LeaseOrderInfo;
import com.m2pool.lease.vo.*;
import org.springframework.web.bind.annotation.RequestBody;
import com.m2pool.lease.vo.v2.CoinAndAlgorithmVo;
import java.math.BigDecimal;
import java.util.List;
@@ -69,4 +71,54 @@ public interface LeaseOrderInfoService extends IService<LeaseOrderInfo> {
* @return
*/
Result<BigDecimal> getCoinPrice(CoinVo coinVo);
/**
* 获取矿机对应的支持币种和算法
* @return
*/
Result<List<CoinAndAlgorithmListDto>> getMachineSupportCoinAndAlgorithm(List<BaseVo> machineIds);
/**
* 获取当前矿机支持的矿池币种信息
* @param machineIds
* @return
*/
Result<List<MiningInfoDto>> getMachineSupportPool(CoinAndAlgorithmVo coinAndAlgorithmVo);
/**
* 添加订单-V2
* @param orderAndCodeVo
* @return
*/
Result<String> addOrdersV2( OrderAndCodeVo orderAndCodeVo);
/**
* 根据订单状态查询订单列表-V2买家
* @param orderInfoStateVo
* @return
*/
PageResult<OrderInfoDto> getOrdersByStatusV2(OrderInfoStateVo orderInfoStateVo);
/**
* 根据订单状态查询订单列表-V2卖家
* @param orderInfoStateVo
* @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);
}

View File

@@ -2,14 +2,13 @@ package com.m2pool.lease.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.m2pool.lease.dto.*;
import com.m2pool.lease.dto.v2.PayWithdrawSellerRecordDto;
import com.m2pool.lease.dto.v2.ShopWalletInfoDto;
import com.m2pool.lease.entity.LeaseShop;
import com.m2pool.lease.vo.BaseVo;
import com.m2pool.lease.vo.ShopConfigVo;
import com.m2pool.lease.vo.ShopVo;
import org.springframework.web.bind.annotation.RequestBody;
import com.m2pool.lease.vo.*;
import com.m2pool.lease.vo.v2.ShopWalletInfoVo;
import java.util.List;
import java.util.Map;
/**
* <p>
@@ -114,4 +113,34 @@ public interface LeaseShopService extends IService<LeaseShop> {
Result<ChainListDto> getChainAndCoin(BaseVo baseVo);
/**
* 根据店铺id 获取店铺配置信息列表--v2 新增手续费和钱包余额
* @param baseVo
* @return
*/
Result<List<ShopWalletInfoDto>> getShopConfigV2(BaseVo baseVo);
/**
* 卖家余额提现
* @param balanceVo
* @return
*/
Result<String> withdrawBalanceForSeller(BalanceVo balanceVo);
/**
* 修改商铺配置--v2 新增手续费和钱包余额
*/
Result<String> updateShopConfigV2(ShopWalletInfoVo shopWalletInfoVo);
/**
* 获取店铺内所有钱包(包含历史钱包)提现列表
* @return
*/
PageResult<PayWithdrawSellerRecordDto> balanceWithdrawList(PageVo pageVo);
}

View File

@@ -4,11 +4,15 @@ import com.baomidou.mybatisplus.extension.service.IService;
import com.m2pool.lease.dto.PageResult;
import com.m2pool.lease.dto.Result;
import com.m2pool.lease.dto.ShopCartDto;
import com.m2pool.lease.dto.v2.ShopCartV2Dto;
import com.m2pool.lease.entity.LeaseShoppingCart;
import com.m2pool.lease.vo.BaseVo;
import com.m2pool.lease.vo.PageVo;
import com.m2pool.lease.vo.ProductAndMachineVo;
import com.m2pool.lease.vo.ShoppingCartInfoURDVo;
import com.m2pool.lease.vo.v2.AddGoodsVo;
import com.m2pool.lease.vo.v2.CartInfoVo;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
@@ -55,4 +59,32 @@ public interface LeaseShoppingCartService extends IService<LeaseShoppingCart> {
* @return
*/
Result<String> deleteBatchGoodsForIsDelete();
/**
* 批量添加购物车
* @param addGoodsVoList
* @return
*/
Result<String> addGoodsV2(List<AddGoodsVo> addGoodsVoList);
/**
* 查询购物车中商品列表
* @param pageVo
* @return
*/
PageResult<ShopCartV2Dto> getGoodsListV2(PageVo pageVo);
/**
* 批量删除购物车中商品
* @param baseVoList
* @return
*/
Result<String> deleteBatchGoodsV2(List<CartInfoVo> baseVoList);
/**
* 批量删除购物车中已下架商品
* @return
*/
Result<String> deleteBatchGoodsForIsDeleteV2();
}

View File

@@ -5,7 +5,10 @@ import com.m2pool.lease.dto.*;
import com.m2pool.lease.entity.LeaseUser;
import com.m2pool.lease.vo.*;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
@@ -90,4 +93,17 @@ public interface LeaseUserService extends IService<LeaseUser> {
Result<List<ChargeDto>> getCharge();
/**
* 下载客户端
* @param response
* @return
*/
void downloadClient(String userEmail,HttpServletRequest request, HttpServletResponse response);
/**
* 获取用户信息
* @return
*/
String getClientVersion();
}

View File

@@ -0,0 +1,20 @@
package com.m2pool.lease.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.m2pool.lease.entity.LeaseMachineConfig;
import com.m2pool.lease.mapper.LeaseMachineConfigMapper;
import com.m2pool.lease.service.LeaseMachineConfigService;
import org.springframework.stereotype.Service;
/**
* <p>
* GPU对应币种挖矿算力(只要gpu类型相同那么就使用这个配置) 服务实现类
* </p>
*
* @author yyb
* @since 2025-11-27
*/
@Service
public class LeaseMachineConfigServiceImpl extends ServiceImpl<LeaseMachineConfigMapper, LeaseMachineConfig> implements LeaseMachineConfigService {
}

View File

@@ -0,0 +1,20 @@
package com.m2pool.lease.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.m2pool.lease.entity.LeaseMachinePrice;
import com.m2pool.lease.mapper.LeaseMachinePriceMapper;
import com.m2pool.lease.service.LeaseMachinePriceService;
import org.springframework.stereotype.Service;
/**
* <p>
* 商品表对应的物品机器表 服务实现类
* </p>
*
* @author yyb
* @since 2025-11-25
*/
@Service
public class LeaseMachinePriceServiceImpl extends ServiceImpl<LeaseMachinePriceMapper, LeaseMachinePrice> implements LeaseMachinePriceService {
}

View File

@@ -0,0 +1,444 @@
package com.m2pool.lease.service.impl;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.StringUtils;
import com.m2pool.common.security.utils.SecurityUtils;
import com.m2pool.lease.dto.*;
import com.m2pool.lease.dto.v2.*;
import com.m2pool.lease.entity.LeaseMachine;
import com.m2pool.lease.entity.LeaseMachineConfig;
import com.m2pool.lease.entity.LeaseMachinePrice;
import com.m2pool.lease.entity.LeaseShop;
import com.m2pool.lease.exception.MachineException;
import com.m2pool.lease.exception.ProductSoldOutException;
import com.m2pool.lease.mapper.*;
import com.m2pool.lease.service.LeaseMachineConfigService;
import com.m2pool.lease.service.LeaseMachinePriceService;
import com.m2pool.lease.service.LeaseMachineService;
import com.m2pool.lease.utils.UuidGeneratorUtil;
import com.m2pool.lease.vo.BaseVo;
import com.m2pool.lease.vo.ProductPageVo;
import com.m2pool.lease.vo.v2.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
/**
* <p>
* gpu类型出售矿机表 服务实现类
* </p>
*
* @author yyb
* @since 2025-11-21
*/
@Service
public class LeaseMachineServiceImpl extends ServiceImpl<LeaseMachineMapper, LeaseMachine> implements LeaseMachineService {
@Resource
private LeaseShopConfigMapper leaseShopConfigMapper;
@Resource
private LeaseMachineMapper leaseMachineMapper;
@Resource
private LeaseShopMapper leaseShopMapper;
@Resource
private LeaseMachineConfigMapper leaseMachineConfigMapper;
@Resource
private LeaseMachinePriceMapper leaseMachinePriceMapper;
@Resource
private LeaseMachineConfigService leaseMachineConfigService;
@Resource
private LeaseMachinePriceService leaseMachinePriceService;
public LeaseShop getShopById() {
return leaseShopMapper.selectOne(new LambdaQueryWrapper<LeaseShop>().eq(LeaseShop::getUserEmail, SecurityUtils.getUsername())
.eq(LeaseShop::getDel, false));
}
@Override
public PageResult<ShopInfoDto> getShopList(ProductPageVo productPageVo) {
//获取到矿机
Set<Long> shopIds = leaseMachineMapper.getShopIds();
if (shopIds.isEmpty()){
return PageResult.fail(new ArrayList<>(), "暂无商品");
}
PageHelper.startPage(productPageVo.getPageNum(), productPageVo.getPageSize());
List<ShopInfoDto> existsMachineShop = leaseMachineMapper.getExistsMachineShop(shopIds, productPageVo.getCoin(), productPageVo.getAlgorithm());
PageInfo<ShopInfoDto> pageInfo = new PageInfo<>(existsMachineShop);
//店铺支付方式
List<PayTypeDto> payType = leaseShopConfigMapper.getPayType(new ArrayList<>(shopIds));
Map<Long, List<PayTypeDto>> payTypeMap = payType.stream().collect(Collectors.groupingBy(PayTypeDto::getShopId));
PageResult<ShopInfoDto> success = PageResult.success(existsMachineShop);
for (ShopInfoDto shopInfoDto : success.getRows()) {
shopInfoDto.setPayTypes(payTypeMap.get(shopInfoDto.getShopId()));
}
success.setTotal(pageInfo.getTotal());
success.setTotalPage(pageInfo.getPages());
PageHelper.clearPage();
return success;
}
@Override
public PageResult<MachineInfoDto> getShopMachineList(ShopMachineVo shopMachineVo) {
//获取店铺钱包信息
List<PayConfigDto> shopWalletInfo = leaseShopMapper.getShopWalletInfo(shopMachineVo.getShopId());
//分页查询矿机基础信息
PageHelper.startPage(shopMachineVo.getPageNum(), shopMachineVo.getPageSize());
shopMachineVo = buildQuery(shopMachineVo,shopWalletInfo.get(0));
List<MachineInfoDto> machineInfoDtoList = leaseMachineMapper.getShopMachineList(shopMachineVo);
PageInfo<MachineInfoDto> pageInfo = new PageInfo<>(machineInfoDtoList);
//获取gpu和asic对应币种算力和月收益信息
PageResult<MachineInfoDto> success = setGpuAndAsicInfo(PageResult.successColumns(machineInfoDtoList),shopMachineVo);
success.setTotal(pageInfo.getTotal());
success.setTotalPage(pageInfo.getPages());
PageHelper.clearPage();
return success;
}
/**
* 设置gpu和asic 对应的收益列表信息
* @param
*/
public PageResult<MachineInfoDto> setGpuAndAsicInfo(PageResult<MachineInfoDto> success,ShopMachineVo shopMachineVo) {
List<MachineInfoDto> machineInfoDtoList = success.getRows();
List<TableHeadersDto> columns = success.getColumns();
if (!machineInfoDtoList.isEmpty()){
List<Long> machineIds = machineInfoDtoList.stream()
.map(MachineInfoDto::getId).collect(Collectors.toList());
//获取价格列表(价格列表只与主表有关与machine_config 表无关)
List<MachinePayTypeDto> machinePriceByMachineIds = leaseMachinePriceMapper.getMachinePriceByMachineIds(machineIds);
Map<Long, List<MachinePayTypeDto>> machinePriceMap = machinePriceByMachineIds.stream()
.collect(Collectors.groupingBy(MachinePayTypeDto::getProductMachineId));
//动态列:获取每个矿机前十个币种算法的配置
List<PowerIncomeInfoDto> gpuConfigList = leaseMachineMapper.getGpuConfigList(machineIds);
Map<Long, List<PowerIncomeInfoDto>> collect = gpuConfigList.stream()
.collect(Collectors.groupingBy(PowerIncomeInfoDto::getMachineId));
for (MachineInfoDto machineInfo : machineInfoDtoList) {
machineInfo.setPriceList(machinePriceMap.get(machineInfo.getId()));
List<PowerIncomeInfoDto> powerIncomeInfoDtoList = collect.get(machineInfo.getId());
for (int i = 0; i < powerIncomeInfoDtoList.size(); i++) {
PowerIncomeInfoDto powerIncomeInfoDto = powerIncomeInfoDtoList.get(i);
setDynamicColumn(columns,powerIncomeInfoDto,machineInfo);
}
}
//排序分页后对象
List<MachineInfoDto> sorted = sorted(machineInfoDtoList, shopMachineVo);
success.setRows(sorted);
success.setColumns(columns);
}
return success;
}
/**
* 设置算力动态列
* @param columns
*/
public void setDynamicColumn(List<TableHeadersDto> columns,PowerIncomeInfoDto powerIncomeInfoDto,MachineInfoDto machineInfo){
TableHeadersDto column = TableHeadersDto.builder()
.label(powerIncomeInfoDto.getCoin())
.type("hashrate")
.unit(powerIncomeInfoDto.getUnit())
.icon(powerIncomeInfoDto.getIcon())
.width(100).build();
int index = 1;
for (TableHeadersDto tableHeadersDto : columns) {
if (index < 10 && "hashrate".equals(tableHeadersDto.getType())){
if (column.getLabel().equals(tableHeadersDto.getLabel())){
setDynamicPower(index,machineInfo,powerIncomeInfoDto);
return;
}else{
index++;
}
}
}
if (index <= 10){
setDynamicPower(index,machineInfo,powerIncomeInfoDto);
column.setKey("c"+index);
columns.add(column);
}
}
private void setDynamicPower(int index,MachineInfoDto machineInfo,PowerIncomeInfoDto powerIncomeInfoDto){
switch (index){
case 1:
machineInfo.setC1(powerIncomeInfoDto.getPower());
break;
case 2:
machineInfo.setC2(powerIncomeInfoDto.getPower());
break;
case 3:
machineInfo.setC3(powerIncomeInfoDto.getPower());
break;
case 4:
machineInfo.setC4(powerIncomeInfoDto.getPower());
break;
case 5:
machineInfo.setC5(powerIncomeInfoDto.getPower());
break;
case 6:
machineInfo.setC6(powerIncomeInfoDto.getPower());
break;
case 7:
machineInfo.setC7(powerIncomeInfoDto.getPower());
break;
case 8:
machineInfo.setC8(powerIncomeInfoDto.getPower());
break;
case 9:
machineInfo.setC9(powerIncomeInfoDto.getPower());
break;
case 10:
machineInfo.setC10(powerIncomeInfoDto.getPower());
break;
}
}
@Override
public PageResult<SellerMachineInfoDto> getShopMachineListForSeller(SellerMachineVo sellerMachineVo) {
LeaseShop leaseShop = getShopById();
PageHelper.startPage(sellerMachineVo.getPageNum(), sellerMachineVo.getPageSize());
List<SellerMachineInfoDto> machineInfoDtoList = leaseMachineMapper.getShopMachineListForSeller(sellerMachineVo,leaseShop.getId());
PageInfo<SellerMachineInfoDto> pageInfo = new PageInfo<>(machineInfoDtoList);
if (machineInfoDtoList.isEmpty()){
return PageResult.fail(new ArrayList<>(), "暂无矿机");
}
PageResult<SellerMachineInfoDto> success = PageResult.success(machineInfoDtoList);
//获取矿机配置信息
List<AsicCoinAndAlgoDto> coinAndAlgoList = leaseMachineMapper.getCoinAndAlgoList(success.getRows());
Map<Long, List<AsicCoinAndAlgoDto>> collect1 = coinAndAlgoList.stream().collect(Collectors.groupingBy(AsicCoinAndAlgoDto::getProductMachineId));
//价格配置信息
List<MachinePayTypeDto> machinePriceList = leaseMachineMapper.getMachinePriceList(success.getRows());
Map<Long, List<MachinePayTypeDto>> collect = machinePriceList.stream().collect(Collectors.groupingBy(MachinePayTypeDto::getProductMachineId));
for (SellerMachineInfoDto row : success.getRows()) {
row.setPriceList(collect.get(row.getId()));
row.setCoinAndAlgoList(collect1.get(row.getId()));
}
success.setTotal(pageInfo.getTotal());
success.setTotalPage(pageInfo.getPages());
PageHelper.clearPage();
return success;
}
/**
* 分页内排序
* @param shopMachineVo 查询条件
* @param machineInfoDtoList 钱包信息
* @return 查询条件
*/
public List<MachineInfoDto> sorted(List<MachineInfoDto> machineInfoDtoList, ShopMachineVo shopMachineVo){
if (shopMachineVo.getPriceSort() != null) {
Comparator<MachineInfoDto> priceComparator = shopMachineVo.getPriceSort() ?
Comparator.comparing(MachineInfoDto::getPrice) :
Comparator.comparing(MachineInfoDto::getPrice).reversed();
return machineInfoDtoList.stream()
.sorted(priceComparator)
.collect(Collectors.toList());
} else if (shopMachineVo.getPowerSort() != null) {
Comparator<MachineInfoDto> powerComparator = shopMachineVo.getPowerSort() ?
Comparator.comparing(MachineInfoDto::getTheoryPower) :
Comparator.comparing(MachineInfoDto::getTheoryPower).reversed();
return machineInfoDtoList.stream()
.sorted(powerComparator)
.collect(Collectors.toList());
}else {
return machineInfoDtoList;
}
}
public ShopMachineVo buildQuery(ShopMachineVo shopMachineVo,PayConfigDto payConfigDto){
if (StringUtils.isEmpty(shopMachineVo.getChain()) && StringUtils.isEmpty(shopMachineVo.getCoin())){
shopMachineVo.setChain(payConfigDto.getPayChain());
shopMachineVo.setCoin(payConfigDto.getPayCoin());
}
//BigDecimal power = PowerUnit.getPower(shopMachineVo.getUnit()).divide(BigDecimal.valueOf(1000 * 1000),2, RoundingMode.HALF_UP);
//shopMachineVo.setMaxPower(shopMachineVo.getMaxPower().multiply(power));
//shopMachineVo.setMinPower(shopMachineVo.getMinPower().multiply(power));
////算力设置
//if (shopMachineVo.getMaxPower().compareTo(shopMachineVo.getMinPower()) < 0){
// shopMachineVo.setMaxPower(shopMachineVo.getMinPower());
// shopMachineVo.setMinPower(shopMachineVo.getMaxPower());
//}
////功耗设置
//if (shopMachineVo.getMaxPowerDissipation().compareTo(shopMachineVo.getMinPowerDissipation()) < 0){
// shopMachineVo.setMaxPowerDissipation(shopMachineVo.getMinPowerDissipation());
// shopMachineVo.setMinPowerDissipation(shopMachineVo.getMaxPowerDissipation());
//}
//价格设置
if (shopMachineVo.getMaxPrice().compareTo(shopMachineVo.getMinPrice()) < 0){
shopMachineVo.setMaxPrice(shopMachineVo.getMinPrice());
shopMachineVo.setMinPrice(shopMachineVo.getMaxPrice());
}
return shopMachineVo;
}
@Override
@Transactional
public Result<String> addAsicMachine(AsicMachineParamsVo asicMachineParamsVo) {
LeaseShop leaseShop = getShopById();
if (leaseShop == null){
return Result.fail("添加ASIC矿机失败,未找到商铺,请重试");
}
//新增lease_machine主表信息
LeaseMachine machine = LeaseMachine.builder()
.hostMac(UuidGeneratorUtil.generateUuidWithoutHyphen())
.shopId(leaseShop.getId())
.type(false)
.maxLeaseDays(asicMachineParamsVo.getMaxLeaseDays())
.saleNumbers(asicMachineParamsVo.getSaleNumbers())
.build();
int machineInsert = leaseMachineMapper.insert(machine);
//新增lease_asic_coin_power_config 配置信息表信息 + lease_machine_price 矿机价格表信息
if (machineInsert > 0){
List<LeaseMachineConfig> collect = asicMachineParamsVo.getCoinAndAlgoList().stream().map(
coinAndAlgo -> LeaseMachineConfig.builder()
.machineId(machine.getId())
.name(asicMachineParamsVo.getName())
.powerDissipation(asicMachineParamsVo.getPowerDissipation())
.coin(coinAndAlgo.getCoin())
.algorithm(coinAndAlgo.getAlgorithm())
.hashrate(coinAndAlgo.getTheoryPower())
.unit(coinAndAlgo.getUnit())
.build()
).collect(Collectors.toList());
List<LeaseMachinePrice> collect1 = asicMachineParamsVo.getPriceList().stream()
.map(machinePayTypeVo ->
LeaseMachinePrice.builder()
.machineId(machine.getId())
.price(machinePayTypeVo.getPrice())
.coin(machinePayTypeVo.getCoin())
.chain(machinePayTypeVo.getChain())
.build()
).collect(Collectors.toList());
boolean b1 = leaseMachinePriceService.saveBatch(collect1);
boolean b = leaseMachineConfigService.saveBatch(collect);
if (!b && !b1){
throw new MachineException("添加asic矿机币价及算力信息失败");
}
return Result.success("添加asic矿机成功");
}
return Result.fail("添加ASIC矿机失败,请重试");
}
@Override
@Transactional
public Result<String> updateAsicMachine(AsicMachineParamsVo asicMachineParamsVo) {
//修改lease_machine主表信息
LeaseMachine machine = LeaseMachine.builder()
.id(asicMachineParamsVo.getId())
.maxLeaseDays(asicMachineParamsVo.getMaxLeaseDays())
.saleNumbers(asicMachineParamsVo.getSaleNumbers())
.state(asicMachineParamsVo.getState())
.build();
int machineUpdate = leaseMachineMapper.updateById(machine);
//修改lease_machine_price 矿机价格表信息 + 修改lease_asic_coin_power_config 配置信息表信息
if (machineUpdate > 0){
List<LeaseMachinePrice> collect = asicMachineParamsVo.getPriceList().stream().map(
machinePayTypeVo ->
LeaseMachinePrice.builder()
.id(machinePayTypeVo.getPayTypeId())
.price(machinePayTypeVo.getPrice())
.build()
).collect(Collectors.toList());
boolean b = leaseMachinePriceService.updateBatchById(collect);
List<LeaseMachineConfig> collect1 = asicMachineParamsVo.getCoinAndAlgoList().stream().map(coinAndAlgo ->
LeaseMachineConfig.builder()
.id(coinAndAlgo.getCoinAndPowerId())
.machineId(machine.getId())
.name(asicMachineParamsVo.getName())
.coin(coinAndAlgo.getCoin())
.hashrate(coinAndAlgo.getTheoryPower())
.algorithm(coinAndAlgo.getAlgorithm())
.unit(coinAndAlgo.getUnit())
.powerDissipation(asicMachineParamsVo.getPowerDissipation())
.build()
).collect(Collectors.toList());
boolean b1 = leaseMachineConfigService.saveOrUpdateBatch(collect1);
if (!b && !b1){
throw new MachineException("修改asic矿机币价及算力信息失败");
}
return Result.success("修改ASIC矿机成功");
}
return Result.fail("修改ASIC矿机失败,请重试");
}
@Override
@Transactional
public Result<String> updateGpuMachine(List<GpuMachineParamsVo> gpuMachineParamsVoList) {
for (GpuMachineParamsVo gpuMachineParamsVo : gpuMachineParamsVoList) {
//修改lease_machine主表信息
LeaseMachine machine = LeaseMachine.builder()
.id(gpuMachineParamsVo.getId())
.maxLeaseDays(gpuMachineParamsVo.getMaxLeaseDays())
.state(gpuMachineParamsVo.getState())
.build();
int machineUpdate = leaseMachineMapper.updateById(machine);
//修改lease_machine_price 矿机价格表信息 + gpu的配置信息是固定的不能修改
if (machineUpdate > 0){
List<MachinePayTypeVo> priceList = gpuMachineParamsVo.getPriceList();
for (MachinePayTypeVo machinePayTypeVo : priceList) {
LeaseMachinePrice build = LeaseMachinePrice.builder()
.id(machinePayTypeVo.getPayTypeId())
.price(machinePayTypeVo.getPrice())
.chain(machinePayTypeVo.getChain())
.coin(machinePayTypeVo.getCoin())
.machineId(machine.getId())
.build();
if (build.getId() == null || build.getId() == 0){
leaseMachinePriceMapper.insert(build);
}else{
leaseMachinePriceMapper.updateById(build);
}
}
}
}
return Result.success("修改GPU矿机成功") ;
}
@Override
@Transactional
public Result<String> updateMachineState(MachineStateVo machineStateVo) {
leaseMachineMapper.updateById(LeaseMachine.builder().id(machineStateVo.getId()).state(machineStateVo.getState()).build());
return machineStateVo.getState() ? Result.success("下架成功") : Result.success("上架成功");
}
@Override
public Result<String> deleteMachine(BaseVo baseVo) {
//TODO 删除之前需要判断是否存在订单
leaseMachineMapper.updateById(LeaseMachine.builder().id(baseVo.getId()).del(true).build());
//删除售价
leaseMachinePriceMapper.update(LeaseMachinePrice.builder().del(true).build(),new LambdaQueryWrapper<LeaseMachinePrice>()
.eq(LeaseMachinePrice::getMachineId,baseVo.getId()));
//ASIC/GPU 矿机删除配置信息
leaseMachineConfigMapper.update(LeaseMachineConfig.builder().del(true).build(),
new LambdaQueryWrapper<LeaseMachineConfig>().eq(LeaseMachineConfig::getMachineId,baseVo.getId()));
return Result.success("删除成功");
}
}

View File

@@ -13,19 +13,25 @@ import com.m2pool.common.security.utils.SecurityUtils;
import com.m2pool.lease.constant.CoinCharge;
import com.m2pool.lease.constant.RedisKey;
import com.m2pool.lease.dto.*;
import com.m2pool.lease.dto.v2.*;
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;
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.util.*;
import java.util.function.Function;
@@ -61,9 +67,6 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
@Resource
private LeasePaymentRecordMapper leasePaymentRecordMapper;
@Resource
private LeaseShoppingCartMapper leaseShoppingCartMapper;
@Resource
private LeaseShoppingCartInfoMapper leaseShoppingCartInfoMapper;
@@ -85,6 +88,8 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
@Resource
private LeaseProductMachinePriceMapper leaseProductMachinePriceMapper;
@Resource
private RedisService redisService;
@@ -168,10 +173,8 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
});
//删除购物车中矿机
LeaseShoppingCart leaseShoppingCart = leaseShoppingCartMapper.selectOne(new LambdaQueryWrapper<LeaseShoppingCart>()
.eq(LeaseShoppingCart::getUserId, SecurityUtils.getUsername()));
int delete = leaseShoppingCartInfoMapper.delete(new LambdaUpdateWrapper<LeaseShoppingCartInfo>()
.eq(LeaseShoppingCartInfo::getCartId, leaseShoppingCart.getId())
.eq(LeaseShoppingCartInfo::getUserId, SecurityUtils.getUsername())
.in(LeaseShoppingCartInfo::getProductMachineId, machineIds));
if (delete < 1){
throw new OrderException("生成订单失败,购物车中不存在矿机商品");
@@ -229,7 +232,6 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
.price(leaseProductMachinePrice.getPrice())
.user(leaseProductMachine.getUser())
.miner(leaseProductMachine.getMiner())
//TODO 这里的理论收益是商家新增机器时通过实时算力计算得来
.theoryIncome(leaseProductMachine.getTheoryIncome().multiply(BigDecimal.valueOf(orderInfoVo.getLeaseTime())))
.coin(leaseProduct.getCoin())
.leaseTime(orderInfoVo.getLeaseTime())
@@ -500,6 +502,7 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
.name(leaseOrderItem.getName())
.price(leaseOrderItem.getPrice())
.image(leaseOrderItem.getImage())
.numbers(leaseOrderItem.getNumbers())
.build());
// 累加支付金额
hasPayAmountMap.merge(leaseOrderItem.getOrderId(), leaseOrderItem.getAlreadyPayAmount(), BigDecimal::add);
@@ -513,7 +516,6 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
BigDecimal hasPayAmount = hasPayAmountMap.get(orderInfoDto.getId());
BigDecimal hasPayRealAmount = hasPayRealAmountMap.get(orderInfoDto.getId());
orderInfoDto.setOrderItemDtoList(orderItems);
//orderInfoDto.setShopId(orderItems.get(0).getShopId());
orderInfoDto.setNoPayAmount(orderInfoDto.getTotalPrice().subtract(hasPayAmount).doubleValue());
orderInfoDto.setPayAmount(hasPayRealAmount.doubleValue()+"/"+hasPayAmount.doubleValue());
});
@@ -568,15 +570,12 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
return Result.fail("取消订单失败");
}
@Resource
private RedisService redisService;
@Override
public Result<BigDecimal> getCoinPrice(CoinVo coinVo) {
BigDecimal coinPrice = getCoinPrice(coinVo.getCoin());
return Result.success(coinPrice);
}
public BigDecimal getCoinPrice(String coin) {
coin = coin.toLowerCase();
String priceKey = RedisKey.getPiceKey(coin);
@@ -594,4 +593,360 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
}
return price;
}
@Resource
private LeaseMachinePriceMapper leaseMachinePriceMapper;
@Resource
private LeaseMachineMapper leaseMachineMapper;
@Resource
private LeaseMachineConfigMapper leaseMachineConfigMapper;
@Override
public Result<List<CoinAndAlgorithmListDto>> getMachineSupportCoinAndAlgorithm(List<BaseVo> machineIds) {
//lease_pool_coin_config 中获取本系统支持的币种和算法
List<CoinAndAlgorithmDto> machineSupportCoinAndAlgorithm = leaseMachineConfigMapper.getMachineSupportCoinAndAlgorithm(machineIds);
// 按币种分组
Map<String, List<CoinAndAlgorithmDto>> coinGroup = machineSupportCoinAndAlgorithm.stream()
.collect(Collectors.groupingBy(CoinAndAlgorithmDto::getCoin));
List<CoinAndAlgorithmListDto> result = new ArrayList<>();
// 遍历每个币种
coinGroup.forEach((coin, dtos) -> {
CoinAndAlgorithmListDto item = new CoinAndAlgorithmListDto();
item.setCoin(coin);
// 转换算法列表
List<CoinAndAlgorithmListDto.AlgorithmListDto> algorithmList = dtos.stream()
.map(dto -> {
CoinAndAlgorithmListDto.AlgorithmListDto algoDto = new CoinAndAlgorithmListDto.AlgorithmListDto();
algoDto.setAlgorithm(dto.getAlgorithm());
// 转换机器ID列表
List<Long> ids = Arrays.stream(dto.getMachineIds().split(","))
.map(Long::valueOf)
.collect(Collectors.toList());
algoDto.setMachineIds(ids);
return algoDto;
})
.collect(Collectors.toList());
item.setAlgorithmList(algorithmList);
result.add(item);
});
return Result.success(result);
}
@Override
public Result<List<MiningInfoDto>> getMachineSupportPool(CoinAndAlgorithmVo coinAndAlgorithmVo) {
List<MiningConfigSelectDto> coinConfigList = leaseMachineConfigMapper.getConfigList(coinAndAlgorithmVo.getCoin(), coinAndAlgorithmVo.getAlgorithm());
// 将查询结果按poolId分组并转换为MiningInfoDto列表
List<MiningInfoDto> miningInfoList = coinConfigList.stream()
.collect(Collectors.groupingBy(MiningConfigSelectDto::getPoolId))
.entrySet().stream()
.map(entry -> {
MiningConfigSelectDto firstConfig = entry.getValue().get(0);
List<MiningCoinInfoDto> coinInfoList = entry.getValue().stream()
.map(config -> MiningCoinInfoDto.builder()
.coinConfigId(config.getCoinConfigId())
.coin(config.getCoin())
.algorithm(config.getAlgorithm())
.miningTcpGpuUrl(config.getMiningTcpGpuUrl())
.modelName(config.getModelName())
.modelFee(config.getModelFee())
.walletMining(config.getWalletMining())
.build())
.collect(Collectors.toList());
return MiningInfoDto.builder()
.poolId(entry.getKey())
.poolName(firstConfig.getPoolName())
.miningCoinInfoList(coinInfoList)
.build();
})
.collect(Collectors.toList());
return Result.success(miningInfoList);
}
@Override
@DSTransactional
public Result<String> addOrdersV2(OrderAndCodeVo orderAndCodeVo) {
String userEmail = SecurityUtils.getUsername();
List<OrderInfoVo> orderInfoVoList = orderAndCodeVo.getOrderInfoVoList();
GoogleInfo googleInfo = leaseUserMapper.getGoogleInfoByEmail(userEmail);
//TODO 开发环境
//if(googleInfo == null || StringUtils.isBlank(googleInfo.getSecret())){
// //未绑定定谷歌验证器
// return Result.fail("您的账号未开启双重验证,请先开启验证!");
//}
//if(!GoogleAuthenticator.checkCode(googleInfo.getSecret(), orderAndCodeVo.getCode(), System.currentTimeMillis())){
// return Result.fail("谷歌验证码错误");
//}
Map<Long, OrderInfoVo> collect1 = orderInfoVoList.stream()
.collect(Collectors.toMap(OrderInfoVo::getMachineId,Function.identity()));
List<LeaseMachine> machineList = leaseMachineMapper.selectBatchIds(collect1.keySet());
Map<Boolean, List<LeaseMachine>> machineTypeMap = machineList.stream().collect(Collectors.groupingBy(LeaseMachine::getType));
List<LeaseMachine> gpuMachines = machineTypeMap.get(true) == null ? new ArrayList<>() : machineTypeMap.get(true);
List<LeaseMachine> asicMachines = machineTypeMap.get(false) == null ? new ArrayList<>() : machineTypeMap.get(false);
//校验asic 可售数量是否充足 + gpu 机器对应客户端是否在线
boolean isAsicPass = checkAsicMachine(asicMachines,collect1);
if (!isAsicPass){
return Result.fail("购买的ASIC商品中,存在可售数量不足的商品!");
}
boolean isGpuPass = checkGpuMachine(gpuMachines);
if (!isGpuPass){
return Result.fail("购买的GPU商品中,存在客户端不在线的矿机!");
}
//存储相同链和币种的map集合
Map<String, Map<String, List<OrderInfoVo>>> chainAndCoinMap = new HashMap<>();
Map<Long, LeaseMachinePrice> orderTotalPriceGroupByChainAndCoin = leaseMachinePriceMapper.getOrderTotalPriceGroupByChainAndCoin(orderInfoVoList);
//chain + coin 钱包 对应的总价
Map<String, BigDecimal> totalPriceMap = new HashMap<>();
//存储根据chain和coin去重后的set集合
Set<ChainAndCoinDto> chainAndCoinSet = new HashSet<>();
Set<ChainAndCoinDto> chainAndCoinAndShopIdSet = new HashSet<>();
//其他
Map<Long, OrderInfoVo> machineMap = new HashMap<>();
LocalDateTime now = LocalDateTime.now();
Long startTime = System.currentTimeMillis();
List<Long> machineIds = new ArrayList<>();
Set<Long> shopIds = new HashSet<>();
for (OrderInfoVo vo : orderInfoVoList) {
String chain = vo.getChain();
String coin = vo.getCoin();
Long shopId = vo.getShopId();
String key = chain +"-"+ coin+"-"+shopId;
chainAndCoinSet.add(new ChainAndCoinDto(coin, chain));
chainAndCoinAndShopIdSet.add(new ChainAndCoinDto(coin, chain,vo.getShopId()));
chainAndCoinMap.computeIfAbsent(chain, k -> new HashMap<>());
Map<String, List<OrderInfoVo>> innerMap = chainAndCoinMap.get(chain);
innerMap.computeIfAbsent(coin, k -> new ArrayList<>()).add(vo);
// 获取对应买方本次链和币对应需要支付的总金额
totalPriceMap.compute(key, (k, v) -> {
if (v == null) {
v = BigDecimal.ZERO;
}
LeaseMachinePrice priceInfo = orderTotalPriceGroupByChainAndCoin.get(vo.getMachineId());
if (priceInfo != null) {
v = v.add(priceInfo.getPrice().multiply(BigDecimal.valueOf(vo.getLeaseTime())));
}
return v;
});
machineMap.put(vo.getMachineId(), vo);
machineIds.add(vo.getMachineId());
shopIds.add(vo.getShopId());
}
//获取买家需要进行支付的几个钱包
List<LeaseUserWalletData> walletDataList = leaseUserWalletDataMapper.selectWalletByChainAndCoinAndUsername(chainAndCoinSet, userEmail);
if (walletDataList.size() != chainAndCoinSet.size()){
return Result.fail("下单失败!订单选择的支付方式中对应钱包您还未绑定并充值!");
}
Map<String, LeaseUserWalletData> fromAddressMap = walletDataList.stream().collect(Collectors.toMap(item -> item.getFromChain() + "-" + item.getFromSymbol(), Function.identity()));
//封装订单主表信息
List<LeaseOrderInfo> leaseOrderInfoList = new ArrayList<>();
totalPriceMap.forEach((key, totalPrice)->{
String[] split = key.split("-");
String coin = split[1];
String chain = split[0];
BigDecimal fee = CoinCharge.getFee(chain, coin, totalPrice);
leaseOrderInfoList.add(LeaseOrderInfo.builder()
.chainAndCoinAndShopIdKey(key)
.chainAndCoinKey(chain+"-"+coin)
.orderNumber(UuidGeneratorUtil.generateUuidWithoutHyphen())
.userId(userEmail)
.fee(fee)
.totalPrice(totalPrice)
.createTime(now)
.build());
});
//删除购物车中矿机
int delete = leaseShoppingCartInfoMapper.delete(new LambdaUpdateWrapper<LeaseShoppingCartInfo>()
.eq(LeaseShoppingCartInfo::getUserId, SecurityUtils.getUsername())
.eq(LeaseShoppingCartInfo::getVersion, 1)
.in(LeaseShoppingCartInfo::getProductMachineId, machineIds));
if (delete < 1){
throw new OrderException("生成订单失败,购物车中不存在矿机商品");
}
//查找矿机详情信息
List<LeaseMachine> machinesList = leaseMachineMapper.selectBatchIds(machineIds);
List<LeaseMachine> collect = machinesList.stream().filter(leaseMachine -> leaseMachine.getState() || leaseMachine.getDel()).collect(Collectors.toList());
if (!collect.isEmpty()){
throw new OrderException("生成订单失败,订单矿机中存在已下架矿机");
}
// 获取卖家店铺钱包配置
List<LeaseShopConfig> payAddressAndPayCoin = leaseShopMapper.getPayAddressAndPayCoin(chainAndCoinAndShopIdSet);
Map<String, LeaseShopConfig> configMap = new HashMap<>();
Map<String, Set<String>> feeMap = new HashMap<>();
for (LeaseShopConfig leaseShopConfig : payAddressAndPayCoin) {
String key = leaseShopConfig.getChain() + "-" + leaseShopConfig.getPayCoin() +"-"+leaseShopConfig.getShopId();
configMap.put(key, leaseShopConfig);
feeMap.computeIfAbsent(leaseShopConfig.getChain() + "-" + leaseShopConfig.getPayCoin(), k -> new HashSet<>()).add(leaseShopConfig.getPayAddress());
}
boolean save = saveBatch(leaseOrderInfoList);
if (!save){
return Result.fail("生成订单失败");
}
Map<String, LeaseOrderInfo> orderInfoMap = leaseOrderInfoList.stream()
.collect(Collectors.toMap(LeaseOrderInfo::getChainAndCoinAndShopIdKey, Function.identity()));
//订单详情表业务
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<>();
for (LeaseMachine leaseMachine : machinesList) {
Long machineId = leaseMachine.getId();
OrderInfoVo orderInfoVo = machineMap.get(machineId);
String chain = orderInfoVo.getChain();
String coin = orderInfoVo.getCoin();
Long shopId = orderInfoVo.getShopId();
String key = chain + "-" + coin + "-" + shopId;
LeaseMachinePrice leaseMachinePrice = orderTotalPriceGroupByChainAndCoin.get(machineId);
LeaseShopConfig leaseShopConfig = configMap.get(key);
LeaseUserWalletData walletInfo = fromAddressMap.get(chain + "-" + coin);
if (leaseShopConfig == null){
throw new OrderException("选择的卖家收款钱包不存在,请重行选择支付方法");
}
LeaseOrderInfo leaseOrderInfo = orderInfoMap.get(key);
//设置销售数量
LeaseShop leaseShop = shopMap.get(leaseMachine.getShopId());
leaseShop.setSaleNumber(leaseShop.getSaleNumber() + orderInfoVo.getNumbers());
leaseOrderItems.add( LeaseOrderItem.builder()
.userId(userEmail)
.orderId(leaseOrderInfo.getId())
.productMachineId(machineId)
.price(leaseMachinePrice.getPrice())
.user(leaseMachine.getUser())
.miner(leaseMachine.getMiner())
//.theoryIncome(leaseMachine.getTheoryIncome().multiply(BigDecimal.valueOf(orderInfoVo.getLeaseTime())))
.leaseTime(orderInfoVo.getLeaseTime())
.payCoin(leaseShopConfig.getPayCoin())
.address(leaseShopConfig.getPayAddress())
.name(leaseMachine.getHostMac())
.chain(leaseShopConfig.getChain())
.symbol(leaseShopConfig.getPayCoin())
.shopId(leaseMachine.getShopId())
.fromAddress(walletInfo.getFromAddress())
.fromChain(walletInfo.getFromChain())
.fromSymbol(walletInfo.getFromSymbol())
.numbers(orderInfoVo.getNumbers())
.build());
Long timestamp =startTime + orderInfoVo.getLeaseTime() * orderInfoVo.getNumbers() * 24 * 60 * 60 * 1000L;
endMiningMap.put(machineId,timestamp);
}
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("订单生成成功");
}
throw new OrderException("订单生成失败");
}
/**
* 检测 gpu客户端是否在线
* @param gpuMachines 购买的GPU矿机信息
*/
private boolean checkGpuMachine(List<LeaseMachine> gpuMachines){
for (LeaseMachine gpuMachine : gpuMachines) {
if (ChannelManager.getChannelByMac(gpuMachine.getHostMac()) == null){
return false;
}
}
return true;
}
/**
* 校验 ASIC 矿机可售数量是否充足
* @param asicMachines 购买的ASIC矿机信息
*/
private boolean checkAsicMachine(List<LeaseMachine> asicMachines,Map<Long, OrderInfoVo> numbers){
for (LeaseMachine leaseMachine : asicMachines) {
Integer number = numbers.get(leaseMachine.getId()).getNumbers();
Integer lockNumbers = leaseMachine.getSaleOutNumbers();
leaseMachine.setLockNumbers(lockNumbers);
leaseMachine.setSaleOutNumbers(lockNumbers + number);
if (leaseMachine.getSaleNumbers() - leaseMachine.getSaleOutNumbers() < number){
return false;
}
}
return true;
}
/**
* GPU矿机的通过mac发送给相应的客户端
*/
public void sendGpuTcpMessageToClient(List<LeaseMachine> gpuMachines, List<OrderMiningInfoDto> orderMiningInfo,Map<Long,Long> endMiningMap){
//TODO 不添加
//if(!checkGpuMachine(gpuMachines)){
// throw new OrderException("购买的GPU商品中,存在客户端不在线的矿机!");
//}
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()));
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());
}
}
}
//TODO ASIC 不是发送给客户端是通过其他方式不过发送消息的结构和gpu相同
@Override
public PageResult<OrderInfoDto> getOrdersByStatusV2(OrderInfoStateVo orderInfoStateVo) {
return null;
}
@Override
public PageResult<OrderInfoDto> getOrdersByStatusForSellerV2(OrderInfoStateVo orderInfoStateVo) {
return null;
}
@Override
public Result<OrderInfoDto> getOrdersByIdsV2(OrderVo orderVo) {
return null;
}
@Override
public Result<BigDecimal> getCoinPriceV2(CoinVo coinVo) {
return null;
}
}

View File

@@ -3,27 +3,40 @@ package com.m2pool.lease.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.StringUtils;
import com.m2pool.common.security.utils.SecurityUtils;
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.PaymentException;
import com.m2pool.lease.mapper.*;
import com.m2pool.lease.mq.message.RabbitmqPayWithdrawMessage;
import com.m2pool.lease.service.LeaseShopConfigService;
import com.m2pool.lease.service.LeaseShopService;
import com.m2pool.lease.utils.HashUtils;
import com.m2pool.lease.utils.QrCodeUtils;
import com.m2pool.lease.utils.UuidGeneratorUtil;
import com.m2pool.lease.utils.WalletRuleCheckUtils;
import com.m2pool.lease.vo.BaseVo;
import com.m2pool.lease.vo.ShopConfigVo;
import com.m2pool.lease.vo.ShopVo;
import com.m2pool.lease.vo.*;
import com.m2pool.lease.vo.v2.ShopWalletInfoVo;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static com.m2pool.lease.constant.CoinCharge.getChargeByChainAndCoin;
import static com.m2pool.lease.constant.RabbitmqConstant.PAY_WITHDRAW_QUEUE;
/**
* <p>
* 店铺表 服务实现类
@@ -56,6 +69,18 @@ public class LeaseShopServiceImpl extends ServiceImpl<LeaseShopMapper, LeaseShop
@Resource
private LeaseProductMachinePriceMapper leaseProductMachinePriceMapper;
@Resource
private LeaseUserMapper leaseUserMapper;
@Resource
private LeasePayWithdrawMessageMapper leasePayWithdrawMessageMapper;
@Resource
private RabbitTemplate rabbitTemplate;
@Resource
private LeaseAutoAddressMapper leaseAutoAddressMapper;
@Override
public Result<String> addShop(ShopVo shopVo) {
LeaseShop leaseShop1 = leaseShopMapper.selectOne(new LambdaQueryWrapper<LeaseShop>()
@@ -67,6 +92,7 @@ public class LeaseShopServiceImpl extends ServiceImpl<LeaseShopMapper, LeaseShop
.image(shopVo.getImage())
.description(shopVo.getDescription())
.state(shopVo.getState())
.feeRate(shopVo.getFeeRate())
.build();
if(leaseShop1 != null){
@@ -97,6 +123,7 @@ public class LeaseShopServiceImpl extends ServiceImpl<LeaseShopMapper, LeaseShop
.image(shopVo.getImage())
.description(shopVo.getDescription())
.state(shopVo.getState())
.feeRate(shopVo.getFeeRate())
.build();
LeaseShop byId = getById(shopVo.getId());
if (byId.getDel()){
@@ -142,6 +169,7 @@ public class LeaseShopServiceImpl extends ServiceImpl<LeaseShopMapper, LeaseShop
.id(leaseShop.getId())
.name(leaseShop.getName())
.image(leaseShop.getImage())
.feeRate(leaseShop.getFeeRate())
.description(leaseShop.getDescription())
.state(leaseShop.getState())
.del(leaseShop.getDel())
@@ -160,6 +188,7 @@ public class LeaseShopServiceImpl extends ServiceImpl<LeaseShopMapper, LeaseShop
.image(leaseShop.getImage())
.description(leaseShop.getDescription())
.state(leaseShop.getState())
.feeRate(leaseShop.getFeeRate())
.del(leaseShop.getDel())
.build());
}
@@ -239,9 +268,13 @@ public class LeaseShopServiceImpl extends ServiceImpl<LeaseShopMapper, LeaseShop
return Result.success(shopConfigDtos);
}
@Override
public Result<String> addShopConfig(ShopConfigVo shopConfigVo) {
if (checkSystemctlAddress(shopConfigVo.getPayAddress(), shopConfigVo.getChain())){
return Result.fail("收款钱包不能使用系统钱包");
}
//钱包地址校验
if (!WalletRuleCheckUtils.checkAddress(shopConfigVo.getChain(),shopConfigVo.getPayAddress())){
return Result.fail("钱包地址格式错误");
@@ -282,6 +315,9 @@ public class LeaseShopServiceImpl extends ServiceImpl<LeaseShopMapper, LeaseShop
@Override
public Result<String> updateShopConfig(ShopConfigVo shopConfigVo) {
if (checkSystemctlAddress(shopConfigVo.getPayAddress(), shopConfigVo.getChain())){
return Result.fail("收款钱包不能使用系统钱包");
}
if ( !WalletRuleCheckUtils.checkAddress(shopConfigVo.getChain(),shopConfigVo.getPayAddress())){
return Result.fail("提现地址格式不符合"+shopConfigVo.getChain()+"节点");
}
@@ -364,6 +400,22 @@ public class LeaseShopServiceImpl extends ServiceImpl<LeaseShopMapper, LeaseShop
return Result.fail("修改失败");
}
/**
* 校验卖家收款钱包是否为本系统钱包
* @param address
* @param chain
* @return
*/
public boolean checkSystemctlAddress(String address, String chain){
LeaseAutoAddress leaseAutoAddress = leaseAutoAddressMapper.selectOne(new LambdaQueryWrapper<LeaseAutoAddress>()
.eq(LeaseAutoAddress::getAddress, address)
.eq(LeaseAutoAddress::getFromChain, chain));
if (leaseAutoAddress != null){
return true;
}
return false;
}
@Override
public Result<String> deleteShopConfig(BaseVo baseVo) {
LeaseShopConfig config = leaseShopConfigMapper.selectById(baseVo.getId());
@@ -490,5 +542,125 @@ public class LeaseShopServiceImpl extends ServiceImpl<LeaseShopMapper, LeaseShop
return result;
}
@Override
public Result<List<ShopWalletInfoDto>> getShopConfigV2(BaseVo baseVo) {
List<ShopWalletInfoDto> list = leaseShopConfigMapper.getShopWalletInfoList(baseVo.getId());
for (ShopWalletInfoDto shopWalletInfoDto : list) {
BigDecimal chargeByChainAndCoin = getChargeByChainAndCoin(shopWalletInfoDto.getChain(), shopWalletInfoDto.getPayCoin());
shopWalletInfoDto.setServiceCharge(chargeByChainAndCoin);
}
return Result.success(list);
}
@Override
public Result<String> withdrawBalanceForSeller(BalanceVo balanceVo) {
//校验地址
if (StringUtils.isEmpty(balanceVo.getToAddress()) || StringUtils.isEmpty(balanceVo.getToChain()) || StringUtils.isEmpty(balanceVo.getToSymbol()) ){
return Result.fail("提现地址,链,币种 不能为空");
}
if (!WalletRuleCheckUtils.checkAddress(balanceVo.getToChain(),balanceVo.getToAddress())){
return Result.fail("提现收款地址格式不正确");
}
LeaseShop leaseShop = leaseShopMapper.selectOne(new LambdaQueryWrapper<LeaseShop>()
.select(LeaseShop::getId).eq(LeaseShop::getUserEmail, SecurityUtils.getUsername()).eq(LeaseShop::getDel, false));
ShopWalletInfoDto shopWalletInfo = leaseShopConfigMapper.getShopWalletInfo(leaseShop.getId(), balanceVo.getFromAddress(), balanceVo.getToChain(), balanceVo.getToSymbol());
if (StringUtils.isNull(shopWalletInfo) || StringUtils.isEmpty(shopWalletInfo.getPayAddress())){
return Result.fail("提现地址不属于您,请登录正确的账号");
}
GoogleInfo googleInfo = leaseUserMapper.getGoogleInfoByEmail(SecurityUtils.getUsername());
//开发环境
//if(googleInfo == null || StringUtils.isBlank(googleInfo.getSecret())){
// //未绑定定谷歌验证器
// return Result.fail("您的账号未开启双重验证,请先开启验证!");
//}
//if(!GoogleAuthenticator.checkCode(googleInfo.getSecret(), balanceVo.getCode(), System.currentTimeMillis())){
// return Result.fail("谷歌验证码错误");
//}
//同一个from只能存在一笔提现中的订单
Integer recentlyOneData = leasePayWithdrawMessageMapper.getRecentlyOneData(balanceVo.getFromAddress(), balanceVo.getToChain());
if (recentlyOneData != null && recentlyOneData == 2){
return Result.fail("提现申请失败,已存在一笔金额正在提现中...");
}
BigDecimal serviceCharge = getChargeByChainAndCoin(balanceVo.getToChain(), balanceVo.getToSymbol());
String queueId = UuidGeneratorUtil.generateUuidWithoutHyphen();
Long timestamp = System.currentTimeMillis()/1000;
//验证通过存储提现记录
int insert = leasePayWithdrawMessageMapper.insert(LeasePayWithdrawMessage.builder()
.queueId(queueId)
.shopId(leaseShop.getId())
.toChain(balanceVo.getToChain())
.toSymbol(balanceVo.getToSymbol())
.fromChain(shopWalletInfo.getChain())
.fromSymbol(shopWalletInfo.getPayCoin())
.fromAddress(shopWalletInfo.getPayAddress())
.serviceCharge(serviceCharge)
.toAddress(balanceVo.getToAddress())
.amount(balanceVo.getAmount())
.build());
LeaseShopConfig build = LeaseShopConfig.builder()
.id(shopWalletInfo.getId())
.balance(shopWalletInfo.getBalance().subtract(balanceVo.getAmount()))
.build();
int update = leaseShopConfigMapper.update(build,new LambdaUpdateWrapper<LeaseShopConfig>()
.eq(LeaseShopConfig::getBalance, shopWalletInfo.getBalance()));
if (insert > 0 && update > 0){
//发送mq消息到队列
RabbitmqPayWithdrawMessage message = RabbitmqPayWithdrawMessage.builder()
.queue_id(queueId)
.from_address(shopWalletInfo.getPayAddress())
.to_address(balanceVo.getToAddress())
.amount(balanceVo.getAmount().subtract(serviceCharge))
.chain(balanceVo.getToChain())
.symbol(balanceVo.getToSymbol())
.fee(serviceCharge)
.timestamp(timestamp)
.sign(HashUtils.sha256(timestamp))
.build();
try {
//发送提现消息
rabbitTemplate.convertAndSend(PAY_WITHDRAW_QUEUE,message);
return Result.success("提现申请成功");
} catch (Exception e) {
throw new PaymentException(("提现失败,失败原因"+e.getMessage()));
}
}
throw new PaymentException("提现失败,余额已变化,请重新提现!");
}
@Override
public Result<String> updateShopConfigV2(ShopWalletInfoVo shopWalletInfoVo) {
if ( !WalletRuleCheckUtils.checkAddress(shopWalletInfoVo.getChain(),shopWalletInfoVo.getPayAddress())){
return Result.fail("提现地址格式不符合"+shopWalletInfoVo.getChain()+"节点");
}
//LeaseShopConfig byId = leaseShopConfigService.getById(shopWalletInfoVo.getId());
//if (byId.getBalance().compareTo(BigDecimal.ZERO) > 0){
// return Result.fail("钱包地址余额不为0,请先提现,否则会造成财产丢失");
//}
boolean b = leaseShopConfigService.updateById(LeaseShopConfig.builder()
.id(shopWalletInfoVo.getId())
.payAddress(shopWalletInfoVo.getPayAddress())
.build());
if (b){
return Result.success("修改成功");
}
return Result.fail("修改失败");
}
@Override
public PageResult<PayWithdrawSellerRecordDto> balanceWithdrawList(PageVo pageVo) {
String userEmail = SecurityUtils.getUsername();
LeaseShop leaseShop = leaseShopMapper.selectOne(new LambdaQueryWrapper<LeaseShop>().eq(LeaseShop::getUserEmail, userEmail));
PageHelper.startPage(pageVo.getPageNum(), pageVo.getPageSize());
List<PayWithdrawSellerRecordDto> payWithdrawMessageDtoList = leasePayWithdrawMessageMapper.getSellerWithdrawRecord(leaseShop.getId());
PageInfo<PayWithdrawSellerRecordDto> pageInfo = new PageInfo<>(payWithdrawMessageDtoList);
PageResult<PayWithdrawSellerRecordDto> success = PageResult.success(payWithdrawMessageDtoList);
success.setTotal(pageInfo.getTotal());
success.setTotalPage(pageInfo.getPages());
PageHelper.clearPage();
return success;
}
}

View File

@@ -10,9 +10,10 @@ import com.m2pool.common.redis.service.RedisService;
import com.m2pool.common.security.utils.SecurityUtils;
import com.m2pool.lease.constant.BlockInterval;
import com.m2pool.lease.constant.CoinCharge;
import com.m2pool.lease.constant.PowerUnit;
import com.m2pool.lease.constant.RedisKey;
import com.m2pool.lease.dto.*;
import com.m2pool.lease.dto.v2.CartMachineInfoDto;
import com.m2pool.lease.dto.v2.ShopCartV2Dto;
import com.m2pool.lease.entity.*;
import com.m2pool.lease.mapper.*;
import com.m2pool.lease.service.LeaseShoppingCartInfoService;
@@ -22,23 +23,20 @@ import com.m2pool.lease.vo.BaseVo;
import com.m2pool.lease.vo.PageVo;
import com.m2pool.lease.vo.ProductAndMachineVo;
import com.m2pool.lease.vo.ShoppingCartInfoURDVo;
import com.m2pool.lease.vo.v2.AddGoodsVo;
import com.m2pool.lease.vo.v2.CartInfoVo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Collectors;
import static com.m2pool.lease.constant.PowerUnit.MH_UNIT;
/**
* <p>
* 购物车表 服务实现类
@@ -50,8 +48,6 @@ import static com.m2pool.lease.constant.PowerUnit.MH_UNIT;
@Service
public class LeaseShoppingCartServiceImpl extends ServiceImpl<LeaseShoppingCartMapper, LeaseShoppingCart> implements LeaseShoppingCartService {
@Resource
private LeaseShoppingCartMapper leaseShoppingCartMapper;
@Resource
private LeaseShoppingCartInfoMapper leaseShoppingCartInfoMapper;
@@ -70,68 +66,40 @@ public class LeaseShoppingCartServiceImpl extends ServiceImpl<LeaseShoppingCartM
@Resource
private RedisService redisService;
@Resource
private LeaseMachineMapper leaseMachineMapper;
@Resource
private LeaseMachinePriceMapper leaseMachinePriceMapper;
@Override
@Transactional
public Result<String> addGoods(List<ShoppingCartInfoURDVo> shoppingCartInfoURDVoList) {
//String userId = SecurityUtils.getUsername();
//if (shoppingCartInfoURDVoList.isEmpty()){
// return Result.fail("请选择商品");
//}
//LeaseShoppingCart leaseShoppingCart = leaseShoppingCartMapper
// .selectOne(new LambdaQueryWrapper<LeaseShoppingCart>().eq(LeaseShoppingCart::getUserId, userId));
//if (leaseShoppingCart == null){
// leaseShoppingCart = LeaseShoppingCart.builder().userId(userId).build();
// int insert = leaseShoppingCartMapper.insert(leaseShoppingCart);
// if (insert <= 0){
// return Result.fail("添加购物车失败");
// }
//}
//Long cartId = leaseShoppingCart.getId();
//List<Long> productMachineIds = shoppingCartInfoURDVoList.stream().map(ShoppingCartInfoURDVo::getProductMachineId).collect(Collectors.toList());
//List<LeaseShoppingCartInfo> leaseShoppingCartInfos = leaseShoppingCartInfoMapper.selectList(new LambdaQueryWrapper<LeaseShoppingCartInfo>()
// .eq(LeaseShoppingCartInfo::getCartId, cartId).in(LeaseShoppingCartInfo::getProductMachineId, productMachineIds));
//
//if (!leaseShoppingCartInfos.isEmpty()){
// return Result.fail("购物车中已存在该机器,不能重复添加");
//}
//List<LeaseShoppingCartInfo> collect = shoppingCartInfoURDVoList.stream().map(shoppingCartInfoURDVo ->
// LeaseShoppingCartInfo.builder()
// .leaseTime(shoppingCartInfoURDVo.getLeaseTime())
// .cartId(cartId)
// .productId(shoppingCartInfoURDVo.getProductId())
// .productMachineId(shoppingCartInfoURDVo.getProductMachineId())
// .build()
//).collect(Collectors.toList());
//boolean b = leaseShoppingCartInfoService.saveBatch(collect);
//if (b){
// return Result.success("添加购物车成功");
//}
//return Result.fail("添加购物车失败");
String userId = SecurityUtils.getUsername();
if (shoppingCartInfoURDVoList.isEmpty()) {
return Result.fail("请选择商品");
}
LeaseShoppingCart leaseShoppingCart = leaseShoppingCartMapper
.selectOne(new LambdaQueryWrapper<LeaseShoppingCart>().eq(LeaseShoppingCart::getUserId, userId));
if (leaseShoppingCart == null) {
leaseShoppingCart = LeaseShoppingCart.builder().userId(userId).build();
int insert = leaseShoppingCartMapper.insert(leaseShoppingCart);
if (insert <= 0) {
return Result.fail("添加购物车失败");
}
}
Long cartId = leaseShoppingCart.getId();
List<Long> productMachineIds = shoppingCartInfoURDVoList.stream().map(ShoppingCartInfoURDVo::getProductMachineId).collect(Collectors.toList());
List<LeaseShoppingCartInfo> leaseShoppingCartInfos = leaseShoppingCartInfoMapper.selectList(new LambdaQueryWrapper<LeaseShoppingCartInfo>()
.eq(LeaseShoppingCartInfo::getCartId, cartId).in(LeaseShoppingCartInfo::getProductMachineId, productMachineIds));
List<Long> machineIds = shoppingCartInfoURDVoList.stream().map(ShoppingCartInfoURDVo::getProductMachineId).collect(Collectors.toList());
List<LeaseProductMachine> leaseProductMachines = leaseProductMachineMapper.selectList(new LambdaQueryWrapper<LeaseProductMachine>()
.select(LeaseProductMachine::getId, LeaseProductMachine::getShopId)
.in(LeaseProductMachine::getId, machineIds));
Map<Long, Long> collect = leaseProductMachines.stream().collect(Collectors.toMap(LeaseProductMachine::getId, LeaseProductMachine::getShopId));
//获取用户购物车中所有矿机
List<LeaseShoppingCartInfo> leaseShoppingCartInfos = leaseShoppingCartInfoMapper
.selectList(new LambdaQueryWrapper<LeaseShoppingCartInfo>()
.eq(LeaseShoppingCartInfo::getUserId, userId));
// 存储需要新增的记录
List<LeaseShoppingCartInfo> newRecords = shoppingCartInfoURDVoList.stream()
.filter(vo -> leaseShoppingCartInfos.stream().noneMatch(info -> info.getProductMachineId().equals(vo.getProductMachineId())))
.filter(vo -> leaseShoppingCartInfos.stream()
.noneMatch(info -> info.getProductMachineId().equals(vo.getProductMachineId())))
.map(shoppingCartInfoURDVo ->
LeaseShoppingCartInfo.builder()
.userId(userId)
.shopId(collect.get(shoppingCartInfoURDVo.getProductMachineId()))
.leaseTime(shoppingCartInfoURDVo.getLeaseTime())
.cartId(cartId)
.productId(shoppingCartInfoURDVo.getProductId())
.productMachineId(shoppingCartInfoURDVo.getProductMachineId())
.build()
@@ -175,28 +143,20 @@ public class LeaseShoppingCartServiceImpl extends ServiceImpl<LeaseShoppingCartM
public PageResult<ShopCartDto> getGoodsList(PageVo pageVo) {
String userId = SecurityUtils.getUsername();
//检查购物车中是否存在商品
LeaseShoppingCart leaseShoppingCart = leaseShoppingCartMapper.selectOne(new LambdaQueryWrapper<LeaseShoppingCart>()
.eq(LeaseShoppingCart::getUserId, userId));
if (leaseShoppingCart == null){
return PageResult.success(new ArrayList<>());
}
List<ShoppingCartInfoDto> cartInfoList = leaseShoppingCartInfoMapper.getProductAndMachineIds(leaseShoppingCart.getId());
List<ShoppingCartInfoDto> cartInfoList = leaseShoppingCartInfoMapper.getProductAndMachineIds(userId,0);
if (cartInfoList.isEmpty()){
return PageResult.success(new ArrayList<>());
}
List<Long> productIds = new ArrayList<>();
List<Long> machineIds = new ArrayList<>();
Map<Long, Integer> machineMap = new HashMap<>();
for (ShoppingCartInfoDto shoppingCartInfoDto : cartInfoList) {
productIds.add(shoppingCartInfoDto.getProductId());
machineIds.add(shoppingCartInfoDto.getProductMachineId());
machineMap.put(shoppingCartInfoDto.getProductMachineId(), shoppingCartInfoDto.getLeaseTime());
}
List<Long> shopIds = leaseShopMapper.getShopIdsByProductIds(productIds);
List<Long> shopIds = leaseShopMapper.getShopIdsByMachineIds(machineIds);
PageHelper.startPage(pageVo.getPageNum(), pageVo.getPageSize());
//查询购物车中店铺信息(一层)
List<LeaseShop> leaseShops = leaseShopMapper.selectList(new LambdaQueryWrapper<LeaseShop>().in(LeaseShop::getId, shopIds));
//获取商品售价
List<MachinePayTypeDto> machinePriceByMachineIds = leaseProductMachinePriceMapper.getMachinePriceByMachineIds(machineIds);
Map<Long, List<MachinePayTypeDto>> machinePriceMap = machinePriceByMachineIds.stream().collect(Collectors.groupingBy(MachinePayTypeDto::getProductMachineId));
@@ -336,19 +296,25 @@ public class LeaseShoppingCartServiceImpl extends ServiceImpl<LeaseShoppingCartM
@Override
@Transactional
public Result<String> deleteBatchGoods(List<ProductAndMachineVo> baseVoList) {
LeaseShoppingCart leaseShoppingCart = leaseShoppingCartMapper.selectOne(new LambdaQueryWrapper<LeaseShoppingCart>().eq(LeaseShoppingCart::getUserId, SecurityUtils.getUsername()));
if (leaseShoppingCart == null){
return Result.fail("删除购物车商品失败,购物车未获取到");
List<LeaseShoppingCartInfo> leaseShoppingCartInfos = leaseShoppingCartInfoMapper.selectList(new LambdaQueryWrapper<LeaseShoppingCartInfo>()
.eq(LeaseShoppingCartInfo::getUserId, SecurityUtils.getUsername())
.eq(LeaseShoppingCartInfo::getVersion, 0));
if (leaseShoppingCartInfos.isEmpty()){
return Result.fail("删除购物车商品失败,购物车中不存在商品");
}
// 执行删除操作
int i = leaseShoppingCartInfoMapper.deleteBatchData(leaseShoppingCart.getId(), baseVoList);
if (i > 0){
return Result.success("删除购物车商品成功");
List<Long> ids = new ArrayList<>();
for (LeaseShoppingCartInfo leaseShoppingCartInfo : leaseShoppingCartInfos) {
for (ProductAndMachineVo baseVo : baseVoList){
if (leaseShoppingCartInfo.getProductMachineId().equals(baseVo.getMachineId())){
ids.add(leaseShoppingCartInfo.getId());
}
}
}
return Result.fail("删除购物车商品失败");
if (ids.isEmpty()){
return Result.fail("删除购物车商品失败,购物车中不存在该商品");
}
leaseShoppingCartInfoMapper.deleteBatchData(ids);
return Result.success("删除购物车商品成功");
}
@Override
@@ -364,14 +330,11 @@ public class LeaseShoppingCartServiceImpl extends ServiceImpl<LeaseShoppingCartM
@Override
public Result<String> deleteBatchGoodsForIsDelete() {
LeaseShoppingCart leaseShoppingCart = leaseShoppingCartMapper.selectOne(new LambdaQueryWrapper<LeaseShoppingCart>()
.eq(LeaseShoppingCart::getUserId, SecurityUtils.getUsername()));
List<LeaseShoppingCartInfo> leaseShoppingCartInfos = leaseShoppingCartInfoMapper.selectList(new LambdaQueryWrapper<LeaseShoppingCartInfo>()
.eq(LeaseShoppingCartInfo::getCartId, leaseShoppingCart.getId()));
.eq(LeaseShoppingCartInfo::getUserId, SecurityUtils.getUsername()));
List<Long> machineIds = leaseShoppingCartInfos.stream().map(LeaseShoppingCartInfo::getProductMachineId).collect(Collectors.toList());
List<Long> machineIds = leaseShoppingCartInfos.stream().map(LeaseShoppingCartInfo::getProductMachineId)
.collect(Collectors.toList());
List<LeaseProductMachine> leaseProductMachines = leaseProductMachineMapper.selectList(new LambdaQueryWrapper<LeaseProductMachine>()
.in(LeaseProductMachine::getId, machineIds));
List<Long> delMachineIds = leaseProductMachines.stream().filter(productMachine -> productMachine.getDel() || productMachine.getState() == 1)
@@ -383,10 +346,212 @@ public class LeaseShoppingCartServiceImpl extends ServiceImpl<LeaseShoppingCartM
int delete = leaseShoppingCartInfoMapper.delete(new LambdaUpdateWrapper<LeaseShoppingCartInfo>()
.in(LeaseShoppingCartInfo::getProductMachineId, delMachineIds));
if (delete > 0){
return Result.success("删除购物车已下架商品成功");
}
return Result.fail("删除失败,不存在已下架商品");
}
@Override
public Result<String> addGoodsV2(List<AddGoodsVo> addGoodsVoList) {
String userId = SecurityUtils.getUsername();
if (addGoodsVoList.isEmpty()) {
return Result.fail("请选择商品");
}
List<Long> machineIds = addGoodsVoList.stream().map(AddGoodsVo::getId).collect(Collectors.toList());
List<LeaseMachine> leaseProductMachines = leaseMachineMapper.selectList(new LambdaQueryWrapper<LeaseMachine>()
.select(LeaseMachine::getId, LeaseMachine::getShopId)
.in(LeaseMachine::getId, machineIds));
Map<Long, Long> collect = leaseProductMachines.stream()
.collect(Collectors.toMap(LeaseMachine::getId, LeaseMachine::getShopId));
//获取用户购物车中所有矿机
List<LeaseShoppingCartInfo> leaseShoppingCartInfos = leaseShoppingCartInfoMapper
.selectList(new LambdaQueryWrapper<LeaseShoppingCartInfo>()
.eq(LeaseShoppingCartInfo::getUserId, userId));
// 存储需要新增的记录
List<LeaseShoppingCartInfo> newRecords = addGoodsVoList.stream()
.filter(vo -> leaseShoppingCartInfos.stream()
.noneMatch(info -> info.getProductMachineId().equals(vo.getId())))
.map(shoppingCartInfoURDVo ->
LeaseShoppingCartInfo.builder()
.userId(userId)
.shopId(collect.get(shoppingCartInfoURDVo.getId()))
.leaseTime(shoppingCartInfoURDVo.getLeaseTime())
.numbers(shoppingCartInfoURDVo.getNumbers())
.productMachineId(shoppingCartInfoURDVo.getId())
.version(1)
.build()
).collect(Collectors.toList());
// 存储需要更新的记录
Map<Long, AddGoodsVo> voMap = addGoodsVoList.stream()
.collect(Collectors.toMap(AddGoodsVo::getId, vo -> vo));
List<LeaseShoppingCartInfo> updateList = leaseShoppingCartInfos.stream()
.peek(info -> {
AddGoodsVo vo = voMap.get(info.getProductMachineId());
if (vo != null && vo.getNumbers() != null) {
info.setNumbers(info.getNumbers()+vo.getNumbers());
info.setLeaseTime(info.getLeaseTime() + vo.getLeaseTime());
info.setId(info.getId());
}
})
.collect(Collectors.toList());
System.out.println("updateList"+ JSONUtil.toJsonPrettyStr(updateList));
System.out.println("newRecords"+ JSONUtil.toJsonPrettyStr(newRecords));
// 执行更新操作
if (!updateList.isEmpty()) {
boolean updateResult = leaseShoppingCartInfoService.updateBatchById(updateList);
if (!updateResult) {
return Result.fail("更新购物车租赁时间失败");
}
}
// 执行新增操作
if (!newRecords.isEmpty()) {
boolean saveResult = leaseShoppingCartInfoService.saveBatch(newRecords);
if (!saveResult) {
return Result.fail("添加新购物车记录失败");
}
}
return Result.success("添加购物车成功");
}
@Override
public PageResult<ShopCartV2Dto> getGoodsListV2(PageVo pageVo) {
String userId = SecurityUtils.getUsername();
//检查购物车中是否存在商品
List<ShoppingCartInfoDto> cartInfoList = leaseShoppingCartInfoMapper.getProductAndMachineIds(userId,1);
if (cartInfoList.isEmpty()){
return PageResult.success(new ArrayList<>());
}
List<Long> machineIds = new ArrayList<>();
Map<Long, Integer> machineMap = new HashMap<>();
for (ShoppingCartInfoDto shoppingCartInfoDto : cartInfoList) {
machineIds.add(shoppingCartInfoDto.getProductMachineId());
machineMap.put(shoppingCartInfoDto.getProductMachineId(), shoppingCartInfoDto.getLeaseTime());
}
List<Long> shopIds = leaseMachineMapper.getShopIdsByMachineIds(machineIds);
PageHelper.startPage(pageVo.getPageNum(), pageVo.getPageSize());
//查询购物车中店铺信息(一层)
List<LeaseShop> leaseShops = leaseShopMapper.selectList(new LambdaQueryWrapper<LeaseShop>()
.in(LeaseShop::getId, shopIds));
//获取商品售价
List<MachinePayTypeDto> machinePriceByMachineIds = leaseMachinePriceMapper.getMachinePriceByMachineIds(machineIds);
Map<Long, List<MachinePayTypeDto>> machinePriceMap = machinePriceByMachineIds.stream()
.collect(Collectors.groupingBy(MachinePayTypeDto::getProductMachineId));
//查询店铺中机器详情
List<CartMachineInfoDto> leaseProductMachines = leaseMachineMapper.getMachinesByIds(machineIds);
//获取实时币价 TODO 这里需要新加入一些币种的全网实时算力以及实时币价和报块奖励
//List<String> coins = leaseProductMachines.stream().map(ProductMachineDto::getCoin).distinct().collect(Collectors.toList());
//Map<String, BigDecimal> coinPriceMap = new HashMap<>();
//Map<String, BigDecimal> coinRewardMap = new HashMap<>();
//Map<String, BigDecimal> coinMhsMap = new HashMap<>();
//for (String coin : coins) {
// BigDecimal price = redisService.getCacheBigDecimal(RedisKey.getPiceKey(coin));
// BigDecimal reward =redisService.getCacheBigDecimal(RedisKey.getRewardKey(coin));
// BigDecimal mhs = redisService.getCacheBigDecimal(RedisKey.getMhsKey(coin));
// coinMhsMap.put(coin, mhs);
// coinRewardMap.put(coin, reward);
// coinPriceMap.put(coin, price);
//}
//设置每个矿机的售价,并按店铺分组
Map<Long, List<CartMachineInfoDto>> shopIdAndMachineInfoMap = leaseProductMachines.stream()
.peek(productMachineDto -> {
productMachineDto.setPriceList(machinePriceMap.get(productMachineDto.getId()));
})
.collect(Collectors.groupingBy(CartMachineInfoDto::getShopId));
//获取商铺钱包配置信息
List<PayConfigDto> shopWalletInfo = leaseShopMapper.getShopWalletInfoList(shopIds);
Map<Long, List<PayConfigDto>> payConfigMap = shopWalletInfo.stream()
.map(payConfigDto->{
payConfigDto.setDeductibleAmount(CoinCharge.getDeductibleAmountByChainAndCoin(payConfigDto.getPayChain(), payConfigDto.getPayCoin()));
payConfigDto.setFee(CoinCharge.getChargeByChainAndCoin(payConfigDto.getPayChain(), payConfigDto.getPayCoin()));
return payConfigDto;
})
.collect(Collectors.groupingBy(PayConfigDto::getShopId));
//组合返回对象
List<ShopCartV2Dto> shopCartList = leaseShops.stream().map(leaseShop -> {
List<CartMachineInfoDto> productMachineList = shopIdAndMachineInfoMap.get(leaseShop.getId());
return ShopCartV2Dto.builder()
.id(leaseShop.getId())
.name(leaseShop.getName())
.totalMachine(productMachineList != null ? productMachineList.size() : 0)
.payConfigList(payConfigMap.get(leaseShop.getId()))
.cartMachineInfoDtoList(productMachineList)
.build();
}).collect(Collectors.toList());
//计算不同链和币种对应的总售价
for (ShopCartV2Dto shopCartDto : shopCartList) {
//遍历每个商铺下的矿机
List<CartMachineInfoDto> productMachineDtoList = shopCartDto.getCartMachineInfoDtoList();
Map<String, BigDecimal> totalPriceMap = new HashMap<>();
Map<String, MachineTotalPriceDto> totalPriceDtoMap = new HashMap<>();
for (CartMachineInfoDto productMachineDto : productMachineDtoList) {
Integer leaseTime = machineMap.get(productMachineDto.getId());
productMachineDto.setLeaseTime(leaseTime);
List<MachinePayTypeDto> priceList = productMachineDto.getPriceList();
for (MachinePayTypeDto machinePayTypeDto : priceList) {
BigDecimal itemPrice = machinePayTypeDto.getPrice().multiply(new BigDecimal(leaseTime));
String key = machinePayTypeDto.getCoin()+"-"+machinePayTypeDto.getChain();
totalPriceMap.merge(key, itemPrice, BigDecimal::add);
totalPriceDtoMap.put(key,new MachineTotalPriceDto(BigDecimal.ZERO, machinePayTypeDto.getChain(), machinePayTypeDto.getCoin()));
}
}
totalPriceDtoMap.forEach((key, value) -> {
value.setPrice(totalPriceMap.get(key));
});
shopCartDto.setTotalPriceList(new ArrayList<>(totalPriceDtoMap.values()));
}
PageInfo<ShopCartV2Dto> pageInfo = new PageInfo<>(shopCartList);
PageResult<ShopCartV2Dto> success = PageResult.success(shopCartList);
success.setTotal(pageInfo.getTotal());
success.setTotalPage(pageInfo.getPages());
PageHelper.clearPage();
return success;
}
@Override
public Result<String> deleteBatchGoodsV2(List<CartInfoVo> baseVoList) {
List<Long> ids = baseVoList.stream().map(CartInfoVo::getCartInfoId).collect(Collectors.toList());
leaseShoppingCartInfoMapper.deleteBatchData(ids);
return Result.success("删除购物车商品成功");
}
@Override
public Result<String> deleteBatchGoodsForIsDeleteV2() {
List<LeaseShoppingCartInfo> leaseShoppingCartInfos = leaseShoppingCartInfoMapper.selectList(new LambdaQueryWrapper<LeaseShoppingCartInfo>()
.eq(LeaseShoppingCartInfo::getUserId, SecurityUtils.getUsername()));
List<Long> machineIds = leaseShoppingCartInfos.stream().map(LeaseShoppingCartInfo::getProductMachineId).collect(Collectors.toList());
List<LeaseMachine> leaseMachines = leaseMachineMapper.selectList(new LambdaQueryWrapper<LeaseMachine>()
.in(LeaseMachine::getId, machineIds));
List<Long> delMachineIds = leaseMachines.stream().filter(machine -> machine.getDel() || machine.getState())
.map(LeaseMachine::getId).collect(Collectors.toList());
if (delMachineIds.isEmpty()){
return Result.success("删除购物车已下架商品成功");
}
int delete = leaseShoppingCartInfoMapper.delete(new LambdaUpdateWrapper<LeaseShoppingCartInfo>()
.in(LeaseShoppingCartInfo::getProductMachineId, delMachineIds));
if (delete > 0){
return Result.success("删除购物车已下架商品成功");
}
return Result.fail("删除失败,不存在已下架商品");
}
}

View File

@@ -1,13 +1,12 @@
package com.m2pool.lease.service.impl;
import cn.hutool.json.JSONUtil;
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.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.m2pool.common.core.utils.GoogleAuthenticator;
import com.m2pool.common.core.utils.DateUtils;
import com.m2pool.common.core.utils.StringUtils;
import com.m2pool.common.security.utils.SecurityUtils;
import com.m2pool.lease.dto.*;
@@ -19,15 +18,30 @@ import com.m2pool.lease.mq.message.RabbitmqPayWithdrawMessage;
import com.m2pool.lease.service.LeaseUserService;
import com.m2pool.lease.utils.*;
import com.m2pool.lease.vo.*;
import org.apache.commons.io.FileUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import static com.m2pool.lease.constant.CoinCharge.getAllChargesAsDtoList;
import static com.m2pool.lease.constant.CoinCharge.getChargeByChainAndCoin;
@@ -73,6 +87,20 @@ public class LeaseUserServiceImpl extends ServiceImpl<LeaseUserMapper, LeaseUser
@Resource
private LeaseShopMapper leaseShopMapper;
@Value("${file.filepath}")
private String localFilePath;
@Value("${client.download.client}")
private String downloadClient;
@Value("${client.download.config}")
private String configName;
@Value("${client.download.path}")
private String downloadPath;
@Override
public Result<UserDto> login(UserURDVo userURDVo) {
LeaseUser leaseUser = leaseUserMapper.selectOne(new LambdaQueryWrapper<LeaseUser>()
@@ -364,7 +392,7 @@ public class LeaseUserServiceImpl extends ServiceImpl<LeaseUserMapper, LeaseUser
walletList,
balancePageVo.getStatus());
PageInfo<PayWithdrawMessageDto> pageInfo = new PageInfo<>(payWithdrawMessageDtos);
System.out.println(userEmail + "用户提现"+ payWithdrawMessageDtos);
System.out.println(userEmail + "用户提现" + payWithdrawMessageDtos);
PageResult<PayWithdrawMessageDto> success = PageResult.success(payWithdrawMessageDtos);
success.setTotal(pageInfo.getTotal());
success.setTotalPage(pageInfo.getPages());
@@ -466,4 +494,150 @@ public class LeaseUserServiceImpl extends ServiceImpl<LeaseUserMapper, LeaseUser
List<ChargeDto> allChargesAsDtoList = getAllChargesAsDtoList();
return Result.success(allChargesAsDtoList);
}
public String getAuthCode(String userEmail){
System.out.println("获取下载权限码"+userEmail);
LeaseShop leaseShop = leaseShopMapper.selectOne(new LambdaQueryWrapper<LeaseShop>()
.select(LeaseShop::getIdentityCode)
.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();
}
@Override
public void downloadClient(String userEmail,HttpServletRequest request, HttpServletResponse response) {
String auth = getAuthCode(userEmail);
//设置响应头信息
response.reset();
response.setCharacterEncoding("utf-8");
response.setContentType("multipart/form-data");
//设置压缩包的名字date为时间戳
String downloadName = "gpu.zip";
String agent = request.getHeader("USER-AGENT");
try {
//针对IE或者以IE为内核的浏览器
if (agent.contains("MSIE") || agent.contains("Trident")) {
downloadName = URLEncoder.encode(downloadName, "UTF-8");
} else {
//非IE浏览器的处理
downloadName = new String(downloadName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
}
// 创建临时目录
Path tempDir = Files.createTempDirectory("client-download");
// 创建auth.txt文件
Path authFile = tempDir.resolve("auth");
Files.write(authFile, auth.getBytes(StandardCharsets.UTF_8));
//读取/home/ubuntu/web/client/bin 下所有文件和 auth文件一起压缩
Path sourceDir = Paths.get(downloadClient);
try {
Files.walk(sourceDir)
.filter(path -> !Files.isDirectory(path))
.forEach(path -> {
try {
Path dest = tempDir.resolve(sourceDir.relativize(path));
Files.createDirectories(dest.getParent());
Files.copy(path, dest);
} catch (IOException e) {
log.error("复制客户端文件失败", e);
}
});
} catch (IOException e) {
log.error("遍历客户端文件失败", e);
}
// 创建新的zip文件
Path zipFile = Files.createTempFile("client", ".zip");
try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(zipFile.toFile().toPath()))) {
// 添加解压后的文件和auth.txt到新zip
Files.walk(tempDir)
.filter(path -> !Files.isDirectory(path))
.forEach(path -> {
try {
// 修改这里,确保文件名编码正确
String entryName = tempDir.relativize(path).toString().replace('\\', '/');
ZipEntry zipEntry = new ZipEntry(entryName);
zos.putNextEntry(zipEntry);
Files.copy(path, zos);
zos.closeEntry();
} catch (IOException e) {
log.error("添加文件到zip失败", e);
}
});
}
// 设置响应头
response.setHeader("Content-Disposition", "attachment;fileName=" + downloadName);
response.setContentLength((int) Files.size(zipFile));
// 发送文件
Files.copy(zipFile, response.getOutputStream());
response.flushBuffer();
// 清理临时文件
Files.walk(tempDir)
.sorted(Comparator.reverseOrder())
.forEach(path -> {
try {
Files.deleteIfExists(path);
} catch (IOException e) {
System.out.println("清理临时文件失败");
}
});
Files.deleteIfExists(zipFile);
} catch (Exception e) {
log.error("系统异常", e);
throw new RuntimeException("下载客户端失败", e);
}
}
@Override
public String getClientVersion() {
try {
return readTxtFromDir(downloadClient, configName);
}catch (Exception e){
return "获取配置信息失败"+e;
}
}
/**
* 读取ZIP压缩文件中指定txt文件的内容
* @param filePath 文件目录
* @param targetTxtName 目标txt文件名含扩展名如"config.txt"
* @return txt文件内容字符串
* @throws IOException 文件操作异常
*/
public String readTxtFromDir(String filePath, String targetTxtName) throws IOException {
//遍历 zipFilePath 然后找到文件名未targetTxtName带后缀 的文件
File dir = new File(filePath);
if (!dir.isDirectory()) {
throw new IllegalArgumentException("提供的路径不是一个目录");
}
File[] files = dir.listFiles((dir1, name) -> name.equals(targetTxtName));
if (files == null || files.length == 0) {
throw new FileNotFoundException("未找到目标文件: " + targetTxtName);
}
return new String(Files.readAllBytes(files[0].toPath()), StandardCharsets.UTF_8);
}
}

View File

@@ -1,12 +1,12 @@
package com.m2pool.lease.vo;
import com.m2pool.lease.dto.v2.OrderMiningInfoDto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.util.List;
/**
@@ -26,4 +26,7 @@ public class OrderAndCodeVo {
@ApiModelProperty(value = "订单信息列表",required = true)
private List<OrderInfoVo> orderInfoVoList;
@ApiModelProperty(value = "挖矿配置",required = true)
private List<OrderMiningInfoDto> orderMiningInfoDtoList;
}

View File

@@ -37,13 +37,16 @@ public class OrderInfoVo extends BaseVo{
@ApiModelProperty(value = "租赁时长",example = "1")
private Integer leaseTime;
@ApiModelProperty(value = "租赁数量",example = "1")
private Integer numbers = 1;
@ApiModelProperty(value = "矿机型号 0 ASIC 1 GPU")
private Integer type;
@ApiModelProperty(value = "支付币种",required = true)
private String coin;
@ApiModelProperty(value = "链名称",required = true)
private String chain;
//@ApiModelProperty(value = "币价",required = true)
//private BigDecimal price;
}

View File

@@ -28,10 +28,10 @@ public class ProductMachineVo extends PageVo{
private Long id;
@ApiModelProperty(value = "最大实际算力")
@ApiModelProperty(value = "最大算力")
private BigDecimal maxPower = BigDecimal.ZERO;
@ApiModelProperty(value = "最小实际算力")
@ApiModelProperty(value = "最小算力")
private BigDecimal minPower = BigDecimal.ZERO;
@ApiModelProperty(value = "最大功耗 单位kw/h",example = "10")

View File

@@ -27,7 +27,6 @@ public class ProductPageVo extends PageVo{
*/
@ApiModelProperty(value = "矿机挖矿币种 nexa rxd dgbo dgbq dgbs alph enx grs mona",example = "nexa")
private String coin;
/**
* 算法
*/

View File

@@ -8,6 +8,7 @@ import io.swagger.annotations.ApiOperation;
import lombok.*;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
@@ -48,6 +49,9 @@ public class ShopVo extends BaseVo{
*/
@ApiModelProperty(value = "商铺状态 0 待审核 1 审核通过(店铺开启) 2 店铺关闭",example = "1")
private Integer state;
@ApiModelProperty(value = "店铺手续费比例 范围0.01-0.1",example = "0.01")
private BigDecimal feeRate;
/**
* 逻辑删除字段
*/

View File

@@ -0,0 +1,35 @@
package com.m2pool.lease.vo.v2;
import com.m2pool.lease.vo.BaseVo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* <p>
* 购物车详情请求对象
* </p>
*
* @author yyb
* @since 2025-07-24
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "购物车详情请求对象V2",value = "AddGoodsVo")
public class AddGoodsVo extends BaseVo {
@ApiModelProperty(value = "矿机ID", example = "1")
private Long id;
@ApiModelProperty(value = "租赁天数", example = "1")
private Integer leaseTime;
@ApiModelProperty(value = "购买数量ASIC类型", example = "1")
private Integer numbers;
}

View File

@@ -0,0 +1,44 @@
package com.m2pool.lease.vo.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.math.BigDecimal;
import java.util.List;
/**
* <p>
* 机器参数请求对象
* </p>
*
* @author yyb
* @since 2025-07-23
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "asic 矿机新增 算力,币种请求对象",value = "AsicCoinAndAlgoVo" )
public class AsicCoinAndAlgoVo {
@ApiModelProperty(value = "币种算力配置id")
private Long coinAndPowerId;
@ApiModelProperty(value = "币种(多个逗号隔开)")
private String coin;
@ApiModelProperty(value = "算法(多个逗号隔开)")
private String algorithm;
@ApiModelProperty(value = "理论算力)",example = "0.01")
private BigDecimal theoryPower;
@ApiModelProperty(value = "商品机器单机算力单位",example = "TH/S")
private String unit;
}

Some files were not shown because too many files have changed in this diff Show More