package client import ( "backend/msg" "backend/util" "context" "fmt" "time" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) const backendRPCTimeout = 3 * time.Second type backendCallMeta struct { AppID int ServerID int Method string Timeout time.Duration } type backendInvoker[T any] func(ctx context.Context, client msg.BackendClient) (T, error) type backendMiddleware[T any] func(meta backendCallMeta, next backendInvoker[T]) backendInvoker[T] type backendCodeResponse interface { GetCode() int32 GetMsg() string } func executeBackendCall[T any](appID, serverID int, method string, invoke backendInvoker[T], middlewares ...backendMiddleware[T]) (T, error) { var zero T serverConfig, err := util.GetServerConfig(appID, serverID) if err != nil { return zero, fmt.Errorf("failed to get server config: %w", err) } if serverConfig == nil { return zero, fmt.Errorf("server config not found for AppId %d and ServerId %d", appID, serverID) } addr := fmt.Sprintf("%s:%d", serverConfig.Host, serverConfig.GrpcPort) // TODO 测试 //addr = ":50051" conn, err := grpc.NewClient( addr, grpc.WithTransportCredentials(insecure.NewCredentials()), ) if err != nil { return zero, fmt.Errorf("failed to connect: %w", err) } defer conn.Close() meta := backendCallMeta{ AppID: appID, ServerID: serverID, Method: method, Timeout: backendRPCTimeout, } wrapped := invoke for index := len(middlewares) - 1; index >= 0; index-- { wrapped = middlewares[index](meta, wrapped) } ctx, cancel := context.WithTimeout(context.Background(), meta.Timeout) defer cancel() return wrapped(ctx, msg.NewBackendClient(conn)) } func wrapRPCError[T any](meta backendCallMeta, next backendInvoker[T]) backendInvoker[T] { return func(ctx context.Context, client msg.BackendClient) (T, error) { var zero T resp, err := next(ctx, client) if err != nil { return zero, fmt.Errorf("%s failed: %w", meta.Method, err) } return resp, nil } } func ensureBackendCode[T backendCodeResponse](meta backendCallMeta, next backendInvoker[T]) backendInvoker[T] { return func(ctx context.Context, client msg.BackendClient) (T, error) { var zero T resp, err := next(ctx, client) if err != nil { return zero, fmt.Errorf("%s failed: %w", meta.Method, err) } if resp.GetCode() != 0 { return zero, fmt.Errorf("%s failed with code: %d, msg: %s", meta.Method, resp.GetCode(), resp.GetMsg()) } return resp, nil } } func OrderShipping(AppId, ServerId int, req *msg.ReqOrderShipping) error { _, err := executeBackendCall( AppId, ServerId, "OrderShipping", func(ctx context.Context, client msg.BackendClient) (*msg.ResOrderShipping, error) { return client.OrderShipping(ctx, req) }, ensureBackendCode[*msg.ResOrderShipping], ) return err } func ReloadActivity(AppId, ServerId int, req *msg.ReqActivityCfgReload) error { _, err := executeBackendCall( AppId, ServerId, "ReloadActivity", func(ctx context.Context, client msg.BackendClient) (*msg.ResActivityCfgReload, error) { return client.ReloadActivity(ctx, req) }, ensureBackendCode[*msg.ResActivityCfgReload], ) return err } func UserDetail(AppId, ServerId int, req *msg.UserDetailParam) (*msg.ResUserDetailInfo, error) { resp, err := executeBackendCall( AppId, ServerId, "UserDetail", func(ctx context.Context, client msg.BackendClient) (*msg.ResUserDetail, error) { return client.UserDetail(ctx, req) }, ensureBackendCode[*msg.ResUserDetail], ) if err != nil { return nil, err } return resp.GetInfo(), nil } func GetServerInfo(AppId, ServerId int, req *msg.ReqServerInfo) (*msg.ResServerInfo, error) { return executeBackendCall( AppId, ServerId, "ServerInfo", func(ctx context.Context, client msg.BackendClient) (*msg.ResServerInfo, error) { return client.ServerInfo(ctx, req) }, wrapRPCError[*msg.ResServerInfo], ) } func PlayerGm(AppId, ServerId int, req *msg.ReqAdminGm) (*msg.ResAdminGm, error) { return executeBackendCall( AppId, ServerId, "PlayerGm", func(ctx context.Context, client msg.BackendClient) (*msg.ResAdminGm, error) { return client.PlayerGm(ctx, req) }, wrapRPCError[*msg.ResAdminGm], ) } func ServerReload(AppId, ServerId int, req *msg.ReqReload) (*msg.ResBackend, error) { return executeBackendCall( AppId, ServerId, "ServerReload", func(ctx context.Context, client msg.BackendClient) (*msg.ResBackend, error) { return client.ServerReload(ctx, req) }, wrapRPCError[*msg.ResBackend], ) } func BanPlayer(AppId, ServerId int, req *msg.ReqAdminBan) (*msg.ResBackend, error) { return executeBackendCall( AppId, ServerId, "BanPlayer", func(ctx context.Context, client msg.BackendClient) (*msg.ResBackend, error) { return client.BanPlayer(ctx, req) }, wrapRPCError[*msg.ResBackend], ) } func ReloadMail(AppId, ServerId int, req *msg.ReqReloadServerMail) (*msg.ResBackend, error) { return executeBackendCall( AppId, ServerId, "ReloadMail", func(ctx context.Context, client msg.BackendClient) (*msg.ResBackend, error) { return client.ReloadMail(ctx, req) }, wrapRPCError[*msg.ResBackend], ) } func MiniGame(AppId, ServerId int, req *msg.ReqMiniGame) (*msg.ResMiniGame, error) { return executeBackendCall( AppId, ServerId, "MiniGameReward", func(ctx context.Context, client msg.BackendClient) (*msg.ResMiniGame, error) { return client.MiniGame(ctx, req) }, wrapRPCError[*msg.ResMiniGame], ) }