@@ -0,0 +1,58 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// ServiceConfig holds configuration shared across all microservices.
|
||||
type ServiceConfig struct {
|
||||
Name string // 服务名称,如 auth-service
|
||||
Port string // 监听端口
|
||||
|
||||
// 基础设施连接(骨架阶段仅定义,暂不使用)
|
||||
PostgresDSN string
|
||||
RedisAddr string
|
||||
MinIOAddr string
|
||||
RabbitMQURL string
|
||||
|
||||
// 运行环境: debug / release
|
||||
GinMode string
|
||||
}
|
||||
|
||||
// Load 从环境变量加载配置,fallback 到默认值。
|
||||
// serviceName 和 defaultPort 用于在没有设置环境变量时提供默认值。
|
||||
func Load(serviceName, defaultPort string) *ServiceConfig {
|
||||
cfg := &ServiceConfig{
|
||||
Name: serviceName,
|
||||
Port: envOr("SERVICE_PORT", defaultPort),
|
||||
PostgresDSN: envOr("POSTGRES_DSN", "postgres://mrcc:mrcc_dev_2026@localhost:5432/mrcc?sslmode=disable"),
|
||||
RedisAddr: envOr("REDIS_ADDR", "localhost:6379"),
|
||||
MinIOAddr: envOr("MINIO_ADDR", "localhost:9000"),
|
||||
RabbitMQURL: envOr("RABBITMQ_URL", "amqp://mrcc:mrcc_dev_2026@localhost:5672/"),
|
||||
GinMode: envOr("GIN_MODE", "debug"),
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
// Address 返回 :port 格式的监听地址
|
||||
func (c *ServiceConfig) Address() string {
|
||||
return fmt.Sprintf(":%s", c.Port)
|
||||
}
|
||||
|
||||
func envOr(key, fallback string) string {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return v
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
func envIntOr(key string, fallback int) int {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
if n, err := strconv.Atoi(v); err == nil {
|
||||
return n
|
||||
}
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package httpserver
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"mrcc/internal/config"
|
||||
"mrcc/pkg/response"
|
||||
)
|
||||
|
||||
// New 创建一个配置好中间件的 Gin 实例,并注册 /health 端点。
|
||||
func New(cfg *config.ServiceConfig) *gin.Engine {
|
||||
gin.SetMode(cfg.GinMode)
|
||||
|
||||
r := gin.New()
|
||||
|
||||
// 中间件
|
||||
r.Use(gin.Recovery())
|
||||
r.Use(gin.LoggerWithFormatter(func(p gin.LogFormatterParams) string {
|
||||
return p.TimeStamp.Format(time.RFC3339) + " | " + p.Method + " | " + p.Path + " | " +
|
||||
itoa(p.StatusCode) + " | " + p.Latency.String() + "\n"
|
||||
}))
|
||||
|
||||
// CORS
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Header("Access-Control-Allow-Origin", "*")
|
||||
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
||||
c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization")
|
||||
if c.Request.Method == "OPTIONS" {
|
||||
c.AbortWithStatus(204)
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
})
|
||||
|
||||
// 健康检查
|
||||
r.GET("/health", func(c *gin.Context) {
|
||||
response.OK(c, gin.H{
|
||||
"service": cfg.Name,
|
||||
"status": "running",
|
||||
})
|
||||
})
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func itoa(n int) string {
|
||||
if n == 0 {
|
||||
return "0"
|
||||
}
|
||||
buf := [10]byte{}
|
||||
pos := len(buf)
|
||||
for n > 0 {
|
||||
pos--
|
||||
buf[pos] = byte('0' + n%10)
|
||||
n /= 10
|
||||
}
|
||||
return string(buf[pos:])
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"os"
|
||||
)
|
||||
|
||||
// 全局默认 logger,使用 JSON handler 输出到 stdout。
|
||||
var defaultLogger *slog.Logger
|
||||
|
||||
func init() {
|
||||
defaultLogger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
|
||||
Level: slog.LevelInfo,
|
||||
}))
|
||||
slog.SetDefault(defaultLogger)
|
||||
}
|
||||
|
||||
// Default 返回全局 logger 实例
|
||||
func Default() *slog.Logger {
|
||||
return defaultLogger
|
||||
}
|
||||
|
||||
// WithService 返回带有 service 字段的 logger
|
||||
func WithService(name string) *slog.Logger {
|
||||
return defaultLogger.With("service", name)
|
||||
}
|
||||
Reference in New Issue
Block a user