feat: 完成用户微服务 gRPC 实现,包含数据库与服务启动逻辑

This commit is contained in:
fish
2026-03-28 18:11:15 +08:00
parent 911654b60b
commit 27da6939f4
6 changed files with 300 additions and 0 deletions

View File

@@ -17,3 +17,11 @@ services:
- "8080:8080" - "8080:8080"
environment: environment:
- GO_ENV=production - GO_ENV=production
user-svc:
build:
context: ./services/user-svc
ports:
- "50051:50051"
environment:
- GO_ENV=production

View File

@@ -0,0 +1,23 @@
FROM golang:1.26.1-alpine3.23 AS builder
WORKDIR /app
COPY go.mod ./
COPY main.go ./
COPY user.proto ./
RUN go mod download
RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
RUN go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
RUN protoc --go_out=. --go-grpc_out=. user.proto
RUN go build -o user-svc main.go
FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/user-svc .
EXPOSE 50051
CMD ["./user-svc"]

View File

@@ -0,0 +1,9 @@
#!/bin/bash
# 生成 gRPC 代码
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
protoc --go_out=. --go-grpc_out=. user.proto
echo "gRPC code generated successfully!"

View File

@@ -0,0 +1,40 @@
module user-svc
go 1.26
require (
google.golang.org/grpc v1.58.0
gorm.io/driver/sqlite v1.5.4
gorm.io/gorm v1.25.5
google.golang.org/protobuf v1.31.0
)
require (
github.com/bytedance/sonic v1.9.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-sqlite3 v1.14.17 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.9.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.12.0 // indirect
yaml.v3 v3.0.1 // indirect
)

View File

@@ -0,0 +1,156 @@
package main
import (
"context"
"fmt"
"log"
"net"
"user-svc/user"
"google.golang.org/grpc"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
type User struct {
ID string `json:"id" gorm:"primaryKey"`
Name string `json:"name"`
Email string `json:"email"`
Password string `json:"password"`
}
type UserServiceServer struct {
user.UnimplementedUserServiceServer
db *gorm.DB
}
func (s *UserServiceServer) CreateUser(ctx context.Context, req *user.CreateUserRequest) (*user.CreateUserResponse, error) {
newUser := User{
ID: fmt.Sprintf("%d", len(s.getAllUsers())+1),
Name: req.Name,
Email: req.Email,
Password: req.Password,
}
result := s.db.Create(&newUser)
if result.Error != nil {
return nil, result.Error
}
return &user.CreateUserResponse{
User: &user.User{
Id: newUser.ID,
Name: newUser.Name,
Email: newUser.Email,
Password: newUser.Password,
},
}, nil
}
func (s *UserServiceServer) GetUser(ctx context.Context, req *user.GetUserRequest) (*user.GetUserResponse, error) {
var user User
result := s.db.First(&user, "id = ?", req.Id)
if result.Error != nil {
return nil, result.Error
}
return &user.GetUserResponse{
User: &user.User{
Id: user.ID,
Name: user.Name,
Email: user.Email,
Password: user.Password,
},
}, nil
}
func (s *UserServiceServer) ListUsers(ctx context.Context, req *user.ListUsersRequest) (*user.ListUsersResponse, error) {
users := s.getAllUsers()
var protoUsers []*user.User
for _, u := range users {
protoUsers = append(protoUsers, &user.User{
Id: u.ID,
Name: u.Name,
Email: u.Email,
Password: u.Password,
})
}
return &user.ListUsersResponse{
Users: protoUsers,
}, nil
}
func (s *UserServiceServer) UpdateUser(ctx context.Context, req *user.UpdateUserRequest) (*user.UpdateUserResponse, error) {
var user User
result := s.db.First(&user, "id = ?", req.Id)
if result.Error != nil {
return nil, result.Error
}
if req.Name != "" {
user.Name = req.Name
}
if req.Email != "" {
user.Email = req.Email
}
if req.Password != "" {
user.Password = req.Password
}
s.db.Save(&user)
return &user.UpdateUserResponse{
User: &user.User{
Id: user.ID,
Name: user.Name,
Email: user.Email,
Password: user.Password,
},
}, nil
}
func (s *UserServiceServer) DeleteUser(ctx context.Context, req *user.DeleteUserRequest) (*user.DeleteUserResponse, error) {
result := s.db.Delete(&User{}, "id = ?", req.Id)
if result.Error != nil {
return nil, result.Error
}
return &user.DeleteUserResponse{
Success: true,
}, nil
}
func (s *UserServiceServer) getAllUsers() []User {
var users []User
s.db.Find(&users)
return users
}
func initDB() *gorm.DB {
db, err := gorm.Open(sqlite.Open("users.db"), &gorm.Config{})
if err != nil {
log.Fatalf("Failed to connect to database: %v", err)
}
db.AutoMigrate(&User{})
return db
}
func main() {
db := initDB()
server := grpc.NewServer()
user.RegisterUserServiceServer(server, &UserServiceServer{db: db})
listener, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
log.Println("User service starting on port 50051...")
if err := server.Serve(listener); err != nil {
log.Fatalf("Failed to serve: %v", err)
}
}

View File

@@ -0,0 +1,64 @@
syntax = "proto3";
package user;
option go_package = "./user";
message User {
string id = 1;
string name = 2;
string email = 3;
string password = 4;
}
message CreateUserRequest {
string name = 1;
string email = 2;
string password = 3;
}
message CreateUserResponse {
User user = 1;
}
message GetUserRequest {
string id = 1;
}
message GetUserResponse {
User user = 1;
}
message ListUsersRequest {
}
message ListUsersResponse {
repeated User users = 1;
}
message UpdateUserRequest {
string id = 1;
string name = 2;
string email = 3;
string password = 4;
}
message UpdateUserResponse {
User user = 1;
}
message DeleteUserRequest {
string id = 1;
}
message DeleteUserResponse {
bool success = 1;
}
service UserService {
rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
rpc GetUser(GetUserRequest) returns (GetUserResponse);
rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
rpc UpdateUser(UpdateUserRequest) returns (UpdateUserResponse);
rpc DeleteUser(DeleteUserRequest) returns (DeleteUserResponse);
}