Skip to content

Commit 48ecca6

Browse files
authored
virtual model enabled (#45)
1 parent 69f6abe commit 48ecca6

File tree

9 files changed

+642
-6
lines changed

9 files changed

+642
-6
lines changed

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
## Description
2+
3+
<!-- A clear and concise description what these changes does. -->
4+
5+
## Checklist
6+
7+
<!-- Replace the [ ] with [x] to check the boxes. -->
8+
9+
- [ ] the pull request title describes what this PR does (not a vague title like Update index.md)
10+
- [ ] the pull request targets the default branch of the repository (main)
11+
- [ ] no unintentional fmt.Print left behind after debugging
12+
- [ ] did I name variables, methods and classes according to the naming rules? (https://go.dev/doc/effective_go#names)
13+
- [ ] caught exceptions or throw them to the upper level for processing, not ignored (https://go.dev/doc/effective_go#errors)
14+
- [ ] did I explain all possible solutions and why I chose the one I did?
15+
- [ ] added any comments to make new functions clearer
16+
- [ ] tests are added for the changes I made (if any source code was modified)
17+
- [ ] documentation added or updated
18+
- [ ] I have run the project locally and verified that there are no errors
19+
- [ ] instructions for how reviewers can test the code locally
20+
- [ ] screenshot of the feature/bug fix (if applicable)

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ jobs:
1919
- uses: actions/checkout@v3
2020
- name: install deps
2121
run: |
22+
go get -u golang.org/x/tools/cmd/goimports
2223
go install golang.org/x/tools/cmd/goimports
2324
2425
- name: golangci-lint

go.mod

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ require (
2020
github.com/kamva/mgm/v3 v3.5.0
2121
github.com/prometheus/client_golang v1.12.1
2222
github.com/robfig/cron/v3 v3.0.1
23+
github.com/sanity-io/litter v1.5.5
2324
github.com/sirupsen/logrus v1.8.1
2425
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
2526
github.com/smartystreets/goconvey v1.7.2
@@ -43,8 +44,6 @@ require (
4344
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
4445
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
4546
golang.org/x/arch v0.3.0 // indirect
46-
golang.org/x/mod v0.12.0 // indirect
47-
golang.org/x/tools v0.13.0 // indirect
4847
)
4948

5049
require (

go.sum

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7
125125
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
126126
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
127127
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
128+
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
128129
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
129130
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
130131
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -419,6 +420,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
419420
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
420421
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
421422
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
423+
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
422424
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
423425
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
424426
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@@ -455,6 +457,8 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
455457
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
456458
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
457459
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
460+
github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
461+
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
458462
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
459463
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
460464
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
@@ -481,6 +485,7 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH
481485
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
482486
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
483487
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
488+
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
484489
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
485490
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
486491
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -600,8 +605,6 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
600605
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
601606
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
602607
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
603-
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
604-
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
605608
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
606609
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
607610
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -800,8 +803,6 @@ golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4f
800803
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
801804
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
802805
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
803-
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
804-
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
805806
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
806807
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
807808
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

testdata/test.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
tableName: test
2+
fields:
3+
- name: id
4+
type: string
5+
primaryKey: true
6+
size: 64
7+
creatable: true
8+
updatable: false
9+
readable: true
10+
Comment: ID
11+
- name: name
12+
type: string
13+
size: 63
14+
creatable: true
15+
updatable: true
16+
readable: true
17+
Comment: 名称
18+
index: name_index

virtual/model/field.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package model
2+
3+
import (
4+
"fmt"
5+
"reflect"
6+
"strings"
7+
"time"
8+
9+
"github.com/google/uuid"
10+
"gorm.io/gorm/schema"
11+
)
12+
13+
/*
14+
* @Author: lwnmengjing<lwnmengjing@qq.com>
15+
* @Date: 2023/9/10 16:11:55
16+
* @Last Modified by: lwnmengjing<lwnmengjing@qq.com>
17+
* @Last Modified time: 2023/9/10 16:11:55
18+
*/
19+
20+
type Field struct {
21+
Name string `json:"name" yaml:"name" binding:"required"`
22+
JsonTag string `json:"jsonTag" yaml:"jsonTag"`
23+
DataType schema.DataType `json:"type" yaml:"type" binding:"required"`
24+
PrimaryKey bool `json:"primaryKey" yaml:"primaryKey"`
25+
AutoIncrement bool `json:"autoIncrement" yaml:"autoIncrement"`
26+
AutoIncrementIncrement int64 `json:"autoIncrementIncrement" yaml:"autoIncrementIncrement"`
27+
Creatable bool `json:"creatable" yaml:"creatable"`
28+
Updatable bool `json:"updatable" yaml:"updatable"`
29+
Readable bool `json:"readable" yaml:"readable"`
30+
DefaultValue string `json:"defaultValue" yaml:"defaultValue"`
31+
DefaultValueFN func() string `json:"-" yaml:"-"`
32+
NotNull bool `json:"notNull" yaml:"notNull"`
33+
Unique bool `json:"unique" yaml:"unique"`
34+
Index string `json:"index" yaml:"index"`
35+
Comment string `json:"comment" yaml:"comment"`
36+
Size int `json:"size" yaml:"size"`
37+
Precision int `json:"precision" yaml:"precision"`
38+
Scale int `json:"scale" yaml:"scale"`
39+
Search string `json:"search" yaml:"search"`
40+
}
41+
42+
type DefaultFN string
43+
44+
const (
45+
UUID DefaultFN = "uuid"
46+
Now DefaultFN = "now"
47+
)
48+
49+
var UUIDFN = func() string {
50+
return strings.ReplaceAll(uuid.New().String(), "-", "")
51+
}
52+
53+
var NowFN = func() string {
54+
return time.Now().String()
55+
}
56+
57+
func (f *Field) Init() {
58+
if f.JsonTag == "" {
59+
f.JsonTag = f.Name
60+
}
61+
if f.PrimaryKey {
62+
f.DefaultValueFN = UUIDFN
63+
}
64+
if f.DataType == schema.Time && f.NotNull {
65+
f.DefaultValueFN = NowFN
66+
}
67+
}
68+
69+
func (f *Field) GetName() string {
70+
return strings.ToUpper(f.Name[:1]) + f.Name[1:]
71+
}
72+
73+
func (f *Field) MakeField() reflect.StructField {
74+
field := reflect.StructField{
75+
Name: f.GetName(),
76+
Tag: reflect.StructTag(fmt.Sprintf(`json:"%s" gorm:"column:%s"`, f.JsonTag, f.Name)),
77+
}
78+
switch f.DataType {
79+
case schema.Bool:
80+
field.Type = reflect.TypeOf(false)
81+
case schema.Float:
82+
field.Type = reflect.TypeOf(float64(0))
83+
case schema.Int:
84+
field.Type = reflect.TypeOf(int(0))
85+
case schema.Uint:
86+
field.Type = reflect.TypeOf(uint(0))
87+
case schema.Time:
88+
field.Type = reflect.TypeOf(time.Time{})
89+
default:
90+
field.Type = reflect.TypeOf("")
91+
}
92+
return field
93+
}

virtual/model/model.go

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package model
2+
3+
import (
4+
"fmt"
5+
"reflect"
6+
"strings"
7+
8+
"github.com/gin-gonic/gin"
9+
"gorm.io/gorm"
10+
"gorm.io/gorm/schema"
11+
)
12+
13+
/*
14+
* @Author: lwnmengjing<lwnmengjing@qq.com>
15+
* @Date: 2023/9/10 15:29:38
16+
* @Last Modified by: lwnmengjing<lwnmengjing@qq.com>
17+
* @Last Modified time: 2023/9/10 15:29:38
18+
*/
19+
20+
type Model struct {
21+
Table string `json:"tableName" yaml:"tableName" binding:"required"`
22+
AutoCreateTime schema.TimeType `json:"autoCreateTime" yaml:"autoCreateTime"`
23+
AutoUpdateTime schema.TimeType `json:"autoUpdateTime" yaml:"autoUpdateTime"`
24+
HardDeleted bool `json:"hardDeleted" yaml:"hardDeleted"`
25+
Fields []*Field `json:"fields" yaml:"fields" binding:"required"`
26+
}
27+
28+
// TableName get table name
29+
func (m *Model) TableName() string {
30+
return m.Table
31+
}
32+
33+
// PrimaryKeys get primary keys
34+
func (m *Model) PrimaryKeys() []string {
35+
var keys []string
36+
for i := range m.Fields {
37+
if m.Fields[i].PrimaryKey {
38+
keys = append(keys, m.Fields[i].Name)
39+
}
40+
}
41+
return keys
42+
}
43+
44+
func (m *Model) Init() {
45+
if m.AutoCreateTime == 0 {
46+
m.AutoCreateTime = schema.UnixSecond
47+
}
48+
if m.AutoUpdateTime == 0 {
49+
m.AutoUpdateTime = schema.UnixSecond
50+
}
51+
for i := range m.Fields {
52+
m.Fields[i].Init()
53+
}
54+
}
55+
56+
func (m *Model) Default(data any) {
57+
for i := range m.Fields {
58+
df := m.Fields[i].DefaultValue
59+
if m.Fields[i].DefaultValueFN != nil {
60+
df = m.Fields[i].DefaultValueFN()
61+
}
62+
if df == "" {
63+
continue
64+
}
65+
reflect.ValueOf(data).Elem().FieldByName(m.Fields[i].GetName()).Set(reflect.ValueOf(df))
66+
}
67+
}
68+
69+
// MakeModel make virtual model
70+
func (m *Model) MakeModel() any {
71+
fieldTypes := make([]reflect.StructField, 0)
72+
for i := range m.Fields {
73+
fieldTypes = append(fieldTypes, m.Fields[i].MakeField())
74+
}
75+
return reflect.New(reflect.StructOf(fieldTypes)).Interface()
76+
}
77+
78+
func (m *Model) MakeList() any {
79+
fieldTypes := make([]reflect.StructField, 0)
80+
for i := range m.Fields {
81+
fieldTypes = append(fieldTypes, m.Fields[i].MakeField())
82+
}
83+
return reflect.New(reflect.SliceOf(reflect.StructOf(fieldTypes))).Interface()
84+
}
85+
86+
func (m *Model) TableScope(db *gorm.DB) *gorm.DB {
87+
return db.Table(m.TableName())
88+
}
89+
90+
func (m *Model) URI(ctx *gin.Context) (f func(*gorm.DB) *gorm.DB) {
91+
return func(db *gorm.DB) *gorm.DB {
92+
db = db.Table(m.TableName())
93+
for _, key := range m.PrimaryKeys() {
94+
db = db.Where(fmt.Sprintf("%s in (?)", key), strings.Split(ctx.Param(key), ","))
95+
}
96+
return db
97+
}
98+
}
99+
100+
func (m *Model) Pagination(ctx *gin.Context, p PaginationImp) (f func(*gorm.DB) *gorm.DB) {
101+
err := ctx.ShouldBindQuery(p)
102+
return func(db *gorm.DB) *gorm.DB {
103+
if err != nil {
104+
_ = db.AddError(err)
105+
return db
106+
}
107+
offset := (p.GetCurrent() - 1) * p.GetPageSize()
108+
return db.Offset(offset).Limit(p.GetPageSize())
109+
}
110+
}
111+
112+
func (m *Model) Search(ctx *gin.Context) (f func(*gorm.DB) *gorm.DB) {
113+
return func(db *gorm.DB) *gorm.DB {
114+
for i := range m.Fields {
115+
v, ok := ctx.GetQuery(m.Fields[i].JsonTag)
116+
if !ok {
117+
continue
118+
}
119+
switch m.Fields[i].Search {
120+
case "exact", "iexact":
121+
db = db.Where(fmt.Sprintf("`%s`.`%s` = ?", m.Table, m.Fields[i].Name), v)
122+
case "contains", "icontains":
123+
db = db.Where(fmt.Sprintf("`%s`.`%s` like ?", m.Table, m.Fields[i].Name), "%"+v+"%")
124+
case "gt":
125+
db = db.Where(fmt.Sprintf("`%s`.`%s` > ?", m.Table, m.Fields[i].Name), v)
126+
case "gte":
127+
db = db.Where(fmt.Sprintf("`%s`.`%s` >= ?", m.Table, m.Fields[i].Name), v)
128+
case "lt":
129+
db = db.Where(fmt.Sprintf("`%s`.`%s` < ?", m.Table, m.Fields[i].Name), v)
130+
case "lte":
131+
db = db.Where(fmt.Sprintf("`%s`.`%s` <= ?", m.Table, m.Fields[i].Name), v)
132+
case "startWith", "istartWith":
133+
db = db.Where(fmt.Sprintf("`%s`.`%s` like ?", m.Table, m.Fields[i].Name), v+"%")
134+
case "endWith", "iendWith":
135+
db = db.Where(fmt.Sprintf("`%s`.`%s` like ?", m.Table, m.Fields[i].Name), "%"+v)
136+
case "in":
137+
arr, ok := ctx.GetQueryArray(m.Fields[i].JsonTag)
138+
if !ok {
139+
continue
140+
}
141+
db = db.Where(fmt.Sprintf("`%s`.`%s` in (?)", m.Table, m.Fields[i].JsonTag), arr)
142+
case "isnull":
143+
db = db.Where(fmt.Sprintf("`%s`.`%s` isnull", m.Table, m.Fields[i].JsonTag))
144+
case "order":
145+
switch v {
146+
case "desc":
147+
db = db.Order(fmt.Sprintf("`%s`.`%s` desc", m.Table, m.Fields[i].JsonTag))
148+
case "asc":
149+
db = db.Order(fmt.Sprintf("`%s`.`%s` asc", m.Table, m.Fields[i].JsonTag))
150+
}
151+
}
152+
}
153+
return db
154+
}
155+
}

0 commit comments

Comments
 (0)