Java业务日志修改解析工具

对象对比差异解析工具

一、项目背景

对用户业务操作记录时,需要记录到具体某个字段变更,
例如:收货地址发生变更:详细地址由【"西红门2栋603"】修改成【"西红门2栋600"】
如果采用纯手动判断修改前修改后的值是否变化,则需要写如下判断:

if (val != newVal) {
  print("收货地址发生变更:详细地址由【val】修改成【newVal】")  
}

当需要判断的属性逐渐增多,则会冗余很多这种逻辑代码。故想利用Java反射来对比对象,从而对象间的获取差异。

二、实现效果

// 测试用例CompareTest
DiffResult diff = DiffUtil.resolve(GoodsOrder.class, getBefore(), getAfter());
log.info("是否有差异:{}", diff.isHasDiff());
log.info("差异内容:{}", diff.getDiffs());
true
订单编号由【"10086"】修改成【"10086001"】
自动配送由【"是"】修改成【"否"】
下单日期由【"2022-04-05"】修改成【"2022-05-05"】
订单有效时间由【"2022-03-30" - "2022-04-30"】修改成【"2022-04-30" - "2022-05-30"】
收货地址发生变更:联系人由【"肯德基"】修改成【"肯德基001"】,详细地址由【"西红门2栋603"】修改成【"西红门2栋600"】
商品列表发生变更:
商品列表,新增行【"小米手机"】
商品列表,删除行【"华为手机"】
商品列表,删除行【"苹果手机"】
商品列表,更新行【"三星手机"】,变更内容: 商品名称由【"三星手机"】修改成【"三星手机001"】,购买数量由【"20"】修改成【"22.22"】

三、接入

3.1 注解说明

3.1.1 @DiffKey

标识此属性会发生变化,无此注解属性,不记录差异

3.1.2 @UnionKey

用户列表对象,标识列表明细对象的唯一标识(可对多个属性配置)
用户判断列表明细对象,是否发生,新增、修改、删除动作

3.1.3 @UnionDisplayKey

用户列表对象新增、修改、删除动作时展示的名称(可对多个属性配置)

3.1.4 @BooleanFormat

对Boolean类型属性格式化

3.1.5 @DateFormat

对Date类型属性格式化

3.1.6 @LocalDateFormat

对LocalDate类型属性格式化

3.1.7 @LocalDateTimeFormat

对LocalDateTime类型属性格式化

3.2 完整示例


/**
 * 订单收货地址
 */
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class OrderAddress {

    @DiffKey(name = "手机号")
    private String phone;

    @DiffKey(name = "联系人")
    private String name;

    @DiffKey(name = "详细地址")
    private String address;

}


/**
 * 商品明细
 */
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class GoodsItem {

    @UnionKey
    @UnionDisplayKey
    @DiffKey(name = "商品编码")
    private String code;

    @UnionDisplayKey
    @DiffKey(name = "商品名称")
    private String name;

    @DiffKey(name = "购买数量")
    private BigDecimal count;

    @DiffKey(name = "购买金额(元)")
    private BigDecimal amount;

}
/**
 * 商品订单
 */
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class GoodsOrder {

    @DiffKey(name = "订单编号")
    private String orderNo;

    @BooleanFormat
    @DiffKey(name = "自动配送")
    private boolean autoDelivery;

    @LocalDateTimeFormat(pattern = "yyyy-MM-dd")
    @DiffKey(name = "有效开始时间", type = DiffConstant.COMBINATION_PROPERTY, combinationName = "订单有效时间")
    private LocalDateTime startTime;

    @LocalDateTimeFormat(pattern = "yyyy-MM-dd")
    @DiffKey(name = "有效结束时间", type = DiffConstant.COMBINATION_PROPERTY, combinationName = "订单有效时间")
    private LocalDateTime endTime;

    @DiffKey(name = "商品列表", type = DiffConstant.ARRAY_PROPERTY, subCls = GoodsItem.class, whenArrayFullProperties = false)
    private List<GoodsItem> items;

    @DiffKey(name = "收货地址", type = DiffConstant.OBJECT_PROPERTY)
    private OrderAddress address;

    @LocalDateFormat(pattern = "yyyy-MM-dd")
    @DiffKey(name = "下单日期")
    private LocalDate orderDate;

    @DateFormat(pattern = "yyyy-MM-dd HH")
    @DiffKey(name = "支付时间")
    private Date paymentTime;
}

