规范了代码,修复了closeTask没法正常工作的bug,增加了限流控制(未测试)
parent
16200365d9
commit
1afafa8818
10
pom.xml
10
pom.xml
|
@ -39,6 +39,10 @@
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-validation</artifactId>
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-aop</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
@ -71,6 +75,7 @@
|
||||||
<artifactId>spring-security-test</artifactId>
|
<artifactId>spring-security-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.baomidou</groupId>
|
<groupId>com.baomidou</groupId>
|
||||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||||
|
@ -117,6 +122,11 @@
|
||||||
<artifactId>poi-ooxml</artifactId>
|
<artifactId>poi-ooxml</artifactId>
|
||||||
<version>5.2.2</version>
|
<version>5.2.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
<version>31.1-jre</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
@ -2,6 +2,7 @@ package cn.edu.hfut.rmdjzz.projectmanagement.advice;
|
||||||
|
|
||||||
import cn.edu.hfut.rmdjzz.projectmanagement.exception.BadRequestException;
|
import cn.edu.hfut.rmdjzz.projectmanagement.exception.BadRequestException;
|
||||||
import cn.edu.hfut.rmdjzz.projectmanagement.exception.ForbiddenException;
|
import cn.edu.hfut.rmdjzz.projectmanagement.exception.ForbiddenException;
|
||||||
|
import cn.edu.hfut.rmdjzz.projectmanagement.exception.TooManyRequestException;
|
||||||
import cn.edu.hfut.rmdjzz.projectmanagement.exception.UnauthorizedException;
|
import cn.edu.hfut.rmdjzz.projectmanagement.exception.UnauthorizedException;
|
||||||
import cn.edu.hfut.rmdjzz.projectmanagement.utils.http.ResponseMap;
|
import cn.edu.hfut.rmdjzz.projectmanagement.utils.http.ResponseMap;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
@ -51,4 +52,10 @@ public class ExceptionHandlerAdvice {
|
||||||
.collect(Collectors.joining(","))
|
.collect(Collectors.joining(","))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(TooManyRequestException.class)
|
||||||
|
@ResponseStatus(HttpStatus.TOO_MANY_REQUESTS)
|
||||||
|
public ResponseMap handleTooManyRequestException(TooManyRequestException e) {
|
||||||
|
return ResponseMap.of(HttpStatus.TOO_MANY_REQUESTS.value(), e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
package cn.edu.hfut.rmdjzz.projectmanagement.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 佘语殊
|
||||||
|
* @since 2022/7/11 16:57
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Target({ElementType.METHOD})
|
||||||
|
public @interface RateLimit {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流量控制令牌桶标识符
|
||||||
|
*/
|
||||||
|
String key() default "";
|
||||||
|
|
||||||
|
int permitsPerSecond();
|
||||||
|
|
||||||
|
long timeout() default 0;
|
||||||
|
|
||||||
|
TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
|
||||||
|
|
||||||
|
long maxBurstSeconds() default 1;
|
||||||
|
|
||||||
|
String msg() default "系统繁忙,请稍后再试";
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package cn.edu.hfut.rmdjzz.projectmanagement.aop;
|
||||||
|
|
||||||
|
import cn.edu.hfut.rmdjzz.projectmanagement.annotation.RateLimit;
|
||||||
|
import cn.edu.hfut.rmdjzz.projectmanagement.exception.TooManyRequestException;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.common.util.concurrent.RateLimiter;
|
||||||
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.Around;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.aspectj.lang.reflect.MethodSignature;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 佘语殊
|
||||||
|
* @since 2022/7/11 17:23
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("UnstableApiUsage")
|
||||||
|
@Aspect
|
||||||
|
@Component
|
||||||
|
public class RateLimitAOP {
|
||||||
|
private final Map<String, RateLimiter> rateLimitMap = Maps.newConcurrentMap();
|
||||||
|
|
||||||
|
@Around("@annotation(cn.edu.hfut.rmdjzz.projectmanagement.annotation.RateLimit)")
|
||||||
|
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||||
|
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||||
|
Method method = signature.getMethod();
|
||||||
|
RateLimit limit = method.getAnnotation(RateLimit.class);
|
||||||
|
if (limit == null) {
|
||||||
|
return joinPoint.proceed();
|
||||||
|
}
|
||||||
|
|
||||||
|
String key = limit.key();
|
||||||
|
RateLimiter limiter = rateLimitMap.get(key);
|
||||||
|
if (limiter == null) {
|
||||||
|
limiter = RateLimiter.create(limit.permitsPerSecond());
|
||||||
|
Class<? extends RateLimiter> clazz = limiter.getClass();
|
||||||
|
//TODO: DEBUG TEST
|
||||||
|
Field burstSecondsField = clazz.getDeclaredField("maxBurstSeconds");
|
||||||
|
burstSecondsField.setAccessible(true);
|
||||||
|
burstSecondsField.set(limiter, limit.maxBurstSeconds());
|
||||||
|
rateLimitMap.put(key, limiter);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!limiter.tryAcquire(limit.timeout(), limit.timeUnit())) {
|
||||||
|
throw new TooManyRequestException(limit.msg());
|
||||||
|
}
|
||||||
|
return joinPoint.proceed();
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,7 +29,7 @@ public class AnnouncementController {
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public ResponseList<AnnouncementDTO> getAnnouncementList(@RequestHeader("Token") String token, @PathVariable Integer projectId) {
|
public ResponseList<AnnouncementDTO> getAnnouncementList(@RequestHeader(TokenUtils.HEADER_TOKEN) String token, @PathVariable Integer projectId) {
|
||||||
if (projectGroupService.getProjectAccessLevel(token, projectId) == 0) {
|
if (projectGroupService.getProjectAccessLevel(token, projectId) == 0) {
|
||||||
throw new ForbiddenException(IProjectGroupService.UNABLE_TO_ACCESS_PROJECT);
|
throw new ForbiddenException(IProjectGroupService.UNABLE_TO_ACCESS_PROJECT);
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ public class AnnouncementController {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@GetMapping("/{announcementId}")
|
@GetMapping("/{announcementId}")
|
||||||
public ResponseMap getAnnouncementById(
|
public ResponseMap getAnnouncementById(
|
||||||
@RequestHeader("Token") String token,
|
@RequestHeader(TokenUtils.HEADER_TOKEN) String token,
|
||||||
@PathVariable Integer projectId,
|
@PathVariable Integer projectId,
|
||||||
@PathVariable Long announcementId
|
@PathVariable Long announcementId
|
||||||
) {
|
) {
|
||||||
|
@ -52,7 +52,7 @@ public class AnnouncementController {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public ResponseMap createAnnouncement(
|
public ResponseMap createAnnouncement(
|
||||||
@RequestHeader("Token") String token,
|
@RequestHeader(TokenUtils.HEADER_TOKEN) String token,
|
||||||
@PathVariable Integer projectId,
|
@PathVariable Integer projectId,
|
||||||
@RequestBody Announcement announcement
|
@RequestBody Announcement announcement
|
||||||
) {
|
) {
|
||||||
|
@ -73,7 +73,7 @@ public class AnnouncementController {
|
||||||
/*@SneakyThrows
|
/*@SneakyThrows
|
||||||
@PutMapping("/{announcementId}")
|
@PutMapping("/{announcementId}")
|
||||||
public ResponseMap modifyAnnouncement(
|
public ResponseMap modifyAnnouncement(
|
||||||
@RequestHeader("Token") String token,
|
@RequestHeader(TokenUtils.HEADER_TOKEN) String token,
|
||||||
@PathVariable Integer projectId,
|
@PathVariable Integer projectId,
|
||||||
@PathVariable Long announcementId,
|
@PathVariable Long announcementId,
|
||||||
@RequestBody Announcement announcement
|
@RequestBody Announcement announcement
|
||||||
|
@ -88,7 +88,7 @@ public class AnnouncementController {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@DeleteMapping("/{announcementId}")
|
@DeleteMapping("/{announcementId}")
|
||||||
public ResponseMap deleteAnnouncement(
|
public ResponseMap deleteAnnouncement(
|
||||||
@RequestHeader("Token") String token,
|
@RequestHeader(TokenUtils.HEADER_TOKEN) String token,
|
||||||
@PathVariable Integer projectId,
|
@PathVariable Integer projectId,
|
||||||
@PathVariable Long announcementId
|
@PathVariable Long announcementId
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import cn.edu.hfut.rmdjzz.projectmanagement.entity.dto.ProjectDTO;
|
||||||
import cn.edu.hfut.rmdjzz.projectmanagement.exception.BadRequestException;
|
import cn.edu.hfut.rmdjzz.projectmanagement.exception.BadRequestException;
|
||||||
import cn.edu.hfut.rmdjzz.projectmanagement.service.IProjectGroupService;
|
import cn.edu.hfut.rmdjzz.projectmanagement.service.IProjectGroupService;
|
||||||
import cn.edu.hfut.rmdjzz.projectmanagement.service.IProjectService;
|
import cn.edu.hfut.rmdjzz.projectmanagement.service.IProjectService;
|
||||||
|
import cn.edu.hfut.rmdjzz.projectmanagement.utils.TokenUtils;
|
||||||
import cn.edu.hfut.rmdjzz.projectmanagement.utils.http.RequestPage;
|
import cn.edu.hfut.rmdjzz.projectmanagement.utils.http.RequestPage;
|
||||||
import cn.edu.hfut.rmdjzz.projectmanagement.utils.http.ResponseList;
|
import cn.edu.hfut.rmdjzz.projectmanagement.utils.http.ResponseList;
|
||||||
import cn.edu.hfut.rmdjzz.projectmanagement.utils.http.ResponseMap;
|
import cn.edu.hfut.rmdjzz.projectmanagement.utils.http.ResponseMap;
|
||||||
|
@ -35,7 +36,7 @@ public class ProjectController {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public ResponseList<ProjectDTO> getProjectListOfStaff(
|
public ResponseList<ProjectDTO> getProjectListOfStaff(
|
||||||
@RequestHeader("Token") String token,
|
@RequestHeader(TokenUtils.HEADER_TOKEN) String token,
|
||||||
@Valid RequestPage page,
|
@Valid RequestPage page,
|
||||||
@Parameter(description = "参数列表见Project实体类,时间可以用xxxxStart与xxxxEnd来确定区间"
|
@Parameter(description = "参数列表见Project实体类,时间可以用xxxxStart与xxxxEnd来确定区间"
|
||||||
, required = true) @RequestParam("paramMap") Map<String, Object> paramMap
|
, required = true) @RequestParam("paramMap") Map<String, Object> paramMap
|
||||||
|
@ -47,7 +48,7 @@ public class ProjectController {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@GetMapping("/{projectId}")
|
@GetMapping("/{projectId}")
|
||||||
public ResponseMap getOneProjectBasicInfo(
|
public ResponseMap getOneProjectBasicInfo(
|
||||||
@RequestHeader("Token") String token,
|
@RequestHeader(TokenUtils.HEADER_TOKEN) String token,
|
||||||
@PathVariable("projectId") Integer projectId
|
@PathVariable("projectId") Integer projectId
|
||||||
) {
|
) {
|
||||||
if (projectGroupService.getProjectAccessLevel(token, projectId) == 0) {
|
if (projectGroupService.getProjectAccessLevel(token, projectId) == 0) {
|
||||||
|
@ -59,7 +60,7 @@ public class ProjectController {
|
||||||
@Operation(description = "根据Token获取该员工的Project数")
|
@Operation(description = "根据Token获取该员工的Project数")
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@GetMapping("/count")
|
@GetMapping("/count")
|
||||||
public ResponseMap getProjectNumOfStaff(@RequestHeader("Token") String token) {
|
public ResponseMap getProjectNumOfStaff(@RequestHeader(TokenUtils.HEADER_TOKEN) String token) {
|
||||||
return ResponseMap.ofSuccess()
|
return ResponseMap.ofSuccess()
|
||||||
.put("totalNum", projectService.countMyProjects(token));
|
.put("totalNum", projectService.countMyProjects(token));
|
||||||
}
|
}
|
||||||
|
@ -67,7 +68,7 @@ public class ProjectController {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@PostMapping("/complete")
|
@PostMapping("/complete")
|
||||||
public ResponseMap completeProject(
|
public ResponseMap completeProject(
|
||||||
@RequestHeader("Token") String token,
|
@RequestHeader(TokenUtils.HEADER_TOKEN) String token,
|
||||||
@Parameter(description = "只需要传projectId即可,例:{\"projectId\": 1}")
|
@Parameter(description = "只需要传projectId即可,例:{\"projectId\": 1}")
|
||||||
@RequestBody Map<String, Object> map
|
@RequestBody Map<String, Object> map
|
||||||
) {
|
) {
|
||||||
|
@ -78,7 +79,7 @@ public class ProjectController {
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public ResponseMap createProject(@RequestHeader("Token") String token, @RequestBody Project project) {
|
public ResponseMap createProject(@RequestHeader(TokenUtils.HEADER_TOKEN) String token, @RequestBody Project project) {
|
||||||
projectService.createProject(token, project);
|
projectService.createProject(token, project);
|
||||||
return ResponseMap.ofSuccess();
|
return ResponseMap.ofSuccess();
|
||||||
}
|
}
|
||||||
|
@ -86,7 +87,7 @@ public class ProjectController {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@PutMapping("/{projectId}")
|
@PutMapping("/{projectId}")
|
||||||
public ResponseMap updateProject(
|
public ResponseMap updateProject(
|
||||||
@RequestHeader("Token") String token,
|
@RequestHeader(TokenUtils.HEADER_TOKEN) String token,
|
||||||
@PathVariable Integer projectId,
|
@PathVariable Integer projectId,
|
||||||
@RequestBody Project project
|
@RequestBody Project project
|
||||||
) {
|
) {
|
||||||
|
@ -100,7 +101,7 @@ public class ProjectController {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@GetMapping("/{projectId}/stats")
|
@GetMapping("/{projectId}/stats")
|
||||||
public ResponseMap getProjectProcess(
|
public ResponseMap getProjectProcess(
|
||||||
@RequestHeader("Token") String token,
|
@RequestHeader(TokenUtils.HEADER_TOKEN) String token,
|
||||||
@PathVariable Integer projectId
|
@PathVariable Integer projectId
|
||||||
) {
|
) {
|
||||||
return ResponseMap.ofSuccess(projectService.getProjectProcess(token, projectId));
|
return ResponseMap.ofSuccess(projectService.getProjectProcess(token, projectId));
|
||||||
|
|
|
@ -43,7 +43,7 @@ public class ProjectGroupController {
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public ResponseList<ProjectGroupDTO> getGroupMembers(
|
public ResponseList<ProjectGroupDTO> getGroupMembers(
|
||||||
@PathVariable Integer projectId,
|
@PathVariable Integer projectId,
|
||||||
@RequestHeader("Token") String token,
|
@RequestHeader(TokenUtils.HEADER_TOKEN) String token,
|
||||||
RequestPage page
|
RequestPage page
|
||||||
) {
|
) {
|
||||||
if (projectGroupService.getProjectAccessLevel(token, projectId) == 0) {
|
if (projectGroupService.getProjectAccessLevel(token, projectId) == 0) {
|
||||||
|
@ -59,7 +59,7 @@ public class ProjectGroupController {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@GetMapping("/{staffId}")
|
@GetMapping("/{staffId}")
|
||||||
public ResponseMap getDesignatedStaffPosition(
|
public ResponseMap getDesignatedStaffPosition(
|
||||||
@RequestHeader("Token") String token,
|
@RequestHeader(TokenUtils.HEADER_TOKEN) String token,
|
||||||
@PathVariable Integer projectId,
|
@PathVariable Integer projectId,
|
||||||
@PathVariable Integer staffId
|
@PathVariable Integer staffId
|
||||||
) {
|
) {
|
||||||
|
@ -77,7 +77,7 @@ public class ProjectGroupController {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public ResponseMap addGroupMember(
|
public ResponseMap addGroupMember(
|
||||||
@RequestHeader("Token") String token,
|
@RequestHeader(TokenUtils.HEADER_TOKEN) String token,
|
||||||
@PathVariable Integer projectId,
|
@PathVariable Integer projectId,
|
||||||
@Parameter(description = "只传staffUsername和projectStaffPosition") @RequestBody GroupPositionVO groupPosition
|
@Parameter(description = "只传staffUsername和projectStaffPosition") @RequestBody GroupPositionVO groupPosition
|
||||||
) {
|
) {
|
||||||
|
@ -91,7 +91,7 @@ public class ProjectGroupController {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@PutMapping("/{staffId}")
|
@PutMapping("/{staffId}")
|
||||||
public ResponseMap modifyDesignatedStaffPosition(
|
public ResponseMap modifyDesignatedStaffPosition(
|
||||||
@RequestHeader("Token") String token,
|
@RequestHeader(TokenUtils.HEADER_TOKEN) String token,
|
||||||
@PathVariable Integer projectId,
|
@PathVariable Integer projectId,
|
||||||
@PathVariable Integer staffId,
|
@PathVariable Integer staffId,
|
||||||
@Parameter(description = "在body中只传projectStaffPosition") @RequestBody GroupPositionVO groupPosition
|
@Parameter(description = "在body中只传projectStaffPosition") @RequestBody GroupPositionVO groupPosition
|
||||||
|
@ -105,7 +105,7 @@ public class ProjectGroupController {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@GetMapping("/stats")
|
@GetMapping("/stats")
|
||||||
public ResponseMap getGroupPositionsStatistics(
|
public ResponseMap getGroupPositionsStatistics(
|
||||||
@RequestHeader("Token") String token,
|
@RequestHeader(TokenUtils.HEADER_TOKEN) String token,
|
||||||
@PathVariable Integer projectId
|
@PathVariable Integer projectId
|
||||||
) {
|
) {
|
||||||
return ResponseMap.ofSuccess(projectGroupService.collectStatsForGroupPositions(token, projectId));
|
return ResponseMap.ofSuccess(projectGroupService.collectStatsForGroupPositions(token, projectId));
|
||||||
|
@ -114,7 +114,7 @@ public class ProjectGroupController {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@GetMapping("/{staffId}/stats")
|
@GetMapping("/{staffId}/stats")
|
||||||
public ResponseList<StaffProcessDTO> getProjectProcessOfStaff(
|
public ResponseList<StaffProcessDTO> getProjectProcessOfStaff(
|
||||||
@RequestHeader("Token") String token,
|
@RequestHeader(TokenUtils.HEADER_TOKEN) String token,
|
||||||
@PathVariable Integer projectId,
|
@PathVariable Integer projectId,
|
||||||
@PathVariable Integer staffId
|
@PathVariable Integer staffId
|
||||||
) {
|
) {
|
||||||
|
@ -129,7 +129,7 @@ public class ProjectGroupController {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@PutMapping("/{staffId}/transfer")
|
@PutMapping("/{staffId}/transfer")
|
||||||
public ResponseMap transferStaffTasks(
|
public ResponseMap transferStaffTasks(
|
||||||
@RequestHeader("Token") String token,
|
@RequestHeader(TokenUtils.HEADER_TOKEN) String token,
|
||||||
@PathVariable Integer projectId,
|
@PathVariable Integer projectId,
|
||||||
@PathVariable Integer staffId,
|
@PathVariable Integer staffId,
|
||||||
@RequestBody Map<Long, Integer> transferMap
|
@RequestBody Map<Long, Integer> transferMap
|
||||||
|
|
|
@ -39,7 +39,7 @@ public class StaffController {
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@PostMapping("/logout")
|
@PostMapping("/logout")
|
||||||
public ResponseMap logout(@RequestHeader("Token") String token) {
|
public ResponseMap logout(@RequestHeader(TokenUtils.HEADER_TOKEN) String token) {
|
||||||
if (staffService.logout(token)) {
|
if (staffService.logout(token)) {
|
||||||
return ResponseMap.ofSuccess("登出成功");
|
return ResponseMap.ofSuccess("登出成功");
|
||||||
}
|
}
|
||||||
|
@ -49,8 +49,8 @@ public class StaffController {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@PostMapping(value = "/import")
|
@PostMapping(value = "/import")
|
||||||
public ResponseMap importStaffs(
|
public ResponseMap importStaffs(
|
||||||
@RequestHeader("Token") String token,
|
@RequestHeader(TokenUtils.HEADER_TOKEN) String token,
|
||||||
@RequestHeader("File-Digest") String digest,
|
@RequestHeader(FileUtils.HEADER_FILE_DIGEST) String digest,
|
||||||
@RequestParam("uploadFile") MultipartFile uploadFile
|
@RequestParam("uploadFile") MultipartFile uploadFile
|
||||||
) {
|
) {
|
||||||
if (null == uploadFile) {
|
if (null == uploadFile) {
|
||||||
|
@ -69,15 +69,15 @@ public class StaffController {
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@GetMapping("/import/template")
|
@GetMapping("/import/template")
|
||||||
public ResponseMap downloadTemplate(
|
public void downloadTemplate(
|
||||||
@RequestHeader("Token") String token,
|
@RequestHeader(TokenUtils.HEADER_TOKEN) String token,
|
||||||
HttpServletResponse response
|
HttpServletResponse response
|
||||||
) {
|
) {
|
||||||
if (TokenUtils.getStaffGlobalLevel(token) > 2) {
|
if (TokenUtils.getStaffGlobalLevel(token) > 2) {
|
||||||
throw new ForbiddenException(ForbiddenException.UNABLE_TO_OPERATE);
|
throw new ForbiddenException(ForbiddenException.UNABLE_TO_OPERATE);
|
||||||
}
|
}
|
||||||
if (FileUtils.downloadResource("static/账户导入模板.xlsx", response)) {
|
if (FileUtils.downloadResource("static/账户导入模板.xlsx", response)) {
|
||||||
return ResponseMap.ofSuccess();
|
return;
|
||||||
}
|
}
|
||||||
throw new BadRequestException(BadRequestException.OPERATE_FAILED);
|
throw new BadRequestException(BadRequestException.OPERATE_FAILED);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import cn.edu.hfut.rmdjzz.projectmanagement.entity.dto.TaskDTO;
|
||||||
import cn.edu.hfut.rmdjzz.projectmanagement.exception.BadRequestException;
|
import cn.edu.hfut.rmdjzz.projectmanagement.exception.BadRequestException;
|
||||||
import cn.edu.hfut.rmdjzz.projectmanagement.service.IProjectService;
|
import cn.edu.hfut.rmdjzz.projectmanagement.service.IProjectService;
|
||||||
import cn.edu.hfut.rmdjzz.projectmanagement.service.ITaskService;
|
import cn.edu.hfut.rmdjzz.projectmanagement.service.ITaskService;
|
||||||
|
import cn.edu.hfut.rmdjzz.projectmanagement.utils.TokenUtils;
|
||||||
import cn.edu.hfut.rmdjzz.projectmanagement.utils.http.ResponseList;
|
import cn.edu.hfut.rmdjzz.projectmanagement.utils.http.ResponseList;
|
||||||
import cn.edu.hfut.rmdjzz.projectmanagement.utils.http.ResponseMap;
|
import cn.edu.hfut.rmdjzz.projectmanagement.utils.http.ResponseMap;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
@ -27,7 +28,7 @@ public class TaskController {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@GetMapping("/{fatherId}/subtask")
|
@GetMapping("/{fatherId}/subtask")
|
||||||
public ResponseList<TaskDTO> getSubTaskList(
|
public ResponseList<TaskDTO> getSubTaskList(
|
||||||
@RequestHeader("Token") String token,
|
@RequestHeader(TokenUtils.HEADER_TOKEN) String token,
|
||||||
@PathVariable("projectId") Integer projectId,
|
@PathVariable("projectId") Integer projectId,
|
||||||
@PathVariable("fatherId") Long fatherId
|
@PathVariable("fatherId") Long fatherId
|
||||||
) {
|
) {
|
||||||
|
@ -37,7 +38,7 @@ public class TaskController {
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@GetMapping("/mine")
|
@GetMapping("/mine")
|
||||||
public ResponseList<Task> getMyTasks(@RequestHeader("Token") String token, @PathVariable("projectId") Integer projectId) {
|
public ResponseList<Task> getMyTasks(@RequestHeader(TokenUtils.HEADER_TOKEN) String token, @PathVariable("projectId") Integer projectId) {
|
||||||
List<Task> result = taskService.listMyTasks(token, projectId);
|
List<Task> result = taskService.listMyTasks(token, projectId);
|
||||||
return ResponseList.ofSuccess(result);
|
return ResponseList.ofSuccess(result);
|
||||||
}
|
}
|
||||||
|
@ -45,7 +46,7 @@ public class TaskController {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@GetMapping("/subtask/exist")
|
@GetMapping("/subtask/exist")
|
||||||
public ResponseMap existSubTask(
|
public ResponseMap existSubTask(
|
||||||
@RequestHeader("Token") String token,
|
@RequestHeader(TokenUtils.HEADER_TOKEN) String token,
|
||||||
@PathVariable("projectId") Integer projectId,
|
@PathVariable("projectId") Integer projectId,
|
||||||
@RequestParam("taskId") Long taskId
|
@RequestParam("taskId") Long taskId
|
||||||
) {
|
) {
|
||||||
|
@ -56,7 +57,7 @@ public class TaskController {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public ResponseMap createTask(
|
public ResponseMap createTask(
|
||||||
@RequestHeader("Token") String token,
|
@RequestHeader(TokenUtils.HEADER_TOKEN) String token,
|
||||||
@PathVariable("projectId") Integer projectId,
|
@PathVariable("projectId") Integer projectId,
|
||||||
@RequestBody Task task
|
@RequestBody Task task
|
||||||
) {
|
) {
|
||||||
|
@ -70,7 +71,7 @@ public class TaskController {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@PutMapping("/{taskId}")
|
@PutMapping("/{taskId}")
|
||||||
public ResponseMap modifyTask(
|
public ResponseMap modifyTask(
|
||||||
@RequestHeader("Token") String token,
|
@RequestHeader(TokenUtils.HEADER_TOKEN) String token,
|
||||||
@PathVariable("projectId") Integer projectId,
|
@PathVariable("projectId") Integer projectId,
|
||||||
@PathVariable("taskId") Long taskId,
|
@PathVariable("taskId") Long taskId,
|
||||||
@RequestBody Task task
|
@RequestBody Task task
|
||||||
|
@ -86,7 +87,7 @@ public class TaskController {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@DeleteMapping("/{taskId}")
|
@DeleteMapping("/{taskId}")
|
||||||
public ResponseMap deleteTaskAndSubTask(
|
public ResponseMap deleteTaskAndSubTask(
|
||||||
@RequestHeader("Token") String token,
|
@RequestHeader(TokenUtils.HEADER_TOKEN) String token,
|
||||||
@PathVariable("projectId") Integer projectId,
|
@PathVariable("projectId") Integer projectId,
|
||||||
@PathVariable("taskId") Long taskId
|
@PathVariable("taskId") Long taskId
|
||||||
) {
|
) {
|
||||||
|
@ -99,7 +100,7 @@ public class TaskController {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@GetMapping("/stats")
|
@GetMapping("/stats")
|
||||||
public ResponseMap getTaskTrend(
|
public ResponseMap getTaskTrend(
|
||||||
@RequestHeader("Token") String token,
|
@RequestHeader(TokenUtils.HEADER_TOKEN) String token,
|
||||||
@PathVariable Integer projectId
|
@PathVariable Integer projectId
|
||||||
) {
|
) {
|
||||||
if(!projectService.checkOpenStatus(projectId)) {
|
if(!projectService.checkOpenStatus(projectId)) {
|
||||||
|
|
|
@ -57,6 +57,7 @@ public class Task {
|
||||||
"demandSource:需求来源 (String), estimatedManHours:预估工时 (Integer), severity:严重程度 (String), recurrenceProbability:复现概率 (String)")
|
"demandSource:需求来源 (String), estimatedManHours:预估工时 (Integer), severity:严重程度 (String), recurrenceProbability:复现概率 (String)")
|
||||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||||
private Map<String, Object> attachedInfo;
|
private Map<String, Object> attachedInfo;
|
||||||
|
private Integer childrenCount;
|
||||||
@TableField("is_deleted")
|
@TableField("is_deleted")
|
||||||
@TableLogic
|
@TableLogic
|
||||||
private Boolean deleted;
|
private Boolean deleted;
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package cn.edu.hfut.rmdjzz.projectmanagement.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 佘语殊
|
||||||
|
* @since 2022/7/11 17:35
|
||||||
|
*/
|
||||||
|
public class TooManyRequestException extends Exception {
|
||||||
|
public TooManyRequestException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,7 +27,7 @@ public class TokenInterceptor implements HandlerInterceptor {
|
||||||
@Override
|
@Override
|
||||||
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws TokenException {
|
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws TokenException {
|
||||||
System.out.println(httpServletRequest.getRequestURL() + " " + httpServletRequest.getMethod());
|
System.out.println(httpServletRequest.getRequestURL() + " " + httpServletRequest.getMethod());
|
||||||
String token = httpServletRequest.getHeader("Token");
|
String token = httpServletRequest.getHeader(TokenUtils.HEADER_TOKEN);
|
||||||
if (null == token || "".equals(token.trim())) {
|
if (null == token || "".equals(token.trim())) {
|
||||||
throw new TokenException("缺少Token");
|
throw new TokenException("缺少Token");
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ public class TokenInterceptor implements HandlerInterceptor {
|
||||||
Objects.requireNonNull(TokenUtils.getDuration(token)), TimeUnit.SECONDS
|
Objects.requireNonNull(TokenUtils.getDuration(token)), TimeUnit.SECONDS
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
httpServletResponse.setHeader("Token", newToken);
|
httpServletResponse.setHeader(TokenUtils.HEADER_TOKEN, newToken);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ public interface ITaskService extends IService<Task> {
|
||||||
|
|
||||||
Task modifyTask(String token, Task task) throws BadRequestException, ForbiddenException;
|
Task modifyTask(String token, Task task) throws BadRequestException, ForbiddenException;
|
||||||
|
|
||||||
Map<String, List<TaskTrendDTO>> getProjectTaskTrend(String token, Integer projectId) throws BadRequestException, ForbiddenException;
|
Map<String, List<TaskTrendDTO>> getProjectTaskTrend(String token, Integer projectId) throws ForbiddenException;
|
||||||
|
|
||||||
Boolean transferStaffTasks(String token, Integer projectId, Integer transferredStaffId, Map<Long, Integer> transferMap) throws ForbiddenException, BadRequestException;
|
Boolean transferStaffTasks(String token, Integer projectId, Integer transferredStaffId, Map<Long, Integer> transferMap) throws ForbiddenException, BadRequestException;
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ public class StaffServiceImpl extends ServiceImpl<StaffMapper, Staff> implements
|
||||||
Objects.requireNonNull(TokenUtils.getDuration(token)), TimeUnit.SECONDS
|
Objects.requireNonNull(TokenUtils.getDuration(token)), TimeUnit.SECONDS
|
||||||
);
|
);
|
||||||
return new MapBuilder()
|
return new MapBuilder()
|
||||||
.put("Token", token)
|
.put(TokenUtils.HEADER_TOKEN, token)
|
||||||
.putAll(BeanUtils.beanToMap(staff, false))
|
.putAll(BeanUtils.beanToMap(staff, false))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
|
@ -246,13 +246,14 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements IT
|
||||||
throw new BadRequestException("还有子工作尚未完成");
|
throw new BadRequestException("还有子工作尚未完成");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
boolean closed = false;
|
||||||
if (typeChangeValue != 0) {
|
if (typeChangeValue != 0) {
|
||||||
task.setTaskClosedTime(LocalDateTime.now());
|
task.setTaskClosedTime(LocalDateTime.now());
|
||||||
}
|
}
|
||||||
if (typeChangeValue == 2) {
|
if (typeChangeValue == 2) {
|
||||||
closeTaskAndSubTask(token, task.getTaskProjectId(), task.getTaskId());
|
closed = closeTaskAndSubTask(token, task.getTaskProjectId(), task.getTaskId());
|
||||||
}
|
}
|
||||||
if (baseMapper.update(task, Wrappers.<Task>lambdaQuery().eq(Task::getTaskId, task.getTaskId())) == 0) {
|
if (!closed && baseMapper.update(task, Wrappers.<Task>lambdaQuery().eq(Task::getTaskId, task.getTaskId())) == 0) {
|
||||||
throw new BadRequestException(BadRequestException.OPERATE_FAILED);
|
throw new BadRequestException(BadRequestException.OPERATE_FAILED);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -263,7 +264,7 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements IT
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, List<TaskTrendDTO>> getProjectTaskTrend(String token, Integer projectId) throws BadRequestException, ForbiddenException {
|
public Map<String, List<TaskTrendDTO>> getProjectTaskTrend(String token, Integer projectId) throws ForbiddenException {
|
||||||
if (projectGroupService.getProjectAccessLevel(token, projectId) == 0) {
|
if (projectGroupService.getProjectAccessLevel(token, projectId) == 0) {
|
||||||
throw new ForbiddenException(ForbiddenException.UNABLE_TO_OPERATE);
|
throw new ForbiddenException(ForbiddenException.UNABLE_TO_OPERATE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,25 +6,36 @@ import org.springframework.util.MimeTypeUtils;
|
||||||
|
|
||||||
import javax.servlet.ServletOutputStream;
|
import javax.servlet.ServletOutputStream;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.*;
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author 佘语殊
|
* @author 佘语殊
|
||||||
* @since 2022/7/11 9:32
|
* @since 2022/7/11 9:32
|
||||||
*/
|
*/
|
||||||
public class FileUtils {
|
public class FileUtils {
|
||||||
|
|
||||||
|
public static final String HEADER_FILE_DIGEST = "File-Digest";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直接在响应体中用八位字节流输出文件内容,并在响应头中加入File-Digest作为md5文件摘要校验码
|
||||||
|
* <p>
|
||||||
|
* 在Controller层调用时需要返回void
|
||||||
|
*/
|
||||||
public static Boolean downloadResource(String resourceName, HttpServletResponse response) throws IOException {
|
public static Boolean downloadResource(String resourceName, HttpServletResponse response) throws IOException {
|
||||||
@Cleanup InputStream is = FileUtils.class.getResourceAsStream(resourceName);
|
@Cleanup InputStream is = FileUtils.class.getClassLoader().getResourceAsStream(resourceName);
|
||||||
if (is == null) {
|
BufferedInputStream bis = new BufferedInputStream(is);
|
||||||
throw new FileNotFoundException("该文件不存在");
|
bis.mark(bis.available() + 1);
|
||||||
}
|
|
||||||
@Cleanup BufferedInputStream bis = new BufferedInputStream(is);
|
|
||||||
response.setContentType(MimeTypeUtils.APPLICATION_OCTET_STREAM_VALUE);
|
response.setContentType(MimeTypeUtils.APPLICATION_OCTET_STREAM_VALUE);
|
||||||
response.setCharacterEncoding("UTF-8");
|
response.setCharacterEncoding("UTF-8");
|
||||||
ServletOutputStream out = response.getOutputStream();
|
ServletOutputStream out = response.getOutputStream();
|
||||||
response.addHeader("File-Digest", DigestUtils.md5DigestAsHex(bis));
|
response.addHeader(HEADER_FILE_DIGEST, DigestUtils.md5DigestAsHex(bis));
|
||||||
|
bis.reset();
|
||||||
@Cleanup BufferedOutputStream bos = new BufferedOutputStream(out);
|
@Cleanup BufferedOutputStream bos = new BufferedOutputStream(out);
|
||||||
bis.transferTo(bos);
|
bis.transferTo(bos);
|
||||||
|
bis.close();
|
||||||
bos.flush();
|
bos.flush();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import java.util.Date;
|
||||||
public final class TokenUtils {
|
public final class TokenUtils {
|
||||||
private final static String PV_KEY = "SignedByRMDJZZ";
|
private final static String PV_KEY = "SignedByRMDJZZ";
|
||||||
|
|
||||||
|
public final static String HEADER_TOKEN = "Token";
|
||||||
private final static String STAFF_USERNAME = "staffUsername";
|
private final static String STAFF_USERNAME = "staffUsername";
|
||||||
private final static String STAFF_ID = "staffId";
|
private final static String STAFF_ID = "staffId";
|
||||||
private final static String STAFF_GLOBAL_LEVEL = "staffGlobalLevel";
|
private final static String STAFF_GLOBAL_LEVEL = "staffGlobalLevel";
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue