【Gin】精准应用:Gin框架中工厂模式的现代软件开发策略与实施技巧(下)

news2024/11/12 17:02:35

【Gin】精准应用:Gin框架中工厂模式的现代软件开发策略与实施技巧(下)

大家好 我是寸铁👊
【Gin】精准应用:Gin框架中工厂模式的现代软件开发策略与实施技巧(下)✨
喜欢的小伙伴可以点点关注 💝

在这里插入图片描述

前言

本次文章分为上下两部分,上部分为对理论的介绍,下部分为具体的底层代码深度剖析和编程实践,感兴趣的伙伴不要错过哦~

上篇:【Gin】精准应用:Gin框架中工厂模式的现代软件开发策略与实施技巧(上)

在现代软件开发中,设计模式是提高代码质量和可维护性的重要工具之一。特别是对于像Gin这样的高性能、轻量级的Web框架,如何合理运用设计模式显得尤为重要。工厂模式作为其中一种经典的创建型模式,能够帮助开发者有效地解耦对象的创建过程,并提供更大的灵活性和可扩展性。本文将深入探讨在Gin框架中工厂模式的具体应用场景、实施技巧以及现代软件开发中的最佳实践。
工厂模式通过引入工厂类来负责创建对象的过程,可以根据需要动态地生成不同类型的对象,而无需直接在代码中指定具体的类。在Gin框架中,合理运用工厂模式可以优化路由、中间件和控制器的管理,提升代码的模块化程度和可测试性。本文旨在帮助开发者深入理解工厂模式在实际项目中的应用,以及如何利用它来构建更加灵活和可扩展的Web应用程序。


关键的类图和时序图

(1) 类图

角色介绍

Client:
客户端,应用程序类,它需要某种产品对象,但不直接创建它们,而是通过工厂来获取所需的产品对象。


GinFactory:
工厂接口或抽象类,声明了创建产品对象的方法。它可以是抽象的,也可以提供默认的实现,但通常不会创建具体的产品对象。


RouterFactory:
具体工厂类,实现了工厂接口或抽象类,并负责创建具体的产品对象。每个具体工厂对应一种具体产品的创建方式。


IRoute:
产品接口或抽象类,定义了工厂方法模式创建的对象类型。具体的产品类将实现这个接口或继承这个抽象类,客户端最终会使用这些产品。


在这里插入图片描述
图1 工厂模式类图

由图1可知:gin工厂类具有创建gin引擎对象的方法,实现IRoute接口,具体的创建对象职责由不同的Router路由工厂类承担。


(2) 时序图

在这里插入图片描述图2 工厂模式时序图

由图2得:客户端请求产品:客户端Client需要获取具体产品invoke(),调用工厂对象的方法来请求产品对象。
工厂方法调用:工厂对象(Gin)接收到客户端的请求,调用工厂方法(Default())来创建具体产品。
具体工厂创建产品:具体工厂类(Router)实现了工厂方法,根据客户端请求创建具体的产品对象New()
产品创建完成:具体产品(IRoute)被创建并返回给客户端,客户端可以继续使用这个产品实例进行操作。


主程序的流程

由如下图3可得:

程序开始 先调用Gin工厂Defalut()获得engine引擎对象,其中Defalut()方法中包含Router工厂的真正创建对象方法New() 返回创建好的engine引擎对象,返回对象后,客户端获取到engine对象,程序结束。

在这里插入图片描述

图 3 工厂模式主程序流程图

程序模块之间的调用关系

如下图展示程序模块之间的调用关系:
在这里插入图片描述
图4 工厂模式调用关系图

由图4得: 客户端调用gin工厂,gin工厂gin.Default()创建一个默认的Gin引擎对象Default()方法中调用New()创建engine对象实例engine对象的类型为Engine,New()方法中用&{}的方式创建Engine结构体对象,对象创建完毕后,再把对象返回给客户端使用。

在上图的基础上,下面结合设计模式对程序中各个模块的源码进行深入剖析:

在这里插入图片描述

图5 客户端调用代码

首先,在客户端中使用gin工厂调用Defalut()方法得到一个Gin引擎对象。这里无需暴露任何的new创建对象,gin.Default() 可以被看作是一个工厂方法,它封装了创建Gin引擎实例的细节。客户端代码(即这段示例中的 main() 函数)不需要了解或直接处理Gin引擎的内部实现细节。通过调用 gin.Default(),客户端只需获取一个已经配置好的Gin引擎实例,然后可以直接开始定义路由和处理HTTP请求。


