add rule and deparment user_id

This commit is contained in:
sunjuzhong 2025-02-17 17:09:29 +08:00
parent c5eade1523
commit bcd8545930
33 changed files with 574 additions and 39 deletions

55
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,55 @@
{
// 使 IntelliSense
//
// 访: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Execute Designated process",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/main.go",
"cwd": "/Users/juzhongsun/Works/yao-project",
"args": [
"run",
"yao.app.menu",
],
},
{
"name": "Launch Yao Project",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/main.go",
"cwd": "/Users/juzhongsun/Works/yao-project",
"args": [
"start"
],
},
{
"name": "Launch Fabric Project",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/main.go",
"cwd": "/Users/juzhongsun/Works/yao-fabric",
"args": [
"start"
],
},
{
"name": "Launch Fabric Process",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/main.go",
"cwd": "/Users/juzhongsun/Works/yao-fabric",
"args": [
"run",
"models.crm.person.Get",
"::{\"withs\": {\"company\": { \"query\": { \"select\": [\"name\"] }}}, \"wheres\": [{\"rel\": \"company\", \"column\":\"name\", \"value\": \"绍兴市金桥纺织有限公司\"}]}"
],
},
]
}

View file

@ -283,7 +283,7 @@ release: clean
# ** XGEN will be renamed to DUI in the feature. and move to the new repository. **
# ** new repository: https://github.com/YaoApp/dui.git **
export NODE_ENV=production
git clone https://github.com/YaoApp/xgen.git .tmp/xgen/v1.0
git clone https://github.com/sjzsdu/xgen.git .tmp/xgen/v1.0
# cd .tmp/xgen/v1.0 && git checkout 5002c3fded585aaa69a4366135b415ea3234964e
echo "BASE=__yao_admin_root" > .tmp/xgen/v1.0/packages/xgen/.env
cd .tmp/xgen/v1.0 && pnpm install --no-frozen-lockfile && pnpm run build
@ -347,7 +347,7 @@ linux-release: clean
# ** XGEN will be renamed to DUI in the feature. and move to the new repository. **
# ** new repository: https://github.com/YaoApp/dui.git **
export NODE_ENV=production
git clone https://github.com/YaoApp/xgen.git .tmp/xgen/v1.0
git clone https://github.com/sjzsdu/xgen.git .tmp/xgen/v1.0
rm -f .tmp/xgen/v1.0/pnpm-lock.yaml
echo "BASE=__yao_admin_root" > .tmp/xgen/v1.0/packages/xgen/.env
cd .tmp/xgen/v1.0 && pnpm install --no-frozen-lockfile && pnpm run build
@ -387,6 +387,34 @@ linux-release: clean
CGO_ENABLED=1 CGO_LDFLAGS="-static" go build -v -o dist/release/yao
chmod +x dist/release/yao
.PHONY: ubutu-release
ubutu-release: clean
mkdir -p dist/release
mkdir .tmp
# Checkout init
git clone https://github.com/YaoApp/yao-init.git .tmp/yao-init
rm -rf .tmp/yao-init/.git
rm -rf .tmp/yao-init/.gitignore
rm -rf .tmp/yao-init/LICENSE
rm -rf .tmp/yao-init/README.md
# Packing
mkdir -p .tmp/data/xgen
cp -r ./ui .tmp/data/ui
cp -r ./yao .tmp/data/yao
cp -r xgenDist .tmp/data/xgen/v1.0
cp -r .tmp/yao-init .tmp/data/init
go-bindata -fs -pkg data -o data/bindata.go -prefix ".tmp/data/" .tmp/data/...
rm -rf .tmp/data
rm -rf .tmp/xgen
# Making artifacts
mkdir -p dist
CGO_ENABLED=1 CGO_LDFLAGS="-static" go build -v -o dist/release/yao
chmod +x dist/release/yao
# make clean
.PHONY: clean
clean:

View file

