插件增加静态文件目录自动映射,优化插件模板引擎与主模块的耦合关系

This commit is contained in:
孟帅
2023-06-14 18:09:49 +08:00
parent a232986311
commit 564107b980
29 changed files with 210 additions and 158 deletions

View File

@@ -11,16 +11,26 @@ import (
"hotgo/internal/consts"
)
func GetTag(name string) string {
return consts.AddonsTag + name
// GetModulePath 获取指定模块相对路径
func GetModulePath(name string) string {
return "./" + consts.AddonsDir + "/" + name
}
func Tpl(name, tpl string) string {
return consts.AddonsDir + "/" + name + "/" + tpl
// ViewPath 默认的插件模板路径
func ViewPath(name string) string {
return consts.AddonsDir + "/" + name + "/" + "resource/template"
}
// StaticPath 默认的插件静态路映射关系
// 最终效果:对外访问地址:/addons/插件模块名称;静态资源路径:/addons/插件模块名称/设置的子路径。
// 如果你不喜欢现在的路由风格,可以自行调整
func StaticPath(name, path string) (string, string) {
return "/" + consts.AddonsDir + "/" + name, consts.AddonsDir + "/" + name + "/" + path
}
// RouterPrefix 路由前缀
// 最终效果:/应用名称/插件模块名称/xxx/xxx。如果你不喜欢现在的路由风格,可以自行调整
// 最终效果:/应用名称/插件模块名称/xxx/xxx。
// 如果你不喜欢现在的路由风格,可以自行调整
func RouterPrefix(ctx context.Context, app, name string) string {
var prefix = "/"
if app != "" {

View File

@@ -2,7 +2,7 @@ package addons
import (
"context"
"fmt"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gstr"
"hotgo/internal/consts"
@@ -16,7 +16,6 @@ func Build(ctx context.Context, sk Skeleton, conf *model.BuildAddonConfig) (err
var (
buildPath = "./" + consts.AddonsDir + "/" + sk.Name
modulesPath = "./" + consts.AddonsDir + "/modules/" + sk.Name + ".go"
templatePath = gstr.Replace(conf.TemplatePath, "{$name}", sk.Name)
webApiPath = gstr.Replace(conf.WebApiPath, "{$name}", sk.Name)
webViewsPath = gstr.Replace(conf.WebViewsPath, "{$name}", sk.Name)
replaces = map[string]string{
@@ -31,7 +30,7 @@ func Build(ctx context.Context, sk Skeleton, conf *model.BuildAddonConfig) (err
}
)
if err = checkBuildDir(buildPath, modulesPath, templatePath, webApiPath, webViewsPath); err != nil {
if err = checkBuildDir(buildPath, modulesPath, webApiPath, webViewsPath); err != nil {
return
}
@@ -46,7 +45,7 @@ func Build(ctx context.Context, sk Skeleton, conf *model.BuildAddonConfig) (err
for _, path := range list {
if !gfile.IsReadable(path) {
err = fmt.Errorf("file%v is unreadable, please check permissions", path)
err = gerror.Newf("file%v is unreadable, please check permissions", path)
return
}
@@ -72,11 +71,6 @@ func Build(ctx context.Context, sk Skeleton, conf *model.BuildAddonConfig) (err
return
}
// home默认页面
if err = gfile.PutContents(templatePath+"/home/index.html", gstr.ReplaceByMap(homeLayout, replaces)); err != nil {
return
}
// webApi
if err = gfile.PutContents(webApiPath+"/config/index.ts", gstr.ReplaceByMap(webApiLayout, replaces)); err != nil {
return
@@ -94,15 +88,15 @@ func Build(ctx context.Context, sk Skeleton, conf *model.BuildAddonConfig) (err
return
}
func checkBuildDir(paths ...string) error {
func checkBuildDir(paths ...string) (err error) {
if len(paths) == 0 {
return nil
return
}
for _, path := range paths {
if gfile.Exists(path) {
return fmt.Errorf("插件已存在,请换一个插件名称或者经确认无误后依次删除文件夹: [%v] 后重新生成", strings.Join(paths, "、\t"))
return gerror.Newf("插件已存在,请换一个插件名称或者经确认无误后依次删除文件夹: [%v] 后重新生成", strings.Join(paths, "、\t"))
}
}
return nil
return
}

View File

@@ -11,39 +11,6 @@ package modules
import _ "hotgo/addons/@{.name}"
`
homeLayout = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no">
<meta name="keywords" content="@{.Keywords}"/>
<meta name="description" content="@{.Description}"/>
<title>@{.Title}</title>
<script type="text/javascript" src="/resource/home/js/jquery-3.6.0.min.js"></script>
<style>
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
background-color: #f6f6f6;
}
</style>
</head>
<body>
<div style="padding-top: 100px;text-align:center;">
<h1><p>Hello@{.Data.name}!!</p></h1>
<h2><p>@{.Data.module}</p></h2>
<h2><p>服务器时间:@{.Data.time}</p></h2>
</div>
</body>
<script>
</script>
</html>`
webApiLayout = `import { http } from '@/utils/http/axios';
export function getConfig(params) {

View File

@@ -23,11 +23,12 @@ type InstallRecord struct {
UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"`
}
func GetModel(ctx context.Context) *gdb.Model {
return g.Model("sys_addons_install").Ctx(ctx)
}
func ScanInstall(m Module) (record *InstallRecord, err error) {
err = g.Model("sys_addons_install").
Ctx(m.Ctx()).
Where("name", m.GetSkeleton().Name).
Scan(&record)
err = GetModel(m.Ctx()).Where("name", m.GetSkeleton().Name).Scan(&record)
return
}
@@ -59,23 +60,14 @@ func Install(m Module) (err error) {
"version": m.GetSkeleton().Version,
"status": consts.AddonsInstallStatusOk,
}
return g.DB().Transaction(m.Ctx(), func(ctx context.Context, tx gdb.TX) error {
if record != nil {
_, err = g.Model("sys_addons_install").
Ctx(m.Ctx()).
Where("id", record.Id).
Delete()
_, _ = GetModel(m.Ctx()).Where("id", record.Id).Delete()
}
_, err = g.Model("sys_addons_install").
Ctx(m.Ctx()).
Data(data).
Insert()
if err != nil {
if _, err = GetModel(m.Ctx()).Data(data).Insert(); err != nil {
return err
}
return m.Install(ctx)
})
}
@@ -94,18 +86,10 @@ func Upgrade(m Module) (err error) {
data := g.Map{
"version": m.GetSkeleton().Version,
}
return g.DB().Transaction(m.Ctx(), func(ctx context.Context, tx gdb.TX) error {
_, err = g.Model("sys_addons_install").
Ctx(m.Ctx()).
Where("id", record.Id).
Data(data).
Update()
if err != nil {
if _, err = GetModel(m.Ctx()).Where("id", record.Id).Data(data).Update(); err != nil {
return err
}
return m.Upgrade(ctx)
})
}
@@ -125,18 +109,10 @@ func UnInstall(m Module) (err error) {
"version": m.GetSkeleton().Version,
"status": consts.AddonsInstallStatusUn,
}
return g.DB().Transaction(m.Ctx(), func(ctx context.Context, tx gdb.TX) error {
_, err = g.Model("sys_addons_install").
Ctx(m.Ctx()).
Where("id", record.Id).
Data(data).
Update()
if err != nil {
if _, err = GetModel(m.Ctx()).Where("id", record.Id).Data(data).Update(); err != nil {
return err
}
return m.UnInstall(ctx)
})
}

View File

@@ -7,9 +7,10 @@ package addons
import (
"context"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gfile"
"hotgo/internal/consts"
"github.com/gogf/gf/v2/os/gview"
"hotgo/internal/model/input/form"
"sort"
"sync"
@@ -17,15 +18,16 @@ import (
// Skeleton 模块骨架
type Skeleton struct {
Label string `json:"label"` // 标识
Name string `json:"name"` // 名称
Group int `json:"group"` // 分组
Logo string `json:"logo"` // logo
Brief string `json:"brief"` // 简介
Description string `json:"description"` // 详细描述
Author string `json:"author"` // 作者
Version string `json:"version"` // 版本号
RootPath string `json:"rootPath"` // 根路径
Label string `json:"label"` // 标识
Name string `json:"name"` // 名称
Group int `json:"group"` // 分组
Logo string `json:"logo"` // logo
Brief string `json:"brief"` // 简介
Description string `json:"description"` // 详细描述
Author string `json:"author"` // 作者
Version string `json:"version"` // 版本号
RootPath string `json:"rootPath"` // 根路径
View *gview.View `json:"view"` // 模板引擎
}
func (s *Skeleton) GetModule() Module {
@@ -71,6 +73,14 @@ func RegisterModule(m Module) Module {
if ok {
panic("module repeat registration, name:" + name)
}
sk := m.GetSkeleton()
if sk == nil {
panic("module skeleton not initialized, name:" + name)
}
sk.RootPath = GetModulePath(name)
sk.View = NewView(m.Ctx(), name)
modules[name] = m
return m
}
@@ -110,9 +120,45 @@ func GetModuleRealPath(name string) string {
return path
}
// GetModulePath 获取指定模块相对路径
func GetModulePath(name string) string {
return "./" + consts.AddonsDir + "/" + name
// NewView 初始化一个插件的模板引擎
func NewView(ctx context.Context, name string) *gview.View {
view := gview.New()
if err := view.SetPath(ViewPath(name)); err != nil {
g.Log().Warningf(ctx, "NewView SetPath err:%+v", err)
return nil
}
// 默认和主模块使用一致的变量分隔符号
delimiters := g.Cfg().MustGet(ctx, "viewer.delimiters", []string{"@{", "}"}).Strings()
if len(delimiters) != 2 {
g.Log().Warning(ctx, "NewView delimiters config error")
return nil
}
view.SetDelimiters(delimiters[0], delimiters[1])
//// 更多配置
//view.SetI18n()
//// ...
return view
}
// AddStaticPath 设置插件静态目录映射
func AddStaticPath(ctx context.Context, server *ghttp.Server, p ...string) {
basePath := g.Cfg().MustGet(ctx, "server.serverRoot").String()
if len(p) > 0 {
basePath = p[0]
}
if basePath == "" {
return
}
for _, module := range filterInstalled() {
name := module.GetSkeleton().Name
prefix, path := StaticPath(name, basePath)
server.AddStaticPath(prefix, path)
}
}
// filterInstalled 过滤已安装模块