diff --git a/api/src/main/java/com/databasir/api/DocumentController.java b/api/src/main/java/com/databasir/api/DocumentController.java index 8010480..6ac8cf3 100644 --- a/api/src/main/java/com/databasir/api/DocumentController.java +++ b/api/src/main/java/com/databasir/api/DocumentController.java @@ -39,9 +39,10 @@ public class DocumentController { private final ProjectService projectService; @PostMapping(Routes.Document.SYNC_ONE) - @AuditLog(module = AuditLog.Modules.PROJECT, name = "文档同步", involvedProjectId = "#projectId") - @Operation(summary = "同步文档") - public JsonData sync(@PathVariable Integer projectId) { + @AuditLog(module = AuditLog.Modules.PROJECT, name = "创建同步任务", involvedProjectId = "#projectId", + retrieveInvolvedGroupId = true) + @Operation(summary = "创建同步任务") + public JsonData createSyncTask(@PathVariable Integer projectId) { Integer userId = LoginUserContext.getLoginUserId(); Optional taskIdOpt = projectService.createSyncTask(projectId, userId, false); return JsonData.ok(taskIdOpt); @@ -67,6 +68,8 @@ public class DocumentController { @GetMapping(Routes.Document.EXPORT) @Operation(summary = "导出文档") + @AuditLog(module = AuditLog.Modules.PROJECT, name = "导出文档", involvedProjectId = "#projectId", + retrieveInvolvedGroupId = true) public ResponseEntity getDocumentFiles(@PathVariable Integer projectId, @RequestParam(required = false) Long version, diff --git a/api/src/main/java/com/databasir/api/DocumentDescriptionController.java b/api/src/main/java/com/databasir/api/DocumentDescriptionController.java index d65d9d4..79f0c24 100644 --- a/api/src/main/java/com/databasir/api/DocumentDescriptionController.java +++ b/api/src/main/java/com/databasir/api/DocumentDescriptionController.java @@ -30,7 +30,8 @@ public class DocumentDescriptionController { @PreAuthorize("hasAnyAuthority('SYS_OWNER', 'GROUP_OWNER?groupId='+#groupId, 'GROUP_MEMBER?groupId='+#groupId)") @AuditLog(module = AuditLog.Modules.PROJECT, name = "更新描述", - involvedProjectId = "#projectId") + involvedProjectId = "#projectId", + retrieveInvolvedGroupId = true) @Operation(summary = "更新描述") public JsonData save(@PathVariable Integer groupId, @PathVariable Integer projectId, diff --git a/api/src/main/java/com/databasir/api/DocumentDiscussionController.java b/api/src/main/java/com/databasir/api/DocumentDiscussionController.java index 1b9fd41..5091a3c 100644 --- a/api/src/main/java/com/databasir/api/DocumentDiscussionController.java +++ b/api/src/main/java/com/databasir/api/DocumentDiscussionController.java @@ -35,7 +35,7 @@ public class DocumentDiscussionController { @PathVariable Integer projectId, @PageableDefault(sort = "id", direction = Sort.Direction.DESC) - Pageable request, + Pageable request, DiscussionListCondition condition) { var data = documentDiscussionService.list(groupId, projectId, request, condition); return JsonData.ok(data); @@ -45,7 +45,8 @@ public class DocumentDiscussionController { @PreAuthorize("hasAnyAuthority('SYS_OWNER', 'GROUP_OWNER?groupId='+#groupId)") @AuditLog(module = AuditLog.Modules.PROJECT, name = "删除评论", - involvedProjectId = "#projectId") + involvedProjectId = "#projectId", + retrieveInvolvedGroupId = true) @Operation(summary = "删除评论") public JsonData delete(@PathVariable Integer groupId, @PathVariable Integer projectId, @@ -58,7 +59,8 @@ public class DocumentDiscussionController { @PreAuthorize("hasAnyAuthority('SYS_OWNER', 'GROUP_OWNER?groupId='+#groupId, 'GROUP_MEMBER?groupId='+#groupId)") @AuditLog(module = AuditLog.Modules.PROJECT, name = "新增评论", - involvedProjectId = "#projectId") + involvedProjectId = "#projectId", + retrieveInvolvedGroupId = true) @Operation(summary = "新增评论") public JsonData create(@PathVariable Integer groupId, @PathVariable Integer projectId, diff --git a/api/src/main/java/com/databasir/api/MockDataController.java b/api/src/main/java/com/databasir/api/MockDataController.java index fd94328..54f64f9 100644 --- a/api/src/main/java/com/databasir/api/MockDataController.java +++ b/api/src/main/java/com/databasir/api/MockDataController.java @@ -1,6 +1,7 @@ package com.databasir.api; import com.databasir.common.JsonData; +import com.databasir.core.domain.log.annotation.AuditLog; import com.databasir.core.domain.mock.MockDataService; import com.databasir.core.domain.mock.data.ColumnMockRuleSaveRequest; import com.databasir.core.domain.mock.data.MockDataGenerateCondition; @@ -45,6 +46,9 @@ public class MockDataController { @PostMapping(Routes.MockData.SAVE_MOCK_RULE) @PreAuthorize("hasAnyAuthority('SYS_OWNER', 'GROUP_OWNER?groupId='+#groupId, 'GROUP_MEMBER?groupId='+#groupId)") @Operation(summary = "保存 Mock Rule") + @AuditLog(module = AuditLog.Modules.PROJECT, name = "保存 Mock Rule", + involvedProjectId = "#projectId", + involvedGroupId = "#groupId") public JsonData saveMockRules(@PathVariable Integer groupId, @PathVariable Integer projectId, @PathVariable Integer tableId, diff --git a/api/src/main/java/com/databasir/api/ProjectController.java b/api/src/main/java/com/databasir/api/ProjectController.java index 9b2f115..1453310 100644 --- a/api/src/main/java/com/databasir/api/ProjectController.java +++ b/api/src/main/java/com/databasir/api/ProjectController.java @@ -107,6 +107,8 @@ public class ProjectController { @PatchMapping(Routes.GroupProject.CANCEL_MANUAL_TASK) @Operation(summary = "取消同步任务") + @AuditLog(module = AuditLog.Modules.PROJECT, name = "取消同步任务", involvedProjectId = "#projectId", + retrieveInvolvedGroupId = true) public JsonData cancelTask(@PathVariable Integer projectId, @PathVariable Integer taskId) { projectService.cancelTask(projectId, taskId); diff --git a/api/src/main/java/com/databasir/api/UserProjectController.java b/api/src/main/java/com/databasir/api/UserProjectController.java index a517917..80c9617 100644 --- a/api/src/main/java/com/databasir/api/UserProjectController.java +++ b/api/src/main/java/com/databasir/api/UserProjectController.java @@ -2,6 +2,7 @@ package com.databasir.api; import com.databasir.api.config.security.DatabasirUserDetails; import com.databasir.common.JsonData; +import com.databasir.core.domain.log.annotation.AuditLog; import com.databasir.core.domain.user.data.FavoriteProjectPageCondition; import com.databasir.core.domain.user.data.FavoriteProjectPageResponse; import com.databasir.core.domain.user.service.UserProjectService; @@ -39,6 +40,9 @@ public class UserProjectController { @PostMapping(Routes.UserProject.ADD) @Operation(summary = "添加用户关注项目") + @AuditLog(module = AuditLog.Modules.PROJECT, name = "关注项目", + involvedProjectId = "#projectId", + retrieveInvolvedGroupId = true) public JsonData addFavorite(@PathVariable Integer projectId) { DatabasirUserDetails user = (DatabasirUserDetails) SecurityContextHolder.getContext() .getAuthentication() @@ -50,6 +54,9 @@ public class UserProjectController { @DeleteMapping(Routes.UserProject.REMOVE) @Operation(summary = "删除用户关注项目") + @AuditLog(module = AuditLog.Modules.PROJECT, name = "取消关注", + involvedProjectId = "#projectId", + retrieveInvolvedGroupId = true) public JsonData removeFavorite(@PathVariable Integer projectId) { DatabasirUserDetails user = (DatabasirUserDetails) SecurityContextHolder.getContext() .getAuthentication() diff --git a/api/src/main/java/com/databasir/api/advice/OperationLogAspect.java b/api/src/main/java/com/databasir/api/advice/OperationLogAspect.java index fd2b1ff..73e19b6 100644 --- a/api/src/main/java/com/databasir/api/advice/OperationLogAspect.java +++ b/api/src/main/java/com/databasir/api/advice/OperationLogAspect.java @@ -5,6 +5,8 @@ import com.databasir.common.JsonData; import com.databasir.core.domain.log.annotation.AuditLog; import com.databasir.core.domain.log.data.OperationLogRequest; import com.databasir.core.domain.log.service.OperationLogService; +import com.databasir.dao.impl.ProjectDao; +import com.databasir.dao.tables.pojos.ProjectPojo; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; @@ -33,13 +35,19 @@ public class OperationLogAspect { private final OperationLogService operationLogService; + private final ProjectDao projectDao; + private SpelExpressionParser spelExpressionParser = new SpelExpressionParser(); private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); @AfterReturning(value = "@annotation(operation)", returning = "returnValue") public void log(JoinPoint joinPoint, Object returnValue, AuditLog operation) { - saveLog(operation, joinPoint, (JsonData) returnValue); + if (returnValue instanceof JsonData) { + saveLog(operation, joinPoint, (JsonData) returnValue); + } else { + saveLog(operation, joinPoint, JsonData.ok()); + } } @AfterThrowing(value = "@annotation(operation)", throwing = "ex") @@ -62,6 +70,14 @@ public class OperationLogAspect { .orElse(null); Integer involvedUserId = getValueBySPEL(method, arguments, operation.involvedUserId(), Integer.class) .orElse(null); + // auto fill involvedProjectId + if (involvedGroupId == null + && operation.retrieveInvolvedGroupId() + && involvedProjectId != null) { + involvedGroupId = projectDao.selectOptionalById(involvedProjectId) + .map(ProjectPojo::getGroupId) + .orElse(null); + } int userId = userId(); String username = principal.getUserPojo().getUsername(); String nickname = principal.getUserPojo().getNickname(); diff --git a/api/src/main/java/com/databasir/job/ProjectSyncTaskScheduler.java b/api/src/main/java/com/databasir/job/ProjectSyncTaskScheduler.java index b38e6e4..878dbf4 100644 --- a/api/src/main/java/com/databasir/job/ProjectSyncTaskScheduler.java +++ b/api/src/main/java/com/databasir/job/ProjectSyncTaskScheduler.java @@ -5,8 +5,10 @@ import com.databasir.core.domain.document.service.DocumentService; import com.databasir.core.domain.log.data.OperationLogRequest; import com.databasir.core.domain.log.service.OperationLogService; import com.databasir.dao.enums.ProjectSyncTaskStatus; +import com.databasir.dao.impl.ProjectDao; import com.databasir.dao.impl.ProjectSyncTaskDao; import com.databasir.dao.impl.UserDao; +import com.databasir.dao.tables.pojos.ProjectSyncTaskPojo; import com.databasir.dao.tables.pojos.UserPojo; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -14,7 +16,10 @@ import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Component; +import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.stream.Collectors; @Component @RequiredArgsConstructor @@ -29,6 +34,8 @@ public class ProjectSyncTaskScheduler { private final ProjectSyncTaskDao projectSyncTaskDao; + private final ProjectDao projectDao; + private final ThreadPoolTaskExecutor projectSyncTaskThreadPoolTaskExecutor; /** @@ -37,26 +44,33 @@ public class ProjectSyncTaskScheduler { @Scheduled(fixedRate = 5000L) public void startSyncTask() { final int size = 10; - projectSyncTaskDao.listNewTasks(size).forEach(task -> { + List tasks = projectSyncTaskDao.listNewTasks(size); + List projectIds = tasks.stream() + .map(ProjectSyncTaskPojo::getProjectId) + .distinct() + .collect(Collectors.toList()); + Map groupIdAndProjectIdMap = projectDao.selectGroupIdsByProjectIdIn(projectIds); + tasks.forEach(task -> { projectSyncTaskThreadPoolTaskExecutor.execute(() -> { Integer taskId = task.getId(); Integer projectId = task.getProjectId(); + Integer groupId = groupIdAndProjectIdMap.get(projectId); Integer userId = task.getUserId(); - sync(taskId, projectId, userId); + sync(taskId, groupId, projectId, userId); }); }); } - private void sync(Integer taskId, Integer projectId, Integer userId) { + private void sync(Integer taskId, Integer groupId, Integer projectId, Integer userId) { try { updateSyncTaskStatus(taskId, ProjectSyncTaskStatus.RUNNING, "running"); documentService.syncByProjectId(projectId); updateSyncTaskStatus(taskId, ProjectSyncTaskStatus.FINISHED, "ok"); - saveOperationLog(projectId, userId, null); + saveOperationLog(groupId, projectId, userId, null); } catch (Exception e) { String result = Objects.requireNonNullElse(e.getMessage(), "unknown"); updateSyncTaskStatus(taskId, ProjectSyncTaskStatus.FAILED, result); - saveOperationLog(projectId, userId, e); + saveOperationLog(groupId, projectId, userId, e); throw e; } } @@ -65,19 +79,19 @@ public class ProjectSyncTaskScheduler { projectSyncTaskDao.updateStatusAndResultById(taskId, status, result); } - private void saveOperationLog(Integer projectId, Integer userId, Exception ex) { + private void saveOperationLog(Integer groupId, Integer projectId, Integer userId, Exception ex) { String operatorNickName; String operatorUsername; String operationName; if (Objects.equals(-1, userId)) { operatorNickName = "system"; operatorUsername = "system"; - operationName = "文档定时同步"; + operationName = "定时同步"; } else { UserPojo user = userDao.selectById(userId); operatorNickName = user.getNickname(); operatorUsername = user.getUsername(); - operationName = "文档手动同步"; + operationName = "手动同步"; } JsonData response; if (ex == null) { @@ -95,6 +109,7 @@ public class ProjectSyncTaskScheduler { .operationResponse(response) .isSuccess(ex == null) .involvedProjectId(projectId) + .involvedGroupId(groupId) .build(); operationLogService.save(operationLog); } diff --git a/core/src/main/java/com/databasir/core/domain/log/annotation/AuditLog.java b/core/src/main/java/com/databasir/core/domain/log/annotation/AuditLog.java index c3dfb7a..d6bbfa0 100644 --- a/core/src/main/java/com/databasir/core/domain/log/annotation/AuditLog.java +++ b/core/src/main/java/com/databasir/core/domain/log/annotation/AuditLog.java @@ -28,6 +28,8 @@ public @interface AuditLog { */ String involvedUserId() default "N/A"; + boolean retrieveInvolvedGroupId() default false; + interface Modules { String UNKNOWN = "UNKNOWN"; String PROJECT = "project"; diff --git a/dao/src/main/java/com/databasir/dao/impl/ProjectDao.java b/dao/src/main/java/com/databasir/dao/impl/ProjectDao.java index ef61298..ca83bd7 100644 --- a/dao/src/main/java/com/databasir/dao/impl/ProjectDao.java +++ b/dao/src/main/java/com/databasir/dao/impl/ProjectDao.java @@ -115,4 +115,15 @@ public class ProjectDao extends BaseDao { .groupBy(DATA_SOURCE.DATABASE_TYPE) .fetchMap(DATA_SOURCE.DATABASE_TYPE, DSL.count(DATA_SOURCE)); } + + public Map selectGroupIdsByProjectIdIn(List projectIds) { + if (projectIds == null || projectIds.isEmpty()) { + return Collections.emptyMap(); + } + return getDslContext() + .select(PROJECT.ID, PROJECT.GROUP_ID) + .from(PROJECT) + .where(PROJECT.ID.in(projectIds)) + .fetchMap(PROJECT.ID, PROJECT.GROUP_ID); + } }