在这里插入图片描述图6 抽象工厂代码

该代码的位置位于gin.go文件中的220-226行,需要注意的是在Golang的世界中,并没有 abstract 关键字。Go语言支持面向对象的设计哲学之一是保持简洁和直接,因此它没有类似于其他面向对象语言(如Java、C#)中的抽象类和抽象方法的概念。并且所有的成员都封装在具体的Struct中,可以类比为Java的类,但与Java不同的是,Struct不存放具体的方法,只是将要用到的成员变量封装到一个结构体Struct中,只承担存储的职责,无任何实际的操作。实际的操作只在文件中的方法中具体编写、展开即可。为了比较好理解设计模式关系,可以暂且认为Default()为抽象工厂部分,具体的实现New为具体工厂部分。

工厂方法的定义:在Default()函数内部,首先调用了 New() 函数来创建一个新的 Engine 实例。这里可以将 New() 函数视为简单工厂方法,用于实例化一个基本的 Engine 对象
封装对象创建过程:接着,通过 engine.Use(Logger(), Recovery()) 将日志记录和恢复中间件附加到新创建的引擎实例上。这一步是在工厂方法内部对对象进行配置的典型操作,确保了每个通过 Default() 创建的引擎都具有相同的预配置行为。

配置默认选项:Default() 方法通过接受可选的 OptionFunc 参数来支持对引擎的额外定制。这些选项可以是函数或者闭包,允许开发者在创建引擎时进一步配置和调整其行为,例如设置路由、中间件或者其他定制化的功能。

灵活的选项扩展:Default() 方法通过接受可选的 OptionFunc 参数来支持对引擎的额外定制。这些选项可以是函数或者闭包,允许开发者在创建引擎时进一步配置和调整其行为,例如设置路由、中间件或者其他定制化的功能。

隐藏具体实现细节:
客户端调用 Default() 方法时,只需知道它返回一个预配置好的 Engine 实例,而无需了解内部的对象创建和配置细节。这种封装隐藏了具体实现,降低了客户端代码与Gin框架的耦合度,同时提升了代码的可维护性和灵活性。

小结:Default() 函数在这段代码中充当了工厂方法的角色,通过封装对象的创建和配置过程,提供了一个统一的接口来获取预配置的 Engine 实例。这种设计符合工厂模式的核心思想,即封装和隐藏对象的创建细节,提供灵活性和可扩展性的同时,简化了客户端代码的使用方式。


在这里插入图片描述

图7 具体工厂代码

该代码的位置位于gin.go文件中的188-218行:由于没有抽象类和具体类的概念,这里可以将New()具体实现部分作为具体工厂角色。下面对具体工厂的职责和对象的初始化代码进行剖析:

在这里插入图片描述
图8 New方法代码

189行:debugPrintWARNINGNew():这是一个调试函数或者日志函数的调用,用于输出或记录关于 New() 函数被调用的警告信息或日志。它通常用于开发和调试阶段,帮助开发人员跟踪函数的调用流程。


在这里插入图片描述
图9 定义Engine对象

191-195行:engine := &Engine{ … }:创建一个名为 engine 的新的 Engine 结构体实例。在这里,使用结构体字面量初始化了 Engine 和 RouterGroup 结构体的字段。Engine 结构体可能包含了整个应用程序的路由、中间件和其他配置信息。


在这里插入图片描述图10 结构体字段代码

196-212行: Engine 结构体的字段初始化:
FuncMap:模板函数映射,初始化为空。
RedirectTrailingSlash:是否重定向尾部斜杠,默认为 true。
RedirectFixedPath:是否重定向固定路径,默认为 false。
HandleMethodNotAllowed:是否处理不允许的方法,默认为 false。
ForwardedByClientIP:是否使用客户端IP地址,默认为 true。
RemoteIPHeaders:远程IP地址的请求头字段,默认为 [“X-Forwarded-For”, “X-Real-IP”]。
TrustedPlatform:受信任的平台,默认为 defaultPlatform。
UseRawPath:是否使用原始路径,默认为 false。
RemoveExtraSlash:是否移除额外的斜杠,默认为 false。
UnescapePathValues:是否解码路径中的值,默认为 true。
MaxMultipartMemory:多部分表单的最大内存,默认为 defaultMultipartMemory。
trees:方法树的初始化,使用 make 函数创建一个容量为 9 的空切片。
delims:渲染模板的分隔符,默认左右分隔符为 {{ 和 }}。
secureJSONPrefix:安全的 JSON 前缀,默认为 “while(1);”。
trustedProxies:受信任的代理服务器列表,默认为 [“0.0.0.0/0”, “::/0”]。
trustedCIDRs:受信任的CIDR列表,默认为 defaultTrustedCIDRs。


在这里插入图片描述图11 engine方法代码

213行: engine.RouterGroup.engine = engine:将当前创建的 engine 实例赋给 RouterGroup 结构体中的 engine 字段。这个步骤确保 RouterGroup 中可以访问到当前的 engine 实例。
214-216行:engine.pool.New = func() any { … }:定义了 pool 结构体中 New 字段的函数回调。这个函数会在需要时创建新的上下文对象。具体来说,它通过调用 engine.allocateContext(engine.maxParams) 来创建一个新的上下文对象。这种设计通常用于对象池技术,用于重用对象并减少内存分配的开销。
217-218行:return engine.With(opts…):最后,调用 With() 方法,并传入 opts… 参数列表中的配置选项,对 engine 进行最后的配置。这里假设 With() 方法用于应用外部传入的配置选项,返回一个完全配置好的 Engine 实例。


在这里插入图片描述图12 engine结构体代码

在这里插入图片描述
图13 engine结构体代码

89-176行:Engine结构体的字段说明如下(省略部分为注释):
RouterGroup:嵌入了一个 RouterGroup 结构体,表示 Engine 结构体包含了 RouterGroup 的属性和方法。
RedirectTrailingSlash bool:布尔类型的属性 RedirectTrailingSlash,用于控制是否重定向尾部斜杠。
RedirectFixedPath bool:布尔类型的属性 RedirectFixedPath,用于控制是否重定向固定路径。
HandleMethodNotAllowed bool:布尔类型的属性 HandleMethodNotAllowed,用于控制是否处理不允许的方法。
ForwardedByClientIP bool:布尔类型的属性 ForwardedByClientIP,用于控制是否通过客户端IP进行转发。
AppEngine bool:布尔类型的属性 AppEngine,表示是否是 AppEngine。
UseRawPath bool:布尔类型的属性 UseRawPath,用于控制是否使用原始路径。
UnescapePathValues bool:布尔类型的属性 UnescapePathValues,用于控制是否解码路径值。
RemoveExtraSlash bool:布尔类型的属性 RemoveExtraSlash,用于控制是否移除额外的斜杠。
RemoteIPHeaders []string:字符串数组类型的属性 RemoteIPHeaders,存储远程IP的头部信息。
TrustedPlatform string:字符串类型的属性 TrustedPlatform,存储受信任的平台。
MaxMultipartMemory int64:int64 类型的属性 MaxMultipartMemory,表示最大的多部分内存大小。
UseH2C bool:布尔类型的属性 UseH2C,表示是否使用 H2C。
ContextWithFallback bool:布尔类型的属性 ContextWithFallback,用于控制是否使用后备上下文。
delims render.Delims:render.Delims 类型的属性 delims,存储渲染分隔符。
secureJSONPrefix string:字符串类型的属性 secureJSONPrefix,存储安全JSON的前缀。
HTMLRender render.HTMLRender:HTMLRender 类型的属性 HTMLRender,表示HTML渲染器。
FuncMap template.FuncMap:template.FuncMap 类型的属性 FuncMap,存储函数映射。
allNoRoute HandlersChain:HandlersChain 类型的属性 allNoRoute,表示所有没有路由的处理程序链。
allNoMethod HandlersChain:HandlersChain 类型的属性 allNoMethod,表示所有没有方法的处理程序链。
noRoute HandlersChain:HandlersChain 类型的属性 noRoute,表示没有路由的处理程序链。
noMethod HandlersChain:HandlersChain 类型的属性 noMethod,表示没有方法的处理程序链。
pool sync.Pool:sync.Pool 类型的属性 pool,表示同步池。
trees methodTrees:methodTrees 类型的属性 trees,表示方法树。
maxParams uint16:uint16 类型的属性 maxParams,表示最大参数数量。
maxSections uint16:uint16 类型的属性 maxSections,表示最大节段数量。
trustedProxies []string:字符串数组类型的属性 trustedProxies,存储受信任的代理。
trustedCIDRs []*net.IPNet:net.IPNet 类型的指针数组属性 trustedCIDRs,存储受信任的CIDR网段。


案例及调试分析

测试案例

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

// 定义一个结构体来封装 Gin 框架相关的操作
type Server struct {
	router *gin.Engine
}

// 初始化 Server 结构体对象
func GetServer() *Server {
	return &Server{
		router: gin.Default(),
	}
}

// Gin引擎对象调用setupRoutes函数,设置路由处理。
func (s *Server) setupRoutes() {
	s.router.GET("/", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "Hello, welcome to the Factory demo!",
		})
	})
}

