Jelajahi Sumber

feat: add response errors

chris 2 tahun lalu
induk
melakukan
1d049bfee6

+ 2 - 2
.gitignore

@@ -1,5 +1,5 @@
 storage
 .idea
 *.log
-scripts/docker-compose/conf
-scripts/docker-compose/data
+deploy/docker-compose/conf
+deploy/docker-compose/data

+ 9 - 2
Makefile

@@ -3,6 +3,12 @@ init:
 	go install github.com/google/wire/cmd/wire@latest
 	go install github.com/golang/mock/mockgen@latest
 
+.PHONY: bootstrap
+bootstrap:
+	cd ./deploy/docker-compose && docker compose up -d && cd ../../
+	go run ./cmd/migration
+	nunu run ./cmd/server
+
 .PHONY: mock
 mock:
 	mockgen -source=internal/service/user.go -destination test/mocks/service/user.go
@@ -15,8 +21,9 @@ test:
 
 .PHONY: build
 build:
-	go build -ldflags="-s -w" -o ./bin/server ./cmd/server/...
+	go build -ldflags="-s -w" -o ./bin/server ./cmd/server
 
 .PHONY: docker
 docker:
-	docker build -f deploy/build/Dockerfile --build-arg APP_RELATIVE_PATH=./cmd/job/... -t 1.1.1.1:5000/demo-api:v1 .
+	docker build -f deploy/build/Dockerfile --build-arg APP_RELATIVE_PATH=./cmd/job -t 1.1.1.1:5000/demo-job:v1 .
+	docker run --rm -i 1.1.1.1:5000/demo-job:v1

+ 2 - 1
cmd/job/main.go

@@ -1,6 +1,7 @@
 package main
 
 import (
+	"github.com/go-nunu/nunu-layout-advanced/cmd/job/wire"
 	"github.com/go-nunu/nunu-layout-advanced/pkg/config"
 	"github.com/go-nunu/nunu-layout-advanced/pkg/log"
 )
