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

作者:后端小肥肠

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

目录

1. 前言

2. 我为什么要自创一套工作流引擎

3. 表结构设计及关系讲解

3.1. 流程类别business_approval_workflow

3.1.1. 表结构

3.1.2. 表关系说明

3.2. 流程定义business_approval_workflow_detail

3.2.1. 表结构

3.2.2. 表关系说明

3.3. 流程任务记录approval_detail

3.3.1. 表结构

3.3.2. 表关系说明

3.4. 流程审批历史 approval_history

3.4.1. 表结构

3.4.2. 表关系说明

3.5. 申请表(流程实例)request

3.5.1. 表结构

4. 核心代码讲解

4.1. 设计用户表

4.2. 集成权限框架SpringSecurity

4.3. 工作流基础代码

4.4. 提交申请

4.5. 查看待我审批

4.6. 执行审批操作

4.7. 查看我已审批

5. 结语


1. 前言

在当前的技术生态中,工作流引擎如Activiti7和Flowable已经成为企业自动化的重要组成部分。然而,使用这些流行解决方案往往伴随着不小的学习成本,特别是Activiti7与Spring Security深度集成,使得不采用Security作为权限框架的系统面临集成上的挑战。此外,许多业务场景并不需要如此复杂的工作流框架。因此,出于对更灵活、更轻量级工作流解决方案的追求,我决定自主开发一套工作流引擎,以更好地满足特定的业务需求,同时减少对外部库的依赖。这不仅是一次技术上的挑战,也是对现有工作流理念的一次实践探索。

2. 我为什么要自创一套工作流引擎

熟悉我的博客读者都知道,我曾经基于Activiti7开发了一套成熟的工作流管理框架。然而,为何我还要自己动手创造一套新的工作流引擎呢?答案很简单:追求轻量化和更精准的边界。我长期专注于数据中台项目,涉及数据的汇集、管理与分发,其中审批流程即为其中一环。除了一个复杂的自定义流程设计和动态指定审批人的项目外,绝大多数系统仅需简单的审批流程,通常仅需1至2级审批,不涉及复杂的流程设计工具。

因此,我开发了一套轻量级工作流引擎,仅需要5张表就能完全满足简单审批流程的需求。这种定制化的设计不仅极大地简化了系统架构,还充分体现了对于业务需求精准匹配的追求。

这一决定不仅是技术上的挑战,更是对传统工作流理念的一次革新实践。

3. 表结构设计及关系讲解

3.1. 流程类别business_approval_workflow

3.1.1. 表结构
CREATE TABLE "public"."business_approval_workflow" ("id" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,"name" varchar(50) COLLATE "pg_catalog"."default" NOT NULL,CONSTRAINT "business_approval_workflow_pkey" PRIMARY KEY ("id")
)
;ALTER TABLE "public"."business_approval_workflow" OWNER TO "postgres";COMMENT ON COLUMN "public"."business_approval_workflow"."name" IS '业务流程名称';

 business_approval_workflow为流程类别表,定义系统中所需流程,id为业务流程id,name为业务流程名称。

3.1.2. 表关系说明

该表为流程类别表,系统中涉及的所有流程都记录在该表中。

3.2. 流程定义business_approval_workflow_detail

3.2.1. 表结构
CREATE TABLE "public"."business_approval_workflow_detail" ("id" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,"workflow_id" varchar(20) COLLATE "pg_catalog"."default" NOT NULL,"serial_number" int4 NOT NULL,"node_name" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,"node_username" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,"is_final" varchar(1) COLLATE "pg_catalog"."default" NOT NULL,CONSTRAINT "business_approval_workflow_detail_pkey" PRIMARY KEY ("id")
)
;ALTER TABLE "public"."business_approval_workflow_detail" OWNER TO "postgres";COMMENT ON COLUMN "public"."business_approval_workflow_detail"."workflow_id" IS '业务流程id';COMMENT ON COLUMN "public"."business_approval_workflow_detail"."serial_number" IS '流程序号';COMMENT ON COLUMN "public"."business_approval_workflow_detail"."node_name" IS '任务节点名称';COMMENT ON COLUMN "public"."business_approval_workflow_detail"."node_username" IS '处理任务节点的username';COMMENT ON COLUMN "public"."business_approval_workflow_detail"."is_final" IS '是否最后一道审批;,1表示是,0表示不是';
3.2.2. 表关系说明

