mirror of
https://gitee.com/lab1024/smart-admin.git
synced 2025-10-08 21:26:40 +08:00
RepeatSubmit Ticket
This commit is contained in:
parent
51bb1a42f2
commit
daa7f47d23
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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> {
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user