All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 4m31s
问题描述: - 认证中间件存储的 UserID 是 uint 类型 - 日志中间件和错误上下文错误地将其断言为 string 类型 - 导致所有认证请求在记录访问日志时发生 panic 修复内容: 1. pkg/logger/middleware.go - 修改 UserID 变量类型从 string 为 uint - 使用安全的类型断言 (uid.(uint)) - 使用 zap.Uint 记录日志 2. pkg/errors/context.go - 修改 UserID 类型断言从 string 为 uint - 使用 strconv.FormatUint 转换为 string 用于错误上下文 影响范围: - 修复所有需要认证的接口的 panic 错误 - 包括 /api/admin/shops, /api/admin/me, /api/admin/permissions 等
93 lines
2.3 KiB
Go
93 lines
2.3 KiB
Go
package errors
|
||
|
||
import (
|
||
"strconv"
|
||
|
||
"github.com/gofiber/fiber/v2"
|
||
"go.uber.org/zap"
|
||
|
||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||
)
|
||
|
||
// ErrorContext 错误发生时的请求上下文(用于日志记录)
|
||
type ErrorContext struct {
|
||
RequestID string // 请求 ID(唯一标识)
|
||
Method string // HTTP 方法
|
||
Path string // 请求路径
|
||
Query string // Query 参数
|
||
Body string // 请求 Body(限制 50KB)
|
||
IP string // 客户端 IP
|
||
UserAgent string // User-Agent
|
||
UserID string // 用户 ID(如果已认证)
|
||
}
|
||
|
||
const (
|
||
// MaxBodyLogSize 请求 Body 日志记录最大字节数(50KB)
|
||
MaxBodyLogSize = 50 * 1024
|
||
)
|
||
|
||
// FromFiberContext 从 Fiber Context 提取错误上下文
|
||
func FromFiberContext(c *fiber.Ctx) *ErrorContext {
|
||
ctx := &ErrorContext{
|
||
Method: c.Method(),
|
||
Path: c.Path(),
|
||
Query: c.Request().URI().QueryArgs().String(),
|
||
IP: c.IP(),
|
||
UserAgent: c.Get("User-Agent"),
|
||
}
|
||
|
||
// 提取 Request ID
|
||
if rid := c.Locals(constants.ContextKeyRequestID); rid != nil {
|
||
ctx.RequestID = rid.(string)
|
||
}
|
||
if ctx.RequestID == "" {
|
||
ctx.RequestID = c.Get("X-Request-ID")
|
||
}
|
||
|
||
// 提取 User ID(如果已认证)
|
||
if uid := c.Locals(constants.ContextKeyUserID); uid != nil {
|
||
if userID, ok := uid.(uint); ok {
|
||
ctx.UserID = strconv.FormatUint(uint64(userID), 10)
|
||
}
|
||
}
|
||
|
||
// 提取请求 Body(限制 50KB)
|
||
bodyBytes := c.Body()
|
||
if len(bodyBytes) > 0 {
|
||
if len(bodyBytes) > MaxBodyLogSize {
|
||
// 超过限制时截断并添加提示
|
||
ctx.Body = string(bodyBytes[:MaxBodyLogSize]) + " ... (truncated)"
|
||
} else {
|
||
ctx.Body = string(bodyBytes)
|
||
}
|
||
}
|
||
|
||
return ctx
|
||
}
|
||
|
||
// ToLogFields 转换为 Zap 日志字段
|
||
func (ec *ErrorContext) ToLogFields() []zap.Field {
|
||
fields := []zap.Field{
|
||
zap.String("request_id", ec.RequestID),
|
||
zap.String("method", ec.Method),
|
||
zap.String("path", ec.Path),
|
||
zap.String("ip", ec.IP),
|
||
}
|
||
|
||
// 可选字段(非空时添加)
|
||
if ec.Query != "" {
|
||
fields = append(fields, zap.String("query", ec.Query))
|
||
}
|
||
if ec.Body != "" {
|
||
fields = append(fields, zap.String("body", ec.Body))
|
||
}
|
||
if ec.UserAgent != "" {
|
||
fields = append(fields, zap.String("user_agent", ec.UserAgent))
|
||
}
|
||
if ec.UserID != "" {
|
||
fields = append(fields, zap.String("user_id", ec.UserID))
|
||
}
|
||
|
||
return fields
|
||
}
|