diff --git a/.gitignore b/.gitignore index adf8f72..ee8ed69 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,4 @@ -# ---> Go -# If you prefer the allow list template instead of the deny list, see community template: -# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore -# +# Reference https://github.com/github/gitignore/blob/master/Go.gitignore # Binaries for programs and plugins *.exe *.exe~ @@ -16,8 +13,29 @@ *.out # Dependency directories (remove the comment below to include it) -# vendor/ +vendor/ -# Go workspace file -go.work +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so +# OS General +Thumbs.db +.DS_Store + +# project +*.cert +*.key +*.log +bin/ + +# Develop tools +.vscode/ +.idea/ +*.swp + +config.yaml +config.yaml.live +*.bat +/json diff --git a/.tmpl/cookiecutter.json b/.tmpl/cookiecutter.json new file mode 100644 index 0000000..779041c --- /dev/null +++ b/.tmpl/cookiecutter.json @@ -0,0 +1,8 @@ +{ + "app_name": "example", + "go_module": "tools", + "db_driver": "mysql", + "_extensions": [ + "jinja2_strcase.StrcaseExtension" + ] +} diff --git a/.tmpl/{{cookiecutter.app_name}}/api/{{cookiecutter.app_name}}/v1/{{cookiecutter.app_name}}.proto b/.tmpl/{{cookiecutter.app_name}}/api/{{cookiecutter.app_name}}/v1/{{cookiecutter.app_name}}.proto new file mode 100644 index 0000000..3a420bc --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/api/{{cookiecutter.app_name}}/v1/{{cookiecutter.app_name}}.proto @@ -0,0 +1,31 @@ +syntax = "proto3"; + +package {{cookiecutter.app_name}}.v1; + +import "google/api/annotations.proto"; +import "validate/validate.proto"; + +option go_package = "{{cookiecutter.go_module}}/api/{{cookiecutter.app_name}}/v1;v1"; +option java_multiple_files = true; +option java_package = "dev.kratos.api.{{cookiecutter.app_name}}.v1"; +option java_outer_classname = "{{cookiecutter.app_name|to_camel}}ProtoV1"; + +// The greeting service definition. +service {{cookiecutter.app_name|to_camel}} { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) { + option (google.api.http) = { + get: "/{{cookiecutter.app_name}}/{name}" + }; + } +} + +// The request message containing the user's name. +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} diff --git a/.tmpl/{{cookiecutter.app_name}}/api/{{cookiecutter.app_name}}/v1/{{cookiecutter.app_name}}_error.proto b/.tmpl/{{cookiecutter.app_name}}/api/{{cookiecutter.app_name}}/v1/{{cookiecutter.app_name}}_error.proto new file mode 100644 index 0000000..e466c42 --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/api/{{cookiecutter.app_name}}/v1/{{cookiecutter.app_name}}_error.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package api.{{cookiecutter.app_name}}.v1; +import "errors/errors.proto"; + +option go_package = "{{cookiecutter.go_module}}/api/{{cookiecutter.app_name}}/v1;v1"; +option java_multiple_files = true; +option java_package = "{{cookiecutter.app_name}}.v1.errors"; +option objc_class_prefix = "API{{cookiecutter.app_name|to_camel}}Errors"; + +enum {{cookiecutter.app_name|to_camel}}Error { + option (errors.default_code) = 500; + + USER_NOT_FOUND = 0 [(errors.code) = 404]; + CONTENT_MISSING = 1 [(errors.code) = 400]; +} diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/Makefile b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/Makefile new file mode 100644 index 0000000..2093406 --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/Makefile @@ -0,0 +1 @@ +include ../../app_makefile \ No newline at end of file diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/cmd/server/main.go b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/cmd/server/main.go new file mode 100644 index 0000000..42695f0 --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/cmd/server/main.go @@ -0,0 +1,110 @@ +package main + +import ( + "flag" + zaplogger "github.com/go-kratos/kratos/contrib/log/zap/v2" + "github.com/go-kratos/kratos/v2" + "github.com/go-kratos/kratos/v2/config" + "github.com/go-kratos/kratos/v2/config/file" + "github.com/go-kratos/kratos/v2/log" + "github.com/go-kratos/kratos/v2/middleware/tracing" + "github.com/go-kratos/kratos/v2/registry" + "github.com/go-kratos/kratos/v2/transport/grpc" + "github.com/go-kratos/kratos/v2/transport/http" + "github.com/tx7do/kratos-transport/transport/kafka" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/jaeger" + "go.opentelemetry.io/otel/sdk/resource" + tracesdk "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "os" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/conf" +) + +// go build -ldflags "-X main.Version=x.y.z" +var ( + // Name is the name of the compiled software. + Name = "{{cookiecutter.app_name}}.rpc" + // Version is the version of the compiled software. + Version = "0.1.0" + // flagconf is the config flag. + flagconf string + + id, _ = os.Hostname() +) + +func init() { + flag.StringVar(&flagconf, "conf", "configs/config.yaml", "config path, eg: -conf config.yaml") +} + +func newApp(logger log.Logger, conf *conf.Server, hs *http.Server, gs *grpc.Server, ks *kafka.Server, rr registry.Registrar) *kratos.App { + return kratos.New( + kratos.ID(id), + kratos.Name(Name), + kratos.Version(Version), + kratos.Metadata(map[string]string{}), + kratos.Logger(logger), + kratos.Server( + hs, + gs, + ks, + ), + kratos.Registrar(rr), + ) +} + +func main() { + flag.Parse() + encoderCfg := zapcore.EncoderConfig{ + LevelKey: "level", + EncodeLevel: zapcore.LowercaseLevelEncoder, + } + out := zapcore.AddSync(os.Stdout) // replace real writer + core := zapcore.NewCore(zapcore.NewJSONEncoder(encoderCfg), out, zap.DebugLevel) + zlogger := zap.New(core).WithOptions() + logger := log.With(zaplogger.NewLogger(zlogger), + "ts", log.DefaultTimestamp, + "caller", log.DefaultCaller, + "service.id", id, + "service.name", Name, + "service.version", Version, + "trace_id", tracing.TraceID(), + "span_id", tracing.SpanID(), + ) + c := config.New( + config.WithSource( + file.NewSource(flagconf), + ), + ) + if err := c.Load(); err != nil { + panic(err) + } + + var bc conf.Bootstrap + if err := c.Scan(&bc); err != nil { + panic(err) + } + exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(bc.Server.TraceEndpoint))) + if err != nil { + panic(err) + } + tp := tracesdk.NewTracerProvider( + tracesdk.WithBatcher(exp), + tracesdk.WithResource(resource.NewSchemaless( + semconv.ServiceNameKey.String(Name), + )), + ) + otel.SetTracerProvider(tp) + app, cleanup, err := initApp(bc.Server, bc.Data, bc.Queue, logger) + if err != nil { + panic(err) + } + defer cleanup() + + // start and wait for stop signal + if err := app.Run(); err != nil { + panic(err) + } +} diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/cmd/server/wire.go b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/cmd/server/wire.go new file mode 100644 index 0000000..0923fa9 --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/cmd/server/wire.go @@ -0,0 +1,22 @@ +//go:build wireinject +// +build wireinject + +// The build tag makes sure the stub is not built in the final build. + +package main + +import ( + "github.com/go-kratos/kratos/v2" + "github.com/go-kratos/kratos/v2/log" + "github.com/google/wire" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/biz" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/conf" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/data" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/server" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/service" +) + +// initApp init kratos application. +func initApp(*conf.Server, *conf.Data, *conf.Queue, log.Logger) (*kratos.App, func(), error) { + panic(wire.Build(server.ProviderSet, data.ProviderSet, biz.ProviderSet, service.ProviderSet, newApp)) +} diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/cmd/server/wire_gen.go b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/cmd/server/wire_gen.go new file mode 100644 index 0000000..59e8e2c --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/cmd/server/wire_gen.go @@ -0,0 +1,44 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +import ( + "github.com/go-kratos/kratos/v2" + "github.com/go-kratos/kratos/v2/log" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/biz" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/conf" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/data" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/server" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/service" +) + +// Injectors from wire.go: + +// initApp init kratos application. +func initApp(confServer *conf.Server, confData *conf.Data, confQueue *conf.Queue, logger log.Logger) (*kratos.App, func(), error) { + broker := data.NewKafkaBroker(confQueue) + client := data.NewAsynqClient(confQueue) + discovery := server.NewDiscovery(confServer) + {{cookiecutter.app_name}}Client := data.New{{cookiecutter.app_name|to_camel}}Client(discovery) + dataData, cleanup, err := data.NewData(confData, broker, client, {{cookiecutter.app_name}}Client, logger) + if err != nil { + return nil, nil, err + } + {{cookiecutter.app_name|to_lower_camel}}Repo := data.New{{cookiecutter.app_name|to_camel}}Repo(dataData, logger) + transaction := data.NewTransaction(dataData) + cache := data.NewCache(dataData) + {{cookiecutter.app_name|to_lower_camel}}Usecase := biz.New{{cookiecutter.app_name|to_camel}}Usecase({{cookiecutter.app_name|to_lower_camel}}Repo, cache, transaction, logger) + {{cookiecutter.app_name|to_lower_camel}}Service := service.New{{cookiecutter.app_name|to_camel}}Service({{cookiecutter.app_name|to_lower_camel}}Usecase, logger) + httpServer := server.NewHTTPServer(confServer, {{cookiecutter.app_name|to_lower_camel}}Service, logger) + grpcServer := server.NewGRPCServer(confServer, {{cookiecutter.app_name|to_lower_camel}}Service, logger) + kafkaServer := server.NewKafkaServer(confQueue, {{cookiecutter.app_name|to_lower_camel}}Usecase, logger) + registrar := server.NewRegistrar(confServer) + app := newApp(logger, confServer, httpServer, grpcServer, kafkaServer, registrar) + return app, func() { + cleanup() + }, nil +} diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/cmd/worker/main.go b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/cmd/worker/main.go new file mode 100644 index 0000000..3be5fda --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/cmd/worker/main.go @@ -0,0 +1,86 @@ +package main + +import ( + "flag" + zaplogger "github.com/go-kratos/kratos/contrib/log/zap/v2" + "github.com/go-kratos/kratos/v2/config" + "github.com/go-kratos/kratos/v2/config/file" + "github.com/go-kratos/kratos/v2/log" + "github.com/go-kratos/kratos/v2/middleware/tracing" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/jaeger" + "go.opentelemetry.io/otel/sdk/resource" + tracesdk "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "os" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/conf" +) + +// go build -ldflags "-X main.Version=x.y.z" +var ( + // Name is the name of the compiled software. + Name = "{{cookiecutter.app_name|lower}}.async" + // Version is the version of the compiled software. + Version = "0.1.0" + // flagconf is the config flag. + flagconf string + + id, _ = os.Hostname() +) + +func init() { + flag.StringVar(&flagconf, "conf", "configs/config.yaml", "config path, eg: -conf config.yaml") +} + +func main() { + flag.Parse() + encoderCfg := zapcore.EncoderConfig{ + LevelKey: "level", + EncodeLevel: zapcore.LowercaseLevelEncoder, + } + out := zapcore.AddSync(os.Stdout) // replace real writer + core := zapcore.NewCore(zapcore.NewJSONEncoder(encoderCfg), out, zap.DebugLevel) + zlogger := zap.New(core).WithOptions() + logger := log.With(zaplogger.NewLogger(zlogger), + "ts", log.DefaultTimestamp, + "caller", log.DefaultCaller, + "service.id", id, + "service.name", Name, + "service.version", Version, + "trace_id", tracing.TraceID(), + "span_id", tracing.SpanID(), + ) + c := config.New( + config.WithSource( + file.NewSource(flagconf), + ), + ) + if err := c.Load(); err != nil { + panic(err) + } + var bc conf.Bootstrap + if err := c.Scan(&bc); err != nil { + panic(err) + } + exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(bc.Server.TraceEndpoint))) + if err != nil { + panic(err) + } + tp := tracesdk.NewTracerProvider( + tracesdk.WithBatcher(exp), + tracesdk.WithResource(resource.NewSchemaless( + semconv.ServiceNameKey.String(Name), + )), + ) + otel.SetTracerProvider(tp) + job, cleanup, err := initApp(bc.Data, bc.Server, bc.Queue, logger) + if err != nil { + panic(err) + } + defer cleanup() + if err := job.Run(); err != nil { + panic(err) + } +} diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/cmd/worker/wire.go b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/cmd/worker/wire.go new file mode 100644 index 0000000..2de1376 --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/cmd/worker/wire.go @@ -0,0 +1,20 @@ +//go:build wireinject +// +build wireinject + +// The build tag makes sure the stub is not built in the final build. + +package main + +import ( + "github.com/go-kratos/kratos/v2/log" + "github.com/google/wire" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/biz" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/conf" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/data" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/server" +) + +// initApp init application. +func initApp(*conf.Data, *conf.Server, *conf.Queue, log.Logger) (*server.AsynqServer, func(), error) { + panic(wire.Build(data.ProviderSet, server.ProviderSet, biz.ProviderSet)) +} \ No newline at end of file diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/cmd/worker/wire_gen.go b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/cmd/worker/wire_gen.go new file mode 100644 index 0000000..8333cc5 --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/cmd/worker/wire_gen.go @@ -0,0 +1,37 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +import ( + "github.com/go-kratos/kratos/v2/log" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/biz" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/conf" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/data" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/server" +) + +// Injectors from wire.go: + +// initApp init application. +func initApp(confData *conf.Data, confServer *conf.Server, confQueue *conf.Queue, logger log.Logger) (*server.AsynqServer, func(), error) { + broker := data.NewKafkaBroker(confQueue) + client := data.NewAsynqClient(confQueue) + discovery := server.NewDiscovery(confServer) + {{cookiecutter.app_name}}Client := data.New{{cookiecutter.app_name|to_camel}}Client(discovery) + dataData, cleanup, err := data.NewData(confData, broker, client, {{cookiecutter.app_name}}Client, logger) + if err != nil { + return nil, nil, err + } + {{cookiecutter.app_name|to_lower_camel}}Repo := data.New{{cookiecutter.app_name|capitalize}}Repo(dataData, logger) + transaction := data.NewTransaction(dataData) + cache := data.NewCache(dataData) + {{cookiecutter.app_name|to_lower_camel}}Usecase := biz.New{{cookiecutter.app_name|capitalize}}Usecase({{cookiecutter.app_name|to_lower_camel}}Repo, cache, transaction, logger) + asynqServer := server.NewAsynqServer(confQueue, {{cookiecutter.app_name|to_lower_camel}}Usecase) + return asynqServer, func() { + cleanup() + }, nil +} \ No newline at end of file diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/configs/config.yaml.example b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/configs/config.yaml.example new file mode 100644 index 0000000..6db812a --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/configs/config.yaml.example @@ -0,0 +1,39 @@ +server: + http: + addr: 0.0.0.0:8001 + timeout: 1s + grpc: + addr: 0.0.0.0:9001 + timeout: 1s + etcd: + addr: + - 127.0.0.1:2379 + trace_endpoint: http://127.0.0.1:14268/api/traces + jwt_token: "L9Irpo8hdCaidG1tsEiW1pJuL9DsHU0vj" + env: "qa" +data: + database: + {% if cookiecutter.db_driver == 'mysql' -%} + driver: mysql + source: root:@tcp(127.0.0.1:3306)/{{cookiecutter.app_name}}?charset=utf8mb4&parseTime=True&loc=UTC + {%- elif cookiecutter.db_driver == 'postgres' -%} + driver: postgres + source: host=localhost port=5432 user=postgres dbname=example sslmode=disable + {%- endif %} + redis: + addr: 127.0.0.1:6379 + read_timeout: 0.2s + write_timeout: 0.2s + +queue: + kafka: + addrs: + - 127.0.0.1:9092 + topic: example + group: example + + asynq: + addr: 127.0.0.1:6379 + read_timeout: 0.2s + write_timeout: 0.2s + concurrency: 10 diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/generate.go b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/generate.go new file mode 100644 index 0000000..081f415 --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/generate.go @@ -0,0 +1,3 @@ +package {{cookiecutter.app_name}} + +//go:generate kratos proto client api diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/biz/README.md b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/biz/README.md new file mode 100644 index 0000000..26a66b6 --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/biz/README.md @@ -0,0 +1 @@ +# Biz diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/biz/biz.go b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/biz/biz.go new file mode 100644 index 0000000..c5c3a80 --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/biz/biz.go @@ -0,0 +1,22 @@ +package biz + +import ( + "context" + "github.com/google/wire" + "gorm.io/gorm" +) + +// ProviderSet is biz providers. +var ProviderSet = wire.NewSet(New{{cookiecutter.app_name|to_camel}}Usecase) + +type Cache interface { + GetValue(ctx context.Context, key string) (string, error) + DelValue(ctx context.Context, keys ...string) error + WriteValue(ctx context.Context, key string, value interface{}, timeout int32) error + Remember(ctx context.Context, key string, secone int32, fn func(ctx context.Context) (interface{}, error)) ([]byte, error) +} + +type Transaction interface { + InTx(context.Context, func(ctx context.Context) error) error + DB(ctx context.Context) *gorm.DB +} diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/biz/queue.go b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/biz/queue.go new file mode 100644 index 0000000..162b4cf --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/biz/queue.go @@ -0,0 +1,34 @@ +package biz + +import ( + "context" + "fmt" + "github.com/tx7do/kratos-transport/broker" +) + +type ExampleStruct struct { + Name string `json:"name"` + ID int `json:"id"` +} + +func ExampleCreator() broker.Any { return &ExampleStruct{} } + +type ExmapleHandler func(_ context.Context, topic string, headers broker.Headers, msg *ExampleStruct) error + +func RegisterExampleHandler(fnc ExmapleHandler) broker.Handler { + return func(ctx context.Context, event broker.Event) error { + if event.Error() != nil { + return event.Error() + } + msg, ok := event.Message().Body.(*ExampleStruct) + if !ok { + return fmt.Errorf("[Kafka] unsupported type: %T", event.Message().Body) + } + + if err := fnc(ctx, event.Topic(), event.Message().Headers, msg); err != nil { + return err + } + + return nil + } +} diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/biz/{{cookiecutter.app_name}}.go b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/biz/{{cookiecutter.app_name}}.go new file mode 100644 index 0000000..17581ee --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/biz/{{cookiecutter.app_name}}.go @@ -0,0 +1,56 @@ +package biz + +import ( + "context" + "github.com/go-kratos/kratos/v2/log" + "github.com/hibiken/asynq" + "github.com/tx7do/kratos-transport/broker" +) + +type {{cookiecutter.app_name|to_camel}} struct { + Hello string +} + +type {{cookiecutter.app_name|to_camel}}Repo interface { + Create{{cookiecutter.app_name|to_camel}}(context.Context, *{{cookiecutter.app_name|to_camel}}) error + Update{{cookiecutter.app_name|to_camel}}(context.Context, *{{cookiecutter.app_name|to_camel}}) error +} + +type {{cookiecutter.app_name|to_camel}}Usecase struct { + repo {{cookiecutter.app_name|to_camel}}Repo + cache Cache + log *log.Helper + tx Transaction +} + +func New{{cookiecutter.app_name|to_camel}}Usecase(repo {{cookiecutter.app_name|to_camel}}Repo, cache Cache, tx Transaction, logger log.Logger) *{{cookiecutter.app_name|to_camel}}Usecase { + return &{{cookiecutter.app_name|to_camel}}Usecase{ + repo: repo, + cache: cache, + tx: tx, + log: log.NewHelper(logger), + } +} + +func (uc *{{cookiecutter.app_name|to_camel}}Usecase) Create(ctx context.Context, g *{{cookiecutter.app_name|to_camel}}) error { + return uc.repo.Create{{cookiecutter.app_name|to_camel}}(ctx, g) +} + +func (uc *{{cookiecutter.app_name|to_camel}}Usecase) Update(ctx context.Context, g *{{cookiecutter.app_name|to_camel}}) error { + return uc.repo.Update{{cookiecutter.app_name|to_camel}}(ctx, g) +} + +func (uc *{{cookiecutter.app_name|to_camel}}Usecase) KakfaExampleConsumer(_ context.Context, topic string, headers broker.Headers, msg *ExampleStruct) error { + log.Infof("Topic %s, Headers: %+v, Payload: %+v\n", topic, headers, msg) + return nil +} + +const {{cookiecutter.app_name|to_camel}}TaskName = "{{cookiecutter.app_name}}:task" + +// Asynq{{cookiecutter.app_name|to_camel}}TaskHandler Asynq handler task +func (uc *{{cookiecutter.app_name|to_camel}}Usecase) Asynq{{cookiecutter.app_name|to_camel}}TaskHandler(ctx context.Context, t *asynq.Task) error { + // do something + // do some else logging + log.Info("Asynq{{cookiecutter.app_name|to_camel}}TaskHandle: ", string(t.Payload())) + return nil +} diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/conf/conf.pb.go b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/conf/conf.pb.go new file mode 100644 index 0000000..86cd454 --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/conf/conf.pb.go @@ -0,0 +1,1107 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.21.5 +// source: internal/conf/conf.proto + +package conf + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + durationpb "google.golang.org/protobuf/types/known/durationpb" + 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 Bootstrap struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Server *Server `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"` + Data *Data `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + Queue *Queue `protobuf:"bytes,3,opt,name=queue,proto3" json:"queue,omitempty"` +} + +func (x *Bootstrap) Reset() { + *x = Bootstrap{} + if protoimpl.UnsafeEnabled { + mi := &file_internal_conf_conf_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Bootstrap) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Bootstrap) ProtoMessage() {} + +func (x *Bootstrap) ProtoReflect() protoreflect.Message { + mi := &file_internal_conf_conf_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 Bootstrap.ProtoReflect.Descriptor instead. +func (*Bootstrap) Descriptor() ([]byte, []int) { + return file_internal_conf_conf_proto_rawDescGZIP(), []int{0} +} + +func (x *Bootstrap) GetServer() *Server { + if x != nil { + return x.Server + } + return nil +} + +func (x *Bootstrap) GetData() *Data { + if x != nil { + return x.Data + } + return nil +} + +func (x *Bootstrap) GetQueue() *Queue { + if x != nil { + return x.Queue + } + return nil +} + +type Server struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Http *Server_HTTP `protobuf:"bytes,1,opt,name=http,proto3" json:"http,omitempty"` + Grpc *Server_GRPC `protobuf:"bytes,2,opt,name=grpc,proto3" json:"grpc,omitempty"` + Etcd *Server_ETCD `protobuf:"bytes,3,opt,name=etcd,proto3" json:"etcd,omitempty"` + TraceEndpoint string `protobuf:"bytes,4,opt,name=trace_endpoint,json=traceEndpoint,proto3" json:"trace_endpoint,omitempty"` +} + +func (x *Server) Reset() { + *x = Server{} + if protoimpl.UnsafeEnabled { + mi := &file_internal_conf_conf_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Server) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Server) ProtoMessage() {} + +func (x *Server) ProtoReflect() protoreflect.Message { + mi := &file_internal_conf_conf_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 Server.ProtoReflect.Descriptor instead. +func (*Server) Descriptor() ([]byte, []int) { + return file_internal_conf_conf_proto_rawDescGZIP(), []int{1} +} + +func (x *Server) GetHttp() *Server_HTTP { + if x != nil { + return x.Http + } + return nil +} + +func (x *Server) GetGrpc() *Server_GRPC { + if x != nil { + return x.Grpc + } + return nil +} + +func (x *Server) GetEtcd() *Server_ETCD { + if x != nil { + return x.Etcd + } + return nil +} + +func (x *Server) GetTraceEndpoint() string { + if x != nil { + return x.TraceEndpoint + } + return "" +} + +type Data struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Database *Data_Database `protobuf:"bytes,1,opt,name=database,proto3" json:"database,omitempty"` + Redis *Data_Redis `protobuf:"bytes,2,opt,name=redis,proto3" json:"redis,omitempty"` +} + +func (x *Data) Reset() { + *x = Data{} + if protoimpl.UnsafeEnabled { + mi := &file_internal_conf_conf_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Data) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Data) ProtoMessage() {} + +func (x *Data) ProtoReflect() protoreflect.Message { + mi := &file_internal_conf_conf_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 Data.ProtoReflect.Descriptor instead. +func (*Data) Descriptor() ([]byte, []int) { + return file_internal_conf_conf_proto_rawDescGZIP(), []int{2} +} + +func (x *Data) GetDatabase() *Data_Database { + if x != nil { + return x.Database + } + return nil +} + +func (x *Data) GetRedis() *Data_Redis { + if x != nil { + return x.Redis + } + return nil +} + +type Queue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Kafka *Queue_Kafka `protobuf:"bytes,1,opt,name=kafka,proto3" json:"kafka,omitempty"` + Asynq *Queue_Asynq `protobuf:"bytes,2,opt,name=asynq,proto3" json:"asynq,omitempty"` +} + +func (x *Queue) Reset() { + *x = Queue{} + if protoimpl.UnsafeEnabled { + mi := &file_internal_conf_conf_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Queue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Queue) ProtoMessage() {} + +func (x *Queue) ProtoReflect() protoreflect.Message { + mi := &file_internal_conf_conf_proto_msgTypes[3] + 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 Queue.ProtoReflect.Descriptor instead. +func (*Queue) Descriptor() ([]byte, []int) { + return file_internal_conf_conf_proto_rawDescGZIP(), []int{3} +} + +func (x *Queue) GetKafka() *Queue_Kafka { + if x != nil { + return x.Kafka + } + return nil +} + +func (x *Queue) GetAsynq() *Queue_Asynq { + if x != nil { + return x.Asynq + } + return nil +} + +type Server_HTTP struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Network string `protobuf:"bytes,1,opt,name=network,proto3" json:"network,omitempty"` + Addr string `protobuf:"bytes,2,opt,name=addr,proto3" json:"addr,omitempty"` + Timeout *durationpb.Duration `protobuf:"bytes,3,opt,name=timeout,proto3" json:"timeout,omitempty"` +} + +func (x *Server_HTTP) Reset() { + *x = Server_HTTP{} + if protoimpl.UnsafeEnabled { + mi := &file_internal_conf_conf_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Server_HTTP) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Server_HTTP) ProtoMessage() {} + +func (x *Server_HTTP) ProtoReflect() protoreflect.Message { + mi := &file_internal_conf_conf_proto_msgTypes[4] + 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 Server_HTTP.ProtoReflect.Descriptor instead. +func (*Server_HTTP) Descriptor() ([]byte, []int) { + return file_internal_conf_conf_proto_rawDescGZIP(), []int{1, 0} +} + +func (x *Server_HTTP) GetNetwork() string { + if x != nil { + return x.Network + } + return "" +} + +func (x *Server_HTTP) GetAddr() string { + if x != nil { + return x.Addr + } + return "" +} + +func (x *Server_HTTP) GetTimeout() *durationpb.Duration { + if x != nil { + return x.Timeout + } + return nil +} + +type Server_GRPC struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Network string `protobuf:"bytes,1,opt,name=network,proto3" json:"network,omitempty"` + Addr string `protobuf:"bytes,2,opt,name=addr,proto3" json:"addr,omitempty"` + Timeout *durationpb.Duration `protobuf:"bytes,3,opt,name=timeout,proto3" json:"timeout,omitempty"` +} + +func (x *Server_GRPC) Reset() { + *x = Server_GRPC{} + if protoimpl.UnsafeEnabled { + mi := &file_internal_conf_conf_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Server_GRPC) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Server_GRPC) ProtoMessage() {} + +func (x *Server_GRPC) ProtoReflect() protoreflect.Message { + mi := &file_internal_conf_conf_proto_msgTypes[5] + 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 Server_GRPC.ProtoReflect.Descriptor instead. +func (*Server_GRPC) Descriptor() ([]byte, []int) { + return file_internal_conf_conf_proto_rawDescGZIP(), []int{1, 1} +} + +func (x *Server_GRPC) GetNetwork() string { + if x != nil { + return x.Network + } + return "" +} + +func (x *Server_GRPC) GetAddr() string { + if x != nil { + return x.Addr + } + return "" +} + +func (x *Server_GRPC) GetTimeout() *durationpb.Duration { + if x != nil { + return x.Timeout + } + return nil +} + +type Server_ETCD struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Addr []string `protobuf:"bytes,1,rep,name=addr,proto3" json:"addr,omitempty"` + Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"` + Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"` +} + +func (x *Server_ETCD) Reset() { + *x = Server_ETCD{} + if protoimpl.UnsafeEnabled { + mi := &file_internal_conf_conf_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Server_ETCD) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Server_ETCD) ProtoMessage() {} + +func (x *Server_ETCD) ProtoReflect() protoreflect.Message { + mi := &file_internal_conf_conf_proto_msgTypes[6] + 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 Server_ETCD.ProtoReflect.Descriptor instead. +func (*Server_ETCD) Descriptor() ([]byte, []int) { + return file_internal_conf_conf_proto_rawDescGZIP(), []int{1, 2} +} + +func (x *Server_ETCD) GetAddr() []string { + if x != nil { + return x.Addr + } + return nil +} + +func (x *Server_ETCD) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *Server_ETCD) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +type Data_Database struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Driver string `protobuf:"bytes,1,opt,name=driver,proto3" json:"driver,omitempty"` + Source string `protobuf:"bytes,2,opt,name=source,proto3" json:"source,omitempty"` +} + +func (x *Data_Database) Reset() { + *x = Data_Database{} + if protoimpl.UnsafeEnabled { + mi := &file_internal_conf_conf_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Data_Database) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Data_Database) ProtoMessage() {} + +func (x *Data_Database) ProtoReflect() protoreflect.Message { + mi := &file_internal_conf_conf_proto_msgTypes[7] + 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 Data_Database.ProtoReflect.Descriptor instead. +func (*Data_Database) Descriptor() ([]byte, []int) { + return file_internal_conf_conf_proto_rawDescGZIP(), []int{2, 0} +} + +func (x *Data_Database) GetDriver() string { + if x != nil { + return x.Driver + } + return "" +} + +func (x *Data_Database) GetSource() string { + if x != nil { + return x.Source + } + return "" +} + +type Data_Redis struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Network string `protobuf:"bytes,1,opt,name=network,proto3" json:"network,omitempty"` + Addr string `protobuf:"bytes,2,opt,name=addr,proto3" json:"addr,omitempty"` + Db int32 `protobuf:"varint,3,opt,name=db,proto3" json:"db,omitempty"` + Password string `protobuf:"bytes,4,opt,name=password,proto3" json:"password,omitempty"` + Pool int32 `protobuf:"varint,5,opt,name=pool,proto3" json:"pool,omitempty"` + ReadTimeout *durationpb.Duration `protobuf:"bytes,6,opt,name=read_timeout,json=readTimeout,proto3" json:"read_timeout,omitempty"` + WriteTimeout *durationpb.Duration `protobuf:"bytes,7,opt,name=write_timeout,json=writeTimeout,proto3" json:"write_timeout,omitempty"` +} + +func (x *Data_Redis) Reset() { + *x = Data_Redis{} + if protoimpl.UnsafeEnabled { + mi := &file_internal_conf_conf_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Data_Redis) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Data_Redis) ProtoMessage() {} + +func (x *Data_Redis) ProtoReflect() protoreflect.Message { + mi := &file_internal_conf_conf_proto_msgTypes[8] + 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 Data_Redis.ProtoReflect.Descriptor instead. +func (*Data_Redis) Descriptor() ([]byte, []int) { + return file_internal_conf_conf_proto_rawDescGZIP(), []int{2, 1} +} + +func (x *Data_Redis) GetNetwork() string { + if x != nil { + return x.Network + } + return "" +} + +func (x *Data_Redis) GetAddr() string { + if x != nil { + return x.Addr + } + return "" +} + +func (x *Data_Redis) GetDb() int32 { + if x != nil { + return x.Db + } + return 0 +} + +func (x *Data_Redis) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +func (x *Data_Redis) GetPool() int32 { + if x != nil { + return x.Pool + } + return 0 +} + +func (x *Data_Redis) GetReadTimeout() *durationpb.Duration { + if x != nil { + return x.ReadTimeout + } + return nil +} + +func (x *Data_Redis) GetWriteTimeout() *durationpb.Duration { + if x != nil { + return x.WriteTimeout + } + return nil +} + +type Queue_Kafka struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Addrs []string `protobuf:"bytes,1,rep,name=addrs,proto3" json:"addrs,omitempty"` + Topic string `protobuf:"bytes,2,opt,name=topic,proto3" json:"topic,omitempty"` + Group string `protobuf:"bytes,3,opt,name=group,proto3" json:"group,omitempty"` + Username string `protobuf:"bytes,4,opt,name=username,proto3" json:"username,omitempty"` + Password string `protobuf:"bytes,5,opt,name=password,proto3" json:"password,omitempty"` +} + +func (x *Queue_Kafka) Reset() { + *x = Queue_Kafka{} + if protoimpl.UnsafeEnabled { + mi := &file_internal_conf_conf_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Queue_Kafka) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Queue_Kafka) ProtoMessage() {} + +func (x *Queue_Kafka) ProtoReflect() protoreflect.Message { + mi := &file_internal_conf_conf_proto_msgTypes[9] + 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 Queue_Kafka.ProtoReflect.Descriptor instead. +func (*Queue_Kafka) Descriptor() ([]byte, []int) { + return file_internal_conf_conf_proto_rawDescGZIP(), []int{3, 0} +} + +func (x *Queue_Kafka) GetAddrs() []string { + if x != nil { + return x.Addrs + } + return nil +} + +func (x *Queue_Kafka) GetTopic() string { + if x != nil { + return x.Topic + } + return "" +} + +func (x *Queue_Kafka) GetGroup() string { + if x != nil { + return x.Group + } + return "" +} + +func (x *Queue_Kafka) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *Queue_Kafka) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +type Queue_Asynq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Network string `protobuf:"bytes,1,opt,name=network,proto3" json:"network,omitempty"` + Addr string `protobuf:"bytes,2,opt,name=addr,proto3" json:"addr,omitempty"` + Db int32 `protobuf:"varint,3,opt,name=db,proto3" json:"db,omitempty"` + Password string `protobuf:"bytes,4,opt,name=password,proto3" json:"password,omitempty"` + Pool int32 `protobuf:"varint,5,opt,name=pool,proto3" json:"pool,omitempty"` + ReadTimeout *durationpb.Duration `protobuf:"bytes,6,opt,name=read_timeout,json=readTimeout,proto3" json:"read_timeout,omitempty"` + WriteTimeout *durationpb.Duration `protobuf:"bytes,7,opt,name=write_timeout,json=writeTimeout,proto3" json:"write_timeout,omitempty"` + Concurrency int32 `protobuf:"varint,8,opt,name=concurrency,proto3" json:"concurrency,omitempty"` +} + +func (x *Queue_Asynq) Reset() { + *x = Queue_Asynq{} + if protoimpl.UnsafeEnabled { + mi := &file_internal_conf_conf_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Queue_Asynq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Queue_Asynq) ProtoMessage() {} + +func (x *Queue_Asynq) ProtoReflect() protoreflect.Message { + mi := &file_internal_conf_conf_proto_msgTypes[10] + 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 Queue_Asynq.ProtoReflect.Descriptor instead. +func (*Queue_Asynq) Descriptor() ([]byte, []int) { + return file_internal_conf_conf_proto_rawDescGZIP(), []int{3, 1} +} + +func (x *Queue_Asynq) GetNetwork() string { + if x != nil { + return x.Network + } + return "" +} + +func (x *Queue_Asynq) GetAddr() string { + if x != nil { + return x.Addr + } + return "" +} + +func (x *Queue_Asynq) GetDb() int32 { + if x != nil { + return x.Db + } + return 0 +} + +func (x *Queue_Asynq) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +func (x *Queue_Asynq) GetPool() int32 { + if x != nil { + return x.Pool + } + return 0 +} + +func (x *Queue_Asynq) GetReadTimeout() *durationpb.Duration { + if x != nil { + return x.ReadTimeout + } + return nil +} + +func (x *Queue_Asynq) GetWriteTimeout() *durationpb.Duration { + if x != nil { + return x.WriteTimeout + } + return nil +} + +func (x *Queue_Asynq) GetConcurrency() int32 { + if x != nil { + return x.Concurrency + } + return 0 +} + +var File_internal_conf_conf_proto protoreflect.FileDescriptor + +var file_internal_conf_conf_proto_rawDesc = []byte{ + 0x0a, 0x18, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x2f, + 0x63, 0x6f, 0x6e, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x6b, 0x72, 0x61, 0x74, + 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x86, 0x01, 0x0a, 0x09, 0x42, 0x6f, 0x6f, 0x74, 0x73, + 0x74, 0x72, 0x61, 0x70, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x12, 0x24, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, + 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x61, 0x74, 0x61, + 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x27, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x75, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x05, 0x71, 0x75, 0x65, 0x75, 0x65, 0x22, + 0xe0, 0x03, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x2b, 0x0a, 0x04, 0x68, 0x74, + 0x74, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, + 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x54, 0x54, + 0x50, 0x52, 0x04, 0x68, 0x74, 0x74, 0x70, 0x12, 0x2b, 0x0a, 0x04, 0x67, 0x72, 0x70, 0x63, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x47, 0x52, 0x50, 0x43, 0x52, 0x04, + 0x67, 0x72, 0x70, 0x63, 0x12, 0x2b, 0x0a, 0x04, 0x65, 0x74, 0x63, 0x64, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x54, 0x43, 0x44, 0x52, 0x04, 0x65, 0x74, 0x63, + 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x63, 0x65, + 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x1a, 0x69, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, + 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, + 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x33, + 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, + 0x6f, 0x75, 0x74, 0x1a, 0x69, 0x0a, 0x04, 0x47, 0x52, 0x50, 0x43, 0x12, 0x18, 0x0a, 0x07, 0x6e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x74, 0x69, 0x6d, + 0x65, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x1a, 0x52, + 0x0a, 0x04, 0x45, 0x54, 0x43, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, + 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, + 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x22, 0x9d, 0x03, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x35, 0x0a, 0x08, 0x64, + 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x2e, + 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, + 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x05, 0x72, 0x65, 0x64, 0x69, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x16, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, + 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x64, 0x69, 0x73, 0x52, 0x05, 0x72, 0x65, 0x64, 0x69, 0x73, + 0x1a, 0x3a, 0x0a, 0x08, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, + 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x72, + 0x69, 0x76, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a, 0xf3, 0x01, 0x0a, + 0x05, 0x52, 0x65, 0x64, 0x69, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x61, 0x64, 0x64, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x64, 0x62, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x02, 0x64, 0x62, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, + 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, + 0x70, 0x6f, 0x6f, 0x6c, 0x12, 0x3c, 0x0a, 0x0c, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x6f, 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x6f, + 0x75, 0x74, 0x12, 0x3e, 0x0a, 0x0d, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x6f, 0x75, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x77, 0x72, 0x69, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, + 0x75, 0x74, 0x22, 0x81, 0x04, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x75, 0x65, 0x12, 0x2d, 0x0a, 0x05, + 0x6b, 0x61, 0x66, 0x6b, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6b, 0x72, + 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x2e, 0x4b, + 0x61, 0x66, 0x6b, 0x61, 0x52, 0x05, 0x6b, 0x61, 0x66, 0x6b, 0x61, 0x12, 0x2d, 0x0a, 0x05, 0x61, + 0x73, 0x79, 0x6e, 0x71, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6b, 0x72, 0x61, + 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x2e, 0x41, 0x73, + 0x79, 0x6e, 0x71, 0x52, 0x05, 0x61, 0x73, 0x79, 0x6e, 0x71, 0x1a, 0x81, 0x01, 0x0a, 0x05, 0x4b, + 0x61, 0x66, 0x6b, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, + 0x70, 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, + 0x12, 0x14, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x1a, 0x95, + 0x02, 0x0a, 0x05, 0x41, 0x73, 0x79, 0x6e, 0x71, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x64, 0x62, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x02, 0x64, 0x62, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x04, 0x70, 0x6f, 0x6f, 0x6c, 0x12, 0x3c, 0x0a, 0x0c, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x64, 0x54, 0x69, 0x6d, + 0x65, 0x6f, 0x75, 0x74, 0x12, 0x3e, 0x0a, 0x0d, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x77, 0x72, 0x69, 0x74, 0x65, 0x54, 0x69, 0x6d, + 0x65, 0x6f, 0x75, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x42, 0x24, 0x5a, 0x22, 0x73, 0x61, 0x6e, 0x2f, 0x61, 0x70, + 0x70, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x3b, 0x63, 0x6f, 0x6e, 0x66, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_internal_conf_conf_proto_rawDescOnce sync.Once + file_internal_conf_conf_proto_rawDescData = file_internal_conf_conf_proto_rawDesc +) + +func file_internal_conf_conf_proto_rawDescGZIP() []byte { + file_internal_conf_conf_proto_rawDescOnce.Do(func() { + file_internal_conf_conf_proto_rawDescData = protoimpl.X.CompressGZIP(file_internal_conf_conf_proto_rawDescData) + }) + return file_internal_conf_conf_proto_rawDescData +} + +var file_internal_conf_conf_proto_msgTypes = make([]protoimpl.MessageInfo, 11) +var file_internal_conf_conf_proto_goTypes = []interface{}{ + (*Bootstrap)(nil), // 0: kratos.api.Bootstrap + (*Server)(nil), // 1: kratos.api.Server + (*Data)(nil), // 2: kratos.api.Data + (*Queue)(nil), // 3: kratos.api.Queue + (*Server_HTTP)(nil), // 4: kratos.api.Server.HTTP + (*Server_GRPC)(nil), // 5: kratos.api.Server.GRPC + (*Server_ETCD)(nil), // 6: kratos.api.Server.ETCD + (*Data_Database)(nil), // 7: kratos.api.Data.Database + (*Data_Redis)(nil), // 8: kratos.api.Data.Redis + (*Queue_Kafka)(nil), // 9: kratos.api.Queue.Kafka + (*Queue_Asynq)(nil), // 10: kratos.api.Queue.Asynq + (*durationpb.Duration)(nil), // 11: google.protobuf.Duration +} +var file_internal_conf_conf_proto_depIdxs = []int32{ + 1, // 0: kratos.api.Bootstrap.server:type_name -> kratos.api.Server + 2, // 1: kratos.api.Bootstrap.data:type_name -> kratos.api.Data + 3, // 2: kratos.api.Bootstrap.queue:type_name -> kratos.api.Queue + 4, // 3: kratos.api.Server.http:type_name -> kratos.api.Server.HTTP + 5, // 4: kratos.api.Server.grpc:type_name -> kratos.api.Server.GRPC + 6, // 5: kratos.api.Server.etcd:type_name -> kratos.api.Server.ETCD + 7, // 6: kratos.api.Data.database:type_name -> kratos.api.Data.Database + 8, // 7: kratos.api.Data.redis:type_name -> kratos.api.Data.Redis + 9, // 8: kratos.api.Queue.kafka:type_name -> kratos.api.Queue.Kafka + 10, // 9: kratos.api.Queue.asynq:type_name -> kratos.api.Queue.Asynq + 11, // 10: kratos.api.Server.HTTP.timeout:type_name -> google.protobuf.Duration + 11, // 11: kratos.api.Server.GRPC.timeout:type_name -> google.protobuf.Duration + 11, // 12: kratos.api.Data.Redis.read_timeout:type_name -> google.protobuf.Duration + 11, // 13: kratos.api.Data.Redis.write_timeout:type_name -> google.protobuf.Duration + 11, // 14: kratos.api.Queue.Asynq.read_timeout:type_name -> google.protobuf.Duration + 11, // 15: kratos.api.Queue.Asynq.write_timeout:type_name -> google.protobuf.Duration + 16, // [16:16] is the sub-list for method output_type + 16, // [16:16] is the sub-list for method input_type + 16, // [16:16] is the sub-list for extension type_name + 16, // [16:16] is the sub-list for extension extendee + 0, // [0:16] is the sub-list for field type_name +} + +func init() { file_internal_conf_conf_proto_init() } +func file_internal_conf_conf_proto_init() { + if File_internal_conf_conf_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_internal_conf_conf_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Bootstrap); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_internal_conf_conf_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Server); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_internal_conf_conf_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Data); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_internal_conf_conf_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Queue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_internal_conf_conf_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Server_HTTP); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_internal_conf_conf_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Server_GRPC); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_internal_conf_conf_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Server_ETCD); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_internal_conf_conf_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Data_Database); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_internal_conf_conf_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Data_Redis); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_internal_conf_conf_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Queue_Kafka); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_internal_conf_conf_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Queue_Asynq); 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_internal_conf_conf_proto_rawDesc, + NumEnums: 0, + NumMessages: 11, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_internal_conf_conf_proto_goTypes, + DependencyIndexes: file_internal_conf_conf_proto_depIdxs, + MessageInfos: file_internal_conf_conf_proto_msgTypes, + }.Build() + File_internal_conf_conf_proto = out.File + file_internal_conf_conf_proto_rawDesc = nil + file_internal_conf_conf_proto_goTypes = nil + file_internal_conf_conf_proto_depIdxs = nil +} diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/conf/conf.proto b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/conf/conf.proto new file mode 100644 index 0000000..f3927ef --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/conf/conf.proto @@ -0,0 +1,76 @@ +syntax = "proto3"; +package kratos.api; + +option go_package = "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name|to_lower_camel}}/internal/conf;conf"; + +import "google/protobuf/duration.proto"; + +message Bootstrap { + Server server = 1; + Data data = 2; + Queue queue = 3; +} + +message Server { + message HTTP { + string network = 1; + string addr = 2; + google.protobuf.Duration timeout = 3; + } + message GRPC { + string network = 1; + string addr = 2; + google.protobuf.Duration timeout = 3; + } + message ETCD { + repeated string addr = 1; + string username = 2; + string password = 3; + } + HTTP http = 1; + GRPC grpc = 2; + ETCD etcd = 3; + string trace_endpoint = 4; + string jwt_token = 5; + string env = 6; +} + +message Data { + message Database { + string driver = 1; + string source = 2; + } + message Redis { + string network = 1; + string addr = 2; + int32 db = 3; + string password = 4; + int32 pool = 5; + google.protobuf.Duration read_timeout = 6; + google.protobuf.Duration write_timeout = 7; + } + Database database = 1; + Redis redis = 2; +} + +message Queue { + message Kafka { + repeated string addrs = 1; + string topic = 2; + string group = 3; + string username = 4; + string password = 5; + } + message Asynq { + string network = 1; + string addr = 2; + int32 db = 3; + string password = 4; + int32 pool = 5; + google.protobuf.Duration read_timeout = 6; + google.protobuf.Duration write_timeout = 7; + int32 concurrency = 8; + } + Kafka kafka = 1; + Asynq asynq = 2; +} diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/data/README.md b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/data/README.md new file mode 100644 index 0000000..599c41b --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/data/README.md @@ -0,0 +1 @@ +# Data diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/data/data.go b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/data/data.go new file mode 100644 index 0000000..cd85f49 --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/data/data.go @@ -0,0 +1,250 @@ +package data + +import ( + "context" + "github.com/go-kratos/kratos/v2/log" + "encoding/json" + "fmt" + "github.com/go-kratos/kratos/v2/middleware/recovery" + "github.com/go-kratos/kratos/v2/middleware/tracing" + "github.com/go-kratos/kratos/v2/registry" + "github.com/go-kratos/kratos/v2/transport/grpc" + "github.com/go-redis/redis/extra/redisotel/v8" + "github.com/go-redis/redis/v8" + "github.com/google/wire" + "github.com/hibiken/asynq" + "github.com/tx7do/kratos-transport/broker" + "github.com/tx7do/kratos-transport/broker/kafka" + "github.com/uptrace/opentelemetry-go-extra/otelgorm" + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + ggrpc "google.golang.org/grpc" + {% if cookiecutter.db_driver == 'mysql' -%} + driver "gorm.io/driver/mysql" + {%- elif cookiecutter.db_driver == 'postgres' -%} + driver "gorm.io/driver/postgres" + {%- endif %} + "gorm.io/gorm" + v1 "{{cookiecutter.go_module}}/api/{{cookiecutter.app_name}}/v1" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/biz" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/conf" + "{{cookiecutter.go_module}}/pkg/utils/snowflake" +) + +// ProviderSet is data providers. +var ProviderSet = wire.NewSet( + NewData, + NewTransaction, + New{{cookiecutter.app_name|to_camel}}Repo, + NewCache, + NewKafkaBroker, + NewAsynqClient, + New{{cookiecutter.app_name|to_camel}}Client, +) + +// Data . +type Data struct { + log *log.Helper + redis *redis.Client + db *gorm.DB + snowflake *snowflake.Node + kbroker broker.Broker + asynqCli *asynq.Client + // TODO just for example, don't use dispatch self in production + ec v1.{{cookiecutter.app_name|to_camel}}Client +} + +type contextTxKey struct{} + +func (d *Data) InTx(ctx context.Context, fn func(ctx context.Context) error) error { + return d.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + ctx = context.WithValue(ctx, contextTxKey{}, tx) + return fn(ctx) + }) +} + +func (d *Data) DB(ctx context.Context) *gorm.DB { + tx, ok := ctx.Value(contextTxKey{}).(*gorm.DB) + if ok { + return tx + } + return d.db +} + +// NewAsynqTask new asynq task +func (d *Data) NewAsynqTask(taskName string, payload []byte) error { + _, err := d.asynqCli.Enqueue(asynq.NewTask(taskName, payload)) + return err +} + +// NewTransaction . +func NewTransaction(d *Data) biz.Transaction { + return d +} + +// NewData . +func NewData(c *conf.Data, kbroker broker.Broker, asynqCli *asynq.Client, ec v1.{{cookiecutter.app_name|to_camel}}Client, logger log.Logger) (*Data, func(), error) { + db := newDB(c.Database.Source) + rdb := newRedis(c.Redis) + cleanup := func() { + log.NewHelper(logger).Info("closing the data resources") + _ = rdb.Close() + sqlDB, _ := db.DB() + _ = sqlDB.Close() + } + data := &Data{ + log: log.NewHelper(logger), + redis: rdb, + db: db, + snowflake: snowflake.GetSnowflakeNode(rdb), + kbroker: kbroker, + asynqCli: asynqCli, + ec: ec, + } + return data, cleanup, nil +} + +func newRedis(c *conf.Data_Redis) *redis.Client { + rdb := redis.NewClient(&redis.Options{ + Network: c.Network, + Addr: c.Addr, + Password: c.Password, + DB: int(c.Db), + PoolSize: int(c.Pool), + }) + rdb.AddHook(redisotel.NewTracingHook(redisotel.WithAttributes(semconv.NetPeerNameKey.String(c.Addr)))) + return rdb +} + +func newDB(dsn string) *gorm.DB { + if dsn == "" { + {% if cookiecutter.db_driver == 'mysql' -%} + dsn = "root:@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=UTC" + {%- elif cookiecutter.db_driver == 'postgres' -%} + dsn = "host=localhost port=5432 user=postgres dbname=example sslmode=disable" + {%- endif %} + } + + db, err := gorm.Open(driver.Open(dsn), &gorm.Config{}) + if err != nil { + log.Fatal("connection database failed: ", err) + } + _ = db.Use(otelgorm.NewPlugin(otelgorm.WithoutQueryVariables())) + return db +} + +// NewCache . +func NewCache(d *Data) biz.Cache { + return d +} + +func (r *Data) Remember(ctx context.Context, key string, second int32, fn func(ctx context.Context) (interface{}, error)) ([]byte, error) { + str, err := r.GetValue(ctx, key) + if err != nil { + value, err := fn(ctx) + if err != nil { + return nil, err + } + + mByte, err := json.Marshal(value) + if err != nil { + return nil, fmt.Errorf("json Marshal cache failed, err: %w ", err) + } + + return mByte, nil + } + + if str == "" { + value, err := fn(ctx) + if err != nil { + return nil, err + } + + var mByte []byte + if str, ok := value.(string); ok { + mByte = []byte(str) + } else { + mByte, err = json.Marshal(value) + } + + if err != nil { + return nil, fmt.Errorf("json Marshal cache null, err: %w ", err) + } + r.WriteValue(ctx, key, mByte, second) + + return mByte, nil + } + + return []byte(str), nil +} + +func (r *Data) GetValue(ctx context.Context, key string) (string, error) { + v, err := r.redis.Get(ctx, key).Result() + if err == redis.Nil { + return v, nil + } else if err != nil { + return v, err + } else { + return v, nil + } +} + +func (r *Data) WriteValue(ctx context.Context, key string, value interface{}, timeout int32) error { + return r.redis.Set(ctx, key, value, time.Duration(timeout)*time.Second).Err() +} + +func (r *Data) DelValue(ctx context.Context, keys ...string) error { + return r.redis.Del(ctx, keys...).Err() +} + + + +// New{{cookiecutter.app_name|to_camel}}Client new {{cookiecutter.app_name}} rpc client +func New{{cookiecutter.app_name|to_camel}}Client(r registry.Discovery) v1.{{cookiecutter.app_name|to_camel}}Client { + return NewRPCClient[v1.{{cookiecutter.app_name|to_camel}}Client](r, "{{cookiecutter.app_name}}.rpc", v1.New{{cookiecutter.app_name|to_camel}}Client) +} + +// NewRPCClient new one rpc client by discovery. +func NewRPCClient[T any](r registry.Discovery, rpcName string, fn func(cc ggrpc.ClientConnInterface) T) T { + conn, err := grpc.DialInsecure( + context.Background(), + grpc.WithEndpoint("discovery:///"+rpcName), + grpc.WithDiscovery(r), + grpc.WithMiddleware( + tracing.Client(), + recovery.Recovery(), + ), + ) + if err != nil { + log.Fatal(err) + } + return fn(conn) +} + + +// NewKafkaBroker new kafka broker. +func NewKafkaBroker(confQueue *conf.Queue) broker.Broker { + opts := []broker.Option{ + broker.WithAddress(confQueue.Kafka.Addrs...), + broker.WithCodec("json"), + broker.WithGlobalTracerProvider(), + } + if confQueue.Kafka.Username != "" && confQueue.Kafka.Password != "" { + opts = append(opts, kafka.WithPlainMechanism(confQueue.Kafka.Username, confQueue.Kafka.Password)) + } + kafkaBroker := kafka.NewBroker(opts...) + _ = kafkaBroker.Init() + return kafkaBroker +} + +// NewAsynqClient new asynq client. +func NewAsynqClient(confQueue *conf.Queue) *asynq.Client { + return asynq.NewClient(asynq.RedisClientOpt{ + Addr: confQueue.Asynq.Addr, + DB: int(confQueue.Asynq.Db), + Password: confQueue.Asynq.Password, + ReadTimeout: confQueue.Asynq.ReadTimeout.AsDuration(), + WriteTimeout: confQueue.Asynq.WriteTimeout.AsDuration(), + PoolSize: int(confQueue.Asynq.Pool), + }) +} + diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/data/{{cookiecutter.app_name}}.go b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/data/{{cookiecutter.app_name}}.go new file mode 100644 index 0000000..7f94252 --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/data/{{cookiecutter.app_name}}.go @@ -0,0 +1,28 @@ +package data + +import ( + "context" + "github.com/go-kratos/kratos/v2/log" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/biz" +) + +type {{cookiecutter.app_name|to_lower_camel}}Repo struct { + data *Data + log *log.Helper +} + +// New{{cookiecutter.app_name|to_camel}}Repo . +func New{{cookiecutter.app_name|to_camel}}Repo(data *Data, logger log.Logger) biz.{{cookiecutter.app_name|to_camel}}Repo { + return &{{cookiecutter.app_name|to_lower_camel}}Repo{ + data: data, + log: log.NewHelper(logger), + } +} + +func (r *{{cookiecutter.app_name|to_lower_camel}}Repo) Create{{cookiecutter.app_name|to_camel}}(ctx context.Context, g *biz.{{cookiecutter.app_name|to_camel}}) error { + return nil +} + +func (r *{{cookiecutter.app_name|to_lower_camel}}Repo) Update{{cookiecutter.app_name|to_camel}}(ctx context.Context, g *biz.{{cookiecutter.app_name|to_camel}}) error { + return nil +} diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/server/asynq.go b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/server/asynq.go new file mode 100644 index 0000000..39ada83 --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/server/asynq.go @@ -0,0 +1,66 @@ +package server + +import ( + "fmt" + "github.com/hibiken/asynq" + "golang.org/x/sync/errgroup" + "log" + "os" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/biz" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/conf" +) + +type AsynqServer struct { + srv *asynq.Server + mux *asynq.ServeMux + scheduler *asynq.Scheduler + entryID string +} + +func (j *AsynqServer) Run() error { + eg := errgroup.Group{} + eg.Go(func() error { + log.Println("schedule start", j.entryID) + return j.scheduler.Run() + }) + eg.Go(func() error { + return j.srv.Run(j.mux) + }) + return eg.Wait() +} + +func NewAsynqServer(conf *conf.Queue, uc *biz.{{cookiecutter.app_name|to_camel}}Usecase) *AsynqServer { + asynqConf := conf.Asynq + redisOpt := asynq.RedisClientOpt{ + Addr: asynqConf.Addr, + Password: asynqConf.Password, + DB: int(asynqConf.Db), + PoolSize: int(asynqConf.Pool), + ReadTimeout: asynqConf.ReadTimeout.AsDuration(), + WriteTimeout: asynqConf.WriteTimeout.AsDuration(), + } + srv := asynq.NewServer( + redisOpt, + asynq.Config{ + Concurrency: int(asynqConf.Concurrency), + Queues: map[string]int{ + "critical": 6, + "default": 3, + "low": 1, + }, + }) + mux := asynq.NewServeMux() + mux.HandleFunc(biz.{{cookiecutter.app_name|to_camel}}TaskName, uc.Asynq{{cookiecutter.app_name|to_camel}}TaskHandler) + scheduler := asynq.NewScheduler(redisOpt, &asynq.SchedulerOpts{}) + task := asynq.NewTask(biz.{{cookiecutter.app_name|to_camel}}TaskName, nil) + entryID, err := scheduler.Register("*/15 * * * *", task, asynq.TaskID(biz.{{cookiecutter.app_name|to_camel}}TaskName)) + checkError(err) + return &AsynqServer{srv: srv, mux: mux, scheduler: scheduler, entryID: entryID} +} + +func checkError(err error) { + if err != nil { + fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error()) + os.Exit(1) + } +} diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/server/grpc.go b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/server/grpc.go new file mode 100644 index 0000000..979affe --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/server/grpc.go @@ -0,0 +1,41 @@ +package server + +import ( + "github.com/go-kratos/kratos/v2/log" + "github.com/go-kratos/kratos/v2/middleware/logging" + "github.com/go-kratos/kratos/v2/middleware/metadata" + "github.com/go-kratos/kratos/v2/middleware/metrics" + "github.com/go-kratos/kratos/v2/middleware/recovery" + "github.com/go-kratos/kratos/v2/middleware/tracing" + "github.com/go-kratos/kratos/v2/middleware/validate" + "github.com/go-kratos/kratos/v2/transport/grpc" + v1 "{{cookiecutter.go_module}}/api/{{cookiecutter.app_name}}/v1" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/conf" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/service" +) + +// NewGRPCServer new a gRPC server. +func NewGRPCServer(c *conf.Server, {{cookiecutter.app_name|to_lower_camel}} *service.{{cookiecutter.app_name|to_camel}}Service, logger log.Logger) *grpc.Server { + var opts = []grpc.ServerOption{ + grpc.Middleware( + recovery.Recovery(), + tracing.Server(), + logging.Server(logger), + metrics.Server(), + validate.Validator(), + metadata.Server(metadata.WithPropagatedPrefix("")), + ), + } + if c.Grpc.Network != "" { + opts = append(opts, grpc.Network(c.Grpc.Network)) + } + if c.Grpc.Addr != "" { + opts = append(opts, grpc.Address(c.Grpc.Addr)) + } + if c.Grpc.Timeout != nil { + opts = append(opts, grpc.Timeout(c.Grpc.Timeout.AsDuration())) + } + srv := grpc.NewServer(opts...) + v1.Register{{cookiecutter.app_name|to_camel}}Server(srv, {{cookiecutter.app_name|to_lower_camel}}) + return srv +} diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/server/http.go b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/server/http.go new file mode 100644 index 0000000..89f4ac8 --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/server/http.go @@ -0,0 +1,44 @@ +package server + +import ( + "github.com/go-kratos/kratos/v2/log" + "github.com/go-kratos/kratos/v2/middleware/logging" + "github.com/go-kratos/kratos/v2/middleware/metadata" + "github.com/go-kratos/kratos/v2/middleware/metrics" + "github.com/go-kratos/kratos/v2/middleware/recovery" + "github.com/go-kratos/kratos/v2/middleware/tracing" + "github.com/go-kratos/kratos/v2/middleware/validate" + "github.com/go-kratos/kratos/v2/transport/http" + "github.com/go-kratos/swagger-api/openapiv2" + v1 "{{cookiecutter.go_module}}/api/{{cookiecutter.app_name}}/v1" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/conf" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/service" +) + +// NewHTTPServer new a HTTP server. +func NewHTTPServer(c *conf.Server, {{cookiecutter.app_name|to_lower_camel}} *service.{{cookiecutter.app_name|to_camel}}Service, logger log.Logger) *http.Server { + var opts = []http.ServerOption{ + http.Middleware( + recovery.Recovery(), + tracing.Server(), + logging.Server(logger), + metrics.Server(), + validate.Validator(), + metadata.Server(metadata.WithPropagatedPrefix("")), + ), + } + if c.Http.Network != "" { + opts = append(opts, http.Network(c.Http.Network)) + } + if c.Http.Addr != "" { + opts = append(opts, http.Address(c.Http.Addr)) + } + if c.Http.Timeout != nil { + opts = append(opts, http.Timeout(c.Http.Timeout.AsDuration())) + } + srv := http.NewServer(opts...) + openAPIhandler := openapiv2.NewHandler() + srv.HandlePrefix("/q/", openAPIhandler) + v1.Register{{cookiecutter.app_name|to_camel}}HTTPServer(srv, {{cookiecutter.app_name|to_lower_camel}}) + return srv +} diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/server/kafka.go b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/server/kafka.go new file mode 100644 index 0000000..e2d4943 --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/server/kafka.go @@ -0,0 +1,31 @@ +package server + +import ( + "context" + "github.com/go-kratos/kratos/v2/log" + "github.com/tx7do/kratos-transport/transport/kafka" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/biz" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/conf" +) + +// NewKafkaServer new a kafka server. +func NewKafkaServer(c *conf.Queue, uc *biz.{{cookiecutter.app_name|to_camel}}Usecase, logger log.Logger) *kafka.Server { + opts := []kafka.ServerOption{ + kafka.WithAddress(c.Kafka.Addrs), + kafka.WithCodec("json"), + kafka.WithGlobalTracerProvider(), + } + if c.Kafka.Username != "" && c.Kafka.Password != "" { + opts = append(opts, kafka.WithPlainMechanism(c.Kafka.Username, c.Kafka.Password)) + } + kafkaSrv := kafka.NewServer(opts...) + err := kafkaSrv.RegisterSubscriber( + context.Background(), c.Kafka.Topic, c.Kafka.Group, false, + biz.RegisterExampleHandler(uc.KakfaExampleConsumer), + biz.ExampleCreator, + ) + if err != nil { + log.Fatal(err) + } + return kafkaSrv +} diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/server/registrar.go b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/server/registrar.go new file mode 100644 index 0000000..c2502ad --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/server/registrar.go @@ -0,0 +1,30 @@ +package server + +import ( + "github.com/go-kratos/kratos/contrib/registry/etcd/v2" + "github.com/go-kratos/kratos/v2/log" + "github.com/go-kratos/kratos/v2/registry" + etcdclient "go.etcd.io/etcd/client/v3" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/conf" +) + +func newEtcd(conf *conf.Server) *etcd.Registry { + client, err := etcdclient.New(etcdclient.Config{ + Endpoints: conf.Etcd.Addr, + Username: conf.Etcd.Username, + Password: conf.Etcd.Password, + }) + if err != nil { + log.Fatal(err) + } + r := etcd.New(client) + return r +} + +func NewDiscovery(conf *conf.Server) registry.Discovery { + return newEtcd(conf) +} + +func NewRegistrar(conf *conf.Server) registry.Registrar { + return newEtcd(conf) +} diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/server/server.go b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/server/server.go new file mode 100644 index 0000000..562ed25 --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/server/server.go @@ -0,0 +1,6 @@ +package server + +import "github.com/google/wire" + +// ProviderSet is server providers. +var ProviderSet = wire.NewSet(NewHTTPServer, NewGRPCServer, NewRegistrar, NewDiscovery, NewAsynqServer, NewKafkaServer) diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/service/README.md b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/service/README.md new file mode 100644 index 0000000..42321b7 --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/service/README.md @@ -0,0 +1 @@ +# Service diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/service/service.go b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/service/service.go new file mode 100644 index 0000000..09e03e6 --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/service/service.go @@ -0,0 +1,6 @@ +package service + +import "github.com/google/wire" + +// ProviderSet is service providers. +var ProviderSet = wire.NewSet(New{{cookiecutter.app_name|to_camel}}Service) diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/service/{{cookiecutter.app_name}}.go b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/service/{{cookiecutter.app_name}}.go new file mode 100644 index 0000000..871121b --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/internal/service/{{cookiecutter.app_name}}.go @@ -0,0 +1,31 @@ +package service + +import ( + "context" + "github.com/go-kratos/kratos/v2/log" + v1 "{{cookiecutter.go_module}}/api/{{cookiecutter.app_name}}/v1" + "{{cookiecutter.go_module}}/app/{{cookiecutter.app_name}}/internal/biz" +) + +// {{cookiecutter.app_name|to_camel}}Service is a {{cookiecutter.app_name}} service. +type {{cookiecutter.app_name|to_camel}}Service struct { + v1.Unimplemented{{cookiecutter.app_name|to_camel}}Server + + uc *biz.{{cookiecutter.app_name|to_camel}}Usecase + log *log.Helper +} + +// New{{cookiecutter.app_name|to_camel}}Service new a greeter service. +func New{{cookiecutter.app_name|to_camel}}Service(uc *biz.{{cookiecutter.app_name|to_camel}}Usecase, logger log.Logger) *{{cookiecutter.app_name|to_camel}}Service { + return &{{cookiecutter.app_name|to_camel}}Service{uc: uc, log: log.NewHelper(logger)} +} + +// SayHello implements helloworld.{{cookiecutter.app_name|to_camel}}Server +func (s *{{cookiecutter.app_name|to_camel}}Service) SayHello(ctx context.Context, in *v1.HelloRequest) (*v1.HelloReply, error) { + s.log.WithContext(ctx).Infof("SayHello Received: %v", in.GetName()) + + if in.GetName() == "error" { + return nil, v1.ErrorUserNotFound("user not found: %s", in.GetName()) + } + return &v1.HelloReply{Message: "Hello " + in.GetName()}, nil +} diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/manifests/Dockerfile b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/manifests/Dockerfile new file mode 100644 index 0000000..690a18f --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/manifests/Dockerfile @@ -0,0 +1,23 @@ +FROM golang:1.17 AS builder + +COPY . /src +WORKDIR /src/app/{{cookiecutter.app_name}} +RUN GOPROXY=https://goproxy.cn,direct make build + +FROM debian:stable-slim + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + netbase \ + && rm -rf /var/lib/apt/lists/ \ + && apt-get autoremove -y && apt-get autoclean -y + +COPY --from=builder /src/app/{{cookiecutter.app_name}}/bin /app + +WORKDIR /app + +EXPOSE 8001 +EXPOSE 9001 +VOLUME /data/conf + +CMD ["./server", "-conf", "/data/conf"] \ No newline at end of file diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/manifests/configmap.yaml b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/manifests/configmap.yaml new file mode 100644 index 0000000..9dbfce1 --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/manifests/configmap.yaml @@ -0,0 +1,31 @@ +apiVersion: v1 +data: + conf.yaml: | + server: + http: + addr: 0.0.0.0:8001 + timeout: 1s + grpc: + addr: 0.0.0.0:9001 + timeout: 1s + etcd: + addr: + - 127.0.0.1:2379 + trace_endpoint: http://127.0.0.1:14268/api/traces + data: + database: + {% if cookiecutter.db_driver == 'mysql' -%} + driver: mysql + source: root:@tcp(127.0.0.1:3306)/{{cookiecutter.app_name}}?charset=utf8mb4&parseTime=True&loc=UTC + {%- elif cookiecutter.db_driver == 'postgres' -%} + driver: postgres + source: host=localhost port=5432 user=postgres dbname=example sslmode=disable + {%- endif %} + redis: + addr: 127.0.0.1:6379 + read_timeout: 0.2s + write_timeout: 0.2s +kind: ConfigMap +metadata: + name: {{cookiecutter.app_name|to_lower_camel}}-conf + namespace: default diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/manifests/deployment.yaml b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/manifests/deployment.yaml new file mode 100644 index 0000000..aaf3f57 --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/manifests/deployment.yaml @@ -0,0 +1,75 @@ +apiVersion: apps/v1 # for versions before 1.8.0 use apps/v1beta1 +kind: Deployment +metadata: + name: {{cookiecutter.app_name|to_lower_camel}}-deployment + labels: + app: {{cookiecutter.app_name|to_lower_camel}} +spec: + replicas: 2 + selector: + matchLabels: + app: {{cookiecutter.app_name|to_lower_camel}} + template: + metadata: + labels: + app: {{cookiecutter.app_name|to_lower_camel}} + spec: + containers: + - name: {{cookiecutter.app_name|to_lower_camel}} + image: nginx + imagePullPolicy: IfNotPresent + ports: + - containerPort: 80 + resources: + requests: + memory: "128Mi" + cpu: "128m" + limits: + memory: "256Mi" + cpu: "256m" + volumeMounts: + - mountPath: /data/conf + name: {{cookiecutter.app_name|to_lower_camel}}-conf + volumes: + - configMap: + defaultMode: 420 + name: {{cookiecutter.app_name|to_lower_camel}}-conf + name: {{cookiecutter.app_name|to_lower_camel}}-conf +--- +apiVersion: apps/v1 # for versions before 1.8.0 use apps/v1beta1 +kind: Deployment +metadata: + name: {{cookiecutter.app_name|to_lower_camel}}-worker-deployment + labels: + app: {{cookiecutter.app_name|to_lower_camel}}-worker +spec: + replicas: 2 + selector: + matchLabels: + app: {{cookiecutter.app_name|to_lower_camel}}-worker + template: + metadata: + labels: + app: {{cookiecutter.app_name|to_lower_camel}}-worker + spec: + containers: + - name: {{cookiecutter.app_name|to_lower_camel}}-worker + image: nginx + imagePullPolicy: IfNotPresent + ports: + - containerPort: 80 + resources: + requests: + memory: "128Mi" + cpu: "128m" + limits: + memory: "256Mi" + cpu: "256m" + volumeMounts: + - mountPath: /data/conf + name: {{cookiecutter.app_name|to_lower_camel}}-conf + volumes: + - configMap: + defaultMode: 420 + name: {{cookiecutter.app_name|to_lower_camel}}-conf + name: {{cookiecutter.app_name|to_lower_camel}}-conf \ No newline at end of file diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/manifests/ingress-grpc.yaml b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/manifests/ingress-grpc.yaml new file mode 100644 index 0000000..7123322 --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/manifests/ingress-grpc.yaml @@ -0,0 +1,26 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + annotations: + nginx.ingress.kubernetes.io/ssl-redirect: "true" + nginx.ingress.kubernetes.io/backend-protocol: "GRPC" + cert-manager.io/cluster-issuer: "letsencrypt-prod" + name: {{cookiecutter.app_name|to_lower_camel}}-grpc-ingress + namespace: default +spec: + ingressClassName: nginx + tls: + - hosts: + - {{cookiecutter.app_name|to_lower_camel}}.example.com + secretName: {{cookiecutter.app_name|to_lower_camel}}-tls + rules: + - host: {{cookiecutter.app_name|to_lower_camel}}.example.com + http: + paths: + - path: /{{cookiecutter.app_name|to_lower_camel}}. + pathType: Prefix + backend: + service: + name: {{cookiecutter.app_name|to_lower_camel}}-svc + port: + number: 9001 diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/manifests/ingress.yaml b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/manifests/ingress.yaml new file mode 100644 index 0000000..0d83b27 --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/manifests/ingress.yaml @@ -0,0 +1,25 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + nginx.ingress.kubernetes.io/ssl-redirect: "true" + name: {{cookiecutter.app_name|to_lower_camel}}-ingress + namespace: default +spec: + ingressClassName: nginx + tls: + - hosts: + - {{cookiecutter.app_name|to_lower_camel}}.example.com + secretName: {{cookiecutter.app_name|to_lower_camel}}-tls + rules: + - host: {{cookiecutter.app_name|to_lower_camel}}.example.com + http: + paths: + - path: /{{cookiecutter.app_name|to_lower_camel}} + pathType: Prefix + backend: + service: + name: {{cookiecutter.app_name|to_lower_camel}}-svc + port: + number: 8001 \ No newline at end of file diff --git a/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/manifests/service.yaml b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/manifests/service.yaml new file mode 100644 index 0000000..70e22f6 --- /dev/null +++ b/.tmpl/{{cookiecutter.app_name}}/app/{{cookiecutter.app_name}}/manifests/service.yaml @@ -0,0 +1,25 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{cookiecutter.app_name|to_lower_camel}}-svc + namespace: default +spec: + clusterIP: None + clusterIPs: + - None + internalTrafficPolicy: Cluster + ipFamilies: + - IPv4 + ports: + - name: http8001 + port: 8001 + protocol: TCP + targetPort: 8001 + - name: grpc9001 + port: 9001 + protocol: TCP + targetPort: 9001 + selector: + app: {{cookiecutter.app_name|to_lower_camel}} + sessionAffinity: None + type: ClusterIP diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e44e6bd --- /dev/null +++ b/Dockerfile @@ -0,0 +1,28 @@ +FROM golang:1.15 AS builder + +ARG APP_RELATIVE_PATH + +COPY . /src +WORKDIR /src/app/${APP_RELATIVE_PATH} + +RUN GOPROXY=https://goproxy.cn make build + +FROM debian:stable-slim + +ARG APP_RELATIVE_PATH + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + netbase \ + && rm -rf /var/lib/apt/lists/ \ + && apt-get autoremove -y && apt-get autoclean -y + +COPY --from=builder /src/app/${APP_RELATIVE_PATH}/bin /app + +WORKDIR /app + +EXPOSE 8000 +EXPOSE 9000 +VOLUME /data/conf + +CMD ["./server", "-conf", "/data/conf"] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..684318c --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 go-kratos + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1db0b57 --- /dev/null +++ b/Makefile @@ -0,0 +1,22 @@ +.PHONY: api +# generate api +api: + find app -type d -depth 1 -print | xargs -L 1 bash -c 'cd "$$0" && pwd && $(MAKE) api' + +.PHONY: wire +# generate wire +wire: + find app -type d -depth 1 -print | xargs -L 1 bash -c 'cd "$$0" && pwd && $(MAKE) wire' + +# generate wire +build: + find app -type d -depth 1 -print | xargs -L 1 bash -c 'cd "$$0" && pwd && $(MAKE) build' + +# build qa for linux +build-linux: + find app -type d -depth 1 -print | xargs -L 1 bash -c 'cd "$$0" && pwd && $(MAKE) build-linux' + +.PHONY: proto +# generate proto +proto: + find app -type d -depth 1 -print | xargs -L 1 bash -c 'cd "$$0" && pwd && $(MAKE) proto' \ No newline at end of file diff --git a/README.md b/README.md index 0405017..236c0e1 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,61 @@ -# tkcashgame_v4 +# Kratos Project Template + +## Install Kratos +``` +go get -u github.com/go-kratos/kratos/cmd/kratos/v2@latest +``` +## Install deps +``` +make init +``` +## Create app +``` +./create_app.sh +``` +## Create a service +``` +# Create a template project +kratos new server + +cd server +# Add a proto template +kratos proto add api/server/server.proto +# Generate the proto code +kratos proto client api/server/server.proto +# Generate the source code of service by proto file +kratos proto server api/server/server.proto -t internal/service + +go generate ./... +go build -o ./bin/ ./... +./bin/server -conf ./configs +``` +## Generate other auxiliary files by Makefile +``` +# Download and update dependencies +make init +# Generate API swagger json files by proto file +make swagger +# Generate API files (include: pb.go, http, grpc, validate, swagger) by proto file +make api +# Generate all files +make all +``` +## Automated Initialization (wire) +``` +# install wire +go get github.com/google/wire/cmd/wire + +# generate wire +cd cmd/server +wire +``` + +## Docker +```bash +# build +docker build -t . + +# run +docker run --rm -p 8000:8000 -p 9000:9000 -v :/data/conf +``` -tk cash game v4 \ No newline at end of file diff --git a/Supfile b/Supfile new file mode 100644 index 0000000..f11e089 --- /dev/null +++ b/Supfile @@ -0,0 +1,67 @@ +# Supfile for engine service + +--- +version: 0.4 + +# Global environment variables +env: + DEPLOY_TO: /data/sandc + +networks: + staging: + hosts: + - www@127.0.0.1:22 + + prod: + hosts: + - www@127.0.0.1:22 + +commands: + ping: + desc: Print OS name and current date/time + run: echo pong `hostname` deploy to ${DEPLOY_TO}/app/${NAME} + + compile: + desc: compile service file + local: | + cd app/${NAME} + make linux_build + + upload: + desc: Upload service file to server + upload: + - src: ./app/${NAME}/bin/ + dst: /tmp/ + script: ./deploy_link.sh + + restart: + desc: Restart engine service + run: | + sudo supervisorctl restart ${NAME}_server + sudo supervisorctl restart ${NAME}_worker + + start: + desc: Start engine service + run: | + sudo supervisorctl start ${NAME}_server + sudo supervisorctl start ${NAME}_worker + + stop: + desc: Stop engine service + run: | + sudo supervisorctl stop ${NAME}_server + sudo supervisorctl stop ${NAME}_worker + + status: + desc: Return engine current status + run: | + sudo supervisorctl status ${NAME}_server + sudo supervisorctl status ${NAME}_worker + + +targets: + deploy: + - ping + - compile + - upload + - restart \ No newline at end of file diff --git a/api/eonline/openapi.yaml b/api/eonline/openapi.yaml new file mode 100644 index 0000000..28a2684 --- /dev/null +++ b/api/eonline/openapi.yaml @@ -0,0 +1,288 @@ +# Generated with protoc-gen-openapi +# https://github.com/google/gnostic/tree/master/cmd/protoc-gen-openapi + +openapi: 3.0.3 +info: + title: Eonline API + description: The greeting service definition. + version: 0.0.1 +paths: + /eonline/pay/init: + post: + tags: + - Eonline + description: PayInit + operationId: Eonline_PayInit + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PayInitReq' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PayInitReply' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /eonline/payout: + post: + tags: + - Eonline + description: Payout + operationId: Eonline_Payout + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PayoutReq' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PayoutReply' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /eonline/payout/callback: + post: + tags: + - Eonline + description: PayoutCallback + operationId: Eonline_PayoutCallback + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PayoutCallbackReq' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PayoutCallbackReply' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /eonline/payout/check: + post: + tags: + - Eonline + description: PayoutCheck + operationId: Eonline_PayoutCheck + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PayoutCheckReq' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PayoutCheckReply' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /eonline/{name}: + get: + tags: + - Eonline + description: Sends a greeting + operationId: Eonline_SayHello + parameters: + - name: name + in: path + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/HelloReply' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' +components: + schemas: + GoogleProtobufAny: + type: object + properties: + '@type': + type: string + description: The type of the serialized message. + additionalProperties: true + description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. + HelloReply: + type: object + properties: + message: + type: string + description: The response message containing the greetings + PayInitReply: + type: object + properties: + uuid: + type: string + items: + type: array + items: + $ref: '#/components/schemas/PayInitReply_Item' + description: PayInitReply init reply + PayInitReply_Item: + type: object + properties: + id: + type: integer + format: int32 + amount: + type: number + format: double + status: + type: integer + format: int32 + PayInitReq: + type: object + properties: + platform: + type: string + deviceid: + type: string + version: + type: string + ts: + type: string + sign: + type: string + ip: + type: string + description: PayInitReq init request + PayoutCallbackReply: + type: object + properties: + message: + type: string + description: PayoutCallbackReply 赔付回调响应 + PayoutCallbackReq: + type: object + properties: + payoutId: + type: string + customCode: + type: string + status: + type: string + msg: + type: string + timestamp: + type: integer + format: int64 + description: PayoutCallbackReq 赔付回调请求 + PayoutCheckReply: + type: object + properties: + status: + type: integer + format: int32 + description: PayoutCheckReply 赔付查询响应 + PayoutCheckReq: + type: object + properties: + platform: + type: string + deviceid: + type: string + version: + type: string + ts: + type: string + sign: + type: string + ip: + type: string + recordNo: + type: string + description: PayoutCheckReq 赔付查询请求 + PayoutReply: + type: object + properties: + id: + type: string + recordNo: + type: string + description: PayoutReply 赔付响应 + PayoutReq: + type: object + properties: + platform: + type: string + deviceid: + type: string + version: + type: string + ts: + type: string + sign: + type: string + account: + type: string + itemId: + type: integer + format: int32 + amount: + type: number + format: double + additionalRemark: + type: string + uuid: + type: string + ip: + type: string + description: PayoutReq 赔付请求 + Status: + type: object + properties: + code: + type: integer + description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + format: int32 + message: + type: string + description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + details: + type: array + items: + $ref: '#/components/schemas/GoogleProtobufAny' + description: A list of messages that carry the error details. There is a common set of message types for APIs to use. + description: 'The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).' +tags: + - name: Eonline diff --git a/api/eonline/v1/eonline.pb.go b/api/eonline/v1/eonline.pb.go new file mode 100644 index 0000000..8067030 --- /dev/null +++ b/api/eonline/v1/eonline.pb.go @@ -0,0 +1,344 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v3.20.3 +// source: api/eonline/v1/eonline.proto + +package v1 + +import ( + _ "google.golang.org/genproto/googleapis/api/annotations" + 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) +) + +// The request message containing the user's name. +type HelloRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *HelloRequest) Reset() { + *x = HelloRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_eonline_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HelloRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HelloRequest) ProtoMessage() {} + +func (x *HelloRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_eonline_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 HelloRequest.ProtoReflect.Descriptor instead. +func (*HelloRequest) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_eonline_proto_rawDescGZIP(), []int{0} +} + +func (x *HelloRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +// The response message containing the greetings +type HelloReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *HelloReply) Reset() { + *x = HelloReply{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_eonline_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HelloReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HelloReply) ProtoMessage() {} + +func (x *HelloReply) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_eonline_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 HelloReply.ProtoReflect.Descriptor instead. +func (*HelloReply) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_eonline_proto_rawDescGZIP(), []int{1} +} + +func (x *HelloReply) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +var File_api_eonline_v1_eonline_proto protoreflect.FileDescriptor + +var file_api_eonline_v1_eonline_proto_rawDesc = []byte{ + 0x0a, 0x1c, 0x61, 0x70, 0x69, 0x2f, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31, + 0x2f, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0e, + 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x1c, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1d, 0x61, 0x70, + 0x69, 0x2f, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x67, + 0x73, 0x6d, 0x69, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x22, 0x0a, 0x0c, 0x48, + 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, + 0x26, 0x0a, 0x0a, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, + 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0xad, 0x0a, 0x0a, 0x07, 0x45, 0x6f, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x12, 0x5e, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, + 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, + 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x12, 0x12, 0x10, 0x2f, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x34, 0x2f, 0x7b, 0x6e, 0x61, + 0x6d, 0x65, 0x7d, 0x12, 0x62, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x1a, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x50, 0x61, 0x79, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x79, 0x49, + 0x6e, 0x69, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, + 0x3a, 0x01, 0x2a, 0x22, 0x12, 0x2f, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x34, 0x2f, 0x70, + 0x61, 0x79, 0x2f, 0x69, 0x6e, 0x69, 0x74, 0x12, 0x5d, 0x0a, 0x06, 0x50, 0x61, 0x79, 0x6f, 0x75, + 0x74, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x1b, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, + 0x79, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x15, 0x3a, 0x01, 0x2a, 0x22, 0x10, 0x2f, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x34, 0x2f, + 0x70, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x12, 0x69, 0x0a, 0x0c, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, + 0x42, 0x72, 0x61, 0x7a, 0x69, 0x6c, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x52, 0x65, + 0x71, 0x1a, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x21, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x3a, 0x01, 0x2a, 0x22, 0x16, 0x2f, 0x65, 0x6f, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x34, 0x2f, 0x70, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x42, 0x72, 0x61, 0x7a, 0x69, + 0x6c, 0x12, 0x7e, 0x0a, 0x0e, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x62, + 0x61, 0x63, 0x6b, 0x12, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x62, + 0x61, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x1a, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x43, 0x61, + 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x24, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x1e, 0x3a, 0x01, 0x2a, 0x22, 0x19, 0x2f, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x34, 0x2f, 0x70, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x2f, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, + 0x6b, 0x12, 0x72, 0x0a, 0x0b, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, + 0x12, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, + 0x1a, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x3a, 0x01, 0x2a, 0x22, 0x16, 0x2f, + 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x34, 0x2f, 0x70, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x2f, + 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x7f, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6f, + 0x75, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x73, 0x74, 0x12, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x79, 0x6f, 0x75, + 0x74, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x73, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x22, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x79, + 0x6f, 0x75, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, + 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x3a, 0x01, 0x2a, 0x22, 0x1a, 0x2f, 0x65, 0x6f, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x34, 0x2f, 0x67, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x55, + 0x73, 0x65, 0x72, 0x4c, 0x73, 0x74, 0x12, 0x7b, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x50, 0x61, 0x79, + 0x6f, 0x75, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x79, 0x6f, 0x75, + 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x21, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x79, 0x6f, + 0x75, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x24, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x3a, 0x01, 0x2a, 0x22, 0x19, 0x2f, 0x65, 0x6f, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x34, 0x2f, 0x73, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x71, 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x12, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, + 0x65, 0x71, 0x1a, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x3a, 0x01, 0x2a, 0x22, + 0x15, 0x2f, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x34, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, + 0x74, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x69, 0x0a, 0x09, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x49, + 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, + 0x71, 0x1a, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x3a, 0x01, 0x2a, 0x22, 0x13, 0x2f, 0x65, + 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x34, 0x2f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x69, 0x6e, 0x66, + 0x6f, 0x12, 0x61, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x43, 0x68, 0x61, 0x74, 0x12, 0x1a, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, + 0x64, 0x43, 0x68, 0x61, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, + 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x43, 0x68, 0x61, + 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x3a, 0x01, + 0x2a, 0x22, 0x11, 0x2f, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x34, 0x2f, 0x61, 0x64, 0x64, + 0x63, 0x68, 0x61, 0x74, 0x12, 0x61, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x74, 0x12, + 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x1c, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x43, 0x68, 0x61, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x16, 0x3a, 0x01, 0x2a, 0x22, 0x11, 0x2f, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x34, 0x2f, + 0x67, 0x65, 0x74, 0x63, 0x68, 0x61, 0x74, 0x42, 0x46, 0x0a, 0x19, 0x64, 0x65, 0x76, 0x2e, 0x6b, + 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x2e, 0x76, 0x31, 0x42, 0x0e, 0x45, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x56, 0x31, 0x50, 0x01, 0x5a, 0x17, 0x73, 0x61, 0x6e, 0x64, 0x63, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x76, 0x31, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_api_eonline_v1_eonline_proto_rawDescOnce sync.Once + file_api_eonline_v1_eonline_proto_rawDescData = file_api_eonline_v1_eonline_proto_rawDesc +) + +func file_api_eonline_v1_eonline_proto_rawDescGZIP() []byte { + file_api_eonline_v1_eonline_proto_rawDescOnce.Do(func() { + file_api_eonline_v1_eonline_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_eonline_v1_eonline_proto_rawDescData) + }) + return file_api_eonline_v1_eonline_proto_rawDescData +} + +var file_api_eonline_v1_eonline_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_api_eonline_v1_eonline_proto_goTypes = []interface{}{ + (*HelloRequest)(nil), // 0: api.eonline.v1.HelloRequest + (*HelloReply)(nil), // 1: api.eonline.v1.HelloReply + (*PayInitReq)(nil), // 2: api.eonline.v1.PayInitReq + (*PayoutReq)(nil), // 3: api.eonline.v1.PayoutReq + (*PayoutCallbackReq)(nil), // 4: api.eonline.v1.PayoutCallbackReq + (*PayoutCheckReq)(nil), // 5: api.eonline.v1.PayoutCheckReq + (*PayoutUserLstReq)(nil), // 6: api.eonline.v1.PayoutUserLstReq + (*PayoutStatusReq)(nil), // 7: api.eonline.v1.PayoutStatusReq + (*SubmitCheckReq)(nil), // 8: api.eonline.v1.SubmitCheckReq + (*CheckInfoReq)(nil), // 9: api.eonline.v1.CheckInfoReq + (*AddChatReq)(nil), // 10: api.eonline.v1.AddChatReq + (*GetChatReq)(nil), // 11: api.eonline.v1.GetChatReq + (*PayInitReply)(nil), // 12: api.eonline.v1.PayInitReply + (*PayoutReply)(nil), // 13: api.eonline.v1.PayoutReply + (*PayoutCallbackReply)(nil), // 14: api.eonline.v1.PayoutCallbackReply + (*PayoutCheckReply)(nil), // 15: api.eonline.v1.PayoutCheckReply + (*PayoutUserLstReply)(nil), // 16: api.eonline.v1.PayoutUserLstReply + (*PayoutStatusReply)(nil), // 17: api.eonline.v1.PayoutStatusReply + (*SubmitCheckReply)(nil), // 18: api.eonline.v1.SubmitCheckReply + (*CheckInfoReply)(nil), // 19: api.eonline.v1.CheckInfoReply + (*AddChatReply)(nil), // 20: api.eonline.v1.AddChatReply + (*GetChatReply)(nil), // 21: api.eonline.v1.GetChatReply +} +var file_api_eonline_v1_eonline_proto_depIdxs = []int32{ + 0, // 0: api.eonline.v1.Eonline.SayHello:input_type -> api.eonline.v1.HelloRequest + 2, // 1: api.eonline.v1.Eonline.PayInit:input_type -> api.eonline.v1.PayInitReq + 3, // 2: api.eonline.v1.Eonline.Payout:input_type -> api.eonline.v1.PayoutReq + 3, // 3: api.eonline.v1.Eonline.PayoutBrazil:input_type -> api.eonline.v1.PayoutReq + 4, // 4: api.eonline.v1.Eonline.PayoutCallback:input_type -> api.eonline.v1.PayoutCallbackReq + 5, // 5: api.eonline.v1.Eonline.PayoutCheck:input_type -> api.eonline.v1.PayoutCheckReq + 6, // 6: api.eonline.v1.Eonline.GetPayoutUserLst:input_type -> api.eonline.v1.PayoutUserLstReq + 7, // 7: api.eonline.v1.Eonline.SetPayoutStatus:input_type -> api.eonline.v1.PayoutStatusReq + 8, // 8: api.eonline.v1.Eonline.SubmitCheck:input_type -> api.eonline.v1.SubmitCheckReq + 9, // 9: api.eonline.v1.Eonline.CheckInfo:input_type -> api.eonline.v1.CheckInfoReq + 10, // 10: api.eonline.v1.Eonline.AddChat:input_type -> api.eonline.v1.AddChatReq + 11, // 11: api.eonline.v1.Eonline.GetChat:input_type -> api.eonline.v1.GetChatReq + 1, // 12: api.eonline.v1.Eonline.SayHello:output_type -> api.eonline.v1.HelloReply + 12, // 13: api.eonline.v1.Eonline.PayInit:output_type -> api.eonline.v1.PayInitReply + 13, // 14: api.eonline.v1.Eonline.Payout:output_type -> api.eonline.v1.PayoutReply + 13, // 15: api.eonline.v1.Eonline.PayoutBrazil:output_type -> api.eonline.v1.PayoutReply + 14, // 16: api.eonline.v1.Eonline.PayoutCallback:output_type -> api.eonline.v1.PayoutCallbackReply + 15, // 17: api.eonline.v1.Eonline.PayoutCheck:output_type -> api.eonline.v1.PayoutCheckReply + 16, // 18: api.eonline.v1.Eonline.GetPayoutUserLst:output_type -> api.eonline.v1.PayoutUserLstReply + 17, // 19: api.eonline.v1.Eonline.SetPayoutStatus:output_type -> api.eonline.v1.PayoutStatusReply + 18, // 20: api.eonline.v1.Eonline.SubmitCheck:output_type -> api.eonline.v1.SubmitCheckReply + 19, // 21: api.eonline.v1.Eonline.CheckInfo:output_type -> api.eonline.v1.CheckInfoReply + 20, // 22: api.eonline.v1.Eonline.AddChat:output_type -> api.eonline.v1.AddChatReply + 21, // 23: api.eonline.v1.Eonline.GetChat:output_type -> api.eonline.v1.GetChatReply + 12, // [12:24] is the sub-list for method output_type + 0, // [0:12] 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_api_eonline_v1_eonline_proto_init() } +func file_api_eonline_v1_eonline_proto_init() { + if File_api_eonline_v1_eonline_proto != nil { + return + } + file_api_eonline_v1_pagsmile_proto_init() + if !protoimpl.UnsafeEnabled { + file_api_eonline_v1_eonline_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HelloRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_eonline_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HelloReply); 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_api_eonline_v1_eonline_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_api_eonline_v1_eonline_proto_goTypes, + DependencyIndexes: file_api_eonline_v1_eonline_proto_depIdxs, + MessageInfos: file_api_eonline_v1_eonline_proto_msgTypes, + }.Build() + File_api_eonline_v1_eonline_proto = out.File + file_api_eonline_v1_eonline_proto_rawDesc = nil + file_api_eonline_v1_eonline_proto_goTypes = nil + file_api_eonline_v1_eonline_proto_depIdxs = nil +} diff --git a/api/eonline/v1/eonline.proto b/api/eonline/v1/eonline.proto new file mode 100644 index 0000000..7f4e487 --- /dev/null +++ b/api/eonline/v1/eonline.proto @@ -0,0 +1,119 @@ +syntax = "proto3"; + +package api.eonline.v1; + +import "google/api/annotations.proto"; +import "api/eonline/v1/pagsmile.proto"; + +option go_package = "sandc/api/eonline/v1;v1"; +option java_multiple_files = true; +option java_package = "dev.kratos.api.eonline.v1"; +option java_outer_classname = "EonlineProtoV1"; + +// The greeting service definition. +service Eonline { + // Sends a greeting,客户端暂未用到 + rpc SayHello(HelloRequest) returns (HelloReply) { + option (google.api.http) = { + get: "/eonline4/{name}" + }; + } + + // PayInit + rpc PayInit(PayInitReq) returns (PayInitReply) { + option (google.api.http) = { + post: "/eonline4/pay/init" + body: "*" + }; + } + + // Payout,客户端暂未用到 + rpc Payout(PayoutReq) returns (PayoutReply) { + option (google.api.http) = { + post: "/eonline4/payout" + body: "*" + }; + } + + // PayoutBrazil,用于巴西PIX支付 + rpc PayoutBrazil(PayoutReq) returns (PayoutReply) { + option (google.api.http) = { + post: "/eonline4/payoutBrazil" + body: "*" + }; + } + + // PayoutCallback,客户端暂未用到 + rpc PayoutCallback(PayoutCallbackReq) returns (PayoutCallbackReply) { + option (google.api.http) = { + post: "/eonline4/payout/callback" + body: "*" + }; + } + + // PayoutCheck + rpc PayoutCheck(PayoutCheckReq) returns (PayoutCheckReply) { + option (google.api.http) = { + post: "/eonline4/payout/check" + body: "*" + }; + } + + // 获取申请提现玩家的列表 + rpc GetPayoutUserLst(PayoutUserLstReq) returns (PayoutUserLstReply) { + option (google.api.http) = { + post: "/eonline4/getPayoutUserLst" + body: "*" + }; + } + + // 设置指定玩家的提现状态 + rpc SetPayoutStatus(PayoutStatusReq) returns (PayoutStatusReply) { + option (google.api.http) = { + post: "/eonline4/setPayoutStatus" + body: "*" + }; + } + + // SubmitCheck,客户端暂未用到 + rpc SubmitCheck(SubmitCheckReq) returns (SubmitCheckReply) { + option (google.api.http) = { + post: "/eonline4/submitcheck" + body: "*" + }; + } + + // CheckInfo,客户端暂未用到 + rpc CheckInfo(CheckInfoReq) returns (CheckInfoReply) { + option (google.api.http) = { + post: "/eonline4/checkinfo" + body: "*" + }; + } + + // 发送聊天消息,客户端暂未用到 + rpc AddChat(AddChatReq) returns (AddChatReply) { + option (google.api.http) = { + post: "/eonline4/addchat" + body: "*" + }; + } + + // 获取聊天消息列表,客户端暂未用到 + rpc GetChat(GetChatReq) returns (GetChatReply) { + option (google.api.http) = { + post: "/eonline4/getchat" + body: "*" + }; + } +} + +// The request message containing the user's name. +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} diff --git a/api/eonline/v1/eonline_error.pb.go b/api/eonline/v1/eonline_error.pb.go new file mode 100644 index 0000000..17ec27f --- /dev/null +++ b/api/eonline/v1/eonline_error.pb.go @@ -0,0 +1,136 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.30.0 +// protoc v3.21.12 +// source: v1/eonline_error.proto + +package v1 + +import ( + _ "github.com/go-kratos/kratos/v2/errors" + 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 EonlineError int32 + +const ( + EonlineError_USER_NOT_FOUND EonlineError = 0 + EonlineError_CONTENT_MISSING EonlineError = 1 +) + +// Enum value maps for EonlineError. +var ( + EonlineError_name = map[int32]string{ + 0: "USER_NOT_FOUND", + 1: "CONTENT_MISSING", + } + EonlineError_value = map[string]int32{ + "USER_NOT_FOUND": 0, + "CONTENT_MISSING": 1, + } +) + +func (x EonlineError) Enum() *EonlineError { + p := new(EonlineError) + *p = x + return p +} + +func (x EonlineError) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (EonlineError) Descriptor() protoreflect.EnumDescriptor { + return file_v1_eonline_error_proto_enumTypes[0].Descriptor() +} + +func (EonlineError) Type() protoreflect.EnumType { + return &file_v1_eonline_error_proto_enumTypes[0] +} + +func (x EonlineError) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use EonlineError.Descriptor instead. +func (EonlineError) EnumDescriptor() ([]byte, []int) { + return file_v1_eonline_error_proto_rawDescGZIP(), []int{0} +} + +var File_v1_eonline_error_proto protoreflect.FileDescriptor + +var file_v1_eonline_error_proto_rawDesc = []byte{ + 0x0a, 0x16, 0x76, 0x31, 0x2f, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x13, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, + 0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2a, 0x49, 0x0a, + 0x0c, 0x45, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, + 0x0e, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x46, 0x4f, 0x55, 0x4e, 0x44, 0x10, + 0x00, 0x1a, 0x04, 0xa8, 0x45, 0x94, 0x03, 0x12, 0x19, 0x0a, 0x0f, 0x43, 0x4f, 0x4e, 0x54, 0x45, + 0x4e, 0x54, 0x5f, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x1a, 0x04, 0xa8, 0x45, + 0x90, 0x03, 0x1a, 0x04, 0xa0, 0x45, 0xf4, 0x03, 0x42, 0x41, 0x0a, 0x11, 0x65, 0x6f, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x50, 0x01, 0x5a, + 0x17, 0x73, 0x61, 0x6e, 0x64, 0x63, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x65, 0x6f, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x76, 0x31, 0xa2, 0x02, 0x10, 0x41, 0x50, 0x49, 0x45, 0x6f, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +} + +var ( + file_v1_eonline_error_proto_rawDescOnce sync.Once + file_v1_eonline_error_proto_rawDescData = file_v1_eonline_error_proto_rawDesc +) + +func file_v1_eonline_error_proto_rawDescGZIP() []byte { + file_v1_eonline_error_proto_rawDescOnce.Do(func() { + file_v1_eonline_error_proto_rawDescData = protoimpl.X.CompressGZIP(file_v1_eonline_error_proto_rawDescData) + }) + return file_v1_eonline_error_proto_rawDescData +} + +var file_v1_eonline_error_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_v1_eonline_error_proto_goTypes = []interface{}{ + (EonlineError)(0), // 0: api.eonline.v1.EonlineError +} +var file_v1_eonline_error_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_v1_eonline_error_proto_init() } +func file_v1_eonline_error_proto_init() { + if File_v1_eonline_error_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_v1_eonline_error_proto_rawDesc, + NumEnums: 1, + NumMessages: 0, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_v1_eonline_error_proto_goTypes, + DependencyIndexes: file_v1_eonline_error_proto_depIdxs, + EnumInfos: file_v1_eonline_error_proto_enumTypes, + }.Build() + File_v1_eonline_error_proto = out.File + file_v1_eonline_error_proto_rawDesc = nil + file_v1_eonline_error_proto_goTypes = nil + file_v1_eonline_error_proto_depIdxs = nil +} diff --git a/api/eonline/v1/eonline_error.pb.validate.go b/api/eonline/v1/eonline_error.pb.validate.go new file mode 100644 index 0000000..09376c2 --- /dev/null +++ b/api/eonline/v1/eonline_error.pb.validate.go @@ -0,0 +1,36 @@ +// Code generated by protoc-gen-validate. DO NOT EDIT. +// source: v1/eonline_error.proto + +package v1 + +import ( + "bytes" + "errors" + "fmt" + "net" + "net/mail" + "net/url" + "regexp" + "sort" + "strings" + "time" + "unicode/utf8" + + "google.golang.org/protobuf/types/known/anypb" +) + +// ensure the imports are used +var ( + _ = bytes.MinRead + _ = errors.New("") + _ = fmt.Print + _ = utf8.UTFMax + _ = (*regexp.Regexp)(nil) + _ = (*strings.Reader)(nil) + _ = net.IPv4len + _ = time.Duration(0) + _ = (*url.URL)(nil) + _ = (*mail.Address)(nil) + _ = anypb.Any{} + _ = sort.Sort +) diff --git a/api/eonline/v1/eonline_error.proto b/api/eonline/v1/eonline_error.proto new file mode 100644 index 0000000..8c0c1e3 --- /dev/null +++ b/api/eonline/v1/eonline_error.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package api.eonline.v1; +import "errors/errors.proto"; + +option go_package = "sandc/api/eonline/v1;v1"; +option java_multiple_files = true; +option java_package = "eonline.v1.errors"; +option objc_class_prefix = "APIEonlineErrors"; + +enum EonlineError { + option (errors.default_code) = 500; + + USER_NOT_FOUND = 0 [(errors.code) = 404]; + CONTENT_MISSING = 1 [(errors.code) = 400]; +} diff --git a/api/eonline/v1/eonline_error_errors.pb.go b/api/eonline/v1/eonline_error_errors.pb.go new file mode 100644 index 0000000..9989287 --- /dev/null +++ b/api/eonline/v1/eonline_error_errors.pb.go @@ -0,0 +1,36 @@ +// Code generated by protoc-gen-go-errors. DO NOT EDIT. + +package v1 + +import ( + fmt "fmt" + errors "github.com/go-kratos/kratos/v2/errors" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the kratos package it is being compiled against. +const _ = errors.SupportPackageIsVersion1 + +func IsUserNotFound(err error) bool { + if err == nil { + return false + } + e := errors.FromError(err) + return e.Reason == EonlineError_USER_NOT_FOUND.String() && e.Code == 404 +} + +func ErrorUserNotFound(format string, args ...interface{}) *errors.Error { + return errors.New(404, EonlineError_USER_NOT_FOUND.String(), fmt.Sprintf(format, args...)) +} + +func IsContentMissing(err error) bool { + if err == nil { + return false + } + e := errors.FromError(err) + return e.Reason == EonlineError_CONTENT_MISSING.String() && e.Code == 400 +} + +func ErrorContentMissing(format string, args ...interface{}) *errors.Error { + return errors.New(400, EonlineError_CONTENT_MISSING.String(), fmt.Sprintf(format, args...)) +} diff --git a/api/eonline/v1/eonline_grpc.pb.go b/api/eonline/v1/eonline_grpc.pb.go new file mode 100644 index 0000000..315cc36 --- /dev/null +++ b/api/eonline/v1/eonline_grpc.pb.go @@ -0,0 +1,540 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v3.20.3 +// source: api/eonline/v1/eonline.proto + +package v1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + Eonline_SayHello_FullMethodName = "/api.eonline.v1.Eonline/SayHello" + Eonline_PayInit_FullMethodName = "/api.eonline.v1.Eonline/PayInit" + Eonline_Payout_FullMethodName = "/api.eonline.v1.Eonline/Payout" + Eonline_PayoutBrazil_FullMethodName = "/api.eonline.v1.Eonline/PayoutBrazil" + Eonline_PayoutCallback_FullMethodName = "/api.eonline.v1.Eonline/PayoutCallback" + Eonline_PayoutCheck_FullMethodName = "/api.eonline.v1.Eonline/PayoutCheck" + Eonline_GetPayoutUserLst_FullMethodName = "/api.eonline.v1.Eonline/GetPayoutUserLst" + Eonline_SetPayoutStatus_FullMethodName = "/api.eonline.v1.Eonline/SetPayoutStatus" + Eonline_SubmitCheck_FullMethodName = "/api.eonline.v1.Eonline/SubmitCheck" + Eonline_CheckInfo_FullMethodName = "/api.eonline.v1.Eonline/CheckInfo" + Eonline_AddChat_FullMethodName = "/api.eonline.v1.Eonline/AddChat" + Eonline_GetChat_FullMethodName = "/api.eonline.v1.Eonline/GetChat" +) + +// EonlineClient is the client API for Eonline service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type EonlineClient interface { + // Sends a greeting,客户端暂未用到 + SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) + // PayInit + PayInit(ctx context.Context, in *PayInitReq, opts ...grpc.CallOption) (*PayInitReply, error) + // Payout,客户端暂未用到 + Payout(ctx context.Context, in *PayoutReq, opts ...grpc.CallOption) (*PayoutReply, error) + // PayoutBrazil,用于巴西PIX支付 + PayoutBrazil(ctx context.Context, in *PayoutReq, opts ...grpc.CallOption) (*PayoutReply, error) + // PayoutCallback,客户端暂未用到 + PayoutCallback(ctx context.Context, in *PayoutCallbackReq, opts ...grpc.CallOption) (*PayoutCallbackReply, error) + // PayoutCheck + PayoutCheck(ctx context.Context, in *PayoutCheckReq, opts ...grpc.CallOption) (*PayoutCheckReply, error) + // 获取申请提现玩家的列表 + GetPayoutUserLst(ctx context.Context, in *PayoutUserLstReq, opts ...grpc.CallOption) (*PayoutUserLstReply, error) + // 设置指定玩家的提现状态 + SetPayoutStatus(ctx context.Context, in *PayoutStatusReq, opts ...grpc.CallOption) (*PayoutStatusReply, error) + // SubmitCheck,客户端暂未用到 + SubmitCheck(ctx context.Context, in *SubmitCheckReq, opts ...grpc.CallOption) (*SubmitCheckReply, error) + // CheckInfo,客户端暂未用到 + CheckInfo(ctx context.Context, in *CheckInfoReq, opts ...grpc.CallOption) (*CheckInfoReply, error) + // 发送聊天消息,客户端暂未用到 + AddChat(ctx context.Context, in *AddChatReq, opts ...grpc.CallOption) (*AddChatReply, error) + // 获取聊天消息列表,客户端暂未用到 + GetChat(ctx context.Context, in *GetChatReq, opts ...grpc.CallOption) (*GetChatReply, error) +} + +type eonlineClient struct { + cc grpc.ClientConnInterface +} + +func NewEonlineClient(cc grpc.ClientConnInterface) EonlineClient { + return &eonlineClient{cc} +} + +func (c *eonlineClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) { + out := new(HelloReply) + err := c.cc.Invoke(ctx, Eonline_SayHello_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *eonlineClient) PayInit(ctx context.Context, in *PayInitReq, opts ...grpc.CallOption) (*PayInitReply, error) { + out := new(PayInitReply) + err := c.cc.Invoke(ctx, Eonline_PayInit_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *eonlineClient) Payout(ctx context.Context, in *PayoutReq, opts ...grpc.CallOption) (*PayoutReply, error) { + out := new(PayoutReply) + err := c.cc.Invoke(ctx, Eonline_Payout_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *eonlineClient) PayoutBrazil(ctx context.Context, in *PayoutReq, opts ...grpc.CallOption) (*PayoutReply, error) { + out := new(PayoutReply) + err := c.cc.Invoke(ctx, Eonline_PayoutBrazil_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *eonlineClient) PayoutCallback(ctx context.Context, in *PayoutCallbackReq, opts ...grpc.CallOption) (*PayoutCallbackReply, error) { + out := new(PayoutCallbackReply) + err := c.cc.Invoke(ctx, Eonline_PayoutCallback_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *eonlineClient) PayoutCheck(ctx context.Context, in *PayoutCheckReq, opts ...grpc.CallOption) (*PayoutCheckReply, error) { + out := new(PayoutCheckReply) + err := c.cc.Invoke(ctx, Eonline_PayoutCheck_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *eonlineClient) GetPayoutUserLst(ctx context.Context, in *PayoutUserLstReq, opts ...grpc.CallOption) (*PayoutUserLstReply, error) { + out := new(PayoutUserLstReply) + err := c.cc.Invoke(ctx, Eonline_GetPayoutUserLst_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *eonlineClient) SetPayoutStatus(ctx context.Context, in *PayoutStatusReq, opts ...grpc.CallOption) (*PayoutStatusReply, error) { + out := new(PayoutStatusReply) + err := c.cc.Invoke(ctx, Eonline_SetPayoutStatus_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *eonlineClient) SubmitCheck(ctx context.Context, in *SubmitCheckReq, opts ...grpc.CallOption) (*SubmitCheckReply, error) { + out := new(SubmitCheckReply) + err := c.cc.Invoke(ctx, Eonline_SubmitCheck_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *eonlineClient) CheckInfo(ctx context.Context, in *CheckInfoReq, opts ...grpc.CallOption) (*CheckInfoReply, error) { + out := new(CheckInfoReply) + err := c.cc.Invoke(ctx, Eonline_CheckInfo_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *eonlineClient) AddChat(ctx context.Context, in *AddChatReq, opts ...grpc.CallOption) (*AddChatReply, error) { + out := new(AddChatReply) + err := c.cc.Invoke(ctx, Eonline_AddChat_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *eonlineClient) GetChat(ctx context.Context, in *GetChatReq, opts ...grpc.CallOption) (*GetChatReply, error) { + out := new(GetChatReply) + err := c.cc.Invoke(ctx, Eonline_GetChat_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// EonlineServer is the server API for Eonline service. +// All implementations must embed UnimplementedEonlineServer +// for forward compatibility +type EonlineServer interface { + // Sends a greeting,客户端暂未用到 + SayHello(context.Context, *HelloRequest) (*HelloReply, error) + // PayInit + PayInit(context.Context, *PayInitReq) (*PayInitReply, error) + // Payout,客户端暂未用到 + Payout(context.Context, *PayoutReq) (*PayoutReply, error) + // PayoutBrazil,用于巴西PIX支付 + PayoutBrazil(context.Context, *PayoutReq) (*PayoutReply, error) + // PayoutCallback,客户端暂未用到 + PayoutCallback(context.Context, *PayoutCallbackReq) (*PayoutCallbackReply, error) + // PayoutCheck + PayoutCheck(context.Context, *PayoutCheckReq) (*PayoutCheckReply, error) + // 获取申请提现玩家的列表 + GetPayoutUserLst(context.Context, *PayoutUserLstReq) (*PayoutUserLstReply, error) + // 设置指定玩家的提现状态 + SetPayoutStatus(context.Context, *PayoutStatusReq) (*PayoutStatusReply, error) + // SubmitCheck,客户端暂未用到 + SubmitCheck(context.Context, *SubmitCheckReq) (*SubmitCheckReply, error) + // CheckInfo,客户端暂未用到 + CheckInfo(context.Context, *CheckInfoReq) (*CheckInfoReply, error) + // 发送聊天消息,客户端暂未用到 + AddChat(context.Context, *AddChatReq) (*AddChatReply, error) + // 获取聊天消息列表,客户端暂未用到 + GetChat(context.Context, *GetChatReq) (*GetChatReply, error) + mustEmbedUnimplementedEonlineServer() +} + +// UnimplementedEonlineServer must be embedded to have forward compatible implementations. +type UnimplementedEonlineServer struct { +} + +func (UnimplementedEonlineServer) SayHello(context.Context, *HelloRequest) (*HelloReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented") +} +func (UnimplementedEonlineServer) PayInit(context.Context, *PayInitReq) (*PayInitReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method PayInit not implemented") +} +func (UnimplementedEonlineServer) Payout(context.Context, *PayoutReq) (*PayoutReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method Payout not implemented") +} +func (UnimplementedEonlineServer) PayoutBrazil(context.Context, *PayoutReq) (*PayoutReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method PayoutBrazil not implemented") +} +func (UnimplementedEonlineServer) PayoutCallback(context.Context, *PayoutCallbackReq) (*PayoutCallbackReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method PayoutCallback not implemented") +} +func (UnimplementedEonlineServer) PayoutCheck(context.Context, *PayoutCheckReq) (*PayoutCheckReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method PayoutCheck not implemented") +} +func (UnimplementedEonlineServer) GetPayoutUserLst(context.Context, *PayoutUserLstReq) (*PayoutUserLstReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPayoutUserLst not implemented") +} +func (UnimplementedEonlineServer) SetPayoutStatus(context.Context, *PayoutStatusReq) (*PayoutStatusReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetPayoutStatus not implemented") +} +func (UnimplementedEonlineServer) SubmitCheck(context.Context, *SubmitCheckReq) (*SubmitCheckReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method SubmitCheck not implemented") +} +func (UnimplementedEonlineServer) CheckInfo(context.Context, *CheckInfoReq) (*CheckInfoReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method CheckInfo not implemented") +} +func (UnimplementedEonlineServer) AddChat(context.Context, *AddChatReq) (*AddChatReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddChat not implemented") +} +func (UnimplementedEonlineServer) GetChat(context.Context, *GetChatReq) (*GetChatReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetChat not implemented") +} +func (UnimplementedEonlineServer) mustEmbedUnimplementedEonlineServer() {} + +// UnsafeEonlineServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to EonlineServer will +// result in compilation errors. +type UnsafeEonlineServer interface { + mustEmbedUnimplementedEonlineServer() +} + +func RegisterEonlineServer(s grpc.ServiceRegistrar, srv EonlineServer) { + s.RegisterService(&Eonline_ServiceDesc, srv) +} + +func _Eonline_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HelloRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EonlineServer).SayHello(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Eonline_SayHello_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EonlineServer).SayHello(ctx, req.(*HelloRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Eonline_PayInit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PayInitReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EonlineServer).PayInit(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Eonline_PayInit_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EonlineServer).PayInit(ctx, req.(*PayInitReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Eonline_Payout_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PayoutReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EonlineServer).Payout(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Eonline_Payout_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EonlineServer).Payout(ctx, req.(*PayoutReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Eonline_PayoutBrazil_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PayoutReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EonlineServer).PayoutBrazil(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Eonline_PayoutBrazil_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EonlineServer).PayoutBrazil(ctx, req.(*PayoutReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Eonline_PayoutCallback_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PayoutCallbackReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EonlineServer).PayoutCallback(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Eonline_PayoutCallback_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EonlineServer).PayoutCallback(ctx, req.(*PayoutCallbackReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Eonline_PayoutCheck_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PayoutCheckReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EonlineServer).PayoutCheck(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Eonline_PayoutCheck_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EonlineServer).PayoutCheck(ctx, req.(*PayoutCheckReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Eonline_GetPayoutUserLst_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PayoutUserLstReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EonlineServer).GetPayoutUserLst(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Eonline_GetPayoutUserLst_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EonlineServer).GetPayoutUserLst(ctx, req.(*PayoutUserLstReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Eonline_SetPayoutStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PayoutStatusReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EonlineServer).SetPayoutStatus(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Eonline_SetPayoutStatus_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EonlineServer).SetPayoutStatus(ctx, req.(*PayoutStatusReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Eonline_SubmitCheck_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SubmitCheckReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EonlineServer).SubmitCheck(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Eonline_SubmitCheck_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EonlineServer).SubmitCheck(ctx, req.(*SubmitCheckReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Eonline_CheckInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CheckInfoReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EonlineServer).CheckInfo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Eonline_CheckInfo_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EonlineServer).CheckInfo(ctx, req.(*CheckInfoReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Eonline_AddChat_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddChatReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EonlineServer).AddChat(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Eonline_AddChat_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EonlineServer).AddChat(ctx, req.(*AddChatReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Eonline_GetChat_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetChatReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EonlineServer).GetChat(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Eonline_GetChat_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EonlineServer).GetChat(ctx, req.(*GetChatReq)) + } + return interceptor(ctx, in, info, handler) +} + +// Eonline_ServiceDesc is the grpc.ServiceDesc for Eonline service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Eonline_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "api.eonline.v1.Eonline", + HandlerType: (*EonlineServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SayHello", + Handler: _Eonline_SayHello_Handler, + }, + { + MethodName: "PayInit", + Handler: _Eonline_PayInit_Handler, + }, + { + MethodName: "Payout", + Handler: _Eonline_Payout_Handler, + }, + { + MethodName: "PayoutBrazil", + Handler: _Eonline_PayoutBrazil_Handler, + }, + { + MethodName: "PayoutCallback", + Handler: _Eonline_PayoutCallback_Handler, + }, + { + MethodName: "PayoutCheck", + Handler: _Eonline_PayoutCheck_Handler, + }, + { + MethodName: "GetPayoutUserLst", + Handler: _Eonline_GetPayoutUserLst_Handler, + }, + { + MethodName: "SetPayoutStatus", + Handler: _Eonline_SetPayoutStatus_Handler, + }, + { + MethodName: "SubmitCheck", + Handler: _Eonline_SubmitCheck_Handler, + }, + { + MethodName: "CheckInfo", + Handler: _Eonline_CheckInfo_Handler, + }, + { + MethodName: "AddChat", + Handler: _Eonline_AddChat_Handler, + }, + { + MethodName: "GetChat", + Handler: _Eonline_GetChat_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "api/eonline/v1/eonline.proto", +} diff --git a/api/eonline/v1/eonline_http.pb.go b/api/eonline/v1/eonline_http.pb.go new file mode 100644 index 0000000..0c8770d --- /dev/null +++ b/api/eonline/v1/eonline_http.pb.go @@ -0,0 +1,519 @@ +// Code generated by protoc-gen-go-http. DO NOT EDIT. +// versions: +// - protoc-gen-go-http v2.7.0 +// - protoc v3.20.3 +// source: api/eonline/v1/eonline.proto + +package v1 + +import ( + context "context" + http "github.com/go-kratos/kratos/v2/transport/http" + binding "github.com/go-kratos/kratos/v2/transport/http/binding" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the kratos package it is being compiled against. +var _ = new(context.Context) +var _ = binding.EncodeURL + +const _ = http.SupportPackageIsVersion1 + +const OperationEonlineAddChat = "/api.eonline.v1.Eonline/AddChat" +const OperationEonlineCheckInfo = "/api.eonline.v1.Eonline/CheckInfo" +const OperationEonlineGetChat = "/api.eonline.v1.Eonline/GetChat" +const OperationEonlineGetPayoutUserLst = "/api.eonline.v1.Eonline/GetPayoutUserLst" +const OperationEonlinePayInit = "/api.eonline.v1.Eonline/PayInit" +const OperationEonlinePayout = "/api.eonline.v1.Eonline/Payout" +const OperationEonlinePayoutBrazil = "/api.eonline.v1.Eonline/PayoutBrazil" +const OperationEonlinePayoutCallback = "/api.eonline.v1.Eonline/PayoutCallback" +const OperationEonlinePayoutCheck = "/api.eonline.v1.Eonline/PayoutCheck" +const OperationEonlineSayHello = "/api.eonline.v1.Eonline/SayHello" +const OperationEonlineSetPayoutStatus = "/api.eonline.v1.Eonline/SetPayoutStatus" +const OperationEonlineSubmitCheck = "/api.eonline.v1.Eonline/SubmitCheck" + +type EonlineHTTPServer interface { + // AddChat 发送聊天消息,客户端暂未用到 + AddChat(context.Context, *AddChatReq) (*AddChatReply, error) + // CheckInfo CheckInfo,客户端暂未用到 + CheckInfo(context.Context, *CheckInfoReq) (*CheckInfoReply, error) + // GetChat 获取聊天消息列表,客户端暂未用到 + GetChat(context.Context, *GetChatReq) (*GetChatReply, error) + // GetPayoutUserLst 获取申请提现玩家的列表 + GetPayoutUserLst(context.Context, *PayoutUserLstReq) (*PayoutUserLstReply, error) + // PayInit PayInit + PayInit(context.Context, *PayInitReq) (*PayInitReply, error) + // Payout Payout,客户端暂未用到 + Payout(context.Context, *PayoutReq) (*PayoutReply, error) + // PayoutBrazil PayoutBrazil,用于巴西PIX支付 + PayoutBrazil(context.Context, *PayoutReq) (*PayoutReply, error) + // PayoutCallback PayoutCallback,客户端暂未用到 + PayoutCallback(context.Context, *PayoutCallbackReq) (*PayoutCallbackReply, error) + // PayoutCheck PayoutCheck + PayoutCheck(context.Context, *PayoutCheckReq) (*PayoutCheckReply, error) + // SayHello Sends a greeting,客户端暂未用到 + SayHello(context.Context, *HelloRequest) (*HelloReply, error) + // SetPayoutStatus 设置指定玩家的提现状态 + SetPayoutStatus(context.Context, *PayoutStatusReq) (*PayoutStatusReply, error) + // SubmitCheck SubmitCheck,客户端暂未用到 + SubmitCheck(context.Context, *SubmitCheckReq) (*SubmitCheckReply, error) +} + +func RegisterEonlineHTTPServer(s *http.Server, srv EonlineHTTPServer) { + r := s.Route("/") + r.GET("/eonline4/{name}", _Eonline_SayHello0_HTTP_Handler(srv)) + r.POST("/eonline4/pay/init", _Eonline_PayInit0_HTTP_Handler(srv)) + r.POST("/eonline4/payout", _Eonline_Payout0_HTTP_Handler(srv)) + r.POST("/eonline4/payoutBrazil", _Eonline_PayoutBrazil0_HTTP_Handler(srv)) + r.POST("/eonline4/payout/callback", _Eonline_PayoutCallback0_HTTP_Handler(srv)) + r.POST("/eonline4/payout/check", _Eonline_PayoutCheck0_HTTP_Handler(srv)) + r.POST("/eonline4/getPayoutUserLst", _Eonline_GetPayoutUserLst0_HTTP_Handler(srv)) + r.POST("/eonline4/setPayoutStatus", _Eonline_SetPayoutStatus0_HTTP_Handler(srv)) + r.POST("/eonline4/submitcheck", _Eonline_SubmitCheck0_HTTP_Handler(srv)) + r.POST("/eonline4/checkinfo", _Eonline_CheckInfo0_HTTP_Handler(srv)) + r.POST("/eonline4/addchat", _Eonline_AddChat0_HTTP_Handler(srv)) + r.POST("/eonline4/getchat", _Eonline_GetChat0_HTTP_Handler(srv)) +} + +func _Eonline_SayHello0_HTTP_Handler(srv EonlineHTTPServer) func(ctx http.Context) error { + return func(ctx http.Context) error { + var in HelloRequest + if err := ctx.BindQuery(&in); err != nil { + return err + } + if err := ctx.BindVars(&in); err != nil { + return err + } + http.SetOperation(ctx, OperationEonlineSayHello) + h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.SayHello(ctx, req.(*HelloRequest)) + }) + out, err := h(ctx, &in) + if err != nil { + return err + } + reply := out.(*HelloReply) + return ctx.Result(200, reply) + } +} + +func _Eonline_PayInit0_HTTP_Handler(srv EonlineHTTPServer) func(ctx http.Context) error { + return func(ctx http.Context) error { + var in PayInitReq + if err := ctx.Bind(&in); err != nil { + return err + } + if err := ctx.BindQuery(&in); err != nil { + return err + } + http.SetOperation(ctx, OperationEonlinePayInit) + h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.PayInit(ctx, req.(*PayInitReq)) + }) + out, err := h(ctx, &in) + if err != nil { + return err + } + reply := out.(*PayInitReply) + return ctx.Result(200, reply) + } +} + +func _Eonline_Payout0_HTTP_Handler(srv EonlineHTTPServer) func(ctx http.Context) error { + return func(ctx http.Context) error { + var in PayoutReq + if err := ctx.Bind(&in); err != nil { + return err + } + if err := ctx.BindQuery(&in); err != nil { + return err + } + http.SetOperation(ctx, OperationEonlinePayout) + h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.Payout(ctx, req.(*PayoutReq)) + }) + out, err := h(ctx, &in) + if err != nil { + return err + } + reply := out.(*PayoutReply) + return ctx.Result(200, reply) + } +} + +func _Eonline_PayoutBrazil0_HTTP_Handler(srv EonlineHTTPServer) func(ctx http.Context) error { + return func(ctx http.Context) error { + var in PayoutReq + if err := ctx.Bind(&in); err != nil { + return err + } + if err := ctx.BindQuery(&in); err != nil { + return err + } + http.SetOperation(ctx, OperationEonlinePayoutBrazil) + h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.PayoutBrazil(ctx, req.(*PayoutReq)) + }) + out, err := h(ctx, &in) + if err != nil { + return err + } + reply := out.(*PayoutReply) + return ctx.Result(200, reply) + } +} + +func _Eonline_PayoutCallback0_HTTP_Handler(srv EonlineHTTPServer) func(ctx http.Context) error { + return func(ctx http.Context) error { + var in PayoutCallbackReq + if err := ctx.Bind(&in); err != nil { + return err + } + if err := ctx.BindQuery(&in); err != nil { + return err + } + http.SetOperation(ctx, OperationEonlinePayoutCallback) + h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.PayoutCallback(ctx, req.(*PayoutCallbackReq)) + }) + out, err := h(ctx, &in) + if err != nil { + return err + } + reply := out.(*PayoutCallbackReply) + return ctx.Result(200, reply) + } +} + +func _Eonline_PayoutCheck0_HTTP_Handler(srv EonlineHTTPServer) func(ctx http.Context) error { + return func(ctx http.Context) error { + var in PayoutCheckReq + if err := ctx.Bind(&in); err != nil { + return err + } + if err := ctx.BindQuery(&in); err != nil { + return err + } + http.SetOperation(ctx, OperationEonlinePayoutCheck) + h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.PayoutCheck(ctx, req.(*PayoutCheckReq)) + }) + out, err := h(ctx, &in) + if err != nil { + return err + } + reply := out.(*PayoutCheckReply) + return ctx.Result(200, reply) + } +} + +func _Eonline_GetPayoutUserLst0_HTTP_Handler(srv EonlineHTTPServer) func(ctx http.Context) error { + return func(ctx http.Context) error { + var in PayoutUserLstReq + if err := ctx.Bind(&in); err != nil { + return err + } + if err := ctx.BindQuery(&in); err != nil { + return err + } + http.SetOperation(ctx, OperationEonlineGetPayoutUserLst) + h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.GetPayoutUserLst(ctx, req.(*PayoutUserLstReq)) + }) + out, err := h(ctx, &in) + if err != nil { + return err + } + reply := out.(*PayoutUserLstReply) + return ctx.Result(200, reply) + } +} + +func _Eonline_SetPayoutStatus0_HTTP_Handler(srv EonlineHTTPServer) func(ctx http.Context) error { + return func(ctx http.Context) error { + var in PayoutStatusReq + if err := ctx.Bind(&in); err != nil { + return err + } + if err := ctx.BindQuery(&in); err != nil { + return err + } + http.SetOperation(ctx, OperationEonlineSetPayoutStatus) + h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.SetPayoutStatus(ctx, req.(*PayoutStatusReq)) + }) + out, err := h(ctx, &in) + if err != nil { + return err + } + reply := out.(*PayoutStatusReply) + return ctx.Result(200, reply) + } +} + +func _Eonline_SubmitCheck0_HTTP_Handler(srv EonlineHTTPServer) func(ctx http.Context) error { + return func(ctx http.Context) error { + var in SubmitCheckReq + if err := ctx.Bind(&in); err != nil { + return err + } + if err := ctx.BindQuery(&in); err != nil { + return err + } + http.SetOperation(ctx, OperationEonlineSubmitCheck) + h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.SubmitCheck(ctx, req.(*SubmitCheckReq)) + }) + out, err := h(ctx, &in) + if err != nil { + return err + } + reply := out.(*SubmitCheckReply) + return ctx.Result(200, reply) + } +} + +func _Eonline_CheckInfo0_HTTP_Handler(srv EonlineHTTPServer) func(ctx http.Context) error { + return func(ctx http.Context) error { + var in CheckInfoReq + if err := ctx.Bind(&in); err != nil { + return err + } + if err := ctx.BindQuery(&in); err != nil { + return err + } + http.SetOperation(ctx, OperationEonlineCheckInfo) + h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.CheckInfo(ctx, req.(*CheckInfoReq)) + }) + out, err := h(ctx, &in) + if err != nil { + return err + } + reply := out.(*CheckInfoReply) + return ctx.Result(200, reply) + } +} + +func _Eonline_AddChat0_HTTP_Handler(srv EonlineHTTPServer) func(ctx http.Context) error { + return func(ctx http.Context) error { + var in AddChatReq + if err := ctx.Bind(&in); err != nil { + return err + } + if err := ctx.BindQuery(&in); err != nil { + return err + } + http.SetOperation(ctx, OperationEonlineAddChat) + h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.AddChat(ctx, req.(*AddChatReq)) + }) + out, err := h(ctx, &in) + if err != nil { + return err + } + reply := out.(*AddChatReply) + return ctx.Result(200, reply) + } +} + +func _Eonline_GetChat0_HTTP_Handler(srv EonlineHTTPServer) func(ctx http.Context) error { + return func(ctx http.Context) error { + var in GetChatReq + if err := ctx.Bind(&in); err != nil { + return err + } + if err := ctx.BindQuery(&in); err != nil { + return err + } + http.SetOperation(ctx, OperationEonlineGetChat) + h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.GetChat(ctx, req.(*GetChatReq)) + }) + out, err := h(ctx, &in) + if err != nil { + return err + } + reply := out.(*GetChatReply) + return ctx.Result(200, reply) + } +} + +type EonlineHTTPClient interface { + AddChat(ctx context.Context, req *AddChatReq, opts ...http.CallOption) (rsp *AddChatReply, err error) + CheckInfo(ctx context.Context, req *CheckInfoReq, opts ...http.CallOption) (rsp *CheckInfoReply, err error) + GetChat(ctx context.Context, req *GetChatReq, opts ...http.CallOption) (rsp *GetChatReply, err error) + GetPayoutUserLst(ctx context.Context, req *PayoutUserLstReq, opts ...http.CallOption) (rsp *PayoutUserLstReply, err error) + PayInit(ctx context.Context, req *PayInitReq, opts ...http.CallOption) (rsp *PayInitReply, err error) + Payout(ctx context.Context, req *PayoutReq, opts ...http.CallOption) (rsp *PayoutReply, err error) + PayoutBrazil(ctx context.Context, req *PayoutReq, opts ...http.CallOption) (rsp *PayoutReply, err error) + PayoutCallback(ctx context.Context, req *PayoutCallbackReq, opts ...http.CallOption) (rsp *PayoutCallbackReply, err error) + PayoutCheck(ctx context.Context, req *PayoutCheckReq, opts ...http.CallOption) (rsp *PayoutCheckReply, err error) + SayHello(ctx context.Context, req *HelloRequest, opts ...http.CallOption) (rsp *HelloReply, err error) + SetPayoutStatus(ctx context.Context, req *PayoutStatusReq, opts ...http.CallOption) (rsp *PayoutStatusReply, err error) + SubmitCheck(ctx context.Context, req *SubmitCheckReq, opts ...http.CallOption) (rsp *SubmitCheckReply, err error) +} + +type EonlineHTTPClientImpl struct { + cc *http.Client +} + +func NewEonlineHTTPClient(client *http.Client) EonlineHTTPClient { + return &EonlineHTTPClientImpl{client} +} + +func (c *EonlineHTTPClientImpl) AddChat(ctx context.Context, in *AddChatReq, opts ...http.CallOption) (*AddChatReply, error) { + var out AddChatReply + pattern := "/eonline4/addchat" + path := binding.EncodeURL(pattern, in, false) + opts = append(opts, http.Operation(OperationEonlineAddChat)) + opts = append(opts, http.PathTemplate(pattern)) + err := c.cc.Invoke(ctx, "POST", path, in, &out, opts...) + if err != nil { + return nil, err + } + return &out, err +} + +func (c *EonlineHTTPClientImpl) CheckInfo(ctx context.Context, in *CheckInfoReq, opts ...http.CallOption) (*CheckInfoReply, error) { + var out CheckInfoReply + pattern := "/eonline4/checkinfo" + path := binding.EncodeURL(pattern, in, false) + opts = append(opts, http.Operation(OperationEonlineCheckInfo)) + opts = append(opts, http.PathTemplate(pattern)) + err := c.cc.Invoke(ctx, "POST", path, in, &out, opts...) + if err != nil { + return nil, err + } + return &out, err +} + +func (c *EonlineHTTPClientImpl) GetChat(ctx context.Context, in *GetChatReq, opts ...http.CallOption) (*GetChatReply, error) { + var out GetChatReply + pattern := "/eonline4/getchat" + path := binding.EncodeURL(pattern, in, false) + opts = append(opts, http.Operation(OperationEonlineGetChat)) + opts = append(opts, http.PathTemplate(pattern)) + err := c.cc.Invoke(ctx, "POST", path, in, &out, opts...) + if err != nil { + return nil, err + } + return &out, err +} + +func (c *EonlineHTTPClientImpl) GetPayoutUserLst(ctx context.Context, in *PayoutUserLstReq, opts ...http.CallOption) (*PayoutUserLstReply, error) { + var out PayoutUserLstReply + pattern := "/eonline4/getPayoutUserLst" + path := binding.EncodeURL(pattern, in, false) + opts = append(opts, http.Operation(OperationEonlineGetPayoutUserLst)) + opts = append(opts, http.PathTemplate(pattern)) + err := c.cc.Invoke(ctx, "POST", path, in, &out, opts...) + if err != nil { + return nil, err + } + return &out, err +} + +func (c *EonlineHTTPClientImpl) PayInit(ctx context.Context, in *PayInitReq, opts ...http.CallOption) (*PayInitReply, error) { + var out PayInitReply + pattern := "/eonline4/pay/init" + path := binding.EncodeURL(pattern, in, false) + opts = append(opts, http.Operation(OperationEonlinePayInit)) + opts = append(opts, http.PathTemplate(pattern)) + err := c.cc.Invoke(ctx, "POST", path, in, &out, opts...) + if err != nil { + return nil, err + } + return &out, err +} + +func (c *EonlineHTTPClientImpl) Payout(ctx context.Context, in *PayoutReq, opts ...http.CallOption) (*PayoutReply, error) { + var out PayoutReply + pattern := "/eonline4/payout" + path := binding.EncodeURL(pattern, in, false) + opts = append(opts, http.Operation(OperationEonlinePayout)) + opts = append(opts, http.PathTemplate(pattern)) + err := c.cc.Invoke(ctx, "POST", path, in, &out, opts...) + if err != nil { + return nil, err + } + return &out, err +} + +func (c *EonlineHTTPClientImpl) PayoutBrazil(ctx context.Context, in *PayoutReq, opts ...http.CallOption) (*PayoutReply, error) { + var out PayoutReply + pattern := "/eonline4/payoutBrazil" + path := binding.EncodeURL(pattern, in, false) + opts = append(opts, http.Operation(OperationEonlinePayoutBrazil)) + opts = append(opts, http.PathTemplate(pattern)) + err := c.cc.Invoke(ctx, "POST", path, in, &out, opts...) + if err != nil { + return nil, err + } + return &out, err +} + +func (c *EonlineHTTPClientImpl) PayoutCallback(ctx context.Context, in *PayoutCallbackReq, opts ...http.CallOption) (*PayoutCallbackReply, error) { + var out PayoutCallbackReply + pattern := "/eonline4/payout/callback" + path := binding.EncodeURL(pattern, in, false) + opts = append(opts, http.Operation(OperationEonlinePayoutCallback)) + opts = append(opts, http.PathTemplate(pattern)) + err := c.cc.Invoke(ctx, "POST", path, in, &out, opts...) + if err != nil { + return nil, err + } + return &out, err +} + +func (c *EonlineHTTPClientImpl) PayoutCheck(ctx context.Context, in *PayoutCheckReq, opts ...http.CallOption) (*PayoutCheckReply, error) { + var out PayoutCheckReply + pattern := "/eonline4/payout/check" + path := binding.EncodeURL(pattern, in, false) + opts = append(opts, http.Operation(OperationEonlinePayoutCheck)) + opts = append(opts, http.PathTemplate(pattern)) + err := c.cc.Invoke(ctx, "POST", path, in, &out, opts...) + if err != nil { + return nil, err + } + return &out, err +} + +func (c *EonlineHTTPClientImpl) SayHello(ctx context.Context, in *HelloRequest, opts ...http.CallOption) (*HelloReply, error) { + var out HelloReply + pattern := "/eonline4/{name}" + path := binding.EncodeURL(pattern, in, true) + opts = append(opts, http.Operation(OperationEonlineSayHello)) + opts = append(opts, http.PathTemplate(pattern)) + err := c.cc.Invoke(ctx, "GET", path, nil, &out, opts...) + if err != nil { + return nil, err + } + return &out, err +} + +func (c *EonlineHTTPClientImpl) SetPayoutStatus(ctx context.Context, in *PayoutStatusReq, opts ...http.CallOption) (*PayoutStatusReply, error) { + var out PayoutStatusReply + pattern := "/eonline4/setPayoutStatus" + path := binding.EncodeURL(pattern, in, false) + opts = append(opts, http.Operation(OperationEonlineSetPayoutStatus)) + opts = append(opts, http.PathTemplate(pattern)) + err := c.cc.Invoke(ctx, "POST", path, in, &out, opts...) + if err != nil { + return nil, err + } + return &out, err +} + +func (c *EonlineHTTPClientImpl) SubmitCheck(ctx context.Context, in *SubmitCheckReq, opts ...http.CallOption) (*SubmitCheckReply, error) { + var out SubmitCheckReply + pattern := "/eonline4/submitcheck" + path := binding.EncodeURL(pattern, in, false) + opts = append(opts, http.Operation(OperationEonlineSubmitCheck)) + opts = append(opts, http.PathTemplate(pattern)) + err := c.cc.Invoke(ctx, "POST", path, in, &out, opts...) + if err != nil { + return nil, err + } + return &out, err +} diff --git a/api/eonline/v1/pagsmile.pb.go b/api/eonline/v1/pagsmile.pb.go new file mode 100644 index 0000000..2e5191a --- /dev/null +++ b/api/eonline/v1/pagsmile.pb.go @@ -0,0 +1,3641 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v3.20.3 +// source: api/eonline/v1/pagsmile.proto + +package v1 + +import ( + _ "github.com/envoyproxy/protoc-gen-validate/validate" + 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) +) + +// PayInitReq init request +type PayInitReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Platform string `protobuf:"bytes,1,opt,name=platform,proto3" json:"platform,omitempty"` + Deviceid string `protobuf:"bytes,2,opt,name=deviceid,proto3" json:"deviceid,omitempty"` + Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` // 版本号为三段式,目前起始版本号为2.0.0,后面2段每段最大长度为2位,即后面2段最大为99.99 + Ip string `protobuf:"bytes,4,opt,name=ip,proto3" json:"ip,omitempty"` // 测试时用,可以不传 + Ts string `protobuf:"bytes,5,opt,name=ts,proto3" json:"ts,omitempty"` // utc时间秒,客户端发送消息时的本地时间;在测试模式下,服务器会把ts当成登录时间计算,可用于改变登录日期 + Sign string `protobuf:"bytes,6,opt,name=sign,proto3" json:"sign,omitempty"` +} + +func (x *PayInitReq) Reset() { + *x = PayInitReq{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PayInitReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PayInitReq) ProtoMessage() {} + +func (x *PayInitReq) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_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 PayInitReq.ProtoReflect.Descriptor instead. +func (*PayInitReq) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{0} +} + +func (x *PayInitReq) GetPlatform() string { + if x != nil { + return x.Platform + } + return "" +} + +func (x *PayInitReq) GetDeviceid() string { + if x != nil { + return x.Deviceid + } + return "" +} + +func (x *PayInitReq) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *PayInitReq) GetIp() string { + if x != nil { + return x.Ip + } + return "" +} + +func (x *PayInitReq) GetTs() string { + if x != nil { + return x.Ts + } + return "" +} + +func (x *PayInitReq) GetSign() string { + if x != nil { + return x.Sign + } + return "" +} + +// PayInitReply init reply +type PayInitReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Uuid string `protobuf:"bytes,1,opt,name=uuid,proto3" json:"uuid,omitempty"` // 用户唯一字符串 + Days uint32 `protobuf:"varint,2,opt,name=days,proto3" json:"days,omitempty"` // 新增,登录天数,从1开始 + Items []*PayInitReply_Item `protobuf:"bytes,3,rep,name=items,proto3" json:"items,omitempty"` // 提现情况 + CanCheckSubmit int32 `protobuf:"varint,4,opt,name=CanCheckSubmit,proto3" json:"CanCheckSubmit,omitempty"` // 0提交审核的人数已满,不能提交了,1可以提交身份审核 + CheckSubmit int32 `protobuf:"varint,5,opt,name=CheckSubmit,proto3" json:"CheckSubmit,omitempty"` // 提交身份文件验证情况,0没有提交,1提交过 + CheckResult int32 `protobuf:"varint,6,opt,name=CheckResult,proto3" json:"CheckResult,omitempty"` // 身份文件审核有反馈的情况,0没有记录,1审核没通过,2审核通过 + CheckPayout int32 `protobuf:"varint,7,opt,name=CheckPayout,proto3" json:"CheckPayout,omitempty"` // 提交身份文件奖励5美元领取的情况,0没有提现记录,1提现中,2提现成功,3提现失败 + CheckCoin int32 `protobuf:"varint,8,opt,name=CheckCoin,proto3" json:"CheckCoin,omitempty"` // 身份文件审核过的提现奖励,美分 + CanCheckPayOut int32 `protobuf:"varint,9,opt,name=CanCheckPayOut,proto3" json:"CanCheckPayOut,omitempty"` // 身份文件审核过的提现奖励5美元 能否 提现,0能提现,1条件不满足,不能提现,2当天已经提现过,不能再次提现 + CheckResultFailedDesc string `protobuf:"bytes,10,opt,name=CheckResultFailedDesc,proto3" json:"CheckResultFailedDesc,omitempty"` // CheckResult==1时,显示的审核没通过的原因描述信息 + Error int32 `protobuf:"varint,11,opt,name=error,proto3" json:"error,omitempty"` // 错误码,0成功,1失败,2签名验证失败,3客户端版本过低,4 ts长度错误 + ClientData string `protobuf:"bytes,12,opt,name=clientData,proto3" json:"clientData,omitempty"` // 客户端上传、需要保存的数据 +} + +func (x *PayInitReply) Reset() { + *x = PayInitReply{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PayInitReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PayInitReply) ProtoMessage() {} + +func (x *PayInitReply) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_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 PayInitReply.ProtoReflect.Descriptor instead. +func (*PayInitReply) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{1} +} + +func (x *PayInitReply) GetUuid() string { + if x != nil { + return x.Uuid + } + return "" +} + +func (x *PayInitReply) GetDays() uint32 { + if x != nil { + return x.Days + } + return 0 +} + +func (x *PayInitReply) GetItems() []*PayInitReply_Item { + if x != nil { + return x.Items + } + return nil +} + +func (x *PayInitReply) GetCanCheckSubmit() int32 { + if x != nil { + return x.CanCheckSubmit + } + return 0 +} + +func (x *PayInitReply) GetCheckSubmit() int32 { + if x != nil { + return x.CheckSubmit + } + return 0 +} + +func (x *PayInitReply) GetCheckResult() int32 { + if x != nil { + return x.CheckResult + } + return 0 +} + +func (x *PayInitReply) GetCheckPayout() int32 { + if x != nil { + return x.CheckPayout + } + return 0 +} + +func (x *PayInitReply) GetCheckCoin() int32 { + if x != nil { + return x.CheckCoin + } + return 0 +} + +func (x *PayInitReply) GetCanCheckPayOut() int32 { + if x != nil { + return x.CanCheckPayOut + } + return 0 +} + +func (x *PayInitReply) GetCheckResultFailedDesc() string { + if x != nil { + return x.CheckResultFailedDesc + } + return "" +} + +func (x *PayInitReply) GetError() int32 { + if x != nil { + return x.Error + } + return 0 +} + +func (x *PayInitReply) GetClientData() string { + if x != nil { + return x.ClientData + } + return "" +} + +type PbReportDataAdjust struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + GpsAdid string `protobuf:"bytes,1,opt,name=gps_adid,json=gpsAdid,proto3" json:"gps_adid,omitempty"` // 用户的gaid + AndroidId string `protobuf:"bytes,2,opt,name=android_id,json=androidId,proto3" json:"android_id,omitempty"` // 原始安卓 ID + Adid string `protobuf:"bytes,3,opt,name=adid,proto3" json:"adid,omitempty"` // 与设备关联的 Adjust 标识符 + UserAgent string `protobuf:"bytes,4,opt,name=user_agent,json=userAgent,proto3" json:"user_agent,omitempty"` // 设备的User-Agent。必须进行 URL 编码。 + Price string `protobuf:"bytes,5,opt,name=price,proto3" json:"price,omitempty"` // 客户端上报的价格 客户端上报的价格,例如0.05 + Currency string `protobuf:"bytes,6,opt,name=currency,proto3" json:"currency,omitempty"` // 货币单位 客户端上报的货币,例如USD +} + +func (x *PbReportDataAdjust) Reset() { + *x = PbReportDataAdjust{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PbReportDataAdjust) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PbReportDataAdjust) ProtoMessage() {} + +func (x *PbReportDataAdjust) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_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 PbReportDataAdjust.ProtoReflect.Descriptor instead. +func (*PbReportDataAdjust) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{2} +} + +func (x *PbReportDataAdjust) GetGpsAdid() string { + if x != nil { + return x.GpsAdid + } + return "" +} + +func (x *PbReportDataAdjust) GetAndroidId() string { + if x != nil { + return x.AndroidId + } + return "" +} + +func (x *PbReportDataAdjust) GetAdid() string { + if x != nil { + return x.Adid + } + return "" +} + +func (x *PbReportDataAdjust) GetUserAgent() string { + if x != nil { + return x.UserAgent + } + return "" +} + +func (x *PbReportDataAdjust) GetPrice() string { + if x != nil { + return x.Price + } + return "" +} + +func (x *PbReportDataAdjust) GetCurrency() string { + if x != nil { + return x.Currency + } + return "" +} + +type PbReportDataShuShu struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + GpsGaid string `protobuf:"bytes,1,opt,name=gps_gaid,json=gpsGaid,proto3" json:"gps_gaid,omitempty"` // 用户的gaid + AndroidId string `protobuf:"bytes,2,opt,name=android_id,json=androidId,proto3" json:"android_id,omitempty"` // 原始安卓 ID + Adid string `protobuf:"bytes,3,opt,name=adid,proto3" json:"adid,omitempty"` // 与设备关联的 Adjust 标识符 + UserAgent string `protobuf:"bytes,4,opt,name=user_agent,json=userAgent,proto3" json:"user_agent,omitempty"` // 设备的User-Agent。必须进行 URL 编码 + Price string `protobuf:"bytes,5,opt,name=price,proto3" json:"price,omitempty"` // 客户端上报的价格 客户端上报的价格,例如0.05 + Currency string `protobuf:"bytes,6,opt,name=currency,proto3" json:"currency,omitempty"` // 货币单位 客户端上报的货币,例如USD + PaymentMethod string `protobuf:"bytes,7,opt,name=payment_method,json=paymentMethod,proto3" json:"payment_method,omitempty"` // 收款方式 暂时只有一种:pix + PaymentType string `protobuf:"bytes,8,opt,name=payment_type,json=paymentType,proto3" json:"payment_type,omitempty"` // 账户形式 cpf/cnpj/evp/email/phone + PaymentNumber string `protobuf:"bytes,9,opt,name=payment_number,json=paymentNumber,proto3" json:"payment_number,omitempty"` // 账户号码 收款账号号码 + IapName string `protobuf:"bytes,10,opt,name=iap_name,json=iapName,proto3" json:"iap_name,omitempty"` // 商品名称 游戏侧自定义的提现项目名称,例如:0.1br/50br/100br + GamecoinNumber string `protobuf:"bytes,11,opt,name=gamecoin_number,json=gamecoinNumber,proto3" json:"gamecoin_number,omitempty"` // 提现消耗的虚拟货币数 提现消耗的虚拟货币数量,例如:1500 + GamecoinType string `protobuf:"bytes,12,opt,name=gamecoin_type,json=gamecoinType,proto3" json:"gamecoin_type,omitempty"` // 提现消耗的虚拟货币类型 金币或钞票,例如:coin/money + SsAccountId string `protobuf:"bytes,13,opt,name=ss_account_id,json=ssAccountId,proto3" json:"ss_account_id,omitempty"` // 数数账号ID 用户的登录ID(如果需要接入数数请务必传此值) + SsDistinctId string `protobuf:"bytes,14,opt,name=ss_distinct_id,json=ssDistinctId,proto3" json:"ss_distinct_id,omitempty"` // 数数访客ID 用户在未登录状态下的ID(如果需要接入数数请务必传此值) + SsSuperProperties string `protobuf:"bytes,15,opt,name=ss_super_properties,json=ssSuperProperties,proto3" json:"ss_super_properties,omitempty"` // 数数的公共属性和预制属性 json字符串 +} + +func (x *PbReportDataShuShu) Reset() { + *x = PbReportDataShuShu{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PbReportDataShuShu) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PbReportDataShuShu) ProtoMessage() {} + +func (x *PbReportDataShuShu) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[3] + 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 PbReportDataShuShu.ProtoReflect.Descriptor instead. +func (*PbReportDataShuShu) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{3} +} + +func (x *PbReportDataShuShu) GetGpsGaid() string { + if x != nil { + return x.GpsGaid + } + return "" +} + +func (x *PbReportDataShuShu) GetAndroidId() string { + if x != nil { + return x.AndroidId + } + return "" +} + +func (x *PbReportDataShuShu) GetAdid() string { + if x != nil { + return x.Adid + } + return "" +} + +func (x *PbReportDataShuShu) GetUserAgent() string { + if x != nil { + return x.UserAgent + } + return "" +} + +func (x *PbReportDataShuShu) GetPrice() string { + if x != nil { + return x.Price + } + return "" +} + +func (x *PbReportDataShuShu) GetCurrency() string { + if x != nil { + return x.Currency + } + return "" +} + +func (x *PbReportDataShuShu) GetPaymentMethod() string { + if x != nil { + return x.PaymentMethod + } + return "" +} + +func (x *PbReportDataShuShu) GetPaymentType() string { + if x != nil { + return x.PaymentType + } + return "" +} + +func (x *PbReportDataShuShu) GetPaymentNumber() string { + if x != nil { + return x.PaymentNumber + } + return "" +} + +func (x *PbReportDataShuShu) GetIapName() string { + if x != nil { + return x.IapName + } + return "" +} + +func (x *PbReportDataShuShu) GetGamecoinNumber() string { + if x != nil { + return x.GamecoinNumber + } + return "" +} + +func (x *PbReportDataShuShu) GetGamecoinType() string { + if x != nil { + return x.GamecoinType + } + return "" +} + +func (x *PbReportDataShuShu) GetSsAccountId() string { + if x != nil { + return x.SsAccountId + } + return "" +} + +func (x *PbReportDataShuShu) GetSsDistinctId() string { + if x != nil { + return x.SsDistinctId + } + return "" +} + +func (x *PbReportDataShuShu) GetSsSuperProperties() string { + if x != nil { + return x.SsSuperProperties + } + return "" +} + +// PayoutReq 赔付请求 +type PayoutReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Platform string `protobuf:"bytes,1,opt,name=platform,proto3" json:"platform,omitempty"` + Deviceid string `protobuf:"bytes,2,opt,name=deviceid,proto3" json:"deviceid,omitempty"` + Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` + Ts string `protobuf:"bytes,4,opt,name=ts,proto3" json:"ts,omitempty"` // utc时间秒 + Sign string `protobuf:"bytes,5,opt,name=sign,proto3" json:"sign,omitempty"` + Account string `protobuf:"bytes,6,opt,name=account,proto3" json:"account,omitempty"` // PIX: Beneficiary's PIX account paypal账号 + ItemId uint32 `protobuf:"varint,7,opt,name=item_id,json=itemId,proto3" json:"item_id,omitempty"` // 1提现0.1,2提现金币大额1,3提现绿钞大额1,4是身份审核通过的奖励提现 + Amount float64 `protobuf:"fixed64,8,opt,name=amount,proto3" json:"amount,omitempty"` + AdditionalRemark string `protobuf:"bytes,9,opt,name=additional_remark,json=additionalRemark,proto3" json:"additional_remark,omitempty"` + Uuid string `protobuf:"bytes,10,opt,name=uuid,proto3" json:"uuid,omitempty"` + Ip string `protobuf:"bytes,11,opt,name=ip,proto3" json:"ip,omitempty"` + AccountType string `protobuf:"bytes,12,opt,name=account_type,json=accountType,proto3" json:"account_type,omitempty"` // 非巴西PIX支付不填,PIX: Beneficiary's PIX account type- One of: CPF, CNPJ, EVP, PHONE, EMAIL + DocumentType string `protobuf:"bytes,13,opt,name=document_type,json=documentType,proto3" json:"document_type,omitempty"` // 非巴西PIX支付不填,PIX: Beneficiary's personal identification type - One of: CPF, CNPJ + DocumentId string `protobuf:"bytes,14,opt,name=document_id,json=documentId,proto3" json:"document_id,omitempty"` // 非巴西PIX支付不填,PIX: Beneficiary's personal identification number + Name string `protobuf:"bytes,15,opt,name=name,proto3" json:"name,omitempty"` // 非巴西PIX支付不填,PIX: Beneficiary's name- Length between 5 and 100 + DataAdjust *PbReportDataAdjust `protobuf:"bytes,16,opt,name=dataAdjust,proto3" json:"dataAdjust,omitempty"` // 客户端上报 adjust 数据 + DataShuShu *PbReportDataShuShu `protobuf:"bytes,17,opt,name=dataShuShu,proto3" json:"dataShuShu,omitempty"` // 客户端上报 数数 数据 + ClientData string `protobuf:"bytes,18,opt,name=clientData,proto3" json:"clientData,omitempty"` // 客户端上传、需要保存的数据 + ClientName string `protobuf:"bytes,19,opt,name=clientName,proto3" json:"clientName,omitempty"` // 客户端包名 + Email string `protobuf:"bytes,20,opt,name=email,proto3" json:"email,omitempty"` // 邮箱 +} + +func (x *PayoutReq) Reset() { + *x = PayoutReq{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PayoutReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PayoutReq) ProtoMessage() {} + +func (x *PayoutReq) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[4] + 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 PayoutReq.ProtoReflect.Descriptor instead. +func (*PayoutReq) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{4} +} + +func (x *PayoutReq) GetPlatform() string { + if x != nil { + return x.Platform + } + return "" +} + +func (x *PayoutReq) GetDeviceid() string { + if x != nil { + return x.Deviceid + } + return "" +} + +func (x *PayoutReq) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *PayoutReq) GetTs() string { + if x != nil { + return x.Ts + } + return "" +} + +func (x *PayoutReq) GetSign() string { + if x != nil { + return x.Sign + } + return "" +} + +func (x *PayoutReq) GetAccount() string { + if x != nil { + return x.Account + } + return "" +} + +func (x *PayoutReq) GetItemId() uint32 { + if x != nil { + return x.ItemId + } + return 0 +} + +func (x *PayoutReq) GetAmount() float64 { + if x != nil { + return x.Amount + } + return 0 +} + +func (x *PayoutReq) GetAdditionalRemark() string { + if x != nil { + return x.AdditionalRemark + } + return "" +} + +func (x *PayoutReq) GetUuid() string { + if x != nil { + return x.Uuid + } + return "" +} + +func (x *PayoutReq) GetIp() string { + if x != nil { + return x.Ip + } + return "" +} + +func (x *PayoutReq) GetAccountType() string { + if x != nil { + return x.AccountType + } + return "" +} + +func (x *PayoutReq) GetDocumentType() string { + if x != nil { + return x.DocumentType + } + return "" +} + +func (x *PayoutReq) GetDocumentId() string { + if x != nil { + return x.DocumentId + } + return "" +} + +func (x *PayoutReq) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *PayoutReq) GetDataAdjust() *PbReportDataAdjust { + if x != nil { + return x.DataAdjust + } + return nil +} + +func (x *PayoutReq) GetDataShuShu() *PbReportDataShuShu { + if x != nil { + return x.DataShuShu + } + return nil +} + +func (x *PayoutReq) GetClientData() string { + if x != nil { + return x.ClientData + } + return "" +} + +func (x *PayoutReq) GetClientName() string { + if x != nil { + return x.ClientName + } + return "" +} + +func (x *PayoutReq) GetEmail() string { + if x != nil { + return x.Email + } + return "" +} + +// PayoutReply 赔付响应 +type PayoutReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + RecordNo string `protobuf:"bytes,2,opt,name=record_no,json=recordNo,proto3" json:"record_no,omitempty"` + Error int32 `protobuf:"varint,3,opt,name=error,proto3" json:"error,omitempty"` // 错误码,0成功,1失败,2签名验证失败,3客户端版本过低,4uuid错误,5所在地国家或地区不在提现限制内,6提现金额不符对应的产品id,7提现产品id不对,8达到提现金额限制,9提现次数超过限制,10今日没有提现机会,11提现账号达到次数限制,12身份审核条件不满足,不能提现,13巴西提现参数 document_type 错误, +} + +func (x *PayoutReply) Reset() { + *x = PayoutReply{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PayoutReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PayoutReply) ProtoMessage() {} + +func (x *PayoutReply) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[5] + 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 PayoutReply.ProtoReflect.Descriptor instead. +func (*PayoutReply) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{5} +} + +func (x *PayoutReply) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *PayoutReply) GetRecordNo() string { + if x != nil { + return x.RecordNo + } + return "" +} + +func (x *PayoutReply) GetError() int32 { + if x != nil { + return x.Error + } + return 0 +} + +// PayoutCallbackReq 赔付回调请求 +type PayoutCallbackReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PayoutId string `protobuf:"bytes,1,opt,name=payout_id,json=payoutId,proto3" json:"payout_id,omitempty"` + CustomCode string `protobuf:"bytes,2,opt,name=custom_code,json=customCode,proto3" json:"custom_code,omitempty"` + Status string `protobuf:"bytes,3,opt,name=status,proto3" json:"status,omitempty"` + Msg string `protobuf:"bytes,4,opt,name=msg,proto3" json:"msg,omitempty"` + Timestamp int64 `protobuf:"varint,5,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (x *PayoutCallbackReq) Reset() { + *x = PayoutCallbackReq{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PayoutCallbackReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PayoutCallbackReq) ProtoMessage() {} + +func (x *PayoutCallbackReq) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[6] + 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 PayoutCallbackReq.ProtoReflect.Descriptor instead. +func (*PayoutCallbackReq) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{6} +} + +func (x *PayoutCallbackReq) GetPayoutId() string { + if x != nil { + return x.PayoutId + } + return "" +} + +func (x *PayoutCallbackReq) GetCustomCode() string { + if x != nil { + return x.CustomCode + } + return "" +} + +func (x *PayoutCallbackReq) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *PayoutCallbackReq) GetMsg() string { + if x != nil { + return x.Msg + } + return "" +} + +func (x *PayoutCallbackReq) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +// PayoutCallbackReply 赔付回调响应 +type PayoutCallbackReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *PayoutCallbackReply) Reset() { + *x = PayoutCallbackReply{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PayoutCallbackReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PayoutCallbackReply) ProtoMessage() {} + +func (x *PayoutCallbackReply) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[7] + 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 PayoutCallbackReply.ProtoReflect.Descriptor instead. +func (*PayoutCallbackReply) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{7} +} + +func (x *PayoutCallbackReply) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +// PayoutCheckReq 赔付查询请求 +type PayoutCheckReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Platform string `protobuf:"bytes,1,opt,name=platform,proto3" json:"platform,omitempty"` + Deviceid string `protobuf:"bytes,2,opt,name=deviceid,proto3" json:"deviceid,omitempty"` + Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` + Ts string `protobuf:"bytes,4,opt,name=ts,proto3" json:"ts,omitempty"` // utc时间秒 + Sign string `protobuf:"bytes,5,opt,name=sign,proto3" json:"sign,omitempty"` + Ip string `protobuf:"bytes,6,opt,name=ip,proto3" json:"ip,omitempty"` + RecordNo string `protobuf:"bytes,7,opt,name=record_no,json=recordNo,proto3" json:"record_no,omitempty"` +} + +func (x *PayoutCheckReq) Reset() { + *x = PayoutCheckReq{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PayoutCheckReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PayoutCheckReq) ProtoMessage() {} + +func (x *PayoutCheckReq) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[8] + 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 PayoutCheckReq.ProtoReflect.Descriptor instead. +func (*PayoutCheckReq) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{8} +} + +func (x *PayoutCheckReq) GetPlatform() string { + if x != nil { + return x.Platform + } + return "" +} + +func (x *PayoutCheckReq) GetDeviceid() string { + if x != nil { + return x.Deviceid + } + return "" +} + +func (x *PayoutCheckReq) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *PayoutCheckReq) GetTs() string { + if x != nil { + return x.Ts + } + return "" +} + +func (x *PayoutCheckReq) GetSign() string { + if x != nil { + return x.Sign + } + return "" +} + +func (x *PayoutCheckReq) GetIp() string { + if x != nil { + return x.Ip + } + return "" +} + +func (x *PayoutCheckReq) GetRecordNo() string { + if x != nil { + return x.RecordNo + } + return "" +} + +// PayoutCheckReply 赔付查询响应 +type PayoutCheckReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Status uint32 `protobuf:"varint,1,opt,name=status,proto3" json:"status,omitempty"` // 提现状态 1:提现中,2:提现成功,3:提现失败 + Error int32 `protobuf:"varint,2,opt,name=error,proto3" json:"error,omitempty"` // 错误码,0成功,1失败,2签名验证失败,3客户端版本过低,4 ts长度错误 +} + +func (x *PayoutCheckReply) Reset() { + *x = PayoutCheckReply{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PayoutCheckReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PayoutCheckReply) ProtoMessage() {} + +func (x *PayoutCheckReply) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[9] + 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 PayoutCheckReply.ProtoReflect.Descriptor instead. +func (*PayoutCheckReply) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{9} +} + +func (x *PayoutCheckReply) GetStatus() uint32 { + if x != nil { + return x.Status + } + return 0 +} + +func (x *PayoutCheckReply) GetError() int32 { + if x != nil { + return x.Error + } + return 0 +} + +// GetPayoutUserLst 查询提现邮箱请求 +type PayoutUserLstReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Platform string `protobuf:"bytes,1,opt,name=platform,proto3" json:"platform,omitempty"` + Deviceid string `protobuf:"bytes,2,opt,name=deviceid,proto3" json:"deviceid,omitempty"` + Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` + Ts string `protobuf:"bytes,4,opt,name=ts,proto3" json:"ts,omitempty"` // utc时间秒 + Sign string `protobuf:"bytes,5,opt,name=sign,proto3" json:"sign,omitempty"` // 签名 + Status uint32 `protobuf:"varint,6,opt,name=status,proto3" json:"status,omitempty"` // 查询提现请求的状态, 提现状态 1:提现中,2:提现成功,3:提现失败 + PageIndex uint32 `protobuf:"varint,7,opt,name=pageIndex,proto3" json:"pageIndex,omitempty"` // 查询页第几页,从1开始 + PageSize uint32 `protobuf:"varint,8,opt,name=pageSize,proto3" json:"pageSize,omitempty"` // 每页多少条记录 +} + +func (x *PayoutUserLstReq) Reset() { + *x = PayoutUserLstReq{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PayoutUserLstReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PayoutUserLstReq) ProtoMessage() {} + +func (x *PayoutUserLstReq) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[10] + 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 PayoutUserLstReq.ProtoReflect.Descriptor instead. +func (*PayoutUserLstReq) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{10} +} + +func (x *PayoutUserLstReq) GetPlatform() string { + if x != nil { + return x.Platform + } + return "" +} + +func (x *PayoutUserLstReq) GetDeviceid() string { + if x != nil { + return x.Deviceid + } + return "" +} + +func (x *PayoutUserLstReq) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *PayoutUserLstReq) GetTs() string { + if x != nil { + return x.Ts + } + return "" +} + +func (x *PayoutUserLstReq) GetSign() string { + if x != nil { + return x.Sign + } + return "" +} + +func (x *PayoutUserLstReq) GetStatus() uint32 { + if x != nil { + return x.Status + } + return 0 +} + +func (x *PayoutUserLstReq) GetPageIndex() uint32 { + if x != nil { + return x.PageIndex + } + return 0 +} + +func (x *PayoutUserLstReq) GetPageSize() uint32 { + if x != nil { + return x.PageSize + } + return 0 +} + +// GetPayoutUserLst 获取申请提现玩家的列表请求响应 +type PayoutUserLstReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Lst []*PayoutUserOne `protobuf:"bytes,1,rep,name=lst,proto3" json:"lst,omitempty"` // 只返回 提现中的 + Error int32 `protobuf:"varint,2,opt,name=error,proto3" json:"error,omitempty"` // 错误码,0成功,1失败,2签名验证失败,3访问数据库出错,4 ts长度错误 +} + +func (x *PayoutUserLstReply) Reset() { + *x = PayoutUserLstReply{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PayoutUserLstReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PayoutUserLstReply) ProtoMessage() {} + +func (x *PayoutUserLstReply) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[11] + 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 PayoutUserLstReply.ProtoReflect.Descriptor instead. +func (*PayoutUserLstReply) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{11} +} + +func (x *PayoutUserLstReply) GetLst() []*PayoutUserOne { + if x != nil { + return x.Lst + } + return nil +} + +func (x *PayoutUserLstReply) GetError() int32 { + if x != nil { + return x.Error + } + return 0 +} + +type PayoutUserOne struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"` // 邮件地址 + RecordNo string `protobuf:"bytes,2,opt,name=recordNo,proto3" json:"recordNo,omitempty"` // 提现唯一编码 + Account string `protobuf:"bytes,3,opt,name=account,proto3" json:"account,omitempty"` // paypal账号 + Status uint32 `protobuf:"varint,4,opt,name=status,proto3" json:"status,omitempty"` // 提现状态 1:提现中,2:提现成功,3:提现失败 +} + +func (x *PayoutUserOne) Reset() { + *x = PayoutUserOne{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PayoutUserOne) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PayoutUserOne) ProtoMessage() {} + +func (x *PayoutUserOne) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[12] + 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 PayoutUserOne.ProtoReflect.Descriptor instead. +func (*PayoutUserOne) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{12} +} + +func (x *PayoutUserOne) GetEmail() string { + if x != nil { + return x.Email + } + return "" +} + +func (x *PayoutUserOne) GetRecordNo() string { + if x != nil { + return x.RecordNo + } + return "" +} + +func (x *PayoutUserOne) GetAccount() string { + if x != nil { + return x.Account + } + return "" +} + +func (x *PayoutUserOne) GetStatus() uint32 { + if x != nil { + return x.Status + } + return 0 +} + +// SetPayoutStatus 设置指定玩家的提现状态 +type PayoutStatusReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Platform string `protobuf:"bytes,1,opt,name=platform,proto3" json:"platform,omitempty"` + Deviceid string `protobuf:"bytes,2,opt,name=deviceid,proto3" json:"deviceid,omitempty"` + Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` + Ts string `protobuf:"bytes,4,opt,name=ts,proto3" json:"ts,omitempty"` // utc时间秒 + Sign string `protobuf:"bytes,5,opt,name=sign,proto3" json:"sign,omitempty"` // 签名 + RecordNo string `protobuf:"bytes,6,opt,name=recordNo,proto3" json:"recordNo,omitempty"` // 提现唯一编码,值来自 PayoutUserOne 的 recordNo + Fail string `protobuf:"bytes,7,opt,name=fail,proto3" json:"fail,omitempty"` // 设置拒绝原因 + Status uint32 `protobuf:"varint,8,opt,name=status,proto3" json:"status,omitempty"` // 设置提现状态 2:提现成功,3:提现失败 +} + +func (x *PayoutStatusReq) Reset() { + *x = PayoutStatusReq{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PayoutStatusReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PayoutStatusReq) ProtoMessage() {} + +func (x *PayoutStatusReq) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[13] + 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 PayoutStatusReq.ProtoReflect.Descriptor instead. +func (*PayoutStatusReq) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{13} +} + +func (x *PayoutStatusReq) GetPlatform() string { + if x != nil { + return x.Platform + } + return "" +} + +func (x *PayoutStatusReq) GetDeviceid() string { + if x != nil { + return x.Deviceid + } + return "" +} + +func (x *PayoutStatusReq) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *PayoutStatusReq) GetTs() string { + if x != nil { + return x.Ts + } + return "" +} + +func (x *PayoutStatusReq) GetSign() string { + if x != nil { + return x.Sign + } + return "" +} + +func (x *PayoutStatusReq) GetRecordNo() string { + if x != nil { + return x.RecordNo + } + return "" +} + +func (x *PayoutStatusReq) GetFail() string { + if x != nil { + return x.Fail + } + return "" +} + +func (x *PayoutStatusReq) GetStatus() uint32 { + if x != nil { + return x.Status + } + return 0 +} + +type PayoutStatusReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RecordNo string `protobuf:"bytes,1,opt,name=recordNo,proto3" json:"recordNo,omitempty"` // 提现唯一编码,值来自 PayoutUserOne 的 recordNo + Status uint32 `protobuf:"varint,2,opt,name=status,proto3" json:"status,omitempty"` + Error uint32 `protobuf:"varint,3,opt,name=error,proto3" json:"error,omitempty"` // 错误码,0成功,1失败,2签名验证失败,3访问数据库出错,4 ts长度错误,5 status值错误 +} + +func (x *PayoutStatusReply) Reset() { + *x = PayoutStatusReply{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PayoutStatusReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PayoutStatusReply) ProtoMessage() {} + +func (x *PayoutStatusReply) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[14] + 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 PayoutStatusReply.ProtoReflect.Descriptor instead. +func (*PayoutStatusReply) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{14} +} + +func (x *PayoutStatusReply) GetRecordNo() string { + if x != nil { + return x.RecordNo + } + return "" +} + +func (x *PayoutStatusReply) GetStatus() uint32 { + if x != nil { + return x.Status + } + return 0 +} + +func (x *PayoutStatusReply) GetError() uint32 { + if x != nil { + return x.Error + } + return 0 +} + +// 提交身份文件验证请求 +type SubmitCheckReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Account string `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` // paypal账号 + Uuid string `protobuf:"bytes,2,opt,name=uuid,proto3" json:"uuid,omitempty"` +} + +func (x *SubmitCheckReq) Reset() { + *x = SubmitCheckReq{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubmitCheckReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubmitCheckReq) ProtoMessage() {} + +func (x *SubmitCheckReq) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[15] + 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 SubmitCheckReq.ProtoReflect.Descriptor instead. +func (*SubmitCheckReq) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{15} +} + +func (x *SubmitCheckReq) GetAccount() string { + if x != nil { + return x.Account + } + return "" +} + +func (x *SubmitCheckReq) GetUuid() string { + if x != nil { + return x.Uuid + } + return "" +} + +type SubmitCheckReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Result int32 `protobuf:"varint,1,opt,name=result,proto3" json:"result,omitempty"` // 0成功,1失败,2以前提交过,还没审核结果,3以前提交过,并审核通过(以前提交过,但审核失败的,可以继续提交), + Error int32 `protobuf:"varint,2,opt,name=error,proto3" json:"error,omitempty"` // 错误码,0成功,1失败, +} + +func (x *SubmitCheckReply) Reset() { + *x = SubmitCheckReply{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubmitCheckReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubmitCheckReply) ProtoMessage() {} + +func (x *SubmitCheckReply) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[16] + 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 SubmitCheckReply.ProtoReflect.Descriptor instead. +func (*SubmitCheckReply) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{16} +} + +func (x *SubmitCheckReply) GetResult() int32 { + if x != nil { + return x.Result + } + return 0 +} + +func (x *SubmitCheckReply) GetError() int32 { + if x != nil { + return x.Error + } + return 0 +} + +// 身份审核信息请求 +type CheckInfoReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Platform string `protobuf:"bytes,1,opt,name=platform,proto3" json:"platform,omitempty"` + Deviceid string `protobuf:"bytes,2,opt,name=deviceid,proto3" json:"deviceid,omitempty"` + Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` + Ip string `protobuf:"bytes,4,opt,name=ip,proto3" json:"ip,omitempty"` + Ts string `protobuf:"bytes,5,opt,name=ts,proto3" json:"ts,omitempty"` // utc时间秒 + Sign string `protobuf:"bytes,6,opt,name=sign,proto3" json:"sign,omitempty"` + Uuid string `protobuf:"bytes,7,opt,name=uuid,proto3" json:"uuid,omitempty"` // 用户唯一字符串 + IsVerificationShow int32 `protobuf:"varint,8,opt,name=isVerificationShow,proto3" json:"isVerificationShow,omitempty"` // 1开,0关 +} + +func (x *CheckInfoReq) Reset() { + *x = CheckInfoReq{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CheckInfoReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CheckInfoReq) ProtoMessage() {} + +func (x *CheckInfoReq) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[17] + 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 CheckInfoReq.ProtoReflect.Descriptor instead. +func (*CheckInfoReq) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{17} +} + +func (x *CheckInfoReq) GetPlatform() string { + if x != nil { + return x.Platform + } + return "" +} + +func (x *CheckInfoReq) GetDeviceid() string { + if x != nil { + return x.Deviceid + } + return "" +} + +func (x *CheckInfoReq) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *CheckInfoReq) GetIp() string { + if x != nil { + return x.Ip + } + return "" +} + +func (x *CheckInfoReq) GetTs() string { + if x != nil { + return x.Ts + } + return "" +} + +func (x *CheckInfoReq) GetSign() string { + if x != nil { + return x.Sign + } + return "" +} + +func (x *CheckInfoReq) GetUuid() string { + if x != nil { + return x.Uuid + } + return "" +} + +func (x *CheckInfoReq) GetIsVerificationShow() int32 { + if x != nil { + return x.IsVerificationShow + } + return 0 +} + +type CheckInfoReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + CanCheckSubmit int32 `protobuf:"varint,1,opt,name=CanCheckSubmit,proto3" json:"CanCheckSubmit,omitempty"` // 0提交审核的人数已满,不能提交了,1可以提交身份审核 + CheckSubmit int32 `protobuf:"varint,2,opt,name=CheckSubmit,proto3" json:"CheckSubmit,omitempty"` // 提交身份文件验证情况,0没有提交,1提交过 + CheckResult int32 `protobuf:"varint,3,opt,name=CheckResult,proto3" json:"CheckResult,omitempty"` // 身份文件审核有反馈的情况,0没有记录,1审核没通过,2审核通过 + CheckPayout int32 `protobuf:"varint,4,opt,name=CheckPayout,proto3" json:"CheckPayout,omitempty"` // 提交身份文件奖励5美元领取的情况,0没有提现记录,1提现中,2提现成功,3提现失败 + CheckCoin int32 `protobuf:"varint,5,opt,name=CheckCoin,proto3" json:"CheckCoin,omitempty"` // 身份文件审核过的提现奖励,美分 + CanCheckPayOut int32 `protobuf:"varint,6,opt,name=CanCheckPayOut,proto3" json:"CanCheckPayOut,omitempty"` // 身份文件审核过的提现奖励5美元 能否 提现,0能提现,1条件不满足,不能提现,2当天已经提现过,不能再次提现 + CheckResultFailedDesc string `protobuf:"bytes,7,opt,name=CheckResultFailedDesc,proto3" json:"CheckResultFailedDesc,omitempty"` // CheckResult==1时,显示的审核没通过的原因描述信息 + Error int32 `protobuf:"varint,8,opt,name=error,proto3" json:"error,omitempty"` // 错误码,0成功,1失败,2 ts长度错误, +} + +func (x *CheckInfoReply) Reset() { + *x = CheckInfoReply{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CheckInfoReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CheckInfoReply) ProtoMessage() {} + +func (x *CheckInfoReply) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[18] + 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 CheckInfoReply.ProtoReflect.Descriptor instead. +func (*CheckInfoReply) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{18} +} + +func (x *CheckInfoReply) GetCanCheckSubmit() int32 { + if x != nil { + return x.CanCheckSubmit + } + return 0 +} + +func (x *CheckInfoReply) GetCheckSubmit() int32 { + if x != nil { + return x.CheckSubmit + } + return 0 +} + +func (x *CheckInfoReply) GetCheckResult() int32 { + if x != nil { + return x.CheckResult + } + return 0 +} + +func (x *CheckInfoReply) GetCheckPayout() int32 { + if x != nil { + return x.CheckPayout + } + return 0 +} + +func (x *CheckInfoReply) GetCheckCoin() int32 { + if x != nil { + return x.CheckCoin + } + return 0 +} + +func (x *CheckInfoReply) GetCanCheckPayOut() int32 { + if x != nil { + return x.CanCheckPayOut + } + return 0 +} + +func (x *CheckInfoReply) GetCheckResultFailedDesc() string { + if x != nil { + return x.CheckResultFailedDesc + } + return "" +} + +func (x *CheckInfoReply) GetError() int32 { + if x != nil { + return x.Error + } + return 0 +} + +type PbMsgOne struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TimeStamp int64 `protobuf:"varint,1,opt,name=timeStamp,proto3" json:"timeStamp,omitempty"` // 消息时间戳utc纳秒 + Uuid string `protobuf:"bytes,2,opt,name=uuid,proto3" json:"uuid,omitempty"` // 用户唯一字符串 + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` // 用户名 + Msg string `protobuf:"bytes,4,opt,name=msg,proto3" json:"msg,omitempty"` // 聊天消息 +} + +func (x *PbMsgOne) Reset() { + *x = PbMsgOne{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PbMsgOne) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PbMsgOne) ProtoMessage() {} + +func (x *PbMsgOne) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[19] + 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 PbMsgOne.ProtoReflect.Descriptor instead. +func (*PbMsgOne) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{19} +} + +func (x *PbMsgOne) GetTimeStamp() int64 { + if x != nil { + return x.TimeStamp + } + return 0 +} + +func (x *PbMsgOne) GetUuid() string { + if x != nil { + return x.Uuid + } + return "" +} + +func (x *PbMsgOne) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *PbMsgOne) GetMsg() string { + if x != nil { + return x.Msg + } + return "" +} + +// 发送聊天消息 +type AddChatReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TimeStamp int64 `protobuf:"varint,1,opt,name=timeStamp,proto3" json:"timeStamp,omitempty"` // 消息时间戳,同PbMsgOne的timeStamp,客户端已有聊天的最后的时间戳,用来获取最新的聊天消息,如果为0,则从最开始获取 + Platform string `protobuf:"bytes,2,opt,name=platform,proto3" json:"platform,omitempty"` + Deviceid string `protobuf:"bytes,3,opt,name=deviceid,proto3" json:"deviceid,omitempty"` + Version string `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` + Ip string `protobuf:"bytes,5,opt,name=ip,proto3" json:"ip,omitempty"` + Ts string `protobuf:"bytes,6,opt,name=ts,proto3" json:"ts,omitempty"` // utc时间秒 + Sign string `protobuf:"bytes,7,opt,name=sign,proto3" json:"sign,omitempty"` + Uuid string `protobuf:"bytes,8,opt,name=uuid,proto3" json:"uuid,omitempty"` // 用户唯一字符串 + Msg string `protobuf:"bytes,9,opt,name=msg,proto3" json:"msg,omitempty"` // 聊天消息 +} + +func (x *AddChatReq) Reset() { + *x = AddChatReq{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AddChatReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddChatReq) ProtoMessage() {} + +func (x *AddChatReq) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[20] + 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 AddChatReq.ProtoReflect.Descriptor instead. +func (*AddChatReq) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{20} +} + +func (x *AddChatReq) GetTimeStamp() int64 { + if x != nil { + return x.TimeStamp + } + return 0 +} + +func (x *AddChatReq) GetPlatform() string { + if x != nil { + return x.Platform + } + return "" +} + +func (x *AddChatReq) GetDeviceid() string { + if x != nil { + return x.Deviceid + } + return "" +} + +func (x *AddChatReq) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *AddChatReq) GetIp() string { + if x != nil { + return x.Ip + } + return "" +} + +func (x *AddChatReq) GetTs() string { + if x != nil { + return x.Ts + } + return "" +} + +func (x *AddChatReq) GetSign() string { + if x != nil { + return x.Sign + } + return "" +} + +func (x *AddChatReq) GetUuid() string { + if x != nil { + return x.Uuid + } + return "" +} + +func (x *AddChatReq) GetMsg() string { + if x != nil { + return x.Msg + } + return "" +} + +type AddChatReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Result uint32 `protobuf:"varint,1,opt,name=result,proto3" json:"result,omitempty"` // 0成功,1失败,2 ts长度错误, + Uuid string `protobuf:"bytes,2,opt,name=uuid,proto3" json:"uuid,omitempty"` // 用户唯一字符串 + Lst []*PbMsgOne `protobuf:"bytes,3,rep,name=lst,proto3" json:"lst,omitempty"` // 聊天消息列表 + Error int32 `protobuf:"varint,4,opt,name=error,proto3" json:"error,omitempty"` // 错误码,0成功,1失败, +} + +func (x *AddChatReply) Reset() { + *x = AddChatReply{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AddChatReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddChatReply) ProtoMessage() {} + +func (x *AddChatReply) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[21] + 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 AddChatReply.ProtoReflect.Descriptor instead. +func (*AddChatReply) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{21} +} + +func (x *AddChatReply) GetResult() uint32 { + if x != nil { + return x.Result + } + return 0 +} + +func (x *AddChatReply) GetUuid() string { + if x != nil { + return x.Uuid + } + return "" +} + +func (x *AddChatReply) GetLst() []*PbMsgOne { + if x != nil { + return x.Lst + } + return nil +} + +func (x *AddChatReply) GetError() int32 { + if x != nil { + return x.Error + } + return 0 +} + +// 获取聊天消息列表 +type GetChatReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TimeStamp int64 `protobuf:"varint,1,opt,name=timeStamp,proto3" json:"timeStamp,omitempty"` // 消息时间戳,同PbMsgOne的timeStamp,客户端已有聊天的最后的时间戳,用来获取最新的聊天消息,如果为0,则从最开始获取 + Platform string `protobuf:"bytes,2,opt,name=platform,proto3" json:"platform,omitempty"` + Deviceid string `protobuf:"bytes,3,opt,name=deviceid,proto3" json:"deviceid,omitempty"` + Version string `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` + Ip string `protobuf:"bytes,5,opt,name=ip,proto3" json:"ip,omitempty"` + Ts string `protobuf:"bytes,6,opt,name=ts,proto3" json:"ts,omitempty"` + Sign string `protobuf:"bytes,7,opt,name=sign,proto3" json:"sign,omitempty"` + Uuid string `protobuf:"bytes,8,opt,name=uuid,proto3" json:"uuid,omitempty"` // 用户唯一字符串 +} + +func (x *GetChatReq) Reset() { + *x = GetChatReq{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetChatReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetChatReq) ProtoMessage() {} + +func (x *GetChatReq) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[22] + 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 GetChatReq.ProtoReflect.Descriptor instead. +func (*GetChatReq) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{22} +} + +func (x *GetChatReq) GetTimeStamp() int64 { + if x != nil { + return x.TimeStamp + } + return 0 +} + +func (x *GetChatReq) GetPlatform() string { + if x != nil { + return x.Platform + } + return "" +} + +func (x *GetChatReq) GetDeviceid() string { + if x != nil { + return x.Deviceid + } + return "" +} + +func (x *GetChatReq) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *GetChatReq) GetIp() string { + if x != nil { + return x.Ip + } + return "" +} + +func (x *GetChatReq) GetTs() string { + if x != nil { + return x.Ts + } + return "" +} + +func (x *GetChatReq) GetSign() string { + if x != nil { + return x.Sign + } + return "" +} + +func (x *GetChatReq) GetUuid() string { + if x != nil { + return x.Uuid + } + return "" +} + +type GetChatReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Uuid string `protobuf:"bytes,1,opt,name=uuid,proto3" json:"uuid,omitempty"` // 用户唯一字符串 + Lst []*PbMsgOne `protobuf:"bytes,2,rep,name=lst,proto3" json:"lst,omitempty"` // 聊天消息列表 + Error int32 `protobuf:"varint,3,opt,name=error,proto3" json:"error,omitempty"` // 错误码,0成功,1失败, +} + +func (x *GetChatReply) Reset() { + *x = GetChatReply{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetChatReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetChatReply) ProtoMessage() {} + +func (x *GetChatReply) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[23] + 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 GetChatReply.ProtoReflect.Descriptor instead. +func (*GetChatReply) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{23} +} + +func (x *GetChatReply) GetUuid() string { + if x != nil { + return x.Uuid + } + return "" +} + +func (x *GetChatReply) GetLst() []*PbMsgOne { + if x != nil { + return x.Lst + } + return nil +} + +func (x *GetChatReply) GetError() int32 { + if x != nil { + return x.Error + } + return 0 +} + +type PbSvrData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + LstChat []*PbMsgOne `protobuf:"bytes,1,rep,name=lstChat,proto3" json:"lstChat,omitempty"` // 聊天消息列表 +} + +func (x *PbSvrData) Reset() { + *x = PbSvrData{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PbSvrData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PbSvrData) ProtoMessage() {} + +func (x *PbSvrData) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[24] + 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 PbSvrData.ProtoReflect.Descriptor instead. +func (*PbSvrData) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{24} +} + +func (x *PbSvrData) GetLstChat() []*PbMsgOne { + if x != nil { + return x.LstChat + } + return nil +} + +type PbUserData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *PbUserData) Reset() { + *x = PbUserData{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PbUserData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PbUserData) ProtoMessage() {} + +func (x *PbUserData) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[25] + 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 PbUserData.ProtoReflect.Descriptor instead. +func (*PbUserData) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{25} +} + +type PbReportData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Adjust *PbAdjustData `protobuf:"bytes,1,opt,name=adjust,proto3" json:"adjust,omitempty"` + ShuShu *PbShuShuData `protobuf:"bytes,2,opt,name=shuShu,proto3" json:"shuShu,omitempty"` + Rf uint32 `protobuf:"varint,3,opt,name=rf,proto3" json:"rf,omitempty"` +} + +func (x *PbReportData) Reset() { + *x = PbReportData{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PbReportData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PbReportData) ProtoMessage() {} + +func (x *PbReportData) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[26] + 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 PbReportData.ProtoReflect.Descriptor instead. +func (*PbReportData) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{26} +} + +func (x *PbReportData) GetAdjust() *PbAdjustData { + if x != nil { + return x.Adjust + } + return nil +} + +func (x *PbReportData) GetShuShu() *PbShuShuData { + if x != nil { + return x.ShuShu + } + return nil +} + +func (x *PbReportData) GetRf() uint32 { + if x != nil { + return x.Rf + } + return 0 +} + +type PbAdjustData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + GpsAdid string `protobuf:"bytes,1,opt,name=GpsAdid,proto3" json:"GpsAdid,omitempty"` + Adid string `protobuf:"bytes,2,opt,name=Adid,proto3" json:"Adid,omitempty"` + AndroidId string `protobuf:"bytes,3,opt,name=AndroidId,proto3" json:"AndroidId,omitempty"` + IpAddress string `protobuf:"bytes,4,opt,name=IpAddress,proto3" json:"IpAddress,omitempty"` + CreatedAtUnix string `protobuf:"bytes,5,opt,name=CreatedAtUnix,proto3" json:"CreatedAtUnix,omitempty"` + Currency string `protobuf:"bytes,6,opt,name=Currency,proto3" json:"Currency,omitempty"` + Environment string `protobuf:"bytes,7,opt,name=Environment,proto3" json:"Environment,omitempty"` + UserAgent string `protobuf:"bytes,8,opt,name=UserAgent,proto3" json:"UserAgent,omitempty"` + Price string `protobuf:"bytes,9,opt,name=Price,proto3" json:"Price,omitempty"` + FailReason string `protobuf:"bytes,10,opt,name=FailReason,proto3" json:"FailReason,omitempty"` + AppToken string `protobuf:"bytes,11,opt,name=AppToken,proto3" json:"AppToken,omitempty"` + EventToken string `protobuf:"bytes,12,opt,name=EventToken,proto3" json:"EventToken,omitempty"` + S2S string `protobuf:"bytes,13,opt,name=S2s,proto3" json:"S2s,omitempty"` + ClientName string `protobuf:"bytes,14,opt,name=clientName,proto3" json:"clientName,omitempty"` +} + +func (x *PbAdjustData) Reset() { + *x = PbAdjustData{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PbAdjustData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PbAdjustData) ProtoMessage() {} + +func (x *PbAdjustData) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[27] + 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 PbAdjustData.ProtoReflect.Descriptor instead. +func (*PbAdjustData) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{27} +} + +func (x *PbAdjustData) GetGpsAdid() string { + if x != nil { + return x.GpsAdid + } + return "" +} + +func (x *PbAdjustData) GetAdid() string { + if x != nil { + return x.Adid + } + return "" +} + +func (x *PbAdjustData) GetAndroidId() string { + if x != nil { + return x.AndroidId + } + return "" +} + +func (x *PbAdjustData) GetIpAddress() string { + if x != nil { + return x.IpAddress + } + return "" +} + +func (x *PbAdjustData) GetCreatedAtUnix() string { + if x != nil { + return x.CreatedAtUnix + } + return "" +} + +func (x *PbAdjustData) GetCurrency() string { + if x != nil { + return x.Currency + } + return "" +} + +func (x *PbAdjustData) GetEnvironment() string { + if x != nil { + return x.Environment + } + return "" +} + +func (x *PbAdjustData) GetUserAgent() string { + if x != nil { + return x.UserAgent + } + return "" +} + +func (x *PbAdjustData) GetPrice() string { + if x != nil { + return x.Price + } + return "" +} + +func (x *PbAdjustData) GetFailReason() string { + if x != nil { + return x.FailReason + } + return "" +} + +func (x *PbAdjustData) GetAppToken() string { + if x != nil { + return x.AppToken + } + return "" +} + +func (x *PbAdjustData) GetEventToken() string { + if x != nil { + return x.EventToken + } + return "" +} + +func (x *PbAdjustData) GetS2S() string { + if x != nil { + return x.S2S + } + return "" +} + +func (x *PbAdjustData) GetClientName() string { + if x != nil { + return x.ClientName + } + return "" +} + +type PbShuShuData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + GpsAdid string `protobuf:"bytes,1,opt,name=GpsAdid,proto3" json:"GpsAdid,omitempty"` + AppToken string `protobuf:"bytes,2,opt,name=AppToken,proto3" json:"AppToken,omitempty"` + EventToken string `protobuf:"bytes,3,opt,name=EventToken,proto3" json:"EventToken,omitempty"` + S2S string `protobuf:"bytes,4,opt,name=S2s,proto3" json:"S2s,omitempty"` + AndroidId string `protobuf:"bytes,5,opt,name=AndroidId,proto3" json:"AndroidId,omitempty"` + Adid string `protobuf:"bytes,6,opt,name=Adid,proto3" json:"Adid,omitempty"` + IpAddress string `protobuf:"bytes,7,opt,name=IpAddress,proto3" json:"IpAddress,omitempty"` + CreatedAtUnix string `protobuf:"bytes,8,opt,name=CreatedAtUnix,proto3" json:"CreatedAtUnix,omitempty"` + UserAgent string `protobuf:"bytes,9,opt,name=UserAgent,proto3" json:"UserAgent,omitempty"` + Price string `protobuf:"bytes,10,opt,name=Price,proto3" json:"Price,omitempty"` + Currency string `protobuf:"bytes,11,opt,name=Currency,proto3" json:"Currency,omitempty"` + FailReason string `protobuf:"bytes,12,opt,name=FailReason,proto3" json:"FailReason,omitempty"` + PayoutId string `protobuf:"bytes,13,opt,name=PayoutId,proto3" json:"PayoutId,omitempty"` + MerchantReference string `protobuf:"bytes,14,opt,name=MerchantReference,proto3" json:"MerchantReference,omitempty"` + PaymentMethod string `protobuf:"bytes,15,opt,name=PaymentMethod,proto3" json:"PaymentMethod,omitempty"` + PaymentType string `protobuf:"bytes,16,opt,name=PaymentType,proto3" json:"PaymentType,omitempty"` + PaymentNumber string `protobuf:"bytes,17,opt,name=PaymentNumber,proto3" json:"PaymentNumber,omitempty"` + IapName string `protobuf:"bytes,18,opt,name=IapName,proto3" json:"IapName,omitempty"` + GamecoinNumber string `protobuf:"bytes,19,opt,name=GamecoinNumber,proto3" json:"GamecoinNumber,omitempty"` + GamecoinType string `protobuf:"bytes,20,opt,name=GamecoinType,proto3" json:"GamecoinType,omitempty"` + SsAccountId string `protobuf:"bytes,21,opt,name=SsAccountId,proto3" json:"SsAccountId,omitempty"` + SsDistinctId string `protobuf:"bytes,22,opt,name=SsDistinctId,proto3" json:"SsDistinctId,omitempty"` + SsSuperProperties string `protobuf:"bytes,23,opt,name=SsSuperProperties,proto3" json:"SsSuperProperties,omitempty"` + ClientName string `protobuf:"bytes,24,opt,name=clientName,proto3" json:"clientName,omitempty"` +} + +func (x *PbShuShuData) Reset() { + *x = PbShuShuData{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PbShuShuData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PbShuShuData) ProtoMessage() {} + +func (x *PbShuShuData) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[28] + 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 PbShuShuData.ProtoReflect.Descriptor instead. +func (*PbShuShuData) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{28} +} + +func (x *PbShuShuData) GetGpsAdid() string { + if x != nil { + return x.GpsAdid + } + return "" +} + +func (x *PbShuShuData) GetAppToken() string { + if x != nil { + return x.AppToken + } + return "" +} + +func (x *PbShuShuData) GetEventToken() string { + if x != nil { + return x.EventToken + } + return "" +} + +func (x *PbShuShuData) GetS2S() string { + if x != nil { + return x.S2S + } + return "" +} + +func (x *PbShuShuData) GetAndroidId() string { + if x != nil { + return x.AndroidId + } + return "" +} + +func (x *PbShuShuData) GetAdid() string { + if x != nil { + return x.Adid + } + return "" +} + +func (x *PbShuShuData) GetIpAddress() string { + if x != nil { + return x.IpAddress + } + return "" +} + +func (x *PbShuShuData) GetCreatedAtUnix() string { + if x != nil { + return x.CreatedAtUnix + } + return "" +} + +func (x *PbShuShuData) GetUserAgent() string { + if x != nil { + return x.UserAgent + } + return "" +} + +func (x *PbShuShuData) GetPrice() string { + if x != nil { + return x.Price + } + return "" +} + +func (x *PbShuShuData) GetCurrency() string { + if x != nil { + return x.Currency + } + return "" +} + +func (x *PbShuShuData) GetFailReason() string { + if x != nil { + return x.FailReason + } + return "" +} + +func (x *PbShuShuData) GetPayoutId() string { + if x != nil { + return x.PayoutId + } + return "" +} + +func (x *PbShuShuData) GetMerchantReference() string { + if x != nil { + return x.MerchantReference + } + return "" +} + +func (x *PbShuShuData) GetPaymentMethod() string { + if x != nil { + return x.PaymentMethod + } + return "" +} + +func (x *PbShuShuData) GetPaymentType() string { + if x != nil { + return x.PaymentType + } + return "" +} + +func (x *PbShuShuData) GetPaymentNumber() string { + if x != nil { + return x.PaymentNumber + } + return "" +} + +func (x *PbShuShuData) GetIapName() string { + if x != nil { + return x.IapName + } + return "" +} + +func (x *PbShuShuData) GetGamecoinNumber() string { + if x != nil { + return x.GamecoinNumber + } + return "" +} + +func (x *PbShuShuData) GetGamecoinType() string { + if x != nil { + return x.GamecoinType + } + return "" +} + +func (x *PbShuShuData) GetSsAccountId() string { + if x != nil { + return x.SsAccountId + } + return "" +} + +func (x *PbShuShuData) GetSsDistinctId() string { + if x != nil { + return x.SsDistinctId + } + return "" +} + +func (x *PbShuShuData) GetSsSuperProperties() string { + if x != nil { + return x.SsSuperProperties + } + return "" +} + +func (x *PbShuShuData) GetClientName() string { + if x != nil { + return x.ClientName + } + return "" +} + +type PayInitReply_Item struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` // 产品id + Amount float64 `protobuf:"fixed64,2,opt,name=amount,proto3" json:"amount,omitempty"` // 产品价格 + Status uint32 `protobuf:"varint,3,opt,name=status,proto3" json:"status,omitempty"` // 状态:1可提现 2:条件未达成 3:当天该产品id已提现成功, 4:禁止提现, 5:提现中 +} + +func (x *PayInitReply_Item) Reset() { + *x = PayInitReply_Item{} + if protoimpl.UnsafeEnabled { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PayInitReply_Item) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PayInitReply_Item) ProtoMessage() {} + +func (x *PayInitReply_Item) ProtoReflect() protoreflect.Message { + mi := &file_api_eonline_v1_pagsmile_proto_msgTypes[29] + 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 PayInitReply_Item.ProtoReflect.Descriptor instead. +func (*PayInitReply_Item) Descriptor() ([]byte, []int) { + return file_api_eonline_v1_pagsmile_proto_rawDescGZIP(), []int{1, 0} +} + +func (x *PayInitReply_Item) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *PayInitReply_Item) GetAmount() float64 { + if x != nil { + return x.Amount + } + return 0 +} + +func (x *PayInitReply_Item) GetStatus() uint32 { + if x != nil { + return x.Status + } + return 0 +} + +var File_api_eonline_v1_pagsmile_proto protoreflect.FileDescriptor + +var file_api_eonline_v1_pagsmile_proto_rawDesc = []byte{ + 0x0a, 0x1d, 0x61, 0x70, 0x69, 0x2f, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31, + 0x2f, 0x70, 0x61, 0x67, 0x73, 0x6d, 0x69, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x0e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x1a, + 0x17, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbf, 0x01, 0x0a, 0x0a, 0x50, 0x61, 0x79, + 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x12, 0x23, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, + 0x6f, 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, + 0x10, 0x01, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x23, 0x0a, 0x08, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, + 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x69, + 0x64, 0x12, 0x21, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x70, 0x12, 0x17, 0x0a, 0x02, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, 0x74, 0x73, 0x12, 0x1b, 0x0a, + 0x04, 0x73, 0x69, 0x67, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, + 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x73, 0x69, 0x67, 0x6e, 0x22, 0xf7, 0x03, 0x0a, 0x0c, 0x50, + 0x61, 0x79, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x75, + 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, + 0x12, 0x0a, 0x04, 0x64, 0x61, 0x79, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x64, + 0x61, 0x79, 0x73, 0x12, 0x37, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x79, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, + 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x26, 0x0a, 0x0e, + 0x43, 0x61, 0x6e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x43, 0x61, 0x6e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x75, + 0x62, 0x6d, 0x69, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x75, 0x62, + 0x6d, 0x69, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x43, 0x68, 0x65, 0x63, 0x6b, + 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x43, + 0x68, 0x65, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x43, 0x68, + 0x65, 0x63, 0x6b, 0x43, 0x6f, 0x69, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x43, + 0x68, 0x65, 0x63, 0x6b, 0x43, 0x6f, 0x69, 0x6e, 0x12, 0x26, 0x0a, 0x0e, 0x43, 0x61, 0x6e, 0x43, + 0x68, 0x65, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x4f, 0x75, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x0e, 0x43, 0x61, 0x6e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x4f, 0x75, 0x74, + 0x12, 0x34, 0x0a, 0x15, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x46, + 0x61, 0x69, 0x6c, 0x65, 0x64, 0x44, 0x65, 0x73, 0x63, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x15, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x46, 0x61, 0x69, 0x6c, + 0x65, 0x64, 0x44, 0x65, 0x73, 0x63, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, + 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x1e, 0x0a, 0x0a, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x1a, 0x46, 0x0a, 0x04, + 0x49, 0x74, 0x65, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x22, 0xb3, 0x01, 0x0a, 0x12, 0x50, 0x62, 0x52, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x44, 0x61, 0x74, 0x61, 0x41, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, + 0x70, 0x73, 0x5f, 0x61, 0x64, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, + 0x70, 0x73, 0x41, 0x64, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, + 0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x6e, 0x64, 0x72, + 0x6f, 0x69, 0x64, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x69, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, + 0x72, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, + 0x73, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1a, + 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x87, 0x04, 0x0a, 0x12, 0x50, + 0x62, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x53, 0x68, 0x75, 0x53, 0x68, + 0x75, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x70, 0x73, 0x5f, 0x67, 0x61, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, 0x70, 0x73, 0x47, 0x61, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, + 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x61, + 0x64, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x69, 0x64, 0x12, + 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x14, + 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, + 0x72, 0x69, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, + 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x68, + 0x6f, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0d, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x61, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x69, 0x61, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, + 0x67, 0x61, 0x6d, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, + 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x67, 0x61, 0x6d, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x4e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x67, 0x61, 0x6d, 0x65, 0x63, 0x6f, 0x69, + 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x67, 0x61, + 0x6d, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x73, 0x73, + 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x73, 0x73, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x24, + 0x0a, 0x0e, 0x73, 0x73, 0x5f, 0x64, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x63, 0x74, 0x5f, 0x69, 0x64, + 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x73, 0x44, 0x69, 0x73, 0x74, 0x69, 0x6e, + 0x63, 0x74, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x73, 0x73, 0x5f, 0x73, 0x75, 0x70, 0x65, 0x72, + 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x11, 0x73, 0x73, 0x53, 0x75, 0x70, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, + 0x74, 0x69, 0x65, 0x73, 0x22, 0x99, 0x06, 0x0a, 0x09, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x52, + 0x65, 0x71, 0x12, 0x23, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x08, 0x70, + 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x23, 0x0a, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, + 0x10, 0x01, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, + 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x17, 0x0a, 0x02, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, + 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x04, 0x73, 0x69, 0x67, 0x6e, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, + 0x04, 0x73, 0x69, 0x67, 0x6e, 0x12, 0x21, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, + 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x07, 0x69, 0x74, 0x65, 0x6d, + 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x2a, 0x04, + 0x18, 0x03, 0x28, 0x01, 0x52, 0x06, 0x69, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x06, + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x42, 0x17, 0xfa, 0x42, + 0x14, 0x12, 0x12, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x40, 0x29, 0x9a, 0x99, 0x99, + 0x99, 0x99, 0x99, 0xb9, 0x3f, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x34, 0x0a, + 0x11, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x6d, 0x61, + 0x72, 0x6b, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, + 0x01, 0x52, 0x10, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x6d, + 0x61, 0x72, 0x6b, 0x12, 0x1b, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x70, + 0x12, 0x2c, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x72, 0x04, 0x10, 0x00, 0x18, + 0x05, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2e, + 0x0a, 0x0d, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x0d, 0x20, 0x01, 0x28, 0x09, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x72, 0x04, 0x10, 0x00, 0x18, 0x04, + 0x52, 0x0c, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2a, + 0x0a, 0x0b, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x0e, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x72, 0x04, 0x10, 0x00, 0x18, 0x64, 0x52, 0x0a, + 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x72, 0x04, 0x10, + 0x00, 0x18, 0x64, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x42, 0x0a, 0x0a, 0x64, 0x61, 0x74, + 0x61, 0x41, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, + 0x62, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x41, 0x64, 0x6a, 0x75, 0x73, + 0x74, 0x52, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x41, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x12, 0x42, 0x0a, + 0x0a, 0x64, 0x61, 0x74, 0x61, 0x53, 0x68, 0x75, 0x53, 0x68, 0x75, 0x18, 0x11, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x62, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x53, + 0x68, 0x75, 0x53, 0x68, 0x75, 0x52, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x53, 0x68, 0x75, 0x53, 0x68, + 0x75, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x18, + 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, + 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, + 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x1d, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, + 0x22, 0x50, 0x0a, 0x0b, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x6e, 0x6f, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4e, 0x6f, 0x12, 0x14, 0x0a, 0x05, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x22, 0x99, 0x01, 0x0a, 0x11, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x43, 0x61, 0x6c, + 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x79, 0x6f, + 0x75, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x79, + 0x6f, 0x75, 0x74, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, + 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x75, 0x73, 0x74, + 0x6f, 0x6d, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x10, + 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, + 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x2f, + 0x0a, 0x13, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, + 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, + 0xe9, 0x01, 0x0a, 0x0e, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, + 0x65, 0x71, 0x12, 0x23, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x08, 0x70, + 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x23, 0x0a, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, + 0x10, 0x01, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, + 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x17, 0x0a, 0x02, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, + 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x04, 0x73, 0x69, 0x67, 0x6e, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, + 0x04, 0x73, 0x69, 0x67, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x69, 0x70, 0x12, 0x24, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, + 0x6e, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, + 0x01, 0x52, 0x08, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4e, 0x6f, 0x22, 0x40, 0x0a, 0x10, 0x50, + 0x61, 0x79, 0x6f, 0x75, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, + 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x92, 0x02, + 0x0a, 0x10, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x73, 0x74, 0x52, + 0x65, 0x71, 0x12, 0x23, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x08, 0x70, + 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x23, 0x0a, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, + 0x10, 0x01, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, + 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x17, 0x0a, 0x02, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, + 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x04, 0x73, 0x69, 0x67, 0x6e, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, + 0x04, 0x73, 0x69, 0x67, 0x6e, 0x12, 0x21, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x2a, 0x04, 0x18, 0x03, 0x28, 0x01, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x70, 0x61, 0x67, + 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, + 0x7a, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, + 0x7a, 0x65, 0x22, 0x5b, 0x0a, 0x12, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x55, 0x73, 0x65, 0x72, + 0x4c, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2f, 0x0a, 0x03, 0x6c, 0x73, 0x74, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x55, 0x73, 0x65, + 0x72, 0x4f, 0x6e, 0x65, 0x52, 0x03, 0x6c, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, + 0x73, 0x0a, 0x0d, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4f, 0x6e, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, + 0x4e, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, + 0x4e, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x22, 0x90, 0x02, 0x0a, 0x0f, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x12, 0x23, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, + 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, + 0x02, 0x10, 0x01, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x23, 0x0a, + 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x69, 0x64, 0x12, 0x21, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x02, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, 0x74, 0x73, 0x12, 0x1b, + 0x0a, 0x04, 0x73, 0x69, 0x67, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, + 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x73, 0x69, 0x67, 0x6e, 0x12, 0x23, 0x0a, 0x08, 0x72, + 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4e, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, + 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x08, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4e, 0x6f, + 0x12, 0x12, 0x0a, 0x04, 0x66, 0x61, 0x69, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x66, 0x61, 0x69, 0x6c, 0x12, 0x21, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x0d, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x2a, 0x04, 0x18, 0x03, 0x28, 0x02, 0x52, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x5d, 0x0a, 0x11, 0x50, 0x61, 0x79, 0x6f, 0x75, + 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1a, 0x0a, 0x08, + 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4e, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4e, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x50, 0x0a, 0x0e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x12, 0x21, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, + 0x10, 0x01, 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x04, 0x75, + 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, + 0x10, 0x01, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x22, 0x40, 0x0a, 0x10, 0x53, 0x75, 0x62, 0x6d, + 0x69, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x16, 0x0a, 0x06, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x85, 0x02, 0x0a, 0x0c, 0x43, + 0x68, 0x65, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x12, 0x23, 0x0a, 0x08, 0x70, + 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, + 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, + 0x12, 0x23, 0x0a, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x08, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x70, 0x12, 0x17, 0x0a, 0x02, 0x74, 0x73, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, 0x74, + 0x73, 0x12, 0x1b, 0x0a, 0x04, 0x73, 0x69, 0x67, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x73, 0x69, 0x67, 0x6e, 0x12, 0x12, + 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, + 0x69, 0x64, 0x12, 0x2e, 0x0a, 0x12, 0x69, 0x73, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x68, 0x6f, 0x77, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, + 0x69, 0x73, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x68, + 0x6f, 0x77, 0x22, 0xb0, 0x02, 0x0a, 0x0e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x26, 0x0a, 0x0e, 0x43, 0x61, 0x6e, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x43, + 0x61, 0x6e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x12, 0x20, 0x0a, + 0x0b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x12, + 0x20, 0x0a, 0x0b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x50, 0x61, 0x79, + 0x6f, 0x75, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x43, 0x6f, 0x69, 0x6e, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x43, 0x6f, 0x69, + 0x6e, 0x12, 0x26, 0x0a, 0x0e, 0x43, 0x61, 0x6e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x50, 0x61, 0x79, + 0x4f, 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x43, 0x61, 0x6e, 0x43, 0x68, + 0x65, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x4f, 0x75, 0x74, 0x12, 0x34, 0x0a, 0x15, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x44, 0x65, + 0x73, 0x63, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x44, 0x65, 0x73, 0x63, 0x12, + 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x62, 0x0a, 0x08, 0x50, 0x62, 0x4d, 0x73, 0x67, 0x4f, 0x6e, + 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x6d, 0x70, 0x12, + 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, + 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x22, 0x83, 0x02, 0x0a, 0x0a, 0x41, 0x64, + 0x64, 0x43, 0x68, 0x61, 0x74, 0x52, 0x65, 0x71, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x53, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x53, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x23, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, + 0x72, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, + 0x01, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x23, 0x0a, 0x08, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, + 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x69, 0x64, + 0x12, 0x21, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x02, 0x69, 0x70, 0x12, 0x17, 0x0a, 0x02, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x04, + 0x73, 0x69, 0x67, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, + 0x02, 0x10, 0x01, 0x52, 0x04, 0x73, 0x69, 0x67, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, + 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x10, 0x0a, + 0x03, 0x6d, 0x73, 0x67, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x22, + 0x7c, 0x0a, 0x0c, 0x41, 0x64, 0x64, 0x43, 0x68, 0x61, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, + 0x16, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x2a, 0x0a, 0x03, 0x6c, + 0x73, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, + 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x62, 0x4d, 0x73, 0x67, 0x4f, + 0x6e, 0x65, 0x52, 0x03, 0x6c, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xf1, 0x01, + 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x74, 0x52, 0x65, 0x71, 0x12, 0x1c, 0x0a, 0x09, + 0x74, 0x69, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x23, 0x0a, 0x08, 0x70, 0x6c, + 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, + 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, + 0x23, 0x0a, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x70, 0x12, 0x17, 0x0a, 0x02, 0x74, 0x73, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, 0x74, 0x73, + 0x12, 0x1b, 0x0a, 0x04, 0x73, 0x69, 0x67, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, + 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x73, 0x69, 0x67, 0x6e, 0x12, 0x12, 0x0a, + 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, + 0x64, 0x22, 0x64, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x74, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x2a, 0x0a, 0x03, 0x6c, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x62, 0x4d, 0x73, 0x67, 0x4f, 0x6e, 0x65, 0x52, 0x03, 0x6c, 0x73, + 0x74, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x3f, 0x0a, 0x09, 0x50, 0x62, 0x53, 0x76, 0x72, + 0x44, 0x61, 0x74, 0x61, 0x12, 0x32, 0x0a, 0x07, 0x6c, 0x73, 0x74, 0x43, 0x68, 0x61, 0x74, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x62, 0x4d, 0x73, 0x67, 0x4f, 0x6e, 0x65, 0x52, + 0x07, 0x6c, 0x73, 0x74, 0x43, 0x68, 0x61, 0x74, 0x22, 0x0c, 0x0a, 0x0a, 0x50, 0x62, 0x55, 0x73, + 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x22, 0x8a, 0x01, 0x0a, 0x0c, 0x50, 0x62, 0x52, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x12, 0x34, 0x0a, 0x06, 0x61, 0x64, 0x6a, 0x75, 0x73, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x62, 0x41, 0x64, 0x6a, 0x75, 0x73, + 0x74, 0x44, 0x61, 0x74, 0x61, 0x52, 0x06, 0x61, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x12, 0x34, 0x0a, + 0x06, 0x73, 0x68, 0x75, 0x53, 0x68, 0x75, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, + 0x62, 0x53, 0x68, 0x75, 0x53, 0x68, 0x75, 0x44, 0x61, 0x74, 0x61, 0x52, 0x06, 0x73, 0x68, 0x75, + 0x53, 0x68, 0x75, 0x12, 0x0e, 0x0a, 0x02, 0x72, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x02, 0x72, 0x66, 0x22, 0x9e, 0x03, 0x0a, 0x0c, 0x50, 0x62, 0x41, 0x64, 0x6a, 0x75, 0x73, 0x74, + 0x44, 0x61, 0x74, 0x61, 0x12, 0x18, 0x0a, 0x07, 0x47, 0x70, 0x73, 0x41, 0x64, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x47, 0x70, 0x73, 0x41, 0x64, 0x69, 0x64, 0x12, 0x12, + 0x0a, 0x04, 0x41, 0x64, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x41, 0x64, + 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x49, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x49, 0x64, + 0x12, 0x1c, 0x0a, 0x09, 0x49, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x49, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x24, + 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x55, 0x6e, 0x69, 0x78, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, + 0x55, 0x6e, 0x69, 0x78, 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, + 0x12, 0x20, 0x0a, 0x0b, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, + 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x55, 0x73, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x55, 0x73, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, + 0x12, 0x14, 0x0a, 0x05, 0x50, 0x72, 0x69, 0x63, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x46, 0x61, 0x69, 0x6c, 0x52, 0x65, + 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x46, 0x61, 0x69, 0x6c, + 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x41, 0x70, 0x70, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, 0x70, 0x70, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x53, 0x32, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x53, 0x32, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4e, 0x61, + 0x6d, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x8e, 0x06, 0x0a, 0x0c, 0x50, 0x62, 0x53, 0x68, 0x75, 0x53, 0x68, + 0x75, 0x44, 0x61, 0x74, 0x61, 0x12, 0x18, 0x0a, 0x07, 0x47, 0x70, 0x73, 0x41, 0x64, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x47, 0x70, 0x73, 0x41, 0x64, 0x69, 0x64, 0x12, + 0x1a, 0x0a, 0x08, 0x41, 0x70, 0x70, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x41, 0x70, 0x70, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x53, + 0x32, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x53, 0x32, 0x73, 0x12, 0x1c, 0x0a, + 0x09, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x49, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x41, + 0x64, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x41, 0x64, 0x69, 0x64, 0x12, + 0x1c, 0x0a, 0x09, 0x49, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x49, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x24, 0x0a, + 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x55, 0x6e, 0x69, 0x78, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x55, + 0x6e, 0x69, 0x78, 0x12, 0x1c, 0x0a, 0x09, 0x55, 0x73, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x55, 0x73, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, + 0x74, 0x12, 0x14, 0x0a, 0x05, 0x50, 0x72, 0x69, 0x63, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x43, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x46, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x61, 0x73, 0x6f, + 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x46, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x61, + 0x73, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x49, 0x64, 0x18, + 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x49, 0x64, 0x12, + 0x2c, 0x0a, 0x11, 0x4d, 0x65, 0x72, 0x63, 0x68, 0x61, 0x6e, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, + 0x65, 0x6e, 0x63, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x4d, 0x65, 0x72, 0x63, + 0x68, 0x61, 0x6e, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x24, 0x0a, + 0x0d, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x0f, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, + 0x70, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x50, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x49, + 0x61, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x49, 0x61, + 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x47, 0x61, 0x6d, 0x65, 0x63, 0x6f, 0x69, + 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x47, + 0x61, 0x6d, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x22, 0x0a, + 0x0c, 0x47, 0x61, 0x6d, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x18, 0x14, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x47, 0x61, 0x6d, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x53, 0x73, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, + 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x53, 0x73, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x53, 0x73, 0x44, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x63, + 0x74, 0x49, 0x64, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x53, 0x73, 0x44, 0x69, 0x73, + 0x74, 0x69, 0x6e, 0x63, 0x74, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x11, 0x53, 0x73, 0x53, 0x75, 0x70, + 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x17, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x11, 0x53, 0x73, 0x53, 0x75, 0x70, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65, + 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4e, + 0x61, 0x6d, 0x65, 0x18, 0x18, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x42, 0x19, 0x5a, 0x17, 0x73, 0x61, 0x6e, 0x64, 0x63, 0x2f, 0x61, + 0x70, 0x69, 0x2f, 0x65, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x76, 0x31, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_api_eonline_v1_pagsmile_proto_rawDescOnce sync.Once + file_api_eonline_v1_pagsmile_proto_rawDescData = file_api_eonline_v1_pagsmile_proto_rawDesc +) + +func file_api_eonline_v1_pagsmile_proto_rawDescGZIP() []byte { + file_api_eonline_v1_pagsmile_proto_rawDescOnce.Do(func() { + file_api_eonline_v1_pagsmile_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_eonline_v1_pagsmile_proto_rawDescData) + }) + return file_api_eonline_v1_pagsmile_proto_rawDescData +} + +var file_api_eonline_v1_pagsmile_proto_msgTypes = make([]protoimpl.MessageInfo, 30) +var file_api_eonline_v1_pagsmile_proto_goTypes = []interface{}{ + (*PayInitReq)(nil), // 0: api.eonline.v1.PayInitReq + (*PayInitReply)(nil), // 1: api.eonline.v1.PayInitReply + (*PbReportDataAdjust)(nil), // 2: api.eonline.v1.PbReportDataAdjust + (*PbReportDataShuShu)(nil), // 3: api.eonline.v1.PbReportDataShuShu + (*PayoutReq)(nil), // 4: api.eonline.v1.PayoutReq + (*PayoutReply)(nil), // 5: api.eonline.v1.PayoutReply + (*PayoutCallbackReq)(nil), // 6: api.eonline.v1.PayoutCallbackReq + (*PayoutCallbackReply)(nil), // 7: api.eonline.v1.PayoutCallbackReply + (*PayoutCheckReq)(nil), // 8: api.eonline.v1.PayoutCheckReq + (*PayoutCheckReply)(nil), // 9: api.eonline.v1.PayoutCheckReply + (*PayoutUserLstReq)(nil), // 10: api.eonline.v1.PayoutUserLstReq + (*PayoutUserLstReply)(nil), // 11: api.eonline.v1.PayoutUserLstReply + (*PayoutUserOne)(nil), // 12: api.eonline.v1.PayoutUserOne + (*PayoutStatusReq)(nil), // 13: api.eonline.v1.PayoutStatusReq + (*PayoutStatusReply)(nil), // 14: api.eonline.v1.PayoutStatusReply + (*SubmitCheckReq)(nil), // 15: api.eonline.v1.SubmitCheckReq + (*SubmitCheckReply)(nil), // 16: api.eonline.v1.SubmitCheckReply + (*CheckInfoReq)(nil), // 17: api.eonline.v1.CheckInfoReq + (*CheckInfoReply)(nil), // 18: api.eonline.v1.CheckInfoReply + (*PbMsgOne)(nil), // 19: api.eonline.v1.PbMsgOne + (*AddChatReq)(nil), // 20: api.eonline.v1.AddChatReq + (*AddChatReply)(nil), // 21: api.eonline.v1.AddChatReply + (*GetChatReq)(nil), // 22: api.eonline.v1.GetChatReq + (*GetChatReply)(nil), // 23: api.eonline.v1.GetChatReply + (*PbSvrData)(nil), // 24: api.eonline.v1.PbSvrData + (*PbUserData)(nil), // 25: api.eonline.v1.PbUserData + (*PbReportData)(nil), // 26: api.eonline.v1.PbReportData + (*PbAdjustData)(nil), // 27: api.eonline.v1.PbAdjustData + (*PbShuShuData)(nil), // 28: api.eonline.v1.PbShuShuData + (*PayInitReply_Item)(nil), // 29: api.eonline.v1.PayInitReply.Item +} +var file_api_eonline_v1_pagsmile_proto_depIdxs = []int32{ + 29, // 0: api.eonline.v1.PayInitReply.items:type_name -> api.eonline.v1.PayInitReply.Item + 2, // 1: api.eonline.v1.PayoutReq.dataAdjust:type_name -> api.eonline.v1.PbReportDataAdjust + 3, // 2: api.eonline.v1.PayoutReq.dataShuShu:type_name -> api.eonline.v1.PbReportDataShuShu + 12, // 3: api.eonline.v1.PayoutUserLstReply.lst:type_name -> api.eonline.v1.PayoutUserOne + 19, // 4: api.eonline.v1.AddChatReply.lst:type_name -> api.eonline.v1.PbMsgOne + 19, // 5: api.eonline.v1.GetChatReply.lst:type_name -> api.eonline.v1.PbMsgOne + 19, // 6: api.eonline.v1.PbSvrData.lstChat:type_name -> api.eonline.v1.PbMsgOne + 27, // 7: api.eonline.v1.PbReportData.adjust:type_name -> api.eonline.v1.PbAdjustData + 28, // 8: api.eonline.v1.PbReportData.shuShu:type_name -> api.eonline.v1.PbShuShuData + 9, // [9:9] is the sub-list for method output_type + 9, // [9:9] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name +} + +func init() { file_api_eonline_v1_pagsmile_proto_init() } +func file_api_eonline_v1_pagsmile_proto_init() { + if File_api_eonline_v1_pagsmile_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_api_eonline_v1_pagsmile_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PayInitReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PayInitReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PbReportDataAdjust); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PbReportDataShuShu); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PayoutReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PayoutReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PayoutCallbackReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PayoutCallbackReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PayoutCheckReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PayoutCheckReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PayoutUserLstReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PayoutUserLstReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PayoutUserOne); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PayoutStatusReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PayoutStatusReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubmitCheckReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubmitCheckReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CheckInfoReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CheckInfoReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PbMsgOne); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddChatReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddChatReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetChatReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetChatReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PbSvrData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PbUserData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PbReportData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PbAdjustData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PbShuShuData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_eonline_v1_pagsmile_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PayInitReply_Item); 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_api_eonline_v1_pagsmile_proto_rawDesc, + NumEnums: 0, + NumMessages: 30, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_api_eonline_v1_pagsmile_proto_goTypes, + DependencyIndexes: file_api_eonline_v1_pagsmile_proto_depIdxs, + MessageInfos: file_api_eonline_v1_pagsmile_proto_msgTypes, + }.Build() + File_api_eonline_v1_pagsmile_proto = out.File + file_api_eonline_v1_pagsmile_proto_rawDesc = nil + file_api_eonline_v1_pagsmile_proto_goTypes = nil + file_api_eonline_v1_pagsmile_proto_depIdxs = nil +} diff --git a/api/eonline/v1/pagsmile.pb.validate.go b/api/eonline/v1/pagsmile.pb.validate.go new file mode 100644 index 0000000..5686c48 --- /dev/null +++ b/api/eonline/v1/pagsmile.pb.validate.go @@ -0,0 +1,4197 @@ +// Code generated by protoc-gen-validate. DO NOT EDIT. +// source: api/eonline/v1/pagsmile.proto + +package v1 + +import ( + "bytes" + "errors" + "fmt" + "net" + "net/mail" + "net/url" + "regexp" + "sort" + "strings" + "time" + "unicode/utf8" + + "google.golang.org/protobuf/types/known/anypb" +) + +// ensure the imports are used +var ( + _ = bytes.MinRead + _ = errors.New("") + _ = fmt.Print + _ = utf8.UTFMax + _ = (*regexp.Regexp)(nil) + _ = (*strings.Reader)(nil) + _ = net.IPv4len + _ = time.Duration(0) + _ = (*url.URL)(nil) + _ = (*mail.Address)(nil) + _ = anypb.Any{} + _ = sort.Sort +) + +// Validate checks the field values on PayInitReq with the rules defined in the +// proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *PayInitReq) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on PayInitReq with the rules defined in +// the proto definition for this message. If any rules are violated, the +// result is a list of violation errors wrapped in PayInitReqMultiError, or +// nil if none found. +func (m *PayInitReq) ValidateAll() error { + return m.validate(true) +} + +func (m *PayInitReq) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if utf8.RuneCountInString(m.GetPlatform()) < 1 { + err := PayInitReqValidationError{ + field: "Platform", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetDeviceid()) < 1 { + err := PayInitReqValidationError{ + field: "Deviceid", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetVersion()) < 1 { + err := PayInitReqValidationError{ + field: "Version", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + // no validation rules for Ip + + if utf8.RuneCountInString(m.GetTs()) < 1 { + err := PayInitReqValidationError{ + field: "Ts", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetSign()) < 1 { + err := PayInitReqValidationError{ + field: "Sign", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if len(errors) > 0 { + return PayInitReqMultiError(errors) + } + + return nil +} + +// PayInitReqMultiError is an error wrapping multiple validation errors +// returned by PayInitReq.ValidateAll() if the designated constraints aren't met. +type PayInitReqMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m PayInitReqMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m PayInitReqMultiError) AllErrors() []error { return m } + +// PayInitReqValidationError is the validation error returned by +// PayInitReq.Validate if the designated constraints aren't met. +type PayInitReqValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e PayInitReqValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e PayInitReqValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e PayInitReqValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e PayInitReqValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e PayInitReqValidationError) ErrorName() string { return "PayInitReqValidationError" } + +// Error satisfies the builtin error interface +func (e PayInitReqValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sPayInitReq.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = PayInitReqValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = PayInitReqValidationError{} + +// Validate checks the field values on PayInitReply with the rules defined in +// the proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *PayInitReply) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on PayInitReply with the rules defined +// in the proto definition for this message. If any rules are violated, the +// result is a list of violation errors wrapped in PayInitReplyMultiError, or +// nil if none found. +func (m *PayInitReply) ValidateAll() error { + return m.validate(true) +} + +func (m *PayInitReply) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for Uuid + + // no validation rules for Days + + for idx, item := range m.GetItems() { + _, _ = idx, item + + if all { + switch v := interface{}(item).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, PayInitReplyValidationError{ + field: fmt.Sprintf("Items[%v]", idx), + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, PayInitReplyValidationError{ + field: fmt.Sprintf("Items[%v]", idx), + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(item).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return PayInitReplyValidationError{ + field: fmt.Sprintf("Items[%v]", idx), + reason: "embedded message failed validation", + cause: err, + } + } + } + + } + + // no validation rules for CanCheckSubmit + + // no validation rules for CheckSubmit + + // no validation rules for CheckResult + + // no validation rules for CheckPayout + + // no validation rules for CheckCoin + + // no validation rules for CanCheckPayOut + + // no validation rules for CheckResultFailedDesc + + // no validation rules for Error + + // no validation rules for ClientData + + if len(errors) > 0 { + return PayInitReplyMultiError(errors) + } + + return nil +} + +// PayInitReplyMultiError is an error wrapping multiple validation errors +// returned by PayInitReply.ValidateAll() if the designated constraints aren't met. +type PayInitReplyMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m PayInitReplyMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m PayInitReplyMultiError) AllErrors() []error { return m } + +// PayInitReplyValidationError is the validation error returned by +// PayInitReply.Validate if the designated constraints aren't met. +type PayInitReplyValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e PayInitReplyValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e PayInitReplyValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e PayInitReplyValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e PayInitReplyValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e PayInitReplyValidationError) ErrorName() string { return "PayInitReplyValidationError" } + +// Error satisfies the builtin error interface +func (e PayInitReplyValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sPayInitReply.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = PayInitReplyValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = PayInitReplyValidationError{} + +// Validate checks the field values on PbReportDataAdjust with the rules +// defined in the proto definition for this message. If any rules are +// violated, the first error encountered is returned, or nil if there are no violations. +func (m *PbReportDataAdjust) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on PbReportDataAdjust with the rules +// defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// PbReportDataAdjustMultiError, or nil if none found. +func (m *PbReportDataAdjust) ValidateAll() error { + return m.validate(true) +} + +func (m *PbReportDataAdjust) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for GpsAdid + + // no validation rules for AndroidId + + // no validation rules for Adid + + // no validation rules for UserAgent + + // no validation rules for Price + + // no validation rules for Currency + + if len(errors) > 0 { + return PbReportDataAdjustMultiError(errors) + } + + return nil +} + +// PbReportDataAdjustMultiError is an error wrapping multiple validation errors +// returned by PbReportDataAdjust.ValidateAll() if the designated constraints +// aren't met. +type PbReportDataAdjustMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m PbReportDataAdjustMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m PbReportDataAdjustMultiError) AllErrors() []error { return m } + +// PbReportDataAdjustValidationError is the validation error returned by +// PbReportDataAdjust.Validate if the designated constraints aren't met. +type PbReportDataAdjustValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e PbReportDataAdjustValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e PbReportDataAdjustValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e PbReportDataAdjustValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e PbReportDataAdjustValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e PbReportDataAdjustValidationError) ErrorName() string { + return "PbReportDataAdjustValidationError" +} + +// Error satisfies the builtin error interface +func (e PbReportDataAdjustValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sPbReportDataAdjust.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = PbReportDataAdjustValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = PbReportDataAdjustValidationError{} + +// Validate checks the field values on PbReportDataShuShu with the rules +// defined in the proto definition for this message. If any rules are +// violated, the first error encountered is returned, or nil if there are no violations. +func (m *PbReportDataShuShu) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on PbReportDataShuShu with the rules +// defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// PbReportDataShuShuMultiError, or nil if none found. +func (m *PbReportDataShuShu) ValidateAll() error { + return m.validate(true) +} + +func (m *PbReportDataShuShu) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for GpsGaid + + // no validation rules for AndroidId + + // no validation rules for Adid + + // no validation rules for UserAgent + + // no validation rules for Price + + // no validation rules for Currency + + // no validation rules for PaymentMethod + + // no validation rules for PaymentType + + // no validation rules for PaymentNumber + + // no validation rules for IapName + + // no validation rules for GamecoinNumber + + // no validation rules for GamecoinType + + // no validation rules for SsAccountId + + // no validation rules for SsDistinctId + + // no validation rules for SsSuperProperties + + if len(errors) > 0 { + return PbReportDataShuShuMultiError(errors) + } + + return nil +} + +// PbReportDataShuShuMultiError is an error wrapping multiple validation errors +// returned by PbReportDataShuShu.ValidateAll() if the designated constraints +// aren't met. +type PbReportDataShuShuMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m PbReportDataShuShuMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m PbReportDataShuShuMultiError) AllErrors() []error { return m } + +// PbReportDataShuShuValidationError is the validation error returned by +// PbReportDataShuShu.Validate if the designated constraints aren't met. +type PbReportDataShuShuValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e PbReportDataShuShuValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e PbReportDataShuShuValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e PbReportDataShuShuValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e PbReportDataShuShuValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e PbReportDataShuShuValidationError) ErrorName() string { + return "PbReportDataShuShuValidationError" +} + +// Error satisfies the builtin error interface +func (e PbReportDataShuShuValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sPbReportDataShuShu.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = PbReportDataShuShuValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = PbReportDataShuShuValidationError{} + +// Validate checks the field values on PayoutReq with the rules defined in the +// proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *PayoutReq) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on PayoutReq with the rules defined in +// the proto definition for this message. If any rules are violated, the +// result is a list of violation errors wrapped in PayoutReqMultiError, or nil +// if none found. +func (m *PayoutReq) ValidateAll() error { + return m.validate(true) +} + +func (m *PayoutReq) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if utf8.RuneCountInString(m.GetPlatform()) < 1 { + err := PayoutReqValidationError{ + field: "Platform", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetDeviceid()) < 1 { + err := PayoutReqValidationError{ + field: "Deviceid", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetVersion()) < 1 { + err := PayoutReqValidationError{ + field: "Version", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetTs()) < 1 { + err := PayoutReqValidationError{ + field: "Ts", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetSign()) < 1 { + err := PayoutReqValidationError{ + field: "Sign", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetAccount()) < 1 { + err := PayoutReqValidationError{ + field: "Account", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if val := m.GetItemId(); val < 1 || val > 3 { + err := PayoutReqValidationError{ + field: "ItemId", + reason: "value must be inside range [1, 3]", + } + if !all { + return err + } + errors = append(errors, err) + } + + if val := m.GetAmount(); val < 0.1 || val > 50 { + err := PayoutReqValidationError{ + field: "Amount", + reason: "value must be inside range [0.1, 50]", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetAdditionalRemark()) < 1 { + err := PayoutReqValidationError{ + field: "AdditionalRemark", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetUuid()) < 1 { + err := PayoutReqValidationError{ + field: "Uuid", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + // no validation rules for Ip + + if l := utf8.RuneCountInString(m.GetAccountType()); l < 0 || l > 5 { + err := PayoutReqValidationError{ + field: "AccountType", + reason: "value length must be between 0 and 5 runes, inclusive", + } + if !all { + return err + } + errors = append(errors, err) + } + + if l := utf8.RuneCountInString(m.GetDocumentType()); l < 0 || l > 4 { + err := PayoutReqValidationError{ + field: "DocumentType", + reason: "value length must be between 0 and 4 runes, inclusive", + } + if !all { + return err + } + errors = append(errors, err) + } + + if l := utf8.RuneCountInString(m.GetDocumentId()); l < 0 || l > 100 { + err := PayoutReqValidationError{ + field: "DocumentId", + reason: "value length must be between 0 and 100 runes, inclusive", + } + if !all { + return err + } + errors = append(errors, err) + } + + if l := utf8.RuneCountInString(m.GetName()); l < 0 || l > 100 { + err := PayoutReqValidationError{ + field: "Name", + reason: "value length must be between 0 and 100 runes, inclusive", + } + if !all { + return err + } + errors = append(errors, err) + } + + if all { + switch v := interface{}(m.GetDataAdjust()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, PayoutReqValidationError{ + field: "DataAdjust", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, PayoutReqValidationError{ + field: "DataAdjust", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetDataAdjust()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return PayoutReqValidationError{ + field: "DataAdjust", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if all { + switch v := interface{}(m.GetDataShuShu()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, PayoutReqValidationError{ + field: "DataShuShu", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, PayoutReqValidationError{ + field: "DataShuShu", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetDataShuShu()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return PayoutReqValidationError{ + field: "DataShuShu", + reason: "embedded message failed validation", + cause: err, + } + } + } + + // no validation rules for ClientData + + // no validation rules for ClientName + + if utf8.RuneCountInString(m.GetEmail()) < 1 { + err := PayoutReqValidationError{ + field: "Email", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if len(errors) > 0 { + return PayoutReqMultiError(errors) + } + + return nil +} + +// PayoutReqMultiError is an error wrapping multiple validation errors returned +// by PayoutReq.ValidateAll() if the designated constraints aren't met. +type PayoutReqMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m PayoutReqMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m PayoutReqMultiError) AllErrors() []error { return m } + +// PayoutReqValidationError is the validation error returned by +// PayoutReq.Validate if the designated constraints aren't met. +type PayoutReqValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e PayoutReqValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e PayoutReqValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e PayoutReqValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e PayoutReqValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e PayoutReqValidationError) ErrorName() string { return "PayoutReqValidationError" } + +// Error satisfies the builtin error interface +func (e PayoutReqValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sPayoutReq.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = PayoutReqValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = PayoutReqValidationError{} + +// Validate checks the field values on PayoutReply with the rules defined in +// the proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *PayoutReply) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on PayoutReply with the rules defined in +// the proto definition for this message. If any rules are violated, the +// result is a list of violation errors wrapped in PayoutReplyMultiError, or +// nil if none found. +func (m *PayoutReply) ValidateAll() error { + return m.validate(true) +} + +func (m *PayoutReply) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for Id + + // no validation rules for RecordNo + + // no validation rules for Error + + if len(errors) > 0 { + return PayoutReplyMultiError(errors) + } + + return nil +} + +// PayoutReplyMultiError is an error wrapping multiple validation errors +// returned by PayoutReply.ValidateAll() if the designated constraints aren't met. +type PayoutReplyMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m PayoutReplyMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m PayoutReplyMultiError) AllErrors() []error { return m } + +// PayoutReplyValidationError is the validation error returned by +// PayoutReply.Validate if the designated constraints aren't met. +type PayoutReplyValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e PayoutReplyValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e PayoutReplyValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e PayoutReplyValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e PayoutReplyValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e PayoutReplyValidationError) ErrorName() string { return "PayoutReplyValidationError" } + +// Error satisfies the builtin error interface +func (e PayoutReplyValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sPayoutReply.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = PayoutReplyValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = PayoutReplyValidationError{} + +// Validate checks the field values on PayoutCallbackReq with the rules defined +// in the proto definition for this message. If any rules are violated, the +// first error encountered is returned, or nil if there are no violations. +func (m *PayoutCallbackReq) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on PayoutCallbackReq with the rules +// defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// PayoutCallbackReqMultiError, or nil if none found. +func (m *PayoutCallbackReq) ValidateAll() error { + return m.validate(true) +} + +func (m *PayoutCallbackReq) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for PayoutId + + // no validation rules for CustomCode + + // no validation rules for Status + + // no validation rules for Msg + + // no validation rules for Timestamp + + if len(errors) > 0 { + return PayoutCallbackReqMultiError(errors) + } + + return nil +} + +// PayoutCallbackReqMultiError is an error wrapping multiple validation errors +// returned by PayoutCallbackReq.ValidateAll() if the designated constraints +// aren't met. +type PayoutCallbackReqMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m PayoutCallbackReqMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m PayoutCallbackReqMultiError) AllErrors() []error { return m } + +// PayoutCallbackReqValidationError is the validation error returned by +// PayoutCallbackReq.Validate if the designated constraints aren't met. +type PayoutCallbackReqValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e PayoutCallbackReqValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e PayoutCallbackReqValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e PayoutCallbackReqValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e PayoutCallbackReqValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e PayoutCallbackReqValidationError) ErrorName() string { + return "PayoutCallbackReqValidationError" +} + +// Error satisfies the builtin error interface +func (e PayoutCallbackReqValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sPayoutCallbackReq.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = PayoutCallbackReqValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = PayoutCallbackReqValidationError{} + +// Validate checks the field values on PayoutCallbackReply with the rules +// defined in the proto definition for this message. If any rules are +// violated, the first error encountered is returned, or nil if there are no violations. +func (m *PayoutCallbackReply) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on PayoutCallbackReply with the rules +// defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// PayoutCallbackReplyMultiError, or nil if none found. +func (m *PayoutCallbackReply) ValidateAll() error { + return m.validate(true) +} + +func (m *PayoutCallbackReply) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for Message + + if len(errors) > 0 { + return PayoutCallbackReplyMultiError(errors) + } + + return nil +} + +// PayoutCallbackReplyMultiError is an error wrapping multiple validation +// errors returned by PayoutCallbackReply.ValidateAll() if the designated +// constraints aren't met. +type PayoutCallbackReplyMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m PayoutCallbackReplyMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m PayoutCallbackReplyMultiError) AllErrors() []error { return m } + +// PayoutCallbackReplyValidationError is the validation error returned by +// PayoutCallbackReply.Validate if the designated constraints aren't met. +type PayoutCallbackReplyValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e PayoutCallbackReplyValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e PayoutCallbackReplyValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e PayoutCallbackReplyValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e PayoutCallbackReplyValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e PayoutCallbackReplyValidationError) ErrorName() string { + return "PayoutCallbackReplyValidationError" +} + +// Error satisfies the builtin error interface +func (e PayoutCallbackReplyValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sPayoutCallbackReply.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = PayoutCallbackReplyValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = PayoutCallbackReplyValidationError{} + +// Validate checks the field values on PayoutCheckReq with the rules defined in +// the proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *PayoutCheckReq) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on PayoutCheckReq with the rules defined +// in the proto definition for this message. If any rules are violated, the +// result is a list of violation errors wrapped in PayoutCheckReqMultiError, +// or nil if none found. +func (m *PayoutCheckReq) ValidateAll() error { + return m.validate(true) +} + +func (m *PayoutCheckReq) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if utf8.RuneCountInString(m.GetPlatform()) < 1 { + err := PayoutCheckReqValidationError{ + field: "Platform", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetDeviceid()) < 1 { + err := PayoutCheckReqValidationError{ + field: "Deviceid", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetVersion()) < 1 { + err := PayoutCheckReqValidationError{ + field: "Version", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetTs()) < 1 { + err := PayoutCheckReqValidationError{ + field: "Ts", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetSign()) < 1 { + err := PayoutCheckReqValidationError{ + field: "Sign", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + // no validation rules for Ip + + if utf8.RuneCountInString(m.GetRecordNo()) < 1 { + err := PayoutCheckReqValidationError{ + field: "RecordNo", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if len(errors) > 0 { + return PayoutCheckReqMultiError(errors) + } + + return nil +} + +// PayoutCheckReqMultiError is an error wrapping multiple validation errors +// returned by PayoutCheckReq.ValidateAll() if the designated constraints +// aren't met. +type PayoutCheckReqMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m PayoutCheckReqMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m PayoutCheckReqMultiError) AllErrors() []error { return m } + +// PayoutCheckReqValidationError is the validation error returned by +// PayoutCheckReq.Validate if the designated constraints aren't met. +type PayoutCheckReqValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e PayoutCheckReqValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e PayoutCheckReqValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e PayoutCheckReqValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e PayoutCheckReqValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e PayoutCheckReqValidationError) ErrorName() string { return "PayoutCheckReqValidationError" } + +// Error satisfies the builtin error interface +func (e PayoutCheckReqValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sPayoutCheckReq.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = PayoutCheckReqValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = PayoutCheckReqValidationError{} + +// Validate checks the field values on PayoutCheckReply with the rules defined +// in the proto definition for this message. If any rules are violated, the +// first error encountered is returned, or nil if there are no violations. +func (m *PayoutCheckReply) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on PayoutCheckReply with the rules +// defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// PayoutCheckReplyMultiError, or nil if none found. +func (m *PayoutCheckReply) ValidateAll() error { + return m.validate(true) +} + +func (m *PayoutCheckReply) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for Status + + // no validation rules for Error + + if len(errors) > 0 { + return PayoutCheckReplyMultiError(errors) + } + + return nil +} + +// PayoutCheckReplyMultiError is an error wrapping multiple validation errors +// returned by PayoutCheckReply.ValidateAll() if the designated constraints +// aren't met. +type PayoutCheckReplyMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m PayoutCheckReplyMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m PayoutCheckReplyMultiError) AllErrors() []error { return m } + +// PayoutCheckReplyValidationError is the validation error returned by +// PayoutCheckReply.Validate if the designated constraints aren't met. +type PayoutCheckReplyValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e PayoutCheckReplyValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e PayoutCheckReplyValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e PayoutCheckReplyValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e PayoutCheckReplyValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e PayoutCheckReplyValidationError) ErrorName() string { return "PayoutCheckReplyValidationError" } + +// Error satisfies the builtin error interface +func (e PayoutCheckReplyValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sPayoutCheckReply.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = PayoutCheckReplyValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = PayoutCheckReplyValidationError{} + +// Validate checks the field values on PayoutUserLstReq with the rules defined +// in the proto definition for this message. If any rules are violated, the +// first error encountered is returned, or nil if there are no violations. +func (m *PayoutUserLstReq) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on PayoutUserLstReq with the rules +// defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// PayoutUserLstReqMultiError, or nil if none found. +func (m *PayoutUserLstReq) ValidateAll() error { + return m.validate(true) +} + +func (m *PayoutUserLstReq) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if utf8.RuneCountInString(m.GetPlatform()) < 1 { + err := PayoutUserLstReqValidationError{ + field: "Platform", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetDeviceid()) < 1 { + err := PayoutUserLstReqValidationError{ + field: "Deviceid", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetVersion()) < 1 { + err := PayoutUserLstReqValidationError{ + field: "Version", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetTs()) < 1 { + err := PayoutUserLstReqValidationError{ + field: "Ts", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetSign()) < 1 { + err := PayoutUserLstReqValidationError{ + field: "Sign", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if val := m.GetStatus(); val < 1 || val > 3 { + err := PayoutUserLstReqValidationError{ + field: "Status", + reason: "value must be inside range [1, 3]", + } + if !all { + return err + } + errors = append(errors, err) + } + + // no validation rules for PageIndex + + // no validation rules for PageSize + + if len(errors) > 0 { + return PayoutUserLstReqMultiError(errors) + } + + return nil +} + +// PayoutUserLstReqMultiError is an error wrapping multiple validation errors +// returned by PayoutUserLstReq.ValidateAll() if the designated constraints +// aren't met. +type PayoutUserLstReqMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m PayoutUserLstReqMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m PayoutUserLstReqMultiError) AllErrors() []error { return m } + +// PayoutUserLstReqValidationError is the validation error returned by +// PayoutUserLstReq.Validate if the designated constraints aren't met. +type PayoutUserLstReqValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e PayoutUserLstReqValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e PayoutUserLstReqValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e PayoutUserLstReqValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e PayoutUserLstReqValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e PayoutUserLstReqValidationError) ErrorName() string { return "PayoutUserLstReqValidationError" } + +// Error satisfies the builtin error interface +func (e PayoutUserLstReqValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sPayoutUserLstReq.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = PayoutUserLstReqValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = PayoutUserLstReqValidationError{} + +// Validate checks the field values on PayoutUserLstReply with the rules +// defined in the proto definition for this message. If any rules are +// violated, the first error encountered is returned, or nil if there are no violations. +func (m *PayoutUserLstReply) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on PayoutUserLstReply with the rules +// defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// PayoutUserLstReplyMultiError, or nil if none found. +func (m *PayoutUserLstReply) ValidateAll() error { + return m.validate(true) +} + +func (m *PayoutUserLstReply) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + for idx, item := range m.GetLst() { + _, _ = idx, item + + if all { + switch v := interface{}(item).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, PayoutUserLstReplyValidationError{ + field: fmt.Sprintf("Lst[%v]", idx), + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, PayoutUserLstReplyValidationError{ + field: fmt.Sprintf("Lst[%v]", idx), + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(item).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return PayoutUserLstReplyValidationError{ + field: fmt.Sprintf("Lst[%v]", idx), + reason: "embedded message failed validation", + cause: err, + } + } + } + + } + + // no validation rules for Error + + if len(errors) > 0 { + return PayoutUserLstReplyMultiError(errors) + } + + return nil +} + +// PayoutUserLstReplyMultiError is an error wrapping multiple validation errors +// returned by PayoutUserLstReply.ValidateAll() if the designated constraints +// aren't met. +type PayoutUserLstReplyMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m PayoutUserLstReplyMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m PayoutUserLstReplyMultiError) AllErrors() []error { return m } + +// PayoutUserLstReplyValidationError is the validation error returned by +// PayoutUserLstReply.Validate if the designated constraints aren't met. +type PayoutUserLstReplyValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e PayoutUserLstReplyValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e PayoutUserLstReplyValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e PayoutUserLstReplyValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e PayoutUserLstReplyValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e PayoutUserLstReplyValidationError) ErrorName() string { + return "PayoutUserLstReplyValidationError" +} + +// Error satisfies the builtin error interface +func (e PayoutUserLstReplyValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sPayoutUserLstReply.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = PayoutUserLstReplyValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = PayoutUserLstReplyValidationError{} + +// Validate checks the field values on PayoutUserOne with the rules defined in +// the proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *PayoutUserOne) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on PayoutUserOne with the rules defined +// in the proto definition for this message. If any rules are violated, the +// result is a list of violation errors wrapped in PayoutUserOneMultiError, or +// nil if none found. +func (m *PayoutUserOne) ValidateAll() error { + return m.validate(true) +} + +func (m *PayoutUserOne) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for Email + + // no validation rules for RecordNo + + // no validation rules for Account + + // no validation rules for Status + + if len(errors) > 0 { + return PayoutUserOneMultiError(errors) + } + + return nil +} + +// PayoutUserOneMultiError is an error wrapping multiple validation errors +// returned by PayoutUserOne.ValidateAll() if the designated constraints +// aren't met. +type PayoutUserOneMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m PayoutUserOneMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m PayoutUserOneMultiError) AllErrors() []error { return m } + +// PayoutUserOneValidationError is the validation error returned by +// PayoutUserOne.Validate if the designated constraints aren't met. +type PayoutUserOneValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e PayoutUserOneValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e PayoutUserOneValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e PayoutUserOneValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e PayoutUserOneValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e PayoutUserOneValidationError) ErrorName() string { return "PayoutUserOneValidationError" } + +// Error satisfies the builtin error interface +func (e PayoutUserOneValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sPayoutUserOne.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = PayoutUserOneValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = PayoutUserOneValidationError{} + +// Validate checks the field values on PayoutStatusReq with the rules defined +// in the proto definition for this message. If any rules are violated, the +// first error encountered is returned, or nil if there are no violations. +func (m *PayoutStatusReq) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on PayoutStatusReq with the rules +// defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// PayoutStatusReqMultiError, or nil if none found. +func (m *PayoutStatusReq) ValidateAll() error { + return m.validate(true) +} + +func (m *PayoutStatusReq) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if utf8.RuneCountInString(m.GetPlatform()) < 1 { + err := PayoutStatusReqValidationError{ + field: "Platform", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetDeviceid()) < 1 { + err := PayoutStatusReqValidationError{ + field: "Deviceid", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetVersion()) < 1 { + err := PayoutStatusReqValidationError{ + field: "Version", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetTs()) < 1 { + err := PayoutStatusReqValidationError{ + field: "Ts", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetSign()) < 1 { + err := PayoutStatusReqValidationError{ + field: "Sign", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetRecordNo()) < 1 { + err := PayoutStatusReqValidationError{ + field: "RecordNo", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + // no validation rules for Fail + + if val := m.GetStatus(); val < 2 || val > 3 { + err := PayoutStatusReqValidationError{ + field: "Status", + reason: "value must be inside range [2, 3]", + } + if !all { + return err + } + errors = append(errors, err) + } + + if len(errors) > 0 { + return PayoutStatusReqMultiError(errors) + } + + return nil +} + +// PayoutStatusReqMultiError is an error wrapping multiple validation errors +// returned by PayoutStatusReq.ValidateAll() if the designated constraints +// aren't met. +type PayoutStatusReqMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m PayoutStatusReqMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m PayoutStatusReqMultiError) AllErrors() []error { return m } + +// PayoutStatusReqValidationError is the validation error returned by +// PayoutStatusReq.Validate if the designated constraints aren't met. +type PayoutStatusReqValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e PayoutStatusReqValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e PayoutStatusReqValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e PayoutStatusReqValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e PayoutStatusReqValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e PayoutStatusReqValidationError) ErrorName() string { return "PayoutStatusReqValidationError" } + +// Error satisfies the builtin error interface +func (e PayoutStatusReqValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sPayoutStatusReq.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = PayoutStatusReqValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = PayoutStatusReqValidationError{} + +// Validate checks the field values on PayoutStatusReply with the rules defined +// in the proto definition for this message. If any rules are violated, the +// first error encountered is returned, or nil if there are no violations. +func (m *PayoutStatusReply) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on PayoutStatusReply with the rules +// defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// PayoutStatusReplyMultiError, or nil if none found. +func (m *PayoutStatusReply) ValidateAll() error { + return m.validate(true) +} + +func (m *PayoutStatusReply) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for RecordNo + + // no validation rules for Status + + // no validation rules for Error + + if len(errors) > 0 { + return PayoutStatusReplyMultiError(errors) + } + + return nil +} + +// PayoutStatusReplyMultiError is an error wrapping multiple validation errors +// returned by PayoutStatusReply.ValidateAll() if the designated constraints +// aren't met. +type PayoutStatusReplyMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m PayoutStatusReplyMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m PayoutStatusReplyMultiError) AllErrors() []error { return m } + +// PayoutStatusReplyValidationError is the validation error returned by +// PayoutStatusReply.Validate if the designated constraints aren't met. +type PayoutStatusReplyValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e PayoutStatusReplyValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e PayoutStatusReplyValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e PayoutStatusReplyValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e PayoutStatusReplyValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e PayoutStatusReplyValidationError) ErrorName() string { + return "PayoutStatusReplyValidationError" +} + +// Error satisfies the builtin error interface +func (e PayoutStatusReplyValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sPayoutStatusReply.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = PayoutStatusReplyValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = PayoutStatusReplyValidationError{} + +// Validate checks the field values on SubmitCheckReq with the rules defined in +// the proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *SubmitCheckReq) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on SubmitCheckReq with the rules defined +// in the proto definition for this message. If any rules are violated, the +// result is a list of violation errors wrapped in SubmitCheckReqMultiError, +// or nil if none found. +func (m *SubmitCheckReq) ValidateAll() error { + return m.validate(true) +} + +func (m *SubmitCheckReq) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if utf8.RuneCountInString(m.GetAccount()) < 1 { + err := SubmitCheckReqValidationError{ + field: "Account", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetUuid()) < 1 { + err := SubmitCheckReqValidationError{ + field: "Uuid", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if len(errors) > 0 { + return SubmitCheckReqMultiError(errors) + } + + return nil +} + +// SubmitCheckReqMultiError is an error wrapping multiple validation errors +// returned by SubmitCheckReq.ValidateAll() if the designated constraints +// aren't met. +type SubmitCheckReqMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m SubmitCheckReqMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m SubmitCheckReqMultiError) AllErrors() []error { return m } + +// SubmitCheckReqValidationError is the validation error returned by +// SubmitCheckReq.Validate if the designated constraints aren't met. +type SubmitCheckReqValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e SubmitCheckReqValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e SubmitCheckReqValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e SubmitCheckReqValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e SubmitCheckReqValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e SubmitCheckReqValidationError) ErrorName() string { return "SubmitCheckReqValidationError" } + +// Error satisfies the builtin error interface +func (e SubmitCheckReqValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sSubmitCheckReq.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = SubmitCheckReqValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = SubmitCheckReqValidationError{} + +// Validate checks the field values on SubmitCheckReply with the rules defined +// in the proto definition for this message. If any rules are violated, the +// first error encountered is returned, or nil if there are no violations. +func (m *SubmitCheckReply) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on SubmitCheckReply with the rules +// defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// SubmitCheckReplyMultiError, or nil if none found. +func (m *SubmitCheckReply) ValidateAll() error { + return m.validate(true) +} + +func (m *SubmitCheckReply) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for Result + + // no validation rules for Error + + if len(errors) > 0 { + return SubmitCheckReplyMultiError(errors) + } + + return nil +} + +// SubmitCheckReplyMultiError is an error wrapping multiple validation errors +// returned by SubmitCheckReply.ValidateAll() if the designated constraints +// aren't met. +type SubmitCheckReplyMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m SubmitCheckReplyMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m SubmitCheckReplyMultiError) AllErrors() []error { return m } + +// SubmitCheckReplyValidationError is the validation error returned by +// SubmitCheckReply.Validate if the designated constraints aren't met. +type SubmitCheckReplyValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e SubmitCheckReplyValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e SubmitCheckReplyValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e SubmitCheckReplyValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e SubmitCheckReplyValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e SubmitCheckReplyValidationError) ErrorName() string { return "SubmitCheckReplyValidationError" } + +// Error satisfies the builtin error interface +func (e SubmitCheckReplyValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sSubmitCheckReply.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = SubmitCheckReplyValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = SubmitCheckReplyValidationError{} + +// Validate checks the field values on CheckInfoReq with the rules defined in +// the proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *CheckInfoReq) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on CheckInfoReq with the rules defined +// in the proto definition for this message. If any rules are violated, the +// result is a list of violation errors wrapped in CheckInfoReqMultiError, or +// nil if none found. +func (m *CheckInfoReq) ValidateAll() error { + return m.validate(true) +} + +func (m *CheckInfoReq) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if utf8.RuneCountInString(m.GetPlatform()) < 1 { + err := CheckInfoReqValidationError{ + field: "Platform", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetDeviceid()) < 1 { + err := CheckInfoReqValidationError{ + field: "Deviceid", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetVersion()) < 1 { + err := CheckInfoReqValidationError{ + field: "Version", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + // no validation rules for Ip + + if utf8.RuneCountInString(m.GetTs()) < 1 { + err := CheckInfoReqValidationError{ + field: "Ts", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetSign()) < 1 { + err := CheckInfoReqValidationError{ + field: "Sign", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + // no validation rules for Uuid + + // no validation rules for IsVerificationShow + + if len(errors) > 0 { + return CheckInfoReqMultiError(errors) + } + + return nil +} + +// CheckInfoReqMultiError is an error wrapping multiple validation errors +// returned by CheckInfoReq.ValidateAll() if the designated constraints aren't met. +type CheckInfoReqMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m CheckInfoReqMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m CheckInfoReqMultiError) AllErrors() []error { return m } + +// CheckInfoReqValidationError is the validation error returned by +// CheckInfoReq.Validate if the designated constraints aren't met. +type CheckInfoReqValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e CheckInfoReqValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e CheckInfoReqValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e CheckInfoReqValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e CheckInfoReqValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e CheckInfoReqValidationError) ErrorName() string { return "CheckInfoReqValidationError" } + +// Error satisfies the builtin error interface +func (e CheckInfoReqValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sCheckInfoReq.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = CheckInfoReqValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = CheckInfoReqValidationError{} + +// Validate checks the field values on CheckInfoReply with the rules defined in +// the proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *CheckInfoReply) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on CheckInfoReply with the rules defined +// in the proto definition for this message. If any rules are violated, the +// result is a list of violation errors wrapped in CheckInfoReplyMultiError, +// or nil if none found. +func (m *CheckInfoReply) ValidateAll() error { + return m.validate(true) +} + +func (m *CheckInfoReply) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for CanCheckSubmit + + // no validation rules for CheckSubmit + + // no validation rules for CheckResult + + // no validation rules for CheckPayout + + // no validation rules for CheckCoin + + // no validation rules for CanCheckPayOut + + // no validation rules for CheckResultFailedDesc + + // no validation rules for Error + + if len(errors) > 0 { + return CheckInfoReplyMultiError(errors) + } + + return nil +} + +// CheckInfoReplyMultiError is an error wrapping multiple validation errors +// returned by CheckInfoReply.ValidateAll() if the designated constraints +// aren't met. +type CheckInfoReplyMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m CheckInfoReplyMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m CheckInfoReplyMultiError) AllErrors() []error { return m } + +// CheckInfoReplyValidationError is the validation error returned by +// CheckInfoReply.Validate if the designated constraints aren't met. +type CheckInfoReplyValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e CheckInfoReplyValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e CheckInfoReplyValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e CheckInfoReplyValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e CheckInfoReplyValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e CheckInfoReplyValidationError) ErrorName() string { return "CheckInfoReplyValidationError" } + +// Error satisfies the builtin error interface +func (e CheckInfoReplyValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sCheckInfoReply.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = CheckInfoReplyValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = CheckInfoReplyValidationError{} + +// Validate checks the field values on PbMsgOne with the rules defined in the +// proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *PbMsgOne) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on PbMsgOne with the rules defined in +// the proto definition for this message. If any rules are violated, the +// result is a list of violation errors wrapped in PbMsgOneMultiError, or nil +// if none found. +func (m *PbMsgOne) ValidateAll() error { + return m.validate(true) +} + +func (m *PbMsgOne) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for TimeStamp + + // no validation rules for Uuid + + // no validation rules for Name + + // no validation rules for Msg + + if len(errors) > 0 { + return PbMsgOneMultiError(errors) + } + + return nil +} + +// PbMsgOneMultiError is an error wrapping multiple validation errors returned +// by PbMsgOne.ValidateAll() if the designated constraints aren't met. +type PbMsgOneMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m PbMsgOneMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m PbMsgOneMultiError) AllErrors() []error { return m } + +// PbMsgOneValidationError is the validation error returned by +// PbMsgOne.Validate if the designated constraints aren't met. +type PbMsgOneValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e PbMsgOneValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e PbMsgOneValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e PbMsgOneValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e PbMsgOneValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e PbMsgOneValidationError) ErrorName() string { return "PbMsgOneValidationError" } + +// Error satisfies the builtin error interface +func (e PbMsgOneValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sPbMsgOne.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = PbMsgOneValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = PbMsgOneValidationError{} + +// Validate checks the field values on AddChatReq with the rules defined in the +// proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *AddChatReq) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on AddChatReq with the rules defined in +// the proto definition for this message. If any rules are violated, the +// result is a list of violation errors wrapped in AddChatReqMultiError, or +// nil if none found. +func (m *AddChatReq) ValidateAll() error { + return m.validate(true) +} + +func (m *AddChatReq) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for TimeStamp + + if utf8.RuneCountInString(m.GetPlatform()) < 1 { + err := AddChatReqValidationError{ + field: "Platform", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetDeviceid()) < 1 { + err := AddChatReqValidationError{ + field: "Deviceid", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetVersion()) < 1 { + err := AddChatReqValidationError{ + field: "Version", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + // no validation rules for Ip + + if utf8.RuneCountInString(m.GetTs()) < 1 { + err := AddChatReqValidationError{ + field: "Ts", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetSign()) < 1 { + err := AddChatReqValidationError{ + field: "Sign", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + // no validation rules for Uuid + + // no validation rules for Msg + + if len(errors) > 0 { + return AddChatReqMultiError(errors) + } + + return nil +} + +// AddChatReqMultiError is an error wrapping multiple validation errors +// returned by AddChatReq.ValidateAll() if the designated constraints aren't met. +type AddChatReqMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m AddChatReqMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m AddChatReqMultiError) AllErrors() []error { return m } + +// AddChatReqValidationError is the validation error returned by +// AddChatReq.Validate if the designated constraints aren't met. +type AddChatReqValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e AddChatReqValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e AddChatReqValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e AddChatReqValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e AddChatReqValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e AddChatReqValidationError) ErrorName() string { return "AddChatReqValidationError" } + +// Error satisfies the builtin error interface +func (e AddChatReqValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sAddChatReq.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = AddChatReqValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = AddChatReqValidationError{} + +// Validate checks the field values on AddChatReply with the rules defined in +// the proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *AddChatReply) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on AddChatReply with the rules defined +// in the proto definition for this message. If any rules are violated, the +// result is a list of violation errors wrapped in AddChatReplyMultiError, or +// nil if none found. +func (m *AddChatReply) ValidateAll() error { + return m.validate(true) +} + +func (m *AddChatReply) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for Result + + // no validation rules for Uuid + + for idx, item := range m.GetLst() { + _, _ = idx, item + + if all { + switch v := interface{}(item).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, AddChatReplyValidationError{ + field: fmt.Sprintf("Lst[%v]", idx), + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, AddChatReplyValidationError{ + field: fmt.Sprintf("Lst[%v]", idx), + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(item).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return AddChatReplyValidationError{ + field: fmt.Sprintf("Lst[%v]", idx), + reason: "embedded message failed validation", + cause: err, + } + } + } + + } + + // no validation rules for Error + + if len(errors) > 0 { + return AddChatReplyMultiError(errors) + } + + return nil +} + +// AddChatReplyMultiError is an error wrapping multiple validation errors +// returned by AddChatReply.ValidateAll() if the designated constraints aren't met. +type AddChatReplyMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m AddChatReplyMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m AddChatReplyMultiError) AllErrors() []error { return m } + +// AddChatReplyValidationError is the validation error returned by +// AddChatReply.Validate if the designated constraints aren't met. +type AddChatReplyValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e AddChatReplyValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e AddChatReplyValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e AddChatReplyValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e AddChatReplyValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e AddChatReplyValidationError) ErrorName() string { return "AddChatReplyValidationError" } + +// Error satisfies the builtin error interface +func (e AddChatReplyValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sAddChatReply.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = AddChatReplyValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = AddChatReplyValidationError{} + +// Validate checks the field values on GetChatReq with the rules defined in the +// proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *GetChatReq) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on GetChatReq with the rules defined in +// the proto definition for this message. If any rules are violated, the +// result is a list of violation errors wrapped in GetChatReqMultiError, or +// nil if none found. +func (m *GetChatReq) ValidateAll() error { + return m.validate(true) +} + +func (m *GetChatReq) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for TimeStamp + + if utf8.RuneCountInString(m.GetPlatform()) < 1 { + err := GetChatReqValidationError{ + field: "Platform", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetDeviceid()) < 1 { + err := GetChatReqValidationError{ + field: "Deviceid", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetVersion()) < 1 { + err := GetChatReqValidationError{ + field: "Version", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + // no validation rules for Ip + + if utf8.RuneCountInString(m.GetTs()) < 1 { + err := GetChatReqValidationError{ + field: "Ts", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetSign()) < 1 { + err := GetChatReqValidationError{ + field: "Sign", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + // no validation rules for Uuid + + if len(errors) > 0 { + return GetChatReqMultiError(errors) + } + + return nil +} + +// GetChatReqMultiError is an error wrapping multiple validation errors +// returned by GetChatReq.ValidateAll() if the designated constraints aren't met. +type GetChatReqMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m GetChatReqMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m GetChatReqMultiError) AllErrors() []error { return m } + +// GetChatReqValidationError is the validation error returned by +// GetChatReq.Validate if the designated constraints aren't met. +type GetChatReqValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e GetChatReqValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e GetChatReqValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e GetChatReqValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e GetChatReqValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e GetChatReqValidationError) ErrorName() string { return "GetChatReqValidationError" } + +// Error satisfies the builtin error interface +func (e GetChatReqValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sGetChatReq.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = GetChatReqValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = GetChatReqValidationError{} + +// Validate checks the field values on GetChatReply with the rules defined in +// the proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *GetChatReply) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on GetChatReply with the rules defined +// in the proto definition for this message. If any rules are violated, the +// result is a list of violation errors wrapped in GetChatReplyMultiError, or +// nil if none found. +func (m *GetChatReply) ValidateAll() error { + return m.validate(true) +} + +func (m *GetChatReply) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for Uuid + + for idx, item := range m.GetLst() { + _, _ = idx, item + + if all { + switch v := interface{}(item).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, GetChatReplyValidationError{ + field: fmt.Sprintf("Lst[%v]", idx), + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, GetChatReplyValidationError{ + field: fmt.Sprintf("Lst[%v]", idx), + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(item).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return GetChatReplyValidationError{ + field: fmt.Sprintf("Lst[%v]", idx), + reason: "embedded message failed validation", + cause: err, + } + } + } + + } + + // no validation rules for Error + + if len(errors) > 0 { + return GetChatReplyMultiError(errors) + } + + return nil +} + +// GetChatReplyMultiError is an error wrapping multiple validation errors +// returned by GetChatReply.ValidateAll() if the designated constraints aren't met. +type GetChatReplyMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m GetChatReplyMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m GetChatReplyMultiError) AllErrors() []error { return m } + +// GetChatReplyValidationError is the validation error returned by +// GetChatReply.Validate if the designated constraints aren't met. +type GetChatReplyValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e GetChatReplyValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e GetChatReplyValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e GetChatReplyValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e GetChatReplyValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e GetChatReplyValidationError) ErrorName() string { return "GetChatReplyValidationError" } + +// Error satisfies the builtin error interface +func (e GetChatReplyValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sGetChatReply.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = GetChatReplyValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = GetChatReplyValidationError{} + +// Validate checks the field values on PbSvrData with the rules defined in the +// proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *PbSvrData) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on PbSvrData with the rules defined in +// the proto definition for this message. If any rules are violated, the +// result is a list of violation errors wrapped in PbSvrDataMultiError, or nil +// if none found. +func (m *PbSvrData) ValidateAll() error { + return m.validate(true) +} + +func (m *PbSvrData) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + for idx, item := range m.GetLstChat() { + _, _ = idx, item + + if all { + switch v := interface{}(item).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, PbSvrDataValidationError{ + field: fmt.Sprintf("LstChat[%v]", idx), + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, PbSvrDataValidationError{ + field: fmt.Sprintf("LstChat[%v]", idx), + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(item).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return PbSvrDataValidationError{ + field: fmt.Sprintf("LstChat[%v]", idx), + reason: "embedded message failed validation", + cause: err, + } + } + } + + } + + if len(errors) > 0 { + return PbSvrDataMultiError(errors) + } + + return nil +} + +// PbSvrDataMultiError is an error wrapping multiple validation errors returned +// by PbSvrData.ValidateAll() if the designated constraints aren't met. +type PbSvrDataMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m PbSvrDataMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m PbSvrDataMultiError) AllErrors() []error { return m } + +// PbSvrDataValidationError is the validation error returned by +// PbSvrData.Validate if the designated constraints aren't met. +type PbSvrDataValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e PbSvrDataValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e PbSvrDataValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e PbSvrDataValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e PbSvrDataValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e PbSvrDataValidationError) ErrorName() string { return "PbSvrDataValidationError" } + +// Error satisfies the builtin error interface +func (e PbSvrDataValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sPbSvrData.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = PbSvrDataValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = PbSvrDataValidationError{} + +// Validate checks the field values on PbUserData with the rules defined in the +// proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *PbUserData) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on PbUserData with the rules defined in +// the proto definition for this message. If any rules are violated, the +// result is a list of violation errors wrapped in PbUserDataMultiError, or +// nil if none found. +func (m *PbUserData) ValidateAll() error { + return m.validate(true) +} + +func (m *PbUserData) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if len(errors) > 0 { + return PbUserDataMultiError(errors) + } + + return nil +} + +// PbUserDataMultiError is an error wrapping multiple validation errors +// returned by PbUserData.ValidateAll() if the designated constraints aren't met. +type PbUserDataMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m PbUserDataMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m PbUserDataMultiError) AllErrors() []error { return m } + +// PbUserDataValidationError is the validation error returned by +// PbUserData.Validate if the designated constraints aren't met. +type PbUserDataValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e PbUserDataValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e PbUserDataValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e PbUserDataValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e PbUserDataValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e PbUserDataValidationError) ErrorName() string { return "PbUserDataValidationError" } + +// Error satisfies the builtin error interface +func (e PbUserDataValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sPbUserData.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = PbUserDataValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = PbUserDataValidationError{} + +// Validate checks the field values on PbReportData with the rules defined in +// the proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *PbReportData) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on PbReportData with the rules defined +// in the proto definition for this message. If any rules are violated, the +// result is a list of violation errors wrapped in PbReportDataMultiError, or +// nil if none found. +func (m *PbReportData) ValidateAll() error { + return m.validate(true) +} + +func (m *PbReportData) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if all { + switch v := interface{}(m.GetAdjust()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, PbReportDataValidationError{ + field: "Adjust", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, PbReportDataValidationError{ + field: "Adjust", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetAdjust()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return PbReportDataValidationError{ + field: "Adjust", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if all { + switch v := interface{}(m.GetShuShu()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, PbReportDataValidationError{ + field: "ShuShu", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, PbReportDataValidationError{ + field: "ShuShu", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetShuShu()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return PbReportDataValidationError{ + field: "ShuShu", + reason: "embedded message failed validation", + cause: err, + } + } + } + + // no validation rules for Rf + + if len(errors) > 0 { + return PbReportDataMultiError(errors) + } + + return nil +} + +// PbReportDataMultiError is an error wrapping multiple validation errors +// returned by PbReportData.ValidateAll() if the designated constraints aren't met. +type PbReportDataMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m PbReportDataMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m PbReportDataMultiError) AllErrors() []error { return m } + +// PbReportDataValidationError is the validation error returned by +// PbReportData.Validate if the designated constraints aren't met. +type PbReportDataValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e PbReportDataValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e PbReportDataValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e PbReportDataValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e PbReportDataValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e PbReportDataValidationError) ErrorName() string { return "PbReportDataValidationError" } + +// Error satisfies the builtin error interface +func (e PbReportDataValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sPbReportData.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = PbReportDataValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = PbReportDataValidationError{} + +// Validate checks the field values on PbAdjustData with the rules defined in +// the proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *PbAdjustData) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on PbAdjustData with the rules defined +// in the proto definition for this message. If any rules are violated, the +// result is a list of violation errors wrapped in PbAdjustDataMultiError, or +// nil if none found. +func (m *PbAdjustData) ValidateAll() error { + return m.validate(true) +} + +func (m *PbAdjustData) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for GpsAdid + + // no validation rules for Adid + + // no validation rules for AndroidId + + // no validation rules for IpAddress + + // no validation rules for CreatedAtUnix + + // no validation rules for Currency + + // no validation rules for Environment + + // no validation rules for UserAgent + + // no validation rules for Price + + // no validation rules for FailReason + + // no validation rules for AppToken + + // no validation rules for EventToken + + // no validation rules for S2S + + // no validation rules for ClientName + + if len(errors) > 0 { + return PbAdjustDataMultiError(errors) + } + + return nil +} + +// PbAdjustDataMultiError is an error wrapping multiple validation errors +// returned by PbAdjustData.ValidateAll() if the designated constraints aren't met. +type PbAdjustDataMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m PbAdjustDataMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m PbAdjustDataMultiError) AllErrors() []error { return m } + +// PbAdjustDataValidationError is the validation error returned by +// PbAdjustData.Validate if the designated constraints aren't met. +type PbAdjustDataValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e PbAdjustDataValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e PbAdjustDataValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e PbAdjustDataValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e PbAdjustDataValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e PbAdjustDataValidationError) ErrorName() string { return "PbAdjustDataValidationError" } + +// Error satisfies the builtin error interface +func (e PbAdjustDataValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sPbAdjustData.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = PbAdjustDataValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = PbAdjustDataValidationError{} + +// Validate checks the field values on PbShuShuData with the rules defined in +// the proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *PbShuShuData) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on PbShuShuData with the rules defined +// in the proto definition for this message. If any rules are violated, the +// result is a list of violation errors wrapped in PbShuShuDataMultiError, or +// nil if none found. +func (m *PbShuShuData) ValidateAll() error { + return m.validate(true) +} + +func (m *PbShuShuData) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for GpsAdid + + // no validation rules for AppToken + + // no validation rules for EventToken + + // no validation rules for S2S + + // no validation rules for AndroidId + + // no validation rules for Adid + + // no validation rules for IpAddress + + // no validation rules for CreatedAtUnix + + // no validation rules for UserAgent + + // no validation rules for Price + + // no validation rules for Currency + + // no validation rules for FailReason + + // no validation rules for PayoutId + + // no validation rules for MerchantReference + + // no validation rules for PaymentMethod + + // no validation rules for PaymentType + + // no validation rules for PaymentNumber + + // no validation rules for IapName + + // no validation rules for GamecoinNumber + + // no validation rules for GamecoinType + + // no validation rules for SsAccountId + + // no validation rules for SsDistinctId + + // no validation rules for SsSuperProperties + + // no validation rules for ClientName + + if len(errors) > 0 { + return PbShuShuDataMultiError(errors) + } + + return nil +} + +// PbShuShuDataMultiError is an error wrapping multiple validation errors +// returned by PbShuShuData.ValidateAll() if the designated constraints aren't met. +type PbShuShuDataMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m PbShuShuDataMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m PbShuShuDataMultiError) AllErrors() []error { return m } + +// PbShuShuDataValidationError is the validation error returned by +// PbShuShuData.Validate if the designated constraints aren't met. +type PbShuShuDataValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e PbShuShuDataValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e PbShuShuDataValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e PbShuShuDataValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e PbShuShuDataValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e PbShuShuDataValidationError) ErrorName() string { return "PbShuShuDataValidationError" } + +// Error satisfies the builtin error interface +func (e PbShuShuDataValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sPbShuShuData.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = PbShuShuDataValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = PbShuShuDataValidationError{} + +// Validate checks the field values on PayInitReply_Item with the rules defined +// in the proto definition for this message. If any rules are violated, the +// first error encountered is returned, or nil if there are no violations. +func (m *PayInitReply_Item) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on PayInitReply_Item with the rules +// defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// PayInitReply_ItemMultiError, or nil if none found. +func (m *PayInitReply_Item) ValidateAll() error { + return m.validate(true) +} + +func (m *PayInitReply_Item) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for Id + + // no validation rules for Amount + + // no validation rules for Status + + if len(errors) > 0 { + return PayInitReply_ItemMultiError(errors) + } + + return nil +} + +// PayInitReply_ItemMultiError is an error wrapping multiple validation errors +// returned by PayInitReply_Item.ValidateAll() if the designated constraints +// aren't met. +type PayInitReply_ItemMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m PayInitReply_ItemMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m PayInitReply_ItemMultiError) AllErrors() []error { return m } + +// PayInitReply_ItemValidationError is the validation error returned by +// PayInitReply_Item.Validate if the designated constraints aren't met. +type PayInitReply_ItemValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e PayInitReply_ItemValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e PayInitReply_ItemValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e PayInitReply_ItemValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e PayInitReply_ItemValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e PayInitReply_ItemValidationError) ErrorName() string { + return "PayInitReply_ItemValidationError" +} + +// Error satisfies the builtin error interface +func (e PayInitReply_ItemValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sPayInitReply_Item.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = PayInitReply_ItemValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = PayInitReply_ItemValidationError{} diff --git a/api/eonline/v1/pagsmile.proto b/api/eonline/v1/pagsmile.proto new file mode 100644 index 0000000..1d581c6 --- /dev/null +++ b/api/eonline/v1/pagsmile.proto @@ -0,0 +1,308 @@ +syntax = "proto3"; + +package api.eonline.v1; + +import "validate/validate.proto"; + +option go_package = "sandc/api/eonline/v1;v1"; + +// PayInitReq init request +message PayInitReq { + string platform = 1 [(validate.rules).string.min_len = 1]; + string deviceid = 2 [(validate.rules).string.min_len = 1]; + string version = 3 [(validate.rules).string.min_len = 1]; // 版本号为三段式,目前起始版本号为2.0.0,后面2段每段最大长度为2位,即后面2段最大为99.99 + string ip = 4; // 测试时用,可以不传 + string ts = 5 [(validate.rules).string.min_len = 1]; // utc时间秒,客户端发送消息时的本地时间;在测试模式下,服务器会把ts当成登录时间计算,可用于改变登录日期 + string sign = 6 [(validate.rules).string.min_len = 1]; +} + +// PayInitReply init reply +message PayInitReply { + string uuid = 1; // 用户唯一字符串 + message Item { + uint32 id = 1; // 产品id + double amount = 2; // 产品价格 + uint32 status = 3; // 状态:1可提现 2:条件未达成 3:当天该产品id已提现成功, 4:禁止提现, 5:提现中 + } + uint32 days = 2; // 新增,登录天数,从1开始 + repeated Item items = 3; // 提现情况 + int32 CanCheckSubmit = 4; // 0提交审核的人数已满,不能提交了,1可以提交身份审核 + int32 CheckSubmit = 5; // 提交身份文件验证情况,0没有提交,1提交过 + int32 CheckResult = 6; // 身份文件审核有反馈的情况,0没有记录,1审核没通过,2审核通过 + int32 CheckPayout = 7; // 提交身份文件奖励5美元领取的情况,0没有提现记录,1提现中,2提现成功,3提现失败 + int32 CheckCoin = 8; // 身份文件审核过的提现奖励,美分 + int32 CanCheckPayOut = 9; // 身份文件审核过的提现奖励5美元 能否 提现,0能提现,1条件不满足,不能提现,2当天已经提现过,不能再次提现 + string CheckResultFailedDesc = 10; // CheckResult==1时,显示的审核没通过的原因描述信息 + int32 error = 11; // 错误码,0成功,1失败,2签名验证失败,3客户端版本过低,4 ts长度错误 + string clientData = 12; // 客户端上传、需要保存的数据 +} + +message PbReportDataAdjust { + string gps_adid = 1; // 用户的gaid + string android_id = 2; // 原始安卓 ID + string adid = 3; // 与设备关联的 Adjust 标识符 + string user_agent = 4; // 设备的User-Agent。必须进行 URL 编码。 + string price = 5; // 客户端上报的价格 客户端上报的价格,例如0.05 + string currency = 6; // 货币单位 客户端上报的货币,例如USD +} + +message PbReportDataShuShu { + string gps_gaid = 1; // 用户的gaid + string android_id = 2; // 原始安卓 ID + string adid = 3; // 与设备关联的 Adjust 标识符 + string user_agent = 4; // 设备的User-Agent。必须进行 URL 编码 + string price = 5; // 客户端上报的价格 客户端上报的价格,例如0.05 + string currency = 6; // 货币单位 客户端上报的货币,例如USD + string payment_method = 7; // 收款方式 暂时只有一种:pix + string payment_type = 8; // 账户形式 cpf/cnpj/evp/email/phone + string payment_number = 9; // 账户号码 收款账号号码 + string iap_name = 10; // 商品名称 游戏侧自定义的提现项目名称,例如:0.1br/50br/100br + string gamecoin_number = 11; // 提现消耗的虚拟货币数 提现消耗的虚拟货币数量,例如:1500 + string gamecoin_type = 12; // 提现消耗的虚拟货币类型 金币或钞票,例如:coin/money + string ss_account_id = 13; // 数数账号ID 用户的登录ID(如果需要接入数数请务必传此值) + string ss_distinct_id = 14; // 数数访客ID 用户在未登录状态下的ID(如果需要接入数数请务必传此值) + string ss_super_properties = 15; // 数数的公共属性和预制属性 json字符串 +} + +// PayoutReq 赔付请求 +message PayoutReq { + string platform = 1 [(validate.rules).string.min_len = 1]; + string deviceid = 2 [(validate.rules).string.min_len = 1]; + string version = 3 [(validate.rules).string.min_len = 1]; + string ts = 4 [(validate.rules).string.min_len = 1]; // utc时间秒 + string sign = 5 [(validate.rules).string.min_len = 1]; + string account = 6 [(validate.rules).string.min_len = 1]; // PIX: Beneficiary's PIX account paypal账号 + uint32 item_id = 7 [(validate.rules).uint32 = { lte: 3, gte: 1 }]; // 1提现0.1,2提现金币大额1,3提现绿钞大额1,4是身份审核通过的奖励提现 + double amount = 8 [(validate.rules).double = { lte: 50, gte: 0.1 }]; + string additional_remark = 9 [(validate.rules).string.min_len = 1]; + string uuid = 10 [(validate.rules).string.min_len = 1]; + string ip = 11; + string account_type = 12 [(validate.rules).string = { min_len: 0, max_len: 5 }]; // 非巴西PIX支付不填,PIX: Beneficiary's PIX account type- One of: CPF, CNPJ, EVP, PHONE, EMAIL + string document_type = 13 [(validate.rules).string = { min_len: 0, max_len: 4 }]; // 非巴西PIX支付不填,PIX: Beneficiary's personal identification type - One of: CPF, CNPJ + string document_id = 14 [(validate.rules).string = { min_len: 0, max_len: 100 }]; // 非巴西PIX支付不填,PIX: Beneficiary's personal identification number + string name = 15 [(validate.rules).string = { min_len: 0, max_len: 100 }]; // 非巴西PIX支付不填,PIX: Beneficiary's name- Length between 5 and 100 + PbReportDataAdjust dataAdjust = 16; // 客户端上报 adjust 数据 + PbReportDataShuShu dataShuShu = 17; // 客户端上报 数数 数据 + string clientData = 18; // 客户端上传、需要保存的数据 + string clientName = 19; // 客户端包名 + string email = 20 [(validate.rules).string.min_len = 1]; // 邮箱 +} + +// PayoutReply 赔付响应 +message PayoutReply { + string id = 1; + string record_no = 2; + int32 error = 3; // 错误码,0成功,1失败,2签名验证失败,3客户端版本过低,4uuid错误,5所在地国家或地区不在提现限制内,6提现金额不符对应的产品id,7提现产品id不对,8达到提现金额限制,9提现次数超过限制,10今日没有提现机会,11提现账号达到次数限制,12身份审核条件不满足,不能提现,13巴西提现参数 document_type 错误, + // 14巴西提现参数 document_id 错误,15 巴西提现参数 AccountType 错误,16 巴西提现参数 Name 错误,17巴西提现参数 Account 和 DocumentId 不同,18巴西提现参数account_type为CPF时 对应的 account 错误,19巴西提现参数account_type为CNPJ时 对应的 account 错误,20巴西提现参数 account_type 错误, + // 21巴西提现参数 document_type 错误,22巴西提现参数account_type为CPF时 对应的 document_id 错误,23巴西提现参数account_type为CNPJ时 对应的 document_id 错误,24 ts长度错误,25 没提0.1就提现其它的 26解析数数出错 27自然量用户 +} + +// PayoutCallbackReq 赔付回调请求 +message PayoutCallbackReq { + string payout_id = 1; + string custom_code = 2; + string status = 3; + string msg = 4; + int64 timestamp = 5; +} + +// PayoutCallbackReply 赔付回调响应 +message PayoutCallbackReply { + string message = 1; +} + +// PayoutCheckReq 赔付查询请求 +message PayoutCheckReq { + string platform = 1 [(validate.rules).string.min_len = 1]; + string deviceid = 2 [(validate.rules).string.min_len = 1]; + string version = 3 [(validate.rules).string.min_len = 1]; + string ts = 4 [(validate.rules).string.min_len = 1]; // utc时间秒 + string sign = 5 [(validate.rules).string.min_len = 1]; + string ip = 6; + string record_no = 7 [(validate.rules).string.min_len = 1]; +} + +// PayoutCheckReply 赔付查询响应 +message PayoutCheckReply { + uint32 status = 1; // 提现状态 1:提现中,2:提现成功,3:提现失败 + int32 error = 2; // 错误码,0成功,1失败,2签名验证失败,3客户端版本过低,4 ts长度错误 +} + +// GetPayoutUserLst 查询提现邮箱请求 +message PayoutUserLstReq { + string platform = 1 [(validate.rules).string.min_len = 1]; + string deviceid = 2 [(validate.rules).string.min_len = 1]; + string version = 3 [(validate.rules).string.min_len = 1]; + string ts = 4 [(validate.rules).string.min_len = 1]; // utc时间秒 + string sign = 5 [(validate.rules).string.min_len = 1]; // 签名 + uint32 status = 6 [(validate.rules).uint32 = { lte: 3, gte: 1 }]; // 查询提现请求的状态, 提现状态 1:提现中,2:提现成功,3:提现失败 + uint32 pageIndex = 7; // 查询页第几页,从1开始 + uint32 pageSize = 8; // 每页多少条记录 +} +// GetPayoutUserLst 获取申请提现玩家的列表请求响应 +message PayoutUserLstReply { + repeated PayoutUserOne lst = 1; // 只返回 提现中的 + int32 error = 2; // 错误码,0成功,1失败,2签名验证失败,3访问数据库出错,4 ts长度错误 +} +message PayoutUserOne { + string email = 1; // 邮件地址 + string recordNo = 2; // 提现唯一编码 + string account = 3; // paypal账号 + uint32 status = 4; // 提现状态 1:提现中,2:提现成功,3:提现失败 +} + +// SetPayoutStatus 设置指定玩家的提现状态 +message PayoutStatusReq { + string platform = 1 [(validate.rules).string.min_len = 1]; + string deviceid = 2 [(validate.rules).string.min_len = 1]; + string version = 3 [(validate.rules).string.min_len = 1]; + string ts = 4 [(validate.rules).string.min_len = 1]; // utc时间秒 + string sign = 5 [(validate.rules).string.min_len = 1]; // 签名 + string recordNo = 6 [(validate.rules).string.min_len = 1]; // 提现唯一编码,值来自 PayoutUserOne 的 recordNo + string fail = 7; // 设置拒绝原因 + uint32 status = 8 [(validate.rules).uint32 = { lte: 3, gte: 2 }]; // 设置提现状态 2:提现成功,3:提现失败 +} +message PayoutStatusReply { + string recordNo = 1; // 提现唯一编码,值来自 PayoutUserOne 的 recordNo + uint32 status = 2; + uint32 error = 3; // 错误码,0成功,1失败,2签名验证失败,3访问数据库出错,4 ts长度错误,5 status值错误 +} + +// 提交身份文件验证请求 +message SubmitCheckReq { + string account = 1 [(validate.rules).string.min_len = 1]; // paypal账号 + string uuid = 2 [(validate.rules).string.min_len = 1]; +} + +message SubmitCheckReply { + int32 result = 1; // 0成功,1失败,2以前提交过,还没审核结果,3以前提交过,并审核通过(以前提交过,但审核失败的,可以继续提交), + int32 error = 2; // 错误码,0成功,1失败, +} + +// 身份审核信息请求 +message CheckInfoReq { + string platform = 1 [(validate.rules).string.min_len = 1]; + string deviceid = 2 [(validate.rules).string.min_len = 1]; + string version = 3 [(validate.rules).string.min_len = 1]; + string ip = 4; + string ts = 5 [(validate.rules).string.min_len = 1]; // utc时间秒 + string sign = 6 [(validate.rules).string.min_len = 1]; + string uuid = 7; // 用户唯一字符串 + int32 isVerificationShow = 8; // 1开,0关 +} + +message CheckInfoReply { + int32 CanCheckSubmit = 1; // 0提交审核的人数已满,不能提交了,1可以提交身份审核 + int32 CheckSubmit = 2; // 提交身份文件验证情况,0没有提交,1提交过 + int32 CheckResult = 3; // 身份文件审核有反馈的情况,0没有记录,1审核没通过,2审核通过 + int32 CheckPayout = 4; // 提交身份文件奖励5美元领取的情况,0没有提现记录,1提现中,2提现成功,3提现失败 + int32 CheckCoin = 5; // 身份文件审核过的提现奖励,美分 + int32 CanCheckPayOut = 6; // 身份文件审核过的提现奖励5美元 能否 提现,0能提现,1条件不满足,不能提现,2当天已经提现过,不能再次提现 + string CheckResultFailedDesc = 7; // CheckResult==1时,显示的审核没通过的原因描述信息 + int32 error = 8; // 错误码,0成功,1失败,2 ts长度错误, +} + +message PbMsgOne { + int64 timeStamp = 1; // 消息时间戳utc纳秒 + string uuid = 2; // 用户唯一字符串 + string name = 3; // 用户名 + string msg = 4; // 聊天消息 +} + +// 发送聊天消息 +message AddChatReq { + int64 timeStamp = 1; // 消息时间戳,同PbMsgOne的timeStamp,客户端已有聊天的最后的时间戳,用来获取最新的聊天消息,如果为0,则从最开始获取 + string platform = 2 [(validate.rules).string.min_len = 1]; + string deviceid = 3 [(validate.rules).string.min_len = 1]; + string version = 4 [(validate.rules).string.min_len = 1]; + string ip = 5; + string ts = 6 [(validate.rules).string.min_len = 1]; // utc时间秒 + string sign = 7 [(validate.rules).string.min_len = 1]; + string uuid = 8; // 用户唯一字符串 + string msg = 9; // 聊天消息 +} +message AddChatReply { + uint32 result = 1; // 0成功,1失败,2 ts长度错误, + string uuid = 2; // 用户唯一字符串 + repeated PbMsgOne lst = 3; // 聊天消息列表 + int32 error = 4; // 错误码,0成功,1失败, +} + +// 获取聊天消息列表 +message GetChatReq { + int64 timeStamp = 1; // 消息时间戳,同PbMsgOne的timeStamp,客户端已有聊天的最后的时间戳,用来获取最新的聊天消息,如果为0,则从最开始获取 + string platform = 2 [(validate.rules).string.min_len = 1]; + string deviceid = 3 [(validate.rules).string.min_len = 1]; + string version = 4 [(validate.rules).string.min_len = 1]; + string ip = 5; + string ts = 6 [(validate.rules).string.min_len = 1]; + string sign = 7 [(validate.rules).string.min_len = 1]; + string uuid = 8; // 用户唯一字符串 +} +message GetChatReply { + string uuid = 1; // 用户唯一字符串 + repeated PbMsgOne lst = 2; // 聊天消息列表 + int32 error = 3; // 错误码,0成功,1失败, +} + +//////////////////////////////////////////////////////////////// +// 以下为服务器端使用的,客户端不需要可忽略 + +message PbSvrData { + repeated PbMsgOne lstChat = 1; // 聊天消息列表 + // map mapReportData = 2; // 上报数据 map + // uint32 rfClearReportData = 3; // 清理上报数据时间戳utc秒 +} + +message PbUserData { +} + +message PbReportData { + PbAdjustData adjust = 1; + PbShuShuData shuShu = 2; + uint32 rf = 3; +} + +message PbAdjustData { + string GpsAdid = 1; + string Adid = 2; + string AndroidId = 3; + string IpAddress = 4; + string CreatedAtUnix = 5; + string Currency = 6; + string Environment = 7; + string UserAgent = 8; + string Price = 9; + string FailReason = 10; + string AppToken = 11; + string EventToken = 12; + string S2s = 13; + string clientName = 14; +} + +message PbShuShuData { + string GpsAdid = 1; + string AppToken = 2; + string EventToken = 3; + string S2s = 4; + string AndroidId = 5; + string Adid = 6; + string IpAddress = 7; + string CreatedAtUnix = 8; + string UserAgent = 9; + string Price = 10; + string Currency = 11; + string FailReason = 12; + string PayoutId = 13; + string MerchantReference = 14; + string PaymentMethod = 15; + string PaymentType = 16; + string PaymentNumber = 17; + string IapName = 18; + string GamecoinNumber = 19; + string GamecoinType = 20; + string SsAccountId = 21; + string SsDistinctId = 22; + string SsSuperProperties = 23; + string clientName = 24; +} diff --git a/app/eonline/Makefile b/app/eonline/Makefile new file mode 100644 index 0000000..2093406 --- /dev/null +++ b/app/eonline/Makefile @@ -0,0 +1 @@ +include ../../app_makefile \ No newline at end of file diff --git a/app/eonline/cmd/server/main.go b/app/eonline/cmd/server/main.go new file mode 100644 index 0000000..c7eb063 --- /dev/null +++ b/app/eonline/cmd/server/main.go @@ -0,0 +1,122 @@ +package main + +import ( + "flag" + "os" + + "sandc/app/eonline/internal/conf" + config2 "sandc/app/eonline/internal/config" + "sandc/app/eonline/internal/service" + "sandc/pkg/log/zaplog" + + zaplogger "github.com/go-kratos/kratos/contrib/log/zap/v2" + "github.com/go-kratos/kratos/v2" + "github.com/go-kratos/kratos/v2/config" + "github.com/go-kratos/kratos/v2/config/file" + "github.com/go-kratos/kratos/v2/encoding/json" + "github.com/go-kratos/kratos/v2/log" + "github.com/go-kratos/kratos/v2/middleware/tracing" + "github.com/go-kratos/kratos/v2/transport/http" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/sdk/resource" + tracesdk "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" + "google.golang.org/protobuf/encoding/protojson" +) + +// go build -ldflags "-X main.Version=x.y.z" +var ( + // Name is the name of the compiled software. + Name = "eonline.rpc" + // Version is the version of the compiled software. + Version = "0.1.0" + // flagconf is the config flag. + flagconf string + + id, _ = os.Hostname() +) + +func init() { + flag.StringVar(&flagconf, "conf", "configs/config.yaml", "config path, eg: -conf config.yaml") + json.MarshalOptions = protojson.MarshalOptions{ + EmitUnpopulated: true, + UseProtoNames: true, + } +} + +// func newApp(logger log.Logger, conf *conf.Server, hs *http.Server, gs *grpc.Server, rr registry.Registrar) *kratos.App { +func newApp(logger log.Logger, conf *conf.Server, hs *http.Server) *kratos.App { + return kratos.New( + kratos.ID(id), + kratos.Name(Name), + kratos.Version(Version), + kratos.Metadata(map[string]string{}), + kratos.Logger(logger), + kratos.Server( + hs, + // gs, + ), + // kratos.Registrar(rr), + ) +} + +func main() { + flag.Parse() + + c := config.New( + config.WithSource( + file.NewSource(flagconf), + ), + ) + if err := c.Load(); err != nil { + panic(err) + } + + var bc conf.Bootstrap + if err := c.Scan(&bc); err != nil { + panic(err) + } + + zc := zaplog.NewConfig(zaplog.SetLogPrintTag(false)) + if err := zaplog.Init(zc); err != nil { + panic(err) + } + + logger := log.With(zaplogger.NewLogger(zaplog.Logger), + "ts", log.DefaultTimestamp, + "caller", log.DefaultCaller, + "service.id", id, + "service.name", Name, + "service.version", Version, + "trace_id", tracing.TraceID(), + "span_id", tracing.SpanID(), + ) + // exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(bc.Server.TraceEndpoint))) + // if err != nil { + // panic(err) + // } + tp := tracesdk.NewTracerProvider( + // tracesdk.WithBatcher(exp), + tracesdk.WithResource(resource.NewSchemaless( + semconv.ServiceNameKey.String(Name), + )), + ) + otel.SetTracerProvider(tp) + + config2.ConfigInit(&bc.ConfigFiles.Path, &bc.Server.Env, &bc.Server.VerCheck) + service.InitTimer() + defer func() { + service.OnDestroyTimer() + }() + + app, cleanup, err := initApp(bc.Server, &bc, bc.Data, bc.Queue, logger) + if err != nil { + panic(err) + } + defer cleanup() + + // start and wait for stop signal + if err := app.Run(); err != nil { + panic(err) + } +} diff --git a/app/eonline/cmd/server/wire.go b/app/eonline/cmd/server/wire.go new file mode 100644 index 0000000..cdd02e5 --- /dev/null +++ b/app/eonline/cmd/server/wire.go @@ -0,0 +1,22 @@ +//go:build wireinject +// +build wireinject + +// The build tag makes sure the stub is not built in the final build. + +package main + +import ( + "github.com/go-kratos/kratos/v2" + "github.com/go-kratos/kratos/v2/log" + "github.com/google/wire" + "sandc/app/eonline/internal/biz" + "sandc/app/eonline/internal/conf" + "sandc/app/eonline/internal/data" + "sandc/app/eonline/internal/server" + "sandc/app/eonline/internal/service" +) + +// initApp init kratos application. +func initApp(*conf.Server, *conf.Bootstrap, *conf.Data, *conf.Queue, log.Logger) (*kratos.App, func(), error) { + panic(wire.Build(server.ProviderSet, data.ProviderSet, biz.ProviderSet, service.ProviderSet, newApp)) +} diff --git a/app/eonline/cmd/server/wire_gen.go b/app/eonline/cmd/server/wire_gen.go new file mode 100644 index 0000000..95b814a --- /dev/null +++ b/app/eonline/cmd/server/wire_gen.go @@ -0,0 +1,40 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +import ( + "sandc/app/eonline/internal/biz" + "sandc/app/eonline/internal/conf" + "sandc/app/eonline/internal/data" + "sandc/app/eonline/internal/server" + "sandc/app/eonline/internal/service" + + "github.com/go-kratos/kratos/v2" + "github.com/go-kratos/kratos/v2/log" +) + +// Injectors from wire.go: + +// initApp init kratos application. +func initApp(confServer *conf.Server, bootstrap *conf.Bootstrap, confData *conf.Data, queue *conf.Queue, logger log.Logger) (*kratos.App, func(), error) { + client := data.NewAsynqClient(queue) + dataData, cleanup, err := data.NewData(confData, bootstrap, client, logger) + if err != nil { + return nil, nil, err + } + eonlineRepo := data.NewEonlineRepo(dataData, logger) + transaction := data.NewTransaction(dataData) + cache := data.NewCache(dataData) + eonlineUsecase := biz.NewEonlineUsecase(eonlineRepo, bootstrap, transaction, logger, cache) + eonlineService := service.NewEonlineService(eonlineUsecase, logger, bootstrap) + httpServer := server.NewHTTPServer(confServer, eonlineService, logger) + app := newApp(logger, confServer, httpServer) + data.InitData(bootstrap) + return app, func() { + cleanup() + }, nil +} diff --git a/app/eonline/cmd/worker/main.go b/app/eonline/cmd/worker/main.go new file mode 100644 index 0000000..e95eee8 --- /dev/null +++ b/app/eonline/cmd/worker/main.go @@ -0,0 +1,87 @@ +package main + +import ( + "flag" + "os" + "sandc/app/eonline/internal/conf" + + zaplogger "github.com/go-kratos/kratos/contrib/log/zap/v2" + "github.com/go-kratos/kratos/v2/config" + "github.com/go-kratos/kratos/v2/config/file" + "github.com/go-kratos/kratos/v2/log" + "github.com/go-kratos/kratos/v2/middleware/tracing" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/jaeger" + "go.opentelemetry.io/otel/sdk/resource" + tracesdk "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +// go build -ldflags "-X main.Version=x.y.z" +var ( + // Name is the name of the compiled software. + Name = "eonline.async" + // Version is the version of the compiled software. + Version = "0.1.0" + // flagconf is the config flag. + flagconf string + + id, _ = os.Hostname() +) + +func init() { + flag.StringVar(&flagconf, "conf", "configs/config.yaml", "config path, eg: -conf config.yaml") +} + +func main() { + flag.Parse() + encoderCfg := zapcore.EncoderConfig{ + LevelKey: "level", + EncodeLevel: zapcore.LowercaseLevelEncoder, + } + out := zapcore.AddSync(os.Stdout) // replace real writer + core := zapcore.NewCore(zapcore.NewJSONEncoder(encoderCfg), out, zap.DebugLevel) + zlogger := zap.New(core).WithOptions() + logger := log.With(zaplogger.NewLogger(zlogger), + "ts", log.DefaultTimestamp, + "caller", log.DefaultCaller, + "service.id", id, + "service.name", Name, + "service.version", Version, + "trace_id", tracing.TraceID(), + "span_id", tracing.SpanID(), + ) + c := config.New( + config.WithSource( + file.NewSource(flagconf), + ), + ) + if err := c.Load(); err != nil { + panic(err) + } + var bc conf.Bootstrap + if err := c.Scan(&bc); err != nil { + panic(err) + } + exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(bc.Server.TraceEndpoint))) + if err != nil { + panic(err) + } + tp := tracesdk.NewTracerProvider( + tracesdk.WithBatcher(exp), + tracesdk.WithResource(resource.NewSchemaless( + semconv.ServiceNameKey.String(Name), + )), + ) + otel.SetTracerProvider(tp) + job, cleanup, err := initApp(bc.Data, &bc, bc.Server, bc.Queue, logger) + if err != nil { + panic(err) + } + defer cleanup() + if err := job.Run(); err != nil { + panic(err) + } +} diff --git a/app/eonline/cmd/worker/wire.go b/app/eonline/cmd/worker/wire.go new file mode 100644 index 0000000..e635201 --- /dev/null +++ b/app/eonline/cmd/worker/wire.go @@ -0,0 +1,20 @@ +//go:build wireinject +// +build wireinject + +// The build tag makes sure the stub is not built in the final build. + +package main + +import ( + "github.com/go-kratos/kratos/v2/log" + "github.com/google/wire" + "sandc/app/eonline/internal/biz" + "sandc/app/eonline/internal/conf" + "sandc/app/eonline/internal/data" + "sandc/app/eonline/internal/server" +) + +// initApp init application. +func initApp(*conf.Data, *conf.Bootstrap, *conf.Server, *conf.Queue, log.Logger) (*server.AsynqServer, func(), error) { + panic(wire.Build(data.ProviderSet, server.ProviderSet, biz.ProviderSet)) +} diff --git a/app/eonline/cmd/worker/wire_gen.go b/app/eonline/cmd/worker/wire_gen.go new file mode 100644 index 0000000..8b79ec4 --- /dev/null +++ b/app/eonline/cmd/worker/wire_gen.go @@ -0,0 +1,35 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package main + +import ( + "sandc/app/eonline/internal/biz" + "sandc/app/eonline/internal/conf" + "sandc/app/eonline/internal/data" + "sandc/app/eonline/internal/server" + + "github.com/go-kratos/kratos/v2/log" +) + +// Injectors from wire.go: + +// initApp init application. +func initApp(confData *conf.Data, bootstrap *conf.Bootstrap, confServer *conf.Server, queue *conf.Queue, logger log.Logger) (*server.AsynqServer, func(), error) { + client := data.NewAsynqClient(queue) + dataData, cleanup, err := data.NewData(confData, bootstrap, client, logger) + if err != nil { + return nil, nil, err + } + eonlineRepo := data.NewEonlineRepo(dataData, logger) + transaction := data.NewTransaction(dataData) + cache := data.NewCache(dataData) + eonlineUsecase := biz.NewEonlineUsecase(eonlineRepo, bootstrap, transaction, logger, cache) + asynqServer := server.NewAsynqServer(queue, eonlineUsecase) + return asynqServer, func() { + cleanup() + }, nil +} diff --git a/app/eonline/configs/config.yaml.example b/app/eonline/configs/config.yaml.example new file mode 100644 index 0000000..21d1067 --- /dev/null +++ b/app/eonline/configs/config.yaml.example @@ -0,0 +1,51 @@ +server: + http: + addr: 0.0.0.0:8300 + timeout: 60s + grpc: + addr: 0.0.0.0:0 + timeout: 30s + etcd: + addr: + - 127.0.0.1:2379 + trace_endpoint: http://127.0.0.1:14268/api/traces + env: "qa" + geo_file: "/Users/wangchuan/www/test/geo/GeoIP2-City_20210706/GeoIP2-City.mmdb" + svr_id: 1 + first_day: 20230908 + ver_check: 2.0.0 + +data: + database: + driver: mysql +# source: root:a123456@tcp(47.94.97.41:33306)/eonline2?charset=utf8mb4&parseTime=True&loc=UTC + source: root:root@tcp(127.0.0.1:3306)/eonline2?charset=utf8mb4&parseTime=True&loc=UTC + redis: + addr: 127.0.0.1:6379 + read_timeout: 0.2s + write_timeout: 0.2s + db: 3 + +queue: + kafka: + addrs: + - 127.0.0.1:9092 + topic: example + group: example + + asynq: + addr: 127.0.0.1:6379 + read_timeout: 0.2s + write_timeout: 0.2s + concurrency: 10 + db: 13 + +pagsmile: + payout: + app_id: "9B6B388E81E6456B901B61AEFA1F58E4" + app_key: "BO9ImO6KHg" + api_url: "https://sandbox.transfersmile.com" + notify_url: "http://pagsmile.dgtverse.cn/eonline2/payout/callback" + +configFiles: + path: "./configs/" diff --git a/app/eonline/configs/config.yaml3 b/app/eonline/configs/config.yaml3 new file mode 100644 index 0000000..b2595b5 --- /dev/null +++ b/app/eonline/configs/config.yaml3 @@ -0,0 +1,64 @@ +server: + http: + addr: 0.0.0.0:8600 + timeout: 60s + grpc: + addr: 0.0.0.0:0 + timeout: 30s + etcd: + addr: + - 127.0.0.1:2379 + trace_endpoint: http://127.0.0.1:14268/api/traces + env: "qa" + env2: "prod" + geo_file: "D:/work/GeoIP2-City.mmdb" + svr_id: 1 + first_day: 20230907 + ver_check: 0.0.0 + timeoutTimerPer10Second: 3500 + +data: + database: + driver: mysql + source: root:a123456@tcp(47.94.97.41:33306)/eonline3?charset=utf8mb4&parseTime=True&loc=UTC +# source: root:root@tcp(127.0.0.1:3306)/eonline2?charset=utf8mb4&parseTime=True&loc=UTC + redis: + addr: 47.94.97.41:6666 + password: a123456 +# addr: 127.0.0.1:6389 + read_timeout: 0.2s + write_timeout: 0.2s + db: 2 + +queue: + kafka: + addrs: + - 127.0.0.1:9092 + topic: example + group: example + + asynq: + addr: 47.94.97.41:6666 + password: a123456 +# addr: 127.0.0.1:6389 + read_timeout: 0.2s + write_timeout: 0.2s + concurrency: 10 + db: 2 + +pagsmile: + payout: + app_id: "9B6B388E81E6456B901B61AEFA1F58E4" + app_key: "BO9ImO6KHg" + api_url: "https://sandbox.transfersmile.com" + notify_url: "http://pagsmile.dgtverse.cn/eonline3/payout/callback" + +configFiles: + path: "./configs/" + +appConfig: + adjustAppToken: "5l2aubga4by8" + adjustS2SToken: "7ea35d86e3e6688c2debcadc4efd7230" + adjustEventTokenSuccess: "xlwfxq" + adjustEventTokenFail: "t4c6tj" + ssAppId: "3774fd57014846d99ccd145a76780866" diff --git a/app/eonline/configs/config.yaml4 b/app/eonline/configs/config.yaml4 new file mode 100644 index 0000000..50c95c6 --- /dev/null +++ b/app/eonline/configs/config.yaml4 @@ -0,0 +1,64 @@ +server: + http: + addr: 0.0.0.0:8600 + timeout: 60s + grpc: + addr: 0.0.0.0:0 + timeout: 30s + etcd: + addr: + - 127.0.0.1:2379 + trace_endpoint: http://127.0.0.1:14268/api/traces + env: "qa" + env2: "prod" + geo_file: "D:/work/GeoIP2-City.mmdb" + svr_id: 1 + first_day: 20230907 + ver_check: 0.0.0 + timeoutTimerPer10Second: 3500 + +data: + database: + driver: mysql + source: root:a123456@tcp(47.94.97.41:33306)/eonline3?charset=utf8mb4&parseTime=True&loc=UTC +# source: root:root@tcp(127.0.0.1:3306)/eonline2?charset=utf8mb4&parseTime=True&loc=UTC + redis: + addr: 47.94.97.41:6666 + password: a123456 +# addr: 127.0.0.1:6389 + read_timeout: 0.2s + write_timeout: 0.2s + db: 2 + +queue: + kafka: + addrs: + - 127.0.0.1:9092 + topic: example + group: example + + asynq: + addr: 47.94.97.41:6666 + password: a123456 +# addr: 127.0.0.1:6389 + read_timeout: 0.2s + write_timeout: 0.2s + concurrency: 10 + db: 2 + +pagsmile: + payout: + app_id: "9B6B388E81E6456B901B61AEFA1F58E4" + app_key: "BO9ImO6KHg" + api_url: "https://sandbox.transfersmile.com" + notify_url: "http://pagsmile.dgtverse.cn/eonline3/payout/callback" + +configFiles: + path: "./configs/" + +appConfig: + adjustAppToken: "cha3p92jj30g" + adjustS2SToken: "d39f286413fba8bd9b1647b54431fdc6" + adjustEventTokenSuccess: "dmb6de" + adjustEventTokenFail: "cz94hi" + ssAppId: "be320c3e32ef4beb990270dcffde55af" diff --git a/app/eonline/configs/config_tbcashout.bytes b/app/eonline/configs/config_tbcashout.bytes new file mode 100644 index 0000000..e8aaf92 Binary files /dev/null and b/app/eonline/configs/config_tbcashout.bytes differ diff --git a/app/eonline/configs/config_tbcoinadreward.bytes b/app/eonline/configs/config_tbcoinadreward.bytes new file mode 100644 index 0000000..7a8e105 Binary files /dev/null and b/app/eonline/configs/config_tbcoinadreward.bytes differ diff --git a/app/eonline/configs/config_tbglobal.bytes b/app/eonline/configs/config_tbglobal.bytes new file mode 100644 index 0000000..852232b Binary files /dev/null and b/app/eonline/configs/config_tbglobal.bytes differ diff --git a/app/eonline/configs/config_tbminigame.bytes b/app/eonline/configs/config_tbminigame.bytes new file mode 100644 index 0000000..e5d71c5 Binary files /dev/null and b/app/eonline/configs/config_tbminigame.bytes differ diff --git a/app/eonline/configs/config_tbuserstate.bytes b/app/eonline/configs/config_tbuserstate.bytes new file mode 100644 index 0000000..0e9b225 Binary files /dev/null and b/app/eonline/configs/config_tbuserstate.bytes differ diff --git a/app/eonline/generate.go b/app/eonline/generate.go new file mode 100644 index 0000000..bb498ec --- /dev/null +++ b/app/eonline/generate.go @@ -0,0 +1,3 @@ +package eonline + +//go:generate kratos proto client api diff --git a/app/eonline/internal/biz/README.md b/app/eonline/internal/biz/README.md new file mode 100644 index 0000000..26a66b6 --- /dev/null +++ b/app/eonline/internal/biz/README.md @@ -0,0 +1 @@ +# Biz diff --git a/app/eonline/internal/biz/ReportData.go b/app/eonline/internal/biz/ReportData.go new file mode 100644 index 0000000..caa7a39 --- /dev/null +++ b/app/eonline/internal/biz/ReportData.go @@ -0,0 +1,163 @@ +package biz + +import ( + "errors" + + v1 "sandc/api/eonline/v1" + + go_redis_orm "github.com/fananchong/go-redis-orm.v2" + "github.com/gomodule/redigo/redis" + "google.golang.org/protobuf/proto" +) + +type ReportData struct { + __key string + data v1.PbReportData + + __dirtyData map[string]interface{} + __dirtyDataForStructFiled map[string]interface{} + __isLoad bool + __dbKey string + __dbName string + __expire uint +} + +func NewReportData(dbName string, key string) *ReportData { + return &ReportData{ + __key: key, + __dbName: dbName, + __dbKey: "ReportData:" + key, + __dirtyData: make(map[string]interface{}), + __dirtyDataForStructFiled: make(map[string]interface{}), + } +} + +// 若访问数据库失败返回-1;若 key 存在返回 1 ,否则返回 0 。 +func (this *ReportData) HasKey() (int, error) { + db := go_redis_orm.GetDB(this.__dbName) + val, err := redis.Int(db.Do("EXISTS", this.__dbKey)) + if err != nil { + return -1, err + } + return val, nil +} + +func (this *ReportData) Load() error { + if this.__isLoad == true { + return errors.New("already load!") + } + db := go_redis_orm.GetDB(this.__dbName) + val, err := redis.Values(db.Do("HGETALL", this.__dbKey)) + if err != nil { + return err + } + if len(val) == 0 { + return go_redis_orm.ERR_ISNOT_EXIST_KEY + } + var data struct { + Data []byte `redis:"data"` + } + if err := redis.ScanStruct(val, &data); err != nil { + return err + } + if err := proto.Unmarshal(data.Data, &this.data); err != nil { + return err + } + + this.__isLoad = true + return nil +} + +func (this *ReportData) Save() error { + if len(this.__dirtyData) == 0 && len(this.__dirtyDataForStructFiled) == 0 { + return nil + } + for k, _ := range this.__dirtyDataForStructFiled { + _ = k + if k == "data" { + data, err := proto.Marshal(&this.data) + if err != nil { + return err + } + this.__dirtyData["data"] = data + } + } + db := go_redis_orm.GetDB(this.__dbName) + if _, err := db.Do("HMSET", redis.Args{}.Add(this.__dbKey).AddFlat(this.__dirtyData)...); err != nil { + return err + } + if this.__expire != 0 { + if _, err := db.Do("EXPIRE", this.__dbKey, this.__expire); err != nil { + return err + } + } + this.__dirtyData = make(map[string]interface{}) + this.__dirtyDataForStructFiled = make(map[string]interface{}) + return nil +} + +func (this *ReportData) Delete() error { + db := go_redis_orm.GetDB(this.__dbName) + _, err := db.Do("DEL", this.__dbKey) + if err == nil { + this.__isLoad = false + this.__dirtyData = make(map[string]interface{}) + this.__dirtyDataForStructFiled = make(map[string]interface{}) + } + return err +} + +func (this *ReportData) IsLoad() bool { + return this.__isLoad +} + +func (this *ReportData) Expire(v uint) { + this.__expire = v +} + +func (this *ReportData) GetKey() string { + return this.__key +} + +func (this *ReportData) DirtyData() (map[string]interface{}, error) { + for k, _ := range this.__dirtyDataForStructFiled { + _ = k + if k == "data" { + data, err := proto.Marshal(&this.data) + if err != nil { + return nil, err + } + this.__dirtyData["data"] = data + } + } + data := make(map[string]interface{}) + for k, v := range this.__dirtyData { + data[k] = v + } + this.__dirtyData = make(map[string]interface{}) + this.__dirtyDataForStructFiled = make(map[string]interface{}) + return data, nil +} + +func (this *ReportData) Save2(dirtyData map[string]interface{}) error { + if len(dirtyData) == 0 { + return nil + } + db := go_redis_orm.GetDB(this.__dbName) + if _, err := db.Do("HMSET", redis.Args{}.Add(this.__dbKey).AddFlat(dirtyData)...); err != nil { + return err + } + if this.__expire != 0 { + if _, err := db.Do("EXPIRE", this.__dbKey, this.__expire); err != nil { + return err + } + } + return nil +} + +func (this *ReportData) GetData(mutable bool) *v1.PbReportData { + if mutable { + this.__dirtyDataForStructFiled["data"] = nil + } + return &this.data +} diff --git a/app/eonline/internal/biz/UserData.go b/app/eonline/internal/biz/UserData.go new file mode 100644 index 0000000..da4ea54 --- /dev/null +++ b/app/eonline/internal/biz/UserData.go @@ -0,0 +1,163 @@ +package biz + +import ( + "errors" + + v1 "sandc/api/eonline/v1" + + go_redis_orm "github.com/fananchong/go-redis-orm.v2" + "github.com/gomodule/redigo/redis" + "google.golang.org/protobuf/proto" +) + +type UserData struct { + __key string + data v1.PbUserData + + __dirtyData map[string]interface{} + __dirtyDataForStructFiled map[string]interface{} + __isLoad bool + __dbKey string + __dbName string + __expire uint +} + +func NewUserData(dbName string, key string) *UserData { + return &UserData{ + __key: key, + __dbName: dbName, + __dbKey: "UserData:" + key, + __dirtyData: make(map[string]interface{}), + __dirtyDataForStructFiled: make(map[string]interface{}), + } +} + +// 若访问数据库失败返回-1;若 key 存在返回 1 ,否则返回 0 。 +func (this *UserData) HasKey() (int, error) { + db := go_redis_orm.GetDB(this.__dbName) + val, err := redis.Int(db.Do("EXISTS", this.__dbKey)) + if err != nil { + return -1, err + } + return val, nil +} + +func (this *UserData) Load() error { + if this.__isLoad == true { + return errors.New("already load!") + } + db := go_redis_orm.GetDB(this.__dbName) + val, err := redis.Values(db.Do("HGETALL", this.__dbKey)) + if err != nil { + return err + } + if len(val) == 0 { + return go_redis_orm.ERR_ISNOT_EXIST_KEY + } + var data struct { + Data []byte `redis:"data"` + } + if err := redis.ScanStruct(val, &data); err != nil { + return err + } + if err := proto.Unmarshal(data.Data, &this.data); err != nil { + return err + } + + this.__isLoad = true + return nil +} + +func (this *UserData) Save() error { + if len(this.__dirtyData) == 0 && len(this.__dirtyDataForStructFiled) == 0 { + return nil + } + for k, _ := range this.__dirtyDataForStructFiled { + _ = k + if k == "data" { + data, err := proto.Marshal(&this.data) + if err != nil { + return err + } + this.__dirtyData["data"] = data + } + } + db := go_redis_orm.GetDB(this.__dbName) + if _, err := db.Do("HMSET", redis.Args{}.Add(this.__dbKey).AddFlat(this.__dirtyData)...); err != nil { + return err + } + if this.__expire != 0 { + if _, err := db.Do("EXPIRE", this.__dbKey, this.__expire); err != nil { + return err + } + } + this.__dirtyData = make(map[string]interface{}) + this.__dirtyDataForStructFiled = make(map[string]interface{}) + return nil +} + +func (this *UserData) Delete() error { + db := go_redis_orm.GetDB(this.__dbName) + _, err := db.Do("DEL", this.__dbKey) + if err == nil { + this.__isLoad = false + this.__dirtyData = make(map[string]interface{}) + this.__dirtyDataForStructFiled = make(map[string]interface{}) + } + return err +} + +func (this *UserData) IsLoad() bool { + return this.__isLoad +} + +func (this *UserData) Expire(v uint) { + this.__expire = v +} + +func (this *UserData) GetKey() string { + return this.__key +} + +func (this *UserData) DirtyData() (map[string]interface{}, error) { + for k, _ := range this.__dirtyDataForStructFiled { + _ = k + if k == "data" { + data, err := proto.Marshal(&this.data) + if err != nil { + return nil, err + } + this.__dirtyData["data"] = data + } + } + data := make(map[string]interface{}) + for k, v := range this.__dirtyData { + data[k] = v + } + this.__dirtyData = make(map[string]interface{}) + this.__dirtyDataForStructFiled = make(map[string]interface{}) + return data, nil +} + +func (this *UserData) Save2(dirtyData map[string]interface{}) error { + if len(dirtyData) == 0 { + return nil + } + db := go_redis_orm.GetDB(this.__dbName) + if _, err := db.Do("HMSET", redis.Args{}.Add(this.__dbKey).AddFlat(dirtyData)...); err != nil { + return err + } + if this.__expire != 0 { + if _, err := db.Do("EXPIRE", this.__dbKey, this.__expire); err != nil { + return err + } + } + return nil +} + +func (this *UserData) GetData(mutable bool) *v1.PbUserData { + if mutable { + this.__dirtyDataForStructFiled["data"] = nil + } + return &this.data +} diff --git a/app/eonline/internal/biz/adjust/client.go b/app/eonline/internal/biz/adjust/client.go new file mode 100644 index 0000000..d8a05fa --- /dev/null +++ b/app/eonline/internal/biz/adjust/client.go @@ -0,0 +1,196 @@ +package adjust + +import ( + "context" + "encoding/json" + "fmt" + "strings" + + "sandc/pkg/bhttp" + + "github.com/go-kratos/kratos/v2/log" +) + +// AdjustEventParams 上报事件参数 +type AdjustEventParams struct { + GpsAdid string `json:"gps_adid"` + Adid string `json:"adid"` + AndroidId string `json:"android_id"` + IpAddress string `json:"ip_address"` + CreatedAtUnix string `json:"created_at_unix"` + Currency string `json:"currency"` + Environment string `json:"environment"` + UserAgent string `json:"user_agent"` + Price string `json:"price"` + FailReason string `json:"fail_reason"` + AppToken string `json:"app_token"` + EventToken string `json:"event_token"` + S2S string `json:"s2s"` + ClientName string `json:"client_name"` // 客户端包名 + // Idfa string `json:"idfa"` + // FireAdid string `json:"fire_adid"` + // Oaid string `json:"oaid"` + // Idfv string `json:"idfv"` + // Revenue string `json:"revenue"` +} + +// AdjustClient adjust客户端接口 +type AdjustClient interface { + // ReportEvent 上报事件 + ReportEvent(ctx context.Context, opts *AdjustEventParams) error +} + +// adjustClient adjust客户端 +type adjustClient struct { + appToken string + s2sToken string + env string + service *bhttp.BhttpService +} + +// NewAdjustClient 创建adjust客户端 +func NewAdjustClient(appToken, s2sToken, env string) (AdjustClient, error) { + service, err := bhttp.NewBhttpService(bhttp.WithTimeout(30)) + if err != nil { + return nil, err + } + service.Client.SetHeader("Content-Type", "application/x-www-form-urlencoded") + service.Client.SetParam("app_token", appToken) + if s2sToken != "" { + service.Client.SetHeader("Authorization", "Bearer "+s2sToken) + service.Client.SetParam("s2s", "1") + } + + return &adjustClient{ + appToken: appToken, + s2sToken: s2sToken, + env: env, + service: service, + }, nil +} + +type ReportEventRes struct { + Error string `json:"error,omitempty"` + Status string `json:"status,omitempty"` +} + +// ReportEvent 上报事件 +func (ac *adjustClient) ReportEvent(ctx context.Context, opts *AdjustEventParams) error { + // if opts.Idfa != "" && validateUUID(opts.Idfa) { + // ac.service.Client.SetParam("idfa", opts.Idfa) + // } + + if opts.GpsAdid != "" { + // 判断兼容客户端问题 + if !validateUUID(opts.GpsAdid) { + // if opts.GpsAdid == opts.Oaid { + // opts.Oaid = "" + // } + opts.GpsAdid = "" + } else { + ac.service.Client.SetParam("gps_adid", opts.GpsAdid) + } + } + + // if opts.FireAdid != "" { + // ac.service.Client.SetParam("fire_adid", opts.FireAdid) + // } + + // if opts.Oaid != "" { + // ac.service.Client.SetParam("oaid", opts.Oaid) + // } + + if opts.Adid != "" { + ac.service.Client.SetParam("adid", opts.Adid) + if opts.GpsAdid == "" { + ac.service.Client.SetParam("gps_adid", opts.Adid) + } + } + + // if opts.Idfv != "" { + // ac.service.Client.SetParam("idfv", opts.Idfv) + // } else { + // if opts.Idfa != "" { + // ac.service.Client.SetParam("idfv", opts.Idfa) + // } + // } + + if opts.AndroidId != "" { + ac.service.Client.SetParam("android_id", opts.AndroidId) + } + + ac.service.Client.SetParam("event_token", opts.EventToken) + ac.service.Client.SetParam("ip_address", opts.IpAddress) + ac.service.Client.SetParam("created_at_unix", opts.CreatedAtUnix) + ac.service.Client.SetParam("user_agent", opts.UserAgent) + ac.service.Client.SetParam("price", opts.Price) + ac.service.Client.SetParam("fail_reason", opts.FailReason) + ac.service.Client.SetParam("client_name", opts.ClientName) + // ac.service.Client.SetParam("revenue", opts.Revenue) + + // 兼容之前的数据错误 + if strings.Contains(opts.Currency, "CNY") { + opts.Currency = "CNY" + } else if strings.Contains(opts.Currency, "USD") { + opts.Currency = "USD" + } + + ac.service.Client.SetParam("currency", opts.Currency) + + if opts.Environment == "prod" { + ac.service.Client.SetParam("environment", "production") + } else { + ac.service.Client.SetParam("environment", "sandbox") + } + + numMax := 1 + for i := 0; i < numMax; i++ { + body, err := ac.service.Client.DoPost("https://s2s.adjust.com/event") + sendData := ac.service.Client.GetReader() + if err != nil { + return fmt.Errorf("adjust server err: %s, reader: %s", err, sendData) + } + + // 临时记录请求参数 + // utils.PrintLog("adjust-request-params: %s \n", ac.service.Client.GetReader()) + // utils.PrintLog("adjust-response-body: %s \n", string(body)) + log.Infof("adjust-request-params: i[%d] %s \n", i, sendData) + log.Infof("adjust-response-body: i[%d] %s \n", i, string(body)) + + var res ReportEventRes + err = json.Unmarshal(body, &res) + if err != nil { + return fmt.Errorf("adjust Unmarshal err: %s, reader: %s", res.Error, sendData) + } + + if res.Error != "" { + return fmt.Errorf("adjust res err: %s, reader: %s", res.Error, sendData) + } + + if res.Status == "OK" { + log.Infof("adjust send successed: i[%d] reader: %s", i, sendData) + break + } else { + log.Infof("adjust res status error: %s, i[%d] reader: %s", res.Status, i, sendData) + + if i+1 >= numMax { + return fmt.Errorf("adjust res status err: %s, reader: %s", res.Status, sendData) + } + } + } + + return nil +} + +// validateUUID 验证uuid的有效性 +func validateUUID(uuid string) bool { + if len(uuid) != 36 { + return false + } + + if strings.ToLower(uuid) == "00000000-0000-0000-0000-000000000000" { + return false + } + + return true +} diff --git a/app/eonline/internal/biz/adjust/utils.go b/app/eonline/internal/biz/adjust/utils.go new file mode 100644 index 0000000..50c7b9e --- /dev/null +++ b/app/eonline/internal/biz/adjust/utils.go @@ -0,0 +1,401 @@ +package adjust + +import ( + "fmt" + "sandc/pkg/utils" + "strings" + "time" +) + +type CallbackInfo struct { + ActivityKind string `json:"activity_kind"` + AdgroupName string `json:"adgroup_name"` + Adid string `json:"adid"` + AndroidId string `json:"android_id"` + AndroidIdMd5 string `json:"android_id_md5"` + AppId string `json:"app_id"` + AppName string `json:"app_name"` + AppNameDashboard string `json:"app_name_dashboard"` + AppToken string `json:"app_token"` + AppVersion string `json:"app_version"` + AppVersionRaw string `json:"app_version_raw"` + AppVersionShort string `json:"app_version_short"` + AttStatus string `json:"att_status"` + AttributionExpiresAt int64 `json:"attribution_expires_at"` + CallbackTtl string `json:"callback_ttl"` + CampaignName string `json:"campaign_name"` + City string `json:"city"` + ClickTime string `json:"click_time"` + ClickTimeHour string `json:"click_time_hour"` + ConnectionType string `json:"connection_type"` + ConversionDuration string `json:"conversion_duration"` + Country string `json:"country"` + CountrySubdivision string `json:"country_subdivision"` + CpuType string `json:"cpu_type"` + CreatedAt int64 `json:"created_at"` + CreatedAtHour int64 `json:"created_at_hour"` + CreatedAtMilli int64 `json:"created_at_milli"` + CreativeName string `json:"creative_name"` + Currency string `json:"currency"` + DbmCampaignType string `json:"dbm_campaign_type"` + DbmCreativeId string `json:"dbm_creative_id"` + DbmExchangeId string `json:"dbm_exchange_id"` + DbmExternalCustomerId string `json:"dbm_external_customer_id"` + DbmInsertionOrderId string `json:"dbm_insertion_order_id"` + DbmLineItemId string `json:"dbm_line_item_id"` + DbmLineItemName string `json:"dbm_line_item_name"` + DcmCampaignType string `json:"dcm_campaign_type"` + DcmCreativeId string `json:"dcm_creative_id"` + DcmExternalCustomerId string `json:"dcm_external_customer_id"` + DcmPlacementId string `json:"dcm_placement_id"` + DcmPlacementName string `json:"dcm_placement_name"` + DcmSiteId string `json:"dcm_site_id"` + Deeplink string `json:"deeplink"` + DeviceAtlasId string `json:"device_atlas_id"` + DeviceManufacturer string `json:"device_manufacturer"` + DeviceModel string `json:"device_model"` + DeviceName string `json:"device_name"` + DeviceType string `json:"device_type"` + EngagementTime string `json:"engagement_time"` + EngagementTimeHour string `json:"engagement_time_hour"` + Environment string `json:"environment"` + Event string `json:"event"` + EventCostId string `json:"event_cost_id"` + EventName string `json:"event_name"` + ExternalDeviceIdMd5 string `json:"external_device_id_md5"` + FbDeeplinkAccountId string `json:"fb_deeplink_account_id"` + FbDeeplinkAdId string `json:"fb_deeplink_ad_id"` + FbDeeplinkAdgroupId string `json:"fb_deeplink_adgroup_id"` + FbDeeplinkCampaignGroupId string `json:"fb_deeplink_campaign_group_id"` + FbDeeplinkCampaignId string `json:"fb_deeplink_campaign_id"` + FbInstallReferrer string `json:"fb_install_referrer"` + FbInstallReferrerAccountId string `json:"fb_install_referrer_account_id"` + FbInstallReferrerAdId string `json:"fb_install_referrer_ad_id"` + FbInstallReferrerAdObjectiveName string `json:"fb_install_referrer_ad_objective_name"` + FbInstallReferrerAdgroupId string `json:"fb_install_referrer_adgroup_id"` + FbInstallReferrerAdgroupName string `json:"fb_install_referrer_adgroup_name"` + FbInstallReferrerCampaignGroupId string `json:"fb_install_referrer_campaign_group_id"` + FbInstallReferrerCampaignGroupName string `json:"fb_install_referrer_campaign_group_name"` + FbInstallReferrerCampaignId string `json:"fb_install_referrer_campaign_id"` + FbInstallReferrerCampaignName string `json:"fb_install_referrer_campaign_name"` + FbInstallReferrerPublisherPlatform string `json:"fb_install_referrer_publisher_platform"` + FireAdid string `json:"fire_adid"` + FirstTracker string `json:"first_tracker"` + FirstTrackerName string `json:"first_tracker_name"` + Gbraid string `json:"gbraid"` + Gclid string `json:"gclid"` + GmpProductType string `json:"gmp_product_type"` + GoogleAdsAdType string `json:"google_ads_ad_type"` + GoogleAdsAdgroupId string `json:"google_ads_adgroup_id"` + GoogleAdsAdgroupName string `json:"google_ads_adgroup_name"` + GoogleAdsCampaignId string `json:"google_ads_campaign_id"` + GoogleAdsCampaignName string `json:"google_ads_campaign_name"` + GoogleAdsCampaignType string `json:"google_ads_campaign_type"` + GoogleAdsCreativeId string `json:"google_ads_creative_id"` + GoogleAdsEngagementType string `json:"google_ads_engagement_type"` + GoogleAdsExternalCustomerId string `json:"google_ads_external_customer_id"` + GoogleAdsKeyword string `json:"google_ads_keyword"` + GoogleAdsMatchtype string `json:"google_ads_matchtype"` + GoogleAdsNetworkSubtype string `json:"google_ads_network_subtype"` + GoogleAdsNetworkType string `json:"google_ads_network_type"` + GoogleAdsPlacement string `json:"google_ads_placement"` + GoogleAdsVideoId string `json:"google_ads_video_id"` + GoogleAppSetId string `json:"google_app_set_id"` + GpsAdid string `json:"gps_adid"` + GpsAdidMd5 string `json:"gps_adid_md5"` + HardwareName string `json:"hardware_name"` + IadAdId string `json:"iad_ad_id"` + IadConversionType string `json:"iad_conversion_type"` + IadCountryOrRegion string `json:"iad_country_or_region"` + IadKeyword string `json:"iad_keyword"` + IadKeywordId string `json:"iad_keyword_id"` + IadOrgId string `json:"iad_org_id"` + Idfa string `json:"idfa"` + IdfaAndroidId string `json:"idfa_android_id"` + IdfaGpsAdid string `json:"idfa_gps_adid"` + IdfaGpsAdidFireAdid string `json:"idfa_gps_adid_fire_adid"` + IdfaMd5 string `json:"idfa_md5"` + IdfaMd5Hex string `json:"idfa_md5_hex"` + IdfaUpper string `json:"idfa_upper"` + Idfv string `json:"idfv"` + IdfvGoogleAppSetId string `json:"idfv_google_app_set_id"` + ImpressionBased string `json:"impression_based"` + ImpressionTime string `json:"impression_time"` + ImpressionTimeHour string `json:"impression_time_hour"` + InstalledAt int64 `json:"installed_at"` + InstalledAtHour int64 `json:"installed_at_hour"` + IpAddress string `json:"ip_address"` + IsImported string `json:"is_imported"` + IsOrganic string `json:"is_organic"` + IsReattributed string `json:"is_reattributed"` + IsS2s string `json:"is_s2s"` + Isp string `json:"isp"` + Label string `json:"label"` + Language string `json:"language"` + LastFallbackTime string `json:"last_fallback_time"` + LastFallbackTimeHour string `json:"last_fallback_time_hour"` + LastSessionTime string `json:"last_session_time"` + LastTracker string `json:"last_tracker"` + LastTrackerName string `json:"last_tracker_name"` + LifetimeSessionCount string `json:"lifetime_session_count"` + MacMd5 string `json:"mac_md5"` + MacSha1 string `json:"mac_sha1"` + MatchType string `json:"match_type"` + Mcc string `json:"mcc"` + Mnc string `json:"mnc"` + NetworkName string `json:"network_name"` + NetworkType string `json:"network_type"` + Nonce string `json:"nonce"` + Oaid string `json:"oaid"` + OaidMd5 string `json:"oaid_md5"` + OsName string `json:"os_name"` + OsVersion string `json:"os_version"` + PartnerParameters string `json:"partner_parameters"` + Platform string `json:"platform"` + PostalCode string `json:"postal_code"` + PredictedValue string `json:"predicted_value"` + PredictionLabel string `json:"prediction_label"` + PredictionPeriodDays string `json:"prediction_period_days"` + ProxyIpAddress string `json:"proxy_ip_address"` + PublisherParameters string `json:"publisher_parameters"` + PushToken string `json:"push_token"` + Random string `json:"random"` + RandomUserId string `json:"random_user_id"` + ReattributedAt int64 `json:"reattributed_at"` + ReattributedAtHour int64 `json:"reattributed_at_hour"` + ReceivedAt int64 `json:"received_at"` + Referrer string `json:"referrer"` + Reftag string `json:"reftag"` + Region string `json:"region"` + ReinstalledAt int64 `json:"reinstalled_at"` + ReportingCurrency string `json:"reporting_currency"` + ReportingRevenue string `json:"reporting_revenue"` + Revenue string `json:"revenue"` + RevenueCny string `json:"revenue_cny"` + RevenueCnyCents string `json:"revenue_cny_cents"` + RevenueFloat string `json:"revenue_float"` + RevenueUsd string `json:"revenue_usd"` + RevenueUsdCents string `json:"revenue_usd_cents"` + Rida string `json:"rida"` + RokuContentId string `json:"roku_content_id"` + RokuPlacementType string `json:"roku_placement_type"` + SanEngagementTimes string `json:"san_engagement_times"` + SdkVersion string `json:"sdk_version"` + SessionCount string `json:"session_count"` + SimSlotIds string `json:"sim_slot_ids"` + SkCoarseConversionValue string `json:"sk_coarse_conversion_value"` + SkConversionValue string `json:"sk_conversion_value"` + SkConversionValueDevice string `json:"sk_conversion_value_device"` + SkConversionValueDeviceTime string `json:"sk_conversion_value_device_time"` + SkConversionValueStatus string `json:"sk_conversion_value_status"` + SkConversionValueTime string `json:"sk_conversion_value_time"` + SkCvWindowExpiration string `json:"sk_cv_window_expiration"` + SkRegisteredAt int64 `json:"sk_registered_at"` + SkTimerExpiration string `json:"sk_timer_expiration"` + SnapchatAdId string `json:"snapchat_ad_id"` + Store string `json:"store"` + ThirdPartySharingDisabled string `json:"third_party_sharing_disabled"` + Tifa string `json:"tifa"` + TimeSpent string `json:"time_spent"` + Timezone string `json:"timezone"` + Tracker string `json:"tracker"` + TrackerName string `json:"tracker_name"` + TrackingEnabled string `json:"tracking_enabled"` + TrackingLimited string `json:"tracking_limited"` + TweetId string `json:"tweet_id"` + TwitterLineItemId string `json:"twitter_line_item_id"` + UninstalledAt int64 `json:"uninstalled_at"` + UserAgent string `json:"user_agent"` + Vida string `json:"vida"` + VizioContentId string `json:"vizio_content_id"` + VizioPlacementType string `json:"vizio_placement_type"` + Wbraid string `json:"wbraid"` + WebUuid string `json:"web_uuid"` + WinAdid string `json:"win_adid"` + WinHwid string `json:"win_hwid"` + WinNaid string `json:"win_naid"` + WinUdid string `json:"win_udid"` + WithinCallbackTtl string `json:"within_callback_ttl"` + YahooCreativeId string `json:"yahoo_creative_id"` + YahooCreativeName string `json:"yahoo_creative_name"` + YahooSiteId string `json:"yahoo_site_id"` + AdImpressionsCount int64 `json:"ad_impressions_count,omitempty"` // 时间 + AdMediationPlatform string `json:"ad_mediation_platform,omitempty"` + AdRevenueNetwork string `json:"ad_revenue_network,omitempty"` + AdRevenuePayload string `json:"ad_revenue_payload,omitempty"` + AdRevenuePlacement string `json:"ad_revenue_placement,omitempty"` + AdRevenueUnit string `json:"ad_revenue_unit,omitempty"` +} + +type AdjustExportFilter struct { + Filename string + Tokens []string + Event string + Fields []string + CreatedAtFrom time.Time + CreatedAtTo time.Time + Location *time.Location //当前时期 + Countries []string + Channels []string + InstallTimeFrom *time.Time + InstallTimeTo *time.Time + InstallHour *int + SumFields []string + Conditons map[string][]string + ConditionsNeq map[string][]string + CsvHeaders []string //csv头部 + Offset int +} + +type AdjustCondition struct { + Key int64 `json:"key"` + Value string `json:"value"` +} + +type CallBackData struct { + Token string + Content []string + MapKeyData map[string]interface{} +} + +type AdjustEventType string + +const ( + AdjustEventTypeInstall AdjustEventType = "install" + AdjustEventTypeGlobal AdjustEventType = "global" + AdjustEventTypeAdRevenue AdjustEventType = "ad_revenue" +) + +func (d AdjustEventType) String() string { + return string(d) +} + +func CheckAdjustQueueCondition(filter *AdjustExportFilter, info *CallbackInfo) (*CallBackData, bool) { + //判断token + if !InSlice(info.AppToken, filter.Tokens) { + return nil, false + } + + //判断事件 + if filter.Event == AdjustEventTypeGlobal.String() { + if info.ActivityKind == AdjustEventTypeAdRevenue.String() { + return nil, false + } + } else { + if info.ActivityKind != filter.Event { + return nil, false + } + } + + //判断创建时间 @todo + fromTime, _ := time.ParseInLocation(time.DateTime, fmt.Sprintf("%s 00:00:00", filter.CreatedAtFrom.In(time.Local).Format(time.DateOnly)), filter.Location) + toTime, _ := time.ParseInLocation(time.DateTime, fmt.Sprintf("%s 23:59:59", filter.CreatedAtTo.In(time.Local).Format(time.DateOnly)), filter.Location) + if info.CreatedAt < fromTime.Unix() || info.CreatedAt > toTime.Unix() { + return nil, false + } + + //判断国家 + if len(filter.Countries) > 0 { + if !InSlice(info.Country, filter.Countries) { + return nil, false + } + } + + //判断渠道 + if len(filter.Channels) > 0 { + if !InSlice(info.NetworkName, filter.Channels) { + return nil, false + } + } + + //判断安装时间 + if filter.InstallTimeFrom != nil && filter.InstallTimeTo != nil { + installFormTime, _ := time.ParseInLocation(time.DateTime, fmt.Sprintf("%s 00:00:00", filter.InstallTimeFrom.In(time.Local).Format(time.DateOnly)), filter.Location) + installToTime, _ := time.ParseInLocation(time.DateTime, fmt.Sprintf("%s 23:59:59", filter.InstallTimeTo.In(time.Local).Format(time.DateOnly)), filter.Location) + if info.InstalledAt < installFormTime.Unix() || info.InstalledAt > installToTime.Unix() { + return nil, false + } + } + + //判断小时 + if filter.InstallHour != nil { + installHour := *filter.InstallHour + if info.CreatedAt-info.InstalledAt > 3600*int64(installHour) { + return nil, false + } + } + + mapKeyData := utils.StructToMapJson(*info) + //判断自定义条件等于,仅支持string + if len(filter.Conditons) > 0 { + for k, filterValue := range filter.Conditons { + //如果自定义字段存在,判断值是否符合 + if mapVal, ok := mapKeyData[k].(string); ok { + if !InSlice(mapVal, filterValue) { + return nil, false + } + } else { + return nil, false + } + } + } + + //判断自定义条件不等于,仅支持string + if len(filter.ConditionsNeq) > 0 { + for k, filterValue := range filter.ConditionsNeq { + //如果自定义字段存在,判断值是否符合 + if mapVal, ok := mapKeyData[k].(string); ok { + if utils.InSlice("NULL", filterValue) { + if mapVal == "" { + return nil, false + } + } else { + // 存在值则跳过数据 + if InSlice(mapVal, filterValue) { + return nil, false + } + } + + } else { + return nil, false + } + } + } + + //获取筛选字段的值 + content := make([]string, 0) + for _, field := range filter.Fields { + if mapVal, ok := mapKeyData[field]; ok { + content = append(content, fmt.Sprintf("%v", mapVal)) + } + + //单独处理date + if field == "date" { + rowDate := time.Unix(info.CreatedAt, 0) + rowDateString := rowDate.In(filter.Location).Format(time.DateOnly) + content = append(content, rowDateString) + } + } + + //根据自定义字段返回数据 + data := &CallBackData{ + Token: info.AppToken, + Content: content, + MapKeyData: mapKeyData, + } + + return data, true +} + +func InSlice(str string, slice []string) bool { + for _, v := range slice { + if formatString(v) == formatString(str) { + return true + } + } + return false +} + +func formatString(str string) string { + return strings.TrimSpace(strings.ToLower(str)) +} diff --git a/app/eonline/internal/biz/biz.go b/app/eonline/internal/biz/biz.go new file mode 100644 index 0000000..fb02052 --- /dev/null +++ b/app/eonline/internal/biz/biz.go @@ -0,0 +1,221 @@ +package biz + +import ( + "context" + "fmt" + "time" + + "sandc/pkg/utils" + + "github.com/google/wire" + "gorm.io/gorm" +) + +// ProviderSet is biz providers. +var ProviderSet = wire.NewSet(NewEonlineUsecase) + +type Cache interface { + GetValue(ctx context.Context, key string) (string, error) + DelValue(ctx context.Context, keys ...string) error + WriteValue(ctx context.Context, key string, value interface{}, timeout int32) error + Remember(ctx context.Context, key string, secone int32, fn func(ctx context.Context) (interface{}, error)) ([]byte, error) + RedisLock(ctx context.Context, key string, value interface{}, timeout int32) (bool, error) + RedisUnLock(ctx context.Context, key string) (bool, error) + IncrValue(ctx context.Context, key string) error +} + +type Transaction interface { + InTx(context.Context, func(ctx context.Context) error) error + DB(ctx context.Context) *gorm.DB +} + +var SignFixedParameters = []string{ + "Platform", + "Deviceid", + "Version", + "Ip", + "Ts", +} + +// PayoutItem 账号状态 +type PayoutItemId int + +const ( + PayoutItemId1 PayoutItemId = iota + 1 // 提现0.1 + PayoutItemId2 // 提现金币大额1 + PayoutItemId3 // 提现绿钞大额1 + PayoutItemId4 // 5.0美金,身份文件提交奖励 +) + +var PayoutItemIdAmountes = []float64{ + PayoutItemId1: 5, + PayoutItemId2: 0.1, + PayoutItemId3: 0.1, +} + +func (d PayoutItemId) Float64() float64 { + n := int(d) + if n < len(PayoutItemIdAmountes) && n > 0 { + return PayoutItemIdAmountes[n] + } + return 0 +} + +// PayoutItemStatus item状态类型 +type PayoutItemStatus uint + +const ( + // 可提现 + PayoutItemStatusAvailable PayoutItemStatus = iota + 1 + // 条件未达成 + PayoutItemStatusUnfinished + // 已提现 + PayoutItemStatusPayouted + // 禁止提现 + PayoutItemStatusForbidden + // 提现中 + PayoutItemStatusPayouting +) + +var PayoutItemStatusNames = []string{ + PayoutItemStatusAvailable: "available", + PayoutItemStatusUnfinished: "unfinished", + PayoutItemStatusPayouted: "payouted", + PayoutItemStatusForbidden: "forbidden", +} + +func (d PayoutItemStatus) String() string { + n := int(d) + if n < len(PayoutItemStatusNames) && n > 0 { + return PayoutItemStatusNames[n] + } + return "" +} + +// PayoutStatus +type PayoutStatus uint + +const ( + // 提现中 + PayoutStatusPayouting PayoutStatus = iota + 1 + // 提现成功 + PayoutStatusPayouted + // 提现失败 + PayoutStatusPayoutFailed +) + +var PayoutStatusNames = []string{ + PayoutStatusPayouting: "payouting", + PayoutStatusPayouted: "payouted", + PayoutStatusPayoutFailed: "payout_failed", +} + +func (d PayoutStatus) String() string { + n := int(d) + if n < len(PayoutStatusNames) && n > 0 { + return PayoutStatusNames[n] + } + return "" +} + +// PayoutRecordLimit1 提现次数总限制 +const PayoutRecordLimit1 = 1 +const PayoutRecordLimit2 = 0 +const PayoutRecordLimit3 = 0 +const PayoutRecordLimit4 = 0 + +// PayoutAmountLimit 提现金额限制 +const PayoutAmountLimit = 5 + +// GenPayoutUuid 生成payout_uuid +func GenPayoutUuid(deviceId string) string { + str := fmt.Sprintf("%s-tk0630", deviceId) + str = utils.MD5Any(str) + return str +} + +// GetUuidPayoutRedisKey 获取uuid redis key,当日提现记录 +func GetUuidPayoutRedisKey(uuid string, date string, itemId uint32) string { + if date == "" { + date = time.Now().Format("20060102") + } + // uuidRedisKey := fmt.Sprintf("pt_%s_%s", uuid, date) + uuidRedisKey := fmt.Sprintf("pt:%s_%s_%d", uuid, date, itemId) + return uuidRedisKey +} + +// GetUuidLockRedisKey 获取uuid redis lock key,提现操作加锁,防止并发 +func GetUuidLockRedisKey(uuid string, date string) string { + if date == "" { + date = time.Now().Format("20060102") + } + uuidRedisKey := fmt.Sprintf("ptlock_%s_%s", uuid, date) + return uuidRedisKey +} + +// GetPayoutNotifyRedisKey 获取提现通知redis key +func GetPayoutNotifyRedisKey(payoutId string, date string) string { + if date == "" { + date = time.Now().Format("20060102") + } + uuidRedisKey := fmt.Sprintf("pt_notify_%s_%s", payoutId, date) + return uuidRedisKey +} + +// GetUserLoginRedisKey 获取用户登录redis key,当日登录记录 +func GetUserLoginRedisKey(userId string, date string) string { + if date == "" { + date = time.Now().Format("20060102") + } + uuidRedisKey := fmt.Sprintf("pt_login_%s_%s", userId, date) + return uuidRedisKey +} + +// GetPayoutOrderIdRedisKey 获取提现订单对应的 提现reids记录 GetUuidPayoutRedisKey生成的 redis key, +func GetPayoutOrderIdRedisKey(orderId string) string { + uuidRedisKey := fmt.Sprintf("ts:%s", orderId) + return uuidRedisKey +} + +// 用户的paypal账号,生成审核结果的 redis key +func GetCheckResultRedisKey(account string) string { + return fmt.Sprintf("pt_result:%s", account) +} + +// 用户的paypal账号,生成审核没通过的原因描述的 redis key +func GetCheckResultFailRedisKey(account string) string { + return fmt.Sprintf("pt_fail:%s", account) +} + +// 用户的paypal账号,生成提交审核的 redis key 存储的数据结构[paypal, uuid] +func GetCheckSubmitRedisKey(account string) string { + return fmt.Sprintf("pt_submit:%s", account) +} + +// 用户的paypal账号,生成提交审核的 redis key 存储的数据结构[uuid, paypal] +func GetUuid2PaypalRedisKey(uuid string) string { + return fmt.Sprintf("pt_uuid:%s", uuid) +} + +// 提交审核计数的 redis key +func GetCheckSubmitNumRedisKey() string { + return fmt.Sprintf("svr_submitNum") +} + +// 返回true,表示t1,t2是同一天 +func isSameDay(t1, t2 time.Time) bool { + y1 := t1.Year() + y2 := t2.Year() + if y1 != y2 { + return false + } + + d1 := t1.YearDay() + d2 := t2.YearDay() + + if d1 != d2 { + return false + } + + return true +} diff --git a/app/eonline/internal/biz/eonline.go b/app/eonline/internal/biz/eonline.go new file mode 100644 index 0000000..377d1ff --- /dev/null +++ b/app/eonline/internal/biz/eonline.go @@ -0,0 +1,1546 @@ +package biz + +import ( + "context" + "encoding/json" + "fmt" + "strconv" + "strings" + "time" + + "sandc/app/eonline/internal/conf" + "sandc/app/eonline/internal/config" + "sandc/pkg/geo" + "sandc/pkg/middleware/xhttp" + "sandc/pkg/utils" + + "github.com/go-kratos/kratos/v2/log" + "github.com/hibiken/asynq" + "github.com/tx7do/kratos-transport/broker" +) + +type Eonline struct { + Hello string +} + +type EonlineRepo interface { + CreateEonline(context.Context, *Eonline) error + UpdateEonline(context.Context, *Eonline) error + + GetUserByUuid(ctx context.Context, uuid string) (*PayoutUser, error) + CreatePayoutUser(ctx context.Context, item *PayoutUser) (*PayoutUser, error) + SearchPayoutRecord(ctx context.Context, opts *SearchPayoutRecordReq) (*ListPayoutRecord, error) + GetPayoutLeftum(ctx context.Context, uuid string) (int, error) + GetPayoutRecordCountByAccount(ctx context.Context, account string) (int, error) + CreatePayoutRecord(ctx context.Context, item *PayoutRecord) (*PayoutRecord, error) + UpdatePayoutRecordNotify(ctx context.Context, recordNo string, payoutId string, status uint8, fail string) error + GetPayoutRecordListByUuid(ctx context.Context, uuid string) ([]*PayoutRecord, error) + // GetPayoutRecordByRecordNo 获取提现记录 + GetPayoutRecordByRecordNo(ctx context.Context, recordNo string) (*PayoutRecord, error) + // 根据account、status 获取提现记录 + GetPayoutRecordByAccountStatus(ctx context.Context, account string, status, pageIndex, pageSize int) ([]*PayoutRecord, error) + // UpdatePayoutUserLoginDays 更新用户登录天数 + UpdatePayoutUserLoginDays(ctx context.Context, uuid string, loginDays uint) error + // 更新 客户端上传的、需要保持的数据 + UpdatePayoutUserClientData(ctx context.Context, uuid string, clientData string) error + + CreateBonusRecord(ctx context.Context, item *BonusRecord) (*BonusRecord, error) + UpdateBonusCur(ctx context.Context, id uint32, bonusCur uint32) error +} + +type EonlineUsecase struct { + repo EonlineRepo + conf *conf.Bootstrap + log *log.Helper + tx Transaction + pscli *PagsmileClient + cache Cache + geocli *geo.GeoClient +} + +func NewEonlineUsecase(repo EonlineRepo, conf *conf.Bootstrap, tx Transaction, logger log.Logger, cache Cache) *EonlineUsecase { + return &EonlineUsecase{ + repo: repo, + tx: tx, + log: log.NewHelper(logger), + pscli: NewPagsmileClient(&PayoutClient{ + AppId: conf.Pagsmile.Payout.AppId, + AppKey: conf.Pagsmile.Payout.AppKey, + ApiUrl: conf.Pagsmile.Payout.ApiUrl, + NotifyUrl: conf.Pagsmile.Payout.NotifyUrl, + }, logger), + conf: conf, + cache: cache, + geocli: geo.NewGeoClient(conf.Server.GeoFile), + } +} + +func (uc *EonlineUsecase) Create(ctx context.Context, g *Eonline) error { + return uc.repo.CreateEonline(ctx, g) +} + +func (uc *EonlineUsecase) Update(ctx context.Context, g *Eonline) error { + return uc.repo.UpdateEonline(ctx, g) +} + +func (uc *EonlineUsecase) KakfaExampleConsumer(_ context.Context, topic string, headers broker.Headers, msg *ExampleStruct) error { + log.Infof("Topic %s, Headers: %+v, Payload: %+v\n", topic, headers, msg) + return nil +} + +const EonlineTaskName = "eonline:task" + +// AsynqEonlineTaskHandler Asynq handler task +func (uc *EonlineUsecase) AsynqEonlineTaskHandler(ctx context.Context, t *asynq.Task) error { + // do something + // do some else logging + log.Info("AsynqEonlineTaskHandle: ", string(t.Payload())) + return nil +} + +// PayInitReq 支付初始化请求 +type PayInitReq struct { + DeviceId string +} + +// PayInitReply 支付初始化响应 +type PayInitReply struct { + UUID string + ClientData string + Days uint32 // 登录天数 + Items []*PayoutItem +} + +// PayoutItem 支付项目 +type PayoutItem struct { + Id uint + Amount float64 + Status uint8 +} + +// PayInit +func (uc *EonlineUsecase) PayInit(ctx context.Context, req *PayoutAddtionalReq) (*PayInitReply, error) { + // 生成唯一用户ID + uuid := GenPayoutUuid(req.DeviceId) + err := uc.initUserLoginDays(ctx, uuid, req) + if err != nil { + return nil, fmt.Errorf("init user login days error: %v", err) + } + + // 判断是否提现 + payoutValidRes, err, _ := uc.checkPayoutValid(ctx, uuid, req) + if err != nil { + // 如果不允许提现,状态全部更改为不可提现 + payoutValidRes.LeftNum[uint(PayoutItemId1)] = -1 + payoutValidRes.LeftNum[uint(PayoutItemId2)] = -1 + payoutValidRes.LeftNum[uint(PayoutItemId3)] = -1 + payoutValidRes.LeftNum[uint(PayoutItemId4)] = -1 + } + + items := make([]*PayoutItem, 0) + for k, v := range PayoutItemIdAmountes { + if k == 0 { + continue + } + // 生成对应的payoutItem + payoutItem := &PayoutItem{ + Id: uint(k), + Amount: v, + Status: uint8(uc.getPayoutItemStatus(ctx, uuid, req.Ts, uint(k), payoutValidRes)), + } + + items = append(items, payoutItem) + } + + reply := &PayInitReply{ + UUID: uuid, + ClientData: payoutValidRes.ClientData, + Days: payoutValidRes.Days, + Items: items, + } + + return reply, nil +} + +// initUserLoginDays 初始化用户登录天数 +func (uc *EonlineUsecase) initUserLoginDays(ctx context.Context, uuid string, req *PayoutAddtionalReq) error { + // 插入用户登录信息 + // 判断当前用户是否存在 + user, err := uc.repo.GetUserByUuid(ctx, uuid) + if err != nil { + return err + } + + // 定义用户登录redis key + keyDay := "" + if uc.conf.Server.Env == "qa" { + ts, _ := strconv.ParseInt(req.Ts, 10, 64) + keyDay = time.Unix(ts, 0).Format("20060102") + } + userLoginRedisKey := GetUserLoginRedisKey(uuid, keyDay) + + // 创建用户 + if user.Id == 0 { + // 记录登录用户 + _, err = uc.repo.CreatePayoutUser(ctx, &PayoutUser{ + Uuid: uuid, + DeviceId: req.DeviceId, + Platform: req.Platform, + Version: req.Version, + Country: req.Country, + Ip: req.Ip, + LoginDays: 1, + }) + if err != nil { + return err + } + + // 是否已记录当前登录行为 + err = uc.cache.WriteValue(ctx, userLoginRedisKey, "1", 24*3600) + if err != nil { + return fmt.Errorf("write redis error: %v", err) + } + } else { + // 更新登录天数 + currentValue, err := uc.cache.GetValue(ctx, userLoginRedisKey) + if err != nil { + return fmt.Errorf("get redis value error: %v", err) + } + + if currentValue == "" { + // 更新登录天数 + loginDays := user.LoginDays + 1 + err = uc.repo.UpdatePayoutUserLoginDays(ctx, uuid, loginDays) + if err != nil { + return fmt.Errorf("update payout user login days error: %v", err) + } + + // 记录当前登录行为 + err = uc.cache.WriteValue(ctx, userLoginRedisKey, "1", 24*3600) + if err != nil { + return fmt.Errorf("write redis error: %v", err) + } + } + } + + return nil +} + +func (uc *EonlineUsecase) updateUserClientData(ctx context.Context, uuid string, clientData string) error { + err := uc.repo.UpdatePayoutUserClientData(ctx, uuid, clientData) + if err != nil { + return fmt.Errorf("update payout user clientData error: %v", err) + } + + return nil +} + +// getPayoutItemStatus 获取提现项目状态 +func (uc *EonlineUsecase) getPayoutItemStatus(ctx context.Context, uuid, ts string, itemId uint, payoutValidRes *PayoutValidRes) PayoutItemStatus { + leftNum, ok := payoutValidRes.LeftNum[itemId] + if !ok || leftNum <= 0 { + return PayoutItemStatusForbidden + } + + if _, ok = payoutValidRes.RecordStatusMap[itemId]; ok { + switch payoutValidRes.RecordStatusMap[itemId] { + // 提现成功的状态,返回已提现 + case uint8(PayoutStatusPayouted): + return PayoutItemStatusPayouted + // 提现失败的状态,返回可提现 + case uint8(PayoutStatusPayoutFailed): + return PayoutItemStatusAvailable + // 提现中的状态,返回提现中 + case uint8(PayoutStatusPayouting): + return PayoutItemStatusPayouting + default: + return PayoutItemStatusForbidden + } + } + + // 如果没有提现记录,返回可提现 + if itemId == 1 { + return PayoutItemStatusAvailable + } + + // 如果没有提现记录,返回可提现 + // 根据uuid查询用户是否满足第二天的提现条件 + // user, err := uc.repo.GetUserByUuid(ctx, uuid) + // if err != nil { + // return PayoutItemStatusForbidden + // } + // + // if user.LoginDays >= 3 && payoutValidRes.LeftNum[itemId] == 1 { + // return PayoutItemStatusAvailable + // } else { + // return PayoutItemStatusUnfinished + // } + + keyDay := "" + if uc.conf.Server.Env == "qa" { + ts, _ := strconv.ParseInt(ts, 10, 64) + keyDay = time.Unix(ts, 0).Format("20060102") + } + + uuidRedisKey := GetUuidPayoutRedisKey(uuid, keyDay, uint32(itemId)) + v, err := uc.cache.GetValue(ctx, uuidRedisKey) + if err != nil { + // uc.log.WithContext(ctx).Errorf("get redis value error: %v", err) + log.Infof("get redis value error: %v", err) + return PayoutItemStatusUnfinished + } + if v == "" && leftNum > 0 { + return PayoutItemStatusAvailable + } else { + return PayoutItemStatusUnfinished + } +} + +// PayoutValidRes 提现是否有效响应 +type PayoutValidRes struct { + Days uint32 + LeftNum map[uint]int + RecordStatusMap map[uint]uint8 + ClientData string +} + +// true表示可以提现的国家 +func canPayoutCountry(country string) bool { + if country == "US" || country == "CA" || country == "IN" || country == "PH" || country == "ID" || country == "VN" || country == "TH" || country == "BR" { + return true + } + return false +} + +// 转换国家代码 +func convertCountry(country string) string { + if country == "US" { // 美国 + return "USA" + } else if country == "CA" { // 加拿大 + return "CAN" + } else if country == "IN" { // 印度 + return "IND" + } else if country == "PH" { // 菲律宾 + return "PHL" + } else if country == "ID" { // 印度尼西亚 + return "IDN" + } else if country == "VN" { // 越南 + return "VNM" + } else if country == "TH" { // 泰国 + return "THA" + } else if country == "BR" { // 巴西 + return "BRA" + } + + log.Infof("convertCountry error: country[%s]", country) + return country +} + +// checkPayoutValid 检查提现是否有效,1+2位数 +func (uc *EonlineUsecase) checkPayoutValid(ctx context.Context, uuid string, opts *PayoutAddtionalReq) (*PayoutValidRes, error, int32) { + payoutValidRes := &PayoutValidRes{ + Days: 0, + LeftNum: make(map[uint]int), + RecordStatusMap: make(map[uint]uint8), + } + + // 判断是否可以提现,国家判断 + // if opts.Country != "US" && opts.Country != "CA" && opts.Country != "IN" && opts.Country != "PH" { + // if !canPayoutCountry(opts.Country) { + // return payoutValidRes, fmt.Errorf("country [%s] not support", opts.Country), 101 + // } + + if opts.ItemId > 0 { + if opts.ItemId <= uint32(len(PayoutItemIdAmountes)) { + // 判断金额是否一致 + if int32(opts.Amount*100) != int32(PayoutItemIdAmountes[int(opts.ItemId)]*100) { + return payoutValidRes, fmt.Errorf("amount error"), 102 + } + } else { + return payoutValidRes, fmt.Errorf("ItemId error"), 103 + } + } + + user, err := uc.repo.GetUserByUuid(ctx, uuid) + if err != nil { + return payoutValidRes, err, 1 + } + + payoutValidRes.Days = uint32(user.LoginDays) + payoutValidRes.ClientData = user.ClientData + + records, err := uc.repo.GetPayoutRecordListByUuid(ctx, uuid) + if err != nil { + return payoutValidRes, err, 1 + } + + var num1, num2, num3, num4 int + var lastest1, lastest2, lastest3, lastest4 *PayoutRecord + var leftAmount float64 + for _, record := range records { + if record.ItemId == uint(PayoutItemId4) { + continue + } + + switch record.ItemId { + case uint(PayoutItemId1): + if record.Status == uint8(PayoutStatusPayouted) || record.Status == uint8(PayoutStatusPayouting) { + num1++ + leftAmount += record.Amount + } + if lastest1 != nil { + if lastest1.CreatedAt.Compare(record.CreatedAt) < 0 { + lastest1 = record + } + } else { + lastest1 = record + } + case uint(PayoutItemId2): + if record.Status == uint8(PayoutStatusPayouted) || record.Status == uint8(PayoutStatusPayouting) { + num2++ + leftAmount += record.Amount + } + if lastest2 != nil { + if lastest2.CreatedAt.Compare(record.CreatedAt) < 0 { + lastest2 = record + } + } else { + lastest2 = record + } + case uint(PayoutItemId3): + if record.Status == uint8(PayoutStatusPayouted) || record.Status == uint8(PayoutStatusPayouting) { + num3++ + leftAmount += record.Amount + } + if lastest3 != nil { + if lastest3.CreatedAt.Compare(record.CreatedAt) < 0 { + lastest3 = record + } + } else { + lastest3 = record + } + case uint(PayoutItemId4): + if record.Status == uint8(PayoutStatusPayouted) || record.Status == uint8(PayoutStatusPayouting) { + num4++ + } + if lastest4 != nil { + if lastest4.CreatedAt.Compare(record.CreatedAt) < 0 { + lastest4 = record + } + } else { + lastest4 = record + } + default: + log.Infof("checkPayoutValid error: uuid[%s] record.ItemId[%d] not dispatch No.1", uuid, record.ItemId) + } + } + + ts, _ := strconv.ParseInt(opts.Ts, 10, 64) + + keyDay := "" + if uc.conf.Server.Env == "qa" { + keyDay = time.Unix(ts, 0).Format("20060102") + } + + var now time.Time + now = time.Unix(ts, 0) + recordStatusMap := make(map[uint]uint8) + if lastest1 != nil { + if isSameDay(now, lastest1.CreatedAt) { + recordStatusMap[lastest1.ItemId] = lastest1.Status + } + } + + if lastest2 != nil { + if isSameDay(now, lastest2.CreatedAt) { + recordStatusMap[lastest2.ItemId] = lastest2.Status + } + } + + if lastest3 != nil { + if isSameDay(now, lastest3.CreatedAt) { + recordStatusMap[lastest3.ItemId] = lastest3.Status + } + } + + if lastest4 != nil { + if isSameDay(now, lastest4.CreatedAt) { + recordStatusMap[lastest4.ItemId] = lastest4.Status + } + } + + // true表示今天已提现过 + // callCheckTodayPayoutStatus := func(itemId PayoutItemId) bool { + // uuidRedisKey := GetUuidPayoutRedisKey(uuid, keyDay, uint32(itemId)) + // value, err := uc.cache.GetValue(ctx, uuidRedisKey) + // if err == nil { + // if value != "" { + // return true + // } + // } + // return false + // } + + // 检查已提现次数 + if num1 < PayoutRecordLimit1 { + payoutValidRes.LeftNum[uint(PayoutItemId1)] = PayoutRecordLimit1 - num1 + } else { + payoutValidRes.LeftNum[uint(PayoutItemId1)] = -1 + } + + if num2 < PayoutRecordLimit2 { + payoutValidRes.LeftNum[uint(PayoutItemId2)] = PayoutRecordLimit2 - num2 + } else { + payoutValidRes.LeftNum[uint(PayoutItemId2)] = -1 + } + + if num3 < PayoutRecordLimit3 { + payoutValidRes.LeftNum[uint(PayoutItemId3)] = PayoutRecordLimit3 - num3 + } else { + payoutValidRes.LeftNum[uint(PayoutItemId3)] = -1 + } + + if num4 < PayoutRecordLimit4 { + payoutValidRes.LeftNum[uint(PayoutItemId4)] = PayoutRecordLimit4 - num4 + } else { + payoutValidRes.LeftNum[uint(PayoutItemId4)] = -1 + } + + payoutValidRes.RecordStatusMap = recordStatusMap + // 判断金额 + leftAmount += opts.Amount + if leftAmount > PayoutAmountLimit { + return payoutValidRes, fmt.Errorf("amount limit"), 104 + } + + if opts.ItemId > 0 { + // 首次提现必须是0.1 + if num1+num2+num3+num4 <= 0 { + if opts.ItemId != uint32(PayoutItemId1) { + return payoutValidRes, fmt.Errorf("ItemId error"), 108 + } + } + + switch opts.ItemId { + case 1: + if num1 >= PayoutRecordLimit1 { + return payoutValidRes, fmt.Errorf("no payout chance"), 105 + } + case 2: + if num2 >= PayoutRecordLimit2 { + return payoutValidRes, fmt.Errorf("no payout chance"), 105 + } + case 3: + if num3 >= PayoutRecordLimit3 { + return payoutValidRes, fmt.Errorf("no payout chance"), 105 + } + case 4: + if num4 >= PayoutRecordLimit4 { + return payoutValidRes, fmt.Errorf("no payout chance"), 105 + } + default: + log.Infof("checkPayoutValid error: uuid[%s] record.ItemId[%d] not dispatch No.2", uuid, opts.ItemId) + return payoutValidRes, fmt.Errorf("no payout chance"), 105 + } + } else { + + } + // if leftNum1 <= 0 { + // return payoutValidRes, fmt.Errorf("no payout chance"), 105 + // } + + if opts.ItemId > 0 { + uuidRedisKey := GetUuidPayoutRedisKey(uuid, keyDay, opts.ItemId) + value, err := uc.cache.GetValue(ctx, uuidRedisKey) + if err != nil { + return payoutValidRes, err, 1 + } + // 次日才能进行第二次提现(逻辑保留) + if value != "" { + return payoutValidRes, fmt.Errorf("no payout chance, please try tomorrow"), 106 + } + } + + // 登录第三日才能体现第二次 + // if leftNum1 == 1 && user.LoginDays < 3 && opts.ItemId > 0 { + // return payoutValidRes, fmt.Errorf("no payout chance, please try tomorrow") + // } + + if len(opts.Account) > 0 { + // 如果是qa环境,test账号不限制提现次数 + // if uc.conf.Server.Env == "qa" && opts.Account == "pagsmile_test_success@pagsmile.com" { + if uc.conf.Server.Env == "qa" && strings.Contains(opts.Account, "@pagsmile.com") { + return payoutValidRes, nil, 0 + } + + // 一个账号只能提现2次 + count, err := uc.repo.GetPayoutRecordCountByAccount(ctx, opts.Account) + if err != nil { + return payoutValidRes, err, 1 + } + + if count >= PayoutRecordLimit1+PayoutRecordLimit2+PayoutRecordLimit3+PayoutRecordLimit4 { + return payoutValidRes, fmt.Errorf("account payouted limit"), 107 + } + } + + return payoutValidRes, nil, 0 +} + +// 1+2位数 +func (uc *EonlineUsecase) checkPayoutValidNew(ctx context.Context, uuid string, opts *PayoutAddtionalReq) (*PayoutValidRes, error, int32) { + x, err := uc.CheckInfo(ctx, &CheckInfoReq{ + Uuid: uuid, + Ts: opts.Ts, + }) + if err != nil { + return nil, fmt.Errorf("error: %w", err), 1 + } + + if x.CanCheckPayOut == 1 { + return nil, fmt.Errorf("can't payout"), 150 + } + + payoutValidRes := &PayoutValidRes{ + LeftNum: make(map[uint]int), + RecordStatusMap: make(map[uint]uint8), + } + // 判断是否可以提现,国家判断 + // if opts.Country != "US" && opts.Country != "CA" && opts.Country != "IN" && opts.Country != "PH" { + // if !canPayoutCountry(opts.Country) { + // return payoutValidRes, fmt.Errorf("country [%s] not support", opts.Country), 101 + // } + + // 判断金额是否一致 + if int32(opts.Amount*100) != config.PublicCheckCoin { + return payoutValidRes, fmt.Errorf("amount error"), 102 + } + + records, err := uc.repo.GetPayoutRecordListByUuid(ctx, uuid) + if err != nil { + return payoutValidRes, err, 1 + } + + recordStatusMap := make(map[uint]uint8) + for _, record := range records { + if _, ok := recordStatusMap[record.ItemId]; !ok { + recordStatusMap[record.ItemId] = record.Status + } + } + payoutValidRes.RecordStatusMap = recordStatusMap + + status, ok := recordStatusMap[uint(PayoutItemId4)] + if ok { + if status == uint8(PayoutStatusPayouted) || status == uint8(PayoutStatusPayouting) { + return payoutValidRes, fmt.Errorf("no payout chance"), 105 + } + } + + // keyDay := "" + // if uc.conf.Server.Env == "qa" { + // ts, _ := strconv.ParseInt(opts.Ts, 10, 64) + // keyDay = time.Unix(ts, 0).Format("20060102") + // } + // + // uuidRedisKey := GetUuidPayoutRedisKey(uuid, keyDay) + // value, err := uc.cache.GetValue(ctx, uuidRedisKey) + // if err != nil { + // return payoutValidRes, err + // } + // + // // 次日才能进行第二次体现(逻辑保留) + // if value != "" && opts.ItemId > 0 { + // return payoutValidRes, fmt.Errorf("no payout chance, please try tomorrow") + // } + + return payoutValidRes, nil, 0 +} + +// PayoutAddtionalReq 支付附加请求 +type PayoutAddtionalReq struct { + Platform string + DeviceId string + Version string + EcpmLvl uint32 + ItemId uint32 + Ip string + Amount float64 + Country string + Account string + Ts string + Email string +} + +// Payout 支付请求 +func (uc *EonlineUsecase) Payout(ctx context.Context, uuid string, opts *PayoutAddtionalReq, req *PayoutReq) (*PayoutReplyData, error, int32) { + // 定义一个uuid的redis key + keyDay := "" + if uc.conf.Server.Env == "qa" { + ts, _ := strconv.ParseInt(opts.Ts, 10, 64) + keyDay = time.Unix(ts, 0).Format("20060102") + } + uuidRedisKey := GetUuidPayoutRedisKey(uuid, keyDay, opts.ItemId) + // 定义一个uuid的redis lock key, 用于锁定, 防止并发 + uuidRedisLockKey := GetUuidLockRedisKey(uuid, keyDay) + lock, _ := uc.cache.RedisLock(ctx, uuidRedisLockKey, 1, 20) + if !lock { + return nil, fmt.Errorf("lock error"), 1 + } + defer func() { + uc.cache.RedisUnLock(ctx, uuidRedisLockKey) + }() + + country, currentIp := uc.GetCountryCodeByIp(ctx, opts.Ip) + opts.Country = country + opts.Ip = currentIp + + // 验证提现是否有效 + var err error + var errCode int32 + if opts.ItemId == uint32(PayoutItemId4) { + _, err, errCode = uc.checkPayoutValidNew(ctx, uuid, opts) + } else { + _, err, errCode = uc.checkPayoutValid(ctx, uuid, opts) + } + if err != nil { + switch errCode { + case 101: + errCode = 5 + case 102: + errCode = 6 + case 103: + errCode = 7 + case 104: + errCode = 8 + case 105: + errCode = 9 + case 106: + errCode = 10 + case 107: + errCode = 11 + case 150: + errCode = 12 + default: + log.Infof("checkPayoutValid error: errCode[%d]", errCode) + errCode = 1 + } + return nil, err, errCode + } + + // 判断当前用户是否存在 + user, err := uc.repo.GetUserByUuid(ctx, uuid) + if err != nil { + return nil, err, 1 + } + + // 支付的时候需要替换掉country code + // if country == "US" { // 美国 + // req.Country = "USA" + // } else if country == "CA" { // 加拿大 + // req.Country = "CAN" + // } else if country == "IN" { // 印度 + // req.Country = "IND" + // } else if country == "PH" { // 菲律宾 + // req.Country = "PHL" + // } + req.Country = convertCountry(country) + + // 创建提现用户 + if user.Id == 0 { + // 记录提现的用户 + _, err = uc.repo.CreatePayoutUser(ctx, &PayoutUser{ + Uuid: uuid, + DeviceId: opts.DeviceId, + Platform: opts.Platform, + Version: opts.Version, + Country: country, + Ip: opts.Ip, + LoginDays: 1, + }) + if err != nil { + return nil, err, 1 + } + } + + // 记录提现及操作 + var payoutReplyData PayoutReplyData + var payoutStatus uint8 // 记录提现状态 + err = uc.tx.InTx(ctx, func(ctx context.Context) error { + // 创建提现记录 + + // 以下参数固定 + req.AccountType = "EMAIL" + req.Method = "WALLET" + req.Channel = "PayPal" + req.FeeBear = "merchant" // beneficiary , merchant + // 如果金额大于等于0.3, 收款方承担手续费 + if opts.Amount > 0.3 { + req.FeeBear = "beneficiary" + } + req.SourceCurrency = "USD" + req.ArrivalCurrency = "USD" + reqMd5 := utils.MD5Any(req) + customCode := fmt.Sprintf("PGS_%s", utils.MD5Any(fmt.Sprintf("%s-%d", reqMd5, time.Now().Unix()))) + // 类似唯一订单号 + req.CustomCode = customCode + + record, err := uc.repo.CreatePayoutRecord(ctx, &PayoutRecord{ + Uuid: uuid, + Channel: req.Channel, + ItemId: uint(opts.ItemId), + Amount: opts.Amount, + Account: req.Account, + Currency: req.SourceCurrency, + Status: uint8(PayoutStatusPayouting), + RecordNo: req.CustomCode, + Version: opts.Version, + Email: opts.Email, + }) + if err != nil { + return fmt.Errorf("create payout record error: %v", err) + } + // 第三方提现 + x, err := uc.pscli.out.Payout(ctx, req) + if err != nil { + return err + } + + payoutReplyData = *x + payoutStatus = uint8(PayoutStatusPayouting) + // 等待回调更新状态 + err = uc.repo.UpdatePayoutRecordNotify(ctx, payoutReplyData.CustomCode, payoutReplyData.Id, payoutStatus, "") + if err != nil { + return fmt.Errorf("update payout record_%d status error: %v", record.Id, err) + } + + return nil + }) + + if err != nil { + return nil, err, 1 + } + + // 记录此次提现,用于判断是否可以继续提现 + uc.cache.WriteValue(ctx, uuidRedisKey, "1", 24*3600) + return &payoutReplyData, nil, 0 +} + +// PayoutBrazil 支付请求 +func (uc *EonlineUsecase) PayoutBrazil(ctx context.Context, uuid string, opts *PayoutAddtionalReq, req *PayoutReq) (*PayoutReplyData, error, int32) { + // 定义一个uuid的redis key + keyDay := "" + if uc.conf.Server.Env == "qa" { + ts, _ := strconv.ParseInt(opts.Ts, 10, 64) + keyDay = time.Unix(ts, 0).Format("20060102") + } + uuidRedisKey := GetUuidPayoutRedisKey(uuid, keyDay, opts.ItemId) + // 定义一个uuid的redis lock key, 用于锁定, 防止并发 + uuidRedisLockKey := GetUuidLockRedisKey(uuid, keyDay) + lock, _ := uc.cache.RedisLock(ctx, uuidRedisLockKey, 1, 20) + if !lock { + return nil, fmt.Errorf("lock error"), 1 + } + defer func() { + uc.cache.RedisUnLock(ctx, uuidRedisLockKey) + }() + + country, currentIp := uc.GetCountryCodeByIp(ctx, opts.Ip) + if country != "BR" { + // return nil, fmt.Errorf("Not Brazil") + country = "BR" + } + + opts.Country = country + opts.Ip = currentIp + + // 验证提现是否有效 + var err error + var errCode int32 + if opts.ItemId == uint32(PayoutItemId4) { + _, err, errCode = uc.checkPayoutValidNew(ctx, uuid, opts) + } else { + _, err, errCode = uc.checkPayoutValid(ctx, uuid, opts) + } + if err != nil { + switch errCode { + case 101: + errCode = 5 + case 102: + errCode = 6 + case 103: + errCode = 7 + case 104: + errCode = 8 + case 105: + errCode = 9 + case 106: + errCode = 10 + case 107: + errCode = 11 + case 108: + errCode = 25 + case 150: + errCode = 12 + default: + log.Infof("checkPayoutValid error: errCode[%d]", errCode) + errCode = 1 + } + return nil, err, errCode + } + + // 判断当前用户是否存在 + user, err := uc.repo.GetUserByUuid(ctx, uuid) + if err != nil { + return nil, err, 1 + } + + // 支付的时候需要替换掉country code + req.Country = convertCountry(country) + + // 创建提现用户 + if user.Id == 0 { + // 记录提现的用户 + _, err = uc.repo.CreatePayoutUser(ctx, &PayoutUser{ + Uuid: uuid, + DeviceId: opts.DeviceId, + Platform: opts.Platform, + Version: opts.Version, + Country: country, + Ip: opts.Ip, + LoginDays: 1, + }) + if err != nil { + return nil, err, 1 + } + } + + if len(req.ClientData) > 0 { + uc.updateUserClientData(ctx, uuid, req.ClientData) + } + + // 记录提现及操作 + var payoutReplyData PayoutReplyData + var payoutStatus uint8 // 记录提现状态 + err = uc.tx.InTx(ctx, func(ctx context.Context) error { + // 创建提现记录 + + // 以下参数固定 + req.Method = "PIX" + // req.Channel = "PayPal" + req.FeeBear = "merchant" // beneficiary , merchant + // 如果金额大于等于0.3, 收款方承担手续费 + if opts.Amount > 0.3 { + req.FeeBear = "beneficiary" + } + req.SourceCurrency = "USD" + req.ArrivalCurrency = "BRL" + reqMd5 := utils.MD5Any(req) + customCode := fmt.Sprintf("PGS_%s", utils.MD5Any(fmt.Sprintf("%s-%d", reqMd5, time.Now().Unix()))) + // 类似唯一订单号 + req.CustomCode = customCode + + record, err := uc.repo.CreatePayoutRecord(ctx, &PayoutRecord{ + Uuid: uuid, + Channel: req.Channel, + ItemId: uint(opts.ItemId), + Amount: opts.Amount, + Account: req.Account, + Currency: req.SourceCurrency, + Status: uint8(PayoutStatusPayouting), + RecordNo: req.CustomCode, + Version: opts.Version, + }) + if err != nil { + return fmt.Errorf("create payout record error: %v", err) + } + + // 第三方提现 + x, err := uc.pscli.out.Payout(ctx, req) + if err != nil { + return err + } + + payoutReplyData = *x + payoutStatus = uint8(PayoutStatusPayouting) + // 等待回调更新状态 + err = uc.repo.UpdatePayoutRecordNotify(ctx, payoutReplyData.CustomCode, payoutReplyData.Id, payoutStatus, "") + if err != nil { + return fmt.Errorf("update payout record_%d status error: %v", record.Id, err) + } + + return nil + }) + + if err != nil { + return nil, err, 1 + } + + // 记录此次提现,用于判断是否可以继续提现 + uc.cache.WriteValue(ctx, uuidRedisKey, "1", 24*3600) + + orderId := GetPayoutOrderIdRedisKey(payoutReplyData.Id) + uc.cache.WriteValue(ctx, orderId, uuidRedisKey, 15*3600) + + return &payoutReplyData, nil, 0 +} + +// PayoutNotify 支付回调 +func (uc *EonlineUsecase) PayoutNotify(ctx context.Context, req *PayoutNotifyReq) error { + value, _ := json.Marshal(req) + fmt.Printf("[%s] payout notify: %s \n", time.Now().Format("2006-01-02 15:04:05"), string(value)) + log.Infof("[%s] payout notify: %s \n", time.Now().Format("2006-01-02 15:04:05"), string(value)) + err := uc.pscli.out.PayoutNotify(ctx, req) + if err != nil { + return err + } + + // 测试用代码 + // callTest := func() { + // uuidRedisKey := "pt_6b59480226f21f10a8939807dded2679_20250518_1" + // orderTs := "TS202505080744025sxBs5nA47qPB" + // err = uc.cache.WriteValue(ctx, uuidRedisKey, "1", 24*3600) + // if err != nil { + // err = nil + // } + // orderId := GetPayoutOrderIdRedisKey(orderTs) + // err = uc.cache.WriteValue(ctx, orderId, uuidRedisKey, 15*3600) + // if err != nil { + // err = nil + // } + // + // // 查找删除 + // ctx2 := ctx + // orderId = GetPayoutOrderIdRedisKey(orderTs) + // value2, err2 := uc.cache.GetValue(ctx2, orderId) + // if err2 == nil { + // if value2 != "" { + // err2 = uc.cache.DelValue(ctx2, value2) + // if err2 == nil { + // log.Infof("del GetUuidPayoutRedisKey: %s", value2) + // + // err2 = uc.cache.DelValue(ctx2, orderId) + // if err2 == nil { + // log.Infof("del redis key orderId: %s", orderId) + // } else { + // log.Infof("del redis key orderId: %s err[%v]", orderId, err2) + // } + // } else { + // log.Infof("del GetUuidPayoutRedisKey error: %s err[%v]", value2, err2) + // } + // } else { + // log.Infof("GetPayoutOrderIdRedisKey error: key[%s] value2 is null", orderId) + // } + // } else { + // log.Infof("GetPayoutOrderIdRedisKey error: key[%s] err[%v]", orderId, err2) + // } + // } + + // 判断回调是否有效 + _, err = uc.repo.GetPayoutRecordByRecordNo(ctx, req.CustomCode) + if err != nil { + return fmt.Errorf("get payout record error: %v", err) + } + + // callTest() + + reportData := GetReportData(req.PayoutId) + + // 更新提现记录状态 + var payoutStatus uint8 + switch req.Status { + case "PAID": + payoutStatus = uint8(PayoutStatusPayouted) + + if reportData != nil && reportData.Adjust != nil && reportData.ShuShu != nil { + adjust := GetAdjustData2(reportData.Adjust, "") + shuShu := GetShuShuData2(reportData.ShuShu, "") + SendReport(ctx, adjust, shuShu) + } + case "REJECTED": + payoutStatus = uint8(PayoutStatusPayoutFailed) + + ctx2 := ctx + // ctx2 := context.Background() + orderId := GetPayoutOrderIdRedisKey(req.PayoutId) + value2, err2 := uc.cache.GetValue(ctx2, orderId) + if err2 == nil { + if value2 != "" { + err2 = uc.cache.DelValue(ctx2, value2) + if err2 == nil { + log.Infof("del GetUuidPayoutRedisKey: %s", value2) + + // err2 = uc.cache.DelValue(ctx2, orderId) + // if err2 == nil { + // log.Infof("del redis key orderId: %s", orderId) + // } else { + // log.Infof("del redis key orderId: %s err[%v]", orderId, err2) + // } + } else { + log.Infof("del GetUuidPayoutRedisKey error: %s err[%v]", value2, err2) + } + } else { + log.Infof("GetPayoutOrderIdRedisKey error: key[%s] value2 is null", orderId) + } + } else { + log.Infof("GetPayoutOrderIdRedisKey error: key[%s] err[%v]", orderId, err2) + } + + if reportData != nil && reportData.Adjust != nil && reportData.ShuShu != nil { + adjust := GetAdjustData2(reportData.Adjust, req.Msg) + shuShu := GetShuShuData2(reportData.ShuShu, req.Msg) + SendReport(ctx, adjust, shuShu) + } + default: + payoutStatus = uint8(PayoutStatusPayouting) + } + + err = uc.repo.UpdatePayoutRecordNotify(ctx, req.CustomCode, req.PayoutId, payoutStatus, req.Msg) + if err != nil { + return fmt.Errorf("update payout record status error: %v", err) + } + + // //存入redis给请求提现的用户 + // payoutNotifyRedisKey := GetPayoutNotifyRedisKey(req.PayoutId) + + // err = uc.cache.WriteValue(ctx, payoutNotifyRedisKey, string(value), 24*3600) + // if err != nil { + // return fmt.Errorf("write redis error: %v", err) + // } + + return nil +} + +// PayoutNotify 支付回调 +func (uc *EonlineUsecase) PayoutNotify2(ctx context.Context, req *PayoutNotifyReq) error { + value, _ := json.Marshal(req) + fmt.Printf("[%s] payout notify: %s \n", time.Now().Format("2006-01-02 15:04:05"), string(value)) + log.Infof("[%s] payout notify: %s \n", time.Now().Format("2006-01-02 15:04:05"), string(value)) + // err := uc.pscli.out.PayoutNotify(ctx, req) + // if err != nil { + // return err + // } + + // 判断回调是否有效 + _, err := uc.repo.GetPayoutRecordByRecordNo(ctx, req.CustomCode) + if err != nil { + return fmt.Errorf("get payout record error: %v", err) + } + + // callTest() + + reportData := GetReportData(req.PayoutId) + + // 更新提现记录状态 + var payoutStatus uint8 + switch req.Status { + case "PAID": + payoutStatus = uint8(PayoutStatusPayouted) + + if reportData != nil && reportData.Adjust != nil && reportData.ShuShu != nil { + adjust := GetAdjustData2(reportData.Adjust, "") + shuShu := GetShuShuData2(reportData.ShuShu, "") + SendReport(ctx, adjust, shuShu) + } + case "REJECTED": + payoutStatus = uint8(PayoutStatusPayoutFailed) + + ctx2 := ctx + // ctx2 := context.Background() + orderId := GetPayoutOrderIdRedisKey(req.PayoutId) + value2, err2 := uc.cache.GetValue(ctx2, orderId) + if err2 == nil { + if value2 != "" { + err2 = uc.cache.DelValue(ctx2, value2) + if err2 == nil { + log.Infof("del GetUuidPayoutRedisKey: %s", value2) + + // err2 = uc.cache.DelValue(ctx2, orderId) + // if err2 == nil { + // log.Infof("del redis key orderId: %s", orderId) + // } else { + // log.Infof("del redis key orderId: %s err[%v]", orderId, err2) + // } + } else { + log.Infof("del GetUuidPayoutRedisKey error: %s err[%v]", value2, err2) + } + } else { + log.Infof("GetPayoutOrderIdRedisKey error: key[%s] value2 is null", orderId) + } + } else { + log.Infof("GetPayoutOrderIdRedisKey error: key[%s] err[%v]", orderId, err2) + } + + if reportData != nil && reportData.Adjust != nil && reportData.ShuShu != nil { + adjust := GetAdjustData2(reportData.Adjust, req.Msg) + shuShu := GetShuShuData2(reportData.ShuShu, req.Msg) + SendReport(ctx, adjust, shuShu) + } + default: + payoutStatus = uint8(PayoutStatusPayouting) + } + + err = uc.repo.UpdatePayoutRecordNotify(ctx, req.CustomCode, req.PayoutId, payoutStatus, req.Msg) + if err != nil { + return fmt.Errorf("update payout record status error: %v", err) + } + + return nil +} + +// ParseIp 解析IP +func (uc *EonlineUsecase) ParseIp(ctx context.Context, ip string) (*geo.GeoInfo, error) { + return uc.geocli.Parse(ip) +} + +// 通过IP获取对应country code 和 country ip, 如果IP为空,从服务端获取 +func (uc *EonlineUsecase) GetCountryCodeByIp(ctx context.Context, ip string) (string, string) { + var country string + var currentIp string + currentIp = ip + // 获取国家 + // 强制通过服务端获取IP,不支持客户端传递, QA环境仍然支持客户端传递 + if ip == "" || uc.conf.Server.Env != "qa" { + httpCtx := xhttp.RequestFromContext(ctx) + if httpCtx != nil { + // 获取当前的IP地址 + currentIp = utils.GetClientPublicIP(httpCtx.Request()) + } else { + uc.log.WithContext(ctx).Errorf("get http context error") + return "", "" + } + } + + // 解析IP地址 + geonInfo, err := uc.geocli.Parse(currentIp) + if err != nil { + uc.log.WithContext(ctx).Errorf("parse ip(%s) error: %v", currentIp, err) + return currentIp, "" + } + country = geonInfo.Country.ISOCode + fmt.Println("parse ip success: ", currentIp, country) + return country, currentIp +} + +// PayoutCheck 检查提现状态 +func (uc *EonlineUsecase) PayoutCheck(ctx context.Context, recordNo string) (uint8, error) { + record, err := uc.repo.GetPayoutRecordByRecordNo(ctx, recordNo) + if err != nil { + return 0, err + } + + return record.Status, nil +} + +// PayoutCheck 检查提现状态 +func (uc *EonlineUsecase) GetPayoutRecordByAccount(ctx context.Context, account string, status, pageIndex, pageSize int) ([]*PayoutRecord, error) { + record, err := uc.repo.GetPayoutRecordByAccountStatus(ctx, account, int(status), int(pageIndex), int(pageSize)) + if err != nil { + return nil, err + } + + return record, nil +} + +type SubmitCheckReq struct { + Account string + Uuid string +} + +type SubmitCheckReply struct { + Result int32 // 0成功,1失败,2以前提交过,还没审核结果,3以前提交过,并审核通过(以前提交过,但审核失败的,可以继续提交), +} + +const ( + StringNull = "" // 审核无结果 + CheckResultFail = "0" // 审核没通过 + CheckResultSuccess = "1" // 审核通过 +) + +// 提交审核,0成功,1失败,2以前提交过,还没审核结果,3以前提交过,并审核通过(以前提交过,但审核失败的,可以继续提交), +func (uc *EonlineUsecase) SubmitCheck(ctx context.Context, req *SubmitCheckReq) (*SubmitCheckReply, error) { + reply := &SubmitCheckReply{} + + // 查询是否已经提交过 + v, err := uc.getCheckSubmit(ctx, req.Account) + if err != nil { + reply.Result = 1 + return reply, err + } + + if v == 1 { + // 提交过,检查审核结果 + v2, err := uc.getCheckResult(ctx, req.Account) + if err != nil { + reply.Result = 1 + return reply, err + } + // 还没审核结果 + if v2 == 0 { + reply.Result = 2 + return reply, nil + } + // 审核通过的 + if v2 == 2 { + reply.Result = 3 + return reply, nil + } + } + + // 检查提交人数 + // v3, err := uc.getCheckSubmitNum(ctx) + // _ = v3 + + // 提交计数 + err = uc.increaseCheckSubmitNum(ctx) + if err != nil { + reply.Result = 1 + return reply, err + } + + // 建立[uuid, paypal] + err = uc.setUuid2Paypal(ctx, req.Account, req.Uuid) + if err != nil { + reply.Result = 1 + return reply, err + } + + // 不加redis锁,并发写入提交记录,也没什么影响 + // 存入redis + key := GetCheckSubmitRedisKey(req.Account) + uc.cache.WriteValue(ctx, key, req.Uuid, 0) + + return reply, nil +} + +type CheckInfoReq struct { + Uuid string + Ts string +} + +type CheckInfoReply struct { + CanCheckSubmit uint32 + CheckSubmit int32 + CheckResult int32 + CheckPayout int32 + CanCheckPayOut int32 + CheckResultFailedDesc string +} + +func (uc *EonlineUsecase) CheckInfo(ctx context.Context, req *CheckInfoReq) (*CheckInfoReply, error) { + reply := &CheckInfoReply{} + + acc, err := uc.getUuid2Paypal(ctx, req.Uuid) + if err != nil { + return nil, err + } + + if acc == StringNull { + reply.CheckSubmit = 0 + reply.CheckResult = 0 + reply.CheckPayout = 0 + } else { + // 提交身份文件验证情况 + v, err := uc.getCheckSubmit(ctx, acc) + if err != nil { + return nil, err + } + if v == 0 { + reply.CheckSubmit = 0 + } else { + reply.CheckSubmit = 1 + } + // 身份文件审核有反馈的情况 + v2, err := uc.getCheckResult(ctx, acc) + if err != nil { + return nil, err + } + if v2 != 0 { + if v2 == 2 { + reply.CheckResult = 2 + } else { + reply.CheckResult = 1 + + reply.CheckResultFailedDesc, err = uc.getCheckResultFailedDesc(ctx, acc) + if err != nil { + return nil, err + } + } + } + // 提交身份文件奖励5美元领取的情况,0没有提现记录,1提现中,2提现成功,3提现失败 + records, err := uc.repo.GetPayoutRecordListByUuid(ctx, req.Uuid) + if err != nil { + return nil, err + } + for _, record := range records { + if record.ItemId == uint(PayoutItemId4) { + switch record.Status { + case uint8(PayoutStatusPayouting): + reply.CheckPayout = 1 + case uint8(PayoutStatusPayouted): + reply.CheckPayout = 2 + case uint8(PayoutStatusPayoutFailed): + reply.CheckPayout = 3 + } + break + } + } + } + + // 获得当前提交审核计数 + v3, err := uc.getCheckSubmitNum(ctx) + if err != nil { + return nil, err + } + reply.CanCheckSubmit = v3 + log.Infof("CheckInfo: getCheckSubmitNum[%d]", v3) + + if reply.CanCheckSubmit < config.PublicCheckNum { + reply.CanCheckSubmit = 1 + } else { + reply.CanCheckSubmit = 0 + } + + var resultTixian int + reply.CanCheckPayOut = 1 // 身份文件审核过的提现奖励5美元 能否 提现,0能提现,1条件不满足,不能提现,2当天已经提现过,不能再次提现 + if reply.CheckSubmit == 1 && reply.CheckResult == 2 && reply.CheckPayout == 0 { + resultTixian, err = uc.GetCashStatus(ctx, req.Uuid, req.Ts, uint32(PayoutItemId4)) + if err != nil { + return nil, fmt.Errorf("error: %w", err) + } + if resultTixian == 1 { + reply.CanCheckPayOut = 2 + } else { + reply.CanCheckPayOut = 0 + } + } + + return reply, nil +} + +// 获取已经提交身份审核计数 +func (uc *EonlineUsecase) getCheckSubmitNum(ctx context.Context) (uint32, error) { + key := GetCheckSubmitNumRedisKey() + v, err := uc.cache.GetValue(ctx, key) + if err != nil { + return 0, err + } + if v == StringNull { + return 0, nil + } else { + x, err := strconv.Atoi(v) + if err != nil { + return 0, err + } + return uint32(x), nil + } +} + +// 提交身份审核计数 自增 +func (uc *EonlineUsecase) increaseCheckSubmitNum(ctx context.Context) error { + key := GetCheckSubmitNumRedisKey() + v, err := uc.cache.GetValue(ctx, key) + if err != nil { + return err + } + + var x int + if v != StringNull { + x, err = strconv.Atoi(v) + if err != nil { + return err + } + } + + err = uc.cache.WriteValue(ctx, key, x+1, 0) + if err != nil { + return err + } + return nil +} + +// 提交身份文件验证情况,0没有提交,1提交过 +func (uc *EonlineUsecase) getCheckSubmit(ctx context.Context, account string) (int32, error) { + key := GetCheckSubmitRedisKey(account) + v, err := uc.cache.GetValue(ctx, key) + if err != nil { + return 0, err + } + if v == StringNull { + return 0, nil + } else { + return 1, nil + } +} + +// 身份文件审核有反馈的情况,0没有记录,1审核没通过,2审核通过 +func (uc *EonlineUsecase) getCheckResult(ctx context.Context, account string) (int32, error) { + key := GetCheckResultRedisKey(account) + v, err := uc.cache.GetValue(ctx, key) + if err != nil { + return 0, err + } + if v == StringNull { + return 0, nil + } + + if v == CheckResultSuccess { + return 2, nil + } else { + return 1, nil + } +} + +// 提交身份文件奖励5美元领取的情况,0没有领取记录,1有领取记录 +func (uc *EonlineUsecase) getCheckPayout(ctx context.Context, account string) (int32, error) { + return 0, nil +} + +// 存储[uuid, paypal] +func (uc *EonlineUsecase) setUuid2Paypal(ctx context.Context, account, uuid string) error { + key := GetUuid2PaypalRedisKey(uuid) + err := uc.cache.WriteValue(ctx, key, account, 0) + if err != nil { + return err + } + return nil +} + +// 根据uuid获取paypal +func (uc *EonlineUsecase) getUuid2Paypal(ctx context.Context, uuid string) (string, error) { + key := GetUuid2PaypalRedisKey(uuid) + v, err := uc.cache.GetValue(ctx, key) + if err != nil { + return StringNull, err + } + return v, nil +} + +// 查询当天是否有提现记录,0没有,1有 +func (uc *EonlineUsecase) GetCashStatus(ctx context.Context, uuid, ts string, itemId uint32) (int, error) { + keyDay := "" + if uc.conf.Server.Env == "qa" { + ts, _ := strconv.ParseInt(ts, 10, 64) + keyDay = time.Unix(ts, 0).Format("20060102") + } + + uuidRedisKey := GetUuidPayoutRedisKey(uuid, keyDay, itemId) + value, err := uc.cache.GetValue(ctx, uuidRedisKey) + if err != nil { + return 0, err + } + + if value != "" { + return 1, nil + } + return 0, nil +} + +// 获取审核没通过的原因描述信息 +func (uc *EonlineUsecase) getCheckResultFailedDesc(ctx context.Context, account string) (string, error) { + key := GetCheckResultFailRedisKey(account) + v, err := uc.cache.GetValue(ctx, key) + if err != nil { + return "", err + } + return v, nil +} diff --git a/app/eonline/internal/biz/pagsmile.go b/app/eonline/internal/biz/pagsmile.go new file mode 100644 index 0000000..9b05c67 --- /dev/null +++ b/app/eonline/internal/biz/pagsmile.go @@ -0,0 +1,158 @@ +package biz + +import ( + "context" + "encoding/json" + "fmt" + "sandc/pkg/middleware/xhttp" + "sandc/pkg/utils" + "sandc/pkg/xcrypto/pscrypto" + + "github.com/go-kratos/kratos/v2/log" +) + +// PayoutReq 支付请求参数 +type PayoutReq struct { + Name string `json:"name,omitempty"` + Phone string `json:"phone,omitempty"` + Email string `json:"email,omitempty"` + Account string `json:"account,omitempty"` + AccountType string `json:"account_type,omitempty"` + Method string `json:"method,omitempty"` + Channel string `json:"channel,omitempty"` + CustomCode string `json:"custom_code,omitempty"` + FeeBear string `json:"fee_bear,omitempty"` + Amount string `json:"amount,omitempty"` + SourceCurrency string `json:"source_currency,omitempty"` + ArrivalCurrency string `json:"arrival_currency,omitempty"` + NotifyUrl string `json:"notify_url,omitempty"` + AdditionalRemark string `json:"additional_remark,omitempty"` + Country string `json:"country,omitempty"` + Document_id string `json:"document_id,omitempty"` + Document_type string `json:"document_type,omitempty"` + ClientData string `json:"clientData,omitempty"` +} + +// PayoutReply 支付请求响应 +type PayoutReply struct { + Code int `json:"code,omitempty"` + Msg string `json:"msg,omitempty"` + Time int `json:"time,omitempty"` + Data PayoutReplyData `json:"data,omitempty"` +} + +type PayoutReplyData struct { + Id string `json:"id,omitempty"` + CustomCode string `json:"custom_code,omitempty"` + ArrivalAmount string `json:"arrival_amount,omitempty"` + ArrivalCurrency string `json:"arrival_currency,omitempty"` + SourceAmount string `json:"source_amount,omitempty"` + SourceCurrency string `json:"source_currency,omitempty"` + Status string `json:"status,omitempty"` +} + +type PagsmileClient struct { + log *log.Helper + out *PayoutClient +} + +type PayoutClient struct { + AppId string + AppKey string + ApiUrl string + NotifyUrl string +} + +func NewPagsmileClient(out *PayoutClient, logger log.Logger) *PagsmileClient { + return &PagsmileClient{ + out: out, + log: log.NewHelper(logger), + } +} + +// genAuthorization 生成签名授权 +func (uc *PayoutClient) genAuthorization(ctx context.Context, req interface{}) string { + mapJson := utils.StructToMapJson(req) + sign := pscrypto.GetSign(mapJson, uc.AppKey) + return sign +} + +// Payout 打款 +func (c *PayoutClient) Payout(ctx context.Context, req *PayoutReq) (*PayoutReplyData, error) { + req.NotifyUrl = c.NotifyUrl + auth := c.genAuthorization(ctx, *req) + str := fmt.Sprintf("auth: %s", auth) + fmt.Println(str) + log.Info(str) + b, err := json.Marshal(req) + if err != nil { + return nil, fmt.Errorf("Payout marshl json failed: %w", err) + } + // 记录当前payout请求全数据: + str = fmt.Sprintf("payout req: %s - %s\n", req.CustomCode, string(b)) + fmt.Println(str) + log.Info(str) + // bhttp, err := bhttp.NewBhttpClient() + // if err != nil { + // return nil, fmt.Errorf("Payout new bhttp client failed: %w", err) + // } + // bhttp.SetHeader("Authorization", auth) + // bhttp.SetHeader("AppId", c.AppId) + // bhttp.SetBody(b) + // payoutUrl := fmt.Sprintf("%s/api/payout", c.ApiUrl) + // str = fmt.Sprintf("payoutUrl:%s", payoutUrl) + // fmt.Println(str) + // log.Info(str) + // body, err := bhttp.DoPost(payoutUrl) + // if err != nil { + // return nil, fmt.Errorf("Payout do post failed: %w", err) + // } + // str = fmt.Sprintf("[%s] payout body: %s - %s\n", time.Now().Format("2006-01-02 15:04:05"), req.CustomCode, string(body)) + // fmt.Println(str) + // log.Info(str) + + var reply PayoutReply + // err = json.Unmarshal(body, &reply) + // if err != nil { + // return nil, fmt.Errorf("Payout unmarshal failed: %w", err) + // } + // + // if reply.Code != http.StatusOK { + // return nil, fmt.Errorf("Payout error: %s", reply.Msg) + // } + // + // if reply.Data.Status != "IN_PROCESSING" { + // return nil, fmt.Errorf("Payout failed: %s", reply.Data.Status) + // } + reply.Data.Id = "Test" + reply.Data.CustomCode = req.CustomCode + + return &reply.Data, nil +} + +// PayoutNotifyReq 打款回调请求参数 +type PayoutNotifyReq struct { + PayoutId string `json:"payoutId,omitempty"` + CustomCode string `json:"custom_code,omitempty"` + Status string `json:"status,omitempty"` + Msg string `json:"msg,omitempty"` + Timestamp int64 `json:"timestamp,omitempty"` +} + +// PayoutNotify 打款回调 +func (c *PayoutClient) PayoutNotify(ctx context.Context, req *PayoutNotifyReq) error { + // 验证回调签名 + auth := c.genAuthorization(ctx, *req) + fmt.Println("auth: ", auth) + httpContext := xhttp.RequestFromContext(ctx) + if httpContext == nil { + return fmt.Errorf("PayoutNotify http context is nil") + } + authHeader := httpContext.Header().Get("Authorization") + if authHeader != auth { + fmt.Printf("authHeader: %s, auth: %s \n", authHeader, auth) + return fmt.Errorf("PayoutNotify auth failed") + } + + return nil +} diff --git a/app/eonline/internal/biz/payout.go b/app/eonline/internal/biz/payout.go new file mode 100644 index 0000000..1c93624 --- /dev/null +++ b/app/eonline/internal/biz/payout.go @@ -0,0 +1,88 @@ +package biz + +import ( + "time" + + "sandc/pkg/pagination" +) + +// PayoutUser 提现用户信息 +type PayoutUser struct { + Id uint `json:"id"` // 用户ID + Platform string `json:"platform"` // 平台 + Ip string `json:"ip"` // ip地址 + Country string `json:"country"` // 国家 + DeviceId string `json:"device_id"` // 设备ID + Version string `json:"version"` // 版本号 + Uuid string `json:"uuid"` // 用户唯一ID,根据ip, country,device_id生成 + LoginDays uint `json:"login_days"` // 登录天数 + CreatedAt time.Time `json:"created_at"` // 创建时间 + UpdatedAt time.Time `json:"updated_at"` // 更新时间 + ClientData string `json:"clientData"` // 客户端数据 +} + +// 提现记录 +type PayoutRecord struct { + Id uint `json:"id"` // 提现记录ID + PayoutId string `json:"payout_id"` // 支付第三方id + RecordNo string `json:"record_no"` // 提现唯一编码 + Channel string `json:"channel"` // 支付渠道 + Uuid string `json:"uuid"` // 提现用户唯一编码 + Account string `json:"account"` // 提现账号 + ItemId uint `json:"item_id"` // 提现的item对应id + Amount float64 `json:"amount"` // 提现金额 + Currency string `json:"currency"` // 货币单位 + Status uint8 `json:"status"` // 提现状态 1:提现中,2:提现成功,3:提现失败 + Ecpm uint8 `json:"ecpm"` // ecpm等级 + Version string `json:"version"` // 版本号 + CreatedAt time.Time `json:"created_at"` // 创建时间 + UpdatedAt time.Time `json:"updated_at"` // 更新时间 + Fail string `json:"fail"` // 提现失败原因 + Email string `json:"email"` // 邮箱 +} + +// 分红记录 +type BonusRecord struct { + Id uint `json:"id"` // 年月日时间ID + Dau uint `json:"dau"` // 原始dau + Pass uint `json:"pass"` // 通关总份额 + Coin uint `json:"coin"` // 分红每一份额美分数 + Bonus uint `json:"bonus"` // 服务器分红总额美分 + BonusCur uint `json:"bonus_cur"` // 服务器实际分红总额美分 + CreatedAt time.Time `json:"created_at"` // 创建时间 + UpdatedAt time.Time `json:"updated_at"` // 更新时间 +} + +// SearchPayoutUserReq 查询提现用户信息请求 +type SearchPayoutUserReq struct { + Page pagination.Paginator // 分页信息 + DisablePaging bool + SelectFields string + SortBy string + Uuid string + StartTime time.Time + EndTime time.Time +} + +// ListPayoutUser 提现用户信息列表 +type ListPayoutUser struct { + Total int32 + Items []*PayoutUser +} + +// SearchPayoutRecordReq 查询提现记录请求 +type SearchPayoutRecordReq struct { + Page pagination.Paginator // 分页信息 + DisablePaging bool + SelectFields string + SortBy string + Uuid string + StartTime time.Time + EndTime time.Time +} + +// ListPayoutRecord +type ListPayoutRecord struct { + Total int32 + Items []*PayoutRecord +} diff --git a/app/eonline/internal/biz/queue.go b/app/eonline/internal/biz/queue.go new file mode 100644 index 0000000..162b4cf --- /dev/null +++ b/app/eonline/internal/biz/queue.go @@ -0,0 +1,34 @@ +package biz + +import ( + "context" + "fmt" + "github.com/tx7do/kratos-transport/broker" +) + +type ExampleStruct struct { + Name string `json:"name"` + ID int `json:"id"` +} + +func ExampleCreator() broker.Any { return &ExampleStruct{} } + +type ExmapleHandler func(_ context.Context, topic string, headers broker.Headers, msg *ExampleStruct) error + +func RegisterExampleHandler(fnc ExmapleHandler) broker.Handler { + return func(ctx context.Context, event broker.Event) error { + if event.Error() != nil { + return event.Error() + } + msg, ok := event.Message().Body.(*ExampleStruct) + if !ok { + return fmt.Errorf("[Kafka] unsupported type: %T", event.Message().Body) + } + + if err := fnc(ctx, event.Topic(), event.Message().Headers, msg); err != nil { + return err + } + + return nil + } +} diff --git a/app/eonline/internal/biz/report.go b/app/eonline/internal/biz/report.go new file mode 100644 index 0000000..20f7fb5 --- /dev/null +++ b/app/eonline/internal/biz/report.go @@ -0,0 +1,372 @@ +package biz + +import ( + "context" + "fmt" + "strconv" + "strings" + "time" + + v1 "sandc/api/eonline/v1" + "sandc/app/eonline/internal/biz/adjust" + "sandc/app/eonline/internal/biz/shushu" + "sandc/app/eonline/internal/conf" + + "github.com/go-kratos/kratos/v2/log" +) + +var gEnv string +var appConfig *conf.AppConfig + +func InitReport(bootstrap *conf.Bootstrap) { + gEnv = strings.ToLower(bootstrap.Server.Env) + appConfig = bootstrap.AppConfig + + log.Infof("InitReport gEnv[%s] AdjustId[%s] AdjustS2SToken[%s] AdjustEventTokenSuccess[%s] AdjustEventTokenFail[%s] SsAppId[%s]", + gEnv, appConfig.AdjustAppToken, appConfig.AdjustS2SToken, appConfig.AdjustEventTokenSuccess, appConfig.AdjustEventTokenFail, appConfig.SsAppId) + + var err error + + _, err = newAdjustClient() + if err != nil { + log.Fatalf("InitReport: adjust.NewAdjustClient err[%+v]", err) + } + + _, err = newShuShuClient() + if err != nil { + log.Fatalf("InitReport: shushu.NewClient err[%+v]", err) + } +} + +func GetAdjustData(req *v1.PayoutReq, errStr, ip string) *adjust.AdjustEventParams { + if req.DataAdjust == nil { + log.Infof("GetAdjustData error: req.DataAdjust is nil Deviceid[%s]", req.Deviceid) + return nil + } + + data := &adjust.AdjustEventParams{ + GpsAdid: req.DataAdjust.GpsAdid, + Adid: req.DataAdjust.Adid, + AndroidId: req.DataAdjust.AndroidId, + IpAddress: ip, + CreatedAtUnix: strconv.Itoa(int(time.Now().Unix())), + Currency: req.DataAdjust.Currency, + Environment: gEnv, + UserAgent: req.DataAdjust.UserAgent, + Price: req.DataAdjust.Price, + FailReason: errStr, + AppToken: appConfig.AdjustAppToken, + EventToken: appConfig.AdjustEventTokenSuccess, + S2S: "1", + ClientName: req.ClientName, + // AppToken: "5l2aubga4by8", + // EventToken: "xlwfxq", + } + + if len(errStr) > 0 { + // data.EventToken = "t4c6tj" + data.EventToken = appConfig.AdjustEventTokenFail + } + + return data +} + +func GetAdjustData2(req *v1.PbAdjustData, errStr string) *adjust.AdjustEventParams { + if req == nil { + log.Infof("GetAdjustData2 error: req is nil") + return nil + } + + data := &adjust.AdjustEventParams{ + GpsAdid: req.GpsAdid, + Adid: req.Adid, + AndroidId: req.AndroidId, + IpAddress: req.IpAddress, + CreatedAtUnix: req.CreatedAtUnix, + Currency: req.Currency, + Environment: gEnv, + UserAgent: req.UserAgent, + Price: req.Price, + FailReason: errStr, + AppToken: req.AppToken, + EventToken: req.EventToken, + S2S: req.S2S, + ClientName: req.ClientName, + } + if len(errStr) > 0 { + // data.EventToken = "t4c6tj" + data.EventToken = appConfig.AdjustEventTokenFail + } + + return data +} + +func getReportKey(key string) string { + return fmt.Sprintf("report:%s", key) +} + +func AddReportData(key string, adjustEventParams *adjust.AdjustEventParams, iapReport *shushu.SSIapProperties) { + data := &v1.PbReportData{ + Adjust: &v1.PbAdjustData{ + GpsAdid: adjustEventParams.GpsAdid, + Adid: adjustEventParams.Adid, + AndroidId: adjustEventParams.AndroidId, + IpAddress: adjustEventParams.IpAddress, + CreatedAtUnix: adjustEventParams.CreatedAtUnix, + Currency: adjustEventParams.Currency, + Environment: adjustEventParams.Environment, + UserAgent: adjustEventParams.UserAgent, + Price: adjustEventParams.Price, + FailReason: adjustEventParams.FailReason, + AppToken: adjustEventParams.AppToken, + EventToken: adjustEventParams.EventToken, + S2S: adjustEventParams.S2S, + ClientName: adjustEventParams.ClientName, + }, + ShuShu: &v1.PbShuShuData{ + GpsAdid: iapReport.GpsAdid, + AppToken: iapReport.AppToken, + EventToken: iapReport.EventToken, + S2S: iapReport.S2S, + AndroidId: iapReport.AndroidId, + Adid: iapReport.Adid, + IpAddress: iapReport.IpAddress, + CreatedAtUnix: iapReport.CreatedAtUnix, + UserAgent: iapReport.UserAgent, + Price: iapReport.Price, + Currency: iapReport.Currency, + FailReason: iapReport.FailReason, + PayoutId: iapReport.PayoutId, + MerchantReference: iapReport.MerchantReference, + PaymentMethod: iapReport.PaymentMethod, + PaymentType: iapReport.PaymentType, + PaymentNumber: iapReport.PaymentNumber, + IapName: iapReport.IapName, + GamecoinNumber: iapReport.GamecoinNumber, + GamecoinType: iapReport.GamecoinType, + SsAccountId: iapReport.SsAccountId, + SsDistinctId: iapReport.SsDistinctId, + SsSuperProperties: iapReport.SsSuperProperties, + ClientName: iapReport.ClientName, + }, + Rf: uint32(time.Now().Unix()), + } + + // AddReportOne(key, data) + + key = getReportKey(key) + report := NewReportData(db_name, key) + *report.GetData(true) = *data + report.Expire(15 * 3600) // 提现平台最后一次发送回调消息是14小时 + err := report.Save() + if err != nil { + log.Infof("report.Save error: key[%s] error[%+v]", key, err) + } +} + +func GetReportData(key string) *v1.PbReportData { + // return GetReportOne(key) + + key = getReportKey(key) + report := NewReportData(db_name, key) + err := report.Load() + if err != nil { + log.Infof("report.Load error: key[%s] error[%+v]", key, err) + return nil + } + + data := report.GetData(false) + err = report.Delete() + if err != nil { + log.Infof("report.Delete error: key[%s] error[%+v]", key, err) + } + return data +} + +func SendReport(ctx context.Context, adjustEventParams *adjust.AdjustEventParams, iapReport *shushu.SSIapProperties) { + if adjustEventParams == nil || iapReport == nil { + return + } + + err1 := sendAdjustReport(ctx, adjustEventParams) + if err1 != nil { + log.Infof("adjust send err[%+v]", err1) + } + + err2 := sendShuShuReport(iapReport) + if err2 != nil { + log.Infof("shushu send err[%+v]", err2) + } + + if err1 == nil && err2 == nil { + log.Infof("SendReport successed: GpsAdid[%s]", adjustEventParams.GpsAdid) + } +} + +func newAdjustClient() (adjust.AdjustClient, error) { + // return adjust.NewAdjustClient("5l2aubga4by8", "7ea35d86e3e6688c2debcadc4efd7230", gEnv) + return adjust.NewAdjustClient(appConfig.AdjustAppToken, appConfig.AdjustS2SToken, gEnv) +} + +func sendAdjustReport(ctx context.Context, adjustEventParams *adjust.AdjustEventParams) error { + adjustClient, err := newAdjustClient() + if err != nil { + return fmt.Errorf("adjust.NewAdjustClient error: %v", err) + } + // 上报IAP数据 + err = adjustClient.ReportEvent(ctx, adjustEventParams) + if err != nil { + return fmt.Errorf("adjustClient.ReportEvent error: %v", err) + } + return nil +} + +func GetShuShuData(req *v1.PayoutReq, errStr, ip string) *shushu.SSIapProperties { + if req.DataAdjust == nil { + log.Infof("GetAdjustData error: req.DataAdjust is nil Deviceid[%s]", req.Deviceid) + return nil + } + + data := &shushu.SSIapProperties{ + // EventToken: "xlwfxq", + // AppToken: "5l2aubga4by8", + GpsAdid: req.DataAdjust.GpsAdid, + AppToken: appConfig.AdjustAppToken, + EventToken: appConfig.AdjustEventTokenSuccess, + S2S: "1", + AndroidId: req.DataAdjust.AndroidId, + Adid: req.DataAdjust.Adid, + IpAddress: ip, + CreatedAtUnix: strconv.Itoa(int(time.Now().Unix())), + UserAgent: req.DataAdjust.UserAgent, + Price: req.DataAdjust.Price, + Currency: req.DataAdjust.Currency, + FailReason: errStr, + PayoutId: "", + MerchantReference: "", + PaymentMethod: req.DataShuShu.PaymentMethod, + PaymentType: req.DataShuShu.PaymentType, + PaymentNumber: req.DataShuShu.PaymentNumber, + IapName: req.DataShuShu.IapName, + GamecoinNumber: req.DataShuShu.GamecoinNumber, + GamecoinType: req.DataShuShu.GamecoinType, + SsAccountId: req.DataShuShu.SsAccountId, + SsDistinctId: req.DataShuShu.SsDistinctId, + SsSuperProperties: req.DataShuShu.SsSuperProperties, + ClientName: req.ClientName, + } + + if len(errStr) > 0 { + // data.EventToken = "t4c6tj" + data.EventToken = appConfig.AdjustEventTokenFail + } + + return data +} + +func GetShuShuData2(req *v1.PbShuShuData, errStr string) *shushu.SSIapProperties { + if req == nil { + log.Infof("GetAdjustData2 error: req is nil") + return nil + } + + data := &shushu.SSIapProperties{ + GpsAdid: req.GpsAdid, + AppToken: req.AppToken, + EventToken: req.EventToken, + S2S: req.S2S, + AndroidId: req.AndroidId, + Adid: req.Adid, + IpAddress: req.IpAddress, + CreatedAtUnix: req.CreatedAtUnix, + UserAgent: req.UserAgent, + Price: req.Price, + Currency: req.Currency, + FailReason: errStr, + PayoutId: req.PayoutId, + MerchantReference: req.MerchantReference, + PaymentMethod: req.PaymentMethod, + PaymentType: req.PaymentType, + PaymentNumber: req.PaymentNumber, + IapName: req.IapName, + GamecoinNumber: req.GamecoinNumber, + GamecoinType: req.GamecoinType, + SsAccountId: req.SsAccountId, + SsDistinctId: req.SsDistinctId, + SsSuperProperties: req.SsSuperProperties, + ClientName: req.ClientName, + } + + if len(errStr) > 0 { + // data.EventToken = "t4c6tj" + data.EventToken = appConfig.AdjustEventTokenFail + } + + return data +} + +func newShuShuClient() (shushu.Client, error) { + // return shushu.NewClient("https://ss.zolnm.com", "3774fd57014846d99ccd145a76780866", gEnv) + return shushu.NewClient("https://ss.zolnm.com", appConfig.SsAppId, gEnv) +} + +func sendShuShuReport(iapReport *shushu.SSIapProperties) error { + ssClient, err := newShuShuClient() + if err != nil { + return fmt.Errorf("shushu.NewClient error: %v", err) + } + + propertiesMap := shushu.SSProperties{ + "gps_adid": iapReport.GpsAdid, + "app_token": iapReport.AppToken, + "event_token": iapReport.EventToken, + "s2s": iapReport.S2S, + "android_id": iapReport.AndroidId, + "adid": iapReport.Adid, + "ip_address": iapReport.IpAddress, + "created_at_unix": iapReport.CreatedAtUnix, + "user_agent": iapReport.UserAgent, + "price": iapReport.Price, + "currency": iapReport.Currency, + "fail_reason": iapReport.FailReason, + "payout_id": iapReport.PayoutId, + "merchant_reference": iapReport.MerchantReference, + "payment_method": iapReport.PaymentMethod, + "payment_type": iapReport.PaymentType, + "payment_number": iapReport.PaymentNumber, + "iap_name": iapReport.IapName, + "gamecoin_number": iapReport.GamecoinNumber, + "gamecoin_type": iapReport.GamecoinType, + "ss_account_id": iapReport.SsAccountId, + "ss_distinct_id": iapReport.SsDistinctId, + "ss_super_properties": iapReport.SsSuperProperties, + "client_name": iapReport.ClientName, + } + + // 上报IAP数据 + if len(iapReport.FailReason) <= 0 { + eventName := "cash_out" + if gEnv == "qa" { + eventName = fmt.Sprintf("%s_qa", eventName) + } + err = ssClient.SyncIapData(iapReport.SsAccountId, iapReport.SsDistinctId, eventName, iapReport.IpAddress, propertiesMap) + if err != nil { + return fmt.Errorf("ssClient.SyncIapData error: %v", err) + } + } else { + eventName := "cash_fail" + if gEnv == "qa" { + eventName = fmt.Sprintf("%s_qa", eventName) + } + err = ssClient.ReportError(iapReport.SsAccountId, iapReport.SsDistinctId, eventName, iapReport.IpAddress, propertiesMap) + if err != nil { + return fmt.Errorf("ssClient.ReportError error: %v", err) + } + } + return nil +} + +func CloseReport() { + log.Infof("CloseReport gEnv[%s] AdjustId[%s] AdjustS2SToken[%s] AdjustEventTokenSuccess[%s] AdjustEventTokenFail[%s] SsAppId[%s]", + gEnv, appConfig.AdjustAppToken, appConfig.AdjustS2SToken, appConfig.AdjustEventTokenSuccess, appConfig.AdjustEventTokenFail, appConfig.SsAppId) +} diff --git a/app/eonline/internal/biz/serverData.go b/app/eonline/internal/biz/serverData.go new file mode 100644 index 0000000..069b23d --- /dev/null +++ b/app/eonline/internal/biz/serverData.go @@ -0,0 +1,240 @@ +package biz + +import ( + "errors" + "fmt" + "sync" + "time" + + go_redis_orm "github.com/fananchong/go-redis-orm.v2" + "github.com/go-kratos/kratos/v2/log" + v1 "sandc/api/eonline/v1" + "sandc/app/eonline/internal/conf" +) + +var ( + G_SvrData *SvrData + MutexSvrData sync.RWMutex + ERR_INIT_USER_DATA = errors.New("init db user data error") +) + +const ( + db_name = "my_redis_db" + NS2MS int64 = 1_000_000 // 纳秒转换成毫秒,需要的被除数 +) + +func InitSvrData(bootstrap *conf.Bootstrap) { + go_redis_orm.SetNewRedisHandler(go_redis_orm.NewDefaultRedisClient) + err := go_redis_orm.CreateDB(db_name, []string{bootstrap.Data.Redis.Addr}, bootstrap.Data.Redis.Password, int(bootstrap.Data.Redis.Db)) + if nil != err { + log.Fatalf("go_redis_orm.CreateDB err: dbName[%v] Idx[%v] Redis[%v] err[%v]", db_name, bootstrap.Data.Redis.Db, bootstrap.Data.Redis.Addr, err) + } + + err = loadSvrData(uint32(bootstrap.Server.SvrId)) + if err != nil { + log.Fatal("loadSvrData error: %v", err) + } else { + log.Infof("loadSvrData success") + } +} + +func SaveSvrDataOnTimer() { + begin := time.Now().UnixNano() + + var save map[string]interface{} + getSaveDataSvrData(&save) + + var err error + go func() { + begin := time.Now().UnixNano() + + err = saveDataSvrData(&save) + if err != nil { + log.Infof("saveDataSvrData error: %v", err) + } + save = nil + + end := time.Now().UnixNano() + delta := end - begin + log.Infof("saveDataSvrData: begin[%dns] end[%dns], spend[%dms %dns]", begin, end, delta/NS2MS, delta%NS2MS) + }() + + end := time.Now().UnixNano() + delta := end - begin + log.Infof("SaveSvrDataOnTimer: begin[%dns] end[%dns], spend[%dms %dns]", begin, end, delta/NS2MS, delta%NS2MS) +} + +func SaveSvrDataShutDown() { + begin := time.Now().UnixNano() + + err := saveDataSvrData2() + if err != nil { + log.Infof("saveSvrDataShutDown error: %v", err) + } + + end := time.Now().UnixNano() + delta := end - begin + log.Infof("SaveSvrDataShutDown: begin[%dns] end[%dns], spend[%dms %dns]", begin, end, delta/NS2MS, delta%NS2MS) +} + +func loadSvrData(key uint32) error { + MutexSvrData.Lock() + defer MutexSvrData.Unlock() + + G_SvrData = NewSvrData(db_name, key) + + if G_SvrData.IsLoad() { + log.Infof("神奇的已加载数据 SvrData") + return nil + } + + var hasKey int + var err error + + hasKey, err = G_SvrData.HasKey() + switch hasKey { + case 1: + err = G_SvrData.Load() + if err == nil { + initValueSvrDataOnLoad(key) + } + case 0: + // new + err := initValueSvrData(key) + if err != nil { + log.Error("initValueSvrData %v data error: %v", key, err) + return ERR_INIT_USER_DATA + } + case -1: + return err + default: + log.Error("loadSvrData HasKey key[%v] hasKey[%d] error: %v", key, hasKey, err) + return errors.New(fmt.Sprintf("error haskey[%d]", hasKey)) + } + + return nil +} + +func initValueSvrData(key uint32) error { + initValueSvrDataOnLoad(key) + + return nil +} +func initValueSvrDataOnLoad(key uint32) { + bChange := false + data := G_SvrData.GetData(false) + if data.LstChat == nil { + bChange = true + data.LstChat = []*v1.PbMsgOne{} + } + // if data.MapReportData == nil { + // bChange = true + // data.MapReportData = map[string]*v1.PbReportData{} + // } + + if bChange { + G_SvrData.GetData(true) + } + +} + +func getSaveDataSvrData(temp *map[string]interface{}) error { + MutexSvrData.Lock() + defer MutexSvrData.Unlock() + + var err error + *temp, err = G_SvrData.DirtyData() + return err +} + +func saveDataSvrData(temp *map[string]interface{}) error { + MutexSvrData.RLock() + defer MutexSvrData.RUnlock() + + var err error + err = G_SvrData.Save2(*temp) + return err +} + +func saveDataSvrData2() error { + MutexSvrData.Lock() + defer MutexSvrData.Unlock() + + var err error + err = G_SvrData.Save() + return err +} + +func AddChat(req *v1.AddChatReq) { + MutexSvrData.Lock() + defer MutexSvrData.Unlock() + + lst := &G_SvrData.GetData(true).LstChat + *lst = append(*lst, &v1.PbMsgOne{ + TimeStamp: time.Now().UnixNano(), + Uuid: req.Uuid, + Name: "", + Msg: req.Msg, + }) + + lstNum := len(*lst) + if lstNum > 100 { + *lst = (*lst)[lstNum-100:] + } +} + +func GetChat(timeStamp int64) []*v1.PbMsgOne { + MutexSvrData.RLock() + defer MutexSvrData.RUnlock() + + lst := G_SvrData.GetData(false).LstChat + + if timeStamp <= 0 { + return lst + } + + for k, v := range lst { + if v.TimeStamp >= timeStamp { + return lst[k:] + } + } + + return lst +} + +func AddReportOne(key string, data *v1.PbReportData) { + // MutexSvrData.Lock() + // defer MutexSvrData.Unlock() + // + // now := uint32(time.Now().Unix()) + // + // svrData := G_SvrData.GetData(true) + // lst := &svrData.MapReportData + // + // // 清理超时的 + // if svrData.RfClearReportData+30*60 <= now { + // svrData.RfClearReportData = now + // + // for k, v := range *lst { + // if v.Rf+15*60*60 <= now { // 提现平台最后一次发送回调消息是14小时 + // delete(*lst, k) + // } + // } + // } + // + // (*lst)[key] = data +} + +func GetReportOne(key string) *v1.PbReportData { + // MutexSvrData.Lock() + // defer MutexSvrData.Unlock() + // + // lst := &G_SvrData.GetData(false).MapReportData + // + // if v, ok := (*lst)[key]; ok { + // delete(*lst, key) + // return v + // } + // + return nil +} diff --git a/app/eonline/internal/biz/shushu/client.go b/app/eonline/internal/biz/shushu/client.go new file mode 100644 index 0000000..b920b53 --- /dev/null +++ b/app/eonline/internal/biz/shushu/client.go @@ -0,0 +1,240 @@ +package shushu + +import ( + "encoding/json" + "fmt" + "time" + + "sandc/pkg/bhttp" + "sandc/pkg/utils" + + "github.com/go-kratos/kratos/v2/log" +) + +// 定义数数client的接口 +type Client interface { + SyncIapData(accountId, distinctId, eventName string, ip string, properties SSProperties) error + ReportError(accountId, distinctId, eventName string, ip string, properties SSProperties) error +} + +// 定义数数client的实例 +type client struct { + serverUrl string + appId string + env string + service *bhttp.BhttpService +} + +// SyncData 定义数数上传结构体 +type SyncData struct { + Appid string `json:"appid,omitempty"` + Debug int `json:"debug,omitempty"` + Data Data `json:"data,omitempty"` +} + +type Data struct { + AccountId string `json:"#account_id,omitempty"` + DistinctId string `json:"#distinct_id,omitempty"` + Type string `json:"#type,omitempty"` + Ip string `json:"#ip,omitempty"` + Uuid string `json:"#uuid,omitempty"` + Time string `json:"#time,omitempty"` + EventName string `json:"#event_name,omitempty"` + Properties interface{} `json:"properties,omitempty"` +} + +// SSIapProperties 定义数数上传Properties结构体 +type SSIapProperties struct { + // Add your properties here + GpsAdid string `json:"gps_adid"` // 用户的gaid + AppToken string `json:"app_token"` // 控制面板上的 Adjust 应用识别码 + EventToken string `json:"event_token"` // 控制面板上的 Adjust 事件识别码 + S2S string `json:"s2s"` // s2s 参数设置为 1 + AndroidId string `json:"android_id"` // 原始安卓 ID + Adid string `json:"adid"` // 与设备关联的 Adjust 标识符 + IpAddress string `json:"ip_address"` // 设备 IP 地址。用于将事件关联至第三方 (例如 Google) 并在回传中包含位置相关信息 (例如city 、 postal_code )。 ip_address参数仅接受 IPv4 地址。当前不支持 IPv6。 + CreatedAtUnix string `json:"created_at_unix"` // 事件发生的日期和时间。 + UserAgent string `json:"user_agent"` // 设备的User-Agent。必须进行 URL 编码。 + Price string `json:"price"` // 客户端上报的价格 + Currency string `json:"currency"` // 货币单位 + FailReason string `json:"fail_reason"` // 失败原因 + PayoutId string `json:"payout_id"` // 提现订单号1,实例:TS202504150316045rg110SPMDjPB + MerchantReference string `json:"merchant_reference"` // 提现订单号2,实例:PGs bfd267c7823a80d97519197a30bfdf28 + PaymentMethod string `json:"payment_method"` // 收款方式 + PaymentType string `json:"payment_type"` // 账户形式 + PaymentNumber string `json:"payment_number"` // 账户号码 + IapName string `json:"iap_name"` // 商品名称 + GamecoinNumber string `json:"gamecoin_number"` // 提现消耗的虚拟货币数 + GamecoinType string `json:"gamecoin_type"` // 提现消耗的虚拟货币类型 + SsAccountId string `json:"ss_account_id"` // 数数账号ID + SsDistinctId string `json:"ss_distinct_id"` // 数数访客ID + SsSuperProperties string `json:"ss_super_properties"` // 数数的公共属性和预制属性 + ClientName string `json:"client_name"` // 客户端包名 + // Value float64 `json:"value"` + // Platform string `json:"platform"` + // PlatformChannel string `json:"platform_channel"` + // PlatformOs string `json:"platform_os"` + // ProductId string `json:"product_id"` + // OrderId string `json:"order_id"` + // Currency string `json:"currency"` + // Environment string `json:"environment"` + // AdNetwork string `json:"ad_network"` + // Campaign string `json:"campaign"` + // Adgroup string `json:"adgroup"` + // Creative string `json:"creative"` +} + +// SSErrorProperties 定义数数上报的错误信息 +type SSErrorProperties struct { + Value float64 `json:"value"` + Platform string `json:"platform"` + ProductId string `json:"product_id"` + OrderId string `json:"order_id"` + Currency string `json:"currency"` + Environment string `json:"environment"` + Msg string `json:"msg"` + AdNetwork string `json:"ad_network"` + Campaign string `json:"campaign"` + Adgroup string `json:"adgroup"` + Creative string `json:"creative"` + PlatformChannel string `json:"platform_channel"` + PlatformOs string `json:"platform_os"` +} + +type SSProperties map[string]interface{} + +// SyncRes 定义数数上传返回结构体 +type SyncRes struct { + Code int `json:"code"` + Message string `json:"msg"` +} + +func NewClient(serverUrl string, appId string, env string) (Client, error) { + service, err := bhttp.NewBhttpService(bhttp.WithCheckStatusOk(true), bhttp.WithTimeout(30)) + if err != nil { + return nil, err + } + service.Client.SetHeader("Content-Type", "application/json") + + return &client{ + appId: appId, + serverUrl: serverUrl, + env: env, + service: service, + }, nil +} + +func (c *client) syncJson(accountId, distinctId, eventName string, ip string, properties interface{}) error { + debug := 0 + if !c.IsProd() { + debug = 1 + } + + if accountId == "" && distinctId == "" { + return fmt.Errorf("accountId or distinctId is empty") + } + + // 根据properties生成唯一的uuid + propertiesStr := utils.MD5Any(properties) + uuid := utils.GenerateUUID(propertiesStr) + + syncData := &SyncData{ + Appid: c.appId, + Debug: debug, + Data: Data{ + AccountId: accountId, + DistinctId: distinctId, + Type: "track", + Ip: ip, + Uuid: uuid, + Time: time.Now().Format("2006-01-02 15:04:05.000"), + EventName: eventName, + Properties: properties, + }, + } + + postBody, err := json.Marshal(syncData) + if err != nil { + return fmt.Errorf("json.Marshal SyncIapData error: %v", err) + } + c.service.Client.SetBody(postBody) + url := fmt.Sprintf("%s/sync_json", c.serverUrl) + // utils.PrintLog("SyncIapData-url: %s", url) + // utils.PrintLog("SyncIapData-postbody: %s", postBody) + log.Infof("SyncIapData-url: %s", url) + log.Infof("SyncIapData-postbody: %s", postBody) + + sendData := postBody + + numMax := 1 + for i := 0; i < numMax; i++ { + body, err := c.service.Client.DoPost(url) + if err != nil { + return fmt.Errorf("shushu server err: %s, reader: %s", err, sendData) + } + + var res SyncRes + err = json.Unmarshal(body, &res) + if err != nil { + return fmt.Errorf("shushu Unmarshal err: %s, reader: %s", res.Message, sendData) + } + + if res.Code == 0 { + log.Infof("shushu send successed: i[%d] reader: %s", i, sendData) + break + } else { + log.Infof("shushu res error: %s, i[%d] reader: %s", res.Message, i, sendData) + + if i+1 >= numMax { + return fmt.Errorf("shushu res err: %s, reader: %s", res.Message, sendData) + } + } + } + + return nil +} + +func setSsSuperPropertiesMap(propertiesMap SSProperties) { + v, ok := propertiesMap["ss_super_properties"] + if !ok || v == nil { + log.Infof("setSsSuperPropertiesMap error: ss_super_properties not found in propertiesMap[%v]", propertiesMap) + return + } + + ssSuperProperties, ok := v.(string) + if !ok { + log.Infof("setSsSuperPropertiesMap error: ss_super_properties not string[%v]", propertiesMap) + return + } + // 解析 ss_super_properties + var ssSuperPropertiesMap SSProperties + if ssSuperProperties != "" { + err := json.Unmarshal([]byte(ssSuperProperties), &ssSuperPropertiesMap) + if err != nil { + log.Infof("setSsSuperPropertiesMap error: unmarshal err[%v], ssSuperProperties[%s]", err, ssSuperProperties) + return + } + // 插入解析出来的新字段 + for key, value := range ssSuperPropertiesMap { + if _, ok := propertiesMap[key]; !ok && key != "#zone_offset" { + propertiesMap[key] = value + } + } + } else { + log.Infof("setSsSuperPropertiesMap error: ss_super_properties is empty[%v]", propertiesMap) + } +} + +func (c *client) ReportError(accountId, distinctId, eventName string, ip string, properties SSProperties) error { + setSsSuperPropertiesMap(properties) + return c.syncJson(accountId, distinctId, eventName, ip, properties) +} + +func (c *client) SyncIapData(accountId, distinctId, eventName string, ip string, properties SSProperties) error { + setSsSuperPropertiesMap(properties) + return c.syncJson(accountId, distinctId, eventName, ip, properties) +} + +func (c *client) IsProd() bool { + return c.env == "prod" +} diff --git a/app/eonline/internal/biz/svrData.go b/app/eonline/internal/biz/svrData.go new file mode 100644 index 0000000..ad92451 --- /dev/null +++ b/app/eonline/internal/biz/svrData.go @@ -0,0 +1,164 @@ +package biz + +import ( + "errors" + "fmt" + + v1 "sandc/api/eonline/v1" + + go_redis_orm "github.com/fananchong/go-redis-orm.v2" + "github.com/gomodule/redigo/redis" + "google.golang.org/protobuf/proto" +) + +type SvrData struct { + __key uint32 + data v1.PbSvrData + + __dirtyData map[string]interface{} + __dirtyDataForStructFiled map[string]interface{} + __isLoad bool + __dbKey string + __dbName string + __expire uint +} + +func NewSvrData(dbName string, key uint32) *SvrData { + return &SvrData{ + __key: key, + __dbName: dbName, + __dbKey: "SvrData:" + fmt.Sprintf("%d", key), + __dirtyData: make(map[string]interface{}), + __dirtyDataForStructFiled: make(map[string]interface{}), + } +} + +// 若访问数据库失败返回-1;若 key 存在返回 1 ,否则返回 0 。 +func (this *SvrData) HasKey() (int, error) { + db := go_redis_orm.GetDB(this.__dbName) + val, err := redis.Int(db.Do("EXISTS", this.__dbKey)) + if err != nil { + return -1, err + } + return val, nil +} + +func (this *SvrData) Load() error { + if this.__isLoad == true { + return errors.New("already load!") + } + db := go_redis_orm.GetDB(this.__dbName) + val, err := redis.Values(db.Do("HGETALL", this.__dbKey)) + if err != nil { + return err + } + if len(val) == 0 { + return go_redis_orm.ERR_ISNOT_EXIST_KEY + } + var data struct { + Data []byte `redis:"data"` + } + if err := redis.ScanStruct(val, &data); err != nil { + return err + } + if err := proto.Unmarshal(data.Data, &this.data); err != nil { + return err + } + + this.__isLoad = true + return nil +} + +func (this *SvrData) Save() error { + if len(this.__dirtyData) == 0 && len(this.__dirtyDataForStructFiled) == 0 { + return nil + } + for k, _ := range this.__dirtyDataForStructFiled { + _ = k + if k == "data" { + data, err := proto.Marshal(&this.data) + if err != nil { + return err + } + this.__dirtyData["data"] = data + } + } + db := go_redis_orm.GetDB(this.__dbName) + if _, err := db.Do("HMSET", redis.Args{}.Add(this.__dbKey).AddFlat(this.__dirtyData)...); err != nil { + return err + } + if this.__expire != 0 { + if _, err := db.Do("EXPIRE", this.__dbKey, this.__expire); err != nil { + return err + } + } + this.__dirtyData = make(map[string]interface{}) + this.__dirtyDataForStructFiled = make(map[string]interface{}) + return nil +} + +func (this *SvrData) Delete() error { + db := go_redis_orm.GetDB(this.__dbName) + _, err := db.Do("DEL", this.__dbKey) + if err == nil { + this.__isLoad = false + this.__dirtyData = make(map[string]interface{}) + this.__dirtyDataForStructFiled = make(map[string]interface{}) + } + return err +} + +func (this *SvrData) IsLoad() bool { + return this.__isLoad +} + +func (this *SvrData) Expire(v uint) { + this.__expire = v +} + +func (this *SvrData) GetKey() uint32 { + return this.__key +} + +func (this *SvrData) DirtyData() (map[string]interface{}, error) { + for k, _ := range this.__dirtyDataForStructFiled { + _ = k + if k == "data" { + data, err := proto.Marshal(&this.data) + if err != nil { + return nil, err + } + this.__dirtyData["data"] = data + } + } + data := make(map[string]interface{}) + for k, v := range this.__dirtyData { + data[k] = v + } + this.__dirtyData = make(map[string]interface{}) + this.__dirtyDataForStructFiled = make(map[string]interface{}) + return data, nil +} + +func (this *SvrData) Save2(dirtyData map[string]interface{}) error { + if len(dirtyData) == 0 { + return nil + } + db := go_redis_orm.GetDB(this.__dbName) + if _, err := db.Do("HMSET", redis.Args{}.Add(this.__dbKey).AddFlat(dirtyData)...); err != nil { + return err + } + if this.__expire != 0 { + if _, err := db.Do("EXPIRE", this.__dbKey, this.__expire); err != nil { + return err + } + } + return nil +} + +func (this *SvrData) GetData(mutable bool) *v1.PbSvrData { + if mutable { + this.__dirtyDataForStructFiled["data"] = nil + } + return &this.data +} diff --git a/app/eonline/internal/biz/user.go b/app/eonline/internal/biz/user.go new file mode 100644 index 0000000..8400d58 --- /dev/null +++ b/app/eonline/internal/biz/user.go @@ -0,0 +1,26 @@ +package biz + +import ( + "github.com/go-kratos/kratos/v2/log" +) + +func getUserData(uuid string) *UserData { + data := NewUserData(db_name, uuid) + err := data.Load() + if err != nil { + log.Infof("getUserData error: uuid[%s] error[%v]", uuid, err) + return nil + } + + return data +} + +func saveUserData(data *UserData) bool { + err := data.Save() + if err != nil { + log.Infof("getUserData error: uuid[%s] error[%v]", data.GetKey(), err) + return false + } + + return true +} diff --git a/app/eonline/internal/conf/conf.pb.go b/app/eonline/internal/conf/conf.pb.go new file mode 100644 index 0000000..98aa5cd --- /dev/null +++ b/app/eonline/internal/conf/conf.pb.go @@ -0,0 +1,1527 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v3.20.3 +// source: conf.proto + +package conf + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + durationpb "google.golang.org/protobuf/types/known/durationpb" + 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 Bootstrap struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Server *Server `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"` + Data *Data `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + Queue *Queue `protobuf:"bytes,3,opt,name=queue,proto3" json:"queue,omitempty"` + Pagsmile *Pagsmile `protobuf:"bytes,4,opt,name=pagsmile,proto3" json:"pagsmile,omitempty"` + ConfigFiles *ConfigFiles `protobuf:"bytes,5,opt,name=configFiles,proto3" json:"configFiles,omitempty"` + AppConfig *AppConfig `protobuf:"bytes,7,opt,name=appConfig,proto3" json:"appConfig,omitempty"` +} + +func (x *Bootstrap) Reset() { + *x = Bootstrap{} + if protoimpl.UnsafeEnabled { + mi := &file_conf_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Bootstrap) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Bootstrap) ProtoMessage() {} + +func (x *Bootstrap) ProtoReflect() protoreflect.Message { + mi := &file_conf_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 Bootstrap.ProtoReflect.Descriptor instead. +func (*Bootstrap) Descriptor() ([]byte, []int) { + return file_conf_proto_rawDescGZIP(), []int{0} +} + +func (x *Bootstrap) GetServer() *Server { + if x != nil { + return x.Server + } + return nil +} + +func (x *Bootstrap) GetData() *Data { + if x != nil { + return x.Data + } + return nil +} + +func (x *Bootstrap) GetQueue() *Queue { + if x != nil { + return x.Queue + } + return nil +} + +func (x *Bootstrap) GetPagsmile() *Pagsmile { + if x != nil { + return x.Pagsmile + } + return nil +} + +func (x *Bootstrap) GetConfigFiles() *ConfigFiles { + if x != nil { + return x.ConfigFiles + } + return nil +} + +func (x *Bootstrap) GetAppConfig() *AppConfig { + if x != nil { + return x.AppConfig + } + return nil +} + +type Server struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Http *Server_HTTP `protobuf:"bytes,1,opt,name=http,proto3" json:"http,omitempty"` + Grpc *Server_GRPC `protobuf:"bytes,2,opt,name=grpc,proto3" json:"grpc,omitempty"` + Etcd *Server_ETCD `protobuf:"bytes,3,opt,name=etcd,proto3" json:"etcd,omitempty"` + TraceEndpoint string `protobuf:"bytes,4,opt,name=trace_endpoint,json=traceEndpoint,proto3" json:"trace_endpoint,omitempty"` + Env string `protobuf:"bytes,6,opt,name=env,proto3" json:"env,omitempty"` + GeoFile string `protobuf:"bytes,7,opt,name=geo_file,json=geoFile,proto3" json:"geo_file,omitempty"` + SvrId int32 `protobuf:"varint,8,opt,name=svr_id,json=svrId,proto3" json:"svr_id,omitempty"` // 当前服务器id + FirstDay int32 `protobuf:"varint,9,opt,name=first_day,json=firstDay,proto3" json:"first_day,omitempty"` // 开服首日,格式类似 20230906,表示2023.9.6 + VerCheck string `protobuf:"bytes,10,opt,name=ver_check,json=verCheck,proto3" json:"ver_check,omitempty"` // 版本号检查,最低版本号 + TimeoutTimerPer10Second int32 `protobuf:"varint,11,opt,name=timeoutTimerPer10Second,proto3" json:"timeoutTimerPer10Second,omitempty"` // 10秒timer执行超时时间纳秒 +} + +func (x *Server) Reset() { + *x = Server{} + if protoimpl.UnsafeEnabled { + mi := &file_conf_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Server) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Server) ProtoMessage() {} + +func (x *Server) ProtoReflect() protoreflect.Message { + mi := &file_conf_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 Server.ProtoReflect.Descriptor instead. +func (*Server) Descriptor() ([]byte, []int) { + return file_conf_proto_rawDescGZIP(), []int{1} +} + +func (x *Server) GetHttp() *Server_HTTP { + if x != nil { + return x.Http + } + return nil +} + +func (x *Server) GetGrpc() *Server_GRPC { + if x != nil { + return x.Grpc + } + return nil +} + +func (x *Server) GetEtcd() *Server_ETCD { + if x != nil { + return x.Etcd + } + return nil +} + +func (x *Server) GetTraceEndpoint() string { + if x != nil { + return x.TraceEndpoint + } + return "" +} + +func (x *Server) GetEnv() string { + if x != nil { + return x.Env + } + return "" +} + +func (x *Server) GetGeoFile() string { + if x != nil { + return x.GeoFile + } + return "" +} + +func (x *Server) GetSvrId() int32 { + if x != nil { + return x.SvrId + } + return 0 +} + +func (x *Server) GetFirstDay() int32 { + if x != nil { + return x.FirstDay + } + return 0 +} + +func (x *Server) GetVerCheck() string { + if x != nil { + return x.VerCheck + } + return "" +} + +func (x *Server) GetTimeoutTimerPer10Second() int32 { + if x != nil { + return x.TimeoutTimerPer10Second + } + return 0 +} + +type Data struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Database *Data_Database `protobuf:"bytes,1,opt,name=database,proto3" json:"database,omitempty"` + Redis *Data_Redis `protobuf:"bytes,2,opt,name=redis,proto3" json:"redis,omitempty"` +} + +func (x *Data) Reset() { + *x = Data{} + if protoimpl.UnsafeEnabled { + mi := &file_conf_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Data) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Data) ProtoMessage() {} + +func (x *Data) ProtoReflect() protoreflect.Message { + mi := &file_conf_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 Data.ProtoReflect.Descriptor instead. +func (*Data) Descriptor() ([]byte, []int) { + return file_conf_proto_rawDescGZIP(), []int{2} +} + +func (x *Data) GetDatabase() *Data_Database { + if x != nil { + return x.Database + } + return nil +} + +func (x *Data) GetRedis() *Data_Redis { + if x != nil { + return x.Redis + } + return nil +} + +type Queue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Kafka *Queue_Kafka `protobuf:"bytes,1,opt,name=kafka,proto3" json:"kafka,omitempty"` + Asynq *Queue_Asynq `protobuf:"bytes,2,opt,name=asynq,proto3" json:"asynq,omitempty"` +} + +func (x *Queue) Reset() { + *x = Queue{} + if protoimpl.UnsafeEnabled { + mi := &file_conf_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Queue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Queue) ProtoMessage() {} + +func (x *Queue) ProtoReflect() protoreflect.Message { + mi := &file_conf_proto_msgTypes[3] + 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 Queue.ProtoReflect.Descriptor instead. +func (*Queue) Descriptor() ([]byte, []int) { + return file_conf_proto_rawDescGZIP(), []int{3} +} + +func (x *Queue) GetKafka() *Queue_Kafka { + if x != nil { + return x.Kafka + } + return nil +} + +func (x *Queue) GetAsynq() *Queue_Asynq { + if x != nil { + return x.Asynq + } + return nil +} + +type Pagsmile struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Payout *Pagsmile_Payout `protobuf:"bytes,1,opt,name=payout,proto3" json:"payout,omitempty"` +} + +func (x *Pagsmile) Reset() { + *x = Pagsmile{} + if protoimpl.UnsafeEnabled { + mi := &file_conf_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Pagsmile) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Pagsmile) ProtoMessage() {} + +func (x *Pagsmile) ProtoReflect() protoreflect.Message { + mi := &file_conf_proto_msgTypes[4] + 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 Pagsmile.ProtoReflect.Descriptor instead. +func (*Pagsmile) Descriptor() ([]byte, []int) { + return file_conf_proto_rawDescGZIP(), []int{4} +} + +func (x *Pagsmile) GetPayout() *Pagsmile_Payout { + if x != nil { + return x.Payout + } + return nil +} + +type ConfigFiles struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` +} + +func (x *ConfigFiles) Reset() { + *x = ConfigFiles{} + if protoimpl.UnsafeEnabled { + mi := &file_conf_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConfigFiles) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConfigFiles) ProtoMessage() {} + +func (x *ConfigFiles) ProtoReflect() protoreflect.Message { + mi := &file_conf_proto_msgTypes[5] + 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 ConfigFiles.ProtoReflect.Descriptor instead. +func (*ConfigFiles) Descriptor() ([]byte, []int) { + return file_conf_proto_rawDescGZIP(), []int{5} +} + +func (x *ConfigFiles) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +type AppConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AdjustAppToken string `protobuf:"bytes,1,opt,name=adjustAppToken,proto3" json:"adjustAppToken,omitempty"` + AdjustS2SToken string `protobuf:"bytes,2,opt,name=adjustS2SToken,proto3" json:"adjustS2SToken,omitempty"` + AdjustEventTokenSuccess string `protobuf:"bytes,3,opt,name=adjustEventTokenSuccess,proto3" json:"adjustEventTokenSuccess,omitempty"` + AdjustEventTokenFail string `protobuf:"bytes,4,opt,name=adjustEventTokenFail,proto3" json:"adjustEventTokenFail,omitempty"` + SsAppId string `protobuf:"bytes,5,opt,name=ssAppId,proto3" json:"ssAppId,omitempty"` +} + +func (x *AppConfig) Reset() { + *x = AppConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_conf_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AppConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AppConfig) ProtoMessage() {} + +func (x *AppConfig) ProtoReflect() protoreflect.Message { + mi := &file_conf_proto_msgTypes[6] + 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 AppConfig.ProtoReflect.Descriptor instead. +func (*AppConfig) Descriptor() ([]byte, []int) { + return file_conf_proto_rawDescGZIP(), []int{6} +} + +func (x *AppConfig) GetAdjustAppToken() string { + if x != nil { + return x.AdjustAppToken + } + return "" +} + +func (x *AppConfig) GetAdjustS2SToken() string { + if x != nil { + return x.AdjustS2SToken + } + return "" +} + +func (x *AppConfig) GetAdjustEventTokenSuccess() string { + if x != nil { + return x.AdjustEventTokenSuccess + } + return "" +} + +func (x *AppConfig) GetAdjustEventTokenFail() string { + if x != nil { + return x.AdjustEventTokenFail + } + return "" +} + +func (x *AppConfig) GetSsAppId() string { + if x != nil { + return x.SsAppId + } + return "" +} + +type Server_HTTP struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Network string `protobuf:"bytes,1,opt,name=network,proto3" json:"network,omitempty"` + Addr string `protobuf:"bytes,2,opt,name=addr,proto3" json:"addr,omitempty"` + Timeout *durationpb.Duration `protobuf:"bytes,3,opt,name=timeout,proto3" json:"timeout,omitempty"` +} + +func (x *Server_HTTP) Reset() { + *x = Server_HTTP{} + if protoimpl.UnsafeEnabled { + mi := &file_conf_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Server_HTTP) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Server_HTTP) ProtoMessage() {} + +func (x *Server_HTTP) ProtoReflect() protoreflect.Message { + mi := &file_conf_proto_msgTypes[7] + 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 Server_HTTP.ProtoReflect.Descriptor instead. +func (*Server_HTTP) Descriptor() ([]byte, []int) { + return file_conf_proto_rawDescGZIP(), []int{1, 0} +} + +func (x *Server_HTTP) GetNetwork() string { + if x != nil { + return x.Network + } + return "" +} + +func (x *Server_HTTP) GetAddr() string { + if x != nil { + return x.Addr + } + return "" +} + +func (x *Server_HTTP) GetTimeout() *durationpb.Duration { + if x != nil { + return x.Timeout + } + return nil +} + +type Server_GRPC struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Network string `protobuf:"bytes,1,opt,name=network,proto3" json:"network,omitempty"` + Addr string `protobuf:"bytes,2,opt,name=addr,proto3" json:"addr,omitempty"` + Timeout *durationpb.Duration `protobuf:"bytes,3,opt,name=timeout,proto3" json:"timeout,omitempty"` +} + +func (x *Server_GRPC) Reset() { + *x = Server_GRPC{} + if protoimpl.UnsafeEnabled { + mi := &file_conf_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Server_GRPC) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Server_GRPC) ProtoMessage() {} + +func (x *Server_GRPC) ProtoReflect() protoreflect.Message { + mi := &file_conf_proto_msgTypes[8] + 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 Server_GRPC.ProtoReflect.Descriptor instead. +func (*Server_GRPC) Descriptor() ([]byte, []int) { + return file_conf_proto_rawDescGZIP(), []int{1, 1} +} + +func (x *Server_GRPC) GetNetwork() string { + if x != nil { + return x.Network + } + return "" +} + +func (x *Server_GRPC) GetAddr() string { + if x != nil { + return x.Addr + } + return "" +} + +func (x *Server_GRPC) GetTimeout() *durationpb.Duration { + if x != nil { + return x.Timeout + } + return nil +} + +type Server_ETCD struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Addr []string `protobuf:"bytes,1,rep,name=addr,proto3" json:"addr,omitempty"` + Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"` + Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"` +} + +func (x *Server_ETCD) Reset() { + *x = Server_ETCD{} + if protoimpl.UnsafeEnabled { + mi := &file_conf_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Server_ETCD) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Server_ETCD) ProtoMessage() {} + +func (x *Server_ETCD) ProtoReflect() protoreflect.Message { + mi := &file_conf_proto_msgTypes[9] + 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 Server_ETCD.ProtoReflect.Descriptor instead. +func (*Server_ETCD) Descriptor() ([]byte, []int) { + return file_conf_proto_rawDescGZIP(), []int{1, 2} +} + +func (x *Server_ETCD) GetAddr() []string { + if x != nil { + return x.Addr + } + return nil +} + +func (x *Server_ETCD) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *Server_ETCD) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +type Data_Database struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Driver string `protobuf:"bytes,1,opt,name=driver,proto3" json:"driver,omitempty"` + Source string `protobuf:"bytes,2,opt,name=source,proto3" json:"source,omitempty"` +} + +func (x *Data_Database) Reset() { + *x = Data_Database{} + if protoimpl.UnsafeEnabled { + mi := &file_conf_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Data_Database) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Data_Database) ProtoMessage() {} + +func (x *Data_Database) ProtoReflect() protoreflect.Message { + mi := &file_conf_proto_msgTypes[10] + 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 Data_Database.ProtoReflect.Descriptor instead. +func (*Data_Database) Descriptor() ([]byte, []int) { + return file_conf_proto_rawDescGZIP(), []int{2, 0} +} + +func (x *Data_Database) GetDriver() string { + if x != nil { + return x.Driver + } + return "" +} + +func (x *Data_Database) GetSource() string { + if x != nil { + return x.Source + } + return "" +} + +type Data_Redis struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Network string `protobuf:"bytes,1,opt,name=network,proto3" json:"network,omitempty"` + Addr string `protobuf:"bytes,2,opt,name=addr,proto3" json:"addr,omitempty"` + Db int32 `protobuf:"varint,3,opt,name=db,proto3" json:"db,omitempty"` + Password string `protobuf:"bytes,4,opt,name=password,proto3" json:"password,omitempty"` + Pool int32 `protobuf:"varint,5,opt,name=pool,proto3" json:"pool,omitempty"` + ReadTimeout *durationpb.Duration `protobuf:"bytes,6,opt,name=read_timeout,json=readTimeout,proto3" json:"read_timeout,omitempty"` + WriteTimeout *durationpb.Duration `protobuf:"bytes,7,opt,name=write_timeout,json=writeTimeout,proto3" json:"write_timeout,omitempty"` +} + +func (x *Data_Redis) Reset() { + *x = Data_Redis{} + if protoimpl.UnsafeEnabled { + mi := &file_conf_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Data_Redis) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Data_Redis) ProtoMessage() {} + +func (x *Data_Redis) ProtoReflect() protoreflect.Message { + mi := &file_conf_proto_msgTypes[11] + 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 Data_Redis.ProtoReflect.Descriptor instead. +func (*Data_Redis) Descriptor() ([]byte, []int) { + return file_conf_proto_rawDescGZIP(), []int{2, 1} +} + +func (x *Data_Redis) GetNetwork() string { + if x != nil { + return x.Network + } + return "" +} + +func (x *Data_Redis) GetAddr() string { + if x != nil { + return x.Addr + } + return "" +} + +func (x *Data_Redis) GetDb() int32 { + if x != nil { + return x.Db + } + return 0 +} + +func (x *Data_Redis) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +func (x *Data_Redis) GetPool() int32 { + if x != nil { + return x.Pool + } + return 0 +} + +func (x *Data_Redis) GetReadTimeout() *durationpb.Duration { + if x != nil { + return x.ReadTimeout + } + return nil +} + +func (x *Data_Redis) GetWriteTimeout() *durationpb.Duration { + if x != nil { + return x.WriteTimeout + } + return nil +} + +type Queue_Kafka struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Addrs []string `protobuf:"bytes,1,rep,name=addrs,proto3" json:"addrs,omitempty"` + Topic string `protobuf:"bytes,2,opt,name=topic,proto3" json:"topic,omitempty"` + Group string `protobuf:"bytes,3,opt,name=group,proto3" json:"group,omitempty"` + Username string `protobuf:"bytes,4,opt,name=username,proto3" json:"username,omitempty"` + Password string `protobuf:"bytes,5,opt,name=password,proto3" json:"password,omitempty"` +} + +func (x *Queue_Kafka) Reset() { + *x = Queue_Kafka{} + if protoimpl.UnsafeEnabled { + mi := &file_conf_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Queue_Kafka) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Queue_Kafka) ProtoMessage() {} + +func (x *Queue_Kafka) ProtoReflect() protoreflect.Message { + mi := &file_conf_proto_msgTypes[12] + 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 Queue_Kafka.ProtoReflect.Descriptor instead. +func (*Queue_Kafka) Descriptor() ([]byte, []int) { + return file_conf_proto_rawDescGZIP(), []int{3, 0} +} + +func (x *Queue_Kafka) GetAddrs() []string { + if x != nil { + return x.Addrs + } + return nil +} + +func (x *Queue_Kafka) GetTopic() string { + if x != nil { + return x.Topic + } + return "" +} + +func (x *Queue_Kafka) GetGroup() string { + if x != nil { + return x.Group + } + return "" +} + +func (x *Queue_Kafka) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *Queue_Kafka) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +type Queue_Asynq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Network string `protobuf:"bytes,1,opt,name=network,proto3" json:"network,omitempty"` + Addr string `protobuf:"bytes,2,opt,name=addr,proto3" json:"addr,omitempty"` + Db int32 `protobuf:"varint,3,opt,name=db,proto3" json:"db,omitempty"` + Password string `protobuf:"bytes,4,opt,name=password,proto3" json:"password,omitempty"` + Pool int32 `protobuf:"varint,5,opt,name=pool,proto3" json:"pool,omitempty"` + ReadTimeout *durationpb.Duration `protobuf:"bytes,6,opt,name=read_timeout,json=readTimeout,proto3" json:"read_timeout,omitempty"` + WriteTimeout *durationpb.Duration `protobuf:"bytes,7,opt,name=write_timeout,json=writeTimeout,proto3" json:"write_timeout,omitempty"` + Concurrency int32 `protobuf:"varint,8,opt,name=concurrency,proto3" json:"concurrency,omitempty"` +} + +func (x *Queue_Asynq) Reset() { + *x = Queue_Asynq{} + if protoimpl.UnsafeEnabled { + mi := &file_conf_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Queue_Asynq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Queue_Asynq) ProtoMessage() {} + +func (x *Queue_Asynq) ProtoReflect() protoreflect.Message { + mi := &file_conf_proto_msgTypes[13] + 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 Queue_Asynq.ProtoReflect.Descriptor instead. +func (*Queue_Asynq) Descriptor() ([]byte, []int) { + return file_conf_proto_rawDescGZIP(), []int{3, 1} +} + +func (x *Queue_Asynq) GetNetwork() string { + if x != nil { + return x.Network + } + return "" +} + +func (x *Queue_Asynq) GetAddr() string { + if x != nil { + return x.Addr + } + return "" +} + +func (x *Queue_Asynq) GetDb() int32 { + if x != nil { + return x.Db + } + return 0 +} + +func (x *Queue_Asynq) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +func (x *Queue_Asynq) GetPool() int32 { + if x != nil { + return x.Pool + } + return 0 +} + +func (x *Queue_Asynq) GetReadTimeout() *durationpb.Duration { + if x != nil { + return x.ReadTimeout + } + return nil +} + +func (x *Queue_Asynq) GetWriteTimeout() *durationpb.Duration { + if x != nil { + return x.WriteTimeout + } + return nil +} + +func (x *Queue_Asynq) GetConcurrency() int32 { + if x != nil { + return x.Concurrency + } + return 0 +} + +type Pagsmile_Payout struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AppId string `protobuf:"bytes,1,opt,name=app_id,json=appId,proto3" json:"app_id,omitempty"` + AppKey string `protobuf:"bytes,2,opt,name=app_key,json=appKey,proto3" json:"app_key,omitempty"` + ApiUrl string `protobuf:"bytes,3,opt,name=api_url,json=apiUrl,proto3" json:"api_url,omitempty"` + NotifyUrl string `protobuf:"bytes,4,opt,name=notify_url,json=notifyUrl,proto3" json:"notify_url,omitempty"` +} + +func (x *Pagsmile_Payout) Reset() { + *x = Pagsmile_Payout{} + if protoimpl.UnsafeEnabled { + mi := &file_conf_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Pagsmile_Payout) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Pagsmile_Payout) ProtoMessage() {} + +func (x *Pagsmile_Payout) ProtoReflect() protoreflect.Message { + mi := &file_conf_proto_msgTypes[14] + 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 Pagsmile_Payout.ProtoReflect.Descriptor instead. +func (*Pagsmile_Payout) Descriptor() ([]byte, []int) { + return file_conf_proto_rawDescGZIP(), []int{4, 0} +} + +func (x *Pagsmile_Payout) GetAppId() string { + if x != nil { + return x.AppId + } + return "" +} + +func (x *Pagsmile_Payout) GetAppKey() string { + if x != nil { + return x.AppKey + } + return "" +} + +func (x *Pagsmile_Payout) GetApiUrl() string { + if x != nil { + return x.ApiUrl + } + return "" +} + +func (x *Pagsmile_Payout) GetNotifyUrl() string { + if x != nil { + return x.NotifyUrl + } + return "" +} + +var File_conf_proto protoreflect.FileDescriptor + +var file_conf_proto_rawDesc = []byte{ + 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x6b, 0x72, + 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa8, 0x02, 0x0a, 0x09, 0x42, 0x6f, 0x6f, + 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x12, 0x24, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x10, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x61, + 0x74, 0x61, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x27, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x75, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x05, 0x71, 0x75, 0x65, 0x75, + 0x65, 0x12, 0x30, 0x0a, 0x08, 0x70, 0x61, 0x67, 0x73, 0x6d, 0x69, 0x6c, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x50, 0x61, 0x67, 0x73, 0x6d, 0x69, 0x6c, 0x65, 0x52, 0x08, 0x70, 0x61, 0x67, 0x73, 0x6d, + 0x69, 0x6c, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46, 0x69, 0x6c, + 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, + 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46, 0x69, 0x6c, 0x65, + 0x73, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x33, + 0x0a, 0x09, 0x61, 0x70, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x15, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, + 0x70, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x61, 0x70, 0x70, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x22, 0x98, 0x05, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x2b, + 0x0a, 0x04, 0x68, 0x74, 0x74, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6b, + 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, 0x04, 0x68, 0x74, 0x74, 0x70, 0x12, 0x2b, 0x0a, 0x04, 0x67, + 0x72, 0x70, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6b, 0x72, 0x61, 0x74, + 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x47, 0x52, + 0x50, 0x43, 0x52, 0x04, 0x67, 0x72, 0x70, 0x63, 0x12, 0x2b, 0x0a, 0x04, 0x65, 0x74, 0x63, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x54, 0x43, 0x44, 0x52, + 0x04, 0x65, 0x74, 0x63, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x65, + 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x74, + 0x72, 0x61, 0x63, 0x65, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, + 0x65, 0x6e, 0x76, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x12, 0x19, + 0x0a, 0x08, 0x67, 0x65, 0x6f, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x67, 0x65, 0x6f, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x76, 0x72, + 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x73, 0x76, 0x72, 0x49, 0x64, + 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x64, 0x61, 0x79, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x08, 0x66, 0x69, 0x72, 0x73, 0x74, 0x44, 0x61, 0x79, 0x12, 0x1b, 0x0a, + 0x09, 0x76, 0x65, 0x72, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x76, 0x65, 0x72, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x38, 0x0a, 0x17, 0x74, 0x69, + 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x50, 0x65, 0x72, 0x31, 0x30, 0x53, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x17, 0x74, 0x69, 0x6d, + 0x65, 0x6f, 0x75, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x50, 0x65, 0x72, 0x31, 0x30, 0x53, 0x65, + 0x63, 0x6f, 0x6e, 0x64, 0x1a, 0x69, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x12, 0x18, 0x0a, 0x07, + 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x74, 0x69, + 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x1a, + 0x69, 0x0a, 0x04, 0x47, 0x52, 0x50, 0x43, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x1a, 0x52, 0x0a, 0x04, 0x45, 0x54, + 0x43, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x9d, + 0x03, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x35, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, + 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6b, 0x72, 0x61, 0x74, + 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x44, 0x61, 0x74, 0x61, + 0x62, 0x61, 0x73, 0x65, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x2c, + 0x0a, 0x05, 0x72, 0x65, 0x64, 0x69, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, + 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x2e, + 0x52, 0x65, 0x64, 0x69, 0x73, 0x52, 0x05, 0x72, 0x65, 0x64, 0x69, 0x73, 0x1a, 0x3a, 0x0a, 0x08, + 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x72, 0x69, 0x76, + 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, + 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a, 0xf3, 0x01, 0x0a, 0x05, 0x52, 0x65, 0x64, + 0x69, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x12, 0x0a, 0x04, + 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, + 0x12, 0x0e, 0x0a, 0x02, 0x64, 0x62, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x64, 0x62, + 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x12, 0x0a, 0x04, + 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x6f, 0x6f, 0x6c, + 0x12, 0x3c, 0x0a, 0x0c, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x3e, + 0x0a, 0x0d, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x0c, 0x77, 0x72, 0x69, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x22, 0x81, + 0x04, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x75, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x6b, 0x61, 0x66, 0x6b, + 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x2e, 0x4b, 0x61, 0x66, 0x6b, 0x61, + 0x52, 0x05, 0x6b, 0x61, 0x66, 0x6b, 0x61, 0x12, 0x2d, 0x0a, 0x05, 0x61, 0x73, 0x79, 0x6e, 0x71, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x2e, 0x41, 0x73, 0x79, 0x6e, 0x71, 0x52, + 0x05, 0x61, 0x73, 0x79, 0x6e, 0x71, 0x1a, 0x81, 0x01, 0x0a, 0x05, 0x4b, 0x61, 0x66, 0x6b, 0x61, + 0x12, 0x14, 0x0a, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x14, 0x0a, 0x05, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, + 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x1a, 0x95, 0x02, 0x0a, 0x05, 0x41, + 0x73, 0x79, 0x6e, 0x71, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x12, + 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, + 0x64, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x64, 0x62, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, + 0x64, 0x62, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x12, + 0x0a, 0x04, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x6f, + 0x6f, 0x6c, 0x12, 0x3c, 0x0a, 0x0c, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, + 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x12, 0x3e, 0x0a, 0x0d, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, + 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x0c, 0x77, 0x72, 0x69, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x63, 0x79, 0x22, 0xb1, 0x01, 0x0a, 0x08, 0x50, 0x61, 0x67, 0x73, 0x6d, 0x69, 0x6c, 0x65, 0x12, + 0x33, 0x0a, 0x06, 0x70, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1b, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x61, 0x67, + 0x73, 0x6d, 0x69, 0x6c, 0x65, 0x2e, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x52, 0x06, 0x70, 0x61, + 0x79, 0x6f, 0x75, 0x74, 0x1a, 0x70, 0x0a, 0x06, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x12, 0x15, + 0x0a, 0x06, 0x61, 0x70, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x61, 0x70, 0x70, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x61, 0x70, 0x70, 0x5f, 0x6b, 0x65, 0x79, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x70, 0x70, 0x4b, 0x65, 0x79, 0x12, 0x17, + 0x0a, 0x07, 0x61, 0x70, 0x69, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x61, 0x70, 0x69, 0x55, 0x72, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x6f, 0x74, 0x69, 0x66, + 0x79, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x6f, 0x74, + 0x69, 0x66, 0x79, 0x55, 0x72, 0x6c, 0x22, 0x21, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0xe3, 0x01, 0x0a, 0x09, 0x41, 0x70, + 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x26, 0x0a, 0x0e, 0x61, 0x64, 0x6a, 0x75, 0x73, + 0x74, 0x41, 0x70, 0x70, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x61, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x41, 0x70, 0x70, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, + 0x26, 0x0a, 0x0e, 0x61, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x53, 0x32, 0x53, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x53, + 0x32, 0x53, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x38, 0x0a, 0x17, 0x61, 0x64, 0x6a, 0x75, 0x73, + 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x75, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x61, 0x64, 0x6a, 0x75, 0x73, 0x74, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x12, 0x32, 0x0a, 0x14, 0x61, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x14, 0x61, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x73, 0x41, 0x70, 0x70, 0x49, 0x64, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x73, 0x41, 0x70, 0x70, 0x49, 0x64, 0x42, + 0x26, 0x5a, 0x24, 0x73, 0x61, 0x6e, 0x64, 0x63, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x65, 0x6f, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x63, 0x6f, + 0x6e, 0x66, 0x3b, 0x63, 0x6f, 0x6e, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_conf_proto_rawDescOnce sync.Once + file_conf_proto_rawDescData = file_conf_proto_rawDesc +) + +func file_conf_proto_rawDescGZIP() []byte { + file_conf_proto_rawDescOnce.Do(func() { + file_conf_proto_rawDescData = protoimpl.X.CompressGZIP(file_conf_proto_rawDescData) + }) + return file_conf_proto_rawDescData +} + +var file_conf_proto_msgTypes = make([]protoimpl.MessageInfo, 15) +var file_conf_proto_goTypes = []interface{}{ + (*Bootstrap)(nil), // 0: kratos.api.Bootstrap + (*Server)(nil), // 1: kratos.api.Server + (*Data)(nil), // 2: kratos.api.Data + (*Queue)(nil), // 3: kratos.api.Queue + (*Pagsmile)(nil), // 4: kratos.api.Pagsmile + (*ConfigFiles)(nil), // 5: kratos.api.ConfigFiles + (*AppConfig)(nil), // 6: kratos.api.AppConfig + (*Server_HTTP)(nil), // 7: kratos.api.Server.HTTP + (*Server_GRPC)(nil), // 8: kratos.api.Server.GRPC + (*Server_ETCD)(nil), // 9: kratos.api.Server.ETCD + (*Data_Database)(nil), // 10: kratos.api.Data.Database + (*Data_Redis)(nil), // 11: kratos.api.Data.Redis + (*Queue_Kafka)(nil), // 12: kratos.api.Queue.Kafka + (*Queue_Asynq)(nil), // 13: kratos.api.Queue.Asynq + (*Pagsmile_Payout)(nil), // 14: kratos.api.Pagsmile.Payout + (*durationpb.Duration)(nil), // 15: google.protobuf.Duration +} +var file_conf_proto_depIdxs = []int32{ + 1, // 0: kratos.api.Bootstrap.server:type_name -> kratos.api.Server + 2, // 1: kratos.api.Bootstrap.data:type_name -> kratos.api.Data + 3, // 2: kratos.api.Bootstrap.queue:type_name -> kratos.api.Queue + 4, // 3: kratos.api.Bootstrap.pagsmile:type_name -> kratos.api.Pagsmile + 5, // 4: kratos.api.Bootstrap.configFiles:type_name -> kratos.api.ConfigFiles + 6, // 5: kratos.api.Bootstrap.appConfig:type_name -> kratos.api.AppConfig + 7, // 6: kratos.api.Server.http:type_name -> kratos.api.Server.HTTP + 8, // 7: kratos.api.Server.grpc:type_name -> kratos.api.Server.GRPC + 9, // 8: kratos.api.Server.etcd:type_name -> kratos.api.Server.ETCD + 10, // 9: kratos.api.Data.database:type_name -> kratos.api.Data.Database + 11, // 10: kratos.api.Data.redis:type_name -> kratos.api.Data.Redis + 12, // 11: kratos.api.Queue.kafka:type_name -> kratos.api.Queue.Kafka + 13, // 12: kratos.api.Queue.asynq:type_name -> kratos.api.Queue.Asynq + 14, // 13: kratos.api.Pagsmile.payout:type_name -> kratos.api.Pagsmile.Payout + 15, // 14: kratos.api.Server.HTTP.timeout:type_name -> google.protobuf.Duration + 15, // 15: kratos.api.Server.GRPC.timeout:type_name -> google.protobuf.Duration + 15, // 16: kratos.api.Data.Redis.read_timeout:type_name -> google.protobuf.Duration + 15, // 17: kratos.api.Data.Redis.write_timeout:type_name -> google.protobuf.Duration + 15, // 18: kratos.api.Queue.Asynq.read_timeout:type_name -> google.protobuf.Duration + 15, // 19: kratos.api.Queue.Asynq.write_timeout:type_name -> google.protobuf.Duration + 20, // [20:20] is the sub-list for method output_type + 20, // [20:20] is the sub-list for method input_type + 20, // [20:20] is the sub-list for extension type_name + 20, // [20:20] is the sub-list for extension extendee + 0, // [0:20] is the sub-list for field type_name +} + +func init() { file_conf_proto_init() } +func file_conf_proto_init() { + if File_conf_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_conf_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Bootstrap); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_conf_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Server); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_conf_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Data); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_conf_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Queue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_conf_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Pagsmile); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_conf_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConfigFiles); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_conf_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AppConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_conf_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Server_HTTP); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_conf_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Server_GRPC); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_conf_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Server_ETCD); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_conf_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Data_Database); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_conf_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Data_Redis); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_conf_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Queue_Kafka); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_conf_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Queue_Asynq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_conf_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Pagsmile_Payout); 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_conf_proto_rawDesc, + NumEnums: 0, + NumMessages: 15, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_conf_proto_goTypes, + DependencyIndexes: file_conf_proto_depIdxs, + MessageInfos: file_conf_proto_msgTypes, + }.Build() + File_conf_proto = out.File + file_conf_proto_rawDesc = nil + file_conf_proto_goTypes = nil + file_conf_proto_depIdxs = nil +} diff --git a/app/eonline/internal/conf/conf.proto b/app/eonline/internal/conf/conf.proto new file mode 100644 index 0000000..316736a --- /dev/null +++ b/app/eonline/internal/conf/conf.proto @@ -0,0 +1,105 @@ +syntax = "proto3"; +package kratos.api; + +option go_package = "sandc/app/eonline/internal/conf;conf"; + +import "google/protobuf/duration.proto"; + +message Bootstrap { + Server server = 1; + Data data = 2; + Queue queue = 3; + Pagsmile pagsmile = 4; + ConfigFiles configFiles = 5; + AppConfig appConfig = 7; +} + +message Server { + message HTTP { + string network = 1; + string addr = 2; + google.protobuf.Duration timeout = 3; + } + message GRPC { + string network = 1; + string addr = 2; + google.protobuf.Duration timeout = 3; + } + message ETCD { + repeated string addr = 1; + string username = 2; + string password = 3; + } + HTTP http = 1; + GRPC grpc = 2; + ETCD etcd = 3; + string trace_endpoint = 4; + string env = 6; + string geo_file = 7; + int32 svr_id = 8; // 当前服务器id + int32 first_day = 9; // 开服首日,格式类似 20230906,表示2023.9.6 + string ver_check = 10; // 版本号检查,最低版本号 + int32 timeoutTimerPer10Second = 11; // 10秒timer执行超时时间纳秒 +} + +message Data { + message Database { + string driver = 1; + string source = 2; + } + message Redis { + string network = 1; + string addr = 2; + int32 db = 3; + string password = 4; + int32 pool = 5; + google.protobuf.Duration read_timeout = 6; + google.protobuf.Duration write_timeout = 7; + } + Database database = 1; + Redis redis = 2; +} + +message Queue { + message Kafka { + repeated string addrs = 1; + string topic = 2; + string group = 3; + string username = 4; + string password = 5; + } + message Asynq { + string network = 1; + string addr = 2; + int32 db = 3; + string password = 4; + int32 pool = 5; + google.protobuf.Duration read_timeout = 6; + google.protobuf.Duration write_timeout = 7; + int32 concurrency = 8; + } + Kafka kafka = 1; + Asynq asynq = 2; +} + +message Pagsmile { + message Payout { + string app_id = 1; + string app_key = 2; + string api_url = 3; + string notify_url = 4; + } + Payout payout = 1; +} + +message ConfigFiles { + string path = 1; +} + +message AppConfig { + string adjustAppToken = 1; + string adjustS2SToken = 2; + string adjustEventTokenSuccess = 3; + string adjustEventTokenFail = 4; + string ssAppId = 5; +} diff --git a/app/eonline/internal/conf/google/protobuf/duration.proto b/app/eonline/internal/conf/google/protobuf/duration.proto new file mode 100644 index 0000000..41f40c2 --- /dev/null +++ b/app/eonline/internal/conf/google/protobuf/duration.proto @@ -0,0 +1,115 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/protobuf/types/known/durationpb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "DurationProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; + +// A Duration represents a signed, fixed-length span of time represented +// as a count of seconds and fractions of seconds at nanosecond +// resolution. It is independent of any calendar and concepts like "day" +// or "month". It is related to Timestamp in that the difference between +// two Timestamp values is a Duration and it can be added or subtracted +// from a Timestamp. Range is approximately +-10,000 years. +// +// # Examples +// +// Example 1: Compute Duration from two Timestamps in pseudo code. +// +// Timestamp start = ...; +// Timestamp end = ...; +// Duration duration = ...; +// +// duration.seconds = end.seconds - start.seconds; +// duration.nanos = end.nanos - start.nanos; +// +// if (duration.seconds < 0 && duration.nanos > 0) { +// duration.seconds += 1; +// duration.nanos -= 1000000000; +// } else if (duration.seconds > 0 && duration.nanos < 0) { +// duration.seconds -= 1; +// duration.nanos += 1000000000; +// } +// +// Example 2: Compute Timestamp from Timestamp + Duration in pseudo code. +// +// Timestamp start = ...; +// Duration duration = ...; +// Timestamp end = ...; +// +// end.seconds = start.seconds + duration.seconds; +// end.nanos = start.nanos + duration.nanos; +// +// if (end.nanos < 0) { +// end.seconds -= 1; +// end.nanos += 1000000000; +// } else if (end.nanos >= 1000000000) { +// end.seconds += 1; +// end.nanos -= 1000000000; +// } +// +// Example 3: Compute Duration from datetime.timedelta in Python. +// +// td = datetime.timedelta(days=3, minutes=10) +// duration = Duration() +// duration.FromTimedelta(td) +// +// # JSON Mapping +// +// In JSON format, the Duration type is encoded as a string rather than an +// object, where the string ends in the suffix "s" (indicating seconds) and +// is preceded by the number of seconds, with nanoseconds expressed as +// fractional seconds. For example, 3 seconds with 0 nanoseconds should be +// encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should +// be expressed in JSON format as "3.000000001s", and 3 seconds and 1 +// microsecond should be expressed in JSON format as "3.000001s". +// +message Duration { + // Signed seconds of the span of time. Must be from -315,576,000,000 + // to +315,576,000,000 inclusive. Note: these bounds are computed from: + // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years + int64 seconds = 1; + + // Signed fractions of a second at nanosecond resolution of the span + // of time. Durations less than one second are represented with a 0 + // `seconds` field and a positive or negative `nanos` field. For durations + // of one second or more, a non-zero value for the `nanos` field must be + // of the same sign as the `seconds` field. Must be from -999,999,999 + // to +999,999,999 inclusive. + int32 nanos = 2; +} diff --git a/app/eonline/internal/conf/openapi.yaml b/app/eonline/internal/conf/openapi.yaml new file mode 100644 index 0000000..f8f3735 --- /dev/null +++ b/app/eonline/internal/conf/openapi.yaml @@ -0,0 +1,10 @@ +# Generated with protoc-gen-openapi +# https://github.com/google/gnostic/tree/master/cmd/protoc-gen-openapi + +openapi: 3.0.3 +info: + title: "" + version: 0.0.1 +paths: {} +components: + schemas: {} diff --git a/app/eonline/internal/config/bright/serialization/ByteBuf.go b/app/eonline/internal/config/bright/serialization/ByteBuf.go new file mode 100644 index 0000000..0b7e23f --- /dev/null +++ b/app/eonline/internal/config/bright/serialization/ByteBuf.go @@ -0,0 +1,662 @@ +package serialization + +import ( + "errors" + "math" +) + +var EmptyBytes []byte + +var UnmarshalErr = errors.New("read not enough") + +func init() { + EmptyBytes = make([]byte, 0) +} + +type ByteBuf struct { + readerIndex int + writerIndex int + capacity int + bytes []byte +} + +func NewByteBuf(capacity int) *ByteBuf { + v := &ByteBuf{} + v.bytes = make([]byte, capacity, capacity) + v.readerIndex = 0 + v.writerIndex = 0 + v.capacity = capacity + return v +} + +func WrapByteBuf(bytes []byte) *ByteBuf { + v := &ByteBuf{} + v.bytes = bytes + v.readerIndex = 0 + v.writerIndex = len(bytes) + v.capacity = len(bytes) + return v +} + +func (buf *ByteBuf) Replace(bytes []byte) { + buf.bytes = bytes + buf.readerIndex = 0 + buf.writerIndex = len(bytes) + buf.capacity = len(bytes) +} + +func (buf *ByteBuf) Clear() { + buf.readerIndex = 0 + buf.writerIndex = 0 +} + +func (buf *ByteBuf) Size() int { + return buf.writerIndex - buf.readerIndex +} + +func (buf *ByteBuf) GetBytes() []byte { + return buf.bytes +} + +func (buf *ByteBuf) CopyRemainData() []byte { + size := len(buf.bytes) + if size > 0 { + bs := make([]byte, size, size) + copy(bs, buf.bytes[buf.readerIndex:buf.writerIndex]) + return bs + } else { + return EmptyBytes + } +} + +func (buf *ByteBuf) CalcNewCap(curSize int, needSize int) int { + curSize *= 2 + if curSize < 16 { + curSize = 16 + } + for ; curSize < needSize; curSize *= 2 { + } + return curSize +} + +func (buf *ByteBuf) EnsureWrite(remain int) { + if buf.writerIndex+remain > buf.capacity { + size := buf.Size() + if size+remain <= buf.capacity { + copy(buf.bytes, buf.bytes[buf.readerIndex:buf.writerIndex]) + } else { + buf.capacity = buf.CalcNewCap(buf.capacity, size+remain) + newBytes := make([]byte, buf.capacity, buf.capacity) + copy(newBytes, buf.bytes[buf.readerIndex:buf.writerIndex]) + buf.bytes = newBytes + } + buf.writerIndex = size + buf.readerIndex = 0 + } +} + +func (buf *ByteBuf) ReadBool() (bool, error) { + if buf.readerIndex < buf.writerIndex { + x := buf.bytes[buf.readerIndex] != 0 + buf.readerIndex++ + return x, nil + } else { + return false, UnmarshalErr + } +} + +func (buf *ByteBuf) WriteBool(x bool) { + buf.EnsureWrite(1) + if x { + buf.bytes[buf.writerIndex] = 1 + } else { + buf.bytes[buf.writerIndex] = 0 + } + buf.writerIndex++ +} + +func (buf *ByteBuf) ReadByte() (byte, error) { + if buf.readerIndex < buf.writerIndex { + x := buf.bytes[buf.readerIndex] + buf.readerIndex++ + return x, nil + } else { + return 0, UnmarshalErr + } +} + +func (buf *ByteBuf) WriteByte(x byte) { + buf.EnsureWrite(1) + buf.bytes[buf.writerIndex] = x + buf.writerIndex++ +} + +func (buf *ByteBuf) ReadShort() (int16, error) { + if buf.readerIndex >= buf.writerIndex { + return 0, UnmarshalErr + } + h := uint32(buf.bytes[buf.readerIndex]) + if h < 0x80 { + buf.readerIndex++ + return int16(h), nil + } else if h < 0xc0 { + if buf.readerIndex+2 > buf.writerIndex { + return 0, UnmarshalErr + } + x := ((h & 0x3f) << 8) | uint32(buf.bytes[buf.readerIndex+1]) + buf.readerIndex += 2 + return int16(x), nil + } else if h == 0xff { + if buf.readerIndex+3 > buf.writerIndex { + return 0, UnmarshalErr + } + x := (uint32(buf.bytes[buf.readerIndex+1]) << 8) | uint32(buf.bytes[buf.readerIndex+2]) + buf.readerIndex += 3 + return int16(x), nil + } else { + return 0, UnmarshalErr + } +} + +func (buf *ByteBuf) WriteShort(x int16) { + if x >= 0 { + if x < 0x80 { + buf.EnsureWrite(1) + buf.bytes[buf.writerIndex] = byte(x) + buf.writerIndex++ + return + } else if x < 0x4000 { + buf.EnsureWrite(2) + buf.bytes[buf.writerIndex+1] = byte(x) + buf.bytes[buf.writerIndex] = byte((x >> 8) | 0x80) + buf.writerIndex += 2 + return + } + } + buf.EnsureWrite(3) + buf.bytes[buf.writerIndex] = 0xff + buf.bytes[buf.writerIndex+2] = byte(x) + buf.bytes[buf.writerIndex+1] = byte(x >> 8) + buf.writerIndex += 3 +} + +func (buf *ByteBuf) ReadFshort() (int16, error) { + if buf.readerIndex+2 > buf.writerIndex { + return 0, UnmarshalErr + } + x := int(buf.bytes[buf.readerIndex]) | (int(buf.bytes[buf.readerIndex+1]) << 8) + buf.readerIndex += 2 + return int16(x), nil +} + +func (buf *ByteBuf) WriteFshort(x int16) { + buf.EnsureWrite(2) + buf.bytes[buf.writerIndex] = byte(x) + buf.bytes[buf.writerIndex+1] = byte(x >> 8) + buf.writerIndex += 2 +} + +func (buf *ByteBuf) ReadInt() (int32, error) { + x, err := buf.ReadUint() + return int32(x), err +} + +func (buf *ByteBuf) WriteInt(x int32) { + buf.WriteUint(uint32(x)) +} + +func (buf *ByteBuf) ReadUint() (uint32, error) { + if buf.readerIndex >= buf.writerIndex { + return 0, UnmarshalErr + } + h := uint32(buf.bytes[buf.readerIndex]) + if h < 0x80 { + buf.readerIndex++ + return h, nil + } else if h < 0xc0 { + if buf.readerIndex+2 > buf.writerIndex { + return 0, UnmarshalErr + } + x := ((h & 0x3f) << 8) | uint32(buf.bytes[buf.readerIndex+1]) + buf.readerIndex += 2 + return x, nil + } else if h < 0xe0 { + if buf.readerIndex+3 > buf.writerIndex { + return 0, UnmarshalErr + } + x := ((h & 0x1f) << 16) | (uint32(buf.bytes[buf.readerIndex+1]) << 8) | uint32(buf.bytes[buf.readerIndex+2]) + buf.readerIndex += 3 + return x, nil + } else if h < 0xf0 { + if buf.readerIndex+4 > buf.writerIndex { + return 0, UnmarshalErr + } + x := ((h & 0x0f) << 24) | (uint32(buf.bytes[buf.readerIndex+1]) << 16) | (uint32(buf.bytes[buf.readerIndex+2]) << 8) | uint32(buf.bytes[buf.readerIndex+3]) + buf.readerIndex += 4 + return x, nil + } else { + if buf.readerIndex+5 > buf.writerIndex { + return 0, UnmarshalErr + } + x := (uint32(buf.bytes[buf.readerIndex+1]) << 24) | (uint32(buf.bytes[buf.readerIndex+2]) << 16) | (uint32(buf.bytes[buf.readerIndex+3]) << 8) | uint32(buf.bytes[buf.readerIndex+4]) + buf.readerIndex += 5 + return x, nil + } +} + +func (buf *ByteBuf) WriteUint(x uint32) { + if x < 0x80 { + buf.EnsureWrite(1) + buf.bytes[buf.writerIndex] = byte(x) + buf.writerIndex++ + } else if x < 0x4000 { + buf.EnsureWrite(2) + buf.bytes[buf.writerIndex+1] = byte(x) + buf.bytes[buf.writerIndex] = byte((x >> 8) | 0x80) + buf.writerIndex += 2 + } else if x < 0x200000 { + buf.EnsureWrite(3) + buf.bytes[buf.writerIndex+2] = byte(x) + buf.bytes[buf.writerIndex+1] = byte(x >> 8) + buf.bytes[buf.writerIndex] = byte((x >> 16) | 0xc0) + buf.writerIndex += 3 + } else if x < 0x10000000 { + buf.EnsureWrite(4) + buf.bytes[buf.writerIndex+3] = byte(x) + buf.bytes[buf.writerIndex+2] = byte(x >> 8) + buf.bytes[buf.writerIndex+1] = byte(x >> 16) + buf.bytes[buf.writerIndex] = byte((x >> 24) | 0xe0) + buf.writerIndex += 4 + } else { + buf.EnsureWrite(5) + buf.bytes[buf.writerIndex] = 0xf0 + buf.bytes[buf.writerIndex+4] = byte(x) + buf.bytes[buf.writerIndex+3] = byte(x >> 8) + buf.bytes[buf.writerIndex+2] = byte(x >> 16) + buf.bytes[buf.writerIndex+1] = byte(x >> 24) + buf.writerIndex += 5 + } +} + +func (buf *ByteBuf) ReadFint() (int32, error) { + if buf.readerIndex+4 > buf.writerIndex { + return 0, UnmarshalErr + } + x := int32(uint(buf.bytes[buf.readerIndex]) | (uint(buf.bytes[buf.readerIndex+1]) << 8) | + (uint(buf.bytes[buf.readerIndex+2]) << 16) | (uint(buf.bytes[buf.readerIndex+3]) << 24)) + buf.readerIndex += 4 + return x, nil +} + +func (buf *ByteBuf) WriteFint(x int32) { + buf.EnsureWrite(4) + buf.bytes[buf.writerIndex] = byte(x) + buf.bytes[buf.writerIndex+1] = byte(x >> 8) + buf.bytes[buf.writerIndex+2] = byte(x >> 16) + buf.bytes[buf.writerIndex+3] = byte(x >> 24) + buf.writerIndex += 4 +} + +func (buf *ByteBuf) ReadLong() (int64, error) { + x, err := buf.ReadUlong() + return int64(x), err +} + +func (buf *ByteBuf) WriteLong(x int64) { + buf.WriteUlong(uint64(x)) +} + +func (buf *ByteBuf) ReadUlong() (uint64, error) { + if buf.readerIndex >= buf.writerIndex { + return 0, UnmarshalErr + } + h := uint64(buf.bytes[buf.readerIndex]) + if h < 0x80 { + buf.readerIndex++ + return h, nil + } else if h < 0xc0 { + if buf.readerIndex+2 > buf.writerIndex { + return 0, UnmarshalErr + } + x := ((h & 0x3f) << 8) | uint64(buf.bytes[buf.readerIndex+1]) + buf.readerIndex += 2 + return x, nil + } else if h < 0xe0 { + if buf.readerIndex+3 > buf.writerIndex { + return 0, UnmarshalErr + } + x := ((h & 0x1f) << 16) | (uint64(buf.bytes[buf.readerIndex+1]) << 8) | uint64(buf.bytes[buf.readerIndex+2]) + buf.readerIndex += 3 + return x, nil + } else if h < 0xf0 { + if buf.readerIndex+4 > buf.writerIndex { + return 0, UnmarshalErr + } + x := ((h & 0x1f) << 24) | (uint64(buf.bytes[buf.readerIndex+1]) << 16) | (uint64(buf.bytes[buf.readerIndex+2]) << 8) | + uint64(buf.bytes[buf.readerIndex+3]) + buf.readerIndex += 4 + return x, nil + } else if h < 0xf8 { + if buf.readerIndex+5 > buf.writerIndex { + return 0, UnmarshalErr + } + x := ((h & 0x7) << 32) | (uint64(buf.bytes[buf.readerIndex+1]) << 24) | (uint64(buf.bytes[buf.readerIndex+2]) << 16) | + (uint64(buf.bytes[buf.readerIndex+3]) << 8) | uint64(buf.bytes[buf.readerIndex+4]) + buf.readerIndex += 5 + return x, nil + } else if h < 0xfc { + if buf.readerIndex+6 > buf.writerIndex { + return 0, UnmarshalErr + } + x := ((h & 0x3) << 40) | (uint64(buf.bytes[buf.readerIndex+1]) << 32) | (uint64(buf.bytes[buf.readerIndex+2]) << 24) | + (uint64(buf.bytes[buf.readerIndex+3]) << 16) | (uint64(buf.bytes[buf.readerIndex+4]) << 8) | + (uint64(buf.bytes[buf.readerIndex+5])) + buf.readerIndex += 6 + return x, nil + } else if h < 0xfe { + if buf.readerIndex+7 > buf.writerIndex { + return 0, UnmarshalErr + } + x := ((h & 0x1) << 48) | (uint64(buf.bytes[buf.readerIndex+1]) << 40) | (uint64(buf.bytes[buf.readerIndex+2]) << 32) | + (uint64(buf.bytes[buf.readerIndex+3]) << 24) | (uint64(buf.bytes[buf.readerIndex+4]) << 16) | + (uint64(buf.bytes[buf.readerIndex+5]) << 8) | (uint64(buf.bytes[buf.readerIndex+6])) + buf.readerIndex += 7 + return x, nil + } else if h < 0xff { + if buf.readerIndex+8 > buf.writerIndex { + return 0, UnmarshalErr + } + x := (uint64(buf.bytes[buf.readerIndex+1]) << 48) | (uint64(buf.bytes[buf.readerIndex+2]) << 40) | + (uint64(buf.bytes[buf.readerIndex+3]) << 32) | (uint64(buf.bytes[buf.readerIndex+4]) << 24) | + (uint64(buf.bytes[buf.readerIndex+5]) << 16) | (uint64(buf.bytes[buf.readerIndex+6]) << 8) | + (uint64(buf.bytes[buf.readerIndex+7])) + buf.readerIndex += 8 + return x, nil + } else { + if buf.readerIndex+9 > buf.writerIndex { + return 0, UnmarshalErr + } + x := (uint64(buf.bytes[buf.readerIndex+1]) << 56) | (uint64(buf.bytes[buf.readerIndex+2]) << 48) | + (uint64(buf.bytes[buf.readerIndex+3]) << 40) | (uint64(buf.bytes[buf.readerIndex+4]) << 32) | + (uint64(buf.bytes[buf.readerIndex+5]) << 24) | (uint64(buf.bytes[buf.readerIndex+6]) << 16) | + (uint64(buf.bytes[buf.readerIndex+7]) << 8) | (uint64(buf.bytes[buf.readerIndex+8])) + buf.readerIndex += 9 + return x, nil + } +} + +func (buf *ByteBuf) WriteUlong(x uint64) { + if x < 0x80 { + buf.EnsureWrite(1) + buf.bytes[buf.writerIndex] = byte(x) + buf.writerIndex++ + } else if x < 0x4000 { + buf.EnsureWrite(2) + buf.bytes[buf.writerIndex+1] = byte(x) + buf.bytes[buf.writerIndex] = byte((x >> 8) | 0x80) + buf.writerIndex += 2 + } else if x < 0x200000 { + buf.EnsureWrite(3) + buf.bytes[buf.writerIndex+2] = byte(x) + buf.bytes[buf.writerIndex+1] = byte(x >> 8) + buf.bytes[buf.writerIndex] = byte((x >> 16) | 0xc0) + buf.writerIndex += 3 + } else if x < 0x10000000 { + buf.EnsureWrite(4) + buf.bytes[buf.writerIndex+3] = byte(x) + buf.bytes[buf.writerIndex+2] = byte(x >> 8) + buf.bytes[buf.writerIndex+1] = byte(x >> 16) + buf.bytes[buf.writerIndex] = byte((x >> 24) | 0xe0) + buf.writerIndex += 4 + } else if x < 0x800000000 { + buf.EnsureWrite(5) + buf.bytes[buf.writerIndex+4] = byte(x) + buf.bytes[buf.writerIndex+3] = byte(x >> 8) + buf.bytes[buf.writerIndex+2] = byte(x >> 16) + buf.bytes[buf.writerIndex+1] = byte(x >> 24) + buf.bytes[buf.writerIndex] = byte((x >> 32) | 0xf0) + buf.writerIndex += 5 + } else if x < 0x40000000000 { + buf.EnsureWrite(6) + buf.bytes[buf.writerIndex+5] = byte(x) + buf.bytes[buf.writerIndex+4] = byte(x >> 8) + buf.bytes[buf.writerIndex+3] = byte(x >> 16) + buf.bytes[buf.writerIndex+2] = byte(x >> 24) + buf.bytes[buf.writerIndex+1] = byte(x >> 32) + buf.bytes[buf.writerIndex] = byte((x >> 40) | 0xf8) + buf.writerIndex += 6 + } else if x < 0x200000000000 { + buf.EnsureWrite(7) + buf.bytes[buf.writerIndex+6] = byte(x) + buf.bytes[buf.writerIndex+5] = byte(x >> 8) + buf.bytes[buf.writerIndex+4] = byte(x >> 16) + buf.bytes[buf.writerIndex+3] = byte(x >> 24) + buf.bytes[buf.writerIndex+2] = byte(x >> 32) + buf.bytes[buf.writerIndex+1] = byte(x >> 40) + buf.bytes[buf.writerIndex] = byte((x >> 48) | 0xfc) + buf.writerIndex += 7 + } else if x < 0x100000000000000 { + buf.EnsureWrite(8) + buf.bytes[buf.writerIndex+7] = byte(x) + buf.bytes[buf.writerIndex+6] = byte(x >> 8) + buf.bytes[buf.writerIndex+5] = byte(x >> 16) + buf.bytes[buf.writerIndex+4] = byte(x >> 24) + buf.bytes[buf.writerIndex+3] = byte(x >> 32) + buf.bytes[buf.writerIndex+2] = byte(x >> 40) + buf.bytes[buf.writerIndex+1] = byte(x >> 48) + buf.bytes[buf.writerIndex] = byte((x >> 56) | 0xfe) + buf.writerIndex += 8 + } else { + buf.EnsureWrite(9) + buf.bytes[buf.writerIndex+8] = byte(x) + buf.bytes[buf.writerIndex+7] = byte(x >> 8) + buf.bytes[buf.writerIndex+6] = byte(x >> 16) + buf.bytes[buf.writerIndex+5] = byte(x >> 24) + buf.bytes[buf.writerIndex+4] = byte(x >> 32) + buf.bytes[buf.writerIndex+3] = byte(x >> 40) + buf.bytes[buf.writerIndex+2] = byte(x >> 48) + buf.bytes[buf.writerIndex+1] = byte(x >> 56) + buf.bytes[buf.writerIndex] = 0xff + buf.writerIndex += 9 + } +} + +func (buf *ByteBuf) ReadFlong() (int64, error) { + if buf.readerIndex+8 > buf.writerIndex { + return 0, UnmarshalErr + } + x := (uint64(buf.bytes[buf.readerIndex+7]) << 56) | (uint64(buf.bytes[buf.readerIndex+6]) << 48) | + (uint64(buf.bytes[buf.readerIndex+5]) << 40) | (uint64(buf.bytes[buf.readerIndex+4]) << 32) | + (uint64(buf.bytes[buf.readerIndex+3]) << 24) | (uint64(buf.bytes[buf.readerIndex+2]) << 16) | + (uint64(buf.bytes[buf.readerIndex+1]) << 8) | (uint64(buf.bytes[buf.readerIndex])) + buf.readerIndex += 8 + return int64(x), nil +} + +func (buf *ByteBuf) WriteFlong(x int64) { + buf.EnsureWrite(8) + buf.bytes[buf.writerIndex] = byte(x) + buf.bytes[buf.writerIndex+1] = byte(x >> 8) + buf.bytes[buf.writerIndex+2] = byte(x >> 16) + buf.bytes[buf.writerIndex+3] = byte(x >> 24) + buf.bytes[buf.writerIndex+4] = byte(x >> 32) + buf.bytes[buf.writerIndex+5] = byte(x >> 40) + buf.bytes[buf.writerIndex+6] = byte(x >> 48) + buf.bytes[buf.writerIndex+7] = byte(x >> 56) + buf.writerIndex += 8 +} + +func (buf *ByteBuf) ReadFloat() (float32, error) { + if x, err := buf.ReadFint(); err == nil { + return math.Float32frombits(uint32(x)), nil + } else { + return 0, err + } +} + +func (buf *ByteBuf) WriteFloat(x float32) { + buf.WriteFint(int32(math.Float32bits(x))) +} + +func (buf *ByteBuf) ReadDouble() (float64, error) { + if x, err := buf.ReadFlong(); err == nil { + return math.Float64frombits(uint64(x)), nil + } else { + return 0, err + } +} + +func (buf *ByteBuf) WriteDouble(x float64) { + buf.WriteFlong(int64(math.Float64bits(x))) +} + +func (buf *ByteBuf) ReadSize() (int, error) { + x, err := buf.ReadUint() + return int(x), err +} + +func (buf *ByteBuf) WriteSize(x int) { + buf.WriteUint(uint32(x)) +} + +// marshal int +// n -> (n << 1) ^ (n >> 31) +// Read +// (x >>> 1) ^ ((x << 31) >> 31) +// (x >>> 1) ^ -(n&1) +func (buf *ByteBuf) ReadSint() (int32, error) { + if x, err := buf.ReadUint(); err == nil { + return int32((x >> 1) ^ ((x & 1) << 31)), nil + } else { + return 0, err + } +} + +func (buf *ByteBuf) WriteSint(x int32) { + buf.WriteUint((uint32(x) << 1) ^ (uint32(x) >> 31)) +} + +// marshal long +// n -> (n << 1) ^ (n >> 63) +// Read +// (x >>> 1) ^((x << 63) >> 63) +// (x >>> 1) ^ -(n&1L) +func (buf *ByteBuf) ReadSlong() (int64, error) { + if x, err := buf.ReadUlong(); err == nil { + return int64((x >> 1) ^ ((x & 1) << 63)), nil + } else { + return 0, err + } +} + +func (buf *ByteBuf) WriteSlong(x int64) { + buf.WriteUlong((uint64(x) << 1) ^ (uint64(x) >> 31)) +} + +func (buf *ByteBuf) WriteString(x string) { + bs := []byte(x) + buf.WriteSize(len(bs)) + buf.WriteBytesWithoutSize(bs) +} + +func (buf *ByteBuf) ReadString() (string, error) { + if size, err := buf.ReadSize(); err == nil { + if buf.readerIndex+size > buf.writerIndex { + return "", UnmarshalErr + } + s := string(buf.bytes[buf.readerIndex : buf.readerIndex+size]) + buf.readerIndex += size + return s, nil + } else { + return "", err + } +} + +func (buf *ByteBuf) ReadBytes() ([]byte, error) { + if size, err := buf.ReadSize(); err == nil { + if size == 0 { + return EmptyBytes, nil + } else if buf.readerIndex+size > buf.writerIndex { + return nil, UnmarshalErr + } else { + bs := make([]byte, size) + copy(bs, buf.bytes[buf.readerIndex:buf.readerIndex+size]) + buf.readerIndex += size + return bs, nil + } + } else { + return nil, err + } +} + +func (buf *ByteBuf) WriteBytes(x []byte) { + size := len(x) + buf.WriteSize(size) + if size > 0 { + buf.EnsureWrite(size) + copy(buf.bytes[buf.writerIndex:], x) + buf.writerIndex += size + } +} + +func (buf *ByteBuf) WriteBytesWithSize(x []byte) { + buf.WriteBytes(x) +} + +func (buf *ByteBuf) WriteBytesWithoutSize(x []byte) { + size := len(x) + buf.EnsureWrite(size) + copy(buf.bytes[buf.writerIndex:], x) + buf.writerIndex += size +} + +func (buf *ByteBuf) ReadVector2() (Vector2, error) { + if x, err := buf.ReadFloat(); err == nil { + if y, err2 := buf.ReadFloat(); err2 == nil { + return Vector2{X: x, Y: y}, nil + } + } + return Vector2{}, UnmarshalErr +} + +func (buf *ByteBuf) WriteVector2(x Vector2) { + buf.WriteFloat(x.X) + buf.WriteFloat(x.Y) +} + +func (buf *ByteBuf) ReadVector3() (Vector3, error) { + if x, err := buf.ReadFloat(); err == nil { + if y, err2 := buf.ReadFloat(); err2 == nil { + if z, err3 := buf.ReadFloat(); err3 == nil { + return Vector3{X: x, Y: y, Z: z}, nil + } + } + } + return Vector3{}, UnmarshalErr +} + +func (buf *ByteBuf) WriteVector3(x Vector3) { + buf.WriteFloat(x.X) + buf.WriteFloat(x.Y) + buf.WriteFloat(x.Z) +} + +func (buf *ByteBuf) ReadVector4() (Vector4, error) { + if x, err := buf.ReadFloat(); err == nil { + if y, err2 := buf.ReadFloat(); err2 == nil { + if z, err3 := buf.ReadFloat(); err3 == nil { + if w, err4 := buf.ReadFloat(); err4 == nil { + return Vector4{X: x, Y: y, Z: z, W: w}, nil + } + } + } + } + return Vector4{}, UnmarshalErr +} + +func (buf *ByteBuf) WriteVector4(x Vector4) { + buf.WriteFloat(x.X) + buf.WriteFloat(x.Y) + buf.WriteFloat(x.Z) + buf.WriteFloat(x.W) +} diff --git a/app/eonline/internal/config/bright/serialization/ISerializable.go b/app/eonline/internal/config/bright/serialization/ISerializable.go new file mode 100644 index 0000000..d5a4662 --- /dev/null +++ b/app/eonline/internal/config/bright/serialization/ISerializable.go @@ -0,0 +1,7 @@ +package serialization + +type ISerializable interface { + GetTypeId() int32 + Serialize(buf *ByteBuf) + Deserialize(buf *ByteBuf) error +} diff --git a/app/eonline/internal/config/bright/serialization/Vector2.go b/app/eonline/internal/config/bright/serialization/Vector2.go new file mode 100644 index 0000000..a317f67 --- /dev/null +++ b/app/eonline/internal/config/bright/serialization/Vector2.go @@ -0,0 +1,10 @@ +package serialization + +type Vector2 struct { + X float32 + Y float32 +} + +func NewVector2(x float32, y float32) Vector2 { + return Vector2{X: x, Y: y} +} diff --git a/app/eonline/internal/config/bright/serialization/Vector3.go b/app/eonline/internal/config/bright/serialization/Vector3.go new file mode 100644 index 0000000..d816028 --- /dev/null +++ b/app/eonline/internal/config/bright/serialization/Vector3.go @@ -0,0 +1,11 @@ +package serialization + +type Vector3 struct { + X float32 + Y float32 + Z float32 +} + +func NewVector3(x float32, y float32, z float32) Vector3 { + return Vector3{X: x, Y: y, Z: z} +} diff --git a/app/eonline/internal/config/bright/serialization/Vector4.go b/app/eonline/internal/config/bright/serialization/Vector4.go new file mode 100644 index 0000000..54201fc --- /dev/null +++ b/app/eonline/internal/config/bright/serialization/Vector4.go @@ -0,0 +1,12 @@ +package serialization + +type Vector4 struct { + X float32 + Y float32 + Z float32 + W float32 +} + +func NewVector4(x float32, y float32, z float32, w float32) Vector4 { + return Vector4{X: x, Y: y, Z: z, W: w} +} diff --git a/app/eonline/internal/config/bright/serialization/marshal_test.go b/app/eonline/internal/config/bright/serialization/marshal_test.go new file mode 100644 index 0000000..0a3fd21 --- /dev/null +++ b/app/eonline/internal/config/bright/serialization/marshal_test.go @@ -0,0 +1,205 @@ +package serialization + +import ( + "bytes" + "testing" +) + +func TestMarshal(t *testing.T) { + buf := NewByteBuf(10) + + for i := 0; i < 2; i++ { + x := i != 0 + buf.WriteBool(x) + if v, err := buf.ReadBool(); err != nil || v != x { + t.Fatalf("expect %v, get %v", x, v) + } + if buf.Size() != 0 { + t.Fatalf("expect empty. but size:%v, x:%v", buf.Size(), x) + } + } + + for i := 0; i < 256; i = i*3/2 + 1 { + x := byte(i) + buf.WriteByte(x) + if v, err := buf.ReadByte(); err != nil || v != x { + t.Fatalf("expect %v, get %v", x, v) + } + if buf.Size() != 0 { + t.Fatalf("expect empty. but size:%v, x:%v", buf.Size(), x) + } + } + + for i := 0; i < 0x10000; i = i*3/2 + 1 { + x := int16(i) + buf.WriteShort(x) + if v, err := buf.ReadShort(); err != nil || v != x { + t.Fatalf("expect %v, get %v", x, v) + } + if buf.Size() != 0 { + t.Fatalf("expect empty. but size:%v, x:%v", buf.Size(), x) + } + } + + for i := 0; i < 0x10000; i = i*3/2 + 1 { + x := int16(i) + buf.WriteFshort(x) + if v, err := buf.ReadFshort(); err != nil || v != x { + t.Fatalf("expect %v, get %v", x, v) + } + if buf.Size() != 0 { + t.Fatalf("expect empty. but size:%v, x:%v", buf.Size(), x) + } + } + + for i := 0; i < 0x1000000000; i = i*3/2 + 1 { + x := int32(i) + buf.WriteInt(x) + if v, err := buf.ReadInt(); err != nil || v != x { + t.Fatalf("expect %v, get %v", x, v) + } + if buf.Size() != 0 { + t.Fatalf("expect empty. but size:%v, x:%v", buf.Size(), x) + } + } + + for i := 0; i < 0x1000000000; i = i*3/2 + 1 { + x := int32(i) + buf.WriteFint(x) + if v, err := buf.ReadFint(); err != nil || v != x { + t.Fatalf("expect %v, get %v", x, v) + } + if buf.Size() != 0 { + t.Fatalf("expect empty. but size:%v, x:%v", buf.Size(), x) + } + } + + for i := 0; i < 0x100000000; i = i*3/2 + 1 { + x := int(i) + buf.WriteSize(x) + if v, err := buf.ReadSize(); err != nil || v != x { + t.Fatalf("expect %v, get %v", x, v) + } + if buf.Size() != 0 { + t.Fatalf("expect empty. but size:%v, x:%v", buf.Size(), x) + } + } + + for i := 0; i < 0x100000000; i = i*3/2 + 1 { + x := int32(i) + buf.WriteSint(x) + if v, err := buf.ReadSint(); err != nil || v != x { + t.Fatalf("expect %v, get %v", x, v) + } + if buf.Size() != 0 { + t.Fatalf("expect empty. but size:%v, x:%v", buf.Size(), x) + } + } + + for i := 0; i < 0x1000000000000000; i = i*3/2 + 1 { + x := int64(i) + buf.WriteLong(x) + if v, err := buf.ReadLong(); err != nil || v != x { + t.Fatalf("expect %v, get %v", x, v) + } + if buf.Size() != 0 { + t.Fatalf("expect empty. but size:%v, x:%v", buf.Size(), x) + } + + x = -x + buf.WriteLong(x) + if v, err := buf.ReadLong(); err != nil || v != x { + t.Fatalf("expect %v, get %v", x, v) + } + if buf.Size() != 0 { + t.Fatalf("expect empty. but size:%v, x:%v", buf.Size(), x) + } + } + + for i := 0; i < 0x100000000; i = i*3/2 + 1 { + x := float32(i) + buf.WriteFloat(x) + if v, err := buf.ReadFloat(); err != nil || v != x { + t.Fatalf("expect %v, get %v", x, v) + } + if buf.Size() != 0 { + t.Fatalf("expect empty. but size:%v, x:%v", buf.Size(), x) + } + } + for i := 0; i < 0x100000000; i = i*3/2 + 1 { + x := float64(i) + buf.WriteDouble(x) + if v, err := buf.ReadDouble(); err != nil || v != x { + t.Fatalf("expect %v, get %v", x, v) + } + if buf.Size() != 0 { + t.Fatalf("expect empty. but size:%v, x:%v", buf.Size(), x) + } + } + + { + x := "walon" + buf.WriteString(x) + if v, err := buf.ReadString(); err != nil || v != x { + t.Fatalf("expect %v, get %v", x, v) + } + if buf.Size() != 0 { + t.Fatalf("expect empty. but size:%v, x:%v", buf.Size(), x) + } + } + + { + x := Vector2{X: 1, Y: 2} + buf.WriteVector2(x) + if v, err := buf.ReadVector2(); err != nil || v != x { + t.Fatalf("expect %v, get %v", x, v) + } + if buf.Size() != 0 { + t.Fatalf("expect empty. but size:%v, x:%v", buf.Size(), x) + } + } + + { + x := Vector3{X: 1, Y: 2, Z: 3} + buf.WriteVector3(x) + if v, err := buf.ReadVector3(); err != nil || v != x { + t.Fatalf("expect %v, get %v", x, v) + } + if buf.Size() != 0 { + t.Fatalf("expect empty. but size:%v, x:%v", buf.Size(), x) + } + } + + { + x := Vector4{X: 1, Y: 2, Z: 3, W: 4} + buf.WriteVector4(x) + if v, err := buf.ReadVector4(); err != nil || v != x { + t.Fatalf("expect %v, get %v", x, v) + } + if buf.Size() != 0 { + t.Fatalf("expect empty. but size:%v, x:%v", buf.Size(), x) + } + } + + { + x := []byte{1, 2, 3} + buf.WriteBytes(x) + if v, err := buf.ReadBytes(); err != nil || !bytes.Equal(x, v) { + t.Fatalf("expect %v, get %v", x, v) + } + if buf.Size() != 0 { + t.Fatalf("expect empty. but size:%v, x:%v", buf.Size(), x) + } + } + { + x := []byte{1, 2, 3, 4} + buf.WriteBytesWithoutSize(x) + if v, err := buf.ReadFint(); err != nil || v != 0x04030201 { + t.Fatalf("expect %v, get %v", x, v) + } + if buf.Size() != 0 { + t.Fatalf("expect empty. but size:%v, x:%v", buf.Size(), x) + } + } + +} diff --git a/app/eonline/internal/config/config.go b/app/eonline/internal/config/config.go new file mode 100644 index 0000000..e5b7248 --- /dev/null +++ b/app/eonline/internal/config/config.go @@ -0,0 +1,114 @@ +package config + +import ( + "fmt" + "io/ioutil" + "strconv" + "strings" + + "sandc/app/eonline/internal/config/bright/serialization" + Config "sandc/app/eonline/internal/config/config" + + "github.com/go-kratos/kratos/v2/log" +) + +var ( + // 处理的特别数据 + PublicCheckNum uint32 // 最大验证数量,超过这个数量,新的用户将不再出现信息验证入口 + PublicCheckCoin int32 // 提交身份审核,提现奖励的钱/美分 + PublicVersionMin uint32 + + // 读表 + g_tables *Config.Tables // bin读表数据 + g_path *string +) + +func ConfigInit(path, env, ver_check *string) { + log.Info("读取cvs配置文件开始") + + g_path = path + + loadConfig(ver_check) + + checkConfig(env) + + log.Info("读取cvs配置文件结束") +} + +func loadConfig(ver_check *string) { + readBin(ver_check) +} + +func loader(file string) (*serialization.ByteBuf, error) { + if bytes, err := ioutil.ReadFile(*g_path + file + ".bytes"); err != nil { + return nil, err + } else { + return serialization.WrapByteBuf(bytes), nil + } +} + +func readBin(ver_check *string) { + var err error + g_tables, err = Config.NewTables(loader) + if err != nil { + log.Fatal("readBin error: %s", err.Error()) + } else { + log.Info("readBin success") + } + + log.Debugf("%+v", g_tables.Tbglobal.Get(100000)) + + // 处理的特别数据 + PublicCheckNum = 5000 + PublicCheckCoin = 500 + + PublicVersionMin = Version2int(ver_check) +} + +// 检查配置 +func checkConfig(env *string) { + if *env != "qa" { + return + } + + { + var item *Config.ConfiguserState + lst := g_tables.TbuserState.GetDataList() + for i := 0; i < len(lst); i++ { + item = lst[i] + + if item.State < 0 || item.State > 1 { + log.Infof("checkConfig error: userState table id[%d] State[%d] < 0 || > 1", item.Id, item.State) + } + } + } + + log.Info("checkConfig结束") +} + +// 将二段、三段式版本号字符串,如“1.0”、“1.0.1”转换成6位数字 +func Version2int(ver *string) uint32 { + strs := strings.Split(*ver, ".") + + var err error + var b, idx int + var result, str string + for idx, str = range strs { + b, err = strconv.Atoi(str) + if err == nil { + result += fmt.Sprintf("%02d", b) + } + } + + b, _ = strconv.Atoi(result) + + for ; idx < 2; idx++ { + b *= 100 + } + + return uint32(b) +} + +func GetUserStateData() []*Config.ConfiguserState { + return g_tables.TbuserState.GetDataList() +} diff --git a/app/eonline/internal/config/config/Tables.go b/app/eonline/internal/config/config/Tables.go new file mode 100644 index 0000000..e91f19f --- /dev/null +++ b/app/eonline/internal/config/config/Tables.go @@ -0,0 +1,59 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +package Config + +import "sandc/app/eonline/internal/config/bright/serialization" + +type ByteBufLoader func(string) (*serialization.ByteBuf, error) + +type Tables struct { + TbCoinAdReward *ConfigTbCoinAdReward + TbMinigame *ConfigTbMinigame + Tbglobal *ConfigTbglobal + TbCashOut *ConfigTbCashOut + TbuserState *ConfigTbuserState +} + +func NewTables(loader ByteBufLoader) (*Tables, error) { + var err error + var buf *serialization.ByteBuf + + tables := &Tables{} + if buf, err = loader("config_tbcoinadreward"); err != nil { + return nil, err + } + if tables.TbCoinAdReward, err = NewConfigTbCoinAdReward(buf); err != nil { + return nil, err + } + if buf, err = loader("config_tbminigame"); err != nil { + return nil, err + } + if tables.TbMinigame, err = NewConfigTbMinigame(buf); err != nil { + return nil, err + } + if buf, err = loader("config_tbglobal"); err != nil { + return nil, err + } + if tables.Tbglobal, err = NewConfigTbglobal(buf); err != nil { + return nil, err + } + if buf, err = loader("config_tbcashout"); err != nil { + return nil, err + } + if tables.TbCashOut, err = NewConfigTbCashOut(buf); err != nil { + return nil, err + } + if buf, err = loader("config_tbuserstate"); err != nil { + return nil, err + } + if tables.TbuserState, err = NewConfigTbuserState(buf); err != nil { + return nil, err + } + return tables, nil +} diff --git a/app/eonline/internal/config/config/config.CashOut.go b/app/eonline/internal/config/config/config.CashOut.go new file mode 100644 index 0000000..3dc58a6 --- /dev/null +++ b/app/eonline/internal/config/config/config.CashOut.go @@ -0,0 +1,76 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +package Config + +import ( + "sandc/app/eonline/internal/config/bright/serialization" +) + +import "errors" + +type ConfigCashOut struct { + ID int32 + Type int32 + LimitTime int32 + Price float32 + PriceShow float32 +} + +const TypeId_ConfigCashOut = 1836307535 + +func (*ConfigCashOut) GetTypeId() int32 { + return 1836307535 +} + +func (_v *ConfigCashOut) Serialize(_buf *serialization.ByteBuf) { + // not support +} + +func (_v *ConfigCashOut) Deserialize(_buf *serialization.ByteBuf) (err error) { + { + if _v.ID, err = _buf.ReadInt(); err != nil { + err = errors.New("_v.ID error") + return + } + } + { + if _v.Type, err = _buf.ReadInt(); err != nil { + err = errors.New("_v.Type error") + return + } + } + { + if _v.LimitTime, err = _buf.ReadInt(); err != nil { + err = errors.New("_v.LimitTime error") + return + } + } + { + if _v.Price, err = _buf.ReadFloat(); err != nil { + err = errors.New("_v.Price error") + return + } + } + { + if _v.PriceShow, err = _buf.ReadFloat(); err != nil { + err = errors.New("_v.PriceShow error") + return + } + } + return +} + +func DeserializeConfigCashOut(_buf *serialization.ByteBuf) (*ConfigCashOut, error) { + v := &ConfigCashOut{} + if err := v.Deserialize(_buf); err == nil { + return v, nil + } else { + return nil, err + } +} diff --git a/app/eonline/internal/config/config/config.CoinAdReward.go b/app/eonline/internal/config/config/config.CoinAdReward.go new file mode 100644 index 0000000..b27b6cb --- /dev/null +++ b/app/eonline/internal/config/config/config.CoinAdReward.go @@ -0,0 +1,76 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +package Config + +import ( + "sandc/app/eonline/internal/config/bright/serialization" +) + +import "errors" + +type ConfigCoinAdReward struct { + ID int32 + Starttimes int32 + Endtimes int32 + SaveCoinMin int32 + SaveCoinMax int32 +} + +const TypeId_ConfigCoinAdReward = -100324817 + +func (*ConfigCoinAdReward) GetTypeId() int32 { + return -100324817 +} + +func (_v *ConfigCoinAdReward) Serialize(_buf *serialization.ByteBuf) { + // not support +} + +func (_v *ConfigCoinAdReward) Deserialize(_buf *serialization.ByteBuf) (err error) { + { + if _v.ID, err = _buf.ReadInt(); err != nil { + err = errors.New("_v.ID error") + return + } + } + { + if _v.Starttimes, err = _buf.ReadInt(); err != nil { + err = errors.New("_v.Starttimes error") + return + } + } + { + if _v.Endtimes, err = _buf.ReadInt(); err != nil { + err = errors.New("_v.Endtimes error") + return + } + } + { + if _v.SaveCoinMin, err = _buf.ReadInt(); err != nil { + err = errors.New("_v.SaveCoinMin error") + return + } + } + { + if _v.SaveCoinMax, err = _buf.ReadInt(); err != nil { + err = errors.New("_v.SaveCoinMax error") + return + } + } + return +} + +func DeserializeConfigCoinAdReward(_buf *serialization.ByteBuf) (*ConfigCoinAdReward, error) { + v := &ConfigCoinAdReward{} + if err := v.Deserialize(_buf); err == nil { + return v, nil + } else { + return nil, err + } +} diff --git a/app/eonline/internal/config/config/config.Minigame.go b/app/eonline/internal/config/config/config.Minigame.go new file mode 100644 index 0000000..a0141e3 --- /dev/null +++ b/app/eonline/internal/config/config/config.Minigame.go @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +package Config + +import ( + "sandc/app/eonline/internal/config/bright/serialization" +) + +import "errors" + +type ConfigMinigame struct { + ID int32 + Type int32 + Level int32 + SaveCoinMin int32 + SaveCoinMax int32 + Double int32 + Weights int32 +} + +const TypeId_ConfigMinigame = -292238827 + +func (*ConfigMinigame) GetTypeId() int32 { + return -292238827 +} + +func (_v *ConfigMinigame) Serialize(_buf *serialization.ByteBuf) { + // not support +} + +func (_v *ConfigMinigame) Deserialize(_buf *serialization.ByteBuf) (err error) { + { + if _v.ID, err = _buf.ReadInt(); err != nil { + err = errors.New("_v.ID error") + return + } + } + { + if _v.Type, err = _buf.ReadInt(); err != nil { + err = errors.New("_v.Type error") + return + } + } + { + if _v.Level, err = _buf.ReadInt(); err != nil { + err = errors.New("_v.Level error") + return + } + } + { + if _v.SaveCoinMin, err = _buf.ReadInt(); err != nil { + err = errors.New("_v.SaveCoinMin error") + return + } + } + { + if _v.SaveCoinMax, err = _buf.ReadInt(); err != nil { + err = errors.New("_v.SaveCoinMax error") + return + } + } + { + if _v.Double, err = _buf.ReadInt(); err != nil { + err = errors.New("_v.Double error") + return + } + } + { + if _v.Weights, err = _buf.ReadInt(); err != nil { + err = errors.New("_v.Weights error") + return + } + } + return +} + +func DeserializeConfigMinigame(_buf *serialization.ByteBuf) (*ConfigMinigame, error) { + v := &ConfigMinigame{} + if err := v.Deserialize(_buf); err == nil { + return v, nil + } else { + return nil, err + } +} diff --git a/app/eonline/internal/config/config/config.TbCashOut.go b/app/eonline/internal/config/config/config.TbCashOut.go new file mode 100644 index 0000000..58a3075 --- /dev/null +++ b/app/eonline/internal/config/config/config.TbCashOut.go @@ -0,0 +1,47 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +package Config + +import "sandc/app/eonline/internal/config/bright/serialization" + +type ConfigTbCashOut struct { + _dataMap map[int32]*ConfigCashOut + _dataList []*ConfigCashOut +} + +func NewConfigTbCashOut(_buf *serialization.ByteBuf) (*ConfigTbCashOut, error) { + if size, err := _buf.ReadSize(); err != nil { + return nil, err + } else { + _dataList := make([]*ConfigCashOut, 0, size) + dataMap := make(map[int32]*ConfigCashOut) + + for i := 0; i < size; i++ { + if _v, err2 := DeserializeConfigCashOut(_buf); err2 != nil { + return nil, err2 + } else { + _dataList = append(_dataList, _v) + dataMap[_v.ID] = _v + } + } + return &ConfigTbCashOut{_dataList: _dataList, _dataMap: dataMap}, nil + } +} + +func (table *ConfigTbCashOut) GetDataMap() map[int32]*ConfigCashOut { + return table._dataMap +} + +func (table *ConfigTbCashOut) GetDataList() []*ConfigCashOut { + return table._dataList +} + +func (table *ConfigTbCashOut) Get(key int32) *ConfigCashOut { + return table._dataMap[key] +} diff --git a/app/eonline/internal/config/config/config.TbCoinAdReward.go b/app/eonline/internal/config/config/config.TbCoinAdReward.go new file mode 100644 index 0000000..78052d5 --- /dev/null +++ b/app/eonline/internal/config/config/config.TbCoinAdReward.go @@ -0,0 +1,47 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +package Config + +import "sandc/app/eonline/internal/config/bright/serialization" + +type ConfigTbCoinAdReward struct { + _dataMap map[int32]*ConfigCoinAdReward + _dataList []*ConfigCoinAdReward +} + +func NewConfigTbCoinAdReward(_buf *serialization.ByteBuf) (*ConfigTbCoinAdReward, error) { + if size, err := _buf.ReadSize(); err != nil { + return nil, err + } else { + _dataList := make([]*ConfigCoinAdReward, 0, size) + dataMap := make(map[int32]*ConfigCoinAdReward) + + for i := 0; i < size; i++ { + if _v, err2 := DeserializeConfigCoinAdReward(_buf); err2 != nil { + return nil, err2 + } else { + _dataList = append(_dataList, _v) + dataMap[_v.ID] = _v + } + } + return &ConfigTbCoinAdReward{_dataList: _dataList, _dataMap: dataMap}, nil + } +} + +func (table *ConfigTbCoinAdReward) GetDataMap() map[int32]*ConfigCoinAdReward { + return table._dataMap +} + +func (table *ConfigTbCoinAdReward) GetDataList() []*ConfigCoinAdReward { + return table._dataList +} + +func (table *ConfigTbCoinAdReward) Get(key int32) *ConfigCoinAdReward { + return table._dataMap[key] +} diff --git a/app/eonline/internal/config/config/config.TbMinigame.go b/app/eonline/internal/config/config/config.TbMinigame.go new file mode 100644 index 0000000..8e5e402 --- /dev/null +++ b/app/eonline/internal/config/config/config.TbMinigame.go @@ -0,0 +1,47 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +package Config + +import "sandc/app/eonline/internal/config/bright/serialization" + +type ConfigTbMinigame struct { + _dataMap map[int32]*ConfigMinigame + _dataList []*ConfigMinigame +} + +func NewConfigTbMinigame(_buf *serialization.ByteBuf) (*ConfigTbMinigame, error) { + if size, err := _buf.ReadSize(); err != nil { + return nil, err + } else { + _dataList := make([]*ConfigMinigame, 0, size) + dataMap := make(map[int32]*ConfigMinigame) + + for i := 0; i < size; i++ { + if _v, err2 := DeserializeConfigMinigame(_buf); err2 != nil { + return nil, err2 + } else { + _dataList = append(_dataList, _v) + dataMap[_v.ID] = _v + } + } + return &ConfigTbMinigame{_dataList: _dataList, _dataMap: dataMap}, nil + } +} + +func (table *ConfigTbMinigame) GetDataMap() map[int32]*ConfigMinigame { + return table._dataMap +} + +func (table *ConfigTbMinigame) GetDataList() []*ConfigMinigame { + return table._dataList +} + +func (table *ConfigTbMinigame) Get(key int32) *ConfigMinigame { + return table._dataMap[key] +} diff --git a/app/eonline/internal/config/config/config.Tbglobal.go b/app/eonline/internal/config/config/config.Tbglobal.go new file mode 100644 index 0000000..26c96c1 --- /dev/null +++ b/app/eonline/internal/config/config/config.Tbglobal.go @@ -0,0 +1,47 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +package Config + +import "sandc/app/eonline/internal/config/bright/serialization" + +type ConfigTbglobal struct { + _dataMap map[int32]*Configglobal + _dataList []*Configglobal +} + +func NewConfigTbglobal(_buf *serialization.ByteBuf) (*ConfigTbglobal, error) { + if size, err := _buf.ReadSize(); err != nil { + return nil, err + } else { + _dataList := make([]*Configglobal, 0, size) + dataMap := make(map[int32]*Configglobal) + + for i := 0; i < size; i++ { + if _v, err2 := DeserializeConfigglobal(_buf); err2 != nil { + return nil, err2 + } else { + _dataList = append(_dataList, _v) + dataMap[_v.ID] = _v + } + } + return &ConfigTbglobal{_dataList: _dataList, _dataMap: dataMap}, nil + } +} + +func (table *ConfigTbglobal) GetDataMap() map[int32]*Configglobal { + return table._dataMap +} + +func (table *ConfigTbglobal) GetDataList() []*Configglobal { + return table._dataList +} + +func (table *ConfigTbglobal) Get(key int32) *Configglobal { + return table._dataMap[key] +} diff --git a/app/eonline/internal/config/config/config.TbuserState.go b/app/eonline/internal/config/config/config.TbuserState.go new file mode 100644 index 0000000..0f5099d --- /dev/null +++ b/app/eonline/internal/config/config/config.TbuserState.go @@ -0,0 +1,47 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +package Config + +import "sandc/app/eonline/internal/config/bright/serialization" + +type ConfigTbuserState struct { + _dataMap map[int32]*ConfiguserState + _dataList []*ConfiguserState +} + +func NewConfigTbuserState(_buf *serialization.ByteBuf) (*ConfigTbuserState, error) { + if size, err := _buf.ReadSize(); err != nil { + return nil, err + } else { + _dataList := make([]*ConfiguserState, 0, size) + dataMap := make(map[int32]*ConfiguserState) + + for i := 0; i < size; i++ { + if _v, err2 := DeserializeConfiguserState(_buf); err2 != nil { + return nil, err2 + } else { + _dataList = append(_dataList, _v) + dataMap[_v.Id] = _v + } + } + return &ConfigTbuserState{_dataList: _dataList, _dataMap: dataMap}, nil + } +} + +func (table *ConfigTbuserState) GetDataMap() map[int32]*ConfiguserState { + return table._dataMap +} + +func (table *ConfigTbuserState) GetDataList() []*ConfiguserState { + return table._dataList +} + +func (table *ConfigTbuserState) Get(key int32) *ConfiguserState { + return table._dataMap[key] +} diff --git a/app/eonline/internal/config/config/config.global.go b/app/eonline/internal/config/config/config.global.go new file mode 100644 index 0000000..35b13f5 --- /dev/null +++ b/app/eonline/internal/config/config/config.global.go @@ -0,0 +1,69 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +package Config + +import ( + "sandc/app/eonline/internal/config/bright/serialization" +) + +import "errors" + +type Configglobal struct { + ID int32 + Name string + Num string + Type string +} + +const TypeId_Configglobal = -1255385137 + +func (*Configglobal) GetTypeId() int32 { + return -1255385137 +} + +func (_v *Configglobal) Serialize(_buf *serialization.ByteBuf) { + // not support +} + +func (_v *Configglobal) Deserialize(_buf *serialization.ByteBuf) (err error) { + { + if _v.ID, err = _buf.ReadInt(); err != nil { + err = errors.New("_v.ID error") + return + } + } + { + if _v.Name, err = _buf.ReadString(); err != nil { + err = errors.New("_v.Name error") + return + } + } + { + if _v.Num, err = _buf.ReadString(); err != nil { + err = errors.New("_v.Num error") + return + } + } + { + if _v.Type, err = _buf.ReadString(); err != nil { + err = errors.New("_v.Type error") + return + } + } + return +} + +func DeserializeConfigglobal(_buf *serialization.ByteBuf) (*Configglobal, error) { + v := &Configglobal{} + if err := v.Deserialize(_buf); err == nil { + return v, nil + } else { + return nil, err + } +} diff --git a/app/eonline/internal/config/config/config.userState.go b/app/eonline/internal/config/config/config.userState.go new file mode 100644 index 0000000..33e0194 --- /dev/null +++ b/app/eonline/internal/config/config/config.userState.go @@ -0,0 +1,69 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +package Config + +import ( + "sandc/app/eonline/internal/config/bright/serialization" +) + +import "errors" + +type ConfiguserState struct { + Id int32 + Paypal string + State int32 + Couse string +} + +const TypeId_ConfiguserState = 1337179098 + +func (*ConfiguserState) GetTypeId() int32 { + return 1337179098 +} + +func (_v *ConfiguserState) Serialize(_buf *serialization.ByteBuf) { + // not support +} + +func (_v *ConfiguserState) Deserialize(_buf *serialization.ByteBuf) (err error) { + { + if _v.Id, err = _buf.ReadInt(); err != nil { + err = errors.New("_v.Id error") + return + } + } + { + if _v.Paypal, err = _buf.ReadString(); err != nil { + err = errors.New("_v.Paypal error") + return + } + } + { + if _v.State, err = _buf.ReadInt(); err != nil { + err = errors.New("_v.State error") + return + } + } + { + if _v.Couse, err = _buf.ReadString(); err != nil { + err = errors.New("_v.Couse error") + return + } + } + return +} + +func DeserializeConfiguserState(_buf *serialization.ByteBuf) (*ConfiguserState, error) { + v := &ConfiguserState{} + if err := v.Deserialize(_buf); err == nil { + return v, nil + } else { + return nil, err + } +} diff --git a/app/eonline/internal/data/README.md b/app/eonline/internal/data/README.md new file mode 100644 index 0000000..599c41b --- /dev/null +++ b/app/eonline/internal/data/README.md @@ -0,0 +1 @@ +# Data diff --git a/app/eonline/internal/data/data.go b/app/eonline/internal/data/data.go new file mode 100644 index 0000000..b0069b1 --- /dev/null +++ b/app/eonline/internal/data/data.go @@ -0,0 +1,262 @@ +package data + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "sandc/app/eonline/internal/biz" + "sandc/app/eonline/internal/conf" + "sandc/pkg/utils/snowflake" + + "github.com/go-kratos/kratos/v2/log" + "github.com/go-kratos/kratos/v2/middleware/recovery" + "github.com/go-kratos/kratos/v2/middleware/tracing" + "github.com/go-kratos/kratos/v2/registry" + "github.com/go-kratos/kratos/v2/transport/grpc" + "github.com/go-redis/redis/extra/redisotel/v8" + "github.com/go-redis/redis/v8" + "github.com/google/wire" + "github.com/hibiken/asynq" + "github.com/tx7do/kratos-transport/broker" + "github.com/tx7do/kratos-transport/broker/kafka" + "github.com/uptrace/opentelemetry-go-extra/otelgorm" + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + ggrpc "google.golang.org/grpc" + driver "gorm.io/driver/mysql" + "gorm.io/gorm" +) + +// ProviderSet is data providers. +var ProviderSet = wire.NewSet( + NewData, + NewTransaction, + NewEonlineRepo, + NewAsynqClient, + NewCache, +) + +// Data . +type Data struct { + log *log.Helper + redis *redis.Client + db *gorm.DB + snowflake *snowflake.Node + asynqCli *asynq.Client + // TODO just for example, don't use dispatch self in production + // ec v1.EonlineClient +} + +type contextTxKey struct{} + +func (d *Data) InTx(ctx context.Context, fn func(ctx context.Context) error) error { + return d.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + ctx = context.WithValue(ctx, contextTxKey{}, tx) + return fn(ctx) + }) +} + +func (d *Data) DB(ctx context.Context) *gorm.DB { + tx, ok := ctx.Value(contextTxKey{}).(*gorm.DB) + if ok { + return tx.Debug() + } + return d.db.Debug() +} + +// NewAsynqTask new asynq task +func (d *Data) NewAsynqTask(taskName string, payload []byte) error { + _, err := d.asynqCli.Enqueue(asynq.NewTask(taskName, payload)) + return err +} + +// NewTransaction . +func NewTransaction(d *Data) biz.Transaction { + return d +} + +// NewData . +func NewData(c *conf.Data, bootstrap *conf.Bootstrap, asynqCli *asynq.Client, logger log.Logger) (*Data, func(), error) { + db := newDB(c.Database.Source) + rdb := newRedis(c.Redis) + cleanup := func() { + biz.SaveSvrDataShutDown() + biz.CloseReport() + + log.NewHelper(logger).Info("closing the data resources") + _ = rdb.Close() + sqlDB, _ := db.DB() + _ = sqlDB.Close() + } + data := &Data{ + log: log.NewHelper(logger), + redis: rdb, + db: db, + snowflake: snowflake.GetSnowflakeNode(rdb), + asynqCli: asynqCli, + } + + biz.InitSvrData(bootstrap) + // biz.InitReport(bootstrap) + + return data, cleanup, nil +} + +func InitData(bootstrap *conf.Bootstrap) { + biz.InitReport(bootstrap) +} + +func newRedis(c *conf.Data_Redis) *redis.Client { + rdb := redis.NewClient(&redis.Options{ + Network: c.Network, + Addr: c.Addr, + Password: c.Password, + DB: int(c.Db), + PoolSize: int(c.Pool), + }) + rdb.AddHook(redisotel.NewTracingHook(redisotel.WithAttributes(semconv.NetPeerNameKey.String(c.Addr)))) + return rdb +} + +func newDB(dsn string) *gorm.DB { + if dsn == "" { + dsn = "root:@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=UTC" + } + + db, err := gorm.Open(driver.Open(dsn), &gorm.Config{}) + if err != nil { + log.Fatal("connection database failed: ", err) + } + _ = db.Use(otelgorm.NewPlugin(otelgorm.WithoutQueryVariables())) + return db +} + +// NewCache . +func NewCache(d *Data) biz.Cache { + return d +} + +func (r *Data) Remember(ctx context.Context, key string, second int32, fn func(ctx context.Context) (interface{}, error)) ([]byte, error) { + str, err := r.GetValue(ctx, key) + if err != nil { + value, err := fn(ctx) + if err != nil { + return nil, err + } + + mByte, err := json.Marshal(value) + if err != nil { + return nil, fmt.Errorf("json Marshal cache failed, err: %w ", err) + } + + return mByte, nil + } + + if str == "" { + value, err := fn(ctx) + if err != nil { + return nil, err + } + + var mByte []byte + if str, ok := value.(string); ok { + mByte = []byte(str) + } else { + mByte, err = json.Marshal(value) + } + + if err != nil { + return nil, fmt.Errorf("json Marshal cache null, err: %w ", err) + } + r.WriteValue(ctx, key, mByte, second) + + return mByte, nil + } + + return []byte(str), nil +} + +func (r *Data) GetValue(ctx context.Context, key string) (string, error) { + v, err := r.redis.Get(ctx, key).Result() + if err == redis.Nil { + return v, nil + } else if err != nil { + return v, err + } else { + return v, nil + } +} + +func (r *Data) WriteValue(ctx context.Context, key string, value interface{}, timeout int32) error { + return r.redis.Set(ctx, key, value, time.Duration(timeout)*time.Second).Err() +} + +func (r *Data) DelValue(ctx context.Context, keys ...string) error { + return r.redis.Del(ctx, keys...).Err() +} + +func (r *Data) RedisLock(ctx context.Context, key string, value interface{}, timeout int32) (bool, error) { + return r.redis.SetNX(ctx, key, value, time.Duration(timeout)*time.Second).Result() +} + +func (r *Data) RedisUnLock(ctx context.Context, key string) (bool, error) { + _, err := r.redis.Del(ctx, key).Result() + if err != nil { + return false, err + } + return true, nil +} + +func (r *Data) IncrValue(ctx context.Context, key string) error { + return r.redis.Incr(ctx, key).Err() +} + +// NewEonlineClient new eonline rpc client +// func NewEonlineClient(r registry.Discovery) v1.EonlineClient { +// return NewRPCClient[v1.EonlineClient](r, "eonline.rpc", v1.NewEonlineClient) +// } + +// NewRPCClient new one rpc client by discovery. +func NewRPCClient[T any](r registry.Discovery, rpcName string, fn func(cc ggrpc.ClientConnInterface) T) T { + conn, err := grpc.DialInsecure( + context.Background(), + grpc.WithEndpoint("discovery:///"+rpcName), + grpc.WithDiscovery(r), + grpc.WithMiddleware( + tracing.Client(), + recovery.Recovery(), + ), + ) + if err != nil { + log.Fatal(err) + } + return fn(conn) +} + +// NewKafkaBroker new kafka broker. +func NewKafkaBroker(confQueue *conf.Queue) broker.Broker { + opts := []broker.Option{ + broker.WithAddress(confQueue.Kafka.Addrs...), + broker.WithCodec("json"), + broker.WithGlobalTracerProvider(), + } + if confQueue.Kafka.Username != "" && confQueue.Kafka.Password != "" { + opts = append(opts, kafka.WithPlainMechanism(confQueue.Kafka.Username, confQueue.Kafka.Password)) + } + kafkaBroker := kafka.NewBroker(opts...) + _ = kafkaBroker.Init() + return kafkaBroker +} + +// NewAsynqClient new asynq client. +func NewAsynqClient(confQueue *conf.Queue) *asynq.Client { + return asynq.NewClient(asynq.RedisClientOpt{ + Addr: confQueue.Asynq.Addr, + DB: int(confQueue.Asynq.Db), + Password: confQueue.Asynq.Password, + ReadTimeout: confQueue.Asynq.ReadTimeout.AsDuration(), + WriteTimeout: confQueue.Asynq.WriteTimeout.AsDuration(), + PoolSize: int(confQueue.Asynq.Pool), + }) +} diff --git a/app/eonline/internal/data/eonline.go b/app/eonline/internal/data/eonline.go new file mode 100644 index 0000000..4be5974 --- /dev/null +++ b/app/eonline/internal/data/eonline.go @@ -0,0 +1,31 @@ +package data + +import ( + "context" + "sandc/app/eonline/internal/biz" + + "github.com/go-kratos/kratos/v2/log" +) + +var _ biz.EonlineRepo = (*eonlineRepo)(nil) + +type eonlineRepo struct { + data *Data + log *log.Helper +} + +// NewEonlineRepo . +func NewEonlineRepo(data *Data, logger log.Logger) biz.EonlineRepo { + return &eonlineRepo{ + data: data, + log: log.NewHelper(logger), + } +} + +func (r *eonlineRepo) CreateEonline(ctx context.Context, g *biz.Eonline) error { + return nil +} + +func (r *eonlineRepo) UpdateEonline(ctx context.Context, g *biz.Eonline) error { + return nil +} diff --git a/app/eonline/internal/data/payout.go b/app/eonline/internal/data/payout.go new file mode 100644 index 0000000..06d8474 --- /dev/null +++ b/app/eonline/internal/data/payout.go @@ -0,0 +1,386 @@ +package data + +import ( + "context" + "fmt" + "time" + + "sandc/app/eonline/internal/biz" + "sandc/pkg/marshal" + "sandc/pkg/utils/stime" + + "gorm.io/gorm" +) + +// PayoutUser 提现用户信息 +type PayoutUser struct { + Id uint `gorm:"column:id" db:"id" json:"id" form:"id"` // 用户ID + Platform string `gorm:"column:platform" db:"platform" json:"platform" form:"platform"` // 平台 + Ip string `gorm:"column:ip" db:"ip" json:"ip" form:"ip"` // ip地址 + Country string `gorm:"column:country" db:"country" json:"country" form:"country"` // 国家 + DeviceId string `gorm:"column:device_id" db:"device_id" json:"device_id" form:"device_id"` // 设备ID + Version string `gorm:"column:version" db:"version" json:"version" form:"version"` // 版本号 + Uuid string `gorm:"column:uuid" db:"uuid" json:"uuid" form:"uuid"` // 用户唯一ID,根据ip, country,device_id生成 + LoginDays uint `gorm:"column:login_days" db:"login_days" json:"login_days" form:"login_days"` // 登录天数 + CreatedAt time.Time `gorm:"column:created_at" db:"created_at" json:"created_at" form:"created_at"` // 创建时间 + UpdatedAt time.Time `gorm:"column:updated_at" db:"updated_at" json:"updated_at" form:"updated_at"` // 更新时间 + ClientData string `gorm:"column:clientData" db:"clientData" json:"clientData" form:"clientData"` // 客户端数据 +} + +// TableName sets the insert table name for this struct type +func (p *PayoutUser) TableName() string { + return "payout_user" +} + +// 提现记录 +type PayoutRecord struct { + Id uint `gorm:"column:id" db:"id" json:"id" form:"id"` // 提现记录ID + PayoutId string `gorm:"column:payout_id" db:"payout_id" json:"payout_id" form:"payout_id"` // 支付第三方id + RecordNo string `gorm:"column:record_no" db:"record_no" json:"record_no" form:"record_no"` // 提现唯一编码 + Channel string `gorm:"column:channel" db:"channel" json:"channel" form:"channel"` // 支付渠道 + Uuid string `gorm:"column:uuid" db:"uuid" json:"uuid" form:"uuid"` // 提现用户唯一编码 + Account string `gorm:"column:account" db:"account" json:"account" form:"account"` // 提现账号 + ItemId uint `gorm:"column:item_id" db:"item_id" json:"item_id" form:"item_id"` // 提现的item对应id + Amount float64 `gorm:"column:amount" db:"amount" json:"amount" form:"amount"` // 提现金额 + Currency string `gorm:"column:currency" db:"currency" json:"currency" form:"currency"` // 货币单位 + Status uint8 `gorm:"column:status" db:"status" json:"status" form:"status"` // 提现状态 1:提现中,2:提现成功,3:提现失败 + Ecpm uint8 `gorm:"column:ecpm" db:"ecpm" json:"ecpm" form:"ecpm"` // ecpm等级 + Version string `gorm:"column:version" db:"version" json:"version" form:"version"` // 版本号 + CreatedAt time.Time `gorm:"column:created_at" db:"created_at" json:"created_at" form:"created_at"` // 创建时间 + UpdatedAt time.Time `gorm:"column:updated_at" db:"updated_at" json:"updated_at" form:"updated_at"` // 更新时间 + Fail string `gorm:"column:fail" db:"fail" json:"fail" form:"fail"` // 提现失败原因 + Email string `gorm:"column:email" db:"email" json:"email" form:"email"` // 邮箱 +} + +// TableName sets the insert table name for this struct type +func (p *PayoutRecord) TableName() string { + return "payout_record" +} + +// 分红记录 +type BonusRecord struct { + Id uint `gorm:"column:id" db:"id" json:"id" form:"id"` // 年月日时间ID + Dau uint `gorm:"column:dau" db:"dau" json:"dau" form:"dau"` // 原始dau + Pass uint `gorm:"column:pass" db:"pass" json:"pass" form:"pass"` // 通关总份额 + Coin uint `gorm:"column:coin" db:"coin" json:"coin" form:"coin"` // 分红每一份额美元数/万分比 + Bonus uint `gorm:"column:bonus" db:"bonus" json:"bonus" form:"bonus"` // 服务器分红总额美分 + BonusCur uint `gorm:"column:bonus_cur" db:"bonus_cur" json:"bonus_cur" form:"bonus_cur"` // 服务器实际分红总额美分 + CreatedAt time.Time `gorm:"column:created_at" db:"created_at" json:"created_at" form:"created_at"` // 创建时间 + UpdatedAt time.Time `gorm:"column:updated_at" db:"updated_at" json:"updated_at" form:"updated_at"` // 更新时间 +} + +// TableName sets the insert table name for this struct type +func (p *BonusRecord) TableName() string { + return "bonus_record" +} + +// SearchPayoutUser 查询提现用户信息 +func (r *eonlineRepo) SearchPayoutUser(ctx context.Context, opts *biz.SearchPayoutUserReq) (*biz.ListPayoutUser, error) { + tx := r.data.DB(ctx).Model(&PayoutUser{}) + sortBy := "id desc" + if opts.SortBy != "" { + sortBy = opts.SortBy + } + + if opts.SelectFields != "" { + tx.Select(opts.SelectFields) + } + + if opts.StartTime.Unix() > 0 { + tx.Where("start_time <= ?", stime.FormatTimeString(opts.StartTime)) + } + + if opts.EndTime.Unix() > 0 { + tx.Where("end_time >= ?", stime.FormatTimeString(opts.EndTime)) + } + + if opts.Uuid != "" { + tx.Where("name = ?", opts.Uuid) + } + + // count + var count int64 + if !opts.DisablePaging { + if err := tx.Count(&count).Error; err != nil { + return nil, fmt.Errorf("count search user: %w", err) + } + } + + if page := opts.Page; page != nil && !opts.DisablePaging { + tx.Limit(int(page.Limit())).Offset(int(page.Offset())) + } + + var list []PayoutUser + if err := tx.Order(sortBy).Find(&list).Error; err != nil { + return nil, fmt.Errorf("find: %w", err) + } + + if opts.DisablePaging { + count = int64(len(list)) + } + + rv := make([]*biz.PayoutUser, 0) + for _, item := range list { + mitem, err := r.unmarshalPayoutUserFromDB(item) + if err != nil { + return nil, fmt.Errorf("unmarshalPayoutUserFromDB err: %w", err) + } + rv = append(rv, mitem) + } + + return &biz.ListPayoutUser{ + Items: rv, + Total: int32(count), + }, nil +} + +// unmarshalPayoutUserFromDB 数据库数据转换为业务数据 +func (r *eonlineRepo) unmarshalPayoutUserFromDB(s PayoutUser) (*biz.PayoutUser, error) { + l := &biz.PayoutUser{} + err := marshal.ConvertStructToOtherType(s, l) + if err != nil { + return nil, err + } + + // 单独处理时间 + l.CreatedAt = s.CreatedAt + l.UpdatedAt = s.UpdatedAt + return l, nil +} + +// GetUserByUuid 根据uuid获取用户信息 +func (r *eonlineRepo) GetUserByUuid(ctx context.Context, uuid string) (*biz.PayoutUser, error) { + var item PayoutUser + if err := r.data.DB(ctx).Where("uuid = ?", uuid).First(&item).Error; err != nil && err != gorm.ErrRecordNotFound { + return nil, fmt.Errorf("get user by uuid: %w", err) + } + + return r.unmarshalPayoutUserFromDB(item) +} + +// CreatePayoutUser 创建提现用户信息 +func (r *eonlineRepo) CreatePayoutUser(ctx context.Context, item *biz.PayoutUser) (*biz.PayoutUser, error) { + tx := r.data.DB(ctx).Model(&PayoutUser{}) + if err := tx.Create(&item).Error; err != nil { + return nil, fmt.Errorf("create: %w", err) + } + return item, nil +} + +// SearchPayoutRecord 查询提现记录信息 +func (r *eonlineRepo) SearchPayoutRecord(ctx context.Context, opts *biz.SearchPayoutRecordReq) (*biz.ListPayoutRecord, error) { + tx := r.data.DB(ctx).Model(&PayoutRecord{}) + sortBy := "id desc" + if opts.SortBy != "" { + sortBy = opts.SortBy + } + + if opts.SelectFields != "" { + tx.Select(opts.SelectFields) + } + + if opts.StartTime.Unix() > 0 { + tx.Where("start_time <= ?", stime.FormatTimeString(opts.StartTime)) + } + + if opts.EndTime.Unix() > 0 { + tx.Where("end_time >= ?", stime.FormatTimeString(opts.EndTime)) + } + + if opts.Uuid != "" { + tx.Where("name = ?", opts.Uuid) + } + + // count + var count int64 + if !opts.DisablePaging { + if err := tx.Count(&count).Error; err != nil { + return nil, fmt.Errorf("count search user: %w", err) + } + } + + if page := opts.Page; page != nil && !opts.DisablePaging { + tx.Limit(int(page.Limit())).Offset(int(page.Offset())) + } + + var list []PayoutRecord + if err := tx.Order(sortBy).Find(&list).Error; err != nil { + return nil, fmt.Errorf("find: %w", err) + } + + if opts.DisablePaging { + count = int64(len(list)) + } + + rv := make([]*biz.PayoutRecord, 0) + for _, item := range list { + mitem, err := r.unmarshalPayoutRecordFromDB(item) + if err != nil { + return nil, fmt.Errorf("unmarshalPayoutRecordFromDB err: %w", err) + } + rv = append(rv, mitem) + } + + return &biz.ListPayoutRecord{ + Items: rv, + Total: int32(count), + }, nil +} + +// CreatePayoutRecord 创建提现记录信息 +func (r *eonlineRepo) CreatePayoutRecord(ctx context.Context, item *biz.PayoutRecord) (*biz.PayoutRecord, error) { + tx := r.data.DB(ctx).Model(&PayoutRecord{}) + if err := tx.Create(&item).Error; err != nil { + return nil, fmt.Errorf("create: %w", err) + } + + return item, nil +} + +// UpdatePayoutRecordStatus 更新提现记录状态 +func (r *eonlineRepo) UpdatePayoutRecordNotify(ctx context.Context, recordNo string, payoutId string, status uint8, fail string) error { + tx := r.data.DB(ctx).Model(&PayoutRecord{}) + if err := tx.Where("record_no = ?", recordNo).Updates(map[string]interface{}{"payout_id": payoutId, "status": status, "fail": fail}).Error; err != nil { + return fmt.Errorf("update: %w", err) + } + + return nil +} + +// CheckPayoutValid 获取可提现次数 +func (r *eonlineRepo) GetPayoutLeftum(ctx context.Context, uuid string) (int, error) { + avalidNum := 2 // 默认可提现次数 + var count int64 + if err := r.data.DB(ctx).Model(&PayoutRecord{}).Where("uuid = ? and status = ?", uuid, int(biz.PayoutStatusPayouted)).Count(&count).Error; err != nil { + return 0, fmt.Errorf("count: %w", err) + } + return avalidNum - int(count), nil +} + +// GetPayoutRecordCountByAccount 根据account获取成功提现的次数 +func (r *eonlineRepo) GetPayoutRecordCountByAccount(ctx context.Context, account string) (int, error) { + var count int64 + if err := r.data.DB(ctx).Model(&PayoutRecord{}).Where("account = ? and (status=? or status=?)", account, int(biz.PayoutStatusPayouted), int(biz.PayoutStatusPayouting)).Count(&count).Error; err != nil { + return 0, fmt.Errorf("count: %w", err) + } + return int(count), nil +} + +// GetPayoutRecordListByUuid 根据uuid获取提现记录列表 +func (r *eonlineRepo) GetPayoutRecordListByUuid(ctx context.Context, uuid string) ([]*biz.PayoutRecord, error) { + var list []PayoutRecord + if err := r.data.DB(ctx).Model(&PayoutRecord{}).Where("uuid = ?", uuid).Order("id DESC").Find(&list).Error; err != nil { + return nil, fmt.Errorf("find: %w", err) + } + + rv := make([]*biz.PayoutRecord, 0) + for _, item := range list { + mitem, err := r.unmarshalPayoutRecordFromDB(item) + if err != nil { + return nil, fmt.Errorf("unmarshalPayoutRecordFromDB err: %w", err) + } + rv = append(rv, mitem) + } + + return rv, nil +} + +// 根据account、status 获取提现记录 +func (r *eonlineRepo) GetPayoutRecordByAccountStatus(ctx context.Context, account string, status, pageIndex, pageSize int) ([]*biz.PayoutRecord, error) { + var list []PayoutRecord + var err error + + if account != "" { + err = r.data.DB(ctx).Model(&PayoutRecord{}).Where("account = ? and status = ?", account, status).Offset(pageIndex * pageSize).Limit(pageSize).Order("id DESC").Find(&list).Error + } else { + err = r.data.DB(ctx).Model(&PayoutRecord{}).Where("status = ?", status).Offset(pageIndex * pageSize).Limit(pageSize).Order("id DESC").Find(&list).Error + } + + if err != nil { + return nil, fmt.Errorf("find: %w", err) + } + + rv := make([]*biz.PayoutRecord, 0) + for _, item := range list { + mitem, err := r.unmarshalPayoutRecordFromDB(item) + if err != nil { + return nil, fmt.Errorf("unmarshalPayoutRecordFromDB err: %w", err) + } + rv = append(rv, mitem) + } + + return rv, nil +} + +// CheckPayoutValid 检查是否可提现 +func (r *eonlineRepo) CheckPayoutValid(ctx context.Context, uuid string) (bool, error) { + leftNum, err := r.GetPayoutLeftum(ctx, uuid) + if err != nil { + return false, fmt.Errorf("GetPayoutLeftum: %w", err) + } + return leftNum > 0, nil +} + +// unmarshalPayoutRecordFromDB 数据库数据转换为业务数据 +func (r *eonlineRepo) unmarshalPayoutRecordFromDB(s PayoutRecord) (*biz.PayoutRecord, error) { + l := &biz.PayoutRecord{} + err := marshal.ConvertStructToOtherType(s, l) + if err != nil { + return nil, err + } + + // 单独处理时间 + l.CreatedAt = s.CreatedAt + l.UpdatedAt = s.UpdatedAt + return l, nil +} + +// GetPayoutRecordByRecordNo 根据recordNo获取提现记录 +func (r *eonlineRepo) GetPayoutRecordByRecordNo(ctx context.Context, recordNo string) (*biz.PayoutRecord, error) { + var item PayoutRecord + if err := r.data.DB(ctx).Model(&PayoutRecord{}).Where("record_no = ?", recordNo).First(&item).Error; err != nil { + return nil, fmt.Errorf("first: %w", err) + } + + return r.unmarshalPayoutRecordFromDB(item) +} + +// UpdatePayoutUserLoginDays 更新用户登录天数 +func (r *eonlineRepo) UpdatePayoutUserLoginDays(ctx context.Context, uuid string, loginDays uint) error { + tx := r.data.DB(ctx).Model(&PayoutUser{}) + if err := tx.Where("uuid = ?", uuid).Updates(map[string]interface{}{"login_days": loginDays}).Error; err != nil { + return fmt.Errorf("update: %w", err) + } + + return nil +} + +// 更新 客户端上传的、需要保持的数据 +func (r *eonlineRepo) UpdatePayoutUserClientData(ctx context.Context, uuid string, clientData string) error { + tx := r.data.DB(ctx).Model(&PayoutUser{}) + if err := tx.Where("uuid = ?", uuid).Updates(map[string]interface{}{"clientData": clientData}).Error; err != nil { + return fmt.Errorf("update: %w", err) + } + + return nil +} + +// 创建分红记录信息 +func (r *eonlineRepo) CreateBonusRecord(ctx context.Context, item *biz.BonusRecord) (*biz.BonusRecord, error) { + tx := r.data.DB(ctx).Model(&BonusRecord{}) + if err := tx.Create(&item).Error; err != nil { + return nil, fmt.Errorf("CreateBonusRecord: %w", err) + } + + return item, nil +} + +// 更新服务器领取的分红总额 +func (r *eonlineRepo) UpdateBonusCur(ctx context.Context, id uint32, bonusCur uint32) error { + tx := r.data.DB(ctx).Model(&BonusRecord{}) + if err := tx.Where("id = ?", id).Updates(map[string]interface{}{"bonus_cur": bonusCur}).Error; err != nil { + return fmt.Errorf("UpdateBonusCur: %w", err) + } + + return nil +} diff --git a/app/eonline/internal/server/asynq.go b/app/eonline/internal/server/asynq.go new file mode 100644 index 0000000..93eb9b2 --- /dev/null +++ b/app/eonline/internal/server/asynq.go @@ -0,0 +1,67 @@ +package server + +import ( + "fmt" + "log" + "os" + "sandc/app/eonline/internal/biz" + "sandc/app/eonline/internal/conf" + + "github.com/hibiken/asynq" + "golang.org/x/sync/errgroup" +) + +type AsynqServer struct { + srv *asynq.Server + mux *asynq.ServeMux + scheduler *asynq.Scheduler + entryID string +} + +func (j *AsynqServer) Run() error { + eg := errgroup.Group{} + eg.Go(func() error { + log.Println("schedule start", j.entryID) + return j.scheduler.Run() + }) + eg.Go(func() error { + return j.srv.Run(j.mux) + }) + return eg.Wait() +} + +func NewAsynqServer(conf *conf.Queue, uc *biz.EonlineUsecase) *AsynqServer { + asynqConf := conf.Asynq + redisOpt := asynq.RedisClientOpt{ + Addr: asynqConf.Addr, + Password: asynqConf.Password, + DB: int(asynqConf.Db), + PoolSize: int(asynqConf.Pool), + ReadTimeout: asynqConf.ReadTimeout.AsDuration(), + WriteTimeout: asynqConf.WriteTimeout.AsDuration(), + } + srv := asynq.NewServer( + redisOpt, + asynq.Config{ + Concurrency: int(asynqConf.Concurrency), + Queues: map[string]int{ + "critical": 6, + "default": 3, + "low": 1, + }, + }) + mux := asynq.NewServeMux() + mux.HandleFunc(biz.EonlineTaskName, uc.AsynqEonlineTaskHandler) + scheduler := asynq.NewScheduler(redisOpt, &asynq.SchedulerOpts{}) + task := asynq.NewTask(biz.EonlineTaskName, nil) + entryID, err := scheduler.Register("*/15 * * * *", task, asynq.TaskID(biz.EonlineTaskName)) + checkError(err) + return &AsynqServer{srv: srv, mux: mux, scheduler: scheduler, entryID: entryID} +} + +func checkError(err error) { + if err != nil { + fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error()) + os.Exit(1) + } +} diff --git a/app/eonline/internal/server/grpc.go b/app/eonline/internal/server/grpc.go new file mode 100644 index 0000000..99a26a7 --- /dev/null +++ b/app/eonline/internal/server/grpc.go @@ -0,0 +1,42 @@ +package server + +import ( + v1 "sandc/api/eonline/v1" + "sandc/app/eonline/internal/conf" + "sandc/app/eonline/internal/service" + + "github.com/go-kratos/kratos/v2/log" + "github.com/go-kratos/kratos/v2/middleware/logging" + "github.com/go-kratos/kratos/v2/middleware/metadata" + "github.com/go-kratos/kratos/v2/middleware/metrics" + "github.com/go-kratos/kratos/v2/middleware/recovery" + "github.com/go-kratos/kratos/v2/middleware/tracing" + "github.com/go-kratos/kratos/v2/middleware/validate" + "github.com/go-kratos/kratos/v2/transport/grpc" +) + +// NewGRPCServer new a gRPC server. +func NewGRPCServer(c *conf.Server, eonline *service.EonlineService, logger log.Logger) *grpc.Server { + var opts = []grpc.ServerOption{ + grpc.Middleware( + recovery.Recovery(), + tracing.Server(), + logging.Server(logger), + metrics.Server(), + validate.Validator(), + metadata.Server(metadata.WithPropagatedPrefix("")), + ), + } + if c.Grpc.Network != "" { + opts = append(opts, grpc.Network(c.Grpc.Network)) + } + if c.Grpc.Addr != "" { + opts = append(opts, grpc.Address(c.Grpc.Addr)) + } + if c.Grpc.Timeout != nil { + opts = append(opts, grpc.Timeout(c.Grpc.Timeout.AsDuration())) + } + srv := grpc.NewServer(opts...) + v1.RegisterEonlineServer(srv, eonline) + return srv +} diff --git a/app/eonline/internal/server/http.go b/app/eonline/internal/server/http.go new file mode 100644 index 0000000..ebac2c3 --- /dev/null +++ b/app/eonline/internal/server/http.go @@ -0,0 +1,50 @@ +package server + +import ( + v1 "sandc/api/eonline/v1" + "sandc/app/eonline/internal/conf" + "sandc/app/eonline/internal/service" + "sandc/pkg/middleware/xhttp" + + "github.com/go-kratos/kratos/v2/log" + "github.com/go-kratos/kratos/v2/middleware/logging" + "github.com/go-kratos/kratos/v2/middleware/metadata" + "github.com/go-kratos/kratos/v2/middleware/metrics" + "github.com/go-kratos/kratos/v2/middleware/recovery" + "github.com/go-kratos/kratos/v2/middleware/tracing" + "github.com/go-kratos/kratos/v2/middleware/validate" + "github.com/go-kratos/kratos/v2/transport/http" + "github.com/go-kratos/swagger-api/openapiv2" +) + +// NewHTTPServer new a HTTP server. +func NewHTTPServer(c *conf.Server, eonline *service.EonlineService, logger log.Logger) *http.Server { + var opts = []http.ServerOption{ + http.Middleware( + xhttp.Server(), + recovery.Recovery(), + tracing.Server(), + logging.Server(logger), + metrics.Server(), + validate.Validator(), + metadata.Server(metadata.WithPropagatedPrefix("")), + ), + http.ErrorEncoder(DefaultErrorEncoder), + http.ResponseEncoder(DefaultResponseEncoder), + http.RequestDecoder(DefaultRequestDecoder), + } + if c.Http.Network != "" { + opts = append(opts, http.Network(c.Http.Network)) + } + if c.Http.Addr != "" { + opts = append(opts, http.Address(c.Http.Addr)) + } + if c.Http.Timeout != nil { + opts = append(opts, http.Timeout(c.Http.Timeout.AsDuration())) + } + srv := http.NewServer(opts...) + openAPIhandler := openapiv2.NewHandler() + srv.HandlePrefix("/q/", openAPIhandler) + v1.RegisterEonlineHTTPServer(srv, eonline) + return srv +} diff --git a/app/eonline/internal/server/kafka.go b/app/eonline/internal/server/kafka.go new file mode 100644 index 0000000..cf9c93e --- /dev/null +++ b/app/eonline/internal/server/kafka.go @@ -0,0 +1,31 @@ +package server + +import ( + "context" + "github.com/go-kratos/kratos/v2/log" + "github.com/tx7do/kratos-transport/transport/kafka" + "sandc/app/eonline/internal/biz" + "sandc/app/eonline/internal/conf" +) + +// NewKafkaServer new a kafka server. +func NewKafkaServer(c *conf.Queue, uc *biz.EonlineUsecase, logger log.Logger) *kafka.Server { + opts := []kafka.ServerOption{ + kafka.WithAddress(c.Kafka.Addrs), + kafka.WithCodec("json"), + kafka.WithGlobalTracerProvider(), + } + if c.Kafka.Username != "" && c.Kafka.Password != "" { + opts = append(opts, kafka.WithPlainMechanism(c.Kafka.Username, c.Kafka.Password)) + } + kafkaSrv := kafka.NewServer(opts...) + err := kafkaSrv.RegisterSubscriber( + context.Background(), c.Kafka.Topic, c.Kafka.Group, false, + biz.RegisterExampleHandler(uc.KakfaExampleConsumer), + biz.ExampleCreator, + ) + if err != nil { + log.Fatal(err) + } + return kafkaSrv +} diff --git a/app/eonline/internal/server/registrar.go b/app/eonline/internal/server/registrar.go new file mode 100644 index 0000000..53a457e --- /dev/null +++ b/app/eonline/internal/server/registrar.go @@ -0,0 +1,30 @@ +package server + +import ( + "github.com/go-kratos/kratos/contrib/registry/etcd/v2" + "github.com/go-kratos/kratos/v2/log" + "github.com/go-kratos/kratos/v2/registry" + etcdclient "go.etcd.io/etcd/client/v3" + "sandc/app/eonline/internal/conf" +) + +func newEtcd(conf *conf.Server) *etcd.Registry { + client, err := etcdclient.New(etcdclient.Config{ + Endpoints: conf.Etcd.Addr, + Username: conf.Etcd.Username, + Password: conf.Etcd.Password, + }) + if err != nil { + log.Fatal(err) + } + r := etcd.New(client) + return r +} + +func NewDiscovery(conf *conf.Server) registry.Discovery { + return newEtcd(conf) +} + +func NewRegistrar(conf *conf.Server) registry.Registrar { + return newEtcd(conf) +} diff --git a/app/eonline/internal/server/server.go b/app/eonline/internal/server/server.go new file mode 100644 index 0000000..ba244ae --- /dev/null +++ b/app/eonline/internal/server/server.go @@ -0,0 +1,137 @@ +package server + +import ( + "encoding/hex" + "io" + v1 "sandc/api/eonline/v1" + "strings" + + nethttp "net/http" + + "github.com/go-kratos/kratos/v2/errors" + "github.com/go-kratos/kratos/v2/transport/http" + "github.com/google/wire" + "github.com/xxtea/xxtea-go/xxtea" +) + +// EncryptKey is the key for encryption +const DecryptKey = "tk423&(gBUjX#$s0628" + +// ProviderSet is server providers. +var ProviderSet = wire.NewSet(NewHTTPServer, NewGRPCServer, NewRegistrar, NewDiscovery, NewAsynqServer) + +type ResData struct { + Code int `json:"code"` + Msg string `json:"msg"` + Data interface{} `json:"data"` +} + +// DefaultErrorEncoder encodes the error to the HTTP response +func DefaultErrorEncoder(w nethttp.ResponseWriter, r *nethttp.Request, err error) { + se := errors.FromError(err) + codec, _ := http.CodecForRequest(r, "Accept") + reply := ResData{ + Code: int(se.Code), + Msg: se.Message, + Data: nil, + } + + // if strings.Contains(se.Message, "email not support") { + // reply.Code = 1 + // } + + // if strings.Contains(se.Message, "no payout chance") { + // reply.Code = 1 + // } + + body, err := codec.Marshal(reply) + if err != nil { + //给出自定义code + w.WriteHeader(nethttp.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", strings.Join([]string{"application", codec.Name()}, "/")) + w.WriteHeader(200) + + //加密 + if r.Header.Get("encrypt") == "true" { + s := xxtea.Encrypt(body, []byte(DecryptKey)) + body = []byte(hex.EncodeToString(s)) + } + + _, _ = w.Write(body) +} + +// DefaultResponseEncoder encodes the object to the HTTP response. +func DefaultResponseEncoder(w nethttp.ResponseWriter, r *nethttp.Request, v interface{}) error { + codec, _ := http.CodecForRequest(r, "Accept") + + //断言接口类型 + if _, ok := v.(*v1.PayoutCallbackReply); ok { + w.Write([]byte("success")) + return nil + } + dv := v + data, err := codec.Marshal(dv) + if err != nil { + return err + } + + reply := struct { + Code int `json:"code"` + Msg string `json:"msg"` + }{ + Code: 0, + Msg: "sucess", + } + + replyData, err := codec.Marshal(reply) + if err != nil { + return err + } + //fmt.Println(string(data)) + var newData = make([]byte, 0, len(replyData)+len(data)+8) + newData = append(newData, replyData[:len(replyData)-1]...) + newData = append(newData, []byte(`,"data":`)...) + newData = append(newData, data...) + newData = append(newData, '}') + + w.Header().Set("Content-Type", "application/json") + + if r.Header.Get("encrypt") == "true" { + s := xxtea.Encrypt(newData, []byte(DecryptKey)) + newData = []byte(hex.EncodeToString(s)) + } + + _, err = w.Write(newData) + if err != nil { + return err + } + return nil +} + +func DefaultRequestDecoder(r *http.Request, v interface{}) error { + codec, ok := http.CodecForRequest(r, "Content-Type") + if !ok { + return errors.BadRequest("CODEC", r.Header.Get("Content-Type")) + } + data, err := io.ReadAll(r.Body) + if err != nil { + return errors.BadRequest("CODEC", err.Error()) + } + _ = r.Body.Close() + if r.Header.Get("encrypt") == "true" { + if r.Header.Get("params") != "" { + p, _ := hex.DecodeString(r.Header.Get("params")) + data = xxtea.Decrypt(p, []byte(DecryptKey)) + } else { + data = []byte("{}") + } + } + + if err = codec.Unmarshal(data, v); err != nil { + return errors.BadRequest("CODEC", err.Error()) + } + return nil +} diff --git a/app/eonline/internal/service/README.md b/app/eonline/internal/service/README.md new file mode 100644 index 0000000..42321b7 --- /dev/null +++ b/app/eonline/internal/service/README.md @@ -0,0 +1 @@ +# Service diff --git a/app/eonline/internal/service/eonline.go b/app/eonline/internal/service/eonline.go new file mode 100644 index 0000000..1af2739 --- /dev/null +++ b/app/eonline/internal/service/eonline.go @@ -0,0 +1,973 @@ +package service + +import ( + "context" + "encoding/json" + "fmt" + "strconv" + "strings" + "time" + + v1 "sandc/api/eonline/v1" + "sandc/app/eonline/internal/biz" + "sandc/app/eonline/internal/conf" + "sandc/app/eonline/internal/config" + "sandc/app/eonline/internal/data" + "sandc/pkg/middleware/xhttp" + "sandc/pkg/utils" + + "github.com/go-kratos/kratos/v2/log" + "github.com/paemuri/brdoc" + "github.com/shopspring/decimal" +) + +// EonlineService is a eonline service. +type EonlineService struct { + v1.UnimplementedEonlineServer + + uc *biz.EonlineUsecase + log *log.Helper + + conf *conf.Bootstrap +} + +var ( + mysqlDB *biz.EonlineUsecase + nowStrSeconds string // 当前时间秒级时间戳字符串 + nowStrMilliSeconds string // 当前时间毫秒级时间戳字符串 +) + +// NewEonlineService new a greeter service. +func NewEonlineService(uc *biz.EonlineUsecase, logger log.Logger, conf *conf.Bootstrap) *EonlineService { + nowStrSeconds = strconv.FormatInt(time.Now().Unix(), 10) + nowStrMilliSeconds = strconv.FormatInt(time.Now().UnixMilli(), 10) + + mysqlDB = uc + timeoutTimerPer10Second = int64(conf.Server.TimeoutTimerPer10Second) + return &EonlineService{uc: uc, log: log.NewHelper(logger), conf: conf} +} + +func checkStrSeconds(ts string) bool { + if len(ts) == len(nowStrSeconds) { + return true + } + return false +} + +func checkStrMilliSeconds(ts string) bool { + if len(ts) == len(nowStrMilliSeconds) { + return true + } + return false +} + +// SayHello implements helloworld.EonlineServer +func (s *EonlineService) SayHello(ctx context.Context, in *v1.HelloRequest) (*v1.HelloReply, error) { + s.log.WithContext(ctx).Infof("SayHello Received: %v", in.GetName()) + + if in.GetName() == "error" { + return nil, v1.ErrorUserNotFound("user not found: %s", in.GetName()) + } + return &v1.HelloReply{Message: "Hello " + in.GetName()}, nil +} + +// PayInit 初始化支付用户信息 +func (s *EonlineService) PayInit(ctx context.Context, req *v1.PayInitReq) (*v1.PayInitReply, error) { + fmt.Printf("PayInit: req[%+v]\n", req) + log.Infof("PayInit: req[%+v]", req) + + reply := &v1.PayInitReply{} + + for { + // 固定结构体参数 + // 验证签名 + err := s.validateSignRequest( + v1.PayInitReq{ + Platform: req.Platform, + Deviceid: req.Deviceid, + Version: req.Version, + Ip: req.Ip, + Ts: req.Ts, + Sign: req.Sign, + }, biz.SignFixedParameters) + if err != nil { + // return nil, fmt.Errorf("validate request error: %w", err) + reply.Error = 2 + break + } + if !checkStrSeconds(req.Ts) { + reply.Error = 4 + break + } + + if s.conf.Server.Env != "qa" { + req.Ts = strconv.Itoa(int(time.Now().Unix())) + } + + // ver := config.Version2int(&req.Version) + // if ver < config.PublicVersionMin { + // // return nil, fmt.Errorf("version [%s] not support", req.Version) + // reply.Error = 3 + // break + // } + + // 获取真实IP地址和国家code + country, currentIp := s.uc.GetCountryCodeByIp(ctx, req.Ip) + req.Ip = currentIp + + x, err := s.uc.PayInit(ctx, &biz.PayoutAddtionalReq{ + Platform: req.Platform, + DeviceId: req.Deviceid, + Ip: req.Ip, + Version: req.Version, + Country: country, + Ts: req.Ts, + }) + + if err != nil { + log.Infof("PayInit error: err[%v]", err) + // return nil, err + reply.Error = 1 + break + } + + // 生成唯一用户ID + uuid := biz.GenPayoutUuid(req.Deviceid) + x2, err := s.uc.CheckInfo(ctx, &biz.CheckInfoReq{ + Uuid: uuid, + Ts: req.Ts, + }) + if err != nil { + log.Infof("PayInit error: err[%v]", err) + reply.Error = 1 + break + } + + reply = &v1.PayInitReply{ + Uuid: x.UUID, + CanCheckSubmit: int32(x2.CanCheckSubmit), + CheckSubmit: x2.CheckSubmit, + CheckResult: x2.CheckResult, + CheckPayout: x2.CheckPayout, + CheckCoin: config.PublicCheckCoin, + CanCheckPayOut: x2.CanCheckPayOut, + CheckResultFailedDesc: x2.CheckResultFailedDesc, + ClientData: x.ClientData, + } + + items := make([]*v1.PayInitReply_Item, 0) + for _, v := range x.Items { + items = append(items, &v1.PayInitReply_Item{ + Id: uint32(v.Id), + Amount: v.Amount, + Status: uint32(v.Status), + }) + } + + var status uint32 // 状态:1可提现 2:条件未达成 3:已提现, 4:禁止提现, 5:提现中 + switch reply.CheckPayout { // 提交身份文件奖励5美元领取的情况,0没有提现记录,1提现中,2提现成功,3提现失败 + case 0: + status = 2 + if reply.CanCheckPayOut != 1 { // 身份文件审核过的提现奖励5美元 能否 提现,0能提现,1条件不满足,不能提现,2当天已经提现过,不能再次提现 + status = 1 + } + case 1: + status = 5 + case 2: + status = 3 + case 3: + status = 1 + } + + items = append(items, &v1.PayInitReply_Item{ + Id: uint32(biz.PayoutItemId4), + Amount: float64(reply.CheckCoin / 100), + Status: status, + }) + + reply.Items = items + + break + } + + log.Infof("PayInit: reply[%+v]", reply) + + return reply, nil +} + +// Name: "WangChuan", +// Phone: "18117858803", +// Email: "w_windysl@foxmail.com", +// Account: "w_windysl@foxmail.com", +// AccountType: "Email", +// Method: "WALLET", +// Channel: "PayPal", +// CustomCode: "custom_chuan9982674851738119", +// FeeBear: "merchant", //beneficiary , merchant +// Amount: "60000", +// SourceCurrency: "USD", +// ArrivalCurrency: "USD", +// NotifyUrl: "https://notify.url", +// AdditionalRemark: "pagsmile payout chuan test", +// Country: "ALB", + +// PayOut +func (s *EonlineService) Payout(ctx context.Context, req *v1.PayoutReq) (*v1.PayoutReply, error) { + fmt.Printf("Payout: req[%+v]\n", req) + log.Infof("Payout: req[%+v]", req) + + reply := &v1.PayoutReply{} + + for { + // 固定结构体参数 + uuid := biz.GenPayoutUuid(req.Deviceid) + + // account替换左右空格 + req.Account = strings.Trim(req.Account, " ") + + if req.Uuid != uuid { + // return nil, fmt.Errorf("uuid error") + reply.Error = 4 + break + } + + // 验证签名 + err := s.validateSignRequest( + v1.PayoutReq{ + Platform: req.Platform, + Deviceid: req.Deviceid, + Version: req.Version, + Ip: req.Ip, + Ts: req.Ts, + Sign: req.Sign, + Account: req.Account, + Amount: req.Amount, + AdditionalRemark: req.AdditionalRemark, + Uuid: req.Uuid, + }, biz.SignFixedParameters) + if err != nil { + // return nil, fmt.Errorf("validate request error: %w", err) + reply.Error = 2 + break + } + if !checkStrSeconds(req.Ts) { + reply.Error = 24 + break + } + if s.conf.Server.Env != "qa" { + req.Ts = strconv.Itoa(int(time.Now().Unix())) + } + + ver := config.Version2int(&req.Version) + if ver < config.PublicVersionMin { + // return nil, fmt.Errorf("version [%s] not support", req.Version) + reply.Error = 3 + break + } + + // 处理请求 + bq := &biz.PayoutReq{ + Account: req.Account, + Amount: decimal.NewFromFloat(req.Amount).String(), + AdditionalRemark: req.AdditionalRemark, + } + + // 业务参数 + opts := &biz.PayoutAddtionalReq{ + Platform: req.Platform, + DeviceId: req.Deviceid, + Version: req.Version, + Ip: req.Ip, + Amount: req.Amount, + ItemId: req.ItemId, + Account: req.Account, + Ts: req.Ts, + Email: req.Email, + } + x, err, errCode := s.uc.Payout(ctx, uuid, opts, bq) + if err != nil { + log.Infof("Payout error: err[%v]", err) + // return nil, err + reply.Error = errCode + break + } + + reply = &v1.PayoutReply{ + Id: x.Id, + RecordNo: x.CustomCode, + } + + break + } + + fmt.Printf("Payout: reply[%+v]", reply) + log.Infof("Payout: reply[%+v]", reply) + + return reply, nil +} + +// PayoutBrazil +func (s *EonlineService) PayoutBrazil(ctx context.Context, req *v1.PayoutReq) (*v1.PayoutReply, error) { + // fmt.Printf("Payout: req[%+v]\n", req) + log.Infof("PayoutBrazil: req[%+v]", req) + + if req.DataAdjust == nil { + log.Infof("PayoutBrazil error: req.DataAdjust is nil Deviceid[%s]", req.Deviceid) + } + if req.DataShuShu == nil { + log.Infof("PayoutBrazil error: req.DataShuShu is nil Deviceid[%s]", req.Deviceid) + } + + reply := &v1.PayoutReply{} + + bSend := false + for { + // 固定结构体参数 + uuid := biz.GenPayoutUuid(req.Deviceid) + + // account替换左右空格 + req.Account = strings.Trim(req.Account, " ") + + if req.Uuid != uuid { + // return nil, fmt.Errorf("uuid error") + reply.Error = 4 + break + } + + if len(req.DocumentType) < 3 { + // return nil, fmt.Errorf("document_type length error") + reply.Error = 13 + break + } + if len(req.DocumentId) < 11 { + // return nil, fmt.Errorf("document_id length error") + reply.Error = 14 + break + } + if len(req.AccountType) < 3 { + // return nil, fmt.Errorf("account_type length error") + reply.Error = 15 + break + } + if len(req.Name) < 5 { + // return nil, fmt.Errorf("name length error") + reply.Error = 16 + break + } + + if req.AccountType == req.DocumentType && req.Account != req.DocumentId { + // return nil, fmt.Errorf("The values of account_type and document_type are the same, but the values of account and document_id are different") + reply.Error = 17 + break + } + + // CPF CNPJ PHONE EMAIL EVP + if req.AccountType == "CPF" { + if len(req.Account) != 11 || !brdoc.IsCPF(req.Account) { + // return nil, fmt.Errorf("account not CPF") + reply.Error = 18 + break + } + } else if req.AccountType == "CNPJ" { + if len(req.Account) != 14 || !brdoc.IsCNPJ(req.Account) { + // return nil, fmt.Errorf("account not CNPJ") + reply.Error = 19 + break + } + } else if req.AccountType == "PHONE" { + } else if req.AccountType == "EMAIL" { + } else if req.AccountType == "EVP" { + } else { + // return nil, fmt.Errorf("account_type error") + reply.Error = 20 + break + } + + // One of: CPF, CNPJ + if req.DocumentType == "CPF" { + if len(req.DocumentId) != 11 || !brdoc.IsCPF(req.DocumentId) { + // return nil, fmt.Errorf("document_id not CPF") + reply.Error = 22 + break + } + } else if req.DocumentType == "CNPJ" { + if len(req.DocumentId) != 14 || !brdoc.IsCNPJ(req.DocumentId) { + // return nil, fmt.Errorf("document_id not CNPJ") + reply.Error = 23 + break + } + } else { + // return nil, fmt.Errorf("document_type error") + reply.Error = 21 + break + } + + // 验证签名 + err := s.validateSignRequest( + v1.PayoutReq{ + Platform: req.Platform, + Deviceid: req.Deviceid, + Version: req.Version, + Ip: req.Ip, + Ts: req.Ts, + Sign: req.Sign, + Account: req.Account, + Amount: req.Amount, + AdditionalRemark: req.AdditionalRemark, + Uuid: req.Uuid, + }, biz.SignFixedParameters) + if err != nil { + // return nil, fmt.Errorf("validate request error: %w", err) + reply.Error = 2 + break + } + if !checkStrSeconds(req.Ts) { + reply.Error = 24 + break + } + if s.conf.Server.Env != "qa" { + req.Ts = strconv.Itoa(int(time.Now().Unix())) + } + + if len(req.ClientName) > 0 { + // if strings.EqualFold(req.ClientName, "com.capy.cash.earn.money.fast.real.rewards.game") { + } else { + ver := config.Version2int(&req.Version) + if ver < config.PublicVersionMin { + type Cmd_shushu struct { + AdNetwork string `json:"ad_network"` + } + + var shushu Cmd_shushu + err = json.Unmarshal([]byte(req.DataShuShu.SsSuperProperties), &shushu) + if err != nil { + reply.Error = 26 + break + } + + if strings.EqualFold(shushu.AdNetwork, "Organic") { + // return nil, fmt.Errorf("version [%s] not support", req.Version) + reply.Error = 3 + break + } + } + } + + // 处理请求 + bq := &biz.PayoutReq{ + Name: req.Name, + AccountType: req.AccountType, + Account: req.Account, + Document_type: req.DocumentType, + Document_id: req.DocumentId, + Amount: decimal.NewFromFloat(req.Amount).String(), + AdditionalRemark: req.AdditionalRemark, + ClientData: req.ClientData, + } + + // 业务参数 + opts := &biz.PayoutAddtionalReq{ + Platform: req.Platform, + DeviceId: req.Deviceid, + Version: req.Version, + Ip: req.Ip, + Amount: req.Amount, + ItemId: req.ItemId, + Account: req.Account, + Ts: req.Ts, + } + x, err, errCode := s.uc.PayoutBrazil(ctx, uuid, opts, bq) + if err != nil { + log.Infof("PayoutBrazil error: err[%v] deviceid[%s]", err, req.Deviceid) + // return nil, err + reply.Error = errCode + + errStr := err.Error() + if strings.Contains(errStr, "Payout error:") { + var currentIp string + httpCtx := xhttp.RequestFromContext(ctx) + if httpCtx != nil { + // 获取当前的IP地址 + currentIp = utils.GetClientPublicIP(httpCtx.Request()) + } + + sendStr := fmt.Sprintf("errCode=%d, %s", errCode, errStr) + + adjust := biz.GetAdjustData(req, sendStr, currentIp) + shuShu := biz.GetShuShuData(req, sendStr, currentIp) + biz.SendReport(ctx, adjust, shuShu) + + bSend = true + } + + // { // 测试用例 + // var currentIp string + // httpCtx := xhttp.RequestFromContext(ctx) + // if httpCtx != nil { + // // 获取当前的IP地址 + // currentIp = utils.GetClientPublicIP(httpCtx.Request()) + // } + // adjust := biz.GetAdjustData(req, "", currentIp) + // shuShu := biz.GetShuShuData(req, "", currentIp) + // if adjust != nil && shuShu != nil { + // shuShu.PayoutId = "TS202309260440574XKGb1AxafqWG" + // shuShu.MerchantReference = "PGS_5c2ca21a78a295b4c10a5bfe0027a4e0" + // biz.AddReportData(shuShu.PayoutId, adjust, shuShu) + // biz.GetReportData(shuShu.PayoutId) + // } + // } + + break + } + + reply = &v1.PayoutReply{ + Id: x.Id, + RecordNo: x.CustomCode, + } + + { + var currentIp string + httpCtx := xhttp.RequestFromContext(ctx) + if httpCtx != nil { + // 获取当前的IP地址 + currentIp = utils.GetClientPublicIP(httpCtx.Request()) + } + adjust := biz.GetAdjustData(req, "", currentIp) + shuShu := biz.GetShuShuData(req, "", currentIp) + if adjust != nil && shuShu != nil { + shuShu.PayoutId = x.Id + shuShu.MerchantReference = x.CustomCode + biz.AddReportData(x.Id, adjust, shuShu) + } + } + + break + } + + log.Infof("PayoutBrazil: reply[%+v]", reply) + + if reply.Error != 0 && !bSend { + var currentIp string + httpCtx := xhttp.RequestFromContext(ctx) + if httpCtx != nil { + // 获取当前的IP地址 + currentIp = utils.GetClientPublicIP(httpCtx.Request()) + } + + sendStr := fmt.Sprintf("errCode=%d", reply.Error) + + adjust := biz.GetAdjustData(req, sendStr, currentIp) + shuShu := biz.GetShuShuData(req, sendStr, currentIp) + biz.SendReport(ctx, adjust, shuShu) + } + + return reply, nil +} + +// PayoutCallback +func (s *EonlineService) PayoutCallback(ctx context.Context, req *v1.PayoutCallbackReq) (*v1.PayoutCallbackReply, error) { + // fmt.Printf("PayoutCallback: req[%+v]\n", req) + log.Infof("PayoutCallback: req[%+v]", req) + err := s.uc.PayoutNotify(ctx, &biz.PayoutNotifyReq{ + PayoutId: req.PayoutId, + CustomCode: req.CustomCode, + Status: req.Status, + Msg: req.Msg, + Timestamp: req.Timestamp, + }) + + reply := &v1.PayoutCallbackReply{ + Message: "success", + } + + log.Infof("PayoutCallback: reply[%+v]", reply) + + return reply, err +} + +// PayoutCheck 检查支付状态 +func (s *EonlineService) PayoutCheck(ctx context.Context, req *v1.PayoutCheckReq) (*v1.PayoutCheckReply, error) { + // fmt.Printf("PayoutCheck: req[%+v]\n", req) + log.Infof("PayoutCheck: req[%+v]", req) + + reply := &v1.PayoutCheckReply{} + + for { + // 固定结构体参数 + // 验证签名 + err := s.validateSignRequest( + v1.PayoutCheckReq{ + Platform: req.Platform, + Deviceid: req.Deviceid, + Version: req.Version, + Ip: req.Ip, + Ts: req.Ts, + Sign: req.Sign, + }, biz.SignFixedParameters) + if err != nil { + // return nil, fmt.Errorf("validate request error: %w", err) + reply.Error = 2 + break + } + if !checkStrSeconds(req.Ts) { + reply.Error = 4 + break + } + if s.conf.Server.Env != "qa" { + req.Ts = strconv.Itoa(int(time.Now().Unix())) + } + + // ver := config.Version2int(&req.Version) + // if ver < config.PublicVersionMin { + // // return nil, fmt.Errorf("version [%s] not support", req.Version) + // reply.Error = 3 + // break + // } + + // 处理请求 + status, err := s.uc.PayoutCheck(ctx, req.RecordNo) + if err != nil { + log.Infof("PayoutCheck error: err[%v]", err) + reply.Error = 1 + break + } + + reply = &v1.PayoutCheckReply{ + Status: uint32(status), + } + + break + } + + log.Infof("PayoutCheck: reply[%+v]", reply) + + return reply, nil +} + +func (s *EonlineService) GetPayoutUserLst(ctx context.Context, req *v1.PayoutUserLstReq) (*v1.PayoutUserLstReply, error) { + log.Infof("GetPayoutUserLst: req[%+v]", req) + + reply := &v1.PayoutUserLstReply{} + + for { + // 验证签名 + err := s.validateSignRequest( + v1.PayInitReq{ + Platform: req.Platform, + Deviceid: req.Deviceid, + Version: req.Version, + Ip: "", + Ts: req.Ts, + Sign: req.Sign, + }, biz.SignFixedParameters) + if err != nil { + reply.Error = 2 + break + } + if !checkStrSeconds(req.Ts) { + reply.Error = 4 + break + } + + if req.PageIndex > 0 { + req.PageIndex -= 1 + } + if req.PageSize <= 0 { + req.PageSize = 2000 + } + + records, err := s.uc.GetPayoutRecordByAccount(ctx, "", int(req.Status), int(req.PageIndex), int(req.PageSize)) + if err != nil { + log.Infof("GetPayoutRecordByAccount error[%+v]", err) + reply.Error = 3 + break + } + + for _, record := range records { + reply.Lst = append(reply.Lst, &v1.PayoutUserOne{ + Email: record.Email, + RecordNo: record.RecordNo, + Account: record.Account, + Status: uint32(record.Status), + }) + } + + break + } + + log.Infof("GetPayoutUserLst: reply[%+v]", reply) + return reply, nil +} + +func (s *EonlineService) SetPayoutStatus(ctx context.Context, req *v1.PayoutStatusReq) (*v1.PayoutStatusReply, error) { + log.Infof("SetPayoutStatus: req[%+v]", req) + + reply := &v1.PayoutStatusReply{ + RecordNo: req.RecordNo, + Status: req.Status, + } + + for { + // 验证签名 + err := s.validateSignRequest( + v1.PayInitReq{ + Platform: req.Platform, + Deviceid: req.Deviceid, + Version: req.Version, + Ip: "", + Ts: req.Ts, + Sign: req.Sign, + }, biz.SignFixedParameters) + if err != nil { + reply.Error = 2 + break + } + if !checkStrSeconds(req.Ts) { + reply.Error = 4 + break + } + + var status string + if req.Status == 2 { + status = "PAID" + } else if req.Status == 3 { + status = "REJECTED" + } else { + reply.Error = 5 + break + } + + err = s.uc.PayoutNotify2(ctx, &biz.PayoutNotifyReq{ + PayoutId: "Test", + CustomCode: req.RecordNo, + Status: status, + Msg: req.Fail, + Timestamp: time.Now().Unix(), + }) + + if err != nil { + log.Infof("SetPayoutStatus: PayoutNotify2 err[%v]", err) + reply.Error = 3 + break + } + + break + } + + log.Infof("SetPayoutStatus: reply[%+v]", reply) + return reply, nil +} + +func (s *EonlineService) Init(data *data.Data) { + lst := config.GetUserStateData() + + log.Infof("EonlineService Init: TbuserState\n%+v", lst) + + if len(lst) > 0 { + // 将配置表的审核结果,导入redis + var err error + var key, key2, val string + ctx := context.Background() + for _, v := range lst { + key = biz.GetCheckResultRedisKey(v.Paypal) + val, err = data.GetValue(ctx, key) + if err == nil { + if val != biz.StringNull { + log.Infof("EonlineService Init redis has key[%s] value[%s]", v.Paypal, val) + + if val == biz.CheckResultSuccess { + continue + } + } + + // 没有key值的,或者 值为0的,写入新值;以前的值为1的,不写入 + if val == biz.StringNull || (val == biz.CheckResultFail && v.State != 0) { + log.Infof("EonlineService Init key[%s] value[%s]=>[%d]", v.Paypal, val, v.State) + err = data.WriteValue(ctx, key, v.State, 0) + if err != nil { + log.Infof("EonlineService Init error: write key[%s] [%d] error[%s]", key, v.State, err.Error()) + } + } + + if v.State == 0 { + key2 = biz.GetCheckResultFailRedisKey(v.Paypal) + err = data.WriteValue(ctx, key2, v.Couse, 0) + if err != nil { + log.Infof("EonlineService Init error: write key[%s] [%s] error[%s]", key2, v.Couse, err.Error()) + } + } + } else { + log.Infof("EonlineService Init error: read key[%s] error[%s]", v.Paypal, err.Error()) + } + } + } + + // biz.PayoutItemIdAmountes[biz.PayoutItemId4] = float64(config.PublicCheckCoin / 100) + // log.Infof("PayoutItemIdAmountes[%d]=%f", biz.PayoutItemId4, biz.PayoutItemIdAmountes[biz.PayoutItemId4]) + + log.Infof("EonlineService Init finished") +} + +// 提交身份文件验证请求 +func (s *EonlineService) SubmitCheck(ctx context.Context, req *v1.SubmitCheckReq) (*v1.SubmitCheckReply, error) { + log.Infof("SubmitCheck: req[%+v]", req) + + result, err := s.uc.SubmitCheck(ctx, &biz.SubmitCheckReq{ + Account: req.Account, + Uuid: req.Uuid, + }) + if err != nil { + return nil, fmt.Errorf("validate request error: %w", err) + } + + reply := &v1.SubmitCheckReply{ + Result: result.Result, + } + + log.Infof("SubmitCheck: reply[%+v]", reply) + + return reply, nil +} + +func (s *EonlineService) CheckInfo(ctx context.Context, req *v1.CheckInfoReq) (*v1.CheckInfoReply, error) { + log.Infof("CheckInfo: req[%+v]", req) + // 验证签名 + err := s.validateSignRequest( + v1.PayInitReq{ + Platform: req.Platform, + Deviceid: req.Deviceid, + Version: req.Version, + Ip: req.Ip, + Ts: req.Ts, + Sign: req.Sign, + }, biz.SignFixedParameters) + if err != nil { + return nil, fmt.Errorf("validate request error: %w", err) + } + if !checkStrSeconds(req.Ts) { + reply := &v1.CheckInfoReply{Error: 2} + return reply, nil + } + req.Ts = strconv.Itoa(int(time.Now().Unix())) + + // 生成唯一用户ID + uuid := biz.GenPayoutUuid(req.Deviceid) + + if uuid != req.Uuid { + return nil, fmt.Errorf("uuid [%s] error", req.Uuid) + } + + x, err := s.uc.CheckInfo(ctx, &biz.CheckInfoReq{ + Uuid: uuid, + Ts: req.Ts, + }) + if err != nil { + return nil, fmt.Errorf("error: %w", err) + } + + reply := &v1.CheckInfoReply{ + CanCheckSubmit: int32(x.CanCheckSubmit), + CheckSubmit: x.CheckSubmit, + CheckResult: x.CheckResult, + CheckPayout: x.CheckPayout, + CheckCoin: config.PublicCheckCoin, + CanCheckPayOut: x.CanCheckPayOut, + CheckResultFailedDesc: x.CheckResultFailedDesc, + } + + log.Infof("CheckInfo: reply[%+v]", reply) + + return reply, nil +} + +func (s *EonlineService) AddChat(ctx context.Context, req *v1.AddChatReq) (*v1.AddChatReply, error) { + log.Infof("AddChat: req[%+v]", req) + + // 验证签名 + err := s.validateSignRequest( + v1.PayInitReq{ + Platform: req.Platform, + Deviceid: req.Deviceid, + Version: req.Version, + Ip: req.Ip, + Ts: req.Ts, + Sign: req.Sign, + }, biz.SignFixedParameters) + if err != nil { + return nil, fmt.Errorf("validate request error: %w", err) + } + if !checkStrSeconds(req.Ts) { + reply := &v1.AddChatReply{Result: 2} + return reply, nil + } + if s.conf.Server.Env != "qa" { + req.Ts = strconv.Itoa(int(time.Now().Unix())) + } + + // ver := config.Version2int(&req.Version) + // if ver < config.PublicVersionMin { + // return nil, fmt.Errorf("version [%s] not support", req.Version) + // } + + // 生成唯一用户ID + uuid := biz.GenPayoutUuid(req.Deviceid) + + if uuid != req.Uuid { + return nil, fmt.Errorf("uuid [%s] error", req.Uuid) + } + + reply := &v1.AddChatReply{ + Uuid: uuid, + Result: 0, + } + + biz.AddChat(req) + + reply.Lst = biz.GetChat(req.TimeStamp) + + // log.Infof("AddChat: reply[%+v]", reply) + + return reply, nil +} +func (s *EonlineService) GetChat(ctx context.Context, req *v1.GetChatReq) (*v1.GetChatReply, error) { + log.Infof("GetChat: req[%+v]", req) + + // 验证签名 + err := s.validateSignRequest( + v1.PayInitReq{ + Platform: req.Platform, + Deviceid: req.Deviceid, + Version: req.Version, + Ip: req.Ip, + Ts: req.Ts, + Sign: req.Sign, + }, biz.SignFixedParameters) + if err != nil { + return nil, fmt.Errorf("validate request error: %w", err) + } + if s.conf.Server.Env != "qa" { + req.Ts = strconv.Itoa(int(time.Now().Unix())) + } + + // ver := config.Version2int(&req.Version) + // if ver < config.PublicVersionMin { + // return nil, fmt.Errorf("version [%s] not support", req.Version) + // } + + // 生成唯一用户ID + uuid := biz.GenPayoutUuid(req.Deviceid) + + if uuid != req.Uuid { + return nil, fmt.Errorf("uuid [%s] error", req.Uuid) + } + + reply := &v1.GetChatReply{ + Uuid: uuid, + } + + reply.Lst = biz.GetChat(req.TimeStamp) + + // log.Infof("AddChat: reply[%+v]", reply) + + return reply, nil +} diff --git a/app/eonline/internal/service/fenHong.go b/app/eonline/internal/service/fenHong.go new file mode 100644 index 0000000..9b2b831 --- /dev/null +++ b/app/eonline/internal/service/fenHong.go @@ -0,0 +1,4 @@ +package service + +func onBreatheFenHong() { +} diff --git a/app/eonline/internal/service/service.go b/app/eonline/internal/service/service.go new file mode 100644 index 0000000..69702fb --- /dev/null +++ b/app/eonline/internal/service/service.go @@ -0,0 +1,101 @@ +package service + +import ( + "fmt" + "reflect" + "strconv" + "strings" + "time" + + "sandc/pkg/utils" + + "github.com/google/wire" + "github.com/shopspring/decimal" +) + +// ProviderSet is service providers. +var ProviderSet = wire.NewSet(NewEonlineService) + +// 定义加密key Secretkey +const Secretkey = "eonline~#*^%$@!~0702" + +// validateSignRequest 验证参数请求 +// Ts, Sign 参数必备 +// signFixedParameters 签名固定参数 + +func (c *EonlineService) validateSignRequest(in any, signFixedParameters []string) error { + var ( + // ts int64 + sign string + // err error + ) + defer func() { + if err := recover(); err != nil { + fmt.Println("validateRequest panic: ", err) + } + }() + + v := reflect.ValueOf(in) + + // 获取签名 + if signValue, ok := v.FieldByName("Sign").Interface().(string); ok { + sign = signValue + } + if sign == "" { + return fmt.Errorf("Sign不能为空") + } + + // // 获取时间戳,ts 字符串转换为int64 + // if tsValue, ok := v.FieldByName("Ts").Interface().(string); ok { + // ts, err = strconv.ParseInt(tsValue, 10, 64) + // if err != nil { + // return fmt.Errorf("Ts转换失败") + // } + // } + // if ts == 0 { + // return fmt.Errorf("Ts不能为空") + // } + // + // currentTime := time.Now().Unix() + // // 比较时间戳是否超过60秒, QA环境不判断次时间 + // if currentTime-ts > 60 || currentTime-ts < -60 { + // fmt.Println("ts invalid: ", ts, currentTime, currentTime-ts) + // if c.conf.Server.Env != "qa" { + // return fmt.Errorf("ts invalid") + // } + // } + secretStr := Secretkey + for _, sfp := range signFixedParameters { + fieldName := strings.ToLower(sfp) + tmpValue := "" + switch vtype := v.FieldByName(sfp).Interface().(type) { + case string: + tmpValue = vtype + case float32: + tmpValue = decimal.NewFromFloat32(vtype).String() + case float64: + tmpValue = decimal.NewFromFloat(vtype).String() + case int64: + tmpValue = decimal.NewFromInt(vtype).String() + case bool: + tmpValue = strconv.FormatBool(vtype) + case int32: + tmpValue = decimal.NewFromInt32(vtype).String() + default: + return fmt.Errorf("参数错误: %s", sfp) + } + + secretStr += fmt.Sprintf("%s=%s", fieldName, tmpValue) + } + + secretSignStr := utils.MD5Hex([]byte(secretStr)) + if secretSignStr != sign { + fmt.Println("---------签名错误---------", time.Now().Format("2006-01-02 15:04:05")) + fmt.Println("加密前: ", secretStr) + fmt.Println("加密后: ", secretSignStr) + fmt.Println("sign: ", sign) + return fmt.Errorf("签名错误") + } + + return nil +} diff --git a/app/eonline/internal/service/timer.go b/app/eonline/internal/service/timer.go new file mode 100644 index 0000000..28780c5 --- /dev/null +++ b/app/eonline/internal/service/timer.go @@ -0,0 +1,72 @@ +package service + +import ( + "runtime" + "time" + + "sandc/app/eonline/internal/biz" + + "github.com/go-kratos/kratos/v2/log" +) + +var ( + timeoutTimerPer10Second int64 + timer10second *time.Timer + timer10mintus *time.Timer +) + +func InitTimer() { + onTimerPer10Second() + onTimerPer10Minute() +} + +func onTimerPer10Second() { + const duration = 10 * time.Second + timer10second = time.AfterFunc(duration, func() { + defer func() { + if r := recover(); r != nil { + buf := make([]byte, 4096) + l := runtime.Stack(buf, false) + log.Infof("onTimerPer10Second error: %v: %s", r, buf[:l]) + } + }() + defer onTimerPer10Second() + + begin := time.Now().UnixNano() + + onBreatheFenHong() + + end := time.Now().UnixNano() + delta := end - begin + if delta > timeoutTimerPer10Second { + log.Infof("onTimerPer10Second: begin[%dns] end[%dns], spend[%dms %dns] > [%dns]", begin, end, delta/biz.NS2MS, delta%biz.NS2MS, timeoutTimerPer10Second) + } + }) +} + +func onTimerPer10Minute() { + const duration = 10*time.Minute + 1*time.Second + timer10mintus = time.AfterFunc(duration, func() { + defer func() { + if r := recover(); r != nil { + buf := make([]byte, 4096) + l := runtime.Stack(buf, false) + log.Infof("onTimerPer10Minute error: %v: %s", r, buf[:l]) + } + }() + defer onTimerPer10Minute() + + begin := time.Now().UnixNano() + + biz.SaveSvrDataOnTimer() + + end := time.Now().UnixNano() + delta := end - begin + log.Infof("onTimerPer10Minute: begin[%dns] end[%dns], spend[%dms %dns]", begin, end, delta/biz.NS2MS, delta%biz.NS2MS) + }) +} + +func OnDestroyTimer() { + timer10second.Stop() + timer10mintus.Stop() +} diff --git a/app/eonline/manifests/Dockerfile b/app/eonline/manifests/Dockerfile new file mode 100644 index 0000000..b397c0e --- /dev/null +++ b/app/eonline/manifests/Dockerfile @@ -0,0 +1,23 @@ +FROM golang:1.17 AS builder + +COPY . /src +WORKDIR /src/app/eonline +RUN GOPROXY=https://goproxy.cn,direct make build + +FROM debian:stable-slim + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + netbase \ + && rm -rf /var/lib/apt/lists/ \ + && apt-get autoremove -y && apt-get autoclean -y + +COPY --from=builder /src/app/eonline/bin /app + +WORKDIR /app + +EXPOSE 8001 +EXPOSE 9001 +VOLUME /data/conf + +CMD ["./server", "-conf", "/data/conf"] \ No newline at end of file diff --git a/app/eonline/manifests/configmap.yaml b/app/eonline/manifests/configmap.yaml new file mode 100644 index 0000000..71240ec --- /dev/null +++ b/app/eonline/manifests/configmap.yaml @@ -0,0 +1,26 @@ +apiVersion: v1 +data: + conf.yaml: | + server: + http: + addr: 0.0.0.0:8001 + timeout: 1s + grpc: + addr: 0.0.0.0:9001 + timeout: 1s + etcd: + addr: + - 127.0.0.1:2379 + trace_endpoint: http://127.0.0.1:14268/api/traces + data: + database: + driver: mysql + source: root:@tcp(127.0.0.1:3306)/eonline?charset=utf8mb4&parseTime=True&loc=UTC + redis: + addr: 127.0.0.1:6379 + read_timeout: 0.2s + write_timeout: 0.2s +kind: ConfigMap +metadata: + name: eonline-conf + namespace: default diff --git a/app/eonline/manifests/deployment.yaml b/app/eonline/manifests/deployment.yaml new file mode 100644 index 0000000..1555b7a --- /dev/null +++ b/app/eonline/manifests/deployment.yaml @@ -0,0 +1,75 @@ +apiVersion: apps/v1 # for versions before 1.8.0 use apps/v1beta1 +kind: Deployment +metadata: + name: eonline-deployment + labels: + app: eonline +spec: + replicas: 2 + selector: + matchLabels: + app: eonline + template: + metadata: + labels: + app: eonline + spec: + containers: + - name: eonline + image: nginx + imagePullPolicy: IfNotPresent + ports: + - containerPort: 80 + resources: + requests: + memory: "128Mi" + cpu: "128m" + limits: + memory: "256Mi" + cpu: "256m" + volumeMounts: + - mountPath: /data/conf + name: eonline-conf + volumes: + - configMap: + defaultMode: 420 + name: eonline-conf + name: eonline-conf +--- +apiVersion: apps/v1 # for versions before 1.8.0 use apps/v1beta1 +kind: Deployment +metadata: + name: eonline-worker-deployment + labels: + app: eonline-worker +spec: + replicas: 2 + selector: + matchLabels: + app: eonline-worker + template: + metadata: + labels: + app: eonline-worker + spec: + containers: + - name: eonline-worker + image: nginx + imagePullPolicy: IfNotPresent + ports: + - containerPort: 80 + resources: + requests: + memory: "128Mi" + cpu: "128m" + limits: + memory: "256Mi" + cpu: "256m" + volumeMounts: + - mountPath: /data/conf + name: eonline-conf + volumes: + - configMap: + defaultMode: 420 + name: eonline-conf + name: eonline-conf \ No newline at end of file diff --git a/app/eonline/manifests/ingress-grpc.yaml b/app/eonline/manifests/ingress-grpc.yaml new file mode 100644 index 0000000..0978cfd --- /dev/null +++ b/app/eonline/manifests/ingress-grpc.yaml @@ -0,0 +1,26 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + annotations: + nginx.ingress.kubernetes.io/ssl-redirect: "true" + nginx.ingress.kubernetes.io/backend-protocol: "GRPC" + cert-manager.io/cluster-issuer: "letsencrypt-prod" + name: eonline-grpc-ingress + namespace: default +spec: + ingressClassName: nginx + tls: + - hosts: + - eonline.example.com + secretName: eonline-tls + rules: + - host: eonline.example.com + http: + paths: + - path: /eonline. + pathType: Prefix + backend: + service: + name: eonline-svc + port: + number: 9001 diff --git a/app/eonline/manifests/ingress.yaml b/app/eonline/manifests/ingress.yaml new file mode 100644 index 0000000..d81d930 --- /dev/null +++ b/app/eonline/manifests/ingress.yaml @@ -0,0 +1,25 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + nginx.ingress.kubernetes.io/ssl-redirect: "true" + name: eonline-ingress + namespace: default +spec: + ingressClassName: nginx + tls: + - hosts: + - eonline.example.com + secretName: eonline-tls + rules: + - host: eonline.example.com + http: + paths: + - path: /eonline + pathType: Prefix + backend: + service: + name: eonline-svc + port: + number: 8001 \ No newline at end of file diff --git a/app/eonline/manifests/service.yaml b/app/eonline/manifests/service.yaml new file mode 100644 index 0000000..ce94b73 --- /dev/null +++ b/app/eonline/manifests/service.yaml @@ -0,0 +1,25 @@ +apiVersion: v1 +kind: Service +metadata: + name: eonline-svc + namespace: default +spec: + clusterIP: None + clusterIPs: + - None + internalTrafficPolicy: Cluster + ipFamilies: + - IPv4 + ports: + - name: http8001 + port: 8001 + protocol: TCP + targetPort: 8001 + - name: grpc9001 + port: 9001 + protocol: TCP + targetPort: 9001 + selector: + app: eonline + sessionAffinity: None + type: ClusterIP diff --git a/app_makefile b/app_makefile new file mode 100644 index 0000000..3e3d708 --- /dev/null +++ b/app_makefile @@ -0,0 +1,118 @@ +GOPATH:=$(shell go env GOPATH) +VERSION=$(shell git describe --tags --always) +APP_RELATIVE_PATH=$(shell a=`basename $$PWD` && echo $$a) +INTERNAL_PROTO_FILES=$(shell find internal -name *.proto) +API_PROTO_FILES=$(shell cd ../../api/$(APP_RELATIVE_PATH) && find . -name *.proto) +KRATOS_VERSION=$(shell go mod graph |grep go-kratos/kratos/v2 |head -n 1 |awk -F '@' '{print $$2}') +KRATOS=$(GOPATH)/pkg/mod/github.com/go-kratos/kratos/v2@$(KRATOS_VERSION) +APP_NAME=$(shell echo $(APP_RELATIVE_PATH)) +DOCKER_IMAGE=$(shell echo $(APP_NAME) |awk -F '@' '{print "sandc/" $$0 ":0.1.0"}') + +.PHONY: init +# init env +init: + go get -u github.com/google/wire/cmd/wire + go get -u github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 + go get -u github.com/go-kratos/kratos/cmd/kratos/v2 + go get -u google.golang.org/protobuf/cmd/protoc-gen-go + go get -u google.golang.org/grpc/cmd/protoc-gen-go-grpc + go get -u github.com/go-kratos/kratos/cmd/protoc-gen-go-http/v2 + go get -u github.com/go-kratos/kratos/cmd/protoc-gen-go-errors/v2 + go get -u github.com/google/gnostic/cmd/protoc-gen-openapi@v0.6.1 + go get -u github.com/envoyproxy/protoc-gen-validate + pip install cookiecutter jinja2_strcase +.PHONY: grpc +# generate grpc code +grpc: + cd ../../api/$(APP_RELATIVE_PATH) && protoc --proto_path=. \ + --proto_path=../../third_party \ + --go_out=paths=source_relative:. \ + --go-grpc_out=paths=source_relative:. \ + --go-http_out=paths=source_relative:. \ + --validate_out=paths=source_relative,lang=go:. \ + --openapi_out==paths=source_relative:. \ + $(API_PROTO_FILES) + +.PHONY: errors +# generate errors code +errors: + cd ../../api/$(APP_RELATIVE_PATH) && protoc --proto_path=. \ + --proto_path=../../third_party \ + --go_out=paths=source_relative:. \ + --go-errors_out=paths=source_relative:. \ + $(API_PROTO_FILES) + +.PHONY: proto +# generate internal proto struct +proto: + protoc --proto_path=. \ + --proto_path=../../third_party \ + --go_out=paths=source_relative:. \ + $(INTERNAL_PROTO_FILES) + +.PHONY: generate +# generate client code +generate: + go generate ./... + +.PHONY: build +# build +build: + mkdir -p bin/ && go build -ldflags "-X main.Version=$(VERSION)" -o ./bin/ ./... +# build qa for linux +build-linux: + mkdir -p bin/ && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-X main.Version=$(VERSION)" -o ./bin/ ./... + +.PHONY: test +# test +test: + go test -v ./... -cover + +.PHONY: run +run: + cd cmd/server/ && go run . + +.PHONY: ent +ent: + cd internal/data/ && ent generate ./ent/schema + +.PHONY: docker +docker: + cd ../.. && docker build -f Dockerfile --build-arg APP_RELATIVE_PATH=$(APP_RELATIVE_PATH) -t $(DOCKER_IMAGE) . + +define execute-wire +cd $(1) && wire gen + +endef +.PHONY: wire +SUBDIRS := $(shell find cmd/* -type d) +# generate wire +wire: + $(foreach x,$(SUBDIRS),$(call execute-wire,$(x))) + +.PHONY: api +# generate api proto +api: grpc errors + +.PHONY: all +# generate all +all: api proto generate build test + +# show help +help: + @echo '' + @echo 'Usage:' + @echo ' make [target]' + @echo '' + @echo 'Targets:' + @awk '/^[a-zA-Z\-\_0-9]+:/ { \ + helpMessage = match(lastLine, /^# (.*)/); \ + if (helpMessage) { \ + helpCommand = substr($$1, 0, index($$1, ":")-1); \ + helpMessage = substr(lastLine, RSTART + 2, RLENGTH); \ + printf "\033[36m%-22s\033[0m %s\n", helpCommand,helpMessage; \ + } \ + } \ + { lastLine = $$0 }' $(MAKEFILE_LIST) + +.DEFAULT_GOAL := help diff --git a/build_docker.sh b/build_docker.sh new file mode 100644 index 0000000..51b73a4 --- /dev/null +++ b/build_docker.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +base_repo=registry.cn-beijing.aliyuncs.com/touka/sandc +app_name=$1 +version=$2 + +if [ -z $app_name ]; then + read -p "Enter app name: " app_name + read -p "Enter app version: " version +fi + +if [ -z $app_name ]; then + echo "app name is empty, exit" + exit 1 +fi + +if [ ! -d "app/$app_name" ]; then + echo "app directory is not exists, exit" + exit 1 +fi + +if [ -z $version ]; then + version=`date '+%y%m%d-%H%M'` +fi + +#docker build -t $app_name:$version --build-arg APP_RELATIVE_PATH=$app_name . +repo=$base_repo:$app_name-$version +docker build -t $repo -f app/$app_name/manifests/Dockerfile . +echo "$repo build success!" +read -p "push remote registry?[yes/no]: " remote +if [[ $(echo $remote | awk '{print tolower($1)}') == "yes" ]]; then + docker push $repo +fi \ No newline at end of file diff --git a/create_app.sh b/create_app.sh new file mode 100644 index 0000000..7d1b9b8 --- /dev/null +++ b/create_app.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +read -p "app_name [example]: " app_name +read -p "go_module [sandc]: " sandc +read -p "db_driver, mysql or postgres [postgres]: " db_driver +if [[ $app_name == "" ]]; then + app_name=example +fi +if [[ $go_module == "" ]]; then + go_module=sandc +fi +if [[ $db_driver == "" ]]; then + db_driver=postgres +fi +if [ -d "app/$app_name" ]; then + echo "app: $app_name already exists" + exit 1 +fi + +cookiecutter --no-input .tmpl app_name=$app_name go_module=$go_module db_driver=$db_driver +mv $app_name/api/$app_name api/ +mv $app_name/app/$app_name app/ +rm -rf $app_name \ No newline at end of file diff --git a/deploy_link.sh b/deploy_link.sh new file mode 100644 index 0000000..f71cf16 --- /dev/null +++ b/deploy_link.sh @@ -0,0 +1,25 @@ +#!/usr/bin/bash + +version=`date -d $SUP_TIME '+%y%m%d%H%M%S'` +export DEPLOY_TO=$DEPLOY_TO/$NAME +release_dir=$DEPLOY_TO/release +mkdir -p $release_dir +mkdir -p $DEPLOY_TO/bin + +cd /tmp/app/${NAME}/bin + +for execName in `ls` ; do + echo "----- $execName -----" + cp $execName $release_dir/$execName-$version + chmod +x $release_dir/$execName-$version + rm -rf $DEPLOY_TO/bin/$execName + ln -s $release_dir/$execName-$version $DEPLOY_TO/bin/$execName + echo "link version: $version to latest success" + count=`ls $release_dir/$execName* |wc -l` + echo "counter old version $release_dir was $count" + if [ $count -gt 20 ]; then + cd $release_dir + ls -t $release_dir/$execName* | awk 'NR>20' | xargs rm + echo "clean old version" + fi +done diff --git a/eonline.sql b/eonline.sql new file mode 100644 index 0000000..956c778 --- /dev/null +++ b/eonline.sql @@ -0,0 +1,84 @@ +/* + Navicat Premium Data Transfer + + Source Server : local + Source Server Type : MySQL + Source Server Version : 80034 + Source Host : localhost:3306 + Source Schema : eonline2 + + Target Server Type : MySQL + Target Server Version : 80034 + File Encoding : 65001 + + Date: 13/09/2023 14:46:45 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for bonus_record +-- ---------------------------- +DROP TABLE IF EXISTS `bonus_record`; +CREATE TABLE `bonus_record` ( + `id` int UNSIGNED NOT NULL COMMENT '年月日时间ID', + `dau` int UNSIGNED NOT NULL COMMENT '原始dau', + `pass` int UNSIGNED NOT NULL COMMENT '通关总份额', + `coin` int UNSIGNED NOT NULL COMMENT '分红每一份额美分数', + `bonus` int UNSIGNED NOT NULL COMMENT '服务器分红总额美分', + `bonus_cur` int UNSIGNED NOT NULL COMMENT '服务器实际分红总额美分', + `created_at` datetime NULL DEFAULT NULL COMMENT '创建时间', + `updated_at` datetime NOT NULL COMMENT '更新时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for payout_record +-- ---------------------------- +DROP TABLE IF EXISTS `payout_record`; +CREATE TABLE `payout_record` ( + `id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '提现记录ID', + `payout_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '支付第三方id', + `record_no` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '提现唯一编码', + `channel` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '支付渠道', + `uuid` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '提现用户唯一编码', + `account` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '提现账号', + `item_id` int UNSIGNED NULL DEFAULT NULL COMMENT '提现的item对应id', + `amount` decimal(8, 2) NULL DEFAULT NULL COMMENT '提现金额', + `currency` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '货币单位', + `status` tinyint UNSIGNED NOT NULL DEFAULT 0 COMMENT '提现状态 1:提现中,2:提现成功,3:提现失败', + `ecpm` tinyint UNSIGNED NOT NULL COMMENT 'ecpm等级', + `version` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '版本号', + `created_at` datetime NULL DEFAULT NULL COMMENT '创建时间', + `updated_at` datetime NOT NULL COMMENT '更新时间', + `fail` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '提现失败错误', + PRIMARY KEY (`id`) USING BTREE, + INDEX `payout_record_uuid_IDX`(`uuid` ASC) USING BTREE, + INDEX `payout_record_status_IDX`(`status` ASC) USING BTREE, + INDEX `payout_record_account_IDX`(`account` ASC) USING BTREE, + INDEX `payout_record_record_no_IDX`(`record_no` ASC) USING BTREE, + INDEX `payout_record_item_id_IDX`(`item_id` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '提现记录' ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for payout_user +-- ---------------------------- +DROP TABLE IF EXISTS `payout_user`; +CREATE TABLE `payout_user` ( + `id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '用户ID', + `platform` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '平台', + `ip` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'ip地址', + `country` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '国家', + `device_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '设备ID', + `version` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '版本号', + `uuid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户唯一ID,根据ip, country,device_id生成', + `login_days` smallint UNSIGNED NOT NULL COMMENT '登录天数', + `created_at` datetime NULL DEFAULT NULL COMMENT '创建时间', + `updated_at` datetime NOT NULL COMMENT '更新时间', + `clientData` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '客户端数据', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `payout_user_UN`(`uuid` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '提现用户信息' ROW_FORMAT = DYNAMIC; + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..44bcf37 --- /dev/null +++ b/go.mod @@ -0,0 +1,138 @@ +module sandc + +go 1.21 + +require ( + github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.2 + github.com/alibabacloud-go/ecs-20140526/v3 v3.0.5 + github.com/alibabacloud-go/sts-20150401/v2 v2.0.1 + github.com/aliyun/aliyun-oss-go-sdk v2.2.7+incompatible + github.com/dgrijalva/jwt-go v3.2.0+incompatible + github.com/envoyproxy/protoc-gen-validate v0.9.1 + github.com/fananchong/go-redis-orm.v2 v0.0.0-20190605162020-575dd961c577 + github.com/go-kratos/kratos/contrib/log/zap/v2 v2.0.0-20230316050305-d05729399e23 + github.com/go-kratos/kratos/contrib/registry/etcd/v2 v2.0.0-20230316050305-d05729399e23 + github.com/go-kratos/kratos/v2 v2.6.1 + github.com/go-kratos/swagger-api v1.0.1 + github.com/go-redis/redis/extra/redisotel/v8 v8.11.5 + github.com/go-redis/redis/v8 v8.11.5 + github.com/google/uuid v1.3.0 + github.com/google/wire v0.5.0 + github.com/hibiken/asynq v0.24.0 + github.com/lithammer/shortuuid v3.0.0+incompatible + github.com/mojocn/base64Captcha v1.3.5 + github.com/natefinch/lumberjack v2.0.0+incompatible + github.com/oschwald/maxminddb-golang v1.11.0 + github.com/shopspring/decimal v1.3.1 + github.com/stretchr/testify v1.8.4 + github.com/tx7do/kratos-transport v1.0.6-0.20230311115932-369dc7e80d1d + github.com/tx7do/kratos-transport/broker/kafka v0.0.0-20230130033939-27a8402223f6 + github.com/tx7do/kratos-transport/transport/kafka v0.0.0-20230130033939-27a8402223f6 + github.com/uptrace/opentelemetry-go-extra/otelgorm v0.1.21 + github.com/xxtea/xxtea-go v1.0.0 + go.etcd.io/etcd/client/v3 v3.5.7 + go.opentelemetry.io/otel v1.14.0 + go.opentelemetry.io/otel/exporters/jaeger v1.14.0 + go.opentelemetry.io/otel/sdk v1.14.0 + go.uber.org/zap v1.24.0 + golang.org/x/crypto v0.8.0 + golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb + golang.org/x/oauth2 v0.4.0 + golang.org/x/sync v0.1.0 + golang.org/x/text v0.9.0 + google.golang.org/api v0.103.0 + google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa + google.golang.org/grpc v1.53.0 + google.golang.org/protobuf v1.30.0 + gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df + gorm.io/driver/mysql v1.4.7 + gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11 +) + +require ( + github.com/FZambia/sentinel v1.0.0 // indirect + github.com/antlabs/stl v0.0.1 // indirect + github.com/fananchong/goredis v0.0.0-20181224141957-8c4a4601c4c9 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.5.0 // indirect + github.com/mna/redisc v1.1.3 // indirect + github.com/paemuri/brdoc v1.1.2 // indirect +) + +require ( + cloud.google.com/go/compute v1.15.1 // indirect + cloud.google.com/go/compute/metadata v0.2.3 // indirect + github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 // indirect + github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68 // indirect + github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect + github.com/alibabacloud-go/openapi-util v0.1.0 // indirect + github.com/alibabacloud-go/tea v1.1.19 // indirect + github.com/alibabacloud-go/tea-utils v1.3.1 // indirect + github.com/alibabacloud-go/tea-utils/v2 v2.0.1 // indirect + github.com/alibabacloud-go/tea-xml v1.1.2 // indirect + github.com/aliyun/credentials-go v1.1.2 // indirect + github.com/antlabs/timer v0.0.11 + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/clbanning/mxj/v2 v2.5.5 // indirect + github.com/coreos/go-semver v0.3.0 // indirect + github.com/coreos/go-systemd/v22 v22.3.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/ghodss/yaml v1.0.0 // indirect + github.com/go-kratos/aegis v0.1.4 // indirect + github.com/go-kratos/grpc-gateway/v2 v2.5.1-0.20210811062259-c92d36e434b1 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-playground/form/v4 v4.2.0 // indirect + github.com/go-redis/redis/extra/rediscmd/v8 v8.11.5 // indirect + github.com/go-sql-driver/mysql v1.7.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect + github.com/golang/glog v1.0.0 // indirect + github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect + github.com/gomodule/redigo v2.0.0+incompatible + github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect + github.com/googleapis/gax-go/v2 v2.7.0 // indirect + github.com/gorilla/mux v1.8.0 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/json-iterator/go v1.1.11 // indirect + github.com/klauspost/compress v1.15.15 // indirect + github.com/kr/pretty v0.3.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/openzipkin/zipkin-go v0.4.1 // indirect + github.com/pierrec/lz4/v4 v4.1.17 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rakyll/statik v0.1.7 // indirect + github.com/robfig/cron/v3 v3.0.1 // indirect + github.com/segmentio/kafka-go v0.4.38 // indirect + github.com/spf13/cast v1.4.1 // indirect + github.com/tjfoc/gmsm v1.3.2 // indirect + github.com/uptrace/opentelemetry-go-extra/otelsql v0.1.21 // indirect + github.com/xdg/scram v1.0.5 // indirect + github.com/xdg/stringprep v1.0.3 // indirect + go.etcd.io/etcd/api/v3 v3.5.7 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.7 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/otel/exporters/zipkin v1.14.0 // indirect + go.opentelemetry.io/otel/metric v0.36.0 // indirect + go.opentelemetry.io/otel/trace v1.14.0 // indirect + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/multierr v1.6.0 // indirect + golang.org/x/image v0.5.0 // indirect + golang.org/x/net v0.9.0 // indirect + golang.org/x/sys v0.9.0 // indirect + golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect + google.golang.org/appengine v1.6.7 // indirect + gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gopkg.in/ini.v1 v1.56.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace github.com/gomodule/redigo v2.0.0+incompatible => github.com/gomodule/redigo v1.8.10-0.20230820113435-a60882bf9e77 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..08e8e5e --- /dev/null +++ b/go.sum @@ -0,0 +1,908 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.107.0 h1:qkj22L7bgkl6vIeZDlOY2po43Mx/TIa2Wsa7VR+PEww= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v1.15.1 h1:7UGq3QknM33pw5xATlpzeoomNxsacIVvTqTTvbfajmE= +cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/FZambia/sentinel v1.0.0 h1:KJ0ryjKTZk5WMp0dXvSdNqp3lFaW1fNFuEYfrkLOYIc= +github.com/FZambia/sentinel v1.0.0/go.mod h1:ytL1Am/RLlAoAXG6Kj5LNuw/TRRQrv2rt2FT26vP5gI= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 h1:iC9YFYKDGEy3n/FtqJnOkZsene9olVspKmkX5A2YBEo= +github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.2 h1:2kR1YkvQloHUstmPcG0Sjk24zTKbza7izzJfJNwBFSs= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.2/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ= +github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68 h1:NqugFkGxx1TXSh/pBcU00Y6bljgDPaFdh5MUSeJ7e50= +github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY= +github.com/alibabacloud-go/ecs-20140526/v3 v3.0.5 h1:LqmPBXU7rPnQT3ULjn2GJPZhjoCj84xX6FLrFdFKgfE= +github.com/alibabacloud-go/ecs-20140526/v3 v3.0.5/go.mod h1:YyFjze22ksIkfCsB1bw+RSKoFkBkk3/cMQXoyQ435SA= +github.com/alibabacloud-go/endpoint-util v1.1.0 h1:r/4D3VSw888XGaeNpP994zDUaxdgTSHBbVfZlzf6b5Q= +github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE= +github.com/alibabacloud-go/openapi-util v0.0.11/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws= +github.com/alibabacloud-go/openapi-util v0.1.0 h1:0z75cIULkDrdEhkLWgi9tnLe+KhAFE/r5Pb3312/eAY= +github.com/alibabacloud-go/openapi-util v0.1.0/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws= +github.com/alibabacloud-go/sts-20150401/v2 v2.0.1 h1:CevZp0VdG7Q+1J3qwNj+JL7ztKxsL27+tknbdTK9Y6M= +github.com/alibabacloud-go/sts-20150401/v2 v2.0.1/go.mod h1:8wJW1xC4mVcdRXzOvWJYfCCxmvFzZ0VB9iilVjBeWBc= +github.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg= +github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= +github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= +github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= +github.com/alibabacloud-go/tea v1.1.19 h1:Xroq0M+pr0mC834Djj3Fl4ZA8+GGoA0i7aWse1vmgf4= +github.com/alibabacloud-go/tea v1.1.19/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= +github.com/alibabacloud-go/tea-utils v1.3.1 h1:iWQeRzRheqCMuiF3+XkfybB3kTgUXkXX+JMrqfLeB2I= +github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE= +github.com/alibabacloud-go/tea-utils/v2 v2.0.0/go.mod h1:U5MTY10WwlquGPS34DOeomUGBB0gXbLueiq5Trwu0C4= +github.com/alibabacloud-go/tea-utils/v2 v2.0.1 h1:K6kwgo+UiYx+/kr6CO0PN5ACZDzE3nnn9d77215AkTs= +github.com/alibabacloud-go/tea-utils/v2 v2.0.1/go.mod h1:U5MTY10WwlquGPS34DOeomUGBB0gXbLueiq5Trwu0C4= +github.com/alibabacloud-go/tea-xml v1.1.2 h1:oLxa7JUXm2EDFzMg+7oRsYc+kutgCVwm+bZlhhmvW5M= +github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8= +github.com/aliyun/aliyun-oss-go-sdk v2.2.7+incompatible h1:KpbJFXwhVeuxNtBJ74MCGbIoaBok2uZvkD7QXp2+Wis= +github.com/aliyun/aliyun-oss-go-sdk v2.2.7+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= +github.com/aliyun/credentials-go v1.1.2 h1:qU1vwGIBb3UJ8BwunHDRFtAhS6jnQLnde/yk0+Ih2GY= +github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/antlabs/stl v0.0.1 h1:TRD3csCrjREeLhLoQ/supaoCvFhNLBTNIwuRGrDIs6Q= +github.com/antlabs/stl v0.0.1/go.mod h1:wvVwP1loadLG3cRjxUxK8RL4Co5xujGaZlhbztmUEqQ= +github.com/antlabs/timer v0.0.11 h1:z75oGFLeTqJHMOcWzUPBKsBbQAz4Ske3AfqJ7bsdcwU= +github.com/antlabs/timer v0.0.11/go.mod h1:JNV8J3yGvMKhCavGXgj9HXrVZkfdQyKCcqXBT8RdyuU= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E= +github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b h1:ACGZRIr7HsgBKHsueQ1yM4WaVaXh21ynwqsF8M8tXhA= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/go-control-plane v0.10.3 h1:xdCVXxEe0Y3FQith+0cj2irwZudqGYvecuLB1HtdexY= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v0.9.1 h1:PS7VIOgmSVhWUEeZwTe7z7zouA22Cr590PzXKbZHOVY= +github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= +github.com/fananchong/go-redis-orm.v2 v0.0.0-20190605162020-575dd961c577 h1:IdO+sF6S14HpqDsk7ImPMBOoCRgG7zKC9yOkyWINbgs= +github.com/fananchong/go-redis-orm.v2 v0.0.0-20190605162020-575dd961c577/go.mod h1:HgSap/1Tq/6LazUQ/g4R25n0GgArz3eDdu3NM8ddEU8= +github.com/fananchong/goredis v0.0.0-20181224141957-8c4a4601c4c9 h1:8TZIKl6eywKmlNw3JGUSnvhNwuUdYiavh4Mp/DJbSXo= +github.com/fananchong/goredis v0.0.0-20181224141957-8c4a4601c4c9/go.mod h1:5LLIOz1tkD4ZKNwGVP2Ns9DlF4bCPsi3a5J7GkwuJQo= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kratos/aegis v0.1.4 h1:rVLXJNohV1d2GUFi7TwPReF23clhaAtAzrz03IP6GMo= +github.com/go-kratos/aegis v0.1.4/go.mod h1:Lk2PFCRyeYiXMJhCYgvJLXdc5AZEDk1Pik66SAIv0gY= +github.com/go-kratos/grpc-gateway/v2 v2.5.1-0.20210811062259-c92d36e434b1 h1:jPqlxMJEoi8Yv4WIAhQNKczdKjADox8WKrTp3bJNjFM= +github.com/go-kratos/grpc-gateway/v2 v2.5.1-0.20210811062259-c92d36e434b1/go.mod h1:qV5/s5A7wfY7GkNzptcUFRGAG4Y8H4mlXpSrOkDEIk0= +github.com/go-kratos/kratos/contrib/log/zap/v2 v2.0.0-20230316050305-d05729399e23 h1:SRqUeAG5GiOA0jcegzQ4INLbpafeEJnxsLbrulv4OhY= +github.com/go-kratos/kratos/contrib/log/zap/v2 v2.0.0-20230316050305-d05729399e23/go.mod h1:paNMxG2u6dfV98d4X/SbbCdxQu0Zlj/QRiTulivJPP0= +github.com/go-kratos/kratos/contrib/registry/etcd/v2 v2.0.0-20230316050305-d05729399e23 h1:JANkYdIVWJB8/oTh353lzPZD4UVgJvr/haPN5I9YoiI= +github.com/go-kratos/kratos/contrib/registry/etcd/v2 v2.0.0-20230316050305-d05729399e23/go.mod h1:V22LCFhAGC0+uEgiUXDes0d/mNdEH7zmyejGo9LZozI= +github.com/go-kratos/kratos/v2 v2.0.3/go.mod h1:Hgl0YPry9YyLtwTTfwLfowPKg+YS0dgZ06O5NHqz5hE= +github.com/go-kratos/kratos/v2 v2.6.1 h1:4GSy7I7YGF93c1W83XkWAXNqY7JzNdC3t4l501rl0Xg= +github.com/go-kratos/kratos/v2 v2.6.1/go.mod h1:OT/2NR0jpfxMgdTdIew8of9cGBab0UKaZRadcgTgqS0= +github.com/go-kratos/swagger-api v1.0.1 h1:zBagw2tWA+icYrRPFqM3jFL60nO743ZiIvuiI6CpV6E= +github.com/go-kratos/swagger-api v1.0.1/go.mod h1:KMYylgeNaApBgPYIF6kIP1kjGFgI4aDQNbQNBTqaewI= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/form/v4 v4.2.0 h1:N1wh+Goz61e6w66vo8vJkQt+uwZSoLz50kZPJWR8eic= +github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U= +github.com/go-redis/redis/extra/rediscmd/v8 v8.11.5 h1:ftG8tp8SG81xyuL2woNEx5t2RZ8mOJuC2+tumi+/NR8= +github.com/go-redis/redis/extra/rediscmd/v8 v8.11.5/go.mod h1:s9f/6bSbS5r/jC2ozpWhWZ2GsoHDNf6iL+kZKnZnasc= +github.com/go-redis/redis/extra/redisotel/v8 v8.11.5 h1:BqyYJgvdSr2S/6O2l7zmCj26ocUTxDLgagsGIRfkS+Q= +github.com/go-redis/redis/extra/redisotel/v8 v8.11.5/go.mod h1:LlDT9RRdBgOrMGvFjT/m1+GrZAmRlBaMcM3UXHPWf8g= +github.com/go-redis/redis/v8 v8.11.2/go.mod h1:DLomh7y2e3ggQXQLd1YgmvIfecPJoFl7WU5SOQ/r06M= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v0.0.0-20210429001901-424d2337a529/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/gomodule/redigo v1.8.10-0.20230820113435-a60882bf9e77 h1:ActCh4pj3YOnDzI7u/OSImbF05RY5TAGjaj9TNEvtoQ= +github.com/gomodule/redigo v1.8.10-0.20230820113435-a60882bf9e77/go.mod h1:76M7UXKeDjV+neXtVEvMiDWnXT5nnUVyWrW1O4Fg8S8= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8= +github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= +github.com/googleapis/enterprise-certificate-proxy v0.2.0 h1:y8Yozv7SZtlU//QXbezB6QkpuE6jMD2/gfzk4AftXjs= +github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ= +github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0= +github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.5.0 h1:ajue7SzQMywqRjg2fK7dcpc0QhFGpTR2plWfV4EZWR4= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.5.0/go.mod h1:r1hZAcvfFXuYmcKyCJI9wlyOPIZUJl6FCB8Cpca/NLE= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hibiken/asynq v0.24.0 h1:r1CiSVYCy1vGq9REKGI/wdB2D5n/QmtzihYHHXOuBUs= +github.com/hibiken/asynq v0.24.0/go.mod h1:FVnRfUTm6gcoDkM/EjF4OIh5/06ergCPUO6pS2B2y+w= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= +github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lithammer/shortuuid v3.0.0+incompatible h1:NcD0xWW/MZYXEHa6ITy6kaXN5nwm/V115vj2YXfhS0w= +github.com/lithammer/shortuuid v3.0.0+incompatible/go.mod h1:FR74pbAuElzOUuenUHTK2Tciko1/vKuIKS9dSkDrA4w= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mna/redisc v1.1.3 h1:jPCXHNsZXQV9BtNNZWZlL8h29SRjSRUeqIdBQKPlykw= +github.com/mna/redisc v1.1.3/go.mod h1:GXeOb7zyYKiT+K8MKdIiJvuv7MfhDoQGcuzfiJQmqQI= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mojocn/base64Captcha v1.3.5 h1:Qeilr7Ta6eDtG4S+tQuZ5+hO+QHbiGAJdi4PfoagaA0= +github.com/mojocn/base64Captcha v1.3.5/go.mod h1:/tTTXn4WTpX9CfrmipqRytCpJ27Uw3G6I7NcP2WwcmY= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= +github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= +github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= +github.com/openzipkin/zipkin-go v0.4.1 h1:kNd/ST2yLLWhaWrkgchya40TJabe8Hioj9udfPcEO5A= +github.com/openzipkin/zipkin-go v0.4.1/go.mod h1:qY0VqDSN1pOBN94dBc6w2GJlWLiovAyg7Qt6/I9HecM= +github.com/oschwald/maxminddb-golang v1.11.0 h1:aSXMqYR/EPNjGE8epgqwDay+P30hCBZIveY0WZbAWh0= +github.com/oschwald/maxminddb-golang v1.11.0/go.mod h1:YmVI+H0zh3ySFR3w+oz8PCfglAFj3PuCmui13+P9zDg= +github.com/paemuri/brdoc v1.1.2 h1:jl3opOVRVvVr+ubc9DpzENe/d1uuYLNac4V1h7Jul/c= +github.com/paemuri/brdoc v1.1.2/go.mod h1:M0bbCy1qPGG0xou2ahCNXAntlkZ3Tl0w7NlC1v5fiZc= +github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc= +github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= +github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/segmentio/kafka-go v0.4.38 h1:iQdOBbUSdfuYlFpvjuALgj7N6DrdPA0HfB4AhREOdtg= +github.com/segmentio/kafka-go v0.4.38/go.mod h1:ikyuGon/60MN/vXFgykf7Zm8P5Be49gJU6vezwjnnhU= +github.com/shirou/gopsutil/v3 v3.21.8/go.mod h1:YWp/H8Qs5fVmf17v7JNZzA0mPJ+mS2e9JdiUF9LlKzQ= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.1.0 h1:MkTeG1DMwsrdH7QtLXy5W+fUxWq+vmb6cLmyJ7aRtF0= +github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= +github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM= +github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= +github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= +github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8= +github.com/tx7do/kratos-transport v1.0.6-0.20230311115932-369dc7e80d1d h1:Fv9zknmFYa21/z1cIDfkSDm6X0pDboXZoyZ0tWYw9dA= +github.com/tx7do/kratos-transport v1.0.6-0.20230311115932-369dc7e80d1d/go.mod h1:Ey6gJSFS5M0uYy5YLaVzglWwlnlaY9e7ZJLdAa2+lP4= +github.com/tx7do/kratos-transport/broker/kafka v0.0.0-20230130033939-27a8402223f6 h1:loGHmiiLvz+OItpzXarb/KYgiu4FSPgRCflMFtrz/dc= +github.com/tx7do/kratos-transport/broker/kafka v0.0.0-20230130033939-27a8402223f6/go.mod h1:y6h5rqMFDpiCNDxKBeH9RE9TFF+Q6jg7JcdzDgPZ2KM= +github.com/tx7do/kratos-transport/transport/kafka v0.0.0-20230130033939-27a8402223f6 h1:5b+Vs6kNw3iC0kGCuSSvT0fwqwATFNO4RHk7tC0pSTw= +github.com/tx7do/kratos-transport/transport/kafka v0.0.0-20230130033939-27a8402223f6/go.mod h1:nz9EdCG7YrKGhK1ou5yYN2UuhcCJgsjtYSo7VVESCyE= +github.com/uptrace/opentelemetry-go-extra/otelgorm v0.1.21 h1:PsmFQCoiULTVpXqFb2S/3E7WbA9ev6CkKFejJt2SFB0= +github.com/uptrace/opentelemetry-go-extra/otelgorm v0.1.21/go.mod h1:bI63nwuxN0yt5yz5kVaCMpY9+jwsngTFkXG/0ksDzvU= +github.com/uptrace/opentelemetry-go-extra/otelsql v0.1.21 h1:iHkIlTU2P3xbSbVJbAiHL9IT+ekYV5empheF+652yeQ= +github.com/uptrace/opentelemetry-go-extra/otelsql v0.1.21/go.mod h1:hiCFa1UeZITKXi8lhu2qwOD5LHXjdGMCUIQHbybxoF0= +github.com/xdg/scram v1.0.5 h1:TuS0RFmt5Is5qm9Tm2SoD89OPqe4IRiFtyFY4iwWXsw= +github.com/xdg/scram v1.0.5/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v1.0.3 h1:cmL5Enob4W83ti/ZHuZLuKD/xqJfus4fVPwE+/BDm+4= +github.com/xdg/stringprep v1.0.3/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= +github.com/xxtea/xxtea-go v1.0.0 h1:HUeElH97FsZS/+l+iroj0lhosKe47VMtdIL2Lz3MeFg= +github.com/xxtea/xxtea-go v1.0.0/go.mod h1:2uvuCBt0VXxijrX5ieiAeeNT2+2MIsrs1DI9iXz7OOQ= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.etcd.io/etcd/api/v3 v3.5.5/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8= +go.etcd.io/etcd/api/v3 v3.5.7 h1:sbcmosSVesNrWOJ58ZQFitHMdncusIifYcrBfwrlJSY= +go.etcd.io/etcd/api/v3 v3.5.7/go.mod h1:9qew1gCdDDLu+VwmeG+iFpL+QlpHTo7iubavdVDgCAA= +go.etcd.io/etcd/client/pkg/v3 v3.5.5/go.mod h1:ggrwbk069qxpKPq8/FKkQ3Xq9y39kbFR4LnKszpRXeQ= +go.etcd.io/etcd/client/pkg/v3 v3.5.7 h1:y3kf5Gbp4e4q7egZdn5T7W9TSHUvkClN6u+Rq9mEOmg= +go.etcd.io/etcd/client/pkg/v3 v3.5.7/go.mod h1:o0Abi1MK86iad3YrWhgUsbGx1pmTS+hrORWc2CamuhY= +go.etcd.io/etcd/client/v3 v3.5.5/go.mod h1:aApjR4WGlSumpnJ2kloS75h6aHUmAyaPLjHMxpc7E7c= +go.etcd.io/etcd/client/v3 v3.5.7 h1:u/OhpiuCgYY8awOHlhIhmGIGpxfBU/GZBUP3m/3/Iz4= +go.etcd.io/etcd/client/v3 v3.5.7/go.mod h1:sOWmj9DZUMyAngS7QQwCyAXXAL6WhgTOPLNS/NabQgw= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/otel v1.0.0-RC1/go.mod h1:x9tRa9HK4hSSq7jf2TKbqFbtt58/TGk0f9XiEYISI1I= +go.opentelemetry.io/otel v1.4.1/go.mod h1:StM6F/0fSwpd8dKWDCdRr7uRvEPYdW0hBSlbdTiUde4= +go.opentelemetry.io/otel v1.5.0/go.mod h1:Jm/m+rNp/z0eqJc74H7LPwQ3G87qkU/AnnAydAjSAHk= +go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= +go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= +go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU= +go.opentelemetry.io/otel/exporters/jaeger v1.14.0 h1:CjbUNd4iN2hHmWekmOqZ+zSCU+dzZppG8XsV+A3oc8Q= +go.opentelemetry.io/otel/exporters/jaeger v1.14.0/go.mod h1:4Ay9kk5vELRrbg5z4cpP9EtmQRFap2Wb0woPG4lujZA= +go.opentelemetry.io/otel/exporters/zipkin v1.14.0 h1:reEVE1upBF9tcujgvSqLJS0SrI7JQPaTKP4s4rymnSs= +go.opentelemetry.io/otel/exporters/zipkin v1.14.0/go.mod h1:RcjvOAcvhzcufQP8aHmzRw1gE9g/VEZufDdo2w+s4sk= +go.opentelemetry.io/otel/metric v0.36.0 h1:t0lgGI+L68QWt3QtOIlqM9gXoxqxWLhZ3R/e5oOAY0Q= +go.opentelemetry.io/otel/metric v0.36.0/go.mod h1:wKVw57sd2HdSZAzyfOM9gTqqE8v7CbqWsYL6AyrH9qk= +go.opentelemetry.io/otel/oteltest v1.0.0-RC1/go.mod h1:+eoIG0gdEOaPNftuy1YScLr1Gb4mL/9lpDkZ0JjMRq4= +go.opentelemetry.io/otel/sdk v1.0.0-RC1/go.mod h1:kj6yPn7Pgt5ByRuwesbaWcRLA+V7BSDg3Hf8xRvsvf8= +go.opentelemetry.io/otel/sdk v1.4.1/go.mod h1:NBwHDgDIBYjwK2WNu1OPgsIc2IJzmBXNnvIJxJc8BpE= +go.opentelemetry.io/otel/sdk v1.7.0/go.mod h1:uTEOTwaqIVuTGiJN7ii13Ibp75wJmYUDe374q6cZwUU= +go.opentelemetry.io/otel/sdk v1.14.0 h1:PDCppFRDq8A1jL9v6KMI6dYesaq+DFcDZvjsoGvxGzY= +go.opentelemetry.io/otel/sdk v1.14.0/go.mod h1:bwIC5TjrNG6QDCHNWvW4HLHtUQ4I+VQDsnjhvyZCALM= +go.opentelemetry.io/otel/trace v1.0.0-RC1/go.mod h1:86UHmyHWFEtWjfWPSbu0+d0Pf9Q6e1U+3ViBOc+NXAg= +go.opentelemetry.io/otel/trace v1.4.1/go.mod h1:iYEVbroFCNut9QkwEczV9vMRPHNKSSwYZjulEtsmhFc= +go.opentelemetry.io/otel/trace v1.5.0/go.mod h1:sq55kfhjXYr1zVSyexg0w1mpa03AYXR5eyTkB9NPPdE= +go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= +go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M= +go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w= +golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190501045829-6d32002ffd75/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI= +golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220706163947-c90051bbdb60/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20210615190721-d04028783cf1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M= +golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M= +golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.103.0 h1:9yuVqlu2JCvcLg9p8S3fcFLZij8EPSyvODIY1rkMizQ= +google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210629200056-84d6f6074151/go.mod h1:yiaVoXHpRzHGyxV3o4DktVWY4mSUErTKaeEOq6C3t3U= +google.golang.org/genproto v0.0.0-20210701191553-46259e63a0a9/go.mod h1:yiaVoXHpRzHGyxV3o4DktVWY4mSUErTKaeEOq6C3t3U= +google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa h1:GZXdWYIKckxQE2EcLHLvF+KLF+bIwoxGdMUxTZizueg= +google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= +gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= +gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= +gopkg.in/ini.v1 v1.56.0 h1:DPMeDvGTM54DXbPkVIZsp19fp/I2K7zwA/itHYHKo8Y= +gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/mysql v1.4.7 h1:rY46lkCspzGHn7+IYsNpSfEv9tA+SU4SkkB+GFX125Y= +gorm.io/driver/mysql v1.4.7/go.mod h1:SxzItlnT1cb6e1e4ZRpgJN2VYtcqJgqnHxWr4wsP8oc= +gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= +gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11 h1:9qNbmu21nNThCNnF5i2R3kw2aL27U8ZwbzccNjOmW0g= +gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/openapi.yaml b/openapi.yaml new file mode 100644 index 0000000..15d0ae1 --- /dev/null +++ b/openapi.yaml @@ -0,0 +1,788 @@ +# Generated with protoc-gen-openapi +# https://github.com/google/gnostic/tree/master/cmd/protoc-gen-openapi + +openapi: 3.0.3 +info: + title: Eonline API + description: The greeting service definition. + version: 0.0.1 +paths: + /eonline4/addchat: + post: + tags: + - Eonline + description: 发送聊天消息,客户端暂未用到 + operationId: Eonline_AddChat + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AddChatReq' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/AddChatReply' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /eonline4/checkinfo: + post: + tags: + - Eonline + description: CheckInfo,客户端暂未用到 + operationId: Eonline_CheckInfo + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CheckInfoReq' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/CheckInfoReply' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /eonline4/getPayoutUserLst: + post: + tags: + - Eonline + description: 获取申请提现玩家的列表 + operationId: Eonline_GetPayoutUserLst + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PayoutUserLstReq' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PayoutUserLstReply' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /eonline4/getchat: + post: + tags: + - Eonline + description: 获取聊天消息列表,客户端暂未用到 + operationId: Eonline_GetChat + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/GetChatReq' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/GetChatReply' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /eonline4/pay/init: + post: + tags: + - Eonline + description: PayInit + operationId: Eonline_PayInit + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PayInitReq' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PayInitReply' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /eonline4/payout: + post: + tags: + - Eonline + description: Payout,客户端暂未用到 + operationId: Eonline_Payout + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PayoutReq' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PayoutReply' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /eonline4/payout/callback: + post: + tags: + - Eonline + description: PayoutCallback,客户端暂未用到 + operationId: Eonline_PayoutCallback + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PayoutCallbackReq' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PayoutCallbackReply' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /eonline4/payout/check: + post: + tags: + - Eonline + description: PayoutCheck + operationId: Eonline_PayoutCheck + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PayoutCheckReq' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PayoutCheckReply' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /eonline4/payoutBrazil: + post: + tags: + - Eonline + description: PayoutBrazil,用于巴西PIX支付 + operationId: Eonline_PayoutBrazil + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PayoutReq' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PayoutReply' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /eonline4/setPayoutStatus: + post: + tags: + - Eonline + description: 设置指定玩家的提现状态 + operationId: Eonline_SetPayoutStatus + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PayoutStatusReq' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PayoutStatusReply' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /eonline4/submitcheck: + post: + tags: + - Eonline + description: SubmitCheck,客户端暂未用到 + operationId: Eonline_SubmitCheck + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SubmitCheckReq' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/SubmitCheckReply' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /eonline4/{name}: + get: + tags: + - Eonline + description: Sends a greeting,客户端暂未用到 + operationId: Eonline_SayHello + parameters: + - name: name + in: path + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/HelloReply' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' +components: + schemas: + AddChatReply: + type: object + properties: + result: + type: integer + format: uint32 + uuid: + type: string + lst: + type: array + items: + $ref: '#/components/schemas/PbMsgOne' + error: + type: integer + format: int32 + AddChatReq: + type: object + properties: + timeStamp: + type: integer + format: int64 + platform: + type: string + deviceid: + type: string + version: + type: string + ip: + type: string + ts: + type: string + sign: + type: string + uuid: + type: string + msg: + type: string + description: 发送聊天消息 + CheckInfoReply: + type: object + properties: + CanCheckSubmit: + type: integer + format: int32 + CheckSubmit: + type: integer + format: int32 + CheckResult: + type: integer + format: int32 + CheckPayout: + type: integer + format: int32 + CheckCoin: + type: integer + format: int32 + CanCheckPayOut: + type: integer + format: int32 + CheckResultFailedDesc: + type: string + error: + type: integer + format: int32 + CheckInfoReq: + type: object + properties: + platform: + type: string + deviceid: + type: string + version: + type: string + ip: + type: string + ts: + type: string + sign: + type: string + uuid: + type: string + isVerificationShow: + type: integer + format: int32 + description: 身份审核信息请求 + GetChatReply: + type: object + properties: + uuid: + type: string + lst: + type: array + items: + $ref: '#/components/schemas/PbMsgOne' + error: + type: integer + format: int32 + GetChatReq: + type: object + properties: + timeStamp: + type: integer + format: int64 + platform: + type: string + deviceid: + type: string + version: + type: string + ip: + type: string + ts: + type: string + sign: + type: string + uuid: + type: string + description: 获取聊天消息列表 + GoogleProtobufAny: + type: object + properties: + '@type': + type: string + description: The type of the serialized message. + additionalProperties: true + description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. + HelloReply: + type: object + properties: + message: + type: string + description: The response message containing the greetings + PayInitReply: + type: object + properties: + uuid: + type: string + days: + type: integer + format: uint32 + items: + type: array + items: + $ref: '#/components/schemas/PayInitReply_Item' + CanCheckSubmit: + type: integer + format: int32 + CheckSubmit: + type: integer + format: int32 + CheckResult: + type: integer + format: int32 + CheckPayout: + type: integer + format: int32 + CheckCoin: + type: integer + format: int32 + CanCheckPayOut: + type: integer + format: int32 + CheckResultFailedDesc: + type: string + error: + type: integer + format: int32 + clientData: + type: string + description: PayInitReply init reply + PayInitReply_Item: + type: object + properties: + id: + type: integer + format: uint32 + amount: + type: number + format: double + status: + type: integer + format: uint32 + PayInitReq: + type: object + properties: + platform: + type: string + deviceid: + type: string + version: + type: string + ip: + type: string + ts: + type: string + sign: + type: string + description: PayInitReq init request + PayoutCallbackReply: + type: object + properties: + message: + type: string + description: PayoutCallbackReply 赔付回调响应 + PayoutCallbackReq: + type: object + properties: + payoutId: + type: string + customCode: + type: string + status: + type: string + msg: + type: string + timestamp: + type: integer + format: int64 + description: PayoutCallbackReq 赔付回调请求 + PayoutCheckReply: + type: object + properties: + status: + type: integer + format: uint32 + error: + type: integer + format: int32 + description: PayoutCheckReply 赔付查询响应 + PayoutCheckReq: + type: object + properties: + platform: + type: string + deviceid: + type: string + version: + type: string + ts: + type: string + sign: + type: string + ip: + type: string + recordNo: + type: string + description: PayoutCheckReq 赔付查询请求 + PayoutReply: + type: object + properties: + id: + type: string + recordNo: + type: string + error: + type: integer + format: int32 + description: PayoutReply 赔付响应 + PayoutReq: + type: object + properties: + platform: + type: string + deviceid: + type: string + version: + type: string + ts: + type: string + sign: + type: string + account: + type: string + itemId: + type: integer + format: uint32 + amount: + type: number + format: double + additionalRemark: + type: string + uuid: + type: string + ip: + type: string + accountType: + type: string + documentType: + type: string + documentId: + type: string + name: + type: string + dataAdjust: + $ref: '#/components/schemas/PbReportDataAdjust' + dataShuShu: + $ref: '#/components/schemas/PbReportDataShuShu' + clientData: + type: string + clientName: + type: string + email: + type: string + description: PayoutReq 赔付请求 + PayoutStatusReply: + type: object + properties: + recordNo: + type: string + status: + type: integer + format: uint32 + error: + type: integer + format: uint32 + PayoutStatusReq: + type: object + properties: + platform: + type: string + deviceid: + type: string + version: + type: string + ts: + type: string + sign: + type: string + recordNo: + type: string + fail: + type: string + status: + type: integer + format: uint32 + description: SetPayoutStatus 设置指定玩家的提现状态 + PayoutUserLstReply: + type: object + properties: + lst: + type: array + items: + $ref: '#/components/schemas/PayoutUserOne' + error: + type: integer + format: int32 + description: GetPayoutUserLst 获取申请提现玩家的列表请求响应 + PayoutUserLstReq: + type: object + properties: + platform: + type: string + deviceid: + type: string + version: + type: string + ts: + type: string + sign: + type: string + status: + type: integer + format: uint32 + pageIndex: + type: integer + format: uint32 + pageSize: + type: integer + format: uint32 + description: GetPayoutUserLst 查询提现邮箱请求 + PayoutUserOne: + type: object + properties: + email: + type: string + recordNo: + type: string + account: + type: string + status: + type: integer + format: uint32 + PbMsgOne: + type: object + properties: + timeStamp: + type: integer + format: int64 + uuid: + type: string + name: + type: string + msg: + type: string + PbReportDataAdjust: + type: object + properties: + gpsAdid: + type: string + androidId: + type: string + adid: + type: string + userAgent: + type: string + price: + type: string + currency: + type: string + PbReportDataShuShu: + type: object + properties: + gpsGaid: + type: string + androidId: + type: string + adid: + type: string + userAgent: + type: string + price: + type: string + currency: + type: string + paymentMethod: + type: string + paymentType: + type: string + paymentNumber: + type: string + iapName: + type: string + gamecoinNumber: + type: string + gamecoinType: + type: string + ssAccountId: + type: string + ssDistinctId: + type: string + ssSuperProperties: + type: string + Status: + type: object + properties: + code: + type: integer + description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + format: int32 + message: + type: string + description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + details: + type: array + items: + $ref: '#/components/schemas/GoogleProtobufAny' + description: A list of messages that carry the error details. There is a common set of message types for APIs to use. + description: 'The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).' + SubmitCheckReply: + type: object + properties: + result: + type: integer + format: int32 + error: + type: integer + format: int32 + SubmitCheckReq: + type: object + properties: + account: + type: string + uuid: + type: string + description: 提交身份文件验证请求 +tags: + - name: Eonline diff --git a/pkg/ali/tkecs/client.go b/pkg/ali/tkecs/client.go new file mode 100644 index 0000000..41cfe8e --- /dev/null +++ b/pkg/ali/tkecs/client.go @@ -0,0 +1,172 @@ +package tkecs + +import ( + "fmt" + "net/http" + "sandc/pkg/ali" + + openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client" + ecs20140526 "github.com/alibabacloud-go/ecs-20140526/v3/client" +) + +type InstanceStatus string + +const ( + InstanceStatusPending InstanceStatus = "Pending" + InstanceStatusRunning InstanceStatus = "Running" + InstanceStatusStarting InstanceStatus = "Starting" + InstanceStatusStopping InstanceStatus = "Stopping" + InstanceStatusStopped InstanceStatus = "Stopped" +) + +type Client struct { + ecs *ecs20140526.Client + regionId string +} + +func NewClient(accessKeyID, accessKeySecret, regionId string) (*Client, error) { + config := &openapi.Config{ + // 必填,您的 AccessKey ID + AccessKeyId: ali.String(accessKeyID), + // 必填,您的 AccessKey Secret + AccessKeySecret: ali.String(accessKeySecret), + } + // 访问的域名 + config.Endpoint = ali.String(ali.GetEcsEndpointByRegionId(regionId)) + ecsClient, err := ecs20140526.NewClient(config) + if err != nil { + return nil, err + } + + return &Client{ + ecs: ecsClient, + regionId: regionId, + }, nil +} + +// CreateProxyInstance 创建阿里云代理实例 +func (c *Client) CreateProxyInstance(name string) (*string, error) { + systemDisk := &ecs20140526.CreateInstanceRequestSystemDisk{ + Size: ali.Int32(20), //20G + } + createInstanceRequest := &ecs20140526.CreateInstanceRequest{ + RegionId: ali.String(c.regionId), + InstanceType: ali.String("ecs.t5-lc2m1.nano"), + ResourceGroupId: ali.String("rg-aek26d4tdurjdui"), + ImageId: ali.String("m-t4nfwmalijh9zhfflt5i"), + InternetMaxBandwidthOut: ali.Int32(10), + InternetMaxBandwidthIn: ali.Int32(10), + SystemDisk: systemDisk, + InstanceChargeType: ali.String("PrePaid"), + PeriodUnit: ali.String("Month"), + Period: ali.Int32(1), + SecurityGroupId: ali.String("sg-t4nazifybjirx7tepv28"), + InstanceName: ali.String(name), + VSwitchId: ali.String("vsw-t4n1mlcjic25hzki9b6nc"), + InternetChargeType: ali.String("PayByTraffic"), + AutoRenew: ali.Bool(true), + AutoRenewPeriod: ali.Int32(1), + } + + res, err := c.ecs.CreateInstance(createInstanceRequest) + if err != nil { + return nil, err + } + + if ali.Int32Value(res.StatusCode) != http.StatusOK { + return nil, fmt.Errorf("CreateProxyInstance status failed: %d", http.StatusOK) + } + + fmt.Println("res: ", res, err) + return res.Body.InstanceId, nil +} + +// AllocatePublicIpAddress 为一台ECS实例分配一个固定公网IP地址 +func (c *Client) AllocatePublicIpAddress(instanceId string) (*string, error) { + allocatePublicIpAddressRequest := &ecs20140526.AllocatePublicIpAddressRequest{ + InstanceId: ali.String(instanceId), + } + res, err := c.ecs.AllocatePublicIpAddress(allocatePublicIpAddressRequest) + if err != nil { + return nil, err + } + + if ali.Int32Value(res.StatusCode) != http.StatusOK { + return nil, fmt.Errorf("AllocatePublicIpAddress status failed: %d", http.StatusOK) + } + + return res.Body.IpAddress, nil +} + +// ConvertNatPublicIpToEip +func (c *Client) ConvertNatPublicIpToEip(instanceId string) error { + convertNatPublicIpToEipRequest := &ecs20140526.ConvertNatPublicIpToEipRequest{ + InstanceId: ali.String(instanceId), + RegionId: ali.String(c.regionId), + } + res, err := c.ecs.ConvertNatPublicIpToEip(convertNatPublicIpToEipRequest) + if err != nil { + return err + } + + if ali.Int32Value(res.StatusCode) != http.StatusOK { + return fmt.Errorf("ConvertNatPublicIpToEip status failed: %d", ali.Int32Value(res.StatusCode)) + } + return nil +} + +// StartInstance 启动一台ECS实例,接口调用成功后实例进入启动中状态 +func (c *Client) StartInstance(instanceId string) error { + startInstanceRequest := &ecs20140526.StartInstanceRequest{ + InstanceId: ali.String(instanceId), + } + res, err := c.ecs.StartInstance(startInstanceRequest) + if err != nil { + return err + } + + if ali.Int32Value(res.StatusCode) != http.StatusOK { + return fmt.Errorf("StartInstance status failed: %d", ali.Int32Value(res.StatusCode)) + } + return nil +} + +// CheckInstanceStatusActive 判断实例是否运行中 +func (c *Client) CheckInstanceStatusActive(instanceId string) (*bool, error) { + describeInstanceAttributeRequest := &ecs20140526.DescribeInstanceAttributeRequest{ + InstanceId: ali.String(instanceId), + } + res, err := c.ecs.DescribeInstanceAttribute(describeInstanceAttributeRequest) + if err != nil { + return nil, err + } + + if ali.Int32Value(res.StatusCode) != http.StatusOK { + return nil, fmt.Errorf("CheckInstanceStatusActive status failed: %d", ali.Int32Value(res.StatusCode)) + } + + if ali.StringValue(res.Body.Status) == string(InstanceStatusRunning) { + return ali.Bool(true), nil + } + + return ali.Bool(false), nil +} + +// ModifyInstanceAttribute 修改实例的名称 +func (c *Client) ModifyInstanceName(instanceId, name string) error { + modifyInstanceAttributeRequest := &ecs20140526.ModifyInstanceAttributeRequest{ + InstanceId: ali.String(instanceId), + InstanceName: ali.String(name), + } + res, err := c.ecs.ModifyInstanceAttribute(modifyInstanceAttributeRequest) + if err != nil { + return err + } + + if ali.Int32Value(res.StatusCode) != http.StatusOK { + return fmt.Errorf("ModifyInstanceName status failed: %d", ali.Int32Value(res.StatusCode)) + } + + return nil + +} diff --git a/pkg/ali/tkoss/client.go b/pkg/ali/tkoss/client.go new file mode 100644 index 0000000..e3391a2 --- /dev/null +++ b/pkg/ali/tkoss/client.go @@ -0,0 +1,245 @@ +package tkoss + +import ( + "bufio" + "bytes" + "fmt" + "io" + "sandc/pkg/ali" + + openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client" + sts20150401 "github.com/alibabacloud-go/sts-20150401/v2/client" + "github.com/aliyun/aliyun-oss-go-sdk/oss" +) + +type StsCredentials struct { + RegionId string + Bucket string + AccessKeyId string + AccessKeySecret string + Expiration string + SecurityToken string +} + +// 定义sts属性 +type stsOption struct { + policy string + roleArn string + roleSessionName string +} + +var stsOp = &stsOption{ + roleArn: "acs:ram::1280524430176126:role/toukaossram", + roleSessionName: "touka_external", + policy: `{"Version": "1", "Statement": [{"Action": ["oss:PutObject","oss:GetObject"], "Effect": "Allow", "Resource": ["acs:oss:*:*:toukadc-sg/*"]}]}`, +} + +type Client struct { + oss *oss.Client + sts *sts20150401.Client + stsOption *stsOption + regionId string + bucket string +} + +func NewClient(accessKeyID, accessKeySecret, regionId, bucket string, options ...oss.ClientOption) (*Client, error) { + client, err := oss.New(ali.GetOssEndpointByRegionId(regionId), accessKeyID, accessKeySecret, options...) + if err != nil { + return nil, fmt.Errorf("init ossclient failed: %w", err) + } + stsClient, err := sts20150401.NewClient(&openapi.Config{ + AccessKeyId: ali.String(accessKeyID), + AccessKeySecret: ali.String(accessKeySecret), + Endpoint: ali.String(ali.GetStsEndpointByRegionId(regionId)), + }) + if err != nil { + return nil, fmt.Errorf("init sts client failed: %w", err) + } + return &Client{ + oss: client, + sts: stsClient, + regionId: regionId, + bucket: bucket, + stsOption: stsOp, + }, nil +} + +// UplaodFile 上传文件 +func (c *Client) UplaodFile(objectKey, filePath string) error { + bucket, err := c.oss.Bucket(c.bucket) + if err != nil { + return err + } + // 用于服务端上传文件。 + return bucket.PutObjectFromFile(objectKey, filePath) +} + +// UploadByte 上传文件 +func (c *Client) UploadByte(objectKey string, data []byte) error { + bucket, err := c.oss.Bucket(c.bucket) + if err != nil { + return err + } + // 用于服务端上传文件。 + return bucket.PutObject(objectKey, bytes.NewReader(data)) +} + +// GenerateSignedUrl 生成临时签名URL +func (c *Client) GenerateSignedUrl(objectKey string, expiredInSec int64) (string, error) { + bucket, err := c.oss.Bucket(c.bucket) + if err != nil { + return "", nil + } + return bucket.SignURL(objectKey, oss.HTTPGet, expiredInSec) +} + +// GetObject 获取文件内容 +func (c *Client) GetObject(objectKey string) (io.ReadCloser, error) { + bucket, err := c.oss.Bucket(c.bucket) + if err != nil { + return nil, err + } + + return bucket.GetObject(objectKey) +} + +// GetObjectByte 获取文件byte内容 +func (c *Client) GetObjectByte(objectKey string) ([]byte, error) { + bucket, err := c.oss.Bucket(c.bucket) + if err != nil { + return nil, err + } + + reader, err := bucket.GetObject(objectKey) + if err != nil { + return nil, fmt.Errorf("reader: %w", err) + } + + newReader := bufio.NewReader(reader) + body := make([]byte, 0) + for { + data, err := newReader.ReadBytes('\n') + if err == io.EOF { + body = append(body, data...) + break + } + if err != nil { + return nil, err + } + body = append(body, data...) + } + + return body, nil +} + +// ListObjectsV2 列举文件 +func (c *Client) ListObjectsV2(prefix string) ([]string, error) { + bucket, err := c.oss.Bucket(c.bucket) + if err != nil { + return nil, err + } + + res, err := bucket.ListObjectsV2(oss.Prefix(prefix)) + if err != nil { + return nil, err + } + + var objectKeys []string + for _, object := range res.Objects { + objectKeys = append(objectKeys, object.Key) + } + + return objectKeys, nil +} + +// UploadFileBySignedUrl 生成签名URL并通过签名URL上传文件 +func (c *Client) UploadFileBySignedUrl(objectKey, filePath string, expiredInSec int64) (string, error) { + bucket, err := c.oss.Bucket(c.bucket) + if err != nil { + return "", err + } + + signedURL, err := bucket.SignURL(objectKey, oss.HTTPPut, expiredInSec) + if err != nil { + return "", err + } + + err = bucket.PutObjectFromFileWithURL(signedURL, filePath) + if err != nil { + return "", err + } + + return c.GenerateSignedUrl(objectKey, expiredInSec) +} + +// UploadObjectBySignedUrl 通过签名URL上传文件 +func (c *Client) UploadObjectBySignedUrl(objectKey string, file []byte, expiredInSec int64) (string, error) { + bucket, err := c.oss.Bucket(c.bucket) + if err != nil { + return "", err + } + + signedURL, err := bucket.SignURL(objectKey, oss.HTTPPut, expiredInSec) + if err != nil { + return "", err + } + + // filename := os.GetFileName(objectKey) // 没必要,保留oss默认的objectKey + // err = bucket.PutObjectWithURL(signedURL, bytes.NewReader(file), oss.ContentDisposition("attachment;filename="+objectKey)) + + err = bucket.PutObjectWithURL(signedURL, bytes.NewReader(file)) + if err != nil { + return "", err + } + + return c.GenerateSignedUrl(objectKey, expiredInSec) +} + +// CopyObject 复制文件 +func (c *Client) CopyObject(srcObjectKey, destObjectKey string) error { + bucket, err := c.oss.Bucket(c.bucket) + if err != nil { + return err + } + + _, err = bucket.CopyObject(srcObjectKey, destObjectKey) + if err != nil { + return err + } + + return nil +} + +// GenerateStsCredentials 生成临时凭证 +func (c *Client) GenerateStsCredentials() (*StsCredentials, error) { + assumeRoleRequest := &sts20150401.AssumeRoleRequest{ + RoleArn: ali.String(c.stsOption.roleArn), + RoleSessionName: ali.String(c.stsOption.roleSessionName), + Policy: ali.String(c.stsOption.policy), + DurationSeconds: ali.Int64(3600), //默认设置1个小时 + } + + res, err := c.sts.AssumeRole(assumeRoleRequest) + if err != nil { + return nil, err + } + + return &StsCredentials{ + RegionId: fmt.Sprintf("oss-%s", c.regionId), + Bucket: c.bucket, + AccessKeyId: ali.StringValue(res.Body.Credentials.AccessKeyId), + AccessKeySecret: ali.StringValue(res.Body.Credentials.AccessKeySecret), + Expiration: ali.StringValue(res.Body.Credentials.Expiration), + SecurityToken: ali.StringValue(res.Body.Credentials.SecurityToken), + }, nil +} + +// DeleteObject 删除文件 +func (c *Client) DeleteObject(objectKey string) error { + bucket, err := c.oss.Bucket(c.bucket) + if err != nil { + return err + } + + return bucket.DeleteObject(objectKey) +} diff --git a/pkg/ali/util.go b/pkg/ali/util.go new file mode 100644 index 0000000..82c86ef --- /dev/null +++ b/pkg/ali/util.go @@ -0,0 +1,507 @@ +package ali + +import ( + "fmt" +) + +func String(a string) *string { + return &a +} + +func StringValue(a *string) string { + if a == nil { + return "" + } + return *a +} + +func Int(a int) *int { + return &a +} + +func IntValue(a *int) int { + if a == nil { + return 0 + } + return *a +} + +func Int8(a int8) *int8 { + return &a +} + +func Int8Value(a *int8) int8 { + if a == nil { + return 0 + } + return *a +} + +func Int16(a int16) *int16 { + return &a +} + +func Int16Value(a *int16) int16 { + if a == nil { + return 0 + } + return *a +} + +func Int32(a int32) *int32 { + return &a +} + +func Int32Value(a *int32) int32 { + if a == nil { + return 0 + } + return *a +} + +func Int64(a int64) *int64 { + return &a +} + +func Int64Value(a *int64) int64 { + if a == nil { + return 0 + } + return *a +} + +func Bool(a bool) *bool { + return &a +} + +func BoolValue(a *bool) bool { + if a == nil { + return false + } + return *a +} + +func Uint(a uint) *uint { + return &a +} + +func UintValue(a *uint) uint { + if a == nil { + return 0 + } + return *a +} + +func Uint8(a uint8) *uint8 { + return &a +} + +func Uint8Value(a *uint8) uint8 { + if a == nil { + return 0 + } + return *a +} + +func Uint16(a uint16) *uint16 { + return &a +} + +func Uint16Value(a *uint16) uint16 { + if a == nil { + return 0 + } + return *a +} + +func Uint32(a uint32) *uint32 { + return &a +} + +func Uint32Value(a *uint32) uint32 { + if a == nil { + return 0 + } + return *a +} + +func Uint64(a uint64) *uint64 { + return &a +} + +func Uint64Value(a *uint64) uint64 { + if a == nil { + return 0 + } + return *a +} + +func Float32(a float32) *float32 { + return &a +} + +func Float32Value(a *float32) float32 { + if a == nil { + return 0 + } + return *a +} + +func Float64(a float64) *float64 { + return &a +} + +func Float64Value(a *float64) float64 { + if a == nil { + return 0 + } + return *a +} + +func IntSlice(a []int) []*int { + if a == nil { + return nil + } + res := make([]*int, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func IntValueSlice(a []*int) []int { + if a == nil { + return nil + } + res := make([]int, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func Int8Slice(a []int8) []*int8 { + if a == nil { + return nil + } + res := make([]*int8, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func Int8ValueSlice(a []*int8) []int8 { + if a == nil { + return nil + } + res := make([]int8, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func Int16Slice(a []int16) []*int16 { + if a == nil { + return nil + } + res := make([]*int16, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func Int16ValueSlice(a []*int16) []int16 { + if a == nil { + return nil + } + res := make([]int16, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func Int32Slice(a []int32) []*int32 { + if a == nil { + return nil + } + res := make([]*int32, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func Int32ValueSlice(a []*int32) []int32 { + if a == nil { + return nil + } + res := make([]int32, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func Int64Slice(a []int64) []*int64 { + if a == nil { + return nil + } + res := make([]*int64, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func Int64ValueSlice(a []*int64) []int64 { + if a == nil { + return nil + } + res := make([]int64, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func UintSlice(a []uint) []*uint { + if a == nil { + return nil + } + res := make([]*uint, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func UintValueSlice(a []*uint) []uint { + if a == nil { + return nil + } + res := make([]uint, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func Uint8Slice(a []uint8) []*uint8 { + if a == nil { + return nil + } + res := make([]*uint8, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func Uint8ValueSlice(a []*uint8) []uint8 { + if a == nil { + return nil + } + res := make([]uint8, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func Uint16Slice(a []uint16) []*uint16 { + if a == nil { + return nil + } + res := make([]*uint16, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func Uint16ValueSlice(a []*uint16) []uint16 { + if a == nil { + return nil + } + res := make([]uint16, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func Uint32Slice(a []uint32) []*uint32 { + if a == nil { + return nil + } + res := make([]*uint32, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func Uint32ValueSlice(a []*uint32) []uint32 { + if a == nil { + return nil + } + res := make([]uint32, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func Uint64Slice(a []uint64) []*uint64 { + if a == nil { + return nil + } + res := make([]*uint64, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func Uint64ValueSlice(a []*uint64) []uint64 { + if a == nil { + return nil + } + res := make([]uint64, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func Float32Slice(a []float32) []*float32 { + if a == nil { + return nil + } + res := make([]*float32, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func Float32ValueSlice(a []*float32) []float32 { + if a == nil { + return nil + } + res := make([]float32, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func Float64Slice(a []float64) []*float64 { + if a == nil { + return nil + } + res := make([]*float64, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func Float64ValueSlice(a []*float64) []float64 { + if a == nil { + return nil + } + res := make([]float64, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func StringSlice(a []string) []*string { + if a == nil { + return nil + } + res := make([]*string, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func StringSliceValue(a []*string) []string { + if a == nil { + return nil + } + res := make([]string, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func BoolSlice(a []bool) []*bool { + if a == nil { + return nil + } + res := make([]*bool, len(a)) + for i := 0; i < len(a); i++ { + res[i] = &a[i] + } + return res +} + +func BoolSliceValue(a []*bool) []bool { + if a == nil { + return nil + } + res := make([]bool, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != nil { + res[i] = *a[i] + } + } + return res +} + +func GetOssEndpointByRegionId(regionId string) string { + return fmt.Sprintf("https://oss-%s.aliyuncs.com", regionId) +} + +func GetStsEndpointByRegionId(regionId string) string { + return fmt.Sprintf("sts.%s.aliyuncs.com", regionId) +} + +func GetEcsEndpointByRegionId(regionId string) string { + return fmt.Sprintf("ecs.%s.aliyuncs.com", regionId) +} diff --git a/pkg/appstore/client.go b/pkg/appstore/client.go new file mode 100644 index 0000000..4384be7 --- /dev/null +++ b/pkg/appstore/client.go @@ -0,0 +1,226 @@ +package appstore + +import ( + "crypto/ecdsa" + "crypto/x509" + "encoding/base64" + "encoding/json" + "encoding/pem" + "fmt" + "sandc/pkg/bhttp" + "strings" + "time" + + jwt "github.com/dgrijalva/jwt-go" +) + +type options struct { + token string +} +type Option func(*options) + +func WithToken(b string) Option { + return func(c *options) { + c.token = b + } +} + +type Client struct { + privateKeyByte []byte + issuer string + keyID string + token string + env string +} + +// NewClient creates a new App Store Connect API client. +func NewClient(issuer, keyID, env string, privateKeyByte []byte, opts ...Option) (*Client, error) { + opt := options{ + token: "", + } + for _, o := range opts { + o(&opt) + } + return &Client{ + privateKeyByte: privateKeyByte, + issuer: issuer, + keyID: keyID, + env: env, + token: opt.token, + }, nil +} + +// IsProdEnv returns true if the client is configured to use the production environment. +func (c *Client) IsProdEnv() bool { + return c.env == "prod" +} + +// loadPrivateKey loads a private key from a file. +func (c *Client) loadPrivateKey(bytes []byte) (*ecdsa.PrivateKey, error) { + block, _ := pem.Decode(bytes) + if block == nil { + return nil, fmt.Errorf("failed to decode PEM block containing private key") + } + + key, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + return nil, err + } + + ecdsaKey, ok := key.(*ecdsa.PrivateKey) + if !ok { + return nil, fmt.Errorf("not an ECDSA private key") + } + + return ecdsaKey, nil +} + +// GenerateToken generates a JWT token for the App Store Connect API. +func (c *Client) GenerateToken(pkg string, expire time.Duration) (string, error) { + privateKey, err := c.loadPrivateKey(c.privateKeyByte) + if err != nil { + return "", err + } + + token := jwt.NewWithClaims(jwt.SigningMethodES256, jwt.MapClaims{ + "iss": c.issuer, + "iat": time.Now().Unix(), + "exp": time.Now().Add(expire).Unix(), + "aud": "appstoreconnect-v1", + "bid": pkg, + }) + + token.Header["kid"] = c.keyID + + signedToken, err := token.SignedString(privateKey) + if err != nil { + return "", err + } + + c.token = signedToken + + return signedToken, nil +} + +// map[bundleId:com.hotpotgames.mergegangster.global environment:Sandbox inAppOwnershipType:PURCHASED originalPurchaseDate:1.689239628e+12 originalTransactionId:2000000368088682 productId:mergegangster_noads purchaseDate:1.689239628e+12 quantity:1 signedDate:1.689594496689e+12 storefront:HKG storefrontId:143463 transactionId:2000000368088682 transactionReason:PURCHASE type:Non-Consumable] +// TransactionInfo represents the transaction info for a given transaction id +type TransactionInfo struct { + BundleId string `json:"bundleId"` + Environment string `json:"environment"` + InAppOwnershipType string `json:"inAppOwnershipType"` + OriginalPurchaseDate int64 `json:"originalPurchaseDate"` + OriginalTransactionId string `json:"originalTransactionId"` + ProductId string `json:"productId"` + PurchaseDate int64 `json:"purchaseDate"` + Quantity int32 `json:"quantity"` + SignedDate int64 `json:"signedDate"` + Storefront string `json:"storefront"` + StorefrontId string `json:"storefrontId"` + TransactionId string `json:"transactionId"` + TransactionReason string `json:"transactionReason"` + Type string `json:"type"` +} + +// TransactionInfoRes represents the transaction info response +type TransactionInfoRes struct { + SignedTransactionInfo string `json:"signedTransactionInfo,omitempty"` + ErrorCode int `json:"errorCode,omitempty"` + ErrorMessage string `json:"errorMessage,omitempty"` +} + +// GetTransactionInfo returns the transaction info for a given transaction id +func (c *Client) GetTransactionInfo(transactionID, pkg string) (*TransactionInfo, error) { + var url string + if c.IsProdEnv() { + url = fmt.Sprintf("https://api.storekit.itunes.apple.com/inApps/v1/transactions/%s", transactionID) + } else { + url = fmt.Sprintf("https://api.storekit-sandbox.itunes.apple.com/inApps/v1/transactions/%s", transactionID) + } + bhttp, err := bhttp.NewBhttpClient() + if err != nil { + return nil, fmt.Errorf("bhttpClient init failed: %w", err) + } + + if c.token == "" { + token, err := c.GenerateToken(pkg, 24*time.Hour) + if err != nil { + return nil, fmt.Errorf("generate token failed: %w", err) + } + c.token = token + fmt.Println("new") + } + + fmt.Println("token: ", c.token) + + bhttp.SetHeader("Authorization", fmt.Sprintf("Bearer %s", c.token)) + if err != nil { + return nil, err + } + + resJson, err := bhttp.DoGet(url) + if err != nil { + return nil, fmt.Errorf("getTransactionInfo http get error: %w", err) + } + + var info TransactionInfoRes + err = json.Unmarshal(resJson, &info) + if err != nil { + return nil, fmt.Errorf("getTransactionInfo unmarshal err: %w, body: %s", err, string(resJson)) + } + + if info.ErrorMessage != "" { + return nil, fmt.Errorf("getTransactionInfo error: %s", info.ErrorMessage) + } + + // signedTransactionInfo A customer’s in-app purchase transaction, signed by Apple, in JSON Web Signature (JWS) format. + signedPayload := info.SignedTransactionInfo + + // decode signedTransactionInfo + segments := strings.Split(signedPayload, ".") + payloadBytes, err := base64.RawURLEncoding.DecodeString(segments[1]) + if err != nil { + return nil, fmt.Errorf("error decoding payload: %w", err) + } + var payload TransactionInfo + err = json.Unmarshal(payloadBytes, &payload) + if err != nil { + return nil, fmt.Errorf("error unmarshaling payload: %w", err) + } + + return &payload, nil +} + +// GenerateToken generates a JWT token for the App Store Connect API. +func GenerateToken(issuer, keyID, pkg string, privateKeyByte []byte, expire time.Duration) (string, error) { + block, _ := pem.Decode(privateKeyByte) + if block == nil { + return "", fmt.Errorf("failed to decode PEM block containing private key") + } + + key, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + return "", err + } + + privateKey, ok := key.(*ecdsa.PrivateKey) + if !ok { + return "", fmt.Errorf("not an ECDSA private key") + } + + token := jwt.NewWithClaims(jwt.SigningMethodES256, jwt.MapClaims{ + "iss": issuer, + "iat": time.Now().Unix(), + "exp": time.Now().Add(expire).Unix(), + "aud": "appstoreconnect-v1", + "bid": pkg, + }) + + token.Header["kid"] = keyID + + signedToken, err := token.SignedString(privateKey) + if err != nil { + return "", err + } + + return signedToken, nil +} diff --git a/pkg/appstore/utils.go b/pkg/appstore/utils.go new file mode 100644 index 0000000..e595fb8 --- /dev/null +++ b/pkg/appstore/utils.go @@ -0,0 +1,43 @@ +package appstore + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "strings" +) + +type SignedTansactionInfo struct { + TransactionId string `json:"transactionId,omitempty"` + OriginalTransactionId string `json:"originalTransactionId,omitempty"` + BundleId string `json:"bundleId,omitempty"` + ProductId string `json:"productId,omitempty"` + PurchaseDate int `json:"purchaseDate,omitempty"` + OriginalPurchaseDate int `json:"originalPurchaseDate,omitempty"` + Quantity int `json:"quantity,omitempty"` + Type string `json:"type,omitempty"` + InAppOwnershipType string `json:"inAppOwnershipType,omitempty"` + SignedDate int `json:"signedDate,omitempty"` + RevocationReason int `json:"revocationReason,omitempty"` + RevocationDate int `json:"revocationDate,omitempty"` + Environment string `json:"environment,omitempty"` + TransactionReason string `json:"transactionReason,omitempty"` + Storefront string `json:"storefront,omitempty"` + StorefrontId string `json:"storefrontId,omitempty"` +} + +// 解析苹果返回的receiptInof +func ParseReceiptInfo(signedPayload string) (*SignedTansactionInfo, error) { + segments := strings.Split(signedPayload, ".") + payloadBytes, err := base64.RawURLEncoding.DecodeString(segments[1]) + if err != nil { + return nil, fmt.Errorf("error decoding payload: %w", err) + } + + var info SignedTansactionInfo + if err := json.Unmarshal(payloadBytes, &info); err != nil { + return nil, fmt.Errorf("error unmarshalling payload: %w", err) + } + + return &info, nil +} diff --git a/pkg/bhttp/http.go b/pkg/bhttp/http.go new file mode 100644 index 0000000..dd2c96b --- /dev/null +++ b/pkg/bhttp/http.go @@ -0,0 +1,363 @@ +// bhttp Support for Http custom parameters and proxy +package bhttp + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io" + "mime/multipart" + "net/http" + "net/url" + "strconv" + "strings" + "time" +) + +// BhttpService bhttp service +type BhttpService struct { + Client *bhttpClient +} + +// NewBhttpService 创建bhttp service +func NewBhttpService(opts ...Option) (*BhttpService, error) { + client, err := NewBhttpClient(opts...) + if err != nil { + return nil, err + } + + return &BhttpService{ + Client: client, + }, nil +} + +type bhttpClient struct { + client *http.Client + httpRequest *http.Request + header *http.Header + values *url.Values + params map[string]string + postBody []byte + retry int32 + checkStatusOk bool + proxyURL string + rateTimeLeft int //下次可执行剩余的时间毫秒 +} + +type options struct { + timeout int32 + proxyURL string + retry int32 + checkStatusOk bool + disableKeepAlives bool +} +type Option func(*options) + +func WithTimeout(b int32) Option { + return func(c *options) { + c.timeout = b + } +} +func WithProxy(b string) Option { + return func(c *options) { + c.proxyURL = b + } +} + +func WithRetry(b int32) Option { + return func(c *options) { + c.retry = b + } +} + +func WithCheckStatusOk(b bool) Option { + return func(c *options) { + c.checkStatusOk = b + } +} + +func WithDisableKeepAlives(b bool) Option { + return func(c *options) { + c.disableKeepAlives = b + } +} + +func NewBhttpClient(opts ...Option) (*bhttpClient, error) { + proxyFunc := http.ProxyFromEnvironment + opt := options{ + timeout: 60, + proxyURL: "", + retry: 0, + checkStatusOk: false, + disableKeepAlives: false, + } + for _, o := range opts { + o(&opt) + } + + if opt.proxyURL != "" { + proxy, err := url.Parse(opt.proxyURL) + if err != nil { + return nil, err + } + proxyFunc = http.ProxyURL(proxy) + } + + return &bhttpClient{ + client: &http.Client{ + Transport: &http.Transport{ + Proxy: proxyFunc, + DisableKeepAlives: opt.disableKeepAlives, + }, + Timeout: time.Duration(opt.timeout) * time.Second, + }, + header: &http.Header{}, + values: &url.Values{}, + params: make(map[string]string), + retry: opt.retry, + checkStatusOk: opt.checkStatusOk, + proxyURL: opt.proxyURL, + }, nil +} + +func (b *bhttpClient) SetParam(key, value string) *bhttpClient { + b.values.Add(key, value) + oParams := b.params + oParams[key] = value + b.params = oParams + + return b +} + +func (b *bhttpClient) SetBody(body []byte) *bhttpClient { + b.postBody = body + return b +} + +func (b *bhttpClient) SetHeader(key, value string) *bhttpClient { + b.header.Add(key, value) + return b +} + +// GetRedirectedURL 获取重定向链接 +func (b *bhttpClient) GetRedirectedURL(url string) (string, error) { + var ( + err error + ) + + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + return "", err + } + + b.httpRequest = req + b.httpRequest.Header = *b.header + + resp, err := b.client.Do(b.httpRequest) + if err != nil { + return "", err + } + + defer resp.Body.Close() + + redirectedURL := resp.Request.URL.String() + return redirectedURL, nil +} + +func (b *bhttpClient) DoGet(reqUrl string) ([]byte, error) { + requestURI, err := url.ParseRequestURI(reqUrl) + if err != nil { + return nil, err + } + reader := b.values.Encode() + if reader != "" { + if requestURI.RawQuery == "" { + requestURI.RawQuery = reader + } else { + requestURI.RawQuery = fmt.Sprintf("%s&%s", requestURI.RawQuery, reader) + } + // fmt.Println("test_debug", requestURI.String()) + } + + return b.doRequest(http.MethodGet, requestURI.String(), "") +} + +func (b *bhttpClient) DoPost(url string) ([]byte, error) { + reader := b.values.Encode() + if len(b.postBody) > 0 { + reader = string(b.postBody) + } + return b.doRequest(http.MethodPost, url, reader) +} + +// DoPatch is a patch method +func (b *bhttpClient) DoPatch(url string) ([]byte, error) { + reader := string(b.postBody) + return b.doRequest(http.MethodPatch, url, reader) +} + +func (b *bhttpClient) DoPut(url string) ([]byte, error) { + reader := string(b.postBody) + return b.doRequest(http.MethodPut, url, reader) +} + +func (b *bhttpClient) doRequest(method, url, reader string) ([]byte, error) { + payload := bytes.NewBuffer([]byte(reader)) + //如果content_type是 multipart/form-data + cType := b.header.Get("Content-Type") + if strings.Contains(cType, "form-data") { + payload = &bytes.Buffer{} + bodyWriter := multipart.NewWriter(payload) + for k, v := range b.params { + bodyWriter.WriteField(k, v) + } + bodyWriter.Close() + b.header.Set("Content-Type", bodyWriter.FormDataContentType()) + } + switch method { + case http.MethodGet: + req, err := http.NewRequest(method, url, nil) + + if err != nil { + return nil, err + } + b.httpRequest = req + case http.MethodPost, http.MethodPut: + req, err := http.NewRequest(method, url, payload) + if err != nil { + return nil, err + } + b.httpRequest = req + case http.MethodPatch: + fmt.Println("url: ", url) + fmt.Println("payload: ", reader) + payload1 := strings.NewReader(`{"status": "FINALIZED"}`) + req, err := http.NewRequest(method, url, payload1) + if err != nil { + return nil, err + } + // 设置查询参数 + // q := req.URL.Query() + // for k, v := range b.params { + // q.Add(k, v) + // } + // req.URL.RawQuery = q.Encode() + b.httpRequest = req + + default: + return nil, errors.New("method not support") + } + + b.httpRequest.Header = *b.header + + var ( + res *http.Response + err error + ) + retry := b.retry + 1 + for i := 1; i <= int(retry); i++ { + body, werr := b.client.Do(b.httpRequest) + err = werr + if err == nil { + res = body + break + } + fmt.Printf("bhttp-临时超时: %d , err: %s\n, proxy: %s, header-auth: %s \n", i, err.Error(), b.proxyURL, b.header.Get("Authorization")) + time.Sleep(time.Duration(i) * time.Second) + } + + if err != nil { + return nil, err + } + + defer res.Body.Close() + + rateTimeLeft := res.Header.Get("rate-limit-msec-left") + if rateTimeLeft != "" { + rateTimeLeftInt, _ := strconv.Atoi(rateTimeLeft) + if rateTimeLeftInt > 0 { + b.rateTimeLeft = rateTimeLeftInt + } + + } + + //ioutil.ReadAll(res.Body) //io/ioutil" has been deprecated since Go 1.19 + newReader := bufio.NewReader(res.Body) + body := make([]byte, 0) + for { + data, err := newReader.ReadBytes('\n') + if err == io.EOF { + body = append(body, data...) + break + } + if err != nil { + return nil, err + } + body = append(body, data...) + } + + if b.checkStatusOk && res.StatusCode != http.StatusOK { + return nil, fmt.Errorf("status error code: %d, res: %s", res.StatusCode, string(body)) + } + + return body, nil +} + +// GetRateTimeLeft get the reader of the request body +func (b *bhttpClient) GetRateTimeLeft() int { + return b.rateTimeLeft +} + +// get the reader of the request body +func (b *bhttpClient) GetReader() string { + return b.values.Encode() +} + +// GetRedirectedURL 获取重定向url +func GetRedirectedURL(initialURL string) (string, error) { + // 创建一个自定义的HTTP客户端 + client := &http.Client{ + // 自定义CheckRedirect函数,遇到重定向时获取Location头并返回 + CheckRedirect: func(req *http.Request, via []*http.Request) error { + if len(via) > 0 { + // 返回重定向后的URL + return http.ErrUseLastResponse + } + return nil + }, + } + + // 发送GET请求 + resp, err := client.Get(initialURL) + if err != nil { + return "", err + } + defer resp.Body.Close() + + // 检查响应状态码是否为302 + if resp.StatusCode == http.StatusFound || resp.StatusCode == http.StatusMovedPermanently { + // 获取Location头中的重定向URL + redirectURL, err := resp.Location() + if err != nil { + return "", err + } + return redirectURL.String(), nil + } + + return "", fmt.Errorf("no redirect found") + +} + +func ParsedUrlWithRetry(link string) (*url.URL, error) { + for i := 0; i <= 5; i++ { + parsedURL, err := url.Parse(link) + if err == nil { + return parsedURL, nil + } + time.Sleep(1 * time.Second) + } + + return nil, fmt.Errorf("failed to parsed url: %s", link) + +} diff --git a/pkg/cache/memcache/LICENSE b/pkg/cache/memcache/LICENSE new file mode 100644 index 0000000..e69de29 diff --git a/pkg/cache/memcache/README.md b/pkg/cache/memcache/README.md new file mode 100644 index 0000000..53677bf --- /dev/null +++ b/pkg/cache/memcache/README.md @@ -0,0 +1,81 @@ +# go-generics-cache + +[![.github/workflows/test.yml](https://github.com/Code-Hex/go-generics-cache/actions/workflows/test.yml/badge.svg)](https://github.com/Code-Hex/go-generics-cache/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/Code-Hex/go-generics-cache/branch/main/graph/badge.svg?token=Wm7UEwgiZu)](https://codecov.io/gh/Code-Hex/go-generics-cache) [![Go Reference](https://pkg.go.dev/badge/github.com/Code-Hex/go-generics-cache.svg)](https://pkg.go.dev/github.com/Code-Hex/go-generics-cache) + +go-generics-cache is an in-memory key:value store/cache that is suitable for applications running on a single machine. This in-memory cache uses [Go Generics](https://go.dev/blog/generics-proposal) which is introduced in 1.18. + +- a thread-safe +- implemented with [Go Generics](https://go.dev/blog/generics-proposal) +- TTL supported (with expiration times) +- Simple cache is like `map[string]interface{}` + - See [examples](https://github.com/Code-Hex/go-generics-cache/blob/main/policy/simple/example_test.go) +- Cache replacement policies + - **Least recently used (LRU)** + - Discards the least recently used items first. + - See [examples](https://github.com/Code-Hex/go-generics-cache/blob/main/policy/lru/example_test.go) + - **Least-frequently used (LFU)** + - Counts how often an item is needed. Those that are used least often are discarded first. + - [An O(1) algorithm for implementing the LFU cache eviction scheme](http://dhruvbird.com/lfu.pdf) + - See [examples](https://github.com/Code-Hex/go-generics-cache/blob/main/policy/lfu/example_test.go) + - **First in first out (FIFO)** + - Using this algorithm the cache behaves in the same way as a [FIFO queue](https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics)). + - The cache evicts the blocks in the order they were added, without any regard to how often or how many times they were accessed before. + - See [examples](https://github.com/Code-Hex/go-generics-cache/blob/main/policy/fifo/example_test.go) + - **Most recently used (MRU)** + - In contrast to Least Recently Used (LRU), MRU discards the most recently used items first. + - See [examples](https://github.com/Code-Hex/go-generics-cache/blob/main/policy/mru/example_test.go) + - **Clock** + - Clock is a more efficient version of FIFO than Second-chance cache algorithm. + - See [examples](https://github.com/Code-Hex/go-generics-cache/blob/main/policy/clock/example_test.go) + +## Requirements + +Go 1.18 or later. + +## Install + + $ go get github.com/Code-Hex/go-generics-cache + +## Usage + +See also [examples](https://github.com/Code-Hex/go-generics-cache/blob/main/example_test.go) or [go playground](https://go.dev/play/p/kDs-6wpRAcX) + +```go +package main + +import ( + "context" + "fmt" + "time" + + cache "github.com/Code-Hex/go-generics-cache" +) + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // use simple cache algorithm without options. + c := cache.NewContext[string, int](ctx) + c.Set("a", 1) + gota, aok := c.Get("a") + gotb, bok := c.Get("b") + fmt.Println(gota, aok) // 1 true + fmt.Println(gotb, bok) // 0 false + + // Create a cache for Number constraint. key as string, value as int. + nc := cache.NewNumber[string, int]() + nc.Set("age", 26, cache.WithExpiration(time.Hour)) + + incremented := nc.Increment("age", 1) + fmt.Println(incremented) // 27 + + decremented := nc.Decrement("age", 1) + fmt.Println(decremented) // 26 +} +``` + +## Articles + +- English: [Some tips and bothers for Go 1.18 Generics](https://dev.to/codehex/some-tips-and-bothers-for-go-118-generics-lc7) +- Japanese: [Go 1.18 の Generics を使ったキャッシュライブラリを作った時に見つけた tips と微妙な点](https://zenn.dev/codehex/articles/3e6935ee6d853e) \ No newline at end of file diff --git a/pkg/cache/memcache/cache.go b/pkg/cache/memcache/cache.go new file mode 100644 index 0000000..e54f938 --- /dev/null +++ b/pkg/cache/memcache/cache.go @@ -0,0 +1,312 @@ +package memcache + +import ( + "context" + "sync" + "time" + + "sandc/pkg/cache/memcache/policy/clock" + "sandc/pkg/cache/memcache/policy/fifo" + "sandc/pkg/cache/memcache/policy/lfu" + "sandc/pkg/cache/memcache/policy/lru" + "sandc/pkg/cache/memcache/policy/mru" + "sandc/pkg/cache/memcache/policy/simple" +) + +// Interface is a common-cache interface. +type Interface[K comparable, V any] interface { + // Get looks up a key's value from the cache. + Get(key K) (value V, ok bool) + // Set sets a value to the cache with key. replacing any existing value. + Set(key K, val V) + // Keys returns the keys of the cache. The order is relied on algorithms. + Keys() []K + // Delete deletes the item with provided key from the cache. + Delete(key K) (value V, found bool) +} + +var ( + _ = []Interface[struct{}, any]{ + (*simple.Cache[struct{}, any])(nil), + (*lru.Cache[struct{}, any])(nil), + (*lfu.Cache[struct{}, any])(nil), + (*fifo.Cache[struct{}, any])(nil), + (*mru.Cache[struct{}, any])(nil), + (*clock.Cache[struct{}, any])(nil), + } +) + +// Item is an item +type Item[K comparable, V any] struct { + Key K + Value V + Expiration time.Time +} + +// Expired returns true if the item has expired. +func (item *Item[K, V]) Expired() bool { + if item.Expiration.IsZero() { + return false + } + return nowFunc().After(item.Expiration) +} + +var nowFunc = time.Now + +// ItemOption is an option for cache item. +type ItemOption func(*itemOptions) + +type itemOptions struct { + expiration time.Time // default none +} + +// WithExpiration is an option to set expiration time for any items. +// If the expiration is zero or negative value, it treats as w/o expiration. +func WithExpiration(exp time.Duration) ItemOption { + return func(o *itemOptions) { + o.expiration = nowFunc().Add(exp) + } +} + +// newItem creates a new item with specified any options. +func newItem[K comparable, V any](key K, val V, opts ...ItemOption) *Item[K, V] { + o := new(itemOptions) + for _, optFunc := range opts { + optFunc(o) + } + return &Item[K, V]{ + Key: key, + Value: val, + Expiration: o.expiration, + } +} + +// Cache is a thread safe cache. +type Cache[K comparable, V any] struct { + cache Interface[K, *Item[K, V]] + //expirations map[K]chan struct{} + // mu is used to do lock in some method process. + mu sync.Mutex + janitor *janitor + onEvicted func(K, V) +} + +// Option is an option for cache. +type Option[K comparable, V any] func(*options[K, V]) + +type options[K comparable, V any] struct { + cache Interface[K, *Item[K, V]] + janitorInterval time.Duration +} + +func newOptions[K comparable, V any]() *options[K, V] { + return &options[K, V]{ + cache: simple.NewCache[K, *Item[K, V]](), + janitorInterval: time.Minute, + } +} + +// AsLRU is an option to make a new Cache as LRU algorithm. +func AsLRU[K comparable, V any](opts ...lru.Option) Option[K, V] { + return func(o *options[K, V]) { + o.cache = lru.NewCache[K, *Item[K, V]](opts...) + } +} + +// AsLFU is an option to make a new Cache as LFU algorithm. +func AsLFU[K comparable, V any](opts ...lfu.Option) Option[K, V] { + return func(o *options[K, V]) { + o.cache = lfu.NewCache[K, *Item[K, V]](opts...) + } +} + +// AsFIFO is an option to make a new Cache as FIFO algorithm. +func AsFIFO[K comparable, V any](opts ...fifo.Option) Option[K, V] { + return func(o *options[K, V]) { + o.cache = fifo.NewCache[K, *Item[K, V]](opts...) + } +} + +// AsMRU is an option to make a new Cache as MRU algorithm. +func AsMRU[K comparable, V any](opts ...mru.Option) Option[K, V] { + return func(o *options[K, V]) { + o.cache = mru.NewCache[K, *Item[K, V]](opts...) + } +} + +// AsClock is an option to make a new Cache as clock algorithm. +func AsClock[K comparable, V any](opts ...clock.Option) Option[K, V] { + return func(o *options[K, V]) { + o.cache = clock.NewCache[K, *Item[K, V]](opts...) + } +} + +// WithJanitorInterval is an option to specify how often cache should delete expired items. +// +// Default is 1 minute. +func WithJanitorInterval[K comparable, V any](d time.Duration) Option[K, V] { + return func(o *options[K, V]) { + o.janitorInterval = d + } +} + +// New creates a new thread safe Cache. +// The janitor will not be stopped which is created by this function. If you +// want to stop the janitor gracefully, You should use the `NewContext` function +// instead of this. +// +// There are several Cache replacement policies available with you specified any options. +func New[K comparable, V any](opts ...Option[K, V]) *Cache[K, V] { + return NewContext(context.Background(), opts...) +} + +// NewContext creates a new thread safe Cache with context. +// This function will be stopped an internal janitor when the context is cancelled. +// +// There are several Cache replacement policies available with you specified any options. +func NewContext[K comparable, V any](ctx context.Context, opts ...Option[K, V]) *Cache[K, V] { + o := newOptions[K, V]() + for _, optFunc := range opts { + optFunc(o) + } + cache := &Cache[K, V]{ + cache: o.cache, + janitor: newJanitor(ctx, o.janitorInterval), + } + cache.janitor.run(cache.DeleteExpired) + return cache +} + +// Get looks up a key's value from the cache. +func (c *Cache[K, V]) Get(key K) (value V, ok bool) { + c.mu.Lock() + defer c.mu.Unlock() + item, ok := c.cache.Get(key) + + if !ok { + return + } + + // Returns nil if the item has been expired. + // Do not delete here and leave it to an external process such as Janitor. + if item.Expired() { + return value, false + } + + return item.Value, true +} + +// Fetch sets a value to the cache with key. if value not exits execute function to set +// not replacing existing value. +func (c *Cache[K, V]) Fetch(key K, duration time.Duration, fn func() V) (v V) { + c.mu.Lock() + defer c.mu.Unlock() + item, ok := c.cache.Get(key) + if !ok || item.Expired() { + v = fn() + item = newItem(key, v, WithExpiration(duration)) + c.cache.Set(key, item) + } + return item.Value +} + +// DeleteExpired all expired items from the cache. +func (c *Cache[K, V]) DeleteExpired() { + c.mu.Lock() + keys := c.cache.Keys() + c.mu.Unlock() + + for _, key := range keys { + c.mu.Lock() + // if is expired, delete it and return nil instead + item, ok := c.cache.Get(key) + if ok && item.Expired() { + v, found := c.cache.Delete(key) + if c.onEvicted != nil && found { + c.onEvicted(key, v.Value) + } + } + c.mu.Unlock() + } +} + +// Sets an (optional) function that is called with the key and value when an +// item is evicted from the cache. (Including when it is deleted manually, but +// not when it is overwritten.) Set to nil to disable. +func (c *Cache[K, V]) OnEvicted(f func(K, V)) { + c.mu.Lock() + c.onEvicted = f + c.mu.Unlock() +} + +// Set sets a value to the cache with key. replacing any existing value. +func (c *Cache[K, V]) Set(key K, val V, opts ...ItemOption) { + c.mu.Lock() + defer c.mu.Unlock() + item := newItem(key, val, opts...) + c.cache.Set(key, item) +} + +// Keys returns the keys of the cache. the order is relied on algorithms. +func (c *Cache[K, V]) Keys() []K { + c.mu.Lock() + defer c.mu.Unlock() + return c.cache.Keys() +} + +// Delete deletes the item with provided key from the cache. +func (c *Cache[K, V]) Delete(key K) { + c.mu.Lock() + defer c.mu.Unlock() + v, found := c.cache.Delete(key) + if c.onEvicted != nil && found { + c.onEvicted(key, v.Value) + } +} + +// Contains reports whether key is within cache. +func (c *Cache[K, V]) Contains(key K) bool { + c.mu.Lock() + defer c.mu.Unlock() + _, ok := c.cache.Get(key) + return ok +} + +// NumberCache is a in-memory cache which is able to store only Number constraint. +type NumberCache[K comparable, V Number] struct { + *Cache[K, V] + // nmu is used to do lock in Increment/Decrement process. + // Note that this must be here as a separate mutex because mu in Cache struct is Locked in Get, + // and if we call mu.Lock in Increment/Decrement, it will cause deadlock. + nmu sync.Mutex +} + +// NewNumber creates a new cache for Number constraint. +func NewNumber[K comparable, V Number](opts ...Option[K, V]) *NumberCache[K, V] { + return &NumberCache[K, V]{ + Cache: New(opts...), + } +} + +// Increment an item of type Number constraint by n. +// Returns the incremented value. +func (nc *NumberCache[K, V]) Increment(key K, n V) V { + // In order to avoid lost update, we must lock whole Increment/Decrement process. + nc.nmu.Lock() + defer nc.nmu.Unlock() + got, _ := nc.Cache.Get(key) + nv := got + n + nc.Cache.Set(key, nv) + return nv +} + +// Decrement an item of type Number constraint by n. +// Returns the decremented value. +func (nc *NumberCache[K, V]) Decrement(key K, n V) V { + nc.nmu.Lock() + defer nc.nmu.Unlock() + got, _ := nc.Cache.Get(key) + nv := got - n + nc.Cache.Set(key, nv) + return nv +} diff --git a/pkg/cache/memcache/cache_internal_test.go b/pkg/cache/memcache/cache_internal_test.go new file mode 100644 index 0000000..8e7646c --- /dev/null +++ b/pkg/cache/memcache/cache_internal_test.go @@ -0,0 +1,28 @@ +package memcache + +import ( + "context" + "testing" + "time" +) + +func TestDeletedCache(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + nc := NewContext[string, int](ctx) + key := "key" + nc.Set(key, 1, WithExpiration(-time.Second)) + + _, ok := nc.cache.Get(key) + if !ok { + t.Fatal("want true") + } + + nc.DeleteExpired() + + _, ok = nc.cache.Get(key) + if ok { + t.Fatal("want false") + } +} diff --git a/pkg/cache/memcache/cache_test.go b/pkg/cache/memcache/cache_test.go new file mode 100644 index 0000000..bb5ef79 --- /dev/null +++ b/pkg/cache/memcache/cache_test.go @@ -0,0 +1,123 @@ +package memcache_test + +import ( + "math/rand" + "sandc/pkg/cache/memcache" + "sync" + "testing" + "time" + + "sandc/pkg/cache/memcache/policy/clock" + "sandc/pkg/cache/memcache/policy/fifo" + "sandc/pkg/cache/memcache/policy/lfu" + "sandc/pkg/cache/memcache/policy/lru" + "sandc/pkg/cache/memcache/policy/mru" +) + +func TestMultiThreadIncr(t *testing.T) { + nc := memcache.NewNumber[string, int]() + nc.Set("counter", 0) + + var wg sync.WaitGroup + + for i := 0; i < 100; i++ { + wg.Add(1) + go func() { + _ = nc.Increment("counter", 1) + wg.Done() + }() + } + + wg.Wait() + + if counter, _ := nc.Get("counter"); counter != 100 { + t.Errorf("want %v but got %v", 100, counter) + } +} + +func TestMultiThreadDecr(t *testing.T) { + nc := memcache.NewNumber[string, int]() + nc.Set("counter", 100) + + var wg sync.WaitGroup + + for i := 0; i < 100; i++ { + wg.Add(1) + go func() { + _ = nc.Decrement("counter", 1) + wg.Done() + }() + } + + wg.Wait() + + if counter, _ := nc.Get("counter"); counter != 0 { + t.Errorf("want %v but got %v", 0, counter) + } +} + +func TestMultiThread(t *testing.T) { + cases := []struct { + name string + policy memcache.Option[int, int] + }{ + { + name: "LRU", + policy: memcache.AsLRU[int, int](lru.WithCapacity(10)), + }, + { + name: "MRU", + policy: memcache.AsMRU[int, int](mru.WithCapacity(10)), + }, + { + name: "FIFO", + policy: memcache.AsFIFO[int, int](fifo.WithCapacity(10)), + }, + { + name: "Clock", + policy: memcache.AsClock[int, int](clock.WithCapacity(10)), + }, + { + name: "LFU", + policy: memcache.AsLFU[int, int](lfu.WithCapacity(10)), + }, + } + for _, tc := range cases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + c := memcache.New(tc.policy) + var wg sync.WaitGroup + for i := int64(0); i < 100; i++ { + wg.Add(1) + go func(i int64) { + defer wg.Done() + m := rand.New(rand.NewSource(i)) + for n := 0; n < 100; n++ { + key := m.Intn(100000) + c.Set(key, m.Intn(100000)) + c.Get(key) + } + }(i) + } + + wg.Wait() + }) + } +} + +func TestCallJanitor(t *testing.T) { + c := memcache.New( + memcache.WithJanitorInterval[string, int](100 * time.Millisecond), + ) + + c.Set("1", 10, memcache.WithExpiration(10*time.Millisecond)) + c.Set("2", 20, memcache.WithExpiration(20*time.Millisecond)) + c.Set("3", 30, memcache.WithExpiration(30*time.Millisecond)) + + <-time.After(300 * time.Millisecond) + + keys := c.Keys() + if len(keys) != 0 { + t.Errorf("want items is empty but got %d", len(keys)) + } +} diff --git a/pkg/cache/memcache/constraint.go b/pkg/cache/memcache/constraint.go new file mode 100644 index 0000000..cdcc70a --- /dev/null +++ b/pkg/cache/memcache/constraint.go @@ -0,0 +1,8 @@ +package memcache + +import "golang.org/x/exp/constraints" + +// Number is a constraint that permits any numeric types. +type Number interface { + constraints.Integer | constraints.Float | constraints.Complex +} diff --git a/pkg/cache/memcache/example_test.go b/pkg/cache/memcache/example_test.go new file mode 100644 index 0000000..2934c74 --- /dev/null +++ b/pkg/cache/memcache/example_test.go @@ -0,0 +1,130 @@ +package memcache_test + +import ( + "context" + "fmt" + "sandc/pkg/cache/memcache" + "time" +) + +func ExampleCache() { + // use simple cache algorithm without options. + c := memcache.New[string, int]() + c.Set("a", 1) + gota, aok := c.Get("a") + gotb, bok := c.Get("b") + fmt.Println(gota, aok) + fmt.Println(gotb, bok) + // Output: + // 1 true + // 0 false +} + +func ExampleNewContext() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // use simple cache algorithm without options. + // an internal janitor will be stopped if specified the context is cancelled. + c := memcache.NewContext(ctx, memcache.WithJanitorInterval[string, int](3*time.Second)) + c.Set("a", 1) + gota, aok := c.Get("a") + gotb, bok := c.Get("b") + fmt.Println(gota, aok) + fmt.Println(gotb, bok) + // Output: + // 1 true + // 0 false +} + +func ExampleAsClock() { + // use clock cache algorithm. + c := memcache.New(memcache.AsClock[string, int]()) + c.Set("a", 1) + gota, aok := c.Get("a") + gotb, bok := c.Get("b") + fmt.Println(gota, aok) + fmt.Println(gotb, bok) + // Output: + // 1 true + // 0 false +} + +func ExampleWithExpiration() { + c := memcache.New(memcache.AsFIFO[string, int]()) + exp := 250 * time.Millisecond + c.Set("a", 1, memcache.WithExpiration(exp)) + + // check item is set. + gota, aok := c.Get("a") + fmt.Println(gota, aok) + + // set again + c.Set("a", 2, memcache.WithExpiration(exp)) + gota2, aok2 := c.Get("a") + fmt.Println(gota2, aok2) + + // waiting expiration. + time.Sleep(exp + 100*time.Millisecond) // + buffer + + gota3, aok3 := c.Get("a") // expired + fmt.Println(gota3, aok3) + // Output: + // 1 true + // 2 true + // 0 false +} + +func ExampleCache_Delete() { + c := memcache.New(memcache.AsMRU[string, int]()) + c.Set("a", 1) + c.Delete("a") + gota, aok := c.Get("a") + fmt.Println(gota, aok) + // Output: + // 0 false +} + +func ExampleCache_Keys() { + c := memcache.New(memcache.AsLFU[string, int]()) + c.Set("a", 1) + c.Set("b", 1) + c.Set("c", 1) + fmt.Println(c.Keys()) + // Output: + // [a b c] +} + +func ExampleCache_Contains() { + c := memcache.New(memcache.AsLRU[string, int]()) + c.Set("a", 1) + fmt.Println(c.Contains("a")) + fmt.Println(c.Contains("b")) + // Output: + // true + // false +} + +func ExampleNewNumber() { + nc := memcache.NewNumber[string, int]() + nc.Set("a", 1) + nc.Set("b", 2, memcache.WithExpiration(time.Minute)) + av := nc.Increment("a", 1) + gota, aok := nc.Get("a") + + bv := nc.Decrement("b", 1) + gotb, bok := nc.Get("b") + + // not set keys + cv := nc.Increment("c", 100) + dv := nc.Decrement("d", 100) + fmt.Println(av, gota, aok) + fmt.Println(bv, gotb, bok) + fmt.Println(cv) + fmt.Println(dv) + // Output: + // 2 2 true + // 1 1 true + // 100 + // -100 +} diff --git a/pkg/cache/memcache/export_test.go b/pkg/cache/memcache/export_test.go new file mode 100644 index 0000000..aad2009 --- /dev/null +++ b/pkg/cache/memcache/export_test.go @@ -0,0 +1,11 @@ +package memcache + +import "time" + +func SetNowFunc(tm time.Time) (reset func()) { + backup := nowFunc + nowFunc = func() time.Time { return tm } + return func() { + nowFunc = backup + } +} diff --git a/pkg/cache/memcache/go.sum b/pkg/cache/memcache/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/pkg/cache/memcache/janitor.go b/pkg/cache/memcache/janitor.go new file mode 100644 index 0000000..a2fce09 --- /dev/null +++ b/pkg/cache/memcache/janitor.go @@ -0,0 +1,48 @@ +package memcache + +import ( + "context" + "sync" + "time" +) + +// janitor for collecting expired items and cleaning them. +type janitor struct { + ctx context.Context + interval time.Duration + done chan struct{} + once sync.Once +} + +func newJanitor(ctx context.Context, interval time.Duration) *janitor { + j := &janitor{ + ctx: ctx, + interval: interval, + done: make(chan struct{}), + } + return j +} + +// stop to stop the janitor. +func (j *janitor) stop() { + j.once.Do(func() { close(j.done) }) +} + +// run with the given cleanup callback function. +func (j *janitor) run(cleanup func()) { + go func() { + ticker := time.NewTicker(j.interval) + defer ticker.Stop() + for { + select { + case <-ticker.C: + cleanup() + case <-j.done: + cleanup() // last call + return + case <-j.ctx.Done(): + j.stop() + } + } + }() +} diff --git a/pkg/cache/memcache/janitor_test.go b/pkg/cache/memcache/janitor_test.go new file mode 100644 index 0000000..081e64e --- /dev/null +++ b/pkg/cache/memcache/janitor_test.go @@ -0,0 +1,36 @@ +package memcache + +import ( + "context" + "sync/atomic" + "testing" + "time" +) + +func TestJanitor(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + janitor := newJanitor(ctx, time.Millisecond) + + checkDone := make(chan struct{}) + janitor.done = checkDone + + calledClean := int64(0) + janitor.run(func() { atomic.AddInt64(&calledClean, 1) }) + + // waiting for cleanup + time.Sleep(10 * time.Millisecond) + cancel() + + select { + case <-checkDone: + case <-time.After(time.Second): + t.Fatalf("failed to call done channel") + } + + got := atomic.LoadInt64(&calledClean) + if got <= 1 { + t.Fatalf("failed to call clean callback in janitor: %d", got) + } +} diff --git a/pkg/cache/memcache/policy/clock/clock.go b/pkg/cache/memcache/policy/clock/clock.go new file mode 100644 index 0000000..424e902 --- /dev/null +++ b/pkg/cache/memcache/policy/clock/clock.go @@ -0,0 +1,140 @@ +package clock + +import ( + "container/ring" +) + +// Cache is used The clock cache replacement policy. +// +// The clock algorithm keeps a circular list of pages in memory, with +// the "hand" (iterator) pointing to the last examined page frame in the list. +// When a page fault occurs and no empty frames exist, then the R (referenced) bit +// is inspected at the hand's location. If R is 0, the new page is put in place of +// the page the "hand" points to, and the hand is advanced one position. Otherwise, +// the R bit is cleared, then the clock hand is incremented and the process is +// repeated until a page is replaced. +type Cache[K comparable, V any] struct { + items map[K]*ring.Ring + hand *ring.Ring + head *ring.Ring + capacity int +} + +type entry[K comparable, V any] struct { + key K + val V + referenceCount int +} + +// Option is an option for clock cache. +type Option func(*options) + +type options struct { + capacity int +} + +func newOptions() *options { + return &options{ + capacity: 128, + } +} + +// WithCapacity is an option to set cache capacity. +func WithCapacity(cap int) Option { + return func(o *options) { + o.capacity = cap + } +} + +// NewCache creates a new non-thread safe clock cache whose capacity is the default size (128). +func NewCache[K comparable, V any](opts ...Option) *Cache[K, V] { + o := newOptions() + for _, optFunc := range opts { + optFunc(o) + } + r := ring.New(o.capacity) + return &Cache[K, V]{ + items: make(map[K]*ring.Ring, o.capacity), + hand: r, + head: r, + capacity: o.capacity, + } +} + +// Set sets any item to the cache. replacing any existing item. +func (c *Cache[K, V]) Set(key K, val V) { + if e, ok := c.items[key]; ok { + entry := e.Value.(*entry[K, V]) + entry.referenceCount++ + entry.val = val + return + } + c.evict() + c.hand.Value = &entry[K, V]{ + key: key, + val: val, + referenceCount: 1, + } + c.items[key] = c.hand + c.hand = c.hand.Next() +} + +// Get looks up a key's value from the cache. +func (c *Cache[K, V]) Get(key K) (zero V, _ bool) { + e, ok := c.items[key] + if !ok { + return + } + entry := e.Value.(*entry[K, V]) + entry.referenceCount++ + return entry.val, true +} + +func (c *Cache[K, V]) evict() { + for c.hand.Value != nil && c.hand.Value.(*entry[K, V]).referenceCount > 0 { + c.hand.Value.(*entry[K, V]).referenceCount-- + c.hand = c.hand.Next() + } + if c.hand.Value != nil { + entry := c.hand.Value.(*entry[K, V]) + delete(c.items, entry.key) + c.hand.Value = nil + } +} + +// Keys returns the keys of the cache. the order as same as current ring order. +func (c *Cache[K, V]) Keys() []K { + keys := make([]K, 0, len(c.items)) + r := c.head + if r.Value == nil { + return []K{} + } + // the first element + keys = append(keys, r.Value.(*entry[K, V]).key) + + // iterating + for p := c.head.Next(); p != r; p = p.Next() { + if p.Value == nil { + continue + } + e := p.Value.(*entry[K, V]) + keys = append(keys, e.key) + } + return keys +} + +// Delete deletes the item with provided key from the cache. +func (c *Cache[K, V]) Delete(key K) (v V, found bool) { + if e, ok := c.items[key]; ok { + delete(c.items, key) + v = e.Value.(*entry[K, V]).val + found = ok + e.Value = nil + } + return +} + +// Len returns the number of items in the cache. +func (c *Cache[K, V]) Len() int { + return len(c.items) +} diff --git a/pkg/cache/memcache/policy/clock/clock_test.go b/pkg/cache/memcache/policy/clock/clock_test.go new file mode 100644 index 0000000..9d458da --- /dev/null +++ b/pkg/cache/memcache/policy/clock/clock_test.go @@ -0,0 +1,151 @@ +package clock_test + +import ( + "sandc/pkg/cache/memcache/policy/clock" + "strconv" + "strings" + "testing" +) + +func TestSet(t *testing.T) { + // set capacity is 1 + cache := clock.NewCache[string, int](clock.WithCapacity(1)) + cache.Set("foo", 1) + if got := cache.Len(); got != 1 { + t.Fatalf("invalid length: %d", got) + } + if got, ok := cache.Get("foo"); got != 1 || !ok { + t.Fatalf("invalid value got %d, cachehit %v", got, ok) + } + + // if over the cap + cache.Set("bar", 2) + if got := cache.Len(); got != 1 { + t.Fatalf("invalid length: %d", got) + } + bar, ok := cache.Get("bar") + if bar != 2 || !ok { + t.Fatalf("invalid value bar %d, cachehit %v", bar, ok) + } + + // checks deleted oldest + if _, ok := cache.Get("foo"); ok { + t.Fatalf("invalid eviction the oldest value for foo %v", ok) + } + + // valid: if over the cap but same key + cache.Set("bar", 100) + if got := cache.Len(); got != 1 { + t.Fatalf("invalid length: %d", got) + } + bar, ok = cache.Get("bar") + if bar != 100 || !ok { + t.Fatalf("invalid replacing value bar %d, cachehit %v", bar, ok) + } +} + +func TestDelete(t *testing.T) { + cache := clock.NewCache[string, int](clock.WithCapacity(1)) + cache.Set("foo", 1) + if got := cache.Len(); got != 1 { + t.Fatalf("invalid length: %d", got) + } + + cache.Delete("foo2") + if got := cache.Len(); got != 1 { + t.Fatalf("invalid length after deleted does not exist key: %d", got) + } + + cache.Delete("foo") + if got := cache.Len(); got != 0 { + t.Fatalf("invalid length after deleted: %d", got) + } + if _, ok := cache.Get("foo"); ok { + t.Fatalf("invalid get after deleted %v", ok) + } +} + +func TestKeys(t *testing.T) { + t.Run("normal", func(t *testing.T) { + cache := clock.NewCache[string, int]() + if len(cache.Keys()) != 0 { + t.Errorf("want number of keys 0, but got %d", cache.Len()) + } + + cache.Set("foo", 1) + cache.Set("bar", 2) + cache.Set("baz", 3) + cache.Set("bar", 4) // again + cache.Set("foo", 5) // again + + got := strings.Join(cache.Keys(), ",") + want := strings.Join([]string{ + "foo", + "bar", + "baz", + }, ",") + if got != want { + t.Errorf("want %q, but got %q", want, got) + } + if len(cache.Keys()) != cache.Len() { + t.Errorf("want number of keys %d, but got %d", len(cache.Keys()), cache.Len()) + } + }) + + t.Run("with deletion", func(t *testing.T) { + cache := clock.NewCache[string, int](clock.WithCapacity(4)) + cache.Set("foo", 1) + cache.Set("bar", 2) + cache.Set("baz", 3) + + cache.Delete("bar") // delete in middle + + got := strings.Join(cache.Keys(), ",") + want := strings.Join([]string{ + "foo", + "baz", + }, ",") + if got != want { + t.Errorf("want %q, but got %q", want, got) + } + if len(cache.Keys()) != cache.Len() { + t.Errorf("want number of keys %d, but got %d", len(cache.Keys()), cache.Len()) + } + + cache.Set("hoge", 4) + cache.Set("fuga", 5) // over the cap. so expected to set "bar" position. + + got2 := strings.Join(cache.Keys(), ",") + want2 := strings.Join([]string{ + "foo", + "fuga", + "baz", + "hoge", + }, ",") + if got2 != want2 { + t.Errorf("want2 %q, but got2 %q", want2, got2) + } + if len(cache.Keys()) != cache.Len() { + t.Errorf("want2 number of keys %d, but got2 %d", len(cache.Keys()), cache.Len()) + } + }) +} + +func TestIssue29(t *testing.T) { + cap := 3 + cache := clock.NewCache[string, int](clock.WithCapacity(cap)) + for i := 0; i < cap; i++ { + cache.Set(strconv.Itoa(i), i) + } + cache.Set(strconv.Itoa(cap), cap) + + keys := cache.Keys() + if got := len(keys); cap != got { + t.Errorf("want number of keys %d, but got %d", cap, got) + } + wantKeys := "3,1,2" + gotKeys := strings.Join(keys, ",") + if wantKeys != gotKeys { + t.Errorf("want keys %q, but got keys %q", wantKeys, gotKeys) + } +} diff --git a/pkg/cache/memcache/policy/clock/example_test.go b/pkg/cache/memcache/policy/clock/example_test.go new file mode 100644 index 0000000..3c9d588 --- /dev/null +++ b/pkg/cache/memcache/policy/clock/example_test.go @@ -0,0 +1,48 @@ +package clock_test + +import ( + "fmt" + "sandc/pkg/cache/memcache/policy/clock" +) + +func ExampleNewCache() { + c := clock.NewCache[string, int]() + c.Set("a", 1) + c.Set("b", 2) + av, aok := c.Get("a") + bv, bok := c.Get("b") + cv, cok := c.Get("c") + fmt.Println(av, aok) + fmt.Println(bv, bok) + fmt.Println(cv, cok) + c.Delete("a") + _, aok2 := c.Get("a") + if !aok2 { + fmt.Println("key 'a' has been deleted") + } + // update + c.Set("b", 3) + newbv, _ := c.Get("b") + fmt.Println(newbv) + // Output: + // 1 true + // 2 true + // 0 false + // key 'a' has been deleted + // 3 +} + +func ExampleCache_Keys() { + c := clock.NewCache[string, int]() + c.Set("foo", 1) + c.Set("bar", 2) + c.Set("baz", 3) + keys := c.Keys() + for _, key := range keys { + fmt.Println(key) + } + // Output: + // foo + // bar + // baz +} diff --git a/pkg/cache/memcache/policy/fifo/example_test.go b/pkg/cache/memcache/policy/fifo/example_test.go new file mode 100644 index 0000000..c1cc8b3 --- /dev/null +++ b/pkg/cache/memcache/policy/fifo/example_test.go @@ -0,0 +1,48 @@ +package fifo_test + +import ( + "fmt" + "sandc/pkg/cache/memcache/policy/fifo" +) + +func ExampleNewCache() { + c := fifo.NewCache[string, int]() + c.Set("a", 1) + c.Set("b", 2) + av, aok := c.Get("a") + bv, bok := c.Get("b") + cv, cok := c.Get("c") + fmt.Println(av, aok) + fmt.Println(bv, bok) + fmt.Println(cv, cok) + c.Delete("a") + _, aok2 := c.Get("a") + if !aok2 { + fmt.Println("key 'a' has been deleted") + } + // update + c.Set("b", 3) + newbv, _ := c.Get("b") + fmt.Println(newbv) + // Output: + // 1 true + // 2 true + // 0 false + // key 'a' has been deleted + // 3 +} + +func ExampleCache_Keys() { + c := fifo.NewCache[string, int]() + c.Set("foo", 1) + c.Set("bar", 2) + c.Set("baz", 3) + keys := c.Keys() + for _, key := range keys { + fmt.Println(key) + } + // Output: + // foo + // bar + // baz +} diff --git a/pkg/cache/memcache/policy/fifo/fifo.go b/pkg/cache/memcache/policy/fifo/fifo.go new file mode 100644 index 0000000..89f5696 --- /dev/null +++ b/pkg/cache/memcache/policy/fifo/fifo.go @@ -0,0 +1,108 @@ +package fifo + +import ( + "container/list" +) + +// Cache is used a FIFO (First in first out) cache replacement policy. +// +// In FIFO the item that enter the cache first is evicted first +// w/o any regard of how often or how many times it was accessed before. +type Cache[K comparable, V any] struct { + items map[K]*list.Element + queue *list.List // keys + capacity int +} + +type entry[K comparable, V any] struct { + key K + val V +} + +// Option is an option for FIFO cache. +type Option func(*options) + +type options struct { + capacity int +} + +func newOptions() *options { + return &options{ + capacity: 128, + } +} + +// WithCapacity is an option to set cache capacity. +func WithCapacity(cap int) Option { + return func(o *options) { + o.capacity = cap + } +} + +// NewCache creates a new non-thread safe FIFO cache whose capacity is the default size (128). +func NewCache[K comparable, V any](opts ...Option) *Cache[K, V] { + o := newOptions() + for _, optFunc := range opts { + optFunc(o) + } + return &Cache[K, V]{ + items: make(map[K]*list.Element, o.capacity), + queue: list.New(), + capacity: o.capacity, + } +} + +// Set sets any item to the cache. replacing any existing item. +func (c *Cache[K, V]) Set(key K, val V) { + if c.queue.Len() == c.capacity { + e := c.dequeue() + delete(c.items, e.Value.(*entry[K, V]).key) + } + c.Delete(key) // delete old key if already exists specified key. + entry := &entry[K, V]{ + key: key, + val: val, + } + e := c.queue.PushBack(entry) + c.items[key] = e +} + +// Get gets an item from the cache. +// Returns the item or zero value, and a bool indicating whether the key was found. +func (c *Cache[K, V]) Get(k K) (val V, ok bool) { + got, found := c.items[k] + if !found { + return + } + return got.Value.(*entry[K, V]).val, true +} + +// Keys returns cache keys. +func (c *Cache[K, V]) Keys() []K { + keys := make([]K, 0, len(c.items)) + for e := c.queue.Front(); e != nil; e = e.Next() { + keys = append(keys, e.Value.(*entry[K, V]).key) + } + return keys +} + +// Delete deletes the item with provided key from the cache. +func (c *Cache[K, V]) Delete(key K) (v V, found bool) { + if e, ok := c.items[key]; ok { + c.queue.Remove(e) + delete(c.items, key) + return e.Value.(*entry[K, V]).val, true + } + return +} + +// Len returns the number of items in the cache. +func (c *Cache[K, V]) Len() int { + return c.queue.Len() +} + +func (c *Cache[K, V]) dequeue() *list.Element { + e := c.queue.Front() + c.queue.Remove(e) + return e +} diff --git a/pkg/cache/memcache/policy/fifo/fifo_test.go b/pkg/cache/memcache/policy/fifo/fifo_test.go new file mode 100644 index 0000000..56c52ee --- /dev/null +++ b/pkg/cache/memcache/policy/fifo/fifo_test.go @@ -0,0 +1,87 @@ +package fifo_test + +import ( + "sandc/pkg/cache/memcache/policy/fifo" + "strings" + "testing" +) + +func TestSet(t *testing.T) { + // set capacity is 1 + cache := fifo.NewCache[string, int](fifo.WithCapacity(1)) + cache.Set("foo", 1) + if got := cache.Len(); got != 1 { + t.Fatalf("invalid length: %d", got) + } + if got, ok := cache.Get("foo"); got != 1 || !ok { + t.Fatalf("invalid value got %d, cachehit %v", got, ok) + } + + // if over the cap + cache.Set("bar", 2) + if got := cache.Len(); got != 1 { + t.Fatalf("invalid length: %d", got) + } + bar, ok := cache.Get("bar") + if bar != 2 || !ok { + t.Fatalf("invalid value bar %d, cachehit %v", bar, ok) + } + + // checks deleted oldest + if _, ok := cache.Get("foo"); ok { + t.Fatalf("invalid eviction the oldest value for foo %v", ok) + } + + // valid: if over the cap but same key + cache.Set("bar", 100) + if got := cache.Len(); got != 1 { + t.Fatalf("invalid length: %d", got) + } + bar, ok = cache.Get("bar") + if bar != 100 || !ok { + t.Fatalf("invalid replacing value bar %d, cachehit %v", bar, ok) + } +} + +func TestDelete(t *testing.T) { + cache := fifo.NewCache[string, int](fifo.WithCapacity(1)) + cache.Set("foo", 1) + if got := cache.Len(); got != 1 { + t.Fatalf("invalid length: %d", got) + } + + cache.Delete("foo2") + if got := cache.Len(); got != 1 { + t.Fatalf("invalid length after deleted does not exist key: %d", got) + } + + cache.Delete("foo") + if got := cache.Len(); got != 0 { + t.Fatalf("invalid length after deleted: %d", got) + } + if _, ok := cache.Get("foo"); ok { + t.Fatalf("invalid get after deleted %v", ok) + } +} + +func TestKeys(t *testing.T) { + cache := fifo.NewCache[string, int]() + cache.Set("foo", 1) + cache.Set("bar", 2) + cache.Set("baz", 3) + cache.Set("bar", 4) // again + cache.Set("foo", 5) // again + + got := strings.Join(cache.Keys(), ",") + want := strings.Join([]string{ + "baz", + "bar", + "foo", + }, ",") + if got != want { + t.Errorf("want %q, but got %q", want, got) + } + if len(cache.Keys()) != cache.Len() { + t.Errorf("want number of keys %d, but got %d", len(cache.Keys()), cache.Len()) + } +} diff --git a/pkg/cache/memcache/policy/lfu/example_test.go b/pkg/cache/memcache/policy/lfu/example_test.go new file mode 100644 index 0000000..ee9ea59 --- /dev/null +++ b/pkg/cache/memcache/policy/lfu/example_test.go @@ -0,0 +1,37 @@ +package lfu_test + +import ( + "fmt" + "sandc/pkg/cache/memcache/policy/lfu" +) + +func ExampleNewCache() { + c := lfu.NewCache[string, int]() + c.Set("a", 1) + c.Set("b", 2) + av, aok := c.Get("a") + bv, bok := c.Get("b") + cv, cok := c.Get("c") + fmt.Println(av, aok) + fmt.Println(bv, bok) + fmt.Println(cv, cok) + // Output: + // 1 true + // 2 true + // 0 false +} + +func ExampleCache_Keys() { + c := lfu.NewCache[string, int]() + c.Set("a", 1) + c.Set("b", 2) + c.Set("c", 3) + keys := c.Keys() + for _, key := range keys { + fmt.Println(key) + } + // Output: + // a + // b + // c +} diff --git a/pkg/cache/memcache/policy/lfu/lfu.go b/pkg/cache/memcache/policy/lfu/lfu.go new file mode 100644 index 0000000..d649b09 --- /dev/null +++ b/pkg/cache/memcache/policy/lfu/lfu.go @@ -0,0 +1,102 @@ +package lfu + +import ( + "container/heap" +) + +// Cache is used a LFU (Least-frequently used) cache replacement policy. +// +// Counts how often an item is needed. Those that are used least often are discarded first. +// This works very similar to LRU except that instead of storing the value of how recently +// a block was accessed, we store the value of how many times it was accessed. So of course +// while running an access sequence we will replace a block which was used fewest times from our cache. +type Cache[K comparable, V any] struct { + cap int + queue *priorityQueue[K, V] + items map[K]*entry[K, V] +} + +// Option is an option for LFU cache. +type Option func(*options) + +type options struct { + capacity int +} + +func newOptions() *options { + return &options{ + capacity: 128, + } +} + +// WithCapacity is an option to set cache capacity. +func WithCapacity(cap int) Option { + return func(o *options) { + o.capacity = cap + } +} + +// NewCache creates a new non-thread safe LFU cache whose capacity is the default size (128). +func NewCache[K comparable, V any](opts ...Option) *Cache[K, V] { + o := newOptions() + for _, optFunc := range opts { + optFunc(o) + } + return &Cache[K, V]{ + cap: o.capacity, + queue: newPriorityQueue[K, V](o.capacity), + items: make(map[K]*entry[K, V], o.capacity), + } +} + +// Get looks up a key's value from the cache. +func (c *Cache[K, V]) Get(key K) (zero V, _ bool) { + e, ok := c.items[key] + if !ok { + return + } + e.referenced() + heap.Fix(c.queue, e.index) + return e.val, true +} + +// Set sets a value to the cache with key. replacing any existing value. +func (c *Cache[K, V]) Set(key K, val V) { + if e, ok := c.items[key]; ok { + c.queue.update(e, val) + return + } + + if len(c.items) == c.cap { + evictedEntry := heap.Pop(c.queue).(*entry[K, V]) + delete(c.items, evictedEntry.key) + } + + e := newEntry(key, val) + heap.Push(c.queue, e) + c.items[key] = e +} + +// Keys returns the keys of the cache. the order is from oldest to newest. +func (c *Cache[K, V]) Keys() []K { + keys := make([]K, 0, len(c.items)) + for _, entry := range *c.queue { + keys = append(keys, entry.key) + } + return keys +} + +// Delete deletes the item with provided key from the cache. +func (c *Cache[K, V]) Delete(key K) (v V, found bool) { + if e, ok := c.items[key]; ok { + heap.Remove(c.queue, e.index) + delete(c.items, key) + return e.val, true + } + return +} + +// Len returns the number of items in the cache. +func (c *Cache[K, V]) Len() int { + return c.queue.Len() +} diff --git a/pkg/cache/memcache/policy/lfu/lfu_test.go b/pkg/cache/memcache/policy/lfu/lfu_test.go new file mode 100644 index 0000000..f2df003 --- /dev/null +++ b/pkg/cache/memcache/policy/lfu/lfu_test.go @@ -0,0 +1,64 @@ +package lfu_test + +import ( + "sandc/pkg/cache/memcache/policy/lfu" + "testing" +) + +func TestSet(t *testing.T) { + // set capacity is 1 + cache := lfu.NewCache[string, int](lfu.WithCapacity(1)) + cache.Set("foo", 1) + if got := cache.Len(); got != 1 { + t.Fatalf("invalid length: %d", got) + } + if got, ok := cache.Get("foo"); got != 1 || !ok { + t.Fatalf("invalid value got %d, cachehit %v", got, ok) + } + + // if over the cap + cache.Set("bar", 2) + if got := cache.Len(); got != 1 { + t.Fatalf("invalid length: %d", got) + } + bar, ok := cache.Get("bar") + if bar != 2 || !ok { + t.Fatalf("invalid value bar %d, cachehit %v", bar, ok) + } + + // checks deleted oldest + if _, ok := cache.Get("foo"); ok { + t.Fatalf("invalid delete oldest value foo %v", ok) + } + + // valid: if over the cap but same key + cache.Set("bar", 100) + if got := cache.Len(); got != 1 { + t.Fatalf("invalid length: %d", got) + } + bar, ok = cache.Get("bar") + if bar != 100 || !ok { + t.Fatalf("invalid replacing value bar %d, cachehit %v", bar, ok) + } +} + +func TestDelete(t *testing.T) { + cache := lfu.NewCache[string, int](lfu.WithCapacity(1)) + cache.Set("foo", 1) + if got := cache.Len(); got != 1 { + t.Fatalf("invalid length: %d", got) + } + + cache.Delete("foo2") + if got := cache.Len(); got != 1 { + t.Fatalf("invalid length after deleted does not exist key: %d", got) + } + + cache.Delete("foo") + if got := cache.Len(); got != 0 { + t.Fatalf("invalid length after deleted: %d", got) + } + if _, ok := cache.Get("foo"); ok { + t.Fatalf("invalid get after deleted %v", ok) + } +} diff --git a/pkg/cache/memcache/policy/lfu/priority_queue_test.go b/pkg/cache/memcache/policy/lfu/priority_queue_test.go new file mode 100644 index 0000000..2a245f1 --- /dev/null +++ b/pkg/cache/memcache/policy/lfu/priority_queue_test.go @@ -0,0 +1,75 @@ +package lfu + +import ( + "container/heap" + "testing" + "time" +) + +func TestPriorityQueue(t *testing.T) { + // perl -MList::Util -e 'print join ",", List::Util::shuffle(1..10)' + nums := []int{2, 1, 4, 5, 6, 9, 7, 10, 8, 3} + queue := newPriorityQueue[int, int](len(nums)) + entries := make([]*entry[int, int], 0, len(nums)) + + for _, v := range nums { + entry := newEntry(v, v) + entries = append(entries, entry) + heap.Push(queue, entry) + } + + if got := queue.Len(); len(nums) != got { + t.Errorf("want %d, but got %d", len(nums), got) + } + + // check the initial state + for idx, entry := range *queue { + if entry.index != idx { + t.Errorf("want index %d, but got %d", entry.index, idx) + } + if entry.referenceCount != 1 { + t.Errorf("want count 1") + } + if got := entry.val; nums[idx] != got { + t.Errorf("want value %d but got %d", nums[idx], got) + } + } + + // updates len - 1 entries (updated all reference count and referenced_at) + // so the lowest priority will be the last element. + // + // this loop creates + // - Reference counters other than the last element are 2. + // - The first element is the oldest referenced_at in reference counter is 2 + for i := 0; i < len(nums)-1; i++ { + entry := entries[i] + queue.update(entry, nums[i]) + time.Sleep(time.Millisecond) + } + + // check the priority by reference counter + wantValue := nums[len(nums)-1] + got := heap.Pop(queue).(*entry[int, int]) + if got.index != -1 { + t.Errorf("want index -1, but got %d", got.index) + } + if wantValue != got.val { + t.Errorf("want the lowest priority value is %d, but got %d", wantValue, got.val) + } + if want, got := len(nums)-1, queue.Len(); want != got { + t.Errorf("want %d, but got %d", want, got) + } + + // check the priority by referenced_at + wantValue2 := nums[0] + got2 := heap.Pop(queue).(*entry[int, int]) + if got.index != -1 { + t.Errorf("want index -1, but got %d", got.index) + } + if wantValue2 != got2.val { + t.Errorf("want the lowest priority value is %d, but got %d", wantValue2, got2.val) + } + if want, got := len(nums)-2, queue.Len(); want != got { + t.Errorf("want %d, but got %d", want, got) + } +} diff --git a/pkg/cache/memcache/policy/lfu/priotiry_queue.go b/pkg/cache/memcache/policy/lfu/priotiry_queue.go new file mode 100644 index 0000000..9e7e091 --- /dev/null +++ b/pkg/cache/memcache/policy/lfu/priotiry_queue.go @@ -0,0 +1,76 @@ +package lfu + +import ( + "container/heap" + "time" +) + +type entry[K comparable, V any] struct { + index int + key K + val V + referenceCount int + referencedAt time.Time +} + +func newEntry[K comparable, V any](key K, val V) *entry[K, V] { + return &entry[K, V]{ + index: 0, + key: key, + val: val, + referenceCount: 1, + referencedAt: time.Now(), + } +} + +func (e *entry[K, V]) referenced() { + e.referenceCount++ + e.referencedAt = time.Now() +} + +type priorityQueue[K comparable, V any] []*entry[K, V] + +func newPriorityQueue[K comparable, V any](cap int) *priorityQueue[K, V] { + queue := make(priorityQueue[K, V], 0, cap) + return &queue +} + +// see example of priority queue: https://pkg.go.dev/container/heap +var _ heap.Interface = (*priorityQueue[struct{}, interface{}])(nil) + +func (l priorityQueue[K, V]) Len() int { return len(l) } + +func (l priorityQueue[K, V]) Less(i, j int) bool { + if l[i].referenceCount == l[j].referenceCount { + return l[i].referencedAt.Before(l[j].referencedAt) + } + return l[i].referenceCount < l[j].referenceCount +} + +func (l priorityQueue[K, V]) Swap(i, j int) { + l[i], l[j] = l[j], l[i] + l[i].index = i + l[i].index = j +} + +func (l *priorityQueue[K, V]) Push(x interface{}) { + entry := x.(*entry[K, V]) + entry.index = len(*l) + *l = append(*l, entry) +} + +func (l *priorityQueue[K, V]) Pop() interface{} { + old := *l + n := len(old) + entry := old[n-1] + old[n-1] = nil // avoid memory leak + entry.index = -1 // for safety + *l = old[0 : n-1] + return entry +} + +func (pq *priorityQueue[K, V]) update(e *entry[K, V], val V) { + e.val = val + e.referenced() + heap.Fix(pq, e.index) +} diff --git a/pkg/cache/memcache/policy/lru/example_test.go b/pkg/cache/memcache/policy/lru/example_test.go new file mode 100644 index 0000000..79f7ede --- /dev/null +++ b/pkg/cache/memcache/policy/lru/example_test.go @@ -0,0 +1,37 @@ +package lru_test + +import ( + "fmt" + "sandc/pkg/cache/memcache/policy/lru" +) + +func ExampleNewCache() { + c := lru.NewCache[string, int]() + c.Set("a", 1) + c.Set("b", 2) + av, aok := c.Get("a") + bv, bok := c.Get("b") + cv, cok := c.Get("c") + fmt.Println(av, aok) + fmt.Println(bv, bok) + fmt.Println(cv, cok) + // Output: + // 1 true + // 2 true + // 0 false +} + +func ExampleCache_Keys() { + c := lru.NewCache[string, int]() + c.Set("a", 1) + c.Set("b", 2) + c.Set("c", 3) + keys := c.Keys() + for _, key := range keys { + fmt.Println(key) + } + // Output: + // a + // b + // c +} diff --git a/pkg/cache/memcache/policy/lru/lru.go b/pkg/cache/memcache/policy/lru/lru.go new file mode 100644 index 0000000..d3308e0 --- /dev/null +++ b/pkg/cache/memcache/policy/lru/lru.go @@ -0,0 +1,122 @@ +package lru + +import ( + "container/list" +) + +// Cache is used a LRU (Least recently used) cache replacement policy. +// +// Discards the least recently used items first. This algorithm requires +// keeping track of what was used when, which is expensive if one wants +// to make sure the algorithm always discards the least recently used item. +type Cache[K comparable, V any] struct { + cap int + list *list.List + items map[K]*list.Element +} + +type entry[K comparable, V any] struct { + key K + val V +} + +// Option is an option for LRU cache. +type Option func(*options) + +type options struct { + capacity int +} + +func newOptions() *options { + return &options{ + capacity: 128, + } +} + +// WithCapacity is an option to set cache capacity. +func WithCapacity(cap int) Option { + return func(o *options) { + o.capacity = cap + } +} + +// NewCache creates a new non-thread safe LRU cache whose capacity is the default size (128). +func NewCache[K comparable, V any](opts ...Option) *Cache[K, V] { + o := newOptions() + for _, optFunc := range opts { + optFunc(o) + } + return &Cache[K, V]{ + cap: o.capacity, + list: list.New(), + items: make(map[K]*list.Element, o.capacity), + } +} + +// Get looks up a key's value from the cache. +func (c *Cache[K, V]) Get(key K) (zero V, _ bool) { + e, ok := c.items[key] + if !ok { + return + } + // updates cache order + c.list.MoveToFront(e) + return e.Value.(*entry[K, V]).val, true +} + +// Set sets a value to the cache with key. replacing any existing value. +func (c *Cache[K, V]) Set(key K, val V) { + if e, ok := c.items[key]; ok { + // updates cache order + c.list.MoveToFront(e) + entry := e.Value.(*entry[K, V]) + entry.val = val + return + } + + newEntry := &entry[K, V]{ + key: key, + val: val, + } + e := c.list.PushFront(newEntry) + c.items[key] = e + + if c.list.Len() > c.cap { + c.deleteOldest() + } +} + +// Keys returns the keys of the cache. the order is from oldest to newest. +func (c *Cache[K, V]) Keys() []K { + keys := make([]K, 0, len(c.items)) + for ent := c.list.Back(); ent != nil; ent = ent.Prev() { + entry := ent.Value.(*entry[K, V]) + keys = append(keys, entry.key) + } + return keys +} + +// Len returns the number of items in the cache. +func (c *Cache[K, V]) Len() int { + return c.list.Len() +} + +// Delete deletes the item with provided key from the cache. +func (c *Cache[K, V]) Delete(key K) (v V, found bool) { + if e, ok := c.items[key]; ok { + c.delete(e) + return e.Value.(*entry[K, V]).val, true + } + return +} + +func (c *Cache[K, V]) deleteOldest() { + e := c.list.Back() + c.delete(e) +} + +func (c *Cache[K, V]) delete(e *list.Element) { + c.list.Remove(e) + entry := e.Value.(*entry[K, V]) + delete(c.items, entry.key) +} diff --git a/pkg/cache/memcache/policy/lru/lru_test.go b/pkg/cache/memcache/policy/lru/lru_test.go new file mode 100644 index 0000000..ab9a284 --- /dev/null +++ b/pkg/cache/memcache/policy/lru/lru_test.go @@ -0,0 +1,64 @@ +package lru_test + +import ( + "sandc/pkg/cache/memcache/policy/lru" + "testing" +) + +func TestSet(t *testing.T) { + // set capacity is 1 + cache := lru.NewCache[string, int](lru.WithCapacity(1)) + cache.Set("foo", 1) + if got := cache.Len(); got != 1 { + t.Fatalf("invalid length: %d", got) + } + if got, ok := cache.Get("foo"); got != 1 || !ok { + t.Fatalf("invalid value got %d, cachehit %v", got, ok) + } + + // if over the cap + cache.Set("bar", 2) + if got := cache.Len(); got != 1 { + t.Fatalf("invalid length: %d", got) + } + bar, ok := cache.Get("bar") + if bar != 2 || !ok { + t.Fatalf("invalid value bar %d, cachehit %v", bar, ok) + } + + // checks deleted oldest + if _, ok := cache.Get("foo"); ok { + t.Fatalf("invalid delete oldest value foo %v", ok) + } + + // valid: if over the cap but same key + cache.Set("bar", 100) + if got := cache.Len(); got != 1 { + t.Fatalf("invalid length: %d", got) + } + bar, ok = cache.Get("bar") + if bar != 100 || !ok { + t.Fatalf("invalid replacing value bar %d, cachehit %v", bar, ok) + } +} + +func TestDelete(t *testing.T) { + cache := lru.NewCache[string, int](lru.WithCapacity(1)) + cache.Set("foo", 1) + if got := cache.Len(); got != 1 { + t.Fatalf("invalid length: %d", got) + } + + cache.Delete("foo2") + if got := cache.Len(); got != 1 { + t.Fatalf("invalid length after deleted does not exist key: %d", got) + } + + cache.Delete("foo") + if got := cache.Len(); got != 0 { + t.Fatalf("invalid length after deleted: %d", got) + } + if _, ok := cache.Get("foo"); ok { + t.Fatalf("invalid get after deleted %v", ok) + } +} diff --git a/pkg/cache/memcache/policy/mru/example_test.go b/pkg/cache/memcache/policy/mru/example_test.go new file mode 100644 index 0000000..dcf76ac --- /dev/null +++ b/pkg/cache/memcache/policy/mru/example_test.go @@ -0,0 +1,37 @@ +package mru_test + +import ( + "fmt" + "sandc/pkg/cache/memcache/policy/mru" +) + +func ExampleNewCache() { + c := mru.NewCache[string, int]() + c.Set("a", 1) + c.Set("b", 2) + av, aok := c.Get("a") + bv, bok := c.Get("b") + cv, cok := c.Get("c") + fmt.Println(av, aok) + fmt.Println(bv, bok) + fmt.Println(cv, cok) + // Output: + // 1 true + // 2 true + // 0 false +} + +func ExampleCache_Keys() { + c := mru.NewCache[string, int]() + c.Set("a", 1) + c.Set("b", 2) + c.Set("c", 3) + keys := c.Keys() + for _, key := range keys { + fmt.Println(key) + } + // Output: + // c + // b + // a +} diff --git a/pkg/cache/memcache/policy/mru/mru.go b/pkg/cache/memcache/policy/mru/mru.go new file mode 100644 index 0000000..6dad163 --- /dev/null +++ b/pkg/cache/memcache/policy/mru/mru.go @@ -0,0 +1,120 @@ +package mru + +import ( + "container/list" +) + +// Cache is used a MRU (Most recently used) cache replacement policy. +// +// In contrast to Least Recently Used (LRU), MRU discards the most recently used items first. +type Cache[K comparable, V any] struct { + cap int + list *list.List + items map[K]*list.Element +} + +type entry[K comparable, V any] struct { + key K + val V +} + +// Option is an option for MRU cache. +type Option func(*options) + +type options struct { + capacity int +} + +func newOptions() *options { + return &options{ + capacity: 128, + } +} + +// WithCapacity is an option to set cache capacity. +func WithCapacity(cap int) Option { + return func(o *options) { + o.capacity = cap + } +} + +// NewCache creates a new non-thread safe MRU cache whose capacity is the default size (128). +func NewCache[K comparable, V any](opts ...Option) *Cache[K, V] { + o := newOptions() + for _, optFunc := range opts { + optFunc(o) + } + return &Cache[K, V]{ + cap: o.capacity, + list: list.New(), + items: make(map[K]*list.Element, o.capacity), + } +} + +// Get looks up a key's value from the cache. +func (c *Cache[K, V]) Get(key K) (zero V, _ bool) { + e, ok := c.items[key] + if !ok { + return + } + // updates cache order + c.list.MoveToBack(e) + return e.Value.(*entry[K, V]).val, true +} + +// Set sets a value to the cache with key. replacing any existing value. +func (c *Cache[K, V]) Set(key K, val V) { + if e, ok := c.items[key]; ok { + // updates cache order + c.list.MoveToBack(e) + entry := e.Value.(*entry[K, V]) + entry.val = val + return + } + + if c.list.Len() == c.cap { + c.deleteNewest() + } + + newEntry := &entry[K, V]{ + key: key, + val: val, + } + e := c.list.PushBack(newEntry) + c.items[key] = e +} + +// Keys returns the keys of the cache. the order is from recently used. +func (c *Cache[K, V]) Keys() []K { + keys := make([]K, 0, len(c.items)) + for ent := c.list.Back(); ent != nil; ent = ent.Prev() { + entry := ent.Value.(*entry[K, V]) + keys = append(keys, entry.key) + } + return keys +} + +// Len returns the number of items in the cache. +func (c *Cache[K, V]) Len() int { + return c.list.Len() +} + +// Delete deletes the item with provided key from the cache. +func (c *Cache[K, V]) Delete(key K) (v V, found bool) { + if e, ok := c.items[key]; ok { + c.delete(e) + return e.Value.(*entry[K, V]).val, true + } + return +} + +func (c *Cache[K, V]) deleteNewest() { + e := c.list.Front() + c.delete(e) +} + +func (c *Cache[K, V]) delete(e *list.Element) { + c.list.Remove(e) + entry := e.Value.(*entry[K, V]) + delete(c.items, entry.key) +} diff --git a/pkg/cache/memcache/policy/mru/mru_test.go b/pkg/cache/memcache/policy/mru/mru_test.go new file mode 100644 index 0000000..de9c775 --- /dev/null +++ b/pkg/cache/memcache/policy/mru/mru_test.go @@ -0,0 +1,92 @@ +package mru_test + +import ( + "sandc/pkg/cache/memcache/policy/mru" + "strings" + "testing" +) + +func TestSet(t *testing.T) { + cache := mru.NewCache[string, int](mru.WithCapacity(2)) + cache.Set("foo", 1) + cache.Set("bar", 2) + if got := cache.Len(); got != 2 { + t.Fatalf("invalid length: %d", got) + } + if got, ok := cache.Get("foo"); got != 1 || !ok { + t.Fatalf("invalid value got %d, cachehit %v", got, ok) + } + + // if over the cap + cache.Set("baz", 3) + if got := cache.Len(); got != 2 { + t.Fatalf("invalid length: %d", got) + } + baz, ok := cache.Get("baz") + if baz != 3 || !ok { + t.Fatalf("invalid value baz %d, cachehit %v", baz, ok) + } + + // check eviction most recently used + if _, ok := cache.Get("bar"); ok { + t.Log(cache.Keys()) + t.Fatalf("invalid eviction the newest value for bar %v", ok) + } + + // current state + // - baz <- recently used + // - foo + + // valid: if over the cap but specify the oldest key + cache.Set("foo", 100) + if got := cache.Len(); got != 2 { + t.Fatalf("invalid length: %d", got) + } + foo, ok := cache.Get("foo") + if foo != 100 || !ok { + t.Fatalf("invalid replacing value foo %d, cachehit %v", foo, ok) + } +} + +func TestDelete(t *testing.T) { + cache := mru.NewCache[string, int](mru.WithCapacity(1)) + cache.Set("foo", 1) + if got := cache.Len(); got != 1 { + t.Fatalf("invalid length: %d", got) + } + + cache.Delete("foo2") + if got := cache.Len(); got != 1 { + t.Fatalf("invalid length after deleted does not exist key: %d", got) + } + + cache.Delete("foo") + if got := cache.Len(); got != 0 { + t.Fatalf("invalid length after deleted: %d", got) + } + if _, ok := cache.Get("foo"); ok { + t.Fatalf("invalid get after deleted %v", ok) + } +} + +func TestKeys(t *testing.T) { + cache := mru.NewCache[string, int]() + cache.Set("foo", 1) + cache.Set("bar", 2) + cache.Set("baz", 3) + cache.Set("bar", 4) // again + cache.Set("foo", 5) // again + + got := strings.Join(cache.Keys(), ",") + want := strings.Join([]string{ + "foo", + "bar", + "baz", + }, ",") + if got != want { + t.Errorf("want %q, but got %q", want, got) + } + if len(cache.Keys()) != cache.Len() { + t.Errorf("want number of keys %d, but got %d", len(cache.Keys()), cache.Len()) + } +} diff --git a/pkg/cache/memcache/policy/simple/example_test.go b/pkg/cache/memcache/policy/simple/example_test.go new file mode 100644 index 0000000..d1039cf --- /dev/null +++ b/pkg/cache/memcache/policy/simple/example_test.go @@ -0,0 +1,48 @@ +package simple_test + +import ( + "fmt" + "sandc/pkg/cache/memcache/policy/simple" +) + +func ExampleNewCache() { + c := simple.NewCache[string, int]() + c.Set("a", 1) + c.Set("b", 2) + av, aok := c.Get("a") + bv, bok := c.Get("b") + cv, cok := c.Get("c") + fmt.Println(av, aok) + fmt.Println(bv, bok) + fmt.Println(cv, cok) + c.Delete("a") + _, aok2 := c.Get("a") + if !aok2 { + fmt.Println("key 'a' has been deleted") + } + // update + c.Set("b", 3) + newbv, _ := c.Get("b") + fmt.Println(newbv) + // Output: + // 1 true + // 2 true + // 0 false + // key 'a' has been deleted + // 3 +} + +func ExampleCache_Keys() { + c := simple.NewCache[string, int]() + c.Set("foo", 1) + c.Set("bar", 2) + c.Set("baz", 3) + keys := c.Keys() + for _, key := range keys { + fmt.Println(key) + } + // Output: + // foo + // bar + // baz +} diff --git a/pkg/cache/memcache/policy/simple/simple.go b/pkg/cache/memcache/policy/simple/simple.go new file mode 100644 index 0000000..a68df08 --- /dev/null +++ b/pkg/cache/memcache/policy/simple/simple.go @@ -0,0 +1,65 @@ +package simple + +import ( + "sort" + "time" +) + +// Cache is a simple cache has no clear priority for evict cache. +type Cache[K comparable, V any] struct { + items map[K]*entry[V] +} + +type entry[V any] struct { + val V + createdAt time.Time +} + +// NewCache creates a new non-thread safe cache. +func NewCache[K comparable, V any]() *Cache[K, V] { + return &Cache[K, V]{ + items: make(map[K]*entry[V], 0), + } +} + +// Set sets any item to the cache. replacing any existing item. +// The default item never expires. +func (c *Cache[K, V]) Set(k K, v V) { + c.items[k] = &entry[V]{ + val: v, + createdAt: time.Now(), + } +} + +// Get gets an item from the cache. +// Returns the item or zero value, and a bool indicating whether the key was found. +func (c *Cache[K, V]) Get(k K) (val V, ok bool) { + got, found := c.items[k] + if !found { + return + } + return got.val, true +} + +// Keys returns cache keys. the order is sorted by created. +func (c *Cache[K, _]) Keys() []K { + ret := make([]K, 0, len(c.items)) + for key := range c.items { + ret = append(ret, key) + } + sort.Slice(ret, func(i, j int) bool { + i1 := c.items[ret[i]] + i2 := c.items[ret[j]] + return i1.createdAt.Before(i2.createdAt) + }) + return ret +} + +// Delete deletes the item with provided key from the cache. +func (c *Cache[K, V]) Delete(key K) (v V, found bool) { + if e, ok := c.items[key]; ok { + delete(c.items, key) + return e.val, true + } + return +} diff --git a/pkg/cache/rockscache/LICENSE b/pkg/cache/rockscache/LICENSE new file mode 100644 index 0000000..e69de29 diff --git a/pkg/cache/rockscache/README.md b/pkg/cache/rockscache/README.md new file mode 100644 index 0000000..08b5e5c --- /dev/null +++ b/pkg/cache/rockscache/README.md @@ -0,0 +1,137 @@ +# forked by https://github.com/dtm-labs/rockscache.git +![license](https://img.shields.io/github/license/dtm-labs/rockscache) +![Build Status](https://github.com/dtm-labs/rockscache/actions/workflows/tests.yml/badge.svg?branch=main) +[![codecov](https://codecov.io/gh/dtm-labs/rockscache/branch/main/graph/badge.svg?token=UKKEYQLP3F)](https://codecov.io/gh/dtm-labs/rockscache) +[![Go Report Card](https://goreportcard.com/badge/github.com/dtm-labs/rockscache)](https://goreportcard.com/report/github.com/dtm-labs/rockscache) +[![Go Reference](https://pkg.go.dev/badge/github.com/dtm-labs/rockscache.svg)](https://pkg.go.dev/github.com/dtm-labs/rockscache) + +English | [简体中文](https://github.com/dtm-labs/rockscache/blob/main/helper/README-cn.md) + +# RocksCache +The first Redis cache library to ensure eventual consistency and strong consistency with DB. + +## Features +- Eventual Consistency: ensures eventual consistency of cache even in extreme cases +- Strong consistency: provides strong consistent access to applications +- Anti-breakdown: a better solution for cache breakdown +- Anti-penetration +- Anti-avalanche +- Batch Query + +## Usage +This cache repository uses the most common `update DB and then delete cache` cache management policy + +### Read cache +``` Go +import "github.com/dtm-labs/rockscache" + +// new a client for rockscache using the default options +rc := rockscache.NewClient(redisClient, NewDefaultOptions()) + +// use Fetch to fetch data +// 1. the first parameter is the key of the data +// 2. the second parameter is the data expiration time +// 3. the third parameter is the data fetch function which is called when the cache does not exist +v, err := rc.Fetch("key1", 300 * time.Second, func()(string, error) { + // fetch data from database or other sources + return "value1", nil +}) +``` + +### Delete the cache +``` Go +rc.TagAsDeleted(key) +``` + +## Batch usage + +### Batch read cache +``` Go +import "github.com/dtm-labs/rockscache" + +// new a client for rockscache using the default options +rc := rockscache.NewClient(redisClient, NewDefaultOptions()) + +// use FetchBatch to fetch data +// 1. the first parameter is the keys list of the data +// 2. the second parameter is the data expiration time +// 3. the third parameter is the batch data fetch function which is called when the cache does not exist +// the parameter of the batch data fetch function is the index list of those keys +// missing in cache, which can be used to form a batch query for missing data. +// the return value of the batch data fetch function is a map, with key of the +// index and value of the corresponding data in form of string +v, err := rc.FetchBatch([]string{"key1", "key2", "key3"}, 300, func(idxs []int) (map[int]string, error) { + // fetch data from database or other sources + values := make(map[int]string) + for _, i := range idxs { + values[i] = fmt.Sprintf("value%d", i) + } + return values, nil +}) +``` + +### Batch delete cache +``` Go +rc.TagAsDeletedBatch(keys) +``` + +## Eventual consistency +With the introduction of caching, consistency problems in a distributed system show up, as the data is stored in two places at the same time: the database and Redis. For background on this consistency problem, and an introduction to popular Redis caching solutions, see. +- [https://yunpengn.github.io/blog/2019/05/04/consistent-redis-sql/](https://yunpengn.github.io/blog/2019/05/04/consistent-redis-sql/) + +But all the caching solutions we've seen so far, without introducing versioning at the application level, fail to address the following data inconsistency scenario. + +cache-version-problem + +Even if you use lock to do the updating, there are still corner cases that can cause inconsistency. + +redis cache inconsistency + +### Solution +This project brings you a brand new solution that guarantee data consistency between the cache and the database, without introducing version. This solution is the first of its kind and has been patented and is now open sourced for everyone to use. + +When the developer calls `Fetch` when reading the data, and makes sure to call `TagAsDeleted` after updating the database, then the cache can guarentee the eventual consistency. When step 5 in the diagram above is writing to v1, the write in this solution will eventually be ignored. +- See [Atomicity of DB and cache operations](https://en.dtm.pub/app/cache.html#atomic) for how to ensure that TagAsDeleted is called after updating the database. +- See [Cache consistency](https://en.dtm.pub/app/cache.html) for why data writes are ignored when step 5 is writing v1 to cache. + +For a full runnable example, see [dtm-cases/cache](https://github.com/dtm-labs/dtm-cases/tree/main/cache) + +## Strongly consistent access +If your application needs to use caching and requires strong consistency rather than eventual consistency, then this can be supported by turning on the option `StrongConsisteny`, with the access method remaining the same +``` Go +rc.Options.StrongConsisteny = true +``` + +Refer to [cache consistency](https://en.dtm.pub/app/cache.html) for detailed principles and [dtm-cases/cache](https://github.com/dtm-labs/dtm-cases/tree/main/cache) for examples + +## Downgrading and strong consistency +The library supports downgrading. The downgrade switch is divided into +- `DisableCacheRead`: turns off cache reads, default `false`; if on, then Fetch does not read from the cache, but calls fn directly to fetch the data +- `DisableCacheDelete`: disables cache delete, default false; if on, then TagAsDeleted does nothing and returns directly + +When Redis has a problem and needs to be downgraded, you can control this with these two switches. If you need to maintain strong consistent access even during a downgrade, rockscache also supports + +Refer to [cache-consistency](https://en.dtm.pub/app/cache.html) for detailed principles and [dtm-cases/cache](https://github.com/dtm-labs/dtm-cases/tree/main/cache) for examples + +## Anti-Breakdown +The use of cache through this library comes with an anti-breakdown feature. On the one hand `Fetch` will use `singleflight` within the process to avoid multiple requests being sent to Redis within a process, and on the other hand distributed locks will be used in the Redis layer to avoid multiple requests being sent to the DB from multiple processes at the same time, ensuring that only one data query request ends up at the DB. + +The project's anti-breakdown provides a faster response time when hot cached data is deleted. If a hot cache data takes 3s to compute, a normal anti-breakdown solution would cause all requests for this hot data to wait 3s for this time, whereas this project's solution returns it immediately. + +## Anti-Penetration +The use of caching through this library comes with anti-penetration features. When `fn` in `Fetch` returns an empty string, this is considered an empty result and the expiry time is set to `EmptyExpire` in the rockscache option. + +`EmptyExpire` defaults to 60s, if set to 0 then anti-penetration is turned off and no empty results are saved + +## Anti-Avalanche +The cache is used with this library and comes with an anti-avalanche. `RandomExpireAdjustment` in rockscache defaults to 0.1, if set to an expiry time of 600 then the expiry time will be set to a random number in the middle of `540s - 600s` to avoid data expiring at the same time + +## Contact us + +## Chat Group + +Join the chat via [https://discord.gg/dV9jS5Rb33](https://discord.gg/dV9jS5Rb33). + +## Give a star! ⭐ + +If you think this project is interesting, or helpful to you, please give a star! diff --git a/pkg/cache/rockscache/batch.go b/pkg/cache/rockscache/batch.go new file mode 100644 index 0000000..772f022 --- /dev/null +++ b/pkg/cache/rockscache/batch.go @@ -0,0 +1,409 @@ +package rockscache + +import ( + "context" + "errors" + "fmt" + "math/rand" + "runtime/debug" + "sync" + "time" + + "github.com/go-redis/redis/v8" + "github.com/lithammer/shortuuid" +) + +var ( + errNeedFetch = errors.New("need fetch") + errNeedAsyncFetch = errors.New("need async fetch") +) + +func (c *Client[T]) luaGetBatch(ctx context.Context, keys []string, owner string) ([]interface{}, error) { + res, err := callLua(ctx, c.rdb, `-- luaGetBatch + local rets = {} + for i, key in ipairs(KEYS) + do + local v = redis.call('HGET', key, 'value') + local lu = redis.call('HGET', key, 'lockUntil') + if lu ~= false and tonumber(lu) < tonumber(ARGV[1]) or lu == false and v == false then + redis.call('HSET', key, 'lockUntil', ARGV[2]) + redis.call('HSET', key, 'lockOwner', ARGV[3]) + table.insert(rets, { v, 'LOCKED' }) + else + table.insert(rets, {v, lu}) + end + end + return rets + `, keys, []interface{}{now(), now() + int64(c.Options.LockExpire/time.Second), owner}) + debugf("luaGetBatch return: %v, %v", res, err) + if err != nil { + return nil, err + } + return res.([]interface{}), nil +} + +func (c *Client[T]) luaSetBatch(ctx context.Context, keys []string, values []string, expires []int, owner string) error { + var vals = make([]interface{}, 0, 2+len(values)) + vals = append(vals, owner) + for _, v := range values { + vals = append(vals, v) + } + for _, ex := range expires { + vals = append(vals, ex) + } + _, err := callLua(ctx, c.rdb, `-- luaSetBatch + local n = #KEYS + for i, key in ipairs(KEYS) + do + local o = redis.call('HGET', key, 'lockOwner') + if o ~= ARGV[1] then + return + end + redis.call('HSET', key, 'value', ARGV[i+1]) + redis.call('HDEL', key, 'lockUntil') + redis.call('HDEL', key, 'lockOwner') + redis.call('EXPIRE', key, ARGV[i+1+n]) + end + `, keys, vals) + return err +} + +func (c *Client[T]) fetchBatch(ctx context.Context, keys []string, idxs []int, expire time.Duration, owner string, fn func(idxs []int) (map[int]string, error)) (map[int]string, error) { + defer func() { + if r := recover(); r != nil { + debug.PrintStack() + } + }() + data, err := fn(idxs) + if err != nil { + for _, idx := range idxs { + _ = c.UnlockForUpdate(ctx, keys[idx], owner) + } + return nil, err + } + + if data == nil { + // incase data is nil + data = make(map[int]string) + } + + var batchKeys []string + var batchValues []string + var batchExpires []int + + for _, idx := range idxs { + v := data[idx] + ex := expire - c.Options.Delay - time.Duration(rand.Float64()*c.Options.RandomExpireAdjustment*float64(expire)) + if v == "" { + if c.Options.EmptyExpire == 0 { // if empty expire is 0, then delete the key + _ = c.rdb.Del(ctx, keys[idx]).Err() + if err != nil { + debugf("batch: del failed key=%s err:%s", keys[idx], err.Error()) + } + continue + } + ex = c.Options.EmptyExpire + + data[idx] = v // incase idx not in data + } + batchKeys = append(batchKeys, keys[idx]) + batchValues = append(batchValues, v) + batchExpires = append(batchExpires, int(ex/time.Second)) + } + + err = c.luaSetBatch(ctx, batchKeys, batchValues, batchExpires, owner) + if err != nil { + debugf("batch: luaSetBatch failed keys=%s err:%s", keys, err.Error()) + } + return data, nil +} + +func (c *Client[T]) keysIdx(keys []string) (idxs []int) { + for i := range keys { + idxs = append(idxs, i) + } + return idxs +} + +type pair struct { + idx int + data string + err error +} + +func (c *Client[T]) weakFetchBatch(ctx context.Context, keys []string, expire time.Duration, fn func(idxs []int) (map[int]string, error)) (map[int]string, error) { + debugf("batch: weakFetch keys=%+v", keys) + var result = make(map[int]string) + owner := shortuuid.New() + var toGet, toFetch, toFetchAsync []int + + // read from redis without sleep + rs, err := c.luaGetBatch(ctx, keys, owner) + if err != nil { + return nil, err + } + for i, v := range rs { + r := v.([]interface{}) + + if r[0] == nil { + if r[1] == locked { + toFetch = append(toFetch, i) + } else { + toGet = append(toGet, i) + } + continue + } + + if r[1] == locked { + toFetchAsync = append(toFetchAsync, i) + // fallthrough with old data + } // else new data + + result[i] = r[0].(string) + } + + if len(toFetchAsync) > 0 { + go func(idxs []int) { + debugf("batch weak: async fetch keys=%+v", keys) + _, _ = c.fetchBatch(ctx, keys, idxs, expire, owner, fn) + }(toFetchAsync) + toFetchAsync = toFetchAsync[:0] // reset toFetch + } + + if len(toFetch) > 0 { + // batch fetch + fetched, err := c.fetchBatch(ctx, keys, toFetch, expire, owner, fn) + if err != nil { + return nil, err + } + for _, k := range toFetch { + result[k] = fetched[k] + } + toFetch = toFetch[:0] // reset toFetch + } + + if len(toGet) > 0 { + // read from redis and sleep to wait + var wg sync.WaitGroup + + var ch = make(chan pair, len(toGet)) + for _, idx := range toGet { + wg.Add(1) + go func(i int) { + defer wg.Done() + r, err := c.luaGet(ctx, keys[i], owner) + for err == nil && r[0] == nil && r[1].(string) != locked { + debugf("batch weak: empty result for %s locked by other, so sleep %s", keys[i], c.Options.LockSleep.String()) + time.Sleep(c.Options.LockSleep) + r, err = c.luaGet(ctx, keys[i], owner) + } + if err != nil { + ch <- pair{idx: i, data: "", err: err} + return + } + if r[1] != locked { // normal value + ch <- pair{idx: i, data: r[0].(string), err: nil} + return + } + if r[0] == nil { + ch <- pair{idx: i, data: "", err: errNeedFetch} + return + } + ch <- pair{idx: i, data: "", err: errNeedAsyncFetch} + }(idx) + } + wg.Wait() + close(ch) + + for p := range ch { + if p.err != nil { + switch p.err { + case errNeedFetch: + toFetch = append(toFetch, p.idx) + continue + case errNeedAsyncFetch: + toFetchAsync = append(toFetchAsync, p.idx) + continue + default: + } + return nil, p.err + } + result[p.idx] = p.data + } + } + + if len(toFetchAsync) > 0 { + go func(idxs []int) { + debugf("batch weak: async 2 fetch keys=%+v", keys) + _, _ = c.fetchBatch(ctx, keys, idxs, expire, owner, fn) + }(toFetchAsync) + } + + if len(toFetch) > 0 { + // batch fetch + fetched, err := c.fetchBatch(ctx, keys, toFetch, expire, owner, fn) + if err != nil { + return nil, err + } + for _, k := range toFetch { + result[k] = fetched[k] + } + } + + return result, nil +} + +func (c *Client[T]) strongFetchBatch(ctx context.Context, keys []string, expire time.Duration, fn func(idxs []int) (map[int]string, error)) (map[int]string, error) { + debugf("batch: strongFetch keys=%+v", keys) + var result = make(map[int]string) + owner := shortuuid.New() + var toGet, toFetch []int + + // read from redis without sleep + rs, err := c.luaGetBatch(ctx, keys, owner) + if err != nil { + return nil, err + } + for i, v := range rs { + r := v.([]interface{}) + if r[1] == nil { // normal value + result[i] = r[0].(string) + continue + } + + if r[1] != locked { // locked by other + debugf("batch: locked by other, continue idx=%d", i) + toGet = append(toGet, i) + continue + } + + // locked for fetch + toFetch = append(toFetch, i) + } + + if len(toFetch) > 0 { + // batch fetch + fetched, err := c.fetchBatch(ctx, keys, toFetch, expire, owner, fn) + if err != nil { + return nil, err + } + for _, k := range toFetch { + result[k] = fetched[k] + } + toFetch = toFetch[:0] // reset toFetch + } + + if len(toGet) > 0 { + // read from redis and sleep to wait + var wg sync.WaitGroup + var ch = make(chan pair, len(toGet)) + for _, idx := range toGet { + wg.Add(1) + go func(i int) { + defer wg.Done() + r, err := c.luaGet(ctx, keys[i], owner) + for err == nil && r[1] != nil && r[1] != locked { // locked by other + debugf("batch: locked by other, so sleep %s", c.Options.LockSleep) + time.Sleep(c.Options.LockSleep) + r, err = c.luaGet(ctx, keys[i], owner) + } + if err != nil { + ch <- pair{idx: i, data: "", err: err} + return + } + if r[1] != locked { // normal value + ch <- pair{idx: i, data: r[0].(string), err: nil} + return + } + // locked for update + ch <- pair{idx: i, data: "", err: errNeedFetch} + }(idx) + } + wg.Wait() + close(ch) + for p := range ch { + if p.err != nil { + if p.err == errNeedFetch { + toFetch = append(toFetch, p.idx) + continue + } + return nil, p.err + } + result[p.idx] = p.data + } + } + + if len(toFetch) > 0 { + // batch fetch + fetched, err := c.fetchBatch(ctx, keys, toFetch, expire, owner, fn) + if err != nil { + return nil, err + } + for _, k := range toFetch { + result[k] = fetched[k] + } + } + + return result, nil +} + +// FetchBatch returns a map with values indexed by index of keys list. +// 1. the first parameter is the keys list of the data +// 2. the second parameter is the data expiration time +// 3. the third parameter is the batch data fetch function which is called when the cache does not exist +// the parameter of the batch data fetch function is the index list of those keys +// missing in cache, which can be used to form a batch query for missing data. +// the return value of the batch data fetch function is a map, with key of the +// index and value of the corresponding data in form of string +func (c *Client[T]) FetchBatch(keys []string, expire time.Duration, fn func(idxs []int) (map[int]string, error)) (map[int]string, error) { + return c.FetchBatch2(c.rdb.Context(), keys, expire, fn) +} + +// FetchBatch2 is same with FetchBatch, except that a user defined context.Context can be provided. +func (c *Client[T]) FetchBatch2(ctx context.Context, keys []string, expire time.Duration, fn func(idxs []int) (map[int]string, error)) (map[int]string, error) { + if c.Options.DisableCacheRead { + return fn(c.keysIdx(keys)) + } else if c.Options.StrongConsistency { + return c.strongFetchBatch(ctx, keys, expire, fn) + } + return c.weakFetchBatch(ctx, keys, expire, fn) +} + +// TagAsDeletedBatch a key list, the keys in list will expire after delay time. +func (c *Client[T]) TagAsDeletedBatch(keys []string) error { + return c.TagAsDeletedBatch2(c.rdb.Context(), keys) +} + +// TagAsDeletedBatch2 a key list, the keys in list will expire after delay time. +func (c *Client[T]) TagAsDeletedBatch2(ctx context.Context, keys []string) error { + if c.Options.DisableCacheDelete { + return nil + } + debugf("batch deleting: keys=%v", keys) + luaFn := func(con redisConn) error { + _, err := callLua(ctx, con, ` -- luaDeleteBatch + for i, key in ipairs(KEYS) do + redis.call('HSET', key, 'lockUntil', 0) + redis.call('HDEL', key, 'lockOwner') + redis.call('EXPIRE', key, ARGV[1]) + end + `, keys, []interface{}{int64(c.Options.Delay / time.Second)}) + return err + } + if c.Options.WaitReplicas > 0 { + err := luaFn(c.rdb) + cmd := redis.NewCmd(ctx, "WAIT", c.Options.WaitReplicas, c.Options.WaitReplicasTimeout) + if err == nil { + err = c.rdb.Process(ctx, cmd) + } + var replicas int + if err == nil { + replicas, err = cmd.Int() + } + if err == nil && replicas < c.Options.WaitReplicas { + err = fmt.Errorf("wait replicas %d failed. result replicas: %d", c.Options.WaitReplicas, replicas) + } + return err + } + return luaFn(c.rdb) +} diff --git a/pkg/cache/rockscache/batch_cover_test.go b/pkg/cache/rockscache/batch_cover_test.go new file mode 100644 index 0000000..b1c2fa6 --- /dev/null +++ b/pkg/cache/rockscache/batch_cover_test.go @@ -0,0 +1,125 @@ +package rockscache + +import ( + "context" + "errors" + "fmt" + "math/rand" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +var ( + n = int(rand.Int31n(20) + 10) +) + +func TestDisableForBatch(t *testing.T) { + idxs := genIdxs(n) + keys := genKeys(idxs) + getFn := func(idxs []int) (map[int]string, error) { + return nil, nil + } + + rc := NewClient(nil, NewDefaultOptions()) + rc.Options.DisableCacheDelete = true + rc.Options.DisableCacheRead = true + + _, err := rc.FetchBatch2(context.Background(), keys, 60, getFn) + assert.Nil(t, err) + err = rc.TagAsDeleted2(context.Background(), keys[0]) + assert.Nil(t, err) +} + +func TestErrorFetchForBatch(t *testing.T) { + idxs := genIdxs(n) + keys := genKeys(idxs) + fetchError := errors.New("fetch error") + fn := func(idxs []int) (map[int]string, error) { + return nil, fetchError + } + clearCache() + rc := NewClient(rdb, NewDefaultOptions()) + _, err := rc.FetchBatch(keys, 60, fn) + assert.ErrorIs(t, err, fetchError) + + rc.Options.StrongConsistency = true + _, err = rc.FetchBatch(keys, 60, fn) + assert.ErrorIs(t, err, fetchError) +} + +func TestEmptyExpireForBatch(t *testing.T) { + testEmptyExpireForBatch(t, 0) + testEmptyExpireForBatch(t, 10*time.Second) +} + +func testEmptyExpireForBatch(t *testing.T, expire time.Duration) { + idxs := genIdxs(n) + keys := genKeys(idxs) + fn := func(idxs []int) (map[int]string, error) { + return nil, nil + } + fetchError := errors.New("fetch error") + errFn := func(idxs []int) (map[int]string, error) { + return nil, fetchError + } + + clearCache() + rc := NewClient(rdb, NewDefaultOptions()) + rc.Options.EmptyExpire = expire + + _, err := rc.FetchBatch(keys, 60, fn) + assert.Nil(t, err) + _, err = rc.FetchBatch(keys, 60, errFn) + if expire == 0 { + assert.ErrorIs(t, err, fetchError) + } else { + assert.Nil(t, err) + } + + clearCache() + rc.Options.StrongConsistency = true + _, err = rc.FetchBatch(keys, 60, fn) + assert.Nil(t, err) + _, err = rc.FetchBatch(keys, 60, errFn) + if expire == 0 { + assert.ErrorIs(t, err, fetchError) + } else { + assert.Nil(t, err) + } +} + +func TestPanicFetchForBatch(t *testing.T) { + idxs := genIdxs(n) + keys := genKeys(idxs) + fn := func(idxs []int) (map[int]string, error) { + return nil, nil + } + fetchError := errors.New("fetch error") + errFn := func(idxs []int) (map[int]string, error) { + panic(fetchError) + } + clearCache() + rc := NewClient(rdb, NewDefaultOptions()) + + _, err := rc.FetchBatch(keys, 60, fn) + assert.Nil(t, err) + rc.TagAsDeleted("key1") + _, err = rc.FetchBatch(keys, 60, errFn) + assert.Nil(t, err) + time.Sleep(20 * time.Millisecond) +} + +func TestTagAsDeletedBatchWait(t *testing.T) { + clearCache() + rc := NewClient(rdb, NewDefaultOptions()) + rc.Options.WaitReplicas = 1 + rc.Options.WaitReplicasTimeout = 10 + err := rc.TagAsDeletedBatch([]string{"key1", "key2"}) + if getCluster() != nil { + assert.Nil(t, err) + } else { + assert.Error(t, err, fmt.Errorf("wait replicas 1 failed. result replicas: 0")) + } +} diff --git a/pkg/cache/rockscache/batch_test.go b/pkg/cache/rockscache/batch_test.go new file mode 100644 index 0000000..86cf6c9 --- /dev/null +++ b/pkg/cache/rockscache/batch_test.go @@ -0,0 +1,254 @@ +package rockscache + +import ( + "errors" + "math/rand" + "strconv" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func genBatchDataFunc(values map[int]string, sleepMilli int) func(idxs []int) (map[int]string, error) { + return func(idxs []int) (map[int]string, error) { + debugf("batch fetching: %v", idxs) + time.Sleep(time.Duration(sleepMilli) * time.Millisecond) + return values, nil + } +} + +func genIdxs(to int) (idxs []int) { + for i := 0; i < to; i++ { + idxs = append(idxs, i) + } + return +} + +func genKeys(idxs []int) (keys []string) { + for _, i := range idxs { + suffix := strconv.Itoa(i) + k := "key" + suffix + keys = append(keys, k) + } + return +} + +func genValues(n int, prefix string) map[int]string { + values := make(map[int]string) + for i := 0; i < n; i++ { + v := prefix + strconv.Itoa(i) + values[i] = v + } + return values +} + +func TestWeakFetchBatch(t *testing.T) { + clearCache() + rc := NewClient(rdb, NewDefaultOptions()) + began := time.Now() + n := int(rand.Int31n(20) + 10) + idxs := genIdxs(n) + keys, values1, values2 := genKeys(idxs), genValues(n, "value_"), genValues(n, "eulav_") + values3 := genValues(n, "vvvv_") + go func() { + dc2 := NewClient(rdb, NewDefaultOptions()) + v, err := dc2.FetchBatch(keys, 60*time.Second, genBatchDataFunc(values1, 200)) + assert.Nil(t, err) + assert.Equal(t, values1, v) + }() + time.Sleep(20 * time.Millisecond) + + v, err := rc.FetchBatch(keys, 60*time.Second, genBatchDataFunc(values2, 200)) + assert.Nil(t, err) + assert.Equal(t, values1, v) + assert.True(t, time.Since(began) > time.Duration(150)*time.Millisecond) + + err = rc.TagAsDeletedBatch(keys) + assert.Nil(t, err) + + began = time.Now() + v, err = rc.FetchBatch(keys, 60*time.Second, genBatchDataFunc(values3, 200)) + assert.Nil(t, err) + assert.Equal(t, values1, v) + assert.True(t, time.Since(began) < time.Duration(200)*time.Millisecond) + + time.Sleep(300 * time.Millisecond) + v, err = rc.FetchBatch(keys, 60*time.Second, genBatchDataFunc(values3, 200)) + assert.Nil(t, err) + assert.Equal(t, values3, v) +} + +func TestWeakFetchBatchOverlap(t *testing.T) { + clearCache() + rc := NewClient(rdb, NewDefaultOptions()) + began := time.Now() + n := 100 + idxs := genIdxs(n) + keys := genKeys(idxs) + keys1, values1 := keys[:60], genValues(60, "value_") + keys2, values2 := keys[40:], genValues(60, "eulav_") + + go func() { + dc2 := NewClient(rdb, NewDefaultOptions()) + v, err := dc2.FetchBatch(keys1, 60*time.Second, genBatchDataFunc(values1, 200)) + assert.Nil(t, err) + assert.Equal(t, values1, v) + }() + time.Sleep(20 * time.Millisecond) + + v, err := rc.FetchBatch(keys2, 60*time.Second, genBatchDataFunc(values2, 200)) + assert.Nil(t, err) + assert.True(t, time.Since(began) > time.Duration(150)*time.Millisecond) + for i := 40; i < 60; i++ { + assert.Equal(t, keys2[i-40], keys1[i]) + assert.Equal(t, values1[i], v[i-40]) + } + for i := 60; i < n; i++ { + assert.Equal(t, values2[i-40], v[i-40]) + } + + rc.TagAsDeletedBatch(keys[40:60]) + began = time.Now() + _, err = rc.FetchBatch(keys2, 60*time.Second, genBatchDataFunc(values2, 200)) + assert.Nil(t, err) + assert.True(t, time.Since(began) < time.Duration(200)*time.Millisecond) + for i := 40; i < 60; i++ { + assert.Equal(t, keys2[i-40], keys1[i]) + assert.Equal(t, values1[i], v[i-40]) + } + for i := 60; i < n; i++ { + assert.Equal(t, values2[i-40], v[i-40]) + } + + time.Sleep(300 * time.Millisecond) + v, err = rc.FetchBatch(keys2, 20*time.Second, genBatchDataFunc(values2, 200)) + assert.Nil(t, err) + assert.Equal(t, values2, v) +} + +func TestStrongFetchBatch(t *testing.T) { + clearCache() + rc := NewClient(rdb, NewDefaultOptions()) + rc.Options.StrongConsistency = true + began := time.Now() + n := int(rand.Int31n(20) + 10) + idxs := genIdxs(n) + keys, values1, values2 := genKeys(idxs), genValues(n, "value_"), genValues(n, "eulav_") + values3, values4 := genValues(n, "vvvv_"), genValues(n, "uuuu_") + go func() { + dc2 := NewClient(rdb, NewDefaultOptions()) + v, err := dc2.FetchBatch(keys, 60*time.Second, genBatchDataFunc(values1, 200)) + assert.Nil(t, err) + assert.Equal(t, values1, v) + }() + time.Sleep(20 * time.Millisecond) + + v, err := rc.FetchBatch(keys, 60*time.Second, genBatchDataFunc(values2, 200)) + assert.Nil(t, err) + assert.Equal(t, values1, v) + assert.True(t, time.Since(began) > time.Duration(150)*time.Millisecond) + + err = rc.TagAsDeletedBatch(keys) + assert.Nil(t, err) + + began = time.Now() + v, err = rc.FetchBatch(keys, 60*time.Second, genBatchDataFunc(values3, 200)) + assert.Nil(t, err) + assert.Equal(t, values3, v) + assert.True(t, time.Since(began) > time.Duration(150)*time.Millisecond) + + v, err = rc.FetchBatch(keys, 60*time.Second, genBatchDataFunc(values4, 200)) + assert.Nil(t, err) + assert.Equal(t, values3, v) +} + +func TestStrongFetchBatchOverlap(t *testing.T) { + clearCache() + rc := NewClient(rdb, NewDefaultOptions()) + rc.Options.StrongConsistency = true + began := time.Now() + n := 100 + idxs := genIdxs(n) + keys := genKeys(idxs) + keys1, values1 := keys[:60], genValues(60, "value_") + keys2, values2 := keys[40:], genValues(60, "eulav_") + + go func() { + dc2 := NewClient(rdb, NewDefaultOptions()) + v, err := dc2.FetchBatch(keys1, 20*time.Second, genBatchDataFunc(values1, 200)) + assert.Nil(t, err) + assert.Equal(t, values1, v) + }() + time.Sleep(20 * time.Millisecond) + + v, err := rc.FetchBatch(keys2, 20*time.Second, genBatchDataFunc(values2, 200)) + assert.Nil(t, err) + assert.True(t, time.Since(began) > time.Duration(150)*time.Millisecond) + for i := 40; i < 60; i++ { + assert.Equal(t, keys2[i-40], keys1[i]) + assert.Equal(t, values1[i], v[i-40]) + } + for i := 60; i < n; i++ { + assert.Equal(t, values2[i-40], v[i-40]) + } +} + +func TestStrongFetchBatchOverlapExpire(t *testing.T) { + clearCache() + opts := NewDefaultOptions() + opts.Delay = 10 * time.Millisecond + opts.StrongConsistency = true + + rc := NewClient(rdb, opts) + began := time.Now() + n := 100 + idxs := genIdxs(n) + keys := genKeys(idxs) + keys1, values1 := keys[:60], genValues(60, "value_") + keys2, values2 := keys[40:], genValues(60, "eulav_") + + v, err := rc.FetchBatch(keys1, 2*time.Second, genBatchDataFunc(values1, 200)) + assert.Nil(t, err) + assert.Equal(t, values1, v) + + v, err = rc.FetchBatch(keys2, 2*time.Second, genBatchDataFunc(values2, 200)) + assert.Nil(t, err) + assert.True(t, time.Since(began) > time.Duration(150)*time.Millisecond) + for i := 40; i < 60; i++ { + assert.Equal(t, keys2[i-40], keys1[i]) + assert.Equal(t, values1[i], v[i-40]) + } + for i := 60; i < n; i++ { + assert.Equal(t, values2[i-40], v[i-40]) + } + + time.Sleep(time.Second) + v, err = rc.FetchBatch(keys2, 2*time.Second, genBatchDataFunc(values2, 100)) + assert.Nil(t, err) + assert.Nil(t, err) + assert.Equal(t, values2, v) +} + +func TestStrongErrorFetchBatch(t *testing.T) { + rc := NewClient(rdb, NewDefaultOptions()) + rc.Options.StrongConsistency = true + + clearCache() + began := time.Now() + + n := 100 + idxs := genIdxs(n) + keys := genKeys(idxs) + + fetchError := errors.New("fetch error") + getFn := func(idxs []int) (map[int]string, error) { + return nil, fetchError + } + _, err := rc.FetchBatch(keys, 60*time.Second, getFn) + assert.Error(t, err) + fetchError = nil + _, err = rc.FetchBatch(keys, 60*time.Second, getFn) + assert.Nil(t, err) + assert.True(t, time.Since(began) < time.Duration(150)*time.Millisecond) +} diff --git a/pkg/cache/rockscache/client.go b/pkg/cache/rockscache/client.go new file mode 100644 index 0000000..fb1a17b --- /dev/null +++ b/pkg/cache/rockscache/client.go @@ -0,0 +1,318 @@ +package rockscache + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "math" + "math/rand" + "time" + + "github.com/go-redis/redis/v8" + "github.com/lithammer/shortuuid" + "golang.org/x/sync/singleflight" +) + +const ( + locked = "LOCKED" + ErrEmptyPlaceholder = "\u0001" +) + +var ErrEmpty = errors.New("empty result") + +// Options represents the options for rockscache client +type Options struct { + // Delay is the delay delete time for keys that are tag deleted. default is 10s + Delay time.Duration + // EmptyExpire is the expire time for empty result. default is 60s + EmptyExpire time.Duration + // LockExpire is the expire time for the lock which is allocated when updating cache. default is 3s + // should be set to the max of the underling data calculating time. + LockExpire time.Duration + // LockSleep is the sleep interval time if try lock failed. default is 100ms + LockSleep time.Duration + // WaitReplicas is the number of replicas to wait for. default is 0 + // if WaitReplicas is > 0, it will use redis WAIT command to wait for TagAsDeleted synchronized. + WaitReplicas int + // WaitReplicasTimeout is the number of replicas to wait for. default is 3000ms + // if WaitReplicas is > 0, WaitReplicasTimeout is the timeout for WAIT command. + WaitReplicasTimeout time.Duration + // RandomExpireAdjustment is the random adjustment for the expire time. default 0.1 + // if the expire time is set to 600s, and this value is set to 0.1, then the actual expire time will be 540s - 600s + // solve the problem of cache avalanche. + RandomExpireAdjustment float64 + // CacheReadDisabled is the flag to disable read cache. default is false + // when redis is down, set this flat to downgrade. + DisableCacheRead bool + // CacheDeleteDisabled is the flag to disable delete cache. default is false + // when redis is down, set this flat to downgrade. + DisableCacheDelete bool + // StrongConsistency is the flag to enable strong consistency. default is false + // if enabled, the Fetch result will be consistent with the db result, but performance is bad. + StrongConsistency bool + // Prefix flag to redis key namespace + Prefix string +} + +// NewDefaultOptions return default options +func NewDefaultOptions() Options { + return Options{ + Delay: 10 * time.Second, + EmptyExpire: 60 * time.Second, + LockExpire: 3 * time.Second, + LockSleep: 100 * time.Millisecond, + RandomExpireAdjustment: 0.1, + WaitReplicasTimeout: 3000 * time.Millisecond, + } +} + +type Serializable interface { + []byte | string +} + +// Client delay client +type Client[T any] struct { + rdb redis.UniversalClient + Options Options + group singleflight.Group +} + +// NewClient return a new rockscache client +// for each key, rockscache client store a hash set, +// the hash set contains the following fields: +// value: the value of the key +// lockUntil: the time when the lock is released. +// lockOwner: the owner of the lock. +// if a thread query the cache for data, and no cache exists, it will lock the key before querying data in DB +func NewClient[T any](rdb redis.UniversalClient, options Options) *Client[T] { + if options.Delay == 0 || options.LockExpire == 0 { + panic("cache options error: Delay and LockExpire should not be 0, you should call NewDefaultOptions() to get default options") + } + return &Client[T]{rdb: rdb, Options: options} +} + +// TagAsDeleted a key, the key will expire after delay time. +func (c *Client[T]) TagAsDeleted(key string) error { + return c.TagAsDeleted2(c.rdb.Context(), key) +} + +// TagAsDeleted2 a key, the key will expire after delay time. +func (c *Client[T]) TagAsDeleted2(ctx context.Context, key string) error { + if c.Options.DisableCacheDelete { + return nil + } + debugf("deleting: key=%s", key) + luaFn := func(con redisConn) error { + _, err := callLua(ctx, con, ` -- delete + redis.call('HSET', KEYS[1], 'lockUntil', 0) + redis.call('HDEL', KEYS[1], 'lockOwner') + redis.call('EXPIRE', KEYS[1], ARGV[1]) + `, []string{key}, []interface{}{int64(c.Options.Delay / time.Second)}) + return err + } + if c.Options.WaitReplicas > 0 { + err := luaFn(c.rdb) + cmd := redis.NewCmd(ctx, "WAIT", c.Options.WaitReplicas, c.Options.WaitReplicasTimeout) + if err == nil { + err = c.rdb.Process(ctx, cmd) + } + var replicas int + if err == nil { + replicas, err = cmd.Int() + } + if err == nil && replicas < c.Options.WaitReplicas { + err = fmt.Errorf("wait replicas %d failed. result replicas: %d", c.Options.WaitReplicas, replicas) + } + return err + } + return luaFn(c.rdb) +} + +// Fetch returns the value store in cache indexed by the key. +// If the key doest not exists, call fn to get result, store it in cache, then return. +func (c *Client[T]) Fetch(key string, expire time.Duration, fn func() (T, error)) (T, error) { + return c.Fetch2(c.rdb.Context(), key, expire, fn) +} + +// Fetch2 returns the value store in cache indexed by the key. +// If the key doest not exists, call fn to get result, store it in cache, then return. +func (c *Client[T]) Fetch2(ctx context.Context, key string, expire time.Duration, fn func() (T, error)) (t T, err error) { + key = c.Options.Prefix + key + ex := expire - c.Options.Delay - time.Duration(rand.Float64()*c.Options.RandomExpireAdjustment*float64(expire)) + v, err, _ := c.group.Do(key, func() (interface{}, error) { + if c.Options.DisableCacheRead { + return fn() + } else if c.Options.StrongConsistency { + return c.strongFetch(ctx, key, ex, fn) + } + return c.weakFetch(ctx, key, ex, fn) + }) + if err != nil { + return + } + return v.(T), nil +} + +func (c *Client[T]) luaGet(ctx context.Context, key string, owner string) ([]interface{}, error) { + res, err := callLua(ctx, c.rdb, ` -- luaGet + local v = redis.call('HGET', KEYS[1], 'value') + local lu = redis.call('HGET', KEYS[1], 'lockUntil') + if lu ~= false and tonumber(lu) < tonumber(ARGV[1]) or lu == false and v == false then + redis.call('HSET', KEYS[1], 'lockUntil', ARGV[2]) + redis.call('HSET', KEYS[1], 'lockOwner', ARGV[3]) + return { v, 'LOCKED' } + end + return {v, lu} + `, []string{key}, []interface{}{now(), now() + int64(c.Options.LockExpire/time.Second), owner}) + debugf("luaGet return: %v, %v", res, err) + if err != nil { + return nil, err + } + return res.([]interface{}), nil +} + +func (c *Client[T]) luaSet(ctx context.Context, key string, value string, expire int, owner string) error { + _, err := callLua(ctx, c.rdb, `-- luaSet + local o = redis.call('HGET', KEYS[1], 'lockOwner') + if o ~= ARGV[2] then + return + end + redis.call('HSET', KEYS[1], 'value', ARGV[1]) + redis.call('HDEL', KEYS[1], 'lockUntil') + redis.call('HDEL', KEYS[1], 'lockOwner') + redis.call('EXPIRE', KEYS[1], ARGV[3]) + `, []string{key}, []interface{}{value, owner, expire}) + return err +} + +func (c *Client[T]) fetchNew(ctx context.Context, key string, expire time.Duration, owner string, fn func() (T, error)) (t T, err error) { + t, err = fn() + isEmpty := err == ErrEmpty + if err != nil && err != ErrEmpty { + _ = c.UnlockForUpdate(ctx, key, owner) + return + } + var data = "" + if err == ErrEmpty { + data = ErrEmptyPlaceholder + if c.Options.EmptyExpire == 0 { // if empty expire is 0, then delete the key + err = c.rdb.Del(ctx, key).Err() + return + } + expire = c.Options.EmptyExpire + } else { + bytes, e := json.Marshal(t) + if e != nil { + _ = c.UnlockForUpdate(ctx, key, owner) + err = e + return + } + data = string(bytes) + } + err = c.luaSet(ctx, key, data, int(expire/time.Second), owner) + if isEmpty { + err = ErrEmpty + return + } + return +} + +func (c *Client[T]) weakFetch(ctx context.Context, key string, expire time.Duration, fn func() (T, error)) (t T, err error) { + debugf("weakFetch: key=%s", key) + owner := shortuuid.New() + r, err := c.luaGet(ctx, key, owner) + for err == nil && r[0] == nil && r[1].(string) != locked { + debugf("empty result for %s locked by other, so sleep %s", key, c.Options.LockSleep.String()) + time.Sleep(c.Options.LockSleep) + r, err = c.luaGet(ctx, key, owner) + } + if err != nil { + return t, err + } + if r[0] == ErrEmptyPlaceholder { + err = ErrEmpty + return + } + if r[1] != locked { // normal value + err = json.Unmarshal([]byte(r[0].(string)), &t) + return + } + if r[0] == nil { + return c.fetchNew(ctx, key, expire, owner, fn) + } + //go withRecover(func() { + // _, _ = c.fetchNew(ctx, key, expire, owner, fn) + //}) + err = json.Unmarshal([]byte(r[0].(string)), &t) + return +} + +func (c *Client[T]) strongFetch(ctx context.Context, key string, expire time.Duration, fn func() (T, error)) (t T, err error) { + debugf("strongFetch: key=%s", key) + owner := shortuuid.New() + r, err := c.luaGet(ctx, key, owner) + for err == nil && r[1] != nil && r[1] != locked { // locked by other + debugf("locked by other, so sleep %s", c.Options.LockSleep) + time.Sleep(c.Options.LockSleep) + r, err = c.luaGet(ctx, key, owner) + } + if err != nil { + return + } + if r[0] == ErrEmptyPlaceholder { + err = ErrEmpty + return + } + if r[1] != locked { // normal value + err = json.Unmarshal([]byte(r[0].(string)), &t) + return + } + return c.fetchNew(ctx, key, expire, owner, fn) +} + +// RawGet returns the value store in cache indexed by the key, no matter if the key locked or not +func (c *Client[T]) RawGet(ctx context.Context, key string) (string, error) { + return c.rdb.HGet(ctx, key, "value").Result() +} + +// RawSet sets the value store in cache indexed by the key, no matter if the key locked or not +func (c *Client[T]) RawSet(ctx context.Context, key string, value string, expire time.Duration) error { + err := c.rdb.HSet(ctx, key, "value", value).Err() + if err == nil { + err = c.rdb.Expire(ctx, key, expire).Err() + } + return err +} + +// LockForUpdate locks the key, used in very strict strong consistency mode +func (c *Client[T]) LockForUpdate(ctx context.Context, key string, owner string) error { + lockUntil := math.Pow10(10) + res, err := callLua(ctx, c.rdb, ` -- luaLock + local lu = redis.call('HGET', KEYS[1], 'lockUntil') + local lo = redis.call('HGET', KEYS[1], 'lockOwner') + if lu == false or tonumber(lu) < tonumber(ARGV[2]) or lo == ARGV[1] then + redis.call('HSET', KEYS[1], 'lockUntil', ARGV[2]) + redis.call('HSET', KEYS[1], 'lockOwner', ARGV[1]) + return 'LOCKED' + end + return lo + `, []string{key}, []interface{}{owner, lockUntil}) + if err == nil && res != "LOCKED" { + return fmt.Errorf("%s has been locked by %s", key, res) + } + return err +} + +// UnlockForUpdate unlocks the key, used in very strict strong consistency mode +func (c *Client[T]) UnlockForUpdate(ctx context.Context, key string, owner string) error { + _, err := callLua(ctx, c.rdb, ` -- luaUnlock + local lo = redis.call('HGET', KEYS[1], 'lockOwner') + if lo == ARGV[1] then + redis.call('HSET', KEYS[1], 'lockUntil', 0) + redis.call('HDEL', KEYS[1], 'lockOwner') + redis.call('EXPIRE', KEYS[1], ARGV[2]) + end + `, []string{key}, []interface{}{owner, c.Options.LockExpire / time.Second}) + return err +} diff --git a/pkg/cache/rockscache/client_cover_test.go b/pkg/cache/rockscache/client_cover_test.go new file mode 100644 index 0000000..880a612 --- /dev/null +++ b/pkg/cache/rockscache/client_cover_test.go @@ -0,0 +1,100 @@ +package rockscache + +import ( + "context" + "errors" + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestBadOptions(t *testing.T) { + assert.Panics(t, func() { + NewClient(nil, Options{}) + }) +} + +func TestDisable(t *testing.T) { + rc := NewClient(nil, NewDefaultOptions()) + rc.Options.DisableCacheDelete = true + rc.Options.DisableCacheRead = true + fn := func() (string, error) { return "", nil } + _, err := rc.Fetch2(context.Background(), "key", 60, fn) + assert.Nil(t, err) + err = rc.TagAsDeleted2(context.Background(), "key") + assert.Nil(t, err) +} + +func TestEmptyExpire(t *testing.T) { + testEmptyExpire(t, 0) + testEmptyExpire(t, 10*time.Second) +} + +func testEmptyExpire(t *testing.T, expire time.Duration) { + clearCache() + rc := NewClient(rdb, NewDefaultOptions()) + rc.Options.EmptyExpire = expire + fn := func() (string, error) { return "", nil } + fetchError := errors.New("fetch error") + errFn := func() (string, error) { + return "", fetchError + } + _, err := rc.Fetch("key1", 600, fn) + assert.Nil(t, err) + _, err = rc.Fetch("key1", 600, errFn) + if expire == 0 { + assert.ErrorIs(t, err, fetchError) + } else { + assert.Nil(t, err) + } + + rc.Options.StrongConsistency = true + _, err = rc.Fetch("key2", 600, fn) + assert.Nil(t, err) + _, err = rc.Fetch("key2", 600, errFn) + if expire == 0 { + assert.ErrorIs(t, err, fetchError) + } else { + assert.Nil(t, err) + } +} + +func TestErrorFetch(t *testing.T) { + fn := func() (string, error) { return "", fmt.Errorf("error") } + clearCache() + rc := NewClient(rdb, NewDefaultOptions()) + _, err := rc.Fetch("key1", 60, fn) + assert.Equal(t, fmt.Errorf("error"), err) + + rc.Options.StrongConsistency = true + _, err = rc.Fetch("key2", 60, fn) + assert.Equal(t, fmt.Errorf("error"), err) +} + +func TestPanicFetch(t *testing.T) { + fn := func() (string, error) { return "abc", nil } + pfn := func() (string, error) { panic(fmt.Errorf("error")) } + clearCache() + rc := NewClient(rdb, NewDefaultOptions()) + _, err := rc.Fetch("key1", 60*time.Second, fn) + assert.Nil(t, err) + rc.TagAsDeleted("key1") + _, err = rc.Fetch("key1", 60*time.Second, pfn) + assert.Nil(t, err) + time.Sleep(20 * time.Millisecond) +} + +func TestTagAsDeletedWait(t *testing.T) { + clearCache() + rc := NewClient(rdb, NewDefaultOptions()) + rc.Options.WaitReplicas = 1 + rc.Options.WaitReplicasTimeout = 10 + err := rc.TagAsDeleted("key1") + if getCluster() != nil { + assert.Nil(t, err) + } else { + assert.Error(t, err, fmt.Errorf("wait replicas 1 failed. result replicas: 0")) + } +} diff --git a/pkg/cache/rockscache/client_test.go b/pkg/cache/rockscache/client_test.go new file mode 100644 index 0000000..59f937f --- /dev/null +++ b/pkg/cache/rockscache/client_test.go @@ -0,0 +1,190 @@ +package rockscache + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/go-redis/redis/v8" + "github.com/stretchr/testify/assert" +) + +var rdbKey = "client-test-key" + +var rdb = redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Username: "root", + Password: "", +}) + +// var rdb = redis.NewClusterClient(&redis.ClusterOptions{ +// Addrs: []string{"43.128.5.63:46381", "43.128.5.63:46382", "43.128.5.63:46380", "43.128.5.63:46383", "43.128.5.63:46384", "43.128.5.63:46385"}, +// Username: "", +// Password: "", +// }) + +type iRedisCluster interface { + ForEachMaster(context.Context, func(context.Context, *redis.Client) error) error +} + +func getCluster() iRedisCluster { + var rr interface{} = rdb + v, _ := rr.(iRedisCluster) + return v +} + +func clearCache() { + var err error + if clu := getCluster(); clu != nil { + err = clu.ForEachMaster(rdb.Context(), func(ctx context.Context, master *redis.Client) error { + return master.FlushAll(ctx).Err() + }) + } else { + err = rdb.FlushDB(rdb.Context()).Err() + } + + if err != nil { + panic(err) + } +} + +func genDataFunc(value string, sleepMilli int) func() (string, error) { + return func() (string, error) { + time.Sleep(time.Duration(sleepMilli) * time.Millisecond) + return value, nil + } +} + +func init() { + SetVerbose(true) +} +func TestWeakFetch(t *testing.T) { + rc := NewClient[string](rdb, NewDefaultOptions()) + + clearCache() + began := time.Now() + expected := "value1" + go func() { + dc2 := NewClient[string](rdb, NewDefaultOptions()) + v, err := dc2.Fetch(rdbKey, 60*time.Second, genDataFunc(expected, 200)) + assert.Nil(t, err) + assert.Equal(t, expected, v) + }() + time.Sleep(20 * time.Millisecond) + + v, err := rc.Fetch(rdbKey, 60*time.Second, genDataFunc(expected, 201)) + assert.Nil(t, err) + assert.Equal(t, expected, v) + assert.True(t, time.Since(began) > time.Duration(150)*time.Millisecond) + + err = rc.TagAsDeleted(rdbKey) + assert.Nil(t, err) + + nv := "value2" + v, err = rc.Fetch(rdbKey, 60*time.Second, genDataFunc(nv, 200)) + assert.Nil(t, err) + assert.Equal(t, expected, v) + + time.Sleep(300 * time.Millisecond) + v, err = rc.Fetch(rdbKey, 60*time.Second, genDataFunc("ignored", 200)) + assert.Nil(t, err) + assert.Equal(t, nv, v) +} + +func TestStrongFetch(t *testing.T) { + clearCache() + rc := NewClient[string](rdb, NewDefaultOptions()) + rc.Options.StrongConsistency = true + began := time.Now() + expected := "value1" + go func() { + dc2 := NewClient[string](rdb, NewDefaultOptions()) + v, err := dc2.Fetch(rdbKey, 60*time.Second, genDataFunc(expected, 200)) + assert.Nil(t, err) + assert.Equal(t, expected, v) + }() + time.Sleep(20 * time.Millisecond) + + v, err := rc.Fetch(rdbKey, 60*time.Second, genDataFunc(expected, 200)) + assert.Nil(t, err) + assert.Equal(t, expected, v) + assert.True(t, time.Since(began) > time.Duration(150)*time.Millisecond) + + err = rc.TagAsDeleted(rdbKey) + assert.Nil(t, err) + + began = time.Now() + nv := "value2" + v, err = rc.Fetch(rdbKey, 60*time.Second, genDataFunc(nv, 200)) + assert.Nil(t, err) + assert.Equal(t, nv, v) + assert.True(t, time.Since(began) > time.Duration(150)*time.Millisecond) + + v, err = rc.Fetch(rdbKey, 60*time.Second, genDataFunc("ignored", 200)) + assert.Nil(t, err) + assert.Equal(t, nv, v) + +} + +func TestStrongErrorFetch(t *testing.T) { + rc := NewClient[string](rdb, NewDefaultOptions()) + rc.Options.StrongConsistency = true + + clearCache() + began := time.Now() + + fetchError := errors.New("fetch error") + getFn := func() (string, error) { + return "", fetchError + } + _, err := rc.Fetch(rdbKey, 60*time.Second, getFn) + assert.Error(t, err) + fetchError = nil + _, err = rc.Fetch(rdbKey, 60*time.Second, getFn) + assert.Nil(t, err) + assert.True(t, time.Since(began) < time.Duration(150)*time.Millisecond) +} + +func TestWeakErrorFetch(t *testing.T) { + rc := NewClient[string](rdb, NewDefaultOptions()) + + clearCache() + began := time.Now() + + fetchError := errors.New("fetch error") + getFn := func() (string, error) { + return "", fetchError + } + _, err := rc.Fetch(rdbKey, 60*time.Second, getFn) + assert.Error(t, err) + fetchError = nil + _, err = rc.Fetch(rdbKey, 60*time.Second, getFn) + assert.Nil(t, err) + assert.True(t, time.Since(began) < time.Duration(150)*time.Millisecond) +} + +func TestRawGet(t *testing.T) { + rc := NewClient(rdb, NewDefaultOptions()) + _, err := rc.RawGet(rdb.Context(), "not-exists") + assert.Error(t, redis.Nil, err) +} + +func TestRawSet(t *testing.T) { + rc := NewClient(rdb, NewDefaultOptions()) + err := rc.RawSet(rdb.Context(), "eeeee", "value", 60*time.Second) + assert.Nil(t, err) +} + +func TestLock(t *testing.T) { + rc := NewClient(rdb, NewDefaultOptions()) + rc.Options.StrongConsistency = true + owner := "test_owner" + key := "test_lock" + err := rc.LockForUpdate(rdb.Context(), key, owner) + assert.Nil(t, err) + err = rc.LockForUpdate(rdb.Context(), key, "other_owner") + assert.Error(t, err) + err = rc.UnlockForUpdate(rdb.Context(), key, owner) + assert.Nil(t, err) +} diff --git a/pkg/cache/rockscache/helper/README-cn.md b/pkg/cache/rockscache/helper/README-cn.md new file mode 100644 index 0000000..e3ca294 --- /dev/null +++ b/pkg/cache/rockscache/helper/README-cn.md @@ -0,0 +1,127 @@ +![license](https://img.shields.io/github/license/dtm-labs/rockscache) +![Build Status](https://github.com/dtm-labs/rockscache/actions/workflows/tests.yml/badge.svg?branch=main) +[![codecov](https://codecov.io/gh/dtm-labs/rockscache/branch/main/graph/badge.svg?token=UKKEYQLP3F)](https://codecov.io/gh/dtm-labs/rockscache) +[![Go Report Card](https://goreportcard.com/badge/github.com/dtm-labs/rockscache)](https://goreportcard.com/report/github.com/dtm-labs/rockscache) +[![Go Reference](https://pkg.go.dev/badge/github.com/dtm-labs/rockscache.svg)](https://pkg.go.dev/github.com/dtm-labs/rockscache) + +简体中文 | [English](https://github.com/dtm-labs/rockscache/blob/main/helper/README-en.md) + +# RocksCache +首个确保最终一致、强一致的 `Redis` 缓存库。 + +## 特性 +- 最终一致:极端情况也能确保缓存的最终一致 +- 强一致:支持给应用提供强一致的访问 +- 高性能:与常见的缓存方案对比,性能无大的差别 +- 防击穿:给出更好的防击穿方案 +- 防穿透 +- 防雪崩 +- 非常易用:仅提供极简的两个函数,对应用无要求 +- 提供批量查询接口 + +## 使用 +本缓存库采用最常见的`更新DB后,删除缓存`的缓存管理策略 + +### 读取缓存 +``` Go +import "github.com/dtm-labs/rockscache" + +// 使用默认选项生成rockscache的客户端 +rc := rockscache.NewClient(redisClient, NewDefaultOptions()) + +// 使用Fetch获取数据,第一个参数是数据的key,第二个参数为数据过期时间,第三个参数为缓存不存在时,数据获取函数 +v, err := rc.Fetch("key1", 300, func()(string, error) { + // 从数据库或其他渠道获取数据 + return "value1", nil +}) +``` + +### 删除缓存 +``` Go +rc.TagAsDeleted(key) +``` + +## 批量查询接口使用 + +### 批量读取缓存 +``` Go +import "github.com/dtm-labs/rockscache" + +// 使用默认选项生成rockscache的客户端 +rc := rockscache.NewClient(redisClient, NewDefaultOptions()) + +// 使用FetchBatch获取数据,第一个参数是数据的keys列表,第二个参数为数据过期时间,第三个参数为缓存不存在时,数据获取函数 +// 数据获取函数的参数为 keys 列表的下标数组,表示 keys 中没命中缓存的 key 的下标,通过这些下标可以构造批量查询条件;返回值是以下标为 key,字符串为值的 map +v, err := rc.FetchBatch([]string{"key1", "key2", "key3"}, 300, func(idxs []int) (map[int]string, error) { + // 从数据库或其他渠道获取数据 + values := make(map[int]string) + for _, i := range idxs { + values[i] = fmt.Sprintf("value%d", i) + } + return values, nil +}) +``` + +### 批量删除缓存 +``` Go +rc.TagAsDeletedBatch(keys) +``` + +## 最终一致 +引入缓存后,由于数据同时存储在两个地方:数据库和Redis,因此就存在分布式系统中的一致性问题。关于这个一致性问题的背景知识,以及Redis缓存流行方案介绍,可以参见: +- 这篇通俗易懂些:[聊聊数据库与缓存数据一致性问题](https://juejin.cn/post/6844903941646319623) +- 这篇更加深入:[携程最终一致和强一致性缓存实践](https://www.infoq.cn/article/hh4iouiijhwb4x46vxeo) + +但是目前看到的所有缓存方案,如果不在应用层引入版本,都无法解决下面这个数据不一致的场景: + +scenario + +### 解决方案 +本项目给大家带来了一个全新方案:标记删除,能够确保缓存与数据库的数据一致性,解决这个大难题。此方案是首创,已申请专利,现开源出来,供大家使用。 + +当开发者读数据时调用`Fetch`,并且确保更新数据库之后调用`TagAsDeleted`,那么就能够确保缓存最终一致。当遇见上图中步骤5写入v1时,最终会放弃写入。 +- 如何确保更新数据库之后会调用TagAsDeleted,参见[DB与缓存操作的原子性](https://dtm.pub/app/cache.html#atomic) +- 步骤5写入v1时,数据写入会被放弃的原理,参见[缓存一致性](https://dtm.pub/app/cache.html) + +完整的可运行的例子,参考[dtm-cases/cache](https://github.com/dtm-labs/dtm-cases/tree/main/cache) + +## 强一致的访问 +如果您的应用需要使用缓存,并且需要的是强一致,而不是最终一致,那么可以通过打开选项`StrongConsisteny`来支持,访问方式不变 +``` Go +rc.Options.StrongConsisteny = true +``` + +详细原理参考[缓存一致性](https://dtm.pub/app/cache.html),例子参考[dtm-cases/cache](https://github.com/dtm-labs/dtm-cases/tree/main/cache) + +## 降级以及强一致 +本库支持降级,降级开关分为 +- `DisableCacheRead`: 关闭缓存读,默认`false`;如果打开,那么Fetch就不从缓存读取数据,而是直接调用fn获取数据 +- `DisableCacheDelete`: 关闭缓存删除,默认false;如果打开,那么TagAsDeleted就什么操作都不做,直接返回 + +当Redis出现问题,需要降级时,可以通过这两个开关控制。如果您需要在降级升级的过程中,也保持强一致的访问,rockscache也是支持的 + +详细原理参考[缓存一致性](https://dtm.pub/app/cache.html),例子参考[dtm-cases/cache](https://github.com/dtm-labs/dtm-cases/tree/main/cache) + +## 防缓存击穿 +通过本库使用缓存,自带防缓存击穿功能。一方面`Fetch`会在进程内部使用`singleflight`,避免一个进程内有多个请求发到Redis,另一方面在Redis层会使用分布式锁,避免多个进程的多个请求同时发到DB,保证最终只有一个数据查询请求到DB。 + +本项目的的防缓存击穿,在热点缓存数据删除时,能够提供更快的响应时间。假如某个热点缓存数据需要花费3s计算,普通的防缓存击穿方案会导致这个时间内的所有这个热点数据的请求都等待3s,而本项目的方案,则能够立即返回。 +## 防缓存穿透 +通过本库使用缓存,自带防缓存穿透功能。当`Fetch`中的`fn`返回空字符串时,认为这是空结果,会将过期时间设定为rockscache选项中的`EmptyExpire`. + +`EmptyExpire` 默认为60s,如果设定为0,那么关闭防缓存穿透,对空结果不保存 + +## 防缓存雪崩 +通过本库使用缓存,自带防缓存雪崩。rockscache中的`RandomExpireAdjustment`默认为0.1,如果设定为600的过期时间,那么过期时间会被设定为`540s - 600s`中间的一个随机数,避免数据出现同时到期 + +## 联系我们 +### 公众号 +dtm-labs官方公众号:《分布式事务》,大量分布式事务干货分享,以及dtm-labs的最新消息 +### 交流群 +如果您希望更快的获得反馈,或者更多的了解其他用户在使用过程中的各种反馈,欢迎加入我们的微信交流群 + +请加作者的微信 yedf2008 好友或者扫码加好友,备注 `rockscache` 按照指引进群 + +![yedf2008](http://service.ivydad.com/cover/dubbingb6b5e2c0-2d2a-cd59-f7c5-c6b90aceb6f1.jpeg) + +欢迎使用[dtm-labs/rockscache](https://github.com/dtm-labs/rockscache),欢迎star支持我们 diff --git a/pkg/cache/rockscache/helper/README-en.md b/pkg/cache/rockscache/helper/README-en.md new file mode 100644 index 0000000..2c3cb8c --- /dev/null +++ b/pkg/cache/rockscache/helper/README-en.md @@ -0,0 +1,136 @@ +![license](https://img.shields.io/github/license/dtm-labs/rockscache) +![Build Status](https://github.com/dtm-labs/rockscache/actions/workflows/tests.yml/badge.svg?branch=main) +[![codecov](https://codecov.io/gh/dtm-labs/rockscache/branch/main/graph/badge.svg?token=UKKEYQLP3F)](https://codecov.io/gh/dtm-labs/rockscache) +[![Go Report Card](https://goreportcard.com/badge/github.com/dtm-labs/rockscache)](https://goreportcard.com/report/github.com/dtm-labs/rockscache) +[![Go Reference](https://pkg.go.dev/badge/github.com/dtm-labs/rockscache.svg)](https://pkg.go.dev/github.com/dtm-labs/rockscache) + +English | [简体中文](https://github.com/dtm-labs/rockscache/blob/main/helper/README-cn.md) + +# RocksCache +The first Redis cache library to ensure eventual consistency and strong consistency with DB. + +## Features +- Eventual Consistency: ensures eventual consistency of cache even in extreme cases +- Strong consistency: provides strong consistent access to applications +- Anti-breakdown: a better solution for cache breakdown +- Anti-penetration +- Anti-avalanche +- Batch Query + +## Usage +This cache repository uses the most common `update DB and then delete cache` cache management policy + +### Read cache +``` Go +import "github.com/dtm-labs/rockscache" + +// new a client for rockscache using the default options +rc := rockscache.NewClient(redisClient, NewDefaultOptions()) + +// use Fetch to fetch data +// 1. the first parameter is the key of the data +// 2. the second parameter is the data expiration time +// 3. the third parameter is the data fetch function which is called when the cache does not exist +v, err := rc.Fetch("key1", 300, func()(string, error) { + // fetch data from database or other sources + return "value1", nil +}) +``` + +### Delete the cache +``` Go +rc.TagAsDeleted(key) +``` + +## Batch usage + +### Batch read cache +``` Go +import "github.com/dtm-labs/rockscache" + +// new a client for rockscache using the default options +rc := rockscache.NewClient(redisClient, NewDefaultOptions()) + +// use FetchBatch to fetch data +// 1. the first parameter is the keys list of the data +// 2. the second parameter is the data expiration time +// 3. the third parameter is the batch data fetch function which is called when the cache does not exist +// the parameter of the batch data fetch function is the index list of those keys +// missing in cache, which can be used to form a batch query for missing data. +// the return value of the batch data fetch function is a map, with key of the +// index and value of the corresponding data in form of string +v, err := rc.FetchBatch([]string{"key1", "key2", "key3"}, 300, func(idxs []int) (map[int]string, error) { + // fetch data from database or other sources + values := make(map[int]string) + for _, i := range idxs { + values[i] = fmt.Sprintf("value%d", i) + } + return values, nil +}) +``` + +### Batch delete cache +``` Go +rc.TagAsDeletedBatch(keys) +``` + +## Eventual consistency +With the introduction of caching, consistency problems in a distributed system show up, as the data is stored in two places at the same time: the database and Redis. For background on this consistency problem, and an introduction to popular Redis caching solutions, see. +- [https://yunpengn.github.io/blog/2019/05/04/consistent-redis-sql/](https://yunpengn.github.io/blog/2019/05/04/consistent-redis-sql/) + +But all the caching solutions we've seen so far, without introducing versioning at the application level, fail to address the following data inconsistency scenario. + +cache-version-problem + +Even if you use lock to do the updating, there are still corner cases that can cause inconsistency. + +redis cache inconsistency + +### Solution +This project brings you a brand new solution that guarantee data consistency between the cache and the database, without introducing version. This solution is the first of its kind and has been patented and is now open sourced for everyone to use. + +When the developer calls `Fetch` when reading the data, and makes sure to call `TagAsDeleted` after updating the database, then the cache can guarentee the eventual consistency. When step 5 in the diagram above is writing to v1, the write in this solution will eventually be ignored. +- See [Atomicity of DB and cache operations](https://en.dtm.pub/app/cache.html#atomic) for how to ensure that TagAsDeleted is called after updating the database. +- See [Cache consistency](https://en.dtm.pub/app/cache.html) for why data writes are ignored when step 5 is writing v1 to cache. + +For a full runnable example, see [dtm-cases/cache](https://github.com/dtm-labs/dtm-cases/tree/main/cache) + +## Strongly consistent access +If your application needs to use caching and requires strong consistency rather than eventual consistency, then this can be supported by turning on the option `StrongConsisteny`, with the access method remaining the same +``` Go +rc.Options.StrongConsisteny = true +``` + +Refer to [cache consistency](https://en.dtm.pub/app/cache.html) for detailed principles and [dtm-cases/cache](https://github.com/dtm-labs/dtm-cases/tree/main/cache) for examples + +## Downgrading and strong consistency +The library supports downgrading. The downgrade switch is divided into +- `DisableCacheRead`: turns off cache reads, default `false`; if on, then Fetch does not read from the cache, but calls fn directly to fetch the data +- `DisableCacheDelete`: disables cache delete, default false; if on, then TagAsDeleted does nothing and returns directly + +When Redis has a problem and needs to be downgraded, you can control this with these two switches. If you need to maintain strong consistent access even during a downgrade, rockscache also supports + +Refer to [cache-consistency](https://en.dtm.pub/app/cache.html) for detailed principles and [dtm-cases/cache](https://github.com/dtm-labs/dtm-cases/tree/main/cache) for examples + +## Anti-Breakdown +The use of cache through this library comes with an anti-breakdown feature. On the one hand `Fetch` will use `singleflight` within the process to avoid multiple requests being sent to Redis within a process, and on the other hand distributed locks will be used in the Redis layer to avoid multiple requests being sent to the DB from multiple processes at the same time, ensuring that only one data query request ends up at the DB. + +The project's anti-breakdown provides a faster response time when hot cached data is deleted. If a hot cache data takes 3s to compute, a normal anti-breakdown solution would cause all requests for this hot data to wait 3s for this time, whereas this project's solution returns it immediately. + +## Anti-Penetration +The use of caching through this library comes with anti-penetration features. When `fn` in `Fetch` returns an empty string, this is considered an empty result and the expiry time is set to `EmptyExpire` in the rockscache option. + +`EmptyExpire` defaults to 60s, if set to 0 then anti-penetration is turned off and no empty results are saved + +## Anti-Avalanche +The cache is used with this library and comes with an anti-avalanche. `RandomExpireAdjustment` in rockscache defaults to 0.1, if set to an expiry time of 600 then the expiry time will be set to a random number in the middle of `540s - 600s` to avoid data expiring at the same time + +## Contact us + +## Chat Group + +Join the chat via [https://discord.gg/dV9jS5Rb33](https://discord.gg/dV9jS5Rb33). + +## Give a star! ⭐ + +If you think this project is interesting, or helpful to you, please give a star! diff --git a/pkg/cache/rockscache/helper/golint.sh b/pkg/cache/rockscache/helper/golint.sh new file mode 100644 index 0000000..e69de29 diff --git a/pkg/cache/rockscache/helper/test-cover.sh b/pkg/cache/rockscache/helper/test-cover.sh new file mode 100644 index 0000000..e69de29 diff --git a/pkg/cache/rockscache/utils.go b/pkg/cache/rockscache/utils.go new file mode 100644 index 0000000..59260ca --- /dev/null +++ b/pkg/cache/rockscache/utils.go @@ -0,0 +1,49 @@ +package rockscache + +import ( + "context" + "log" + "runtime/debug" + "time" + + "github.com/go-redis/redis/v8" +) + +var verbose = false + +// SetVerbose sets verbose mode. +func SetVerbose(v bool) { + verbose = v +} + +func debugf(format string, args ...interface{}) { + if verbose { + log.Printf(format, args...) + } +} +func now() int64 { + return time.Now().Unix() +} + +type redisConn interface { + Eval(ctx context.Context, script string, keys []string, args ...interface{}) *redis.Cmd +} + +func callLua(ctx context.Context, rdb redisConn, script string, keys []string, args []interface{}) (interface{}, error) { + debugf("callLua: script=%s, keys=%v, args=%v", script, keys, args) + v, err := rdb.Eval(ctx, script, keys, args).Result() + if err == redis.Nil { + err = nil + } + debugf("callLua result: v=%v, err=%v", v, err) + return v, err +} + +func withRecover(f func()) { + defer func() { + if r := recover(); r != nil { + debug.PrintStack() + } + }() + f() +} diff --git a/pkg/captcha/captcha.go b/pkg/captcha/captcha.go new file mode 100644 index 0000000..4cdc0fe --- /dev/null +++ b/pkg/captcha/captcha.go @@ -0,0 +1,77 @@ +package captcha + +import ( + "math/rand" + "time" + + "github.com/mojocn/base64Captcha" +) + +func init() { + //init rand seed + rand.Seed(time.Now().UnixNano()) +} + +// Captcha captcha basic information. +type Captcha struct { + Driver base64Captcha.Driver + Store base64Captcha.Store + Config *configJsonBody +} + +// NewCaptcha creates a captcha instance from driver and store +func NewCaptcha() *Captcha { + config := &configJsonBody{ + DriverAudio: audioConfig(), + DriverDigit: digitConfig(), + DriverString: stringConfig(), + DriverMath: mathConfig(), + DriverChinese: chineseConfig(), + } + driver := config.DriverMath + return &Captcha{ + Driver: driver, + Store: base64Captcha.DefaultMemStore, + Config: config, + } +} + +func (c *Captcha) SetDriver(captchaType string) *Captcha { + var driver base64Captcha.Driver + //create base64 encoding captcha + switch captchaType { + case "audio": + driver = c.Config.DriverAudio + case "string": + driver = c.Config.DriverString + case "math": + driver = c.Config.DriverMath + case "chinese": + driver = c.Config.DriverChinese + default: + driver = c.Config.DriverDigit + } + c.Driver = driver + return c +} + +// Generate generates a random id, base64 image string or an error if any +func (c *Captcha) Generate() (id, b64s string, err error) { + id, content, answer := c.Driver.GenerateIdQuestionAnswer() + item, err := c.Driver.DrawCaptcha(content) + if err != nil { + return "", "", err + } + c.Store.Set(id, answer) + b64s = item.EncodeB64string() + return +} + +// Verify by a given id key and remove the captcha value in store, +// return boolean value. +// if you has multiple captcha instances which share a same store. +// You may want to call `store.Verify` method instead. +func (c *Captcha) Verify(id, answer string, clear bool) (match bool) { + match = c.Store.Get(id, clear) == answer + return +} diff --git a/pkg/captcha/config.go b/pkg/captcha/config.go new file mode 100644 index 0000000..b987ca9 --- /dev/null +++ b/pkg/captcha/config.go @@ -0,0 +1,96 @@ +// captcha https://captcha.mojotv.cn/ +package captcha + +import ( + "image/color" + + "github.com/mojocn/base64Captcha" +) + +// configJsonBody json request body. +type configJsonBody struct { + DriverAudio *base64Captcha.DriverAudio + DriverString *base64Captcha.DriverString + DriverChinese *base64Captcha.DriverChinese + DriverMath *base64Captcha.DriverMath + DriverDigit *base64Captcha.DriverDigit +} + +// digitConfig 生成图形化数字验证码配置 +func digitConfig() *base64Captcha.DriverDigit { + digitType := &base64Captcha.DriverDigit{ + Height: 50, + Width: 100, + Length: 4, + MaxSkew: 0.45, + DotCount: 50, + } + return digitType +} + +// mathConfig 生成图形化算术验证码配置 +func mathConfig() *base64Captcha.DriverMath { + mathType := &base64Captcha.DriverMath{ + Height: 50, + Width: 150, + NoiseCount: 0, + ShowLineOptions: 0, + BgColor: &color.RGBA{ + R: 0, + G: 0, + B: 0, + A: 0, + }, + Fonts: []string{"RitaSmith.ttc"}, + } + return mathType +} + +// stringConfig 生成图形化字符串验证码配置 +func stringConfig() *base64Captcha.DriverString { + stringType := &base64Captcha.DriverString{ + Height: 100, + Width: 50, + NoiseCount: 0, + ShowLineOptions: base64Captcha.OptionShowHollowLine | base64Captcha.OptionShowSlimeLine, + Length: 5, + Source: "123456789qwertyuiopasdfghjklzxcvb", + BgColor: &color.RGBA{ + R: 40, + G: 30, + B: 89, + A: 29, + }, + Fonts: nil, + } + return stringType +} + +// chineseConfig 生成图形化汉字验证码配置 +func chineseConfig() *base64Captcha.DriverChinese { + chineseType := &base64Captcha.DriverChinese{ + Height: 50, + Width: 100, + NoiseCount: 0, + ShowLineOptions: base64Captcha.OptionShowSlimeLine, + Length: 2, + Source: "设想,你在,处理,消费者,的音,频输,出音,频可,能无,论什,么都,没有,任何,输出,或者,它可,能是,单声道,立体声,或是,环绕立,体声的,不想要,的值", + BgColor: &color.RGBA{ + R: 40, + G: 30, + B: 89, + A: 29, + }, + Fonts: nil, + } + return chineseType +} + +// audioConfig 生成图形化数字音频验证码配置 +func audioConfig() *base64Captcha.DriverAudio { + chineseType := &base64Captcha.DriverAudio{ + Length: 4, + Language: "zh", + } + return chineseType +} diff --git a/pkg/entity/common.go b/pkg/entity/common.go new file mode 100644 index 0000000..b03e82b --- /dev/null +++ b/pkg/entity/common.go @@ -0,0 +1,11 @@ +package entity + +type Result struct { + Code int `json:"code"` + Msg string `json:"msg"` + Data interface{} `json:"data"` +} + +type FailResult struct { + Msg string `json:"msg"` +} diff --git a/pkg/entity/kv.go b/pkg/entity/kv.go new file mode 100644 index 0000000..01367c6 --- /dev/null +++ b/pkg/entity/kv.go @@ -0,0 +1,56 @@ +package entity + +import ( + "encoding/json" +) + +type KV map[string]interface{} + +func NewKv(v interface{}) KV { + var kv = KV{} + + switch v.(type) { + case string: + kv.LoadJsonString(v.(string)) + case []byte: + kv.LoadJsonByte(v.([]byte)) + default: + kv.Load(v) + } + + return kv +} + +func (kv KV) Result(key string) KV { + return NewKv(kv[key]) +} + +func (kv KV) Set(key string, v interface{}) { + kv[key] = v +} + +func (kv *KV) Load(v interface{}) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + return json.Unmarshal(b, &kv) +} + +func (kv *KV) LoadJsonByte(v []byte) error { + return json.Unmarshal(v, &kv) +} + +func (kv *KV) LoadJsonString(v string) error { + return json.Unmarshal([]byte(v), &kv) +} + +func (kv KV) Json() string { + b, _ := json.Marshal(kv) + return string(b) +} + +func (kv KV) JsonByte() []byte { + b, _ := json.Marshal(kv) + return b +} diff --git a/pkg/entity/url.go b/pkg/entity/url.go new file mode 100644 index 0000000..67723b5 --- /dev/null +++ b/pkg/entity/url.go @@ -0,0 +1,119 @@ +package entity + +import ( + "encoding/json" + "net/url" + "sort" + "strings" +) + +type UrlValue map[string]string + +func QueryToMap(query string) (result UrlValue) { + result = UrlValue{} + var value url.Values + fUrl, _ := url.Parse(query) + if fUrl.Scheme == "" { + q, _ := url.ParseQuery(query) + value = q + } else { + value = fUrl.Query() + } + for k, v := range value { + result[k] = v[0] + } + return result +} + +func (value *UrlValue) Reader() *strings.Reader { + b, _ := json.Marshal(value) + return strings.NewReader(string(b)) +} + +func (value *UrlValue) Body() *strings.Reader { + return strings.NewReader(value.Encode()) +} + +func (value UrlValue) JsonString() string { + b, _ := json.Marshal(value) + return string(b) +} + +func (value *UrlValue) Encode() string { + values := url.Values{} + for k, v := range *value { + values.Set(k, v) + } + return values.Encode() +} + +func (value UrlValue) Query() string { + var buf strings.Builder + keys := make([]string, 0, len(value)) + for k := range value { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + v := value[k] + if buf.Len() > 0 { + buf.WriteByte('&') + } + buf.WriteString(k) + buf.WriteByte('=') + buf.WriteString(v) + } + return buf.String() +} + +func UrlMergeQuery(rawUrl, query string) string { + q, _ := url.Parse(rawUrl) + rawKeys, rawValues := parseQuery(q.RawQuery) + keys, values := parseQuery(query) + for idx, k := range keys { + value := values[idx] + cidx := containsIndex(rawKeys, k) + if cidx == -1 { + rawKeys = append(rawKeys, k) + rawValues = append(rawValues, value) + } else { + rawValues[cidx] = value + } + } + rawQuery := []string{} + for idx, key := range rawKeys { + rawQuery = append(rawQuery, key+"="+rawValues[idx]) + } + q.RawQuery = strings.Join(rawQuery, "&") + return q.String() +} + +func parseQuery(query string) (keys, values []string) { + for query != "" { + key := query + if i := strings.IndexAny(key, "&;"); i >= 0 { + key, query = key[:i], key[i+1:] + } else { + query = "" + } + if key == "" { + continue + } + value := "" + if i := strings.Index(key, "="); i >= 0 { + key, value = key[:i], key[i+1:] + } + keys = append(keys, key) + values = append(values, value) + } + return +} + +func containsIndex(list []string, elem string) int { + for idx, v := range list { + if elem == v { + return idx + } + } + return -1 +} diff --git a/pkg/geo/client.go b/pkg/geo/client.go new file mode 100644 index 0000000..fb77d48 --- /dev/null +++ b/pkg/geo/client.go @@ -0,0 +1,60 @@ +package geo + +import ( + "log" + "net" + + "github.com/oschwald/maxminddb-golang" +) + +type GeoClient struct { + Reader *maxminddb.Reader +} + +func NewGeoClient(file string) *GeoClient { + db, err := maxminddb.Open(file) + if err != nil { + log.Fatalf("geoip file not found") + } + return &GeoClient{ + Reader: db, + } +} + +// GeoInfo 地理信息 +type GeoInfo struct { + Country struct { + ISOCode string `maxminddb:"iso_code"` + Names map[string]string + GeonameID int `maxminddb:"geoname_id"` + } `maxminddb:"country"` + City struct { + Names map[string]string `maxminddb:"names"` + GeonameID int `maxminddb:"geoname_id"` + } `maxminddb:"city"` + Continent struct { + Names map[string]string `maxminddb:"names"` + } `maxminddb:"continent"` + Location struct { + Latitude float64 `maxminddb:"latitude"` + Longitude float64 `maxminddb:"longitude"` + } `maxminddb:"location"` + Postal struct { + Code string `maxminddb:"code"` + } `maxminddb:"postal"` + Subdivisions []struct { + ISOCode string `maxminddb:"iso_code"` + Names map[string]string `maxminddb:"names"` + } `maxminddb:"subdivisions"` +} + +// 解析IP地址 +func (c *GeoClient) Parse(ip string) (*GeoInfo, error) { + var info GeoInfo + netIp := net.ParseIP(ip) + err := c.Reader.Lookup(netIp, &info) + if err != nil { + return nil, err + } + return &info, nil +} diff --git a/pkg/gomailer/mailer.go b/pkg/gomailer/mailer.go new file mode 100644 index 0000000..1e3a5b1 --- /dev/null +++ b/pkg/gomailer/mailer.go @@ -0,0 +1,44 @@ +package gomailer + +import ( + "crypto/tls" + "fmt" + + "gopkg.in/gomail.v2" +) + +// Mailer 邮件发送接口 +type Mailer interface { + SendMail(to []string, subject string, body string) error +} + +// MailerImpl 邮件发送实现 +type MailerImpl struct { + message *gomail.Message + dialer *gomail.Dialer +} + +// NewMailer 创建邮件发送实例 +func NewMailer(host string, port int, username, password, nickname string) Mailer { + m := gomail.NewMessage() + m.SetHeader(`From`, m.FormatAddress(username, nickname)) + // 下面的配置改成你自己的邮箱配置 + d := gomail.NewDialer(host, port, username, password) + // 修改TLSconfig + d.TLSConfig = &tls.Config{InsecureSkipVerify: true} + return &MailerImpl{ + message: m, + dialer: d, + } +} + +// SendMail 发送邮件 +func (m *MailerImpl) SendMail(to []string, subject string, body string) error { + m.message.SetHeader(`To`, to...) + m.message.SetHeader(`Subject`, subject) + m.message.SetBody(`text/html`, body) + if err := m.dialer.DialAndSend(m.message); err != nil { + return fmt.Errorf("send mail failed: %v", err) + } + return nil +} diff --git a/pkg/google/androidpublisher/client.go b/pkg/google/androidpublisher/client.go new file mode 100644 index 0000000..5a8232f --- /dev/null +++ b/pkg/google/androidpublisher/client.go @@ -0,0 +1,42 @@ +package androidpublisher + +import ( + "context" + "fmt" + "net/http" + "net/url" + "time" + + "golang.org/x/oauth2" + "golang.org/x/oauth2/google" + apv3 "google.golang.org/api/androidpublisher/v3" + "google.golang.org/api/option" +) + +// NewService 创建androidpublisher service +func NewService(ctx context.Context, jsonKey []byte, proxyUrl string) (*apv3.Service, error) { + c := &http.Client{ + Timeout: 10 * time.Second, + } + + if proxyUrl != "" { + c.Transport = &http.Transport{ + Proxy: func(request *http.Request) (*url.URL, error) { + return url.Parse(proxyUrl) + }, + } + } + + ctx = context.WithValue(ctx, oauth2.HTTPClient, c) + conf, err := google.JWTConfigFromJSON(jsonKey, apv3.AndroidpublisherScope) + if err != nil { + return nil, fmt.Errorf("google.JWTConfigFromJSON: %w", err) + } + val := conf.Client(ctx).Transport.(*oauth2.Transport) + _, err = val.Source.Token() + if err != nil { + return nil, fmt.Errorf("val.Source.Token: %w", err) + } + service, err := apv3.NewService(ctx, option.WithHTTPClient(conf.Client(ctx))) + return service, err +} diff --git a/pkg/log/jacklog/chown.go b/pkg/log/jacklog/chown.go new file mode 100644 index 0000000..f8f3c9f --- /dev/null +++ b/pkg/log/jacklog/chown.go @@ -0,0 +1,9 @@ +package jacklog + +import ( + "os" +) + +func chown(_ string, _ os.FileInfo) error { + return nil +} diff --git a/pkg/log/jacklog/chown_linux.go b/pkg/log/jacklog/chown_linux.go new file mode 100644 index 0000000..0b00dae --- /dev/null +++ b/pkg/log/jacklog/chown_linux.go @@ -0,0 +1,19 @@ +package jacklog + +import ( + "os" + "syscall" +) + +// osChown is a var so we can mock it out during tests. +var osChown = os.Chown + +func chown(name string, info os.FileInfo) error { + f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, info.Mode()) + if err != nil { + return err + } + f.Close() + stat := info.Sys().(*syscall.Stat_t) + return osChown(name, int(stat.Uid), int(stat.Gid)) +} diff --git a/pkg/log/jacklog/jacklog.go b/pkg/log/jacklog/jacklog.go new file mode 100644 index 0000000..95906d7 --- /dev/null +++ b/pkg/log/jacklog/jacklog.go @@ -0,0 +1,654 @@ +package jacklog + +import ( + "compress/gzip" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" + "sync" + "time" +) + +const ( + //backupTimeFormat = "2006-01-02T15-04-05.000" + backupTimeFormat = time.RFC3339 + compressSuffix = ".gz" + defaultMaxSize = 100 + defaultFilemode = 0644 +) + +type PeriodType int + +const ( + PeriodNone PeriodType = iota + PeriodFiveMinute + PeriodTwentyMinute + PeriodHouly + PeriodDaily + PeriodMonthly + PeriodYearly +) + +// ensure we always implement io.WriteCloser +var _ io.WriteCloser = (*Logger)(nil) + +// Logger is an io.WriteCloser that writes to the specified filename. +// +// Logger opens or creates the logfile on first Write. If the file exists and +// is less than MaxSize megabytes, lumberjack will open and append to that file. +// If the file exists and its size is >= MaxSize megabytes, the file is renamed +// by putting the current time in a timestamp in the name immediately before the +// file's extension (or the end of the filename if there's no extension). A new +// log file is then created using original filename. +// +// Whenever a write would cause the current log file exceed MaxSize megabytes, +// the current file is closed, renamed, and a new log file created with the +// original name. Thus, the filename you give Logger is always the "current" log +// file. +// +// Backups use the log file name given to Logger, in the form +// `name-timestamp.ext` where name is the filename without the extension, +// timestamp is the time at which the log was rotated formatted with the +// time.Time format of `2006-01-02T15-04-05.000` and the extension is the +// original extension. For example, if your Logger.Filename is +// `/var/log/foo/server.log`, a backup created at 6:30pm on Nov 11 2016 would +// use the filename `/var/log/foo/server-2016-11-04T18-30-00.000.log` +// +// # Cleaning Up Old Log Files +// +// Whenever a new logfile gets created, old log files may be deleted. The most +// recent files according to the encoded timestamp will be retained, up to a +// number equal to MaxBackups (or all of them if MaxBackups is 0). Any files +// with an encoded timestamp older than MaxAge days are deleted, regardless of +// MaxBackups. Note that the time encoded in the timestamp is the rotation +// time, which may differ from the last time that file was written to. +// +// If MaxBackups and MaxAsge are both 0, no old log files will be deleted. +type Logger struct { + // Filename is the file to write logs to. Backup log files will be retained + // in the same directory. It uses -lumberjack.log in + // os.TempDir() if empty. + Filename string `json:"filename" yaml:"filename"` + + // MaxSize is the maximum size in megabytes of the log file before it gets + // rotated. It defaults to 100 megabytes. + MaxSize int `json:"maxsize" yaml:"maxsize"` + + // MaxAge is the maximum number of days to retain old log files based on the + // timestamp encoded in their filename. Note that a day is defined as 24 + // hours and may not exactly correspond to calendar days due to daylight + // savings, leap seconds, etc. The default is not to remove old log files + // based on age. + MaxAge int `json:"maxage" yaml:"maxage"` + + MaxPeriod PeriodType `json:"period" yaml:"period"` + + // MaxBackups is the maximum number of old log files to retain. The default + // is to retain all old log files (though MaxAge may still cause them to get + // deleted.) + MaxBackups int `json:"maxbackups" yaml:"maxbackups"` + + // LocalTime determines if the time used for formatting the timestamps in + // backup files is the computer's local time. The default is to use UTC + // time. + LocalTime bool `json:"localtime" yaml:"localtime"` + + // Compress determines if the rotated log files should be compressed + // using gzip. The default is not to perform compression. + Compress bool `json:"compress" yaml:"compress"` + + CompressFunc func(string) + + RotateHead string `json:"rotate_head" yaml:"rotate_head"` + + FileMode os.FileMode + + size int64 + file *os.File + mu sync.Mutex + + millCh chan bool + startMill sync.Once + + periodOnce sync.Once + periodTicker *time.Ticker + periodLastAt time.Time + lastWriteAt time.Time +} + +var ( + // currentTime exists so it can be mocked out by tests. + currentTime = time.Now + + // os_Stat exists so it can be mocked out by tests. + osStat = os.Stat + + // megabyte is the conversion factor between MaxSize and bytes. It is a + // variable so tests can mock it out and not need to write megabytes of data + // to disk. + megabyte = 1024 * 1024 +) + +// Write implements io.Writer. If a write would cause the log file to be larger +// than MaxSize, the file is closed, renamed to include a timestamp of the +// current time, and a new log file is created using the original log file name. +// If the length of the write is greater than MaxSize, an error is returned. +func (l *Logger) Write(p []byte) (n int, err error) { + l.mu.Lock() + defer l.mu.Unlock() + + writeLen := int64(len(p)) + if writeLen > l.max() { + return 0, fmt.Errorf( + "write length %d exceeds maximum file size %d", writeLen, l.max(), + ) + } + + if l.file == nil { + if err = l.openExistingOrNew(len(p)); err != nil { + return 0, err + } + } + + if l.size+writeLen > l.max() { + if err := l.rotate(); err != nil { + return 0, err + } + } + + now := currentTime() + + if l.MaxPeriod != PeriodNone { + l.periodCheck() + if l.periodLastAt.IsZero() { + l.periodLastAt = now + } + if l.periodIsExpired(now) { + l.periodLastAt = now + if err := l.rotate(); err != nil { + return 0, err + } + } + } + + if now.Sub(l.lastWriteAt).Seconds() > 10 { + l.checkFileExists() + } + + if l.size == 0 && l.RotateHead != "" { + n, _ := l.file.Write([]byte(l.RotateHead)) + l.size += int64(n) + } + + l.lastWriteAt = now + n, err = l.file.Write(p) + l.size += int64(n) + + return n, err +} + +func (l *Logger) periodCheck() { + l.periodOnce.Do(func() { + l.periodTicker = time.NewTicker(time.Minute) + go func() { + for _ = range l.periodTicker.C { + now := currentTime() + if l.periodIsExpired(now) && l.size > 0 { + l.periodLastAt = now + l.Rotate() + } + } + }() + }) +} + +func (l Logger) periodIsExpired(t time.Time) bool { + p := l.periodLastAt + switch l.MaxPeriod { + case PeriodFiveMinute: + beforeMod := p.Minute() / 5 + nowMod := t.Minute() / 5 + if beforeMod != nowMod || t.Hour() != p.Hour() { + return true + } + case PeriodTwentyMinute: + beforeMod := p.Minute() / 20 + nowMod := t.Minute() / 20 + if beforeMod != nowMod || t.Hour() != p.Hour() { + return true + } + case PeriodHouly: + if t.Hour() != p.Hour() || t.Day() != p.Day() { + return true + } + case PeriodDaily: + if t.Day() != p.Day() || t.Month() != t.Month() { + return true + } + case PeriodMonthly: + if t.Month() != p.Month() { + return true + } + case PeriodYearly: + if t.Year() != p.Year() { + return true + } + } + return false +} + +// Close implements io.Closer, and closes the current logfile. +func (l *Logger) Close() error { + l.mu.Lock() + defer l.mu.Unlock() + if err := l.backup(); err != nil { + return err + } + return l.close() +} + +// close closes the file if it is open. +func (l *Logger) close() error { + if l.file == nil { + return nil + } + err := l.file.Close() + l.file = nil + return err +} + +// Rotate causes Logger to close the existing log file and immediately create a +// new one. This is a helper function for applications that want to initiate +// rotations outside of the normal rotation rules, such as in response to +// SIGHUP. After rotating, this initiates compression and removal of old log +// files according to the configuration. +func (l *Logger) Rotate() error { + l.mu.Lock() + defer l.mu.Unlock() + return l.rotate() +} + +// rotate closes the current file, moves it aside with a timestamp in the name, +// (if it exists), opens a new file with the original filename, and then runs +// post-rotation processing and removal. +func (l *Logger) rotate() error { + if err := l.close(); err != nil { + return err + } + if err := l.openNew(); err != nil { + return err + } + l.mill() + return nil +} + +// openNew opens a new log file for writing, moving any old log file out of the +// way. This methods assumes the file has already been closed. +func (l *Logger) openNew() error { + l.backup() + + name := l.filename() + if l.FileMode == 0 { + l.FileMode = defaultFilemode + } + mode := os.FileMode(l.FileMode) + info, err := osStat(name) + if err == nil { + mode = info.Mode() + } + + // we use truncate here because this should only get called when we've moved + // the file ourselves. if someone else creates the file in the meantime, + // just wipe out the contents. + f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode) + if err != nil { + return fmt.Errorf("can't open new logfile: %s", err) + } + l.file = f + l.size = 0 + return nil +} + +func (l *Logger) backup() error { + if l.size == 0 { + return nil + } + err := os.MkdirAll(l.dir(), 0755) + if err != nil { + return fmt.Errorf("can't make directories for new logfile: %s", err) + } + + name := l.filename() + info, err := osStat(name) + if err == nil { + // Copy the mode off the old logfile. + // move the existing file + newname := l.backupName() + if err := os.Rename(name, newname); err != nil { + return fmt.Errorf("can't rename log file: %s", err) + } + + // this is a no-op anywhere but linux + if err := chown(name, info); err != nil { + return err + } + } + return nil +} + +// backupName creates a new filename from the given name, inserting a timestamp +// between the filename and the extension, using the local time if requested +// (otherwise UTC). +func (l Logger) backupName() string { + name := l.filename() + dir := filepath.Dir(name) + filename := filepath.Base(name) + ext := filepath.Ext(filename) + prefix := filename[:len(filename)-len(ext)] + endT := l.lastWriteAt + if !l.LocalTime { + endT = endT.UTC() + } + + timestamp := endT.Format(backupTimeFormat) + return filepath.Join(dir, fmt.Sprintf("%s-%s%s", prefix, timestamp, ext)) +} + +// openExistingOrNew opens the logfile if it exists and if the current write +// would not put it over MaxSize. If there is no such file or the write would +// put it over the MaxSize, a new file is created. +func (l *Logger) openExistingOrNew(writeLen int) error { + l.mill() + + filename := l.filename() + info, err := osStat(filename) + if os.IsNotExist(err) { + return l.openNew() + } + if err != nil { + return fmt.Errorf("error getting log file info: %s", err) + } + + if info.Size()+int64(writeLen) >= l.max() { + return l.rotate() + } + + file, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, defaultFilemode) + if err != nil { + // if we fail to open the old log file for some reason, just ignore + // it and open a new log file. + return l.openNew() + } + l.file = file + l.size = info.Size() + return nil +} + +// filename generates the name of the logfile from the current time. +func (l *Logger) filename() string { + if l.Filename != "" { + return l.Filename + } + name := filepath.Base(os.Args[0]) + "-jack.log" + return filepath.Join(os.TempDir(), name) +} + +// millRunOnce performs compression and removal of stale log files. +// Log files are compressed if enabled via configuration and old log +// files are removed, keeping at most l.MaxBackups files, as long as +// none of them are older than MaxAge. +func (l *Logger) millRunOnce() error { + if l.MaxBackups == 0 && l.MaxAge == 0 && !l.Compress { + return nil + } + + files, err := l.oldLogFiles() + if err != nil { + return err + } + + var compress, remove []logInfo + + if l.MaxBackups > 0 && l.MaxBackups < len(files) { + preserved := make(map[string]bool) + var remaining []logInfo + for _, f := range files { + // Only count the uncompressed log file or the + // compressed log file, not both. + fn := f.Name() + if strings.HasSuffix(fn, compressSuffix) { + fn = fn[:len(fn)-len(compressSuffix)] + } + preserved[fn] = true + + if len(preserved) > l.MaxBackups { + remove = append(remove, f) + } else { + remaining = append(remaining, f) + } + } + files = remaining + } + if l.MaxAge > 0 { + diff := time.Duration(int64(24*time.Hour) * int64(l.MaxAge)) + cutoff := currentTime().Add(-1 * diff) + + var remaining []logInfo + for _, f := range files { + if f.timestamp.Before(cutoff) { + remove = append(remove, f) + } else { + remaining = append(remaining, f) + } + } + files = remaining + } + + if l.Compress { + for _, f := range files { + if !strings.HasSuffix(f.Name(), compressSuffix) { + compress = append(compress, f) + } + } + } + + for _, f := range remove { + errRemove := os.Remove(filepath.Join(l.dir(), f.Name())) + if err == nil && errRemove != nil { + err = errRemove + } + } + + for _, f := range compress { + fn := filepath.Join(l.dir(), f.Name()) + errCompress := compressLogFile(fn, fn+compressSuffix, l.CompressFunc) + if err == nil && errCompress != nil { + err = errCompress + } + } + + return err +} + +// millRun runs in a goroutine to manage post-rotation compression and removal +// of old log files. +func (l *Logger) millRun() { + for range l.millCh { + // what am I going to do, log this? + _ = l.millRunOnce() + } +} + +// mill performs post-rotation compression and removal of stale log files, +// starting the mill goroutine if necessary. +func (l *Logger) mill() { + l.startMill.Do(func() { + l.millCh = make(chan bool, 1) + go l.millRun() + }) + select { + case l.millCh <- true: + default: + } +} + +func (l *Logger) checkFileExists() { + _, err := osStat(l.filename()) + if os.IsNotExist(err) { + l.openNew() + } +} + +// oldLogFiles returns the list of backup log files stored in the same +// directory as the current log file, sorted by ModTime +func (l *Logger) oldLogFiles() ([]logInfo, error) { + files, err := ioutil.ReadDir(l.dir()) + if err != nil { + return nil, fmt.Errorf("can't read log file directory: %s", err) + } + logFiles := []logInfo{} + + prefix, ext := l.prefixAndExt() + + for _, f := range files { + if f.IsDir() { + continue + } + if t, err := l.timeFromName(f.Name(), prefix, ext); err == nil { + logFiles = append(logFiles, logInfo{t, f}) + continue + } + if t, err := l.timeFromName(f.Name(), prefix, ext+compressSuffix); err == nil { + logFiles = append(logFiles, logInfo{t, f}) + continue + } + // error parsing means that the suffix at the end was not generated + // by lumberjack, and therefore it's not a backup file. + } + + sort.Sort(byFormatTime(logFiles)) + + return logFiles, nil +} + +// timeFromName extracts the formatted time from the filename by stripping off +// the filename's prefix and extension. This prevents someone's filename from +// confusing time.parse. +func (l *Logger) timeFromName(filename, prefix, ext string) (time.Time, error) { + if !strings.HasPrefix(filename, prefix) { + return time.Time{}, errors.New("mismatched prefix") + } + if !strings.HasSuffix(filename, ext) { + return time.Time{}, errors.New("mismatched extension") + } + ts := filename[len(prefix) : len(filename)-len(ext)] + return time.Parse(backupTimeFormat, ts) +} + +// max returns the maximum size in bytes of log files before rolling. +func (l *Logger) max() int64 { + if l.MaxSize == 0 { + return int64(defaultMaxSize * megabyte) + } + return int64(l.MaxSize) * int64(megabyte) +} + +// dir returns the directory for the current filename. +func (l *Logger) dir() string { + return filepath.Dir(l.filename()) +} + +// prefixAndExt returns the filename part and extension part from the Logger's +// filename. +func (l *Logger) prefixAndExt() (prefix, ext string) { + filename := filepath.Base(l.filename()) + ext = filepath.Ext(filename) + prefix = filename[:len(filename)-len(ext)] + "-" + return prefix, ext +} + +// compressLogFile compresses the given log file, removing the +// uncompressed log file if successful. +func compressLogFile(src, dst string, fn func(string)) (err error) { + f, err := os.Open(src) + if err != nil { + return fmt.Errorf("failed to open log file: %v", err) + } + defer f.Close() + + fi, err := osStat(src) + if err != nil { + return fmt.Errorf("failed to stat log file: %v", err) + } + + if err := chown(dst, fi); err != nil { + return fmt.Errorf("failed to chown compressed log file: %v", err) + } + + // If this file already exists, we presume it was created by + // a previous attempt to compress the log file. + gzf, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fi.Mode()) + if err != nil { + return fmt.Errorf("failed to open compressed log file: %v", err) + } + defer gzf.Close() + + gz := gzip.NewWriter(gzf) + + defer func() { + if err != nil { + os.Remove(dst) + err = fmt.Errorf("failed to compress log file: %v", err) + } + }() + + if _, err := io.Copy(gz, f); err != nil { + return err + } + if err := gz.Close(); err != nil { + return err + } + if err := gzf.Close(); err != nil { + return err + } + + if err := f.Close(); err != nil { + return err + } + if err := os.Remove(src); err != nil { + return err + } + + if fn != nil { + fn(dst) + } + + return nil +} + +// logInfo is a convenience struct to return the filename and its embedded +// timestamp. +type logInfo struct { + timestamp time.Time + os.FileInfo +} + +// byFormatTime sorts by newest time formatted in the name. +type byFormatTime []logInfo + +func (b byFormatTime) Less(i, j int) bool { + return b[i].timestamp.After(b[j].timestamp) +} + +func (b byFormatTime) Swap(i, j int) { + b[i], b[j] = b[j], b[i] +} + +func (b byFormatTime) Len() int { + return len(b) +} diff --git a/pkg/log/zaplog/config.go b/pkg/log/zaplog/config.go new file mode 100644 index 0000000..4b649fa --- /dev/null +++ b/pkg/log/zaplog/config.go @@ -0,0 +1,150 @@ +// Package zaplog 封装zap日志库,配置文件 +// @Description: 二次封装开源zap日志库 +// @File: zaplog +package zaplog + +// 定义默认的常量 +const ( + defaultBaseDirectoryName = "logs" // 日志根目录 + defaultInfoDirectoryName = "info" // info日志目录 + defaultWarnDirectoryName = "warn" // warn日志目录 + defaultErrorDirectoryName = "error" // error日志目录 + defaultInfoFileName = "info.log" // info日志文件 + defaultWarnFileName = "warn.log" // warn日志文件 + defaultErrorFileName = "error.log" // error日志文件 + defaultLogFileMaxSize = 128 // 日志文件大小,单位:MB + defaultLogFileMaxBackups = 180 // 日志文件保留个数 多于180个文件后,清理比价旧的日志 + defaultLogFileMaxAge = 360 // 任何编码时间戳超过 MaxAge 值的文件都将被删除 day + defaultLogFileCompress = false // 日志文件是否压缩 + defaultLogPrintTag = false // true:在终端和文件同时输出日志; false:只在文件输出日志 + +) + +// Config 配置文件结构体 +type Config struct { + BaseDirectoryName string + InfoDirectoryName string + WarnDirectoryName string + ErrorDirectoryName string + InfoFileName string + WarnFileName string + ErrorFileName string + LogFileMaxSize int + LogFileMaxBackups int + LogFileMaxAge int + LogFileCompress bool + LogPrintTag bool +} + +// Option 定义配置选项函数 +type Option func(*Config) + +// SetBaseDirectoryName 自定义日志根目录 +func SetBaseDirectoryName(name string) Option { + return func(c *Config) { + c.BaseDirectoryName = name + } +} + +// SetInfoDirectoryName 自定义info日志目录 +func SetInfoDirectoryName(name string) Option { + return func(c *Config) { + c.InfoDirectoryName = name + } +} + +// SetWarnDirectoryName 自定义warn日志目录 +func SetWarnDirectoryName(name string) Option { + return func(c *Config) { + c.WarnDirectoryName = name + } +} + +// SetErrorDirectoryName 自定义error日志目录 +func SetErrorDirectoryName(name string) Option { + return func(c *Config) { + c.ErrorDirectoryName = name + } +} + +// SetInfoFileName 自定义info文件名 +func SetInfoFileName(name string) Option { + return func(c *Config) { + c.InfoFileName = name + } +} + +// SetWarnFileName 自定义warn文件名 +func SetWarnFileName(name string) Option { + return func(c *Config) { + c.WarnFileName = name + } +} + +// SetErrorFileName 自定义error文件名 +func SetErrorFileName(name string) Option { + return func(c *Config) { + c.ErrorFileName = name + } +} + +// SetLogFileMaxSize 自定义日志文件大小 +func SetLogFileMaxSize(size int) Option { + return func(c *Config) { + c.LogFileMaxSize = size + } +} + +// SetLogFileMaxBackups 自定义日志文件保留个数 +func SetLogFileMaxBackups(size int) Option { + return func(c *Config) { + c.LogFileMaxBackups = size + } +} + +// SetLogFileMaxAge 自定义日志保留时间 +func SetLogFileMaxAge(size int) Option { + return func(c *Config) { + c.LogFileMaxAge = size + } +} + +// SetLogFileCompress 自定义日志文件是否压缩 +func SetLogFileCompress(compress bool) Option { + return func(c *Config) { + c.LogFileCompress = compress + } +} + +// SetLogPrintTag 自定义日志输出标记位 true:在终端和文件同时输出日志; false:只在文件输出日志 +func SetLogPrintTag(tag bool) Option { + return func(c *Config) { + c.LogPrintTag = tag + } +} + +// NewConfig 应用函数选项配置 +func NewConfig(opts ...Option) Config { + // 初始化默认值 + defaultConfig := Config{ + BaseDirectoryName: defaultBaseDirectoryName, + InfoDirectoryName: defaultInfoDirectoryName, + WarnDirectoryName: defaultWarnDirectoryName, + ErrorDirectoryName: defaultErrorDirectoryName, + InfoFileName: defaultInfoFileName, + WarnFileName: defaultWarnFileName, + ErrorFileName: defaultErrorFileName, + LogFileMaxSize: defaultLogFileMaxSize, + LogFileMaxBackups: defaultLogFileMaxBackups, + LogFileMaxAge: defaultLogFileMaxAge, + LogFileCompress: defaultLogFileCompress, + LogPrintTag: defaultLogPrintTag, + } + + // 依次调用opts函数列表中的函数,为结构体成员赋值 + for _, opt := range opts { + opt(&defaultConfig) + } + + return defaultConfig +} diff --git a/pkg/log/zaplog/zap.go b/pkg/log/zaplog/zap.go new file mode 100644 index 0000000..546200d --- /dev/null +++ b/pkg/log/zaplog/zap.go @@ -0,0 +1,121 @@ +// Package zaplog 封装zap日志库主程序文件 +// @Description: 二次封装开源zap日志库 +// @File: zaplog +package zaplog + +import ( + "fmt" + "io" + "os" + "time" + + "github.com/natefinch/lumberjack" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +// Logger 全局变量,导出给调用者使用 +var Logger *zap.Logger + +// Init 初始化日志相关目录 +func Init(config Config) error { + // 创建日志根目录 + if _, err := os.Stat(config.BaseDirectoryName); os.IsNotExist(err) { + err := os.MkdirAll(config.BaseDirectoryName, os.ModePerm) + if err != nil { + return fmt.Errorf("error creating directory, err: %v", err) + } + } + + // 创建日志子目录 + if err := os.MkdirAll(fmt.Sprintf("%s/%s", config.BaseDirectoryName, config.InfoDirectoryName), os.ModePerm); err != nil { + return fmt.Errorf("error creating info directory, err: %v", err) + } + if err := os.MkdirAll(fmt.Sprintf("%s/%s", config.BaseDirectoryName, config.WarnDirectoryName), os.ModePerm); err != nil { + return fmt.Errorf("error creating warn directory, err: %v", err) + } + if err := os.MkdirAll(fmt.Sprintf("%s/%s", config.BaseDirectoryName, config.ErrorDirectoryName), os.ModePerm); err != nil { + return fmt.Errorf("error creating err directory, err: %v", err) + } + + // 自定义初始化zap库 + initLogger(config) + + return nil +} + +// getWriter 获取wirter文件写入 +func getWriter(logBasePath, logLevelPath, LogFileName string, config Config) io.Writer { + return &lumberjack.Logger{ + Filename: fmt.Sprintf("%s/%s/%s", logBasePath, logLevelPath, LogFileName), + MaxBackups: config.LogFileMaxBackups, + MaxSize: config.LogFileMaxSize, + MaxAge: config.LogFileMaxAge, + Compress: config.LogFileCompress, + } +} + +// initLog 初始化日志 +func initLogger(c Config) { + // 获取io.Writer实现 + infoWriter := getWriter(c.BaseDirectoryName, c.InfoDirectoryName, c.InfoFileName, c) + warnWriter := getWriter(c.BaseDirectoryName, c.WarnDirectoryName, c.WarnFileName, c) + errWriter := getWriter(c.BaseDirectoryName, c.ErrorDirectoryName, c.ErrorFileName, c) + + // 获取日志默认配置 + encoderConfig := zap.NewProductionEncoderConfig() + + // 自定义日志输出格式 + // 修改TimeKey + encoderConfig.TimeKey = "time" + // 修改MessageKey + encoderConfig.MessageKey = "message" + // 时间格式符合人类观看 + encoderConfig.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { + enc.AppendString(t.Format("2006-01-02 15:04:05.000")) + } + // 日志等级大写INFO + encoderConfig.EncodeLevel = func(lvl zapcore.Level, enc zapcore.PrimitiveArrayEncoder) { + enc.AppendString(lvl.CapitalString()) + } + // 日志打印时所处代码位置 + encoderConfig.EncodeCaller = func(caller zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) { + enc.AppendString(caller.TrimmedPath()) + } + // 加载自定义配置为json格式 + encoder := zapcore.NewJSONEncoder(encoderConfig) + + // 自定义日志级别 info + infoLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { + return lvl <= zap.InfoLevel && lvl >= zap.DebugLevel + }) + // 自定义日志级别 warn + warnLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { + return lvl <= zap.WarnLevel && lvl > zap.InfoLevel + }) + // 自定义日志级别 err + errLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { + return lvl <= zap.FatalLevel && lvl > zap.WarnLevel + }) + + // 日志文件输出位置 + var core zapcore.Core + if c.LogPrintTag { + //同时在文件和终端输出日志 + core = zapcore.NewTee( + zapcore.NewCore(encoder, zapcore.AddSync(infoWriter), infoLevel), // info级别日志 + zapcore.NewCore(encoder, zapcore.AddSync(warnWriter), warnLevel), // warn级别日志 + zapcore.NewCore(encoder, zapcore.AddSync(errWriter), errLevel), // error级别日志 + zapcore.NewCore(encoder, zapcore.Lock(os.Stdout), zap.DebugLevel), + ) + } else { + //只在文件输出日志 + core = zapcore.NewTee( + zapcore.NewCore(encoder, zapcore.AddSync(infoWriter), infoLevel), // info级别日志 + zapcore.NewCore(encoder, zapcore.AddSync(warnWriter), warnLevel), // warn级别日志 + zapcore.NewCore(encoder, zapcore.AddSync(errWriter), errLevel), // error级别日志 + ) + } + + Logger = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1)) +} diff --git a/pkg/marshal/json.go b/pkg/marshal/json.go new file mode 100644 index 0000000..cc45e06 --- /dev/null +++ b/pkg/marshal/json.go @@ -0,0 +1,11 @@ +package marshal + +import "encoding/json" + +func ConvertStructToOtherType(src interface{}, dst interface{}) error { + data, err := json.Marshal(src) + if err != nil { + return err + } + return json.Unmarshal(data, dst) +} diff --git a/pkg/middleware/xhttp/xhttp.go b/pkg/middleware/xhttp/xhttp.go new file mode 100644 index 0000000..5d3f217 --- /dev/null +++ b/pkg/middleware/xhttp/xhttp.go @@ -0,0 +1,45 @@ +// Just for kratos post body with http query param +package xhttp + +import ( + "context" + + "github.com/go-kratos/kratos/v2/middleware" + "github.com/go-kratos/kratos/v2/transport/http" +) + +// Server returns a new server middleware for kratos http context. +func Server() middleware.Middleware { + return func(handler middleware.Handler) middleware.Handler { + return func(ctx context.Context, req interface{}) (reply interface{}, err error) { + if c, ok := ctx.(http.Context); ok { + // c.Header().Set("Access-Control-Allow-Origin", "*") + // c.Header().Set("Access-Control-Allow-Origin", "*") // 这是允许访问所有域 + // c.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE") //服务器支持的所有跨域请求的方法,为了避免浏览次请求的多次'预检'请求 + // // header的类型 + // c.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,X_Requested_With,Accept, Origin, Host, Connection, Accept-Encoding, Accept-Language,DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma") + // // 允许跨域设置 可以返回其他子段 + // c.Header().Set("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar") // 跨域关键设置 让浏览器可以解析 + // c.Header().Set("Access-Control-Max-Age", "172800") // 缓存请求信息 单位为秒 + // c.Header().Set("Access-Control-Allow-Credentials", "false") + // c.Header().Set("content-type", "application/json") // 设置返回格式是json + ctx = RequestToContext(c) + } + return handler(ctx, req) + } + } +} + +type httpReqKey struct{} + +func RequestToContext(ctx http.Context) context.Context { + return context.WithValue(ctx, httpReqKey{}, ctx) +} + +func RequestFromContext(ctx context.Context) http.Context { + c, ok := ctx.Value(httpReqKey{}).(http.Context) + if !ok { + return nil + } + return c +} diff --git a/pkg/os/command_path.go b/pkg/os/command_path.go new file mode 100644 index 0000000..331ace6 --- /dev/null +++ b/pkg/os/command_path.go @@ -0,0 +1,23 @@ +package os + +import ( + "os" + "strings" +) + +// CommandPath return executable command +func CommandPath(cmd string) string { + var cmdPath string + envPath := os.Getenv("PATH") + pathArray := strings.Split(envPath, ":") + for _, path := range pathArray { + p := path + "/" + cmd + if info, err := os.Stat(p); err == nil { + if (info.Mode() & 0111) > 0 { + cmdPath = p + break + } + } + } + return cmdPath +} diff --git a/pkg/os/file.go b/pkg/os/file.go new file mode 100644 index 0000000..58adebd --- /dev/null +++ b/pkg/os/file.go @@ -0,0 +1,22 @@ +package os + +import ( + "os" + "strings" +) + +// FileExists check file is exists +func FileExists(name string) bool { + if _, err := os.Stat(name); err != nil { + if os.IsNotExist(err) { + return false + } + } + return true +} + +// GetFileName return file name +func GetFileName(file string) string { + b := strings.Split(file, "/") + return b[len(b)-1] +} diff --git a/pkg/os/gzip.go b/pkg/os/gzip.go new file mode 100644 index 0000000..698e55a --- /dev/null +++ b/pkg/os/gzip.go @@ -0,0 +1,27 @@ +package os + +import ( + "compress/gzip" + "io" +) + +func GzipPack(srcs []string, w io.Writer) error { + gw := gzip.NewWriter(w) + defer gw.Close() + + err := TarPack(srcs, gw) + + return err +} + +func GzipUnpack(dst string, r io.Reader) error { + gr, err := gzip.NewReader(r) + + if err != nil { + return err + } + + fwErr := TarUnpack(dst, gr) + + return fwErr +} diff --git a/pkg/os/tar.go b/pkg/os/tar.go new file mode 100644 index 0000000..78a1a30 --- /dev/null +++ b/pkg/os/tar.go @@ -0,0 +1,159 @@ +package os + +import ( + "archive/tar" + "fmt" + "io" + "log" + "os" + "path/filepath" + "strings" +) + +func TarPack(srcs []string, w io.Writer) error { + tw := tar.NewWriter(w) + defer tw.Close() + + // Loop through each source + var fwErr error + for _, s := range srcs { + // ensure the src actually exists before trying to tar it + if _, err := os.Stat(s); err != nil { + return err + } + + // walk path + fwErr = filepath.Walk(s, func(path string, fi os.FileInfo, err error) error { + if err != nil { + return err + } + + header, err := tar.FileInfoHeader(fi, fi.Name()) + if err != nil { + return err + } + + var link string + if fi.Mode()&os.ModeSymlink == os.ModeSymlink { + if link, err = os.Readlink(path); err != nil { + return err + } + log.Println("Symbolic link found at %s to %s", path, link) + + // Rewrite header for SymLink + header, err = tar.FileInfoHeader(fi, link) + if err != nil { + return err + } + } + + header.Name = strings.TrimPrefix(filepath.ToSlash(path), "/") + + if err = tw.WriteHeader(header); err != nil { + return err + } + + if !fi.Mode().IsRegular() { + log.Println("Directory found at %s", path) + return nil + } + + log.Println("File found at %s", path) + + file, err := os.Open(path) + if err != nil { + return err + } + + defer file.Close() + _, err = io.Copy(tw, file) + return err + }) + + if fwErr != nil { + return fwErr + } + } + + return fwErr +} + +func TarUnpack(dst string, r io.Reader) error { + tr := tar.NewReader(r) + + for { + header, err := tr.Next() + + switch { + + // if no more files are found return + case err == io.EOF: + return nil + + // return any other error + case err != nil: + return err + + // if the header is nil, just skip it (not sure how this happens) + case header == nil: + continue + } + + // the target location where the dir/file should be created + target := filepath.Join(dst, header.Name) + + // the following switch could also be done using fi.Mode(), not sure if there + // a benefit of using one vs. the other. + // fi := header.FileInfo() + + // check the file type + switch header.Typeflag { + + // if its a symlink and it doesn't exist create it + case tar.TypeSymlink: + log.Println("Symlink found at %s", target) + + // Check if something already exists + _, err := os.Stat(target) + if err == nil { + return fmt.Errorf("Failed to create symlink because file already exists at %s", target) + } + + // Create the link + log.Println("Creating link %s to %s", target, header.Linkname) + err = os.Symlink(header.Linkname, target) + + if err != nil { + log.Println("Failed creating link %s to %s", target, header.Linkname) + return err + } + + // if its a dir and it doesn't exist create it + case tar.TypeDir: + log.Println("Directory found at %s", target) + if _, err := os.Stat(target); err != nil { + if err := os.MkdirAll(target, 0755); err != nil { + return err + } + } + + // if it's a file create it + case tar.TypeReg: + log.Println("File found at %s", target) + f, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode)) + if err != nil { + return err + } + + // copy over contents + _, err = io.Copy(f, tr) + + // Explicitly close otherwise too many files remain open + f.Close() + + if err != nil { + return err + } + } + } +} diff --git a/pkg/pagination/pagination.go b/pkg/pagination/pagination.go new file mode 100644 index 0000000..f87852b --- /dev/null +++ b/pkg/pagination/pagination.go @@ -0,0 +1,98 @@ +package pagination + +import ( + "math" +) + +type Paginator interface { + // Limit 数据条目数限制 + Limit() int32 + + // Offset 数据偏移量 + Offset() int32 +} + +const ( + // DefaultPage 默认分页数 + DefaultPage = 1 + + // DefaultPageSize 默认分页条目数 + DefaultPageSize = 20 +) + +var _ Paginator = (*Pagination)(nil) + +type options struct { + total int32 +} + +type Option func(*options) + +type Pagination struct { + page int32 + pageSize int32 + total int32 +} + +func WithTotal(b int32) Option { + return func(c *options) { + c.total = b + } +} + +func NewPagination(page, pageSize int32, opts ...Option) *Pagination { + opt := options{ + total: 0, + } + + for _, o := range opts { + o(&opt) + } + + return &Pagination{page: page, pageSize: pageSize, total: opt.total} +} + +func (p Pagination) Page() int32 { + if p.page < 1 { + return DefaultPage + } + return p.page +} + +func (p Pagination) PageSize() int32 { + if p.pageSize < 1 { + return DefaultPageSize + } + return p.pageSize +} + +func (p Pagination) From() int32 { + return (p.Page()-1)*p.PageSize() + 1 +} + +func (p Pagination) To() int32 { + var to int32 + to = p.PageSize() * p.Page() + if to > p.total { + to = p.total + } + return to +} + +func (p Pagination) LastPage() int32 { + lastPage := math.Ceil(float64(p.total) / float64(p.PageSize())) + if lastPage <= 0 { + lastPage = 0 + } + + return int32(lastPage) +} + +// Limit 实现 pagination.Paginator 方法 +func (p Pagination) Limit() int32 { + return p.PageSize() +} + +func (p Pagination) Offset() int32 { + return p.Limit() * (p.Page() - 1) +} diff --git a/pkg/tkasynq/asynq.go b/pkg/tkasynq/asynq.go new file mode 100644 index 0000000..860a8fa --- /dev/null +++ b/pkg/tkasynq/asynq.go @@ -0,0 +1,228 @@ +package tkasynq + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/google/uuid" + "github.com/hibiken/asynq" +) + +type TaskInfo struct { + // ID is the identifier of the task. + ID string + + // Queue is the name of the queue in which the task belongs. + Queue string + + // Type is the type name of the task. + Type string + + // Payload is the payload data of the task. + Payload []byte +} +type AsynqClient struct { + client *asynq.Client + inspector *asynq.Inspector +} + +const ( + // Default max retry count used if nothing is specified. + defaultMaxRetry = 25 + + // Default timeout used if both timeout and deadline are not specified. + defaultTimeout = 30 * time.Minute +) + +type OptionType int + +const ( + MaxRetryOpt OptionType = iota + QueueOpt + TimeoutOpt + ProcessAtOpt + ProcessInOpt + TaskIDOpt +) + +type ( + retryOption int + queueOption string + taskIDOption string + timeoutOption time.Duration + processAtOption time.Time + processInOption time.Duration +) + +// Option specifies the task processing behavior. +type Option interface { + // String returns a string representation of the option. + String() string + + // Type describes the type of the option. + Type() OptionType + + // Value returns a value used to create this option. + Value() interface{} +} + +// MaxRetry returns an option to specify the max number of times +// the task will be retried. +// +// Negative retry count is treated as zero retry. +func MaxRetry(n int) Option { + if n < 0 { + n = 0 + } + return retryOption(n) +} + +func (n retryOption) String() string { return fmt.Sprintf("MaxRetry(%d)", int(n)) } +func (n retryOption) Type() OptionType { return MaxRetryOpt } +func (n retryOption) Value() interface{} { return int(n) } + +// Timeout returns an option to specify how long a task may run. +// If the timeout elapses before the Handler returns, then the task +// will be retried. +// +// Zero duration means no limit. +// +// If there's a conflicting Deadline option, whichever comes earliest +// will be used. +func Timeout(d time.Duration) Option { + return timeoutOption(d) +} + +func (d timeoutOption) String() string { return fmt.Sprintf("Timeout(%v)", time.Duration(d)) } +func (d timeoutOption) Type() OptionType { return TimeoutOpt } +func (d timeoutOption) Value() interface{} { return time.Duration(d) } + +// Queue returns an option to specify the queue to enqueue the task into. +func Queue(name string) Option { + return queueOption(name) +} + +func (name queueOption) String() string { return fmt.Sprintf("Queue(%q)", string(name)) } +func (name queueOption) Type() OptionType { return QueueOpt } +func (name queueOption) Value() interface{} { return string(name) } + +// ProcessAt returns an option to specify when to process the given task. +// +// If there's a conflicting ProcessIn option, the last option passed to Enqueue overrides the others. +func ProcessAt(t time.Time) Option { + return processAtOption(t) +} + +func (t processAtOption) String() string { + return fmt.Sprintf("ProcessAt(%v)", time.Time(t).Format(time.UnixDate)) +} +func (t processAtOption) Type() OptionType { return ProcessAtOpt } +func (t processAtOption) Value() interface{} { return time.Time(t) } + +// ProcessIn returns an option to specify when to process the given task relative to the current time. +// +// If there's a conflicting ProcessAt option, the last option passed to Enqueue overrides the others. +func ProcessIn(d time.Duration) Option { + return processInOption(d) +} + +func (d processInOption) String() string { return fmt.Sprintf("ProcessIn(%v)", time.Duration(d)) } +func (d processInOption) Type() OptionType { return ProcessInOpt } +func (d processInOption) Value() interface{} { return time.Duration(d) } + +// TaskID returns an option to specify the task ID. +func TaskID(id string) Option { + return taskIDOption(id) +} + +func (id taskIDOption) String() string { return fmt.Sprintf("TaskID(%q)", string(id)) } +func (id taskIDOption) Type() OptionType { return TaskIDOpt } +func (id taskIDOption) Value() interface{} { return string(id) } + +// NewClient 初始化client +func NewClient(client *asynq.Client, clientOpt *asynq.RedisClientOpt) *AsynqClient { + return &AsynqClient{ + client: client, + inspector: asynq.NewInspector(clientOpt), + } +} + +type option struct { + retry int + queue string + timeout time.Duration + processAt time.Time + taskId string +} + +func (y *AsynqClient) composeOptions(opts ...Option) option { + res := option{ + retry: defaultMaxRetry, + queue: "default", + timeout: 0, // do not set to deafultTimeout here + processAt: time.Now(), + taskId: uuid.NewString(), + } + + for _, opt := range opts { + switch opt := opt.(type) { + case retryOption: + res.retry = int(opt) + case queueOption: + res.queue = string(opt) + case timeoutOption: + res.timeout = time.Duration(opt) + case processAtOption: + res.processAt = time.Time(opt) + case processInOption: + res.processAt = time.Now().Add(time.Duration(opt)) + case taskIDOption: + res.taskId = string(opt) + fmt.Println("here-taksId: ", res.taskId) + } + + } + return res + +} + +// EnqueueTask 支持及时任务,支持时间点任务,多少时间后任务 +func (y *AsynqClient) EnqueueTask(jobTag string, v interface{}, queue string, opts ...Option) (*TaskInfo, error) { + payload, err := json.Marshal(v) + if err != nil { + return nil, err + } + + task := asynq.NewTask(jobTag, payload) + opt := y.composeOptions(opts...) + info, err := y.client.Enqueue( + task, + asynq.Queue(queue), + asynq.MaxRetry(opt.retry), + asynq.Timeout(time.Duration(opt.timeout)*time.Second), + asynq.ProcessAt(opt.processAt), + asynq.TaskID(opt.taskId), + ) + + if err != nil { + return nil, err + } + + return &TaskInfo{ + ID: info.ID, + Queue: info.Queue, + Type: info.Type, + Payload: info.Payload, + }, nil +} + +// RunTask 执行对应taskId任务 +func (y *AsynqClient) RunTask(queue, id string) error { + return y.inspector.RunTask(queue, id) +} + +// DeleteTask 删除对应taskId任务 +func (y *AsynqClient) DeleteTask(queue, id string) error { + return y.inspector.DeleteTask(queue, id) +} diff --git a/pkg/utils/download.go b/pkg/utils/download.go new file mode 100644 index 0000000..540e692 --- /dev/null +++ b/pkg/utils/download.go @@ -0,0 +1,27 @@ +package utils + +import ( + "io" + "net/http" + "os" +) + +func DownloadFile(filepath string, url string) error { + // Get the data + resp, err := http.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + + // Create the file + out, err := os.Create(filepath) + if err != nil { + return err + } + defer out.Close() + + // Write the body to file + _, err = io.Copy(out, resp.Body) + return err +} diff --git a/pkg/utils/encoding_convert.go b/pkg/utils/encoding_convert.go new file mode 100644 index 0000000..1c99e16 --- /dev/null +++ b/pkg/utils/encoding_convert.go @@ -0,0 +1,26 @@ +package utils + +import ( + "bytes" + "golang.org/x/text/encoding/simplifiedchinese" + "golang.org/x/text/transform" + "io/ioutil" +) + +func GbkToUtf8(s []byte) ([]byte, error) { + reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewDecoder()) + d, e := ioutil.ReadAll(reader) + if e != nil { + return nil, e + } + return d, nil +} + +func Utf8ToGbk(s []byte) ([]byte, error) { + reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewEncoder()) + d, e := ioutil.ReadAll(reader) + if e != nil { + return nil, e + } + return d, nil +} diff --git a/pkg/utils/janitor/janitor.go b/pkg/utils/janitor/janitor.go new file mode 100644 index 0000000..9755855 --- /dev/null +++ b/pkg/utils/janitor/janitor.go @@ -0,0 +1,48 @@ +package janitor + +import ( + "context" + "sync" + "time" +) + +// Janitor for collecting expired items and cleaning them. +type Janitor struct { + ctx context.Context + interval time.Duration + done chan struct{} + once sync.Once +} + +func NewJanitor(ctx context.Context, interval time.Duration) *Janitor { + j := &Janitor{ + ctx: ctx, + interval: interval, + done: make(chan struct{}), + } + return j +} + +// stop to stop the janitor. +func (j *Janitor) stop() { + j.once.Do(func() { close(j.done) }) +} + +// Run with the given cleanup callback function. +func (j *Janitor) Run(fn func()) { + go func() { + ticker := time.NewTicker(j.interval) + defer ticker.Stop() + for { + select { + case <-ticker.C: + fn() + case <-j.done: + fn() // last call + return + case <-j.ctx.Done(): + j.stop() + } + } + }() +} diff --git a/pkg/utils/match/match.go b/pkg/utils/match/match.go new file mode 100644 index 0000000..c5461f5 --- /dev/null +++ b/pkg/utils/match/match.go @@ -0,0 +1,240 @@ +// Fork by https://github.com/tidwall/match +// Package match provides a simple pattern matcher with unicode support. +package match + +import ( + "unicode/utf8" +) + +// Match returns true if str matches pattern. This is a very +// simple wildcard match where '*' matches on any number characters +// and '?' matches on any one character. +// +// pattern: +// +// { term } +// +// term: +// +// '*' matches any sequence of non-Separator characters +// '?' matches any single non-Separator character +// c matches character c (c != '*', '?', '\\') +// '\\' c matches character c +func Match(str, pattern string) bool { + if pattern == "*" { + return true + } + return match(str, pattern, 0, nil, -1) == rMatch +} + +// MatchLimit is the same as Match but will limit the complexity of the match +// operation. This is to avoid long running matches, specifically to avoid ReDos +// attacks from arbritary inputs. +// +// How it works: +// The underlying match routine is recursive and may call itself when it +// encounters a sandwiched wildcard pattern, such as: `user:*:name`. +// Everytime it calls itself a counter is incremented. +// The operation is stopped when counter > maxcomp*len(str). +func MatchLimit(str, pattern string, maxcomp int) (matched, stopped bool) { + if pattern == "*" { + return true, false + } + counter := 0 + r := match(str, pattern, len(str), &counter, maxcomp) + if r == rStop { + return false, true + } + return r == rMatch, false +} + +type result int + +const ( + rNoMatch result = iota + rMatch + rStop +) + +func match(str, pat string, slen int, counter *int, maxcomp int) result { + // check complexity limit + if maxcomp > -1 { + if *counter > slen*maxcomp { + return rStop + } + *counter++ + } + + for len(pat) > 0 { + var wild bool + pc, ps := rune(pat[0]), 1 + if pc > 0x7f { + pc, ps = utf8.DecodeRuneInString(pat) + } + var sc rune + var ss int + if len(str) > 0 { + sc, ss = rune(str[0]), 1 + if sc > 0x7f { + sc, ss = utf8.DecodeRuneInString(str) + } + } + switch pc { + case '?': + if ss == 0 { + return rNoMatch + } + case '*': + // Ignore repeating stars. + for len(pat) > 1 && pat[1] == '*' { + pat = pat[1:] + } + + // If this star is the last character then it must be a match. + if len(pat) == 1 { + return rMatch + } + + // Match and trim any non-wildcard suffix characters. + var ok bool + str, pat, ok = matchTrimSuffix(str, pat) + if !ok { + return rNoMatch + } + + // Check for single star again. + if len(pat) == 1 { + return rMatch + } + + // Perform recursive wildcard search. + r := match(str, pat[1:], slen, counter, maxcomp) + if r != rNoMatch { + return r + } + if len(str) == 0 { + return rNoMatch + } + wild = true + default: + if ss == 0 { + return rNoMatch + } + if pc == '\\' { + pat = pat[ps:] + pc, ps = utf8.DecodeRuneInString(pat) + if ps == 0 { + return rNoMatch + } + } + if sc != pc { + return rNoMatch + } + } + str = str[ss:] + if !wild { + pat = pat[ps:] + } + } + if len(str) == 0 { + return rMatch + } + return rNoMatch +} + +// matchTrimSuffix matches and trims any non-wildcard suffix characters. +// Returns the trimed string and pattern. +// +// This is called because the pattern contains extra data after the wildcard +// star. Here we compare any suffix characters in the pattern to the suffix of +// the target string. Basically a reverse match that stops when a wildcard +// character is reached. This is a little trickier than a forward match because +// we need to evaluate an escaped character in reverse. +// +// Any matched characters will be trimmed from both the target +// string and the pattern. +func matchTrimSuffix(str, pat string) (string, string, bool) { + // It's expected that the pattern has at least two bytes and the first byte + // is a wildcard star '*' + match := true + for len(str) > 0 && len(pat) > 1 { + pc, ps := utf8.DecodeLastRuneInString(pat) + var esc bool + for i := 0; ; i++ { + if pat[len(pat)-ps-i-1] != '\\' { + if i&1 == 1 { + esc = true + ps++ + } + break + } + } + if pc == '*' && !esc { + match = true + break + } + sc, ss := utf8.DecodeLastRuneInString(str) + if !((pc == '?' && !esc) || pc == sc) { + match = false + break + } + str = str[:len(str)-ss] + pat = pat[:len(pat)-ps] + } + return str, pat, match +} + +var maxRuneBytes = [...]byte{244, 143, 191, 191} + +// Allowable parses the pattern and determines the minimum and maximum allowable +// values that the pattern can represent. +// When the max cannot be determined, 'true' will be returned +// for infinite. +func Allowable(pattern string) (min, max string) { + if pattern == "" || pattern[0] == '*' { + return "", "" + } + + minb := make([]byte, 0, len(pattern)) + maxb := make([]byte, 0, len(pattern)) + var wild bool + for i := 0; i < len(pattern); i++ { + if pattern[i] == '*' { + wild = true + break + } + if pattern[i] == '?' { + minb = append(minb, 0) + maxb = append(maxb, maxRuneBytes[:]...) + } else { + minb = append(minb, pattern[i]) + maxb = append(maxb, pattern[i]) + } + } + if wild { + r, n := utf8.DecodeLastRune(maxb) + if r != utf8.RuneError { + if r < utf8.MaxRune { + r++ + if r > 0x7f { + b := make([]byte, 4) + nn := utf8.EncodeRune(b, r) + maxb = append(maxb[:len(maxb)-n], b[:nn]...) + } else { + maxb = append(maxb[:len(maxb)-n], byte(r)) + } + } + } + } + return string(minb), string(maxb) +} + +// IsPattern returns true if the string is a pattern. +func IsPattern(str string) bool { + for i := 0; i < len(str); i++ { + if str[i] == '*' || str[i] == '?' { + return true + } + } + return false +} diff --git a/pkg/utils/math.go b/pkg/utils/math.go new file mode 100644 index 0000000..f8d6ef1 --- /dev/null +++ b/pkg/utils/math.go @@ -0,0 +1,8 @@ +package utils + +import "fmt" + +func GetSaltedPwd(pwd, salt string) string { + str := fmt.Sprintf("%s-%s", salt, pwd) + return MD5Hex([]byte(str)) +} diff --git a/pkg/utils/md5.go b/pkg/utils/md5.go new file mode 100644 index 0000000..65ca467 --- /dev/null +++ b/pkg/utils/md5.go @@ -0,0 +1,37 @@ +package utils + +import ( + "crypto/hmac" + "crypto/md5" + "crypto/sha256" + "encoding/base64" + "encoding/hex" + "encoding/json" + "fmt" +) + +func MD5Base64(content []byte) string { + h := md5.New() + h.Write(content) + return base64.StdEncoding.EncodeToString(h.Sum(nil)) +} + +func MD5Hex(content []byte) string { + h := md5.New() + h.Write(content) + return hex.EncodeToString(h.Sum(nil)) +} + +func HmacSha256(data string, secret string) string { + h := hmac.New(sha256.New, []byte(secret)) + h.Write([]byte(data)) + //return base64.StdEncoding.EncodeToString(h.Sum(nil)) + return hex.EncodeToString(h.Sum(nil)) +} + +// MD5Any 仅仅是为了判断唯一性 +func MD5Any(v any) string { + b, _ := json.Marshal(v) + md5Bytes := md5.Sum(b) + return fmt.Sprintf("%x", md5Bytes) +} diff --git a/pkg/utils/net.go b/pkg/utils/net.go new file mode 100644 index 0000000..a2d6e4d --- /dev/null +++ b/pkg/utils/net.go @@ -0,0 +1,51 @@ +package utils + +import ( + "net" + "net/http" + "strings" +) + +// GetLocalIP returns the non loopback local IP of the host +func GetLocalIP() string { + addrs, err := net.InterfaceAddrs() + if err != nil { + return "" + } + for _, address := range addrs { + // check the address type and if it is not a loopback the display it + if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + if ipnet.IP.To4() != nil { + return ipnet.IP.String() + } + } + } + return "" +} + +// GetClientPublicIP returns the client public IP +func GetClientPublicIP(r *http.Request) string { + defaultIp := "127.0.0.1" + ip := r.Header.Get("X-Real-IP") + if net.ParseIP(ip) != nil { + return ip + } + + ip = r.Header.Get("X-Forward-For") + for _, i := range strings.Split(ip, ",") { + if net.ParseIP(i) != nil { + return i + } + } + + ip, _, err := net.SplitHostPort(r.RemoteAddr) + if err != nil { + return defaultIp + } + + if net.ParseIP(ip) != nil { + return ip + } + + return defaultIp +} diff --git a/pkg/utils/pool.go b/pkg/utils/pool.go new file mode 100644 index 0000000..58664be --- /dev/null +++ b/pkg/utils/pool.go @@ -0,0 +1,70 @@ +package utils + +import "fmt" + +// GoroutinePool define routine pool +type GoroutinePool struct { + Queue chan func() error + Number int + Total int + + result chan error + finishCallback func() +} + +// Init routine pool +func (g *GoroutinePool) Init(number int, total int) { + g.Queue = make(chan func() error, total) + g.Number = number + g.Total = total + g.result = make(chan error, total) +} + +// Start routine pool +func (g *GoroutinePool) Start() { + for i := 0; i < g.Number; i++ { + go func() { + for { + task, ok := <-g.Queue + if !ok { + break + } + + err := task() + g.result <- err + } + }() + } + + // get each work result + for j := 0; j < g.Total; j++ { + res, ok := <-g.result + if !ok { + break + } + + if res != nil { + fmt.Println(res) + } + } + + if g.finishCallback != nil { + g.finishCallback() + } +} + +// Stop close queue +func (g *GoroutinePool) Stop() { + close(g.Queue) + close(g.result) +} + +// AddTask job func +func (g *GoroutinePool) AddTask(task func() error) { + g.Queue <- task +} + +// SetFinishCallback func +func (g *GoroutinePool) SetFinishCallback(callback func()) { + g.finishCallback = callback +} diff --git a/pkg/utils/random.go b/pkg/utils/random.go new file mode 100644 index 0000000..0ce9ee3 --- /dev/null +++ b/pkg/utils/random.go @@ -0,0 +1,21 @@ +package utils + +import ( + "math/rand" + "time" +) + +var letterRunes = []rune("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + +func init() { + rand.Seed(time.Now().UnixNano()) +} + +// RandString return random strings +func RandString(n int) string { + b := make([]rune, n) + for i := range b { + b[i] = letterRunes[rand.Intn(len(letterRunes))] + } + return string(b) +} diff --git a/pkg/utils/snowflake/snowflake.go b/pkg/utils/snowflake/snowflake.go new file mode 100644 index 0000000..e74f8ca --- /dev/null +++ b/pkg/utils/snowflake/snowflake.go @@ -0,0 +1,366 @@ +package snowflake + +import ( + "encoding/base64" + "encoding/binary" + "errors" + "fmt" + "strconv" + "sync" + "time" +) + +var ( + // Epoch is set to the snowflake epoch of Nov 04 2010 01:42:54 UTC in milliseconds + // You may customize this to set a different epoch for your application. + Epoch int64 = 1577836800000 + + // NodeBits holds the number of bits to use for Node + // Remember, you have a total 22 bits to share between Node/Step + NodeBits uint8 = 10 + + // StepBits holds the number of bits to use for Step + // Remember, you have a total 22 bits to share between Node/Step + StepBits uint8 = 12 + + // DEPRECATED: the below four variables will be removed in a future release. + mu sync.Mutex + nodeMax int64 = -1 ^ (-1 << NodeBits) + nodeMask = nodeMax << StepBits + stepMask int64 = -1 ^ (-1 << StepBits) + timeShift = NodeBits + StepBits + nodeShift = StepBits +) + +const encodeBase32Map = "ybndrfg8ejkmcpqxot1uwisza345h769" + +var decodeBase32Map [256]byte + +const encodeBase58Map = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ" + +var decodeBase58Map [256]byte + +// A JSONSyntaxError is returned from UnmarshalJSON if an invalid ID is provided. +type JSONSyntaxError struct{ original []byte } + +func (j JSONSyntaxError) Error() string { + return fmt.Sprintf("invalid snowflake ID %q", string(j.original)) +} + +// ErrInvalidBase58 is returned by ParseBase58 when given an invalid []byte +var ErrInvalidBase58 = errors.New("invalid base58") + +// ErrInvalidBase32 is returned by ParseBase32 when given an invalid []byte +var ErrInvalidBase32 = errors.New("invalid base32") + +// Create maps for decoding Base58/Base32. +// This speeds up the process tremendously. +func init() { + + for i := 0; i < len(encodeBase58Map); i++ { + decodeBase58Map[i] = 0xFF + } + + for i := 0; i < len(encodeBase58Map); i++ { + decodeBase58Map[encodeBase58Map[i]] = byte(i) + } + + for i := 0; i < len(encodeBase32Map); i++ { + decodeBase32Map[i] = 0xFF + } + + for i := 0; i < len(encodeBase32Map); i++ { + decodeBase32Map[encodeBase32Map[i]] = byte(i) + } + + n, _ = NewNode(0) +} + +// A Node struct holds the basic information needed for a snowflake generator +// node +type Node struct { + mu sync.Mutex + epoch time.Time + time int64 + node int64 + step int64 + + nodeMax int64 + nodeMask int64 + stepMask int64 + timeShift uint8 + nodeShift uint8 +} + +var n *Node + +// An ID is a custom type used for a snowflake ID. This is used so we can +// attach methods onto the ID. +type ID int64 + +// NewNode returns a new snowflake node that can be used to generate snowflake +// IDs +func NewNode(node int64) (*Node, error) { + + // re-calc in case custom NodeBits or StepBits were set + // DEPRECATED: the below block will be removed in a future release. + mu.Lock() + nodeMax = -1 ^ (-1 << NodeBits) + nodeMask = nodeMax << StepBits + stepMask = -1 ^ (-1 << StepBits) + timeShift = NodeBits + StepBits + nodeShift = StepBits + mu.Unlock() + + n := Node{} + n.node = node + n.nodeMax = -1 ^ (-1 << NodeBits) + n.nodeMask = n.nodeMax << StepBits + n.stepMask = -1 ^ (-1 << StepBits) + n.timeShift = NodeBits + StepBits + n.nodeShift = StepBits + + if n.node < 0 || n.node > n.nodeMax { + return nil, errors.New("Node number must be between 0 and " + strconv.FormatInt(n.nodeMax, 10)) + } + + var curTime = time.Now() + // add time.Duration to curTime to make sure we use the monotonic clock if available + n.epoch = curTime.Add(time.Unix(Epoch/1000, (Epoch%1000)*1000000).Sub(curTime)) + + return &n, nil +} + +// Generate creates and returns a unique snowflake ID +// To help guarantee uniqueness +// - Make sure your system is keeping accurate system time +// - Make sure you never have multiple nodes running with the same node ID +func Generate() ID { return n.Generate() } +func (n *Node) Generate() ID { + + n.mu.Lock() + + now := time.Since(n.epoch).Nanoseconds() / 1000 / 1000 + + if now == n.time { + n.step = (n.step + 1) & n.stepMask + + if n.step == 0 { + for now <= n.time { + now = time.Since(n.epoch).Nanoseconds() / 1000 / 1000 + } + } + } else { + n.step = 0 + } + + n.time = now + + r := ID((now)<= 32 { + b = append(b, encodeBase32Map[f%32]) + f /= 32 + } + b = append(b, encodeBase32Map[f]) + + for x, y := 0, len(b)-1; x < y; x, y = x+1, y-1 { + b[x], b[y] = b[y], b[x] + } + + return string(b) +} + +// ParseBase32 parses a base32 []byte into a snowflake ID +// NOTE: There are many different base32 implementations so becareful when +// doing any interoperation. +func ParseBase32(b []byte) (ID, error) { + + var id int64 + + for i := range b { + if decodeBase32Map[b[i]] == 0xFF { + return -1, ErrInvalidBase32 + } + id = id*32 + int64(decodeBase32Map[b[i]]) + } + + return ID(id), nil +} + +// Base36 returns a base36 string of the snowflake ID +func (f ID) Base36() string { + return strconv.FormatInt(int64(f), 36) +} + +// ParseBase36 converts a Base36 string into a snowflake ID +func ParseBase36(id string) (ID, error) { + i, err := strconv.ParseInt(id, 36, 64) + return ID(i), err +} + +// Base58 returns a base58 string of the snowflake ID +func (f ID) Base58() string { + + if f < 58 { + return string(encodeBase58Map[f]) + } + + b := make([]byte, 0, 11) + for f >= 58 { + b = append(b, encodeBase58Map[f%58]) + f /= 58 + } + b = append(b, encodeBase58Map[f]) + + for x, y := 0, len(b)-1; x < y; x, y = x+1, y-1 { + b[x], b[y] = b[y], b[x] + } + + return string(b) +} + +// ParseBase58 parses a base58 []byte into a snowflake ID +func ParseBase58(b []byte) (ID, error) { + + var id int64 + + for i := range b { + if decodeBase58Map[b[i]] == 0xFF { + return -1, ErrInvalidBase58 + } + id = id*58 + int64(decodeBase58Map[b[i]]) + } + + return ID(id), nil +} + +// Base64 returns a base64 string of the snowflake ID +func (f ID) Base64() string { + return base64.StdEncoding.EncodeToString(f.Bytes()) +} + +// ParseBase64 converts a base64 string into a snowflake ID +func ParseBase64(id string) (ID, error) { + b, err := base64.StdEncoding.DecodeString(id) + if err != nil { + return -1, err + } + return ParseBytes(b) + +} + +// Bytes returns a byte slice of the snowflake ID +func (f ID) Bytes() []byte { + return []byte(f.String()) +} + +// ParseBytes converts a byte slice into a snowflake ID +func ParseBytes(id []byte) (ID, error) { + i, err := strconv.ParseInt(string(id), 10, 64) + return ID(i), err +} + +// IntBytes returns an array of bytes of the snowflake ID, encoded as a +// big endian integer. +func (f ID) IntBytes() [8]byte { + var b [8]byte + binary.BigEndian.PutUint64(b[:], uint64(f)) + return b +} + +// ParseIntBytes converts an array of bytes encoded as big endian integer as +// a snowflake ID +func ParseIntBytes(id [8]byte) ID { + return ID(int64(binary.BigEndian.Uint64(id[:]))) +} + +// Time returns an int64 unix timestamp in milliseconds of the snowflake ID time +func (f ID) Time() int64 { + return (int64(f) >> timeShift) + Epoch +} + +// Node returns an int64 of the snowflake ID node number +func (f ID) Node() int64 { + return int64(f) & nodeMask >> nodeShift +} + +// Step returns an int64 of the snowflake step (or sequence) number +func (f ID) Step() int64 { + return int64(f) & stepMask +} + +// MarshalJSON returns a json byte array string of the snowflake ID. +func (f ID) MarshalJSON() ([]byte, error) { + buff := make([]byte, 0, 22) + buff = append(buff, '"') + buff = strconv.AppendInt(buff, int64(f), 10) + buff = append(buff, '"') + return buff, nil +} + +// UnmarshalJSON converts a json byte array of a snowflake ID into an ID type. +func (f *ID) UnmarshalJSON(b []byte) error { + if len(b) < 3 || b[0] != '"' || b[len(b)-1] != '"' { + return JSONSyntaxError{b} + } + + i, err := strconv.ParseInt(string(b[1:len(b)-1]), 10, 64) + if err != nil { + return err + } + + *f = ID(i) + return nil +} diff --git a/pkg/utils/snowflake/snowflake_extend.go b/pkg/utils/snowflake/snowflake_extend.go new file mode 100644 index 0000000..13fb6f0 --- /dev/null +++ b/pkg/utils/snowflake/snowflake_extend.go @@ -0,0 +1,55 @@ +package snowflake + +import ( + "context" + "errors" + "fmt" + "log" + "os" + "strconv" + "time" + + "sandc/pkg/utils/janitor" + + log2 "github.com/go-kratos/kratos/v2/log" + + "github.com/go-redis/redis/v8" +) + +var ( + SNOWFLAKE_KEY = "snowflake:" + SNOWFLAKE_NODE_TTL = 60 + EMPTY_NODE_ERROR = errors.New("nodemax is full") +) + +func GetSnowflakeNode(red redis.Cmdable) *Node { + hostname, _ := os.Hostname() + var key string + var nodeID int + var value string + for i := 0; i < 1023; i++ { + nodeID = i + field := strconv.Itoa(i) + value = fmt.Sprintf("%s#%d", hostname, os.Getpid()) + key = SNOWFLAKE_KEY + field + cmd := red.SetNX(context.Background(), key, value, time.Duration(SNOWFLAKE_NODE_TTL)*time.Second) + if cmd.Val() { + log2.Infof("GetSnowflakeNode: key[%s] value[%s]", key, value) + break + } else { + log2.Errorf("GetSnowflakeNode: key[%s] value[%s] err[%v]", key, value, cmd.Err()) + } + } + if key == "" { + log.Fatalln(EMPTY_NODE_ERROR) + } + sf, err := NewNode(int64(nodeID)) + if err != nil { + log.Fatalln(err) + } + j := janitor.NewJanitor(context.Background(), time.Second*3) + j.Run(func() { + red.Expire(context.Background(), key, time.Duration(SNOWFLAKE_NODE_TTL)*time.Second) + }) + return sf +} diff --git a/pkg/utils/stime/time.go b/pkg/utils/stime/time.go new file mode 100644 index 0000000..bdb7cb6 --- /dev/null +++ b/pkg/utils/stime/time.go @@ -0,0 +1,54 @@ +package stime + +import ( + "time" + + "google.golang.org/protobuf/types/known/timestamppb" +) + +func FormatPBTime(pbtime *timestamppb.Timestamp) string { + if pbtime.AsTime().Unix() > 0 { + return pbtime.AsTime().Local().Format("2006-01-02 15:04:05") + } + return "" +} + +func FormatPBTimeToTime(pbtime *timestamppb.Timestamp) time.Time { + return pbtime.AsTime().Local() +} + +func FormatStringToTime(stime string) (time.Time, error) { + return time.ParseInLocation("2006-01-02 15:04:05", stime, time.Local) +} + +func FormatStringToPBTime(stime string) (*timestamppb.Timestamp, error) { + t, err := time.ParseInLocation("2006-01-02 15:04:05", stime, time.Local) + if err != nil { + return nil, err + } + return FormatTimePB(t), nil +} + +func FormatTimePB(ttime time.Time) *timestamppb.Timestamp { + return timestamppb.New(ttime) +} + +func FormatTimeString(ttime time.Time) string { + if ttime.Unix() > 0 { + return ttime.Local().Format("2006-01-02 15:04:05") + } + return "" +} + +func FormatInt64ToString(n int64) string { + time := time.Unix(n, 0) + return FormatTimeString(time) +} + +func FormatStringToInt64(stime string) int64 { + time, err := FormatStringToTime(stime) + if err != nil { + return 0 + } + return time.Unix() +} diff --git a/pkg/utils/string.go b/pkg/utils/string.go new file mode 100644 index 0000000..6e33dcb --- /dev/null +++ b/pkg/utils/string.go @@ -0,0 +1,60 @@ +package utils + +import ( + "strconv" + "strings" +) + +// ConvertStringToInt32 convert string to int32 +func ConvertStringToInt32(a string) int32 { + if a != "" { + b, err := strconv.Atoi(a) + if err != nil { + return -1 + } else { + return int32(b) + } + } + return 0 +} + +// ConvertSliceStringToInt32 convert slice string to slice int32 +func ConvertSliceStringToInt32(m []string) []int32 { + rs := make([]int32, 0) + for _, v := range m { + rs = append(rs, ConvertStringToInt32(v)) + } + return rs +} + +// ConverStringToInt32Slice convert string to slice int32 +func ConverStringToInt32Slice(s string) []int32 { + slice := strings.Split(strings.Trim(s, ","), ",") + mIn32 := ConvertSliceStringToInt32(slice) + return mIn32 +} + +// ConvertIntStringToBoolString convert int string to bool string +func ConvertIntStringToBoolString(a string) string { + if a != "" { + b, err := strconv.Atoi(a) + if err != nil { + return "false" + } else { + if b == 0 { + return "false" + } else { + return "true" + } + } + } + return "false" +} + +// 获取切片中指定下标的值 +func GetSliceValueByIndex(slice []string, index int) string { + if len(slice) > index { + return slice[index] + } + return "" +} diff --git a/pkg/utils/struct.go b/pkg/utils/struct.go new file mode 100644 index 0000000..499bb12 --- /dev/null +++ b/pkg/utils/struct.go @@ -0,0 +1,32 @@ +package utils + +import ( + "reflect" + "strings" +) + +// StructToMapJson struct to map json tag +func StructToMapJson(obj interface{}) map[string]interface{} { + t := reflect.TypeOf(obj) + v := reflect.ValueOf(obj) + var data = make(map[string]interface{}) + for i := 0; i < t.NumField(); i++ { + jsonKey := t.Field(i).Tag.Get("json") + if jsonKey != "-" { + // tagName, tagOpts + tagName, _ := parseTags(jsonKey) + data[tagName] = v.Field(i).Interface() + } + } + return data +} + +// parseTags "age,omitempty" +func parseTags(s string) (string, []string) { + if len(s) == 0 { + return "", nil + } + + sl := strings.Split(s, ",") + return sl[0], sl[1:] +} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go new file mode 100644 index 0000000..f5d1aae --- /dev/null +++ b/pkg/utils/utils.go @@ -0,0 +1,109 @@ +package utils + +import ( + "crypto/md5" + "fmt" + "math/rand" + "strconv" + "time" + + "github.com/google/uuid" +) + +func BoolToInt32(value bool) int32 { + if value { + return 1 + } + return 0 +} + +// Int32ToBool convert int32 to bool +func Int32ToBool(value int32) bool { + return value == 1 +} + +// ChangColumnIndexToAxis 将列索引转换为Excel列坐标 +func ChangColumnIndexToAxis(intIndexX, intIndexY int) string { + var arr = []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "AA", "AB", "AC", "AD", "AE", "AF", "AG", "AH", "AI", "AJ", "AK", "AL", "AM", "AN"} + intIndexY = intIndexY + 1 + resultY := "" + coloumnTotal := len(arr) + for { + if intIndexY <= coloumnTotal { + resultY = resultY + arr[intIndexY-1] + break + } + mo := intIndexY % coloumnTotal + resultY = arr[mo-1] + resultY + + shang := intIndexY / coloumnTotal + if shang <= coloumnTotal { + resultY = arr[shang-1] + resultY + break + } + intIndexY = shang + } + return resultY + strconv.Itoa(intIndexX) +} + +func IsDiffDay(last, now int64) bool { + lastYear, lastMonth, lastDay := time.Unix(last, 0).Date() + nowYear, nowMonth, nowDay := time.Unix(now, 0).Date() + if lastDay != nowDay || lastMonth != nowMonth || lastYear != nowYear { + return true + } + return false +} + +func RandInterval(b1, b2 int32) int32 { + if b1 == b2 { + return b1 + } + + min, max := int64(b1), int64(b2) + if min > max { + min, max = max, min + } + return int32(rand.Int63n(max-min+1) + min) +} + +// 获取utc时间t的days前(负数)或者后(正数)个的0点前时间 +func ZeroDays(t int64, days int) int64 { + // t += int64(days-1) * 86400 + t += int64(days) * 86400 + year, month, day := time.Unix(t, 0).Date() + date := time.Date(year, month, day, 23, 59, 59, 0, time.Local) + return date.Unix() +} + +func GetDateNum() uint32 { + date, _ := strconv.Atoi(time.Now().Format("20060102")) + return uint32(date) +} + +func GetDateNum2(t int64) uint32 { + date, _ := strconv.Atoi(time.Unix(t, 0).Format("20060102")) + return uint32(date) +} + +// 根据固定的字符串生成uuid +func GenerateUUID(specificString string) string { + hash := md5.Sum([]byte(specificString)) + + uniqueUUID := fmt.Sprintf("%x", hash) + parsedUUID, err := uuid.Parse(uniqueUUID) + if err != nil { + return "invalid UUID" + } + return parsedUUID.String() +} + +// InSlice 判断字符串是否在切片中 +func InSlice(str string, slice []string) bool { + for _, v := range slice { + if v == str { + return true + } + } + return false +} diff --git a/pkg/utils/writer.go b/pkg/utils/writer.go new file mode 100644 index 0000000..a3adea4 --- /dev/null +++ b/pkg/utils/writer.go @@ -0,0 +1,51 @@ +package utils + +import ( + "errors" + "io" + "log" + "os" +) + +var MissingTarget = errors.New("Missing write target") + +func NewWriter(paths ...string) (io.Writer, error) { + if len(paths) == 0 { + return nil, MissingTarget + } else if len(paths) == 1 { + return stringToWriter(paths[0]) + } + var writers []io.Writer + for _, path := range paths { + writer, err := stringToWriter(path) + if err != nil { + return nil, err + } + writers = append(writers, writer) + } + ioWriter := io.MultiWriter(writers...) + return ioWriter, nil +} + +func MustWriter(paths ...string) io.Writer { + writer, err := NewWriter(paths...) + if err != nil { + log.Fatalln(err) + } + return writer +} + +var stringWriter = map[string]io.Writer{ + "stdout": os.Stdout, + "stderr": os.Stderr, + "stdin": os.Stdin, + "discard": io.Discard, +} + +func stringToWriter(path string) (io.Writer, error) { + if writer, ok := stringWriter[path]; ok { + return writer, nil + } + f, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666) + return f, err +} diff --git a/pkg/xcrypto/aes.go b/pkg/xcrypto/aes.go new file mode 100644 index 0000000..896f470 --- /dev/null +++ b/pkg/xcrypto/aes.go @@ -0,0 +1,66 @@ +package xcrypto + +import ( + "crypto/aes" + "encoding/base64" + "errors" +) + +// key字节长度16 +type Aes struct { + key []byte +} + +// NewAes +func NewAes(key []byte) *Aes { + return &Aes{key: generateKey(key)} +} + +func (a Aes) EncryptECB(src string) string { + origData := []byte(src) + cipher, _ := aes.NewCipher(a.key) + length := (len(origData) + aes.BlockSize) / aes.BlockSize + plain := make([]byte, length*aes.BlockSize) + copy(plain, origData) + pad := byte(len(plain) - len(origData)) + for i := len(origData); i < len(plain); i++ { + plain[i] = pad + } + encrypted := make([]byte, len(plain)) + for bs, be := 0, cipher.BlockSize(); bs <= len(origData); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() { + cipher.Encrypt(encrypted[bs:be], plain[bs:be]) + } + + return base64.StdEncoding.EncodeToString(encrypted) +} +func (a Aes) DecryptECB(src string) (string, error) { + encrypted, err := base64.StdEncoding.DecodeString(src) + if err != nil { + return "", err + } + cipher, _ := aes.NewCipher(a.key) + decrypted := make([]byte, len(encrypted)) + for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() { + cipher.Decrypt(decrypted[bs:be], encrypted[bs:be]) + } + + trim := 0 + if len(decrypted) > 0 { + trim = len(decrypted) - int(decrypted[len(decrypted)-1]) + } + if trim < 0 { + return "", errors.New("decrypt error") + } + return string(decrypted[:trim]), nil +} + +func generateKey(key []byte) (genKey []byte) { + genKey = make([]byte, 16) + copy(genKey, key) + for i := 16; i < len(key); { + for j := 0; j < 16 && i < len(key); j, i = j+1, i+1 { + genKey[j] ^= key[i] + } + } + return genKey +} diff --git a/pkg/xcrypto/plumcrypto/crypto.go b/pkg/xcrypto/plumcrypto/crypto.go new file mode 100644 index 0000000..05b2b80 --- /dev/null +++ b/pkg/xcrypto/plumcrypto/crypto.go @@ -0,0 +1,138 @@ +package plumcrypto + +import ( + "encoding/json" + "fmt" + "sandc/pkg/bhttp" + "strings" +) + +// PlumResData plum加解密返回数据 +type PlumResData struct { + Data PlumData `json:"data,omitempty"` + Success bool `json:"success,omitempty"` + Error string `json:"error,omitempty"` +} + +// PlumData plum加解密返回数据data +type PlumData struct { + Key string `json:"key,omitempty"` + Val string `json:"val,omitempty"` +} + +// PlumReq plum请求bdoy参数 +type PlumReq struct { + Pkg string `json:"pkg"` + FirebaseAppId string `json:"firebase_app_id"` + Key string `json:"key"` + Val string `json:"val"` + Token string `json:"token"` +} + +// PlumCrypto plum加解密 +type PlumCrypto struct { + encryptUrl string + decryptUrl string + token string +} + +// supportParams 支持的plum参数强制 +const supportParams string = ",_ads_config,_pop_config," + +// NewPlumCrypto +func NewPlumCrypto(plumCryptUrl, token string) *PlumCrypto { + encryptUrl := fmt.Sprintf("%s/encrypt", plumCryptUrl) + decryptUrl := fmt.Sprintf("%s/decrypt", plumCryptUrl) + return &PlumCrypto{encryptUrl: encryptUrl, decryptUrl: decryptUrl, token: token} +} + +// Encrypt 对plum数据进行加密 +func (a *PlumCrypto) Encrypt(appId, packageName, key, value string) (*PlumData, error) { + if !strings.Contains(supportParams, fmt.Sprintf(",%s,", key)) { + return nil, fmt.Errorf("不支持的参数: %s", key) + } + x, err := bhttp.NewBhttpClient() + if err != nil { + return nil, err + } + x.SetHeader("Content-Type", "application/json") + req := PlumReq{ + Pkg: packageName, + FirebaseAppId: appId, + Key: key, + Val: value, + Token: a.token, + } + reqBody, _ := json.Marshal(req) + x.SetBody(reqBody) + fmt.Println("reqBody: ", string(reqBody)) + if a.encryptUrl == "" { + return nil, fmt.Errorf("加密url丢失") + } + fmt.Println("a.encryptUrl: ", a.encryptUrl) + res, err := x.DoPost(a.encryptUrl) + if err != nil { + return nil, err + } + + var info PlumResData + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + fmt.Println(string(res)) + + if !info.Success { + return nil, fmt.Errorf(info.Error) + } + + if info.Data.Val == "" { + return nil, fmt.Errorf("plum加密失败: %s, 请输入有效的Json数据", key) + } + + return &info.Data, nil +} + +// Decrypt 对WJ数据进行加密 +func (a *PlumCrypto) Decrypt(appId, packageName, key, value string) (*PlumData, error) { + x, err := bhttp.NewBhttpClient() + if err != nil { + return nil, err + } + x.SetHeader("Content-Type", "application/json") + req := PlumReq{ + Pkg: packageName, + FirebaseAppId: appId, + Key: key, + Val: value, + Token: a.token, + } + reqBody, _ := json.Marshal(req) + fmt.Println("reqBody: ", string(reqBody)) + x.SetBody(reqBody) + if a.decryptUrl == "" { + return nil, fmt.Errorf("解密url丢失") + } + + res, err := x.DoPost(a.decryptUrl) + if err != nil { + return nil, err + } + + var info PlumResData + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + if !info.Success { + return nil, fmt.Errorf(info.Error) + } + + if !strings.Contains(supportParams, fmt.Sprintf(",%s,", info.Data.Key)) { + return nil, fmt.Errorf("不支持的参数: %s", info.Data.Key) + } + + return &info.Data, nil +} diff --git a/pkg/xcrypto/pscrypto/crypto.go b/pkg/xcrypto/pscrypto/crypto.go new file mode 100644 index 0000000..1ad5a1e --- /dev/null +++ b/pkg/xcrypto/pscrypto/crypto.go @@ -0,0 +1,56 @@ +package pscrypto + +import ( + "crypto/sha256" + "fmt" + "io" + "sort" + "strings" + + "github.com/shopspring/decimal" +) + +// signature based sha256 +func GetSign(m map[string]interface{}, merchantKey string) string { + var w = sha256.New() + _, _ = io.WriteString(w, sortedAndBuild(m)+merchantKey) + return fmt.Sprintf("%x", w.Sum(nil)) +} + +func sortedAndBuild(m map[string]interface{}) string { + var b strings.Builder + l, c := len(m), 0 + keySet := make([]string, 0, l) + for k, v := range m { + keySet = append(keySet, k) + if _, ok := v.(string); ok { + c = len(k) + len(v.(string)) + 2 + } else { + c = len(k) + 10 + 2 + } + } + + b.Grow(c) + sort.Strings(keySet) + for _, k := range keySet { + if v, ok := m[k]; ok { + var str string + if s, okk := v.(string); okk { + str = s + } else if d, okkk := v.(decimal.Decimal); okkk { + str = d.String() + } else { + str = fmt.Sprintf("%v", v) + } + + if len(str) > 0 { + b.WriteString(k) + b.WriteString("=") + b.WriteString(str) + b.WriteString("&") + } + } + } + r := strings.TrimRight(b.String(), "&") + return r +} diff --git a/pkg/xcrypto/rsa.go b/pkg/xcrypto/rsa.go new file mode 100644 index 0000000..fe70e0d --- /dev/null +++ b/pkg/xcrypto/rsa.go @@ -0,0 +1,184 @@ +package xcrypto + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "errors" + "golang.org/x/crypto/pkcs12" + "io/ioutil" + "os" + "strings" +) + +const ( + PRIVATE_PEM_BEGIN = "-----BEGIN RSA PRIVATE KEY-----\n" + PRIVATE_PEM_END = "\n-----END RSA PRIVATE KEY-----" + PUBLIC_PEM_BEGIN = "-----BEGIN PUBLIC KEY-----\n" + PUBLIC_PEM_END = "\n-----END PUBLIC KEY-----" +) + +type PublicStruct struct { + publicKey *rsa.PublicKey +} + +func (p PublicStruct) Verify(sign, content string, hash crypto.Hash) error { + return Verify(sign, content, p.publicKey, hash) +} + +func (p PublicStruct) Encrypt(content string) (string, error) { + b, err := rsa.EncryptPKCS1v15(rand.Reader, p.publicKey, []byte(content)) + if err != nil { + return "", err + } + return base64.StdEncoding.EncodeToString(b), nil +} + +func NewPublicRSA(key string) (p PublicStruct, err error) { + priKey, err := ParsePublicKey(key) + if err != nil { + return + } + p.publicKey = priKey + return +} + +type PrivateStruct struct { + privateKey *rsa.PrivateKey + PublicStruct +} + +func (p PrivateStruct) Sign(content string, hash crypto.Hash) (string, error) { + return Sign(content, p.privateKey, hash) +} + +func (p PrivateStruct) Decrypt(content string) (string, error) { + bcontent, err := base64.StdEncoding.DecodeString(content) + if err != nil { + return "", err + } + b, err := rsa.DecryptPKCS1v15(rand.Reader, p.privateKey, bcontent) + if err != nil { + return "", err + } + return string(b), nil +} + +func NewPrivateRSA(key string) (p PrivateStruct, err error) { + priKey, err := ParsePrivateKey(key) + if err != nil { + return + } + p.privateKey = priKey + p.publicKey = &priKey.PublicKey + return +} + +func NewPrivateRSAPfx(key, password string) (p PrivateStruct, err error) { + priKey, err := ParsePrivateKeyPfx(key, password) + if err != nil { + return + } + p.privateKey = priKey + p.publicKey = &priKey.PublicKey + return +} + +func Sign(signContent string, privateKey *rsa.PrivateKey, hash crypto.Hash) (string, error) { + h := hash.New() + h.Write([]byte(signContent)) + hashed := h.Sum(nil) + + signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, hash, hashed) + if err != nil { + panic(err) + } + return base64.StdEncoding.EncodeToString(signature), nil +} + +func Verify(sign, content string, publicKey *rsa.PublicKey, hash crypto.Hash) error { + h := hash.New() + h.Write([]byte(content)) + hashed := h.Sum(nil) + sig, _ := base64.StdEncoding.DecodeString(sign) + return rsa.VerifyPKCS1v15(publicKey, hash, hashed, sig) +} + +func ParsePrivateKey(privateKey string) (*rsa.PrivateKey, error) { + privateKey = FormatPrivateKey(privateKey) + // 2、解码私钥字节,生成加密对象 + block, _ := pem.Decode([]byte(privateKey)) + if block == nil { + return nil, errors.New("private key info error") + } + // 3、解析DER编码的私钥,生成私钥对象 + priKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return nil, err + } + return priKey, nil +} + +func ParsePublicKey(publicKey string) (*rsa.PublicKey, error) { + publicKey = FormatPublicKey(publicKey) + // 2、解码私钥字节,生成加密对象 + block, _ := pem.Decode([]byte(publicKey)) + if block == nil { + return nil, errors.New("public key info error") + } + // 3、解析DER编码的私钥,生成公钥对象 + pubKey, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return nil, err + } + return pubKey.(*rsa.PublicKey), nil +} + +func FormatPrivateKey(privateKey string) string { + if !strings.HasPrefix(privateKey, PRIVATE_PEM_BEGIN) { + privateKey = PRIVATE_PEM_BEGIN + privateKey + } + if !strings.HasSuffix(privateKey, PRIVATE_PEM_END) { + privateKey = privateKey + PRIVATE_PEM_END + } + return privateKey +} + +func FormatPublicKey(publicKey string) string { + if !strings.HasPrefix(publicKey, PUBLIC_PEM_BEGIN) { + publicKey = PUBLIC_PEM_BEGIN + publicKey + } + if !strings.HasSuffix(publicKey, PUBLIC_PEM_END) { + publicKey = publicKey + PUBLIC_PEM_END + } + return publicKey +} + +func ParsePrivateKeyPfx(privateKeyName, privatePassword string) (*rsa.PrivateKey, error) { + f, err := os.Open(privateKeyName) + if err != nil { + return nil, err + } + + bytes, err := ioutil.ReadAll(f) + if err != nil { + return nil, err + } + // 因为pfx证书公钥和密钥是成对的,所以要先转成pem.Block + blocks, err := pkcs12.ToPEM(bytes, privatePassword) + if err != nil { + return nil, err + } + //if len(blocks) != 2 { + // return nil, errors.New("解密错误") + //} + // 拿到第一个block,用x509解析出私钥(当然公钥也是可以的) + privateKey, err := x509.ParsePKCS1PrivateKey(blocks[0].Bytes) + if err != nil { + return nil, err + } + return privateKey, nil +} diff --git a/pkg/xcrypto/tkcrypto/tkaes.go b/pkg/xcrypto/tkcrypto/tkaes.go new file mode 100644 index 0000000..2d7088b --- /dev/null +++ b/pkg/xcrypto/tkcrypto/tkaes.go @@ -0,0 +1,77 @@ +package tkcrypto + +import ( + "crypto/aes" + "crypto/md5" + "encoding/hex" + "errors" + "strings" +) + +// key字节长度16 +type Aes struct { + key []byte +} + +// NewAes +func NewAes(key string) *Aes { + return &Aes{key: generateKey(key)} +} + +func (a Aes) EncryptECB(src string) string { + origData := []byte(src) + cipher, _ := aes.NewCipher(a.key) + length := (len(origData) + aes.BlockSize) / aes.BlockSize + plain := make([]byte, length*aes.BlockSize) + copy(plain, origData) + pad := byte(len(plain) - len(origData)) + for i := len(origData); i < len(plain); i++ { + plain[i] = pad + } + encrypted := make([]byte, len(plain)) + for bs, be := 0, cipher.BlockSize(); bs <= len(origData); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() { + cipher.Encrypt(encrypted[bs:be], plain[bs:be]) + } + + return hex.EncodeToString(encrypted) +} +func (a Aes) DecryptECB(src string) (string, error) { + encrypted, err := hex.DecodeString(src) + if err != nil { + return "", err + } + cipher, _ := aes.NewCipher(a.key) + decrypted := make([]byte, len(encrypted)) + for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() { + cipher.Decrypt(decrypted[bs:be], encrypted[bs:be]) + } + + trim := 0 + if len(decrypted) > 0 { + trim = len(decrypted) - int(decrypted[len(decrypted)-1]) + } + if trim < 0 { + return "", errors.New("decrypt error") + } + + return string(decrypted[:trim]), nil +} + +func generateKey(key string) (genKey []byte) { + + hash := md5.New() + + hash.Write([]byte(key)) + + md5Hash := hex.EncodeToString(hash.Sum(nil)) + + fileName := strings.ToUpper(md5Hash) + + //需要兼容Nodejs + return generateMd5Key(fileName) +} + +func generateMd5Key(str string) []byte { + checksum := md5.Sum([]byte(str)) + return checksum[0:] +} diff --git a/pkg/xcrypto/wjcrypto/crypto.go b/pkg/xcrypto/wjcrypto/crypto.go new file mode 100644 index 0000000..474df72 --- /dev/null +++ b/pkg/xcrypto/wjcrypto/crypto.go @@ -0,0 +1,110 @@ +package wjcrypto + +import ( + "encoding/json" + "sandc/pkg/bhttp" + + "fmt" +) + +type WJResData struct { + Code int `json:"code,omitempty"` + Data WJData `json:"data,omitempty"` + Err string `json:"err,omitempty"` +} + +type WJData struct { + Key string `json:"key,omitempty"` + Val string `json:"val,omitempty"` +} + +type WJCrypto struct { + encryptUrl string + decryptUrl string +} + +// WJSuccessTag wj加解密成功返回标记 +const WJSuccessTag = "success" + +// NewWJCrypto +func NewWJCrypto(wjCryptUrl string) *WJCrypto { + encryptUrl := fmt.Sprintf("%s/encrypt", wjCryptUrl) + decryptUrl := fmt.Sprintf("%s/decrypt", wjCryptUrl) + return &WJCrypto{encryptUrl: encryptUrl, decryptUrl: decryptUrl} +} + +// Encrypt 对WJ数据进行加密 +func (a *WJCrypto) Encrypt(projectId, key, value string) (*WJData, error) { + x, err := bhttp.NewBhttpClient(bhttp.WithTimeout(2), bhttp.WithRetry(10)) + if err != nil { + return nil, err + } + x.SetHeader("Content-Type", "multipart/form-data") + x.SetParam("project_name", projectId) + x.SetParam("key", key) + x.SetParam("val", value) + + if a.encryptUrl == "" { + return nil, fmt.Errorf("加密url丢失") + } + + res, err := x.DoPost(a.encryptUrl) + + if err != nil { + return nil, fmt.Errorf("wj超时: %w", err) + } + + var info WJResData + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + if info.Err != WJSuccessTag { + return nil, fmt.Errorf(info.Err) + } + + if info.Data.Key == "" || info.Data.Val == "" { + return nil, fmt.Errorf("解密失败,数据为空") + } + + return &info.Data, nil +} + +// Decrypt 对WJ数据进行解密 +func (a *WJCrypto) Decrypt(projectId, key, value string) (*WJData, error) { + x, err := bhttp.NewBhttpClient(bhttp.WithTimeout(2), bhttp.WithRetry(10)) + if err != nil { + return nil, err + } + x.SetHeader("Content-Type", "multipart/form-data") + x.SetParam("project_name", projectId) + x.SetParam("key", key) + x.SetParam("val", value) + + if a.decryptUrl == "" { + return nil, fmt.Errorf("解密url丢失") + } + + res, err := x.DoPost(a.decryptUrl) + + if err != nil { + return nil, fmt.Errorf("wj超时: %w", err) + } + + var info WJResData + err = json.Unmarshal(res, &info) + if err != nil { + return nil, err + } + + if info.Err != WJSuccessTag { + return nil, fmt.Errorf(info.Err) + } + + if info.Data.Key == "" || info.Data.Val == "" { + return nil, fmt.Errorf("解密失败,数据为空") + } + + return &info.Data, nil +} diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..91ae67c --- /dev/null +++ b/readme.txt @@ -0,0 +1,8 @@ + +v4 +2025.4.30 + +2025.2.20 +v3 + + diff --git a/third_party/README.md b/third_party/README.md new file mode 100644 index 0000000..005faa2 --- /dev/null +++ b/third_party/README.md @@ -0,0 +1 @@ +# third_party diff --git a/third_party/errors/errors.proto b/third_party/errors/errors.proto new file mode 100644 index 0000000..0963d91 --- /dev/null +++ b/third_party/errors/errors.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +package errors; + +option go_package = "github.com/go-kratos/kratos/v2/errors;errors"; +option java_multiple_files = true; +option java_package = "com.github.kratos.errors"; +option objc_class_prefix = "KratosErrors"; + +import "google/protobuf/descriptor.proto"; + +extend google.protobuf.EnumOptions { + int32 default_code = 1108; +} + +extend google.protobuf.EnumValueOptions { + int32 code = 1109; +} \ No newline at end of file diff --git a/third_party/google/api/annotations.proto b/third_party/google/api/annotations.proto new file mode 100644 index 0000000..85c361b --- /dev/null +++ b/third_party/google/api/annotations.proto @@ -0,0 +1,31 @@ +// Copyright (c) 2015, Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/api/http.proto"; +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "AnnotationsProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.MethodOptions { + // See `HttpRule`. + HttpRule http = 72295728; +} diff --git a/third_party/google/api/http.proto b/third_party/google/api/http.proto new file mode 100644 index 0000000..69460cf --- /dev/null +++ b/third_party/google/api/http.proto @@ -0,0 +1,375 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "HttpProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Defines the HTTP configuration for an API service. It contains a list of +// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method +// to one or more HTTP REST API methods. +message Http { + // A list of HTTP configuration rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated HttpRule rules = 1; + + // When set to true, URL path parameters will be fully URI-decoded except in + // cases of single segment matches in reserved expansion, where "%2F" will be + // left encoded. + // + // The default behavior is to not decode RFC 6570 reserved characters in multi + // segment matches. + bool fully_decode_reserved_expansion = 2; +} + +// # gRPC Transcoding +// +// gRPC Transcoding is a feature for mapping between a gRPC method and one or +// more HTTP REST endpoints. It allows developers to build a single API service +// that supports both gRPC APIs and REST APIs. Many systems, including [Google +// APIs](https://github.com/googleapis/googleapis), +// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC +// Gateway](https://github.com/grpc-ecosystem/grpc-gateway), +// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature +// and use it for large scale production services. +// +// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies +// how different portions of the gRPC request message are mapped to the URL +// path, URL query parameters, and HTTP request body. It also controls how the +// gRPC response message is mapped to the HTTP response body. `HttpRule` is +// typically specified as an `google.api.http` annotation on the gRPC method. +// +// Each mapping specifies a URL path template and an HTTP method. The path +// template may refer to one or more fields in the gRPC request message, as long +// as each field is a non-repeated field with a primitive (non-message) type. +// The path template controls how fields of the request message are mapped to +// the URL path. +// +// Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/{name=messages/*}" +// }; +// } +// } +// message GetMessageRequest { +// string name = 1; // Mapped to URL path. +// } +// message Message { +// string text = 1; // The resource content. +// } +// +// This enables an HTTP REST to gRPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(name: "messages/123456")` +// +// Any fields in the request message which are not bound by the path template +// automatically become HTTP query parameters if there is no HTTP request body. +// For example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get:"/v1/messages/{message_id}" +// }; +// } +// } +// message GetMessageRequest { +// message SubMessage { +// string subfield = 1; +// } +// string message_id = 1; // Mapped to URL path. +// int64 revision = 2; // Mapped to URL query parameter `revision`. +// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`. +// } +// +// This enables a HTTP JSON to RPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456?revision=2&sub.subfield=foo` | +// `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: +// "foo"))` +// +// Note that fields which are mapped to URL query parameters must have a +// primitive type or a repeated primitive type or a non-repeated message type. +// In the case of a repeated type, the parameter can be repeated in the URL +// as `...?param=A¶m=B`. In the case of a message type, each field of the +// message is mapped to a separate parameter, such as +// `...?foo.a=A&foo.b=B&foo.c=C`. +// +// For HTTP methods that allow a request body, the `body` field +// specifies the mapping. Consider a REST update method on the +// message resource collection: +// +// service Messaging { +// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "message" +// }; +// } +// } +// message UpdateMessageRequest { +// string message_id = 1; // mapped to the URL +// Message message = 2; // mapped to the body +// } +// +// The following HTTP JSON to RPC mapping is enabled, where the +// representation of the JSON in the request body is determined by +// protos JSON encoding: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" message { text: "Hi!" })` +// +// The special name `*` can be used in the body mapping to define that +// every field not bound by the path template should be mapped to the +// request body. This enables the following alternative definition of +// the update method: +// +// service Messaging { +// rpc UpdateMessage(Message) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "*" +// }; +// } +// } +// message Message { +// string message_id = 1; +// string text = 2; +// } +// +// +// The following HTTP JSON to RPC mapping is enabled: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" text: "Hi!")` +// +// Note that when using `*` in the body mapping, it is not possible to +// have HTTP parameters, as all fields not bound by the path end in +// the body. This makes this option more rarely used in practice when +// defining REST APIs. The common usage of `*` is in custom methods +// which don't use the URL at all for transferring data. +// +// It is possible to define multiple HTTP methods for one RPC by using +// the `additional_bindings` option. Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/messages/{message_id}" +// additional_bindings { +// get: "/v1/users/{user_id}/messages/{message_id}" +// } +// }; +// } +// } +// message GetMessageRequest { +// string message_id = 1; +// string user_id = 2; +// } +// +// This enables the following two alternative HTTP JSON to RPC mappings: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")` +// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: +// "123456")` +// +// ## Rules for HTTP mapping +// +// 1. Leaf request fields (recursive expansion nested messages in the request +// message) are classified into three categories: +// - Fields referred by the path template. They are passed via the URL path. +// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They are passed via the HTTP +// request body. +// - All other fields are passed via the URL query parameters, and the +// parameter name is the field path in the request message. A repeated +// field can be represented as multiple query parameters under the same +// name. +// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL query parameter, all fields +// are passed via URL path and HTTP request body. +// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP request body, all +// fields are passed via URL path and URL query parameters. +// +// ### Path template syntax +// +// Template = "/" Segments [ Verb ] ; +// Segments = Segment { "/" Segment } ; +// Segment = "*" | "**" | LITERAL | Variable ; +// Variable = "{" FieldPath [ "=" Segments ] "}" ; +// FieldPath = IDENT { "." IDENT } ; +// Verb = ":" LITERAL ; +// +// The syntax `*` matches a single URL path segment. The syntax `**` matches +// zero or more URL path segments, which must be the last part of the URL path +// except the `Verb`. +// +// The syntax `Variable` matches part of the URL path as specified by its +// template. A variable template must not contain other variables. If a variable +// matches a single path segment, its template may be omitted, e.g. `{var}` +// is equivalent to `{var=*}`. +// +// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL` +// contains any reserved character, such characters should be percent-encoded +// before the matching. +// +// If a variable contains exactly one path segment, such as `"{var}"` or +// `"{var=*}"`, when such a variable is expanded into a URL path on the client +// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The +// server side does the reverse decoding. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{var}`. +// +// If a variable contains multiple path segments, such as `"{var=foo/*}"` +// or `"{var=**}"`, when such a variable is expanded into a URL path on the +// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. +// The server side does the reverse decoding, except "%2F" and "%2f" are left +// unchanged. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{+var}`. +// +// ## Using gRPC API Service Configuration +// +// gRPC API Service Configuration (service config) is a configuration language +// for configuring a gRPC service to become a user-facing product. The +// service config is simply the YAML representation of the `google.api.Service` +// proto message. +// +// As an alternative to annotating your proto file, you can configure gRPC +// transcoding in your service config YAML files. You do this by specifying a +// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same +// effect as the proto annotation. This can be particularly useful if you +// have a proto that is reused in multiple services. Note that any transcoding +// specified in the service config will override any matching transcoding +// configuration in the proto. +// +// Example: +// +// http: +// rules: +// # Selects a gRPC method and applies HttpRule to it. +// - selector: example.v1.Messaging.GetMessage +// get: /v1/messages/{message_id}/{sub.subfield} +// +// ## Special notes +// +// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the +// proto to JSON conversion must follow the [proto3 +// specification](https://developers.google.com/protocol-buffers/docs/proto3#json). +// +// While the single segment variable follows the semantics of +// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String +// Expansion, the multi segment variable **does not** follow RFC 6570 Section +// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion +// does not expand special characters like `?` and `#`, which would lead +// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding +// for multi segment variables. +// +// The path variables **must not** refer to any repeated or mapped field, +// because client libraries are not capable of handling such variable expansion. +// +// The path variables **must not** capture the leading "/" character. The reason +// is that the most common use case "{var}" does not capture the leading "/" +// character. For consistency, all path variables must share the same behavior. +// +// Repeated message fields must not be mapped to URL query parameters, because +// no client library can support such complicated mapping. +// +// If an API needs to use a JSON array for request or response body, it can map +// the request or response body to a repeated field. However, some gRPC +// Transcoding implementations may not support this feature. +message HttpRule { + // Selects a method to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax details. + string selector = 1; + + // Determines the URL pattern is matched by this rules. This pattern can be + // used with any of the {get|put|post|delete|patch} methods. A custom method + // can be defined using the 'custom' field. + oneof pattern { + // Maps to HTTP GET. Used for listing and getting information about + // resources. + string get = 2; + + // Maps to HTTP PUT. Used for replacing a resource. + string put = 3; + + // Maps to HTTP POST. Used for creating a resource or performing an action. + string post = 4; + + // Maps to HTTP DELETE. Used for deleting a resource. + string delete = 5; + + // Maps to HTTP PATCH. Used for updating a resource. + string patch = 6; + + // The custom pattern is used for specifying an HTTP method that is not + // included in the `pattern` field, such as HEAD, or "*" to leave the + // HTTP method unspecified for this rule. The wild-card rule is useful + // for services that provide content to Web (HTML) clients. + CustomHttpPattern custom = 8; + } + + // The name of the request field whose value is mapped to the HTTP request + // body, or `*` for mapping all request fields not captured by the path + // pattern to the HTTP body, or omitted for not having any HTTP request body. + // + // NOTE: the referred field must be present at the top-level of the request + // message type. + string body = 7; + + // Optional. The name of the response field whose value is mapped to the HTTP + // response body. When omitted, the entire response message will be used + // as the HTTP response body. + // + // NOTE: The referred field must be present at the top-level of the response + // message type. + string response_body = 12; + + // Additional HTTP bindings for the selector. Nested bindings must + // not contain an `additional_bindings` field themselves (that is, + // the nesting may only be one level deep). + repeated HttpRule additional_bindings = 11; +} + +// A custom pattern is used for defining custom HTTP verb. +message CustomHttpPattern { + // The name of this custom HTTP verb. + string kind = 1; + + // The path matched by this custom verb. + string path = 2; +} diff --git a/third_party/google/api/httpbody.proto b/third_party/google/api/httpbody.proto new file mode 100644 index 0000000..1a5bb78 --- /dev/null +++ b/third_party/google/api/httpbody.proto @@ -0,0 +1,77 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/protobuf/any.proto"; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/api/httpbody;httpbody"; +option java_multiple_files = true; +option java_outer_classname = "HttpBodyProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Message that represents an arbitrary HTTP body. It should only be used for +// payload formats that can't be represented as JSON, such as raw binary or +// an HTML page. +// +// +// This message can be used both in streaming and non-streaming API methods in +// the request as well as the response. +// +// It can be used as a top-level request field, which is convenient if one +// wants to extract parameters from either the URL or HTTP template into the +// request fields and also want access to the raw HTTP body. +// +// Example: +// +// message GetResourceRequest { +// // A unique request id. +// string request_id = 1; +// +// // The raw HTTP body is bound to this field. +// google.api.HttpBody http_body = 2; +// } +// +// service ResourceService { +// rpc GetResource(GetResourceRequest) returns (google.api.HttpBody); +// rpc UpdateResource(google.api.HttpBody) returns +// (google.protobuf.Empty); +// } +// +// Example with streaming methods: +// +// service CaldavService { +// rpc GetCalendar(stream google.api.HttpBody) +// returns (stream google.api.HttpBody); +// rpc UpdateCalendar(stream google.api.HttpBody) +// returns (stream google.api.HttpBody); +// } +// +// Use of this type only changes how the request and response bodies are +// handled, all other features will continue to work unchanged. +message HttpBody { + // The HTTP Content-Type header value specifying the content type of the body. + string content_type = 1; + + // The HTTP request/response body as raw binary. + bytes data = 2; + + // Application specific response metadata. Must be set in the first response + // for streaming APIs. + repeated google.protobuf.Any extensions = 3; +} diff --git a/third_party/google/protobuf/duration.proto b/third_party/google/protobuf/duration.proto new file mode 100644 index 0000000..41f40c2 --- /dev/null +++ b/third_party/google/protobuf/duration.proto @@ -0,0 +1,115 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/protobuf/types/known/durationpb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "DurationProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; + +// A Duration represents a signed, fixed-length span of time represented +// as a count of seconds and fractions of seconds at nanosecond +// resolution. It is independent of any calendar and concepts like "day" +// or "month". It is related to Timestamp in that the difference between +// two Timestamp values is a Duration and it can be added or subtracted +// from a Timestamp. Range is approximately +-10,000 years. +// +// # Examples +// +// Example 1: Compute Duration from two Timestamps in pseudo code. +// +// Timestamp start = ...; +// Timestamp end = ...; +// Duration duration = ...; +// +// duration.seconds = end.seconds - start.seconds; +// duration.nanos = end.nanos - start.nanos; +// +// if (duration.seconds < 0 && duration.nanos > 0) { +// duration.seconds += 1; +// duration.nanos -= 1000000000; +// } else if (duration.seconds > 0 && duration.nanos < 0) { +// duration.seconds -= 1; +// duration.nanos += 1000000000; +// } +// +// Example 2: Compute Timestamp from Timestamp + Duration in pseudo code. +// +// Timestamp start = ...; +// Duration duration = ...; +// Timestamp end = ...; +// +// end.seconds = start.seconds + duration.seconds; +// end.nanos = start.nanos + duration.nanos; +// +// if (end.nanos < 0) { +// end.seconds -= 1; +// end.nanos += 1000000000; +// } else if (end.nanos >= 1000000000) { +// end.seconds += 1; +// end.nanos -= 1000000000; +// } +// +// Example 3: Compute Duration from datetime.timedelta in Python. +// +// td = datetime.timedelta(days=3, minutes=10) +// duration = Duration() +// duration.FromTimedelta(td) +// +// # JSON Mapping +// +// In JSON format, the Duration type is encoded as a string rather than an +// object, where the string ends in the suffix "s" (indicating seconds) and +// is preceded by the number of seconds, with nanoseconds expressed as +// fractional seconds. For example, 3 seconds with 0 nanoseconds should be +// encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should +// be expressed in JSON format as "3.000000001s", and 3 seconds and 1 +// microsecond should be expressed in JSON format as "3.000001s". +// +message Duration { + // Signed seconds of the span of time. Must be from -315,576,000,000 + // to +315,576,000,000 inclusive. Note: these bounds are computed from: + // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years + int64 seconds = 1; + + // Signed fractions of a second at nanosecond resolution of the span + // of time. Durations less than one second are represented with a 0 + // `seconds` field and a positive or negative `nanos` field. For durations + // of one second or more, a non-zero value for the `nanos` field must be + // of the same sign as the `seconds` field. Must be from -999,999,999 + // to +999,999,999 inclusive. + int32 nanos = 2; +} diff --git a/third_party/google/protobuf/timestamp.proto b/third_party/google/protobuf/timestamp.proto new file mode 100644 index 0000000..fd0bc07 --- /dev/null +++ b/third_party/google/protobuf/timestamp.proto @@ -0,0 +1,144 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/protobuf/types/known/timestamppb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "TimestampProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; + +// A Timestamp represents a point in time independent of any time zone or local +// calendar, encoded as a count of seconds and fractions of seconds at +// nanosecond resolution. The count is relative to an epoch at UTC midnight on +// January 1, 1970, in the proleptic Gregorian calendar which extends the +// Gregorian calendar backwards to year one. +// +// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap +// second table is needed for interpretation, using a [24-hour linear +// smear](https://developers.google.com/time/smear). +// +// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By +// restricting to that range, we ensure that we can convert to and from [RFC +// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. +// +// # Examples +// +// Example 1: Compute Timestamp from POSIX `time()`. +// +// Timestamp timestamp; +// timestamp.set_seconds(time(NULL)); +// timestamp.set_nanos(0); +// +// Example 2: Compute Timestamp from POSIX `gettimeofday()`. +// +// struct timeval tv; +// gettimeofday(&tv, NULL); +// +// Timestamp timestamp; +// timestamp.set_seconds(tv.tv_sec); +// timestamp.set_nanos(tv.tv_usec * 1000); +// +// Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. +// +// FILETIME ft; +// GetSystemTimeAsFileTime(&ft); +// UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; +// +// // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z +// // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. +// Timestamp timestamp; +// timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); +// timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); +// +// Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. +// +// long millis = System.currentTimeMillis(); +// +// Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) +// .setNanos((int) ((millis % 1000) * 1000000)).build(); +// +// Example 5: Compute Timestamp from Java `Instant.now()`. +// +// Instant now = Instant.now(); +// +// Timestamp timestamp = +// Timestamp.newBuilder().setSeconds(now.getEpochSecond()) +// .setNanos(now.getNano()).build(); +// +// Example 6: Compute Timestamp from current time in Python. +// +// timestamp = Timestamp() +// timestamp.GetCurrentTime() +// +// # JSON Mapping +// +// In JSON format, the Timestamp type is encoded as a string in the +// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the +// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" +// where {year} is always expressed using four digits while {month}, {day}, +// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional +// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), +// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone +// is required. A proto3 JSON serializer should always use UTC (as indicated by +// "Z") when printing the Timestamp type and a proto3 JSON parser should be +// able to accept both UTC and other timezones (as indicated by an offset). +// +// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past +// 01:30 UTC on January 15, 2017. +// +// In JavaScript, one can convert a Date object to this format using the +// standard +// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) +// method. In Python, a standard `datetime.datetime` object can be converted +// to this format using +// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with +// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use +// the Joda Time's [`ISODateTimeFormat.dateTime()`]( +// http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime() +// ) to obtain a formatter capable of generating timestamps in this format. +// +message Timestamp { + // Represents seconds of UTC time since Unix epoch + // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + // 9999-12-31T23:59:59Z inclusive. + int64 seconds = 1; + + // Non-negative fractions of a second at nanosecond resolution. Negative + // second values with fractions must still have non-negative nanos values + // that count forward in time. Must be from 0 to 999,999,999 + // inclusive. + int32 nanos = 2; +} diff --git a/third_party/protoc-gen-openapiv2/options/annotations.proto b/third_party/protoc-gen-openapiv2/options/annotations.proto new file mode 100644 index 0000000..9d4f4c9 --- /dev/null +++ b/third_party/protoc-gen-openapiv2/options/annotations.proto @@ -0,0 +1,44 @@ +syntax = "proto3"; + +package grpc.gateway.protoc_gen_openapiv2.options; + +import "google/protobuf/descriptor.proto"; +import "protoc-gen-openapiv2/options/openapiv2.proto"; + +option go_package = "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"; + +extend google.protobuf.FileOptions { + // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project. + // + // All IDs are the same, as assigned. It is okay that they are the same, as they extend + // different descriptor messages. + Swagger openapiv2_swagger = 1042; +} +extend google.protobuf.MethodOptions { + // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project. + // + // All IDs are the same, as assigned. It is okay that they are the same, as they extend + // different descriptor messages. + Operation openapiv2_operation = 1042; +} +extend google.protobuf.MessageOptions { + // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project. + // + // All IDs are the same, as assigned. It is okay that they are the same, as they extend + // different descriptor messages. + Schema openapiv2_schema = 1042; +} +extend google.protobuf.ServiceOptions { + // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project. + // + // All IDs are the same, as assigned. It is okay that they are the same, as they extend + // different descriptor messages. + Tag openapiv2_tag = 1042; +} +extend google.protobuf.FieldOptions { + // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project. + // + // All IDs are the same, as assigned. It is okay that they are the same, as they extend + // different descriptor messages. + JSONSchema openapiv2_field = 1042; +} \ No newline at end of file diff --git a/third_party/protoc-gen-openapiv2/options/openapiv2.proto b/third_party/protoc-gen-openapiv2/options/openapiv2.proto new file mode 100644 index 0000000..c9aae5c --- /dev/null +++ b/third_party/protoc-gen-openapiv2/options/openapiv2.proto @@ -0,0 +1,720 @@ +syntax = "proto3"; + +package grpc.gateway.protoc_gen_openapiv2.options; + +import "google/protobuf/struct.proto"; + +option go_package = "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"; + +// Scheme describes the schemes supported by the OpenAPI Swagger +// and Operation objects. +enum Scheme { + UNKNOWN = 0; + HTTP = 1; + HTTPS = 2; + WS = 3; + WSS = 4; +} + +// `Swagger` is a representation of OpenAPI v2 specification's Swagger object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#swaggerObject +// +// Example: +// +// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { +// info: { +// title: "Echo API"; +// version: "1.0"; +// description: ""; +// contact: { +// name: "gRPC-Gateway project"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway"; +// email: "none@example.com"; +// }; +// license: { +// name: "BSD 3-Clause License"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway/blob/main/LICENSE.txt"; +// }; +// }; +// schemes: HTTPS; +// consumes: "application/json"; +// produces: "application/json"; +// }; +// +message Swagger { + // Specifies the OpenAPI Specification version being used. It can be + // used by the OpenAPI UI and other clients to interpret the API listing. The + // value MUST be "2.0". + string swagger = 1; + // Provides metadata about the API. The metadata can be used by the + // clients if needed. + Info info = 2; + // The host (name or ip) serving the API. This MUST be the host only and does + // not include the scheme nor sub-paths. It MAY include a port. If the host is + // not included, the host serving the documentation is to be used (including + // the port). The host does not support path templating. + string host = 3; + // The base path on which the API is served, which is relative to the host. If + // it is not included, the API is served directly under the host. The value + // MUST start with a leading slash (/). The basePath does not support path + // templating. + // Note that using `base_path` does not change the endpoint paths that are + // generated in the resulting OpenAPI file. If you wish to use `base_path` + // with relatively generated OpenAPI paths, the `base_path` prefix must be + // manually removed from your `google.api.http` paths and your code changed to + // serve the API from the `base_path`. + string base_path = 4; + // The transfer protocol of the API. Values MUST be from the list: "http", + // "https", "ws", "wss". If the schemes is not included, the default scheme to + // be used is the one used to access the OpenAPI definition itself. + repeated Scheme schemes = 5; + // A list of MIME types the APIs can consume. This is global to all APIs but + // can be overridden on specific API calls. Value MUST be as described under + // Mime Types. + repeated string consumes = 6; + // A list of MIME types the APIs can produce. This is global to all APIs but + // can be overridden on specific API calls. Value MUST be as described under + // Mime Types. + repeated string produces = 7; + // field 8 is reserved for 'paths'. + reserved 8; + // field 9 is reserved for 'definitions', which at this time are already + // exposed as and customizable as proto messages. + reserved 9; + // An object to hold responses that can be used across operations. This + // property does not define global responses for all operations. + map responses = 10; + // Security scheme definitions that can be used across the specification. + SecurityDefinitions security_definitions = 11; + // A declaration of which security schemes are applied for the API as a whole. + // The list of values describes alternative security schemes that can be used + // (that is, there is a logical OR between the security requirements). + // Individual operations can override this definition. + repeated SecurityRequirement security = 12; + // A list of tags for API documentation control. Tags can be used for logical + // grouping of operations by resources or any other qualifier. + repeated Tag tags = 13; + // Additional external documentation. + ExternalDocumentation external_docs = 14; + // Custom properties that start with "x-" such as "x-foo" used to describe + // extra functionality that is not covered by the standard OpenAPI Specification. + // See: https://swagger.io/docs/specification/2-0/swagger-extensions/ + map extensions = 15; +} + +// `Operation` is a representation of OpenAPI v2 specification's Operation object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#operationObject +// +// Example: +// +// service EchoService { +// rpc Echo(SimpleMessage) returns (SimpleMessage) { +// option (google.api.http) = { +// get: "/v1/example/echo/{id}" +// }; +// +// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { +// summary: "Get a message."; +// operation_id: "getMessage"; +// tags: "echo"; +// responses: { +// key: "200" +// value: { +// description: "OK"; +// } +// } +// }; +// } +// } +message Operation { + // A list of tags for API documentation control. Tags can be used for logical + // grouping of operations by resources or any other qualifier. + repeated string tags = 1; + // A short summary of what the operation does. For maximum readability in the + // swagger-ui, this field SHOULD be less than 120 characters. + string summary = 2; + // A verbose explanation of the operation behavior. GFM syntax can be used for + // rich text representation. + string description = 3; + // Additional external documentation for this operation. + ExternalDocumentation external_docs = 4; + // Unique string used to identify the operation. The id MUST be unique among + // all operations described in the API. Tools and libraries MAY use the + // operationId to uniquely identify an operation, therefore, it is recommended + // to follow common programming naming conventions. + string operation_id = 5; + // A list of MIME types the operation can consume. This overrides the consumes + // definition at the OpenAPI Object. An empty value MAY be used to clear the + // global definition. Value MUST be as described under Mime Types. + repeated string consumes = 6; + // A list of MIME types the operation can produce. This overrides the produces + // definition at the OpenAPI Object. An empty value MAY be used to clear the + // global definition. Value MUST be as described under Mime Types. + repeated string produces = 7; + // field 8 is reserved for 'parameters'. + reserved 8; + // The list of possible responses as they are returned from executing this + // operation. + map responses = 9; + // The transfer protocol for the operation. Values MUST be from the list: + // "http", "https", "ws", "wss". The value overrides the OpenAPI Object + // schemes definition. + repeated Scheme schemes = 10; + // Declares this operation to be deprecated. Usage of the declared operation + // should be refrained. Default value is false. + bool deprecated = 11; + // A declaration of which security schemes are applied for this operation. The + // list of values describes alternative security schemes that can be used + // (that is, there is a logical OR between the security requirements). This + // definition overrides any declared top-level security. To remove a top-level + // security declaration, an empty array can be used. + repeated SecurityRequirement security = 12; + // Custom properties that start with "x-" such as "x-foo" used to describe + // extra functionality that is not covered by the standard OpenAPI Specification. + // See: https://swagger.io/docs/specification/2-0/swagger-extensions/ + map extensions = 13; + // Custom parameters such as HTTP request headers. + // See: https://swagger.io/docs/specification/2-0/describing-parameters/ + // and https://swagger.io/specification/v2/#parameter-object. + Parameters parameters = 14; +} + +// `Parameters` is a representation of OpenAPI v2 specification's parameters object. +// Note: This technically breaks compatibility with the OpenAPI 2 definition structure as we only +// allow header parameters to be set here since we do not want users specifying custom non-header +// parameters beyond those inferred from the Protobuf schema. +// See: https://swagger.io/specification/v2/#parameter-object +message Parameters { + // `Headers` is one or more HTTP header parameter. + // See: https://swagger.io/docs/specification/2-0/describing-parameters/#header-parameters + repeated HeaderParameter headers = 1; +} + +// `HeaderParameter` a HTTP header parameter. +// See: https://swagger.io/specification/v2/#parameter-object +message HeaderParameter { + // `Type` is a a supported HTTP header type. + // See https://swagger.io/specification/v2/#parameterType. + enum Type { + UNKNOWN = 0; + STRING = 1; + NUMBER = 2; + INTEGER = 3; + BOOLEAN = 4; + } + + // `Name` is the header name. + string name = 1; + // `Description` is a short description of the header. + string description = 2; + // `Type` is the type of the object. The value MUST be one of "string", "number", "integer", or "boolean". The "array" type is not supported. + // See: https://swagger.io/specification/v2/#parameterType. + Type type = 3; + // `Format` The extending format for the previously mentioned type. + string format = 4; + // `Required` indicates if the header is optional + bool required = 5; + // field 6 is reserved for 'items', but in OpenAPI-specific way. + reserved 6; + // field 7 is reserved `Collection Format`. Determines the format of the array if type array is used. + reserved 7; +} + +// `Header` is a representation of OpenAPI v2 specification's Header object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#headerObject +// +message Header { + // `Description` is a short description of the header. + string description = 1; + // The type of the object. The value MUST be one of "string", "number", "integer", or "boolean". The "array" type is not supported. + string type = 2; + // `Format` The extending format for the previously mentioned type. + string format = 3; + // field 4 is reserved for 'items', but in OpenAPI-specific way. + reserved 4; + // field 5 is reserved `Collection Format` Determines the format of the array if type array is used. + reserved 5; + // `Default` Declares the value of the header that the server will use if none is provided. + // See: https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-6.2. + // Unlike JSON Schema this value MUST conform to the defined type for the header. + string default = 6; + // field 7 is reserved for 'maximum'. + reserved 7; + // field 8 is reserved for 'exclusiveMaximum'. + reserved 8; + // field 9 is reserved for 'minimum'. + reserved 9; + // field 10 is reserved for 'exclusiveMinimum'. + reserved 10; + // field 11 is reserved for 'maxLength'. + reserved 11; + // field 12 is reserved for 'minLength'. + reserved 12; + // 'Pattern' See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.2.3. + string pattern = 13; + // field 14 is reserved for 'maxItems'. + reserved 14; + // field 15 is reserved for 'minItems'. + reserved 15; + // field 16 is reserved for 'uniqueItems'. + reserved 16; + // field 17 is reserved for 'enum'. + reserved 17; + // field 18 is reserved for 'multipleOf'. + reserved 18; +} + +// `Response` is a representation of OpenAPI v2 specification's Response object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#responseObject +// +message Response { + // `Description` is a short description of the response. + // GFM syntax can be used for rich text representation. + string description = 1; + // `Schema` optionally defines the structure of the response. + // If `Schema` is not provided, it means there is no content to the response. + Schema schema = 2; + // `Headers` A list of headers that are sent with the response. + // `Header` name is expected to be a string in the canonical format of the MIME header key + // See: https://golang.org/pkg/net/textproto/#CanonicalMIMEHeaderKey + map headers = 3; + // `Examples` gives per-mimetype response examples. + // See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#example-object + map examples = 4; + // Custom properties that start with "x-" such as "x-foo" used to describe + // extra functionality that is not covered by the standard OpenAPI Specification. + // See: https://swagger.io/docs/specification/2-0/swagger-extensions/ + map extensions = 5; +} + +// `Info` is a representation of OpenAPI v2 specification's Info object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#infoObject +// +// Example: +// +// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { +// info: { +// title: "Echo API"; +// version: "1.0"; +// description: ""; +// contact: { +// name: "gRPC-Gateway project"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway"; +// email: "none@example.com"; +// }; +// license: { +// name: "BSD 3-Clause License"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway/blob/main/LICENSE.txt"; +// }; +// }; +// ... +// }; +// +message Info { + // The title of the application. + string title = 1; + // A short description of the application. GFM syntax can be used for rich + // text representation. + string description = 2; + // The Terms of Service for the API. + string terms_of_service = 3; + // The contact information for the exposed API. + Contact contact = 4; + // The license information for the exposed API. + License license = 5; + // Provides the version of the application API (not to be confused + // with the specification version). + string version = 6; + // Custom properties that start with "x-" such as "x-foo" used to describe + // extra functionality that is not covered by the standard OpenAPI Specification. + // See: https://swagger.io/docs/specification/2-0/swagger-extensions/ + map extensions = 7; +} + +// `Contact` is a representation of OpenAPI v2 specification's Contact object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#contactObject +// +// Example: +// +// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { +// info: { +// ... +// contact: { +// name: "gRPC-Gateway project"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway"; +// email: "none@example.com"; +// }; +// ... +// }; +// ... +// }; +// +message Contact { + // The identifying name of the contact person/organization. + string name = 1; + // The URL pointing to the contact information. MUST be in the format of a + // URL. + string url = 2; + // The email address of the contact person/organization. MUST be in the format + // of an email address. + string email = 3; +} + +// `License` is a representation of OpenAPI v2 specification's License object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#licenseObject +// +// Example: +// +// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { +// info: { +// ... +// license: { +// name: "BSD 3-Clause License"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway/blob/main/LICENSE.txt"; +// }; +// ... +// }; +// ... +// }; +// +message License { + // The license name used for the API. + string name = 1; + // A URL to the license used for the API. MUST be in the format of a URL. + string url = 2; +} + +// `ExternalDocumentation` is a representation of OpenAPI v2 specification's +// ExternalDocumentation object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#externalDocumentationObject +// +// Example: +// +// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { +// ... +// external_docs: { +// description: "More about gRPC-Gateway"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway"; +// } +// ... +// }; +// +message ExternalDocumentation { + // A short description of the target documentation. GFM syntax can be used for + // rich text representation. + string description = 1; + // The URL for the target documentation. Value MUST be in the format + // of a URL. + string url = 2; +} + +// `Schema` is a representation of OpenAPI v2 specification's Schema object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject +// +message Schema { + JSONSchema json_schema = 1; + // Adds support for polymorphism. The discriminator is the schema property + // name that is used to differentiate between other schema that inherit this + // schema. The property name used MUST be defined at this schema and it MUST + // be in the required property list. When used, the value MUST be the name of + // this schema or any schema that inherits it. + string discriminator = 2; + // Relevant only for Schema "properties" definitions. Declares the property as + // "read only". This means that it MAY be sent as part of a response but MUST + // NOT be sent as part of the request. Properties marked as readOnly being + // true SHOULD NOT be in the required list of the defined schema. Default + // value is false. + bool read_only = 3; + // field 4 is reserved for 'xml'. + reserved 4; + // Additional external documentation for this schema. + ExternalDocumentation external_docs = 5; + // A free-form property to include an example of an instance for this schema in JSON. + // This is copied verbatim to the output. + string example = 6; +} + +// `JSONSchema` represents properties from JSON Schema taken, and as used, in +// the OpenAPI v2 spec. +// +// This includes changes made by OpenAPI v2. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject +// +// See also: https://cswr.github.io/JsonSchema/spec/basic_types/, +// https://github.com/json-schema-org/json-schema-spec/blob/master/schema.json +// +// Example: +// +// message SimpleMessage { +// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = { +// json_schema: { +// title: "SimpleMessage" +// description: "A simple message." +// required: ["id"] +// } +// }; +// +// // Id represents the message identifier. +// string id = 1; [ +// (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { +// description: "The unique identifier of the simple message." +// }]; +// } +// +message JSONSchema { + // field 1 is reserved for '$id', omitted from OpenAPI v2. + reserved 1; + // field 2 is reserved for '$schema', omitted from OpenAPI v2. + reserved 2; + // Ref is used to define an external reference to include in the message. + // This could be a fully qualified proto message reference, and that type must + // be imported into the protofile. If no message is identified, the Ref will + // be used verbatim in the output. + // For example: + // `ref: ".google.protobuf.Timestamp"`. + string ref = 3; + // field 4 is reserved for '$comment', omitted from OpenAPI v2. + reserved 4; + // The title of the schema. + string title = 5; + // A short description of the schema. + string description = 6; + string default = 7; + bool read_only = 8; + // A free-form property to include a JSON example of this field. This is copied + // verbatim to the output swagger.json. Quotes must be escaped. + // This property is the same for 2.0 and 3.0.0 https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/3.0.0.md#schemaObject https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject + string example = 9; + double multiple_of = 10; + // Maximum represents an inclusive upper limit for a numeric instance. The + // value of MUST be a number, + double maximum = 11; + bool exclusive_maximum = 12; + // minimum represents an inclusive lower limit for a numeric instance. The + // value of MUST be a number, + double minimum = 13; + bool exclusive_minimum = 14; + uint64 max_length = 15; + uint64 min_length = 16; + string pattern = 17; + // field 18 is reserved for 'additionalItems', omitted from OpenAPI v2. + reserved 18; + // field 19 is reserved for 'items', but in OpenAPI-specific way. + // TODO(ivucica): add 'items'? + reserved 19; + uint64 max_items = 20; + uint64 min_items = 21; + bool unique_items = 22; + // field 23 is reserved for 'contains', omitted from OpenAPI v2. + reserved 23; + uint64 max_properties = 24; + uint64 min_properties = 25; + repeated string required = 26; + // field 27 is reserved for 'additionalProperties', but in OpenAPI-specific + // way. TODO(ivucica): add 'additionalProperties'? + reserved 27; + // field 28 is reserved for 'definitions', omitted from OpenAPI v2. + reserved 28; + // field 29 is reserved for 'properties', but in OpenAPI-specific way. + // TODO(ivucica): add 'additionalProperties'? + reserved 29; + // following fields are reserved, as the properties have been omitted from + // OpenAPI v2: + // patternProperties, dependencies, propertyNames, const + reserved 30 to 33; + // Items in 'array' must be unique. + repeated string array = 34; + + enum JSONSchemaSimpleTypes { + UNKNOWN = 0; + ARRAY = 1; + BOOLEAN = 2; + INTEGER = 3; + NULL = 4; + NUMBER = 5; + OBJECT = 6; + STRING = 7; + } + + repeated JSONSchemaSimpleTypes type = 35; + // `Format` + string format = 36; + // following fields are reserved, as the properties have been omitted from + // OpenAPI v2: contentMediaType, contentEncoding, if, then, else + reserved 37 to 41; + // field 42 is reserved for 'allOf', but in OpenAPI-specific way. + // TODO(ivucica): add 'allOf'? + reserved 42; + // following fields are reserved, as the properties have been omitted from + // OpenAPI v2: + // anyOf, oneOf, not + reserved 43 to 45; + // Items in `enum` must be unique https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.5.1 + repeated string enum = 46; + + // Additional field level properties used when generating the OpenAPI v2 file. + FieldConfiguration field_configuration = 1001; + + // 'FieldConfiguration' provides additional field level properties used when generating the OpenAPI v2 file. + // These properties are not defined by OpenAPIv2, but they are used to control the generation. + message FieldConfiguration { + // Alternative parameter name when used as path parameter. If set, this will + // be used as the complete parameter name when this field is used as a path + // parameter. Use this to avoid having auto generated path parameter names + // for overlapping paths. + string path_param_name = 47; + } + // Custom properties that start with "x-" such as "x-foo" used to describe + // extra functionality that is not covered by the standard OpenAPI Specification. + // See: https://swagger.io/docs/specification/2-0/swagger-extensions/ + map extensions = 48; +} + +// `Tag` is a representation of OpenAPI v2 specification's Tag object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#tagObject +// +message Tag { + // The name of the tag. Use it to allow override of the name of a + // global Tag object, then use that name to reference the tag throughout the + // OpenAPI file. + string name = 1; + // A short description for the tag. GFM syntax can be used for rich text + // representation. + string description = 2; + // Additional external documentation for this tag. + ExternalDocumentation external_docs = 3; + // Custom properties that start with "x-" such as "x-foo" used to describe + // extra functionality that is not covered by the standard OpenAPI Specification. + // See: https://swagger.io/docs/specification/2-0/swagger-extensions/ + map extensions = 4; +} + +// `SecurityDefinitions` is a representation of OpenAPI v2 specification's +// Security Definitions object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#securityDefinitionsObject +// +// A declaration of the security schemes available to be used in the +// specification. This does not enforce the security schemes on the operations +// and only serves to provide the relevant details for each scheme. +message SecurityDefinitions { + // A single security scheme definition, mapping a "name" to the scheme it + // defines. + map security = 1; +} + +// `SecurityScheme` is a representation of OpenAPI v2 specification's +// Security Scheme object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#securitySchemeObject +// +// Allows the definition of a security scheme that can be used by the +// operations. Supported schemes are basic authentication, an API key (either as +// a header or as a query parameter) and OAuth2's common flows (implicit, +// password, application and access code). +message SecurityScheme { + // The type of the security scheme. Valid values are "basic", + // "apiKey" or "oauth2". + enum Type { + TYPE_INVALID = 0; + TYPE_BASIC = 1; + TYPE_API_KEY = 2; + TYPE_OAUTH2 = 3; + } + + // The location of the API key. Valid values are "query" or "header". + enum In { + IN_INVALID = 0; + IN_QUERY = 1; + IN_HEADER = 2; + } + + // The flow used by the OAuth2 security scheme. Valid values are + // "implicit", "password", "application" or "accessCode". + enum Flow { + FLOW_INVALID = 0; + FLOW_IMPLICIT = 1; + FLOW_PASSWORD = 2; + FLOW_APPLICATION = 3; + FLOW_ACCESS_CODE = 4; + } + + // The type of the security scheme. Valid values are "basic", + // "apiKey" or "oauth2". + Type type = 1; + // A short description for security scheme. + string description = 2; + // The name of the header or query parameter to be used. + // Valid for apiKey. + string name = 3; + // The location of the API key. Valid values are "query" or + // "header". + // Valid for apiKey. + In in = 4; + // The flow used by the OAuth2 security scheme. Valid values are + // "implicit", "password", "application" or "accessCode". + // Valid for oauth2. + Flow flow = 5; + // The authorization URL to be used for this flow. This SHOULD be in + // the form of a URL. + // Valid for oauth2/implicit and oauth2/accessCode. + string authorization_url = 6; + // The token URL to be used for this flow. This SHOULD be in the + // form of a URL. + // Valid for oauth2/password, oauth2/application and oauth2/accessCode. + string token_url = 7; + // The available scopes for the OAuth2 security scheme. + // Valid for oauth2. + Scopes scopes = 8; + // Custom properties that start with "x-" such as "x-foo" used to describe + // extra functionality that is not covered by the standard OpenAPI Specification. + // See: https://swagger.io/docs/specification/2-0/swagger-extensions/ + map extensions = 9; +} + +// `SecurityRequirement` is a representation of OpenAPI v2 specification's +// Security Requirement object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#securityRequirementObject +// +// Lists the required security schemes to execute this operation. The object can +// have multiple security schemes declared in it which are all required (that +// is, there is a logical AND between the schemes). +// +// The name used for each property MUST correspond to a security scheme +// declared in the Security Definitions. +message SecurityRequirement { + // If the security scheme is of type "oauth2", then the value is a list of + // scope names required for the execution. For other security scheme types, + // the array MUST be empty. + message SecurityRequirementValue { + repeated string scope = 1; + } + // Each name must correspond to a security scheme which is declared in + // the Security Definitions. If the security scheme is of type "oauth2", + // then the value is a list of scope names required for the execution. + // For other security scheme types, the array MUST be empty. + map security_requirement = 1; +} + +// `Scopes` is a representation of OpenAPI v2 specification's Scopes object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#scopesObject +// +// Lists the available scopes for an OAuth2 security scheme. +message Scopes { + // Maps between a name of a scope to a short description of it (as the value + // of the property). + map scope = 1; +} \ No newline at end of file diff --git a/third_party/validate/README.md b/third_party/validate/README.md new file mode 100644 index 0000000..56698db --- /dev/null +++ b/third_party/validate/README.md @@ -0,0 +1,3 @@ +# protoc-gen-validate (PGV) + +* https://github.com/envoyproxy/protoc-gen-validate diff --git a/third_party/validate/validate.proto b/third_party/validate/validate.proto new file mode 100644 index 0000000..4195ecf --- /dev/null +++ b/third_party/validate/validate.proto @@ -0,0 +1,863 @@ +syntax = "proto2"; +package validate; + +option go_package = "github.com/envoyproxy/protoc-gen-validate/validate"; +option java_package = "io.envoyproxy.pgv.validate"; + +import "google/protobuf/descriptor.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/timestamp.proto"; + +// Validation rules applied at the message level +extend google.protobuf.MessageOptions { + // Disabled nullifies any validation rules for this message, including any + // message fields associated with it that do support validation. + optional bool disabled = 1071; + // Ignore skips generation of validation methods for this message. + optional bool ignored = 1072; +} + +// Validation rules applied at the oneof level +extend google.protobuf.OneofOptions { + // Required ensures that exactly one the field options in a oneof is set; + // validation fails if no fields in the oneof are set. + optional bool required = 1071; +} + +// Validation rules applied at the field level +extend google.protobuf.FieldOptions { + // Rules specify the validations to be performed on this field. By default, + // no validation is performed against a field. + optional FieldRules rules = 1071; +} + +// FieldRules encapsulates the rules for each type of field. Depending on the +// field, the correct set should be used to ensure proper validations. +message FieldRules { + optional MessageRules message = 17; + oneof type { + // Scalar Field Types + FloatRules float = 1; + DoubleRules double = 2; + Int32Rules int32 = 3; + Int64Rules int64 = 4; + UInt32Rules uint32 = 5; + UInt64Rules uint64 = 6; + SInt32Rules sint32 = 7; + SInt64Rules sint64 = 8; + Fixed32Rules fixed32 = 9; + Fixed64Rules fixed64 = 10; + SFixed32Rules sfixed32 = 11; + SFixed64Rules sfixed64 = 12; + BoolRules bool = 13; + StringRules string = 14; + BytesRules bytes = 15; + + // Complex Field Types + EnumRules enum = 16; + RepeatedRules repeated = 18; + MapRules map = 19; + + // Well-Known Field Types + AnyRules any = 20; + DurationRules duration = 21; + TimestampRules timestamp = 22; + } +} + +// FloatRules describes the constraints applied to `float` values +message FloatRules { + // Const specifies that this field must be exactly the specified value + optional float const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional float lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional float lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional float gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional float gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated float in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated float not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// DoubleRules describes the constraints applied to `double` values +message DoubleRules { + // Const specifies that this field must be exactly the specified value + optional double const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional double lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional double lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional double gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional double gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated double in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated double not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// Int32Rules describes the constraints applied to `int32` values +message Int32Rules { + // Const specifies that this field must be exactly the specified value + optional int32 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional int32 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional int32 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional int32 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional int32 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated int32 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated int32 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// Int64Rules describes the constraints applied to `int64` values +message Int64Rules { + // Const specifies that this field must be exactly the specified value + optional int64 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional int64 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional int64 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional int64 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional int64 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated int64 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated int64 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// UInt32Rules describes the constraints applied to `uint32` values +message UInt32Rules { + // Const specifies that this field must be exactly the specified value + optional uint32 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional uint32 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional uint32 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional uint32 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional uint32 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated uint32 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated uint32 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// UInt64Rules describes the constraints applied to `uint64` values +message UInt64Rules { + // Const specifies that this field must be exactly the specified value + optional uint64 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional uint64 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional uint64 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional uint64 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional uint64 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated uint64 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated uint64 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// SInt32Rules describes the constraints applied to `sint32` values +message SInt32Rules { + // Const specifies that this field must be exactly the specified value + optional sint32 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional sint32 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional sint32 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional sint32 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional sint32 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated sint32 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated sint32 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// SInt64Rules describes the constraints applied to `sint64` values +message SInt64Rules { + // Const specifies that this field must be exactly the specified value + optional sint64 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional sint64 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional sint64 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional sint64 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional sint64 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated sint64 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated sint64 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// Fixed32Rules describes the constraints applied to `fixed32` values +message Fixed32Rules { + // Const specifies that this field must be exactly the specified value + optional fixed32 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional fixed32 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional fixed32 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional fixed32 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional fixed32 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated fixed32 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated fixed32 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// Fixed64Rules describes the constraints applied to `fixed64` values +message Fixed64Rules { + // Const specifies that this field must be exactly the specified value + optional fixed64 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional fixed64 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional fixed64 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional fixed64 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional fixed64 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated fixed64 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated fixed64 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// SFixed32Rules describes the constraints applied to `sfixed32` values +message SFixed32Rules { + // Const specifies that this field must be exactly the specified value + optional sfixed32 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional sfixed32 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional sfixed32 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional sfixed32 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional sfixed32 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated sfixed32 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated sfixed32 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// SFixed64Rules describes the constraints applied to `sfixed64` values +message SFixed64Rules { + // Const specifies that this field must be exactly the specified value + optional sfixed64 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional sfixed64 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional sfixed64 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional sfixed64 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional sfixed64 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated sfixed64 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated sfixed64 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// BoolRules describes the constraints applied to `bool` values +message BoolRules { + // Const specifies that this field must be exactly the specified value + optional bool const = 1; +} + +// StringRules describe the constraints applied to `string` values +message StringRules { + // Const specifies that this field must be exactly the specified value + optional string const = 1; + + // Len specifies that this field must be the specified number of + // characters (Unicode code points). Note that the number of + // characters may differ from the number of bytes in the string. + optional uint64 len = 19; + + // MinLen specifies that this field must be the specified number of + // characters (Unicode code points) at a minimum. Note that the number of + // characters may differ from the number of bytes in the string. + optional uint64 min_len = 2; + + // MaxLen specifies that this field must be the specified number of + // characters (Unicode code points) at a maximum. Note that the number of + // characters may differ from the number of bytes in the string. + optional uint64 max_len = 3; + + // LenBytes specifies that this field must be the specified number of bytes + // at a minimum + optional uint64 len_bytes = 20; + + // MinBytes specifies that this field must be the specified number of bytes + // at a minimum + optional uint64 min_bytes = 4; + + // MaxBytes specifies that this field must be the specified number of bytes + // at a maximum + optional uint64 max_bytes = 5; + + // Pattern specifes that this field must match against the specified + // regular expression (RE2 syntax). The included expression should elide + // any delimiters. + optional string pattern = 6; + + // Prefix specifies that this field must have the specified substring at + // the beginning of the string. + optional string prefix = 7; + + // Suffix specifies that this field must have the specified substring at + // the end of the string. + optional string suffix = 8; + + // Contains specifies that this field must have the specified substring + // anywhere in the string. + optional string contains = 9; + + // NotContains specifies that this field cannot have the specified substring + // anywhere in the string. + optional string not_contains = 23; + + // In specifies that this field must be equal to one of the specified + // values + repeated string in = 10; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated string not_in = 11; + + // WellKnown rules provide advanced constraints against common string + // patterns + oneof well_known { + // Email specifies that the field must be a valid email address as + // defined by RFC 5322 + bool email = 12; + + // Hostname specifies that the field must be a valid hostname as + // defined by RFC 1034. This constraint does not support + // internationalized domain names (IDNs). + bool hostname = 13; + + // Ip specifies that the field must be a valid IP (v4 or v6) address. + // Valid IPv6 addresses should not include surrounding square brackets. + bool ip = 14; + + // Ipv4 specifies that the field must be a valid IPv4 address. + bool ipv4 = 15; + + // Ipv6 specifies that the field must be a valid IPv6 address. Valid + // IPv6 addresses should not include surrounding square brackets. + bool ipv6 = 16; + + // Uri specifies that the field must be a valid, absolute URI as defined + // by RFC 3986 + bool uri = 17; + + // UriRef specifies that the field must be a valid URI as defined by RFC + // 3986 and may be relative or absolute. + bool uri_ref = 18; + + // Address specifies that the field must be either a valid hostname as + // defined by RFC 1034 (which does not support internationalized domain + // names or IDNs), or it can be a valid IP (v4 or v6). + bool address = 21; + + // Uuid specifies that the field must be a valid UUID as defined by + // RFC 4122 + bool uuid = 22; + + // WellKnownRegex specifies a common well known pattern defined as a regex. + KnownRegex well_known_regex = 24; + } + + // This applies to regexes HTTP_HEADER_NAME and HTTP_HEADER_VALUE to enable + // strict header validation. + // By default, this is true, and HTTP header validations are RFC-compliant. + // Setting to false will enable a looser validations that only disallows + // \r\n\0 characters, which can be used to bypass header matching rules. + optional bool strict = 25 [default = true]; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 26; +} + +// WellKnownRegex contain some well-known patterns. +enum KnownRegex { + UNKNOWN = 0; + + // HTTP header name as defined by RFC 7230. + HTTP_HEADER_NAME = 1; + + // HTTP header value as defined by RFC 7230. + HTTP_HEADER_VALUE = 2; +} + +// BytesRules describe the constraints applied to `bytes` values +message BytesRules { + // Const specifies that this field must be exactly the specified value + optional bytes const = 1; + + // Len specifies that this field must be the specified number of bytes + optional uint64 len = 13; + + // MinLen specifies that this field must be the specified number of bytes + // at a minimum + optional uint64 min_len = 2; + + // MaxLen specifies that this field must be the specified number of bytes + // at a maximum + optional uint64 max_len = 3; + + // Pattern specifes that this field must match against the specified + // regular expression (RE2 syntax). The included expression should elide + // any delimiters. + optional string pattern = 4; + + // Prefix specifies that this field must have the specified bytes at the + // beginning of the string. + optional bytes prefix = 5; + + // Suffix specifies that this field must have the specified bytes at the + // end of the string. + optional bytes suffix = 6; + + // Contains specifies that this field must have the specified bytes + // anywhere in the string. + optional bytes contains = 7; + + // In specifies that this field must be equal to one of the specified + // values + repeated bytes in = 8; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated bytes not_in = 9; + + // WellKnown rules provide advanced constraints against common byte + // patterns + oneof well_known { + // Ip specifies that the field must be a valid IP (v4 or v6) address in + // byte format + bool ip = 10; + + // Ipv4 specifies that the field must be a valid IPv4 address in byte + // format + bool ipv4 = 11; + + // Ipv6 specifies that the field must be a valid IPv6 address in byte + // format + bool ipv6 = 12; + } + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 14; +} + +// EnumRules describe the constraints applied to enum values +message EnumRules { + // Const specifies that this field must be exactly the specified value + optional int32 const = 1; + + // DefinedOnly specifies that this field must be only one of the defined + // values for this enum, failing on any undefined value. + optional bool defined_only = 2; + + // In specifies that this field must be equal to one of the specified + // values + repeated int32 in = 3; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated int32 not_in = 4; +} + +// MessageRules describe the constraints applied to embedded message values. +// For message-type fields, validation is performed recursively. +message MessageRules { + // Skip specifies that the validation rules of this field should not be + // evaluated + optional bool skip = 1; + + // Required specifies that this field must be set + optional bool required = 2; +} + +// RepeatedRules describe the constraints applied to `repeated` values +message RepeatedRules { + // MinItems specifies that this field must have the specified number of + // items at a minimum + optional uint64 min_items = 1; + + // MaxItems specifies that this field must have the specified number of + // items at a maximum + optional uint64 max_items = 2; + + // Unique specifies that all elements in this field must be unique. This + // contraint is only applicable to scalar and enum types (messages are not + // supported). + optional bool unique = 3; + + // Items specifies the contraints to be applied to each item in the field. + // Repeated message fields will still execute validation against each item + // unless skip is specified here. + optional FieldRules items = 4; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 5; +} + +// MapRules describe the constraints applied to `map` values +message MapRules { + // MinPairs specifies that this field must have the specified number of + // KVs at a minimum + optional uint64 min_pairs = 1; + + // MaxPairs specifies that this field must have the specified number of + // KVs at a maximum + optional uint64 max_pairs = 2; + + // NoSparse specifies values in this field cannot be unset. This only + // applies to map's with message value types. + optional bool no_sparse = 3; + + // Keys specifies the constraints to be applied to each key in the field. + optional FieldRules keys = 4; + + // Values specifies the constraints to be applied to the value of each key + // in the field. Message values will still have their validations evaluated + // unless skip is specified here. + optional FieldRules values = 5; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 6; +} + +// AnyRules describe constraints applied exclusively to the +// `google.protobuf.Any` well-known type +message AnyRules { + // Required specifies that this field must be set + optional bool required = 1; + + // In specifies that this field's `type_url` must be equal to one of the + // specified values. + repeated string in = 2; + + // NotIn specifies that this field's `type_url` must not be equal to any of + // the specified values. + repeated string not_in = 3; +} + +// DurationRules describe the constraints applied exclusively to the +// `google.protobuf.Duration` well-known type +message DurationRules { + // Required specifies that this field must be set + optional bool required = 1; + + // Const specifies that this field must be exactly the specified value + optional google.protobuf.Duration const = 2; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional google.protobuf.Duration lt = 3; + + // Lt specifies that this field must be less than the specified value, + // inclusive + optional google.protobuf.Duration lte = 4; + + // Gt specifies that this field must be greater than the specified value, + // exclusive + optional google.protobuf.Duration gt = 5; + + // Gte specifies that this field must be greater than the specified value, + // inclusive + optional google.protobuf.Duration gte = 6; + + // In specifies that this field must be equal to one of the specified + // values + repeated google.protobuf.Duration in = 7; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated google.protobuf.Duration not_in = 8; +} + +// TimestampRules describe the constraints applied exclusively to the +// `google.protobuf.Timestamp` well-known type +message TimestampRules { + // Required specifies that this field must be set + optional bool required = 1; + + // Const specifies that this field must be exactly the specified value + optional google.protobuf.Timestamp const = 2; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional google.protobuf.Timestamp lt = 3; + + // Lte specifies that this field must be less than the specified value, + // inclusive + optional google.protobuf.Timestamp lte = 4; + + // Gt specifies that this field must be greater than the specified value, + // exclusive + optional google.protobuf.Timestamp gt = 5; + + // Gte specifies that this field must be greater than the specified value, + // inclusive + optional google.protobuf.Timestamp gte = 6; + + // LtNow specifies that this must be less than the current time. LtNow + // can only be used with the Within rule. + optional bool lt_now = 7; + + // GtNow specifies that this must be greater than the current time. GtNow + // can only be used with the Within rule. + optional bool gt_now = 8; + + // Within specifies that this field must be within this duration of the + // current time. This constraint can be used alone or with the LtNow and + // GtNow rules. + optional google.protobuf.Duration within = 9; +} diff --git a/wai/eonline.go b/wai/eonline.go new file mode 100644 index 0000000..29b696b --- /dev/null +++ b/wai/eonline.go @@ -0,0 +1,52 @@ +package service + +import ( + "context" + + pb "sandc/api/eonline/v1" +) + +type EonlineService struct { + pb.UnimplementedEonlineServer +} + +func NewEonlineService() *EonlineService { + return &EonlineService{} +} + +func (s *EonlineService) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) { + return &pb.HelloReply{}, nil +} +func (s *EonlineService) PayInit(ctx context.Context, req *pb.PayInitReq) (*pb.PayInitReply, error) { + return &pb.PayInitReply{}, nil +} +func (s *EonlineService) Payout(ctx context.Context, req *pb.PayoutReq) (*pb.PayoutReply, error) { + return &pb.PayoutReply{}, nil +} +func (s *EonlineService) PayoutBrazil(ctx context.Context, req *pb.PayoutReq) (*pb.PayoutReply, error) { + return &pb.PayoutReply{}, nil +} +func (s *EonlineService) PayoutCallback(ctx context.Context, req *pb.PayoutCallbackReq) (*pb.PayoutCallbackReply, error) { + return &pb.PayoutCallbackReply{}, nil +} +func (s *EonlineService) PayoutCheck(ctx context.Context, req *pb.PayoutCheckReq) (*pb.PayoutCheckReply, error) { + return &pb.PayoutCheckReply{}, nil +} +func (s *EonlineService) GetPayoutUserLst(ctx context.Context, req *pb.PayoutUserLstReq) (*pb.PayoutUserLstReply, error) { + return &pb.PayoutUserLstReply{}, nil +} +func (s *EonlineService) SetPayoutStatus(ctx context.Context, req *pb.PayoutStatusReq) (*pb.PayoutStatusReply, error) { + return &pb.PayoutStatusReply{}, nil +} +func (s *EonlineService) SubmitCheck(ctx context.Context, req *pb.SubmitCheckReq) (*pb.SubmitCheckReply, error) { + return &pb.SubmitCheckReply{}, nil +} +func (s *EonlineService) CheckInfo(ctx context.Context, req *pb.CheckInfoReq) (*pb.CheckInfoReply, error) { + return &pb.CheckInfoReply{}, nil +} +func (s *EonlineService) AddChat(ctx context.Context, req *pb.AddChatReq) (*pb.AddChatReply, error) { + return &pb.AddChatReply{}, nil +} +func (s *EonlineService) GetChat(ctx context.Context, req *pb.GetChatReq) (*pb.GetChatReply, error) { + return &pb.GetChatReply{}, nil +}