spring boot 存储操作日志(mysql) | 您所在的位置:网站首页 › springboot实现操作日志 › spring boot 存储操作日志(mysql) |
spring boot 存储操作日志(mysql)
记录聊天app, 操作日志存储实现 操作日志实体类 SQL(table 数据表) create table if not exists `br_operate_log` ( `operate_type` smallint unsigned null comment '操作人类别', `business_type` smallint unsigned null comment '操作功能', `method` varchar(16) null comment '请求方法', `topic` varchar(32) null comment '主题', `summary` varchar(120) null comment '概况', `notes` varchar(255) null comment '备注', `url` varchar(255) null comment '请求url', `params` varchar(514) null comment '请求参数', `response` varchar(2048) null comment '请求响应体', `is_success` boolean null comment '成功处理', `id` bigint(20) unsigned primary key, `create_at` datetime null comment '创建时间', `create_by` bigint unsigned null comment '创建人', `create_ip` varchar(64) null comment '创建ip', `create_address` varchar(64) null comment '创建ip地址 "国家|区域|省|市|宽带"', `update_at` datetime null comment '最后更新时间', `update_by` bigint unsigned null comment '最后更新人', `update_ip` varchar(64) null comment '最后更新ip', `update_address` varchar(64) null comment '最后更新ip地址 "国家|区域|省|市|宽带"', `remark` varchar(256) null comment '备注', `version` int default 0 null comment '乐观锁', `is_deleted` boolean default false comment '逻辑删除 0 未删除 `null` 删除', `is_status` boolean default true comment '是否启用 0 关闭 1 开启' ) auto_increment 100 comment '日志记录表'; 复制代码 BaseeEntity(基础实体类) package com.bluereba.br_pojo.entity; import com.baomidou.mybatisplus.annotation.*; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.io.Serializable; import java.time.LocalDateTime; /** * @author tuuuuuun * @since 2022/10/10 0:12 */ @Data @ApiModel(value = "基础实体类", description = "") public class BaseEntity implements Serializable { private static final long serialVersionUID = 1L; @TableId(value = "id", type = IdType.ASSIGN_ID) private Long id; @ApiModelProperty("创建时间") @TableField(value = "create_at", fill = FieldFill.INSERT) private LocalDateTime createAt; @ApiModelProperty("创建人") @TableField(value = "create_by", fill = FieldFill.INSERT) private Long createBy; @ApiModelProperty("创建ip") @TableField(value = "create_ip", fill = FieldFill.INSERT) private String createIp; @ApiModelProperty("创建ip地址 "国家|区域|省|市|宽带"") @TableField(value = "create_address", fill = FieldFill.INSERT) private String createAddress; @ApiModelProperty("最后更新时间") @TableField(value = "update_at", fill = FieldFill.INSERT_UPDATE) private LocalDateTime updateAt; @ApiModelProperty("最后更新人") @TableField(value = "update_by", fill = FieldFill.INSERT_UPDATE) private Long updateBy; @ApiModelProperty("最后更新ip") @TableField(value = "update_ip", fill = FieldFill.INSERT_UPDATE) private String updateIp; @ApiModelProperty("最后更新ip地址 "国家|区域|省|市|宽带"") @TableField(value = "update_address", fill = FieldFill.INSERT_UPDATE) private String updateAddress; @ApiModelProperty("备注") @TableField("remark") private String remark; @ApiModelProperty("乐观锁") @TableField("version") @Version private Integer version; @ApiModelProperty("逻辑删除 0 未删除 `null` 删除") @TableField("is_deleted") @TableLogic private Boolean isDeleted; @ApiModelProperty("是否启用 0 关闭 1 开启") @TableField("is_status") private Boolean isStatus; } 复制代码 OperateLogEntity(日志实体类) package com.bluereba.br_pojo.entity; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import com.bluereba.br_pojo.entity.type.BusinessType; import com.bluereba.br_pojo.entity.type.OperatorType; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.*; /** ** 日志记录表 * * * @author tuuuuuun * @since 2022-10-19 02:09:15 */ @Data @Builder @NoArgsConstructor @AllArgsConstructor @TableName("br_operate_log") @EqualsAndHashCode(callSuper = true) @ApiModel(value = "日志实体", description = "记录操作日志") public class OperateLogEntity extends BaseEntity { private static final long serialVersionUID = 1L; /** * 操作参数 */ @TableField("operate_type") @ApiModelProperty(value = "操作人类别", example = "", notes = "") private OperatorType operateType; @TableField("business_type") @ApiModelProperty(value = "操作功能", example = "", notes = "") private BusinessType businessType; @TableField("is_success") @ApiModelProperty(value = "成功处理", example = "", notes = "") private Boolean isSuccess; @TableField("topic") @ApiModelProperty(value = "操作模块", example = "", notes = "") private String topic; @TableField("summary") @ApiModelProperty(value = "操作概括", example = "", notes = "") private String summary; @TableField("notes") @ApiModelProperty(value = "操作说明", example = "", notes = "") private String notes; /** * 请求参数 */ @TableField("url") @ApiModelProperty(value = "请求url", example = "", notes = "") private String url; @TableField("method") @ApiModelProperty(value = "请求方法", example = "", notes = "") private String method; @TableField("params") @ApiModelProperty(value = "请求参数", example = "", notes = "") private String params; /** * 响应参数 */ @ApiModelProperty(value = "响应实体", example = "", notes = "") private String response; } 复制代码 MybatisPlus Config@MapperScan("com.bluereba.br_dao.mapper") 指定 mapper 包路径,插件无所谓。也可以直接放到Spring Boot 启动类上 package com.bluereba.br_stomp.config; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * MybatisPlus 配置类 * * @author tuuuuuun * @since 2022/7/15 15:13 */ @Configuration @MapperScan("com.bluereba.br_dao.mapper") public class MyBatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // TODO.1 分页器 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // TODO.2 乐观锁 interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); // TODO.3 阻断全表更新、删除的操作 interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); return interceptor; } } 复制代码 MapperBaseMapper 提供了许多单表操作的基础方法 package com.bluereba.br_dao.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.bluereba.br_pojo.entity.OperateLogEntity; /** ** 日志记录表 Mapper 接口 * * * @author tuuuuuun * @since 2022-10-19 02:09:15 */ public interface OperateLogMapper extends BaseMapper {} 复制代码 ServiceIService 定义了基础的业务方法,需要实现 package com.bluereba.br_service.service; import com.baomidou.mybatisplus.extension.service.IService; import com.bluereba.br_pojo.entity.OperateLogEntity; /** ** 日志记录表 服务类 * * * @author tuuuuuun * @since 2022-10-19 02:09:15 */ public interface OperateLogService extends IService {} 复制代码 ServiceImplServiceImpl 实现了基础的业务方法 package com.bluereba.br_service.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.bluereba.br_dao.mapper.OperateLogMapper; import com.bluereba.br_pojo.entity.OperateLogEntity; import com.bluereba.br_service.service.OperateLogService; import org.springframework.stereotype.Service; /** ** 日志记录表 服务实现类 * * * @author tuuuuuun * @since 2022-10-19 02:09:15 */ @Service public class OperateLogServiceImpl extends ServiceImpl implements OperateLogService {} 复制代码 Aop OperatorType 枚举 (操作人类型)@EnumValue 指定数据库存储的字段 @JsonValue 响应前端的字段 package com.bluereba.br_pojo.entity.type; import com.baomidou.mybatisplus.annotation.EnumValue; import com.fasterxml.jackson.annotation.JsonValue; import lombok.AllArgsConstructor; import lombok.Getter; /** * @author tuuuuuun * @since 2022/10/18 6:36 */ @Getter @AllArgsConstructor public enum OperatorType { /** * 操作人类型 */ USER(1, "user"), GROUP(2, "group"), SYSTEM(3, "system"); @EnumValue private final Integer id; @JsonValue private final String type; } 复制代码 BusinessType (操作动作 类型)@EnumValue 指定数据库存储的字段 @JsonValue 响应前端的字段 package com.bluereba.br_pojo.entity.type; import com.baomidou.mybatisplus.annotation.EnumValue; import com.fasterxml.jackson.annotation.JsonValue; import lombok.AllArgsConstructor; import lombok.Getter; /** * @author tuuuuuun * @since 2022/10/18 6:36 */ @Getter @AllArgsConstructor public enum BusinessType { /** * 操作动作 */ INSERT(1, "新增操作"), LOGIN(2, "登录操作"), LOGOUT(3, "登出操作"), UPDATE(4, "编辑操作"), DELETE(5, "删除操作"), FORCE(6, "强退"), IMPORT(7, "导入"), OTHER(8, "其他"), GENERATE(9, "生成代码"), CLEAN(10, "清空数据"), GRANT(11, "授权"); @EnumValue private final Integer id; @JsonValue private final String type; } 复制代码 操作日志注解 package com.bluereba.br_operate_log; import com.bluereba.br_pojo.entity.type.BusinessType; import com.bluereba.br_pojo.entity.type.OperatorType; import java.lang.annotation.*; /** * @author tuuuuuun * @since 2022/10/18 6:32 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface OperateLog { // 操作模块 String topic() default ""; // 操作概括 String summary() default ""; // 操作说明 String notes() default ""; // 操作功能(OTHER其他、INSERT新增、UPDATE修改、DELETE删除、GRANT授权、EXPORT导出、IMPORT导入、FORCE强退、GENCODE生成代码、CLEAN清空数据) BusinessType businessType() default BusinessType.OTHER; // 操作人类型(OTHER其他、MANAGE后台用户、MOBILE手机端用户) OperatorType operatorType() default OperatorType.USER; // 保存请求的参数 boolean request() default true; // 保存响应的参数 boolean response() default true; // 保存详情数据 {{@code ApiEntity }} save ? data boolean detail() default false; } 复制代码 OperateHelper(构建日志实体类辅助类) 响应参数 请求参数 操作参数 ApiEntity(响应实体类) package com.bluereba.br_common.api; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.time.LocalDateTime; /** * @author tuuuuuun * @since 2022/7/15 16:05 */ @Data @ApiModel("响应对象") @AllArgsConstructor @NoArgsConstructor public class ApiEntity { @ApiModelProperty(value = "状态码") Integer status; @ApiModelProperty(value = "消息") String message; @ApiModelProperty(value = "数据") T data; @ApiModelProperty(value = "时间") LocalDateTime timestamp; public ApiEntity(Integer status, T data) { this.status = status; this.data = data; } public ApiEntity(Integer status, String message, T data) { this.status = status; this.message = message; this.data = data; } public ApiEntity(Integer status, String message) { this.status = status; this.message = message; } } 复制代码 package com.bluereba.br_operate_log.utils; import com.bluereba.br_common.api.ApiEntity; import com.bluereba.br_operate_log.OperateLog; import com.bluereba.br_pojo.entity.OperateLogEntity; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.AllArgsConstructor; import lombok.NoArgsConstructor; import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.util.Optional; /** * 日志实体构建 辅助类 * * @author tuuuuuun * @since 2022/10/18 20:15 */ @AllArgsConstructor @NoArgsConstructor @Component public class OperateHelper { private static ObjectMapper objectMapper; private OperateLogEntity.OperateLogEntityBuilder builder = null; private JoinPoint joinPoint = null; private ApiEntity result = null; public static OperateHelper builder(@NonNull JoinPoint joinPoint, @Nullable ApiEntity result ) { return new OperateHelper(OperateLogEntity.builder(), joinPoint, result); } /** * 获取 {{@code ApiOperation}} 注解 * * @return ApiOperation annotation */ public Optional getApiOperationAnnotation() { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); ApiOperation operation = signature.getMethod().getAnnotation(ApiOperation.class); return Optional.ofNullable(operation); } /** * 获取 {{@code Api}} 注解 * * @return Api annotation */ public Optional getApiAnnotation() { Api api = joinPoint.getTarget().getClass() .getAnnotation(Api.class); return Optional.ofNullable(api); } /** * 获取 {{@code OperateLog}} 注解 * * @return OperateLog annotation */ public Optional getOperateLogAnnotation() { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); OperateLog operateLog = signature.getMethod().getAnnotation(OperateLog.class); return Optional.ofNullable(operateLog); } /** * 存储请求数据 * * @return 结果 */ public boolean saveRequestData() { Optional operateLog = getOperateLogAnnotation(); if (operateLog.isPresent()) { OperateLog operate = operateLog.get(); // 存储 请求参数 return operate.response(); } return false; } /** * 存储响应数据 * * @return 结果 */ public boolean saveResponseData() { Optional operateLog = getOperateLogAnnotation(); if (operateLog.isPresent()) { OperateLog operate = operateLog.get(); // 存储 响应参数 return operate.response(); } return false; } @Autowired public void setObjectMapper(ObjectMapper objectMapper) { OperateHelper.objectMapper = objectMapper; } /** * 操作参数 */ public OperateHelper operate() { getOperateLogAnnotation().ifPresent(annotation -> { // 操作功能 builder.businessType(annotation.businessType()); // 操作人类型 builder.operateType(annotation.operatorType()); // 日志主题 builder.topic(annotation.topic()); // 日志概括 builder.summary(annotation.summary()); // 日志说明 builder.notes(annotation.notes()); }); // 覆盖操作 getApiAnnotation().ifPresent(annotation -> { String topic = StringUtils.join(annotation.tags(), "-"); if (StringUtils.isNotBlank(topic)) { // 日志主题 builder.topic(topic); } }); // 覆盖操作 getApiOperationAnnotation().ifPresent(annotation -> { // 日志概括 if (StringUtils.isNotBlank(annotation.value())) { builder.summary(annotation.value()); } // 日志说明 if (StringUtils.isNotBlank(annotation.notes())) { builder.notes(annotation.notes()); } }); return this; } /** * 请求参数 */ public OperateHelper request() throws Exception { if (!saveRequestData()) { return this; } ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (requestAttributes != null) { HttpServletRequest request = requestAttributes.getRequest(); Object[] requestParam = joinPoint.getArgs(); String method = request.getMethod(); String url = request.getRequestURI(); String requestParams = objectMapper.writeValueAsString(requestParam); builder .params(requestParams) .method(method) .url(url); } return this; } /** * 响应参数 */ public OperateHelper response() throws JsonProcessingException { if (!saveResponseData()) { return this; } // 响应实体 Boolean saveDetail = getOperateLogAnnotation() .map(OperateLog::detail) .orElse(false); String result; if (!saveDetail) { // copy result, 直接修改默认的 result,会影响返回给前端的数据 ApiEntity copyEntity = new ApiEntity(); BeanUtils.copyProperties(this.result, copyEntity); copyEntity.setData("*"); result = objectMapper.writeValueAsString(copyEntity); } else { result = objectMapper.writeValueAsString(this.result); } builder.response(result); return this; } public OperateHelper success() { builder.isSuccess(true); return this; } public OperateHelper fail() { builder.isSuccess(false); return this; } public OperateLogEntity build() { return builder.build(); } } 复制代码 Mybatis 自动填充 填充基础实体类字段(用户参数,创建、更新参数) PrincipalContextHolder获取 Spring Security 上下文用户配置 注: UserDetailsEntity(实现了 UserDetails) package com.bluereba.br_service.utils; import com.bluereba.br_pojo.entity.security.UserDetailsEntity; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import java.util.Optional; /** * @author tuuuuuun * @since 2022/10/16 2:28 */ public class PrincipalContextHolder { /** * 获取 当前用户 ID * * @return 用户 id */ public static Long getUserId() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication != null) { Object principal = authentication.getPrincipal(); if (principal instanceof UserDetailsEntity) { UserDetailsEntity userDetails = (UserDetailsEntity) authentication.getPrincipal(); return userDetails.getUid(); } } return null; } public static Optional getUserIdOpt() { return Optional.ofNullable(getUserId()); } } 复制代码 MyMetaObjectHandler当插入实体时,会调用 insertFill 方法 当更新实体时,会调用 updateFill 方法 注: 只有构建了实体对象才会调用方法 package com.bluereba.br_web.handler; import cn.hutool.extra.servlet.ServletUtil; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import com.bluereba.br_common.utils.AddressTemplate; import com.bluereba.br_service.utils.PrincipalContextHolder; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; import org.apache.ibatis.reflection.MetaObject; import org.lionsoul.ip2region.xdb.Searcher; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.time.LocalDateTime; /** * mybatis 自动填充, 可能会有填充问题 * * @author tuuuuuun * @since 2022/7/15 17:54 */ @Slf4j @Component @RequiredArgsConstructor public class MyMetaObjectHandler implements MetaObjectHandler { private final HttpServletRequest httpServletRequest; private final Searcher searcher; private final AddressTemplate addressTemplate; @Override public void insertFill(MetaObject metaObject) { PrincipalContextHolder.getUserIdOpt().ifPresent(uid -> { // 填充创建人,更新人 this.strictInsertFill(metaObject, "createBy", Long.class, uid); this.strictInsertFill(metaObject, "updateBy", Long.class, uid); }); // 填充创建时间 和 更新时间 this.strictInsertFill(metaObject, "createAt", LocalDateTime::now, LocalDateTime.class); this.strictInsertFill(metaObject, "updateAt", LocalDateTime::now, LocalDateTime.class); // 填充 ip 已经 ip address try { // 糊涂包 工具类, 获取实际请求ip (包括nginx代理) String ip = ServletUtil.getClientIP(httpServletRequest); // ip2region 包, 离线将 ip 转 地址("国家|区域|省|市|宽带") String address = searcher.search(ip); this.strictInsertFill(metaObject, "createIp", String.class, ip); this.strictInsertFill(metaObject, "createAddress", String.class, address); this.strictInsertFill(metaObject, "updateIp", String.class, ip); this.strictInsertFill(metaObject, "updateAddress", String.class, address); } catch (Exception e) { log.error(e.getMessage(), e); } } @Override public void updateFill(MetaObject metaObject) { PrincipalContextHolder.getUserIdOpt().ifPresent(uid -> { // 填充创建人,更新人 this.strictUpdateFill(metaObject, "updateBy", Long.class, uid); }); // 填充 ip 已经 ip address try { String ip = ServletUtil.getClientIP(httpServletRequest); String address = searcher.search(ip); this.strictUpdateFill(metaObject, "updateIp", String.class, ip); this.strictUpdateFill(metaObject, "updateAddress", String.class, address); } catch (Exception e) { log.error(e.getMessage(), e); } // 填充更新时间 /*this.strictUpdateFill(metaObject, "updateAt", LocalDateTime::now, LocalDateTime.class);*/ this.setFieldValByName("updateAt", LocalDateTime.now(), metaObject); } } 复制代码 Aspect package com.bluereba.br_operate_log.aspect; import com.bluereba.br_common.api.ApiEntity; import com.bluereba.br_operate_log.utils.OperateHelper; import com.bluereba.br_pojo.entity.OperateLogEntity; import com.bluereba.br_service.service.OperateLogService; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; /** * @author tuuuuuun * @since 2022/3/2 20:43 */ @Slf4j @Aspect @Component @RequiredArgsConstructor public class OperateLogAspect { private final OperateLogService operateLogService; /** * 通过注解类标识 控制操作 (OperateLog 注解标识) */ @Pointcut("@annotation(com.bluereba.br_operate_log.OperateLog)") public void operateLog() { } /** * 方法成功执行 */ @Async @SneakyThrows @AfterReturning(value = "operateLog()", returning = "result") public void afterReturnOperate(JoinPoint joinPoint, ApiEntity result) { OperateLogEntity operateEntity = OperateHelper.builder(joinPoint, result) .request() .response() .operate() .success() .build(); // 由 mybatis plus 提供 向数据库中插入一条数据 operateLogService.save(operateEntity); } /** * 方法执行中异常 */ @Async @SneakyThrows @AfterThrowing(pointcut = "operateLog()", throwing = "e") public void afterThrowingOperate(JoinPoint joinPoint, Throwable e) { OperateLogEntity operateEntity = OperateHelper.builder(joinPoint, null) .request() .operate() .fail() .build(); // 由 mybatis plus 提供 向数据库中插入一条数据 operateLogService.save(operateEntity); } } 复制代码 处理 RestController 返回的 ApiEntity 实体 当消息为空 或 消息为默认消息 将替换为 swagger注解 {{@ApiOperation value}} 值 package com.bluereba.br_common.handler; import com.bluereba.br_common.api.ApiEntity; import com.bluereba.br_common.api.type.base.ApiEnum; import io.swagger.annotations.ApiOperation; import org.apache.commons.lang3.StringUtils; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import java.time.LocalDateTime; /** * @author tuuuuuun * @since 2022/10/17 21:38 */ @RestControllerAdvice public class ResponseBodyHandler implements ResponseBodyAdvice c = returnType.getParameterType(); return c == ApiEntity.class; } @Override public ApiEntity beforeBodyWrite(@Nullable ApiEntity body, @NonNull MethodParameter returnType, @NonNull MediaType selectedContentType, @NonNull Class> selectedConverterType, @NonNull ServerHttpRequest request, @NonNull ServerHttpResponse response) { if (body != null) { // 获取 {{@code ApiOperation}} 注解 ApiOperation apiOperation = returnType.getAnnotatedElement() .getAnnotation(ApiOperation.class); if (apiOperation != null) { // {{@code ApiOperation}} 注解 @value String message = apiOperation.value(); // 响应对象 消息 String bodyMessage = body.getMessage(); // 默认成功消息 String successMessage = ApiEnum.OK.getMessage(); // 当消息为空 或 消息为默认消息 将替换为 swagger注解{{@ApiOperation value}} if (StringUtils.isEmpty(bodyMessage) || successMessage.equals(bodyMessage)) { body.setMessage(message); } } body.setTimestamp(LocalDateTime.now()); } return body; } } 复制代码 使用 UserControllersuccess 静态方法是对 ApiEnity 进行的封装 package com.bluereba.br_stomp.controller; import com.bluereba.br_common.api.ApiEntity; import com.bluereba.br_operate_log.OperateLog; import com.bluereba.br_pojo.dto.UserLoginDto; import com.bluereba.br_pojo.entity.UserEntity; import com.bluereba.br_pojo.entity.type.BusinessType; import com.bluereba.br_pojo.vo.UserRegisterVo; import com.bluereba.br_service.service.UserService; import com.github.xiaoymin.knife4j.annotations.ApiSupport; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; import static com.bluereba.br_common.api.ApiRes.success; /** * @author tuuuuuun * @since 2022/10/9 19:15 */ @Api(tags = "用户模块") // Api.tags 将成为 操作日志的主题(topic) @RestController @RequestMapping("user") @RequiredArgsConstructor public class UserController { private final UserService userService; @PostMapping("/login") @ApiOperation(value = "用户登陆") // ApiOperation.value 将成为 操作日志的操作概况(summary) @OperateLog(businessType = BusinessType.LOGIN) public ApiEntity login(@RequestBody UserRegisterVo vo) { UserLoginDto dto = userService.signUp(vo); return success(dto); } @DeleteMapping("/logout") @ApiOperation(value = "用户退出") @OperateLog(businessType = BusinessType.LOGOUT) public ApiEntity logout() { userService.logout(); return success(); } @GetMapping("/info") @ApiOperation(value = "用户信息") public ApiEntity info() { UserEntity info = userService.info(); return success(info); } } 复制代码 脱敏 UserRegisterVo package com.bluereba.br_pojo.vo; import com.bluereba.br_pojo.json.ChineseNameSerialize; import com.bluereba.br_pojo.json.PasswordSerialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import javax.validation.constraints.NotNull; import java.io.Serializable; /** * 登录视图 * * @author tuuuuuun * @since 2022/10/10 0:00 */ @Data @NoArgsConstructor @AllArgsConstructor @ApiModel(value = "登陆vo", description = "") public class UserRegisterVo implements Serializable { @NotNull(message = "登陆用户名不能为空") @ApiModelProperty(value = "用户名", example = "", notes = "") @JsonSerialize(using = ChineseNameSerialize.class) private String username; @NotNull(message = "用户密码不能为空") @ApiModelProperty(value = "用户密码", example = "", notes = "") @JsonSerialize(using = PasswordSerialize.class) private String password; @ApiModelProperty(value = "记住我", example = "false", notes = "true, false, 1, 0, True, False") private Boolean readme = false; } 复制代码 ChineseNameSerialize(中文名序列化) package com.bluereba.br_pojo.json; import cn.hutool.core.util.DesensitizedUtil; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import java.io.IOException; /** * @author tuuuuuun * @since 2022/10/18 23:00 */ public class ChineseNameSerialize extends JsonSerializer { @Override public void serialize(String v, JsonGenerator g, SerializerProvider s) throws IOException { // DesensitizedUtil 糊涂包下的脱敏工具类, // chineseName 方法是对首字符后的字符串串进行 * 替换 g.writeString(DesensitizedUtil.chineseName(v)); } } 复制代码 PasswordSerialize(密码序列化) package com.bluereba.br_pojo.json; import cn.hutool.core.util.DesensitizedUtil; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import java.io.IOException; /** * @author tuuuuuun * @since 2022/10/18 23:00 */ public class PasswordSerialize extends JsonSerializer { @Override public void serialize(String v, JsonGenerator g, SerializerProvider s) throws IOException { // DesensitizedUtil 糊涂包下的脱敏工具类, // password 方法是对全字符串进行 * 替换 g.writeString(DesensitizedUtil.password(v)); } } 复制代码 效果 Success 接口 请求登陆接口 数据库存储 Fail 接口 数据存储 |
CopyRight 2018-2019 实验室设备网 版权所有 |