feat():learning后台管理项目初始化

This commit is contained in:
yuj
2025-12-04 16:23:46 +08:00
parent 39886d50d2
commit 88e048f4d1
154 changed files with 28966 additions and 6 deletions

133
pkg/utils/README.md Normal file
View File

@@ -0,0 +1,133 @@
# 工具包
本模块提供各种实用工具函数和类。
## 功能特性
- 加密工具MD5、SHA256、密码哈希
- JWT token管理
- 统一响应处理
- 数据验证
- 日志管理
## 模块结构
```
utils/
├── crypto.go # 加密工具
├── jwt.go # JWT管理
├── response.go # 响应处理
├── validator.go # 数据验证
├── logger.go # 日志管理
└── README.md # 说明文档
```
## 使用方法
### 加密工具
```go
import "goalfymax-admin/pkg/utils"
// MD5哈希
hash := utils.MD5Hash("password")
// SHA256哈希
hash := utils.SHA256Hash("password")
// 生成盐值
salt, err := utils.GenerateSalt()
// 哈希密码
hashedPassword := utils.HashPassword("password", salt)
// 验证密码
isValid := utils.VerifyPassword("password", salt, hashedPassword)
```
### JWT管理
```go
// 创建JWT管理器
jwtManager := utils.NewJWTManager("your-secret-key")
// 生成token
token, err := jwtManager.GenerateToken(1, "admin", "admin")
// 解析token
claims, err := jwtManager.ParseToken(token)
// 刷新token
newToken, err := jwtManager.RefreshToken(token)
```
### 响应处理
```go
// 创建响应实例
resp := utils.NewResponse()
// 成功响应
resp.Success(c, data)
// 错误响应
resp.Error(c, 400, "参数错误")
resp.BadRequest(c, "请求参数错误")
resp.Unauthorized(c, "未授权")
resp.Forbidden(c, "禁止访问")
resp.NotFound(c, "资源不存在")
resp.InternalServerError(c, "服务器内部错误")
// 分页响应
resp.Page(c, data, total, page, size)
```
### 数据验证
```go
// 创建验证器
validator := utils.NewValidator()
// 验证邮箱
isValid := validator.IsEmail("user@example.com")
// 验证手机号
isValid := validator.IsPhone("13800138000")
// 验证用户名
isValid := validator.IsUsername("admin")
// 验证密码强度
isValid := validator.IsPassword("password123")
// 验证URL
isValid := validator.IsURL("https://example.com")
// 检查是否为空
isEmpty := validator.IsEmpty("")
// 验证角色
isValid := validator.IsValidRole("admin")
```
### 日志管理
```go
// 创建日志实例
logger, err := utils.NewLogger("info", "json", "stdout")
if err != nil {
log.Fatal(err)
}
// 记录日志
logger.Info("用户登录", zap.String("username", "admin"))
logger.Error("登录失败", zap.String("error", "密码错误"))
// 添加字段
logger.WithField("user_id", 1).Info("用户操作")
logger.WithFields(map[string]interface{}{
"user_id": 1,
"action": "login",
}).Info("用户登录")
```

55
pkg/utils/crypto.go Normal file
View File

@@ -0,0 +1,55 @@
package utils
import (
"crypto/md5"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"math/big"
)
// MD5Hash 计算MD5哈希
func MD5Hash(text string) string {
hash := md5.Sum([]byte(text))
return hex.EncodeToString(hash[:])
}
// SHA256Hash 计算SHA256哈希
func SHA256Hash(text string) string {
hash := sha256.Sum256([]byte(text))
return hex.EncodeToString(hash[:])
}
// GenerateRandomString 生成指定长度的随机字符串
func GenerateRandomString(length int) (string, error) {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
b := make([]byte, length)
for i := range b {
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
if err != nil {
return "", err
}
b[i] = charset[num.Int64()]
}
return string(b), nil
}
// GenerateSalt 生成盐值
func GenerateSalt() (string, error) {
return GenerateRandomString(16)
}
// HashPassword 使用盐值哈希密码
func HashPassword(password, salt string) string {
return SHA256Hash(password + salt)
}
// VerifyPassword 验证密码
func VerifyPassword(password, salt, hash string) bool {
return HashPassword(password, salt) == hash
}
// GenerateToken 生成随机token
func GenerateToken() (string, error) {
return GenerateRandomString(32)
}