// 启动 HTTP 服务器
func (s *Server) runServer(addr string) error {
	return s.router.Run(addr)
}

func main() {
	server := GetServer()
	server.setupRoutes()
	err := server.runServer(":8080")
	if err != nil {
		panic(err)
	}
}


工厂模式案例详细说明如下:
在这里插入图片描述
图82 定义的结构体信息

Server 结构体用来封装 Gin 框架的相关操作,其中包括一个 router 字段,类型为 *gin.Engine,即 Gin 框架的核心引擎。


在这里插入图片描述

图83 获得Server的方法

GetServer是一个工厂方法,用来创建并初始化 Server 结构体的实例。在这里,它返回一个新的 Server 实例,并通过gin.Default()方法创建一个默认配置的 Gin 引擎,并将其赋值给 router 字段


在这里插入图片描述

图84 设置路由的方法

setupRoute方法用于设置 HTTP 请求的路由处理函数。在这里,它定义了一个 GET 请求的处理函数,当用户访问根路径 “/” 时,返回一个 JSON 响应,内容为 {"message": "Hello, welcome to the Factory demo!"}


在这里插入图片描述图85 启动HTTP服务器的方法

runServer 方法用于启动 HTTP 服务器,监听指定的地址(addr 参数)。它调用了 Gin 框架的 Run 方法来启动 HTTP 服务器,并返回可能出现的错误error。