business_approval_workflow_detail为流程定义表,定义流程表中的workflow_id对应business_approval_workflow中的id(逻辑外键)。

该表主要用于定义已知流程中有哪些任务节点(node_name),节点是顺序(serial_number),任务节点的审批人(node_username)。

3.3. 流程任务记录approval_detail

3.3.1. 表结构

approval_detail表记录流程审批过程中任务节点的状态。

CREATE TABLE "public"."approval_detail" ("id" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,"request_id" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,"approver_username" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,"approval_time" timestamp(6),"next_approver_username" varchar(32) COLLATE "pg_catalog"."default","status" varchar(6) COLLATE "pg_catalog"."default" NOT NULL,"remark" varchar(900) COLLATE "pg_catalog"."default","workflow_id" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,"version" int4 NOT NULL DEFAULT 1,"is_deleted" int4 NOT NULL DEFAULT 0,"create_time" timestamp(6),"update_time" timestamp(6),"node_name" varchar(255) COLLATE "pg_catalog"."default","next_node_name" varchar(255) COLLATE "pg_catalog"."default",CONSTRAINT "approval_detail_pkey" PRIMARY KEY ("id")
)
;ALTER TABLE "public"."approval_detail" OWNER TO "postgres";COMMENT ON COLUMN "public"."approval_detail"."id" IS '主键';COMMENT ON COLUMN "public"."approval_detail"."request_id" IS 'request表id';COMMENT ON COLUMN "public"."approval_detail"."approver_username" IS '审批人username';COMMENT ON COLUMN "public"."approval_detail"."approval_time" IS '审批时间';COMMENT ON COLUMN "public"."approval_detail"."next_approver_username" IS '下一个审批人的username';COMMENT ON COLUMN "public"."approval_detail"."status" IS '审批状态;1.待我审批;2.通过;3.驳回; ';COMMENT ON COLUMN "public"."approval_detail"."remark" IS '审批意见';COMMENT ON COLUMN "public"."approval_detail"."workflow_id" IS '业务流程id';COMMENT ON COLUMN "public"."approval_detail"."node_name" IS '当前任务节点名称';COMMENT ON COLUMN "public"."approval_detail"."next_node_name" IS '下一任务节点名称';
3.3.2. 表关系说明

approval_detail表记录流程运行中的任务节点信息。request_id对应request表中的id(逻辑外键);workflow_id对应business_approval_workflow表中的id(逻辑外键),此表可以类比理解为Activiti7中的act_ru_task

3.4. 流程审批历史 approval_history

3.4.1. 表结构

approval_history表记录流程运行时或运转后每个任务节点的历史信息。

CREATE TABLE "public"."approval_history" ("id" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,"request_id" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,"approver_name" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,"approval_time" timestamp(6),"status" varchar(1) COLLATE "pg_catalog"."default","remark" varchar(900) COLLATE "pg_catalog"."default","workflow_id" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,"version" int4 NOT NULL DEFAULT 1,"is_deleted" int4 NOT NULL DEFAULT 0,"applicant_phone" varchar(11) COLLATE "pg_catalog"."default","purpose" varchar(255) COLLATE "pg_catalog"."default","applicant_name" varchar(255) COLLATE "pg_catalog"."default","approver_username" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,"create_time" timestamp(6),"update_time" timestamp(6),CONSTRAINT "approval_history_pkey" PRIMARY KEY ("id")
)
;ALTER TABLE "public"."approval_history" OWNER TO "postgres";COMMENT ON COLUMN "public"."approval_history"."request_id" IS '申请id';COMMENT ON COLUMN "public"."approval_history"."approver_name" IS '审批人姓名';COMMENT ON COLUMN "public"."approval_history"."approval_time" IS '审批时间';COMMENT ON COLUMN "public"."approval_history"."status" IS '审批状态1.待我审批;2.通过;3.驳回';COMMENT ON COLUMN "public"."approval_history"."remark" IS '审批意见';COMMENT ON COLUMN "public"."approval_history"."workflow_id" IS '业务流程id';COMMENT ON COLUMN "public"."approval_history"."applicant_phone" IS '申请人电话';COMMENT ON COLUMN "public"."approval_history"."purpose" IS '申请理由';COMMENT ON COLUMN "public"."approval_history"."applicant_name" IS '申请人姓名';COMMENT ON COLUMN "public"."approval_history"."approver_username" IS '审批人username';
3.4.2. 表关系说明

