fix: improve audit log (#176)
* fix: group owner and member can't view project log * feat: improve audit log aspect * fix: miss group id when request project log
This commit is contained in:
parent
bd07d8a018
commit
59570a9ee6
|
@ -25,7 +25,9 @@ public class AuditLogController {
|
|||
private final OperationLogService operationLogService;
|
||||
|
||||
@GetMapping(Routes.OperationLog.LIST)
|
||||
@PreAuthorize("hasAnyAuthority('SYS_OWNER')")
|
||||
@PreAuthorize("hasAnyAuthority('SYS_OWNER',"
|
||||
+ " 'GROUP_OWNER?groupId='+#condition.involveGroupId,"
|
||||
+ " 'GROUP_MEMBER?groupId='+#condition.involveGroupId)")
|
||||
@Operation(summary = "查询操作日志")
|
||||
public JsonData<Page<OperationLogPageResponse>> list(@PageableDefault(sort = "id", direction = Sort.Direction.DESC)
|
||||
Pageable page,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>databasir</title><script defer="defer" type="module" src="/js/chunk-vendors.9effab81.js"></script><script defer="defer" type="module" src="/js/app.06544fba.js"></script><link href="/css/chunk-vendors.81898547.css" rel="stylesheet"><link href="/css/app.15b40a89.css" rel="stylesheet"><script defer="defer" src="/js/chunk-vendors-legacy.fb0c8458.js" nomodule></script><script defer="defer" src="/js/app-legacy.b786ff99.js" nomodule></script></head><body><noscript><strong>We're sorry but databasir doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
|
||||
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>databasir</title><script defer="defer" type="module" src="/js/chunk-vendors.9effab81.js"></script><script defer="defer" type="module" src="/js/app.1aa391ea.js"></script><link href="/css/chunk-vendors.81898547.css" rel="stylesheet"><link href="/css/app.15b40a89.css" rel="stylesheet"><script defer="defer" src="/js/chunk-vendors-legacy.fb0c8458.js" nomodule></script><script defer="defer" src="/js/app-legacy.18135b1d.js" nomodule></script></head><body><noscript><strong>We're sorry but databasir doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 8a953f7a8a2dec3d838c2faf34c847e9e77a074c
|
||||
Subproject commit b027e133261b2df4e56bc6cab26fcb99113e0f0e
|
Loading…
Reference in New Issue