update 租赁系统1.1.0 新增登录系统,准备迁移该模块为独立模块
This commit is contained in:
@@ -164,6 +164,15 @@
|
||||
<version>3.1.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Thymeleaf 用于发送邮箱 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -90,7 +90,7 @@ public class DecryptAspect {
|
||||
field.set(obj, decryptedValue);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RSAException("解密失败: " + e.getMessage());
|
||||
throw new RSAException("校验失败: " + e.getMessage());
|
||||
} finally {
|
||||
field.setAccessible(false);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.m2pool.lease.config;
|
||||
|
||||
import com.m2pool.common.redis.service.RedisService;
|
||||
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.utils.JwtUtils;
|
||||
import com.m2pool.lease.utils.UserThreadLocal;
|
||||
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);
|
||||
System.out.println("用户token:"+token);
|
||||
if (token == null || !isValidToken(token)) {
|
||||
throw new AuthException("用户未登录");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 验证token 并刷新token
|
||||
*/
|
||||
private boolean isValidToken(String token) {
|
||||
String userEmail = JwtUtils.getUserName(token);
|
||||
//1.把userEmail存入ThreadLocal 本地线程变量中
|
||||
UserThreadLocal.setUserEmail(userEmail);
|
||||
//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) {
|
||||
UserThreadLocal.remove();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.m2pool.lease.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
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("/**");
|
||||
}
|
||||
}
|
||||
@@ -13,13 +13,12 @@ import java.util.Map;
|
||||
* @Author yyb
|
||||
*/
|
||||
public class Algorithm {
|
||||
// GRS 出块间隔时间单位s
|
||||
public static final String GRS_ALGORITHM= "groestl";
|
||||
// Mona 出块间隔时间单位s
|
||||
|
||||
public static final String MONA_ALGORITHM= "Lyra2REv2";
|
||||
// NEXA 出块间隔时间单位s
|
||||
|
||||
public static final String NEXA_ALGORITHM= "NexaPow";
|
||||
// RXD 出块间隔时间单位s
|
||||
|
||||
public static final String RXD_ALGORITHM= "Sha512256D";
|
||||
|
||||
public static final String DGBQ_ALGORITHM= "DigiByte(Qubit)";
|
||||
|
||||
@@ -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<String> 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<String> trimBrand(List<String> 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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.m2pool.lease.controller;
|
||||
|
||||
|
||||
import com.m2pool.lease.annotation.Decrypt;
|
||||
import com.m2pool.lease.annotation.LoginRequired;
|
||||
import com.m2pool.lease.dto.*;
|
||||
import com.m2pool.lease.service.LeaseUserService;
|
||||
import com.m2pool.lease.vo.*;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.Valid;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 用户表 前端控制器
|
||||
* </p>
|
||||
*
|
||||
* @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<Map<String, Object>> login(@RequestBody UserLoginVo userLoginVo){
|
||||
return leaseUserService.login(userLoginVo);
|
||||
}
|
||||
|
||||
|
||||
@Decrypt
|
||||
@PostMapping("/register")
|
||||
@ApiOperation(value = "用户注册")
|
||||
public Result<String> register(@RequestBody UserRegisterVo userRegisterVo){
|
||||
return leaseUserService.register(userRegisterVo);
|
||||
}
|
||||
|
||||
@Decrypt
|
||||
@LoginRequired
|
||||
@PostMapping("/updatePassword")
|
||||
@ApiOperation(value = "修改密码")
|
||||
public Result<String> updatePassword(@RequestBody UserLoginVo userLoginVo){
|
||||
return leaseUserService.updatePassword(userLoginVo);
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/logout")
|
||||
@ApiOperation(value = "登出")
|
||||
public Result<String> logout(){
|
||||
return leaseUserService.logout();
|
||||
}
|
||||
|
||||
@PostMapping("/sendLoginCode")
|
||||
@ApiOperation(value = "发送用户登录邮箱验证码")
|
||||
public Result<String> sendLoginCode(@RequestBody @Valid EmailCodeVo emailCodeVo){
|
||||
return leaseUserService.sendLoginCode(emailCodeVo);
|
||||
}
|
||||
|
||||
@PostMapping("/sendRegisterCode")
|
||||
@ApiOperation(value = "发送用户注册邮箱验证码")
|
||||
public Result<String> sendRegisterCode(@RequestBody @Valid EmailCodeVo emailCodeVo){
|
||||
return leaseUserService.sendRegisterCode(emailCodeVo);
|
||||
}
|
||||
|
||||
@PostMapping("/sendUpdatePwdCode")
|
||||
@ApiOperation(value = "发送修改密码验证码")
|
||||
public Result<String> sendUpdatePwdCode(@RequestBody @Valid EmailCodeVo emailCodeVo){
|
||||
return leaseUserService.sendUpdatePwdCode(emailCodeVo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.m2pool.lease.controller;
|
||||
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 矿池nexa机器实时平均算力 前端控制器
|
||||
* </p>
|
||||
*
|
||||
* @author yyb
|
||||
* @since 2025-07-29
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/machine/avg/power")
|
||||
public class LeaseMachineAvgPowerController {
|
||||
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.m2pool.lease.controller;
|
||||
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 已售商品矿工实时算力表 前端控制器
|
||||
* </p>
|
||||
*
|
||||
* @author yyb
|
||||
* @since 2025-07-25
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/machine/power")
|
||||
public class LeaseMachinePowerController {
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.m2pool.lease.controller;
|
||||
|
||||
import com.m2pool.common.security.annotation.RequiresLogin;
|
||||
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;
|
||||
@@ -29,6 +30,7 @@ import java.util.List;
|
||||
* @since 2025-07-23
|
||||
*/
|
||||
@Api(tags = "订单控制器")
|
||||
@LoginRequired
|
||||
@RestController
|
||||
@RequestMapping("/order/info")
|
||||
public class LeaseOrderInfoController {
|
||||
|
||||
@@ -2,6 +2,7 @@ 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.*;
|
||||
@@ -29,6 +30,7 @@ import java.util.List;
|
||||
* @since 2025-07-23
|
||||
*/
|
||||
@Api(tags = "v2版本--订单控制器")
|
||||
@LoginRequired
|
||||
@RestController
|
||||
@RequestMapping("/v2/order/info")
|
||||
public class LeaseOrderInfoV2Controller {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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;
|
||||
@@ -27,6 +28,7 @@ import java.util.Map;
|
||||
* @since 2025-07-23
|
||||
*/
|
||||
@Api(tags = "商品矿机控制器(库存控制器)")
|
||||
@LoginRequired
|
||||
@RestController
|
||||
@RequestMapping("/product/machine")
|
||||
public class LeaseProductMachineController {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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;
|
||||
@@ -24,6 +25,7 @@ import java.util.List;
|
||||
* @since 2025-07-23
|
||||
*/
|
||||
@Api(tags = "v2版本--矿机控制器类")
|
||||
@LoginRequired
|
||||
@RestController
|
||||
@RequestMapping("/v2/product/machine")
|
||||
public class LeaseProductMachineV2Controller {
|
||||
@@ -33,6 +35,7 @@ public class LeaseProductMachineV2Controller {
|
||||
private LeaseMachineService leaseMachineService;
|
||||
|
||||
@ApiOperation("商城首页---店铺列表")
|
||||
@LoginRequired(value = false)
|
||||
@PostMapping("/getShopList")
|
||||
public PageResult<ShopInfoDto> getShopList(@RequestBody(required = false) ProductPageVo productPageVo) {
|
||||
if(productPageVo == null){
|
||||
@@ -43,6 +46,7 @@ public class LeaseProductMachineV2Controller {
|
||||
|
||||
|
||||
@ApiOperation("商城首页---店铺对应矿机详情列表")
|
||||
@LoginRequired(value = false)
|
||||
@PostMapping("/getShopMachineList")
|
||||
public PageResult<MachineInfoDto> getShopMachineList(@RequestBody ShopMachineVo shopMachineVo) {
|
||||
return leaseMachineService.getShopMachineList(shopMachineVo);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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;
|
||||
@@ -22,6 +23,7 @@ import java.util.List;
|
||||
* @since 2025-08-05
|
||||
*/
|
||||
@Api(tags = "店铺控制器")
|
||||
@LoginRequired
|
||||
@RestController
|
||||
@RequestMapping("/shop")
|
||||
public class LeaseShopController {
|
||||
|
||||
@@ -3,6 +3,7 @@ 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;
|
||||
@@ -31,6 +32,7 @@ import java.util.List;
|
||||
* @since 2025-08-05
|
||||
*/
|
||||
@Api(tags = "v2版本--店铺控制器")
|
||||
@LoginRequired
|
||||
@RestController
|
||||
@RequestMapping("/v2/shop")
|
||||
public class LeaseShopV2Controller {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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;
|
||||
@@ -28,6 +29,7 @@ import java.util.List;
|
||||
* @since 2025-07-23
|
||||
*/
|
||||
@Api(tags = "购物车表控制器")
|
||||
@LoginRequired
|
||||
@RestController
|
||||
@RequestMapping("/shopping/cart")
|
||||
public class LeaseShoppingCartController {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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;
|
||||
@@ -31,6 +32,7 @@ import java.util.List;
|
||||
* @since 2025-07-23
|
||||
*/
|
||||
@Api(tags = "v2版本---购物车表控制器")
|
||||
@LoginRequired
|
||||
@RestController
|
||||
@RequestMapping("/v2/shopping/cart")
|
||||
public class LeaseShoppingCartV2Controller {
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package com.m2pool.lease.controller;
|
||||
|
||||
|
||||
import com.m2pool.common.security.annotation.RequiresLogin;
|
||||
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.*;
|
||||
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.*;
|
||||
@@ -24,19 +27,14 @@ import java.util.List;
|
||||
* @author yyb
|
||||
* @since 2025-07-23
|
||||
*/
|
||||
@Api(tags = "用户控制器")
|
||||
@Api(tags = "用户信息控制器")
|
||||
@LoginRequired
|
||||
@RestController
|
||||
@RequestMapping("/user")
|
||||
public class LeaseUserController {
|
||||
|
||||
@Resource
|
||||
private LeaseUserService leaseUserService;
|
||||
@PostMapping("/login")
|
||||
@ApiOperation(value = "用户登录/注册")
|
||||
@Deprecated
|
||||
public Result<UserDto> login(@RequestBody UserURDVo userURDVo){
|
||||
return leaseUserService.login(userURDVo);
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/bindWallet")
|
||||
|
||||
@@ -39,8 +39,15 @@ public class LeaseUser implements Serializable {
|
||||
* 密码
|
||||
*/
|
||||
private String password;
|
||||
/**
|
||||
* 登录token
|
||||
*/
|
||||
private String token;
|
||||
|
||||
|
||||
/**
|
||||
* 最后登录时间
|
||||
*/
|
||||
private LocalDateTime lastLoginTime;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@@ -51,5 +58,7 @@ public class LeaseUser implements Serializable {
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
private Boolean del;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.m2pool.lease.exception;
|
||||
|
||||
/**
|
||||
* 权限校验异常
|
||||
*/
|
||||
public class AuthException extends RuntimeException {
|
||||
public AuthException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,11 @@ public class GlobalExceptionHandler {
|
||||
return Result.fail(e.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(AuthException.class)
|
||||
public Result<String> handleAuthException(AuthException e) {
|
||||
return Result.fail(e.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理其他未明确捕获的异常,返回统一的错误结果。
|
||||
*
|
||||
|
||||
@@ -3,6 +3,7 @@ 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.*;
|
||||
@@ -93,8 +94,7 @@ public class ServerChannelHandler extends SimpleChannelInboundHandler<ClientMess
|
||||
GpuAndSoftMessage gpuAndSoftMessage = JSONUtil.toBean(params.toString(), GpuAndSoftMessage.class);
|
||||
Map<String, GpuMessage> gpus = gpuAndSoftMessage.getGpus();
|
||||
List<String> miningsofts = gpuAndSoftMessage.getMiningsofts();
|
||||
List<String> modelList = gpus.values().stream().map(GpuMessage::getModel).collect(Collectors.toList());
|
||||
|
||||
List<String> modelList = GpuBrand.trimBrand(gpus.values().stream().map(GpuMessage::getModel).collect(Collectors.toList()));
|
||||
//挖矿软件公共配置
|
||||
List<CoinAndAlgorithmDto> supportAlgorithmAndCoin = leaseMiningSoftwareConfigMapper.selectSupportAlgorithm(miningsofts,modelList);
|
||||
// 新增主机相关信息
|
||||
@@ -162,7 +162,6 @@ public class ServerChannelHandler extends SimpleChannelInboundHandler<ClientMess
|
||||
*/
|
||||
public List<LeaseMachineConfig> packageInsert(Long machineId,Map<String, GpuMessage> gpus,List<CoinAndAlgorithmDto> supportAlgorithmAndCoin){
|
||||
List<LeaseMachineConfig> list = new ArrayList<>();
|
||||
System.out.println("gpu信息"+ JSONUtil.toJsonStr(gpus.values()));
|
||||
for (GpuMessage value : gpus.values()) {
|
||||
for (CoinAndAlgorithmDto coinAndAlgorithmDto : supportAlgorithmAndCoin) {
|
||||
if (coinAndAlgorithmDto.getModel() != null && value.getModel().contains(coinAndAlgorithmDto.getModel())){
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
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";
|
||||
|
||||
/**
|
||||
* 获取登录验证码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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.m2pool.lease.constant;
|
||||
package com.m2pool.lease.redis;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -9,7 +9,7 @@ import java.util.Map;
|
||||
* @Date 2025/8/18 09:49
|
||||
* @Author yyb
|
||||
*/
|
||||
public class RedisKey {
|
||||
public class RedisCoinInfoKey {
|
||||
// 静态不可变的 Map,存储币种名和对应价格标识
|
||||
private static final Map<String, String> COIN_PRICE_MAP;
|
||||
|
||||
@@ -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<T> implements RedisSerializer<T> {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
|
||||
|
||||
private Class<T> clazz;
|
||||
|
||||
static
|
||||
{
|
||||
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
|
||||
}
|
||||
|
||||
public FastJson2JsonRedisSerializer(Class<T> 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
//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<Object, Object> redisTemplate(@Qualifier("redisConnectionFactory0") RedisConnectionFactory connectionFactory)
|
||||
// {
|
||||
// RedisTemplate<Object, Object> 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<Object, Object> redisTemplate7(@Qualifier("redisConnectionFactory7") RedisConnectionFactory redisConnectionFactory0) {
|
||||
// RedisTemplate<Object, Object> 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<Long> limitScript()
|
||||
// {
|
||||
// // 泛型是返回值的类型
|
||||
// DefaultRedisScript<Long> 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);";
|
||||
// }
|
||||
//
|
||||
//
|
||||
//}
|
||||
@@ -0,0 +1,313 @@
|
||||
//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 <T> void setCacheObject(final String key, final T value)
|
||||
// {
|
||||
// redisTemplate.opsForValue().set(key, value);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * 缓存基本的对象,Integer、String、实体类等
|
||||
// *
|
||||
// * @param key 缓存的键值
|
||||
// * @param value 缓存的值
|
||||
// * @param timeout 时间
|
||||
// * @param timeUnit 时间颗粒度
|
||||
// */
|
||||
// public <T> void setCacheObject(final String key, final T value, final Long timeout, final TimeUnit timeUnit)
|
||||
// {
|
||||
// redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// public <T> 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> T getCacheObject(final String key)
|
||||
// {
|
||||
// ValueOperations<String, T> operation = redisTemplate.opsForValue();
|
||||
// return operation.get(key);
|
||||
// }
|
||||
//
|
||||
// public <T> T getCacheObject7(final String key)
|
||||
// {
|
||||
// ValueOperations<String, T> operation = redisTemplate7.opsForValue();
|
||||
// return operation.get(key);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 所有数字类型转换为BigDecimal
|
||||
// * @param key 缓存键值
|
||||
// * @return 缓存键值对应数据
|
||||
// */
|
||||
// public BigDecimal getCacheBigDecimal(final String key) {
|
||||
// ValueOperations<String, Object> 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 <T> long setCacheList(final String key, final List<T> dataList)
|
||||
// {
|
||||
// Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
|
||||
// return count == null ? 0 : count;
|
||||
// }
|
||||
// /**
|
||||
// * 获得缓存的list对象
|
||||
// *
|
||||
// * @param key 缓存的键值
|
||||
// * @return 缓存键值对应的数据
|
||||
// */
|
||||
// public <T> List<T> getCacheList(final String key)
|
||||
// {
|
||||
// return redisTemplate.opsForList().range(key, 0, -1);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 缓存Set
|
||||
// *
|
||||
// * @param key 缓存键值
|
||||
// * @param dataSet 缓存的数据
|
||||
// * @return 缓存数据的对象
|
||||
// */
|
||||
// public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
|
||||
// {
|
||||
// BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
|
||||
// Iterator<T> it = dataSet.iterator();
|
||||
// while (it.hasNext())
|
||||
// {
|
||||
// setOperation.add(it.next());
|
||||
// }
|
||||
// return setOperation;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获得缓存的set
|
||||
// *
|
||||
// * @param key
|
||||
// * @return
|
||||
// */
|
||||
// public <T> Set<T> getCacheSet(final String key)
|
||||
// {
|
||||
// return redisTemplate.opsForSet().members(key);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 缓存Map
|
||||
// *
|
||||
// * @param key
|
||||
// * @param dataMap
|
||||
// */
|
||||
// public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
|
||||
// {
|
||||
// if (dataMap != null) {
|
||||
// redisTemplate.opsForHash().putAll(key, dataMap);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获得缓存的Map
|
||||
// *
|
||||
// * @param key
|
||||
// * @return
|
||||
// */
|
||||
// public <T> Map<String, T> getCacheMap(final String key)
|
||||
// {
|
||||
// return redisTemplate.opsForHash().entries(key);
|
||||
//
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 往Hash中存入数据
|
||||
// *
|
||||
// * @param key Redis键
|
||||
// * @param hKey Hash键
|
||||
// * @param value 值
|
||||
// */
|
||||
// public <T> 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> T getCacheMapValue(final String key, final String hKey)
|
||||
// {
|
||||
// HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
|
||||
// return opsForHash.get(key, hKey);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获取多个Hash中的数据
|
||||
// *
|
||||
// * @param key Redis键
|
||||
// * @param hKeys Hash键集合
|
||||
// * @return Hash对象集合
|
||||
// */
|
||||
// public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
|
||||
// {
|
||||
// return redisTemplate.opsForHash().multiGet(key, hKeys);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获得缓存的基本对象列表
|
||||
// *
|
||||
// * @param pattern 字符串前缀
|
||||
// * @return 对象列表
|
||||
// */
|
||||
// public Collection<String> keys(final String pattern)
|
||||
// {
|
||||
// return redisTemplate.keys(pattern);
|
||||
// }
|
||||
//
|
||||
//}
|
||||
@@ -5,11 +5,11 @@ import com.m2pool.lease.dto.*;
|
||||
import com.m2pool.lease.entity.LeaseUser;
|
||||
import com.m2pool.lease.vo.*;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -23,10 +23,58 @@ public interface LeaseUserService extends IService<LeaseUser> {
|
||||
|
||||
/**
|
||||
* 用户登录/注册
|
||||
* @param userURDVo
|
||||
* @param userLoginVo
|
||||
* @return
|
||||
*/
|
||||
Result<UserDto> login(UserURDVo userURDVo);
|
||||
Result<Map<String, Object>> login(UserLoginVo userLoginVo);
|
||||
|
||||
|
||||
/**
|
||||
* 用户注册
|
||||
* @param userRegisterVo
|
||||
* @return
|
||||
*/
|
||||
Result<String> register( UserRegisterVo userRegisterVo);
|
||||
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
* @param userLoginVo
|
||||
* @return
|
||||
*/
|
||||
Result<String> updatePassword(UserLoginVo userLoginVo);
|
||||
|
||||
/**
|
||||
* 发送用户登录邮箱验证码
|
||||
* @param emailCodeVo
|
||||
* @return
|
||||
*/
|
||||
Result<String> sendLoginCode( EmailCodeVo emailCodeVo);
|
||||
|
||||
|
||||
/**
|
||||
* 获取用户注册邮箱验证码
|
||||
* @param emailCodeVo
|
||||
* @return
|
||||
*/
|
||||
Result<String> sendRegisterCode(EmailCodeVo emailCodeVo);
|
||||
|
||||
|
||||
/**
|
||||
* 获取用户修改密码邮箱验证码
|
||||
* @param emailCodeVo
|
||||
* @return
|
||||
*/
|
||||
Result<String> sendUpdatePwdCode(EmailCodeVo emailCodeVo);
|
||||
|
||||
|
||||
/**
|
||||
* 登出
|
||||
* @return
|
||||
*/
|
||||
Result<String> logout();
|
||||
|
||||
//---------------用户权限-----------------------
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.m2pool.lease.service.impl;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
@@ -13,7 +12,7 @@ import com.m2pool.common.core.utils.StringUtils;
|
||||
import com.m2pool.common.redis.service.RedisService;
|
||||
import com.m2pool.common.security.utils.SecurityUtils;
|
||||
import com.m2pool.lease.constant.CoinCharge;
|
||||
import com.m2pool.lease.constant.RedisKey;
|
||||
import com.m2pool.lease.redis.RedisCoinInfoKey;
|
||||
import com.m2pool.lease.dto.*;
|
||||
import com.m2pool.lease.dto.v2.*;
|
||||
import com.m2pool.lease.entity.*;
|
||||
@@ -467,7 +466,9 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
|
||||
public PageResult<OrderInfoDto> getOrdersByStatusForSeller(OrderInfoStateVo orderInfoStateVo) {
|
||||
LeaseShop leaseShop = leaseShopMapper.selectOne(new LambdaQueryWrapper<LeaseShop>()
|
||||
.eq(LeaseShop::getUserEmail, SecurityUtils.getUsername()));
|
||||
|
||||
if (leaseShop == null){
|
||||
return PageResult.fail(new ArrayList<>(),"不存在商铺");
|
||||
}
|
||||
//查询到商铺的所有订单
|
||||
List<LeaseOrderItem> orderItemList = leaseOrderItemMapper.selectList(new LambdaQueryWrapper<LeaseOrderItem>()
|
||||
.eq(LeaseOrderItem::getShopId, leaseShop.getId()));
|
||||
@@ -590,7 +591,7 @@ public class LeaseOrderInfoServiceImpl extends ServiceImpl<LeaseOrderInfoMapper,
|
||||
}
|
||||
public BigDecimal getCoinPrice(String coin) {
|
||||
coin = coin.toLowerCase();
|
||||
String priceKey = RedisKey.getPiceKey(coin);
|
||||
String priceKey = RedisCoinInfoKey.getPiceKey(coin);
|
||||
if (priceKey == null){
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package com.m2pool.lease.service.impl;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
|
||||
import com.github.pagehelper.PageHelper;
|
||||
@@ -11,18 +9,15 @@ import com.github.pagehelper.PageInfo;
|
||||
import com.m2pool.common.redis.service.RedisService;
|
||||
import com.m2pool.common.security.utils.SecurityUtils;
|
||||
import com.m2pool.lease.constant.BlockInterval;
|
||||
import com.m2pool.lease.constant.RedisKey;
|
||||
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.LeaseNexaMachinePowerMapper;
|
||||
import com.m2pool.lease.mapper.LeaseProductMachineMapper;
|
||||
import com.m2pool.lease.mapper.LeaseProductMachinePriceMapper;
|
||||
import com.m2pool.lease.mapper.LeaseProductMapper;
|
||||
import com.m2pool.lease.service.LeaseProductMachinePriceService;
|
||||
import com.m2pool.lease.service.LeaseProductMachineService;
|
||||
import com.m2pool.lease.service.LeaseProductService;
|
||||
import com.m2pool.lease.utils.PowerUnitUtils;
|
||||
import com.m2pool.lease.vo.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -183,9 +178,9 @@ public class LeaseProductMachineServiceImpl extends ServiceImpl<LeaseProductMach
|
||||
dto -> dto.getUser() + "-" + dto.getMiner(),
|
||||
dto -> dto
|
||||
));
|
||||
String priceKey = RedisKey.getPiceKey(product.getCoin());
|
||||
String rewardKey = RedisKey.getRewardKey(product.getCoin());
|
||||
String mhsKey = RedisKey.getMhsKey(product.getCoin());
|
||||
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("添加商品失败,该币种挖矿机器暂时不支持");
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.m2pool.lease.service.impl;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.alibaba.druid.support.json.JSONUtils;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
@@ -13,7 +12,7 @@ import com.m2pool.common.security.utils.SecurityUtils;
|
||||
import com.m2pool.lease.constant.Algorithm;
|
||||
import com.m2pool.lease.constant.BlockInterval;
|
||||
import com.m2pool.lease.constant.PowerUnit;
|
||||
import com.m2pool.lease.constant.RedisKey;
|
||||
import com.m2pool.lease.redis.RedisCoinInfoKey;
|
||||
import com.m2pool.lease.dto.*;
|
||||
import com.m2pool.lease.entity.*;
|
||||
import com.m2pool.lease.exception.ProductSoldOutException;
|
||||
@@ -35,7 +34,6 @@ import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -148,11 +146,11 @@ public class LeaseProductServiceImpl extends ServiceImpl<LeaseProductMapper, Lea
|
||||
return PageResult.fail(new ArrayList<>(),"不存在矿机");
|
||||
}
|
||||
//币价 单位usdt
|
||||
BigDecimal price = redisService.getCacheBigDecimal(RedisKey.getPiceKey(product.getCoin())) == null ? BigDecimal.ZERO : redisService.getCacheBigDecimal(RedisKey.getPiceKey(product.getCoin()));
|
||||
BigDecimal price = redisService.getCacheBigDecimal(RedisCoinInfoKey.getPiceKey(product.getCoin())) == null ? BigDecimal.ZERO : redisService.getCacheBigDecimal(RedisCoinInfoKey.getPiceKey(product.getCoin()));
|
||||
//报块奖励
|
||||
BigDecimal reward =redisService.getCacheBigDecimal(RedisKey.getRewardKey(product.getCoin())) == null ? BigDecimal.ZERO : redisService.getCacheBigDecimal(RedisKey.getRewardKey(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(RedisKey.getMhsKey(product.getCoin())) == null ? BigDecimal.ZERO : redisService.getCacheBigDecimal(RedisKey.getMhsKey(product.getCoin()));
|
||||
BigDecimal mhs = redisService.getCacheBigDecimal(RedisCoinInfoKey.getMhsKey(product.getCoin())) == null ? BigDecimal.ZERO : redisService.getCacheBigDecimal(RedisCoinInfoKey.getMhsKey(product.getCoin()));
|
||||
|
||||
List<ProductMachineDto> productMachines = productMachineDtoList.stream()
|
||||
.peek(productMachineDto -> {
|
||||
@@ -432,7 +430,7 @@ public class LeaseProductServiceImpl extends ServiceImpl<LeaseProductMapper, Lea
|
||||
}
|
||||
//2.获取商品对应矿机
|
||||
List<ProductMachineForWalletConfigDto> productMachineForWalletConfigDtoList = leaseProductMachineMapper.getProductListForShopWalletConfig(shopId);
|
||||
BigDecimal usdtPrice = redisService.getCacheBigDecimal(RedisKey.getPiceKey(productForShopWalletConfigDtoList.get(0).getCoin()));
|
||||
BigDecimal usdtPrice = redisService.getCacheBigDecimal(RedisCoinInfoKey.getPiceKey(productForShopWalletConfigDtoList.get(0).getCoin()));
|
||||
Map<Long, List<ProductMachineForWalletConfigDto>> collect = productMachineForWalletConfigDtoList.stream()
|
||||
.peek(productMachineForWalletConfigDto -> {
|
||||
productMachineForWalletConfigDto.setTheoryUsdtIncome(productMachineForWalletConfigDto.getTheoryIncome()
|
||||
|
||||
@@ -10,7 +10,7 @@ import com.m2pool.common.redis.service.RedisService;
|
||||
import com.m2pool.common.security.utils.SecurityUtils;
|
||||
import com.m2pool.lease.constant.BlockInterval;
|
||||
import com.m2pool.lease.constant.CoinCharge;
|
||||
import com.m2pool.lease.constant.RedisKey;
|
||||
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;
|
||||
@@ -168,9 +168,9 @@ public class LeaseShoppingCartServiceImpl extends ServiceImpl<LeaseShoppingCartM
|
||||
Map<String, BigDecimal> coinRewardMap = new HashMap<>();
|
||||
Map<String, BigDecimal> coinMhsMap = new HashMap<>();
|
||||
for (String coin : coins) {
|
||||
BigDecimal price = redisService.getCacheBigDecimal(RedisKey.getPiceKey(coin));
|
||||
BigDecimal reward =redisService.getCacheBigDecimal(RedisKey.getRewardKey(coin));
|
||||
BigDecimal mhs = redisService.getCacheBigDecimal(RedisKey.getMhsKey(coin));
|
||||
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);
|
||||
@@ -455,9 +455,9 @@ public class LeaseShoppingCartServiceImpl extends ServiceImpl<LeaseShoppingCartM
|
||||
//Map<String, BigDecimal> coinRewardMap = new HashMap<>();
|
||||
//Map<String, BigDecimal> coinMhsMap = new HashMap<>();
|
||||
//for (String coin : coins) {
|
||||
// BigDecimal price = redisService.getCacheBigDecimal(RedisKey.getPiceKey(coin));
|
||||
// BigDecimal reward =redisService.getCacheBigDecimal(RedisKey.getRewardKey(coin));
|
||||
// BigDecimal mhs = redisService.getCacheBigDecimal(RedisKey.getMhsKey(coin));
|
||||
// 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);
|
||||
|
||||
@@ -7,7 +7,7 @@ import com.github.pagehelper.PageHelper;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.m2pool.common.redis.service.RedisService;
|
||||
import com.m2pool.common.security.utils.SecurityUtils;
|
||||
import com.m2pool.lease.constant.RedisKey;
|
||||
import com.m2pool.lease.redis.RedisCoinInfoKey;
|
||||
import com.m2pool.lease.dto.*;
|
||||
import com.m2pool.lease.entity.*;
|
||||
import com.m2pool.lease.mapper.*;
|
||||
@@ -21,7 +21,6 @@ import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -88,7 +87,7 @@ public class LeaseUserOwnedProductServiceImpl extends ServiceImpl<LeaseUserOwned
|
||||
//封装返回对象
|
||||
List<UserOwnedProductDto> userOwnedProducts = leaseUserOwnedProducts.stream().map(leaseUserOwnedProduct ->{
|
||||
String coin = leaseUserOwnedProduct.getCoin();
|
||||
BigDecimal bigDecimal = coinPrice.putIfAbsent(coin, redisService.getCacheBigDecimal(RedisKey.getPiceKey(coin)));
|
||||
BigDecimal bigDecimal = coinPrice.putIfAbsent(coin, redisService.getCacheBigDecimal(RedisCoinInfoKey.getPiceKey(coin)));
|
||||
|
||||
UserOwnedProductDto build = UserOwnedProductDto.builder()
|
||||
.id(leaseUserOwnedProduct.getId())
|
||||
@@ -130,7 +129,7 @@ public class LeaseUserOwnedProductServiceImpl extends ServiceImpl<LeaseUserOwned
|
||||
.user(leaseUserOwnedProduct.getUser())
|
||||
.miner(leaseUserOwnedProduct.getMiner())
|
||||
.build());
|
||||
BigDecimal price = redisService.getCacheBigDecimal(RedisKey.getPiceKey(leaseUserOwnedProduct.getCoin()));
|
||||
BigDecimal price = redisService.getCacheBigDecimal(RedisCoinInfoKey.getPiceKey(leaseUserOwnedProduct.getCoin()));
|
||||
//获取实时算力
|
||||
List<ProductMachineDto> recentlyFiveMinutesData = leaseProductMachineMapper.getRecentlyFiveMinutesData(leaseProductMachines, leaseUserOwnedProduct.getCoin());
|
||||
Integer status = leaseUserOwnedProduct.getStatus();
|
||||
@@ -177,7 +176,7 @@ public class LeaseUserOwnedProductServiceImpl extends ServiceImpl<LeaseUserOwned
|
||||
// .in(LeaseOrderItem::getProductMachineId, ids));
|
||||
|
||||
//获取卖方收款币种币价
|
||||
BigDecimal price = redisService.getCacheBigDecimal(RedisKey.getPiceKey(leaseOrderItems.get(0).getCoin()));
|
||||
BigDecimal price = redisService.getCacheBigDecimal(RedisCoinInfoKey.getPiceKey(leaseOrderItems.get(0).getCoin()));
|
||||
List<LeaseUserOwnedProduct> collect = leaseOrderItems.stream()
|
||||
.map(item -> {
|
||||
// 获取当前的 LocalDateTime
|
||||
|
||||
@@ -1,52 +1,72 @@
|
||||
package com.m2pool.lease.service.impl;
|
||||
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.m2pool.common.core.utils.DateUtils;
|
||||
import com.m2pool.common.core.Result.R;
|
||||
import com.m2pool.common.core.exception.ServiceException;
|
||||
import com.m2pool.common.core.text.Convert;
|
||||
import com.m2pool.common.core.utils.CodeUtils;
|
||||
import com.m2pool.common.core.utils.StringUtils;
|
||||
import com.m2pool.common.core.utils.uuid.IdUtils;
|
||||
import com.m2pool.common.redis.service.RedisService;
|
||||
import com.m2pool.common.security.utils.SecurityUtils;
|
||||
import com.m2pool.lease.dto.*;
|
||||
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.service.LeaseUserService;
|
||||
import com.m2pool.lease.utils.*;
|
||||
import com.m2pool.lease.vo.*;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import com.m2pool.system.api.entity.EmailTemplateEntity;
|
||||
import com.m2pool.system.api.entity.SysUser;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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.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 java.io.*;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import static com.m2pool.lease.constant.CoinCharge.getAllChargesAsDtoList;
|
||||
import static com.m2pool.lease.constant.CoinCharge.getChargeByChainAndCoin;
|
||||
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;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -87,7 +107,14 @@ public class LeaseUserServiceImpl extends ServiceImpl<LeaseUserMapper, LeaseUser
|
||||
@Resource
|
||||
private LeaseShopMapper leaseShopMapper;
|
||||
|
||||
@Resource
|
||||
private RedisService redisService;
|
||||
|
||||
@Autowired
|
||||
private JavaMailSenderImpl javaMailSender;
|
||||
|
||||
@Resource
|
||||
private TemplateEngine templateEngine;
|
||||
|
||||
@Value("${client.download.client.linux}")
|
||||
private String downloadClientLinux;
|
||||
@@ -98,35 +125,437 @@ public class LeaseUserServiceImpl extends ServiceImpl<LeaseUserMapper, LeaseUser
|
||||
@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<String, Object> 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<String, Object> claimsMap = new HashMap<String, Object>();
|
||||
claimsMap.put(JwtUtils.USER_KEY, token);
|
||||
claimsMap.put(JwtUtils.DETAILS_USER_ID, loginUser.getId());
|
||||
claimsMap.put(JwtUtils.DETAILS_USERNAME, loginUser.getUserId());
|
||||
|
||||
// 接口返回信息
|
||||
Map<String, Object> rspMap = new HashMap<String, Object>();
|
||||
rspMap.put("access_token", JwtUtils.createToken(claimsMap));
|
||||
rspMap.put("expires_in", EXPIRETIME);
|
||||
rspMap.put("userName", loginUser.getUserId());
|
||||
return rspMap;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<Map<String, Object>> 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<String> 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<String> updatePassword(UserLoginVo userLoginVo) {
|
||||
String email = userLoginVo.getEmail();
|
||||
String password = userLoginVo.getPassword();
|
||||
String updatePwdCode = userLoginVo.getCode();
|
||||
//1.校验密码,邮箱等参数格式
|
||||
verifyParams(email,password);
|
||||
//2.验证码校验
|
||||
verifyCode(RedisAuthKey.getRegisterCodeKey(email),userLoginVo.getCode());
|
||||
//3.修改密码
|
||||
LeaseUser user = getUser(email);
|
||||
if (CryptoUtils.matchesPassword(password, user.getPassword())){
|
||||
throw new AuthException("新密码不能与原密码一致");
|
||||
}
|
||||
String updatePasswordCodeKey = RedisAuthKey.getUpdatePasswordCodeKey(email);
|
||||
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<String> logout() {
|
||||
String userEmail = UserThreadLocal.getUserEmail();
|
||||
boolean b = redisService.deleteObject(RedisAuthKey.getUserLoginKey(userEmail));
|
||||
if (b){
|
||||
return Result.success("登出成功");
|
||||
}
|
||||
return Result.fail("登出失败");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<String> 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
|
||||
);
|
||||
|
||||
sendLoginCodeMailMessage(email, emailCodeValue.getEmailCode());
|
||||
//设置失效时间10分钟
|
||||
redisService.setCacheObject(loginCodeKey, emailCodeValue,
|
||||
10L, TimeUnit.MINUTES
|
||||
);
|
||||
}
|
||||
}
|
||||
return Result.success("发送验证码成功,验证码已经发送至用户邮箱");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<String> 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
|
||||
);
|
||||
|
||||
sendRegisterMessage(email, emailCodeValue.getEmailCode());
|
||||
//设置失效时间10分钟
|
||||
redisService.setCacheObject(loginCodeKey, emailCodeValue,
|
||||
10L, TimeUnit.MINUTES
|
||||
);
|
||||
}
|
||||
}
|
||||
return Result.success("发送验证码成功,验证码已经发送至用户邮箱");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Result<UserDto> login(UserURDVo userURDVo) {
|
||||
LeaseUser leaseUser = leaseUserMapper.selectOne(new LambdaQueryWrapper<LeaseUser>()
|
||||
.eq(LeaseUser::getUserId, userURDVo.getUserId()));
|
||||
if (leaseUser != null){
|
||||
return Result.success(UserDto.builder()
|
||||
.id(leaseUser.getId())
|
||||
.userId(leaseUser.getUserId())
|
||||
.password(leaseUser.getPassword())
|
||||
.build());
|
||||
}
|
||||
LeaseUser build = LeaseUser.builder().password(userURDVo.getPassword()).userId(userURDVo.getUserId()).build();
|
||||
int insert = leaseUserMapper.insert(build);
|
||||
@Transactional
|
||||
public Result<String> sendUpdatePwdCode(EmailCodeVo emailCodeVo) {
|
||||
String email = emailCodeVo.getEmail();
|
||||
//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
|
||||
);
|
||||
|
||||
if (insert > 0){
|
||||
return Result.success(UserDto.builder()
|
||||
.id(build.getId())
|
||||
.userId(build.getUserId())
|
||||
.password(build.getPassword())
|
||||
.build());
|
||||
}else {
|
||||
return Result.fail("登录失败");
|
||||
sendLoginCodeMailMessage(email, emailCodeValue.getEmailCode());
|
||||
//设置失效时间10分钟
|
||||
redisService.setCacheObject(loginCodeKey, emailCodeValue,
|
||||
10L, TimeUnit.MINUTES
|
||||
);
|
||||
}
|
||||
return Result.success("发送验证码成功,验证码已经发送至用户邮箱");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 检查发送次数是否大于5次
|
||||
* @param email
|
||||
* @param key
|
||||
* @param type 0 登录验证码 1 注册验证码 2 修改密码验证码
|
||||
* @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());
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据邮箱获取用户
|
||||
* @param userEmail
|
||||
* @return
|
||||
*/
|
||||
public LeaseUser getUser(String userEmail){
|
||||
return leaseUserMapper.selectOne(new LambdaQueryWrapper<LeaseUser>()
|
||||
.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<String, Object> 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<String, Object> 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<String, Object> 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过thymeleaf发送html邮件
|
||||
* @param to
|
||||
* @param subject
|
||||
* @param templateName
|
||||
* @param variables
|
||||
* @throws MessagingException
|
||||
*/
|
||||
public void sendHtmlEmail(String to, String subject, String templateName, Map<String, Object> 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) {
|
||||
System.out.println("发送失败原因"+e.getMessage());
|
||||
throw new AuthException("发送邮箱验证码失败,请稍后重试");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<UserWalletDataDto> bindWallet(ChainAndCoinVo chainAndCoinVo) {
|
||||
|
||||
@@ -138,9 +138,8 @@ public class OwnProductTask {
|
||||
}
|
||||
|
||||
|
||||
//开发
|
||||
//@Scheduled(cron = "0 35 0/1 * * ? ")
|
||||
@Scheduled(cron = "30 0/2 * * * ? ")
|
||||
@Scheduled(cron = "0 35 0/1 * * ? ")
|
||||
//@Scheduled(cron = "30 0/2 * * * ? ")
|
||||
@DSTransactional
|
||||
public void updateNexaIncomeTask(){
|
||||
List<LeaseUserOwnedProduct> updateList = computeIncome("nexa");
|
||||
@@ -149,8 +148,8 @@ public class OwnProductTask {
|
||||
|
||||
}
|
||||
|
||||
//@Scheduled(cron = "10 35 0/1 * * ? ")
|
||||
@Scheduled(cron = "30 0/2 * * * ? ")
|
||||
@Scheduled(cron = "10 35 0/1 * * ? ")
|
||||
//@Scheduled(cron = "30 0/2 * * * ? ")
|
||||
@DSTransactional
|
||||
public void updateGrsIncomeTask(){
|
||||
List<LeaseUserOwnedProduct> updateList = computeIncome("grs");
|
||||
@@ -158,8 +157,8 @@ public class OwnProductTask {
|
||||
batchUpdateLeaseUserOwnedProducts(updateList);
|
||||
}
|
||||
|
||||
//@Scheduled(cron = "20 35 0/1 * * ? ")
|
||||
@Scheduled(cron = "30 0/2 * * * ? ")
|
||||
@Scheduled(cron = "20 35 0/1 * * ? ")
|
||||
//@Scheduled(cron = "30 0/2 * * * ? ")
|
||||
@DSTransactional
|
||||
public void updateRxdIncomeTask(){
|
||||
List<LeaseUserOwnedProduct> updateList = computeIncome("rxd");
|
||||
@@ -167,8 +166,8 @@ public class OwnProductTask {
|
||||
batchUpdateLeaseUserOwnedProducts(updateList);
|
||||
}
|
||||
|
||||
//@Scheduled(cron = "30 35 0/1 * * ? ")
|
||||
@Scheduled(cron = "30 0/2 * * * ? ")
|
||||
@Scheduled(cron = "30 35 0/1 * * ? ")
|
||||
//@Scheduled(cron = "30 0/2 * * * ? ")
|
||||
@DSTransactional
|
||||
public void updateMonaIncomeTask(){
|
||||
List<LeaseUserOwnedProduct> updateList = computeIncome("mona");
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.m2pool.lease.utils;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* @Description TODO
|
||||
* @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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package com.m2pool.lease.utils;
|
||||
|
||||
|
||||
import com.m2pool.common.core.constant.SecurityConstants;
|
||||
import com.m2pool.common.core.text.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;
|
||||
|
||||
/**
|
||||
* 用户标识
|
||||
*/
|
||||
public static final String USER_KEY = "user_key";
|
||||
|
||||
/**
|
||||
* 用户ID字段
|
||||
*/
|
||||
public static final String DETAILS_USER_ID = "user_id";
|
||||
|
||||
/**
|
||||
* 用户名字段
|
||||
*/
|
||||
public static final String DETAILS_USERNAME = "username";
|
||||
|
||||
/**
|
||||
* 令牌自定义标识
|
||||
*/
|
||||
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<String, Object> 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, SecurityConstants.USER_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据令牌获取用户标识
|
||||
*
|
||||
* @param claims 身份信息
|
||||
* @return 用户ID
|
||||
*/
|
||||
public static String getUserKey(Claims claims)
|
||||
{
|
||||
return getValue(claims, SecurityConstants.USER_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据令牌获取用户ID
|
||||
*
|
||||
* @param token 令牌
|
||||
* @return 用户ID
|
||||
*/
|
||||
public static String getUserId(String token)
|
||||
{
|
||||
Claims claims = parseToken(token);
|
||||
return getValue(claims, SecurityConstants.DETAILS_USER_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据身份信息获取用户ID
|
||||
*
|
||||
* @param claims 身份信息
|
||||
* @return 用户ID
|
||||
*/
|
||||
public static String getUserId(Claims claims)
|
||||
{
|
||||
return getValue(claims, SecurityConstants.DETAILS_USER_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据令牌获取用户名
|
||||
*
|
||||
* @param token 令牌
|
||||
* @return 用户名
|
||||
*/
|
||||
public static String getUserName(String token)
|
||||
{
|
||||
Claims claims = parseToken(token);
|
||||
return getValue(claims, SecurityConstants.DETAILS_USERNAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据身份信息获取用户名
|
||||
*
|
||||
* @param claims 身份信息
|
||||
* @return 用户名
|
||||
*/
|
||||
public static String getUserName(Claims claims)
|
||||
{
|
||||
return getValue(claims, SecurityConstants.DETAILS_USERNAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据身份信息获取键值
|
||||
*
|
||||
* @param claims 身份信息
|
||||
* @param key 键
|
||||
* @return 值
|
||||
*/
|
||||
public static String getValue(Claims claims, String key)
|
||||
{
|
||||
return Convert.toStr(claims.get(key), "");
|
||||
}
|
||||
}
|
||||
@@ -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 ";
|
||||
|
||||
/**
|
||||
* 令牌秘钥
|
||||
*/
|
||||
//TODO 根据情况更改密钥
|
||||
public final static String SECRET = "mabsadba2basdsanwepsdaowwwxxsodddl";
|
||||
|
||||
public final static String API_SECRET = "mabsadba2bttxdnwcabpiowwwxxsodddl";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.m2pool.lease.utils;
|
||||
|
||||
/**
|
||||
* 线程变量
|
||||
*/
|
||||
public class UserThreadLocal {
|
||||
private static final ThreadLocal<String> USER_EMAIL = new ThreadLocal<>();
|
||||
|
||||
public static void setUserEmail(String email) {
|
||||
USER_EMAIL.set(email);
|
||||
}
|
||||
|
||||
public static String getUserEmail() {
|
||||
return USER_EMAIL.get();
|
||||
}
|
||||
|
||||
public static void remove() {
|
||||
USER_EMAIL.remove();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.m2pool.lease.vo;
|
||||
|
||||
import com.m2pool.lease.annotation.EncryptedField;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -19,17 +20,16 @@ import lombok.NoArgsConstructor;
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ApiModel(description = "用户请求对象",value = "UserDto")
|
||||
public class UserURDVo {
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
@ApiModelProperty(value = "用户id(邮箱)",example = "Eudora.law@outlook.com")
|
||||
private String userId;
|
||||
@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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 用户请求对象
|
||||
* </p>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
@@ -5,6 +5,23 @@ server:
|
||||
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
|
||||
|
||||
@@ -5,6 +5,43 @@ server:
|
||||
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!
|
||||
|
||||
#端口号
|
||||
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
|
||||
|
||||
mvc:
|
||||
pathmatch:
|
||||
matching-strategy: ant_path_matcher
|
||||
@@ -57,3 +94,5 @@ netty:
|
||||
tcp:
|
||||
client:
|
||||
port: 2345
|
||||
image:
|
||||
prefix: https://test.m2pool.com
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
<foreach collection="list" item="item" separator=";">
|
||||
UPDATE lease_shop_config
|
||||
SET balance = balance + #{item.realAmount}
|
||||
WHERE pay_address = #{item.payAddress} AND pay_coin = #{item.payCoin} AND chain = #{item.chain} AND del = false
|
||||
WHERE pay_address = #{item.fromAddress} AND pay_coin = #{item.fromSymbol} AND chain = #{item.fromChain} AND del = false
|
||||
</foreach>
|
||||
</update>
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN" xmlns:th="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Email code</title>
|
||||
</head>
|
||||
|
||||
<body style="margin:0; padding:0;">
|
||||
<!-- 合并 th:style 属性 -->
|
||||
<div class="container" role="region" aria-label="M2POOL Email verification code page" tabindex="0"
|
||||
th:style="'background-image: url(' + @{${imagePrefix} + '/img/email/bg1.png'} + '); width:100%; max-width:600px; height:auto; min-height:100vh; margin:0 auto; box-sizing:border-box; padding:5%; position:relative; overflow:hidden; background-color:#f5f5f5; background-size:cover; background-repeat:no-repeat; '">
|
||||
<!-- 顶部品牌标识 -->
|
||||
<div class="brand" aria-label="M2POOL brand logo" style="width:100%;">
|
||||
<img th:src= "@{${imagePrefix} + '/img/email/logo.png'}" alt="logo" style="width: 8vw; height: auto; margin-left: 10%; margin-top: 6%;">
|
||||
</div>
|
||||
|
||||
<!-- 中心验证码卡片 -->
|
||||
<!-- 合并 th:style 属性 -->
|
||||
<section class="card" aria-label="Verification code card"
|
||||
th:style="'background-image: url(' + @{${imagePrefix} + '/img/email/daio.png'} + '); width:100%; max-width:320px; height:auto; min-height:250px; margin:0 auto; margin-top:10vh; box-shadow:0 8px 24px rgba(17, 24, 39, 0.10), 0 2px 6px rgba(17, 24, 39, 0.06); position:relative; overflow:hidden; display:block; text-align:center; background-color:#ffffff; background-size:120% auto; background-position:center; background-repeat:no-repeat; border-radius:12px; padding:5%;'">
|
||||
<!-- 合并 th:style 属性 -->
|
||||
<p class="notice" th:text="${text}" style="color:rgba(0,0,0,0.7); font-size:18px; margin-top:10%; text-align:center;"></p>
|
||||
<!-- 合并 th:style 属性 -->
|
||||
<div class="code-box" aria-live="polite" aria-label="CAPTCHA"
|
||||
style="position:relative; display:inline-flex; align-items:center; justify-content:center; margin-top:2vh; width:80%; max-width:300px; height:50px; border-radius:12px; background:#ffffff; border:3px solid #651EFE;">
|
||||
<!-- 合并 th:style 属性 -->
|
||||
<span class="code-text js-code-text" data-default-code="4MKM6AX" th:text="${code}"
|
||||
style="font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; font-weight:800; font-size:24px; letter-spacing:0.18em; color:#111827;margin:auto;"></span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 底部帮助与联系入口 -->
|
||||
<footer class="page-footer" style="margin-top:18vh; width:100%; height:auto; text-align:center;">
|
||||
<!-- 合并 th:style 属性 -->
|
||||
<div class="divider" aria-hidden="true"
|
||||
style="width:100%; height:3px; background:#DDDDDD; margin:8px 0 20px;"></div>
|
||||
<!-- 合并 th:style 属性 -->
|
||||
<p style="margin:0; padding:0; font-size:14px; color:#777777;">
|
||||
Thank you for choosing M2POOL. Need help?
|
||||
<a class="contact-link" href="mailto:support@m2pool.com" aria-label="Contact customer service email"
|
||||
style="color:#111827; font-weight:800; text-decoration:none; border-bottom:2px solid #111827; outline:none;">Please
|
||||
contact customer service.</a>
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,37 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN" xmlns:th="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>offline notification</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container" role="region" aria-label="M2POOL offline notification" tabindex="0"
|
||||
th:style="'background-image: url(' + @{${imagePrefix} + '/img/email/bg1.png'} + '); width:100%; max-width:600px; height:auto; min-height:100vh; margin:0 auto; position:relative; overflow:hidden; padding:2%; background-color:#f5f5f5; background-size:cover; background-repeat:no-repeat;box-sizing:border-box;'">
|
||||
<!-- 顶部品牌标识 -->
|
||||
<div class="brand" aria-label="M2POOL brand logo">
|
||||
<img th:src= "@{${imagePrefix} + '/img/email/logo.png'}" alt="logo" style="width:120px; height:auto; margin-left:10%; margin-top:6%;">
|
||||
</div>
|
||||
|
||||
<!-- 中心验证码卡片 -->
|
||||
<section class="card" aria-label="Offline Notification Card" th:style="'background-image: url(' + @{${imagePrefix} + '/img/email/daio.png'} + ');width:100%; max-width:310px; height:auto; min-height:240px; margin:0 auto; margin-top:10vh; box-shadow:0 8px 24px rgba(17, 24, 39, 0.10), 0 2px 6px rgba(17, 24, 39, 0.06); position:relative; overflow:hidden; padding:44px 40px 36px; text-align:center; background-color:#ffffff; background-size:105% 112%; background-position:center; background-repeat:no-repeat; border-radius:12px;'">
|
||||
<p class="notice" style="color:rgba(0,0,0,0.8); font-size:18px; margin-bottom:15px;">Your <span class="account-name" th:text="${coin}" style="color:#F94D87; font-weight:900; font-size:20px;"></span> mining account: <span class="account-name" style="color:#F94D87; font-weight:900; font-size:20px;" th:text="${user}"></span> </p>
|
||||
<p class="notice" style="color:rgba(0,0,0,0.8); font-size:18px; margin-bottom:15px;"> <span class="account-name" th:text="${offOnlineNumbers}" style="color:#F94D87; font-weight:900; font-size:20px;"></span> mining machines are offline!</p>
|
||||
<p class="notice" style="color:rgba(0,0,0,0.8); font-size:18px;">If your mining rig has been abnormally disconnected, please address it promptly!</p>
|
||||
</section>
|
||||
|
||||
<!-- 底部帮助与联系入口 -->
|
||||
<footer class="page-footer" style="margin-top:18vh; width:100%; height:auto; text-align:center;">
|
||||
<div class="divider" aria-hidden="true" style="width:100%; height:3px; background:#DDDDDD; margin:8px 0 20px;"></div>
|
||||
<p style="margin:0; padding:0; font-size:14px; color:#777777;">
|
||||
Thank you for choosing M2POOL. Need help?
|
||||
<a class="contact-link" style="color:#111827; font-weight:800; text-decoration:none; border-bottom:2px solid #111827; outline:none;" href="mailto:support@m2pool.com" aria-label="Contact Customer Service Email">Please contact customer service.</a>
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user