approval_history表中,request_id对应request表中的id(逻辑外键);workflow_id对应business_approval_workflow表中的id(逻辑外键)。

3.5. 申请表(流程实例)request

3.5.1. 表结构

request表为申请表,同时也记录着整个流程的审批状态,当用户提交申请时在该表中填充数据,当审批状态发生改变时需要同步更新request表中status的状态,与approval_detail中的status不同,request表中status记录的是整个流程的审批状态。

CREATE TABLE "public"."request" ("id" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,"workflow_id" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,"create_time" timestamp(6) NOT NULL,"purpose" varchar(900) COLLATE "pg_catalog"."default" NOT NULL,"status" varchar(1) COLLATE "pg_catalog"."default" NOT NULL,"version" int4 DEFAULT 1,"applicant_name" varchar(255) COLLATE "pg_catalog"."default","applicant_phone" varchar(11) COLLATE "pg_catalog"."default","is_deleted" int4 DEFAULT 0,"update_time" timestamp(6),"applicat_username" varchar(50) COLLATE "pg_catalog"."default","applicat_unit" varchar(50) COLLATE "pg_catalog"."default","resource_id" varchar(255) COLLATE "pg_catalog"."default" DEFAULT ''::character varying
)
;ALTER TABLE "public"."request" OWNER TO "postgres";COMMENT ON COLUMN "public"."request"."workflow_id" IS '业务类型id';COMMENT ON COLUMN "public"."request"."create_time" IS '申请时间;数据库自动填充';COMMENT ON COLUMN "public"."request"."purpose" IS '使用目的';COMMENT ON COLUMN "public"."request"."status" IS '审批状态;1.正在审核;2.通过;3.驳回';COMMENT ON COLUMN "public"."request"."applicant_name" IS '申请人姓名';COMMENT ON COLUMN "public"."request"."applicant_phone" IS '申请人电话';COMMENT ON COLUMN "public"."request"."update_time" IS '更新时间';COMMENT ON COLUMN "public"."request"."applicat_username" IS '申请用户名';COMMENT ON COLUMN "public"."request"."applicat_unit" IS '申请单位';COMMENT ON COLUMN "public"."request"."resource_id" IS '申请资源id';

4. 核心代码讲解

4.1. 设计用户表

CREATE TABLE "public"."sys_user" ("id" varchar(40) COLLATE "pg_catalog"."default" NOT NULL,"username" varchar(60) COLLATE "pg_catalog"."default","password" varchar(60) COLLATE "pg_catalog"."default","nick_name" varchar(60) COLLATE "pg_catalog"."default",CONSTRAINT "sys_user_pkey" PRIMARY KEY ("id")
)
;ALTER TABLE "public"."sys_user" OWNER TO "postgres";CREATE INDEX "username" ON "public"."sys_user" USING btree ("username" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST
);COMMENT ON COLUMN "public"."sys_user"."id" IS '用户 ID';COMMENT ON COLUMN "public"."sys_user"."username" IS '用户名';COMMENT ON COLUMN "public"."sys_user"."password" IS '密码,加密存储';COMMENT ON COLUMN "public"."sys_user"."nick_name" IS '昵称';COMMENT ON TABLE "public"."sys_user" IS '用户信息表';

在表中写死几个用户,设定好他们的角色。

4.2. 集成权限框架SpringSecurity

