微服務 library-user-service ,用戶管理服務,提供用戶管理的 Restful 介面,主要實作用戶注冊、根據用戶ID或者 email 查詢用戶、查詢用戶所借書籍等功能,
完整代碼:
https://github.com/Justin02180218/micro-kit
包結構說明

-
dao:資料訪問層
-
dto:資料傳輸層
-
models:資料庫表映射層
-
service:業務邏輯層
-
endpoint:go-kit 的概念,將服務提供的每一個方法封裝成一個 endpoint,
-
transport:go-kit 的概念,協議傳輸層,支持 grpc,thrift,http 等協議,這里與gin web框架結合使用,
-
user.yaml:服務使用的組態檔
-
main.go:服務啟動程式
代碼實作
資料庫表
首先在 library 資料庫建立 user 表
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT '',
`password` varchar(255) DEFAULT '',
`email` varchar(255) DEFAULT '',
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
models層
對應在 models 層創建 user.go,定義與表 user 對應的 User struct:
type User struct {
ID uint64 `gorm:"primary_key" json:"id" form:"id"`
CreatedAt time.Time `form:"created_at" json:"created_at"`
UpdatedAt time.Time `form:"updated_at" json:"updated_at"`
Username string
Password string
Email string
}
dao層
在 dao 層創建與資料庫互動的 user_dao.go
定義 UserDao 介面及實作:
type UserDao interface {
SelectByID(id uint64) (*models.User, error)
SelectByEmail(email string) (*models.User, error)
Save(user *models.User) error
}
type UserDaoImpl struct{}
func NewUserDaoImpl() UserDao {
return &UserDaoImpl{}
}
-
SelectByID:根據用戶ID查詢用戶資訊
-
SelectByEail:根據用戶email查詢用戶資訊
-
Save:保存用戶資訊
UserDao 介面的函式實作:
func (u *UserDaoImpl) SelectByID(id uint64) (*models.User, error) {
user := &models.User{}
err := databases.DB.Where("id = ?", id).First(user).Error
if err != nil {
return nil, err
}
return user, nil
}
func (u *UserDaoImpl) SelectByEmail(email string) (*models.User, error) {
user := &models.User{}
err := databases.DB.Where("email = ?", email).First(user).Error
if err != nil {
return nil, err
}
return user, nil
}
func (u *UserDaoImpl) Save(user *models.User) error {
return databases.DB.Create(user).Error
}
dto層
在 dto 層的 user_dto.go 中創建用于資料傳輸的struct UserInfo 和 RegisterUser:
type UserInfo struct {
ID uint64 `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
}
type RegisterUser struct {
Username string
Password string
Email string
}
service層
在 service 層創建 user_service.go
定義 UserService 介面及實作:
type UserService interface {
Register(ctx context.Context, vo *dto.RegisterUser) (*dto.UserInfo, error)
FindByID(ctx context.Context, id uint64) (*dto.UserInfo, error)
FindByEmail(ctx context.Context, email string) (*dto.UserInfo, error)
}
type UserServiceImpl struct {
userDao dao.UserDao
}
func NewUserServiceImpl(userDao dao.UserDao) UserService {
return &UserServiceImpl{
userDao: userDao,
}
}
UserService 介面的函式實作:
func (u *UserServiceImpl) Register(ctx context.Context, vo *dto.RegisterUser) (*dto.UserInfo, error) {
user, err := u.userDao.SelectByEmail(vo.Email)
if user != nil {
log.Println("User is already exist!")
return &dto.UserInfo{}, ErrUserExisted
}
if err == gorm.ErrRecordNotFound || err == nil {
newUser := &models.User{
Username: vo.Username,
Password: vo.Password,
Email: vo.Email,
}
err = u.userDao.Save(newUser)
if err != nil {
return nil, ErrRegistering
}
return &dto.UserInfo{
ID: newUser.ID,
Username: newUser.Username,
Email: newUser.Email,
}, nil
}
return nil, err
}
func (u *UserServiceImpl) FindByID(ctx context.Context, id uint64) (*dto.UserInfo, error) {
user, err := u.userDao.SelectByID(id)
if err != nil {
return nil, ErrNotFound
}
return &dto.UserInfo{
ID: user.ID,
Username: user.Username,
Email: user.Email,
}, nil
}
func (u *UserServiceImpl) FindByEmail(ctx context.Context, email string) (*dto.UserInfo, error) {
user, err := u.userDao.SelectByEmail(email)
if err != nil {
return nil, ErrNotFound
}
return &dto.UserInfo{
ID: user.ID,
Username: user.Username,
Email: user.Email,
}, nil
}
endpoint層
在 endpoint 層創建 user_endpoint.go,
定義 UserEndpoints struct,每一個請求對應一個endpoint,
type UserEndpoints struct {
RegisterEndpoint endpoint.Endpoint
FindByIDEndpoint endpoint.Endpoint
FindByEmailEndpoint endpoint.Endpoint
}
創建各endpoint
func MakeRegisterEndpoint(svc service.UserService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
req := request.(*dto.RegisterUser)
user, err := svc.Register(ctx, req)
if err != nil {
return nil, err
}
return user, nil
}
}
func MakeFindByIDEndpoint(svc service.UserService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
id, _ := strconv.ParseUint(request.(string), 10, 64)
user, err := svc.FindByID(ctx, id)
if err != nil {
return nil, err
}
return user, nil
}
}
func MakeFindByEmailEndpoint(svc service.UserService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
email := request.(string)
user, err := svc.FindByEmail(ctx, email)
if err != nil {
return nil, err
}
return user, nil
}
}
transport層
請求轉發我們采用 gin web 框架與 go-kit transport 相結合的方式,因此在 http_util.go 中定 義 Router ,回傳 gin.Engine
func NewRouter(mode string) *gin.Engine {
gin.SetMode(mode)
r := gin.New()
r.Use(gin.Recovery())
return r
}
在 user_transport.go 中定義 NewHttpHandler 回傳 gin.Engine,gin 的請求處理 handler 由 go-kit 的http.NewServer 處理,
func NewHttpHandler(ctx context.Context, endpoints *endpoint.UserEndpoints) *gin.Engine {
r := utils.NewRouter(ctx.Value("ginMod").(string))
e := r.Group("/api/v1")
{
e.POST("register", func(c *gin.Context) {
kithttp.NewServer(
endpoints.RegisterEndpoint,
decodeRegisterRequest,
utils.EncodeJsonResponse,
).ServeHTTP(c.Writer, c.Request)
})
e.GET("findByID", func(c *gin.Context) {
kithttp.NewServer(
endpoints.FindByIDEndpoint,
decodeFindByIDRequest,
utils.EncodeJsonResponse,
).ServeHTTP(c.Writer, c.Request)
})
e.GET("findByEmail", func(c *gin.Context) {
kithttp.NewServer(
endpoints.FindByEmailEndpoint,
decodeFindByEmailRequest,
utils.EncodeJsonResponse,
).ServeHTTP(c.Writer, c.Request)
})
}
return r
}
啟動服務
補全 main.go
var confFile = flag.String("f", "user.yaml", "user config file")
func main() {
flag.Parse()
err := configs.Init(*confFile)
if err != nil {
panic(err)
}
err = databases.InitMySql(configs.Conf.MySQLConfig)
if err != nil {
fmt.Println("load mysql failed")
}
ctx := context.Background()
userDao := dao.NewUserDaoImpl()
userService := service.NewUserServiceImpl(userDao)
userEndpoints := &endpoint.UserEndpoints{
RegisterEndpoint: endpoint.MakeRegisterEndpoint(userService),
FindByIDEndpoint: endpoint.MakeFindByIDEndpoint(userService),
FindByEmailEndpoint: endpoint.MakeFindByEmailEndpoint(userService),
}
ctx = context.WithValue(ctx, "ginMod", configs.Conf.ServerConfig.Mode)
r := transport.NewHttpHandler(ctx, userEndpoints)
errChan := make(chan error)
go func() {
errChan <- r.Run(fmt.Sprintf(":%s", strconv.Itoa(configs.Conf.ServerConfig.Port)))
}()
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
errChan <- fmt.Errorf("%s", <-c)
}()
fmt.Println(<-errChan)
}
啟動
進入 library-user-service 目錄,執行 go run main.go 如圖:

服務成功啟動,監聽10086埠
介面測驗
使用postman進行介面測驗,在這里進行了 register 的介面測驗,結果如圖:

測驗成功,資料庫成功插入一條用戶記錄

下一篇文章,我們開始撰寫書籍管理微服務:library-book-service
完整代碼:
https://github.com/Justin02180218/micro-kit
更多【分布式專輯】【架構實戰專輯】系列文章,請關注公眾號

轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/299403.html
標籤:區塊鏈
上一篇:FIL會一直跌到哪個價格?FIL幣三年后合理價格是多少?
下一篇:【flutter】解決 dart:html 只支持 flutter_web 其他平臺編譯報錯 Avoid using web-only libraries outside Flutter web