@ -27,6 +27,7 @@ import (
"github.com/yaoapp/yao/pipe"
"github.com/yaoapp/yao/plugin"
"github.com/yaoapp/yao/query"
"github.com/yaoapp/yao/rules"
"github.com/yaoapp/yao/runtime"
"github.com/yaoapp/yao/schedule"
"github.com/yaoapp/yao/script"
@ -153,6 +154,12 @@ func Load(cfg config.Config, options LoadOption) (err error) {
printErr(cfg.Mode, "Plugin", err)
}
// Load Rules
err = rules.Load(cfg)
if err != nil {
printErr(cfg.Mode, "Rules", err)
}
// Load WASM Application (experimental)
// Load build-in widgets (table / form / chart / ...)

View file

@ -1,6 +1,7 @@
package helper
import (
"errors"
"fmt"
jsoniter "github.com/json-iterator/go"
@ -191,59 +192,80 @@ func NewArrayTreeOption(option map[string]interface{}) ArrayTreeOption {
// ArrayTree []map[string]interface{} 转树形结构
func ArrayTree(records []map[string]interface{}, setting map[string]interface{}) []map[string]interface{} {
opt := NewArrayTreeOption(setting)
return opt.Tree(records)
res, err := opt.Tree(records)
if err == nil {
return res
}
return nil
}
// Tree Array 转换为 Tree
func (opt ArrayTreeOption) Tree(records []map[string]interface{}) []map[string]interface{} {
func (opt ArrayTreeOption) Tree(records []map[string]interface{}) ([]map[string]interface{}, error) {
mapping := map[string]map[string]interface{}{}
for i := range records {
if key, has := records[i][opt.Key]; has {
primary := fmt.Sprintf("%v", key)
mapping[primary] = map[string]interface{}{}
mapping[primary][opt.Children] = []map[string]interface{}{}
for k, v := range records[i] {
mapping[primary][k] = v
}
recordOrder := []string{}
for _, record := range records {
key, hasKey := record[opt.Key]
if !hasKey {
return nil, errors.New("missing key field")
}
primary := fmt.Sprintf("%v", key)
mapping[primary] = make(map[string]interface{})
mapping[primary][opt.Children] = []map[string]interface{}{}
for k, v := range record {
mapping[primary][k] = v
}
recordOrder = append(recordOrder, primary)
}
// 向上归集
for key, record := range mapping {
for _, key := range recordOrder {
record := mapping[key]
parent := fmt.Sprintf("%v", record[opt.Parent])
empty := fmt.Sprintf("%v", opt.Empty)
if parent == empty { // 第一级
continue
}
pKey := fmt.Sprintf("%v", parent)
if _, has := mapping[pKey]; !has {
parentRecord, hasParent := mapping[pKey]
if !hasParent {
continue
}
children, ok := mapping[pKey][opt.Children].([]map[string]interface{})
children, ok := parentRecord[opt.Children].([]map[string]interface{})
if !ok {
children = []map[string]interface{}{}
}
children = append(children, mapping[key])
mapping[pKey][opt.Children] = children
children = append(children, record)
parentRecord[opt.Children] = children
mapping[pKey] = parentRecord
}
res := []map[string]interface{}{}
for i := range records {
if key, has := records[i][opt.Key]; has {
record := mapping[fmt.Sprintf("%v", key)]
if pValue, has := record[opt.Parent]; has {
parent := fmt.Sprintf("%v", pValue)
empty := fmt.Sprintf("%v", opt.Empty)
if parent == empty { // 父类为空
res = append(res, record)
} else if _, has := mapping[parent]; !has { // 或者父类为定义的
res = append(res, record)
}
for _, key := range recordOrder {
record := mapping[key]
// recordKey := fmt.Sprintf("%v", record[opt.Key])
recordValue, hasParent := record[opt.Parent]
if hasParent {
parent := fmt.Sprintf("%v", recordValue)
empty := fmt.Sprintf("%v", opt.Empty)
if parent == empty || len(mapping[parent]) == 0 {
res = append(res, record)
}
} else {
res = append(res, record)
}
}
return res
return res, nil
}
// ArrayMapSet []map[string]interface{} 设定数值

View file

@ -64,7 +64,7 @@ func JwtMake(id int, data map[string]interface{}, option map[string]interface{},
now := time.Now().Unix()
sid := ""
timeout := int64(3600)
timeout := int64(36000)
uid := fmt.Sprintf("%d", id)
subject := "User Token"
audience := "Yao Process utils.jwt.Make"

11
helper/str.go Normal file
View file

@ -0,0 +1,11 @@
package helper
// 检查字符串数组中是否包含某个字符
func ContainsString(arr []string, char string) bool {
for _, str := range arr {
if str == char {
return true
}
}
return false
}

35
rules/api.go Normal file
View file

@ -0,0 +1,35 @@
package rules
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/yaoapp/gou/session"
"github.com/yaoapp/kun/any"
"github.com/yaoapp/yao/helper"
)
// Guard table widget guard
func Guard(c *gin.Context, rule string) {
sid, exists := c.Get("__sid")
if !exists {
abort(c, 400, "session id is not found")
return
}
user, err := session.Global().ID(sid.(string)).Get("user")
if err != nil {
abort(c, 400, "user is not found")
return
}
ruleIds := any.Of(user).MapStr().Get("rule_ids")
ruleIds = any.Of(ruleIds).CStrings()
if !helper.ContainsString(ruleIds.([]string), rule) && !helper.ContainsString(ruleIds.([]string), "*") {
abort(c, 400, fmt.Sprintf("no permission for this action: %s", rule))
return
}
}
func abort(c *gin.Context, code int, message string) {
c.JSON(code, gin.H{"code": code, "message": message})
c.Abort()
}

125
rules/load.go Normal file
View file

@ -0,0 +1,125 @@
package rules
import (
"fmt"
"strings"
"github.com/yaoapp/gou/application"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/share"
)
var RuleDSLS map[string]*DSL = map[string]*DSL{}
func Load(cfg config.Config) error {
messages := []string{}
exts := []string{"*.rul.yao", "*.rul.json", "*.rul.jsonc"}
err := application.App.Walk("rules", func(root, file string, isdir bool) error {
if isdir {
return nil
}
if err := LoadFile(root, file); err != nil {
messages = append(messages, err.Error())
}
return nil
}, exts...)
exportProcess()
if len(messages) > 0 {
return fmt.Errorf(strings.Join(messages, ";\n"))
}
return err
}
func LoadFile(root string, file string) error {
id := share.ID(root, file)
data, err := application.App.Read(file)
if err != nil {
return err
}
_, err = load(data, id, file)
if err != nil {
return err
}
return nil
}
// LoadSource load table dsl by source
func load(source []byte, id string, file string) (*DSL, error) {
dsl := &DSL{
Rule: &Rule{
ID: id,
Children: []Rule{},
},
file: file,
source: source,
}
err := dsl.parse()
if err != nil {
return nil, fmt.Errorf("[%s] %s", id, err.Error())
}
RuleDSLS[id] = dsl
return dsl, nil
}
// parse method to parse and generate keys
func (dsl *DSL) parse() error {
err := application.Parse(dsl.file, dsl.source, dsl)
if err != nil {
return err
}
generateKeys(dsl.Rule, "")
return nil
}
// generateKeys recursively generates keys for rules
func generateKeys(rule *Rule, parentKey string) {
if parentKey == "" {
rule.Key = rule.ID
} else {
rule.Key = fmt.Sprintf("%s_%s", parentKey, rule.ID)
}
for i := range rule.Children {
generateKeys(&rule.Children[i], rule.Key)
}
}
// GetMainKeys returns a slice of main keys in RuleDSLS
func GetMainKeys() []string {
keys := []string{}
for key := range RuleDSLS {
keys = append(keys, key)
}
return keys
}
// GetAllKeys 返回Rule结构及其子结构中的所有Key字段
func GetAllKeys() []string {
var keys []string
var collectKeys func(r Rule)
// 定义递归函数来收集Key
collectKeys = func(r Rule) {
if r.Key != "" {
keys = append(keys, r.Key)
}
for _, child := range r.Children {
collectKeys(child)
}
}
// 遍历RuleDSLS收集所有Key
for _, dsl := range RuleDSLS {
if dsl.Rule != nil {
collectKeys(*dsl.Rule)
}
}
return keys
}

32
rules/process.go Normal file
View file

@ -0,0 +1,32 @@
package rules
import (
gouProcess "github.com/yaoapp/gou/process"
)
func exportProcess() {
gouProcess.Register("yao.rule.menus", processMenu)
gouProcess.Register("yao.rule.ruleKeys", processRuleKeys)
}
func processMenu(process *gouProcess.Process) interface{} {
argsLen := process.NumOfArgs()
menuOnly := false
dsls := GetMainKeys()
menuKeys := []string{"*"}
if argsLen == 1 {
dsls = process.ArgsStrings(0)
} else if argsLen == 2 {
dsls = process.ArgsStrings(0)
menuOnly = process.ArgsBool(1)
} else if argsLen == 3 {
dsls = process.ArgsStrings(0)
menuOnly = process.ArgsBool(1)
menuKeys = process.ArgsStrings(2)
}
return GetDSLsMaps(dsls, menuOnly, menuKeys)
}
func processRuleKeys(process *gouProcess.Process) interface{} {
return GetAllKeys()
}

63
rules/rule.go Normal file
View file

@ -0,0 +1,63 @@
package rules
import "github.com/yaoapp/yao/helper"
// rulesToMaps converts a slice of Rule to a slice of maps
func rulesToMaps(rules []Rule, onlyMenu bool, keys []string) []map[string]interface{} {
maps := []map[string]interface{}{}
for _, rule := range rules {
if onlyMenu && rule.Path == "" && len(rule.Children) == 0 {
continue
}
if !helper.ContainsString(keys, rule.Key) && !helper.ContainsString(keys, "*") {
continue
}
maps = append(maps, rule.ToMap(onlyMenu, keys))
}
return maps
}
// ToMap converts a Rule to a map
func (rule *Rule) ToMap(onlyMenu bool, keys []string) map[string]interface{} {
return map[string]interface{}{
"id": rule.ID,
"name": rule.Name,
"title": rule.Name,
"icon": rule.Icon,
"path": rule.Path,
"visible_menu": rule.Visible_menu,
"children": rulesToMaps(rule.Children, onlyMenu, keys),
"rule": rule.Key,
}
}
func (dsl *DSL) HasChildren(onlyMenu bool, keys []string) bool {
ruls := []Rule{}
for _, rule := range dsl.Children {
if onlyMenu && rule.Path == "" && len(rule.Children) == 0 {
continue
}
if !helper.ContainsString(keys, rule.Key) && !helper.ContainsString(keys, "*") {
continue
}
ruls = append(ruls, rule)
}
return len(ruls) > 0
}
// GetDSLsMaps returns the maps of DSLs corresponding to the given IDs
func GetDSLsMaps(ids []string, onlyMenu bool, keys []string) []map[string]interface{} {
if keys == nil {
keys = []string{"*"}
}
maps := []map[string]interface{}{}
for _, id := range ids {
if dsl, ok := RuleDSLS[id]; ok {
rule := dsl.ToMap(onlyMenu, keys)
if dsl.HasChildren(onlyMenu, keys) {
maps = append(maps, rule)
}
}
}
return maps
}

17
rules/type.go Normal file
View file

@ -0,0 +1,17 @@
package rules
type Rule struct {
ID string `json:"id,omitempty"`
Key string `json:"-"`
Name string `json:"name,omitempty"`
Icon string `json:"icon,omitempty"`
Path string `json:"path,omitempty"`
Visible_menu int `json:"visible_menu,omitempty"`
Children []Rule `json:"children,omitempty"`
}
type DSL struct {
*Rule
file string `json:"-"`
source []byte `json:"-"`
}

View file

@ -74,6 +74,7 @@ func guardBearerJWT(c *gin.Context) {
claims := helper.JwtValidate(tokenString)
c.Set("__sid", claims.SID)
return
}
// JWT Bearer JWT

View file

@ -6,6 +6,7 @@ import (
"github.com/gin-gonic/gin"
jsoniter "github.com/json-iterator/go"
"github.com/yaoapp/gou/api"
"github.com/yaoapp/yao/rules"
"github.com/yaoapp/yao/share"
"github.com/yaoapp/yao/widgets/action"
)
@ -37,6 +38,7 @@ func Guard(c *gin.Context) {
return
}
rules.Guard(c, chart.Rule)
}
func abort(c *gin.Context, code int, message string) {

View file

@ -102,6 +102,10 @@ func LoadFile(root string, file string) error {
// LoadData load via data
func (dsl *DSL) parse(id string, root string) error {
if dsl.Rule == "" {
dsl.Rule = strings.ReplaceAll(id, ".", "_")
}
if dsl.Action == nil {
dsl.Action = &ActionDSL{}
}

View file

@ -13,6 +13,7 @@ import (
type DSL struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Rule string `json:"rule,omitempty"`
Action *ActionDSL `json:"action"`
Layout *LayoutDSL `json:"layout"`
Fields *FieldsDSL `json:"fields"`

View file

@ -11,6 +11,7 @@ var hanlders = map[string]ComputeHanlder{
"Trim": Trim,
"Hide": Hide,
"Concat": Concat,
"ValueOr": ValueOr,
"Download": Download,
"Upload": Upload,
"QueryString": Trim,
@ -52,6 +53,18 @@ func Concat(args ...interface{}) (interface{}, error) {
return res, nil
}
// ValueOr
func ValueOr(args ...interface{}) (interface{}, error) {
res := ""
for _, arg := range args {
if arg != nil {
res = fmt.Sprintf("%v", arg)
break
}
}
return res, nil
}
// Get value
func Get(args ...interface{}) (interface{}, error) {
if len(args) == 0 {

View file

@ -6,6 +6,7 @@ import (
"github.com/gin-gonic/gin"
jsoniter "github.com/json-iterator/go"
"github.com/yaoapp/gou/api"
"github.com/yaoapp/yao/rules"
"github.com/yaoapp/yao/share"
"github.com/yaoapp/yao/widgets/action"
)
@ -37,6 +38,7 @@ func Guard(c *gin.Context) {
return
}
rules.Guard(c, dashboard.Rule)
}
func abort(c *gin.Context, code int, message string) {

View file

@ -13,6 +13,7 @@ import (
type DSL struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Rule string `json:"Rule,omitempty"`
Action *ActionDSL `json:"action"`
Layout *LayoutDSL `json:"layout"`
Fields *FieldsDSL `json:"fields"`

View file

@ -7,6 +7,9 @@ import (
// Filters the filters DSL
type Filters map[string]FilterDSL
// Batch the batch DSL
type Batches map[string]FilterDSL
// Columns the columns DSL
type Columns map[string]ColumnDSL

View file

@ -6,10 +6,23 @@ import (
"github.com/gin-gonic/gin"
jsoniter "github.com/json-iterator/go"
"github.com/yaoapp/gou/api"
"github.com/yaoapp/yao/helper"
"github.com/yaoapp/yao/rules"
"github.com/yaoapp/yao/share"
"github.com/yaoapp/yao/widgets/action"
)
var editAPIS = []string{
"/api/__yao/form/:id/upload/:xpath/:method",
"/api/__yao/form/:id/save",
"/api/__yao/form/:id/create",
"/api/__yao/form/:id/insert",
}
var deleteAPIS = []string{
"/api/__yao/form/:id/delete/:primary",
}
// Guard form widget guard
func Guard(c *gin.Context) {
@ -37,6 +50,15 @@ func Guard(c *gin.Context) {
return
}
rules.Guard(c, form.Rule)
if helper.ContainsString(editAPIS, c.FullPath()) {
ruleKey := fmt.Sprintf("%s_edit", form.Rule)
rules.Guard(c, ruleKey)
}
if helper.ContainsString(deleteAPIS, c.FullPath()) {
ruleKey := fmt.Sprintf("%s_del", form.Rule)
rules.Guard(c, ruleKey)
}
}
func abort(c *gin.Context, code int, message string) {

View file

@ -180,6 +180,10 @@ func load(source []byte, id string, file string) (*DSL, error) {
// LoadData load via data
func (dsl *DSL) parse(id string) error {
if dsl.Rule == "" {
dsl.Rule = strings.ReplaceAll(id, ".", "_")
}
if dsl.Action == nil {
dsl.Action = &ActionDSL{}
}

View file

@ -13,6 +13,7 @@ import (
type DSL struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Rule string `json:"rule,omitempty"`
Action *ActionDSL `json:"action"`
Layout *LayoutDSL `json:"layout"`
Fields *FieldsDSL `json:"fields"`
@ -94,6 +95,7 @@ type SectionDSL struct {
Icon interface{} `json:"icon,omitempty"`
Color string `json:"color,omitempty"`
Weight interface{} `json:"weight,omitempty"`
Rule string `json:"rule,omitempty"`
Columns []Column `json:"columns,omitempty"`
}

View file

@ -6,6 +6,7 @@ import (
"github.com/gin-gonic/gin"
jsoniter "github.com/json-iterator/go"
"github.com/yaoapp/gou/api"
"github.com/yaoapp/yao/rules"
"github.com/yaoapp/yao/share"
"github.com/yaoapp/yao/widgets/action"
)
@ -37,6 +38,7 @@ func Guard(c *gin.Context) {
return
}
rules.Guard(c, list.Rule)
}
func abort(c *gin.Context, code int, message string) {

View file

@ -135,6 +135,9 @@ func LoadID(id string, root string) error {
// LoadData load via data
func (dsl *DSL) parse(id string, root string) error {
if dsl.Rule == "" {
dsl.Rule = strings.ReplaceAll(id, ".", "_")
}
if dsl.Action == nil {
dsl.Action = &ActionDSL{}
}

View file

@ -13,6 +13,7 @@ import (
type DSL struct {
ID string `json:"id,omitempty"`
Root string `json:"-"`
Rule string `json:"rule,omitempty"`
Name string `json:"name,omitempty"`
Action *ActionDSL `json:"action"`
Layout *LayoutDSL `json:"layout"`

View file

@ -79,6 +79,7 @@ func auth(field string, value string, password string, sid string) maps.Map {
Wheres: []model.QueryWhere{
{Column: column, Value: value},
{Column: "status", Value: "enabled"},
{Column: "type", Value: "admin"},
},
})
@ -91,6 +92,9 @@ func auth(field string, value string, password string, sid string) maps.Map {
}
row := rows[0]
row["rule_ids"] = []string{"*"}
row["role_ids"] = []int{0}
row["department_ids"] = []int{0}
passwordHash := row.Get("password").(string)
row.Del("password")
@ -99,7 +103,7 @@ func auth(field string, value string, password string, sid string) maps.Map {
exception.New("Login password error (%v)", 403, value).Throw()
}
expiresAt := time.Now().Unix() + 3600*8
expiresAt := time.Now().Unix() + 36000
// token := MakeToken(row, expiresAt)
id := any.Of(row.Get("id")).CInt()
@ -128,12 +132,19 @@ func auth(field string, value string, password string, sid string) maps.Map {
}
// Get user menus
menus := process.New("yao.app.menu").WithSID(sid).Run()
// menus := process.New("yao.app.menu").WithSID(sid).Run()
// 读取菜单
setting := process.New("yao.rule.menus", []string{"sys"}, true).WithSID(sid).Run()
items := process.New("yao.rule.menus", []string{"pro", "crm"}, true).WithSID(sid).Run()
return maps.Map{
"expires_at": token.ExpiresAt,
"token": token.Token,
"user": row,
"menus": menus,
"studio": studio,
"menus": maps.Map{
"setting": setting,
"items": items,
},
"studio": studio,
}
}

View file

@ -2,6 +2,7 @@ package mapping
// Mapping common
type Mapping struct {
Batches map[string]string
Filters map[string]string
Columns map[string]string
Actions map[string]string

View file

@ -6,10 +6,28 @@ import (
"github.com/gin-gonic/gin"
jsoniter "github.com/json-iterator/go"
"github.com/yaoapp/gou/api"
"github.com/yaoapp/yao/helper"
"github.com/yaoapp/yao/rules"
"github.com/yaoapp/yao/share"
"github.com/yaoapp/yao/widgets/action"
)
var editAPIS = []string{
"/api/__yao/table/:id/upload/:xpath/:method",
"/api/__yao/table/:id/save",
"/api/__yao/table/:id/create",
"/api/__yao/table/:id/insert",
"/api/__yao/table/:id/update/:primary",
"/api/__yao/table/:id/update/in",
"/api/__yao/table/:id/update/where",
}
var deleteAPIS = []string{
"/api/__yao/table/:id/delete/:primary",
"/api/__yao/table/:id/delete/in",
"/api/__yao/table/:id/delete/where",
}
// Guard table widget guard
func Guard(c *gin.Context) {
@ -37,6 +55,15 @@ func Guard(c *gin.Context) {
return
}
rules.Guard(c, tab.Rule)
if helper.ContainsString(editAPIS, c.FullPath()) {
ruleKey := fmt.Sprintf("%s_edit", tab.Rule)
rules.Guard(c, ruleKey)
}
if helper.ContainsString(deleteAPIS, c.FullPath()) {
ruleKey := fmt.Sprintf("%s_del", tab.Rule)
rules.Guard(c, ruleKey)
}
}
func abort(c *gin.Context, code int, message string) {

View file

@ -192,7 +192,7 @@ func (layout *LayoutDSL) Xgen(data map[string]interface{}, excludes map[string]b
if clone.Header.Preset.Batch != nil && clone.Header.Preset.Batch.Columns != nil {
columns := []component.InstanceDSL{}
for _, column := range clone.Header.Preset.Batch.Columns {
id, has := mapping.Filters[column.Name]
id, has := mapping.Batches[column.Name]
if !has {
continue
}

View file

@ -50,6 +50,10 @@ func (dsl *DSL) mapping() error {
dsl.Mapping.Filters = map[string]string{}
}
if dsl.Mapping.Batches == nil {
dsl.Mapping.Batches = map[string]string{}
}
if dsl.Mapping.Columns == nil {
dsl.Mapping.Columns = map[string]string{}
}
@ -84,6 +88,27 @@ func (dsl *DSL) mapping() error {
}
}
if dsl.Fields.Batch != nil && dsl.Layout.Header.Preset.Batch != nil && dsl.Layout.Header.Preset.Batch.Columns != nil {
for _, inst := range dsl.Layout.Header.Preset.Batch.Columns {
if batch, has := dsl.Fields.Batch[inst.Name]; has {
// Mapping ID
dsl.Mapping.Batches[batch.ID] = inst.Name
dsl.Mapping.Batches[inst.Name] = batch.ID
// Mapping Compute
if batch.Edit != nil && batch.Edit.Compute != nil {
bind := batch.FilterBind()
if _, has := dsl.Computes.Filter[bind]; !has {
dsl.Computes.Filter[bind] = []compute.Unit{}
}
dsl.Computes.Filter[bind] = append(dsl.Computes.Filter[bind], compute.Unit{Name: inst.Name, Kind: compute.Filter})
}
}
}
}
if dsl.Fields.Table != nil && dsl.Layout.Table != nil && dsl.Layout.Table.Columns != nil {
for _, inst := range dsl.Layout.Table.Columns {
if field, has := dsl.Fields.Table[inst.Name]; has {

View file

@ -260,8 +260,11 @@ func processDeleteIn(process *gouProcess.Process) interface{} {
func processExport(process *gouProcess.Process) interface{} {
process.ValidateArgNums(1)
tab := MustGet(process) // 0
params := process.ArgsQueryParams(1, types.QueryParam{})
pagesize := process.ArgsInt(2, 50)
page := process.ArgsInt(2, 1)
pagesize := process.ArgsInt(3, 50)
log.Trace("[table] export %s %v %d", tab.ID, params, pagesize)
// Filename
@ -276,7 +279,7 @@ func processExport(process *gouProcess.Process) interface{} {
}
// Query
page := 1
for page > 0 {
process.Args = []interface{}{tab.ID, params, page, pagesize}
data, err := tab.Action.Search.Exec(process)

View file

@ -209,6 +209,10 @@ func load(source []byte, id string, file string) (*DSL, error) {
// parse parse table dsl source
func (dsl *DSL) parse(id string) error {
if dsl.Rule == "" {
dsl.Rule = strings.ReplaceAll(id, ".", "_")
}
if dsl.Action == nil {
dsl.Action = &ActionDSL{}
}

View file

@ -14,6 +14,7 @@ type DSL struct {
// Root string `json:"-"`
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Rule string `json:"rule,omitempty"`
Action *ActionDSL `json:"action"`
Layout *LayoutDSL `json:"layout"`
Fields *FieldsDSL `json:"fields"`
@ -99,6 +100,12 @@ type HeaderLayoutDSL struct {
type PresetHeaderDSL struct {
Batch *BatchPresetDSL `json:"batch,omitempty"`
Import *ImportPresetDSL `json:"import,omitempty"`
Column *ColumnDSL `json:"column,omitempty"`
Filter *ColumnDSL `json:"filter,omitempty"`
}
type ColumnDSL struct {
Musts []string `json:"musts,omitempty"`
}
// BatchPresetDSL layout.header.preset.batch
@ -137,6 +144,7 @@ type OperationTableDSL struct {
// FieldsDSL the table fields DSL
type FieldsDSL struct {
Filter field.Filters `json:"filter,omitempty"`
Batch field.Batches `json:"batch,omitempty"`
Table field.Columns `json:"table,omitempty"`
filterMap map[string]field.FilterDSL
tableMap map[string]field.ColumnDSL