80
pkg/utils/jwt.go Normal file
View File

@@ -0,0 +1,80 @@
package utils
import (
"errors"
"time"
"github.com/golang-jwt/jwt/v5"
)
// JWTClaims JWT声明
type JWTClaims struct {
UserID uint `json:"user_id"`
Username string `json:"username"`
Role string `json:"role"`
jwt.RegisteredClaims
}
// JWTManager JWT管理器
type JWTManager struct {
secretKey string
}
// NewJWTManager 创建JWT管理器
func NewJWTManager(secretKey string) *JWTManager {
return &JWTManager{
secretKey: secretKey,
}
}
// GenerateToken 生成JWT token
func (j *JWTManager) GenerateToken(userID uint, username string) (string, error) {
claims := JWTClaims{
UserID: userID,
Username: username,
Role: "", // 不再使用role字段
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
NotBefore: jwt.NewNumericDate(time.Now()),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(j.secretKey))
}
// ParseToken 解析JWT token
func (j *JWTManager) ParseToken(tokenString string) (*JWTClaims, error) {
token, err := jwt.ParseWithClaims(tokenString, &JWTClaims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, errors.New("unexpected signing method")
}
return []byte(j.secretKey), nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*JWTClaims); ok && token.Valid {
return claims, nil
}
return nil, errors.New("invalid token")
}
// RefreshToken 刷新token
func (j *JWTManager) RefreshToken(tokenString string) (string, error) {
claims, err := j.ParseToken(tokenString)
if err != nil {
return "", err
}
// 检查token是否即将过期剩余时间少于1小时
if time.Until(claims.ExpiresAt.Time) < time.Hour {
return j.GenerateToken(claims.UserID, claims.Username)
}
return tokenString, nil
}

98
pkg/utils/logger.go Normal file
View File

@@ -0,0 +1,98 @@
package utils
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// Logger 日志管理器
type Logger struct {
*zap.Logger
}
// NewLogger 创建日志实例
func NewLogger(level, format, output string) (*Logger, error) {
// 设置日志级别
var zapLevel zapcore.Level
switch level {
case "debug":
zapLevel = zapcore.DebugLevel
case "info":
zapLevel = zapcore.InfoLevel
case "warn":
zapLevel = zapcore.WarnLevel
case "error":
zapLevel = zapcore.ErrorLevel
default:
zapLevel = zapcore.InfoLevel
}
// 设置编码格式
var encoderConfig zapcore.EncoderConfig
if format == "json" {
encoderConfig = zap.NewProductionEncoderConfig()
} else {
encoderConfig = zap.NewDevelopmentEncoderConfig()
}
// 设置时间格式
encoderConfig.TimeKey = "timestamp"
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
// 创建配置
config := zap.Config{
Level: zap.NewAtomicLevelAt(zapLevel),
Development: format != "json",
Encoding: format,
EncoderConfig: encoderConfig,
OutputPaths: []string{output},
ErrorOutputPaths: []string{output},
}
// 创建logger
logger, err := config.Build()
if err != nil {
return nil, err
}
return &Logger{Logger: logger}, nil
}
// Info 记录信息日志
func (l *Logger) Info(msg string, fields ...zap.Field) {
l.Logger.Info(msg, fields...)
}
// Debug 记录调试日志
func (l *Logger) Debug(msg string, fields ...zap.Field) {
l.Logger.Debug(msg, fields...)
}
// Warn 记录警告日志
func (l *Logger) Warn(msg string, fields ...zap.Field) {
l.Logger.Warn(msg, fields...)
}
// Error 记录错误日志
func (l *Logger) Error(msg string, fields ...zap.Field) {
l.Logger.Error(msg, fields...)
}
// Fatal 记录致命错误日志
func (l *Logger) Fatal(msg string, fields ...zap.Field) {
l.Logger.Fatal(msg, fields...)
}
// WithField 添加字段
func (l *Logger) WithField(key string, value interface{}) *Logger {
return &Logger{Logger: l.Logger.With(zap.Any(key, value))}
}
// WithFields 添加多个字段
func (l *Logger) WithFields(fields map[string]interface{}) *Logger {
zapFields := make([]zap.Field, 0, len(fields))
for k, v := range fields {
zapFields = append(zapFields, zap.Any(k, v))
}
return &Logger{Logger: l.Logger.With(zapFields...)}
}

