diff --git a/internal/api/response_envelope.go b/internal/api/response_envelope.go index 064fe00..94742e7 100644 --- a/internal/api/response_envelope.go +++ b/internal/api/response_envelope.go @@ -1,12 +1,16 @@ package api // ResponseEnvelope is the standard wrapper for EZ-API JSON responses. -// Code is a stable business code string (e.g., ok, invalid_request, not_found). -// Message is empty for success and mirrors the top-level error for failures. -// Data holds the original response payload. +// Code is a numeric business code: 0 for success, non-zero for errors. +// Message is "success" for success, error description for failures. +// Data holds the original response payload for success; null for errors. +// TraceID is a request correlation identifier. +// Details holds optional structured error information. // swagger:model ResponseEnvelope type ResponseEnvelope struct { - Code string `json:"code" example:"ok"` + Code int `json:"code" example:"0"` + Message string `json:"message" example:"success"` Data any `json:"data" swaggertype:"object"` - Message string `json:"message" example:""` + TraceID string `json:"trace_id" example:"a1b2c3d4e5f6g7h8"` + Details any `json:"details,omitempty" swaggertype:"object"` } diff --git a/internal/dto/response.go b/internal/dto/response.go deleted file mode 100644 index 8577c67..0000000 --- a/internal/dto/response.go +++ /dev/null @@ -1,60 +0,0 @@ -package dto - -// ResponseEnvelope is the standard API response wrapper. -// All JSON API responses are wrapped in this envelope format. -// @Description Standard API response envelope -type ResponseEnvelope struct { - // Business code: 0 for success, non-zero for errors - // 0 = success - // 1xxx = common errors (invalid param, unauthorized, forbidden, rate limited) - // 4xxx = client errors (resource not found, conflict, invalid state) - // 5xxx = server errors (internal error, service unavailable, timeout) - Code int `json:"code" example:"0"` - - // Human-readable message: "success" for success, error description for errors - Message string `json:"message" example:"success"` - - // Response data: business payload for success, null for errors - Data any `json:"data"` - - // Request trace ID for debugging and log correlation - TraceID string `json:"trace_id" example:"a1b2c3d4e5f6g7h8"` - - // Optional error details (e.g., field validation errors) - Details any `json:"details,omitempty"` -} - -// ErrorResponse is returned when an error occurs. -// @Description Error response format -type ErrorResponse struct { - // Business error code (non-zero) - Code int `json:"code" example:"4001"` - - // Error message - Message string `json:"message" example:"resource not found"` - - // Always null for errors - Data any `json:"data" example:"null"` - - // Request trace ID - TraceID string `json:"trace_id" example:"a1b2c3d4e5f6g7h8"` - - // Optional structured error details - Details any `json:"details,omitempty"` -} - -// SuccessResponse is returned for successful operations. -// @Description Success response format -type SuccessResponse struct { - // Always 0 for success - Code int `json:"code" example:"0"` - - // Always "success" - Message string `json:"message" example:"success"` - - // Response payload - Data any `json:"data"` - - // Request trace ID - TraceID string `json:"trace_id" example:"a1b2c3d4e5f6g7h8"` -} diff --git a/internal/middleware/response_envelope.go b/internal/middleware/response_envelope.go index fdaf0c5..81fb536 100644 --- a/internal/middleware/response_envelope.go +++ b/internal/middleware/response_envelope.go @@ -5,8 +5,10 @@ import ( "crypto/rand" "encoding/hex" "encoding/json" + "fmt" "net/http" "strings" + "time" "github.com/gin-gonic/gin" ) @@ -227,6 +229,12 @@ func ResponseEnvelope() gin.HandlerFunc { } func isExcludedPath(path string) bool { + if path == "" { + return false + } + for len(path) > 1 && strings.HasSuffix(path, "/") { + path = strings.TrimSuffix(path, "/") + } if excludedPaths[path] { return true } @@ -298,7 +306,9 @@ func getTraceID(c *gin.Context) string { func generateTraceID() string { b := make([]byte, 16) - _, _ = rand.Read(b) + if _, err := rand.Read(b); err != nil { + return fmt.Sprintf("%032x", time.Now().UnixNano()) + } return hex.EncodeToString(b) }