+ Service

This commit is contained in:
Max 2021-09-28 00:34:38 +08:00
parent 0eb9e533e2
commit 20a3e89cb7
10 changed files with 1167 additions and 93 deletions

589
app/models/kind.json Normal file
View file

@ -0,0 +1,589 @@
{
"name": "服务类型",
"table": {
"name": "kind",
"comment": "服务类型",
"engine": "InnoDB"
},
"columns": [
{ "name": "id", "type": "ID" },
{
"name": "category_id",
"type": "integer",
"length": 128,
"comment": "gw_category ID(废弃)",
"nullable": true,
"index": true
},
{
"name": "type_id",
"type": "integer",
"length": 128,
"comment": "gw_type ID(废弃)",
"nullable": true,
"index": true
},
{
"name": "name",
"type": "string",
"comment": "名称",
"index": true
},
{
"name": "parent",
"type": "bigInteger",
"length": 128,
"comment": "父类型ID",
"nullable": false,
"default": 0,
"index": true
},
{
"name": "status",
"type": "enum",
"option": ["enabled", "disabled"],
"comment": "状态 enabled 开启, disabled 关闭",
"default": "enabled",
"index": true
},
{
"name": "rank",
"type": "bigInteger",
"length": 128,
"comment": "排序",
"nullable": false,
"default": 9999,
"index": true
}
],
"relations": {
"parent": {
"type": "hasOne",
"key": "parent",
"model": "Kind",
"foreign": "id"
},
"children": {
"type": "hasMany",
"key": "id",
"model": "Kind",
"foreign": "parent"
},
"nodes": {
"type": "treeNodes",
"key": "id",
"model": "Kind",
"parent": "parent",
"max_level": 256
}
},
"option": { "timestamps": true, "soft_deletes": true },
"values": [
{
"id": 1,
"name": "云服务",
"category_id": null,
"type_id": null,
"rank": 1,
"parent": 0
},
{
"id": 2,
"name": "基础服务",
"category_id": null,
"type_id": null,
"rank": 1,
"parent": 1
},
{
"id": 3,
"name": "云主机",
"category_id": null,
"type_id": 4,
"rank": 1,
"parent": 2
},
{
"id": 4,
"name": "对象存储",
"category_id": null,
"type_id": 5,
"rank": 2,
"parent": 2
},
{
"id": 5,
"name": "云数据库",
"category_id": null,
"type_id": 6,
"rank": 3,
"parent": 2
},
{
"id": 6,
"name": "块存储",
"category_id": null,
"type_id": 7,
"rank": 4,
"parent": 2
},
{
"id": 7,
"name": "应用托管容器",
"category_id": null,
"type_id": 8,
"rank": 5,
"parent": 2
},
{
"id": 8,
"name": "云缓存",
"category_id": null,
"type_id": 9,
"rank": 6,
"parent": 2
},
{
"id": 9,
"name": "本地负载均衡",
"category_id": null,
"type_id": 10,
"rank": 7,
"parent": 2
},
{
"id": 10,
"name": "全局负载均衡",
"category_id": null,
"type_id": 13,
"rank": 8,
"parent": 2
},
{
"id": 11,
"name": "云分发",
"category_id": null,
"type_id": 11,
"rank": 9,
"parent": 2
},
{
"id": 12,
"name": "企业级SaaS",
"category_id": null,
"type_id": 12,
"rank": 10,
"parent": 2
},
{
"id": 13,
"name": "云桌面",
"category_id": null,
"type_id": 14,
"rank": 11,
"parent": 2
},
{
"id": 14,
"name": "云备份",
"category_id": null,
"type_id": 17,
"rank": 12,
"parent": 2
},
{
"id": 15,
"name": "GPU云主机",
"category_id": null,
"type_id": 18,
"rank": 13,
"parent": 2
},
{
"id": 16,
"name": "物理云主机",
"category_id": null,
"type_id": 20,
"rank": 14,
"parent": 2
},
{
"id": 17,
"name": "智能云",
"category_id": 41,
"type_id": null,
"rank": 2,
"parent": 2
},
{
"id": 18,
"name": "软件和开发",
"category_id": null,
"type_id": null,
"rank": 2,
"parent": 0
},
{
"id": 19,
"name": "虚拟化及管理",
"category_id": 59,
"type_id": null,
"rank": 1,
"parent": 18
},
{
"id": 20,
"name": "容器解决方案",
"category_id": 65,
"type_id": null,
"rank": 2,
"parent": 18
},
{
"id": 21,
"name": "微服务解决方案",
"category_id": 76,
"type_id": null,
"rank": 3,
"parent": 18
},
{
"id": 22,
"name": "serverless解决方案",
"category_id": 82,
"type_id": null,
"rank": 4,
"parent": 18
},
{
"id": 23,
"name": "云管理和云运营",
"category_id": null,
"type_id": null,
"rank": 3,
"parent": 0
},
{
"id": 24,
"name": "混合云",
"category_id": null,
"type_id": null,
"rank": 1,
"parent": 23
},
{
"id": 25,
"name": "混合云解决方案",
"category_id": 88,
"type_id": null,
"rank": 1,
"parent": 24
},
{
"id": 26,
"name": "混合云安全",
"category_id": 671,
"type_id": null,
"rank": 2,
"parent": 24
},
{
"id": 27,
"name": "多云管理",
"category_id": 94,
"type_id": null,
"rank": 2,
"parent": 23
},
{
"id": 28,
"name": "金牌运维",
"category_id": 120,
"type_id": null,
"rank": 3,
"parent": 23
},
{
"id": 29,
"name": "研发运营一体化",
"category_id": 443,
"type_id": null,
"rank": 4,
"parent": 23
},
{
"id": 30,
"name": "MSP",
"category_id": 112,
"type_id": null,
"rank": 5,
"parent": 23
},
{
"id": 31,
"name": "安全与保险",
"category_id": null,
"type_id": null,
"rank": 4,
"parent": 0
},
{
"id": 32,
"name": "风险管理",
"category_id": 141,
"type_id": null,
"rank": 1,
"parent": 31
},
{
"id": 33,
"name": "云服务用户数据保护",
"category_id": 152,
"type_id": null,
"rank": 2,
"parent": 31
},
{
"id": 34,
"name": "业务风控",
"category_id": null,
"type_id": null,
"rank": 3,
"parent": 31
},
{
"id": 35,
"name": "内容安全",
"category_id": 159,
"type_id": null,
"rank": 1,
"parent": 34
},
{
"id": 36,
"name": "反交易欺诈",
"category_id": 164,
"type_id": null,
"rank": 2,
"parent": 34
},
{
"id": 37,
"name": "反信贷欺诈",
"category_id": 165,
"type_id": null,
"rank": 3,
"parent": 34
},
{
"id": 38,
"name": "反营销欺诈",
"category_id": 166,
"type_id": null,
"rank": 4,
"parent": 34
},
{
"id": 39,
"name": "反钓鱼欺诈",
"category_id": 167,
"type_id": null,
"rank": 5,
"parent": 34
},
{
"id": 40,
"name": "云主机安全",
"category_id": 184,
"type_id": null,
"rank": 4,
"parent": 31
},
{
"id": 41,
"name": "态势感知",
"category_id": 190,
"type_id": null,
"rank": 5,
"parent": 31
},
{
"id": 42,
"name": "云保险",
"category_id": 272,
"type_id": null,
"rank": 6,
"parent": 31
},
{
"id": 43,
"name": "云网&云边",
"category_id": null,
"type_id": null,
"rank": 5,
"parent": 0
},
{
"id": 44,
"name": "云平台网络能力",
"category_id": 102,
"type_id": null,
"rank": 1,
"parent": 43
},
{
"id": 45,
"name": "SD-WAN",
"category_id": 106,
"type_id": null,
"rank": 2,
"parent": 43
},
{
"id": 46,
"name": "物联网",
"category_id": 234,
"type_id": null,
"rank": 3,
"parent": 43
},
{
"id": 47,
"name": "行业云",
"category_id": null,
"type_id": null,
"rank": 6,
"parent": 0
},
{
"id": 48,
"name": "政务",
"category_id": null,
"type_id": null,
"rank": 1,
"parent": 47
},
{
"id": 49,
"name": "政务云综合水平评估",
"category_id": 215,
"type_id": null,
"rank": 1,
"parent": 48
},
{
"id": 50,
"name": "可信政务云评估",
"category_id": 216,
"type_id": null,
"rank": 2,
"parent": 48
},
{
"id": 51,
"name": "金融",
"category_id": 225,
"type_id": null,
"rank": 2,
"parent": 47
},
{
"id": 52,
"name": "开源治理",
"category_id": null,
"type_id": null,
"rank": 7,
"parent": 0
},
{
"id": 53,
"name": "面向开源用户企业",
"category_id": 196,
"type_id": null,
"rank": 1,
"parent": 52
},
{
"id": 54,
"name": "面向自发开源企业",
"category_id": 202,
"type_id": null,
"rank": 2,
"parent": 52
},
{
"id": 55,
"name": "开源项目评估",
"category_id": 711,
"type_id": null,
"rank": 3,
"parent": 52
},
{
"id": 56,
"name": "开源工具评估",
"category_id": 720,
"type_id": null,
"rank": 4,
"parent": 52
},
{
"id": 57,
"name": "检测平台",
"category_id": null,
"type_id": null,
"rank": 8,
"parent": 0
},
{
"id": 58,
"name": "云主机分级",
"category_id": 371,
"type_id": null,
"rank": 1,
"parent": 57
},
{
"id": 59,
"name": "监管支撑",
"category_id": null,
"type_id": null,
"rank": 9,
"parent": 0
},
{
"id": 60,
"name": "企业上云效果成熟度",
"category_id": 250,
"type_id": null,
"rank": 1,
"parent": 59
},
{
"id": 61,
"name": "综合信用评估",
"category_id": null,
"type_id": null,
"rank": 2,
"parent": 59
},
{
"id": 62,
"name": "云服务企业",
"category_id": 261,
"type_id": null,
"rank": 1,
"parent": 61
},
{
"id": 63,
"name": "CDN服务企业",
"category_id": 271,
"type_id": null,
"rank": 2,
"parent": 61
}
]
}