61
pkg/utils/response.go Normal file
View File

@@ -0,0 +1,61 @@
package utils
import (
"net/http"
"github.com/gin-gonic/gin"
"goalfymax-admin/internal/models"
)
// Response 统一响应处理
type Response struct{}
// Success 成功响应
func (r *Response) Success(c *gin.Context, data interface{}) {
c.JSON(http.StatusOK, models.NewSuccessResponse(data))
}
// Error 错误响应
func (r *Response) Error(c *gin.Context, code int, message string) {
c.JSON(code, models.NewResponse(code, message, nil))
}
// BadRequest 400错误
func (r *Response) BadRequest(c *gin.Context, message string) {
r.Error(c, http.StatusBadRequest, message)
}
// Unauthorized 401错误
func (r *Response) Unauthorized(c *gin.Context, message string) {
r.Error(c, http.StatusUnauthorized, message)
}
// Forbidden 403错误
func (r *Response) Forbidden(c *gin.Context, message string) {
r.Error(c, http.StatusForbidden, message)
}
// NotFound 404错误
func (r *Response) NotFound(c *gin.Context, message string) {
r.Error(c, http.StatusNotFound, message)
}
// InternalServerError 500错误
func (r *Response) InternalServerError(c *gin.Context, message string) {
r.Error(c, http.StatusInternalServerError, message)
}
// Page 分页响应
func (r *Response) Page(c *gin.Context, data interface{}, total int64, page, size int) {
c.JSON(http.StatusOK, models.NewPageResponse(data, total, page, size))
}
// ValidateError 验证错误响应
func (r *Response) ValidateError(c *gin.Context, err error) {
r.BadRequest(c, err.Error())
}
// NewResponse 创建响应实例
func NewResponse() *Response {
return &Response{}
}

72
pkg/utils/validator.go Normal file
View File

@@ -0,0 +1,72 @@
package utils
import (
"regexp"
"strings"
)
// Validator 验证器
type Validator struct{}
// IsEmail 验证邮箱格式
func (v *Validator) IsEmail(email string) bool {
pattern := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
matched, _ := regexp.MatchString(pattern, email)
return matched
}
// IsPhone 验证手机号格式
func (v *Validator) IsPhone(phone string) bool {
pattern := `^1[3-9]\d{9}$`
matched, _ := regexp.MatchString(pattern, phone)
return matched
}
// IsUsername 验证用户名格式
func (v *Validator) IsUsername(username string) bool {
// 用户名只能包含字母、数字、下划线长度3-20
pattern := `^[a-zA-Z0-9_]{3,20}$`
matched, _ := regexp.MatchString(pattern, username)
return matched
}
// IsPassword 验证密码强度
func (v *Validator) IsPassword(password string) bool {
// 密码至少6位包含字母和数字
if len(password) < 6 {
return false
}
hasLetter := regexp.MustCompile(`[a-zA-Z]`).MatchString(password)
hasNumber := regexp.MustCompile(`[0-9]`).MatchString(password)
return hasLetter && hasNumber
}
// IsURL 验证URL格式
func (v *Validator) IsURL(url string) bool {
pattern := `^https?://[^\s/$.?#].[^\s]*$`
matched, _ := regexp.MatchString(pattern, url)
return matched
}
// IsEmpty 检查字符串是否为空
func (v *Validator) IsEmpty(str string) bool {
return strings.TrimSpace(str) == ""
}
// IsValidRole 验证角色名称
func (v *Validator) IsValidRole(role string) bool {
validRoles := []string{"admin", "user", "guest"}
for _, validRole := range validRoles {
if role == validRole {
return true
}
}
return false
}
// NewValidator 创建验证器实例
func NewValidator() *Validator {
return &Validator{}
}