diff --git a/action/action.go b/action/action.go index 83f474f..827352f 100755 --- a/action/action.go +++ b/action/action.go @@ -44,6 +44,8 @@ type Action struct { NewAction func(ctx context.Context, method string, req model.Map) *Action HooksMap map[string][]*Hook + + ret model.Map } func New(ctx context.Context, actionConfig *config.ActionConfig, method string, req model.Map) *Action { @@ -118,67 +120,68 @@ func (a *Action) parse() error { return nil } -func (a *Action) Result() (model.Map, error) { - - err := a.parse() - if err != nil { - return nil, err +func (a *Action) hookExecute(i int, inTransaction bool) error { + k := a.tagRequest.ExecQueue[i] + node := a.children[k] + nodeHookReq := &HookReq{ + Node: node, + Method: a.method, + ctx: a.ctx, + nextIdx: -1, + isInTransaction: inTransaction, + hooks: getHooksByAccessName(a.HooksMap, k), } - ret := model.Map{} - - for _, k := range a.tagRequest.ExecQueue { - node := a.children[k] + nodeHookReq.handler = func(ctx context.Context, n *Node, method string) error { - actionHookReq := &HookReq{ - Node: node, - Method: a.method, - ctx: a.ctx, - nextIdx: -1, - isInTransaction: false, - hooks: getHooksByAccessName(a.HooksMap, k), + if i+1 < len(a.tagRequest.ExecQueue) { + return a.hookExecute(i+1, inTransaction) } - actionHookReq.handler = func(ctx context.Context, n *Node, method string) error { + // 执行完了普通hook的before,开始执行事务内 + if !inTransaction { + transactionHandler := noTransactionHandler if a.tagRequest.Transaction != nil && *a.tagRequest.Transaction == true { h := GetTransactionHandler(a.ctx, a) if h == nil { - err = consts.NewSysErr("transaction handler is nil") + err := consts.NewSysErr("transaction handler is nil") return err } - transactionHandler = h - } - err = transactionHandler(a.ctx, func(ctx context.Context) error { - for _, k := range a.tagRequest.ExecQueue { - node := a.children[k] - ret[k], err = node.execute(ctx, a.method) - if err != nil { - return err - } - } - return nil + err := transactionHandler(a.ctx, func(ctx context.Context) error { + return a.hookExecute(0, !inTransaction) }) return err } - err = node.reqUpdate() - if err != nil { - return nil, err - } + var err error + a.ret[k], err = node.execute(ctx, a.method) + return err + } - err := actionHookReq.Next() - if err != nil { - return nil, err - } + err := nodeHookReq.Next() + return err +} + +func (a *Action) Result() (model.Map, error) { + + err := a.parse() + if err != nil { + return nil, err } - return ret, nil + a.ret = model.Map{} + + err = a.hookExecute(0, false) + if err != nil { + a.err = err + } + return a.ret, err } func checkTag(req model.Map, method string, requestCfg *config.ActionConfig) (*config.RequestConfig, error) { diff --git a/action/hook.go b/action/hook.go index 04bbd39..6e25377 100755 --- a/action/hook.go +++ b/action/hook.go @@ -5,13 +5,6 @@ import ( "net/http" ) -// const ( -// BeforeNodeExec = iota -// AfterNodeExec -// BeforeExecutorDo -// AfterExecutorDo -// ) - type HookReq struct { Node *Node Method string @@ -40,17 +33,7 @@ func (r *HookReq) Next() error { var h *Hook - for r.nextIdx < len(r.hooks) && h == nil { - - if r.nextIdx+1 >= len(r.hooks) { - if r.isInTransaction { - // finish all - return r.handler(r.ctx, r.Node, r.Method) - } else { - r.nextIdx = -1 - r.isInTransaction = true - } - } + for r.nextIdx+1 < len(r.hooks) && h == nil { r.nextIdx++ @@ -70,13 +53,17 @@ func (r *HookReq) Next() error { } - if r.nextIdx < len(r.hooks) { - if r.isInTransaction { - return h.HandlerInTransaction(r.ctx, r) - } + if h != nil { + if r.nextIdx < len(r.hooks) { + if r.isInTransaction { + return h.HandlerInTransaction(r.ctx, r) + } - return h.Handler(r.ctx, r) + return h.Handler(r.ctx, r) + } } + + return r.handler(r.ctx, r.Node, r.Method) } } @@ -95,42 +82,3 @@ func getHooksByAccessName(hooksMap map[string][]*Hook, accessName string) []*Hoo hooks := append(hooksMap["*"], hooksMap[accessName]...) return hooks } - -// -// type Hook2 struct { -// For []string // -// // Exec 事务外 -// BeforeNodeExec func(ctx context.Context, n *Node, method string) error -// AfterNodeExec func(ctx context.Context, n *Node, method string) error -// -// // Do 事务内 -// BeforeExecutorDo func(ctx context.Context, n *Node, method string) error -// AfterExecutorDo func(ctx context.Context, n *Node, method string) error -// } -// -// func emitHook(ctx context.Context, hooksMap map[string][]Hook, hookAt int, node *Node, method string) error { -// -// hooks := append(hooksMap["*"], hooksMap[node.Key]...) -// for _, hook := range hooks { -// -// var handler func(ctx context.Context, n *Node, method string) error -// switch hookAt { -// case BeforeNodeExec: -// handler = hook.BeforeNodeExec -// case AfterNodeExec: -// handler = hook.AfterNodeExec -// case BeforeExecutorDo: -// handler = hook.BeforeExecutorDo -// case AfterExecutorDo: -// handler = hook.AfterExecutorDo -// } -// -// if handler != nil { -// err := handler(ctx, node, method) -// if err != nil { -// return err -// } -// } -// } -// return nil -// } diff --git a/action/node.go b/action/node.go index 4eadbef..761e3d5 100755 --- a/action/node.go +++ b/action/node.go @@ -9,6 +9,7 @@ import ( "github.com/glennliao/apijson-go/consts" "github.com/glennliao/apijson-go/model" "github.com/glennliao/apijson-go/util" + "github.com/gogf/gf/v2/errors/gerror" "github.com/samber/lo" ) @@ -190,12 +191,19 @@ func (n *Node) whereUpdate(ctx context.Context, method string, accessRoles []str condition := config.NewConditionRet() + req := model.Map{} + + for k, v := range item { + k := n.Action.DbFieldStyle(ctx, n.RowKey, k) + req[k] = v + } + conditionReq := config.ConditionReq{ AccessName: n.Key, TableAccessRoleList: accessRoles, Method: method, NodeRole: n.Role, - NodeReq: item, + NodeReq: req, } err := n.Action.ActionConfig.ConditionFunc(ctx, conditionReq, condition) @@ -205,11 +213,11 @@ func (n *Node) whereUpdate(ctx context.Context, method string, accessRoles []str } if method == http.MethodPost { - for k, v := range condition.Where() { + for k, v := range condition.AllWhere() { n.Data[i][k] = v } } else { - for k, v := range condition.Where() { + for k, v := range condition.AllWhere() { n.Where[i][k] = v } } @@ -343,7 +351,7 @@ func (n *Node) do(ctx context.Context, method string) (ret model.Map, err error) rowKeyVal, err = n.Action.ActionConfig.RowKeyGen(ctx, access.RowKeyGen, n.Key, n.tableName, n.Data[i]) if err != nil { - return nil, err + return nil, gerror.Wrap(err, "RowKeyGen") } for k, v := range rowKeyVal { diff --git a/action/z_hook_test.go b/action/z_hook_test.go new file mode 100644 index 0000000..adaf71a --- /dev/null +++ b/action/z_hook_test.go @@ -0,0 +1,9 @@ +package action + +import ( + "testing" +) + +func TestHook(t *testing.T) { + +} diff --git a/config/access.go b/config/access.go index efa8938..42906b1 100755 --- a/config/access.go +++ b/config/access.go @@ -16,34 +16,6 @@ type ConditionReq struct { NodeRole string // 节点的角色 } -type ConditionRet struct { - condition map[string]any - rawCondition map[string][]any -} - -func NewConditionRet() *ConditionRet { - c := ConditionRet{ - condition: map[string]any{}, - rawCondition: map[string][]any{}, - } - return &c -} - -func (c *ConditionRet) Add(k string, v any) { - c.condition[k] = v -} - -func (c *ConditionRet) AddRaw(k string, v ...any) { - c.rawCondition[k] = v -} - -func (c *ConditionRet) Where() map[string]any { - if len(c.rawCondition) > 0 { - c.condition[consts.Raw] = c.rawCondition - } - return c.condition -} - type AccessCondition func(ctx context.Context, req ConditionReq, condition *ConditionRet) error type RoleReq struct { @@ -79,6 +51,8 @@ type Access struct { func NewAccess() *Access { + // fixme 统一access字段名大小写问题 + // fixme a := &Access{} a.ConditionFunc = defaultCondition a.DefaultRoleFunc = defaultRole diff --git a/config/access_condition.go b/config/access_condition.go new file mode 100644 index 0000000..d2808d3 --- /dev/null +++ b/config/access_condition.go @@ -0,0 +1,33 @@ +package config + +import ( + "github.com/glennliao/apijson-go/consts" +) + +type ConditionRet struct { + condition map[string]any + rawCondition map[string][]any +} + +func NewConditionRet() *ConditionRet { + c := ConditionRet{ + condition: map[string]any{}, + rawCondition: map[string][]any{}, + } + return &c +} + +func (c *ConditionRet) Add(k string, v any) { + c.condition[k] = v +} + +func (c *ConditionRet) AddRaw(k string, v ...any) { + c.rawCondition[k] = v +} + +func (c *ConditionRet) AllWhere() map[string]any { + if len(c.rawCondition) > 0 { + c.condition[consts.Raw] = c.rawCondition + } + return c.condition +} diff --git a/config/functions.go b/config/functions.go index 5beb788..dfa0649 100755 --- a/config/functions.go +++ b/config/functions.go @@ -12,6 +12,7 @@ import ( const ( ParamTypeInt = "int" ParamTypeString = "string" + FromRes = "res" // 只从响应的数据字段中取, 不从用户传递的数据取 ) type ParamItem struct { @@ -19,13 +20,17 @@ type ParamItem struct { Name string Desc string Default any + From string // 指定参数从何处取值 + V string // 参数校验规则 } type Func struct { - Desc string // 描述 - ParamList []ParamItem // 参数列表 - Batch bool // 是否为批量处理, 例如在获取列表后一次性将id传入, 然后按照传入的参数数组返回结果数组 - Handler func(ctx context.Context, param model.FuncParam) (res any, err error) + Desc string // 描述 + // 参数可直接读取函数参数传递过来的, ''括起来 + ParamList []ParamItem // 参数列表 // fixme 限制参数来源,强制用户传递的无法覆盖内部的,减免权限的重复判断, 参数校验限制 , v (最大值,最小值,默认值, 自定义校验。 使用gvaild) + + Batch bool // 是否为批量处理, 例如在获取列表后一次性将id传入, 然后按照传入的参数数组返回结果数组 + Handler func(ctx context.Context, param model.FuncParam) (res any, err error) } type functions struct { @@ -49,7 +54,7 @@ func (f *functions) Call(ctx context.Context, name string, param g.Map) (any, er return f.funcMap[name].Handler(ctx, params) } -// functions 提供的功能 +// functions 可能提供的功能 // 1. 增加响应字段 -> 该字段需要与系统中别的数据结合处理,如果只是静态处理(去空格,与常量拼接等可直接前端处理即可) 目前会不受_access_ext 中field_get控制, 需处理. 响应字段修改(脱敏、加密、字典转换) 不提供前端控制, 由_access_ext处理 // 2. 通过func节点获取一些系统信息 // 3. actions 中 自定义校验参数、自定义校验权限, 请求体修改(批量字段替换处理?) diff --git a/query/node_func.go b/query/node_func.go index d065d1c..26cd84d 100755 --- a/query/node_func.go +++ b/query/node_func.go @@ -39,10 +39,7 @@ func (h *funcNode) result() { return } - if n.isList && _func.Batch { - n.later = true - return - } + // todo batch support param := model.Map{} diff --git a/query/node_query.go b/query/node_query.go index 768e663..c4f681e 100755 --- a/query/node_query.go +++ b/query/node_query.go @@ -69,7 +69,7 @@ func (q *queryNode) parse() { return } - accessWhereCondition = condition.Where() + accessWhereCondition = condition.AllWhere() } queryExecutor, err := NewExecutor(n.executorConfig.Executor(), n.ctx, n.executorConfig) @@ -258,7 +258,7 @@ func (q *queryNode) fetch() { continue } - k = k[0 : len(k)-2] + k = k[0 : len(k)-len(consts.FunctionsKeySuffix)] functionName, paramKeys := util.ParseFunctionsStr(v.(string)) _func := queryConfig.Func(functionName) @@ -269,32 +269,66 @@ func (q *queryNode) fetch() { } if n.isList { - for i, item := range n.ret.([]model.Map) { - // todo 统一functions调用处理 + // todo 统一func的调用? + + // 组装参数 + var paramList []model.Map + retList := n.ret.([]model.Map) + for _, ret := range retList { param := model.Map{} for paramI, paramItem := range _func.ParamList { + paramK := paramKeys[paramI] if paramItem.Name == consts.FunctionOriReqParam { - param[paramItem.Name] = util.String(item) + param[paramItem.Name] = util.String(ret) } else { - param[paramItem.Name] = util.String(item[paramKeys[paramI]]) + if strings.HasPrefix(paramK, "'") && strings.HasSuffix(paramK, "'") { + param[paramItem.Name] = paramK[1 : len(paramK)-1] + } else { + param[paramItem.Name] = util.String(ret[paramK]) + } } } + paramList = append(paramList, param) + } + + if _func.Batch { - val, err := queryConfig.CallFunc(n.ctx, functionName, param) + param := model.Map{} + + valList, err := queryConfig.CallFunc(n.ctx, functionName, param) if err != nil { n.err = err return } - n.ret.([]model.Map)[i][k] = val + list := gconv.Interfaces(valList) + for i := range retList { + retList[i][k] = list[i] + } + + } else { + for i, param := range paramList { + val, err := queryConfig.CallFunc(n.ctx, functionName, param) + if err != nil { + n.err = err + return + } + retList[i][k] = val + } } + } else { param := model.Map{} for paramI, paramItem := range _func.ParamList { + paramK := paramKeys[paramI] if paramItem.Name == consts.FunctionOriReqParam { - param[paramItem.Name] = n.ret.(model.Map) + param[paramItem.Name] = util.String(n.ret) } else { - param[paramItem.Name] = n.ret.(model.Map)[paramKeys[paramI]] + if strings.HasPrefix(paramK, "'") && strings.HasSuffix(paramK, "'") { + param[paramItem.Name] = paramK[1 : len(paramK)-1] + } else { + param[paramItem.Name] = util.String(n.ret.(model.Map)[paramK]) + } } } diff --git a/query/query.go b/query/query.go index 2664d1c..6cf005a 100755 --- a/query/query.go +++ b/query/query.go @@ -49,7 +49,7 @@ type Query struct { // jsonFieldStyle 数据库返回的字段 JsonFieldStyle config.FieldStyle - //Config *config.Config + // Config *config.Config } func New(ctx context.Context, qc *config.QueryConfig, req model.Map) *Query { @@ -57,6 +57,7 @@ func New(ctx context.Context, qc *config.QueryConfig, req model.Map) *Query { q := &Query{ queryConfig: qc, } + q.init(ctx, req) q.NoAccessVerify = qc.NoVerify() diff --git a/query/util.go b/query/util.go index 39c7b68..b03f870 100755 --- a/query/util.go +++ b/query/util.go @@ -56,6 +56,15 @@ func hasAccess(node *Node) (hasAccess bool, condition *config.ConditionRet, err condition = config.NewConditionRet() + _req := model.Map{} + for k, v := range node.req { + if !strings.HasSuffix(k, consts.FunctionsKeySuffix) && !strings.HasSuffix(k, consts.RefKeySuffix) && !strings.HasPrefix(k, consts.CtrlKeyPrefix) { + k = node.queryContext.DbFieldStyle(node.ctx, node.Key, k) + } + _req[k] = v + } + + node.req = _req err = node.queryContext.AccessCondition(node.ctx, config.ConditionReq{ AccessName: node.Key, TableAccessRoleList: accessRoles,