在这里插入图片描述
图86 客户端主方法

main 函数是程序的入口点。在 main 函数中:首先使用 GetServer 方法创建了一个 Server 实例。然后调用 setupRoutes 方法设置了 HTTP 请求的路由处理函数。最后调用runServer方法启动 HTTP 服务器,监听 8080 端口。如果启动过程中发生错误,通过panic函数处理并输出错误信息。 调试分析如下:Gin引擎对象启动成功,正在监听8080端口,Demo启动成功!

在这里插入图片描述
图87 工厂模式调试成功图


测试结果

服务端监听8080端口,并往客户端的端口发送一条Get类型的Json请求信息。工厂模式测试成功!

在这里插入图片描述图127 服务端监听8080端口


在这里插入图片描述
图128 获取服务端的响应信息


结语

通过本文的探讨,我们深入了解了工厂模式在现代软件开发中的关键作用和实际应用。工厂模式不仅提供了一种灵活的对象创建机制,还能有效地管理复杂系统中的对象依赖关系。无论是简单工厂、工厂方法还是抽象工厂模式,它们都为开发者提供了多种选择,以应对不同的设计需求和业务场景。在未来的软件开发中,结合工厂模式的设计思想,将有助于构建更加稳健、可维护和可扩展的应用程序。

注:本次文章分为上下两部分,上部分为对理论的介绍,下部分为具体的底层代码深度剖析和编程实践,感兴趣的伙伴不要错过哦~


看到这里的小伙伴,恭喜你又掌握了一个技能👊
希望大家能取得胜利,坚持就是胜利💪
我是寸铁!我们下期再见💕


在这里插入图片描述

往期好文💕

保姆级教程

【保姆级教程】Windows11下go-zero的etcd安装与初步使用

【保姆级教程】Windows11安装go-zero代码生成工具goctl、protoc、go-zero

【Go-Zero】手把手带你在goland中创建api文件并设置高亮


报错解决

