Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## Description

<!-- A clear and concise description what these changes does. -->

## Checklist

<!-- Replace the [ ] with [x] to check the boxes. -->

- [ ] the pull request title describes what this PR does (not a vague title like Update index.md)
- [ ] the pull request targets the default branch of the repository (main)
- [ ] no unintentional fmt.Print left behind after debugging
- [ ] did I name variables, methods and classes according to the naming rules? (https://go.dev/doc/effective_go#names)
- [ ] caught exceptions or throw them to the upper level for processing, not ignored (https://go.dev/doc/effective_go#errors)
- [ ] did I explain all possible solutions and why I chose the one I did?
- [ ] added any comments to make new functions clearer
- [ ] tests are added for the changes I made (if any source code was modified)
- [ ] documentation added or updated
- [ ] I have run the project locally and verified that there are no errors
- [ ] instructions for how reviewers can test the code locally
- [ ] screenshot of the feature/bug fix (if applicable)
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ jobs:
- uses: actions/checkout@v3
- name: install deps
run: |
go get -u golang.org/x/tools/cmd/goimports
go install golang.org/x/tools/cmd/goimports

- name: golangci-lint
Expand Down
3 changes: 1 addition & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ require (
github.com/kamva/mgm/v3 v3.5.0
github.com/prometheus/client_golang v1.12.1
github.com/robfig/cron/v3 v3.0.1
github.com/sanity-io/litter v1.5.5
github.com/sirupsen/logrus v1.8.1
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/smartystreets/goconvey v1.7.2
Expand All @@ -43,8 +44,6 @@ require (
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/tools v0.13.0 // indirect
)

require (
Expand Down
9 changes: 5 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down Expand Up @@ -419,6 +420,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
Expand Down Expand Up @@ -455,6 +457,8 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
Expand All @@ -481,6 +485,7 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
Expand Down Expand Up @@ -600,8 +605,6 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down Expand Up @@ -800,8 +803,6 @@ golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
18 changes: 18 additions & 0 deletions testdata/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
tableName: test
fields:
- name: id
type: string
primaryKey: true
size: 64
creatable: true
updatable: false
readable: true
Comment: ID
- name: name
type: string
size: 63
creatable: true
updatable: true
readable: true
Comment: 名称
index: name_index
93 changes: 93 additions & 0 deletions virtual/model/field.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package model

import (
"fmt"
"reflect"
"strings"
"time"

"github.com/google/uuid"
"gorm.io/gorm/schema"
)

/*
* @Author: lwnmengjing<lwnmengjing@qq.com>
* @Date: 2023/9/10 16:11:55
* @Last Modified by: lwnmengjing<lwnmengjing@qq.com>
* @Last Modified time: 2023/9/10 16:11:55
*/

type Field struct {
Name string `json:"name" yaml:"name" binding:"required"`
JsonTag string `json:"jsonTag" yaml:"jsonTag"`
DataType schema.DataType `json:"type" yaml:"type" binding:"required"`
PrimaryKey bool `json:"primaryKey" yaml:"primaryKey"`
AutoIncrement bool `json:"autoIncrement" yaml:"autoIncrement"`
AutoIncrementIncrement int64 `json:"autoIncrementIncrement" yaml:"autoIncrementIncrement"`
Creatable bool `json:"creatable" yaml:"creatable"`
Updatable bool `json:"updatable" yaml:"updatable"`
Readable bool `json:"readable" yaml:"readable"`
DefaultValue string `json:"defaultValue" yaml:"defaultValue"`
DefaultValueFN func() string `json:"-" yaml:"-"`
NotNull bool `json:"notNull" yaml:"notNull"`
Unique bool `json:"unique" yaml:"unique"`
Index string `json:"index" yaml:"index"`
Comment string `json:"comment" yaml:"comment"`
Size int `json:"size" yaml:"size"`
Precision int `json:"precision" yaml:"precision"`
Scale int `json:"scale" yaml:"scale"`
Search string `json:"search" yaml:"search"`
}

type DefaultFN string

const (
UUID DefaultFN = "uuid"
Now DefaultFN = "now"
)

var UUIDFN = func() string {
return strings.ReplaceAll(uuid.New().String(), "-", "")
}

var NowFN = func() string {
return time.Now().String()
}

func (f *Field) Init() {
if f.JsonTag == "" {
f.JsonTag = f.Name
}
if f.PrimaryKey {
f.DefaultValueFN = UUIDFN
}
if f.DataType == schema.Time && f.NotNull {
f.DefaultValueFN = NowFN
}
}

func (f *Field) GetName() string {
return strings.ToUpper(f.Name[:1]) + f.Name[1:]
}

func (f *Field) MakeField() reflect.StructField {
field := reflect.StructField{
Name: f.GetName(),
Tag: reflect.StructTag(fmt.Sprintf(`json:"%s" gorm:"column:%s"`, f.JsonTag, f.Name)),
}
switch f.DataType {
case schema.Bool:
field.Type = reflect.TypeOf(false)
case schema.Float:
field.Type = reflect.TypeOf(float64(0))
case schema.Int:
field.Type = reflect.TypeOf(int(0))
case schema.Uint:
field.Type = reflect.TypeOf(uint(0))
case schema.Time:
field.Type = reflect.TypeOf(time.Time{})
default:
field.Type = reflect.TypeOf("")
}
return field
}
155 changes: 155 additions & 0 deletions virtual/model/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package model

import (
"fmt"
"reflect"
"strings"

"github.com/gin-gonic/gin"
"gorm.io/gorm"
"gorm.io/gorm/schema"
)

/*
* @Author: lwnmengjing<lwnmengjing@qq.com>
* @Date: 2023/9/10 15:29:38
* @Last Modified by: lwnmengjing<lwnmengjing@qq.com>
* @Last Modified time: 2023/9/10 15:29:38
*/

type Model struct {
Table string `json:"tableName" yaml:"tableName" binding:"required"`
AutoCreateTime schema.TimeType `json:"autoCreateTime" yaml:"autoCreateTime"`
AutoUpdateTime schema.TimeType `json:"autoUpdateTime" yaml:"autoUpdateTime"`
HardDeleted bool `json:"hardDeleted" yaml:"hardDeleted"`
Fields []*Field `json:"fields" yaml:"fields" binding:"required"`
}

// TableName get table name
func (m *Model) TableName() string {
return m.Table
}

// PrimaryKeys get primary keys
func (m *Model) PrimaryKeys() []string {
var keys []string
for i := range m.Fields {
if m.Fields[i].PrimaryKey {
keys = append(keys, m.Fields[i].Name)
}
}
return keys
}

func (m *Model) Init() {
if m.AutoCreateTime == 0 {
m.AutoCreateTime = schema.UnixSecond
}
if m.AutoUpdateTime == 0 {
m.AutoUpdateTime = schema.UnixSecond
}
for i := range m.Fields {
m.Fields[i].Init()
}
}

func (m *Model) Default(data any) {
for i := range m.Fields {
df := m.Fields[i].DefaultValue
if m.Fields[i].DefaultValueFN != nil {
df = m.Fields[i].DefaultValueFN()
}
if df == "" {
continue
}
reflect.ValueOf(data).Elem().FieldByName(m.Fields[i].GetName()).Set(reflect.ValueOf(df))
}
}

// MakeModel make virtual model
func (m *Model) MakeModel() any {
fieldTypes := make([]reflect.StructField, 0)
for i := range m.Fields {
fieldTypes = append(fieldTypes, m.Fields[i].MakeField())
}
return reflect.New(reflect.StructOf(fieldTypes)).Interface()
}

func (m *Model) MakeList() any {
fieldTypes := make([]reflect.StructField, 0)
for i := range m.Fields {
fieldTypes = append(fieldTypes, m.Fields[i].MakeField())
}
return reflect.New(reflect.SliceOf(reflect.StructOf(fieldTypes))).Interface()
}

func (m *Model) TableScope(db *gorm.DB) *gorm.DB {
return db.Table(m.TableName())
}

func (m *Model) URI(ctx *gin.Context) (f func(*gorm.DB) *gorm.DB) {
return func(db *gorm.DB) *gorm.DB {
db = db.Table(m.TableName())
for _, key := range m.PrimaryKeys() {
db = db.Where(fmt.Sprintf("%s in (?)", key), strings.Split(ctx.Param(key), ","))
}
return db
}
}

func (m *Model) Pagination(ctx *gin.Context, p PaginationImp) (f func(*gorm.DB) *gorm.DB) {
err := ctx.ShouldBindQuery(p)
return func(db *gorm.DB) *gorm.DB {
if err != nil {
_ = db.AddError(err)
return db
}
offset := (p.GetCurrent() - 1) * p.GetPageSize()
return db.Offset(offset).Limit(p.GetPageSize())
}
}

func (m *Model) Search(ctx *gin.Context) (f func(*gorm.DB) *gorm.DB) {
return func(db *gorm.DB) *gorm.DB {
for i := range m.Fields {
v, ok := ctx.GetQuery(m.Fields[i].JsonTag)
if !ok {
continue
}
switch m.Fields[i].Search {
case "exact", "iexact":
db = db.Where(fmt.Sprintf("`%s`.`%s` = ?", m.Table, m.Fields[i].Name), v)
case "contains", "icontains":
db = db.Where(fmt.Sprintf("`%s`.`%s` like ?", m.Table, m.Fields[i].Name), "%"+v+"%")
case "gt":
db = db.Where(fmt.Sprintf("`%s`.`%s` > ?", m.Table, m.Fields[i].Name), v)
case "gte":
db = db.Where(fmt.Sprintf("`%s`.`%s` >= ?", m.Table, m.Fields[i].Name), v)
case "lt":
db = db.Where(fmt.Sprintf("`%s`.`%s` < ?", m.Table, m.Fields[i].Name), v)
case "lte":
db = db.Where(fmt.Sprintf("`%s`.`%s` <= ?", m.Table, m.Fields[i].Name), v)
case "startWith", "istartWith":
db = db.Where(fmt.Sprintf("`%s`.`%s` like ?", m.Table, m.Fields[i].Name), v+"%")
case "endWith", "iendWith":
db = db.Where(fmt.Sprintf("`%s`.`%s` like ?", m.Table, m.Fields[i].Name), "%"+v)
case "in":
arr, ok := ctx.GetQueryArray(m.Fields[i].JsonTag)
if !ok {
continue
}
db = db.Where(fmt.Sprintf("`%s`.`%s` in (?)", m.Table, m.Fields[i].JsonTag), arr)
case "isnull":
db = db.Where(fmt.Sprintf("`%s`.`%s` isnull", m.Table, m.Fields[i].JsonTag))
case "order":
switch v {
case "desc":
db = db.Order(fmt.Sprintf("`%s`.`%s` desc", m.Table, m.Fields[i].JsonTag))
case "asc":
db = db.Order(fmt.Sprintf("`%s`.`%s` asc", m.Table, m.Fields[i].JsonTag))
}
}
}
return db
}
}
Loading