四、拓展

4.1 自定义属性格式化文案

/resourcesMETA-INF/services目录下新建com.jumper.property.comparer.pattern.DiffPattern文件
可以按需重写文案规则,${val}修饰变量,重写保证原来的变量同时存在

@SPI
public class CustomDiffPattern extends DefaultDiffPattern {

    @Override
    public String getPropertyDiffPattern() {
        return "${property}值由原值【${before}】变成修改后的值【${after}】";
    }

}

重写属性规则后输出差异结果:

// 修改前输出
自动配送由【"是"】修改成【"否"】
// 修改后输出
自动配送值由原值【"是"】变成修改后的值【"否"】

其他需重写规则参见:DefaultDiffPattern

五、集成mybatis

5.1引入依赖

<!-- 日志依赖 -->
<dependency>
    <groupId>com.jumper</groupId>
    <artifactId>property-comparer-starter</artifactId>
    <version>1.0.0</version>
</dependency>

5.2添加注解 @EnableBizLog

@EnableBizLog
@SpringBootApplication
public class LogDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(LogDemoApplication.class, args);
    }

}

5.3编写日志相关代码

5.3.1 日志业务表
CREATE TABLE `order_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`gmt_created` datetime NOT NULL DEFAULT '1000-01-01 00:00:00' COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT '1000-01-01 00:00:00' COMMENT '更新时间',
`action` varchar(100) NOT NULL COMMENT '执行动作',
`data_key` varchar(100) NOT NULL DEFAULT '' COMMENT '数据标识',
`log_value` text NOT NULL COMMENT '日志内容',
`opt_user_name` varchar(60) NOT NULL DEFAULT '' COMMENT '操作用户名称',
`opt_user_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '操作用户ID',
PRIMARY KEY (`id`),
KEY `idx_g_c` (`gmt_created`) COMMENT '创建时间',
KEY `idx_d_k` (`data_key`) COMMENT '数据标识'
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='业务日志-订单日志';
5.3.2 日志业务枚举
/**
 * 日志表
 */
@Getter
@AllArgsConstructor
public enum LogTableEnum implements LogTable {
    ORDER_LOG("order_log", "订单日志");

    private final String logTableName;
    private final String logTableDesc;
}

5.3.3 日志业务manager
/**
 * 订单日志管理器
 */
@Component
public class OrderLogManager extends AbstractLogManager<String> {

    public OrderLogManager(LogRepository repository) {
        super(repository);
    }

    @Autowired
    private OrderService orderService;

    @Override
    public LogTable getBusType() {
        return LogTableEnum.ORDER_LOG;
    }

    @Override
    public Object getOptData(String orderNum) {
        return orderService.getOrder(orderNum);
    }
}
5.3.4 简单调用
    /**
     * 创建订单日志
     */
    @SneakyThrows
    @Test
    void createOrderLog() {
        GoodsOrder order = createOrder();
        logManager.logAction(order.getOrderNo(), "创建", "创建订单", User.of("1", "张三"));
    
        // 默认异步写日志
        Thread.sleep(2000);
    }
    
    /**
     * 修改订单日志
     */
    @SneakyThrows
    @Test
    void updateOrderLog() {
        GoodsOrder order = createOrder();
        logManager.execute(order.getOrderNo(), "修改", GoodsOrder.class, User.of("1", "张三"), (query) -> {
            transactionTemplate.execute(status -> {
                try {
                    // 更新订单
                    updateOrder(order);
                    // 最好事务内执行
                    query.executeQuery();
                } catch (Exception ex) {
                    status.setRollbackOnly();
                    throw ex;
                }
                return null;
            });
        });
        
        // 默认异步写日志
        Thread.sleep(2000);
    }
    
    /**
     * 获取订单日志
     */
    @SneakyThrows
    @Test
    void queryOrderLog() {
        LogPageParam pageParam = new LogPageParam();
        pageParam.setPageNum(1);
        pageParam.setPageSize(10);
        pageParam.setDataKey("10086");
        LogEntityPage logPage = logManager.getLogPage(pageParam);
        return;
    }
5.3.4 其他

用例代码见 log-demo 测试用例

六、gitee地址

https://gitee.com/MyAngle/java-property-comparer

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值