数据表格热更新

This commit is contained in:
Max 2021-09-28 16:43:58 +08:00
parent 5a8629f43f
commit d3073bee82
5 changed files with 155 additions and 32 deletions

View file

@ -122,4 +122,13 @@ func (cfg *Config) SetDefaults() {
if cfg.RootPlugin == "" {
cfg.RootPlugin = cfg.Root + "/plugins"
}
if cfg.RootTable == "" {
cfg.RootTable = cfg.Root + "/tables"
}
if cfg.RootChart == "" {
cfg.RootChart = cfg.Root + "/charts"
}
if cfg.RootScreen == "" {
cfg.RootScreen = cfg.Root + "/screens"
}
}

View file

@ -21,10 +21,29 @@ type Script struct {
File string
}
// AppRoot 应用目录
type AppRoot struct {
APIs string
Flows string
Models string
Plugins string
Tables string
Charts string
Screens string
}
// Load 根据配置加载 API, FLow, Model, Plugin
func Load(cfg Config) {
LoadEngine(cfg.Path)
LoadApp(cfg.RootAPI, cfg.RootFLow, cfg.RootModel, cfg.RootPlugin)
LoadApp(AppRoot{
APIs: cfg.RootAPI,
Flows: cfg.RootFLow,
Models: cfg.RootModel,
Plugins: cfg.RootPlugin,
Tables: cfg.RootTable,
Charts: cfg.RootChart,
Screens: cfg.RootScreen,
})
}
// Reload 根据配置重新加载 API, FLow, Model, Plugin
@ -56,7 +75,7 @@ func LoadEngine(from string) {
exception.New("读取文件失败, 未找到任何可执行脚本", 500, from).Throw()
}
// 加载 API, Flow, Models, Table
// 加载 API, Flow, Models, Table, Chart, Screens
for _, script := range scripts {
switch script.Type {
case "models":
@ -76,10 +95,11 @@ func LoadEngine(from string) {
}
// LoadApp 加载应用的 API, Flow, Model 和 Plugin
func LoadApp(api string, flow string, model string, plugin string) {
func LoadApp(app AppRoot) {
// api string, flow string, model string, plugin string
// 创建应用目录
paths := []string{api, flow, model, plugin}
paths := []string{app.APIs, app.Flows, app.Models, app.Plugins, app.Charts, app.Screens}
for _, p := range paths {
if !strings.HasPrefix(p, "fs://") && strings.Contains(p, "://") {
continue
@ -98,8 +118,8 @@ func LoadApp(api string, flow string, model string, plugin string) {
}
// 加载API
if strings.HasPrefix(api, "fs://") || !strings.Contains(api, "://") {
root := strings.TrimPrefix(api, "fs://")
if strings.HasPrefix(app.APIs, "fs://") || !strings.Contains(app.APIs, "://") {
root := strings.TrimPrefix(app.APIs, "fs://")
scripts := getAppFilesFS(root, ".json")
for _, script := range scripts {
// 验证API 加载逻辑
@ -108,8 +128,8 @@ func LoadApp(api string, flow string, model string, plugin string) {
}
// 加载Flow
if strings.HasPrefix(flow, "fs://") || !strings.Contains(flow, "://") {
root := strings.TrimPrefix(flow, "fs://")
if strings.HasPrefix(app.Flows, "fs://") || !strings.Contains(app.Flows, "://") {
root := strings.TrimPrefix(app.Flows, "fs://")
scripts := getAppFilesFS(root, ".json")
for _, script := range scripts {
gou.LoadFlow(string(script.Content), script.Name)
@ -117,8 +137,8 @@ func LoadApp(api string, flow string, model string, plugin string) {
}
// 加载Model
if strings.HasPrefix(model, "fs://") || !strings.Contains(model, "://") {
root := strings.TrimPrefix(model, "fs://")
if strings.HasPrefix(app.Models, "fs://") || !strings.Contains(app.Models, "://") {
root := strings.TrimPrefix(app.Models, "fs://")
scripts := getAppFilesFS(root, ".json")
for _, script := range scripts {
gou.LoadModel(string(script.Content), script.Name)
@ -126,13 +146,24 @@ func LoadApp(api string, flow string, model string, plugin string) {
}
// 加载Plugin
if strings.HasPrefix(plugin, "fs://") || !strings.Contains(plugin, "://") {
root := strings.TrimPrefix(plugin, "fs://")
if strings.HasPrefix(app.Plugins, "fs://") || !strings.Contains(app.Plugins, "://") {
root := strings.TrimPrefix(app.Plugins, "fs://")
scripts := getAppPlugins(root, ".so")
for _, script := range scripts {
gou.LoadPlugin(script.File, script.Name)
}
}
// 加载Table
if strings.HasPrefix(app.Tables, "fs://") || !strings.Contains(app.Tables, "://") {
root := strings.TrimPrefix(app.Tables, "fs://")
scripts := getAppFilesFS(root, ".json")
for _, script := range scripts {
// 验证API 加载逻辑
table.Load(string(script.Content), script.Name)
}
}
}
// / getAppPluins 遍历应用目录,读取文件列表

View file

@ -33,6 +33,14 @@ func TestLoadEngineBin(t *testing.T) {
// 从文件系统载入应用脚本
func TestLoadAppFS(t *testing.T) {
assert.NotPanics(t, func() {
LoadApp(Conf.RootAPI, Conf.RootFLow, Conf.RootModel, Conf.RootPlugin)
LoadApp(AppRoot{
APIs: Conf.RootAPI,
Flows: Conf.RootFLow,
Models: Conf.RootModel,
Plugins: Conf.RootPlugin,
Tables: Conf.RootTable,
Charts: Conf.RootChart,
Screens: Conf.RootScreen,
})
})
}

View file

@ -6,6 +6,7 @@ import (
"strings"
"github.com/yaoapp/gou"
"github.com/yaoapp/xiang/table"
)
var shutdown = make(chan bool)
@ -37,7 +38,15 @@ func ServiceStop(onComplete func()) {
// WatchChanges 监听配置文件变更
func WatchChanges() {
watchEngine(Conf.Path)
watchApp(Conf.RootAPI, Conf.RootFLow, Conf.RootModel, Conf.RootPlugin)
watchApp(AppRoot{
APIs: Conf.RootAPI,
Flows: Conf.RootFLow,
Models: Conf.RootModel,
Plugins: Conf.RootPlugin,
Tables: Conf.RootTable,
Charts: Conf.RootChart,
Screens: Conf.RootScreen,
})
}
// watchEngine 监听引擎目录文件变更
@ -129,14 +138,92 @@ func watchEngine(from string) {
})
}
})
// 监听 tables
go Watch(filepath.Join(rootAbs, "tables"), func(op string, file string) {
if !strings.HasSuffix(file, ".json") {
return
}
if op == "write" || op == "create" {
script := getFile(root, file)
table.Load(string(script.Content), "xiang."+script.Name) // Reload
api, has := gou.APIs["xiang.table"]
if has {
api.Reload()
}
log.Printf("数据表格 %s 已重新加载完毕", "xiang."+script.Name)
} else if op == "remove" || op == "rename" {
name := "xiang." + getFileName(root, file)
if _, has := table.Tables[name]; has {
delete(table.Tables, name)
log.Printf("数据表格 %s 已经移除", name)
}
}
// 重启服务器
if op == "write" || op == "create" || op == "remove" || op == "rename" {
ServiceStop(func() {
log.Printf("服务器重启完毕")
go ServiceStart()
})
}
})
}
// watchApp 监听应用目录文件变更
func watchApp(api string, flow string, model string, plugin string) {
watchAppAPI(api)
watchAppFlow(flow)
watchAppModel(model)
watchAppPlugin(plugin)
func watchApp(app AppRoot) {
watchAppAPI(app.APIs)
watchAppFlow(app.Flows)
watchAppModel(app.Models)
watchAppPlugin(app.Plugins)
watchAppTable(app.Tables)
}
// watchAppTable 监听数据表格变更
func watchAppTable(rootTable string) {
if !strings.HasPrefix(rootTable, "fs://") && strings.Contains(rootTable, "://") {
return
}
root := strings.TrimPrefix(rootTable, "fs://")
rootAbs, err := filepath.Abs(root)
if err != nil {
log.Panicf("路径错误 %s %s", root, err)
}
go Watch(rootAbs, func(op string, file string) {
if !strings.HasSuffix(file, ".json") {
return
}
if op == "write" || op == "create" {
script := getAppFile(root, file)
table.Load(string(script.Content), script.Name) // Reload
api, has := gou.APIs["xiang.table"]
if has {
api.Reload()
}
log.Printf("数据表格 %s 已重新加载完毕", script.Name)
} else if op == "remove" || op == "rename" {
name := getAppFileName(root, file)
if _, has := gou.APIs[name]; has {
delete(table.Tables, name)
log.Printf("数据表格 %s 已经移除", name)
}
}
// 重启服务器
if op == "write" || op == "create" || op == "remove" || op == "rename" {
ServiceStop(func() {
log.Printf("服务器重启完毕")
go ServiceStart()
})
}
})
}
// watchAppAPI 监听API变更

View file

@ -3,19 +3,7 @@
"version": "1.0.0",
"decription": "象传用户",
"bind": {
"model": "xiang.user",
"withs": {
"manu": {
"query": {
"select": ["id", "name", "short_name", "status"]
}
},
"kind": {
"query": {
"select": ["id", "name"]
}
}
}
"model": "xiang.user"
},
"apis": {},
"columns": {},