Compare commits

...

2 Commits

18 changed files with 172 additions and 46 deletions

10
pom.xml
View File

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

View File

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

View File

@ -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 "系统繁忙,请稍后再试";
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -268,13 +268,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) {
@ -285,7 +286,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);
} }

View File

@ -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-Digestmd5
* <p>
* Controllervoid
*/
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;
} }

View File

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