feat:统一公共审计填充并调整系统枚举接口风格

This commit is contained in:
zhiye.sun
2026-05-21 13:10:33 +08:00
parent 1ada88c02a
commit 088853b098
10 changed files with 238 additions and 41 deletions

View File

@@ -0,0 +1,28 @@
package com.bruce.common.config;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class EntityAuditMetaObjectHandler implements MetaObjectHandler {
private static final String SYSTEM_USER = "system";
@Override
public void insertFill(MetaObject metaObject) {
Date now = new Date();
strictInsertFill(metaObject, "createTime", Date.class, now);
strictInsertFill(metaObject, "updateTime", Date.class, now);
strictInsertFill(metaObject, "createBy", String.class, SYSTEM_USER);
strictInsertFill(metaObject, "updateBy", String.class, SYSTEM_USER);
}
@Override
public void updateFill(MetaObject metaObject) {
strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
strictUpdateFill(metaObject, "updateBy", String.class, SYSTEM_USER);
}
}

View File

@@ -9,64 +9,88 @@ import com.bruce.common.dto.response.SysEnumResponse;
import com.bruce.common.service.ISysEnumService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Tag(name = "系统枚举管理")
@Slf4j
@RestController
@RequestMapping("/api/sys-enums")
@RequestMapping("/api/sys-enum")
public class SysEnumController {
@Autowired
private ISysEnumService sysEnumService;
@Operation(summary = "查询全部系统枚举")
@GetMapping
@PostMapping("/list")
public RequestResult<List<SysEnumResponse>> list() {
return RequestResult.success(sysEnumService.listResponses());
log.info("SysEnumController.list start");
List<SysEnumResponse> responses = sysEnumService.listResponses();
log.info("SysEnumController.list success, count={}", responses.size());
return RequestResult.success(responses);
}
@Operation(summary = "根据模块和类型查询系统枚举")
@PostMapping("/query")
public RequestResult<List<SysEnumResponse>> queryByCatalogAndType(@RequestBody SysEnumQueryRequest request) {
return RequestResult.success(sysEnumService.listByCatalogAndTypeResponses(request));
log.info("SysEnumController.queryByCatalogAndType start, request={}", request);
List<SysEnumResponse> responses = sysEnumService.listByCatalogAndTypeResponses(request);
log.info("SysEnumController.queryByCatalogAndType success, count={}", responses.size());
return RequestResult.success(responses);
}
@Operation(summary = "管理端查询系统枚举")
@PostMapping("/manage/query")
@PostMapping("/queryForManagement")
public RequestResult<List<SysEnumResponse>> queryForManagement(@RequestBody(required = false) SysEnumManageQueryRequest request) {
return RequestResult.success(sysEnumService.listForManagement(request));
log.info("SysEnumController.queryForManagement start, request={}", request);
List<SysEnumResponse> responses = sysEnumService.listForManagement(request);
log.info("SysEnumController.queryForManagement success, count={}", responses.size());
return RequestResult.success(responses);
}
@Operation(summary = "查询系统枚举详情")
@GetMapping("/{id}")
public RequestResult<SysEnumResponse> getById(@PathVariable Long id) {
return RequestResult.success(sysEnumService.getResponseById(id));
@GetMapping("/detail")
public RequestResult<SysEnumResponse> getById(@RequestParam("id") Long id) {
log.info("SysEnumController.getById start, id={}", id);
SysEnumResponse response = sysEnumService.getResponseById(id);
log.info("SysEnumController.getById success, id={}, found={}", id, response != null);
return RequestResult.success(response);
}
@Operation(summary = "新增或修改系统枚举")
@PostMapping
@PostMapping("/save")
public RequestResult<Boolean> saveOrUpdate(@RequestBody SysEnumSaveRequest request) {
return RequestResult.success(sysEnumService.saveOrUpdate(request));
log.info("SysEnumController.saveOrUpdate start, request={}", request);
Boolean result = sysEnumService.saveOrUpdate(request);
log.info("SysEnumController.saveOrUpdate success, id={}, catalog={}, type={}, value={}, result={}",
request.getId(), request.getCatalog(), request.getType(), request.getValue(), result);
return RequestResult.success(result);
}
@Operation(summary = "批量新增系统枚举")
@PostMapping("/batch")
@PostMapping("/batchSave")
public RequestResult<Boolean> batchSave(@RequestBody SysEnumBatchSaveRequest request) {
return RequestResult.success(sysEnumService.batchSave(request));
log.info("SysEnumController.batchSave start, request={}", request);
Boolean result = sysEnumService.batchSave(request);
log.info("SysEnumController.batchSave success, catalog={}, type={}, itemCount={}, result={}",
request.getCatalog(), request.getType(), request.getItems() == null ? 0 : request.getItems().size(), result);
return RequestResult.success(result);
}
@Operation(summary = "删除系统枚举")
@DeleteMapping("/{id}")
public RequestResult<Boolean> deleteById(@PathVariable Long id) {
return RequestResult.success(sysEnumService.removeById(id));
@PostMapping("/delete")
public RequestResult<Boolean> deleteById(@RequestParam("id") Long id) {
log.info("SysEnumController.deleteById start, id={}", id);
Boolean result = sysEnumService.removeById(id);
log.info("SysEnumController.deleteById success, id={}, result={}", id, result);
return RequestResult.success(result);
}
}

View File

@@ -1,5 +1,6 @@
package com.bruce.common.domain.model;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
@@ -19,21 +20,21 @@ public class BaseEntity {
private Long id;
@Schema(description = "创建者")
@TableField(value = "create_by")
@TableField(value = "create_by", fill = FieldFill.INSERT)
private String createBy;
@Schema(description = "创建时间", example = "2026-05-18 20:00:00")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(value = "create_time")
@TableField(value = "create_time", fill = FieldFill.INSERT)
private Date createTime;
@Schema(description = "更新者")
@TableField(value = "update_by")
@TableField(value = "update_by", fill = FieldFill.INSERT_UPDATE)
private String updateBy;
@Schema(description = "更新时间", example = "2026-05-18 20:00:00")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(value = "update_time")
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
@Schema(description = "版本")

View File

@@ -0,0 +1,30 @@
package com.bruce.common.handler;
import com.bruce.common.domain.model.RequestResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<RequestResult<Void>> handleIllegalArgumentException(IllegalArgumentException exception) {
log.warn("GlobalExceptionHandler.handleIllegalArgumentException, message={}", exception.getMessage(), exception);
return buildResponse(HttpStatus.BAD_REQUEST, exception.getMessage());
}
@ExceptionHandler(Exception.class)
public ResponseEntity<RequestResult<Void>> handleException(Exception exception) {
log.error("GlobalExceptionHandler.handleException", exception);
return buildResponse(HttpStatus.INTERNAL_SERVER_ERROR, "系统内部错误,请稍后重试");
}
private ResponseEntity<RequestResult<Void>> buildResponse(HttpStatus status, String message) {
return ResponseEntity.status(status)
.body(RequestResult.fail(String.valueOf(status.value()), message));
}
}

View File

@@ -9,6 +9,7 @@ import com.bruce.common.dto.request.SysEnumSaveRequest;
import com.bruce.common.dto.response.SysEnumResponse;
import com.bruce.common.mapper.SysEnumMapper;
import com.bruce.common.service.ISysEnumService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
@@ -16,36 +17,47 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Slf4j
@Service
public class SysEnumServiceImpl extends ServiceImpl<SysEnumMapper, SysEnum> implements ISysEnumService {
@Override
public List<SysEnum> listByCatalogAndType(SysEnumQueryRequest request) {
log.info("SysEnumServiceImpl.listByCatalogAndType start, request={}", request);
if (request == null) {
throw new IllegalArgumentException("查询请求不能为空");
}
return lambdaQuery()
List<SysEnum> result = lambdaQuery()
.eq(StringUtils.hasText(request.getCatalog()), SysEnum::getCatalog, request.getCatalog())
.eq(StringUtils.hasText(request.getType()), SysEnum::getType, request.getType())
.orderByAsc(SysEnum::getSort)
.list();
log.info("SysEnumServiceImpl.listByCatalogAndType success, count={}", result.size());
return result;
}
@Override
public List<SysEnumResponse> listResponses() {
return toResponses(list());
log.info("SysEnumServiceImpl.listResponses start");
List<SysEnumResponse> responses = toResponses(list());
log.info("SysEnumServiceImpl.listResponses success, count={}", responses.size());
return responses;
}
@Override
public List<SysEnumResponse> listByCatalogAndTypeResponses(SysEnumQueryRequest request) {
return toResponses(listByCatalogAndType(request));
log.info("SysEnumServiceImpl.listByCatalogAndTypeResponses start");
List<SysEnumResponse> responses = toResponses(listByCatalogAndType(request));
log.info("SysEnumServiceImpl.listByCatalogAndTypeResponses success, count={}", responses.size());
return responses;
}
@Override
public List<SysEnumResponse> listForManagement(SysEnumManageQueryRequest request) {
log.info("SysEnumServiceImpl.listForManagement start, request={}", request);
SysEnumManageQueryRequest queryRequest = request == null ? new SysEnumManageQueryRequest() : request;
String keyword = queryRequest.getKeyword();
return toResponses(lambdaQuery()
List<SysEnumResponse> responses = toResponses(lambdaQuery()
.eq(StringUtils.hasText(queryRequest.getCatalog()), SysEnum::getCatalog, queryRequest.getCatalog())
.eq(StringUtils.hasText(queryRequest.getType()), SysEnum::getType, queryRequest.getType())
.and(StringUtils.hasText(keyword), wrapper -> wrapper
@@ -63,15 +75,21 @@ public class SysEnumServiceImpl extends ServiceImpl<SysEnumMapper, SysEnum> impl
.orderByAsc(SysEnum::getSort)
.orderByAsc(SysEnum::getId)
.list());
log.info("SysEnumServiceImpl.listForManagement success, count={}", responses.size());
return responses;
}
@Override
public SysEnumResponse getResponseById(Long id) {
return SysEnumResponse.fromEntity(getById(id));
log.info("SysEnumServiceImpl.getResponseById start, id={}", id);
SysEnumResponse response = SysEnumResponse.fromEntity(getById(id));
log.info("SysEnumServiceImpl.getResponseById success, id={}, found={}", id, response != null);
return response;
}
@Override
public boolean saveOrUpdate(SysEnumSaveRequest request) {
log.info("SysEnumServiceImpl.saveOrUpdate start, request={}", request);
if (request == null) {
throw new IllegalArgumentException("保存请求不能为空");
}
@@ -85,11 +103,15 @@ public class SysEnumServiceImpl extends ServiceImpl<SysEnumMapper, SysEnum> impl
sysEnum.setStrvalue(request.getStrvalue());
sysEnum.setSort(request.getSort());
sysEnum.setRemark(request.getRemark());
return super.saveOrUpdate(sysEnum);
boolean result = super.saveOrUpdate(sysEnum);
log.info("SysEnumServiceImpl.saveOrUpdate success, id={}, catalog={}, type={}, value={}, result={}",
request.getId(), request.getCatalog(), request.getType(), request.getValue(), result);
return result;
}
@Override
public boolean batchSave(SysEnumBatchSaveRequest request) {
log.info("SysEnumServiceImpl.batchSave start, request={}", request);
List<SysEnum> existingEnums = lambdaQuery()
.eq(request != null && StringUtils.hasText(request.getCatalog()), SysEnum::getCatalog, request == null ? null : request.getCatalog())
.eq(request != null && StringUtils.hasText(request.getType()), SysEnum::getType, request == null ? null : request.getType())
@@ -109,10 +131,14 @@ public class SysEnumServiceImpl extends ServiceImpl<SysEnumMapper, SysEnum> impl
return sysEnum;
})
.toList();
return saveBatch(enums);
boolean result = saveBatch(enums);
log.info("SysEnumServiceImpl.batchSave success, catalog={}, type={}, itemCount={}, result={}",
request.getCatalog(), request.getType(), enums.size(), result);
return result;
}
public void validateBatchSaveRequest(SysEnumBatchSaveRequest request, List<SysEnum> existingEnums) {
log.info("SysEnumServiceImpl.validateBatchSaveRequest start");
if (request == null) {
throw new IllegalArgumentException("批量保存请求不能为空");
}
@@ -153,6 +179,8 @@ public class SysEnumServiceImpl extends ServiceImpl<SysEnumMapper, SysEnum> impl
throw new IllegalArgumentException("枚举值已存在: " + value);
}
}
log.info("SysEnumServiceImpl.validateBatchSaveRequest success, catalog={}, type={}, requestValueCount={}, existingValueCount={}",
request.getCatalog(), request.getType(), requestValues.size(), existingValues.size());
}
private List<SysEnumResponse> toResponses(List<SysEnum> enums) {

View File

@@ -0,0 +1,36 @@
package com.bruce.common.config;
import com.bruce.rag.entity.RagStore;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class EntityAuditMetaObjectHandlerTests {
@Test
void insertFillShouldPopulateAuditFields() {
EntityAuditMetaObjectHandler handler = new EntityAuditMetaObjectHandler();
RagStore entity = new RagStore();
handler.insertFill(SystemMetaObject.forObject(entity));
assertNotNull(entity.getCreateTime());
assertNotNull(entity.getUpdateTime());
assertEquals("system", entity.getCreateBy());
assertEquals("system", entity.getUpdateBy());
}
@Test
void updateFillShouldRefreshUpdateAuditFields() {
EntityAuditMetaObjectHandler handler = new EntityAuditMetaObjectHandler();
RagStore entity = new RagStore();
entity.setUpdateBy("oldUser");
handler.updateFill(SystemMetaObject.forObject(entity));
assertNotNull(entity.getUpdateTime());
assertEquals("system", entity.getUpdateBy());
}
}

View File

@@ -1,6 +1,8 @@
package com.bruce.common.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.Version;
import com.bruce.common.domain.model.BaseEntity;
@@ -65,4 +67,21 @@ class EntityStructureTests {
assertTrue(Arrays.stream(SysEnum.class.getDeclaredFields()).noneMatch(field -> "version".equals(field.getName())));
assertTrue(Arrays.stream(SysAttachment.class.getDeclaredFields()).noneMatch(field -> "version".equals(field.getName())));
}
@Test
void auditFieldsShouldUseMybatisPlusAutoFill() throws NoSuchFieldException {
TableField createBy = BaseEntity.class.getDeclaredField("createBy").getAnnotation(TableField.class);
TableField createTime = BaseEntity.class.getDeclaredField("createTime").getAnnotation(TableField.class);
TableField updateBy = BaseEntity.class.getDeclaredField("updateBy").getAnnotation(TableField.class);
TableField updateTime = BaseEntity.class.getDeclaredField("updateTime").getAnnotation(TableField.class);
assertNotNull(createBy);
assertNotNull(createTime);
assertNotNull(updateBy);
assertNotNull(updateTime);
assertEquals(FieldFill.INSERT, createBy.fill());
assertEquals(FieldFill.INSERT, createTime.fill());
assertEquals(FieldFill.INSERT_UPDATE, updateBy.fill());
assertEquals(FieldFill.INSERT_UPDATE, updateTime.fill());
}
}

View File

@@ -0,0 +1,25 @@
package com.bruce.common.handler;
import com.bruce.common.domain.model.RequestResult;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class GlobalExceptionHandlerTests {
@Test
void shouldReturnStructuredBadRequestForIllegalArgumentException() {
GlobalExceptionHandler handler = new GlobalExceptionHandler();
ResponseEntity<RequestResult<Void>> response = handler.handleIllegalArgumentException(
new IllegalArgumentException("知识库编码已存在: TEST-1"));
assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
assertNotNull(response.getBody());
assertEquals("400", response.getBody().getResultcode());
assertEquals("知识库编码已存在: TEST-1", response.getBody().getMessage());
assertEquals(null, response.getBody().getData());
}
}