123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522 |
- package calculate
- import (
- "errors"
- "math"
- "zhiyuan/pkg/db"
- "zhiyuan/pkg/logger"
- "zhiyuan/pkg/utils"
- "zhiyuan/services/admin"
- "github.com/Knetic/govaluate"
- "github.com/gin-gonic/gin"
- )
- type Call struct {
- Name string `json:"name" label:"变量名"`
- Item *OrderItem `json:"item" label:"项目"`
- }
- type Stack struct {
- OrderNumber int64 `json:"order_number"`
- Item *OrderItem `json:"item" label:"项目"`
- }
- type Context struct {
- Order *Order `json:"order" label:"当前订单"`
- OrderNumber int64 `json:"order_number"`
- Item *OrderItem `json:"item" label:"当前项目"`
- Calls []Call `json:"calls"`
- Stack []Stack `json:"stack"`
- C *gin.Context
- }
- func NewContext(order *Order, c *gin.Context) *Context {
- context := new(Context)
- context.Order = order
- context.OrderNumber = -1
- context.Calls = make([]Call, 0)
- context.Stack = make([]Stack, 0)
- context.C = c
- return context
- }
- func ToDataType(value interface{}, dataType string) (interface{}, bool) {
- if value != nil {
- switch dataType {
- case "number":
- ret, ok := db.ToFloat64(value)
- if ok {
- return ret, true
- }
- case "string":
- return db.ToString(value), true
- case "array":
- ret, ok := value.([]interface{})
- if !ok {
- var array interface{}
- utils.JsonDecode(db.ToString(value)).To(&array)
- ret, ok = db.ToArray(array)
- }
- if ok {
- return ret, true
- }
- }
- }
- return nil, false
- }
- func (context *Context) Push(item *OrderItem) {
- context.Stack = append(context.Stack, Stack{context.OrderNumber, context.Item})
- context.Item = item
- context.OrderNumber = -1
- }
- func (context *Context) Pop() {
- context.Item = context.Stack[len(context.Stack)-1].Item
- context.OrderNumber = context.Stack[len(context.Stack)-1].OrderNumber
- context.Stack = context.Stack[:len(context.Stack)-1]
- }
- func (context *Context) Get(name string) (interface{}, error) {
- for _, call := range context.Calls {
- if call.Item == context.Item && call.Name == name {
- return nil, errors.New("circular reference: " + name)
- }
- }
- context.Calls = append(context.Calls, Call{name, context.Item})
- defer func() { context.Calls = context.Calls[:len(context.Calls)-1] }()
- item := context.Item
- for ; item != nil; item = item.Parent {
- if name == "项目名称" {
- return item.Item.Name, nil
- }
- for _, prop := range item.Item.Props {
- if name == prop.Name {
- var value interface{} = nil
- switch prop.Type {
- case 0:
- value = prop.Value
- case 1:
- if _, ok := item.From.Results[prop.ID]; !ok {
- result, err := context.Exec(prop.Value, item, -1)
- if err != nil {
- return nil, err
- }
- item.From.Results[prop.ID] = result
- }
- value = item.From.Results[prop.ID]
- case 2, 3:
- value = item.From.Prop[prop.ID]
- }
- if ret, ok := ToDataType(value, prop.DataType); ok {
- return ret, nil
- }
- }
- }
- if item == context.Item && item.From.Product.ID != 0 {
- if name == "产品名称" {
- return item.From.Product.Name, nil
- }
- if item.Product != nil {
- propMap := utils.JsonDecode(item.Product.Property).ToMap()
- if _, ok := propMap[name]; ok {
- if number, ok := db.ToFloat64(propMap[name]); ok {
- return number, nil
- }
- return propMap[name], nil
- }
- }
- if _, ok := item.From.Product.Spec[name]; ok {
- return item.From.Product.Spec[name], nil
- }
- }
- }
- for _, prop := range context.Order.Calc.Props {
- if name == prop.Name {
- var value interface{} = nil
- switch prop.Type {
- case 0:
- value = prop.Value
- case 1:
- if _, ok := context.Order.From.Results[prop.ID]; !ok {
- result, err := context.Exec(prop.Value, item, -1)
- if err != nil {
- return nil, err
- }
- context.Order.From.Results[prop.ID] = result
- }
- value = context.Order.From.Results[prop.ID]
- case 2, 3:
- value = context.Order.From.Prop[prop.ID]
- }
- if ret, ok := ToDataType(value, prop.DataType); ok {
- return ret, nil
- }
- }
- }
- return nil, errors.New("undeclared name: " + name)
- }
- func (context *Context) GetArray(args ...interface{}) (interface{}, error) {
- if len(args) == 0 || len(args) > 3 {
- return nil, errors.New("not enough arguments in call to call")
- }
- var ok bool
- expr := ""
- if expr, ok = args[0].(string); !ok {
- return nil, errors.New("wrong parameter type: call")
- }
- cond := ""
- if len(args) >= 2 {
- if cond, ok = args[1].(string); !ok {
- return nil, errors.New("wrong parameter type: call")
- }
- }
- inChild := false
- if len(args) >= 3 {
- if inChild, ok = args[2].(bool); !ok {
- return nil, errors.New("wrong parameter type: call")
- }
- }
- items := context.Order.Items
- if context.Item != nil {
- items = context.Item.Childs
- }
- var array []interface{}
- orderNumber := int64(0)
- for i, _ := range items {
- if inChild {
- context.Push(items[i])
- ret, err := context.GetArray(expr, cond, inChild)
- context.Pop()
- if err == nil {
- if reta, ok := ret.([]interface{}); ok {
- array = append(array, reta...)
- }
- }
- }
- if cond != "" {
- ret, err := context.Exec(cond, items[i], int64(i))
- if err != nil {
- continue
- }
- if retb, ok := ret.(bool); !ok || !retb {
- continue
- }
- }
- result, err := context.Exec(expr, items[i], orderNumber)
- orderNumber++
- if err == nil {
- array = append(array, result)
- }
- }
- return array, nil
- }
- func (context *Context) ToFloat(value interface{}) float64 {
- ret, ok := db.ToFloat64(value)
- if ok {
- return ret
- }
- return 0
- }
- func (context *Context) Exec(expression string, item *OrderItem, orderNumber int64) (interface{}, error) {
- if item != context.Item {
- context.Push(item)
- context.OrderNumber = orderNumber
- defer context.Pop()
- }
- return context.EvaluableExpression(expression)
- }
- func (context *Context) EvaluableExpression(expression string) (interface{}, error) {
- functions := map[string]govaluate.ExpressionFunction{
- "call": context.GetArray,
- "sum": func(args ...interface{}) (interface{}, error) {
- array, err := context.GetArray(args...)
- if err != nil {
- return nil, err
- }
- var result float64
- for _, item := range array.([]interface{}) {
- result += context.ToFloat(item)
- }
- return result, nil
- },
- "len": func(args ...interface{}) (interface{}, error) {
- return len(args), nil
- },
- "index": func(args ...interface{}) (interface{}, error) {
- if len(args) < 2 {
- return nil, errors.New("not enough arguments in call to call")
- }
- index := int(context.ToFloat(args[len(args)-1]))
- if index >= len(args)-1 {
- return nil, errors.New("index out of range: index")
- }
- return args[index], nil
- },
- "string_slice": func(args ...interface{}) (interface{}, error) {
- if len(args) == 0 || len(args) > 3 {
- return nil, errors.New("not enough arguments in call to call")
- }
- str := []rune("")
- if s, ok := args[0].(string); ok {
- str = []rune(s)
- } else {
- return nil, errors.New("wrong parameter type: string_slice")
- }
- start := int(0)
- if len(args) >= 2 {
- if num, ok := args[1].(float64); ok {
- start = int(num)
- } else {
- return nil, errors.New("wrong parameter type: string_slice")
- }
- }
- if start < 0 {
- start = len(str) + start
- }
- if start < 0 {
- start = 0
- }
- if start > len(args) {
- start = len(args)
- }
- end := len(str)
- if len(args) >= 3 {
- if num, ok := args[2].(float64); ok {
- end = int(num)
- } else {
- return nil, errors.New("wrong parameter type: string_slice")
- }
- }
- if end < 0 {
- end = len(str) + end
- }
- if end < 0 {
- end = 0
- }
- if end > len(args) {
- end = len(args)
- }
- return string(str[start:end]), nil
- },
- "count": func(args ...interface{}) (interface{}, error) {
- array, err := context.GetArray(args...)
- if err != nil {
- return nil, err
- }
- return len(array.([]interface{})), nil
- },
- "sub": func(args ...interface{}) (interface{}, error) {
- var ok bool
- expr := ""
- if expr, ok = args[0].(string); !ok {
- return nil, errors.New("wrong parameter type: call")
- }
- cond := ""
- if len(args) >= 2 {
- if cond, ok = args[1].(string); !ok {
- return nil, errors.New("wrong parameter type: call")
- }
- }
- num := int64(0)
- if len(args) >= 3 {
- if _, ok = args[2].(float64); !ok {
- return nil, errors.New("wrong parameter type: call")
- }
- num = int64(args[2].(float64))
- }
- array, err := context.GetArray(expr, cond, false)
- if err != nil {
- return nil, err
- }
- if num < 0 {
- num = int64(len(array.([]interface{}))) + num
- }
- if num < 0 {
- num = 0
- }
- if num >= int64(len(array.([]interface{}))) {
- num = int64(len(array.([]interface{}))) - 1
- }
- if num < 0 {
- return 0, nil
- }
- return array.([]interface{})[num], nil
- },
- "name": func(args ...interface{}) (interface{}, error) {
- if context.Item == nil {
- return context.Order.Calc.Name, nil
- }
- return context.Item.Item.Name, nil
- },
- "product": func(args ...interface{}) (interface{}, error) {
- if context.Item.From.Product.ID != 0 {
- return context.Item.From.Product.Name, nil
- }
- return "", nil
- },
- "order": func(args ...interface{}) (interface{}, error) {
- if context.OrderNumber < 0 {
- if context.Item == nil {
- return 0, nil
- }
- return float64(context.Item.OrderNumber), nil
- }
- return float64(context.OrderNumber), nil
- },
- "unit": func(args ...interface{}) (interface{}, error) {
- var ok bool
- name := ""
- if name, ok = args[0].(string); !ok {
- return nil, errors.New("wrong parameter type: call")
- }
- item := context.Item
- for ; item != nil; item = item.Parent {
- for _, prop := range item.Item.Props {
- if name == prop.Name {
- return prop.Unit, nil
- }
- }
- }
- for _, prop := range context.Order.Calc.Props {
- if name == prop.Name {
- return prop.Unit, nil
- }
- }
- return "", nil
- },
- "author": func(args ...interface{}) (interface{}, error) {
- type AdminInfo struct {
- ID int `json:"id"`
- UserName string `json:"username"`
- Phone string `json:"phone"`
- }
- var adminInfo AdminInfo
- _, err := admin.GetInfoByID(int(context.Order.AdminId), []string{"id", "username", "phone"}, &adminInfo)
- if err != nil {
- return "", nil
- }
- return adminInfo.UserName, nil
- },
- "round": func(args ...interface{}) (interface{}, error) {
- var ok bool
- value := float64(0)
- if value, ok = args[0].(float64); !ok {
- return nil, errors.New("wrong parameter type: round")
- }
- precision := float64(0)
- if len(args) >= 2 {
- if precision, ok = args[1].(float64); !ok {
- return nil, errors.New("wrong parameter type: round")
- }
- }
- p := math.Pow10(int(precision))
- return math.Floor(value*p+0.5) / p, nil
- },
- }
- expr, err := govaluate.NewEvaluableExpressionWithFunctions(expression, functions)
- if err != nil {
- if context.Item == nil {
- logger.Sugar.Infof("expr: %v error: %v", expression, err.Error())
- } else {
- logger.Sugar.Infof("item: %v %v expr: %v error: %v", context.Item.Item.ID, context.Item.Item.Name, expression, err.Error())
- }
- return nil, err
- }
- result, err := expr.Eval(context)
- if err != nil {
- if context.Item == nil {
- logger.Sugar.Infof("expr: %v error: %v", expression, err.Error())
- } else {
- logger.Sugar.Infof("item: %v %v expr: %v error: %v", context.Item.Item.ID, context.Item.Item.Name, expression, err.Error())
- }
- return nil, err
- }
- return result, nil
- }
- func (context *Context) Eval() error {
- if context.Item == nil {
- for _, item := range context.Order.Items {
- context.Push(item)
- err := context.Eval()
- context.Pop()
- if err != nil {
- return err
- }
- }
- for _, prop := range context.Order.Calc.Props {
- if prop.Type == 1 {
- if _, ok := context.Order.From.Results[prop.ID]; !ok {
- result, err := context.EvaluableExpression(prop.Value)
- if err != nil {
- return err
- }
- if context.Order.From.Results[prop.ID], ok = ToDataType(result, prop.DataType); !ok {
- return errors.New("wrong property type")
- }
- }
- }
- }
- } else {
- for _, item := range context.Item.Childs {
- context.Push(item)
- err := context.Eval()
- context.Pop()
- if err != nil {
- return err
- }
- }
- for _, prop := range context.Item.Item.Props {
- if prop.Type == 1 {
- if _, ok := context.Item.From.Results[prop.ID]; !ok {
- result, err := context.EvaluableExpression(prop.Value)
- if err != nil {
- return err
- }
- context.Item.From.Results[prop.ID] = result
- }
- }
- }
- }
- return nil
- }
|