spring boot 存储操作日志(mysql) 您所在的位置:网站首页 springboot实现操作日志 spring boot 存储操作日志(mysql)

spring boot 存储操作日志(mysql)

2023-03-23 01:21| 来源: 网络整理| 查看: 265

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; } } ​ 复制代码 Mapper

BaseMapper 提供了许多单表操作的基础方法

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 {} 复制代码 Service

IService 定义了基础的业务方法,需要实现

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 {} 复制代码 ServiceImpl

ServiceImpl 实现了基础的业务方法

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; } } ​ 复制代码 使用 UserController

success 静态方法是对 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 接口

image-20221019191923290.png

请求登陆接口

image-20221019191512697.png

数据库存储

image-20221019191810470.png

Fail 接口

image-20221019192322103.png

数据存储

image-20221019192419600.png



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有