做完了一部分,备份一下,防止以外删除

This commit is contained in:
2025-11-11 15:16:38 +08:00
parent 9600e5b6e0
commit e98dd4d725
39 changed files with 2423 additions and 183 deletions

View File

@@ -1,25 +1,185 @@
package main
import (
"context"
"os"
"os/signal"
"strconv"
"syscall"
"github.com/bytedance/sonic"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/compress"
"github.com/gofiber/fiber/v2/middleware/requestid"
"github.com/google/uuid"
"github.com/redis/go-redis/v9"
"go.uber.org/zap"
"github.com/break/junhong_cmp_fiber/internal/handler"
"github.com/break/junhong_cmp_fiber/internal/middleware"
"github.com/break/junhong_cmp_fiber/pkg/config"
"github.com/break/junhong_cmp_fiber/pkg/logger"
"github.com/break/junhong_cmp_fiber/pkg/validator"
)
func main() {
// 加载配置
cfg, err := config.Load()
if err != nil {
panic("加载配置失败: " + err.Error())
}
// 初始化日志
if err := logger.InitLoggers(
cfg.Logging.Level,
cfg.Logging.Development,
logger.LogRotationConfig{
Filename: cfg.Logging.AppLog.Filename,
MaxSize: cfg.Logging.AppLog.MaxSize,
MaxBackups: cfg.Logging.AppLog.MaxBackups,
MaxAge: cfg.Logging.AppLog.MaxAge,
Compress: cfg.Logging.AppLog.Compress,
},
logger.LogRotationConfig{
Filename: cfg.Logging.AccessLog.Filename,
MaxSize: cfg.Logging.AccessLog.MaxSize,
MaxBackups: cfg.Logging.AccessLog.MaxBackups,
MaxAge: cfg.Logging.AccessLog.MaxAge,
Compress: cfg.Logging.AccessLog.Compress,
},
); err != nil {
panic("初始化日志失败: " + err.Error())
}
defer logger.Sync()
appLogger := logger.GetAppLogger()
appLogger.Info("应用程序启动中...",
zap.String("address", cfg.Server.Address),
)
redisAddr := cfg.Redis.Address + ":" + strconv.Itoa(cfg.Redis.Port)
// 连接 Redis
redisClient := redis.NewClient(&redis.Options{
Addr: redisAddr,
Password: cfg.Redis.Password,
DB: cfg.Redis.DB,
PoolSize: cfg.Redis.PoolSize,
MinIdleConns: cfg.Redis.MinIdleConns,
DialTimeout: cfg.Redis.DialTimeout,
ReadTimeout: cfg.Redis.ReadTimeout,
WriteTimeout: cfg.Redis.WriteTimeout,
})
defer redisClient.Close()
// 测试 Redis 连接
ctx := context.Background()
if err := redisClient.Ping(ctx).Err(); err != nil {
appLogger.Fatal("连接 Redis 失败", zap.Error(err))
}
appLogger.Info("Redis 已连接", zap.String("address", redisAddr))
// 创建令牌验证器
tokenValidator := validator.NewTokenValidator(redisClient, appLogger)
// 启动配置文件监听器(热重载)
watchCtx, cancelWatch := context.WithCancel(context.Background())
defer cancelWatch()
go config.Watch(watchCtx, appLogger)
// 创建 Fiber 应用
app := fiber.New(fiber.Config{
AppName: "君鸿卡管系统 v0.0.1",
AppName: "君鸿卡管系统 v1.0.0",
StrictRouting: true,
CaseSensitive: true,
JSONEncoder: sonic.Marshal,
JSONDecoder: sonic.Unmarshal,
// Prefork: true, // 该字段是用来开启监听端口是否可以被多个应用监听的,非必要不要开了
Prefork: cfg.Server.Prefork,
ReadTimeout: cfg.Server.ReadTimeout,
WriteTimeout: cfg.Server.WriteTimeout,
})
//压缩中间件-根据Accept-Encoding使用gzip 、 deflate 和 brotli 压缩来压缩响应
app.Use(compress.New(compress.Config{
// Next: func(c *fiber.Ctx) bool { return c.Path() == "/test"}, //返回值为true时会跳过压缩中间件
Level: 0,
// 中间件注册(顺序很重要)
// 1. Recover - 必须第一个,捕获所有 panic
app.Use(middleware.Recover(appLogger))
// 2. RequestID - 为每个请求生成唯一 ID
app.Use(requestid.New(requestid.Config{
Generator: func() string {
return uuid.NewString()
},
}))
app.Listen(":3000")
// 3. Logger - 记录所有请求
app.Use(logger.Middleware())
// 4. Compress - 响应压缩
app.Use(compress.New(compress.Config{
Level: compress.LevelDefault,
}))
// 路由注册
// 公共端点(无需认证)
app.Get("/health", handler.HealthCheck)
// API v1 路由组
v1 := app.Group("/api/v1")
// 受保护的端点(需要认证)
if cfg.Middleware.EnableAuth {
v1.Use(middleware.KeyAuth(tokenValidator, appLogger))
}
// 可选:启用限流器
if cfg.Middleware.EnableRateLimiter {
var rateLimitStorage fiber.Storage
// 根据配置选择存储后端
if cfg.Middleware.RateLimiter.Storage == "redis" {
rateLimitStorage = middleware.NewRedisStorage(
cfg.Redis.Address,
cfg.Redis.Password,
cfg.Redis.DB,
cfg.Redis.Port,
)
appLogger.Info("限流器使用 Redis 存储", zap.String("redis_address", cfg.Redis.Address))
} else {
rateLimitStorage = nil // 使用内存存储
appLogger.Info("限流器使用内存存储")
}
v1.Use(middleware.RateLimiter(
cfg.Middleware.RateLimiter.Max,
cfg.Middleware.RateLimiter.Expiration,
rateLimitStorage,
))
}
// 注册受保护的路由
v1.Get("/users", handler.GetUsers)
// 优雅关闭
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
go func() {
if err := app.Listen(cfg.Server.Address); err != nil {
appLogger.Fatal("服务器启动失败", zap.Error(err))
}
}()
appLogger.Info("服务器已启动", zap.String("address", cfg.Server.Address))
// 等待关闭信号
<-quit
appLogger.Info("正在关闭服务器...")
// 取消配置监听器
cancelWatch()
// 关闭 HTTP 服务器
if err := app.ShutdownWithTimeout(cfg.Server.ShutdownTimeout); err != nil {
appLogger.Error("强制关闭服务器", zap.Error(err))
}
appLogger.Info("服务器已停止")
}