正在显示
14 个修改的文件
包含
1195 行增加
和
62 行删除
@@ -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"` | 23 | + creationBasicGateway := allied_creation_basic.NewHttplibAlliedCreationBasic(domain.Operator{}) |
24 | + result, err := creationBasicGateway.GetDictionarysByCode(allied_creation_basic.ReqGetDictionaryByCode{ | ||
25 | + DictCodes: getDictionaryQuery.DictCode, | ||
26 | + }) | ||
27 | + if err != nil { | ||
28 | + return nil, err | ||
31 | } | 29 | } |
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", | ||
66 | - }) | ||
67 | - } | ||
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) { |
@@ -181,17 +181,18 @@ func (usersService *UsersService) CompanyUserEnable(companyUserEnableCommand *co | @@ -181,17 +181,18 @@ func (usersService *UsersService) CompanyUserEnable(companyUserEnableCommand *co | ||
181 | func (usersService *UsersService) CompanyUserList(companyUserListQuery *query.CompanyUserListQuery) (int64, interface{}, error) { | 181 | func (usersService *UsersService) CompanyUserList(companyUserListQuery *query.CompanyUserListQuery) (int64, interface{}, error) { |
182 | creationUserGateway := allied_creation_user.NewHttplibAlliedCreationUser(domain.Operator{}) | 182 | creationUserGateway := allied_creation_user.NewHttplibAlliedCreationUser(domain.Operator{}) |
183 | result, err := creationUserGateway.UserSearch(allied_creation_user.ReqUserSearch{ | 183 | result, err := creationUserGateway.UserSearch(allied_creation_user.ReqUserSearch{ |
184 | - Offset: (companyUserListQuery.PageNumber - 1) * companyUserListQuery.PageSize, | ||
185 | - Limit: companyUserListQuery.PageSize, | ||
186 | - CompanyId: companyUserListQuery.Operator.CompanyId, | ||
187 | - OrganizationId: 0, | ||
188 | - DepartmentId: 0, | ||
189 | - UserName: companyUserListQuery.UserName, | ||
190 | - DepName: companyUserListQuery.DepartmentName, | ||
191 | - Phone: "", | ||
192 | - UserType: domain.UserTypeEmployee, | ||
193 | - InOrgIds: companyUserListQuery.Operator.OrgIds, | ||
194 | - PullRealTime: true, | 184 | + Offset: (companyUserListQuery.PageNumber - 1) * companyUserListQuery.PageSize, |
185 | + Limit: companyUserListQuery.PageSize, | ||
186 | + CompanyId: companyUserListQuery.Operator.CompanyId, | ||
187 | + OrganizationId: 0, | ||
188 | + DepartmentId: 0, | ||
189 | + UserName: companyUserListQuery.UserName, | ||
190 | + DepName: companyUserListQuery.DepartmentName, | ||
191 | + Phone: "", | ||
192 | + UserType: domain.UserTypeEmployee, | ||
193 | + InOrgIds: companyUserListQuery.Operator.OrgIds, | ||
194 | + PullRealTime: true, | ||
195 | + AdvancedQueries: domain.AdvancedQuerySql(domain.UserModel{}.ModelName(), companyUserListQuery.AdvancedQueries), | ||
195 | }) | 196 | }) |
196 | 197 | ||
197 | if err != nil { | 198 | if err != nil { |
pkg/domain/advance.go
0 → 100644
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") |
pkg/util/advance/column_exprs_test.go
0 → 100644
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 | +} |
pkg/util/advance/expr.go
0 → 100644
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 | +} |
pkg/util/advance/types.go
0 → 100644
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 | +} |
-
请 注册 或 登录 后发表评论