update 租赁系统1.1.0 新增登录系统,准备迁移该模块为独立模块

This commit is contained in:
yyb
2025-12-25 10:03:41 +08:00
parent bc2f76662c
commit 709681d7e5
50 changed files with 1967 additions and 142 deletions

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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();
}
}

View File

@@ -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("/**");
}
}

View File

@@ -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)";

View File

@@ -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());
}
}

View File

@@ -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);
}
}

View File

@@ -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 {
}

View File

@@ -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 {
}

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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")

View File

@@ -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;
}

View File

@@ -0,0 +1,10 @@
package com.m2pool.lease.exception;
/**
* 权限校验异常
*/
public class AuthException extends RuntimeException {
public AuthException(String message) {
super(message);
}
}

View File

@@ -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());
}
/**
* 处理其他未明确捕获的异常,返回统一的错误结果。
*

View File

@@ -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())){

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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);";
// }
//
//
//}

View File

@@ -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);
// }
//
//}

View File

@@ -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();
//---------------用户权限-----------------------
/**

View File

@@ -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;
}

View File

@@ -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("添加商品失败,该币种挖矿机器暂时不支持");

View File

@@ -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()

View File

@@ -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);

View File

@@ -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

View File

@@ -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) {

View File

@@ -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");

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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), "");
}
}

View File

@@ -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";
}

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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>

View File

@@ -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>

View File

@@ -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: &nbsp; <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>