工作流一般都需要跟权限框架联动,这里假设整合的是SpringSecurity(实际上我没整合权限框架,哈哈,我把用户都写死在代码里面了),整合步骤就略过了,我之出过SpringSecurity系列,包含了用户登录、权限和菜单分配,都有很详细的代码,链接如下:

【Spring Security系列】Spring Security+JWT+Redis实现用户认证登录及登出_spring security jwt 退出登录-CSDN博客

【Spring Security系列】基于Spring Security实现权限动态分配之用户-角色分配_springsecurity新增角色分配权限-CSDN博客

【Spring Security系列】基于Spring Security实现权限动态分配之菜单-角色分配及动态鉴权实践_springsecurity 角色关联菜单-CSDN博客

4.3. 工作流基础代码

本章将详细解说工作流基础代码,包含提交申请查看待我审批执行审批操作查看我已审批接口。

4.4. 提交申请

controller层

    @GetMapping("")public Boolean addRequest(@RequestBody RequestDTO requestDTO){return requestService.addRequest(requestDTO);}

 RequestDTO编写

@Data
@AllArgsConstructor
@NoArgsConstructor
public class RequestDTO {private String workflowId;private String purpose;private String applicantName;private String applicantPhone;private String applicatUsername;private String applicatUnit;private String resourceId;}

servic层

    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 = workflowDetailService.findFirstNodeByWorkflowId(request.getWorkflowId());//获取下一级节点 填充下级节点审批人BusinessApprovalWorkflowDetail nextNode=workflowDetailService.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;}

上述代码实现了一个方法 addRequest,它接收一个 RequestDTO 对象作为参数,首先将其转换为 Request 对象并插入到数据库的 request 表中,设置流程状态为正在审核。然后根据流程定义查询首个审批节点和下一个审批节点的信息,并将这些信息作为审批详情记录插入到 approval_detail 表中,确保申请与审批流程的正确关联和处理。 

4.5. 查看待我审批

controller层

    @GetMapping("/pending-approval")public List<ApprovalDetail> getPendingAppprovalList() {return approvalDetailService.getPendingAppprovalList();}

servic层

    public List<ApprovalDetail> getPendingAppprovalList() {//这里我写死了,实际获取应该走权限框架获取当前在线用户usernameString username="xfc";LambdaQueryWrapper<ApprovalDetail> queryWrapper=new LambdaQueryWrapper<>();queryWrapper.eq(ApprovalDetail::getApproverUsername,username);List<ApprovalDetail> approvalDetails = baseMapper.selectList(queryWrapper);return approvalDetails;}

4.6. 执行审批操作

controller层

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

servic层

    @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()); // 删除当前审批记录// 填充 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); // 插入审批历史记录// 更新 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.setNextApproverUsername(nextNextNode!=null?nextNextNode.getNodeUsername():null);approvalDetail.setApprovalTime(new Date());approvalDetail.setStatus("1"); // 设置为待审批状态baseMapper.updateById(approvalDetail);}} 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; // 或者根据实际需求返回其他业务逻辑}

上述方法实现了审批申请的功能。首先,根据审批人的用户名查询当前需要审批的详细信息,并根据审批状态(通过或驳回)进行不同的处理逻辑。如果是审批通过并且当前节点为流程的最后一个节点,则删除当前审批记录并更新申请状态为通过;否则,更新到下一个审批节点的信息。如果是审批驳回,则直接删除当前审批记录,并更新申请状态为驳回。通过事务管理,保证了数据库操作的原子性和一致性。 

4.7. 查看我已审批

controller层

    @GetMapping("/approved")public List<ApprovalHistory> getApprovedList() {return approvalHistoryService.getApprovedList();}

