RepeatSubmit Ticket

This commit is contained in:
yandanyang 2021-10-09 20:20:18 +08:00
parent 51bb1a42f2
commit daa7f47d23
13 changed files with 259 additions and 68 deletions

View File

@ -18,7 +18,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
@EnableCaching
@EnableScheduling
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
@MapperScan(value = "net.lab1024.smartadmin.service.*",basePackageClasses = Mapper.class)
@MapperScan(value = "net.lab1024.smartadmin.service.*",annotationClass = Mapper.class)
public class SmartAdminApplication {
public static void main(String[] args) {

View File

@ -0,0 +1,34 @@
package net.lab1024.smartadmin.service.config;
import net.lab1024.smartadmin.service.module.support.heartbeat.core.HeartBeatManager;
import net.lab1024.smartadmin.service.module.support.heartbeat.core.IHeartBeatRecordHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* [ ]
*
* @author 罗伊
* @date 2021/10/9 18:47
*/
@Configuration
public class HeartBeatConfig {
/**
* 间隔时间
*/
@Value("${heart-beat.intervalTime}")
private Long intervalTime;
@Autowired
private IHeartBeatRecordHandler heartBeatRecordHandler;
@Bean
public HeartBeatManager heartBeatManager() {
return new HeartBeatManager(intervalTime, heartBeatRecordHandler);
}
}

View File

@ -0,0 +1,47 @@
package net.lab1024.smartadmin.service.config;
import net.lab1024.smartadmin.service.common.util.SmartEmployeeTokenUtil;
import net.lab1024.smartadmin.service.module.support.repeatsubmit.RepeatSubmitAspect;
import net.lab1024.smartadmin.service.module.support.repeatsubmit.RepeatSubmitCaffeineTicket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* [ ]
*
* @author 罗伊
* @date 2021/10/9 18:47
*/
@Configuration
public class RepeatSubmitConfig {
// @Autowired
// private ValueOperations<String, String> redisValueOperations;
//
// @Bean
// public RepeatSubmitAspect repeatSubmitAspect() {
// RepeatSubmitRedisTicket redisTicket = new RepeatSubmitRedisTicket(redisValueOperations, this::ticket);
// return new RepeatSubmitAspect(redisTicket);
// }
@Bean
public RepeatSubmitAspect repeatSubmitAspect() {
RepeatSubmitCaffeineTicket caffeineTicket = new RepeatSubmitCaffeineTicket(this::ticket);
return new RepeatSubmitAspect(caffeineTicket);
}
/**
* 获取指明某个用户的凭证
*
* @return
*/
private String ticket(String servletPath) {
Long employeeId = SmartEmployeeTokenUtil.getRequestEmployeeId();
if (employeeId == null) {
return "";
}
return servletPath + "_" + employeeId.toString();
}
}

View File

@ -3,6 +3,7 @@ package net.lab1024.smartadmin.service.module.business.goods;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.lab1024.smartadmin.service.module.business.goods.domain.*;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Component;
@ -14,6 +15,7 @@ import java.util.List;
* @author 胡克
* @date 2021/8/6 15:26
*/
@Mapper
@Component
public interface GoodsDao extends BaseMapper<GoodsEntity> {

View File

@ -11,6 +11,7 @@ import net.lab1024.smartadmin.service.module.support.file.domain.vo.FileVO;
import net.lab1024.smartadmin.service.module.support.file.service.FileService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import net.lab1024.smartadmin.service.module.support.repeatsubmit.annoation.RepeatSubmit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@ -58,6 +59,7 @@ public class FileController extends SupportBaseController {
@ApiOperation(value = "系统文件查询 by listen")
@PostMapping("/file/query")
@RepeatSubmit(3000)
public ResponseDTO<PageResultDTO<FileVO>> queryListByPage(@RequestBody @Valid FileQueryForm queryForm) {
return fileService.queryListByPage(queryForm);
}

View File

@ -18,6 +18,7 @@ import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;
@ -38,6 +39,7 @@ import java.util.Map;
* @date 2020/8/25 11:57
*/
@Slf4j
@Component
@ConditionalOnProperty(prefix = "file.storage", name = {"mode"}, havingValue = "cloud")
public class FileStorageCloudServiceImpl implements IFileStorageService {

View File

@ -12,6 +12,7 @@ import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;
@ -26,8 +27,8 @@ import java.io.InputStream;
* @author 罗伊
* @date 2020/8/25 11:57
*/
@Slf4j
@Component
@ConditionalOnProperty(prefix = "file.storage", name = {"mode"}, havingValue = "local")
public class FileStorageLocalServiceImpl implements IFileStorageService {

View File

@ -0,0 +1,50 @@
package net.lab1024.smartadmin.service.module.support.repeatsubmit;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
/**
* [ ]
*
* @author yandanyang
* @date 2021/10/9 19:10
*/
public abstract class AbstractRepeatSubmitTicket {
private Function<String,String> ticketFunction;
public AbstractRepeatSubmitTicket(Function<String, String> ticketFunction) {
this.ticketFunction = ticketFunction;
}
/**
* 获取凭证
* @param servletPath
* @return
*/
public String getTicket(String servletPath){
return this.ticketFunction.apply(servletPath);
}
/**
* 获取凭证 时间戳
* @param ticket
* @return
*/
abstract Long ticketTimeStamp(String ticket);
/**
* 设置本次请求时间
* @param ticket
*/
abstract void putTicket(String ticket);
/**
* 移除凭证
* @param ticket
*/
abstract void removeTicket(String ticket);
}

View File

@ -1,10 +1,9 @@
package net.lab1024.smartadmin.service.module.support.repeatsubmit;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import net.lab1024.smartadmin.service.common.code.UserErrorCode;
import net.lab1024.smartadmin.service.common.domain.ResponseDTO;
import net.lab1024.smartadmin.service.module.support.repeatsubmit.annoation.RepeatSubmit;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@ -12,10 +11,7 @@ import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
/**
* [ ]
@ -26,23 +22,16 @@ import java.util.function.Function;
@Aspect
public class RepeatSubmitAspect {
/**
* 限制缓存最大数量 超过后先放入的会自动移除
* 默认缓存时间
*/
private static Cache<Object, Object> cache = Caffeine.newBuilder()
.maximumSize(5000)
.expireAfterWrite(RepeatSubmit.MAX_INTERVAL, TimeUnit.MILLISECONDS).build();
private Function<HttpServletRequest, RepeatSubmitTicket> userFunction;
private AbstractRepeatSubmitTicket repeatSubmitTicket;
/**
* 获取用户信息
*rep
* @param userFunction
* 获取凭证信息
* rep
*
* @param repeatSubmitTicket
*/
public RepeatSubmitAspect(Function<HttpServletRequest, RepeatSubmitTicket> userFunction) {
this.userFunction = userFunction;
public RepeatSubmitAspect(AbstractRepeatSubmitTicket repeatSubmitTicket) {
this.repeatSubmitTicket = repeatSubmitTicket;
}
/**
@ -54,29 +43,30 @@ public class RepeatSubmitAspect {
*/
@Around("@annotation(net.lab1024.smartadmin.service.module.support.repeatsubmit.annoation.RepeatSubmit)")
public Object around(ProceedingJoinPoint point) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
RepeatSubmitTicket user = this.userFunction.apply(request);
if (user == null) {
return point.proceed();
}
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
String servletPath = attributes.getRequest().getServletPath();
String key = user.getUserId() + "_" + servletPath;
Object value = cache.getIfPresent(key);
if (value != null) {
String ticket = this.repeatSubmitTicket.getTicket(servletPath);
if (StringUtils.isEmpty(ticket)) {
return point.proceed();
}
Long timeStamp = this.repeatSubmitTicket.ticketTimeStamp(ticket);
if (timeStamp != null) {
Method method = ((MethodSignature) point.getSignature()).getMethod();
RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
int interval = Math.min(annotation.value(), RepeatSubmit.MAX_INTERVAL);
if (System.currentTimeMillis() < (long) value + interval) {
if (System.currentTimeMillis() < (long) timeStamp + interval) {
// 提交频繁
return ResponseDTO.error(UserErrorCode.REPEAT_SUBMIT);
}
}
cache.put(key, System.currentTimeMillis());
Object obj = point.proceed();
cache.put(key, System.currentTimeMillis());
Object obj = null;
try {
obj = point.proceed();
this.repeatSubmitTicket.putTicket(ticket);
}catch (Throwable throwable){
this.repeatSubmitTicket.removeTicket(ticket);
}
return obj;
}

View File

@ -0,0 +1,47 @@
package net.lab1024.smartadmin.service.module.support.repeatsubmit;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import net.lab1024.smartadmin.service.module.support.repeatsubmit.annoation.RepeatSubmit;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
/**
* [ ]
*
* @author yandanyang
* @date 2021/10/9 19:10
*/
public class RepeatSubmitCaffeineTicket extends AbstractRepeatSubmitTicket {
/**
* 限制缓存最大数量 超过后先放入的会自动移除
* 默认缓存时间
*/
private static Cache<String, Long> cache = Caffeine.newBuilder()
.maximumSize(5000)
.expireAfterWrite(RepeatSubmit.MAX_INTERVAL, TimeUnit.MILLISECONDS).build();
public RepeatSubmitCaffeineTicket(Function<String, String> ticketFunction) {
super(ticketFunction);
}
@Override
public Long ticketTimeStamp(String ticket) {
return cache.getIfPresent(ticket);
}
@Override
public void putTicket(String ticket) {
cache.put(ticket, System.currentTimeMillis());
}
@Override
void removeTicket(String ticket) {
cache.invalidate(ticket);
}
}

View File

@ -0,0 +1,45 @@
package net.lab1024.smartadmin.service.module.support.repeatsubmit;
import net.lab1024.smartadmin.service.module.support.repeatsubmit.annoation.RepeatSubmit;
import org.springframework.data.redis.core.ValueOperations;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
/**
* [ ]
*
* @author yandanyang
* @date 2021/10/9 19:10
*/
public class RepeatSubmitRedisTicket extends AbstractRepeatSubmitTicket {
private ValueOperations<String, String> redisValueOperations;
public RepeatSubmitRedisTicket(ValueOperations<String, String> redisValueOperations,
Function<String, String> ticketFunction) {
super(ticketFunction);
this.redisValueOperations = redisValueOperations;
}
@Override
public Long ticketTimeStamp(String ticket) {
Long timeStamp = System.currentTimeMillis();
boolean setFlag = redisValueOperations.setIfAbsent(ticket, String.valueOf(timeStamp), RepeatSubmit.MAX_INTERVAL, TimeUnit.MILLISECONDS);
if(!setFlag){
timeStamp = Long.valueOf(redisValueOperations.get(ticket));
}
return timeStamp;
}
@Override
public void putTicket(String ticket) {
redisValueOperations.getOperations().delete(ticket);
this.ticketTimeStamp(ticket);
}
@Override
void removeTicket(String ticket) {
redisValueOperations.getOperations().delete(ticket);
}
}

View File

@ -1,29 +0,0 @@
package net.lab1024.smartadmin.service.module.support.repeatsubmit;
import lombok.Data;
/**
*
* 重复提交的ticket
*
* @author zhuoda
*/
@Data
public class RepeatSubmitTicket {
/**
* 用户id
*/
private Long userId;
/**
* 用户名
*/
private String userName;
/**
* 扩展信息
*/
private String extData;
}

View File

@ -22,9 +22,9 @@ spring.jackson.time-zone=GMT+8
spring.jackson.serialization.write-dates-as-timestamps=false
######################### database #########################
spring.datasource.url=jdbc:p6spy:mysql://127.0.0.1:3306/smart_admin_v2?autoReconnect=true&useServerPreparedStmts=false&rewriteBatchedStatements=true&characterEncoding=UTF-8&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai
spring.datasource.url=jdbc:p6spy:mysql://115.29.150.222:11024/smart_admin_v2?autoReconnect=true&useServerPreparedStmts=false&rewriteBatchedStatements=true&characterEncoding=UTF-8&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.password=11024Lab
spring.datasource.initial-size=2
spring.datasource.min-idle=1
spring.datasource.max-active=10
@ -40,14 +40,14 @@ spring.datasource.druid.service.scanner=net.lab1024.smartadmin.module..*Service.
######################### redis #######################################
spring.redis.database=1
spring.redis.host=127.0.0.1
spring.redis.host=115.29.150.222
spring.redis.lettuce.pool.max-active=100
spring.redis.lettuce.pool.min-idle=5
spring.redis.lettuce.pool.max-idle=10
spring.redis.lettuce.pool.max-wait=30000ms
spring.redis.port=1234
spring.redis.port=21024
spring.redis.timeout=10000ms
spring.redis.password=root
spring.redis.password=21024Lab
######################### swagger #########################
swagger.apiGroupName=smartAdmin