正在显示
14 个修改的文件
包含
1026 行增加
和
94 行删除
| @@ -116,10 +116,17 @@ func (querySetService *QuerySetService) DependencyGraph(ctx *domain.Context, dep | @@ -116,10 +116,17 @@ func (querySetService *QuerySetService) DependencyGraph(ctx *domain.Context, dep | ||
| 116 | defer func() { | 116 | defer func() { |
| 117 | transactionContext.RollbackTransaction() | 117 | transactionContext.RollbackTransaction() |
| 118 | }() | 118 | }() |
| 119 | + | ||
| 120 | + svr, _ := factory.FastQuerySetServices(transactionContext) | ||
| 121 | + data, err := svr.DependencyGraph(ctx, dependencyGraphQuery.QuerySetId) | ||
| 122 | + if err != nil { | ||
| 123 | + return nil, factory.FastError(err) | ||
| 124 | + } | ||
| 125 | + | ||
| 119 | if err := transactionContext.CommitTransaction(); err != nil { | 126 | if err := transactionContext.CommitTransaction(); err != nil { |
| 120 | return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error()) | 127 | return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error()) |
| 121 | } | 128 | } |
| 122 | - return nil, nil | 129 | + return data, nil |
| 123 | } | 130 | } |
| 124 | 131 | ||
| 125 | // 返回查询集合服务 | 132 | // 返回查询集合服务 |
| @@ -27,6 +27,18 @@ type QueryComponent struct { | @@ -27,6 +27,18 @@ type QueryComponent struct { | ||
| 27 | SelectFromTables []QueryComponentTable `json:"-"` //selectTables | 27 | SelectFromTables []QueryComponentTable `json:"-"` //selectTables |
| 28 | Description string `json:"description"` | 28 | Description string `json:"description"` |
| 29 | } | 29 | } |
| 30 | + | ||
| 31 | +func (qc QueryComponent) AllSelectExpr() []SelectExpr { | ||
| 32 | + var res = make([]SelectExpr, 0) | ||
| 33 | + for _, s := range qc.Selects { | ||
| 34 | + res = append(res, s.SelectExpr) | ||
| 35 | + if len(s.SubSelects) > 0 { | ||
| 36 | + res = append(res, s.SubSelects...) | ||
| 37 | + } | ||
| 38 | + } | ||
| 39 | + return res | ||
| 40 | +} | ||
| 41 | + | ||
| 30 | type ConditionExpr struct { // 条件表达式 | 42 | type ConditionExpr struct { // 条件表达式 |
| 31 | Id string `json:"id"` | 43 | Id string `json:"id"` |
| 32 | FieldLeft FieldExpr `json:"fieldLeft"` | 44 | FieldLeft FieldExpr `json:"fieldLeft"` |
| @@ -35,6 +47,16 @@ type ConditionExpr struct { // 条件表达式 | @@ -35,6 +47,16 @@ type ConditionExpr struct { // 条件表达式 | ||
| 35 | AndOr string `json:"andOr"` // and or | 47 | AndOr string `json:"andOr"` // and or |
| 36 | } | 48 | } |
| 37 | 49 | ||
| 50 | +func (c ConditionExpr) Equal(compare ConditionExpr) bool { | ||
| 51 | + return c.OperatorSymbol == compare.OperatorSymbol && | ||
| 52 | + c.FieldLeft.ExprSql == compare.FieldLeft.ExprSql && | ||
| 53 | + c.FieldRight.ExprSql == compare.FieldRight.ExprSql | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | +func (c ConditionExpr) ExprHuman() string { | ||
| 57 | + return c.FieldLeft.ExprHuman + c.OperatorSymbol + c.FieldRight.ExprHuman | ||
| 58 | +} | ||
| 59 | + | ||
| 38 | type SelectExpr struct { // 查询表达式 | 60 | type SelectExpr struct { // 查询表达式 |
| 39 | Id string `json:"id"` | 61 | Id string `json:"id"` |
| 40 | FieldLeft FieldExpr `json:"fieldLeft"` | 62 | FieldLeft FieldExpr `json:"fieldLeft"` |
| @@ -42,6 +64,16 @@ type SelectExpr struct { // 查询表达式 | @@ -42,6 +64,16 @@ type SelectExpr struct { // 查询表达式 | ||
| 42 | Type string `json:"type"` // 1.拆分 2.拆方赋值 3.正常赋值 | 64 | Type string `json:"type"` // 1.拆分 2.拆方赋值 3.正常赋值 |
| 43 | //SubGroup []SelectExpr `json:"subGroup,omitempty"` | 65 | //SubGroup []SelectExpr `json:"subGroup,omitempty"` |
| 44 | } | 66 | } |
| 67 | + | ||
| 68 | +func (s SelectExpr) Equal(compare SelectExpr) bool { | ||
| 69 | + return s.FieldLeft.ExprSql == compare.FieldLeft.ExprSql && | ||
| 70 | + s.FieldRight.ExprSql == compare.FieldRight.ExprSql | ||
| 71 | +} | ||
| 72 | + | ||
| 73 | +func (c SelectExpr) ExprHuman() string { | ||
| 74 | + return c.FieldLeft.ExprHuman + "=" + c.FieldRight.ExprHuman | ||
| 75 | +} | ||
| 76 | + | ||
| 45 | type SelectExprGroup struct { // 查询表达式 | 77 | type SelectExprGroup struct { // 查询表达式 |
| 46 | SelectExpr | 78 | SelectExpr |
| 47 | SubSelects []SelectExpr `json:"subSelects,omitempty"` | 79 | SubSelects []SelectExpr `json:"subSelects,omitempty"` |
| @@ -50,7 +82,7 @@ type SelectExprGroup struct { // 查询表达式 | @@ -50,7 +82,7 @@ type SelectExprGroup struct { // 查询表达式 | ||
| 50 | type FieldExpr struct { | 82 | type FieldExpr struct { |
| 51 | //LabelColumns []LabelColumn `json:"labelColumns"` | 83 | //LabelColumns []LabelColumn `json:"labelColumns"` |
| 52 | TableFields []TableField `json:"tableFields"` | 84 | TableFields []TableField `json:"tableFields"` |
| 53 | - //ExprHuman string `json:"exprHuman"` | 85 | + ExprHuman string `json:"exprHuman"` |
| 54 | ExprSql string `json:"exprSql"` | 86 | ExprSql string `json:"exprSql"` |
| 55 | } | 87 | } |
| 56 | 88 | ||
| @@ -101,16 +133,32 @@ func NewQueryComponentTable(t *Table) QueryComponentTable { | @@ -101,16 +133,32 @@ func NewQueryComponentTable(t *Table) QueryComponentTable { | ||
| 101 | } | 133 | } |
| 102 | } | 134 | } |
| 103 | 135 | ||
| 104 | -func ConditionsToMapById(items []*ConditionExpr) map[string]*ConditionExpr { | ||
| 105 | - var res = make(map[string]*ConditionExpr) | 136 | +func QueryComponentsToMapById(items []*QueryComponent) map[string]*QueryComponent { |
| 137 | + var res = make(map[string]*QueryComponent) | ||
| 138 | + for i := range items { | ||
| 139 | + res[items[i].Id] = items[i] | ||
| 140 | + } | ||
| 141 | + return res | ||
| 142 | +} | ||
| 143 | + | ||
| 144 | +func ConditionsToMapById(items []ConditionExpr) map[string]ConditionExpr { | ||
| 145 | + var res = make(map[string]ConditionExpr) | ||
| 146 | + for i := range items { | ||
| 147 | + res[items[i].Id] = items[i] | ||
| 148 | + } | ||
| 149 | + return res | ||
| 150 | +} | ||
| 151 | + | ||
| 152 | +func SelectsToMapById(items []SelectExprGroup) map[string]SelectExprGroup { | ||
| 153 | + var res = make(map[string]SelectExprGroup) | ||
| 106 | for i := range items { | 154 | for i := range items { |
| 107 | res[items[i].Id] = items[i] | 155 | res[items[i].Id] = items[i] |
| 108 | } | 156 | } |
| 109 | return res | 157 | return res |
| 110 | } | 158 | } |
| 111 | 159 | ||
| 112 | -func SelectsToMapById(items []*SelectExprGroup) map[string]*SelectExprGroup { | ||
| 113 | - var res = make(map[string]*SelectExprGroup) | 160 | +func SelectsExprToMapById(items []SelectExpr) map[string]SelectExpr { |
| 161 | + var res = make(map[string]SelectExpr) | ||
| 114 | for i := range items { | 162 | for i := range items { |
| 115 | res[items[i].Id] = items[i] | 163 | res[items[i].Id] = items[i] |
| 116 | } | 164 | } |
| @@ -113,3 +113,10 @@ func (t *Table) MatchField(field *Field) (*Field, bool) { | @@ -113,3 +113,10 @@ func (t *Table) MatchField(field *Field) (*Field, bool) { | ||
| 113 | } | 113 | } |
| 114 | return nil, false | 114 | return nil, false |
| 115 | } | 115 | } |
| 116 | + | ||
| 117 | +func (t *Table) DependencyTables() []int { | ||
| 118 | + if t.TableInfo == nil { | ||
| 119 | + return []int{} | ||
| 120 | + } | ||
| 121 | + return t.TableInfo.DependencyTables | ||
| 122 | +} |
| @@ -3,6 +3,7 @@ package dao | @@ -3,6 +3,7 @@ package dao | ||
| 3 | import ( | 3 | import ( |
| 4 | pgTransaction "github.com/linmadan/egglib-go/transaction/pg" | 4 | pgTransaction "github.com/linmadan/egglib-go/transaction/pg" |
| 5 | "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain" | 5 | "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain" |
| 6 | + "time" | ||
| 6 | ) | 7 | ) |
| 7 | 8 | ||
| 8 | func TableDelete(ptr *pgTransaction.TransactionContext, tableId int, tableType domain.TableType) error { | 9 | func TableDelete(ptr *pgTransaction.TransactionContext, tableId int, tableType domain.TableType) error { |
| @@ -10,3 +11,9 @@ func TableDelete(ptr *pgTransaction.TransactionContext, tableId int, tableType d | @@ -10,3 +11,9 @@ func TableDelete(ptr *pgTransaction.TransactionContext, tableId int, tableType d | ||
| 10 | _, err := ptr.PgTx.Exec(sql, tableId, tableType.ToString()) | 11 | _, err := ptr.PgTx.Exec(sql, tableId, tableType.ToString()) |
| 11 | return err | 12 | return err |
| 12 | } | 13 | } |
| 14 | + | ||
| 15 | +func TableSoftDelete(ptr *pgTransaction.TransactionContext, tableId int, tableType domain.TableType) error { | ||
| 16 | + sql := "update metadata.tables set deleted_at=? where table_id = ? and table_type = ?" | ||
| 17 | + _, err := ptr.PgTx.Exec(sql, time.Now(), tableId, tableType.ToString()) | ||
| 18 | + return err | ||
| 19 | +} |
| @@ -60,6 +60,20 @@ func (ptr *PGLogService) Log(logType domain.LogType, sourceId int, logEntry Log) | @@ -60,6 +60,20 @@ func (ptr *PGLogService) Log(logType domain.LogType, sourceId int, logEntry Log) | ||
| 60 | return err | 60 | return err |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | +type FastSourceLog struct { | ||
| 64 | + LogType domain.LogType | ||
| 65 | + SourceId int | ||
| 66 | + LogEntry Log | ||
| 67 | +} | ||
| 68 | + | ||
| 69 | +func NewFastSourceLog(logType domain.LogType, sourceId int, logEntry Log) FastSourceLog { | ||
| 70 | + return FastSourceLog{ | ||
| 71 | + LogType: logType, | ||
| 72 | + SourceId: sourceId, | ||
| 73 | + LogEntry: logEntry, | ||
| 74 | + } | ||
| 75 | +} | ||
| 76 | + | ||
| 63 | func (ptr *PGLogService) NewLogEntry() domain.LogEntry { | 77 | func (ptr *PGLogService) NewLogEntry() domain.LogEntry { |
| 64 | return domain.LogEntry{} | 78 | return domain.LogEntry{} |
| 65 | } | 79 | } |
| @@ -396,3 +410,53 @@ func (l *CopyQuerySetLog) Content() string { | @@ -396,3 +410,53 @@ func (l *CopyQuerySetLog) Content() string { | ||
| 396 | return fmt.Sprintf(`%s"%s"复制为%s"%s""`, domain.EnumsDescription(domain.ObjectTypeMap, l.From.Type), l.From.Name, | 410 | return fmt.Sprintf(`%s"%s"复制为%s"%s""`, domain.EnumsDescription(domain.ObjectTypeMap, l.From.Type), l.From.Name, |
| 397 | domain.EnumsDescription(domain.ObjectTypeMap, l.To.Type), l.To.Name) | 411 | domain.EnumsDescription(domain.ObjectTypeMap, l.To.Type), l.To.Name) |
| 398 | } | 412 | } |
| 413 | + | ||
| 414 | +type EditQuerySetConditionLog struct { | ||
| 415 | + domain.LogEntry | ||
| 416 | + OperationType domain.OperationType | ||
| 417 | + Sources []string | ||
| 418 | + SourceTargets [][]string | ||
| 419 | +} | ||
| 420 | + | ||
| 421 | +func (l *EditQuerySetConditionLog) OperateType() string { | ||
| 422 | + return l.OperationType.ToString() | ||
| 423 | +} | ||
| 424 | + | ||
| 425 | +func (l *EditQuerySetConditionLog) Content() string { | ||
| 426 | + if l.OperationType == domain.AddSetCondition { | ||
| 427 | + return fmt.Sprintf("新增条件:%v", strings.Join(l.Sources, ";")) | ||
| 428 | + } | ||
| 429 | + if l.OperationType == domain.EditSetCondition { | ||
| 430 | + items := make([]string, 0) | ||
| 431 | + for _, sourceTarget := range l.SourceTargets { | ||
| 432 | + items = append(items, fmt.Sprintf("%v 修改为 %v", sourceTarget[0], sourceTarget[1])) | ||
| 433 | + } | ||
| 434 | + return fmt.Sprintf("编辑条件:%v", strings.Join(items, ";")) | ||
| 435 | + } | ||
| 436 | + return "删除条件:" + strings.Join(l.Sources, ";") | ||
| 437 | +} | ||
| 438 | + | ||
| 439 | +type EditSelectConditionLog struct { | ||
| 440 | + domain.LogEntry | ||
| 441 | + OperationType domain.OperationType // 编辑类型 1:add 2.edit 3.delete | ||
| 442 | + Sources []string | ||
| 443 | + SourceTargets [][]string | ||
| 444 | +} | ||
| 445 | + | ||
| 446 | +func (l *EditSelectConditionLog) OperateType() string { | ||
| 447 | + return l.OperationType.ToString() | ||
| 448 | +} | ||
| 449 | + | ||
| 450 | +func (l *EditSelectConditionLog) Content() string { | ||
| 451 | + if l.OperationType == domain.AddSelectCondition { | ||
| 452 | + return fmt.Sprintf("新增拆分规则:%v", strings.Join(l.Sources, ";")) | ||
| 453 | + } | ||
| 454 | + if l.OperationType == domain.EditSelectCondition { | ||
| 455 | + items := make([]string, 0) | ||
| 456 | + for _, sourceTarget := range l.SourceTargets { | ||
| 457 | + items = append(items, fmt.Sprintf("%v 修改为 %v", sourceTarget[0], sourceTarget[1])) | ||
| 458 | + } | ||
| 459 | + return fmt.Sprintf("编辑拆分规则:%v", strings.Join(items, ";")) | ||
| 460 | + } | ||
| 461 | + return "删除拆分规则:" + strings.Join(l.Sources, ";") | ||
| 462 | +} |
| @@ -91,14 +91,16 @@ func (ptr *QuerySetService) Update(ctx *domain.Context, querySetId int, queryCom | @@ -91,14 +91,16 @@ func (ptr *QuerySetService) Update(ctx *domain.Context, querySetId int, queryCom | ||
| 91 | if err != nil { | 91 | if err != nil { |
| 92 | return err | 92 | return err |
| 93 | } | 93 | } |
| 94 | - // 调用底层的组装sql | ||
| 95 | 94 | ||
| 96 | // 生成Table | 95 | // 生成Table |
| 97 | masterTable := queryComponents[0].MasterTable | 96 | masterTable := queryComponents[0].MasterTable |
| 98 | - table, err := ptr.CreateOrReFreshQuerySetTable(ctx, qs, masterTable, queryComponents) | 97 | + table, err := ptr.CreateOrUpdateQuerySetTable(ctx, qs, masterTable, queryComponents) |
| 99 | if err != nil { | 98 | if err != nil { |
| 100 | return err | 99 | return err |
| 101 | } | 100 | } |
| 101 | + | ||
| 102 | + // 调用底层的组装sql | ||
| 103 | + | ||
| 102 | // 生成日志 | 104 | // 生成日志 |
| 103 | if err = ptr.UpdateQuerySetLog(ctx, qs, queryComponents); err != nil { | 105 | if err = ptr.UpdateQuerySetLog(ctx, qs, queryComponents); err != nil { |
| 104 | return err | 106 | return err |
| @@ -113,14 +115,196 @@ func (ptr *QuerySetService) Update(ctx *domain.Context, querySetId int, queryCom | @@ -113,14 +115,196 @@ func (ptr *QuerySetService) Update(ctx *domain.Context, querySetId int, queryCom | ||
| 113 | } | 115 | } |
| 114 | 116 | ||
| 115 | func (ptr *QuerySetService) UpdateQuerySetLog(ctx *domain.Context, querySet *domain.QuerySet, queryComponents []*domain.QueryComponent) error { | 117 | func (ptr *QuerySetService) UpdateQuerySetLog(ctx *domain.Context, querySet *domain.QuerySet, queryComponents []*domain.QueryComponent) error { |
| 118 | + var res = make([]FastSourceLog, 0) | ||
| 119 | + if logs := conditionsEditLog(ctx, querySet, queryComponents); len(logs) > 0 { | ||
| 120 | + res = append(res, logs...) | ||
| 121 | + } | ||
| 122 | + if logs := selectsEditLog(ctx, querySet, queryComponents); len(logs) > 0 { | ||
| 123 | + res = append(res, logs...) | ||
| 124 | + } | ||
| 125 | + | ||
| 126 | + for _, l := range res { | ||
| 127 | + FastLog(ptr.transactionContext, l.LogType, l.SourceId, l.LogEntry) | ||
| 128 | + } | ||
| 116 | return nil | 129 | return nil |
| 117 | } | 130 | } |
| 118 | 131 | ||
| 119 | -func (ptr *QuerySetService) CreateOrReFreshQuerySetTable(ctx *domain.Context, querySet *domain.QuerySet, masterTable domain.QueryComponentTable, queryComponents []*domain.QueryComponent) (*domain.Table, error) { | 132 | +func conditionsEditLog(ctx *domain.Context, querySet *domain.QuerySet, queryComponents []*domain.QueryComponent) []FastSourceLog { |
| 133 | + var res = make([]FastSourceLog, 0) | ||
| 134 | + oldQCs := domain.QueryComponentsToMapById(querySet.QueryComponents) | ||
| 135 | + sourceId := querySet.QuerySetId | ||
| 136 | + entry := domain.NewLogEntry(querySet.Name, querySet.Type, domain.UnKnown, ctx) | ||
| 137 | + | ||
| 138 | + // 新增条件判断 | ||
| 139 | + addExprList := make([]string, 0) | ||
| 140 | + editExprList := make([][]string, 0) | ||
| 141 | + deleteExprList := make([]string, 0) | ||
| 142 | + | ||
| 143 | + for _, item := range queryComponents { | ||
| 144 | + if len(item.Id) == 0 { | ||
| 145 | + continue | ||
| 146 | + } | ||
| 147 | + oldItem, ok := oldQCs[item.Id] | ||
| 148 | + if !ok { | ||
| 149 | + continue | ||
| 150 | + } | ||
| 151 | + | ||
| 152 | + // 条件判断编辑情况 | ||
| 153 | + mapOldItems := domain.ConditionsToMapById(oldItem.Conditions) | ||
| 154 | + mapNewItems := domain.ConditionsToMapById(item.Conditions) | ||
| 155 | + for _, add := range item.Conditions { | ||
| 156 | + if len(add.Id) != 0 { | ||
| 157 | + continue | ||
| 158 | + } | ||
| 159 | + addExprList = append(addExprList, add.ExprHuman()) | ||
| 160 | + } | ||
| 161 | + | ||
| 162 | + // 编辑 | ||
| 163 | + for _, add := range item.Conditions { | ||
| 164 | + if len(add.Id) == 0 { | ||
| 165 | + continue | ||
| 166 | + } | ||
| 167 | + v, ok := mapOldItems[add.Id] | ||
| 168 | + if !ok { | ||
| 169 | + continue | ||
| 170 | + } | ||
| 171 | + if add.Equal(v) { | ||
| 172 | + continue | ||
| 173 | + } | ||
| 174 | + editExprList = append(editExprList, []string{v.ExprHuman(), add.ExprHuman()}) | ||
| 175 | + } | ||
| 176 | + | ||
| 177 | + // 删除 | ||
| 178 | + for _, item := range oldItem.Conditions { | ||
| 179 | + if len(item.Id) == 0 { | ||
| 180 | + continue | ||
| 181 | + } | ||
| 182 | + _, ok := mapNewItems[item.Id] | ||
| 183 | + if ok { | ||
| 184 | + continue | ||
| 185 | + } | ||
| 186 | + deleteExprList = append(deleteExprList, item.ExprHuman()) | ||
| 187 | + } | ||
| 188 | + | ||
| 189 | + } | ||
| 190 | + | ||
| 191 | + if len(addExprList) > 0 { | ||
| 192 | + res = append(res, NewFastSourceLog(domain.QuerySetLog, sourceId, &EditQuerySetConditionLog{ | ||
| 193 | + LogEntry: entry, | ||
| 194 | + OperationType: domain.AddSetCondition, | ||
| 195 | + Sources: addExprList, | ||
| 196 | + })) | ||
| 197 | + } | ||
| 198 | + | ||
| 199 | + if len(editExprList) > 0 { | ||
| 200 | + res = append(res, NewFastSourceLog(domain.QuerySetLog, sourceId, &EditQuerySetConditionLog{ | ||
| 201 | + LogEntry: entry, | ||
| 202 | + OperationType: domain.EditSetCondition, | ||
| 203 | + SourceTargets: editExprList, | ||
| 204 | + })) | ||
| 205 | + } | ||
| 206 | + | ||
| 207 | + if len(deleteExprList) > 0 { | ||
| 208 | + res = append(res, NewFastSourceLog(domain.QuerySetLog, sourceId, &EditQuerySetConditionLog{ | ||
| 209 | + LogEntry: entry, | ||
| 210 | + OperationType: domain.AddSetCondition, | ||
| 211 | + Sources: deleteExprList, | ||
| 212 | + })) | ||
| 213 | + } | ||
| 214 | + return res | ||
| 215 | +} | ||
| 216 | + | ||
| 217 | +func selectsEditLog(ctx *domain.Context, querySet *domain.QuerySet, queryComponents []*domain.QueryComponent) []FastSourceLog { | ||
| 218 | + var res = make([]FastSourceLog, 0) | ||
| 219 | + oldQCs := domain.QueryComponentsToMapById(querySet.QueryComponents) | ||
| 220 | + //newQCs := domain.QueryComponentsToMapById(queryComponents) | ||
| 221 | + sourceId := querySet.QuerySetId | ||
| 222 | + entry := domain.NewLogEntry(querySet.Name, querySet.Type, domain.UnKnown, ctx) | ||
| 223 | + | ||
| 224 | + addExprList := make([]string, 0) | ||
| 225 | + editExprList := make([][]string, 0) | ||
| 226 | + deleteExprList := make([]string, 0) | ||
| 227 | + for _, item := range queryComponents { | ||
| 228 | + if len(item.Id) == 0 { | ||
| 229 | + continue | ||
| 230 | + } | ||
| 231 | + oldItem, ok := oldQCs[item.Id] | ||
| 232 | + if !ok { | ||
| 233 | + continue | ||
| 234 | + } | ||
| 235 | + | ||
| 236 | + // 条件判断编辑情况 | ||
| 237 | + allOldSelectExpr := oldItem.AllSelectExpr() | ||
| 238 | + mapOldConditions := domain.SelectsExprToMapById(allOldSelectExpr) | ||
| 239 | + allNewSelectExpr := item.AllSelectExpr() | ||
| 240 | + mapNewConditions := domain.SelectsExprToMapById(allNewSelectExpr) | ||
| 241 | + // 新增条件判断 | ||
| 242 | + for _, item := range allNewSelectExpr { | ||
| 243 | + if len(item.Id) != 0 { | ||
| 244 | + continue | ||
| 245 | + } | ||
| 246 | + addExprList = append(addExprList, item.ExprHuman()) | ||
| 247 | + } | ||
| 248 | + // 编辑 | ||
| 249 | + for _, item := range allNewSelectExpr { | ||
| 250 | + if len(item.Id) == 0 { | ||
| 251 | + continue | ||
| 252 | + } | ||
| 253 | + v, ok := mapOldConditions[item.Id] | ||
| 254 | + if !ok { | ||
| 255 | + continue | ||
| 256 | + } | ||
| 257 | + if item.Equal(v) { | ||
| 258 | + continue | ||
| 259 | + } | ||
| 260 | + editExprList = append(editExprList, []string{v.ExprHuman(), item.ExprHuman()}) | ||
| 261 | + } | ||
| 262 | + | ||
| 263 | + // 删除 | ||
| 264 | + for _, item := range allOldSelectExpr { | ||
| 265 | + if len(item.Id) == 0 { | ||
| 266 | + continue | ||
| 267 | + } | ||
| 268 | + _, ok := mapNewConditions[item.Id] | ||
| 269 | + if ok { | ||
| 270 | + continue | ||
| 271 | + } | ||
| 272 | + deleteExprList = append(deleteExprList, item.ExprHuman()) | ||
| 273 | + } | ||
| 274 | + } | ||
| 275 | + | ||
| 276 | + if len(addExprList) > 0 { | ||
| 277 | + res = append(res, NewFastSourceLog(domain.QuerySetLog, sourceId, &EditSelectConditionLog{ | ||
| 278 | + LogEntry: entry, | ||
| 279 | + OperationType: domain.AddSelectCondition, | ||
| 280 | + Sources: addExprList, | ||
| 281 | + })) | ||
| 282 | + } | ||
| 283 | + | ||
| 284 | + if len(editExprList) > 0 { | ||
| 285 | + res = append(res, NewFastSourceLog(domain.QuerySetLog, sourceId, &EditSelectConditionLog{ | ||
| 286 | + LogEntry: entry, | ||
| 287 | + OperationType: domain.EditSelectCondition, | ||
| 288 | + SourceTargets: editExprList, | ||
| 289 | + })) | ||
| 290 | + } | ||
| 291 | + | ||
| 292 | + if len(deleteExprList) > 0 { | ||
| 293 | + res = append(res, NewFastSourceLog(domain.QuerySetLog, sourceId, &EditSelectConditionLog{ | ||
| 294 | + LogEntry: entry, | ||
| 295 | + OperationType: domain.DeleteSelectCondition, | ||
| 296 | + Sources: deleteExprList, | ||
| 297 | + })) | ||
| 298 | + } | ||
| 299 | + return res | ||
| 300 | +} | ||
| 301 | + | ||
| 302 | +func (ptr *QuerySetService) CreateOrUpdateQuerySetTable(ctx *domain.Context, querySet *domain.QuerySet, masterTable domain.QueryComponentTable, queryComponents []*domain.QueryComponent) (*domain.Table, error) { | ||
| 120 | var ( | 303 | var ( |
| 121 | err error | 304 | err error |
| 122 | foundMasterTable *domain.Table | 305 | foundMasterTable *domain.Table |
| 123 | ) | 306 | ) |
| 307 | + dependencyTables := querySet.GetDependencyTables(queryComponents) | ||
| 124 | tableRepository, _ := repository.NewTableRepository(ptr.transactionContext) | 308 | tableRepository, _ := repository.NewTableRepository(ptr.transactionContext) |
| 125 | foundMasterTable, err = tableRepository.FindOne(map[string]interface{}{"context": ctx, "tableId": masterTable.TableId}) | 309 | foundMasterTable, err = tableRepository.FindOne(map[string]interface{}{"context": ctx, "tableId": masterTable.TableId}) |
| 126 | if err != nil { | 310 | if err != nil { |
| @@ -136,8 +320,24 @@ func (ptr *QuerySetService) CreateOrReFreshQuerySetTable(ctx *domain.Context, qu | @@ -136,8 +320,24 @@ func (ptr *QuerySetService) CreateOrReFreshQuerySetTable(ctx *domain.Context, qu | ||
| 136 | table.DataFields = masterTable.Fields | 320 | table.DataFields = masterTable.Fields |
| 137 | table.UpdatedAt = time.Now() | 321 | table.UpdatedAt = time.Now() |
| 138 | } | 322 | } |
| 323 | + | ||
| 324 | + // 循环依赖判断 | ||
| 325 | + tableDependencyService, _ := NewTableDependencyService(ptr.transactionContext) | ||
| 326 | + if len(dependencyTables) > 0 { | ||
| 327 | + _, tables, err := tableRepository.Find(map[string]interface{}{"context": ctx, "tableIds": dependencyTables, "tableTypes": []string{domain.SchemaTable.ToString(), domain.SubProcessTable.ToString()}}) | ||
| 328 | + if err != nil { | ||
| 329 | + return nil, err | ||
| 330 | + } | ||
| 331 | + if len(tables) > 0 { | ||
| 332 | + tree := tableDependencyService.TableDependTree(tables, querySet.QuerySetInfo.BindTableId) | ||
| 333 | + if tableDependencyService.Detect(ctx, tree.EdgesArray()) { | ||
| 334 | + return nil, NewCircleDependError(tableDependencyService.CircleTable(), querySet) | ||
| 335 | + } | ||
| 336 | + } | ||
| 337 | + } | ||
| 338 | + | ||
| 139 | table.TableInfo.ApplyOnModule = domain.ModuleAll | 339 | table.TableInfo.ApplyOnModule = domain.ModuleAll |
| 140 | - table.TableInfo.DependencyTables = querySet.GetDependencyTables(queryComponents) | 340 | + table.TableInfo.DependencyTables = dependencyTables |
| 141 | table, err = tableRepository.Save(table) | 341 | table, err = tableRepository.Save(table) |
| 142 | if err != nil { | 342 | if err != nil { |
| 143 | return nil, err | 343 | return nil, err |
| @@ -257,10 +457,19 @@ func (ptr *QuerySetService) Delete(ctx *domain.Context, querySetId int) error { | @@ -257,10 +457,19 @@ func (ptr *QuerySetService) Delete(ctx *domain.Context, querySetId int) error { | ||
| 257 | return err | 457 | return err |
| 258 | } | 458 | } |
| 259 | querySetRepository, _ := repository.NewQuerySetRepository(ptr.transactionContext) | 459 | querySetRepository, _ := repository.NewQuerySetRepository(ptr.transactionContext) |
| 460 | + tableDependencyService, _ := NewTableDependencyService(ptr.transactionContext) | ||
| 260 | for i := range querySets { | 461 | for i := range querySets { |
| 261 | if _, err := querySetRepository.Remove(querySets[i]); err != nil { | 462 | if _, err := querySetRepository.Remove(querySets[i]); err != nil { |
| 262 | return err | 463 | return err |
| 263 | } | 464 | } |
| 465 | + if querySets[i].QuerySetInfo.BindTableId > 0 { | ||
| 466 | + if err := tableDependencyService.HasDependencyError(ctx, querySets[i].QuerySetInfo.BindTableId); err != nil { | ||
| 467 | + return err | ||
| 468 | + } | ||
| 469 | + if err := dao.TableSoftDelete(ptr.transactionContext, querySets[i].QuerySetInfo.BindTableId, domain.TableType(querySets[i].Type)); err != nil { | ||
| 470 | + return err | ||
| 471 | + } | ||
| 472 | + } | ||
| 264 | } | 473 | } |
| 265 | // 日志 | 474 | // 日志 |
| 266 | if len(querySets) > 0 { | 475 | if len(querySets) > 0 { |
| @@ -353,7 +562,7 @@ func insertQuerySetsByIndex(list []*domain.QuerySet, item *domain.QuerySet, inde | @@ -353,7 +562,7 @@ func insertQuerySetsByIndex(list []*domain.QuerySet, item *domain.QuerySet, inde | ||
| 353 | func (ptr *QuerySetService) DependencyGraph(ctx *domain.Context, querySetId int) (interface{}, error) { | 562 | func (ptr *QuerySetService) DependencyGraph(ctx *domain.Context, querySetId int) (interface{}, error) { |
| 354 | tableRepository, _ := repository.NewTableRepository(ptr.transactionContext) | 563 | tableRepository, _ := repository.NewTableRepository(ptr.transactionContext) |
| 355 | _, tables, err := tableRepository.Find(map[string]interface{}{"context": ctx, "tableTypes": []string{domain.SchemaTable.ToString(), domain.SubProcessTable.ToString()}}) | 564 | _, tables, err := tableRepository.Find(map[string]interface{}{"context": ctx, "tableTypes": []string{domain.SchemaTable.ToString(), domain.SubProcessTable.ToString()}}) |
| 356 | - dependencies := make([]dependency, 0) | 565 | + var dependencies TableDependTree = NewTableDependTree(nil, nil) |
| 357 | if err != nil { | 566 | if err != nil { |
| 358 | return dependencies, err | 567 | return dependencies, err |
| 359 | } | 568 | } |
| @@ -368,89 +577,10 @@ func (ptr *QuerySetService) DependencyGraph(ctx *domain.Context, querySetId int) | @@ -368,89 +577,10 @@ func (ptr *QuerySetService) DependencyGraph(ctx *domain.Context, querySetId int) | ||
| 368 | if querySet.QuerySetInfo.BindTableId == 0 { | 577 | if querySet.QuerySetInfo.BindTableId == 0 { |
| 369 | return dependencies, nil | 578 | return dependencies, nil |
| 370 | } | 579 | } |
| 371 | - dependencies = makeDependencyGraph(querySet.QuerySetInfo.BindTableId, tables) | ||
| 372 | - return dependencies, nil | ||
| 373 | -} | ||
| 374 | - | ||
| 375 | -func makeDependencyGraph(bindTableId int, tables []*domain.Table) []dependency { | ||
| 376 | - dependencies := make([]dependency, 0) | ||
| 377 | - tableMap := make(map[int]*domain.Table) | ||
| 378 | - graph := make(map[int][]int, 0) | ||
| 379 | - for i := range tables { | ||
| 380 | - tableMap[tables[i].TableId] = tables[i] | ||
| 381 | - graph[tables[i].TableId] = tables[i].TableInfo.DependencyTables | ||
| 382 | - } | ||
| 383 | - // parent depend | ||
| 384 | - dependTables := []int{bindTableId} | ||
| 385 | 580 | ||
| 386 | - foundDependency := make(map[string]bool, 0) | ||
| 387 | - for { | ||
| 388 | - if len(dependTables) == 0 { | ||
| 389 | - break | ||
| 390 | - } | ||
| 391 | - parent := dependTables[0] | ||
| 392 | - parentTable, ok := tableMap[parent] | ||
| 393 | - if !ok { | ||
| 394 | - continue | ||
| 395 | - } | ||
| 396 | - for _, dependChildId := range parentTable.TableInfo.DependencyTables { | ||
| 397 | - dependChild, ok := tableMap[dependChildId] | ||
| 398 | - if !ok { | ||
| 399 | - continue | ||
| 400 | - } | ||
| 401 | - depend := NewDependency(parentTable.TableId, dependChild) | ||
| 402 | - if _, ok := foundDependency[depend.String()]; !ok { | ||
| 403 | - dependencies = append(dependencies, depend) | ||
| 404 | - } | ||
| 405 | - } | ||
| 406 | - dependTables = dependTables[1:] | ||
| 407 | - } | ||
| 408 | - | ||
| 409 | - // dependToChild | ||
| 410 | - return dependencies | ||
| 411 | -} | ||
| 412 | - | ||
| 413 | -func dependParents(tables []*domain.Table, tableMap map[int]*domain.Table, bindTableId int) []int { | ||
| 414 | - foundDependTable := make(map[int]bool) | ||
| 415 | - traceStack := []int{bindTableId} | ||
| 416 | - res := []int{bindTableId} | ||
| 417 | - for { | ||
| 418 | - if len(traceStack) == 0 { | ||
| 419 | - break | ||
| 420 | - } | ||
| 421 | - last := traceStack[0] | ||
| 422 | - traceStack = traceStack[1:] | ||
| 423 | - table := tableMap[last] | ||
| 424 | - if table == nil { | ||
| 425 | - continue | ||
| 426 | - } | ||
| 427 | - // for | ||
| 428 | - if _, ok := foundDependTable[last]; !ok { | ||
| 429 | - foundDependTable[last] = true | ||
| 430 | - } | ||
| 431 | - } | ||
| 432 | - return res | ||
| 433 | -} | ||
| 434 | - | ||
| 435 | -func NewDependency(parentId int, child *domain.Table) dependency { | ||
| 436 | - return dependency{ | ||
| 437 | - Id: parentId, | ||
| 438 | - } | ||
| 439 | -} | ||
| 440 | - | ||
| 441 | -type dependency struct { | ||
| 442 | - // 标识 | ||
| 443 | - Id int `json:"id"` | ||
| 444 | - // 表类型 MainTable:主表 SideTable:副表 SubTable:分表 ExcelTable:Excel表 | ||
| 445 | - Type string `json:"type"` | ||
| 446 | - // 名称 | ||
| 447 | - Name string `json:"name"` | ||
| 448 | - // 依赖的表 | ||
| 449 | - DependChildId int `json:"dependChildId"` | ||
| 450 | -} | ||
| 451 | - | ||
| 452 | -func (d dependency) String() string { | ||
| 453 | - return fmt.Sprintf("%d-%d", d.Id, d.DependChildId) | 581 | + tableDependencyService, _ := NewTableDependencyService(ptr.transactionContext) |
| 582 | + dependencies = tableDependencyService.TableDependTree(tables, querySet.QuerySetInfo.BindTableId) | ||
| 583 | + return dependencies, nil | ||
| 454 | } | 584 | } |
| 455 | 585 | ||
| 456 | func (ptr *QuerySetService) GetAllChild(ctx *domain.Context, querySetId int, includeSelf bool, onlyNextLevel bool) ([]*domain.QuerySet, error) { | 586 | func (ptr *QuerySetService) GetAllChild(ctx *domain.Context, querySetId int, includeSelf bool, onlyNextLevel bool) ([]*domain.QuerySet, error) { |
| 1 | +package domainService | ||
| 2 | + | ||
| 3 | +import ( | ||
| 4 | + "github.com/stretchr/testify/assert" | ||
| 5 | + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain" | ||
| 6 | + "testing" | ||
| 7 | +) | ||
| 8 | + | ||
| 9 | +func TestSelectsEditLog(t *testing.T) { | ||
| 10 | + oldQueryComponents := []*domain.QueryComponent{ | ||
| 11 | + &domain.QueryComponent{ | ||
| 12 | + Id: "1", | ||
| 13 | + Selects: []domain.SelectExprGroup{ | ||
| 14 | + { | ||
| 15 | + SelectExpr: domain.SelectExpr{ | ||
| 16 | + Id: "c_1", | ||
| 17 | + FieldLeft: domain.FieldExpr{ | ||
| 18 | + ExprSql: "t.a", | ||
| 19 | + ExprHuman: "T.a", | ||
| 20 | + }, | ||
| 21 | + FieldRight: domain.FieldExpr{ | ||
| 22 | + ExprSql: "t.b", | ||
| 23 | + ExprHuman: "T.b", | ||
| 24 | + }, | ||
| 25 | + }, | ||
| 26 | + SubSelects: []domain.SelectExpr{ | ||
| 27 | + { | ||
| 28 | + Id: "c_s_1", | ||
| 29 | + FieldLeft: domain.FieldExpr{ | ||
| 30 | + ExprSql: "t.a", | ||
| 31 | + ExprHuman: "T.a", | ||
| 32 | + }, | ||
| 33 | + FieldRight: domain.FieldExpr{ | ||
| 34 | + ExprSql: "t.b", | ||
| 35 | + ExprHuman: "T.b", | ||
| 36 | + }, | ||
| 37 | + }, | ||
| 38 | + }, | ||
| 39 | + }, | ||
| 40 | + { | ||
| 41 | + SelectExpr: domain.SelectExpr{ | ||
| 42 | + Id: "c_2", | ||
| 43 | + FieldLeft: domain.FieldExpr{ | ||
| 44 | + ExprSql: "t.c", | ||
| 45 | + ExprHuman: "T.c", | ||
| 46 | + }, | ||
| 47 | + FieldRight: domain.FieldExpr{ | ||
| 48 | + ExprSql: "t.d", | ||
| 49 | + ExprHuman: "T.d", | ||
| 50 | + }, | ||
| 51 | + }, | ||
| 52 | + }, | ||
| 53 | + }, | ||
| 54 | + }, | ||
| 55 | + } | ||
| 56 | + newQueryComponents := []*domain.QueryComponent{ | ||
| 57 | + &domain.QueryComponent{ | ||
| 58 | + Id: "1", | ||
| 59 | + Selects: []domain.SelectExprGroup{ | ||
| 60 | + { | ||
| 61 | + SelectExpr: domain.SelectExpr{ | ||
| 62 | + Id: "c_1", | ||
| 63 | + FieldLeft: domain.FieldExpr{ | ||
| 64 | + ExprSql: "t.a", | ||
| 65 | + ExprHuman: "T.a", | ||
| 66 | + }, | ||
| 67 | + FieldRight: domain.FieldExpr{ | ||
| 68 | + ExprSql: "t.c", | ||
| 69 | + ExprHuman: "T.c", | ||
| 70 | + }, | ||
| 71 | + }, | ||
| 72 | + }, | ||
| 73 | + }, | ||
| 74 | + }, | ||
| 75 | + } | ||
| 76 | + querySet := &domain.QuerySet{ | ||
| 77 | + QuerySetId: 1, | ||
| 78 | + Type: domain.SchemaTable.ToString(), | ||
| 79 | + Flag: domain.FlagSet, | ||
| 80 | + Name: "测试集合", | ||
| 81 | + QueryComponents: oldQueryComponents, | ||
| 82 | + } | ||
| 83 | + ctx := &domain.Context{CompanyId: 1, OperatorId: 1, OperatorName: "test"} | ||
| 84 | + logs := selectsEditLog(ctx, querySet, newQueryComponents) | ||
| 85 | + assert.Equal(t, len(logs), 3) | ||
| 86 | +} | ||
| 87 | + | ||
| 88 | +func TestConditionsEditLog(t *testing.T) { | ||
| 89 | + oldQueryComponents := []*domain.QueryComponent{ | ||
| 90 | + &domain.QueryComponent{ | ||
| 91 | + Id: "1", | ||
| 92 | + Conditions: []domain.ConditionExpr{ | ||
| 93 | + { | ||
| 94 | + Id: "c_1", | ||
| 95 | + FieldLeft: domain.FieldExpr{ | ||
| 96 | + ExprSql: "t.a", | ||
| 97 | + ExprHuman: "T.a", | ||
| 98 | + }, | ||
| 99 | + FieldRight: domain.FieldExpr{ | ||
| 100 | + ExprSql: "t.b", | ||
| 101 | + ExprHuman: "T.b", | ||
| 102 | + }, | ||
| 103 | + OperatorSymbol: "=", | ||
| 104 | + }, | ||
| 105 | + { | ||
| 106 | + Id: "c_2", | ||
| 107 | + FieldLeft: domain.FieldExpr{ | ||
| 108 | + ExprSql: "t.c", | ||
| 109 | + ExprHuman: "T.c", | ||
| 110 | + }, | ||
| 111 | + FieldRight: domain.FieldExpr{ | ||
| 112 | + ExprSql: "t.d", | ||
| 113 | + ExprHuman: "T.d", | ||
| 114 | + }, | ||
| 115 | + OperatorSymbol: "=", | ||
| 116 | + }, | ||
| 117 | + }, | ||
| 118 | + }, | ||
| 119 | + } | ||
| 120 | + newQueryComponents := []*domain.QueryComponent{ | ||
| 121 | + &domain.QueryComponent{ | ||
| 122 | + Id: "1", | ||
| 123 | + Conditions: []domain.ConditionExpr{ | ||
| 124 | + { | ||
| 125 | + Id: "c_1", | ||
| 126 | + FieldLeft: domain.FieldExpr{ | ||
| 127 | + ExprSql: "t.a", | ||
| 128 | + ExprHuman: "T.a", | ||
| 129 | + }, | ||
| 130 | + FieldRight: domain.FieldExpr{ | ||
| 131 | + ExprSql: "t.c", | ||
| 132 | + ExprHuman: "T.c", | ||
| 133 | + }, | ||
| 134 | + OperatorSymbol: "=", | ||
| 135 | + }, | ||
| 136 | + }, | ||
| 137 | + }, | ||
| 138 | + } | ||
| 139 | + querySet := &domain.QuerySet{ | ||
| 140 | + QuerySetId: 1, | ||
| 141 | + Type: domain.SchemaTable.ToString(), | ||
| 142 | + Flag: domain.FlagSet, | ||
| 143 | + Name: "测试集合", | ||
| 144 | + QueryComponents: oldQueryComponents, | ||
| 145 | + } | ||
| 146 | + ctx := &domain.Context{CompanyId: 1, OperatorId: 1, OperatorName: "test"} | ||
| 147 | + logs := conditionsEditLog(ctx, querySet, newQueryComponents) | ||
| 148 | + assert.Equal(t, len(logs), 2) | ||
| 149 | +} |
| @@ -64,8 +64,12 @@ func (ptr *DeleteDataTableService) DeleteTables(ctx *domain.Context, tables ...* | @@ -64,8 +64,12 @@ func (ptr *DeleteDataTableService) DeleteTables(ctx *domain.Context, tables ...* | ||
| 64 | return nil | 64 | return nil |
| 65 | } | 65 | } |
| 66 | tableRepository, _ := repository.NewTableRepository(ptr.transactionContext) | 66 | tableRepository, _ := repository.NewTableRepository(ptr.transactionContext) |
| 67 | + tableDependencyService, _ := NewTableDependencyService(ptr.transactionContext) | ||
| 67 | var tableIds []int | 68 | var tableIds []int |
| 68 | for _, t := range tables { | 69 | for _, t := range tables { |
| 70 | + if err := tableDependencyService.HasDependencyError(ctx, t.TableId); err != nil { | ||
| 71 | + return err | ||
| 72 | + } | ||
| 69 | if _, err := tableRepository.Remove(t); err != nil { | 73 | if _, err := tableRepository.Remove(t); err != nil { |
| 70 | return err | 74 | return err |
| 71 | } | 75 | } |
| 1 | +package domainService | ||
| 2 | + | ||
| 3 | +import ( | ||
| 4 | + "errors" | ||
| 5 | + "fmt" | ||
| 6 | + pgTransaction "github.com/linmadan/egglib-go/transaction/pg" | ||
| 7 | + "github.com/zeromicro/go-zero/core/collection" | ||
| 8 | + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain" | ||
| 9 | + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/repository" | ||
| 10 | + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/utils" | ||
| 11 | +) | ||
| 12 | + | ||
| 13 | +type DependencyError struct { | ||
| 14 | + t *domain.Table | ||
| 15 | +} | ||
| 16 | + | ||
| 17 | +func (d DependencyError) Error() string { | ||
| 18 | + return fmt.Sprintf("已关联%v:%v", domain.EnumsDescription(domain.ObjectTypeMap, d.t.TableType), d.t.Name) | ||
| 19 | +} | ||
| 20 | + | ||
| 21 | +type CircleDependError struct { | ||
| 22 | + circleTable *domain.Table | ||
| 23 | + querySet *domain.QuerySet | ||
| 24 | +} | ||
| 25 | + | ||
| 26 | +func (d CircleDependError) Error() string { | ||
| 27 | + circleTableName := "" | ||
| 28 | + if d.circleTable != nil { | ||
| 29 | + circleTableName = d.circleTable.Name | ||
| 30 | + } | ||
| 31 | + return fmt.Sprintf("循环引用%s(%s)", domain.EnumsDescription(domain.ObjectTypeMap, d.querySet.Type), circleTableName) | ||
| 32 | +} | ||
| 33 | + | ||
| 34 | +func NewCircleDependError(t *domain.Table, qs *domain.QuerySet) CircleDependError { | ||
| 35 | + return CircleDependError{ | ||
| 36 | + circleTable: t, | ||
| 37 | + querySet: qs, | ||
| 38 | + } | ||
| 39 | +} | ||
| 40 | + | ||
| 41 | +type TableDependencyService struct { | ||
| 42 | + transactionContext *pgTransaction.TransactionContext | ||
| 43 | + DetectedCycleCallBack func() | ||
| 44 | + DebugLog func(string) | ||
| 45 | + CircleNode int | ||
| 46 | + TableMap map[int]*domain.Table | ||
| 47 | +} | ||
| 48 | + | ||
| 49 | +func NewTableDependencyService(transactionContext *pgTransaction.TransactionContext) (*TableDependencyService, error) { | ||
| 50 | + if transactionContext == nil { | ||
| 51 | + return nil, fmt.Errorf("transactionContext参数不能为nil") | ||
| 52 | + } else { | ||
| 53 | + return &TableDependencyService{ | ||
| 54 | + transactionContext: transactionContext, | ||
| 55 | + }, nil | ||
| 56 | + } | ||
| 57 | +} | ||
| 58 | + | ||
| 59 | +func (ptr *TableDependencyService) CircleTable() *domain.Table { | ||
| 60 | + if ptr.TableMap == nil { | ||
| 61 | + return nil | ||
| 62 | + } | ||
| 63 | + return ptr.TableMap[ptr.CircleNode] | ||
| 64 | +} | ||
| 65 | + | ||
| 66 | +func (ptr *TableDependencyService) HasDependencyError(ctx *domain.Context, dependencyTable int) error { | ||
| 67 | + table, ok := TableHasDependency(ptr.transactionContext, ctx, dependencyTable) | ||
| 68 | + if !ok { | ||
| 69 | + return nil | ||
| 70 | + } | ||
| 71 | + return DependencyError{table} | ||
| 72 | +} | ||
| 73 | + | ||
| 74 | +func (ptr *TableDependencyService) Detect(ctx *domain.Context, edges [][]int) bool { | ||
| 75 | + graph, nodes := ptr.BuildGraph(edges) | ||
| 76 | + | ||
| 77 | + visiting := map[int]bool{} | ||
| 78 | + visited := map[int]bool{} | ||
| 79 | + | ||
| 80 | + for _, node := range nodes { | ||
| 81 | + ptr.log("------------------------------------------\n") | ||
| 82 | + ptr.log(fmt.Sprintf("dfs node %v \n", node)) | ||
| 83 | + if ptr.DetectCycle(graph, node, visiting, visited) { | ||
| 84 | + return true | ||
| 85 | + } | ||
| 86 | + } | ||
| 87 | + return false | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | +func (ptr *TableDependencyService) BuildGraph(edges [][]int) (map[int][]int, []int) { | ||
| 91 | + var graph = make(map[int][]int) | ||
| 92 | + var nodes []int | ||
| 93 | + for _, edge := range edges { | ||
| 94 | + a, b := edge[0], edge[1] | ||
| 95 | + if _, ok := graph[a]; !ok { | ||
| 96 | + nodes = append(nodes, a) | ||
| 97 | + graph[a] = make([]int, 0) | ||
| 98 | + } | ||
| 99 | + graph[a] = append(graph[a], b) | ||
| 100 | + } | ||
| 101 | + return graph, nodes | ||
| 102 | +} | ||
| 103 | + | ||
| 104 | +func (ptr *TableDependencyService) DetectCycle(graph map[int][]int, node int, visiting, visited map[int]bool) bool { | ||
| 105 | + if _, found := visited[node]; found { | ||
| 106 | + ptr.log(fmt.Sprintf("node %d is already visited(black) -> skip\n", node)) | ||
| 107 | + return false | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + if _, found := visiting[node]; found { | ||
| 111 | + ptr.log(fmt.Sprintf("node %d is in visiting(gray) -> a cycle is detected\n\n", node)) | ||
| 112 | + ptr.CircleNode = node | ||
| 113 | + return true | ||
| 114 | + } | ||
| 115 | + visiting[node] = true | ||
| 116 | + ptr.log(fmt.Sprintf("nodes in visiting(gray): %+v\n", visiting)) | ||
| 117 | + ptr.log(fmt.Sprintf("nodes in visited(black): %+v\n\n", visited)) | ||
| 118 | + | ||
| 119 | + for _, descendant := range graph[node] { | ||
| 120 | + ptr.log(fmt.Sprintf("current node: node %d\n", node)) | ||
| 121 | + ptr.log(fmt.Sprintf("visit descendant: node %d\n", descendant)) | ||
| 122 | + if ptr.DetectCycle(graph, descendant, visiting, visited) { | ||
| 123 | + return true | ||
| 124 | + } | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + delete(visiting, node) | ||
| 128 | + visited[node] = true | ||
| 129 | + ptr.log(fmt.Sprintf("finish explore node %d\n", node)) | ||
| 130 | + ptr.log(fmt.Sprintf("nodes in visiting(gray): %+v\n", visiting)) | ||
| 131 | + ptr.log(fmt.Sprintf("nodes in visited(black): %+v\n\n", visited)) | ||
| 132 | + return false | ||
| 133 | +} | ||
| 134 | + | ||
| 135 | +func (ptr *TableDependencyService) log(text string) { | ||
| 136 | + if ptr.DebugLog == nil { | ||
| 137 | + return | ||
| 138 | + } | ||
| 139 | + ptr.DebugLog(text) | ||
| 140 | +} | ||
| 141 | + | ||
| 142 | +func (ptr *TableDependencyService) TableDependTree(tables []*domain.Table, bindTableId int) TableDependTree { | ||
| 143 | + var ( | ||
| 144 | + parentMap = ptr.makeParentMap(tables) | ||
| 145 | + childMap = ptr.makeChildMap(tables) | ||
| 146 | + rootNodes = ptr.findRootNodes(parentMap, bindTableId) | ||
| 147 | + tree = ptr.makeTrees(childMap, rootNodes) | ||
| 148 | + ) | ||
| 149 | + | ||
| 150 | + tableMap := make(map[int]*domain.Table) | ||
| 151 | + for i := range tables { | ||
| 152 | + tableMap[tables[i].TableId] = tables[i] | ||
| 153 | + } | ||
| 154 | + ptr.TableMap = tableMap | ||
| 155 | + tableDependTree := NewTableDependTree(tree, tableMap) | ||
| 156 | + tableDependTree.NodeSelected = NewTableNode(tableMap[bindTableId]) | ||
| 157 | + return tableDependTree | ||
| 158 | +} | ||
| 159 | +func (ptr *TableDependencyService) makeParentMap(tables []*domain.Table) map[int][]int { | ||
| 160 | + res := make(map[int][]int) | ||
| 161 | + for i := range tables { | ||
| 162 | + if _, ok := res[tables[i].TableId]; !ok { | ||
| 163 | + res[tables[i].TableId] = make([]int, 0) | ||
| 164 | + } | ||
| 165 | + for _, t := range tables[i].TableInfo.DependencyTables { | ||
| 166 | + if _, ok := res[t]; !ok { | ||
| 167 | + res[t] = make([]int, 0) | ||
| 168 | + } | ||
| 169 | + res[t] = append(res[t], tables[i].TableId) | ||
| 170 | + } | ||
| 171 | + } | ||
| 172 | + return res | ||
| 173 | +} | ||
| 174 | +func (ptr *TableDependencyService) makeChildMap(tables []*domain.Table) map[int][]int { | ||
| 175 | + res := make(map[int][]int) | ||
| 176 | + for i := range tables { | ||
| 177 | + id := tables[i].TableId | ||
| 178 | + if _, ok := res[id]; !ok { | ||
| 179 | + res[id] = make([]int, 0) | ||
| 180 | + } | ||
| 181 | + | ||
| 182 | + res[id] = append(res[id], tables[i].TableInfo.DependencyTables...) | ||
| 183 | + } | ||
| 184 | + return res | ||
| 185 | +} | ||
| 186 | +func (ptr *TableDependencyService) findRootNodes(parentsMap map[int][]int, nodeId int) []int { | ||
| 187 | + var rootNodes []int | ||
| 188 | + set := collection.NewSet() | ||
| 189 | + stack := utils.NewEmptyStack() | ||
| 190 | + stack.Push(nodeId) | ||
| 191 | + for { | ||
| 192 | + if stack.Size() == 0 { | ||
| 193 | + break | ||
| 194 | + } | ||
| 195 | + t := stack.Pop().(int) | ||
| 196 | + if set.Contains(t) { | ||
| 197 | + continue | ||
| 198 | + } | ||
| 199 | + set.Add(t) | ||
| 200 | + parents, ok := parentsMap[t] | ||
| 201 | + if !ok { | ||
| 202 | + continue | ||
| 203 | + } | ||
| 204 | + if len(parents) == 0 { | ||
| 205 | + rootNodes = append(rootNodes, t) | ||
| 206 | + } | ||
| 207 | + for _, item := range parents { | ||
| 208 | + stack.Push(item) | ||
| 209 | + } | ||
| 210 | + } | ||
| 211 | + return rootNodes | ||
| 212 | +} | ||
| 213 | +func (ptr *TableDependencyService) makeTrees(childMap map[int][]int, rootNodes []int) []int { | ||
| 214 | + set := collection.NewSet() | ||
| 215 | + stack := utils.NewEmptyStack() | ||
| 216 | + for _, node := range rootNodes { | ||
| 217 | + stack.Push(node) | ||
| 218 | + } | ||
| 219 | + | ||
| 220 | + for { | ||
| 221 | + if stack.Size() == 0 { | ||
| 222 | + break | ||
| 223 | + } | ||
| 224 | + t := stack.Pop().(int) | ||
| 225 | + if set.Contains(t) { | ||
| 226 | + continue | ||
| 227 | + } | ||
| 228 | + set.Add(t) | ||
| 229 | + parents, ok := childMap[t] | ||
| 230 | + if !ok { | ||
| 231 | + continue | ||
| 232 | + } | ||
| 233 | + if len(parents) == 0 { | ||
| 234 | + rootNodes = append(rootNodes, t) | ||
| 235 | + } | ||
| 236 | + for _, item := range parents { | ||
| 237 | + stack.Push(item) | ||
| 238 | + } | ||
| 239 | + } | ||
| 240 | + return set.KeysInt() | ||
| 241 | +} | ||
| 242 | + | ||
| 243 | +func TableHasDependency(transactionContext *pgTransaction.TransactionContext, ctx *domain.Context, dependencyTable int) (*domain.Table, bool) { | ||
| 244 | + tableRepository, _ := repository.NewTableRepository(transactionContext) | ||
| 245 | + table, err := tableRepository.FindOne(map[string]interface{}{"context": ctx, "dependencyTable": dependencyTable}) | ||
| 246 | + if errors.Is(err, domain.ErrorNotFound) { | ||
| 247 | + return nil, false | ||
| 248 | + } | ||
| 249 | + if table == nil && err != nil { | ||
| 250 | + return nil, false | ||
| 251 | + } | ||
| 252 | + return table, true | ||
| 253 | +} | ||
| 254 | + | ||
| 255 | +type TableDependTree struct { | ||
| 256 | + Tree []int `json:"-"` | ||
| 257 | + Nodes []TableNode `json:"nodes"` | ||
| 258 | + Edges []TableEdge `json:"edges"` | ||
| 259 | + NodeSelected TableNode `json:"nodeSelected"` | ||
| 260 | +} | ||
| 261 | + | ||
| 262 | +func (td TableDependTree) EdgesArray() [][]int { | ||
| 263 | + var res = make([][]int, 0) | ||
| 264 | + for _, edge := range td.Edges { | ||
| 265 | + res = append(res, []int{edge.Id, edge.DependChildId}) | ||
| 266 | + } | ||
| 267 | + return res | ||
| 268 | +} | ||
| 269 | + | ||
| 270 | +type TableNode struct { | ||
| 271 | + TableId int `json:"tableId"` | ||
| 272 | + // 表类型 MainTable:主表 SideTable:副表 SubTable:分表 | ||
| 273 | + Type string `json:"type"` | ||
| 274 | + // 名称 | ||
| 275 | + Name string `json:"name"` | ||
| 276 | + // 依赖关联的表 | ||
| 277 | + DependencyTables []int `json:"-"` | ||
| 278 | +} | ||
| 279 | +type TableEdge struct { | ||
| 280 | + // 标识 | ||
| 281 | + Id int `json:"tableId"` | ||
| 282 | + // 表类型 MainTable:主表 SideTable:副表 SubTable:分表 | ||
| 283 | + Type string `json:"type"` | ||
| 284 | + // 名称 | ||
| 285 | + Name string `json:"name"` | ||
| 286 | + // 依赖的表 | ||
| 287 | + DependChildId int `json:"dependTableId"` | ||
| 288 | +} | ||
| 289 | + | ||
| 290 | +func NewTableDependTree(tree []int, tableMap map[int]*domain.Table) TableDependTree { | ||
| 291 | + dependTree := TableDependTree{ | ||
| 292 | + Nodes: make([]TableNode, 0), | ||
| 293 | + Edges: make([]TableEdge, 0), | ||
| 294 | + Tree: tree, | ||
| 295 | + } | ||
| 296 | + | ||
| 297 | + for _, node := range tree { | ||
| 298 | + t, ok := tableMap[node] | ||
| 299 | + if !ok { | ||
| 300 | + continue | ||
| 301 | + } | ||
| 302 | + dependTree.Nodes = append(dependTree.Nodes, NewTableNode(t)) | ||
| 303 | + for _, item := range t.DependencyTables() { | ||
| 304 | + dependTable, ok := tableMap[item] | ||
| 305 | + if !ok { | ||
| 306 | + continue | ||
| 307 | + } | ||
| 308 | + dependTree.Edges = append(dependTree.Edges, NewTableEdge(t, dependTable)) | ||
| 309 | + } | ||
| 310 | + } | ||
| 311 | + return dependTree | ||
| 312 | +} | ||
| 313 | +func NewTableNode(table *domain.Table) TableNode { | ||
| 314 | + return TableNode{ | ||
| 315 | + TableId: table.TableId, | ||
| 316 | + Type: table.TableType, | ||
| 317 | + Name: table.Name, | ||
| 318 | + DependencyTables: table.TableInfo.DependencyTables, | ||
| 319 | + } | ||
| 320 | +} | ||
| 321 | +func NewTableEdge(table *domain.Table, dependTable *domain.Table) TableEdge { | ||
| 322 | + return TableEdge{ | ||
| 323 | + Id: table.TableId, | ||
| 324 | + Type: table.TableType, | ||
| 325 | + Name: table.Name, | ||
| 326 | + DependChildId: dependTable.TableId, | ||
| 327 | + } | ||
| 328 | +} |
| 1 | +package domainService | ||
| 2 | + | ||
| 3 | +import ( | ||
| 4 | + "fmt" | ||
| 5 | + "github.com/stretchr/testify/assert" | ||
| 6 | + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain" | ||
| 7 | + "sort" | ||
| 8 | + "testing" | ||
| 9 | +) | ||
| 10 | + | ||
| 11 | +func TestTableDependTree(t *testing.T) { | ||
| 12 | + /* | ||
| 13 | + 5 8 | ||
| 14 | + / \ / \ | ||
| 15 | + 1 3 9 10 | ||
| 16 | + / / \ / \ | ||
| 17 | + 4 6 2 11 12 | ||
| 18 | + \ | ||
| 19 | + 7 | ||
| 20 | + */ | ||
| 21 | + tables := []*domain.Table{ | ||
| 22 | + newTable(5, 1, 3), | ||
| 23 | + newTable(1, 4), | ||
| 24 | + newTable(4), | ||
| 25 | + newTable(3, 6, 2), | ||
| 26 | + newTable(6), | ||
| 27 | + newTable(2, 7), | ||
| 28 | + newTable(7), | ||
| 29 | + | ||
| 30 | + newTable(8, 9, 10), | ||
| 31 | + newTable(10), | ||
| 32 | + newTable(9, 11, 12), | ||
| 33 | + newTable(11), | ||
| 34 | + newTable(12, 9), | ||
| 35 | + } | ||
| 36 | + tableDependencyService := &TableDependencyService{DebugLog: func(s string) { | ||
| 37 | + //t.Log(s) | ||
| 38 | + }} | ||
| 39 | + | ||
| 40 | + inputs := []struct { | ||
| 41 | + BindTableId int | ||
| 42 | + Trees []int | ||
| 43 | + Circle bool | ||
| 44 | + }{ | ||
| 45 | + { | ||
| 46 | + 3, | ||
| 47 | + []int{1, 2, 3, 4, 5, 6, 7}, | ||
| 48 | + false, | ||
| 49 | + }, | ||
| 50 | + { | ||
| 51 | + 4, | ||
| 52 | + []int{1, 2, 3, 4, 5, 6, 7}, | ||
| 53 | + false, | ||
| 54 | + }, | ||
| 55 | + | ||
| 56 | + { | ||
| 57 | + 12, | ||
| 58 | + []int{8, 9, 10, 11, 12}, | ||
| 59 | + true, | ||
| 60 | + }, | ||
| 61 | + { | ||
| 62 | + 8, | ||
| 63 | + []int{8, 9, 10, 11, 12}, | ||
| 64 | + true, | ||
| 65 | + }, | ||
| 66 | + { | ||
| 67 | + 10, | ||
| 68 | + []int{8, 9, 10, 11, 12}, | ||
| 69 | + true, | ||
| 70 | + }, | ||
| 71 | + } | ||
| 72 | + for _, input := range inputs { | ||
| 73 | + tree := tableDependencyService.TableDependTree(tables, input.BindTableId) | ||
| 74 | + got := tree.Tree | ||
| 75 | + sort.Ints(got) | ||
| 76 | + assert.Equal(t, got, input.Trees) | ||
| 77 | + | ||
| 78 | + assert.Equal(t, tableDependencyService.Detect(nil, tree.EdgesArray()), input.Circle) | ||
| 79 | + } | ||
| 80 | +} | ||
| 81 | + | ||
| 82 | +func newTable(id int, dependTables ...int) *domain.Table { | ||
| 83 | + return &domain.Table{ | ||
| 84 | + TableId: id, | ||
| 85 | + Name: fmt.Sprintf("t%d", id), | ||
| 86 | + TableInfo: &domain.TableInfo{ | ||
| 87 | + DependencyTables: dependTables, | ||
| 88 | + }, | ||
| 89 | + } | ||
| 90 | +} |
| @@ -55,6 +55,13 @@ func (ptr *UpdateTableStructService) UpdateTableStruct(ctx *domain.Context, tabl | @@ -55,6 +55,13 @@ func (ptr *UpdateTableStructService) UpdateTableStruct(ctx *domain.Context, tabl | ||
| 55 | if _, err = tableRepository.Save(mainTable); err != nil { | 55 | if _, err = tableRepository.Save(mainTable); err != nil { |
| 56 | return nil, err | 56 | return nil, err |
| 57 | } | 57 | } |
| 58 | + // 字段被删除 | ||
| 59 | + if len(deletes) > 0 { | ||
| 60 | + tableDependencyService, _ := NewTableDependencyService(ptr.transactionContext) | ||
| 61 | + if err := tableDependencyService.HasDependencyError(ctx, tableId); err != nil { | ||
| 62 | + return nil, err | ||
| 63 | + } | ||
| 64 | + } | ||
| 58 | 65 | ||
| 59 | // Log | 66 | // Log |
| 60 | // 日志 | 67 | // 日志 |
| @@ -144,6 +144,9 @@ func (repository *TableRepository) FindOne(queryOptions map[string]interface{}) | @@ -144,6 +144,9 @@ func (repository *TableRepository) FindOne(queryOptions map[string]interface{}) | ||
| 144 | if v, ok := queryOptions["tableTypes"]; ok && len(v.([]string)) > 0 { | 144 | if v, ok := queryOptions["tableTypes"]; ok && len(v.([]string)) > 0 { |
| 145 | query.Where(`table_type in (?)`, pg.In(v.([]string))) | 145 | query.Where(`table_type in (?)`, pg.In(v.([]string))) |
| 146 | } | 146 | } |
| 147 | + if v, ok := queryOptions["dependencyTable"]; ok && v.(int) > 0 { | ||
| 148 | + query.Where(`table_info->'dependencyTables' @> '[?]'`, v.(int)) | ||
| 149 | + } | ||
| 147 | if err := query.First(); err != nil { | 150 | if err := query.First(); err != nil { |
| 148 | if err.Error() == "pg: no rows in result set" { | 151 | if err.Error() == "pg: no rows in result set" { |
| 149 | return nil, domain.ErrorNotFound | 152 | return nil, domain.ErrorNotFound |
| @@ -166,6 +169,9 @@ func (repository *TableRepository) Find(queryOptions map[string]interface{}) (in | @@ -166,6 +169,9 @@ func (repository *TableRepository) Find(queryOptions map[string]interface{}) (in | ||
| 166 | query.SetWhereByQueryOption(fmt.Sprintf("name like '%%%v%%'", queryOptions["name"]), "name") | 169 | query.SetWhereByQueryOption(fmt.Sprintf("name like '%%%v%%'", queryOptions["name"]), "name") |
| 167 | 170 | ||
| 168 | query.SetWhereByQueryOption("parent_id = ?", "parentId") | 171 | query.SetWhereByQueryOption("parent_id = ?", "parentId") |
| 172 | + if v, ok := queryOptions["tableIds"]; ok && len(v.([]int)) > 0 { | ||
| 173 | + query.Where(`table_id in (?)`, pg.In(v.([]int))) | ||
| 174 | + } | ||
| 169 | if v, ok := queryOptions["tableTypes"]; ok && len(v.([]string)) > 0 { | 175 | if v, ok := queryOptions["tableTypes"]; ok && len(v.([]string)) > 0 { |
| 170 | query.Where(`table_type in (?)`, pg.In(v.([]string))) | 176 | query.Where(`table_type in (?)`, pg.In(v.([]string))) |
| 171 | } | 177 | } |
pkg/infrastructure/utils/stack.go
0 → 100644
| 1 | +package utils | ||
| 2 | + | ||
| 3 | +import "sync" | ||
| 4 | + | ||
| 5 | +// Item interface to store any data type in stack | ||
| 6 | +type Item interface{} | ||
| 7 | + | ||
| 8 | +// Stack struct which contains a list of Items | ||
| 9 | +type Stack struct { | ||
| 10 | + items []Item | ||
| 11 | + mutex sync.Mutex | ||
| 12 | +} | ||
| 13 | + | ||
| 14 | +// NewEmptyStack() returns a new instance of Stack with zero elements | ||
| 15 | +func NewEmptyStack() *Stack { | ||
| 16 | + return &Stack{ | ||
| 17 | + items: nil, | ||
| 18 | + } | ||
| 19 | +} | ||
| 20 | + | ||
| 21 | +// NewStack() returns a new instance of Stack with list of specified elements | ||
| 22 | +func NewStack(items []Item) *Stack { | ||
| 23 | + return &Stack{ | ||
| 24 | + items: items, | ||
| 25 | + } | ||
| 26 | +} | ||
| 27 | + | ||
| 28 | +// Push() adds new item to top of existing/empty stack | ||
| 29 | +func (stack *Stack) Push(item Item) { | ||
| 30 | + stack.mutex.Lock() | ||
| 31 | + defer stack.mutex.Unlock() | ||
| 32 | + | ||
| 33 | + stack.items = append(stack.items, item) | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +// Pop() removes most recent item(top) from stack | ||
| 37 | +func (stack *Stack) Pop() Item { | ||
| 38 | + stack.mutex.Lock() | ||
| 39 | + defer stack.mutex.Unlock() | ||
| 40 | + | ||
| 41 | + if len(stack.items) == 0 { | ||
| 42 | + return nil | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + lastItem := stack.items[len(stack.items)-1] | ||
| 46 | + stack.items = stack.items[:len(stack.items)-1] | ||
| 47 | + | ||
| 48 | + return lastItem | ||
| 49 | +} | ||
| 50 | + | ||
| 51 | +// IsEmpty() returns whether the stack is empty or not (boolean result) | ||
| 52 | +func (stack *Stack) IsEmpty() bool { | ||
| 53 | + stack.mutex.Lock() | ||
| 54 | + defer stack.mutex.Unlock() | ||
| 55 | + | ||
| 56 | + return len(stack.items) == 0 | ||
| 57 | +} | ||
| 58 | + | ||
| 59 | +// Top() returns the last inserted item in stack without removing it. | ||
| 60 | +func (stack *Stack) Top() Item { | ||
| 61 | + stack.mutex.Lock() | ||
| 62 | + defer stack.mutex.Unlock() | ||
| 63 | + | ||
| 64 | + if len(stack.items) == 0 { | ||
| 65 | + return nil | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + return stack.items[len(stack.items)-1] | ||
| 69 | +} | ||
| 70 | + | ||
| 71 | +// Size() returns the number of items currently in the stack | ||
| 72 | +func (stack *Stack) Size() int { | ||
| 73 | + stack.mutex.Lock() | ||
| 74 | + defer stack.mutex.Unlock() | ||
| 75 | + | ||
| 76 | + return len(stack.items) | ||
| 77 | +} | ||
| 78 | + | ||
| 79 | +// Clear() removes all items from the stack | ||
| 80 | +func (stack *Stack) Clear() { | ||
| 81 | + stack.mutex.Lock() | ||
| 82 | + defer stack.mutex.Unlock() | ||
| 83 | + | ||
| 84 | + stack.items = nil | ||
| 85 | +} |
| @@ -22,7 +22,7 @@ func (controller *QuerySetController) CreateQuerySet() { | @@ -22,7 +22,7 @@ func (controller *QuerySetController) CreateQuerySet() { | ||
| 22 | func (controller *QuerySetController) UpdateQuerySet() { | 22 | func (controller *QuerySetController) UpdateQuerySet() { |
| 23 | querySetService := service.NewQuerySetService(nil) | 23 | querySetService := service.NewQuerySetService(nil) |
| 24 | updateQuerySetCommand := &command.UpdateQuerySetCommand{} | 24 | updateQuerySetCommand := &command.UpdateQuerySetCommand{} |
| 25 | - controller.Unmarshal(updateQuerySetCommand) | 25 | + Must(controller.Unmarshal(updateQuerySetCommand)) |
| 26 | Id, _ := controller.GetInt(":querySetId") | 26 | Id, _ := controller.GetInt(":querySetId") |
| 27 | updateQuerySetCommand.QuerySetId = Id | 27 | updateQuerySetCommand.QuerySetId = Id |
| 28 | data, err := querySetService.UpdateQuerySet(ParseContext(controller.BaseController), updateQuerySetCommand) | 28 | data, err := querySetService.UpdateQuerySet(ParseContext(controller.BaseController), updateQuerySetCommand) |
-
请 注册 或 登录 后发表评论