mirror of
https://github.com/quarkcloudio/quark-go.git
synced 2025-09-26 20:11:11 +08:00
688 lines
15 KiB
Go
688 lines
15 KiB
Go
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))
|
||
}
|