122
app/models/service.json Normal file
View file

@ -0,0 +1,122 @@
{
"name": "云服务库",
"table": {
"name": "service",
"comment": "云服务表",
"engine": "InnoDB"
},
"columns": [
{ "name": "id", "type": "ID" },
{
"name": "short_name",
"type": "string",
"length": 200,
"comment": "简称",
"index": true
},
{
"name": "name",
"type": "string",
"length": 200,
"comment": "服务名称",
"index": true
},
{
"name": "kind",
"type": "string",
"length": 200,
"comment": "服务类型",
"index": true
},
{
"name": "manu",
"type": "string",
"length": 200,
"comment": "所属厂商",
"index": true
},
{
"name": "registed_at",
"type": "string",
"length": 200,
"comment": "注册地",
"nullable": true,
"index": true
},
{
"name": "link",
"type": "string",
"length": 200,
"comment": "官网地址",
"nullable": true
},
{
"name": "contact_name",
"type": "string",
"length": 200,
"comment": "联系人",
"nullable": true,
"index": true
},
{
"name": "contact_mobile",
"type": "string",
"comment": "联系电话",
"nullable": true,
"index": true
},
{
"name": "fields",
"type": "string",
"length": 500,
"comment": "JSON|服务领域(多选)",
"nullable": true,
"index": true
},
{
"name": "price_options",
"type": "string",
"length": 500,
"default": "[\"按月订阅\"]",
"comment": "JSON|订阅方式(多选)",
"index": true
},
{
"name": "industries",
"type": "string",
"length": 500,
"comment": "JSON|行业覆盖(多选)",
"nullable": true,
"index": true
},
{
"name": "rank",
"type": "integer",
"default": 9999999,
"comment": "查询排序"
},
{
"name": "status",
"type": "enum",
"default": "enabled",
"option": ["enabled", "disabled"],
"comment": "状态 enabled 开启, disabled 关闭",
"index": true
}
],
"relations": {
"manu": {
"type": "hasOne",
"key": "manu",
"model": "Manu",
"foreign": "id"
},
"kind": {
"type": "hasOne",
"key": "kind",
"model": "Kind",
"foreign": "id"
}
},
"option": { "timestamps": true, "soft_deletes": true }
}

