docs: add operation log implementation plan
- Break down into 9 bite-sized tasks - Follow TDD approach with tests first - Include exact file paths and complete code - Provide verification steps for each task
This commit is contained in:
@@ -0,0 +1,672 @@
|
||||
# 操作日志记录功能实施计划
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**目标**: 实现基于注解的操作日志记录功能,自动记录关键业务操作到数据库,解决Dashboard操作日志显示0的问题。
|
||||
|
||||
**架构**: 采用AOP切面 + 注解驱动的架构,使用Spring AOP拦截带`@OperationLog`注解的方法,异步记录操作日志到数据库,不影响主业务流程性能。
|
||||
|
||||
**技术栈**: Java 21, Spring Boot 3.5.13, Spring AOP, Project Reactor, Jackson
|
||||
|
||||
---
|
||||
|
||||
## Task 1: 创建 @OperationLog 注解
|
||||
|
||||
**文件:**
|
||||
- 创建: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/OperationLog.java`
|
||||
|
||||
**Step 1: 创建注解文件**
|
||||
|
||||
```java
|
||||
package cn.novalon.manage.sys.audit;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 操作日志注解
|
||||
* 标记需要记录操作日志的方法
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-04-03
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface OperationLog {
|
||||
|
||||
/**
|
||||
* 操作名称
|
||||
* 例如:"创建用户"、"删除角色"
|
||||
*/
|
||||
String operation();
|
||||
|
||||
/**
|
||||
* 模块名称
|
||||
* 例如:"用户管理"、"角色管理"
|
||||
*/
|
||||
String module();
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: 验证注解创建成功**
|
||||
|
||||
运行: `ls -la novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/OperationLog.java`
|
||||
|
||||
预期: 文件存在且内容正确
|
||||
|
||||
**Step 3: 提交**
|
||||
|
||||
```bash
|
||||
git add novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/OperationLog.java
|
||||
git commit -m "feat: add @OperationLog annotation for operation logging"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 2: 创建 OperationLogAspect 切面(基础结构)
|
||||
|
||||
**文件:**
|
||||
- 创建: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/OperationLogAspect.java`
|
||||
|
||||
**Step 1: 创建切面基础结构**
|
||||
|
||||
```java
|
||||
package cn.novalon.manage.sys.audit;
|
||||
|
||||
import cn.novalon.manage.sys.core.domain.OperationLog;
|
||||
import cn.novalon.manage.sys.core.service.IOperationLogService;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
||||
import org.springframework.stereotype.Component;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
/**
|
||||
* 操作日志切面
|
||||
*
|
||||
* 文件定义:使用AOP自动拦截带@OperationLog注解的方法,记录操作日志
|
||||
* 涉及业务:自动记录用户操作,包括操作人、操作时间、参数、结果、耗时等
|
||||
* 算法:使用异步方式记录日志,不阻塞主流程
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-04-03
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class OperationLogAspect {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(OperationLogAspect.class);
|
||||
|
||||
private final IOperationLogService logService;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public OperationLogAspect(IOperationLogService logService, ObjectMapper objectMapper) {
|
||||
this.logService = logService;
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
@Around("@annotation(operationLog)")
|
||||
public Object around(ProceedingJoinPoint point, OperationLog operationLog) throws Throwable {
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
// 获取基本信息
|
||||
String username = getCurrentUsername();
|
||||
String ip = "unknown";
|
||||
String method = point.getSignature().toShortString();
|
||||
String params = serializeParams(point.getArgs());
|
||||
|
||||
// 执行业务方法
|
||||
Object result = null;
|
||||
String status = "0";
|
||||
String errorMsg = null;
|
||||
|
||||
try {
|
||||
result = point.proceed();
|
||||
|
||||
// 处理响应式结果
|
||||
if (result instanceof Mono) {
|
||||
return ((Mono<?>) result)
|
||||
.doOnSuccess(res -> {
|
||||
long duration = System.currentTimeMillis() - startTime;
|
||||
saveLogAsync(operationLog, username, ip, method,
|
||||
params, res, duration, "0", null);
|
||||
})
|
||||
.doOnError(error -> {
|
||||
long duration = System.currentTimeMillis() - startTime;
|
||||
saveLogAsync(operationLog, username, ip, method,
|
||||
params, null, duration, "1", error.getMessage());
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (Throwable error) {
|
||||
status = "1";
|
||||
errorMsg = error.getMessage();
|
||||
throw error;
|
||||
} finally {
|
||||
if (!(result instanceof Mono)) {
|
||||
long duration = System.currentTimeMillis() - startTime;
|
||||
saveLogAsync(operationLog, username, ip, method,
|
||||
params, result, duration, status, errorMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getCurrentUsername() {
|
||||
try {
|
||||
return ReactiveSecurityContextHolder.getContext()
|
||||
.map(ctx -> ctx.getAuthentication().getPrincipal())
|
||||
.cast(String.class)
|
||||
.blockOptional()
|
||||
.orElse("system");
|
||||
} catch (Exception e) {
|
||||
logger.warn("获取当前用户名失败: {}", e.getMessage());
|
||||
return "system";
|
||||
}
|
||||
}
|
||||
|
||||
private String serializeParams(Object[] args) {
|
||||
try {
|
||||
if (args == null || args.length == 0) {
|
||||
return null;
|
||||
}
|
||||
return objectMapper.writeValueAsString(args);
|
||||
} catch (Exception e) {
|
||||
logger.warn("序列化参数失败: {}", e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String serializeResult(Object result) {
|
||||
try {
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
return objectMapper.writeValueAsString(result);
|
||||
} catch (Exception e) {
|
||||
logger.warn("序列化结果失败: {}", e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void saveLogAsync(OperationLog annotation, String username,
|
||||
String ip, String method, String params,
|
||||
Object result, long duration, String status,
|
||||
String errorMsg) {
|
||||
Mono.fromRunnable(() -> {
|
||||
OperationLog log = new OperationLog();
|
||||
log.setUsername(username);
|
||||
log.setOperation(annotation.module() + " - " + annotation.operation());
|
||||
log.setMethod(method);
|
||||
log.setParams(params);
|
||||
log.setResult(serializeResult(result));
|
||||
log.setIp(ip);
|
||||
log.setDuration(duration);
|
||||
log.setStatus(status);
|
||||
log.setErrorMsg(errorMsg);
|
||||
|
||||
logService.save(log)
|
||||
.doOnSuccess(saved -> logger.debug("操作日志保存成功: {} - {}",
|
||||
annotation.module(), annotation.operation()))
|
||||
.doOnError(error -> logger.error("操作日志保存失败: {}",
|
||||
error.getMessage()))
|
||||
.subscribe();
|
||||
})
|
||||
.subscribeOn(Schedulers.boundedElastic())
|
||||
.subscribe();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: 验证切面创建成功**
|
||||
|
||||
运行: `ls -la novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/OperationLogAspect.java`
|
||||
|
||||
预期: 文件存在且内容正确
|
||||
|
||||
**Step 3: 提交**
|
||||
|
||||
```bash
|
||||
git add novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/OperationLogAspect.java
|
||||
git commit -m "feat: implement OperationLogAspect for automatic operation logging"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 3: 编写单元测试
|
||||
|
||||
**文件:**
|
||||
- 创建: `novalon-manage-api/manage-sys/src/test/java/cn/novalon/manage/sys/audit/OperationLogAspectTest.java`
|
||||
|
||||
**Step 1: 创建测试文件**
|
||||
|
||||
```java
|
||||
package cn.novalon.manage.sys.audit;
|
||||
|
||||
import cn.novalon.manage.sys.core.domain.OperationLog;
|
||||
import cn.novalon.manage.sys.core.service.IOperationLogService;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.Signature;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* OperationLogAspect 单元测试
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-04-03
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class OperationLogAspectTest {
|
||||
|
||||
@Mock
|
||||
private IOperationLogService logService;
|
||||
|
||||
@Mock
|
||||
private ProceedingJoinPoint joinPoint;
|
||||
|
||||
@Mock
|
||||
private Signature signature;
|
||||
|
||||
private OperationLogAspect aspect;
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
objectMapper = new ObjectMapper();
|
||||
aspect = new OperationLogAspect(logService, objectMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAround_WithMonoResult_ShouldSaveLog() throws Throwable {
|
||||
OperationLog annotation = new OperationLog() {
|
||||
@Override
|
||||
public String operation() {
|
||||
return "创建用户";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String module() {
|
||||
return "用户管理";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends java.lang.annotation.Annotation> annotationType() {
|
||||
return OperationLog.class;
|
||||
}
|
||||
};
|
||||
|
||||
when(joinPoint.getSignature()).thenReturn(signature);
|
||||
when(signature.toShortString()).thenReturn("SysUserHandler.createUser");
|
||||
when(joinPoint.getArgs()).thenReturn(new Object[]{"test"});
|
||||
when(joinPoint.proceed()).thenReturn(Mono.just("success"));
|
||||
when(logService.save(any(OperationLog.class))).thenReturn(Mono.just(new OperationLog()));
|
||||
|
||||
Object result = aspect.around(joinPoint, annotation);
|
||||
|
||||
StepVerifier.create((Mono<?>) result)
|
||||
.expectNext("success")
|
||||
.verifyComplete();
|
||||
|
||||
verify(logService, timeout(1000)).save(any(OperationLog.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAround_WithException_ShouldSaveErrorLog() throws Throwable {
|
||||
OperationLog annotation = new OperationLog() {
|
||||
@Override
|
||||
public String operation() {
|
||||
return "删除用户";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String module() {
|
||||
return "用户管理";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends java.lang.annotation.Annotation> annotationType() {
|
||||
return OperationLog.class;
|
||||
}
|
||||
};
|
||||
|
||||
when(joinPoint.getSignature()).thenReturn(signature);
|
||||
when(signature.toShortString()).thenReturn("SysUserHandler.deleteUser");
|
||||
when(joinPoint.getArgs()).thenReturn(new Object[]{1L});
|
||||
when(joinPoint.proceed()).thenThrow(new RuntimeException("删除失败"));
|
||||
|
||||
try {
|
||||
aspect.around(joinPoint, annotation);
|
||||
} catch (RuntimeException e) {
|
||||
assert e.getMessage().equals("删除失败");
|
||||
}
|
||||
|
||||
verify(logService, timeout(1000)).save(any(OperationLog.class));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: 运行测试验证**
|
||||
|
||||
运行: `cd novalon-manage-api && ./mvnw test -Dtest=OperationLogAspectTest -pl manage-sys`
|
||||
|
||||
预期: 测试通过
|
||||
|
||||
**Step 3: 提交**
|
||||
|
||||
```bash
|
||||
git add novalon-manage-api/manage-sys/src/test/java/cn/novalon/manage/sys/audit/OperationLogAspectTest.java
|
||||
git commit -m "test: add unit tests for OperationLogAspect"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 4: 在用户管理Handler上添加注解
|
||||
|
||||
**文件:**
|
||||
- 修改: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/handler/user/SysUserHandler.java`
|
||||
|
||||
**Step 1: 在createUser方法上添加注解**
|
||||
|
||||
找到 `createUser` 方法,在方法上添加:
|
||||
|
||||
```java
|
||||
@OperationLog(operation = "创建用户", module = "用户管理")
|
||||
public Mono<ServerResponse> createUser(ServerRequest request) {
|
||||
// 现有代码保持不变
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: 在updateUser方法上添加注解**
|
||||
|
||||
找到 `updateUser` 方法,在方法上添加:
|
||||
|
||||
```java
|
||||
@OperationLog(operation = "更新用户", module = "用户管理")
|
||||
public Mono<ServerResponse> updateUser(ServerRequest request) {
|
||||
// 现有代码保持不变
|
||||
}
|
||||
```
|
||||
|
||||
**Step 3: 在deleteUser方法上添加注解**
|
||||
|
||||
找到 `deleteUser` 方法,在方法上添加:
|
||||
|
||||
```java
|
||||
@OperationLog(operation = "删除用户", module = "用户管理")
|
||||
public Mono<ServerResponse> deleteUser(ServerRequest request) {
|
||||
// 现有代码保持不变
|
||||
}
|
||||
```
|
||||
|
||||
**Step 4: 在changePassword方法上添加注解**
|
||||
|
||||
找到 `changePassword` 方法,在方法上添加:
|
||||
|
||||
```java
|
||||
@OperationLog(operation = "修改密码", module = "用户管理")
|
||||
public Mono<ServerResponse> changePassword(ServerRequest request) {
|
||||
// 现有代码保持不变
|
||||
}
|
||||
```
|
||||
|
||||
**Step 5: 在assignRoles方法上添加注解**
|
||||
|
||||
找到 `assignRoles` 方法,在方法上添加:
|
||||
|
||||
```java
|
||||
@OperationLog(operation = "分配角色", module = "用户管理")
|
||||
public Mono<ServerResponse> assignRoles(ServerRequest request) {
|
||||
// 现有代码保持不变
|
||||
}
|
||||
```
|
||||
|
||||
**Step 6: 验证修改**
|
||||
|
||||
运行: `cd novalon-manage-api && ./mvnw compile -pl manage-sys`
|
||||
|
||||
预期: 编译成功
|
||||
|
||||
**Step 7: 提交**
|
||||
|
||||
```bash
|
||||
git add novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/handler/user/SysUserHandler.java
|
||||
git commit -m "feat: add @OperationLog annotations to user management operations"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 5: 在角色管理Handler上添加注解
|
||||
|
||||
**文件:**
|
||||
- 修改: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/handler/role/SysRoleHandler.java`
|
||||
|
||||
**Step 1: 在createRole方法上添加注解**
|
||||
|
||||
```java
|
||||
@OperationLog(operation = "创建角色", module = "角色管理")
|
||||
public Mono<ServerResponse> createRole(ServerRequest request) {
|
||||
// 现有代码保持不变
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: 在updateRole方法上添加注解**
|
||||
|
||||
```java
|
||||
@OperationLog(operation = "更新角色", module = "角色管理")
|
||||
public Mono<ServerResponse> updateRole(ServerRequest request) {
|
||||
// 现有代码保持不变
|
||||
}
|
||||
```
|
||||
|
||||
**Step 3: 在deleteRole方法上添加注解**
|
||||
|
||||
```java
|
||||
@OperationLog(operation = "删除角色", module = "角色管理")
|
||||
public Mono<ServerResponse> deleteRole(ServerRequest request) {
|
||||
// 现有代码保持不变
|
||||
}
|
||||
```
|
||||
|
||||
**Step 4: 验证修改**
|
||||
|
||||
运行: `cd novalon-manage-api && ./mvnw compile -pl manage-sys`
|
||||
|
||||
预期: 编译成功
|
||||
|
||||
**Step 5: 提交**
|
||||
|
||||
```bash
|
||||
git add novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/handler/role/SysRoleHandler.java
|
||||
git commit -m "feat: add @OperationLog annotations to role management operations"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 6: 在菜单管理Handler上添加注解
|
||||
|
||||
**文件:**
|
||||
- 修改: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/handler/menu/MenuHandler.java`
|
||||
|
||||
**Step 1: 在createMenu方法上添加注解**
|
||||
|
||||
```java
|
||||
@OperationLog(operation = "创建菜单", module = "菜单管理")
|
||||
public Mono<ServerResponse> createMenu(ServerRequest request) {
|
||||
// 现有代码保持不变
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: 在updateMenu方法上添加注解**
|
||||
|
||||
```java
|
||||
@OperationLog(operation = "更新菜单", module = "菜单管理")
|
||||
public Mono<ServerResponse> updateMenu(ServerRequest request) {
|
||||
// 现有代码保持不变
|
||||
}
|
||||
```
|
||||
|
||||
**Step 3: 在deleteMenu方法上添加注解**
|
||||
|
||||
```java
|
||||
@OperationLog(operation = "删除菜单", module = "菜单管理")
|
||||
public Mono<ServerResponse> deleteMenu(ServerRequest request) {
|
||||
// 现有代码保持不变
|
||||
}
|
||||
```
|
||||
|
||||
**Step 4: 验证修改**
|
||||
|
||||
运行: `cd novalon-manage-api && ./mvnw compile -pl manage-sys`
|
||||
|
||||
预期: 编译成功
|
||||
|
||||
**Step 5: 提交**
|
||||
|
||||
```bash
|
||||
git add novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/handler/menu/MenuHandler.java
|
||||
git commit -m "feat: add @OperationLog annotations to menu management operations"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 7: 运行集成测试验证
|
||||
|
||||
**Step 1: 启动后端服务**
|
||||
|
||||
运行: `cd novalon-manage-api && ./mvnw spring-boot:run -pl manage-app -Dspring-boot.run.profiles=test`
|
||||
|
||||
等待服务启动完成(约30秒)
|
||||
|
||||
**Step 2: 执行用户创建操作**
|
||||
|
||||
运行:
|
||||
```bash
|
||||
TOKEN=$(curl -s -X POST http://localhost:8084/api/auth/login -H "Content-Type: application/json" -d '{"username":"e2e_test_user","password":"admin123"}' | grep -o '"token":"[^"]*' | cut -d'"' -f4)
|
||||
|
||||
curl -X POST http://localhost:8084/api/users -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{"username":"test_op_log","password":"Test123!@#","email":"test@example.com","phone":"13900139001","nickname":"测试操作日志"}'
|
||||
```
|
||||
|
||||
预期: 用户创建成功
|
||||
|
||||
**Step 3: 验证操作日志已记录**
|
||||
|
||||
运行:
|
||||
```bash
|
||||
curl -X GET "http://localhost:8084/api/logs/operation/count" -H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
预期: 返回值大于0
|
||||
|
||||
**Step 4: 查看操作日志详情**
|
||||
|
||||
运行:
|
||||
```bash
|
||||
curl -X GET "http://localhost:8084/api/logs/operation" -H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
预期: 返回包含"创建用户"操作的日志记录
|
||||
|
||||
**Step 5: 停止后端服务**
|
||||
|
||||
按 Ctrl+C 停止服务
|
||||
|
||||
---
|
||||
|
||||
## Task 8: 运行E2E测试验证
|
||||
|
||||
**Step 1: 启动前端和后端服务**
|
||||
|
||||
运行:
|
||||
```bash
|
||||
# 终端1: 启动后端
|
||||
cd novalon-manage-api && ./mvnw spring-boot:run -pl manage-app -Dspring-boot.run.profiles=test
|
||||
|
||||
# 终端2: 启动前端
|
||||
cd novalon-manage-web && pnpm dev
|
||||
```
|
||||
|
||||
等待服务启动完成
|
||||
|
||||
**Step 2: 运行E2E测试**
|
||||
|
||||
运行: `cd novalon-manage-web && npx playwright test e2e/user-management.spec.ts --project=chromium`
|
||||
|
||||
预期: 测试通过
|
||||
|
||||
**Step 3: 手动验证Dashboard**
|
||||
|
||||
1. 打开浏览器访问 http://localhost:3002
|
||||
2. 登录系统(用户名: e2e_test_user, 密码: admin123)
|
||||
3. 执行用户管理操作(创建、更新、删除用户)
|
||||
4. 查看Dashboard操作日志数量是否增加
|
||||
|
||||
预期: 操作日志数量随操作增加
|
||||
|
||||
**Step 4: 停止服务**
|
||||
|
||||
按 Ctrl+C 停止所有服务
|
||||
|
||||
---
|
||||
|
||||
## Task 9: 最终验证和提交
|
||||
|
||||
**Step 1: 运行所有后端测试**
|
||||
|
||||
运行: `cd novalon-manage-api && ./mvnw test`
|
||||
|
||||
预期: 所有测试通过
|
||||
|
||||
**Step 2: 检查代码质量**
|
||||
|
||||
运行: `cd novalon-manage-api && ./mvnw checkstyle:check`
|
||||
|
||||
预期: 检查通过
|
||||
|
||||
**Step 3: 更新README文档**
|
||||
|
||||
在 `README.md` 中添加操作日志功能说明:
|
||||
|
||||
```markdown
|
||||
## 操作日志功能
|
||||
|
||||
系统自动记录关键业务操作,包括:
|
||||
- 用户管理:创建、更新、删除用户、修改密码、分配角色
|
||||
- 角色管理:创建、更新、删除角色
|
||||
- 菜单管理:创建、更新、删除菜单
|
||||
|
||||
操作日志可在Dashboard中查看,用于系统审计和问题追踪。
|
||||
```
|
||||
|
||||
**Step 4: 最终提交**
|
||||
|
||||
```bash
|
||||
git add README.md
|
||||
git commit -m "docs: update README with operation log feature description"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 完成标准
|
||||
|
||||
- ✅ 所有单元测试通过
|
||||
- ✅ 所有集成测试通过
|
||||
- ✅ E2E测试通过
|
||||
- ✅ Dashboard操作日志数量正常显示
|
||||
- ✅ 代码质量检查通过
|
||||
- ✅ 文档更新完成
|
||||
- ✅ 所有代码已提交到Git
|
||||
Reference in New Issue
Block a user