作者 yangfu

Merge branch 'test'

@@ -11,6 +11,7 @@ require ( @@ -11,6 +11,7 @@ require (
11 github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 // indirect 11 github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 // indirect
12 github.com/fatih/structs v1.1.0 // indirect 12 github.com/fatih/structs v1.1.0 // indirect
13 github.com/gavv/httpexpect v2.0.0+incompatible 13 github.com/gavv/httpexpect v2.0.0+incompatible
  14 + github.com/go-gota/gota v0.12.0
14 github.com/go-pg/pg/v10 v10.10.6 15 github.com/go-pg/pg/v10 v10.10.6
15 github.com/go-redis/redis v6.15.9+incompatible 16 github.com/go-redis/redis v6.15.9+incompatible
16 github.com/google/go-querystring v1.1.0 // indirect 17 github.com/google/go-querystring v1.1.0 // indirect
@@ -29,6 +30,7 @@ require ( @@ -29,6 +30,7 @@ require (
29 github.com/stretchr/testify v1.7.1 30 github.com/stretchr/testify v1.7.1
30 github.com/valyala/fasthttp v1.38.0 // indirect 31 github.com/valyala/fasthttp v1.38.0 // indirect
31 github.com/xeipuuv/gojsonschema v1.2.0 // indirect 32 github.com/xeipuuv/gojsonschema v1.2.0 // indirect
  33 + github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 // indirect
32 github.com/xuri/excelize/v2 v2.6.0 34 github.com/xuri/excelize/v2 v2.6.0
33 github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 // indirect 35 github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 // indirect
34 github.com/yudai/gojsondiff v1.0.0 // indirect 36 github.com/yudai/gojsondiff v1.0.0 // indirect
  1 +package astexpr
  2 +
  3 +import (
  4 + "errors"
  5 + "fmt"
  6 + "strconv"
  7 + "strings"
  8 +)
  9 +
  10 +type AST struct {
  11 + Tokens []*Token
  12 +
  13 + source string
  14 + currTok *Token
  15 + currIndex int
  16 + depth int
  17 +
  18 + Err error
  19 +}
  20 +
  21 +func NewAST(toks []*Token, s string) *AST {
  22 + a := &AST{
  23 + Tokens: toks,
  24 + source: s,
  25 + }
  26 + if a.Tokens == nil || len(a.Tokens) == 0 {
  27 + a.Err = errors.New("empty token")
  28 + } else {
  29 + a.currIndex = 0
  30 + a.currTok = a.Tokens[0]
  31 + }
  32 + return a
  33 +}
  34 +
  35 +// ParseExpression 解析表达式
  36 +func (a *AST) ParseExpression() ExprAST {
  37 + a.depth++ // called depth
  38 + lhs := a.parsePrimary()
  39 + r := a.parseBinOpRHS(0, lhs)
  40 + a.depth--
  41 + if a.depth == 0 && a.currIndex != len(a.Tokens) && a.Err == nil {
  42 + a.Err = errors.New(
  43 + fmt.Sprintf("bad expression, reaching the end or missing the operator\n%s",
  44 + ErrPos(a.source, a.currTok.Offset)))
  45 + }
  46 + return r
  47 +}
  48 +
  49 +func (a *AST) getNextToken() *Token {
  50 + a.currIndex++
  51 + if a.currIndex < len(a.Tokens) {
  52 + a.currTok = a.Tokens[a.currIndex]
  53 + return a.currTok
  54 + }
  55 + return nil
  56 +}
  57 +
  58 +func (a *AST) getTokPrecedence() int {
  59 + if p, ok := precedence[a.currTok.Tok]; ok {
  60 + return p
  61 + }
  62 + return -1
  63 +}
  64 +
  65 +func (a *AST) parseNumber() NumberExprAST {
  66 + f64, err := strconv.ParseFloat(a.currTok.Tok, 64)
  67 + if err != nil {
  68 + a.Err = errors.New(
  69 + fmt.Sprintf("%v\nwant '(' or '0-9' but get '%s'\n%s",
  70 + err.Error(),
  71 + a.currTok.Tok,
  72 + ErrPos(a.source, a.currTok.Offset)))
  73 + return NumberExprAST{}
  74 + }
  75 + n := NumberExprAST{
  76 + Val: f64,
  77 + Str: a.currTok.Tok,
  78 + }
  79 + a.getNextToken()
  80 + return n
  81 +}
  82 +
  83 +func (a *AST) parseFunCallerOrConst() ExprAST {
  84 + name := a.currTok.Tok
  85 + a.getNextToken()
  86 + // call func
  87 + if a.currTok.Tok == "(" {
  88 + f := FunCallerExprAST{}
  89 + if _, ok := defFunc[strings.ToLower(name)]; !ok {
  90 + a.Err = errors.New(
  91 + fmt.Sprintf("function `%s` is undefined\n%s",
  92 + name,
  93 + ErrPos(a.source, a.currTok.Offset)))
  94 + return f
  95 + }
  96 + a.getNextToken()
  97 + exprs := make([]ExprAST, 0)
  98 + if a.currTok.Tok == ")" {
  99 + // function call without parameters
  100 + // ignore the process of parameter resolution
  101 + } else {
  102 + exprs = append(exprs, a.ParseExpression())
  103 + for a.currTok.Tok != ")" && a.getNextToken() != nil {
  104 + if a.currTok.Type == COMMA {
  105 + continue
  106 + }
  107 + exprs = append(exprs, a.ParseExpression())
  108 + }
  109 + }
  110 + def := defFunc[strings.ToLower(name)]
  111 + if def.argc >= 0 && len(exprs) != def.argc {
  112 + a.Err = errors.New(
  113 + fmt.Sprintf("wrong way calling function `%s`, parameters want %d but get %d\n%s",
  114 + name,
  115 + def.argc,
  116 + len(exprs),
  117 + ErrPos(a.source, a.currTok.Offset)))
  118 + }
  119 + a.getNextToken()
  120 + f = NewFunCallerExprAST(name, exprs...)
  121 + return f
  122 + }
  123 + // call const
  124 + if v, ok := defConst[name]; ok {
  125 + return NumberExprAST{
  126 + Val: v,
  127 + Str: strconv.FormatFloat(v, 'f', 0, 64),
  128 + }
  129 + } else {
  130 + if strings.Contains(name, ".") {
  131 + return NewFieldExprAST(name)
  132 + }
  133 + a.Err = errors.New(
  134 + fmt.Sprintf("const `%s` is undefined\n%s",
  135 + name,
  136 + ErrPos(a.source, a.currTok.Offset)))
  137 + return NumberExprAST{}
  138 + }
  139 +}
  140 +
  141 +func (a *AST) parsePrimary() ExprAST {
  142 + switch a.currTok.Type {
  143 + case Identifier:
  144 + return a.parseFunCallerOrConst()
  145 + case Literal:
  146 + return a.parseNumber()
  147 + case StringArgs:
  148 + e := NewValueExprAST(a.currTok.Tok)
  149 + a.getNextToken()
  150 + return e
  151 + case Operator:
  152 + if a.currTok.Tok == "(" {
  153 + t := a.getNextToken()
  154 + if t == nil {
  155 + a.Err = errors.New(
  156 + fmt.Sprintf("want '(' or '0-9' but get EOF\n%s",
  157 + ErrPos(a.source, a.currTok.Offset)))
  158 + return nil
  159 + }
  160 + e := a.ParseExpression()
  161 + if e == nil {
  162 + return nil
  163 + }
  164 + if a.currTok.Tok != ")" {
  165 + a.Err = errors.New(
  166 + fmt.Sprintf("want ')' but get %s\n%s",
  167 + a.currTok.Tok,
  168 + ErrPos(a.source, a.currTok.Offset)))
  169 + return nil
  170 + }
  171 + a.getNextToken()
  172 + return e
  173 + } else if a.currTok.Tok == "-" {
  174 + if a.getNextToken() == nil {
  175 + a.Err = errors.New(
  176 + fmt.Sprintf("want '0-9' but get '-'\n%s",
  177 + ErrPos(a.source, a.currTok.Offset)))
  178 + return nil
  179 + }
  180 + bin := NewBinaryExprAST("-", NumberExprAST{}, a.parsePrimary())
  181 + return bin
  182 + } else {
  183 + return a.parseNumber()
  184 + }
  185 + case COMMA:
  186 + a.Err = errors.New(
  187 + fmt.Sprintf("want '(' or '0-9' but get %s\n%s",
  188 + a.currTok.Tok,
  189 + ErrPos(a.source, a.currTok.Offset)))
  190 + return nil
  191 + default:
  192 + return nil
  193 + }
  194 +}
  195 +
  196 +func (a *AST) parseBinOpRHS(execPrec int, lhs ExprAST) ExprAST {
  197 + for {
  198 + tokPrec := a.getTokPrecedence()
  199 + if tokPrec < execPrec {
  200 + return lhs
  201 + }
  202 + binOp := a.currTok.Tok
  203 + if a.getNextToken() == nil {
  204 + a.Err = errors.New(
  205 + fmt.Sprintf("want '(' or '0-9' but get EOF\n%s",
  206 + ErrPos(a.source, a.currTok.Offset)))
  207 + return nil
  208 + }
  209 + rhs := a.parsePrimary()
  210 + if rhs == nil {
  211 + return nil
  212 + }
  213 + nextPrec := a.getTokPrecedence()
  214 + if tokPrec < nextPrec {
  215 + rhs = a.parseBinOpRHS(tokPrec+1, rhs)
  216 + if rhs == nil {
  217 + return nil
  218 + }
  219 + }
  220 + lhs = NewBinaryExprAST(binOp, lhs, rhs)
  221 + }
  222 +}
  1 +package astexpr
  2 +
  3 +import (
  4 + "errors"
  5 + "math"
  6 +)
  7 +
  8 +const (
  9 + RadianMode = iota
  10 + AngleMode
  11 +)
  12 +
  13 +type defS struct {
  14 + argc int
  15 + fun func(expr ...ExprAST) float64
  16 +}
  17 +
  18 +// TrigonometricMode enum "RadianMode", "AngleMode"
  19 +var TrigonometricMode = RadianMode
  20 +
  21 +var defConst = map[string]float64{
  22 + "pi": math.Pi,
  23 +}
  24 +
  25 +var defFunc map[string]defS
  26 +
  27 +func init() {
  28 + defFunc = map[string]defS{
  29 + "sin": {1, defSin},
  30 + "cos": {1, defCos},
  31 + "tan": {1, defTan},
  32 + "cot": {1, defCot},
  33 + "sec": {1, defSec},
  34 + "csc": {1, defCsc},
  35 +
  36 + "abs": {1, defAbs},
  37 + "ceil": {1, defCeil},
  38 + "floor": {1, defFloor},
  39 + "round": {1, defRound},
  40 + "sqrt": {1, defSqrt},
  41 + "cbrt": {1, defCbrt},
  42 +
  43 + "noerr": {1, defNoErr},
  44 +
  45 + "max": {-1, defMax},
  46 + "min": {-1, defMin},
  47 +
  48 + // excel support
  49 + "sum": {-1, defNone},
  50 + "if": {-1, defNone},
  51 + "sumif": {-1, defNone},
  52 + "and": {-1, defNone},
  53 + "or": {-1, defNone},
  54 + "month": {-1, defNone},
  55 + "year": {-1, defNone},
  56 + //"round": {-1, defNone},
  57 + "rounddown": {-1, defNone},
  58 + "roundup": {-1, defNone},
  59 + "count": {-1, defNone},
  60 + "countifs": {-1, defNone},
  61 + //"&": {-1, defNone},
  62 + "concat": {-1, defNone},
  63 + "sumifs": {-1, defNone},
  64 + }
  65 +}
  66 +
  67 +// sin(pi/2) = 1
  68 +func defSin(expr ...ExprAST) float64 {
  69 + return math.Sin(expr2Radian(expr[0]))
  70 +}
  71 +
  72 +// cos(0) = 1
  73 +func defCos(expr ...ExprAST) float64 {
  74 + return math.Cos(expr2Radian(expr[0]))
  75 +}
  76 +
  77 +// tan(pi/4) = 1
  78 +func defTan(expr ...ExprAST) float64 {
  79 + return math.Tan(expr2Radian(expr[0]))
  80 +}
  81 +
  82 +// cot(pi/4) = 1
  83 +func defCot(expr ...ExprAST) float64 {
  84 + return 1 / defTan(expr...)
  85 +}
  86 +
  87 +// sec(0) = 1
  88 +func defSec(expr ...ExprAST) float64 {
  89 + return 1 / defCos(expr...)
  90 +}
  91 +
  92 +// csc(pi/2) = 1
  93 +func defCsc(expr ...ExprAST) float64 {
  94 + return 1 / defSin(expr...)
  95 +}
  96 +
  97 +// abs(-2) = 2
  98 +func defAbs(expr ...ExprAST) float64 {
  99 + return math.Abs(ExprASTResult(expr[0]))
  100 +}
  101 +
  102 +// ceil(4.2) = ceil(4.8) = 5
  103 +func defCeil(expr ...ExprAST) float64 {
  104 + return math.Ceil(ExprASTResult(expr[0]))
  105 +}
  106 +
  107 +// floor(4.2) = floor(4.8) = 4
  108 +func defFloor(expr ...ExprAST) float64 {
  109 + return math.Floor(ExprASTResult(expr[0]))
  110 +}
  111 +
  112 +// round(4.2) = 4
  113 +// round(4.6) = 5
  114 +func defRound(expr ...ExprAST) float64 {
  115 + return math.Round(ExprASTResult(expr[0]))
  116 +}
  117 +
  118 +// sqrt(4) = 2
  119 +// sqrt(4) = abs(sqrt(4))
  120 +// returns only the absolute value of the result
  121 +func defSqrt(expr ...ExprAST) float64 {
  122 + return math.Sqrt(ExprASTResult(expr[0]))
  123 +}
  124 +
  125 +// cbrt(27) = 3
  126 +func defCbrt(expr ...ExprAST) float64 {
  127 + return math.Cbrt(ExprASTResult(expr[0]))
  128 +}
  129 +
  130 +// max(2) = 2
  131 +// max(2, 3) = 3
  132 +// max(2, 3, 1) = 3
  133 +func defMax(expr ...ExprAST) float64 {
  134 + if len(expr) == 0 {
  135 + panic(errors.New("calling function `max` must have at least one parameter."))
  136 + }
  137 + if len(expr) == 1 {
  138 + return ExprASTResult(expr[0])
  139 + }
  140 + maxV := ExprASTResult(expr[0])
  141 + for i := 1; i < len(expr); i++ {
  142 + v := ExprASTResult(expr[i])
  143 + maxV = math.Max(maxV, v)
  144 + }
  145 + return maxV
  146 +}
  147 +
  148 +// min(2) = 2
  149 +// min(2, 3) = 2
  150 +// min(2, 3, 1) = 1
  151 +func defMin(expr ...ExprAST) float64 {
  152 + if len(expr) == 0 {
  153 + panic(errors.New("calling function `min` must have at least one parameter."))
  154 + }
  155 + if len(expr) == 1 {
  156 + return ExprASTResult(expr[0])
  157 + }
  158 + maxV := ExprASTResult(expr[0])
  159 + for i := 1; i < len(expr); i++ {
  160 + v := ExprASTResult(expr[i])
  161 + maxV = math.Min(maxV, v)
  162 + }
  163 + return maxV
  164 +}
  165 +
  166 +// noerr(1/0) = 0
  167 +// noerr(2.5/(1-1)) = 0
  168 +func defNoErr(expr ...ExprAST) (r float64) {
  169 + defer func() {
  170 + if e := recover(); e != nil {
  171 + r = 0
  172 + }
  173 + }()
  174 + return ExprASTResult(expr[0])
  175 +}
  176 +
  177 +func defNone(expr ...ExprAST) (r float64) {
  178 + return 0
  179 +}
1 -package domain 1 +package astexpr
2 2
3 import ( 3 import (
4 "encoding/json" 4 "encoding/json"
5 "fmt" 5 "fmt"
  6 + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain"
6 ) 7 )
7 8
8 const ( 9 const (
@@ -10,12 +11,19 @@ const ( @@ -10,12 +11,19 @@ const (
10 TypeBinaryExprAST = "BinaryExprAST" 11 TypeBinaryExprAST = "BinaryExprAST"
11 TypeFieldExprAST = "FieldExprAST" 12 TypeFieldExprAST = "FieldExprAST"
12 TypeValueExprAST = "ValueExprAST" 13 TypeValueExprAST = "ValueExprAST"
  14 + TypeNumberExprAST = "NumberExprAST"
13 ) 15 )
14 16
15 type ExprAST interface { 17 type ExprAST interface {
16 toStr() string 18 toStr() string
17 } 19 }
18 20
  21 +type NumberExprAST struct {
  22 + ExprType string `json:"exprType"`
  23 + Val float64
  24 + Str string
  25 +}
  26 +
19 type ValueExprAST struct { 27 type ValueExprAST struct {
20 ExprType string `json:"exprType"` 28 ExprType string `json:"exprType"`
21 Val string `json:"val"` 29 Val string `json:"val"`
@@ -23,9 +31,9 @@ type ValueExprAST struct { @@ -23,9 +31,9 @@ type ValueExprAST struct {
23 } 31 }
24 32
25 type FieldExprAST struct { 33 type FieldExprAST struct {
26 - ExprType string `json:"exprType"`  
27 - Str string `json:"str"`  
28 - Field *TableField `json:"field"` 34 + ExprType string `json:"exprType"`
  35 + Str string `json:"str"`
  36 + Field *domain.TableField `json:"field"`
29 } 37 }
30 38
31 type BinaryExprAST struct { 39 type BinaryExprAST struct {
@@ -36,10 +44,17 @@ type BinaryExprAST struct { @@ -36,10 +44,17 @@ type BinaryExprAST struct {
36 } 44 }
37 45
38 type FunCallerExprAST struct { 46 type FunCallerExprAST struct {
39 - ArrayFlag bool `json:"arrayFlag"`  
40 - ExprType string `json:"exprType"`  
41 - Name string `json:"name"`  
42 - Args []ExprAST `json:"args"` 47 + //ArrayFlag bool `json:"arrayFlag"`
  48 + ExprType string `json:"exprType"`
  49 + Name string `json:"name"`
  50 + Args []ExprAST `json:"args"`
  51 +}
  52 +
  53 +func (n NumberExprAST) toStr() string {
  54 + return fmt.Sprintf(
  55 + "NumberExprAST:%s",
  56 + n.Str,
  57 + )
43 } 58 }
44 59
45 func (n ValueExprAST) toStr() string { 60 func (n ValueExprAST) toStr() string {
@@ -73,9 +88,9 @@ func (n FunCallerExprAST) toStr() string { @@ -73,9 +88,9 @@ func (n FunCallerExprAST) toStr() string {
73 } 88 }
74 89
75 type CloneFunCallerExprAST struct { 90 type CloneFunCallerExprAST struct {
76 - ArrayFlag bool `json:"arrayFlag"`  
77 - Name string `json:"name"`  
78 - Arg []json.RawMessage `json:"args"` 91 + //ArrayFlag bool `json:"arrayFlag"`
  92 + Name string `json:"name"`
  93 + Arg []json.RawMessage `json:"args"`
79 } 94 }
80 95
81 type CloneBinaryExprAST struct { 96 type CloneBinaryExprAST struct {
@@ -84,7 +99,7 @@ type CloneBinaryExprAST struct { @@ -84,7 +99,7 @@ type CloneBinaryExprAST struct {
84 Rhs json.RawMessage `json:"rhs"` 99 Rhs json.RawMessage `json:"rhs"`
85 } 100 }
86 101
87 -func AstExprUnmarshalJSON(data []byte) (interface{}, error) { 102 +func ExprUnmarshal(data []byte) (interface{}, error) {
88 var m = make(map[string]interface{}) 103 var m = make(map[string]interface{})
89 if err := json.Unmarshal(data, &m); err != nil { 104 if err := json.Unmarshal(data, &m); err != nil {
90 return nil, err 105 return nil, err
@@ -108,7 +123,7 @@ func unmarshalMapInterface(m map[string]interface{}, rawData []byte) (interface{ @@ -108,7 +123,7 @@ func unmarshalMapInterface(m map[string]interface{}, rawData []byte) (interface{
108 } 123 }
109 exprReturn := &FunCallerExprAST{Name: expr.Name, ExprType: TypeFunCallerExprAST} 124 exprReturn := &FunCallerExprAST{Name: expr.Name, ExprType: TypeFunCallerExprAST}
110 for i := range expr.Arg { 125 for i := range expr.Arg {
111 - subExpr, err := AstExprUnmarshalJSON(expr.Arg[i]) 126 + subExpr, err := ExprUnmarshal(expr.Arg[i])
112 if err != nil { 127 if err != nil {
113 return nil, err 128 return nil, err
114 } 129 }
@@ -126,7 +141,7 @@ func unmarshalMapInterface(m map[string]interface{}, rawData []byte) (interface{ @@ -126,7 +141,7 @@ func unmarshalMapInterface(m map[string]interface{}, rawData []byte) (interface{
126 } 141 }
127 exprReturn := &BinaryExprAST{Op: expr.Op, ExprType: TypeBinaryExprAST} 142 exprReturn := &BinaryExprAST{Op: expr.Op, ExprType: TypeBinaryExprAST}
128 if len(expr.Lhs) > 0 { 143 if len(expr.Lhs) > 0 {
129 - subExpr, err := AstExprUnmarshalJSON(expr.Lhs) 144 + subExpr, err := ExprUnmarshal(expr.Lhs)
130 if err != nil { 145 if err != nil {
131 return nil, err 146 return nil, err
132 } 147 }
@@ -135,7 +150,7 @@ func unmarshalMapInterface(m map[string]interface{}, rawData []byte) (interface{ @@ -135,7 +150,7 @@ func unmarshalMapInterface(m map[string]interface{}, rawData []byte) (interface{
135 } 150 }
136 } 151 }
137 if len(expr.Rhs) > 0 { 152 if len(expr.Rhs) > 0 {
138 - subExpr, err := AstExprUnmarshalJSON(expr.Rhs) 153 + subExpr, err := ExprUnmarshal(expr.Rhs)
139 if err != nil { 154 if err != nil {
140 return nil, err 155 return nil, err
141 } 156 }
  1 +package astexpr
  2 +
  3 +import (
  4 + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain"
  5 + "strconv"
  6 + "strings"
  7 +)
  8 +
  9 +func NewNumberExprAST(val string) NumberExprAST {
  10 + num, _ := strconv.ParseFloat(val, 64)
  11 + return NumberExprAST{
  12 + ExprType: TypeNumberExprAST,
  13 + Val: num,
  14 + Str: val,
  15 + }
  16 +}
  17 +
  18 +func NewValueExprAST(val string) ValueExprAST {
  19 + return ValueExprAST{
  20 + ExprType: TypeValueExprAST,
  21 + Val: val,
  22 + Str: val,
  23 + }
  24 +}
  25 +
  26 +func NewFieldExprAST(val string) FieldExprAST {
  27 + words := strings.Split(val, ".")
  28 + table := words[0]
  29 + filed := words[0]
  30 + if len(words) > 0 {
  31 + filed = words[1]
  32 + }
  33 + return FieldExprAST{
  34 + ExprType: TypeFieldExprAST,
  35 + Str: val,
  36 + Field: &domain.TableField{
  37 + FieldName: filed,
  38 + FieldSqlName: filed,
  39 + TableName: table,
  40 + },
  41 + }
  42 +}
  43 +
  44 +func NewBinaryExprAST(op string, lhs ExprAST, rhs ExprAST) BinaryExprAST {
  45 + return BinaryExprAST{
  46 + ExprType: TypeBinaryExprAST,
  47 + Op: op,
  48 + Lhs: lhs,
  49 + Rhs: rhs,
  50 + }
  51 +}
  52 +
  53 +func NewFunCallerExprAST(name string, args ...ExprAST) FunCallerExprAST {
  54 + return FunCallerExprAST{
  55 + ExprType: TypeFunCallerExprAST,
  56 + Name: name,
  57 + Args: args,
  58 + }
  59 +}
  1 +package astexpr
  2 +
  3 +import (
  4 + "fmt"
  5 + "github.com/go-gota/gota/dataframe"
  6 + "github.com/go-gota/gota/series"
  7 + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain"
  8 + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/utils"
  9 + "strings"
  10 +)
  11 +
  12 +type Calculator struct {
  13 + ExprAST ExprAST
  14 + DataTable *domain.DataTable
  15 + Result []string
  16 +}
  17 +
  18 +func NewCalculator(expr string) (*Calculator, error) {
  19 + toks, err := ParseToken(expr)
  20 + if err != nil {
  21 + return nil, err
  22 + }
  23 + ast := NewAST(toks, expr)
  24 + if ast.Err != nil {
  25 + return nil, ast.Err
  26 + }
  27 + ar := ast.ParseExpression()
  28 + if ast.Err != nil {
  29 + return nil, ast.Err
  30 + }
  31 +
  32 + cal := &Calculator{
  33 + ExprAST: ar,
  34 + }
  35 + return cal, nil
  36 +}
  37 +
  38 +func (cal *Calculator) SetDataTable(t *domain.DataTable) *Calculator {
  39 + cal.DataTable = t
  40 + return cal
  41 +}
  42 +
  43 +func (cal *Calculator) Exec() error {
  44 + return nil
  45 +}
  46 +
  47 +func (cal *Calculator) ExprASTResult(ast ExprAST) (*param, error) {
  48 + switch ast.(type) {
  49 + case BinaryExprAST:
  50 + var l, r *param
  51 + var err error
  52 + ast := ast.(BinaryExprAST)
  53 + l, err = cal.ExprASTResult(ast.Lhs)
  54 + if err != nil {
  55 + return nil, err
  56 + }
  57 + r, err = cal.ExprASTResult(ast.Rhs)
  58 + if err != nil {
  59 + return nil, err
  60 + }
  61 + switch ast.Op {
  62 + case "+", "-", "*", "/", "%":
  63 + return cal.OpCalc(ast.Op, l, r), nil
  64 + default:
  65 +
  66 + }
  67 + case NumberExprAST:
  68 + f := ast.(NumberExprAST)
  69 + return NewResult([]string{f.Str}), nil
  70 + case ValueExprAST:
  71 + f := ast.(ValueExprAST)
  72 + return NewResult([]string{f.Val}), nil
  73 + case FieldExprAST:
  74 + f := ast.(FieldExprAST)
  75 + values := cal.DataTable.Values(&domain.Field{SQLName: f.Field.FieldSqlName})
  76 + return NewResult(values), nil
  77 + case FunCallerExprAST:
  78 + f := ast.(FunCallerExprAST)
  79 + //def := defFunc[f.Name]
  80 + //def.fun(f.Args...)
  81 +
  82 + args := make([]*param, 0)
  83 + for i := range f.Args {
  84 + argValue, err := cal.ExprASTResult(f.Args[i])
  85 + if err != nil {
  86 + return nil, err
  87 + }
  88 + args = append(args, argValue)
  89 + }
  90 + return cal.callDef(f.Name, args), nil
  91 + }
  92 + return nil, nil
  93 +}
  94 +
  95 +func (cal *Calculator) callDef(name string, args []*param) *param {
  96 + switch strings.ToLower(name) {
  97 + case "sum":
  98 + return cal.sum(args...)
  99 + case "sumifs":
  100 + return cal.sumifs(args...)
  101 + case "countifs":
  102 + return cal.countifs(args...)
  103 + }
  104 + return cal.sum(args...)
  105 +}
  106 +
  107 +func (cal *Calculator) sum(params ...*param) *param {
  108 + var res = make([]string, 0)
  109 + var total float64
  110 + for _, p := range params {
  111 + for _, v := range p.data {
  112 + total += utils.NewNumberString(v).MustFloat64()
  113 + }
  114 + }
  115 + res = append(res, utils.AssertString(total))
  116 + return NewResult(res)
  117 +}
  118 +
  119 +func (cal *Calculator) sumifs(params ...*param) *param {
  120 + var list = make([]series.Series, 0)
  121 + var filters = make([]dataframe.F, 0)
  122 + for i := 0; i < len(params)-1; i++ {
  123 + col := colName(i)
  124 + if i == 0 {
  125 + list = append(list, series.New(params[i].Data(), series.Float, col))
  126 + continue
  127 + }
  128 + if i%2 == 1 {
  129 + list = append(list, series.New(params[i+1].Data(), series.String, col))
  130 + if f, ok := cal.resolverFilter(col, params[i]); ok {
  131 + filters = append(filters, f)
  132 + }
  133 + i++
  134 + }
  135 + }
  136 + df := dataframe.New(list...)
  137 + df = df.FilterAggregation(dataframe.And, filters...)
  138 + s := df.Col("A0")
  139 + return NewResult(s.Records())
  140 +}
  141 +
  142 +func (cal *Calculator) countifs(params ...*param) *param {
  143 + var list = make([]series.Series, 0)
  144 + var filters = make([]dataframe.F, 0)
  145 + for i := 0; i < len(params)-1; i++ {
  146 + col := colName(i)
  147 + if i%2 == 0 {
  148 + list = append(list, series.New(params[i].Data(), series.String, col))
  149 + if f, ok := cal.resolverFilter(col, params[i+1]); ok {
  150 + filters = append(filters, f)
  151 + }
  152 + i++
  153 + }
  154 + }
  155 + df := dataframe.New(list...)
  156 + df = df.FilterAggregation(dataframe.And, filters...)
  157 + count := df.Col("A0").Len()
  158 + return NewResult([]string{fmt.Sprintf("%d", count)})
  159 +}
  160 +
  161 +func (cal *Calculator) resolverFilter(key string, param *param) (dataframe.F, bool) {
  162 + if len(param.Data()) == 1 {
  163 + condition := param.Data()[0]
  164 + tokens, _ := ParseToken(formatTok(condition))
  165 +
  166 + switch tokens[0].Type {
  167 + case Operator, CompareOperator:
  168 + if tokens[0].Tok == "*" {
  169 + return dataframe.F{Colname: key, Comparator: series.CompFunc, Comparando: func(el series.Element) bool {
  170 + return strings.Contains(el.String(), strings.Trim(formatTok(condition), "*"))
  171 + }}, true
  172 + }
  173 + return dataframe.F{Colname: key, Comparator: series.Comparator(tokens[0].Tok), Comparando: formatTok(tokens[1].Tok)}, true
  174 + case Identifier, Literal, StringArgs:
  175 + if tokens[len(tokens)-1].Tok == "*" || tokens[0].Tok == "*" {
  176 + return dataframe.F{Colname: key, Comparator: series.CompFunc, Comparando: func(el series.Element) bool {
  177 + return strings.Contains(el.String(), strings.Trim(formatTok(condition), "*"))
  178 + }}, true
  179 + }
  180 + return dataframe.F{Colname: key, Comparator: series.Eq, Comparando: formatTok(condition)}, true
  181 + }
  182 + }
  183 + return dataframe.F{}, false
  184 +}
  185 +
  186 +func colName(i int) string {
  187 + return fmt.Sprintf("A%v", i)
  188 +}
  189 +
  190 +func formatTok(tok string) string {
  191 + return strings.Trim(tok, `"`)
  192 +}
  193 +
  194 +func (cal *Calculator) OpCalc(op string, lp *param, rp *param) *param {
  195 + var res = make([]string, 0)
  196 + temp := make([]string, 0)
  197 + temp = lp.Data()
  198 + l := lp.Data()
  199 + r := rp.Data()
  200 + if lp.Len() < rp.Len() {
  201 + l = r
  202 + r = temp
  203 + }
  204 + rIsSingleValue := len(r) == 1
  205 + var rValue string
  206 + if rIsSingleValue {
  207 + rValue = r[0]
  208 + }
  209 + for i, lValue := range l {
  210 + if rIsSingleValue {
  211 + res = append(res, opCalc(op, lValue, rValue))
  212 + continue
  213 + }
  214 + if i >= len(r) {
  215 + break
  216 + }
  217 + res = append(res, opCalc(op, lValue, r[i]))
  218 + }
  219 + return NewResult(res)
  220 +}
  221 +
  222 +func opCalc(op, v1, v2 string) string {
  223 + fv1 := utils.NumberString(v1).MustFloat64()
  224 + fv2 := utils.NumberString(v2).MustFloat64()
  225 + switch op {
  226 + case "+":
  227 + return utils.AssertString(fv1 + fv2)
  228 + case "-":
  229 + return utils.AssertString(fv1 - fv2)
  230 + case "*":
  231 + return utils.AssertString(fv1 * fv2)
  232 + case "/":
  233 + return utils.AssertString(fv1 / fv2)
  234 + }
  235 + return ""
  236 +}
  237 +
  238 +type param struct {
  239 + data []string
  240 +}
  241 +
  242 +func (p *param) Len() int {
  243 + return len(p.data)
  244 +}
  245 +
  246 +func (p *param) Data() []string {
  247 + return p.data
  248 +}
  249 +
  250 +func NewResult(data []string) *param {
  251 + return &param{
  252 + data: data,
  253 + }
  254 +}
  1 +package astexpr
  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 +var table = &domain.DataTable{
  10 + Fields: []*domain.Field{
  11 + {
  12 + Name: "业绩",
  13 + SQLName: "业绩",
  14 + },
  15 + {
  16 + Name: "绩效",
  17 + SQLName: "绩效",
  18 + },
  19 + {
  20 + Name: "月份",
  21 + SQLName: "月份",
  22 + },
  23 + },
  24 + Data: [][]string{
  25 + {
  26 + "10000", "0.90", "2月",
  27 + },
  28 + {
  29 + "20000", "0.95", "3月",
  30 + },
  31 + {
  32 + "20000", "0.95", "3月",
  33 + },
  34 + },
  35 +}
  36 +
  37 +var table1 = &domain.DataTable{
  38 + Fields: []*domain.Field{
  39 + {
  40 + Name: "产品",
  41 + SQLName: "产品",
  42 + },
  43 + {
  44 + Name: "季度",
  45 + SQLName: "季度",
  46 + },
  47 + {
  48 + Name: "数量",
  49 + SQLName: "数量",
  50 + },
  51 + },
  52 + Data: [][]string{
  53 + {
  54 + "苹果", "第一季度", "800",
  55 + },
  56 + {
  57 + "香蕉", "第一季度", "906",
  58 + },
  59 + {
  60 + "西瓜", "第一季度", "968",
  61 + },
  62 + {
  63 + "菠萝", "第一季度", "227",
  64 + },
  65 + {
  66 + "芒果", "第一季度", "612",
  67 + },
  68 + {
  69 + "苹果", "第二季度", "530",
  70 + },
  71 + {
  72 + "香蕉", "第二季度", "950",
  73 + },
  74 + {
  75 + "西瓜", "第二季度", "533",
  76 + },
  77 + {
  78 + "菠萝", "第二季度", "642",
  79 + },
  80 + {
  81 + "芒果", "第二季度", "489",
  82 + },
  83 + },
  84 +}
  85 +
  86 +func TestSumCalculator(t *testing.T) {
  87 + inputs := []struct {
  88 + expr string
  89 + want []string
  90 + }{
  91 + {
  92 + expr: `sum(1000+销售明细.业绩*销售明细.绩效)`,
  93 + want: []string{"50000"},
  94 + },
  95 + {
  96 + expr: `sum(销售明细.业绩)`,
  97 + want: []string{"50000"},
  98 + },
  99 + {
  100 + expr: `sum(销售明细.业绩/10)`,
  101 + want: []string{"5000"},
  102 + },
  103 + {
  104 + expr: `sum(10000,20000,20000)`,
  105 + want: []string{"50000"},
  106 + },
  107 + }
  108 + for _, expr := range inputs {
  109 + calc, err := NewCalculator(expr.expr)
  110 + if err != nil {
  111 + t.Fatal(err)
  112 + }
  113 + calc.SetDataTable(table)
  114 + got, err := calc.ExprASTResult(calc.ExprAST)
  115 + if err != nil {
  116 + t.Fatal(err)
  117 + }
  118 + assert.Equal(t, expr.want, got.data)
  119 + }
  120 +}
  121 +
  122 +func TestSumIfCalculator(t *testing.T) {
  123 + inputs := []struct {
  124 + expr string
  125 + want []string
  126 + }{
  127 + {
  128 + expr: `sum(sumifs(销售明细.业绩,"3月",销售明细.月份))`,
  129 + want: []string{"40000"},
  130 + },
  131 + {
  132 + expr: `sum(sumifs(销售明细.业绩,"3月",销售明细.月份,"<25000",销售明细.业绩))`,
  133 + want: []string{"40000"},
  134 + },
  135 + {
  136 + expr: `sum(sumifs(销售明细.业绩,"3*",销售明细.月份))`,
  137 + want: []string{"40000"},
  138 + },
  139 + {
  140 + expr: `sum(sumifs(销售明细.业绩,"*月",销售明细.月份))`,
  141 + want: []string{"50000"},
  142 + },
  143 + }
  144 + for _, expr := range inputs {
  145 + calc, err := NewCalculator(expr.expr)
  146 + if err != nil {
  147 + t.Fatal(err)
  148 + }
  149 + calc.SetDataTable(table)
  150 + got, err := calc.ExprASTResult(calc.ExprAST)
  151 + if err != nil {
  152 + t.Fatal(err)
  153 + }
  154 + assert.Equal(t, expr.want, got.data)
  155 + }
  156 +}
  157 +
  158 +func TestCountIfCalculator(t *testing.T) {
  159 + inputs := []struct {
  160 + expr string
  161 + want []string
  162 + }{
  163 + {
  164 + expr: `countifs(水果季度.产品,"苹果")`,
  165 + want: []string{"2"},
  166 + },
  167 + {
  168 + expr: `countifs(水果季度.产品,"*果")`,
  169 + want: []string{"4"},
  170 + },
  171 + {
  172 + expr: `countifs(水果季度.季度,"第一季度",水果季度.数量,">600")`,
  173 + want: []string{"4"},
  174 + },
  175 + {
  176 + expr: `countifs(水果季度.产品,"*果",水果季度.数量,">600")`,
  177 + want: []string{"2"},
  178 + },
  179 + }
  180 + for _, expr := range inputs {
  181 + calc, err := NewCalculator(expr.expr)
  182 + if err != nil {
  183 + t.Fatal(err)
  184 + }
  185 + calc.SetDataTable(table1)
  186 + got, err := calc.ExprASTResult(calc.ExprAST)
  187 + if err != nil {
  188 + t.Fatal(err)
  189 + }
  190 + assert.Equal(t, expr.want, got.data)
  191 + }
  192 +}
1 -package domain 1 +package astexpr
2 2
3 import ( 3 import (
  4 + "fmt"
  5 + "testing"
  6 +
4 "github.com/linmadan/egglib-go/utils/json" 7 "github.com/linmadan/egglib-go/utils/json"
5 "github.com/stretchr/testify/assert" 8 "github.com/stretchr/testify/assert"
6 - "testing" 9 + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain"
7 ) 10 )
8 11
9 func TestAstExprUnmarshalJSON(t *testing.T) { 12 func TestAstExprUnmarshalJSON(t *testing.T) {
@@ -57,7 +60,7 @@ func TestAstExprUnmarshalJSON(t *testing.T) { @@ -57,7 +60,7 @@ func TestAstExprUnmarshalJSON(t *testing.T) {
57 &FieldExprAST{ 60 &FieldExprAST{
58 ExprType: TypeFieldExprAST, 61 ExprType: TypeFieldExprAST,
59 Str: "业绩1", 62 Str: "业绩1",
60 - Field: &TableField{ 63 + Field: &domain.TableField{
61 TableId: 1, 64 TableId: 1,
62 TableName: "测试ABC", 65 TableName: "测试ABC",
63 TableSqlName: "table_abc_test", 66 TableSqlName: "table_abc_test",
@@ -83,7 +86,7 @@ func TestAstExprUnmarshalJSON(t *testing.T) { @@ -83,7 +86,7 @@ func TestAstExprUnmarshalJSON(t *testing.T) {
83 if err != nil { 86 if err != nil {
84 t.Fatal(err) 87 t.Fatal(err)
85 } 88 }
86 - v, err := AstExprUnmarshalJSON(data) 89 + v, err := ExprUnmarshal(data)
87 if err != nil { 90 if err != nil {
88 t.Fatal(err) 91 t.Fatal(err)
89 } 92 }
@@ -93,3 +96,45 @@ func TestAstExprUnmarshalJSON(t *testing.T) { @@ -93,3 +96,45 @@ func TestAstExprUnmarshalJSON(t *testing.T) {
93 assert.Equal(t, input.data, v) 96 assert.Equal(t, input.data, v)
94 } 97 }
95 } 98 }
  99 +
  100 +func TestAstExprParse(t *testing.T) {
  101 + funs := []struct {
  102 + Name string
  103 + Exp []string
  104 + Debug bool
  105 + }{
  106 + {
  107 + "COUNTIF 多级嵌套",
  108 + []string{
  109 + `COUNTIF(销售明细.业绩,"<=1000")-COUNTIF(销售明细.业绩,"<=100")`,
  110 + `SUM(1/(COUNTIF(销售明细.业绩,"<=1000")-COUNTIF(销售明细.业绩,"<=100")))`,
  111 + },
  112 + true,
  113 + },
  114 + {
  115 + "COUNTIF 多级嵌套",
  116 + []string{
  117 + `COUNTIF(销售明细.业绩,"<=1000")-COUNTIF(销售明细.业绩,"<=100")`,
  118 + `SUM(COUNTIF(销售明细.业绩,"<=1000")-COUNTIF(销售明细.业绩,"<=100"))`,
  119 + },
  120 + false,
  121 + },
  122 + }
  123 + for _, f := range funs {
  124 + if !f.Debug {
  125 + continue
  126 + }
  127 + fmt.Println("测试项目", f.Name)
  128 + fmt.Println()
  129 + for _, exp := range f.Exp {
  130 + fmt.Println("表达式:", exp)
  131 + r, err := Parse(exp)
  132 + if err != nil {
  133 + t.Error(err)
  134 + }
  135 + if r != 0 {
  136 +
  137 + }
  138 + }
  139 + }
  140 +}
  1 +package astexpr
  2 +
  3 +import (
  4 + "errors"
  5 + "fmt"
  6 + "strings"
  7 +)
  8 +
  9 +var precedence = map[string]int{
  10 + "+": 20, "-": 20, "*": 40, "/": 40, "%": 40, "^": 60,
  11 + "=": 10, ">": 10, "<": 10, "<=": 10, ">=": 10, "&": 40,
  12 +}
  13 +
  14 +const (
  15 + // Identifier 标识符 e.g.函数名、表字段
  16 + Identifier = iota
  17 + // Literal 文字 e.g. 50
  18 + Literal
  19 + // Operator 计算操作 e.g. + - * /
  20 + Operator
  21 + // COMMA 命令, e.g. (
  22 + COMMA
  23 + // CompareOperator 比较操作 e.g. < = >
  24 + CompareOperator
  25 + // StringArgs 字符串参数
  26 + StringArgs
  27 +)
  28 +
  29 +type Token struct {
  30 + // raw characters
  31 + Tok string
  32 + // type with Literal/Operator
  33 + Type,
  34 + Flag int
  35 +
  36 + Offset int
  37 +}
  38 +
  39 +type Parser struct {
  40 + Source string
  41 + SourceRunes []rune
  42 + ch rune
  43 + offset int
  44 +
  45 + err error
  46 +}
  47 +
  48 +func ParseToken(s string) ([]*Token, error) {
  49 + p := &Parser{
  50 + Source: s,
  51 + SourceRunes: []rune(s),
  52 + err: nil,
  53 + //ch: s[0],
  54 + }
  55 + p.ch = p.SourceRunes[0]
  56 + toks := p.parse()
  57 + if p.err != nil {
  58 + return nil, p.err
  59 + }
  60 + return toks, nil
  61 +}
  62 +
  63 +func (p *Parser) parse() []*Token {
  64 + toks := make([]*Token, 0)
  65 + for {
  66 + tok := p.nextTok()
  67 + if tok == nil {
  68 + break
  69 + }
  70 + toks = append(toks, tok)
  71 + }
  72 + return toks
  73 +}
  74 +
  75 +func (p *Parser) nextTok() *Token {
  76 + if p.offset >= len(p.SourceRunes) || p.err != nil {
  77 + return nil
  78 + }
  79 + var err error
  80 + for p.isWhitespace(p.ch) && err == nil {
  81 + err = p.nextCh()
  82 + }
  83 + start := p.offset
  84 + var tok *Token
  85 + switch p.ch {
  86 + case
  87 + '(',
  88 + ')',
  89 + '+',
  90 + '-',
  91 + '*',
  92 + '/',
  93 + '^',
  94 + '%',
  95 + '&':
  96 + tok = &Token{
  97 + Tok: string(p.ch),
  98 + Type: Operator,
  99 + }
  100 + tok.Offset = start
  101 + err = p.nextCh()
  102 + case
  103 + '>',
  104 + '<',
  105 + '=':
  106 + if p.isCompareWordChar(p.ch) {
  107 + for p.isCompareWordChar(p.ch) && p.nextCh() == nil {
  108 + }
  109 + tok = &Token{
  110 + Tok: string(p.SourceRunes[start:p.offset]),
  111 + Type: CompareOperator,
  112 + }
  113 + tok.Offset = start
  114 + } else if p.ch != ' ' {
  115 + s := fmt.Sprintf("symbol error: unknown '%v', pos [%v:]\n%s",
  116 + string(p.ch),
  117 + start,
  118 + ErrPos(p.Source, start))
  119 + p.err = errors.New(s)
  120 + }
  121 + case
  122 + '0',
  123 + '1',
  124 + '2',
  125 + '3',
  126 + '4',
  127 + '5',
  128 + '6',
  129 + '7',
  130 + '8',
  131 + '9':
  132 + for p.isDigitNum(p.ch) && p.nextCh() == nil {
  133 + if (p.ch == '-' || p.ch == '+') && p.SourceRunes[p.offset-1] != 'e' {
  134 + break
  135 + }
  136 + }
  137 + tok = &Token{
  138 + Tok: strings.ReplaceAll(string(p.SourceRunes[start:p.offset]), "_", ""),
  139 + Type: Literal,
  140 + }
  141 + tok.Offset = start
  142 + case '"':
  143 + for (p.isDigitNum(p.ch) || p.isChar(p.ch) || p.isCompareWordChar(p.ch) || p.ch == '*') && p.nextCh() == nil {
  144 + if p.ch == '"' {
  145 + break
  146 + }
  147 + }
  148 + err = p.nextCh()
  149 + tok = &Token{
  150 + Tok: string(p.SourceRunes[start:p.offset]),
  151 + Type: StringArgs,
  152 + }
  153 + tok.Offset = start
  154 + case ',':
  155 + tok = &Token{
  156 + Tok: string(p.ch),
  157 + Type: COMMA,
  158 + }
  159 + tok.Offset = start
  160 + err = p.nextCh()
  161 +
  162 + default:
  163 + if p.isChar(p.ch) {
  164 + for p.isWordChar(p.ch) && p.nextCh() == nil {
  165 + }
  166 + tok = &Token{
  167 + Tok: string(p.SourceRunes[start:p.offset]),
  168 + Type: Identifier,
  169 + }
  170 + tok.Offset = start
  171 + } else if p.ch != ' ' {
  172 + s := fmt.Sprintf("symbol error: unknown '%v', pos [%v:]\n%s",
  173 + string(p.ch),
  174 + start,
  175 + ErrPos(p.Source, start))
  176 + p.err = errors.New(s)
  177 + }
  178 + }
  179 + return tok
  180 +}
  181 +
  182 +func (p *Parser) nextCh() error {
  183 + p.offset++
  184 + if p.offset < len(p.SourceRunes) {
  185 + p.ch = p.SourceRunes[p.offset]
  186 + return nil
  187 + }
  188 + return errors.New("EOF")
  189 +}
  190 +
  191 +func (p *Parser) isWhitespace(c rune) bool {
  192 + return c == ' ' ||
  193 + c == '\t' ||
  194 + c == '\n' ||
  195 + c == '\v' ||
  196 + c == '\f' ||
  197 + c == '\r'
  198 +}
  199 +
  200 +func (p *Parser) isDigitNum(c rune) bool {
  201 + return '0' <= c && c <= '9' || c == '.' || c == '_' || c == 'e' || c == '-' || c == '+'
  202 +}
  203 +
  204 +func (p *Parser) isChar(c rune) bool {
  205 + return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '.' || c == '"' || isChineseCharacter(c)
  206 + //判断是汉字
  207 +}
  208 +
  209 +func (p *Parser) isWordChar(c rune) bool {
  210 + return p.isChar(c) || '0' <= c && c <= '9'
  211 +}
  212 +
  213 +func (p *Parser) isCompareWordChar(c rune) bool {
  214 + return c == '=' || c == '<' || c == '>'
  215 +}
  216 +
  217 +func isChineseCharacter(c rune) bool {
  218 + return len([]byte(string(c))) > 2
  219 +}
  1 +package astexpr
  2 +
  3 +import (
  4 + "bytes"
  5 + "encoding/json"
  6 + "fmt"
  7 + "testing"
  8 +)
  9 +
  10 +func TestExecA(t *testing.T) {
  11 + exp := "1+2"
  12 + exec(exp)
  13 +}
  14 +
  15 +func TestExecB(t *testing.T) {
  16 + exp := "1+2-4"
  17 + exec(exp)
  18 +}
  19 +
  20 +func TestExecC(t *testing.T) {
  21 + exp := "1+2-4*3-8"
  22 + exec(exp)
  23 +}
  24 +
  25 +func TestExecD(t *testing.T) {
  26 + exp := "1+2-(4*3-8)"
  27 + exec(exp)
  28 +}
  29 +
  30 +func TestExecE(t *testing.T) {
  31 + exp := "1+2-(4*3+(1-8))"
  32 + exec(exp)
  33 +}
  34 +
  35 +func TestExecF(t *testing.T) {
  36 + exp := "1+(2-(4*3+(1-8)))"
  37 + exec(exp)
  38 +}
  39 +
  40 +func TestExecG(t *testing.T) {
  41 + exp := "((1-2)*(3-8))*((((9+2222))))"
  42 + exec(exp)
  43 +}
  44 +
  45 +func TestExecH(t *testing.T) {
  46 + exp := "0.8888-0.1 * 444 -0.2"
  47 + exec(exp)
  48 +}
  49 +
  50 +func TestExecI(t *testing.T) {
  51 + exp := "0.8888-0.1 * (444 -0.2)"
  52 + exec(exp)
  53 +}
  54 +
  55 +func TestExecJ(t *testing.T) {
  56 + exp := "1_234_567*2-3"
  57 + exec(exp)
  58 +}
  59 +
  60 +func TestExecK(t *testing.T) {
  61 + exp := "2.3e4*4/3"
  62 + exec(exp)
  63 +}
  64 +
  65 +func TestExecL(t *testing.T) {
  66 + exp := "-1+9-88"
  67 + exec(exp)
  68 +}
  69 +
  70 +func TestExecM(t *testing.T) {
  71 + exp := "-1+9-88+(88)"
  72 + exec(exp)
  73 +}
  74 +
  75 +func TestExecN(t *testing.T) {
  76 + exp := "-1+9-88+(-88)*666-1"
  77 + exec(exp)
  78 +}
  79 +
  80 +func TestExecO(t *testing.T) {
  81 + exp := "-(1)+(3)-(-3)*7-((-3))"
  82 + exec(exp)
  83 +}
  84 +
  85 +func TestExecP(t *testing.T) {
  86 + exp := "-(-9+3)"
  87 + exec(exp)
  88 +}
  89 +
  90 +func TestExecQ(t *testing.T) {
  91 + exp := "2e-3*2+2e2+1"
  92 + exec(exp)
  93 +}
  94 +
  95 +func TestExecR(t *testing.T) {
  96 + exp := "3.8 - 56 / (1-1) - 4"
  97 + exec(exp)
  98 +}
  99 +
  100 +func TestExecS(t *testing.T) {
  101 + exp := "noerr(3.8 - 56 / (1-1) - 4)"
  102 + exec(exp)
  103 +}
  104 +
  105 +func TestFunCaller(t *testing.T) {
  106 + funs := []struct {
  107 + Name string
  108 + Argc int
  109 + Fun func(expr ...ExprAST) float64
  110 + Exp string
  111 + R float64
  112 + }{
  113 + //{
  114 + // "double",
  115 + // 1,
  116 + // func(expr ...engine.ExprAST) float64 {
  117 + // return engine.ExprASTResult(expr[0]) * 2
  118 + // },
  119 + // "double(6)",
  120 + // 12,
  121 + //},
  122 + {
  123 + "sum",
  124 + -1,
  125 + nil,
  126 + "sum(if(100+10,table.a,20))",
  127 + 10,
  128 + },
  129 + {
  130 + "sum",
  131 + -1,
  132 + nil,
  133 + "sum(if(100<10,table.a,20))",
  134 + 10,
  135 + },
  136 + {
  137 + "sum",
  138 + -1,
  139 + nil,
  140 + "sum(if(100<10,table.a,20+30))",
  141 + 10,
  142 + },
  143 + {
  144 + "sum",
  145 + -1,
  146 + nil,
  147 + "sum(if(table.a<table.b,table.a,20+30))",
  148 + 10,
  149 + },
  150 + {
  151 + "sum",
  152 + -1,
  153 + nil,
  154 + "sum(if(table.a<=table.b,table.a,20+30))",
  155 + 10,
  156 + },
  157 + }
  158 + for _, f := range funs {
  159 + if f.Fun != nil {
  160 + _ = RegFunction(f.Name, f.Argc, f.Fun)
  161 + }
  162 + r, err := Parse(f.Exp)
  163 + if err != nil {
  164 +
  165 + }
  166 + if r != 0 {
  167 +
  168 + }
  169 + }
  170 +}
  171 +
  172 +func TestFunCaller2(t *testing.T) {
  173 + funs := []struct {
  174 + Name string
  175 + Exp []string
  176 + }{
  177 + {
  178 + "sum",
  179 + []string{"sum(table.a)"},
  180 + },
  181 + {
  182 + "sumif",
  183 + []string{"sumif(table.month,10,table.count)"},
  184 + },
  185 + {
  186 + "if",
  187 + []string{"if(table.month>10,table.count1,table.count2)"},
  188 + },
  189 + {
  190 + "and",
  191 + []string{"and(table.year=2011,table.month=6)"},
  192 + },
  193 + {
  194 + "or",
  195 + []string{"or(table.year=2011,table.year=2012)"},
  196 + },
  197 + {
  198 + "month",
  199 + []string{"month(\"1991-1-1\")"},
  200 + },
  201 + {
  202 + "year",
  203 + []string{"year(\"1991-1-1\")"},
  204 + },
  205 + {
  206 + "round",
  207 + []string{
  208 + "round(1.56)",
  209 + "round(table.a)",
  210 + },
  211 + },
  212 + {
  213 + "rounddown",
  214 + []string{
  215 + "rounddown(1.56)",
  216 + "rounddown(table.a)",
  217 + },
  218 + },
  219 + {
  220 + "roundup",
  221 + []string{
  222 + "roundup(1.56)",
  223 + "roundup(table.a)",
  224 + },
  225 + },
  226 + {
  227 + "count",
  228 + []string{
  229 + "count(1.56)",
  230 + "count(table.a)",
  231 + },
  232 + },
  233 + {
  234 + "&",
  235 + []string{
  236 + "table.a&table.b",
  237 + },
  238 + },
  239 + }
  240 + for _, f := range funs {
  241 + for _, exp := range f.Exp {
  242 + r, err := Parse(exp)
  243 + if err != nil {
  244 + t.Error(err)
  245 + }
  246 + if r != 0 {
  247 +
  248 + }
  249 + }
  250 + }
  251 +}
  252 +
  253 +func Parse(s string) (r float64, err error) {
  254 + toks, err := ParseToken(s)
  255 + if err != nil {
  256 + return 0, err
  257 + }
  258 + ast := NewAST(toks, s)
  259 + if ast.Err != nil {
  260 + return 0, ast.Err
  261 + }
  262 + ar := ast.ParseExpression()
  263 + if ast.Err != nil {
  264 + return 0, ast.Err
  265 + }
  266 + defer func() {
  267 + if e := recover(); e != nil {
  268 + err = e.(error)
  269 + }
  270 + }()
  271 + buf := bytes.NewBuffer(nil)
  272 + enc := json.NewEncoder(buf)
  273 + enc.SetEscapeHTML(false)
  274 + enc.Encode(ar)
  275 + // if ar != nil {
  276 + // fmt.Printf("ExprAST: %+v\n", ar)
  277 + // }
  278 + fmt.Println(buf.String())
  279 + return 0, err
  280 +}
  281 +
  282 +// call engine
  283 +func exec(exp string) {
  284 + // input text -> []token
  285 + toks, err := ParseToken(exp)
  286 + if err != nil {
  287 + fmt.Println("ERROR: " + err.Error())
  288 + return
  289 + }
  290 +
  291 + // []token -> AST Tree
  292 + ast := NewAST(toks, exp)
  293 + if ast.Err != nil {
  294 + fmt.Println("ERROR: " + ast.Err.Error())
  295 + return
  296 + }
  297 + // AST builder
  298 + ar := ast.ParseExpression()
  299 + if ast.Err != nil {
  300 + fmt.Println("ERROR: " + ast.Err.Error())
  301 + return
  302 + }
  303 + fmt.Printf("ExprAST: %+v\n", ar)
  304 + // catch runtime errors
  305 + defer func() {
  306 + if e := recover(); e != nil {
  307 + fmt.Println("ERROR: ", e)
  308 + }
  309 + }()
  310 + // AST traversal -> result
  311 + r := ExprASTResult(ar)
  312 + fmt.Println("progressing ...\t", r)
  313 + fmt.Printf("%s = %v\n", exp, r)
  314 +}
  1 +package astexpr
  2 +
  3 +import (
  4 + "errors"
  5 + "fmt"
  6 + "math"
  7 + "math/big"
  8 + "strconv"
  9 + "strings"
  10 +)
  11 +
  12 +// Top level function
  13 +// Analytical expression and execution
  14 +// err is not nil if an error occurs (including arithmetic runtime errors)
  15 +func ParseAndExec(s string) (r float64, err error) {
  16 + toks, err := ParseToken(s)
  17 + if err != nil {
  18 + return 0, err
  19 + }
  20 + ast := NewAST(toks, s)
  21 + if ast.Err != nil {
  22 + return 0, ast.Err
  23 + }
  24 + ar := ast.ParseExpression()
  25 + if ast.Err != nil {
  26 + return 0, ast.Err
  27 + }
  28 + defer func() {
  29 + if e := recover(); e != nil {
  30 + err = e.(error)
  31 + }
  32 + }()
  33 + return ExprASTResult(ar), err
  34 +}
  35 +
  36 +func ErrPos(s string, pos int) string {
  37 + r := strings.Repeat("-", len(s)) + "\n"
  38 + s += "\n"
  39 + for i := 0; i < pos; i++ {
  40 + s += " "
  41 + }
  42 + s += "^\n"
  43 + return r + s + r
  44 +}
  45 +
  46 +// the integer power of a number
  47 +func Pow(x float64, n float64) float64 {
  48 + return math.Pow(x, n)
  49 +}
  50 +
  51 +func expr2Radian(expr ExprAST) float64 {
  52 + r := ExprASTResult(expr)
  53 + if TrigonometricMode == AngleMode {
  54 + r = r / 180 * math.Pi
  55 + }
  56 + return r
  57 +}
  58 +
  59 +// Float64ToStr float64 -> string
  60 +func Float64ToStr(f float64) string {
  61 + return strconv.FormatFloat(f, 'f', -1, 64)
  62 +}
  63 +
  64 +// RegFunction is Top level function
  65 +// register a new function to use in expressions
  66 +// name: be register function name. the same function name only needs to be registered once.
  67 +// argc: this is a number of parameter signatures. should be -1, 0, or a positive integer
  68 +//
  69 +// -1 variable-length argument; >=0 fixed numbers argument
  70 +//
  71 +// fun: function handler
  72 +func RegFunction(name string, argc int, fun func(...ExprAST) float64) error {
  73 + if len(name) == 0 {
  74 + return errors.New("RegFunction name is not empty")
  75 + }
  76 + if argc < -1 {
  77 + return errors.New("RegFunction argc should be -1, 0, or a positive integer")
  78 + }
  79 + if _, ok := defFunc[name]; ok {
  80 + return errors.New("RegFunction name is already exist")
  81 + }
  82 + defFunc[name] = defS{argc, fun}
  83 + return nil
  84 +}
  85 +
  86 +// ExprASTResult is a Top level function
  87 +// AST traversal
  88 +// if an arithmetic runtime error occurs, a panic exception is thrown
  89 +func ExprASTResult(expr ExprAST) float64 {
  90 + var l, r float64
  91 + switch expr.(type) {
  92 + case BinaryExprAST:
  93 + ast := expr.(BinaryExprAST)
  94 + l = ExprASTResult(ast.Lhs)
  95 + r = ExprASTResult(ast.Rhs)
  96 + switch ast.Op {
  97 + case "+":
  98 + lh, _ := new(big.Float).SetString(Float64ToStr(l))
  99 + rh, _ := new(big.Float).SetString(Float64ToStr(r))
  100 + f, _ := new(big.Float).Add(lh, rh).Float64()
  101 + return f
  102 + case "-":
  103 + lh, _ := new(big.Float).SetString(Float64ToStr(l))
  104 + rh, _ := new(big.Float).SetString(Float64ToStr(r))
  105 + f, _ := new(big.Float).Sub(lh, rh).Float64()
  106 + return f
  107 + case "*":
  108 + f, _ := new(big.Float).Mul(new(big.Float).SetFloat64(l), new(big.Float).SetFloat64(r)).Float64()
  109 + return f
  110 + case "/":
  111 + if r == 0 {
  112 + panic(errors.New(
  113 + fmt.Sprintf("violation of arithmetic specification: a division by zero in ExprASTResult: [%g/%g]",
  114 + l,
  115 + r)))
  116 + }
  117 + f, _ := new(big.Float).Quo(new(big.Float).SetFloat64(l), new(big.Float).SetFloat64(r)).Float64()
  118 + return f
  119 + case "%":
  120 + if r == 0 {
  121 + panic(errors.New(
  122 + fmt.Sprintf("violation of arithmetic specification: a division by zero in ExprASTResult: [%g%%%g]",
  123 + l,
  124 + r)))
  125 + }
  126 + return float64(int(l) % int(r))
  127 + case "^":
  128 + return Pow(l, r)
  129 + default:
  130 +
  131 + }
  132 + case NumberExprAST:
  133 + return expr.(NumberExprAST).Val
  134 + case FunCallerExprAST:
  135 + f := expr.(FunCallerExprAST)
  136 + def := defFunc[f.Name]
  137 + return def.fun(f.Args...)
  138 + }
  139 +
  140 + return 0.0
  141 +}
  1 +package astexpr
  2 +
  3 +import (
  4 + "math/rand"
  5 + "testing"
  6 + "time"
  7 +)
  8 +
  9 +func TestParseAndExecSimple(t *testing.T) {
  10 + type U struct {
  11 + Expr string
  12 + R float64
  13 + }
  14 + exprs := []U{
  15 + {"1", 1},
  16 + {"--1", 1},
  17 + {"1+2", 3},
  18 + {"-1+2", 1},
  19 + {"-(1+2)", -3},
  20 + {"-(1+2)*5", -15},
  21 + {"-(1+2)*5/3", -5},
  22 + {"1+(-(1+2)*5/3)", -4},
  23 + {"3^4", 81},
  24 + {"3^4.5", 140.29611541307906},
  25 + {"3.5^4.5", 280.7412308013823},
  26 + {"8%2", 0},
  27 + {"8%3", 2},
  28 + {"8%3.5", 2},
  29 + {"1e2", 100},
  30 + {"1e+2", 100},
  31 + {"1e-2", 0.01},
  32 + {"1e-2+1e2", 100.01},
  33 + {"1e-2+1e2*6/3", 200.01},
  34 + {"(1e-2+1e2)*6/3", 200.02},
  35 + {"(88*8)+(1+1+1+1)+(6/1.5)-(99%9*(2^4))", 712},
  36 + {"1/3*3", 1},
  37 + {"123_456_789", 123456789},
  38 + {"123_456_789___", 123456789},
  39 + {"pi", 3.141592653589793},
  40 + {"abs(1)", 1},
  41 + {"abs(-1)", 1},
  42 + {"ceil(90.2)", 91},
  43 + {"ceil(90.8)", 91},
  44 + {"ceil(90.0)", 90},
  45 + {"floor(90.2)", 90},
  46 + {"floor(90.8)", 90},
  47 + {"floor(90.0)", 90},
  48 + {"round(90.0)", 90},
  49 + {"round(90.4)", 90},
  50 + {"round(90.5)", 91},
  51 + {"round(90.9)", 91},
  52 + {"sqrt(4)", 2},
  53 + {"cbrt(27)", 3},
  54 + {"sqrt(4) + cbrt(27)", 5},
  55 + {"sqrt(2^2) + cbrt(3^3)", 5},
  56 + {"127^2+5/2-sqrt(2^2) + cbrt(3^3)", 16132.5},
  57 + {"max(2)", 2},
  58 + {"max(abs(1)+10)", 11},
  59 + {"max(abs(1)+10)*2-1", 21},
  60 + {"max(2,3.5)", 3.5},
  61 + {"max(2^3,3+abs(-1)*6)", 9},
  62 + {"max(2^3,3+abs(-1)*6, 20)", 20},
  63 + {"max(2^3,3+abs(-1)*6,ceil(9.4))", 10},
  64 + {"max(1,2,3,4,5,6,10,7,4,5,6,9.8)", 10},
  65 + {"min(3.5)", 3.5},
  66 + {"min(ceil(1.2))", 2},
  67 + {"min(2,3.5)", 2},
  68 + {"min(2^3,3+abs(-1)*6)", 8},
  69 + {"min(2^3,3+abs(-1)*6,1^10)", 1},
  70 + {"min(99.1,0.2,3,4,5,6,10,7,4,5,6,9.8)", 0.2},
  71 + {"max(2^3,3^2)", 9},
  72 + {"min(2^3,3^2)", 8},
  73 + {"noerr(1/0)", 0},
  74 + {"noerr(1/(1-1))", 0},
  75 + {"0.1+0.2", 0.3},
  76 + {"0.3-0.1", 0.2},
  77 + {"10^-1", 0.1},
  78 + {"10^-2", 0.01},
  79 + {"10^-1*100", 10},
  80 + {"10%0", 0},
  81 + }
  82 + for _, e := range exprs {
  83 + r, _ := ParseAndExec(e.Expr)
  84 + if r != e.R {
  85 + t.Error(e, " ParseAndExec:", r)
  86 + }
  87 + }
  88 +}
  89 +
  90 +func TestParseAndExecTrigonometric(t *testing.T) {
  91 + type U struct {
  92 + Expr string
  93 + RadianMode float64
  94 + AngleMode float64
  95 + }
  96 + exprs := []U{
  97 + {"sin(pi/2)", 1, 0.027412133592044294},
  98 + {"csc(pi/2)", 1, 36.48019577324057},
  99 + {"cos(0)", 1, 1},
  100 + {"sec(0)", 1, 1},
  101 + {"tan(pi/4)", 1, 0.013708642534394057},
  102 + {"cot(pi/4)", 1, 72.94668290394674},
  103 +
  104 + {"sin(90)", 0.893996663600558, 1},
  105 + {"csc(90)", 1.1185724071637082, 1},
  106 + {"cos(0)", 1, 1},
  107 + {"sec(0)", 1, 1},
  108 + {"tan(45)", 1.6197751905438615, 1},
  109 + {"cot(45)", 0.6173696237835551, 1},
  110 + }
  111 + for _, e := range exprs {
  112 + TrigonometricMode = RadianMode
  113 + r, _ := ParseAndExec(e.Expr)
  114 + if r != e.RadianMode {
  115 + t.Error(e, " ParseAndExec RadianMode:", r)
  116 + }
  117 + TrigonometricMode = AngleMode
  118 + r, _ = ParseAndExec(e.Expr)
  119 + if r != e.AngleMode {
  120 + t.Error(e, " ParseAndExec AngleMode:", r)
  121 + }
  122 + }
  123 +}
  124 +
  125 +func TestRegFunction(t *testing.T) {
  126 + funs := []struct {
  127 + Name string
  128 + Argc int
  129 + Fun func(expr ...ExprAST) float64
  130 + Exp string
  131 + R float64
  132 + }{
  133 + {
  134 + "double",
  135 + 1,
  136 + func(expr ...ExprAST) float64 {
  137 + return ExprASTResult(expr[0]) * 2
  138 + },
  139 + "double(6)",
  140 + 12,
  141 + },
  142 + {
  143 + "percentage50",
  144 + 1,
  145 + func(expr ...ExprAST) float64 {
  146 + return ExprASTResult(expr[0]) / 2
  147 + },
  148 + "percentage50(6)",
  149 + 3,
  150 + },
  151 + {
  152 + "range",
  153 + 0,
  154 + func(expr ...ExprAST) float64 {
  155 + return 10.0
  156 + },
  157 + "range()",
  158 + 10,
  159 + },
  160 + {
  161 + "choice",
  162 + -1,
  163 + func(expr ...ExprAST) float64 {
  164 + rand.Seed(time.Now().UnixNano())
  165 + return ExprASTResult(expr[rand.Intn(len(expr))])
  166 + },
  167 + "choice(1.1, 9.8, 2.5, 100)",
  168 + 10,
  169 + },
  170 + }
  171 + for _, f := range funs {
  172 + _ = RegFunction(f.Name, f.Argc, f.Fun)
  173 + r, err := ParseAndExec(f.Exp)
  174 + if f.Name == "choice" {
  175 + if !inSlices(r, []float64{1.1, 9.8, 2.5, 100}) {
  176 + t.Error(err, "RegFunction errors when register new function: ", f.Name)
  177 + }
  178 + continue
  179 + } else if r != f.R {
  180 + t.Error(err, "RegFunction errors when register new function: ", f.Name)
  181 + }
  182 + }
  183 +
  184 +}
  185 +
  186 +func TestParseAndExecError(t *testing.T) {
  187 + exprs := []string{
  188 + "(",
  189 + "((((((",
  190 + "((xscdfddff",
  191 + "(1",
  192 + "(1+",
  193 + "1+",
  194 + "1*",
  195 + "+2344",
  196 + "3+(",
  197 + "4+(90-",
  198 + "3-(4*7-2)+",
  199 + "3-(4*7-2)+98*",
  200 + "1#1",
  201 + "_123_456_789___",
  202 + "1ee3+3",
  203 + "sin()",
  204 + "sin",
  205 + "pi(",
  206 + "sin(1, 50)",
  207 + "max",
  208 + "max()",
  209 + "max(1,)",
  210 + "max(1,4,6,7,5,)",
  211 + "min",
  212 + "min(,)",
  213 + "min()",
  214 + "min(1,)",
  215 + "min(1,998,4,23,234,2,)",
  216 + "min(1,998,4,23,234,2,,,)",
  217 + "1/0",
  218 + "99.9 / (2-1-1)",
  219 + "(1+2)3",
  220 + "1+1 111",
  221 + "1+1 111+2",
  222 + "1 3",
  223 + "1 3-",
  224 + }
  225 + for _, e := range exprs {
  226 + _, err := ParseAndExec(e)
  227 + if err == nil {
  228 + t.Error(e, " this is error expr!")
  229 + }
  230 + }
  231 +}
  232 +
  233 +func inSlices(target float64, s []float64) bool {
  234 + for _, v := range s {
  235 + if v == target {
  236 + return true
  237 + }
  238 + }
  239 + return false
  240 +}
@@ -160,7 +160,7 @@ func (t TableType) ToString() string { @@ -160,7 +160,7 @@ func (t TableType) ToString() string {
160 } 160 }
161 161
162 func (t TableType) TableStatusEditable() bool { 162 func (t TableType) TableStatusEditable() bool {
163 - return t == SchemaTable || t == CalculateItem || t == CalculateTable || t == CalculateSet 163 + return t == SchemaTable || t == CalculateItem || t == CalculateSet
164 } 164 }
165 165
166 func (t TableType) TableHasGroup() bool { 166 func (t TableType) TableHasGroup() bool {
@@ -28,6 +28,16 @@ type Field struct { @@ -28,6 +28,16 @@ type Field struct {
28 Order string `json:"order,omitempty"` 28 Order string `json:"order,omitempty"`
29 } 29 }
30 30
  31 +func (f *Field) SqlTypeEqual(compare *Field) bool {
  32 + if f.SQLType == compare.SQLType {
  33 + return true
  34 + }
  35 + if SQLType(f.SQLType).IsString() == SQLType(compare.SQLType).IsString() {
  36 + return true
  37 + }
  38 + return false
  39 +}
  40 +
31 func (f *Field) Valid() error { 41 func (f *Field) Valid() error {
32 if _, ok := SQLTypeMap[strings.ToUpper(f.SQLType)]; !ok { 42 if _, ok := SQLTypeMap[strings.ToUpper(f.SQLType)]; !ok {
33 return fmt.Errorf("unknown sql type:%v", f.SQLType) 43 return fmt.Errorf("unknown sql type:%v", f.SQLType)
@@ -133,6 +133,25 @@ func (querySet *QuerySet) GetDependencyTables(queryComponents []*QueryComponent) @@ -133,6 +133,25 @@ func (querySet *QuerySet) GetDependencyTables(queryComponents []*QueryComponent)
133 return res 133 return res
134 } 134 }
135 135
  136 +func (querySet *QuerySet) Valid(queryComponents []*QueryComponent) error {
  137 + switch querySet.Type {
  138 + case CalculateTable.ToString():
  139 + if len(queryComponents) == 0 {
  140 + return fmt.Errorf("行、值不能同时为空")
  141 + }
  142 + qc := queryComponents[0]
  143 + set := collection.NewSet()
  144 + for _, f := range qc.Aggregation.AggregationFields() {
  145 + if !set.Contains(f.DisplayName) {
  146 + set.AddStr(f.DisplayName)
  147 + } else {
  148 + return fmt.Errorf("字段'%s'存在重名,请进行重命名", f.DisplayName)
  149 + }
  150 + }
  151 + }
  152 + return nil
  153 +}
  154 +
136 type QuerySets []*QuerySet 155 type QuerySets []*QuerySet
137 156
138 func (querySets QuerySets) ToMap() map[int]*QuerySet { 157 func (querySets QuerySets) ToMap() map[int]*QuerySet {
@@ -67,7 +67,7 @@ func (table *Table) TableIdString() string { @@ -67,7 +67,7 @@ func (table *Table) TableIdString() string {
67 67
68 func (table *Table) WithContext(ctx *Context) *Table { 68 func (table *Table) WithContext(ctx *Context) *Table {
69 rand.Seed(time.Now().Unix()) 69 rand.Seed(time.Now().Unix())
70 - table.SQLName = fmt.Sprintf("%v_t%v_c%v", limitStringLen(table.SQLName, 40), rand.Intn(1000000), limitStringLen(fmt.Sprintf("%d", ctx.CompanyId), 8)) 70 + table.SQLName = fmt.Sprintf("%v_t%v_c%v", limitStringLen(table.SQLName, 30), rand.Intn(1000000), limitStringLen(fmt.Sprintf("%d", ctx.CompanyId), 8))
71 table.Context = ctx 71 table.Context = ctx
72 return table 72 return table
73 } 73 }
@@ -188,7 +188,7 @@ func NewTableAppendRequest(param domain.ReqAppendData) TableAppendRequest { @@ -188,7 +188,7 @@ func NewTableAppendRequest(param domain.ReqAppendData) TableAppendRequest {
188 OriginalTableId: intToString(param.FileId), 188 OriginalTableId: intToString(param.FileId),
189 CheckoutTableFileUrl: param.FileUrl, 189 CheckoutTableFileUrl: param.FileUrl,
190 DatabaseTableName: param.Table.SQLName, 190 DatabaseTableName: param.Table.SQLName,
191 - ColumnSchemas: DomainFieldsToColumnSchemas(param.ExcelTable.DataFields), 191 + ColumnSchemas: DomainFieldsToColumnSchemas(param.From), //param.ExcelTable.DataFields
192 FieldSchemas: ToFieldSchemas(param.Table.DataFields), 192 FieldSchemas: ToFieldSchemas(param.Table.DataFields),
193 SchemaMap: make(map[string]domain.ColumnSchema), 193 SchemaMap: make(map[string]domain.ColumnSchema),
194 } 194 }
@@ -118,6 +118,9 @@ func (d *FilePreviewDto) Load(fileId int, m *domain.DataLoadDataTable, file *red @@ -118,6 +118,9 @@ func (d *FilePreviewDto) Load(fileId int, m *domain.DataLoadDataTable, file *red
118 if s == "<NA>" { 118 if s == "<NA>" {
119 return "" 119 return ""
120 } 120 }
  121 + if s == "nan" {
  122 + return ""
  123 + }
121 return s 124 return s
122 }), true) 125 }), true)
123 d.Data = domain.GripData(mapData, int64(m.Total)) 126 d.Data = domain.GripData(mapData, int64(m.Total))
@@ -8,6 +8,7 @@ import ( @@ -8,6 +8,7 @@ import (
8 "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/redis" 8 "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/redis"
9 "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/repository" 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/starrocks" 10 "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/starrocks"
  11 + "reflect"
11 "strings" 12 "strings"
12 "time" 13 "time"
13 ) 14 )
@@ -273,6 +274,9 @@ func (ptr *QuerySetService) PreviewPrepare(ctx *domain.Context, querySetId int, @@ -273,6 +274,9 @@ func (ptr *QuerySetService) PreviewPrepare(ctx *domain.Context, querySetId int,
273 if err != nil { 274 if err != nil {
274 return nil, err 275 return nil, err
275 } 276 }
  277 + if err = querySet.Valid(queryComponents); err != nil {
  278 + return nil, err
  279 + }
276 if !queryComponentsHasEdit(ctx, querySet, queryComponents) && querySet.QuerySetInfo.BindTableId > 0 { 280 if !queryComponentsHasEdit(ctx, querySet, queryComponents) && querySet.QuerySetInfo.BindTableId > 0 {
277 if t, _ := tableRepository.FindOne(map[string]interface{}{"context": ctx, "tableId": querySet.QuerySetInfo.BindTableId}); t != nil { 281 if t, _ := tableRepository.FindOne(map[string]interface{}{"context": ctx, "tableId": querySet.QuerySetInfo.BindTableId}); t != nil {
278 return t, nil 282 return t, nil
@@ -655,6 +659,29 @@ func aggregationEditLog(ctx *domain.Context, querySet *domain.QuerySet, queryCom @@ -655,6 +659,29 @@ func aggregationEditLog(ctx *domain.Context, querySet *domain.QuerySet, queryCom
655 return res 659 return res
656 } 660 }
657 661
  662 +func aggregationHasEdit(ctx *domain.Context, querySet *domain.QuerySet, queryComponents []*domain.QueryComponent) bool {
  663 + if len(queryComponents) > 0 && len(querySet.QueryComponents) > 0 {
  664 + oldQC := querySet.QueryComponents[0]
  665 + newQC := queryComponents[0]
  666 + if oldQC.Aggregation == nil || newQC.Aggregation == nil {
  667 + return false
  668 + }
  669 +
  670 + c1 := make([]string, 0)
  671 + for _, f := range oldQC.Aggregation.AggregationFields() {
  672 + c1 = append(c1, f.Expr.ExprSql)
  673 + }
  674 + c2 := make([]string, 0)
  675 + for _, f := range newQC.Aggregation.AggregationFields() {
  676 + c2 = append(c2, f.Expr.ExprSql)
  677 + }
  678 + if !reflect.DeepEqual(c1, c2) {
  679 + return true
  680 + }
  681 + }
  682 + return false
  683 +}
  684 +
658 func queryComponentsHasEdit(ctx *domain.Context, querySet *domain.QuerySet, queryComponents []*domain.QueryComponent) bool { 685 func queryComponentsHasEdit(ctx *domain.Context, querySet *domain.QuerySet, queryComponents []*domain.QueryComponent) bool {
659 logs := selectsEditLog(ctx, querySet, queryComponents) 686 logs := selectsEditLog(ctx, querySet, queryComponents)
660 if len(logs) > 0 { 687 if len(logs) > 0 {
@@ -668,6 +695,9 @@ func queryComponentsHasEdit(ctx *domain.Context, querySet *domain.QuerySet, quer @@ -668,6 +695,9 @@ func queryComponentsHasEdit(ctx *domain.Context, querySet *domain.QuerySet, quer
668 if len(logs) > 0 { 695 if len(logs) > 0 {
669 return true 696 return true
670 } 697 }
  698 + if aggregationHasEdit(ctx, querySet, queryComponents) {
  699 + return true
  700 + }
671 for _, item := range queryComponents { 701 for _, item := range queryComponents {
672 if len(item.Id) == 0 { 702 if len(item.Id) == 0 {
673 return true 703 return true
@@ -757,6 +787,9 @@ func (ptr *QuerySetService) CreateOrUpdateCalculateTable(ctx *domain.Context, qu @@ -757,6 +787,9 @@ func (ptr *QuerySetService) CreateOrUpdateCalculateTable(ctx *domain.Context, qu
757 err error 787 err error
758 foundMasterTable *domain.Table 788 foundMasterTable *domain.Table
759 ) 789 )
  790 + if err = querySet.Valid(queryComponents); err != nil {
  791 + return nil, err
  792 + }
760 dependencyTables := querySet.GetDependencyTables(queryComponents) 793 dependencyTables := querySet.GetDependencyTables(queryComponents)
761 tableRepository, _ := repository.NewTableRepository(ptr.transactionContext) 794 tableRepository, _ := repository.NewTableRepository(ptr.transactionContext)
762 foundMasterTable, err = tableRepository.FindOne(map[string]interface{}{"context": ctx, "tableId": masterTable.TableId}) 795 foundMasterTable, err = tableRepository.FindOne(map[string]interface{}{"context": ctx, "tableId": masterTable.TableId})
@@ -79,6 +79,13 @@ func (ptr *AppendDataToTableService) AppendData(ctx *domain.Context, fileId int, @@ -79,6 +79,13 @@ func (ptr *AppendDataToTableService) AppendData(ctx *domain.Context, fileId int,
79 continue 79 continue
80 } 80 }
81 } 81 }
  82 + if fromField.SQLType != "" && !toField.SqlTypeEqual(fromField) {
  83 + //return nil, fmt.Errorf("字段【%s】的类型与导入数据表的类型不匹配", toField.Name)
  84 + return map[string]interface{}{
  85 + "result": fmt.Sprintf("字段【%s】的类型与导入数据表的类型不匹配", toField.Name),
  86 + }, nil
  87 + }
  88 + fromField.SQLType = toField.SQLType // 兼容 INT BIGINT
82 requestData.To = append(requestData.To, toField) 89 requestData.To = append(requestData.To, toField)
83 requestData.From = append(requestData.From, fromField) 90 requestData.From = append(requestData.From, fromField)
84 } 91 }