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

news2024/12/28 20:04:56

文章目录

  • 总结:技术点 动态代理 + 模版方法模式 + 线程池异步处理/MQ
  • 一、目的
  • 二、难点:如何实现不同接口不同入参的数据转换?
  • 三、代码实现
    • 1.定义 切点注解接口
    • 2.定义 转换接口和模版方法
    • 3.定义 切面类
    • 4.定义 转换实现类
    • 5.业务方法使用注解
    • 6.常量类和枚举


总结:技术点 动态代理 + 模版方法模式 + 线程池异步处理/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();
}

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

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

/**
 * descr 不同业务转为相同的订单消息对象
 *
 * @author: bjh
 * @date: 2023/9/5
 **/
public interface OrderMessageConvert<Param> {

    /**
     * 入参转标准模型
     *
     * @param param
     * @return
     */
    List<OrderMessageDO> convert(Param param);
}

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;
    }
}

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());
    }
}

代码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);
    }
}

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));
    }

代码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));
    }

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";
}

枚举:


/**
 * 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;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1003158.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pytorch中的seq2seq的人们国籍分类的示例

一、查漏补缺、用法分析&#xff1a; 1.这个简单的RNN网络示意图是我看过的最清晰明了的&#xff1a; 2.震惊&#xff01;原来之前关于seq2seq不明白的地方这里都进行了对应的讲解 02 用字符级别的循环神经网络来判断一个人的名字是哪个国家的常用名 - 知乎 (zhihu.com) 请认…

通过bigMap工具获取地图上各地方的经纬度范围

首先去官网下载bigMap工具,地址&#xff1a;http://www.bigemap.com/reader/download/ 下载成功点击软件&#xff0c;我们会出现这个页面&#xff0c;然后接下来就是选择区域了 相对应上面的操作之后&#xff0c;我们点击箭头&#xff0c;把我们的这个区域下载下来&#xff0c;…

win 10 安装新程序总是弹出提示:目前无法访问smartscreen

win 10 安装新程序总是弹出提示&#xff1a;目前无法访问smartscreen 因为后来无法再现警告&#xff0c;无法截图 搜索安全&#xff0c;找到 Windows 安全中心 找到“应用和浏览器控制”&#xff0c;关闭其中 2 项 SmartScreen 奇怪的是&#xff0c;之后我又改回去了&#x…

自助式数据分析平台:JVS智能BI功能介绍(二)数据集管理

数据集是JVS-智能BI中承载数据、使用数据、管理数据的基础&#xff0c;同样也是构建数据分析的基础。可以通俗地将其理解为数据库中的普通的表&#xff0c;他来源于智能的ETL数据加工工具&#xff0c;可以将数据集进行分析图表、统计报表、数字大屏、数据服务等制作。 在整体的…

Codeforces Round 897 (Div. 2) D. Cyclic Operations

置换环。 i向a[i]连边&#xff0c;最终一定会连出一个环&#xff0c;有可能存在着一条链在后边跟这&#xff0c;这个环的长度一定要等于k才是满足题意的。 出现链的原因就是链在成环的路上被另一条成环的路覆盖了。 对于任何k > 2的情况&#xff0c;链是可以任意长的&…

开学ipad手写笔有必要买原装吗?推荐平价好用的电容笔

学生们都在为即将到来的新学期做准备&#xff0c;而iPad又是必不可少的一部分。科技的飞速发展改变了人们的生活。现在&#xff0c;各种各样的电子和数字产品都有了&#xff0c;所以才有了这种可以和平板电脑搭配使用的电容笔。随着国内电容笔的芯片技术的不断进步&#xff0c;…

Ubuntu22.04开启后屏幕黄屏

1. 故障现象 系统&#xff1a;Ubuntu22.04 现象&#xff1a;电脑从开机到进入桌面一直屏幕黄屏 2. 故障分析 可能为屏幕色彩调节出现故障 3. 解决方案 系统设置——》色彩——》删除原来的配置&#xff08;remove profile&#xff09;——》添加配置Colorspace:Compatibl…

Ubuntu 22.04LTS + 深度学习环境安装全流程

一、 CUDA Toolkit 安装 1. 选择需要安装的版本(下载地址) 2. 选择自己的系统版本获取下载地址和安装指令 3. 运行安装指令进行安装 wget https://developer.download.nvidia.com/compute/cuda/12.2.2/local_installers/cuda_12.2.2_535.104.05_linux.run sudo sh cuda_12.2.…

_locked勒索病毒有什么特点,中了_locked勒索病毒该如何解决?

_locked是一种新型的勒索病毒&#xff0c;它通常通过电子邮件附件、恶意网站、云存储等途径传播。这种病毒一旦感染系统&#xff0c;就会加密受感染设备中的文件&#xff0c;并要求受害者支付一定数额的赎金以解密文件。这种病毒的特点、解决方案和预防措施如下&#xff1a; 特…

皕杰报表(BIOS Report)中设置序号的方法之四

我们分享了皕杰报表设置序号的三种方法&#xff0c;ds.#0&#xff0c;&cellx和row()&#xff0c;有时用这些方法设置序号并不能满足我们的需求&#xff0c;需要配以显示值的使用&#xff0c;使得序号分类更加清楚。我们看下面的主从表&#xff0c;是如何处理序号的显示值的…

第二证券:股利支付率和留存收益率的关系?

股利付出率和留存收益率是股票出资中非常重要的目标&#xff0c;它们可以反映公司的盈余才能和未来开展的潜力。那么&#xff0c;二者之间究竟有什么联系呢&#xff1f; 一、股利付出率和留存收益率的定义 股利付出率是指公司向股东分配的股息占当期净利润的比例&#xff0c;通…

【OAuth漏洞】第三方身份验证-账号接管

目录 什么是OAuth&#xff1f; OAuth 如何用于身份验证&#xff1f; 在 Booking.com 实施 OAuth 为什么 Booking.com OAuth 在 Booking.com 中的工作原理 Booking.com 帐户接管 安全漏洞 1 - 不允许唯一路径 安全漏洞 2 - 开放重定向 安全漏洞 1 2 帐户接管尝试 更…

navicat SSH连接数据库报错: Putty key format too new

问题 下载 Putty 0.79 生成了密钥&#xff0c;但是在navicat 15 使用SSH通道连接数据库报错: Putty key format too new 错误原因和处理 原来是因为生成的私钥格式是 V3 &#xff0c; navicat 15 只能识别 V2 所以&#xff0c;在 PuTTYgen Load 私钥&#xff0c;重新保存为 …

Windows MongoDB详细安装与配置

MongoDB详细安装与配置 下载地址 MongoDB安装 1.双击安装包进行安装 2.点击I accept the tems in the license agreement&#xff0c;点击Next 3.选择默认安装路径&#xff0c;点击completem&#xff1b;自定义安装路径&#xff0c;点击custom,点击browse选择路径&#xff0c…

听说,你想做大模型时代的应用层创业!

亲爱的科技探险家们和代码魔法师们&#xff1a; 未来的钟声已经敲响&#xff0c;预示着一场极度炫酷的虚拟现实游戏即将展开。从初期简单的智能识别&#xff0c;到设计师级别的图纸设计&#xff0c;生成式AI技术&#xff08;Generative AI&#xff09;以其独特理念和创新模式重…

【校招VIP】前端计算机网络之UDP相关

考点介绍 UDP是一个简单的面向消息的传输层协议&#xff0c;尽管UDP提供标头和有效负载的完整性验证&#xff08;通过校验和&#xff09;&#xff0c;但它不保证向上层协议提供消息传递&#xff0c;并且UDP层在发送后不会保留UDP 消息的状态。因此&#xff0c;UDP有时被称为不可…

2023 9月最新彩虹易支付系统源码

2023 9月最新彩虹易支付系统源码 更新日志&#xff1a; 1.新增微信公众号消息提醒功能 2.重构转账付款功能&#xff0c;支持通过插件扩展 3.商户后台新增代付功能 4.后台新增付款记录列表 5.支付宝插件新增预授权支付 6.优化支付通道列表&#xff0c;支持翻页与快速复制…

【UE5 智慧城市系列】5-通过鼠标键盘控制摄像机

目标 通过鼠标WASD键控制摄像机前后左右移动&#xff0c;鼠标滚轮控制弹簧臂长度的修改&#xff0c;鼠标中键控制摄像机旋转。 步骤 1. 首先创建一个游戏模式&#xff0c;这里命名为“BP_GameMode” 2. 再创建一个玩家控制器&#xff0c;这里命名为“BP_PlayerController” …

坚果投影人事地震:IPO之前,创始人被投资人踢出公司

大数据产业创新服务媒体 ——聚焦数据 改变商业 一份流传于网络的《董事会函件》以及一张微信截图显示&#xff0c;智能投影行业市占率第二的坚果投影8月28日发生人事地震&#xff0c;公司创始人胡震宇被董事会罢免董事长一职&#xff0c;由资方代表连萌担任临时董事长一职。 …

长胜证券:政策东风频吹 慢牛格局或已打开

长胜证券认为&#xff0c;目前商场遭到央行社融数据提振&#xff0c;全体预期出现了必定的回暖&#xff0c;经济运行的部分不确定性得以落地&#xff0c;8月社融数据作为先行指标提振了出资者信心。操作上看出资者可逐步加大仓位&#xff0c;选择前期调整较为充沛&#xff0c;有…