@@ -10,7 +11,7 @@ func main() {
 	logger := log.NewLog(conf)
 	logger.Info("start")
 
-	app, cleanup, err := newApp(conf, logger)
+	app, cleanup, err := wire.NewApp(conf, logger)
 	if err != nil {
 		panic(err)
 	}

+ 2 - 2
cmd/job/wire.go → cmd/job/wire/wire.go

@@ -1,7 +1,7 @@
 //go:build wireinject
 // +build wireinject
 
-package main
+package wire
 
 import (
 	"github.com/go-nunu/nunu-layout-advanced/internal/job"
@@ -12,7 +12,7 @@ import (
 
 var JobSet = wire.NewSet(job.NewJob)
 
-func newApp(*viper.Viper, *log.Logger) (*job.Job, func(), error) {
+func NewApp(*viper.Viper, *log.Logger) (*job.Job, func(), error) {
 	panic(wire.Build(
 		JobSet,
 	))

+ 2 - 2
cmd/job/wire_gen.go → cmd/job/wire/wire_gen.go

@@ -4,7 +4,7 @@
 //go:build !wireinject
 // +build !wireinject
 
-package main
+package wire
 
 import (
 	"github.com/go-nunu/nunu-layout-advanced/internal/job"
@@ -15,7 +15,7 @@ import (
 
 // Injectors from wire.go:
 
-func newApp(viperViper *viper.Viper, logger *log.Logger) (*job.Job, func(), error) {
+func NewApp(viperViper *viper.Viper, logger *log.Logger) (*job.Job, func(), error) {
 	jobJob := job.NewJob(logger)
 	return jobJob, func() {
 	}, nil

+ 1 - 1
cmd/migration/migration.go → cmd/migration/internal/migration.go

@@ -1,4 +1,4 @@
-package main
+package internal
 
 import (
 	"github.com/go-nunu/nunu-layout-advanced/internal/model"

+ 2 - 1
cmd/migration/main.go

@@ -1,6 +1,7 @@
 package main
 
 import (
+	"github.com/go-nunu/nunu-layout-advanced/cmd/migration/wire"
 	"github.com/go-nunu/nunu-layout-advanced/pkg/config"
 	"github.com/go-nunu/nunu-layout-advanced/pkg/log"
 )
@@ -9,7 +10,7 @@ func main() {
 	conf := config.NewConfig()
 	logger := log.NewLog(conf)
 
-	app, cleanup, err := newApp(conf, logger)
+	app, cleanup, err := wire.NewApp(conf, logger)
 	if err != nil {
 		panic(err)
 	}

+ 4 - 3
cmd/migration/wire.go → cmd/migration/wire/wire.go

@@ -1,9 +1,10 @@
 //go:build wireinject
 // +build wireinject
 
-package main
+package wire
 
 import (
+	"github.com/go-nunu/nunu-layout-advanced/cmd/migration/internal"
 	"github.com/go-nunu/nunu-layout-advanced/internal/repository"
 	"github.com/go-nunu/nunu-layout-advanced/pkg/log"
 	"github.com/google/wire"
@@ -17,9 +18,9 @@ var RepositorySet = wire.NewSet(
 	repository.NewUserRepository,
 )
 
-func newApp(*viper.Viper, *log.Logger) (*Migrate, func(), error) {
+func NewApp(*viper.Viper, *log.Logger) (*internal.Migrate, func(), error) {
 	panic(wire.Build(
 		RepositorySet,
-		NewMigrate,
+		internal.NewMigrate,
 	))
 }

+ 4 - 3
cmd/migration/wire_gen.go → cmd/migration/wire/wire_gen.go

@@ -4,9 +4,10 @@
 //go:build !wireinject
 // +build !wireinject
 
-package main
+package wire
 
 import (
+	"github.com/go-nunu/nunu-layout-advanced/cmd/migration/internal"
 	"github.com/go-nunu/nunu-layout-advanced/internal/repository"
 	"github.com/go-nunu/nunu-layout-advanced/pkg/log"
 	"github.com/google/wire"
@@ -15,9 +16,9 @@ import (
 
 // Injectors from wire.go:
 
-func newApp(viperViper *viper.Viper, logger *log.Logger) (*Migrate, func(), error) {
+func NewApp(viperViper *viper.Viper, logger *log.Logger) (*internal.Migrate, func(), error) {
 	db := repository.NewDB(viperViper)
-	migrate := NewMigrate(db, logger)
+	migrate := internal.NewMigrate(db, logger)
 	return migrate, func() {
 	}, nil
 }

+ 2 - 1
cmd/server/main.go

@@ -2,6 +2,7 @@ package main
 
 import (
 	"fmt"
+	"github.com/go-nunu/nunu-layout-advanced/cmd/server/wire"
 	"github.com/go-nunu/nunu-layout-advanced/pkg/config"
 	"github.com/go-nunu/nunu-layout-advanced/pkg/http"
 	"github.com/go-nunu/nunu-layout-advanced/pkg/log"
@@ -12,7 +13,7 @@ func main() {
 	conf := config.NewConfig()
 	logger := log.NewLog(conf)
 
-	servers, cleanup, err := newApp(conf, logger)
+	servers, cleanup, err := wire.NewApp(conf, logger)
 	if err != nil {
 		panic(err)
 	}

+ 2 - 2
cmd/server/wire.go → cmd/server/wire/wire.go

@@ -1,7 +1,7 @@
 //go:build wireinject
 // +build wireinject
 
-package main
+package wire
 
 import (
 	"github.com/go-nunu/nunu-layout-advanced/internal/handler"
@@ -32,7 +32,7 @@ var RepositorySet = wire.NewSet(
 	repository.NewUserRepository,
 )
 
-func newApp(*viper.Viper, *log.Logger) (*server.Server, func(), error) {
+func NewApp(*viper.Viper, *log.Logger) (*server.Server, func(), error) {
 	panic(wire.Build(
 		RepositorySet,
 		ServiceSet,

+ 2 - 2
cmd/server/wire_gen.go → cmd/server/wire/wire_gen.go

@@ -4,7 +4,7 @@
 //go:build !wireinject
 // +build !wireinject
 
-package main
+package wire
 
 import (
 	"github.com/go-nunu/nunu-layout-advanced/internal/handler"
@@ -20,7 +20,7 @@ import (
 
 // Injectors from wire.go:
 
-func newApp(viperViper *viper.Viper, logger *log.Logger) (*server.Server, func(), error) {
+func NewApp(viperViper *viper.Viper, logger *log.Logger) (*server.Server, func(), error) {
 	jwtJWT := jwt.NewJwt(viperViper)
 	handlerHandler := handler.NewHandler(logger)
 	sidSid := sid.NewSid()

+ 1 - 1
scripts/build/Dockerfile → deploy/build/Dockerfile

@@ -3,7 +3,7 @@ RUN set -eux && sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk
 
 ARG APP_RELATIVE_PATH
 
-COPY ../../deploy /data/app
+COPY .. /data/app
 WORKDIR /data/app
 
 RUN rm -rf /data/app/bin/

+ 0 - 0
scripts/docker-compose/docker-compose.yml → deploy/docker-compose/docker-compose.yml


+ 13 - 14
internal/handler/user.go

@@ -3,9 +3,8 @@ package handler
 import (
 	"github.com/gin-gonic/gin"
 	"github.com/go-nunu/nunu-layout-advanced/internal/pkg/request"
+	"github.com/go-nunu/nunu-layout-advanced/internal/pkg/response"
 	"github.com/go-nunu/nunu-layout-advanced/internal/service"
-	"github.com/go-nunu/nunu-layout-advanced/pkg/helper/resp"
-	"github.com/pkg/errors"
 	"net/http"
 )
 
@@ -40,32 +39,32 @@ type userHandler struct {
 func (h *userHandler) Register(ctx *gin.Context) {
 	req := new(request.RegisterRequest)
 	if err := ctx.ShouldBindJSON(req); err != nil {
-		resp.HandleError(ctx, http.StatusBadRequest, 1, errors.Wrap(err, "invalid request").Error(), nil)
+		response.HandleError(ctx, http.StatusBadRequest, response.ErrBadRequest, nil)
 		return
 	}
 
 	if err := h.userService.Register(ctx, req); err != nil {
-		resp.HandleError(ctx, http.StatusBadRequest, 1, errors.Wrap(err, "invalid request").Error(), nil)
+		response.HandleError(ctx, http.StatusBadRequest, response.ErrBadRequest, nil)
 		return
 	}
 
-	resp.HandleSuccess(ctx, nil)
+	response.HandleSuccess(ctx, nil)
 }
 
 func (h *userHandler) Login(ctx *gin.Context) {
 	var req request.LoginRequest
 	if err := ctx.ShouldBindJSON(&req); err != nil {
-		resp.HandleError(ctx, http.StatusBadRequest, 1, errors.Wrap(err, "invalid request").Error(), nil)
+		response.HandleError(ctx, http.StatusBadRequest, response.ErrBadRequest, nil)
 		return
 	}
 
 	token, err := h.userService.Login(ctx, &req)
 	if err != nil {
-		resp.HandleError(ctx, http.StatusUnauthorized, 1, err.Error(), nil)
+		response.HandleError(ctx, http.StatusUnauthorized, response.ErrUnauthorized, nil)
 		return
 	}
 
-	resp.HandleSuccess(ctx, gin.H{
+	response.HandleSuccess(ctx, gin.H{
 		"accessToken": token,
 	})
 }
@@ -73,17 +72,17 @@ func (h *userHandler) Login(ctx *gin.Context) {
 func (h *userHandler) GetProfile(ctx *gin.Context) {
 	userId := GetUserIdFromCtx(ctx)
 	if userId == "" {
-		resp.HandleError(ctx, http.StatusUnauthorized, 1, "unauthorized", nil)
+		response.HandleError(ctx, http.StatusUnauthorized, response.ErrUnauthorized, nil)
 		return
 	}
 
 	user, err := h.userService.GetProfile(ctx, userId)
 	if err != nil {
-		resp.HandleError(ctx, http.StatusBadRequest, 1, err.Error(), nil)
+		response.HandleError(ctx, http.StatusBadRequest, response.ErrBadRequest, nil)
 		return
 	}
 
-	resp.HandleSuccess(ctx, user)
+	response.HandleSuccess(ctx, user)
 }
 
 func (h *userHandler) UpdateProfile(ctx *gin.Context) {
@@ -91,14 +90,14 @@ func (h *userHandler) UpdateProfile(ctx *gin.Context) {
 
 	var req request.UpdateProfileRequest
 	if err := ctx.ShouldBindJSON(&req); err != nil {
-		resp.HandleError(ctx, http.StatusBadRequest, 1, errors.Wrap(err, "invalid request").Error(), nil)
+		response.HandleError(ctx, http.StatusBadRequest, response.ErrBadRequest, nil)
 		return
 	}
 
 	if err := h.userService.UpdateProfile(ctx, userId, &req); err != nil {
-		resp.HandleError(ctx, http.StatusBadRequest, 1, err.Error(), nil)
+		response.HandleError(ctx, http.StatusInternalServerError, response.ErrInternalServerError, nil)
 		return
 	}
 
-	resp.HandleSuccess(ctx, nil)
+	response.HandleSuccess(ctx, nil)
 }

+ 4 - 4
internal/pkg/middleware/jwt.go

@@ -2,7 +2,7 @@ package middleware
 
 import (
 	"github.com/gin-gonic/gin"
-	"github.com/go-nunu/nunu-layout-advanced/pkg/helper/resp"
+	"github.com/go-nunu/nunu-layout-advanced/internal/pkg/response"
 	"github.com/go-nunu/nunu-layout-advanced/pkg/jwt"
 	"github.com/go-nunu/nunu-layout-advanced/pkg/log"
 	"go.uber.org/zap"
@@ -13,11 +13,11 @@ func StrictAuth(j *jwt.JWT, logger *log.Logger) gin.HandlerFunc {
 	return func(ctx *gin.Context) {
 		tokenString := ctx.Request.Header.Get("Authorization")
 		if tokenString == "" {
-			logger.WithContext(ctx).Warn("请求未携带token,无权限访问", zap.Any("data", map[string]interface{}{
+			logger.WithContext(ctx).Warn("No token", zap.Any("data", map[string]interface{}{
 				"url":    ctx.Request.URL,
 				"params": ctx.Params,
 			}))
-			resp.HandleError(ctx, http.StatusUnauthorized, 1, "no token", nil)
+			response.HandleError(ctx, http.StatusUnauthorized, response.ErrUnauthorized, nil)
 			ctx.Abort()
 			return
 		}
@@ -28,7 +28,7 @@ func StrictAuth(j *jwt.JWT, logger *log.Logger) gin.HandlerFunc {
 				"url":    ctx.Request.URL,
 				"params": ctx.Params,
 			}))
-			resp.HandleError(ctx, http.StatusUnauthorized, 1, err.Error(), nil)
+			response.HandleError(ctx, http.StatusUnauthorized, response.ErrUnauthorized, nil)
 			ctx.Abort()
 			return
 		}

+ 3 - 3
internal/pkg/middleware/sign.go

@@ -2,8 +2,8 @@ package middleware
 
 import (
 	"github.com/gin-gonic/gin"
+	"github.com/go-nunu/nunu-layout-advanced/internal/pkg/response"
 	"github.com/go-nunu/nunu-layout-advanced/pkg/helper/md5"
-	"github.com/go-nunu/nunu-layout-advanced/pkg/helper/resp"
 	"github.com/go-nunu/nunu-layout-advanced/pkg/log"
 	"github.com/spf13/viper"
 	"net/http"
@@ -18,7 +18,7 @@ func SignMiddleware(logger *log.Logger, conf *viper.Viper) gin.HandlerFunc {
 		for _, header := range requiredHeaders {
 			value, ok := ctx.Request.Header[header]
 			if !ok || len(value) == 0 {
-				resp.HandleError(ctx, http.StatusBadRequest, 1, "sign error.", nil)
+				response.HandleError(ctx, http.StatusBadRequest, response.ErrBadRequest, nil)
 				ctx.Abort()
 				return
 			}
@@ -44,7 +44,7 @@ func SignMiddleware(logger *log.Logger, conf *viper.Viper) gin.HandlerFunc {
 		str += conf.GetString("security.api_sign.app_security")
 
 		if ctx.Request.Header.Get("Sign") != strings.ToUpper(md5.Md5(str)) {
-			resp.HandleError(ctx, http.StatusBadRequest, 1, "sign error.", nil)
+			response.HandleError(ctx, http.StatusBadRequest, response.ErrBadRequest, nil)
 			ctx.Abort()
 			return
 		}

+ 13 - 0
internal/pkg/response/errors.go

@@ -0,0 +1,13 @@
+package response
+
+var (
+	// common errors
+	ErrSuccess             = newError(0, "ok")
+	ErrBadRequest          = newError(400, "Bad Request")
+	ErrUnauthorized        = newError(401, "Unauthorized")
+	ErrNotFound            = newError(404, "Not Found")
+	ErrInternalServerError = newError(500, "Internal Server Error")
+
+	// more biz errors
+	ErrUsernameAlreadyUse = newError(1001, "The username is already in use.")
+)

+ 48 - 0
internal/pkg/response/response.go

@@ -0,0 +1,48 @@
+package response
+
+import (
+	"errors"
+	"github.com/gin-gonic/gin"
+	"net/http"
+)
+
+type response struct {
+	Code    int         `json:"code"`
+	Message string      `json:"message"`
+	Data    interface{} `json:"data"`
+}
+
+func HandleSuccess(ctx *gin.Context, data interface{}) {
+	if data == nil {
+		data = map[string]string{}
+	}
+	resp := response{Code: ErrSuccess.Code, Message: ErrSuccess.Message, Data: data}
+	ctx.JSON(http.StatusOK, resp)
+}
+
+func HandleError(ctx *gin.Context, httpCode int, err error, data interface{}) {
+	if data == nil {
+		data = map[string]string{}
+	}
+	resp := response{Code: errorCodeMap[err], Message: err.Error(), Data: data}
+	ctx.JSON(httpCode, resp)
+}
+
+type Error struct {
+	Code    int
+	Message string
+}
+
+var errorCodeMap = map[error]int{}
+
+func newError(code int, msg string) *Error {
+	err := errors.New(msg)
+	errorCodeMap[err] = code
+	return &Error{
+		Code:    code,
+		Message: msg,
+	}
+}
+func (e Error) Error() string {
+	return e.Message
+}

+ 1 - 1
internal/repository/repository.go

@@ -28,7 +28,7 @@ func NewRepository(db *gorm.DB, rdb *redis.Client, logger *log.Logger) *Reposito
 func NewDB(conf *viper.Viper) *gorm.DB {
 	db, err := gorm.Open(mysql.Open(conf.GetString("data.mysql.user")), &gorm.Config{})
 	if err != nil {
-		panic(err)
+		panic(fmt.Sprintf("mysql error: %s", err.Error()))
 	}
 	return db
 }

+ 5 - 2
internal/repository/user.go

@@ -3,6 +3,7 @@ package repository
 import (
 	"context"
 	"github.com/go-nunu/nunu-layout-advanced/internal/model"
+	"github.com/go-nunu/nunu-layout-advanced/internal/pkg/response"
 	"github.com/pkg/errors"
 	"gorm.io/gorm"
 )
@@ -42,7 +43,9 @@ func (r *userRepository) Update(ctx context.Context, user *model.User) error {
 func (r *userRepository) GetByID(ctx context.Context, userId string) (*model.User, error) {
 	var user model.User
 	if err := r.db.Where("user_id = ?", userId).First(&user).Error; err != nil {
-
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			return nil, response.ErrNotFound
+		}
 		return nil, errors.Wrap(err, "failed to get user by ID")
 	}
 
@@ -52,7 +55,7 @@ func (r *userRepository) GetByID(ctx context.Context, userId string) (*model.Use
 func (r *userRepository) GetByUsername(ctx context.Context, username string) (*model.User, error) {
 	var user model.User
 	if err := r.db.Where("username = ?", username).First(&user).Error; err != nil {
-		if err == gorm.ErrRecordNotFound {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
 			return nil, nil
 		}
 		return nil, errors.Wrap(err, "failed to get user by username")

+ 3 - 3
internal/server/http.go

@@ -4,7 +4,7 @@ import (
 	"github.com/gin-gonic/gin"
 	"github.com/go-nunu/nunu-layout-advanced/internal/handler"
 	"github.com/go-nunu/nunu-layout-advanced/internal/pkg/middleware"
-	"github.com/go-nunu/nunu-layout-advanced/pkg/helper/resp"
+	"github.com/go-nunu/nunu-layout-advanced/internal/pkg/response"
 	"github.com/go-nunu/nunu-layout-advanced/pkg/jwt"
 	"github.com/go-nunu/nunu-layout-advanced/pkg/log"
 )
@@ -30,8 +30,8 @@ func NewServerHTTP(
 
 		noAuthRouter.GET("/", func(ctx *gin.Context) {
 			logger.WithContext(ctx).Info("hello")
-			resp.HandleSuccess(ctx, map[string]interface{}{
-				"say": "Hi Nunu!",
+			response.HandleSuccess(ctx, map[string]interface{}{
+				":)": "Thank you for using nunu!",
 			})
 		})
 

+ 3 - 2
internal/service/user.go

@@ -4,6 +4,7 @@ import (
 	"context"
 	"github.com/go-nunu/nunu-layout-advanced/internal/model"
 	"github.com/go-nunu/nunu-layout-advanced/internal/pkg/request"
+	"github.com/go-nunu/nunu-layout-advanced/internal/pkg/response"
 	"github.com/go-nunu/nunu-layout-advanced/internal/repository"
 	"github.com/pkg/errors"
 	"golang.org/x/crypto/bcrypt"
@@ -30,9 +31,9 @@ type userService struct {
 }
 
 func (s *userService) Register(ctx context.Context, req *request.RegisterRequest) error {
-	// 检查用户名是否已存在
+	// check username
 	if user, err := s.userRepo.GetByUsername(ctx, req.Username); err == nil && user != nil {
-		return errors.New("username already exists")
+		return response.ErrUsernameAlreadyUse
 	}
 
 	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)

+ 0 - 28
pkg/helper/resp/resp.go

@@ -1,28 +0,0 @@
-package resp
-
-import (
-	"github.com/gin-gonic/gin"
-	"net/http"
-)
-
-type response struct {
-	Code    int         `json:"code"`
-	Message string      `json:"message"`
-	Data    interface{} `json:"data"`
-}
-
-func HandleSuccess(ctx *gin.Context, data interface{}) {
-	if data == nil {
-		data = map[string]string{}
-	}
-	resp := response{Code: 0, Message: "success", Data: data}
-	ctx.JSON(http.StatusOK, resp)
-}
-
-func HandleError(ctx *gin.Context, httpCode, code int, message string, data interface{}) {
-	if data == nil {
-		data = map[string]string{}
-	}
-	resp := response{Code: code, Message: message, Data: data}
-	ctx.JSON(httpCode, resp)
-}

+ 11 - 0
scripts/README.md

@@ -0,0 +1,11 @@
+# `/scripts`
+
+Scripts to perform various build, install, analysis, etc operations.
+
+These scripts keep the root level Makefile small and simple.
+
+Examples:
+
+* https://github.com/kubernetes/helm/tree/master/scripts
+* https://github.com/cockroachdb/cockroach/tree/master/scripts
+* https://github.com/hashicorp/terraform/tree/master/scripts