2024050402-重学 Java 设计模式《实战责任链模式》

news2024/11/22 13:28:37

重学 Java 设计模式:实战责任链模式「模拟618电商大促期间,项目上线流程多级负责人审批场景」

一、前言

场地和场景的重要性

射击🏹需要去靶场学习、滑雪🏂需要去雪场体验、开车🚗需要能上路实践,而编程开发除了能完成产品的功能流程,还需要保证系统的可靠性能。就像你能听到的一些系统监控指标;QPSTPSTP99TP999可用率响应时长等等,而这些指标的总和评估就是一个系统的健康度。但如果你几乎没有听到这样的技术术语,也没接触过类似高并发场景,那么就很像驾驶证的科目1考了100分,但不能上路。没有这样的技术场景给你训练,让你不断的体会系统的脾气秉性,即便你有再多的想法都没法实现。所以,如果真的想学习一定要去一个有实操的场景,下水试试才能学会狗刨。

你的视觉盲区有多大

同样一本书、同样一条路、同样一座城,你真的以为生活有选择吗?有时候很多选项都是摆设,给你多少次机会你都选的一模一样。这不是你选不选而是你的认知范围决定了你下一秒做的事情,另外的一个下一秒又决定了再下一个下一秒。就像管中窥豹一样,20%的面积在你视觉里都是黑色的,甚至就总是忽略看不到,而这看不到的20%就是生命中的时运!但,人可以学习,可以成长,可以脱胎换骨,可以努力付出,通过一次次的蜕变而看到剩下的20%!

没有设计图纸你敢盖楼吗

编程开发中最好的什么,是设计。运用架构思维、经验心得、才华灵感,构建出最佳的系统。真正的研发会把自己写的代码当做作品来欣赏,你说这是一份工作,但在这样的人眼里这可不是一份工作,而是一份工匠精神。就像可能时而你也会为自己因为一个niubility的设计而豪迈万丈,为能上线一个扛得住每秒200万访问量的系统会精神焕发。这样的自豪感就是一次次垒砖一样垫高脚底,不断的把你的视野提高,让你能看到上层设计也能知晓根基建设。可以把控全局,也可以治理细节。这一份份知识的沉淀,来帮助你绘制出一张系统架构蓝图。

二、开发环境

  1. JDK 1.8
  2. Idea + Maven
  3. 涉及工程三个,可以通过关注公众号bugstack虫洞栈,回复源码下载获取(打开获取的链接,找到序号18)
工程描述
itstack-demo-design-13-00场景模拟工程;模拟一个上线流程审批的接口。
itstack-demo-design-13-01使用一坨代码实现业务需求
itstack-demo-design-13-02通过设计模式优化改造代码,产生对比性从而学习

三、责任链模式介绍

责任链模式,图片来自 refactoringguru.cn

  • 图片来自:https://refactoringguru.cn/design-patterns/chain-of-responsibility

击鼓传雷,看上图你是否想起周星驰有一个电影,大家坐在海边围成一个圈,拿着一个点燃的炸弹,互相传递。

责任链模式的核心是解决一组服务中的先后执行处理关系,就有点像你没钱花了,需要家庭财务支出审批,10块钱以下找闺女审批,100块钱先闺女审批在媳妇审批。你可以理解想象成当你要跳槽的时候被安排的明明白白的被各个领导签字放行。

四、案例场景模拟

场景模拟;618大促场景上线审批场景

在本案例中我们模拟在618大促期间的业务系统上线审批流程场景

像是这些一线电商类的互联网公司,阿里、京东、拼多多等,在618期间都会做一些运营活动场景以及提供的扩容备战,就像过年期间百度的红包一样。但是所有开发的这些系统都需要陆续的上线,因为临近618有时候也有一些紧急的调整的需要上线,但为了保障线上系统的稳定性是尽可能的减少上线的,也会相应的增强审批力度。就像一级响应、二级响应一样。

而这审批的过程在随着特定时间点会增加不同级别的负责人加入,每个人就像责任链模式中的每一个核心点。对于研发小伙伴并不需要关心具体的审批流程处理细节,只需要知道这个上线更严格,级别也更高,但对于研发人员来说同样是点击相同的提审按钮,等待审核。

接下来我们就模拟这样一个业务诉求场景,使用责任链的设计模式来实现此功能。

1. 场景模拟工程

itstack-demo-design-13-00
└── src
    └── main
        └── java
            └── org.itstack.demo.design
                └── AuthService.java
  • 这里的代码结构比较简单,只有一个模拟审核和查询审核结果的服务类。相当于你可以调用这个类去审核工程和获取审核结构,这部分结果信息是模拟的写到缓存实现。

2. 场景简述

2.1 模拟审核服务
public class AuthService {

    private static Map<String, Date> authMap = new ConcurrentHashMap<String, Date>();

    public static Date queryAuthInfo(String uId, String orderId) {
        return authMap.get(uId.concat(orderId));
    }

    public static void auth(String uId, String orderId) {
        authMap.put(uId.concat(orderId), new Date());
    }

}
  • 这里面提供了两个接口一个是查询审核结果(queryAuthInfo)、另外一个是处理审核(auth)。
  • 这部分是把由谁审核的和审核的单子ID作为唯一key值记录到内存Map结构中。

五、用一坨坨代码实现

这里我们先使用最直接的方式来实现功能

按照我们的需求审批流程,平常系统上线只需要三级负责人审批就可以,但是到了618大促时间点,就需要由二级负责以及一级负责人一起加入审批系统上线流程。在这里我们使用非常直接的if判断方式来实现这样的需求。

1. 工程结构

itstack-demo-design-13-01
└── src
    └── main
        └── java
            └── org.itstack.demo.design
                └── AuthController.java
  • 这部分非常简单的只包含了一个审核的控制类,就像有些伙伴开始写代码一样,一个类写所有需求。

2. 代码实现

public class AuthController {

    private SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 时间格式化

    public AuthInfo doAuth(String uId, String orderId, Date authDate) throws ParseException {

        // 三级审批
        Date date = AuthService.queryAuthInfo("1000013", orderId);
        if (null == date) return new AuthInfo("0001", "单号:", orderId, " 状态:待三级审批负责人 ", "王工");

        // 二级审批
        if (authDate.after(f.parse("2020-06-01 00:00:00")) && authDate.before(f.parse("2020-06-25 23:59:59"))) {
            date = AuthService.queryAuthInfo("1000012", orderId);
            if (null == date) return new AuthInfo("0001", "单号:", orderId, " 状态:待二级审批负责人 ", "张经理");
        }

        // 一级审批
        if (authDate.after(f.parse("2020-06-11 00:00:00")) && authDate.before(f.parse("2020-06-20 23:59:59"))) {
            date = AuthService.queryAuthInfo("1000011", orderId);
            if (null == date) return new AuthInfo("0001", "单号:", orderId, " 状态:待一级审批负责人 ", "段总");
        }

        return new AuthInfo("0001", "单号:", orderId, " 状态:审批完成");
    }

}
  • 这里从上到下分别判断了在指定时间范围内由不同的人员进行审批,就像618上线的时候需要三个负责人都审批才能让系统进行上线。
  • 像是这样的功能看起来很简单的,但是实际的业务中会有很多部门,但如果这样实现就很难进行扩展,并且在改动扩展调整也非常麻烦。

3. 测试验证

3.1 编写测试类
@Test
public void test_AuthController() throws ParseException {
    AuthController authController = new AuthController();  

    // 模拟三级负责人审批
    logger.info("测试结果:{}", JSON.toJSONString(authController.doAuth("小傅哥", "1000998004813441", new Date())));
    logger.info("测试结果:{}", "模拟三级负责人审批,王工");
    AuthService.auth("1000013", "1000998004813441");  

    // 模拟二级负责人审批                                 
    logger.info("测试结果:{}", JSON.toJSONString(authController.doAuth("小傅哥", "1000998004813441", new Date())));
    logger.info("测试结果:{}", "模拟二级负责人审批,张经理");
    AuthService.auth("1000012", "1000998004813441");    

    // 模拟一级负责人审批
    logger.info("测试结果:{}", JSON.toJSONString(authController.doAuth("小傅哥", "1000998004813441", new Date())));
    logger.info("测试结果:{}", "模拟一级负责人审批,段总");
    AuthService.auth("1000011", "1000998004813441");            

    logger.info("测试结果:{}", "审批完成");
}
  • 这里模拟每次查询是否审批完成,随着审批的不同节点,之后继续由不同的负责人进行审批操作。
  • authController.doAuth,是查看审批的流程节点、AuthService.auth,是审批方法用于操作节点流程状态。
3.2 测试结果
23:25:00.363 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:{"code":"0001","info":"单号:1000998004813441 状态:待三级审批负责人 王工"}
23:25:00.366 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:模拟三级负责人审批,王工
23:25:00.367 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:{"code":"0001","info":"单号:1000998004813441 状态:待二级审批负责人 张经理"}
23:25:00.367 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:模拟二级负责人审批,张经理
23:25:00.368 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:{"code":"0001","info":"单号:1000998004813441 状态:待一级审批负责人 段总"}
23:25:00.368 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:模拟一级负责人审批,段总
23:25:00.368 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:审批完成

Process finished with exit code 0
  • 从测试结果上可以看到一层层的由不同的人员进行审批,审批完成后到下一个人进行处理。单看结果是满足我们的诉求,只不过很难扩展和调整流程,相当于代码写的死死的。

六、责任链模式重构代码

接下来使用装饰器模式来进行代码优化,也算是一次很小的重构。

责任链模式可以让各个服务模块更加清晰,而每一个模块间可以通过next的方式进行获取。而每一个next是由继承的统一抽象类实现的。最终所有类的职责可以动态的进行编排使用,编排的过程可以做成可配置化。

1. 工程结构

itstack-demo-design-13-02
└── src
    └── main
        └── java
            └── org.itstack.demo.design
                ├── impl
                │    ├── Level1AuthLink.java
                │    ├── Level2AuthLink.java
                │    └── Level3AuthLink.java
                ├── AuthInfo.java
                └── AuthLink.java

责任链类图

责任链类图

责任链模式模型结构

责任链模式模型结构

  • 上图是这个业务模型中责任链结构的核心部分,通过三个实现了统一抽象类AuthLink的不同规则,再进行责任编排模拟出一条链路。这个链路就是业务中的责任链。
  • 一般在使用责任链时候如果是场景比较固定,可以通过写死到代码中进行初始化。但如果业务场景经常变化可以做成xml配置的方式进行处理,也可以落到库里进行初始化操作。

2. 代码实现

2.1 责任链中返回对象定义
public class AuthInfo {

    private String code;
    private String info = "";

    public AuthInfo(String code, String ...infos) {
        this.code = code;
        for (String str:infos){
            this.info = this.info.concat(str);
        }
    }
    
    // ...get/set
}
  • 这个类的是包装了责任链处理过程中返回结果的类,方面处理每个责任链的返回信息。
2.2 链路抽象类定义
public abstract class AuthLink {

    protected Logger logger = LoggerFactory.getLogger(AuthLink.class);

    protected SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 时间格式化
    protected String levelUserId;                           // 级别人员ID
    protected String levelUserName;                         // 级别人员姓名
    private AuthLink next;                                  // 责任链

    public AuthLink(String levelUserId, String levelUserName) {
        this.levelUserId = levelUserId;
        this.levelUserName = levelUserName;
    }

    public AuthLink next() {
        return next;
    }

    public AuthLink appendNext(AuthLink next) {
        this.next = next;
        return this;
    }

    public abstract AuthInfo doAuth(String uId, String orderId, Date authDate);

}
  • 这部分是责任链,链接起来的核心部分。AuthLink next,重点在于可以通过next方式获取下一个链路需要处理的节点。
  • levelUserIdlevelUserName,是责任链中的公用信息,标记每一个审核节点的人员信息。
  • 抽象类中定义了一个抽象方法,abstract AuthInfo doAuth,这是每一个实现者必须实现的类,不同的审核级别处理不同的业务。
2.3 三个审核实现类

Level1AuthLink

public class Level1AuthLink extends AuthLink {

    public Level1AuthLink(String levelUserId, String levelUserName) {
        super(levelUserId, levelUserName);
    }

    public AuthInfo doAuth(String uId, String orderId, Date authDate) {
        Date date = AuthService.queryAuthInfo(levelUserId, orderId);
        if (null == date) {
            return new AuthInfo("0001", "单号:", orderId, " 状态:待一级审批负责人 ", levelUserName);
        }
        AuthLink next = super.next();
        if (null == next) {
            return new AuthInfo("0000", "单号:", orderId, " 状态:一级审批完成负责人", " 时间:", f.format(date), " 审批人:", levelUserName);
        }

        return next.doAuth(uId, orderId, authDate);
    }

}

Level2AuthLink

public class Level2AuthLink extends AuthLink {

    private Date beginDate = f.parse("2020-06-11 00:00:00");
    private Date endDate = f.parse("2020-06-20 23:59:59");

    public Level2AuthLink(String levelUserId, String levelUserName) throws ParseException {
        super(levelUserId, levelUserName);
    }

    public AuthInfo doAuth(String uId, String orderId, Date authDate) {
        Date date = AuthService.queryAuthInfo(levelUserId, orderId);
        if (null == date) {
            return new AuthInfo("0001", "单号:", orderId, " 状态:待二级审批负责人 ", levelUserName);
        }
        AuthLink next = super.next();
        if (null == next) {
            return new AuthInfo("0000", "单号:", orderId, " 状态:二级审批完成负责人", " 时间:", f.format(date), " 审批人:", levelUserName);
        }

        if (authDate.before(beginDate) || authDate.after(endDate)) {
            return new AuthInfo("0000", "单号:", orderId, " 状态:二级审批完成负责人", " 时间:", f.format(date), " 审批人:", levelUserName);
        }

        return next.doAuth(uId, orderId, authDate);
    }

}

Level3AuthLink

public class Level3AuthLink extends AuthLink {

    private Date beginDate = f.parse("2020-06-01 00:00:00");
    private Date endDate = f.parse("2020-06-25 23:59:59");

    public Level3AuthLink(String levelUserId, String levelUserName) throws ParseException {
        super(levelUserId, levelUserName);
    }

    public AuthInfo doAuth(String uId, String orderId, Date authDate) {
        Date date = AuthService.queryAuthInfo(levelUserId, orderId);
        if (null == date) {
            return new AuthInfo("0001", "单号:", orderId, " 状态:待三级审批负责人 ", levelUserName);
        }
        AuthLink next = super.next();
        if (null == next) {
            return new AuthInfo("0000", "单号:", orderId, " 状态:三级审批负责人完成", " 时间:", f.format(date), " 审批人:", levelUserName);
        }

        if (authDate.before(beginDate) || authDate.after(endDate)) {
            return new AuthInfo("0000", "单号:", orderId, " 状态:三级审批负责人完成", " 时间:", f.format(date), " 审批人:", levelUserName);
        }

        return next.doAuth(uId, orderId, authDate);
    }

}
  • 如上三个类;Level1AuthLinkLevel2AuthLinkLevel3AuthLink,实现了不同的审核级别处理的简单逻辑。
  • 例如第一个审核类中会先判断是否审核通过,如果没有审核通过则返回结果给调用方,引导去审核。(这里简单模拟审核后有时间信息不为空,作为判断条件)
  • 判断完成后获取下一个审核节点;super.next();,如果不存在下一个节点,则直接返回结果。
  • 之后是根据不同的业务时间段进行判断是否需要,二级和一级的审核。
  • 最后返回下一个审核结果;next.doAuth(uId, orderId, authDate);,有点像递归调用。

3. 测试验证

3.1 编写测试类
@Test
public void test_AuthLink() throws ParseException {
    AuthLink authLink = new Level3AuthLink("1000013", "王工")
            .appendNext(new Level2AuthLink("1000012", "张经理")
                    .appendNext(new Level1AuthLink("1000011", "段总")));

    logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("小傅哥", "1000998004813441", new Date())));

    // 模拟三级负责人审批
    AuthService.auth("1000013", "1000998004813441");
    logger.info("测试结果:{}", "模拟三级负责人审批,王工");
    logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("小傅哥", "1000998004813441", new Date())));

    // 模拟二级负责人审批
    AuthService.auth("1000012", "1000998004813441");
    logger.info("测试结果:{}", "模拟二级负责人审批,张经理");
    logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("小傅哥", "1000998004813441", new Date())));

    // 模拟一级负责人审批
    AuthService.auth("1000011", "1000998004813441");
    logger.info("测试结果:{}", "模拟一级负责人审批,段总");
    logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("小傅哥", "1000998004813441", new Date())));
}
  • 这里包括最核心的责任链创建,实际的业务中会包装到控制层; AuthLink authLink = new Level3AuthLink("1000013", "王工") .appendNext(new Level2AuthLink("1000012", "张经理") .appendNext(new Level1AuthLink("1000011", "段总"))); 通过把不同的责任节点进行组装,构成一条完整业务的责任链。
  • 接下里不断的执行查看审核链路authLink.doAuth(...),通过返回结果对数据进行3、2、1级负责人审核,直至最后审核全部完成。
3.2 测试结果
23:49:46.585 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:{"code":"0001","info":"单号:1000998004813441 状态:待三级审批负责人 王工"}
23:49:46.590 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:模拟三级负责人审批,王工
23:49:46.590 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:{"code":"0001","info":"单号:1000998004813441 状态:待二级审批负责人 张经理"}
23:49:46.590 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:模拟二级负责人审批,张经理
23:49:46.590 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:{"code":"0001","info":"单号:1000998004813441 状态:待一级审批负责人 段总"}
23:49:46.590 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:模拟一级负责人审批,段总
23:49:46.590 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:{"code":"0000","info":"单号:1000998004813441 状态:一级审批完成负责人 时间:2020-06-18 23:49:46 审批人:段总"}

Process finished with exit code 0
  • 从上述的结果可以看到我们的责任链已经生效,按照责任链的结构一层层审批,直至最后输出审批结束到一级完成的结果。
  • 这样责任链的设计方式可以方便的进行扩展和维护,也把if语句干掉了。

七、总结

  • 从上面代码从if语句重构到使用责任链模式开发可以看到,我们的代码结构变得清晰干净了,也解决了大量if语句的使用。并不是if语句不好,只不过if语句并不适合做系统流程设计,但是在做判断和行为逻辑处理中还是非常可以使用的。
  • 在我们前面学习结构性模式中讲到过组合模式,它像是一颗组合树一样,我们搭建出一个流程决策树。其实这样的模式也是可以和责任链模型进行组合扩展使用,而这部分的重点在于如何关联链路的关联,最终的执行都是在执行在中间的关系链。
  • 责任链模式很好的处理单一职责和开闭原则,简单了耦合也使对象关系更加清晰,而且外部的调用方并不需要关心责任链是如何进行处理的*(以上程序中可以把责任链的组合进行包装,在提供给外部使用)*。但除了这些优点外也需要是适当的场景才进行使用,避免造成性能以及编排混乱调试测试疏漏问题。

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

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

相关文章

【python】OpenCV—Bitplane

学习来自&#xff1a; 位平面分割&#xff08;Bit-Plane Slicing&#xff09;使用OpenCVPython进行图像处理的初学者指南 位平面 位平面&#xff08;bitplane&#xff09;是一个在计算机科学中用于描述图像数据的概念&#xff0c;具体定义如下&#xff1a; 【定义】&#x…

vue3之拆若依--记实现后台管理首页(左侧菜单栏、头部信息区域...)

效果图 前期准备 启动若依在本地 启动若依后台,跑在自己本地: 这里对于如何下载若依相关的前后端代码请参考若依官网:RuoYi 若依官方网站 |后台管理系统|权限管理系统|快速开发框架|企业管理系统|开源框架|微服务框架|前后端分离框架|开源后台系统|RuoYi|RuoYi-Vue|RuoYi-…

CSS学习笔记之高级教程(五)

23、CSS 媒体查询 - 实例 /* 如果屏幕尺寸超过 600 像素&#xff0c;把 <div> 的字体大小设置为 80 像素 */ media screen and (min-width: 600px) {div.example {font-size: 80px;} }/* 如果屏幕大小为 600px 或更小&#xff0c;把 <div> 的字体大小设置为 30px …

数据虚拟化:零数据搬运,实现全域数据的集成和自适应加速

数据虚拟化技术的兴起&#xff0c;与传统数据仓库体系的弊端日益显现有着密切关系。 过去&#xff0c;企业通常会构建数据仓库来存储与加工结构化数据。数据仓库虽然实现了数据的物理集中存储&#xff0c;但过于依赖大量的 ETL 工程师来支持数据的集成、准备、开发与管理。随着…

SEATA如何起步

https://seata.apache.org/zh-cn/https://seata.apache.org/zh-cn/ seata官网网址 Seata术语:(具体看官网) 具体SEATA有几种模式: TCC , AT(即自动模式): 因为 , 事务已经提交 , 肯定无法 回滚 , 所以TCC模式是需要自己自己写补偿代码, AT则是需要一份UNDO_LOG 表 来告诉AT模…

DxO ViewPoint v4.16 解锁版安装教程 (校正几何和透视的图像处理)

前言 DxO ViewPoint中文版是一款能够校正几何和透视的图像处理软件,摄影师通过ViewPoint破解版修复构图和光学缺陷并恢复拍摄对象平衡,重新调整如弯曲架构和扭曲图案等细节,让图像具备更强冲击力和更优平衡性。 一、下载地址 下载链接&#xff1a;http://dygod/source 点击搜…

智能交通SCI期刊,中科院2区,IF=7.9,国产期刊,影响力高,口碑佳

一、期刊名称 Digital Communications and Networks 二、期刊简介概况 期刊类型&#xff1a;SCI 学科领域&#xff1a;智能交通 影响因子&#xff1a;7.9 中科院分区&#xff1a;2区 三、期刊征稿范围 《数字通信与网络》与科爱出版社和重庆邮电大学合作出版季刊&#xf…

关于Stream.toList()方法使用小记

对照示例 public static void main(String[] args) {final List<String> list new ArrayList<>();list.add("aa");list.add("bb");list.add("cc");list.remove("cc");System.out.println(list);}结果&#xff1a; Stre…

SAS:import进来的excel数据集中的空格无法去除

目的&#xff1a;如果去除ECGTPT中的空格&#xff1f; 尝试&#xff1a;用compress函数或者strip无法去除正解&#xff1a;kcompress(ecgtpt,0d’x);释疑&#xff1a; 中间空的地方不是空格&#xff0c;是换行符。因数据集是UTF-8编码&#xff0c;还需要用kcompress替换compres…

详解 Flink 的运行架构

一、组件 1. JobManager 作业管理器是一个 Flink 集群中任务管理和调度的核心&#xff0c;是控制应用执行的主进程 1.1 JobMaster JobMaster 是 JobManager 中最核心的组件&#xff0c;负责处理单独的作业&#xff08;Job&#xff09;。JobMaster 和具体的 Job 是一一对应的&…

rpm安装

rpm安装 命令格式&#xff1a; rpm 【选项】 文件名 选项&#xff1a; -i&#xff1a;安装软件 -v:显示安装过程信息 -h:用#表示安装进度&#xff0c;一个#代表2% -ivh&#xff1a;安装软件&#xff0c;显示安装过程 -e:卸载软件 -q:查看软件是否安装 -ql&#xff1…

码农危是否到来? AI大模型时代到来程序员能做啥?

前言 “马斯克提到人工智能会让工作变得毫无意义&#xff0c;并建议人们可能需要去编写人工智能程序&#xff0c;以避免被AI剥夺就业”&#xff0c;AI大模型的爆发&#xff0c;各种自动化编码应用工具&#xff0c;AI机器人出现&#xff0c;“前有2023年2月份&#xff0c;ChatG…

「实战应用」如何用图表控件LightningChart JS创建SQL仪表板应用(一)

LightningChart JS是Web上性能特高的图表库&#xff0c;具有出色的执行性能 - 使用高数据速率同时监控数十个数据源。 GPU加速和WebGL渲染确保您的设备的图形处理器得到有效利用&#xff0c;从而实现高刷新率和流畅的动画&#xff0c;常用于贸易&#xff0c;工程&#xff0c;航…

Polar Web【简单】php very nice

Polar Web【简单】php very nice Contents Polar Web【简单】php very nice思路EXP运行&总结 思路 打开网页源代码&#xff0c;由下图的代码&#xff0c;可见本题涉及到反序列化以及变量覆盖。 因此考虑传递GET参数a来构造序列字符串。 由上图中的代码&#xff0c;在Exampl…

8.7k Star!Khoj:你的AI第二大脑、开源RAG Cop​​ilot、平替 MS Copilot与ChatGPT

原文链接&#xff1a;&#xff08;更好排版、视频播放、社群交流、最新AI开源项目、AI工具分享都在这个公众号&#xff01;&#xff09; 8.7k Star&#xff01;Khoj&#xff1a;你的AI第二大脑、开源RAG Cop​​ilot、平替 MS Copilot与ChatGPT &#x1f31f;你的AI第二大脑。…

Splatter Image: Ultra-Fast Single-View 3D Reconstruction

Splatter Image: Ultra-Fast Single-View 3D Reconstruction 飞溅图像&#xff1a;超快速单视图3D重建 Stanislaw Szymanowicz  Christian Rupprecht  Andrea Vedaldi 克里斯蒂安鲁普雷希特安德烈韦达尔迪 Visual Geometry Group — University of Oxford {stan,chrisr,vedal…

优卡集团冲刺港股上市:90后创始团队孵化,IPO前突击大额分红

现年26岁的鲁圳&#xff0c;正在带领其6年以来的创业成果冲击资本市场。 近日&#xff0c;金融居间机构服务商优卡集团&#xff08;Yoc Group&#xff09;向港交所递交上市申请&#xff0c;民银资本为其独家保荐人。透过招股书可知&#xff0c;优卡集团成立于2018年&#xff0…

自动化测试-Selenium(一),简介

自动化测试-Selenium 1. 什么是自动化测试 1.1 自动化测试介绍 自动化测试是一种通过自动化工具执行测试用例来验证软件功能和性能的过程。与手动测试不同&#xff0c;自动化测试使用脚本和软件来自动执行测试步骤&#xff0c;记录结果&#xff0c;并比较预期输出和实际输出…

Gitlab---添加描述模版

0 Preface/Foreword Gitlab是代码托管平台&#xff0c;DevOps。因其免费&#xff0c;被广泛使用。GitLab不但可以管理代码&#xff0c;也可以管理issue&#xff0c;创建milestone等等。针对issue管理&#xff0c;支持描述模版功能&#xff0c;即对于新建的issue&#xff0c;可…

山东大学软件学院项目实训-创新实训-基于大模型的旅游平台(二十八)- 微服务(8)

目录 11.4 SpringAMQP 11.4.2 Work Queue工作队列 11.4.3 发布订阅模型 11.4.4 FanoutExchange(广播交换机) 11.4.5 DirectExchange(路由模式交换机) 11.4.6 TopicExchange 11.5 消息转换器 11.4 SpringAMQP 父工程引入AMQP依赖 <!--AMQP依赖&#xff0c;包含RabbitMQ…