作者 yangfu

高级查询修改

@@ -6,7 +6,7 @@ require ( @@ -6,7 +6,7 @@ require (
6 github.com/GeeTeam/gt3-golang-sdk v0.0.0-20200116043922-446ca8a507d2 6 github.com/GeeTeam/gt3-golang-sdk v0.0.0-20200116043922-446ca8a507d2
7 github.com/beego/beego/v2 v2.0.1 7 github.com/beego/beego/v2 v2.0.1
8 github.com/dgrijalva/jwt-go v3.2.0+incompatible 8 github.com/dgrijalva/jwt-go v3.2.0+incompatible
9 - github.com/forgoer/openssl v0.0.0-20210828150411-6c5378b5b719 // indirect 9 + github.com/forgoer/openssl v0.0.0-20210828150411-6c5378b5b719
10 github.com/go-pg/pg/v10 v10.10.1 10 github.com/go-pg/pg/v10 v10.10.1
11 github.com/go-redis/redis v6.14.2+incompatible 11 github.com/go-redis/redis v6.14.2+incompatible
12 github.com/google/uuid v1.1.1 12 github.com/google/uuid v1.1.1
@@ -5,6 +5,7 @@ import ( @@ -5,6 +5,7 @@ import (
5 "github.com/linmadan/egglib-go/log/logrus" 5 "github.com/linmadan/egglib-go/log/logrus"
6 "gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/constant" 6 "gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/constant"
7 "gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/infrastructure/cache" 7 "gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/infrastructure/cache"
  8 + "gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/infrastructure/domainService"
8 "gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/log" 9 "gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/log"
9 10
10 _ "gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/port/beego" 11 _ "gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/port/beego"
@@ -12,6 +13,7 @@ import ( @@ -12,6 +13,7 @@ import (
12 13
13 func init() { 14 func init() {
14 cache.InitRedist() 15 cache.InitRedist()
  16 + domainService.AdvancedSettingInit()
15 } 17 }
16 18
17 func main() { 19 func main() {
  1 +package query
  2 +
  3 +import (
  4 + "fmt"
  5 + "github.com/beego/beego/v2/core/validation"
  6 +)
  7 +
  8 +type GetAdvancedSettingQuery struct {
  9 + //操作人
  10 + //操作人
  11 + //Operator domain.Operator `json:"-"`
  12 + Model string `json:"model"`
  13 +}
  14 +
  15 +func (q *GetAdvancedSettingQuery) Valid(validation *validation.Validation) {
  16 + if len(q.Model) == 0 {
  17 + validation.Error("模型名称不能为空")
  18 + }
  19 +}
  20 +
  21 +func (q *GetAdvancedSettingQuery) ValidateQuery() error {
  22 + valid := validation.Validation{}
  23 + b, err := valid.Valid(q)
  24 + if err != nil {
  25 + return err
  26 + }
  27 + if !b {
  28 + for _, validErr := range valid.Errors {
  29 + return fmt.Errorf("%s %s", validErr.Key, validErr.Message)
  30 + }
  31 + }
  32 + return nil
  33 +}
1 package service 1 package service
2 2
3 import ( 3 import (
  4 + "fmt"
  5 + "github.com/linmadan/egglib-go/core/application"
4 "gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/application/common/query" 6 "gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/application/common/query"
  7 + "gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/domain"
  8 + "gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/infrastructure/service_gateway/allied_creation_basic"
5 ) 9 )
6 10
7 const IOSPage = "http://fir.fjmaimaimai.com/pdvn" 11 const IOSPage = "http://fir.fjmaimaimai.com/pdvn"
@@ -16,57 +20,56 @@ func NewCommonService(options map[string]interface{}) *CommonService { @@ -16,57 +20,56 @@ func NewCommonService(options map[string]interface{}) *CommonService {
16 20
17 //GetDictionaryByCode 根据code获取字典数据 21 //GetDictionaryByCode 根据code获取字典数据
18 func (srv *CommonService) GetDictionaryByCode(getDictionaryQuery *query.GetDictionaryByCodeQuery) (interface{}, error) { 22 func (srv *CommonService) GetDictionaryByCode(getDictionaryQuery *query.GetDictionaryByCodeQuery) (interface{}, error) {
19 - //creationBasicGateway := allied_creation_basic.NewHttplibAlliedCreationBasic(domain.Operator{})  
20 - //result, err := creationBasicGateway.GetDictionarysByCode(allied_creation_basic.ReqGetDictionaryByCode{  
21 - // DictCode: getDictionaryQuery.DictCode,  
22 - //})  
23 - //if err != nil {  
24 - // return nil, err  
25 - //}  
26 - //return result, nil  
27 - // TODO:测试使用,后期移除掉  
28 - type dictItem struct {  
29 - ItemCode string `json:"itemCode"`  
30 - ItemValue string `json:"itemValue"`  
31 - }  
32 - dictionaries := make([]interface{}, 0)  
33 - for i := range getDictionaryQuery.DictCode {  
34 - switch getDictionaryQuery.DictCode[i] {  
35 - case "MenuType":  
36 - dictionaries = append(dictionaries, map[string]interface{}{  
37 - "dictName": "菜单类型",  
38 - "dictItems": []dictItem{  
39 - {"目录", "catalog"},  
40 - {"菜单", "menu"},  
41 - {"按钮", "button"},  
42 - },  
43 - "dictCode": "MenuType",  
44 - })  
45 - case "XTZD-001":  
46 - dictionaries = append(dictionaries, map[string]interface{}{  
47 - "dictName": "规模",  
48 - "dictItems": []dictItem{  
49 - {"1", "0~100人"},  
50 - {"2", "101~200人"},  
51 - {"3", "201~500人"},  
52 - {"4", "501~1000人"},  
53 - {"5", "1000人以上"},  
54 - },  
55 - "dictCode": "XTZD-001",  
56 - })  
57 - case "XTZD-002":  
58 - dictionaries = append(dictionaries, map[string]interface{}{  
59 - "dictName": "产业类型",  
60 - "dictItems": []dictItem{  
61 - {"1", "食品行业"},  
62 - {"2", "电子行业"},  
63 - {"3", "纺织业"},  
64 - },  
65 - "dictCode": "XTZD-002", 23 + creationBasicGateway := allied_creation_basic.NewHttplibAlliedCreationBasic(domain.Operator{})
  24 + result, err := creationBasicGateway.GetDictionarysByCode(allied_creation_basic.ReqGetDictionaryByCode{
  25 + DictCodes: getDictionaryQuery.DictCode,
66 }) 26 })
  27 + if err != nil {
  28 + return nil, err
67 } 29 }
68 - }  
69 - return map[string]interface{}{"dictionarys": dictionaries}, nil 30 + return result, nil
  31 + //type dictItem struct {
  32 + // ItemCode string `json:"itemCode"`
  33 + // ItemValue string `json:"itemValue"`
  34 + //}
  35 + //dictionaries := make([]interface{}, 0)
  36 + //for i := range getDictionaryQuery.DictCode {
  37 + // switch getDictionaryQuery.DictCode[i] {
  38 + // case "MenuType":
  39 + // dictionaries = append(dictionaries, map[string]interface{}{
  40 + // "dictName": "菜单类型",
  41 + // "dictItems": []dictItem{
  42 + // {"目录", "catalog"},
  43 + // {"菜单", "menu"},
  44 + // {"按钮", "button"},
  45 + // },
  46 + // "dictCode": "MenuType",
  47 + // })
  48 + // case "XTZD-001":
  49 + // dictionaries = append(dictionaries, map[string]interface{}{
  50 + // "dictName": "规模",
  51 + // "dictItems": []dictItem{
  52 + // {"1", "0~100人"},
  53 + // {"2", "101~200人"},
  54 + // {"3", "201~500人"},
  55 + // {"4", "501~1000人"},
  56 + // {"5", "1000人以上"},
  57 + // },
  58 + // "dictCode": "XTZD-001",
  59 + // })
  60 + // case "XTZD-002":
  61 + // dictionaries = append(dictionaries, map[string]interface{}{
  62 + // "dictName": "产业类型",
  63 + // "dictItems": []dictItem{
  64 + // {"1", "食品行业"},
  65 + // {"2", "电子行业"},
  66 + // {"3", "纺织业"},
  67 + // },
  68 + // "dictCode": "XTZD-002",
  69 + // })
  70 + // }
  71 + //}
  72 + //return map[string]interface{}{"dictionarys": dictionaries}, nil
70 } 73 }
71 74
72 //LatestVersionInfo 版本升级 75 //LatestVersionInfo 版本升级
@@ -115,3 +118,15 @@ func (srv *CommonService) AppSharing(q *query.GetLatestVersionQuery) (interface{ @@ -115,3 +118,15 @@ func (srv *CommonService) AppSharing(q *query.GetLatestVersionQuery) (interface{
115 //} 118 //}
116 //return data, nil 119 //return data, nil
117 } 120 }
  121 +
  122 +// AdvancedSetting 高级查询 配置
  123 +func (srv *CommonService) AdvancedSetting(q *query.GetAdvancedSettingQuery) (interface{}, error) {
  124 + if err := q.ValidateQuery(); err != nil {
  125 + return nil, application.ThrowError(application.BUSINESS_ERROR, err.Error())
  126 + }
  127 + m, ok := domain.GetModel(q.Model)
  128 + if !ok {
  129 + return nil, application.ThrowError(application.BUSINESS_ERROR, fmt.Sprintf("模型:%v 不存在", q.Model))
  130 + }
  131 + return m, nil
  132 +}
@@ -2,6 +2,7 @@ package query @@ -2,6 +2,7 @@ package query
2 2
3 import ( 3 import (
4 "fmt" 4 "fmt"
  5 + "gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/util/advance"
5 6
6 "github.com/beego/beego/v2/core/validation" 7 "github.com/beego/beego/v2/core/validation"
7 "gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/domain" 8 "gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/domain"
@@ -19,6 +20,8 @@ type CompanyUserListQuery struct { @@ -19,6 +20,8 @@ type CompanyUserListQuery struct {
19 DepartmentName string `json:"departmentName"` 20 DepartmentName string `json:"departmentName"`
20 //操作人 21 //操作人
21 Operator domain.Operator `json:"-"` 22 Operator domain.Operator `json:"-"`
  23 +
  24 + AdvancedQueries advance.AdvancedQueries `json:"advancedQueries"`
22 } 25 }
23 26
24 func (companyUserListQuery *CompanyUserListQuery) Valid(validation *validation.Validation) { 27 func (companyUserListQuery *CompanyUserListQuery) Valid(validation *validation.Validation) {
@@ -192,6 +192,7 @@ func (usersService *UsersService) CompanyUserList(companyUserListQuery *query.Co @@ -192,6 +192,7 @@ func (usersService *UsersService) CompanyUserList(companyUserListQuery *query.Co
192 UserType: domain.UserTypeEmployee, 192 UserType: domain.UserTypeEmployee,
193 InOrgIds: companyUserListQuery.Operator.OrgIds, 193 InOrgIds: companyUserListQuery.Operator.OrgIds,
194 PullRealTime: true, 194 PullRealTime: true,
  195 + AdvancedQueries: domain.AdvancedQuerySql(domain.UserModel{}.ModelName(), companyUserListQuery.AdvancedQueries),
195 }) 196 })
196 197
197 if err != nil { 198 if err != nil {
  1 +package domain
  2 +
  3 +import (
  4 + "gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/log"
  5 + "gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/util/advance"
  6 +)
  7 +
  8 +type Model struct {
  9 + Columns []advance.Column `json:"columns"`
  10 + MapColumn advance.MapColumn `json:"-"`
  11 + Name string `json:"name"`
  12 +}
  13 +
  14 +var registerModels = make(map[string]Model)
  15 +
  16 +func RegisModel(m Model) {
  17 + if _, ok := registerModels[m.Name]; ok {
  18 + panic("register modes exists:" + m.Name)
  19 + }
  20 + registerModels[m.Name] = m
  21 +}
  22 +
  23 +func AdvancedQuerySql(model string, quires advance.AdvancedQueries) string {
  24 + if len(quires) == 0 {
  25 + return ""
  26 + }
  27 + fixQueries := fixAdvanceQueries(model, quires)
  28 + sql, err := advance.AdvancedQuerySql(fixQueries)
  29 + if err != nil {
  30 + log.Logger.Error(err.Error())
  31 + }
  32 + return sql
  33 +}
  34 +
  35 +func fixAdvanceQueries(model string, quires advance.AdvancedQueries) []advance.AdvancedQuery {
  36 + m, ok := GetModel(model)
  37 + response := make([]advance.AdvancedQuery, 0)
  38 + mapResponse := make(map[string]advance.AdvancedQuery)
  39 + if !ok {
  40 + return response
  41 + }
  42 + for i := range quires {
  43 + c, ok := m.MapColumn[quires[i].Column.Column]
  44 + if !ok {
  45 + continue
  46 + }
  47 + quires[i].Column = c
  48 + if q, ok := mapResponse[c.Column]; ok {
  49 + q.Exprs = append(q.Exprs, quires[i].Exprs...)
  50 + } else {
  51 + mapResponse[c.Column] = quires[i]
  52 + }
  53 + }
  54 + for _, v := range mapResponse {
  55 + response = append(response, v)
  56 + }
  57 + return response
  58 +}
  59 +
  60 +func GetModel(name string) (Model, bool) {
  61 + m, ok := registerModels[name]
  62 + return m, ok
  63 +}
  64 +
  65 +func NewModel(m ModelInterface) Model {
  66 + return Model{
  67 + Name: m.ModelName(),
  68 + Columns: m.Columns(),
  69 + MapColumn: advance.NewMapColumn(m.Columns()),
  70 + }
  71 +}
  72 +
  73 +type ModelInterface interface {
  74 + ModelName() string
  75 + Columns() []advance.Column
  76 +}
  77 +
  78 +/*User*/
  79 +type UserModel struct{}
  80 +
  81 +func (u UserModel) ModelName() string { return "user" }
  82 +func (u UserModel) Columns() []advance.Column {
  83 + return []advance.Column{
  84 + {
  85 + Column: "userCode",
  86 + Name: "用户编号",
  87 + DbAlias: "user_code",
  88 + ValueType: advance.ValueChars,
  89 + },
  90 + {
  91 + Column: "userName",
  92 + Name: "姓名",
  93 + DbAlias: "ext->>'userName'",
  94 + ValueType: advance.ValueChars,
  95 + },
  96 + {
  97 + Column: "phone",
  98 + Name: "手机号",
  99 + DbAlias: "ext->>'phone'",
  100 + ValueType: advance.ValueChars,
  101 + },
  102 + {
  103 + Column: "depName",
  104 + Name: "所属部门",
  105 + DbAlias: "ext->>'depName'",
  106 + ValueType: advance.ValueChars,
  107 + },
  108 + {
  109 + Column: "status",
  110 + Name: "状态",
  111 + DbAlias: "enable_status",
  112 + ValueType: advance.ValueNumber,
  113 + },
  114 + {
  115 + Column: "orgName",
  116 + Name: "组织机构",
  117 + DbAlias: "ext->>'orgName'",
  118 + ValueType: advance.ValueChars,
  119 + },
  120 + }
  121 +}
@@ -8,6 +8,10 @@ import ( @@ -8,6 +8,10 @@ import (
8 "gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/log" 8 "gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/log"
9 ) 9 )
10 10
  11 +func AdvancedSettingInit() {
  12 + domain.RegisModel(domain.NewModel(domain.UserModel{}))
  13 +}
  14 +
11 //GetInitPassword 获取公司初始化密码 15 //GetInitPassword 获取公司初始化密码
12 func GetInitPassword(operator domain.Operator) (string, string, error) { 16 func GetInitPassword(operator domain.Operator) (string, string, error) {
13 var password string 17 var password string
@@ -106,6 +106,9 @@ type ( @@ -106,6 +106,9 @@ type (
106 EnableStatus int `cname:"状态(1:启用 2:禁用 3:注销)" json:"enableStatus,omitempty"` 106 EnableStatus int `cname:"状态(1:启用 2:禁用 3:注销)" json:"enableStatus,omitempty"`
107 // 状态(1:启用 2:禁用 3:注销) 107 // 状态(1:启用 2:禁用 3:注销)
108 InEnableStatus []int `cname:"状态(1:启用 2:禁用 3:注销)" json:"inEnableStatus,omitempty"` 108 InEnableStatus []int `cname:"状态(1:启用 2:禁用 3:注销)" json:"inEnableStatus,omitempty"`
  109 +
  110 + // 自定义高级查询
  111 + AdvancedQueries string `json:"advancedQueries"`
109 } 112 }
110 113
111 //DataUserSearch 搜索用户列表 114 //DataUserSearch 搜索用户列表
@@ -30,6 +30,14 @@ func (controller *CommonController) LatestVersionInfo() { @@ -30,6 +30,14 @@ func (controller *CommonController) LatestVersionInfo() {
30 controller.Response(data, err) 30 controller.Response(data, err)
31 } 31 }
32 32
  33 +func (controller *CommonController) AdvancedSetting() {
  34 + commonService := service.NewCommonService(nil)
  35 + queryParam := &query.GetAdvancedSettingQuery{}
  36 + _ = controller.Unmarshal(queryParam)
  37 + data, err := commonService.AdvancedSetting(queryParam)
  38 + controller.Response(data, err)
  39 +}
  40 +
33 func (controller *CommonController) AppSharing() { 41 func (controller *CommonController) AppSharing() {
34 commonService := service.NewCommonService(nil) 42 commonService := service.NewCommonService(nil)
35 queryParam := &query.GetLatestVersionQuery{} 43 queryParam := &query.GetLatestVersionQuery{}
@@ -7,6 +7,7 @@ import ( @@ -7,6 +7,7 @@ import (
7 7
8 func init() { 8 func init() {
9 web.Router("/v1/common/dictionary/search", &controllers.CommonController{}, "Post:GetDictionaryByCode") 9 web.Router("/v1/common/dictionary/search", &controllers.CommonController{}, "Post:GetDictionaryByCode")
  10 + web.Router("/v1/common/advanced-query/setting", &controllers.CommonController{}, "Post:AdvancedSetting")
10 web.Router("/v1/common/version/getLatestVersionInfo", &controllers.CommonController{}, "Post:LatestVersionInfo") 11 web.Router("/v1/common/version/getLatestVersionInfo", &controllers.CommonController{}, "Post:LatestVersionInfo")
11 web.Router("/v1/common/app-sharing", &controllers.CommonController{}, "Post:AppSharing") 12 web.Router("/v1/common/app-sharing", &controllers.CommonController{}, "Post:AppSharing")
12 web.Router("/log", &controllers.CommonController{}, "Get:LogData") 13 web.Router("/log", &controllers.CommonController{}, "Get:LogData")
  1 +package advance
  2 +
  3 +import (
  4 + "github.com/stretchr/testify/assert"
  5 + "sort"
  6 + "testing"
  7 +)
  8 +
  9 +var exprNumberTable = [][]Expr{
  10 + []Expr{
  11 + {OpChar: Eq, Value: []interface{}{100, 200, 600}},
  12 + //{OpChar: Eq, Value: []interface{}{500}},
  13 + {OpChar: Range, Value: []interface{}{50, 200}, LeftOp: GreaterThan, RightOp: LessThan},
  14 + {OpChar: LessThanEqual, Value: []interface{}{Infinity, 50}},
  15 + {OpChar: LessThan, Value: []interface{}{Infinity, 250}},
  16 + {OpChar: Range, Value: []interface{}{60, 100}},
  17 + {OpChar: Range, Value: []interface{}{60, 70}},
  18 + {OpChar: NotEqual, Value: []interface{}{60, 61, 62}},
  19 + },
  20 + []Expr{
  21 + {OpChar: Range, Value: []interface{}{100, 200}, LeftOp: GreaterThan, RightOp: LessThanEqual},
  22 + {OpChar: Range, Value: []interface{}{Infinity, 50}, LeftOp: GreaterThan, RightOp: LessThanEqual},
  23 + {OpChar: Range, Value: []interface{}{50, 90}, LeftOp: GreaterThan, RightOp: LessThanEqual},
  24 + {OpChar: Range, Value: []interface{}{150, 300}, LeftOp: GreaterThan, RightOp: LessThanEqual},
  25 + },
  26 +}
  27 +
  28 +var exprDateTable = [][]Expr{
  29 + []Expr{
  30 + {OpChar: Range, Value: []interface{}{1611731000, 1611735000}},
  31 + {OpChar: LessThanEqual, Value: []interface{}{Infinity, 1611721000}},
  32 + {OpChar: Range, Value: []interface{}{1611734000, 1611737000}},
  33 + },
  34 + []Expr{
  35 + {OpChar: Range, Value: []interface{}{1611731000, 1611735000}},
  36 + {OpChar: LessThanEqual, Value: []interface{}{Infinity, 1611721000}},
  37 + {OpChar: Range, Value: []interface{}{1611734000, 1611737000}},
  38 + {OpChar: Recent, Value: []interface{}{5}},
  39 + },
  40 +}
  41 +
  42 +var exprCharsTable = [][]Expr{
  43 + []Expr{
  44 + {OpChar: In, Value: []interface{}{"abc", "abd"}},
  45 + {OpChar: Eq, Value: []interface{}{"abc"}},
  46 + },
  47 + []Expr{
  48 + {OpChar: In, Value: []interface{}{"华南", "华北"}},
  49 + {OpChar: Eq, Value: []interface{}{"华北"}},
  50 + {OpChar: Like, Value: []interface{}{"华"}},
  51 + {OpChar: Like, Value: []interface{}{"中"}},
  52 + },
  53 +}
  54 +
  55 +var columnChar = Column{
  56 + Column: "userName",
  57 + Name: "姓名",
  58 + DbAlias: "ext->>'userName'",
  59 + ValueType: ValueChars,
  60 +}
  61 +
  62 +var columnNumber = Column{
  63 + Column: "age",
  64 + Name: "年龄",
  65 + DbAlias: "age",
  66 + ValueType: ValueNumber,
  67 +}
  68 +
  69 +// 数字表达式合并
  70 +func TestJoinNumberColumnExpr(t *testing.T) {
  71 + for i := range exprNumberTable {
  72 + out := JoinColumnExprNumber(exprNumberTable[i])
  73 + t.Log(out)
  74 + }
  75 +}
  76 +
  77 +// 时间表达式合并
  78 +func TestJoinDateColumnExpr(t *testing.T) {
  79 + for i := range exprDateTable {
  80 + out := JoinColumnExprDate(exprDateTable[i])
  81 + t.Log(out)
  82 + }
  83 +}
  84 +
  85 +// 字符串表达式合并
  86 +func TestJoinCharsColumnExpr(t *testing.T) {
  87 + for i := range exprCharsTable {
  88 + out := JoinColumnExprChars(exprCharsTable[i])
  89 + t.Log(out)
  90 + }
  91 +}
  92 +
  93 +// 排序测试
  94 +func TestSortExprList(t *testing.T) {
  95 + rec := RangeNumberExprCompute{valueType: ValueNumber}
  96 + expr := exprNumberTable[0]
  97 + for i := range expr {
  98 + if expr[i].OpChar == Range || expr[i].OpChar == LessThanEqual || expr[i].OpChar == GreaterThanEqual {
  99 + rec.expr = append(rec.expr, expr[i])
  100 + }
  101 + }
  102 + var exprSort = exprSortable(rec.expr)
  103 + //log.Info("before:", exprSort)
  104 + sort.Sort(exprSort)
  105 + //log.Info("after:", exprSort)
  106 + rec.expr = exprSort
  107 +}
  108 +
  109 +func TestAdvancedQuerySqlNormal(t *testing.T) {
  110 + inputs := []AdvancedQuery{
  111 + {
  112 + Column: columnChar,
  113 + Exprs: exprCharsTable[1],
  114 + },
  115 + {
  116 + Column: columnNumber,
  117 + Exprs: exprNumberTable[0],
  118 + },
  119 + }
  120 + sql, _ := AdvancedQuerySql(inputs)
  121 + t.Log(sql)
  122 +}
  123 +
  124 +func TestAdvancedQuerySqlMerge(t *testing.T) {
  125 + inputs := []AdvancedQuery{
  126 + {
  127 + Column: columnChar,
  128 + Exprs: exprCharsTable[1],
  129 + },
  130 + {
  131 + Column: columnNumber,
  132 + Exprs: exprCharsTable[1],
  133 + },
  134 + }
  135 + sql, _ := AdvancedQuerySql(inputs)
  136 + t.Log(sql)
  137 +}
  138 +
  139 +func TestAdvancedQuerySqlNumber(t *testing.T) {
  140 + inputs := []AdvancedQuery{
  141 + {
  142 + Column: columnChar,
  143 + Exprs: exprCharsTable[1],
  144 + },
  145 + {
  146 + Column: columnNumber,
  147 + Exprs: exprNumberTable[1],
  148 + },
  149 + }
  150 + sql, _ := AdvancedQuerySql(inputs)
  151 + t.Log(sql)
  152 +}
  153 +
  154 +func TestAdvancedQuerySql_PG(t *testing.T) {
  155 + tables := []struct {
  156 + name string
  157 + col Column
  158 + ins []Expr
  159 + except interface{}
  160 + exceptSql string
  161 + ok bool
  162 + }{
  163 + // in
  164 + {
  165 + col: columnChar,
  166 + name: "in zero item",
  167 + ins: []Expr{
  168 + {OpChar: In, Value: []interface{}{}},
  169 + },
  170 + except: nil,
  171 + exceptSql: "",
  172 + ok: false,
  173 + },
  174 + {
  175 + col: columnChar,
  176 + name: "in one item",
  177 + ins: []Expr{
  178 + {OpChar: In, Value: []interface{}{"foo"}},
  179 + },
  180 + except: nil,
  181 + exceptSql: "(ext->>'userName' in ('foo'))",
  182 + ok: true,
  183 + },
  184 + {
  185 + col: columnChar,
  186 + name: "in many item",
  187 + ins: []Expr{
  188 + {OpChar: In, Value: []interface{}{"bar", "ccc", "foo", "ele"}},
  189 + },
  190 + except: nil,
  191 + exceptSql: "(ext->>'userName' in ('bar','ccc','ele','foo'))",
  192 + ok: true,
  193 + },
  194 + {
  195 + col: columnNumber,
  196 + name: "in many item (number)",
  197 + ins: []Expr{
  198 + {OpChar: In, Value: []interface{}{1, 2, 3, 4}},
  199 + },
  200 + except: nil,
  201 + exceptSql: "(age in (1,2,3,4))",
  202 + ok: true,
  203 + },
  204 + {
  205 + col: columnNumber,
  206 + name: "in many item (number)",
  207 + ins: []Expr{
  208 + {OpChar: In, Value: []interface{}{1, 2, 3, 4}},
  209 + },
  210 + except: nil,
  211 + exceptSql: "(age in (1,2,3,4))",
  212 + ok: true,
  213 + },
  214 +
  215 + // range
  216 + {
  217 + col: columnChar,
  218 + name: "range one item",
  219 + ins: []Expr{
  220 + {OpChar: LessThanEqual, Value: []interface{}{Infinity, 60}},
  221 + },
  222 + except: nil,
  223 + exceptSql: "",
  224 + ok: true,
  225 + },
  226 + {
  227 + col: columnNumber,
  228 + name: "range one item",
  229 + ins: []Expr{
  230 + {OpChar: LessThanEqual, Value: []interface{}{Infinity, 60}},
  231 + },
  232 + except: nil,
  233 + exceptSql: "(( age <= 60 ))",
  234 + ok: true,
  235 + },
  236 + {
  237 + col: columnNumber,
  238 + name: "range many item (LessThanEqual)",
  239 + ins: []Expr{
  240 + {OpChar: LessThanEqual, Value: []interface{}{Infinity, 60}},
  241 + {OpChar: LessThanEqual, Value: []interface{}{Infinity, 80}},
  242 + },
  243 + except: nil,
  244 + exceptSql: "(( age <= 80 ))",
  245 + ok: true,
  246 + },
  247 + {
  248 + col: columnNumber,
  249 + name: "range many item (GreaterThanEqual)",
  250 + ins: []Expr{
  251 + {OpChar: LessThanEqual, Value: []interface{}{Infinity, 60}},
  252 + {OpChar: GreaterThanEqual, Value: []interface{}{80, Infinity}},
  253 + {OpChar: GreaterThanEqual, Value: []interface{}{200, Infinity}},
  254 + {OpChar: LessThanEqual, Value: []interface{}{Infinity, 70}},
  255 + },
  256 + except: nil,
  257 + exceptSql: "(( age <= 70 ) or ( age >= 80 ))",
  258 + ok: true,
  259 + },
  260 + {
  261 + col: columnNumber,
  262 + name: "range many item (100<=n<=200)",
  263 + ins: []Expr{
  264 + {OpChar: Range, Value: []interface{}{100, 200}, LeftOp: GreaterThanEqual, RightOp: LessThanEqual},
  265 + },
  266 + except: nil,
  267 + exceptSql: "(( age >= 100 and age <= 200 ))",
  268 + ok: true,
  269 + },
  270 + {
  271 + col: columnNumber,
  272 + name: "range many item (80<n<220)",
  273 + ins: []Expr{
  274 + {OpChar: Range, Value: []interface{}{150, 180}, LeftOp: GreaterThanEqual, RightOp: LessThanEqual},
  275 + {OpChar: Range, Value: []interface{}{120, 220}, LeftOp: GreaterThanEqual, RightOp: LessThan},
  276 + {OpChar: Range, Value: []interface{}{100, 200}, LeftOp: GreaterThanEqual, RightOp: LessThanEqual},
  277 + {OpChar: Range, Value: []interface{}{80, 100}, LeftOp: GreaterThan, RightOp: LessThanEqual},
  278 + },
  279 + except: nil,
  280 + exceptSql: "(( age > 80 and age < 220 ))",
  281 + ok: true,
  282 + },
  283 +
  284 + // like
  285 + {
  286 + col: columnChar,
  287 + name: "like zero item",
  288 + ins: []Expr{
  289 + {OpChar: Like, Value: []interface{}{}},
  290 + },
  291 + except: nil,
  292 + exceptSql: "",
  293 + ok: false,
  294 + },
  295 + {
  296 + col: columnChar,
  297 + name: "like one item",
  298 + ins: []Expr{
  299 + {OpChar: Like, Value: []interface{}{"foo"}},
  300 + },
  301 + except: nil,
  302 + exceptSql: "((ext->>'userName' like '%foo%'))",
  303 + ok: true,
  304 + },
  305 + {
  306 + col: columnChar,
  307 + name: "like many item",
  308 + ins: []Expr{
  309 + {OpChar: Like, Value: []interface{}{"bar", "ccc"}},
  310 + },
  311 + except: nil,
  312 + exceptSql: "((ext->>'userName' like '%bar%' or ext->>'userName' like '%ccc%'))",
  313 + ok: true,
  314 + },
  315 + {
  316 + col: columnNumber,
  317 + name: "like many item (number)",
  318 + ins: []Expr{
  319 + {OpChar: Like, Value: []interface{}{10, 20}},
  320 + },
  321 + except: nil,
  322 + exceptSql: "((age::text like '%10%' or age::text like '%20%'))",
  323 + ok: true,
  324 + },
  325 + }
  326 +
  327 + for i := range tables {
  328 + q := []AdvancedQuery{
  329 + {
  330 + Column: tables[i].col,
  331 + Exprs: tables[i].ins,
  332 + },
  333 + }
  334 + t.Run(tables[i].name, func(t *testing.T) {
  335 + sql, err := AdvancedQuerySql(q)
  336 + assert.Equal(t, tables[i].except, err)
  337 + assert.Equal(t, tables[i].exceptSql, sql)
  338 + if tables[i].ok {
  339 + assert.Nil(t, err)
  340 + }
  341 + })
  342 + }
  343 +}
  1 +package advance
  2 +
  3 +import (
  4 + "bytes"
  5 + "fmt"
  6 + "strings"
  7 +)
  8 +
  9 +type (
  10 + Model struct {
  11 + mc MapColumn
  12 + name string
  13 + }
  14 + AdvancedQuery struct {
  15 + Column Column `json:"column"`
  16 + Exprs []Expr `json:"exprs"`
  17 + }
  18 + ColumnExprResult struct {
  19 + Column Column
  20 + Result []ExprResult
  21 + }
  22 + // 高级查询参数
  23 + AdvancedQueries []AdvancedQuery
  24 +)
  25 +
  26 +var registerModels = make(map[string]Model)
  27 +
  28 +func RegisModel(m Model) {
  29 + if _, ok := registerModels[m.name]; ok {
  30 + panic("register modes exists:" + m.name)
  31 + }
  32 + registerModels[m.name] = m
  33 +}
  34 +
  35 +func NewModel(name string, columns []Column) Model {
  36 + return Model{
  37 + name: name,
  38 + mc: NewMapColumn(columns),
  39 + }
  40 +}
  41 +
  42 +func ComputeColumnExpr(queries []AdvancedQuery) []ColumnExprResult {
  43 + var result = make([]ColumnExprResult, 0)
  44 + queries = mergeQuery(queries)
  45 + for i := range queries {
  46 + q := queries[i]
  47 + var tmpResult []ExprResult
  48 + switch q.Column.ValueType {
  49 + case ValueNumber:
  50 + tmpResult = append(JoinColumnExprNumber(q.Exprs))
  51 + case ValueChars:
  52 + tmpResult = append(JoinColumnExprChars(q.Exprs))
  53 + case ValueDate:
  54 + tmpResult = append(JoinColumnExprDate(q.Exprs))
  55 + }
  56 + if len(tmpResult) == 0 {
  57 + continue
  58 + }
  59 + result = append(result, ColumnExprResult{
  60 + Column: q.Column,
  61 + Result: tmpResult,
  62 + })
  63 + }
  64 + return result
  65 +}
  66 +
  67 +func JoinColumnExprNumber(expr []Expr) []ExprResult {
  68 + var ec = []exprCompute{NewInExprCompute(expr, ValueNumber), NewRangeExprCompute(expr, ValueNumber), NewNotEqualExprCompute(expr, ValueNumber), NewLikeExprCompute(expr, ValueNumber)}
  69 + return joinExprResult(ec)
  70 +}
  71 +
  72 +func JoinColumnExprDate(expr []Expr) []ExprResult {
  73 + var ec = []exprCompute{NewRangeExprCompute(expr, ValueDate), NewRecentDateExprCompute(expr, ValueDate), NewNotEqualExprCompute(expr, ValueChars)}
  74 + return joinExprResult(ec)
  75 +}
  76 +
  77 +func JoinColumnExprChars(expr []Expr) []ExprResult {
  78 + var ec = []exprCompute{NewInExprCompute(expr, ValueChars), NewLikeExprCompute(expr, ValueChars), NewNotEqualExprCompute(expr, ValueChars)}
  79 + return joinExprResult(ec)
  80 +}
  81 +
  82 +type MapColumn map[string]Column
  83 +
  84 +func NewMapColumn(cols []Column) MapColumn {
  85 + mapColumn := make(map[string]Column)
  86 + for i := range cols {
  87 + mapColumn[cols[i].Column] = cols[i]
  88 + }
  89 + return mapColumn
  90 +}
  91 +
  92 +func (m MapColumn) FindItem(col string) Column {
  93 + if item, ok := m[col]; ok {
  94 + return item
  95 + }
  96 + return Column{}
  97 +}
  98 +
  99 +type PgSqlGenerator struct{}
  100 +
  101 +func (p PgSqlGenerator) SqlGen(q ColumnExprResult) string {
  102 + sql := bytes.NewBuffer(nil)
  103 + sql.WriteString("(")
  104 + var wheres []string
  105 + for i := range q.Result {
  106 + item := q.Result[i]
  107 + var where string
  108 + switch item.OpChar {
  109 + case In:
  110 + where = p.In(q.Column, item.Value)
  111 + case NotIn:
  112 + where = p.NotIn(q.Column, item.Value)
  113 + case Like:
  114 + where = p.Like(q.Column, item.Value)
  115 + case Range:
  116 + where = p.Range(q.Column, item)
  117 + default:
  118 + where = fmt.Sprintf(" %s %s %v ", q.Column.DbAlias, item.OpChar, item.Value[0])
  119 + }
  120 + if len(where) == 0 {
  121 + continue
  122 + }
  123 + wheres = append(wheres, where)
  124 + }
  125 + sql.WriteString(strings.Join(wheres, sepOr))
  126 + sql.WriteString(")")
  127 + return sql.String()
  128 +}
  129 +
  130 +func (p PgSqlGenerator) In(c Column, values []interface{}) string {
  131 + if len(values) < 1 {
  132 + return ""
  133 + }
  134 + var ret []string
  135 + for i := range values {
  136 + if c.ValueType == ValueNumber {
  137 + ret = append(ret, fmt.Sprintf("%v", values[i]))
  138 + } else {
  139 + ret = append(ret, fmt.Sprintf("'%v'", values[i]))
  140 + }
  141 + }
  142 + return fmt.Sprintf("%s in (%s)", c.DbAlias, strings.Join(ret, ","))
  143 +}
  144 +
  145 +func (p PgSqlGenerator) NotIn(c Column, values []interface{}) string {
  146 + if len(values) < 1 {
  147 + return ""
  148 + }
  149 + var ret []string
  150 + for i := range values {
  151 + if c.ValueType == ValueNumber {
  152 + ret = append(ret, fmt.Sprintf("%v", values[i]))
  153 + } else {
  154 + ret = append(ret, fmt.Sprintf("'%v'", values[i]))
  155 + }
  156 + }
  157 + return fmt.Sprintf("%s not in (%s)", c.DbAlias, strings.Join(ret, ","))
  158 +}
  159 +
  160 +func (p PgSqlGenerator) Like(c Column, values []interface{}) string {
  161 + if len(values) < 1 {
  162 + return ""
  163 + }
  164 + sql := bytes.NewBuffer(nil)
  165 + sql.WriteString("(")
  166 + var wheres []string
  167 + for i := range values {
  168 + if c.ValueType == ValueNumber {
  169 + wheres = append(wheres, fmt.Sprintf("%s::text like '%%%v%%'", c.DbAlias, values[i]))
  170 + } else {
  171 + wheres = append(wheres, fmt.Sprintf("%s like '%%%v%%'", c.DbAlias, values[i]))
  172 + }
  173 + }
  174 + sql.WriteString(strings.Join(wheres, sepOr))
  175 + sql.WriteString(")")
  176 + return sql.String()
  177 +}
  178 +
  179 +func (p PgSqlGenerator) Range(c Column, res ExprResult) string {
  180 + if len(res.Value) != 2 {
  181 + return ""
  182 + }
  183 + sql := bytes.NewBuffer(nil)
  184 + sql.WriteString("(")
  185 + var wheres []string
  186 + if !isInfinity(res.Value[0]) {
  187 + wheres = append(wheres, fmt.Sprintf(" %s %s %v ", c.DbAlias, res.LeftOp, res.Value[0]))
  188 + }
  189 + if !isInfinity(res.Value[1]) {
  190 + wheres = append(wheres, fmt.Sprintf(" %s %s %v ", c.DbAlias, res.RightOp, res.Value[1]))
  191 + }
  192 + sql.WriteString(strings.Join(wheres, sepAnd))
  193 + sql.WriteString(")")
  194 + return sql.String()
  195 +}
  196 +
  197 +// AdvancedQuerySql 高级查询Sql生成器
  198 +func AdvancedQuerySql(queries []AdvancedQuery) (string, error) {
  199 + if len(queries) == 0 {
  200 + return "", nil
  201 + }
  202 + gen := PgSqlGenerator{}
  203 + results := ComputeColumnExpr(queries)
  204 + if len(results) == 0 {
  205 + return "", nil
  206 + }
  207 + sql := bytes.NewBuffer(nil)
  208 + var wheres []string
  209 + for i := range results {
  210 + wheres = append(wheres, gen.SqlGen(results[i]))
  211 + }
  212 + sql.WriteString(strings.Join(wheres, sepAnd))
  213 + // 空条件 ()
  214 + if len(sql.String()) == 2 {
  215 + return "", nil
  216 + }
  217 + return sql.String(), nil
  218 +}
  219 +
  220 +func mergeQuery(queries []AdvancedQuery) []AdvancedQuery {
  221 + var rsp = make([]AdvancedQuery, 0)
  222 + var mapColumn = make(map[string]AdvancedQuery)
  223 +
  224 + for i := range queries {
  225 + var item AdvancedQuery
  226 + var ok bool
  227 + if item, ok = mapColumn[queries[i].Column.Column]; !ok {
  228 + mapColumn[queries[i].Column.Column] = queries[i]
  229 + } else {
  230 + item.Exprs = append(item.Exprs, queries[i].Exprs...)
  231 + }
  232 + }
  233 + for _, v := range mapColumn {
  234 + rsp = append(rsp, v)
  235 + }
  236 + return rsp
  237 +}
  1 +package advance
  2 +
  3 +import (
  4 + "fmt"
  5 + "math"
  6 + "sort"
  7 + "strconv"
  8 + "time"
  9 +)
  10 +
  11 +const (
  12 + ValueNumber ValueType = "number"
  13 + ValueDate ValueType = "date"
  14 + ValueChars ValueType = "chars"
  15 +)
  16 +
  17 +const (
  18 + daySec = 60 * 60 * 24 //一天的秒数
  19 + Infinity = "$" // 表示无穷
  20 +
  21 + // 分隔符
  22 + sepAnd = " and "
  23 + sepOr = " or "
  24 +)
  25 +
  26 +const (
  27 + Eq OpType = "=" //eq =>>in
  28 + LessThanEqual OpType = "<=" //le
  29 + GreaterThanEqual OpType = ">=" //ge
  30 + Like OpType = "like" //like
  31 + LessThan OpType = "<" //lt
  32 + GreaterThan OpType = ">" //gt
  33 + NotEqual OpType = "<>" //ne =>> not in
  34 +
  35 + NotIn OpType = "not in"
  36 + In OpType = "in" //in
  37 + Range OpType = "range" //range
  38 + Recent OpType = "recent" //recent 近几天
  39 +)
  40 +
  41 +type (
  42 + ValueType string
  43 + OpType string
  44 + Column struct {
  45 + // 字段 userName
  46 + Column string `json:"column"`
  47 + // 名称
  48 + Name string `json:"name"`
  49 + // 列别名 对应数据库字段 ext->>'userName'
  50 + DbAlias string `json:"-"`
  51 + // 值类型
  52 + ValueType ValueType `json:"valueType"`
  53 + }
  54 + Expr struct {
  55 + // 操作符
  56 + OpChar OpType `json:"oc"`
  57 + // 如果 OpChar=range ,LeftOp、RightOp需要有值
  58 + LeftOp OpType `json:"loc"` // "<= <"
  59 + RightOp OpType `json:"roc"` // ">= >"
  60 + // 值
  61 + Value []interface{} `json:"values"`
  62 + }
  63 + ExprResult struct {
  64 + // 操作符
  65 + OpChar OpType
  66 + // 值类型
  67 + ValueType ValueType
  68 + // 值
  69 + Value []interface{}
  70 + // 如果 OpType=range ,LeftOp、RightOp需要有值
  71 + LeftOp OpType
  72 + RightOp OpType
  73 + }
  74 +)
  75 +
  76 +type (
  77 + exprCompute interface {
  78 + Append(result []ExprResult) []ExprResult
  79 + }
  80 + sqlGenerator interface {
  81 + SqlGen(q ColumnExprResult) string
  82 + }
  83 + InExprCompute struct {
  84 + expr []Expr
  85 + valueType ValueType
  86 + OpType OpType
  87 + }
  88 + RangeNumberExprCompute struct {
  89 + expr []Expr
  90 + valueType ValueType
  91 + }
  92 + RecentDateExprCompute struct {
  93 + expr []Expr
  94 + valueType ValueType
  95 + }
  96 + NotEqualExprCompute struct {
  97 + expr []Expr
  98 + valueType ValueType
  99 + OpType OpType
  100 + }
  101 +)
  102 +
  103 +// in合并
  104 +func NewInExprCompute(expr []Expr, valueType ValueType) exprCompute {
  105 + inExpr := InExprCompute{valueType: valueType}
  106 + for i := 0; i < len(expr); i++ {
  107 + if expr[i].OpChar == Eq || expr[i].OpChar == In {
  108 + inExpr.expr = append(inExpr.expr, expr[i])
  109 + }
  110 + }
  111 + if len(inExpr.expr) > 0 {
  112 + return inExpr
  113 + }
  114 + return nil
  115 +}
  116 +func (ex InExprCompute) Append(result []ExprResult) []ExprResult {
  117 + var res = ExprResult{
  118 + OpChar: In,
  119 + ValueType: ex.valueType,
  120 + }
  121 + if ex.OpType != "" {
  122 + res.OpChar = ex.OpType
  123 + }
  124 + for i := range ex.expr {
  125 + res.Value = combine(append(res.Value, ex.expr[i].Value...))
  126 + }
  127 + result = append(result, res)
  128 + return result
  129 +}
  130 +func combine(arr []interface{}) []interface{} {
  131 + var mapArr = make(map[string]interface{})
  132 + for i := range arr {
  133 + key := fmt.Sprintf("%v", arr[i])
  134 + if _, ok := mapArr[key]; !ok {
  135 + mapArr[key] = arr[i]
  136 + }
  137 + }
  138 + var keys []string
  139 + for k, _ := range mapArr {
  140 + keys = append(keys, k)
  141 + }
  142 + sort.Strings(keys)
  143 + var res []interface{}
  144 + for i := range keys {
  145 + res = append(res, mapArr[keys[i]])
  146 + }
  147 + return res
  148 +}
  149 +
  150 +//范围合并
  151 +func NewRangeExprCompute(expr []Expr, valueType ValueType) exprCompute {
  152 + rec := RangeNumberExprCompute{valueType: valueType}
  153 + for i := range expr {
  154 + if expr[i].OpChar == Range || expr[i].OpChar == LessThanEqual || expr[i].OpChar == GreaterThanEqual || expr[i].OpChar == LessThan || expr[i].OpChar == GreaterThan {
  155 + rec.expr = append(rec.expr, expr[i])
  156 + }
  157 + }
  158 + if len(rec.expr) == 0 {
  159 + return nil
  160 + }
  161 + var exprSort = exprSortable(rec.expr)
  162 + sort.Sort(exprSort)
  163 + rec.expr = exprSort
  164 + return rec
  165 +}
  166 +func (ex RangeNumberExprCompute) Append(result []ExprResult) []ExprResult {
  167 + arr := &ExprResult{
  168 + OpChar: Range,
  169 + ValueType: ex.valueType,
  170 + Value: ex.expr[0].Value,
  171 + LeftOp: ex.expr[0].OpChar,
  172 + RightOp: ex.expr[0].OpChar,
  173 + }
  174 + if len(ex.expr[0].LeftOp) != 0 {
  175 + arr.LeftOp = ex.expr[0].LeftOp
  176 + }
  177 + if len(ex.expr[0].RightOp) != 0 {
  178 + arr.RightOp = ex.expr[0].RightOp
  179 + }
  180 + for i := 1; i < len(ex.expr); i++ {
  181 + if !arr.NumberCompare(ex.expr[i]) {
  182 + result = append(result, *arr)
  183 + arr.Value = ex.expr[i].Value
  184 + arr.LeftOp = ex.expr[i].OpChar
  185 + arr.RightOp = ex.expr[i].OpChar
  186 + //if len(ex.expr[0].LeftOp) != 0 {
  187 + // arr.LeftOp = ex.expr[0].LeftOp
  188 + //}
  189 + if len(ex.expr[0].RightOp) != 0 {
  190 + arr.RightOp = ex.expr[0].RightOp
  191 + }
  192 + continue
  193 + }
  194 + }
  195 + result = append(result, *arr)
  196 + return result
  197 +}
  198 +
  199 +// recent范围
  200 +func NewRecentDateExprCompute(expr []Expr, valueType ValueType) exprCompute {
  201 + inExpr := RecentDateExprCompute{valueType: valueType}
  202 + for i := 0; i < len(expr); i++ {
  203 + if expr[i].OpChar == Recent {
  204 + inExpr.expr = append(inExpr.expr, expr[i])
  205 + }
  206 + }
  207 + if len(inExpr.expr) > 0 {
  208 + return inExpr
  209 + }
  210 + return nil
  211 +}
  212 +func (ex RecentDateExprCompute) Append(result []ExprResult) []ExprResult {
  213 + var res = ExprResult{
  214 + OpChar: Recent,
  215 + ValueType: ex.valueType,
  216 + }
  217 + var recent int64 = 0
  218 + for i := range ex.expr {
  219 + v, _ := strconv.ParseInt(fmt.Sprintf("%v", ex.expr[i].Value[0]), 10, 64)
  220 + if v > recent {
  221 + recent = v
  222 + }
  223 + }
  224 + res.Value = append(res.Value, []interface{}{time.Now().Unix() - daySec*recent, Infinity})
  225 + result = append(result, res)
  226 + return result
  227 +}
  228 +
  229 +// like 合并(跟in操作一致)
  230 +func NewLikeExprCompute(expr []Expr, valueType ValueType) exprCompute {
  231 + inExpr := InExprCompute{valueType: valueType, OpType: Like}
  232 + for i := 0; i < len(expr); i++ {
  233 + if expr[i].OpChar == Like {
  234 + inExpr.expr = append(inExpr.expr, expr[i])
  235 + }
  236 + }
  237 + if len(inExpr.expr) > 0 {
  238 + return inExpr
  239 + }
  240 + return nil
  241 +}
  242 +
  243 +// not equal合并
  244 +func NewNotEqualExprCompute(expr []Expr, valueType ValueType) exprCompute {
  245 + notEqualExpr := NotEqualExprCompute{valueType: valueType, OpType: NotIn}
  246 + for i := 0; i < len(expr); i++ {
  247 + if expr[i].OpChar == NotEqual {
  248 + notEqualExpr.expr = append(notEqualExpr.expr, expr[i])
  249 + }
  250 + }
  251 + if len(notEqualExpr.expr) > 0 {
  252 + return notEqualExpr
  253 + }
  254 + return nil
  255 +}
  256 +func (ex NotEqualExprCompute) Append(result []ExprResult) []ExprResult {
  257 + var res = ExprResult{
  258 + OpChar: NotIn,
  259 + ValueType: ex.valueType,
  260 + }
  261 + if ex.OpType != "" {
  262 + res.OpChar = ex.OpType
  263 + }
  264 + for i := range ex.expr {
  265 + res.Value = append(res.Value, ex.expr[i].Value...)
  266 + }
  267 + result = append(result, res)
  268 + return result
  269 +}
  270 +
  271 +func (er *ExprResult) NumberCompare(expr Expr) bool {
  272 + if len(expr.Value) != 2 {
  273 + return false
  274 + }
  275 + _, x2 := toFloat64(er.Value[0], er.Value[1])
  276 + y1, _ := toFloat64(expr.Value[0], expr.Value[1])
  277 + if y1 <= x2 {
  278 + er.Value[1] = max(er.Value[1], expr.Value[1])
  279 + if isEqual(er.Value[1], expr.Value[1]) {
  280 + er.RightOp = expr.OpChar
  281 + if len(expr.RightOp) != 0 {
  282 + er.RightOp = expr.RightOp
  283 + }
  284 + }
  285 + return true
  286 + }
  287 + if isInfinity(er.Value[1]) {
  288 + return true
  289 + }
  290 + if isInfinity(er.Value[0]) && isInfinity(er.Value[1]) {
  291 + return true
  292 + }
  293 + return false
  294 +}
  295 +
  296 +type exprSortable []Expr
  297 +
  298 +func (e exprSortable) Len() int {
  299 + return len(e)
  300 +}
  301 +func (e exprSortable) Less(i, j int) bool {
  302 + var a, b float64
  303 + initValue := func(vi, vj interface{}) {
  304 + a, _ = strconv.ParseFloat(fmt.Sprintf("%v", vi), 64)
  305 + b, _ = strconv.ParseFloat(fmt.Sprintf("%v", vj), 64)
  306 + }
  307 + if isInfinity(e[i].Value[0]) && !isInfinity(e[j].Value[0]) {
  308 + return true
  309 + }
  310 + if isInfinity(e[i].Value[0]) && isInfinity(e[j].Value[0]) {
  311 + initValue(e[i].Value[1], e[j].Value[1])
  312 + return a < b
  313 + }
  314 + if e[i].Value[0] == e[j].Value[0] {
  315 + initValue(e[i].Value[1], e[j].Value[1])
  316 + return a < b
  317 + }
  318 + initValue(e[i].Value[0], e[j].Value[0])
  319 + return a < b
  320 +}
  321 +func (e exprSortable) Swap(i, j int) {
  322 + e[i], e[j] = e[j], e[i]
  323 +}
  324 +
  325 +func joinExprResult(ec []exprCompute) []ExprResult {
  326 + var result []ExprResult
  327 + for _, ecItem := range ec {
  328 + if ecItem != nil {
  329 + result = ecItem.Append(result)
  330 + }
  331 + }
  332 + return result
  333 +}
  334 +
  335 +func toFloat64(vi, vj interface{}) (float64, float64) {
  336 + a, _ := strconv.ParseFloat(fmt.Sprintf("%v", vi), 64)
  337 + b, _ := strconv.ParseFloat(fmt.Sprintf("%v", vj), 64)
  338 + return a, b
  339 +}
  340 +
  341 +func max(x, y interface{}) interface{} {
  342 + if isInfinity(x) {
  343 + return x
  344 + }
  345 + if isInfinity(y) {
  346 + return y
  347 + }
  348 + fx, fy := toFloat64(x, y)
  349 + return math.Max(fx, fy)
  350 +}
  351 +
  352 +func isInfinity(val interface{}) bool {
  353 + v := fmt.Sprintf("%v", val)
  354 + inf := fmt.Sprintf("%v", Infinity)
  355 + return v == inf
  356 +}
  357 +
  358 +func isEqual(v1, v2 interface{}) bool {
  359 + sv1 := fmt.Sprintf("%v", v1)
  360 + sv2 := fmt.Sprintf("%v", v2)
  361 + return sv1 == sv2
  362 +}