servic层

    public List<ApprovalHistory> getApprovedList() {//这里我写死了,实际获取应该走权限框架获取当前在线用户usernameString username="xfc";LambdaQueryWrapper<ApprovalHistory>queryWrapper=new LambdaQueryWrapper<>();queryWrapper.eq(ApprovalHistory::getApproverUsername,username);List<ApprovalHistory> approvalHistories = baseMapper.selectList(queryWrapper);return approvalHistories;}

5. 结语

在本文中,我们系统地介绍了如何用Java语言自创一套工作流引擎。通过设计核心表结构和实现基础代码框架,我们打下了坚实的理论基础。在下篇文章中,我们将结合实际项目,展示工作流引擎的实际应用和效果。我们将深入探讨工作流引擎在复杂业务流程中的应用。如本文对您有帮助,请动动小手点点关注哦~

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LVS+Keepalived NGINX+Keepalived 高可用群集实战部署

Keepalived及其工作原理 Keepalived 是一个基于VRRP协议来实现的LVS服务高可用方案&#xff0c;可以解决静态路由出现的单点故障问题。 VRRP协议&#xff08;虚拟路由冗余协议&#xff09; 是针对路由器的一种备份解决方案由多台路由器组成一个热备组&#xff0c;通过共用的…

Linux:基础IO(二.缓冲区、模拟一下缓冲区、详细讲解文件系统)

上次介绍了&#xff1a;Linux&#xff1a;基础IO&#xff08;一.C语言文件接口与系统调用、默认打开的文件流、详解文件描述符与dup2系统调用&#xff09; 文章目录 1.缓冲区1.1概念1.2作用与意义 2.语言级别的缓冲区2.1刷新策略2.2具体在哪里2.3支持格式化 3.自己来模拟一下缓…

简单谈谈云服务器私网IP的存在意义及优势

云服务器是基于虚拟化技术的计算资源&#xff0c;可以在云平台上灵活创建和管理。为了满足不同用户的需求&#xff0c;云服务提供商在云服务器上分配了两种类型的IP地址&#xff1a;公网IP和私网IP。其中&#xff0c;私网IP是指在局域网内使用的内部IP地址&#xff0c;无法通过…

计算机图形学入门11:图形管线与着色器

1.什么是图形管线 把场景中的物体经过一系列的处理&#xff0c;最后一张图像的形式在屏幕上显示出来&#xff0c;这一系列过程就是图形管线(Graphics Pipeline)&#xff0c;也叫实时渲染管线(Real-time Rendering Pipeline)。如下图所示&#xff0c;为整个渲染管线的过程。 渲染…

《幻影大师:透视缠中说禅的虚像与真相》

而且他从不犯错&#xff0c;至少在他的叙述中是这样&#xff0c;所有的文章和言论都被粉饰得完美无瑕&#xff0c;即便有误&#xff0c;他也绝不公开承认&#xff0c;更别提什么真诚的道歉和改正了。那些对他推崇备至的人&#xff0c;多是盲目追随&#xff0c;将他神化为无所不…

Win11 问题集

文章目录 一、Win11 选择其他应用打开无反应1、新建 1.reg 文件2、新建 2.reg 文件3、运行 reg 文件 二、Win11 账户怎么改名 一、Win11 选择其他应用打开无反应 Win11选择打开方式卡死怎么办? 选择打开方式没有反应的解决办法 1、新建 1.reg 文件 1.reg Windows Registry…

代理IP协议有何区别?深入了解 SOCKS5、HTTP 代理

在数字通信领域&#xff0c;数据安全和匿名性都是非常重要的指标。互联网的不断发展催生了几种协议&#xff0c;每种协议都有独特的优势和挑战。其中&#xff0c;SOCKS5 代理、HTTP代理最为广泛使用&#xff0c;下面给大家一起讨论&#xff0c;HTTP代理与 SOCKS5代理&#xff0…

华为OD机试 - 多段线数据压缩(Java 2024 D卷 100分)

华为OD机试 2024D卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;D卷C卷A卷B卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测…

探索 Docker:容器化技术的未来

1. 引言 在传统的软件开发和部署过程中&#xff0c;经常会遇到诸如“开发环境和生产环境不一致”、“依赖环境冲突”、“部署困难”等问题。为了解决这些问题&#xff0c;容器化技术应运而生。Docker 作为最受欢迎的容器平台之一&#xff0c;已经在业界得到广泛应用。它不仅简化…

【chatbot-api开源项目】开发文档

chatbot-api 1. 需求分析1-1. 需求分析1-2. 系统流程图 2. 技术选型3. 项目开发3-1. 项目初始化3-2. 爬取接口获取问题接口回答问题接口创建对应对象 3-3. 调用AI3-4. 定时自动化回答 4. Docker部署5. 扩展5-1. 如果cookie失效了怎么处理5-2. 如何更好的对接多个回答系统 Gitee…

Unity 3D 物体的Inspector面板

1、Transform&#xff1a;位置、旋转、大小 2、Mesh Filter&#xff1a;物体的形状 3、Mesh Renderer&#xff1a;物体渲染&#xff08;物体的衣服&#xff09; 4、Collider&#xff1a;碰撞体

北京多商入驻app开发项目的主要优势及功能

多商入驻app开发项目的定义 随着电子支付技术的不断成熟&#xff0c;全国各地的消费者通过网络在线上购物的频率越来越高&#xff0c;为此&#xff0c;多商入驻app开发项目应用而生。各商家也纷纷开始申请入驻商城平台&#xff0c;开设自己的店铺。 图片来源&#xff1a;unspl…

【iOS】自定义cell及其复用机制

文章目录 cell的复用注册非注册两者的区别 自定义cell cell的复用 当用户滚动 UITableView 或 UICollectionView 时,只有少量可见的 cell 会被实际创建和显示。对于那些暂时不可见的 cell,系统会将它们缓存起来以备将来复用。这就是所谓的 cell 复用机制。 为什么需要cell的复…

【论文阅读】AttnDreamBooth | 面向文本对齐的个性化图片生成

文章目录 1 动机2 方法3 实验 1 动机 使用灵活的文本控制可以实现一些特定的概念的注入从而实现个性化的图片生成。 最经典的比如一些好玩的动漫人物的概念&#xff0c;SD大模型本身是不知道这些概念的&#xff0c;但是通过概念注入是可以实现的从而生成对应的动漫人物 两个…

Vue笔记(三)

上一篇&#xff1a;Vue二&#xff09;-CSDN博客 目录 1.自定义指令 v-loading的封装 2.插槽 文本插槽 文本插槽&#xff08;有默认值&#xff09; 具名插槽 作用域插槽 详细做一个练习 实现如下效果 目录结构 准备数据 父传子数据 使用文本插槽自定义按钮文本 实…

PyTorch计算机视觉入门:测试模型与评估,对单帧图片进行推理

在完成模型的训练之后&#xff0c;对模型进行测试与评估是至关重要的一步&#xff0c;它能帮助我们理解模型在未知数据上的泛化能力。本篇指南将带您了解如何使用PyTorch进行模型测试&#xff0c;并对测试结果进行分析。我们将基于之前训练好的模型&#xff0c;演示如何加载数据…

msvcp120.dll丢失原因分析与解决方法分享

msvcp120.dll 是一个动态链接库&#xff08;Dynamic Link Library, DLL&#xff09;&#xff0c;属于 Microsoft Visual C 2013 再发行组件包的一部分。它提供了 C 标准库的实现&#xff0c;使得使用 C 编写的应用程序能够在运行时动态链接到该库&#xff0c;从而访问其提供的函…

element-plus表单组件之自动补全组件el-autocomplete和级联选择器组件el-cascader

el-autocomplete 自动补全组件 自补全组件的功能和可以根据输入过滤的el-select组件有些类似。 fetch-suggestions 根据输入框的输入获取建议的内容&#xff0c;其接受值是一个函数&#xff0c;有2个参数&#xff0c;querystring:输入的内容&#xff0c;callback内置函数&…

Django DetailView视图

Django的DetailView是一个用于显示单个对象详情的视图。下面是一个使用DetailView来显示单个书籍详情的例子。 1&#xff0c;添加视图 Test/app3/views.py from django.shortcuts import render# Create your views here. from django.views.generic import ListView from .m…

如何判断一个js对象是否存在循环引用

一、背景 在前端JSON.stringfy是我们常用的一个方法&#xff0c;可以将一个对象序列化。 例如将如下对象序列化 const person { name: kalory, age:18}JSON.stringfy(person) // 结果 {"name":"kalory","age":18}将一个数组序列化const arr …