Files
quark-go/pkg/builder/engine.go
2024-02-24 14:09:42 +08:00

688 lines
15 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package builder
import (
"io"
"net/http"
"reflect"
"runtime"
"strings"
"github.com/gorilla/sessions"
"github.com/labstack/echo/v4"
"github.com/quarkcloudio/quark-go/v2/pkg/dal"
"github.com/quarkcloudio/quark-go/v2/pkg/gopkg"
"github.com/quarkcloudio/quark-go/v2/pkg/utils/file"
"github.com/redis/go-redis/v9"
"gorm.io/gorm"
)
const (
// 应用名称
AppName = "QuarkGo"
// 版本号
Version = "2.3.0"
// 包名
PkgName = "github.com/quarkcloudio/quark-go/v2"
)
type Engine struct {
echo *echo.Echo // Echo框架实例
useHandlers []func(ctx *Context) error // 中间件方法
config *Config // 配置
cookieStore *sessions.CookieStore // Cookie存储用于保存Session
providers []interface{} // 服务列表
urlPaths []*UrlPath // 请求路径列表
routePaths []*RouteMapping // 路由路径列表
}
type RouteMapping struct {
Method string
Path string
Handler func(ctx *Context) error
}
type UrlPath struct {
Method string
Url string
}
type DBConfig struct {
Dialector gorm.Dialector
Opts gorm.Option
}
type RedisConfig struct {
Host string // 地址
Password string // 密码
Port string // 端口
Database int // 数据库
}
type Config struct {
AppKey string // 应用加密Key用于JWT认证
DBConfig *DBConfig // 数据库配置
RedisConfig *RedisConfig // Redis配置
CookieStore *sessions.CookieStore // Cookie存储用于保存Session
StaticPath string // 静态文件目录
Providers []interface{} // 服务列表
}
// 定义路由组
type Group struct {
engine *Engine
echoGroup *echo.Group
}
// 定义路由方法类型
type Handle func(ctx *Context) error
// 全局配置
var AppConfig *Config
// 初始化对象
func New(config *Config) *Engine {
// 初始化应用配置
AppConfig = config
// 初始化echo引擎
e := echo.New()
// 隐藏banner
e.HideBanner = true
// 初始化数据库
if config.DBConfig != nil {
dal.InitDB(config.DBConfig.Dialector, config.DBConfig.Opts)
}
// 初始化Redis
if config.RedisConfig != nil {
dal.InitRedis(&redis.Options{
Addr: config.RedisConfig.Host + ":" + config.RedisConfig.Port,
Password: config.RedisConfig.Password,
DB: config.RedisConfig.Database,
})
}
cookieStore := sessions.NewCookieStore([]byte(config.AppKey))
// 初始化Cookie存储
if config.CookieStore != nil {
cookieStore = config.CookieStore
}
// 定义结构体
engine := &Engine{
echo: e,
providers: config.Providers,
config: config,
cookieStore: cookieStore,
}
// 默认WEB资源目录
if config.StaticPath == "" {
config.StaticPath = "./web"
}
// 下载静态文件
if !file.IsExist(config.StaticPath) {
err := gopkg.New(PkgName, Version).Save("web", config.StaticPath)
if err != nil {
panic(err)
}
}
// 初始化请求列表
engine.initPaths()
// 调用初始化方法
return engine
}
// 获取当前配置
func GetConfig() *Config {
return AppConfig
}
// 获取当前配置
func (p *Engine) GetConfig() *Config {
return p.config
}
// 获取所有服务
func (p *Engine) GetProviders() []interface{} {
return p.providers
}
// 创建上下文
func (p *Engine) NewContext(writer http.ResponseWriter, request *http.Request) *Context {
echoContext := p.echo.NewContext(request, writer)
return &Context{
Engine: p,
EchoContext: echoContext,
Request: request,
Writer: writer,
}
}
// 转换Request、Response对象
func (p *Engine) TransformContext(fullPath string, header map[string][]string, method string, url string, body io.Reader, writer io.Writer) *Context {
// 转换为http.ResponseWriter
w := NewResponse(writer)
// 转换为http.Request
r := NewRequest(method, url, body)
// 转换Header
r.Header = header
// 创建上下文
ctx := p.NewContext(w, r)
// 设置当前路由
ctx.SetFullPath(fullPath)
// 返回对象
return ctx
}
// 初始化请求列表
func (p *Engine) initPaths() {
var (
urlPaths []*UrlPath
routePaths []*RouteMapping
)
if p.urlPaths != nil && p.routePaths != nil {
return
}
for _, provider := range p.providers {
// 初始化路由
provider.(interface {
RouteInit() interface{}
}).RouteInit()
// 加载自定义路由
provider.(interface {
Route() interface{}
}).Route()
// 获取模板定义的路由
templateRoutes := provider.(interface {
GetRouteMapping() []*RouteMapping
}).GetRouteMapping()
for _, v := range templateRoutes {
providerName := reflect.TypeOf(provider).String()
getNames := strings.Split(providerName, ".")
structName := getNames[len(getNames)-1]
if strings.Contains(v.Path, ":resource") {
url := strings.Replace(v.Path, ":resource", strings.ToLower(structName), -1)
// 处理行为
if strings.Contains(url, ":uriKey") {
// 获取行为
actions := provider.(interface {
Actions(ctx *Context) []interface{}
}).Actions(&Context{})
// 解析行为
for _, av := range actions {
// 模版初始化
av.(interface {
TemplateInit(ctx *Context) interface{}
}).TemplateInit(&Context{})
// uri唯一标识
uriKey := av.(interface {
GetUriKey(interface{}) string
}).GetUriKey(av)
// 行为类型
actionType := av.(interface {
GetActionType() string
}).GetActionType()
// 解析行为
if actionType == "dropdown" {
// 获取dropdown里面行为
dropdownActions := av.(interface {
GetActions() []interface{}
}).GetActions()
// 解析行为
for _, dropdownAction := range dropdownActions {
// uri唯一标识
uriKey := dropdownAction.(interface {
GetUriKey(interface{}) string
}).GetUriKey(dropdownAction)
url = strings.Replace(url, ":uriKey", uriKey, -1)
}
} else {
url = strings.Replace(url, ":uriKey", uriKey, -1)
}
}
}
urlPaths = append(urlPaths, &UrlPath{
Method: v.Method,
Url: url,
})
}
if !hasRoutePath(routePaths, v.Method, v.Path) {
routePaths = append(routePaths, &RouteMapping{v.Method, v.Path, v.Handler})
}
}
}
p.urlPaths = urlPaths
p.routePaths = routePaths
}
// 判断是否存在RoutePath
func hasRoutePath(routePaths []*RouteMapping, method string, path string) bool {
var has bool
for _, v := range routePaths {
if v.Method == method && v.Path == path {
has = true
}
}
return has
}
// 获取请求列表
func (p *Engine) GetUrlPaths() []*UrlPath {
return p.urlPaths
}
// 获取路由列表
func (p *Engine) GetRoutePaths() []*RouteMapping {
return p.routePaths
}
// 通用调用方法
func (p *Engine) Use(args interface{}) {
argsName := reflect.TypeOf(args).String()
switch argsName {
case "func(*builder.Context) error":
p.useHandlers = append(p.useHandlers, args.(func(ctx *Context) error))
default:
panic(argsName + " arguments was not found")
}
}
// 获取通用调用方法
func (p *Engine) UseHandlers() []func(ctx *Context) error {
return p.useHandlers
}
// 解析模版方法
func (p *Engine) handleParser(ctx *Context) error {
var (
result []reflect.Value
err error
templateInstance interface{}
)
// 获取模板实例
templateInstance = ctx.Template
if templateInstance == nil {
return ctx.String(200, "unable to find resource instance")
}
// 执行挂载的方法
for _, v := range p.routePaths {
if v.Path == ctx.FullPath() {
// 反射实例值
value := reflect.ValueOf(templateInstance)
if !value.IsValid() {
continue
}
// 获取指针
pc := reflect.ValueOf(v.Handler).Pointer()
// 获取func全路径
fn := runtime.FuncForPC(pc)
fullPaths := strings.Split(fn.Name(), ".")
lastPathName := fullPaths[len(fullPaths)-1]
// 获取方法名称
funcNames := strings.Split(lastPathName, "-")
if len(funcNames) <= 0 {
continue
}
funcName := funcNames[0]
// 获取实例上方法
method := value.MethodByName(funcName)
if !method.IsValid() {
continue
}
// 反射执行结果
result = method.Call([]reflect.Value{
reflect.ValueOf(ctx),
})
if len(result) != 1 {
continue
}
// 执行结果
if v, ok := result[0].Interface().(error); ok {
err = v
}
}
}
return err
}
// 渲染
func (p *Engine) Render(ctx *Context) error {
// 初始化模板
err := ctx.InitTemplate(ctx)
if err != nil {
return err
}
// 解析UseHandler方法
err = ctx.useHandlerParser()
if err != nil {
if err.Error() == ctx.Next().Error() {
// 解析模版方法
return p.handleParser(ctx)
}
}
// 解析模版方法
return err
}
// 处理模版上的路由映射关系
func (p *Engine) routeMappingParser() {
for _, routePath := range p.routePaths {
switch routePath.Method {
case "GET":
p.GET(routePath.Path, func(ctx *Context) error {
return p.handleParser(ctx)
})
case "HEAD":
p.HEAD(routePath.Path, func(ctx *Context) error {
return p.handleParser(ctx)
})
case "OPTIONS":
p.OPTIONS(routePath.Path, func(ctx *Context) error {
return p.handleParser(ctx)
})
case "POST":
p.POST(routePath.Path, func(ctx *Context) error {
return p.handleParser(ctx)
})
case "PUT":
p.PUT(routePath.Path, func(ctx *Context) error {
return p.handleParser(ctx)
})
case "PATCH":
p.PATCH(routePath.Path, func(ctx *Context) error {
return p.handleParser(ctx)
})
case "DELETE":
p.DELETE(routePath.Path, func(ctx *Context) error {
return p.handleParser(ctx)
})
case "Any":
p.Any(routePath.Path, func(ctx *Context) error {
return p.handleParser(ctx)
})
}
}
}
// 获取Echo框架实例
func (p *Engine) Echo() *echo.Echo {
return p.echo
}
// 适配Echo框架方法
func (p *Engine) echoHandle(path string, handle Handle, c echo.Context) error {
// 创建上下文
ctx := p.NewContext(c.Response().Writer, c.Request())
// 设置路由路径
ctx.SetFullPath(path)
// 初始化模板
ctx.InitTemplate(ctx)
// 解析UseHandler方法
err := ctx.useHandlerParser()
if err != nil {
if err.Error() == ctx.Next().Error() {
// 执行方法
return handle(ctx)
}
}
return err
}
// 加载静态文件
func (p *Engine) Static(pathPrefix string, fsRoot string) {
p.echo.Static(pathPrefix, fsRoot)
}
// GET请求
func (p *Engine) GET(path string, handle Handle) error {
p.echo.GET(path, func(c echo.Context) error {
return p.echoHandle(path, handle, c)
})
return nil
}
// HEAD请求
func (p *Engine) HEAD(path string, handle Handle) error {
p.echo.HEAD(path, func(c echo.Context) error {
return p.echoHandle(path, handle, c)
})
return nil
}
// OPTIONS请求
func (p *Engine) OPTIONS(path string, handle Handle) error {
p.echo.OPTIONS(path, func(c echo.Context) error {
return p.echoHandle(path, handle, c)
})
return nil
}
// POST请求
func (p *Engine) POST(path string, handle Handle) error {
p.echo.POST(path, func(c echo.Context) error {
return p.echoHandle(path, handle, c)
})
return nil
}
// PUT请求
func (p *Engine) PUT(path string, handle Handle) error {
p.echo.PUT(path, func(c echo.Context) error {
return p.echoHandle(path, handle, c)
})
return nil
}
// PATCH请求
func (p *Engine) PATCH(path string, handle Handle) error {
p.echo.PATCH(path, func(c echo.Context) error {
return p.echoHandle(path, handle, c)
})
return nil
}
// DELETE请求
func (p *Engine) DELETE(path string, handle Handle) error {
p.echo.DELETE(path, func(c echo.Context) error {
return p.echoHandle(path, handle, c)
})
return nil
}
// Any请求
func (p *Engine) Any(path string, handle Handle) error {
p.echo.Any(path, func(c echo.Context) error {
return p.echoHandle(path, handle, c)
})
return nil
}
// 路由组
func (p *Engine) Group(path string, handlers ...Handle) *Group {
echoGroup := p.echo.Group(path, func(next echo.HandlerFunc) echo.HandlerFunc {
if len(handlers) > 0 {
for _, handle := range handlers {
newHandle := func(c echo.Context) error {
err := p.echoHandle(path, handle, c)
if err != nil {
// 执行下一步操作这里应该放到数组里面执行暂时用NextUseHandler
if err.Error() == "NextUseHandler" {
return next(c)
}
}
return err
}
return newHandle
}
}
return next
})
return &Group{engine: p, echoGroup: echoGroup}
}
// GET请求
func (p *Group) GET(path string, handle Handle) error {
p.echoGroup.GET(path, func(c echo.Context) error {
return p.engine.echoHandle(path, handle, c)
})
return nil
}
// HEAD请求
func (p *Group) HEAD(path string, handle Handle) error {
p.echoGroup.HEAD(path, func(c echo.Context) error {
return p.engine.echoHandle(path, handle, c)
})
return nil
}
// OPTIONS请求
func (p *Group) OPTIONS(path string, handle Handle) error {
p.echoGroup.OPTIONS(path, func(c echo.Context) error {
return p.engine.echoHandle(path, handle, c)
})
return nil
}
// POST请求
func (p *Group) POST(path string, handle Handle) error {
p.echoGroup.POST(path, func(c echo.Context) error {
return p.engine.echoHandle(path, handle, c)
})
return nil
}
// PUT请求
func (p *Group) PUT(path string, handle Handle) error {
p.echoGroup.PUT(path, func(c echo.Context) error {
return p.engine.echoHandle(path, handle, c)
})
return nil
}
// PATCH请求
func (p *Group) PATCH(path string, handle Handle) error {
p.echoGroup.PATCH(path, func(c echo.Context) error {
return p.engine.echoHandle(path, handle, c)
})
return nil
}
// DELETE请求
func (p *Group) DELETE(path string, handle Handle) error {
p.echoGroup.DELETE(path, func(c echo.Context) error {
return p.engine.echoHandle(path, handle, c)
})
return nil
}
// Any请求
func (p *Group) Any(path string, handle Handle) error {
p.echoGroup.Any(path, func(c echo.Context) error {
return p.engine.echoHandle(path, handle, c)
})
return nil
}
// 路由组
func (p *Group) Group(path string, handlers ...Handle) *Group {
echoGroup := p.engine.echo.Group(path, func(next echo.HandlerFunc) echo.HandlerFunc {
if len(handlers) > 0 {
for _, handle := range handlers {
newHandle := func(c echo.Context) error {
err := p.engine.echoHandle(path, handle, c)
if err != nil {
// 执行下一步操作这里应该放到数组里面执行暂时用NextUseHandler
if err.Error() == "NextUseHandler" {
return next(c)
}
}
return err
}
return newHandle
}
}
return next
})
return &Group{engine: p.engine, echoGroup: echoGroup}
}
// Run Server
func (p *Engine) Run(addr string) {
// 处理模版上的路由映射关系
p.routeMappingParser()
// 启动服务
p.echo.Logger.Fatal(p.echo.Start(addr))
}