lxutils/lxDb/model.go

294 lines
10 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package lxDb
import (
"fmt"
"gitea.listensoft.net/tool/lxutils/lxUtil"
"gorm.io/gorm"
"reflect"
"strings"
"time"
)
type BaseModel struct {
ID uint `json:"id" gorm:"primaryKey"` // old version is: gorm:"primary_key", TODO: 兼容旧的 有没有影响?
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
CreatedBy uint `json:"createdBy"`
UpdatedBy uint `json:"updatedBy"`
}
// BaseModel2 带操作人ID的BaseModel
type BaseModel2 struct {
BaseModel
CreatedBy uint `json:"createdBy"`
UpdatedBy uint `json:"updatedBy"`
//DeletedBy uint `json:"-"`
}
//func (m BaseModel)AsID (id uint) {
// m.ID = id
//}
// NewModel 返回被 uid 填充的 model
//func NewModel(uid uint) Model {
// return Model{CreatedBy: uid, UpdatedBy: uid}
//}
// Condition 查询条件
type Condition struct {
Field string // 字段
Operator string // 操作类型
Arg interface{} // 值
Args []interface{}
}
// PaginationQuery 查询条件
// TODO: 考虑在包之外使用方法修改属性而不是直接操作属性
// TODO: 考虑要明确指定不分页查询, 否则就强制分页, 以免误查大量数据, 但是给非前端调用带来麻烦
// TODO: 要明确指定是不分页查询, 默认“下一页”不查询total. 考虑前端使用的友好性,及可配置化(config)
type PaginationQuery struct {
Conditions []Condition `json:"-"` // 查询条件
OrderBy string `json:"orderBy"` // 排序 会被直接拼接到 sql 里 // TODO: 这个会保留吗, 支持特殊的自定义排序.
Orders []string `json:"orders"` // 排序 会被直接拼接到 sql 里 // TODO: 支持多条件排序 string 还是对象? 还未做
Offset int `json:"page"` // 第几页
Limit int `json:"limit"` // 一页几条数据 or size ? // TODO:
NoTotal bool `json:"noTotal"` // 不需要记录条数, true: 不查询Total
Total int `json:"total"` // 记录条数
Summary string `json:"summary"` // 汇总字段,如: "inAmt,inCount"
SummarySql []string `json:"-"` // 汇总子sql, 如: ["SUM(int_amt) AS inAmt", "SUM(int_count) AS inCount"]
SummaryResult map[string]interface{} `json:"summaryResult"` // 汇总结果
}
// Eq equal 等于
func (q *PaginationQuery) Eq(field string, arg interface{}) {
q.Conditions = append(q.Conditions, Condition{Field: field, Operator: "eq", Arg: arg})
}
// Ne not equal 不等于
func (q *PaginationQuery) Ne(field string, arg interface{}) {
q.Conditions = append(q.Conditions, Condition{Field: field, Operator: "ne", Arg: arg})
}
// Lt less than 小于
func (q *PaginationQuery) Lt(field string, arg interface{}) {
q.Conditions = append(q.Conditions, Condition{Field: field, Operator: "lt", Arg: arg})
}
// Le less equal 小于等于
func (q *PaginationQuery) Le(field string, arg interface{}) {
q.Conditions = append(q.Conditions, Condition{Field: field, Operator: "le", Arg: arg})
}
// Gt greater than 大于
func (q *PaginationQuery) Gt(field string, arg interface{}) {
q.Conditions = append(q.Conditions, Condition{Field: field, Operator: "gt", Arg: arg})
}
// Ge greater equal 大于等于
func (q *PaginationQuery) Ge(field string, arg interface{}) {
q.Conditions = append(q.Conditions, Condition{Field: field, Operator: "ge", Arg: arg})
}
// Like 包含 LIKE '%arg%'
func (q *PaginationQuery) Like(field string, arg interface{}) {
q.Conditions = append(q.Conditions, Condition{Field: field, Operator: "like", Arg: arg})
}
// Like2 自指定包含方式 LIKE 'arg'. arg是灵活的, 如: 前匹配abs%, 后匹配%abc, 通配符'_1_'
func (q *PaginationQuery) Like2(field string, value interface{}) {
q.Conditions = append(q.Conditions, Condition{Field: field, Operator: "like2", Arg: value})
}
func (q *PaginationQuery) NotLike(field string, value interface{}) {
q.Conditions = append(q.Conditions, Condition{Field: field, Operator: "notlike", Arg: value})
}
// In IN (...)
func (q *PaginationQuery) In(field string, arg interface{}) {
q.Conditions = append(q.Conditions, Condition{Field: field, Operator: "in", Arg: arg})
}
// Between BETWEEN v1 AND v2
func (q *PaginationQuery) Between(field string, v1, v2 interface{}) {
q.Conditions = append(q.Conditions, Condition{Field: field, Operator: "between", Arg: [2]interface{}{v1, v2}})
}
// Or OR
func (q *PaginationQuery) Or(field string, arg interface{}) {
q.Conditions = append(q.Conditions, Condition{Field: field, Operator: "or", Arg: arg})
}
// Deprecated: 有SQL注入风险用 And() 代替
func (q *PaginationQuery) Ors(condition string) {
q.And("(" + condition + ")")
}
// And 自定义条件, 如: (length(field) = 4 OR length(field) = 6)
func (q *PaginationQuery) And(condition string, args ...interface{}) {
q.Conditions = append(q.Conditions, Condition{Field: condition, Operator: "and", Args: args})
}
// Group GROUP BY
func (q *PaginationQuery) Group(field string) {
q.Conditions = append(q.Conditions, Condition{Field: field, Operator: "group"})
}
// Join JOIN table_name ON
func (q *PaginationQuery) Join(joinSql string, args ...interface{}) {
q.Conditions = append(q.Conditions, Condition{Field: joinSql, Operator: "join", Arg: args})
}
// Select 要查询的字段. 可以将字段都写在第一个参数, 也可以作为多个参数传递. ("aa, bb, cc") == ("aa", "bb", "cc")
func (q *PaginationQuery) Select(query string, args ...interface{}) {
q.Conditions = append(q.Conditions, Condition{Field: query, Operator: "select", Arg: args})
}
// Build 构造查询条件
func (q *PaginationQuery) Build(tx *gorm.DB) *gorm.DB {
for _, con := range q.Conditions {
switch con.Operator {
case "eq":
field := fmt.Sprintf("%s = ?", con.Field)
tx = tx.Where(field, con.Arg)
case "ne":
field := fmt.Sprintf("%s <> ?", con.Field)
tx = tx.Where(field, con.Arg)
case "lt":
field := fmt.Sprintf("%s < ?", con.Field)
tx = tx.Where(field, con.Arg)
case "le":
field := fmt.Sprintf("%s <= ?", con.Field)
tx = tx.Where(field, con.Arg)
case "gt":
field := fmt.Sprintf("%s > ?", con.Field)
tx = tx.Where(field, con.Arg)
case "ge":
field := fmt.Sprintf("%s >= ?", con.Field)
tx = tx.Where(field, con.Arg)
case "like":
field := fmt.Sprintf("%s LIKE ?", con.Field) // field LIKE '%arg%'
tx = tx.Where(field, fmt.Sprintf("%%%v%%", con.Arg))
case "like2":
field := fmt.Sprintf("%s LIKE ?", con.Field) // field LIKE 'arg', support: 'abc%', '%abc', '_1_'
tx = tx.Where(field, con.Arg)
case "notlike":
field := fmt.Sprintf("%s NOT LIKE ?", con.Field)
tx = tx.Where(field, fmt.Sprintf("%%%v%%", con.Arg))
case "in":
field := fmt.Sprintf("%s IN (?)", con.Field)
tx = tx.Where(field, con.Arg)
case "between":
field := fmt.Sprintf("%s BETWEEN ? AND ?", con.Field)
value1, value2, ok := split(con.Arg)
if ok {
tx = tx.Where(field, value1, value2)
}
case "or":
field := fmt.Sprintf("%s = ?", con.Field)
tx = tx.Or(field, con.Arg)
case "and":
tx = tx.Where(con.Field) // 自定义条件, 如: (length(field) = 4 OR length(field) = 6)
case "group":
tx = tx.Group(con.Field)
case "join":
//args := cast.ToStringSlice(con.Arg) // not work, cast.ToSlice not work too
tx = tx.Joins(con.Field, lxUtil.ToSlice1(con.Arg)...)
case "select":
tx = tx.Select(con.Field, lxUtil.ToSlice1(con.Arg)...)
}
}
return tx
}
// BuildRawWhere 构造原生SQL的查询条件
func (q *PaginationQuery) BuildRawWhere() (where string, args []interface{}) {
var sb strings.Builder
for _, con := range q.Conditions {
switch con.Operator {
case "eq":
sb.WriteString(fmt.Sprintf(" AND %s = ?", con.Field))
args = append(args, con.Arg)
case "ne":
sb.WriteString(fmt.Sprintf(" AND %s <> ?", con.Field))
args = append(args, con.Arg)
case "lt":
sb.WriteString(fmt.Sprintf(" AND %s < ?", con.Field))
args = append(args, con.Arg)
case "le":
sb.WriteString(fmt.Sprintf(" AND %s <= ?", con.Field))
args = append(args, con.Arg)
case "gt":
sb.WriteString(fmt.Sprintf(" AND %s > ?", con.Field))
args = append(args, con.Arg)
case "ge":
sb.WriteString(fmt.Sprintf(" AND %s >= ?", con.Field))
args = append(args, con.Arg)
case "like":
sb.WriteString(fmt.Sprintf(" AND %s LIKE ?", con.Field)) // field LIKE '%arg%'
args = append(args, fmt.Sprintf("%%%v%%", con.Arg))
case "like2":
sb.WriteString(fmt.Sprintf(" AND %s LIKE ?", con.Field)) // field LIKE 'arg', support: 'abc%', '%abc', '_1_'
args = append(args, con.Arg)
case "notlike":
sb.WriteString(fmt.Sprintf(" AND %s NOT LIKE ?", con.Field))
args = append(args, fmt.Sprintf("%%%v%%", con.Arg))
case "in":
sb.WriteString(fmt.Sprintf(" AND %s IN (?)", con.Field))
args = append(args, con.Arg)
case "between":
value1, value2, ok := split(con.Arg)
if ok {
sb.WriteString(fmt.Sprintf(" AND %s BETWEEN ? AND ?", con.Field))
args = append(args, value1, value2)
}
case "or":
sb.WriteString(fmt.Sprintf(" OR %s = ?", con.Field))
args = append(args, con.Arg)
case "and":
sb.WriteString(fmt.Sprintf(" AND (%s)", con.Field)) // 自定义条件, 如: (length(field) = 4 OR length(field) = 6)
args = append(args, con.Args...)
case "group":
sb.WriteString(fmt.Sprintf(" GROUP BY %s", con.Field))
//case "join":
// if con.Arg == nil {
// tx = tx.Joins(con.Field)
// } else {
// tx = tx.Joins(con.Field, con.Arg)
// }
//case "select":
// if con.Arg == nil {
// tx = tx.Select(con.Field)
// } else {
// tx = tx.Select(con.Field, con.Arg)
// }
}
}
where = sb.String()
if args == nil {
args = make([]interface{}, 0)
}
return
}
// 参数拆分 // TODO: 不知道有没有更好的实现方式
// TODO: 加异常处理, 如应该两个值只传了一个值
//func split(value interface{}) (interface{}, interface{}) {
// values := value.([2]interface{})
// return values[0], values[1]
//}
// 参数拆分
func split(value interface{}) (v1 interface{}, v2 interface{}, ok bool) {
rv := reflect.ValueOf(value)
// 取值
for rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}
// 校验是否为长度为 2 的数组或切片
if (rv.Kind() != reflect.Slice && rv.Kind() != reflect.Array) || rv.Len() != 2 {
return
}
return rv.Index(0).Interface(), rv.Index(1).Interface(), true
}