Skip to content

Commit decda6d

Browse files
committed
## 0.2.0-beta9 增加错误返回而不是直接panic+action的事务开关
1 parent 8476aff commit decda6d

23 files changed

+422
-228
lines changed

action/action.go

+26-9
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@ import (
77
"github.com/glennliao/apijson-go/config"
88
"github.com/glennliao/apijson-go/consts"
99
"github.com/glennliao/apijson-go/model"
10-
"github.com/gogf/gf/v2/database/gdb"
11-
"github.com/gogf/gf/v2/errors/gerror"
12-
"github.com/gogf/gf/v2/frame/g"
10+
"github.com/glennliao/apijson-go/query"
11+
"github.com/glennliao/apijson-go/util"
1312
"github.com/gogf/gf/v2/util/gconv"
1413
)
1514

@@ -40,6 +39,9 @@ type Action struct {
4039
JsonFieldStyle config.FieldStyle
4140

4241
ActionConfig *config.ActionConfig
42+
43+
NewQuery func(ctx context.Context, req model.Map) *query.Query
44+
NewAction func(ctx context.Context, method string, req model.Map) *Action
4345
}
4446

4547
func New(ctx context.Context, actionConfig *config.ActionConfig, method string, req model.Map) *Action {
@@ -50,7 +52,7 @@ func New(ctx context.Context, actionConfig *config.ActionConfig, method string,
5052
}
5153

5254
delete(req, consts.Tag)
53-
delete(req, "version")
55+
delete(req, consts.Version)
5456

5557
a := &Action{
5658
ctx: ctx,
@@ -72,12 +74,13 @@ func (a *Action) parse() error {
7274

7375
structuresKey := key
7476
if strings.HasSuffix(key, consts.ListKeySuffix) {
75-
structuresKey = structuresKey[0 : len(structuresKey)-2]
77+
structuresKey = util.RemoveSuffix(key, consts.ListKeySuffix)
7678
}
79+
7780
structure, ok := structures[key]
7881
if !ok {
7982
if structure, ok = structures[structuresKey]; !ok { // User[]可读取User或者User[]
80-
return gerror.New("structure错误: 400, 缺少" + key)
83+
return consts.NewStructureKeyNoFoundErr(key)
8184
}
8285
}
8386

@@ -133,7 +136,21 @@ func (a *Action) Result() (model.Map, error) {
133136
}
134137
}
135138

136-
err = g.DB().Transaction(a.ctx, func(ctx context.Context, tx gdb.TX) error {
139+
transactionHandler := noTransactionHandler
140+
141+
if *a.tagRequest.Transaction == true {
142+
transactionResolver = GetTransactionHandler
143+
h := transactionResolver(a.ctx, a)
144+
if h != nil {
145+
err = consts.NewSysErr("transaction handler is nil")
146+
return nil, err
147+
}
148+
149+
transactionHandler = h
150+
151+
}
152+
153+
err = transactionHandler(a.ctx, func(ctx context.Context) error {
137154
for _, k := range a.tagRequest.ExecQueue {
138155
node := a.children[k]
139156
ret[k], err = node.execute(ctx, a.method)
@@ -162,11 +179,11 @@ func (a *Action) Result() (model.Map, error) {
162179
func checkTag(req model.Map, method string, requestCfg *config.ActionConfig) (*config.RequestConfig, error) {
163180
_tag, ok := req[consts.Tag]
164181
if !ok {
165-
return nil, gerror.New("tag 缺失")
182+
return nil, consts.ErrNoTag
166183
}
167184

168185
tag := gconv.String(_tag)
169-
version := req["version"]
186+
version := req[consts.Version]
170187

171188
request, err := requestCfg.GetRequest(tag, method, gconv.String(version))
172189
if err != nil {

action/executor.go

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package action
2+
3+
import (
4+
"context"
5+
6+
"github.com/glennliao/apijson-go/config"
7+
"github.com/glennliao/apijson-go/consts"
8+
"github.com/glennliao/apijson-go/model"
9+
"github.com/glennliao/apijson-go/query"
10+
"github.com/samber/lo"
11+
)
12+
13+
type ActionExecutorReq struct {
14+
Method string
15+
Table string
16+
Data []model.Map
17+
Where []model.Map
18+
Access *config.AccessConfig
19+
Config *config.ActionConfig
20+
NewQuery func(ctx context.Context, req model.Map) *query.Query
21+
}
22+
23+
var actionExecutorMap = map[string]ActionExecutor{}
24+
25+
func RegExecutor(name string, e ActionExecutor) {
26+
actionExecutorMap[name] = e
27+
}
28+
29+
func GetActionExecutor(name string) (ActionExecutor, error) {
30+
if name == "" {
31+
name = "default"
32+
}
33+
if v, exists := actionExecutorMap[name]; exists {
34+
return v, nil
35+
}
36+
return nil, consts.NewSysErr("action executor not found: " + name)
37+
}
38+
39+
func ActionExecutorList() []string {
40+
return lo.Keys(actionExecutorMap)
41+
}
42+
43+
type ActionExecutor interface {
44+
Do(ctx context.Context, req ActionExecutorReq) (ret model.Map, err error)
45+
}
46+
47+
// TransactionHandler 事务处理函数
48+
49+
type TransactionHandler func(ctx context.Context, action func(ctx context.Context) error) error
50+
51+
type TransactionResolver func(ctx context.Context, req *Action) TransactionHandler
52+
53+
var noTransactionHandler = func(ctx context.Context, action func(ctx context.Context) error) error {
54+
return action(ctx)
55+
}
56+
57+
var transactionResolver TransactionResolver
58+
59+
func RegTransactionResolver(r TransactionResolver) {
60+
transactionResolver = r
61+
}
62+
63+
func GetTransactionHandler(ctx context.Context, req *Action) TransactionHandler {
64+
return transactionResolver(ctx, req)
65+
}

action/node.go

+23-24
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@ import (
66
"strings"
77

88
"github.com/glennliao/apijson-go/config"
9-
"github.com/glennliao/apijson-go/config/executor"
109
"github.com/glennliao/apijson-go/consts"
1110
"github.com/glennliao/apijson-go/model"
1211
"github.com/glennliao/apijson-go/util"
13-
"github.com/gogf/gf/v2/errors/gerror"
1412
"github.com/samber/lo"
1513
)
1614

@@ -45,7 +43,6 @@ func newNode(key string, req []model.Map, structure *config.Structure, executor
4543
n.Data = []model.Map{}
4644
n.Where = []model.Map{}
4745

48-
// ?
4946
for _ = range n.req {
5047

5148
n.Data = append(n.Data, model.Map{})
@@ -54,7 +51,7 @@ func newNode(key string, req []model.Map, structure *config.Structure, executor
5451
}
5552

5653
if strings.HasSuffix(key, consts.ListKeySuffix) {
57-
n.Key = key[0 : len(key)-len(consts.ListKeySuffix)]
54+
n.Key = util.RemoveSuffix(key, consts.ListKeySuffix)
5855
n.IsList = true
5956
}
6057

@@ -65,10 +62,6 @@ func newNode(key string, req []model.Map, structure *config.Structure, executor
6562
func (n *Node) parseReq(method string) {
6663

6764
for i, item := range n.req {
68-
69-
// n.Data = append(n.Data, model.Map{})
70-
// n.Where = append(n.Where, model.Map{})
71-
7265
for key, val := range item {
7366

7467
if key == consts.Role {
@@ -84,7 +77,7 @@ func (n *Node) parseReq(method string) {
8477
case http.MethodDelete:
8578
n.Where[i][key] = val
8679
case http.MethodPut:
87-
if key == n.RowKey || key == n.RowKey+"{}" {
80+
if key == n.RowKey || key == n.RowKey+consts.OpIn {
8881
n.Where[i][key] = val
8982
} else {
9083
n.Data[i][key] = val
@@ -100,7 +93,7 @@ func (n *Node) parse(ctx context.Context, method string) error {
10093

10194
key := n.Key
10295
if strings.HasSuffix(key, consts.ListKeySuffix) {
103-
key = key[0 : len(key)-2]
96+
key = util.RemoveSuffix(key, consts.ListKeySuffix)
10497
}
10598
access, err := n.Action.ActionConfig.GetAccessConfig(key, true)
10699

@@ -179,13 +172,13 @@ func (n *Node) checkAccess(ctx context.Context, method string, accessRoles []str
179172
}
180173

181174
if role == consts.DENY {
182-
return gerror.Newf("deny node: %s with %s", n.Key, n.Role)
175+
return consts.NewDenyErr(n.Key, n.Role)
183176
}
184177

185178
n.Role = role
186179

187180
if !lo.Contains(accessRoles, role) {
188-
return gerror.Newf("node not access: %s with %s", n.Key, n.Role)
181+
return consts.NewNoAccessErr(n.Key, n.Role)
189182
}
190183

191184
return nil
@@ -231,26 +224,26 @@ func (n *Node) checkReq() error {
231224
// must
232225
for _, key := range n.structure.Must {
233226
if _, exists := item[key]; !exists {
234-
return gerror.New("structure错误: 400, 缺少" + n.Key + "." + key)
227+
return consts.NewStructureKeyNoFoundErr(n.Key + "." + key)
235228
}
236229
}
237230

238231
// refuse
239232
if len(n.structure.Refuse) > 0 && n.structure.Refuse[0] == "!" {
240233
if len(n.structure.Must) == 0 {
241-
return gerror.New("structure错误: 400, REFUSE为!时必须指定MUST" + n.Key)
234+
return consts.NewValidStructureErr("REFUSE为!时必须指定MUST:" + n.Key)
242235
}
243236

244237
for key, _ := range item {
245238
if !lo.Contains(n.structure.Must, key) {
246-
return gerror.New("structure错误: 400, 不能包含" + n.Key + "." + key)
239+
return consts.NewValidStructureErr("不能包含:" + n.Key + "." + key)
247240
}
248241
}
249242

250243
} else {
251244
for _, key := range n.structure.Refuse {
252245
if _, exists := item[key]; exists {
253-
return gerror.New("structure错误: 400, 不能包含" + n.Key + "." + key)
246+
return consts.NewValidStructureErr("不能包含:" + n.Key + "." + key)
254247
}
255248
}
256249
}
@@ -375,16 +368,22 @@ func (n *Node) do(ctx context.Context, method string) (ret model.Map, err error)
375368
case http.MethodDelete:
376369

377370
default:
378-
return nil, gerror.New("undefined method:" + method)
371+
return nil, consts.NewMethodNotSupportErr(method)
372+
}
373+
374+
executor, err := GetActionExecutor(n.executor)
375+
if err != nil {
376+
return nil, err
379377
}
380378

381-
ret, err = executor.GetActionExecutor(n.executor).Do(ctx, executor.ActionExecutorReq{
382-
Method: method,
383-
Table: n.tableName,
384-
Data: n.Data,
385-
Where: n.Where,
386-
Access: access,
387-
Config: n.Action.ActionConfig,
379+
ret, err = executor.Do(ctx, ActionExecutorReq{
380+
Method: method,
381+
Table: n.tableName,
382+
Data: n.Data,
383+
Where: n.Where,
384+
Access: access,
385+
Config: n.Action.ActionConfig,
386+
NewQuery: n.Action.NewQuery,
388387
})
389388

390389
if err != nil {

apijson.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package apijson
22

33
import (
44
"context"
5+
56
"github.com/glennliao/apijson-go/action"
67
"github.com/glennliao/apijson-go/config"
78
"github.com/glennliao/apijson-go/model"
@@ -20,9 +21,6 @@ type ApiJson struct {
2021

2122
var DefaultApiJson = New()
2223

23-
type App struct {
24-
}
25-
2624
func New() *ApiJson {
2725
a := &ApiJson{}
2826
a.config = config.New()
@@ -76,5 +74,7 @@ func (a *ApiJson) NewAction(ctx context.Context, method string, req model.Map) *
7674
act.DbFieldStyle = a.config.DbFieldStyle
7775
act.JsonFieldStyle = a.config.JsonFieldStyle
7876

77+
act.NewQuery = a.NewQuery
78+
7979
return act
8080
}

config/access_config.go

+11-10
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package config
22

33
import (
4-
"github.com/gogf/gf/v2/errors/gerror"
4+
"net/http"
5+
6+
"github.com/glennliao/apijson-go/consts"
57
"github.com/gogf/gf/v2/os/gtime"
68
"github.com/samber/lo"
7-
"net/http"
89
)
910

1011
type FieldsGetValue struct {
@@ -56,28 +57,28 @@ func (a *AccessConfig) GetFieldsGetInByRole(role string) map[string][]string {
5657
return inFieldsMap
5758
}
5859

59-
func (a *Access) GetAccess(tableAlias string, noVerify bool) (*AccessConfig, error) {
60-
access, ok := a.accessConfigMap[tableAlias]
60+
func (a *Access) GetAccess(accessName string, noVerify bool) (*AccessConfig, error) {
61+
access, ok := a.accessConfigMap[accessName]
6162

6263
if !ok {
6364
if noVerify {
6465
return &AccessConfig{
6566
Debug: 0,
66-
Name: tableAlias,
67-
Alias: tableAlias,
67+
Name: accessName,
68+
Alias: accessName,
6869
}, nil
6970
}
70-
return nil, gerror.Newf("access[%s]: 404", tableAlias)
71+
return nil, consts.NewAccessNoFoundErr(accessName)
7172
}
7273

7374
return &access, nil
7475
}
7576

76-
func (a *Access) GetAccessRole(table string, method string) ([]string, string, error) {
77-
access, ok := a.accessConfigMap[table]
77+
func (a *Access) GetAccessRole(accessName string, method string) ([]string, string, error) {
78+
access, ok := a.accessConfigMap[accessName]
7879

7980
if !ok {
80-
return nil, "", gerror.Newf("access[%s]: 404", table)
81+
return nil, "", consts.NewAccessNoFoundErr(accessName)
8182
}
8283

8384
switch method {

0 commit comments

Comments
 (0)