Bladeren bron

project: 牧场管理&& æ

Yi 1 jaar geleden
bovenliggende
commit
bb00b6a90f
76 gewijzigde bestanden met toevoegingen van 3842 en 818 verwijderingen
  1. 21 0
      Makefile
  2. 116 0
      backend/common/errors.proto
  3. 14 0
      backend/operation/enum.proto
  4. 33 0
      backend/operation/pasture.proto
  5. 14 0
      backend/operation/system.proto
  6. 2 2
      cmd/http.go
  7. 5 6
      config/app.go
  8. 6 3
      config/app.test.yaml
  9. 5 1
      dep/dep.go
  10. 1 1
      dep/di.go
  11. 4 1
      dep/di_http.go
  12. 27 1
      go.mod
  13. 44 705
      go.sum
  14. 2 6
      http/handler/default.go
  15. 39 0
      http/handler/pasture/pasture.go
  16. 13 0
      http/handler/system/role.go
  17. 13 0
      http/handler/system/user.go
  18. 11 0
      http/middleware/hub.go
  19. 2 2
      http/middleware/sentry.go
  20. 2 2
      http/route/api_debug_route.go
  21. 12 7
      http/route/app_api.go
  22. 53 0
      logger/logrus-2023-05-06.log
  23. 3 0
      logger/zap-2023-05-06.log
  24. 3 3
      main.go
  25. 0 17
      model/event_fileds.go
  26. 85 0
      model/group_pasture.go
  27. 0 19
      model/kpe_event.go
  28. 0 19
      model/kpt_fileds.go
  29. 24 0
      model/system_menu.go
  30. 19 0
      model/system_role.go
  31. 20 0
      model/system_user.go
  32. 44 0
      module/backend/interface.go
  33. 48 0
      module/backend/service.go
  34. 207 0
      pkg/apierr/apierr.go
  35. 761 0
      pkg/apierr/apierr_gen.go
  36. 47 0
      pkg/apierr/config.go
  37. 23 0
      pkg/apiok/common.go
  38. 5 5
      pkg/cleanup/entry.go
  39. 0 0
      pkg/cleanup/entry_test.go
  40. 0 0
      pkg/cputil/cp.go
  41. 0 0
      pkg/cputil/cp_test.go
  42. 0 0
      pkg/di/annotation.go
  43. 3 3
      pkg/di/hub.go
  44. 0 0
      pkg/di/hub_test.go
  45. 1 1
      pkg/di/option.go
  46. 0 0
      pkg/di/xreflect/reflect.go
  47. 0 0
      pkg/di/xreflect/reflect_test.go
  48. 0 0
      pkg/di/xreflect/stack.go
  49. 0 0
      pkg/di/xreflect/stack_test.go
  50. 57 0
      pkg/grpcutil/client_conn.go
  51. 61 0
      pkg/grpcutil/error.go
  52. 22 0
      pkg/grpcutil/grpc_error.go
  53. 16 4
      pkg/logger/logrus/log.go
  54. 84 0
      pkg/logger/zaplog/log.go
  55. 0 0
      pkg/runtimeutil/caller.go
  56. 0 0
      pkg/runtimeutil/caller_test.go
  57. 1 1
      pkg/sentry/sentry.go
  58. 244 0
      pkg/tool/tool.go
  59. 124 0
      pkg/tool/tool_test.go
  60. 2 2
      pkg/xerr/custom_error.go
  61. 0 0
      pkg/xerr/custom_error_test.go
  62. 0 0
      pkg/xerr/errors.go
  63. 0 0
      pkg/xerr/errors_test.go
  64. 0 0
      pkg/xerr/stack.go
  65. 0 0
      pkg/xerr/stack_test.go
  66. 3 7
      pkg/xerr/xerr.go
  67. 0 0
      pkg/xerr/xerr_test.go
  68. 187 0
      proto/go/backend/common/enum.pb.go
  69. 365 0
      proto/go/backend/common/errors.pb.go
  70. 188 0
      proto/go/backend/operation/enum.pb.go
  71. 427 0
      proto/go/backend/operation/pasture.pb.go
  72. 179 0
      proto/go/backend/operation/system.pb.go
  73. 41 0
      scripts/consumer-images.sh
  74. 41 0
      scripts/http-images.sh
  75. 6 0
      scripts/proto.sh
  76. 62 0
      store/kptstore/rw_store.go

+ 21 - 0
Makefile

