diff --git a/pom.xml b/pom.xml index 4dfca6b..52d2888 100644 --- a/pom.xml +++ b/pom.xml @@ -39,6 +39,10 @@ org.springframework.boot spring-boot-starter-validation + + org.springframework.boot + spring-boot-starter-aop + org.springframework.boot @@ -71,6 +75,7 @@ spring-security-test test + com.baomidou mybatis-plus-boot-starter @@ -117,6 +122,11 @@ poi-ooxml 5.2.2 + + com.google.guava + guava + 31.1-jre + diff --git a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/advice/ExceptionHandlerAdvice.java b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/advice/ExceptionHandlerAdvice.java index c4ca39e..2ce4e01 100644 --- a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/advice/ExceptionHandlerAdvice.java +++ b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/advice/ExceptionHandlerAdvice.java @@ -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.ForbiddenException; +import cn.edu.hfut.rmdjzz.projectmanagement.exception.TooManyRequestException; import cn.edu.hfut.rmdjzz.projectmanagement.exception.UnauthorizedException; import cn.edu.hfut.rmdjzz.projectmanagement.utils.http.ResponseMap; import lombok.extern.slf4j.Slf4j; @@ -51,4 +52,10 @@ public class ExceptionHandlerAdvice { .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()); + } } diff --git a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/annotation/RateLimit.java b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/annotation/RateLimit.java new file mode 100644 index 0000000..a971953 --- /dev/null +++ b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/annotation/RateLimit.java @@ -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 "系统繁忙,请稍后再试"; +} diff --git a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/aop/RateLimitAOP.java b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/aop/RateLimitAOP.java new file mode 100644 index 0000000..84f0470 --- /dev/null +++ b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/aop/RateLimitAOP.java @@ -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 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 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(); + } +} diff --git a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/controller/AnnouncementController.java b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/controller/AnnouncementController.java index 9954183..35b1137 100644 --- a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/controller/AnnouncementController.java +++ b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/controller/AnnouncementController.java @@ -29,7 +29,7 @@ public class AnnouncementController { @SneakyThrows @GetMapping - public ResponseList getAnnouncementList(@RequestHeader("Token") String token, @PathVariable Integer projectId) { + public ResponseList getAnnouncementList(@RequestHeader(TokenUtils.HEADER_TOKEN) String token, @PathVariable Integer projectId) { if (projectGroupService.getProjectAccessLevel(token, projectId) == 0) { throw new ForbiddenException(IProjectGroupService.UNABLE_TO_ACCESS_PROJECT); } @@ -39,7 +39,7 @@ public class AnnouncementController { @SneakyThrows @GetMapping("/{announcementId}") public ResponseMap getAnnouncementById( - @RequestHeader("Token") String token, + @RequestHeader(TokenUtils.HEADER_TOKEN) String token, @PathVariable Integer projectId, @PathVariable Long announcementId ) { @@ -52,7 +52,7 @@ public class AnnouncementController { @SneakyThrows @PostMapping public ResponseMap createAnnouncement( - @RequestHeader("Token") String token, + @RequestHeader(TokenUtils.HEADER_TOKEN) String token, @PathVariable Integer projectId, @RequestBody Announcement announcement ) { @@ -73,7 +73,7 @@ public class AnnouncementController { /*@SneakyThrows @PutMapping("/{announcementId}") public ResponseMap modifyAnnouncement( - @RequestHeader("Token") String token, + @RequestHeader(TokenUtils.HEADER_TOKEN) String token, @PathVariable Integer projectId, @PathVariable Long announcementId, @RequestBody Announcement announcement @@ -88,7 +88,7 @@ public class AnnouncementController { @SneakyThrows @DeleteMapping("/{announcementId}") public ResponseMap deleteAnnouncement( - @RequestHeader("Token") String token, + @RequestHeader(TokenUtils.HEADER_TOKEN) String token, @PathVariable Integer projectId, @PathVariable Long announcementId ) { diff --git a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/controller/ProjectController.java b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/controller/ProjectController.java index 8a550d9..02a2c19 100644 --- a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/controller/ProjectController.java +++ b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/controller/ProjectController.java @@ -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.service.IProjectGroupService; 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.ResponseList; import cn.edu.hfut.rmdjzz.projectmanagement.utils.http.ResponseMap; @@ -35,7 +36,7 @@ public class ProjectController { @SneakyThrows @GetMapping public ResponseList getProjectListOfStaff( - @RequestHeader("Token") String token, + @RequestHeader(TokenUtils.HEADER_TOKEN) String token, @Valid RequestPage page, @Parameter(description = "参数列表见Project实体类,时间可以用xxxxStart与xxxxEnd来确定区间" , required = true) @RequestParam("paramMap") Map paramMap @@ -47,7 +48,7 @@ public class ProjectController { @SneakyThrows @GetMapping("/{projectId}") public ResponseMap getOneProjectBasicInfo( - @RequestHeader("Token") String token, + @RequestHeader(TokenUtils.HEADER_TOKEN) String token, @PathVariable("projectId") Integer projectId ) { if (projectGroupService.getProjectAccessLevel(token, projectId) == 0) { @@ -59,7 +60,7 @@ public class ProjectController { @Operation(description = "根据Token获取该员工的Project数") @SneakyThrows @GetMapping("/count") - public ResponseMap getProjectNumOfStaff(@RequestHeader("Token") String token) { + public ResponseMap getProjectNumOfStaff(@RequestHeader(TokenUtils.HEADER_TOKEN) String token) { return ResponseMap.ofSuccess() .put("totalNum", projectService.countMyProjects(token)); } @@ -67,7 +68,7 @@ public class ProjectController { @SneakyThrows @PostMapping("/complete") public ResponseMap completeProject( - @RequestHeader("Token") String token, + @RequestHeader(TokenUtils.HEADER_TOKEN) String token, @Parameter(description = "只需要传projectId即可,例:{\"projectId\": 1}") @RequestBody Map map ) { @@ -78,7 +79,7 @@ public class ProjectController { @SneakyThrows @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); return ResponseMap.ofSuccess(); } @@ -86,7 +87,7 @@ public class ProjectController { @SneakyThrows @PutMapping("/{projectId}") public ResponseMap updateProject( - @RequestHeader("Token") String token, + @RequestHeader(TokenUtils.HEADER_TOKEN) String token, @PathVariable Integer projectId, @RequestBody Project project ) { @@ -100,7 +101,7 @@ public class ProjectController { @SneakyThrows @GetMapping("/{projectId}/stats") public ResponseMap getProjectProcess( - @RequestHeader("Token") String token, + @RequestHeader(TokenUtils.HEADER_TOKEN) String token, @PathVariable Integer projectId ) { return ResponseMap.ofSuccess(projectService.getProjectProcess(token, projectId)); diff --git a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/controller/ProjectGroupController.java b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/controller/ProjectGroupController.java index 3bf7fcd..ca553a8 100644 --- a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/controller/ProjectGroupController.java +++ b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/controller/ProjectGroupController.java @@ -43,7 +43,7 @@ public class ProjectGroupController { @GetMapping public ResponseList getGroupMembers( @PathVariable Integer projectId, - @RequestHeader("Token") String token, + @RequestHeader(TokenUtils.HEADER_TOKEN) String token, RequestPage page ) { if (projectGroupService.getProjectAccessLevel(token, projectId) == 0) { @@ -59,7 +59,7 @@ public class ProjectGroupController { @SneakyThrows @GetMapping("/{staffId}") public ResponseMap getDesignatedStaffPosition( - @RequestHeader("Token") String token, + @RequestHeader(TokenUtils.HEADER_TOKEN) String token, @PathVariable Integer projectId, @PathVariable Integer staffId ) { @@ -77,7 +77,7 @@ public class ProjectGroupController { @SneakyThrows @PostMapping public ResponseMap addGroupMember( - @RequestHeader("Token") String token, + @RequestHeader(TokenUtils.HEADER_TOKEN) String token, @PathVariable Integer projectId, @Parameter(description = "只传staffUsername和projectStaffPosition") @RequestBody GroupPositionVO groupPosition ) { @@ -91,7 +91,7 @@ public class ProjectGroupController { @SneakyThrows @PutMapping("/{staffId}") public ResponseMap modifyDesignatedStaffPosition( - @RequestHeader("Token") String token, + @RequestHeader(TokenUtils.HEADER_TOKEN) String token, @PathVariable Integer projectId, @PathVariable Integer staffId, @Parameter(description = "在body中只传projectStaffPosition") @RequestBody GroupPositionVO groupPosition @@ -105,7 +105,7 @@ public class ProjectGroupController { @SneakyThrows @GetMapping("/stats") public ResponseMap getGroupPositionsStatistics( - @RequestHeader("Token") String token, + @RequestHeader(TokenUtils.HEADER_TOKEN) String token, @PathVariable Integer projectId ) { return ResponseMap.ofSuccess(projectGroupService.collectStatsForGroupPositions(token, projectId)); @@ -114,7 +114,7 @@ public class ProjectGroupController { @SneakyThrows @GetMapping("/{staffId}/stats") public ResponseList getProjectProcessOfStaff( - @RequestHeader("Token") String token, + @RequestHeader(TokenUtils.HEADER_TOKEN) String token, @PathVariable Integer projectId, @PathVariable Integer staffId ) { @@ -129,7 +129,7 @@ public class ProjectGroupController { @SneakyThrows @PutMapping("/{staffId}/transfer") public ResponseMap transferStaffTasks( - @RequestHeader("Token") String token, + @RequestHeader(TokenUtils.HEADER_TOKEN) String token, @PathVariable Integer projectId, @PathVariable Integer staffId, @RequestBody Map transferMap diff --git a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/controller/StaffController.java b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/controller/StaffController.java index eec249c..0d3ceb6 100644 --- a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/controller/StaffController.java +++ b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/controller/StaffController.java @@ -39,7 +39,7 @@ public class StaffController { @SneakyThrows @PostMapping("/logout") - public ResponseMap logout(@RequestHeader("Token") String token) { + public ResponseMap logout(@RequestHeader(TokenUtils.HEADER_TOKEN) String token) { if (staffService.logout(token)) { return ResponseMap.ofSuccess("登出成功"); } @@ -49,8 +49,8 @@ public class StaffController { @SneakyThrows @PostMapping(value = "/import") public ResponseMap importStaffs( - @RequestHeader("Token") String token, - @RequestHeader("File-Digest") String digest, + @RequestHeader(TokenUtils.HEADER_TOKEN) String token, + @RequestHeader(FileUtils.HEADER_FILE_DIGEST) String digest, @RequestParam("uploadFile") MultipartFile uploadFile ) { if (null == uploadFile) { @@ -69,15 +69,15 @@ public class StaffController { @SneakyThrows @GetMapping("/import/template") - public ResponseMap downloadTemplate( - @RequestHeader("Token") String token, + public void downloadTemplate( + @RequestHeader(TokenUtils.HEADER_TOKEN) String token, HttpServletResponse response ) { if (TokenUtils.getStaffGlobalLevel(token) > 2) { throw new ForbiddenException(ForbiddenException.UNABLE_TO_OPERATE); } if (FileUtils.downloadResource("static/账户导入模板.xlsx", response)) { - return ResponseMap.ofSuccess(); + return; } throw new BadRequestException(BadRequestException.OPERATE_FAILED); } diff --git a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/controller/TaskController.java b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/controller/TaskController.java index a4adf14..3c3e718 100644 --- a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/controller/TaskController.java +++ b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/controller/TaskController.java @@ -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.service.IProjectService; 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.ResponseMap; import lombok.SneakyThrows; @@ -27,7 +28,7 @@ public class TaskController { @SneakyThrows @GetMapping("/{fatherId}/subtask") public ResponseList getSubTaskList( - @RequestHeader("Token") String token, + @RequestHeader(TokenUtils.HEADER_TOKEN) String token, @PathVariable("projectId") Integer projectId, @PathVariable("fatherId") Long fatherId ) { @@ -37,7 +38,7 @@ public class TaskController { @SneakyThrows @GetMapping("/mine") - public ResponseList getMyTasks(@RequestHeader("Token") String token, @PathVariable("projectId") Integer projectId) { + public ResponseList getMyTasks(@RequestHeader(TokenUtils.HEADER_TOKEN) String token, @PathVariable("projectId") Integer projectId) { List result = taskService.listMyTasks(token, projectId); return ResponseList.ofSuccess(result); } @@ -45,7 +46,7 @@ public class TaskController { @SneakyThrows @GetMapping("/subtask/exist") public ResponseMap existSubTask( - @RequestHeader("Token") String token, + @RequestHeader(TokenUtils.HEADER_TOKEN) String token, @PathVariable("projectId") Integer projectId, @RequestParam("taskId") Long taskId ) { @@ -56,7 +57,7 @@ public class TaskController { @SneakyThrows @PostMapping public ResponseMap createTask( - @RequestHeader("Token") String token, + @RequestHeader(TokenUtils.HEADER_TOKEN) String token, @PathVariable("projectId") Integer projectId, @RequestBody Task task ) { @@ -70,7 +71,7 @@ public class TaskController { @SneakyThrows @PutMapping("/{taskId}") public ResponseMap modifyTask( - @RequestHeader("Token") String token, + @RequestHeader(TokenUtils.HEADER_TOKEN) String token, @PathVariable("projectId") Integer projectId, @PathVariable("taskId") Long taskId, @RequestBody Task task @@ -86,7 +87,7 @@ public class TaskController { @SneakyThrows @DeleteMapping("/{taskId}") public ResponseMap deleteTaskAndSubTask( - @RequestHeader("Token") String token, + @RequestHeader(TokenUtils.HEADER_TOKEN) String token, @PathVariable("projectId") Integer projectId, @PathVariable("taskId") Long taskId ) { @@ -99,7 +100,7 @@ public class TaskController { @SneakyThrows @GetMapping("/stats") public ResponseMap getTaskTrend( - @RequestHeader("Token") String token, + @RequestHeader(TokenUtils.HEADER_TOKEN) String token, @PathVariable Integer projectId ) { if(!projectService.checkOpenStatus(projectId)) { diff --git a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/entity/Task.java b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/entity/Task.java index 3ea2b55..2f77fac 100644 --- a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/entity/Task.java +++ b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/entity/Task.java @@ -57,6 +57,7 @@ public class Task { "demandSource:需求来源 (String), estimatedManHours:预估工时 (Integer), severity:严重程度 (String), recurrenceProbability:复现概率 (String)") @TableField(typeHandler = JacksonTypeHandler.class) private Map attachedInfo; + private Integer childrenCount; @TableField("is_deleted") @TableLogic private Boolean deleted; diff --git a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/exception/TooManyRequestException.java b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/exception/TooManyRequestException.java new file mode 100644 index 0000000..8d8cefb --- /dev/null +++ b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/exception/TooManyRequestException.java @@ -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); + } +} diff --git a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/interceptor/TokenInterceptor.java b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/interceptor/TokenInterceptor.java index bc65bf2..294c5f1 100644 --- a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/interceptor/TokenInterceptor.java +++ b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/interceptor/TokenInterceptor.java @@ -27,7 +27,7 @@ public class TokenInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws TokenException { 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())) { throw new TokenException("缺少Token"); } @@ -50,7 +50,7 @@ public class TokenInterceptor implements HandlerInterceptor { Objects.requireNonNull(TokenUtils.getDuration(token)), TimeUnit.SECONDS ); } - httpServletResponse.setHeader("Token", newToken); + httpServletResponse.setHeader(TokenUtils.HEADER_TOKEN, newToken); return true; } } diff --git a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/service/ITaskService.java b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/service/ITaskService.java index c16e35f..d8702a4 100644 --- a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/service/ITaskService.java +++ b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/service/ITaskService.java @@ -45,7 +45,7 @@ public interface ITaskService extends IService { Task modifyTask(String token, Task task) throws BadRequestException, ForbiddenException; - Map> getProjectTaskTrend(String token, Integer projectId) throws BadRequestException, ForbiddenException; + Map> getProjectTaskTrend(String token, Integer projectId) throws ForbiddenException; Boolean transferStaffTasks(String token, Integer projectId, Integer transferredStaffId, Map transferMap) throws ForbiddenException, BadRequestException; diff --git a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/service/impl/StaffServiceImpl.java b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/service/impl/StaffServiceImpl.java index ba7be16..ae5ee78 100644 --- a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/service/impl/StaffServiceImpl.java +++ b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/service/impl/StaffServiceImpl.java @@ -62,7 +62,7 @@ public class StaffServiceImpl extends ServiceImpl implements Objects.requireNonNull(TokenUtils.getDuration(token)), TimeUnit.SECONDS ); return new MapBuilder() - .put("Token", token) + .put(TokenUtils.HEADER_TOKEN, token) .putAll(BeanUtils.beanToMap(staff, false)) .build(); } diff --git a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/service/impl/TaskServiceImpl.java b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/service/impl/TaskServiceImpl.java index e121674..6cdc9b7 100644 --- a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/service/impl/TaskServiceImpl.java +++ b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/service/impl/TaskServiceImpl.java @@ -246,13 +246,14 @@ public class TaskServiceImpl extends ServiceImpl implements IT throw new BadRequestException("还有子工作尚未完成"); } try { + boolean closed = false; if (typeChangeValue != 0) { task.setTaskClosedTime(LocalDateTime.now()); } if (typeChangeValue == 2) { - closeTaskAndSubTask(token, task.getTaskProjectId(), task.getTaskId()); + closed = closeTaskAndSubTask(token, task.getTaskProjectId(), task.getTaskId()); } - if (baseMapper.update(task, Wrappers.lambdaQuery().eq(Task::getTaskId, task.getTaskId())) == 0) { + if (!closed && baseMapper.update(task, Wrappers.lambdaQuery().eq(Task::getTaskId, task.getTaskId())) == 0) { throw new BadRequestException(BadRequestException.OPERATE_FAILED); } } catch (Exception e) { @@ -263,7 +264,7 @@ public class TaskServiceImpl extends ServiceImpl implements IT } @Override - public Map> getProjectTaskTrend(String token, Integer projectId) throws BadRequestException, ForbiddenException { + public Map> getProjectTaskTrend(String token, Integer projectId) throws ForbiddenException { if (projectGroupService.getProjectAccessLevel(token, projectId) == 0) { throw new ForbiddenException(ForbiddenException.UNABLE_TO_OPERATE); } diff --git a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/utils/FileUtils.java b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/utils/FileUtils.java index 6a5420b..4ea5e9f 100644 --- a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/utils/FileUtils.java +++ b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/utils/FileUtils.java @@ -6,25 +6,36 @@ import org.springframework.util.MimeTypeUtils; import javax.servlet.ServletOutputStream; 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 佘语殊 * @since 2022/7/11 9:32 */ public class FileUtils { + + public static final String HEADER_FILE_DIGEST = "File-Digest"; + + /** + * 直接在响应体中用八位字节流输出文件内容,并在响应头中加入File-Digest作为md5文件摘要校验码 + *

+ * 在Controller层调用时需要返回void + */ public static Boolean downloadResource(String resourceName, HttpServletResponse response) throws IOException { - @Cleanup InputStream is = FileUtils.class.getResourceAsStream(resourceName); - if (is == null) { - throw new FileNotFoundException("该文件不存在"); - } - @Cleanup BufferedInputStream bis = new BufferedInputStream(is); + @Cleanup InputStream is = FileUtils.class.getClassLoader().getResourceAsStream(resourceName); + BufferedInputStream bis = new BufferedInputStream(is); + bis.mark(bis.available() + 1); response.setContentType(MimeTypeUtils.APPLICATION_OCTET_STREAM_VALUE); response.setCharacterEncoding("UTF-8"); 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); bis.transferTo(bos); + bis.close(); bos.flush(); return true; } diff --git a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/utils/TokenUtils.java b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/utils/TokenUtils.java index c41330e..c2da352 100644 --- a/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/utils/TokenUtils.java +++ b/src/main/java/cn/edu/hfut/rmdjzz/projectmanagement/utils/TokenUtils.java @@ -19,6 +19,7 @@ import java.util.Date; public final class TokenUtils { 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_ID = "staffId"; private final static String STAFF_GLOBAL_LEVEL = "staffGlobalLevel"; diff --git a/src/main/resources/static/账户导入模板.xlsx b/src/main/resources/static/账户导入模板.xlsx new file mode 100644 index 0000000..35cd573 Binary files /dev/null and b/src/main/resources/static/账户导入模板.xlsx differ