diff --git a/src/main/java/com/m2pool/lease/LeaseApplication.java b/src/main/java/com/m2pool/lease/LeaseApplication.java new file mode 100644 index 0000000..966750a --- /dev/null +++ b/src/main/java/com/m2pool/lease/LeaseApplication.java @@ -0,0 +1,35 @@ +package com.m2pool.lease; + + +import com.m2pool.lease.annotation.EnableCustomSwagger2; +import com.m2pool.lease.netty.server.NettyTcpServer; +import io.netty.channel.ChannelFuture; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import javax.annotation.Resource; + +@EnableCustomSwagger2 +@SpringBootApplication +@MapperScan({"com.m2pool.lease.mapper"}) +public class LeaseApplication implements CommandLineRunner { + + public static void main(String[] args) { + SpringApplication.run(LeaseApplication.class, args); + System.out.println("运算力平台启动成功"); + } + + @Resource + NettyTcpServer nettyTcpServer; + + + @Override + public void run(String... args) { + //启动服务端 + ChannelFuture start = nettyTcpServer.start(); + //服务端管道关闭的监听器并同步阻塞,直到channel关闭,线程才会往下执行,结束进程 + start.channel().closeFuture().syncUninterruptibly(); + } +} diff --git a/src/main/java/com/m2pool/lease/annotation/Decrypt.java b/src/main/java/com/m2pool/lease/annotation/Decrypt.java new file mode 100644 index 0000000..f0e5089 --- /dev/null +++ b/src/main/java/com/m2pool/lease/annotation/Decrypt.java @@ -0,0 +1,14 @@ +package com.m2pool.lease.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 标记在方法上,表示需要对请求体进行解密 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Decrypt { +} diff --git a/src/main/java/com/m2pool/lease/annotation/EnableCustomSwagger2.java b/src/main/java/com/m2pool/lease/annotation/EnableCustomSwagger2.java new file mode 100644 index 0000000..65292cc --- /dev/null +++ b/src/main/java/com/m2pool/lease/annotation/EnableCustomSwagger2.java @@ -0,0 +1,16 @@ +package com.m2pool.lease.annotation; + + +import com.m2pool.lease.config.SwaggerAutoConfiguration; +import org.springframework.context.annotation.Import; + +import java.lang.annotation.*; + +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@Import({ SwaggerAutoConfiguration.class }) +public @interface EnableCustomSwagger2 { + +} diff --git a/src/main/java/com/m2pool/lease/annotation/EncryptedField.java b/src/main/java/com/m2pool/lease/annotation/EncryptedField.java new file mode 100644 index 0000000..b554ada --- /dev/null +++ b/src/main/java/com/m2pool/lease/annotation/EncryptedField.java @@ -0,0 +1,14 @@ +package com.m2pool.lease.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 标记在字段上,表示该字段需要解密 + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface EncryptedField { +} diff --git a/src/main/java/com/m2pool/lease/annotation/HashRateDB.java b/src/main/java/com/m2pool/lease/annotation/HashRateDB.java new file mode 100644 index 0000000..00969be --- /dev/null +++ b/src/main/java/com/m2pool/lease/annotation/HashRateDB.java @@ -0,0 +1,15 @@ +package com.m2pool.lease.annotation; + +import com.baomidou.dynamic.datasource.annotation.DS; + +import java.lang.annotation.*; + +/** + * 算力数据库(只读) + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@DS("hashrate") +public @interface HashRateDB { +} diff --git a/src/main/java/com/m2pool/lease/annotation/LedgerLog.java b/src/main/java/com/m2pool/lease/annotation/LedgerLog.java new file mode 100644 index 0000000..3ca7b0c --- /dev/null +++ b/src/main/java/com/m2pool/lease/annotation/LedgerLog.java @@ -0,0 +1,15 @@ +package com.m2pool.lease.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/*** + * 账本日志注解 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface LedgerLog { + String value(); +} diff --git a/src/main/java/com/m2pool/lease/annotation/LedgerLogParam.java b/src/main/java/com/m2pool/lease/annotation/LedgerLogParam.java new file mode 100644 index 0000000..d38859c --- /dev/null +++ b/src/main/java/com/m2pool/lease/annotation/LedgerLogParam.java @@ -0,0 +1,10 @@ +package com.m2pool.lease.annotation; + +import java.lang.annotation.*; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface LedgerLogParam { + String value() default ""; +} diff --git a/src/main/java/com/m2pool/lease/annotation/LedgerLogTag.java b/src/main/java/com/m2pool/lease/annotation/LedgerLogTag.java new file mode 100644 index 0000000..4d8a7cf --- /dev/null +++ b/src/main/java/com/m2pool/lease/annotation/LedgerLogTag.java @@ -0,0 +1,17 @@ +package com.m2pool.lease.annotation; + +import java.lang.annotation.*; + +/*** + * 账本日志地址标识注解 + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface LedgerLogTag { + String value() default ""; + /** + * 优先级顺序 值越小,优先级越高 + */ + int order() default 0; +} diff --git a/src/main/java/com/m2pool/lease/annotation/LoginRequired.java b/src/main/java/com/m2pool/lease/annotation/LoginRequired.java new file mode 100644 index 0000000..e3e75e2 --- /dev/null +++ b/src/main/java/com/m2pool/lease/annotation/LoginRequired.java @@ -0,0 +1,10 @@ +package com.m2pool.lease.annotation; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface LoginRequired { + boolean value() default true; +} diff --git a/src/main/java/com/m2pool/lease/annotation/MiningDB.java b/src/main/java/com/m2pool/lease/annotation/MiningDB.java new file mode 100644 index 0000000..3c6b1ed --- /dev/null +++ b/src/main/java/com/m2pool/lease/annotation/MiningDB.java @@ -0,0 +1,15 @@ +package com.m2pool.lease.annotation; + +import com.baomidou.dynamic.datasource.annotation.DS; + +import java.lang.annotation.*; + +/** + * 矿池数据库(只读) + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@DS("mining") +public @interface MiningDB { +} diff --git a/src/main/java/com/m2pool/lease/annotation/Pool2DB.java b/src/main/java/com/m2pool/lease/annotation/Pool2DB.java new file mode 100644 index 0000000..d7af2e0 --- /dev/null +++ b/src/main/java/com/m2pool/lease/annotation/Pool2DB.java @@ -0,0 +1,15 @@ +package com.m2pool.lease.annotation; + +import com.baomidou.dynamic.datasource.annotation.DS; + +import java.lang.annotation.*; + +/** + * 矿池数据库(只读) + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@DS("pool2") +public @interface Pool2DB { +} diff --git a/src/main/java/com/m2pool/lease/aspect/DecryptAspect.java b/src/main/java/com/m2pool/lease/aspect/DecryptAspect.java new file mode 100644 index 0000000..b53eca1 --- /dev/null +++ b/src/main/java/com/m2pool/lease/aspect/DecryptAspect.java @@ -0,0 +1,126 @@ +package com.m2pool.lease.aspect; + + +import cn.hutool.crypto.asymmetric.RSA; +import cn.hutool.json.JSONUtil; +import com.m2pool.lease.annotation.EncryptedField; +import com.m2pool.lease.exception.RSAException; +import com.m2pool.lease.utils.RsaUtils; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.RequestBody; + +import java.lang.reflect.Field; +import java.lang.reflect.Parameter; +import java.math.BigDecimal; +import java.util.Collection; +import java.util.Map; + +@Aspect +@Component +public class DecryptAspect { + + // 定义切点,拦截所有带 @Decrypt 注解的方法 + @Pointcut("@annotation(com.m2pool.lease.annotation.Decrypt)") + public void decryptPointcut() {} + + @Around("decryptPointcut()") + public Object around(ProceedingJoinPoint joinPoint) throws Throwable { + Object[] args = joinPoint.getArgs(); + Parameter[] parameters = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameters(); + + for (int i = 0; i < parameters.length; i++) { + // 找到被 @RequestBody 标记的参数 + if (parameters[i].isAnnotationPresent(RequestBody.class)) { + Object requestBody = args[i]; + // 递归解密对象中的加密字段 + decryptObjectFields(requestBody); + } + } + // 继续执行原方法 + return joinPoint.proceed(args); + } + + private void decryptObjectFields(Object obj) { + if (obj == null) { + return; + } + + // 处理集合或数组类型 + if (obj instanceof Collection) { + for (Object item : (Collection) obj) { + decryptObjectFields(item); + } + return; + } + + // 处理Map类型 + if (obj instanceof Map) { + for (Object value : ((Map) obj).values()) { + decryptObjectFields(value); + } + return; + } + + // 处理普通对象 + Field[] fields = obj.getClass().getDeclaredFields(); + for (Field field : fields) { + if (field.isAnnotationPresent(EncryptedField.class)) { + handleEncryptedField(obj, field); + } else { + //这里可能是普通对象或者集合或其他非EncryptedField标记的普通属性字段 + handleNestedField(obj, field); + } + } + } + + private void handleEncryptedField(Object obj, Field field) { + field.setAccessible(true); + try { + Object value = field.get(obj); + if (value != null) { + String encryptedValue = value.toString(); + System.out.println("加密前地址:"+value +"类型"+(value instanceof String)); + String decryptedValue = RsaUtils.decrypt(encryptedValue); + field.set(obj, decryptedValue); + } + } catch (Exception e) { + throw new RSAException("解密失败: " + e.getMessage()); + } finally { + field.setAccessible(false); + } + } + + private void handleNestedField(Object obj, Field field) { + field.setAccessible(true); + try { + Object fieldValue = field.get(obj); + if (fieldValue != null && !isPrimitiveOrWrapper(fieldValue)) { + decryptObjectFields(fieldValue); + } + } catch (IllegalAccessException e) { + // 忽略访问异常 + } finally { + field.setAccessible(false); + } + } + + private boolean isPrimitiveOrWrapper(Object obj) { + Class clazz = obj.getClass(); + return clazz.isPrimitive() || + clazz == String.class || + clazz == Integer.class || + clazz == Long.class || + clazz == Double.class || + clazz == Float.class || + clazz == Boolean.class || + clazz == Character.class || + clazz == Byte.class || + clazz == Short.class || + clazz == BigDecimal.class; + } +} diff --git a/src/main/java/com/m2pool/lease/aspect/LedgerLogService.java b/src/main/java/com/m2pool/lease/aspect/LedgerLogService.java new file mode 100644 index 0000000..29a781c --- /dev/null +++ b/src/main/java/com/m2pool/lease/aspect/LedgerLogService.java @@ -0,0 +1,38 @@ +package com.m2pool.lease.aspect; + +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * 用户账本日志服务类 + */ +@Service +public class LedgerLogService { + private static final String LOG_DIR = "/home/ubuntu/web/logs/ledger/"; + private static final DateTimeFormatter MONTH_FORMAT = DateTimeFormatter.ofPattern("yyyyMM"); + + public void logOperation(String tag, String operation, String content) { + String month = LocalDate.now().format(MONTH_FORMAT); + String fileName = String.format("%s%s_%s.log", LOG_DIR, tag, month); + + try { + Files.createDirectories(Paths.get(LOG_DIR)); + String logEntry = String.format("[%s] %s: %s%n", + LocalDateTime.now(), operation, content); + Files.write(Paths.get(fileName), + logEntry.getBytes(StandardCharsets.UTF_8), + StandardOpenOption.CREATE, + StandardOpenOption.APPEND); + } catch (IOException e) { + System.out.println("创建用户日志文件失败"+e.getMessage()); + } + } +} diff --git a/src/main/java/com/m2pool/lease/aspect/OperationLogAspect.java b/src/main/java/com/m2pool/lease/aspect/OperationLogAspect.java new file mode 100644 index 0000000..f7d1c82 --- /dev/null +++ b/src/main/java/com/m2pool/lease/aspect/OperationLogAspect.java @@ -0,0 +1,126 @@ +package com.m2pool.lease.aspect; + + +import com.m2pool.lease.annotation.LedgerLog; +import com.m2pool.lease.annotation.LedgerLogParam; +import com.m2pool.lease.annotation.LedgerLogTag; +import com.m2pool.lease.utils.SecurityUtils; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Comparator; +import java.util.Map; +import java.util.PriorityQueue; +import java.util.concurrent.ConcurrentHashMap; + +@Aspect +@Component +public class OperationLogAspect { + @Autowired + private LedgerLogService ledgerLogService; + + private static final ThreadLocal> EXTRA_LOG_PARAMS = ThreadLocal.withInitial(ConcurrentHashMap::new); + + @Pointcut("@annotation(com.m2pool.lease.annotation.LedgerLog)") + public void operationPointcut() {} + + @AfterReturning(pointcut = "operationPointcut()", returning = "result") + public void afterOperation(JoinPoint joinPoint, Object result) { + // 获取当前钱包地址 + StringBuilder tag = new StringBuilder(); + // 获取操作类型 + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + LedgerLog annotation = method.getAnnotation(LedgerLog.class); + //获取操作信息 + StringBuilder ledgerLog = new StringBuilder(); + for (Object arg : joinPoint.getArgs()) { + if (arg != null) { + Field[] fields = arg.getClass().getDeclaredFields(); + // 使用优先队列按order排序 + PriorityQueue tagFields = new PriorityQueue<>( + Comparator.comparingInt(f -> f.getAnnotation(LedgerLogTag.class).order()) + ); + for (Field field : fields) { + try { + if (field.isAnnotationPresent(LedgerLogParam.class)) { + field.setAccessible(true); + Object value = field.get(arg); + ledgerLog.append(field.getAnnotation(LedgerLogParam.class).value()) + .append("=") + .append(value) + .append("; "); + + }else if (field.isAnnotationPresent(LedgerLogTag.class)){ + tagFields.add(field); + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + while (!tagFields.isEmpty()) { + Field field = tagFields.poll(); + field.setAccessible(true); + try { + if (!tagFields.isEmpty() || tag.length() > 0) { // 如果不是第一个元素或者address不为空,才加"--" + tag.append("--"); + } + tag.append((String) field.get(arg)); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + } + } + + // 设置额外的日志打印信息(用于请求参数中不带的信息) + getExtraLogParam(ledgerLog); + //文件追加用户邮箱和用户id(文件名格式:地址--邮箱--用户id) + String username = SecurityUtils.getUsername(); + if (username != null){ + tag.append("--").append(username).append("--").append(SecurityUtils.getUserId()); + } + // 记录日志 + ledgerLogService.logOperation(tag.toString(), annotation.value(), ledgerLog.toString()); + } + + + + + @AfterThrowing(pointcut = "operationPointcut()", throwing = "ex") + public void afterOperationError(JoinPoint joinPoint, Exception ex) { + // 获取当前登录用户 + String username = SecurityUtils.getUsername(); + String operation = joinPoint.getSignature().getName(); + ledgerLogService.logOperation(username, operation, "操作失败: " + ex.getMessage()); + } + + /** + * 获取额外的日志参数 + * @param ledgerLog + */ + public static void getExtraLogParam(StringBuilder ledgerLog) { + Map extraParams = EXTRA_LOG_PARAMS.get(); + if (!extraParams.isEmpty()) { + extraParams.forEach((k, v) -> ledgerLog.append(k).append("=").append(v).append("; ")); + EXTRA_LOG_PARAMS.remove(); // 清除ThreadLocal + } + } + + /** + * 用于添加额外日志参数 + * @param key + * @param value + */ + public static void addExtraLogParam(String key, Object value) { + EXTRA_LOG_PARAMS.get().put(key, value); + } +} diff --git a/src/main/java/com/m2pool/lease/config/JacksonMessageConverter.java b/src/main/java/com/m2pool/lease/config/JacksonMessageConverter.java new file mode 100644 index 0000000..f94de52 --- /dev/null +++ b/src/main/java/com/m2pool/lease/config/JacksonMessageConverter.java @@ -0,0 +1,16 @@ +package com.m2pool.lease.config; + +import org.springframework.amqp.core.Message; +import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; + +public class JacksonMessageConverter extends Jackson2JsonMessageConverter { + public JacksonMessageConverter() { + super(); + } + + @Override + public Object fromMessage(Message message) { + message.getMessageProperties().setContentType("application/json"); + return super.fromMessage(message); + } +} diff --git a/src/main/java/com/m2pool/lease/config/LoginInterceptor.java b/src/main/java/com/m2pool/lease/config/LoginInterceptor.java new file mode 100644 index 0000000..7f34fbc --- /dev/null +++ b/src/main/java/com/m2pool/lease/config/LoginInterceptor.java @@ -0,0 +1,91 @@ +package com.m2pool.lease.config; + +import com.m2pool.lease.annotation.LoginRequired; +import com.m2pool.lease.entity.LeaseUser; +import com.m2pool.lease.exception.AuthException; +import com.m2pool.lease.redis.RedisAuthKey; +import com.m2pool.lease.redis.service.RedisService; +import com.m2pool.lease.utils.JwtUtils; +import com.m2pool.lease.utils.SecurityUtils; +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.lang.reflect.Method; +import java.util.concurrent.TimeUnit; + +import static com.m2pool.lease.utils.JwtUtils.AUTHENTICATION; +import static com.m2pool.lease.utils.JwtUtils.EXPIRETIME; + +@Component +public class LoginInterceptor implements HandlerInterceptor { + + @Resource + private RedisService redisService; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + // 如果不是映射到方法直接通过 + if (!(handler instanceof HandlerMethod)) { + return true; + } + + String uri = request.getRequestURI(); + if (uri.contains("/doc.html") || uri.contains("/webjars/") + || uri.contains("/swagger-resources") || uri.contains("/v2/api-docs")) { + return true; + } + + HandlerMethod handlerMethod = (HandlerMethod) handler; + Method method = handlerMethod.getMethod(); + + // 获取方法上的注解 + LoginRequired loginRequired = method.getAnnotation(LoginRequired.class); + // 如果方法上没有注解,获取类上的注解 + if (loginRequired == null) { + loginRequired = handlerMethod.getBeanType().getAnnotation(LoginRequired.class); + } + + // 如果没有注解或者注解值为true,则校验token + if (loginRequired == null || loginRequired.value()) { + String token = request.getHeader(AUTHENTICATION); + if (token == null || !isValidToken(token)) { + throw new AuthException("用户未登录"); + } + } + + return true; + } + + + + /** + * 验证token 并刷新token + */ + private boolean isValidToken(String token) { + String userEmail = JwtUtils.getUserName(token); + String getUserId = JwtUtils.getUserId(token); + //1.把userEmail存入ThreadLocal 本地线程变量中 + SecurityUtils.setUserEmail(userEmail); + SecurityUtils.setUserId(Long.valueOf(getUserId)); + //2.校验token是否过期 + String userKey = RedisAuthKey.getUserLoginKey(userEmail); + LeaseUser loginUser = redisService.getCacheObject(userKey); + if (loginUser == null){ + return false; + } + //3.刷新token 过期时间 + redisService.setCacheObject(userKey, loginUser, EXPIRETIME, TimeUnit.MINUTES); + + return true; + } + + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { + SecurityUtils.remove(); + } +} diff --git a/src/main/java/com/m2pool/lease/config/RabbitMQConfig.java b/src/main/java/com/m2pool/lease/config/RabbitMQConfig.java new file mode 100644 index 0000000..80c1812 --- /dev/null +++ b/src/main/java/com/m2pool/lease/config/RabbitMQConfig.java @@ -0,0 +1,289 @@ +package com.m2pool.lease.config; + + +import org.springframework.amqp.core.*; +import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; +import org.springframework.amqp.support.converter.MessageConverter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.HashMap; +import java.util.Map; + +import static com.m2pool.lease.constant.RabbitmqConstant.*; + +@Configuration +public class RabbitMQConfig { + + @Bean + public MessageConverter jackson2JsonMessageConverter() { + //自动生成消息唯一id + //jackson2JsonMessageConverter.setCreateMessageIds(true); + return new JacksonMessageConverter(); + } + + @Bean + public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) { + RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); + rabbitTemplate.setMessageConverter(jackson2JsonMessageConverter()); + + //// 自定义 MessagePostProcessor 来设置 content-type + //rabbitTemplate.setBeforePublishPostProcessors(new MessagePostProcessor() { + // @Override + // public Message postProcessMessage(Message message) { + // // 设置 content-type 为 application/json + // message.getMessageProperties().setContentType("application/json"); + // return message; + // } + //}); + // 开启发布确认模式 + rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> { + if (ack) { + System.out.println("消息发送成功,correlationData: " + correlationData); + } else { + System.out.println("消息发送失败,原因: " + cause); + // 这里可以添加将失败消息存储到数据库的逻辑 + } + }); + rabbitTemplate.setMandatory(true); + return rabbitTemplate; + } + + + @Bean + public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) { + SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory(); + factory.setConnectionFactory(connectionFactory); + //消费者序列化 + factory.setMessageConverter(jackson2JsonMessageConverter()); + factory.setConcurrentConsumers(3); // 设置初始消费者数量 + factory.setMaxConcurrentConsumers(5); // 设置最大消费者数量 + return factory; + } + + + /** + * 矿池代理队列 + * @return + */ + @Bean + public Queue poolProxyQueue() { + // durable 设置为 true 表示队列持久化 + return new Queue(POOL_PROXY_QUEUE_NAME, true); + } + + //----------------定义订单延迟队列------------------------ + + /** + * 死信 交换机 + * @return + */ + @Bean + public DirectExchange deadLetterExchange() { + return new DirectExchange(DEAD_LETTER_EXCHANGE_NAME); + } + + /** + * 死信 队列 + * @return + */ + @Bean + public Queue deadLetterQueue() { + return new Queue(DEAD_LETTER_QUEUE_NAME, true); + } + + /** + * 死信 队列绑定死信交换机 + * @return + */ + @Bean + public Binding deadLetterBinding() { + return BindingBuilder.bind(deadLetterQueue()).to(deadLetterExchange()).with(DEAD_LETTER_ROUTING_KEY); + } + + /** + * 订单超时消息 交换机 + * @return + */ + @Bean + public DirectExchange orderOvertimeExchange() { + return new DirectExchange(ORDER_OVERTIME_EXCHANGE_NAME); + } + + /** + * 订单超时消息 队列 (死信交换机达成延迟队列功能) + * @return + */ + @Bean + public Queue orderOvertimeQueue() { + Map args = new HashMap<>(); + // 设置死信交换机 + args.put("x-dead-letter-exchange", DEAD_LETTER_EXCHANGE_NAME); + // 设置死信路由键 + args.put("x-dead-letter-routing-key", DEAD_LETTER_ROUTING_KEY); + // 设置队列中消息的 TTL(单位:毫秒) + args.put("x-message-ttl", 900000); + return new Queue(ORDER_OVERTIME_QUEUE_NAME, true, false, false, args); + } + + /** + * 订单超时消息 队列绑定普通交换机 + * @return + */ + @Bean + public Binding orderOvertimeBinding() { + return BindingBuilder.bind(orderOvertimeQueue()).to(orderOvertimeExchange()).with(ORDER_OVERTIME_ROUTING_KEY); + } + + //----------------定义订单延迟队列------------------------ + + //----------------定义支付相关队列------------------------ + /** + * 声明 Topic 类型的交换机 + */ + @Bean + public DirectExchange payExchange() { + return new DirectExchange(PAY_EXCHANGE); + } + + // 支付相关队列声明 + /** + * 声明支付消息队列 + */ + @Bean + public Queue payAutoQueue() { + return new Queue(PAY_AUTO_QUEUE, true); + } + + /** + * 声明支付返回消息队列 + */ + @Bean + public Queue payAutoReturnQueue() { + return new Queue(PAY_AUTO_RETURN_QUEUE, true); + } + + // 余额充值相关队列声明 + /** + * 声明余额充值消息队列 + */ + @Bean + public Queue payRechargeQueue() { + return new Queue(PAY_RECHARGE_QUEUE, true); + } + + /** + * 声明余额充值返回信息队列 + */ + @Bean + public Queue payRechargeReturnQueue() { + return new Queue(PAY_RECHARGE_RETURN_QUEUE, true); + } + + // 余额提现相关队列声明 + /** + * 声明余额提现消息队列 + */ + @Bean + public Queue payWithdrawQueue() { + return new Queue(PAY_WITHDRAW_QUEUE, true); + } + + /** + * 声明余额提现返回信息队列 + */ + @Bean + public Queue payWithdrawReturnQueue() { + return new Queue(PAY_WITHDRAW_RETURN_QUEUE, true); + } + + // 支付相关绑定 + /** + * 将支付消息队列绑定到交换机 + */ + @Bean + public Binding payAutoBinding() { + return BindingBuilder.bind(payAutoQueue()).to(payExchange()).with(PAY_AUTO_ROUTING_KEY); + } + + /** + * 将支付返回消息队列绑定到交换机 + */ + @Bean + public Binding payAutoReturnBinding() { + return BindingBuilder.bind(payAutoReturnQueue()).to(payExchange()).with(PAY_AUTO_RETURN_ROUTING_KEY); + } + + + // 余额充值相关绑定 + /** + * 将余额充值消息队列绑定到交换机 + */ + @Bean + public Binding payRechargeBinding() { + return BindingBuilder.bind(payRechargeQueue()).to(payExchange()).with(PAY_RECHARGE_ROUTING_KEY); + } + + /** + * 将余额充值返回信息队列绑定到交换机 + */ + @Bean + public Binding payRechargeReturnBinding() { + return BindingBuilder.bind(payRechargeReturnQueue()).to(payExchange()).with(PAY_RECHARGE_RETURN_ROUTING_KEY); + } + + // 余额提现相关绑定 + /** + * 将余额提现消息队列绑定到交换机 + */ + @Bean + public Binding payWithdrawBinding() { + return BindingBuilder.bind(payWithdrawQueue()).to(payExchange()).with(PAY_WITHDRAW_ROUTING_KEY); + } + + /** + * 将余额提现返回信息队列绑定到交换机 + */ + @Bean + public Binding payWithdrawReturnBinding() { + return BindingBuilder.bind(payWithdrawReturnQueue()).to(payExchange()).with(PAY_WITHDRAW_RETURN_ROUTING_KEY); + } + + + + + //钱包删除 提现相关绑定 + + + @Bean + public Queue deleteWalletReturnQueue() { + return new Queue(DELETE_WALLET_RETURN_QUEUE, true); + } + + @Bean + public Queue deleteWalletQueue() { + return new Queue(DELETE_WALLET_QUEUE, true); + } + + /** + * 将钱包删除消息队列绑定到交换机 + */ + @Bean + public Binding deleteWalletBinding() { + return BindingBuilder.bind(deleteWalletQueue()).to(payExchange()).with(DELETE_WALLET_ROUTING_KEY); + } + + /** + * 将钱包删除返回信息队列绑定到交换机 + */ + @Bean + public Binding deleteWalletReturnBinding() { + return BindingBuilder.bind(deleteWalletReturnQueue()).to(payExchange()).with(DELETE_WALLET_RETURN_ROUTING_KEY); + } + + + //----------------定义支付相关队列------------------------ +} diff --git a/src/main/java/com/m2pool/lease/config/SwaggerAutoConfiguration.java b/src/main/java/com/m2pool/lease/config/SwaggerAutoConfiguration.java new file mode 100644 index 0000000..a9d480b --- /dev/null +++ b/src/main/java/com/m2pool/lease/config/SwaggerAutoConfiguration.java @@ -0,0 +1,128 @@ +package com.m2pool.lease.config; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.*; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spi.service.contexts.SecurityContext; +import springfox.documentation.spring.web.plugins.ApiSelectorBuilder; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Predicate; + +/** + * @Description swagger自动配置 + * @Date 2024/6/12 15:28 + * @Author dy + */ +@EnableSwagger2 +@Configuration +@EnableAutoConfiguration +@ConditionalOnProperty(name = "swagger.enabled", matchIfMissing = true) +public class SwaggerAutoConfiguration { + + /** 默认的排除路径,排除Spring Boot默认的错误处理路径和端点 */ + private static final List DEFAULT_EXCLUDE_PATH = Arrays.asList("/error", "/actuator/**"); + + private static final String BASE_PATH = "/**"; + + @Bean + @ConditionalOnMissingBean + public SwaggerProperties swaggerProperties() + { + return new SwaggerProperties(); + } + + @Bean + public Docket api(SwaggerProperties swaggerProperties) + { + // base-path处理 + if (swaggerProperties.getBasePath().isEmpty()) + { + swaggerProperties.getBasePath().add(BASE_PATH); + } + + // noinspection unchecked + List> basePath = new ArrayList>(); + swaggerProperties.getBasePath().forEach(path -> basePath.add(PathSelectors.ant(path))); + + // exclude-path处理 需要额外排除的url规则 + if (swaggerProperties.getExcludePath().isEmpty()) + { + swaggerProperties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH); + } + + ApiSelectorBuilder builder = new Docket(DocumentationType.SWAGGER_2).host(swaggerProperties.getHost()) + .apiInfo(apiInfo(swaggerProperties)).select() + .apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage())); + + swaggerProperties.getBasePath().forEach(p -> builder.paths(PathSelectors.ant(p))); + swaggerProperties.getExcludePath().forEach(p -> builder.paths(PathSelectors.ant(p).negate())); + + + return builder.build().securitySchemes(securitySchemes()).securityContexts(securityContexts()).pathMapping("/lease"); + + } + + /** 安全模式,指定token通过Authorization头请求头传递 */ + private List securitySchemes() + { + List apiKeyList = new ArrayList(); + apiKeyList.add(new ApiKey("Authorization", "Authorization", "header")); + return apiKeyList; + } + + /** 安全上下文 */ + private List securityContexts() + { + List securityContexts = new ArrayList<>(); + securityContexts.add( + SecurityContext.builder() + .securityReferences(defaultAuth()) + .operationSelector(o -> o.requestMappingPattern().matches("/.*")) + .build()); + return securityContexts; + } + + + /** + * 默认的全局鉴权策略 + * + * @return + */ + private List defaultAuth() + { + // + AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); + AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; + authorizationScopes[0] = authorizationScope; + List securityReferences = new ArrayList<>(); + securityReferences.add(new SecurityReference("Authorization", authorizationScopes)); + return securityReferences; + } + + + private ApiInfo apiInfo(SwaggerProperties swaggerProperties) + { + return new ApiInfoBuilder() + .title(swaggerProperties.getTitle()) + .description(swaggerProperties.getDescription()) + .license(swaggerProperties.getLicense()) + .licenseUrl(swaggerProperties.getLicenseUrl()) + .termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl()) + .contact(new Contact(swaggerProperties.getContact().getName(), swaggerProperties.getContact().getUrl(), swaggerProperties.getContact().getEmail())) + .version(swaggerProperties.getVersion()) + .build(); + + } +} diff --git a/src/main/java/com/m2pool/lease/config/SwaggerBeanPostProcessor.java b/src/main/java/com/m2pool/lease/config/SwaggerBeanPostProcessor.java new file mode 100644 index 0000000..b3a270a --- /dev/null +++ b/src/main/java/com/m2pool/lease/config/SwaggerBeanPostProcessor.java @@ -0,0 +1,56 @@ +package com.m2pool.lease.config; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.stereotype.Component; +import org.springframework.util.ReflectionUtils; +import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; +import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; +import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; + +import java.lang.reflect.Field; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @Description TODO + * @Date 2024/6/12 17:14 + * @Author dy + */ +@Component +public class SwaggerBeanPostProcessor implements BeanPostProcessor { + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException + { + if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) + { + customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); + } + return bean; + } + + private void customizeSpringfoxHandlerMappings(List mappings) + { + List copy = mappings.stream().filter(mapping -> mapping.getPatternParser() == null) + .collect(Collectors.toList()); + mappings.clear(); + mappings.addAll(copy); + } + + @SuppressWarnings("unchecked") + private List getHandlerMappings(Object bean) + { + try + { + Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); + field.setAccessible(true); + return (List) field.get(bean); + } + catch (IllegalArgumentException | IllegalAccessException e) + { + throw new IllegalStateException(e); + } + } + +} diff --git a/src/main/java/com/m2pool/lease/config/SwaggerProperties.java b/src/main/java/com/m2pool/lease/config/SwaggerProperties.java new file mode 100644 index 0000000..6765969 --- /dev/null +++ b/src/main/java/com/m2pool/lease/config/SwaggerProperties.java @@ -0,0 +1,350 @@ +package com.m2pool.lease.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * @Description Swagger配置 + * @Date 2024/6/12 15:16 + * @Author dy + */ +@Component +@ConfigurationProperties("swagger") +public class SwaggerProperties { + /** + * 是否开启swagger + */ + private Boolean enabled; + + /** + * swagger会解析的包路径 + **/ + private String basePackage = ""; + + /** + * swagger会解析的url规则 + **/ + private List basePath = new ArrayList<>(); + + /** + * 在basePath基础上需要排除的url规则 + **/ + private List excludePath = new ArrayList<>(); + + /** + * 标题 + **/ + private String title = ""; + + /** + * 描述 + **/ + private String description = ""; + + /** + * 版本 + **/ + private String version = ""; + + /** + * 许可证 + **/ + private String license = ""; + + /** + * 许可证URL + **/ + private String licenseUrl = ""; + + /** + * 服务条款URL + **/ + private String termsOfServiceUrl = ""; + + /** + * host信息 + **/ + private String host = ""; + + /** + * 联系人信息 + */ + private Contact contact = new Contact(); + + /** + * 全局统一鉴权配置 + **/ + private Authorization authorization = new Authorization(); + + public Boolean getEnabled() + { + return enabled; + } + + public void setEnabled(Boolean enabled) + { + this.enabled = enabled; + } + + public String getBasePackage() + { + return basePackage; + } + + public void setBasePackage(String basePackage) + { + this.basePackage = basePackage; + } + + public List getBasePath() + { + return basePath; + } + + public void setBasePath(List basePath) + { + this.basePath = basePath; + } + + public List getExcludePath() + { + return excludePath; + } + + public void setExcludePath(List excludePath) + { + this.excludePath = excludePath; + } + + public String getTitle() + { + return title; + } + + public void setTitle(String title) + { + this.title = title; + } + + public String getDescription() + { + return description; + } + + public void setDescription(String description) + { + this.description = description; + } + + public String getVersion() + { + return version; + } + + public void setVersion(String version) + { + this.version = version; + } + + public String getLicense() + { + return license; + } + + public void setLicense(String license) + { + this.license = license; + } + + public String getLicenseUrl() + { + return licenseUrl; + } + + public void setLicenseUrl(String licenseUrl) + { + this.licenseUrl = licenseUrl; + } + + public String getTermsOfServiceUrl() + { + return termsOfServiceUrl; + } + + public void setTermsOfServiceUrl(String termsOfServiceUrl) + { + this.termsOfServiceUrl = termsOfServiceUrl; + } + + public String getHost() + { + return host; + } + + public void setHost(String host) + { + this.host = host; + } + + public Contact getContact() + { + return contact; + } + + public void setContact(Contact contact) + { + this.contact = contact; + } + + public Authorization getAuthorization() + { + return authorization; + } + + public void setAuthorization(Authorization authorization) + { + this.authorization = authorization; + } + + public static class Contact + { + /** + * 联系人 + **/ + private String name = ""; + /** + * 联系人url + **/ + private String url = ""; + /** + * 联系人email + **/ + private String email = ""; + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getUrl() + { + return url; + } + + public void setUrl(String url) + { + this.url = url; + } + + public String getEmail() + { + return email; + } + + public void setEmail(String email) + { + this.email = email; + } + } + + public static class Authorization + { + /** + * 鉴权策略ID,需要和SecurityReferences ID保持一致 + */ + private String name = ""; + + /** + * 需要开启鉴权URL的正则 + */ + private String authRegex = "^.*$"; + + /** + * 鉴权作用域列表 + */ + private List authorizationScopeList = new ArrayList<>(); + + private List tokenUrlList = new ArrayList<>(); + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getAuthRegex() + { + return authRegex; + } + + public void setAuthRegex(String authRegex) + { + this.authRegex = authRegex; + } + + public List getAuthorizationScopeList() + { + return authorizationScopeList; + } + + public void setAuthorizationScopeList(List authorizationScopeList) + { + this.authorizationScopeList = authorizationScopeList; + } + + public List getTokenUrlList() + { + return tokenUrlList; + } + + public void setTokenUrlList(List tokenUrlList) + { + this.tokenUrlList = tokenUrlList; + } + } + + public static class AuthorizationScope + { + /** + * 作用域名称 + */ + private String scope = ""; + + /** + * 作用域描述 + */ + private String description = ""; + + public String getScope() + { + return scope; + } + + public void setScope(String scope) + { + this.scope = scope; + } + + public String getDescription() + { + return description; + } + + public void setDescription(String description) + { + this.description = description; + } + } +} diff --git a/src/main/java/com/m2pool/lease/config/SwaggerWebConfiguration.java b/src/main/java/com/m2pool/lease/config/SwaggerWebConfiguration.java new file mode 100644 index 0000000..156d67d --- /dev/null +++ b/src/main/java/com/m2pool/lease/config/SwaggerWebConfiguration.java @@ -0,0 +1,25 @@ +package com.m2pool.lease.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * @Description 资源映射路径 + * @Date 2024/6/12 17:25 + * @Author dy + */ +@Configuration +public class SwaggerWebConfiguration implements WebMvcConfigurer { + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) + { + /** swagger-ui 地址 */ + registry.addResourceHandler("/swagger-ui/**") + .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/"); + registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/"); + registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); + } + +} diff --git a/src/main/java/com/m2pool/lease/config/ThreadPoolConfig.java b/src/main/java/com/m2pool/lease/config/ThreadPoolConfig.java new file mode 100644 index 0000000..20e7f7f --- /dev/null +++ b/src/main/java/com/m2pool/lease/config/ThreadPoolConfig.java @@ -0,0 +1,31 @@ +package com.m2pool.lease.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.ThreadPoolExecutor; + +@Configuration +public class ThreadPoolConfig { + + @Bean(name = "customThreadPool") + public ThreadPoolTaskExecutor customTaskThreadPool() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + // 核心线程数 + executor.setCorePoolSize(5); + // 最大线程数 + executor.setMaxPoolSize(10); + // 队列容量 + executor.setQueueCapacity(50); + // 线程空闲时间(秒) + executor.setKeepAliveSeconds(30); + // 线程名前缀 + executor.setThreadNamePrefix("custom-task-thread-"); + // 拒绝策略 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + // 初始化 + executor.initialize(); + return executor; + } +} diff --git a/src/main/java/com/m2pool/lease/config/WebMvcConfig.java b/src/main/java/com/m2pool/lease/config/WebMvcConfig.java new file mode 100644 index 0000000..c00e542 --- /dev/null +++ b/src/main/java/com/m2pool/lease/config/WebMvcConfig.java @@ -0,0 +1,38 @@ +package com.m2pool.lease.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import javax.annotation.Resource; + +@Configuration +public class WebMvcConfig implements WebMvcConfigurer { + + @Resource + private LoginInterceptor loginInterceptor; + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(loginInterceptor) + .addPathPatterns("/**"); + } + + @Override + public void addCorsMappings(CorsRegistry registry) { + + // 设置允许跨域请求路径 + registry.addMapping("/**") + // 设置允许跨域请求的域名 + .allowedOriginPatterns("*") //或.allowedOriginPatterns("http://www.baidu.com") 指定域名 + //是否允许cookie + .allowCredentials(true) + // 设置允许的请求方式 + .allowedMethods("GET", "POST", "PUT", "DELETE") + // 设置允许的header属性 + .allowedHeaders("*") + // 允许跨域时间 + .maxAge(3600); + } +} diff --git a/src/main/java/com/m2pool/lease/constant/Algorithm.java b/src/main/java/com/m2pool/lease/constant/Algorithm.java new file mode 100644 index 0000000..6c73c58 --- /dev/null +++ b/src/main/java/com/m2pool/lease/constant/Algorithm.java @@ -0,0 +1,105 @@ +package com.m2pool.lease.constant; + + +import com.alibaba.cloud.commons.lang.StringUtils; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * @Description 币种算法 + * @Date 2025/8/18 16:19 + * @Author yyb + */ +public class Algorithm { + public static final String GRS_ALGORITHM= "groestl"; + + public static final String MONA_ALGORITHM= "Lyra2REv2"; + + public static final String NEXA_ALGORITHM= "NexaPow"; + + public static final String RXD_ALGORITHM= "Sha512256D"; + + public static final String DGBQ_ALGORITHM= "DigiByte(Qubit)"; + + public static final String DGBS_ALGORITHM= "DigiByte(Skein)"; + + public static final String DGBO_ALGORITHM= "DigiByte(Odocrypt)"; + + public static final String MONERO_ALGORITHM= "randomx"; + + public static final String ALPH_ALGORITHM= "Blake3"; + + + + public static final String GRS_FULL_NAME= "Groestlcoin"; + + public static final String MONA_FULL_NAME= "Monacoin"; + + public static final String NEXA_FULL_NAME= "nexa"; + + public static final String RXD_FULL_NAME= "Radiant"; + + public static final String DGBQ_FULL_NAME= "DigiByte(qubit)"; + + public static final String DGBS_FULL_NAME= "DigiByte(skein)"; + + public static final String DGBO_FULL_NAME= "DigiByte(odocrypt)"; + + public static final String MONERO_FULL_NAME= "monero"; + + public static final String ALPH_FULL_NAME = "Alephium"; + + private static final Map ALGORITHM_MAP; + + private static final Map COINFLULLNAME_MAP; + + + static { + HashMap map = new HashMap<>(); + map.put("grs", GRS_ALGORITHM); + map.put("mona", MONA_ALGORITHM); + map.put("nexa", NEXA_ALGORITHM); + map.put("rxd", RXD_ALGORITHM); + map.put("dgbq", DGBQ_ALGORITHM); + map.put("dgbs", DGBS_ALGORITHM); + map.put("dgbo", DGBO_ALGORITHM); + map.put("monero", MONERO_ALGORITHM); + map.put("alph", ALPH_ALGORITHM); + ALGORITHM_MAP = Collections.unmodifiableMap(map); + HashMap mapFullName = new HashMap<>(); + mapFullName.put("grs", GRS_FULL_NAME); + mapFullName.put("mona", MONA_FULL_NAME); + mapFullName.put("nexa", NEXA_FULL_NAME); + mapFullName.put("rxd", RXD_FULL_NAME); + mapFullName.put("dgbq", DGBQ_FULL_NAME); + mapFullName.put("dgbs", DGBS_FULL_NAME); + mapFullName.put("dgbo", DGBO_FULL_NAME); + mapFullName.put("monero", MONERO_FULL_NAME); + mapFullName.put("alph", ALPH_FULL_NAME); + COINFLULLNAME_MAP = Collections.unmodifiableMap(mapFullName); + } + + /** + * 根据币种名称获取对应的算法 + * @param coinName 币种名称,不区分大小写 + * @return 对应的每日理论出块数,如果未找到则返回 null + */ + public static String getAlgorithm(String coinName) { + String algorithm = ALGORITHM_MAP.get(coinName.toLowerCase()); + if (StringUtils.isEmpty(algorithm)){ + return ""; + } + return algorithm; + } + + + public static String getCoinFullName(String coinName) { + String algorithm = COINFLULLNAME_MAP.get(coinName.toLowerCase()); + if (StringUtils.isEmpty(algorithm)){ + return ""; + } + return algorithm; + } +} diff --git a/src/main/java/com/m2pool/lease/constant/BlockInterval.java b/src/main/java/com/m2pool/lease/constant/BlockInterval.java new file mode 100644 index 0000000..19d9c26 --- /dev/null +++ b/src/main/java/com/m2pool/lease/constant/BlockInterval.java @@ -0,0 +1,45 @@ +package com.m2pool.lease.constant; + +import java.math.BigDecimal; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * @Description 出块间隔数 + * @Date 2025/8/18 16:19 + * @Author yyb + */ +public class BlockInterval { + // GRS 出块间隔时间单位s + public static final BigDecimal GRS_BLOCK_INTERVAL = BigDecimal.valueOf(60); + // Mona 出块间隔时间单位s + public static final BigDecimal MONA_BLOCK_INTERVAL = BigDecimal.valueOf(90); + // NEXA 出块间隔时间单位s + public static final BigDecimal NEXA_BLOCK_INTERVAL = BigDecimal.valueOf(120); + // RXD 出块间隔时间单位s + public static final BigDecimal RXD_BLOCK_INTERVAL= BigDecimal.valueOf(300); + + private static final Map BLOCK_MAP; + + static { + HashMap map = new HashMap<>(); + map.put("grs", GRS_BLOCK_INTERVAL); + map.put("mona", MONA_BLOCK_INTERVAL); + map.put("nexa", NEXA_BLOCK_INTERVAL); + map.put("rxd", RXD_BLOCK_INTERVAL); + BLOCK_MAP = Collections.unmodifiableMap(map); + } + + /** + * 根据币种名称获取对应的每日理论出块数 + * @param coinName 币种名称,不区分大小写 + * @return 对应的每日理论出块数,如果未找到则返回 null + */ + public static BigDecimal getBlockCountByCoinName(String coinName) { + if (coinName == null) { + return BigDecimal.ZERO; + } + return BLOCK_MAP.get(coinName.toLowerCase()); + } +} diff --git a/src/main/java/com/m2pool/lease/constant/CoinCharge.java b/src/main/java/com/m2pool/lease/constant/CoinCharge.java new file mode 100644 index 0000000..bfc9bc3 --- /dev/null +++ b/src/main/java/com/m2pool/lease/constant/CoinCharge.java @@ -0,0 +1,108 @@ +package com.m2pool.lease.constant; + +import com.m2pool.lease.dto.ChargeDto; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.List; + +/** + * @Description 币种手续费 + * @Date 2025/10/23 11:15 + * @Author yyb + */ +public enum CoinCharge { + + ETH_USDT("ETH","USDT", BigDecimal.valueOf(1), BigDecimal.valueOf(1)), + ETH_ETH("ETH","ETH", BigDecimal.valueOf(0.00005),BigDecimal.valueOf(0.01)), + TRON_USDT("TRON","USDT", BigDecimal.valueOf(1),BigDecimal.valueOf(1)), + TRON_NEXA("TRON","NEXA", BigDecimal.valueOf(1000),BigDecimal.valueOf(0.1)); + + private final String chain; + /** 币种参数名 */ + private final String coin; + /** 手续费 */ + private final BigDecimal amount; + /** 费率 */ + private final BigDecimal feeRate; + + CoinCharge(String chain, String coin, BigDecimal amount, BigDecimal feeRate) { + this.chain = chain; + this.coin = coin; + this.amount = amount; + this.feeRate = feeRate; + } + /** + * 根据 chain 和 coin 查找对应的手续费,未找到则返回 1 + * @param chain 链名 + * @param coin 币种名 + * @return 对应的手续费,未找到返回 1 + */ + public static BigDecimal getChargeByChainAndCoin(String chain, String coin) { + for (CoinCharge charge : CoinCharge.values()) { + if (charge.chain.equals(chain) && charge.coin.equals(coin)) { + return charge.amount; + } + } + return BigDecimal.ONE; + } + + /** + * 根据 chain 和 coin 查找对应的手续费,未找到则返回 1 + * @param chain 链名 + * @param coin 币种名 + * @return 对应的手续费,未找到返回 1 + */ + public static BigDecimal getDeductibleAmountByChainAndCoin(String chain, String coin) { + for (CoinCharge charge : CoinCharge.values()) { + if (charge.chain.equals(chain) && charge.coin.equals(coin)) { + return charge.amount.divide(charge.feeRate, 4, RoundingMode.HALF_UP); + } + } + return BigDecimal.ONE; + } + + + /** + * 根据 chain 和 coin 找到手续费和费率,然后比较totalPrice是否小于 费率/手续费 + * @param chain 链名 + * @param coin 币种名 + * @return 对应的手续费,未找到返回 1 + */ + public static BigDecimal getFee(String chain, String coin, BigDecimal totalPrice) { + BigDecimal fee = BigDecimal.ONE; + for (CoinCharge charge : CoinCharge.values()) { + if (charge.chain.equals(chain) && charge.coin.equals(coin)) { + BigDecimal feeRate = charge.feeRate; + BigDecimal deductible= charge.amount.divide(feeRate, 4, RoundingMode.HALF_UP); + if(deductible.compareTo(totalPrice) < 0){ + fee = BigDecimal.ZERO; + }else{ + fee = charge.amount; + } + } + } + return fee; + } + + + /** + * 获取枚举类中所有枚举,并封装成 List + * @return 包含所有枚举信息的 ChargeDto 列表 + */ + public static List getAllChargesAsDtoList() { + List chargeDtoList = new ArrayList<>(); + for (CoinCharge charge : CoinCharge.values()) { + chargeDtoList.add(new ChargeDto( + charge.amount, + charge.chain, + charge.coin, + charge.feeRate + )); + } + return chargeDtoList; + } + + +} diff --git a/src/main/java/com/m2pool/lease/constant/DailyBlockOutputConstant.java b/src/main/java/com/m2pool/lease/constant/DailyBlockOutputConstant.java new file mode 100644 index 0000000..1f5db3c --- /dev/null +++ b/src/main/java/com/m2pool/lease/constant/DailyBlockOutputConstant.java @@ -0,0 +1,65 @@ +package com.m2pool.lease.constant; + +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Map; + +public class DailyBlockOutputConstant { + // GRS 每日理论出块数常量 + public static final BigDecimal GRS_BLOCK = BigDecimal.valueOf(1440); + // Mona 每日理论出块数常量 + public static final BigDecimal MONA_BLOCK = BigDecimal.valueOf(960); + // DGBS 每日理论出块数常量 + public static final BigDecimal DGBS_BLOCK = BigDecimal.valueOf(1180); + // DGBQ 每日理论出块数常量 + public static final BigDecimal DGBQ_BLOCK = BigDecimal.valueOf(1180); + // DGBO 每日理论出块数常量 + public static final BigDecimal DGBO_BLOCK = BigDecimal.valueOf(1180); + // DGB2_ODO 每日理论出块数常量 + public static final BigDecimal DGB2_ODO_BLOCK = BigDecimal.valueOf(720); + // DGB_QUBIT_A10 每日理论出块数常量 + public static final BigDecimal DGB_QUBIT_A10_BLOCK = BigDecimal.valueOf(720); + // DGB_SKEIN_A10 每日理论出块数常量 + public static final BigDecimal DGB_SKEIN_A10_BLOCK = BigDecimal.valueOf(720); + // DGB_ODO_B20 每日理论出块数常量 + public static final BigDecimal DGB_ODO_B20_BLOCK = BigDecimal.valueOf(720); + // NEXA 每日理论出块数常量 + public static final BigDecimal NEXA_BLOCK = BigDecimal.valueOf(720); + // RXD 每日理论出块数常量 + public static final BigDecimal RXD_BLOCK = BigDecimal.valueOf(288); + // ALPH 每日理论出块数常量 + public static final BigDecimal ALPH_BLOCK = BigDecimal.valueOf(5400); + // ENX 每日理论出块数常量 + public static final BigDecimal ENX_BLOCK = BigDecimal.valueOf(86400); + + private static final Map BLOCK_MAP; + + static { + BLOCK_MAP = new HashMap<>(); + BLOCK_MAP.put("grs", GRS_BLOCK); + BLOCK_MAP.put("mona", MONA_BLOCK); + BLOCK_MAP.put("dgbs", DGBS_BLOCK); + BLOCK_MAP.put("dgbq", DGBQ_BLOCK); + BLOCK_MAP.put("dgbo", DGBO_BLOCK); + BLOCK_MAP.put("dgb2_odo", DGB2_ODO_BLOCK); + BLOCK_MAP.put("dgb_qubit_a10", DGB_QUBIT_A10_BLOCK); + BLOCK_MAP.put("dgb_skein_a10", DGB_SKEIN_A10_BLOCK); + BLOCK_MAP.put("dgb_odo_b20", DGB_ODO_B20_BLOCK); + BLOCK_MAP.put("nexa", NEXA_BLOCK); + BLOCK_MAP.put("rxd", RXD_BLOCK); + BLOCK_MAP.put("alph", ALPH_BLOCK); + BLOCK_MAP.put("enx", ENX_BLOCK); + } + + /** + * 根据币种名称获取对应的每日理论出块数 + * @param coinName 币种名称,不区分大小写 + * @return 对应的每日理论出块数,如果未找到则返回 null + */ + public static BigDecimal getBlockCountByCoinName(String coinName) { + if (coinName == null) { + return BigDecimal.ZERO; + } + return BLOCK_MAP.get(coinName.toLowerCase()); + } +} diff --git a/src/main/java/com/m2pool/lease/constant/GpuBrand.java b/src/main/java/com/m2pool/lease/constant/GpuBrand.java new file mode 100644 index 0000000..7f2f25d --- /dev/null +++ b/src/main/java/com/m2pool/lease/constant/GpuBrand.java @@ -0,0 +1,47 @@ +package com.m2pool.lease.constant; + +import com.m2pool.lease.netty.message.GpuMessage; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @Description 币种算法 + * @Date 2025/8/18 16:19 + * @Author yyb + */ +public class GpuBrand { + + private static final String NVIDIA= "NVIDIA"; + + private static final String AMD= "ADM"; + + private static final String APPLE= "APPLE"; + + private static final String INTEL= "INTEL"; + + private static final List BRAND_LIST; + + + static { + BRAND_LIST = new ArrayList<>(); + BRAND_LIST.add(NVIDIA); + BRAND_LIST.add(AMD); + BRAND_LIST.add(APPLE); + BRAND_LIST.add(INTEL); + } + + public static List trimBrand(List targetList) { + return targetList.stream() + .map(model -> { + for (String brand : BRAND_LIST) { + if (model.contains(brand)) { + return model.replace(brand, "").trim(); + } + } + return model.trim(); + }) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/com/m2pool/lease/constant/OrderStatus.java b/src/main/java/com/m2pool/lease/constant/OrderStatus.java new file mode 100644 index 0000000..a9f1fc7 --- /dev/null +++ b/src/main/java/com/m2pool/lease/constant/OrderStatus.java @@ -0,0 +1,55 @@ +package com.m2pool.lease.constant; + +/** + * @Description 普通订单状态 + * @Date 2025/9/3 16:19 + * @Author yyb + */ +public enum OrderStatus { + PENDING_PAYMENT(0, "待支付"), + FULLY_PAID(1, "(全部)已支付"), + CANCELLED(2, "已取消"), + AFTER_SALES(3, "售后状态"), + REFUNDED(4, "已退款"), + PAYMENT_TIMEOUT(5, "支付已超时"), + PAYMENT_IN_PROGRESS(6, "支付中"), + PARTIALLY_PAID(10, "部分已支付"); + + private final int code; + private final String description; + + OrderStatus(int code, String description) { + this.code = code; + this.description = description; + } + + /** + * 获取订单状态编码 + * @return 订单状态编码 + */ + public int getCode() { + return code; + } + + /** + * 获取订单状态描述 + * @return 订单状态描述 + */ + public String getDescription() { + return description; + } + + /** + * 根据编码获取对应的订单状态枚举 + * @param code 订单状态编码 + * @return 订单状态枚举,如果未找到则返回 null + */ + public static OrderStatus getByCode(int code) { + for (OrderStatus status : OrderStatus.values()) { + if (status.getCode() == code) { + return status; + } + } + return null; + } +} diff --git a/src/main/java/com/m2pool/lease/constant/PaymentStatus.java b/src/main/java/com/m2pool/lease/constant/PaymentStatus.java new file mode 100644 index 0000000..bc3a78c --- /dev/null +++ b/src/main/java/com/m2pool/lease/constant/PaymentStatus.java @@ -0,0 +1,53 @@ +package com.m2pool.lease.constant; + +/** + * @Description 支付订单状态 + * @Date 2025/9/3 16:19 + * @Author yyb + */ +public enum PaymentStatus { + PAYMENT_FAILED(0, "支付失败"), + PAYMENT_SUCCESS_FULL(1, "支付成功--全部货款已支付"), + PENDING_PAYMENT(2, "待支付"), + PAYMENT_TIMEOUT(5, "支付已超时"), + PAYMENT_IN_PROGRESS(6, "支付中"), + PAYMENT_SUCCESS_PARTIAL(10, "支付成功--已支付部分货款"); + + private final int code; + private final String description; + + PaymentStatus(int code, String description) { + this.code = code; + this.description = description; + } + + /** + * 获取支付状态编码 + * @return 支付状态编码 + */ + public int getCode() { + return code; + } + + /** + * 获取支付状态描述 + * @return 支付状态描述 + */ + public String getDescription() { + return description; + } + + /** + * 根据编码获取对应的支付状态枚举 + * @param code 支付状态编码 + * @return 支付状态枚举,如果未找到则返回 null + */ + public static PaymentStatus getByCode(int code) { + for (PaymentStatus status : PaymentStatus.values()) { + if (status.getCode() == code) { + return status; + } + } + return null; + } +} diff --git a/src/main/java/com/m2pool/lease/constant/PowerUnit.java b/src/main/java/com/m2pool/lease/constant/PowerUnit.java new file mode 100644 index 0000000..36c071b --- /dev/null +++ b/src/main/java/com/m2pool/lease/constant/PowerUnit.java @@ -0,0 +1,44 @@ +package com.m2pool.lease.constant; + +import java.math.BigDecimal; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * @Description 单位换算 + * @Date 2025/8/18 16:19 + * @Author yyb + */ +public class PowerUnit { + + public static final String KH_UNIT = "KH/S"; + + public static final String MH_UNIT = "MH/S"; + + public static final String GH_UNIT = "GH/S"; + + public static final String TH_UNIT = "TH/S"; + + public static final String PH_UNIT = "PH/S"; + + private static final Map UNIT_MAP; + + static { + HashMap map = new HashMap<>(); + map.put(KH_UNIT, BigDecimal.valueOf(1000)); + map.put(MH_UNIT, BigDecimal.valueOf(1000 * 1000)); + map.put(GH_UNIT, BigDecimal.valueOf(1000L * 1000 * 1000)); + map.put(TH_UNIT, BigDecimal.valueOf(1000L * 1000 * 1000 * 1000 )); + map.put(PH_UNIT, BigDecimal.valueOf(1000L * 1000 * 1000 * 1000 * 1000)); + UNIT_MAP = Collections.unmodifiableMap(map); + } + + + public static BigDecimal getPower(String unit) { + if (unit == null) { + return BigDecimal.ZERO; + } + return UNIT_MAP.get(unit); + } +} diff --git a/src/main/java/com/m2pool/lease/constant/RabbitmqConstant.java b/src/main/java/com/m2pool/lease/constant/RabbitmqConstant.java new file mode 100644 index 0000000..2992f62 --- /dev/null +++ b/src/main/java/com/m2pool/lease/constant/RabbitmqConstant.java @@ -0,0 +1,156 @@ +package com.m2pool.lease.constant; + +/** + * @Description 常量信息 + * @Date 2024/6/11 18:13 + * @Author dy + */ +public class RabbitmqConstant { + + + + /** + * 矿池代理消息队列 + */ + public static final String POOL_PROXY_QUEUE_NAME = "pool.proxy.queue"; + + /** + * 矿池代理消息correlationData(用于生成者手动ack) + */ + public static final String POOL_PROXY_CORRELATION = "pool.proxy.message"; + + + + + + + /** + * 订单超时消息 交换机 + */ + public static final String ORDER_OVERTIME_EXCHANGE_NAME = "order.overtime.exchange"; + + /** + * 订单超时消息 路由键 + */ + public static final String ORDER_OVERTIME_ROUTING_KEY = "order.overtime.routing.key"; + /** + * + * 订单超时消息 队列 + */ + public static final String ORDER_OVERTIME_QUEUE_NAME = "order.overtime.queue"; + /** + * 订单超时消息correlationData(用于生成者手动ack) + */ + public static final String ORDER_OVERTIME_CORRELATION= "order.overtime.message"; + + /** + * 死信 队列 + */ + public static final String DEAD_LETTER_QUEUE_NAME = "dead.letter.queue"; + /** + * 死信 交换机 + */ + public static final String DEAD_LETTER_EXCHANGE_NAME = "dead.letter.exchange"; + /** + * 死信 路由键 + */ + public static final String DEAD_LETTER_ROUTING_KEY = "dead.letter.routing.key"; + + + //----------------定义支付相关队列------------------------ + + /** + * 支付模块 交换机 + */ + public static final String PAY_EXCHANGE = "pay.exchange"; + + + /** + * 支付 消息队列 + */ + public static final String PAY_AUTO_QUEUE = "pay.auto.queue"; + + /** + * 支付 路由键 + */ + public static final String PAY_AUTO_ROUTING_KEY = "pay.auto.routing.key"; + + /** + * 支付 返回消息消息队列 + */ + public static final String PAY_AUTO_RETURN_QUEUE = "pay.auto.return.queue"; + + /** + * 支付 返回消息路由键 + */ + public static final String PAY_AUTO_RETURN_ROUTING_KEY = "pay.auto.return.routing.key"; + + + /** + * 余额充值 消息队列 + */ + public static final String PAY_RECHARGE_QUEUE = "pay.recharge.queue"; + + /** + * 余额充值 路由键 + * + */ + public static final String PAY_RECHARGE_ROUTING_KEY = "pay.recharge.routing.key"; + + /** + * 余额充值 返回信息消息队列 + */ + public static final String PAY_RECHARGE_RETURN_QUEUE = "pay.recharge.return.queue"; + + /** + * 余额充值 返回信息路由键 + * + */ + public static final String PAY_RECHARGE_RETURN_ROUTING_KEY = "pay.recharge.return.routing.key"; + /** + * 余额提现 消息队列 + */ + public static final String PAY_WITHDRAW_QUEUE = "pay.withdraw.queue"; + + /** + * 余额提现 路由键 + * + */ + public static final String PAY_WITHDRAW_ROUTING_KEY = "pay.withdraw.routing.key"; + + /** + * 余额提现 返回信息消息队列 + */ + public static final String PAY_WITHDRAW_RETURN_QUEUE = "pay.withdraw.return.queue"; + /** + * 余额提现 返回信息路由键 + * + */ + public static final String PAY_WITHDRAW_RETURN_ROUTING_KEY = "pay.withdraw.return.routing.key"; + + + /** + * 钱包删除 信息消息队列 + */ + public static final String DELETE_WALLET_QUEUE = "pay.remove.queue"; + /** + * 钱包删除 信息路由键 + * + */ + public static final String DELETE_WALLET_ROUTING_KEY = "pay.remove.routing.key"; + + + /** + * 钱包删除 返回信息消息队列 + */ + public static final String DELETE_WALLET_RETURN_QUEUE = "pay.remove.return.queue"; + /** + * 钱包删除 返回信息路由键 + * + */ + public static final String DELETE_WALLET_RETURN_ROUTING_KEY = "pay.remove.return.routing.key"; + + + //----------------定义支付相关队列------------------------ + +} diff --git a/src/main/java/com/m2pool/lease/constant/ResponseConstant.java b/src/main/java/com/m2pool/lease/constant/ResponseConstant.java new file mode 100644 index 0000000..95d7ead --- /dev/null +++ b/src/main/java/com/m2pool/lease/constant/ResponseConstant.java @@ -0,0 +1,92 @@ +package com.m2pool.lease.constant; + +/** + * @Description 常量信息 + * @Date 2024/6/11 18:13 + * @Author dy + */ +public class ResponseConstant { + /** + * 成功标记 + */ + public static final Integer SUCCESS = 200; + + /** + * 失败标记 + */ + public static final Integer FAIL = 500; + + /** + * 登录成功状态 + */ + public static final String LOGIN_SUCCESS_STATUS = "0"; + + /** + * 登录失败状态 + */ + public static final String LOGIN_FAIL_STATUS = "1"; + + /** + * 登录成功 + */ + public static final String LOGIN_SUCCESS = "Success"; + + /** + * 注销 + */ + public static final String LOGOUT = "Logout"; + + /** + * 注册 + */ + public static final String REGISTER = "Register"; + + /** + * 登录失败 + */ + public static final String LOGIN_FAIL = "Error"; + + /** + * 当前记录起始索引 + */ + public static final String PAGE_NUM = "pageNum"; + + /** + * 每页显示记录数 + */ + public static final String PAGE_SIZE = "pageSize"; + + /** + * 排序列 + */ + public static final String ORDER_BY_COLUMN = "orderByColumn"; + + /** + * 排序的方向 "desc" 或者 "asc". + */ + public static final String IS_ASC = "isAsc"; + + /** + * 验证码 redis key + */ + public static final String CAPTCHA_CODE_KEY = "captcha_codes:"; + + /** + * 验证码有效期(分钟) + */ + public static final long CAPTCHA_EXPIRATION = 2; + + + /** + * 参数管理 cache key + */ + public static final String SYS_CONFIG_KEY = "sys_config:"; + + /** + * 资源映射路径 前缀 + */ + public static final String RESOURCE_PREFIX = "/profile"; + + + +} diff --git a/src/main/java/com/m2pool/lease/controller/LeaseAuthController.java b/src/main/java/com/m2pool/lease/controller/LeaseAuthController.java new file mode 100644 index 0000000..16f20c6 --- /dev/null +++ b/src/main/java/com/m2pool/lease/controller/LeaseAuthController.java @@ -0,0 +1,172 @@ +package com.m2pool.lease.controller; + + +import com.m2pool.lease.annotation.Decrypt; +import com.m2pool.lease.annotation.LoginRequired; +import com.m2pool.lease.dto.Result; +import com.m2pool.lease.dto.v2.GoogleInfoDto; +import com.m2pool.lease.service.LeaseUserService; +import com.m2pool.lease.vo.EmailCodeVo; +import com.m2pool.lease.vo.RestPwdVo; +import com.m2pool.lease.vo.UserLoginVo; +import com.m2pool.lease.vo.UserRegisterVo; +import com.m2pool.lease.vo.v2.CloseAccountVo; +import com.m2pool.lease.vo.v2.GoogleBindVo; +import com.m2pool.lease.vo.v2.GoogleCloseVo; +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 javax.validation.Valid; +import java.util.Map; + +/** + *

+ * 用户表 前端控制器 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Api(tags = "用户授权控制器") +@LoginRequired(value = false) +@RestController +@RequestMapping("/auth") +public class LeaseAuthController { + + @Resource + private LeaseUserService leaseUserService; + + @Decrypt + @PostMapping("/login") + @ApiOperation(value = "用户登录") + public Result> login(@RequestBody UserLoginVo userLoginVo){ + return leaseUserService.login(userLoginVo); + } + + + @Decrypt + @PostMapping("/register") + @ApiOperation(value = "用户注册") + public Result register(@RequestBody UserRegisterVo userRegisterVo){ + return leaseUserService.register(userRegisterVo); + } + + @Decrypt + @PostMapping("/updatePassword") + @ApiOperation(value = "登录页面----忘记密码:重置密码") + public Result updatePassword(@RequestBody UserLoginVo userLoginVo){ + return leaseUserService.updatePassword(userLoginVo); + } + + @Decrypt + @PostMapping("/updatePasswordInCenter") + @ApiOperation(value = "个人中心--修改密码") + public Result updatePasswordInCenter(@RequestBody RestPwdVo restPwdVo){ + return leaseUserService.updatePasswordInCenter(restPwdVo); + } + + @LoginRequired + @PostMapping("/logout") + @ApiOperation(value = "登出") + public Result logout(){ + return leaseUserService.logout(); + } + + @PostMapping("/sendLoginCode") + @ApiOperation(value = "发送用户登录邮箱验证码") + public Result sendLoginCode(@RequestBody @Valid EmailCodeVo emailCodeVo){ + return leaseUserService.sendLoginCode(emailCodeVo); + } + + @PostMapping("/sendRegisterCode") + @ApiOperation(value = "发送用户注册邮箱验证码") + public Result sendRegisterCode(@RequestBody @Valid EmailCodeVo emailCodeVo){ + return leaseUserService.sendRegisterCode(emailCodeVo); + } + + @PostMapping("/sendUpdatePwdCode") + @ApiOperation(value = "发送修改/重置密码验证码") + public Result sendUpdatePwdCode(@RequestBody @Valid EmailCodeVo emailCodeVo){ + return leaseUserService.sendUpdatePwdCode(emailCodeVo); + } + + @LoginRequired + @PostMapping("/closeAccount") + @ApiOperation(value = "用户注销") + public Result closeAccount(@RequestBody CloseAccountVo closeAccountVo){ + return leaseUserService.closeAccount(closeAccountVo); + } + + @PostMapping("/sendCloseAccount") + @ApiOperation(value = "发送注销用户邮箱验证码") + public Result sendCloseAccount(@RequestBody @Valid EmailCodeVo emailCodeVo){ + return leaseUserService.sendCloseAccount(emailCodeVo); + } + + + /*-----------------------------谷歌双重验证接口----------------------------*/ + + @Decrypt + @LoginRequired + @PostMapping("/bindGoogle") + @ApiOperation(value = "绑定谷歌验证码") + public Result bindGoogle(@RequestBody @Valid GoogleBindVo googleBindVo){ + return leaseUserService.bindGoogle(googleBindVo); + } + + @LoginRequired + @PostMapping("/getBindInfo") + @ApiOperation(value = "获取谷歌验证器安全码和验证码") + public Result getBindInfo(){ + return leaseUserService.getBindInfo(); + + } + + @LoginRequired + @PostMapping("/sendOpenGoogleCode") + @ApiOperation(value = "开启/绑定谷歌验证器 发送邮箱验证码") + public Result sendOpenGoogleCode(){ + return leaseUserService.sendOpenGoogleCode(); + } + + @LoginRequired + @PostMapping("/sendCloseGoogleCode") + @ApiOperation(value = "关闭谷歌验证器 发送邮箱验证码") + public Result sendCloseGoogleCode(){ + return leaseUserService.sendCloseGoogleCode(); + } + + @LoginRequired + @PostMapping("/closeStepTwo") + @ApiOperation(value = "关闭双重验证") + public Result closeStepTwo(@Valid @RequestBody GoogleCloseVo vo){ + return leaseUserService.closeStepTwo(vo); + + } + + @LoginRequired + @PostMapping("/getGoogleStatus") + @ApiOperation(value = "谷歌验证开启状态",notes = "返回值:谷歌验证状态 0 已绑定,开启 1未绑定 2已绑定,关闭") + public Result getGoogleStatus(){ + return leaseUserService.getGoogleStatus(); + } + + + @LoginRequired + @PostMapping("/openStepTwo") + @ApiOperation(value = "开启双重验证") + public Result openStepTwo(@RequestBody @Valid GoogleCloseVo vo){ + return leaseUserService.openStepTwo(vo); + + } + + /*-----------------------------谷歌双重验证接口----------------------------*/ + + +} + diff --git a/src/main/java/com/m2pool/lease/controller/LeaseOrderInfoController.java b/src/main/java/com/m2pool/lease/controller/LeaseOrderInfoController.java new file mode 100644 index 0000000..01c7a58 --- /dev/null +++ b/src/main/java/com/m2pool/lease/controller/LeaseOrderInfoController.java @@ -0,0 +1,87 @@ +package com.m2pool.lease.controller; + + +import com.m2pool.lease.annotation.Decrypt; +import com.m2pool.lease.annotation.LoginRequired; +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.service.LeaseOrderInfoService; +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 javax.annotation.Resource; +import java.math.BigDecimal; +import java.util.List; + +/** + *

+ * 订单表 前端控制器 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Api(tags = "订单控制器") +@LoginRequired +@RestController +@RequestMapping("/order/info") +public class LeaseOrderInfoController { + + + @Resource + private LeaseOrderInfoService leaseOrderInfoService; + + @ApiOperation("创建订单及订单详情 + 支付订单(返回二维码内容)") + @PostMapping("/addOrders") + @Decrypt + public Result addOrders(@RequestBody OrderAndCodeVo orderAndCodeVo) { + return leaseOrderInfoService.addOrders(orderAndCodeVo); + } + + @ApiOperation("订单支付超时--再次购买功能") + @PostMapping("/buyAgain") + @Deprecated + public Result> buyAgain(@RequestBody List orderInfoVoList) { + return leaseOrderInfoService.buyAgain(orderInfoVoList); + } + + @ApiOperation("查询订单列表(买家)") + @PostMapping("/getOrdersByStatus") + public PageResult getOrdersByStatus(@RequestBody OrderInfoStateVo orderInfoStateVo) { + return leaseOrderInfoService.getOrdersByStatus(orderInfoStateVo); + } + + + @ApiOperation("卖家已售出订单列表(卖家)") + @PostMapping("/getOrdersByStatusForSeller") + public PageResult getOrdersByStatusForSeller(@RequestBody OrderInfoStateVo orderInfoStateVo) { + return leaseOrderInfoService.getOrdersByStatusForSeller(orderInfoStateVo); + } + + + @ApiOperation("根据订单id查询订单信息") + @PostMapping("/getOrdersByIds") + public Result getOrdersByIds(@RequestBody OrderVo orderVo) { + return leaseOrderInfoService.getOrdersByIds(orderVo); + } + + @ApiOperation("取消订单(如果有支付订单同时取消)") + @PostMapping("/cancelOrder") + public Result cancelOrder(@RequestBody OrderVo orderVo) { + return leaseOrderInfoService.cancelOrder(orderVo); + } + + @ApiOperation("生成订单时获取用户选择的支付币种 实时币价") + @PostMapping("/getCoinPrice") + public Result getCoinPrice(@RequestBody CoinVo coinVo){ + return leaseOrderInfoService.getCoinPrice(coinVo); + } +} + diff --git a/src/main/java/com/m2pool/lease/controller/LeaseOrderInfoV2Controller.java b/src/main/java/com/m2pool/lease/controller/LeaseOrderInfoV2Controller.java new file mode 100644 index 0000000..190928f --- /dev/null +++ b/src/main/java/com/m2pool/lease/controller/LeaseOrderInfoV2Controller.java @@ -0,0 +1,78 @@ +package com.m2pool.lease.controller; + + +import com.m2pool.lease.annotation.Decrypt; +import com.m2pool.lease.annotation.LoginRequired; +import com.m2pool.lease.dto.PageResult; +import com.m2pool.lease.dto.Result; +import com.m2pool.lease.dto.v2.*; +import com.m2pool.lease.service.LeaseOrderInfoService; +import com.m2pool.lease.vo.BaseVo; +import com.m2pool.lease.vo.OrderAndCodeVo; +import com.m2pool.lease.vo.PageVo; +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; + +/** + *

+ * 订单表 前端控制器 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Api(tags = "v2版本--订单控制器") +@LoginRequired +@RestController +@RequestMapping("/v2/order/info") +public class LeaseOrderInfoV2Controller { + + @Resource + private LeaseOrderInfoService leaseOrderInfoService; + + @ApiOperation("算法币种选择框:购物车结算获取选中矿机支持的算法和币种列表") + @PostMapping("/getMachineSupportCoinAndAlgorithm") + public Result> getMachineSupportCoinAndAlgorithm(@RequestBody List machineIds) { + return leaseOrderInfoService.getMachineSupportCoinAndAlgorithm(machineIds); + } + + @ApiOperation("矿池选择框:根据矿机id 获取当前矿机支持的矿池币种信息") + @PostMapping("/getMachineSupportPool") + public Result> getMachineSupportPool(@RequestBody CoinAndAlgorithmVo coinAndAlgorithmVo) { + return leaseOrderInfoService.getMachineSupportPool(coinAndAlgorithmVo); + } + + @ApiOperation("创建订单及订单详情 + 支付订单(返回二维码内容)") + @PostMapping("/addOrdersV2") + @Decrypt + public Result addOrdersV2(@RequestBody OrderAndCodeVo orderAndCodeVo) { + return leaseOrderInfoService.addOrdersV2(orderAndCodeVo); + } + + + @ApiOperation("买家:已购矿机列表") + @PostMapping("/getPurchasedItems") + public PageResult getPurchasedItems(@RequestBody(required = false) PageVo pageVo) { + if (pageVo == null){ + pageVo = new PageVo(); + } + return leaseOrderInfoService.getPurchasedItems(pageVo); + } + + + + @ApiOperation("买家:根据id查询已购矿机详情") + @PostMapping("/getPurchasedInfo") + public Result getPurchasedInfo(@RequestBody BaseVo baseVo) { + return leaseOrderInfoService.getPurchasedInfo(baseVo); + } +} + diff --git a/src/main/java/com/m2pool/lease/controller/LeasePaymentRecordController.java b/src/main/java/com/m2pool/lease/controller/LeasePaymentRecordController.java new file mode 100644 index 0000000..a92b80c --- /dev/null +++ b/src/main/java/com/m2pool/lease/controller/LeasePaymentRecordController.java @@ -0,0 +1,67 @@ +package com.m2pool.lease.controller; + + +import com.m2pool.lease.dto.PaymentCallbackDto; +import com.m2pool.lease.dto.PaymentRecordDto; +import com.m2pool.lease.dto.Result; +import com.m2pool.lease.service.LeasePaymentRecordService; +import com.m2pool.lease.vo.CheckPayStatusVo; +import com.m2pool.lease.vo.OrderVo; +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; + +/** + *

+ * 支付记录表 前端控制器 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Api(tags = "支付记录控制器") +@RestController +@RequestMapping("/payment/record") +public class LeasePaymentRecordController { + + @Resource + private LeasePaymentRecordService leasePaymentRecordService; + + @ApiOperation("根据订单详情信息生成 支付订单 + 根据返回的集合生成多个支付二维码") + @PostMapping("/addPayOrder") + @Deprecated + public Result> addPayOrder(@RequestBody OrderVo orderVo) { + return leasePaymentRecordService.addPayOrder(orderVo); + } + + + @ApiOperation("根据订单id找到支付订单") + @PostMapping("/getPayOrderByOrderId") + public Result> getPayOrderByOrderId(@RequestBody OrderVo orderVo) { + return leasePaymentRecordService.getPayOrderByOrderId(orderVo); + } + + + @ApiOperation("支付回调结果---根据订单id批量校验本次支付是否成功") + @PostMapping("/paymentCallbackBatch") + @Deprecated + public Result> paymentCallbackBatch(@RequestBody List checkPayStatusVoList) { + return leasePaymentRecordService.paymentCallbackBatch(checkPayStatusVoList); + } + + @ApiOperation("支付回调结果----根据支付id校验支付是否成功") + @PostMapping("/paymentCallbackByPayId") + @Deprecated + public Result paymentCallbackByPayId(@RequestBody CheckPayStatusVo checkPayStatusVo) { + return leasePaymentRecordService.paymentCallbackByPayId(checkPayStatusVo); + } + + +} + diff --git a/src/main/java/com/m2pool/lease/controller/LeaseProductController.java b/src/main/java/com/m2pool/lease/controller/LeaseProductController.java new file mode 100644 index 0000000..fdb2316 --- /dev/null +++ b/src/main/java/com/m2pool/lease/controller/LeaseProductController.java @@ -0,0 +1,131 @@ +package com.m2pool.lease.controller; + + +import com.m2pool.lease.annotation.Decrypt; +import com.m2pool.lease.dto.*; +import com.m2pool.lease.service.LeaseProductService; +import com.m2pool.lease.service.LeaseUserOwnedProductService; +import com.m2pool.lease.vo.*; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +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 javax.validation.Valid; +import java.util.List; + +/** + *

+ * 商品表 前端控制器 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Api(tags = "商品控制器") +@RestController +@RequestMapping("/product") +public class LeaseProductController { + @Autowired + private LeaseProductService leaseProductService; + + + @Resource + private LeaseUserOwnedProductService leaseUserOwnedProductService; + @ApiOperation("查询商品列表(不包含商品对应的售卖机器详情列表)") + @PostMapping("/getList") + public PageResult getProductList(@RequestBody(required = false) ProductPageVo productPageVo) { + if (productPageVo == null){ + productPageVo = new ProductPageVo(); + } + return leaseProductService.getProductList(productPageVo); + } + + + @ApiOperation("根据商品id查询单个商品详情信息") + @PostMapping("/getMachineInfoById") + public Result getMachineInfoById(@RequestBody BaseVo baseVo) { + return leaseProductService.getMachineInfoById(baseVo); + } + + + + @ApiOperation("查询单个商品详情(包含商品对应的售卖机器详情列表)") + @PostMapping("/getMachineInfo") + public PageResult getProductMachineInfo(@RequestBody ProductMachineVo productMachineVo) { + return leaseProductService.getProductMachineInfo(productMachineVo); + } + + @ApiOperation("单个商品矿机列表页面---获取支付方式") + @PostMapping("/getPayTypes") + public Result> getPayTypes(@RequestBody(required = false) ShopPayTypeVo shopPayTypeVo) { + return leaseProductService.getPayTypes(shopPayTypeVo); + } + + + + @ApiOperation("新增商品(不包含商品对应的售卖机器)") + @PostMapping("/add") + public Result addProduct(@RequestBody ProductURDVo productURDVo) { + return leaseProductService.addProduct(productURDVo); + } + + @ApiOperation("编辑商品 + 商品上下架(不包含商品对应的售卖机器)") + @PostMapping("/update") + public Result updateProduct(@RequestBody ProductURDVo productURDVo) { + return leaseProductService.updateProduct(productURDVo); + } + + @ApiOperation("删除商品(包含商品对应的售卖机器)") + @PostMapping("/delete") + public Result deleteProduct(@RequestBody BaseVo baseVo) { + return leaseProductService.deleteProduct(baseVo.getId()); + } + + + @ApiOperation("用户查询当前自身已购商品列表") + @PostMapping("/getOwnedList") + public PageResult getOwnedList(@RequestBody(required = false) UserOwnedProductVo userOwnedProductVo) { + if (userOwnedProductVo == null){ + userOwnedProductVo = new UserOwnedProductVo(); + } + return leaseUserOwnedProductService.getOwnedList(userOwnedProductVo); + } + + + @ApiOperation("根据id查询当前自身已购商品详情") + @PostMapping("/getOwnedById") + public Result getOwnedById(@RequestBody BaseVo baseVo) { + return leaseUserOwnedProductService.getOwnedById(baseVo); + } + + + @ApiOperation("查询用户店铺支持的支付方式") + @PostMapping("/getSupportPayType") + public Result> getSupportPayType() { + return leaseProductService.getSupportPayType(); + } + + + //TODO 2025-11-21 查询店铺的矿机列表,不存在商品了 + + @ApiOperation("获取店铺商品列表用于新增绑定店铺钱包") + @PostMapping("/getProductListForShopWalletConfig") + public Result> getProductListForShopWalletConfig() { + return leaseProductService.getProductListForShopWalletConfig(); + } + + //TODO 2025-11-21 修改这些矿机的价格,并且绑定新钱包 + + @ApiOperation("新增绑定店铺钱包并设置店铺下面每个矿机该钱包币种的售价 + 钱包绑定") + @PostMapping("/updateProductListForShopWalletConfig") + @Decrypt + public Result updateProductListForShopWalletConfig(@RequestBody ProductMachineForWalletConfigVo productMachineForWalletConfigVo) { + return leaseProductService.updateProductListForShopWalletConfig(productMachineForWalletConfigVo); + } +} + diff --git a/src/main/java/com/m2pool/lease/controller/LeaseProductMachineController.java b/src/main/java/com/m2pool/lease/controller/LeaseProductMachineController.java new file mode 100644 index 0000000..8f126e8 --- /dev/null +++ b/src/main/java/com/m2pool/lease/controller/LeaseProductMachineController.java @@ -0,0 +1,90 @@ +package com.m2pool.lease.controller; + + +import com.m2pool.lease.annotation.LoginRequired; +import com.m2pool.lease.dto.PageResult; +import com.m2pool.lease.dto.ProductUpdateMachineDto; +import com.m2pool.lease.dto.Result; +import com.m2pool.lease.dto.UserMinerDto; +import com.m2pool.lease.service.LeaseProductMachineService; +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 javax.annotation.Resource; +import java.util.List; +import java.util.Map; + +/** + *

+ * 商品表对应的物品机器表 前端控制器 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Api(tags = "商品矿机控制器(库存控制器)") +@LoginRequired +@RestController +@RequestMapping("/product/machine") +public class LeaseProductMachineController { + + + @Resource + private LeaseProductMachineService leaseProductMachineService; + + + @ApiOperation("根据 登录账户 获取挖矿账户及挖矿币种集合----用于用户为商品添加实际出售机器库存") + @PostMapping("/getUserMinersList") + public Result>> getUserMinersList(@RequestBody UserMinerVo userMinerVo) { + return leaseProductMachineService.getUserMinersList(userMinerVo); + } + + @ApiOperation("根据挖矿账户获取对应的 矿机机器编码集合----用于用户为商品添加实际出售机器库存") + @PostMapping("/getUserMachineList") + public Result> getUserMachineList(@RequestBody UserMinerVo userMinerVo) { + return leaseProductMachineService.getUserMachineList(userMinerVo); + } + + + + + @ApiOperation("根据商品id查询商品矿机列表----用于卖方修改矿机信息") + @PostMapping("/getMachineListForUpdate") + public PageResult getMachineListForUpdate(@RequestBody ProductForUpdateMachineVo productForUpdateMachineVo) { + return leaseProductMachineService.getMachineListForUpdate(productForUpdateMachineVo); + } + + + @ApiOperation("单个新增或批量新增出售机器") + @PostMapping("/addSingleOrBatchMachine") + public Result addSingleOrBatchMachine(@RequestBody ProductMachineParamsVo productMachineParamsVoList) { + return leaseProductMachineService.addSingleOrBatchMachine(productMachineParamsVoList); + } + + @ApiOperation("单个/批量 编辑矿机 + 矿机上下架") + @PostMapping("/updateMachine") + public Result updateMachine(@RequestBody List productUpdateMachineVoList) { + return leaseProductMachineService.updateMachine(productUpdateMachineVoList); + } + + @ApiOperation("根据矿机id 删除商品矿机") + @PostMapping("/delete") + public Result deleteMachine(@RequestBody BaseVo baseVo) { + return leaseProductMachineService.deleteMachine(baseVo); + } + + + //@ApiOperation("价格计算器---添加机器到商品中时需要给一个价格") + //@PostMapping("/calculatePrice") + //public Result calculatePrice(@RequestBody PriceCalculateVo priceCalculateVo) { + // return leaseProductMachineService.calculatePrice(priceCalculateVo); + //} + + +} + diff --git a/src/main/java/com/m2pool/lease/controller/LeaseProductMachineV2Controller.java b/src/main/java/com/m2pool/lease/controller/LeaseProductMachineV2Controller.java new file mode 100644 index 0000000..83408cd --- /dev/null +++ b/src/main/java/com/m2pool/lease/controller/LeaseProductMachineV2Controller.java @@ -0,0 +1,112 @@ +package com.m2pool.lease.controller; + + +import com.m2pool.lease.annotation.LoginRequired; +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; + +/** + *

+ * 商品表对应的物品机器表 前端控制器 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Api(tags = "v2版本--矿机控制器类") +@LoginRequired +@RestController +@RequestMapping("/v2/product/machine") +public class LeaseProductMachineV2Controller { + + + @Resource + private LeaseMachineService leaseMachineService; + + @ApiOperation("商城首页---店铺列表") + @LoginRequired(value = false) + @PostMapping("/getShopList") + public PageResult getShopList(@RequestBody(required = false) ProductPageVo productPageVo) { + if(productPageVo == null){ + productPageVo = new ProductPageVo(); + } + return leaseMachineService.getShopList(productPageVo); + } + + + @ApiOperation("商城首页---店铺对应矿机详情列表") + @LoginRequired(value = false) + @PostMapping("/getShopMachineList") + public PageResult getShopMachineList(@RequestBody ShopMachineVo shopMachineVo) { + return leaseMachineService.getShopMachineList(shopMachineVo); + } + + @ApiOperation("卖家页面---矿机列表") + @PostMapping("/getShopMachineListForSeller") + public PageResult getShopMachineListForSeller(@RequestBody(required = false) SellerMachineVo sellerMachineVo) { + if (sellerMachineVo == null) { + sellerMachineVo = new SellerMachineVo(); + } + return leaseMachineService.getShopMachineListForSeller(sellerMachineVo); + } + + @ApiOperation("卖家页面---新增ASIC矿机") + @PostMapping("/addAsicMachine") + public Result addAsicMachine(@RequestBody AsicMachineParamsVo asicMachineParamsVo) { + return leaseMachineService.addAsicMachine(asicMachineParamsVo); + } + + @ApiOperation("卖家页面---修改ASIC矿机") + @PostMapping("/updateAsicMachine") + public Result updateAsicMachine(@RequestBody AsicMachineParamsVo asicMachineParamsVo) { + return leaseMachineService.updateAsicMachine(asicMachineParamsVo); + } + + @ApiOperation("卖家页面--- 批量修改GPU矿机 和 批量上下架") + @PostMapping("/updateGpuMachine") + public Result updateGpuMachine(@RequestBody List gpuMachineParamsVoList) { + return leaseMachineService.updateGpuMachine(gpuMachineParamsVoList); + } + + @ApiOperation("卖家页面---GPU/ASIC矿机上下架") + @PostMapping("/updateMachineState") + public Result updateMachineState(@RequestBody MachineStateVo machineStateVo) { + return leaseMachineService.updateMachineState(machineStateVo); + } + + + @ApiOperation("卖家页面---根据矿机id删除GPU/ASIC矿机") + @PostMapping("/deleteMachine") + public Result deleteMachine(@RequestBody BaseVo baseVo) { + return leaseMachineService.deleteMachine(baseVo); + } + + @ApiOperation("获取租赁系统支持的币种") + @GetMapping("/getSupportCoin") + public Result> getSupportCoin() { + return leaseMachineService.getSupportCoin(); + } + + @ApiOperation("根据币种获取租赁系统支持的算法") + @GetMapping("/getSupportAlgo") + public Result> getSupportAlgo(@RequestParam String coin) { + return leaseMachineService.getSupportAlgo(coin); + } + + + + + +} + diff --git a/src/main/java/com/m2pool/lease/controller/LeaseShopController.java b/src/main/java/com/m2pool/lease/controller/LeaseShopController.java new file mode 100644 index 0000000..38408e7 --- /dev/null +++ b/src/main/java/com/m2pool/lease/controller/LeaseShopController.java @@ -0,0 +1,176 @@ +package com.m2pool.lease.controller; + + +import com.m2pool.lease.annotation.LoginRequired; +import com.m2pool.lease.dto.*; +import com.m2pool.lease.service.LeaseShopService; +import com.m2pool.lease.vo.BaseVo; +import com.m2pool.lease.vo.ShopConfigVo; +import com.m2pool.lease.vo.ShopVo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.List; + +/** + *

+ * 店铺表 前端控制器 + *

+ * + * @author yyb + * @since 2025-08-05 + */ +@Api(tags = "店铺控制器") +@LoginRequired +@RestController +@RequestMapping("/shop") +public class LeaseShopController { + + @Resource + private LeaseShopService leaseShopService; + + /** + * 新增店铺 + * @param shopVo 店铺信息 + * @return 操作结果 + */ + @ApiOperation("新增店铺") + @PostMapping("/addShop") + public Result addShop(@RequestBody ShopVo shopVo) { + + return leaseShopService.addShop(shopVo); + } + + + /** + * 修改店铺 + * @param shopVo 店铺信息 + * @return 操作结果 + */ + @ApiOperation("根据店铺id修改店铺") + @PostMapping("/updateShop") + public Result updateShop(@RequestBody ShopVo shopVo) { + return leaseShopService.updateShop(shopVo); + } + + /** + * 关闭/打开店铺 + * @param baseVo 店铺 ID + * @return 操作结果 + */ + @ApiOperation("根据店铺id关闭店铺") + @PostMapping("/closeShop") + public Result closeShop(@RequestBody BaseVo baseVo) { + return leaseShopService.closeShop(baseVo); + } + + /** + * 根据用户邮箱查询店铺 + * @return 店铺信息 + */ + @ApiOperation("根据用户邮箱查询店铺") + @GetMapping("/getShopByUserEmail") + public Result getShopByUserEmail() { + return leaseShopService.getShopByUserEmail(); + } + + /** + * 根据店铺id查询店铺 + * @param baseVo 店铺 ID + * @return 店铺信息 + */ + @ApiOperation("根据店铺id查询店铺") + @PostMapping("/getShopById") + public Result getShopById(@RequestBody BaseVo baseVo) { + return leaseShopService.getShopById(baseVo); + } + + + + /** + * 删除店铺(逻辑删除) + * @param baseVo 店铺 ID + * @return 操作结果 + */ + @ApiOperation("删除店铺") + @PostMapping("/deleteShop") + public Result deleteShop(@RequestBody BaseVo baseVo) { + return leaseShopService.deleteShop(baseVo); + } + + + + @ApiOperation("根据店铺id获取到商品列表") + @PostMapping("/getProductListById") + public Result> getProductListById(@RequestBody BaseVo baseVo) { + return leaseShopService.getProductListById(baseVo); + } + /** + * 根据店铺id 查询配置信息列表 + * @param baseVo + * @return 操作结果 + */ + @ApiOperation("钱包配置----根据店铺id查询收款钱包绑定信息列表") + @PostMapping("/getShopConfig") + public Result> getShopConfig(@RequestBody BaseVo baseVo) { + return leaseShopService.getShopConfig(baseVo); + } + + /** + * 新增商铺配置 (新增配置时,如果没有指定商品,就默通用配置) + * @param shopConfigVo 商铺配置信息 + * @return 操作结果 + */ + @ApiOperation("钱包配置---新增商铺收款钱包绑定配置(弃用,业务移到/product/updateProductListForShopWalletConfig 接口)") + @PostMapping("/addShopConfig") + @Deprecated + public Result addShopConfig(@RequestBody ShopConfigVo shopConfigVo) { + return leaseShopService.addShopConfig(shopConfigVo); + } + + + + /** + * 修改配置 + * @param shopConfigVo 商铺配置信息 + * @return 操作结果 + */ + @ApiOperation("钱包配置----根据配置id 修改商铺收款钱包配置") + @PostMapping("/updateShopConfig") + public Result updateShopConfig(@RequestBody ShopConfigVo shopConfigVo) { + return leaseShopService.updateShopConfig(shopConfigVo); + } + + /** + * 删除配置(逻辑删除) + * @param baseVo 配置 ID + * @return 操作结果 + */ + @ApiOperation("钱包配置----根据配置id 删除商铺收款钱包配置") + @PostMapping("/deleteShopConfig") + public Result deleteShopConfig(@RequestBody BaseVo baseVo) { + return leaseShopService.deleteShopConfig(baseVo); + } + + @ApiOperation("钱包配置(用于充值选择链和币种)----获取链(一级)和币(二级) 下拉列表(获取本系统支持的链和币种)") + @PostMapping("/getChainAndList") + public Result> getChainAndList(){ + return leaseShopService.getChainAndList(); + } + + @ApiOperation("钱包配置(用于下单选择商家支持的链和币种)----获取链(一级)和币(二级) 下拉列表(获取本商家支持的链和币种)") + @PostMapping("/getChainAndListForSeller") + public Result> getChainAndListForSeller(@RequestBody BaseVo baseVo){ + return leaseShopService.getChainAndListForSeller(baseVo); + } + + + @ApiOperation("钱包配置(用于修改卖家钱包地址)----获取链(一级)和币(二级) 下拉列表(获取本系统支持的链和币种)") + @PostMapping("/getChainAndCoin") + public Result getChainAndCoin(@RequestBody BaseVo baseVo){ + return leaseShopService.getChainAndCoin(baseVo); + } +} + diff --git a/src/main/java/com/m2pool/lease/controller/LeaseShopV2Controller.java b/src/main/java/com/m2pool/lease/controller/LeaseShopV2Controller.java new file mode 100644 index 0000000..00470a3 --- /dev/null +++ b/src/main/java/com/m2pool/lease/controller/LeaseShopV2Controller.java @@ -0,0 +1,79 @@ +package com.m2pool.lease.controller; + + +import com.m2pool.lease.annotation.Decrypt; +import com.m2pool.lease.annotation.LedgerLog; +import com.m2pool.lease.annotation.LoginRequired; +import com.m2pool.lease.dto.PageResult; +import com.m2pool.lease.dto.Result; +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.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.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; + +/** + *

+ * 店铺表 前端控制器 + *

+ * + * @author yyb + * @since 2025-08-05 + */ +@Api(tags = "v2版本--店铺控制器") +@LoginRequired +@RestController +@RequestMapping("/v2/shop") +public class LeaseShopV2Controller { + + @Resource + private LeaseShopService leaseShopService; + + /** + * 根据店铺id 查询配置信息列表 + * @param baseVo + * @return 操作结果 + */ + @ApiOperation("钱包配置----根据店铺id查询收款钱包绑定信息列表") + @PostMapping("/getShopConfigV2") + public Result> getShopConfigV2(@RequestBody BaseVo baseVo) { + return leaseShopService.getShopConfigV2(baseVo); + } + + @LedgerLog(value = "卖家钱包--余额提现申请") + @Decrypt + @PostMapping("/withdrawBalanceForSeller") + @ApiOperation(value = "卖家店铺钱包余额提现") + public Result withdrawBalanceForSeller(@RequestBody BalanceVo balanceVo){ + return leaseShopService.withdrawBalanceForSeller(balanceVo); + } + + @ApiOperation("钱包配置----根据配置id 修改商铺收款钱包配置") + @PostMapping("/updateShopConfigV2") + @Decrypt + public Result updateShopConfigV2(@RequestBody ShopWalletInfoVo shopWalletInfoVo) { + return leaseShopService.updateShopConfigV2(shopWalletInfoVo); + } + + + @PostMapping("/balanceWithdrawList") + @ApiOperation(value = "卖家:余额提现记录列表") + public PageResult balanceWithdrawList(@RequestBody PageVo pageVo){ + return leaseShopService.balanceWithdrawList(pageVo); + } + + + +} + diff --git a/src/main/java/com/m2pool/lease/controller/LeaseShoppingCartController.java b/src/main/java/com/m2pool/lease/controller/LeaseShoppingCartController.java new file mode 100644 index 0000000..2517bfe --- /dev/null +++ b/src/main/java/com/m2pool/lease/controller/LeaseShoppingCartController.java @@ -0,0 +1,79 @@ +package com.m2pool.lease.controller; + + +import com.m2pool.lease.annotation.LoginRequired; +import com.m2pool.lease.dto.PageResult; +import com.m2pool.lease.dto.Result; +import com.m2pool.lease.dto.ShopCartDto; +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 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; + +/** + *

+ * 购物车表 前端控制器 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Api(tags = "购物车表控制器") +@LoginRequired +@RestController +@RequestMapping("/shopping/cart") +public class LeaseShoppingCartController { + + @Resource + private LeaseShoppingCartService leaseShoppingCartService; + + @ApiOperation("添加商品到购物车(批量+单个)") + @PostMapping("/addGoods") + public Result addGoods(@RequestBody List shoppingCartInfoURDVoList) { + return leaseShoppingCartService.addGoods(shoppingCartInfoURDVoList); + } + + + @ApiOperation("查询购物车中商品列表") + @PostMapping("/getGoodsList") + public PageResult getGoodsList(@RequestBody(required = false) PageVo pageVo) { + if (pageVo == null){ + pageVo = new PageVo(); + } + return leaseShoppingCartService.getGoodsList(pageVo); + } + + @ApiOperation("批量删除购物车中商品") + @PostMapping("/deleteBatchGoods") + public Result deleteBatchGoods(@RequestBody List baseVoList) { + return leaseShoppingCartService.deleteBatchGoods(baseVoList); + } + + @ApiOperation("删除购物车中商品") + @PostMapping("/deleteGoods") + @Deprecated + public Result deleteGoods(@RequestBody BaseVo baseVo) { + return leaseShoppingCartService.deleteGoods(baseVo); + } + + + @ApiOperation("批量删除购物车中已下架商品") + @PostMapping("/deleteBatchGoodsForIsDelete") + public Result deleteBatchGoodsForIsDelete() { + return leaseShoppingCartService.deleteBatchGoodsForIsDelete(); + } + + + +} + diff --git a/src/main/java/com/m2pool/lease/controller/LeaseShoppingCartV2Controller.java b/src/main/java/com/m2pool/lease/controller/LeaseShoppingCartV2Controller.java new file mode 100644 index 0000000..c9ce578 --- /dev/null +++ b/src/main/java/com/m2pool/lease/controller/LeaseShoppingCartV2Controller.java @@ -0,0 +1,75 @@ +package com.m2pool.lease.controller; + + +import com.m2pool.lease.annotation.LoginRequired; +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; + +/** + *

+ * 购物车表 前端控制器 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Api(tags = "v2版本---购物车表控制器") +@LoginRequired +@RestController +@RequestMapping("/v2/shopping/cart") +public class LeaseShoppingCartV2Controller { + + @Resource + private LeaseShoppingCartService leaseShoppingCartService; + + @ApiOperation("添加商品到购物车(批量+单个)") + @PostMapping("/addGoodsV2") + public Result addGoodsV2(@RequestBody List addGoodsVoList) { + return leaseShoppingCartService.addGoodsV2(addGoodsVoList); + } + + @ApiOperation("查询购物车中商品列表") + @PostMapping("/getGoodsListV2") + public PageResult getGoodsListV2(@RequestBody(required = false) PageVo pageVo) { + if (pageVo == null){ + pageVo = new PageVo(); + } + return leaseShoppingCartService.getGoodsListV2(pageVo); + } + + + @ApiOperation("批量删除购物车中商品") + @PostMapping("/deleteBatchGoodsV2") + public Result deleteBatchGoodsV2(@RequestBody List baseVoList) { + return leaseShoppingCartService.deleteBatchGoodsV2(baseVoList); + } + + + @ApiOperation("批量删除购物车中已下架商品") + @PostMapping("/deleteBatchGoodsForIsDeleteV2") + public Result deleteBatchGoodsForIsDeleteV2() { + return leaseShoppingCartService.deleteBatchGoodsForIsDeleteV2(); + } + + + +} + diff --git a/src/main/java/com/m2pool/lease/controller/LeaseUserController.java b/src/main/java/com/m2pool/lease/controller/LeaseUserController.java new file mode 100644 index 0000000..e0da72a --- /dev/null +++ b/src/main/java/com/m2pool/lease/controller/LeaseUserController.java @@ -0,0 +1,116 @@ +package com.m2pool.lease.controller; + + +import com.m2pool.lease.annotation.Decrypt; +import com.m2pool.lease.annotation.LedgerLog; +import com.m2pool.lease.annotation.LoginRequired; +import com.m2pool.lease.dto.*; +import com.m2pool.lease.service.LeaseUserService; +import com.m2pool.lease.vo.BalancePageVo; +import com.m2pool.lease.vo.BalanceVo; +import com.m2pool.lease.vo.ChainAndCoinVo; +import com.m2pool.lease.vo.RecordTypePageVo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + *

+ * 用户表 前端控制器 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Api(tags = "用户信息控制器") +@LoginRequired +@RestController +@RequestMapping("/user") +public class LeaseUserController { + + @Resource + private LeaseUserService leaseUserService; + + + @PostMapping("/bindWallet") + @ApiOperation(value = "买家:充值步骤选择链和钱包后自动绑定钱包") + public Result bindWallet(@RequestBody ChainAndCoinVo chainAndCoinVo){ + return leaseUserService.bindWallet(chainAndCoinVo); + } + + @PostMapping("/getWalletInfo") + @ApiOperation(value = "获取用户相关钱包信息") + public Result> getWalletInfo() { + return leaseUserService.getWalletInfo(); + } + + + @LedgerLog(value = "买家钱包--余额提现申请") + @Decrypt + @PostMapping("/withdrawBalance") + @ApiOperation(value = "申请余额提现") + public Result withdrawBalance(@RequestBody BalanceVo balanceVo){ + return leaseUserService.withdrawBalance(balanceVo); + } + + + @PostMapping("/balanceWithdrawList") + @ApiOperation(value = "买家:余额提现记录列表") + @Deprecated + public PageResult balanceWithdrawList(@RequestBody BalancePageVo balancePageVo){ + return leaseUserService.balanceWithdrawList(balancePageVo); + } + + + @PostMapping("/balanceRechargeList") + @ApiOperation(value = "买家:余额充值记录列表") + @Deprecated + public PageResult balanceRechargeList(@RequestBody BalancePageVo balancePageVo){ + return leaseUserService.balanceRechargeList(balancePageVo); + } + + @PostMapping("/balancePayList") + @ApiOperation(value = "卖家:钱包交易(收款)记录列表") + public PageResult balancePayList(@RequestBody BalancePageVo balancePageVo){ + return leaseUserService.balancePayList(balancePageVo); + } + + @PostMapping("/transactionRecord") + @ApiOperation(value = "买家:交易流水(支付,提现,充值)") + public PageResult transactionRecord(@RequestBody RecordTypePageVo recordTypePageVo){ + return leaseUserService.transactionRecord(recordTypePageVo); + } + + @PostMapping("/getRecentlyTransaction") + @ApiOperation(value = "最近5条交易") + public Result> getRecentlyTransaction(){ + return leaseUserService.getRecentlyTransaction(); + } + + + @PostMapping("/getCharge") + @ApiOperation(value = "获取系统支持的交易手续费") + @Deprecated + public Result> getCharge(){ + return leaseUserService.getCharge(); + } + + @GetMapping("/downloadClient") + @ApiOperation(value = "卖家:下载客户端") + public void downloadClient(@RequestParam String userEmail,@RequestParam String type,HttpServletRequest request, HttpServletResponse response){ + leaseUserService.downloadClient(userEmail,type,request, response); + } + + @GetMapping("/getClientVersion") + @ApiOperation(value = "获取客户端版本号") + public String getClientVersion(){ + return leaseUserService.getClientVersion(); + } + +} + diff --git a/src/main/java/com/m2pool/lease/dto/ChainAndCoinDto.java b/src/main/java/com/m2pool/lease/dto/ChainAndCoinDto.java new file mode 100644 index 0000000..afa4007 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/ChainAndCoinDto.java @@ -0,0 +1,58 @@ +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; + +import java.math.BigDecimal; +import java.util.Objects; + +/** + *

+ * 余额提现请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ChainAndCoinDto { + + + private String coin; + private String chain; + private Long shopId; + + public ChainAndCoinDto( String coin,String chain) { + this.chain = chain; + this.coin = coin; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ChainAndCoinDto that = (ChainAndCoinDto) o; + return Objects.equals(coin, that.coin) && Objects.equals(chain, that.chain) && Objects.equals(shopId, that.shopId); + } + + @Override + public int hashCode() { + return Objects.hash(coin, chain,shopId); + } + + @Override + public String toString() { + return "ChainAndCoinDto{" + + "coin='" + coin + '\'' + + ", chain='" + chain + '\'' + + ", shopId='" + shopId + '\'' + + '}'; + } +} diff --git a/src/main/java/com/m2pool/lease/dto/ChainListDto.java b/src/main/java/com/m2pool/lease/dto/ChainListDto.java new file mode 100644 index 0000000..224e07e --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/ChainListDto.java @@ -0,0 +1,53 @@ +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; + +import java.util.List; + +/** + * @Description 币种实时收益实体类 + * @Date 2024/6/14 15:57 + * @Author dy + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@ApiModel(description = "链(一级) 下拉列表返回对象",value = "ChainListDto") +public class ChainListDto { + + + @ApiModelProperty(value = "链名value") + private String value; + + @ApiModelProperty(value = "地址") + private String address; + + @ApiModelProperty(value = "联名标签") + private String label; + + @ApiModelProperty(value = "币(二级) 下拉列表") + private List children; + + + @Data + @ApiModel(description = "币(二级) 下拉列表返回对象",value = "CoinListDto") + public static class CoinListDto{ + @ApiModelProperty(value = "币种value") + private String value; + + @ApiModelProperty(value = "币种label") + private String label; + + @ApiModelProperty(value = "币种是否绑定(只用于卖家钱包绑定页面相关接口) 0 未绑定 1 已绑定") + private Integer hasBind; + } + + +} diff --git a/src/main/java/com/m2pool/lease/dto/ChargeDto.java b/src/main/java/com/m2pool/lease/dto/ChargeDto.java new file mode 100644 index 0000000..3e214d3 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/ChargeDto.java @@ -0,0 +1,39 @@ +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; + +import java.math.BigDecimal; + +/** + *

+ * 余额提现请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "交易费接口",value = "ChargeDto") +public class ChargeDto { + + @ApiModelProperty(value = "手续费",required = true) + private BigDecimal amount; + + @ApiModelProperty(value = "链",required = true) + private String chain; + + @ApiModelProperty(value = "币种",required = true) + private String coin; + + @ApiModelProperty(value = "手续费率",required = true) + private BigDecimal feeRate; + +} diff --git a/src/main/java/com/m2pool/lease/dto/CheckAddressDto.java b/src/main/java/com/m2pool/lease/dto/CheckAddressDto.java new file mode 100644 index 0000000..3d7de21 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/CheckAddressDto.java @@ -0,0 +1,35 @@ +package com.m2pool.lease.dto; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * @Description 币种实时收益实体类 + * @Date 2024/6/14 15:57 + * @Author dy + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class CheckAddressDto { + + /** + * 地址 + */ + private String fromAddress; + + private String fromChain; + + private String fromSymbol; + /** + * 半年内是否有操作 + */ + private String hasOperator; +} diff --git a/src/main/java/com/m2pool/lease/dto/CoinFullDto.java b/src/main/java/com/m2pool/lease/dto/CoinFullDto.java new file mode 100644 index 0000000..90474c0 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/CoinFullDto.java @@ -0,0 +1,32 @@ +package com.m2pool.lease.dto; + + +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Description 币种实时收益实体类 + * @Date 2024/6/14 15:57 + * @Author dy + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class CoinFullDto { + + private String chainValue; + + private String chainLabel; + + + private String coinValue; + + private String coinLabel; + + private Integer hasBind; + +} diff --git a/src/main/java/com/m2pool/lease/dto/HasPayTotalDto.java b/src/main/java/com/m2pool/lease/dto/HasPayTotalDto.java new file mode 100644 index 0000000..95c3376 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/HasPayTotalDto.java @@ -0,0 +1,38 @@ +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; + +import java.math.BigDecimal; + +/** + *

+ * 支付订单返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "已支付和理论支付总金额返回对象",value = "HasPayTotalDto") +public class HasPayTotalDto { + + + /** + * 理论支付金额 + */ + private BigDecimal totalAmount; + + /** + * 实际支付金额 + */ + private BigDecimal totalRealAmount; + +} diff --git a/src/main/java/com/m2pool/lease/dto/HourIncomeDto.java b/src/main/java/com/m2pool/lease/dto/HourIncomeDto.java new file mode 100644 index 0000000..a5a315a --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/HourIncomeDto.java @@ -0,0 +1,47 @@ +package com.m2pool.lease.dto; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * @Description 币种实时收益实体类 + * @Date 2024/6/14 15:57 + * @Author dy + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class HourIncomeDto{ + + private Long id; + + /** + * 挖矿账户 + */ + private String user; + + /** + * 矿工编号 + */ + private String miner; + + /** + * 挖矿收益nexa 币 + */ + private BigDecimal income; + + /** + * 挖矿收益 稳定币usdt + */ + private BigDecimal usdtIncome; + + private LocalDateTime createTime; +} diff --git a/src/main/java/com/m2pool/lease/dto/MachinePayTypeDto.java b/src/main/java/com/m2pool/lease/dto/MachinePayTypeDto.java new file mode 100644 index 0000000..6801ffa --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/MachinePayTypeDto.java @@ -0,0 +1,41 @@ +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; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + *

+ * 矿机售价配置返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "矿机售价配置返回对象",value = "MachinePayTypeDto" ) +public class MachinePayTypeDto { + @ApiModelProperty(value = "矿机币种售价配置id") + private Long payTypeId; + + @ApiModelProperty(value = "矿机id(忽略)") + private Long productMachineId; + + @ApiModelProperty(value = "售价") + private BigDecimal price; + + @ApiModelProperty(value = "链") + private String chain; + + @ApiModelProperty(value = "售价单位币种") + private String coin; +} diff --git a/src/main/java/com/m2pool/lease/dto/MachinePowerDto.java b/src/main/java/com/m2pool/lease/dto/MachinePowerDto.java new file mode 100644 index 0000000..6437d4a --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/MachinePowerDto.java @@ -0,0 +1,61 @@ +package com.m2pool.lease.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + *

+ * 已售商品矿工实时算力表 + *

+ * + * @author yyb + * @since 2025-07-25 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class MachinePowerDto{ + + /** + * ID + */ + private Long id; + + /** + * 挖矿账户 + */ + private String user; + + /** + * 矿工 + */ + private String miner; + + /** + * 记录时间(每五分钟整点) + */ + private LocalDateTime date; + + /** + * 五分钟一次的矿工矿机算力 + */ + private BigDecimal accepts; + + /** + * 矿机在离线状态 离线offline 在线online + */ + private String state; + + /** + * 实际记录时间 + */ + private LocalDateTime lastSubmit; + + +} diff --git a/src/main/java/com/m2pool/lease/dto/MachineTotalPriceDto.java b/src/main/java/com/m2pool/lease/dto/MachineTotalPriceDto.java new file mode 100644 index 0000000..503b851 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/MachineTotalPriceDto.java @@ -0,0 +1,34 @@ +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; + +import java.math.BigDecimal; + +/** + *

+ * 矿机售价配置返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "购物车矿机总价返回对象",value = "MachineTotalPriceDto" ) +public class MachineTotalPriceDto { + @ApiModelProperty(value = "售价") + private BigDecimal price; + + @ApiModelProperty(value = "链") + private String chain; + + @ApiModelProperty(value = "售价单位币种") + private String coin; +} diff --git a/src/main/java/com/m2pool/lease/dto/OrderInfoDto.java b/src/main/java/com/m2pool/lease/dto/OrderInfoDto.java new file mode 100644 index 0000000..8262a30 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/OrderInfoDto.java @@ -0,0 +1,102 @@ +package com.m2pool.lease.dto; + +import io.swagger.annotations.Api; +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; +import java.util.List; + +/** + *

+ * 订单详情返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "订单详情返回对象",value = "OrderInfoDto") +public class OrderInfoDto { + + /** + * 订单 ID + */ + @ApiModelProperty(value = "订单 ID") + private Long id; + + @ApiModelProperty(value = "店铺id") + private Long shopId; + + /** + * 订单号 + */ + @ApiModelProperty(value = "订单号") + private String orderNumber; + + /** + * 用户id + */ + @ApiModelProperty(value = "用户id") + private String userId; + /** + * 订单总价 + */ + @ApiModelProperty(value = "订单总价") + private BigDecimal totalPrice; + + ///** + // * 收货地址 + // */ + //private String address; + + /** + * 订单状态 0待付款 1待发货 2待收货 3待评价 4已完成 5取消订单 + */ + @ApiModelProperty(value = "订单状态 7 订单进行中 8 订单已完成") + private Integer status; + + /** + * 创建时间 + */ + @ApiModelProperty(value = "创建时间") + private LocalDateTime createTime; + + + @ApiModelProperty(value = "订单完成时间") + private LocalDateTime endTime; + + + // ---------------------------------------------- 一个普通订单对应一个支付订单(多个封装下面四个字段为一个对象,并返回list)---------------------------------------------------------------------- + + + @ApiModelProperty(value = "二维码图片") + private String img; + + @ApiModelProperty(value = "需支付总金额") + private BigDecimal amount; + + @ApiModelProperty(value = "已支付金额") + private String payAmount; + + @ApiModelProperty(value = "未支付金额") + private Double noPayAmount; + + @ApiModelProperty(value = "店铺名称") + private String shopName; + + //---------------------------------------------- 一个普通订单对应一个支付订单(多个封装下面四个字段为一个对象,并返回list)- ---------------------------------------------------------------------- + /** + * 订单详情 + */ + @ApiModelProperty(value = "订单详情") + private List orderItemDtoList; +} diff --git a/src/main/java/com/m2pool/lease/dto/OrderItemDto.java b/src/main/java/com/m2pool/lease/dto/OrderItemDto.java new file mode 100644 index 0000000..b2e3c06 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/OrderItemDto.java @@ -0,0 +1,92 @@ +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; + +import java.math.BigDecimal; + +/** + *

+ * 订单列表返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "订单列表返回对象",value = "LeaseOrderItemDto") +public class OrderItemDto { + + /** + * 订单 ID + */ + @ApiModelProperty(value = "订单 ID") + private Long orderId; + + @ApiModelProperty(value = "店铺id") + private Long shopId; + + /** + * 商品 ID + */ + @ApiModelProperty(value = "商品 ID") + private Long productId; + + /** + * 机器id + */ + @ApiModelProperty(value = "机器id") + private Long productMachineId; + + /** + * 商品租期天数 + */ + @ApiModelProperty(value = "商品租期天数") + private Integer leaseTime; + + + /** + * 商品名称 + */ + @ApiModelProperty(value = "商品名称") + private String name; + /** + * 商品图片路径 + */ + @ApiModelProperty(value = "商品图片路径") + private String image; + + + /** + * 收货地址(模拟) + */ + @ApiModelProperty(value = "收货地址(模拟)") + private String address; + + + @ApiModelProperty(value = "机器单价") + private BigDecimal price; + + /** + * 支付币种 + */ + @ApiModelProperty(value = "支付币种") + private String payCoin; + + + @ApiModelProperty(value = "租赁矿机数量") + private Integer numbers; + + @ApiModelProperty(value = "矿机类型 0 ASIC 1 GPU") + private Integer type; + + + +} diff --git a/src/main/java/com/m2pool/lease/dto/OrderStatusDto.java b/src/main/java/com/m2pool/lease/dto/OrderStatusDto.java new file mode 100644 index 0000000..98e7308 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/OrderStatusDto.java @@ -0,0 +1,36 @@ +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; + +import java.math.BigDecimal; + +/** + *

+ * 订单状态修改返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "订单状态修改返回对象",value = "OrderStatusDto") +public class OrderStatusDto { + + /** + * 订单 ID + */ + @ApiModelProperty(value = "订单 ID") + private Long orderId; + + + @ApiModelProperty(value = "订单主表状态") + private Integer orderInfoStatus; +} diff --git a/src/main/java/com/m2pool/lease/dto/PageResult.java b/src/main/java/com/m2pool/lease/dto/PageResult.java new file mode 100644 index 0000000..d46b4fb --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/PageResult.java @@ -0,0 +1,117 @@ +package com.m2pool.lease.dto; + +import com.github.pagehelper.PageInfo; +import com.m2pool.lease.constant.ResponseConstant; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * @Description 表格分页数据对象 + * @Date 2024/6/13 11:59 + * @Author dy + */ +@Data +@NoArgsConstructor +@ApiModel(description = "分页返回对象") +public class PageResult implements Serializable +{ + private static final long serialVersionUID = 1L; + + + /** 成功 */ + public static final int SUCCESS = ResponseConstant.SUCCESS; + + /** 失败 */ + public static final int FAIL = ResponseConstant.FAIL; + + /** 总记录数 */ + @ApiModelProperty(value = "总记录数", example = "1") + private long total; + + /** 总页数 */ + @ApiModelProperty(value = "总页数", example = "1") + private long totalPage; + + /** 列表数据 */ + @ApiModelProperty(value = "查询结果集合") + private List rows; + + /** 消息状态码 */ + @ApiModelProperty(value = "消息状态码", example = "200") + private int code; + + /** 消息内容 */ + @ApiModelProperty(value = "查询结果描述", example = "成功") + private String msg; + + @ApiModelProperty(value = "表头信息") + private List columns; + + public static PageResult success(List data) + { + return setPageResult(data, SUCCESS, "成功"); + } + + public static PageResult successColumns(List data) { + return setPageResultAndHeader(data, SUCCESS, "成功"); + } + + public static PageResult fail(List data, String msg) + { + return setPageResult(data, FAIL, msg); + } + + private static PageResult setPageResult(List data, int code, String msg) + { + PageResult rspData = new PageResult(); + rspData.setCode(SUCCESS); + rspData.setRows(data); + rspData.setMsg("查询成功"); + return rspData; + } + private static PageResult setPageResultAndHeader(List data, int code, String msg) { + List 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 rspData = new PageResult(); + rspData.setColumns(columns); + rspData.setCode(SUCCESS); + rspData.setRows(data); + rspData.setMsg("查询成功"); + return rspData; + } +} diff --git a/src/main/java/com/m2pool/lease/dto/PayConfigDto.java b/src/main/java/com/m2pool/lease/dto/PayConfigDto.java new file mode 100644 index 0000000..263541a --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/PayConfigDto.java @@ -0,0 +1,54 @@ +package com.m2pool.lease.dto; + +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; + +/** + *

+ * 店铺商品配置返回对象 + *

+ * + * @author yyb + * @since 2025-08-05 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "商品收款钱包配置",value = "PayConfigDto") +public class PayConfigDto { + + + /** + * nexa rxd dgbo dgbq dgbs alph enx grs mona usdt + */ + @ApiModelProperty(value = "支持的支付币种") + private String payCoin; + + + /** + * 卖方对应收款钱包 + */ + @ApiModelProperty(value = "币种图标") + private String payCoinImage; + + + @ApiModelProperty(value = "链") + private String payChain; + + + private Long shopId; + + @ApiModelProperty(value = "起付金额") + private BigDecimal deductibleAmount; + + @ApiModelProperty(value = "手续费") + private BigDecimal fee; +} diff --git a/src/main/java/com/m2pool/lease/dto/PayOrderDto.java b/src/main/java/com/m2pool/lease/dto/PayOrderDto.java new file mode 100644 index 0000000..3b431fb --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/PayOrderDto.java @@ -0,0 +1,48 @@ +package com.m2pool.lease.dto; + +import io.swagger.annotations.Api; +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; + +/** + *

+ * 支付订单返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "支付订单返回对象",value = "PayOrderDto") +public class PayOrderDto { + + /** + * 支付方式,nexa rxd dgbo dgbq dgbs alph enx grs mona usdt usdc busd + */ + @ApiModelProperty(value = "支付方式,nexa rxd dgbo dgbq dgbs alph enx grs mona usdt usdc busd") + private String payCoin; + + /** + * 支付金额 + */ + @ApiModelProperty(value = "支付金额") + private BigDecimal amount; + + + /** + * 支付地址 + */ + @ApiModelProperty(value = "支付地址") + private String payAddress; + +} diff --git a/src/main/java/com/m2pool/lease/dto/PayRechargeMessageDto.java b/src/main/java/com/m2pool/lease/dto/PayRechargeMessageDto.java new file mode 100644 index 0000000..b4f0dd7 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/PayRechargeMessageDto.java @@ -0,0 +1,78 @@ +package com.m2pool.lease.dto; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.*; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + *

+ * 充值记录表 + *

+ * + * @author yyb + * @since 2025-09-10 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "余额充值返回对象",value = "PayRechargeMessageDto" ) +public class PayRechargeMessageDto { + + + + private Long id; + + + /** + * 充值地址 + */ + @ApiModelProperty(value = "充值地址(官方自动生成绑定地址)") + private String address; + + /** + * 支付金额 + */ + @ApiModelProperty(value = "充值金额") + private BigDecimal amount; + + + /** + * 币种 + */ + @ApiModelProperty(value = "币种") + private String fromSymbol; + + /** + * 链名称 + */ + @ApiModelProperty(value = "链名称") + private String fromChain; + + /** + * 充值时间 + */ + @ApiModelProperty(value = "充值到账时间)") + private LocalDateTime createTime; + + + /** + * 0 充值失败 1 充值成功 2 充值中 + */ + @ApiModelProperty(value = "充值状态 0 充值失败 1 充值成功 2 充值中 3 校验失败") + private Integer status; + + /** + * 交易hash + */ + @ApiModelProperty(value = "交易hash") + private String txHash; + + +} diff --git a/src/main/java/com/m2pool/lease/dto/PayRecordMessageDto.java b/src/main/java/com/m2pool/lease/dto/PayRecordMessageDto.java new file mode 100644 index 0000000..fb28e05 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/PayRecordMessageDto.java @@ -0,0 +1,112 @@ +package com.m2pool.lease.dto; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.*; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + *

+ * 支付消息记录表 + *

+ * + * @author yyb + * @since 2025-09-10 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "支付返回对象",value = "PayRecordMessageDto" ) +public class PayRecordMessageDto { + + + /** + * 消息ID + */ + @ApiModelProperty(value = "消息ID") + private String queueId; + + /** + * 买家充值地址 + */ + @ApiModelProperty(value = "买家钱包地址") + private String fromAddress; + + /** + * 卖家地址(卖家自定义地址) + */ + @ApiModelProperty(value = "收款地址(卖家自定义地址)") + private String toAddress; + + /** + * 支付金额 + */ + @ApiModelProperty(value = "理论支付支付金额") + private BigDecimal amount; + + /** + *实际支付金额(根据一天内预估算力和实际算力差值计算得来,并且已经真实支付成功的金额) + */ + @ApiModelProperty(value = "实际支付金额(根据一天内预估算力和实际算力差值计算得来,并且已经真实支付成功的金额)") + private BigDecimal realAmount; + /** + * 卖家钱包币种 + */ + @ApiModelProperty(value = "收款钱包币种") + private String toSymbol; + + /** + * 卖家钱包链名称 + */ + @ApiModelProperty(value = "收款钱包链名称") + private String toChain; + + + /** + * 买家钱包币种 + */ + @ApiModelProperty(value = "买家钱包币种") + private String fromSymbol; + + /** + * 买家钱包链名称 + */ + @ApiModelProperty(value = "买家钱包链名称") + private String fromChain; + + /** + * 交易hash + */ + @ApiModelProperty(value = "交易hash") + private String txHash; + + /** + * 订单号 + */ + @ApiModelProperty(value = "订单号") + private String orderId; + + /** + * 支付时间 + */ + @ApiModelProperty(value = "支付时间") + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @ApiModelProperty(value = "支付成功/失败/证书校验失败时间") + private LocalDateTime updateTime; + + /** + * 0 支付失败 1 支付成功 2 等待校验 3 证书校验失败 + */ + @ApiModelProperty(value = "支付状态 0 支付失败 1 支付成功 2 待校验 3 校验失败") + private Integer status; +} diff --git a/src/main/java/com/m2pool/lease/dto/PayTypeDto.java b/src/main/java/com/m2pool/lease/dto/PayTypeDto.java new file mode 100644 index 0000000..01b5b64 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/PayTypeDto.java @@ -0,0 +1,37 @@ +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; + +import java.util.List; + +/** + * @Description 店铺支持的支付方式 + * @Date 2024/6/14 15:57 + * @Author dy + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@ApiModel(description = "店铺支持的支付方式",value = "PayTypeDto") +public class PayTypeDto { + + @ApiModelProperty(value = "店铺id(不展示)") + private Long shopId; + + @ApiModelProperty(value = "链") + private String chain; + + @ApiModelProperty(value = "支付币种") + private String coin; + + @ApiModelProperty(value = "支付币种图标") + private String image; + +} diff --git a/src/main/java/com/m2pool/lease/dto/PayWithdrawMessageDto.java b/src/main/java/com/m2pool/lease/dto/PayWithdrawMessageDto.java new file mode 100644 index 0000000..7789f58 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/PayWithdrawMessageDto.java @@ -0,0 +1,98 @@ +package com.m2pool.lease.dto; + +import com.alibaba.nacos.shaded.org.checkerframework.checker.units.qual.A; +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; + +/** + *

+ * 余额提现返回对象 + *

+ * + * @author yyb + * @since 2025-09-10 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "余额提现返回对象",value = "PayWithdrawMessageDto" ) +public class PayWithdrawMessageDto { + + private Long id; + + + /** + * 用户充值地址 + */ + @ApiModelProperty(value = "用户充值地址") + private String fromAddress; + + /** + * 提现地址(用户自定义) + */ + @ApiModelProperty(value = "提现地址(用户自定义)") + private String toAddress; + + /** + * 支付金额 + */ + @ApiModelProperty(value = "提现金额") + private BigDecimal amount; + + /** + * 币种 + */ + @ApiModelProperty(value = "币种") + private String toSymbol; + + /** + * 链名称 + */ + @ApiModelProperty(value = "链名称") + private String toChain; + + + /** + * 币种 + */ + @ApiModelProperty(value = "币种") + private String fromSymbol; + + /** + * 链名称 + */ + @ApiModelProperty(value = "链名称") + private String fromChain; + + /** + * 充值时间 + */ + @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; +} diff --git a/src/main/java/com/m2pool/lease/dto/PaymentCallbackDto.java b/src/main/java/com/m2pool/lease/dto/PaymentCallbackDto.java new file mode 100644 index 0000000..8b76b01 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/PaymentCallbackDto.java @@ -0,0 +1,42 @@ +package com.m2pool.lease.dto; + +import io.swagger.annotations.Api; +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; + +/** + *

+ * 支付回调结果返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "支付回调结果返回对象",value = "PaymentCallbackDto") +public class PaymentCallbackDto { + + @ApiModelProperty(value = "本次支付是否支付完成 ---- 支付状态0支付失败 1支付成功--全部货款已支付 2待支付 5支付已超时 10支付成功--已支付部分货款 ") + private int isPayAll; + + @ApiModelProperty(value = "剩余未支付的金额") + private BigDecimal noPayAmount; + + @ApiModelProperty(value = "需要支付的总金额 = payAmount + noPayAmount") + private BigDecimal totalAmount; + + @ApiModelProperty(value = "已支付的金额(从卖方钱包交易记录中获取的)") + private BigDecimal payAmount; + + @ApiModelProperty(value = "卖方二维码图片(如果支付状态为 1 则不再返回该字段)") + private String img; +} diff --git a/src/main/java/com/m2pool/lease/dto/PaymentRecordDto.java b/src/main/java/com/m2pool/lease/dto/PaymentRecordDto.java new file mode 100644 index 0000000..8ec7c59 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/PaymentRecordDto.java @@ -0,0 +1,59 @@ +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; + +import java.math.BigDecimal; + +/** + *

+ * 支付记录返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "支付记录返回对象",value = "PaymentRecordDto") +public class PaymentRecordDto { + + /** + * 支付记录 ID + */ + @ApiModelProperty(value = "支付记录 ID") + private Long id; + + /** + * 支付方式,nexa rxd dgbo dgbq dgbs alph enx grs mona usdt usdc busd + */ + @ApiModelProperty(value = "支付方式,nexa rxd dgbo dgbq dgbs alph enx grs mona usdt usdc busd") + private String payCoin; + + /** + * 支付金额 + */ + @ApiModelProperty(value = "支付金额") + private BigDecimal amount; + + /** + * 支付状态0支付失败 1支付成并验证 2待支付 10表示支付成功未验证 + */ + @ApiModelProperty(value = "支付状态0支付失败 1支付成功--全部货款已支付 2待支付 5支付已超时 6 支付中( 目前只有这三种状态 7 订单进行中 8 订单已完成 9 余额不足,订单已取消) 10支付成功--已支付部分货款") + private Integer status; + + /** + * 支付地址 + */ + @ApiModelProperty(value = "支付地址(卖方)") + private String payAddress; + + @ApiModelProperty(value = "二维码图片") + private String img; +} diff --git a/src/main/java/com/m2pool/lease/dto/PriceDto.java b/src/main/java/com/m2pool/lease/dto/PriceDto.java new file mode 100644 index 0000000..2bb5354 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/PriceDto.java @@ -0,0 +1,41 @@ +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; + +import java.math.BigDecimal; +import java.util.List; + +/** + *

+ * 价格范围返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "价格范围返回对象",value = "PriceDto" ) +public class PriceDto { + + + + @ApiModelProperty(value = "库存") + private Integer totalMachineNumber; + + @ApiModelProperty(value = "价格最大值") + private BigDecimal priceMax; + + + @ApiModelProperty(value = "价格最小值") + private BigDecimal priceMin; + + +} diff --git a/src/main/java/com/m2pool/lease/dto/ProductDto.java b/src/main/java/com/m2pool/lease/dto/ProductDto.java new file mode 100644 index 0000000..bc30aa8 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/ProductDto.java @@ -0,0 +1,114 @@ +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; + +import java.math.BigDecimal; +import java.util.List; + +/** + *

+ * 商品返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "商品返回对象",value = "ProductDto" ) +public class ProductDto { + + @ApiModelProperty(value = "商品id") + private Long id; + /** + * 店铺id + */ + @ApiModelProperty(value = "店铺id") + private Long shopId; + + @ApiModelProperty(value = "店铺名称") + private String shopName; + + /** + * 商品名称 + */ + @ApiModelProperty(value = "商品名称") + private String name; + + /** + * 商品图片路径 + */ + @ApiModelProperty(value = "商品图片路径") + private String image; + + /** + * 商品类型,0 挖矿机器套餐,1 算力套餐 + */ + @ApiModelProperty(value = "商品类型,0 挖矿机器套餐,1 算力套餐") + private Integer type; + + /** + * 上下架状态,0 上架,1 下架 + */ + @ApiModelProperty(value = "上下架状态,0 上架,1 下架") + private Integer state; + + + + /** + * 库存中机器价格范围 + */ + @ApiModelProperty(value = "价格范围") + @Deprecated + private String priceRange; + + @ApiModelProperty(value = "店铺支持的支付方式") + private List payTypes; + + /** + * 算法 + */ + @ApiModelProperty(value = "算法") + private String algorithm; + + /** + * 商品描述 + */ + @ApiModelProperty(value = "商品描述") + private String description; + /** + * 矿机挖矿币种 nexa rxd dgbo dgbq dgbs alph enx grs mona + */ + @ApiModelProperty(value = "矿机挖矿币种 nexa rxd dgbo dgbq dgbs alph enx grs mona") + private String coin; + + /** + * 矿机挖矿币种全称 + */ + @ApiModelProperty(value = "矿机挖矿币种全称") + private String coinFullName; + + + /** + * 销售机器数 + */ + @ApiModelProperty(value = "已售矿机数") + private Integer saleNumber; + + /** + * 总矿机数 + */ + @ApiModelProperty(value = "总矿机数(已售 + 未售出矿机)") + private Integer totalMachineNumber; + + + @ApiModelProperty(value = "商品对应的出售矿机集合") + List productMachineRangeList; +} diff --git a/src/main/java/com/m2pool/lease/dto/ProductForWalletConfigDto.java b/src/main/java/com/m2pool/lease/dto/ProductForWalletConfigDto.java new file mode 100644 index 0000000..a2c331a --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/ProductForWalletConfigDto.java @@ -0,0 +1,71 @@ +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; + +import java.util.List; + +/** + *

+ * 钱包配置修改商品支付方式返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "钱包配置修改商品支付方式返回对象",value = "ProductForWalletConfigDto" ) +public class ProductForWalletConfigDto { + + @ApiModelProperty(value = "商品id") + private Long productId; + /** + * 商品名称 + */ + @ApiModelProperty(value = "商品名称") + private String name; + + /** + * 商品图片路径 + */ + @ApiModelProperty(value = "商品图片路径") + private String image; + + /** + * 上下架状态,0 上架,1 下架 + */ + @ApiModelProperty(value = "上下架状态,0 上架,1 下架") + private Integer state; + + /** + * 算法 + */ + @ApiModelProperty(value = "算法") + private String algorithm; + + /** + * 商品描述 + */ + @ApiModelProperty(value = "商品描述") + private String description; + /** + * 矿机挖矿币种 nexa rxd dgbo dgbq dgbs alph enx grs mona + */ + @ApiModelProperty(value = "矿机挖矿币种 nexa rxd dgbo dgbq dgbs alph enx grs mona") + private String coin; + /** + * 总矿机数 + */ + @ApiModelProperty(value = "总矿机数(已售 + 未售出矿机)") + private Integer totalMachineNumber; + + @ApiModelProperty(value = "矿机列表") + private List machineList; +} diff --git a/src/main/java/com/m2pool/lease/dto/ProductMachineDto.java b/src/main/java/com/m2pool/lease/dto/ProductMachineDto.java new file mode 100644 index 0000000..75ac525 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/ProductMachineDto.java @@ -0,0 +1,113 @@ +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; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +/** + *

+ * 商品对应实际商品返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "商品对应实际商品返回对象",value = "ProductMachineDto" ) +public class ProductMachineDto { + + @ApiModelProperty(value = "矿机ID") + private Long id; + + @ApiModelProperty(value = "购物车详情id") + private Long cartInfoId; + + @ApiModelProperty(value = "店铺id") + private Long shopId; + + @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; + + @ApiModelProperty(value = "上下架状态,0 上架,1 下架") + private Integer state; + + @ApiModelProperty(value = "售出状态 0未售出 1已售出 2售出中") + private Integer saleState; + + @ApiModelProperty(value = "机器算力最早记录的一次时间(和endTime相减得到商品上实时算力平均值计算的时间段)") + private LocalDateTime startTime; + + @ApiModelProperty(value = "机器算力最近记录的一次时间") + private LocalDateTime endTime; + + + @ApiModelProperty(value = "单机理论收入(每日) 单位币种") + private BigDecimal theoryIncome; + + @ApiModelProperty(value = "单机理论收入(每日) 单位USDT") + private BigDecimal theoryUsdtIncome; + + @ApiModelProperty(value = "租赁天数") + private Integer leaseTime; + + @ApiModelProperty(value = "算法") + private String algorithm; + + @ApiModelProperty(value = "功耗 单位kw/h",example = "10") + private BigDecimal powerDissipation; + + @ApiModelProperty(value = "最大可租借天数(默认七天)",example = "7") + private Integer maxLeaseDays; + + @ApiModelProperty(value = "挖矿机器挖矿币种") + private String coin; + + @ApiModelProperty(value = "商品名称") + private String name; + + @ApiModelProperty(value = "是否删除 0否 1是") + private Integer del; + + @ApiModelProperty(value = "支付币种") + private String payCoin; + + + @ApiModelProperty(value = "售价集合") + private List priceList; +} diff --git a/src/main/java/com/m2pool/lease/dto/ProductMachineForWalletConfigDto.java b/src/main/java/com/m2pool/lease/dto/ProductMachineForWalletConfigDto.java new file mode 100644 index 0000000..4874c18 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/ProductMachineForWalletConfigDto.java @@ -0,0 +1,105 @@ +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; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + *

+ * 商品对应实际商品返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "钱包配置修改矿机支付方式返回对象",value = "ProductMachineForWalletConfigDto" ) +public class ProductMachineForWalletConfigDto { + + @ApiModelProperty(value = "矿机ID") + private Long productMachineId; + + @ApiModelProperty(value = "商品ID") + private Long productId; + + /** + * 挖矿机器 对应的矿工账号 + */ + @ApiModelProperty(value = "挖矿机器 对应的矿工账号") + private String user; + + /** + * 挖矿机器型号 + */ + @ApiModelProperty(value = "挖矿机器型号") + private String type; + + /** + * 挖矿机器编号 + */ + @ApiModelProperty(value = "挖矿机器编号") + private String miner; + + @ApiModelProperty(value = "挖矿币种") + private String coin; + + ///** + // * 实际算力(计算得到,商家不能够自己添加和修改) + // */ + //@ApiModelProperty(value = "3天算力平均大小---实际实时算力(计算得到,商家不能够自己添加和修改)") + //private BigDecimal computingPower; + + @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; + + /** + * 理论算力 + */ + @ApiModelProperty(value = "理论算力(卖方手动填写)") + private BigDecimal theoryPower; + + @ApiModelProperty(value = "单机理论收入(每日) 单位币种") + private BigDecimal theoryIncome; + + @ApiModelProperty(value = "单机理论收入(每日) 单位USDT") + private BigDecimal theoryUsdtIncome; + + @ApiModelProperty(value = "功耗 单位kw/h",example = "10") + private BigDecimal powerDissipation; + + + + /** + * 单价 + */ + @ApiModelProperty(value = "单价") + private BigDecimal price; + + @ApiModelProperty(value = "支付链") + private BigDecimal chain; + + @ApiModelProperty(value = "支付币种") + private BigDecimal symbol; + +} diff --git a/src/main/java/com/m2pool/lease/dto/ProductMachineForWalletConfigVo.java b/src/main/java/com/m2pool/lease/dto/ProductMachineForWalletConfigVo.java new file mode 100644 index 0000000..7ba3315 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/ProductMachineForWalletConfigVo.java @@ -0,0 +1,51 @@ +package com.m2pool.lease.dto; + +import com.m2pool.lease.annotation.EncryptedField; +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; + +/** + *

+ * 商品对应实际商品返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "钱包配置修改矿机支付方式请求对象",value = "ProductMachineForWalletConfigVo" ) +public class ProductMachineForWalletConfigVo { + + @ApiModelProperty(value = "钱包地址") + @EncryptedField + private String payAddress; + + @ApiModelProperty(value = "支付链") + private String chain; + + @ApiModelProperty(value = "支付币种,可以多个以逗号隔开") + private String symbol; + + @ApiModelProperty(value = "价格列表") + private List productMachineForWalletConfigVoList; + + @Data + @ApiModel(description = "币(二级) 下拉列表返回对象",value = "PriceVo") + public static class PriceVo{ + @ApiModelProperty(value = "矿机ID") + private Long productMachineId; + @ApiModelProperty(value = "单价,可以多个以逗号隔开") + private String price; + } + + +} diff --git a/src/main/java/com/m2pool/lease/dto/ProductMachineInfoDto.java b/src/main/java/com/m2pool/lease/dto/ProductMachineInfoDto.java new file mode 100644 index 0000000..74a3169 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/ProductMachineInfoDto.java @@ -0,0 +1,36 @@ +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; + +import java.util.List; + +/** + *

+ * 机器范围返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "商城商城矿机返回对象",value = "ProductMachineInfoDto" ) +public class ProductMachineInfoDto { + + @ApiModelProperty(value = "商品支持的支付地址") + private List payConfigList; + + + @ApiModelProperty(value = "矿机范围及矿机详情返回对象") + private List machineRangeInfoList; + + + +} diff --git a/src/main/java/com/m2pool/lease/dto/ProductMachineRangeDto.java b/src/main/java/com/m2pool/lease/dto/ProductMachineRangeDto.java new file mode 100644 index 0000000..06b714c --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/ProductMachineRangeDto.java @@ -0,0 +1,47 @@ +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; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +/** + *

+ * 机器范围返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "机器范围返回对象",value = "ProductMachineRangeDto" ) +public class ProductMachineRangeDto { + + @ApiModelProperty(value = "功耗范围") + private String powerRange; + + @ApiModelProperty(value = "理论算力范围") + private String theoryPowerRange; + + @ApiModelProperty(value = "实际算力范围") + private String computingPowerRange; + + @ApiModelProperty(value = "数量") + private Integer number; + + @ApiModelProperty(value = "实际单价") + private BigDecimal price; + + @ApiModelProperty(value = "商品对应的出售矿机") + List productMachines; + +} diff --git a/src/main/java/com/m2pool/lease/dto/ProductMachineRangeGroupDto.java b/src/main/java/com/m2pool/lease/dto/ProductMachineRangeGroupDto.java new file mode 100644 index 0000000..ab7fb8d --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/ProductMachineRangeGroupDto.java @@ -0,0 +1,49 @@ +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; + +import java.math.BigDecimal; +import java.util.List; + +/** + *

+ * 机器范围返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "机器范围返回对象",value = "ProductMachineRangeGroupDto" ) +public class ProductMachineRangeGroupDto { + + @ApiModelProperty(value = "唯一标识") + private Integer onlyKey; + + @ApiModelProperty(value = "功耗范围") + private String powerRange; + + @ApiModelProperty(value = "理论算力范围") + private String theoryPowerRange; + + @ApiModelProperty(value = "实际算力范围") + private String computingPowerRange; + + @ApiModelProperty(value = "数量") + private Integer number; + + @ApiModelProperty(value = "实际单价") + private BigDecimal price; + + @ApiModelProperty(value = "算力单位") + private String unit; + +} diff --git a/src/main/java/com/m2pool/lease/dto/ProductMachineRangeInfoDto.java b/src/main/java/com/m2pool/lease/dto/ProductMachineRangeInfoDto.java new file mode 100644 index 0000000..a780313 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/ProductMachineRangeInfoDto.java @@ -0,0 +1,34 @@ +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; + +import java.math.BigDecimal; +import java.util.List; + +/** + *

+ * 机器范围返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "机器范围返回对象",value = "ProductMachineRangeInfoDto" ) +public class ProductMachineRangeInfoDto { + + @ApiModelProperty(value = "商城商品详情---价格分组对象") + private ProductMachineRangeGroupDto productMachineRangeGroupDto; + + @ApiModelProperty(value = "价格分组下对应的商品矿机") + private List productMachines; + +} diff --git a/src/main/java/com/m2pool/lease/dto/ProductUpdateMachineDto.java b/src/main/java/com/m2pool/lease/dto/ProductUpdateMachineDto.java new file mode 100644 index 0000000..5067305 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/ProductUpdateMachineDto.java @@ -0,0 +1,98 @@ +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; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +/** + *

+ * 商品机器修改返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "商品机器修改返回对象",value = "ProductUpdateMachineDto" ) +public class ProductUpdateMachineDto { + + @ApiModelProperty(value = "矿机ID") + private Long id; + + /** + * 挖矿机器 对应的矿工账号 + */ + @ApiModelProperty(value = "挖矿机器 对应的矿工账号") + private String user; + + /** + * 挖矿机器型号 + */ + @ApiModelProperty(value = "挖矿机器型号") + private String type; + + /** + * 挖矿机器编号 + */ + @ApiModelProperty(value = "挖矿机器编号") + private String miner; + + + /** + * 单价 + */ + @ApiModelProperty(value = "售价集合") + private List priceList; + + /** + * 实际算力(计算得到,商家不能够自己添加和修改) + */ + @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; + + @ApiModelProperty(value = "最大可租借天数(默认七天)",example = "7") + private Integer maxLeaseDays; + + /** + * 单机预估实际收入 + */ + //@ApiModelProperty(value = "单机预估实际收入(每日)") + //private BigDecimal actualIncome; + + //@ApiModelProperty(value = "算法") + //private String algorithm; + + @ApiModelProperty(value = "功耗 单位kw/h",example = "10") + private BigDecimal powerDissipation; +} diff --git a/src/main/java/com/m2pool/lease/dto/QrCodeDto.java b/src/main/java/com/m2pool/lease/dto/QrCodeDto.java new file mode 100644 index 0000000..c1d13ab --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/QrCodeDto.java @@ -0,0 +1,40 @@ +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; + +import java.math.BigDecimal; + +/** + *

+ * 二维码 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "二维码",value = "QrCodeDto") +public class QrCodeDto { + + + + /** + * 支付金额 + */ + @ApiModelProperty(value = "支付金额") + private BigDecimal amount; + + /** + * 支付地址 + */ + @ApiModelProperty(value = "支付地址(卖方)") + private String payAddress; +} diff --git a/src/main/java/com/m2pool/lease/dto/RecentlyTransactionDto.java b/src/main/java/com/m2pool/lease/dto/RecentlyTransactionDto.java new file mode 100644 index 0000000..2264ccd --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/RecentlyTransactionDto.java @@ -0,0 +1,50 @@ +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; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + *

+ * 最近交易返回对象 + *

+ * + * @author yyb + * @since 2025-09-10 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "最近交易返回对象",value = "RecentlyTransactionDto" ) +public class RecentlyTransactionDto { + + + + /** + * 支付金额 + */ + @ApiModelProperty(value = "交易金额") + private BigDecimal amount; + + /** + * 交易类型 + */ + @ApiModelProperty(value = "交易类型 0 支付 1 充值 2 提现") + private Integer type; + + /** + * 交易完成时间 + */ + @ApiModelProperty(value = "交易完成时间") + private LocalDateTime updateTime; + + @ApiModelProperty(value = "交易状态 0 失败 1 成功 2 充值/提现/支付中 3校验失败") + private Integer status; +} diff --git a/src/main/java/com/m2pool/lease/dto/Result.java b/src/main/java/com/m2pool/lease/dto/Result.java new file mode 100644 index 0000000..ac8ed04 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/Result.java @@ -0,0 +1,78 @@ +package com.m2pool.lease.dto; + +import com.m2pool.lease.constant.ResponseConstant; +import lombok.Data; + +import java.io.Serializable; + +/** + * @Description 响应对象封装类 + * @Date 2024/6/11 9:18 + * @Author yyb + */ +@Data +public class Result implements Serializable +{ + private static final long serialVersionUID = 3276842119125241681L; + + /** 成功 */ + public static final int SUCCESS = ResponseConstant.SUCCESS; + + /** 失败 */ + public static final int FAIL = ResponseConstant.FAIL; + + private int code; + + private String msg; + + private T data; + + public static Result success() + { + return restResult(null, SUCCESS, null); + } + + public static Result success(T data) + { + return restResult(data, SUCCESS, null); + } + + public static Result success(T data, String msg) + { + return restResult(data, SUCCESS, msg); + } + + public static Result fail() + { + return restResult(null, FAIL, null); + } + + public static Result fail(String msg) + { + return restResult(null, FAIL, msg); + } + + public static Result fail(T data) + { + return restResult(data, FAIL, null); + } + + public static Result fail(T data, String msg) + { + return restResult(data, FAIL, msg); + } + + public static Result fail(int code, String msg) + { + return restResult(null, code, msg); + } + + private static Result restResult(T data, int code, String msg) + { + Result apiResult = new Result<>(); + apiResult.setCode(code); + apiResult.setData(data); + apiResult.setMsg(msg); + return apiResult; + } +} diff --git a/src/main/java/com/m2pool/lease/dto/ShopCartDto.java b/src/main/java/com/m2pool/lease/dto/ShopCartDto.java new file mode 100644 index 0000000..53a3d21 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/ShopCartDto.java @@ -0,0 +1,49 @@ +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; + +import java.math.BigDecimal; +import java.util.List; + +/** + *

+ * 购物车一层列表 + *

+ * + * @author yyb + * @since 2025-08-05 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "购物车一层商铺列表返回对象",value = "ShopCartDto") +public class ShopCartDto { + + @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 totalPriceList; + + @ApiModelProperty(value = "商品支持的支付地址") + private List payConfigList; + + @ApiModelProperty(value = "商品机器列表") + List productMachineDtoList; +} diff --git a/src/main/java/com/m2pool/lease/dto/ShopChainAndCoinDto.java b/src/main/java/com/m2pool/lease/dto/ShopChainAndCoinDto.java new file mode 100644 index 0000000..09ebdd3 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/ShopChainAndCoinDto.java @@ -0,0 +1,51 @@ +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; + +import java.math.BigDecimal; + +/** + *

+ * 店铺钱包配置返对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "店铺钱包配置返对象",value = "ShopChainAndCoinDto") +public class ShopChainAndCoinDto { + private Long shopId; + private String chain; + private String coin; + + + /** + * 用于stream distinct()去重 + * @param o + * @return + */ + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ShopChainAndCoinDto that = (ShopChainAndCoinDto) o; + return shopId.equals(that.shopId) && coin.equals(that.coin) && chain.equals(that.chain); + } + + @Override + public int hashCode() { + int result = shopId.hashCode(); + result = 31 * result + coin.hashCode(); + result = 31 * result + chain.hashCode(); + return result; + } +} diff --git a/src/main/java/com/m2pool/lease/dto/ShopConfigDelDto.java b/src/main/java/com/m2pool/lease/dto/ShopConfigDelDto.java new file mode 100644 index 0000000..8aa71e0 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/ShopConfigDelDto.java @@ -0,0 +1,39 @@ +package com.m2pool.lease.dto; + +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.util.List; + +/** + *

+ * 店铺商品配置返回对象 + *

+ * + * @author yyb + * @since 2025-08-05 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "店铺商品配置返回对象",value = "ShopConfigDelDto") +public class ShopConfigDelDto extends BaseVo { + + + private Long shopId; + + + private String payAddress; + + private String chain; + + private Boolean del; + + +} diff --git a/src/main/java/com/m2pool/lease/dto/ShopConfigDto.java b/src/main/java/com/m2pool/lease/dto/ShopConfigDto.java new file mode 100644 index 0000000..e9f69e6 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/ShopConfigDto.java @@ -0,0 +1,66 @@ +package com.m2pool.lease.dto; + +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.util.List; + +/** + *

+ * 店铺商品配置返回对象 + *

+ * + * @author yyb + * @since 2025-08-05 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "店铺商品配置返回对象",value = "ShopConfigDto") +public class ShopConfigDto extends BaseVo { + + + /** + * 商铺ID + */ + @ApiModelProperty(value = "商铺ID") + private Long shopId; + + + /** + * 币种类型 0 虚拟币 1 稳定币 + */ + @ApiModelProperty(value = "币种类型 0 虚拟币 1 稳定币") + private Integer payType; + + /** + * 卖方对应收款钱包 + */ + @ApiModelProperty(value = "卖方对应收款钱包") + private String payAddress; + + @ApiModelProperty(value = "链") + private String chain; + + @ApiModelProperty(value = "币(二级) 下拉列表") + private List children; + + @Data + @ApiModel(description = "币种图片返回对象",value = "CoinDto") + public static class CoinDto{ + @ApiModelProperty(value = "商品支付方式及价格单位 取值 虚拟币( nexa rxd dgbo dgbq dgbs alph enx grs mona) 稳定币(usdt usdc busd)") + private String payCoin; + + @ApiModelProperty(value = "币种图片") + private String image; + + } + + +} diff --git a/src/main/java/com/m2pool/lease/dto/ShopDto.java b/src/main/java/com/m2pool/lease/dto/ShopDto.java new file mode 100644 index 0000000..b445851 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/ShopDto.java @@ -0,0 +1,63 @@ +package com.m2pool.lease.dto; + +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; + +/** + *

+ * 店铺表 + *

+ * + * @author yyb + * @since 2025-08-05 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "店铺返回对象",value = "ShopDto") +public class ShopDto { + + @ApiModelProperty(value = "店铺id") + private Long id; + + /** + * 店铺名称 + */ + @ApiModelProperty(value = "店铺名称") + private String name; + + /** + * 店铺图片路径 + */ + @ApiModelProperty(value = "店铺图片路径") + private String image; + + /** + * 店铺描述 + */ + @ApiModelProperty(value = "店铺描述") + private String description; + + /** + * 商铺状态 0 待审核 1 审核通过(店铺开启) 2 店铺关闭 + */ + @ApiModelProperty(value = "商铺状态 0 待审核 1 审核通过(店铺开启) 2 店铺关闭") + private Integer state; + /** + * 逻辑删除字段 + */ + @ApiModelProperty(value = "逻辑删除字段") + private Boolean del; + + + @ApiModelProperty(value = "店铺手续费比例 范围0.01-0.1",example = "0.01") + private BigDecimal feeRate; +} diff --git a/src/main/java/com/m2pool/lease/dto/ShopNameDto.java b/src/main/java/com/m2pool/lease/dto/ShopNameDto.java new file mode 100644 index 0000000..c78530e --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/ShopNameDto.java @@ -0,0 +1,33 @@ +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; + +/** + *

+ * 店铺配置商品列表返回对象 + *

+ * + * @author yyb + * @since 2025-08-05 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "店铺配置商品列表返回对象",value = "ShopNameDto") +public class ShopNameDto { + + @ApiModelProperty(value = "店铺id") + private Long id; + + /** + * 店铺名称 + */ + @ApiModelProperty(value = "店铺名称") + private String name; +} diff --git a/src/main/java/com/m2pool/lease/dto/ShoppingCartInfoDto.java b/src/main/java/com/m2pool/lease/dto/ShoppingCartInfoDto.java new file mode 100644 index 0000000..478f9f4 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/ShoppingCartInfoDto.java @@ -0,0 +1,40 @@ +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; + +import java.util.List; + +/** + *

+ * 购物车详情表 + *

+ * + * @author yyb + * @since 2025-07-24 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "购物车二层商品列表返回对象(不再使用)",value = "ShoppingCartInfoDto" ) +public class ShoppingCartInfoDto { + /** + * 商品 ID + */ + private Long productId; + + /** + * 商品机器ID + */ + private Long productMachineId; + /** + * 租期 + */ + private Integer leaseTime; + +} diff --git a/src/main/java/com/m2pool/lease/dto/TableHeadersDto.java b/src/main/java/com/m2pool/lease/dto/TableHeadersDto.java new file mode 100644 index 0000000..9409807 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/TableHeadersDto.java @@ -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; +} diff --git a/src/main/java/com/m2pool/lease/dto/TransactionRecordDto.java b/src/main/java/com/m2pool/lease/dto/TransactionRecordDto.java new file mode 100644 index 0000000..6d66de1 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/TransactionRecordDto.java @@ -0,0 +1,118 @@ +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; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + *

+ * 支付消息记录表 + *

+ * + * @author yyb + * @since 2025-09-10 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "交易流水返回对象",value = "TransactionRecordDto" ) +public class TransactionRecordDto { + + + /** + * 消息ID + */ + @ApiModelProperty(value = "消息ID") + private String queueId; + + /** + * 买家充值地址 + */ + @ApiModelProperty(value = "买家充值地址") + private String fromAddress; + + /** + * 卖家地址(卖家自定义地址) + */ + @ApiModelProperty(value = "提现流水:用户提现地址 充值流水:不需要该字段 支付流水:卖家收款地址") + private String toAddress; + + /** + * 支付金额 + */ + @ApiModelProperty(value = "理论支付支付金额") + private BigDecimal amount; + + /** + *实际支付金额(根据一天内预估算力和实际算力差值计算得来,并且已经真实支付成功的金额) + */ + @ApiModelProperty(value = "实际支付金额(根据一天内预估算力和实际算力差值计算得来,并且已经真实支付成功的金额)") + private BigDecimal realAmount; + + /** + * 钱包冻结金额 + */ + @ApiModelProperty(value = "钱包冻结金额") + private BigDecimal blockAmount; + /** + * 卖家钱包币种 + */ + @ApiModelProperty(value = "提现流水:用户提现钱包币种 充值流水:不需要该字段 支付流水:卖家收款钱包币种") + private String toSymbol; + + /** + * 卖家钱包链名称 + */ + @ApiModelProperty(value = "提现流水:用户提现链链名称 充值流水:不需要该字段 支付流水:卖家收款链名称") + private String toChain; + + + /** + * 买家钱包币种 + */ + @ApiModelProperty(value = "买家钱包币种") + private String fromSymbol; + + /** + * 买家钱包链名称 + */ + @ApiModelProperty(value = "买家钱包链名称") + private String fromChain; + + /** + * 交易hash + */ + @ApiModelProperty(value = "交易hash") + private String txHash; + + /** + * 订单号 + */ + @ApiModelProperty(value = "订单号") + private String orderId; + + /** + * 支付时间 + */ + @ApiModelProperty(value = "流水建立时间") + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @ApiModelProperty(value = "成功/失败/证书校验失败时间") + private LocalDateTime updateTime; + + /** + * 0 失败 1 成功 2 等待校验 3 证书校验失败 + */ + @ApiModelProperty(value = " 0 支付/充值/提现失败 1 支付/充值/提现成功 2 支付/充值/提现校验中 3 支付/充值/提现证书校验失败") + private Integer status; +} diff --git a/src/main/java/com/m2pool/lease/dto/UserDto.java b/src/main/java/com/m2pool/lease/dto/UserDto.java new file mode 100644 index 0000000..b5b9d9b --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/UserDto.java @@ -0,0 +1,39 @@ +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; + +/** + *

+ * 用户返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "用户返回对象",value = "UserDto" ) +public class UserDto{ + + private Long id; + + /** + * 用户id(邮箱) + */ + @ApiModelProperty(value = "用户id(邮箱)") + private String userId; + + /** + * 密码 + */ + @ApiModelProperty(value = "密码") + private String password; + +} diff --git a/src/main/java/com/m2pool/lease/dto/UserMinerDto.java b/src/main/java/com/m2pool/lease/dto/UserMinerDto.java new file mode 100644 index 0000000..1b7671d --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/UserMinerDto.java @@ -0,0 +1,43 @@ +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; + +import java.math.BigDecimal; + +/** + *

+ * 用户挖矿账户矿工返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "用户挖矿账户矿工返回对象",value = "UserMinerDto" ) +public class UserMinerDto { + /** + * 挖矿机器 对应的矿工账号 + */ + @ApiModelProperty(value = "挖矿机器 对应的矿工账号") + private String user; + /** + * 挖矿机器编号 + */ + @ApiModelProperty(value = "挖矿机器编号") + private String miner; + + @ApiModelProperty(value = "机器挖矿币种") + private String coin; + + @ApiModelProperty(value = "实时算力(单位MH/S)") + private BigDecimal realPower; + +} diff --git a/src/main/java/com/m2pool/lease/dto/UserMinerPowerDto.java b/src/main/java/com/m2pool/lease/dto/UserMinerPowerDto.java new file mode 100644 index 0000000..02bf571 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/UserMinerPowerDto.java @@ -0,0 +1,48 @@ +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; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + *

+ * 用户挖矿账户矿工返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "用户挖矿账户矿工实时算力返回对象",value = "UserMinerPowerDto" ) +public class UserMinerPowerDto { + /** + * 挖矿机器 对应的矿工账号 + */ + @ApiModelProperty(value = "挖矿机器 对应的矿工账号") + private String user; + /** + * 挖矿机器编号 + */ + @ApiModelProperty(value = "挖矿机器编号") + private String miner; + + @ApiModelProperty(value = "机器挖矿币种") + private String coin; + + @ApiModelProperty(value = "机器算力") + private BigDecimal power; + + @ApiModelProperty(value = "记录开始时间") + private LocalDateTime startTime; + + +} diff --git a/src/main/java/com/m2pool/lease/dto/UserOwnedProductDto.java b/src/main/java/com/m2pool/lease/dto/UserOwnedProductDto.java new file mode 100644 index 0000000..06af2ad --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/UserOwnedProductDto.java @@ -0,0 +1,115 @@ +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; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + *

+ * 用户拥有商品返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(value="UserOwnedProductDto", description="UserOwnedProductDto") +public class UserOwnedProductDto { + + @ApiModelProperty(value = "ID") + private Long id; + + /** + * 用户ID(暂时使用租赁者钱包地址) + */ + @ApiModelProperty(value = "用户id(邮箱)") + private String userId; + + /** + * 商品对应的机器id + */ + @ApiModelProperty(value = "商品对应的机器id") + private Long productMachineId; + + /** + * 订单明细 ID,关联订单中的购买记录 + */ + @ApiModelProperty(value = "订单明细 ID,关联订单中的购买记录") + private Long orderItemId; + + /** + * 商品开始使用时间(购买当天24点生效) + */ + @ApiModelProperty(value = "商品开始使用时间(购买当天24点生效)") + private LocalDateTime startTime; + + /** + * 商品使用结束时间 + */ + @ApiModelProperty(value = "商品使用结束时间") + private LocalDateTime endTime; + + /** + * 购买算力的量,当 type 为 1 时有效 + */ + @ApiModelProperty(value = "购买算力的量,当 type 为 1 时有效") + private BigDecimal purchasedComputingPower; + + /** + * 当前实时算力 + */ + @ApiModelProperty(value = "当前实时算力:单位默认MH/S") + private BigDecimal currentComputingPower; + + /** + * 商品状态,0 表示运行中,1 表示已过期 + */ + @ApiModelProperty(value = "商品状态,0 表示运行中,1 表示已过期") + private Integer status; + + /** + * 商品类型,0 表示挖矿机器,1 表示算力套餐 + */ + @ApiModelProperty(value = "商品类型,0 表示挖矿机器,1 表示算力套餐") + private Integer type; + + /** + * 购买时间 + */ + @ApiModelProperty(value = "购买时间") + private LocalDateTime createTime; + + + /** + * 预估商品结束时的总收益 + */ + @ApiModelProperty(value = "预估商品结束时的总收益") + private BigDecimal estimatedEndIncome; + + + /** + * 预估商品结束时的总收益 单位usdt + */ + @ApiModelProperty(value = "预估商品结束时的总收益 单位usdt") + private BigDecimal estimatedEndUsdtIncome; + /** + * 当前收益(根据购买机器到现在的平均算力计算得到) + */ + @ApiModelProperty(value = "当前收益(根据购买机器到现在的平均算力计算得到)") + private BigDecimal currentIncome; + + /** + * 当前收益(根据购买机器到现在的平均算力计算得到)单位usdt + */ + @ApiModelProperty(value = "当前收益(根据购买机器到现在的平均算力计算得到)单位usdt") + private BigDecimal currentUsdtIncome; +} diff --git a/src/main/java/com/m2pool/lease/dto/UserWalletDataDto.java b/src/main/java/com/m2pool/lease/dto/UserWalletDataDto.java new file mode 100644 index 0000000..3f17da0 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/UserWalletDataDto.java @@ -0,0 +1,101 @@ +package com.m2pool.lease.dto; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.*; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + *

+ * 用户钱包信息表 + *

+ * + * @author 根据实际情况填写 + * @since 2025-09-08 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "用户钱包资料信息",value = "UserWalletDataDto") +public class UserWalletDataDto { + + + @ApiModelProperty(value = "用户钱包信息id") + private Long id; + + /** + * 钱包唯一id(用于充值功能) + */ + private String queueId; + + /** + * 用户id(邮箱) + */ + @ApiModelProperty(value = "用户id(邮箱)") + private String userId; + + /** + * 钱包地址 + */ + @ApiModelProperty(value = "充值钱包地址(我们提供") + private String fromAddress; + + + /** + * 钱包地址 + */ + @ApiModelProperty(value = "收款钱包地址(用户自己填写)") + private String toAddress; + + /** + * 余额 + */ + @ApiModelProperty(value = "余额") + private BigDecimal balance; + + @ApiModelProperty(value = "冻结余额,不能用于提现(用户购买机器完成租约所需金额)") + private BigDecimal blockedBalance; + + /** + * 支付地址二维码 + */ + @ApiModelProperty(value = "支付地址二维码") + private String qrcode; + + /** + * 币种 + */ + @ApiModelProperty(value = "用户自定义钱包地址的币种") + private String toSymbol; + + /** + * 链名称 + */ + @ApiModelProperty(value = "用户自定义钱包地址的链名称") + private String toChain; + + + /** + * 币种 + */ + @ApiModelProperty(value = "官方自动绑定钱包地址的币种") + private String fromSymbol; + + /** + * 链名称 + */ + @ApiModelProperty(value = "官方自动绑定钱包地址的链名称") + private String fromChain; + + private LocalDateTime createTime; + + @ApiModelProperty(value = "手续费") + private BigDecimal charge; + +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/AlgorithmShopIdMapDto.java b/src/main/java/com/m2pool/lease/dto/v2/AlgorithmShopIdMapDto.java new file mode 100644 index 0000000..8f134c6 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/AlgorithmShopIdMapDto.java @@ -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; + +/** + *

+ * 商品列表分页请求对象 + *

+ * + * @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; + +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/AsicCoinAndAlgoDto.java b/src/main/java/com/m2pool/lease/dto/v2/AsicCoinAndAlgoDto.java new file mode 100644 index 0000000..2204246 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/AsicCoinAndAlgoDto.java @@ -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; + +/** + *

+ * 机器参数请求对象 + *

+ * + * @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; +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/CartMachineInfoDto.java b/src/main/java/com/m2pool/lease/dto/v2/CartMachineInfoDto.java new file mode 100644 index 0000000..3b7d1f2 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/CartMachineInfoDto.java @@ -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; + +/** + *

+ * 商品对应实际商品返回对象 + *

+ * + * @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 powerIncomeInfoList; + + @ApiModelProperty(value = "店铺售价集合") + private List priceList; +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/CoinAndAlgorithmDto.java b/src/main/java/com/m2pool/lease/dto/v2/CoinAndAlgorithmDto.java new file mode 100644 index 0000000..d997641 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/CoinAndAlgorithmDto.java @@ -0,0 +1,62 @@ +package com.m2pool.lease.dto.v2; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class CoinAndAlgorithmDto { + + /** + * 币种名称 + */ + private String coin; + + /** + * 算法名称 + */ + private String algorithm; + + /** + * 矿机id,多个已逗号隔开 + */ + private String machineIds; + + /** + * 是否生效 + */ + private Boolean isEffect; + + /** + * gpu名称 + */ + private String model; + + /** + * gpu 功耗 + */ + private BigDecimal powerDissipation; + + /** + * gpu 理论算力 + */ + private BigDecimal hashRate; + + /** + * 月收入 + */ + private BigDecimal monthIncome; + + /** + * 是否新增 gpu 配置 + */ + private Boolean isAdd; + +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/CoinAndAlgorithmListDto.java b/src/main/java/com/m2pool/lease/dto/v2/CoinAndAlgorithmListDto.java new file mode 100644 index 0000000..c961d8f --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/CoinAndAlgorithmListDto.java @@ -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 algorithmList; + + @Data + @ApiModel(description = "币种对应算法返回对象",value = "AlgorithmListDto") + public static class AlgorithmListDto{ + @ApiModelProperty(value = "算法") + private String algorithm; + + @ApiModelProperty(value = "支持该算法的矿机id集合") + private List machineIds; + } + + + +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/GoogleInfoDto.java b/src/main/java/com/m2pool/lease/dto/v2/GoogleInfoDto.java new file mode 100644 index 0000000..7d088b7 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/GoogleInfoDto.java @@ -0,0 +1,21 @@ +package com.m2pool.lease.dto.v2; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +@Data +@ApiModel(description = "谷歌验证信息返回对象",value = "GoogleInfoDto" ) +public class GoogleInfoDto { + + /** 谷歌验证码 */ + + @ApiModelProperty(value = "密钥") + private String secret; + + /** 二维码 */ + @ApiModelProperty(value = "二维码/base64") + private String img; +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/GpuConfigDto.java b/src/main/java/com/m2pool/lease/dto/v2/GpuConfigDto.java new file mode 100644 index 0000000..937b39b --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/GpuConfigDto.java @@ -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; + +/** + *

+ * 商品对应实际商品返回对象 + *

+ * + * @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; + +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/MachineConfigDto.java b/src/main/java/com/m2pool/lease/dto/v2/MachineConfigDto.java new file mode 100644 index 0000000..f9fe8c3 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/MachineConfigDto.java @@ -0,0 +1,66 @@ +package com.m2pool.lease.dto.v2; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import io.swagger.annotations.ApiModel; +import lombok.*; + +import java.io.Serializable; +import java.math.BigDecimal; + + +/** + *

+ * GPU对应币种挖矿算力(只要gpu类型相同那么就使用这个配置) + *

+ * + * @author yyb + * @since 2025-11-27 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class MachineConfigDto { + + + private Long id; + + /** + * 机器id + */ + private Long machineId; + + /** + * gpu/asic品牌 + */ + private String brand; + + /** + * gpu 名称/型号 + */ + private String name; + + /** + * 内存单位M + */ + private Integer memory; + + + + /** + * 支持的币种 + */ + private String coin; + + /** + * 算法 + */ + private String algorithm; + + /** + * 是否是临时信息 + */ + private Boolean isTemp; + +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/MachineEffectDto.java b/src/main/java/com/m2pool/lease/dto/v2/MachineEffectDto.java new file mode 100644 index 0000000..0f41d46 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/MachineEffectDto.java @@ -0,0 +1,15 @@ +package com.m2pool.lease.dto.v2; + +import lombok.Data; + +/** + * @Description 矿机是否能够上架 + * @Date 2025/12/12 14:41 + * @Author yyb + */ +@Data +public class MachineEffectDto { + private Long machineId; + + private Integer effect; +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/MachineHashRateMapDto.java b/src/main/java/com/m2pool/lease/dto/v2/MachineHashRateMapDto.java new file mode 100644 index 0000000..3aa9808 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/MachineHashRateMapDto.java @@ -0,0 +1,27 @@ +package com.m2pool.lease.dto.v2; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +/** + *

+ * 商品列表分页请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class MachineHashRateMapDto { + private Long machineId; + + private BigDecimal hashRate; + +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/MachineInfoDto.java b/src/main/java/com/m2pool/lease/dto/v2/MachineInfoDto.java new file mode 100644 index 0000000..194e7b7 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/MachineInfoDto.java @@ -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; + +/** + *

+ * 商品对应实际商品返回对象 + *

+ * + * @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 String c1; + + private String c2; + + private String c3; + + private String c4; + + private String c5; + + private String c6; + + private String c7; + + private String c8; + + private String c9; + + private String c10; + + + + //@ApiModelProperty(value = "币种动态列集合") + //private List powerIncomeInfoList; + + @ApiModelProperty(value = "店铺售价集合") + private List priceList; +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/MachineInfoInterface.java b/src/main/java/com/m2pool/lease/dto/v2/MachineInfoInterface.java new file mode 100644 index 0000000..fe3ea23 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/MachineInfoInterface.java @@ -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 powerIncomeInfoList); +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/MiningCoinInfoDto.java b/src/main/java/com/m2pool/lease/dto/v2/MiningCoinInfoDto.java new file mode 100644 index 0000000..7ab99f3 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/MiningCoinInfoDto.java @@ -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; +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/MiningConfigDto.java b/src/main/java/com/m2pool/lease/dto/v2/MiningConfigDto.java new file mode 100644 index 0000000..a69ecc3 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/MiningConfigDto.java @@ -0,0 +1,48 @@ +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 { + + private Long id; + + @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; +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/MiningConfigInfoDto.java b/src/main/java/com/m2pool/lease/dto/v2/MiningConfigInfoDto.java new file mode 100644 index 0000000..b8f92c0 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/MiningConfigInfoDto.java @@ -0,0 +1,54 @@ +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; + +/** + *

+ * 挖矿中订单 + *

+ * + * @author yyb + * @since 2025-12-02 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "已购商品挖矿配置详情返回对象",value = "MiningConfigInfoDto" ) +public class MiningConfigInfoDto { + + @ApiModelProperty(value = "矿池所挖币种") + private String coin; + + @ApiModelProperty(value = "币种对应算法") + private String algorithm; + + + @ApiModelProperty(value = "矿池名称") + private String pool; + + + @ApiModelProperty(value = "矿池挖矿地址") + private String poolUrl; + + + @ApiModelProperty(value = "矿池挖矿账户") + private String poolUser; + + @ApiModelProperty(value = "收款钱包") + private String walletAddress; + + @ApiModelProperty(value = "挖矿信息页面地址") + private String watchUrl; + + @ApiModelProperty(value = "已购矿机信息") + private List purchasedMachines; + +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/MiningConfigSelectDto.java b/src/main/java/com/m2pool/lease/dto/v2/MiningConfigSelectDto.java new file mode 100644 index 0000000..fcb17a0 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/MiningConfigSelectDto.java @@ -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; +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/MiningHashrateInfoDto.java b/src/main/java/com/m2pool/lease/dto/v2/MiningHashrateInfoDto.java new file mode 100644 index 0000000..2817d44 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/MiningHashrateInfoDto.java @@ -0,0 +1,36 @@ +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; + + +/** + *

+ * GPU对应币种挖矿算力(只要gpu类型相同那么就使用这个配置) + *

+ * + * @author yyb + * @since 2025-11-27 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "矿机对应实时算力放回对象",value = "MiningHashrateInfoDto" ) +public class MiningHashrateInfoDto { + @ApiModelProperty(value = "实时算力") + private BigDecimal power; + + @ApiModelProperty(value = "最近实时算力记录时间") + private LocalDateTime recordTime; + + @ApiModelProperty(value = "矿工号") + private String miner; +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/MiningInfoDto.java b/src/main/java/com/m2pool/lease/dto/v2/MiningInfoDto.java new file mode 100644 index 0000000..7b2ca45 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/MiningInfoDto.java @@ -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 miningCoinInfoList; +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/OrderMiningInfoDto.java b/src/main/java/com/m2pool/lease/dto/v2/OrderMiningInfoDto.java new file mode 100644 index 0000000..cf577f6 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/OrderMiningInfoDto.java @@ -0,0 +1,48 @@ +package com.m2pool.lease.dto.v2; + +import com.m2pool.lease.annotation.EncryptedField; +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 Long machineId; + + @ApiModelProperty(value = "挖矿钱包(用户自己输入)") + @EncryptedField + private String walletAddress; + + @ApiModelProperty(value = "挖矿账号(用户自己输入)") + private String poolUser; + + @ApiModelProperty(value = "矿工号(用户自己输入)") + private String workerId; + + @ApiModelProperty(value = "挖矿钱包币种") + private String coin; +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/OrderTimeInfoDto.java b/src/main/java/com/m2pool/lease/dto/v2/OrderTimeInfoDto.java new file mode 100644 index 0000000..f97968e --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/OrderTimeInfoDto.java @@ -0,0 +1,39 @@ +package com.m2pool.lease.dto.v2; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Date; + +/** + *

+ * 订单状态修改返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class OrderTimeInfoDto { + + private Long id; + + private Long orderId; + + private Long orderItemId; + + private Integer leaseTime; + + private LocalDateTime startTime; + + private BigDecimal price; + + private Integer numbers; +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/PayWithdrawSellerRecordDto.java b/src/main/java/com/m2pool/lease/dto/v2/PayWithdrawSellerRecordDto.java new file mode 100644 index 0000000..a4abac5 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/PayWithdrawSellerRecordDto.java @@ -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; + +/** + *

+ * 余额提现返回对象 + *

+ * + * @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; +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/PowerIncomeInfoDto.java b/src/main/java/com/m2pool/lease/dto/v2/PowerIncomeInfoDto.java new file mode 100644 index 0000000..d7758fb --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/PowerIncomeInfoDto.java @@ -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; + +/** + *

+ * 商品对应实际商品返回对象 + *

+ * + * @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; + +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/PurchasedMachineDto.java b/src/main/java/com/m2pool/lease/dto/v2/PurchasedMachineDto.java new file mode 100644 index 0000000..3d1db01 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/PurchasedMachineDto.java @@ -0,0 +1,56 @@ +package com.m2pool.lease.dto.v2; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + *

+ * 挖矿中订单 + *

+ * + * @author yyb + * @since 2025-12-02 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "买家已购矿机返回对象",value = "PurchasedMachineDto" ) +public class PurchasedMachineDto { + + @ApiModelProperty(value = "id") + private Long id; + + //@ApiModelProperty(value = "订单编号") + //private String orderNumbers; + + + @ApiModelProperty(value = "矿工号") + private String workerId; + + @ApiModelProperty(value = "0 租约已到期 1挖矿中 2卖家矿机启动中") + private Integer status; + + + @ApiModelProperty(value = "挖矿开始时间") + private LocalDateTime startTime; + + + @ApiModelProperty(value = "挖矿结束时间") + private LocalDateTime endTime; + + + @ApiModelProperty(value = "实时算力") + private BigDecimal power; + + @ApiModelProperty(value = "最近实时算力记录时间") + private LocalDateTime recordTime; + +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/PurchasedMachineListDto.java b/src/main/java/com/m2pool/lease/dto/v2/PurchasedMachineListDto.java new file mode 100644 index 0000000..e34617b --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/PurchasedMachineListDto.java @@ -0,0 +1,71 @@ +package com.m2pool.lease.dto.v2; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.*; + +import java.time.LocalDateTime; + +/** + *

+ * 挖矿中订单 + *

+ * + * @author yyb + * @since 2025-12-02 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "买家已购矿机返回对象",value = "PurchasedMachineListDto" ) +public class PurchasedMachineListDto { + + @ApiModelProperty(value = "id") + private Long id; + + //@ApiModelProperty(value = "订单编号") + //private String orderNumbers; + + @ApiModelProperty(value = "矿池所挖币种") + private String coin; + + @ApiModelProperty(value = "币种对应算法") + private String algorithm; + + + @ApiModelProperty(value = "矿池名称") + private String pool; + + + @ApiModelProperty(value = "矿池挖矿地址") + private String poolUrl; + + + @ApiModelProperty(value = "矿池挖矿账户") + private String poolUser; + + + @ApiModelProperty(value = "收款钱包") + private String walletAddress; + + @ApiModelProperty(value = "挖矿信息页面地址") + private String watchUrl; + + + private String workerId; + // + //@ApiModelProperty(value = "0 租约已到期 1挖矿中 2卖家矿机启动中") + //private Integer status; + // + // + //@ApiModelProperty(value = "挖矿开始时间") + //private LocalDateTime startTime; + // + // + //@ApiModelProperty(value = "挖矿结束时间") + //private LocalDateTime endTime; + + + private Long orderItemId; +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/RealHashrateInfoDto.java b/src/main/java/com/m2pool/lease/dto/v2/RealHashrateInfoDto.java new file mode 100644 index 0000000..561a69b --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/RealHashrateInfoDto.java @@ -0,0 +1,61 @@ +package com.m2pool.lease.dto.v2; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + + +/** + *

+ * GPU对应币种挖矿算力(只要gpu类型相同那么就使用这个配置) + *

+ * + * @author yyb + * @since 2025-11-27 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class RealHashrateInfoDto { + + + /** + * 订单项ID + */ + private Long orderItemId; + + /** + * 算力 + */ + private BigDecimal power; + + + /** + * 矿池名称 + */ + private String pool; + + /** + * 收款钱包 + */ + private String walletAddress; + + /** + * 矿工名称 + */ + private String miner; + + /** + * 币种 + */ + private String coin; + + /** + * 币种对应算法 + */ + private String algorithm; +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/SellerMachineInfoDto.java b/src/main/java/com/m2pool/lease/dto/v2/SellerMachineInfoDto.java new file mode 100644 index 0000000..3e8ff0f --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/SellerMachineInfoDto.java @@ -0,0 +1,99 @@ +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; + +/** + *

+ * 商品对应实际商品返回对象 + *

+ * + * @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 = "矿机是否能够上架 0 不能 1 能") + private Integer effect; + + //@ApiModelProperty(value = "gpu和asic对应币种算力和月收益信息") + //private List powerIncomeInfoList; + + @ApiModelProperty(value = "币种算法算力信息") + private List coinAndAlgoList; + + @ApiModelProperty(value = "店铺售价集合") + private List priceList; +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/ShopCartV2Dto.java b/src/main/java/com/m2pool/lease/dto/v2/ShopCartV2Dto.java new file mode 100644 index 0000000..88901b5 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/ShopCartV2Dto.java @@ -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; + +/** + *

+ * 购物车一层列表 + *

+ * + * @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 totalPriceList; + + @ApiModelProperty(value = "商品支持的支付地址") + private List payConfigList; + + @ApiModelProperty(value = "商品机器列表") + List cartMachineInfoDtoList; +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/ShopInfoDto.java b/src/main/java/com/m2pool/lease/dto/v2/ShopInfoDto.java new file mode 100644 index 0000000..06ef8f8 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/ShopInfoDto.java @@ -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; + +/** + *

+ * 商品对应实际商品返回对象 + *

+ * + * @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 payTypes; + + +} diff --git a/src/main/java/com/m2pool/lease/dto/v2/ShopWalletInfoDto.java b/src/main/java/com/m2pool/lease/dto/v2/ShopWalletInfoDto.java new file mode 100644 index 0000000..0e05974 --- /dev/null +++ b/src/main/java/com/m2pool/lease/dto/v2/ShopWalletInfoDto.java @@ -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; + +/** + *

+ * 店铺钱包配置返回对象 + *

+ * + * @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; +} diff --git a/src/main/java/com/m2pool/lease/entity/GoogleInfo.java b/src/main/java/com/m2pool/lease/entity/GoogleInfo.java new file mode 100644 index 0000000..5f34cd4 --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/GoogleInfo.java @@ -0,0 +1,32 @@ +package com.m2pool.lease.entity; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * @Description 文件 + * @Date 2024/6/14 15:57 + * @Author dy + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class GoogleInfo implements Serializable { + + /** 邮箱 */ + private String email; + + /** 谷歌验证码 */ + private String secret; + + /** 谷歌验证码 */ + private int status; + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeaseAutoAddress.java b/src/main/java/com/m2pool/lease/entity/LeaseAutoAddress.java new file mode 100644 index 0000000..34c572c --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeaseAutoAddress.java @@ -0,0 +1,58 @@ +package com.m2pool.lease.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.m2pool.lease.dto.PageResult; +import io.swagger.annotations.ApiModelProperty; +import lombok.*; + +import java.io.Serializable; + +/** + *

+ * 矿池nexa机器实时平均算力 - 自动钱包地址表 + *

+ * + * @author yyb + * @since 2025-09-04 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class LeaseAutoAddress implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 钱包地址 + */ + private String address; + + /** + * 币种 + */ + private String fromSymbol; + + /** + * 链名称 + */ + private String fromChain; + /** + * 0 未使用 1 已使用 + */ + private Integer status; + + /** + * 绑定用户用户邮箱 + */ + private String bindUser; + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeaseGpuConfig.java b/src/main/java/com/m2pool/lease/entity/LeaseGpuConfig.java new file mode 100644 index 0000000..7d80026 --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeaseGpuConfig.java @@ -0,0 +1,96 @@ +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; + +/** + *

+ * GPU对应币种挖矿算力(只要gpu类型相同那么就使用这个配置) + *

+ * + * @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; + + /** + * 币种 + */ + private String coin; + + + /** + * 算力 单位MH/s + */ + private BigDecimal hashrate; + + + /** + * 算法 + */ + private String algorithm; + + /** + * 图标 + */ + private String icon; + + /** + * 单位 默认MH/S + */ + private String unit; + + /** + * 月收入 + */ + private BigDecimal monthIncome; + + /** + * 功耗 单位kw/h + */ + private BigDecimal powerDissipation; + + /** + * 逻辑删除字段 + */ + private Boolean del; + + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeaseMachine.java b/src/main/java/com/m2pool/lease/entity/LeaseMachine.java new file mode 100644 index 0000000..76ac1e9 --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeaseMachine.java @@ -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; + +/** + *

+ * gpu类型出售矿机表 + *

+ * + * @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; + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeaseMachineConfig.java b/src/main/java/com/m2pool/lease/entity/LeaseMachineConfig.java new file mode 100644 index 0000000..697423f --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeaseMachineConfig.java @@ -0,0 +1,104 @@ +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; + + +/** + *

+ * GPU对应币种挖矿算力(只要gpu类型相同那么就使用这个配置) + *

+ * + * @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; + + @TableField(exist = false) + private LocalDateTime effectTime; + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeaseMachinePrice.java b/src/main/java/com/m2pool/lease/entity/LeaseMachinePrice.java new file mode 100644 index 0000000..9df1eb8 --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeaseMachinePrice.java @@ -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; + +/** + *

+ * 商品表对应的物品机器表 + *

+ * + * @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; + + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeaseMachineTempConfig.java b/src/main/java/com/m2pool/lease/entity/LeaseMachineTempConfig.java new file mode 100644 index 0000000..b3949ce --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeaseMachineTempConfig.java @@ -0,0 +1,105 @@ +package com.m2pool.lease.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.*; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + *

+ * GPU对应币种挖矿算力临时表(主机gpu信息变更临时表) + *

+ * + * @author yyb + * @since 2025-12-09 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class LeaseMachineTempConfig implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * id + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 机器id + */ + private Long machineId; + + /** + * gpu/asic品牌 + */ + private String brand; + + /** + * gpu/asic 名称/型号 + */ + private String name; + + /** + * 内存单位M + */ + private Integer memory; + + /** + * 0 禁用 1 启用 + */ + private Boolean status; + + /** + * 支持的币种 + */ + private String coin; + + /** + * 算力 + */ + private BigDecimal hashrate; + + /** + * 算法 + */ + private String algorithm; + + /** + * 币种图片 + */ + private String icon; + + /** + * 算力单位 GH/s TH/s PH/s等 + */ + private String unit; + + /** + * 月收益usdt + */ + private BigDecimal monthIncome; + + /** + * 功耗 单位kw/h + */ + private BigDecimal powerDissipation; + + /** + * 逻辑删除字段 + */ + private Boolean del; + + /** + * 创建时间 + */ + private LocalDateTime effectTime; + + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeaseMiningSoftwareConfig.java b/src/main/java/com/m2pool/lease/entity/LeaseMiningSoftwareConfig.java new file mode 100644 index 0000000..1a0642f --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeaseMiningSoftwareConfig.java @@ -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; + +/** + *

+ * 挖矿软件公共配置表 + *

+ * + * @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; + + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeaseNexaMachineAvgPower.java b/src/main/java/com/m2pool/lease/entity/LeaseNexaMachineAvgPower.java new file mode 100644 index 0000000..f46fffc --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeaseNexaMachineAvgPower.java @@ -0,0 +1,60 @@ +package com.m2pool.lease.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.*; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + *

+ * 矿池nexa机器实时平均算力 + *

+ * + * @author yyb + * @since 2025-07-29 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class LeaseNexaMachineAvgPower implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 挖矿账户 + */ + private String user; + + /** + * 矿工 + */ + private String miner; + + /** + * 记录开始时间 + */ + private LocalDateTime startTime; + + /** + * 记录结束时间 + */ + private LocalDateTime endTime; + + /** + * 实时平均算力 + */ + private BigDecimal realAvgPower; + + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeaseNexaMachinePower.java b/src/main/java/com/m2pool/lease/entity/LeaseNexaMachinePower.java new file mode 100644 index 0000000..633d1b4 --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeaseNexaMachinePower.java @@ -0,0 +1,65 @@ +package com.m2pool.lease.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.*; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + *

+ * 已售商品矿工实时算力表 + *

+ * + * @author yyb + * @since 2025-07-25 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class LeaseNexaMachinePower implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 挖矿账户 + */ + private String user; + + /** + * 矿工 + */ + private String miner; + + /** + * 记录时间(每五分钟整点) + */ + private LocalDateTime date; + + /** + * 五分钟一次的矿工矿机算力 + */ + private BigDecimal accepts; + + /** + * 矿机在离线状态 离线offline 在线online + */ + private String state; + + /** + * 实际记录时间 + */ + private LocalDateTime lastSubmit; + + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeaseOrderFee.java b/src/main/java/com/m2pool/lease/entity/LeaseOrderFee.java new file mode 100644 index 0000000..bfc8a6e --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeaseOrderFee.java @@ -0,0 +1,46 @@ +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; + +/** + *

+ * 订单手续费表 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class LeaseOrderFee implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * 订单 ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + private Long orderId; + + private BigDecimal fee; + + private String toAddress; + + private Boolean status; + + + + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeaseOrderInfo.java b/src/main/java/com/m2pool/lease/entity/LeaseOrderInfo.java new file mode 100644 index 0000000..154209a --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeaseOrderInfo.java @@ -0,0 +1,91 @@ +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; + +/** + *

+ * 订单表 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class LeaseOrderInfo implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * 订单 ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 订单号 + */ + private String orderNumber; + + /** + * 用户 ID + */ + private String userId; + + /** + * 用户自增id(外键) + */ + private Long authId; + + /** + * 订单总价 + */ + private BigDecimal totalPrice; + + /** + * 订单状态,0 表示待支付,1 表示(全部)已支付,2 表示已取消 3售后状态 4已退款 5已超时 10 部分已支付 + */ + private Integer status; + /** + * 下单时间 + */ + private LocalDateTime createTime; + + /** + * 更新时间 + */ + private LocalDateTime updateTime; + + /** + * 逻辑删除字段 + */ + private Boolean del; + + /** + * 手续费 + */ + private BigDecimal fee; + + /** + * 店铺名称 + */ + private String shopName; + + @TableField(exist = false) + private String chainAndCoinAndShopIdKey; + + + @TableField(exist = false) + private String chainAndCoinKey; + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeaseOrderItem.java b/src/main/java/com/m2pool/lease/entity/LeaseOrderItem.java new file mode 100644 index 0000000..b93d9f0 --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeaseOrderItem.java @@ -0,0 +1,185 @@ +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; + +/** + *

+ * 订单明细表 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class LeaseOrderItem implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * 订单明细 ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 店铺 ID + */ + private Long shopId; + + /** + * 用户 ID(邮箱) + */ + private String userId; + /** + * 订单 ID + */ + private Long orderId; + + /** + * 商品 ID + */ + private Long productId; + + /** + * 机器id + */ + private Long productMachineId; + + /** + * 矿工账号 + */ + private String user; + + /** + * 矿机编号 + */ + private String miner; + + /** + * 商品数量 + */ + private Integer quantity; + + /** + * 收货地址(模拟) + */ + private String address; + + + /** + *卖家地址链名称 + */ + private String chain; + + /** + * 卖家地址支付币种 + */ + private String symbol; + + private String fromAddress; + + private String fromChain; + + private String fromSymbol; + + /** + * 租期 + */ + private Integer leaseTime; + + /** + * 支付币种 + */ + private String payCoin; + + /** + * 商品单价 + */ + private BigDecimal price; + + /** + * 矿机挖矿币种 nexa rxd dgbo dgbq dgbs alph enx grs mona + */ + private String coin; + + /** + * 单机理论算力 + */ + private BigDecimal theoryPower; + + /** + * 单机理论收益 + */ + private BigDecimal theoryIncome; + + /** + * 单机预估实际收益 + */ + //private BigDecimal actualIncome; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + + /** + * 更新时间 + */ + private LocalDateTime updateTime; + + /** + * 逻辑删除字段 + */ + private Boolean del; + + /** + * 0 租约已过期 1 租约生效中 + */ + private Integer status; + + + /** + * 商品名称 + */ + private String name; + /** + * 商品图片路径 + */ + private String image; + + /** + * 已支付金额 + */ + private BigDecimal alreadyPayAmount; + + /** + * 已支付金额(真实) + */ + private BigDecimal alreadyPayRealAmount; + + /** + * 当日待支付确认金额 + */ + private BigDecimal settlePayRealAmount; + + private Integer numbers; + + + /** + * 0 矿机 asic 1 矿池 gpu + */ + private Boolean type; + + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeaseOrderMining.java b/src/main/java/com/m2pool/lease/entity/LeaseOrderMining.java new file mode 100644 index 0000000..c43793e --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeaseOrderMining.java @@ -0,0 +1,116 @@ +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; + +/** + *

+ * 挖矿中订单 + *

+ * + * @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 machineId; + + /** + * 用户 ID + */ + private String userId; + + /** + * 用户主键id + */ + private Long authId; + + /** + * 订单 ID + */ + private Long orderId; + + private Long orderItemId; + + /** + * 币种 + */ + private String coin; + + /** + * 算法 + */ + private String algorithm; + + /** + * 矿池 + */ + private String pool; + + /** + * 挖矿地址 + */ + private String poolUrl; + + /** + * 挖矿账户 + */ + private String poolUser; + + /** + * 矿工号 + */ + private String workerId; + + /** + * 收款钱包 + */ + private String walletAddress; + + /** + * 挖矿信息页面 + */ + private String watchUrl; + + /** + * 0 租约已到期 1挖矿中 2等待卖家矿机启动中 + */ + private Boolean status; + + /** + * 挖矿开始时间 + */ + private LocalDateTime startTime; + + /** + * 挖矿结束时间 + */ + private LocalDateTime endTime; + + /** + * 逻辑删除字段 + */ + private Boolean del; + + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeasePayRechargeMessage.java b/src/main/java/com/m2pool/lease/entity/LeasePayRechargeMessage.java new file mode 100644 index 0000000..766bdf9 --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeasePayRechargeMessage.java @@ -0,0 +1,105 @@ +package com.m2pool.lease.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.*; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + *

+ * 充值记录表 + *

+ * + * @author yyb + * @since 2025-09-10 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class LeasePayRechargeMessage implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + *消息ID + */ + private String queueId; + + + /** + * 充值地址 + */ + private String address; + + /** + * 支付金额 + */ + private BigDecimal amount; + /** + * 币种 + */ + private String symbol; + + /** + * 链名称 + */ + private String chain; + + // + ///** + // * 币种 + // */ + //private String fromSymbol; + // + ///** + // * 链名称 + // */ + //private String fromChain; + + /** + * 充值时间 + */ + private LocalDateTime createTime; + + /** + * 更新时间 + */ + private LocalDateTime updateTime; + + /** + * 交易hash + */ + private String txHash; + + /** + * 0 充值失败 1 充值成功 2 充值中 + */ + private Integer status; + + /** + * 逻辑删除字段 + */ + private Boolean del; + + /** + * 区块高度 + */ + private Long blockHeight; + + /** + * 充值来源地址(用户自己的钱包 + */ + private String fromAddress; + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeasePayRecordMessage.java b/src/main/java/com/m2pool/lease/entity/LeasePayRecordMessage.java new file mode 100644 index 0000000..05ccc6b --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeasePayRecordMessage.java @@ -0,0 +1,150 @@ +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; + +/** + *

+ * 支付消息记录表 + *

+ * + * @author yyb + * @since 2025-09-10 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class LeasePayRecordMessage implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 用户主键ID + */ + private Long authId; + + /** + * 消息ID + */ + private String queueId; + + /** + * 买家充值地址 + */ + private String fromAddress; + + /** + * 卖家充值地址 + */ + private String toAddress; + + + /** + * 订单id + */ + private String orderId; + + /** + * 订单号 + */ + private String orderNumber; + + /** + * 店铺ID + */ + private Long shopId; + + /** + * 用户邮箱 + */ + private String userId; + + /** + * 理论支付金额(根据商家定价) + */ + private BigDecimal amount; + + /** + *真实支付金额 + */ + private BigDecimal realAmount; + + /** + * 实际应支付金额(根据一天内预估算力和实际算力差值计算得来) + */ + private BigDecimal needAmount; + + /** + * 钱包冻结金额 + */ + private BigDecimal blockAmount; + /** + * 币种 + */ + private String toSymbol; + + /** + * 链名称 + */ + private String toChain; + + + /** + * 币种 + */ + private String fromSymbol; + + /** + * 链名称 + */ + private String fromChain; + + /** + * 交易hash + */ + private String txHash; + + private Long blockHeight; + + + /** + * 支付时间 + */ + private LocalDateTime createTime; + + /** + * 更新时间 + */ + private LocalDateTime updateTime; + + /** + * 0 支付失败 1 支付成功 2 等待校验 + */ + private Integer status; + + /** + * 逻辑删除字段 + */ + private boolean del; + + @TableField(exist = false) + private String sellerEmail; + + @TableField(exist = false) + private Long sellerId; + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeasePayRecordMessageInfo.java b/src/main/java/com/m2pool/lease/entity/LeasePayRecordMessageInfo.java new file mode 100644 index 0000000..777302e --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeasePayRecordMessageInfo.java @@ -0,0 +1,143 @@ +package com.m2pool.lease.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.*; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + *

+ * 支付消息记录表 + *

+ * + * @author yyb + * @since 2025-09-10 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class LeasePayRecordMessageInfo implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 用户主键ID + */ + private Long authId; + + /** + * 消息ID + */ + private String queueId; + + /** + * 买家充值地址 + */ + private String fromAddress; + + /** + * 卖家充值地址 + */ + private String toAddress; + + + /** + * 订单id + */ + private String orderId; + + /** + * 订单号 + */ + private String orderNumber; + + /** + * 店铺ID + */ + private Long shopId; + + /** + * 用户邮箱 + */ + private String userId; + + /** + * 理论支付金额(根据商家定价) + */ + private BigDecimal amount; + + /** + *真实支付金额 + */ + private BigDecimal realAmount; + + /** + * 实际应支付金额(根据一天内预估算力和实际算力差值计算得来) + */ + private BigDecimal needAmount; + + /** + * 钱包冻结金额 + */ + private BigDecimal blockAmount; + /** + * 币种 + */ + private String toSymbol; + + /** + * 链名称 + */ + private String toChain; + + + /** + * 币种 + */ + private String fromSymbol; + + /** + * 链名称 + */ + private String fromChain; + + /** + * 交易hash + */ + private String txHash; + + private Long blockHeight; + + + /** + * 支付时间 + */ + private LocalDateTime createTime; + + /** + * 更新时间 + */ + private LocalDateTime updateTime; + + /** + * 0 支付失败 1 支付成功 2 等待校验 + */ + private Integer status; + + /** + * 逻辑删除字段 + */ + private boolean del; + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeasePayWithdrawMessage.java b/src/main/java/com/m2pool/lease/entity/LeasePayWithdrawMessage.java new file mode 100644 index 0000000..d7c51ec --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeasePayWithdrawMessage.java @@ -0,0 +1,121 @@ +package com.m2pool.lease.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.*; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + *

+ * 提现记录表 + *

+ * + * @author yyb + * @since 2025-09-10 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class LeasePayWithdrawMessage implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + + /** + * 用户主键ID + */ + private Long authId; + + /** + * 店铺ID + */ + private Long shopId; + + /** + * 消息ID + */ + private String queueId; + + /** + * 用户充值地址 + */ + private String fromAddress; + + /** + * 提现地址(用户自定义) + */ + private String toAddress; + + /** + * 支付金额 + */ + private BigDecimal amount; + + /** + * 交易hash + */ + private String txHash; + + /** + * 手续费 + */ + private BigDecimal serviceCharge; + + /** + * 币种 + */ + private String toSymbol; + + /** + * 链名称 + */ + private String toChain; + + + /** + * 币种 + */ + private String fromSymbol; + + /** + * 链名称 + */ + private String fromChain; + + /** + * 充值时间 + */ + private LocalDateTime createTime; + + /** + * 更新时间 + */ + private LocalDateTime updateTime; + + /** + * 0 支付失败 1 支付成功 2 支付中 + */ + private Integer status; + + /** + * 逻辑删除字段 + */ + private Boolean del; + + /** + * 区块高度 + */ + private Long blockHeight; + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeasePaymentRecord.java b/src/main/java/com/m2pool/lease/entity/LeasePaymentRecord.java new file mode 100644 index 0000000..daf1a5c --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeasePaymentRecord.java @@ -0,0 +1,109 @@ +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; + +/** + *

+ * 支付记录表 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class LeasePaymentRecord implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * 支付记录 ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 订单 ID + */ + private Long orderId; + + /** + * 店铺 ID + */ + private Long shopId; + + /** + * 商品 ID + */ + private Long productId; + + /** + * 支付方式,nexa rxd dgbo dgbq dgbs alph enx grs mona usdt usdc busd + */ + private String payCoin; + + /** + * 支付金额 + */ + private BigDecimal amount; + + /** + * 已支付金额 + */ + private BigDecimal payAmount; + + /** + * 支付状态0支付失败 1支付成功--全部货款已支付 2待支付 5支付已超时 10支付成功--已支付部分货款 + */ + private Integer status; + + /** + * 支付地址 + */ + private String payAddress; + + /** + * 交易hash(交易成功后会生成) + */ + private String hash; + + + /** + * 买家钱包地址 + */ + + @TableField("`from`") + private String from; + + /** + * 支付二维码 + */ + private String qrcode; + + /** + * 支付时间 + */ + private LocalDateTime createTime; + + /** + * 更新时间 + */ + private LocalDateTime updateTime; + + /** + * 逻辑删除字段 + */ + private Boolean del; + + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeaseProduct.java b/src/main/java/com/m2pool/lease/entity/LeaseProduct.java new file mode 100644 index 0000000..36a0c16 --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeaseProduct.java @@ -0,0 +1,115 @@ +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; + +/** + *

+ * 商品表 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class LeaseProduct implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * 商品 ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 店铺id + */ + private Long shopId; + + /** + * 商品名称 + */ + private String name; + + /** + * 商品图片路径 + */ + private String image; + + /** + * 商品类型,0 挖矿机器套餐,1 算力套餐 + */ + private Integer type; + + /** + * 上下架状态,0 上架,1 下架 + */ + private Integer state; + + /** + * 最高价格 + */ + private BigDecimal maxPrice; + + /** + * 最低价格 + */ + private BigDecimal minPrice; + + + /** + * 矿机挖矿币种 nexa rxd dgbo dgbq dgbs alph enx grs mona + */ + private String coin; + + /** + * 矿机挖矿币种全称 + */ + private String coinFullName; + + /** + * 算法 + */ + private String algorithm; + + /** + * 商品描述 + */ + private String description; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + + /** + * 更新时间 + */ + private LocalDateTime updateTime; + + /** + * 逻辑删除字段 + */ + private Boolean del; + + /** + * 销售机器数 + */ + private Integer saleNumber; + + /** + * 总矿机数 + */ + private Integer totalMachineNumber; +} diff --git a/src/main/java/com/m2pool/lease/entity/LeaseProductIncome.java b/src/main/java/com/m2pool/lease/entity/LeaseProductIncome.java new file mode 100644 index 0000000..fb8a232 --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeaseProductIncome.java @@ -0,0 +1,60 @@ +package com.m2pool.lease.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.*; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + *

+ * 商品收益表 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class LeaseProductIncome implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * 收益记录 ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 用户拥有商品详情 ID,关联 lease_user_owned_product 表 + */ + private Long userOwnedProductId; + + /** + * 预估商品结束时的总收益 + */ + private BigDecimal estimatedEndIncome; + + /** + * 当前实际总收益 + */ + private BigDecimal currentActualIncome; + + /** + * 记录创建时间 + */ + private LocalDateTime createTime; + + /** + * 记录更新时间 + */ + private LocalDateTime updateTime; + + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeaseProductMachine.java b/src/main/java/com/m2pool/lease/entity/LeaseProductMachine.java new file mode 100644 index 0000000..503e2c2 --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeaseProductMachine.java @@ -0,0 +1,150 @@ +package com.m2pool.lease.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +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; +import java.util.concurrent.atomic.AtomicInteger; + +/** + *

+ * 商品表对应的物品机器表 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class LeaseProductMachine implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * 主键ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + + /** + * 店铺ID + */ + private Long shopId; + + /** + * 商品 ID + */ + private Long productId; + + /** + * 挖矿机器 对应的矿工账号 + */ + private String user; + + /** + * 挖矿机器编号 + */ + private String miner; + + /** + * 矿机挖矿币种 nexa rxd dgbo dgbq dgbs alph enx grs mona + */ + private String coin; + + /** + * 单机理论收入(每日) + */ + private BigDecimal theoryIncome; + + /** + * 单机预估实际收入(每日) + */ + //private BigDecimal actualIncome; + + /** + * 矿机型号 + */ + private String type; + + /** + * 实际价格单价 + */ + private BigDecimal price; + + /** + * 算力大小(计算得到,商家不能够自己添加和修改) + */ + private BigDecimal computingPower; + + /** + * 上下架状态,0 上架,1 下架 + */ + private Integer state; + + /** + * 售出状态 0未售出 1已售出 2售出中 + */ + private Integer saleState; + + /** + * 逻辑删除字段 + */ + private Boolean del; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + + /** + * 更新时间 + */ + private LocalDateTime updateTime; + + + /** + * 商品机器单机理论算力(卖方手动填写) + */ + private BigDecimal theoryPower; + + /** + * 商品机器单机算力单位 + */ + private String unit; + + + /** + * 功耗 单位kw/h + */ + private BigDecimal powerDissipation; + + /** + * 默认价格$ [ 功耗 * 电费 * 24 * (1 + 收益率) ] + */ + private BigDecimal cost; + + + /** + * 电费 单位 $/度 + */ + //private BigDecimal electricityBill; + + /** + * 收益率 单位 % + */ + private BigDecimal incomeRate; + + private Integer maxLeaseDays; + + @TableField(exist = false) + private Integer updateCount; +} diff --git a/src/main/java/com/m2pool/lease/entity/LeaseProductMachinePrice.java b/src/main/java/com/m2pool/lease/entity/LeaseProductMachinePrice.java new file mode 100644 index 0000000..ce17305 --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeaseProductMachinePrice.java @@ -0,0 +1,48 @@ +package com.m2pool.lease.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.*; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + *

+ * 商品表对应的物品机器表 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class LeaseProductMachinePrice implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * 主键ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + private Long productMachineId; + + /** + * 0 自营矿池 1 非自营矿池(gpu 和asic) + */ + private Boolean type; + + private BigDecimal price; + + private String coin; + + private String chain; + + private Boolean del; +} diff --git a/src/main/java/com/m2pool/lease/entity/LeaseShop.java b/src/main/java/com/m2pool/lease/entity/LeaseShop.java new file mode 100644 index 0000000..ea78c39 --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeaseShop.java @@ -0,0 +1,92 @@ +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; + +/** + *

+ * 店铺表 + *

+ * + * @author yyb + * @since 2025-08-05 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class LeaseShop implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * 店铺ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 用户ID + */ + private Long authId; + + /** + * 店铺唯一标识码 + */ + private String identityCode; + + /** + * 店铺拥有者邮箱 + */ + private String userEmail; + + /** + * 店铺名称 + */ + private String name; + + /** + * 店铺图片路径 + */ + private String image; + + /** + * 店铺描述 + */ + private String description; + + /** + * 商铺状态 0 待审核 1 审核通过(店铺开启) 2 店铺关闭 10 店铺开启 + */ + private Integer state; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + + /** + * 更新时间 + */ + private LocalDateTime updateTime; + + /** + * 逻辑删除字段 + */ + private Boolean del; + + private BigDecimal feeRate; + + /** + * 售出矿机数 + */ + private Integer saleNumber; + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeaseShopAddressConfig.java b/src/main/java/com/m2pool/lease/entity/LeaseShopAddressConfig.java new file mode 100644 index 0000000..18799e8 --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeaseShopAddressConfig.java @@ -0,0 +1,52 @@ +package com.m2pool.lease.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.*; + +import java.io.Serializable; + +/** + *

+ * 矿池nexa机器实时平均算力 - 商铺地址配置表 + *

+ * + * @author yyb + * @since 2025-09-04 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class LeaseShopAddressConfig implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 买方账号id(邮箱) + */ + private String userId; + + /** + * 卖方收款钱包 每个买方对应一个地址(自动生成匹配) + */ + private String address; + + /** + * 商铺收款钱包二维码 + */ + private String qrcode; + + /** + * 商铺ID + */ + private Long shopId; + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeaseShopConfig.java b/src/main/java/com/m2pool/lease/entity/LeaseShopConfig.java new file mode 100644 index 0000000..e7964d6 --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeaseShopConfig.java @@ -0,0 +1,98 @@ +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; + +/** + *

+ * 店铺商品配置表 + *

+ * + * @author yyb + * @since 2025-08-05 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class LeaseShopConfig implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * 配置ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 商铺ID + */ + private Long shopId; + + /** + * 商品 ID (商品id为0代表适用于店铺中所有商品) + */ + private Long productId; + + /** + * 链名称 + */ + private String chain; + + /** + * nexa rxd dgbo dgbq dgbs alph enx grs mona usdt + */ + private String payCoin; + + /** + * 币种图标 + */ + private String payCoinImage; + + /** + * 币种类型 0 虚拟币 1 稳定币 + */ + private Integer payType; + + /** + * 卖方对应收款钱包 + */ + private String payAddress; + + /** + * 支付二维码 + */ + private String qrcode; + + /** + * 余额(这个余额是租赁系统的收款余额,而不是卖家钱包真实余额,该余额只能用于提现) + */ + private BigDecimal balance; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + + /** + * 更新时间 + */ + private LocalDateTime updateTime; + + /** + * 逻辑删除字段 + */ + private Boolean del; + + @TableField(exist = false) + private BigDecimal price; + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeaseShoppingCart.java b/src/main/java/com/m2pool/lease/entity/LeaseShoppingCart.java new file mode 100644 index 0000000..0869570 --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeaseShoppingCart.java @@ -0,0 +1,49 @@ +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; + +/** + *

+ * 购物车表 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class LeaseShoppingCart implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * 购物车 ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 用户 ID(邮箱) + */ + private String userId; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + + /** + * 更新时间 + */ + private LocalDateTime updateTime; + + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeaseShoppingCartInfo.java b/src/main/java/com/m2pool/lease/entity/LeaseShoppingCartInfo.java new file mode 100644 index 0000000..0a51d10 --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeaseShoppingCartInfo.java @@ -0,0 +1,79 @@ +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; + +/** + *

+ * 购物车详情表 + *

+ * + * @author yyb + * @since 2025-07-24 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class LeaseShoppingCartInfo implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 用户ID + */ + private String userId; + + /** + * 商店ID + */ + private Long shopId; + + /** + * 商品 ID + */ + private Long productId; + + /** + * 商品机器ID + */ + private Long productMachineId; + + /** + * 租期 + */ + private Integer leaseTime; + + /** + * ASIC 租售台数 + */ + private Integer numbers; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + + /** + * 更新时间 + */ + private LocalDateTime updateTime; + + /** + * 版本 0 旧版本 1 新版本 + */ + private Integer version; + + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeaseUser.java b/src/main/java/com/m2pool/lease/entity/LeaseUser.java new file mode 100644 index 0000000..8e785d8 --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeaseUser.java @@ -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.time.LocalDateTime; + +/** + *

+ * 用户表 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class LeaseUser implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 用户ID(邮箱) + */ + private String userId; + + /** + * 密码 + */ + private String password; + /** + * 登录token + */ + private String token; + + /** + * 最后登录时间 + */ + private LocalDateTime lastLoginTime; + /** + * 创建时间 + */ + private LocalDateTime createTime; + + /** + * 更新时间 + */ + private LocalDateTime updateTime; + + private Boolean del; + + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeaseUserOwnedProduct.java b/src/main/java/com/m2pool/lease/entity/LeaseUserOwnedProduct.java new file mode 100644 index 0000000..853aac5 --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeaseUserOwnedProduct.java @@ -0,0 +1,143 @@ +package com.m2pool.lease.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.*; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + *

+ * 用户拥有商品详情表 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class LeaseUserOwnedProduct implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 用户ID(暂时使用租赁者钱包地址) + */ + private String userId; + + private Long orderId; + + + /** + * 挖矿机器 对应的矿工账号 + */ + private String user; + + /** + * 挖矿机器编号 + */ + private String miner; + + + /** + * 商品对应的机器id + */ + private Long productMachineId; + + /** + * 订单明细 ID,关联订单中的购买记录 + */ + private Long orderItemId; + + + + /** + * 商品开始使用时间(购买当天24点生效) + */ + private LocalDateTime startTime; + + /** + * 商品使用结束时间 + */ + private LocalDateTime endTime; + + /** + * 挖矿币种 nexa rxd dgbo dgbq dgbs alph enx grs mona + */ + private String coin; + + /** + * 购买算力的量,当 type 为 1 时有效 + */ + private BigDecimal purchasedComputingPower; + + + /** + * 预估商品结束时的总收益 单位币种 + */ + private BigDecimal estimatedEndIncome; + + + /** + * 预估商品结束时的总收益 单位usdt + */ + private BigDecimal estimatedEndUsdtIncome; + + /** + * 当前收益(根据购买机器到现在的平均算力计算得到) 单位币种 + */ + private BigDecimal currentIncome; + + + /** + * 当前收益(根据购买机器到现在的平均算力计算得到)单位usdt + */ + private BigDecimal currentUsdtIncome; + + private BigDecimal settleIncome ; + + private BigDecimal settleUsdtIncome ; + + /** + * 商品状态,0 表示运行中,1 表示已过期 + */ + private Integer status; + + /** + * 商品类型,0 表示挖矿机器,1 表示算力套餐 + */ + private Integer type; + + /** + * 卖方钱包地址 + */ + private String address; + + /** + * 购买时间 + */ + private LocalDateTime createTime; + + /** + * 更新时间 + */ + private LocalDateTime updateTime; + + /** + * 逻辑删除字段 + */ + private Boolean del; + + +} diff --git a/src/main/java/com/m2pool/lease/entity/LeaseUserWalletData.java b/src/main/java/com/m2pool/lease/entity/LeaseUserWalletData.java new file mode 100644 index 0000000..ffd4d17 --- /dev/null +++ b/src/main/java/com/m2pool/lease/entity/LeaseUserWalletData.java @@ -0,0 +1,116 @@ +package com.m2pool.lease.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.sun.org.apache.xpath.internal.operations.Bool; +import io.swagger.annotations.ApiModelProperty; +import lombok.*; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + *

+ * 用户钱包信息表 + *

+ * + * @author 根据实际情况填写 + * @since 2025-09-08 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class LeaseUserWalletData implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + + /** + * 钱包唯一id(用于充值功能) + */ + private String queueId; + /** + * 用户id(邮箱) + */ + private String userId; + + /** + * 用户id(主键) + */ + private Long authId; + + /** + * 充值钱包地址(我们提供) + */ + private String fromAddress; + + /** + * 收款钱包地址(用户自己填写) + */ + private String toAddress; + + /** + * 币种 + */ + private String toSymbol; + + /** + * 链名称 + */ + private String toChain; + + + /** + * 币种 + */ + private String fromSymbol; + + /** + * 链名称 + */ + private String fromChain; + + /** + * 余额 + */ + private BigDecimal balance; + + /** + * 冻结余额(用户购买机器完成租约所需金额) + */ + private BigDecimal blockedBalance; + + /** + * 支付地址二维码 + */ + private String qrcode; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + + /** + * 更新时间 + */ + private LocalDateTime updateTime; + + /** + * 删除标识 + */ + private Boolean del; + + @TableField(exist = false) + private Long shopId; + +} diff --git a/src/main/java/com/m2pool/lease/exception/AuthException.java b/src/main/java/com/m2pool/lease/exception/AuthException.java new file mode 100644 index 0000000..dd0dfc2 --- /dev/null +++ b/src/main/java/com/m2pool/lease/exception/AuthException.java @@ -0,0 +1,10 @@ +package com.m2pool.lease.exception; + +/** + * 权限校验异常 + */ +public class AuthException extends RuntimeException { + public AuthException(String message) { + super(message); + } +} diff --git a/src/main/java/com/m2pool/lease/exception/GlobalExceptionHandler.java b/src/main/java/com/m2pool/lease/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..8e658a2 --- /dev/null +++ b/src/main/java/com/m2pool/lease/exception/GlobalExceptionHandler.java @@ -0,0 +1,66 @@ +package com.m2pool.lease.exception; + +import cn.hutool.json.JSONUtil; +import com.m2pool.lease.dto.Result; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +/** + * 全局异常处理器,用于捕获并处理应用中抛出的异常。 + */ +@RestControllerAdvice +public class GlobalExceptionHandler { + + /** + * 处理商品已出售异常,返回统一的错误结果。 + * + * @param e 商品已出售异常对象 + * @return 包含错误信息的统一结果对象 + */ + @ExceptionHandler(ProductSoldOutException.class) + public Result handleProductSoldOutException(ProductSoldOutException e) { + return Result.fail(e.getMessage()); + } + + @ExceptionHandler(MachineException.class) + public Result handleMachineException(MachineException e) { + return Result.fail(e.getMessage()); + } + + @ExceptionHandler(PaymentException.class) + public Result handlePaymentException(PaymentException e) { + return Result.fail(e.getMessage()); + } + + @ExceptionHandler(OrderException.class) + public Result handleOrderException(OrderException e) { + return Result.fail(e.getMessage()); + } + + @ExceptionHandler(PayRechargeException.class) + public Result handleOrderException(PayRechargeException e) { + return Result.fail(e.getMessage()); + } + + @ExceptionHandler(RSAException.class) + public Result handleRSAException(RSAException e) { + return Result.fail(e.getMessage()); + } + + @ExceptionHandler(AuthException.class) + public Result handleAuthException(AuthException e) { + return Result.fail(e.getMessage()); + } + + /** + * 处理其他未明确捕获的异常,返回统一的错误结果。 + * + * @param e 异常对象 + * @return 包含错误信息的统一结果对象 + */ + //@ExceptionHandler(Exception.class) + //public Result handleException(Exception e) { + // System.out.println(e); + // return Result.fail("系统异常,请稍后再试!"); + //} +} diff --git a/src/main/java/com/m2pool/lease/exception/MachineException.java b/src/main/java/com/m2pool/lease/exception/MachineException.java new file mode 100644 index 0000000..bb868ac --- /dev/null +++ b/src/main/java/com/m2pool/lease/exception/MachineException.java @@ -0,0 +1,10 @@ +package com.m2pool.lease.exception; + +/** + * 商品已出售异常类,用于表示在添加订单时商品已被出售的异常情况。 + */ +public class MachineException extends RuntimeException { + public MachineException(String message) { + super(message); + } +} diff --git a/src/main/java/com/m2pool/lease/exception/OrderException.java b/src/main/java/com/m2pool/lease/exception/OrderException.java new file mode 100644 index 0000000..a0efa10 --- /dev/null +++ b/src/main/java/com/m2pool/lease/exception/OrderException.java @@ -0,0 +1,10 @@ +package com.m2pool.lease.exception; + +/** + * 订单异常 + */ +public class OrderException extends RuntimeException { + public OrderException(String message) { + super(message); + } +} diff --git a/src/main/java/com/m2pool/lease/exception/PayRechargeException.java b/src/main/java/com/m2pool/lease/exception/PayRechargeException.java new file mode 100644 index 0000000..3732663 --- /dev/null +++ b/src/main/java/com/m2pool/lease/exception/PayRechargeException.java @@ -0,0 +1,10 @@ +package com.m2pool.lease.exception; + +/** + * 支付异常 + */ +public class PayRechargeException extends RuntimeException { + public PayRechargeException(String message) { + super(message); + } +} diff --git a/src/main/java/com/m2pool/lease/exception/PaymentException.java b/src/main/java/com/m2pool/lease/exception/PaymentException.java new file mode 100644 index 0000000..fb33400 --- /dev/null +++ b/src/main/java/com/m2pool/lease/exception/PaymentException.java @@ -0,0 +1,10 @@ +package com.m2pool.lease.exception; + +/** + * 支付异常 + */ +public class PaymentException extends RuntimeException { + public PaymentException(String message) { + super(message); + } +} diff --git a/src/main/java/com/m2pool/lease/exception/ProductSoldOutException.java b/src/main/java/com/m2pool/lease/exception/ProductSoldOutException.java new file mode 100644 index 0000000..40f1c02 --- /dev/null +++ b/src/main/java/com/m2pool/lease/exception/ProductSoldOutException.java @@ -0,0 +1,10 @@ +package com.m2pool.lease.exception; + +/** + * 商品已出售异常类,用于表示在添加订单时商品已被出售的异常情况。 + */ +public class ProductSoldOutException extends RuntimeException { + public ProductSoldOutException(String message) { + super(message); + } +} diff --git a/src/main/java/com/m2pool/lease/exception/RSAException.java b/src/main/java/com/m2pool/lease/exception/RSAException.java new file mode 100644 index 0000000..7b5c4a9 --- /dev/null +++ b/src/main/java/com/m2pool/lease/exception/RSAException.java @@ -0,0 +1,10 @@ +package com.m2pool.lease.exception; + +/** + * 非对称加密异常 + */ +public class RSAException extends RuntimeException { + public RSAException(String message) { + super(message); + } +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeaseAutoAddressMapper.java b/src/main/java/com/m2pool/lease/mapper/LeaseAutoAddressMapper.java new file mode 100644 index 0000000..e44af86 --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeaseAutoAddressMapper.java @@ -0,0 +1,25 @@ +package com.m2pool.lease.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.m2pool.lease.dto.UserWalletDataDto; +import com.m2pool.lease.entity.LeaseAutoAddress; +import org.apache.ibatis.annotations.MapKey; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface LeaseAutoAddressMapper extends BaseMapper { + /** + * 获取一个未使用的地址 + * @return + */ + LeaseAutoAddress getOneStatusIsNoUse(); + + + /** + * 获取所有有效的地址 + * @return + */ + List getValidAddress(); +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeaseGpuConfigMapper.java b/src/main/java/com/m2pool/lease/mapper/LeaseGpuConfigMapper.java new file mode 100644 index 0000000..7a13982 --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeaseGpuConfigMapper.java @@ -0,0 +1,38 @@ +package com.m2pool.lease.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.m2pool.lease.dto.v2.CoinAndAlgorithmDto; +import com.m2pool.lease.entity.LeaseGpuConfig; +import com.m2pool.lease.netty.message.GpuMessage; +import com.m2pool.lease.task.info.BlockInfo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Collection; +import java.util.List; + +/** + *

+ * GPU对应币种挖矿算力(只要gpu类型相同那么就使用这个配置) Mapper 接口 + *

+ * + * @author yyb + * @since 2025-11-27 + */ +@Mapper +public interface LeaseGpuConfigMapper extends BaseMapper { + /** + * 批量插入或更新 + * @param gpuMessages + * @return + */ + int insertOrUpdateBatchByGpuInfo(@Param("list") List gpuMessages); + + /** + * 通过实体类批量插入或更新 + * @param leaseGpuConfigs + * @return + */ + int insertOrUpdateBatchByEntity(@Param("list") List leaseGpuConfigs); + +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeaseMachineConfigMapper.java b/src/main/java/com/m2pool/lease/mapper/LeaseMachineConfigMapper.java new file mode 100644 index 0000000..a1ba8ea --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeaseMachineConfigMapper.java @@ -0,0 +1,47 @@ +package com.m2pool.lease.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.m2pool.lease.dto.v2.*; +import com.m2pool.lease.entity.LeaseMachineConfig; +import com.m2pool.lease.vo.BaseVo; +import com.m2pool.lease.vo.v2.CoinAndAlgorithmVo; +import org.apache.ibatis.annotations.MapKey; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + *

+ * GPU对应币种挖矿算力(只要gpu类型相同那么就使用这个配置) Mapper 接口 + *

+ * + * @author yyb + * @since 2025-11-27 + */ +@Mapper +public interface LeaseMachineConfigMapper extends BaseMapper { + /** + * 获取配置列表 + * @param coin + * @param algorithm + * @return + */ + List getConfigList(@Param("coin") String coin,@Param("algorithm") String algorithm); + + + + /** + * 获取矿机支持的币种和算法 + * @return + */ + List getMachineSupportCoinAndAlgorithm(@Param("machineIds") List machineIds); + + + /** + * 获取理论算力 + * @return + */ + List getTheoryHashRate(@Param("list") List orderMiningInfoDtoList); +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeaseMachineMapper.java b/src/main/java/com/m2pool/lease/mapper/LeaseMachineMapper.java new file mode 100644 index 0000000..569e927 --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeaseMachineMapper.java @@ -0,0 +1,165 @@ +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; + +/** + *

+ * gpu类型出售矿机表 Mapper 接口 + *

+ * + * @author yyb + * @since 2025-11-21 + */ +public interface LeaseMachineMapper extends BaseMapper { + + /** + * 获取店铺ids + * @return + */ + Set getShopIds(); + + /** + * 根据id集合查询店铺信息 + * @return + */ + List getExistsMachineShop(@Param("ids") Set ids, @Param("coin") String coin , @Param("algorithm") String algorithm); + + + /** + * 获取店铺对应GPU或ASIC矿机信息 + * @param shopMachineVo + * @return + */ + List getShopMachineList(@Param("shopMachineVo") ShopMachineVo shopMachineVo); + + + /** + * 根据id集合获取gpu配置信息 + * @param ids + * @return + */ + List getGpuConfigList(@Param("ids") List ids); + + + /** + * 获取店铺Gpu或Asic对应矿机信息 + * @param sellerMachineVo + * @return + */ + List getShopMachineListForSeller(@Param("sellerMachineVo") SellerMachineVo sellerMachineVo, @Param("shopId") Long shopId); + + /** + * 根据矿机id获取对应的矿机价格列表 + * @param ids + * @return + */ + List getMachinePriceList(@Param("list") List ids); + + + /** + * 根据矿机id获取对应的矿机价格列表 + * @param machineIds + * @return + */ + List getShopIdsByMachineIds(@Param("machineIds") List machineIds); + + + /** + * 获取购物车对应矿机信息 + * @param machineIds + * @return + */ + List getMachinesByIds(@Param("machineIds") List machineIds); + + + /** + * 获取购物车对应矿机信息 + * @param ids + * @return + */ + List getCoinAndAlgoList(@Param("list") List ids); + + + /** + * 修改gpu矿机售出状态 + * @param asicMachines + * @return + */ + int updateLockState(@Param("list") List asicMachines); + + + /** + * 修改asic 矿机售出数量 + * @param gpuMachines + * @return + */ + int updateLockNumbers(@Param("list") List gpuMachines); + + + + /** + * 根据id 获取配置算法和币种 + * @param ids + * @return + */ + @MapKey("id") + Map getCoinAndAlgoById(@Param("ids") List ids); + + + + /** + * 获取矿机详情信息含临时信息 + * @param machineId + * @return + */ + List getEffectMachineAndTempMachineList(@Param("machineId") Long machineId); + + + /** + * 获取租赁系统中支持的币种(挖矿软件支持 + 矿池支持的交集) + * @return + */ + List getSupportCoin(); + + + /** + * 获取租赁系统中支持的算力(挖矿软件支持 + 矿池支持的交集) + * @return + */ + List getSupportAlgo(@Param("coin") String coin); + + /** + * 检测矿机是否能够上架 + * @param ids + * @return + */ + @MapKey("machineId") + Map checkIsEffect(@Param("list") List ids); + + + + /** + * 批量修改矿机已售可售数量 + * @param list + * @return + */ + boolean updateBatchSaleNumbers(@Param("list") List list); + + /** + * 检测店铺下是否有已出售的矿机 + * @return + */ + int checkHasSaleMachineByShopId(@Param("shopId") Long shopId); + +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeaseMachinePriceMapper.java b/src/main/java/com/m2pool/lease/mapper/LeaseMachinePriceMapper.java new file mode 100644 index 0000000..8c18674 --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeaseMachinePriceMapper.java @@ -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; + +/** + *

+ * 商品表对应的物品机器表 Mapper 接口 + *

+ * + * @author yyb + * @since 2025-11-25 + */ +@Mapper +public interface LeaseMachinePriceMapper extends BaseMapper { + /** + * 获取机器价格通过id + * @param machineIds + * @return + */ + List getMachinePriceByMachineIds(@Param("list") List machineIds); + + + /** + * 获取订单总金额 按照chain 和 分组 coin -v2 + * @param orderInfoVoList + * @return + */ + @MapKey("machineId") + Map getOrderTotalPriceGroupByChainAndCoin(@Param("list") List orderInfoVoList); + +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeaseMachineTempConfigMapper.java b/src/main/java/com/m2pool/lease/mapper/LeaseMachineTempConfigMapper.java new file mode 100644 index 0000000..dff63fe --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeaseMachineTempConfigMapper.java @@ -0,0 +1,35 @@ +package com.m2pool.lease.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.m2pool.lease.entity.LeaseMachineConfig; +import com.m2pool.lease.entity.LeaseMachineTempConfig; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + *

+ * GPU对应币种挖矿算力临时表(主机gpu信息变更临时表) Mapper 接口 + *

+ * + * @author yyb + * @since 2025-12-09 + */ +@Mapper +public interface LeaseMachineTempConfigMapper extends BaseMapper { + + /** + * 批量插入 + * @param needUpdateInfo + * @return + */ + int insertBatch(@Param("list") List needUpdateInfo); + + /** + * 批量插入 + * @param needUpdateInfo + * @return + */ + int insertBatchByTempList(@Param("list") List needUpdateInfo); +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeaseMiningSoftwareConfigMapper.java b/src/main/java/com/m2pool/lease/mapper/LeaseMiningSoftwareConfigMapper.java new file mode 100644 index 0000000..309f91a --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeaseMiningSoftwareConfigMapper.java @@ -0,0 +1,27 @@ +package com.m2pool.lease.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.m2pool.lease.dto.v2.CoinAndAlgorithmDto; +import com.m2pool.lease.entity.LeaseMiningSoftwareConfig; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + *

+ * 挖矿软件公共配置表 Mapper 接口 + *

+ * + * @author yyb + * @since 2025-11-27 + */ +@Mapper +public interface LeaseMiningSoftwareConfigMapper extends BaseMapper { + /** + * 查询支持的币种和算法 + * @param miningsofts + * @return + */ + List selectSupportAlgorithm(@Param("miningsofts") List miningsofts,@Param("modelList") List modelList); +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeaseNexaMachineAvgPowerMapper.java b/src/main/java/com/m2pool/lease/mapper/LeaseNexaMachineAvgPowerMapper.java new file mode 100644 index 0000000..260808b --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeaseNexaMachineAvgPowerMapper.java @@ -0,0 +1,37 @@ +package com.m2pool.lease.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.m2pool.lease.dto.UserMinerDto; +import com.m2pool.lease.entity.LeaseNexaMachineAvgPower; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + *

+ * 矿池nexa机器实时平均算力 Mapper 接口 + *

+ * + * @author yyb + * @since 2025-07-29 + */ +@Mapper +public interface LeaseNexaMachineAvgPowerMapper extends BaseMapper { + + /** + * 批量插入数据 + * + * @param list + * @return + */ + boolean insertBatchDatas(@Param("list") List list); + + /** + * 根据用户和矿工查询数据 + * + * @param userMinerList + * @return + */ + List selectListByUserAndMiner(@Param("list") List userMinerList); +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeaseNexaMachinePowerMapper.java b/src/main/java/com/m2pool/lease/mapper/LeaseNexaMachinePowerMapper.java new file mode 100644 index 0000000..1312823 --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeaseNexaMachinePowerMapper.java @@ -0,0 +1,46 @@ +package com.m2pool.lease.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.m2pool.lease.annotation.HashRateDB; +import com.m2pool.lease.dto.ProductMachineDto; +import com.m2pool.lease.entity.LeaseNexaMachinePower; +import com.m2pool.lease.entity.LeaseProductMachine; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Date; +import java.util.List; + +/** + *

+ * 已售商品矿工实时算力表 Mapper 接口 + *

+ * + * @author yyb + * @since 2025-07-25 + */ +@Mapper +public interface LeaseNexaMachinePowerMapper extends BaseMapper { + + /** + * 获取指定时间段内指定表内指定矿工的实时算力 + * @param leaseProductMachines + * @param pointDate + * @param tableName + * @return + */ + @HashRateDB + List getMachinePowerInHashRateById(@Param("leaseProductMachines") List leaseProductMachines, @Param("pointDate")Date pointDate, @Param("tableName") String tableName); + + /** + * 获取3天内算力总和 + * @param leaseProductMachines + * @param coin + * @return + */ + @HashRateDB + List getMachineThreePower(@Param("leaseProductMachines") List leaseProductMachines, @Param("coin") String coin); + + +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeaseOrderFeeMapper.java b/src/main/java/com/m2pool/lease/mapper/LeaseOrderFeeMapper.java new file mode 100644 index 0000000..4e475af --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeaseOrderFeeMapper.java @@ -0,0 +1,26 @@ +package com.m2pool.lease.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.m2pool.lease.dto.OrderStatusDto; +import com.m2pool.lease.entity.LeaseOrderFee; +import com.m2pool.lease.entity.LeaseOrderItem; +import com.m2pool.lease.entity.LeasePaymentRecord; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Set; + +/** + *

+ * 订单明细表 Mapper 接口 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Mapper +public interface LeaseOrderFeeMapper extends BaseMapper { + + +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeaseOrderInfoMapper.java b/src/main/java/com/m2pool/lease/mapper/LeaseOrderInfoMapper.java new file mode 100644 index 0000000..e886496 --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeaseOrderInfoMapper.java @@ -0,0 +1,61 @@ +package com.m2pool.lease.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.m2pool.lease.annotation.Pool2DB; +import com.m2pool.lease.dto.OrderInfoDto; +import com.m2pool.lease.entity.LeaseOrderInfo; +import com.m2pool.lease.entity.LeaseUserOwnedProduct; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.math.BigDecimal; +import java.util.List; + +/** + *

+ * 订单表 Mapper 接口 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Mapper +public interface LeaseOrderInfoMapper extends BaseMapper { + + /** + * 根据订单状态查询订单(买家) + * + * @param status 订单状态 + * @return 订单列表 + */ + List getOrdersByStatus(@Param("authId") Long authId, @Param("status") Integer status); + + + /** + * 根据订单状态查询订单(卖家) + * + * @param status 订单状态 + * @param ids 订单id集合 + * @return 订单列表 + */ + List getOrdersByStatusForSeller( @Param("status") Integer status,@Param("ids") List ids); + + /** + * 根据机器id集合查看订单详情信息 + * @param ids + * @return + */ + List getOrderItemsByMachineId(@Param("ids") List ids); + + /** + * 获取需要付款的订单列表 状态为7 订单进行中的订单 + * @return + */ + List getNeedPayOrderList(); + + @Pool2DB + BigDecimal getCoinPrice(@Param("coin") String coin); + + + +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeaseOrderItemMapper.java b/src/main/java/com/m2pool/lease/mapper/LeaseOrderItemMapper.java new file mode 100644 index 0000000..4ab96da --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeaseOrderItemMapper.java @@ -0,0 +1,83 @@ +package com.m2pool.lease.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.m2pool.lease.dto.OrderStatusDto; +import com.m2pool.lease.dto.v2.OrderTimeInfoDto; +import com.m2pool.lease.entity.LeaseOrderItem; +import com.m2pool.lease.entity.LeasePaymentRecord; +import com.m2pool.lease.mq.message.RabbitmqPayAutoReturnInfoMessage; +import org.apache.ibatis.annotations.MapKey; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.transaction.Transaction; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + *

+ * 订单明细表 Mapper 接口 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Mapper +public interface LeaseOrderItemMapper extends BaseMapper { + + /** + * 获取订单状态为支付中的订单机器id + * + * @param list + * @return + */ + List getMachineByOrderStatusIsPayInData(@Param("list") List list); + + /** + *通过订单详情id找到订单id + * @param orderItemIds + * @return + */ + List getOrderIds(@Param("orderItemIds") List orderItemIds); + + + + List getOrderInfoStatus(@Param("orderIds") List orderIds); + + + + /** + * 通过订单详情id找到订单id + * @param itemIds + * @return + */ + List selectOrderInfoIds(@Param("itemIds") Set itemIds); + + + /** + * 获取需要更新已实际支付总金额的订单详情 + * @param address + * @param transaction + * @return + */ + List getNeedUpdateOrderItem(@Param("orderId") Long orderId); + + + + /** + * 通过订单id找到订单详情 + * @param orderIds + * @return + */ + List getOrderItemByOrderIds(@Param("orderIds") Set orderIds); + + + /** + * 获取订单详情id + * @return + */ + List getOneDayApartOrder(); + + +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeaseOrderMiningMapper.java b/src/main/java/com/m2pool/lease/mapper/LeaseOrderMiningMapper.java new file mode 100644 index 0000000..a2731dc --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeaseOrderMiningMapper.java @@ -0,0 +1,98 @@ +package com.m2pool.lease.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.m2pool.lease.annotation.MiningDB; +import com.m2pool.lease.dto.v2.*; +import com.m2pool.lease.entity.LeaseOrderMining; +import com.m2pool.lease.netty.message.ClientConfigurationMining; +import org.apache.ibatis.annotations.MapKey; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + *

+ * 挖矿中订单 Mapper 接口 + *

+ * + * @author yyb + * @since 2025-12-02 + */ +@Mapper +public interface LeaseOrderMiningMapper extends BaseMapper { + /** + * 批量插入 + * + * @param orderMiningList + * @return + */ + int insertBatch(@Param("list") List orderMiningList); + + + /** + * 获取矿池正在挖矿中的信息 + * + * @param clientConfigurationMining + * @return + */ + int updateOrderMining(@Param("userEmail") String userEmail,@Param("clientConfigurationMining") ClientConfigurationMining clientConfigurationMining); + + /** + * 获取用户购买的机器列表 + * @param authId + * @return + */ + List getPurchasedItems(@Param("authId") Long authId); + + /** + * 根据id获取用户购买的机器信息 + * @param id + * @return + */ + MiningConfigInfoDto getPurchasedInfo(@Param("id") Long id); + + + /** + * 获取最近一次的算力 + * @param list + * @return + */ + @MiningDB + @MapKey("miner") + Map getRecentlyHashrate(@Param("pool") String pool,@Param("wallet") String wallet,@Param("coin") String coin,@Param("list") List list); + + + /** + * 获取最近24小时的算力 + * @return + */ + @MiningDB + List getRecently24HourHashrate(@Param("list") List list,@Param("tableName") String tableName,@Param("now") Date now); + + + /** + * 根据orderId集合获取挖矿中的信息 + * @param orderIds + * @return + */ + List getMiningInfoByOrderId(@Param("orderIds") List orderIds); + + + /** + * 检查是否存在挖矿中矿工 + * @param list + * @return + */ + List checkMiningMinersExist(@Param("list") List list); + + + /** + * 获取用户购买的矿机信息 + * @param info + * @return + */ + List getPurchasedMachineInfo(@Param("info") MiningConfigInfoDto info); +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeasePayRechargeMessageMapper.java b/src/main/java/com/m2pool/lease/mapper/LeasePayRechargeMessageMapper.java new file mode 100644 index 0000000..aab431b --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeasePayRechargeMessageMapper.java @@ -0,0 +1,76 @@ +package com.m2pool.lease.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.m2pool.lease.dto.*; +import com.m2pool.lease.entity.LeasePayRechargeMessage; +import com.m2pool.lease.entity.LeaseUserWalletData; +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; + +/** + *

+ * 充值业务mapper层 + *

+ * + * @author yyb + * @since 2025-09-10 + */ +@Mapper +public interface LeasePayRechargeMessageMapper extends BaseMapper { + + /** + * 获取充值记录列表 + * + * @param walletList 钱包集合 + * @param status + * @return + */ + @Deprecated + List balanceRechargeList(@Param("walletList") List walletList, @Param("status") Integer status); + + /** + * 获取近半年是否有充值记录 + * + * @param fromAddress + * @return + */ + boolean checkRechargeOperator(@Param("fromAddress") String fromAddress); + + /** + * 批量获取近半年是否有充值记录 + * + * @param fromAddressList + * @return + */ + @MapKey("fromAddress") + Map checkRechargeOperatorBatch(@Param("list") List fromAddressList); + + /** + * 交易流水---充值 + * + * @param walletList + * @return + */ + List transactionRecord(@Param("walletList") List walletList); + + + /** + * 最近交易 + * + * @param walletList + * @return + */ + List getRecentlyTransaction(@Param("walletList") List walletList); + + /** + * 根据唯一索引修改或删除 + * + * @param leasePayRechargeMessage + * @return + */ + int saveOrUpdateByIndex(@Param("leasePayRechargeMessage") LeasePayRechargeMessage leasePayRechargeMessage); +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeasePayRecordMessageInfoMapper.java b/src/main/java/com/m2pool/lease/mapper/LeasePayRecordMessageInfoMapper.java new file mode 100644 index 0000000..b1bf8e0 --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeasePayRecordMessageInfoMapper.java @@ -0,0 +1,30 @@ +package com.m2pool.lease.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.m2pool.lease.dto.CheckAddressDto; +import com.m2pool.lease.dto.PayRecordMessageDto; +import com.m2pool.lease.dto.RecentlyTransactionDto; +import com.m2pool.lease.dto.TransactionRecordDto; +import com.m2pool.lease.entity.LeasePayRecordMessage; +import com.m2pool.lease.entity.LeasePayRecordMessageInfo; +import com.m2pool.lease.entity.LeaseShopConfig; +import com.m2pool.lease.entity.LeaseUserWalletData; +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; + +/** + *

+ * LeasePayRecordMessageMapper 接口 + *

+ * + * @author yyb + * @since 2025-09-10 + */ +@Mapper +public interface LeasePayRecordMessageInfoMapper extends BaseMapper { + +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeasePayRecordMessageMapper.java b/src/main/java/com/m2pool/lease/mapper/LeasePayRecordMessageMapper.java new file mode 100644 index 0000000..841c16a --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeasePayRecordMessageMapper.java @@ -0,0 +1,82 @@ +package com.m2pool.lease.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.m2pool.lease.dto.*; +import com.m2pool.lease.entity.LeasePayRecordMessage; +import com.m2pool.lease.entity.LeaseShopConfig; +import com.m2pool.lease.entity.LeaseUserWalletData; +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; +import java.util.Set; + +/** + *

+ * LeasePayRecordMessageMapper 接口 + *

+ * + * @author yyb + * @since 2025-09-10 + */ +@Mapper +public interface LeasePayRecordMessageMapper extends BaseMapper { + /** + * 检查用户半年内是否有支付操作 + * @param fromAddress + * @return + */ + boolean checkPayOperator(@Param("fromAddress") String fromAddress); + + + /** + * 检查用户批量是否有支付操作 + * @param fromAddressList + * @return + */ + @MapKey("fromAddress") + Map checkPayOperatorBatch(@Param("list") List fromAddressList); + + /** + * 批量查询用户余额支付列表 + * @param walletList + * @param status + * @return + */ + + List balancePayList(@Param("walletList") List walletList, @Param("status") Integer status); + + /** + * 交易流水---支付 + * @param walletList + * @return + */ + List transactionRecord(@Param("walletList") List walletList); + + + /** + * 最近交易 + * @param walletList + * @return + */ + List getRecentlyTransaction(@Param("walletList") List walletList); + + + /** + * 保存或者更新根据txHash + * @param leasePayRecordMessage + * @return + */ + int updateByTxHash(@Param("leasePayRecordMessage") LeasePayRecordMessage leasePayRecordMessage,@Param("txHash") String txHash); + + /** + * 根据订单id获取队列id + * @param infoIds + * @return + */ + @MapKey("orderId") + Map getQueueIdByInfoIds(@Param("infoIds") List infoIds); + +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeasePayWithdrawMessageMapper.java b/src/main/java/com/m2pool/lease/mapper/LeasePayWithdrawMessageMapper.java new file mode 100644 index 0000000..ff39f0b --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeasePayWithdrawMessageMapper.java @@ -0,0 +1,87 @@ +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; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 提现mapper层 + *

+ * + * @author yyb + * @since 2025-09-10 + */ +@Mapper +public interface LeasePayWithdrawMessageMapper extends BaseMapper { + + /** + * 获取提现记录列表 + * + * @param walletList 钱包集合 + * @param status + * @return + */ + @Deprecated + List balanceWithdrawList(@Param("walletList") List walletList, + @Param("status") Integer status); + + + /** + * 检查用户半年内是否有提现操作 + * @param fromAddress + * @return + */ + boolean checkWithdrawOperator(@Param("fromAddress") String fromAddress); + + + /** + * 检查用户半年内是否有提现操作 + * @param fromAddressList + * @return + */ + @MapKey("fromAddress") + Map checkWithdrawOperatorBatch(@Param("list") List fromAddressList); + + /** + * 交易流水---提现 + * + * @param walletList + * @return + */ + List transactionRecord(@Param("walletList") List walletList); + + /** + * 最近交易 + * + * @param walletList + * @return + */ + List getRecentlyTransaction(@Param("walletList") List walletList); + + + /** + * 获取最近一条数据的提现状态 + * + * @param fromAddress + * @return + */ + Integer getRecentlyOneData(@Param("fromAddress") String fromAddress,@Param("chain")String chain); + + + /** + * 获取商户提现记录 + * + * @param shopId + * @return + */ + List getSellerWithdrawRecord(@Param("shopId") Long shopId); +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeasePaymentRecordMapper.java b/src/main/java/com/m2pool/lease/mapper/LeasePaymentRecordMapper.java new file mode 100644 index 0000000..b09368e --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeasePaymentRecordMapper.java @@ -0,0 +1,48 @@ +package com.m2pool.lease.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.m2pool.lease.mq.message.RabbitmqOrderMessage; +import com.m2pool.lease.entity.LeasePaymentRecord; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + *

+ * 支付记录表 Mapper 接口 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Mapper +public interface LeasePaymentRecordMapper extends BaseMapper { + + /** + * 检查订单支付状态 + * + * @param message + * @return + */ + List checkPaymentRecordStatus(@Param("list") List message); + + /** + * 批量更新订单支付状态 + * + * @param list + * @return + */ + int updateBatchStatusForOderIdAndShopId(@Param("list") List list); + + + /** + * 查询最近15分钟处于支付中的订单 + * @return + */ + List getIsPayInData(); + + + + +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeaseProductIncomeMapper.java b/src/main/java/com/m2pool/lease/mapper/LeaseProductIncomeMapper.java new file mode 100644 index 0000000..a0154c1 --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeaseProductIncomeMapper.java @@ -0,0 +1,18 @@ +package com.m2pool.lease.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.m2pool.lease.entity.LeaseProductIncome; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 商品收益表 Mapper 接口 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Mapper +public interface LeaseProductIncomeMapper extends BaseMapper { + +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeaseProductMachineMapper.java b/src/main/java/com/m2pool/lease/mapper/LeaseProductMachineMapper.java new file mode 100644 index 0000000..c2c7140 --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeaseProductMachineMapper.java @@ -0,0 +1,155 @@ +package com.m2pool.lease.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.m2pool.lease.annotation.Pool2DB; +import com.m2pool.lease.dto.*; +import com.m2pool.lease.entity.LeaseProductMachine; +import com.m2pool.lease.vo.ProductMachineVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + *

+ * 商品表对应的物品机器表 Mapper 接口 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Mapper +public interface LeaseProductMachineMapper extends BaseMapper { + + + /** + * 获取当前账户对应的挖矿账户 + * + * @param userId 账户ID + * @return 挖矿账户集合 + */ + @Pool2DB + List getUserMinersList(@Param("userId") String userId, @Param("coin") String coin); + + /** + * 根据机器ID集合获取机器信息 + * + * @param machineIds 机器ID集合 + * @return 机器信息集合 + */ + List getMachinesByIds(@Param("machineIds") List machineIds); + + /** + * 创建订单业务----修改乐观锁(sale_state 改为 2 售出中) + * @param machineIds + * @return + */ + int updateLockState(@Param("machineIds") List machineIds); + + /** + * 获取当前账户对应的挖矿账户的机器矿工集合 (1天内有过算力的才能上架) + * + * @param user 账户ID + * @param coin 币种 + * @return 挖矿账户的机器矿工集合 + */ + @Pool2DB + List getMinerMachines(@Param("user") String user, @Param("coin") String coin); + + /** + * 获取当前账户对应的挖矿账户的已经上架正在售出或已售出的机器矿工 + * + * @param user 账户ID + * @param coin 币种 + * @return 挖矿账户的机器矿工集合 + */ + List getMinerMachinesIsSale(@Param("user") String user, @Param("coin") String coin); + + /** + * 获取商品价格范围 + * + * @param productId 商品ID + * @return 商品价格范围 + */ + PriceDto getPriceRange(@Param("productId") Long productId); + + + @Pool2DB + List getRecentlyFiveMinutesData(@Param("leaseProductMachines") List leaseProductMachines, @Param("coin") String coin); + + + + int saveOrUpdateBatchs(@Param("leaseProductMachines") List leaseProductMachines); + + + /** + * 检测当前商铺是否有已售出机器 + * + * @param shopId 商家ID + * @return 检测结果 + */ + int checkHasSaleMachineByShopId(@Param("shopId") Long shopId); + + /** + * 检测当前商品是否存在已售出机器 + * + * @param productId 商品ID + * @return 检测结果 + */ + int checkHasSaleMachineByProductId(@Param("productId") Long productId); + + + + /** + * 获取机器售价列表 + * + * @param machineIds 机器ID集合 + * @return 机器价格列表 + */ + List getPriceList(@Param("list")List machineIds); + + + /** + * 查询绑定新钱包后,已存在的商品矿机 + * + * @param shopId 店铺id + * @return 商品列表 + */ + List getProductListForShopWalletConfig(@Param("shopId")Long shopId); + + + /** + * 获取商品对应的机器ID集合 + * + * @param productId 商品ID + * @return 机器ID集合 + */ + List getIdsForProductId(@Param("productId")Long productId); + + + /** + * 获取店铺对应的机器ID集合 + * + * @param productId 商品ID + * @return 机器ID集合 + */ + List getIdsForShopId(@Param("shopId")Long shopId); + + /** + * 根据价格范围,功耗范围,算力范围 获取商品对应的机器 + * + * @param productMachineVo 商品参数 + * @return 机器列表 + */ + List getMachinesByPriceAndPowerAndDissipation(@Param("productMachineVo") ProductMachineVo productMachineVo,@Param("coin") String coin); + + + /** + * 批量插入机器算力数据 + * + * @param list 机器算力数据 + * @return 插入结果 + */ + int batchInsertPowers(@Param("coin")String coin,@Param("list")List list); + +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeaseProductMachinePriceMapper.java b/src/main/java/com/m2pool/lease/mapper/LeaseProductMachinePriceMapper.java new file mode 100644 index 0000000..3e7cd34 --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeaseProductMachinePriceMapper.java @@ -0,0 +1,44 @@ +package com.m2pool.lease.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.m2pool.lease.dto.MachinePayTypeDto; +import com.m2pool.lease.entity.LeaseOrderItem; +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; + +/** + *

+ * 商品表对应的物品机器表 Mapper 接口 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Mapper +public interface LeaseProductMachinePriceMapper extends BaseMapper { + + /** + * 获取订单总金额 按照chain 和 分组 coin + * @param orderInfoVoList + * @return + */ + @MapKey("productMachineId") + Map getOrderTotalPriceGroupByChainAndCoin(@Param("list") List orderInfoVoList); + + + /** + * 获取机器价格通过id + * @param machineIds + * @return + */ + List getMachinePriceByMachineIds(@Param("list") List machineIds); + + @MapKey("productMachineId") + Map getPriceByOrderItems(@Param("list") List orderInfoVoList); +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeaseProductMapper.java b/src/main/java/com/m2pool/lease/mapper/LeaseProductMapper.java new file mode 100644 index 0000000..290f112 --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeaseProductMapper.java @@ -0,0 +1,76 @@ +package com.m2pool.lease.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.m2pool.lease.annotation.Pool2DB; +import com.m2pool.lease.dto.PayTypeDto; +import com.m2pool.lease.dto.ProductDto; +import com.m2pool.lease.dto.ProductForWalletConfigDto; +import com.m2pool.lease.entity.LeaseProduct; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.math.BigDecimal; +import java.util.List; + +/** + *

+ * 商品表 Mapper 接口 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Mapper +public interface LeaseProductMapper extends BaseMapper { + + /** + * 获取近三天币种对应的全网算力 + * @param coin + * @return + */ + @Pool2DB + BigDecimal getCoinNetPower(@Param("coin") String coin); + + + /** + * 获取近三天的全网实际报块数 + * @param coin + * @return + */ + @Pool2DB + BigDecimal getNetBlocks(@Param("coin") String coin); + + + /** + * 获取商品列表(弃用) + * @param coin + * @param algorithm + * @return + */ + List getProductList(@Param("coin") String coin , @Param("algorithm") String algorithm); + + + /** + * 用户个人中心 和 首页商城 商品列表获取 + * @param coin 用于商城首页 + * @param algorithm 用于商城首页 + * @param shopId 用于个人中心商品列表 + * @return + */ + List getProductListForShopAndUserCenter(@Param("coin") String coin , @Param("algorithm") String algorithm,@Param("shopId") Long shopId); + + /** + * 获取支持的支付方式 + * @param shopId + * @return + */ + List getSupportPayType(@Param("shopId")Long shopId); + + + /** + * 获取商品列表(用于钱包配置) + * @param shopId + * @return + */ + List getProductListForShopWalletConfig(@Param("shopId")Long shopId); +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeaseShopAddressConfigMapper.java b/src/main/java/com/m2pool/lease/mapper/LeaseShopAddressConfigMapper.java new file mode 100644 index 0000000..4bfc126 --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeaseShopAddressConfigMapper.java @@ -0,0 +1,10 @@ +package com.m2pool.lease.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.m2pool.lease.entity.LeaseShopAddressConfig; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface LeaseShopAddressConfigMapper extends BaseMapper { + +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeaseShopConfigMapper.java b/src/main/java/com/m2pool/lease/mapper/LeaseShopConfigMapper.java new file mode 100644 index 0000000..b5a6a62 --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeaseShopConfigMapper.java @@ -0,0 +1,74 @@ +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.LeasePayRecordMessage; +import com.m2pool.lease.entity.LeaseShopConfig; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + *

+ * 店铺商品配置表 Mapper 接口 + *

+ * + * @author yyb + * @since 2025-08-05 + */ +public interface LeaseShopConfigMapper extends BaseMapper { + /** + * 获取链和币种 + * @return + */ + List getChainAndList(@Param("chain") String chain); + + List getChainAndListForSeller(@Param("shopId") Long shopId); + + /** + * 更具链名称和币种名称获取币种图标 + * @param chain + * @param coinList + * @return + */ + List getCoinIconByChainAndCoin(@Param("chain") String chain, @Param("coinList") List coinList); + + /** + * 根据店铺id和链名称和币种获取店铺配置(包含已删除的配置) + * @param list + * @return + */ + List selectShopConfigByShopIdAndSymbolAndChain(@Param("list") List list); + + /** + * 获取店铺支持的支付方式 + * @param shopIds + * @return + */ + List getPayType(@Param("list") List shopIds); + + /** + * 获取店铺钱包信息 + * @param shopId + * @return + */ + List 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); + + + + /** + * 根据支付记录修改余额 + * @param reocrdList 钱包信息 + * @return 更新数量 + */ + int updateBalance(@Param("list") List reocrdList); +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeaseShopMapper.java b/src/main/java/com/m2pool/lease/mapper/LeaseShopMapper.java new file mode 100644 index 0000000..cc129c6 --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeaseShopMapper.java @@ -0,0 +1,65 @@ +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.LeaseShop; +import com.m2pool.lease.entity.LeaseShopConfig; +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; +import java.util.Set; + +/** + *

+ * 店铺表 Mapper 接口 + *

+ * + * @author yyb + * @since 2025-08-05 + */ +@Mapper +public interface LeaseShopMapper extends BaseMapper { + + /** + * 获取买家自定义的支付地址和支付币种和链 + * @param chainAndCoinSet + * @return + */ + List getPayAddressAndPayCoin(@Param("chainAndCoinSet") Set chainAndCoinSet); + + + /** + * 获取单个店铺钱包信息 + * @param shopId + * @return + */ + List getShopWalletInfo(@Param("shopId") Long shopId); + + + /** + * 获取多个店铺钱包信息 + * @param shopIds + * @return + */ + List getShopWalletInfoList(@Param("shopIds") List shopIds); + + + /** + * 根据商品id集合获取店铺id集合 + * @param machineIds + * @return + */ + List getShopIdsByMachineIds(@Param("machineIds") List machineIds); + + /** + * 根据店铺id集合获取店铺名称 + * @param shopIds + * @return + */ + @MapKey("id") + Map getShopNameMapByIds(@Param("shopIds") List shopIds); +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeaseShoppingCartInfoMapper.java b/src/main/java/com/m2pool/lease/mapper/LeaseShoppingCartInfoMapper.java new file mode 100644 index 0000000..604c4ba --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeaseShoppingCartInfoMapper.java @@ -0,0 +1,37 @@ +package com.m2pool.lease.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.m2pool.lease.dto.ShoppingCartInfoDto; +import com.m2pool.lease.entity.LeaseShoppingCartInfo; +import com.m2pool.lease.vo.ProductAndMachineVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + *

+ * 购物车详情表 Mapper 接口 + *

+ * + * @author yyb + * @since 2025-07-24 + */ +@Mapper +public interface LeaseShoppingCartInfoMapper extends BaseMapper { + + /** + * 查询购物车详情列表 + * @return + */ + List getProductAndMachineIds(@Param("userId") String userId, @Param("version") Integer version); + + + + /** + * 批量删除购物车详情数据 + * @param ids + * @return + */ + int deleteBatchData(@Param("ids") List ids); +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeaseShoppingCartMapper.java b/src/main/java/com/m2pool/lease/mapper/LeaseShoppingCartMapper.java new file mode 100644 index 0000000..ea3518e --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeaseShoppingCartMapper.java @@ -0,0 +1,18 @@ +package com.m2pool.lease.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.m2pool.lease.entity.LeaseShoppingCart; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 购物车表 Mapper 接口 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Mapper +public interface LeaseShoppingCartMapper extends BaseMapper { + +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeaseUserMapper.java b/src/main/java/com/m2pool/lease/mapper/LeaseUserMapper.java new file mode 100644 index 0000000..6250063 --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeaseUserMapper.java @@ -0,0 +1,71 @@ +package com.m2pool.lease.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.m2pool.lease.entity.GoogleInfo; +import com.m2pool.lease.entity.LeaseUser; +import com.m2pool.lease.vo.v2.UserGoogleAuthInfo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + *

+ * 用户表 Mapper 接口 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Mapper +public interface LeaseUserMapper extends BaseMapper { + /** + * 获取谷歌验证相关信息 + * @param email + * @return + */ + GoogleInfo getGoogleInfoByEmail(@Param("email") String email); + + /** + * 验证码是否已存在 + * @param secret + * @return + */ + boolean checkSecretIfExist(@Param("secret") String secret); + + /** + * 获取用户谷歌验证码相关信息(包括密码) + * @param email + * @return + */ + UserGoogleAuthInfo getUserInfoByEmail(@Param("email") String email); + + + /** + * 绑定谷歌验证码 + * @param info + * @return + */ + boolean bingGoogleAuthenticator(@Param("info") UserGoogleAuthInfo info); + + + /** + * 关闭谷歌验证码 + * @param info + * @return + */ + boolean closeGoogleAuthenticator(@Param("info") UserGoogleAuthInfo info); + + + /** + * 验证用户是否已绑定谷歌验证码 + * @param email + * @return + */ + Integer checkIfBindByEmail(@Param("email") String email); + + /** + * 开启谷歌验证码 + * @param info + * @return + */ + boolean openGoogleAuthenticator(@Param("info") UserGoogleAuthInfo info); +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeaseUserOwnedProductMapper.java b/src/main/java/com/m2pool/lease/mapper/LeaseUserOwnedProductMapper.java new file mode 100644 index 0000000..305aa73 --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeaseUserOwnedProductMapper.java @@ -0,0 +1,61 @@ +package com.m2pool.lease.mapper; + +import cn.hutool.core.date.DateTime; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.m2pool.lease.annotation.Pool2DB; +import com.m2pool.lease.dto.HourIncomeDto; +import com.m2pool.lease.dto.UserMinerDto; +import com.m2pool.lease.dto.UserMinerPowerDto; +import com.m2pool.lease.entity.LeaseUserOwnedProduct; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.time.LocalDateTime; +import java.util.List; + +/** + *

+ * 用户拥有商品详情表 Mapper 接口 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Mapper +public interface LeaseUserOwnedProductMapper extends BaseMapper { + /** + * 获取已生效商品机器id集合 + * @param beginOfDay + * @return + */ + List getProductMachineIds(@Param("beginOfDay") DateTime beginOfDay); + + + /** + * 获取小时收益列表 + * @param coin + * @param start + * @param end + * @return + */ + @Pool2DB + List getHourIncomeList(@Param("list") List list,@Param("coin") String coin, @Param("start") LocalDateTime start, @Param("end") LocalDateTime end); + + + /** + * 批量更新收益 + * @param list + * @return + */ + int updateBatchIncome(@Param("list") List list); + + + /** + * 获取矿机实时算力(30m) + * @param list + * @param coin + * @return + */ + @Pool2DB + List getUserMinerPower(@Param("list")List list,@Param("coin") String coin); +} diff --git a/src/main/java/com/m2pool/lease/mapper/LeaseUserWalletDataMapper.java b/src/main/java/com/m2pool/lease/mapper/LeaseUserWalletDataMapper.java new file mode 100644 index 0000000..84940f6 --- /dev/null +++ b/src/main/java/com/m2pool/lease/mapper/LeaseUserWalletDataMapper.java @@ -0,0 +1,61 @@ +package com.m2pool.lease.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.m2pool.lease.dto.ChainAndCoinDto; +import com.m2pool.lease.dto.UserWalletDataDto; +import com.m2pool.lease.entity.LeasePayRecordMessage; +import com.m2pool.lease.entity.LeaseUserWalletData; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Set; + +@Mapper +public interface LeaseUserWalletDataMapper extends BaseMapper { + + /** + * 根据用户 ID 查询钱包信息 + * @param authId 用户 ID + * @return 钱包信息 + */ + List selectByUserId(@Param("authId") Long authId); + + /** + * 根据钱包地址查询钱包信息 + * @param address 钱包地址 + * @return 钱包信息 + */ + UserWalletDataDto getWalletInfoByAddress(@Param("address") String address,@Param("symbol") String symbol,@Param("userId") String userId); + + /** + * 根据链查询钱包信息 + * @param chain 链 + * @return 钱包信息 + */ + List getWalletInfoByChain(@Param("chain") String chain,@Param("authId") Long authId); + + + /** + * 获取余额为 不为0 的钱包地址 + * @param chain 链 + * @return 钱包地址 + */ + List getWalletForBalanceIsZero(@Param("list") List addressList); + + /** + * 根据链和币种查询钱包信息 + * @param chainAndCoinSet 链和币种 + * @return 钱包信息 + */ + List selectWalletByChainAndCoinAndUsername(@Param("list") Set chainAndCoinSet,@Param("authId") Long authId); + + + /** + * 根据支付记录修改余额及冻结余额 + * @param reocrdList 钱包信息 + * @return 更新数量 + */ + int updateBalanceAndBlockBalance(@Param("list") List reocrdList); +} diff --git a/src/main/java/com/m2pool/lease/mq/MessageReceiver.java b/src/main/java/com/m2pool/lease/mq/MessageReceiver.java new file mode 100644 index 0000000..76592c2 --- /dev/null +++ b/src/main/java/com/m2pool/lease/mq/MessageReceiver.java @@ -0,0 +1,620 @@ +package com.m2pool.lease.mq; + +import cn.hutool.json.JSONUtil; +import com.alibaba.cloud.commons.lang.StringUtils; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.m2pool.lease.annotation.LedgerLog; +import com.m2pool.lease.constant.RabbitmqConstant; +import com.m2pool.lease.entity.*; +import com.m2pool.lease.exception.PayRechargeException; +import com.m2pool.lease.mapper.*; +import com.m2pool.lease.mq.message.RabbitmqDeleteWalletReturnMessage; +import com.m2pool.lease.mq.message.RabbitmqPayAutoReturnMessage; +import com.m2pool.lease.mq.message.RabbitmqPayRechargeReturnMessage; +import com.m2pool.lease.mq.message.RabbitmqPayWithdrawReturnMessage; +import com.m2pool.lease.service.LeaseOrderItemService; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.messaging.handler.annotation.Payload; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; + +@Service +public class MessageReceiver { + + @Resource + private LeasePayWithdrawMessageMapper leasePayWithdrawMessageMapper; + @Resource + private LeasePayRechargeMessageMapper leasePayRechargeMessageMapper; + @Resource + private LeaseUserWalletDataMapper leaseUserWalletDataMapper; + @Resource + private LeasePayRecordMessageMapper leasePayRecordMessageMapper; + @Resource + private LeaseAutoAddressMapper leaseAutoAddressMapper; + @Resource + private LeaseOrderItemMapper leaseOrderItemMapper; + @Resource + private LeaseOrderItemService leaseOrderItemService; + @Resource + private LeaseShopConfigMapper leaseShopConfigMapper; + + @Resource + private RabbitTemplate rabbitTemplate; + ///** + // * 订单超时 消费者 + // */ + //@RabbitListener(queues = RabbitmqConstant.DEAD_LETTER_QUEUE_NAME) + //@Transactional(rollbackFor = Exception.class) + //public void orderOvertimeMessage(@Payload List message /**,Message amqpMessage */) { + // List leasePaymentRecords = leasePaymentRecordMapper.checkPaymentRecordStatus(message); + // List collect = leasePaymentRecords.stream() + // .filter(record -> record.getStatus() == 6) + // .peek(record-> record.setStatus(5)) + // .collect(Collectors.toList()); + // System.out.println("订单超时消费者"+collect); + // if (!collect.isEmpty()){ + // //1. 修改支付订单状态为 6支付中 状态改为 5 订单已超时 + // boolean b = leasePaymentRecordService.updateBatchById(collect); + // //2. 修改普通订单状态 6支付中 状态改为 5 订单已超时(普通订单和支付订单是同时创建的,所以用的同一个消息) + // Long orderId = collect.get(0).getOrderId(); + // int i = leaseOrderInfoMapper.updateById(LeaseOrderInfo.builder().id(orderId).status(5).build()); + // //3.修改该订单下状态为售出中的机器为可售出状态 + // List orderStatusIsPayInData = leaseOrderItemMapper.getMachineByOrderStatusIsPayInData(leasePaymentRecords); + // //这里只更新机器售出状态为2 售出中的机器 + // int update = leaseProductMachineMapper.update(LeaseProductMachine.builder().saleState(0).build(), + // new LambdaQueryWrapper() + // .eq(LeaseProductMachine::getSaleState, 2).in(LeaseProductMachine::getId, orderStatusIsPayInData)); + // + // } + // //try { + // // // 手动确认消息 + // // channel.basicAck(amqpMessage.getMessageProperties().getDeliveryTag(), false); + // //} catch (Exception e) { + // // try { + // // // 拒绝消息,不重新入队 + // // channel.basicReject(amqpMessage.getMessageProperties().getDeliveryTag(), false); + // // } catch (IOException ioException) {} + // //} + //} + + + /** + * 支付 消费者 + * @param payAutoReturnMessage + */ + @RabbitListener(queues = RabbitmqConstant.PAY_AUTO_RETURN_QUEUE,containerFactory ="rabbitListenerContainerFactory") + @Transactional(rollbackFor = Exception.class) + public void listenerPayBalanceStatusQueueMessage(@Payload RabbitmqPayAutoReturnMessage payAutoReturnMessage) { + System.out.println("支付消费者"+JSONUtil.toJsonPrettyStr(payAutoReturnMessage)); + //查找到数据库对应的支付记录 现在已经每日记录和总记录拆分开了,实际查出来的只会有一条记录 + List leasePayRecordMessagesList = leasePayRecordMessageMapper.selectList(new LambdaQueryWrapper() + .eq(LeasePayRecordMessage::getQueueId, payAutoReturnMessage.getQueue_id())); + if (leasePayRecordMessagesList.isEmpty()){ + return ; + } + //买家钱包 + LeasePayRecordMessage payRecordMessage = leasePayRecordMessagesList.get(0); + + LeaseUserWalletData buyer = leaseUserWalletDataMapper.selectOne(new LambdaQueryWrapper() + .eq(LeaseUserWalletData::getFromAddress,payRecordMessage.getFromAddress()) + .eq(LeaseUserWalletData::getFromChain, payRecordMessage.getFromChain()) + .eq(LeaseUserWalletData::getFromSymbol, payRecordMessage.getFromSymbol()) + .eq(LeaseUserWalletData::getDel, false) + ); + //获取初始余额和冻结余额 + BigDecimal initBalance = buyer.getBalance(); + BigDecimal initBlockBalance = buyer.getBlockedBalance(); + BigDecimal balance = buyer.getBalance().subtract(payAutoReturnMessage.getAmount()); + + //支付成功修改order_item表实际支付金额 + for (LeasePayRecordMessage leasePayRecordMessage : leasePayRecordMessagesList) { + BigDecimal blockBalance = buyer.getBlockedBalance().subtract(leasePayRecordMessage.getBlockAmount()); + leasePayRecordMessage.setUpdateTime(LocalDateTime.now()); + leasePayRecordMessage.setBlockHeight(payAutoReturnMessage.getBlock_height()); + leasePayRecordMessage.setRealAmount(payAutoReturnMessage.getAmount()); + //当天已存在支付的信息 + leasePayRecordMessage.setStatus(payAutoReturnMessage.getStatus()); + leasePayRecordMessage.setTxHash(payAutoReturnMessage.getTx_hash()); + List needUpdateOrderItem = leaseOrderItemMapper.getNeedUpdateOrderItem(Long.valueOf(leasePayRecordMessage.getOrderId())); + if(payAutoReturnMessage.getStatus() == 1){ + //支付成功 买家 钱包总余额 + 冻结余额 减少 卖家 钱包总余额增加 + buyer.setBalance(balance); + buyer.setBlockedBalance(blockBalance); + needUpdateOrderItem = needUpdateOrderItem.stream().peek(item ->{ + item.setAlreadyPayRealAmount(item.getAlreadyPayRealAmount().add(item.getSettlePayRealAmount())); + item.setSettlePayRealAmount(BigDecimal.ZERO); + }).collect(Collectors.toList()); + } + if (payAutoReturnMessage.getStatus() == 0){ + buyer.setBlockedBalance(blockBalance); + needUpdateOrderItem = needUpdateOrderItem.stream().peek(item ->{ + item.setSettlePayRealAmount(BigDecimal.ZERO); + }).collect(Collectors.toList()); + } + //修改支付记录状态 + int i = leasePayRecordMessageMapper.updateById(leasePayRecordMessage); + //支付成功修改order_item表实际支付金额 + boolean b = leaseOrderItemService.updateBatchById(needUpdateOrderItem); + if (i < 1 || !b){ + throw new PayRechargeException("支付失败,该支付记录已修改,消息重试!!!"); + } + } + //使用乐观锁防止余额少减少 + int update = leaseUserWalletDataMapper.update(buyer, new LambdaQueryWrapper() + .eq(LeaseUserWalletData::getId, buyer.getId()) + .eq(LeaseUserWalletData::getBalance, initBalance) + .eq(LeaseUserWalletData::getBlockedBalance, initBlockBalance) + ); + if (update < 1){ + throw new PayRechargeException("支付失败,余额金额错误,消息重试!!!"); + } + } + + //@RabbitListener(queues = RabbitmqConstant.PAY_AUTO_RETURN_QUEUE,containerFactory ="rabbitListenerContainerFactory") + //@Transactional(rollbackFor = Exception.class) + //public void listenerPayBalanceStatusQueueMessage(@Payload RabbitmqPayAutoReturnMessage payAutoReturnMessage) { + // System.out.println("支付消费者"+JSONUtil.toJsonPrettyStr(payAutoReturnMessage)); + // //查找到数据库对应的支付记录 + // List leasePayRecordMessages = leasePayRecordMessageMapper.selectList(new LambdaQueryWrapper() + // .eq(LeasePayRecordMessage::getQueueId, payAutoReturnMessage.getQueue_id())); + // Map> recordMap = leasePayRecordMessages.stream() + // .collect(Collectors.groupingBy(item -> item.getToAddress() + "-" + item.getToChain())); + // //买家钱包 + // Map transactions = payAutoReturnMessage.getTransactions(); + // LeasePayRecordMessage leasePayRecordMessage1 = leasePayRecordMessages.get(0); + // LeaseUserWalletData buyer = leaseUserWalletDataMapper.selectOne(new LambdaQueryWrapper() + // .eq(LeaseUserWalletData::getFromAddress,leasePayRecordMessage1.getFromAddress()) + // .eq(LeaseUserWalletData::getFromChain, leasePayRecordMessage1.getFromChain()) + // .eq(LeaseUserWalletData::getFromSymbol, leasePayRecordMessage1.getFromSymbol()) + // .eq(LeaseUserWalletData::getDel, false) + // ); + // //获取初始余额和冻结余额 + // BigDecimal initBalance = buyer.getBalance(); + // BigDecimal initBlockBalance = buyer.getBlockedBalance(); + // //支付成功修改order_item表实际支付金额 + // transactions.forEach((key,transaction) -> { + // String key1 = transaction.getTo_address() + "-" + payAutoReturnMessage.getChain(); + // List leasePayRecordMessageList = recordMap.get(key1); + // if (leasePayRecordMessageList != null){ + // for (LeasePayRecordMessage leasePayRecordMessage : leasePayRecordMessageList) { + // BigDecimal balance = buyer.getBalance().subtract(transaction.getAmount()); + // BigDecimal blockBalance = buyer.getBlockedBalance().subtract(leasePayRecordMessage.getBlockAmount()) + // .subtract(leasePayRecordMessage.getNeedAmount()); + // + // leasePayRecordMessage.setUpdateTime(LocalDateTime.now()); + // leasePayRecordMessage.setBlockHeight(transaction.getBlock_height()); + // leasePayRecordMessage.setRealAmount(transaction.getAmount()); + // //当天已存在支付的信息 + // leasePayRecordMessage.setStatus(transaction.getStatus()); + // leasePayRecordMessage.setTxHash(transaction.getTx_hash()); + // List needUpdateOrderItem = leaseOrderItemMapper.getNeedUpdateOrderItem(Long.valueOf(leasePayRecordMessage.getOrderId())); + // if(transaction.getStatus() == 1){ + // //支付成功 买家 钱包总余额 + 冻结余额 减少 卖家 钱包总余额增加 + // buyer.setBalance(balance); + // buyer.setBlockedBalance(blockBalance); + // needUpdateOrderItem = needUpdateOrderItem.stream().peek(item ->{ + // item.setAlreadyPayRealAmount(item.getAlreadyPayRealAmount().add(item.getSettlePayRealAmount())); + // item.setSettlePayRealAmount(BigDecimal.ZERO); + // }).collect(Collectors.toList()); + // } + // if (transaction.getStatus() == 0){ + // buyer.setBlockedBalance(blockBalance); + // needUpdateOrderItem = needUpdateOrderItem.stream().peek(item ->{ + // item.setSettlePayRealAmount(BigDecimal.ZERO); + // }).collect(Collectors.toList()); + // } + // //修改支付记录状态 + // System.out.println("支付成功1"+JSONUtil.toJsonPrettyStr(leasePayRecordMessage)); + // int i = leasePayRecordMessageMapper.updateById(leasePayRecordMessage); + // //支付成功修改order_item表实际支付金额 + // System.out.println("支付成功2"+JSONUtil.toJsonPrettyStr(needUpdateOrderItem)); + // boolean b = leaseOrderItemService.updateBatchById(needUpdateOrderItem); + // + // System.out.println("支付成功修改order_item表实际支付金额"+ b +"----"+ i); + // if (i < 1 || !b){ + // throw new PayRechargeException("支付失败,该支付记录已修改,消息重试!!!"); + // } + // } + // + // } + // + // }); + // //使用乐观锁防止余额少减少 + // int update = leaseUserWalletDataMapper.update(buyer, new LambdaQueryWrapper() + // .eq(LeaseUserWalletData::getId, buyer.getId()) + // .eq(LeaseUserWalletData::getBalance, initBalance) + // .eq(LeaseUserWalletData::getBlockedBalance, initBlockBalance) + // ); + // if (update < 1){ + // throw new PayRechargeException("支付失败,余额金额错误,消息重试!!!"); + // } + //} + + + + /** + * 余额充值 消费者 (正常流程一笔充值会有两个消息 2 充值中 1充值成功) + * @param payRechargeReturnMessage + */ + @LedgerLog(value = "余额充值") + @RabbitListener(queues = RabbitmqConstant.PAY_RECHARGE_RETURN_QUEUE,containerFactory ="rabbitListenerContainerFactory") + @Transactional(rollbackFor = Exception.class) + public void listenerPayRechargeStatusQueueMessage(@Payload RabbitmqPayRechargeReturnMessage payRechargeReturnMessage) { + System.out.println("充值消费者---"+JSONUtil.toJsonPrettyStr(payRechargeReturnMessage)); + //获取到需要操作的钱包 + List leaseUserWalletDataList = leaseUserWalletDataMapper.selectList(new LambdaQueryWrapper() + .eq(LeaseUserWalletData::getFromAddress, payRechargeReturnMessage.getAddress()) + .eq(LeaseUserWalletData::getDel, false) + ); + if (leaseUserWalletDataList.isEmpty()){ + return; + } + LeaseUserWalletData leaseUserWalletData = null; + for (LeaseUserWalletData item : leaseUserWalletDataList) { + if (item.getFromSymbol().equals(payRechargeReturnMessage.getSymbol())){ + leaseUserWalletData = item; + break; + } + } + //需要新增该币种的钱包 + if (leaseUserWalletData == null){ + LeaseUserWalletData leaseUserWalletData1 = leaseUserWalletDataList.get(0); + leaseUserWalletData = LeaseUserWalletData.builder() + .queueId(payRechargeReturnMessage.getQueue_id()) + .userId(leaseUserWalletData1.getUserId()) + .fromAddress(payRechargeReturnMessage.getAddress()) + .fromChain(payRechargeReturnMessage.getChain()) + .fromSymbol(payRechargeReturnMessage.getSymbol()) + .balance(BigDecimal.ZERO) + .blockedBalance(BigDecimal.ZERO) + .qrcode(leaseUserWalletData1.getQrcode()) + .build(); + leaseUserWalletDataMapper.insert(leaseUserWalletData); + } + LeasePayRechargeMessage leasePayRechargeMessage = leasePayRechargeMessageMapper.selectOne(new LambdaQueryWrapper() + .eq(LeasePayRechargeMessage::getTxHash, payRechargeReturnMessage.getTx_hash())); + BigDecimal balance = leaseUserWalletData.getBalance(); + //处理支付中消息 + if(payRechargeReturnMessage.getStatus() == 2 || leasePayRechargeMessage == null){ + if (leasePayRechargeMessage != null){ + return; + } + + LeasePayRechargeMessage build = LeasePayRechargeMessage.builder() + .queueId(payRechargeReturnMessage.getQueue_id()) + .chain(payRechargeReturnMessage.getChain()) + .blockHeight(payRechargeReturnMessage.getBlock_height()) + .fromAddress(payRechargeReturnMessage.getFromAddress()) + .symbol(payRechargeReturnMessage.getSymbol()) + .amount(payRechargeReturnMessage.getAmount()) + .address(payRechargeReturnMessage.getAddress()) + .txHash(payRechargeReturnMessage.getTx_hash()) + .status(payRechargeReturnMessage.getStatus()) + .build(); + leasePayRechargeMessageMapper.saveOrUpdateByIndex(build); + }else { + //已存在已完成充值的消息 + if (leasePayRechargeMessage.getStatus() == 1){ + return; + } + leasePayRechargeMessage.setStatus(payRechargeReturnMessage.getStatus()); + leasePayRechargeMessage.setUpdateTime(LocalDateTime.now()); + leasePayRechargeMessageMapper.updateById(leasePayRechargeMessage); + } + //处理支付成功消息 + if (payRechargeReturnMessage.getStatus() == 1){ + leaseUserWalletData.setBalance(balance.add(payRechargeReturnMessage.getAmount())); + //使用乐观锁防止余额少加 + int update = leaseUserWalletDataMapper.update(leaseUserWalletData, new LambdaQueryWrapper() + .eq(LeaseUserWalletData::getId, leaseUserWalletData.getId()) + .eq(LeaseUserWalletData::getBalance, balance) + ); + if (update < 1){ + throw new PayRechargeException("余额充值失败,余额已被修改"); + } + } + } + + + /** + * 余额提现 消费者 + * @param payWithdrawReturnMessage + */ + @LedgerLog(value = "余额提现结果") + @RabbitListener(queues = RabbitmqConstant.PAY_WITHDRAW_RETURN_QUEUE,containerFactory ="rabbitListenerContainerFactory") + @Transactional(rollbackFor = Exception.class) + public void listenerPayWithdrawStatusQueueMessage(@Payload RabbitmqPayWithdrawReturnMessage payWithdrawReturnMessage) { + System.out.println("提现消费者----"+ JSONUtil.toJsonPrettyStr(payWithdrawReturnMessage)); + List leasePayWithdrawMessageList = leasePayWithdrawMessageMapper.selectList(new LambdaQueryWrapper() + .eq(LeasePayWithdrawMessage::getQueueId, payWithdrawReturnMessage.getQueue_id())); + if (leasePayWithdrawMessageList.isEmpty()){ + return ; + } + LeasePayWithdrawMessage leasePayWithdrawMessage = handlerBuyerWithdraw(payWithdrawReturnMessage, leasePayWithdrawMessageList); + if (leasePayWithdrawMessage != null){ + LeaseShopConfig leaseShopConfig = leaseShopConfigMapper.selectOne(new LambdaQueryWrapper() + .eq(LeaseShopConfig::getPayAddress, leasePayWithdrawMessage.getFromAddress()) + .eq(LeaseShopConfig::getChain, leasePayWithdrawMessage.getFromChain()) + .eq(LeaseShopConfig::getPayCoin, leasePayWithdrawMessage.getFromSymbol()) + .eq(LeaseShopConfig::getDel, false)); + 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()) + .txHash(payWithdrawReturnMessage.getTx_hash()) + .blockHeight(payWithdrawReturnMessage.getBlock_height()) + .build()); + //TODO 修改leaseShopConfig 金额(暂时不修改,提现申请的时候就修改了) + } + } + + } + + /** + * 处理买家提现消息 + * @param payWithdrawReturnMessage + * @param leasePayWithdrawMessageList + * @return + */ + public LeasePayWithdrawMessage handlerBuyerWithdraw(RabbitmqPayWithdrawReturnMessage payWithdrawReturnMessage,List leasePayWithdrawMessageList){ + LeasePayWithdrawMessage leasePayWithdrawMessage = leasePayWithdrawMessageList.get(0); + //获取对应的提现钱包 + LeaseUserWalletData leaseUserWalletData = leaseUserWalletDataMapper.selectOne(new LambdaQueryWrapper() + .eq(LeaseUserWalletData::getFromAddress, leasePayWithdrawMessage.getFromAddress()) + .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()); + BigDecimal blockBalance = leaseUserWalletData.getBlockedBalance() + .subtract(payWithdrawReturnMessage.getAmount()) + .subtract(leasePayWithdrawMessage.getServiceCharge()); + + if (leasePayWithdrawMessageList.size() == 1){ + //这种情况下说明不是同一笔交易了,但是它又提现了,但是数据库里面没有该提现记录 需要去新增这笔记录并只减去钱包余额 + if (StringUtils.isNotEmpty(leasePayWithdrawMessage.getTxHash()) && leasePayWithdrawMessage.getStatus() == 1 && !leasePayWithdrawMessage.getTxHash().equals(payWithdrawReturnMessage.getTx_hash())){ + leasePayWithdrawMessageMapper.insert(LeasePayWithdrawMessage.builder() + .toChain(leasePayWithdrawMessage.getToChain()) + .toSymbol(leasePayWithdrawMessage.getToSymbol()) + .fromChain(leasePayWithdrawMessage.getFromChain()) + .fromSymbol(leasePayWithdrawMessage.getFromSymbol()) + .fromAddress(leasePayWithdrawMessage.getFromAddress()) + .serviceCharge(leasePayWithdrawMessage.getServiceCharge()) + .toAddress(leasePayWithdrawMessage.getToAddress()) + .status(payWithdrawReturnMessage.getStatus()) + .amount(payWithdrawReturnMessage.getAmount().add(payWithdrawReturnMessage.getFee())) + .queueId(payWithdrawReturnMessage.getQueue_id()) + .txHash(payWithdrawReturnMessage.getTx_hash()) + .build()); + blockBalance = leaseUserWalletData.getBlockedBalance(); + }else{ + //要修改的钱包状态为1 且提现的状态为1 则不处理 + if (leasePayWithdrawMessage.getStatus() == 1 && payWithdrawReturnMessage.getStatus() == 1){ + return null; + } + leasePayWithdrawMessageMapper.updateById(LeasePayWithdrawMessage.builder() + .id(leasePayWithdrawMessage.getId()) + .amount(payWithdrawReturnMessage.getAmount().add(payWithdrawReturnMessage.getFee())) + .status(payWithdrawReturnMessage.getStatus()) + .blockHeight(payWithdrawReturnMessage.getBlock_height()) + .txHash(payWithdrawReturnMessage.getTx_hash()) + .build()); + } + updateWalletInfo(payWithdrawReturnMessage.getStatus(), leaseUserWalletData, balance, blockBalance); + }else if(leasePayWithdrawMessageList.size() >= 2){ + //极少情况 连续三次以上发送相同queueid 的消息 + LeasePayWithdrawMessage with = null; + for (LeasePayWithdrawMessage withdraw : leasePayWithdrawMessageList) { + if (withdraw.getTxHash().equals(payWithdrawReturnMessage.getTx_hash()) ){ + with = withdraw; + } + } + if(with == null){ + leasePayWithdrawMessageMapper.insert(LeasePayWithdrawMessage.builder() + .queueId(payWithdrawReturnMessage.getQueue_id()) + .txHash(payWithdrawReturnMessage.getTx_hash()) + .toChain(leasePayWithdrawMessage.getToChain()) + .toSymbol(leasePayWithdrawMessage.getToSymbol()) + .fromChain(leasePayWithdrawMessage.getFromChain()) + .fromSymbol(leasePayWithdrawMessage.getFromSymbol()) + .fromAddress(leasePayWithdrawMessage.getFromAddress()) + .serviceCharge(payWithdrawReturnMessage.getFee()) + .toAddress(leasePayWithdrawMessage.getToAddress()) + .amount(payWithdrawReturnMessage.getAmount().add(payWithdrawReturnMessage.getFee())) + .build()); + blockBalance =leaseUserWalletData.getBlockedBalance(); + updateWalletInfo(payWithdrawReturnMessage.getStatus(), leaseUserWalletData, balance, blockBalance); + }else{ + leasePayWithdrawMessageMapper.updateById(LeasePayWithdrawMessage.builder() + .id(with.getId()) + .amount(payWithdrawReturnMessage.getAmount().add(payWithdrawReturnMessage.getFee())) + .status(payWithdrawReturnMessage.getStatus()) + .txHash(payWithdrawReturnMessage.getTx_hash()) + .build()); + if (with.getStatus() != 1 && payWithdrawReturnMessage.getStatus() == 1){ + updateWalletInfo(payWithdrawReturnMessage.getStatus(), leaseUserWalletData, balance, blockBalance); + } + } + } + + return null; + } + + + /** + * 更新钱包余额信息 + * @param status + * @param leaseUserWalletData + * @param balance + * @param blockBalance + */ + public void updateWalletInfo(Integer status,LeaseUserWalletData leaseUserWalletData,BigDecimal balance,BigDecimal blockBalance){ + BigDecimal initBalance = leaseUserWalletData.getBalance(); + BigDecimal initBlockBalance = leaseUserWalletData.getBlockedBalance(); + //余额 和 冻结余额 减去提现金额 + 手续费 状态为3不做任何操作 + if (status == 1){ + leaseUserWalletData.setBalance(balance); + leaseUserWalletData.setBlockedBalance(blockBalance); + }else if (status == 0 ){ + //余额提现失败 冻结金额 需要减去提现手续费和提现金额 + leaseUserWalletData.setBlockedBalance(blockBalance); + } + leaseUserWalletData.setUpdateTime(LocalDateTime.now()); + //使用乐观锁防止余额少加 + int update = leaseUserWalletDataMapper.update(leaseUserWalletData, new LambdaQueryWrapper() + .eq(LeaseUserWalletData::getId, leaseUserWalletData.getId()) + .eq(LeaseUserWalletData::getBalance, initBalance) + .eq(LeaseUserWalletData::getBlockedBalance, initBlockBalance) + ); + if (update < 1){ + System.out.println("余额提现/支付失败"); + throw new PayRechargeException("余额提现失败,余额已被修改"); + } + System.out.println("余额提现/支付成功"); + } + + @RabbitListener(queues = RabbitmqConstant.DELETE_WALLET_RETURN_QUEUE,containerFactory ="rabbitListenerContainerFactory") + @Transactional(rollbackFor = Exception.class) + public void listenerDeleteWalletQueue(@Payload RabbitmqDeleteWalletReturnMessage rabbitmqDeleteWalletReturnMessage) { + System.out.println("钱包监听删除----删除钱包地址"+rabbitmqDeleteWalletReturnMessage.getAddress()); + LeaseAutoAddress build; + if (rabbitmqDeleteWalletReturnMessage.getStatus() == 1){ + //设置钱包使用状态为2 已过期 + build = LeaseAutoAddress.builder().status(2).build(); + }else{ + //设置钱包使用状态为3 删除监听失败 + build = LeaseAutoAddress.builder().status(3).build(); + } + int i = leaseAutoAddressMapper.update(build, new LambdaUpdateWrapper() + .eq(LeaseAutoAddress::getAddress, rabbitmqDeleteWalletReturnMessage.getAddress()) + .eq(LeaseAutoAddress::getFromChain, rabbitmqDeleteWalletReturnMessage.getChain()) + .eq(LeaseAutoAddress::getFromSymbol, rabbitmqDeleteWalletReturnMessage.getSymbol()) + ); + int update = leaseUserWalletDataMapper.update(LeaseUserWalletData.builder().del(true).build(), new LambdaUpdateWrapper() + .eq(LeaseUserWalletData::getFromAddress, rabbitmqDeleteWalletReturnMessage.getAddress()) + .eq(LeaseUserWalletData::getFromChain, rabbitmqDeleteWalletReturnMessage.getChain()) + .eq(LeaseUserWalletData::getFromSymbol, rabbitmqDeleteWalletReturnMessage.getSymbol()) + ); + if (i > 0 && update > 0 ){ + System.out.println("钱包监听删除成功:"+rabbitmqDeleteWalletReturnMessage.getAddress()); + } + } + + ////测试 开发环境 支付回调测试 + //@RabbitListener(queues = RabbitmqConstant.PAY_AUTO_QUEUE,containerFactory ="rabbitListenerContainerFactory") + //@Transactional(rollbackFor = Exception.class) + //public void listenerPayBalanceStatusQueueMessage(@Payload RabbitmqPayAutoMessage payAutoReturnMessage) { + // //消费消息 + // System.out.println("自动支付功能queueId"+payAutoReturnMessage.getQueue_id()); + // + // RabbitmqPayAutoReturnMessage rabbitmqPayAutoReturnMessage = RabbitmqPayAutoReturnMessage.builder() + // .queue_id(payAutoReturnMessage.getQueue_id()) + // .from_address(payAutoReturnMessage.getFrom_address()) + // .to_address(payAutoReturnMessage.getTo_address()) + // .chain(payAutoReturnMessage.getChain()) + // .symbol(payAutoReturnMessage.getSymbol()) + // .fee(payAutoReturnMessage.getFee()) + // .amount(payAutoReturnMessage.getAmount()) + // .status(1) + // .tx_hash(UuidGeneratorUtil.generateUuidWithoutHyphen()) + // .block_height(1000L) + // .order_id(payAutoReturnMessage.getOrder_id()) + // .build(); + // + // rabbitTemplate.convertAndSend(RabbitmqConstant.PAY_AUTO_RETURN_QUEUE,rabbitmqPayAutoReturnMessage); + //} + // + //测试 开发环境 充值测试 + //@RabbitListener(queues = RabbitmqConstant.PAY_RECHARGE_QUEUE,containerFactory ="rabbitListenerContainerFactory") + //@Transactional(rollbackFor = Exception.class) + //public void listenerPayRechargeQueueMessage(@Payload RabbitmqPayRechargeMessage payAutoReturnMessage) { + // String s = UuidGeneratorUtil.generateUuidWithoutHyphen(); + // ////发送充值消息 + // //RabbitmqPayRechargeReturnMessage rabbitmqPayRechargeReturnMessage = RabbitmqPayRechargeReturnMessage.builder() + // // .queue_id(payAutoReturnMessage.getQueue_id()) + // // .status(2) + // // .amount(BigDecimal.valueOf(20)) + // // .chain(payAutoReturnMessage.getChain()) + // // .symbol(payAutoReturnMessage.getSymbol()) + // // .address(payAutoReturnMessage.getAddress()) + // // .tx_hash(s) + // // .build(); + // //rabbitTemplate.convertAndSend(RabbitmqConstant.PAY_RECHARGE_RETURN_QUEUE,rabbitmqPayRechargeReturnMessage); + // + // + // //发送充值消息 + // RabbitmqPayRechargeReturnMessage rabbitmqPayRechargeReturnMessage1 = RabbitmqPayRechargeReturnMessage.builder() + // .queue_id(payAutoReturnMessage.getQueue_id()) + // .status(1) + // .amount(BigDecimal.valueOf(20)) + // .chain(payAutoReturnMessage.getChain()) + // .symbol(payAutoReturnMessage.getSymbol()) + // .address(payAutoReturnMessage.getAddress()) + // .tx_hash(s) + // .build(); + // rabbitTemplate.convertAndSend(RabbitmqConstant.PAY_RECHARGE_RETURN_QUEUE,rabbitmqPayRechargeReturnMessage1); + //} + + + //提现 + //@RabbitListener(queues = RabbitmqConstant.PAY_WITHDRAW_QUEUE,containerFactory ="rabbitListenerContainerFactory") + //@Transactional(rollbackFor = Exception.class) + //public void listenerWithdrawQueueMessage(@Payload RabbitmqPayWithdrawMessage payAutoReturnMessage) { + // //发送充值消息 + // RabbitmqPayWithdrawReturnMessage rabbitmqPayRechargeReturnMessage = RabbitmqPayWithdrawReturnMessage.builder() + // .queue_id(payAutoReturnMessage.getQueue_id()) + // .status(1) + // .amount(payAutoReturnMessage.getAmount()) + // .chain(payAutoReturnMessage.getChain()) + // .symbol(payAutoReturnMessage.getSymbol()) + // .from_address(payAutoReturnMessage.getFrom_address()) + // .tx_hash(UuidGeneratorUtil.generateUuidWithoutHyphen()) + // .fee(payAutoReturnMessage.getFee()) + // .build(); + // rabbitTemplate.convertAndSend(RabbitmqConstant.PAY_WITHDRAW_RETURN_QUEUE,rabbitmqPayRechargeReturnMessage); + //} + + + ////测试 开发环境 删除钱包测试 + //@RabbitListener(queues = RabbitmqConstant.DELETE_WALLET_QUEUE,containerFactory ="rabbitListenerContainerFactory") + //@Transactional(rollbackFor = Exception.class) + //public void listenerDeleteWalletQueueMessage(@Payload RabbitmqDeleteWalletMessage payAutoReturnMessage) { + // //发送充值消息 + // RabbitmqDeleteWalletReturnMessage rabbitmqDeleteWalletReturnMessage = RabbitmqDeleteWalletReturnMessage.builder() + // .queue_id(payAutoReturnMessage.getQueue_id()) + // .status(1) + // .chain(payAutoReturnMessage.getChain()) + // .symbol(payAutoReturnMessage.getSymbol()) + // .address(payAutoReturnMessage.getAddress()) + // .build(); + // rabbitTemplate.convertAndSend(RabbitmqConstant.DELETE_WALLET_RETURN_QUEUE,rabbitmqDeleteWalletReturnMessage); + //} + // + + + + +} diff --git a/src/main/java/com/m2pool/lease/mq/message/RabbitmqDeleteWalletMessage.java b/src/main/java/com/m2pool/lease/mq/message/RabbitmqDeleteWalletMessage.java new file mode 100644 index 0000000..6e55f4b --- /dev/null +++ b/src/main/java/com/m2pool/lease/mq/message/RabbitmqDeleteWalletMessage.java @@ -0,0 +1,49 @@ +package com.m2pool.lease.mq.message; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Description 钱包半年内无操作 取消监听消息 + * @Date 2025/7/31 10:42 + * @Author yyb + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class RabbitmqDeleteWalletMessage { + /** + * 方法 默认值0 就行了 + */ + private Integer msg_type; + + private String queue_id; + /** + * 链 + */ + private String chain; + + /** + * 币种 + */ + private String symbol; + + /** + * 钱包地址 + */ + private String address; + + /** + * 时间戳 + */ + private Long timestamp; + + /** + * 签名 时间戳+m2pool + */ + private String sign; + +} diff --git a/src/main/java/com/m2pool/lease/mq/message/RabbitmqDeleteWalletReturnMessage.java b/src/main/java/com/m2pool/lease/mq/message/RabbitmqDeleteWalletReturnMessage.java new file mode 100644 index 0000000..f032576 --- /dev/null +++ b/src/main/java/com/m2pool/lease/mq/message/RabbitmqDeleteWalletReturnMessage.java @@ -0,0 +1,45 @@ +package com.m2pool.lease.mq.message; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Description 钱包半年内无操作 取消监听消息 + * @Date 2025/7/31 10:42 + * @Author yyb + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class RabbitmqDeleteWalletReturnMessage { + /** + * 方法 默认值0 就行了 + */ + private Integer msg_type; + + private String queue_id; + + /** + * 链 + */ + private String chain; + + /** + * 币种 + */ + private String symbol; + + /** + * 钱包地址 + */ + private String address; + + /** + * 状态 0 失败 1成功 2 校验中 3 校验失败 + */ + private Integer status; + +} diff --git a/src/main/java/com/m2pool/lease/mq/message/RabbitmqOrderMessage.java b/src/main/java/com/m2pool/lease/mq/message/RabbitmqOrderMessage.java new file mode 100644 index 0000000..be2faa5 --- /dev/null +++ b/src/main/java/com/m2pool/lease/mq/message/RabbitmqOrderMessage.java @@ -0,0 +1,28 @@ +package com.m2pool.lease.mq.message; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Description ZMQ消息对象 + * @Date 2025/7/31 10:42 + * @Author yyb + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class RabbitmqOrderMessage { + /** + * 订单id + */ + private Long orderId; + + /** + * 店铺id + */ + private Long shopId; + +} diff --git a/src/main/java/com/m2pool/lease/mq/message/RabbitmqPayAutoInfoMessage.java b/src/main/java/com/m2pool/lease/mq/message/RabbitmqPayAutoInfoMessage.java new file mode 100644 index 0000000..8b9e33e --- /dev/null +++ b/src/main/java/com/m2pool/lease/mq/message/RabbitmqPayAutoInfoMessage.java @@ -0,0 +1,74 @@ +package com.m2pool.lease.mq.message; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +/** + * @Description 支付 生产者 + * @Date 2025/7/31 10:42 + * @Author yyb + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class RabbitmqPayAutoInfoMessage { + + /** + * 订单号(该字段不再传递 一个fromAddress 可以对应多个订单) + */ + private String order_id; + + /** + * 卖家充值地址 + */ + private String to_address; + + /** + * 支付金额 + */ + private BigDecimal amount; + + + /** + * 手续费 + */ + private BigDecimal fee; + + /** + * 交易ID + */ + private String tx_hash; + + /** + * 块高 + */ + private Long block_height; + + + //下面五个不需要传输,只存数据库 + /** + * 买方钱包冻结金额 + */ + private BigDecimal blockAmount; + + /** + * 实际应支付金额 + */ + private BigDecimal needAmount; + + /** + * 店铺ID + */ + private Long shopId; + + /** + * 用户邮箱 + */ + private String userId; + +} diff --git a/src/main/java/com/m2pool/lease/mq/message/RabbitmqPayAutoMessage.java b/src/main/java/com/m2pool/lease/mq/message/RabbitmqPayAutoMessage.java new file mode 100644 index 0000000..31c4a1f --- /dev/null +++ b/src/main/java/com/m2pool/lease/mq/message/RabbitmqPayAutoMessage.java @@ -0,0 +1,149 @@ +package com.m2pool.lease.mq.message; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; + +/** + * @Description 支付 生产者 + * @Date 2025/7/31 10:42 + * @Author yyb + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class RabbitmqPayAutoMessage { + + /** + * 消息ID + */ + private String queue_id; + + /** + * 用户支付地址 + */ + private String from_address; + + /** + * 收款地址(用户自定义) + */ + private String to_address; + + + /** + * 支付金额 + */ + private BigDecimal amount; + + /** + * 手续费 + */ + private BigDecimal fee; + + + /** + * 链名称 + */ + private String chain; + + /** + * 币种 + */ + private String symbol; + + + /** + * 时间戳 + */ + private Long timestamp; + + /** + * 签名 时间戳+m2pool + */ + private String sign; + + /** + * 订单号(该字段不再传递 一个fromAddress 可以对应多个订单) + */ + private String order_id; + //下面五个不需要传输,只存数据库 + /** + * 买方钱包冻结金额 + */ + private BigDecimal blockAmount; + + /** + * 实际应支付金额 + */ + private BigDecimal needAmount; + + /** + * 店铺ID + */ + private Long shopId; + + /** + * 用户邮箱 + */ + private String userId; + + + + ///** + // * 消息ID + // */ + //private String queue_id; + // + ///** + // * 买家充值地址 + // */ + //private String from_address; + // + ///** + // * 链名称 + // */ + //private String chain; + // + ///** + // * 币种 + // */ + //private String symbol; + // + ///** + // * 订单总金额 + // */ + //private BigDecimal total_amount; + // + ///** + // * 订单总手续费 + // */ + //private BigDecimal total_fee; + // + ///** + // * 时间戳 + // */ + //private Long timestamp; + // + // + ///** + // * 签名 时间戳+m2pool + // */ + //private String sign; + ///** + // * 收款方信息 + // */ + //private Map transactions; + + + + + + + +} diff --git a/src/main/java/com/m2pool/lease/mq/message/RabbitmqPayAutoReturnInfoMessage.java b/src/main/java/com/m2pool/lease/mq/message/RabbitmqPayAutoReturnInfoMessage.java new file mode 100644 index 0000000..60caf1e --- /dev/null +++ b/src/main/java/com/m2pool/lease/mq/message/RabbitmqPayAutoReturnInfoMessage.java @@ -0,0 +1,64 @@ +package com.m2pool.lease.mq.message; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +/** + * @Description rabbitmq 支付 消费者 + * @Date 2025/7/31 10:42 + * @Author yyb + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class RabbitmqPayAutoReturnInfoMessage { + + /** + * 订单号 + */ + private String order_id; + + + /** + * 卖家 + */ + private String to_address; + + + /** + * 支付金额 + */ + private BigDecimal amount; + + /** + * 手续费 + */ + private BigDecimal fee; + + /** + * 支付结果 + */ + private Integer status; + + /** + * 交易id + */ + private String tx_hash; + + + + /** + * 块高 + */ + private Long block_height; + + + + + +} diff --git a/src/main/java/com/m2pool/lease/mq/message/RabbitmqPayAutoReturnMessage.java b/src/main/java/com/m2pool/lease/mq/message/RabbitmqPayAutoReturnMessage.java new file mode 100644 index 0000000..a39fabc --- /dev/null +++ b/src/main/java/com/m2pool/lease/mq/message/RabbitmqPayAutoReturnMessage.java @@ -0,0 +1,114 @@ +package com.m2pool.lease.mq.message; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; + +/** + * @Description rabbitmq 支付 消费者 + * @Date 2025/7/31 10:42 + * @Author yyb + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class RabbitmqPayAutoReturnMessage{ + + /** + * 消息ID + */ + private String queue_id; + + /** + * 链名称 + */ + private String chain; + + /** + * 币种 + */ + private String symbol; + + + /** + * 支付结果 + */ + private Integer status; + + + /** + * 支付金额 + */ + private BigDecimal amount; + + /** + * 手续费 + */ + private BigDecimal fee; + + + /** + * 交易hash + */ + private String tx_hash; + + /** + * 块高 + */ + private Long block_height; + + + /** + * 买家支付地址 + */ + private String from_address; + + /** + * 订单号 + */ + private String order_id; + + + /** + * 卖家 + */ + private String to_address; + + + ///** + // * 消息ID + // */ + //private String queue_id; + // + ///** + // * 支付结果 + // */ + //private Integer pay_status; + // + ///** + // * 买家支付地址 + // */ + //private String from_address; + ///** + // * 链名称 + // */ + //private String chain; + // + ///** + // * 币种 + // */ + //private String symbol; + // + ///** + // * 收款方信息 + // */ + //private Map transactions; +} diff --git a/src/main/java/com/m2pool/lease/mq/message/RabbitmqPayRechargeMessage.java b/src/main/java/com/m2pool/lease/mq/message/RabbitmqPayRechargeMessage.java new file mode 100644 index 0000000..24dd0d8 --- /dev/null +++ b/src/main/java/com/m2pool/lease/mq/message/RabbitmqPayRechargeMessage.java @@ -0,0 +1,52 @@ +package com.m2pool.lease.mq.message; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Description rabbitmq 余额充值 生产者 + * @Date 2025/7/31 10:42 + * @Author yyb + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class RabbitmqPayRechargeMessage { + + /** + * 交易id + */ + private String queue_id; + /** + * 链名称 + */ + private String chain; + /** + * 币种 + */ + private String symbol; + + /** + * 充值地址 + */ + private String address; + + /** + * 时间戳 + */ + private Long timestamp; + + /** + * 签名 时间戳+m2pool + */ + private String sign; + + /** + * 用户邮箱 + */ + private String user_email; + +} diff --git a/src/main/java/com/m2pool/lease/mq/message/RabbitmqPayRechargeReturnMessage.java b/src/main/java/com/m2pool/lease/mq/message/RabbitmqPayRechargeReturnMessage.java new file mode 100644 index 0000000..c5effb6 --- /dev/null +++ b/src/main/java/com/m2pool/lease/mq/message/RabbitmqPayRechargeReturnMessage.java @@ -0,0 +1,80 @@ +package com.m2pool.lease.mq.message; + +import com.m2pool.lease.annotation.LedgerLogParam; +import com.m2pool.lease.annotation.LedgerLogTag; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +/** + * @Description rabbitmq 余额充值 消费者 + * @Date 2025/7/31 10:42 + * @Author yyb + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class RabbitmqPayRechargeReturnMessage { + /** + * 消息ID + */ + @LedgerLogParam(value = "消息ID") + private String queue_id; + + + /** + * 充值地址 + */ + @LedgerLogTag(value = "充值地址", order = 1) + private String address; + + /** + * 来源地址(用户自己的钱包) + */ + @LedgerLogParam(value = "地址") + private String fromAddress; + + /** + * 充值是否成功 + */ + @LedgerLogParam(value = "充值结果") + private Integer status; + + /** + * 链名称 + */ + @LedgerLogParam(value = "链") + private String chain; + + /** + * 币种 + */ + @LedgerLogParam(value = "币种") + private String symbol; + + /** + * 充值金额 + */ + @LedgerLogParam(value = "充值金额") + private BigDecimal amount; + + /** + * 交易ID + */ + @LedgerLogParam(value = "交易ID") + private String tx_hash; + + /** + * 区块高度 + */ + private Long block_height; + + + @LedgerLogTag(value = "用户邮箱", order = 2) + private String user_email; + +} diff --git a/src/main/java/com/m2pool/lease/mq/message/RabbitmqPayWithdrawMessage.java b/src/main/java/com/m2pool/lease/mq/message/RabbitmqPayWithdrawMessage.java new file mode 100644 index 0000000..fe2c6b5 --- /dev/null +++ b/src/main/java/com/m2pool/lease/mq/message/RabbitmqPayWithdrawMessage.java @@ -0,0 +1,75 @@ +package com.m2pool.lease.mq.message; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +/** + * @Description rabbitmq 余额提现 生产者 + * @Date 2025/7/31 10:42 + * @Author yyb + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class RabbitmqPayWithdrawMessage { + /** + * 消息ID + */ + private String queue_id; + + /** + * 用户充值地址 + */ + private String from_address; + + /** + * 提现地址(用户自定义) + */ + private String to_address; + + + /** + * 提现金额 + */ + private BigDecimal amount; + + /** + * 手续费 + */ + private BigDecimal fee; + + + /** + * 链名称 + */ + private String chain; + + /** + * 币种 + */ + private String symbol; + + + /** + * 时间戳 + */ + private Long timestamp; + + /** + * 签名 时间戳+m2pool + */ + private String sign; + + /** + * 用户邮箱 + */ + private String user_email; + + + +} diff --git a/src/main/java/com/m2pool/lease/mq/message/RabbitmqPayWithdrawReturnMessage.java b/src/main/java/com/m2pool/lease/mq/message/RabbitmqPayWithdrawReturnMessage.java new file mode 100644 index 0000000..33496ff --- /dev/null +++ b/src/main/java/com/m2pool/lease/mq/message/RabbitmqPayWithdrawReturnMessage.java @@ -0,0 +1,86 @@ +package com.m2pool.lease.mq.message; + +import com.m2pool.lease.annotation.LedgerLogParam; +import com.m2pool.lease.annotation.LedgerLogTag; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +/** + * @Description rabbitmq 余额提现 消费者 + * @Date 2025/7/31 10:42 + * @Author yyb + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class RabbitmqPayWithdrawReturnMessage { + /** + * 消息ID + */ + @LedgerLogParam(value = "消息ID") + private String queue_id; + + /** + * 链名称 + */ + @LedgerLogParam(value = "链") + private String chain; + + /** + * 币种 + */ + @LedgerLogParam(value = "币种") + private String symbol; + + + /** + * 提现结果 + */ + @LedgerLogParam(value = "提现结果") + private Integer status; + + + /** + * 提现金额 + */ + @LedgerLogParam(value = "提现金额") + private BigDecimal amount; + + /** + * 手续费 + */ + @LedgerLogParam(value = "手续费") + private BigDecimal fee; + + + /** + * 交易hash + */ + @LedgerLogParam(value = "交易hash") + private String tx_hash; + + /** + * 块高 + */ + private Long block_height; + + /** + * 来源地址 + */ + @LedgerLogTag(value = "来源地址", order = 1) + private String from_address; + + + @LedgerLogTag(value = "用户邮箱", order = 2) + private String user_email; + + ///** + // * 收款地址 + // */ + //private String to_address; +} diff --git a/src/main/java/com/m2pool/lease/mq/message/RabbitmqPoolProxyMessage.java b/src/main/java/com/m2pool/lease/mq/message/RabbitmqPoolProxyMessage.java new file mode 100644 index 0000000..b455b36 --- /dev/null +++ b/src/main/java/com/m2pool/lease/mq/message/RabbitmqPoolProxyMessage.java @@ -0,0 +1,37 @@ +package com.m2pool.lease.mq.message; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Description ZMQ消息对象 + * @Date 2025/7/31 10:42 + * @Author yyb + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class RabbitmqPoolProxyMessage { + /** + * 方法ID 0 add 1 delete + */ + private Integer MethodID; + + /** + * user-miner + */ + private String ID; + + /** + * 转发目标地址 + */ + private String Address; + + /** + * 转发矿池的mining.authorize方法配置参数 + */ + private String Msg; +} diff --git a/src/main/java/com/m2pool/lease/netty/handler/ChannelManager.java b/src/main/java/com/m2pool/lease/netty/handler/ChannelManager.java new file mode 100644 index 0000000..b5fa676 --- /dev/null +++ b/src/main/java/com/m2pool/lease/netty/handler/ChannelManager.java @@ -0,0 +1,86 @@ +package com.m2pool.lease.netty.handler; + +import io.netty.channel.Channel; + +import java.util.Map; +import java.util.Optional; +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 channelMap = new ConcurrentHashMap<>(); + + /** + * key channel value id + */ + public static final Map idMap = 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) { + Channel channel = channelMap.get(ip); + if (channel != null){ + idMap.remove(channel); + channelMap.remove(ip); + } + + + } + + + + public static String getIdByChannel(String ip) { + Channel channel = channelMap.get(ip); + if (channel != null){ + return idMap.get(channel); + } + return null; + } + + /** + * 根据id: 邮箱::mac地址获取Channel + * @param id + * @return + */ + public static Channel getChannelById(String id) { + Optional key = idMap.entrySet() + .stream() + .filter(entry -> id.equals(entry.getValue())) + .map(Map.Entry::getKey) + .findFirst(); + return key.orElse(null); + } + public static boolean sendToClient(String id, Object message) { + Channel channel = getChannelById(id); + if (channel != null && channel.isActive()) { + channel.writeAndFlush(message).syncUninterruptibly(); + return false; + }else{ + return true; + } + } + +} diff --git a/src/main/java/com/m2pool/lease/netty/handler/MessageToJsonDecoder.java b/src/main/java/com/m2pool/lease/netty/handler/MessageToJsonDecoder.java new file mode 100644 index 0000000..dfa3e87 --- /dev/null +++ b/src/main/java/com/m2pool/lease/netty/handler/MessageToJsonDecoder.java @@ -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> 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 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) { + // 继续尝试下一个解析器 + } + } + } + } +} diff --git a/src/main/java/com/m2pool/lease/netty/handler/MessageToJsonEncoder.java b/src/main/java/com/m2pool/lease/netty/handler/MessageToJsonEncoder.java new file mode 100644 index 0000000..db77446 --- /dev/null +++ b/src/main/java/com/m2pool/lease/netty/handler/MessageToJsonEncoder.java @@ -0,0 +1,33 @@ +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 { + 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); + //客户端粘包半包问题 按换行符分割 + buffer.writeBytes("\n".getBytes()); + try { + // 将生成的 ByteBuf 内容写入输出的 ByteBuf + out.writeBytes(buffer); + } finally { + // 释放临时创建的 ByteBuf,避免内存泄漏 + buffer.release(); + } + } +} diff --git a/src/main/java/com/m2pool/lease/netty/handler/ServerChannelHandler.java b/src/main/java/com/m2pool/lease/netty/handler/ServerChannelHandler.java new file mode 100644 index 0000000..1ed325b --- /dev/null +++ b/src/main/java/com/m2pool/lease/netty/handler/ServerChannelHandler.java @@ -0,0 +1,455 @@ +package com.m2pool.lease.netty.handler; + +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.m2pool.lease.constant.GpuBrand; +import com.m2pool.lease.dto.v2.CoinAndAlgorithmDto; +import com.m2pool.lease.dto.v2.MachineConfigDto; +import com.m2pool.lease.entity.*; +import com.m2pool.lease.mapper.*; +import com.m2pool.lease.netty.message.*; +import com.m2pool.lease.service.LeaseMachineConfigService; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.timeout.IdleState; +import io.netty.handler.timeout.IdleStateEvent; +import org.springframework.stereotype.Component; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.TransactionCallbackWithoutResult; +import org.springframework.transaction.support.TransactionTemplate; + +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +import static com.m2pool.lease.netty.message.MethodConstant.PING; + +@Component +@ChannelHandler.Sharable +public class ServerChannelHandler extends SimpleChannelInboundHandler> { + + @Resource + private TransactionTemplate transactionTemplate; + + @Resource + private LeaseShopMapper leaseShopMapper; + + @Resource + private LeaseMachineConfigService leaseMachineConfigService; + + @Resource + private LeaseMachineMapper leaseMachineMapper; + + @Resource + private LeaseGpuConfigMapper leaseGpuConfigMapper; + + @Resource + private LeaseMiningSoftwareConfigMapper leaseMiningSoftwareConfigMapper; + + @Resource + private LeaseMachineTempConfigMapper leaseMachineTempConfigMapper; + + @Resource + private LeaseOrderItemMapper leaseOrderItemMapper; + + @Resource + private LeaseOrderMiningMapper leaseOrderMiningMapper; + + /** + * 接收到客户端的信息 + * + * @param ctx + * @param msg + * @throws Exception + */ + @Override + protected void channelRead0(ChannelHandlerContext ctx, ClientMessage 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 msg){ + String id = msg.getId(); + if (checkMessage(ctx,id)){ + String[] split = id.split("::"); + Object params = msg.getParams(); + GpuAndSoftMessage gpuAndSoftMessage = JSONUtil.toBean(params.toString(), GpuAndSoftMessage.class); + Map gpus = gpuAndSoftMessage.getGpus(); + List miningsofts = gpuAndSoftMessage.getMiningsofts(); + List modelList = GpuBrand.trimBrand(gpus.values().stream().map(GpuMessage::getModel).collect(Collectors.toList())); + //挖矿软件公共配置 + List supportAlgorithmAndCoin = leaseMiningSoftwareConfigMapper.selectSupportAlgorithm(miningsofts,modelList); + // 新增主机相关信息 + transactionTemplate.execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus status) { + try { + LeaseMachine leaseMachine = leaseMachineMapper.selectOne(new LambdaQueryWrapper() + .eq(LeaseMachine::getHostMac, id).eq(LeaseMachine::getDel, false)); + if (leaseMachine == null){ + LeaseShop shop = getShop(split[0]); + leaseMachine = LeaseMachine.builder() + .shopId(shop.getId()) + .hostMac(id) + .type(true) + .state(true) + .saleNumbers(0).build(); + List needAddAlgo = supportAlgorithmAndCoin.stream() + .filter(CoinAndAlgorithmDto::getIsAdd).collect(Collectors.toList()); + + if (!needAddAlgo.isEmpty()){ + List needAdd = new ArrayList<>(); + for (String model : modelList) { + for (CoinAndAlgorithmDto coinAndAlgorithmDto : needAddAlgo) { + needAdd.add(CoinAndAlgorithmDto.builder() + .isEffect(coinAndAlgorithmDto.getIsEffect()) + .coin(coinAndAlgorithmDto.getCoin()) + .algorithm(coinAndAlgorithmDto.getAlgorithm()) + .model(model) + .build() + ); + } + } + //GPU配置信息配置 + leaseGpuConfigMapper.insertOrUpdateBatchByGpuInfo(needAdd); + } + leaseMachineMapper.insert(leaseMachine); + List leaseMachineConfigs = packageInsert(leaseMachine.getId(),gpus, supportAlgorithmAndCoin); + leaseMachineConfigService.saveBatch(leaseMachineConfigs); + }else { + leaseMachine.setOnlineStatus(true); + leaseMachineMapper.updateById(leaseMachine); + //检测对照表 + List needUpdateInfo = packageInsert(leaseMachine.getId(),gpus, supportAlgorithmAndCoin); + //带检测矿机 + List needCheckInfo = leaseMachineMapper.getEffectMachineAndTempMachineList(leaseMachine.getId()); + Map> collect = needCheckInfo.stream().collect(Collectors.groupingBy(MachineConfigDto::getIsTemp)); + checkMachineAndInsertOrUpdate(leaseMachine.getId(),needUpdateInfo, collect); + } + } catch (Exception e) { + // 回滚事务 + status.setRollbackOnly(); + throw e; + } + } + }); + } + } + + + /** + * 封装矿机子表信息 + * 矿机详情表新增数据 + * @return + */ + public List packageInsert(Long machineId,Map gpus,List supportAlgorithmAndCoin){ + List list = new ArrayList<>(); + for (GpuMessage value : gpus.values()) { + for (CoinAndAlgorithmDto coinAndAlgorithmDto : supportAlgorithmAndCoin) { + if (coinAndAlgorithmDto.getModel() != null && value.getModel().contains(coinAndAlgorithmDto.getModel())){ + list.add(LeaseMachineConfig.builder() + .machineId(machineId) + .brand(value.getBrand()) + .name(value.getModel()) + .memory(value.getMem()) + .coin(coinAndAlgorithmDto.getCoin()) + .algorithm(coinAndAlgorithmDto.getAlgorithm()) + .status(coinAndAlgorithmDto.getIsEffect()) + .powerDissipation(coinAndAlgorithmDto.getPowerDissipation()) + .hashrate(coinAndAlgorithmDto.getHashRate()) + .monthIncome(coinAndAlgorithmDto.getMonthIncome()) + .build()); + } + + } + } + return list; + } + + /** + * 检测主机上gpu是否修改 并且根据情况做出相应的增删改操作 + * @param machineId 检测的主机id + * @param needUpdateInfo 检测对照组 + * @param needCheckInfo 检测组 + * @return + */ + public void checkMachineAndInsertOrUpdate(Long machineId,List needUpdateInfo,Map> needCheckInfo){ + //先检测该矿机是否存在合约 + LeaseOrderItem leaseOrderItem = leaseOrderItemMapper.selectOne(new LambdaQueryWrapper() + .eq(LeaseOrderItem::getProductMachineId, machineId) + .eq(LeaseOrderItem::getStatus, 1) + ); + if (leaseOrderItem != null){ + LocalDateTime effectTime = leaseOrderItem.getCreateTime().plusDays(leaseOrderItem.getLeaseTime()); + needUpdateInfo.forEach(item-> item.setEffectTime(effectTime)); + List realMachineList = needCheckInfo.get(false); + List tempMachineList = needCheckInfo.get(true); + boolean realCheck = compare(needUpdateInfo, realMachineList); + //主表信息和本次客户端发送消息不相同 + 并且临时表没有信息 + if(!realCheck && tempMachineList.isEmpty()){ + //直接向临时表中加入数据 + leaseMachineTempConfigMapper.insertBatch(needUpdateInfo); + return; + } + boolean tempCheck = compare(needUpdateInfo, tempMachineList); + if (!realCheck && !tempCheck){ + //删除临时表数据 + 新增临时表数据 + leaseMachineTempConfigMapper.delete(new LambdaQueryWrapper() + .eq(LeaseMachineTempConfig::getMachineId, machineId)); + leaseMachineTempConfigMapper.insertBatch(needUpdateInfo); + return; + } + //ABA + if (realCheck && !tempCheck){ + //删除临时表数据 + leaseMachineTempConfigMapper.delete(new LambdaQueryWrapper() + .eq(LeaseMachineTempConfig::getMachineId, machineId)); + } + }else{ + //删除主表信息 + 新增主表数据 + leaseMachineConfigService.remove(new LambdaUpdateWrapper() + .eq(LeaseMachineConfig::getMachineId, machineId)); + leaseMachineConfigService.saveBatch(needUpdateInfo); + } + } + + + public static boolean compare(List needUpdateInfo, + List needCheckInfo) { + // 如果数量不相等直接返回false + if (needUpdateInfo.size() != needCheckInfo.size()) { + return false; + } + + // 创建两个列表的副本用于比较 + List list1 = new ArrayList<>(needUpdateInfo); + List list2 = new ArrayList<>(needCheckInfo); + + // 比较每个元素 + Iterator it1 = list1.iterator(); + while (it1.hasNext()) { + LeaseMachineConfig config1 = it1.next(); + boolean foundMatch = false; + + Iterator it2 = list2.iterator(); + while (it2.hasNext()) { + MachineConfigDto config2 = it2.next(); + + // 比较指定字段 + if (Objects.equals(config1.getBrand(), config2.getBrand()) && + Objects.equals(config1.getName(), config2.getName()) && + Objects.equals(config1.getMemory(), config2.getMemory()) && + Objects.equals(config1.getCoin(), config2.getCoin()) && + Objects.equals(config1.getAlgorithm(), config2.getAlgorithm())) { + + it2.remove(); + foundMatch = true; + break; + } + } + + if (!foundMatch) { + return false; + } + it1.remove(); + } + + return list1.isEmpty() && list2.isEmpty(); + } + + /** + * 处理客户端发送的挖矿开始信息(暂时不做任何处理) + * @param msg + */ + private void handlerClientMiningMessage(ChannelHandlerContext ctx,ClientMessage msg){ + if (checkMessage(ctx,msg.getId())){ + String[] split = msg.getId().split("::"); + Object params = msg.getParams(); + ClientConfigurationMining clientConfigurationMining = JSONUtil.toBean(params.toString(), ClientConfigurationMining.class); + //修改 当前邮箱账户 对应的该矿池的 一些基本信息 TODO 这里可以设置真实开始时间 和 状态改为1 + leaseOrderMiningMapper.updateOrderMining(split[0],clientConfigurationMining); + } + } + + /** + * 根据身份码获取店铺信息 + * @param authCode + * @return + */ + public LeaseShop getShop(String authCode){ + return leaseShopMapper.selectOne(new LambdaQueryWrapper() + .eq(LeaseShop::getUserEmail, authCode)); + } + + + /** + * 检查消息的合法性 + * @return + */ + private boolean checkMessage(ChannelHandlerContext ctx,String id){ + System.out.println("客户端消息id"+ id); + String[] split = id.split("::"); + if (split.length != 2){ + return false; + } + LeaseShop leaseShop = getShop(split[0]); + Channel channel = ctx.channel(); + ChannelManager.idMap.put(channel,id); + //不存在对应身份码的店铺 发送消息给客户端 + if (leaseShop == null){ + sendErrorResp(ctx,id); + return false; + } + return true; + } + + /** + * 身份码校验失败 + * @param ctx + * @param id + */ + public void sendErrorResp(ChannelHandlerContext ctx,String id){ + ServerMessage errorMsg = new ServerMessage<>(); + errorMsg.setId(id); + errorMsg.setResult(false); + ctx.channel().writeAndFlush(errorMsg).syncUninterruptibly(); + } + + + /** + * 活跃的、有效的通道 + * 第一次连接成功后进入的方法 + * + * @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) { + System.out.println("ip:"+getIPString(ctx)+"断开连接"); + String id = ChannelManager.idMap.get(ctx.channel()); + //修改矿机离线状态为 离线 + if (id != null){ + leaseMachineMapper.update(LeaseMachine.builder().onlineStatus(false).build(), + new LambdaUpdateWrapper().eq(LeaseMachine::getHostMac,id)); + } + 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 { + ClientMessage bean = null; + if (msg instanceof String) { + bean = JSONUtil.toBean(msg.toString(), ClientMessage.class); + } + super.channelRead(ctx, bean); + } + /** + * 心跳机制,超时处理 + * + * @param ctx + * @param evt + * @throws Exception + */ + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + if (evt instanceof IdleStateEvent) { + IdleStateEvent event = (IdleStateEvent) evt; + String ip = getIPString(ctx); + String id = ChannelManager.getIdByChannel(ip); + if (event.state() == IdleState.READER_IDLE) { + System.out.println(id+"用户写超时,接受pong"+System.currentTimeMillis()); + //System.out.println(id+"用户读超时,断开连接"); + ChannelManager.removeChannel(ip); + ctx.disconnect();//断开 + } else if (event.state() == IdleState.WRITER_IDLE ) { + System.out.println(id+"用户写超时,发送ping"+System.currentTimeMillis()); + if (id != null){ + ClientMessage ping = new ClientMessage(); + ping.setMethod(PING); + ping.setId(id); + ctx.writeAndFlush(ping); + } + } + //else if (event.state() == IdleState.ALL_IDLE) { + // 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; + } + +} diff --git a/src/main/java/com/m2pool/lease/netty/message/ClientConfigurationMining.java b/src/main/java/com/m2pool/lease/netty/message/ClientConfigurationMining.java new file mode 100644 index 0000000..a61daba --- /dev/null +++ b/src/main/java/com/m2pool/lease/netty/message/ClientConfigurationMining.java @@ -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; + + /** + * 矿池挖矿号 仅支持不支持钱包挖矿的矿池 如 f2pool,m2pool + */ + private String worker_id; + + + /** + * 收款钱包 + */ + private String wallet_address; + + + /** + * 挖矿信息页面 只有支持钱包挖矿的矿池才有这个字段 + */ + private String watch_url; + + +} diff --git a/src/main/java/com/m2pool/lease/netty/message/ClientMessage.java b/src/main/java/com/m2pool/lease/netty/message/ClientMessage.java new file mode 100644 index 0000000..1e7851f --- /dev/null +++ b/src/main/java/com/m2pool/lease/netty/message/ClientMessage.java @@ -0,0 +1,29 @@ +package com.m2pool.lease.netty.message; + +import lombok.Data; + +/** + * @Description 客户端消息 + * @Date 2025/11/26 11:13 + * @Author yyb + */ +@Data +public class ClientMessage { + /** + * 身份码.主机码 + */ + private String id; + + /** + * 方法名 auth.machineCode + */ + private String method; + + /** + * 消息体 + */ + private T params; + + + +} diff --git a/src/main/java/com/m2pool/lease/netty/message/GpuAndSoftMessage.java b/src/main/java/com/m2pool/lease/netty/message/GpuAndSoftMessage.java new file mode 100644 index 0000000..bfe227c --- /dev/null +++ b/src/main/java/com/m2pool/lease/netty/message/GpuAndSoftMessage.java @@ -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 miningsofts; + + /** + * gpu集合 + */ + private Map gpus; + +} diff --git a/src/main/java/com/m2pool/lease/netty/message/GpuMessage.java b/src/main/java/com/m2pool/lease/netty/message/GpuMessage.java new file mode 100644 index 0000000..cebf104 --- /dev/null +++ b/src/main/java/com/m2pool/lease/netty/message/GpuMessage.java @@ -0,0 +1,26 @@ +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 Integer mem; + +} diff --git a/src/main/java/com/m2pool/lease/netty/message/MethodConstant.java b/src/main/java/com/m2pool/lease/netty/message/MethodConstant.java new file mode 100644 index 0000000..59b2bd7 --- /dev/null +++ b/src/main/java/com/m2pool/lease/netty/message/MethodConstant.java @@ -0,0 +1,50 @@ +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"; + + + /** + * 心跳pong + */ + public static final String PONG = "pong"; + + + /** + * 心跳ping + */ + public static final String PING = "ping"; + + /** + * 心跳ping间隔时间 + */ + public static final Integer PING_TIME = 30; + + + /** + * 心跳pong间隔时间 + */ + public static final Integer PONG_TIME = 60; +} diff --git a/src/main/java/com/m2pool/lease/netty/message/ServerConfigurationMining.java b/src/main/java/com/m2pool/lease/netty/message/ServerConfigurationMining.java new file mode 100644 index 0000000..15a04a1 --- /dev/null +++ b/src/main/java/com/m2pool/lease/netty/message/ServerConfigurationMining.java @@ -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; + + /** + * 矿池挖矿账号 仅支持不支持钱包挖矿的矿池 如 f2pool,m2pool + */ + private String pool_user; + /** + * 矿池挖矿号 仅支持不支持钱包挖矿的矿池 如 f2pool,m2pool + */ + private String worker_id; + + /** + * 合约结束时间 + */ + private Long end_timestamp; + +} diff --git a/src/main/java/com/m2pool/lease/netty/message/ServerMessage.java b/src/main/java/com/m2pool/lease/netty/message/ServerMessage.java new file mode 100644 index 0000000..052b5fc --- /dev/null +++ b/src/main/java/com/m2pool/lease/netty/message/ServerMessage.java @@ -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 { + /** + * 身份码.主机码 + */ + private String id; + + /** + * 方法名 0 false 1 true + */ + private Boolean result; + + /** + * 方法名 auth.machineCode 详情见 + */ + private String method; + + /** + * 消息体 + */ + private T data; +} diff --git a/src/main/java/com/m2pool/lease/netty/parser/ClientConfigMiningParser.java b/src/main/java/com/m2pool/lease/netty/parser/ClientConfigMiningParser.java new file mode 100644 index 0000000..47bb87d --- /dev/null +++ b/src/main/java/com/m2pool/lease/netty/parser/ClientConfigMiningParser.java @@ -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> { + @Override + public ClientMessage parse(ObjectMapper objectMapper, String jsonStr) throws Exception { + return objectMapper.readValue(jsonStr, getTypeReference()); + } + + @Override + public TypeReference> getTypeReference() { + return new TypeReference>() {}; + } +} diff --git a/src/main/java/com/m2pool/lease/netty/parser/GpuMessageMapParser.java b/src/main/java/com/m2pool/lease/netty/parser/GpuMessageMapParser.java new file mode 100644 index 0000000..a567535 --- /dev/null +++ b/src/main/java/com/m2pool/lease/netty/parser/GpuMessageMapParser.java @@ -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>> { + @Override + public ClientMessage> parse(ObjectMapper objectMapper, String jsonStr) throws Exception { + return objectMapper.readValue(jsonStr, getTypeReference()); + } + + @Override + public TypeReference>> getTypeReference() { + return new TypeReference>>() {}; + } +} diff --git a/src/main/java/com/m2pool/lease/netty/parser/MessageParser.java b/src/main/java/com/m2pool/lease/netty/parser/MessageParser.java new file mode 100644 index 0000000..64bd63c --- /dev/null +++ b/src/main/java/com/m2pool/lease/netty/parser/MessageParser.java @@ -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 { + /** + * 解析 JSON 字符串为指定类型的消息对象 + * @param objectMapper Jackson 的 ObjectMapper 实例 + * @param jsonStr 待解析的 JSON 字符串 + * @return 解析后的消息对象 + * @throws Exception 解析过程中可能抛出的异常 + */ + T parse(ObjectMapper objectMapper, String jsonStr) throws Exception; + + /** + * 获取该解析器对应的类型引用 + * @return 类型引用 + */ + TypeReference getTypeReference(); +} diff --git a/src/main/java/com/m2pool/lease/netty/parser/ServerConfigMiningParser.java b/src/main/java/com/m2pool/lease/netty/parser/ServerConfigMiningParser.java new file mode 100644 index 0000000..10b6ff8 --- /dev/null +++ b/src/main/java/com/m2pool/lease/netty/parser/ServerConfigMiningParser.java @@ -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> { + @Override + public ServerMessage parse(ObjectMapper objectMapper, String jsonStr) throws Exception { + return objectMapper.readValue(jsonStr, getTypeReference()); + } + + @Override + public TypeReference> getTypeReference() { + return new TypeReference>() {}; + } +} diff --git a/src/main/java/com/m2pool/lease/netty/parser/StringParser.java b/src/main/java/com/m2pool/lease/netty/parser/StringParser.java new file mode 100644 index 0000000..1fd380a --- /dev/null +++ b/src/main/java/com/m2pool/lease/netty/parser/StringParser.java @@ -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 { + @Override + public String parse(ObjectMapper objectMapper, String jsonStr) throws Exception { + return jsonStr; + } + + @Override + public TypeReference getTypeReference() { + return new TypeReference() {}; + } +} diff --git a/src/main/java/com/m2pool/lease/netty/server/NettyTcpServer.java b/src/main/java/com/m2pool/lease/netty/server/NettyTcpServer.java new file mode 100644 index 0000000..432239e --- /dev/null +++ b/src/main/java/com/m2pool/lease/netty/server/NettyTcpServer.java @@ -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参数,当服务器请求处理程全满时,用于临时存放已完成三次握手请求的队列的最大长度。如果未设置或所设置的值小于1,Java将使用默认值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"); + } + +} diff --git a/src/main/java/com/m2pool/lease/netty/server/ServerChannelInitializer.java b/src/main/java/com/m2pool/lease/netty/server/ServerChannelInitializer.java new file mode 100644 index 0000000..c3c7953 --- /dev/null +++ b/src/main/java/com/m2pool/lease/netty/server/ServerChannelInitializer.java @@ -0,0 +1,39 @@ +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.codec.LineBasedFrameDecoder; +import io.netty.handler.timeout.IdleStateHandler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +import static com.m2pool.lease.netty.message.MethodConstant.PING_TIME; +import static com.m2pool.lease.netty.message.MethodConstant.PONG_TIME; + + +@Component +public class ServerChannelInitializer extends ChannelInitializer { + + @Autowired + ServerChannelHandler serverChannelHandler; + + @Override + protected void initChannel(SocketChannel socketChannel) throws Exception { + ChannelPipeline pipeline = socketChannel.pipeline(); + //IdleStateHandler心跳机制,如果超时触发Handle中userEventTrigger()方法 + pipeline.addLast("idleStateHandler", + new IdleStateHandler(PONG_TIME, PING_TIME, 0, TimeUnit.SECONDS)); + pipeline.addLast(new LineBasedFrameDecoder(1024)); + //字符串编解码器 + pipeline.addLast(new MessageToJsonDecoder(),new MessageToJsonEncoder()); + //自定义Handler + pipeline.addLast("serverChannelHandler", serverChannelHandler); + } +} diff --git a/src/main/java/com/m2pool/lease/redis/EmailCodeValue.java b/src/main/java/com/m2pool/lease/redis/EmailCodeValue.java new file mode 100644 index 0000000..0d50c79 --- /dev/null +++ b/src/main/java/com/m2pool/lease/redis/EmailCodeValue.java @@ -0,0 +1,24 @@ +package com.m2pool.lease.redis; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * @Description 用户登录对象 + * @Date 2024/6/11 14:13 + * @Author dy + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class EmailCodeValue implements Serializable { + + private String email; + + private String emailCode; + + private int times; +} diff --git a/src/main/java/com/m2pool/lease/redis/RedisAuthKey.java b/src/main/java/com/m2pool/lease/redis/RedisAuthKey.java new file mode 100644 index 0000000..f2e5856 --- /dev/null +++ b/src/main/java/com/m2pool/lease/redis/RedisAuthKey.java @@ -0,0 +1,115 @@ +package com.m2pool.lease.redis; + +/** + * @Description 用户权限redis + * @Date 2025/12/23 16:19 + * @Author yyb + */ +public class RedisAuthKey { + + private static final String LOGIN_CODE_KEY= "login:code"; + + private static final String REGISTER_CODE_KEY= "register:code"; + + private static final String UPDATE_PASSWORD_CODE_KEY= "update:password:code"; + + private static final String USER_LOCK_KEY= "user:lock"; + + private static final String PASSWORD_ERROR_COUNT_KEY= "password:error:count"; + + private static final String USER_LOGIN_KEY= "user:login"; + + private static final String GOOGLE_BIND_CODE_KEY = "google:bind:code"; + + private static final String GOOGLE_CLOSE_CODE_KEY= "google:close:code"; + + private static final String GOOGLE_OPEN_CODE_KEY= "google:open:code"; + + private static final String CLOSE_ACCOUNT_KEY= "close:account"; + /** + * 获取登录验证码key + * @param email + * @return + */ + public static String getLoginCodeKey(String email) { + return LOGIN_CODE_KEY + ":" + email; + } + + /** + * 获取注册验证码key + * @param email + * @return + */ + public static String getRegisterCodeKey(String email) { + return REGISTER_CODE_KEY + ":" + email; + } + + /** + * 获取修改密码验证码key + * @param email + * @return + */ + public static String getUpdatePasswordCodeKey(String email) { + return UPDATE_PASSWORD_CODE_KEY + ":" + email; + } + + /** + * 获取用户密码输入次数过多 禁止登录 key + * @param email + * @return + */ + public static String getUserLockKey(String email) { + return USER_LOCK_KEY + ":" + email; + } + + /** + * 获取用户密码输入次数 key + * @param email + * @return + */ + public static String getPasswordErrorCountKey(String email) { + return PASSWORD_ERROR_COUNT_KEY + ":" + email; + } + + /** + * 获取用户登录信息 key + * @param email + * @return + */ + public static String getUserLoginKey(String email) { + return USER_LOGIN_KEY + ":" + email; + } + + + /** + * 获取用户谷歌bind验证码 key + * @param email + * @return + */ + public static String getGoogleBindCodeKey(String email) { + return GOOGLE_BIND_CODE_KEY + ":" + email; + } + + /** + * 获取用户谷歌关闭验证码 key + * @param email + * @return + */ + public static String getGoogleCloseCodeKey(String email) { + return GOOGLE_CLOSE_CODE_KEY + ":" + email; + } + + + /** + * 获取用户谷歌验证开启 key + * @param email + * @return + */ + public static String getGoogleOpenCodeKey(String email) { + return GOOGLE_BIND_CODE_KEY + ":" + email; + } + + public static String getCloseAccountKey(String email) { + return CLOSE_ACCOUNT_KEY + ":" + email; + } +} diff --git a/src/main/java/com/m2pool/lease/redis/RedisCoinInfoKey.java b/src/main/java/com/m2pool/lease/redis/RedisCoinInfoKey.java new file mode 100644 index 0000000..44800e5 --- /dev/null +++ b/src/main/java/com/m2pool/lease/redis/RedisCoinInfoKey.java @@ -0,0 +1,72 @@ +package com.m2pool.lease.redis; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * @Description redis key + * @Date 2025/8/18 09:49 + * @Author yyb + */ +public class RedisCoinInfoKey { + // 静态不可变的 Map,存储币种名和对应价格标识 + private static final Map COIN_PRICE_MAP; + + private static final Map COIN_MHS_MAP; + + private static final Map COIN_REWARD_MAP; + static { + + Map mapPrice = new HashMap<>(); + mapPrice.put("nexa", "nexa:price"); + mapPrice.put("mona", "mona:price"); + mapPrice.put("rxd", "rxd:price"); + mapPrice.put("grs", "grs:price"); + mapPrice.put("dgbs","dgb:price"); + mapPrice.put("dgbq","dgb:price"); + mapPrice.put("dgbo","dgb:price"); + mapPrice.put("alph","alph:price"); + mapPrice.put("eth","eth:price"); + + Map mapMhs = new HashMap<>(); + mapMhs.put("nexa", "nexa:mhs"); + mapMhs.put("mona", "mona:mhs"); + mapMhs.put("rxd", "rxd:mhs"); + mapMhs.put("grs", "grs:mhs"); + mapMhs.put("dgbo","dgbo:mhs"); + mapMhs.put("dgbq","dgbq:mhs"); + mapMhs.put("dgbs","dgbs:mhs"); + mapMhs.put("alph","alph:mhs"); + + + Map mapReward = new HashMap<>(); + mapReward.put("nexa", "nexa:reward"); + mapReward.put("mona", "mona:reward"); + mapReward.put("rxd", "rxd:reward"); + mapReward.put("grs", "grs:reward"); + mapReward.put("dgbs","dgb:reward"); + mapReward.put("dgbq","dgb:reward"); + mapReward.put("dgbo","dgb:reward"); + mapReward.put("alph","alph:reward"); + COIN_PRICE_MAP = Collections.unmodifiableMap(mapPrice); + COIN_MHS_MAP = Collections.unmodifiableMap(mapMhs); + COIN_REWARD_MAP = Collections.unmodifiableMap(mapReward); + } + + /** + * 根据币种名获取对应的价格标识 + * @param coinName 币种名 + * @return 对应的价格标识,若不存在则返回 null + */ + public static String getPiceKey(String coinName) { + return COIN_PRICE_MAP.get(coinName); + } + + public static String getMhsKey(String coinName) { + return COIN_MHS_MAP.get(coinName); + } + public static String getRewardKey(String coinName) { + return COIN_REWARD_MAP.get(coinName); + } +} diff --git a/src/main/java/com/m2pool/lease/redis/config/FastJson2JsonRedisSerializer.java b/src/main/java/com/m2pool/lease/redis/config/FastJson2JsonRedisSerializer.java new file mode 100644 index 0000000..1047ccb --- /dev/null +++ b/src/main/java/com/m2pool/lease/redis/config/FastJson2JsonRedisSerializer.java @@ -0,0 +1,73 @@ +package com.m2pool.lease.redis.config; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.parser.ParserConfig; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.TypeFactory; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.SerializationException; +import org.springframework.util.Assert; + +import java.nio.charset.Charset; + +/** + * @Description redis使用FastJson序列化 + * @Date 2024/6/13 10:01 + * @Author dy + */ +public class FastJson2JsonRedisSerializer implements RedisSerializer { + + @SuppressWarnings("unused") + private ObjectMapper objectMapper = new ObjectMapper(); + + public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + private Class clazz; + + static + { + ParserConfig.getGlobalInstance().setAutoTypeSupport(true); + } + + public FastJson2JsonRedisSerializer(Class clazz) + { + super(); + this.clazz = clazz; + } + + @Override + public byte[] serialize(T t) throws SerializationException + { + if (t == null) + { + return new byte[0]; + } + return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET); + } + + @Override + public T deserialize(byte[] bytes) throws SerializationException + { + if (bytes == null || bytes.length <= 0) + { + return null; + } + String str = new String(bytes, DEFAULT_CHARSET); + + return JSON.parseObject(str, clazz); + } + + public void setObjectMapper(ObjectMapper objectMapper) + { + Assert.notNull(objectMapper, "'objectMapper' must not be null"); + this.objectMapper = objectMapper; + } + + protected JavaType getJavaType(Class clazz) + { + return TypeFactory.defaultInstance().constructType(clazz); + } + +} diff --git a/src/main/java/com/m2pool/lease/redis/config/RedisConfig.java b/src/main/java/com/m2pool/lease/redis/config/RedisConfig.java new file mode 100644 index 0000000..e0a9a73 --- /dev/null +++ b/src/main/java/com/m2pool/lease/redis/config/RedisConfig.java @@ -0,0 +1,139 @@ +package com.m2pool.lease.redis.config;//package com.m2pool.lease.redis.config; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + * @Description redis配置 + * @Date 2024/6/13 09:58 + * @Author dy + */ +@RefreshScope +@Configuration +@EnableCaching +public class RedisConfig extends CachingConfigurerSupport { + + @Value("${spring.redis.host}") + private String host; + @Value("${spring.redis.port}") + private int port; + @Value("${spring.redis.database:0}") + private int database = 0; + + @Bean + @SuppressWarnings(value = { "unchecked", "rawtypes" }) + public RedisTemplate redisTemplate(@Qualifier("redisConnectionFactory0") RedisConnectionFactory connectionFactory) + { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); + + + ObjectMapper mapper = new ObjectMapper(); + mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); + serializer.setObjectMapper(mapper); + + // 使用StringRedisSerializer来序列化和反序列化redis的key值 + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(serializer); + + // Hash的key也采用StringRedisSerializer的序列化方式 + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(serializer); + + template.afterPropertiesSet(); + return template; + } + + @Bean(name = "redisTemplate7") + public RedisTemplate redisTemplate7(@Qualifier("redisConnectionFactory7") RedisConnectionFactory redisConnectionFactory0) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(redisConnectionFactory0); + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(new StringRedisSerializer()); + return template; + } + + + /** + * 7号 数据库 - > + * @return + */ + @Bean + public RedisConnectionFactory redisConnectionFactory7() { + LettuceConnectionFactory factory = new LettuceConnectionFactory(); + factory.setHostName(host); + factory.setPort(port); + factory.setDatabase(7); + return factory; + } + + /** + * 默认 数据库 + * @return + */ + @Bean + @Primary //添加@Primary默认使用的库 + public RedisConnectionFactory redisConnectionFactory0() { + LettuceConnectionFactory factory = new LettuceConnectionFactory(); + factory.setHostName(host); + factory.setPort(port); + factory.setDatabase(database); + return factory; + } + + + + @Bean + public DefaultRedisScript limitScript() + { + // 泛型是返回值的类型 + DefaultRedisScript redisScript = new DefaultRedisScript<>(); + // 设置脚本 + redisScript.setScriptText(limitScriptText()); + // 设置返回值类型 + redisScript.setResultType(Long.class); + return redisScript; + } + + + + /** + * 限流脚本 + */ + private String limitScriptText() + { + return "local key = KEYS[1]\n" + + "local count = tonumber(ARGV[1])\n" + + "local time = tonumber(ARGV[2])\n" + + "local current = redis.call('get', key);\n" + + "if current and tonumber(current) > count then\n" + + " return tonumber(current);\n" + + "end\n" + + "current = redis.call('incr', key)\n" + + "if tonumber(current) == 1 then\n" + + " redis.call('expire', key, time)\n" + + "end\n" + + "return tonumber(current);"; + } + + +} diff --git a/src/main/java/com/m2pool/lease/redis/service/RedisService.java b/src/main/java/com/m2pool/lease/redis/service/RedisService.java new file mode 100644 index 0000000..a59b174 --- /dev/null +++ b/src/main/java/com/m2pool/lease/redis/service/RedisService.java @@ -0,0 +1,313 @@ +package com.m2pool.lease.redis.service;//package com.m2pool.lease.redis.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.redis.core.BoundSetOperations; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * @Description redis 相关 + * @Date 2024/6/13 9:22 + * @Author dy + */ +@SuppressWarnings(value = { "unchecked", "rawtypes" }) +@Component +public class RedisService { + + @Autowired + @Qualifier("redisTemplate") + private RedisTemplate redisTemplate; + + @Autowired + @Qualifier("redisTemplate7") + private RedisTemplate redisTemplate7; + + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + */ + public void setCacheObject(final String key, final T value) + { + redisTemplate.opsForValue().set(key, value); + } + + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @param timeout 时间 + * @param timeUnit 时间颗粒度 + */ + public void setCacheObject(final String key, final T value, final Long timeout, final TimeUnit timeUnit) + { + redisTemplate.opsForValue().set(key, value, timeout, timeUnit); + } + + + public void setCacheObject7(final String key, final T value, final Long timeout, final TimeUnit timeUnit) + { + redisTemplate7.opsForValue().set(key, value, timeout, timeUnit); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout) + { + return expire(key, timeout, TimeUnit.SECONDS); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @param unit 时间单位 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout, final TimeUnit unit) + { + return redisTemplate.expire(key, timeout, unit); + } + + /** + * 获取有效时间 + * + * @param key Redis键 + * @return 有效时间 + */ + public long getExpire(final String key) + { + return redisTemplate.getExpire(key); + } + + /** + * 获取有效时间 + * + * @param key Redis键 + * @return 有效时间 + */ + public long getExpire(final String key,TimeUnit timetype) + { + return redisTemplate.getExpire(key,timetype); + } + + + /** + * 判断 key是否存在 + * + * @param key 键 + * @return true 存在 false不存在 + */ + public Boolean hasKey(String key) + { + return redisTemplate.hasKey(key); + } + + /** + * 获得缓存的基本对象。 + * + * @param key 缓存键值 + * @return 缓存键值对应的数据 + */ + public T getCacheObject(final String key) + { + ValueOperations operation = redisTemplate.opsForValue(); + return operation.get(key); + } + + public T getCacheObject7(final String key) + { + ValueOperations operation = redisTemplate7.opsForValue(); + return operation.get(key); + } + + /** + * 所有数字类型转换为BigDecimal + * @param key 缓存键值 + * @return 缓存键值对应数据 + */ + public BigDecimal getCacheBigDecimal(final String key) { + ValueOperations operation = redisTemplate.opsForValue(); + Object value = operation.get(key); + if (value != null) { + if (value instanceof BigDecimal) { + return (BigDecimal) value; + } else if (value instanceof String) { + try { + return new BigDecimal((String) value); + } catch (NumberFormatException e) { + // 处理字符串无法转换为 BigDecimal 的情况 + return null; + } + } else if (value instanceof Number) { + return new BigDecimal(value.toString()); + } + } + return null; + } + + /** + * 删除单个对象 + * + * @param key + */ + public boolean deleteObject(final String key) + { + return redisTemplate.delete(key); + } + + + /** + * 删除集合对象 + * + * @param collection 多个对象 + * @return + */ + public long deleteObject(final Collection collection) + { + return redisTemplate.delete(collection); + } + + /** + * 缓存List数据 + * + * @param key 缓存的键值 + * @param dataList 待缓存的List数据 + * @return 缓存的对象 + */ + public long setCacheList(final String key, final List dataList) + { + Long count = redisTemplate.opsForList().rightPushAll(key, dataList); + return count == null ? 0 : count; + } + /** + * 获得缓存的list对象 + * + * @param key 缓存的键值 + * @return 缓存键值对应的数据 + */ + public List getCacheList(final String key) + { + return redisTemplate.opsForList().range(key, 0, -1); + } + + /** + * 缓存Set + * + * @param key 缓存键值 + * @param dataSet 缓存的数据 + * @return 缓存数据的对象 + */ + public BoundSetOperations setCacheSet(final String key, final Set dataSet) + { + BoundSetOperations setOperation = redisTemplate.boundSetOps(key); + Iterator it = dataSet.iterator(); + while (it.hasNext()) + { + setOperation.add(it.next()); + } + return setOperation; + } + + /** + * 获得缓存的set + * + * @param key + * @return + */ + public Set getCacheSet(final String key) + { + return redisTemplate.opsForSet().members(key); + } + + /** + * 缓存Map + * + * @param key + * @param dataMap + */ + public void setCacheMap(final String key, final Map dataMap) + { + if (dataMap != null) { + redisTemplate.opsForHash().putAll(key, dataMap); + } + } + + /** + * 获得缓存的Map + * + * @param key + * @return + */ + public Map getCacheMap(final String key) + { + return redisTemplate.opsForHash().entries(key); + + } + + /** + * 往Hash中存入数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @param value 值 + */ + public void setCacheMapValue(final String key, final String hKey, final T value) + { + redisTemplate.opsForHash().put(key, hKey, value); + } + + /** + * 获取Hash中的数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return Hash中的对象 + */ + public T getCacheMapValue(final String key, final String hKey) + { + HashOperations opsForHash = redisTemplate.opsForHash(); + return opsForHash.get(key, hKey); + } + + /** + * 获取多个Hash中的数据 + * + * @param key Redis键 + * @param hKeys Hash键集合 + * @return Hash对象集合 + */ + public List getMultiCacheMapValue(final String key, final Collection hKeys) + { + return redisTemplate.opsForHash().multiGet(key, hKeys); + } + + /** + * 获得缓存的基本对象列表 + * + * @param pattern 字符串前缀 + * @return 对象列表 + */ + public Collection keys(final String pattern) + { + return redisTemplate.keys(pattern); + } + +} diff --git a/src/main/java/com/m2pool/lease/service/LeaseMachineConfigService.java b/src/main/java/com/m2pool/lease/service/LeaseMachineConfigService.java new file mode 100644 index 0000000..562169c --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/LeaseMachineConfigService.java @@ -0,0 +1,16 @@ +package com.m2pool.lease.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.m2pool.lease.entity.LeaseMachineConfig; + +/** + *

+ * GPU对应币种挖矿算力(只要gpu类型相同那么就使用这个配置) 服务类 + *

+ * + * @author yyb + * @since 2025-11-27 + */ +public interface LeaseMachineConfigService extends IService { + +} diff --git a/src/main/java/com/m2pool/lease/service/LeaseMachinePriceService.java b/src/main/java/com/m2pool/lease/service/LeaseMachinePriceService.java new file mode 100644 index 0000000..fdc5670 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/LeaseMachinePriceService.java @@ -0,0 +1,16 @@ +package com.m2pool.lease.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.m2pool.lease.entity.LeaseMachinePrice; + +/** + *

+ * 商品表对应的物品机器表 服务类 + *

+ * + * @author yyb + * @since 2025-11-25 + */ +public interface LeaseMachinePriceService extends IService { + +} diff --git a/src/main/java/com/m2pool/lease/service/LeaseMachineService.java b/src/main/java/com/m2pool/lease/service/LeaseMachineService.java new file mode 100644 index 0000000..cf5ab1b --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/LeaseMachineService.java @@ -0,0 +1,95 @@ +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 org.springframework.web.bind.annotation.RequestParam; + +import java.util.List; + +/** + *

+ * gpu类型出售矿机表 服务类 + *

+ * + * @author yyb + * @since 2025-11-21 + */ +public interface LeaseMachineService extends IService { + + /** + * 获取商品对应的实际商品列表 + * @return + */ + PageResult getShopList(ProductPageVo productPageVo); + + + /** + * 获取店铺矿机详情 + * @return + */ + PageResult getShopMachineList(ShopMachineVo shopMachineVo); + + + /** + * 获取卖家页面矿机列表 + * @return + */ + PageResult getShopMachineListForSeller(SellerMachineVo sellerMachineVo); + + + /** + * 添加ASIC矿机 + * @return + */ + Result addAsicMachine( AsicMachineParamsVo asicMachineParamsVo); + + + /** + * 修改ASIC矿机 + * @return + */ + Result updateAsicMachine( AsicMachineParamsVo asicMachineParamsVo); + + + /** + * 修改GPU矿机 + * @return + */ + Result updateGpuMachine(List gpuMachineParamsVoList); + + /** + * 矿机上下架 + * @return + */ + Result updateMachineState(MachineStateVo machineStateVo); + + + /** + * 删除矿机 + * @return + */ + Result deleteMachine(BaseVo baseVo); + + + /** + * 获取支持的币种 + * @return + */ + Result> getSupportCoin(); + + + /** + * 获取支持的算法 + * @return + */ + Result> getSupportAlgo( String coin); +} diff --git a/src/main/java/com/m2pool/lease/service/LeaseNexaMachineAvgPowerService.java b/src/main/java/com/m2pool/lease/service/LeaseNexaMachineAvgPowerService.java new file mode 100644 index 0000000..e0c56d4 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/LeaseNexaMachineAvgPowerService.java @@ -0,0 +1,16 @@ +package com.m2pool.lease.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.m2pool.lease.entity.LeaseNexaMachineAvgPower; + +/** + *

+ * 矿池nexa机器实时平均算力 服务类 + *

+ * + * @author yyb + * @since 2025-07-29 + */ +public interface LeaseNexaMachineAvgPowerService extends IService { + +} diff --git a/src/main/java/com/m2pool/lease/service/LeaseNexaMachinePowerService.java b/src/main/java/com/m2pool/lease/service/LeaseNexaMachinePowerService.java new file mode 100644 index 0000000..e373713 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/LeaseNexaMachinePowerService.java @@ -0,0 +1,16 @@ +package com.m2pool.lease.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.m2pool.lease.entity.LeaseNexaMachinePower; + +/** + *

+ * 已售商品矿工实时算力表 服务类 + *

+ * + * @author yyb + * @since 2025-07-25 + */ +public interface LeaseNexaMachinePowerService extends IService { + +} diff --git a/src/main/java/com/m2pool/lease/service/LeaseOrderInfoService.java b/src/main/java/com/m2pool/lease/service/LeaseOrderInfoService.java new file mode 100644 index 0000000..e979ed2 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/LeaseOrderInfoService.java @@ -0,0 +1,107 @@ +package com.m2pool.lease.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.m2pool.lease.dto.*; +import com.m2pool.lease.dto.v2.*; +import com.m2pool.lease.entity.LeaseOrderInfo; +import com.m2pool.lease.vo.*; +import com.m2pool.lease.vo.v2.CoinAndAlgorithmVo; + +import java.math.BigDecimal; +import java.util.List; + +/** + *

+ * 订单表 服务类 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +public interface LeaseOrderInfoService extends IService { + /** + * 添加订单 + * @param orderAndCodeVo + * @return + */ + Result addOrders(OrderAndCodeVo orderAndCodeVo); + + + /** + * 再次购买 + * @param orderInfoVoList + * @return + */ + Result> buyAgain(List orderInfoVoList); + /** + * 根据订单状态查询订单列表(买家) + * @param orderInfoStateVo + * @return + */ + PageResult getOrdersByStatus(OrderInfoStateVo orderInfoStateVo); + + /** + * 根据订单状态查询订单列表(卖家) + * @param orderInfoStateVo + * @return + */ + PageResult getOrdersByStatusForSeller(OrderInfoStateVo orderInfoStateVo); + + /** + * 根据订单id查询订单 + * @param orderVo + * @return + */ + Result getOrdersByIds( OrderVo orderVo); + + /** + * 取消订单 + * @param orderVo + * @return + */ + Result cancelOrder(OrderVo orderVo); + + /** + * 获取用户选择的支付币种 获取实时币价 + * @param coinVo + * @return + */ + Result getCoinPrice(CoinVo coinVo); + + + /** + * 获取矿机对应的支持币种和算法 + * @return + */ + Result> getMachineSupportCoinAndAlgorithm(List machineIds); + + /** + * 获取当前矿机支持的矿池币种信息 + * @param machineIds + * @return + */ + Result> getMachineSupportPool(CoinAndAlgorithmVo coinAndAlgorithmVo); + + /** + * 添加订单-V2 + * @param orderAndCodeVo + * @return + */ + Result addOrdersV2( OrderAndCodeVo orderAndCodeVo); + + + /** + * 获取用户已购商品列表-V2 + * @param pageVo + * @return + */ + PageResult getPurchasedItems(PageVo pageVo); + + + /** + * 根据id查询用户已购商品详情-V2 + * @param baseVo + * @return + */ + Result getPurchasedInfo(BaseVo baseVo); +} diff --git a/src/main/java/com/m2pool/lease/service/LeaseOrderItemService.java b/src/main/java/com/m2pool/lease/service/LeaseOrderItemService.java new file mode 100644 index 0000000..4e213ba --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/LeaseOrderItemService.java @@ -0,0 +1,16 @@ +package com.m2pool.lease.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.m2pool.lease.entity.LeaseOrderItem; + +/** + *

+ * 订单明细表 服务类 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +public interface LeaseOrderItemService extends IService { + +} diff --git a/src/main/java/com/m2pool/lease/service/LeasePayRecordMessageInfoService.java b/src/main/java/com/m2pool/lease/service/LeasePayRecordMessageInfoService.java new file mode 100644 index 0000000..eca0502 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/LeasePayRecordMessageInfoService.java @@ -0,0 +1,16 @@ +package com.m2pool.lease.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.m2pool.lease.entity.LeasePayRecordMessage; +import com.m2pool.lease.entity.LeasePayRecordMessageInfo; + +/** + *

+ * service层支付 + *

+ * + * @author yyb + * @since 2025-09-10 + */ +public interface LeasePayRecordMessageInfoService extends IService { +} diff --git a/src/main/java/com/m2pool/lease/service/LeasePayRecordMessageService.java b/src/main/java/com/m2pool/lease/service/LeasePayRecordMessageService.java new file mode 100644 index 0000000..c752e2e --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/LeasePayRecordMessageService.java @@ -0,0 +1,15 @@ +package com.m2pool.lease.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.m2pool.lease.entity.LeasePayRecordMessage; + +/** + *

+ * service层支付 + *

+ * + * @author yyb + * @since 2025-09-10 + */ +public interface LeasePayRecordMessageService extends IService { +} diff --git a/src/main/java/com/m2pool/lease/service/LeasePaymentRecordService.java b/src/main/java/com/m2pool/lease/service/LeasePaymentRecordService.java new file mode 100644 index 0000000..22b83b0 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/LeasePaymentRecordService.java @@ -0,0 +1,66 @@ +package com.m2pool.lease.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.m2pool.lease.dto.PaymentCallbackDto; +import com.m2pool.lease.dto.PaymentRecordDto; +import com.m2pool.lease.dto.Result; +import com.m2pool.lease.entity.LeasePaymentRecord; +import com.m2pool.lease.vo.BaseVo; +import com.m2pool.lease.vo.CheckPayStatusVo; +import com.m2pool.lease.vo.OrderVo; +import org.springframework.web.bind.annotation.RequestBody; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 支付记录表 服务类 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +public interface LeasePaymentRecordService extends IService { + + + /** + * 创建支付订单 + * + * @param orderVo + * @return + */ + Result> addPayOrder(OrderVo orderVo); + + /** + * 根据订单id查询支付订单 + * + * @param orderVo + * @return + */ + Result> getPayOrderByOrderId( OrderVo orderVo); + /** + * 支付回调结果---校验支付是否成功(批量校验) + * + * @param checkPayStatusVoList + * @return + */ + Result> paymentCallbackBatch(List checkPayStatusVoList); + + + /** + * 支付回调结果----根据支付订单id校验支付是否成功(单个校验) + * @param checkPayStatusVo + * @return + */ + Result paymentCallbackByPayId(CheckPayStatusVo checkPayStatusVo); + + + + /** + * 支付回调结果-----用于定时任务 + * @param leasePaymentRecordList + * @return + */ + void paymentCallbackTask(List leasePaymentRecordList); +} diff --git a/src/main/java/com/m2pool/lease/service/LeaseProductIncomeService.java b/src/main/java/com/m2pool/lease/service/LeaseProductIncomeService.java new file mode 100644 index 0000000..6a661fb --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/LeaseProductIncomeService.java @@ -0,0 +1,16 @@ +package com.m2pool.lease.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.m2pool.lease.entity.LeaseProductIncome; + +/** + *

+ * 商品收益表 服务类 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +public interface LeaseProductIncomeService extends IService { + +} diff --git a/src/main/java/com/m2pool/lease/service/LeaseProductMachinePriceService.java b/src/main/java/com/m2pool/lease/service/LeaseProductMachinePriceService.java new file mode 100644 index 0000000..581ec30 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/LeaseProductMachinePriceService.java @@ -0,0 +1,17 @@ +package com.m2pool.lease.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.m2pool.lease.entity.LeaseProductMachinePrice; + +/** + *

+ * 商品表对应的物品机器表 服务类 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +public interface LeaseProductMachinePriceService extends IService { + + +} diff --git a/src/main/java/com/m2pool/lease/service/LeaseProductMachineService.java b/src/main/java/com/m2pool/lease/service/LeaseProductMachineService.java new file mode 100644 index 0000000..7915ca8 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/LeaseProductMachineService.java @@ -0,0 +1,77 @@ +package com.m2pool.lease.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.m2pool.lease.dto.PageResult; +import com.m2pool.lease.dto.ProductUpdateMachineDto; +import com.m2pool.lease.dto.Result; +import com.m2pool.lease.dto.UserMinerDto; +import com.m2pool.lease.entity.LeaseProductMachine; +import com.m2pool.lease.vo.*; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; + +/** + *

+ * 商品表对应的物品机器表 服务类 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +public interface LeaseProductMachineService extends IService { + + + /** + * 获取当前账户对应的挖矿账户 + * @return + */ + Result>> getUserMinersList(UserMinerVo userMinerVo); + + /** + * 获取当前账户对应的挖矿账户的机器矿工集合 + * @param userMinerVo + * @return + */ + Result> getUserMachineList(UserMinerVo userMinerVo); + + + /** + * 根据商品id查询商品矿机列表---用于卖方修改矿机信息 + * @param productForUpdateMachineVo + * @return + */ + PageResult getMachineListForUpdate(ProductForUpdateMachineVo productForUpdateMachineVo); + + /** + * 新增商品库存---实际需出售机器 + * @param productMachineParamsVoList + * @return + */ + Result addSingleOrBatchMachine(ProductMachineParamsVo productMachineParamsVoList); + + + /** + * 编辑商品 + 商品上下架---实际需出售机器 + * @param productUpdateMachineVoList + * @return + */ + Result updateMachine(List productUpdateMachineVoList); + + + /** + * 删除商品---实际需出售机器 + * @param baseVo + * @return + */ + Result deleteMachine(BaseVo baseVo); + + + /** + * 计算价格 + * @param priceCalculateVo + * @return + */ + Result calculatePrice(PriceCalculateVo priceCalculateVo); +} diff --git a/src/main/java/com/m2pool/lease/service/LeaseProductService.java b/src/main/java/com/m2pool/lease/service/LeaseProductService.java new file mode 100644 index 0000000..cee5d12 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/LeaseProductService.java @@ -0,0 +1,93 @@ +package com.m2pool.lease.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.m2pool.lease.dto.*; +import com.m2pool.lease.entity.LeaseProduct; +import com.m2pool.lease.vo.*; +import org.springframework.web.bind.annotation.RequestBody; + +import java.util.List; + +/** + *

+ * 商品表 服务类 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +public interface LeaseProductService extends IService { + /** + * 查询商品列表 + * + * @return Result> 封装后的商品 DTO 列表 + */ + PageResult getProductList(ProductPageVo productPageVo); + + + /** + * 根据商品 ID 查询商品详情 + * + * @param BaseVo 商品 ID + * @return Result 封装后的商品 DTO + */ + Result getMachineInfoById( BaseVo baseVo); + + /** + * 根据商品 ID 查询商品详情 + * + * @param productId 商品 ID + * @return Result 封装后的商品 DTO + */ + PageResult getProductMachineInfo(ProductMachineVo productMachineVo); + + + /** + * 单个商品矿机列表页面---获取支付方式 + * @return + */ + Result> getPayTypes(ShopPayTypeVo shopPayTypeVo); + /** + * 新增商品 + * + * @param productURDVo 商品 DTO + * @return Result 操作结果 + */ + Result addProduct(ProductURDVo productURDVo); + + /** + * 编辑商品 + * + * @param productURDVo 商品 DTO + * @return Result 操作结果 + */ + Result updateProduct(ProductURDVo productURDVo); + + + /** + * 删除商品 + * @param id + * @return + */ + Result deleteProduct(Long id); + + /** + * 查询用户店铺支持的支付方式 + * @return + */ + Result> getSupportPayType(); + + + /** + * 获取商品列表用于店铺钱包配置 + * @return + */ + Result> getProductListForShopWalletConfig(); + + + /** + * 修改商品列表用于店铺钱包配置 + * @return + */ + Result updateProductListForShopWalletConfig(ProductMachineForWalletConfigVo productMachineForWalletConfigVo); +} diff --git a/src/main/java/com/m2pool/lease/service/LeaseShopConfigService.java b/src/main/java/com/m2pool/lease/service/LeaseShopConfigService.java new file mode 100644 index 0000000..3ebca23 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/LeaseShopConfigService.java @@ -0,0 +1,16 @@ +package com.m2pool.lease.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.m2pool.lease.entity.LeaseShopConfig; + +/** + *

+ * 店铺商品配置表 服务类 + *

+ * + * @author yyb + * @since 2025-08-05 + */ +public interface LeaseShopConfigService extends IService { + +} diff --git a/src/main/java/com/m2pool/lease/service/LeaseShopService.java b/src/main/java/com/m2pool/lease/service/LeaseShopService.java new file mode 100644 index 0000000..e492909 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/LeaseShopService.java @@ -0,0 +1,146 @@ +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.*; +import com.m2pool.lease.vo.v2.ShopWalletInfoVo; + +import java.util.List; + +/** + *

+ * 店铺表 服务类 + *

+ * + * @author yyb + * @since 2025-08-05 + */ +public interface LeaseShopService extends IService { + + + /** + * 新增店铺 + * @param shopVo + * @return + */ + Result addShop(ShopVo shopVo); + + + /** + * 修改店铺 + * @param shopVo + * @return + */ + Result updateShop( ShopVo shopVo); + + /** + * 关闭店铺 + * @param baseVo + * @return + */ + Result closeShop( BaseVo baseVo); + + + /** + * 根据用户邮箱查询店铺 + * @return + */ + Result getShopByUserEmail(); + + /** + * 根据店铺id查询店铺 + * @param baseVo + * @return + */ + Result getShopById(BaseVo baseVo); + + + /** + * 删除店铺 + * @param baseVo + * @return + */ + Result deleteShop(BaseVo baseVo); + + + /** + * 根据店铺id 获取店铺配置信息列表 + * @param baseVo + * @return + */ + Result> getShopConfig(BaseVo baseVo); + + /** + * 根据 店铺id 获取店铺商品列表 + * @param baseVo + * @return + */ + Result> getProductListById(BaseVo baseVo); + + /** + * 新增商铺配置 + * @param shopConfigVo + * @return + */ + Result addShopConfig( ShopConfigVo shopConfigVo); + + + /** + * 修改商铺配置 + * @param shopConfigVo + * @return + */ + Result updateShopConfig( ShopConfigVo shopConfigVo); + + /** + * 删除商铺配置 + * @param baseVo + * @return + */ + Result deleteShopConfig(BaseVo baseVo); + + /** + * 获取链和链列表 + * @return + */ + Result> getChainAndList(); + + + Result> getChainAndListForSeller( BaseVo baseVo); + + + Result getChainAndCoin(BaseVo baseVo); + + + /** + * 根据店铺id 获取店铺配置信息列表--v2 新增手续费和钱包余额 + * @param baseVo + * @return + */ + Result> getShopConfigV2(BaseVo baseVo); + + + /** + * 卖家余额提现 + * @param balanceVo + * @return + */ + Result withdrawBalanceForSeller(BalanceVo balanceVo); + + + /** + * 修改商铺配置--v2 新增手续费和钱包余额 + */ + Result updateShopConfigV2(ShopWalletInfoVo shopWalletInfoVo); + + + + /** + * 获取店铺内所有钱包(包含历史钱包)提现列表 + * @return + */ + PageResult balanceWithdrawList(PageVo pageVo); +} diff --git a/src/main/java/com/m2pool/lease/service/LeaseShoppingCartInfoService.java b/src/main/java/com/m2pool/lease/service/LeaseShoppingCartInfoService.java new file mode 100644 index 0000000..0ec949e --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/LeaseShoppingCartInfoService.java @@ -0,0 +1,17 @@ +package com.m2pool.lease.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.m2pool.lease.entity.LeaseShoppingCartInfo; + +/** + *

+ * 购物车详情表 服务类 + *

+ * + * @author yyb + * @since 2025-07-24 + */ +public interface LeaseShoppingCartInfoService extends IService { + + +} diff --git a/src/main/java/com/m2pool/lease/service/LeaseShoppingCartService.java b/src/main/java/com/m2pool/lease/service/LeaseShoppingCartService.java new file mode 100644 index 0000000..6f58288 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/LeaseShoppingCartService.java @@ -0,0 +1,90 @@ +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.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; + +/** + *

+ * 购物车表 服务类 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +public interface LeaseShoppingCartService extends IService { + /** + * 添加购物车 + * @param shoppingCartInfoURDVoList + * @return + */ + Result addGoods(List shoppingCartInfoURDVoList); + + + /** + * 获取购物车列表 + * @param pageVo + * @return + */ + PageResult getGoodsList(PageVo pageVo); + + /** + * 批量删除购物车 + * @param baseVoList + * @return + */ + Result deleteBatchGoods(List baseVoList); + /** + * 删除购物车 + * @param baseVo + * @return + */ + Result deleteGoods(BaseVo baseVo); + + + /** + * 批量删除购物车中已下架商品 + * @return + */ + Result deleteBatchGoodsForIsDelete(); + + + /** + * 批量添加购物车 + * @param addGoodsVoList + * @return + */ + Result addGoodsV2(List addGoodsVoList); + + + /** + * 查询购物车中商品列表 + * @param pageVo + * @return + */ + PageResult getGoodsListV2(PageVo pageVo); + /** + * 批量删除购物车中商品 + * @param baseVoList + * @return + */ + Result deleteBatchGoodsV2(List baseVoList); + + /** + * 批量删除购物车中已下架商品 + * @return + */ + Result deleteBatchGoodsForIsDeleteV2(); +} diff --git a/src/main/java/com/m2pool/lease/service/LeaseUserOwnedProductService.java b/src/main/java/com/m2pool/lease/service/LeaseUserOwnedProductService.java new file mode 100644 index 0000000..5edea6e --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/LeaseUserOwnedProductService.java @@ -0,0 +1,49 @@ +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.UserOwnedProductDto; +import com.m2pool.lease.entity.LeaseOrderItem; +import com.m2pool.lease.entity.LeasePaymentRecord; +import com.m2pool.lease.entity.LeaseUserOwnedProduct; +import com.m2pool.lease.vo.BaseVo; +import com.m2pool.lease.vo.UserOwnedProductVo; +import org.springframework.web.bind.annotation.RequestBody; + +import java.util.List; + +/** + *

+ * 用户拥有商品详情表 服务类 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +public interface LeaseUserOwnedProductService extends IService { + + /** + * 用户查询当前自身已购商品列表 + * + * @param userOwnedProductVo + * @return + */ + PageResult getOwnedList(UserOwnedProductVo userOwnedProductVo); + + + /** + * 根据id查询当前自身已购商品详情 + * + * @param baseVo + * @return + */ + Result getOwnedById(BaseVo baseVo); + + /** + * 支付验证成功后去修改订单等信息 + * @param leaseOrderItems + * @return + */ + boolean afterPaySuccessTodoSomeEvent(List leaseOrderItems); +} diff --git a/src/main/java/com/m2pool/lease/service/LeaseUserService.java b/src/main/java/com/m2pool/lease/service/LeaseUserService.java new file mode 100644 index 0000000..c523f69 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/LeaseUserService.java @@ -0,0 +1,245 @@ +package com.m2pool.lease.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.m2pool.lease.dto.*; +import com.m2pool.lease.dto.v2.GoogleInfoDto; +import com.m2pool.lease.entity.LeaseUser; +import com.m2pool.lease.vo.*; +import com.m2pool.lease.vo.v2.CloseAccountVo; +import com.m2pool.lease.vo.v2.GoogleBindVo; +import com.m2pool.lease.vo.v2.GoogleCloseVo; +import org.springframework.web.bind.annotation.RequestBody; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import java.util.List; +import java.util.Map; + +/** + *

+ * 用户表 服务类 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +public interface LeaseUserService extends IService { + + /*---------------用户权限-----------------------*/ + + /** + * 用户登录/注册 + * @param userLoginVo + * @return + */ + Result> login(UserLoginVo userLoginVo); + + + /** + * 用户注册 + * @param userRegisterVo + * @return + */ + Result register( UserRegisterVo userRegisterVo); + + + /** + * 登录页面----忘记密码:重置密码 + * @param userLoginVo + * @return + */ + Result updatePassword(UserLoginVo userLoginVo); + + + /** + * 个人中心--修改密码 + * @param restPwdVo + * @return + */ + Result updatePasswordInCenter(RestPwdVo restPwdVo); + + /** + * 发送用户登录邮箱验证码 + * @param emailCodeVo + * @return + */ + Result sendLoginCode( EmailCodeVo emailCodeVo); + + + /** + * 获取用户注册邮箱验证码 + * @param emailCodeVo + * @return + */ + Result sendRegisterCode(EmailCodeVo emailCodeVo); + + + /** + * 获取用户修改密码邮箱验证码 + * @param emailCodeVo + * @return + */ + Result sendUpdatePwdCode(EmailCodeVo emailCodeVo); + + + /** + * 登出 + * @return + */ + Result logout(); + + + /** + * 用户注销 + * @param closeAccountVo + * @return + */ + Result closeAccount(CloseAccountVo closeAccountVo); + + + /** + * 发送用户注销邮箱验证码 + * @param emailCodeVo + * @return + */ + Result sendCloseAccount( EmailCodeVo emailCodeVo); + /*---------------用户权限-----------------------*/ + + + /*---------------谷歌双重验证-----------------------*/ + + /** + * 绑定谷歌验证码 + * @param googleBindVo + * @return + */ + Result bindGoogle( GoogleBindVo googleBindVo); + + /** + * 获取谷歌验证器安全码和验证码 + * @return + */ + Result getBindInfo(); + + /** + * 发送谷歌验证器邮箱验证码 + * @return + */ + Result sendOpenGoogleCode(); + + + /** + * 发送关闭谷歌验证 邮箱验证码 + * @return + */ + Result sendCloseGoogleCode(); + + /** + * 关闭双重验证 + * @param vo + * @return + */ + Result closeStepTwo(GoogleCloseVo vo); + + + /** + * 获取谷歌验证器状态 + * @return + */ + Result getGoogleStatus(); + + + /** + * 开启双重验证 + * @param vo + * @return + */ + Result openStepTwo(GoogleCloseVo vo); + + /*---------------谷歌双重验证-----------------------*/ + + + /*---------------用户信息-----------------------*/ + + /** + * 绑定钱包 + * @param chainAndCoinVo + * @return + */ + Result bindWallet( ChainAndCoinVo chainAndCoinVo); + + /** + * 获取用户相关钱包信息 + * @return + */ + Result> getWalletInfo(); + + + /** + * 余额提现 + * @return + */ + Result withdrawBalance(BalanceVo balanceVo); + + + /** + * 获取余额提现记录列表 + * @param balancePageVo + * @return + */ + PageResult balanceWithdrawList(BalancePageVo balancePageVo); + + /** + * 获取余额充值记录列表 + * @param balancePageVo + * @return + */ + PageResult balanceRechargeList(BalancePageVo balancePageVo); + + + /** + * 卖家:获取钱包交易(收款)记录表 + * @param balancePageVo + * @return + */ + PageResult balancePayList(BalancePageVo balancePageVo); + + /** + * 卖家:获取钱包交易(收款)记录表 + * @param recordTypePageVo + * @return + */ + PageResult transactionRecord( RecordTypePageVo recordTypePageVo); + + /** + * 最近5条交易 + * @return + */ + Result> getRecentlyTransaction(); + + + /** + * 获取手续费 + * @return + */ + Result> getCharge(); + + + /** + * 下载客户端 + * @param response + * @return + */ + void downloadClient(String userEmail,String type,HttpServletRequest request, HttpServletResponse response); + + + /** + * 获取用户信息 + * @return + */ + String getClientVersion(); + + + /*---------------用户信息-----------------------*/ +} diff --git a/src/main/java/com/m2pool/lease/service/impl/LeaseMachineConfigServiceImpl.java b/src/main/java/com/m2pool/lease/service/impl/LeaseMachineConfigServiceImpl.java new file mode 100644 index 0000000..ee81e7c --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/impl/LeaseMachineConfigServiceImpl.java @@ -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; + +/** + *

+ * GPU对应币种挖矿算力(只要gpu类型相同那么就使用这个配置) 服务实现类 + *

+ * + * @author yyb + * @since 2025-11-27 + */ +@Service +public class LeaseMachineConfigServiceImpl extends ServiceImpl implements LeaseMachineConfigService { + +} diff --git a/src/main/java/com/m2pool/lease/service/impl/LeaseMachinePriceServiceImpl.java b/src/main/java/com/m2pool/lease/service/impl/LeaseMachinePriceServiceImpl.java new file mode 100644 index 0000000..9742b81 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/impl/LeaseMachinePriceServiceImpl.java @@ -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; + +/** + *

+ * 商品表对应的物品机器表 服务实现类 + *

+ * + * @author yyb + * @since 2025-11-25 + */ +@Service +public class LeaseMachinePriceServiceImpl extends ServiceImpl implements LeaseMachinePriceService { + +} diff --git a/src/main/java/com/m2pool/lease/service/impl/LeaseMachineServiceImpl.java b/src/main/java/com/m2pool/lease/service/impl/LeaseMachineServiceImpl.java new file mode 100644 index 0000000..92c812f --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/impl/LeaseMachineServiceImpl.java @@ -0,0 +1,470 @@ +package com.m2pool.lease.service.impl; + +import com.alibaba.cloud.commons.lang.StringUtils; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +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.mapper.*; +import com.m2pool.lease.service.LeaseMachineConfigService; +import com.m2pool.lease.service.LeaseMachinePriceService; +import com.m2pool.lease.service.LeaseMachineService; +import com.m2pool.lease.utils.SecurityUtils; +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.util.*; +import java.util.stream.Collectors; + +/** + *

+ * gpu类型出售矿机表 服务实现类 + *

+ * + * @author yyb + * @since 2025-11-21 + */ +@Service +public class LeaseMachineServiceImpl extends ServiceImpl 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().eq(LeaseShop::getUserEmail, SecurityUtils.getUsername()) + .eq(LeaseShop::getDel, false)); + } + @Override + public PageResult getShopList(ProductPageVo productPageVo) { + //获取到矿机 + Set shopIds = leaseMachineMapper.getShopIds(); + if (shopIds.isEmpty()){ + //未查询到店铺 + return PageResult.success(new ArrayList<>()); + } + PageHelper.startPage(productPageVo.getPageNum(), productPageVo.getPageSize()); + List existsMachineShop = leaseMachineMapper.getExistsMachineShop(shopIds, productPageVo.getCoin(), productPageVo.getAlgorithm()); + PageInfo pageInfo = new PageInfo<>(existsMachineShop); + //店铺支付方式 + List payType = leaseShopConfigMapper.getPayType(new ArrayList<>(shopIds)); + Map> payTypeMap = payType.stream().collect(Collectors.groupingBy(PayTypeDto::getShopId)); + + PageResult 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 getShopMachineList(ShopMachineVo shopMachineVo) { + //获取店铺钱包信息 + List shopWalletInfo = leaseShopMapper.getShopWalletInfo(shopMachineVo.getShopId()); + //分页查询矿机基础信息 + PageHelper.startPage(shopMachineVo.getPageNum(), shopMachineVo.getPageSize()); + shopMachineVo = buildQuery(shopMachineVo,shopWalletInfo.get(0)); + List machineInfoDtoList = leaseMachineMapper.getShopMachineList(shopMachineVo); + PageInfo pageInfo = new PageInfo<>(machineInfoDtoList); + //获取gpu和asic对应币种算力和月收益信息 + PageResult success = setGpuAndAsicInfo(PageResult.successColumns(machineInfoDtoList),shopMachineVo); + success.setTotal(pageInfo.getTotal()); + success.setTotalPage(pageInfo.getPages()); + PageHelper.clearPage(); + return success; + } + + /** + * 设置gpu和asic 对应的收益列表信息 + * @param + */ + public PageResult setGpuAndAsicInfo(PageResult success,ShopMachineVo shopMachineVo) { + List machineInfoDtoList = success.getRows(); + List columns = success.getColumns(); + if (!machineInfoDtoList.isEmpty()){ + List machineIds = machineInfoDtoList.stream() + .map(MachineInfoDto::getId).collect(Collectors.toList()); + //获取价格列表(价格列表只与主表有关,与machine_config 表无关) + List machinePriceByMachineIds = leaseMachinePriceMapper.getMachinePriceByMachineIds(machineIds); + Map> machinePriceMap = machinePriceByMachineIds.stream() + .collect(Collectors.groupingBy(MachinePayTypeDto::getProductMachineId)); + + //动态列:获取每个矿机前十个币种算法的配置 + List gpuConfigList = leaseMachineMapper.getGpuConfigList(machineIds); + Map> collect = gpuConfigList.stream() + .collect(Collectors.groupingBy(PowerIncomeInfoDto::getMachineId)); + for (MachineInfoDto machineInfo : machineInfoDtoList) { + machineInfo.setPriceList(machinePriceMap.get(machineInfo.getId())); + List powerIncomeInfoDtoList = collect.get(machineInfo.getId()); + for (int i = 0; i < powerIncomeInfoDtoList.size(); i++) { + PowerIncomeInfoDto powerIncomeInfoDto = powerIncomeInfoDtoList.get(i); + setDynamicColumn(columns,powerIncomeInfoDto,machineInfo); + } + } + //排序分页后对象 + List sorted = sorted(machineInfoDtoList, shopMachineVo); + success.setRows(sorted); + success.setColumns(columns); + + } + return success; + } + + /** + * 设置算力动态列 + * @param columns + */ + public void setDynamicColumn(List 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() + powerIncomeInfoDto.getUnit()); + break; + case 2: + machineInfo.setC2(powerIncomeInfoDto.getPower() + powerIncomeInfoDto.getUnit()); + break; + case 3: + machineInfo.setC3(powerIncomeInfoDto.getPower() + powerIncomeInfoDto.getUnit()); + break; + case 4: + machineInfo.setC4(powerIncomeInfoDto.getPower() + powerIncomeInfoDto.getUnit()); + break; + case 5: + machineInfo.setC5(powerIncomeInfoDto.getPower() + powerIncomeInfoDto.getUnit()); + break; + case 6: + machineInfo.setC6(powerIncomeInfoDto.getPower() + powerIncomeInfoDto.getUnit()); + break; + case 7: + machineInfo.setC7(powerIncomeInfoDto.getPower() + powerIncomeInfoDto.getUnit()); + break; + case 8: + machineInfo.setC8(powerIncomeInfoDto.getPower() + powerIncomeInfoDto.getUnit()); + break; + case 9: + machineInfo.setC9(powerIncomeInfoDto.getPower() + powerIncomeInfoDto.getUnit()); + break; + case 10: + machineInfo.setC10(powerIncomeInfoDto.getPower() + powerIncomeInfoDto.getUnit()); + break; + } + } + + @Override + public PageResult getShopMachineListForSeller(SellerMachineVo sellerMachineVo) { + LeaseShop leaseShop = getShopById(); + if (leaseShop == null){ + //不存在店铺 + return PageResult.success(new ArrayList<>()); + } + PageHelper.startPage(sellerMachineVo.getPageNum(), sellerMachineVo.getPageSize()); + List machineInfoDtoList = leaseMachineMapper.getShopMachineListForSeller(sellerMachineVo,leaseShop.getId()); + PageInfo pageInfo = new PageInfo<>(machineInfoDtoList); + if (machineInfoDtoList.isEmpty()){ + //没有矿机 + return PageResult.success(new ArrayList<>()); + } + PageResult success = PageResult.success(machineInfoDtoList); + //获取矿机配置信息 + List coinAndAlgoList = leaseMachineMapper.getCoinAndAlgoList(success.getRows()); + Map> collect1 = coinAndAlgoList.stream().collect(Collectors.groupingBy(AsicCoinAndAlgoDto::getProductMachineId)); + //价格配置信息 + List machinePriceList = leaseMachineMapper.getMachinePriceList(success.getRows()); + Map> collect = machinePriceList.stream().collect(Collectors.groupingBy(MachinePayTypeDto::getProductMachineId)); + //能否上架 + Map longMachineEffectDtoMap = leaseMachineMapper.checkIsEffect(success.getRows()); + + for (SellerMachineInfoDto row : success.getRows()) { + MachineEffectDto machineEffectDto = longMachineEffectDtoMap.get(row.getId()); + row.setEffect(machineEffectDto != null ? machineEffectDto.getEffect() : 0); + 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 sorted(List machineInfoDtoList, ShopMachineVo shopMachineVo){ + + if (shopMachineVo.getPriceSort() != null) { + Comparator 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 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 addAsicMachine(AsicMachineParamsVo asicMachineParamsVo) { + if(asicMachineParamsVo.getPriceList().isEmpty()){ + return Result.fail("添加ASIC矿机失败,请先绑定收款钱包"); + } + 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()) + .canSaleNumbers(asicMachineParamsVo.getSaleNumbers()) + .build(); + int machineInsert = leaseMachineMapper.insert(machine); + + //新增lease_asic_coin_power_config 配置信息表信息 + lease_machine_price 矿机价格表信息 + if (machineInsert > 0){ + List 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 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 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 collect = asicMachineParamsVo.getPriceList().stream().map( + machinePayTypeVo -> + LeaseMachinePrice.builder() + .id(machinePayTypeVo.getPayTypeId()) + .price(machinePayTypeVo.getPrice()) + .build() + ).collect(Collectors.toList()); + boolean b = leaseMachinePriceService.updateBatchById(collect); + List 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 updateGpuMachine(List 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 矿机价格表信息 + if (machineUpdate > 0){ + List 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 updateMachineState(MachineStateVo machineStateVo) { + leaseMachineMapper.updateById(LeaseMachine.builder().id(machineStateVo.getId()).state(machineStateVo.getState()).build()); + return machineStateVo.getState() ? Result.success("下架成功") : Result.success("上架成功"); + } + + @Override + public Result deleteMachine(BaseVo baseVo) { + //TODO 删除之前需要判断是否存在订单 + leaseMachineMapper.updateById(LeaseMachine.builder().id(baseVo.getId()).del(true).build()); + //删除售价 + leaseMachinePriceMapper.update(LeaseMachinePrice.builder().del(true).build(),new LambdaQueryWrapper() + .eq(LeaseMachinePrice::getMachineId,baseVo.getId())); + //ASIC/GPU 矿机删除配置信息 + leaseMachineConfigMapper.update(LeaseMachineConfig.builder().del(true).build(), + new LambdaQueryWrapper().eq(LeaseMachineConfig::getMachineId,baseVo.getId())); + + return Result.success("删除成功"); + } + + + @Override + public Result> getSupportCoin() { + List supportCoin = leaseMachineMapper.getSupportCoin(); + return Result.success(supportCoin); + } + + @Override + public Result> getSupportAlgo(String coin) { + List supportAlgo = leaseMachineMapper.getSupportAlgo(coin); + return Result.success(supportAlgo); + } +} diff --git a/src/main/java/com/m2pool/lease/service/impl/LeaseNexaMachineAvgPowerServiceImpl.java b/src/main/java/com/m2pool/lease/service/impl/LeaseNexaMachineAvgPowerServiceImpl.java new file mode 100644 index 0000000..0052d83 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/impl/LeaseNexaMachineAvgPowerServiceImpl.java @@ -0,0 +1,20 @@ +package com.m2pool.lease.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.m2pool.lease.entity.LeaseNexaMachineAvgPower; +import com.m2pool.lease.mapper.LeaseNexaMachineAvgPowerMapper; +import com.m2pool.lease.service.LeaseNexaMachineAvgPowerService; +import org.springframework.stereotype.Service; + +/** + *

+ * 矿池nexa机器实时平均算力 服务实现类 + *

+ * + * @author yyb + * @since 2025-07-29 + */ +@Service +public class LeaseNexaMachineAvgPowerServiceImpl extends ServiceImpl implements LeaseNexaMachineAvgPowerService { + +} diff --git a/src/main/java/com/m2pool/lease/service/impl/LeaseNexaMachinePowerServiceImpl.java b/src/main/java/com/m2pool/lease/service/impl/LeaseNexaMachinePowerServiceImpl.java new file mode 100644 index 0000000..81b411a --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/impl/LeaseNexaMachinePowerServiceImpl.java @@ -0,0 +1,20 @@ +package com.m2pool.lease.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.m2pool.lease.entity.LeaseNexaMachinePower; +import com.m2pool.lease.mapper.LeaseNexaMachinePowerMapper; +import com.m2pool.lease.service.LeaseNexaMachinePowerService; +import org.springframework.stereotype.Service; + +/** + *

+ * 已售商品矿工实时算力表 服务实现类 + *

+ * + * @author yyb + * @since 2025-07-25 + */ +@Service +public class LeaseNexaMachinePowerServiceImpl extends ServiceImpl implements LeaseNexaMachinePowerService { + +} diff --git a/src/main/java/com/m2pool/lease/service/impl/LeaseOrderInfoServiceImpl.java b/src/main/java/com/m2pool/lease/service/impl/LeaseOrderInfoServiceImpl.java new file mode 100644 index 0000000..491c1a0 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/impl/LeaseOrderInfoServiceImpl.java @@ -0,0 +1,1123 @@ +package com.m2pool.lease.service.impl; + +import cn.hutool.json.JSONUtil; +import com.alibaba.cloud.commons.lang.StringUtils; +import com.baomidou.dynamic.datasource.annotation.DSTransactional; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +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.lease.constant.CoinCharge; +import com.m2pool.lease.redis.RedisCoinInfoKey; +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.message.ClientMessage; +import com.m2pool.lease.redis.service.RedisService; +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.DateUtils; +import com.m2pool.lease.utils.SecurityUtils; +import com.m2pool.lease.utils.UuidGeneratorUtil; +import com.m2pool.lease.utils.WalletRuleCheckUtils; +import com.m2pool.lease.vo.*; +import com.m2pool.lease.vo.v2.CoinAndAlgorithmVo; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static com.m2pool.lease.netty.message.MethodConstant.MINING_RAG; + +/** + *

+ * 订单表 服务实现类 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Service +public class LeaseOrderInfoServiceImpl extends ServiceImpl implements LeaseOrderInfoService { + + + @Resource + private LeaseOrderInfoMapper leaseOrderInfoMapper; + + @Resource + private LeaseProductMachineMapper leaseProductMachineMapper; + + @Resource + private LeaseOrderItemService leaseOrderItemService; + + @Resource + private LeaseOrderItemMapper leaseOrderItemMapper; + + @Resource + private LeaseShopMapper leaseShopMapper; + + @Resource + private LeasePaymentRecordMapper leasePaymentRecordMapper; + + @Resource + private LeaseShoppingCartInfoMapper leaseShoppingCartInfoMapper; + + @Resource + private LeaseProductMapper leaseProductMapper; + + @Resource + private LeaseProductService leaseProductService; + + @Resource + private LeaseUserWalletDataMapper leaseUserWalletDataMapper; + + @Resource + private LeaseUserOwnedProductService leaseUserOwnedProductService; + + @Resource + private LeaseUserMapper leaseUserMapper; + + @Resource + private LeaseProductMachinePriceMapper leaseProductMachinePriceMapper; + + @Resource + private LeaseOrderMiningMapper leaseOrderMiningMapper; + + @Resource + private RedisService redisService; + + + + @Override + @DSTransactional + public Result addOrders(OrderAndCodeVo orderAndCodeVo ) { + String userEmail = SecurityUtils.getUsername(); + 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("谷歌验证码错误"); + //} + + List orderInfoVoList = orderAndCodeVo.getOrderInfoVoList(); + + //存储相同链和币种的map集合 + Map>> chainAndCoinMap = new HashMap<>(); + Map orderTotalPriceGroupByChainAndCoin = leaseProductMachinePriceMapper.getOrderTotalPriceGroupByChainAndCoin(orderInfoVoList); + //chain + coin 钱包 对应的总价 + Map totalPriceMap = new HashMap<>(); + //存储根据chain和coin去重后的set集合 + Set chainAndCoinSet = new HashSet<>(); + Set chainAndCoinAndShopIdSet = new HashSet<>(); + //其他 + Map machineMap = new HashMap<>(); + LocalDateTime now = LocalDateTime.now(); + List machineIds = new ArrayList<>(); + Set productIds = 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> innerMap = chainAndCoinMap.get(chain); + innerMap.computeIfAbsent(coin, k -> new ArrayList<>()).add(vo); + // 获取对应买方本次链和币对应需要支付的总金额 + totalPriceMap.compute(key, (k, v) -> { + if (v == null) { + v = BigDecimal.ZERO; + } + LeaseProductMachinePrice 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()); + productIds.add(vo.getProductId()); + } + //获取买家需要进行支付的几个钱包 + List walletDataList = leaseUserWalletDataMapper.selectWalletByChainAndCoinAndUsername(chainAndCoinSet, SecurityUtils.getUserId()); + if (walletDataList.size() != chainAndCoinSet.size()){ + return Result.fail("下单失败!订单选择的支付方式中对应钱包您还未绑定并充值!"); + } + + Map fromAddressMap = walletDataList.stream().collect(Collectors.toMap(item -> item.getFromChain() + "-" + item.getFromSymbol(), Function.identity())); + //封装订单主表信息 + List 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() + .eq(LeaseShoppingCartInfo::getUserId, SecurityUtils.getUsername()) + .in(LeaseShoppingCartInfo::getProductMachineId, machineIds)); + if (delete < 1){ + throw new OrderException("生成订单失败,购物车中不存在矿机商品"); + } + //查找矿机详情信息 + List machinesList = leaseProductMachineMapper.selectBatchIds(machineIds); + for (LeaseProductMachine leaseProductMachine : machinesList) { + if (leaseProductMachine.getState() == 1 || leaseProductMachine.getDel()){ + throw new OrderException("生成订单失败,订单矿机中存在已下架矿机"); + } + } + // 获取卖家店铺钱包配置 + List payAddressAndPayCoin = leaseShopMapper.getPayAddressAndPayCoin(chainAndCoinAndShopIdSet); + Map configMap = new HashMap<>(); + Map> 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 orderInfoMap = leaseOrderInfoList.stream() + .collect(Collectors.toMap(LeaseOrderInfo::getChainAndCoinAndShopIdKey, Function.identity())); + + //订单详情表业务 + List leaseProducts = leaseProductMapper.selectBatchIds(productIds); + Map productMap = leaseProducts.stream().collect(Collectors.toMap(LeaseProduct::getId, Function.identity())); + List leaseOrderItems = new ArrayList<>(); + + for (LeaseProductMachine leaseProductMachine : machinesList) { + Long machineId = leaseProductMachine.getId(); + OrderInfoVo orderInfoVo = machineMap.get(machineId); + String chain = orderInfoVo.getChain(); + String coin = orderInfoVo.getCoin(); + Long shopId = orderInfoVo.getShopId(); + String key = chain + "-" + coin + "-" + shopId; + LeaseProductMachinePrice leaseProductMachinePrice = 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); + //设置销售数量 + LeaseProduct leaseProduct = productMap.get(leaseProductMachine.getProductId()); + leaseProduct.setSaleNumber(leaseProduct.getSaleNumber() + 1); + leaseOrderItems.add( LeaseOrderItem.builder() + .userId(userEmail) + .orderId(leaseOrderInfo.getId()) + .productId(leaseProductMachine.getProductId()) + .productMachineId(leaseProductMachine.getId()) + .price(leaseProductMachinePrice.getPrice()) + .user(leaseProductMachine.getUser()) + .miner(leaseProductMachine.getMiner()) + .theoryIncome(leaseProductMachine.getTheoryIncome().multiply(BigDecimal.valueOf(orderInfoVo.getLeaseTime()))) + .coin(leaseProduct.getCoin()) + .leaseTime(orderInfoVo.getLeaseTime()) + .payCoin(leaseShopConfig.getPayCoin()) + .address(leaseShopConfig.getPayAddress()) + .chain(leaseShopConfig.getChain()) + .symbol(leaseShopConfig.getPayCoin()) + .name(leaseProduct.getName()) + .image(leaseProduct.getImage()) + .shopId(leaseProductMachine.getShopId()) + .fromAddress(walletInfo.getFromAddress()) + .fromChain(walletInfo.getFromChain()) + .fromSymbol(walletInfo.getFromSymbol()) + .type(leaseProduct.getType() == 1) + .build()); + } + + boolean a = leaseProductService.updateBatchById(leaseProducts); + boolean b = leaseOrderItemService.saveBatch(leaseOrderItems); + //新增上述矿机到用户已购机器表中 + boolean c = leaseUserOwnedProductService.afterPaySuccessTodoSomeEvent(leaseOrderItems); + + //开始生成支付订单并返回 + if (a && b && c){ + checkBalanceAndSetBlockBalance(fromAddressMap,leaseOrderInfoList); + //修改商品矿机售出状态 以商品机器sale_state作为乐观锁 + int i = leaseProductMachineMapper.updateLockState(machineIds); + if (i != machineIds.size()){ + throw new OrderException("订单中已有商品售出,请刷新购物车删除已售出商品,重新结算生成订单"); + } + return Result.success("订单生成成功"); + } + throw new OrderException("订单生成失败"); + } + + + /** + * 检测钱包余额是否充足 余额充足则将余额冻结 + * @return + */ + public void checkBalanceAndSetBlockBalance(Map fromAddressMap,List leaseOrderInfoList){ + Map> collect = leaseOrderInfoList.stream().collect(Collectors.groupingBy(LeaseOrderInfo::getChainAndCoinKey)); + + fromAddressMap.forEach((chainAndCoinKey, leaseUserWalletData) -> { + List leaseOrderInfos = collect.get(chainAndCoinKey); + BigDecimal totalAmount = leaseOrderInfos.stream().map(LeaseOrderInfo::getTotalPrice).reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal totalFee = leaseOrderInfos.stream().map(LeaseOrderInfo::getFee).reduce(BigDecimal.ZERO, BigDecimal::add); + + if (leaseUserWalletData.getBalance().subtract(leaseUserWalletData.getBlockedBalance()).compareTo(totalAmount.add(totalFee)) < 0){ + BigDecimal lackAmount = totalAmount.add(totalFee) + .subtract(leaseUserWalletData.getBalance().subtract(leaseUserWalletData.getBlockedBalance())) + .stripTrailingZeros(); + throw new OrderException("下单失败,买家"+chainAndCoinKey+"钱包余额不足,缺少" + lackAmount + "满足支付需求"); + } + leaseUserWalletData.setBlockedBalance(leaseUserWalletData.getBlockedBalance().add(totalAmount).add(totalFee)); + leaseUserWalletDataMapper.updateById(leaseUserWalletData); + }); + + } + + ///** + // * 获取本次订单需总支付金额 + // * + // * @param leaseOrderItems + // * @return + // */ + //public BigDecimal createPaymentOrders(List leaseOrderItems,Map shopConfigMap,LocalDateTime now,LeaseOrderInfo build){ + // //按店铺id分组 + // Map> shopMap = leaseOrderItems.stream().collect(Collectors.groupingBy(LeaseOrderItem::getShopId)); + // List paymentRecordDTOs = new ArrayList<>(); + // List paymentRecords = shopMap.values().stream() + // .map(items -> { + // LeaseOrderItem firstItem = items.get(0); + // LeaseShopConfig leaseShopConfig = shopConfigMap.get(firstItem.getShopId()); + // BigDecimal totalAmount = BigDecimal.ZERO; + // for (LeaseOrderItem item : items) { + // totalAmount = totalAmount.add(item.getPrice().multiply(BigDecimal.valueOf(item.getLeaseTime()))); + // } + // Long orderId = build.getId(); + // Long productId = firstItem.getProductId(); + // String payCoin = firstItem.getPayCoin(); + // String address = firstItem.getAddress(); + // + // + // paymentRecordDTOs.add(PaymentRecordDto.builder() + // .payCoin(payCoin) + // .amount(totalAmount) + // .payAddress(address) + // .img(leaseShopConfig.getQrcode()) + // .build()); + // + // return LeasePaymentRecord.builder() + // .orderId(orderId) + // .shopId(firstItem.getShopId()) + // .productId(productId) + // .payCoin(payCoin) + // .amount(totalAmount) + // .payAddress(address) + // .qrcode(leaseShopConfig.getQrcode()) + // .createTime(now) + // .build(); + // }) + // .collect(Collectors.toList()); + // + // + // //4.保存支付订单 + // //boolean saved = leasePaymentRecordService.saveBatch(paymentRecords); + // //List rabbitmqOrderMessageList = paymentRecords.stream() + // // .map(paymentRecord -> RabbitmqOrderMessage.builder().orderId(paymentRecord.getOrderId()).shopId(paymentRecord.getShopId()).build()) + // // .collect(Collectors.toList()); + // // + // ////5.发送订单mq超时消息 + // //rabbitTemplate.convertAndSend( + // // ORDER_OVERTIME_EXCHANGE_NAME, + // // ORDER_OVERTIME_ROUTING_KEY, + // // rabbitmqOrderMessageList); + // + // + // + // + // //返回总支付金额 + // return paymentRecordDTOs.stream().map(PaymentRecordDto::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add); + // + // //throw new PaymentException("添加支付订单失败"); + //} + + + @Override + public Result> buyAgain(List orderInfoVoList) { + //LocalDateTime now = LocalDateTime.now(); + //List machineIds = new ArrayList<>(); + //Set shopIds = new HashSet<>(); + //Set productIds = new HashSet<>(); + // + //for (OrderInfoVo vo : orderInfoVoList) { + // machineIds.add(vo.getMachineId()); + // shopIds.add(vo.getShopId()); + // productIds.add(vo.getProductId()); + //} + //List machinesList = leaseProductMachineMapper.selectBatchIds(machineIds); + //BigDecimal totalPrice = calculateOrderPrice(orderInfoVoList, machinesList); + ////获取商铺配置信息(支付地址,支付币种) 目前配置里面的支付地址 改为从leaseShopAddressConfig 获取 + //List payAddressAndPayCoin = leaseShopMapper.getPayAddressAndPayCoin(shopIds); + //Map shopConfigMap = payAddressAndPayCoin.stream().collect(Collectors.toMap(LeaseShopConfig::getShopId, Function.identity())); + //// 这里修改店铺配置的地址为我们自营的地址 + //updatePayAddressForSelf(shopConfigMap); + ////为商品机器加锁 以商品机器sale_state作为乐观锁 + //int i = leaseProductMachineMapper.updateLockState(machineIds); + //if (i != machineIds.size()){ + // throw new ProductSoldOutException("订单中已有商品售出,不能再次购买"); + //} + ////订单主信息新增 + //String orderNumber = UuidGeneratorUtil.generateUuidWithoutHyphen(); + //LeaseOrderInfo build = LeaseOrderInfo.builder() + // .orderNumber(orderNumber) + // .userId(SecurityUtils.getUsername()) + // .totalPrice(totalPrice) + // .createTime(now) + // .build(); + //boolean save = save(build); + //if (!save){ + // return Result.fail("添加订单失败"); + //} + // + ////创建订单明细 + //Map machineMap = orderInfoVoList.stream().collect(Collectors.toMap(OrderInfoVo::getMachineId, Function.identity())); + //List leaseProducts = leaseProductMapper.selectBatchIds(productIds); + //Map productMap = leaseProducts.stream().collect(Collectors.toMap(LeaseProduct::getId, Function.identity())); + //List leaseOrderItems = new ArrayList<>(); + //for (LeaseProductMachine leaseProductMachine : machinesList) { + // LeaseShopConfig leaseShopConfig = shopConfigMap.get(leaseProductMachine.getShopId()); + // OrderInfoVo orderInfoVo = machineMap.get(leaseProductMachine.getId()); + // LeaseProduct leaseProduct = productMap.get(leaseProductMachine.getProductId()); + // leaseOrderItems.add( LeaseOrderItem.builder() + // .orderId(build.getId()) + // .productId(leaseProductMachine.getProductId()) + // .productMachineId(leaseProductMachine.getId()) + // .price(leaseProductMachine.getPrice()) + // .user(leaseProductMachine.getUser()) + // .miner(leaseProductMachine.getMiner()) + // .theoryIncome(leaseProductMachine.getTheoryIncome().multiply(BigDecimal.valueOf(orderInfoVo.getLeaseTime()))) + // .coin(leaseProduct.getCoin()) + // .leaseTime(orderInfoVo.getLeaseTime()) + // .payCoin(leaseShopConfig.getPayCoin()) + // .address(leaseShopConfig.getPayAddress()) + // .name(leaseProduct.getName()) + // .image(leaseProduct.getImage()) + // .shopId(leaseProductMachine.getShopId()) + // .build()); + //} + //boolean b = leaseOrderItemService.saveBatch(leaseOrderItems); + ////开始生成支付订单并返回 + //if (b){ + // return Result.success(createPaymentOrders(leaseOrderItems, shopConfigMap, now, build)); + //} + //return Result.fail("生成支付订单失败"); + return null; + } + + @Override + public PageResult getOrdersByStatus(OrderInfoStateVo orderInfoStateVo) { + PageHelper.startPage(orderInfoStateVo.getPageNum(), orderInfoStateVo.getPageSize()); + + //获取订单信息 + List ordersByStatus = leaseOrderInfoMapper.getOrdersByStatus(SecurityUtils.getUserId(),orderInfoStateVo.getStatus()); + PageInfo pageInfo = new PageInfo<>(ordersByStatus); + List ids = ordersByStatus.stream().map(OrderInfoDto::getId).collect(Collectors.toList()); + if (ids.isEmpty()){ + //不存在订单 + return PageResult.success(new ArrayList<>()); + } + //获取订单对应的订单详情列表 + List leaseOrderItems = leaseOrderItemMapper.selectList(new QueryWrapper() + .in("order_id",ids)); + + //设置订单详情集合,已支付金额,待支付金额 + setPayAmountAndItems(leaseOrderItems,ordersByStatus,ids); + + PageResult success = PageResult.success(ordersByStatus); + success.setTotal(pageInfo.getTotal()); + success.setTotalPage(pageInfo.getPages()); + PageHelper.clearPage(); + return success; + } + + + + @Override + public PageResult getOrdersByStatusForSeller(OrderInfoStateVo orderInfoStateVo) { + LeaseShop leaseShop = leaseShopMapper.selectOne(new LambdaQueryWrapper() + .eq(LeaseShop::getUserEmail, SecurityUtils.getUsername()).eq(LeaseShop::getDel,false)); + + if (leaseShop == null){ + //不存在商铺 + return PageResult.success(new ArrayList<>()); + } + + //查询到商铺的所有订单 + List orderItemList = leaseOrderItemMapper.selectList(new LambdaQueryWrapper() + .eq(LeaseOrderItem::getShopId, leaseShop.getId())); + if (orderItemList.isEmpty()){ + //不存在订单 + return PageResult.success(new ArrayList<>()); + } + List orderInfoIds = orderItemList.stream().map(LeaseOrderItem::getOrderId).distinct().collect(Collectors.toList()); + PageHelper.startPage(orderInfoStateVo.getPageNum(), orderInfoStateVo.getPageSize()); + //获取订单信息 + List ordersByStatus = leaseOrderInfoMapper.getOrdersByStatusForSeller(orderInfoStateVo.getStatus(), orderInfoIds); + PageInfo pageInfo = new PageInfo<>(ordersByStatus); + setPayAmountAndItems(orderItemList,ordersByStatus,orderInfoIds); + PageResult success = PageResult.success(ordersByStatus); + success.setTotal(pageInfo.getTotal()); + success.setTotalPage(pageInfo.getPages()); + PageHelper.clearPage(); + return success; + + } + + /** + * 遍历订单详情集合。统计 已支付金额,待支付金额 + * @param leaseOrderItems + * @param ordersByStatus + * @param ids + */ + public void setPayAmountAndItems(List leaseOrderItems,List ordersByStatus,List ids){ + Map hasPayAmountMap = new HashMap<>(); + Map hasPayRealAmountMap = new HashMap<>(); + Map> orderItemDtoMap = new HashMap<>(); + for (LeaseOrderItem leaseOrderItem : leaseOrderItems) { + orderItemDtoMap.computeIfAbsent(leaseOrderItem.getOrderId(), k -> new ArrayList<>()).add( OrderItemDto.builder() + .shopId(leaseOrderItem.getShopId()) + .orderId(leaseOrderItem.getOrderId()) + .productId(leaseOrderItem.getProductId()) + .productMachineId(leaseOrderItem.getProductMachineId()) + .leaseTime(leaseOrderItem.getLeaseTime()) + .address(leaseOrderItem.getAddress()) + .payCoin(leaseOrderItem.getPayCoin()) + .name(leaseOrderItem.getName()) + .price(leaseOrderItem.getPrice()) + .image(leaseOrderItem.getImage()) + .numbers(leaseOrderItem.getNumbers()) + .type(leaseOrderItem.getType() ? 1 : 0) + .build()); + // 累加支付金额 + hasPayAmountMap.merge(leaseOrderItem.getOrderId(), leaseOrderItem.getAlreadyPayAmount(), BigDecimal::add); + // 累加实际支付金额 + hasPayRealAmountMap.merge(leaseOrderItem.getOrderId(), leaseOrderItem.getAlreadyPayRealAmount(), BigDecimal::add); + } + + + ordersByStatus.forEach(orderInfoDto -> { + List orderItems = orderItemDtoMap.get(orderInfoDto.getId()); + BigDecimal hasPayAmount = hasPayAmountMap.get(orderInfoDto.getId()); + BigDecimal hasPayRealAmount = hasPayRealAmountMap.get(orderInfoDto.getId()); + orderInfoDto.setOrderItemDtoList(orderItems); + orderInfoDto.setNoPayAmount(orderInfoDto.getTotalPrice().subtract(hasPayAmount).doubleValue()); + orderInfoDto.setPayAmount(hasPayRealAmount.doubleValue()+"/"+hasPayAmount.doubleValue()); + }); + } + + @Override + public Result getOrdersByIds(OrderVo orderVo) { + + //获取订单主信息 + LeaseOrderInfo leaseOrderInfo = leaseOrderInfoMapper.selectById(orderVo.getOrderId()); + if (leaseOrderInfo == null){ + //"未找到该订单" + return Result.success(null); + } + //获取订单对应的订单详情列表 + List leaseOrderItems = leaseOrderItemMapper.selectList(new LambdaQueryWrapper() + .eq(LeaseOrderItem::getOrderId, orderVo.getOrderId())); + + List collect1 = leaseOrderItems.stream().map(leaseOrderItem -> OrderItemDto.builder() + .orderId(leaseOrderItem.getOrderId()) + .productId(leaseOrderItem.getProductId()) + .productMachineId(leaseOrderItem.getProductMachineId()) + .leaseTime(leaseOrderItem.getLeaseTime()) + .address(leaseOrderItem.getAddress()) + .payCoin(leaseOrderItem.getPayCoin()) + .name(leaseOrderItem.getName()) + .image(leaseOrderItem.getImage()) + .price(leaseOrderItem.getPrice()) + .type(leaseOrderItem.getType() ? 1 : 0) + .build()).collect(Collectors.toList()); + + OrderInfoDto orderInfoDto = OrderInfoDto.builder() + .id(leaseOrderInfo.getId()) + .shopName(leaseOrderInfo.getShopName()) + .orderNumber(leaseOrderInfo.getOrderNumber()) + .userId(leaseOrderInfo.getUserId()) + .totalPrice(leaseOrderInfo.getTotalPrice()) + .status(leaseOrderInfo.getStatus()) + .createTime(leaseOrderInfo.getCreateTime()) + .endTime(leaseOrderInfo.getUpdateTime()) + .orderItemDtoList(collect1) + .build(); + + return Result.success(orderInfoDto); + } + + + @Override + public Result cancelOrder(OrderVo orderVo) { + int i = leaseOrderInfoMapper.updateById(LeaseOrderInfo.builder().id(orderVo.getOrderId()).status(2).build()); + if (i > 0){ + //删除支付订单 + int update = leasePaymentRecordMapper.update(LeasePaymentRecord.builder().del(false).build(), new LambdaQueryWrapper().eq(LeasePaymentRecord::getOrderId, orderVo.getOrderId())); + return Result.success("取消订单成功"); + } + return Result.fail("取消订单失败"); + } + + + @Override + public Result getCoinPrice(CoinVo coinVo) { + BigDecimal coinPrice = getCoinPrice(coinVo.getCoin()); + return Result.success(coinPrice); + } + public BigDecimal getCoinPrice(String coin) { + coin = coin.toLowerCase(); + String priceKey = RedisCoinInfoKey.getPiceKey(coin); + if (priceKey == null){ + return BigDecimal.ZERO; + } + BigDecimal price = redisService.getCacheBigDecimal(priceKey); + if (price != null){ + return price; + } + price = leaseOrderInfoMapper.getCoinPrice(coin); + //从数据库中获取最后一次存储的币价(可能不够实时) + if (price == null){ + return BigDecimal.ZERO; + } + return price; + } + + @Resource + private LeaseMachinePriceMapper leaseMachinePriceMapper; + + @Resource + private LeaseMachineMapper leaseMachineMapper; + + @Resource + private LeaseMachineConfigMapper leaseMachineConfigMapper; + + + @Override + public Result> getMachineSupportCoinAndAlgorithm(List machineIds) { + //lease_pool_coin_config 中获取本系统支持的币种和算法 + List machineSupportCoinAndAlgorithm = leaseMachineConfigMapper.getMachineSupportCoinAndAlgorithm(machineIds); + // 按币种分组 + Map> coinGroup = machineSupportCoinAndAlgorithm.stream() + .collect(Collectors.groupingBy(CoinAndAlgorithmDto::getCoin)); + List result = new ArrayList<>(); + // 遍历每个币种 + coinGroup.forEach((coin, dtos) -> { + CoinAndAlgorithmListDto item = new CoinAndAlgorithmListDto(); + item.setCoin(coin); + // 转换算法列表 + List algorithmList = dtos.stream() + .map(dto -> { + CoinAndAlgorithmListDto.AlgorithmListDto algoDto = new CoinAndAlgorithmListDto.AlgorithmListDto(); + algoDto.setAlgorithm(dto.getAlgorithm()); + + // 转换机器ID列表 + List 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> getMachineSupportPool(CoinAndAlgorithmVo coinAndAlgorithmVo) { + List coinConfigList = leaseMachineConfigMapper.getConfigList(coinAndAlgorithmVo.getCoin(), coinAndAlgorithmVo.getAlgorithm()); + // 将查询结果按poolId分组并转换为MiningInfoDto列表 + List miningInfoList = coinConfigList.stream() + .collect(Collectors.groupingBy(MiningConfigSelectDto::getPoolId)) + .entrySet().stream() + .map(entry -> { + MiningConfigSelectDto firstConfig = entry.getValue().get(0); + + List 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 addOrdersV2(OrderAndCodeVo orderAndCodeVo) { + String userEmail = SecurityUtils.getUsername(); + + List orderInfoVoList = orderAndCodeVo.getOrderInfoVoList(); + List orderMiningInfoDtoList = orderAndCodeVo.getOrderMiningInfoDtoList(); + + String errors = checkMiningMinersExistAndAddress(orderMiningInfoDtoList); + if (StringUtils.isNotEmpty(errors)){ + return Result.fail(errors); + } + Map userAndMinerMap = orderMiningInfoDtoList.stream().collect(Collectors.toMap(OrderMiningInfoDto::getMachineId, Function.identity())); + 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 collect1 = orderInfoVoList.stream() + .collect(Collectors.toMap(OrderInfoVo::getMachineId,Function.identity())); + List machineList = leaseMachineMapper.selectBatchIds(collect1.keySet()); + Map> machineTypeMap = machineList.stream().collect(Collectors.groupingBy(LeaseMachine::getType)); + List gpuMachines = machineTypeMap.get(true) == null ? new ArrayList<>() : machineTypeMap.get(true); + List 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); + //TODO 开发环境 + //if (!isGpuPass){ + // return Result.fail("购买的GPU商品中,存在客户端不在线的矿机!"); + //} + //存储相同链和币种的map集合 + Map>> chainAndCoinMap = new HashMap<>(); + Map orderTotalPriceGroupByChainAndCoin = leaseMachinePriceMapper.getOrderTotalPriceGroupByChainAndCoin(orderInfoVoList); + //chain + coin 钱包 对应的总价 + Map totalPriceMap = new HashMap<>(); + //存储根据chain和coin去重后的set集合 + Set chainAndCoinSet = new HashSet<>(); + Set chainAndCoinAndShopIdSet = new HashSet<>(); + //其他 + Map machineMap = new HashMap<>(); + LocalDateTime now = LocalDateTime.now(); + long startTime = System.currentTimeMillis(); + List machineIds = new ArrayList<>(); + Set 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> 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.getNumbers())).multiply(BigDecimal.valueOf(vo.getLeaseTime()))); + } + return v; + }); + machineMap.put(vo.getMachineId(), vo); + machineIds.add(vo.getMachineId()); + shopIds.add(vo.getShopId()); + } + //店铺名 + Map shopNameMapByIds = leaseShopMapper.getShopNameMapByIds(new ArrayList<>(shopIds)); + //获取买家需要进行支付的几个钱包 + List walletDataList = leaseUserWalletDataMapper.selectWalletByChainAndCoinAndUsername(chainAndCoinSet, SecurityUtils.getUserId()); + if (walletDataList.size() != chainAndCoinSet.size()){ + return Result.fail("下单失败!订单选择的支付方式中对应钱包您还未绑定并充值!"); + } + + Map fromAddressMap = walletDataList.stream().collect(Collectors.toMap(item -> item.getFromChain() + "-" + item.getFromSymbol(), Function.identity())); + //封装订单主表信息 + List 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) + .authId(SecurityUtils.getUserId()) + .shopName(shopNameMapByIds.get(Long.parseLong(split[2])).getName()) + .fee(fee) + .totalPrice(totalPrice) + .createTime(now) + .build()); + }); + + //删除购物车中矿机 + int delete = leaseShoppingCartInfoMapper.delete(new LambdaUpdateWrapper() + .eq(LeaseShoppingCartInfo::getUserId, userEmail) + .eq(LeaseShoppingCartInfo::getVersion, 1) + .in(LeaseShoppingCartInfo::getProductMachineId, machineIds)); + if (delete < 1){ + throw new OrderException("生成订单失败,购物车中不存在矿机商品"); + } + //查找矿机详情信息 + List machinesList = leaseMachineMapper.selectBatchIds(machineIds); + List collect = machinesList.stream().filter(leaseMachine -> leaseMachine.getState() || leaseMachine.getDel()).collect(Collectors.toList()); + if (!collect.isEmpty()){ + throw new OrderException("生成订单失败,订单矿机中存在已下架矿机"); + } + // 获取卖家店铺钱包配置 + List payAddressAndPayCoin = leaseShopMapper.getPayAddressAndPayCoin(chainAndCoinAndShopIdSet); + Map configMap = new HashMap<>(); + Map> 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 orderInfoMap = leaseOrderInfoList.stream() + .collect(Collectors.toMap(LeaseOrderInfo::getChainAndCoinAndShopIdKey, Function.identity())); + + //订单详情表业务 + List shopList = leaseShopMapper.selectBatchIds(shopIds); + Map shopMap = shopList.stream().collect(Collectors.toMap(LeaseShop::getId, Function.identity())); + + //获取理论算力 + List theoryHashRate1 = leaseMachineConfigMapper.getTheoryHashRate(orderMiningInfoDtoList); + Map> theoryHashRateMap = theoryHashRate1.stream() + .collect(Collectors.groupingBy(MachineHashRateMapDto::getMachineId)); + + Map endMiningMap = new HashMap<>(); + Map machineOrderIdMap = new HashMap<>(); + Map machineOrderItemIdMap = new HashMap<>(); + for (LeaseMachine leaseMachine : machinesList) { + Long machineId = leaseMachine.getId(); + OrderInfoVo orderInfoVo = machineMap.get(machineId); + List machineHashRateMapDtos = theoryHashRateMap.get(machineId); + BigDecimal theoryHashRate = machineHashRateMapDtos.isEmpty() ? BigDecimal.ZERO : machineHashRateMapDtos.get(0).getHashRate(); + String chain = orderInfoVo.getChain(); + String coin = orderInfoVo.getCoin(); + Long shopId = orderInfoVo.getShopId(); + 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()); + + LeaseOrderItem build = LeaseOrderItem.builder() + .userId(userEmail) + .orderId(leaseOrderInfo.getId()) + .productMachineId(machineId) + .price(leaseMachinePrice.getPrice()) + .user(userAndMinerMap.get(machineId).getPoolUser()) + .miner(userAndMinerMap.get(machineId).getWorkerId()) + //.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()) + .type(leaseMachine.getType()) + .theoryPower(theoryHashRate) + .build(); + Long timestamp = startTime + orderInfoVo.getLeaseTime() * 24 * 60 * 60 * 1000L; + endMiningMap.put(machineId,timestamp); + machineOrderIdMap.put(machineId,leaseOrderInfo.getId()); + + leaseOrderItemService.save(build); + + //获取itemId + machineOrderItemIdMap.put(machineId,build.getId()); + } + //开始生成支付订单并返回 + checkBalanceAndSetBlockBalance(fromAddressMap,leaseOrderInfoList); + int i = 0; + //修改GPU 矿机售出状态为 1 已售出 + if (!gpuMachines.isEmpty()){ + int gpuUpdate = leaseMachineMapper.updateLockState(gpuMachines); + i += gpuUpdate; + } + //修改ASIC 矿机出售数量 + if (!asicMachines.isEmpty()){ + int asicUpdate = leaseMachineMapper.updateLockNumbers(asicMachines); + i += asicUpdate; + } + if (i != machineIds.size()){ + throw new OrderException("订单中已有商品售出,请刷新购物车删除已售出商品,重新结算生成订单"); + } + //TODO 开发环境 不添加 + //if(!checkGpuMachine(gpuMachines)){ + // throw new OrderException("购买的GPU商品中,存在客户端不在线的矿机!"); + //} + //发送消息 + sendMessageToClientAndInsertPurchasedMachine(SecurityUtils.getUserId(),userEmail,gpuMachines,orderMiningInfoDtoList,endMiningMap,machineOrderIdMap,machineOrderItemIdMap); + return Result.success("订单生成成功"); + } + + /** + * 检测 gpu客户端是否在线 + * @param gpuMachines 购买的GPU矿机信息 + */ + private boolean checkGpuMachine(List gpuMachines){ + for (LeaseMachine gpuMachine : gpuMachines) { + if (ChannelManager.getChannelById(gpuMachine.getHostMac()) == null){ + return false; + } + } + return true; + } + + /** + * 校验 ASIC 矿机可售数量是否充足 + * @param asicMachines 购买的ASIC矿机信息 + */ + private boolean checkAsicMachine(List asicMachines,Map numbers){ + for (LeaseMachine leaseMachine : asicMachines) { + Integer number = numbers.get(leaseMachine.getId()).getNumbers(); + //已售出数量 + Integer lockNumbers = leaseMachine.getSaleOutNumbers(); + //总可售出数量 + Integer saleNumbers = leaseMachine.getSaleNumbers(); + + //能售出数量 + Integer canSaleNumbers = leaseMachine.getCanSaleNumbers(); + + if (canSaleNumbers < number){ + return false; + } + //乐观锁 + leaseMachine.setLockNumbers(lockNumbers); + //已售出数量 + leaseMachine.setSaleOutNumbers(lockNumbers + number); + //能够售出数量 + leaseMachine.setCanSaleNumbers(saleNumbers - leaseMachine.getSaleOutNumbers()); + } + return true; + } + + + /** + * 检测本次输入的钱包对应矿工号,是否已经存在并在挖矿中 + * @param orderMiningInfoDtoList + */ + private String checkMiningMinersExistAndAddress(List orderMiningInfoDtoList){ + List orderMiningInfoDtos = leaseOrderMiningMapper.checkMiningMinersExist(orderMiningInfoDtoList); + + StringBuilder errors = new StringBuilder(); + String addressVerify = ""; + List addressInfos = new ArrayList(); + if(!orderMiningInfoDtos.isEmpty()){ + Map> collect = orderMiningInfoDtos.stream().collect(Collectors.groupingBy(OrderMiningInfoDto::getWalletAddress)); + collect.forEach((address,list)->{ + errors.append("钱包地址: ").append(address).append("-已存在挖矿中矿工号"); + for (OrderMiningInfoDto orderMiningInfoDto : list) { + errors.append(orderMiningInfoDto.getWorkerId()).append(" "); + } + errors.append("请重新输入矿工号\n"); + addressInfos.add(list.get(0)); + }); + } + + if (StringUtils.isNotEmpty(errors.toString())){ + return errors.toString(); + } + for (OrderMiningInfoDto addressInfo : addressInfos) { + if (!WalletRuleCheckUtils.checkAddress(addressInfo.getCoin(),addressInfo.getWalletAddress())){ + addressVerify = addressInfo.getWalletAddress() + "钱包地址格式错误"; + break; + } + } + return addressVerify; + } + + /** + * GPU矿机的通过mac发送给相应的客户端 + */ + public void sendMessageToClientAndInsertPurchasedMachine(Long authId,String userEmail, List gpuMachines, List orderMiningInfo, Map endMiningMap, Map machineOrderIdMap,Map machineOrderItemIdMap){ + + List configIds = orderMiningInfo.stream() + .map(OrderMiningInfoDto::getCoinConfigId).distinct().collect(Collectors.toList()); + Map coinAndAlgoMap = leaseMachineMapper.getCoinAndAlgoById(configIds); + Map idAndMacMap = gpuMachines.stream().collect(Collectors + .toMap(LeaseMachine::getId, Function.identity())); + Map> macMessageMap = new HashMap<>(); + List orderMiningList = new ArrayList<>(); + + for (OrderMiningInfoDto orderMiningInfoDto : orderMiningInfo) { + MiningConfigDto miningConfigDto = coinAndAlgoMap.get(orderMiningInfoDto.getCoinConfigId()); + Long machineId = orderMiningInfoDto.getMachineId(); + LeaseMachine leaseMachine = idAndMacMap.get(machineId); + Long orderId = machineOrderIdMap.get(machineId); + Long endTime = endMiningMap.get(machineId); + Long orderItemId = machineOrderItemIdMap.get(machineId); + String poolName = orderMiningInfoDto.getPoolName(); + String walletAddress = orderMiningInfoDto.getWalletAddress(); + String algo = miningConfigDto.getAlgo(); + String coin = miningConfigDto.getCoin(); + String poolUrl = miningConfigDto.getPool(); + String poolUser = orderMiningInfoDto.getPoolUser(); + String poolWorkerId = orderMiningInfoDto.getWorkerId(); + //这里因为orderMiningInfo 存在ASIC 的信息可能为空 + if (leaseMachine != null){ + String mac = leaseMachine.getHostMac(); + ClientMessage message = new ClientMessage<>(); + MiningConfigDto build = MiningConfigDto.builder() + .algo(algo) + .coin(coin) + .pool(poolName) + .pool_url(poolUrl) + .wallet_mining(orderMiningInfoDto.getWalletMining()) + .wallet_address(walletAddress) + .pool_user(poolUser) + .worker_id(poolWorkerId) + .end_timestamp(endTime / 1000).build(); + message.setId(mac); + message.setMethod(MINING_RAG); + message.setParams( build); + //TCP 消息 + macMessageMap.put(mac,message); + } + LeaseOrderMining build = LeaseOrderMining.builder() + .orderId(orderId) + .orderItemId(orderItemId) + .machineId(machineId) + .userId(userEmail) + .authId(authId) + .coin(coin) + .algorithm(algo) + .poolUrl(poolUrl) + .pool(poolName) + .poolUser(poolUser) + .workerId(poolWorkerId) + .walletAddress(walletAddress) + .startTime(LocalDateTime.now()) + .endTime(DateUtils.toLocalDateTime(new Date(endTime))) + .build(); + //已购商品信息 + orderMiningList.add(build); + long timeout = ChronoUnit.HOURS.between(build.getStartTime(), build.getEndTime().plusHours(1)); + redisService.setCacheObject7(combinationKey(poolName,walletAddress,coin,algo),"1",timeout, TimeUnit.HOURS); + } + leaseOrderMiningMapper.insertBatch(orderMiningList); + macMessageMap.forEach(this::sendMessage); + + } + + public String combinationKey(String pool,String walletAddress,String coin,String algo){ + return pool +":"+ walletAddress +":"+ coin +":"+ algo; + } + + /** + * 发送矿机配置信息给矿机客户端 + * @param mac 矿机mac + * @param message 消息内容 + */ + public void sendMessage(String mac,ClientMessage message){ + ChannelManager.sendToClient(mac,message); + } + + + + + @Override + public PageResult getPurchasedItems(PageVo pageVo) { + Long authId = SecurityUtils.getUserId(); + PageHelper.startPage(pageVo.getPageNum(), pageVo.getPageSize()); + + List purchasedItems = leaseOrderMiningMapper.getPurchasedItems(authId); + //获取订单信息 + PageInfo pageInfo = new PageInfo<>(purchasedItems); + PageResult success = PageResult.success(purchasedItems); + success.setTotal(pageInfo.getTotal()); + success.setTotalPage(pageInfo.getPages()); + PageHelper.clearPage(); + return success; + } + + @Override + @DSTransactional + public Result getPurchasedInfo(BaseVo baseVo) { + //找到miner 和钱包 ,pool 矿池名 + MiningConfigInfoDto info = leaseOrderMiningMapper.getPurchasedInfo(baseVo.getId()); + List list = leaseOrderMiningMapper.getPurchasedMachineInfo(info); + + + //根据钱包 + 币种 + 矿工号 查询 pool 库 pool.kryptex 表 中对应 算力 + Map recentlyHashRateMap = leaseOrderMiningMapper + .getRecentlyHashrate(info.getPool(), info.getWalletAddress(), info.getCoin(), list); + for (PurchasedMachineDto purchasedMachineDto : list) { + MiningHashrateInfoDto miningHashrateInfoDto = recentlyHashRateMap.get(purchasedMachineDto.getWorkerId()); + if (miningHashrateInfoDto != null){ + purchasedMachineDto.setRecordTime(miningHashrateInfoDto.getRecordTime()); + purchasedMachineDto.setPower(miningHashrateInfoDto.getPower()); + } + } + + info.setPurchasedMachines(list); + return Result.success(info); + } +} diff --git a/src/main/java/com/m2pool/lease/service/impl/LeaseOrderItemServiceImpl.java b/src/main/java/com/m2pool/lease/service/impl/LeaseOrderItemServiceImpl.java new file mode 100644 index 0000000..8a30225 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/impl/LeaseOrderItemServiceImpl.java @@ -0,0 +1,20 @@ +package com.m2pool.lease.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.m2pool.lease.entity.LeaseOrderItem; +import com.m2pool.lease.mapper.LeaseOrderItemMapper; +import com.m2pool.lease.service.LeaseOrderItemService; +import org.springframework.stereotype.Service; + +/** + *

+ * 订单明细表 服务实现类 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Service +public class LeaseOrderItemServiceImpl extends ServiceImpl implements LeaseOrderItemService { + +} diff --git a/src/main/java/com/m2pool/lease/service/impl/LeasePayRecordMessageInfoServiceImpl.java b/src/main/java/com/m2pool/lease/service/impl/LeasePayRecordMessageInfoServiceImpl.java new file mode 100644 index 0000000..c38b506 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/impl/LeasePayRecordMessageInfoServiceImpl.java @@ -0,0 +1,22 @@ +package com.m2pool.lease.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.m2pool.lease.entity.LeasePayRecordMessage; +import com.m2pool.lease.entity.LeasePayRecordMessageInfo; +import com.m2pool.lease.mapper.LeasePayRecordMessageInfoMapper; +import com.m2pool.lease.mapper.LeasePayRecordMessageMapper; +import com.m2pool.lease.service.LeasePayRecordMessageInfoService; +import com.m2pool.lease.service.LeasePayRecordMessageService; +import org.springframework.stereotype.Service; + +/** + *

+ * service层支付 + *

+ * + * @author yyb + * @since 2025-09-10 + */ +@Service +public class LeasePayRecordMessageInfoServiceImpl extends ServiceImpl implements LeasePayRecordMessageInfoService { +} diff --git a/src/main/java/com/m2pool/lease/service/impl/LeasePayRecordMessageServiceImpl.java b/src/main/java/com/m2pool/lease/service/impl/LeasePayRecordMessageServiceImpl.java new file mode 100644 index 0000000..d1e6f97 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/impl/LeasePayRecordMessageServiceImpl.java @@ -0,0 +1,19 @@ +package com.m2pool.lease.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.m2pool.lease.entity.LeasePayRecordMessage; +import com.m2pool.lease.mapper.LeasePayRecordMessageMapper; +import com.m2pool.lease.service.LeasePayRecordMessageService; +import org.springframework.stereotype.Service; + +/** + *

+ * service层支付 + *

+ * + * @author yyb + * @since 2025-09-10 + */ +@Service +public class LeasePayRecordMessageServiceImpl extends ServiceImpl implements LeasePayRecordMessageService { +} diff --git a/src/main/java/com/m2pool/lease/service/impl/LeasePaymentRecordServiceImpl.java b/src/main/java/com/m2pool/lease/service/impl/LeasePaymentRecordServiceImpl.java new file mode 100644 index 0000000..ae7fb42 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/impl/LeasePaymentRecordServiceImpl.java @@ -0,0 +1,497 @@ +package com.m2pool.lease.service.impl; + +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.m2pool.lease.constant.PaymentStatus; +import com.m2pool.lease.dto.PaymentCallbackDto; +import com.m2pool.lease.dto.PaymentRecordDto; +import com.m2pool.lease.mq.message.RabbitmqPoolProxyMessage; +import com.m2pool.lease.dto.Result; +import com.m2pool.lease.entity.LeaseOrderInfo; +import com.m2pool.lease.entity.LeaseOrderItem; +import com.m2pool.lease.entity.LeasePaymentRecord; +import com.m2pool.lease.exception.PaymentException; +import com.m2pool.lease.mapper.LeaseOrderInfoMapper; +import com.m2pool.lease.mapper.LeaseOrderItemMapper; +import com.m2pool.lease.mapper.LeasePaymentRecordMapper; +import com.m2pool.lease.service.LeasePaymentRecordService; +import com.m2pool.lease.service.LeaseUserOwnedProductService; +import com.m2pool.lease.utils.HttpPayUtils; +import com.m2pool.lease.vo.CheckPayStatusVo; +import com.m2pool.lease.vo.OrderVo; +import com.m2pool.lease.vo.PayVo; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +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.ZoneId; +import java.util.*; +import java.util.stream.Collectors; + +import static com.m2pool.lease.constant.OrderStatus.FULLY_PAID; +import static com.m2pool.lease.constant.OrderStatus.PARTIALLY_PAID; +import static com.m2pool.lease.constant.PaymentStatus.*; +import static com.m2pool.lease.constant.RabbitmqConstant.POOL_PROXY_QUEUE_NAME; +import static com.m2pool.lease.utils.HttpPayUtils.PAY_ETH; + + +/** + *

+ * 支付记录表 服务实现类 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Service +public class LeasePaymentRecordServiceImpl extends ServiceImpl implements LeasePaymentRecordService { + + + + @Resource + private RabbitTemplate rabbitTemplate; + + @Resource + private LeaseOrderItemMapper leaseOrderItemMapper; + + @Resource + private LeasePaymentRecordMapper leasePaymentRecordMapper; + + @Resource + private ThreadPoolTaskExecutor customThreadPool; + + @Resource + private LeaseOrderInfoMapper leaseOrderInfoMapper; + + @Resource + private LeaseUserOwnedProductService leaseUserOwnedProductService; + + @Override + @Transactional + public Result> addPayOrder(OrderVo orderVo) { + //1.根据订单id查询订单详情信息 + List leaseOrderItems = leaseOrderItemMapper.selectList(new LambdaQueryWrapper() + .eq(LeaseOrderItem::getOrderId, orderVo.getOrderId())); + + //2.按商品ID分组,并生成支付订单列表 + Map> productItemsMap = leaseOrderItems.stream() + .collect(Collectors.groupingBy(LeaseOrderItem::getProductId)); + //3.生成支付订单列表 + 封装返回对象 + List paymentRecordDTOs = new ArrayList<>(); + + List paymentRecords = productItemsMap.values().stream() + .map(items -> { + LeaseOrderItem firstItem = items.get(0); + BigDecimal totalAmount = items.stream() + .map(LeaseOrderItem::getPrice) + .reduce(BigDecimal.ZERO, BigDecimal::add); + Long orderId = orderVo.getOrderId(); + Long productId = firstItem.getProductId(); + String payCoin = firstItem.getPayCoin(); + String address = firstItem.getAddress(); + paymentRecordDTOs.add(PaymentRecordDto.builder() + .payCoin(payCoin) + .amount(totalAmount) + .payAddress(address) + .build()); + + return LeasePaymentRecord.builder() + .orderId(orderId) + .productId(productId) + .payCoin(payCoin) + .amount(totalAmount) + .payAddress(address) + .build(); + }) + .collect(Collectors.toList()); + + //4.保存支付订单 + boolean saved = saveBatch(paymentRecords); + if (saved) { + return Result.success(paymentRecordDTOs); + } else { + return Result.fail("生成支付订单失败"); + } + } + + @Override + public Result> getPayOrderByOrderId(OrderVo orderVo) { + List leasePaymentRecords = leasePaymentRecordMapper.selectList(new LambdaQueryWrapper() + .eq(LeasePaymentRecord::getOrderId, orderVo.getOrderId()) + .eq(LeasePaymentRecord::getDel, false)); + List collect = leasePaymentRecords.stream() + .map(record -> PaymentRecordDto.builder() + .id(record.getId()) + .payCoin(record.getPayCoin()) + .amount(record.getAmount()) + .payAddress(record.getPayAddress()) + .status(record.getStatus()) + .img(record.getQrcode()) + .build()).collect(Collectors.toList()); + return Result.success(collect); + } + + + @Override + @Transactional + public Result> paymentCallbackBatch(List checkPayStatusVoList) { + List paymentCallbackDTOs = new ArrayList<>(); + //1.查询处于待支付中状态(2)的订单 + List payIds = checkPayStatusVoList.stream().map(CheckPayStatusVo::getId).collect(Collectors.toList()); + List leasePaymentRecords = leasePaymentRecordMapper.selectList(new LambdaQueryWrapper() + .in(LeasePaymentRecord::getId, payIds) + .eq(LeasePaymentRecord::getDel, true) + .nested(wrapper -> wrapper + .eq(LeasePaymentRecord::getStatus, 2) + .or() + .eq(LeasePaymentRecord::getStatus, 10))); + + List orderIds = leasePaymentRecords.stream().map(LeasePaymentRecord::getOrderId).collect(Collectors.toList()); + //2.根据订单id查询订单详情信息 + List leaseOrderItems = leaseOrderItemMapper.selectList(new LambdaQueryWrapper() + .in(LeaseOrderItem::getOrderId, orderIds)); + //3.按商店id分组 + Map> productItemsMap = leaseOrderItems.stream() + .collect(Collectors.groupingBy(LeaseOrderItem::getShopId)); + + //记录是否所有都支付成功 + int isAll = 0; + //4.按店铺分组---->合并店铺下所有支付订单----> 按所有总交易金额 去填充这些订单---->填充慢则表示这一笔订单支付成功----> 发货 + for (LeasePaymentRecord record : leasePaymentRecords) { + //4.1 调用三方api校验支付是否成功 + PaymentCallbackDto paymentCallbackDto = paymentCallback(record); + + if (paymentCallbackDto.getIsPayAll() == 1){ + //4.2 修改商品售出状态 + 新增机器到已购表中 (为了保证成功需要通过消息中间件达成最终一致性 demo暂时不修改) + List leaseOrderItemList = productItemsMap.get(record.getShopId()); + boolean isSuccess = leaseUserOwnedProductService.afterPaySuccessTodoSomeEvent(leaseOrderItemList); + if (!isSuccess){ + return Result.fail("支付校验成功√-->货款已付清√-->发货失败×。请提交工单发送订单给客服或者通过聊天系统联系客服解决"); + } + record.setStatus(1); + int i = leasePaymentRecordMapper.updateById(record); + isAll += i; + //4.3 发送rabbitmq消息给矿池代理 + sendRabbitmqMessage(leaseOrderItemList); + }else if (paymentCallbackDto.getIsPayAll() == 10){ + //支付了部分货款改状态---但是不发货 + leasePaymentRecordMapper.updateById(LeasePaymentRecord.builder().id(record.getId()).status(10).build()); + } + paymentCallbackDto.setTotalAmount(record.getAmount()); + paymentCallbackDTOs.add(paymentCallbackDto); + } + //5.修改订单状态为 1全部已支付 10部分已支付 + int status = isAll == leasePaymentRecords.size() ? 1 : 10; + Long orderId = leasePaymentRecords.get(0).getOrderId(); + int i = leaseOrderInfoMapper.updateById(LeaseOrderInfo.builder().id(orderId).status(status).build()); + if (i > 0){ + return Result.success(paymentCallbackDTOs); + }else { + return Result.fail("校验失败"); + } + } + + + @Override + @Transactional + public Result paymentCallbackByPayId(CheckPayStatusVo baseVo) { + LeasePaymentRecord record = leasePaymentRecordMapper.selectById(baseVo.getId()); + record.setFrom(baseVo.getForm()); + record.setHash(baseVo.getHash()); + //支付状态为2 待支付 或者 10 支付成功--已支付部分货款 两种状态可校验 + if (record.getStatus() == 10 || record.getStatus() == 2){ + //1.根据订单id + 店铺id 查询订单详情信息 (支付订单按shop分的,不再按商品分) + List leaseOrderItems = leaseOrderItemMapper.selectList(new LambdaQueryWrapper() + .eq(LeaseOrderItem::getOrderId, record.getOrderId()).eq(LeaseOrderItem::getShopId, record.getShopId())); + + //2.调用三方api校验支付是否成功 + PaymentCallbackDto paymentCallbackDto = paymentCallback(record); + if (paymentCallbackDto.getIsPayAll() == 1){ + boolean isSuccess = leaseUserOwnedProductService.afterPaySuccessTodoSomeEvent(leaseOrderItems); + if (!isSuccess){ + return Result.fail("支付校验成功√-->货款已付清√-->发货失败×。请提交工单发送订单给客服或者通过聊天系统联系客服解决"); + } + record.setStatus(1); + leasePaymentRecordMapper.updateById(record); + //2.1 发送rabbitmq消息给矿池代理 + sendRabbitmqMessage(leaseOrderItems); + } else if (paymentCallbackDto.getIsPayAll() == 10){ + //2.2 支付了部分货款改状态---但是不发货 + leasePaymentRecordMapper.updateById(LeasePaymentRecord.builder().id(record.getId()).status(10).build()); + } + //3.查询订单是否下所有支付订单都已支付完成 + Long l = leasePaymentRecordMapper.selectCount(new LambdaQueryWrapper() + .eq(LeasePaymentRecord::getOrderId, record.getOrderId()) + .eq(LeasePaymentRecord::getDel, true) + .ne(LeasePaymentRecord::getStatus,1)); + //4.修改订单为全部已支付 + if (l == 0){ + leaseOrderInfoMapper.updateById(LeaseOrderInfo.builder().id(record.getOrderId()).status(1).build()); + } + return Result.success(paymentCallbackDto); + } + return Result.fail("验证失败,该订单状态不为支付成功未验证状态"); + } + + @Override + @Transactional + public void paymentCallbackTask(List leasePaymentRecordList) { + List orderIds = leasePaymentRecordList.stream().map(LeasePaymentRecord::getOrderId).distinct().collect(Collectors.toList()); + //1---根据订单id查询订单详情信息 + List leaseOrderItems = leaseOrderItemMapper.selectList(new LambdaQueryWrapper() + .in(LeaseOrderItem::getOrderId, orderIds)); + //2---按商店id - 订单id 分组 + Map> productItemsMap = leaseOrderItems.stream() + .collect(Collectors.groupingBy( + item -> item.getShopId() + "-" + item.getOrderId() + )); + //3---支付订单按支付地址分组 校验 + Map> shopMaps = leasePaymentRecordList.stream().collect(Collectors.groupingBy(LeasePaymentRecord::getPayAddress)); + + List afterCheckPaymentList = shopMaps.entrySet().stream() + .map(entry -> { + String key = entry.getKey(); + List values = entry.getValue(); + // 查找已经支付的金额 + BigDecimal paySuccessAmount = values.stream().map(LeasePaymentRecord::getPayAmount).reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal alreadyPayAmount = getAlreadyPayAmount(key, values).subtract(paySuccessAmount); + + // 在迭代过程中修改列表,使用 fori 循环避免 ConcurrentModificationException + for (int i = 0; i < values.size(); i++) { + LeasePaymentRecord leasePaymentRecord = values.get(i); + BigDecimal needPayAmount = leasePaymentRecord.getAmount(); + BigDecimal payAmount = leasePaymentRecord.getPayAmount(); + // 如果该支付订单已支付了部分金额 剩下需支付需减去该值 + if (payAmount.compareTo(BigDecimal.ZERO) != 0) { + needPayAmount = needPayAmount.subtract(payAmount); + } + // 已支付金额 不等于 需支付总金额 + if (payAmount.compareTo(needPayAmount) != 0) { + if (needPayAmount.compareTo(alreadyPayAmount) < 0) { + // 条件1: 交易记录金额大于需要支付的金额------> 该订单状态可改为 1全部已支付 + alreadyPayAmount = alreadyPayAmount.subtract(needPayAmount); + leasePaymentRecord.setStatus(PAYMENT_SUCCESS_FULL.getCode()); + leasePaymentRecord.setPayAmount(leasePaymentRecord.getAmount()); + } else if (needPayAmount.compareTo(alreadyPayAmount) > 0 && alreadyPayAmount.compareTo(BigDecimal.ZERO) != 0) { + // 条件2: 交易记录金额小于需要支付的金额 ,但是交易记录金额不为0 ------> 该订单可改为状态 10部分已支付 + alreadyPayAmount = BigDecimal.ZERO; + leasePaymentRecord.setStatus(PAYMENT_SUCCESS_PARTIAL.getCode()); + leasePaymentRecord.setPayAmount(alreadyPayAmount); + } else if (PAYMENT_SUCCESS_PARTIAL.getCode() == leasePaymentRecord.getStatus()) { + // 条件3: 走到这里说明,交易金额已经分配完毕。 如果本身为部分已支付,直接去除 + values.remove(i); + i--; + } + } else { + // 已支付金额 等于 需支付总金额 直接剔除 + values.remove(i); + i--; + } + } + return values; + }) + .flatMap(List::stream) // 展开嵌套的列表 + .collect(Collectors.toList()); + + + List rabbitmqMessages = new ArrayList<>(); + //4---更新支付状态变化的支付订单 + 支付订单为完成支付的 订单开始 发货 + for (LeasePaymentRecord record : afterCheckPaymentList) { + //4.1全部支付成功的才能发货 + if (PaymentStatus.PAYMENT_SUCCESS_FULL.getCode() == record.getStatus()){ + String key = record.getShopId() + "-" + record.getOrderId(); + List leaseOrderItemList = productItemsMap.get(key); + leaseUserOwnedProductService.afterPaySuccessTodoSomeEvent(leaseOrderItemList); + rabbitmqMessages.addAll(leaseOrderItemList); + } + //4.2 修改支付订单的状态 + leasePaymentRecordMapper.updateById(record); + } + + + + //5---发送rabbitmq消息给矿池代理 + sendRabbitmqMessage(rabbitmqMessages); + + //6---修改普通订单状态 + //统计每个普通订单对应的支付订单数量 + Map orderIdCountMap = leasePaymentRecordList.stream() + .collect(Collectors.groupingBy(LeasePaymentRecord::getOrderId, Collectors.counting())); + // 统计每个订单对应的支付已全部完成的数量 + Map fullPaidCountMap = leasePaymentRecordList.stream() + .filter(record -> record.getStatus() == PAYMENT_SUCCESS_FULL.getCode()) + .collect(Collectors.groupingBy(LeasePaymentRecord::getOrderId, Collectors.counting())); + // 统计每个订单对应的部分已完成的数量 + Map partialPaidCountMap = leasePaymentRecordList.stream() + .filter(record -> record.getStatus() == PAYMENT_SUCCESS_PARTIAL.getCode()) + .collect(Collectors.groupingBy(LeasePaymentRecord::getOrderId, Collectors.counting())); + + // 遍历 orderIdCountMap,更新订单状态 + for (Map.Entry entry : orderIdCountMap.entrySet()) { + Long orderId = entry.getKey(); + Long totalCount = entry.getValue(); + Long fullPaidCount = fullPaidCountMap.getOrDefault(orderId, 0L); + Long partialPaidCount = partialPaidCountMap.getOrDefault(orderId, 0L); + if (Objects.equals(fullPaidCount, totalCount)) { + // 全部已完成 + leaseOrderInfoMapper.updateById(LeaseOrderInfo.builder().id(orderId).status(FULLY_PAID.getCode()).build()); + //TODO 长连接联系前端支付已完成 + } else if (partialPaidCount > 0) { + // 部分已完成 + leaseOrderInfoMapper.updateById(LeaseOrderInfo.builder().id(orderId).status(PARTIALLY_PAID.getCode()).build()); + } + } + + + + } + + + /** + * 获取当前用户已支付的金额 + * @param leasePaymentRecordList + * @return + */ + private BigDecimal getAlreadyPayAmount(String payAddress,List leasePaymentRecordList) { + LeasePaymentRecord leasePaymentRecord = leasePaymentRecordList.get(0); + PayVo build = PayVo.builder() + .ts(leasePaymentRecord.getCreateTime().atZone(ZoneId.systemDefault()).toInstant().getEpochSecond()) + .address(payAddress) + .build(); + HttpPayUtils.PaymentDto paymentDto = HttpPayUtils.doPost(PAY_ETH, JSONUtil.toJsonStr(build)); + + BigDecimal totalAmount = BigDecimal.ZERO; + List paymentData = handlerResponse(paymentDto); + for (HttpPayUtils.PaymentData result : paymentData){ + String valueStr = result.getValue(); + BigDecimal divisor = BigDecimal.TEN.pow(6); + BigDecimal resultAmount = new BigDecimal(valueStr).divide(divisor,6, RoundingMode.HALF_UP); + totalAmount = totalAmount.add(resultAmount); + } + + return totalAmount; + } + + + /** + * 调用三方api 进行支付校验 + * @param record + * @return + */ + private PaymentCallbackDto paymentCallback(LeasePaymentRecord record) { + PayVo build = PayVo.builder() + .ts(record.getCreateTime().atZone(ZoneId.systemDefault()).toInstant().getEpochSecond()) + .coin(record.getPayCoin()) + .amount(record.getAmount()) + .address(record.getPayAddress()) + .build(); + HttpPayUtils.PaymentDto paymentDto = HttpPayUtils.doPost(PAY_ETH, JSONUtil.toJsonStr(build)); + + List paymentData = handlerResponse(paymentDto); + if (paymentData != null && !paymentData.isEmpty()){ + return payIsSuccess(record, paymentData); + }else{ + throw new PaymentException("验证失败,卖方不存在转账记录"); + } + } + + /** + * 校验代码 + * @param record + * @param paymentData + * @return + */ + private PaymentCallbackDto payIsSuccess(LeasePaymentRecord record,List paymentData){ + //待支付状态为2 + int isPayAll = 2; + BigDecimal noPayAmount = record.getAmount(); + for (HttpPayUtils.PaymentData result : paymentData){ + String valueStr = result.getValue(); + BigDecimal divisor = BigDecimal.TEN.pow(6); + BigDecimal resultAmount = new BigDecimal(valueStr).divide(divisor,6, RoundingMode.HALF_UP); + long timeStamp = Long.parseLong(result.getTimeStamp()); + long ts = record.getCreateTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + //交易hash 和 买家钱包地址 有一个校验通过即可(只适用于自营校验) + if (timeStamp >= ts && (result.getFrom().equals(record.getFrom()) || result.getHash().equals(record.getHash()))){ + noPayAmount = noPayAmount.subtract(resultAmount); + //全部货款已支付 + if (noPayAmount.compareTo(BigDecimal.ZERO) == 0){ + isPayAll = 1; + break; + } + } + + + } + //已支付部分货款 + if (noPayAmount.compareTo(record.getAmount()) != 0 && noPayAmount.compareTo(BigDecimal.ZERO) != 0){ + isPayAll = 10; + } + + return PaymentCallbackDto.builder().isPayAll(isPayAll).noPayAmount(noPayAmount).build(); + } + + /** + * 三方api调用异常 + * @param paymentDto + * @return + */ + private List handlerResponse(HttpPayUtils.PaymentDto paymentDto){ + if (paymentDto != null){ + if (paymentDto.isResult() && paymentDto.getCode() == 0) { + // 支付成功,处理返回的交易数据 + List transactions = paymentDto.getData(); + return transactions; + } else { + // 支付失败,根据 code 处理不同错误 + switch (paymentDto.getCode()) { + case -1: + throw new PaymentException("未转账"); + case -2: + throw new PaymentException("coin参数不支持"); + default: + throw new PaymentException("支付未知错误"); + } + } + }else { + return null; + } + + } + + /** + * 发送rabbitmq消息给矿机代理 + * @param leaseOrderItems + */ + public void sendRabbitmqMessage( List leaseOrderItems) { + leaseOrderItems.forEach(item -> { + customThreadPool.execute(()->{ + RabbitmqPoolProxyMessage rabbitmqPoolProxyMessage = RabbitmqPoolProxyMessage.builder() + .MethodID(0) + .Msg("") + .ID(item.getUser() + "-" + item.getMiner()) + .Address(item.getAddress()) + .build(); + try { + rabbitTemplate.convertAndSend(POOL_PROXY_QUEUE_NAME, rabbitmqPoolProxyMessage); + } catch (Exception e) { + System.err.println("发送失败: " + e.getMessage()); + } + }); + }); + + } + + + + + + + + +} diff --git a/src/main/java/com/m2pool/lease/service/impl/LeaseProductIncomeServiceImpl.java b/src/main/java/com/m2pool/lease/service/impl/LeaseProductIncomeServiceImpl.java new file mode 100644 index 0000000..ebb6c95 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/impl/LeaseProductIncomeServiceImpl.java @@ -0,0 +1,20 @@ +package com.m2pool.lease.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.m2pool.lease.entity.LeaseProductIncome; +import com.m2pool.lease.mapper.LeaseProductIncomeMapper; +import com.m2pool.lease.service.LeaseProductIncomeService; +import org.springframework.stereotype.Service; + +/** + *

+ * 商品收益表 服务实现类 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Service +public class LeaseProductIncomeServiceImpl extends ServiceImpl implements LeaseProductIncomeService { + +} diff --git a/src/main/java/com/m2pool/lease/service/impl/LeaseProductMachinePriceServiceImpl.java b/src/main/java/com/m2pool/lease/service/impl/LeaseProductMachinePriceServiceImpl.java new file mode 100644 index 0000000..e778d16 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/impl/LeaseProductMachinePriceServiceImpl.java @@ -0,0 +1,21 @@ +package com.m2pool.lease.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.m2pool.lease.entity.LeaseProductMachinePrice; +import com.m2pool.lease.mapper.LeaseProductMachinePriceMapper; +import com.m2pool.lease.service.LeaseProductMachinePriceService; +import org.springframework.stereotype.Service; + +/** + *

+ * 商品表对应的物品机器表 服务实现类 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Service +public class LeaseProductMachinePriceServiceImpl extends ServiceImpl implements LeaseProductMachinePriceService { + + +} diff --git a/src/main/java/com/m2pool/lease/service/impl/LeaseProductMachineServiceImpl.java b/src/main/java/com/m2pool/lease/service/impl/LeaseProductMachineServiceImpl.java new file mode 100644 index 0000000..9ca781a --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/impl/LeaseProductMachineServiceImpl.java @@ -0,0 +1,360 @@ +package com.m2pool.lease.service.impl; + +import com.baomidou.dynamic.datasource.annotation.DSTransactional; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import com.m2pool.lease.constant.BlockInterval; +import com.m2pool.lease.redis.RedisCoinInfoKey; +import com.m2pool.lease.dto.*; +import com.m2pool.lease.entity.LeaseProduct; +import com.m2pool.lease.entity.LeaseProductMachine; +import com.m2pool.lease.entity.LeaseProductMachinePrice; +import com.m2pool.lease.mapper.LeaseProductMachineMapper; +import com.m2pool.lease.mapper.LeaseProductMapper; +import com.m2pool.lease.redis.service.RedisService; +import com.m2pool.lease.service.LeaseProductMachinePriceService; +import com.m2pool.lease.service.LeaseProductMachineService; +import com.m2pool.lease.utils.PowerUnitUtils; +import com.m2pool.lease.utils.SecurityUtils; +import com.m2pool.lease.vo.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +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.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +/** + *

+ * 商品表对应的物品机器表 服务实现类 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Service +public class LeaseProductMachineServiceImpl extends ServiceImpl implements LeaseProductMachineService { + + @Resource + private LeaseProductMachineMapper leaseProductMachineMapper; + @Resource + private LeaseProductMapper leaseProductMapper; + @Resource + private RedisService redisService; + @Resource + private LeaseProductMachinePriceService leaseProductMachinePriceService; + @Autowired + private ApplicationContext applicationContext; + @Override + public Result> > getUserMinersList(UserMinerVo userMinerVo) { + String userId = SecurityUtils.getUsername(); + + //开发环境 + //userId = "Eudora.law@outlook.com"; + List userMinersList = leaseProductMachineMapper.getUserMinersList(userId,userMinerVo.getCoin()); + Map> collect = userMinersList.stream().collect(Collectors.groupingBy(UserMinerDto::getCoin)); + return Result.success(collect); + } + + @Override + public Result> getUserMachineList(UserMinerVo userMinerVo) { + //获取1天内的存在算力的机器(这里user 和coin 组成了表的唯一键,不用担心查询到其他用户相同的user和coin的矿机) + List minerMachines = leaseProductMachineMapper.getMinerMachines(userMinerVo.getUser(), userMinerVo.getCoin()); + //根据矿工账号查询已经新增到商品机器表中的机器 + List minerMachinesIsSale = leaseProductMachineMapper.getMinerMachinesIsSale(userMinerVo.getUser(), userMinerVo.getCoin()); + // 过滤 minerMachines 集合,只保留 miner 字段值不在 existingMiners 中的元素 + minerMachines = minerMachines.stream() + .filter(dto -> !minerMachinesIsSale.contains(dto.getMiner())) + .peek(dto -> { + dto.setRealPower(dto.getRealPower().divide(BigDecimal.valueOf(24 * 60 * 60),2, RoundingMode.HALF_UP)); + dto.setRealPower(dto.getRealPower().setScale(2, RoundingMode.HALF_UP)); + }) + .collect(Collectors.toList()); + return Result.success(minerMachines); + } + + @Override + public PageResult getMachineListForUpdate(ProductForUpdateMachineVo productForUpdateMachineVo) { + PageHelper.startPage(productForUpdateMachineVo.getPageNum(), productForUpdateMachineVo.getPageSize()); + List leaseProductMachines = leaseProductMachineMapper.selectList(new LambdaQueryWrapper() + .eq(LeaseProductMachine::getProductId, productForUpdateMachineVo.getId()).eq(LeaseProductMachine::getDel,false)); + PageInfo pageInfo = new PageInfo<>(leaseProductMachines); + if (leaseProductMachines.isEmpty()){ + //不存在租售的矿机 + return PageResult.success(new ArrayList<>()); + } + //获取矿机对应的币价 + List machineIds = leaseProductMachines.stream().map(LeaseProductMachine::getId).collect(Collectors.toList()); + List priceList = leaseProductMachineMapper.getPriceList(machineIds); + Map> collect = priceList.stream().collect(Collectors.groupingBy(MachinePayTypeDto::getProductMachineId)); + + List productUpdateMachineDtos = leaseProductMachines.stream().map(leaseProductMachine -> + ProductUpdateMachineDto.builder() + .id(leaseProductMachine.getId()) + .user(leaseProductMachine.getUser()) + .type(leaseProductMachine.getType()) + .miner(leaseProductMachine.getMiner()) + .priceList(collect.get(leaseProductMachine.getId())) + .computingPower(leaseProductMachine.getComputingPower().setScale(2, RoundingMode.HALF_UP)) + .theoryPower(leaseProductMachine.getTheoryPower()) + .unit(leaseProductMachine.getUnit()) + .maxLeaseDays(leaseProductMachine.getMaxLeaseDays()) + .state(leaseProductMachine.getState()) + .saleState(leaseProductMachine.getSaleState()) + .powerDissipation(leaseProductMachine.getPowerDissipation()) + .build() + ).collect(Collectors.toList()); + PageResult success = PageResult.success(productUpdateMachineDtos); + success.setTotal(pageInfo.getTotal()); + success.setTotalPage(pageInfo.getPages()); + PageHelper.clearPage(); + return success; + } + + @Override + @DSTransactional + public Result addSingleOrBatchMachine(ProductMachineParamsVo productMachineParamsVo) { + LeaseProduct product = leaseProductMapper.selectById(productMachineParamsVo.getProductId()); + + String coin = product.getCoin(); + if (coin == null){ + return Result.fail("商品币种不能为空"); + } + //封装需要添加的商品的机器 + List leaseProductMachines = new ArrayList<>(); + List productMachineURDVos = productMachineParamsVo.getProductMachineURDVos(); + + Map> payTypeMap = new HashMap<>(); + AtomicInteger index = new AtomicInteger(1); + productMachineURDVos.forEach(machine -> { + int andIncrement = index.getAndIncrement(); + leaseProductMachines.add(LeaseProductMachine.builder() + .updateCount(andIncrement) + .shopId(product.getShopId()) + .productId(productMachineParamsVo.getProductId()) + .type(machine.getType() == null ? productMachineParamsVo.getType() : machine.getType()) + .theoryPower(machine.getTheoryPower() == null ? productMachineParamsVo.getTheoryPower() : machine.getTheoryPower()) + .powerDissipation(productMachineParamsVo.getPowerDissipation()) + .cost(productMachineParamsVo.getCost()) + .price(BigDecimal.ZERO) + .incomeRate(BigDecimal.ZERO) + .maxLeaseDays(machine.getMaxLeaseDays() == null ? productMachineParamsVo.getMaxLeaseDays() : machine.getMaxLeaseDays()) + .unit(machine.getUnit() == null ? productMachineParamsVo.getUnit() : machine.getUnit()) + .powerDissipation(machine.getPowerDissipation() == null ? productMachineParamsVo.getPowerDissipation() : machine.getPowerDissipation()) + .user(machine.getUser()) + .miner(machine.getMiner()) + .coin(coin) + .state(machine.getState()) + .del( false) + .saleState(0) + .build()); + payTypeMap.put(andIncrement,machine.getPriceList().stream() + .map(payType -> LeaseProductMachinePrice.builder() + .price(payType.getPrice()) + .coin(payType.getCoin()) + .chain(payType.getChain()) + .build()) + .collect(Collectors.toList())); + + }); + + + + //计算对应user+miner 一天的总算力 + List nexaMinersv2 = leaseProductMachineMapper.getRecentlyFiveMinutesData(leaseProductMachines, product.getCoin()); + // 创建一个 Map 用于存储 user 和 miner 组合对应的 ProductMachineDto 对象 + Map machineDtoMap = nexaMinersv2.stream() + .collect(Collectors.toMap( + dto -> dto.getUser() + "-" + dto.getMiner(), + dto -> dto + )); + String priceKey = RedisCoinInfoKey.getPiceKey(product.getCoin()); + String rewardKey = RedisCoinInfoKey.getRewardKey(product.getCoin()); + String mhsKey = RedisCoinInfoKey.getMhsKey(product.getCoin()); + + if (priceKey == null || rewardKey == null || mhsKey == null){ + return Result.fail("添加商品失败,该币种挖矿机器暂时不支持"); + } + + BigDecimal price = redisService.getCacheBigDecimal(priceKey) == null ? BigDecimal.ZERO : redisService.getCacheBigDecimal(priceKey); + BigDecimal reward = redisService.getCacheBigDecimal(rewardKey) == null ? BigDecimal.ZERO : redisService.getCacheBigDecimal(rewardKey); + //全网算力单位是MH/s + BigDecimal netPower = redisService.getCacheBigDecimal(mhsKey) == null ? BigDecimal.ZERO : redisService.getCacheBigDecimal(mhsKey); + + if (price == null){ + return Result.fail("添加商品失败,未获取到"+product.getCoin()+"币的币价,请稍后重试"); + } + if (reward == null){ + return Result.fail("添加商品失败,未获取到"+product.getCoin()+"币的报快奖励,请稍后重试"); + } + if (netPower == null){ + return Result.fail("添加商品失败,未获取到"+product.getCoin()+"币的全网算力,请稍后重试"); + } + + + + List collect = leaseProductMachines.stream().peek(productMachineURDVo -> { + //获取单机实际算力 + String key = productMachineURDVo.getUser() + "-" + productMachineURDVo.getMiner(); + ProductMachineDto productMachineDto = machineDtoMap.get(key); + BigDecimal singleTheoryMachineIncome = BigDecimal.ZERO; + BigDecimal computingPower = BigDecimal.ZERO; + + if (productMachineDto != null && productMachineDto.getComputingPower() != null){ + //计算矿机一天内实际每秒平均算力 + computingPower = PowerUnitUtils.getPower(productMachineURDVo.getUnit(), productMachineDto.getComputingPower() + .multiply(BigDecimal.valueOf(1000 * 1000)) + .divide(BigDecimal.valueOf(24 * 60 * 60), 2, RoundingMode.HALF_UP)); + BigDecimal power = PowerUnitUtils.getPower(productMachineURDVo.getUnit(), netPower.multiply(BigDecimal.valueOf(1000 * 1000))); + //理论收益 = 矿机算力/全网算力 * 24h /出块间隔秒数 * 单块奖励(redis中nexa:reward) + singleTheoryMachineIncome = computingPower.divide( power,6, RoundingMode.HALF_UP) + .multiply(BigDecimal.valueOf(24 * 60 * 60) + .divide(BlockInterval.getBlockCountByCoinName(product.getCoin()), 6, RoundingMode.HALF_UP)) + .multiply(reward) + ; + + } + productMachineURDVo.setShopId(product.getShopId()); + productMachineURDVo.setComputingPower(computingPower); + productMachineURDVo.setTheoryIncome(singleTheoryMachineIncome); + }).collect(Collectors.toList()); + //新增矿机 索引改为普通联合索引 + LeaseProductMachineServiceImpl service = applicationContext.getBean(LeaseProductMachineServiceImpl.class); + //TODO 修改,还是得用唯一索引 + //leaseProductMachineMapper.saveOrUpdateBatchs(collect); + boolean b = service.saveBatch(collect); + if (b){ + //修改商品的价格范围 及商品下机器库存数量 + updatePriceRange(product.getId()); + List list = new ArrayList<>(); + for (LeaseProductMachine leaseProductMachine : collect) { + + List leaseProductMachinePrices = payTypeMap.get(leaseProductMachine.getUpdateCount()); + list.addAll(leaseProductMachinePrices.stream() + .peek(leaseProductMachinePrice -> leaseProductMachinePrice.setProductMachineId(leaseProductMachine.getId())) + .collect(Collectors.toList())); + } + //修改机器配置 + leaseProductMachinePriceService.saveBatch(list); + return Result.success("添加成功,添加矿机数:"+collect.size()); + } + return Result.fail("添加失败"); + } + + @Override + @Transactional + public Result updateMachine(List productUpdateMachineVoList) { + + List ids = new ArrayList<>(); + List leaseProductMachines = new ArrayList<>(); + List list = new ArrayList<>(); + productUpdateMachineVoList.forEach(machine -> { + leaseProductMachines.add( LeaseProductMachine.builder() + .id(machine.getId()) + .type(machine.getType()) + .theoryPower(machine.getTheoryPower()) + .unit(machine.getUnit()) + .powerDissipation(machine.getPowerDissipation()) + //.electricityBill(machine.getElectricityBill()) + .incomeRate(machine.getIncomeRate()) + .price(machine.getPrice()) + .maxLeaseDays(machine.getMaxLeaseDays()) + .user(machine.getUser()) + .miner(machine.getMiner()) + .state(machine.getState()) + .build()); + list.addAll(machine.getPriceList().stream() + .map(payType -> LeaseProductMachinePrice.builder() + .id(payType.getPayTypeId()) + .price(payType.getPrice()) + .chain(payType.getChain()) + .build()) + .collect(Collectors.toList())); + if (machine.getState() == 1){ + ids.add(machine.getId()); + } + }); + if (!ids.isEmpty()){ + List leaseProductMachines1 = leaseProductMachineMapper.selectBatchIds(ids); + long count = leaseProductMachines1.stream() + .filter(leaseProductMachine -> leaseProductMachine.getSaleState() == 1).count(); + if (count >= 1){ + return Result.fail("商品修改失败,部分矿机租约已存在,不能下架"); + } + } + + + + boolean b = updateBatchById(leaseProductMachines); + if (b){ + LeaseProductMachine leaseProductMachine = leaseProductMachineMapper.selectById(productUpdateMachineVoList.get(0).getId()); + //修改商品的价格范围 + updatePriceRange(leaseProductMachine.getProductId()); + leaseProductMachinePriceService.updateBatchById(list); + return Result.success("修改成功"); + } + return Result.fail("修改失败"); + } + + + @Override + @Transactional + public Result deleteMachine(BaseVo baseVo) { + LeaseProductMachine leaseProductMachine = leaseProductMachineMapper.selectById(baseVo.getId()); + if (leaseProductMachine.getSaleState() != 0){ + return Result.fail("该商品已售出,暂时不能删除"); + } + //修改商品的价格范围 及商品下机器库存数量 + + boolean b = updateById(LeaseProductMachine.builder().id(baseVo.getId()).del(true).build()); + if (b){ + updatePriceRange(leaseProductMachine.getProductId()); + //删除售价 + leaseProductMachinePriceService.updateById(LeaseProductMachinePrice.builder().id(baseVo.getId()).del(true).build()); + return Result.success("删除成功"); + } + return Result.fail("删除失败"); + } + + /** + * 修改商品价格范围 + * @param productId + */ + public void updatePriceRange(Long productId){ + + //修改商品的价格范围 + PriceDto priceDto = leaseProductMachineMapper.getPriceRange(productId); + BigDecimal maxPrice = BigDecimal.ZERO; + BigDecimal minPrice = BigDecimal.ZERO; + Integer totalMachineNumber = 0; + if (priceDto != null){ + totalMachineNumber = priceDto.getTotalMachineNumber(); + maxPrice = priceDto.getPriceMax(); + minPrice = priceDto.getPriceMin(); + } + LeaseProduct build = LeaseProduct.builder().id(productId) + .minPrice(minPrice) + .maxPrice(maxPrice) + .totalMachineNumber(totalMachineNumber ) + .build(); + leaseProductMapper.updateById(build); + } + + @Override + public Result calculatePrice(PriceCalculateVo priceCalculateVo) { + //TODO 第三方计算器 + + return null; + } +} diff --git a/src/main/java/com/m2pool/lease/service/impl/LeaseProductServiceImpl.java b/src/main/java/com/m2pool/lease/service/impl/LeaseProductServiceImpl.java new file mode 100644 index 0000000..d3c65bd --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/impl/LeaseProductServiceImpl.java @@ -0,0 +1,539 @@ +package com.m2pool.lease.service.impl; + +import cn.hutool.json.JSONUtil; +import com.alibaba.cloud.commons.lang.StringUtils; +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.lease.constant.Algorithm; +import com.m2pool.lease.constant.BlockInterval; +import com.m2pool.lease.constant.PowerUnit; +import com.m2pool.lease.redis.RedisCoinInfoKey; +import com.m2pool.lease.dto.*; +import com.m2pool.lease.entity.*; +import com.m2pool.lease.exception.ProductSoldOutException; +import com.m2pool.lease.mapper.*; +import com.m2pool.lease.redis.service.RedisService; +import com.m2pool.lease.service.LeaseProductMachinePriceService; +import com.m2pool.lease.service.LeaseProductService; +import com.m2pool.lease.service.LeaseShopConfigService; +import com.m2pool.lease.utils.PowerUnitUtils; +import com.m2pool.lease.utils.QrCodeUtils; +import com.m2pool.lease.utils.SecurityUtils; +import com.m2pool.lease.utils.WalletRuleCheckUtils; +import com.m2pool.lease.vo.*; +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.util.*; +import java.util.stream.Collectors; + +/** + *

+ * 商品表 服务实现类 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Service +public class LeaseProductServiceImpl extends ServiceImpl implements LeaseProductService { + + + @Resource + private LeaseProductMachineMapper leaseProductMachineMapper; + + @Resource + private LeaseProductMapper leaseProductMapper; + + @Resource + private RedisService redisService; + + @Resource + private LeaseShopMapper leaseShopMapper; + + @Resource + private LeaseShopConfigMapper leaseShopConfigMapper; + + @Resource + private LeaseProductMachinePriceService leaseProductMachinePriceService; + @Resource + private LeaseShopConfigService leaseShopConfigService; + + @Resource + private LeaseProductMachinePriceMapper leaseProductMachinePriceMapper; + + + @Override + public PageResult getProductList(ProductPageVo productPageVo) { + Long shopId = 0L; + System.out.println("用户邮箱参数:"+productPageVo.getUserEmail()); + //1.用户邮箱参数不为空,则表示个人中心的商品列表。这个时候能看到下架的商品 + if (!StringUtils.isEmpty(productPageVo.getUserEmail())){ + LeaseShop leaseShop = leaseShopMapper.selectOne(new LambdaQueryWrapper() + .eq(LeaseShop::getUserEmail, productPageVo.getUserEmail())); + if (leaseShop != null){ + shopId = leaseShop.getId(); + }else{ + //不存在出售商品 + return PageResult.success(new ArrayList<>()); + } + } + PageHelper.startPage(productPageVo.getPageNum(), productPageVo.getPageSize()); + List leaseProducts = leaseProductMapper.getProductListForShopAndUserCenter(productPageVo.getCoin(), productPageVo.getAlgorithm(), shopId); + PageInfo pageInfo = new PageInfo<>(leaseProducts); + if(leaseProducts.isEmpty()){ + //, "不存在出售商品" + return PageResult.success(new ArrayList<>()); + } + //获取店铺对应的支付方式 + List shopIds = leaseProducts.stream().map(ProductDto::getShopId).distinct().collect(Collectors.toList()); + List payType = leaseShopConfigMapper.getPayType(shopIds); + Map> payTypeMap = payType.stream().collect(Collectors.groupingBy(PayTypeDto::getShopId)); + //配置支付列表 + for (ProductDto productDto : leaseProducts) { + productDto.setPayTypes(payTypeMap.get(productDto.getShopId())); + } + + PageResult success = PageResult.success(leaseProducts); + success.setTotal(pageInfo.getTotal()); + success.setTotalPage(pageInfo.getPages()); + PageHelper.clearPage(); + return success; + } + + + @Override + public Result getMachineInfoById(BaseVo baseVo) { + LeaseProduct leaseProduct = leaseProductMapper.selectById(baseVo.getId()); + if (leaseProduct == null) { + //"商品不存在" + return Result.success(null); + } + + String priceRange = leaseProduct.getMinPrice().equals(leaseProduct.getMaxPrice()) ? leaseProduct.getMinPrice().toString(): leaseProduct.getMinPrice() +"-"+leaseProduct.getMaxPrice(); + ProductDto productDto = ProductDto.builder() + .id(leaseProduct.getId()) + .shopId(leaseProduct.getShopId()) + .name(leaseProduct.getName()) + .image(leaseProduct.getImage()) + .type(leaseProduct.getType()) + .state(leaseProduct.getState()) + .priceRange(priceRange) + .description(leaseProduct.getDescription()) + .coin(leaseProduct.getCoin()) + .algorithm(leaseProduct.getAlgorithm()) + .build(); + return Result.success(productDto); + } + + @Override + public PageResult getProductMachineInfo(ProductMachineVo productMachineVo) { + Long productId = productMachineVo.getId(); + LeaseProduct product = getById(productId); + List shopWalletInfo = leaseShopMapper.getShopWalletInfo(product.getShopId()); + PageHelper.startPage(productMachineVo.getPageNum(), productMachineVo.getPageSize()); + productMachineVo = buildQuery(productMachineVo,shopWalletInfo.get(0)); + List productMachineDtoList = leaseProductMachineMapper.getMachinesByPriceAndPowerAndDissipation(productMachineVo, product.getCoin()); + PageInfo pageInfo = new PageInfo<>(productMachineDtoList); + + if (productMachineDtoList.isEmpty()){ + //,"不存在矿机" + return PageResult.success(new ArrayList<>()); + } + //币价 单位usdt + BigDecimal price = redisService.getCacheBigDecimal(RedisCoinInfoKey.getPiceKey(product.getCoin())) == null ? BigDecimal.ZERO : redisService.getCacheBigDecimal(RedisCoinInfoKey.getPiceKey(product.getCoin())); + //报块奖励 + BigDecimal reward =redisService.getCacheBigDecimal(RedisCoinInfoKey.getRewardKey(product.getCoin())) == null ? BigDecimal.ZERO : redisService.getCacheBigDecimal(RedisCoinInfoKey.getRewardKey(product.getCoin())) ; + //全网算力单位是MH/s + BigDecimal mhs = redisService.getCacheBigDecimal(RedisCoinInfoKey.getMhsKey(product.getCoin())) == null ? BigDecimal.ZERO : redisService.getCacheBigDecimal(RedisCoinInfoKey.getMhsKey(product.getCoin())); + + List productMachines = productMachineDtoList.stream() + .peek(productMachineDto -> { + //理论收益 + BigDecimal singleTheoryMachineIncome = BigDecimal.ZERO; + productMachineDto.setAlgorithm(product.getAlgorithm()); + // 矿机算力单位转换 + 计算单矿机平均算力(ComputingPower coin_real_power 24小时总算力) + BigDecimal computingPower = PowerUnitUtils.getPower(productMachineDto.getUnit(), productMachineDto.getComputingPower() + .multiply(BigDecimal.valueOf(1000 * 1000))); + //全网算力单位转换 + BigDecimal power = PowerUnitUtils.getPower(productMachineDto.getUnit(), mhs.multiply(BigDecimal.valueOf(1000 * 1000))); + //(理论收益 = 矿机算力/全网算力 * 24h /出块间隔秒数 * 单块奖励(redis中nexa:reward)) + singleTheoryMachineIncome = computingPower.divide( power,6, RoundingMode.HALF_UP) + .multiply(BigDecimal.valueOf(24 * 60 * 60) + .divide(BlockInterval.getBlockCountByCoinName(product.getCoin()), 6, RoundingMode.HALF_UP)) + .multiply(reward); + productMachineDto.setComputingPower(computingPower); + productMachineDto.setTheoryIncome(singleTheoryMachineIncome); + productMachineDto.setTheoryUsdtIncome(singleTheoryMachineIncome.multiply(price)); + + }) + .collect(Collectors.toList()); + //分页内排序 + productMachines = sorted(productMachines,productMachineVo); + PageResult success = PageResult.success(productMachines); + success.setTotal(pageInfo.getTotal()); + success.setTotalPage(pageInfo.getPages()); + PageHelper.clearPage(); + return success; + } + + /** + * 构建查询条件 + * @param productMachineVo + * @param payConfigDto + * @return + */ + public ProductMachineVo buildQuery(ProductMachineVo productMachineVo,PayConfigDto payConfigDto){ + if (StringUtils.isEmpty(productMachineVo.getChain()) && StringUtils.isEmpty(productMachineVo.getCoin())){ + productMachineVo.setChain(payConfigDto.getPayChain()); + productMachineVo.setCoin(payConfigDto.getPayCoin()); + } + BigDecimal power = PowerUnit.getPower(productMachineVo.getUnit()).divide(BigDecimal.valueOf(1000 * 1000),2,RoundingMode.HALF_UP); + productMachineVo.setMaxPower(productMachineVo.getMaxPower().multiply(power)); + productMachineVo.setMinPower(productMachineVo.getMinPower().multiply(power)); + //算力设置 + if (productMachineVo.getMaxPower().compareTo(productMachineVo.getMinPower()) < 0){ + productMachineVo.setMaxPower(productMachineVo.getMinPower()); + productMachineVo.setMinPower(productMachineVo.getMaxPower()); + } + //功耗设置 + if (productMachineVo.getMaxPowerDissipation().compareTo(productMachineVo.getMinPowerDissipation()) < 0){ + productMachineVo.setMaxPowerDissipation(productMachineVo.getMinPowerDissipation()); + productMachineVo.setMinPowerDissipation(productMachineVo.getMaxPowerDissipation()); + } + //价格设置 + if (productMachineVo.getMaxPrice().compareTo(productMachineVo.getMinPrice()) < 0){ + + productMachineVo.setMaxPrice(productMachineVo.getMinPrice()); + productMachineVo.setMinPrice(productMachineVo.getMaxPrice()); + } + System.out.println("请求参数"+ JSONUtil.toJsonPrettyStr(productMachineVo)); + return productMachineVo; + } + + public List sorted(List productMachineDtoList,ProductMachineVo productMachineVo){ + + if (productMachineVo.getPriceSort() != null) { + Comparator priceComparator = productMachineVo.getPriceSort() ? + Comparator.comparing(ProductMachineDto::getPrice) : + Comparator.comparing(ProductMachineDto::getPrice).reversed(); + return productMachineDtoList.stream() + .sorted(priceComparator) + .collect(Collectors.toList()); + } else if (productMachineVo.getPowerSort() != null) { + Comparator powerComparator = productMachineVo.getPowerSort() ? + Comparator.comparing(ProductMachineDto::getComputingPower) : + Comparator.comparing(ProductMachineDto::getComputingPower).reversed(); + return productMachineDtoList.stream() + .sorted(powerComparator) + .collect(Collectors.toList()); + } else if (productMachineVo.getPowerDissipationSort() != null) { + Comparator powerDissipationComparator = productMachineVo.getPowerDissipationSort() ? + Comparator.comparing(ProductMachineDto::getPowerDissipation) : + Comparator.comparing(ProductMachineDto::getPowerDissipation).reversed(); + return productMachineDtoList.stream() + .sorted(powerDissipationComparator) + .collect(Collectors.toList()); + } else { + return productMachineDtoList; + } + } + + @Override + public Result> getPayTypes(ShopPayTypeVo shopPayTypeVo) { + Long shopId ; + if (shopPayTypeVo == null){ + LeaseShop leaseShop = leaseShopMapper.selectOne(new LambdaQueryWrapper() + .select(LeaseShop::getId) + .eq(LeaseShop::getUserEmail, SecurityUtils.getUsername()) + .eq(LeaseShop::getDel, false)); + if (leaseShop == null){ + //"店铺不存在" + return Result.success(null); + } + shopId = leaseShop.getId(); + }else{ + shopId = shopPayTypeVo.getShopId(); + } + List shopWalletInfo = leaseShopMapper.getShopWalletInfo(shopId); + return Result.success(shopWalletInfo); + } + + ///** + // * 计算每个价格分组中商品机器的功耗、理论算力、实际算力范围,并返回对应的范围信息对象 + // * @param productMachineDtoMap 按价格分组的商品机器列表 + // * @return 每个价格对应的商品机器范围信息 + // */ + //public static Map calculateRanges(Map> productMachineDtoMap) { + // // 使用 AtomicInteger 来实现 onlyKey 递增 + // AtomicInteger onlyKey = new AtomicInteger(0); + // return productMachineDtoMap.entrySet().stream() + // .collect(Collectors.toMap( + // Map.Entry::getKey, + // entry -> { + // List productMachines = entry.getValue(); + // int number = productMachines.size(); + // BigDecimal price = entry.getKey(); + // + // // 计算功耗范围 + // BigDecimal minPower = productMachines.stream() + // .map(ProductMachineDto::getPowerDissipation) + // .min(BigDecimal::compareTo) + // .orElse(BigDecimal.ZERO); + // BigDecimal maxPower = productMachines.stream() + // .map(ProductMachineDto::getPowerDissipation) + // .max(BigDecimal::compareTo) + // .orElse(BigDecimal.ZERO); + // String powerRange = minPower.equals(maxPower) ? String.valueOf(minPower) : minPower + " - " + maxPower; + // + // // 计算理论算力范围 + // BigDecimal minTheoryPower = productMachines.stream() + // .map(ProductMachineDto::getTheoryPower) + // .min(BigDecimal::compareTo) + // .orElse(BigDecimal.ZERO); + // BigDecimal maxTheoryPower = productMachines.stream() + // .map(ProductMachineDto::getTheoryPower) + // .max(BigDecimal::compareTo) + // .orElse(BigDecimal.ZERO); + // String theoryPowerRange = minTheoryPower.equals(maxTheoryPower) ? String.valueOf(minTheoryPower) : minTheoryPower + " - " + maxTheoryPower; + // + // // 计算实际算力范围 + // BigDecimal minComputingPower = productMachines.stream() + // .map(ProductMachineDto::getComputingPower) + // .min(BigDecimal::compareTo) + // .orElse(BigDecimal.ZERO); + // BigDecimal maxComputingPower = productMachines.stream() + // .map(ProductMachineDto::getComputingPower) + // .max(BigDecimal::compareTo) + // .orElse(BigDecimal.ZERO); + // String computingPowerRange = minComputingPower.equals(maxComputingPower) ? String.valueOf(minComputingPower) : minComputingPower + " - " + maxComputingPower; + // String unit = productMachines.get(0).getUnit(); + // int onlyKey1 = onlyKey.incrementAndGet(); + // return ProductMachineRangeGroupDto.builder() + // .onlyKey(onlyKey1) + // .powerRange(powerRange) + // .theoryPowerRange(theoryPowerRange) + // .computingPowerRange(computingPowerRange) + // .number(number) + // .price(price) + // .unit(unit) + // .build(); + // } + // )); + //} + + + @Override + public Result addProduct(ProductURDVo productURDVo) { + //校验是否存在收款钱包,不存在无法创建商品 + + List leaseShopConfig = leaseShopConfigMapper.selectList(new LambdaQueryWrapper() + .eq(LeaseShopConfig::getShopId, productURDVo.getShopId()).eq(LeaseShopConfig::getDel, 0)); + if (leaseShopConfig.isEmpty()){ + return Result.fail("添加商品失败,请先绑定收款钱包"); + } + String algorithm = Algorithm.getAlgorithm(productURDVo.getCoin()); + String coinFullName = Algorithm.getCoinFullName(productURDVo.getCoin()); + LeaseProduct product = LeaseProduct.builder() + .image(productURDVo.getImage()) + .name(productURDVo.getName()) + .algorithm(algorithm) + .description(productURDVo.getDescription()) + .state(productURDVo.getState()) + .type(productURDVo.getType()) + .coin(productURDVo.getCoin()) + .shopId(productURDVo.getShopId()) + .coinFullName(coinFullName) + .build(); + boolean save = save(product); + if (!save){ + return Result.fail("新增商品失败"); + } + return Result.success("新增商品成功"); + } + + @Override + @Transactional + public Result updateProduct(ProductURDVo productURDVo) {; + PriceDto priceDto = leaseProductMachineMapper.getPriceRange(productURDVo.getId()); + BigDecimal maxPrice = BigDecimal.ZERO; + BigDecimal minPrice = BigDecimal.ZERO; + if (priceDto != null){ + maxPrice = priceDto.getPriceMax(); + minPrice = priceDto.getPriceMin(); + } + + //校验商品是否能够上下架 + if (productURDVo.getState() == 1 && leaseProductMachineMapper.checkHasSaleMachineByProductId(productURDVo.getId()) >= 1){ + return Result.fail("商品下架失败,商品存在租约中矿机"); + } + + //修改商品主表 + String algorithm = Algorithm.getAlgorithm(productURDVo.getCoin()); + String coinFullName = Algorithm.getCoinFullName(productURDVo.getCoin()); + LeaseProduct product = LeaseProduct.builder() + .id(productURDVo.getId()) + .image(productURDVo.getImage()) + .name(productURDVo.getName()) + .description(productURDVo.getDescription()) + .state(productURDVo.getState()) + .algorithm(algorithm) + .maxPrice(maxPrice) + .minPrice(minPrice) + .type(productURDVo.getType()) + .coinFullName(coinFullName) + .build(); + + boolean b1 = updateById(product); + if (b1){ + return Result.success("修改商品成功"); + } + return Result.fail("修改商品失败"); + } + + @Override + @Transactional + public Result deleteProduct(Long id) { + int i = leaseProductMachineMapper.checkHasSaleMachineByProductId(id); + if(i >= 1){ + return Result.fail("删除失败,该商品存在租约中的机器"); + } + boolean updateFather = updateById(LeaseProduct.builder().id(id).del(true).build()); + List machineIds = leaseProductMachineMapper.getIdsForProductId(id); + if (updateFather){ + if (!machineIds.isEmpty()){ + leaseProductMachineMapper.update(LeaseProductMachine.builder().del(true).build(), + new LambdaQueryWrapper().in(LeaseProductMachine::getId, machineIds)); + //删除矿机对应的售价 + leaseProductMachinePriceService.update(LeaseProductMachinePrice.builder().del(true).build(),new LambdaUpdateWrapper().in(LeaseProductMachinePrice::getProductMachineId, machineIds)); + } + + return Result.success("删除成功"); + } + return Result.fail("删除失败"); + } + + @Override + public Result> getSupportPayType() { + Long shopId = getShopIdByProductId(); + List supportPayType = leaseProductMapper.getSupportPayType(shopId); + return Result.success(supportPayType); + } + + + + @Override + public Result> getProductListForShopWalletConfig() { + Long shopId = getShopIdByProductId(); + //1.获取商品列表 + List productForShopWalletConfigDtoList = leaseProductMapper.getProductListForShopWalletConfig(shopId); + if (productForShopWalletConfigDtoList.isEmpty()){ + return Result.success(productForShopWalletConfigDtoList); + } + //2.获取商品对应矿机 + List productMachineForWalletConfigDtoList = leaseProductMachineMapper.getProductListForShopWalletConfig(shopId); + BigDecimal usdtPrice = redisService.getCacheBigDecimal(RedisCoinInfoKey.getPiceKey(productForShopWalletConfigDtoList.get(0).getCoin())); + Map> collect = productMachineForWalletConfigDtoList.stream() + .peek(productMachineForWalletConfigDto -> { + productMachineForWalletConfigDto.setTheoryUsdtIncome(productMachineForWalletConfigDto.getTheoryIncome() + .multiply(usdtPrice)); + }) + .collect(Collectors.groupingBy(ProductMachineForWalletConfigDto::getProductId)); + for (ProductForWalletConfigDto productForWalletConfigDto : productForShopWalletConfigDtoList) { + List list = collect.get(productForWalletConfigDto.getProductId()); + if (list.isEmpty()){ + productForShopWalletConfigDtoList.remove(productForWalletConfigDto); + } + productForWalletConfigDto.setMachineList(list); + } + return Result.success(productForShopWalletConfigDtoList); + } + + public Long getShopIdByProductId(){ + LeaseShop leaseShop = leaseShopMapper.selectOne(new LambdaQueryWrapper() + .select(LeaseShop::getId) + .eq(LeaseShop::getUserEmail, SecurityUtils.getUsername()) + .eq(LeaseShop::getDel, false)); + return leaseShop.getId(); + } + + + @Override + @Transactional + public Result updateProductListForShopWalletConfig(ProductMachineForWalletConfigVo productMachineForWalletConfigVo) { + List payCoinsList = new ArrayList<>(Arrays.asList(productMachineForWalletConfigVo.getSymbol().split(","))); + String chain = productMachineForWalletConfigVo.getChain(); + String address = productMachineForWalletConfigVo.getPayAddress(); + LeaseShop leaseShop = leaseShopMapper.selectOne(new LambdaQueryWrapper() + .eq(LeaseShop::getAuthId, SecurityUtils.getUserId())); + boolean check = checkHashWalletInfo(leaseShop.getId(),chain,address, payCoinsList); + if (!check){ + return Result.fail("绑定钱包失败,钱包格式不正确或该链和币种钱包已绑定过"); + } + String[] symbolList = productMachineForWalletConfigVo.getSymbol().split(","); + List priceAndIdList = productMachineForWalletConfigVo.getProductMachineForWalletConfigVoList(); + List leaseProductMachinePriceList = new ArrayList<>(); + for (ProductMachineForWalletConfigVo.PriceVo priceVo : priceAndIdList) { + String[] priceList = priceVo.getPrice().split(","); + if (priceList.length != symbolList.length){ + return Result.fail("绑定钱包失败,存在商品矿机未设置新钱包售价"); + } + for (int i = 0; i < priceList.length; i++) { + leaseProductMachinePriceList.add(LeaseProductMachinePrice.builder() + .productMachineId(priceVo.getProductMachineId()) + .price(new BigDecimal(priceList[i])) + .coin(symbolList[i]) + .chain(chain) + .build()); + } + } + boolean b = leaseProductMachinePriceService.saveBatch(leaseProductMachinePriceList); + if (b || leaseProductMachinePriceList.isEmpty()){ + //绑定钱包新钱包 + List shopConfigList = leaseShopConfigMapper.getCoinIconByChainAndCoin(chain, payCoinsList); + String qrCode = QrCodeUtils.creatRrCode(address, 200, 200); + for (LeaseShopConfig leaseShopConfig : shopConfigList) { + leaseShopConfig.setShopId(leaseShop.getId()); + leaseShopConfig.setChain(chain); + leaseShopConfig.setPayAddress(address); + leaseShopConfig.setQrcode(qrCode); + } + boolean insetWallet = leaseShopConfigService.saveBatch(shopConfigList); + if (insetWallet){ + return Result.success("绑定钱包成功"); + } + throw new ProductSoldOutException ("钱包绑定失败"); + } + throw new ProductSoldOutException ("新币种钱包商品售价新增失败"); + } + + + /** + * 钱包格式校验,以及判断该链和币种钱包是否已存在 + * @param chain + * @param address + * @param coinList + * @return + */ + public boolean checkHashWalletInfo(Long shopId,String chain,String address,List coinList){ + if (!WalletRuleCheckUtils.checkAddress(chain,address)){ + return false; + } + List configList = leaseShopConfigMapper.selectList(new LambdaQueryWrapper() + .eq(LeaseShopConfig::getShopId, shopId) + .eq(LeaseShopConfig::getChain, chain) + .in(LeaseShopConfig::getPayCoin, coinList) + .eq(LeaseShopConfig::getDel, false) + ); + if (!configList.isEmpty()){ + return false; + } + return true; + } +} diff --git a/src/main/java/com/m2pool/lease/service/impl/LeaseShopConfigServiceImpl.java b/src/main/java/com/m2pool/lease/service/impl/LeaseShopConfigServiceImpl.java new file mode 100644 index 0000000..6443898 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/impl/LeaseShopConfigServiceImpl.java @@ -0,0 +1,20 @@ +package com.m2pool.lease.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.m2pool.lease.entity.LeaseShopConfig; +import com.m2pool.lease.mapper.LeaseShopConfigMapper; +import com.m2pool.lease.service.LeaseShopConfigService; +import org.springframework.stereotype.Service; + +/** + *

+ * 店铺商品配置表 服务实现类 + *

+ * + * @author yyb + * @since 2025-08-05 + */ +@Service +public class LeaseShopConfigServiceImpl extends ServiceImpl implements LeaseShopConfigService { + +} diff --git a/src/main/java/com/m2pool/lease/service/impl/LeaseShopServiceImpl.java b/src/main/java/com/m2pool/lease/service/impl/LeaseShopServiceImpl.java new file mode 100644 index 0000000..d93eb25 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/impl/LeaseShopServiceImpl.java @@ -0,0 +1,758 @@ +package com.m2pool.lease.service.impl; + +import com.alibaba.nacos.common.utils.StringUtils; +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.lease.aspect.OperationLogAspect; +import com.m2pool.lease.dto.*; +import com.m2pool.lease.dto.v2.PayWithdrawSellerRecordDto; +import com.m2pool.lease.dto.v2.ShopWalletInfoDto; +import com.m2pool.lease.entity.*; +import com.m2pool.lease.exception.MachineException; +import com.m2pool.lease.exception.PaymentException; +import com.m2pool.lease.mapper.*; +import com.m2pool.lease.mq.message.RabbitmqPayWithdrawMessage; +import com.m2pool.lease.service.LeaseShopConfigService; +import com.m2pool.lease.service.LeaseShopService; +import com.m2pool.lease.utils.*; +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; + +/** + *

+ * 店铺表 服务实现类 + *

+ * + * @author yyb + * @since 2025-08-05 + */ +@Service +public class LeaseShopServiceImpl extends ServiceImpl implements LeaseShopService { + + @Resource + private LeaseShopMapper leaseShopMapper; + + @Resource + private LeaseShopConfigMapper leaseShopConfigMapper; + + @Resource + private LeaseOrderItemMapper leaseOrderItemMapper; + + @Resource + private LeaseShopConfigService leaseShopConfigService; + + @Resource + private LeaseProductMapper leaseProductMapper; + + @Resource + private LeaseProductMachineMapper leaseProductMachineMapper; + + + @Resource + private LeaseMachinePriceMapper leaseMachinePriceMapper; + + @Resource + private LeaseUserMapper leaseUserMapper; + + @Resource + private LeasePayWithdrawMessageMapper leasePayWithdrawMessageMapper; + + @Resource + private RabbitTemplate rabbitTemplate; + + @Resource + private LeaseAutoAddressMapper leaseAutoAddressMapper; + + @Resource + private LeaseMachineMapper leaseMachineMapper; + + @Resource + private LeaseMachineConfigMapper leaseMachineConfigMapper; + + @Override + public Result addShop(ShopVo shopVo) { + LeaseShop leaseShop1 = leaseShopMapper.selectOne(new LambdaQueryWrapper() + .eq(LeaseShop::getUserEmail, SecurityUtils.getUsername()).eq(LeaseShop::getDel, false)); + + LeaseShop leaseShop = LeaseShop.builder() + .userEmail(SecurityUtils.getUsername()) + .name(shopVo.getName()) + .image(shopVo.getImage()) + .description(shopVo.getDescription()) + .state(shopVo.getState()) + .feeRate(shopVo.getFeeRate()) + .authId(SecurityUtils.getUserId()) + .build(); + + if(leaseShop1 != null){ + return Result.fail("当前已存在店铺,请勿重复添加"); + } + boolean save = save(leaseShop); + return save ? Result.success("新增店铺成功") : Result.fail("新增店铺失败"); + + } + + @Override + public Result updateShop(ShopVo shopVo) { + + if (shopVo.getId() == null || shopVo.getId() == 0){ + return Result.fail("id不能为空或0"); + } + + LeaseShop leaseShop = LeaseShop.builder() + .id(shopVo.getId()) + .userEmail(SecurityUtils.getUsername()) + .name(shopVo.getName()) + .image(shopVo.getImage()) + .description(shopVo.getDescription()) + .state(shopVo.getState()) + .feeRate(shopVo.getFeeRate()) + .authId(SecurityUtils.getUserId()) + .build(); + LeaseShop byId = getById(shopVo.getId()); + if (byId.getDel()){ + return Result.fail("修改失败,店铺已被删除"); + } + boolean save = updateById(leaseShop); + if (save) { + return Result.success("修改店铺成功"); + } + return Result.fail("修改店铺失败"); + } + + @Override + public Result closeShop(BaseVo baseVo) { + LeaseShop leaseShop = getById(baseVo.getId()); + if (leaseShop == null) { + return Result.fail("店铺不存在"); + } + //校验是否存在已售出的矿机 + if (leaseProductMachineMapper.checkHasSaleMachineByShopId(baseVo.getId()) >= 1 || leaseMachineMapper.checkHasSaleMachineByShopId(baseVo.getId()) >= 1){ + return Result.fail("关闭店铺失败,店铺存在租约中矿机"); + } + + + if (leaseShop.getState() == 1){ + leaseShop.setState(2); + boolean b = updateById(leaseShop); + return b ? Result.success("关闭店铺成功") : Result.fail("关闭店铺失败"); + } + if (leaseShop.getState() == 2){ + leaseShop.setState(1); + boolean b = updateById(leaseShop); + return b ? Result.success("开启店铺成功") : Result.fail("开启店铺失败"); + } + return Result.fail("店铺正在审核中不能开启和关闭店铺"); + } + + @Override + public Result getShopByUserEmail() { + LeaseShop leaseShop = leaseShopMapper.selectOne(new LambdaQueryWrapper() + .eq(LeaseShop::getUserEmail, SecurityUtils.getUsername()) + .eq(LeaseShop::getDel, false)); + ShopDto build = null; + if (leaseShop != null){ + build = ShopDto.builder() + .id(leaseShop.getId()) + .name(leaseShop.getName()) + .image(leaseShop.getImage()) + .feeRate(leaseShop.getFeeRate()) + .description(leaseShop.getDescription()) + .state(leaseShop.getState()) + .del(leaseShop.getDel()) + .build(); + + } + return Result.success(build); + } + + @Override + public Result getShopById(BaseVo baseVo) { + LeaseShop leaseShop = leaseShopMapper.selectById(baseVo.getId()); + ShopDto build = null; + if (leaseShop != null && !leaseShop.getDel()){ + build = ShopDto.builder() + .id(leaseShop.getId()) + .name(leaseShop.getName()) + .image(leaseShop.getImage()) + .description(leaseShop.getDescription()) + .state(leaseShop.getState()) + .feeRate(leaseShop.getFeeRate()) + .del(leaseShop.getDel()) + .build(); + + } + return Result.success(build); + } + + @Override + @Transactional + public Result deleteShop(BaseVo baseVo) { + LeaseShop byId = getById(baseVo.getId()); + if (byId.getDel()){ + return Result.fail("删除店铺失败,店铺已删除,请刷新页面"); + } + updateMachineInfoV1(baseVo.getId()); + updateMachineInfoV2(baseVo.getId()); + boolean b = updateById(LeaseShop.builder().id(baseVo.getId()).del(true).build()); + if (b){ + return Result.success("删除成功"); + } + return Result.fail("删除失败"); + } + + + /** + * 删除店铺时,将店铺下所有矿机和对应钱包删除 V1版本 + * + * */ + public void updateMachineInfoV1(Long shopId){ + //校验是否存在已售出的矿机 + if (leaseProductMachineMapper.checkHasSaleMachineByShopId(shopId) >= 1){ + throw new MachineException("注销失败,店铺存在租约中矿机"); + } + leaseProductMapper.update(LeaseProduct.builder().del(true).build(), new LambdaUpdateWrapper() + .eq(LeaseProduct::getShopId, shopId)); + leaseProductMachineMapper.update(LeaseProductMachine.builder().del(true).build(), new LambdaUpdateWrapper() + .eq(LeaseProductMachine::getShopId, shopId)); + } + + /** + * 删除店铺时,将店铺下所有矿机和对应钱包删除 V2版本 + * + * */ + public void updateMachineInfoV2(Long shopId){ + //校验是否存在已售出的矿机 + if (leaseMachineMapper.checkHasSaleMachineByShopId(shopId) >= 1){ + throw new MachineException("注销失败,店铺存在租约中矿机"); + } + //校验是否存在余额不为0的钱包 + List leaseShopConfigs = leaseShopConfigMapper.selectList(new LambdaQueryWrapper() + .select(LeaseShopConfig::getId,LeaseShopConfig::getBalance) + .eq(LeaseShopConfig::getShopId, shopId).eq(LeaseShopConfig::getDel, false)); + if (leaseShopConfigs.stream().anyMatch(leaseShopConfig -> leaseShopConfig.getBalance().compareTo(BigDecimal.ZERO) > 0)){ + throw new MachineException("注销失败,店铺存在余额不为0的钱包"); + } + leaseMachineMapper.update(LeaseMachine.builder().del(true).build(), new LambdaUpdateWrapper() + .eq(LeaseMachine::getShopId, shopId)); + + List ids = leaseMachineMapper.selectList(new LambdaQueryWrapper() + .select(LeaseMachine::getId) + .eq(LeaseMachine::getShopId, shopId)) + .stream().map(LeaseMachine::getId) + .collect(Collectors.toList()); + if (!ids.isEmpty()){ + leaseMachineConfigMapper.delete(new LambdaQueryWrapper().in(LeaseMachineConfig::getMachineId, ids)); + } + leaseShopConfigMapper.update(LeaseShopConfig.builder().del(true).build(), new LambdaUpdateWrapper() + .eq(LeaseShopConfig::getShopId, shopId)); + } + + + + @Override + public Result> getProductListById(BaseVo baseVo) { + List leaseProducts = leaseProductMapper.selectList(new LambdaQueryWrapper().eq(LeaseProduct::getShopId, baseVo.getId()).eq(LeaseProduct::getDel, false)); + List collect = leaseProducts.stream().map(leaseProduct -> + ShopNameDto.builder() + .id(leaseProduct.getId()) + .name(leaseProduct.getName()).build() + ).collect(Collectors.toList()); + return Result.success(collect); + } + + @Override + public Result> getShopConfig(BaseVo baseVo) { + List leaseShopConfigs = leaseShopConfigMapper.selectList(new LambdaQueryWrapper() + .eq(LeaseShopConfig::getShopId, baseVo.getId()) + .eq(LeaseShopConfig::getDel, false)); + if (leaseShopConfigs.isEmpty()){ + //店铺还未设置钱包 + return Result.success(null); + } + Map> collect = leaseShopConfigs.stream().collect(Collectors.groupingBy(LeaseShopConfig::getChain)); + List shopConfigDtos = new ArrayList<>(); + collect.forEach((chain, list) -> { + LeaseShopConfig leaseShopConfig = list.get(0); + + List coinDtoList = list.stream().map(shopConfig -> { + ShopConfigDto.CoinDto coinDto = new ShopConfigDto.CoinDto(); + coinDto.setPayCoin(shopConfig.getPayCoin()); + coinDto.setImage(shopConfig.getPayCoinImage()); + return coinDto; + }).collect(Collectors.toList()); + + ShopConfigDto build = ShopConfigDto.builder() + .shopId(leaseShopConfig.getShopId()) + .chain(leaseShopConfig.getChain()) + .payType(leaseShopConfig.getPayType()) + .payAddress(leaseShopConfig.getPayAddress()) + .children(coinDtoList) + .build(); + build.setId(leaseShopConfig.getId()); + shopConfigDtos.add( build); + + }); + + return Result.success(shopConfigDtos); + } + + + + @Override + public Result addShopConfig(ShopConfigVo shopConfigVo) { + if (checkSystemctlAddress(shopConfigVo.getPayAddress(), shopConfigVo.getChain())){ + return Result.fail("收款钱包不能使用系统钱包"); + } + //钱包地址校验 + if (!WalletRuleCheckUtils.checkAddress(shopConfigVo.getChain(),shopConfigVo.getPayAddress())){ + return Result.fail("钱包地址格式错误"); + } + LeaseShop leaseShop = leaseShopMapper.selectOne(new LambdaQueryWrapper() + .eq(LeaseShop::getUserEmail, SecurityUtils.getUsername())); + + List payCoinsList = new ArrayList<>(Arrays.asList(shopConfigVo.getPayCoin().split(","))); + + LeaseShopConfig configList = leaseShopConfigMapper.selectOne(new LambdaQueryWrapper() + .eq(LeaseShopConfig::getPayAddress, shopConfigVo.getPayAddress()) + .eq(LeaseShopConfig::getPayCoin, shopConfigVo.getPayCoin()) + .eq(LeaseShopConfig::getChain, shopConfigVo.getChain()) + .eq(LeaseShopConfig::getDel, false) + ); + if (configList != null){ + return Result.fail("绑定"+shopConfigVo.getPayAddress()+"钱包失败!" + shopConfigVo.getChain()+"链"+ shopConfigVo.getPayCoin()+ "币种钱包,已被绑定"); + } + + //获取币种图标 + List shopConfigList = leaseShopConfigMapper.getCoinIconByChainAndCoin(shopConfigVo.getChain(), payCoinsList); + + String payAddress = shopConfigVo.getPayAddress(); + String qrCode = QrCodeUtils.creatRrCode(payAddress, 200, 200); + for (LeaseShopConfig leaseShopConfig : shopConfigList) { + leaseShopConfig.setShopId(leaseShop.getId()); + leaseShopConfig.setPayType(shopConfigVo.getPayType()); + leaseShopConfig.setChain(shopConfigVo.getChain()); + leaseShopConfig.setPayAddress(payAddress); + leaseShopConfig.setQrcode(qrCode); + } + boolean b = leaseShopConfigService.saveBatch(shopConfigList); + if (b){ + return Result.success("添加成功"); + } + return Result.fail("添加失败"); + } + + @Override + public Result updateShopConfig(ShopConfigVo shopConfigVo) { + if (checkSystemctlAddress(shopConfigVo.getPayAddress(), shopConfigVo.getChain())){ + return Result.fail("收款钱包不能使用系统钱包"); + } + if ( !WalletRuleCheckUtils.checkAddress(shopConfigVo.getChain(),shopConfigVo.getPayAddress())){ + return Result.fail("提现地址格式不符合"+shopConfigVo.getChain()+"节点"); + } + LeaseShop leaseShop = leaseShopMapper.selectOne(new LambdaQueryWrapper().eq(LeaseShop::getUserEmail, SecurityUtils.getUsername())); + List payCoinsList = new ArrayList<>(Arrays.asList(shopConfigVo.getPayCoin().split(","))); + String payAddress = shopConfigVo.getPayAddress(); + String qrCode = QrCodeUtils.creatRrCode(payAddress, 200, 200); + List configList = leaseShopConfigMapper.selectList(new LambdaQueryWrapper() + .eq(LeaseShopConfig::getShopId, leaseShop.getId()) + .eq(LeaseShopConfig::getChain, shopConfigVo.getChain()) + .eq(LeaseShopConfig::getDel, false) + ); + //比较已绑定币种 和 本次修改币种的差异 + List insertOrUpdateList = new ArrayList<>(); + List deleteList = new ArrayList<>(); + + for (LeaseShopConfig leaseShopConfig : configList) { + if(!payCoinsList.contains(leaseShopConfig.getPayCoin())){ + leaseShopConfig.setDel( true); + deleteList.add(leaseShopConfig); + }else if (shopConfigVo.getPayAddress().equalsIgnoreCase(leaseShopConfig.getPayAddress())){ + insertOrUpdateList.add(LeaseShopConfig.builder() + .id(leaseShopConfig.getId()) + .shopId(leaseShop.getId()) + .payType(shopConfigVo.getPayType()) + .chain(shopConfigVo.getChain()) + .qrcode(qrCode) + .payCoin(leaseShopConfig.getPayCoin()) + .payAddress(payAddress) + .build()); + }else{ + //如果修改了地址 那么就是新增一个 旧的删除 + insertOrUpdateList.add(LeaseShopConfig.builder() + .shopId(leaseShop.getId()) + .payType(shopConfigVo.getPayType()) + .chain(shopConfigVo.getChain()) + .qrcode(qrCode) + .payCoin(leaseShopConfig.getPayCoin()) + .payAddress(payAddress) + .build()); + leaseShopConfig.setDel( true); + deleteList.add(leaseShopConfig); + } + } + List alreadyExistCoinList = configList.stream().map(LeaseShopConfig::getPayCoin).collect(Collectors.toList()); + for (String coin : payCoinsList) { + //不存在新增 + if (!alreadyExistCoinList.contains(coin)){ + insertOrUpdateList.add(LeaseShopConfig.builder() + .shopId(leaseShop.getId()) + .payCoin(coin) + .payType(shopConfigVo.getPayType()) + .chain(shopConfigVo.getChain()) + .qrcode(qrCode) + .payAddress(payAddress) + .build()); + } + } + + if (!deleteList.isEmpty()){ + boolean delete = leaseShopConfigService.updateBatchById(deleteList); + } + List coinList = insertOrUpdateList.stream().map(LeaseShopConfig::getPayCoin).collect(Collectors.toList()); + //获取币种对应的图片 + List coinIconByChainAndCoin = leaseShopConfigMapper.getCoinIconByChainAndCoin(shopConfigVo.getChain(), coinList); + + for (LeaseShopConfig leaseShopConfig : insertOrUpdateList) { + for (LeaseShopConfig configImage : coinIconByChainAndCoin) { + if (leaseShopConfig.getPayCoin().equals(configImage.getPayCoin())){ + leaseShopConfig.setPayCoinImage(configImage.getPayCoinImage()); + } + } + + } + + boolean save = leaseShopConfigService.saveOrUpdateBatch(insertOrUpdateList); + if (save){ + return Result.success("修改成功"); + } + return Result.fail("修改失败"); + } + + /** + * 校验卖家收款钱包是否为本系统钱包 + * @param address + * @param chain + * @return + */ + public boolean checkSystemctlAddress(String address, String chain){ + LeaseAutoAddress leaseAutoAddress = leaseAutoAddressMapper.selectOne(new LambdaQueryWrapper() + .eq(LeaseAutoAddress::getAddress, address) + .eq(LeaseAutoAddress::getFromChain, chain)); + if (leaseAutoAddress != null){ + return true; + } + return false; + } + + @Override + public Result deleteShopConfig(BaseVo baseVo) { + //1.根据id找到要删除的钱包配置 + LeaseShopConfig config = leaseShopConfigMapper.selectById(baseVo.getId()); + if (config.getBalance().compareTo(BigDecimal.ZERO) > 0){ + return Result.fail("删除失败,钱包余额不为0"); + } + + //2.校验当前用户还有几个卖家钱包 + Long configNums = leaseShopConfigMapper.selectCount(new LambdaQueryWrapper() + .eq(LeaseShopConfig::getShopId, config.getShopId()).eq(LeaseShopConfig::getDel, false)); + + //3.获取当前用户店铺是否存在商品 + //List ids = leaseProductMachineMapper.getIdsForShopId(config.getShopId()); + //Long productNums = leaseProductMapper.selectCount(new LambdaQueryWrapper() + // .eq(LeaseProduct::getShopId, config.getShopId()) + // .eq(LeaseProduct::getDel, false)); + List leaseMachines = leaseMachineMapper.selectList(new LambdaQueryWrapper() + .eq(LeaseMachine::getShopId, config.getShopId()) + .eq(LeaseMachine::getDel, false) + ); + List ids = leaseMachines.stream().map(LeaseMachine::getId).collect(Collectors.toList()); + // 拥有的钱包数小于等于1 且 商品数不为0 不能删除 + if (configNums <= 1 && !leaseMachines.isEmpty()){ + return Result.fail("删除失败,店铺若存在商品,至少需要一个收款钱包"); + } + //4.查询店铺中是否存在要删除链的钱包 的订单,存在订单不能删除 + Long l = leaseOrderItemMapper.selectCount(new LambdaQueryWrapper() + .eq(LeaseOrderItem::getChain, config.getChain()) + .ne(LeaseOrderItem::getStatus, 0) + ); + if (l > 0){ + return Result.fail("删除钱包失败,该钱包存在交易中的订单"); + } + config.setDel(true); + //5.删除配置 + boolean b = leaseShopConfigService.updateById(config); + if (b ){ + //6.删除所有矿机对应的价格配置 + if (!ids.isEmpty()){ + leaseMachinePriceMapper.update(LeaseMachinePrice.builder().del(true).build(), + new LambdaQueryWrapper() + .eq(LeaseMachinePrice::getChain, config.getChain()) + .in(LeaseMachinePrice::getMachineId,ids) + ); + } + return Result.success("删除成功"); + } + return Result.fail("删除失败"); + } + + @Override + public Result> getChainAndList() { + //LeaseShop leaseShop = leaseShopMapper.selectOne(new LambdaQueryWrapper().eq(LeaseShop::getUserEmail, SecurityUtils.getUsername()) + // .eq(LeaseShop::getDel, false)); + //获取本系统支持的所有链和币种 + List chainAndList = leaseShopConfigMapper.getChainAndList(null); + //获取卖家已绑定的币种 + //List leaseShopConfigs = leaseShopConfigMapper.selectList(new LambdaQueryWrapper() + // .eq(LeaseShopConfig::getShopId, leaseShop.getId()) + //); + //for (CoinFullDto coinFullDto : chainAndList) { + // for (LeaseShopConfig leaseShopConfig : leaseShopConfigs) { + // if (coinFullDto.getChainValue().equals(leaseShopConfig.getChain()) && coinFullDto.getCoinValue().equals(leaseShopConfig.getPayCoin())){ + // coinFullDto.setHasBind(1); + // } + // } + //} + return Result.success(buildChainList(chainAndList)); + } + + + @Override + public Result> getChainAndListForSeller(BaseVo baseVo) { + //LeaseShop leaseShop = leaseShopMapper.selectById(baseVo.getId()); + if (baseVo.getId() == null){ + return Result.fail("未检测到该店铺"); + } + List chainAndListForSeller = leaseShopConfigMapper.getChainAndListForSeller(baseVo.getId()); + return Result.success(buildChainList(chainAndListForSeller)); + } + + + @Override + public Result getChainAndCoin(BaseVo baseVo) { + LeaseShopConfig config = leaseShopConfigMapper.selectOne(new LambdaQueryWrapper().eq(LeaseShopConfig::getId, baseVo.getId())); + if (config == null){ + return Result.fail("未检测到该币种"); + } + //商家链已绑定的币种 + List leaseShopConfigs = leaseShopConfigMapper.selectList(new LambdaQueryWrapper() + .eq(LeaseShopConfig::getChain, config.getChain()) + .eq(LeaseShopConfig::getShopId, config.getShopId()) + ); + //获取本系统该链支持的所有币种 + List chainAndList = leaseShopConfigMapper.getChainAndList(config.getChain()); + for (CoinFullDto coinFullDto : chainAndList) { + for (LeaseShopConfig leaseShopConfig : leaseShopConfigs) { + if (coinFullDto.getChainValue().equals(leaseShopConfig.getChain()) && coinFullDto.getCoinValue().equals(leaseShopConfig.getPayCoin())){ + coinFullDto.setHasBind(1); + } + } + } + ChainListDto chainListDto = buildChainList(chainAndList).get(0); + chainListDto.setAddress(config.getPayAddress()); + return Result.success(chainListDto); + } + + private List buildChainList(List chainAndList){ + Map> chainGroup = chainAndList.stream() + .collect(Collectors.groupingBy(CoinFullDto::getChainValue)); + + List result = new ArrayList<>(); + for (Map.Entry> entry : chainGroup.entrySet()) { + String chainValue = entry.getKey(); + List group = entry.getValue(); + // 取第一个元素获取链标签 + String chainLabel = group.get(0).getChainLabel(); + + List coinList = group.stream() + .map(coinFullDto -> { + ChainListDto.CoinListDto coinDto = new ChainListDto.CoinListDto(); + coinDto.setValue(coinFullDto.getCoinValue()); + coinDto.setLabel(coinFullDto.getCoinLabel()); + coinDto.setHasBind(coinFullDto.getHasBind()); + return coinDto; + }) + .collect(Collectors.toList()); + + ChainListDto chainListDto = ChainListDto.builder() + .value(chainValue) + .label(chainLabel) + .children(coinList) + .build(); + result.add(chainListDto); + } + return result; + } + + @Override + public Result> getShopConfigV2(BaseVo baseVo) { + List 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 withdrawBalanceForSeller(BalanceVo balanceVo) { + String username = SecurityUtils.getUsername(); + Long authId = SecurityUtils.getUserId(); + //校验地址 + 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() + .select(LeaseShop::getId).eq(LeaseShop::getUserEmail, username).eq(LeaseShop::getDel, false)); + ShopWalletInfoDto shopWalletInfo = leaseShopConfigMapper.getShopWalletInfo(leaseShop.getId(), balanceVo.getFromAddress(), balanceVo.getToChain(), balanceVo.getToSymbol()); + if (shopWalletInfo == null || StringUtils.isEmpty(shopWalletInfo.getPayAddress())){ + return Result.fail("提现地址不属于您,请登录正确的账号"); + } + GoogleInfo googleInfo = leaseUserMapper.getGoogleInfoByEmail(username); + //开发环境 + //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()) + .authId(authId) + .build()); + LeaseShopConfig build = LeaseShopConfig.builder() + .id(shopWalletInfo.getId()) + .balance(shopWalletInfo.getBalance()) + .build(); + int update = leaseShopConfigMapper.update(build,new LambdaUpdateWrapper() + .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)) + .user_email(username+"--"+authId) + .build(); + try { + //发送提现消息 + rabbitTemplate.convertAndSend(PAY_WITHDRAW_QUEUE,message); + OperationLogAspect.addExtraLogParam("手续费", serviceCharge.toString()); + OperationLogAspect.addExtraLogParam("消息ID", queueId); + return Result.success("提现申请成功"); + } catch (Exception e) { + throw new PaymentException("提现失败,失败原因"+e.getMessage()); + } + } + throw new PaymentException("提现失败,余额已变化,请重新提现!"); + + } + + @Override + public Result updateShopConfigV2(ShopWalletInfoVo shopWalletInfoVo) { + + String username = SecurityUtils.getUsername(); + 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,请先提现,否则会造成财产丢失"); + } + + //谷歌验证 + GoogleInfo googleInfo = leaseUserMapper.getGoogleInfoByEmail(username); + //开发环境 + //if(googleInfo == null || StringUtils.isBlank(googleInfo.getSecret())){ + // //未绑定定谷歌验证器 + // return Result.fail("您的账号未开启双重验证,请先开启验证!"); + //} + //if(!GoogleAuthenticator.checkCode(googleInfo.getSecret(), balanceVo.getCode(), System.currentTimeMillis())){ + // return Result.fail("谷歌验证码错误"); + //} + + LeaseShopConfig leaseShopConfig = leaseShopConfigMapper.selectOne(new LambdaQueryWrapper() + .eq(LeaseShopConfig::getPayAddress, shopWalletInfoVo.getPayAddress()) + .eq(LeaseShopConfig::getChain, shopWalletInfoVo.getChain()) + .eq(LeaseShopConfig::getPayCoin, byId.getPayCoin()) + .eq(LeaseShopConfig::getDel, false) + ); + if (leaseShopConfig != null){ + return Result.fail("修改失败,该钱包地址下链和币种已被绑定,请重新输入地址或先解绑后重新绑定"); + } + boolean b = leaseShopConfigService.updateById(LeaseShopConfig.builder() + .id(shopWalletInfoVo.getId()) + .payAddress(shopWalletInfoVo.getPayAddress()) + .build()); + if (b){ + return Result.success("修改成功"); + } + return Result.fail("修改失败"); + } + + + @Override + public PageResult balanceWithdrawList(PageVo pageVo) { + String userEmail = SecurityUtils.getUsername(); + LeaseShop leaseShop = leaseShopMapper.selectOne(new LambdaQueryWrapper() + .eq(LeaseShop::getUserEmail, userEmail).eq(LeaseShop::getDel, false)); + if (leaseShop == null){ + return PageResult.success(new ArrayList<>()); + } + PageHelper.startPage(pageVo.getPageNum(), pageVo.getPageSize()); + List payWithdrawMessageDtoList = leasePayWithdrawMessageMapper.getSellerWithdrawRecord(leaseShop.getId()); + PageInfo pageInfo = new PageInfo<>(payWithdrawMessageDtoList); + PageResult success = PageResult.success(payWithdrawMessageDtoList); + success.setTotal(pageInfo.getTotal()); + success.setTotalPage(pageInfo.getPages()); + PageHelper.clearPage(); + return success; + } +} diff --git a/src/main/java/com/m2pool/lease/service/impl/LeaseShoppingCartServiceImpl.java b/src/main/java/com/m2pool/lease/service/impl/LeaseShoppingCartServiceImpl.java new file mode 100644 index 0000000..1d59da7 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/impl/LeaseShoppingCartServiceImpl.java @@ -0,0 +1,542 @@ +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.lease.constant.BlockInterval; +import com.m2pool.lease.constant.CoinCharge; +import com.m2pool.lease.redis.RedisCoinInfoKey; +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.redis.service.RedisService; +import com.m2pool.lease.service.LeaseShoppingCartInfoService; +import com.m2pool.lease.service.LeaseShoppingCartService; +import com.m2pool.lease.utils.PowerUnitUtils; +import com.m2pool.lease.utils.SecurityUtils; +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.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + *

+ * 购物车表 服务实现类 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Service +public class LeaseShoppingCartServiceImpl extends ServiceImpl implements LeaseShoppingCartService { + + + @Resource + private LeaseShoppingCartInfoMapper leaseShoppingCartInfoMapper; + + @Resource + private LeaseShoppingCartInfoService leaseShoppingCartInfoService; + + @Resource + private LeaseProductMachineMapper leaseProductMachineMapper; + + @Resource + private LeaseShopMapper leaseShopMapper; + + @Resource + private LeaseProductMachinePriceMapper leaseProductMachinePriceMapper; + + @Resource + private RedisService redisService; + + @Resource + private LeaseMachineMapper leaseMachineMapper; + + @Resource + private LeaseMachinePriceMapper leaseMachinePriceMapper; + + @Override + @Transactional + public Result addGoods(List shoppingCartInfoURDVoList) { + String userId = SecurityUtils.getUsername(); + if (shoppingCartInfoURDVoList.isEmpty()) { + return Result.fail("请选择商品"); + } + + List machineIds = shoppingCartInfoURDVoList.stream().map(ShoppingCartInfoURDVo::getProductMachineId).collect(Collectors.toList()); + List leaseProductMachines = leaseProductMachineMapper.selectList(new LambdaQueryWrapper() + .select(LeaseProductMachine::getId, LeaseProductMachine::getShopId) + .in(LeaseProductMachine::getId, machineIds)); + Map collect = leaseProductMachines.stream().collect(Collectors.toMap(LeaseProductMachine::getId, LeaseProductMachine::getShopId)); + //获取用户购物车中所有矿机 + List leaseShoppingCartInfos = leaseShoppingCartInfoMapper + .selectList(new LambdaQueryWrapper() + .eq(LeaseShoppingCartInfo::getUserId, userId)); + + // 存储需要新增的记录 + List newRecords = shoppingCartInfoURDVoList.stream() + .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()) + .productId(shoppingCartInfoURDVo.getProductId()) + .productMachineId(shoppingCartInfoURDVo.getProductMachineId()) + .build() + ).collect(Collectors.toList()); + + // 存储需要更新的记录 + Map voMap = shoppingCartInfoURDVoList.stream() + .collect(Collectors.toMap(ShoppingCartInfoURDVo::getProductMachineId, vo -> vo)); + List updateList = leaseShoppingCartInfos.stream() + .peek(info -> { + ShoppingCartInfoURDVo vo = voMap.get(info.getProductMachineId()); + if (vo != null) { + info.setLeaseTime(info.getLeaseTime() + vo.getLeaseTime()); + info.setId(info.getId()); + } + }) + .collect(Collectors.toList()); + + // 执行更新操作 + 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 getGoodsList(PageVo pageVo) { + String userId = SecurityUtils.getUsername(); + //检查购物车中是否存在商品 + List cartInfoList = leaseShoppingCartInfoMapper.getProductAndMachineIds(userId,0); + if (cartInfoList.isEmpty()){ + return PageResult.success(new ArrayList<>()); + } + List machineIds = new ArrayList<>(); + Map machineMap = new HashMap<>(); + for (ShoppingCartInfoDto shoppingCartInfoDto : cartInfoList) { + machineIds.add(shoppingCartInfoDto.getProductMachineId()); + machineMap.put(shoppingCartInfoDto.getProductMachineId(), shoppingCartInfoDto.getLeaseTime()); + } + List shopIds = leaseShopMapper.getShopIdsByMachineIds(machineIds); + PageHelper.startPage(pageVo.getPageNum(), pageVo.getPageSize()); + //查询购物车中店铺信息(一层) + List leaseShops = leaseShopMapper.selectList(new LambdaQueryWrapper().in(LeaseShop::getId, shopIds)); + //获取商品售价 + List machinePriceByMachineIds = leaseProductMachinePriceMapper.getMachinePriceByMachineIds(machineIds); + Map> machinePriceMap = machinePriceByMachineIds.stream().collect(Collectors.groupingBy(MachinePayTypeDto::getProductMachineId)); + //查询商品中机器详情 + 按照币种分组 (二层) + List leaseProductMachines = leaseProductMachineMapper.getMachinesByIds(machineIds); + //获取实时币价 + List coins = leaseProductMachines.stream().map(ProductMachineDto::getCoin).distinct().collect(Collectors.toList()); + Map coinPriceMap = new HashMap<>(); + Map coinRewardMap = new HashMap<>(); + Map coinMhsMap = new HashMap<>(); + for (String coin : coins) { + BigDecimal price = redisService.getCacheBigDecimal(RedisCoinInfoKey.getPiceKey(coin)); + BigDecimal reward =redisService.getCacheBigDecimal(RedisCoinInfoKey.getRewardKey(coin)); + BigDecimal mhs = redisService.getCacheBigDecimal(RedisCoinInfoKey.getMhsKey(coin)); + coinMhsMap.put(coin, mhs); + coinRewardMap.put(coin, reward); + coinPriceMap.put(coin, price); + } + //按币种查询每个矿机的最近一次30分钟的实时算力(后续修改为24小时平均算力) + List userMinerPowerList = new ArrayList<>(); + Map> userAndMinerList = leaseProductMachines.stream().map( + ProductMachineDto -> + LeaseProductMachine.builder() + .user(ProductMachineDto.getUser()) + .miner(ProductMachineDto.getMiner()) + .coin(ProductMachineDto.getCoin()) + .build() + ).collect(Collectors.groupingBy(LeaseProductMachine::getCoin)); + + userAndMinerList.forEach((coin, userMinerDtos) -> { + List recentlyFiveMinutesData = leaseProductMachineMapper.getRecentlyFiveMinutesData(userMinerDtos, coin); + List collect = recentlyFiveMinutesData.stream().peek(dto -> { + dto.setCoin(coin); + }).collect(Collectors.toList()); + userMinerPowerList.addAll(collect); + }); + + Map realPowerList = userMinerPowerList.stream() + .collect(Collectors.toMap( + dto -> dto.getUser() + "-" + dto.getMiner() + "-" + dto.getCoin(), + dto -> dto + )); + //为每个矿机设置实时的算力 + 按店铺分组 + Map> shopIdAndMachineInfoMap = leaseProductMachines.stream().peek(productMachineDto -> { + ProductMachineDto userMinerPowerDto = realPowerList.get(productMachineDto.getUser() + "-" + productMachineDto.getMiner() + "-" + productMachineDto.getCoin()); + List machinePayTypeDtoList = machinePriceMap.get(productMachineDto.getId()); + productMachineDto.setPriceList(machinePayTypeDtoList); + //理论收益 + BigDecimal singleTheoryMachineIncome; + BigDecimal computingPower; + BigDecimal singleTheoryUSDTMachineIncome; + if (userMinerPowerDto != null) { + // 矿机算力单位转换 + 计算单矿机平均算力(ComputingPower coin_mhs30m 平均算力) + computingPower = PowerUnitUtils.getPower(productMachineDto.getUnit(), + userMinerPowerDto.getComputingPower() + .multiply(BigDecimal.valueOf(1000 * 1000)) + .divide(BigDecimal.valueOf(24 * 60 * 60 ), 2, RoundingMode.HALF_UP)); + BigDecimal mhs = coinMhsMap.get(productMachineDto.getCoin()); + BigDecimal reward = coinRewardMap.get(productMachineDto.getCoin()); + BigDecimal price = coinPriceMap.get(productMachineDto.getCoin()); + if (mhs == null || reward == null || price == null){ + singleTheoryMachineIncome = productMachineDto.getTheoryIncome(); + singleTheoryUSDTMachineIncome = singleTheoryMachineIncome.multiply(price); + }else{ + //全网算力单位转换 + BigDecimal power = PowerUnitUtils.getPower(productMachineDto.getUnit(),mhs).multiply(BigDecimal.valueOf(1000 * 1000)); + //(理论收益 = 矿机算力/全网算力 * 24h /出块间隔秒数 * 单块奖励(redis中nexa:reward)) + singleTheoryMachineIncome = computingPower.divide( power,6, RoundingMode.HALF_UP) + .multiply(BigDecimal.valueOf(24 * 60 * 60) + .divide(BlockInterval.getBlockCountByCoinName(productMachineDto.getCoin()), 6, RoundingMode.HALF_UP)).multiply(reward); + singleTheoryUSDTMachineIncome = singleTheoryMachineIncome.multiply(price); + } + //单位为MH/s 需要转换成 矿机设定的单位 + productMachineDto.setComputingPower(computingPower); + productMachineDto.setTheoryIncome(singleTheoryMachineIncome); + productMachineDto.setTheoryUsdtIncome(singleTheoryUSDTMachineIncome); + } + }).collect(Collectors.groupingBy(ProductMachineDto::getShopId)); + + //获取商铺钱包配置信息 + List shopWalletInfo = leaseShopMapper.getShopWalletInfoList(shopIds); + + Map> 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 shopCartList = leaseShops.stream().map(leaseShop -> { + List productMachineList = shopIdAndMachineInfoMap.get(leaseShop.getId()); + return ShopCartDto.builder() + .id(leaseShop.getId()) + .name(leaseShop.getName()) + .totalMachine(productMachineList != null ? productMachineList.size() : 0) + .payConfigList(payConfigMap.get(leaseShop.getId())) + .productMachineDtoList(productMachineList) + .build(); + }).collect(Collectors.toList()); + + + + //计算不同链和币种对应的总售价 + for (ShopCartDto shopCartDto : shopCartList) { + //遍历每个商铺下的矿机 + List productMachineDtoList = shopCartDto.getProductMachineDtoList(); + Map totalPriceMap = new HashMap<>(); + Map totalPriceDtoMap = new HashMap<>(); + for (ProductMachineDto productMachineDto : productMachineDtoList) { + Integer leaseTime = machineMap.get(productMachineDto.getId()); + productMachineDto.setLeaseTime(leaseTime); + List 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 pageInfo = new PageInfo<>(shopCartList); + PageResult success = PageResult.success(shopCartList); + success.setTotal(pageInfo.getTotal()); + success.setTotalPage(pageInfo.getPages()); + PageHelper.clearPage(); + return success; + + } + + + @Override + @Transactional + public Result deleteBatchGoods(List baseVoList) { + List leaseShoppingCartInfos = leaseShoppingCartInfoMapper.selectList(new LambdaQueryWrapper() + .eq(LeaseShoppingCartInfo::getUserId, SecurityUtils.getUsername()) + .eq(LeaseShoppingCartInfo::getVersion, 0)); + if (leaseShoppingCartInfos.isEmpty()){ + return Result.fail("删除购物车商品失败,购物车中不存在商品"); + } + List ids = new ArrayList<>(); + for (LeaseShoppingCartInfo leaseShoppingCartInfo : leaseShoppingCartInfos) { + for (ProductAndMachineVo baseVo : baseVoList){ + if (leaseShoppingCartInfo.getProductMachineId().equals(baseVo.getMachineId())){ + ids.add(leaseShoppingCartInfo.getId()); + } + } + } + if (ids.isEmpty()){ + return Result.fail("删除购物车商品失败,购物车中不存在该商品"); + } + leaseShoppingCartInfoMapper.deleteBatchData(ids); + return Result.success("删除购物车商品成功"); + } + + @Override + @Transactional + public Result deleteGoods(BaseVo baseVo) { + int delete = leaseShoppingCartInfoMapper.deleteById(baseVo.getId()); + if (delete > 0){ + return Result.success("删除购物车商品成功"); + } + return Result.fail("删除购物车商品失败"); + } + + + @Override + public Result deleteBatchGoodsForIsDelete() { + List leaseShoppingCartInfos = leaseShoppingCartInfoMapper.selectList(new LambdaQueryWrapper() + .eq(LeaseShoppingCartInfo::getUserId, SecurityUtils.getUsername())); + + List machineIds = leaseShoppingCartInfos.stream().map(LeaseShoppingCartInfo::getProductMachineId) + .collect(Collectors.toList()); + List leaseProductMachines = leaseProductMachineMapper.selectList(new LambdaQueryWrapper() + .in(LeaseProductMachine::getId, machineIds)); + List delMachineIds = leaseProductMachines.stream().filter(productMachine -> productMachine.getDel() || productMachine.getState() == 1) + .map(LeaseProductMachine::getId).collect(Collectors.toList()); + + if (delMachineIds.isEmpty()){ + return Result.success("删除购物车已下架商品成功"); + } + + int delete = leaseShoppingCartInfoMapper.delete(new LambdaUpdateWrapper() + .in(LeaseShoppingCartInfo::getProductMachineId, delMachineIds)); + if (delete > 0){ + return Result.success("删除购物车已下架商品成功"); + } + return Result.fail("删除失败,不存在已下架商品"); + } + + @Override + public Result addGoodsV2(List addGoodsVoList) { + + String userId = SecurityUtils.getUsername(); + if (addGoodsVoList.isEmpty()) { + return Result.fail("请选择商品"); + } + + List machineIds = addGoodsVoList.stream().map(AddGoodsVo::getId).collect(Collectors.toList()); + List leaseProductMachines = leaseMachineMapper.selectList(new LambdaQueryWrapper() + .select(LeaseMachine::getId, LeaseMachine::getShopId) + .in(LeaseMachine::getId, machineIds)); + Map collect = leaseProductMachines.stream() + .collect(Collectors.toMap(LeaseMachine::getId, LeaseMachine::getShopId)); + //获取用户购物车中所有矿机 + List leaseShoppingCartInfos = leaseShoppingCartInfoMapper + .selectList(new LambdaQueryWrapper() + .eq(LeaseShoppingCartInfo::getUserId, userId)); + + // 存储需要新增的记录 + List 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 voMap = addGoodsVoList.stream() + .collect(Collectors.toMap(AddGoodsVo::getId, vo -> vo)); + List 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()); + + // 执行更新操作 + 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 getGoodsListV2(PageVo pageVo) { + String userId = SecurityUtils.getUsername(); + //检查购物车中是否存在商品 + List cartInfoList = leaseShoppingCartInfoMapper.getProductAndMachineIds(userId,1); + if (cartInfoList.isEmpty()){ + return PageResult.success(new ArrayList<>()); + } + List machineIds = new ArrayList<>(); + Map machineMap = new HashMap<>(); + for (ShoppingCartInfoDto shoppingCartInfoDto : cartInfoList) { + machineIds.add(shoppingCartInfoDto.getProductMachineId()); + machineMap.put(shoppingCartInfoDto.getProductMachineId(), shoppingCartInfoDto.getLeaseTime()); + } + + List shopIds = leaseMachineMapper.getShopIdsByMachineIds(machineIds); + PageHelper.startPage(pageVo.getPageNum(), pageVo.getPageSize()); + //查询购物车中店铺信息(一层) + List leaseShops = leaseShopMapper.selectList(new LambdaQueryWrapper() + .in(LeaseShop::getId, shopIds)); + + //获取商品售价 + List machinePriceByMachineIds = leaseMachinePriceMapper.getMachinePriceByMachineIds(machineIds); + Map> machinePriceMap = machinePriceByMachineIds.stream() + .collect(Collectors.groupingBy(MachinePayTypeDto::getProductMachineId)); + + //查询店铺中机器详情 + List leaseProductMachines = leaseMachineMapper.getMachinesByIds(machineIds); + + //设置每个矿机的售价,并按店铺分组 + Map> shopIdAndMachineInfoMap = leaseProductMachines.stream() + .peek(productMachineDto -> { + productMachineDto.setPriceList(machinePriceMap.get(productMachineDto.getId())); + }) + .collect(Collectors.groupingBy(CartMachineInfoDto::getShopId)); + //获取商铺钱包配置信息 + List shopWalletInfo = leaseShopMapper.getShopWalletInfoList(shopIds); + Map> 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 shopCartList = leaseShops.stream().map(leaseShop -> { + List 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 productMachineDtoList = shopCartDto.getCartMachineInfoDtoList(); + Map totalPriceMap = new HashMap<>(); + Map totalPriceDtoMap = new HashMap<>(); + for (CartMachineInfoDto productMachineDto : productMachineDtoList) { + Integer leaseTime = machineMap.get(productMachineDto.getId()); + productMachineDto.setLeaseTime(leaseTime); + List priceList = productMachineDto.getPriceList(); + for (MachinePayTypeDto machinePayTypeDto : priceList) { + BigDecimal itemPrice = machinePayTypeDto.getPrice().multiply(new BigDecimal(leaseTime)) + .multiply(BigDecimal.valueOf(productMachineDto.getNumbers())); + 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 pageInfo = new PageInfo<>(shopCartList); + PageResult success = PageResult.success(shopCartList); + success.setTotal(pageInfo.getTotal()); + success.setTotalPage(pageInfo.getPages()); + PageHelper.clearPage(); + return success; + } + + @Override + public Result deleteBatchGoodsV2(List baseVoList) { + List ids = baseVoList.stream().map(CartInfoVo::getCartInfoId).collect(Collectors.toList()); + leaseShoppingCartInfoMapper.deleteBatchData(ids); + return Result.success("删除购物车商品成功"); + } + + @Override + public Result deleteBatchGoodsForIsDeleteV2() { + List leaseShoppingCartInfos = leaseShoppingCartInfoMapper.selectList(new LambdaQueryWrapper() + .eq(LeaseShoppingCartInfo::getUserId, SecurityUtils.getUsername())); + + List machineIds = leaseShoppingCartInfos.stream().map(LeaseShoppingCartInfo::getProductMachineId).collect(Collectors.toList()); + List leaseMachines = leaseMachineMapper.selectList(new LambdaQueryWrapper() + .in(LeaseMachine::getId, machineIds)); + + List 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() + .in(LeaseShoppingCartInfo::getProductMachineId, delMachineIds)); + if (delete > 0){ + return Result.success("删除购物车已下架商品成功"); + } + return Result.fail("删除失败,不存在已下架商品"); + + } +} diff --git a/src/main/java/com/m2pool/lease/service/impl/LeaseShoppingCatInfoServiceImpl.java b/src/main/java/com/m2pool/lease/service/impl/LeaseShoppingCatInfoServiceImpl.java new file mode 100644 index 0000000..ca76c69 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/impl/LeaseShoppingCatInfoServiceImpl.java @@ -0,0 +1,21 @@ +package com.m2pool.lease.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.m2pool.lease.entity.LeaseShoppingCartInfo; +import com.m2pool.lease.mapper.LeaseShoppingCartInfoMapper; +import com.m2pool.lease.service.LeaseShoppingCartInfoService; +import org.springframework.stereotype.Service; + +/** + *

+ * 购物车详情表 服务实现类 + *

+ * + * @author yyb + * @since 2025-07-24 + */ +@Service +public class LeaseShoppingCatInfoServiceImpl extends ServiceImpl implements LeaseShoppingCartInfoService { + + +} diff --git a/src/main/java/com/m2pool/lease/service/impl/LeaseUserOwnedProductServiceImpl.java b/src/main/java/com/m2pool/lease/service/impl/LeaseUserOwnedProductServiceImpl.java new file mode 100644 index 0000000..e21989a --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/impl/LeaseUserOwnedProductServiceImpl.java @@ -0,0 +1,208 @@ +package com.m2pool.lease.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.github.pagehelper.PageHelper; + +import com.github.pagehelper.PageInfo; +import com.m2pool.lease.redis.RedisCoinInfoKey; +import com.m2pool.lease.dto.*; +import com.m2pool.lease.entity.*; +import com.m2pool.lease.mapper.*; +import com.m2pool.lease.redis.service.RedisService; +import com.m2pool.lease.service.LeaseUserOwnedProductService; +import com.m2pool.lease.utils.SecurityUtils; +import com.m2pool.lease.vo.BaseVo; +import com.m2pool.lease.vo.UserOwnedProductVo; +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.stream.Collectors; + +/** + *

+ * 用户拥有商品详情表 服务实现类 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Service +public class LeaseUserOwnedProductServiceImpl extends ServiceImpl implements LeaseUserOwnedProductService { + + @Resource + private LeaseUserOwnedProductMapper leaseUserOwnedProductMapper; + + @Resource + private LeaseProductMachineMapper leaseProductMachineMapper; + + @Resource + private RedisService redisService; + + @Override + public PageResult getOwnedList(UserOwnedProductVo userOwnedProductVo) { + PageHelper.startPage(userOwnedProductVo.getPageNum(), userOwnedProductVo.getPageSize()); + //查询已购买的商品集合 按租约状态 未过期在前 + 结束时间 最先结束的在前排序 + List leaseUserOwnedProducts = leaseUserOwnedProductMapper.selectList(new LambdaQueryWrapper() + .eq(LeaseUserOwnedProduct::getUserId, SecurityUtils.getUsername()) + .eq(LeaseUserOwnedProduct::getDel, false) + .orderByAsc(LeaseUserOwnedProduct::getStatus) + .orderByDesc(LeaseUserOwnedProduct::getEndTime)); + PageInfo pageInfo = new PageInfo<>(leaseUserOwnedProducts); + if (leaseUserOwnedProducts.isEmpty()){ + //不存在已购商品 + PageResult fail = PageResult.success(null); + PageHelper.clearPage(); + return fail; + } + Map> userAndMinerList = leaseUserOwnedProducts.stream().map( + leaseUserOwnedProduct -> + UserMinerDto.builder() + .user(leaseUserOwnedProduct.getUser()) + .miner(leaseUserOwnedProduct.getMiner()) + .coin(leaseUserOwnedProduct.getCoin()) + .build() + ).collect(Collectors.groupingBy(UserMinerDto::getCoin)); + List userMinerPowerList = new ArrayList<>(); + userAndMinerList.forEach((coin, userMinerDtos) -> { + userMinerPowerList.addAll( leaseUserOwnedProductMapper.getUserMinerPower(userMinerDtos, coin)); + }); + + //根据user+miner+coin 获取到实时的算力 + Map collect = userMinerPowerList.stream() + .collect(Collectors.toMap( + dto -> dto.getUser() + "-" + dto.getMiner() + "-" + dto.getCoin(), + dto -> dto + )); + + //获取币价 + Map coinPrice = new HashMap<>(); + //封装返回对象 + List userOwnedProducts = leaseUserOwnedProducts.stream().map(leaseUserOwnedProduct ->{ + String coin = leaseUserOwnedProduct.getCoin(); + BigDecimal bigDecimal = coinPrice.putIfAbsent(coin, redisService.getCacheBigDecimal(RedisCoinInfoKey.getPiceKey(coin))); + + UserOwnedProductDto build = UserOwnedProductDto.builder() + .id(leaseUserOwnedProduct.getId()) + .userId(leaseUserOwnedProduct.getUserId()) + .productMachineId(leaseUserOwnedProduct.getProductMachineId()) + .orderItemId(leaseUserOwnedProduct.getOrderItemId()) + .startTime(leaseUserOwnedProduct.getStartTime()) + .productMachineId(leaseUserOwnedProduct.getProductMachineId()) + .endTime(leaseUserOwnedProduct.getEndTime()) + .currentIncome(leaseUserOwnedProduct.getCurrentIncome()) + .estimatedEndIncome(leaseUserOwnedProduct.getEstimatedEndIncome()) + .currentUsdtIncome(bigDecimal == null ? leaseUserOwnedProduct.getCurrentUsdtIncome() : bigDecimal.multiply(leaseUserOwnedProduct.getCurrentIncome())) + .estimatedEndUsdtIncome(leaseUserOwnedProduct.getEstimatedEndUsdtIncome()) + .purchasedComputingPower(leaseUserOwnedProduct.getPurchasedComputingPower()) + .status(leaseUserOwnedProduct.getStatus()) + .type(leaseUserOwnedProduct.getType()) + .createTime(leaseUserOwnedProduct.getCreateTime()) + .build(); + UserMinerPowerDto userMinerPowerDto = collect.get(leaseUserOwnedProduct.getUser() + "-" + leaseUserOwnedProduct.getMiner() + "-"+ leaseUserOwnedProduct.getCoin()); + if (userMinerPowerDto != null){ + //已购机器30分算力 + build.setCurrentComputingPower(userMinerPowerDto.getPower()); + } + return build; + }).collect(Collectors.toList()); + PageResult success = PageResult.success(userOwnedProducts); + success.setTotal(pageInfo.getTotal()); + success.setTotalPage(pageInfo.getPages()); + PageHelper.clearPage(); + return success; + } + + + @Override + public Result getOwnedById(BaseVo baseVo) { + LeaseUserOwnedProduct leaseUserOwnedProduct = leaseUserOwnedProductMapper.selectById(baseVo.getId()); + List leaseProductMachines = new ArrayList<>(); + leaseProductMachines.add(LeaseProductMachine.builder() + .user(leaseUserOwnedProduct.getUser()) + .miner(leaseUserOwnedProduct.getMiner()) + .build()); + BigDecimal price = redisService.getCacheBigDecimal(RedisCoinInfoKey.getPiceKey(leaseUserOwnedProduct.getCoin())); + //获取实时算力 + List recentlyFiveMinutesData = leaseProductMachineMapper.getRecentlyFiveMinutesData(leaseProductMachines, leaseUserOwnedProduct.getCoin()); + Integer status = leaseUserOwnedProduct.getStatus(); + BigDecimal currentIncome = status == 0 ? leaseUserOwnedProduct.getSettleIncome() : leaseUserOwnedProduct.getCurrentIncome(); + BigDecimal currentUsdtIncome = currentIncome.multiply(price); + UserOwnedProductDto build = UserOwnedProductDto.builder() + .id(leaseUserOwnedProduct.getId()) + .userId(leaseUserOwnedProduct.getUserId()) + .productMachineId(leaseUserOwnedProduct.getProductMachineId()) + .orderItemId(leaseUserOwnedProduct.getOrderItemId()) + .startTime(leaseUserOwnedProduct.getStartTime()) + .productMachineId(leaseUserOwnedProduct.getProductMachineId()) + .endTime(leaseUserOwnedProduct.getEndTime()) + .estimatedEndIncome(leaseUserOwnedProduct.getEstimatedEndIncome()) + .estimatedEndUsdtIncome(leaseUserOwnedProduct.getEstimatedEndUsdtIncome()) + .purchasedComputingPower(leaseUserOwnedProduct.getPurchasedComputingPower()) + .currentIncome(currentIncome) + .currentUsdtIncome(currentUsdtIncome) + .status(status) + .type(leaseUserOwnedProduct.getType()) + .createTime(leaseUserOwnedProduct.getCreateTime()) + .build(); + if (!recentlyFiveMinutesData.isEmpty()){ + build.setCurrentComputingPower(recentlyFiveMinutesData.get(0).getComputingPower().divide(BigDecimal.valueOf(24 * 60 * 60), 2, RoundingMode.HALF_UP)); + } + return Result.success(build); + } + + /** + * 支付验证成功后去修改订单等信息(迁移到这里让事务生效) + * @param leaseOrderItems + * @return 支付订单验证成功的个数 + */ + @Transactional + @Override + public boolean afterPaySuccessTodoSomeEvent(List leaseOrderItems){ + //回调才需要 + // 修改商品售出状态 + //List ids = leaseOrderItems.stream().map(LeaseOrderItem::getProductMachineId).collect(Collectors.toList()); + //int update = leaseProductMachineMapper.update(LeaseProductMachine.builder().saleState(1).build(), + // new LambdaQueryWrapper().eq(LeaseProductMachine::getDel, false).in(LeaseProductMachine::getId, ids)); + // 添加购买记录 + //List leaseOrderItems2 = leaseOrderItemMapper.selectList(new LambdaQueryWrapper() + // .in(LeaseOrderItem::getProductMachineId, ids)); + + //获取卖方收款币种币价 + BigDecimal price = redisService.getCacheBigDecimal(RedisCoinInfoKey.getPiceKey(leaseOrderItems.get(0).getCoin())); + List collect = leaseOrderItems.stream() + .map(item -> { + // 获取当前的 LocalDateTime + LocalDateTime now = LocalDateTime.now(); + // 获取当天 0 点的 LocalDateTime + LocalDateTime startOfDay = now.toLocalDate().atStartOfDay().plusDays(1); + return LeaseUserOwnedProduct.builder() + .miner(item.getMiner()) + .userId(SecurityUtils.getUsername()) + .user(item.getUser()) + .address(item.getAddress()) + .productMachineId(item.getProductMachineId()) + .orderItemId(item.getId()) + .orderId(item.getOrderId()) + //预估收益由订单创建时固定 实际收益通过定时任务实时计算 + .estimatedEndIncome(item.getTheoryIncome()) + .estimatedEndUsdtIncome(price == null ? BigDecimal.ZERO : item.getTheoryIncome().multiply(price)) + .coin(item.getCoin()) + .startTime(startOfDay) + .type(item.getType() ? 1 : 0) + .endTime(startOfDay.plusDays(item.getLeaseTime())) + .build(); + }).collect(Collectors.toList()); + boolean b1 = saveBatch(collect); + + return b1; + } +} diff --git a/src/main/java/com/m2pool/lease/service/impl/LeaseUserServiceImpl.java b/src/main/java/com/m2pool/lease/service/impl/LeaseUserServiceImpl.java new file mode 100644 index 0000000..e2dfcf4 --- /dev/null +++ b/src/main/java/com/m2pool/lease/service/impl/LeaseUserServiceImpl.java @@ -0,0 +1,1574 @@ +package com.m2pool.lease.service.impl; + + +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.nacos.common.utils.StringUtils; +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.lease.aspect.LedgerLogService; +import com.m2pool.lease.aspect.OperationLogAspect; +import com.m2pool.lease.dto.*; +import com.m2pool.lease.dto.v2.GoogleInfoDto; +import com.m2pool.lease.entity.*; +import com.m2pool.lease.exception.AuthException; +import com.m2pool.lease.exception.PaymentException; +import com.m2pool.lease.mapper.*; +import com.m2pool.lease.mq.message.RabbitmqPayRechargeMessage; +import com.m2pool.lease.mq.message.RabbitmqPayWithdrawMessage; +import com.m2pool.lease.redis.EmailCodeValue; +import com.m2pool.lease.redis.RedisAuthKey; +import com.m2pool.lease.redis.service.RedisService; +import com.m2pool.lease.service.LeaseShopService; +import com.m2pool.lease.service.LeaseUserService; +import com.m2pool.lease.utils.*; +import com.m2pool.lease.utils.email.EmailTemplateEntity; +import com.m2pool.lease.utils.text.Convert; +import com.m2pool.lease.utils.uuid.IdUtils; +import com.m2pool.lease.vo.*; +import com.m2pool.lease.vo.v2.CloseAccountVo; +import com.m2pool.lease.vo.v2.GoogleBindVo; +import com.m2pool.lease.vo.v2.GoogleCloseVo; +import com.m2pool.lease.vo.v2.UserGoogleAuthInfo; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mail.javamail.JavaMailSenderImpl; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.RequestBody; +import org.thymeleaf.TemplateEngine; +import org.thymeleaf.context.Context; + +import javax.annotation.Resource; +import javax.mail.MessagingException; +import javax.mail.internet.MimeMessage; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.math.BigDecimal; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import static com.m2pool.lease.constant.CoinCharge.getAllChargesAsDtoList; +import static com.m2pool.lease.constant.CoinCharge.getChargeByChainAndCoin; +import static com.m2pool.lease.constant.RabbitmqConstant.PAY_RECHARGE_QUEUE; +import static com.m2pool.lease.constant.RabbitmqConstant.PAY_WITHDRAW_QUEUE; +import static com.m2pool.lease.utils.JwtUtils.EXPIRETIME; + +/** + *

+ * 用户表 服务实现类 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Service +public class LeaseUserServiceImpl extends ServiceImpl implements LeaseUserService { + + @Resource + private LeaseUserMapper leaseUserMapper; + + @Resource + private LeaseUserWalletDataMapper leaseUserWalletDataMapper; + + @Resource + private LeaseAutoAddressMapper leaseAutoAddressMapper; + + @Resource + private RabbitTemplate rabbitTemplate; + + @Resource + private LeasePayWithdrawMessageMapper leasePayWithdrawMessageMapper; + + @Resource + private LeasePayRechargeMessageMapper leasePayRechargeMessageMapper; + + + @Resource + private LeasePayRecordMessageMapper leasePayRecordMessageMapper; + + @Resource + private LeaseShopConfigMapper leaseShopConfigMapper; + + @Resource + private LeaseShopMapper leaseShopMapper; + + @Resource + private RedisService redisService; + + @Resource + private JavaMailSenderImpl javaMailSender; + + @Resource + private TemplateEngine templateEngine; + + @Resource + private LedgerLogService ledgerLogService; + + @Resource + private LeaseShopService leaseShopService; + + @Resource + private LeaseShoppingCartInfoMapper leaseShoppingCartInfoMapper; + + @Value("${client.download.client.linux}") + private String downloadClientLinux; + + @Value("${client.download.client.windows}") + private String downloadClientWindows; + + @Value("${client.download.version}") + private String version; + + @Value("${spring.mail.username}") + private String sendMailer; + + + @Value("${image.prefix}") + private String imagePrefix; + + + public static String EMAIL_REGEX="^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$"; + + public static String PWD_REGEX="^(?=.*[A-Z])(?=.*[a-z])(?=.*\\d)(?=.*[!@#$%^&*()./_])[A-Za-z\\d!@#$%^&*()./_]{8,32}$"; + public static final int PASSWORD_MIN_LENGTH = 6; + + public static final int PASSWORD_MAX_LENGTH = 32; + + + + public void verifyParams(String email, String password){ + if (StringUtils.isAnyBlank(email,password)) { + throw new AuthException("邮箱/密码必须填写"); + } + if(!StringUtils.isBlank(email)){ + if(!email.matches(EMAIL_REGEX)){ + throw new AuthException("邮箱格式错误"); + } + }else { + throw new AuthException("邮箱为必填项"); + } + if(!password.matches(PWD_REGEX)){ + throw new AuthException("密码格式错误,密码应包含大写字母、小写字母、数字以及特殊字符,8到32位"); + } + if (password.length() < PASSWORD_MIN_LENGTH || password.length() > PASSWORD_MAX_LENGTH) { + throw new AuthException("用户密码长度应该为:"+PASSWORD_MIN_LENGTH+"~"+PASSWORD_MAX_LENGTH); + } + } + + /** + * 验证码校验 + * @param key + * @param code + */ + public void verifyCode(String key, String code){ + + if(redisService.hasKey(key)){ + Object o = redisService.getCacheObject(key);//user:emailCode:email + EmailCodeValue emailCodeValue = JSON.parseObject(JSON.toJSONString(o), EmailCodeValue.class); + //验证验证码 + if(!code.equals(emailCodeValue.getEmailCode())){ + throw new AuthException("验证码错误"); + } + }else{ + throw new AuthException("验证码未获取或已过期,请重新获取验证码"); + } + } + + /** + * jwt 令牌创建 + * @param loginUser + * @return + */ + public Map createToken(LeaseUser loginUser) + { + String token = IdUtils.fastUUID(); + loginUser.setToken(token); + //刷新token + String userKey = RedisAuthKey.getUserLoginKey(loginUser.getUserId()); + redisService.setCacheObject(userKey, loginUser, EXPIRETIME, TimeUnit.MINUTES); + + // Jwt存储信息 + Map claimsMap = new HashMap(); + claimsMap.put(JwtUtils.USER_KEY, token); + claimsMap.put(JwtUtils.DETAILS_USER_ID, loginUser.getId()); + claimsMap.put(JwtUtils.DETAILS_USERNAME, loginUser.getUserId()); + + // 接口返回信息 + Map rspMap = new HashMap(); + rspMap.put("access_token", JwtUtils.createToken(claimsMap)); + rspMap.put("expires_in", EXPIRETIME); + rspMap.put("userName", loginUser.getUserId()); + return rspMap; + } + + + @Override + @Transactional + public Result> login(UserLoginVo userLoginVo) { + String email = userLoginVo.getEmail(); + String password = userLoginVo.getPassword(); + String code = userLoginVo.getCode(); + //1.校验密码,邮箱等参数格式 + verifyParams(email,password); + //2.验证码校验 + verifyCode(RedisAuthKey.getLoginCodeKey(email),code); + //3.校验用户信息 + LeaseUser user = getUser(email); + if (user == null){ + throw new AuthException("用户未注册"); + } + //4.密码输入错误次数过多,禁止登录 + String userLockKey = RedisAuthKey.getUserLockKey(email); + if(redisService.hasKey(userLockKey)){ + throw new AuthException("该账户因密码错误多次被暂时限制登陆24h!"); + } + //校验登录密码是否是注册密码 + String passwordErrorCountKey = RedisAuthKey.getPasswordErrorCountKey( email); + if (!CryptoUtils.matchesPassword(password, user.getPassword())){ + int count = 0; + if(redisService.hasKey(passwordErrorCountKey)){ + //判断key是否过期 + long expire = redisService.getExpire(passwordErrorCountKey, TimeUnit.SECONDS); + if(expire > 0){ + count = Convert.toInt(redisService.getCacheObject(passwordErrorCountKey)); + } + } + count++; + if(count == 1){ + redisService.setCacheObject(passwordErrorCountKey,count,1L,TimeUnit.HOURS); + }else if(count < 6){ + long expire = redisService.getExpire(passwordErrorCountKey, TimeUnit.SECONDS); + System.out.println(expire); + redisService.setCacheObject(passwordErrorCountKey,count,redisService.getExpire(passwordErrorCountKey,TimeUnit.SECONDS),TimeUnit.SECONDS); + } + if(count >= 5){ + //锁定用户账号 并清除输入错误次数的记录 + redisService.setCacheObject(userLockKey, email, 24L, TimeUnit.HOURS); + //清除输入错误次数的记录 + redisService.deleteObject(passwordErrorCountKey); + } + throw new AuthException("密码错误,一小时内错误五次之后锁定账户24h,当前已失败"+count+"次"); + } + user.setLastLoginTime(LocalDateTime.now()); + leaseUserMapper.updateById(user); + return Result.success(createToken(user)); + } + + + @Override + @Transactional + public Result register(UserRegisterVo userRegisterVo) { + String email = userRegisterVo.getUserEmail(); + String password = userRegisterVo.getPassword(); + //1.校验密码,邮箱等参数格式 + verifyParams(email,password); + //2.验证码校验 + verifyCode(RedisAuthKey.getRegisterCodeKey(email),userRegisterVo.getCode()); + //3.注册用户信息 + int insert = leaseUserMapper.insert(LeaseUser.builder() + .userId(email) + .password(CryptoUtils.encryptPassword(password)) + .del(false) + .build()); + if (insert > 0){ + return Result.success("注册成功"); + } + return Result.fail("注册失败"); + } + + @Override + @Transactional + public Result updatePassword(UserLoginVo userLoginVo) { + String email = userLoginVo.getEmail(); + String password = userLoginVo.getPassword(); + String updatePwdCode = userLoginVo.getCode(); + String updatePasswordCodeKey = RedisAuthKey.getUpdatePasswordCodeKey(email); + //1.校验密码,邮箱等参数格式 + verifyParams(email,password); + //2.验证码校验 + verifyCode(updatePasswordCodeKey,userLoginVo.getCode()); + //3.修改密码 + LeaseUser user = getUser(email); + if (CryptoUtils.matchesPassword(password, user.getPassword())){ + throw new AuthException("新密码不能与原密码一致"); + } + if ( redisService.hasKey(updatePasswordCodeKey)){ + Object o = redisService.getCacheObject(updatePasswordCodeKey);//user:emailCode:username + EmailCodeValue emailCodeValue = JSON.parseObject(JSON.toJSONString(o), EmailCodeValue.class); + if(updatePwdCode.equals(emailCodeValue.getEmailCode())){ + user.setPassword(CryptoUtils.encryptPassword(password)); + leaseUserMapper.updateById(user); + }else { + return Result.fail("邮箱验证码错误"); + } + } + + return Result.success("修改密码成功"); + } + + + @Override + public Result updatePasswordInCenter(RestPwdVo restPwdVo) { + String email = restPwdVo.getEmail(); + String password = restPwdVo.getPassword(); + String updatePwdCode = restPwdVo.getCode(); + String updatePasswordCodeKey = RedisAuthKey.getUpdatePasswordCodeKey(email); + //1.校验密码,邮箱等参数格式 + verifyParams(email,password); + //2.验证码校验 和谷歌验证码校验 + GoogleInfo googleInfo = leaseUserMapper.getGoogleInfoByEmail(SecurityUtils.getUsername()); + //开发环境 + //if(googleInfo == null || StringUtils.isBlank(googleInfo.getSecret())){ + // //未绑定定谷歌验证器 + // return Result.fail("您的账号未开启双重验证,请先开启验证!"); + //} + //if(!GoogleAuthenticator.checkCode(googleInfo.getSecret(), restPwdVo.getGCode(), System.currentTimeMillis())){ + // return Result.fail("谷歌验证码错误"); + //} + verifyCode(updatePasswordCodeKey,restPwdVo.getCode()); + //3.修改密码 + LeaseUser user = getUser(email); + if (CryptoUtils.matchesPassword(password, user.getPassword())){ + throw new AuthException("新密码不能与原密码一致"); + } + if ( redisService.hasKey(updatePasswordCodeKey)){ + Object o = redisService.getCacheObject(updatePasswordCodeKey);//user:emailCode:username + EmailCodeValue emailCodeValue = JSON.parseObject(JSON.toJSONString(o), EmailCodeValue.class); + if(updatePwdCode.equals(emailCodeValue.getEmailCode())){ + user.setPassword(CryptoUtils.encryptPassword(password)); + leaseUserMapper.updateById(user); + }else { + return Result.fail("邮箱验证码错误"); + } + } + return Result.success("修改密码成功"); + } + + @Override + public Result logout() { + String userEmail = SecurityUtils.getUsername(); + boolean b = redisService.deleteObject(RedisAuthKey.getUserLoginKey(userEmail)); + if (b){ + return Result.success("登出成功"); + } + return Result.fail("登出失败"); + } + + @Override + @Transactional + public Result closeAccount(CloseAccountVo closeAccountVo) { + String userEmail = SecurityUtils.getUsername(); + //1.谷歌验证 和邮箱验证码验证 + GoogleInfo googleInfo = leaseUserMapper.getGoogleInfoByEmail(userEmail); + // 开发环境 + //if(googleInfo == null || StringUtils.isBlank(googleInfo.getSecret())){ + // //未绑定定谷歌验证器 + // return Result.fail("您的账号未开启双重验证,请先开启验证!"); + //} + //if(!GoogleAuthenticator.checkCode(googleInfo.getSecret(), closeAccountVo.getGCode(), System.currentTimeMillis())){ + // return Result.fail("谷歌验证码错误"); + //} + verifyCode(RedisAuthKey.getCloseAccountKey(userEmail),closeAccountVo.getECode()); + + //2.删除用户信息 + int update = leaseUserMapper.update(LeaseUser.builder().del(true).build(), new LambdaUpdateWrapper() + .eq(LeaseUser::getUserId, userEmail)); + + //3.删除用户相关数据 + if (update > 0){ + LeaseShop leaseShop = leaseShopMapper.selectOne(new LambdaQueryWrapper().eq(LeaseShop::getUserEmail, userEmail) + .eq(LeaseShop::getDel, false)); + + //删除购物车商品 + leaseShoppingCartInfoMapper.delete(new LambdaQueryWrapper() + .eq(LeaseShoppingCartInfo::getUserId, userEmail)); + //删除用户店铺,矿机,店铺配置(钱包)相关信息 + BaseVo baseVo = new BaseVo(); + baseVo.setId(leaseShop.getId()); + Result stringResult = leaseShopService.deleteShop(baseVo); + //删除钱包 + leaseUserWalletDataMapper.update(LeaseUserWalletData.builder().del(true).build(), + new LambdaUpdateWrapper() + .eq(LeaseUserWalletData::getUserId, userEmail)); + + if (stringResult.getCode() == 200){ + redisService.deleteObject(RedisAuthKey.getUserLoginKey(userEmail)); + return Result.success("删除成功"); + } + } + + return Result.fail("删除失败"); + } + + + @Override + public Result sendCloseAccount(EmailCodeVo emailCodeVo) { + String email = emailCodeVo.getEmail(); + //2.请求次数限制 + String closeKey = RedisAuthKey.getCloseAccountKey(email); + if (redisService.hasKey(closeKey)){ + if (checkSendTimes(email, closeKey,3)){ + return Result.fail("请求次数过多,请10分钟后再试,(同一用户10分钟内只能请求5次)"); + } + }else{ + String emailCode = CodeUtils.creatCode(5); + // 最多允许用户在10分钟内发送2次的邮箱验证 + // 0s倒计时后用户可以再发送验证码,但是间隔在10分钟内只能再发送1次 + EmailCodeValue emailCodeValue = new EmailCodeValue( + email, emailCode,1 + ); + //设置失效时间10分钟 + redisService.setCacheObject(closeKey, emailCodeValue, + 10L, TimeUnit.MINUTES + ); + sendCloseAccountMailMessage(email, emailCodeValue.getEmailCode()); + } + return Result.success("发送验证码成功,验证码已经发送至用户邮箱"); + } + + @Override + public Result sendLoginCode(EmailCodeVo emailCodeVo) { + //1.邮箱格式校验(注解校验) + //String email = emailCodeVo.getEmail(); + //if(!StringUtils.isBlank(email)){ + // if(!email.matches(EMAIL_REGEX)){ + // return Result.fail("邮箱格式错误"); + // } + //}else { + // return Result.fail("601,\"邮箱不能为空\""); + //} + String email = emailCodeVo.getEmail(); + //2.请求次数限制 + String loginCodeKey = RedisAuthKey.getLoginCodeKey(email); + if (redisService.hasKey(loginCodeKey)){ + if (checkSendTimes(email, loginCodeKey,0)){ + return Result.fail("请求次数过多,请10分钟后再试,(同一用户10分钟内只能请求5次)"); + } + }else{ + LeaseUser user = getUser(email); + if (user == null){ + return Result.fail(606,"邮箱"+email+"未注册"); + }else{ + String emailCode = CodeUtils.creatCode(5); + // 最多允许用户在10分钟内发送2次的邮箱验证 + // 0s倒计时后用户可以再发送验证码,但是间隔在10分钟内只能再发送1次 + EmailCodeValue emailCodeValue = new EmailCodeValue( + email, emailCode,1 + ); + //设置失效时间10分钟 + redisService.setCacheObject(loginCodeKey, emailCodeValue, + 10L, TimeUnit.MINUTES + ); + sendLoginCodeMailMessage(email, emailCodeValue.getEmailCode()); + } + } + return Result.success("发送验证码成功,验证码已经发送至用户邮箱"); + } + + @Override + public Result sendRegisterCode(EmailCodeVo emailCodeVo) { + String email = emailCodeVo.getEmail(); + //2.请求次数限制 + String loginCodeKey = RedisAuthKey.getRegisterCodeKey(email); + if (redisService.hasKey(loginCodeKey)){ + if (checkSendTimes(email, loginCodeKey,1)){ + return Result.fail("请求次数过多,请10分钟后再试,(同一用户10分钟内只能请求5次)"); + } + }else{ + LeaseUser user = getUser(email); + if (user != null){ + return Result.fail(606,"邮箱"+email+"已被注册"); + }else{ + String emailCode = CodeUtils.creatCode(5); + // 最多允许用户在10分钟内发送2次的邮箱验证 + // 0s倒计时后用户可以再发送验证码,但是间隔在10分钟内只能再发送1次 + EmailCodeValue emailCodeValue = new EmailCodeValue( + email, emailCode,1 + ); + //设置失效时间10分钟 + redisService.setCacheObject(loginCodeKey, emailCodeValue, + 10L, TimeUnit.MINUTES + ); + sendRegisterMessage(email, emailCodeValue.getEmailCode()); + } + } + return Result.success("发送验证码成功,验证码已经发送至用户邮箱"); + } + + + + @Override + public Result sendUpdatePwdCode(EmailCodeVo emailCodeVo) { + String email = emailCodeVo.getEmail(); + LeaseUser leaseUser = leaseUserMapper.selectOne(new LambdaQueryWrapper() + .eq(LeaseUser::getUserId, email).eq(LeaseUser::getDel, false)); + if (leaseUser == null){ + return Result.fail("邮箱"+email+"未注册"); + } + //2.请求次数限制 + String loginCodeKey = RedisAuthKey.getUpdatePasswordCodeKey(email); + if (redisService.hasKey(loginCodeKey)){ + if (checkSendTimes(email, loginCodeKey,2)){ + return Result.fail("请求次数过多,请10分钟后再试,(同一用户10分钟内只能请求5次)"); + } + }else{ + String emailCode = CodeUtils.creatCode(5); + // 最多允许用户在10分钟内发送2次的邮箱验证 + // 0s倒计时后用户可以再发送验证码,但是间隔在10分钟内只能再发送1次 + EmailCodeValue emailCodeValue = new EmailCodeValue( + email, emailCode,1 + ); + //设置失效时间10分钟 + redisService.setCacheObject(loginCodeKey, emailCodeValue, + 10L, TimeUnit.MINUTES + ); + sendLoginCodeMailMessage(email, emailCodeValue.getEmailCode()); + } + return Result.success("发送验证码成功,验证码已经发送至用户邮箱"); + } + + + + + + /** + * 检查发送次数是否大于5次 + * @param email + * @param key + * @param type 0 登录验证码 1 注册验证码 2 修改密码验证码 3 注销用户验证码 + * @return + */ + private boolean checkSendTimes(String email, String key,Integer type) { + Object o = redisService.getCacheObject(key); + EmailCodeValue emailCodeValue = JSON.parseObject(JSON.toJSONString(o), EmailCodeValue.class); + if (emailCodeValue.getTimes() >= 5){ + return true; + }else{ + String emailCode = CodeUtils.creatCode(6); + emailCodeValue.setEmailCode(emailCode); + emailCodeValue.setTimes(emailCodeValue.getTimes() + 1); + long overTime = redisService.getExpire(key); + redisService.setCacheObject(key, emailCodeValue, overTime, TimeUnit.SECONDS); + if (type == 0){ + sendLoginCodeMailMessage(email, emailCodeValue.getEmailCode()); + }else if (type == 1){ + sendRegisterMessage(email, emailCodeValue.getEmailCode()); + }else if (type == 2){ + sendUpdatePwdMailMessage(email, emailCodeValue.getEmailCode()); + }else if (type == 3){ + sendCloseAccountMailMessage(email, emailCodeValue.getEmailCode()); + } + + } + return false; + } + + /** + * 根据邮箱获取用户 + * @param userEmail + * @return + */ + public LeaseUser getUser(String userEmail){ + return leaseUserMapper.selectOne(new LambdaQueryWrapper() + .eq(LeaseUser::getUserId,userEmail ).eq(LeaseUser::getDel,false)); + } + + /** + * 发送登录验证码 + * @param to + * @param code + */ + public void sendLoginCodeMailMessage(String to, String code) { + //String subject = "用户登录,邮箱验证码"; + //String text = "登录验证码10分钟内有效:\n\t"+code; + Map content = new HashMap<>(); + content.put("code",code); + content.put("text","Login verification code is valid within 10 minutes"); + EmailTemplateEntity entity = new EmailTemplateEntity(to,"User login, email verification code","emailCode-en",content); + sendHtmlEmail(entity.getEmail(),entity.getSubject(),entity.getTemplateName(),entity.getData()); + } + + /** + * 发送注册验证码 + * @param to + * @param code + */ + public void sendRegisterMessage(String to, String code) { + //String subject = "账号注册,邮箱验证码"; + //String text = "注册验证码10分钟内有效:\n\t"+code; + Map content = new HashMap<>(); + content.put("code",code); + content.put("text","The verification code for registration is valid for 10 minutes"); + EmailTemplateEntity message = new EmailTemplateEntity(to,"Account registration, email verification code","emailCode-en",content); + sendHtmlEmail(message.getEmail(),message.getSubject(),message.getTemplateName(),message.getData()); + } + + public void sendUpdatePwdMailMessage(String to, String code) { + //String subject = "修改密码,邮箱验证码"; + //String text = "您正在修改密码,如果不是您本人操作,请忽略。验证码10分钟内有效:\n\t"+code; + + Map content = new HashMap<>(); + content.put("code",code); + content.put("text","You are currently modifying your password. If this is not done by you, please ignore it. The verification code is valid for 10 minutes."); + EmailTemplateEntity entity = new EmailTemplateEntity(to,"Change password, email verification code","emailCode-en",content); + sendHtmlEmail(entity.getEmail(),entity.getSubject(),entity.getTemplateName(),entity.getData()); + } + + + public void sendCloseAccountMailMessage(String to, String code) { + //String subject = "修改密码,邮箱验证码"; + //String text = "您正在修改密码,如果不是您本人操作,请忽略。验证码10分钟内有效:\n\t"+code; + + Map content = new HashMap<>(); + content.put("code",code); + content.put("text","You are logging out of the user account. If this is not done by you, please ignore it. The verification code is valid for 10 minutes."); + EmailTemplateEntity entity = new EmailTemplateEntity(to,"Change password, email verification code","emailCode-en",content); + sendHtmlEmail(entity.getEmail(),entity.getSubject(),entity.getTemplateName(),entity.getData()); + } + /** + * 通过thymeleaf发送html邮件 + * @param to + * @param subject + * @param templateName + * @param variables + * @throws MessagingException + */ + public void sendHtmlEmail(String to, String subject, String templateName, Map variables) { + + // 创建 MimeMessage 对象 + MimeMessage message = javaMailSender.createMimeMessage(); + MimeMessageHelper helper = null; + try { + helper = new MimeMessageHelper(message, true); + //邮件发信人 + helper.setFrom(sendMailer); + + // 收件人一个活多个 + helper.setTo(to.split(",")); + helper.setSubject(subject); + + // 创建 Thymeleaf 上下文并添加变量 + Context context = new Context(); + //设置图片访问前缀 + variables.put("imagePrefix", imagePrefix); + context.setVariables(variables); + + // 处理 HTML 模板 + String htmlContent = templateEngine.process(templateName, context); + + // 设置邮件内容为 HTML + helper.setText(htmlContent, true); + + //邮件发送时间 + helper.setSentDate(new Date()); + + // 发送邮件 + javaMailSender.send(message); + System.out.println("发送邮件成功:"+sendMailer+"->"+to); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Value("${myenv.domain}") + private String domain; + + @Override + public Result bindGoogle(GoogleBindVo googleBindVo) { + // 参数校验 + if (googleBindVo == null){ + return Result.fail("未获取到请求参数"); + } + String email = SecurityUtils.getUsername(); + if(StringUtils.isEmpty(email)){ + return Result.fail("token解析异常"); + } + // 根据邮箱查询是否已绑定 + UserGoogleAuthInfo info = leaseUserMapper.getUserInfoByEmail(email); + + if(StringUtils.isBlank(info.getSecret())){ + //未绑定 正常走绑定流程 + //校验gcode + boolean gResult = GoogleAuthenticator.checkCode(googleBindVo.getSecret(), + googleBindVo.getGCode(), System.currentTimeMillis()); + //开发环境 + //if(!gResult){ + // return Result.fail("谷歌验证码错误"); + //} + //校验邮箱验证码 + String redisKey = RedisAuthKey.getGoogleBindCodeKey(email); + if(redisService.hasKey(redisKey)){ + + Object o = redisService.getCacheObject(redisKey);//google:email:code + + EmailCodeValue emailCodeValue = JSON.parseObject(JSON.toJSONString(o), EmailCodeValue.class); + + //验证验证码 + if(!googleBindVo.getECode().equals(emailCodeValue.getEmailCode())){ + return Result.fail("邮箱验证码错误"); + } + }else { + return Result.fail("邮箱验证码 未获取或已过期(有效期10分钟),请重新获取验证码"); + } + + //校验密码 + if (!CryptoUtils.matchesPassword(googleBindVo.getPwd(), info.getPwd())){ + return Result.fail("密码错误"); + } + //入库 + UserGoogleAuthInfo bingInfo = new UserGoogleAuthInfo(); + bingInfo.setSecret(googleBindVo.getSecret()); + //0 已绑定 + bingInfo.setStatus(0); + bingInfo.setEmail(info.getEmail()); + boolean bingResult = leaseUserMapper.bingGoogleAuthenticator(bingInfo); + if(bingResult){ + return Result.success("绑定成功"); + }else { + return Result.fail("请求服务器失败,请重试"); + } + + }else { + if(info.getStatus() == 2){ + return Result.fail("您绑定但是关闭了双重验证,请重新打开"); + }else { + return Result.fail("当前用户已绑定过谷歌登录器."); + } + } + } + + @Override + public Result getBindInfo() { + String email = SecurityUtils.getUsername(); + if(StringUtils.isEmpty(email)){ + return Result.fail("用户未登录"); + } + GoogleInfo info = leaseUserMapper.getGoogleInfoByEmail(email); + + GoogleInfoDto dto = new GoogleInfoDto(); + if(StringUtils.isBlank(info.getSecret())){ + //生成谷歌验证码 + String secret = GoogleAuthenticator.getSecretKey(); + String qrCodeText = GoogleAuthenticator.getQrCodeText(secret, email, domain); + String img = QrCodeUtils.creatRrCode(qrCodeText, 200, 200); + + boolean ifExist = leaseUserMapper.checkSecretIfExist(secret); + if(ifExist){ + secret = GoogleAuthenticator.getSecretKey(); + qrCodeText = GoogleAuthenticator.getQrCodeText(secret, email, domain); + img = QrCodeUtils.creatRrCode(qrCodeText,200,200); + + ifExist = leaseUserMapper.checkSecretIfExist(secret); + if(ifExist){ + return Result.fail("网络异常,请稍后再试"); + } + } + dto.setSecret(secret); + dto.setImg(img); + + }else { + //数据库已有谷歌验证码 直接返回 + BeanUtils.copyProperties(info,dto); + dto.setImg(QrCodeUtils.creatRrCode( GoogleAuthenticator.getQrCodeText(dto.getSecret(),email , domain),200,200)); + } + return Result.success(dto); + } + + @Override + @Transactional + public Result sendOpenGoogleCode() { + String email = SecurityUtils.getUsername(); + if(StringUtils.isEmpty(email)){ + return Result.fail("token解析异常"); + } + boolean success ; + + //判断用户是不是恶意刷邮箱,在规定时间内进行的 + String redisKey = RedisAuthKey.getGoogleBindCodeKey( email); + if (redisService.hasKey(redisKey)) { + Object o = redisService.getCacheObject(redisKey); + + EmailCodeValue emailCodeValue = JSON.parseObject(JSON.toJSONString(o), EmailCodeValue.class); + if (emailCodeValue.getTimes() > 4) { + return Result.fail("请求次数过多,请10分钟后再试,(同一用户10分钟内只能请求4次)"); + } else { + + String emailCode = CodeUtils.creatCode(6); + emailCodeValue.setEmailCode(emailCode); + emailCodeValue.setTimes(emailCodeValue.getTimes() + 1); + long overTime = redisService.getExpire(redisKey); + redisService.setCacheObject(redisKey, emailCodeValue, overTime, TimeUnit.SECONDS + ); + success = sendEmail(email,emailCode); + } + } else { + String emailCode = CodeUtils.creatCode(6); + // 最多允许用户在10分钟内发送2次的邮箱验证 + // 0s倒计时后用户可以再发送验证码,但是间隔在10分钟内只能再发送1次 + EmailCodeValue emailCodeValue = new EmailCodeValue(); + emailCodeValue.setEmail(email); + emailCodeValue.setEmailCode(emailCode); + emailCodeValue.setTimes(1); + //设置失效时间10分钟 + redisService.setCacheObject(redisKey, emailCodeValue, + 10L, TimeUnit.MINUTES + ); + success = sendEmail(email,emailCode); + } + + return success ? Result.success("发送邮箱验证码成功") : Result.fail("发送邮箱验证码失败,请稍后重试"); + } + + public boolean sendEmail(String email,String code){ + String text = "您正在绑定谷歌验证器!\n" + + "您的邮箱验证验证码是:\n\t"+code + + "\n此验证码10分钟有效。"; + try { + //true 代表支持复杂的类型 + MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(javaMailSender.createMimeMessage(),true); + //邮件发信人 + mimeMessageHelper.setFrom(sendMailer); + //邮件收信人 1或多个 + mimeMessageHelper.setTo(email); + //邮件主题 + mimeMessageHelper.setSubject("[M2Pool] 邮箱验证码"); + //邮件内容 + mimeMessageHelper.setText(text); + //邮件发送时间 + mimeMessageHelper.setSentDate(new Date()); + + //发送邮件 + javaMailSender.send(mimeMessageHelper.getMimeMessage()); + //System.out.println("发送邮件成功:"+sendMailer+"->"+to); + return true; + } catch (Exception e) { + System.out.println("发送邮件失败:"+e.getMessage()); + return false; + } + } + + @Override + public Result sendCloseGoogleCode() { + String email = SecurityUtils.getUsername(); + if(StringUtils.isEmpty(email)){ + return Result.fail("token解析异常"); + } + + boolean success; + //判断用户是不是恶意刷邮箱,在规定时间内进行的 + String redisKey = RedisAuthKey.getGoogleCloseCodeKey( email); + if (redisService.hasKey(redisKey)) { + Object o = redisService.getCacheObject(redisKey); + + EmailCodeValue emailCodeValue = JSON.parseObject(JSON.toJSONString(o), EmailCodeValue.class); + if (emailCodeValue.getTimes() > 4) { + return Result.fail("请求次数过多,请10分钟后再试,(同一用户10分钟内只能请求4次)"); + } else { + + String emailCode = CodeUtils.creatCode(6); + emailCodeValue.setEmailCode(emailCode); + + emailCodeValue.setTimes(emailCodeValue.getTimes() + 1); + long overTime = redisService.getExpire(redisKey); + redisService.setCacheObject(redisKey, emailCodeValue, overTime, TimeUnit.SECONDS + ); + success = sendEmail(email,emailCode); + } + } else { + String emailCode = CodeUtils.creatCode(6); + // 最多允许用户在10分钟内发送2次的邮箱验证 + // 0s倒计时后用户可以再发送验证码,但是间隔在10分钟内只能再发送1次 + EmailCodeValue emailCodeValue = new EmailCodeValue(); + emailCodeValue.setEmail(email); + emailCodeValue.setEmailCode(emailCode); + emailCodeValue.setTimes(1); + //设置失效时间10分钟 + redisService.setCacheObject(redisKey, emailCodeValue, + 10L, TimeUnit.MINUTES + ); + success = sendCloseEmail(email,emailCode); + } + return success ? Result.success("发送邮箱验证码成功") : Result.fail("发送邮箱验证码失败,请稍后重试"); + } + public boolean sendCloseEmail(String email,String code){ + String text = "M2Pool:\n" + + "您正在关闭谷歌验证器!\n" + + "您的邮箱验证验证码是:\n\t"+code + + "\n此验证码10分钟有效。"; + try { + //true 代表支持复杂的类型 + MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(javaMailSender.createMimeMessage(),true); + //邮件发信人 + mimeMessageHelper.setFrom(sendMailer); + //邮件收信人 1或多个 + mimeMessageHelper.setTo(email); + //邮件主题 + mimeMessageHelper.setSubject("[M2Pool] 邮箱验证码"); + //邮件内容 + mimeMessageHelper.setText(text); + //邮件发送时间 + mimeMessageHelper.setSentDate(new Date()); + + //发送邮件 + javaMailSender.send(mimeMessageHelper.getMimeMessage()); + //System.out.println("发送邮件成功:"+sendMailer+"->"+to); + return true; + } catch (Exception e) { + System.out.println("发送邮件失败:"+e.getMessage()); + return false; + } + } + @Override + public Result closeStepTwo(GoogleCloseVo vo) { + // 参数校验 + if (vo == null){ + return Result.fail("未获取到请求参数"); + } + String email = SecurityUtils.getUsername(); + if(StringUtils.isEmpty(email)){ + return Result.fail("token解析异常"); + } + + // 根据邮箱查询是否已绑定 + UserGoogleAuthInfo info = leaseUserMapper.getUserInfoByEmail(email); + + if(StringUtils.isBlank(info.getSecret())){ + return Result.success(); + }else { + + //已绑定 正常走关闭谷歌验证流程 + //校验gcode + boolean gResult = GoogleAuthenticator.checkCode(info.getSecret(), vo.getGCode(), System.currentTimeMillis()); + //开发环境 + //if(!gResult){ + // return Result.fail("谷歌验证码错误"); + //} + //校验邮箱验证码 + String redisKey = RedisAuthKey.getGoogleCloseCodeKey( email); + if(redisService.hasKey(redisKey)){ + + Object o = redisService.getCacheObject(redisKey);//google:email:close + + EmailCodeValue emailCodeValue = JSON.parseObject(JSON.toJSONString(o), EmailCodeValue.class); + + //验证验证码 + if(!vo.getECode().equals(emailCodeValue.getEmailCode())){ + return Result.fail("邮箱验证码错误"); + } + }else { + return Result.fail("邮箱验证码 未获取或已过期(有效期10分钟),请重新获取验证码"); + } + + //入库 + boolean bingResult = leaseUserMapper.closeGoogleAuthenticator(info); + if(bingResult){ + return Result.success(); + }else { + return Result.fail("请求服务器失败,请重试"); + } + } + } + + + @Override + public Result getGoogleStatus() { + return Result.success(leaseUserMapper.checkIfBindByEmail(SecurityUtils.getUsername())); + } + + + @Override + public Result openStepTwo(GoogleCloseVo vo) { + // 参数校验 + if (vo == null){ + return Result.fail("未获取到请求参数"); + } + String email = SecurityUtils.getUsername(); + if(StringUtils.isEmpty(email)){ + return Result.fail("token解析异常"); + } + + // 根据邮箱查询是否已绑定 + UserGoogleAuthInfo info = leaseUserMapper.getUserInfoByEmail(email); + + //已绑定 正常走关闭谷歌验证流程 + //校验gcode + boolean gResult = GoogleAuthenticator.checkCode(info.getSecret(), vo.getGCode(), System.currentTimeMillis()); + //开发环境 + //if(!gResult){ + // return Result.fail("谷歌验证码错误"); + //} + //校验邮箱验证码 + String redisKey = RedisAuthKey.getGoogleBindCodeKey( email); + if(redisService.hasKey(redisKey)){ + + Object o = redisService.getCacheObject(redisKey);//google:email:close + + EmailCodeValue emailCodeValue = JSON.parseObject(JSON.toJSONString(o), EmailCodeValue.class); + + //验证验证码 + if(!vo.getECode().equals(emailCodeValue.getEmailCode())){ + return Result.fail("邮箱验证码错误"); + } + }else { + return Result.fail("邮箱验证码 未获取或已过期(有效期10分钟),请重新获取验证码"); + } + + //入库 + boolean bingResult = leaseUserMapper.openGoogleAuthenticator(info); + if(bingResult){ + return Result.success(); + }else { + return Result.fail("请求服务器失败,请重试"); + } + } + + @Override + @Transactional + public Result bindWallet(ChainAndCoinVo chainAndCoinVo) { + Long authId = SecurityUtils.getUserId(); + //同一用户同一个链只会存在一个钱包 + List userWalletDataDtoList = leaseUserWalletDataMapper.getWalletInfoByChain(chainAndCoinVo.getChain(),authId); + UserWalletDataDto result; + //如果某个链 未查找到钱包地址 去新绑定一个 + if (userWalletDataDtoList.isEmpty()){ + LeaseAutoAddress bindAddress = getBindAddress(chainAndCoinVo.getChain(), chainAndCoinVo.getCoin()); + if (bindAddress != null){ + String address = bindAddress.getAddress(); + String qrcode = QrCodeUtils.creatRrCode(bindAddress.getAddress(), 200, 200); + String username = SecurityUtils.getUsername(); + String queueId = UuidGeneratorUtil.generateUuidWithoutHyphen(); + result = UserWalletDataDto.builder() + .queueId(queueId) + .fromAddress(address) + .fromChain(chainAndCoinVo.getChain()) + .fromSymbol(chainAndCoinVo.getCoin()) + .balance(BigDecimal.ZERO) + .userId(username) + .qrcode(qrcode) + .id(bindAddress.getId()) + .build(); + leaseUserWalletDataMapper.insert(LeaseUserWalletData.builder() + .queueId(queueId) + .fromChain(chainAndCoinVo.getChain()) + .fromAddress(address) + .fromSymbol(chainAndCoinVo.getCoin()) + .qrcode(qrcode) + .userId(username) + .authId(authId) + .balance(BigDecimal.ZERO) + .build()); + bindAddress.setStatus(1); + leaseAutoAddressMapper.updateById(bindAddress); + //监听钱包 + sendMessage(result,username,authId); + }else{ + return Result.fail("没有可用的地址,请联系管理员生成可用的钱包地址"); + } + }else{ + for (UserWalletDataDto userWalletDataDto : userWalletDataDtoList) { + if (userWalletDataDto.getFromSymbol().equals(chainAndCoinVo.getCoin())){ + //开发环境 + //sendMessage(UserWalletDataDto.builder() + // .queueId(userWalletDataDto.getQueueId()) + // .fromAddress(userWalletDataDto.getFromAddress()) + // .fromChain(chainAndCoinVo.getChain()) + // .fromSymbol(chainAndCoinVo.getCoin()) + // .balance(BigDecimal.ZERO) + // .userId(userWalletDataDto.getUserId()) + // .qrcode(userWalletDataDto.getQrcode()) + // .build()); + return Result.success(userWalletDataDto); + } + } + UserWalletDataDto userWalletDataDto = userWalletDataDtoList.get(0); + result = UserWalletDataDto.builder() + .queueId(userWalletDataDto.getQueueId()) + .fromAddress(userWalletDataDto.getFromAddress()) + .fromChain(chainAndCoinVo.getChain()) + .fromSymbol(chainAndCoinVo.getCoin()) + .balance(BigDecimal.ZERO) + .userId(userWalletDataDto.getUserId()) + .qrcode(userWalletDataDto.getQrcode()) + .build(); + + LeaseUserWalletData build = LeaseUserWalletData.builder() + .queueId(userWalletDataDto.getQueueId()) + .fromChain(userWalletDataDto.getFromChain()) + .fromAddress(userWalletDataDto.getFromAddress()) + .fromSymbol(chainAndCoinVo.getCoin()) + .qrcode(userWalletDataDto.getQrcode()) + .userId(userWalletDataDto.getUserId()) + .authId(authId) + .balance(BigDecimal.ZERO) + .build(); + //链钱包存在 但是新增币种 + leaseUserWalletDataMapper.insert(build); + } + + return Result.success(result); + } + + + @Override + @Transactional + public Result> getWalletInfo() { + List userWalletDataDto = leaseUserWalletDataMapper.selectByUserId(SecurityUtils.getUserId()); + //获取手续费 + for (UserWalletDataDto walletDataDto : userWalletDataDto) { + BigDecimal chargeByChainAndCoin = getChargeByChainAndCoin(walletDataDto.getFromChain(), walletDataDto.getFromSymbol()); + walletDataDto.setCharge(chargeByChainAndCoin); + } + List collect = new ArrayList<>(); + if (!userWalletDataDto.isEmpty()){ + collect = userWalletDataDto.stream().peek(item -> item.setBalance(item.getBalance().subtract(item.getBlockedBalance()))).collect(Collectors.toList()); + } + return Result.success(collect); + } + + /** + * 检查地址是否半年没有操作(支付,充值,提现) 这个方法可以改成监测 钱包(提现和充值都会修改钱包) 的最近修改时间 + 支付操作 + * @param fromAddress + * @return + */ + public boolean checkAddress(String fromAddress){ + //支付操作 + boolean b = leasePayRecordMessageMapper.checkPayOperator(fromAddress); + //提现操作 + boolean b1 = leasePayWithdrawMessageMapper.checkWithdrawOperator(fromAddress); + //充值操作 + boolean b2 = leasePayRechargeMessageMapper.checkRechargeOperator(fromAddress); + if (b || b1 || b2){ + return true; + } + return false; + } + + /** + * 获取一个未使用的地址 + * @return + */ + public LeaseAutoAddress getBindAddress(String chain, String coin){ + LeaseAutoAddress leaseAutoAddress = leaseAutoAddressMapper.selectOne(new LambdaQueryWrapper() + .eq(LeaseAutoAddress::getStatus, 0) + .eq(LeaseAutoAddress::getFromChain, chain) + .last("limit 1")); + return leaseAutoAddress; + } + + /** + * 发送mq 消息,从而启动钱包监听 + */ + public void sendMessage(UserWalletDataDto userWalletDataDto,String username,Long authId){ + try { + System.out.println("发送mq消息"); + long l = System.currentTimeMillis()/1000; + RabbitmqPayRechargeMessage build = RabbitmqPayRechargeMessage.builder() + .queue_id(userWalletDataDto.getQueueId()) + .address(userWalletDataDto.getFromAddress()) + .chain(userWalletDataDto.getFromChain()) + .symbol(userWalletDataDto.getFromSymbol()) + .timestamp(l) + .sign(HashUtils.sha256(l)) + .user_email(username) + .build(); + rabbitTemplate.convertAndSend(PAY_RECHARGE_QUEUE,build); + //记录监听钱包开始时间 + ledgerLogService.logOperation(userWalletDataDto.getFromAddress()+"--"+username+"--"+authId, "-----监听钱包开始------",""); + }catch (Exception e){ + throw new PaymentException("获取钱包信息失败,请刷新重试"); + } + } + + + @Override + @DSTransactional + public Result withdrawBalance(BalanceVo balanceVo) { + String username = SecurityUtils.getUsername(); + Long authId = SecurityUtils.getUserId(); + UserWalletDataDto userWalletDataDto = leaseUserWalletDataMapper. + getWalletInfoByAddress(balanceVo.getFromAddress(),balanceVo.getToSymbol(),username); + if (balanceVo.getAmount().compareTo(BigDecimal.valueOf(2)) < 0){ + return Result.fail("提现失败:提现金额不能小于 2 USDT"); + } + + if (userWalletDataDto == null || StringUtils.isEmpty(userWalletDataDto.getFromAddress())){ + return Result.fail("未查询到需要提现的钱包地址"); + } + //校验地址 + 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("提现收款地址格式不正确"); + } + + //获取未冻结余额 + BigDecimal noBlockedBalance = userWalletDataDto.getBalance().subtract(userWalletDataDto.getBlockedBalance()); + + //校验余额是否足够 + if (noBlockedBalance.compareTo(balanceVo.getAmount()) < 0){ + return Result.fail("提现失败:当前未冻结余额:"+noBlockedBalance+"USDT,需提现金额:"+balanceVo.getAmount()+"USDT"); + } + + GoogleInfo googleInfo = leaseUserMapper.getGoogleInfoByEmail(username); + //开发环境 + //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) + .toChain(balanceVo.getToChain()) + .toSymbol(balanceVo.getToSymbol()) + .fromChain(userWalletDataDto.getFromChain()) + .fromSymbol(userWalletDataDto.getFromSymbol()) + .fromAddress(userWalletDataDto.getFromAddress()) + .serviceCharge(serviceCharge) + .toAddress(balanceVo.getToAddress()) + .amount(balanceVo.getAmount()) + .authId(authId) + .build()); + + LeaseUserWalletData build = LeaseUserWalletData.builder() + .id(userWalletDataDto.getId()) + .blockedBalance(userWalletDataDto.getBlockedBalance().add(balanceVo.getAmount())) + .build(); + //toAddress 为空,或则与这次请求的钱包地址不同,修改表中toAddress 的值为本次请求参数的钱包地址 + if (userWalletDataDto.getToAddress().isEmpty() || !userWalletDataDto.getToAddress().equals(balanceVo.getToAddress())){ + build.setToAddress(balanceVo.getToAddress()); + build.setToChain(balanceVo.getToChain()); + build.setToSymbol(balanceVo.getToSymbol()); + } + //修改钱包冻结余额 乐观锁 + int i = leaseUserWalletDataMapper.update(build, new LambdaUpdateWrapper() + .eq(LeaseUserWalletData::getBlockedBalance, userWalletDataDto.getBlockedBalance())); + if (insert > 0 && i > 0){ + //发送mq消息到队列 + RabbitmqPayWithdrawMessage message = RabbitmqPayWithdrawMessage.builder() + .queue_id(queueId) + .from_address(userWalletDataDto.getFromAddress()) + .to_address(balanceVo.getToAddress()) + .amount(balanceVo.getAmount().subtract(serviceCharge)) + .chain(balanceVo.getToChain()) + .symbol(balanceVo.getToSymbol()) + .fee(serviceCharge) + .timestamp(timestamp) + .sign(HashUtils.sha256(timestamp)) + .user_email(username+"--"+authId) + .build(); + try { + //发送 提现 消息 + rabbitTemplate.convertAndSend(PAY_WITHDRAW_QUEUE,message); + OperationLogAspect.addExtraLogParam("手续费", serviceCharge.toString()); + OperationLogAspect.addExtraLogParam("消息ID", queueId); + return Result.success("提现申请成功"); + } catch (Exception e) { + throw new PaymentException("提现失败,失败原因"+e.getMessage()); + } + } + throw new PaymentException("提现失败,冻结金额已变化,请重新提现!"); + } + + @Override + public PageResult balanceWithdrawList(BalancePageVo balancePageVo) { + + String userEmail = SecurityUtils.getUsername(); + System.out.println("用户"+userEmail+"提现列表"); + List walletList = leaseUserWalletDataMapper.selectList(new LambdaQueryWrapper() + .eq(LeaseUserWalletData::getUserId,userEmail)); + PageHelper.startPage(balancePageVo.getPageNum(), balancePageVo.getPageSize()); + List payWithdrawMessageDtos = leasePayWithdrawMessageMapper + .balanceWithdrawList( + walletList, + balancePageVo.getStatus()); + PageInfo pageInfo = new PageInfo<>(payWithdrawMessageDtos); + System.out.println(userEmail + "用户提现" + payWithdrawMessageDtos); + PageResult success = PageResult.success(payWithdrawMessageDtos); + success.setTotal(pageInfo.getTotal()); + success.setTotalPage(pageInfo.getPages()); + PageHelper.clearPage(); + return success; + } + + @Override + public PageResult balanceRechargeList(BalancePageVo balancePageVo) { + + List walletList = leaseUserWalletDataMapper.selectList(new LambdaQueryWrapper() + .eq(LeaseUserWalletData::getUserId, SecurityUtils.getUsername())); + PageHelper.startPage(balancePageVo.getPageNum(), balancePageVo.getPageSize()); + List payWithdrawMessageDtos = leasePayRechargeMessageMapper.balanceRechargeList(walletList,balancePageVo.getStatus()); + PageInfo pageInfo = new PageInfo<>(payWithdrawMessageDtos); + PageResult success = PageResult.success(payWithdrawMessageDtos); + success.setTotal(pageInfo.getTotal()); + success.setTotalPage(pageInfo.getPages()); + PageHelper.clearPage(); + return success; + } + + @Override + public PageResult balancePayList(BalancePageVo balancePageVo) { + //获取店铺信息 + LeaseShop leaseShop = leaseShopMapper.selectOne(new LambdaQueryWrapper() + .eq(LeaseShop::getUserEmail, SecurityUtils.getUsername()).eq(LeaseShop::getDel, false)); + if (leaseShop == null){ + //不存在店铺,没有交易记录 + return PageResult.success(null); + } + //获取卖家的自定义钱包地址 + List leaseShopConfigs = leaseShopConfigMapper.selectList(new LambdaQueryWrapper() + .eq(LeaseShopConfig::getShopId, leaseShop.getId())); + if(leaseShopConfigs.isEmpty()){ + //,"未绑定钱包,不存在交易流水" + return PageResult.success(null); + } + PageHelper.startPage(balancePageVo.getPageNum(), balancePageVo.getPageSize()); + List payRecordMessageDtos = leasePayRecordMessageMapper.balancePayList(leaseShopConfigs,balancePageVo.getStatus()); + PageInfo pageInfo = new PageInfo<>(payRecordMessageDtos); + PageResult success = PageResult.success(payRecordMessageDtos); + success.setTotal(pageInfo.getTotal()); + success.setTotalPage(pageInfo.getPages()); + PageHelper.clearPage(); + return success; + } + + @Override + public PageResult transactionRecord(RecordTypePageVo recordTypePageVo) { + Integer status = recordTypePageVo.getStatus(); + //先查到用户绑定的所有钱包 + List walletList = leaseUserWalletDataMapper.selectList(new LambdaQueryWrapper() + .eq(LeaseUserWalletData::getAuthId, SecurityUtils.getUserId())); + if (walletList.isEmpty()){ + //"未绑定钱包,不存在交易流水" + return PageResult.success(null); + } + PageHelper.startPage(recordTypePageVo.getPageNum(), recordTypePageVo.getPageSize()); + List transactionRecordDtoList = new ArrayList<>(); + //查询支付记录 + if (status == 0){ + transactionRecordDtoList = leasePayRecordMessageMapper.transactionRecord(walletList); + } + //查询提现记录 + if (status == 1){ + transactionRecordDtoList = leasePayWithdrawMessageMapper.transactionRecord(walletList); + } + //查询充值记录 + if (status == 2){ + transactionRecordDtoList = leasePayRechargeMessageMapper.transactionRecord(walletList); + } + + PageInfo pageInfo = new PageInfo<>(transactionRecordDtoList); + PageResult success = PageResult.success(transactionRecordDtoList); + success.setTotal(pageInfo.getTotal()); + success.setTotalPage(pageInfo.getPages()); + PageHelper.clearPage(); + return success; + } + + @Override + public Result> getRecentlyTransaction() { + List walletList = leaseUserWalletDataMapper.selectList(new LambdaQueryWrapper() + .eq(LeaseUserWalletData::getAuthId, SecurityUtils.getUserId())); + List recentlyTransaction = new ArrayList<>(); + if (!walletList.isEmpty()){ + recentlyTransaction = leasePayRecordMessageMapper.getRecentlyTransaction(walletList); + recentlyTransaction.addAll(leasePayWithdrawMessageMapper.getRecentlyTransaction(walletList)) ; + recentlyTransaction.addAll(leasePayRechargeMessageMapper.getRecentlyTransaction(walletList)); + // 按 updateTime 倒序排序,并取前五个元素 + recentlyTransaction = recentlyTransaction.stream() + .sorted((dto1, dto2) -> dto2.getUpdateTime().compareTo(dto1.getUpdateTime())) + .limit(5) + .collect(Collectors.toList()); + + } + return Result.success(recentlyTransaction); + } + + + @Override + public Result> getCharge() { + List allChargesAsDtoList = getAllChargesAsDtoList(); + return Result.success(allChargesAsDtoList); + } + + public String getAuthCode(String userEmail){ + + System.out.println("获取下载权限码"+userEmail); + LeaseShop leaseShop = leaseShopMapper.selectOne(new LambdaQueryWrapper() + .select(LeaseShop::getUserEmail) + .eq(LeaseShop::getUserEmail, userEmail)); + + if (leaseShop == null){ + throw new RuntimeException("下载失败,下载客户端权限,请重新登录并创建店铺"); + } + // 直接把邮箱作为身份码 + //if (StringUtils.isEmpty(leaseShop.getIdentityCode())) { + // leaseShop.setIdentityCode(UuidGeneratorUtil.generateUuidWithoutHyphen()); + // leaseShopMapper.updateById(leaseShop); + //} + return leaseShop.getUserEmail(); + } + + + @Override + public void downloadClient(String userEmail,String type,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/linux 或 /home/ubuntu/web/client/windows 下所有文件和 auth文件一起压缩 + Path sourceDir = Paths.get(downloadClientLinux); + 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(downloadClientLinux, version); + }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); + } + +} diff --git a/src/main/java/com/m2pool/lease/task/GpuRequestApiTask.java b/src/main/java/com/m2pool/lease/task/GpuRequestApiTask.java new file mode 100644 index 0000000..ee0f832 --- /dev/null +++ b/src/main/java/com/m2pool/lease/task/GpuRequestApiTask.java @@ -0,0 +1,197 @@ +package com.m2pool.lease.task; + +import cn.hutool.http.HttpRequest; +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.m2pool.lease.constant.BlockInterval; +import com.m2pool.lease.entity.LeaseGpuConfig; +import com.m2pool.lease.entity.LeaseMiningSoftwareConfig; +import com.m2pool.lease.mapper.LeaseGpuConfigMapper; +import com.m2pool.lease.mapper.LeaseMiningSoftwareConfigMapper; +import com.m2pool.lease.task.info.*; +import io.lettuce.core.ScriptOutputType; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @Description 获取gpu 信息三方api:whattomine.com 定时任务 + * @Date 2025/12/16 15:38 + * @Author yyb + */ +@Configuration +@EnableScheduling +public class GpuRequestApiTask { + + @Resource + private LeaseGpuConfigMapper leaseGpuConfigMapper; + + + @Resource + private LeaseMiningSoftwareConfigMapper leaseMiningSoftwareConfigMapper; + + @Scheduled(cron = "0 0 0/6 * * ? ") + //@Scheduled(cron = "0 0/5 * * * ? ") + public void gpuInfoTask(){ + List gpuInfos = fetchGpuInfo(); + List leaseGpuConfigs = convertGpuInfoToLeaseConfig(gpuInfos); + List leaseMiningSoftwareConfigs = leaseMiningSoftwareConfigMapper. + selectList(new LambdaQueryWrapper<>()); + //选出挖矿软件只支持的算法和币种 + List leaseGpuConfigs1 = new ArrayList<>(); + for (LeaseGpuConfig leaseGpuConfig : leaseGpuConfigs) { + for (LeaseMiningSoftwareConfig leaseMiningSoftwareConfig : leaseMiningSoftwareConfigs) { + if (leaseMiningSoftwareConfig.getAlgorithm().contains(leaseGpuConfig.getAlgorithm())){ + leaseGpuConfig.setCoin(leaseMiningSoftwareConfig.getCoin()); + leaseGpuConfigs1.add(leaseGpuConfig); + break; + } + } + } + List coinNetPowers = fetchMiningPower(); + if (!coinNetPowers.isEmpty()){ + Map coinPowerMap = coinNetPowers.stream() + .collect(Collectors.toMap(AlgorithmInfo-> AlgorithmInfo.getCoin()+"-"+AlgorithmInfo.getName(), Function.identity())); + for (LeaseGpuConfig leaseGpuConfig : leaseGpuConfigs1) { + AlgorithmInfo coinPower = coinPowerMap.get(leaseGpuConfig.getCoin()+"-"+leaseGpuConfig.getAlgorithm()); + BigDecimal monthIncome = calculateMonthIncome(leaseGpuConfig, coinPower); + leaseGpuConfig.setMonthIncome(monthIncome); + } + + } + leaseGpuConfigMapper.insertOrUpdateBatchByEntity(leaseGpuConfigs1); + } + + /** + * 计算月收入 + * @param leaseGpuConfig + * @param coinPower + * @return + */ + private static BigDecimal calculateMonthIncome(LeaseGpuConfig leaseGpuConfig, AlgorithmInfo coinPower) { + BigDecimal monthIncome = BigDecimal.ZERO; + if (coinPower != null){ + monthIncome = leaseGpuConfig.getHashrate() + .divide(coinPower.getHashrate().divide(BigDecimal.valueOf(1000 * 1000), 8, RoundingMode.HALF_UP), 8, RoundingMode.HALF_UP) + .multiply(BigDecimal.valueOf(24 * 60 * 60) + .divide(coinPower.getBlockInterval(), 8, RoundingMode.HALF_UP)) + .multiply(coinPower.getBlockReward()).multiply(BigDecimal.valueOf(30)) + .multiply(coinPower.getPrice()) + ; + } + return monthIncome; + } + + + /** + * 三方api 获取gpu信息 + * @return + */ + public List fetchGpuInfo() { + String url = "https://whattomine.com/api/v1/gpus?api_token=8ea12738081eaa70a37d62364ddfffecc0c96a6047f02b4cc728af7588fcdbd8"; + String response = HttpRequest.get(url).execute().body(); + + // 将JSONUtil.parseArray改为JSON.parseArray + JSONArray gpusArray = JSON.parseArray(response); + List gpuInfoList = new ArrayList<>(); + + for (Object obj : gpusArray) { + // JSONObject转换方式 + JSONObject gpuJson = (JSONObject) obj; + GpuInfo gpuInfo = new GpuInfo(); + + // 设置基本GPU信息 + gpuInfo.setId(gpuJson.getLong("id")); + gpuInfo.setName(gpuJson.getString("name")); + gpuInfo.setRelease_date(gpuJson.getString("release_date")); + + // 处理算法信息 + JSONArray algorithmsArray = gpuJson.getJSONArray("algorithms"); + List algorithmInfos = new ArrayList<>(); + + for (Object algoObj : algorithmsArray) { + // JSONObject转换方式 + JSONObject algoJson = (JSONObject) algoObj; + AlgorithmInfo algorithmInfo = new AlgorithmInfo(); + + algorithmInfo.setName(algoJson.getString("name")); + algorithmInfo.setHashrate(algoJson.getBigDecimal("hashrate")); + algorithmInfo.setPower(algoJson.getBigDecimal("power")); + + algorithmInfos.add(algorithmInfo); + } + + gpuInfo.setAlgorithms(algorithmInfos); + gpuInfoList.add(gpuInfo); + } + + return gpuInfoList; + } + + + public List convertGpuInfoToLeaseConfig(List gpuInfos) { + List leaseGpuConfigs = new ArrayList<>(); + + for (GpuInfo gpuInfo : gpuInfos) { + if (gpuInfo.getAlgorithms() != null) { + for (AlgorithmInfo algorithm : gpuInfo.getAlgorithms()) { + LeaseGpuConfig config = buildGpuConfig(gpuInfo, algorithm); + leaseGpuConfigs.add(config); + } + } + } + + return leaseGpuConfigs; + } + + private static LeaseGpuConfig buildGpuConfig(GpuInfo gpuInfo, AlgorithmInfo algorithm) { + LeaseGpuConfig config = new LeaseGpuConfig(); + config.setName(gpuInfo.getName()); + config.setAlgorithm(algorithm.getName()); + config.setHashrate(algorithm.getHashrate().divide(BigDecimal.valueOf(1000 * 1000),2, RoundingMode.HALF_UP)); + config.setPowerDissipation(algorithm.getPower()); + return config; + } + + + /** + * 三方api 获取gpu 对应算法的收益信息 + * @return + */ + public static List fetchMiningPower() { + String url = "https://whattomine.com/api/v1/coins?api_token=8ea12738081eaa70a37d62364ddfffecc0c96a6047f02b4cc728af7588fcdbd8"; + String response = HttpRequest.get(url).execute().body(); + JSONArray gpusArray = JSON.parseArray(response); + List hashRateInfos = new ArrayList<>(); + for (int i = 0; i < gpusArray.size(); i++) { + JSONObject item = gpusArray.getJSONObject(i); + AlgorithmInfo info = new AlgorithmInfo(); + info.setCoin(item.getString("tag")); + info.setName(item.getString("algorithm")); + info.setHashrate(new BigDecimal(item.getLong("nethash"))); + info.setBlockReward(BigDecimal.valueOf(item.getDouble("block_reward"))); + info.setBlockInterval(BigDecimal.valueOf(item.getDouble("block_time"))); + // 解析exchanges数组并获取第一个对象的price值 + JSONArray exchanges = item.getJSONArray("exchanges"); + if (!exchanges.isEmpty()) { + JSONObject firstExchange = exchanges.getJSONObject(0); + //30日平均币价 + info.setPrice(BigDecimal.valueOf(firstExchange.getDouble("price30")).multiply(BigDecimal.valueOf(100000))); + } + hashRateInfos.add(info); + } + return hashRateInfos; + } +} diff --git a/src/main/java/com/m2pool/lease/task/OrderAndPayTask.java b/src/main/java/com/m2pool/lease/task/OrderAndPayTask.java new file mode 100644 index 0000000..a25df55 --- /dev/null +++ b/src/main/java/com/m2pool/lease/task/OrderAndPayTask.java @@ -0,0 +1,1081 @@ +package com.m2pool.lease.task; + +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.m2pool.lease.aspect.LedgerLogService; +import com.m2pool.lease.constant.PowerUnit; +import com.m2pool.lease.dto.*; +import com.m2pool.lease.dto.v2.PurchasedMachineListDto; +import com.m2pool.lease.dto.v2.RealHashrateInfoDto; +import com.m2pool.lease.entity.*; +import com.m2pool.lease.mapper.*; +import com.m2pool.lease.mq.message.RabbitmqDeleteWalletMessage; +import com.m2pool.lease.mq.message.RabbitmqPayAutoMessage; +import com.m2pool.lease.service.LeaseOrderItemService; +import com.m2pool.lease.service.LeasePayRecordMessageInfoService; +import com.m2pool.lease.service.LeasePayRecordMessageService; +import com.m2pool.lease.service.LeaseUserOwnedProductService; +import com.m2pool.lease.utils.DateUtils; +import com.m2pool.lease.utils.HashUtils; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static com.m2pool.lease.constant.RabbitmqConstant.DELETE_WALLET_QUEUE; +import static com.m2pool.lease.constant.RabbitmqConstant.PAY_AUTO_QUEUE; + +/** + * @Description 订单超时 定时任务 + * @Date 2025/7/15 15:45 + * @Author yyb + */ +@Configuration +@EnableScheduling +public class OrderAndPayTask { + + + @Resource + private RabbitTemplate rabbitTemplate; + + @Resource + private LeaseOrderInfoMapper leaseOrderInfoMapper; + @Resource + private LeaseOrderItemMapper leaseOrderItemMapper; + @Resource + private LeaseProductMachineMapper leaseProductMachineMapper; + @Resource + private LeaseUserWalletDataMapper leaseUserWalletDataMapper; + @Resource + private LeasePayRecordMessageService leasePayRecordMessageService; + @Resource + private LeasePayRecordMessageMapper leasePayRecordMessageMapper; + @Resource + private LeasePayWithdrawMessageMapper leasePayWithdrawMessageMapper; + @Resource + private LeasePayRechargeMessageMapper leasePayRechargeMessageMapper; + @Resource + private LeaseUserOwnedProductService leaseUserOwnedProductService; + @Resource + private LeaseAutoAddressMapper leaseAutoAddressMapper; + @Resource + private LeaseShopConfigMapper leaseShopConfigMapper; + @Resource + private LeaseShopMapper leaseShopMapper; + @Resource + private LeaseOrderItemService leaseOrderItemService; + @Resource + private LeasePayRecordMessageInfoService leasePayRecordMessageInfoService; + @Resource + private LeaseMachineTempConfigMapper leaseMachineTempConfigMapper; + @Resource + private LeaseOrderMiningMapper leaseOrderMiningMapper; + @Resource + private LeaseMachineMapper leaseMachineMapper; + @Resource + private LedgerLogService ledgerLogService; + + /** + * 检查钱包半年内是否有 支付,充值,提现操作 + */ + @Scheduled(cron = "0 5 1 * * ? ") + //@Scheduled(cron = "0 0/1 * * * ? ") + @Transactional + public void checkOperator(){ + //1.查询lease_auto_address 表 状态为1 已使用 的钱包 + List validAddress = leaseAutoAddressMapper.getValidAddress(); + if (validAddress.isEmpty()){ + return; + } + //2.获取创建时间大于半年,且余额为0的钱包 + List walletForBalanceIsZero = leaseUserWalletDataMapper.getWalletForBalanceIsZero(validAddress); + if (walletForBalanceIsZero.isEmpty()){ + return; + } + //3.根据钱包查询lease_pay_record_message,lease_pay_recharge_message,lease_pay_withdraw_message 三表半年内是否有记录 + Map payRecordMessageMap = leasePayRecordMessageMapper.checkPayOperatorBatch(walletForBalanceIsZero); + Map withdrawMessageMap = leasePayWithdrawMessageMapper.checkWithdrawOperatorBatch(walletForBalanceIsZero); + Map rechargeMessageMap = leasePayRechargeMessageMapper.checkRechargeOperatorBatch(walletForBalanceIsZero); + + //3.如果没有记录,则修改钱包的状态为 2已过期 + for (LeaseUserWalletData userWalletDataDto : walletForBalanceIsZero) { + String address = userWalletDataDto.getFromAddress(); + CheckAddressDto payRecordMessage = payRecordMessageMap.get(address); + CheckAddressDto withdrawMessage = withdrawMessageMap.get(address); + CheckAddressDto rechargeMessage = rechargeMessageMap.get(address); + + if (payRecordMessage == null && withdrawMessage == null && rechargeMessage == null){ + //发送钱包过期删除监听mq消息 + long l = System.currentTimeMillis()/1000L; + RabbitmqDeleteWalletMessage build = RabbitmqDeleteWalletMessage.builder() + .msg_type(0) + .queue_id(userWalletDataDto.getQueueId()) + .chain(userWalletDataDto.getFromChain()) + .symbol(userWalletDataDto.getFromSymbol()) + .address(userWalletDataDto.getFromAddress()) + .timestamp(l) + .sign(HashUtils.sha256(l)).build(); + rabbitTemplate.convertAndSend(DELETE_WALLET_QUEUE, build); + System.out.println("发送钱包过期删除监听mq消息成功"); + } + } + + } + + @Scheduled(cron = "0 0/1 * * * ? ") + @DSTransactional + public void paymentTaskV2(){ + + Date now = DateUtils.getPreviousHalfHourOrFullHour(new Date()); + //开发环境 + LocalDateTime now1 = LocalDateTime.now(); + //1.查找在进行中的订单 + List needPayOrderList = leaseOrderInfoMapper.getNeedPayOrderList(); + if (needPayOrderList.isEmpty()){ + System.out.println("没有进行中的订单"); + return; + } + List infoIds = new ArrayList<>(); + Map queueIdMap = new HashMap<>(); + for (LeaseOrderInfo leaseOrderInfo : needPayOrderList) { + queueIdMap.put(leaseOrderInfo.getId(), leaseOrderInfo); + infoIds.add(leaseOrderInfo.getId()); + } + + //2.根据订单号找到对应的订单详情(状态为1 租约生效中) ,并根据这些订单详情找到每个矿机的信息 + List leaseOrderItems = leaseOrderItemMapper.selectList(new LambdaQueryWrapper() + .eq(LeaseOrderItem::getStatus,1) + .in(LeaseOrderItem::getOrderId, infoIds)); + + //本次全部完成的订单id 集合 + List orderComplete = new ArrayList<>(); + //本次部分已完成订单详情id + 矿机 集合 + List itemCompleteIds = new ArrayList<>(); + + List completeMachines = new ArrayList<>(); + //本此需支付订单详情id 集合 + List needPayIds = new ArrayList<>(); + //本次需支付订单详情 集合 + List needPayInfos = new ArrayList<>(); + getCompleteOrderAndNeedPayOrder(now1,orderComplete,completeMachines,itemCompleteIds,needPayIds,needPayInfos,leaseOrderItems); + //订单进行过程中,卖方可能修改收款地址。所以每天支付前,需要修改旧收款地址为新地址 + needPayInfos = updateOrderItemSellerWalletAddress(needPayInfos); + + //1.处理需要支付的订单 + //根据 lease_order_item 中订单id 查找到 对应的lease_order_mining 信息 + List miningInfoByOrderId = leaseOrderMiningMapper.getMiningInfoByOrderId(needPayIds); + //根据lease_order_mining 中相关信息 钱包 + 币种 + 算法 + 矿工号 + 矿池名 找到对应的实时算力 + if (!miningInfoByOrderId.isEmpty()){ + Map> collect = miningInfoByOrderId.stream().collect(Collectors.groupingBy(PurchasedMachineListDto::getPool)); + + //挖矿信息 与 订单详情id 映射 + Map miningAnditemIdMap = miningInfoByOrderId.stream() + .collect(Collectors.toMap( + dto -> dto.getPool() + "_" + dto.getWalletAddress() + "_" + + dto.getWorkerId() + "_" + dto.getCoin() + "_" + dto.getAlgorithm(), + Function.identity(), + (existing, replacement) -> replacement + )); + + List realPowerList = new ArrayList<>(); + collect.forEach((pool, list) -> { + realPowerList.addAll(leaseOrderMiningMapper.getRecently24HourHashrate(list, pool, now)); + }); + //挖矿信息 与 实时算力 映射 + Map> groupedData = realPowerList.stream() + .collect(Collectors.groupingBy( + dto -> dto.getPool() + "_" + dto.getWalletAddress() + "_" + + dto.getMiner() + "_" + dto.getCoin() + "_" + dto.getAlgorithm() + )); + + //订单id 与 实时算力 映射 + Map> orderIdToHashrateMap = new HashMap<>(); + getOrderItemIdToHashrateMap(orderIdToHashrateMap,miningAnditemIdMap,groupedData); + + //根据实时算力波动情况 求得订单实际支付金额 + Map orderIdToPriceMap = needPayInfos.stream().collect(Collectors.toMap(LeaseOrderItem::getId,Function.identity())); + Map orderItemToPriceMap = orderPaymentWaveAlgorithm(orderIdToHashrateMap,orderIdToPriceMap); + + Map> userMapItem = needPayInfos.stream() + .collect(Collectors.groupingBy(LeaseOrderItem::getOrderId)); + + List recordMessageInfoList = new ArrayList<>(); + List saleIngList = new ArrayList<>(); + // 按照订单分组 + userMapItem.forEach((orderId, items) -> { + LeaseOrderItem leaseOrderItem = items.get(0); + //因为同一个订单的queueId相同,所以这里直接使用订单id + LeaseOrderInfo orderInfo = queueIdMap.get(orderId); + //买方信息 + LeasePayRecordMessageInfo build = LeasePayRecordMessageInfo.builder() + .orderId(String.valueOf(orderId)) + .orderNumber(orderInfo.getOrderNumber()) + .queueId(orderInfo.getOrderNumber()) + .fromAddress(leaseOrderItem.getFromAddress()) + .toAddress(leaseOrderItem.getAddress()) + .amount(BigDecimal.ZERO) + .realAmount(BigDecimal.ZERO) + .needAmount(BigDecimal.ZERO) + .blockAmount(BigDecimal.ZERO) + .fromChain(leaseOrderItem.getChain()) + .fromSymbol(leaseOrderItem.getSymbol()) + .shopId(leaseOrderItem.getShopId()) + .userId(leaseOrderItem.getUserId()) + .toChain(leaseOrderItem.getChain()) + .toSymbol(leaseOrderItem.getSymbol()) + .authId(orderInfo.getAuthId()) + .build(); + + for (LeaseOrderItem item : items) { + BigDecimal realAmount = orderItemToPriceMap.get(item.getId()); + BigDecimal realPayAmount = realAmount== null ? BigDecimal.ZERO : realAmount; + //已支付金额 和实际待支付金额 + item.setAlreadyPayAmount(item.getAlreadyPayAmount().add(item.getPrice().multiply(BigDecimal.valueOf(item.getNumbers())))); + item.setSettlePayRealAmount(item.getSettlePayRealAmount().add(realPayAmount)); + //设置理论支付金额 和 冻结金额 额 (冻结金额 = 理论支付金额) + build.setBlockAmount(build.getBlockAmount().add(item.getPrice().multiply(BigDecimal.valueOf(item.getNumbers())))); + build.setAmount(build.getAmount().add(item.getPrice().multiply(BigDecimal.valueOf(item.getNumbers())))); + // 实际支付金额 + 理论实际应支付金额 + build.setRealAmount(build.getRealAmount().add(realPayAmount)); + build.setNeedAmount(build.getNeedAmount().add(realPayAmount)); + saleIngList.add(item); + } + //实际支付金额即使为0也要记录 + recordMessageInfoList.add(build); + //实际支付不为0才记录 + //if (build.getRealAmount().compareTo(BigDecimal.ZERO) > 0){ + // recordMessageInfoList.add(build); + //} + }); + + //修改订单支付金额等相关信息 + leaseOrderItemService.updateBatchById(saleIngList); + //保存支付信息到支付记录表中 + if (!recordMessageInfoList.isEmpty()){ + leasePayRecordMessageInfoService.saveBatch(recordMessageInfoList); + } + } + //2.处理完成的订单 (订单部分完成) + if(!itemCompleteIds.isEmpty()){ + handlerOrderPartComplete(itemCompleteIds,completeMachines); + } + + //3.处理完成的订单 + 发送mq消息去支付(订单全部完成) + if (!orderComplete.isEmpty()){ + handlerOrderAllComplete(orderComplete); + } + } + + + /** + * 获取订单完成和需要支付的订单id + * @param orderComplete 订单是否完成(所有子订单完成) + * @param completeIds 本次完成子订单 id集合 + * @param needPayIds 本次需要支付的订单 id集合 + * @param needPayInfos 本次需要支付的订单信息(租赁数量,租赁天数,单价等) + * @param oneDayApartOrder + */ + public void getCompleteOrderAndNeedPayOrder(LocalDateTime now,List orderComplete,List completeMachines,List completeIds,List needPayIds,List needPayInfos,List oneDayApartOrder){ + + Map orderCompleteMap = new HashMap<>(); + for (LeaseOrderItem orderTimeInfoDto : oneDayApartOrder) { + orderCompleteMap.putIfAbsent(orderTimeInfoDto.getOrderId(), true); + // 计算订单结束时间(startTime + leaseTime天) + //LocalDateTime endTime = orderTimeInfoDto.getCreateTime().plusDays(orderTimeInfoDto.getLeaseTime()); + + //TODO 开发环境测试用1分钟当一天 差值改为差1分钟 + LocalDateTime endTime = orderTimeInfoDto.getCreateTime().plusMinutes(orderTimeInfoDto.getLeaseTime()); + + // 获取订单完成的订单详情:比较 now 和 endTime 的年月日时分秒差值是否小于30分 + if (Duration.between(now, endTime).toMinutes() <= 1) { + completeMachines.add(LeaseMachine.builder() + .id(orderTimeInfoDto.getProductMachineId()) + .canSaleNumbers(orderTimeInfoDto.getNumbers()) + .saleOutNumbers(orderTimeInfoDto.getNumbers()) + .saleState(false) + .build()); + completeIds.add(orderTimeInfoDto.getId()); + needPayIds.add(orderTimeInfoDto.getId()); + needPayInfos.add(orderTimeInfoDto); + continue; + } + // 获取本次定时任务需要支付的订单:当前时间和订单创建时间日期不同,比较当前时间的时分秒与订单创建时间的时分秒差值是否小于30分钟 + //LocalTime nowTime = now.toLocalTime(); + //LocalTime startTime = orderTimeInfoDto.getCreateTime().toLocalTime(); + //LocalDate nowDate = now.toLocalDate(); + //LocalDate startDate = orderTimeInfoDto.getCreateTime().toLocalDate(); + // + //if (!nowDate.equals(startDate) && Duration.between(startTime, nowTime).toMinutes() <= 30) { + // needPayIds.add(orderTimeInfoDto.getId()); + // needPayInfos.add(orderTimeInfoDto); + //} + + //开发环境 + needPayIds.add(orderTimeInfoDto.getId()); + needPayInfos.add(orderTimeInfoDto); + //记录某个订单下所有子项目是否都完成 + orderCompleteMap.put(orderTimeInfoDto.getOrderId(), false); + } + orderCompleteMap.forEach( + (orderId, complete) -> { + if (complete) { + orderComplete.add(orderId); + } + } + ); + } + + + /** + * 获取各orderItemId 所需支付的金额 + * @param orderIdToHashrateMap 订单id 与算力的映射 + * @param orderIdToPriceMap 订单id 与价格映射 + * @return + */ + public Map orderPaymentWaveAlgorithm(Map> orderIdToHashrateMap, Map orderIdToPriceMap){ + + Map orderItemIdToWaveMap = new HashMap<>(); + + orderIdToHashrateMap.forEach((orderItemId, realPowerList) -> { + + LeaseOrderItem itemInfo = orderIdToPriceMap.get(orderItemId); + + + // 计算每个数据点 理论算力的比值 + List ratios = realPowerList.stream() + .map(dto -> dto.getPower() + .divide(itemInfo.getTheoryPower(), 4, RoundingMode.HALF_UP)) + .collect(Collectors.toList()); + + // 补零到48个数据点 + while (ratios.size() < 48) { + ratios.add(BigDecimal.ZERO); + } + + // 计算总和并除以48 + BigDecimal finalValue = ratios.stream() + .reduce(BigDecimal.ZERO, BigDecimal::add) + .divide(BigDecimal.valueOf(48), 2, RoundingMode.HALF_UP); + + + + BigDecimal payAmount = itemInfo.getPrice() + .multiply(BigDecimal.valueOf(itemInfo.getNumbers())); + if (finalValue.compareTo(BigDecimal.valueOf(0.95)) <= 0){ + payAmount = payAmount.multiply(finalValue); + } + orderItemIdToWaveMap.put(orderItemId, payAmount); + }); + return orderItemIdToWaveMap; + } + + /** + * 获取订单项id 与实时算力映射 + **/ + public void getOrderItemIdToHashrateMap(Map> orderIdToHashrateMap,Map miningAnditemIdMap,Map> groupedData){ + for (Map.Entry> entry : groupedData.entrySet()) { + String key = entry.getKey(); + List hashrateList = entry.getValue(); + + // 从第一个映射中获取对应的订单信息 + PurchasedMachineListDto orderInfo = miningAnditemIdMap.get(key); + if (orderInfo != null) { + // 将订单ID与实时算力关联起来 + orderIdToHashrateMap.put(orderInfo.getOrderItemId(), hashrateList); + } + } + + } + + /** + * 处理订单全部完成 + * @param orderIds 订单子项全部完成的订单id + */ + public void handlerOrderAllComplete(List orderIds){ + //修改状态为已完成 + leaseOrderInfoMapper.update(LeaseOrderInfo.builder().status(8).build(), new LambdaQueryWrapper().in(LeaseOrderInfo::getId, orderIds)); + //老版本:发送支付消息 + sendMessageToMq(orderIds); + } + + /** + * 处理订单部分完成 + * @param itemIds 已完成订单项id + * @param completeMachines 已完成机器id + */ + public void handlerOrderPartComplete(List itemIds,List completeMachines){ + //修改订单详情表中状态为 0 租约已过期 + leaseOrderItemMapper.update(LeaseOrderItem.builder().status(0).build(), new LambdaUpdateWrapper() + .in(LeaseOrderItem::getId, itemIds)); + //修改商品库存矿机表 矿机状态为 0 未售出 + leaseMachineMapper.updateBatchSaleNumbers(completeMachines); + //挖矿订单信息 改为状态0 租约已过期 + leaseOrderMiningMapper.update(LeaseOrderMining.builder().status(false).build(), new LambdaUpdateWrapper() + .in(LeaseOrderMining::getOrderItemId, itemIds)); + } + + /** + * 支付 定时任务 + */ + //@Scheduled(cron = "0 5 0 * * ? ") + //@Scheduled(cron = "0 0/2 * * * ? ") + @DSTransactional + public void paymentTask(){ + // 获取当天 0 点的 时间 + LocalDateTime now = LocalDateTime.now(); + LocalDateTime startOfDay = now.toLocalDate().atStartOfDay(); + //1.查找在进行中的订单 + List needPayOrderList = leaseOrderInfoMapper.getNeedPayOrderList(); + if (needPayOrderList.isEmpty()){ + System.out.println("没有进行中的订单"); + return; + } + List infoIds = new ArrayList<>(); + Map queueIdMap = new HashMap<>(); + for (LeaseOrderInfo leaseOrderInfo : needPayOrderList) { + queueIdMap.put(leaseOrderInfo.getId(), leaseOrderInfo); + infoIds.add(leaseOrderInfo.getId()); + } + + //2.根据订单号找到对应的订单详情(状态为1 租约生效中) ,并根据这些订单详情找到每个矿机的信息 + List leaseOrderItems = leaseOrderItemMapper.selectList(new LambdaQueryWrapper() + .eq(LeaseOrderItem::getStatus,1) + .in(LeaseOrderItem::getOrderId, infoIds)); + //订单进行过程中,卖方可能修改收款地址。所以每天支付前,需要修改旧收款地址为新地址 + leaseOrderItems = updateOrderItemSellerWalletAddress(leaseOrderItems); + + List machineIdList = leaseOrderItems.stream().map(LeaseOrderItem::getProductMachineId).collect(Collectors.toList()); + List leaseProductMachines = leaseProductMachineMapper.selectBatchIds(machineIdList); + + Map> coinAndMachineMap = leaseProductMachines.stream().collect(Collectors.groupingBy(LeaseProductMachine::getCoin)); + Map idAndMachineMap = leaseProductMachines.stream() + .collect(Collectors.toMap(LeaseProductMachine::getId, Function.identity())); + + List productMachineList = new ArrayList<>(); + //2.1 查询到矿机集合后,按挖矿币种分组-----> 并查询每一组矿机的24小时的总算力 + coinAndMachineMap.forEach((coin, machines) -> { + productMachineList.addAll(leaseProductMachineMapper.getRecentlyFiveMinutesData(leaseProductMachines, coin)); + }); + Map machineDtoMap = productMachineList.stream() + .collect(Collectors.toMap( + dto -> dto.getUser() + "-" + dto.getMiner(), + dto -> dto + )); + //2.2 订单详情集合 按照 order_id 分组 + Map> userMapItem = leaseOrderItems.stream() + .collect(Collectors.groupingBy(LeaseOrderItem::getOrderId)); + //.collect(Collectors.groupingBy( + // item-> item.getFromAddress() + "-" + item.getChain()+"-" + item.getSymbol() + //)); + + //3.筛选出租期到期的商品 + List expireProductList = new ArrayList<>(); + List recordMessageInfoList = new ArrayList<>(); + Map orderInfoIdAndIsComplete = new HashMap<>(); + List saleIngList = new ArrayList<>(); + // 按照订单分组 + userMapItem.forEach((orderId, items) -> { + LeaseOrderItem leaseOrderItem = items.get(0); + List expire = new ArrayList<>(); + //因为同一个订单的queueId相同,所以这里直接使用订单id + LeaseOrderInfo orderInfo = queueIdMap.get(leaseOrderItem.getOrderId()); + //买方信息 + LeasePayRecordMessageInfo build = LeasePayRecordMessageInfo.builder() + .orderId(String.valueOf(orderId)) + .orderNumber(orderInfo.getOrderNumber()) + .queueId(orderInfo.getOrderNumber()) + .fromAddress(leaseOrderItem.getFromAddress()) + .toAddress(leaseOrderItem.getAddress()) + .amount(BigDecimal.ZERO) + .realAmount(BigDecimal.ZERO) + .needAmount(BigDecimal.ZERO) + .blockAmount(BigDecimal.ZERO) + .fromChain(leaseOrderItem.getChain()) + .fromSymbol(leaseOrderItem.getSymbol()) + .shopId(leaseOrderItem.getShopId()) + .userId(leaseOrderItem.getUserId()) + .toChain(leaseOrderItem.getChain()) + .toSymbol(leaseOrderItem.getSymbol()) + .build(); + + + for (LeaseOrderItem item : items) { + orderInfoIdAndIsComplete.putIfAbsent(item.getOrderId(), true); + LocalDateTime expireTime = item.getCreateTime().plusDays(1).toLocalDate().atStartOfDay().plusDays(item.getLeaseTime()); + //开发环境 + LocalDateTime a = item.getCreateTime().plusMinutes(item.getLeaseTime() * 2); + // 租期已过 + if (startOfDay.isAfter(expireTime) /**开发环境*/|| now.isAfter(a)) { + item.setStatus(0); + expire.add(item); + }else{ + orderInfoIdAndIsComplete.put(item.getOrderId(), false); + //根据算力波动实时计算需支付金额 + String key = item.getUser() + "-" + item.getMiner(); + LeaseProductMachine machine = idAndMachineMap.get(item.getProductMachineId()); + ProductMachineDto productMachineDto = machineDtoMap.get(key); + build.setAmount(build.getAmount().add(item.getPrice())); + build.setBlockAmount(build.getBlockAmount().add(item.getPrice())); + if (productMachineDto != null){ + //理论算力 + BigDecimal theoryPower = machine.getTheoryPower().multiply(PowerUnit.getPower(machine.getUnit())) + .divide(BigDecimal.valueOf(1000 * 1000),2,RoundingMode.HALF_UP); + //实际算力 + BigDecimal realPower = productMachineDto.getComputingPower() + .divide(BigDecimal.valueOf(24 * 60 * 60), 6, RoundingMode.HALF_UP); + //设置实际支付金额 + BigDecimal divide = theoryPower.subtract(realPower).divide(theoryPower, 10, RoundingMode.HALF_UP); + //设置实际需要发送消息去支付的各订单金额和总金额 + if (divide.compareTo(BigDecimal.valueOf(0.05)) > 0){ + BigDecimal add = item.getPrice().multiply(BigDecimal.ONE.subtract(divide)); + item.setSettlePayRealAmount(item.getSettlePayRealAmount().add(add)); + build.setRealAmount(build.getRealAmount().add(add)); + build.setNeedAmount(build.getNeedAmount().add(add)); + }else{ + BigDecimal add = item.getPrice(); + item.setSettlePayRealAmount(item.getSettlePayRealAmount().add(add)); + build.setRealAmount(build.getRealAmount().add(add)); + build.setNeedAmount(build.getNeedAmount().add(add)); + } + } + item.setAlreadyPayAmount(item.getAlreadyPayAmount().add(item.getPrice())); + + saleIngList.add(item); + } + } + + + ////TODO 下面这个是一个订单 一个买家对应多个卖家(多个收款地址)的情况 + ////封装卖方收款信息 + //Map sellerPayInfoMessages = new HashMap<>(); + ////买方收款信息 相同订单最后整合成一个卖方信息 + //Map> orderIdsMap = items.stream().collect(Collectors.groupingBy(LeaseOrderItem::getAddress)); + //orderIdsMap.forEach((toAddress, orderItemList) -> { + // LeaseOrderItem orderItem = orderItemList.get(0); + // RabbitmqPayAutoInfoMessage sellInfo = RabbitmqPayAutoInfoMessage.builder() + // .order_id(orderItem.getOrderId().toString()) + // .to_address(toAddress) + // .amount(BigDecimal.ZERO) + // .blockAmount(BigDecimal.ZERO) + // .needAmount(BigDecimal.ZERO) + // .shopId(orderItem.getShopId()) + // .userId(orderItem.getUserId()) + // .build(); + // for (LeaseOrderItem item : orderItemList) { + // orderInfoIdAndIsComplete.putIfAbsent(item.getOrderId(), true); + // LocalDateTime expireTime = item.getCreateTime().plusDays(1).toLocalDate().atStartOfDay().plusDays(item.getLeaseTime()); + // //开发环境 + // LocalDateTime a = item.getCreateTime().plusMinutes(item.getLeaseTime() * 2); + // // 租期已过 + // if (startOfDay.isAfter(expireTime) /**开发环境*/|| now.isAfter(a)) { + // item.setStatus(0); + // expire.add(item); + // }else{ + // orderInfoIdAndIsComplete.put(item.getOrderId(), false); + // //根据算力波动实时计算需支付金额 + // String key = item.getUser() + "-" + item.getMiner(); + // LeaseProductMachine machine = idAndMachineMap.get(item.getProductMachineId()); + // ProductMachineDto productMachineDto = machineDtoMap.get(key); + // sellInfo.setNeedAmount(sellInfo.getNeedAmount().add(item.getPrice())); + // sellInfo.setBlockAmount(sellInfo.getBlockAmount().add(item.getPrice())); + // if (productMachineDto != null){ + // //理论算力 + // BigDecimal theoryPower = machine.getTheoryPower().multiply(PowerUnit.getPower(machine.getUnit())) + // .divide(BigDecimal.valueOf(1000 * 1000),2,RoundingMode.HALF_UP); + // //实际算力 + // BigDecimal realPower = productMachineDto.getComputingPower() + // .divide(BigDecimal.valueOf(24 * 60 * 60), 6, RoundingMode.HALF_UP); + // //设置实际支付金额 + // BigDecimal divide = theoryPower.subtract(realPower).divide(theoryPower, 10, RoundingMode.HALF_UP); + // //设置实际需要发送消息去支付的各订单金额和总金额 + // if (divide.compareTo(BigDecimal.valueOf(0.05)) > 0){ + // BigDecimal add = item.getPrice().multiply(BigDecimal.ONE.subtract(divide)); + // item.setSettlePayRealAmount(add); + // sellInfo.setAmount(sellInfo.getAmount().add(add)); + // build.setTotal_amount(build.getTotal_amount().add(add)); + // }else{ + // BigDecimal add = item.getPrice(); + // item.setSettlePayRealAmount(add); + // build.setTotal_amount(build.getTotal_amount().add(add)); + // sellInfo.setAmount(sellInfo.getAmount().add(add)); + // } + // } + // item.setAlreadyPayAmount(item.getAlreadyPayAmount().add(item.getPrice())); + // + // saleIngList.add(item); + // } + // } + // sellerPayInfoMessages.put(sellInfo.getTo_address(), sellInfo); + // + //}); + //build.setTransactions(sellerPayInfoMessages); + if (build.getAmount().compareTo(BigDecimal.ZERO) > 0){ + recordMessageInfoList.add(build); + } + expireProductList.addAll(expire); + }); + + + //4.保存支付消息到支付记录表中 + if (!recordMessageInfoList.isEmpty()){ + leasePayRecordMessageInfoService.saveBatch(recordMessageInfoList); + } + + //5.租约到期相关信息修改 + if (!expireProductList.isEmpty()){ + List itemIds = new ArrayList<>(); + List machineIds = new ArrayList<>(); + for (LeaseOrderItem leaseOrderItem : expireProductList) { + itemIds.add(leaseOrderItem.getId()); + machineIds.add(leaseOrderItem.getProductMachineId()); + } + //5.1 修改订单详情表中状态为 0 租约已过期 + leaseOrderItemMapper.update(LeaseOrderItem.builder().status(0).build(), new LambdaUpdateWrapper() + .in(LeaseOrderItem::getId, itemIds)); + //5.2 修改商品库存矿机表 矿机状态为 0 未售出 + leaseProductMachineMapper.update(LeaseProductMachine.builder().saleState(0).build(), new LambdaUpdateWrapper() + .in(LeaseProductMachine::getId, machineIds)); + //5.3 修改用户已购矿机表状态为 1 套餐已过期 + List list = leaseUserOwnedProductService.list(new LambdaQueryWrapper() + .select(LeaseUserOwnedProduct::getId,LeaseUserOwnedProduct::getSettleIncome,LeaseUserOwnedProduct::getCurrentIncome).in(LeaseUserOwnedProduct::getProductMachineId, machineIds)); + List collect = list.stream().map(item -> { + item.setCurrentIncome(item.getSettleIncome()); + item.setSettleIncome(BigDecimal.ZERO); + //item.setDel(true); + item.setStatus(1); + return item; + }).collect(Collectors.toList()); + leaseUserOwnedProductService.updateBatchById(collect); + + //5.4 矿机对应商品 销售量修改 + //Map productIdAndCountMap = expireProductList.stream().collect(Collectors.groupingBy(LeaseOrderItem::getProductId, Collectors.counting())); + //Set longs = productIdAndCountMap.keySet(); + //List leaseProducts = leaseProductService.list(new LambdaQueryWrapper().in(LeaseProduct::getId, longs)); + //leaseProducts.forEach(leaseProduct -> { + // leaseProduct.setSaleNumber(leaseProduct.getSaleNumber() - productIdAndCountMap.get(leaseProduct.getId()).intValue()); + //}); + //leaseProductService.updateBatchById(leaseProducts); + + //5.5 找到详情表所有状态为0 租约已过期 ,并且订单info表订单状态为7 进行中的订单 并改为状态8 订单已完成 ; 并且发送支付消息 + List orderIds = orderInfoIdAndIsComplete.entrySet().stream() + .filter(entry -> Boolean.TRUE.equals(entry.getValue())) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + if(!orderIds.isEmpty()){ + //订单状态改为已完成 + leaseOrderInfoMapper.update(LeaseOrderInfo.builder().status(8).build(), new LambdaQueryWrapper().in(LeaseOrderInfo::getId, orderIds)); + //发送支付消息 + sendMessageToMq(orderIds); + } + + } + //6.正在售出中矿机已支付金额 + leaseOrderItemService.updateBatchById(saleIngList); + + } + + /** + * 修改卖家钱包被修改后 的 order_item 的 中对应卖家的钱包地址 + * @param orderItemList 今天需要支付的订单 + */ + public List updateOrderItemSellerWalletAddress( List orderItemList){ + List collect = orderItemList.stream() + .map(LeaseOrderItem -> ShopChainAndCoinDto.builder().shopId(LeaseOrderItem.getShopId()) + .coin(LeaseOrderItem.getSymbol()) + .chain(LeaseOrderItem.getChain()).build()) + .distinct() + .collect(Collectors.toList()); + //查询到对应商铺对应的 chain 然后比较上面的链对应的地址是否改变 + List shopConfigList = leaseShopConfigMapper.selectShopConfigByShopIdAndSymbolAndChain(collect); + List updateOrderItemList = new ArrayList<>(); + // 遍历 orderItemList + for (LeaseOrderItem orderItem : orderItemList) { + for (ShopConfigDelDto shopConfig : shopConfigList) { + // 检查 chain 和 shopId 是否相同 + if (orderItem.getShopId().equals(shopConfig.getShopId()) && + orderItem.getChain().equals(shopConfig.getChain())) { + // 检查 address 是否不同 + if (!orderItem.getAddress().equals(shopConfig.getPayAddress())) { + // 更新 orderItem 的 address + orderItem.setAddress(shopConfig.getPayAddress()); + updateOrderItemList.add(orderItem); + } + break; + } + } + } + leaseOrderItemService.updateBatchById(updateOrderItemList); + return orderItemList; + + } + + + + /** + *订单完成后---发送支付消息到mq 一个买家对应一个卖家 + * @param orderIds + */ + public void sendMessageToMq(List orderIds){ + List leasePayRecordMessages = leasePayRecordMessageInfoService.list(new LambdaQueryWrapper() + .in(LeasePayRecordMessageInfo::getOrderId, orderIds)); + + // 合并后的处理 + Map> infoMap = new HashMap<>(); + List shopIds = new ArrayList<>(); + for (LeasePayRecordMessageInfo message : leasePayRecordMessages) { + // 处理分组逻辑 + infoMap.computeIfAbsent(message.getOrderId(), k -> new ArrayList<>()).add(message); + // 收集shopId + shopIds.add(message.getShopId()); + } + //获取卖家邮箱和用户id信息 + List leaseShops = leaseShopMapper.selectBatchIds(shopIds); + Map sellerInfoMap = leaseShops.stream().collect(Collectors.toMap(LeaseShop::getId, Function.identity())); + + List reocrdList = new ArrayList<>(); + //遍历按订单id分组后的map + infoMap.forEach((orderId, payRecordMessagesList) -> { + LeasePayRecordMessageInfo initForm = payRecordMessagesList.get(0); + //long timestamp = System.currentTimeMillis()/1000; + //RabbitmqPayAutoMessage build = RabbitmqPayAutoMessage.builder() + // .queue_id(initForm.getQueueId()) + // .chain(initForm.getFromChain()) + // .symbol(initForm.getFromSymbol()) + // .from_address(initForm.getFromAddress()) + // .to_address(initForm.getToAddress()) + // .fee(feeMap.get(orderId)) + // .amount(BigDecimal.ZERO) + // .blockAmount(BigDecimal.ZERO) + // .needAmount(BigDecimal.ZERO) + // .timestamp(timestamp) + // .sign(HashUtils.sha256(timestamp)) + // .build(); + LeaseShop leaseShop = sellerInfoMap.get(initForm.getShopId()); + LeasePayRecordMessage build = LeasePayRecordMessage.builder() + .queueId(initForm.getQueueId()) + .fromAddress(initForm.getFromAddress()) + .toAddress(initForm.getToAddress()) + .orderNumber(initForm.getOrderNumber()) + .orderId(orderId) + .shopId(initForm.getShopId()) + .userId(initForm.getUserId()) + .authId(initForm.getAuthId()) + .blockAmount(BigDecimal.ZERO) + .amount(BigDecimal.ZERO) + .realAmount(BigDecimal.ZERO) + .needAmount(BigDecimal.ZERO) + .fromSymbol(initForm.getFromSymbol()) + .fromChain(initForm.getFromChain()) + .toSymbol(initForm.getToSymbol()) + .toChain(initForm.getToChain()) + .sellerId(leaseShop.getAuthId()) + .status(1) + .sellerEmail(leaseShop.getUserEmail()) + .build(); + for (LeasePayRecordMessageInfo leasePayRecordMessage : payRecordMessagesList) { + build.setAmount(build.getAmount().add(leasePayRecordMessage.getAmount())); + build.setBlockAmount(build.getBlockAmount().add(leasePayRecordMessage.getBlockAmount())); + //现在可以删除NeedAmount这个字段了.新版本不存在需支付真实金额和实际支付真实金额不同的情况 + build.setNeedAmount(build.getNeedAmount().add(leasePayRecordMessage.getNeedAmount())); + build.setRealAmount(build.getRealAmount().add(leasePayRecordMessage.getRealAmount())); + } + reocrdList.add(build); + + + //旧版本 + //订单全部完成后不再需要发送支付信息,直接从买家钱包转账到卖家钱包 + //try { + // rabbitTemplate.convertAndSend(PAY_AUTO_QUEUE, build); + //}catch (Exception e){ + // //设置状态为消息发送失败 + // build1.setStatus(4); + // System.out.println("消息发送失败"+e.getMessage()); + //}finally { + // reocrdList.add(build1); + //} + + //新版本:买家钱包 冻结余额和余额减少 卖家钱包余额增加 + + + }); + leasePayRecordMessageService.saveBatch(reocrdList); + int buyerUpdate = leaseUserWalletDataMapper.updateBalanceAndBlockBalance(reocrdList); + int sellerUpdate = leaseShopConfigMapper.updateBalance(reocrdList); + + if (buyerUpdate > 0 && sellerUpdate > 0){ + for (LeasePayRecordMessage item : reocrdList) { + recordLog(item); + } + } + } + + /** + * 记录日志 + * @param item + */ + private void recordLog(LeasePayRecordMessage item ){ + String buyerInfo = "订单号:" + item.getOrderNumber() + + "理论支付金额:" + item.getAmount() + + "实际支付金额:" + item.getRealAmount()+ + "收款地址:" + item.getToAddress()+ + "支付链:" + item.getFromChain()+ + "支付币种" + item.getFromSymbol(); + + String sellerInfo = "订单号:" + item.getOrderNumber() + + "收款金额:" + item.getRealAmount()+ + "支付地址:" + item.getFromAddress()+ + "支付链:" + item.getFromChain()+ + "支付币种" + item.getFromSymbol() ; + ledgerLogService.logOperation(item.getFromAddress()+"--"+item.getUserId()+"--"+item.getAuthId(), "支付(付款)", buyerInfo); + ledgerLogService.logOperation(item.getToAddress()+"--"+item.getSellerEmail()+"--"+item.getSellerId(), "支付(收款)",sellerInfo); + } + + /** + * 获取手续费 + * @param orderIds + * @return + */ + private Map getFee(List< Long> orderIds){ + List leaseOrderInfos = leaseOrderInfoMapper.selectList(new LambdaQueryWrapper() + .select(LeaseOrderInfo::getFee,LeaseOrderInfo::getOrderNumber) + .in(LeaseOrderInfo::getId, orderIds)); + return leaseOrderInfos.stream() + .collect(Collectors.toMap(LeaseOrderInfo::getOrderNumber, LeaseOrderInfo::getFee)); + } + + + + + /** + * 一些校验失败的支付消息需要重新发送(新版本不再发送支付消息) + */ + //@Scheduled(cron = "0 0 0/1 * * ? ") + //@Transactional + public void checkPushFailPayMessage(){ + //查找出状态为3 校验失败 4 发送消息失败 的消息需要重发 + List list = leasePayRecordMessageService.list(new LambdaQueryWrapper() + .eq(LeasePayRecordMessage::getStatus, 3) + .ge(LeasePayRecordMessage::getCreateTime, LocalDateTime.now().minusHours(3)) + .or().eq(LeasePayRecordMessage::getStatus, 4)); + if (list.isEmpty()){ + return; + } + List orderIds = list.stream().map(leasePayRecordMessage->Long.valueOf(leasePayRecordMessage.getOrderId())).collect(Collectors.toList()); + Map feeMap = getFee(orderIds); + List againSendList = new ArrayList<>(); + for (LeasePayRecordMessage leasePayRecordMessage : list) { + Long timestamp = System.currentTimeMillis()/1000; + RabbitmqPayAutoMessage build = RabbitmqPayAutoMessage.builder() + .queue_id(leasePayRecordMessage.getQueueId()) + .order_id(leasePayRecordMessage.getOrderId()) + .chain(leasePayRecordMessage.getFromChain()) + .symbol(leasePayRecordMessage.getFromSymbol()) + .from_address(leasePayRecordMessage.getFromAddress()) + .to_address(leasePayRecordMessage.getToAddress()) + .fee(feeMap.get(leasePayRecordMessage.getOrderId())) + .amount(leasePayRecordMessage.getAmount()) + .timestamp(timestamp) + .sign(HashUtils.sha256(timestamp)) + .build(); + try { + rabbitTemplate.convertAndSend(PAY_AUTO_QUEUE, build); + leasePayRecordMessage.setStatus(2); + againSendList.add(leasePayRecordMessage); + }catch (Exception e){ + System.out.println("消息发送失败"+e.getMessage()); + } + } + leasePayRecordMessageService.updateBatchById(againSendList); + + } + + + /** + * 检测 临时表数据是否需要持久化到矿机表中 + */ + @Scheduled(cron = "0 0 0/1 * * ? ") + @Transactional + public void checkMachineTempTable(){ + List leaseMachineTempConfigs = leaseMachineTempConfigMapper.selectList(new LambdaQueryWrapper() + .le(LeaseMachineTempConfig::getEffectTime, LocalDateTime.now())); + if (!leaseMachineTempConfigs.isEmpty()){ + leaseMachineTempConfigMapper.insertBatchByTempList(leaseMachineTempConfigs); + leaseMachineTempConfigMapper.deleteBatchIds(leaseMachineTempConfigs); + } + } + + + + ///** + // * 订单完成后---发送支付消息到mq 一个买家对应多个卖家 + // * @param orderIds + // */ + //public void sendMessageToMq(List orderIds){ + // List leasePayRecordMessages = leasePayRecordMessageMapper.selectList(new LambdaQueryWrapper() + // .in(LeasePayRecordMessage::getOrderId, orderIds)); + // Map>> collect = leasePayRecordMessages.stream() + // .collect(Collectors.groupingBy(LeasePayRecordMessage::getOrderId, Collectors.groupingBy(LeasePayRecordMessage::getToAddress))); + // //遍历按订单id分组后的map + // collect.forEach((orderId, payRecordMessagesMap) -> { + // + // long timestamp = System.currentTimeMillis()/1000; + // List orDefault = payRecordMessagesMap.getOrDefault(payRecordMessagesMap.keySet().iterator().next(), new ArrayList<>()); + // LeasePayRecordMessage initForm = orDefault.get(0); + // RabbitmqPayAutoMessage build = RabbitmqPayAutoMessage.builder() + // .queue_id(initForm.getQueueId()) + // .from_address(initForm.getFromAddress()) + // .chain(initForm.getFromChain()) + // .symbol(initForm.getFromSymbol()) + // .total_amount(BigDecimal.ZERO) + // .timestamp(timestamp) + // .sign(HashUtils.sha256(timestamp)) + // .build(); + // + // + // Map transactions = new HashMap<>(); + // //遍历按toAddress 分组后的map + // payRecordMessagesMap.forEach((toAddress,payRecordMessageList) -> { + // LeasePayRecordMessage init = payRecordMessageList.get(0); + // RabbitmqPayAutoInfoMessage sellInfo = RabbitmqPayAutoInfoMessage.builder() + // .to_address(init.getToAddress()) + // .amount(BigDecimal.ZERO) + // .blockAmount(BigDecimal.ZERO) + // .needAmount(BigDecimal.ZERO) + // .shopId(init.getShopId()) + // .userId(init.getUserId()) + // .build(); + // for (LeasePayRecordMessage leasePayRecordMessage : payRecordMessageList) { + // sellInfo.setAmount(sellInfo.getAmount().add(leasePayRecordMessage.getRealAmount())); + // sellInfo.setBlockAmount(sellInfo.getBlockAmount().add(leasePayRecordMessage.getBlockAmount())); + // sellInfo.setNeedAmount(sellInfo.getNeedAmount().add(leasePayRecordMessage.getNeedAmount())); + // } + // build.setTotal_amount(build.getTotal_amount().add(sellInfo.getAmount())); + // transactions.put(toAddress,sellInfo); + // }); + // build.setTransactions(transactions); + // rabbitTemplate.convertAndSend(PAY_AUTO_QUEUE, build); + // }); + //} + + //@Scheduled(cron = "0 0/1 * * * ? ") + //@Transactional + //public void test(){ + // System.out.println("发送消息到队列------------------------------------------"); + // long l = System.currentTimeMillis()/1000; + // //支付 + // rabbitTemplate.convertAndSend(PAY_AUTO_QUEUE, RabbitmqPayAutoMessage.builder() + // .queue_id("1") + // .from_address("1") + // .to_address("1") + // .amount(BigDecimal.valueOf(1)) + // .chain("1") + // .symbol("1") + // .order_id("1") + // .timestamp(l) + // .sign(HashUtils.sha256(l)) + // .build() + // ); + // //充值 + // rabbitTemplate.convertAndSend(PAY_RECHARGE_QUEUE, RabbitmqPayRechargeMessage.builder() + // .address("2") + // .chain("2") + // .symbol("2") + // .timestamp(l) + // .sign(HashUtils.sha256(l)) + // .build() + // ); + // //提现 + // rabbitTemplate.convertAndSend(PAY_WITHDRAW_QUEUE, RabbitmqPayWithdrawMessage.builder() + // .queue_id("1") + // .from_address("1") + // .to_address("1") + // .amount(BigDecimal.valueOf(1)) + // .chain("1") + // .symbol("1") + // .timestamp(l) + // .sign(HashUtils.sha256(l)) + // .build() + // ); + // + // + // + //} + + /** + * 每分钟检查一次超时的订单,是否修改了状态为 6 订单已超时 + */ + //@Scheduled(cron = "0 0/1 * * * ? ") + //@Transactional + //public void checkIsExpireOrder(){ + // LocalDateTime now = LocalDateTime.now(); + // LocalDateTime createTime = now.minusMinutes(15); + // List leaseOrderInfos = leaseOrderInfoMapper.selectList(new LambdaQueryWrapper() + // .eq(LeaseOrderInfo::getStatus, PAYMENT_IN_PROGRESS.getCode()) + // .lt(LeaseOrderInfo::getCreateTime, createTime)); + // + // //普通订单状态跟着支付订单走,先修改支付订单状态(查找15m前的支付中订单) + // List leasePaymentRecords = leasePaymentRecordMapper.selectList(new LambdaQueryWrapper() + // .eq(LeasePaymentRecord::getStatus, PAYMENT_IN_PROGRESS.getCode()) + // .lt(LeasePaymentRecord::getCreateTime, createTime)); + // if (leasePaymentRecords.isEmpty()){ + // return; + // } + // //1.根据 订单实际时间已超时 但是订单状态为 6支付中 的情况----->修改这些订单的状态为 5订单已超时 + // int updatePayStatus = leasePaymentRecordMapper.updateBatchStatusForOderIdAndShopId(leasePaymentRecords); + // + // //2.修改机器中售出状态 从2售出中改为 0未售出 + // List orderStatusIsPayInData = leaseOrderItemMapper.getMachineByOrderStatusIsPayInData(leasePaymentRecords); + // if (orderStatusIsPayInData.isEmpty()){ + // return; + // } + // int updateMachineStatus = leaseProductMachineMapper.update(LeaseProductMachine.builder().saleState(0).build(), + // new LambdaQueryWrapper() + // .eq(LeaseProductMachine::getSaleState, 2).in(LeaseProductMachine::getId, orderStatusIsPayInData)); + // + // //3.修改普通订单状态为 5订单已超时 + // List orderIds = leaseOrderInfos.stream().map(LeaseOrderInfo::getId).collect(Collectors.toList()); + // if (orderIds.isEmpty()){ + // return; + // } + // int updateOrderStatus = leaseOrderInfoMapper.update(LeaseOrderInfo.builder().status(5).build(), + // new LambdaQueryWrapper().eq(LeaseOrderInfo::getStatus, 6).in(LeaseOrderInfo::getId, orderIds)); + // + // System.out.println("修改了" + updatePayStatus + "个支付订单状态为5订单已超时。" + // + updateMachineStatus + "个机器状态为未售出。"+ updateOrderStatus + "个普通订单状态为5订单已超时"); + // + //} + + + /** + * //获取 15 钟内支付订单 再次去校验是否支付全部成功 全部成功则修改订单状态为 1全部支付成功 + */ + //@Scheduled(cron = "0/30 * * * * ? ") + //@Transactional + //public void paymentRecord(){ + // List leasePaymentRecordList = leasePaymentRecordMapper.selectList(new LambdaQueryWrapper() + // .eq(LeasePaymentRecord::getDel, false) + // .ge(LeasePaymentRecord::getCreateTime, LocalDateTime.now().minusMinutes(15))); + // + // if (!leasePaymentRecordList.isEmpty()){ + // leasePaymentRecordService.paymentCallbackTask(leasePaymentRecordList); + // } + //} + + + + +} diff --git a/src/main/java/com/m2pool/lease/task/OwnProductTask.java b/src/main/java/com/m2pool/lease/task/OwnProductTask.java new file mode 100644 index 0000000..b8b6ceb --- /dev/null +++ b/src/main/java/com/m2pool/lease/task/OwnProductTask.java @@ -0,0 +1,324 @@ +package com.m2pool.lease.task; + +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.m2pool.lease.dto.HourIncomeDto; +import com.m2pool.lease.dto.OrderStatusDto; +import com.m2pool.lease.entity.*; +import com.m2pool.lease.mapper.*; +import com.m2pool.lease.mq.message.RabbitmqPoolProxyMessage; +import com.m2pool.lease.service.LeaseOrderInfoService; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.rmi.dgc.Lease; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static com.m2pool.lease.constant.RabbitmqConstant.POOL_PROXY_QUEUE_NAME; + +/** + * @Description 后台管理系统 定时任务 + * @Date 2025/7/15 15:45 + * @Author yyb + */ +@Configuration +@EnableScheduling +public class OwnProductTask { + + @Resource + private LeaseUserOwnedProductMapper leaseUserOwnedProductMapper; + + + @Resource + private LeaseProductMachineMapper leaseProductMachineMapper; + + @Resource + private LeaseOrderItemMapper leaseOrderItemMapper; + + @Resource + private LeaseOrderInfoService leaseOrderInfoService; + + @Resource + private LeasePayRecordMessageMapper leasePayRecordMessageMapper; + + @Resource + private RabbitTemplate rabbitTemplate; + + private static final int THREAD_POOL_SIZE = Runtime.getRuntime().availableProcessors(); + private static final int BATCH_SIZE = 1000; + private final ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE); + /** + * 修改已购表租约过期状态为1 已过期 + 机器表状态为0 未售出 + 修改租约到期订单状态为已完成 + */ + @Scheduled(cron = "0 1,5,7 0 * * ? ") + //@Scheduled(cron = "0 0/3 * * * ? ") + @Transactional + public void updateOwnMachineStateTask(){ + // 获取当天 0 点的 LocalDateTime + LocalDateTime now = LocalDateTime.now(); + LocalDateTime startOfDay = now.toLocalDate().atStartOfDay(); + //1. 查询当天失效机器 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper() + .eq(LeaseUserOwnedProduct::getEndTime, startOfDay).eq(LeaseUserOwnedProduct::getStatus, 0); + List leaseUserOwnedProducts = leaseUserOwnedProductMapper.selectList(wrapper); + + if (!leaseUserOwnedProducts.isEmpty()){ + //2.修改商品机器表中 销售状态为 0 未售出 + List ids = new ArrayList<>(); + List isExpireIds = new ArrayList<>(); + List itemIds = new ArrayList<>(); + for (LeaseUserOwnedProduct leaseUserOwnedProduct : leaseUserOwnedProducts) { + ids.add(leaseUserOwnedProduct.getId()); + isExpireIds.add(leaseUserOwnedProduct.getProductMachineId()); + itemIds.add(leaseUserOwnedProduct.getOrderItemId()); + } + leaseUserOwnedProductMapper.update(LeaseUserOwnedProduct.builder().status(1).build(),new LambdaUpdateWrapper() + .in(LeaseUserOwnedProduct::getId, ids)); + leaseProductMachineMapper.update(LeaseProductMachine.builder().saleState(0).build(), + new LambdaQueryWrapper().in(LeaseProductMachine::getId, isExpireIds)); + leaseOrderItemMapper.update(LeaseOrderItem.builder().status(0).build(),new LambdaQueryWrapper().in(LeaseOrderItem::getId, itemIds)); + //3.订单所有机器已过期,直接修改订单状态为已完成 + List orderIds = leaseOrderItemMapper.getOrderIds(itemIds); + List orderInfoStatus = leaseOrderItemMapper.getOrderInfoStatus(orderIds); + + List infoList = new ArrayList<>(); + orderInfoStatus.forEach(item->{ + if (item.getOrderInfoStatus() == 8){ + infoList.add(LeaseOrderInfo.builder().id(item.getOrderId()).status(8).build()); + } + }); + if (!infoList.isEmpty()){ + leaseOrderInfoService.saveOrUpdateBatch(infoList); + } + + System.out.println("租约过期机器状态修改成功"); + //3.发送rabbitmq消息给矿池代理删除代理的矿机 + //sendRabbitmqMessage(leaseUserOwnedProducts); + } + } + + public void sendRabbitmqMessage( List leaseUserOwnedProducts) { + leaseUserOwnedProducts.forEach(item -> { + executorService.execute(()->{ + RabbitmqPoolProxyMessage rabbitmqPoolProxyMessage = RabbitmqPoolProxyMessage.builder() + .MethodID(1) + .Msg("") + .ID(item.getUser() + "-" + item.getMiner()) + .Address(item.getAddress()) + .build(); + try { + rabbitTemplate.convertAndSend(POOL_PROXY_QUEUE_NAME, rabbitmqPoolProxyMessage); + //同步阻塞等待消息发送成功 + //boolean b = rabbitTemplate.waitForConfirms(10000); + } catch (Exception e) { + + System.err.println("发送失败: " + e.getMessage()); + } + }); + }); + + } + + + @Scheduled(cron = "0 35 0/1 * * ? ") + //@Scheduled(cron = "30 0/2 * * * ? ") + @DSTransactional + public void updateNexaIncomeTask(){ + List updateList = computeIncome("nexa"); + //批量修改 + batchUpdateLeaseUserOwnedProducts(updateList); + + } + + @Scheduled(cron = "10 35 0/1 * * ? ") + //@Scheduled(cron = "30 0/2 * * * ? ") + @DSTransactional + public void updateGrsIncomeTask(){ + List updateList = computeIncome("grs"); + //批量修改 + batchUpdateLeaseUserOwnedProducts(updateList); + } + + @Scheduled(cron = "20 35 0/1 * * ? ") + //@Scheduled(cron = "30 0/2 * * * ? ") + @DSTransactional + public void updateRxdIncomeTask(){ + List updateList = computeIncome("rxd"); + //批量修改 + batchUpdateLeaseUserOwnedProducts(updateList); + } + + @Scheduled(cron = "30 35 0/1 * * ? ") + //@Scheduled(cron = "30 0/2 * * * ? ") + @DSTransactional + public void updateMonaIncomeTask(){ + List updateList = computeIncome("mona"); + //批量修改 + batchUpdateLeaseUserOwnedProducts(updateList); + } + + + /** + * 计算已购生效机器过去一小时收益 + * @param coin + */ + public List computeIncome(String coin){ + //获取当前时间整点时间 + LocalDateTime now = LocalDateTime.now().withMinute(0).withSecond(0).withNano(0); + //获取当天开始时间 + //LocalDateTime startOfDay = now.with(LocalTime.MIN); + LocalDateTime end = now.minusHours(1); + LocalDateTime start = now.minusHours(2); + //1. 查询在有效期内的已购机器 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper() + .eq(LeaseUserOwnedProduct::getCoin, coin) + .eq(LeaseUserOwnedProduct::getStatus, 0); + List leaseUserOwnedProducts = leaseUserOwnedProductMapper.selectList(wrapper); + + //2.获取近一个小时矿机的收益 + 并计算实时收益 + List updateList = new ArrayList<>(); + if (!leaseUserOwnedProducts.isEmpty()){ + List incomeDtos = leaseUserOwnedProductMapper.getHourIncomeList(leaseUserOwnedProducts,coin, start, end); + Map collect = leaseUserOwnedProducts.stream() + .collect(Collectors.toMap(item-> item.getUser() + "-" + item.getMiner()+ "-" + item.getCoin(),Function.identity())); + for (HourIncomeDto hourIncomeDto : incomeDtos) { + String key = hourIncomeDto.getUser() + "-" + hourIncomeDto.getMiner()+ "-" + coin; + LeaseUserOwnedProduct leaseUserOwnedProduct = collect.get(key) ; + leaseUserOwnedProduct.setSettleIncome(hourIncomeDto.getIncome().add(leaseUserOwnedProduct.getSettleIncome())); + //leaseUserOwnedProduct.setSettleUsdtIncome(hourIncomeDto.getUsdtIncome().add(leaseUserOwnedProduct.getSettleUsdtIncome())); + updateList.add(leaseUserOwnedProduct); + } + + } + return updateList; + } + + + ///** + // * 计算已购生效机器过去一小时收益 + // * @param coin + // */ + //public List computeIncome(String coin){ + // //获取当前时间整点时间 + // LocalDateTime now = LocalDateTime.now().withMinute(0).withSecond(0).withNano(0); + // //获取当天开始时间 + // LocalDateTime startOfDay = now.with(LocalTime.MIN); + // LocalDateTime end = now.minusHours(1); + // LocalDateTime start = now.minusHours(2); + // //1. 查询在有效期内的已购机器 + // LambdaQueryWrapper wrapper = new LambdaQueryWrapper() + // .eq(LeaseUserOwnedProduct::getCoin, coin) + // .eq(LeaseUserOwnedProduct::getStatus, 0); + // List leaseUserOwnedProducts = leaseUserOwnedProductMapper.selectList(wrapper); + // System.out.println("个人收益--矿机: " + JSONUtil.toJsonPrettyStr(leaseUserOwnedProducts)); + // //2.获取近一个小时矿机的收益 + 并计算实时收益 + // List updateList = new ArrayList<>(); + // if (!leaseUserOwnedProducts.isEmpty()){ + // Map collect = new HashMap<>(); + // Set orderInfoIds = new HashSet<>(); + // for (LeaseUserOwnedProduct item : leaseUserOwnedProducts) { + // collect.put(item.getUser() + "-" + item.getMiner() + "-" + item.getCoin(), item); + // orderInfoIds.add(item.getOrderId()); + // } + // + // //2.1 查询当天已支付订单 + // //查询订单详情对应的信息formAddress + fromChain + fromSymbol + toAddress + toChain + toSymbol + // List leaseOrderItems = leaseOrderItemMapper.getOrderItemByOrderIds(orderInfoIds); + // Map collect1 = leaseOrderItems.stream().collect(Collectors.toMap(item -> item.getUser() + "-" + item.getMiner() + "-" + item.getCoin(), Function.identity())); + // + // List incomeDtos = leaseUserOwnedProductMapper.getHourIncomeList(leaseUserOwnedProducts,coin, start, end); + // for (HourIncomeDto hourIncomeDto : incomeDtos) { + // String key = hourIncomeDto.getUser() + "-" + hourIncomeDto.getMiner()+ "-" + coin; + // System.out.println("个人收益--矿机详情: " + key); + // LeaseUserOwnedProduct leaseUserOwnedProduct = collect.get(key) ; + // LeaseOrderItem leaseOrderItem = collect1.get(key); + // System.out.println("个人收益--订单详情: " + JSONUtil.toJsonPrettyStr(leaseOrderItem)); + // if (leaseOrderItem != null && leaseOrderItem.getStatus() == 1){ + // BigDecimal coinIncome = hourIncomeDto.getIncome().add(leaseUserOwnedProduct.getCurrentIncome()); + // BigDecimal usdtIncome = hourIncomeDto.getUsdtIncome().add(leaseUserOwnedProduct.getCurrentUsdtIncome()); + // //当日待结算收益不为0,把待结算收益加到当前收益中 + // if (leaseUserOwnedProduct.getSettleIncome().compareTo(BigDecimal.ZERO) > 0){ + // coinIncome = coinIncome.add(leaseUserOwnedProduct.getSettleIncome()); + // usdtIncome = usdtIncome.add(leaseUserOwnedProduct.getSettleUsdtIncome()); + // leaseUserOwnedProduct.setSettleIncome(BigDecimal.ZERO); + // leaseUserOwnedProduct.setSettleUsdtIncome(BigDecimal.ZERO); + // } + // leaseUserOwnedProduct.setCurrentIncome(coinIncome); + // leaseUserOwnedProduct.setCurrentUsdtIncome(usdtIncome); + // + // }else{ + // leaseUserOwnedProduct.setSettleIncome(hourIncomeDto.getIncome().add(leaseUserOwnedProduct.getSettleIncome())); + // leaseUserOwnedProduct.setSettleUsdtIncome(hourIncomeDto.getUsdtIncome().add(leaseUserOwnedProduct.getSettleUsdtIncome())); + // } + // if (now.equals(startOfDay)){ + // leaseUserOwnedProduct.setSettleIncome(BigDecimal.ZERO); + // leaseUserOwnedProduct.setSettleUsdtIncome(BigDecimal.ZERO) ; + // } + // + // updateList.add(leaseUserOwnedProduct); + // } + // } + // return updateList; + //} + + /** + * 批量修改已购机器的收益 + * @param updateList + */ + public void batchUpdateLeaseUserOwnedProducts(List updateList) { + // 用于记录执行过程中的异常 + AtomicReference exceptionRef = new AtomicReference<>(); + List> futures = new ArrayList<>(); + + for (int i = 0; i < updateList.size(); i += BATCH_SIZE) { + int toIndex = Math.min(i + BATCH_SIZE, updateList.size()); + List subList = updateList.subList(i, toIndex); + + CompletableFuture future = CompletableFuture.runAsync(() -> { + try { + if (!subList.isEmpty()){ + leaseUserOwnedProductMapper.updateBatchIncome(subList); + } + } catch (Exception e) { + exceptionRef.set(e); + throw new RuntimeException(e); + } + }, executorService); + + futures.add(future); + } + + // 等待所有任务完成 + CompletableFuture allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); + try { + allFutures.get(); + } catch (InterruptedException | ExecutionException e) { + // 如果有异常,将异常信息设置到 exceptionRef 中 + exceptionRef.set(e); + } + + // 如果有异常,抛出异常触发事务回滚 + if (exceptionRef.get() != null) { + throw new RuntimeException("批量更新数据时发生错误", exceptionRef.get()); + } + } + +} diff --git a/src/main/java/com/m2pool/lease/task/RealPowerInsetTask.java b/src/main/java/com/m2pool/lease/task/RealPowerInsetTask.java new file mode 100644 index 0000000..e0705f4 --- /dev/null +++ b/src/main/java/com/m2pool/lease/task/RealPowerInsetTask.java @@ -0,0 +1,116 @@ +package com.m2pool.lease.task; + +import com.baomidou.dynamic.datasource.annotation.DSTransactional; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.m2pool.lease.dto.ProductMachineDto; +import com.m2pool.lease.entity.LeaseProductMachine; +import com.m2pool.lease.mapper.LeaseProductMachineMapper; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicReference; + +/** + * @Description 矿机实时算力入库 + * @Date 2025/11/7 14:16 + * @Author yyb + */ +@Configuration +@EnableScheduling +public class RealPowerInsetTask { + + @Resource + private LeaseProductMachineMapper leaseProductMachineMapper; + + private static final int BATCH_SIZE = 1000; + private static final int THREAD_POOL_SIZE = Runtime.getRuntime().availableProcessors(); + private final ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE); + public void batchInsert(String coin,List recentlyFiveMinutesData) { + // 用于记录执行过程中的异常 + AtomicReference exceptionRef = new AtomicReference<>(); + List> futures = new ArrayList<>(); + + for (int i = 0; i < recentlyFiveMinutesData.size(); i += BATCH_SIZE) { + int toIndex = Math.min(i + BATCH_SIZE, recentlyFiveMinutesData.size()); + List subList = recentlyFiveMinutesData.subList(i, toIndex); + + CompletableFuture future = CompletableFuture.runAsync(() -> { + try { + leaseProductMachineMapper.batchInsertPowers(coin, subList); + } catch (Exception e) { + exceptionRef.set(e); + throw new RuntimeException(e); + } + }, executorService); + + futures.add(future); + } + // 等待所有任务完成 + CompletableFuture allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); + try { + allFutures.get(); + } catch (InterruptedException | ExecutionException e) { + // 如果有异常,将异常信息设置到 exceptionRef 中 + exceptionRef.set(e); + } + + // 如果有异常,抛出异常触发事务回滚 + if (exceptionRef.get() != null) { + System.out.println("批量插入数据实时算力数据出错"+exceptionRef.get()); + throw new RuntimeException("批量插入数据实时算力数据出错", exceptionRef.get()); + } + } + + public List getRealPower(String coin){ + List leaseProductMachines = leaseProductMachineMapper.selectList(new LambdaQueryWrapper() + .eq(LeaseProductMachine::getDel, false)); + List recentlyFiveMinutesData = leaseProductMachineMapper.getRecentlyFiveMinutesData(leaseProductMachines, coin); + for (ProductMachineDto recentlyFiveMinutesDatum : recentlyFiveMinutesData) { + recentlyFiveMinutesDatum.setComputingPower(recentlyFiveMinutesDatum.getComputingPower() + .divide(BigDecimal.valueOf(24 * 60 * 60),2, RoundingMode.HALF_UP)); + } + return recentlyFiveMinutesData; + } + + + @Scheduled(cron = "0 0/5 * * * ? ") + @DSTransactional + public void nexaRealPowerInset(){ + List nexaPower = getRealPower("nexa"); + batchInsert("nexa",nexaPower); + + } + + @Scheduled(cron = "0 0/5 * * * ? ") + @DSTransactional + public void monaRealPowerInset(){ + List monaPower = getRealPower("mona"); + batchInsert("mona",monaPower); + } + + @Scheduled(cron = "0 0/5 * * * ? ") + @DSTransactional + public void rxdRealPowerInset(){ + List rxdPower = getRealPower("mona"); + batchInsert("rxd",rxdPower); + } + + @Scheduled(cron = "0 0/5 * * * ? ") + @DSTransactional + public void grsRealPowerInset(){ + List rxdPower = getRealPower("grs"); + batchInsert("grs",rxdPower); + } + +} diff --git a/src/main/java/com/m2pool/lease/task/info/AlgorithmInfo.java b/src/main/java/com/m2pool/lease/task/info/AlgorithmInfo.java new file mode 100644 index 0000000..982991c --- /dev/null +++ b/src/main/java/com/m2pool/lease/task/info/AlgorithmInfo.java @@ -0,0 +1,46 @@ +package com.m2pool.lease.task.info; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @Description 算法信息类 + * @Date 2025/12/16 15:43 + * @Author yyb + */ +@Data +public class AlgorithmInfo { + + /** + * 币种名称 + */ + private String coin; + + /** + * 算法名称 + */ + private String name; + + /** + * 挖矿算力 + */ + private BigDecimal hashrate; + + /** + * 功耗 + */ + private BigDecimal power; + + /** + * 报块间隔 + */ + private BigDecimal blockInterval; + + /** + * 报块奖励 + */ + private BigDecimal blockReward; + + private BigDecimal price; +} diff --git a/src/main/java/com/m2pool/lease/task/info/BlockInfo.java b/src/main/java/com/m2pool/lease/task/info/BlockInfo.java new file mode 100644 index 0000000..127f788 --- /dev/null +++ b/src/main/java/com/m2pool/lease/task/info/BlockInfo.java @@ -0,0 +1,34 @@ +package com.m2pool.lease.task.info; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @Description 算法信息类 + * @Date 2025/12/16 15:43 + * @Author yyb + */ +@Data +public class BlockInfo { + + /** + * 币种 + */ + private String coin; + + /** + * 每日报块数 + */ + private String blocks; + + /** + * 报块间隔 + */ + private BigDecimal blockInterval; + + /** + * 报块奖励 + */ + private BigDecimal blockReward; +} diff --git a/src/main/java/com/m2pool/lease/task/info/GpuInfo.java b/src/main/java/com/m2pool/lease/task/info/GpuInfo.java new file mode 100644 index 0000000..8ef3d9c --- /dev/null +++ b/src/main/java/com/m2pool/lease/task/info/GpuInfo.java @@ -0,0 +1,34 @@ +package com.m2pool.lease.task.info; + +import lombok.Data; + +import java.util.List; + +/** + * @Description gpu 信息类 + * @Date 2025/12/16 15:42 + * @Author yyb + */ +@Data +public class GpuInfo { + /** + * gpu id + */ + private Long id; + + /** + * gpu 名称 + */ + private String name; + + /** + * gpu 发布日期 + */ + private String release_date; + + + /** + * gpu 算法信息 + */ + private List algorithms; +} diff --git a/src/main/java/com/m2pool/lease/task/info/MiningRewardInfo.java b/src/main/java/com/m2pool/lease/task/info/MiningRewardInfo.java new file mode 100644 index 0000000..10cf78c --- /dev/null +++ b/src/main/java/com/m2pool/lease/task/info/MiningRewardInfo.java @@ -0,0 +1,21 @@ +package com.m2pool.lease.task.info; + +import lombok.Data; + +@Data +public class MiningRewardInfo { + private int id; + /** + * 币种名 + */ + private String tag; + private String name; + private String estimated_rewards; + private String estimated_rewards24; + private String btc_revenue; + private String btc_revenue24; + private double revenue; + private double revenue24; + private double profit; + private double profit24; +} diff --git a/src/main/java/com/m2pool/lease/task/info/MonthIncomeInfo.java b/src/main/java/com/m2pool/lease/task/info/MonthIncomeInfo.java new file mode 100644 index 0000000..1c58e19 --- /dev/null +++ b/src/main/java/com/m2pool/lease/task/info/MonthIncomeInfo.java @@ -0,0 +1,31 @@ +package com.m2pool.lease.task.info; + +import lombok.Builder; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @Description 月收益信息 + * @Date 2025/12/16 15:43 + * @Author yyb + */ +@Builder +@Data +public class MonthIncomeInfo { + + /** + * 算法名称 + */ + private String algorithm; + + /** + * 功耗 + */ + private BigDecimal power; + + /** + * 矿机算力 单位H/S + */ + private BigDecimal hashrate; +} diff --git a/src/main/java/com/m2pool/lease/utils/CodeUtils.java b/src/main/java/com/m2pool/lease/utils/CodeUtils.java new file mode 100644 index 0000000..b6d71ff --- /dev/null +++ b/src/main/java/com/m2pool/lease/utils/CodeUtils.java @@ -0,0 +1,43 @@ +package com.m2pool.lease.utils; + +import java.util.Random; + +/** + * @Description 验证码随机生成 + * @Date 2024/6/13 14:26 + * @Author dy + */ +public class CodeUtils { + //public static void main(String[] args) { + // String s = creatCode(4); + // //System.out.println("随机验证码为:" + s); + //} + + //定义一个方法返回一个随机验证码 + public static String creatCode(int n) { + + String code = ""; + Random r = new Random(); + //2.在方法内部使用for循环生成指定位数的随机字符,并连接起来 + for (int i = 0; i <= n; i++) { + //生成一个随机字符:大写 ,小写 ,数字(0 1 2) + int type = r.nextInt(3); + switch (type) { + case 0: + char ch = (char) (r.nextInt(26) + 65); + code += ch; + break; + case 1: + char ch1 = (char) (r.nextInt(26) + 97); + code += ch1; + break; + case 2: + code += r.nextInt(10); + break; + } + + } + return code; + } + +} diff --git a/src/main/java/com/m2pool/lease/utils/CryptoUtils.java b/src/main/java/com/m2pool/lease/utils/CryptoUtils.java new file mode 100644 index 0000000..94f3453 --- /dev/null +++ b/src/main/java/com/m2pool/lease/utils/CryptoUtils.java @@ -0,0 +1,30 @@ +package com.m2pool.lease.utils; + +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +public class CryptoUtils { + /** + * 生成BCryptPasswordEncoder密码 + * + * @param password 密码 + * @return 加密字符串 + */ + public static String encryptPassword(String password) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.encode(password); + } + + /** + * 判断密码是否相同 + * + * @param rawPassword 真实密码 + * @param encodedPassword 加密后字符 + * @return 结果 + */ + public static boolean matchesPassword(String rawPassword, String encodedPassword) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.matches(rawPassword, encodedPassword); + } +} diff --git a/src/main/java/com/m2pool/lease/utils/DateUtils.java b/src/main/java/com/m2pool/lease/utils/DateUtils.java new file mode 100644 index 0000000..6ec7527 --- /dev/null +++ b/src/main/java/com/m2pool/lease/utils/DateUtils.java @@ -0,0 +1,365 @@ +package com.m2pool.lease.utils; + +import org.apache.commons.lang3.time.DateFormatUtils; + +import java.lang.management.ManagementFactory; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.*; +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + +/** + * @Description 时间工具类 + * @Date 2024/6/13 11:21 + * @Author dy + */ +public class DateUtils extends org.apache.commons.lang3.time.DateUtils +{ + public static String YYYY = "yyyy"; + + public static String YYYY_MM = "yyyy-MM"; + + public static String YYYY_MM_DD = "yyyy-MM-dd"; + + public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; + + public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; + + private static String[] parsePatterns = { + "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", + "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", + "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; + + /** + * 获取当前Date型日期 + * + * @return Date() 当前日期 + */ + public static Date getNowDate() + { + return new Date(); + } + + /** + * 获取当前日期, 默认格式为yyyy-MM-dd + * + * @return String + */ + public static String getDate() + { + return dateTimeNow(YYYY_MM_DD); + } + + public static final String getTime() + { + return dateTimeNow(YYYY_MM_DD_HH_MM_SS); + } + + public static final String dateTimeNow() + { + return dateTimeNow(YYYYMMDDHHMMSS); + } + + public static final String dateTimeNow(final String format) + { + return parseDateToStr(format, new Date()); + } + + public static final String dateTime(final Date date) + { + return parseDateToStr(YYYY_MM_DD, date); + } + + public static final String parseDateToStr(final String format, final Date date) + { + return new SimpleDateFormat(format).format(date); + } + + public static final Date dateTime(final String format, final String ts) + { + try + { + return new SimpleDateFormat(format).parse(ts); + } + catch (ParseException e) + { + throw new RuntimeException(e); + } + } + + /** + * 日期路径 即年/月/日 如2018/08/08 + */ + public static final String datePath() + { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyy/MM/dd"); + } + + /** + * 日期路径 即年/月/日 如20180808 + */ + public static final String dateTime() + { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyyMMdd"); + } + + /** + * 日期型字符串转化为日期 格式 + */ + public static Date parseDate(Object str) + { + if (str == null) + { + return null; + } + try + { + return parseDate(str.toString(), parsePatterns); + } + catch (ParseException e) + { + return null; + } + } + + /** + * 日期型字符串转化为日期 格式 + */ + public static Date parseCNDate(Object str) + { + if (str == null) + { + return null; + } + try + { + Date date = parseDate(str.toString(), parsePatterns); + return UTCToCNDate(date); + } + catch (ParseException e) + { + return null; + } + } + + /** + * 获取服务器启动时间 + */ + public static Date getServerStartDate() + { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + + /** + * 计算两个时间差 + */ + public static String getDatePoor(Date endDate, Date nowDate) + { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + // long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - nowDate.getTime(); + // 计算差多少天 + long day = diff / nd; + // 计算差多少小时 + long hour = diff % nd / nh; + // 计算差多少分钟 + long min = diff % nd % nh / nm; + // 计算差多少秒//输出结果 + // long sec = diff % nd % nh % nm / ns; + return day + "天" + hour + "小时" + min + "分钟"; + } + /** + * 计算两个时间差 天数 + */ + public static Long getDateDiff(Date endDate, Date nowDate) + { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + // long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - nowDate.getTime(); + // 计算差多少天 + long day = diff / nd; + return day; + } + + public static Long getDateDiffSecond(Date endDate, Date nowDate) + { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - nowDate.getTime(); + // 计算差多少秒 + long sec = diff % nd % nh % nm / ns; + return sec; + } + + public static Long getDateDiffMinute(Date endDate, Date preDate) + { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - preDate.getTime(); + // 计算差多少秒 + long minute = diff / nm; + return minute; + } + + /** + * 计算两个时间差 按月 + */ + public static int getMonthDiff(Date d1, Date d2) { + Calendar c1 = Calendar.getInstance(); + Calendar c2 = Calendar.getInstance(); + c1.setTime(d1); + c2.setTime(d2); + int year1 = c1.get(Calendar.YEAR); + int year2 = c2.get(Calendar.YEAR); + int month1 = c1.get(Calendar.MONTH); + int month2 = c2.get(Calendar.MONTH); + int day1 = c1.get(Calendar.DAY_OF_MONTH); + int day2 = c2.get(Calendar.DAY_OF_MONTH); + // 获取年的差值 + int yearInterval = year1 - year2; + // 如果 d1的 月-日 小于 d2的 月-日 那么 yearInterval-- 这样就得到了相差的年数 + if (month1 < month2 || month1 == month2 && day1 < day2) { + yearInterval--; + } + // 获取月数差值 + int monthInterval = (month1 + 12) - month2; + if (day1 < day2) { + monthInterval--; + } + monthInterval %= 12; + int monthsDiff = Math.abs(yearInterval * 12 + monthInterval); + return monthsDiff; + } + + + /** + * 增加 LocalDateTime ==> Date + */ + public static Date toDate(LocalDateTime temporalAccessor) + { + ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + /** + * 增加 LocalDate ==> Date + */ + public static Date toDate(LocalDate temporalAccessor) + { + LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0)); + ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + public static LocalDate toLocalDate(Date date) + { + LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + return localDate; + } + + public static LocalDateTime toLocalDateTime(Date date) + { + LocalDateTime localDateTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); + return localDateTime; + } + + public static boolean isSameDay(final Date d1,final Date d2){ + String s1 = dateTime(d1); + String s2 = dateTime(d2); + + if (s1 == null || s2 == null){ + return false; + } + + if (s1.equals(s2)){ + return true; + } + return false; + } + + public static boolean isSameDate(final Date d1,final Date d2){ + + String s1 = parseDateToStr(YYYY_MM_DD_HH_MM_SS,d1); + String s2 = parseDateToStr(YYYY_MM_DD_HH_MM_SS,d2); + + if (s1 == null || s2 == null){ + return false; + } + + if (s1.equals(s2)){ + return true; + } + return false; + } + + public static Date UTCToCNDate(Date date) + { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + // 设置时区为北京 + simpleDateFormat.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); + + String format = simpleDateFormat.format(date); + + return DateUtils.parseDate(format); + } + + /** + * 获取当前时间前一个包含 30 分或整点的 30 分钟时间段起始时间 + * @param date 输入的日期 + * @return 前一个 30 分或整点时间段起始的日期 + */ + public static Date getPreviousHalfHourOrFullHour(Date date) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + + int minute = calendar.get(Calendar.MINUTE); + // 如果当前分钟数小于 30,前一个时间段起始是上一个整点 + if (minute < 30) { + calendar.set(Calendar.MINUTE, 0); + } else { + // 如果当前分钟数大于等于 30,前一个时间段起始是 30 分 + calendar.set(Calendar.MINUTE, 30); + } + + // 将秒和毫秒置为 0 + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + + // 如果当前分钟已经是 0 分或者 30 分,需要再往前推 30 分钟 + if (minute == 0 || minute == 30) { + calendar.add(Calendar.MINUTE, -30); + } + + return calendar.getTime(); + } + + /** + * 获取指定日期一个月前的时间 + * @param date 输入的日期 + * @return 一个月前的日期 + */ + public static Date getOneMonthAgo(Date date) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.add(Calendar.DAY_OF_MONTH, -30); + return calendar.getTime(); + } + +} diff --git a/src/main/java/com/m2pool/lease/utils/GoogleAuthenticator.java b/src/main/java/com/m2pool/lease/utils/GoogleAuthenticator.java new file mode 100644 index 0000000..af590a1 --- /dev/null +++ b/src/main/java/com/m2pool/lease/utils/GoogleAuthenticator.java @@ -0,0 +1,158 @@ +package com.m2pool.lease.utils; + +import com.alibaba.nacos.common.utils.StringUtils; +import org.apache.commons.codec.binary.Base32; +import org.apache.commons.codec.binary.Hex; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +/** + * @Description 谷歌身份验证器工具类 + * @Date 2024/8/21 10:14 + * @Author 杜懿 + */ +public class GoogleAuthenticator { + + /** + * 时间前后偏移量 + * 用于防止客户端时间不精确导致生成的TOTP与服务器端的TOTP一直不一致 + * 如果为0,当前时间为 10:10:15 + * 则表明在 10:10:00-10:10:30 之间生成的TOTP 能校验通过 + * 如果为1,则表明在 + * 10:09:30-10:10:00 + * 10:10:00-10:10:30 + * 10:10:30-10:11:00 之间生成的TOTP 能校验通过 + * 以此类推 + */ + private static int WINDOW_SIZE = 1; + + /** + * 加密方式,HmacSHA1、HmacSHA256、HmacSHA512 + */ + private static final String CRYPTO = "HmacSHA1"; + + /** + * 生成密钥,每个用户独享一份密钥 + * @return + */ + public static String getSecretKey() { + SecureRandom random = new SecureRandom(); +// byte[] bytes = new byte[20]; + byte[] bytes = new byte[10]; + random.nextBytes(bytes); + Base32 base32 = new Base32(); + String secretKey = base32.encodeToString(bytes); + // make the secret key more human-readable by lower-casing and + // inserting spaces between each group of 4 characters + return secretKey.toUpperCase(); + } + + /** + * 生成二维码内容 + * @param secretKey 密钥 + * @param account 账户名 + * @param issuer 网站地址(可不写) + * @return + */ + public static String getQrCodeText(String secretKey, String account, String issuer) { + String normalizedBase32Key = secretKey.replace(" ", "").toUpperCase(); + try { + return "otpauth://totp/" + + URLEncoder.encode((!StringUtils.isEmpty(issuer) ? (issuer + ":") : "") + account, "UTF-8").replace("+", "%20") + + "?secret=" + URLEncoder.encode(normalizedBase32Key, "UTF-8").replace("+", "%20") + + (!StringUtils.isEmpty(issuer) ? ("&issuer=" + URLEncoder.encode(issuer, "UTF-8").replace("+", "%20")) : ""); + } catch (UnsupportedEncodingException e) { + throw new IllegalStateException(e); + } + } + + /** + * 获取验证码 + * @param secretKey + * @return + */ + public static String getCode(String secretKey) { + String normalizedBase32Key = secretKey.replace(" ", "").toUpperCase(); + Base32 base32 = new Base32(); + byte[] bytes = base32.decode(normalizedBase32Key); + String hexKey = Hex.encodeHexString(bytes); + long time = (System.currentTimeMillis() / 1000) / 30; + String hexTime = Long.toHexString(time); + return TOTP.generateTOTP(hexKey, hexTime, "6", CRYPTO); + } + + /** + * 检验 code 是否正确 + * @param secret 密钥 + * @param code code + * @param time 时间戳 + * @return + */ + public static boolean checkCode(String secret, long code, long time) { + Base32 codec = new Base32(); + byte[] decodedKey = codec.decode(secret); + // convert unix msec time into a 30 second "window" + // this is per the TOTP spec (see the RFC for details) + long t = (time / 1000L) / 30L; + // Window is used to check codes generated in the near past. + // You can use this value to tune how far you're willing to go. + long hash; + for (int i = -WINDOW_SIZE; i <= WINDOW_SIZE; ++i) { + try { + hash = verifyCode(decodedKey, t + i); + } catch (Exception e) { + // Yes, this is bad form - but + // the exceptions thrown would be rare and a static + // configuration problem + // e.printStackTrace(); + +// throw new RuntimeException(e.getMessage()); + return false; + + } + if (hash == code) { + return true; + } + } + return false; + } + + /** + * 根据时间偏移量计算 + * @param key + * @param t + * @return + * @throws NoSuchAlgorithmException + * @throws InvalidKeyException + */ + private static long verifyCode(byte[] key, long t) throws NoSuchAlgorithmException, InvalidKeyException { + byte[] data = new byte[8]; + long value = t; + for (int i = 8; i-- > 0; value >>>= 8) { + data[i] = (byte) value; + } + SecretKeySpec signKey = new SecretKeySpec(key, CRYPTO); + Mac mac = Mac.getInstance(CRYPTO); + mac.init(signKey); + byte[] hash = mac.doFinal(data); + int offset = hash[20 - 1] & 0xF; + // We're using a long because Java hasn't got unsigned int. + long truncatedHash = 0; + for (int i = 0; i < 4; ++i) { + truncatedHash <<= 8; + // We are dealing with signed bytes: + // we just keep the first byte. + truncatedHash |= (hash[offset + i] & 0xFF); + } + truncatedHash &= 0x7FFFFFFF; + truncatedHash %= 1000000; + return truncatedHash; + } + +} diff --git a/src/main/java/com/m2pool/lease/utils/HashUtils.java b/src/main/java/com/m2pool/lease/utils/HashUtils.java new file mode 100644 index 0000000..8e33f55 --- /dev/null +++ b/src/main/java/com/m2pool/lease/utils/HashUtils.java @@ -0,0 +1,58 @@ +package com.m2pool.lease.utils; + +import java.math.BigInteger; +import java.security.MessageDigest; + +public class HashUtils { + + private static final String key = "9f3c7a12"; + + /** + * 将十六进制字符串转换为字节数组 (两个字符代表一个字节) + * @param hex 十六进制字符串 + * @return 转换后的字节数组 + */ + public static byte[] hexStringToByteArray(String hex) { + int len = hex.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + + Character.digit(hex.charAt(i+1), 16)); + } + return data; + } + + public static String sha256(Long input) { + try { + // 获取 SHA-256 算法的 MessageDigest 实例 + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + String key = getkey(input); + // 将十六进制字符串转换为字节数组 + byte[] inputBytes = hexStringToByteArray(key); + //时间戳 + 秘钥 + + // 将输入字节数组进行 SHA-256 哈希计算 + byte[] hashBytes = digest.digest(inputBytes); + String s = JnaUtils.bytesToHex(hashBytes); + return s; + } catch (Exception e) { + throw new RuntimeException("SHA-256算法不可用", e); + } + } + + /** + * 获取key + * @param timestamp 时间戳 + * @return key + */ + public static String getkey(Long timestamp){ + String inputStr = timestamp.toString(); + // 将字符串转换为 BigInteger 对象,指定基数为 10 + BigInteger decimalValue = new BigInteger(inputStr, 10); + // 将 BigInteger 对象转换为十六进制字符串 + return decimalValue.toString(16) + key; + } + + public static void main(String[] args) throws Exception { + } +} diff --git a/src/main/java/com/m2pool/lease/utils/HttpPayUtils.java b/src/main/java/com/m2pool/lease/utils/HttpPayUtils.java new file mode 100644 index 0000000..b2f3900 --- /dev/null +++ b/src/main/java/com/m2pool/lease/utils/HttpPayUtils.java @@ -0,0 +1,92 @@ +package com.m2pool.lease.utils; + +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONUtil; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.Map; + +/** + * @Description 支付工具类 + * @Date 2025/8/8 11:05 + * @Author yyb + */ +public class HttpPayUtils { + + private static final String URI = "http://127.0.0.1:12345"; + + public static final String PAY_ETH = "/checkPayment/eth"; + /** + * 发送 GET 请求 + * @param params 请求参数,键值对形式 + * @return 响应结果字符串 + */ + public static String doGet(String method,Map params) { + String URL = URI + method; + if (params != null && !params.isEmpty()) { + return HttpUtil.get(URL, params); + } + return HttpUtil.get(URL); + } + /** + * 发送 POST 请求,使用 JSON 格式提交数据 + * @param json 请求的 JSON 数据 + * @return 响应结果字符串 + */ + public static PaymentDto doPost(String method,String json) { + String URL = URI + method; + HttpResponse response = HttpRequest.post(URL) + .header("Content-Type", "application/json") + .body(json) + .execute(); + System.out.println("支付回调结果"+response); + if (response.body() != null && !response.body().isEmpty()) { + return JSONUtil.toBean(response.body(), PaymentDto.class); + } + return null; + } + + + @Data + public class PaymentDto { + private int code; + private boolean result; + private List data; + } + + + @Data + public class PaymentData { + private String blockNumber; + //大于等于支付订单的时间 + private String timeStamp; + //交易id + private String hash; + private String nonce; + private String blockHash; + //发送者钱包地址 + private String from; + private String contractAddress; + // 接收者钱包地址 + private String to; + // 转账金额一致 + private String value; + private String tokenName; + private String tokenSymbol; + private String tokenDecimal; + private String transactionIndex; + private String gas; + private String gasPrice; + private String gasUsed; + private String cumulativeGasUsed; + private String input; + private String methodId; + private String functionName; + private String confirmations; + } +} diff --git a/src/main/java/com/m2pool/lease/utils/JnaUtils.java b/src/main/java/com/m2pool/lease/utils/JnaUtils.java new file mode 100644 index 0000000..1fe8501 --- /dev/null +++ b/src/main/java/com/m2pool/lease/utils/JnaUtils.java @@ -0,0 +1,54 @@ +package com.m2pool.lease.utils; + +import java.math.BigInteger; + +/** + * @Description 支付工具类 + * @Date 2025/8/8 11:05 + * @Author yyb + */ +public class JnaUtils { + + private static final String key = "m2pool"; + + /** + * 获取签名秘钥 + * @param timeStamp + * @return + */ + public static String getSign(Long timeStamp){ + String inputStr = "1758610297"; + // 将字符串转换为 BigInteger 对象,指定基数为 10 + BigInteger decimalValue = new BigInteger(inputStr, 10); + // 将 BigInteger 对象转换为十六进制字符串 + String hexValue = decimalValue.toString(16); + // 输出结果长度为 32 字节 + byte[] output = new byte[32]; + try { + NativeLibrary.INSTANCE.alph_hash(output, hexValue); + // 处理输出结果 + } catch (Exception e) { + System.err.println("调用本地方法失败: " + e.getMessage()); + e.printStackTrace(); + } + + + String s = bytesToHex(output); + System.out.println("加密结果"+s); + return s; + } + + public static String bytesToHex(byte[] bytes) { + StringBuilder hexString = new StringBuilder(); + for (byte b : bytes) { + String hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + return hexString.toString(); + } + + +} diff --git a/src/main/java/com/m2pool/lease/utils/JwtUtils.java b/src/main/java/com/m2pool/lease/utils/JwtUtils.java new file mode 100644 index 0000000..253646c --- /dev/null +++ b/src/main/java/com/m2pool/lease/utils/JwtUtils.java @@ -0,0 +1,168 @@ +package com.m2pool.lease.utils; + + +import cn.hutool.core.convert.Convert; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; + +import java.util.Map; + +/** + * @Description Jwt工具类 + * @Date 2024/6/12 16:38 + * @Author dy + */ +public class JwtUtils +{ + public static String secret = TokenConstants.SECRET; + + /** + * 用户ID字段 + */ + public static final String DETAILS_USER_ID = "user_id"; + + /** + * 用户名字段 + */ + public static final String DETAILS_USERNAME = "username"; + + /** + * 授权信息字段 + */ + public static final String AUTHORIZATION_HEADER = "authorization"; + + /** + * 请求来源 + */ + public static final String FROM_SOURCE = "from-source"; + + /** + * 内部请求 + */ + public static final String INNER = "inner"; + + /** + * 用户标识 + */ + public static final String USER_KEY = "user_key"; + + /** + * 登录用户 + */ + public static final String LOGIN_USER = "login_user"; + + + /** + * 令牌自定义标识 + */ + public static final String AUTHENTICATION = "Authorization"; + + /** + * 缓存有效期,默认6个月 60*24*30*3=129600(分钟) + */ + public final static long EXPIRETIME = 129600; + /** + * 从数据声明生成令牌 + * + * @param claims 数据声明 + * @return 令牌 + */ + public static String createToken(Map claims) + { + String token = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact(); + return token; + } + + /** + * 从令牌中获取数据声明 + * + * @param token 令牌 + * @return 数据声明 + */ + public static Claims parseToken(String token) + { + return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); + } + + /** + * 根据令牌获取用户标识 + * + * @param token 令牌 + * @return 用户ID + */ + public static String getUserKey(String token) + { + Claims claims = parseToken(token); + return getValue(claims, USER_KEY); + } + + /** + * 根据令牌获取用户标识 + * + * @param claims 身份信息 + * @return 用户ID + */ + public static String getUserKey(Claims claims) + { + return getValue(claims, USER_KEY); + } + + /** + * 根据令牌获取用户ID + * + * @param token 令牌 + * @return 用户ID + */ + public static String getUserId(String token) + { + Claims claims = parseToken(token); + return getValue(claims, DETAILS_USER_ID); + } + + /** + * 根据身份信息获取用户ID + * + * @param claims 身份信息 + * @return 用户ID + */ + public static String getUserId(Claims claims) + { + return getValue(claims, DETAILS_USER_ID); + } + + /** + * 根据令牌获取用户名 + * + * @param token 令牌 + * @return 用户名 + */ + public static String getUserName(String token) + { + Claims claims = parseToken(token); + return getValue(claims, DETAILS_USERNAME); + } + + /** + * 根据身份信息获取用户名 + * + * @param claims 身份信息 + * @return 用户名 + */ + public static String getUserName(Claims claims) + { + return getValue(claims, DETAILS_USERNAME); + } + + /** + * 根据身份信息获取键值 + * + * @param claims 身份信息 + * @param key 键 + * @return 值 + */ + public static String getValue(Claims claims, String key) + { + return Convert.toStr(claims.get(key), ""); + } +} diff --git a/src/main/java/com/m2pool/lease/utils/NativeLibrary.java b/src/main/java/com/m2pool/lease/utils/NativeLibrary.java new file mode 100644 index 0000000..dd3ed1a --- /dev/null +++ b/src/main/java/com/m2pool/lease/utils/NativeLibrary.java @@ -0,0 +1,12 @@ +package com.m2pool.lease.utils; + +import com.sun.jna.Library; +import com.sun.jna.Native; + +public interface NativeLibrary extends Library { + // 加载本地库,注意在 Linux 下库名不需要带 lib 前缀和 .so 后缀 + NativeLibrary INSTANCE = Native.load("/home/ubuntu/cnative/libblake3.so", NativeLibrary.class); + + // 定义本地方法,方法签名要和 C 头文件中的函数签名对应 + void alph_hash(byte[] output, String input); +} diff --git a/src/main/java/com/m2pool/lease/utils/PowerUnitUtils.java b/src/main/java/com/m2pool/lease/utils/PowerUnitUtils.java new file mode 100644 index 0000000..71e1658 --- /dev/null +++ b/src/main/java/com/m2pool/lease/utils/PowerUnitUtils.java @@ -0,0 +1,60 @@ +package com.m2pool.lease.utils; + +import com.alibaba.nacos.common.utils.StringUtils; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * @Description 算力转换 + * @Date 2024/6/13 10:54 + * @Author dy + */ +public class PowerUnitUtils { + + /** + * 根据unit 对value 进行转换 + * @param unit + * @param value + * @return + */ + public static BigDecimal getPower(String unit,BigDecimal value) { + + + if (StringUtils.isEmpty(unit)) { + unit = "gh/s"; + } + unit = unit.toLowerCase(); + // 根据输入单位计算对应的转换因子 + BigDecimal conversionFactor; + switch (unit) { + case "h/s": + conversionFactor = BigDecimal.ONE; + break; + case "kh/s": + conversionFactor = BigDecimal.valueOf(1000L); + break; + case "mh/s": + conversionFactor = BigDecimal.valueOf(1000L).pow(2); + break; + case "gh/s": + conversionFactor = BigDecimal.valueOf(1000L).pow(3); + break; + case "th/s": + conversionFactor = BigDecimal.valueOf(1000L).pow(4); + break; + case "ph/s": + conversionFactor = BigDecimal.valueOf(1000L).pow(5); + break; + case "eh/s": + conversionFactor = BigDecimal.valueOf(1000L).pow(6); + break; + default: + throw new IllegalArgumentException("不支持的单位: " + unit); + } + + // 进行单位转换并保留小数 + return value.divide(conversionFactor,6, RoundingMode.HALF_UP); + } + +} diff --git a/src/main/java/com/m2pool/lease/utils/QrCodeUtils.java b/src/main/java/com/m2pool/lease/utils/QrCodeUtils.java new file mode 100644 index 0000000..ab022c8 --- /dev/null +++ b/src/main/java/com/m2pool/lease/utils/QrCodeUtils.java @@ -0,0 +1,94 @@ +package com.m2pool.lease.utils; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.EncodeHintType; +import com.google.zxing.MultiFormatWriter; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; +import org.apache.commons.codec.binary.Base64; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Hashtable; + +/** + * @Description URL转Base64二维码 + * @Date 2024/8/21 11:18 + * @Author 杜懿 + */ +public class QrCodeUtils { + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static String creatRrCode(String contents, int width, int height) { + String base64 = ""; + + Hashtable hints = new Hashtable(); + hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); + hints.put(EncodeHintType.MARGIN, 1); + + try { + BitMatrix bitMatrix = new MultiFormatWriter().encode(contents, BarcodeFormat.QR_CODE, width, height, hints); + bitMatrix = deleteWhite(bitMatrix); + + // 1、读取文件转换为字节数组 + ByteArrayOutputStream out = new ByteArrayOutputStream(); + BufferedImage image = toBufferedImage(bitMatrix); + + //转换成png格式的IO流 + ImageIO.write(image, "png", out); + byte[] bytes = out.toByteArray(); + + // 2、将字节数组转为二进制 + base64 = Base64.encodeBase64String(bytes).trim(); + + } catch (WriterException e) { + e.printStackTrace(); + + } catch (IOException e) { + e.printStackTrace(); + } + + return base64; + } + + /** + * image流数据处理 + */ + private static BufferedImage toBufferedImage(BitMatrix matrix) { + int width = matrix.getWidth(); + int height = matrix.getHeight(); + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + image.setRGB(x, y, matrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF); + } + } + return image; + } + + /** + * 去白边 + */ + private static BitMatrix deleteWhite(BitMatrix matrix) { + int[] rec = matrix.getEnclosingRectangle(); + int resWidth = rec[2] + 1; + int resHeight = rec[3] + 1; + + BitMatrix resMatrix = new BitMatrix(resWidth, resHeight); + resMatrix.clear(); + for (int i = 0; i < resWidth; i++) { + for (int j = 0; j < resHeight; j++) { + if (matrix.get(i + rec[0], j + rec[1])) + resMatrix.set(i, j); + } + } + return resMatrix; + } + + public static void main(String[] args) { + //System.out.println(creatRrCode("0x1c3d5d2a0eeb78476bcefadb44fbf24d29eeb964", 200,200)); + } + + +} diff --git a/src/main/java/com/m2pool/lease/utils/RsaUtils.java b/src/main/java/com/m2pool/lease/utils/RsaUtils.java new file mode 100644 index 0000000..d312f89 --- /dev/null +++ b/src/main/java/com/m2pool/lease/utils/RsaUtils.java @@ -0,0 +1,31 @@ +package com.m2pool.lease.utils; + +import javax.crypto.Cipher; +import java.nio.charset.StandardCharsets; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; + +public class RsaUtils { + public static final String PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsQVIKYozXCfnXUw8+omYLdcdL1pTzmQh35YPsvn22wM4SQJKvMmXmcS6bI5Bu+5zCjL0F56DzfKz0BNZEwb46UshUOO+KFBUr8CxjYE8NOgIsoe5FUn57O6er9/KySaWlkpGZX49K+l3e90R+dFUEfRE/ijYpeZWkLRwcgWZ+2u6HGpl9h/eF6XD0aW9asDjdAbxUQ48TlaWgfP+OHC+Zy2GKGQG16EcDMczrN6a2HbFnwRIUKrFP67UqyRq11BTUziOhXLY8J0MFuwXUk2OY4VpqjrJjHHjlHYADjIL/5K4Io2AhRU9+QSsKFR2wGxi4e8vw2IXDzscrDuah/7YSwIDAQAB"; // 可公开 + private static final String PRIVATE_KEY = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCxBUgpijNcJ+ddTDz6iZgt1x0vWlPOZCHflg+y+fbbAzhJAkq8yZeZxLpsjkG77nMKMvQXnoPN8rPQE1kTBvjpSyFQ474oUFSvwLGNgTw06Aiyh7kVSfns7p6v38rJJpaWSkZlfj0r6Xd73RH50VQR9ET+KNil5laQtHByBZn7a7ocamX2H94XpcPRpb1qwON0BvFRDjxOVpaB8/44cL5nLYYoZAbXoRwMxzOs3prYdsWfBEhQqsU/rtSrJGrXUFNTOI6FctjwnQwW7BdSTY5jhWmqOsmMceOUdgAOMgv/krgijYCFFT35BKwoVHbAbGLh7y/DYhcPOxysO5qH/thLAgMBAAECggEAYE00TczuVfMrbdkcz1ttW3rMWMd2EjDtMJqNXduYjNs6kpVZnUy6WZ/pn0bgweFd2i2QFw1YPQENC2SLL8u6PAhwTnYnK/1PpPFaZlXpMAsPODiX2toMyfWihKFly3pfr9EmbO55TttK8ip5LgPQDZhqtC36dn5Rl5ViGtWrSFgQOJm4Mu7px/6DbAu5c3u9yBqwA5Z90XDGIOivjjquQA4upexCvtBPYm/krCwsZcYHRBeihi+s4mt2Mz+a3u8NkJGxE7+QpXkWC0XR2KKiG4kdD6Sn+d9fG7UZUUBrb1p9Z5n3NV2oh3EKnrV3Pf/J0WEzrDZKBF4I9PMNzEkd2QKBgQDx9uW2HOqGnGv993L57+Twon7BxJHd0WQzdskTNEU7+cdNBYPbailjEOByGBPuupwOc2qTU6j+kyqSH5SoWDpnp1BnmFaJF6X52DJCQ6+2z5MfPApZbMDNSG/MOgPpHRjVcMfdPR/48W0S/1jlPh3ZIQ6bvL/011HYKqNF+NBp5QKBgQC7SfzSgqftZmLiZ4q4DfYGiWjSPQ+G4LwT+ehRNQ1IuJL8yO2tnuOxEN+Egxm5PEY55syeQ41alKWSeoRSFaopdCZZvPnuAEH8rhCgi0ymHIk+24TREZZMJhimBas77JCkRnWfJmfIPCXlv0fS4etzczx+Prntd5B7CKDKYPZWbwKBgQDw6Yblv/yycmQDxbCwse4cuW3Dt9gUJzvBEfA6Mr0MbjHFukwC1rzGajNO5jJSa3Kd4MSoU4/biOPGAbOdudrNAYXI36CpduZG0L+ZwpLdTeeKHdmgF+wPeKP3j3v9Sn9DZLtayFVhI+AaHiuAdEx7t7I31cK6IPjYZfuS252HEQKBgB6WK7r5aZFkyjtta4M5jHdu03aaUEHXy/97LcytXFkHA8JS0jYrn8XYGpjajEXrKRDlt38VSy2C3zN33MpHDX4toOHA8hRDaINqrf56IW9zc5qXYS5/r8sJ4bajcF6d+NVLSIRQUlyqLgpCXel7yecV58g6WEUG5MqjExdaFIDzAoGBAIlhI+jGPRn7lJVTZxik6/Kvj2KKpmpF+WMKer7hLuKd8Tu6iAldNjce9Y9uvD5lvyn+eSGG4OgEKKlPZ4vbY2c3AsYlsJu3ltaz203c61SVYa6tc3xeas9fiLUF7IXd6+6/1nDubG7mDwnMhmGV0pxVJKMUVqx+2LFDFqHaaTAN"; // 这里替换为你的实际私钥 + + public static String decrypt(String encryptedData) throws Exception { + // Base64解码加密数据 + byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData); + + // 加载私钥 + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(PRIVATE_KEY)); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PrivateKey privateKey = keyFactory.generatePrivate(keySpec); + + // 初始化Cipher进行解密 + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + + // 执行解密 + byte[] decryptedBytes = cipher.doFinal(encryptedBytes); + return new String(decryptedBytes, StandardCharsets.UTF_8); + } +} diff --git a/src/main/java/com/m2pool/lease/utils/SecurityUtils.java b/src/main/java/com/m2pool/lease/utils/SecurityUtils.java new file mode 100644 index 0000000..defd2ab --- /dev/null +++ b/src/main/java/com/m2pool/lease/utils/SecurityUtils.java @@ -0,0 +1,35 @@ +package com.m2pool.lease.utils; + +/** + * 线程变量 + */ +public class SecurityUtils { + /** + * 用户邮箱 + */ + private static final ThreadLocal USER_EMAIL = new ThreadLocal<>(); + /** + * 用户ID + */ + private static final ThreadLocal USER_ID = new ThreadLocal<>(); + + public static void setUserId(Long id) { + USER_ID.set(id); + } + public static void setUserEmail(String email) { + USER_EMAIL.set(email); + } + + public static String getUsername() { + return USER_EMAIL.get(); + } + + public static Long getUserId() { + return USER_ID.get(); + } + + public static void remove() { + USER_EMAIL.remove(); + USER_ID.remove(); + } +} diff --git a/src/main/java/com/m2pool/lease/utils/TOTP.java b/src/main/java/com/m2pool/lease/utils/TOTP.java new file mode 100644 index 0000000..ee506e4 --- /dev/null +++ b/src/main/java/com/m2pool/lease/utils/TOTP.java @@ -0,0 +1,97 @@ +package com.m2pool.lease.utils; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.lang.reflect.UndeclaredThrowableException; +import java.math.BigInteger; +import java.security.GeneralSecurityException; + +/** + * @Description 验证码生成工具类 + * @Date 2024/8/21 11:12 + * @Author 杜懿 + */ +public class TOTP { + + // 0 1 2 3 4 5 6 7 8 + private static final int[] DIGITS_POWER = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000}; + + /** + * This method uses the JCE to provide the crypto algorithm. HMAC computes a + * Hashed Message Authentication Code with the crypto hash algorithm as a + * parameter. + * + * @param crypto : the crypto algorithm (HmacSHA1, HmacSHA256, HmacSHA512) + * @param keyBytes : the bytes to use for the HMAC key + * @param text : the message or text to be authenticated + */ + private static byte[] hmac_sha(String crypto, byte[] keyBytes, byte[] text) { + try { + Mac hmac; + hmac = Mac.getInstance(crypto); + SecretKeySpec macKey = new SecretKeySpec(keyBytes, "RAW"); + hmac.init(macKey); + return hmac.doFinal(text); + } catch (GeneralSecurityException gse) { + throw new UndeclaredThrowableException(gse); + } + } + + /** + * This method converts a HEX string to Byte[] + * + * @param hex : the HEX string + * @return: a byte array + */ + private static byte[] hexStr2Bytes(String hex) { + // Adding one byte to get the right conversion + // Values starting with "0" can be converted + byte[] bArray = new BigInteger("10" + hex, 16).toByteArray(); + + // Copy all the REAL bytes, not the "first" + byte[] ret = new byte[bArray.length - 1]; + System.arraycopy(bArray, 1, ret, 0, ret.length); + return ret; + } + + /** + * This method generates a TOTP value for the given set of parameters. + * + * @param key : the shared secret, HEX encoded + * @param time : a value that reflects a time + * @param returnDigits : number of digits to return + * @param crypto : the crypto function to use + * @return: a numeric String in base 10 that includes + */ + public static String generateTOTP(String key, String time, String returnDigits, String crypto) { + int codeDigits = Integer.decode(returnDigits); + String result = null; + + // Using the counter + // First 8 bytes are for the movingFactor + // Compliant with base RFC 4226 (HOTP) + while (time.length() < 16) { + time = "0" + time; + } + + // Get the HEX in a Byte[] + byte[] msg = hexStr2Bytes(time); + byte[] k = hexStr2Bytes(key); + byte[] hash = hmac_sha(crypto, k, msg); + + // put selected bytes into result int + int offset = hash[hash.length - 1] & 0xf; + + int binary = ((hash[offset] & 0x7f) << 24) + | ((hash[offset + 1] & 0xff) << 16) + | ((hash[offset + 2] & 0xff) << 8) | (hash[offset + 3] & 0xff); + + int otp = binary % DIGITS_POWER[codeDigits]; + + result = Integer.toString(otp); + while (result.length() < codeDigits) { + result = "0" + result; + } + return result; + } +} diff --git a/src/main/java/com/m2pool/lease/utils/TokenConstants.java b/src/main/java/com/m2pool/lease/utils/TokenConstants.java new file mode 100644 index 0000000..c15e775 --- /dev/null +++ b/src/main/java/com/m2pool/lease/utils/TokenConstants.java @@ -0,0 +1,30 @@ +package com.m2pool.lease.utils; + +/** + * @Description Token的Key常量 + * @Date 2024/6/11 16:12 + * @Author dy + */ +public class TokenConstants { + + /** + * 令牌自定义标识 + */ + public static final String AUTHENTICATION = "Authorization"; + + public static final String API_KEY = "API-KEY"; + + /** + * 令牌前缀 + */ + public static final String PREFIX = "Bearer "; + + /** + * 令牌秘钥 + */ + //根据情况更改密钥 + public final static String SECRET = "mabsadba2basdsanwepsdaowwwxxsodddl"; + + public final static String API_SECRET = "mabsadba2bttxdnwcabpiowwwxxsodddl"; + +} diff --git a/src/main/java/com/m2pool/lease/utils/UuidGeneratorUtil.java b/src/main/java/com/m2pool/lease/utils/UuidGeneratorUtil.java new file mode 100644 index 0000000..0811426 --- /dev/null +++ b/src/main/java/com/m2pool/lease/utils/UuidGeneratorUtil.java @@ -0,0 +1,40 @@ +package com.m2pool.lease.utils; + +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.UUID; + +/** + * UUID 生成工具类,提供生成标准 UUID 和去除连字符的 UUID 的方法。 + */ +public class UuidGeneratorUtil { + + /** + * 生成标准格式的 UUID,包含连字符,例如:550e8400-e29b-41d4-a716-446655440000 + * + * @return 标准格式的 UUID 字符串 + */ + public static String generateStandardUuid() { + return UUID.randomUUID().toString(); + } + + /** + * 生成去除连字符的 UUID,例如:550e8400e29b41d4a716446655440000 + * + * @return 去除连字符的 UUID 字符串 + */ + public static String generateUuidWithoutHyphen() { + return generateStandardUuid().replace("-", ""); + } + + // 测试代码 + public static void main(String[] args) { + //System.out.println("标准 UUID: " + generateStandardUuid()); + //System.out.println("无连字符 UUID: " + generateUuidWithoutHyphen()); + long daysBetween = ChronoUnit.HOURS.between( + LocalDateTime.of(2025, 12, 9, 23, 43), + LocalDateTime.of(2025, 12, 12, 23, 42)); + System.out.println(daysBetween); + } + +} diff --git a/src/main/java/com/m2pool/lease/utils/WalletRuleCheckUtils.java b/src/main/java/com/m2pool/lease/utils/WalletRuleCheckUtils.java new file mode 100644 index 0000000..a7e8af4 --- /dev/null +++ b/src/main/java/com/m2pool/lease/utils/WalletRuleCheckUtils.java @@ -0,0 +1,182 @@ +package com.m2pool.lease.utils; + +import cn.hutool.core.net.URLEncoder; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.alibaba.nacos.common.utils.StringUtils; + +import java.nio.charset.StandardCharsets; +import java.util.regex.Pattern; + +public class WalletRuleCheckUtils { + + /** + * 校验地址 + * @param chainOrCoin + * @param address + * @return + */ + public static boolean checkAddress(String chainOrCoin, String address) { + if (address == null) { + return false; + } + chainOrCoin = chainOrCoin.toUpperCase(); + switch (chainOrCoin) { + case "TRON": + return checkTRON(address); + case "ETH": + return checkETH(address); + case "XTM": + return checkXTM(address); + case "IRON": + return checkIRON(address); + case "XEL": + return checkXEL(address); + case "RVN": + return checkRVN(address); + case "ERG": + return checkERG(address); + case "CFX": + return checkCFX(address); + case "NEXA": + return checkNEXA(address); + case "XNA": + return checkXNA(address); + case "KLS": + return checkKLS(address); + case "CLORE": + return checkCLORE(address); + default: + return false; + } + } + + // ---------------TRON校验------------------- + + private static final String API_KEY = "0925bdd7-7def-4a46-b65f-e17f02dc67c7"; + + /** + * 调用 Tronscan API 获取账户信息 + * @param address 要查询的账户地址 + * @return API 响应结果 + */ + public static boolean checkTRON(String address) { + String baseUrl = "https://apilist.tronscanapi.com/api/accountv2"; + // 对地址进行 URL 编码 + String encodedAddress = URLEncoder.createDefault().encode(address, StandardCharsets.UTF_8); + // 构建请求 URL + String url = baseUrl + "?address=" + encodedAddress; + // 发送带有请求头的 GET 请求 + HttpResponse response = HttpRequest.get(url) + .header("TRON-PRO-API-KEY", API_KEY) + .execute(); + + String s = parseResult(response.body()); + if (!StringUtils.isEmpty(s) && "some parameters are invalid or out of range".equals(s)){ + return false; + } + return true; + } + + /** + * 解析 API 响应结果,获取 result 字段 + * @param response API 响应结果 + * @return result 字段的值 + */ + private static String parseResult(String response) { + JSONObject jsonObject = JSONUtil.parseObj(response); + return jsonObject.getStr("message"); + } + + // ---------------TRON校验------------------- + + + + // ---------------ETH校验 目前只做了基础校验还需要EIP-55 校验------------------- + private static final Pattern ETH_BASIC_PATTERN = Pattern.compile("^0x[a-fA-F0-9]{40}$"); + + /** + * 基础格式校验以太坊地址 + * @param address 待校验的以太坊地址 + * @return 如果地址格式正确返回 true,否则返回 false + */ + public static boolean checkETH(String address) { + if (address == null) { + return false; + } + return ETH_BASIC_PATTERN.matcher(address).matches(); + } + // ---------------ETH校验------------------- + + + + // ---------------XTM校验------------------- + private static final Pattern XTM_PATTERN = Pattern.compile("^P[0-9a-zA-Z]{34}$"); + public static boolean checkXTM(String address) { + return StringUtils.isNotEmpty( address) && XTM_PATTERN.matcher(address).matches(); + } + + // ---------------IRON校验------------------- + private static final Pattern IRON_PATTERN = Pattern.compile("^i[0-9a-zA-Z]{33}$"); + public static boolean checkIRON(String address) { + return StringUtils.isNotEmpty( address) && IRON_PATTERN.matcher(address).matches(); + } + + // ---------------XEL校验------------------- + private static final Pattern XEL_PATTERN = Pattern.compile("^XEL[0-9a-zA-Z]{33}$"); + public static boolean checkXEL(String address) { + return StringUtils.isNotEmpty( address) && XEL_PATTERN.matcher(address).matches(); + } + + // ---------------RVN校验------------------- + private static final Pattern RVN_PATTERN = Pattern.compile("^R[0-9a-zA-Z]{33}$"); + public static boolean checkRVN(String address) { + return StringUtils.isNotEmpty( address) && RVN_PATTERN.matcher(address).matches(); + } + + // ---------------ERG校验------------------- + private static final Pattern ERG_PATTERN = Pattern.compile("^9[0-9a-zA-Z]{50}$"); + public static boolean checkERG(String address) { + return StringUtils.isNotEmpty( address) && ERG_PATTERN.matcher(address).matches(); + } + + // ---------------CFX校验------------------- + private static final Pattern CFX_PATTERN = Pattern.compile("^cfx:[a-z0-9]{42}$"); + public static boolean checkCFX(String address) { + return StringUtils.isNotEmpty( address) && CFX_PATTERN.matcher(address).matches(); + } + + // ---------------NEXA校验------------------- + private static final Pattern NEXA_PATTERN = Pattern.compile("^nexa:[0-9a-zA-Z]{40}$"); + private static final Pattern NEXA_PATTERN2 = Pattern.compile("^[0-9a-zA-Z]{40}$"); + public static boolean checkNEXA(String address) { + if (address.startsWith("nexa:")){ + return StringUtils.isNotEmpty(address) && NEXA_PATTERN.matcher(address).matches(); + }else{ + return StringUtils.isNotEmpty(address) && NEXA_PATTERN2.matcher(address).matches(); + } + } + + // ---------------XNA校验------------------- + private static final Pattern XNA_PATTERN = Pattern.compile("^X[0-9a-zA-Z]{33}$"); + public static boolean checkXNA(String address) { + return StringUtils.isNotEmpty( address) && XNA_PATTERN.matcher(address).matches(); + } + + // ---------------KLS校验------------------- + private static final Pattern KLS_PATTERN = Pattern.compile("^k[0-9a-zA-Z]{33}$"); + public static boolean checkKLS(String address) { + return StringUtils.isNotEmpty( address) && KLS_PATTERN.matcher(address).matches(); + } + + // ---------------CLORE校验------------------- + private static final Pattern CLORE_PATTERN = Pattern.compile("^C[0-9a-zA-Z]{33}$"); + public static boolean checkCLORE(String address) { + return StringUtils.isNotEmpty( address) && CLORE_PATTERN.matcher(address).matches(); + } + public static void main(String[] args) { + + } +} diff --git a/src/main/java/com/m2pool/lease/utils/email/EmailTemplateEntity.java b/src/main/java/com/m2pool/lease/utils/email/EmailTemplateEntity.java new file mode 100644 index 0000000..15063a9 --- /dev/null +++ b/src/main/java/com/m2pool/lease/utils/email/EmailTemplateEntity.java @@ -0,0 +1,26 @@ +package com.m2pool.lease.utils.email; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Map; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class EmailTemplateEntity implements Serializable { + + /** 邮箱 */ + private String email; + + /** 主题 */ + private String subject; + + /** 模版名称 */ + private String templateName; + + /** 填充到模版的数据 */ + private Map data; +} diff --git a/src/main/java/com/m2pool/lease/utils/text/Convert.java b/src/main/java/com/m2pool/lease/utils/text/Convert.java new file mode 100644 index 0000000..b212f1f --- /dev/null +++ b/src/main/java/com/m2pool/lease/utils/text/Convert.java @@ -0,0 +1,906 @@ +package com.m2pool.lease.utils.text; + + +import com.alibaba.nacos.common.utils.StringUtils; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.text.NumberFormat; +import java.util.Set; + +/** + * @Description 类型转换器 + * @Date 2024/6/11 9:13 + * @Author dy + */ +public class Convert +{ + /** + * 转换为字符串
+ * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static String toStr(Object value, String defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof String) + { + return (String) value; + } + return value.toString(); + } + + /** + * 转换为字符串
+ * + * @param value 被转换的值 + * @return 结果 + */ + public static String toStr(Object value) + { + return toStr(value, null); + } + + /** + * 转换为字符
+ * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Character toChar(Object value, Character defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof Character) + { + return (Character) value; + } + + final String valueStr = toStr(value, null); + return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0); + } + + /** + * 转换为字符
+ * + * @param value 被转换的值 + * @return 结果 + */ + public static Character toChar(Object value) + { + return toChar(value, null); + } + + /** + * 转换为byte
+ * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Byte toByte(Object value, Byte defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Byte) + { + return (Byte) value; + } + if (value instanceof Number) + { + return ((Number) value).byteValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Byte.parseByte(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为byte
+ * + * @param value 被转换的值 + * @return 结果 + */ + public static Byte toByte(Object value) + { + return toByte(value, null); + } + + /** + * 转换为Short
+ * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Short toShort(Object value, Short defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Short) + { + return (Short) value; + } + if (value instanceof Number) + { + return ((Number) value).shortValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Short.parseShort(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Short
+ * + * @param value 被转换的值 + * @return 结果 + */ + public static Short toShort(Object value) + { + return toShort(value, null); + } + + /** + * 转换为Number
+ * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Number toNumber(Object value, Number defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Number) + { + return (Number) value; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return NumberFormat.getInstance().parse(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Number
+ * + * @param value 被转换的值 + * @return 结果 + */ + public static Number toNumber(Object value) + { + return toNumber(value, null); + } + + /** + * 转换为int
+ * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Integer toInt(Object value, Integer defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Integer) + { + return (Integer) value; + } + if (value instanceof Number) + { + return ((Number) value).intValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Integer.parseInt(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为int
+ * + * @param value 被转换的值 + * @return 结果 + */ + public static Integer toInt(Object value) + { + return toInt(value, null); + } + + /** + * 转换为Integer数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String str) + { + return toIntArray(",", str); + } + + /** + * 转换为Long数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String str) + { + return toLongArray(",", str); + } + + /** + * 转换为Integer数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Integer[] {}; + } + String[] arr = str.split(split); + final Integer[] ints = new Integer[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Integer v = toInt(arr[i], 0); + ints[i] = v; + } + return ints; + } + + /** + * 转换为Long数组
+ * + * @param split 分隔符 + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Long[] {}; + } + String[] arr = str.split(split); + final Long[] longs = new Long[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Long v = toLong(arr[i], null); + longs[i] = v; + } + return longs; + } + + /** + * 转换为String数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String str) + { + return toStrArray(",", str); + } + + /** + * 转换为String数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String split, String str) + { + return str.split(split); + } + + /** + * 转换为long
+ * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Long toLong(Object value, Long defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Long) + { + return (Long) value; + } + if (value instanceof Number) + { + return ((Number) value).longValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).longValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为long
+ * + * @param value 被转换的值 + * @return 结果 + */ + public static Long toLong(Object value) + { + return toLong(value, null); + } + + /** + * 转换为double
+ * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Double toDouble(Object value, Double defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Double) + { + return (Double) value; + } + if (value instanceof Number) + { + return ((Number) value).doubleValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).doubleValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为double
+ * + * @param value 被转换的值 + * @return 结果 + */ + public static Double toDouble(Object value) + { + return toDouble(value, null); + } + + /** + * 转换为Float
+ * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Float toFloat(Object value, Float defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Float) + { + return (Float) value; + } + if (value instanceof Number) + { + return ((Number) value).floatValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Float.parseFloat(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Float
+ * + * @param value 被转换的值 + * @return 结果 + */ + public static Float toFloat(Object value) + { + return toFloat(value, null); + } + + /** + * 转换为boolean
+ * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Boolean toBool(Object value, Boolean defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Boolean) + { + return (Boolean) value; + } + String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + valueStr = valueStr.trim().toLowerCase(); + switch (valueStr) + { + case "true": + return true; + case "false": + return false; + case "yes": + return true; + case "ok": + return true; + case "no": + return false; + case "1": + return true; + case "0": + return false; + default: + return defaultValue; + } + } + + /** + * 转换为boolean
+ * + * @param value 被转换的值 + * @return 结果 + */ + public static Boolean toBool(Object value) + { + return toBool(value, null); + } + + /** + * 转换为BigInteger
+ * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value, BigInteger defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigInteger) + { + return (BigInteger) value; + } + if (value instanceof Long) + { + return BigInteger.valueOf((Long) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigInteger(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigInteger
+ * + * @param value 被转换的值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value) + { + return toBigInteger(value, null); + } + + /** + * 转换为BigDecimal
+ * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigDecimal) + { + return (BigDecimal) value; + } + if (value instanceof Long) + { + return new BigDecimal((Long) value); + } + if (value instanceof Double) + { + return new BigDecimal((Double) value); + } + if (value instanceof Integer) + { + return new BigDecimal((Integer) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigDecimal(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value) + { + return toBigDecimal(value, null); + } + + /** + * 将对象转为字符串
+ * + * @param obj 对象 + * @param charsetName 字符集 + * @return 字符串 + */ + public static String str(Object obj, String charsetName) + { + return str(obj, Charset.forName(charsetName)); + } + + /** + * 将对象转为字符串
+ * + * @param obj 对象 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(Object obj, Charset charset) + { + if (null == obj) + { + return null; + } + + if (obj instanceof String) + { + return (String) obj; + } + else if (obj instanceof byte[] || obj instanceof Byte[]) + { + if (obj instanceof byte[]) + { + return str((byte[]) obj, charset); + } + else + { + Byte[] bytes = (Byte[]) obj; + int length = bytes.length; + byte[] dest = new byte[length]; + for (int i = 0; i < length; i++) + { + dest[i] = bytes[i]; + } + return str(dest, charset); + } + } + else if (obj instanceof ByteBuffer) + { + return str((ByteBuffer) obj, charset); + } + return obj.toString(); + } + + /** + * 将byte数组转为字符串 + * + * @param bytes byte数组 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(byte[] bytes, String charset) + { + return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset)); + } + + /** + * 解码字节码 + * + * @param data 字符串 + * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 + * @return 解码后的字符串 + */ + public static String str(byte[] data, Charset charset) + { + if (data == null) + { + return null; + } + + if (null == charset) + { + return new String(data); + } + return new String(data, charset); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, String charset) + { + if (data == null) + { + return null; + } + + return str(data, Charset.forName(charset)); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, Charset charset) + { + if (null == charset) + { + charset = Charset.defaultCharset(); + } + return charset.decode(data).toString(); + } + + // ----------------------------------------------------------------------- 全角半角转换 + /** + * 半角转全角 + * + * @param input String. + * @return 全角字符串. + */ + public static String toSBC(String input) + { + return toSBC(input, null); + } + + /** + * 半角转全角 + * + * @param input String + * @param notConvertSet 不替换的字符集合 + * @return 全角字符串. + */ + public static String toSBC(String input, Set notConvertSet) + { + char c[] = input.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == ' ') + { + c[i] = '\u3000'; + } + else if (c[i] < '\177') + { + c[i] = (char) (c[i] + 65248); + + } + } + return new String(c); + } + + /** + * 全角转半角 + * + * @param input String. + * @return 半角字符串 + */ + public static String toDBC(String input) + { + return toDBC(input, null); + } + + /** + * 替换全角为半角 + * + * @param text 文本 + * @param notConvertSet 不替换的字符集合 + * @return 替换后的字符 + */ + public static String toDBC(String text, Set notConvertSet) + { + char c[] = text.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == '\u3000') + { + c[i] = ' '; + } + else if (c[i] > '\uFF00' && c[i] < '\uFF5F') + { + c[i] = (char) (c[i] - 65248); + } + } + String returnString = new String(c); + + return returnString; + } + + /** + * 数字金额大写转换 先写个完整的然后将如零拾替换成零 + * + * @param n 数字 + * @return 中文大写数字 + */ + public static String digitUppercase(double n) + { + String[] fraction = { "角", "分" }; + String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" }; + String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } }; + + String head = n < 0 ? "负" : ""; + n = Math.abs(n); + + String s = ""; + for (int i = 0; i < fraction.length; i++) + { + s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", ""); + } + if (s.length() < 1) + { + s = "整"; + } + int integerPart = (int) Math.floor(n); + + for (int i = 0; i < unit[0].length && integerPart > 0; i++) + { + String p = ""; + for (int j = 0; j < unit[1].length && n > 0; j++) + { + p = digit[integerPart % 10] + unit[1][j] + p; + integerPart = integerPart / 10; + } + s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s; + } + return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整"); + } +} diff --git a/src/main/java/com/m2pool/lease/utils/uuid/IdUtils.java b/src/main/java/com/m2pool/lease/utils/uuid/IdUtils.java new file mode 100644 index 0000000..d52a806 --- /dev/null +++ b/src/main/java/com/m2pool/lease/utils/uuid/IdUtils.java @@ -0,0 +1,51 @@ +package com.m2pool.lease.utils.uuid; + +import cn.hutool.core.lang.UUID; + +/** + * @Description ID生成器 + * @Date 2024/6/13 11:24 + * @Author dy + */ +public class IdUtils +{ + /** + * 获取随机UUID + * + * @return 随机UUID + */ + public static String randomUUID() + { + return UUID.randomUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线 + * + * @return 简化的UUID,去掉了横线 + */ + public static String simpleUUID() + { + return UUID.randomUUID().toString(true); + } + + /** + * 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 随机UUID + */ + public static String fastUUID() + { + return UUID.fastUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 简化的UUID,去掉了横线 + */ + public static String fastSimpleUUID() + { + return UUID.fastUUID().toString(true); + } +} diff --git a/src/main/java/com/m2pool/lease/vo/BalancePageVo.java b/src/main/java/com/m2pool/lease/vo/BalancePageVo.java new file mode 100644 index 0000000..8ed6504 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/BalancePageVo.java @@ -0,0 +1,29 @@ +package com.m2pool.lease.vo; + +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; + +/** + *

+ * 余额提现/充值分页请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "余额提现/充值分页请求对象",value = "BalancePageVo") +public class BalancePageVo extends PageVo{ + + @ApiModelProperty(value = "记录状态 充值业务 : 0 充值失败 1 充值成功 2 充值中 3 证书校验失败 ---提现业务: 0 提现失败 1 提现成功 2 提现中 3 证书校验失败 支付服务:0 支付失败 1 支付成功 2 支付中 3 证书校验失败 ") + private Integer status; +} diff --git a/src/main/java/com/m2pool/lease/vo/BalanceVo.java b/src/main/java/com/m2pool/lease/vo/BalanceVo.java new file mode 100644 index 0000000..c148c35 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/BalanceVo.java @@ -0,0 +1,71 @@ +package com.m2pool.lease.vo; + +import com.m2pool.lease.annotation.EncryptedField; +import com.m2pool.lease.annotation.LedgerLogParam; +import com.m2pool.lease.annotation.LedgerLogTag; +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; + +/** + *

+ * 余额提现请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "余额提现请求对象",value = "BalanceVo") +public class BalanceVo { + + + /** + * 提现金额 + */ + @ApiModelProperty(value = "提现 金额",required = true) + @LedgerLogParam("提现金额") + private BigDecimal amount; + + /** + * 链 + */ + @ApiModelProperty(value = "用户自定义的钱包地址链",required = true) + @LedgerLogParam("链") + private String toChain; + + /** + * 币种 + */ + @ApiModelProperty(value = "用户自定义的钱包地址的币种",required = true) + @LedgerLogParam("币种") + private String toSymbol; + + @ApiModelProperty(value = "手续费",required = true) + private BigDecimal serviceCharge; + + @ApiModelProperty(value = "绑定的地址",required = true) + @EncryptedField + @LedgerLogTag + private String fromAddress; + /** + * 提现地址(用户自定义) + */ + @ApiModelProperty(value = "提现地址(用户自定义)",required = true) + @EncryptedField + @LedgerLogParam("提现目标地址") + private String toAddress; + + @ApiModelProperty(value = "谷歌双重验证验证码",required = true) + private Long code; + + +} diff --git a/src/main/java/com/m2pool/lease/vo/BaseVo.java b/src/main/java/com/m2pool/lease/vo/BaseVo.java new file mode 100644 index 0000000..e949d0e --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/BaseVo.java @@ -0,0 +1,23 @@ +package com.m2pool.lease.vo; + +import io.swagger.annotations.Api; +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/7/25 11:15 + * @Author yyb + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "基础请求对象",value = "BaseVo" ) +public class BaseVo { + @ApiModelProperty(value = "id") + private Long id; +} diff --git a/src/main/java/com/m2pool/lease/vo/ChainAndCoinVo.java b/src/main/java/com/m2pool/lease/vo/ChainAndCoinVo.java new file mode 100644 index 0000000..a0d6a08 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/ChainAndCoinVo.java @@ -0,0 +1,30 @@ +package com.m2pool.lease.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + *

+ * 余额提现/充值分页请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "充值步骤选择链和钱包后自动绑定钱包请求对象",value = "ChainAndCoinVo") +public class ChainAndCoinVo { + + @ApiModelProperty(value = "链名称",example = "TRON") + private String chain; + + @ApiModelProperty(value = "币种名称",example = "BTC") + private String coin; +} diff --git a/src/main/java/com/m2pool/lease/vo/CheckPayStatusVo.java b/src/main/java/com/m2pool/lease/vo/CheckPayStatusVo.java new file mode 100644 index 0000000..b90d9c9 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/CheckPayStatusVo.java @@ -0,0 +1,55 @@ +package com.m2pool.lease.vo; + +import io.swagger.annotations.Api; +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; + +/** + *

+ * 支付校验请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "支付校验请求对象",value = "CheckPayStatusVo") +public class CheckPayStatusVo { + + @ApiModelProperty(value = "支付订单ID") + private Long id; + + //@ApiModelProperty(value = "订单ID") + //private Long orderId; + // + //@ApiModelProperty(value = "商铺ID") + //private Long shopId; + // + //@ApiModelProperty(value = "支付订单状态") + //private Integer status; + + + /** + * 卖方地址 + */ + @ApiModelProperty(value = "买方钱包地址") + private String form; + + /** + * 交易id + */ + @ApiModelProperty(value = "交易id,多个交易id以英文','隔开") + private String hash; + + + +} diff --git a/src/main/java/com/m2pool/lease/vo/CoinVo.java b/src/main/java/com/m2pool/lease/vo/CoinVo.java new file mode 100644 index 0000000..99af1ec --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/CoinVo.java @@ -0,0 +1,22 @@ +package com.m2pool.lease.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Description 币种请求对象 + * @Date 2025/7/25 11:15 + * @Author yyb + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "币种请求对象",value = "CoinVo" ) +public class CoinVo { + + @ApiModelProperty(value = "币种") + private String coin; +} diff --git a/src/main/java/com/m2pool/lease/vo/EmailCodeVo.java b/src/main/java/com/m2pool/lease/vo/EmailCodeVo.java new file mode 100644 index 0000000..021c25a --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/EmailCodeVo.java @@ -0,0 +1,23 @@ +package com.m2pool.lease.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.Email; +import javax.validation.constraints.NotNull; + +/** + * @Description 用户登录对象 + * @Date 2024/6/12 16:13 + * @Author dy + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class EmailCodeVo { + /** 邮箱 */ + @NotNull(message = "用户邮箱不能为空") + @Email(message = "邮箱格式错误") + private String email; +} diff --git a/src/main/java/com/m2pool/lease/vo/OrderAndCodeVo.java b/src/main/java/com/m2pool/lease/vo/OrderAndCodeVo.java new file mode 100644 index 0000000..97dd420 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/OrderAndCodeVo.java @@ -0,0 +1,32 @@ +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.util.List; + +/** + * @Description 支付订单请求对象 + * @Date 2025/7/25 11:15 + * @Author yyb + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "订单结算及谷歌验证码请求对象",value = "OrderAndCodeVo" ) +public class OrderAndCodeVo { + + + @ApiModelProperty(value = "谷歌验证码",required = true) + private Long code; + + @ApiModelProperty(value = "订单信息列表",required = true) + private List orderInfoVoList; + + @ApiModelProperty(value = "挖矿配置",required = true) + private List orderMiningInfoDtoList; +} diff --git a/src/main/java/com/m2pool/lease/vo/OrderInfoStateVo.java b/src/main/java/com/m2pool/lease/vo/OrderInfoStateVo.java new file mode 100644 index 0000000..80fd167 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/OrderInfoStateVo.java @@ -0,0 +1,31 @@ +package com.m2pool.lease.vo; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + *

+ * 订单状态请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "订单状态请求对象",value = "OrderInfoStateVo" ) +public class OrderInfoStateVo extends PageVo{ + + /** + * 7 订单进行中 8 订单已完成 + */ + @ApiModelProperty(value = " 7 订单进行中 8 订单已完成") + private Integer status; +} diff --git a/src/main/java/com/m2pool/lease/vo/OrderInfoURDVo.java b/src/main/java/com/m2pool/lease/vo/OrderInfoURDVo.java new file mode 100644 index 0000000..9f57461 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/OrderInfoURDVo.java @@ -0,0 +1,52 @@ +package com.m2pool.lease.vo; + +import io.swagger.annotations.Api; +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; + +/** + *

+ * 订单请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "订单请求对象",value = "OrderInfoURDVo" ) +public class OrderInfoURDVo extends BaseVo{ + + /** + * 订单号 + */ + @ApiModelProperty(value = "订单号") + private String orderNumber; + + /** + * 用户 ID + */ + @ApiModelProperty(value = "用户 ID") + private Long userId; + + /** + * 订单总价 + */ + @ApiModelProperty(value = "订单总价") + private BigDecimal totalPrice; + + /** + * 收货地址(模拟) + */ + @ApiModelProperty(value = "收货地址(模拟)") + private String address; + +} diff --git a/src/main/java/com/m2pool/lease/vo/OrderInfoVo.java b/src/main/java/com/m2pool/lease/vo/OrderInfoVo.java new file mode 100644 index 0000000..a31f17b --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/OrderInfoVo.java @@ -0,0 +1,52 @@ +package com.m2pool.lease.vo; + +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; + +/** + *

+ * 订单生成请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "订单生成请求对象",value = "OrderInfoVo") +public class OrderInfoVo extends BaseVo{ + + @ApiModelProperty(value = "店铺ID",example = "8") + private Long shopId; + + @ApiModelProperty(value = "商品 ID",example = "2") + private Long productId; + + @ApiModelProperty(value = "矿机 ID",example = "1") + private Long machineId; + + @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; + +} diff --git a/src/main/java/com/m2pool/lease/vo/OrderItemVo.java b/src/main/java/com/m2pool/lease/vo/OrderItemVo.java new file mode 100644 index 0000000..25eb2b9 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/OrderItemVo.java @@ -0,0 +1,47 @@ +package com.m2pool.lease.vo; + +import io.swagger.annotations.Api; +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; + +/** + *

+ * 订单明细请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "订单明细请求对象",value = "OrderItemVo") +public class OrderItemVo extends BaseVo{ + /** + * 机器id + */ + @ApiModelProperty(value = "机器id",example = "1") + private Long productMachineId; + + @ApiModelProperty(value = "矿工账号") + private String user; + + @ApiModelProperty(value = "矿机编号") + private String miner; + + /** + * 商品单价 + */ + @ApiModelProperty(value = "商品价格") + private BigDecimal price; + + @ApiModelProperty(value = "租赁天数",example = "1") + private Integer leaseTime; +} diff --git a/src/main/java/com/m2pool/lease/vo/OrderVo.java b/src/main/java/com/m2pool/lease/vo/OrderVo.java new file mode 100644 index 0000000..9018269 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/OrderVo.java @@ -0,0 +1,21 @@ +package com.m2pool.lease.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Description 支付订单请求对象 + * @Date 2025/7/25 11:15 + * @Author yyb + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "支付订单请求对象",value = "OrderVo" ) +public class OrderVo { + @ApiModelProperty(value = "普通订单id") + private Long orderId; +} diff --git a/src/main/java/com/m2pool/lease/vo/PageVo.java b/src/main/java/com/m2pool/lease/vo/PageVo.java new file mode 100644 index 0000000..379e7a7 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/PageVo.java @@ -0,0 +1,21 @@ +package com.m2pool.lease.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @Description 分页请求对象 + * @Date 2025/5/23 09:52 + * @Author yyb + */ +@Data +@ApiModel(value = "分页请求对象") +public class PageVo { + @ApiModelProperty(value = "当前页码 (默认为1)",example = "1") + private Integer pageNum = 1; + + @ApiModelProperty(value = "每页条数(默认为20)",example = "20") + private Integer pageSize = 20; + +} diff --git a/src/main/java/com/m2pool/lease/vo/PayVo.java b/src/main/java/com/m2pool/lease/vo/PayVo.java new file mode 100644 index 0000000..8e1279e --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/PayVo.java @@ -0,0 +1,48 @@ +package com.m2pool.lease.vo; + +import io.swagger.annotations.Api; +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; + +/** + *

+ * 支付请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "支付请求对象",value = "PayVo") +public class PayVo { + + /** + * 支付方式,nexa rxd dgbo dgbq dgbs alph enx grs mona usdt usdc busd + */ + private String coin; + + /** + * 支付金额 + */ + private BigDecimal amount; + + /** + * 支付地址 + */ + private String address; + + /** + * 支付订单生成时间戳 + */ + private Long ts; + +} diff --git a/src/main/java/com/m2pool/lease/vo/PriceCalculateVo.java b/src/main/java/com/m2pool/lease/vo/PriceCalculateVo.java new file mode 100644 index 0000000..f1c7dbe --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/PriceCalculateVo.java @@ -0,0 +1,26 @@ +package com.m2pool.lease.vo; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +/** + * @Description 算力价格计算器请求对象 + * @Date 2025/7/25 11:15 + * @Author yyb + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "算力价格计算器请求对象",value = "PriceCalculateVo" ) +public class PriceCalculateVo { + + @ApiModelProperty(value = "机器实时算力") + private BigDecimal power; + +} diff --git a/src/main/java/com/m2pool/lease/vo/ProductAndMachineVo.java b/src/main/java/com/m2pool/lease/vo/ProductAndMachineVo.java new file mode 100644 index 0000000..f2a5a99 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/ProductAndMachineVo.java @@ -0,0 +1,25 @@ +package com.m2pool.lease.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Description 购物车商品机器删除请求对象 + * @Date 2025/7/25 11:15 + * @Author yyb + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "购物车商品机器删除请求对象",value = "ProductAndMachineVo" ) +public class ProductAndMachineVo { + @ApiModelProperty(value = "商品id") + private Long productId; + + @ApiModelProperty(value = "机器id") + private Long machineId; + +} diff --git a/src/main/java/com/m2pool/lease/vo/ProductForUpdateMachineVo.java b/src/main/java/com/m2pool/lease/vo/ProductForUpdateMachineVo.java new file mode 100644 index 0000000..7401e05 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/ProductForUpdateMachineVo.java @@ -0,0 +1,21 @@ +package com.m2pool.lease.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Description 卖方查询商品矿机列表请求对象 + * @Date 2025/7/25 11:15 + * @Author yyb + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "卖方查询商品矿机列表请求对象",value = "ProductForUpdateMachineVo" ) +public class ProductForUpdateMachineVo extends PageVo{ + @ApiModelProperty(value = "商品id") + private Long id; +} diff --git a/src/main/java/com/m2pool/lease/vo/ProductMachineParamsVo.java b/src/main/java/com/m2pool/lease/vo/ProductMachineParamsVo.java new file mode 100644 index 0000000..bd8e9ce --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/ProductMachineParamsVo.java @@ -0,0 +1,52 @@ +package com.m2pool.lease.vo; + +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; + +/** + *

+ * 机器参数请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "机器参数请求对象",value = "ProductMachineParamsVo" ) +public class ProductMachineParamsVo{ + + @ApiModelProperty(value = "商品 ID",example = "2") + private Long productId; + + @ApiModelProperty(value = "矿机型号",example = "龍珠") + private String type; + + @ApiModelProperty(value = "商品机器单机理论算力(卖方手动填写)",example = "0.01") + private BigDecimal theoryPower; + + @ApiModelProperty(value = "商品机器单机算力单位",example = "TH/S") + private String unit; + + @ApiModelProperty(value = "功耗",example = "0.01") + private BigDecimal powerDissipation; + + @ApiModelProperty(value = "机器成本价格单位$ [ 功耗 * 电费 * 24 * (1 + 收益率) ]",example = "0.01") + private BigDecimal cost; + + + @ApiModelProperty(value = "最大可租借天数(默认七天)",example = "7") + private Integer maxLeaseDays; + + @ApiModelProperty(value = "商品对应的机器列表") + List productMachineURDVos; +} diff --git a/src/main/java/com/m2pool/lease/vo/ProductMachineURDVo.java b/src/main/java/com/m2pool/lease/vo/ProductMachineURDVo.java new file mode 100644 index 0000000..bd561cc --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/ProductMachineURDVo.java @@ -0,0 +1,70 @@ +package com.m2pool.lease.vo; + +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; + +/** + *

+ * 商品对应实际商品请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "商品对应实际商品请求对象",value = "ProductMachineURDVo" ) +public class ProductMachineURDVo extends BaseVo{ + + @ApiModelProperty(value = "矿机型号",example = "龍珠") + private String type; + + /** + * 挖矿机器 对应的矿工账号 + */ + @ApiModelProperty(value = "挖矿机器 对应的矿工账号",example = "a20") + private String user; + + /** + * 挖矿机器编号 + */ + @ApiModelProperty(value = "挖矿机器编号",example = "100x1") + private String miner; + + /** + * 单价 + */ + @ApiModelProperty(value = "实际价格(同一个商品下的矿机单价默认为商品表的cost字段值= 功耗 * 电费 $/度 * 24h * (1+收益率) 用户可以自定义该价格)",example = "2760.00") + private BigDecimal price; + + /** + * 上下架状态,0 上架,1 下架 + */ + @ApiModelProperty(value = "上下架状态,0 上架,1 下架",example = "1") + private Integer state; + + @ApiModelProperty(value = "最大可租借天数(默认七天)",example = "7") + private Integer maxLeaseDays; + + @ApiModelProperty(value = "商品机器单机理论算力(卖方手动填写)",example = "0.01") + private BigDecimal theoryPower; + + @ApiModelProperty(value = "商品机器单机算力单位",example = "TH/S") + private String unit; + + @ApiModelProperty(value = "功耗",example = "0.01") + private BigDecimal powerDissipation; + + @ApiModelProperty(value = "币种价格配置") + private List priceList; +} diff --git a/src/main/java/com/m2pool/lease/vo/ProductMachineVo.java b/src/main/java/com/m2pool/lease/vo/ProductMachineVo.java new file mode 100644 index 0000000..78408b4 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/ProductMachineVo.java @@ -0,0 +1,69 @@ +package com.m2pool.lease.vo; + +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; + +/** + *

+ * 首页商品详情页矿机列表请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "首页商品详情页矿机列表请求对象",value = "ProductMachineVo" ) +public class ProductMachineVo extends PageVo{ + + @ApiModelProperty(value = "商品 ID") + private Long id; + + + @ApiModelProperty(value = "最大算力") + private BigDecimal maxPower = BigDecimal.ZERO; + + @ApiModelProperty(value = "最小算力") + private BigDecimal minPower = BigDecimal.ZERO; + + @ApiModelProperty(value = "最大功耗 单位kw/h",example = "10") + private BigDecimal maxPowerDissipation = BigDecimal.ZERO; + + @ApiModelProperty(value = "最小功耗 单位kw/h",example = "10") + private BigDecimal minPowerDissipation = BigDecimal.ZERO; + + + @ApiModelProperty(value = "最大单价") + private BigDecimal maxPrice = BigDecimal.ZERO; + + @ApiModelProperty(value = "最小单价") + private BigDecimal minPrice = BigDecimal.ZERO; + + @ApiModelProperty(value = "币种") + private String coin; + + @ApiModelProperty(value = "链") + private String chain; + + @ApiModelProperty(value = "价格排序方式 true:降序 false:升序") + private Boolean priceSort; + + @ApiModelProperty(value = "实际算力排序方式 true:降序 false:升序") + private Boolean powerSort; + + @ApiModelProperty(value = "功耗排序方式 true:降序 false:升序") + private Boolean powerDissipationSort; + + @ApiModelProperty(value = "币种单位") + private String unit; + + +} diff --git a/src/main/java/com/m2pool/lease/vo/ProductPageVo.java b/src/main/java/com/m2pool/lease/vo/ProductPageVo.java new file mode 100644 index 0000000..b33ec81 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/ProductPageVo.java @@ -0,0 +1,38 @@ +package com.m2pool.lease.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +/** + *

+ * 商品列表分页请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "商品列表分页请求对象",value = "ProductPageVo" ) +public class ProductPageVo extends PageVo{ + + /** + * 矿机挖矿币种 nexa rxd dgbo dgbq dgbs alph enx grs mona + */ + @ApiModelProperty(value = "矿机挖矿币种 nexa rxd dgbo dgbq dgbs alph enx grs mona",example = "nexa") + private String coin; + /** + * 算法 + */ + @ApiModelProperty(value = "算法") + private String algorithm; + + @ApiModelProperty(value = "用户邮箱(个人中心商品列表)") + private String userEmail; +} diff --git a/src/main/java/com/m2pool/lease/vo/ProductURDVo.java b/src/main/java/com/m2pool/lease/vo/ProductURDVo.java new file mode 100644 index 0000000..e9ef8db --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/ProductURDVo.java @@ -0,0 +1,59 @@ +package com.m2pool.lease.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +/** + *

+ * 商品返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "商品返回对象",value = "ProductURDVo" ) +public class ProductURDVo extends BaseVo{ + + @ApiModelProperty(value = "上下架状态 0 上架,1 下架",example = "1") + private Integer state; + + @ApiModelProperty(value = "店铺id",example = "8") + private Long shopId; + /** + * 商品名称 + */ + @ApiModelProperty(value = "商品名称",example = "Nexa-M2-Miner") + private String name; + + /** + * 商品图片路径 + */ + @ApiModelProperty(value = "商品图片路径",example = "这是图片路径") + private String image; + + /** + * 商品描述 + */ + @ApiModelProperty(value = "商品描述",example = "这是商品描述") + private String description; + + /** + * 矿机挖矿币种 nexa rxd dgbo dgbq dgbs alph enx grs mona + */ + @ApiModelProperty(value = "矿机挖矿币种 nexa rxd dgbo dgbq dgbs alph enx grs mona",example = "nexa") + private String coin; + + + @ApiModelProperty(value = "商品类型 0 矿机,1 算力",example = "0") + private Integer type; + + +} diff --git a/src/main/java/com/m2pool/lease/vo/ProductUpdateMachineVo.java b/src/main/java/com/m2pool/lease/vo/ProductUpdateMachineVo.java new file mode 100644 index 0000000..8f53aa7 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/ProductUpdateMachineVo.java @@ -0,0 +1,81 @@ +package com.m2pool.lease.vo; + +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; + +/** + *

+ * 矿机修改接口请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "矿机修改接口请求对象",value = "ProductUpdateMachineVo" ) +public class ProductUpdateMachineVo { + + @ApiModelProperty(value = "矿机id") + private Long id; + + @ApiModelProperty(value = "矿机型号",example = "龍珠") + private String type; + + @ApiModelProperty(value = "商品机器单机理论算力(卖方手动填写)",example = "0.01") + private BigDecimal theoryPower; + + @ApiModelProperty(value = "商品机器单机算力单位",example = "TH/S") + private String unit; + + @ApiModelProperty(value = "功耗",example = "0.01") + private BigDecimal powerDissipation; + + //@ApiModelProperty(value = "电费",example = "0.01") + //private BigDecimal electricityBill; + + @ApiModelProperty(value = "收益率",example = "0.01") + private BigDecimal incomeRate; + + /** + * 挖矿机器 对应的矿工账号 + */ + @ApiModelProperty(value = "挖矿机器 对应的矿工账号",example = "a20") + private String user; + + /** + * 挖矿机器编号 + */ + @ApiModelProperty(value = "挖矿机器编号",example = "100x1") + private String miner; + + /** + * 单价 + */ + @ApiModelProperty(value = "实际价格(同一个商品下的矿机单价默认为商品表的cost字段值= 功耗 * 电费 $/度 * 24h * (1+收益率) 用户可以自定义该价格)",example = "2760.00") + private BigDecimal price; + + @ApiModelProperty(value = "币种价格配置") + private List priceList; + + + /** + * 上下架状态,0 上架,1 下架 + */ + @ApiModelProperty(value = "上下架状态,0 上架,1 下架",example = "1") + private Integer state; + + @ApiModelProperty(value = "最大可租借天数(默认七天)",example = "7") + private Integer maxLeaseDays; + +} diff --git a/src/main/java/com/m2pool/lease/vo/RechargeVo.java b/src/main/java/com/m2pool/lease/vo/RechargeVo.java new file mode 100644 index 0000000..d9a6891 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/RechargeVo.java @@ -0,0 +1,51 @@ +package com.m2pool.lease.vo; + +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; + +/** + *

+ * 余额提现请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "余额充值请求对象",value = "RechargeVo") +public class RechargeVo { + + + /** + * 提现金额 + */ + @ApiModelProperty(value = "提现/充值 金额",required = true) + private BigDecimal amount; + + /** + * 链 + */ + @ApiModelProperty(value = "链",required = true) + private String chain; + + /** + * 币种 + */ + @ApiModelProperty(value = "币种",required = true) + private String symbol; + + /** + * 提现地址(用户自定义) + */ + @ApiModelProperty(value = "提现地址(用户自定义)",required = true) + private String address; +} diff --git a/src/main/java/com/m2pool/lease/vo/RecordTypePageVo.java b/src/main/java/com/m2pool/lease/vo/RecordTypePageVo.java new file mode 100644 index 0000000..904dae6 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/RecordTypePageVo.java @@ -0,0 +1,27 @@ +package com.m2pool.lease.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + *

+ * 交易流水请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "交易流水请求对象",value = "RecordTypePageVo") +public class RecordTypePageVo extends PageVo{ + + @ApiModelProperty(value = "0 支付 1 提现 2 充值") + private Integer status; +} diff --git a/src/main/java/com/m2pool/lease/vo/RestPwdVo.java b/src/main/java/com/m2pool/lease/vo/RestPwdVo.java new file mode 100644 index 0000000..5f04ff0 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/RestPwdVo.java @@ -0,0 +1,38 @@ +package com.m2pool.lease.vo; + +import com.m2pool.lease.annotation.EncryptedField; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + *

+ * 用户请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "重置密码请求",value = "RestPwdVo") +public class RestPwdVo { + + @ApiModelProperty(value = "用户eamil",example = "Eudora.law@outlook.com") + private String email; + + @ApiModelProperty(value = "密码",example = "123456") + @EncryptedField + private String password; + + @ApiModelProperty(value = "邮箱验证码",example = "123456") + private String code; + + @ApiModelProperty(value = "谷歌验证码",example = "123456") + private Long gCode; +} diff --git a/src/main/java/com/m2pool/lease/vo/ShopConfigVo.java b/src/main/java/com/m2pool/lease/vo/ShopConfigVo.java new file mode 100644 index 0000000..c1fe46e --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/ShopConfigVo.java @@ -0,0 +1,53 @@ +package com.m2pool.lease.vo; + +import com.m2pool.lease.annotation.EncryptedField; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + *

+ * 店铺商品配置请求对象 + *

+ * + * @author yyb + * @since 2025-08-05 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "店铺商品配置请求对象",value = "ShopConfigVo") +public class ShopConfigVo extends BaseVo { + + + + /** + * 链名称 + */ + @ApiModelProperty(value = "链名称",example = "tron") + private String chain; + + /** + * nexa rxd dgbo dgbq dgbs alph enx grs mona usdt + */ + @ApiModelProperty(value = "支付币种(多个以逗号隔开)",example = "nexa") + private String payCoin; + + /** + * 币种类型 0 虚拟币 1 稳定币 + */ + @ApiModelProperty(value = "币种类型 0 虚拟币 1 稳定币",example = "0") + private Integer payType; + + /** + * 卖方对应收款钱包 + */ + @ApiModelProperty(value = "卖方对应收款钱包",example = "nexa:nqtsq5g50jkkmklvjyaflg46c4nwuy46z9gzswqe3l0csc7g") + + private String payAddress; + +} diff --git a/src/main/java/com/m2pool/lease/vo/ShopPayTypeVo.java b/src/main/java/com/m2pool/lease/vo/ShopPayTypeVo.java new file mode 100644 index 0000000..0698fcd --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/ShopPayTypeVo.java @@ -0,0 +1,25 @@ +package com.m2pool.lease.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + *

+ * 商品返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "店铺支付方式请求对象",value = "ShopPayTypeVo" ) +public class ShopPayTypeVo { + @ApiModelProperty(value = "店铺id",example = "8") + private Long shopId; + +} diff --git a/src/main/java/com/m2pool/lease/vo/ShopVo.java b/src/main/java/com/m2pool/lease/vo/ShopVo.java new file mode 100644 index 0000000..26a21db --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/ShopVo.java @@ -0,0 +1,61 @@ +package com.m2pool.lease.vo; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiOperation; +import lombok.*; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + *

+ * 店铺表 + *

+ * + * @author yyb + * @since 2025-08-05 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "店铺请求对象",value = "ShopVo") +public class ShopVo extends BaseVo{ + + /** + * 店铺名称 + */ + @ApiModelProperty(value = "店铺名称",example = "nexa算力租赁专卖店") + private String name; + + /** + * 店铺图片路径 + */ + @ApiModelProperty(value = "店铺图片路径") + private String image; + + /** + * 店铺描述 + */ + @ApiModelProperty(value = "店铺描述",example = "欢迎来到nexa算力租赁专卖店") + private String description; + + /** + * 商铺状态 0 待审核 1 审核通过(店铺开启) 2 店铺关闭 + */ + @ApiModelProperty(value = "商铺状态 0 待审核 1 审核通过(店铺开启) 2 店铺关闭",example = "1") + private Integer state; + + @ApiModelProperty(value = "店铺手续费比例 范围0.01-0.1",example = "0.01") + private BigDecimal feeRate; + /** + * 逻辑删除字段 + */ + //@ApiModelProperty(value = "逻辑删除字段",example = "false") + //private Boolean del; + +} diff --git a/src/main/java/com/m2pool/lease/vo/ShoppingCartInfoURDVo.java b/src/main/java/com/m2pool/lease/vo/ShoppingCartInfoURDVo.java new file mode 100644 index 0000000..c7cfd67 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/ShoppingCartInfoURDVo.java @@ -0,0 +1,45 @@ +package com.m2pool.lease.vo; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + *

+ * 购物车详情请求对象 + *

+ * + * @author yyb + * @since 2025-07-24 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "购物车详情请求对象",value = "ShoppingCartInfoDto") +public class ShoppingCartInfoURDVo extends BaseVo{ + + + /** + * 商品 ID + */ + @ApiModelProperty(value = "商品 ID", example = "2") + private Long productId; + + /** + * 商品机器ID + */ + @ApiModelProperty(value = "商品机器ID", example = "1") + private Long productMachineId; + + /** + * 租赁天数 + */ + @ApiModelProperty(value = "租赁天数", example = "1") + private Integer leaseTime; + +} diff --git a/src/main/java/com/m2pool/lease/vo/UserLoginVo.java b/src/main/java/com/m2pool/lease/vo/UserLoginVo.java new file mode 100644 index 0000000..f0ebdf3 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/UserLoginVo.java @@ -0,0 +1,35 @@ +package com.m2pool.lease.vo; + +import com.m2pool.lease.annotation.EncryptedField; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + *

+ * 用户请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "用户登录请求对象",value = "UserLoginVo") +public class UserLoginVo { + + @ApiModelProperty(value = "用户eamil",example = "Eudora.law@outlook.com") + private String email; + + @ApiModelProperty(value = "密码",example = "123456") + @EncryptedField + private String password; + + @ApiModelProperty(value = "验证码",example = "123456") + private String code; +} diff --git a/src/main/java/com/m2pool/lease/vo/UserMinerVo.java b/src/main/java/com/m2pool/lease/vo/UserMinerVo.java new file mode 100644 index 0000000..d4854f1 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/UserMinerVo.java @@ -0,0 +1,40 @@ +package com.m2pool.lease.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + *

+ * 用户挖矿账户矿工请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "用户挖矿账户矿工请求对象",value = "UserMinerVo" ) +public class UserMinerVo { + /** + * 挖矿机器 对应的矿工账号 + */ + @ApiModelProperty(value = "挖矿机器 对应的矿工账号",example = "a20") + private String user; + + /** + * 挖矿机器编号 + */ + //@ApiModelProperty(value = "挖矿机器编号") + //private String miner; + + + @ApiModelProperty(value = "机器挖矿币种(默认nexa)",example = "nexa",required = true) + private String coin = "nexa"; + +} diff --git a/src/main/java/com/m2pool/lease/vo/UserOwnedProductVo.java b/src/main/java/com/m2pool/lease/vo/UserOwnedProductVo.java new file mode 100644 index 0000000..aa1ea49 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/UserOwnedProductVo.java @@ -0,0 +1,30 @@ +package com.m2pool.lease.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + *

+ * 用户拥有商品基础请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "用户拥有商品基础请求对象",value = "UserOwnedBaseVo" ) +public class UserOwnedProductVo extends PageVo{ + /** + * ID + */ + @ApiModelProperty(value = "ID") + private Long id; + +} diff --git a/src/main/java/com/m2pool/lease/vo/UserRegisterVo.java b/src/main/java/com/m2pool/lease/vo/UserRegisterVo.java new file mode 100644 index 0000000..c8e190c --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/UserRegisterVo.java @@ -0,0 +1,35 @@ +package com.m2pool.lease.vo; + +import com.m2pool.lease.annotation.EncryptedField; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + *

+ * 用户请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "用户注册请求对象",value = "UserRegisterVo") +public class UserRegisterVo { + + @ApiModelProperty(value = "用户eamil",example = "Eudora.law@outlook.com") + private String userEmail; + + @ApiModelProperty(value = "密码",example = "123456") + @EncryptedField + private String password; + + @ApiModelProperty(value = "验证码",example = "123456") + private String code; +} diff --git a/src/main/java/com/m2pool/lease/vo/UserVo.java b/src/main/java/com/m2pool/lease/vo/UserVo.java new file mode 100644 index 0000000..5f37bc3 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/UserVo.java @@ -0,0 +1,24 @@ +package com.m2pool.lease.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + *

+ * 用户基础请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "用户基础请求对象",value = "UserDto") +public class UserVo { + @ApiModelProperty(value = "id") + private Long id; +} diff --git a/src/main/java/com/m2pool/lease/vo/v2/AddGoodsVo.java b/src/main/java/com/m2pool/lease/vo/v2/AddGoodsVo.java new file mode 100644 index 0000000..747edca --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/v2/AddGoodsVo.java @@ -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; + +/** + *

+ * 购物车详情请求对象 + *

+ * + * @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; + +} diff --git a/src/main/java/com/m2pool/lease/vo/v2/AsicCoinAndAlgoVo.java b/src/main/java/com/m2pool/lease/vo/v2/AsicCoinAndAlgoVo.java new file mode 100644 index 0000000..41c7a4f --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/v2/AsicCoinAndAlgoVo.java @@ -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; + +/** + *

+ * 机器参数请求对象 + *

+ * + * @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; +} diff --git a/src/main/java/com/m2pool/lease/vo/v2/AsicMachineParamsVo.java b/src/main/java/com/m2pool/lease/vo/v2/AsicMachineParamsVo.java new file mode 100644 index 0000000..1ec2cf5 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/v2/AsicMachineParamsVo.java @@ -0,0 +1,54 @@ +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; + +/** + *

+ * 机器参数请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "机器参数请求对象",value = "AsicMachineParamsVo" ) +public class AsicMachineParamsVo { + + @ApiModelProperty(value = "矿机id(修改接口)") + private Long id; + + @ApiModelProperty(value = "矿机型号/名称",example = "龍珠") + private String name; + + @ApiModelProperty(value = "最大可租借天数(默认七天)",example = "7") + private Integer maxLeaseDays; + + @ApiModelProperty(value = "可出售数量",example = "1") + private Integer saleNumbers; + + + @ApiModelProperty(value = "功耗",example = "0.01") + private BigDecimal powerDissipation; + + @ApiModelProperty(value = "状态0上架 1下架",example = "上架") + private Boolean state; + + @ApiModelProperty(value = "币种和算力配置") + private List coinAndAlgoList; + + @ApiModelProperty(value = "币种价格配置") + private List priceList; + + +} diff --git a/src/main/java/com/m2pool/lease/vo/v2/CartInfoVo.java b/src/main/java/com/m2pool/lease/vo/v2/CartInfoVo.java new file mode 100644 index 0000000..564a4ff --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/v2/CartInfoVo.java @@ -0,0 +1,23 @@ +package com.m2pool.lease.vo.v2; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Description 购物车商品机器删除请求对象 + * @Date 2025/7/25 11:15 + * @Author yyb + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "购物车商品机器删除请求对象",value = "CartInfoVo" ) +public class CartInfoVo { + + @ApiModelProperty(value = "购物车详情id") + private Long cartInfoId; + +} diff --git a/src/main/java/com/m2pool/lease/vo/v2/CloseAccountVo.java b/src/main/java/com/m2pool/lease/vo/v2/CloseAccountVo.java new file mode 100644 index 0000000..808fa91 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/v2/CloseAccountVo.java @@ -0,0 +1,33 @@ +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 javax.validation.constraints.NotBlank; + +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "账户注销请求对象",value = "CloseAccountVo" ) +public class CloseAccountVo { + + + /** + * 谷歌验证器验证码 + */ + @ApiModelProperty(value = "谷歌验证码",required = true) + public long gCode; + + /** + * 邮箱验证码 + */ + @NotBlank + @ApiModelProperty(value = "邮箱验证码",required = true) + public String eCode; + +} diff --git a/src/main/java/com/m2pool/lease/vo/v2/CoinAndAlgorithmVo.java b/src/main/java/com/m2pool/lease/vo/v2/CoinAndAlgorithmVo.java new file mode 100644 index 0000000..eb41e6b --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/v2/CoinAndAlgorithmVo.java @@ -0,0 +1,26 @@ +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.util.List; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@ApiModel(description = "币种算法请求对象",value = "CoinAndAlgorithmVo") +public class CoinAndAlgorithmVo { + + + @ApiModelProperty(value = "币种") + private String coin; + + @ApiModelProperty(value = "算法") + private String algorithm; +} diff --git a/src/main/java/com/m2pool/lease/vo/v2/GoogleBindVo.java b/src/main/java/com/m2pool/lease/vo/v2/GoogleBindVo.java new file mode 100644 index 0000000..24d36da --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/v2/GoogleBindVo.java @@ -0,0 +1,50 @@ +package com.m2pool.lease.vo.v2; + +import com.m2pool.lease.annotation.EncryptedField; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; + +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "绑定谷歌验证器请求对象",value = "GoogleBindVo") +public class GoogleBindVo { + + /** + * 谷歌验证器 安全码 + */ + @NotBlank + @ApiModelProperty(value = "密钥",required = true) + public String secret; + + /** + * 谷歌验证器验证码 + */ + @ApiModelProperty(value = "谷歌验证码",required = true) + public long gCode; + + /** + * 邮箱验证码 + */ + @NotBlank + @ApiModelProperty(value = "邮箱验证码",required = true) + public String eCode; + + /** + * 当前用户密码 + */ + @EncryptedField + @NotBlank + @ApiModelProperty(value = "当前用户密码",required = true) + public String pwd; + + + +} diff --git a/src/main/java/com/m2pool/lease/vo/v2/GoogleCloseVo.java b/src/main/java/com/m2pool/lease/vo/v2/GoogleCloseVo.java new file mode 100644 index 0000000..e335427 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/v2/GoogleCloseVo.java @@ -0,0 +1,34 @@ +package com.m2pool.lease.vo.v2; + +import com.alibaba.nacos.shaded.org.checkerframework.checker.units.qual.A; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; + +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "谷歌验证信息返回对象",value = "GoogleCloseVo" ) +public class GoogleCloseVo { + + + /** + * 谷歌验证器验证码 + */ + @ApiModelProperty(value = "谷歌验证码",required = true) + public long gCode; + + /** + * 邮箱验证码 + */ + @NotBlank + @ApiModelProperty(value = "邮箱验证码",required = true) + public String eCode; + +} diff --git a/src/main/java/com/m2pool/lease/vo/v2/GpuMachineParamsVo.java b/src/main/java/com/m2pool/lease/vo/v2/GpuMachineParamsVo.java new file mode 100644 index 0000000..b9dc309 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/v2/GpuMachineParamsVo.java @@ -0,0 +1,39 @@ +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; + +/** + *

+ * 机器参数请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "机器参数请求对象",value = "GpuMachineParamsVo" ) +public class GpuMachineParamsVo { + + @ApiModelProperty(value = "矿机id(修改接口)") + private Long id; + + @ApiModelProperty(value = "最大可租借天数(默认七天)",example = "7") + private Integer maxLeaseDays; + + @ApiModelProperty(value = "0 上架 1 下架",example = "1") + private Boolean state; + + @ApiModelProperty(value = "币种价格配置") + private List priceList; +} diff --git a/src/main/java/com/m2pool/lease/vo/v2/MachinePayTypeVo.java b/src/main/java/com/m2pool/lease/vo/v2/MachinePayTypeVo.java new file mode 100644 index 0000000..ce5a26a --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/v2/MachinePayTypeVo.java @@ -0,0 +1,40 @@ +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; + +/** + *

+ * 矿机售价配置返回对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "矿机售价配置请求对象",value = "MachinePayTypeVo" ) +public class MachinePayTypeVo { + @ApiModelProperty(value = "矿机币种售价配置id") + private Long payTypeId; + + @ApiModelProperty(value = "矿机id(忽略)") + private Long productMachineId; + + @ApiModelProperty(value = "售价") + private BigDecimal price; + + @ApiModelProperty(value = "链") + private String chain; + + @ApiModelProperty(value = "售价单位币种") + private String coin; +} diff --git a/src/main/java/com/m2pool/lease/vo/v2/MachineStateVo.java b/src/main/java/com/m2pool/lease/vo/v2/MachineStateVo.java new file mode 100644 index 0000000..9297711 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/v2/MachineStateVo.java @@ -0,0 +1,34 @@ +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; + +/** + *

+ * 机器参数请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "矿机上下架状态请求对象",value = "MachineStateVo" ) +public class MachineStateVo { + + @ApiModelProperty(value = "矿机id(修改接口)") + private Long id; + + @ApiModelProperty(value = "上下架状态: 0 上架,1 下架") + private Boolean state; + +} diff --git a/src/main/java/com/m2pool/lease/vo/v2/SellerMachineVo.java b/src/main/java/com/m2pool/lease/vo/v2/SellerMachineVo.java new file mode 100644 index 0000000..79a8f5c --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/v2/SellerMachineVo.java @@ -0,0 +1,34 @@ +package com.m2pool.lease.vo.v2; + +import com.m2pool.lease.vo.PageVo; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +/** + *

+ * 首页商品详情页矿机列表请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "卖家详情页矿机列表请求对象",value = "SellerMachineVo" ) +public class SellerMachineVo extends PageVo { + @ApiModelProperty(value = "型号(gpu名称 或asic 名称)",example = "4090") + private String name; + + @ApiModelProperty(value = "搜索关键词",example = "4090") + private String keyword; + + @ApiModelProperty(value = "类型 0:ASIC 1:GPU") + private Boolean type; + +} diff --git a/src/main/java/com/m2pool/lease/vo/v2/ShopMachineVo.java b/src/main/java/com/m2pool/lease/vo/v2/ShopMachineVo.java new file mode 100644 index 0000000..2f4f66e --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/v2/ShopMachineVo.java @@ -0,0 +1,73 @@ +package com.m2pool.lease.vo.v2; + +import com.m2pool.lease.vo.PageVo; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +/** + *

+ * 首页商品详情页矿机列表请求对象 + *

+ * + * @author yyb + * @since 2025-07-23 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "首页店铺详情页矿机列表请求对象",value = "ShopMachineVo" ) +public class ShopMachineVo extends PageVo { + + @ApiModelProperty(value = "店铺id") + private Long shopId; + + //@ApiModelProperty(value = "最大算力") + //private BigDecimal maxPower = BigDecimal.ZERO; + // + //@ApiModelProperty(value = "最小算力") + //private BigDecimal minPower = BigDecimal.ZERO; + // + //@ApiModelProperty(value = "最大功耗 单位kw/h",example = "10") + //private BigDecimal maxPowerDissipation = BigDecimal.ZERO; + // + //@ApiModelProperty(value = "最小功耗 单位kw/h",example = "10") + //private BigDecimal minPowerDissipation = BigDecimal.ZERO; + + + @ApiModelProperty(value = "最大单价") + private BigDecimal maxPrice = BigDecimal.ZERO; + + @ApiModelProperty(value = "最小单价") + private BigDecimal minPrice = BigDecimal.ZERO; + + @ApiModelProperty(value = "币种") + private String coin; + + @ApiModelProperty(value = "链") + private String chain; + + @ApiModelProperty(value = "价格排序方式 true:降序 false:升序") + private Boolean priceSort; + + @ApiModelProperty(value = "实际算力排序方式 true:降序 false:升序") + private Boolean powerSort; + + @ApiModelProperty(value = "功耗排序方式 true:降序 false:升序") + private Boolean powerDissipationSort; + + @ApiModelProperty(value = "币种单位",example = "MH/S") + private String unit; + + @ApiModelProperty(value = "类型 0:ASIC 1:GPU") + private Boolean type; + + @ApiModelProperty(value = "币种算法关键字") + private String keyword; + + +} diff --git a/src/main/java/com/m2pool/lease/vo/v2/ShopWalletInfoVo.java b/src/main/java/com/m2pool/lease/vo/v2/ShopWalletInfoVo.java new file mode 100644 index 0000000..ab9d29c --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/v2/ShopWalletInfoVo.java @@ -0,0 +1,41 @@ +package com.m2pool.lease.vo.v2; + +import com.m2pool.lease.annotation.EncryptedField; +import com.m2pool.lease.vo.BaseVo; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + *

+ * 店铺商品配置请求对象 + *

+ * + * @author yyb + * @since 2025-08-05 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(description = "店铺钱包配置请求对象",value = "ShopWalletInfoVo") +public class ShopWalletInfoVo { + + + @ApiModelProperty(value = "id",example = "1") + private Long id; + + @ApiModelProperty(value = "链名称",example = "tron") + private String chain; + + @ApiModelProperty(value = "卖方对应收款钱包",example = "nexa:nqtsq5g50jkkmklvjyaflg46c4nwuy46z9gzswqe3l0csc7g") + @EncryptedField + private String payAddress; + + @ApiModelProperty(value = "谷歌验证码",example = "123456") + private Long gCode; + +} diff --git a/src/main/java/com/m2pool/lease/vo/v2/UserGoogleAuthInfo.java b/src/main/java/com/m2pool/lease/vo/v2/UserGoogleAuthInfo.java new file mode 100644 index 0000000..70e58f7 --- /dev/null +++ b/src/main/java/com/m2pool/lease/vo/v2/UserGoogleAuthInfo.java @@ -0,0 +1,23 @@ +package com.m2pool.lease.vo.v2; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class UserGoogleAuthInfo implements Serializable { + + /** 邮箱 */ + private String email; + + /** 谷歌验证码 */ + private String secret; + + private String pwd; + + /** 谷歌验证码 */ + private int status; + + + +} diff --git a/src/main/resources/bootstrap-dev.yml b/src/main/resources/bootstrap-dev.yml new file mode 100644 index 0000000..3d912e0 --- /dev/null +++ b/src/main/resources/bootstrap-dev.yml @@ -0,0 +1,62 @@ +#开发环境 +server: + port: 6666 + compression: + enabled: true + mime-types: application/json +spring: + #邮箱基本配置 + mail: + # 配置在limit_time内,用户可以发送limit次验证码 + limit: 2 这个是我额外的配置,结合邮箱服务用的 + limitTime: 10 这个是我额外的配置 + #配置smtp服务主机地址 + # sina smtp.sina.cn + # aliyun smtp.aliyun.com + # 163 smtp.163.com 端口号465或994 + host: mail.privateemail.com + #发送者邮箱 + username: do.not.reply@m2pool.com + #配置密码,注意不是真正的密码,而是刚刚申请到的授权码 + # password: + # password: M2202401! + # password: axvm-zfgx-cgcg-qhhu + password: M2202401! + mvc: + pathmatch: + matching-strategy: ant_path_matcher + application: + # 应用名称 + name: m2pool-lease + # profiles: + # # 环境配置 + # active: test + cloud: + nacos: + discovery: + # 服务注册地址 + server-addr: 10.168.2.220:8848 + namespace: m2_dev + group: m2_dev_group + # server-addr: 127.0.0.1:8808 + config: + # 配置中心地址 + server-addr: 10.168.2.220:8848 + # server-addr: 127.0.0.1:8808 + # 配置文件格式 + file-extension: yml + # 共享配置 + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + namespace: m2_dev + group: m2_dev_group + servlet: + multipart: + max-file-size: 2MB + max-request-size: 8MB + + +netty: + tcp: + client: + port: 2345 diff --git a/src/main/resources/bootstrap-test.yml b/src/main/resources/bootstrap-test.yml new file mode 100644 index 0000000..26b25aa --- /dev/null +++ b/src/main/resources/bootstrap-test.yml @@ -0,0 +1,104 @@ +#测试环境 +server: + port: 8888 + compression: + enabled: true + mime-types: application/json +spring: + mvc: + pathmatch: + matching-strategy: ant_path_matcher + #邮箱基本配置 + mail: + # 配置在limit_time内,用户可以发送limit次验证码 + limit: 2 这个是我额外的配置,结合邮箱服务用的 + limitTime: 10 这个是我额外的配置 + #配置smtp服务主机地址 + # sina smtp.sina.cn + # aliyun smtp.aliyun.com + # 163 smtp.163.com 端口号465或994 + host: mail.privateemail.com + #发送者邮箱 + username: do.not.reply@m2pool.com + #配置密码,注意不是真正的密码,而是刚刚申请到的授权码 + # password: + # password: M2202401! + # password: axvm-zfgx-cgcg-qhhu + password: M2202401! + + #端口号 + port: 587 + # port: 465 + #默认的邮件编码为UTF-8 + default-encoding: UTF-8 + #其他参数 + properties: + mail: + #配置SSL 加密工厂 + smtp: + ssl: + #本地测试,先放开ssl + enable: false + required: false + #开启debug模式,这样邮件发送过程的日志会在控制台打印出来,方便排查错误 + debug: false + socketFactory: + class: javax.net.ssl.SSLSocketFactory + + application: + # 应用名称 + name: m2pool-lease + # profiles: + # # 环境配置 + # active: test + cloud: + nacos: + discovery: + # 服务注册地址 + server-addr: 127.0.0.1:8808 + namespace: m2_test + group: m2_test_group + # server-addr: 127.0.0.1:8808 + config: + # 配置中心地址 + server-addr: 127.0.0.1:8808 + # server-addr: 127.0.0.1:8808 + # 配置文件格式 + file-extension: yml + # 共享配置 + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + namespace: m2_test + group: m2_test_group + servlet: + multipart: + max-file-size: 2MB + max-request-size: 8MB +file: + domain: https://test.m2pool.com + path: /var/www/html/web_test + # img: /img + filepath: /home/ubuntu/web + prefix: /statics + +client: + # 客户端安装包 + download: + client: + windows: /home/ubuntu/web/client/windows + linux: /home/ubuntu/web/client/linux + # 客户端版本号文件名 + version: version + +netty: + tcp: + client: + port: 2345 +image: + prefix: https://test.m2pool.com + +myenv: + domain: https://www.m2pool.com + path: /var/www/html/web + img: /img + filepath: /home/ubuntu/prod diff --git a/src/main/resources/bootstrap.yml b/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..3d7808a --- /dev/null +++ b/src/main/resources/bootstrap.yml @@ -0,0 +1,3 @@ +spring: + profiles: + active: dev diff --git a/src/main/resources/mapper/lease/LeaseAutoAddressMapper.xml b/src/main/resources/mapper/lease/LeaseAutoAddressMapper.xml new file mode 100644 index 0000000..0f90ceb --- /dev/null +++ b/src/main/resources/mapper/lease/LeaseAutoAddressMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + id, address, status,from_symbol,from_chain + + + + + + diff --git a/src/main/resources/mapper/lease/LeaseGpuConfigMapper.xml b/src/main/resources/mapper/lease/LeaseGpuConfigMapper.xml new file mode 100644 index 0000000..f83aefb --- /dev/null +++ b/src/main/resources/mapper/lease/LeaseGpuConfigMapper.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + id, brand, name, status, memory, power_dissipation, del + + + insert into lease_gpu_config ( name, algorithm,coin) + values + + (#{item.model}, #{item.algorithm},'') + + ON DUPLICATE KEY UPDATE + name = VALUES(`name`), + `algorithm` = VALUES(`algorithm`), + coin = VALUES(coin) + + + insert into lease_gpu_config ( name, algorithm, power_dissipation,hashrate,month_income) + values + + (#{item.name}, #{item.algorithm}, #{item.powerDissipation},#{item.hashrate},#{item.monthIncome}) + + ON DUPLICATE KEY UPDATE + name = VALUES(`name`), + `algorithm` = VALUES(`algorithm`), + power_dissipation = VALUES(power_dissipation), + hashrate = VALUES(hashrate), + month_income = VALUES(month_income) + + + + diff --git a/src/main/resources/mapper/lease/LeaseMachineConfigMapper.xml b/src/main/resources/mapper/lease/LeaseMachineConfigMapper.xml new file mode 100644 index 0000000..546c1bc --- /dev/null +++ b/src/main/resources/mapper/lease/LeaseMachineConfigMapper.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + id, machine_id, brand, name, status, coin, hashrate, algorithm, icon, unit, month_income, power_dissipation, del + + + + + + diff --git a/src/main/resources/mapper/lease/LeaseMachineMapper.xml b/src/main/resources/mapper/lease/LeaseMachineMapper.xml new file mode 100644 index 0000000..6804b01 --- /dev/null +++ b/src/main/resources/mapper/lease/LeaseMachineMapper.xml @@ -0,0 +1,471 @@ + + + + + + + + + + + + + + + + + + + + + + + + id, shop_id, type, single_power, total_power, unit, max_lease_days, price, power_dissipation, sale_state, state, del, create_time, update_time, coin, algorithm, gpu_config_id, user, miner, sale_numbers, sale_out_numbers + + + + + + + + + + + + + + + + + + + + + + + + UPDATE lease_machine + SET + sale_state = 1, + can_sale_numbers = 0, + sale_out_numbers = 1 + WHERE + id IN + + #{item.id} + + and sale_state = 0 + + + + UPDATE lease_machine + SET + sale_out_numbers = #{item.saleOutNumbers}, + sale_state = CASE WHEN #{item.saleOutNumbers} = sale_numbers THEN 1 ELSE sale_state END, + can_sale_numbers = #{item.canSaleNumbers} + WHERE + id = #{item.id} + AND sale_out_numbers = #{item.lockNumbers} + + + + + + + + + + + + + + + UPDATE lease_machine + SET + sale_out_numbers = sale_out_numbers - #{item.saleOutNumbers}, + can_sale_numbers = can_sale_numbers + #{item.canSaleNumbers} + WHERE + id = #{item.id} + + + + + + diff --git a/src/main/resources/mapper/lease/LeaseMachinePriceMapper.xml b/src/main/resources/mapper/lease/LeaseMachinePriceMapper.xml new file mode 100644 index 0000000..86cfca1 --- /dev/null +++ b/src/main/resources/mapper/lease/LeaseMachinePriceMapper.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + id, machine_id, price, coin, chain, del + + + + + diff --git a/src/main/resources/mapper/lease/LeaseMachineTempConfigMapper.xml b/src/main/resources/mapper/lease/LeaseMachineTempConfigMapper.xml new file mode 100644 index 0000000..8b6abf6 --- /dev/null +++ b/src/main/resources/mapper/lease/LeaseMachineTempConfigMapper.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + id, machine_id, brand, name, memory, status, coin, hashrate, algorithm, icon, unit, month_income, power_dissipation, del, effect_time + + + INSERT INTO lease_machine_temp_config ( + machine_id, + brand, + `name`, + memory, + status, + coin, + hashrate, + algorithm, + icon, + unit, + month_income, + power_dissipation, + effect_time + ) + VALUES + + (#{item.machineId}, + #{item.brand}, + #{item.name}, + #{item.memory}, + #{item.status}, + #{item.coin}, + #{item.hashrate}, + #{item.algorithm}, + #{item.icon}, + #{item.unit}, + #{item.monthIncome}, + #{item.powerDissipation}, + #{item.effectTime}) + + + + INSERT INTO lease_machine_config ( + machine_id, + brand, + `name`, + memory, + status, + coin, + hashrate, + algorithm, + icon, + unit, + month_income, + power_dissipation + ) + VALUES + + (#{item.machineId}, + #{item.brand}, + #{item.name}, + #{item.memory}, + #{item.status}, + #{item.coin}, + #{item.hashrate}, + #{item.algorithm}, + #{item.icon}, + #{item.unit}, + #{item.monthIncome}, + #{item.powerDissipation}) + + + + diff --git a/src/main/resources/mapper/lease/LeaseMiningSoftwareConfigMapper.xml b/src/main/resources/mapper/lease/LeaseMiningSoftwareConfigMapper.xml new file mode 100644 index 0000000..1d52bd2 --- /dev/null +++ b/src/main/resources/mapper/lease/LeaseMiningSoftwareConfigMapper.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + id, name, coin, algorithm, icon, del + + + + diff --git a/src/main/resources/mapper/lease/LeaseNexaMachineAvgPowerMapper.xml b/src/main/resources/mapper/lease/LeaseNexaMachineAvgPowerMapper.xml new file mode 100644 index 0000000..11964e0 --- /dev/null +++ b/src/main/resources/mapper/lease/LeaseNexaMachineAvgPowerMapper.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + id, user, miner, start_time, end_time, real_avg_power + + + insert into lease_nexa_machine_avg_power (user, miner, start_time, end_time, real_avg_power) values + + (#{item.user}, #{item.miner}, #{item.startTime}, #{item.endTime}, #{item.realAvgPower}) + + ON DUPLICATE KEY UPDATE + `start_time` = VALUES(`start_time`), + `end_time` = VALUES(`end_time`), + `real_avg_power` = VALUES(`real_avg_power`) + + + + diff --git a/src/main/resources/mapper/lease/LeaseNexaMachinePowerMapper.xml b/src/main/resources/mapper/lease/LeaseNexaMachinePowerMapper.xml new file mode 100644 index 0000000..8b60413 --- /dev/null +++ b/src/main/resources/mapper/lease/LeaseNexaMachinePowerMapper.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + id, `user`, miner, `date`, accepts, `state`, last_submit + + + + + + diff --git a/src/main/resources/mapper/lease/LeaseOrderFeeMapper.xml b/src/main/resources/mapper/lease/LeaseOrderFeeMapper.xml new file mode 100644 index 0000000..7f53aa6 --- /dev/null +++ b/src/main/resources/mapper/lease/LeaseOrderFeeMapper.xml @@ -0,0 +1,4 @@ + + + + diff --git a/src/main/resources/mapper/lease/LeaseOrderInfoMapper.xml b/src/main/resources/mapper/lease/LeaseOrderInfoMapper.xml new file mode 100644 index 0000000..e2c4b30 --- /dev/null +++ b/src/main/resources/mapper/lease/LeaseOrderInfoMapper.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + id, order_number, user_id, total_price, status, address, create_time, update_time, del + + + + + + + + + diff --git a/src/main/resources/mapper/lease/LeaseOrderItemMapper.xml b/src/main/resources/mapper/lease/LeaseOrderItemMapper.xml new file mode 100644 index 0000000..35c2b81 --- /dev/null +++ b/src/main/resources/mapper/lease/LeaseOrderItemMapper.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + id, order_id, product_id,product_machine_id,`name`, unit, image, quantity, price, create_time, update_time, del + + + + + + + + + + + diff --git a/src/main/resources/mapper/lease/LeaseOrderMiningMapper.xml b/src/main/resources/mapper/lease/LeaseOrderMiningMapper.xml new file mode 100644 index 0000000..084f0c6 --- /dev/null +++ b/src/main/resources/mapper/lease/LeaseOrderMiningMapper.xml @@ -0,0 +1,189 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + id,user_id, order_id, coin, algorithm, pool, pool_url, pool_user, worker_id, wallet_address, watch_url, status, start_time, end_time, del + + + + INSERT INTO lease_order_mining ( + order_id, + machine_id, + order_item_id, + user_id, + auth_id, + coin, + algorithm, + pool, + pool_url, + pool_user, + worker_id, + wallet_address, + start_time, + end_time + ) + VALUES + + ( + #{item.orderId}, + #{item.machineId}, + #{item.orderItemId}, + #{item.userId}, + #{item.authId}, + #{item.coin}, + #{item.algorithm}, + #{item.pool}, + #{item.poolUrl}, + #{item.poolUser}, + #{item.workerId}, + #{item.walletAddress}, + #{item.startTime}, + #{item.endTime}) + + + + UPDATE lease_order_mining + SET watch_url = #{clientConfigurationMining.watchUrl} + WHERE user_id = #{userEmail} AND pool = #{clientConfigurationMining.pool} + + + + + + + + + + + + diff --git a/src/main/resources/mapper/lease/LeasePayRechargeMessageMapper.xml b/src/main/resources/mapper/lease/LeasePayRechargeMessageMapper.xml new file mode 100644 index 0000000..1e41bca --- /dev/null +++ b/src/main/resources/mapper/lease/LeasePayRechargeMessageMapper.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + id, address, amount, symbol,`chain`, create_time, update_time, status, del + + + + + + + + + + insert into lease_pay_recharge_message (queue_id,address, amount, chain, symbol, status,tx_hash) + values (#{leasePayRechargeMessage.queueId},#{leasePayRechargeMessage.address}, + #{leasePayRechargeMessage.amount}, #{leasePayRechargeMessage.chain}, + #{leasePayRechargeMessage.symbol},#{leasePayRechargeMessage.status},#{leasePayRechargeMessage.txHash}) + ON DUPLICATE KEY UPDATE + `queue_id` = VALUES(`queue_id`), + `address` = VALUES(`address`), + `amount` = VALUES(`amount`), + `chain` = VALUES(`chain`), + `symbol` = VALUES(`symbol`), + `status` = VALUES(`status`), + `tx_hash` = VALUES(`tx_hash`) + + diff --git a/src/main/resources/mapper/lease/LeasePayRecordMessageInfoMapper.xml b/src/main/resources/mapper/lease/LeasePayRecordMessageInfoMapper.xml new file mode 100644 index 0000000..86f81c1 --- /dev/null +++ b/src/main/resources/mapper/lease/LeasePayRecordMessageInfoMapper.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + id, queue_id, from_address, to_address, amount, `to_chain`, to_symbol,from_chain,from_symbol, order_id,create_time, status, del + + diff --git a/src/main/resources/mapper/lease/LeasePayRecordMessageMapper.xml b/src/main/resources/mapper/lease/LeasePayRecordMessageMapper.xml new file mode 100644 index 0000000..08ef924 --- /dev/null +++ b/src/main/resources/mapper/lease/LeasePayRecordMessageMapper.xml @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + id, queue_id, from_address, to_address, amount, `to_chain`, to_symbol,from_chain,from_symbol, order_id,create_time, status, del + + + + + + + + + + + update lease_pay_record_message + set + status = #{leasePayRecordMessage.status}, + update_time = #{leasePayRecordMessage.updateTime}, + tx_hash = #{leasePayRecordMessage.txHash}, + block_height = #{leasePayRecordMessage.blockHeight}, + real_amount = #{leasePayRecordMessage.realAmount} + where id = #{leasePayRecordMessage.id} and (tx_hash = "" or tx_hash = #{txHash}) + + + diff --git a/src/main/resources/mapper/lease/LeasePayWithdrawMessageMapper.xml b/src/main/resources/mapper/lease/LeasePayWithdrawMessageMapper.xml new file mode 100644 index 0000000..7abd0a1 --- /dev/null +++ b/src/main/resources/mapper/lease/LeasePayWithdrawMessageMapper.xml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + id, queue_id, from_address, to_address, amount, from_chain, from_symbol,to_chain,to_symbol, create_time, update_time, status, del + + + + + + + + + + diff --git a/src/main/resources/mapper/lease/LeasePaymentRecordMapper.xml b/src/main/resources/mapper/lease/LeasePaymentRecordMapper.xml new file mode 100644 index 0000000..120be10 --- /dev/null +++ b/src/main/resources/mapper/lease/LeasePaymentRecordMapper.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + id, order_id, payment_method, amount, status, create_time, update_time, del + + + update lease_payment_record set status = 5 + where + status = 6 AND + ( + (`order_id` = #{item.orderId} and `shop_id` = #{item.shopId}) + ) + + + + + + diff --git a/src/main/resources/mapper/lease/LeaseProductIncomeMapper.xml b/src/main/resources/mapper/lease/LeaseProductIncomeMapper.xml new file mode 100644 index 0000000..cd0b89a --- /dev/null +++ b/src/main/resources/mapper/lease/LeaseProductIncomeMapper.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + id, user_owned_product_id, average_income, estimated_end_income, current_actual_income, create_time, update_time + + + diff --git a/src/main/resources/mapper/lease/LeaseProductMachineMapper.xml b/src/main/resources/mapper/lease/LeaseProductMachineMapper.xml new file mode 100644 index 0000000..9e5a209 --- /dev/null +++ b/src/main/resources/mapper/lease/LeaseProductMachineMapper.xml @@ -0,0 +1,303 @@ + + + + + + + + + + + + + + + + + + + + + + id, product_id, user, miner, price, computing_power, state, del, create_time, update_time + + + + UPDATE lease_product_machine + SET + sale_state = 1 + WHERE + id IN + + #{id} + + and sale_state = 0 + + + + + + + + + + + + INSERT INTO lease_product_machine ( + shop_id, product_id, user, type, coin, miner, theory_power, + computing_power, theory_income, unit, + power_dissipation, income_rate, cost, price, sale_state, + state, del,max_lease_days + ) + VALUES + + ( + #{item.shopId}, #{item.productId}, #{item.user}, #{item.type}, #{item.coin}, #{item.miner}, + #{item.theoryPower}, #{item.computingPower}, #{item.theoryIncome}, #{item.unit}, #{item.powerDissipation}, #{item.incomeRate}, #{item.cost}, + #{item.price}, #{item.saleState}, #{item.state}, #{item.del}, #{item.maxLeaseDays} + ) + + ON DUPLICATE KEY UPDATE + shop_id = VALUES(shop_id), + product_id = VALUES(product_id), + `type` = VALUES(`type`), + theory_power = VALUES(theory_power), + computing_power = VALUES(computing_power), + theory_income = VALUES(theory_income), + unit = VALUES(unit), + power_dissipation = VALUES(power_dissipation), + income_rate = VALUES(income_rate), + cost = VALUES(cost), + price = VALUES(price), + sale_state = VALUES(sale_state), + `state` = VALUES(`state`), + del = VALUES(del), + max_lease_days = VALUES(max_lease_days) + + + INSERT INTO ${coin}_real_power (user,miner,power,`date`) + VALUES + + ( + #{item.user}, + #{item.miner}, + #{item.computingPower}, + now() + ) + + ON DUPLICATE KEY UPDATE + power = VALUES(power), + `date` = VALUES(`date`) + + + + + + + + + + + diff --git a/src/main/resources/mapper/lease/LeaseProductMachinePriceMapper.xml b/src/main/resources/mapper/lease/LeaseProductMachinePriceMapper.xml new file mode 100644 index 0000000..d9ec5a6 --- /dev/null +++ b/src/main/resources/mapper/lease/LeaseProductMachinePriceMapper.xml @@ -0,0 +1,40 @@ + + + + + + + + + + diff --git a/src/main/resources/mapper/lease/LeaseProductMapper.xml b/src/main/resources/mapper/lease/LeaseProductMapper.xml new file mode 100644 index 0000000..6e2d4e3 --- /dev/null +++ b/src/main/resources/mapper/lease/LeaseProductMapper.xml @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + id, `name`, image,`state`,unit, `type`, description, create_time, update_time, del + + + + + + + + + diff --git a/src/main/resources/mapper/lease/LeaseShopAddressConfigMapper.xml b/src/main/resources/mapper/lease/LeaseShopAddressConfigMapper.xml new file mode 100644 index 0000000..d71e433 --- /dev/null +++ b/src/main/resources/mapper/lease/LeaseShopAddressConfigMapper.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + id, user_id, address, shop_id + + + diff --git a/src/main/resources/mapper/lease/LeaseShopConfigMapper.xml b/src/main/resources/mapper/lease/LeaseShopConfigMapper.xml new file mode 100644 index 0000000..3df827c --- /dev/null +++ b/src/main/resources/mapper/lease/LeaseShopConfigMapper.xml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + id, shop_id, product_id, pay_coin, pay_type, pay_address, create_time, update_time, del + + + + + + + + + + + + UPDATE lease_shop_config + SET balance = balance + #{item.realAmount} + WHERE pay_address = #{item.fromAddress} AND pay_coin = #{item.fromSymbol} AND chain = #{item.fromChain} AND del = false + + + + diff --git a/src/main/resources/mapper/lease/LeaseShopMapper.xml b/src/main/resources/mapper/lease/LeaseShopMapper.xml new file mode 100644 index 0000000..8188d89 --- /dev/null +++ b/src/main/resources/mapper/lease/LeaseShopMapper.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + id, user_email, name, image, description, create_time, update_time, del + + + + + + + + + diff --git a/src/main/resources/mapper/lease/LeaseShoppingCartInfoMapper.xml b/src/main/resources/mapper/lease/LeaseShoppingCartInfoMapper.xml new file mode 100644 index 0000000..87f8f79 --- /dev/null +++ b/src/main/resources/mapper/lease/LeaseShoppingCartInfoMapper.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + id, cart_id, product_id, product_machine_id, create_time, update_time + + + + + + DELETE FROM lease_shopping_cart_info + WHERE id IN + + #{id} + + + + diff --git a/src/main/resources/mapper/lease/LeaseShoppingCartMapper.xml b/src/main/resources/mapper/lease/LeaseShoppingCartMapper.xml new file mode 100644 index 0000000..9dfbcb1 --- /dev/null +++ b/src/main/resources/mapper/lease/LeaseShoppingCartMapper.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + id, user_id, create_time, update_time + + + diff --git a/src/main/resources/mapper/lease/LeaseUserMapper.xml b/src/main/resources/mapper/lease/LeaseUserMapper.xml new file mode 100644 index 0000000..4fc021e --- /dev/null +++ b/src/main/resources/mapper/lease/LeaseUserMapper.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + id, user_id, password, create_time, update_time + + + + + + + + + + update lease_user + set + google_auth = #{info.secret}, + google_status = #{info.status}, + update_time = sysdate() + where user_id = #{info.email} + + + update lease_user + set + google_auth = '', + google_status = 2 + where user_id = #{info.email} + + + + update lease_user + set + google_auth = '', + google_status = 0 + where user_id = #{info.email} + + + diff --git a/src/main/resources/mapper/lease/LeaseUserOwnedProductMapper.xml b/src/main/resources/mapper/lease/LeaseUserOwnedProductMapper.xml new file mode 100644 index 0000000..a1e459b --- /dev/null +++ b/src/main/resources/mapper/lease/LeaseUserOwnedProductMapper.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + id, address, product_machine_id, order_item_id, start_time, end_time, purchased_computing_power, status, type, create_time, update_time, del + + + + + + + INSERT INTO lease_user_owned_product (id, settle_income, settle_usdt_income,start_time, end_time) + VALUES + + #{item.id}, #{item.settleIncome}, #{item.settleUsdtIncome}, #{item.startTime}, #{item.endTime} + + ON DUPLICATE KEY UPDATE + settle_income = VALUES(settle_income), + settle_usdt_income = VALUES(settle_usdt_income), + start_time = VALUES(start_time), + end_time = VALUES(end_time) + + diff --git a/src/main/resources/mapper/lease/LeaseUserWalletDataMapper.xml b/src/main/resources/mapper/lease/LeaseUserWalletDataMapper.xml new file mode 100644 index 0000000..1381ab9 --- /dev/null +++ b/src/main/resources/mapper/lease/LeaseUserWalletDataMapper.xml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + id, user_id, from_address, balance, to_address, from_symbol, from_chain, to_symbol, to_chain, qrcode, create_time, update_time, del + + + + + + + + + + + + + + UPDATE lease_user_wallet_data + SET balance = balance - #{item.realAmount}, + blocked_balance = blocked_balance - #{item.blockAmount} + WHERE from_address = #{item.fromAddress} AND from_symbol = #{item.fromSymbol} AND from_chain = #{item.fromChain} AND del = false + + + + diff --git a/src/main/resources/templates/emailCode-en.html b/src/main/resources/templates/emailCode-en.html new file mode 100644 index 0000000..144b875 --- /dev/null +++ b/src/main/resources/templates/emailCode-en.html @@ -0,0 +1,50 @@ + + + + + + + Email code + + + + +
+ +
+ logo +
+ + + +
+ +

+ +
+ + +
+
+ + + +
+ + + diff --git a/src/main/resources/templates/emailoffline-en.html b/src/main/resources/templates/emailoffline-en.html new file mode 100644 index 0000000..b99f740 --- /dev/null +++ b/src/main/resources/templates/emailoffline-en.html @@ -0,0 +1,37 @@ + + + + + + offline notification + + +
+ +
+ logo +
+ + +
+

Your mining account:  

+

mining machines are offline!

+

If your mining rig has been abnormally disconnected, please address it promptly!

+
+ + + +
+ + + +