【Go-Zero】Error: user.api 27:9 syntax error: expected ‘:‘ | ‘IDENT‘ | ‘INT‘, got ‘(‘ 报错解决方案及api路由注意事项

【Go-Zero】Error: only one service expected goctl一键转换生成rpc服务错误解决方案

【Go-Zero】【error】 failed to initialize database, got error Error 1045 (28000):报错解决方案

【Go-Zero】Error 1045 (28000): Access denied for user ‘root‘@‘localhost‘ (using password: YES)报错解决方案

【Go-Zero】type mismatch for field “Auth.AccessSecret“, expect “string“, actual “number“报错解决方案

【Go-Zero】Error: user.api 30:2 syntax error: expected ‘)‘ | ‘KEY‘, got ‘IDENT‘报错解决方案

【Go-Zero】Windows启动rpc服务报错panic:context deadline exceeded解决方案


Go面试向

【Go面试向】defer与time.sleep初探

【Go面试向】defer与return的执行顺序初探

【Go面试向】Go程序的执行顺序

【Go面试向】rune和byte类型的认识与使用

【Go面试向】实现map稳定的有序遍历的方式

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

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

相关文章

智能家居全在手机端进行控制,未来已来!

未来触手可及:智能家居,手机端的全控时代 艾斯视觉的观点是:在不远的将来,家,这个温馨的港湾,将不再只是我们休憩的场所,而是科技与智慧的结晶。想象一下,只需轻触手机屏幕&#xf…

如何实现CPU最大处理效率

如何实现CPU最大处理效率 CPU,或称为中央处理器,是计算机中负责执行指令和处理数据的核心部件。它的工作原理可简单概括为"取指、译码、执行、存储"四个步骤,也称为计算机的指令周期。 取指(Fetch):在取指阶段,CPU从内存中获取下一条要执行的指令,并存放在指…

回顾网络路,心率就过速

笔者上网写作已满16年,其间加盟过国内互联网的知名网站自媒体至少在40至50家之多,但由于有的被已被勒令停刊了(如《天涯论坛》),有的则因其改版而只保留了极少数擅于唱颂的写手(如《强国论坛》)…

【SpringCloud】企业认证、分布式事务,分布式锁方案落地-1

目录 HR企业入驻 HR企业入驻 - 认证流程解析 HR企业入驻 - 查询企业是否存在 HR企业入驻 - 上传企业logo与营业执照 HR企业入驻 - 新企业(数据字典与行业tree结构解析) 行业tree 行业tree - 创建节点 行业tree - 查询一级分类 行业tree - 查询子分…

计算存储背景与发展

随着云计算、企业级应用以及物联网领域的飞速发展,当前的数据处理需求正以前所未有的规模增长,以满足存储行业不断变化的需求。这种增长导致网络带宽压力增大,并对主机计算资源(如内存和CPU)造成极大负担,进…

Redis的使用场景——热点数据缓存

热点数据缓存 Redis的使用场景——热点数据的缓存 1.1 什么是缓存 为了把一些经常访问的数据,放入缓存中以减少对数据库的访问效率,从而减少数据库的压力,提高程序的性能。【在内存中存储】 1.2 缓存的原理 查询缓存中是否存在对应的数据如…

05 capture软件创建元器件库(以STM32为例)

05 创建元器件库_以STM32为例 一、新建原理图库文件二、新建器件三、开始创建元器件 一些IC类元件,需要自己创建元器件库。 先看视频,然后自己创建STM32F103C8T6的LQFP48的元器件。 STM32F103C8T6是目前为止,自己用的最多的芯片。 先要有数据…

nodejs安装及环境配置建材商城管理系统App

✌网站介绍:✌10年项目辅导经验、专注于计算机技术领域学生项目实战辅导。 ✌服务范围:Java(SpringBoo/SSM)、Python、PHP、Nodejs、爬虫、数据可视化、小程序、安卓app、大数据等设计与开发。 ✌服务内容:免费功能设计、免费提供开题答辩P…

文件包涵条件竞争(ctfshow82)

Web82 利用 session.upload_progress 包含文件漏洞 <!DOCTYPE html> <html> <body> <form action"https://09558c1b-9569-4abd-bf78-86c4a6cb6608.challenge.ctf.show//" method"POST" enctype"multipart/form-data"> …

C语言的发展过程介绍

引言 C语言&#xff0c;由丹尼斯里奇&#xff08;Dennis Ritchie&#xff09;在20世纪70年代初期于贝尔实验室开发&#xff0c;是计算机科学史上最具影响力的编程语言之一。本文将概述C语言的发展历程&#xff0c;并提供一些代码示例来展示其演变。 起源&#xff1a;UNIX和C语言…

