• SpringBoot AOP实现日志或告警(高级)



    总结:技术点 动态代理 + 模版方法模式 + 线程池异步处理/MQ

    其中代理模式使用jdk动态代理

    一、目的

    实现:客户订单全局消息提醒业务员
    涉及业务:客户订单、产品包、出库结算、客留存、结算单预处理、字印任务等6个模块;
    未来涉及:采购订单、委外订单、订单核销等
    归纳:全局信息的处理;如权限或日志等类似这样的需求

    二、难点:如何实现不同接口不同入参的数据转换?

    1.定义注解的转换类,将不同的业务转换成相同的消息或日志,再进行入库
    2.采用SpEL表达式进行实现

    三、代码实现

    1.定义 切点注解接口

    说明:通过此接口作用在目标方法上,作为切入点

    /**
     * descr
     *
     * @author: bjh
     * @date: 2023/9/5
     **/
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface OrderMessageOperate {
    
        /**
         * 客户订单操作类型:1.根据客户订单ID 2.根据客户订单编号
         *
         * @return 描述
         */
        String operateType() default "";
    
        /**
         * 根据不同操作类型实现不同的convert进行转换
         *
         * @return 转换子类
         */
        Class<? extends OrderMessageConvert> convert();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    2.定义 转换接口和模版方法

    说明:将不同入参转换为消息或DO进行入库
    当前业务:目前订单状态的变更需要通知业务员和业务员领导,所以这里返回为list类型;业务上也出现了一对多的情况,如一个结算单对应同一个客户的多个订单,每个订单需要通知相应的业务员和领导,这里也是需要转为list

    /**
     * descr 不同业务转为相同的订单消息对象
     *
     * @author: bjh
     * @date: 2023/9/5
     **/
    public interface OrderMessageConvert<Param> {
    
        /**
         * 入参转标准模型
         *
         * @param param
         * @return
         */
        List<OrderMessageDO> convert(Param param);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    3.定义 切面类

    说明:切面包含多个切点类(如pointCut)和多种增强方式(即横切逻辑 如@Arround,@Before,@AfterReturning,@AfterThrowing等)
    技术:@Aspect默认使用jdk动态代理;

    /**
     * descr
     *
     * @author: bjh
     * @date: 2023/9/5
     **/
    @Aspect
    @Component
    public class OrderMessageAspect {
    
        // 异步线程执行
        private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                1, 1,
                1, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(100));
    
        /**
         * 1.定义切入点
         * 2.横切逻辑
         * 3.植入(spring)
         */
        @Pointcut("@annotation(cn.iocoder.yudao.module.wh.aop.customerorder.OrderMessageOperate)")
        public void pointCut() {
        }
    
        @Around("pointCut()")
        public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            // 执行切入点方法,拿到返回结果 ???
            final Object result = proceedingJoinPoint.proceed();
            this.threadPoolExecutor.execute(() -> {
                try {
                    final MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
                    final OrderMessageOperate annotation = methodSignature.getMethod().getAnnotation(OrderMessageOperate.class);
                    final OrderMessageConvert orderMessageConvert = annotation.convert().newInstance();
                    final List<OrderMessageDO> orderMessageDOS = orderMessageConvert.convert(proceedingJoinPoint.getArgs()[0]);
                    for (OrderMessageDO orderMessageDO : orderMessageDOS) {
                        orderMessageDO.setOperateType(annotation.operateType());
                    }
                    if (ObjectUtil.isNotNull(orderMessageDOS)) {
                        SpringUtil.getBean(OrderMessageService.class).insertBatchOrderMessage(orderMessageDOS);
                    }
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
    
            });
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    4.定义 转换实现类

    说明:根据不同切入接口定义不同的转换类,如下

    1.图
    在这里插入图片描述

    2.举两代码例子
    代码1

    
    /**
     * descr    通过客户订单Create对象进行转换
     *
     * @author: bjh
     * @date: 2023/9/5
     **/
    public class CustomerOrderCreateConvert implements OrderMessageConvert<CustomerOrderCreateReqVO> {
    
        @Override
        public List<OrderMessageDO> convert(CustomerOrderCreateReqVO orderCreateReqVO) {
            final CustomerOrderService customerOrderService = SpringUtil.getBean(CustomerOrderService.class);
            final CustomerOrderRespVO orderRespVO = customerOrderService.getCustomerOrderByOrderNo(orderCreateReqVO.getOrderNo(), true);
            return SpringUtil.getBean(OrderMessageService.class).convertByCustomerOrderId(orderRespVO.getId());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    代码2

    
    /**
     * descr    通过客户订单ID进行转换
     *
     * @author: bjh
     * @date: 2023/9/5
     **/
    public class CustomerOrderIdConvert implements OrderMessageConvert<Long> {
    
        @Override
        public List<OrderMessageDO> convert(Long customerOrderId) {
            return SpringUtil.getBean(OrderMessageService.class).convertByCustomerOrderId(customerOrderId);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    5.业务方法使用注解

    说明:下面代码中OrderOperateConstant.ORDER_CREATE_OPT/ORDER_TO_PICK_OPT是常量类型,定义操作类型(新增/修改/审核等)

    代码1

     @PostMapping("/create")
        @Operation(summary = "创建客户订单")
        @PreAuthorize("@ss.hasPermission('wh:customer-order:create')")
        @OrderMessageOperate(operateType = OrderOperateConstant.ORDER_CREATE_OPT, convert = CustomerOrderCreateConvert.class)
        public CommonResult<Long> createCustomerOrder(@Valid @RequestBody CustomerOrderCreateReqVO createReqVO) {
            return success(customerOrderService.createCustomerOrder(createReqVO));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    代码2

    @PutMapping("/allow-to-pick")
        @Operation(summary = "与客户沟通可拣货")
        @Parameter(name = "id", description = "编号", required = true, example = "1024")
        @PreAuthorize("@ss.hasPermission('wh:customer-order:allow-to-pick')")
        @OrderMessageOperate(operateType = OrderOperateConstant.ORDER_TO_PICK_OPT, convert = CustomerOrderIdConvert.class)
        public CommonResult<Boolean> allowToPickOrder(@RequestParam("id") Long id) {
            return success(this.customerOrderService.allowToPickOrder(id));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    6.常量类和枚举

    说明:
    疑问:有了枚举为什么还要定义一个常量类,getKey不就完事了吗?
    我的想法:增加一个常量类,在维护代码时ctrl+鼠标左键可以直接定位到引用的代码行;同样的操作getkey会列出所有选项让我们去选,枚举实例特别多的时候,选准确耗时多,我个人不习惯这种感觉。所以就增加了常量类

    常量类:

    
    /**
     * descr
     *
     * @author: bjh
     * @date: 2023/9/5
     **/
    public interface OrderOperateConstant {
        String ORDER_PREPOSSESS_OPT = "01";
        String ORDER_TO_PICK_OPT = "02";
        String ORDER_RETURN_RECEIVE_OPT = "03";
        String ORDER_RETURN_OPT = "04";
        String ORDER_RECEIVE_OPT = "05";
        String ORDER_TRANSFER_OPT = "06";
        String ORDER_APPLY_OPT = "07";
        String ORDER_REMAIN_OPT = "08";
        String ORDER_DISCARD_OPT = "09";
        String ORDER_DELIVERED_OPT = "10";
        String ORDER_DELIVERABLE_OPT = "11";
        String ORDER_PACKING_OPT = "12";
        String ORDER_SYNC_OPT = "13";
        String ORDER_INFIRM_OPT = "14";
        String ORDER_CREATE_OPT = "15";
        String ORDER_UPDATE_OPT = "16";
        String ORDER_APPLY_CONFIRM_OPT = "17";
    
        String PACK_CREATE_OPT = "18";
        String PACK_UPDATE_OPT = "19";
        String PACK_DELETE_OPT = "20";
        String PACK_CONFIRM_OPT = "21";
        String PACK_INFIRM_OPT = "22";
        String PACK_DISCARD_OPT = "23";
        String PACK_TO_SALE_OPT = "24";
        String PACK_SALE_RECEIVE_OPT = "25";
        String PACK_SALE_RETURN_OPT = "26";
        String PACK_RECEIVE_OPT = "27";
    
        String SALE_BILL_CREATE_OPT = "28";
        String SALE_BILL_UPDATE_OPT = "29";
        String SALE_BILL_DELETE_OPT = "30";
        String SALE_BILL_CONFIRM_OPT = "31";
        String SALE_BILL_INFIRM_OPT = "32";
        String SALE_BILL_DISCARD_OPT = "33";
        String SALE_BILL_DELIVERY_APPLY_OPT = "34";
        String SALE_BILL_DELIVERY_CONFIRM_OPT = "35"; // todo 审核问题
        String SALE_BILL_UPLOAD_OPT = "36";
        String SALE_BILL_DELIVERED_OPT = "37";
    
        String MARKING_BILL_CREATE_OPT = "38";
        String MARKING_BILL_UPDATE_OPT = "39";
        String MARKING_BILL_DELETE_OPT = "40";
        String MARKING_BILL_CONFIRM_OPT = "41";
        String MARKING_BILL_INFIRM_OPT = "42";
        String MARKING_BILL_DISCARD_OPT = "43";
        String MARKING_BILL_MARKING_OPT = "44";
        String MARKING_BILL_FINISH_OPT = "45";
    
        String SALE_PREPOSSESSING_CREATE_OPT = "46";
        String SALE_PREPOSSESSING_UPDATE_OPT = "47";
        String SALE_PREPOSSESSING_DELETE_OPT = "48";
        String SALE_PREPOSSESSING_CONFIRM_OPT = "49";
        String SALE_PREPOSSESSING_INFIRM_OPT = "50";
        String SALE_PREPOSSESSING_DISCARD_OPT = "51";
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    枚举:

    
    /**
     * descr
     *
     * @author: bjh
     * @date: 2023/9/5
     **/
    public enum OrderOperateEnum {
        ORDER_PREPOSSESS_OPT(OrderOperateConstant.ORDER_PREPOSSESS_OPT, "结算预处理"),
        ORDER_ALLOW_TO_PICK_OPT(OrderOperateConstant.ORDER_TO_PICK_OPT, "客户沟通可拣货"),
        ORDER_RETURN_RECEIVE_OPT(OrderOperateConstant.ORDER_RETURN_RECEIVE_OPT, "接受退回产品包"),
        ORDER_RETURN_OPT(OrderOperateConstant.ORDER_RETURN_OPT, "结算台退回产品包"),
        ORDER_RECEIVE_OPT(OrderOperateConstant.ORDER_RECEIVE_OPT, "字印部接收"),
        ORDER_TRANSFER_OPT(OrderOperateConstant.ORDER_TRANSFER_OPT, "调拨产品包到字印部门"),
        ORDER_APPLY_OPT(OrderOperateConstant.ORDER_APPLY_OPT, "客留存延期申请"),
        ORDER_REMAIN_OPT(OrderOperateConstant.ORDER_REMAIN_OPT, "客留存"),
        ORDER_DISCARD_OPT(OrderOperateConstant.ORDER_DISCARD_OPT, "客户订单作废"),
        ORDER_DELIVERED_OPT(OrderOperateConstant.ORDER_DELIVERED_OPT, "客户订单出货接口(老庙)"),
        ORDER_DELIVERABLE_OPT(OrderOperateConstant.ORDER_DELIVERABLE_OPT, "客户订单可出货接口(老庙)"),
        ORDER_PACKING_OPT(OrderOperateConstant.ORDER_PACKING_OPT, "客户订单备货接口(老庙)"),
        ORDER_SYNC_OPT(OrderOperateConstant.ORDER_SYNC_OPT, "客户订单同步(老庙)"),
        ORDER_INFIRM_OPT(OrderOperateConstant.ORDER_INFIRM_OPT, "取消拣货(老庙)"),
        ORDER_CREATE_OPT(OrderOperateConstant.ORDER_CREATE_OPT, "客户订单创建"),
        ORDER_UPDATE_OPT(OrderOperateConstant.ORDER_UPDATE_OPT, "客户订单修改"),
        ORDER_APPLY_CONFIRM_OPT(OrderOperateConstant.ORDER_APPLY_CONFIRM_OPT, "延期申请审核"),
    
        PACK_CREATE_OPT(OrderOperateConstant.PACK_CREATE_OPT, "产品包创建"),
        PACK_UPDATE_OPT(OrderOperateConstant.PACK_UPDATE_OPT, "产品包修改"),
        PACK_DELETE_OPT(OrderOperateConstant.PACK_DELETE_OPT, "产品包删除"),
        PACK_CONFIRM_OPT(OrderOperateConstant.PACK_CONFIRM_OPT, "产品包审核"),
        PACK_INFIRM_OPT(OrderOperateConstant.PACK_INFIRM_OPT, "产品包反审"),
        PACK_DISCARD_OPT(OrderOperateConstant.PACK_DISCARD_OPT, "产品包作废"),
        PACK_TO_SALE_OPT(OrderOperateConstant.PACK_TO_SALE_OPT, "产品包调拨到结算"),
    
        PACK_SALE_RECEIVE_OPT(OrderOperateConstant.PACK_SALE_RECEIVE_OPT, "结算接收产品包"),
        PACK_SALE_RETURN_OPT(OrderOperateConstant.PACK_SALE_RETURN_OPT, "结算退回产品包"),
        PACK_RECEIVE_OPT(OrderOperateConstant.PACK_RECEIVE_OPT, "接受退回产品包"),
        SALE_BILL_CREATE_OPT(OrderOperateConstant.SALE_BILL_CREATE_OPT, "结算单创建"),
        SALE_BILL_UPDATE_OPT(OrderOperateConstant.SALE_BILL_UPDATE_OPT, "结算单修改"),
        SALE_BILL_DELETE_OPT(OrderOperateConstant.SALE_BILL_DELETE_OPT, "结算单删除"),
        SALE_BILL_CONFIRM_OPT(OrderOperateConstant.SALE_BILL_CONFIRM_OPT, "结算单审核"),
        SALE_BILL_INFIRM_OPT(OrderOperateConstant.SALE_BILL_INFIRM_OPT, "结算单反审"),
        SALE_BILL_DISCARD_OPT(OrderOperateConstant.SALE_BILL_DISCARD_OPT, "结算单作废"),
        SALE_BILL_DELIVERY_APPLY_OPT(OrderOperateConstant.SALE_BILL_DELIVERY_APPLY_OPT, "结算单发货申请"),
        SALE_BILL_DELIVERY_CONFIRM_OPT(OrderOperateConstant.SALE_BILL_DELIVERY_CONFIRM_OPT, "结算单发货审批"),
        SALE_BILL_UPLOAD_OPT(OrderOperateConstant.SALE_BILL_UPLOAD_OPT, "结算单上传签字图片"),
        SALE_BILL_DELIVERED_OPT(OrderOperateConstant.SALE_BILL_DELIVERED_OPT, "结算单已发货"),
    
        MARKING_BILL_CREATE_OPT(OrderOperateConstant.MARKING_BILL_CREATE_OPT, "字印单创建"),
        MARKING_BILL_UPDATE_OPT(OrderOperateConstant.MARKING_BILL_UPDATE_OPT, "字印单修改"),
        MARKING_BILL_DELETE_OPT(OrderOperateConstant.MARKING_BILL_DELETE_OPT, "字印单删除"),
        MARKING_BILL_CONFIRM_OPT(OrderOperateConstant.MARKING_BILL_CONFIRM_OPT, "字印单审核"),
        MARKING_BILL_INFIRM_OPT(OrderOperateConstant.MARKING_BILL_INFIRM_OPT, "字印单反审"),
        MARKING_BILL_DISCARD_OPT(OrderOperateConstant.MARKING_BILL_DISCARD_OPT, "字印单作废"),
        MARKING_BILL_MARKING_OPT(OrderOperateConstant.MARKING_BILL_MARKING_OPT, "字印单字印中"),
        MARKING_BILL_FINISH_OPT(OrderOperateConstant.MARKING_BILL_FINISH_OPT, "字印单字印完成"),
    
        SALE_PREPOSSESSING_CREATE_OPT(OrderOperateConstant.SALE_PREPOSSESSING_CREATE_OPT, "结算预处理创建"),
        SALE_PREPOSSESSING_UPDATE_OPT(OrderOperateConstant.SALE_PREPOSSESSING_UPDATE_OPT, "结算预处理修改"),
        SALE_PREPOSSESSING_DELETE_OPT(OrderOperateConstant.SALE_PREPOSSESSING_DELETE_OPT, "结算预处理删除"),
        SALE_PREPOSSESSING_CONFIRM_OPT(OrderOperateConstant.SALE_PREPOSSESSING_CONFIRM_OPT, "结算预处理审核"),
        SALE_PREPOSSESSING_INFIRM_OPT(OrderOperateConstant.SALE_PREPOSSESSING_INFIRM_OPT, "结算预处理反审"),
        SALE_PREPOSSESSING_DISCARD_OPT(OrderOperateConstant.SALE_PREPOSSESSING_DISCARD_OPT, "结算预处理作废"),
        ;
    
        @Getter
        private final String key;
        private final String text;
    
        OrderOperateEnum(String key, String text) {
            this.key = key;
            this.text = text;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
  • 相关阅读:
    多个vtk文件合并
    【35. 多重背包】
    Apache Hudi初探(五)(与flink的结合)--Flink 中hudi clean操作
    思科交换机65系列配置
    Freemarker
    【程序员日记】---从业务编排到低代码
    【Python从入门到精通】(九)Python中字符串的各种骚操作你已经烂熟于心了么?【收藏下来就挺好的】
    Redux基础必知必会 reducer拆分 中间件 单向数据流
    LabVIEW样式检查表8
    Linux简单命令之用户组操作
  • 原文地址:https://blog.csdn.net/User_bie/article/details/132767283