From afd781cd8683ab75f3b3476e146c8595f6943a65 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 19 Oct 2021 10:48:02 +0800 Subject: [PATCH] =?UTF-8?q?+=20=E6=95=B0=E6=8D=AE=E5=9B=BE=E8=A1=A8?= =?UTF-8?q?=E8=BD=BD=E5=85=A5=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chart/REAME.md | 1 + chart/chart.go | 49 +++++++++++++++++++++++++++++++ chart/chart_test.go | 29 ++++++++++++++++++ chart/types.go | 21 +++++++++++++ query/README.md | 1 + query/query.go | 29 ++++++++++++++++++ tests/charts/service/compare.json | 30 ++++++++++--------- 7 files changed, 146 insertions(+), 14 deletions(-) create mode 100644 chart/REAME.md create mode 100644 chart/chart.go create mode 100644 chart/chart_test.go create mode 100644 chart/types.go create mode 100644 query/README.md create mode 100644 query/query.go diff --git a/chart/REAME.md b/chart/REAME.md new file mode 100644 index 00000000..3f12113c --- /dev/null +++ b/chart/REAME.md @@ -0,0 +1 @@ +# 数据图表 diff --git a/chart/chart.go b/chart/chart.go new file mode 100644 index 00000000..0e180941 --- /dev/null +++ b/chart/chart.go @@ -0,0 +1,49 @@ +package chart + +import ( + jsoniter "github.com/json-iterator/go" + "github.com/yaoapp/kun/exception" + "github.com/yaoapp/xiang/config" + "github.com/yaoapp/xiang/share" + "github.com/yaoapp/xiang/xlog" +) + +// Charts 已载入图表 +var Charts = map[string]*Chart{} + +// Load 加载数据表格 +func Load(cfg config.Config) { + LoadFrom(cfg.RootChart, "") +} + +// LoadFrom 从特定目录加载 +func LoadFrom(dir string, prefix string) { + + if share.DirNotExists(dir) { + return + } + + share.Walk(dir, ".json", func(root, filename string) { + name := share.SpecName(root, filename) + content := share.ReadFile(filename) + chart, err := LoadChart(content, name) + if err != nil { + exception.New("%s 图表格式错误", 400, name).Ctx(filename).Throw() + } + Charts[name] = chart + }) +} + +// LoadChart 载入数据表格 +func LoadChart(source []byte, name string) (*Chart, error) { + chart := Chart{} + err := jsoniter.Unmarshal(source, &chart) + if err != nil { + xlog.Println(name) + xlog.Println(err.Error()) + xlog.Println(string(source)) + return nil, err + } + chart.Prepare() + return &chart, nil +} diff --git a/chart/chart_test.go b/chart/chart_test.go new file mode 100644 index 00000000..e1344720 --- /dev/null +++ b/chart/chart_test.go @@ -0,0 +1,29 @@ +package chart + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/yaoapp/xiang/config" + "github.com/yaoapp/xiang/model" + "github.com/yaoapp/xiang/query" + "github.com/yaoapp/xiang/share" +) + +func TestLoad(t *testing.T) { + share.DBConnect(config.Conf.Database) + model.Load(config.Conf) + query.Load(config.Conf) + + Load(config.Conf) + LoadFrom("not a path", "404.") + check(t) +} + +func check(t *testing.T) { + keys := []string{} + for key := range Charts { + keys = append(keys, key) + } + assert.Equal(t, 1, len(keys)) +} diff --git a/chart/types.go b/chart/types.go new file mode 100644 index 00000000..6fd8e4aa --- /dev/null +++ b/chart/types.go @@ -0,0 +1,21 @@ +package chart + +import ( + "github.com/yaoapp/gou" + "github.com/yaoapp/xiang/share" +) + +// Chart 图表格式 +type Chart struct { + gou.Flow + APIs map[string]API `json:"apis,omitempty"` + Filters map[string]share.Filter `json:"filters,omitempty"` + Page share.Page `json:"page,omitempty"` +} + +// API 图表 API +type API struct { + Disable bool `json:"disable,omitempty"` + Guard string `json:"guard,omitempty"` + Default interface{} `json:"default,omitempty"` +} diff --git a/query/README.md b/query/README.md new file mode 100644 index 00000000..4a97944e --- /dev/null +++ b/query/README.md @@ -0,0 +1 @@ +# 数据分析查询引擎 diff --git a/query/query.go b/query/query.go new file mode 100644 index 00000000..8c19ba6c --- /dev/null +++ b/query/query.go @@ -0,0 +1,29 @@ +package query + +import ( + "github.com/yaoapp/gou" + dsl "github.com/yaoapp/gou/query/gou" + "github.com/yaoapp/kun/exception" + "github.com/yaoapp/xiang/config" + "github.com/yaoapp/xun/capsule" +) + +// Load 加载查询引擎 +func Load(cfg config.Config) { + XiangQuery() +} + +// XiangQuery 注册应用引擎象 xiang +func XiangQuery() { + gou.RegisterEngine("xiang", &dsl.Query{ + Query: capsule.Query(), + GetTableName: func(s string) string { + if mod, has := gou.Models[s]; has { + return mod.MetaData.Table.Name + } + exception.New("%s 数据模型尚未加载", 404).Throw() + return s + }, + AESKey: config.Conf.Database.AESKey, + }) +} diff --git a/tests/charts/service/compare.json b/tests/charts/service/compare.json index bb9392ed..d88956af 100644 --- a/tests/charts/service/compare.json +++ b/tests/charts/service/compare.json @@ -1,35 +1,37 @@ { "engine": "gou", - "name": "指标对比", + "label": "指标对比", "version": "1.0.0", "decription": "指标对比", "nodes": [ { "name": "行业分布", - "process": "models.service.get", + "engine": "xiang", "query": { - "select": ["city", ":COUNT(id) as cnt", "@industries.$ as industry"], + "select": ["city", ":COUNT(id) as cnt", "industries[*] as industry"], + "from": "$service", "wheres": [ - { "column": "created_at", "value": "{{$query.from}}", "op": "ge" }, - { "column": "created_at", "value": "{{$query.to}}", "op": "le" } + { "field": "created_at", ">": "?:$in.from" }, + { "field": "created_at", "<": "?:$in.to" } ], - "order": ["cnt.desc"], + "order": "cnt desc", "limit": 100, - "group": ["@industries.$", "city"] + "group": ["industries[*]", "city"] } }, { "name": "计费方式", - "process": "models.service.get", + "engine": "xiang", "query": { - "select": ["city", ":COUNT(id) as cnt", "@price_options.$ as option"], + "select": ["city", ":COUNT(id) as cnt", "price_options[*] as option"], + "from": "$service", "wheres": [ - { "column": "created_at", "value": "{{$query.from}}", "op": "ge" }, - { "column": "created_at", "value": "{{$query.to}}", "op": "le" } + { "field": "created_at", ">": "?:$in.from" }, + { "field": "created_at", "<": "?:$in.to" } ], - "order": ["cnt.desc"], + "order": "cnt desc", "limit": 100, - "group": ["@price_options.$", "city"] + "group": ["price_options[*]", "city"] } }, { @@ -44,7 +46,7 @@ ] } ], - "data": "{{$res.合并结果}}", + "output": "{{$res.合并结果}}", "filters": {}, "page": { "primary": "城市",