feat: improve audit log aspect

This commit is contained in:
vran 2022-05-14 17:56:35 +08:00
parent 919bd6de87
commit 92775dfd3b
10 changed files with 79 additions and 16 deletions

View File

@ -39,9 +39,10 @@ public class DocumentController {
private final ProjectService projectService; private final ProjectService projectService;
@PostMapping(Routes.Document.SYNC_ONE) @PostMapping(Routes.Document.SYNC_ONE)
@AuditLog(module = AuditLog.Modules.PROJECT, name = "文档同步", involvedProjectId = "#projectId") @AuditLog(module = AuditLog.Modules.PROJECT, name = "创建同步任务", involvedProjectId = "#projectId",
@Operation(summary = "同步文档") retrieveInvolvedGroupId = true)
public JsonData<Integer> sync(@PathVariable Integer projectId) { @Operation(summary = "创建同步任务")
public JsonData<Integer> createSyncTask(@PathVariable Integer projectId) {
Integer userId = LoginUserContext.getLoginUserId(); Integer userId = LoginUserContext.getLoginUserId();
Optional<Integer> taskIdOpt = projectService.createSyncTask(projectId, userId, false); Optional<Integer> taskIdOpt = projectService.createSyncTask(projectId, userId, false);
return JsonData.ok(taskIdOpt); return JsonData.ok(taskIdOpt);
@ -67,6 +68,8 @@ public class DocumentController {
@GetMapping(Routes.Document.EXPORT) @GetMapping(Routes.Document.EXPORT)
@Operation(summary = "导出文档") @Operation(summary = "导出文档")
@AuditLog(module = AuditLog.Modules.PROJECT, name = "导出文档", involvedProjectId = "#projectId",
retrieveInvolvedGroupId = true)
public ResponseEntity<StreamingResponseBody> getDocumentFiles(@PathVariable Integer projectId, public ResponseEntity<StreamingResponseBody> getDocumentFiles(@PathVariable Integer projectId,
@RequestParam(required = false) @RequestParam(required = false)
Long version, Long version,

View File

@ -30,7 +30,8 @@ public class DocumentDescriptionController {
@PreAuthorize("hasAnyAuthority('SYS_OWNER', 'GROUP_OWNER?groupId='+#groupId, 'GROUP_MEMBER?groupId='+#groupId)") @PreAuthorize("hasAnyAuthority('SYS_OWNER', 'GROUP_OWNER?groupId='+#groupId, 'GROUP_MEMBER?groupId='+#groupId)")
@AuditLog(module = AuditLog.Modules.PROJECT, @AuditLog(module = AuditLog.Modules.PROJECT,
name = "更新描述", name = "更新描述",
involvedProjectId = "#projectId") involvedProjectId = "#projectId",
retrieveInvolvedGroupId = true)
@Operation(summary = "更新描述") @Operation(summary = "更新描述")
public JsonData<Void> save(@PathVariable Integer groupId, public JsonData<Void> save(@PathVariable Integer groupId,
@PathVariable Integer projectId, @PathVariable Integer projectId,

View File

@ -45,7 +45,8 @@ public class DocumentDiscussionController {
@PreAuthorize("hasAnyAuthority('SYS_OWNER', 'GROUP_OWNER?groupId='+#groupId)") @PreAuthorize("hasAnyAuthority('SYS_OWNER', 'GROUP_OWNER?groupId='+#groupId)")
@AuditLog(module = AuditLog.Modules.PROJECT, @AuditLog(module = AuditLog.Modules.PROJECT,
name = "删除评论", name = "删除评论",
involvedProjectId = "#projectId") involvedProjectId = "#projectId",
retrieveInvolvedGroupId = true)
@Operation(summary = "删除评论") @Operation(summary = "删除评论")
public JsonData<Void> delete(@PathVariable Integer groupId, public JsonData<Void> delete(@PathVariable Integer groupId,
@PathVariable Integer projectId, @PathVariable Integer projectId,
@ -58,7 +59,8 @@ public class DocumentDiscussionController {
@PreAuthorize("hasAnyAuthority('SYS_OWNER', 'GROUP_OWNER?groupId='+#groupId, 'GROUP_MEMBER?groupId='+#groupId)") @PreAuthorize("hasAnyAuthority('SYS_OWNER', 'GROUP_OWNER?groupId='+#groupId, 'GROUP_MEMBER?groupId='+#groupId)")
@AuditLog(module = AuditLog.Modules.PROJECT, @AuditLog(module = AuditLog.Modules.PROJECT,
name = "新增评论", name = "新增评论",
involvedProjectId = "#projectId") involvedProjectId = "#projectId",
retrieveInvolvedGroupId = true)
@Operation(summary = "新增评论") @Operation(summary = "新增评论")
public JsonData<Void> create(@PathVariable Integer groupId, public JsonData<Void> create(@PathVariable Integer groupId,
@PathVariable Integer projectId, @PathVariable Integer projectId,

View File

@ -1,6 +1,7 @@
package com.databasir.api; package com.databasir.api;
import com.databasir.common.JsonData; 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.MockDataService;
import com.databasir.core.domain.mock.data.ColumnMockRuleSaveRequest; import com.databasir.core.domain.mock.data.ColumnMockRuleSaveRequest;
import com.databasir.core.domain.mock.data.MockDataGenerateCondition; import com.databasir.core.domain.mock.data.MockDataGenerateCondition;
@ -45,6 +46,9 @@ public class MockDataController {
@PostMapping(Routes.MockData.SAVE_MOCK_RULE) @PostMapping(Routes.MockData.SAVE_MOCK_RULE)
@PreAuthorize("hasAnyAuthority('SYS_OWNER', 'GROUP_OWNER?groupId='+#groupId, 'GROUP_MEMBER?groupId='+#groupId)") @PreAuthorize("hasAnyAuthority('SYS_OWNER', 'GROUP_OWNER?groupId='+#groupId, 'GROUP_MEMBER?groupId='+#groupId)")
@Operation(summary = "保存 Mock Rule") @Operation(summary = "保存 Mock Rule")
@AuditLog(module = AuditLog.Modules.PROJECT, name = "保存 Mock Rule",
involvedProjectId = "#projectId",
involvedGroupId = "#groupId")
public JsonData<Void> saveMockRules(@PathVariable Integer groupId, public JsonData<Void> saveMockRules(@PathVariable Integer groupId,
@PathVariable Integer projectId, @PathVariable Integer projectId,
@PathVariable Integer tableId, @PathVariable Integer tableId,

View File

@ -107,6 +107,8 @@ public class ProjectController {
@PatchMapping(Routes.GroupProject.CANCEL_MANUAL_TASK) @PatchMapping(Routes.GroupProject.CANCEL_MANUAL_TASK)
@Operation(summary = "取消同步任务") @Operation(summary = "取消同步任务")
@AuditLog(module = AuditLog.Modules.PROJECT, name = "取消同步任务", involvedProjectId = "#projectId",
retrieveInvolvedGroupId = true)
public JsonData<Void> cancelTask(@PathVariable Integer projectId, public JsonData<Void> cancelTask(@PathVariable Integer projectId,
@PathVariable Integer taskId) { @PathVariable Integer taskId) {
projectService.cancelTask(projectId, taskId); projectService.cancelTask(projectId, taskId);

View File

@ -2,6 +2,7 @@ package com.databasir.api;
import com.databasir.api.config.security.DatabasirUserDetails; import com.databasir.api.config.security.DatabasirUserDetails;
import com.databasir.common.JsonData; 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.FavoriteProjectPageCondition;
import com.databasir.core.domain.user.data.FavoriteProjectPageResponse; import com.databasir.core.domain.user.data.FavoriteProjectPageResponse;
import com.databasir.core.domain.user.service.UserProjectService; import com.databasir.core.domain.user.service.UserProjectService;
@ -39,6 +40,9 @@ public class UserProjectController {
@PostMapping(Routes.UserProject.ADD) @PostMapping(Routes.UserProject.ADD)
@Operation(summary = "添加用户关注项目") @Operation(summary = "添加用户关注项目")
@AuditLog(module = AuditLog.Modules.PROJECT, name = "关注项目",
involvedProjectId = "#projectId",
retrieveInvolvedGroupId = true)
public JsonData<Void> addFavorite(@PathVariable Integer projectId) { public JsonData<Void> addFavorite(@PathVariable Integer projectId) {
DatabasirUserDetails user = (DatabasirUserDetails) SecurityContextHolder.getContext() DatabasirUserDetails user = (DatabasirUserDetails) SecurityContextHolder.getContext()
.getAuthentication() .getAuthentication()
@ -50,6 +54,9 @@ public class UserProjectController {
@DeleteMapping(Routes.UserProject.REMOVE) @DeleteMapping(Routes.UserProject.REMOVE)
@Operation(summary = "删除用户关注项目") @Operation(summary = "删除用户关注项目")
@AuditLog(module = AuditLog.Modules.PROJECT, name = "取消关注",
involvedProjectId = "#projectId",
retrieveInvolvedGroupId = true)
public JsonData<Void> removeFavorite(@PathVariable Integer projectId) { public JsonData<Void> removeFavorite(@PathVariable Integer projectId) {
DatabasirUserDetails user = (DatabasirUserDetails) SecurityContextHolder.getContext() DatabasirUserDetails user = (DatabasirUserDetails) SecurityContextHolder.getContext()
.getAuthentication() .getAuthentication()

View File

@ -5,6 +5,8 @@ import com.databasir.common.JsonData;
import com.databasir.core.domain.log.annotation.AuditLog; import com.databasir.core.domain.log.annotation.AuditLog;
import com.databasir.core.domain.log.data.OperationLogRequest; import com.databasir.core.domain.log.data.OperationLogRequest;
import com.databasir.core.domain.log.service.OperationLogService; 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.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.JoinPoint;
@ -33,13 +35,19 @@ public class OperationLogAspect {
private final OperationLogService operationLogService; private final OperationLogService operationLogService;
private final ProjectDao projectDao;
private SpelExpressionParser spelExpressionParser = new SpelExpressionParser(); private SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
@AfterReturning(value = "@annotation(operation)", returning = "returnValue") @AfterReturning(value = "@annotation(operation)", returning = "returnValue")
public void log(JoinPoint joinPoint, Object returnValue, AuditLog operation) { public void log(JoinPoint joinPoint, Object returnValue, AuditLog operation) {
if (returnValue instanceof JsonData) {
saveLog(operation, joinPoint, (JsonData<Object>) returnValue); saveLog(operation, joinPoint, (JsonData<Object>) returnValue);
} else {
saveLog(operation, joinPoint, JsonData.ok());
}
} }
@AfterThrowing(value = "@annotation(operation)", throwing = "ex") @AfterThrowing(value = "@annotation(operation)", throwing = "ex")
@ -62,6 +70,14 @@ public class OperationLogAspect {
.orElse(null); .orElse(null);
Integer involvedUserId = getValueBySPEL(method, arguments, operation.involvedUserId(), Integer.class) Integer involvedUserId = getValueBySPEL(method, arguments, operation.involvedUserId(), Integer.class)
.orElse(null); .orElse(null);
// auto fill involvedProjectId
if (involvedGroupId == null
&& operation.retrieveInvolvedGroupId()
&& involvedProjectId != null) {
involvedGroupId = projectDao.selectOptionalById(involvedProjectId)
.map(ProjectPojo::getGroupId)
.orElse(null);
}
int userId = userId(); int userId = userId();
String username = principal.getUserPojo().getUsername(); String username = principal.getUserPojo().getUsername();
String nickname = principal.getUserPojo().getNickname(); String nickname = principal.getUserPojo().getNickname();

View File

@ -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.data.OperationLogRequest;
import com.databasir.core.domain.log.service.OperationLogService; import com.databasir.core.domain.log.service.OperationLogService;
import com.databasir.dao.enums.ProjectSyncTaskStatus; import com.databasir.dao.enums.ProjectSyncTaskStatus;
import com.databasir.dao.impl.ProjectDao;
import com.databasir.dao.impl.ProjectSyncTaskDao; import com.databasir.dao.impl.ProjectSyncTaskDao;
import com.databasir.dao.impl.UserDao; import com.databasir.dao.impl.UserDao;
import com.databasir.dao.tables.pojos.ProjectSyncTaskPojo;
import com.databasir.dao.tables.pojos.UserPojo; import com.databasir.dao.tables.pojos.UserPojo;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -14,7 +16,10 @@ import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Collectors;
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
@ -29,6 +34,8 @@ public class ProjectSyncTaskScheduler {
private final ProjectSyncTaskDao projectSyncTaskDao; private final ProjectSyncTaskDao projectSyncTaskDao;
private final ProjectDao projectDao;
private final ThreadPoolTaskExecutor projectSyncTaskThreadPoolTaskExecutor; private final ThreadPoolTaskExecutor projectSyncTaskThreadPoolTaskExecutor;
/** /**
@ -37,26 +44,33 @@ public class ProjectSyncTaskScheduler {
@Scheduled(fixedRate = 5000L) @Scheduled(fixedRate = 5000L)
public void startSyncTask() { public void startSyncTask() {
final int size = 10; final int size = 10;
projectSyncTaskDao.listNewTasks(size).forEach(task -> { List<ProjectSyncTaskPojo> tasks = projectSyncTaskDao.listNewTasks(size);
List<Integer> projectIds = tasks.stream()
.map(ProjectSyncTaskPojo::getProjectId)
.distinct()
.collect(Collectors.toList());
Map<Integer, Integer> groupIdAndProjectIdMap = projectDao.selectGroupIdsByProjectIdIn(projectIds);
tasks.forEach(task -> {
projectSyncTaskThreadPoolTaskExecutor.execute(() -> { projectSyncTaskThreadPoolTaskExecutor.execute(() -> {
Integer taskId = task.getId(); Integer taskId = task.getId();
Integer projectId = task.getProjectId(); Integer projectId = task.getProjectId();
Integer groupId = groupIdAndProjectIdMap.get(projectId);
Integer userId = task.getUserId(); 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 { try {
updateSyncTaskStatus(taskId, ProjectSyncTaskStatus.RUNNING, "running"); updateSyncTaskStatus(taskId, ProjectSyncTaskStatus.RUNNING, "running");
documentService.syncByProjectId(projectId); documentService.syncByProjectId(projectId);
updateSyncTaskStatus(taskId, ProjectSyncTaskStatus.FINISHED, "ok"); updateSyncTaskStatus(taskId, ProjectSyncTaskStatus.FINISHED, "ok");
saveOperationLog(projectId, userId, null); saveOperationLog(groupId, projectId, userId, null);
} catch (Exception e) { } catch (Exception e) {
String result = Objects.requireNonNullElse(e.getMessage(), "unknown"); String result = Objects.requireNonNullElse(e.getMessage(), "unknown");
updateSyncTaskStatus(taskId, ProjectSyncTaskStatus.FAILED, result); updateSyncTaskStatus(taskId, ProjectSyncTaskStatus.FAILED, result);
saveOperationLog(projectId, userId, e); saveOperationLog(groupId, projectId, userId, e);
throw e; throw e;
} }
} }
@ -65,19 +79,19 @@ public class ProjectSyncTaskScheduler {
projectSyncTaskDao.updateStatusAndResultById(taskId, status, result); 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 operatorNickName;
String operatorUsername; String operatorUsername;
String operationName; String operationName;
if (Objects.equals(-1, userId)) { if (Objects.equals(-1, userId)) {
operatorNickName = "system"; operatorNickName = "system";
operatorUsername = "system"; operatorUsername = "system";
operationName = "文档定时同步"; operationName = "定时同步";
} else { } else {
UserPojo user = userDao.selectById(userId); UserPojo user = userDao.selectById(userId);
operatorNickName = user.getNickname(); operatorNickName = user.getNickname();
operatorUsername = user.getUsername(); operatorUsername = user.getUsername();
operationName = "文档手动同步"; operationName = "手动同步";
} }
JsonData response; JsonData response;
if (ex == null) { if (ex == null) {
@ -95,6 +109,7 @@ public class ProjectSyncTaskScheduler {
.operationResponse(response) .operationResponse(response)
.isSuccess(ex == null) .isSuccess(ex == null)
.involvedProjectId(projectId) .involvedProjectId(projectId)
.involvedGroupId(groupId)
.build(); .build();
operationLogService.save(operationLog); operationLogService.save(operationLog);
} }

View File

@ -28,6 +28,8 @@ public @interface AuditLog {
*/ */
String involvedUserId() default "N/A"; String involvedUserId() default "N/A";
boolean retrieveInvolvedGroupId() default false;
interface Modules { interface Modules {
String UNKNOWN = "UNKNOWN"; String UNKNOWN = "UNKNOWN";
String PROJECT = "project"; String PROJECT = "project";

View File

@ -115,4 +115,15 @@ public class ProjectDao extends BaseDao<ProjectPojo> {
.groupBy(DATA_SOURCE.DATABASE_TYPE) .groupBy(DATA_SOURCE.DATABASE_TYPE)
.fetchMap(DATA_SOURCE.DATABASE_TYPE, DSL.count(DATA_SOURCE)); .fetchMap(DATA_SOURCE.DATABASE_TYPE, DSL.count(DATA_SOURCE));
} }
public Map<Integer, Integer> selectGroupIdsByProjectIdIn(List<Integer> 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);
}
} }