@@ -0,0 +1,21 @@
+GO_FILES=`go list ./... | grep -v -E "mock|store|test|fake|cmd|bin|backend|google|logger|proto"`
+
+proto-build:
+	protoc -I=. --go_out=:./proto/go/ --go_opt=paths=source_relative \
+    --go-grpc_out=:./proto/go/ --go-grpc_opt=paths=source_relative ./backend/common/*.proto
+
+	protoc -I=. --go_out=:./proto/go/ --go_opt=paths=source_relative \
+    --go-grpc_out=:./proto/go/ --go-grpc_opt=paths=source_relative ./backend/operation/*.proto
+
+ci-test:
+	go test $(GO_FILES) -coverprofile .cover.txt
+	go tool cover -func .cover.txt
+	rm .cover.txt
+
+lint:
+	golangci-lint run ./...
+
+build:
+	rm -rf bin
+	mkdir -p bin
+	GOARCH=amd64 GOOS=linux CGO_ENABLED=0 go build -o bin/kptEvent -ldflags "-X kpt.kptyun.cn:3000/kpt-event/pod.appVersion=${version}" main.go

+ 116 - 0
backend/common/errors.proto

@@ -0,0 +1,116 @@
+syntax = "proto3";
+
+package backend.common;
+
+option go_package = ".;commonPb";
+
+message Error {
+  enum Code {
+    OK = 0;
+
+    reserved 1 to 9999;
+
+    // ========= Common =========
+
+    // 鉴权
+    UNAUTHORIZED = 10000;
+    reserved 10001 to 10999;
+
+    // 通用请求错误
+    BAD_REQUEST = 11000;
+    INVALID_CONTENT_TYPE = 11001;
+    INVALID_CONTENT_ENCODING = 11002;
+    TOO_MANY_REQUESTS = 11003;
+    reserved 11004 to 11099;
+
+    // ========= Biz =========
+
+    // Config
+    INVALID_STORAGE_TYPE = 11100;
+    reserved 11101 to 11199;
+
+    // DataEvent
+    INVALID_DE_DATA = 11200;
+    reserved 11201 to 19999;
+
+    // Checkin
+    CHECKIN_REPEATED = 20000;
+    reserved 20001 to 20999;
+
+    // Course
+    COURSE_NOT_FOUND = 21000;
+    // 课程没有权益
+    COURSE_NOT_INTEREST = 21001;
+    reserved 21002 to 21999;
+
+    // Recommend
+    MODULE_NOT_FOUND = 22000;
+    reserved 22001 to 22999;
+
+    // User Course
+    USER_COURSE_ALREADY_ADDED = 23000;
+    USER_COURSE_NOT_FOUND = 23001;
+    reserved 23002 to 23999;
+
+    // PT
+    PT_LIMITED = 24000;
+    reserved 24001 to 24099;
+
+    // Payment 24100 - 24299
+
+    // 无效的价格
+    INVALID_PRICE = 24100;
+    // 无效的 product ID
+    INVALID_PRODUCT_ID = 24101;
+    // 无效的订单号
+    INVALID_ORDER_NUMBER = 24102;
+    // 无效的用户 ID
+    INVALID_USER_ID = 24103;
+    // 无效的收据
+    INVALID_RECEIPT = 24104;
+    // 异常的 iOS 收据,需要客户端 check 和重试
+    EMPTY_IOS_RECEIPT = 24105;
+
+    reserved 24106 to 24299;
+
+    // UserPlan 相关
+    // 免费用户限制 plan 课程数量
+    USER_PLAN_LIMITED_COURSE_COUNT = 24300;
+
+
+    // 保留业务段 24400 to 89999
+    reserved 24400 to 89999;
+
+    // ========= SYSTEM =========
+    // 服务自身错误
+    INTERNAL_ERROR = 90000;
+    reserved 90001 to 90099;
+
+    // encoding/decoding error
+
+    // JSONPB encoding/decoding with error
+    JSONPB_ERROR = 90100;
+
+    // JSON encoding/decoding with error
+    JSON_ERROR = 90101;
+
+    // PB encoding/decoding with error
+    PB_ERROR = 90102;
+    reserved 90103 to 90999;
+
+    // 依赖服务错误
+    EXTERNAL_ERROR = 91000;
+
+    reserved 91001 to max;
+  }
+
+  // 业务错误码
+  Code code = 1;
+
+  // 错误信息
+  string msg = 2;
+
+  // 补充错误信息
+  // @optional
+  repeated string errors = 3;
+}

+ 14 - 0
backend/operation/enum.proto

@@ -0,0 +1,14 @@
+syntax = "proto3";
+
+package backend.operation;
+
+option go_package = ".;operationPb";
+
+// 字段类型
+message IsShow {
+  enum Kind {
+    INVALID = 0;  // 无效
+    OK = 1;       // 是
+    NO = 2;       // 否
+  }
+}

+ 33 - 0
backend/operation/pasture.proto

@@ -0,0 +1,33 @@
+syntax = "proto3";
+package backend.operation;
+
+option go_package = ".;operationPb";
+
+import "backend/operation/enum.proto";
+
+message AddPastureRequest {
+  int64 id = 1;
+  string name = 2; // 牧场名称
+  string manager_user = 3; // 牧场负责人名称
+  string manager_password = 4; // 牧场负责人账号
+  string manager_phone = 5;   // 牧场负责人手机号
+  string address = 6;   // 牧场地址
+  IsShow.Kind is_show = 7;    // 是否启用
+  int64 Created_at = 8;    // 创建时间
+}
+
+message SearchPastureRequest {
+  int32 page = 1;        // 第几页,从1开始
+  int32 page_size = 2;   // 每页size,一般为20
+  string name = 3;       // 牧场名称
+  string manager_user = 4; // 牧场负责人名称
+  string manager_phone = 5;   // 牧场负责人手机号
+  int64 start_time = 6;       // 开始时间
+  int64 end_time = 7;    // 结束时间
+}
+
+message SearchPastureResponse {
+  int32 page = 1;
+  int32 total = 2;
+  repeated AddPastureRequest data = 3;
+}

+ 14 - 0
backend/operation/system.proto

@@ -0,0 +1,14 @@
+syntax = "proto3";
+package backend.operation;
+
+option go_package = ".;operationPb";
+
+import "backend/operation/enum.proto";
+// 用户权限
+message AddRoleRequest {
+  int64 id = 1;
+  string name = 2;      // 角色名称
+  string remarks = 3;   // 角色备注
+  IsShow is_show = 4;   // 是否启用
+}
+

+ 2 - 2
cmd/http.go

@@ -5,7 +5,7 @@ import (
 	"kpt-tmr-group/config"
 	"kpt-tmr-group/dep"
 	"kpt-tmr-group/http"
-	log "kpt-tmr-group/util/logger"
+	"kpt-tmr-group/pkg/logger/logrus"
 
 	"github.com/spf13/cobra"
 )
@@ -21,7 +21,7 @@ var httpCmd = &cobra.Command{
 
 func bootHTTPServer(cfg *config.AppConfig) {
 	dependency := dep.DIHttpDependency()
-	log.Info("kpt-tmr-group: boot HTTP server")
+	logrus.Info("kpt-tmr-group: boot HTTP server")
 	server := http.NewServer(
 		http.ExportLogOption(),
 		http.WithDependency(dependency),

+ 5 - 6
config/app.go

@@ -1,8 +1,7 @@
 package config
 
 import (
-	"kpt-tmr-group/util/di"
-	log "kpt-tmr-group/util/logger"
+	"kpt-tmr-group/pkg/di"
 	"os"
 	"strings"
 	"sync"
@@ -31,9 +30,10 @@ type AppConfig struct {
 // StoreSetting 数据库配置
 type StoreSetting struct {
 	// 开启 SyDb SQL 记录
+	DriverName      string `yaml:"driver_name" json:"driver_name"`
 	ShowSQL         bool   `yaml:"show_sql" env:"STORE_SHOW_SQL"`
-	KptEventDSNRW   string `yaml:"kpt_event_rw" env:"LINGO_COURSE_DSN_RW"`
-	KptEventDSNMigr string `yaml:"kpt_event_migr" env:"LINGO_COURSE_DSN_MIGR"`
+	KptEventDSNRW   string `yaml:"kpt_tmr_group_rw" env:"LINGO_COURSE_DSN_RW"`
+	KptEventDSNMigr string `yaml:"kpt_tmr_group_migr" env:"LINGO_COURSE_DSN_MIGR"`
 }
 
 func Options() *AppConfig {
@@ -49,14 +49,13 @@ func init() {
 		switch appEnv {
 		default:
 			err = Initialize("app.test.yaml", cfg)
-			log.SetLevel(log.ErrorLevel)
 		case "development":
 			err = Initialize("app.develop.yaml", cfg)
 		case "production":
 			err = Initialize("app.production.yaml", cfg)
 		}
 		if err != nil {
-			log.Fatalf("%+v", err)
+			panic(err)
 		}
 		options = cfg
 	})

+ 6 - 3
config/app.test.yaml

@@ -1,8 +1,11 @@
-app_name: kpt-tmr-group
+app_name: kpt-event
 app_environment: test
 debug: true
 http_server_addr: ':8000'
+http_metrics_addr: ':23332'
 
 store:
-  kpt_event_rw: "root@tcp(127.0.0.1:3306)/kpt_tmr_group?charset=utf8mb4&parseTime=true&loc=Local&allowNativePasswords=true&timeout=300s&readTimeout=300s&writeTimeout=300s"
-  kpt_event_migr: "root@tcp(127.0.0.1:3306)/kpt_tmr_group?charset=utf8mb4&parseTime=true&loc=Local&allowNativePasswords=true&timeout=300s&readTimeout=300s&writeTimeout=300s"
+  show_sql: true
+  driver_name: mysql
+  kpt_tmr_group_rw: "root:123456@tcp(127.0.0.1:3306)/kpt_tmr_group?charset=utf8mb4&parseTime=true&loc=Local&allowNativePasswords=true&timeout=300s&readTimeout=300s&writeTimeout=300s"
+  kpt_tmr_group_migr: "root:123456@tcp(127.0.0.1:3306)/kpt_tmr_group?charset=utf8mb4&parseTime=true&loc=Local&allowNativePasswords=true&timeout=300s&readTimeout=300s&writeTimeout=300s"

+ 5 - 1
dep/dep.go

@@ -2,7 +2,9 @@ package dep
 
 import (
 	"kpt-tmr-group/config"
-	"kpt-tmr-group/util/di"
+	"kpt-tmr-group/module/backend"
+	"kpt-tmr-group/pkg/di"
+	"kpt-tmr-group/store/kptstore"
 )
 
 // Global 全局所有的依赖
@@ -19,5 +21,7 @@ func Options() []di.HubOption {
 		// 基础依赖
 		config.Module,
 		// store
+		kptstore.Module,
+		backend.Module,
 	}
 }

+ 1 - 1
dep/di.go

@@ -1,6 +1,6 @@
 package dep
 
-import "kpt-tmr-group/util/di"
+import "kpt-tmr-group/pkg/di"
 
 func DI(opts ...di.HubOption) *di.Hub {
 	var hubOpts []di.HubOption

+ 4 - 1
dep/di_http.go

@@ -1,6 +1,8 @@
 package dep
 
 import (
+	"kpt-tmr-group/module/backend"
+
 	"go.uber.org/dig"
 )
 
@@ -23,5 +25,6 @@ type HttpDependency struct {
 	// module
 	//Content    llscontent.Content       // Content 内容获取服务
 	//DataCenter llsdatacenter.DataCenter // DMP 数据中心
-	//Event      *event.Event
+
+	StoreEventHub backend.Hub
 }

+ 27 - 1
go.mod

@@ -9,18 +9,30 @@ require (
 	github.com/gin-contrib/requestid v0.0.6
 	github.com/gin-gonic/gin v1.9.0
 	github.com/google/go-cmp v0.5.9
+	github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
+	github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
 	github.com/jinzhu/copier v0.3.5
+	github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
 	github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de
 	github.com/mitchellh/mapstructure v1.5.0
+	github.com/natefinch/lumberjack v2.0.0+incompatible
 	github.com/sirupsen/logrus v1.9.0
 	github.com/spf13/cobra v1.7.0
 	github.com/spf13/viper v1.15.0
 	github.com/stretchr/testify v1.8.2
 	go.uber.org/dig v1.15.0
+	go.uber.org/zap v1.21.0
+	google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef
+	google.golang.org/grpc v1.52.0
+	google.golang.org/protobuf v1.30.0
+	gorm.io/driver/mysql v1.5.0
+	gorm.io/gorm v1.25.0
 )
 
 require (
+	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/bytedance/sonic v1.8.0 // indirect
+	github.com/cespare/xxhash/v2 v2.2.0 // indirect
 	github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/fsnotify/fsnotify v1.6.0 // indirect
@@ -28,20 +40,32 @@ require (
 	github.com/go-playground/locales v0.14.1 // indirect
 	github.com/go-playground/universal-translator v0.18.1 // indirect
 	github.com/go-playground/validator/v10 v10.11.2 // indirect
+	github.com/go-sql-driver/mysql v1.7.0 // indirect
 	github.com/goccy/go-json v0.10.0 // indirect
+	github.com/golang/protobuf v1.5.3 // indirect
 	github.com/google/uuid v1.3.0 // indirect
 	github.com/hashicorp/hcl v1.0.0 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
+	github.com/jinzhu/inflection v1.0.0 // indirect
+	github.com/jinzhu/now v1.1.5 // indirect
+	github.com/jonboulle/clockwork v0.4.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/klauspost/cpuid/v2 v2.0.9 // indirect
 	github.com/leodido/go-urn v1.2.1 // indirect
+	github.com/lestrrat-go/strftime v1.0.6 // indirect
 	github.com/magiconair/properties v1.8.7 // indirect
 	github.com/mattn/go-isatty v0.0.17 // indirect
 	github.com/mattn/go-sqlite3 v1.14.16 // indirect
+	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/pelletier/go-toml/v2 v2.0.6 // indirect
+	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
+	github.com/prometheus/client_golang v1.15.1 // indirect
+	github.com/prometheus/client_model v0.3.0 // indirect
+	github.com/prometheus/common v0.42.0 // indirect
+	github.com/prometheus/procfs v0.9.0 // indirect
 	github.com/spf13/afero v1.9.3 // indirect
 	github.com/spf13/cast v1.5.0 // indirect
 	github.com/spf13/jwalterweatherman v1.1.0 // indirect
@@ -49,12 +73,14 @@ require (
 	github.com/subosito/gotenv v1.4.2 // indirect
 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
 	github.com/ugorji/go/codec v1.2.9 // indirect
+	go.uber.org/atomic v1.9.0 // indirect
+	go.uber.org/multierr v1.8.0 // indirect
 	golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
 	golang.org/x/crypto v0.7.0 // indirect
 	golang.org/x/net v0.8.0 // indirect
 	golang.org/x/sys v0.6.0 // indirect
 	golang.org/x/text v0.8.0 // indirect
-	google.golang.org/protobuf v1.29.1 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
+	gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )

File diff suppressed because it is too large
+ 44 - 705
go.sum


+ 2 - 6
http/api/default.go → http/handler/default.go

@@ -1,10 +1,10 @@
-package api
+package handler
 
 import (
 	"compress/gzip"
 	"fmt"
 	"io/ioutil"
-	"kpt-tmr-group/util/xerr"
+	"kpt-tmr-group/pkg/xerr"
 	"net/http"
 
 	"github.com/gin-gonic/gin"
@@ -37,7 +37,3 @@ func dumpPerfEventBody(c *gin.Context) ([]byte, error) {
 
 	return ioutil.ReadAll(reader)
 }
-
-func Hello(c *gin.Context) {
-	c.String(http.StatusOK, "hello world!")
-}

+ 39 - 0
http/handler/pasture/pasture.go

@@ -0,0 +1,39 @@
+package pasture
+
+import (
+	"kpt-tmr-group/http/middleware"
+	"kpt-tmr-group/pkg/apierr"
+	"kpt-tmr-group/pkg/apiok"
+	operationPb "kpt-tmr-group/proto/go/backend/operation"
+	"net/http"
+
+	"github.com/gin-gonic/gin"
+)
+
+func AddPasture(c *gin.Context) {
+	req := make([]*operationPb.AddPastureRequest, 0)
+	if err := c.BindJSON(&req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+	if err := middleware.BackendOperation(c).OpsService.CreatePastureList(c, req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	c.JSON(http.StatusOK, apiok.CommonResponse(apiok.NewApiOk(true)))
+}
+
+func SearchPastureList(c *gin.Context) {
+	req := &operationPb.SearchPastureRequest{}
+	if err := c.BindJSON(req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+	res, err := middleware.BackendOperation(c).OpsService.SearchPastureList(c, req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	c.JSON(http.StatusOK, apiok.CommonResponse(res.ToPB()))
+}

+ 13 - 0
http/handler/system/role.go

@@ -0,0 +1,13 @@
+package system
+
+import (
+	"kpt-tmr-group/pkg/apiok"
+	"net/http"
+
+	"github.com/gin-gonic/gin"
+)
+
+func AddRole(c *gin.Context) {
+
+	c.JSON(http.StatusOK, apiok.CommonResponse(apiok.NewApiOk(true)))
+}

+ 13 - 0
http/handler/system/user.go

@@ -0,0 +1,13 @@
+package system
+
+import (
+	"kpt-tmr-group/pkg/apiok"
+	"net/http"
+
+	"github.com/gin-gonic/gin"
+)
+
+func AddUser(c *gin.Context) {
+
+	c.JSON(http.StatusOK, apiok.CommonResponse(apiok.NewApiOk(true)))
+}

+ 11 - 0
http/middleware/hub.go

@@ -0,0 +1,11 @@
+package middleware
+
+import (
+	"kpt-tmr-group/module/backend"
+
+	"github.com/gin-gonic/gin"
+)
+
+func BackendOperation(c *gin.Context) *backend.Hub {
+	return &(Dependency(c).StoreEventHub)
+}

+ 2 - 2
http/middleware/sentry.go

@@ -1,8 +1,8 @@
 package middleware
 
 import (
-	sentry2 "kpt-tmr-group/util/sentry"
-	"kpt-tmr-group/util/xerr"
+	sentry2 "kpt-tmr-group/pkg/sentry"
+	"kpt-tmr-group/pkg/xerr"
 	"sync"
 
 	"github.com/getsentry/sentry-go"

+ 2 - 2
http/route/api_debug_route.go

@@ -1,8 +1,8 @@
 package route
 
 import (
-	"kpt-tmr-group/http/api"
 	"kpt-tmr-group/http/debug"
+	"kpt-tmr-group/http/handler"
 
 	"github.com/gin-gonic/gin"
 )
@@ -14,7 +14,7 @@ func DebugAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		}
 
 		// Not Found
-		s.NoRoute(api.Handle404)
+		s.NoRoute(handler.Handle404)
 		debugRoute := authRouteGroup(s, "/api/v1/kpt/debug/")
 		// kpt debug api
 		debugRoute.GET("hello", debug.HelloOk)

+ 12 - 7
http/route/app_api.go

@@ -1,8 +1,9 @@
 package route
 
 import (
-	// m "git.llsapp.com/zhenghe/pkg/http/middleware"
-	"kpt-tmr-group/http/api"
+	"kpt-tmr-group/http/handler"
+	"kpt-tmr-group/http/handler/pasture"
+	"kpt-tmr-group/http/handler/system"
 
 	"github.com/gin-gonic/gin"
 )
@@ -13,13 +14,17 @@ func AppAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 			opt(s)
 		}
 		// Not Found
-		s.NoRoute(api.Handle404)
+		s.NoRoute(handler.Handle404)
 		// Health Check
-		s.GET("/check", api.Health)
+		s.GET("/check", handler.Health)
 
-		// lingo API 组
-		lingoRoute := authRouteGroup(s, "/api/v1/kpt-tmr-group/")
-		lingoRoute.GET("/hello", api.Hello)
+		// system API 组
+		lingoRoute := authRouteGroup(s, "/api/v1/system/")
+		lingoRoute.POST("/user/add", system.AddUser)
+
+		// 牧场管理
+		lingoRoute.POST("/pasture/add", pasture.AddPasture)
+		lingoRoute.POST("/pasture/list", pasture.SearchPastureList)
 	}
 }
 

+ 53 - 0
logger/logrus-2023-05-06.log

@@ -0,0 +1,53 @@
+{"level":"info","msg":"kpe-event: is starting","time":"2023-05-06T16:51:38+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/store/kptstore/rw_store.go:47\n[error] failed to initialize database, got error Error 1045 (28000): Access denied for user 'root'@'172.19.0.1' (using password: NO)","time":"2023-05-06T16:51:38+08:00"}
+{"level":"info","msg":"kpe-event: is starting","time":"2023-05-06T16:54:30+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/store/kptstore/rw_store.go:47\n[error] failed to initialize database, got error Error 1049 (42000): Unknown database 'kpt_tmr-group'","time":"2023-05-06T16:54:30+08:00"}
+{"level":"info","msg":"kpe-event: is starting","time":"2023-05-06T16:54:56+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/store/kptstore/rw_store.go:47\n[error] failed to initialize database, got error Error 1049 (42000): Unknown database 'kpt_tmr-group'","time":"2023-05-06T16:54:56+08:00"}
+{"level":"info","msg":"kpe-event: is starting","time":"2023-05-06T16:55:18+08:00"}
+{"level":"info","msg":"kpt-tmr-group: boot HTTP server","time":"2023-05-06T16:55:18+08:00"}
+{"level":"info","msg":"kpe-event: is starting","time":"2023-05-06T17:59:10+08:00"}
+{"level":"info","msg":"kpt-tmr-group: boot HTTP server","time":"2023-05-06T17:59:10+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:38\n[66.660ms] [rows:1] SELECT count(*) FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%'","time":"2023-05-06T18:06:55+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:39\n[5.984ms] [rows:0] SELECT * FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%' ORDER BY id desc LIMIT 10","time":"2023-05-06T18:06:55+08:00"}
+{"level":"info","msg":"kpe-event: is starting","time":"2023-05-06T18:09:56+08:00"}
+{"level":"info","msg":"kpt-tmr-group: boot HTTP server","time":"2023-05-06T18:09:56+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:38\n[15.212ms] [rows:1] SELECT count(*) FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%'","time":"2023-05-06T18:10:01+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:39\n[6.141ms] [rows:0] SELECT * FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%' ORDER BY id desc LIMIT 10","time":"2023-05-06T18:10:01+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:38\n[4.496ms] [rows:1] SELECT count(*) FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%'","time":"2023-05-06T18:11:22+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:39\n[3.826ms] [rows:0] SELECT * FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%' ORDER BY id desc LIMIT 10","time":"2023-05-06T18:11:22+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:38\n[2.234ms] [rows:1] SELECT count(*) FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%'","time":"2023-05-06T18:11:22+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:39\n[3.514ms] [rows:0] SELECT * FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%' ORDER BY id desc LIMIT 10","time":"2023-05-06T18:11:22+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:38\n[5.620ms] [rows:1] SELECT count(*) FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%'","time":"2023-05-06T18:11:23+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:39\n[2.685ms] [rows:0] SELECT * FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%' ORDER BY id desc LIMIT 10","time":"2023-05-06T18:11:23+08:00"}
+{"level":"info","msg":"kpe-event: is starting","time":"2023-05-06T18:12:14+08:00"}
+{"level":"info","msg":"kpt-tmr-group: boot HTTP server","time":"2023-05-06T18:12:14+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:38\n[9.825ms] [rows:1] SELECT count(*) FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%'","time":"2023-05-06T18:12:24+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:39\n[2.170ms] [rows:0] SELECT * FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%' ORDER BY id desc LIMIT 10","time":"2023-05-06T18:12:24+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:38\n[2.756ms] [rows:1] SELECT count(*) FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%'","time":"2023-05-06T18:12:26+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:39\n[1.601ms] [rows:0] SELECT * FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%' ORDER BY id desc LIMIT 10","time":"2023-05-06T18:12:26+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:38\n[2.269ms] [rows:1] SELECT count(*) FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%'","time":"2023-05-06T18:12:27+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:39\n[1.699ms] [rows:0] SELECT * FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%' ORDER BY id desc LIMIT 10","time":"2023-05-06T18:12:27+08:00"}
+{"level":"info","msg":"kpe-event: is starting","time":"2023-05-06T18:12:54+08:00"}
+{"level":"info","msg":"kpt-tmr-group: boot HTTP server","time":"2023-05-06T18:12:54+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:38\n[8.080ms] [rows:1] SELECT count(*) FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%'","time":"2023-05-06T18:13:13+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:39\n[3.225ms] [rows:0] SELECT * FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%' ORDER BY id desc LIMIT 10","time":"2023-05-06T18:13:13+08:00"}
+{"level":"info","msg":"kpe-event: is starting","time":"2023-05-06T18:14:27+08:00"}
+{"level":"info","msg":"kpt-tmr-group: boot HTTP server","time":"2023-05-06T18:14:27+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:38\n[10.386ms] [rows:1] SELECT count(*) FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%'","time":"2023-05-06T18:14:32+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:39\n[2.703ms] [rows:0] SELECT * FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%' ORDER BY id desc LIMIT 10","time":"2023-05-06T18:14:32+08:00"}
+{"level":"info","msg":"kpe-event: is starting","time":"2023-05-06T18:15:01+08:00"}
+{"level":"info","msg":"kpt-tmr-group: boot HTTP server","time":"2023-05-06T18:15:01+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:38\n[15.669ms] [rows:1] SELECT count(*) FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%'","time":"2023-05-06T18:15:06+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:39\n[2.203ms] [rows:0] SELECT * FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%' ORDER BY id desc LIMIT 10","time":"2023-05-06T18:15:06+08:00"}
+{"level":"info","msg":"kpe-event: is starting","time":"2023-05-06T18:16:02+08:00"}
+{"level":"info","msg":"kpt-tmr-group: boot HTTP server","time":"2023-05-06T18:16:02+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:38\n[15.813ms] [rows:1] SELECT count(*) FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%'","time":"2023-05-06T18:16:06+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:39\n[2.120ms] [rows:0] SELECT * FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%' ORDER BY id desc LIMIT 10","time":"2023-05-06T18:16:06+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:38\n[4.555ms] [rows:1] SELECT count(*) FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%'","time":"2023-05-06T18:16:08+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:39\n[3.303ms] [rows:0] SELECT * FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%' ORDER BY id desc LIMIT 10","time":"2023-05-06T18:16:08+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:38\n[2.195ms] [rows:1] SELECT count(*) FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%'","time":"2023-05-06T18:18:12+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:39\n[2.724ms] [rows:0] SELECT * FROM `group_pasture` WHERE is_show = 1  AND name like '%现代牧业%' ORDER BY id desc LIMIT 10","time":"2023-05-06T18:18:12+08:00"}
+{"level":"info","msg":"kpe-event: is starting","time":"2023-05-06T18:27:20+08:00"}
+{"level":"info","msg":"kpt-tmr-group: boot HTTP server","time":"2023-05-06T18:27:21+08:00"}
+{"level":"info","msg":"D:/project/golangNew/kpt-tmr-group/module/backend/service.go:14 Error 1265 (01000): Data truncated for column 'created_at' at row 1\n[31.262ms] [rows:0] INSERT INTO `group_pasture` (`name`,`manager_user`,`manager_password`,`manager_phone`,`is_show`,`address`,`created_at`,`updated_at`) VALUES ('选地低识要','officia cillum','e10adc3949ba59abbe56e057f20f883e','18642558992',1,'福建省汕尾市巴林左旗','2023-05-06 18:27:26.506','2023-05-06 18:27:26.506'),('种切命','minim Duis sint aliqua deserunt','e10adc3949ba59abbe56e057f20f883e','18135781774',1,'贵州省七台河市贵南县','2023-05-06 18:27:26.506','2023-05-06 18:27:26.506')","time":"2023-05-06T18:27:26+08:00"}

+ 3 - 0
logger/zap-2023-05-06.log

@@ -0,0 +1,3 @@
+{"L":"ERROR","T":"2023-05-06T16:51:38.918+0800","M":"MustNewStore","Err":"Error 1045 (28000): Access denied for user 'root'@'172.19.0.1' (using password: NO)","KptEventDSNRW":"root@tcp(127.0.0.1:3306)/kpt_tmr_group?charset=utf8mb4&parseTime=true&loc=Local&allowNativePasswords=true&timeout=300s&readTimeout=300s&writeTimeout=300s","func":"kptstore.MustNewStore","file":"D:/project/golangNew/kpt-tmr-group/store/kptstore/rw_store.go","line":53}
+{"L":"ERROR","T":"2023-05-06T16:54:30.471+0800","M":"MustNewStore","Err":"Error 1049 (42000): Unknown database 'kpt_tmr-group'","KptEventDSNRW":"root:123456@tcp(192.168.1.70:3306)/kpt_tmr-group?charset=utf8mb4&parseTime=true&loc=Local&allowNativePasswords=true&timeout=300s&readTimeout=300s&writeTimeout=300s","func":"kptstore.MustNewStore","file":"D:/project/golangNew/kpt-tmr-group/store/kptstore/rw_store.go","line":53}
+{"L":"ERROR","T":"2023-05-06T16:54:56.679+0800","M":"MustNewStore","Err":"Error 1049 (42000): Unknown database 'kpt_tmr-group'","KptEventDSNRW":"root:123456@tcp(127.0.0.1:3306)/kpt_tmr-group?charset=utf8mb4&parseTime=true&loc=Local&allowNativePasswords=true&timeout=300s&readTimeout=300s&writeTimeout=300s","func":"kptstore.MustNewStore","file":"D:/project/golangNew/kpt-tmr-group/store/kptstore/rw_store.go","line":53}

+ 3 - 3
main.go

@@ -6,11 +6,11 @@ package main
 
 import (
 	"kpt-tmr-group/cmd"
-	log "kpt-tmr-group/util/logger"
+	"kpt-tmr-group/pkg/logger/logrus"
 )
 
 func main() {
-	log.Info("kpe-event: is starting")
+	logrus.Info("kpe-event: is starting")
 	cmd.Execute()
-	log.Error("kpt-tmr-group: is shut down")
+	logrus.Error("kpt-tmr-group: is shut down")
 }

+ 0 - 17
model/event_fileds.go

@@ -1,17 +0,0 @@
-package model
-
-import (
-	"time"
-)
-
-type EventFileds struct {
-	Id         int       `xorm:"not null pk default 0 comment('主键自增ID') INT(11)"`
-	EventId    int       `xorm:"not null default 0 comment('事件表id') index INT(11)"`
-	FiledId    int       `xorm:"not null default 0 comment('字段表id') INT(11)"`
-	IsRequired int       `xorm:"not null default 0 comment('改字段是否必填 1 是 0 否') TINYINT(1)"`
-	DataSource int       `xorm:"not null default 0 comment('数据来源 1 自动生成 0 手动输入') TINYINT(1)"`
-	IsList     int       `xorm:"not null default 0 comment('列表是否可见 1 是 0 否') TINYINT(1)"`
-	ShowLine   int       `xorm:"not null comment('显示分布 1 显示1行 2 显示2行 3 显示3行...') TINYINT(1)"`
-	CreateTime time.Time `xorm:"not null default 'CURRENT_TIMESTAMP' comment('创建时间') DATETIME"`
-	UpdateTime time.Time `xorm:"not null default 'CURRENT_TIMESTAMP' comment('更新时间') DATETIME"`
-}

+ 85 - 0
model/group_pasture.go

@@ -0,0 +1,85 @@
+package model
+
+import (
+	"kpt-tmr-group/pkg/tool"
+	operationPb "kpt-tmr-group/proto/go/backend/operation"
+)
+
+type GroupPasture struct {
+	Id              int64                   `json:"id,omitempty"`
+	Name            string                  `json:"name,omitempty"`
+	ManagerUser     string                  `json:"manager_user"`
+	ManagerPassword string                  `json:"manager_password"`
+	ManagerPhone    string                  `json:"manager_phone"`
+	IsShow          operationPb.IsShow_Kind `json:"is_show,omitempty"`
+	Address         string                  `json:"address"`
+	CreatedAt       int64                   `json:"created_at,omitempty"`
+	UpdatedAt       int64                   `json:"updated_at,omitempty"`
+}
+
+func (s *GroupPasture) TableName() string {
+	return "group_pasture"
+}
+
+const InitPassword = "123456"
+
+func NewGroupPastureList(reqs []*operationPb.AddPastureRequest) []*GroupPasture {
+	groupPastureList := make([]*GroupPasture, len(reqs))
+	for k, pasture := range reqs {
+		groupPastureList[k] = &GroupPasture{
+			Name:            pasture.Name,
+			ManagerUser:     pasture.ManagerUser,
+			ManagerPassword: tool.Md5String(InitPassword),
+			ManagerPhone:    pasture.ManagerPhone,
+			IsShow:          operationPb.IsShow_OK,
+			Address:         pasture.Address,
+		}
+	}
+	return groupPastureList
+}
+
+type GroupPastureSlice []*GroupPasture
+
+func (g GroupPastureSlice) ToPB() []*operationPb.AddPastureRequest {
+	res := make([]*operationPb.AddPastureRequest, len(g))
+	for i, v := range g {
+		res[i] = &operationPb.AddPastureRequest{
+			Id:              v.Id,
+			Name:            v.Name,
+			ManagerUser:     v.ManagerUser,
+			ManagerPassword: v.ManagerPassword,
+			ManagerPhone:    v.ManagerPhone,
+			Address:         v.ManagerPassword,
+			IsShow:          v.IsShow,
+			CreatedAt:       v.CreatedAt,
+		}
+	}
+	return res
+}
+
+func (g *GroupPasture) ToPb() *operationPb.AddPastureRequest {
+	return &operationPb.AddPastureRequest{
+		Id:              g.Id,
+		Name:            g.Name,
+		ManagerUser:     g.ManagerUser,
+		ManagerPassword: g.ManagerPassword,
+		ManagerPhone:    g.ManagerPhone,
+		Address:         g.Address,
+		IsShow:          g.IsShow,
+		CreatedAt:       g.CreatedAt,
+	}
+}
+
+type GroupPastureResponse struct {
+	Page  int32                            `json:"page"`
+	Total int32                            `json:"total"`
+	Data  []*operationPb.AddPastureRequest `json:"data"`
+}
+
+func (g *GroupPastureResponse) ToPB() *operationPb.SearchPastureResponse {
+	return &operationPb.SearchPastureResponse{
+		Page:  g.Page,
+		Total: g.Total,
+		Data:  g.Data,
+	}
+}

+ 0 - 19
model/kpe_event.go

@@ -1,19 +0,0 @@
-package model
-
-import (
-	"time"
-)
-
-type KpeEvent struct {
-	Id            int       `xorm:"not null pk default 0 comment('主键自增ID') INT(11)"`
-	Name          string    `xorm:"not null default '' comment('事件名称') VARCHAR(260)"`
-	Remarks       string    `xorm:"not null default '' comment('事件备注') VARCHAR(260)"`
-	Category      int       `xorm:"not null default 0 comment('事件分类') SMALLINT(2)"`
-	IndicatorsIds string    `xorm:"not null default '{}' comment('指标影响IDS json格式') VARCHAR(260)"`
-	BaseIds       string    `xorm:"not null default '{}' comment('字段影响IDS json格式') VARCHAR(260)"`
-	EventIds      string    `xorm:"not null default '{}' comment('事件IDS json格式') VARCHAR(260)"`
-	FiledIds      string    `xorm:"not null default '{}' comment('字段IDS json格式') VARCHAR(260)"`
-	IsShow        int       `xorm:"not null default 1 comment('是否显示菜单 1:显示 0: 隐藏') TINYINT(1)"`
-	CreateTime    time.Time `xorm:"not null default 'CURRENT_TIMESTAMP' comment('创建时间') DATETIME"`
-	UpdateTime    time.Time `xorm:"not null default 'CURRENT_TIMESTAMP' comment('更新时间') DATETIME"`
-}

+ 0 - 19
model/kpt_fileds.go

@@ -1,19 +0,0 @@
-package model
-
-import (
-	"time"
-)
-
-type KptFileds struct {
-	Id             int       `xorm:"not null pk autoincr comment('主键自增') INT(11)"`
-	FieldName      string    `xorm:"not null comment('字段名称') VARCHAR(260)"`
-	ComponentsType int       `xorm:"not null default 0 comment('组件类型 0 单行文本 1 多行文本 2 下拉框 3 单选框 4 多选框 5 日期选择器 6 时间选择器 7 开关') TINYINT(1)"`
-	FieldType      int       `xorm:"not null default 0 comment('字段类型 0 无效类型 1 日期类型 2 时间类型 3 字符串 4 小数类型 5 bool类型') TINYINT(1)"`
-	FiledLen       int       `xorm:"not null default 0 comment('字段长度') SMALLINT(5)"`
-	MinValue       int       `xorm:"not null default 0 comment('取值范围: 最小值') INT(11)"`
-	MaxValue       int       `xorm:"not null default 0 comment('取值范围: 最大值') INT(11)"`
-	IsShow         int       `xorm:"not null default 1 comment('是否启用 0 否 1 是') TINYINT(1)"`
-	Description    string    `xorm:"not null default '' comment('字段描述') VARCHAR(260)"`
-	CreateTime     time.Time `xorm:"not null default 'CURRENT_TIMESTAMP' comment('创建时间') DATETIME"`
-	UpdateTime     time.Time `xorm:"not null default 'CURRENT_TIMESTAMP' comment('更新时间') DATETIME"`
-}

+ 24 - 0
model/system_menu.go

@@ -0,0 +1,24 @@
+package model
+
+import (
+	operationPb "kpt-tmr-group/proto/go/backend/operation"
+)
+
+type SystemMenu struct {
+	Id        int64                   `json:"id,omitempty"`
+	Name      string                  `json:"name,omitempty"`
+	MenuType  int                     `json:"menu_type,omitempty"`
+	Title     string                  `json:"title,omitempty"`
+	Path      string                  `json:"path,omitempty"`
+	IsShow    operationPb.IsShow_Kind `json:"is_show,omitempty"`
+	Component string                  `json:"component,omitempty"`
+	Icon      string                  `json:"icon,omitempty"`
+	Sort      int                     `json:"sort,omitempty"`
+	Redirect  string                  `json:"redirect,omitempty"`
+	CreatedAt int64                   `json:"created_at,omitempty"`
+	UpdatedAt int64                   `json:"updated_at,omitempty"`
+}
+
+func (s *SystemMenu) TableName() string {
+	return "system_role"
+}

+ 19 - 0
model/system_role.go

@@ -0,0 +1,19 @@
+package model
+
+import (
+	operationPb "kpt-tmr-group/proto/go/backend/operation"
+)
+
+type SystemRole struct {
+	Id         int64                   `json:"id,omitempty"`
+	Name       string                  `json:"name,omitempty"`
+	Remarks    string                  `json:"remarks,omitempty"`
+	IsShow     operationPb.IsShow_Kind `json:"is_show,omitempty"`
+	CreateUser string                  `json:"create_user,omitempty"`
+	CreatedAt  int64                   `json:"created_at,omitempty"`
+	UpdatedAt  int64                   `json:"updated_at,omitempty"`
+}
+
+func (s *SystemRole) TableName() string {
+	return "system_role"
+}

+ 20 - 0
model/system_user.go

@@ -0,0 +1,20 @@
+package model
+
+import (
+	operationPb "kpt-tmr-group/proto/go/backend/operation"
+)
+
+type SystemUser struct {
+	Id         int64                   `json:"id,omitempty"`
+	Name       string                  `json:"name,omitempty"`
+	RoleId     int                     `json:"role_id,omitempty"`
+	RoleName   string                  `json:"role_name,omitempty"`
+	CreateUser string                  `json:"create_user,omitempty"`
+	IsShow     operationPb.IsShow_Kind `json:"is_show,omitempty"`
+	CreatedAt  int64                   `json:"created_at,omitempty"`
+	UpdatedAt  int64                   `json:"updated_at,omitempty"`
+}
+
+func (s *SystemUser) TableName() string {
+	return "system_user"
+}

+ 44 - 0
module/backend/interface.go

@@ -0,0 +1,44 @@
+package backend
+
+import (
+	"context"
+	"kpt-tmr-group/config"
+	"kpt-tmr-group/model"
+	"kpt-tmr-group/pkg/di"
+	operationPb "kpt-tmr-group/proto/go/backend/operation"
+	"kpt-tmr-group/store/kptstore"
+
+	"go.uber.org/dig"
+)
+
+var Module = di.Options(
+	di.Provide(NewStore),
+)
+
+type Hub struct {
+	dig.In
+	OpsService KptService
+}
+
+type StoreEntry struct {
+	dig.In
+
+	Cfg *config.AppConfig
+	DB  *kptstore.DB
+	// AsynqClient asynqsvc.Client
+	// Cache *redis.Client
+}
+
+func NewStore(store StoreEntry) KptService {
+	return &store
+}
+
+type KptService interface {
+	Operation
+}
+
+type Operation interface {
+	// CreatePastureList 牧场管理相关
+	CreatePastureList(ctx context.Context, req []*operationPb.AddPastureRequest) error
+	SearchPastureList(ctx context.Context, req *operationPb.SearchPastureRequest) (*model.GroupPastureResponse, error)
+}

+ 48 - 0
module/backend/service.go

@@ -0,0 +1,48 @@
+package backend
+
+import (
+	"context"
+	"fmt"
+	"kpt-tmr-group/model"
+	"kpt-tmr-group/pkg/xerr"
+	operationPb "kpt-tmr-group/proto/go/backend/operation"
+)
+
+// CreatePastureList 创建牧场
+func (s *StoreEntry) CreatePastureList(ctx context.Context, req []*operationPb.AddPastureRequest) error {
+	pastureListModel := model.NewGroupPastureList(req)
+	if err := s.DB.Create(pastureListModel).Error; err != nil {
+		return xerr.WithStack(err)
+	}
+	return nil
+}
+
+// SearchPastureList 查询牧场列表
+func (s *StoreEntry) SearchPastureList(ctx context.Context, req *operationPb.SearchPastureRequest) (*model.GroupPastureResponse, error) {
+
+	groupPasture := make([]*model.GroupPasture, 0)
+	var count int64 = 0
+
+	pref := s.DB.Model(new(model.GroupPasture)).Where("is_show = ? ", operationPb.IsShow_OK)
+	if req.Name != "" {
+		pref.Where("name like ?", fmt.Sprintf("%s%s%s", "%", req.Name, "%"))
+	}
+	if req.ManagerPhone != "" {
+		pref.Where("manager_phone like ?", fmt.Sprintf("%s%s%s", "%", req.ManagerPhone, "%"))
+	}
+
+	if req.ManagerUser != "" {
+		pref.Where("manager_user like ?", fmt.Sprintf("%s%s%s", "%", req.ManagerUser, "%"))
+	}
+
+	if err := pref.Order("id desc").Count(&count).Limit(int(req.GetPageSize())).Offset(int((req.GetPage() - 1) * req.GetPageSize())).
+		Find(&groupPasture).Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	return &model.GroupPastureResponse{
+		Page:  req.Page,
+		Total: int32(count),
+		Data:  model.GroupPastureSlice(groupPasture).ToPB(),
+	}, nil
+}

+ 207 - 0
pkg/apierr/apierr.go

@@ -0,0 +1,207 @@
+package apierr
+
+import (
+	"encoding/json"
+	"kpt-tmr-group/pkg/xerr"
+	common "kpt-tmr-group/proto/go/backend/common"
+	"net/http"
+
+	"github.com/gin-gonic/gin"
+)
+
+func New(code common.Error_Code) *Error {
+	return &Error{err: &common.Error{
+		Code: code,
+		Msg:  errorMessage(code),
+	}}
+}
+
+func errorMessage(code common.Error_Code) string {
+	var errMessage string
+	if msg, ok := common.Error_Code_name[int32(code)]; ok {
+		errMessage = msg
+	} else {
+		errMessage = "INTERNAL_ERROR"
+	}
+
+	return errMessage
+}
+
+type Error struct {
+	err *common.Error
+}
+
+func (e *Error) GetCode() common.Error_Code {
+	return e.err.Code
+}
+
+func (e *Error) GetMsg() string {
+	return e.err.Msg
+}
+
+func (e *Error) GetErrors() []string {
+	return e.err.Errors
+}
+
+func (e *Error) Error() string {
+	bs, _ := json.Marshal(e.err)
+	return string(bs)
+}
+
+// Is 判断 err 和 e 是否相同
+func (e *Error) Is(err error) bool {
+	if gotErr, ok := err.(*Error); !ok {
+		return false
+	} else {
+		return e.err.Code == gotErr.err.Code
+	}
+}
+
+func (e *Error) MarshalJSON() ([]byte, error) {
+	return json.Marshal(e.err)
+}
+
+func (e *Error) UnmarshalJSON(data []byte) error {
+	var commonErr common.Error
+	if err := json.Unmarshal(data, &commonErr); err != nil {
+		return err
+	}
+
+	e.err = &commonErr
+	return nil
+}
+
+func (e *Error) WithLocaleMessage(c *gin.Context) *Error {
+	if msg := e.GetMsg(); msg != "" {
+		return e.SetMessage(msg)
+	}
+
+	return e
+}
+
+func (e *Error) SetMessage(m string) *Error {
+	e.err.Msg = m
+	return e
+}
+
+func (e *Error) SetErrors(errMessages []string) *Error {
+	e.err.Errors = errMessages
+	return e
+}
+
+// With more information
+func (e *Error) With(errs ...error) *Error {
+	for _, err := range errs {
+		e.err.Errors = append(e.err.Errors, err.Error())
+	}
+
+	return e
+}
+
+// WithContext return error with i18n message ?
+func WithContext(c *gin.Context, code common.Error_Code) *Error {
+	return New(code).WithLocaleMessage(c)
+}
+
+// AbortError 用来处理多种内部错误.
+// 在复杂业务场景中,存在一个接口返回多种业务错误码情况,通过这个函数来统一处理
+func AbortError(c *gin.Context, err error) {
+	if err == nil {
+		return
+	}
+
+	if e, ok := xerr.Cause(err).(*Error); ok {
+		// 取默认状态码
+		statusCode := DefaultErrorStatusCode(e.err.Code)
+		if !shouldIgnoreCode(statusCode) {
+			c.Error(e)
+		}
+		c.AbortWithStatusJSON(statusCode, e.WithLocaleMessage(c))
+		return
+	}
+
+	c.Error(err)
+	c.AbortWithStatusJSON(http.StatusInternalServerError, WithContext(c, common.Error_INTERNAL_ERROR).With(err))
+}
+
+// AbortStatusError 用来处理多种内部错误和定制返回的 http status code.
+// 在复杂业务场景中,存在一个接口返回多种业务错误码情况,通过这个函数来统一处理
+func AbortStatusError(c *gin.Context, httpCode int, err error) {
+	if err == nil {
+		return
+	}
+
+	if e, ok := xerr.Cause(err).(*Error); ok {
+		c.Error(e)
+		c.AbortWithStatusJSON(httpCode, WithContext(c, e.err.Code))
+		return
+	}
+
+	c.Error(err)
+	c.AbortWithStatusJSON(httpCode, WithContext(c, common.Error_INTERNAL_ERROR).With(err))
+}
+
+// DefaultErrorStatusCode 返回错误码对应的默认 http status code
+func DefaultErrorStatusCode(code common.Error_Code) int {
+	if statusCode, ok := errorStatusCode[int(code)]; ok {
+		return statusCode
+	}
+
+	return http.StatusInternalServerError
+}
+
+// 错误对应默认返回的 http status code
+var errorStatusCode = map[int]int{
+	0:     http.StatusOK,
+	10000: http.StatusUnauthorized,
+	11000: http.StatusBadRequest,
+	11001: http.StatusBadRequest,
+	11002: http.StatusBadRequest,
+	11003: http.StatusTooManyRequests,
+	11100: http.StatusBadRequest,
+	11200: http.StatusBadRequest,
+	20000: http.StatusBadRequest,
+	21000: http.StatusBadRequest,
+	22000: http.StatusBadRequest,
+	23000: http.StatusBadRequest,
+	23001: http.StatusBadRequest,
+	24000: http.StatusBadRequest,
+	24100: http.StatusBadRequest,
+	24101: http.StatusBadRequest,
+	24102: http.StatusBadRequest,
+	24103: http.StatusBadRequest,
+	24104: http.StatusBadRequest,
+	24400: http.StatusBadRequest,
+	24500: http.StatusBadRequest,
+	24501: http.StatusBadRequest,
+	24502: http.StatusBadRequest,
+	24520: http.StatusBadRequest,
+	24521: http.StatusBadRequest,
+	24522: http.StatusBadRequest,
+	24523: http.StatusBadRequest,
+	24524: http.StatusBadRequest,
+	24525: http.StatusBadRequest,
+	24526: http.StatusBadRequest,
+	24527: http.StatusBadRequest,
+	24528: http.StatusBadRequest,
+	24600: http.StatusBadRequest,
+	24601: http.StatusBadRequest,
+	24602: http.StatusBadRequest,
+	24603: http.StatusBadRequest,
+	24700: http.StatusBadRequest,
+	24701: http.StatusBadRequest,
+	24702: http.StatusBadRequest,
+	24704: http.StatusBadRequest,
+	24705: http.StatusBadRequest,
+	24706: http.StatusBadRequest,
+	24707: http.StatusBadRequest,
+	24800: http.StatusBadRequest,
+	24801: http.StatusBadRequest,
+	24802: http.StatusBadRequest,
+	24803: http.StatusBadRequest,
+	90000: http.StatusInternalServerError,
+	90100: http.StatusBadRequest,
+	90101: http.StatusBadRequest,
+	90102: http.StatusBadRequest,
+	91000: http.StatusInternalServerError,
+}

+ 761 - 0
pkg/apierr/apierr_gen.go

@@ -0,0 +1,761 @@
+package apierr
+
+import (
+	common "kpt-tmr-group/proto/go/backend/common"
+
+	"github.com/gin-gonic/gin"
+)
+
+// NewErrUnauthorized create err with default message
+func NewErrUnauthorized(err ...error) *Error {
+	return New(common.Error_UNAUTHORIZED).With(err...)
+}
+
+// ErrUnauthorized create err with locales
+func ErrUnauthorized(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_UNAUTHORIZED).With(err...)
+}
+
+// AbortUnauthorized abort with status code and log common.Error_UNAUTHORIZED to newrelic
+func AbortUnauthorized(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrUnauthorized(c, err...))
+}
+
+// AbortStatusUnauthorized abort with status code and log common.Error_UNAUTHORIZED to newrelic
+func AbortStatusUnauthorized(c *gin.Context, err ...error) {
+	AbortUnauthorized(c, 401, err...)
+}
+
+// NewErrBadRequest create err with default message
+func NewErrBadRequest(err ...error) *Error {
+	return New(common.Error_BAD_REQUEST).With(err...)
+}
+
+// ErrBadRequest create err with locales
+func ErrBadRequest(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_BAD_REQUEST).With(err...)
+}
+
+// AbortBadRequest abort with status code and log common.Error_BAD_REQUEST to newrelic
+func AbortBadRequest(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrBadRequest(c, err...))
+}
+
+// AbortStatusBadRequest abort with status code and log common.Error_BAD_REQUEST to newrelic
+func AbortStatusBadRequest(c *gin.Context, err ...error) {
+	AbortBadRequest(c, 400, err...)
+}
+
+// NewErrInvalidContentType create err with default message
+func NewErrInvalidContentType(err ...error) *Error {
+	return New(common.Error_INVALID_CONTENT_TYPE).With(err...)
+}
+
+// ErrInvalidContentType create err with locales
+func ErrInvalidContentType(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_INVALID_CONTENT_TYPE).With(err...)
+}
+
+// AbortInvalidContentType abort with status code and log common.Error_INVALID_CONTENT_TYPE to newrelic
+func AbortInvalidContentType(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrInvalidContentType(c, err...))
+}
+
+// AbortStatusInvalidContentType abort with status code and log common.Error_INVALID_CONTENT_TYPE to newrelic
+func AbortStatusInvalidContentType(c *gin.Context, err ...error) {
+	AbortInvalidContentType(c, 400, err...)
+}
+
+// NewErrInvalidContentEncoding create err with default message
+func NewErrInvalidContentEncoding(err ...error) *Error {
+	return New(common.Error_INVALID_CONTENT_ENCODING).With(err...)
+}
+
+// ErrInvalidContentEncoding create err with locales
+func ErrInvalidContentEncoding(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_INVALID_CONTENT_ENCODING).With(err...)
+}
+
+// AbortInvalidContentEncoding abort with status code and log common.Error_INVALID_CONTENT_ENCODING to newrelic
+func AbortInvalidContentEncoding(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrInvalidContentEncoding(c, err...))
+}
+
+// AbortStatusInvalidContentEncoding abort with status code and log common.Error_INVALID_CONTENT_ENCODING to newrelic
+func AbortStatusInvalidContentEncoding(c *gin.Context, err ...error) {
+	AbortInvalidContentEncoding(c, 400, err...)
+}
+
+// NewErrTooManyRequests create err with default message
+func NewErrTooManyRequests(err ...error) *Error {
+	return New(common.Error_TOO_MANY_REQUESTS).With(err...)
+}
+
+// ErrTooManyRequests create err with locales
+func ErrTooManyRequests(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_TOO_MANY_REQUESTS).With(err...)
+}
+
+// AbortTooManyRequests abort with status code and log common.Error_TOO_MANY_REQUESTS to newrelic
+func AbortTooManyRequests(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrTooManyRequests(c, err...))
+}
+
+// AbortStatusTooManyRequests abort with status code and log common.Error_TOO_MANY_REQUESTS to newrelic
+func AbortStatusTooManyRequests(c *gin.Context, err ...error) {
+	AbortTooManyRequests(c, 429, err...)
+}
+
+// NewErrInvalidStorageType create err with default message
+func NewErrInvalidStorageType(err ...error) *Error {
+	return New(common.Error_INVALID_STORAGE_TYPE).With(err...)
+}
+
+// ErrInvalidStorageType create err with locales
+func ErrInvalidStorageType(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_INVALID_STORAGE_TYPE).With(err...)
+}
+
+// AbortInvalidStorageType abort with status code and log common.Error_INVALID_STORAGE_TYPE to newrelic
+func AbortInvalidStorageType(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrInvalidStorageType(c, err...))
+}
+
+// AbortStatusInvalidStorageType abort with status code and log common.Error_INVALID_STORAGE_TYPE to newrelic
+func AbortStatusInvalidStorageType(c *gin.Context, err ...error) {
+	AbortInvalidStorageType(c, 400, err...)
+}
+
+// NewErrInvalidDeData create err with default message
+func NewErrInvalidDeData(err ...error) *Error {
+	return New(common.Error_INVALID_DE_DATA).With(err...)
+}
+
+// ErrInvalidDeData create err with locales
+func ErrInvalidDeData(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_INVALID_DE_DATA).With(err...)
+}
+
+// AbortInvalidDeData abort with status code and log common.Error_INVALID_DE_DATA to newrelic
+func AbortInvalidDeData(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrInvalidDeData(c, err...))
+}
+
+// AbortStatusInvalidDeData abort with status code and log common.Error_INVALID_DE_DATA to newrelic
+func AbortStatusInvalidDeData(c *gin.Context, err ...error) {
+	AbortInvalidDeData(c, 400, err...)
+}
+
+// NewErrCheckinRepeated create err with default message
+func NewErrCheckinRepeated(err ...error) *Error {
+	return New(common.Error_CHECKIN_REPEATED).With(err...)
+}
+
+// ErrCheckinRepeated create err with locales
+func ErrCheckinRepeated(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_CHECKIN_REPEATED).With(err...)
+}
+
+// AbortCheckinRepeated abort with status code and log common.Error_CHECKIN_REPEATED to newrelic
+func AbortCheckinRepeated(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrCheckinRepeated(c, err...))
+}
+
+// AbortStatusCheckinRepeated abort with status code and log common.Error_CHECKIN_REPEATED to newrelic
+func AbortStatusCheckinRepeated(c *gin.Context, err ...error) {
+	AbortCheckinRepeated(c, 400, err...)
+}
+
+// NewErrCourseNotFound create err with default message
+func NewErrCourseNotFound(err ...error) *Error {
+	return New(common.Error_COURSE_NOT_FOUND).With(err...)
+}
+
+// ErrCourseNotFound create err with locales
+func ErrCourseNotFound(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_COURSE_NOT_FOUND).With(err...)
+}
+
+// AbortCourseNotFound abort with status code and log common.Error_COURSE_NOT_FOUND to newrelic
+func AbortCourseNotFound(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrCourseNotFound(c, err...))
+}
+
+// AbortStatusCourseNotFound abort with status code and log common.Error_COURSE_NOT_FOUND to newrelic
+func AbortStatusCourseNotFound(c *gin.Context, err ...error) {
+	AbortCourseNotFound(c, 400, err...)
+}
+
+// NewErrCourseNotInterest create err with default message
+func NewErrCourseNotInterest(err ...error) *Error {
+	return New(common.Error_COURSE_NOT_INTEREST).With(err...)
+}
+
+// ErrCourseNotInterest create err with locales
+func ErrCourseNotInterest(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_COURSE_NOT_INTEREST).With(err...)
+}
+
+// AbortCourseNotInterest abort with status code and log common.Error_COURSE_NOT_INTEREST to newrelic
+func AbortCourseNotInterest(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrCourseNotInterest(c, err...))
+}
+
+// AbortStatusCourseNotInterest abort with status code and log common.Error_COURSE_NOT_INTEREST to newrelic
+func AbortStatusCourseNotInterest(c *gin.Context, err ...error) {
+	AbortCourseNotInterest(c, 500, err...)
+}
+
+// NewErrModuleNotFound create err with default message
+func NewErrModuleNotFound(err ...error) *Error {
+	return New(common.Error_MODULE_NOT_FOUND).With(err...)
+}
+
+// ErrModuleNotFound create err with locales
+func ErrModuleNotFound(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_MODULE_NOT_FOUND).With(err...)
+}
+
+// AbortModuleNotFound abort with status code and log common.Error_MODULE_NOT_FOUND to newrelic
+func AbortModuleNotFound(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrModuleNotFound(c, err...))
+}
+
+// AbortStatusModuleNotFound abort with status code and log common.Error_MODULE_NOT_FOUND to newrelic
+func AbortStatusModuleNotFound(c *gin.Context, err ...error) {
+	AbortModuleNotFound(c, 400, err...)
+}
+
+// NewErrUserCourseAlreadyAdded create err with default message
+func NewErrUserCourseAlreadyAdded(err ...error) *Error {
+	return New(common.Error_USER_COURSE_ALREADY_ADDED).With(err...)
+}
+
+// ErrUserCourseAlreadyAdded create err with locales
+func ErrUserCourseAlreadyAdded(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_USER_COURSE_ALREADY_ADDED).With(err...)
+}
+
+// AbortUserCourseAlreadyAdded abort with status code and log common.Error_USER_COURSE_ALREADY_ADDED to newrelic
+func AbortUserCourseAlreadyAdded(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrUserCourseAlreadyAdded(c, err...))
+}
+
+// AbortStatusUserCourseAlreadyAdded abort with status code and log common.Error_USER_COURSE_ALREADY_ADDED to newrelic
+func AbortStatusUserCourseAlreadyAdded(c *gin.Context, err ...error) {
+	AbortUserCourseAlreadyAdded(c, 400, err...)
+}
+
+// NewErrUserCourseNotFound create err with default message
+func NewErrUserCourseNotFound(err ...error) *Error {
+	return New(common.Error_USER_COURSE_NOT_FOUND).With(err...)
+}
+
+// ErrUserCourseNotFound create err with locales
+func ErrUserCourseNotFound(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_USER_COURSE_NOT_FOUND).With(err...)
+}
+
+// AbortUserCourseNotFound abort with status code and log common.Error_USER_COURSE_NOT_FOUND to newrelic
+func AbortUserCourseNotFound(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrUserCourseNotFound(c, err...))
+}
+
+// AbortStatusUserCourseNotFound abort with status code and log common.Error_USER_COURSE_NOT_FOUND to newrelic
+func AbortStatusUserCourseNotFound(c *gin.Context, err ...error) {
+	AbortUserCourseNotFound(c, 400, err...)
+}
+
+// NewErrPtLimited create err with default message
+func NewErrPtLimited(err ...error) *Error {
+	return New(common.Error_PT_LIMITED).With(err...)
+}
+
+// ErrPtLimited create err with locales
+func ErrPtLimited(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_PT_LIMITED).With(err...)
+}
+
+// AbortPtLimited abort with status code and log common.Error_PT_LIMITED to newrelic
+func AbortPtLimited(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrPtLimited(c, err...))
+}
+
+// AbortStatusPtLimited abort with status code and log common.Error_PT_LIMITED to newrelic
+func AbortStatusPtLimited(c *gin.Context, err ...error) {
+	AbortPtLimited(c, 400, err...)
+}
+
+// NewErrInvalidPrice create err with default message
+func NewErrInvalidPrice(err ...error) *Error {
+	return New(common.Error_INVALID_PRICE).With(err...)
+}
+
+// ErrInvalidPrice create err with locales
+func ErrInvalidPrice(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_INVALID_PRICE).With(err...)
+}
+
+// AbortInvalidPrice abort with status code and log common.Error_INVALID_PRICE to newrelic
+func AbortInvalidPrice(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrInvalidPrice(c, err...))
+}
+
+// AbortStatusInvalidPrice abort with status code and log common.Error_INVALID_PRICE to newrelic
+func AbortStatusInvalidPrice(c *gin.Context, err ...error) {
+	AbortInvalidPrice(c, 400, err...)
+}
+
+// NewErrInvalidProductId create err with default message
+func NewErrInvalidProductId(err ...error) *Error {
+	return New(common.Error_INVALID_PRODUCT_ID).With(err...)
+}
+
+// ErrInvalidProductId create err with locales
+func ErrInvalidProductId(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_INVALID_PRODUCT_ID).With(err...)
+}
+
+// AbortInvalidProductId abort with status code and log common.Error_INVALID_PRODUCT_ID to newrelic
+func AbortInvalidProductId(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrInvalidProductId(c, err...))
+}
+
+// AbortStatusInvalidProductId abort with status code and log common.Error_INVALID_PRODUCT_ID to newrelic
+func AbortStatusInvalidProductId(c *gin.Context, err ...error) {
+	AbortInvalidProductId(c, 400, err...)
+}
+
+// NewErrInvalidOrderNumber create err with default message
+func NewErrInvalidOrderNumber(err ...error) *Error {
+	return New(common.Error_INVALID_ORDER_NUMBER).With(err...)
+}
+
+// ErrInvalidOrderNumber create err with locales
+func ErrInvalidOrderNumber(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_INVALID_ORDER_NUMBER).With(err...)
+}
+
+// AbortInvalidOrderNumber abort with status code and log common.Error_INVALID_ORDER_NUMBER to newrelic
+func AbortInvalidOrderNumber(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrInvalidOrderNumber(c, err...))
+}
+
+// AbortStatusInvalidOrderNumber abort with status code and log common.Error_INVALID_ORDER_NUMBER to newrelic
+func AbortStatusInvalidOrderNumber(c *gin.Context, err ...error) {
+	AbortInvalidOrderNumber(c, 400, err...)
+}
+
+// NewErrInvalidUserId create err with default message
+func NewErrInvalidUserId(err ...error) *Error {
+	return New(common.Error_INVALID_USER_ID).With(err...)
+}
+
+// ErrInvalidUserId create err with locales
+func ErrInvalidUserId(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_INVALID_USER_ID).With(err...)
+}
+
+// AbortInvalidUserId abort with status code and log common.Error_INVALID_USER_ID to newrelic
+func AbortInvalidUserId(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrInvalidUserId(c, err...))
+}
+
+// AbortStatusInvalidUserId abort with status code and log common.Error_INVALID_USER_ID to newrelic
+func AbortStatusInvalidUserId(c *gin.Context, err ...error) {
+	AbortInvalidUserId(c, 400, err...)
+}
+
+// NewErrInvalidReceipt create err with default message
+func NewErrInvalidReceipt(err ...error) *Error {
+	return New(common.Error_INVALID_RECEIPT).With(err...)
+}
+
+// ErrInvalidReceipt create err with locales
+func ErrInvalidReceipt(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_INVALID_RECEIPT).With(err...)
+}
+
+// AbortInvalidReceipt abort with status code and log common.Error_INVALID_RECEIPT to newrelic
+func AbortInvalidReceipt(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrInvalidReceipt(c, err...))
+}
+
+// AbortStatusInvalidReceipt abort with status code and log common.Error_INVALID_RECEIPT to newrelic
+func AbortStatusInvalidReceipt(c *gin.Context, err ...error) {
+	AbortInvalidReceipt(c, 400, err...)
+}
+
+// NewErrEmptyIosReceipt create err with default message
+func NewErrEmptyIosReceipt(err ...error) *Error {
+	return New(common.Error_EMPTY_IOS_RECEIPT).With(err...)
+}
+
+// ErrEmptyIosReceipt create err with locales
+func ErrEmptyIosReceipt(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_EMPTY_IOS_RECEIPT).With(err...)
+}
+
+// AbortEmptyIosReceipt abort with status code and log common.Error_EMPTY_IOS_RECEIPT to newrelic
+func AbortEmptyIosReceipt(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrEmptyIosReceipt(c, err...))
+}
+
+// AbortStatusEmptyIosReceipt abort with status code and log common.Error_EMPTY_IOS_RECEIPT to newrelic
+func AbortStatusEmptyIosReceipt(c *gin.Context, err ...error) {
+	AbortEmptyIosReceipt(c, 500, err...)
+}
+
+// NewErrUserPlanLimitedCourseCount create err with default message
+func NewErrUserPlanLimitedCourseCount(err ...error) *Error {
+	return New(common.Error_USER_PLAN_LIMITED_COURSE_COUNT).With(err...)
+}
+
+// ErrUserPlanLimitedCourseCount create err with locales
+func ErrUserPlanLimitedCourseCount(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_USER_PLAN_LIMITED_COURSE_COUNT).With(err...)
+}
+
+// AbortUserPlanLimitedCourseCount abort with status code and log common.Error_USER_PLAN_LIMITED_COURSE_COUNT to newrelic
+func AbortUserPlanLimitedCourseCount(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrUserPlanLimitedCourseCount(c, err...))
+}
+
+// AbortStatusUserPlanLimitedCourseCount abort with status code and log common.Error_USER_PLAN_LIMITED_COURSE_COUNT to newrelic
+func AbortStatusUserPlanLimitedCourseCount(c *gin.Context, err ...error) {
+	AbortUserPlanLimitedCourseCount(c, 500, err...)
+}
+
+// NewErrInternalError create err with default message
+func NewErrInternalError(err ...error) *Error {
+	return New(common.Error_INTERNAL_ERROR).With(err...)
+}
+
+// ErrInternalError create err with locales
+func ErrInternalError(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_INTERNAL_ERROR).With(err...)
+}
+
+// AbortInternalError abort with status code and log common.Error_INTERNAL_ERROR to newrelic
+func AbortInternalError(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrInternalError(c, err...))
+}
+
+// AbortStatusInternalError abort with status code and log common.Error_INTERNAL_ERROR to newrelic
+func AbortStatusInternalError(c *gin.Context, err ...error) {
+	AbortInternalError(c, 500, err...)
+}
+
+// NewErrJsonpbError create err with default message
+func NewErrJsonpbError(err ...error) *Error {
+	return New(common.Error_JSONPB_ERROR).With(err...)
+}
+
+// ErrJsonpbError create err with locales
+func ErrJsonpbError(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_JSONPB_ERROR).With(err...)
+}
+
+// AbortJsonpbError abort with status code and log common.Error_JSONPB_ERROR to newrelic
+func AbortJsonpbError(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrJsonpbError(c, err...))
+}
+
+// AbortStatusJsonpbError abort with status code and log common.Error_JSONPB_ERROR to newrelic
+func AbortStatusJsonpbError(c *gin.Context, err ...error) {
+	AbortJsonpbError(c, 400, err...)
+}
+
+// NewErrJsonError create err with default message
+func NewErrJsonError(err ...error) *Error {
+	return New(common.Error_JSON_ERROR).With(err...)
+}
+
+// ErrJsonError create err with locales
+func ErrJsonError(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_JSON_ERROR).With(err...)
+}
+
+// AbortJsonError abort with status code and log common.Error_JSON_ERROR to newrelic
+func AbortJsonError(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrJsonError(c, err...))
+}
+
+// AbortStatusJsonError abort with status code and log common.Error_JSON_ERROR to newrelic
+func AbortStatusJsonError(c *gin.Context, err ...error) {
+	AbortJsonError(c, 400, err...)
+}
+
+// NewErrPbError create err with default message
+func NewErrPbError(err ...error) *Error {
+	return New(common.Error_PB_ERROR).With(err...)
+}
+
+// ErrPbError create err with locales
+func ErrPbError(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_PB_ERROR).With(err...)
+}
+
+// AbortPbError abort with status code and log common.Error_PB_ERROR to newrelic
+func AbortPbError(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrPbError(c, err...))
+}
+
+// AbortStatusPbError abort with status code and log common.Error_PB_ERROR to newrelic
+func AbortStatusPbError(c *gin.Context, err ...error) {
+	AbortPbError(c, 400, err...)
+}
+
+// NewErrExternalError create err with default message
+func NewErrExternalError(err ...error) *Error {
+	return New(common.Error_EXTERNAL_ERROR).With(err...)
+}
+
+// ErrExternalError create err with locales
+func ErrExternalError(c *gin.Context, err ...error) *Error {
+	return WithContext(c, common.Error_EXTERNAL_ERROR).With(err...)
+}
+
+// AbortExternalError abort with status code and log common.Error_EXTERNAL_ERROR to newrelic
+func AbortExternalError(c *gin.Context, code int, err ...error) {
+	for _, e := range err {
+		// if err is nil, gin will panic
+		if e != nil {
+			// so many 4xx error in new relic, only ignore 400/401/404 error, have fun!
+			if !shouldIgnoreCode(code) {
+				c.Error(e)
+			}
+		}
+	}
+	c.AbortWithStatusJSON(code, ErrExternalError(c, err...))
+}
+
+// AbortStatusExternalError abort with status code and log common.Error_EXTERNAL_ERROR to newrelic
+func AbortStatusExternalError(c *gin.Context, err ...error) {
+	AbortExternalError(c, 500, err...)
+}

+ 47 - 0
pkg/apierr/config.go

@@ -0,0 +1,47 @@
+package apierr
+
+import (
+	"net/http"
+	"sort"
+
+	"github.com/gin-gonic/gin"
+
+	"kpt-tmr-group/pkg/grpcutil"
+	"kpt-tmr-group/pkg/xerr"
+)
+
+var (
+	ignoreCodes = []int{400, 401, 404, 429}
+	codesSize   = int(3)
+)
+
+// IgnoreStatusCodes ignore error with status codes
+func IgnoreStatusCodes(codes ...int) {
+	sort.Ints(codes)
+
+	ignoreCodes = codes
+	codesSize = len(codes)
+}
+
+func shouldIgnoreCode(code int) bool {
+	// SearchInts using binary search, more fast
+	idx := sort.SearchInts(ignoreCodes, code)
+	return idx < codesSize && ignoreCodes[idx] == code
+}
+
+// ClassifiedAbort classify base on error type
+func ClassifiedAbort(c *gin.Context, err error) {
+	if err == nil {
+		return
+	}
+
+	if e, isc := xerr.IsCustomError(err); isc {
+		// 服务内部自定义错误
+		AbortBadRequest(c, http.StatusBadRequest, e)
+	} else if !grpcutil.FilterServerError(err) {
+		// gRPC 部分错误
+		AbortBadRequest(c, http.StatusBadRequest, err)
+	} else {
+		AbortError(c, err)
+	}
+}

+ 23 - 0
pkg/apiok/common.go

@@ -0,0 +1,23 @@
+package apiok
+
+type OkResponse struct {
+	Code   int         `json:"code"`
+	Msg    string      `json:"msg"`
+	Result interface{} `json:"result"`
+}
+
+func CommonResponse(data interface{}) *OkResponse {
+	return &OkResponse{
+		Code:   200,
+		Msg:    "ok",
+		Result: data,
+	}
+}
+
+type ApiOk struct {
+	Success bool `json:"success"`
+}
+
+func NewApiOk(success bool) *ApiOk {
+	return &ApiOk{Success: success}
+}

+ 5 - 5
util/cleanup/entry.go → pkg/cleanup/entry.go

@@ -23,8 +23,8 @@ import (
 	"reflect"
 	"sync"
 
-	log "kpt-tmr-group/util/logger"
-	"kpt-tmr-group/util/xerr"
+	"kpt-tmr-group/pkg/logger/logrus"
+	"kpt-tmr-group/pkg/xerr"
 )
 
 // entry global cleanup entry
@@ -48,7 +48,7 @@ type Entry struct {
 
 // Run runs all the cleanup functions registered.
 func (entry *Entry) Run() {
-	log.Infof("cleanup: performing %d cleanups", len(entry.fns))
+	logrus.Infof("cleanup: performing %d cleanups", len(entry.fns))
 
 	entry.once.Do(func() {
 		for _, f := range entry.fns {
@@ -56,7 +56,7 @@ func (entry *Entry) Run() {
 		}
 	})
 
-	log.Infof("cleanup: all done")
+	logrus.Infof("cleanup: all done")
 }
 
 // Register adds a function to the cleanup queue.
@@ -92,7 +92,7 @@ func (entry *Entry) RegisterStruct(ctor interface{}) error {
 		}
 		if method.IsValid() {
 			if err := entry.RegisterFunc(method.Interface()); err != nil {
-				log.WithError(err).WithField("fieldName", field.Type().Name()).Error("register func failed")
+				logrus.WithError(err).WithField("fieldName", field.Type().Name()).Error("register func failed")
 			}
 		}
 	}

+ 0 - 0
util/cleanup/entry_test.go → pkg/cleanup/entry_test.go


+ 0 - 0
util/cputil/cp.go → pkg/cputil/cp.go


+ 0 - 0
util/cputil/cp_test.go → pkg/cputil/cp_test.go


+ 0 - 0
util/di/annotation.go → pkg/di/annotation.go


+ 3 - 3
util/di/hub.go → pkg/di/hub.go

@@ -3,9 +3,9 @@ package di
 import (
 	"reflect"
 
-	"kpt-tmr-group/util/cleanup"
-	"kpt-tmr-group/util/di/xreflect"
-	"kpt-tmr-group/util/xerr"
+	"kpt-tmr-group/pkg/cleanup"
+	"kpt-tmr-group/pkg/di/xreflect"
+	"kpt-tmr-group/pkg/xerr"
 
 	"go.uber.org/dig"
 )

+ 0 - 0
util/di/hub_test.go → pkg/di/hub_test.go


+ 1 - 1
util/di/option.go → pkg/di/option.go

@@ -4,7 +4,7 @@ import (
 	"fmt"
 	"strings"
 
-	"kpt-tmr-group/util/di/xreflect"
+	"kpt-tmr-group/pkg/di/xreflect"
 
 	"go.uber.org/dig"
 )

+ 0 - 0
util/di/xreflect/reflect.go → pkg/di/xreflect/reflect.go


+ 0 - 0
util/di/xreflect/reflect_test.go → pkg/di/xreflect/reflect_test.go


+ 0 - 0
util/di/xreflect/stack.go → pkg/di/xreflect/stack.go


+ 0 - 0
util/di/xreflect/stack_test.go → pkg/di/xreflect/stack_test.go


+ 57 - 0
pkg/grpcutil/client_conn.go

@@ -0,0 +1,57 @@
+package grpcutil
+
+import (
+	"context"
+	"time"
+
+	"kpt-tmr-group/pkg/xerr"
+
+	grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
+	grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry"
+	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/codes"
+)
+
+func DialContext(ctx context.Context, target string, options ...grpc.DialOption) (*grpc.ClientConn, error) {
+	if len(options) == 0 {
+		options = DefaultDialOptions()
+	}
+
+	conn, err := grpc.DialContext(ctx, target, options...)
+	if err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	return conn, err
+}
+
+func DefaultDialOptions() []grpc.DialOption {
+	options := DefaultAsyncDialOptions()
+	return append(options, grpc.WithBlock())
+}
+
+func DefaultAsyncDialOptions() []grpc.DialOption {
+	unaryInterceptors := []grpc.UnaryClientInterceptor{
+		grpc_retry.UnaryClientInterceptor(
+			grpc_retry.WithMax(3),
+			grpc_retry.WithCodes(codes.Aborted, codes.DeadlineExceeded),
+			grpc_retry.WithPerRetryTimeout(time.Millisecond*500),
+		),
+		grpc_prometheus.UnaryClientInterceptor,
+	}
+	streamInterceptors := []grpc.StreamClientInterceptor{
+		grpc_retry.StreamClientInterceptor(
+			grpc_retry.WithMax(3),
+			grpc_retry.WithCodes(codes.Aborted, codes.DeadlineExceeded),
+			grpc_retry.WithPerRetryTimeout(time.Millisecond*500),
+		),
+		grpc_prometheus.StreamClientInterceptor,
+	}
+
+	return []grpc.DialOption{
+		grpc.WithInsecure(),
+		grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(unaryInterceptors...)),
+		grpc.WithStreamInterceptor(grpc_middleware.ChainStreamClient(streamInterceptors...)),
+	}
+}

+ 61 - 0
pkg/grpcutil/error.go

@@ -0,0 +1,61 @@
+package grpcutil
+
+import (
+	"errors"
+	"reflect"
+
+	"kpt-tmr-group/pkg/xerr"
+
+	spb "google.golang.org/genproto/googleapis/rpc/status"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
+)
+
+// StatusError cause of grpc status error
+func StatusError(err error) (*status.Status, bool) {
+	statusErr, ok := status.FromError(xerr.Cause(err))
+	if !ok {
+		return nil, false
+	}
+
+	return statusErr, true
+}
+
+// FilterServerError 优化服务端错误上报逻辑
+func FilterServerError(err error) bool {
+	statusErr, ok := StatusError(err)
+	if !ok {
+		return true
+	}
+
+	details := statusErr.Details()
+	for _, detail := range details {
+		if _, ok := detail.(*spb.Status); ok && statusErr.Code() != codes.Canceled {
+			return false
+		}
+	}
+
+	ignoreStatus := []codes.Code{codes.InvalidArgument, codes.Unauthenticated, codes.NotFound, codes.FailedPrecondition, codes.AlreadyExists}
+	if inArray, _ := In(statusErr.Code(), ignoreStatus); inArray {
+		return false
+	}
+	return true
+}
+
+func In(obj interface{}, target interface{}) (bool, error) {
+	targetValue := reflect.ValueOf(target)
+	switch reflect.TypeOf(target).Kind() {
+	case reflect.Slice, reflect.Array:
+		for i := 0; i < targetValue.Len(); i++ {
+			if targetValue.Index(i).Interface() == obj {
+				return true, nil
+			}
+		}
+	case reflect.Map:
+		if targetValue.MapIndex(reflect.ValueOf(obj)).IsValid() {
+			return true, nil
+		}
+	}
+
+	return false, errors.New("not in array")
+}

+ 22 - 0
pkg/grpcutil/grpc_error.go

@@ -0,0 +1,22 @@
+package grpcutil
+
+import (
+	"fmt"
+
+	"kpt-tmr-group/pkg/xerr"
+
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
+)
+
+func ClassifyError(err error, request interface{}) error {
+	if err == nil {
+		return nil
+	}
+	_, isCus := xerr.IsCustomError(err)
+	if isCus {
+		return status.Error(codes.InvalidArgument, xerr.WrapWithLog(fmt.Errorf("err: %v, request: %+v", err, request)).Error())
+	} else {
+		return status.Error(codes.Internal, xerr.WrapWithLog(fmt.Errorf("err: %v, request: %+v", err, request)).Error())
+	}
+}

+ 16 - 4
util/logger/log.go → pkg/logger/logrus/log.go

@@ -1,14 +1,21 @@
-package log
+package logrus
 
 import (
 	"fmt"
-	"os"
+	"kpt-tmr-group/pkg/tool"
 	"path"
 	"runtime"
+	"time"
 
+	rotatelogs "github.com/lestrrat-go/file-rotatelogs"
 	"github.com/sirupsen/logrus"
 )
 
+var (
+	logPath     = "./logger"
+	logFileName = fmt.Sprintf("/logrus-%s.log", time.Now().Format(tool.DateTime))
+)
+
 func init() {
 	// Log as JSON instead of the default ASCII formatter.
 	logrus.SetFormatter(&logrus.JSONFormatter{
@@ -20,8 +27,13 @@ func init() {
 
 	// Output to stdout instead of the default stderr
 	// Can be any io.Writer, see below for File example
-	logrus.SetOutput(os.Stdout)
-
+	writer, _ := rotatelogs.New(
+		fmt.Sprintf("%s%s", logPath, logFileName),
+		// rotatelogs.WithLinkName(logPath),
+		rotatelogs.WithMaxAge(time.Duration(7*24)*time.Hour),     // 备份7天的日志
+		rotatelogs.WithRotationTime(time.Duration(24)*time.Hour), // 24小时切割一次日志
+	)
+	logrus.SetOutput(writer) // logrus 设置日志的输出方式
 	// Only log the warning severity or above.
 	logrus.SetLevel(DebugLevel)
 }

+ 84 - 0
pkg/logger/zaplog/log.go

@@ -0,0 +1,84 @@
+package zaplog
+
+import (
+	"fmt"
+	"kpt-tmr-group/pkg/tool"
+	"path"
+	"runtime"
+	"time"
+
+	"github.com/natefinch/lumberjack"
+
+	"go.uber.org/zap"
+	"go.uber.org/zap/zapcore"
+)
+
+var (
+	Logger      *zap.Logger
+	logFileName = fmt.Sprintf("./logger/zap-%s.log", time.Now().Format(tool.DateTime))
+)
+
+func init() {
+	encoderConfig := zap.NewDevelopmentEncoderConfig()
+	// 设置日志时间格式
+	encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
+	// 日志encoder 还是json encode,把日志进行格式化json格式的
+	encoder := zapcore.NewJSONEncoder(encoderConfig)
+
+	// topicErrors := zapcore.AddSync(ioutil.Discard)  //kafka topic
+
+	fileWriteSyncer := getFileLogWriter(logFileName)
+	core := zapcore.NewTee(
+		// zapcore.NewCore(encoder, zapcore.AddSync(os.Stdout), zapcore.DebugLevel),  // 打印到控制台
+		// zapcore.NewCore(zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()), topicErrors, zapcore.ErrorLevel),   // 打印到kafka 待验证
+		zapcore.NewCore(encoder, fileWriteSyncer, zapcore.DebugLevel), // 打印到指定的日志文件
+	)
+	Logger = zap.New(core)
+}
+
+func getFileLogWriter(logFileName string) zapcore.WriteSyncer {
+	lumberJackLogger := &lumberjack.Logger{
+		Filename:   logFileName,
+		MaxSize:    100, // 单个文件最大100M
+		MaxBackups: 10,  // 大于60个日志文件后,清理比较旧的日志文件
+		MaxAge:     1,   // 一天切割1次
+		Compress:   false,
+	}
+	return zapcore.AddSync(lumberJackLogger)
+}
+
+func getCallerInfoForLog() (callerFields []zap.Field) {
+	pc, file, line, ok := runtime.Caller(2)
+	if !ok {
+		return
+	}
+	funcName := runtime.FuncForPC(pc).Name()
+	funcName = path.Base(funcName)
+
+	callerFields = append(callerFields, zap.String("func", funcName), zap.String("file", file), zap.Int("line", line))
+	return
+}
+
+func Info(message string, fields ...zap.Field) {
+	callerFields := getCallerInfoForLog()
+	fields = append(fields, callerFields...)
+	Logger.Info(message, fields...)
+}
+
+func Debug(message string, fields ...zap.Field) {
+	callerFields := getCallerInfoForLog()
+	fields = append(fields, callerFields...)
+	Logger.Debug(message, fields...)
+}
+
+func Error(message string, fields ...zap.Field) {
+	callerFields := getCallerInfoForLog()
+	fields = append(fields, callerFields...)
+	Logger.Error(message, fields...)
+}
+
+func Warn(message string, fields ...zap.Field) {
+	callerFields := getCallerInfoForLog()
+	fields = append(fields, callerFields...)
+	Logger.Warn(message, fields...)
+}

+ 0 - 0
util/runtimeutil/caller.go → pkg/runtimeutil/caller.go


+ 0 - 0
util/runtimeutil/caller_test.go → pkg/runtimeutil/caller_test.go


+ 1 - 1
util/sentry/sentry.go → pkg/sentry/sentry.go

@@ -2,7 +2,7 @@ package sentry
 
 import (
 	"context"
-	log "kpt-tmr-group/util/logger"
+	log "kpt-tmr-group/pkg/logger/logrus"
 
 	"github.com/getsentry/sentry-go"
 )

+ 244 - 0
pkg/tool/tool.go

@@ -0,0 +1,244 @@
+package tool
+
+import (
+	"crypto/md5"
+	"encoding/hex"
+	"fmt"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+)
+
+const (
+	Layout          = "2006-01-02 15:04:05"
+	DateTime        = "2006-01-02"
+	DefaultExecTime = "0_0_0"
+)
+
+// StringToTimeUnix 时间字符串转换成时间戳
+// a.g 6_2_3  ===> 转换成 6天2小时3分钟后的时间戳
+// TODO 需要优化代码写得太死,需要优化后兼容秒的场景
+func StringToTimeUnix(execTimeStr string) int64 {
+	var processAt = time.Now().Unix()
+	if execTimeStr == DefaultExecTime {
+		return processAt
+	}
+
+	execTime := strings.Split(execTimeStr, "_")
+	if len(execTime) < 3 {
+		return processAt
+	}
+
+	// 天数
+	days, _ := strconv.Atoi(execTime[0])
+	processAt += int64(days) * 24 * 60 * 60
+	// 小时
+	hours, _ := strconv.Atoi(execTime[1])
+	processAt += int64(hours) * 60 * 60
+
+	// 分钟
+	minutes, _ := strconv.Atoi(execTime[2])
+	processAt += int64(minutes) * 60
+
+	return processAt
+}
+
+func GetLocalTime(timeStr string) time.Time {
+	execTime, _ := time.ParseInLocation(Layout, timeStr, time.Local)
+	return execTime
+}
+
+// GetTargetByValueForTag 利用反射根据cond获取对应字段数据,主要tag必须为gorm
+// e.g person{Name:"ping",Age:12}
+// e.g GetTargetByTag(person,age) ==> 12
+func GetTargetByValueForTag(target interface{}, cond string) interface{} {
+	tf := reflect.Value{}
+	// 判断是不是指针类型
+	if reflect.TypeOf(target).Kind() == reflect.Ptr {
+		if reflect.ValueOf(target).IsNil() { // 空指针
+			return ""
+		}
+		tf = reflect.Indirect(reflect.ValueOf(target))
+	} else {
+		tf = reflect.ValueOf(target)
+	}
+
+	typ := tf.Type()
+	var condValue interface{}
+	for k := 0; k < typ.NumField(); k++ {
+		key := typ.Field(k).Tag.Get("gorm")
+		if key != cond {
+			continue
+		}
+		field := typ.Field(k).Name
+		switch tf.FieldByName(field).Kind() {
+		case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+			condValue = tf.FieldByName(field).Int()
+		case reflect.String:
+			condValue = tf.FieldByName(field).String()
+		}
+	}
+	return condValue
+}
+
+// GetTargetByTypeForTag 利用反射根据cond获取对应的字段类型默认值
+// e.g person{Name:"ping",Age:12,EventStatus: fieldPb.EventStatus_ADMISSION}
+// e.g GetTargetByTypeForTag(person,age) ==> person{Name:"ping",Age:0,EventStatus: fieldPb.EventStatus_ADMISSION}
+// e.g GetTargetByTypeForTag(person,name) ==> person{Name:"",Age:12,EventStatus: fieldPb.EventStatus_ADMISSION}
+// e.g GetTargetByTypeForTag(person,event_status) ==> person{Name:"ping",Age:12,EventStatus: fieldPb.EventStatus_INVALID}
+func GetTargetByTypeForTag(target interface{}, cond string) {
+	tf := reflect.TypeOf(target)
+	// 判断是不是指针类型
+	if tf.Kind() == reflect.Ptr {
+		tf = tf.Elem()
+	}
+	tv := reflect.ValueOf(target)
+	if tv.Kind() == reflect.Ptr {
+		tv = tv.Elem()
+	}
+	for i := 0; i < tf.NumField(); i++ {
+		key := tf.Field(i).Tag.Get("gorm")
+		if key != cond {
+			continue
+		}
+		fieldName := tf.Field(i).Name
+		tfKind := tf.Field(i).Type
+
+		if tfKind.Kind() == reflect.String {
+			tv.FieldByName(fieldName).SetString("")
+		}
+
+		if tfKind.Kind() == reflect.Int || tfKind.Kind() == reflect.Int8 || tfKind.Kind() == reflect.Int16 ||
+			tfKind.Kind() == reflect.Int32 || tfKind.Kind() == reflect.Int64 {
+			tv.FieldByName(fieldName).SetInt(0)
+		}
+
+		if tfKind.Kind() == reflect.Uint || tfKind.Kind() == reflect.Uint8 || tfKind.Kind() == reflect.Uint16 ||
+			tfKind.Kind() == reflect.Uint32 || tfKind.Kind() == reflect.Uint64 {
+			tv.FieldByName(fieldName).SetUint(0)
+		}
+		if tfKind.Kind() == reflect.Float32 || tfKind.Kind() == reflect.Float64 {
+			tv.FieldByName(fieldName).SetFloat(0)
+		}
+	}
+}
+
+// MonthLastDay 获取当月最后一天
+//eg 2023-01 => 2023-01-31
+//eg 2023-02 => 2023-02-28
+func MonthLastDay(yearMonth string) string {
+	item := strings.Split(yearMonth, "-")
+	if len(item) < 2 {
+		return ""
+	}
+
+	year, _ := strconv.Atoi(item[0])
+	loc, _ := time.LoadLocation("Local")
+	theTime, _ := time.ParseInLocation(Layout, fmt.Sprintf("%s-01 00:00:00", yearMonth), loc)
+
+	return time.Date(year, theTime.Month()+1, 0, 0, 0, 0, 0, time.Local).Format("2006-01-02")
+}
+
+// TimeParseLocalUnix 获取当天零点的时间戳
+// eg 2023-02-22 => 1676995200
+func TimeParseLocalUnix(DayTime string) int64 {
+	value := DayTime
+	if len(DayTime) <= 11 {
+		value = fmt.Sprintf("%s 00:00:00", DayTime)
+	}
+
+	loc, _ := time.LoadLocation("Local")
+	theTime, _ := time.ParseInLocation(Layout, value, loc)
+	return theTime.Unix()
+}
+
+// ColumNameValueJoinSqlString 获取columName对应的值拼接成sql语句
+// eg columName = "a,b,c"  ==> ('1','2','3')
+func ColumNameValueJoinSqlString(columName string, target interface{}) string {
+	columNames := strings.Split(columName, ",")
+	insertValue := "("
+	if target == nil {
+		return insertValue
+	}
+
+	for _, c := range columNames {
+		val := reflect.Value{}
+		if reflect.TypeOf(target).Kind() == reflect.Ptr {
+			val = reflect.Indirect(reflect.ValueOf(target))
+		} else {
+			val = reflect.ValueOf(target)
+		}
+
+		typ := val.Type()
+		strValue := ""
+		for k := 0; k < typ.NumField(); k++ {
+			key := typ.Field(k).Tag.Get("json")
+			key = strings.ReplaceAll(key, ",omitempty", "")
+			if key != c {
+				continue
+			}
+			field := typ.Field(k).Name
+			switch val.FieldByName(field).Kind() {
+			case reflect.String:
+				strValue = fmt.Sprintf("'%s'", val.FieldByName(field).String())
+			case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+				strValue = fmt.Sprintf("%d", val.FieldByName(field).Int())
+			case reflect.Float64, reflect.Float32:
+				strValue = fmt.Sprintf("%f", val.FieldByName(field).Float())
+			}
+		}
+
+		insertValue += fmt.Sprintf("%s,", strValue)
+	}
+
+	return fmt.Sprintf("%s)", strings.TrimRight(insertValue, ","))
+}
+
+// ColumNameValue 获取columName 对应的值
+// eg a => 1
+func ColumNameValue(columName string, target interface{}) string {
+	strValue := ""
+	if target == nil {
+		return strValue
+	}
+
+	val := reflect.Value{}
+	if reflect.TypeOf(target).Kind() == reflect.Ptr {
+		val = reflect.Indirect(reflect.ValueOf(target))
+	} else {
+		val = reflect.ValueOf(target)
+	}
+
+	typ := val.Type()
+	for k := 0; k < typ.NumField(); k++ {
+		key := typ.Field(k).Tag.Get("json")
+		key = strings.ReplaceAll(key, ",omitempty", "")
+		if key != columName {
+			continue
+		}
+		field := typ.Field(k).Name
+		switch val.FieldByName(field).Kind() {
+		case reflect.String:
+			strValue = fmt.Sprintf("'%s'", val.FieldByName(field).String())
+		case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+			strValue = fmt.Sprintf("%d", val.FieldByName(field).Int())
+		case reflect.Float64, reflect.Float32:
+			strValue = fmt.Sprintf("%f", val.FieldByName(field).Float())
+		}
+	}
+
+	return strValue
+}
+
+// UnixTimeString unix时间戳转化成string  eg 1673939363 => 2023-01-17
+func UnixTimeString(unixTime int64) string {
+	return time.Unix(unixTime, 0).Format(DateTime)
+}
+
+func Md5String(input string) string {
+	s := md5.New()
+	digest := strings.ReplaceAll(string(input), "\n", "")
+	s.Write([]byte(digest))
+	return hex.EncodeToString(s.Sum(nil))
+}

+ 124 - 0
pkg/tool/tool_test.go

@@ -0,0 +1,124 @@
+package tool
+
+import (
+	"fmt"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestConditionsInterpreter(t *testing.T) {
+	conditions := `[
+    {
+        "condition_item": [
+            {
+                "condition_name": "aaa",
+                "conditions_kind": 2,
+                "conditions_value": "11111"
+            },
+            {
+                "condition_name": "bbb",
+                "conditions_kind": 1,
+                "conditions_value": "22222"
+            }
+        ]
+    },
+    {
+        "condition_item": [
+            {
+                "condition_name": "ccc",
+                "conditions_kind": 4,
+                "conditions_value": "3333"
+            },
+            {
+                "condition_name": "ddddd",
+                "conditions_kind": 1,
+                "conditions_value": "4444"
+            }
+        ]
+    }
+]`
+	t.Run("ok", func(t *testing.T) {
+		sql, err := ConditionsInterpreter(conditions)
+		if err != nil {
+			t.Error(err)
+		}
+		want := "( aaa != '11111'  AND bbb = '22222' ) OR ( ccc <= '3333'  AND ddddd = '4444' )"
+		assert.Equal(t, sql, want)
+	})
+
+}
+
+func TestTimeParseLocalUnix(t *testing.T) {
+	t.Run("ok", func(t *testing.T) {
+		testMap := map[string]int64{
+			"2023-02-22": 1676995200,
+			"2023-02-23": 1677081600,
+		}
+		for localTime, timestamp := range testMap {
+			assert.Equal(t, TimeParseLocalUnix(localTime), timestamp)
+		}
+	})
+}
+
+func TestGetTargetByValueForTag(t *testing.T) {
+
+}
+
+func TestStringToTimeUnix(t *testing.T) {
+	// 测试分钟维度
+	t.Run("minute", func(t *testing.T) {
+		for i := 0; i < 10; i++ {
+			execTime := fmt.Sprintf("0_0_%d", i)
+			want := time.Now().Add(time.Duration(i) * time.Minute).Unix()
+			got := StringToTimeUnix(execTime)
+			assert.Equal(t, want, got)
+		}
+	})
+
+	// 测试小时维度
+	t.Run("hour", func(t *testing.T) {
+		for i := 0; i < 10; i++ {
+			execTime := fmt.Sprintf("0_%d_0", i)
+			want := time.Now().Add(time.Duration(i) * time.Hour).Unix()
+			got := StringToTimeUnix(execTime)
+			assert.Equal(t, want, got)
+		}
+	})
+
+	// 测试天维度
+	t.Run("day", func(t *testing.T) {
+		for i := 0; i < 10; i++ {
+			execTime := fmt.Sprintf("%d_0_0", i)
+			want := time.Now().Add(time.Duration(i) * time.Hour * 24).Unix()
+			got := StringToTimeUnix(execTime)
+			assert.Equal(t, want, got)
+		}
+	})
+}
+
+func TestGetLocalTime(t *testing.T) {
+	t.Run("ok", func(t *testing.T) {
+		tests := []struct {
+			PushTimeStr string
+			Got         int64
+		}{
+			{
+				PushTimeStr: "2023-02-28 09:30:00",
+				Got:         1677547800,
+			},
+		}
+		for _, tt := range tests {
+			want := GetLocalTime(tt.PushTimeStr)
+			assert.Equal(t, want.Unix(), tt.Got)
+		}
+	})
+}
+
+func TestDemo(t *testing.T) {
+
+	t.Run("ok", func(t *testing.T) {
+
+	})
+}

+ 2 - 2
util/xerr/custom_error.go → pkg/xerr/custom_error.go

@@ -5,7 +5,7 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/lunny/log"
+	"kpt-tmr-group/pkg/logger/logrus"
 )
 
 // Custom biz custom error
@@ -49,7 +49,7 @@ func IgnoreDuplicateEntryError(err error, msg ...interface{}) error {
 	if IsUniqueError(err) {
 		if len(msg) != 0 {
 			bs, _ := json.Marshal(msg)
-			log.Debugf("ignore duplicate record %s", string(bs))
+			logrus.Debugf("ignore duplicate record %s", string(bs))
 		}
 
 		return nil

+ 0 - 0
util/xerr/custom_error_test.go → pkg/xerr/custom_error_test.go


+ 0 - 0
util/xerr/errors.go → pkg/xerr/errors.go


+ 0 - 0
util/xerr/errors_test.go → pkg/xerr/errors_test.go


+ 0 - 0
util/xerr/stack.go → pkg/xerr/stack.go


+ 0 - 0
util/xerr/stack_test.go → pkg/xerr/stack_test.go


+ 3 - 7
util/xerr/xerr.go → pkg/xerr/xerr.go

@@ -2,11 +2,10 @@ package xerr
 
 import (
 	"context"
-	log "kpt-tmr-group/util/logger"
-	"kpt-tmr-group/util/sentry"
+	log "kpt-tmr-group/pkg/logger/logrus"
+	"kpt-tmr-group/pkg/runtimeutil"
+	"kpt-tmr-group/pkg/sentry"
 	"strings"
-
-	"kpt-tmr-group/util/runtimeutil"
 )
 
 // 自定义处理过的函数放置在这里
@@ -30,9 +29,6 @@ func ReportError(ctx context.Context, err error, messages ...string) error {
 
 	e := Wrap(err, messages...)
 
-	// 上报错误到 NewRelic 并且出发报警 🚔
-	// _ = nrutil.NoticeError(getOperation(1), originErr)
-
 	// 上报错误到 Sentry 记录错误
 	sentry.ReportPanic(ctx, e)
 

+ 0 - 0
util/xerr/xerr_test.go → pkg/xerr/xerr_test.go


+ 187 - 0
proto/go/backend/common/enum.pb.go

@@ -0,0 +1,187 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.28.1
+// 	protoc        v3.21.9
+// source: backend/common/enum.proto
+
+package commonPb
+
+import (
+	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)
+)
+
+type IsShow_Kind int32
+
+const (
+	IsShow_INVALID IsShow_Kind = 0 // 无效
+	IsShow_OK      IsShow_Kind = 1 // 是
+	IsShow_NO      IsShow_Kind = 2 // 否
+)
+
+// Enum value maps for IsShow_Kind.
+var (
+	IsShow_Kind_name = map[int32]string{
+		0: "INVALID",
+		1: "OK",
+		2: "NO",
+	}
+	IsShow_Kind_value = map[string]int32{
+		"INVALID": 0,
+		"OK":      1,
+		"NO":      2,
+	}
+)
+
+func (x IsShow_Kind) Enum() *IsShow_Kind {
+	p := new(IsShow_Kind)
+	*p = x
+	return p
+}
+
+func (x IsShow_Kind) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (IsShow_Kind) Descriptor() protoreflect.EnumDescriptor {
+	return file_backend_common_enum_proto_enumTypes[0].Descriptor()
+}
+
+func (IsShow_Kind) Type() protoreflect.EnumType {
+	return &file_backend_common_enum_proto_enumTypes[0]
+}
+
+func (x IsShow_Kind) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use IsShow_Kind.Descriptor instead.
+func (IsShow_Kind) EnumDescriptor() ([]byte, []int) {
+	return file_backend_common_enum_proto_rawDescGZIP(), []int{0, 0}
+}
+
+// 是否标识
+type IsShow struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+}
+
+func (x *IsShow) Reset() {
+	*x = IsShow{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_backend_common_enum_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *IsShow) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*IsShow) ProtoMessage() {}
+
+func (x *IsShow) ProtoReflect() protoreflect.Message {
+	mi := &file_backend_common_enum_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 IsShow.ProtoReflect.Descriptor instead.
+func (*IsShow) Descriptor() ([]byte, []int) {
+	return file_backend_common_enum_proto_rawDescGZIP(), []int{0}
+}
+
+var File_backend_common_enum_proto protoreflect.FileDescriptor
+
+var file_backend_common_enum_proto_rawDesc = []byte{
+	0x0a, 0x19, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
+	0x2f, 0x65, 0x6e, 0x75, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0b, 0x62, 0x61, 0x63,
+	0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x6f, 0x70, 0x73, 0x22, 0x2d, 0x0a, 0x06, 0x49, 0x73, 0x53, 0x68,
+	0x6f, 0x77, 0x22, 0x23, 0x0a, 0x04, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e,
+	0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4b, 0x10, 0x01, 0x12,
+	0x06, 0x0a, 0x02, 0x4e, 0x4f, 0x10, 0x02, 0x42, 0x0c, 0x5a, 0x0a, 0x2e, 0x3b, 0x63, 0x6f, 0x6d,
+	0x6d, 0x6f, 0x6e, 0x50, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_backend_common_enum_proto_rawDescOnce sync.Once
+	file_backend_common_enum_proto_rawDescData = file_backend_common_enum_proto_rawDesc
+)
+
+func file_backend_common_enum_proto_rawDescGZIP() []byte {
+	file_backend_common_enum_proto_rawDescOnce.Do(func() {
+		file_backend_common_enum_proto_rawDescData = protoimpl.X.CompressGZIP(file_backend_common_enum_proto_rawDescData)
+	})
+	return file_backend_common_enum_proto_rawDescData
+}
+
+var file_backend_common_enum_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_backend_common_enum_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_backend_common_enum_proto_goTypes = []interface{}{
+	(IsShow_Kind)(0), // 0: backend.ops.IsShow.Kind
+	(*IsShow)(nil),   // 1: backend.ops.IsShow
+}
+var file_backend_common_enum_proto_depIdxs = []int32{
+	0, // [0:0] is the sub-list for method output_type
+	0, // [0:0] 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_backend_common_enum_proto_init() }
+func file_backend_common_enum_proto_init() {
+	if File_backend_common_enum_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_backend_common_enum_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*IsShow); 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_backend_common_enum_proto_rawDesc,
+			NumEnums:      1,
+			NumMessages:   1,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_backend_common_enum_proto_goTypes,
+		DependencyIndexes: file_backend_common_enum_proto_depIdxs,
+		EnumInfos:         file_backend_common_enum_proto_enumTypes,
+		MessageInfos:      file_backend_common_enum_proto_msgTypes,
+	}.Build()
+	File_backend_common_enum_proto = out.File
+	file_backend_common_enum_proto_rawDesc = nil
+	file_backend_common_enum_proto_goTypes = nil
+	file_backend_common_enum_proto_depIdxs = nil
+}

+ 365 - 0
proto/go/backend/common/errors.pb.go

@@ -0,0 +1,365 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.28.1
+// 	protoc        v3.21.9
+// source: backend/common/errors.proto
+
+package commonPb
+
+import (
+	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)
+)
+
+type Error_Code int32
+
+const (
+	Error_OK Error_Code = 0
+	// 鉴权
+	Error_UNAUTHORIZED Error_Code = 10000
+	// 通用请求错误
+	Error_BAD_REQUEST              Error_Code = 11000
+	Error_INVALID_CONTENT_TYPE     Error_Code = 11001
+	Error_INVALID_CONTENT_ENCODING Error_Code = 11002
+	Error_TOO_MANY_REQUESTS        Error_Code = 11003
+	// Config
+	Error_INVALID_STORAGE_TYPE Error_Code = 11100
+	// DataEvent
+	Error_INVALID_DE_DATA Error_Code = 11200
+	// Checkin
+	Error_CHECKIN_REPEATED Error_Code = 20000
+	// Course
+	Error_COURSE_NOT_FOUND Error_Code = 21000
+	// 课程没有权益
+	Error_COURSE_NOT_INTEREST Error_Code = 21001
+	// Recommend
+	Error_MODULE_NOT_FOUND Error_Code = 22000
+	// User Course
+	Error_USER_COURSE_ALREADY_ADDED Error_Code = 23000
+	Error_USER_COURSE_NOT_FOUND     Error_Code = 23001
+	// PT
+	Error_PT_LIMITED Error_Code = 24000
+	// 无效的价格
+	Error_INVALID_PRICE Error_Code = 24100
+	// 无效的 product ID
+	Error_INVALID_PRODUCT_ID Error_Code = 24101
+	// 无效的订单号
+	Error_INVALID_ORDER_NUMBER Error_Code = 24102
+	// 无效的用户 ID
+	Error_INVALID_USER_ID Error_Code = 24103
+	// 无效的收据
+	Error_INVALID_RECEIPT Error_Code = 24104
+	// 异常的 iOS 收据,需要客户端 check 和重试
+	Error_EMPTY_IOS_RECEIPT Error_Code = 24105
+	// UserPlan 相关
+	// 免费用户限制 plan 课程数量
+	Error_USER_PLAN_LIMITED_COURSE_COUNT Error_Code = 24300
+	// ========= SYSTEM =========
+	// 服务自身错误
+	Error_INTERNAL_ERROR Error_Code = 90000
+	// JSONPB encoding/decoding with error
+	Error_JSONPB_ERROR Error_Code = 90100
+	// JSON encoding/decoding with error
+	Error_JSON_ERROR Error_Code = 90101
+	// PB encoding/decoding with error
+	Error_PB_ERROR Error_Code = 90102
+	// 依赖服务错误
+	Error_EXTERNAL_ERROR Error_Code = 91000
+)
+
+// Enum value maps for Error_Code.
+var (
+	Error_Code_name = map[int32]string{
+		0:     "OK",
+		10000: "UNAUTHORIZED",
+		11000: "BAD_REQUEST",
+		11001: "INVALID_CONTENT_TYPE",
+		11002: "INVALID_CONTENT_ENCODING",
+		11003: "TOO_MANY_REQUESTS",
+		11100: "INVALID_STORAGE_TYPE",
+		11200: "INVALID_DE_DATA",
+		20000: "CHECKIN_REPEATED",
+		21000: "COURSE_NOT_FOUND",
+		21001: "COURSE_NOT_INTEREST",
+		22000: "MODULE_NOT_FOUND",
+		23000: "USER_COURSE_ALREADY_ADDED",
+		23001: "USER_COURSE_NOT_FOUND",
+		24000: "PT_LIMITED",
+		24100: "INVALID_PRICE",
+		24101: "INVALID_PRODUCT_ID",
+		24102: "INVALID_ORDER_NUMBER",
+		24103: "INVALID_USER_ID",
+		24104: "INVALID_RECEIPT",
+		24105: "EMPTY_IOS_RECEIPT",
+		24300: "USER_PLAN_LIMITED_COURSE_COUNT",
+		90000: "INTERNAL_ERROR",
+		90100: "JSONPB_ERROR",
+		90101: "JSON_ERROR",
+		90102: "PB_ERROR",
+		91000: "EXTERNAL_ERROR",
+	}
+	Error_Code_value = map[string]int32{
+		"OK":                             0,
+		"UNAUTHORIZED":                   10000,
+		"BAD_REQUEST":                    11000,
+		"INVALID_CONTENT_TYPE":           11001,
+		"INVALID_CONTENT_ENCODING":       11002,
+		"TOO_MANY_REQUESTS":              11003,
+		"INVALID_STORAGE_TYPE":           11100,
+		"INVALID_DE_DATA":                11200,
+		"CHECKIN_REPEATED":               20000,
+		"COURSE_NOT_FOUND":               21000,
+		"COURSE_NOT_INTEREST":            21001,
+		"MODULE_NOT_FOUND":               22000,
+		"USER_COURSE_ALREADY_ADDED":      23000,
+		"USER_COURSE_NOT_FOUND":          23001,
+		"PT_LIMITED":                     24000,
+		"INVALID_PRICE":                  24100,
+		"INVALID_PRODUCT_ID":             24101,
+		"INVALID_ORDER_NUMBER":           24102,
+		"INVALID_USER_ID":                24103,
+		"INVALID_RECEIPT":                24104,
+		"EMPTY_IOS_RECEIPT":              24105,
+		"USER_PLAN_LIMITED_COURSE_COUNT": 24300,
+		"INTERNAL_ERROR":                 90000,
+		"JSONPB_ERROR":                   90100,
+		"JSON_ERROR":                     90101,
+		"PB_ERROR":                       90102,
+		"EXTERNAL_ERROR":                 91000,
+	}
+)
+
+func (x Error_Code) Enum() *Error_Code {
+	p := new(Error_Code)
+	*p = x
+	return p
+}
+
+func (x Error_Code) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (Error_Code) Descriptor() protoreflect.EnumDescriptor {
+	return file_backend_common_errors_proto_enumTypes[0].Descriptor()
+}
+
+func (Error_Code) Type() protoreflect.EnumType {
+	return &file_backend_common_errors_proto_enumTypes[0]
+}
+
+func (x Error_Code) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use Error_Code.Descriptor instead.
+func (Error_Code) EnumDescriptor() ([]byte, []int) {
+	return file_backend_common_errors_proto_rawDescGZIP(), []int{0, 0}
+}
+
+type Error struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// 业务错误码
+	Code Error_Code `protobuf:"varint,1,opt,name=code,proto3,enum=backend.common.Error_Code" json:"code,omitempty"`
+	// 错误信息
+	Msg string `protobuf:"bytes,2,opt,name=msg,proto3" json:"msg,omitempty"`
+	// 补充错误信息
+	// @optional
+	Errors []string `protobuf:"bytes,3,rep,name=errors,proto3" json:"errors,omitempty"`
+}
+
+func (x *Error) Reset() {
+	*x = Error{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_backend_common_errors_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Error) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Error) ProtoMessage() {}
+
+func (x *Error) ProtoReflect() protoreflect.Message {
+	mi := &file_backend_common_errors_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 Error.ProtoReflect.Descriptor instead.
+func (*Error) Descriptor() ([]byte, []int) {
+	return file_backend_common_errors_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Error) GetCode() Error_Code {
+	if x != nil {
+		return x.Code
+	}
+	return Error_OK
+}
+
+func (x *Error) GetMsg() string {
+	if x != nil {
+		return x.Msg
+	}
+	return ""
+}
+
+func (x *Error) GetErrors() []string {
+	if x != nil {
+		return x.Errors
+	}
+	return nil
+}
+
+var File_backend_common_errors_proto protoreflect.FileDescriptor
+
+var file_backend_common_errors_proto_rawDesc = []byte{
+	0x0a, 0x1b, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
+	0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0e, 0x62,
+	0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x22, 0xf5, 0x06,
+	0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x2e, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e,
+	0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x43, 0x6f, 0x64,
+	0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x72, 0x72,
+	0x6f, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72,
+	0x73, 0x22, 0x91, 0x06, 0x0a, 0x04, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4b,
+	0x10, 0x00, 0x12, 0x11, 0x0a, 0x0c, 0x55, 0x4e, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x5a,
+	0x45, 0x44, 0x10, 0x90, 0x4e, 0x12, 0x10, 0x0a, 0x0b, 0x42, 0x41, 0x44, 0x5f, 0x52, 0x45, 0x51,
+	0x55, 0x45, 0x53, 0x54, 0x10, 0xf8, 0x55, 0x12, 0x19, 0x0a, 0x14, 0x49, 0x4e, 0x56, 0x41, 0x4c,
+	0x49, 0x44, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10,
+	0xf9, 0x55, 0x12, 0x1d, 0x0a, 0x18, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x43, 0x4f,
+	0x4e, 0x54, 0x45, 0x4e, 0x54, 0x5f, 0x45, 0x4e, 0x43, 0x4f, 0x44, 0x49, 0x4e, 0x47, 0x10, 0xfa,
+	0x55, 0x12, 0x16, 0x0a, 0x11, 0x54, 0x4f, 0x4f, 0x5f, 0x4d, 0x41, 0x4e, 0x59, 0x5f, 0x52, 0x45,
+	0x51, 0x55, 0x45, 0x53, 0x54, 0x53, 0x10, 0xfb, 0x55, 0x12, 0x19, 0x0a, 0x14, 0x49, 0x4e, 0x56,
+	0x41, 0x4c, 0x49, 0x44, 0x5f, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50,
+	0x45, 0x10, 0xdc, 0x56, 0x12, 0x14, 0x0a, 0x0f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f,
+	0x44, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x10, 0xc0, 0x57, 0x12, 0x16, 0x0a, 0x10, 0x43, 0x48,
+	0x45, 0x43, 0x4b, 0x49, 0x4e, 0x5f, 0x52, 0x45, 0x50, 0x45, 0x41, 0x54, 0x45, 0x44, 0x10, 0xa0,
+	0x9c, 0x01, 0x12, 0x16, 0x0a, 0x10, 0x43, 0x4f, 0x55, 0x52, 0x53, 0x45, 0x5f, 0x4e, 0x4f, 0x54,
+	0x5f, 0x46, 0x4f, 0x55, 0x4e, 0x44, 0x10, 0x88, 0xa4, 0x01, 0x12, 0x19, 0x0a, 0x13, 0x43, 0x4f,
+	0x55, 0x52, 0x53, 0x45, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x45, 0x53,
+	0x54, 0x10, 0x89, 0xa4, 0x01, 0x12, 0x16, 0x0a, 0x10, 0x4d, 0x4f, 0x44, 0x55, 0x4c, 0x45, 0x5f,
+	0x4e, 0x4f, 0x54, 0x5f, 0x46, 0x4f, 0x55, 0x4e, 0x44, 0x10, 0xf0, 0xab, 0x01, 0x12, 0x1f, 0x0a,
+	0x19, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x43, 0x4f, 0x55, 0x52, 0x53, 0x45, 0x5f, 0x41, 0x4c, 0x52,
+	0x45, 0x41, 0x44, 0x59, 0x5f, 0x41, 0x44, 0x44, 0x45, 0x44, 0x10, 0xd8, 0xb3, 0x01, 0x12, 0x1b,
+	0x0a, 0x15, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x43, 0x4f, 0x55, 0x52, 0x53, 0x45, 0x5f, 0x4e, 0x4f,
+	0x54, 0x5f, 0x46, 0x4f, 0x55, 0x4e, 0x44, 0x10, 0xd9, 0xb3, 0x01, 0x12, 0x10, 0x0a, 0x0a, 0x50,
+	0x54, 0x5f, 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x45, 0x44, 0x10, 0xc0, 0xbb, 0x01, 0x12, 0x13, 0x0a,
+	0x0d, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x50, 0x52, 0x49, 0x43, 0x45, 0x10, 0xa4,
+	0xbc, 0x01, 0x12, 0x18, 0x0a, 0x12, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x50, 0x52,
+	0x4f, 0x44, 0x55, 0x43, 0x54, 0x5f, 0x49, 0x44, 0x10, 0xa5, 0xbc, 0x01, 0x12, 0x1a, 0x0a, 0x14,
+	0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x52, 0x44, 0x45, 0x52, 0x5f, 0x4e, 0x55,
+	0x4d, 0x42, 0x45, 0x52, 0x10, 0xa6, 0xbc, 0x01, 0x12, 0x15, 0x0a, 0x0f, 0x49, 0x4e, 0x56, 0x41,
+	0x4c, 0x49, 0x44, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x49, 0x44, 0x10, 0xa7, 0xbc, 0x01, 0x12,
+	0x15, 0x0a, 0x0f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x52, 0x45, 0x43, 0x45, 0x49,
+	0x50, 0x54, 0x10, 0xa8, 0xbc, 0x01, 0x12, 0x17, 0x0a, 0x11, 0x45, 0x4d, 0x50, 0x54, 0x59, 0x5f,
+	0x49, 0x4f, 0x53, 0x5f, 0x52, 0x45, 0x43, 0x45, 0x49, 0x50, 0x54, 0x10, 0xa9, 0xbc, 0x01, 0x12,
+	0x24, 0x0a, 0x1e, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x50, 0x4c, 0x41, 0x4e, 0x5f, 0x4c, 0x49, 0x4d,
+	0x49, 0x54, 0x45, 0x44, 0x5f, 0x43, 0x4f, 0x55, 0x52, 0x53, 0x45, 0x5f, 0x43, 0x4f, 0x55, 0x4e,
+	0x54, 0x10, 0xec, 0xbd, 0x01, 0x12, 0x14, 0x0a, 0x0e, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41,
+	0x4c, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x90, 0xbf, 0x05, 0x12, 0x12, 0x0a, 0x0c, 0x4a,
+	0x53, 0x4f, 0x4e, 0x50, 0x42, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0xf4, 0xbf, 0x05, 0x12,
+	0x10, 0x0a, 0x0a, 0x4a, 0x53, 0x4f, 0x4e, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0xf5, 0xbf,
+	0x05, 0x12, 0x0e, 0x0a, 0x08, 0x50, 0x42, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0xf6, 0xbf,
+	0x05, 0x12, 0x14, 0x0a, 0x0e, 0x45, 0x58, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x5f, 0x45, 0x52,
+	0x52, 0x4f, 0x52, 0x10, 0xf8, 0xc6, 0x05, 0x22, 0x05, 0x08, 0x01, 0x10, 0x8f, 0x4e, 0x22, 0x06,
+	0x08, 0x91, 0x4e, 0x10, 0xf7, 0x55, 0x22, 0x06, 0x08, 0xfc, 0x55, 0x10, 0xdb, 0x56, 0x22, 0x06,
+	0x08, 0xdd, 0x56, 0x10, 0xbf, 0x57, 0x22, 0x07, 0x08, 0xc1, 0x57, 0x10, 0x9f, 0x9c, 0x01, 0x22,
+	0x08, 0x08, 0xa1, 0x9c, 0x01, 0x10, 0x87, 0xa4, 0x01, 0x22, 0x08, 0x08, 0x8a, 0xa4, 0x01, 0x10,
+	0xef, 0xab, 0x01, 0x22, 0x08, 0x08, 0xf1, 0xab, 0x01, 0x10, 0xd7, 0xb3, 0x01, 0x22, 0x08, 0x08,
+	0xda, 0xb3, 0x01, 0x10, 0xbf, 0xbb, 0x01, 0x22, 0x08, 0x08, 0xc1, 0xbb, 0x01, 0x10, 0xa3, 0xbc,
+	0x01, 0x22, 0x08, 0x08, 0xaa, 0xbc, 0x01, 0x10, 0xeb, 0xbd, 0x01, 0x22, 0x08, 0x08, 0xd0, 0xbe,
+	0x01, 0x10, 0x8f, 0xbf, 0x05, 0x22, 0x08, 0x08, 0x91, 0xbf, 0x05, 0x10, 0xf3, 0xbf, 0x05, 0x22,
+	0x08, 0x08, 0xf7, 0xbf, 0x05, 0x10, 0xf7, 0xc6, 0x05, 0x22, 0x0a, 0x08, 0xf9, 0xc6, 0x05, 0x10,
+	0xff, 0xff, 0xff, 0xff, 0x07, 0x42, 0x0c, 0x5a, 0x0a, 0x2e, 0x3b, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
+	0x6e, 0x50, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_backend_common_errors_proto_rawDescOnce sync.Once
+	file_backend_common_errors_proto_rawDescData = file_backend_common_errors_proto_rawDesc
+)
+
+func file_backend_common_errors_proto_rawDescGZIP() []byte {
+	file_backend_common_errors_proto_rawDescOnce.Do(func() {
+		file_backend_common_errors_proto_rawDescData = protoimpl.X.CompressGZIP(file_backend_common_errors_proto_rawDescData)
+	})
+	return file_backend_common_errors_proto_rawDescData
+}
+
+var file_backend_common_errors_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_backend_common_errors_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_backend_common_errors_proto_goTypes = []interface{}{
+	(Error_Code)(0), // 0: backend.common.Error.Code
+	(*Error)(nil),   // 1: backend.common.Error
+}
+var file_backend_common_errors_proto_depIdxs = []int32{
+	0, // 0: backend.common.Error.code:type_name -> backend.common.Error.Code
+	1, // [1:1] is the sub-list for method output_type
+	1, // [1:1] is the sub-list for method input_type
+	1, // [1:1] is the sub-list for extension type_name
+	1, // [1:1] is the sub-list for extension extendee
+	0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_backend_common_errors_proto_init() }
+func file_backend_common_errors_proto_init() {
+	if File_backend_common_errors_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_backend_common_errors_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Error); 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_backend_common_errors_proto_rawDesc,
+			NumEnums:      1,
+			NumMessages:   1,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_backend_common_errors_proto_goTypes,
+		DependencyIndexes: file_backend_common_errors_proto_depIdxs,
+		EnumInfos:         file_backend_common_errors_proto_enumTypes,
+		MessageInfos:      file_backend_common_errors_proto_msgTypes,
+	}.Build()
+	File_backend_common_errors_proto = out.File
+	file_backend_common_errors_proto_rawDesc = nil
+	file_backend_common_errors_proto_goTypes = nil
+	file_backend_common_errors_proto_depIdxs = nil
+}

+ 188 - 0
proto/go/backend/operation/enum.pb.go

@@ -0,0 +1,188 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.28.1
+// 	protoc        v3.21.9
+// source: backend/operation/enum.proto
+
+package operationPb
+
+import (
+	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)
+)
+
+type IsShow_Kind int32
+
+const (
+	IsShow_INVALID IsShow_Kind = 0 // 无效
+	IsShow_OK      IsShow_Kind = 1 // 是
+	IsShow_NO      IsShow_Kind = 2 // 否
+)
+
+// Enum value maps for IsShow_Kind.
+var (
+	IsShow_Kind_name = map[int32]string{
+		0: "INVALID",
+		1: "OK",
+		2: "NO",
+	}
+	IsShow_Kind_value = map[string]int32{
+		"INVALID": 0,
+		"OK":      1,
+		"NO":      2,
+	}
+)
+
+func (x IsShow_Kind) Enum() *IsShow_Kind {
+	p := new(IsShow_Kind)
+	*p = x
+	return p
+}
+
+func (x IsShow_Kind) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (IsShow_Kind) Descriptor() protoreflect.EnumDescriptor {
+	return file_backend_operation_enum_proto_enumTypes[0].Descriptor()
+}
+
+func (IsShow_Kind) Type() protoreflect.EnumType {
+	return &file_backend_operation_enum_proto_enumTypes[0]
+}
+
+func (x IsShow_Kind) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use IsShow_Kind.Descriptor instead.
+func (IsShow_Kind) EnumDescriptor() ([]byte, []int) {
+	return file_backend_operation_enum_proto_rawDescGZIP(), []int{0, 0}
+}
+
+// 字段类型
+type IsShow struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+}
+
+func (x *IsShow) Reset() {
+	*x = IsShow{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_backend_operation_enum_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *IsShow) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*IsShow) ProtoMessage() {}
+
+func (x *IsShow) ProtoReflect() protoreflect.Message {
+	mi := &file_backend_operation_enum_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 IsShow.ProtoReflect.Descriptor instead.
+func (*IsShow) Descriptor() ([]byte, []int) {
+	return file_backend_operation_enum_proto_rawDescGZIP(), []int{0}
+}
+
+var File_backend_operation_enum_proto protoreflect.FileDescriptor
+
+var file_backend_operation_enum_proto_rawDesc = []byte{
+	0x0a, 0x1c, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74,
+	0x69, 0x6f, 0x6e, 0x2f, 0x65, 0x6e, 0x75, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11,
+	0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f,
+	0x6e, 0x22, 0x2d, 0x0a, 0x06, 0x49, 0x73, 0x53, 0x68, 0x6f, 0x77, 0x22, 0x23, 0x0a, 0x04, 0x4b,
+	0x69, 0x6e, 0x64, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00,
+	0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4b, 0x10, 0x01, 0x12, 0x06, 0x0a, 0x02, 0x4e, 0x4f, 0x10, 0x02,
+	0x42, 0x0f, 0x5a, 0x0d, 0x2e, 0x3b, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50,
+	0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_backend_operation_enum_proto_rawDescOnce sync.Once
+	file_backend_operation_enum_proto_rawDescData = file_backend_operation_enum_proto_rawDesc
+)
+
+func file_backend_operation_enum_proto_rawDescGZIP() []byte {
+	file_backend_operation_enum_proto_rawDescOnce.Do(func() {
+		file_backend_operation_enum_proto_rawDescData = protoimpl.X.CompressGZIP(file_backend_operation_enum_proto_rawDescData)
+	})
+	return file_backend_operation_enum_proto_rawDescData
+}
+
+var file_backend_operation_enum_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_backend_operation_enum_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_backend_operation_enum_proto_goTypes = []interface{}{
+	(IsShow_Kind)(0), // 0: backend.operation.IsShow.Kind
+	(*IsShow)(nil),   // 1: backend.operation.IsShow
+}
+var file_backend_operation_enum_proto_depIdxs = []int32{
+	0, // [0:0] is the sub-list for method output_type
+	0, // [0:0] 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_backend_operation_enum_proto_init() }
+func file_backend_operation_enum_proto_init() {
+	if File_backend_operation_enum_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_backend_operation_enum_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*IsShow); 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_backend_operation_enum_proto_rawDesc,
+			NumEnums:      1,
+			NumMessages:   1,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_backend_operation_enum_proto_goTypes,
+		DependencyIndexes: file_backend_operation_enum_proto_depIdxs,
+		EnumInfos:         file_backend_operation_enum_proto_enumTypes,
+		MessageInfos:      file_backend_operation_enum_proto_msgTypes,
+	}.Build()
+	File_backend_operation_enum_proto = out.File
+	file_backend_operation_enum_proto_rawDesc = nil
+	file_backend_operation_enum_proto_goTypes = nil
+	file_backend_operation_enum_proto_depIdxs = nil
+}

+ 427 - 0
proto/go/backend/operation/pasture.pb.go

@@ -0,0 +1,427 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.28.1
+// 	protoc        v3.21.9
+// source: backend/operation/pasture.proto
+
+package operationPb
+
+import (
+	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)
+)
+
+type AddPastureRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Id              int64       `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
+	Name            string      `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`                                                       // 牧场名称
+	ManagerUser     string      `protobuf:"bytes,3,opt,name=manager_user,json=managerUser,proto3" json:"manager_user,omitempty"`                      // 牧场负责人名称
+	ManagerPassword string      `protobuf:"bytes,4,opt,name=manager_password,json=managerPassword,proto3" json:"manager_password,omitempty"`          // 牧场负责人账号
+	ManagerPhone    string      `protobuf:"bytes,5,opt,name=manager_phone,json=managerPhone,proto3" json:"manager_phone,omitempty"`                   // 牧场负责人手机号
+	Address         string      `protobuf:"bytes,6,opt,name=address,proto3" json:"address,omitempty"`                                                 // 牧场地址
+	IsShow          IsShow_Kind `protobuf:"varint,7,opt,name=is_show,json=isShow,proto3,enum=backend.operation.IsShow_Kind" json:"is_show,omitempty"` // 是否启用
+	CreatedAt       int64       `protobuf:"varint,8,opt,name=Created_at,json=CreatedAt,proto3" json:"Created_at,omitempty"`                           // 创建时间
+}
+
+func (x *AddPastureRequest) Reset() {
+	*x = AddPastureRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_backend_operation_pasture_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *AddPastureRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*AddPastureRequest) ProtoMessage() {}
+
+func (x *AddPastureRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_backend_operation_pasture_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 AddPastureRequest.ProtoReflect.Descriptor instead.
+func (*AddPastureRequest) Descriptor() ([]byte, []int) {
+	return file_backend_operation_pasture_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *AddPastureRequest) GetId() int64 {
+	if x != nil {
+		return x.Id
+	}
+	return 0
+}
+
+func (x *AddPastureRequest) GetName() string {
+	if x != nil {
+		return x.Name
+	}
+	return ""
+}
+
+func (x *AddPastureRequest) GetManagerUser() string {
+	if x != nil {
+		return x.ManagerUser
+	}
+	return ""
+}
+
+func (x *AddPastureRequest) GetManagerPassword() string {
+	if x != nil {
+		return x.ManagerPassword
+	}
+	return ""
+}
+
+func (x *AddPastureRequest) GetManagerPhone() string {
+	if x != nil {
+		return x.ManagerPhone
+	}
+	return ""
+}
+
+func (x *AddPastureRequest) GetAddress() string {
+	if x != nil {
+		return x.Address
+	}
+	return ""
+}
+
+func (x *AddPastureRequest) GetIsShow() IsShow_Kind {
+	if x != nil {
+		return x.IsShow
+	}
+	return IsShow_INVALID
+}
+
+func (x *AddPastureRequest) GetCreatedAt() int64 {
+	if x != nil {
+		return x.CreatedAt
+	}
+	return 0
+}
+
+type SearchPastureRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Page         int32  `protobuf:"varint,1,opt,name=page,proto3" json:"page,omitempty"`                                    // 第几页,从1开始
+	PageSize     int32  `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"`            // 每页size,一般为20
+	Name         string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"`                                     // 牧场名称
+	ManagerUser  string `protobuf:"bytes,4,opt,name=manager_user,json=managerUser,proto3" json:"manager_user,omitempty"`    // 牧场负责人名称
+	ManagerPhone string `protobuf:"bytes,5,opt,name=manager_phone,json=managerPhone,proto3" json:"manager_phone,omitempty"` // 牧场负责人手机号
+	StartTime    int64  `protobuf:"varint,6,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"`         // 开始时间
+	EndTime      int64  `protobuf:"varint,7,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"`               // 结束时间
+}
+
+func (x *SearchPastureRequest) Reset() {
+	*x = SearchPastureRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_backend_operation_pasture_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *SearchPastureRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SearchPastureRequest) ProtoMessage() {}
+
+func (x *SearchPastureRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_backend_operation_pasture_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 SearchPastureRequest.ProtoReflect.Descriptor instead.
+func (*SearchPastureRequest) Descriptor() ([]byte, []int) {
+	return file_backend_operation_pasture_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *SearchPastureRequest) GetPage() int32 {
+	if x != nil {
+		return x.Page
+	}
+	return 0
+}
+
+func (x *SearchPastureRequest) GetPageSize() int32 {
+	if x != nil {
+		return x.PageSize
+	}
+	return 0
+}
+
+func (x *SearchPastureRequest) GetName() string {
+	if x != nil {
+		return x.Name
+	}
+	return ""
+}
+
+func (x *SearchPastureRequest) GetManagerUser() string {
+	if x != nil {
+		return x.ManagerUser
+	}
+	return ""
+}
+
+func (x *SearchPastureRequest) GetManagerPhone() string {
+	if x != nil {
+		return x.ManagerPhone
+	}
+	return ""
+}
+
+func (x *SearchPastureRequest) GetStartTime() int64 {
+	if x != nil {
+		return x.StartTime
+	}
+	return 0
+}
+
+func (x *SearchPastureRequest) GetEndTime() int64 {
+	if x != nil {
+		return x.EndTime
+	}
+	return 0
+}
+
+type SearchPastureResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Page  int32                `protobuf:"varint,1,opt,name=page,proto3" json:"page,omitempty"`
+	Total int32                `protobuf:"varint,2,opt,name=total,proto3" json:"total,omitempty"`
+	Data  []*AddPastureRequest `protobuf:"bytes,3,rep,name=data,proto3" json:"data,omitempty"`
+}
+
+func (x *SearchPastureResponse) Reset() {
+	*x = SearchPastureResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_backend_operation_pasture_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *SearchPastureResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SearchPastureResponse) ProtoMessage() {}
+
+func (x *SearchPastureResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_backend_operation_pasture_proto_msgTypes[2]
+	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 SearchPastureResponse.ProtoReflect.Descriptor instead.
+func (*SearchPastureResponse) Descriptor() ([]byte, []int) {
+	return file_backend_operation_pasture_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *SearchPastureResponse) GetPage() int32 {
+	if x != nil {
+		return x.Page
+	}
+	return 0
+}
+
+func (x *SearchPastureResponse) GetTotal() int32 {
+	if x != nil {
+		return x.Total
+	}
+	return 0
+}
+
+func (x *SearchPastureResponse) GetData() []*AddPastureRequest {
+	if x != nil {
+		return x.Data
+	}
+	return nil
+}
+
+var File_backend_operation_pasture_proto protoreflect.FileDescriptor
+
+var file_backend_operation_pasture_proto_rawDesc = []byte{
+	0x0a, 0x1f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74,
+	0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x61, 0x73, 0x74, 0x75, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x12, 0x11, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x6f, 0x70, 0x65, 0x72, 0x61,
+	0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x1c, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x6f, 0x70,
+	0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x65, 0x6e, 0x75, 0x6d, 0x2e, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x22, 0x9c, 0x02, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x50, 0x61, 0x73, 0x74, 0x75, 0x72,
+	0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c,
+	0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x0b, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x12,
+	0x29, 0x0a, 0x10, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77,
+	0x6f, 0x72, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6d, 0x61, 0x6e, 0x61, 0x67,
+	0x65, 0x72, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x61,
+	0x6e, 0x61, 0x67, 0x65, 0x72, 0x5f, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x0c, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x50, 0x68, 0x6f, 0x6e, 0x65, 0x12,
+	0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x37, 0x0a, 0x07, 0x69, 0x73, 0x5f,
+	0x73, 0x68, 0x6f, 0x77, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x62, 0x61, 0x63,
+	0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x49,
+	0x73, 0x53, 0x68, 0x6f, 0x77, 0x2e, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x06, 0x69, 0x73, 0x53, 0x68,
+	0x6f, 0x77, 0x12, 0x1d, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74,
+	0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41,
+	0x74, 0x22, 0xdd, 0x01, 0x0a, 0x14, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x50, 0x61, 0x73, 0x74,
+	0x75, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61,
+	0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x1b,
+	0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x05, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e,
+	0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
+	0x21, 0x0a, 0x0c, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x18,
+	0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x55, 0x73,
+	0x65, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x5f, 0x70, 0x68,
+	0x6f, 0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6d, 0x61, 0x6e, 0x61, 0x67,
+	0x65, 0x72, 0x50, 0x68, 0x6f, 0x6e, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74,
+	0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x74, 0x61,
+	0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69,
+	0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d,
+	0x65, 0x22, 0x7b, 0x0a, 0x15, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x50, 0x61, 0x73, 0x74, 0x75,
+	0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61,
+	0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x14,
+	0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x74,
+	0x6f, 0x74, 0x61, 0x6c, 0x12, 0x38, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x03,
+	0x28, 0x0b, 0x32, 0x24, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x6f, 0x70, 0x65,
+	0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x61, 0x73, 0x74, 0x75, 0x72,
+	0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x42, 0x0f,
+	0x5a, 0x0d, 0x2e, 0x3b, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x62, 0x62,
+	0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_backend_operation_pasture_proto_rawDescOnce sync.Once
+	file_backend_operation_pasture_proto_rawDescData = file_backend_operation_pasture_proto_rawDesc
+)
+
+func file_backend_operation_pasture_proto_rawDescGZIP() []byte {
+	file_backend_operation_pasture_proto_rawDescOnce.Do(func() {
+		file_backend_operation_pasture_proto_rawDescData = protoimpl.X.CompressGZIP(file_backend_operation_pasture_proto_rawDescData)
+	})
+	return file_backend_operation_pasture_proto_rawDescData
+}
+
+var file_backend_operation_pasture_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
+var file_backend_operation_pasture_proto_goTypes = []interface{}{
+	(*AddPastureRequest)(nil),     // 0: backend.operation.AddPastureRequest
+	(*SearchPastureRequest)(nil),  // 1: backend.operation.SearchPastureRequest
+	(*SearchPastureResponse)(nil), // 2: backend.operation.SearchPastureResponse
+	(IsShow_Kind)(0),              // 3: backend.operation.IsShow.Kind
+}
+var file_backend_operation_pasture_proto_depIdxs = []int32{
+	3, // 0: backend.operation.AddPastureRequest.is_show:type_name -> backend.operation.IsShow.Kind
+	0, // 1: backend.operation.SearchPastureResponse.data:type_name -> backend.operation.AddPastureRequest
+	2, // [2:2] is the sub-list for method output_type
+	2, // [2:2] is the sub-list for method input_type
+	2, // [2:2] is the sub-list for extension type_name
+	2, // [2:2] is the sub-list for extension extendee
+	0, // [0:2] is the sub-list for field type_name
+}
+
+func init() { file_backend_operation_pasture_proto_init() }
+func file_backend_operation_pasture_proto_init() {
+	if File_backend_operation_pasture_proto != nil {
+		return
+	}
+	file_backend_operation_enum_proto_init()
+	if !protoimpl.UnsafeEnabled {
+		file_backend_operation_pasture_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*AddPastureRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_backend_operation_pasture_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*SearchPastureRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_backend_operation_pasture_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*SearchPastureResponse); 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_backend_operation_pasture_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   3,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_backend_operation_pasture_proto_goTypes,
+		DependencyIndexes: file_backend_operation_pasture_proto_depIdxs,
+		MessageInfos:      file_backend_operation_pasture_proto_msgTypes,
+	}.Build()
+	File_backend_operation_pasture_proto = out.File
+	file_backend_operation_pasture_proto_rawDesc = nil
+	file_backend_operation_pasture_proto_goTypes = nil
+	file_backend_operation_pasture_proto_depIdxs = nil
+}

+ 179 - 0
proto/go/backend/operation/system.pb.go

@@ -0,0 +1,179 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.28.1
+// 	protoc        v3.21.9
+// source: backend/operation/system.proto
+
+package operationPb
+
+import (
+	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)
+)
+
+// 用户权限
+type AddRoleRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Id      int64   `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
+	Name    string  `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`                   // 角色名称
+	Remarks string  `protobuf:"bytes,3,opt,name=remarks,proto3" json:"remarks,omitempty"`             // 角色备注
+	IsShow  *IsShow `protobuf:"bytes,4,opt,name=is_show,json=isShow,proto3" json:"is_show,omitempty"` // 是否启用
+}
+
+func (x *AddRoleRequest) Reset() {
+	*x = AddRoleRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_backend_operation_system_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *AddRoleRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*AddRoleRequest) ProtoMessage() {}
+
+func (x *AddRoleRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_backend_operation_system_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 AddRoleRequest.ProtoReflect.Descriptor instead.
+func (*AddRoleRequest) Descriptor() ([]byte, []int) {
+	return file_backend_operation_system_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *AddRoleRequest) GetId() int64 {
+	if x != nil {
+		return x.Id
+	}
+	return 0
+}
+
+func (x *AddRoleRequest) GetName() string {
+	if x != nil {
+		return x.Name
+	}
+	return ""
+}
+
+func (x *AddRoleRequest) GetRemarks() string {
+	if x != nil {
+		return x.Remarks
+	}
+	return ""
+}
+
+func (x *AddRoleRequest) GetIsShow() *IsShow {
+	if x != nil {
+		return x.IsShow
+	}
+	return nil
+}
+
+var File_backend_operation_system_proto protoreflect.FileDescriptor
+
+var file_backend_operation_system_proto_rawDesc = []byte{
+	0x0a, 0x1e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74,
+	0x69, 0x6f, 0x6e, 0x2f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x12, 0x11, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74,
+	0x69, 0x6f, 0x6e, 0x1a, 0x1c, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x6f, 0x70, 0x65,
+	0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x65, 0x6e, 0x75, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x22, 0x82, 0x01, 0x0a, 0x0e, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03,
+	0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x6d, 0x61,
+	0x72, 0x6b, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, 0x6d, 0x61, 0x72,
+	0x6b, 0x73, 0x12, 0x32, 0x0a, 0x07, 0x69, 0x73, 0x5f, 0x73, 0x68, 0x6f, 0x77, 0x18, 0x04, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x6f, 0x70,
+	0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x49, 0x73, 0x53, 0x68, 0x6f, 0x77, 0x52, 0x06,
+	0x69, 0x73, 0x53, 0x68, 0x6f, 0x77, 0x42, 0x0f, 0x5a, 0x0d, 0x2e, 0x3b, 0x6f, 0x70, 0x65, 0x72,
+	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_backend_operation_system_proto_rawDescOnce sync.Once
+	file_backend_operation_system_proto_rawDescData = file_backend_operation_system_proto_rawDesc
+)
+
+func file_backend_operation_system_proto_rawDescGZIP() []byte {
+	file_backend_operation_system_proto_rawDescOnce.Do(func() {
+		file_backend_operation_system_proto_rawDescData = protoimpl.X.CompressGZIP(file_backend_operation_system_proto_rawDescData)
+	})
+	return file_backend_operation_system_proto_rawDescData
+}
+
+var file_backend_operation_system_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_backend_operation_system_proto_goTypes = []interface{}{
+	(*AddRoleRequest)(nil), // 0: backend.operation.AddRoleRequest
+	(*IsShow)(nil),         // 1: backend.operation.IsShow
+}
+var file_backend_operation_system_proto_depIdxs = []int32{
+	1, // 0: backend.operation.AddRoleRequest.is_show:type_name -> backend.operation.IsShow
+	1, // [1:1] is the sub-list for method output_type
+	1, // [1:1] is the sub-list for method input_type
+	1, // [1:1] is the sub-list for extension type_name
+	1, // [1:1] is the sub-list for extension extendee
+	0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_backend_operation_system_proto_init() }
+func file_backend_operation_system_proto_init() {
+	if File_backend_operation_system_proto != nil {
+		return
+	}
+	file_backend_operation_enum_proto_init()
+	if !protoimpl.UnsafeEnabled {
+		file_backend_operation_system_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*AddRoleRequest); 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_backend_operation_system_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   1,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_backend_operation_system_proto_goTypes,
+		DependencyIndexes: file_backend_operation_system_proto_depIdxs,
+		MessageInfos:      file_backend_operation_system_proto_msgTypes,
+	}.Build()
+	File_backend_operation_system_proto = out.File
+	file_backend_operation_system_proto_rawDesc = nil
+	file_backend_operation_system_proto_goTypes = nil
+	file_backend_operation_system_proto_depIdxs = nil
+}

+ 41 - 0
scripts/consumer-images.sh

@@ -0,0 +1,41 @@
+#!/bin/bash
+set -e
+
+tag=$1
+if [ -n "$tag" ]
+then
+  echo "当前镜像tag: $tag"
+else
+  echo "请输入当前镜像tag"
+  exit
+fi
+
+### go testing
+echo "============ go test start ================"
+make ci-test
+echo "============  go test end  ================"
+
+### go build
+echo "============ go build start ================"
+make build version=${tag}
+echo "============  go build end  ================"
+
+echo "============push images start================"
+
+export aliYunDockerDNS=registry.cn-hangzhou.aliyuncs.com
+export aliYunDockerUsername=kptzhu@163.com
+export aliYunDockerPassword=zhuz1898
+export images=kpt-event/event-consumer
+
+docker build -t ${images}:${tag} -f ./docker/consumer/Dockerfile .
+docker login ${aliYunDockerDNS} --username ${aliYunDockerUsername} --password ${aliYunDockerPassword}
+
+docker tag ${images}:${tag}  ${aliYunDockerDNS}/${images}:${tag}
+docker tag ${images}:${tag}  ${aliYunDockerDNS}/${images}:latest
+
+docker push ${aliYunDockerDNS}/${images}:${tag}
+docker push ${aliYunDockerDNS}/${images}:latest
+
+docker rmi ${images}:${tag} ${aliYunDockerDNS}/${images}:${tag} ${aliYunDockerDNS}/${images}:latest
+
+echo "============push images end ================"

+ 41 - 0
scripts/http-images.sh

@@ -0,0 +1,41 @@
+#!/bin/bash
+set -e
+
+tag=$1
+if [ -n "$tag" ]
+then
+  echo "当前镜像tag: $tag"
+else
+  echo "请输入当前镜像tag"
+  exit
+fi
+
+### go testing
+echo "============ go test start ================"
+make ci-test
+echo "============  go test end  ================"
+
+### go build
+echo "============ go build start ================"
+make build version=${tag}
+echo "============  go build end  ================"
+
+echo "============push images start================"
+
+export aliYunDockerDNS=registry.cn-hangzhou.aliyuncs.com
+export aliYunDockerUsername=kptzhu@163.com
+export aliYunDockerPassword=zhuz1898
+export images=kpt-event/event-http
+
+docker build -t ${images}:${tag} -f ./docker/http/Dockerfile .
+docker login ${aliYunDockerDNS} --username ${aliYunDockerUsername} --password ${aliYunDockerPassword}
+
+docker tag ${images}:${tag}  ${aliYunDockerDNS}/${images}:${tag}
+docker tag ${images}:${tag}  ${aliYunDockerDNS}/${images}:latest
+
+docker push ${aliYunDockerDNS}/${images}:${tag}
+docker push ${aliYunDockerDNS}/${images}:latest
+
+docker rmi ${images}:${tag} ${aliYunDockerDNS}/${images}:${tag} ${aliYunDockerDNS}/${images}:latest
+
+echo "============push images end ================"

+ 6 - 0
scripts/proto.sh

@@ -0,0 +1,6 @@
+#!/bin/bash
+set -e
+### proto build
+echo "============ go proto start ================"
+make proto-build
+echo "============  go proto end  ================"

+ 62 - 0
store/kptstore/rw_store.go

@@ -0,0 +1,62 @@
+package kptstore
+
+import (
+	"kpt-tmr-group/config"
+	"kpt-tmr-group/pkg/logger/logrus"
+	"kpt-tmr-group/pkg/logger/zaplog"
+	"kpt-tmr-group/pkg/xerr"
+	"time"
+
+	"go.uber.org/zap"
+
+	"gorm.io/driver/mysql"
+	"gorm.io/gorm"
+	"gorm.io/gorm/logger"
+
+	"kpt-tmr-group/pkg/di"
+)
+
+var Module = di.Options(
+	di.Provide(MustNewStore),
+)
+
+type DB struct {
+	*gorm.DB
+}
+
+func NewStore(engine *gorm.DB) *DB {
+	return &DB{engine}
+}
+
+type goRmLog struct {
+}
+
+func (g goRmLog) Printf(s string, i ...interface{}) {
+	logrus.Infof(s, i...)
+}
+
+func MustNewStore(cfg *config.AppConfig) *DB {
+	newLogger := logger.New(
+		goRmLog{},
+		logger.Config{
+			SlowThreshold: 5 * time.Second,
+			LogLevel:      logger.Info,
+		},
+	)
+
+	db, err := gorm.Open(mysql.New(mysql.Config{
+		DriverName: cfg.StoreSetting.DriverName,
+		DSN:        cfg.StoreSetting.KptEventDSNRW}),
+		&gorm.Config{Logger: newLogger},
+	)
+	if err != nil {
+		zaplog.Error("MustNewStore", zap.String("Err", err.Error()), zap.Any("KptEventDSNRW", cfg.StoreSetting.KptEventDSNRW))
+		panic(xerr.WithStack(err))
+	}
+
+	if cfg.StoreSetting.ShowSQL {
+		db.Logger.LogMode(logger.Info)
+	}
+
+	return NewStore(db)
+}

Some files were not shown because too many files changed in this diff