feat: improve audit log aspect
This commit is contained in:
parent
919bd6de87
commit
92775dfd3b
|
@ -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<Integer> sync(@PathVariable Integer projectId) {
|
||||
@AuditLog(module = AuditLog.Modules.PROJECT, name = "创建同步任务", involvedProjectId = "#projectId",
|
||||
retrieveInvolvedGroupId = true)
|
||||
@Operation(summary = "创建同步任务")
|
||||
public JsonData<Integer> createSyncTask(@PathVariable Integer projectId) {
|
||||
Integer userId = LoginUserContext.getLoginUserId();
|
||||
Optional<Integer> 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<StreamingResponseBody> getDocumentFiles(@PathVariable Integer projectId,
|
||||
@RequestParam(required = false)
|
||||
Long version,
|
||||
|
|
|
@ -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<Void> save(@PathVariable Integer groupId,
|
||||
@PathVariable Integer projectId,
|
||||
|
|
|
@ -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<Void> 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<Void> create(@PathVariable Integer groupId,
|
||||
@PathVariable Integer projectId,
|
||||
|
|
|
@ -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<Void> saveMockRules(@PathVariable Integer groupId,
|
||||
@PathVariable Integer projectId,
|
||||
@PathVariable Integer tableId,
|
||||
|
|
|
@ -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<Void> cancelTask(@PathVariable Integer projectId,
|
||||
@PathVariable Integer taskId) {
|
||||
projectService.cancelTask(projectId, taskId);
|
||||
|
|
|
@ -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<Void> 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<Void> removeFavorite(@PathVariable Integer projectId) {
|
||||
DatabasirUserDetails user = (DatabasirUserDetails) SecurityContextHolder.getContext()
|
||||
.getAuthentication()
|
||||
|
|
|
@ -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<Object>) returnValue);
|
||||
if (returnValue instanceof JsonData) {
|
||||
saveLog(operation, joinPoint, (JsonData<Object>) 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();
|
||||
|
|
|
@ -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<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(() -> {
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@ public @interface AuditLog {
|
|||
*/
|
||||
String involvedUserId() default "N/A";
|
||||
|
||||
boolean retrieveInvolvedGroupId() default false;
|
||||
|
||||
interface Modules {
|
||||
String UNKNOWN = "UNKNOWN";
|
||||
String PROJECT = "project";
|
||||
|
|
|
@ -115,4 +115,15 @@ public class ProjectDao extends BaseDao<ProjectPojo> {
|
|||
.groupBy(DATA_SOURCE.DATABASE_TYPE)
|
||||
.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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue