当前位置: 首页 > news >正文

独辟蹊径:我是如何用Java自创一套工作流引擎的(下)

作者:后端小肥肠

创作不易,未经允许严禁转载。

姊妹篇:独辟蹊径:我是如何用Java自创一套工作流引擎的(上)_java工作流引擎-CSDN博客

1. 前言

在上一篇博客中,我们详细介绍了如何利用Java语言从零开始打造一套工作流引擎的基础架构。通过设计核心表结构和实现基础代码框架,我们建立了一个坚实的理论基础。今天,我们迈入《独辟蹊径:我是如何用Java自创一套工作流引擎的(下)》,将深入探讨这一引擎在实际项目中的应用和效果。

2. 项目场景模拟

本章我们将以实际项目场景来模拟自研工作流引擎的使用,分别是申请数据资源的流程和请假申请流程。

2.1. 申请数据资源流程

2.1.1. 技术流程

假设申请数据的审批级数为2级。申请数据资源的流程图如下:

在上述流程图中,从普通用户一级审批人员二级审批人员视角呈现了申请数据资源的整体流程;

1. 用户提交审批数据表单,填入申请人信息(姓名、电话),申请理由和需要申请的数资源;

2. 一级审批人员收到用户提交的申请后进行审批,如果驳回则整个流程结束,如果通过则进入下一审批环节;

3. 一级审批通过后二级审批人员可进行审批,如果驳回则整个流程结束,如何通过则开放数据下载链接,用户可根据链接下载申请的数据。

2.2. 请假申请流程

2.2.1. 技术流程

请假流程如下:

在上述流程中,从用户一级审批人员的角度呈现了整个请假流程:

1. 用户提交审批数据表单,填入申请人信息(姓名、电话)、申请理由、请假天数;

2. 一级审批人员收到用户提交的申请后进行审批,如果驳回则整个流程结束,同时通知用户流程未通过,如果通过则结束流程。

2.3. 技术实现

要在工作流中集成以上两套流程,需要基于一下几个步骤实现:

1. 设计流程定义,在business_approval_workflow新建数据审批流程和请假流程。

2. 设计流程细节,设计数据审批流程和请假流程的节点细节。

上图中,申请业务数据包含两个流程节点,第一个节点审批人为admin,第二个节点审批人为super;请假流程包含一个流程节点,审批人为admin。

3. 编写提交申请接口。

提交申请业务数据流程接口直接使用《独辟蹊径:我是如何用Java自创一套工作流引擎的(上)》中提交申请接口就行:

    public Boolean addRequest(RequestDTO requestDTO) {Request request= BeanCopyUtils.copyBean(requestDTO,Request.class);request.setStatus("1");//设置整个流程状态为正在审核// 1. 插入数据到 request 表baseMapper.insert(request);// 2. 根据 workflow_id 查询业务流程的节点信息,找到 serial_number 为 1 的节点,即流程开始时的第一个节点BusinessApprovalWorkflowDetail firstNode = workflowDetaiSlService.findFirstNodeByWorkflowId(request.getWorkflowId());//获取下一级节点 填充下级节点审批人BusinessApprovalWorkflowDetail nextNode=workflowDetaiSlService.getNextNodeByPreNode(firstNode);if (firstNode != null) {// 创建一个 approval_detail 记录示例,需要根据具体情况设置字段值ApprovalDetail approvalDetail = new ApprovalDetail();approvalDetail.setRequestId(request.getId()); // 假设设置关联的 request_idapprovalDetail.setApproverUsername(firstNode.getNodeUsername()); // 设置首次节点的审批人用户名approvalDetail.setApprovalTime(new Date());approvalDetail.setNextApproverUsername(nextNode.getNodeUsername());//设置下游节点的审批人用户名approvalDetail.setStatus("1"); // 设置初始状态为待审批approvalDetail.setWorkflowId(request.getWorkflowId());approvalDetail.setNodeName(firstNode.getNodeName());approvalDetail.setNextNodeName(nextNode.getNodeName());// 插入数据到 approval_detail 表approvalDetailService.save(approvalDetail);} else {// 如果未找到对应的节点,根据实际需求进行错误处理或日志记录throw new RuntimeException("Unable to find the first node for workflow id: " + request.getWorkflowId());}return true;}

提交请假流程接口,在编写提交请假流程接口前,需要先明确请假申请表的表结构:

CREATE TABLE "public"."leave_request" ("id" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,"workflow_id" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,"purpose" varchar(900) COLLATE "pg_catalog"."default" NOT NULL,"leave_days" int2 NOT NULL,"applicant_name" varchar(50) COLLATE "pg_catalog"."default","applicat_username" varchar(50) COLLATE "pg_catalog"."default","applicant_phone" varchar(11) COLLATE "pg_catalog"."default","version" int4 DEFAULT 1,"is_deleted" int4 DEFAULT 0,"create_time" timestamp(6) NOT NULL,"update_time" timestamp(6)
)
;ALTER TABLE "public"."leave_request" OWNER TO "postgres";COMMENT ON COLUMN "public"."leave_request"."workflow_id" IS '业务流程id';COMMENT ON COLUMN "public"."leave_request"."purpose" IS '请假理由';COMMENT ON COLUMN "public"."leave_request"."leave_days" IS '请假天数';COMMENT ON COLUMN "public"."leave_request"."applicant_name" IS '申请人姓名';COMMENT ON COLUMN "public"."leave_request"."applicat_username" IS '申请人用户名';COMMENT ON COLUMN "public"."leave_request"."applicant_phone" IS '申请人电话';

编写controller层:

    @GetMapping("")public Boolean addRequest(@Validated @RequestBody LeaveRequestDTO leaveRequestDTO){return leaveRequestService.addRequest(leaveRequestDTO);}

编写LeaveRequestDTO:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class LeaveRequestDTO {private String workflowId;private String purpose;private Integer leaveDays;private String applicantName;private String applicantPhone;private String applicatUsername;
}

编写service层:

    public Boolean addRequest(LeaveRequestDTO leaveRequestDTO) {LeaveRequest leaveRequest= BeanCopyUtils.copyBean(leaveRequestDTO,LeaveRequest.class);// 1. 插入数据到 request 表baseMapper.insert(leaveRequest);// 2. 根据 workflow_id 查询业务流程的节点信息,找到 serial_number 为 1 的节点,即流程开始时的第一个节点BusinessApprovalWorkflowDetail firstNode = workflowDetaiSlService.findFirstNodeByWorkflowId(leaveRequest.getWorkflowId());//获取下一级节点 填充下级节点审批人BusinessApprovalWorkflowDetail nextNode=workflowDetaiSlService.getNextNodeByPreNode(firstNode);if (firstNode != null) {// 创建一个 approval_detail 记录示例,需要根据具体情况设置字段值ApprovalDetail approvalDetail = new ApprovalDetail();approvalDetail.setRequestId(leaveRequest.getId()); // 假设设置关联的 request_idapprovalDetail.setApproverUsername(firstNode.getNodeUsername()); // 设置首次节点的审批人用户名approvalDetail.setApprovalTime(new Date());approvalDetail.setNextApproverUsername(nextNode.getNodeUsername());//设置下游节点的审批人用户名approvalDetail.setStatus("1"); // 设置初始状态为待审批approvalDetail.setWorkflowId(leaveRequest.getWorkflowId());approvalDetail.setNodeName(firstNode.getNodeName());approvalDetail.setNextNodeName(nextNode.getNodeName());// 插入数据到 approval_detail 表approvalDetailService.save(approvalDetail);} else {// 如果未找到对应的节点,根据实际需求进行错误处理或日志记录throw new RuntimeException("Unable to find the first node for workflow id: " + leaveRequest.getWorkflowId());}return true;}

上述代码实现了提交请假申请的功能:首先将从DTO转换后的请假请求数据插入数据库,然后根据流程ID查询流程的第一个节点信息,设置首次节点的审批人并插入到审批详情表中,状态设置为待审批。 

4. 基于策略模式优化审批接口。

审批申请方法在上篇中如下:

 @Transactional@Overridepublic Boolean approvalApplication(ApprovalDTO approvalDTO) {// 这里我写死了,实际获取应该走权限框架获取当前在线用户 usernameString username = "xfc";
//        审批人姓名,从用户表中获取String name="小肥肠";//查询出当前任务节点ApprovalDetail approvalDetail = baseMapper.selectById(approvalDTO.getId());//获取当前审批的申请信息Request request = requestMapper.selectById(approvalDetail.getRequestId());if(request==null){throw new RuntimeException("申请id有误");}// 审批通过if (approvalDTO.getStatus().equals("2")) {// 根据 workflow_id 和 node_name 联查 business_approval_workflow_detail 表,获取当前流程是否为最后节点即 is_final=1BusinessApprovalWorkflowDetail currentWorkflowDetail = businessApprovalWorkflowDetailService.findByWorkflowIdAndNodeName(approvalDTO.getWorkflowId(), approvalDetail.getNodeName());if (currentWorkflowDetail != null && currentWorkflowDetail.getIsFinal().equals("1")) {// 如果是最后节点,则删除该条数据,填充 approval_history 表,根据 request 表修改 request 数据的 status 为 2baseMapper.deleteById(approvalDetail.getId()); // 删除当前审批记录// 更新 request 表中的状态为 2(通过)request.setStatus("2");requestMapper.updateById(request);} else {// 如果不是最后节点,则更新 business_approval_workflow_detail 为下一个节点审批信息BusinessApprovalWorkflowDetail nextNode = businessApprovalWorkflowDetailService.getNextNodeByPreNode(currentWorkflowDetail);
//                获取下一级节点的更下一级BusinessApprovalWorkflowDetail nextNextNode= businessApprovalWorkflowDetailService.getNextNodeByPreNode(nextNode);// 更新当前 approval_detail 表中的审批人和下一个审批人信息approvalDetail.setApproverUsername(nextNode.getNodeUsername());approvalDetail.setNodeName(nextNextNode.getNodeName());approvalDetail.setNextApproverUsername(nextNextNode!=null?nextNextNode.getNodeUsername():"");approvalDetail.setNextNodeName(nextNextNode!=null?nextNextNode.getNodeName():"");approvalDetail.setApprovalTime(new Date());approvalDetail.setStatus("1"); // 设置为待审批状态baseMapper.updateById(approvalDetail);}// 填充 approval_history 表ApprovalHistory approvalHistory = new ApprovalHistory();approvalHistory.setRequestId(request.getId());approvalHistory.setApproverName(name); // 设置审批人姓名,或者从用户表中获取approvalHistory.setApprovalTime(new Date());approvalHistory.setStatus("2"); // 通过approvalHistory.setRemark(approvalDTO.getRemark());approvalHistory.setWorkflowId(approvalDTO.getWorkflowId());approvalHistory.setApplicantPhone(request.getApplicantPhone());approvalHistory.setPurpose(request.getPurpose());approvalHistory.setApplicantName(request.getApplicantName());approvalHistory.setApproverUsername(username); // 设置审批人用户名,或者从用户表中获取approvalHistoryMapper.insert(approvalHistory); // 插入审批历史记录} else if (approvalDTO.getStatus().equals("3")) {// 审批驳回baseMapper.deleteById(approvalDetail.getId()); // 删除当前审批记录// 填充 approval_history 表ApprovalHistory approvalHistory = new ApprovalHistory();approvalHistory.setRequestId(request.getId());approvalHistory.setApproverName(name); // 设置审批人姓名,或者从用户表中获取approvalHistory.setApprovalTime(new Date());approvalHistory.setStatus("3"); // 驳回approvalHistory.setRemark(approvalDTO.getRemark());approvalHistory.setWorkflowId(approvalDTO.getWorkflowId());approvalHistory.setApplicantPhone(request.getApplicantPhone());approvalHistory.setPurpose(request.getPurpose());approvalHistory.setApplicantName(request.getApplicantName());approvalHistory.setApproverUsername(username); // 设置审批人用户名,或者从用户表中获取approvalHistoryMapper.insert(approvalHistory); // 插入审批历史记录// 更新 request 表中的状态为 3(驳回)request.setStatus("3");requestMapper.updateById(request);}return true; // 或者根据实际需求返回其他业务逻辑}

上述代码其实是针对申请数据资源流程的审批操作,那如果要申请别的流程该怎么做呢?常规操作应该是在上述代码中增加if-else判断操作,根据不通业务进行士审批操作,但是随着业务流程增加,就会新增许多if-else操作,代码会十分雍总,代码可读性较差,可以通过引入策略工厂来解决上述问题。步骤如下:

1.新增审批策略工厂

@Service
public class ApprovalFactory {@AutowiredApprovalDataRequestService approvalDataRequestService;@AutowiredApprovalLeaveRequestService approvalLeaveRequestService;private static Map<String, Function<ApprovalDTO,Boolean>> approvalMap = null;@PostConstructpublic void init(){approvalMap=new HashMap<>();approvalMap.put("2",approvalDTO->approvalDataRequestService.approvalApplication(approvalDTO));approvalMap.put("1",approvalDTO ->approvalLeaveRequestService.approvalApplication(approvalDTO));}public Boolean approvalApplication(ApprovalDTO approvalDTO) {return approvalMap.get(approvalDTO.getWorkflowId()).apply(approvalDTO);}
}

上述代码为针对审批操作的策略工厂,在类初始化过程中(使用 @PostConstruct 注解的 init() 方法),通过静态的 approvalMap 对象将审批动作和对应的处理函数关联起来。具体来说:

  • workflowId 为 "2" 时,映射到 approvalDataRequestServiceapprovalApplication 方法处理数据请求的审批逻辑。
  • workflowId 为 "1" 时,映射到 approvalLeaveRequestServiceapprovalApplication 方法处理请假请求的审批逻辑。

最后,approvalApplication(ApprovalDTO approvalDTO) 方法根据传入的 approvalDTO 中的 workflowIdapprovalMap 中获取相应的处理函数,并执行该函数来完成审批操作,返回处理结果,controller层直接调用策略工厂即可:

    @PostMapping("/approval")public Boolean approvalApplication(@Validated @RequestBody ApprovalDTO approvalDTO) {return approvalFactory.approvalApplication(approvalDTO);}

2. 新增数据资源审批类

@Service
@Slf4j
public class ApprovalDataRequestService {@AutowiredIBusinessApprovalWorkflowDetailService businessApprovalWorkflowDetailService;@AutowiredRequestMapper requestMapper;@AutowiredApprovalHistoryMapper approvalHistoryMapper;@AutowiredApprovalDetailMapper approvalDetailMapper;@Transactionalpublic Boolean approvalApplication(ApprovalDTO approvalDTO) {// 这里我写死了,实际获取应该走权限框架获取当前在线用户 usernameString username = "xfc";
//        审批人姓名,从用户表中获取String name="小肥肠";//查询出当前任务节点ApprovalDetail approvalDetail = approvalDetailMapper.selectById(approvalDTO.getId());//获取当前审批的申请信息Request request = requestMapper.selectById(approvalDetail.getRequestId());if(request==null){throw new RuntimeException("申请id有误");}// 审批通过if (approvalDTO.getStatus().equals("2")) {// 根据 workflow_id 和 node_name 联查 business_approval_workflow_detail 表,获取当前流程是否为最后节点即 is_final=1BusinessApprovalWorkflowDetail currentWorkflowDetail = businessApprovalWorkflowDetailService.findByWorkflowIdAndNodeName(approvalDTO.getWorkflowId(), approvalDetail.getNodeName());if (currentWorkflowDetail != null && currentWorkflowDetail.getIsFinal().equals("1")) {// 如果是最后节点,则删除该条数据,填充 approval_history 表,根据 request 表修改 request 数据的 status 为 2approvalDetailMapper.deleteById(approvalDetail.getId()); // 删除当前审批记录// 更新 request 表中的状态为 2(通过)request.setStatus("2");requestMapper.updateById(request);} else {// 如果不是最后节点,则更新 business_approval_workflow_detail 为下一个节点审批信息BusinessApprovalWorkflowDetail nextNode = businessApprovalWorkflowDetailService.getNextNodeByPreNode(currentWorkflowDetail);
//                获取下一级节点的更下一级BusinessApprovalWorkflowDetail nextNextNode= businessApprovalWorkflowDetailService.getNextNodeByPreNode(nextNode);// 更新当前 approval_detail 表中的审批人和下一个审批人信息approvalDetail.setApproverUsername(nextNode.getNodeUsername());approvalDetail.setNodeName(nextNextNode.getNodeName());approvalDetail.setNextApproverUsername(nextNextNode!=null?nextNextNode.getNodeUsername():"");approvalDetail.setNextNodeName(nextNextNode!=null?nextNextNode.getNodeName():"");approvalDetail.setApprovalTime(new Date());approvalDetail.setStatus("1"); // 设置为待审批状态approvalDetailMapper.updateById(approvalDetail);}// 填充 approval_history 表ApprovalHistory approvalHistory = new ApprovalHistory();approvalHistory.setRequestId(request.getId());approvalHistory.setApproverName(name); // 设置审批人姓名,或者从用户表中获取approvalHistory.setApprovalTime(new Date());approvalHistory.setStatus("2"); // 通过approvalHistory.setRemark(approvalDTO.getRemark());approvalHistory.setWorkflowId(approvalDTO.getWorkflowId());approvalHistory.setApplicantPhone(request.getApplicantPhone());approvalHistory.setPurpose(request.getPurpose());approvalHistory.setApplicantName(request.getApplicantName());approvalHistory.setApproverUsername(username); // 设置审批人用户名,或者从用户表中获取approvalHistoryMapper.insert(approvalHistory); // 插入审批历史记录} else if (approvalDTO.getStatus().equals("3")) {// 审批驳回approvalDetailMapper.deleteById(approvalDetail.getId()); // 删除当前审批记录// 填充 approval_history 表ApprovalHistory approvalHistory = new ApprovalHistory();approvalHistory.setRequestId(request.getId());approvalHistory.setApproverName(name); // 设置审批人姓名,或者从用户表中获取approvalHistory.setApprovalTime(new Date());approvalHistory.setStatus("3"); // 驳回approvalHistory.setRemark(approvalDTO.getRemark());approvalHistory.setWorkflowId(approvalDTO.getWorkflowId());approvalHistory.setApplicantPhone(request.getApplicantPhone());approvalHistory.setPurpose(request.getPurpose());approvalHistory.setApplicantName(request.getApplicantName());approvalHistory.setApproverUsername(username); // 设置审批人用户名,或者从用户表中获取approvalHistoryMapper.insert(approvalHistory); // 插入审批历史记录// 更新 request 表中的状态为 3(驳回)request.setStatus("3");requestMapper.updateById(request);}return true; // 或者根据实际需求返回其他业务逻辑}}

3.新增请假审批类

@Service
public class ApprovalLeaveRequestService {public Boolean approvalApplication(ApprovalDTO approvalDTO) {/*** 一样的逻辑,把对request表的操作改为leave_request*/return true;}
}

3. 结语

在本文中,针对《独辟蹊径:我是如何用Java自创一套工作流引擎的(上)》中的工作流基础代码进行了结合实际项目的扩展,本工作流引擎适用于任何相对简单的审批场景,有新的业务流程仅需针对申请表单和审批逻辑进行接口新增和策略工厂扩展即可,如本文对你有帮助请一键三连哦~

相关文章:

独辟蹊径:我是如何用Java自创一套工作流引擎的(下)

作者&#xff1a;后端小肥肠 创作不易&#xff0c;未经允许严禁转载。 姊妹篇&#xff1a;独辟蹊径&#xff1a;我是如何用Java自创一套工作流引擎的&#xff08;上&#xff09;_java工作流引擎-CSDN博客 1. 前言 在上一篇博客中&#xff0c;我们详细介绍了如何利用Java语言从…...

【Python】pycharm常用快捷键操作

目录 一.pycharm自定义修改快捷键 二.pycharm默认常用快捷键 一.pycharm自定义修改快捷键 在file-setting-keymap中可以修改快捷键&#xff0c;建议刚开始没特殊需求就不用修改&#xff0c;先熟悉系统默认的常用快捷键&#xff0c;但是以下情况可以考虑修改: 之前使用其他I…...

es6语法复习一

es6语法 1.var 变量提升 2.let 不存在变量提升&#xff0c;只能定义一次 3.const 先定义再使用&#xff0c;定义好来不能修改 4.解构赋值 [a,b,c][1,2,3],{a,b,c}{a:1,b:2,c:3} 5.模版字符串 let aaa; ${a} is ok 6.对象简化写法 const school{ name, change, improve(){ cons…...

【python入门】自定义函数

文章目录 定义自定义函数的基本语法参数类型示例代码函数作用域匿名函数&#xff08;Lambda&#xff09;闭包装饰器 Python中的自定义函数允许你编写一段可重用的代码块&#xff0c;这段代码可以带参数&#xff08;输入&#xff09;&#xff0c;并可能返回一个值&#xff08;输…...

ONLYOFFICE 桌面编辑器 8.1 版发布:全面提升文档处理效率的新体验

文章目录 什么是ONLYOFFICE &#xff1f;ONLYOFFICE 桌面编辑器 8.1 发布&#xff1a;新功能和改进功能强大的 PDF 编辑器幻灯片版式功能从右至左语言支持多媒体功能增强无缝切换工作模式其他改进和优化总结 什么是ONLYOFFICE &#xff1f; https://www.onlyoffice.com/zh/off…...

ESP32实现UDP连接——micropython版本

代码&#xff1a; import network import socket import timedef wifiInit(name, port):ap network.WLAN(network.AP_IF) # 创建一个热点ap.config(essidname, authmodenetwork.AUTH_OPEN) # 无需密码ap.active(True) # 激活热点ip ap.ifconfig()[0] # 获取ip地址print(…...

Windows Ternimal

Windows Ternimal 安装 Windows 终端概述 | Microsoft Learn wt --help在当前目录打开 lextm/windowsterminal-shell: Install/uninstall scripts for Windows Terminal context menu items 打开指定目录 wt -d %USERPROFILE% ohmyposh 美化 1 安装 2 添加 ohmyposh bin…...

Unity扩展编辑器功能的特性

1.添加分组标题 用于在Unity的Inspector视图中为属性或变量组创建一个自定义的标题或头部&#xff0c;有助于在Inspector中组织和分类不同的属性&#xff0c;使其更易于阅读和管理。 [Header("Common Properties")] public float MouseSensitivity 5; public float…...

API类别 - UI核心

API类别 - UI核心 引言 在当今的数字时代,用户界面(UI)是任何软件或应用成功的关键因素之一。UI核心API作为构建用户界面的基础,提供了丰富的功能和工具,使得开发者能够创建出既美观又实用的用户界面。本文将深入探讨UI核心API的不同类别,以及它们如何影响现代软件开发…...

Redis-主从复制-配置主从关系

文章目录 1、修改配置文件中的 bind ,注释该配置,取消绑定仅主机登录2、修改protected-mode 为no,取消保护模式3、查看redis的进程状态4、配置6380是6379的从机5、配置6381是6379的从机6、查看主机 6379 的主从信息 1、修改配置文件中的 bind ,注释该配置,取消绑定仅主机登录 …...

DigiRL:让 AI 自己学会控制手机

类似于苹果此前发布的Ferret-UI 的安卓开源平替。主要用于在 Android 设备上识别 UI 和执行指令&#xff0c;不同的是它利用了离线到在线强化学习&#xff08;Offline-to-Online RL&#xff09;&#xff0c;能够快速适应应用更新或 UI 变化。...

04.Ambari自定义服务开发-自定义服务配置文件在Ambari中的设置方法

文章目录 设置方法配置文件设置Custom xxx配置文件详细的配置方法.xml文件的整体格式基础参数格式value-attributes配置介绍设置属性在服务安装后不可修改设置允许字段为空是否显示配置名称参数类型设置字符串类型PasswordBooleanIntFloatDirectoryDirectoriesContent-多行文本…...

LSTM时间序列基础学习

时间序列 时间序列可以是一维&#xff0c;二维&#xff0c;三维甚至更高维度的数据&#xff0c;在深度学习的世界中常见的是三维时间序列&#xff0c;这三个维度分别是&#xff08;batch_size,time_step,input_dimensions&#xff09;。 其中time_step是时间步&#xff0c;它…...

『Z-Workshop』 6月22日线下ALCOVE分享活动

2024 求是创新 ZJUBCA Sponsored by the ALCOVE Community TIME&#xff1a;2024/06/22 ADD&#xff1a;浙江大学紫金港校区 --- Alcove 是 Aptos 公链与 Alibaba Cloud 共同打造的亚洲首个 Move 开发者社区&#xff0c;致力于支持开发者使用 Move 语言构建下一代 Web3 应用&am…...

【机器学习】机器学习重要方法——迁移学习:理论、方法与实践

文章目录 迁移学习&#xff1a;理论、方法与实践引言第一章 迁移学习的基本概念1.1 什么是迁移学习1.2 迁移学习的类型1.3 迁移学习的优势 第二章 迁移学习的核心方法2.1 特征重用&#xff08;Feature Reuse&#xff09;2.2 微调&#xff08;Fine-Tuning&#xff09;2.3 领域适…...

uniapp, ‍[⁠TypeError⁠]‍ “Failed to fetch dynamically imported module“ 报错解决思路

文章目录 1. 背景2. 报错3. 解决思路4. 思考参考1. 背景 最近基于uniapp开发一款设备参数调试的APP软件,在使用第三方插件的过程中,出现下面的报错。 2. 报错 [plugin:vite:import-analysis] Cannot find module ‘D:/leaning/uniapp/demo/jk-uts-udp示例/uni_modules/uts-…...

四川省高等职业学校大数据技术专业建设暨专业质量监测研讨活动顺利开展

6月21日&#xff0c;省教育评估院在四川邮电职业技术学院组织开展全省高等职业学校大数据技术专业建设暨专业质量监测研讨活动。省教育评估院副院长赖长春&#xff0c;四川邮电职业技术学院党委副书记、校长冯远洪&#xff0c;四川邮电职业技术学院党委委员、副校长程德杰等出席…...

深入解析三大跨平台开发框架:Flutter、React Native 和 uniapp

深入解析三大跨平台开发框架&#xff1a;Flutter、React Native 和 uniapp 在移动开发中&#xff0c;跨平台开发框架已经成为开发者的首选工具。本篇将深入解析三大主流跨平台开发框架&#xff1a;Flutter、React Native 和 uniapp。下面将详细探讨它们的原理、优势和劣势。 …...

【吊打面试官系列-MyBatis面试题】#{}和${}的区别是什么?

大家好&#xff0c;我是锋哥。今天分享关于 【#{}和${}的区别是什么&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; #{}和${}的区别是什么&#xff1f; #{} 是预编译处理&#xff0c;${}是字符串替换。 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网…...

解决HTTP 400 Bad Request错误的方法

解决HTTP 400 Bad Request错误的方法 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在进行网络通信时&#xff0c;HTTP 400 Bad Request错误是相对常见的问题…...

Html的表单标签。(Java程序员需要掌握的前端)

表单标签 2.5.1 表单 2.5.1.1 介绍 那表单呢,在我们日常的上网的过程中,基本上每天都会遇到。比如&#xff0c;我们经常在访问网站时&#xff0c;出现的登录页面、注册页面、个人信息提交页面&#xff0c;其实都是一个一个的表单 。 当我们在这些表单中录入数据之后&#xf…...

Arduino (esp ) 下String的内存释放

在个人的开源项目 GitHub - StarCompute/tftziku: 这是一个通过单片机在各种屏幕上显示中文的解决方案 中为了方便快速检索使用了string&#xff0c;于是这个string在esp8266中占了40多k,原本以为当string设置为""的时候这个40k就可以回收&#xff0c;结果发觉不行…...

图灵虚拟机配置

导入虚拟机 点击新建&#xff0c;选择虚拟硬盘文件 环境机器.vmdk 配置网络...

【SQL常用日期函数(一)】

SQL 常用日期函数-基于impala 引擎 当前日期&#xff08;YYYY-MM-DD&#xff09; SELECT CURRENT_DATE(); -- 2024-06-30昨天 SELECT CURRENT_DATE(); -- 2024-06-30 SELECT CAST( DAYS_ADD(TO_DATE( CURRENT_DATE() ), -1 ) AS VARCHAR(10) ); -- 2024-06-29 SELECT CAST( …...

C++操作系列(二):VSCode安装和配置C++开发环境

1. VSCode下载 进入VSCode的官网网页&#xff1a;Download Visual Studio Code - Mac, Linux, Windows 下载相应的版本&#xff1a; 2. 安装VSCode 安装到指定位置&#xff1a; 一路下一步&#xff0c;直至安装完成&#xff1a; 3. 安装C插件 3.1. 安装C/C 点击扩展图标&…...

【java12】java12新特性之File的mismatch方法

Java12引入了一个新的方法 mismatch&#xff0c;它属于java.nio.file.Files类。此方法用于比较两个文件的内容&#xff0c;并返回第一个不匹配字节的位置。如果两个文件完全相同&#xff0c;则返回-1。 Files.mismatch 方法声明 public static long mismatch(Path path1, Pat…...

uni-app (通过HBuilderX 和 VS Code 开发)详细连接过程教学。

使用 HBuilderX 创建 uni-app 项目 并编译到微信开发者工具。 uni-app 支持两种方式创建项目&#xff1a; 通过 HBuilderX 创建 通过命令行创建 首先我们需要先下载HBuilderX 下载链接地址&#xff1a;DCloud - HBuilder、HBuilderX、uni-app、uniapp、5、5plus、mui、wap2…...

安宝特方案 | AR术者培养:AR眼镜如何帮助医生从“看”到“做”?

每一种新药品的上市都需要通过大量的临床试验&#xff0c;而每一种新的手术工具在普及使用之前也需要经过反复的实践和验证。医疗器械公司都面临着这样的挑战&#xff1a;如何促使保守谨慎的医生从仅仅观察新工具在手术中的应用&#xff0c;转变为在实际手术中实操这项工具。安…...

20240628每日前端---------解决vue项目滥用watch

主题 滥用watch。 名字解释 watch 例子 先看一个代码例子&#xff1a; <template>{{ dataList }} </template><script setup lang"ts"> import { ref, watch } from "vue";const dataList ref([]); const props defineProps([&q…...

【LLM 评估】GLUE benchmark:NLU 的多任务 benchmark

论文&#xff1a;GLUE: A Multi-Task Benchmark and Analysis Platform for Natural Language Understanding ⭐⭐⭐⭐ arXiv:1804.07461, ICLR 2019 Site: https://gluebenchmark.com/ 文章目录 一、论文速读二、GLUE 任务列表2.1 CoLA&#xff08;Corpus of Linguistic Accep…...

Go线程调度器

基本结构 字段gcwaiting、stopwait和stopnoted都是串行运行时任务执行前后的辅助协调手段 gcwaiting字段的值用于表示是否需要停止调度 在停止调度前&#xff0c;该值会被设置为1在恢复调度之前&#xff0c;该值会被设置为0这样做的作用是&#xff0c;一些调度任务在执行时只…...

使用 fvm 管理 Flutter 版本

文章目录 Github官网fvm 安装Mac/Linux 环境Windows 环境 fvm 环境变量fvm 基本命令 Github https://github.com/leoafarias/fvmhttps://github.com/flutter/flutter 官网 https://fvm.app/ fvm 安装 Mac/Linux 环境 Install.sh curl -fsSL https://fvm.app/install.sh …...

若依-前后端分离项目学习

★★★★★省流 直接看第一集和最后一集★★★★★ 第一天&#xff08;6.24&#xff09; 具体参考视频 b站 楠哥教你学Java 【【开源项目学习】若依前后端分离版&#xff0c;通俗易懂&#xff0c;快速上手】 https://www.bilibili.com/video/BV1HT4y1d7oA/?shar…...

使用adb shell getprop命令获取Android设备的属性

常用属性获取&#xff1a; adb shell getprop ro.build.version.emui —查询EMUI版本 adb shell getprop ro.product.brand —查询手机品牌 adb shell getprop ro.product.name --查询设备名称 adb shell getprop ro.serialno —查询设备序列号 获取手机系统信息( CPU,厂商…...

LNMP环境部署指南

本文档将指导您在CentOS 6.5上部署LNMP&#xff08;Linux、Nginx、MySQL、PHP&#xff09;环境。 系统环境 系统平台&#xff1a;CentOS release 6.5 安装前准备 在安装LNMP之前&#xff0c;您需要安装一些编译器和依赖包。 必备编译器和工具 #安装gcc、gcc-c编译器&#…...

[stm32]温湿度采集与OLED显示

一、I2C总线协议 I2C&#xff08;Inter-integrated circuit &#xff09;是一种允许从不同的芯片或电路与不同的主芯片通信的协议。它仅用于短距离通信&#xff0c;是一种用于两个或多个设备之间进行数据传输的串行总线技术&#xff0c;它可以让你在微处理器、传感器、存储器、…...

大模型知识库的使用

大模型知识库的使用通常涉及以下几个方面&#xff0c;使用大模型知识库可以提高信息检索的准确性和效率&#xff0c;促进知识的传播和应用。同时&#xff0c;也需要关注知识库的质量和更新&#xff0c;以确保提供的知识是准确和可靠的。北京木奇移动技术有限公司&#xff0c;专…...

Docker - Oracle Database 23ai Free

博文目录 文章目录 说明命令NavicatSYSTEMPDBADMIN 扩展公共用户本地用户 说明 Oracle 官方镜像仓库 Database 23ai Free | Oracle Docker 官方没有提供 Oracle Database 相关镜像, 但是 Oracle 官方镜像仓库有提供, 打开上面的链接, 选择 Database, 选择合适的版本, 如 enter…...

spring常用方法

1. 读取配置文件信息 方式一&#xff1a; // 获取文件路径 String fileName "application.yaml"; String filePath this.getClass().getClassLoader().getResource(fileName).getPath();BufferedReader bufferedReader new BufferedReader(new FileReader(path)…...

虚拟机能装在移动硬盘里吗安全吗 PD虚拟机迁移到移动硬盘的方法

虚拟机技术的迅速发展为用户提供了更为灵活的跨系统办公方案。许多用户希望在不同的电脑设备上运行相同的虚拟机&#xff0c;同时带来的也有一个问题&#xff1a;虚拟机能否装在移动硬盘里&#xff1f;针对用户的疑问&#xff0c;接下来给大家介绍虚拟机能装在移动硬盘里吗&…...

刷算法Leetcode---7(二叉树篇)(前中后序遍历)

前言 本文是跟着代码随想录的栈与队列顺序进行刷题并编写的 代码随想录 好久没刷算法了&#xff0c;最近又开始继续刷&#xff0c;果然还是要坚持。 二叉树的题目比之前多了好多&#xff0c;就多分几次写啦~ 这是力扣刷算法的其他文章链接&#xff1a;刷算法Leetcode文章汇总 …...

AliyunOS安装Node.js

方法1&#xff1a;dnf软件包安装工具自动安装 最方便的安装方式是通过系统的dnf工具&#xff0c;我测试使用的AliyunOS的版本是Alibaba Cloud Linux 3.2104&#xff0c;具体流程如下&#xff1a; dnf module list nodejs #列出服务器中可以使用的所有nodejs版本确定下来希望安…...

three.js - MeshPhongMaterial材质(实现玻璃水晶球效果)

1、概念 phong网格材质&#xff1a;Mesh - Phong - Material 一种用于具有镜面高光的光泽表面的材质。 它可以模拟&#xff0c;具有镜面高光的光泽表面&#xff0c;提供镜面反射效果。 MeshPhongMaterial&#xff1a; MeshPhongMaterial是一种基于Phong光照模型的材质&#…...

笔记本电脑安装CentOS

正文共&#xff1a;1234 字 24 图&#xff0c;预估阅读时间&#xff1a;2 分钟 前面我们对VPP进行了多次介绍&#xff08;羡慕&#xff01;大佬的VPP能达到180G性能&#xff0c;而我的却只有13.5G&#xff09;&#xff0c;可以发现他的很多优点&#xff0c;但是我们也可以发现它…...

ssh转发功能入门

端口转发概述 端口转发&#xff0c;能够将其他TCP端口的网络数据通过SSH链路转发&#xff0c;并且提供了ssh的加密和解密的服务。 ssh端口转发有如下这些优点&#xff1a; 提供了ssh的加密传输&#xff0c;利于安全能够突破防火墙限制 目前ssh端口转发有如下几种方式&#x…...

Listary(Windows 文件搜索工具)专业版值得购买吗?

说到经典的国货软件&#xff0c;有一款 Win 软件是一定绕不过去的。它就是知名的本地文件搜索工具 Listary&#xff01; 便捷的文件搜索窗口&#xff1b;快捷操作的体验&#xff1b;与系统更匹配的外观设计&#xff1b;更智能的排序和更可靠的索引。 便捷的文件搜索窗口 紧凑…...

面试突击指南:Java基础面试题2

面向对象和集合 1. 面向对象和面向过程的区别 面向过程:面向过程的编程方式是分析解决问题的步骤,然后用函数把这些步骤一步一步地实现,并在使用的时候逐个调用。这种方式性能较高,因此在单片机和嵌入式开发中经常采用面向过程开发。 面向对象:面向对象的编程方式是把问…...

MySQL快速安装(mysql8.0.30区别之前yum安装)

目录 一.初始化环境并解压 二.创建程序用户管理 三.修改mysql目录和配置文件的权限 四.修改配置文件 五.设置环境变量&#xff0c;申明/宣告mysql命令便于系统识别 六.初始化数据库 七.设置系统识别&#xff0c;进行操作 八.初始化数据库密码 九.用户并设置密码 十.赋…...

俄罗斯防空系统

俄罗斯的S系列防空系统是一系列先进的地对空导弹系统&#xff0c;旨在防御各类空中威胁&#xff0c;包括飞机、无人机、巡航导弹和弹道导弹。以下是几种主要的S系列防空系统&#xff1a; 1. **S-300系统**&#xff1a; - **S-300P**&#xff1a;最早期的版本&#xff0c;用…...

文件上传漏洞---Pyload

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 本文重点从靶场案例分析文件上传漏洞常见的Pylod&#xff0c;本文演示靶场upload-labs 一.文件类型---Pyload 不同的文件对应不同的文件类型&#xff0c;后端代码通过限制特定的文件类型…...

虚拟机交叉编译基于ARM平台的opencv(ffmpeg/x264)

背景&#xff1a; 由于手上有一块rk3568的开发板&#xff0c;需要运行yolov5跑深度学习模型&#xff0c;但是原有的opencv不能对x264格式的视频进行解码&#xff0c;这里就需要将ffmpegx264编译进opencv。 但是开发板算力有限&#xff0c;所以这里采用在windows下&#xff0c;安…...

入门机器视觉的正确打开方式——徒手撸一个python+opencv实现的机器视觉简易调试工具(下)

目录 1.引言2.框架思路3.图像处理流程化的实现3.1如何解析图像流程数据结构3.2 使用networkx网络图库3.3 python实现 4.结论5.python源码PS.扩展阅读ps1.六自由度机器人相关文章资源ps2.四轴机器相关文章资源ps3.移动小车相关文章资源 1.引言 在当今AI时代&#xff0c;关于视觉…...

Perl 语言开发(七):哈希和关联数组

目录 1. 哈希与关联数组的概述 2. 哈希的基本操作 2.1 创建哈希 2.2 访问哈希值 2.3 添加和修改哈希值 2.4 删除哈希值 2.5 检查哈希中是否存在某个键 3. 迭代哈希 3.1 使用 keys 和 values 3.2 使用 each 4. 复杂数据结构中的哈希 4.1 哈希的数组 4.2 哈希的哈希…...

谷粒商城学习-09-配置Docker阿里云镜像加速及各种docker问题记录

文章目录 一&#xff0c;配置Docker阿里云镜像加速二&#xff0c;Docker安装过程中的几个问题1&#xff0c;安装报错&#xff1a;Could not resolve host: mirrorlist.centos.org; Unknown error1.1 检测虚拟机网络1.2 重设yum源 2&#xff0c;报错&#xff1a;Could not fetch…...

昇思25天学习打卡营第17天(+1)|Diffusion扩散模型

1. 学习内容复盘 本文基于Hugging Face&#xff1a;The Annotated Diffusion Model一文翻译迁移而来&#xff0c;同时参考了由浅入深了解Diffusion Model一文。 本教程在Jupyter Notebook上成功运行。如您下载本文档为Python文件&#xff0c;执行Python文件时&#xff0c;请确…...

为什么写Python脚本时要加上if __name__ == ‘__main__‘?

目录 一、__name__ 的秘密 二、if __name__ __main__: 的作用 三、代码示例与案例分析 示例一&#xff1a;简单的数学工具模块 示例二&#xff1a;命令行工具 四、实际应用场景 五、进阶应用 1. 插件开发 2. 动态加载模块 3. 交互式与脚本模式切换 六、结论 在Pyth…...

AIONV霸王龙车门首开,首款全球战略车“外刚内柔”

埃安凭借重磅车型带起了产品推新的节奏。6月20日,埃安首款全球战略车型埃安V?霸王龙全球媒体品鉴会上,新车内饰设计首次全面公开,同时,埃安官方宣布,品牌将全面启用AION字母标作为品牌全球标识,以更具国际化的形象面向全球。埃安V?霸王龙定位为中型SUV,与燃油车时代的…...

Matlab|基于PMU相量测量单元进行电力系统电压幅值和相角状态估计

主要内容 程序采用三种方法对14节点和30节点电力系统状态进行评估&#xff1a; ①PMU同步相量测量单元结合加权最小二乘法&#xff08;WLS&#xff09;分析电力系统的电压幅值和相角状态&#xff1b; ②并采用牛顿-拉夫逊方法进行系统潮流计算&#xff0c;结果作为理论分…...

HNU-计算机体系结构-实验3-缓存一致性

计算机体系结构 实验3 计科210X 甘晴void 202108010XXX 文章目录 计算机体系结构 实验31 实验目的2 实验过程2.0 预备知识2.0.1 多cache一致性算法——监听法2.0.1.1 MSI协议2.0.1.2 MESI协议2.0.1.3 本题讲解 2.0.2 多cache一致性算法——目录法2.0.2.1 有中心的目录法2.0.2…...

d20(184-190)-勇敢开始Java,咖啡拯救人生

目录 网络通信 网络通信三要素&#xff08;IP地址&#xff0c;端口号&#xff0c;协议 IP地址 InetAddress 端口号 协议 传输层的两个通信协议 UDP通信 java.net.Datagramsocket类 客户端 服务端 UDP通信多收多发 客户端 服务端 TCP通信 java.net.Socket类 客…...

【Numpy】NumPy基础入门:创建和管理多维数组

NumPy基础入门&#xff1a;创建和管理多维数组 简介 NumPy&#xff08;Numerical Python的简称&#xff09;是Python语言的一个基础科学计算库&#xff0c;广泛应用于数据分析、机器学习、科学计算和工程领域。NumPy的核心是其强大的N维数组对象ndarray&#xff0c;它为Pytho…...

【深度学习实战—7】:基于Pytorch的多标签图像分类-Fashion-Product-Images

✨博客主页&#xff1a;王乐予&#x1f388; ✨年轻人要&#xff1a;Living for the moment&#xff08;活在当下&#xff09;&#xff01;&#x1f4aa; &#x1f3c6;推荐专栏&#xff1a;【图像处理】【千锤百炼Python】【深度学习】【排序算法】 目录 &#x1f63a;一、数据…...