123
This commit is contained in:
parent
5a1008b142
commit
c28cf29933
16
README.md
16
README.md
@ -10,23 +10,27 @@ $ go get -u git.listensoft.net/tool/lxutils
|
||||
```
|
||||
|
||||
## Get a value
|
||||
Get searches json for the specified path. A path is in dot syntax, such as "name.last" or "age". When the value is found it's returned immediately.
|
||||
Go.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.listensoft.net/tool/lxutils"
|
||||
"git.listensoft.net/tool/lxutils/lxlog"
|
||||
"git.listensoft.net/tool/lxutils/lxrun"
|
||||
"git.listensoft.net/tool/lxutils/lxzap"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
lxutils.Aa()
|
||||
lxlog.InitLog()
|
||||
err := lxzap.InitLogger(lxzap.LogConfig{})
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
lxrun.Run("prod", "8989", func() {
|
||||
//可执行定时器等等
|
||||
fmt.Println("run cron、mq")
|
||||
}, router, lxzap.LogConfig{})
|
||||
}
|
||||
func router(router *gin.Engine) {
|
||||
|
||||
}
|
||||
```
|
||||
3
go.mod
3
go.mod
@ -11,7 +11,9 @@ require (
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/gomodule/redigo v1.8.9 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
@ -33,5 +35,6 @@ require (
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gorm.io/driver/mysql v1.5.2 // indirect
|
||||
gorm.io/gorm v1.25.5 // indirect
|
||||
)
|
||||
|
||||
7
go.sum
7
go.sum
@ -18,9 +18,13 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
|
||||
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
|
||||
github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
@ -85,6 +89,9 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.5.2 h1:QC2HRskSE75wBuOxe0+iCkyJZ+RqpudsQtqkp+IMuXs=
|
||||
gorm.io/driver/mysql v1.5.2/go.mod h1:pQLhh1Ut/WUAySdTHwBpBv6+JKcj+ua4ZFx1QQTBzb8=
|
||||
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
|
||||
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
||||
84
lxCommon/common.go
Normal file
84
lxCommon/common.go
Normal file
@ -0,0 +1,84 @@
|
||||
package lxCommon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type errorCode struct {
|
||||
SUCCESS int
|
||||
ERROR int
|
||||
NotFound int
|
||||
LoginError int
|
||||
LoginTimeout int
|
||||
InActive int
|
||||
}
|
||||
|
||||
// ErrorCode 错误码
|
||||
var ErrorCode = errorCode{
|
||||
SUCCESS: 0,
|
||||
ERROR: 1,
|
||||
NotFound: 404,
|
||||
LoginError: 1000, //用户名或密码错误
|
||||
LoginTimeout: 1001, //登录超时
|
||||
InActive: 1002, //未激活账号
|
||||
}
|
||||
|
||||
func SendErrJSON(msg string, args ...interface{}) {
|
||||
if len(args) == 0 {
|
||||
panic("缺少 *gin.Context")
|
||||
}
|
||||
var c *gin.Context
|
||||
var errNo = ErrorCode.ERROR
|
||||
if len(args) == 1 {
|
||||
theCtx, ok := args[0].(*gin.Context)
|
||||
if !ok {
|
||||
panic("缺少 *gin.Context")
|
||||
}
|
||||
c = theCtx
|
||||
} else if len(args) == 2 {
|
||||
theErrNo, ok := args[0].(int)
|
||||
if !ok {
|
||||
panic("errNo不正确")
|
||||
}
|
||||
errNo = theErrNo
|
||||
theCtx, ok := args[1].(*gin.Context)
|
||||
if !ok {
|
||||
panic("缺少 *gin.Context")
|
||||
}
|
||||
c = theCtx
|
||||
}
|
||||
fmt.Println(msg) // todo for debug, added by leek, 应该放到业务里决定是否打印错误日志 还是统一放到这里?
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"errNo": errNo,
|
||||
"msg": msg,
|
||||
"data": gin.H{},
|
||||
})
|
||||
// 终止请求链
|
||||
c.Abort()
|
||||
}
|
||||
|
||||
func Ok(c *gin.Context, data interface{}) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"errNo": ErrorCode.SUCCESS,
|
||||
"msg": "success",
|
||||
"data": data,
|
||||
})
|
||||
}
|
||||
|
||||
func HandleParamError(c *gin.Context, err error) bool {
|
||||
if err != nil {
|
||||
SendErrJSON("参数错误:"+err.Error(), c)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func HandleError(c *gin.Context, err error) bool {
|
||||
if err != nil {
|
||||
SendErrJSON(err.Error(), c)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
75
lxDb/db.go
Normal file
75
lxDb/db.go
Normal file
@ -0,0 +1,75 @@
|
||||
package lxDb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.listensoft.net/tool/lxutils/lxzap"
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
"gorm.io/gorm/schema"
|
||||
"os"
|
||||
)
|
||||
|
||||
// DB2.0 数据库连接 gorm2.0
|
||||
var DB *gorm.DB //gs 数据库
|
||||
type DbConfig struct {
|
||||
Host string
|
||||
Port string
|
||||
User string
|
||||
Password string
|
||||
Database string
|
||||
Charset string
|
||||
}
|
||||
|
||||
func InitDB(env string, conf DbConfig) {
|
||||
//dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
|
||||
//newLogger := logger.New(
|
||||
// //zap.NewStdLog(),
|
||||
// log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
|
||||
// logger.Config{
|
||||
// SlowThreshold: 200 * time.Millisecond, // Slow SQL threshold
|
||||
// LogLevel: logger.Info, // Log level
|
||||
// IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger
|
||||
// Colorful: false, // Disable color
|
||||
// },
|
||||
//)
|
||||
if conf.Host == "" {
|
||||
DB = nil
|
||||
fmt.Println("未配置Db连接")
|
||||
return
|
||||
}
|
||||
if conf.Charset == "" {
|
||||
conf.Charset = "utf8mb4"
|
||||
}
|
||||
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local",
|
||||
conf.User, conf.Password, conf.Host, conf.Port, conf.Database, conf.Charset)
|
||||
if env == "dev" {
|
||||
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
|
||||
Logger: logger.Default.LogMode(logger.Info),
|
||||
NamingStrategy: schema.NamingStrategy{
|
||||
SingularTable: true, // 使用单数表名,启用该选项,此时,`User` 的表名应该是 `user`
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
os.Exit(-1)
|
||||
}
|
||||
DB = db
|
||||
} else {
|
||||
logger2 := lxzap.NewGormZap(zap.L())
|
||||
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
|
||||
//Logger: logger.Default.LogMode(logger.Info),
|
||||
Logger: logger2,
|
||||
NamingStrategy: schema.NamingStrategy{
|
||||
SingularTable: true, // 使用单数表名,启用该选项,此时,`User` 的表名应该是 `user`
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
os.Exit(-1)
|
||||
}
|
||||
DB = db
|
||||
}
|
||||
//db.AutoMigrate(TaskData{}, Task{}, Version{})
|
||||
}
|
||||
291
lxDb/model.go
Normal file
291
lxDb/model.go
Normal file
@ -0,0 +1,291 @@
|
||||
package lxDb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.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{} // 值
|
||||
}
|
||||
|
||||
// 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})
|
||||
}
|
||||
|
||||
// Ors Deprecated: replaced by And 自定义条件, 如: (length(field) = 4 OR length(field) = 6)
|
||||
func (q *PaginationQuery) Ors(condition string) {
|
||||
q.And("(" + condition + ")")
|
||||
}
|
||||
|
||||
// And 自定义条件, 如: (length(field) = 4 OR length(field) = 6)
|
||||
func (q *PaginationQuery) And(condition string) {
|
||||
q.Conditions = append(q.Conditions, Condition{Field: condition, Operator: "and"})
|
||||
}
|
||||
|
||||
// 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)
|
||||
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
|
||||
}
|
||||
47
lxDb/redis.go
Normal file
47
lxDb/redis.go
Normal file
@ -0,0 +1,47 @@
|
||||
package lxDb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gomodule/redigo/redis"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// RedisPool Redis连接池
|
||||
var RedisPool *redis.Pool
|
||||
|
||||
type RedisConfig struct {
|
||||
URL string
|
||||
MaxIdle int
|
||||
MaxActive int
|
||||
Password string
|
||||
}
|
||||
|
||||
func InitRedis(conf RedisConfig) {
|
||||
if conf.URL == "" {
|
||||
RedisPool = nil
|
||||
fmt.Println("未配置Redis连接")
|
||||
return
|
||||
}
|
||||
|
||||
RedisPool = &redis.Pool{
|
||||
MaxIdle: conf.MaxIdle,
|
||||
MaxActive: conf.MaxActive,
|
||||
IdleTimeout: 240 * time.Second,
|
||||
Wait: true,
|
||||
Dial: func() (redis.Conn, error) {
|
||||
c, err := redis.Dial("tcp", conf.URL, redis.DialPassword(conf.Password))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
},
|
||||
}
|
||||
c := RedisPool.Get()
|
||||
defer c.Close()
|
||||
_, err := redis.String(c.Do("PING")) // Redis Ping: PONG
|
||||
if err != nil {
|
||||
fmt.Println("error while connecting redis:", err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
}
|
||||
444
lxDb/sql.go
Normal file
444
lxDb/sql.go
Normal file
@ -0,0 +1,444 @@
|
||||
package lxDb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.listensoft.net/tool/lxutils/lxUtil"
|
||||
"gorm.io/gorm"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 带事务的, 和不带事务的 说明:
|
||||
// 如果需要支持事务请调aaaTx方法, 并传开启事务的DB
|
||||
|
||||
// OneById 通过ID查询一条
|
||||
func OneById(m interface{}, id uint) (err error) {
|
||||
return OneByIdTx(DB, m, id)
|
||||
}
|
||||
|
||||
// OneByIdTx 通过ID查询一条
|
||||
func OneByIdTx(tx *gorm.DB, m interface{}, id uint) (err error) {
|
||||
if id == 0 {
|
||||
return errors.New("主键ID不能为空")
|
||||
}
|
||||
if tx.Take(m, id).Error != nil { // or First ?
|
||||
return errors.New("未找到记录")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// One 查询一条 使用m的有值属性作为条件, m必须是Model结构体. 条件为空时报错.
|
||||
func One(m interface{}) (err error) {
|
||||
return OneTx(DB, m)
|
||||
}
|
||||
|
||||
// OneTx 查询一条 使用m的有值属性作为条件, m必须是Model结构体. 条件为空时报错.
|
||||
func OneTx(tx *gorm.DB, m interface{}) error {
|
||||
return oneTx(tx, m, "")
|
||||
}
|
||||
|
||||
func oneTx(tx *gorm.DB, m interface{}, Type string) (err error) {
|
||||
//reflectVal := reflect.ValueOf(m)
|
||||
//t := reflect.Indirect(reflectVal).Type()
|
||||
//newObj := reflect.New(t)
|
||||
if lxUtil.IsZeroOfUnderlyingType(m) {
|
||||
return errors.New("条件不能为空")
|
||||
}
|
||||
|
||||
// 这里有一个特别的情况, 如果m.id有值, 生成sql的where里id条件出现两次, 但是不影响效果
|
||||
if Type == "first" { // 第一个
|
||||
err = tx.Where(m).First(m).Error
|
||||
} else if Type == "last" { // 最后一个
|
||||
err = tx.Where(m).Last(m).Error
|
||||
} else { // 就是一个
|
||||
err = tx.Where(m).Take(m).Error
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// First 查询第一条 使用m的有值属性作为条件, m必须是Model结构体. 条件为空时报错.
|
||||
func First(m interface{}) (err error) {
|
||||
return FirstTx(DB, m)
|
||||
}
|
||||
|
||||
// FirstTx 查询第一条 使用m的有值属性作为条件, m必须是Model结构体. 条件为空时报错.
|
||||
func FirstTx(tx *gorm.DB, m interface{}) error {
|
||||
return oneTx(tx, m, "first")
|
||||
}
|
||||
|
||||
// Last 查询最后一条 使用m的有值属性作为条件, m必须是Model结构体. 条件为空时报错.
|
||||
func Last(m interface{}) (err error) {
|
||||
return LastTx(DB, m)
|
||||
}
|
||||
|
||||
// LastTx 查询最后一条 使用m的有值属性作为条件, m必须是Model结构体. 条件为空时报错.
|
||||
func LastTx(tx *gorm.DB, m interface{}) error {
|
||||
return oneTx(tx, m, "last")
|
||||
}
|
||||
|
||||
// One 查询一条. 这种方式时不行的, 实际上对应的表是 base_model
|
||||
//func (m *BaseModel) One() (err error) {
|
||||
// //if DB.Where(m).First(one).RecordNotFound() {
|
||||
// if DB.Where(m).First(m).Error != nil {
|
||||
// return errors.New("resource is not found")
|
||||
// }
|
||||
// return nil
|
||||
//}
|
||||
|
||||
// Create 新增
|
||||
func Create(m interface{}) error {
|
||||
return CreateTx(DB, m)
|
||||
}
|
||||
|
||||
// CreateTx 带事务的, 新增
|
||||
func CreateTx(tx *gorm.DB, m interface{}) error {
|
||||
return tx.Create(m).Error
|
||||
}
|
||||
|
||||
// Update 更新一条数据的单个字段, m.ID必须有值
|
||||
func Update(m interface{}, field string, value interface{}) error {
|
||||
return UpdateTx(DB, m, field, value)
|
||||
}
|
||||
|
||||
// UpdateTx 带事务的, 更新一条数据的单个字段, m.ID必须有值
|
||||
func UpdateTx(tx *gorm.DB, m interface{}, field string, value interface{}) error {
|
||||
db := tx.Model(m).Update(field, value)
|
||||
if err := db.Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if db.RowsAffected != 1 {
|
||||
return errors.New("id is invalid and resource is not found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Updates 更新一条数据的多个字段, m.ID必须有值
|
||||
// FIXME: 只会更新非零值的字段!!
|
||||
// 尽量不要让m和fields是同一个对象, fields只赋值需要更新的字段
|
||||
// TODO 支持更新成零值, 支持结构体 map, 放在表结构体里? 不通过ID更新?
|
||||
// TODO: fields 里不能ID有值, 否则也更新ID, 这是不行的
|
||||
func Updates(m interface{}, fields interface{}) error {
|
||||
return UpdatesTx(DB, m, fields)
|
||||
}
|
||||
|
||||
// UpdatesTx 带事务的, 更新一条数据的多个字段, m.ID必须有值
|
||||
func UpdatesTx(tx *gorm.DB, m interface{}, fields interface{}) error {
|
||||
db := tx.Model(m).Updates(fields)
|
||||
if err := db.Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if db.RowsAffected != 1 {
|
||||
return errors.New("id is invalid and resource is not found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete 删除, m.ID必须有值.
|
||||
func Delete(m interface{}) error {
|
||||
//func Delete(m interface{}, conds ...interface{}) error { // 不做这种支持, 由ma.DB.Delete()去支持
|
||||
return DeleteTx(DB, m)
|
||||
//return DeleteTx(nil, m, conds...)
|
||||
}
|
||||
|
||||
// DeleteTx 删除, m.ID必须有值. tx不为空就是带事务的
|
||||
func DeleteTx(tx *gorm.DB, m interface{}) error {
|
||||
//func DeleteTx(tx *gorm.DB, m interface{}, conds ...interface{}) error {
|
||||
db := tx.Delete(m)
|
||||
//db := tx.Delete(m, conds...)
|
||||
if err := db.Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if db.RowsAffected != 1 {
|
||||
return errors.New("未找到要删除的数据")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// List 查询数据列表, m是库表结构体, m的有值属性会作为查询条件, 且必须有条件, list里个体的类型可以与m的类型不同
|
||||
func List(m interface{}, list interface{}) (err error) {
|
||||
return ListTx(DB, m, list)
|
||||
}
|
||||
|
||||
// ListTx 查询数据列表, m是库表结构体, m的有值属性会作为查询条件, 且必须有条件, list里个体的类型可以与m的类型不同
|
||||
func ListTx(tx *gorm.DB, m interface{}, list interface{}) (err error) {
|
||||
if lxUtil.IsZeroOfUnderlyingType(m) {
|
||||
return errors.New("条件不能为空")
|
||||
}
|
||||
if tx.Model(m).Where(m).Find(list).Error != nil {
|
||||
// if tx.Where(m).Find(list).Error != nil {
|
||||
return errors.New("查询出现错误")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListAll 查询所有数据, m是库表结构体, m的有值属性不会作为查询条件, list里个体的类型可以与m的类型不同
|
||||
func ListAll(m interface{}, list interface{}) (err error) {
|
||||
return ListAllTx(DB, m, list)
|
||||
}
|
||||
|
||||
// ListAllTx 查询所有数据, m是库表结构体, m的有值属性不会作为查询条件, list里个体的类型可以与m的类型不同
|
||||
func ListAllTx(tx *gorm.DB, m interface{}, list interface{}) (err error) {
|
||||
if tx.Model(m).Find(list).Error != nil {
|
||||
// if tx.Where(m).Find(list).Error != nil {
|
||||
return errors.New("查询出现错误")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Query 查询数据列表, 支持分页、总数、汇总, 见PaginationQuery属性. PaginationQuery不可为nil
|
||||
func Query(m interface{}, list interface{}, q *PaginationQuery) (err error) {
|
||||
QueryTx(DB, m, list, q)
|
||||
return
|
||||
}
|
||||
|
||||
// QueryTx 带事务的查询数据列表. tx为空就是不带事务, 否则认为是开启了事务. 你应该避免使用次方法, 而是使用Query
|
||||
func QueryTx(tx *gorm.DB, m interface{}, list interface{}, q *PaginationQuery) (err error) {
|
||||
// !! 关于会话 新的Statement实例 及复用 ref: https://gorm.io/zh_CN/docs/method_chaining.html
|
||||
|
||||
if q == nil {
|
||||
err = errors.New("paginationQuery不可为nil")
|
||||
return
|
||||
}
|
||||
|
||||
tx = tx.Model(m)
|
||||
|
||||
// 注意: count, 查询list, summary的顺序不能变.
|
||||
|
||||
tx = q.Build(tx) // 构造查询条件
|
||||
|
||||
// 记录条数
|
||||
if needDoCount(q) {
|
||||
var total int64
|
||||
tx = tx.Count(&total)
|
||||
q.Total = int(total)
|
||||
if total == 0 { // 如果查了记录条数并且是0, 就不需要查记录和汇总了
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if q.OrderBy != "" {
|
||||
tx = tx.Order(lxUtil.FieldToColumn(q.OrderBy)) // TODO: q.OrderBy是字符串,可能多个字段 会有问题吗
|
||||
//tx = tx.Order(q.OrderBy)
|
||||
}
|
||||
|
||||
if q.Offset > 0 {
|
||||
tx = tx.Offset((q.Offset - 1) * q.Limit)
|
||||
}
|
||||
if q.Limit > 0 {
|
||||
tx = tx.Limit(q.Limit)
|
||||
}
|
||||
|
||||
// 获取查询值
|
||||
err = tx.Find(list).Error
|
||||
|
||||
// 获取汇总信息, 如果不需要查记录条数就不再查汇总
|
||||
if needDoCount(q) {
|
||||
if q.Summary != "" && len(q.SummarySql) == 0 {
|
||||
q.SummarySql = fieldsToSumSql(q.Summary)
|
||||
}
|
||||
if len(q.Summary) != 0 {
|
||||
tx = tx.Offset(-1) // 需要去除offset, 否则结果可能为空, 注意: 设置0不起作用.
|
||||
var summary = make(map[string]interface{})
|
||||
//tx.Order("") // FIXME: 怎么去掉order by, sum是不需要order by的, 影响性能.
|
||||
tx.Select(q.SummarySql).Take(&summary)
|
||||
|
||||
// []byte 转 string. 不太合理, 应该返回int或float
|
||||
for k, v := range summary {
|
||||
if bs, ok := v.([]byte); ok {
|
||||
summary[k] = string(bs)
|
||||
}
|
||||
}
|
||||
q.SummaryResult = summary
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
//// SqlOne 原生SQL查询一个
|
||||
//func SqlOne(sql string, m interface{}) {
|
||||
// DB.Raw(sql, m)
|
||||
//}
|
||||
//
|
||||
//// SqlList 原生SQL查询列表
|
||||
//func SqlList() {
|
||||
//
|
||||
//}
|
||||
|
||||
// SqlQuery 原生SQL查询列表, 支持分页. PaginationQuery可为nil
|
||||
func SqlQuery(sql string, list interface{}, q *PaginationQuery, params ...interface{}) (err error) {
|
||||
return SqlQueryTx(DB, sql, list, q, params...)
|
||||
}
|
||||
|
||||
func SqlQueryTx(tx *gorm.DB, sql string, list interface{}, q *PaginationQuery, params ...interface{}) (err error) {
|
||||
var builder strings.Builder
|
||||
builder.WriteString(sql)
|
||||
|
||||
if params == nil {
|
||||
params = make([]interface{}, 0)
|
||||
}
|
||||
|
||||
// 条件字段
|
||||
if q != nil {
|
||||
where, args := q.BuildRawWhere()
|
||||
if hasWhere(sql) { // 原SQL已有WHERE子句
|
||||
builder.WriteString(where) // 去掉where 前头的and or ..
|
||||
} else { // 原SQL没有WHERE子句
|
||||
if strings.HasPrefix(where, " AND ") {
|
||||
where = strings.Replace(where, " AND ", " WHERE ", 1)
|
||||
builder.WriteString(where)
|
||||
} else if strings.HasPrefix(where, " OR ") {
|
||||
where = strings.Replace(where, " OR ", " WHERE ", 1)
|
||||
builder.WriteString(where)
|
||||
} else {
|
||||
builder.WriteString(where) // "" 或者 " GROUP BY ..."
|
||||
}
|
||||
}
|
||||
if len(args) > 0 {
|
||||
params = append(params, args...)
|
||||
}
|
||||
|
||||
// 半成品 sql 用于查询其他信息
|
||||
var sql2 = builder.String()
|
||||
|
||||
// 记录条数
|
||||
if needDoCount(q) {
|
||||
var total int64
|
||||
//tx = tx.Count(&total)
|
||||
tx.Raw("SELECT COUNT(*) as total FROM ("+sql2+") aaaa", params...).Take(&total)
|
||||
q.Total = int(total)
|
||||
if total == 0 { // 如果查了记录条数并且是0, 就不需要查记录和汇总了
|
||||
return
|
||||
}
|
||||
|
||||
// 获取汇总信息 // TODO: 汇总应该放到查询列表的后面
|
||||
if q.Summary != "" && len(q.SummarySql) == 0 {
|
||||
q.SummarySql = fieldsToSumSql(q.Summary)
|
||||
}
|
||||
if len(q.Summary) != 0 {
|
||||
tx = tx.Offset(-1) // 需要去除offset, 否则结果可能为空, 注意: 设置0不起作用.
|
||||
var summary = make(map[string]interface{})
|
||||
//tx.Order("") // FIXME: 怎么去掉order by, sum是不需要order by的, 影响性能.
|
||||
//tx.Select(q.SummarySql).Take(&summary) // 不适合rawsql?
|
||||
|
||||
tx.Raw("SELECT "+strings.Join(q.SummarySql, ", ")+" FROM ("+sql2+") ssss", params...).Take(&summary)
|
||||
|
||||
// []byte 转 string. 不太合理, 应该返回int或float
|
||||
for k, v := range summary {
|
||||
if bs, ok := v.([]byte); ok {
|
||||
summary[k] = string(bs)
|
||||
}
|
||||
}
|
||||
q.SummaryResult = summary
|
||||
}
|
||||
}
|
||||
|
||||
// 排序处理
|
||||
if q.OrderBy != "" {
|
||||
s := fmt.Sprintf(" ORDER BY %s", lxUtil.FieldToColumn(q.OrderBy)) // TODO: q.OrderBy是字符串,可能多个字段 会有问题吗
|
||||
builder.WriteString(s)
|
||||
}
|
||||
|
||||
// 偏移量处理
|
||||
if q.Limit > 0 {
|
||||
if q.Offset > 0 {
|
||||
offset := (q.Offset - 1) * q.Limit
|
||||
s := fmt.Sprintf(" LIMIT %d, %d", offset, q.Limit)
|
||||
builder.WriteString(s)
|
||||
} else {
|
||||
s := fmt.Sprintf(" LIMIT %d", q.Limit)
|
||||
builder.WriteString(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//tx.Raw(builder.String(), params...).Scan(list) // FIXME: unsupported data type: &[] why?
|
||||
tx.Raw(builder.String(), params...).Find(list) // Find与Scan区别: list传入[]时, 查询为空的情况下, Find返回的是[], 而Scan返回的是nil.
|
||||
// ref: What is the difference between Find and Scan: https://github.com/go-gorm/gorm/issues/4218
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 是否需要查询记录条数
|
||||
func needDoCount(q *PaginationQuery) bool {
|
||||
if q.NoTotal {
|
||||
return false
|
||||
}
|
||||
|
||||
if q.Limit == 0 { // 不限制条数, 等同于查所有记录, 这时候就不需要查记录条数
|
||||
return false
|
||||
}
|
||||
|
||||
//return q.Offset <= 1 // todo lcs 为什么要这样写 第二页都没了
|
||||
return true
|
||||
}
|
||||
|
||||
// utils -----------------
|
||||
|
||||
// "inAmt,inCount" -> ["SUM(int_amt) AS inAmt", "SUM(int_count) AS inCount"]
|
||||
func fieldsToSumSql(fields string) (sumSqls []string) {
|
||||
strs := strings.Split(strings.TrimSpace(fields), ",")
|
||||
for _, str := range strs {
|
||||
field := strings.TrimSpace(str)
|
||||
if field != "" {
|
||||
sumSqls = append(sumSqls, "SUM("+lxUtil.FieldToColumn(field)+") AS "+field+"")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SELECT...FROM...[WHERE] 句式的 SQL 中是否存在 WHERE 子句
|
||||
func hasWhere(sql string) bool {
|
||||
deep := 0 // 括号嵌套层数
|
||||
step := 0 // "where" 匹配进度
|
||||
|
||||
// 遍历 sql 忽略 ' ( ` 判断是否存在 where
|
||||
for i := 0; i < len(sql); i++ {
|
||||
switch sql[i] {
|
||||
case '(':
|
||||
deep++
|
||||
case ')':
|
||||
deep--
|
||||
case 96: // "`"
|
||||
// 下一个 "`" 的下标
|
||||
// 忽略其他字符
|
||||
for i = i + 1; i < len(sql); i++ {
|
||||
if sql[i] == 96 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 39: // "'"
|
||||
// 下一个 "'" 的下标
|
||||
// 忽略其他字符
|
||||
for i = i + 1; i < len(sql); i++ {
|
||||
if sql[i] == 39 {
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
if deep != 0 {
|
||||
continue
|
||||
}
|
||||
if step == 5 {
|
||||
return true
|
||||
}
|
||||
if sql[i] == where[step][0] || sql[i] == where[step][1] {
|
||||
step++
|
||||
} else {
|
||||
step = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var where = []string{
|
||||
"Ww",
|
||||
"Hh",
|
||||
"Ee",
|
||||
"Rr",
|
||||
"Ee",
|
||||
}
|
||||
83
lxUtil/lxutils.go
Normal file
83
lxUtil/lxutils.go
Normal file
@ -0,0 +1,83 @@
|
||||
package lxUtil
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IsZeroOfUnderlyingType 判断是否零值对象, 考虑到了model是指针的情况
|
||||
func IsZeroOfUnderlyingType(model interface{}) bool {
|
||||
// ref: https://www.coder.work/article/200965
|
||||
rawType := reflect.TypeOf(model)
|
||||
if rawType.Kind() == reflect.Ptr { // 指针
|
||||
rawType = rawType.Elem()
|
||||
return reflect.DeepEqual(model, reflect.New(rawType).Interface())
|
||||
}
|
||||
return reflect.DeepEqual(model, reflect.Zero(rawType).Interface())
|
||||
}
|
||||
|
||||
// ToSlice 为什么不能被 cast.ToSlice 替代?
|
||||
func ToSlice1(v interface{}) []interface{} {
|
||||
var out []interface{}
|
||||
rv := reflect.ValueOf(v)
|
||||
if rv.Kind() == reflect.Slice {
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
out = append(out, rv.Index(i).Interface())
|
||||
}
|
||||
} else {
|
||||
out = append(out, v)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// FieldToColumn inAmt -> in_amt 结构体属性转成库表字段
|
||||
func FieldToColumn(s string) string {
|
||||
return CamelCaseToSnakeCase(s)
|
||||
}
|
||||
|
||||
// CamelCaseToSnakeCase 小驼峰转下划线
|
||||
// mesLine -> mes_line
|
||||
// mesLine-lineCode-lineDesc -> mes_line-line_code-line_desc
|
||||
func CamelCaseToSnakeCase(s string) string {
|
||||
if s = strings.TrimSpace(s); s == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
var (
|
||||
buf strings.Builder
|
||||
prevUpper, nextUpper, nextNumber bool
|
||||
curUpper = s[0] <= 'Z' && s[0] >= 'A'
|
||||
)
|
||||
|
||||
for i, r := range s[:len(s)-1] {
|
||||
nextUpper = 'A' <= s[i+1] && s[i+1] <= 'Z'
|
||||
nextNumber = '0' <= s[i+1] && s[i+1] <= '9'
|
||||
|
||||
if curUpper {
|
||||
if prevUpper && (nextUpper || nextNumber) {
|
||||
buf.WriteRune(r + 32)
|
||||
} else {
|
||||
if i > 0 && s[i-1] != '_' && s[i+1] != '_' {
|
||||
buf.WriteByte('_')
|
||||
}
|
||||
buf.WriteRune(r + 32)
|
||||
}
|
||||
} else {
|
||||
buf.WriteRune(r)
|
||||
}
|
||||
|
||||
prevUpper = curUpper
|
||||
curUpper = nextUpper
|
||||
}
|
||||
|
||||
if curUpper {
|
||||
if !prevUpper && len(s) > 1 {
|
||||
buf.WriteByte('_')
|
||||
}
|
||||
buf.WriteByte(s[len(s)-1] + 32)
|
||||
} else {
|
||||
buf.WriteByte(s[len(s)-1])
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
@ -3,6 +3,7 @@ package lxrun
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"git.listensoft.net/tool/lxutils/lxDb"
|
||||
"git.listensoft.net/tool/lxutils/lxlog"
|
||||
"git.listensoft.net/tool/lxutils/lxzap"
|
||||
"github.com/gin-gonic/gin"
|
||||
@ -14,7 +15,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func Run(env string, port string, fun func(), r func(router *gin.Engine), zapConfig lxzap.LogConfig) {
|
||||
func Run(env string, port string, fun func(), r func(router *gin.Engine), zapConfig lxzap.LogConfig, dbConf lxDb.DbConfig, redisConf lxDb.RedisConfig) {
|
||||
_ = lxlog.InitLog()
|
||||
err := lxzap.InitLogger(zapConfig)
|
||||
if err != nil {
|
||||
@ -27,6 +28,8 @@ func Run(env string, port string, fun func(), r func(router *gin.Engine), zapCon
|
||||
} else {
|
||||
app.Use(lxzap.GinLogger(), lxzap.GinRecovery(true))
|
||||
}
|
||||
lxDb.InitDB(env, dbConf)
|
||||
lxDb.InitRedis(redisConf)
|
||||
fun()
|
||||
r(app)
|
||||
srv := http.Server{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user