init
This commit is contained in:
172
serve/internal/common/errors.go
Normal file
172
serve/internal/common/errors.go
Normal file
@@ -0,0 +1,172 @@
|
||||
package common
|
||||
|
||||
import "errors"
|
||||
|
||||
// 业务错误码定义
|
||||
const (
|
||||
// 通用错误码 1000-1999
|
||||
ErrCodeSuccess = 200
|
||||
ErrCodeBadRequest = 400
|
||||
ErrCodeUnauthorized = 401
|
||||
ErrCodeForbidden = 403
|
||||
ErrCodeNotFound = 404
|
||||
ErrCodeInternalError = 500
|
||||
ErrCodeValidationFailed = 1001
|
||||
ErrCodeDatabaseError = 1002
|
||||
ErrCodeRedisError = 1003
|
||||
|
||||
// 用户相关错误码 2000-2999
|
||||
ErrCodeUserNotFound = 2001
|
||||
ErrCodeUserExists = 2002
|
||||
ErrCodeInvalidPassword = 2003
|
||||
ErrCodeUserDisabled = 2004
|
||||
ErrCodeEmailExists = 2005
|
||||
ErrCodeUsernameExists = 2006
|
||||
ErrCodeInvalidEmail = 2007
|
||||
ErrCodePasswordTooWeak = 2008
|
||||
|
||||
// 认证相关错误码 3000-3999
|
||||
ErrCodeInvalidToken = 3001
|
||||
ErrCodeTokenExpired = 3002
|
||||
ErrCodeTokenNotFound = 3003
|
||||
ErrCodeRefreshTokenInvalid = 3004
|
||||
ErrCodeLoginRequired = 3005
|
||||
|
||||
// 词汇相关错误码 4000-4999
|
||||
ErrCodeVocabularyNotFound = 4001
|
||||
ErrCodeCategoryNotFound = 4002
|
||||
ErrCodeWordExists = 4003
|
||||
ErrCodeInvalidDifficulty = 4004
|
||||
|
||||
// 学习相关错误码 5000-5999
|
||||
ErrCodeProgressNotFound = 5001
|
||||
ErrCodeInvalidScore = 5002
|
||||
ErrCodeTestNotFound = 5003
|
||||
ErrCodeExerciseNotFound = 5004
|
||||
|
||||
// 文件相关错误码 6000-6999
|
||||
ErrCodeFileUploadFailed = 6001
|
||||
ErrCodeFileNotFound = 6002
|
||||
ErrCodeInvalidFileType = 6003
|
||||
ErrCodeFileTooLarge = 6004
|
||||
|
||||
// AI相关错误码 7000-7999
|
||||
ErrCodeAIServiceUnavailable = 7001
|
||||
ErrCodeAIProcessingFailed = 7002
|
||||
ErrCodeInvalidAudioFormat = 7003
|
||||
ErrCodeSpeechRecognitionFailed = 7004
|
||||
)
|
||||
|
||||
// 错误消息映射
|
||||
var ErrorMessages = map[int]string{
|
||||
// 通用错误
|
||||
ErrCodeSuccess: "操作成功",
|
||||
ErrCodeBadRequest: "请求参数错误",
|
||||
ErrCodeUnauthorized: "未授权访问",
|
||||
ErrCodeForbidden: "禁止访问",
|
||||
ErrCodeNotFound: "资源不存在",
|
||||
ErrCodeInternalError: "服务器内部错误",
|
||||
ErrCodeValidationFailed: "参数验证失败",
|
||||
ErrCodeDatabaseError: "数据库操作失败",
|
||||
ErrCodeRedisError: "缓存操作失败",
|
||||
|
||||
// 用户相关错误
|
||||
ErrCodeUserNotFound: "用户不存在",
|
||||
ErrCodeUserExists: "用户已存在",
|
||||
ErrCodeInvalidPassword: "密码错误",
|
||||
ErrCodeUserDisabled: "用户已被禁用",
|
||||
ErrCodeEmailExists: "邮箱已被注册",
|
||||
ErrCodeUsernameExists: "用户名已被注册",
|
||||
ErrCodeInvalidEmail: "邮箱格式不正确",
|
||||
ErrCodePasswordTooWeak: "密码强度不够",
|
||||
|
||||
// 认证相关错误
|
||||
ErrCodeInvalidToken: "无效的访问令牌",
|
||||
ErrCodeTokenExpired: "访问令牌已过期",
|
||||
ErrCodeTokenNotFound: "访问令牌不存在",
|
||||
ErrCodeRefreshTokenInvalid: "刷新令牌无效",
|
||||
ErrCodeLoginRequired: "请先登录",
|
||||
|
||||
// 词汇相关错误
|
||||
ErrCodeVocabularyNotFound: "词汇不存在",
|
||||
ErrCodeCategoryNotFound: "词汇分类不存在",
|
||||
ErrCodeWordExists: "词汇已存在",
|
||||
ErrCodeInvalidDifficulty: "无效的难度等级",
|
||||
|
||||
// 学习相关错误
|
||||
ErrCodeProgressNotFound: "学习进度不存在",
|
||||
ErrCodeInvalidScore: "无效的分数",
|
||||
ErrCodeTestNotFound: "测试不存在",
|
||||
ErrCodeExerciseNotFound: "练习不存在",
|
||||
|
||||
// 文件相关错误
|
||||
ErrCodeFileUploadFailed: "文件上传失败",
|
||||
ErrCodeFileNotFound: "文件不存在",
|
||||
ErrCodeInvalidFileType: "不支持的文件类型",
|
||||
ErrCodeFileTooLarge: "文件大小超出限制",
|
||||
|
||||
// AI相关错误
|
||||
ErrCodeAIServiceUnavailable: "AI服务暂不可用",
|
||||
ErrCodeAIProcessingFailed: "AI处理失败",
|
||||
ErrCodeInvalidAudioFormat: "不支持的音频格式",
|
||||
ErrCodeSpeechRecognitionFailed: "语音识别失败",
|
||||
}
|
||||
|
||||
// BusinessError 业务错误结构
|
||||
type BusinessError struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (e *BusinessError) Error() string {
|
||||
return e.Message
|
||||
}
|
||||
|
||||
// NewBusinessError 创建业务错误
|
||||
func NewBusinessError(code int, message string) *BusinessError {
|
||||
if message == "" {
|
||||
if msg, exists := ErrorMessages[code]; exists {
|
||||
message = msg
|
||||
} else {
|
||||
message = "未知错误"
|
||||
}
|
||||
}
|
||||
return &BusinessError{
|
||||
Code: code,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
// 预定义的常用错误
|
||||
var (
|
||||
ErrUserNotFound = NewBusinessError(ErrCodeUserNotFound, "")
|
||||
ErrUserExists = NewBusinessError(ErrCodeUserExists, "")
|
||||
ErrInvalidPassword = NewBusinessError(ErrCodeInvalidPassword, "")
|
||||
ErrInvalidToken = NewBusinessError(ErrCodeInvalidToken, "")
|
||||
ErrTokenExpired = NewBusinessError(ErrCodeTokenExpired, "")
|
||||
ErrEmailExists = NewBusinessError(ErrCodeEmailExists, "")
|
||||
ErrUsernameExists = NewBusinessError(ErrCodeUsernameExists, "")
|
||||
ErrVocabularyNotFound = NewBusinessError(ErrCodeVocabularyNotFound, "")
|
||||
ErrVocabularyTestNotFound = NewBusinessError(ErrCodeTestNotFound, "")
|
||||
ErrDatabaseError = NewBusinessError(ErrCodeDatabaseError, "")
|
||||
)
|
||||
|
||||
// 通用系统错误
|
||||
var (
|
||||
ErrInvalidInput = errors.New("invalid input")
|
||||
ErrNotFound = errors.New("not found")
|
||||
ErrUnauthorized = errors.New("unauthorized")
|
||||
ErrForbidden = errors.New("forbidden")
|
||||
)
|
||||
|
||||
// IsBusinessError 判断是否为业务错误
|
||||
func IsBusinessError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
if _, ok := err.(*BusinessError); ok {
|
||||
return true
|
||||
}
|
||||
var be *BusinessError
|
||||
return errors.As(err, &be)
|
||||
}
|
||||
125
serve/internal/common/response.go
Normal file
125
serve/internal/common/response.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Response 通用响应结构
|
||||
type Response struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
}
|
||||
|
||||
// PaginationResponse 分页响应结构
|
||||
type PaginationResponse struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data interface{} `json:"data"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
}
|
||||
|
||||
// PaginationData 分页数据结构
|
||||
type PaginationData struct {
|
||||
Items interface{} `json:"items"`
|
||||
Pagination *Pagination `json:"pagination"`
|
||||
}
|
||||
|
||||
// Pagination 分页信息
|
||||
type Pagination struct {
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"page_size"`
|
||||
Total int `json:"total"`
|
||||
TotalPages int `json:"total_pages"`
|
||||
}
|
||||
|
||||
// SuccessResponse 成功响应
|
||||
func SuccessResponse(c *gin.Context, data interface{}) {
|
||||
c.JSON(http.StatusOK, Response{
|
||||
Code: http.StatusOK,
|
||||
Message: "success",
|
||||
Data: data,
|
||||
Timestamp: time.Now().UTC().Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
|
||||
// SuccessWithMessage 带消息的成功响应
|
||||
func SuccessWithMessage(c *gin.Context, message string, data interface{}) {
|
||||
c.JSON(http.StatusOK, Response{
|
||||
Code: http.StatusOK,
|
||||
Message: message,
|
||||
Data: data,
|
||||
Timestamp: time.Now().UTC().Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
|
||||
// PaginationSuccessResponse 分页成功响应
|
||||
func PaginationSuccessResponse(c *gin.Context, items interface{}, pagination *Pagination) {
|
||||
c.JSON(http.StatusOK, PaginationResponse{
|
||||
Code: http.StatusOK,
|
||||
Message: "success",
|
||||
Data: PaginationData{
|
||||
Items: items,
|
||||
Pagination: pagination,
|
||||
},
|
||||
Timestamp: time.Now().UTC().Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
|
||||
// ErrorResponse 错误响应
|
||||
func ErrorResponse(c *gin.Context, code int, message string) {
|
||||
c.JSON(code, Response{
|
||||
Code: code,
|
||||
Message: message,
|
||||
Timestamp: time.Now().UTC().Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
|
||||
// BadRequestResponse 400错误响应
|
||||
func BadRequestResponse(c *gin.Context, message string) {
|
||||
ErrorResponse(c, http.StatusBadRequest, message)
|
||||
}
|
||||
|
||||
// UnauthorizedResponse 401错误响应
|
||||
func UnauthorizedResponse(c *gin.Context, message string) {
|
||||
ErrorResponse(c, http.StatusUnauthorized, message)
|
||||
}
|
||||
|
||||
// ForbiddenResponse 403错误响应
|
||||
func ForbiddenResponse(c *gin.Context, message string) {
|
||||
ErrorResponse(c, http.StatusForbidden, message)
|
||||
}
|
||||
|
||||
// NotFoundResponse 404错误响应
|
||||
func NotFoundResponse(c *gin.Context, message string) {
|
||||
ErrorResponse(c, http.StatusNotFound, message)
|
||||
}
|
||||
|
||||
// InternalServerErrorResponse 500错误响应
|
||||
func InternalServerErrorResponse(c *gin.Context, message string) {
|
||||
ErrorResponse(c, http.StatusInternalServerError, message)
|
||||
}
|
||||
|
||||
// ValidationErrorResponse 参数验证错误响应
|
||||
func ValidationErrorResponse(c *gin.Context, errors interface{}) {
|
||||
c.JSON(http.StatusBadRequest, Response{
|
||||
Code: http.StatusBadRequest,
|
||||
Message: "参数验证失败",
|
||||
Data: errors,
|
||||
Timestamp: time.Now().UTC().Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
|
||||
// SuccessResponseWithStatus 带自定义状态码的成功响应
|
||||
func SuccessResponseWithStatus(c *gin.Context, statusCode int, data interface{}) {
|
||||
c.JSON(statusCode, Response{
|
||||
Code: statusCode,
|
||||
Message: "success",
|
||||
Data: data,
|
||||
Timestamp: time.Now().UTC().Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user