diff --git a/.gitea/workflows/cd-test.yml b/.gitea/workflows/cd-test.yml index 11b5bc4..9145a65 100644 --- a/.gitea/workflows/cd-test.yml +++ b/.gitea/workflows/cd-test.yml @@ -48,4 +48,5 @@ jobs: docker pull ${{ vars.REGISTRY }}/lone/record:latest docker pull ${{ vars.REGISTRY }}/lone/factory:latest docker pull ${{ vars.REGISTRY }}/lone/message:latest + docker pull ${{ vars.REGISTRY }}/lone/support:latest docker compose up -d diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index b2a509a..e532c76 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: - name: Detect changes id: filter run: | - SERVICES="bff product admin user ad chore order express sale wecom task equipment record payment factory message" + SERVICES="bff product admin user ad chore order express sale wecom task equipment record payment factory message support" pattern_for() { case "$1" in @@ -97,6 +97,7 @@ jobs: - factory - message - order + - support env: BUILD_LIST: ${{ needs.changes.outputs.build_list }} NAME: lone/${{ matrix.name }} diff --git a/rpc/support/pb/support.pb.go b/rpc/support/pb/support.pb.go new file mode 100644 index 0000000..a5b15b0 --- /dev/null +++ b/rpc/support/pb/support.pb.go @@ -0,0 +1,229 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc v3.19.4 +// source: support/support.proto + +package support + +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" + unsafe "unsafe" +) + +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 Response struct { + state protoimpl.MessageState `protogen:"open.v1"` + Code int32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` + Msg string `protobuf:"bytes,2,opt,name=msg,proto3" json:"msg,omitempty"` + Data string `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Response) Reset() { + *x = Response{} + mi := &file_support_support_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Response) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Response) ProtoMessage() {} + +func (x *Response) ProtoReflect() protoreflect.Message { + mi := &file_support_support_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Response.ProtoReflect.Descriptor instead. +func (*Response) Descriptor() ([]byte, []int) { + return file_support_support_proto_rawDescGZIP(), []int{0} +} + +func (x *Response) GetCode() int32 { + if x != nil { + return x.Code + } + return 0 +} + +func (x *Response) GetMsg() string { + if x != nil { + return x.Msg + } + return "" +} + +func (x *Response) GetData() string { + if x != nil { + return x.Data + } + return "" +} + +type CallReq struct { + state protoimpl.MessageState `protogen:"open.v1"` + Action string `protobuf:"bytes,1,opt,name=action,proto3" json:"action,omitempty"` + Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` + Request string `protobuf:"bytes,3,opt,name=request,proto3" json:"request,omitempty"` + // 与旧云呼一致:前端传入的 AccessKey + A string `protobuf:"bytes,4,opt,name=a,proto3" json:"a,omitempty"` + S string `protobuf:"bytes,5,opt,name=s,proto3" json:"s,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CallReq) Reset() { + *x = CallReq{} + mi := &file_support_support_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CallReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CallReq) ProtoMessage() {} + +func (x *CallReq) ProtoReflect() protoreflect.Message { + mi := &file_support_support_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CallReq.ProtoReflect.Descriptor instead. +func (*CallReq) Descriptor() ([]byte, []int) { + return file_support_support_proto_rawDescGZIP(), []int{1} +} + +func (x *CallReq) GetAction() string { + if x != nil { + return x.Action + } + return "" +} + +func (x *CallReq) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *CallReq) GetRequest() string { + if x != nil { + return x.Request + } + return "" +} + +func (x *CallReq) GetA() string { + if x != nil { + return x.A + } + return "" +} + +func (x *CallReq) GetS() string { + if x != nil { + return x.S + } + return "" +} + +var File_support_support_proto protoreflect.FileDescriptor + +const file_support_support_proto_rawDesc = "" + + "\n" + + "\x15support/support.proto\x12\asupport\x1a\x1cgoogle/api/annotations.proto\"D\n" + + "\bResponse\x12\x12\n" + + "\x04code\x18\x01 \x01(\x05R\x04code\x12\x10\n" + + "\x03msg\x18\x02 \x01(\tR\x03msg\x12\x12\n" + + "\x04data\x18\x03 \x01(\tR\x04data\"q\n" + + "\aCallReq\x12\x16\n" + + "\x06action\x18\x01 \x01(\tR\x06action\x12\x18\n" + + "\aversion\x18\x02 \x01(\tR\aversion\x12\x18\n" + + "\arequest\x18\x03 \x01(\tR\arequest\x12\f\n" + + "\x01a\x18\x04 \x01(\tR\x01a\x12\f\n" + + "\x01s\x18\x05 \x01(\tR\x01s2W\n" + + "\aSupport\x12L\n" + + "\x04Call\x12\x10.support.CallReq\x1a\x11.support.Response\"\x1f\x82\xd3\xe4\x93\x02\x19:\x01*\"\x14/api/v3/support/callB\x1bZ\x19lone-services/rpc/supportb\x06proto3" + +var ( + file_support_support_proto_rawDescOnce sync.Once + file_support_support_proto_rawDescData []byte +) + +func file_support_support_proto_rawDescGZIP() []byte { + file_support_support_proto_rawDescOnce.Do(func() { + file_support_support_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_support_support_proto_rawDesc), len(file_support_support_proto_rawDesc))) + }) + return file_support_support_proto_rawDescData +} + +var file_support_support_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_support_support_proto_goTypes = []any{ + (*Response)(nil), // 0: support.Response + (*CallReq)(nil), // 1: support.CallReq +} +var file_support_support_proto_depIdxs = []int32{ + 1, // 0: support.Support.Call:input_type -> support.CallReq + 0, // 1: support.Support.Call:output_type -> support.Response + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_support_support_proto_init() } +func file_support_support_proto_init() { + if File_support_support_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_support_support_proto_rawDesc), len(file_support_support_proto_rawDesc)), + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_support_support_proto_goTypes, + DependencyIndexes: file_support_support_proto_depIdxs, + MessageInfos: file_support_support_proto_msgTypes, + }.Build() + File_support_support_proto = out.File + file_support_support_proto_goTypes = nil + file_support_support_proto_depIdxs = nil +} diff --git a/rpc/support/pb/support_grpc.pb.go b/rpc/support/pb/support_grpc.pb.go new file mode 100644 index 0000000..0e80a8b --- /dev/null +++ b/rpc/support/pb/support_grpc.pb.go @@ -0,0 +1,121 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.6.2 +// - protoc v3.19.4 +// source: support/support.proto + +package support + +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.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + Support_Call_FullMethodName = "/support.Support/Call" +) + +// SupportClient is the client API for Support 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 SupportClient interface { + Call(ctx context.Context, in *CallReq, opts ...grpc.CallOption) (*Response, error) +} + +type supportClient struct { + cc grpc.ClientConnInterface +} + +func NewSupportClient(cc grpc.ClientConnInterface) SupportClient { + return &supportClient{cc} +} + +func (c *supportClient) Call(ctx context.Context, in *CallReq, opts ...grpc.CallOption) (*Response, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Response) + err := c.cc.Invoke(ctx, Support_Call_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// SupportServer is the server API for Support service. +// All implementations must embed UnimplementedSupportServer +// for forward compatibility. +type SupportServer interface { + Call(context.Context, *CallReq) (*Response, error) + mustEmbedUnimplementedSupportServer() +} + +// UnimplementedSupportServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedSupportServer struct{} + +func (UnimplementedSupportServer) Call(context.Context, *CallReq) (*Response, error) { + return nil, status.Error(codes.Unimplemented, "method Call not implemented") +} +func (UnimplementedSupportServer) mustEmbedUnimplementedSupportServer() {} +func (UnimplementedSupportServer) testEmbeddedByValue() {} + +// UnsafeSupportServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to SupportServer will +// result in compilation errors. +type UnsafeSupportServer interface { + mustEmbedUnimplementedSupportServer() +} + +func RegisterSupportServer(s grpc.ServiceRegistrar, srv SupportServer) { + // If the following call panics, it indicates UnimplementedSupportServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&Support_ServiceDesc, srv) +} + +func _Support_Call_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CallReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SupportServer).Call(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Support_Call_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SupportServer).Call(ctx, req.(*CallReq)) + } + return interceptor(ctx, in, info, handler) +} + +// Support_ServiceDesc is the grpc.ServiceDesc for Support service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Support_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "support.Support", + HandlerType: (*SupportServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Call", + Handler: _Support_Call_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "support/support.proto", +} diff --git a/rpc/support/support.pb b/rpc/support/support.pb new file mode 100644 index 0000000..24c1aff Binary files /dev/null and b/rpc/support/support.pb differ diff --git a/rpc/support/support.proto b/rpc/support/support.proto new file mode 100644 index 0000000..99202a4 --- /dev/null +++ b/rpc/support/support.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; + +package support; +option go_package = "lone-services/rpc/support"; +import "google/api/annotations.proto"; + +service Support { + rpc Call(CallReq) returns (Response) { + option (google.api.http) = { + post: "/api/v3/support/call" + body: "*" + }; + } +} + +message Response { + int32 code = 1; + string msg = 2; + string data = 3; +} + +message CallReq { + string action = 1; + string version = 2; + string request = 3; + // 与旧云呼一致:前端传入的 AccessKey + string a = 4; + string s = 5; +} diff --git a/services/support/.gitignore b/services/support/.gitignore new file mode 100644 index 0000000..6164c78 --- /dev/null +++ b/services/support/.gitignore @@ -0,0 +1,2 @@ +/ +!.gitignore diff --git a/services/support/Dockerfile b/services/support/Dockerfile new file mode 100644 index 0000000..3360359 --- /dev/null +++ b/services/support/Dockerfile @@ -0,0 +1,39 @@ +FROM golang:1.26.5-alpine AS builder + +WORKDIR /src + +ENV GOPROXY=https://goproxy.cn,direct \ + CGO_ENABLED=0 \ + GOOS=linux \ + GOARCH=amd64 + +COPY pkg ./pkg +COPY go.mod go.sum ./ + +RUN --mount=type=cache,target=/go/pkg/mod \ + go mod download + +COPY rpc/ ./rpc +COPY services/support/ ./services/support/ + +WORKDIR /src/services/support + +RUN --mount=type=cache,target=/go/pkg/mod \ + --mount=type=cache,target=/root/.cache/go-build \ + go build -ldflags="-s -w" -o /out/main ./ + +FROM alpine:3.22 + +WORKDIR /app + +ENV TZ=Asia/Shanghai + +RUN apk add --no-cache tzdata \ + && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ + && echo "Asia/Shanghai" > /etc/timezone + +COPY --from=builder /out/main . + +EXPOSE 11600 + +CMD ["./main", "-f", "etc/support.yaml"] diff --git a/services/support/etc/support.yaml b/services/support/etc/support.yaml new file mode 100644 index 0000000..551c236 --- /dev/null +++ b/services/support/etc/support.yaml @@ -0,0 +1,7 @@ +Nacos: + Hosts: + - rnacos:8848 + NamespaceId: test + Group: LONE_SERVICES + RegisterIP: support + ConfigID: support diff --git a/services/support/internal/ccc/client.go b/services/support/internal/ccc/client.go new file mode 100644 index 0000000..3a9d6ea --- /dev/null +++ b/services/support/internal/ccc/client.go @@ -0,0 +1,80 @@ +package ccc + +import ( + "encoding/json" + "fmt" + "strings" + + openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client" + openapiutil "github.com/alibabacloud-go/openapi-util/service" + util "github.com/alibabacloud-go/tea-utils/v2/service" + "github.com/alibabacloud-go/tea/tea" +) + +const defaultEndpoint = "ccc.cn-shanghai.aliyuncs.com" + +func createClient(accessKeyId, accessKeySecret, endpoint string) (*openapi.Client, error) { + if endpoint == "" { + endpoint = defaultEndpoint + } + cfg := &openapi.Config{ + AccessKeyId: tea.String(accessKeyId), + AccessKeySecret: tea.String(accessKeySecret), + Endpoint: tea.String(endpoint), + } + return openapi.NewClient(cfg) +} + +func createApiInfo(action, version string) *openapi.Params { + return &openapi.Params{ + Action: tea.String(action), + Version: tea.String(version), + Protocol: tea.String("HTTPS"), + Method: tea.String("POST"), + AuthType: tea.String("AK"), + Style: tea.String("RPC"), + Pathname: tea.String("/"), + ReqBodyType: tea.String("json"), + BodyType: tea.String("json"), + } +} + +func capitalizeKey(key string) string { + if key == "" { + return key + } + return strings.ToUpper(key[:1]) + key[1:] +} + +func Call(action, version, requestJSON, accessKeyId, accessKeySecret, endpoint string) (any, error) { + client, err := createClient(accessKeyId, accessKeySecret, endpoint) + if err != nil { + return nil, err + } + + requestStr := strings.TrimSpace(requestJSON) + if requestStr == "" { + requestStr = "{}" + } + + var requestStruct map[string]any + if err = json.NewDecoder(strings.NewReader(requestStr)).Decode(&requestStruct); err != nil { + return nil, fmt.Errorf("request json: %w", err) + } + + queries := make(map[string]any, len(requestStruct)) + for key, value := range requestStruct { + queries[capitalizeKey(key)] = value + } + + res, err := client.CallApi(createApiInfo(action, version), &openapi.OpenApiRequest{ + Query: openapiutil.Query(queries), + }, &util.RuntimeOptions{}) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res["body"], nil +} diff --git a/services/support/internal/config/config.go b/services/support/internal/config/config.go new file mode 100644 index 0000000..a1979dd --- /dev/null +++ b/services/support/internal/config/config.go @@ -0,0 +1,13 @@ +package config + +type Config struct { + Nacos NacosConf +} + +type NacosConf struct { + Hosts []string + NamespaceId string `json:",optional"` + Group string `json:",optional"` + RegisterIP string `json:",optional"` + ConfigID string `json:",optional"` +} diff --git a/services/support/internal/logic/callLogic.go b/services/support/internal/logic/callLogic.go new file mode 100644 index 0000000..759d4ff --- /dev/null +++ b/services/support/internal/logic/callLogic.go @@ -0,0 +1,43 @@ +package logic + +import ( + "context" + + "lone-services/pkg/utils" + "lone-services/pkg/validate" + support "lone-services/rpc/support/pb" + "lone-services/services/support/internal/ccc" + "lone-services/services/support/internal/svc" + "lone-services/services/support/validator" + + "github.com/zeromicro/go-zero/core/logx" +) + +type CallLogic struct { + ctx context.Context + svcCtx *svc.ServiceContext + logx.Logger +} + +func NewCallLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CallLogic { + return &CallLogic{ + ctx: ctx, + svcCtx: svcCtx, + Logger: logx.WithContext(ctx), + } +} + +func (l *CallLogic) Call(in *support.CallReq) (*support.Response, error) { + var req validator.CallValidator + if msg := validate.ValidateFromProto(in, &req); msg != utils.StringEmpty { + return outResponse(utils.ErrorParams, msg), nil + } + + endpoint := utils.GetConfigString("ccc.endpoint") + body, err := ccc.Call(req.Action, req.Version, req.Request, req.A, req.S, endpoint) + if err != nil { + l.Errorf("ccc call action=%s version=%s: %v", req.Action, req.Version, err) + return outResponse(utils.Fail, err.Error()), nil + } + return okResponse(body), nil +} diff --git a/services/support/internal/logic/response.go b/services/support/internal/logic/response.go new file mode 100644 index 0000000..d867dbb --- /dev/null +++ b/services/support/internal/logic/response.go @@ -0,0 +1,31 @@ +package logic + +import ( + "lone-services/pkg/utils" + support "lone-services/rpc/support/pb" + + jsoniter "github.com/json-iterator/go" +) + +func okResponse(data any) *support.Response { + buf, _ := jsoniter.Marshal(data) + return &support.Response{ + Code: utils.Ok.Code, + Msg: utils.Ok.Msg, + Data: string(buf), + } +} + +func failResponse(status utils.Status) *support.Response { + return &support.Response{ + Code: status.Code, + Msg: status.Msg, + } +} + +func outResponse(status utils.Status, msg string) *support.Response { + return &support.Response{ + Code: status.Code, + Msg: msg, + } +} diff --git a/services/support/internal/server/supportServer.go b/services/support/internal/server/supportServer.go new file mode 100644 index 0000000..2199346 --- /dev/null +++ b/services/support/internal/server/supportServer.go @@ -0,0 +1,25 @@ +package server + +import ( + "context" + + support "lone-services/rpc/support/pb" + "lone-services/services/support/internal/logic" + "lone-services/services/support/internal/svc" +) + +type SupportServer struct { + svcCtx *svc.ServiceContext + support.UnimplementedSupportServer +} + +func NewSupportServer(svcCtx *svc.ServiceContext) *SupportServer { + return &SupportServer{ + svcCtx: svcCtx, + } +} + +func (s *SupportServer) Call(ctx context.Context, in *support.CallReq) (*support.Response, error) { + l := logic.NewCallLogic(ctx, s.svcCtx) + return l.Call(in) +} diff --git a/services/support/internal/svc/serviceContext.go b/services/support/internal/svc/serviceContext.go new file mode 100644 index 0000000..7c92181 --- /dev/null +++ b/services/support/internal/svc/serviceContext.go @@ -0,0 +1,22 @@ +package svc + +import ( + "lone-services/pkg/utils" + "lone-services/services/support/internal/config" + + "gorm.io/gorm" +) + +type ServiceContext struct { + Config config.Config + DB *gorm.DB + Prefix string +} + +func NewServiceContext(c config.Config, db *gorm.DB) *ServiceContext { + return &ServiceContext{ + Config: c, + DB: db, + Prefix: utils.GetConfigString("mysql.prefix"), + } +} diff --git a/services/support/run.toml b/services/support/run.toml new file mode 100644 index 0000000..27a3cca --- /dev/null +++ b/services/support/run.toml @@ -0,0 +1,48 @@ +[base] + login_out_time=43200 + login_refresh_out_time=83200 + name = "support-service" + listenOn = "0.0.0.0:11600" + mode = "dev" +[log] + path = "logs" + serviceName = "support-service" + mode = "file" + encoding = "plain" + level = "info" + keepDays = 7 + maxSize = 50 + maxBackups = 5 + compress = false + +[mysql] + host = '39.106.171.204' + port = 33066 + user = 'root' + password = 'MOLXRZNOU4Y4' + database = 'dms-support' + charset = 'utf8mb4' + prefix = '' + debug = true +[mysql_read] + host = '39.106.171.204' + port = 33066 + user = 'root' + password = 'MOLXRZNOU4Y4' + database = 'dms-support' + charset = 'utf8mb4' + prefix = '' + +[redis] + host = '39.106.171.204' + password = 'lLMLcuPpzSj' + port = 6379 + db = 0 +[log-redis] + host = '39.106.171.204' + password = 'lLMLcuPpzSj' + port = 6379 + db = 3 + +[encrypt] + data_key = "u2t9T3luZtoRfhBstkFN6TiIMW38BA8a" diff --git a/services/support/support.go b/services/support/support.go new file mode 100644 index 0000000..d4173d3 --- /dev/null +++ b/services/support/support.go @@ -0,0 +1,152 @@ +package main + +import ( + "flag" + "net" + "os" + "strconv" + + "lone-services/pkg/discovery" + "lone-services/pkg/modelbase" + "lone-services/pkg/mysql" + "lone-services/pkg/redis" + "lone-services/pkg/utils" + "lone-services/pkg/validate" + support "lone-services/rpc/support/pb" + "lone-services/services/support/internal/config" + "lone-services/services/support/internal/server" + "lone-services/services/support/internal/svc" + + "github.com/zeromicro/go-zero/core/conf" + "github.com/zeromicro/go-zero/core/logx" + "github.com/zeromicro/go-zero/core/service" + "github.com/zeromicro/go-zero/zrpc" + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" +) + +var configFile = flag.String("f", "etc/support.yaml", "the config file") + +func main() { + flag.Parse() + var c config.Config + conf.MustLoad(*configFile, &c) + + nacosParam := utils.NacosConfig{ + Hosts: c.Nacos.Hosts, + NamespaceId: c.Nacos.NamespaceId, + Group: c.Nacos.Group, + ConfigID: c.Nacos.ConfigID, + } + utils.InitConfig(nacosParam) + + logConf := logx.LogConf{ + ServiceName: utils.GetConfigString("log.serviceName"), + Mode: utils.GetConfigString("log.mode"), + Encoding: utils.GetConfigString("log.encoding"), + Level: utils.GetConfigString("log.level"), + Path: utils.GetConfigString("log.path"), + KeepDays: utils.GetConfigInt("log.keepDays"), + MaxSize: utils.GetConfigInt("log.maxSize"), + MaxBackups: utils.GetConfigInt("log.maxBackups"), + Compress: utils.GetConfigBool("log.compress"), + } + logx.SetUp(logConf) + logx.AddWriter(logx.NewWriter(os.Stdout)) + + listenOn := utils.GetConfigString("base.listenOn") + mode := utils.GetConfigString("base.mode") + serviceName := utils.GetConfigString("base.name") + + if err := discovery.Init(discovery.Config{ + Hosts: c.Nacos.Hosts, + NamespaceId: c.Nacos.NamespaceId, + Group: c.Nacos.Group, + }); err != nil { + logx.Errorf("nacos init: %v", err) + os.Exit(1) + } + + port, err := listenPort(listenOn) + if err != nil { + logx.Errorf("parse ListenOn: %v", err) + os.Exit(1) + } + + if err := discovery.Register(discovery.Instance{ + ServiceName: serviceName, + IP: c.Nacos.RegisterIP, + Port: port, + Group: c.Nacos.Group, + }); err != nil { + logx.Errorf("nacos register: %v", err) + os.Exit(1) + } + logx.Infof("服务注册成功: %s:%d", c.Nacos.RegisterIP, port) + + defer func() { + if err := discovery.Deregister(); err != nil { + logx.Errorf("nacos deregister: %v", err) + } + }() + + db, err := mysql.New(mysql.Config{ + Host: utils.GetConfigString("mysql.host"), + Port: utils.GetConfigInt("mysql.port"), + User: utils.GetConfigString("mysql.user"), + Password: utils.GetConfigString("mysql.password"), + Database: utils.GetConfigString("mysql.database"), + Charset: utils.GetConfigString("mysql.charset"), + Prefix: utils.GetConfigString("mysql.prefix"), + ReadHost: utils.GetConfigString("mysql_read.host"), + ReadPort: utils.GetConfigInt("mysql_read.port"), + ReadUser: utils.GetConfigString("mysql_read.user"), + ReadPassword: utils.GetConfigString("mysql_read.password"), + ReadDatabase: utils.GetConfigString("mysql_read.database"), + }) + if err != nil { + logx.Errorf("mysql init: %v", err) + os.Exit(1) + } + + if err := redis.Init(redis.Config{ + Host: utils.GetConfigString("redis.host"), + Port: utils.GetConfigInt("redis.port"), + Password: utils.GetConfigString("redis.password"), + DB: utils.GetConfigInt("redis.db"), + }); err != nil { + logx.Errorf("redis init: %v", err) + os.Exit(1) + } + + debug := utils.GetConfigBool("mysql.debug") + modelbase.Init(db, modelbase.Config{Prefix: utils.GetConfigString("mysql.prefix"), Debug: debug}) + + rpcConf := zrpc.RpcServerConf{ + ListenOn: listenOn, + } + rpcConf.Mode = mode + + ctx := svc.NewServiceContext(c, db) + + s := zrpc.MustNewServer(rpcConf, func(grpcServer *grpc.Server) { + support.RegisterSupportServer(grpcServer, server.NewSupportServer(ctx)) + if mode == service.DevMode || mode == service.TestMode { + reflection.Register(grpcServer) + } + }) + defer s.Stop() + + s.AddUnaryInterceptors(validate.UnaryServerInterceptor(validate.MustNew())) + + logx.Infof("Starting rpc server at %s...", listenOn) + s.Start() +} + +func listenPort(listenOn string) (uint64, error) { + _, portStr, err := net.SplitHostPort(listenOn) + if err != nil { + return 0, err + } + return strconv.ParseUint(portStr, 10, 64) +} diff --git a/services/support/supportClient/support.go b/services/support/supportClient/support.go new file mode 100644 index 0000000..fa1e502 --- /dev/null +++ b/services/support/supportClient/support.go @@ -0,0 +1,32 @@ +package supportClient + +import ( + "context" + + support "lone-services/rpc/support/pb" + + "github.com/zeromicro/go-zero/zrpc" + "google.golang.org/grpc" +) + +type ( + CallReq = support.CallReq + Response = support.Response + + Support interface { + Call(ctx context.Context, in *CallReq, opts ...grpc.CallOption) (*Response, error) + } + + defaultSupport struct { + cli zrpc.Client + } +) + +func NewSupport(cli zrpc.Client) Support { + return &defaultSupport{cli: cli} +} + +func (m *defaultSupport) Call(ctx context.Context, in *CallReq, opts ...grpc.CallOption) (*Response, error) { + client := support.NewSupportClient(m.cli.Conn()) + return client.Call(ctx, in, opts...) +} diff --git a/services/support/validator/call.go b/services/support/validator/call.go new file mode 100644 index 0000000..fc4c122 --- /dev/null +++ b/services/support/validator/call.go @@ -0,0 +1,25 @@ +package validator + +import "lone-services/pkg/validate" + +type CallValidator struct { + Action string `validate:"required,max=128"` + Version string `validate:"required,max=32"` + Request string `validate:"max=1024"` + A string `validate:"required,max=128"` + S string `validate:"required,max=128"` +} + +func (p CallValidator) GetMessage() validate.ValidatorMessages { + return validate.ValidatorMessages{ + "Action.required": "action不能为空", + "Action.max": "action过长", + "Version.required": "version不能为空", + "Version.max": "version过长", + "Request.max": "request过长", + "A.required": "accessKeyId不能为空", + "A.max": "accessKeyId过长", + "S.required": "accessKeySecret不能为空", + "S.max": "accessKeySecret过长", + } +}