自动化测试--WebDriver API

1. 元素定位方法 通过 ID 定位&#xff1a;如果元素具有唯一的 ID 属性&#xff0c;可以使用 findElement(By.id("elementId")) 方法来定位元素。通过 Name 定位&#xff1a;使用 findElement(By.name("elementName")) 来查找具有指定名称的元素。通过 Cl…

重生之“我打数据结构,真的假的?”--5.堆(无习题)

1.堆的概念与结构 如果有⼀个关键码的集合 &#xff0c;把它的所有元素按完全⼆叉树的顺序存储⽅ 式存储&#xff0c;在⼀个⼀维数组中&#xff0c;并满⾜&#xff1a; &#xff08; 且 &#xff09;&#xff0c; i 0、1、2... &#xff0c;则称为⼩堆(或⼤堆)。将根结点最⼤的…

逻辑处理模块:FPGA复旦微JFM7VX690T36+网络加速器:雄立XC13080-500C

逻辑处理模块通常是指在计算机系统、软件应用或电子设备中负责执行逻辑运算和决策过程的组件。 在不同的领域和技术中&#xff0c;逻辑处理模块可能有不同的实现方式和名称&#xff0c;但它们的核心功能都是基于输入数据进行逻辑判断和处理&#xff0c;并产生相应的输出结果。下…

GO-学习-03-基本数据类型

数据类型&#xff1a;基本数据类型和复合数据类型 基本数据类型&#xff1a;整型、浮点型、布尔型、字符串 复合数据类型&#xff1a;数组、切片、结构体、函数、map、通道&#xff08;channel&#xff09;、接口 整型&#xff1a; package main import "fmt" im…

react-native从入门到实战系列教程一环境安装篇

充分阅读官网的环境配置指南&#xff0c;严格按照他的指导作业&#xff0c;不然你一直只能在web或沙箱环境下玩玩 极快的网络和科学上网&#xff0c;必备其中的一个较好的心理忍受能力&#xff0c;因为上面一点就可以让你放弃坚持不懈&#xff0c;努力尝试 成功效果 三大件 …

「Unity3D」场景中的距离单位Unit与相关设置PixelsToUnits、PixelsPerUnit

GameObject在场景的位置Position&#xff0c;并没有明确是什么具体单位——如&#xff1a;Transform的x、y、z&#xff0c;或RectTransform的PosX、PosY、PosZ。而RectTransform在面板上显示的Width和Height&#xff0c;也没有具体单位&#xff0c;其实并不是像素。 事实上&am…

python+vue3+onlyoffice在线文档系统实战20240725笔记,首页开发

解决遗留问题 内容区域的高度没有生效&#xff0c;会随着菜单的高度自动变化。 解决方案&#xff1a;给侧边加上一个最小高度。 首页设计 另一种设计&#xff1a; 进来以后&#xff0c;是所有的文件夹和最近的文件。 有一张表格&#xff0c;类似于Windows目录详情&…

友盟U-APM——优秀的前端性能监控工具

在数字化转型浪潮的推动下,移动应用已成为企业连接用户、驱动业务增长的核心载体。然而,随着应用复杂度的日益提升,用户对于应用性能稳定性的期待也水涨船高。面对应用崩溃、卡顿、加载缓慢等频发问题,如何确保应用的流畅运行,成为产研团队亟待解决的关键挑战。在此背景下,友盟…

vite5-macos仿macOS网页osx管理系统|vue3+arcoDesign桌面os

基于vite5.xvue3arco-design原创自研网页版os管理框架ViteWebOS。 使用最新前端技术vite5vue3pinia2arcoDesignsortablejsecharts搭建网页pc版桌面os式后台管理系统解决方案。支持自定义桌面栅格布局引擎、可拖拽桌面图标、多屏分页管理、自定义桌面壁纸主题、毛玻璃虚化背景等…

【漏洞复现】E-Cology OA——WorkflowServiceXml——SQL注入

声明&#xff1a;本文档或演示材料仅供教育和教学目的使用&#xff0c;任何个人或组织使用本文档中的信息进行非法活动&#xff0c;均与本文档的作者或发布者无关。 文章目录 漏洞描述漏洞复现测试工具 漏洞描述 E-Cology OA协同商务系统是一款面向中大型组织的数字化办公产品…