diff --git a/server/go.mod b/server/go.mod index b5e23f9..011259d 100644 --- a/server/go.mod +++ b/server/go.mod @@ -6,6 +6,8 @@ require ( github.com/go-sql-driver/mysql v1.7.1 github.com/tjfoc/gmsm v1.4.1 github.com/xuri/excelize/v2 v2.8.1 + google.golang.org/grpc v1.60.1 + google.golang.org/protobuf v1.32.0 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/server/internal/config/config.go b/server/internal/config/config.go index 161de03..e8844b4 100644 --- a/server/internal/config/config.go +++ b/server/internal/config/config.go @@ -17,14 +17,27 @@ type DB struct { Name string `yaml:"name"` } +type GRPCServer struct { + Host string `yaml:"host"` + Port string `yaml:"port"` +} + +func (g GRPCServer) Address() string { + if g.Host == "" || g.Port == "" { + return "" + } + return g.Host + ":" + g.Port +} + type App struct { - Port string `yaml:"port"` - MarketingDB DB `yaml:"marketing_db"` - MarketingAuthorizationDB DB `yaml:"marketing_authorization_db"` - MarketingResellerDB DB `yaml:"marketing_reseller_db"` - YMTDB DB `yaml:"ymt_db"` - YMTTestDB DB `yaml:"ymt_test_db"` - YMTKeyDecryptKeyB64 string `yaml:"ymt_key_decrypt_key_b64"` + Port string `yaml:"port"` + MarketingDB DB `yaml:"marketing_db"` + MarketingAuthorizationDB DB `yaml:"marketing_authorization_db"` + MarketingResellerDB DB `yaml:"marketing_reseller_db"` + YMTDB DB `yaml:"ymt_db"` + YMTTestDB DB `yaml:"ymt_test_db"` + YMTKeyDecryptKeyB64 string `yaml:"ymt_key_decrypt_key_b64"` + GRPCServer GRPCServer `yaml:"grpc_server"` } type root struct { @@ -84,6 +97,13 @@ func Load() App { if v := os.Getenv("YMT_TEST_DB_NAME"); v != "" { cfg.YMTTestDB.Name = v } + // gRPC server env overrides + if v := os.Getenv("GRPC_SERVER_HOST"); v != "" { + cfg.GRPCServer.Host = v + } + if v := os.Getenv("GRPC_SERVER_PORT"); v != "" { + cfg.GRPCServer.Port = v + } return cfg } diff --git a/server/internal/grpc/user_client.go b/server/internal/grpc/user_client.go new file mode 100644 index 0000000..c0180c0 --- /dev/null +++ b/server/internal/grpc/user_client.go @@ -0,0 +1,93 @@ +package grpc + +import ( + "context" + "fmt" + "log" + "sync" + "time" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/keepalive" + + // 使用生成的 proto 代码 + // 注意:需要先运行 cd grpc && make generate 生成代码 + userv1 "server/grpc/user/userv1" +) + +var ( + userClientOnce sync.Once + userClient *UserClient +) + +type UserClient struct { + conn *grpc.ClientConn + addr string + client userv1.UserClient +} + +// NewUserClient 创建用户服务 gRPC 客户端 +func NewUserClient(addr string) (*UserClient, error) { + if addr == "" { + return nil, fmt.Errorf("gRPC server address is empty") + } + + var err error + userClientOnce.Do(func() { + opts := []grpc.DialOption{ + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithKeepaliveParams(keepalive.ClientParameters{ + Time: 10 * time.Second, + Timeout: 3 * time.Second, + PermitWithoutStream: true, + }), + } + + conn, dialErr := grpc.Dial(addr, opts...) + if dialErr != nil { + err = fmt.Errorf("failed to connect to gRPC server %s: %w", addr, dialErr) + return + } + + client := userv1.NewUserClient(conn) + userClient = &UserClient{ + conn: conn, + addr: addr, + client: client, + } + log.Printf("gRPC user client connected to %s", addr) + }) + + if err != nil { + return nil, err + } + + return userClient, nil +} + +// SimpleListAllUser 调用 SimpleListAllUser gRPC 方法 +func (c *UserClient) SimpleListAllUser(ctx context.Context, keyword string) (*userv1.SimpleListAllUserResp, error) { + req := &userv1.SimpleListAllUserReq{ + Keyword: keyword, + } + resp, err := c.client.SimpleListAllUser(ctx, req) + if err != nil { + return nil, fmt.Errorf("gRPC SimpleListAllUser failed: %w", err) + } + return resp, nil +} + +// Close 关闭连接 +func (c *UserClient) Close() error { + if c.conn != nil { + return c.conn.Close() + } + return nil +} + +// GetConn 获取 gRPC 连接(用于直接调用生成的客户端) +func (c *UserClient) GetConn() *grpc.ClientConn { + return c.conn +} +