View file

@ -1,86 +1,64 @@
{
"name": "云服务库",
"version": "1.0.0",
"decription": "可信云云服务库",
"apis": {
"bind": {
"model": "service",
"withs": {
"manu": {
"query": {
"select": ["id", "name", "short_name", "status"]
}
},
"kind": {
"query": {
"select": ["id", "name"]
}
"bind": {
"model": "service",
"withs": {
"manu": {
"query": {
"select": ["id", "name", "short_name", "status"]
}
},
"kind": {
"query": {
"select": ["id", "name"]
}
}
},
}
},
"apis": {
"search": {
"process": "models.service.Paginate",
"in": [":query-param", "$query.page", "$query.pagesize"],
"default": {
"withs": {
"manu": {
"query": {
"select": ["id", "name", "short_name", "status"]
"default": [
{
"withs": {
"manu": {
"query": {
"select": ["id", "name", "short_name", "status"]
}
},
"kind": {
"query": {
"select": ["id", "name"]
}
}
},
"kind": {
"query": {
"select": ["id", "name"]
}
}
"wheres": [{ "column": "status", "value": "enabled" }]
},
"wheres": [{ "column": "status", "value": "enabled" }],
"pagesize": 15
}
null,
15
]
},
"find": {
"process": "models.service.Find",
"in": ["$param.id"],
"default": {
"withs": {
"manu": {
"query": {
"select": ["id", "name", "short_name", "status"]
}
},
"kind": {
"query": {
"select": ["id", "name"]
"default": [
null,
{
"withs": {
"manu": {
"query": {
"select": ["id", "name", "short_name", "status"]
}
},
"kind": {
"query": {
"select": ["id", "name"]
}
}
}
}
}
},
"save": {
"process": "models.service.Find",
"in": [":payload"],
"default": {
"fields": ["视频会议"]
}
},
"delete": {
"process": "models.service.Delete",
"in": ["$param.id"]
},
"insert": {
"process": "models.service.Insert",
"in": ["$payload.columns", "$payload.data"]
},
"deletewhere": {
"process": "models.service.DeleteWhere",
"in": [":query-param"]
},
"updatewhere": {
"process": "models.service.UpdateWhere",
"in": [":query-param", ":payload"]
},
"import": {
"process": "models.service.Import",
"in": ["$file.file", "$param.kind", ":headers"]
]
}
},
"columns": {
@ -295,24 +273,28 @@
"关键词": {
"label": "关键词",
"bind": "where.name.like",
"type": "input",
"props": {
"placeholder": "请输入关键词"
"input": {
"type": "input",
"props": {
"placeholder": "请输入关键词"
}
}
},
"所属厂商": {
"label": "厂商",
"bind": "where.manu_id.in",
"type": "select",
"props": {
"searchable": true,
"multiple": true,
"placeholder": "请选择所属厂商",
"remote": {
"api": "/api/manu/get",
"query": {
"select": ["id", "name"],
"keyword": "where_name_like"
"input": {
"type": "select",
"props": {
"searchable": true,
"multiple": true,
"placeholder": "请选择所属厂商",
"remote": {
"api": "/api/manu/get",
"query": {
"select": ["id", "name"],
"keyword": "where_name_like"
}
}
}
}
@ -320,17 +302,19 @@
"服务类型": {
"label": "类型",
"bind": "where.kind_id.in",
"type": "select",
"props": {
"tree": true,
"searchable": true,
"multiple": true,
"placeholder": "请选择所属类型",
"remote": {
"api": "/api/kind/tree",
"query": {
"select": ["id", "name"],
"keyword": "where_name_like"
"input": {
"type": "select",
"props": {
"tree": true,
"searchable": true,
"multiple": true,
"placeholder": "请选择所属类型",
"remote": {
"api": "/api/kind/tree",
"query": {
"select": ["id", "name"],
"keyword": "where_name_like"
}
}
}
}
@ -373,7 +357,7 @@
"deleteWhere": {},
"updateSelect": {},
"deleteSelect": {},
"pagination": ["pages", "prev", "next"],
"pagination": {},
"setting": {}
}
},

View file

@ -46,7 +46,9 @@ func init() {
Conf = NewConfig()
// 数据库连接
capsule.AddConn("primary", "mysql", Conf.Database.Primary[0]).SetAsGlobal()
if len(Conf.Database.Primary) > 0 {
capsule.AddConn("primary", "mysql", Conf.Database.Primary[0]).SetAsGlobal()
}
// 加密密钥
gou.LoadCrypt(fmt.Sprintf(`{"key":"%s"}`, Conf.Database.AESKey), "AES")

5
table/api.go Normal file
View file

@ -0,0 +1,5 @@
package table
// loadAPIs 加载数据管理 API
func (tab *Table) loadAPIs() {
}

113
table/columns.go Normal file
View file

@ -0,0 +1,113 @@
package table
import (
"fmt"
"github.com/yaoapp/gou"
)
var elms = map[string]Column{
"string": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"char": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"text": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"mediumText": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"longText": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"binary": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"date": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"datetime": {View: Render{Type: "label"}, Edit: Render{Type: "datetime"}},
"datetimeTz": {View: Render{Type: "label"}, Edit: Render{Type: "datetime"}},
"time": {View: Render{Type: "label"}, Edit: Render{Type: "time"}},
"timeTz": {View: Render{Type: "label"}, Edit: Render{Type: "time"}},
"timestamp": {View: Render{Type: "label"}, Edit: Render{Type: "datetime"}},
"timestampTz": {View: Render{Type: "label"}, Edit: Render{Type: "datetime"}},
"tinyInteger": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"tinyIncrements": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"unsignedTinyInteger": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"smallInteger": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"smallIncrements": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"unsignedSmallInteger": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"integer": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"increments": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"unsignedInteger": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"bigInteger": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"bigIncrements": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"unsignedBigInteger": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"id": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"ID": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"decimal": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"unsignedDecimal": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"float": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"unsignedFloat": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"double": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"unsignedDouble": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"boolean": {View: Render{Type: "label"}, Edit: Render{Type: "checkbox"}},
"enum": {View: Render{Type: "label"}, Edit: Render{Type: "select"}},
"json": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"JSON": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"jsonb": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"JSONB": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"uuid": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"ipAddress": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"macAddress": {View: Render{Type: "label"}, Edit: Render{Type: "input"}},
"year": {View: Render{Type: "label"}, Edit: Render{Type: "datetime"}},
}
// loadColumns 加载字段呈现方式
func (table *Table) loadColumns() {
if table.Bind.Model == "" {
return
}
defaults := getDefaultColumns(table.Bind.Model)
for name, column := range table.Columns {
defaults[name] = column
}
table.Columns = defaults
}
// getDefaultColumns 读取数据模型字段的呈现方式
func getDefaultColumns(name string) map[string]Column {
mod := gou.Select(name)
cmap := mod.Columns
columns := map[string]Column{}
for name, col := range cmap {
vcol, has := elms[col.Type]
if !has {
continue
}
label := col.Label
if label == "" {
label = col.Comment
}
if label == "" {
label = name
}
vcol.Label = label
if vcol.View.Props == nil {
vcol.View.Props = map[string]interface{}{}
}
if vcol.Edit.Props == nil {
vcol.Edit.Props = map[string]interface{}{}
}
vcol.View.Props["value"] = fmt.Sprintf(":%s", col.Name)
vcol.Edit.Props["value"] = fmt.Sprintf(":%s", col.Name)
// 枚举型
if col.Type == "enum" {
options := []map[string]string{}
for _, opt := range col.Option {
options = append(options, map[string]string{
"label": opt,
"value": opt,
})
}
vcol.Edit.Props["options"] = options
}
columns[name] = vcol
columns[label] = vcol
}
return columns
}

77
table/filters.go Normal file
View file

@ -0,0 +1,77 @@
package table
import (
"fmt"
"github.com/yaoapp/gou"
)
// loadFilters 加载查询过滤器
func (table *Table) loadFilters() {
if table.Bind.Model == "" {
return
}
defaults := getDefaultFilters(table.Bind.Model)
for name, filter := range table.Filters {
defaults[name] = filter
}
table.Filters = defaults
}
// getDefaultFilters 读取数据模型索引字段的过滤器
func getDefaultFilters(name string) map[string]Filter {
mod := gou.Select(name)
cmap := mod.Columns
filters := map[string]Filter{}
for _, index := range mod.MetaData.Indexes {
for _, col := range index.Columns {
if _, has := cmap[col]; !has {
continue
}
// primary,unique,index,match
switch index.Type {
case "index", "match":
cmap[col].Index = true
break
case "unique":
cmap[col].Unique = true
break
case "primary":
cmap[col].Primary = true
break
}
}
}
for name, col := range cmap {
if col.Type != "ID" && !col.Index && !col.Unique && !col.Primary {
continue
}
vcol, has := elms[col.Type]
if !has {
continue
}
label := col.Label
if label == "" {
label = col.Comment
}
if label == "" {
label = name
}
filter := Filter{
Label: label,
Bind: fmt.Sprintf("where.%s.eq", name),
Input: vcol.Edit,
}
filters[name] = filter
filters[label] = filter
}
return filters
}

64
table/table.go Normal file
View file

@ -0,0 +1,64 @@
package table
import (
"fmt"
"io"
"os"
"strings"
"github.com/yaoapp/gou/helper"
"github.com/yaoapp/kun/exception"
)
// Tables 已载入模型
var Tables = map[string]*Table{}
// Load 载入数据表格
func Load(source string, name string) *Table {
var input io.Reader = nil
if strings.HasPrefix(source, "file://") || strings.HasPrefix(source, "fs://") {
filename := strings.TrimPrefix(source, "file://")
filename = strings.TrimPrefix(filename, "fs://")
file, err := os.Open(filename)
if err != nil {
exception.Err(err, 400).Throw()
}
defer file.Close()
input = file
} else {
input = strings.NewReader(source)
}
table := Table{
Source: source,
Table: name,
}
err := helper.UnmarshalFile(input, &table)
if err != nil {
exception.Err(err, 400).Throw()
}
table.loadColumns()
table.loadFilters()
table.loadAPIs()
Tables[name] = &table
return Tables[name]
}
// Select 读取已加载表格配置
func Select(name string) *Table {
tab, has := Tables[name]
if !has {
exception.New(
fmt.Sprintf("Table:%s; 尚未加载", name),
400,
).Throw()
}
return tab
}
// Reload 更新数据表格配置
func (tab *Table) Reload() *Table {
tab = Load(tab.Source, tab.Name)
return tab
}

38
table/table_test.go Normal file
View file

@ -0,0 +1,38 @@
package table
import (
"path"
"testing"
"github.com/stretchr/testify/assert"
"github.com/yaoapp/xiang/global"
)
func TestLoad(t *testing.T) {
global.Load(global.Conf)
root := "fs://" + path.Join(global.Conf.Source, "/app/tables/service.json")
Load(root, "service").Reload()
table, has := Tables["service"]
assert.Equal(t, table.Table, "service")
assert.Equal(t, table.Source, root)
assert.True(t, has)
_, has = table.Columns["id"]
assert.True(t, has)
price, has := table.Columns["计费方式"]
assert.True(t, has)
if has {
assert.True(t, price.Edit.Props["multiple"].(bool))
}
_, has = table.Filters["id"]
assert.True(t, has)
keywords, has := table.Filters["关键词"]
assert.True(t, has)
if has {
assert.True(t, keywords.Bind == "where.name.like")
}
}

80
table/types.go Normal file
View file

@ -0,0 +1,80 @@
package table
import "github.com/yaoapp/gou"
// Table 数据表格配置结构
type Table struct {
Table string `json:"-"`
Source string `json:"-"`
Name string `json:"name"`
Version string `json:"version"`
Bind Bind `json:"bind,omitempty"`
APIs APIs `json:"apis,omitempty"`
Columns map[string]Column `json:"columns,omitempty"`
Filters map[string]Filter `json:"filters,omitempty"`
List Page `json:"list,omitempty"`
Edit Page `json:"edit,omitempty"`
View Page `json:"view,omitempty"`
Insert Page `json:"insert,omitempty"`
}
// Bind 绑定数据模型
type Bind struct {
Model string `json:"model"`
Withs map[string]gou.With `json:"withs,omitempty"`
}
// APIs API 配置数据结构
type APIs struct {
Search API `json:"search,omitempty"`
Find API `json:"find,omitempty"`
Save API `json:"save,omitempty"`
Delete API `json:"delete,omitempty"`
Insert API `json:"insert,omitempty"`
DeleteWhere API `json:"delete-where,omitempty"`
DeleteIn API `json:"delete-in,omitempty"`
UpdateWhere API `json:"update-where,omitempty"`
UpdateIn API `json:"update-in,omitempty"`
ImportUpload API `json:"import-upload,omitempty"`
ImportPreview API `json:"import-preview,omitempty"`
ImportSync API `json:"import-sync,omitempty"`
ImportAsync API `json:"import-async,omitempty"`
ImportAsyncTasks API `json:"import-async-tasks,omitempty"`
ImportAsyncTasksID API `json:"import-async-tasks-id,omitempty"`
Setting API `json:"setting,omitempty"`
}
// API API 配置数据结构
type API struct {
Process string `json:"process,omitempty"`
Guard string `json:"guard,omitempty"`
Default []interface{} `json:"default,omitempty"`
}
// Column 字段呈现方式
type Column struct {
Label string `json:"label"`
View Render `json:"view,omitempty"`
Edit Render `json:"edit,omitempty"`
Form Render `json:"form,omitempty"`
}
// Filter 查询过滤器
type Filter struct {
Label string `json:"label"`
Bind string `json:"bind,omitempty"`
Input Render `json:"input,omitempty"`
}
// Page 页面
type Page struct {
Primary string `json:"primary"`
Layout map[string]interface{} `json:"layout"`
Actions map[string]Render `json:"actions,omitempty"`
}
// Render 组件渲染方式
type Render struct {
Type string `json:"type,omitempty"`
Props map[string]interface{} `json:"props,omitempty"`
}