作者 yangfu

高级查询修改

... ... @@ -6,7 +6,7 @@ require (
github.com/GeeTeam/gt3-golang-sdk v0.0.0-20200116043922-446ca8a507d2
github.com/beego/beego/v2 v2.0.1
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/forgoer/openssl v0.0.0-20210828150411-6c5378b5b719 // indirect
github.com/forgoer/openssl v0.0.0-20210828150411-6c5378b5b719
github.com/go-pg/pg/v10 v10.10.1
github.com/go-redis/redis v6.14.2+incompatible
github.com/google/uuid v1.1.1
... ...
... ... @@ -5,6 +5,7 @@ import (
"github.com/linmadan/egglib-go/log/logrus"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/constant"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/infrastructure/cache"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/infrastructure/domainService"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/log"
_ "gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/port/beego"
... ... @@ -12,6 +13,7 @@ import (
func init() {
cache.InitRedist()
domainService.AdvancedSettingInit()
}
func main() {
... ...
package query
import (
"fmt"
"github.com/beego/beego/v2/core/validation"
)
type GetAdvancedSettingQuery struct {
//操作人
//操作人
//Operator domain.Operator `json:"-"`
Model string `json:"model"`
}
func (q *GetAdvancedSettingQuery) Valid(validation *validation.Validation) {
if len(q.Model) == 0 {
validation.Error("模型名称不能为空")
}
}
func (q *GetAdvancedSettingQuery) ValidateQuery() error {
valid := validation.Validation{}
b, err := valid.Valid(q)
if err != nil {
return err
}
if !b {
for _, validErr := range valid.Errors {
return fmt.Errorf("%s %s", validErr.Key, validErr.Message)
}
}
return nil
}
... ...
package service
import (
"fmt"
"github.com/linmadan/egglib-go/core/application"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/application/common/query"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/domain"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/infrastructure/service_gateway/allied_creation_basic"
)
const IOSPage = "http://fir.fjmaimaimai.com/pdvn"
... ... @@ -16,57 +20,56 @@ func NewCommonService(options map[string]interface{}) *CommonService {
//GetDictionaryByCode 根据code获取字典数据
func (srv *CommonService) GetDictionaryByCode(getDictionaryQuery *query.GetDictionaryByCodeQuery) (interface{}, error) {
//creationBasicGateway := allied_creation_basic.NewHttplibAlliedCreationBasic(domain.Operator{})
//result, err := creationBasicGateway.GetDictionarysByCode(allied_creation_basic.ReqGetDictionaryByCode{
// DictCode: getDictionaryQuery.DictCode,
//})
//if err != nil {
// return nil, err
//}
//return result, nil
// TODO:测试使用,后期移除掉
type dictItem struct {
ItemCode string `json:"itemCode"`
ItemValue string `json:"itemValue"`
creationBasicGateway := allied_creation_basic.NewHttplibAlliedCreationBasic(domain.Operator{})
result, err := creationBasicGateway.GetDictionarysByCode(allied_creation_basic.ReqGetDictionaryByCode{
DictCodes: getDictionaryQuery.DictCode,
})
if err != nil {
return nil, err
}
dictionaries := make([]interface{}, 0)
for i := range getDictionaryQuery.DictCode {
switch getDictionaryQuery.DictCode[i] {
case "MenuType":
dictionaries = append(dictionaries, map[string]interface{}{
"dictName": "菜单类型",
"dictItems": []dictItem{
{"目录", "catalog"},
{"菜单", "menu"},
{"按钮", "button"},
},
"dictCode": "MenuType",
})
case "XTZD-001":
dictionaries = append(dictionaries, map[string]interface{}{
"dictName": "规模",
"dictItems": []dictItem{
{"1", "0~100人"},
{"2", "101~200人"},
{"3", "201~500人"},
{"4", "501~1000人"},
{"5", "1000人以上"},
},
"dictCode": "XTZD-001",
})
case "XTZD-002":
dictionaries = append(dictionaries, map[string]interface{}{
"dictName": "产业类型",
"dictItems": []dictItem{
{"1", "食品行业"},
{"2", "电子行业"},
{"3", "纺织业"},
},
"dictCode": "XTZD-002",
})
}
}
return map[string]interface{}{"dictionarys": dictionaries}, nil
return result, nil
//type dictItem struct {
// ItemCode string `json:"itemCode"`
// ItemValue string `json:"itemValue"`
//}
//dictionaries := make([]interface{}, 0)
//for i := range getDictionaryQuery.DictCode {
// switch getDictionaryQuery.DictCode[i] {
// case "MenuType":
// dictionaries = append(dictionaries, map[string]interface{}{
// "dictName": "菜单类型",
// "dictItems": []dictItem{
// {"目录", "catalog"},
// {"菜单", "menu"},
// {"按钮", "button"},
// },
// "dictCode": "MenuType",
// })
// case "XTZD-001":
// dictionaries = append(dictionaries, map[string]interface{}{
// "dictName": "规模",
// "dictItems": []dictItem{
// {"1", "0~100人"},
// {"2", "101~200人"},
// {"3", "201~500人"},
// {"4", "501~1000人"},
// {"5", "1000人以上"},
// },
// "dictCode": "XTZD-001",
// })
// case "XTZD-002":
// dictionaries = append(dictionaries, map[string]interface{}{
// "dictName": "产业类型",
// "dictItems": []dictItem{
// {"1", "食品行业"},
// {"2", "电子行业"},
// {"3", "纺织业"},
// },
// "dictCode": "XTZD-002",
// })
// }
//}
//return map[string]interface{}{"dictionarys": dictionaries}, nil
}
//LatestVersionInfo 版本升级
... ... @@ -115,3 +118,15 @@ func (srv *CommonService) AppSharing(q *query.GetLatestVersionQuery) (interface{
//}
//return data, nil
}
// AdvancedSetting 高级查询 配置
func (srv *CommonService) AdvancedSetting(q *query.GetAdvancedSettingQuery) (interface{}, error) {
if err := q.ValidateQuery(); err != nil {
return nil, application.ThrowError(application.BUSINESS_ERROR, err.Error())
}
m, ok := domain.GetModel(q.Model)
if !ok {
return nil, application.ThrowError(application.BUSINESS_ERROR, fmt.Sprintf("模型:%v 不存在", q.Model))
}
return m, nil
}
... ...
... ... @@ -2,6 +2,7 @@ package query
import (
"fmt"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/util/advance"
"github.com/beego/beego/v2/core/validation"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/domain"
... ... @@ -19,6 +20,8 @@ type CompanyUserListQuery struct {
DepartmentName string `json:"departmentName"`
//操作人
Operator domain.Operator `json:"-"`
AdvancedQueries advance.AdvancedQueries `json:"advancedQueries"`
}
func (companyUserListQuery *CompanyUserListQuery) Valid(validation *validation.Validation) {
... ...
... ... @@ -181,17 +181,18 @@ func (usersService *UsersService) CompanyUserEnable(companyUserEnableCommand *co
func (usersService *UsersService) CompanyUserList(companyUserListQuery *query.CompanyUserListQuery) (int64, interface{}, error) {
creationUserGateway := allied_creation_user.NewHttplibAlliedCreationUser(domain.Operator{})
result, err := creationUserGateway.UserSearch(allied_creation_user.ReqUserSearch{
Offset: (companyUserListQuery.PageNumber - 1) * companyUserListQuery.PageSize,
Limit: companyUserListQuery.PageSize,
CompanyId: companyUserListQuery.Operator.CompanyId,
OrganizationId: 0,
DepartmentId: 0,
UserName: companyUserListQuery.UserName,
DepName: companyUserListQuery.DepartmentName,
Phone: "",
UserType: domain.UserTypeEmployee,
InOrgIds: companyUserListQuery.Operator.OrgIds,
PullRealTime: true,
Offset: (companyUserListQuery.PageNumber - 1) * companyUserListQuery.PageSize,
Limit: companyUserListQuery.PageSize,
CompanyId: companyUserListQuery.Operator.CompanyId,
OrganizationId: 0,
DepartmentId: 0,
UserName: companyUserListQuery.UserName,
DepName: companyUserListQuery.DepartmentName,
Phone: "",
UserType: domain.UserTypeEmployee,
InOrgIds: companyUserListQuery.Operator.OrgIds,
PullRealTime: true,
AdvancedQueries: domain.AdvancedQuerySql(domain.UserModel{}.ModelName(), companyUserListQuery.AdvancedQueries),
})
if err != nil {
... ...
package domain
import (
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/log"
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/util/advance"
)
type Model struct {
Columns []advance.Column `json:"columns"`
MapColumn advance.MapColumn `json:"-"`
Name string `json:"name"`
}
var registerModels = make(map[string]Model)
func RegisModel(m Model) {
if _, ok := registerModels[m.Name]; ok {
panic("register modes exists:" + m.Name)
}
registerModels[m.Name] = m
}
func AdvancedQuerySql(model string, quires advance.AdvancedQueries) string {
if len(quires) == 0 {
return ""
}
fixQueries := fixAdvanceQueries(model, quires)
sql, err := advance.AdvancedQuerySql(fixQueries)
if err != nil {
log.Logger.Error(err.Error())
}
return sql
}
func fixAdvanceQueries(model string, quires advance.AdvancedQueries) []advance.AdvancedQuery {
m, ok := GetModel(model)
response := make([]advance.AdvancedQuery, 0)
mapResponse := make(map[string]advance.AdvancedQuery)
if !ok {
return response
}
for i := range quires {
c, ok := m.MapColumn[quires[i].Column.Column]
if !ok {
continue
}
quires[i].Column = c
if q, ok := mapResponse[c.Column]; ok {
q.Exprs = append(q.Exprs, quires[i].Exprs...)
} else {
mapResponse[c.Column] = quires[i]
}
}
for _, v := range mapResponse {
response = append(response, v)
}
return response
}
func GetModel(name string) (Model, bool) {
m, ok := registerModels[name]
return m, ok
}
func NewModel(m ModelInterface) Model {
return Model{
Name: m.ModelName(),
Columns: m.Columns(),
MapColumn: advance.NewMapColumn(m.Columns()),
}
}
type ModelInterface interface {
ModelName() string
Columns() []advance.Column
}
/*User*/
type UserModel struct{}
func (u UserModel) ModelName() string { return "user" }
func (u UserModel) Columns() []advance.Column {
return []advance.Column{
{
Column: "userCode",
Name: "用户编号",
DbAlias: "user_code",
ValueType: advance.ValueChars,
},
{
Column: "userName",
Name: "姓名",
DbAlias: "ext->>'userName'",
ValueType: advance.ValueChars,
},
{
Column: "phone",
Name: "手机号",
DbAlias: "ext->>'phone'",
ValueType: advance.ValueChars,
},
{
Column: "depName",
Name: "所属部门",
DbAlias: "ext->>'depName'",
ValueType: advance.ValueChars,
},
{
Column: "status",
Name: "状态",
DbAlias: "enable_status",
ValueType: advance.ValueNumber,
},
{
Column: "orgName",
Name: "组织机构",
DbAlias: "ext->>'orgName'",
ValueType: advance.ValueChars,
},
}
}
... ...
... ... @@ -8,6 +8,10 @@ import (
"gitlab.fjmaimaimai.com/allied-creation/allied-creation-gateway/pkg/log"
)
func AdvancedSettingInit() {
domain.RegisModel(domain.NewModel(domain.UserModel{}))
}
//GetInitPassword 获取公司初始化密码
func GetInitPassword(operator domain.Operator) (string, string, error) {
var password string
... ...
... ... @@ -106,6 +106,9 @@ type (
EnableStatus int `cname:"状态(1:启用 2:禁用 3:注销)" json:"enableStatus,omitempty"`
// 状态(1:启用 2:禁用 3:注销)
InEnableStatus []int `cname:"状态(1:启用 2:禁用 3:注销)" json:"inEnableStatus,omitempty"`
// 自定义高级查询
AdvancedQueries string `json:"advancedQueries"`
}
//DataUserSearch 搜索用户列表
... ...
... ... @@ -30,6 +30,14 @@ func (controller *CommonController) LatestVersionInfo() {
controller.Response(data, err)
}
func (controller *CommonController) AdvancedSetting() {
commonService := service.NewCommonService(nil)
queryParam := &query.GetAdvancedSettingQuery{}
_ = controller.Unmarshal(queryParam)
data, err := commonService.AdvancedSetting(queryParam)
controller.Response(data, err)
}
func (controller *CommonController) AppSharing() {
commonService := service.NewCommonService(nil)
queryParam := &query.GetLatestVersionQuery{}
... ...
... ... @@ -7,6 +7,7 @@ import (
func init() {
web.Router("/v1/common/dictionary/search", &controllers.CommonController{}, "Post:GetDictionaryByCode")
web.Router("/v1/common/advanced-query/setting", &controllers.CommonController{}, "Post:AdvancedSetting")
web.Router("/v1/common/version/getLatestVersionInfo", &controllers.CommonController{}, "Post:LatestVersionInfo")
web.Router("/v1/common/app-sharing", &controllers.CommonController{}, "Post:AppSharing")
web.Router("/log", &controllers.CommonController{}, "Get:LogData")
... ...
package advance
import (
"github.com/stretchr/testify/assert"
"sort"
"testing"
)
var exprNumberTable = [][]Expr{
[]Expr{
{OpChar: Eq, Value: []interface{}{100, 200, 600}},
//{OpChar: Eq, Value: []interface{}{500}},
{OpChar: Range, Value: []interface{}{50, 200}, LeftOp: GreaterThan, RightOp: LessThan},
{OpChar: LessThanEqual, Value: []interface{}{Infinity, 50}},
{OpChar: LessThan, Value: []interface{}{Infinity, 250}},
{OpChar: Range, Value: []interface{}{60, 100}},
{OpChar: Range, Value: []interface{}{60, 70}},
{OpChar: NotEqual, Value: []interface{}{60, 61, 62}},
},
[]Expr{
{OpChar: Range, Value: []interface{}{100, 200}, LeftOp: GreaterThan, RightOp: LessThanEqual},
{OpChar: Range, Value: []interface{}{Infinity, 50}, LeftOp: GreaterThan, RightOp: LessThanEqual},
{OpChar: Range, Value: []interface{}{50, 90}, LeftOp: GreaterThan, RightOp: LessThanEqual},
{OpChar: Range, Value: []interface{}{150, 300}, LeftOp: GreaterThan, RightOp: LessThanEqual},
},
}
var exprDateTable = [][]Expr{
[]Expr{
{OpChar: Range, Value: []interface{}{1611731000, 1611735000}},
{OpChar: LessThanEqual, Value: []interface{}{Infinity, 1611721000}},
{OpChar: Range, Value: []interface{}{1611734000, 1611737000}},
},
[]Expr{
{OpChar: Range, Value: []interface{}{1611731000, 1611735000}},
{OpChar: LessThanEqual, Value: []interface{}{Infinity, 1611721000}},
{OpChar: Range, Value: []interface{}{1611734000, 1611737000}},
{OpChar: Recent, Value: []interface{}{5}},
},
}
var exprCharsTable = [][]Expr{
[]Expr{
{OpChar: In, Value: []interface{}{"abc", "abd"}},
{OpChar: Eq, Value: []interface{}{"abc"}},
},
[]Expr{
{OpChar: In, Value: []interface{}{"华南", "华北"}},
{OpChar: Eq, Value: []interface{}{"华北"}},
{OpChar: Like, Value: []interface{}{"华"}},
{OpChar: Like, Value: []interface{}{"中"}},
},
}
var columnChar = Column{
Column: "userName",
Name: "姓名",
DbAlias: "ext->>'userName'",
ValueType: ValueChars,
}
var columnNumber = Column{
Column: "age",
Name: "年龄",
DbAlias: "age",
ValueType: ValueNumber,
}
// 数字表达式合并
func TestJoinNumberColumnExpr(t *testing.T) {
for i := range exprNumberTable {
out := JoinColumnExprNumber(exprNumberTable[i])
t.Log(out)
}
}
// 时间表达式合并
func TestJoinDateColumnExpr(t *testing.T) {
for i := range exprDateTable {
out := JoinColumnExprDate(exprDateTable[i])
t.Log(out)
}
}
// 字符串表达式合并
func TestJoinCharsColumnExpr(t *testing.T) {
for i := range exprCharsTable {
out := JoinColumnExprChars(exprCharsTable[i])
t.Log(out)
}
}
// 排序测试
func TestSortExprList(t *testing.T) {
rec := RangeNumberExprCompute{valueType: ValueNumber}
expr := exprNumberTable[0]
for i := range expr {
if expr[i].OpChar == Range || expr[i].OpChar == LessThanEqual || expr[i].OpChar == GreaterThanEqual {
rec.expr = append(rec.expr, expr[i])
}
}
var exprSort = exprSortable(rec.expr)
//log.Info("before:", exprSort)
sort.Sort(exprSort)
//log.Info("after:", exprSort)
rec.expr = exprSort
}
func TestAdvancedQuerySqlNormal(t *testing.T) {
inputs := []AdvancedQuery{
{
Column: columnChar,
Exprs: exprCharsTable[1],
},
{
Column: columnNumber,
Exprs: exprNumberTable[0],
},
}
sql, _ := AdvancedQuerySql(inputs)
t.Log(sql)
}
func TestAdvancedQuerySqlMerge(t *testing.T) {
inputs := []AdvancedQuery{
{
Column: columnChar,
Exprs: exprCharsTable[1],
},
{
Column: columnNumber,
Exprs: exprCharsTable[1],
},
}
sql, _ := AdvancedQuerySql(inputs)
t.Log(sql)
}
func TestAdvancedQuerySqlNumber(t *testing.T) {
inputs := []AdvancedQuery{
{
Column: columnChar,
Exprs: exprCharsTable[1],
},
{
Column: columnNumber,
Exprs: exprNumberTable[1],
},
}
sql, _ := AdvancedQuerySql(inputs)
t.Log(sql)
}
func TestAdvancedQuerySql_PG(t *testing.T) {
tables := []struct {
name string
col Column
ins []Expr
except interface{}
exceptSql string
ok bool
}{
// in
{
col: columnChar,
name: "in zero item",
ins: []Expr{
{OpChar: In, Value: []interface{}{}},
},
except: nil,
exceptSql: "",
ok: false,
},
{
col: columnChar,
name: "in one item",
ins: []Expr{
{OpChar: In, Value: []interface{}{"foo"}},
},
except: nil,
exceptSql: "(ext->>'userName' in ('foo'))",
ok: true,
},
{
col: columnChar,
name: "in many item",
ins: []Expr{
{OpChar: In, Value: []interface{}{"bar", "ccc", "foo", "ele"}},
},
except: nil,
exceptSql: "(ext->>'userName' in ('bar','ccc','ele','foo'))",
ok: true,
},
{
col: columnNumber,
name: "in many item (number)",
ins: []Expr{
{OpChar: In, Value: []interface{}{1, 2, 3, 4}},
},
except: nil,
exceptSql: "(age in (1,2,3,4))",
ok: true,
},
{
col: columnNumber,
name: "in many item (number)",
ins: []Expr{
{OpChar: In, Value: []interface{}{1, 2, 3, 4}},
},
except: nil,
exceptSql: "(age in (1,2,3,4))",
ok: true,
},
// range
{
col: columnChar,
name: "range one item",
ins: []Expr{
{OpChar: LessThanEqual, Value: []interface{}{Infinity, 60}},
},
except: nil,
exceptSql: "",
ok: true,
},
{
col: columnNumber,
name: "range one item",
ins: []Expr{
{OpChar: LessThanEqual, Value: []interface{}{Infinity, 60}},
},
except: nil,
exceptSql: "(( age <= 60 ))",
ok: true,
},
{
col: columnNumber,
name: "range many item (LessThanEqual)",
ins: []Expr{
{OpChar: LessThanEqual, Value: []interface{}{Infinity, 60}},
{OpChar: LessThanEqual, Value: []interface{}{Infinity, 80}},
},
except: nil,
exceptSql: "(( age <= 80 ))",
ok: true,
},
{
col: columnNumber,
name: "range many item (GreaterThanEqual)",
ins: []Expr{
{OpChar: LessThanEqual, Value: []interface{}{Infinity, 60}},
{OpChar: GreaterThanEqual, Value: []interface{}{80, Infinity}},
{OpChar: GreaterThanEqual, Value: []interface{}{200, Infinity}},
{OpChar: LessThanEqual, Value: []interface{}{Infinity, 70}},
},
except: nil,
exceptSql: "(( age <= 70 ) or ( age >= 80 ))",
ok: true,
},
{
col: columnNumber,
name: "range many item (100<=n<=200)",
ins: []Expr{
{OpChar: Range, Value: []interface{}{100, 200}, LeftOp: GreaterThanEqual, RightOp: LessThanEqual},
},
except: nil,
exceptSql: "(( age >= 100 and age <= 200 ))",
ok: true,
},
{
col: columnNumber,
name: "range many item (80<n<220)",
ins: []Expr{
{OpChar: Range, Value: []interface{}{150, 180}, LeftOp: GreaterThanEqual, RightOp: LessThanEqual},
{OpChar: Range, Value: []interface{}{120, 220}, LeftOp: GreaterThanEqual, RightOp: LessThan},
{OpChar: Range, Value: []interface{}{100, 200}, LeftOp: GreaterThanEqual, RightOp: LessThanEqual},
{OpChar: Range, Value: []interface{}{80, 100}, LeftOp: GreaterThan, RightOp: LessThanEqual},
},
except: nil,
exceptSql: "(( age > 80 and age < 220 ))",
ok: true,
},
// like
{
col: columnChar,
name: "like zero item",
ins: []Expr{
{OpChar: Like, Value: []interface{}{}},
},
except: nil,
exceptSql: "",
ok: false,
},
{
col: columnChar,
name: "like one item",
ins: []Expr{
{OpChar: Like, Value: []interface{}{"foo"}},
},
except: nil,
exceptSql: "((ext->>'userName' like '%foo%'))",
ok: true,
},
{
col: columnChar,
name: "like many item",
ins: []Expr{
{OpChar: Like, Value: []interface{}{"bar", "ccc"}},
},
except: nil,
exceptSql: "((ext->>'userName' like '%bar%' or ext->>'userName' like '%ccc%'))",
ok: true,
},
{
col: columnNumber,
name: "like many item (number)",
ins: []Expr{
{OpChar: Like, Value: []interface{}{10, 20}},
},
except: nil,
exceptSql: "((age::text like '%10%' or age::text like '%20%'))",
ok: true,
},
}
for i := range tables {
q := []AdvancedQuery{
{
Column: tables[i].col,
Exprs: tables[i].ins,
},
}
t.Run(tables[i].name, func(t *testing.T) {
sql, err := AdvancedQuerySql(q)
assert.Equal(t, tables[i].except, err)
assert.Equal(t, tables[i].exceptSql, sql)
if tables[i].ok {
assert.Nil(t, err)
}
})
}
}
... ...
package advance
import (
"bytes"
"fmt"
"strings"
)
type (
Model struct {
mc MapColumn
name string
}
AdvancedQuery struct {
Column Column `json:"column"`
Exprs []Expr `json:"exprs"`
}
ColumnExprResult struct {
Column Column
Result []ExprResult
}
// 高级查询参数
AdvancedQueries []AdvancedQuery
)
var registerModels = make(map[string]Model)
func RegisModel(m Model) {
if _, ok := registerModels[m.name]; ok {
panic("register modes exists:" + m.name)
}
registerModels[m.name] = m
}
func NewModel(name string, columns []Column) Model {
return Model{
name: name,
mc: NewMapColumn(columns),
}
}
func ComputeColumnExpr(queries []AdvancedQuery) []ColumnExprResult {
var result = make([]ColumnExprResult, 0)
queries = mergeQuery(queries)
for i := range queries {
q := queries[i]
var tmpResult []ExprResult
switch q.Column.ValueType {
case ValueNumber:
tmpResult = append(JoinColumnExprNumber(q.Exprs))
case ValueChars:
tmpResult = append(JoinColumnExprChars(q.Exprs))
case ValueDate:
tmpResult = append(JoinColumnExprDate(q.Exprs))
}
if len(tmpResult) == 0 {
continue
}
result = append(result, ColumnExprResult{
Column: q.Column,
Result: tmpResult,
})
}
return result
}
func JoinColumnExprNumber(expr []Expr) []ExprResult {
var ec = []exprCompute{NewInExprCompute(expr, ValueNumber), NewRangeExprCompute(expr, ValueNumber), NewNotEqualExprCompute(expr, ValueNumber), NewLikeExprCompute(expr, ValueNumber)}
return joinExprResult(ec)
}
func JoinColumnExprDate(expr []Expr) []ExprResult {
var ec = []exprCompute{NewRangeExprCompute(expr, ValueDate), NewRecentDateExprCompute(expr, ValueDate), NewNotEqualExprCompute(expr, ValueChars)}
return joinExprResult(ec)
}
func JoinColumnExprChars(expr []Expr) []ExprResult {
var ec = []exprCompute{NewInExprCompute(expr, ValueChars), NewLikeExprCompute(expr, ValueChars), NewNotEqualExprCompute(expr, ValueChars)}
return joinExprResult(ec)
}
type MapColumn map[string]Column
func NewMapColumn(cols []Column) MapColumn {
mapColumn := make(map[string]Column)
for i := range cols {
mapColumn[cols[i].Column] = cols[i]
}
return mapColumn
}
func (m MapColumn) FindItem(col string) Column {
if item, ok := m[col]; ok {
return item
}
return Column{}
}
type PgSqlGenerator struct{}
func (p PgSqlGenerator) SqlGen(q ColumnExprResult) string {
sql := bytes.NewBuffer(nil)
sql.WriteString("(")
var wheres []string
for i := range q.Result {
item := q.Result[i]
var where string
switch item.OpChar {
case In:
where = p.In(q.Column, item.Value)
case NotIn:
where = p.NotIn(q.Column, item.Value)
case Like:
where = p.Like(q.Column, item.Value)
case Range:
where = p.Range(q.Column, item)
default:
where = fmt.Sprintf(" %s %s %v ", q.Column.DbAlias, item.OpChar, item.Value[0])
}
if len(where) == 0 {
continue
}
wheres = append(wheres, where)
}
sql.WriteString(strings.Join(wheres, sepOr))
sql.WriteString(")")
return sql.String()
}
func (p PgSqlGenerator) In(c Column, values []interface{}) string {
if len(values) < 1 {
return ""
}
var ret []string
for i := range values {
if c.ValueType == ValueNumber {
ret = append(ret, fmt.Sprintf("%v", values[i]))
} else {
ret = append(ret, fmt.Sprintf("'%v'", values[i]))
}
}
return fmt.Sprintf("%s in (%s)", c.DbAlias, strings.Join(ret, ","))
}
func (p PgSqlGenerator) NotIn(c Column, values []interface{}) string {
if len(values) < 1 {
return ""
}
var ret []string
for i := range values {
if c.ValueType == ValueNumber {
ret = append(ret, fmt.Sprintf("%v", values[i]))
} else {
ret = append(ret, fmt.Sprintf("'%v'", values[i]))
}
}
return fmt.Sprintf("%s not in (%s)", c.DbAlias, strings.Join(ret, ","))
}
func (p PgSqlGenerator) Like(c Column, values []interface{}) string {
if len(values) < 1 {
return ""
}
sql := bytes.NewBuffer(nil)
sql.WriteString("(")
var wheres []string
for i := range values {
if c.ValueType == ValueNumber {
wheres = append(wheres, fmt.Sprintf("%s::text like '%%%v%%'", c.DbAlias, values[i]))
} else {
wheres = append(wheres, fmt.Sprintf("%s like '%%%v%%'", c.DbAlias, values[i]))
}
}
sql.WriteString(strings.Join(wheres, sepOr))
sql.WriteString(")")
return sql.String()
}
func (p PgSqlGenerator) Range(c Column, res ExprResult) string {
if len(res.Value) != 2 {
return ""
}
sql := bytes.NewBuffer(nil)
sql.WriteString("(")
var wheres []string
if !isInfinity(res.Value[0]) {
wheres = append(wheres, fmt.Sprintf(" %s %s %v ", c.DbAlias, res.LeftOp, res.Value[0]))
}
if !isInfinity(res.Value[1]) {
wheres = append(wheres, fmt.Sprintf(" %s %s %v ", c.DbAlias, res.RightOp, res.Value[1]))
}
sql.WriteString(strings.Join(wheres, sepAnd))
sql.WriteString(")")
return sql.String()
}
// AdvancedQuerySql 高级查询Sql生成器
func AdvancedQuerySql(queries []AdvancedQuery) (string, error) {
if len(queries) == 0 {
return "", nil
}
gen := PgSqlGenerator{}
results := ComputeColumnExpr(queries)
if len(results) == 0 {
return "", nil
}
sql := bytes.NewBuffer(nil)
var wheres []string
for i := range results {
wheres = append(wheres, gen.SqlGen(results[i]))
}
sql.WriteString(strings.Join(wheres, sepAnd))
// 空条件 ()
if len(sql.String()) == 2 {
return "", nil
}
return sql.String(), nil
}
func mergeQuery(queries []AdvancedQuery) []AdvancedQuery {
var rsp = make([]AdvancedQuery, 0)
var mapColumn = make(map[string]AdvancedQuery)
for i := range queries {
var item AdvancedQuery
var ok bool
if item, ok = mapColumn[queries[i].Column.Column]; !ok {
mapColumn[queries[i].Column.Column] = queries[i]
} else {
item.Exprs = append(item.Exprs, queries[i].Exprs...)
}
}
for _, v := range mapColumn {
rsp = append(rsp, v)
}
return rsp
}
... ...
package advance
import (
"fmt"
"math"
"sort"
"strconv"
"time"
)
const (
ValueNumber ValueType = "number"
ValueDate ValueType = "date"
ValueChars ValueType = "chars"
)
const (
daySec = 60 * 60 * 24 //一天的秒数
Infinity = "$" // 表示无穷
// 分隔符
sepAnd = " and "
sepOr = " or "
)
const (
Eq OpType = "=" //eq =>>in
LessThanEqual OpType = "<=" //le
GreaterThanEqual OpType = ">=" //ge
Like OpType = "like" //like
LessThan OpType = "<" //lt
GreaterThan OpType = ">" //gt
NotEqual OpType = "<>" //ne =>> not in
NotIn OpType = "not in"
In OpType = "in" //in
Range OpType = "range" //range
Recent OpType = "recent" //recent 近几天
)
type (
ValueType string
OpType string
Column struct {
// 字段 userName
Column string `json:"column"`
// 名称
Name string `json:"name"`
// 列别名 对应数据库字段 ext->>'userName'
DbAlias string `json:"-"`
// 值类型
ValueType ValueType `json:"valueType"`
}
Expr struct {
// 操作符
OpChar OpType `json:"oc"`
// 如果 OpChar=range ,LeftOp、RightOp需要有值
LeftOp OpType `json:"loc"` // "<= <"
RightOp OpType `json:"roc"` // ">= >"
// 值
Value []interface{} `json:"values"`
}
ExprResult struct {
// 操作符
OpChar OpType
// 值类型
ValueType ValueType
// 值
Value []interface{}
// 如果 OpType=range ,LeftOp、RightOp需要有值
LeftOp OpType
RightOp OpType
}
)
type (
exprCompute interface {
Append(result []ExprResult) []ExprResult
}
sqlGenerator interface {
SqlGen(q ColumnExprResult) string
}
InExprCompute struct {
expr []Expr
valueType ValueType
OpType OpType
}
RangeNumberExprCompute struct {
expr []Expr
valueType ValueType
}
RecentDateExprCompute struct {
expr []Expr
valueType ValueType
}
NotEqualExprCompute struct {
expr []Expr
valueType ValueType
OpType OpType
}
)
// in合并
func NewInExprCompute(expr []Expr, valueType ValueType) exprCompute {
inExpr := InExprCompute{valueType: valueType}
for i := 0; i < len(expr); i++ {
if expr[i].OpChar == Eq || expr[i].OpChar == In {
inExpr.expr = append(inExpr.expr, expr[i])
}
}
if len(inExpr.expr) > 0 {
return inExpr
}
return nil
}
func (ex InExprCompute) Append(result []ExprResult) []ExprResult {
var res = ExprResult{
OpChar: In,
ValueType: ex.valueType,
}
if ex.OpType != "" {
res.OpChar = ex.OpType
}
for i := range ex.expr {
res.Value = combine(append(res.Value, ex.expr[i].Value...))
}
result = append(result, res)
return result
}
func combine(arr []interface{}) []interface{} {
var mapArr = make(map[string]interface{})
for i := range arr {
key := fmt.Sprintf("%v", arr[i])
if _, ok := mapArr[key]; !ok {
mapArr[key] = arr[i]
}
}
var keys []string
for k, _ := range mapArr {
keys = append(keys, k)
}
sort.Strings(keys)
var res []interface{}
for i := range keys {
res = append(res, mapArr[keys[i]])
}
return res
}
//范围合并
func NewRangeExprCompute(expr []Expr, valueType ValueType) exprCompute {
rec := RangeNumberExprCompute{valueType: valueType}
for i := range expr {
if expr[i].OpChar == Range || expr[i].OpChar == LessThanEqual || expr[i].OpChar == GreaterThanEqual || expr[i].OpChar == LessThan || expr[i].OpChar == GreaterThan {
rec.expr = append(rec.expr, expr[i])
}
}
if len(rec.expr) == 0 {
return nil
}
var exprSort = exprSortable(rec.expr)
sort.Sort(exprSort)
rec.expr = exprSort
return rec
}
func (ex RangeNumberExprCompute) Append(result []ExprResult) []ExprResult {
arr := &ExprResult{
OpChar: Range,
ValueType: ex.valueType,
Value: ex.expr[0].Value,
LeftOp: ex.expr[0].OpChar,
RightOp: ex.expr[0].OpChar,
}
if len(ex.expr[0].LeftOp) != 0 {
arr.LeftOp = ex.expr[0].LeftOp
}
if len(ex.expr[0].RightOp) != 0 {
arr.RightOp = ex.expr[0].RightOp
}
for i := 1; i < len(ex.expr); i++ {
if !arr.NumberCompare(ex.expr[i]) {
result = append(result, *arr)
arr.Value = ex.expr[i].Value
arr.LeftOp = ex.expr[i].OpChar
arr.RightOp = ex.expr[i].OpChar
//if len(ex.expr[0].LeftOp) != 0 {
// arr.LeftOp = ex.expr[0].LeftOp
//}
if len(ex.expr[0].RightOp) != 0 {
arr.RightOp = ex.expr[0].RightOp
}
continue
}
}
result = append(result, *arr)
return result
}
// recent范围
func NewRecentDateExprCompute(expr []Expr, valueType ValueType) exprCompute {
inExpr := RecentDateExprCompute{valueType: valueType}
for i := 0; i < len(expr); i++ {
if expr[i].OpChar == Recent {
inExpr.expr = append(inExpr.expr, expr[i])
}
}
if len(inExpr.expr) > 0 {
return inExpr
}
return nil
}
func (ex RecentDateExprCompute) Append(result []ExprResult) []ExprResult {
var res = ExprResult{
OpChar: Recent,
ValueType: ex.valueType,
}
var recent int64 = 0
for i := range ex.expr {
v, _ := strconv.ParseInt(fmt.Sprintf("%v", ex.expr[i].Value[0]), 10, 64)
if v > recent {
recent = v
}
}
res.Value = append(res.Value, []interface{}{time.Now().Unix() - daySec*recent, Infinity})
result = append(result, res)
return result
}
// like 合并(跟in操作一致)
func NewLikeExprCompute(expr []Expr, valueType ValueType) exprCompute {
inExpr := InExprCompute{valueType: valueType, OpType: Like}
for i := 0; i < len(expr); i++ {
if expr[i].OpChar == Like {
inExpr.expr = append(inExpr.expr, expr[i])
}
}
if len(inExpr.expr) > 0 {
return inExpr
}
return nil
}
// not equal合并
func NewNotEqualExprCompute(expr []Expr, valueType ValueType) exprCompute {
notEqualExpr := NotEqualExprCompute{valueType: valueType, OpType: NotIn}
for i := 0; i < len(expr); i++ {
if expr[i].OpChar == NotEqual {
notEqualExpr.expr = append(notEqualExpr.expr, expr[i])
}
}
if len(notEqualExpr.expr) > 0 {
return notEqualExpr
}
return nil
}
func (ex NotEqualExprCompute) Append(result []ExprResult) []ExprResult {
var res = ExprResult{
OpChar: NotIn,
ValueType: ex.valueType,
}
if ex.OpType != "" {
res.OpChar = ex.OpType
}
for i := range ex.expr {
res.Value = append(res.Value, ex.expr[i].Value...)
}
result = append(result, res)
return result
}
func (er *ExprResult) NumberCompare(expr Expr) bool {
if len(expr.Value) != 2 {
return false
}
_, x2 := toFloat64(er.Value[0], er.Value[1])
y1, _ := toFloat64(expr.Value[0], expr.Value[1])
if y1 <= x2 {
er.Value[1] = max(er.Value[1], expr.Value[1])
if isEqual(er.Value[1], expr.Value[1]) {
er.RightOp = expr.OpChar
if len(expr.RightOp) != 0 {
er.RightOp = expr.RightOp
}
}
return true
}
if isInfinity(er.Value[1]) {
return true
}
if isInfinity(er.Value[0]) && isInfinity(er.Value[1]) {
return true
}
return false
}
type exprSortable []Expr
func (e exprSortable) Len() int {
return len(e)
}
func (e exprSortable) Less(i, j int) bool {
var a, b float64
initValue := func(vi, vj interface{}) {
a, _ = strconv.ParseFloat(fmt.Sprintf("%v", vi), 64)
b, _ = strconv.ParseFloat(fmt.Sprintf("%v", vj), 64)
}
if isInfinity(e[i].Value[0]) && !isInfinity(e[j].Value[0]) {
return true
}
if isInfinity(e[i].Value[0]) && isInfinity(e[j].Value[0]) {
initValue(e[i].Value[1], e[j].Value[1])
return a < b
}
if e[i].Value[0] == e[j].Value[0] {
initValue(e[i].Value[1], e[j].Value[1])
return a < b
}
initValue(e[i].Value[0], e[j].Value[0])
return a < b
}
func (e exprSortable) Swap(i, j int) {
e[i], e[j] = e[j], e[i]
}
func joinExprResult(ec []exprCompute) []ExprResult {
var result []ExprResult
for _, ecItem := range ec {
if ecItem != nil {
result = ecItem.Append(result)
}
}
return result
}
func toFloat64(vi, vj interface{}) (float64, float64) {
a, _ := strconv.ParseFloat(fmt.Sprintf("%v", vi), 64)
b, _ := strconv.ParseFloat(fmt.Sprintf("%v", vj), 64)
return a, b
}
func max(x, y interface{}) interface{} {
if isInfinity(x) {
return x
}
if isInfinity(y) {
return y
}
fx, fy := toFloat64(x, y)
return math.Max(fx, fy)
}
func isInfinity(val interface{}) bool {
v := fmt.Sprintf("%v", val)
inf := fmt.Sprintf("%v", Infinity)
return v == inf
}
func isEqual(v1, v2 interface{}) bool {
sv1 := fmt.Sprintf("%v", v1)
sv2 := fmt.Sprintf("%v", v2)
return sv1 == sv2
}
... ...