操作日志公共组件设计实现

1.简介/定位

该组件主要定位是提供通用和个性化的 记录操作日志 功能,接入简单、可拔插、可扩展、不和业务逻辑耦合。

2.背景

系统没有进操作日志的记录(例如:某个人在xx时间进行了xx功能的操作,产生了xx结果。或者其他个性化的日志需求)。

又由于每个业务系统都有记录操作日志的需求,如果都单独开发一套操作日志记录功能,耗时耗力。

故进行该公共组件的抽取实现。

3.技术选型

  • SpringBoot(aop)
  • Spring (SPEL表达式)
  • RocketMQ(异步解耦消峰)
  • MongoDB(日志数据存储)

4.设计思路

4.1 操作日志记录内容

/**
     * 操作用户id
     */
    private Long userId;

    /**
     * 操作用户名称
     */
    private String username;

    /**
     * 操作用户账号
     */
    private String account;

    /**
     * 操作类型(增删查改)
     */
    private String operateType;

    /**
     * 操作功能简述(例如:新增员工)
     */
    private String description;

    /**
     * 操作结果(成功或者失败)
     */
    private String operateResult;

    /**
     * 操作详情(例如:员工名称不能为空)
     */
    private String detail;

    /**
     * 操作时间
     */
    private Date operateTime;

    /**
     * 操作应用
     */
    private String appName;

    /**
     * 机构id
     */
    private Long orgId;

    /**
     * 租户id
     */
    private Long tenantId;

    /**
     * 备注
     */
    private String remark;

4.2 操作日志实现思路

项目启动效果

以下是默认版本实现流程。如果不想让日志存入公共的 mogodb 库中,可以自己扩展实现

实现思路

4.3 业务系统接入方式

1.引入依赖:

<dependency>
<groupId>com.gwx</groupId> 
<artifactId>gwx-operatelog-starter</artifactId>    
<version>1.0.1-SNAPSHOT</version>        
</dependency>

2.增加yml配置

gwx-operatelog:
  enable: true
  platform: exmaple
  banner: true
  rocketmq:
    #rocketmq服务器地址
    name-server-addr:
    #rocketmq生产者组
    producer-group:

3.实现IOperatorService接口,放入当前操作人信息:

/**
 * 获取操作者
 */
public interface IOperatorService {

    /**
     * 当前用户id
     *
     * @return
     */
    Long getUserId();

    /**
     * 当前用户名称
     *
     * @return
     */
    String getUserName();

    /**
     * 当前用户账号
     *
     * @return
     */
    String getAccount();

    /**
     * 获取当前操作者
     *
     * @return
     */
    Long getOrgId();

    /**
     * 当前租户
     *
     * @return
     */
    Long getTenantId();
}

示例:

public class DefaultOperatorServiceImpl implements IOperatorService {


    @Override
    public Long getTenantId() {
        return 0L;
    }

    @Override
    public Long getUserId() {
        return 0L;
    }

    @Override
    public String getUserName() {
        return "gwx";
    }

    @Override
    public String getAccount() {
        return "123";
    }

    @Override
    public Long getOrgId() {
        return 0L;
    }
}

4.使用:接口方法增加注解

@OperateLog(description = "测试", operateType = OperateTypeEnum.QUERY, success = "测试 {{#name}}",appName = AppEnum.SAAS)
  @GetMapping("/test")
  public String test(@RequestParam String name) {
      return name;
  }

5.扩展点

5.1日志数据个性化处理

当然:如果你想对操作日志做个性化处理存储,该组件也提供了扩展接口:【ILogSaveService】

/**
 * 保存日志接口
 */
public interface ILogSaveService {

    /**
     * 保存日志
     * @param operateLogDTO
     */
    void save(OperateLogDTO operateLogDTO);

}

以下是默认实现类:

让操作日志放入rocketmq,日志服务会自动进行拉取消费入库mogodb

@Slf4j
public class DefaultLogSaveServiceImpl implements ILogSaveService {

    @Resource
    private MqProducer mqProducer;


    @Override
    public void save(OperateLogDTO operateLogDTO) {
        try {
            mqProducer.asyncSend(JSON.toJSONString(operateLogDTO),"gwx-operate-log-topic", new SendCallback() {
                @Override
                public void onSuccess(SendResult sendResult) {
                    log.info("-->gwx-operatelog操作日志消息发送成功:{}", JSON.toJSONString(sendResult));
                }

                @Override
                public void onException(Throwable throwable) {
                    log.error("-->gwx-operatelog操作日志消息发送失败", throwable.getMessage());
                }
            }, 15000L);
        }catch (Exception e){
            log.error("-->gwx-operatelog操作日志消息发送异常", e);
        }
    }

}

5.2动态日志

如果你想记录的日志是动态日志:例如我们要记录这样一条日志:员工名称由xx改为xx。

5.2.1 你需要实现自定义函数接口:

/**
 * 自定义函数接口
 *
 * @Author guwx
 * @Date 2022-06-06
 */
public interface IPraseFunction {

    /**
     * 自定义函数是否在业务代码执行之前解析
     *
     * @return
     */
    default boolean executeBefore(){
        return false;
    };

    /**
     * 自定义函数名称
     *
     * @return
     */
    String functionName();

    /**
     * 执行
     * @param param
     * @return
     */
    String apply(String param);
}

获取旧员工名称函数例子:

/**
 * 获取修改前的员工名称
 * @author gwx
 * @date 2022/06/06 14:38
 */
@Component
public class GetBeforeStaffNameByStaffId implements IPraseFunction {
    @Override
    public boolean executeBefore() {
        return true;
    }

    @Override
    public String functionName() {
        return "getBeforeStaffNameByStaffId";
    }

    @Override
    public String apply(String value) {
        //此处可以写根据staffId查询stffName的逻辑代码
        return value;
    }
}

获取新员工名称函数例子:

/**
 * 获取修改后的员工名称
 * @author gwx
 * @date 2022/06/06 14:38
 */
@Component
public class GetAfterStaffName implements IPraseFunction {
    @Override
    public boolean executeBefore() {
        return false;
    }

    @Override
    public String functionName() {
        return "getAfterStaffName";
    }

    @Override
    public String apply(String value) {
        //获取新员工名称逻辑
        return value;
    }
}

5.2.2 使用方式

@OperateLog(description = "修改员工", operateType = OperateTypeEnum.QUERY,
success = "员工名称由【{getBeforeStaffNameByStaffId{#userDto.id}}】更改为【{getAfterStaffName{#userDto.name}}】",
condition = "{{#userDto.name == 'oplog'}}",appName = AppEnum.SAAS)
@PostMapping("/test")
public String test(@RequestBody UserDto userDto) {
// 代码中修改了名称,getAfterStaffName可以拿到修改后的名称
userDto.setName(userDto.getName() + "新");
return userDto.getName();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会飞的架狗师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值