mirror of
https://github.com/zeromicro/go-zero.git
synced 2026-05-11 08:50:00 +08:00
Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d7f1d23b4 | ||
|
|
84ab11ac09 | ||
|
|
67804a6bb2 | ||
|
|
65ee877236 | ||
|
|
b060867009 | ||
|
|
4d53045c6b | ||
|
|
cecd4b1b75 | ||
|
|
7cd0463953 | ||
|
|
7a82cf80ce | ||
|
|
f997aee3ba | ||
|
|
88ec89bdbd | ||
|
|
7d1b43780a | ||
|
|
4b5c2de376 | ||
|
|
e5c560e8ba | ||
|
|
bed494d904 | ||
|
|
2dfecda465 | ||
|
|
3ebb1e0221 | ||
|
|
348184904c | ||
|
|
7a27fa50a1 | ||
|
|
8d4951c990 | ||
|
|
6e57f6c527 | ||
|
|
b9ac51b6c3 | ||
|
|
702e8d79ce | ||
|
|
95a9dabf8b | ||
|
|
bae66c49c2 | ||
|
|
e0afe0b4bb | ||
|
|
24fb29a356 | ||
|
|
71083b5e64 | ||
|
|
1174f17bd9 | ||
|
|
d6d8fc21d8 | ||
|
|
9592639cb4 | ||
|
|
abcb28e506 | ||
|
|
a92f65580c | ||
|
|
3819f67cf4 | ||
|
|
295c8d2934 | ||
|
|
88da8685dd | ||
|
|
c7831ac96d | ||
|
|
e898761762 | ||
|
|
13d1c5cd00 |
@@ -74,27 +74,29 @@ func (rw *RollingWindow) span() int {
|
||||
|
||||
func (rw *RollingWindow) updateOffset() {
|
||||
span := rw.span()
|
||||
if span > 0 {
|
||||
offset := rw.offset
|
||||
// reset expired buckets
|
||||
start := offset + 1
|
||||
steps := start + span
|
||||
var remainder int
|
||||
if steps > rw.size {
|
||||
remainder = steps - rw.size
|
||||
steps = rw.size
|
||||
}
|
||||
for i := start; i < steps; i++ {
|
||||
rw.win.resetBucket(i)
|
||||
offset = i
|
||||
}
|
||||
for i := 0; i < remainder; i++ {
|
||||
rw.win.resetBucket(i)
|
||||
offset = i
|
||||
}
|
||||
rw.offset = offset
|
||||
rw.lastTime = timex.Now()
|
||||
if span <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
offset := rw.offset
|
||||
start := offset + 1
|
||||
steps := start + span
|
||||
var remainder int
|
||||
if steps > rw.size {
|
||||
remainder = steps - rw.size
|
||||
steps = rw.size
|
||||
}
|
||||
|
||||
// reset expired buckets
|
||||
for i := start; i < steps; i++ {
|
||||
rw.win.resetBucket(i)
|
||||
}
|
||||
for i := 0; i < remainder; i++ {
|
||||
rw.win.resetBucket(i)
|
||||
}
|
||||
|
||||
rw.offset = (offset + span) % rw.size
|
||||
rw.lastTime = timex.Now()
|
||||
}
|
||||
|
||||
type Bucket struct {
|
||||
@@ -118,9 +120,9 @@ type window struct {
|
||||
}
|
||||
|
||||
func newWindow(size int) *window {
|
||||
var buckets []*Bucket
|
||||
buckets := make([]*Bucket, size)
|
||||
for i := 0; i < size; i++ {
|
||||
buckets = append(buckets, new(Bucket))
|
||||
buckets[i] = new(Bucket)
|
||||
}
|
||||
return &window{
|
||||
buckets: buckets,
|
||||
@@ -134,12 +136,12 @@ func (w *window) add(offset int, v float64) {
|
||||
|
||||
func (w *window) reduce(start, count int, fn func(b *Bucket)) {
|
||||
for i := 0; i < count; i++ {
|
||||
fn(w.buckets[(start+i)%len(w.buckets)])
|
||||
fn(w.buckets[(start+i)%w.size])
|
||||
}
|
||||
}
|
||||
|
||||
func (w *window) resetBucket(offset int) {
|
||||
w.buckets[offset].reset()
|
||||
w.buckets[offset%w.size].reset()
|
||||
}
|
||||
|
||||
func IgnoreCurrentBucket() RollingWindowOption {
|
||||
|
||||
@@ -204,6 +204,7 @@ func (tw *TimingWheel) removeTask(key interface{}) {
|
||||
|
||||
timer := val.(*positionEntry)
|
||||
timer.item.removed = true
|
||||
tw.timers.Del(key)
|
||||
}
|
||||
|
||||
func (tw *TimingWheel) run() {
|
||||
@@ -248,7 +249,6 @@ func (tw *TimingWheel) scanAndRunTasks(l *list.List) {
|
||||
if task.removed {
|
||||
next := e.Next()
|
||||
l.Remove(e)
|
||||
tw.timers.Del(task.key)
|
||||
e = next
|
||||
continue
|
||||
} else if task.circle > 0 {
|
||||
@@ -301,6 +301,7 @@ func (tw *TimingWheel) setTask(task *timingEntry) {
|
||||
func (tw *TimingWheel) setTimerPosition(pos int, task *timingEntry) {
|
||||
if val, ok := tw.timers.Get(task.key); ok {
|
||||
timer := val.(*positionEntry)
|
||||
timer.item = task
|
||||
timer.pos = pos
|
||||
} else {
|
||||
tw.timers.Set(task.key, &positionEntry{
|
||||
|
||||
@@ -594,6 +594,31 @@ func TestTimingWheel_ElapsedAndSetThenMove(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMoveAndRemoveTask(t *testing.T) {
|
||||
ticker := timex.NewFakeTicker()
|
||||
tick := func(v int) {
|
||||
for i := 0; i < v; i++ {
|
||||
ticker.Tick()
|
||||
}
|
||||
}
|
||||
var keys []int
|
||||
tw, _ := newTimingWheelWithClock(testStep, 10, func(k, v interface{}) {
|
||||
assert.Equal(t, "any", k)
|
||||
assert.Equal(t, 3, v.(int))
|
||||
keys = append(keys, v.(int))
|
||||
ticker.Done()
|
||||
}, ticker)
|
||||
defer tw.Stop()
|
||||
tw.SetTimer("any", 3, testStep*8)
|
||||
tick(6)
|
||||
tw.MoveTimer("any", testStep*7)
|
||||
tick(3)
|
||||
tw.RemoveTimer("any")
|
||||
tick(30)
|
||||
time.Sleep(time.Millisecond)
|
||||
assert.Equal(t, 0, len(keys))
|
||||
}
|
||||
|
||||
func BenchmarkTimingWheel(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package limit
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/alicebob/miniredis"
|
||||
"github.com/alicebob/miniredis/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tal-tech/go-zero/core/stores/redis"
|
||||
"github.com/tal-tech/go-zero/core/stores/redis/redistest"
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/alicebob/miniredis"
|
||||
"github.com/alicebob/miniredis/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tal-tech/go-zero/core/logx"
|
||||
"github.com/tal-tech/go-zero/core/stores/redis"
|
||||
|
||||
2
core/stores/cache/cachenode_test.go
vendored
2
core/stores/cache/cachenode_test.go
vendored
@@ -9,7 +9,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/alicebob/miniredis"
|
||||
"github.com/alicebob/miniredis/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tal-tech/go-zero/core/logx"
|
||||
"github.com/tal-tech/go-zero/core/mathx"
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/alicebob/miniredis"
|
||||
"github.com/alicebob/miniredis/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tal-tech/go-zero/core/hash"
|
||||
"github.com/tal-tech/go-zero/core/stores/cache"
|
||||
|
||||
@@ -42,6 +42,12 @@ type (
|
||||
red.Cmdable
|
||||
}
|
||||
|
||||
// GeoLocation is used with GeoAdd to add geospatial location.
|
||||
GeoLocation = red.GeoLocation
|
||||
// GeoRadiusQuery is used with GeoRadius to query geospatial index.
|
||||
GeoRadiusQuery = red.GeoRadiusQuery
|
||||
GeoPos = red.GeoPos
|
||||
|
||||
Pipeliner = red.Pipeliner
|
||||
|
||||
// Z represents sorted set member.
|
||||
@@ -173,6 +179,107 @@ func (s *Redis) Expireat(key string, expireTime int64) error {
|
||||
}, acceptable)
|
||||
}
|
||||
|
||||
func (s *Redis) GeoAdd(key string, geoLocation ...*GeoLocation) (val int64, err error) {
|
||||
err = s.brk.DoWithAcceptable(func() error {
|
||||
conn, err := getRedis(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if v, err := conn.GeoAdd(key, geoLocation...).Result(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
val = v
|
||||
return nil
|
||||
}
|
||||
}, acceptable)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Redis) GeoDist(key string, member1, member2, unit string) (val float64, err error) {
|
||||
err = s.brk.DoWithAcceptable(func() error {
|
||||
conn, err := getRedis(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if v, err := conn.GeoDist(key, member1, member2, unit).Result(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
val = v
|
||||
return nil
|
||||
}
|
||||
}, acceptable)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Redis) GeoHash(key string, members ...string) (val []string, err error) {
|
||||
err = s.brk.DoWithAcceptable(func() error {
|
||||
conn, err := getRedis(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if v, err := conn.GeoHash(key, members...).Result(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
val = v
|
||||
return nil
|
||||
}
|
||||
}, acceptable)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Redis) GeoRadius(key string, longitude, latitude float64, query *GeoRadiusQuery) (val []GeoLocation, err error) {
|
||||
err = s.brk.DoWithAcceptable(func() error {
|
||||
conn, err := getRedis(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if v, err := conn.GeoRadius(key, longitude, latitude, query).Result(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
val = v
|
||||
return nil
|
||||
}
|
||||
}, acceptable)
|
||||
return
|
||||
}
|
||||
func (s *Redis) GeoRadiusByMember(key, member string, query *GeoRadiusQuery) (val []GeoLocation, err error) {
|
||||
err = s.brk.DoWithAcceptable(func() error {
|
||||
conn, err := getRedis(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if v, err := conn.GeoRadiusByMember(key, member, query).Result(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
val = v
|
||||
return nil
|
||||
}
|
||||
}, acceptable)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Redis) GeoPos(key string, members ...string) (val []*GeoPos, err error) {
|
||||
err = s.brk.DoWithAcceptable(func() error {
|
||||
conn, err := getRedis(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if v, err := conn.GeoPos(key, members...).Result(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
val = v
|
||||
return nil
|
||||
}
|
||||
}, acceptable)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Redis) Get(key string) (val string, err error) {
|
||||
err = s.brk.DoWithAcceptable(func() error {
|
||||
conn, err := getRedis(s)
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/alicebob/miniredis"
|
||||
"github.com/alicebob/miniredis/v2"
|
||||
red "github.com/go-redis/redis"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -556,7 +556,7 @@ func TestRedis_SortedSet(t *testing.T) {
|
||||
val, err = client.Zscore("key", "value1")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, int64(5), val)
|
||||
val, err = NewRedis(client.Addr, "").Zadds("key")
|
||||
_, err = NewRedis(client.Addr, "").Zadds("key")
|
||||
assert.NotNil(t, err)
|
||||
val, err = client.Zadds("key", Pair{
|
||||
Key: "value2",
|
||||
@@ -567,9 +567,9 @@ func TestRedis_SortedSet(t *testing.T) {
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, int64(2), val)
|
||||
pairs, err := NewRedis(client.Addr, "").ZRevRangeWithScores("key", 1, 3)
|
||||
_, err = NewRedis(client.Addr, "").ZRevRangeWithScores("key", 1, 3)
|
||||
assert.NotNil(t, err)
|
||||
pairs, err = client.ZRevRangeWithScores("key", 1, 3)
|
||||
pairs, err := client.ZRevRangeWithScores("key", 1, 3)
|
||||
assert.Nil(t, err)
|
||||
assert.EqualValues(t, []Pair{
|
||||
{
|
||||
@@ -816,6 +816,38 @@ func TestRedisBlpopEx(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestRedisGeo(t *testing.T) {
|
||||
runOnRedis(t, func(client *Redis) {
|
||||
client.Ping()
|
||||
var geoLocation = []*GeoLocation{{Longitude: 13.361389, Latitude: 38.115556, Name: "Palermo"}, {Longitude: 15.087269, Latitude: 37.502669, Name: "Catania"}}
|
||||
v, err := client.GeoAdd("sicily", geoLocation...)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, int64(2), v)
|
||||
v2, err := client.GeoDist("sicily", "Palermo", "Catania", "m")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 166274, int(v2))
|
||||
// GeoHash not support
|
||||
v3, err := client.GeoPos("sicily", "Palermo", "Catania")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, int64(v3[0].Longitude), int64(13))
|
||||
assert.Equal(t, int64(v3[0].Latitude), int64(38))
|
||||
assert.Equal(t, int64(v3[1].Longitude), int64(15))
|
||||
assert.Equal(t, int64(v3[1].Latitude), int64(37))
|
||||
v4, err := client.GeoRadius("sicily", 15, 37, &red.GeoRadiusQuery{WithDist: true, Unit: "km", Radius: 200})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, int64(v4[0].Dist), int64(190))
|
||||
assert.Equal(t, int64(v4[1].Dist), int64(56))
|
||||
var geoLocation2 = []*GeoLocation{{Longitude: 13.583333, Latitude: 37.316667, Name: "Agrigento"}}
|
||||
v5, err := client.GeoAdd("sicily", geoLocation2...)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, int64(1), v5)
|
||||
v6, err := client.GeoRadiusByMember("sicily", "Agrigento", &red.GeoRadiusQuery{Unit: "km", Radius: 100})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, v6[0].Name, "Agrigento")
|
||||
assert.Equal(t, v6[1].Name, "Palermo")
|
||||
})
|
||||
}
|
||||
|
||||
func runOnRedis(t *testing.T, fn func(client *Redis)) {
|
||||
s, err := miniredis.Run()
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -3,7 +3,7 @@ package redistest
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/alicebob/miniredis"
|
||||
"github.com/alicebob/miniredis/v2"
|
||||
"github.com/tal-tech/go-zero/core/lang"
|
||||
"github.com/tal-tech/go-zero/core/stores/redis"
|
||||
)
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/alicebob/miniredis"
|
||||
"github.com/alicebob/miniredis/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tal-tech/go-zero/core/fx"
|
||||
"github.com/tal-tech/go-zero/core/logx"
|
||||
|
||||
@@ -1,33 +1,29 @@
|
||||
type (
|
||||
addReq struct {
|
||||
book string `form:"book"`
|
||||
price int64 `form:"price"`
|
||||
}
|
||||
|
||||
addResp struct {
|
||||
ok bool `json:"ok"`
|
||||
}
|
||||
addReq {
|
||||
book string `form:"book"`
|
||||
price int64 `form:"price"`
|
||||
}
|
||||
|
||||
addResp {
|
||||
ok bool `json:"ok"`
|
||||
}
|
||||
)
|
||||
|
||||
type (
|
||||
checkReq struct {
|
||||
book string `form:"book"`
|
||||
}
|
||||
|
||||
checkResp struct {
|
||||
found bool `json:"found"`
|
||||
price int64 `json:"price"`
|
||||
}
|
||||
checkReq {
|
||||
book string `form:"book"`
|
||||
}
|
||||
|
||||
checkResp {
|
||||
found bool `json:"found"`
|
||||
price int64 `json:"price"`
|
||||
}
|
||||
)
|
||||
|
||||
service bookstore-api {
|
||||
@server(
|
||||
handler: AddHandler
|
||||
)
|
||||
get /add (addReq) returns (addResp)
|
||||
|
||||
@server(
|
||||
handler: CheckHandler
|
||||
)
|
||||
get /check (checkReq) returns (checkResp)
|
||||
@handler AddHandler
|
||||
get /add (addReq) returns (addResp)
|
||||
|
||||
@handler CheckHandler
|
||||
get /check (checkReq) returns (checkResp)
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/tal-tech/go-zero/rest/httpx"
|
||||
)
|
||||
|
||||
func addHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||
func AddHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.AddReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/tal-tech/go-zero/rest/httpx"
|
||||
)
|
||||
|
||||
func checkHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||
func CheckHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.CheckReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
|
||||
@@ -10,16 +10,18 @@ import (
|
||||
)
|
||||
|
||||
func RegisterHandlers(engine *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
engine.AddRoutes([]rest.Route{
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/add",
|
||||
Handler: addHandler(serverCtx),
|
||||
engine.AddRoutes(
|
||||
[]rest.Route{
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/add",
|
||||
Handler: AddHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/check",
|
||||
Handler: CheckHandler(serverCtx),
|
||||
},
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/check",
|
||||
Handler: checkHandler(serverCtx),
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
@@ -29,7 +29,8 @@ func (l *CheckLogic) Check(req types.CheckReq) (*types.CheckResp, error) {
|
||||
Book: req.Book,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
logx.Error(err)
|
||||
return &types.CheckResp{}, err
|
||||
}
|
||||
|
||||
return &types.CheckResp{
|
||||
|
||||
@@ -5,7 +5,8 @@ go 1.15
|
||||
require (
|
||||
github.com/golang/mock v1.4.3
|
||||
github.com/golang/protobuf v1.4.2
|
||||
github.com/tal-tech/go-zero v1.0.16
|
||||
github.com/tal-tech/go-zero v1.0.27
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381
|
||||
google.golang.org/grpc v1.29.1
|
||||
google.golang.org/protobuf v1.25.0
|
||||
)
|
||||
|
||||
@@ -36,6 +36,7 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
|
||||
github.com/coreos/go-systemd/v22 v22.0.0 h1:XJIw/+VlJ+87J+doOxznsAWIdmWuViOVhkQamW5YV28=
|
||||
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
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=
|
||||
@@ -46,6 +47,7 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
|
||||
github.com/dsymonds/gotoc v0.0.0-20160928043926-5aebcfc91819/go.mod h1:MvzMVHq8BH2Ji/o8TGDocVA70byvLrAgFTxkEnmjO4Y=
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4 h1:qk/FSDDxo05wdJH28W+p5yivv7LuLYLRXPPD8KQCtZs=
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/emicklei/proto v1.9.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
@@ -122,9 +124,11 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.14.3 h1:OCJlWkOUoTnl0neNGlf4fUm3TmbEtguw7vR+nGtnDjY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.14.3/go.mod h1:6CwZWGDSPRJidgKAtJVvND6soZe6fT7iteq8wDPdhb0=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
|
||||
github.com/iancoleman/strcase v0.1.2/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
||||
@@ -175,6 +179,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
@@ -210,6 +215,7 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
@@ -232,11 +238,14 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/tal-tech/go-zero v1.0.16 h1:oT7sOFftEUdD/XcXF0xEugX9yhnw4DcQkeMNFLi5KO8=
|
||||
github.com/tal-tech/go-zero v1.0.16/go.mod h1:y2wBHTkxNJw79K9/wCSeDKzv2pCT6x45oOmXEsJdQK8=
|
||||
github.com/tal-tech/go-zero v1.0.27 h1:QMIbaTxibMc/OsO5RTAuKZ8ndbl2dGN6pITQEtp2x/A=
|
||||
github.com/tal-tech/go-zero v1.0.27/go.mod h1:JtNXlsh/CgeIHyQnt5C5M2IcSevW7V0NAnqO93TQgm8=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 h1:lYIiVDtZnyTWlNwiAxLj0bbpTcx1BWCFhXjfsvmPdNc=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
||||
@@ -382,6 +391,7 @@ gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
@@ -393,6 +403,8 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
|
||||
"bookstore/rpc/add/add"
|
||||
"bookstore/rpc/add/internal/config"
|
||||
add "bookstore/rpc/add/internal/pb"
|
||||
"bookstore/rpc/add/internal/server"
|
||||
"bookstore/rpc/add/internal/svc"
|
||||
|
||||
|
||||
305
example/bookstore/rpc/add/add/add.pb.go
Normal file
305
example/bookstore/rpc/add/add/add.pb.go
Normal file
@@ -0,0 +1,305 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.25.0
|
||||
// protoc v3.14.0
|
||||
// source: add.proto
|
||||
|
||||
package add
|
||||
|
||||
import (
|
||||
context "context"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||
// of the legacy proto package is being used.
|
||||
const _ = proto.ProtoPackageIsVersion4
|
||||
|
||||
type AddReq struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Book string `protobuf:"bytes,1,opt,name=book,proto3" json:"book,omitempty"`
|
||||
Price int64 `protobuf:"varint,2,opt,name=price,proto3" json:"price,omitempty"`
|
||||
}
|
||||
|
||||
func (x *AddReq) Reset() {
|
||||
*x = AddReq{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_add_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *AddReq) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*AddReq) ProtoMessage() {}
|
||||
|
||||
func (x *AddReq) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_add_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use AddReq.ProtoReflect.Descriptor instead.
|
||||
func (*AddReq) Descriptor() ([]byte, []int) {
|
||||
return file_add_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *AddReq) GetBook() string {
|
||||
if x != nil {
|
||||
return x.Book
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AddReq) GetPrice() int64 {
|
||||
if x != nil {
|
||||
return x.Price
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type AddResp struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Ok bool `protobuf:"varint,1,opt,name=ok,proto3" json:"ok,omitempty"`
|
||||
}
|
||||
|
||||
func (x *AddResp) Reset() {
|
||||
*x = AddResp{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_add_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *AddResp) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*AddResp) ProtoMessage() {}
|
||||
|
||||
func (x *AddResp) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_add_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use AddResp.ProtoReflect.Descriptor instead.
|
||||
func (*AddResp) Descriptor() ([]byte, []int) {
|
||||
return file_add_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *AddResp) GetOk() bool {
|
||||
if x != nil {
|
||||
return x.Ok
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var File_add_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_add_proto_rawDesc = []byte{
|
||||
0x0a, 0x09, 0x61, 0x64, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x61, 0x64, 0x64,
|
||||
0x22, 0x32, 0x0a, 0x06, 0x61, 0x64, 0x64, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f,
|
||||
0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x6f, 0x6b, 0x12, 0x14,
|
||||
0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x70,
|
||||
0x72, 0x69, 0x63, 0x65, 0x22, 0x19, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x12,
|
||||
0x0e, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x32,
|
||||
0x29, 0x0a, 0x05, 0x61, 0x64, 0x64, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x03, 0x61, 0x64, 0x64, 0x12,
|
||||
0x0b, 0x2e, 0x61, 0x64, 0x64, 0x2e, 0x61, 0x64, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x0c, 0x2e, 0x61,
|
||||
0x64, 0x64, 0x2e, 0x61, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_add_proto_rawDescOnce sync.Once
|
||||
file_add_proto_rawDescData = file_add_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_add_proto_rawDescGZIP() []byte {
|
||||
file_add_proto_rawDescOnce.Do(func() {
|
||||
file_add_proto_rawDescData = protoimpl.X.CompressGZIP(file_add_proto_rawDescData)
|
||||
})
|
||||
return file_add_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_add_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_add_proto_goTypes = []interface{}{
|
||||
(*AddReq)(nil), // 0: add.addReq
|
||||
(*AddResp)(nil), // 1: add.addResp
|
||||
}
|
||||
var file_add_proto_depIdxs = []int32{
|
||||
0, // 0: add.adder.add:input_type -> add.addReq
|
||||
1, // 1: add.adder.add:output_type -> add.addResp
|
||||
1, // [1:2] is the sub-list for method output_type
|
||||
0, // [0:1] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_add_proto_init() }
|
||||
func file_add_proto_init() {
|
||||
if File_add_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_add_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*AddReq); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_add_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*AddResp); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_add_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_add_proto_goTypes,
|
||||
DependencyIndexes: file_add_proto_depIdxs,
|
||||
MessageInfos: file_add_proto_msgTypes,
|
||||
}.Build()
|
||||
File_add_proto = out.File
|
||||
file_add_proto_rawDesc = nil
|
||||
file_add_proto_goTypes = nil
|
||||
file_add_proto_depIdxs = nil
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConnInterface
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion6
|
||||
|
||||
// AdderClient is the client API for Adder service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type AdderClient interface {
|
||||
Add(ctx context.Context, in *AddReq, opts ...grpc.CallOption) (*AddResp, error)
|
||||
}
|
||||
|
||||
type adderClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewAdderClient(cc grpc.ClientConnInterface) AdderClient {
|
||||
return &adderClient{cc}
|
||||
}
|
||||
|
||||
func (c *adderClient) Add(ctx context.Context, in *AddReq, opts ...grpc.CallOption) (*AddResp, error) {
|
||||
out := new(AddResp)
|
||||
err := c.cc.Invoke(ctx, "/add.adder/add", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// AdderServer is the server API for Adder service.
|
||||
type AdderServer interface {
|
||||
Add(context.Context, *AddReq) (*AddResp, error)
|
||||
}
|
||||
|
||||
// UnimplementedAdderServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedAdderServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedAdderServer) Add(context.Context, *AddReq) (*AddResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Add not implemented")
|
||||
}
|
||||
|
||||
func RegisterAdderServer(s *grpc.Server, srv AdderServer) {
|
||||
s.RegisterService(&_Adder_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Adder_Add_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(AddReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AdderServer).Add(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/add.adder/Add",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AdderServer).Add(ctx, req.(*AddReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Adder_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "add.adder",
|
||||
HandlerType: (*AdderServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "add",
|
||||
Handler: _Adder_Add_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "add.proto",
|
||||
}
|
||||
@@ -8,7 +8,7 @@ package adder
|
||||
import (
|
||||
"context"
|
||||
|
||||
add "bookstore/rpc/add/internal/pb"
|
||||
"bookstore/rpc/add/add"
|
||||
|
||||
"github.com/tal-tech/go-zero/zrpc"
|
||||
)
|
||||
@@ -33,6 +33,6 @@ func NewAdder(cli zrpc.Client) Adder {
|
||||
}
|
||||
|
||||
func (m *defaultAdder) Add(ctx context.Context, in *AddReq) (*AddResp, error) {
|
||||
adder := add.NewAdderClient(m.cli.Conn())
|
||||
return adder.Add(ctx, in)
|
||||
client := add.NewAdderClient(m.cli.Conn())
|
||||
return client.Add(ctx, in)
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: adder.go
|
||||
|
||||
// Package adder is a generated GoMock package.
|
||||
package adder
|
||||
|
||||
import (
|
||||
context "context"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// MockAdder is a mock of Adder interface
|
||||
type MockAdder struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockAdderMockRecorder
|
||||
}
|
||||
|
||||
// MockAdderMockRecorder is the mock recorder for MockAdder
|
||||
type MockAdderMockRecorder struct {
|
||||
mock *MockAdder
|
||||
}
|
||||
|
||||
// NewMockAdder creates a new mock instance
|
||||
func NewMockAdder(ctrl *gomock.Controller) *MockAdder {
|
||||
mock := &MockAdder{ctrl: ctrl}
|
||||
mock.recorder = &MockAdderMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockAdder) EXPECT() *MockAdderMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Add mocks base method
|
||||
func (m *MockAdder) Add(ctx context.Context, in *AddReq) (*AddResp, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Add", ctx, in)
|
||||
ret0, _ := ret[0].(*AddResp)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Add indicates an expected call of Add
|
||||
func (mr *MockAdderMockRecorder) Add(ctx, in interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockAdder)(nil).Add), ctx, in)
|
||||
}
|
||||
@@ -8,6 +8,5 @@ import (
|
||||
type Config struct {
|
||||
zrpc.RpcServerConf
|
||||
DataSource string
|
||||
Table string
|
||||
Cache cache.CacheConf
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package logic
|
||||
import (
|
||||
"context"
|
||||
|
||||
add "bookstore/rpc/add/internal/pb"
|
||||
add "bookstore/rpc/add/adder"
|
||||
"bookstore/rpc/add/internal/svc"
|
||||
"bookstore/rpc/model"
|
||||
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
// Code generated by protoc-gen-go.
|
||||
// source: add.proto
|
||||
// DO NOT EDIT!
|
||||
|
||||
/*
|
||||
Package add is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
add.proto
|
||||
|
||||
It has these top-level messages:
|
||||
AddReq
|
||||
AddResp
|
||||
*/
|
||||
package add
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
type AddReq struct {
|
||||
Book string `protobuf:"bytes,1,opt,name=book" json:"book,omitempty"`
|
||||
Price int64 `protobuf:"varint,2,opt,name=price" json:"price,omitempty"`
|
||||
}
|
||||
|
||||
func (m *AddReq) Reset() { *m = AddReq{} }
|
||||
func (m *AddReq) String() string { return proto.CompactTextString(m) }
|
||||
func (*AddReq) ProtoMessage() {}
|
||||
func (*AddReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
|
||||
func (m *AddReq) GetBook() string {
|
||||
if m != nil {
|
||||
return m.Book
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *AddReq) GetPrice() int64 {
|
||||
if m != nil {
|
||||
return m.Price
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type AddResp struct {
|
||||
Ok bool `protobuf:"varint,1,opt,name=ok" json:"ok,omitempty"`
|
||||
}
|
||||
|
||||
func (m *AddResp) Reset() { *m = AddResp{} }
|
||||
func (m *AddResp) String() string { return proto.CompactTextString(m) }
|
||||
func (*AddResp) ProtoMessage() {}
|
||||
func (*AddResp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||
|
||||
func (m *AddResp) GetOk() bool {
|
||||
if m != nil {
|
||||
return m.Ok
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*AddReq)(nil), "add.addReq")
|
||||
proto.RegisterType((*AddResp)(nil), "add.addResp")
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// Client API for Adder service
|
||||
|
||||
type AdderClient interface {
|
||||
Add(ctx context.Context, in *AddReq, opts ...grpc.CallOption) (*AddResp, error)
|
||||
}
|
||||
|
||||
type adderClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewAdderClient(cc *grpc.ClientConn) AdderClient {
|
||||
return &adderClient{cc}
|
||||
}
|
||||
|
||||
func (c *adderClient) Add(ctx context.Context, in *AddReq, opts ...grpc.CallOption) (*AddResp, error) {
|
||||
out := new(AddResp)
|
||||
err := grpc.Invoke(ctx, "/add.adder/add", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for Adder service
|
||||
|
||||
type AdderServer interface {
|
||||
Add(context.Context, *AddReq) (*AddResp, error)
|
||||
}
|
||||
|
||||
func RegisterAdderServer(s *grpc.Server, srv AdderServer) {
|
||||
s.RegisterService(&_Adder_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Adder_Add_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(AddReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AdderServer).Add(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/add.adder/Add",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AdderServer).Add(ctx, req.(*AddReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Adder_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "add.adder",
|
||||
HandlerType: (*AdderServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "add",
|
||||
Handler: _Adder_Add_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "add.proto",
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("add.proto", fileDescriptor0) }
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 136 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x4c, 0x4c, 0x49, 0xd1,
|
||||
0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x4e, 0x4c, 0x49, 0x51, 0x32, 0xe2, 0x62, 0x4b, 0x4c,
|
||||
0x49, 0x09, 0x4a, 0x2d, 0x14, 0x12, 0xe2, 0x62, 0x49, 0xca, 0xcf, 0xcf, 0x96, 0x60, 0x54, 0x60,
|
||||
0xd4, 0xe0, 0x0c, 0x02, 0xb3, 0x85, 0x44, 0xb8, 0x58, 0x0b, 0x8a, 0x32, 0x93, 0x53, 0x25, 0x98,
|
||||
0x14, 0x18, 0x35, 0x98, 0x83, 0x20, 0x1c, 0x25, 0x49, 0x2e, 0x76, 0xb0, 0x9e, 0xe2, 0x02, 0x21,
|
||||
0x3e, 0x2e, 0x26, 0xa8, 0x16, 0x8e, 0x20, 0xa6, 0xfc, 0x6c, 0x23, 0x4d, 0x2e, 0xd6, 0xc4, 0x94,
|
||||
0x94, 0xd4, 0x22, 0x21, 0x05, 0x2e, 0x90, 0xf1, 0x42, 0xdc, 0x7a, 0x20, 0xfb, 0x20, 0x36, 0x48,
|
||||
0xf1, 0x20, 0x38, 0xc5, 0x05, 0x49, 0x6c, 0x60, 0x57, 0x18, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff,
|
||||
0xe2, 0x6d, 0xb5, 0x91, 0x92, 0x00, 0x00, 0x00,
|
||||
}
|
||||
@@ -6,8 +6,8 @@ package server
|
||||
import (
|
||||
"context"
|
||||
|
||||
"bookstore/rpc/add/add"
|
||||
"bookstore/rpc/add/internal/logic"
|
||||
add "bookstore/rpc/add/internal/pb"
|
||||
"bookstore/rpc/add/internal/svc"
|
||||
)
|
||||
|
||||
|
||||
@@ -9,12 +9,12 @@ import (
|
||||
|
||||
type ServiceContext struct {
|
||||
c config.Config
|
||||
Model *model.BookModel
|
||||
Model model.BookModel
|
||||
}
|
||||
|
||||
func NewServiceContext(c config.Config) *ServiceContext {
|
||||
return &ServiceContext{
|
||||
c: c,
|
||||
Model: model.NewBookModel(sqlx.NewMysql(c.DataSource), c.Cache, c.Table),
|
||||
c: c,
|
||||
Model: model.NewBookModel(sqlx.NewMysql(c.DataSource), c.Cache),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
|
||||
"bookstore/rpc/check/check"
|
||||
"bookstore/rpc/check/internal/config"
|
||||
check "bookstore/rpc/check/internal/pb"
|
||||
"bookstore/rpc/check/internal/server"
|
||||
"bookstore/rpc/check/internal/svc"
|
||||
|
||||
|
||||
306
example/bookstore/rpc/check/check/check.pb.go
Normal file
306
example/bookstore/rpc/check/check/check.pb.go
Normal file
@@ -0,0 +1,306 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.25.0
|
||||
// protoc v3.14.0
|
||||
// source: check.proto
|
||||
|
||||
package check
|
||||
|
||||
import (
|
||||
context "context"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||
// of the legacy proto package is being used.
|
||||
const _ = proto.ProtoPackageIsVersion4
|
||||
|
||||
type CheckReq struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Book string `protobuf:"bytes,1,opt,name=book,proto3" json:"book,omitempty"`
|
||||
}
|
||||
|
||||
func (x *CheckReq) Reset() {
|
||||
*x = CheckReq{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_check_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *CheckReq) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CheckReq) ProtoMessage() {}
|
||||
|
||||
func (x *CheckReq) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_check_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use CheckReq.ProtoReflect.Descriptor instead.
|
||||
func (*CheckReq) Descriptor() ([]byte, []int) {
|
||||
return file_check_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *CheckReq) GetBook() string {
|
||||
if x != nil {
|
||||
return x.Book
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type CheckResp struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Found bool `protobuf:"varint,1,opt,name=found,proto3" json:"found,omitempty"`
|
||||
Price int64 `protobuf:"varint,2,opt,name=price,proto3" json:"price,omitempty"`
|
||||
}
|
||||
|
||||
func (x *CheckResp) Reset() {
|
||||
*x = CheckResp{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_check_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *CheckResp) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CheckResp) ProtoMessage() {}
|
||||
|
||||
func (x *CheckResp) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_check_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use CheckResp.ProtoReflect.Descriptor instead.
|
||||
func (*CheckResp) Descriptor() ([]byte, []int) {
|
||||
return file_check_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *CheckResp) GetFound() bool {
|
||||
if x != nil {
|
||||
return x.Found
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *CheckResp) GetPrice() int64 {
|
||||
if x != nil {
|
||||
return x.Price
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var File_check_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_check_proto_rawDesc = []byte{
|
||||
0x0a, 0x0b, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x63,
|
||||
0x68, 0x65, 0x63, 0x6b, 0x22, 0x1e, 0x0a, 0x08, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71,
|
||||
0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
|
||||
0x62, 0x6f, 0x6f, 0x6b, 0x22, 0x37, 0x0a, 0x09, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73,
|
||||
0x70, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08,
|
||||
0x52, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x32, 0x35, 0x0a,
|
||||
0x07, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x12, 0x2a, 0x0a, 0x05, 0x63, 0x68, 0x65, 0x63,
|
||||
0x6b, 0x12, 0x0f, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x52,
|
||||
0x65, 0x71, 0x1a, 0x10, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b,
|
||||
0x52, 0x65, 0x73, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_check_proto_rawDescOnce sync.Once
|
||||
file_check_proto_rawDescData = file_check_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_check_proto_rawDescGZIP() []byte {
|
||||
file_check_proto_rawDescOnce.Do(func() {
|
||||
file_check_proto_rawDescData = protoimpl.X.CompressGZIP(file_check_proto_rawDescData)
|
||||
})
|
||||
return file_check_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_check_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_check_proto_goTypes = []interface{}{
|
||||
(*CheckReq)(nil), // 0: check.checkReq
|
||||
(*CheckResp)(nil), // 1: check.checkResp
|
||||
}
|
||||
var file_check_proto_depIdxs = []int32{
|
||||
0, // 0: check.checker.check:input_type -> check.checkReq
|
||||
1, // 1: check.checker.check:output_type -> check.checkResp
|
||||
1, // [1:2] is the sub-list for method output_type
|
||||
0, // [0:1] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_check_proto_init() }
|
||||
func file_check_proto_init() {
|
||||
if File_check_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_check_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*CheckReq); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_check_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*CheckResp); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_check_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_check_proto_goTypes,
|
||||
DependencyIndexes: file_check_proto_depIdxs,
|
||||
MessageInfos: file_check_proto_msgTypes,
|
||||
}.Build()
|
||||
File_check_proto = out.File
|
||||
file_check_proto_rawDesc = nil
|
||||
file_check_proto_goTypes = nil
|
||||
file_check_proto_depIdxs = nil
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConnInterface
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion6
|
||||
|
||||
// CheckerClient is the client API for Checker service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type CheckerClient interface {
|
||||
Check(ctx context.Context, in *CheckReq, opts ...grpc.CallOption) (*CheckResp, error)
|
||||
}
|
||||
|
||||
type checkerClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewCheckerClient(cc grpc.ClientConnInterface) CheckerClient {
|
||||
return &checkerClient{cc}
|
||||
}
|
||||
|
||||
func (c *checkerClient) Check(ctx context.Context, in *CheckReq, opts ...grpc.CallOption) (*CheckResp, error) {
|
||||
out := new(CheckResp)
|
||||
err := c.cc.Invoke(ctx, "/check.checker/check", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// CheckerServer is the server API for Checker service.
|
||||
type CheckerServer interface {
|
||||
Check(context.Context, *CheckReq) (*CheckResp, error)
|
||||
}
|
||||
|
||||
// UnimplementedCheckerServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedCheckerServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedCheckerServer) Check(context.Context, *CheckReq) (*CheckResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Check not implemented")
|
||||
}
|
||||
|
||||
func RegisterCheckerServer(s *grpc.Server, srv CheckerServer) {
|
||||
s.RegisterService(&_Checker_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Checker_Check_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CheckReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(CheckerServer).Check(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/check.checker/Check",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(CheckerServer).Check(ctx, req.(*CheckReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Checker_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "check.checker",
|
||||
HandlerType: (*CheckerServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "check",
|
||||
Handler: _Checker_Check_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "check.proto",
|
||||
}
|
||||
@@ -8,7 +8,7 @@ package checker
|
||||
import (
|
||||
"context"
|
||||
|
||||
check "bookstore/rpc/check/internal/pb"
|
||||
"bookstore/rpc/check/check"
|
||||
|
||||
"github.com/tal-tech/go-zero/zrpc"
|
||||
)
|
||||
@@ -33,6 +33,6 @@ func NewChecker(cli zrpc.Client) Checker {
|
||||
}
|
||||
|
||||
func (m *defaultChecker) Check(ctx context.Context, in *CheckReq) (*CheckResp, error) {
|
||||
checker := check.NewCheckerClient(m.cli.Conn())
|
||||
return checker.Check(ctx, in)
|
||||
client := check.NewCheckerClient(m.cli.Conn())
|
||||
return client.Check(ctx, in)
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: checker.go
|
||||
|
||||
// Package checker is a generated GoMock package.
|
||||
package checker
|
||||
|
||||
import (
|
||||
context "context"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// MockChecker is a mock of Checker interface
|
||||
type MockChecker struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockCheckerMockRecorder
|
||||
}
|
||||
|
||||
// MockCheckerMockRecorder is the mock recorder for MockChecker
|
||||
type MockCheckerMockRecorder struct {
|
||||
mock *MockChecker
|
||||
}
|
||||
|
||||
// NewMockChecker creates a new mock instance
|
||||
func NewMockChecker(ctrl *gomock.Controller) *MockChecker {
|
||||
mock := &MockChecker{ctrl: ctrl}
|
||||
mock.recorder = &MockCheckerMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockChecker) EXPECT() *MockCheckerMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Check mocks base method
|
||||
func (m *MockChecker) Check(ctx context.Context, in *CheckReq) (*CheckResp, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Check", ctx, in)
|
||||
ret0, _ := ret[0].(*CheckResp)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Check indicates an expected call of Check
|
||||
func (mr *MockCheckerMockRecorder) Check(ctx, in interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Check", reflect.TypeOf((*MockChecker)(nil).Check), ctx, in)
|
||||
}
|
||||
@@ -8,6 +8,5 @@ import (
|
||||
type Config struct {
|
||||
zrpc.RpcServerConf
|
||||
DataSource string
|
||||
Table string
|
||||
Cache cache.CacheConf
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package logic
|
||||
import (
|
||||
"context"
|
||||
|
||||
check "bookstore/rpc/check/internal/pb"
|
||||
check "bookstore/rpc/check/checker"
|
||||
"bookstore/rpc/check/internal/svc"
|
||||
|
||||
"github.com/tal-tech/go-zero/core/logx"
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
// Code generated by protoc-gen-go.
|
||||
// source: check.proto
|
||||
// DO NOT EDIT!
|
||||
|
||||
/*
|
||||
Package check is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
check.proto
|
||||
|
||||
It has these top-level messages:
|
||||
CheckReq
|
||||
CheckResp
|
||||
*/
|
||||
package check
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
type CheckReq struct {
|
||||
Book string `protobuf:"bytes,1,opt,name=book" json:"book,omitempty"`
|
||||
}
|
||||
|
||||
func (m *CheckReq) Reset() { *m = CheckReq{} }
|
||||
func (m *CheckReq) String() string { return proto.CompactTextString(m) }
|
||||
func (*CheckReq) ProtoMessage() {}
|
||||
func (*CheckReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
|
||||
func (m *CheckReq) GetBook() string {
|
||||
if m != nil {
|
||||
return m.Book
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type CheckResp struct {
|
||||
Found bool `protobuf:"varint,1,opt,name=found" json:"found,omitempty"`
|
||||
Price int64 `protobuf:"varint,2,opt,name=price" json:"price,omitempty"`
|
||||
}
|
||||
|
||||
func (m *CheckResp) Reset() { *m = CheckResp{} }
|
||||
func (m *CheckResp) String() string { return proto.CompactTextString(m) }
|
||||
func (*CheckResp) ProtoMessage() {}
|
||||
func (*CheckResp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||
|
||||
func (m *CheckResp) GetFound() bool {
|
||||
if m != nil {
|
||||
return m.Found
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *CheckResp) GetPrice() int64 {
|
||||
if m != nil {
|
||||
return m.Price
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*CheckReq)(nil), "check.checkReq")
|
||||
proto.RegisterType((*CheckResp)(nil), "check.checkResp")
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// Client API for Checker service
|
||||
|
||||
type CheckerClient interface {
|
||||
Check(ctx context.Context, in *CheckReq, opts ...grpc.CallOption) (*CheckResp, error)
|
||||
}
|
||||
|
||||
type checkerClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewCheckerClient(cc *grpc.ClientConn) CheckerClient {
|
||||
return &checkerClient{cc}
|
||||
}
|
||||
|
||||
func (c *checkerClient) Check(ctx context.Context, in *CheckReq, opts ...grpc.CallOption) (*CheckResp, error) {
|
||||
out := new(CheckResp)
|
||||
err := grpc.Invoke(ctx, "/check.checker/check", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for Checker service
|
||||
|
||||
type CheckerServer interface {
|
||||
Check(context.Context, *CheckReq) (*CheckResp, error)
|
||||
}
|
||||
|
||||
func RegisterCheckerServer(s *grpc.Server, srv CheckerServer) {
|
||||
s.RegisterService(&_Checker_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Checker_Check_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CheckReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(CheckerServer).Check(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/check.checker/Check",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(CheckerServer).Check(ctx, req.(*CheckReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Checker_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "check.checker",
|
||||
HandlerType: (*CheckerServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "check",
|
||||
Handler: _Checker_Check_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "check.proto",
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("check.proto", fileDescriptor0) }
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 136 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x4e, 0xce, 0x48, 0x4d,
|
||||
0xce, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, 0x73, 0x94, 0xe4, 0xb8, 0x38, 0xc0,
|
||||
0x8c, 0xa0, 0xd4, 0x42, 0x21, 0x21, 0x2e, 0x96, 0xa4, 0xfc, 0xfc, 0x6c, 0x09, 0x46, 0x05, 0x46,
|
||||
0x0d, 0xce, 0x20, 0x30, 0x5b, 0xc9, 0x9c, 0x8b, 0x13, 0x2a, 0x5f, 0x5c, 0x20, 0x24, 0xc2, 0xc5,
|
||||
0x9a, 0x96, 0x5f, 0x9a, 0x97, 0x02, 0x56, 0xc1, 0x11, 0x04, 0xe1, 0x80, 0x44, 0x0b, 0x8a, 0x32,
|
||||
0x93, 0x53, 0x25, 0x98, 0x14, 0x18, 0x35, 0x98, 0x83, 0x20, 0x1c, 0x23, 0x53, 0x2e, 0x76, 0xb0,
|
||||
0xc6, 0xd4, 0x22, 0x21, 0x2d, 0x2e, 0x88, 0x65, 0x42, 0xfc, 0x7a, 0x10, 0x17, 0xc0, 0x6c, 0x94,
|
||||
0x12, 0x40, 0x15, 0x28, 0x2e, 0x48, 0x62, 0x03, 0xbb, 0xce, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff,
|
||||
0x6e, 0x6f, 0xa7, 0x1d, 0xac, 0x00, 0x00, 0x00,
|
||||
}
|
||||
@@ -6,8 +6,8 @@ package server
|
||||
import (
|
||||
"context"
|
||||
|
||||
"bookstore/rpc/check/check"
|
||||
"bookstore/rpc/check/internal/logic"
|
||||
check "bookstore/rpc/check/internal/pb"
|
||||
"bookstore/rpc/check/internal/svc"
|
||||
)
|
||||
|
||||
|
||||
@@ -9,12 +9,12 @@ import (
|
||||
|
||||
type ServiceContext struct {
|
||||
c config.Config
|
||||
Model *model.BookModel
|
||||
Model model.BookModel
|
||||
}
|
||||
|
||||
func NewServiceContext(c config.Config) *ServiceContext {
|
||||
return &ServiceContext{
|
||||
c: c,
|
||||
Model: model.NewBookModel(sqlx.NewMysql(c.DataSource), c.Cache, c.Table),
|
||||
c: c,
|
||||
Model: model.NewBookModel(sqlx.NewMysql(c.DataSource), c.Cache),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,18 @@ var (
|
||||
bookRowsExpectAutoSet = strings.Join(stringx.Remove(bookFieldNames, "create_time", "update_time"), ",")
|
||||
bookRowsWithPlaceHolder = strings.Join(stringx.Remove(bookFieldNames, "book", "create_time", "update_time"), "=?,") + "=?"
|
||||
|
||||
cacheBookBookPrefix = "cache#Book#book#"
|
||||
cacheBookPrefix = "cache#Book#book#"
|
||||
)
|
||||
|
||||
type (
|
||||
BookModel struct {
|
||||
BookModel interface {
|
||||
Insert(data Book) (sql.Result, error)
|
||||
FindOne(book string) (*Book, error)
|
||||
Update(data Book) error
|
||||
Delete(book string) error
|
||||
}
|
||||
|
||||
defaultBookModel struct {
|
||||
sqlc.CachedConn
|
||||
table string
|
||||
}
|
||||
@@ -33,23 +40,25 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func NewBookModel(conn sqlx.SqlConn, c cache.CacheConf, table string) *BookModel {
|
||||
return &BookModel{
|
||||
func NewBookModel(conn sqlx.SqlConn, c cache.CacheConf) BookModel {
|
||||
return &defaultBookModel{
|
||||
CachedConn: sqlc.NewConn(conn, c),
|
||||
table: table,
|
||||
table: "book",
|
||||
}
|
||||
}
|
||||
|
||||
func (m *BookModel) Insert(data Book) (sql.Result, error) {
|
||||
query := `insert into ` + m.table + ` (` + bookRowsExpectAutoSet + `) values (?, ?)`
|
||||
return m.ExecNoCache(query, data.Book, data.Price)
|
||||
func (m *defaultBookModel) Insert(data Book) (sql.Result, error) {
|
||||
query := fmt.Sprintf("insert into %s (%s) values (?, ?)", m.table, bookRowsExpectAutoSet)
|
||||
ret, err := m.ExecNoCache(query, data.Book, data.Price)
|
||||
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func (m *BookModel) FindOne(book string) (*Book, error) {
|
||||
bookBookKey := fmt.Sprintf("%s%v", cacheBookBookPrefix, book)
|
||||
func (m *defaultBookModel) FindOne(book string) (*Book, error) {
|
||||
bookKey := fmt.Sprintf("%s%v", cacheBookPrefix, book)
|
||||
var resp Book
|
||||
err := m.QueryRow(&resp, bookBookKey, func(conn sqlx.SqlConn, v interface{}) error {
|
||||
query := `select ` + bookRows + ` from ` + m.table + ` where book = ? limit 1`
|
||||
err := m.QueryRow(&resp, bookKey, func(conn sqlx.SqlConn, v interface{}) error {
|
||||
query := fmt.Sprintf("select %s from %s where book = ? limit 1", bookRows, m.table)
|
||||
return conn.QueryRow(v, query, book)
|
||||
})
|
||||
switch err {
|
||||
@@ -62,21 +71,30 @@ func (m *BookModel) FindOne(book string) (*Book, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *BookModel) Update(data Book) error {
|
||||
bookBookKey := fmt.Sprintf("%s%v", cacheBookBookPrefix, data.Book)
|
||||
func (m *defaultBookModel) Update(data Book) error {
|
||||
bookKey := fmt.Sprintf("%s%v", cacheBookPrefix, data.Book)
|
||||
_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||
query := `update ` + m.table + ` set ` + bookRowsWithPlaceHolder + ` where book = ?`
|
||||
query := fmt.Sprintf("update %s set %s where book = ?", m.table, bookRowsWithPlaceHolder)
|
||||
return conn.Exec(query, data.Price, data.Book)
|
||||
}, bookBookKey)
|
||||
}, bookKey)
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *BookModel) Delete(book string) error {
|
||||
func (m *defaultBookModel) Delete(book string) error {
|
||||
|
||||
bookBookKey := fmt.Sprintf("%s%v", cacheBookBookPrefix, book)
|
||||
bookKey := fmt.Sprintf("%s%v", cacheBookPrefix, book)
|
||||
_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||
query := `delete from ` + m.table + ` where book = ?`
|
||||
query := fmt.Sprintf("delete from %s where book = ?", m.table)
|
||||
return conn.Exec(query, book)
|
||||
}, bookBookKey)
|
||||
}, bookKey)
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *defaultBookModel) formatPrimary(primary interface{}) string {
|
||||
return fmt.Sprintf("%s%v", cacheBookPrefix, primary)
|
||||
}
|
||||
|
||||
func (m *defaultBookModel) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error {
|
||||
query := fmt.Sprintf("select %s from %s where book = ? limit 1", bookRows, m.table)
|
||||
return conn.QueryRow(v, query, primary)
|
||||
}
|
||||
|
||||
0
example/bookstore/rpc/model/vars.go
Executable file → Normal file
0
example/bookstore/rpc/model/vars.go
Executable file → Normal file
@@ -41,6 +41,7 @@ func main() {
|
||||
var allowed, denied int32
|
||||
var wait sync.WaitGroup
|
||||
for i := 0; i < *threads; i++ {
|
||||
i := i
|
||||
wait.Add(1)
|
||||
go func() {
|
||||
for {
|
||||
|
||||
@@ -5,7 +5,7 @@ go 1.15
|
||||
require (
|
||||
github.com/golang/mock v1.4.3
|
||||
github.com/golang/protobuf v1.4.2
|
||||
github.com/tal-tech/go-zero v1.0.16
|
||||
github.com/tal-tech/go-zero v1.0.27
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381
|
||||
google.golang.org/grpc v1.29.1
|
||||
)
|
||||
|
||||
@@ -37,6 +37,8 @@ github.com/coreos/go-systemd/v22 v22.0.0 h1:XJIw/+VlJ+87J+doOxznsAWIdmWuViOVhkQa
|
||||
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
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=
|
||||
@@ -48,6 +50,8 @@ github.com/dsymonds/gotoc v0.0.0-20160928043926-5aebcfc91819 h1:9778zj477h/VauD8
|
||||
github.com/dsymonds/gotoc v0.0.0-20160928043926-5aebcfc91819/go.mod h1:MvzMVHq8BH2Ji/o8TGDocVA70byvLrAgFTxkEnmjO4Y=
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4 h1:qk/FSDDxo05wdJH28W+p5yivv7LuLYLRXPPD8KQCtZs=
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/emicklei/proto v1.9.0 h1:l0QiNT6Qs7Yj0Mb4X6dnWBQer4ebei2BFcgQLbGqUDc=
|
||||
github.com/emicklei/proto v1.9.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
@@ -124,10 +128,13 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.14.3 h1:OCJlWkOUoTnl0neNGlf4fUm3TmbEtguw7vR+nGtnDjY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.14.3/go.mod h1:6CwZWGDSPRJidgKAtJVvND6soZe6fT7iteq8wDPdhb0=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334 h1:VHgatEHNcBFEB7inlalqfNqw65aNkM1lGX2yt3NmbS8=
|
||||
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
|
||||
github.com/iancoleman/strcase v0.1.2 h1:gnomlvw9tnV3ITTAxzKSgTF+8kFWcU/f+TgttpXGz1U=
|
||||
github.com/iancoleman/strcase v0.1.2/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
||||
@@ -179,6 +186,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
@@ -215,6 +223,8 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
@@ -238,12 +248,16 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/tal-tech/go-zero v1.0.16 h1:oT7sOFftEUdD/XcXF0xEugX9yhnw4DcQkeMNFLi5KO8=
|
||||
github.com/tal-tech/go-zero v1.0.16/go.mod h1:y2wBHTkxNJw79K9/wCSeDKzv2pCT6x45oOmXEsJdQK8=
|
||||
github.com/tal-tech/go-zero v1.0.27 h1:QMIbaTxibMc/OsO5RTAuKZ8ndbl2dGN6pITQEtp2x/A=
|
||||
github.com/tal-tech/go-zero v1.0.27/go.mod h1:JtNXlsh/CgeIHyQnt5C5M2IcSevW7V0NAnqO93TQgm8=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 h1:lYIiVDtZnyTWlNwiAxLj0bbpTcx1BWCFhXjfsvmPdNc=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA=
|
||||
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
|
||||
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
||||
@@ -390,6 +404,7 @@ gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
@@ -401,6 +416,8 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
|
||||
12
go.mod
12
go.mod
@@ -1,17 +1,17 @@
|
||||
module github.com/tal-tech/go-zero
|
||||
|
||||
go 1.13
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/ClickHouse/clickhouse-go v1.4.3
|
||||
github.com/DATA-DOG/go-sqlmock v1.4.1
|
||||
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 // indirect
|
||||
github.com/alicebob/miniredis v2.5.0+incompatible
|
||||
github.com/alicebob/miniredis/v2 v2.14.1
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||
github.com/dchest/siphash v1.2.1
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/emicklei/proto v1.9.0
|
||||
github.com/fatih/color v1.9.0 // indirect
|
||||
github.com/fatih/structtag v1.2.0
|
||||
github.com/frankban/quicktest v1.7.2 // indirect
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8
|
||||
github.com/go-redis/redis v6.15.7+incompatible
|
||||
@@ -21,7 +21,6 @@ require (
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
|
||||
github.com/golang/mock v1.4.3
|
||||
github.com/golang/protobuf v1.4.2
|
||||
github.com/gomodule/redigo v2.0.0+incompatible // indirect
|
||||
github.com/google/gops v0.3.7
|
||||
github.com/google/uuid v1.1.1
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
@@ -47,7 +46,6 @@ require (
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 // indirect
|
||||
github.com/urfave/cli v1.22.5
|
||||
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2
|
||||
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb // indirect
|
||||
go.etcd.io/etcd v0.0.0-20200402134248-51bdeb39e698
|
||||
go.uber.org/automaxprocs v1.3.0
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
|
||||
@@ -58,10 +56,10 @@ require (
|
||||
golang.org/x/tools v0.0.0-20200410132612-ae9902aceb98 // indirect
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f // indirect
|
||||
google.golang.org/grpc v1.29.1
|
||||
google.golang.org/protobuf v1.25.0
|
||||
google.golang.org/protobuf v1.25.0 // indirect
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28
|
||||
gopkg.in/h2non/gock.v1 v1.0.15
|
||||
gopkg.in/yaml.v2 v2.3.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
honnef.co/go/tools v0.0.1-2020.1.4 // indirect
|
||||
sigs.k8s.io/yaml v1.2.0 // indirect
|
||||
)
|
||||
|
||||
22
go.sum
22
go.sum
@@ -11,10 +11,10 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafo
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 h1:45bxf7AZMwWcqkLzDAQugVEwedisr5nRJ1r+7LYnv0U=
|
||||
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||
github.com/alicebob/miniredis v2.5.0+incompatible h1:yBHoLpsyjupjz3NL3MhKMVkR41j82Yjf3KFv7ApYzUI=
|
||||
github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
|
||||
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk=
|
||||
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||
github.com/alicebob/miniredis/v2 v2.14.1 h1:GjlbSeoJ24bzdLRs13HoMEeaRZx9kg5nHoRW7QV/nCs=
|
||||
github.com/alicebob/miniredis/v2 v2.14.1/go.mod h1:uS970Sw5Gs9/iK3yBg0l9Uj9s25wXxSpQUE9EaJ/Blg=
|
||||
github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
@@ -63,6 +63,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||
github.com/frankban/quicktest v1.7.2 h1:2QxQoC1TS09S7fhCPsrvqYdvP1H5M1P1ih5ABm3BTYk=
|
||||
github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
@@ -114,8 +116,6 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
@@ -147,8 +147,6 @@ github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslC
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334 h1:VHgatEHNcBFEB7inlalqfNqw65aNkM1lGX2yt3NmbS8=
|
||||
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
|
||||
github.com/iancoleman/strcase v0.1.2 h1:gnomlvw9tnV3ITTAxzKSgTF+8kFWcU/f+TgttpXGz1U=
|
||||
github.com/iancoleman/strcase v0.1.2/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
@@ -211,6 +209,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
|
||||
@@ -281,8 +280,6 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 h1:lYIiVDtZnyTWlNwiAxLj0bbpTcx1BWCFhXjfsvmPdNc=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA=
|
||||
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
|
||||
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
|
||||
@@ -425,6 +422,7 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
@@ -454,8 +452,8 @@ gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||
|
||||
@@ -174,7 +174,7 @@ goctl api -o greet.api
|
||||
you can check it by curl:
|
||||
|
||||
```shell
|
||||
curl -i http://localhost:8888/greet/from/you
|
||||
curl -i http://localhost:8888/from/you
|
||||
```
|
||||
|
||||
the response looks like:
|
||||
|
||||
12
readme.md
12
readme.md
@@ -1,4 +1,4 @@
|
||||
<img align="right" width="150px" src="doc/images/go-zero.png">
|
||||
<img align="right" width="150px" src="https://gitee.com/kevwan/static/raw/master/doc/images/go-zero.png">
|
||||
|
||||
# go-zero
|
||||
|
||||
@@ -25,7 +25,7 @@ go-zero 包含极简的 API 定义和生成工具 goctl,可以根据定义的
|
||||
* 自动校验客户端请求参数合法性
|
||||
* 大量微服务治理和并发工具包
|
||||
|
||||
<img src="doc/images/architecture.png" alt="架构图" width="1500" />
|
||||
<img src="https://gitee.com/kevwan/static/raw/master/doc/images/architecture.png" alt="架构图" width="1500" />
|
||||
|
||||
## 1. go-zero 框架背景
|
||||
|
||||
@@ -77,7 +77,7 @@ go-zero 是一个集成了各种工程实践的包含 web 和 rpc 框架,有
|
||||
|
||||
如下图,我们从多个层面保障了整体服务的高可用:
|
||||
|
||||

|
||||

|
||||
|
||||
觉得不错的话,别忘 **star** 👏
|
||||
|
||||
@@ -120,7 +120,7 @@ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/tal-tech/
|
||||
默认侦听在 8888 端口(可以在配置文件里修改),可以通过 curl 请求:
|
||||
|
||||
```shell
|
||||
curl -i http://localhost:8888/greet/from/you
|
||||
curl -i http://localhost:8888/from/you
|
||||
```
|
||||
|
||||
返回如下:
|
||||
@@ -150,7 +150,7 @@ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/tal-tech/
|
||||
|
||||
## 6. Benchmark
|
||||
|
||||

|
||||

|
||||
|
||||
[测试代码见这里](https://github.com/smallnest/go-web-framework-benchmark)
|
||||
|
||||
@@ -189,5 +189,3 @@ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/tal-tech/
|
||||
项目地址:[https://github.com/tal-tech/go-zero](https://github.com/tal-tech/go-zero)
|
||||
|
||||
码云地址:[https://gitee.com/kevwan/go-zero](https://gitee.com/kevwan/go-zero) (国内用户可访问gitee,每日自动从github同步代码)
|
||||
|
||||
开源中国年度评选,给 **go-zero** 投上一票:[https://www.oschina.net/p/go-zero](https://www.oschina.net/p/go-zero)
|
||||
|
||||
@@ -26,11 +26,11 @@ func ContentSecurityHandler(decrypters map[string]codec.RsaDecrypter, tolerance
|
||||
case http.MethodDelete, http.MethodGet, http.MethodPost, http.MethodPut:
|
||||
header, err := security.ParseContentSecurity(decrypters, r)
|
||||
if err != nil {
|
||||
logx.Infof("Signature parse failed, X-Content-Security: %s, error: %s",
|
||||
logx.Errorf("Signature parse failed, X-Content-Security: %s, error: %s",
|
||||
r.Header.Get(contentSecurity), err.Error())
|
||||
executeCallbacks(w, r, next, strict, httpx.CodeSignatureInvalidHeader, callbacks)
|
||||
} else if code := security.VerifySignature(r, header, tolerance); code != httpx.CodeSignaturePass {
|
||||
logx.Infof("Signature verification failed, X-Content-Security: %s",
|
||||
logx.Errorf("Signature verification failed, X-Content-Security: %s",
|
||||
r.Header.Get(contentSecurity))
|
||||
executeCallbacks(w, r, next, strict, code, callbacks)
|
||||
} else if r.ContentLength > 0 && header.Encrypted() {
|
||||
@@ -54,7 +54,7 @@ func executeCallbacks(w http.ResponseWriter, r *http.Request, next http.Handler,
|
||||
|
||||
func handleVerificationFailure(w http.ResponseWriter, r *http.Request, next http.Handler, strict bool, code int) {
|
||||
if strict {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
} else {
|
||||
next.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ func TestContentSecurityHandler(t *testing.T) {
|
||||
strict: true,
|
||||
crypt: true,
|
||||
timestamp: time.Now().Add(timeDiff).Unix(),
|
||||
statusCode: http.StatusUnauthorized,
|
||||
statusCode: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
method: http.MethodPost,
|
||||
@@ -122,7 +122,7 @@ func TestContentSecurityHandler(t *testing.T) {
|
||||
strict: true,
|
||||
crypt: true,
|
||||
timestamp: time.Now().Add(-timeDiff).Unix(),
|
||||
statusCode: http.StatusUnauthorized,
|
||||
statusCode: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
method: http.MethodPost,
|
||||
@@ -148,7 +148,7 @@ func TestContentSecurityHandler(t *testing.T) {
|
||||
crypt: true,
|
||||
timestamp: time.Now().Add(-timeDiff).Unix(),
|
||||
fingerprint: "badone",
|
||||
statusCode: http.StatusUnauthorized,
|
||||
statusCode: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
method: http.MethodPost,
|
||||
@@ -157,7 +157,7 @@ func TestContentSecurityHandler(t *testing.T) {
|
||||
strict: true,
|
||||
crypt: true,
|
||||
missHeader: true,
|
||||
statusCode: http.StatusUnauthorized,
|
||||
statusCode: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
method: http.MethodHead,
|
||||
@@ -171,7 +171,7 @@ func TestContentSecurityHandler(t *testing.T) {
|
||||
strict: true,
|
||||
crypt: false,
|
||||
signature: "badone",
|
||||
statusCode: http.StatusUnauthorized,
|
||||
statusCode: http.StatusForbidden,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
multipartFormData = "multipart/form-data"
|
||||
formKey = "form"
|
||||
pathKey = "path"
|
||||
emptyJson = "{}"
|
||||
@@ -39,12 +38,12 @@ func Parse(r *http.Request, v interface{}) error {
|
||||
|
||||
// Parses the form request.
|
||||
func ParseForm(r *http.Request, v interface{}) error {
|
||||
if strings.Contains(r.Header.Get(ContentType), multipartFormData) {
|
||||
if err := r.ParseMultipartForm(maxMemory); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.ParseMultipartForm(maxMemory); err != nil {
|
||||
if err != http.ErrNotMultipart {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,33 @@ package httpx
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/tal-tech/go-zero/core/logx"
|
||||
)
|
||||
|
||||
var (
|
||||
errorHandler func(error) (int, interface{})
|
||||
lock sync.RWMutex
|
||||
)
|
||||
|
||||
func Error(w http.ResponseWriter, err error) {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
lock.RLock()
|
||||
handler := errorHandler
|
||||
lock.RUnlock()
|
||||
|
||||
if handler == nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
code, body := errorHandler(err)
|
||||
e, ok := body.(error)
|
||||
if ok {
|
||||
http.Error(w, e.Error(), code)
|
||||
} else {
|
||||
WriteJson(w, code, body)
|
||||
}
|
||||
}
|
||||
|
||||
func Ok(w http.ResponseWriter) {
|
||||
@@ -19,6 +40,12 @@ func OkJson(w http.ResponseWriter, v interface{}) {
|
||||
WriteJson(w, http.StatusOK, v)
|
||||
}
|
||||
|
||||
func SetErrorHandler(handler func(error) (int, interface{})) {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
errorHandler = handler
|
||||
}
|
||||
|
||||
func WriteJson(w http.ResponseWriter, code int, v interface{}) {
|
||||
w.Header().Set(ContentType, ApplicationJson)
|
||||
w.WriteHeader(code)
|
||||
|
||||
@@ -19,13 +19,65 @@ func init() {
|
||||
}
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
const body = "foo"
|
||||
w := tracedResponseWriter{
|
||||
headers: make(map[string][]string),
|
||||
const (
|
||||
body = "foo"
|
||||
wrappedBody = `"foo"`
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
errorHandler func(error) (int, interface{})
|
||||
expectBody string
|
||||
expectCode int
|
||||
}{
|
||||
{
|
||||
name: "default error handler",
|
||||
input: body,
|
||||
expectBody: body,
|
||||
expectCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "customized error handler return string",
|
||||
input: body,
|
||||
errorHandler: func(err error) (int, interface{}) {
|
||||
return http.StatusForbidden, err.Error()
|
||||
},
|
||||
expectBody: wrappedBody,
|
||||
expectCode: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
name: "customized error handler return error",
|
||||
input: body,
|
||||
errorHandler: func(err error) (int, interface{}) {
|
||||
return http.StatusForbidden, err
|
||||
},
|
||||
expectBody: body,
|
||||
expectCode: http.StatusForbidden,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
w := tracedResponseWriter{
|
||||
headers: make(map[string][]string),
|
||||
}
|
||||
if test.errorHandler != nil {
|
||||
lock.RLock()
|
||||
prev := errorHandler
|
||||
lock.RUnlock()
|
||||
SetErrorHandler(test.errorHandler)
|
||||
defer func() {
|
||||
lock.Lock()
|
||||
errorHandler = prev
|
||||
lock.Unlock()
|
||||
}()
|
||||
}
|
||||
Error(&w, errors.New(test.input))
|
||||
assert.Equal(t, test.expectCode, w.code)
|
||||
assert.Equal(t, test.expectBody, strings.TrimSpace(w.builder.String()))
|
||||
})
|
||||
}
|
||||
Error(&w, errors.New(body))
|
||||
assert.Equal(t, http.StatusBadRequest, w.code)
|
||||
assert.Equal(t, body, strings.TrimSpace(w.builder.String()))
|
||||
}
|
||||
|
||||
func TestOk(t *testing.T) {
|
||||
|
||||
@@ -2,13 +2,13 @@ version := $(shell /bin/date "+%Y-%m-%d %H:%M")
|
||||
|
||||
build:
|
||||
go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" goctl.go
|
||||
command -v upx &> /dev/null && upx goctl
|
||||
$(if $(shell command -v upx), upx goctl)
|
||||
mac:
|
||||
GOOS=darwin go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" -o goctl-darwin goctl.go
|
||||
command -v upx &> /dev/null && upx goctl-darwin
|
||||
$(if $(shell command -v upx), upx goctl-darwin)
|
||||
win:
|
||||
GOOS=windows go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" -o goctl.exe goctl.go
|
||||
command -v upx &> /dev/null && upx goctl.exe
|
||||
$(if $(shell command -v upx), upx goctl.exe)
|
||||
linux:
|
||||
GOOS=linux go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" -o goctl-linux goctl.go
|
||||
command -v upx &> /dev/null && upx goctl-linux
|
||||
$(if $(shell command -v upx), upx goctl-linux)
|
||||
|
||||
@@ -28,10 +28,10 @@ type response struct {
|
||||
}
|
||||
|
||||
service {{.serviceName}} {
|
||||
@handler // TODO: set handler name and delete this comment
|
||||
@handler GetUser // TODO: set handler name and delete this comment
|
||||
get /users/id/:userId(request) returns(response)
|
||||
|
||||
@handler // TODO: set handler name and delete this comment
|
||||
@handler CreateUser // TODO: set handler name and delete this comment
|
||||
post /users/create(request)
|
||||
}
|
||||
`
|
||||
|
||||
@@ -4,13 +4,10 @@ import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"go/scanner"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/tal-tech/go-zero/core/errorx"
|
||||
@@ -19,8 +16,11 @@ import (
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
reg = regexp.MustCompile("type (?P<name>.*)[\\s]+{")
|
||||
const (
|
||||
leftParenthesis = "("
|
||||
rightParenthesis = ")"
|
||||
leftBrace = "{"
|
||||
rightBrace = "}"
|
||||
)
|
||||
|
||||
func GoFormatApi(c *cli.Context) error {
|
||||
@@ -95,86 +95,32 @@ func ApiFormatByPath(apiFilePath string) error {
|
||||
}
|
||||
|
||||
func apiFormat(data string) (string, error) {
|
||||
r := reg.ReplaceAllStringFunc(data, func(m string) string {
|
||||
parts := reg.FindStringSubmatch(m)
|
||||
if len(parts) < 2 {
|
||||
return m
|
||||
}
|
||||
if !strings.Contains(m, "struct") {
|
||||
return "type " + parts[1] + " struct {"
|
||||
}
|
||||
return m
|
||||
})
|
||||
|
||||
apiStruct, err := parser.ParseApi(r)
|
||||
_, err := parser.ParseApi(data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
info := strings.TrimSpace(apiStruct.Info)
|
||||
if len(apiStruct.Service) == 0 {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
fs, err := format.Source([]byte(strings.TrimSpace(apiStruct.Type)))
|
||||
if err != nil {
|
||||
str := err.Error()
|
||||
lineNumber := strings.Index(str, ":")
|
||||
if lineNumber > 0 {
|
||||
ln, err := strconv.ParseInt(str[:lineNumber], 10, 64)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
pn := 0
|
||||
if len(info) > 0 {
|
||||
pn = countRune(info, '\n') + 1
|
||||
}
|
||||
number := int(ln) + pn + 1
|
||||
return "", errors.New(fmt.Sprintf("line: %d, %s", number, str[lineNumber+1:]))
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
var result string
|
||||
if len(strings.TrimSpace(info)) > 0 {
|
||||
result += strings.TrimSpace(info) + "\n\n"
|
||||
}
|
||||
if len(strings.TrimSpace(apiStruct.Imports)) > 0 {
|
||||
result += strings.TrimSpace(apiStruct.Imports) + "\n\n"
|
||||
}
|
||||
if len(strings.TrimSpace(string(fs))) > 0 {
|
||||
result += strings.TrimSpace(string(fs)) + "\n\n"
|
||||
}
|
||||
if len(strings.TrimSpace(apiStruct.Service)) > 0 {
|
||||
result += formatService(apiStruct.Service) + "\n\n"
|
||||
}
|
||||
|
||||
return strings.TrimSpace(result), nil
|
||||
}
|
||||
|
||||
func formatService(str string) string {
|
||||
var builder strings.Builder
|
||||
scanner := bufio.NewScanner(strings.NewReader(str))
|
||||
s := bufio.NewScanner(strings.NewReader(data))
|
||||
var tapCount = 0
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if line == ")" || line == "}" {
|
||||
for s.Scan() {
|
||||
line := strings.TrimSpace(s.Text())
|
||||
noCommentLine := util.RemoveComment(line)
|
||||
if noCommentLine == rightParenthesis || noCommentLine == rightBrace {
|
||||
tapCount -= 1
|
||||
}
|
||||
if tapCount < 0 {
|
||||
line = strings.TrimSuffix(line, rightBrace)
|
||||
line = strings.TrimSpace(line)
|
||||
if strings.HasSuffix(line, leftBrace) {
|
||||
tapCount += 1
|
||||
}
|
||||
}
|
||||
util.WriteIndent(&builder, tapCount)
|
||||
builder.WriteString(line + "\n")
|
||||
if strings.HasSuffix(line, "(") || strings.HasSuffix(line, "{") {
|
||||
if strings.HasSuffix(noCommentLine, leftParenthesis) || strings.HasSuffix(noCommentLine, leftBrace) {
|
||||
tapCount += 1
|
||||
}
|
||||
}
|
||||
return strings.TrimSpace(builder.String())
|
||||
}
|
||||
|
||||
func countRune(s string, r rune) int {
|
||||
count := 0
|
||||
for _, c := range s {
|
||||
if c == r {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
return strings.TrimSpace(builder.String()), nil
|
||||
}
|
||||
|
||||
@@ -12,13 +12,13 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/logrusorgru/aurora"
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/tal-tech/go-zero/core/logx"
|
||||
apiformat "github.com/tal-tech/go-zero/tools/goctl/api/format"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/api/parser"
|
||||
apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/config"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
const tmpFile = "%s-%d"
|
||||
@@ -28,6 +28,8 @@ var tmpDir = path.Join(os.TempDir(), "goctl")
|
||||
func GoCommand(c *cli.Context) error {
|
||||
apiFile := c.String("api")
|
||||
dir := c.String("dir")
|
||||
namingStyle := c.String("style")
|
||||
|
||||
if len(apiFile) == 0 {
|
||||
return errors.New("missing -api")
|
||||
}
|
||||
@@ -35,10 +37,10 @@ func GoCommand(c *cli.Context) error {
|
||||
return errors.New("missing -dir")
|
||||
}
|
||||
|
||||
return DoGenProject(apiFile, dir)
|
||||
return DoGenProject(apiFile, dir, namingStyle)
|
||||
}
|
||||
|
||||
func DoGenProject(apiFile, dir string) error {
|
||||
func DoGenProject(apiFile, dir, style string) error {
|
||||
p, err := parser.NewParser(apiFile)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -48,15 +50,21 @@ func DoGenProject(apiFile, dir string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg, err := config.NewConfig(style)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logx.Must(util.MkdirIfNotExist(dir))
|
||||
logx.Must(genEtc(dir, api))
|
||||
logx.Must(genConfig(dir, api))
|
||||
logx.Must(genMain(dir, api))
|
||||
logx.Must(genServiceContext(dir, api))
|
||||
logx.Must(genTypes(dir, api))
|
||||
logx.Must(genHandlers(dir, api))
|
||||
logx.Must(genRoutes(dir, api))
|
||||
logx.Must(genLogic(dir, api))
|
||||
logx.Must(genEtc(dir, cfg, api))
|
||||
logx.Must(genConfig(dir, cfg, api))
|
||||
logx.Must(genMain(dir, cfg, api))
|
||||
logx.Must(genServiceContext(dir, cfg, api))
|
||||
logx.Must(genTypes(dir, cfg, api))
|
||||
logx.Must(genRoutes(dir, cfg, api))
|
||||
logx.Must(genHandlers(dir, cfg, api))
|
||||
logx.Must(genLogic(dir, cfg, api))
|
||||
logx.Must(genMiddleware(dir, cfg, api))
|
||||
|
||||
if err := backupAndSweep(apiFile); err != nil {
|
||||
return err
|
||||
|
||||
@@ -22,10 +22,14 @@ info(
|
||||
version: 1.0
|
||||
)
|
||||
|
||||
type Request struct {
|
||||
// TODO: test
|
||||
// {
|
||||
type Request struct { // TODO: test
|
||||
// TODO
|
||||
Name string ` + "`" + `path:"name,options=you|me"` + "`" + ` // }
|
||||
}
|
||||
} // TODO: test
|
||||
|
||||
// TODO: test
|
||||
type Response struct {
|
||||
Message string ` + "`" + `json:"message"` + "`" + `
|
||||
}
|
||||
@@ -279,6 +283,42 @@ service A-api {
|
||||
}
|
||||
`
|
||||
|
||||
const noStructTagApi = `
|
||||
type Request {
|
||||
Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
|
||||
}
|
||||
|
||||
type XXX {}
|
||||
|
||||
type (
|
||||
Response {
|
||||
Message string ` + "`" + `json:"message"` + "`" + `
|
||||
}
|
||||
|
||||
A {}
|
||||
|
||||
B struct {}
|
||||
)
|
||||
|
||||
service A-api {
|
||||
@handler GreetHandler
|
||||
get /greet/from/:name(Request) returns (Response)
|
||||
}
|
||||
`
|
||||
|
||||
const nestTypeApi = `
|
||||
type Request {
|
||||
Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
|
||||
XXX struct {
|
||||
}
|
||||
}
|
||||
|
||||
service A-api {
|
||||
@handler GreetHandler
|
||||
get /greet/from/:name(Request)
|
||||
}
|
||||
`
|
||||
|
||||
func TestParser(t *testing.T) {
|
||||
filename := "greet.api"
|
||||
err := ioutil.WriteFile(filename, []byte(testApiTemplate), os.ModePerm)
|
||||
@@ -494,13 +534,55 @@ func TestHasImportApi(t *testing.T) {
|
||||
}
|
||||
}
|
||||
assert.True(t, hasInline)
|
||||
|
||||
validate(t, filename)
|
||||
}
|
||||
|
||||
func TestNoStructApi(t *testing.T) {
|
||||
filename := "greet.api"
|
||||
err := ioutil.WriteFile(filename, []byte(noStructTagApi), os.ModePerm)
|
||||
assert.Nil(t, err)
|
||||
defer os.Remove(filename)
|
||||
|
||||
parser, err := parser.NewParser(filename)
|
||||
assert.Nil(t, err)
|
||||
|
||||
spec, err := parser.Parse()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, len(spec.Types), 5)
|
||||
|
||||
validate(t, filename)
|
||||
}
|
||||
|
||||
func TestNestTypeApi(t *testing.T) {
|
||||
filename := "greet.api"
|
||||
err := ioutil.WriteFile(filename, []byte(nestTypeApi), os.ModePerm)
|
||||
assert.Nil(t, err)
|
||||
defer os.Remove(filename)
|
||||
_, err = parser.NewParser(filename)
|
||||
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestCamelStyle(t *testing.T) {
|
||||
filename := "greet.api"
|
||||
err := ioutil.WriteFile(filename, []byte(testApiTemplate), os.ModePerm)
|
||||
assert.Nil(t, err)
|
||||
defer os.Remove(filename)
|
||||
_, err = parser.NewParser(filename)
|
||||
assert.Nil(t, err)
|
||||
|
||||
validateWithCamel(t, filename, "GoZero")
|
||||
}
|
||||
|
||||
func validate(t *testing.T, api string) {
|
||||
validateWithCamel(t, api, "gozero")
|
||||
}
|
||||
|
||||
func validateWithCamel(t *testing.T, api, camel string) {
|
||||
dir := "_go"
|
||||
os.RemoveAll(dir)
|
||||
err := DoGenProject(api, dir)
|
||||
err := DoGenProject(api, dir, camel)
|
||||
defer os.RemoveAll(dir)
|
||||
assert.Nil(t, err)
|
||||
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
|
||||
@@ -8,12 +8,14 @@ import (
|
||||
|
||||
"github.com/tal-tech/go-zero/tools/goctl/api/spec"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/api/util"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/config"
|
||||
ctlutil "github.com/tal-tech/go-zero/tools/goctl/util"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util/format"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/vars"
|
||||
)
|
||||
|
||||
const (
|
||||
configFile = "config.go"
|
||||
configFile = "config"
|
||||
configTemplate = `package config
|
||||
|
||||
import {{.authImport}}
|
||||
@@ -31,8 +33,13 @@ type Config struct {
|
||||
`
|
||||
)
|
||||
|
||||
func genConfig(dir string, api *spec.ApiSpec) error {
|
||||
fp, created, err := util.MaybeCreateFile(dir, configDir, configFile)
|
||||
func genConfig(dir string, cfg *config.Config, api *spec.ApiSpec) error {
|
||||
filename, err := format.FileNamingFormat(cfg.NamingFormat, configFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fp, created, err := util.MaybeCreateFile(dir, configDir, filename+".go")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ import (
|
||||
|
||||
"github.com/tal-tech/go-zero/tools/goctl/api/spec"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/api/util"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/config"
|
||||
ctlutil "github.com/tal-tech/go-zero/tools/goctl/util"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util/format"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -20,8 +22,13 @@ Port: {{.port}}
|
||||
`
|
||||
)
|
||||
|
||||
func genEtc(dir string, api *spec.ApiSpec) error {
|
||||
fp, created, err := util.MaybeCreateFile(dir, etcDir, fmt.Sprintf("%s.yaml", api.Service.Name))
|
||||
func genEtc(dir string, cfg *config.Config, api *spec.ApiSpec) error {
|
||||
filename, err := format.FileNamingFormat(cfg.NamingFormat, api.Service.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fp, created, err := util.MaybeCreateFile(dir, etcDir, fmt.Sprintf("%s.yaml", filename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2,14 +2,18 @@ package gogen
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
"text/template"
|
||||
"unicode"
|
||||
|
||||
"github.com/tal-tech/go-zero/tools/goctl/api/spec"
|
||||
apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/config"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util/format"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/vars"
|
||||
)
|
||||
|
||||
@@ -50,13 +54,8 @@ type Handler struct {
|
||||
HasRequest bool
|
||||
}
|
||||
|
||||
func genHandler(dir string, group spec.Group, route spec.Route) error {
|
||||
handler, ok := apiutil.GetAnnotationValue(route.Annotations, "server", "handler")
|
||||
if !ok {
|
||||
return fmt.Errorf("missing handler annotation for %q", route.Path)
|
||||
}
|
||||
|
||||
handler = getHandlerName(handler)
|
||||
func genHandler(dir string, cfg *config.Config, group spec.Group, route spec.Route) error {
|
||||
handler := getHandlerName(route)
|
||||
if getHandlerFolderPath(group, route) != handlerDir {
|
||||
handler = strings.Title(handler)
|
||||
}
|
||||
@@ -65,27 +64,25 @@ func genHandler(dir string, group spec.Group, route spec.Route) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return doGenToFile(dir, handler, group, route, Handler{
|
||||
return doGenToFile(dir, handler, cfg, group, route, Handler{
|
||||
ImportPackages: genHandlerImports(group, route, parentPkg),
|
||||
HandlerName: handler,
|
||||
RequestType: util.Title(route.RequestType.Name),
|
||||
LogicType: strings.TrimSuffix(strings.Title(handler), "Handler") + "Logic",
|
||||
LogicType: strings.Title(getLogicName(route)),
|
||||
Call: strings.Title(strings.TrimSuffix(handler, "Handler")),
|
||||
HasResp: len(route.ResponseType.Name) > 0,
|
||||
HasRequest: len(route.RequestType.Name) > 0,
|
||||
})
|
||||
}
|
||||
|
||||
func doGenToFile(dir, handler string, group spec.Group, route spec.Route, handleObj Handler) error {
|
||||
if getHandlerFolderPath(group, route) != handlerDir {
|
||||
handler = strings.Title(handler)
|
||||
}
|
||||
filename := strings.ToLower(handler)
|
||||
if strings.HasSuffix(filename, "handler") {
|
||||
filename = filename + ".go"
|
||||
} else {
|
||||
filename = filename + "handler.go"
|
||||
func doGenToFile(dir, handler string, cfg *config.Config, group spec.Group,
|
||||
route spec.Route, handleObj Handler) error {
|
||||
filename, err := format.FileNamingFormat(cfg.NamingFormat, handler)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filename = filename + ".go"
|
||||
fp, created, err := apiutil.MaybeCreateFile(dir, getHandlerFolderPath(group, route), filename)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -111,10 +108,10 @@ func doGenToFile(dir, handler string, group spec.Group, route spec.Route, handle
|
||||
return err
|
||||
}
|
||||
|
||||
func genHandlers(dir string, api *spec.ApiSpec) error {
|
||||
func genHandlers(dir string, cfg *config.Config, api *spec.ApiSpec) error {
|
||||
for _, group := range api.Service.Groups {
|
||||
for _, route := range group.Routes {
|
||||
if err := genHandler(dir, group, route); err != nil {
|
||||
if err := genHandler(dir, cfg, group, route); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -136,14 +133,23 @@ func genHandlerImports(group spec.Group, route spec.Route, parentPkg string) str
|
||||
return strings.Join(imports, "\n\t")
|
||||
}
|
||||
|
||||
func getHandlerBaseName(handler string) string {
|
||||
handlerName := util.Untitle(handler)
|
||||
if strings.HasSuffix(handlerName, "handler") {
|
||||
handlerName = strings.ReplaceAll(handlerName, "handler", "")
|
||||
} else if strings.HasSuffix(handlerName, "Handler") {
|
||||
handlerName = strings.ReplaceAll(handlerName, "Handler", "")
|
||||
func getHandlerBaseName(route spec.Route) (string, error) {
|
||||
handler, ok := apiutil.GetAnnotationValue(route.Annotations, "server", "handler")
|
||||
if !ok {
|
||||
return "", fmt.Errorf("missing handler annotation for %q", route.Path)
|
||||
}
|
||||
return handlerName
|
||||
|
||||
for _, char := range handler {
|
||||
if !unicode.IsDigit(char) && !unicode.IsLetter(char) {
|
||||
return "", errors.New(fmt.Sprintf("route [%s] handler [%s] invalid, handler name should only contains letter or digit",
|
||||
route.Path, handler))
|
||||
}
|
||||
}
|
||||
|
||||
handler = strings.TrimSpace(handler)
|
||||
handler = strings.TrimSuffix(handler, "handler")
|
||||
handler = strings.TrimSuffix(handler, "Handler")
|
||||
return handler, nil
|
||||
}
|
||||
|
||||
func getHandlerFolderPath(group spec.Group, route spec.Route) string {
|
||||
@@ -159,6 +165,20 @@ func getHandlerFolderPath(group spec.Group, route spec.Route) string {
|
||||
return path.Join(handlerDir, folder)
|
||||
}
|
||||
|
||||
func getHandlerName(handler string) string {
|
||||
return getHandlerBaseName(handler) + "Handler"
|
||||
func getHandlerName(route spec.Route) string {
|
||||
handler, err := getHandlerBaseName(route)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return handler + "Handler"
|
||||
}
|
||||
|
||||
func getLogicName(route spec.Route) string {
|
||||
handler, err := getHandlerBaseName(route)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return handler + "Logic"
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@ import (
|
||||
|
||||
"github.com/tal-tech/go-zero/tools/goctl/api/spec"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/api/util"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/config"
|
||||
ctlutil "github.com/tal-tech/go-zero/tools/goctl/util"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util/format"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/vars"
|
||||
)
|
||||
|
||||
@@ -40,10 +42,10 @@ func (l *{{.logic}}) {{.function}}({{.request}}) {{.responseType}} {
|
||||
}
|
||||
`
|
||||
|
||||
func genLogic(dir string, api *spec.ApiSpec) error {
|
||||
func genLogic(dir string, cfg *config.Config, api *spec.ApiSpec) error {
|
||||
for _, g := range api.Service.Groups {
|
||||
for _, r := range g.Routes {
|
||||
err := genLogicByRoute(dir, g, r)
|
||||
err := genLogicByRoute(dir, cfg, g, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -52,16 +54,14 @@ func genLogic(dir string, api *spec.ApiSpec) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func genLogicByRoute(dir string, group spec.Group, route spec.Route) error {
|
||||
handler, ok := util.GetAnnotationValue(route.Annotations, "server", "handler")
|
||||
if !ok {
|
||||
return fmt.Errorf("missing handler annotation for %q", route.Path)
|
||||
func genLogicByRoute(dir string, cfg *config.Config, group spec.Group, route spec.Route) error {
|
||||
logic := getLogicName(route)
|
||||
goFile, err := format.FileNamingFormat(cfg.NamingFormat, logic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
handler = strings.TrimSuffix(handler, "handler")
|
||||
handler = strings.TrimSuffix(handler, "Handler")
|
||||
filename := strings.ToLower(handler)
|
||||
goFile := filename + "logic.go"
|
||||
goFile = goFile + ".go"
|
||||
fp, created, err := util.MaybeCreateFile(dir, getLogicFolderPath(group, route), goFile)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -102,8 +102,8 @@ func genLogicByRoute(dir string, group spec.Group, route spec.Route) error {
|
||||
buffer := new(bytes.Buffer)
|
||||
err = t.Execute(fp, map[string]string{
|
||||
"imports": imports,
|
||||
"logic": strings.Title(handler) + "Logic",
|
||||
"function": strings.Title(strings.TrimSuffix(handler, "Handler")),
|
||||
"logic": strings.Title(logic),
|
||||
"function": strings.Title(strings.TrimSuffix(logic, "Logic")),
|
||||
"responseType": responseString,
|
||||
"returnString": returnString,
|
||||
"request": requestString,
|
||||
|
||||
@@ -8,7 +8,9 @@ import (
|
||||
|
||||
"github.com/tal-tech/go-zero/tools/goctl/api/spec"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/api/util"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/config"
|
||||
ctlutil "github.com/tal-tech/go-zero/tools/goctl/util"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util/format"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/vars"
|
||||
)
|
||||
|
||||
@@ -40,12 +42,17 @@ func main() {
|
||||
}
|
||||
`
|
||||
|
||||
func genMain(dir string, api *spec.ApiSpec) error {
|
||||
func genMain(dir string, cfg *config.Config, api *spec.ApiSpec) error {
|
||||
name := strings.ToLower(api.Service.Name)
|
||||
if strings.HasSuffix(name, "-api") {
|
||||
name = strings.ReplaceAll(name, "-api", "")
|
||||
}
|
||||
goFile := name + ".go"
|
||||
filename, err := format.FileNamingFormat(cfg.NamingFormat, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
goFile := filename + ".go"
|
||||
fp, created, err := util.MaybeCreateFile(dir, "", goFile)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -5,7 +5,10 @@ import (
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/tal-tech/go-zero/tools/goctl/api/spec"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/api/util"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/config"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util/format"
|
||||
)
|
||||
|
||||
var middlewareImplementCode = `
|
||||
@@ -30,9 +33,16 @@ func (m *{{.name}})Handle(next http.HandlerFunc) http.HandlerFunc {
|
||||
}
|
||||
`
|
||||
|
||||
func genMiddleware(dir string, middlewares []string) error {
|
||||
func genMiddleware(dir string, cfg *config.Config, api *spec.ApiSpec) error {
|
||||
var middlewares = getMiddleware(api)
|
||||
for _, item := range middlewares {
|
||||
filename := strings.TrimSuffix(strings.ToLower(item), "middleware") + "middleware" + ".go"
|
||||
middlewareFilename := strings.TrimSuffix(strings.ToLower(item), "middleware") + "_middleware"
|
||||
formatName, err := format.FileNamingFormat(cfg.NamingFormat, middlewareFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filename := formatName + ".go"
|
||||
fp, created, err := util.MaybeCreateFile(dir, middlewareDir, filename)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -12,12 +12,14 @@ import (
|
||||
"github.com/tal-tech/go-zero/core/collection"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/api/spec"
|
||||
apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/config"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util/format"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/vars"
|
||||
)
|
||||
|
||||
const (
|
||||
routesFilename = "routes.go"
|
||||
routesFilename = "routes"
|
||||
routesTemplate = `// Code generated by goctl. DO NOT EDIT.
|
||||
package handler
|
||||
|
||||
@@ -62,7 +64,7 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func genRoutes(dir string, api *spec.ApiSpec) error {
|
||||
func genRoutes(dir string, cfg *config.Config, api *spec.ApiSpec) error {
|
||||
var builder strings.Builder
|
||||
groups, err := getRoutes(api)
|
||||
if err != nil {
|
||||
@@ -121,10 +123,16 @@ func genRoutes(dir string, api *spec.ApiSpec) error {
|
||||
return err
|
||||
}
|
||||
|
||||
filename := path.Join(dir, handlerDir, routesFilename)
|
||||
routeFilename, err := format.FileNamingFormat(cfg.NamingFormat, routesFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
routeFilename = routeFilename + ".go"
|
||||
|
||||
filename := path.Join(dir, handlerDir, routeFilename)
|
||||
os.Remove(filename)
|
||||
|
||||
fp, created, err := apiutil.MaybeCreateFile(dir, handlerDir, routesFilename)
|
||||
fp, created, err := apiutil.MaybeCreateFile(dir, handlerDir, routeFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -176,11 +184,8 @@ func getRoutes(api *spec.ApiSpec) ([]group, error) {
|
||||
for _, g := range api.Service.Groups {
|
||||
var groupedRoutes group
|
||||
for _, r := range g.Routes {
|
||||
handler, ok := apiutil.GetAnnotationValue(r.Annotations, "server", "handler")
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("missing handler annotation for route %q", r.Path)
|
||||
}
|
||||
handler = getHandlerBaseName(handler) + "Handler(serverCtx)"
|
||||
handler := getHandlerName(r)
|
||||
handler = handler + "(serverCtx)"
|
||||
folder, ok := apiutil.GetAnnotationValue(r.Annotations, "server", groupProperty)
|
||||
if ok {
|
||||
handler = toPrefix(folder) + "." + strings.ToUpper(handler[:1]) + handler[1:]
|
||||
|
||||
@@ -8,12 +8,14 @@ import (
|
||||
|
||||
"github.com/tal-tech/go-zero/tools/goctl/api/spec"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/api/util"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/config"
|
||||
ctlutil "github.com/tal-tech/go-zero/tools/goctl/util"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util/format"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/vars"
|
||||
)
|
||||
|
||||
const (
|
||||
contextFilename = "servicecontext.go"
|
||||
contextFilename = "service_context"
|
||||
contextTemplate = `package svc
|
||||
|
||||
import (
|
||||
@@ -35,8 +37,13 @@ func NewServiceContext(c {{.config}}) *ServiceContext {
|
||||
`
|
||||
)
|
||||
|
||||
func genServiceContext(dir string, api *spec.ApiSpec) error {
|
||||
fp, created, err := util.MaybeCreateFile(dir, contextDir, contextFilename)
|
||||
func genServiceContext(dir string, cfg *config.Config, api *spec.ApiSpec) error {
|
||||
filename, err := format.FileNamingFormat(cfg.NamingFormat, contextFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fp, created, err := util.MaybeCreateFile(dir, contextDir, filename+".go")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -64,10 +71,6 @@ func genServiceContext(dir string, api *spec.ApiSpec) error {
|
||||
var middlewareStr string
|
||||
var middlewareAssignment string
|
||||
var middlewares = getMiddleware(api)
|
||||
err = genMiddleware(dir, middlewares)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, item := range middlewares {
|
||||
middlewareStr += fmt.Sprintf("%s rest.Middleware\n", item)
|
||||
|
||||
@@ -12,11 +12,13 @@ import (
|
||||
|
||||
"github.com/tal-tech/go-zero/tools/goctl/api/spec"
|
||||
apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/config"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util/format"
|
||||
)
|
||||
|
||||
const (
|
||||
typesFile = "types.go"
|
||||
typesFile = "types"
|
||||
typesTemplate = `// Code generated by goctl. DO NOT EDIT.
|
||||
package types{{if .containsTime}}
|
||||
import (
|
||||
@@ -43,19 +45,25 @@ func BuildTypes(types []spec.Type) (string, error) {
|
||||
return builder.String(), nil
|
||||
}
|
||||
|
||||
func genTypes(dir string, api *spec.ApiSpec) error {
|
||||
func genTypes(dir string, cfg *config.Config, api *spec.ApiSpec) error {
|
||||
val, err := BuildTypes(api.Types)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filename := path.Join(dir, typesDir, typesFile)
|
||||
os.Remove(filename)
|
||||
|
||||
fp, created, err := apiutil.MaybeCreateFile(dir, typesDir, typesFile)
|
||||
typeFilename, err := format.FileNamingFormat(cfg.NamingFormat, typesFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
typeFilename = typeFilename + ".go"
|
||||
filename := path.Join(dir, typesDir, typeFilename)
|
||||
os.Remove(filename)
|
||||
|
||||
fp, created, err := apiutil.MaybeCreateFile(dir, typesDir, typeFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !created {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,25 +3,27 @@ package new
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/tal-tech/go-zero/tools/goctl/api/gogen"
|
||||
conf "github.com/tal-tech/go-zero/tools/goctl/config"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
const apiTemplate = `
|
||||
type Request struct {
|
||||
type Request {
|
||||
Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
type Response {
|
||||
Message string ` + "`" + `json:"message"` + "`" + `
|
||||
}
|
||||
|
||||
service {{.name}}-api {
|
||||
@handler GreetHandler
|
||||
get /greet/from/:name(Request) returns (Response);
|
||||
@handler {{.handler}}Handler
|
||||
get /from/:name(Request) returns (Response);
|
||||
}
|
||||
`
|
||||
|
||||
@@ -53,11 +55,12 @@ func NewService(c *cli.Context) error {
|
||||
defer fp.Close()
|
||||
t := template.Must(template.New("template").Parse(apiTemplate))
|
||||
if err := t.Execute(fp, map[string]string{
|
||||
"name": dirName,
|
||||
"name": dirName,
|
||||
"handler": strings.Title(dirName),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = gogen.DoGenProject(apiFilePath, abs)
|
||||
err = gogen.DoGenProject(apiFilePath, abs, conf.DefaultFormat)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/tal-tech/go-zero/core/stringx"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/api/util"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -15,6 +18,7 @@ const (
|
||||
tokenType = "type"
|
||||
tokenService = "service"
|
||||
tokenServiceAnnotation = "@server"
|
||||
tokenStruct = "struct"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -72,7 +76,7 @@ func ParseApi(src string) (*ApiStruct, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *apiRootState) process(api *ApiStruct, token string) (apiFileState, error) {
|
||||
func (s *apiRootState) process(api *ApiStruct, _ string) (apiFileState, error) {
|
||||
var builder strings.Builder
|
||||
for {
|
||||
ch, err := s.readSkipComment()
|
||||
@@ -124,7 +128,7 @@ func (s *apiInfoState) process(api *ApiStruct, token string) (apiFileState, erro
|
||||
return nil, err
|
||||
}
|
||||
|
||||
api.Info += "\n" + token + line
|
||||
api.Info += newline + token + line
|
||||
token = ""
|
||||
if strings.TrimSpace(line) == string(rightParenthesis) {
|
||||
return &apiRootState{s.baseState}, nil
|
||||
@@ -139,39 +143,60 @@ func (s *apiImportState) process(api *ApiStruct, token string) (apiFileState, er
|
||||
}
|
||||
|
||||
line = token + line
|
||||
line = util.RemoveComment(line)
|
||||
if len(strings.Fields(line)) != 2 {
|
||||
return nil, errors.New("import syntax error: " + line)
|
||||
}
|
||||
|
||||
api.Imports += "\n" + line
|
||||
api.Imports += newline + line
|
||||
return &apiRootState{s.baseState}, nil
|
||||
}
|
||||
|
||||
func (s *apiTypeState) process(api *ApiStruct, token string) (apiFileState, error) {
|
||||
var blockCount = 0
|
||||
var braceCount = 0
|
||||
for {
|
||||
line, err := s.readLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
api.Type += "\n\n" + token + line
|
||||
token = ""
|
||||
line = token + line
|
||||
if braceCount == 0 {
|
||||
line = mayInsertStructKeyword(line)
|
||||
}
|
||||
api.Type += newline + newline + line
|
||||
line = strings.TrimSpace(line)
|
||||
line = removeComment(line)
|
||||
line = util.RemoveComment(line)
|
||||
token = ""
|
||||
|
||||
if strings.HasSuffix(line, leftBrace) {
|
||||
blockCount++
|
||||
braceCount++
|
||||
}
|
||||
if strings.HasSuffix(line, string(leftParenthesis)) {
|
||||
blockCount++
|
||||
}
|
||||
if strings.HasSuffix(line, string(rightBrace)) {
|
||||
blockCount--
|
||||
braceCount--
|
||||
}
|
||||
if strings.HasSuffix(line, string(rightParenthesis)) {
|
||||
blockCount--
|
||||
}
|
||||
|
||||
if braceCount >= 2 {
|
||||
return nil, errors.New("nested type not supported: " + line)
|
||||
}
|
||||
if braceCount < 0 {
|
||||
line = strings.TrimSuffix(line, string(rightBrace))
|
||||
line = strings.TrimSpace(line)
|
||||
if strings.HasSuffix(line, leftBrace) {
|
||||
blockCount++
|
||||
braceCount++
|
||||
}
|
||||
}
|
||||
|
||||
if blockCount == 0 {
|
||||
return &apiRootState{s.baseState}, nil
|
||||
}
|
||||
@@ -188,9 +213,10 @@ func (s *apiServiceState) process(api *ApiStruct, token string) (apiFileState, e
|
||||
|
||||
line = token + line
|
||||
token = ""
|
||||
api.Service += "\n" + line
|
||||
api.Service += newline + line
|
||||
line = strings.TrimSpace(line)
|
||||
line = removeComment(line)
|
||||
line = util.RemoveComment(line)
|
||||
|
||||
if strings.HasSuffix(line, leftBrace) {
|
||||
blockCount++
|
||||
}
|
||||
@@ -210,10 +236,33 @@ func (s *apiServiceState) process(api *ApiStruct, token string) (apiFileState, e
|
||||
}
|
||||
}
|
||||
|
||||
func removeComment(line string) string {
|
||||
var commentIdx = strings.Index(line, "//")
|
||||
if commentIdx >= 0 {
|
||||
return line[:commentIdx]
|
||||
func mayInsertStructKeyword(line string) string {
|
||||
line = util.RemoveComment(line)
|
||||
if !strings.HasSuffix(line, leftBrace) && !strings.HasSuffix(line, string(rightBrace)) {
|
||||
return line
|
||||
}
|
||||
return line
|
||||
|
||||
fields := strings.Fields(line)
|
||||
if stringx.Contains(fields, tokenStruct) ||
|
||||
stringx.Contains(fields, tokenStruct+leftBrace) ||
|
||||
stringx.Contains(fields, tokenStruct+leftBrace+string(rightBrace)) ||
|
||||
len(fields) <= 1 {
|
||||
return line
|
||||
}
|
||||
|
||||
var insertIndex int
|
||||
if fields[0] == tokenType {
|
||||
insertIndex = 2
|
||||
} else {
|
||||
insertIndex = 1
|
||||
}
|
||||
if insertIndex >= len(fields) {
|
||||
return line
|
||||
}
|
||||
|
||||
var result []string
|
||||
result = append(result, fields[:insertIndex]...)
|
||||
result = append(result, tokenStruct)
|
||||
result = append(result, fields[insertIndex:]...)
|
||||
return strings.Join(result, " ")
|
||||
}
|
||||
|
||||
@@ -15,9 +15,8 @@ import (
|
||||
)
|
||||
|
||||
type Parser struct {
|
||||
r *bufio.Reader
|
||||
typeDef string
|
||||
api *ApiStruct
|
||||
r *bufio.Reader
|
||||
api *ApiStruct
|
||||
}
|
||||
|
||||
func NewParser(filename string) (*Parser, error) {
|
||||
@@ -73,15 +72,14 @@ func NewParser(filename string) (*Parser, error) {
|
||||
var buffer = new(bytes.Buffer)
|
||||
buffer.WriteString(apiStruct.Service)
|
||||
return &Parser{
|
||||
r: bufio.NewReader(buffer),
|
||||
typeDef: apiStruct.Type,
|
||||
api: apiStruct,
|
||||
r: bufio.NewReader(buffer),
|
||||
api: apiStruct,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Parser) Parse() (api *spec.ApiSpec, err error) {
|
||||
api = new(spec.ApiSpec)
|
||||
var sp = StructParser{Src: p.typeDef}
|
||||
var sp = StructParser{Src: p.api.Type}
|
||||
types, err := sp.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -16,4 +16,5 @@ const (
|
||||
multilineBeginTag = '>'
|
||||
multilineEndTag = '<'
|
||||
semicolon = ';'
|
||||
newline = "\n"
|
||||
)
|
||||
|
||||
@@ -2,30 +2,15 @@ package spec
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/tal-tech/go-zero/core/stringx"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||
)
|
||||
|
||||
const (
|
||||
TagKey = "tag"
|
||||
NameKey = "name"
|
||||
OptionKey = "option"
|
||||
BodyTag = "json"
|
||||
)
|
||||
const bodyTagKey = "json"
|
||||
|
||||
var (
|
||||
TagRe = regexp.MustCompile(`(?P<tag>\w+):"(?P<name>[^,"]+)[,]?(?P<option>[^"]*)"`)
|
||||
TagSubNames = TagRe.SubexpNames()
|
||||
definedTags = []string{TagKey, NameKey, OptionKey}
|
||||
)
|
||||
|
||||
type Attribute struct {
|
||||
Key string
|
||||
value string
|
||||
}
|
||||
var definedKeys = []string{"json", "form", "path"}
|
||||
|
||||
func (s Service) Routes() []Route {
|
||||
var result []Route
|
||||
@@ -35,81 +20,62 @@ func (s Service) Routes() []Route {
|
||||
return result
|
||||
}
|
||||
|
||||
func (m Member) IsOptional() bool {
|
||||
var option string
|
||||
|
||||
matches := TagRe.FindStringSubmatch(m.Tag)
|
||||
for i := range matches {
|
||||
name := TagSubNames[i]
|
||||
if name == OptionKey {
|
||||
option = matches[i]
|
||||
}
|
||||
func (m Member) Tags() []*Tag {
|
||||
tags, err := Parse(m.Tag)
|
||||
if err != nil {
|
||||
panic(m.Tag + ", " + err.Error())
|
||||
}
|
||||
|
||||
if len(option) == 0 {
|
||||
return tags.Tags()
|
||||
}
|
||||
|
||||
func (m Member) IsOptional() bool {
|
||||
if !m.IsBodyMember() {
|
||||
return false
|
||||
}
|
||||
|
||||
fields := strings.Split(option, ",")
|
||||
for _, field := range fields {
|
||||
if field == "optional" || strings.HasPrefix(field, "default=") {
|
||||
return true
|
||||
tag := m.Tags()
|
||||
for _, item := range tag {
|
||||
if item.Key == bodyTagKey {
|
||||
if stringx.Contains(item.Options, "optional") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (m Member) IsOmitempty() bool {
|
||||
var option string
|
||||
|
||||
matches := TagRe.FindStringSubmatch(m.Tag)
|
||||
for i := range matches {
|
||||
name := TagSubNames[i]
|
||||
if name == OptionKey {
|
||||
option = matches[i]
|
||||
}
|
||||
}
|
||||
|
||||
if len(option) == 0 {
|
||||
if !m.IsBodyMember() {
|
||||
return false
|
||||
}
|
||||
|
||||
fields := strings.Split(option, ",")
|
||||
for _, field := range fields {
|
||||
if field == "omitempty" {
|
||||
return true
|
||||
tag := m.Tags()
|
||||
for _, item := range tag {
|
||||
if item.Key == bodyTagKey {
|
||||
if stringx.Contains(item.Options, "omitempty") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (m Member) GetAttributes() []Attribute {
|
||||
matches := TagRe.FindStringSubmatch(m.Tag)
|
||||
var result []Attribute
|
||||
for i := range matches {
|
||||
name := TagSubNames[i]
|
||||
if stringx.Contains(definedTags, name) {
|
||||
result = append(result, Attribute{
|
||||
Key: name,
|
||||
value: matches[i],
|
||||
})
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (m Member) GetPropertyName() (string, error) {
|
||||
attrs := m.GetAttributes()
|
||||
for _, attr := range attrs {
|
||||
if attr.Key == NameKey && len(attr.value) > 0 {
|
||||
if attr.value == "-" {
|
||||
tags := m.Tags()
|
||||
if len(tags) == 0 {
|
||||
return "", errors.New("json property name not exist, member: " + m.Name)
|
||||
}
|
||||
|
||||
for _, tag := range tags {
|
||||
if stringx.Contains(definedKeys, tag.Key) {
|
||||
if tag.Name == "-" {
|
||||
return util.Untitle(m.Name), nil
|
||||
}
|
||||
return attr.value, nil
|
||||
return tag.Name, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", errors.New("json property name not exist, member: " + m.Name)
|
||||
}
|
||||
|
||||
@@ -121,9 +87,10 @@ func (m Member) IsBodyMember() bool {
|
||||
if m.IsInline {
|
||||
return true
|
||||
}
|
||||
attrs := m.GetAttributes()
|
||||
for _, attr := range attrs {
|
||||
if attr.value == BodyTag {
|
||||
|
||||
tags := m.Tags()
|
||||
for _, tag := range tags {
|
||||
if tag.Key == bodyTagKey {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
67
tools/goctl/api/spec/tags.go
Normal file
67
tools/goctl/api/spec/tags.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package spec
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/structtag"
|
||||
)
|
||||
|
||||
var errTagNotExist = errors.New("tag does not exist")
|
||||
|
||||
type (
|
||||
Tag struct {
|
||||
// Key is the tag key, such as json, xml, etc..
|
||||
// i.e: `json:"foo,omitempty". Here key is: "json"
|
||||
Key string
|
||||
|
||||
// Name is a part of the value
|
||||
// i.e: `json:"foo,omitempty". Here name is: "foo"
|
||||
Name string
|
||||
|
||||
// Options is a part of the value. It contains a slice of tag options i.e:
|
||||
// `json:"foo,omitempty". Here options is: ["omitempty"]
|
||||
Options []string
|
||||
}
|
||||
|
||||
Tags struct {
|
||||
tags []*Tag
|
||||
}
|
||||
)
|
||||
|
||||
func Parse(tag string) (*Tags, error) {
|
||||
tag = strings.TrimPrefix(tag, "`")
|
||||
tag = strings.TrimSuffix(tag, "`")
|
||||
tags, err := structtag.Parse(tag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result Tags
|
||||
for _, item := range tags.Tags() {
|
||||
result.tags = append(result.tags, &Tag{Key: item.Key, Name: item.Name, Options: item.Options})
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (t *Tags) Get(key string) (*Tag, error) {
|
||||
for _, tag := range t.tags {
|
||||
if tag.Key == key {
|
||||
return tag, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errTagNotExist
|
||||
}
|
||||
|
||||
func (t *Tags) Keys() []string {
|
||||
var keys []string
|
||||
for _, tag := range t.tags {
|
||||
keys = append(keys, tag.Key)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
func (t *Tags) Tags() []*Tag {
|
||||
return t.tags
|
||||
}
|
||||
@@ -26,19 +26,6 @@ func MaybeCreateFile(dir, subdir, file string) (fp *os.File, created bool, err e
|
||||
return
|
||||
}
|
||||
|
||||
func ClearAndOpenFile(fpath string) (*os.File, error) {
|
||||
f, err := os.OpenFile(fpath, os.O_WRONLY|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = f.WriteString("")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func WrapErr(err error, message string) error {
|
||||
return errors.New(message + ", " + err.Error())
|
||||
}
|
||||
@@ -81,3 +68,11 @@ func WriteIndent(writer io.Writer, indent int) {
|
||||
fmt.Fprint(writer, "\t")
|
||||
}
|
||||
}
|
||||
|
||||
func RemoveComment(line string) string {
|
||||
var commentIdx = strings.Index(line, "//")
|
||||
if commentIdx >= 0 {
|
||||
return strings.TrimSpace(line[:commentIdx])
|
||||
}
|
||||
return strings.TrimSpace(line)
|
||||
}
|
||||
|
||||
122
tools/goctl/config/config.go
Normal file
122
tools/goctl/config/config.go
Normal file
@@ -0,0 +1,122 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
const (
|
||||
configFile = "config.yaml"
|
||||
configFolder = "config"
|
||||
DefaultFormat = "gozero"
|
||||
)
|
||||
|
||||
const defaultYaml = `# namingFormat is used to define the naming format of the generated file name.
|
||||
# just like time formatting, you can specify the formatting style through the
|
||||
# two format characters go, and zero. for example: snake format you can
|
||||
# define as go_zero, camel case format you can it is defined as goZero,
|
||||
# and even split characters can be specified, such as go#zero. in theory,
|
||||
# any combination can be used, but the prerequisite must meet the naming conventions
|
||||
# of each operating system file name. if you want to independently control the file
|
||||
# naming style of the api, rpc, and model layers, you can set it through apiNamingFormat,
|
||||
# rpcNamingFormat, modelNamingFormat, and independent control is not enabled by default.
|
||||
# for more information, please see #{apiNamingFormat},#{rpcNamingFormat},#{modelNamingFormat}
|
||||
# Note: namingFormat is based on snake or camel string
|
||||
namingFormat: gozero
|
||||
`
|
||||
|
||||
type Config struct {
|
||||
// NamingFormat is used to define the naming format of the generated file name.
|
||||
// just like time formatting, you can specify the formatting style through the
|
||||
// two format characters go, and zero. for example: snake format you can
|
||||
// define as go_zero, camel case format you can it is defined as goZero,
|
||||
// and even split characters can be specified, such as go#zero. in theory,
|
||||
// any combination can be used, but the prerequisite must meet the naming conventions
|
||||
// of each operating system file name.
|
||||
// Note: NamingFormat is based on snake or camel string
|
||||
NamingFormat string `yaml:"namingFormat"`
|
||||
}
|
||||
|
||||
func NewConfig(format string) (*Config, error) {
|
||||
if len(format) == 0 {
|
||||
format = DefaultFormat
|
||||
}
|
||||
cfg := &Config{NamingFormat: format}
|
||||
err := validate(cfg)
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
func InitOrGetConfig() (*Config, error) {
|
||||
var (
|
||||
defaultConfig Config
|
||||
)
|
||||
err := yaml.Unmarshal([]byte(defaultYaml), &defaultConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
goctlHome, err := util.GetGoctlHome()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
configDir := filepath.Join(goctlHome, configFolder)
|
||||
configFilename := filepath.Join(configDir, configFile)
|
||||
if util.FileExists(configFilename) {
|
||||
data, err := ioutil.ReadFile(configFilename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = yaml.Unmarshal(data, &defaultConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = validate(&defaultConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &defaultConfig, nil
|
||||
}
|
||||
|
||||
err = util.MkdirIfNotExist(configDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
f, err := os.Create(configFilename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = f.Close()
|
||||
}()
|
||||
|
||||
_, err = f.WriteString(defaultYaml)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = validate(&defaultConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &defaultConfig, nil
|
||||
}
|
||||
|
||||
func validate(cfg *Config) error {
|
||||
if len(strings.TrimSpace(cfg.NamingFormat)) == 0 {
|
||||
return errors.New("missing namingFormat")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
50
tools/goctl/config/readme.md
Normal file
50
tools/goctl/config/readme.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# 配置项管理
|
||||
|
||||
| 名称 | 是否可选 | 说明 |
|
||||
|-------------------|----------|-----------------------------------------------|
|
||||
| namingFormat | YES | 文件名称格式化符 |
|
||||
|
||||
# naming-format
|
||||
`namingFormat`可以用于对生成代码的文件名称进行格式化,和日期格式化符(yyyy-MM-dd)类似,在代码生成时可以根据这些配置项的格式化符进行格式化。
|
||||
|
||||
## 格式化符(gozero)
|
||||
格式化符有`go`,`zero`组成,如常见的三种格式化风格你可以这样编写:
|
||||
* lower: `gozero`
|
||||
* camel: `goZero`
|
||||
* snake: `go_zero`
|
||||
|
||||
常见格式化符生成示例
|
||||
源字符:welcome_to_go_zero
|
||||
|
||||
| 格式化符 | 格式化结果 | 说明 |
|
||||
|------------|-----------------------|---------------------------|
|
||||
| gozero | welcometogozero | 小写 |
|
||||
| goZero | welcomeToGoZero | 驼峰 |
|
||||
| go_zero | welcome_to_go_zero | snake |
|
||||
| Go#zero | Welcome#to#go#zero | #号分割Title类型 |
|
||||
| GOZERO | WELCOMETOGOZERO | 大写 |
|
||||
| \_go#zero_ | \_welcome#to#go#zero_ | 下划线做前后缀,并且#分割 |
|
||||
|
||||
错误格式化符示例
|
||||
* go
|
||||
* gOZero
|
||||
* zero
|
||||
* goZEro
|
||||
* goZERo
|
||||
* goZeRo
|
||||
* tal
|
||||
|
||||
# 使用方法
|
||||
目前可通过在生成api、rpc、model时通过`--style`参数指定format格式,如:
|
||||
```shell script
|
||||
goctl api go test.api -dir . -style gozero
|
||||
```
|
||||
```shell script
|
||||
goctl rpc proto -src test.proto -dir . -style go_zero
|
||||
```
|
||||
```shell script
|
||||
goctl model mysql datasource -url="" -table="*" -dir ./snake -style GoZero
|
||||
```
|
||||
|
||||
# 默认值
|
||||
当不指定-style时默认值为`gozero`
|
||||
@@ -43,8 +43,8 @@ func GenConfigCommand(c *cli.Context) error {
|
||||
return errors.New("abs failed: " + c.String("path"))
|
||||
}
|
||||
|
||||
goModPath, hasFound := util.FindGoModPath(path)
|
||||
if !hasFound {
|
||||
goModPath, found := util.FindGoModPath(path)
|
||||
if !found {
|
||||
return errors.New("go mod not initial")
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,12 @@ package docker
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||
ctlutil "github.com/tal-tech/go-zero/tools/goctl/util"
|
||||
@@ -13,22 +15,48 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
etcDir = "etc"
|
||||
yamlEtx = ".yaml"
|
||||
etcDir = "etc"
|
||||
yamlEtx = ".yaml"
|
||||
cstOffset = 60 * 60 * 8 // 8 hours offset for Chinese Standard Time
|
||||
)
|
||||
|
||||
type Docker struct {
|
||||
Chinese bool
|
||||
GoRelPath string
|
||||
GoFile string
|
||||
ExeFile string
|
||||
Argument string
|
||||
}
|
||||
|
||||
func DockerCommand(c *cli.Context) error {
|
||||
goFile := c.String("go")
|
||||
if len(goFile) == 0 {
|
||||
return errors.New("-go can't be empty")
|
||||
}
|
||||
|
||||
if !util.FileExists(goFile) {
|
||||
return fmt.Errorf("file %q not found", goFile)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(etcDir); os.IsNotExist(err) {
|
||||
return generateDockerfile(goFile)
|
||||
}
|
||||
|
||||
cfg, err := findConfig(goFile, etcDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return generateDockerfile(goFile, "-f", "etc/"+cfg)
|
||||
if err := generateDockerfile(goFile, "-f", "etc/"+cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
projDir, ok := util.FindProjectPath(goFile)
|
||||
if ok {
|
||||
fmt.Printf("Run \"docker build ...\" command in dir %q\n", projDir)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func findConfig(file, dir string) (string, error) {
|
||||
@@ -87,12 +115,14 @@ func generateDockerfile(goFile string, args ...string) error {
|
||||
builder.WriteString(`, "` + arg + `"`)
|
||||
}
|
||||
|
||||
_, offset := time.Now().Zone()
|
||||
t := template.Must(template.New("dockerfile").Parse(text))
|
||||
return t.Execute(out, map[string]string{
|
||||
"goRelPath": projPath,
|
||||
"goFile": goFile,
|
||||
"exeFile": util.FileNameWithoutExt(filepath.Base(goFile)),
|
||||
"argument": builder.String(),
|
||||
return t.Execute(out, Docker{
|
||||
Chinese: offset == cstOffset,
|
||||
GoRelPath: projPath,
|
||||
GoFile: goFile,
|
||||
ExeFile: util.FileNameWithoutExt(filepath.Base(goFile)),
|
||||
Argument: builder.String(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -14,12 +14,16 @@ LABEL stage=gobuilder
|
||||
|
||||
ENV CGO_ENABLED 0
|
||||
ENV GOOS linux
|
||||
ENV GOPROXY https://goproxy.cn,direct
|
||||
{{if .Chinese}}ENV GOPROXY https://goproxy.cn,direct{{end}}
|
||||
|
||||
WORKDIR /build/zero
|
||||
|
||||
ADD go.mod .
|
||||
ADD go.sum .
|
||||
RUN go mod download
|
||||
COPY . .
|
||||
COPY {{.goRelPath}}/etc /app/etc
|
||||
RUN go build -ldflags="-s -w" -o /app/{{.exeFile}} {{.goRelPath}}/{{.goFile}}
|
||||
COPY {{.GoRelPath}}/etc /app/etc
|
||||
RUN go build -ldflags="-s -w" -o /app/{{.ExeFile}} {{.GoRelPath}}/{{.GoFile}}
|
||||
|
||||
|
||||
FROM alpine
|
||||
@@ -30,10 +34,10 @@ RUN apk add --no-cache tzdata
|
||||
ENV TZ Asia/Shanghai
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=builder /app/{{.exeFile}} /app/{{.exeFile}}
|
||||
COPY --from=builder /app/{{.ExeFile}} /app/{{.ExeFile}}
|
||||
COPY --from=builder /app/etc /app/etc
|
||||
|
||||
CMD ["./{{.exeFile}}"{{.argument}}]
|
||||
CMD ["./{{.ExeFile}}"{{.Argument}}]
|
||||
`
|
||||
)
|
||||
|
||||
|
||||
@@ -18,14 +18,16 @@ import (
|
||||
"github.com/tal-tech/go-zero/tools/goctl/api/validate"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/configgen"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/docker"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/kube"
|
||||
model "github.com/tal-tech/go-zero/tools/goctl/model/sql/command"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/plugin"
|
||||
rpc "github.com/tal-tech/go-zero/tools/goctl/rpc/cli"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/tpl"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
BuildVersion = "20201108"
|
||||
BuildVersion = "20201125"
|
||||
commands = []cli.Command{
|
||||
{
|
||||
Name: "api",
|
||||
@@ -98,6 +100,11 @@ var (
|
||||
Name: "api",
|
||||
Usage: "the api file",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "style",
|
||||
Required: false,
|
||||
Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]",
|
||||
},
|
||||
},
|
||||
Action: gogen.GoCommand,
|
||||
},
|
||||
@@ -180,6 +187,30 @@ var (
|
||||
},
|
||||
Action: ktgen.KtCommand,
|
||||
},
|
||||
{
|
||||
Name: "plugin",
|
||||
Usage: "custom file generator",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "plugin, p",
|
||||
Usage: "the plugin file",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "dir",
|
||||
Usage: "the target directory",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "api",
|
||||
Usage: "the api file",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "style",
|
||||
Required: false,
|
||||
Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]",
|
||||
},
|
||||
},
|
||||
Action: plugin.PluginCommand,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -193,6 +224,94 @@ var (
|
||||
},
|
||||
Action: docker.DockerCommand,
|
||||
},
|
||||
{
|
||||
Name: "kube",
|
||||
Usage: "generate kubernetes files",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "deploy",
|
||||
Usage: "generate deployment yaml file",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "name",
|
||||
Usage: "the name of deployment",
|
||||
Required: true,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "namespace",
|
||||
Usage: "the namespace of deployment",
|
||||
Required: true,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "image",
|
||||
Usage: "the docker image of deployment",
|
||||
Required: true,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "secret",
|
||||
Usage: "the image pull secret",
|
||||
Required: true,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "requestCpu",
|
||||
Usage: "the request cpu to deploy",
|
||||
Value: 500,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "requestMem",
|
||||
Usage: "the request memory to deploy",
|
||||
Value: 512,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "limitCpu",
|
||||
Usage: "the limit cpu to deploy",
|
||||
Value: 1000,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "limitMem",
|
||||
Usage: "the limit memory to deploy",
|
||||
Value: 1024,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "o",
|
||||
Usage: "the output yaml file",
|
||||
Required: true,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "replicas",
|
||||
Usage: "the number of replicas to deploy",
|
||||
Value: 3,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "revisions",
|
||||
Usage: "the number of revision history to limit",
|
||||
Value: 5,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "port",
|
||||
Usage: "the port of the deployment to listen on pod",
|
||||
Required: true,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "nodePort",
|
||||
Usage: "the nodePort of the deployment to expose",
|
||||
Value: 0,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "minReplicas",
|
||||
Usage: "the min replicas to deploy",
|
||||
Value: 3,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "maxReplicas",
|
||||
Usage: "the max replicas of deploy",
|
||||
Value: 10,
|
||||
},
|
||||
},
|
||||
Action: kube.DeploymentCommand,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "rpc",
|
||||
Usage: "generate rpc code",
|
||||
@@ -201,6 +320,11 @@ var (
|
||||
Name: "new",
|
||||
Usage: `generate rpc demo service`,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "style",
|
||||
Required: false,
|
||||
Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "idea",
|
||||
Usage: "whether the command execution environment is from idea plugin. [optional]",
|
||||
@@ -235,6 +359,11 @@ var (
|
||||
Name: "dir, d",
|
||||
Usage: `the target path of the code`,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "style",
|
||||
Required: false,
|
||||
Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "idea",
|
||||
Usage: "whether the command execution environment is from idea plugin. [optional]",
|
||||
@@ -265,8 +394,9 @@ var (
|
||||
Usage: "the target dir",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "style",
|
||||
Usage: "the file naming style, lower|camel|underline,default is lower",
|
||||
Name: "style",
|
||||
Required: false,
|
||||
Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "cache, c",
|
||||
@@ -300,8 +430,9 @@ var (
|
||||
Usage: "the target dir",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "style",
|
||||
Usage: "the file naming style, lower|camel|snake, default is lower",
|
||||
Name: "style",
|
||||
Required: false,
|
||||
Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "idea",
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
package k8s
|
||||
|
||||
var apiRpcTmeplate = `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{.name}}
|
||||
namespace: {{.namespace}}
|
||||
labels:
|
||||
app: {{.name}}
|
||||
spec:
|
||||
replicas: {{.replicas}}
|
||||
revisionHistoryLimit: {{.revisionHistoryLimit}}
|
||||
selector:
|
||||
matchLabels:
|
||||
app: {{.name}}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: {{.name}}
|
||||
spec:{{if .envIsDev}}
|
||||
terminationGracePeriodSeconds: 60{{end}}
|
||||
containers:
|
||||
- name: {{.name}}
|
||||
image: registry-vpc.cn-hangzhou.aliyuncs.com/{{.namespace}}/
|
||||
lifecycle:
|
||||
preStop:
|
||||
exec:
|
||||
command: ["sh","-c","sleep 5"]
|
||||
ports:
|
||||
- containerPort: {{.port}}
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: {{.port}}
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: {{.port}}
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 20
|
||||
env:
|
||||
- name: aliyun_logs_k8slog
|
||||
value: "stdout"
|
||||
- name: aliyun_logs_k8slog_tags
|
||||
value: "stage={{.env}}"
|
||||
- name: aliyun_logs_k8slog_format
|
||||
value: "json"
|
||||
resources:
|
||||
limits:
|
||||
cpu: {{.limitCpu}}m
|
||||
memory: {{.limitMem}}Mi
|
||||
requests:
|
||||
cpu: {{.requestCpu}}m
|
||||
memory: {{.requestMem}}Mi
|
||||
command:
|
||||
- ./{{.serviceName}}
|
||||
- -f
|
||||
- ./{{.name}}.json
|
||||
volumeMounts:
|
||||
- name: timezone
|
||||
mountPath: /etc/localtime
|
||||
imagePullSecrets:
|
||||
- name: {{.namespace}}
|
||||
volumes:
|
||||
- name: timezone
|
||||
hostPath:
|
||||
path: /usr/share/zoneinfo/Asia/Shanghai
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{.name}}-svc
|
||||
namespace: {{.namespace}}
|
||||
spec:
|
||||
ports:
|
||||
- nodePort: 3{{.port}}
|
||||
port: {{.port}}
|
||||
protocol: TCP
|
||||
targetPort: {{.port}}
|
||||
selector:
|
||||
app: {{.name}}
|
||||
sessionAffinity: None
|
||||
type: NodePort{{if .envIsPreOrPro}}
|
||||
|
||||
---
|
||||
|
||||
apiVersion: autoscaling/v2beta1
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{.name}}-hpa-c
|
||||
namespace: {{.namespace}}
|
||||
labels:
|
||||
app: {{.name}}-hpa-c
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
name: di-api
|
||||
minReplicas: {{.minReplicas}}
|
||||
maxReplicas: {{.maxReplicas}}
|
||||
metrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
targetAverageUtilization: 80
|
||||
|
||||
---
|
||||
|
||||
apiVersion: autoscaling/v2beta1
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{.name}}-hpa-m
|
||||
namespace: {{.namespace}}
|
||||
labels:
|
||||
app: {{.name}}-hpa-m
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
name: {{.name}}
|
||||
minReplicas: {{.minReplicas}}
|
||||
maxReplicas: {{.maxReplicas}}
|
||||
metrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
targetAverageUtilization: 80{{end}}
|
||||
`
|
||||
@@ -1,46 +0,0 @@
|
||||
package k8s
|
||||
|
||||
var jobTmeplate = `apiVersion: batch/v1beta1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: {{.name}}
|
||||
namespace: {{.namespace}}
|
||||
spec:
|
||||
successfulJobsHistoryLimit: {{.successfulJobsHistoryLimit}}
|
||||
schedule: "{{.schedule}}"
|
||||
jobTemplate:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: {{.name}}
|
||||
image: registry-vpc.cn-hangzhou.aliyuncs.com/{{.namespace}}/
|
||||
env:
|
||||
- name: aliyun_logs_k8slog
|
||||
value: "stdout"
|
||||
- name: aliyun_logs_k8slog_tags
|
||||
value: "stage={{.env}}"
|
||||
- name: aliyun_logs_k8slog_format
|
||||
value: "json"
|
||||
resources:
|
||||
limits:
|
||||
cpu: {{.limitCpu}}m
|
||||
memory: {{.limitMem}}Mi
|
||||
requests:
|
||||
cpu: {{.requestCpu}}m
|
||||
memory: {{.requestMem}}Mi
|
||||
command:
|
||||
- ./{{.serviceName}}
|
||||
- -f
|
||||
- ./{{.name}}.json
|
||||
volumeMounts:
|
||||
- name: timezone
|
||||
mountPath: /etc/localtime
|
||||
imagePullSecrets:
|
||||
- name: {{.namespace}}
|
||||
restartPolicy: OnFailure
|
||||
volumes:
|
||||
- name: timezone
|
||||
hostPath:
|
||||
path: /usr/share/zoneinfo/Asia/Shanghai
|
||||
`
|
||||
@@ -1,103 +0,0 @@
|
||||
package k8s
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
const (
|
||||
ServiceTypeApi ServiceType = "api"
|
||||
ServiceTypeRpc ServiceType = "rpc"
|
||||
ServiceTypeJob ServiceType = "job"
|
||||
envDev = "dev"
|
||||
)
|
||||
|
||||
var errUnknownServiceType = errors.New("unknown service type")
|
||||
|
||||
type (
|
||||
ServiceType string
|
||||
|
||||
KubeRequest struct {
|
||||
Env string
|
||||
ServiceName string
|
||||
ServiceType ServiceType
|
||||
Namespace string
|
||||
Schedule string
|
||||
Replicas int
|
||||
RevisionHistoryLimit int
|
||||
Port int
|
||||
LimitCpu int
|
||||
LimitMem int
|
||||
RequestCpu int
|
||||
RequestMem int
|
||||
SuccessfulJobsHistoryLimit int
|
||||
HpaMinReplicas int
|
||||
HpaMaxReplicas int
|
||||
}
|
||||
)
|
||||
|
||||
func Gen(req KubeRequest) (string, error) {
|
||||
switch req.ServiceType {
|
||||
case ServiceTypeApi, ServiceTypeRpc:
|
||||
return genApiRpc(req)
|
||||
case ServiceTypeJob:
|
||||
return genJob(req)
|
||||
default:
|
||||
return "", errUnknownServiceType
|
||||
}
|
||||
}
|
||||
|
||||
func genApiRpc(req KubeRequest) (string, error) {
|
||||
t, err := template.New("api_rpc").Parse(apiRpcTmeplate)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buffer := new(bytes.Buffer)
|
||||
err = t.Execute(buffer, map[string]interface{}{
|
||||
"name": fmt.Sprintf("%s-%s", req.ServiceName, req.ServiceType),
|
||||
"namespace": req.Namespace,
|
||||
"replicas": req.Replicas,
|
||||
"revisionHistoryLimit": req.RevisionHistoryLimit,
|
||||
"port": req.Port,
|
||||
"limitCpu": req.LimitCpu,
|
||||
"limitMem": req.LimitMem,
|
||||
"requestCpu": req.RequestCpu,
|
||||
"requestMem": req.RequestMem,
|
||||
"serviceName": req.ServiceName,
|
||||
"env": req.Env,
|
||||
"envIsPreOrPro": req.Env != envDev,
|
||||
"envIsDev": req.Env == envDev,
|
||||
"minReplicas": req.HpaMinReplicas,
|
||||
"maxReplicas": req.HpaMaxReplicas,
|
||||
})
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
return buffer.String(), nil
|
||||
}
|
||||
|
||||
func genJob(req KubeRequest) (string, error) {
|
||||
t, err := template.New("job").Parse(jobTmeplate)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buffer := new(bytes.Buffer)
|
||||
err = t.Execute(buffer, map[string]interface{}{
|
||||
"name": fmt.Sprintf("%s-%s", req.ServiceName, req.ServiceType),
|
||||
"namespace": req.Namespace,
|
||||
"schedule": req.Schedule,
|
||||
"successfulJobsHistoryLimit": req.SuccessfulJobsHistoryLimit,
|
||||
"limitCpu": req.LimitCpu,
|
||||
"limitMem": req.LimitMem,
|
||||
"requestCpu": req.RequestCpu,
|
||||
"requestMem": req.RequestMem,
|
||||
"serviceName": req.ServiceName,
|
||||
"env": req.Env,
|
||||
})
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
return buffer.String(), nil
|
||||
}
|
||||
117
tools/goctl/kube/deployment.go
Normal file
117
tools/goctl/kube/deployment.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package kube
|
||||
|
||||
var deploymentTemplate = `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{.Name}}
|
||||
namespace: {{.Namespace}}
|
||||
labels:
|
||||
app: {{.Name}}
|
||||
spec:
|
||||
replicas: {{.Replicas}}
|
||||
revisionHistoryLimit: {{.Revisions}}
|
||||
selector:
|
||||
matchLabels:
|
||||
app: {{.Name}}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: {{.Name}}
|
||||
spec:
|
||||
containers:
|
||||
- name: {{.Name}}
|
||||
image: {{.Image}}
|
||||
lifecycle:
|
||||
preStop:
|
||||
exec:
|
||||
command: ["sh","-c","sleep 5"]
|
||||
ports:
|
||||
- containerPort: {{.Port}}
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: {{.Port}}
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: {{.Port}}
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 20
|
||||
resources:
|
||||
requests:
|
||||
cpu: {{.RequestCpu}}m
|
||||
memory: {{.RequestMem}}Mi
|
||||
limits:
|
||||
cpu: {{.LimitCpu}}m
|
||||
memory: {{.LimitMem}}Mi
|
||||
volumeMounts:
|
||||
- name: timezone
|
||||
mountPath: /etc/localtime
|
||||
imagePullSecrets:
|
||||
- name: {{.Secret}}
|
||||
volumes:
|
||||
- name: timezone
|
||||
hostPath:
|
||||
path: /usr/share/zoneinfo/Asia/Shanghai
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{.Name}}-svc
|
||||
namespace: {{.Namespace}}
|
||||
spec:
|
||||
ports:
|
||||
{{if .UseNodePort}}- nodePort: {{.NodePort}}
|
||||
port: {{.Port}}
|
||||
protocol: TCP
|
||||
targetPort: {{.Port}}
|
||||
type: NodePort{{else}}- port: {{.Port}}{{end}}
|
||||
selector:
|
||||
app: {{.Name}}
|
||||
|
||||
---
|
||||
|
||||
apiVersion: autoscaling/v2beta1
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{.Name}}-hpa-c
|
||||
namespace: {{.Namespace}}
|
||||
labels:
|
||||
app: {{.Name}}-hpa-c
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{.Name}}
|
||||
minReplicas: {{.MinReplicas}}
|
||||
maxReplicas: {{.MaxReplicas}}
|
||||
metrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
targetAverageUtilization: 80
|
||||
|
||||
---
|
||||
|
||||
apiVersion: autoscaling/v2beta1
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{.Name}}-hpa-m
|
||||
namespace: {{.Namespace}}
|
||||
labels:
|
||||
app: {{.Name}}-hpa-m
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{.Name}}
|
||||
minReplicas: {{.MinReplicas}}
|
||||
maxReplicas: {{.MaxReplicas}}
|
||||
metrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
targetAverageUtilization: 80
|
||||
`
|
||||
39
tools/goctl/kube/job.go
Normal file
39
tools/goctl/kube/job.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package kube
|
||||
|
||||
var jobTmeplate = `apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: {{.Name}}
|
||||
namespace: {{.Namespace}}
|
||||
spec:
|
||||
successfulJobsHistoryLimit: {{.SuccessfulJobsHistoryLimit}}
|
||||
schedule: "{{.Schedule}}"
|
||||
jobTemplate:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: {{.Name}}
|
||||
image: # todo image url
|
||||
resources:
|
||||
requests:
|
||||
cpu: {{.RequestCpu}}m
|
||||
memory: {{.RequestMem}}Mi
|
||||
limits:
|
||||
cpu: {{.LimitCpu}}m
|
||||
memory: {{.LimitMem}}Mi
|
||||
command:
|
||||
- ./{{.ServiceName}}
|
||||
- -f
|
||||
- ./{{.Name}}.yaml
|
||||
volumeMounts:
|
||||
- name: timezone
|
||||
mountPath: /etc/localtime
|
||||
imagePullSecrets:
|
||||
- name: # registry secret, if no, remove this
|
||||
restartPolicy: OnFailure
|
||||
volumes:
|
||||
- name: timezone
|
||||
hostPath:
|
||||
path: /usr/share/zoneinfo/Asia/Shanghai
|
||||
`
|
||||
104
tools/goctl/kube/kube.go
Normal file
104
tools/goctl/kube/kube.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package kube
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"text/template"
|
||||
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
const (
|
||||
category = "kube"
|
||||
deployTemplateFile = "deployment.tpl"
|
||||
jobTemplateFile = "job.tpl"
|
||||
basePort = 30000
|
||||
portLimit = 32767
|
||||
)
|
||||
|
||||
var errUnknownServiceType = errors.New("unknown service type")
|
||||
|
||||
type (
|
||||
ServiceType string
|
||||
|
||||
KubeRequest struct {
|
||||
Env string
|
||||
ServiceName string
|
||||
ServiceType ServiceType
|
||||
Namespace string
|
||||
Schedule string
|
||||
Replicas int
|
||||
RevisionHistoryLimit int
|
||||
Port int
|
||||
LimitCpu int
|
||||
LimitMem int
|
||||
RequestCpu int
|
||||
RequestMem int
|
||||
SuccessfulJobsHistoryLimit int
|
||||
HpaMinReplicas int
|
||||
HpaMaxReplicas int
|
||||
}
|
||||
|
||||
Deployment struct {
|
||||
Name string
|
||||
Namespace string
|
||||
Image string
|
||||
Secret string
|
||||
Replicas int
|
||||
Revisions int
|
||||
Port int
|
||||
NodePort int
|
||||
UseNodePort bool
|
||||
RequestCpu int
|
||||
RequestMem int
|
||||
LimitCpu int
|
||||
LimitMem int
|
||||
MinReplicas int
|
||||
MaxReplicas int
|
||||
}
|
||||
)
|
||||
|
||||
func DeploymentCommand(c *cli.Context) error {
|
||||
nodePort := c.Int("nodePort")
|
||||
// 0 to disable the nodePort type
|
||||
if nodePort != 0 && (nodePort < basePort || nodePort > portLimit) {
|
||||
return errors.New("nodePort should be between 30000 and 32767")
|
||||
}
|
||||
|
||||
text, err := util.LoadTemplate(category, deployTemplateFile, deploymentTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out, err := util.CreateIfNotExist(c.String("o"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
t := template.Must(template.New("deploymentTemplate").Parse(text))
|
||||
return t.Execute(out, Deployment{
|
||||
Name: c.String("name"),
|
||||
Namespace: c.String("namespace"),
|
||||
Image: c.String("image"),
|
||||
Secret: c.String("secret"),
|
||||
Replicas: c.Int("replicas"),
|
||||
Revisions: c.Int("revisions"),
|
||||
Port: c.Int("port"),
|
||||
NodePort: nodePort,
|
||||
UseNodePort: nodePort > 0,
|
||||
RequestCpu: c.Int("requestCpu"),
|
||||
RequestMem: c.Int("requestMem"),
|
||||
LimitCpu: c.Int("limitCpu"),
|
||||
LimitMem: c.Int("limitMem"),
|
||||
MinReplicas: c.Int("minReplicas"),
|
||||
MaxReplicas: c.Int("maxReplicas"),
|
||||
})
|
||||
}
|
||||
|
||||
func GenTemplates(_ *cli.Context) error {
|
||||
return util.InitTemplates(category, map[string]string{
|
||||
deployTemplateFile: deploymentTemplate,
|
||||
jobTemplateFile: jobTmeplate,
|
||||
})
|
||||
}
|
||||
@@ -7,7 +7,7 @@ goctl model 为go-zero下的工具模块中的组件之一,目前支持识别m
|
||||
* 通过ddl生成
|
||||
|
||||
```shell script
|
||||
goctl model mysql ddl -src="./*.sql" -dir="./sql/model" -c=true
|
||||
goctl model mysql ddl -src="./*.sql" -dir="./sql/model" -c
|
||||
```
|
||||
|
||||
执行上述命令后即可快速生成CURD代码。
|
||||
@@ -29,156 +29,191 @@ goctl model 为go-zero下的工具模块中的组件之一,目前支持识别m
|
||||
```go
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/tal-tech/go-zero/core/stores/cache"
|
||||
"github.com/tal-tech/go-zero/core/stores/sqlc"
|
||||
"github.com/tal-tech/go-zero/core/stores/sqlx"
|
||||
"github.com/tal-tech/go-zero/core/stringx"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/model/sql/builderx"
|
||||
)
|
||||
|
||||
var (
|
||||
userFieldNames = builderx.FieldNames(&User{})
|
||||
userRows = strings.Join(userFieldNames, ",")
|
||||
userRowsExpectAutoSet = strings.Join(stringx.Remove(userFieldNames, "id", "create_time", "update_time"), ",")
|
||||
userRowsWithPlaceHolder = strings.Join(stringx.Remove(userFieldNames, "id", "create_time", "update_time"), "=?,") + "=?"
|
||||
|
||||
cacheUserPrefix = "cache#User#user#"
|
||||
cacheUserNamePrefix = "cache#User#name#"
|
||||
cacheUserMobilePrefix = "cache#User#mobile#"
|
||||
cacheUserIdPrefix = "cache#User#id#"
|
||||
)
|
||||
|
||||
type (
|
||||
UserModel interface {
|
||||
Insert(data User) (sql.Result, error)
|
||||
FindOne(id int64) (*User, error)
|
||||
FindOneByUser(user string) (*User, error)
|
||||
FindOneByName(name string) (*User, error)
|
||||
FindOneByMobile(mobile string) (*User, error)
|
||||
Update(data User) error
|
||||
Delete(id int64) error
|
||||
}
|
||||
|
||||
defaultUserModel struct {
|
||||
sqlc.CachedConn
|
||||
table string
|
||||
}
|
||||
|
||||
User struct {
|
||||
Id int64 `db:"id"`
|
||||
User string `db:"user"` // 用户
|
||||
Name string `db:"name"` // 用户名称
|
||||
Password string `db:"password"` // 用户密码
|
||||
Mobile string `db:"mobile"` // 手机号
|
||||
Gender string `db:"gender"` // 男|女|未公开
|
||||
Nickname sql.NullString `db:"nickname"` // 用户昵称
|
||||
CreateTime sql.NullTime `db:"create_time"`
|
||||
UpdateTime time.Time `db:"update_time"`
|
||||
}
|
||||
)
|
||||
|
||||
func NewUserModel(conn sqlx.SqlConn, c cache.CacheConf) UserModel {
|
||||
return &defaultUserModel{
|
||||
CachedConn: sqlc.NewConn(conn, c),
|
||||
table: "user",
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultUserModel) Insert(data User) (sql.Result, error) {
|
||||
userKey := fmt.Sprintf("%s%v", cacheUserPrefix, data.User)
|
||||
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, data.Name)
|
||||
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile)
|
||||
ret, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?)", m.table, userRowsExpectAutoSet)
|
||||
return conn.Exec(query, data.User, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname)
|
||||
}, userMobileKey, userKey, userNameKey)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func (m *defaultUserModel) FindOne(id int64) (*User, error) {
|
||||
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
|
||||
var resp User
|
||||
err := m.QueryRow(&resp, userIdKey, func(conn sqlx.SqlConn, v interface{}) error {
|
||||
query := fmt.Sprintf("select %s from %s where id = ? limit 1", userRows, m.table)
|
||||
return conn.QueryRow(v, query, id)
|
||||
})
|
||||
switch err {
|
||||
case nil:
|
||||
return &resp, nil
|
||||
case sqlc.ErrNotFound:
|
||||
return nil, ErrNotFound
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultUserModel) FindOneByUser(user string) (*User, error) {
|
||||
userKey := fmt.Sprintf("%s%v", cacheUserPrefix, user)
|
||||
var resp User
|
||||
err := m.QueryRowIndex(&resp, userKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
|
||||
query := fmt.Sprintf("select %s from %s where user = ? limit 1", userRows, m.table)
|
||||
if err := conn.QueryRow(&resp, query, user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Id, nil
|
||||
}, m.queryPrimary)
|
||||
switch err {
|
||||
case nil:
|
||||
return &resp, nil
|
||||
case sqlc.ErrNotFound:
|
||||
return nil, ErrNotFound
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultUserModel) FindOneByName(name string) (*User, error) {
|
||||
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, name)
|
||||
var resp User
|
||||
err := m.QueryRowIndex(&resp, userNameKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
|
||||
query := fmt.Sprintf("select %s from %s where name = ? limit 1", userRows, m.table)
|
||||
if err := conn.QueryRow(&resp, query, name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Id, nil
|
||||
}, m.queryPrimary)
|
||||
switch err {
|
||||
case nil:
|
||||
return &resp, nil
|
||||
case sqlc.ErrNotFound:
|
||||
return nil, ErrNotFound
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultUserModel) FindOneByMobile(mobile string) (*User, error) {
|
||||
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, mobile)
|
||||
var resp User
|
||||
err := m.QueryRowIndex(&resp, userMobileKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
|
||||
query := fmt.Sprintf("select %s from %s where mobile = ? limit 1", userRows, m.table)
|
||||
if err := conn.QueryRow(&resp, query, mobile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Id, nil
|
||||
}, m.queryPrimary)
|
||||
switch err {
|
||||
case nil:
|
||||
return &resp, nil
|
||||
case sqlc.ErrNotFound:
|
||||
return nil, ErrNotFound
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultUserModel) Update(data User) error {
|
||||
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, data.Id)
|
||||
_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||
query := fmt.Sprintf("update %s set %s where id = ?", m.table, userRowsWithPlaceHolder)
|
||||
return conn.Exec(query, data.User, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname, data.Id)
|
||||
}, userIdKey)
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *defaultUserModel) Delete(id int64) error {
|
||||
data, err := m.FindOne(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
|
||||
userKey := fmt.Sprintf("%s%v", cacheUserPrefix, data.User)
|
||||
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, data.Name)
|
||||
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile)
|
||||
_, err = m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||
query := fmt.Sprintf("delete from %s where id = ?", m.table)
|
||||
return conn.Exec(query, id)
|
||||
}, userIdKey, userKey, userNameKey, userMobileKey)
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *defaultUserModel) formatPrimary(primary interface{}) string {
|
||||
return fmt.Sprintf("%s%v", cacheUserIdPrefix, primary)
|
||||
}
|
||||
|
||||
func (m *defaultUserModel) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error {
|
||||
query := fmt.Sprintf("select %s from %s where id = ? limit 1", userRows, m.table)
|
||||
return conn.QueryRow(v, query, primary)
|
||||
}
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/tal-tech/go-zero/core/stores/cache"
|
||||
"github.com/tal-tech/go-zero/core/stores/sqlc"
|
||||
"github.com/tal-tech/go-zero/core/stores/sqlx"
|
||||
"github.com/tal-tech/go-zero/core/stringx"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/model/sql/builderx"
|
||||
)
|
||||
|
||||
var (
|
||||
userFieldNames = builderx.FieldNames(&User{})
|
||||
userRows = strings.Join(userFieldNames, ",")
|
||||
userRowsExpectAutoSet = strings.Join(stringx.Remove(userFieldNames, "id", "create_time", "update_time"), ",")
|
||||
userRowsWithPlaceHolder = strings.Join(stringx.Remove(userFieldNames, "id", "create_time", "update_time"), "=?,") + "=?"
|
||||
|
||||
cacheUserIdPrefix = "cache#User#id#"
|
||||
cacheUserNamePrefix = "cache#User#name#"
|
||||
cacheUserMobilePrefix = "cache#User#mobile#"
|
||||
)
|
||||
|
||||
type (
|
||||
UserModel struct {
|
||||
sqlc.CachedConn
|
||||
table string
|
||||
}
|
||||
|
||||
User struct {
|
||||
Id int64 `db:"id"`
|
||||
Name string `db:"name"` // 用户名称
|
||||
Password string `db:"password"` // 用户密码
|
||||
Mobile string `db:"mobile"` // 手机号
|
||||
Gender string `db:"gender"` // 男|女|未公开
|
||||
Nickname string `db:"nickname"` // 用户昵称
|
||||
CreateTime time.Time `db:"create_time"`
|
||||
UpdateTime time.Time `db:"update_time"`
|
||||
}
|
||||
)
|
||||
|
||||
func NewUserModel(conn sqlx.SqlConn, c cache.CacheConf) *UserModel {
|
||||
return &UserModel{
|
||||
CachedConn: sqlc.NewConn(conn, c),
|
||||
table: "user",
|
||||
}
|
||||
}
|
||||
|
||||
func (m *UserModel) Insert(data User) (sql.Result, error) {
|
||||
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, data.Name)
|
||||
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile)
|
||||
ret, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?)", m.table, userRowsExpectAutoSet)
|
||||
return conn.Exec(query, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname)
|
||||
}, userNameKey, userMobileKey)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func (m *UserModel) FindOne(id int64) (*User, error) {
|
||||
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
|
||||
var resp User
|
||||
err := m.QueryRow(&resp, userIdKey, func(conn sqlx.SqlConn, v interface{}) error {
|
||||
query := fmt.Sprintf("select %s from %s where id = ? limit 1", userRows, m.table)
|
||||
return conn.QueryRow(v, query, id)
|
||||
})
|
||||
switch err {
|
||||
case nil:
|
||||
return &resp, nil
|
||||
case sqlc.ErrNotFound:
|
||||
return nil, ErrNotFound
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *UserModel) FindOneByName(name string) (*User, error) {
|
||||
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, name)
|
||||
var resp User
|
||||
err := m.QueryRowIndex(&resp, userNameKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
|
||||
query := fmt.Sprintf("select %s from %s where name = ? limit 1", userRows, m.table)
|
||||
if err := conn.QueryRow(&resp, query, name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Id, nil
|
||||
}, m.queryPrimary)
|
||||
switch err {
|
||||
case nil:
|
||||
return &resp, nil
|
||||
case sqlc.ErrNotFound:
|
||||
return nil, ErrNotFound
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *UserModel) FindOneByMobile(mobile string) (*User, error) {
|
||||
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, mobile)
|
||||
var resp User
|
||||
err := m.QueryRowIndex(&resp, userMobileKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
|
||||
query := fmt.Sprintf("select %s from %s where mobile = ? limit 1", userRows, m.table)
|
||||
if err := conn.QueryRow(&resp, query, mobile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Id, nil
|
||||
}, m.queryPrimary)
|
||||
switch err {
|
||||
case nil:
|
||||
return &resp, nil
|
||||
case sqlc.ErrNotFound:
|
||||
return nil, ErrNotFound
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *UserModel) Update(data User) error {
|
||||
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, data.Id)
|
||||
_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||
query := fmt.Sprintf("update %s set %s where id = ?", m.table, userRowsWithPlaceHolder)
|
||||
return conn.Exec(query, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname, data.Id)
|
||||
}, userIdKey)
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *UserModel) Delete(id int64) error {
|
||||
data, err := m.FindOne(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile)
|
||||
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
|
||||
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, data.Name)
|
||||
_, err = m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||
query := fmt.Sprintf("delete from %s where id = ?", m.table)
|
||||
return conn.Exec(query, id)
|
||||
}, userMobileKey, userIdKey, userNameKey)
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *UserModel) formatPrimary(primary interface{}) string {
|
||||
return fmt.Sprintf("%s%v", cacheUserIdPrefix, primary)
|
||||
}
|
||||
|
||||
func (m *UserModel) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error {
|
||||
query := fmt.Sprintf("select %s from %s where id = ? limit 1", userRows, m.table)
|
||||
return conn.QueryRow(v, query, primary)
|
||||
}
|
||||
```
|
||||
|
||||
## 用法
|
||||
@@ -211,25 +246,24 @@ OPTIONS:
|
||||
* ddl
|
||||
|
||||
```shell script
|
||||
goctl model mysql -src={patterns} -dir={dir} -cache=true
|
||||
goctl model mysql -src={patterns} -dir={dir} -cache
|
||||
```
|
||||
|
||||
help
|
||||
|
||||
```
|
||||
NAME:
|
||||
goctl model mysql ddl - generate mysql model from ddl
|
||||
|
||||
USAGE:
|
||||
goctl model mysql ddl [command options] [arguments...]
|
||||
|
||||
OPTIONS:
|
||||
--src value, -s value the path or path globbing patterns of the ddl
|
||||
--dir value, -d value the target dir
|
||||
--style value the file naming style, lower|camel|underline,default is lower
|
||||
--cache, -c generate code with cache [optional]
|
||||
--idea for idea plugin [optional]
|
||||
|
||||
goctl model mysql ddl - generate mysql model from ddl
|
||||
|
||||
USAGE:
|
||||
goctl model mysql ddl [command options] [arguments...]
|
||||
|
||||
OPTIONS:
|
||||
--src value, -s value the path or path globbing patterns of the ddl
|
||||
--dir value, -d value the target dir
|
||||
--style value the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]
|
||||
--cache, -c generate code with cache [optional]
|
||||
--idea for idea plugin [optional]
|
||||
```
|
||||
|
||||
* datasource
|
||||
@@ -242,18 +276,19 @@ OPTIONS:
|
||||
|
||||
```
|
||||
NAME:
|
||||
goctl model mysql datasource - generate model from datasource
|
||||
goctl model mysql datasource - generate model from datasource
|
||||
|
||||
USAGE:
|
||||
goctl model mysql datasource [command options] [arguments...]
|
||||
|
||||
OPTIONS:
|
||||
--url value the data source of database,like "root:password@tcp(127.0.0.1:3306)/database
|
||||
--table value, -t value the table or table globbing patterns in the database
|
||||
--cache, -c generate code with cache [optional]
|
||||
--dir value, -d value the target dir
|
||||
--style value the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]
|
||||
--idea for idea plugin [optional]
|
||||
|
||||
USAGE:
|
||||
goctl model mysql datasource [command options] [arguments...]
|
||||
|
||||
OPTIONS:
|
||||
--url value the data source of database,like "root:password@tcp(127.0.0.1:3306)/database
|
||||
--table value, -t value the table or table globbing patterns in the database
|
||||
--cache, -c generate code with cache [optional]
|
||||
--dir value, -d value the target dir
|
||||
--style value the file naming style, lower|camel|snake, default is lower
|
||||
--idea for idea plugin [optional]
|
||||
|
||||
```
|
||||
|
||||
@@ -281,13 +316,13 @@ OPTIONS:
|
||||
* ddl
|
||||
|
||||
```shell script
|
||||
goctl model -src={patterns} -dir={dir} -cache=false
|
||||
goctl model -src={patterns} -dir={dir}
|
||||
```
|
||||
|
||||
* datasource
|
||||
|
||||
```shell script
|
||||
goctl model mysql datasource -url={datasource} -table={patterns} -dir={dir} -cache=false
|
||||
goctl model mysql datasource -url={datasource} -table={patterns} -dir={dir}
|
||||
```
|
||||
|
||||
生成代码仅基本的CURD结构。
|
||||
@@ -312,3 +347,33 @@ OPTIONS:
|
||||
|
||||
目前,我认为除了基本的CURD外,其他的代码均属于<i>业务型</i>代码,这个我觉得开发人员根据业务需要进行编写更好。
|
||||
|
||||
# 类型转换规则
|
||||
| mysql dataType | golang dataType | golang dataType(if null&&default null) |
|
||||
|----------------|-----------------|----------------------------------------|
|
||||
| bool | int64 | sql.NullInt64 |
|
||||
| boolean | int64 | sql.NullInt64 |
|
||||
| tinyint | int64 | sql.NullInt64 |
|
||||
| smallint | int64 | sql.NullInt64 |
|
||||
| mediumint | int64 | sql.NullInt64 |
|
||||
| int | int64 | sql.NullInt64 |
|
||||
| integer | int64 | sql.NullInt64 |
|
||||
| bigint | int64 | sql.NullInt64 |
|
||||
| float | float64 | sql.NullFloat64 |
|
||||
| double | float64 | sql.NullFloat64 |
|
||||
| decimal | float64 | sql.NullFloat64 |
|
||||
| date | time.Time | sql.NullTime |
|
||||
| datetime | time.Time | sql.NullTime |
|
||||
| timestamp | time.Time | sql.NullTime |
|
||||
| time | string | sql.NullString |
|
||||
| year | time.Time | sql.NullInt64 |
|
||||
| char | string | sql.NullString |
|
||||
| varchar | string | sql.NullString |
|
||||
| binary | string | sql.NullString |
|
||||
| varbinary | string | sql.NullString |
|
||||
| tinytext | string | sql.NullString |
|
||||
| text | string | sql.NullString |
|
||||
| mediumtext | string | sql.NullString |
|
||||
| longtext | string | sql.NullString |
|
||||
| enum | string | sql.NullString |
|
||||
| set | string | sql.NullString |
|
||||
| json | string | sql.NullString |
|
||||
@@ -68,30 +68,3 @@ func FieldNames(in interface{}) []string {
|
||||
}
|
||||
return out
|
||||
}
|
||||
func FieldNamesAlias(in interface{}, alias string) []string {
|
||||
out := make([]string, 0)
|
||||
v := reflect.ValueOf(in)
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
// we only accept structs
|
||||
if v.Kind() != reflect.Struct {
|
||||
panic(fmt.Errorf("ToMap only accepts structs; got %T", v))
|
||||
}
|
||||
typ := v.Type()
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
// gets us a StructField
|
||||
fi := typ.Field(i)
|
||||
tagName := ""
|
||||
if tagv := fi.Tag.Get(dbTag); tagv != "" {
|
||||
tagName = tagv
|
||||
} else {
|
||||
tagName = fi.Name
|
||||
}
|
||||
if len(alias) > 0 {
|
||||
tagName = alias + "." + tagName
|
||||
}
|
||||
out = append(out, tagName)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -10,6 +9,7 @@ import (
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/tal-tech/go-zero/core/logx"
|
||||
"github.com/tal-tech/go-zero/core/stores/sqlx"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/config"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/model/sql/gen"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/model/sql/model"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/model/sql/util"
|
||||
@@ -17,14 +17,16 @@ import (
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var errNotMatched = errors.New("sql not matched")
|
||||
|
||||
const (
|
||||
flagSrc = "src"
|
||||
flagDir = "dir"
|
||||
flagCache = "cache"
|
||||
flagIdea = "idea"
|
||||
flagStyle = "style"
|
||||
flagUrl = "url"
|
||||
flagTable = "table"
|
||||
flagStyle = "style"
|
||||
)
|
||||
|
||||
func MysqlDDL(ctx *cli.Context) error {
|
||||
@@ -32,49 +34,64 @@ func MysqlDDL(ctx *cli.Context) error {
|
||||
dir := ctx.String(flagDir)
|
||||
cache := ctx.Bool(flagCache)
|
||||
idea := ctx.Bool(flagIdea)
|
||||
namingStyle := strings.TrimSpace(ctx.String(flagStyle))
|
||||
style := ctx.String(flagStyle)
|
||||
cfg, err := config.NewConfig(style)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return fromDDl(src, dir, cfg, cache, idea)
|
||||
}
|
||||
|
||||
func MyDataSource(ctx *cli.Context) error {
|
||||
url := strings.TrimSpace(ctx.String(flagUrl))
|
||||
dir := strings.TrimSpace(ctx.String(flagDir))
|
||||
cache := ctx.Bool(flagCache)
|
||||
idea := ctx.Bool(flagIdea)
|
||||
style := ctx.String(flagStyle)
|
||||
pattern := strings.TrimSpace(ctx.String(flagTable))
|
||||
cfg, err := config.NewConfig(style)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return fromDataSource(url, pattern, dir, cfg, cache, idea)
|
||||
}
|
||||
|
||||
func fromDDl(src, dir string, cfg *config.Config, cache, idea bool) error {
|
||||
log := console.NewConsole(idea)
|
||||
src = strings.TrimSpace(src)
|
||||
if len(src) == 0 {
|
||||
return errors.New("expected path or path globbing patterns, but nothing found")
|
||||
}
|
||||
|
||||
switch namingStyle {
|
||||
case gen.NamingLower, gen.NamingCamel, gen.NamingSnake:
|
||||
case "":
|
||||
namingStyle = gen.NamingLower
|
||||
default:
|
||||
return fmt.Errorf("unexpected naming style: %s", namingStyle)
|
||||
}
|
||||
|
||||
files, err := util.MatchFiles(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(files) == 0 {
|
||||
return errNotMatched
|
||||
}
|
||||
|
||||
var source []string
|
||||
for _, file := range files {
|
||||
data, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
source = append(source, string(data))
|
||||
}
|
||||
generator := gen.NewDefaultGenerator(strings.Join(source, "\n"), dir, namingStyle, gen.WithConsoleOption(log))
|
||||
err = generator.Start(cache)
|
||||
generator, err := gen.NewDefaultGenerator(dir, cfg, gen.WithConsoleOption(log))
|
||||
if err != nil {
|
||||
log.Error("%v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
err = generator.StartFromDDL(strings.Join(source, "\n"), cache)
|
||||
return err
|
||||
}
|
||||
|
||||
func MyDataSource(ctx *cli.Context) error {
|
||||
url := strings.TrimSpace(ctx.String(flagUrl))
|
||||
dir := strings.TrimSpace(ctx.String(flagDir))
|
||||
cache := ctx.Bool(flagCache)
|
||||
idea := ctx.Bool(flagIdea)
|
||||
namingStyle := strings.TrimSpace(ctx.String(flagStyle))
|
||||
pattern := strings.TrimSpace(ctx.String(flagTable))
|
||||
func fromDataSource(url, pattern, dir string, cfg *config.Config, cache, idea bool) error {
|
||||
log := console.NewConsole(idea)
|
||||
if len(url) == 0 {
|
||||
log.Error("%v", "expected data source of mysql, but nothing found")
|
||||
@@ -86,32 +103,22 @@ func MyDataSource(ctx *cli.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch namingStyle {
|
||||
case gen.NamingLower, gen.NamingCamel, gen.NamingSnake:
|
||||
case "":
|
||||
namingStyle = gen.NamingLower
|
||||
default:
|
||||
return fmt.Errorf("unexpected naming style: %s", namingStyle)
|
||||
}
|
||||
|
||||
cfg, err := mysql.ParseDSN(url)
|
||||
dsn, err := mysql.ParseDSN(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logx.Disable()
|
||||
conn := sqlx.NewMysql(url)
|
||||
databaseSource := strings.TrimSuffix(url, "/"+cfg.DBName) + "/information_schema"
|
||||
databaseSource := strings.TrimSuffix(url, "/"+dsn.DBName) + "/information_schema"
|
||||
db := sqlx.NewMysql(databaseSource)
|
||||
m := model.NewDDLModel(conn)
|
||||
im := model.NewInformationSchemaModel(db)
|
||||
|
||||
tables, err := im.GetAllTables(cfg.DBName)
|
||||
tables, err := im.GetAllTables(dsn.DBName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var matchTables []string
|
||||
matchTables := make(map[string][]*model.Column)
|
||||
for _, item := range tables {
|
||||
match, err := filepath.Match(pattern, item)
|
||||
if err != nil {
|
||||
@@ -121,24 +128,22 @@ func MyDataSource(ctx *cli.Context) error {
|
||||
if !match {
|
||||
continue
|
||||
}
|
||||
|
||||
matchTables = append(matchTables, item)
|
||||
columns, err := im.FindByTableName(dsn.DBName, item)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
matchTables[item] = columns
|
||||
}
|
||||
|
||||
if len(matchTables) == 0 {
|
||||
return errors.New("no tables matched")
|
||||
}
|
||||
|
||||
ddl, err := m.ShowDDL(matchTables...)
|
||||
generator, err := gen.NewDefaultGenerator(dir, cfg, gen.WithConsoleOption(log))
|
||||
if err != nil {
|
||||
log.Error("%v", err)
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
generator := gen.NewDefaultGenerator(strings.Join(ddl, "\n"), dir, namingStyle, gen.WithConsoleOption(log))
|
||||
err = generator.Start(cache)
|
||||
if err != nil {
|
||||
log.Error("%v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
err = generator.StartFromInformationSchema(dsn.DBName, matchTables, cache)
|
||||
return err
|
||||
}
|
||||
|
||||
71
tools/goctl/model/sql/command/command_test.go
Normal file
71
tools/goctl/model/sql/command/command_test.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/config"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||
)
|
||||
|
||||
var sql = "-- 用户表 --\nCREATE TABLE `user` (\n `id` bigint(10) NOT NULL AUTO_INCREMENT,\n `name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名称',\n `password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码',\n `mobile` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号',\n `gender` char(5) COLLATE utf8mb4_general_ci NOT NULL COMMENT '男|女|未公开',\n `nickname` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '用户昵称',\n `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n PRIMARY KEY (`id`),\n UNIQUE KEY `name_index` (`name`),\n UNIQUE KEY `mobile_index` (`mobile`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\n\n"
|
||||
var cfg = &config.Config{
|
||||
NamingFormat: "gozero",
|
||||
}
|
||||
|
||||
func TestFromDDl(t *testing.T) {
|
||||
err := fromDDl("./user.sql", t.TempDir(), cfg, true, false)
|
||||
assert.Equal(t, errNotMatched, err)
|
||||
|
||||
// case dir is not exists
|
||||
unknownDir := filepath.Join(t.TempDir(), "test", "user.sql")
|
||||
err = fromDDl(unknownDir, t.TempDir(), cfg, true, false)
|
||||
assert.True(t, func() bool {
|
||||
switch err.(type) {
|
||||
case *os.PathError:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}())
|
||||
|
||||
// case empty src
|
||||
err = fromDDl("", t.TempDir(), cfg, true, false)
|
||||
if err != nil {
|
||||
assert.Equal(t, "expected path or path globbing patterns, but nothing found", err.Error())
|
||||
}
|
||||
|
||||
tempDir := filepath.Join(t.TempDir(), "test")
|
||||
err = util.MkdirIfNotExist(tempDir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
user1Sql := filepath.Join(tempDir, "user1.sql")
|
||||
user2Sql := filepath.Join(tempDir, "user2.sql")
|
||||
|
||||
err = ioutil.WriteFile(user1Sql, []byte(sql), os.ModePerm)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(user2Sql, []byte(sql), os.ModePerm)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = os.Stat(user1Sql)
|
||||
assert.Nil(t, err)
|
||||
|
||||
_, err = os.Stat(user2Sql)
|
||||
assert.Nil(t, err)
|
||||
|
||||
err = fromDDl(filepath.Join(tempDir, "user*.sql"), tempDir, cfg, true, false)
|
||||
assert.Nil(t, err)
|
||||
|
||||
_, err = os.Stat(filepath.Join(tempDir, "usermodel.go"))
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
@@ -41,12 +41,34 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func ConvertDataType(dataBaseType string) (goDataType string, err error) {
|
||||
func ConvertDataType(dataBaseType string, isDefaultNull bool) (string, error) {
|
||||
tp, ok := commonMysqlDataTypeMap[strings.ToLower(dataBaseType)]
|
||||
if !ok {
|
||||
err = fmt.Errorf("unexpected database type: %s", dataBaseType)
|
||||
return
|
||||
return "", fmt.Errorf("unexpected database type: %s", dataBaseType)
|
||||
}
|
||||
|
||||
return mayConvertNullType(tp, isDefaultNull), nil
|
||||
}
|
||||
|
||||
func mayConvertNullType(goDataType string, isDefaultNull bool) string {
|
||||
if !isDefaultNull {
|
||||
return goDataType
|
||||
}
|
||||
|
||||
switch goDataType {
|
||||
case "int64":
|
||||
return "sql.NullInt64"
|
||||
case "int32":
|
||||
return "sql.NullInt32"
|
||||
case "float64":
|
||||
return "sql.NullFloat64"
|
||||
case "bool":
|
||||
return "sql.NullBool"
|
||||
case "string":
|
||||
return "sql.NullString"
|
||||
case "time.Time":
|
||||
return "sql.NullTime"
|
||||
default:
|
||||
return goDataType
|
||||
}
|
||||
goDataType = tp
|
||||
return
|
||||
}
|
||||
|
||||
@@ -7,14 +7,22 @@ import (
|
||||
)
|
||||
|
||||
func TestConvertDataType(t *testing.T) {
|
||||
v, err := ConvertDataType("tinyint")
|
||||
v, err := ConvertDataType("tinyint", false)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "int64", v)
|
||||
|
||||
v, err = ConvertDataType("timestamp")
|
||||
v, err = ConvertDataType("tinyint", true)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "sql.NullInt64", v)
|
||||
|
||||
v, err = ConvertDataType("timestamp", false)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "time.Time", v)
|
||||
|
||||
_, err = ConvertDataType("float32")
|
||||
v, err = ConvertDataType("timestamp", true)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "sql.NullTime", v)
|
||||
|
||||
_, err = ConvertDataType("float32", false)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# generate model with cache from ddl
|
||||
goctl model mysql ddl -src="./sql/*.sql" -dir="./sql/model/user" -c
|
||||
|
||||
# generate model with cache from data source
|
||||
#user=root
|
||||
#password=password
|
||||
#datasource=127.0.0.1:3306
|
||||
#database=test
|
||||
#goctl model mysql datasource -url="${user}:${password}@tcp(${datasource})/${database}" -table="*" -dir ./model
|
||||
15
tools/goctl/model/sql/example/makefile
Normal file
15
tools/goctl/model/sql/example/makefile
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
# generate model with cache from ddl
|
||||
fromDDL:
|
||||
goctl model mysql ddl -src="./sql/*.sql" -dir="./sql/model/user" -cache
|
||||
|
||||
|
||||
# generate model with cache from data source
|
||||
user=root
|
||||
password=password
|
||||
datasource=127.0.0.1:3306
|
||||
database=gozero
|
||||
|
||||
fromDataSource:
|
||||
goctl model mysql datasource -url="$(user):$(password)@tcp($(datasource))/$(database)" -table="*" -dir ./model/cache -c -style gozero
|
||||
@@ -1,6 +1,7 @@
|
||||
-- 用户表 --
|
||||
CREATE TABLE `user` (
|
||||
`id` bigint(10) NOT NULL AUTO_INCREMENT,
|
||||
`user` varchar(50) NOT NULL DEFAULT '' COMMENT '用户',
|
||||
`name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名称',
|
||||
`password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码',
|
||||
`mobile` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号',
|
||||
@@ -10,6 +11,16 @@ CREATE TABLE `user` (
|
||||
`update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `name_index` (`name`),
|
||||
UNIQUE KEY `user_index` (`user`),
|
||||
UNIQUE KEY `mobile_index` (`mobile`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
CREATE TABLE `student` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
|
||||
`age` tinyint DEFAULT NULL,
|
||||
`score` float(10,0) DEFAULT NULL,
|
||||
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`update_time` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
@@ -1,15 +0,0 @@
|
||||
-- 用户表 --
|
||||
CREATE TABLE `user1` (
|
||||
`id` bigint(10) NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名称',
|
||||
`password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码',
|
||||
`mobile` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号',
|
||||
`gender` char(5) COLLATE utf8mb4_general_ci NOT NULL COMMENT '男|女|未公开',
|
||||
`nickname` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '用户昵称',
|
||||
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `name_index` (`name`),
|
||||
UNIQUE KEY `mobile_index` (`mobile`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
|
||||
)
|
||||
|
||||
func genDelete(table Table, withCache bool) (string, error) {
|
||||
func genDelete(table Table, withCache bool) (string, string, error) {
|
||||
keySet := collection.NewSet()
|
||||
keyVariableSet := collection.NewSet()
|
||||
for fieldName, key := range table.CacheKey {
|
||||
@@ -24,7 +24,7 @@ func genDelete(table Table, withCache bool) (string, error) {
|
||||
camel := table.Name.ToCamel()
|
||||
text, err := util.LoadTemplate(category, deleteTemplateFile, template.Delete)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
output, err := util.With("delete").
|
||||
@@ -33,14 +33,30 @@ func genDelete(table Table, withCache bool) (string, error) {
|
||||
"upperStartCamelObject": camel,
|
||||
"withCache": withCache,
|
||||
"containsIndexCache": table.ContainsUniqueKey,
|
||||
"lowerStartCamelPrimaryKey": stringx.From(table.PrimaryKey.Name.ToCamel()).UnTitle(),
|
||||
"lowerStartCamelPrimaryKey": stringx.From(table.PrimaryKey.Name.ToCamel()).Untitle(),
|
||||
"dataType": table.PrimaryKey.DataType,
|
||||
"keys": strings.Join(keySet.KeysStr(), "\n"),
|
||||
"originalPrimaryKey": table.PrimaryKey.Name.Source(),
|
||||
"keyValues": strings.Join(keyVariableSet.KeysStr(), ", "),
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", "", err
|
||||
}
|
||||
return output.String(), nil
|
||||
|
||||
// interface method
|
||||
text, err = util.LoadTemplate(category, deleteMethodTemplateFile, template.DeleteMethod)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
deleteMethodOut, err := util.With("deleteMethod").
|
||||
Parse(text).
|
||||
Execute(map[string]interface{}{
|
||||
"lowerStartCamelPrimaryKey": stringx.From(table.PrimaryKey.Name.ToCamel()).Untitle(),
|
||||
"dataType": table.PrimaryKey.DataType,
|
||||
})
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return output.String(), deleteMethodOut.String(), nil
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ func genFields(fields []parser.Field) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
list = append(list, result)
|
||||
}
|
||||
return strings.Join(list, "\n"), nil
|
||||
@@ -43,5 +44,6 @@ func genField(field parser.Field) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return output.String(), nil
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
|
||||
)
|
||||
|
||||
func genFindOne(table Table, withCache bool) (string, error) {
|
||||
func genFindOne(table Table, withCache bool) (string, string, error) {
|
||||
camel := table.Name.ToCamel()
|
||||
text, err := util.LoadTemplate(category, findOneTemplateFile, template.FindOne)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
output, err := util.With("findOne").
|
||||
@@ -18,15 +18,31 @@ func genFindOne(table Table, withCache bool) (string, error) {
|
||||
Execute(map[string]interface{}{
|
||||
"withCache": withCache,
|
||||
"upperStartCamelObject": camel,
|
||||
"lowerStartCamelObject": stringx.From(camel).UnTitle(),
|
||||
"lowerStartCamelObject": stringx.From(camel).Untitle(),
|
||||
"originalPrimaryKey": table.PrimaryKey.Name.Source(),
|
||||
"lowerStartCamelPrimaryKey": stringx.From(table.PrimaryKey.Name.ToCamel()).UnTitle(),
|
||||
"lowerStartCamelPrimaryKey": stringx.From(table.PrimaryKey.Name.ToCamel()).Untitle(),
|
||||
"dataType": table.PrimaryKey.DataType,
|
||||
"cacheKey": table.CacheKey[table.PrimaryKey.Name.Source()].KeyExpression,
|
||||
"cacheKeyVariable": table.CacheKey[table.PrimaryKey.Name.Source()].Variable,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", "", err
|
||||
}
|
||||
return output.String(), nil
|
||||
|
||||
text, err = util.LoadTemplate(category, findOneMethodTemplateFile, template.FindOneMethod)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
findOneMethod, err := util.With("findOneMethod").
|
||||
Parse(text).
|
||||
Execute(map[string]interface{}{
|
||||
"upperStartCamelObject": camel,
|
||||
"lowerStartCamelPrimaryKey": stringx.From(table.PrimaryKey.Name.ToCamel()).Untitle(),
|
||||
"dataType": table.PrimaryKey.DataType,
|
||||
})
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return output.String(), findOneMethod.String(), nil
|
||||
}
|
||||
|
||||
@@ -9,10 +9,16 @@ import (
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
|
||||
)
|
||||
|
||||
func genFindOneByField(table Table, withCache bool) (string, string, error) {
|
||||
type findOneCode struct {
|
||||
findOneMethod string
|
||||
findOneInterfaceMethod string
|
||||
cacheExtra string
|
||||
}
|
||||
|
||||
func genFindOneByField(table Table, withCache bool) (*findOneCode, error) {
|
||||
text, err := util.LoadTemplate(category, findOneByFieldTemplateFile, template.FindOneByField)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t := util.With("findOneByField").Parse(text)
|
||||
@@ -26,39 +32,71 @@ func genFindOneByField(table Table, withCache bool) (string, string, error) {
|
||||
output, err := t.Execute(map[string]interface{}{
|
||||
"upperStartCamelObject": camelTableName,
|
||||
"upperField": camelFieldName,
|
||||
"in": fmt.Sprintf("%s %s", stringx.From(camelFieldName).UnTitle(), field.DataType),
|
||||
"in": fmt.Sprintf("%s %s", stringx.From(camelFieldName).Untitle(), field.DataType),
|
||||
"withCache": withCache,
|
||||
"cacheKey": table.CacheKey[field.Name.Source()].KeyExpression,
|
||||
"cacheKeyVariable": table.CacheKey[field.Name.Source()].Variable,
|
||||
"lowerStartCamelObject": stringx.From(camelTableName).UnTitle(),
|
||||
"lowerStartCamelField": stringx.From(camelFieldName).UnTitle(),
|
||||
"lowerStartCamelObject": stringx.From(camelTableName).Untitle(),
|
||||
"lowerStartCamelField": stringx.From(camelFieldName).Untitle(),
|
||||
"upperStartCamelPrimaryKey": table.PrimaryKey.Name.ToCamel(),
|
||||
"originalField": field.Name.Source(),
|
||||
})
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
list = append(list, output.String())
|
||||
}
|
||||
|
||||
text, err = util.LoadTemplate(category, findOneByFieldMethodTemplateFile, template.FindOneByFieldMethod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t = util.With("findOneByFieldMethod").Parse(text)
|
||||
var listMethod []string
|
||||
for _, field := range table.Fields {
|
||||
if field.IsPrimaryKey || !field.IsUniqueKey {
|
||||
continue
|
||||
}
|
||||
camelFieldName := field.Name.ToCamel()
|
||||
output, err := t.Execute(map[string]interface{}{
|
||||
"upperStartCamelObject": camelTableName,
|
||||
"upperField": camelFieldName,
|
||||
"in": fmt.Sprintf("%s %s", stringx.From(camelFieldName).Untitle(), field.DataType),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
listMethod = append(listMethod, output.String())
|
||||
}
|
||||
|
||||
if withCache {
|
||||
text, err := util.LoadTemplate(category, findOneByFieldExtraMethodTemplateFile, template.FindOneByFieldExtraMethod)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out, err := util.With("findOneByFieldExtraMethod").Parse(text).Execute(map[string]interface{}{
|
||||
"upperStartCamelObject": camelTableName,
|
||||
"primaryKeyLeft": table.CacheKey[table.PrimaryKey.Name.Source()].Left,
|
||||
"lowerStartCamelObject": stringx.From(camelTableName).UnTitle(),
|
||||
"lowerStartCamelObject": stringx.From(camelTableName).Untitle(),
|
||||
"originalPrimaryField": table.PrimaryKey.Name.Source(),
|
||||
})
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return strings.Join(list, "\n"), out.String(), nil
|
||||
return &findOneCode{
|
||||
findOneMethod: strings.Join(list, util.NL),
|
||||
findOneInterfaceMethod: strings.Join(listMethod, util.NL),
|
||||
cacheExtra: out.String(),
|
||||
}, nil
|
||||
}
|
||||
return strings.Join(list, "\n"), "", nil
|
||||
|
||||
return &findOneCode{
|
||||
findOneMethod: strings.Join(list, util.NL),
|
||||
findOneInterfaceMethod: strings.Join(listMethod, util.NL),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -7,10 +7,14 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/tal-tech/go-zero/tools/goctl/config"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/model/sql/model"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/model/sql/parser"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/model/sql/template"
|
||||
modelutil "github.com/tal-tech/go-zero/tools/goctl/model/sql/util"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util/console"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util/format"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
|
||||
)
|
||||
|
||||
@@ -24,27 +28,39 @@ const (
|
||||
|
||||
type (
|
||||
defaultGenerator struct {
|
||||
source string
|
||||
dir string
|
||||
//source string
|
||||
dir string
|
||||
console.Console
|
||||
pkg string
|
||||
namingStyle string
|
||||
pkg string
|
||||
cfg *config.Config
|
||||
}
|
||||
Option func(generator *defaultGenerator)
|
||||
)
|
||||
|
||||
func NewDefaultGenerator(source, dir, namingStyle string, opt ...Option) *defaultGenerator {
|
||||
func NewDefaultGenerator(dir string, cfg *config.Config, opt ...Option) (*defaultGenerator, error) {
|
||||
if dir == "" {
|
||||
dir = pwd
|
||||
}
|
||||
generator := &defaultGenerator{source: source, dir: dir, namingStyle: namingStyle}
|
||||
dirAbs, err := filepath.Abs(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dir = dirAbs
|
||||
pkg := filepath.Base(dirAbs)
|
||||
err = util.MkdirIfNotExist(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
generator := &defaultGenerator{dir: dir, cfg: cfg, pkg: pkg}
|
||||
var optionList []Option
|
||||
optionList = append(optionList, newDefaultOption())
|
||||
optionList = append(optionList, opt...)
|
||||
for _, fn := range optionList {
|
||||
fn(generator)
|
||||
}
|
||||
return generator
|
||||
return generator, nil
|
||||
}
|
||||
|
||||
func WithConsoleOption(c console.Console) Option {
|
||||
@@ -59,31 +75,54 @@ func newDefaultOption() Option {
|
||||
}
|
||||
}
|
||||
|
||||
func (g *defaultGenerator) Start(withCache bool) error {
|
||||
func (g *defaultGenerator) StartFromDDL(source string, withCache bool) error {
|
||||
modelList, err := g.genFromDDL(source, withCache)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return g.createFile(modelList)
|
||||
}
|
||||
|
||||
func (g *defaultGenerator) StartFromInformationSchema(db string, columns map[string][]*model.Column, withCache bool) error {
|
||||
m := make(map[string]string)
|
||||
for tableName, column := range columns {
|
||||
table, err := parser.ConvertColumn(db, tableName, column)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
code, err := g.genModel(*table, withCache)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m[table.Name.Source()] = code
|
||||
}
|
||||
return g.createFile(m)
|
||||
}
|
||||
|
||||
func (g *defaultGenerator) createFile(modelList map[string]string) error {
|
||||
dirAbs, err := filepath.Abs(g.dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
g.dir = dirAbs
|
||||
g.pkg = filepath.Base(dirAbs)
|
||||
err = util.MkdirIfNotExist(dirAbs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
modelList, err := g.genFromDDL(withCache)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for tableName, code := range modelList {
|
||||
tn := stringx.From(tableName)
|
||||
name := fmt.Sprintf("%smodel.go", strings.ToLower(tn.ToCamel()))
|
||||
switch g.namingStyle {
|
||||
case NamingCamel:
|
||||
name = fmt.Sprintf("%sModel.go", tn.ToCamel())
|
||||
case NamingSnake:
|
||||
name = fmt.Sprintf("%s_model.go", tn.ToSnake())
|
||||
modelFilename, err := format.FileNamingFormat(g.cfg.NamingFormat, fmt.Sprintf("%s_model", tn.Source()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name := modelFilename + ".go"
|
||||
filename := filepath.Join(dirAbs, name)
|
||||
if util.FileExists(filename) {
|
||||
g.Warning("%s already exists, ignored.", name)
|
||||
@@ -95,7 +134,12 @@ func (g *defaultGenerator) Start(withCache bool) error {
|
||||
}
|
||||
}
|
||||
// generate error file
|
||||
filename := filepath.Join(dirAbs, "vars.go")
|
||||
varFilename, err := format.FileNamingFormat(g.cfg.NamingFormat, "vars")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filename := filepath.Join(dirAbs, varFilename+".go")
|
||||
text, err := util.LoadTemplate(category, errTemplateFile, template.Error)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -113,8 +157,8 @@ func (g *defaultGenerator) Start(withCache bool) error {
|
||||
}
|
||||
|
||||
// ret1: key-table name,value-code
|
||||
func (g *defaultGenerator) genFromDDL(withCache bool) (map[string]string, error) {
|
||||
ddlList := g.split()
|
||||
func (g *defaultGenerator) genFromDDL(source string, withCache bool) (map[string]string, error) {
|
||||
ddlList := g.split(source)
|
||||
m := make(map[string]string)
|
||||
for _, ddl := range ddlList {
|
||||
table, err := parser.Parse(ddl)
|
||||
@@ -139,10 +183,15 @@ type (
|
||||
)
|
||||
|
||||
func (g *defaultGenerator) genModel(in parser.Table, withCache bool) (string, error) {
|
||||
if len(in.PrimaryKey.Name.Source()) == 0 {
|
||||
return "", fmt.Errorf("table %s: missing primary key", in.Name.Source())
|
||||
}
|
||||
|
||||
text, err := util.LoadTemplate(category, modelTemplateFile, template.Model)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
t := util.With("model").
|
||||
Parse(text).
|
||||
GoFmt(true)
|
||||
@@ -174,39 +223,41 @@ func (g *defaultGenerator) genModel(in parser.Table, withCache bool) (string, er
|
||||
return "", err
|
||||
}
|
||||
|
||||
typesCode, err := genTypes(table, withCache)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
newCode, err := genNew(table, withCache)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
insertCode, err := genInsert(table, withCache)
|
||||
insertCode, insertCodeMethod, err := genInsert(table, withCache)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var findCode = make([]string, 0)
|
||||
findOneCode, err := genFindOne(table, withCache)
|
||||
findOneCode, findOneCodeMethod, err := genFindOne(table, withCache)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
findOneByFieldCode, extraMethod, err := genFindOneByField(table, withCache)
|
||||
ret, err := genFindOneByField(table, withCache)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
findCode = append(findCode, findOneCode, findOneByFieldCode)
|
||||
updateCode, err := genUpdate(table, withCache)
|
||||
findCode = append(findCode, findOneCode, ret.findOneMethod)
|
||||
updateCode, updateCodeMethod, err := genUpdate(table, withCache)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
deleteCode, err := genDelete(table, withCache)
|
||||
deleteCode, deleteCodeMethod, err := genDelete(table, withCache)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var list []string
|
||||
list = append(list, insertCodeMethod, findOneCodeMethod, ret.findOneInterfaceMethod, updateCodeMethod, deleteCodeMethod)
|
||||
typesCode, err := genTypes(table, strings.Join(modelutil.TrimStringSlice(list), util.NL), withCache)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
newCode, err := genNew(table, withCache)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -221,7 +272,7 @@ func (g *defaultGenerator) genModel(in parser.Table, withCache bool) (string, er
|
||||
"find": strings.Join(findCode, "\n"),
|
||||
"update": updateCode,
|
||||
"delete": deleteCode,
|
||||
"extraMethod": extraMethod,
|
||||
"extraMethod": ret.cacheExtra,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tal-tech/go-zero/core/logx"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/config"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -22,15 +23,23 @@ func TestCacheModel(t *testing.T) {
|
||||
defer func() {
|
||||
_ = os.RemoveAll(dir)
|
||||
}()
|
||||
g := NewDefaultGenerator(source, cacheDir, NamingLower)
|
||||
err := g.Start(true)
|
||||
g, err := NewDefaultGenerator(cacheDir, &config.Config{
|
||||
NamingFormat: "GoZero",
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
|
||||
err = g.StartFromDDL(source, true)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, func() bool {
|
||||
_, err := os.Stat(filepath.Join(cacheDir, "testuserinfomodel.go"))
|
||||
_, err := os.Stat(filepath.Join(cacheDir, "TestUserInfoModel.go"))
|
||||
return err == nil
|
||||
}())
|
||||
g = NewDefaultGenerator(source, noCacheDir, NamingLower)
|
||||
err = g.Start(false)
|
||||
g, err = NewDefaultGenerator(noCacheDir, &config.Config{
|
||||
NamingFormat: "gozero",
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
|
||||
err = g.StartFromDDL(source, false)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, func() bool {
|
||||
_, err := os.Stat(filepath.Join(noCacheDir, "testuserinfomodel.go"))
|
||||
@@ -47,15 +56,23 @@ func TestNamingModel(t *testing.T) {
|
||||
defer func() {
|
||||
_ = os.RemoveAll(dir)
|
||||
}()
|
||||
g := NewDefaultGenerator(source, camelDir, NamingCamel)
|
||||
err := g.Start(true)
|
||||
g, err := NewDefaultGenerator(camelDir, &config.Config{
|
||||
NamingFormat: "GoZero",
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
|
||||
err = g.StartFromDDL(source, true)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, func() bool {
|
||||
_, err := os.Stat(filepath.Join(camelDir, "TestUserInfoModel.go"))
|
||||
return err == nil
|
||||
}())
|
||||
g = NewDefaultGenerator(source, snakeDir, NamingSnake)
|
||||
err = g.Start(true)
|
||||
g, err = NewDefaultGenerator(snakeDir, &config.Config{
|
||||
NamingFormat: "go_zero",
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
|
||||
err = g.StartFromDDL(source, true)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, func() bool {
|
||||
_, err := os.Stat(filepath.Join(snakeDir, "test_user_info_model.go"))
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
|
||||
)
|
||||
|
||||
func genInsert(table Table, withCache bool) (string, error) {
|
||||
func genInsert(table Table, withCache bool) (string, string, error) {
|
||||
keySet := collection.NewSet()
|
||||
keyVariableSet := collection.NewSet()
|
||||
for fieldName, key := range table.CacheKey {
|
||||
@@ -36,7 +36,7 @@ func genInsert(table Table, withCache bool) (string, error) {
|
||||
camel := table.Name.ToCamel()
|
||||
text, err := util.LoadTemplate(category, insertTemplateFile, template.Insert)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
output, err := util.With("insert").
|
||||
@@ -45,15 +45,30 @@ func genInsert(table Table, withCache bool) (string, error) {
|
||||
"withCache": withCache,
|
||||
"containsIndexCache": table.ContainsUniqueKey,
|
||||
"upperStartCamelObject": camel,
|
||||
"lowerStartCamelObject": stringx.From(camel).UnTitle(),
|
||||
"lowerStartCamelObject": stringx.From(camel).Untitle(),
|
||||
"expression": strings.Join(expressions, ", "),
|
||||
"expressionValues": strings.Join(expressionValues, ", "),
|
||||
"keys": strings.Join(keySet.KeysStr(), "\n"),
|
||||
"keyValues": strings.Join(keyVariableSet.KeysStr(), ", "),
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return output.String(), nil
|
||||
// interface method
|
||||
text, err = util.LoadTemplate(category, insertTemplateMethodFile, template.InsertMethod)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
insertMethodOutput, err := util.With("insertMethod").
|
||||
Parse(text).
|
||||
Execute(map[string]interface{}{
|
||||
"upperStartCamelObject": camel,
|
||||
})
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return output.String(), insertMethodOutput.String(), nil
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user