package response import ( "encoding/json" "fmt" "net/http" "reflect" "strings" "time" "affiliate_dash/internal/pkg/timeutil" "github.com/gin-gonic/gin" ) var ( timeType = reflect.TypeOf(time.Time{}) rawMessageType = reflect.TypeOf(json.RawMessage{}) ) type Body struct { Code int `json:"code"` Message string `json:"message"` Data interface{} `json:"data,omitempty"` } func OK(c *gin.Context, data interface{}) { WithStatus(c, http.StatusOK, data) } func WithStatus(c *gin.Context, httpStatus int, data interface{}) { c.JSON(httpStatus, Body{Code: 0, Message: "ok", Data: NormalizeTime(data)}) } func Fail(c *gin.Context, httpStatus int, code int, message string) { c.JSON(httpStatus, Body{Code: code, Message: message}) } func BadRequest(c *gin.Context, message string) { Fail(c, http.StatusBadRequest, 400, message) } func Unauthorized(c *gin.Context, message string) { Fail(c, http.StatusUnauthorized, 401, message) } func Forbidden(c *gin.Context, message string) { Fail(c, http.StatusForbidden, 403, message) } func NotFound(c *gin.Context, message string) { Fail(c, http.StatusNotFound, 404, message) } func ServerError(c *gin.Context, message string) { Fail(c, http.StatusInternalServerError, 500, message) } type PageData struct { List interface{} `json:"list"` Total int64 `json:"total"` Page int `json:"page"` Size int `json:"size"` } func Page(c *gin.Context, list interface{}, total int64, page, size int) { OK(c, PageData{List: list, Total: total, Page: page, Size: size}) } func FormatTime(value time.Time) string { return timeutil.FormatAPITime(value) } func NormalizeTime(value interface{}) interface{} { return normalizeTimeValue(reflect.ValueOf(value)) } func normalizeTimeValue(value reflect.Value) interface{} { if !value.IsValid() { return nil } if value.Kind() == reflect.Interface { if value.IsNil() { return nil } return normalizeTimeValue(value.Elem()) } if value.Type() == rawMessageType { return value.Interface() } if value.Type() == timeType { if !value.CanInterface() { return nil } return FormatTime(value.Interface().(time.Time)) } switch value.Kind() { case reflect.Pointer: if value.IsNil() { return nil } if value.Type().Elem() == timeType { return FormatTime(value.Elem().Interface().(time.Time)) } return normalizeTimeValue(value.Elem()) case reflect.Slice, reflect.Array: if value.Kind() == reflect.Slice && value.IsNil() { return nil } if value.Type().Elem().Kind() == reflect.Uint8 { return value.Interface() } out := make([]interface{}, 0, value.Len()) for i := 0; i < value.Len(); i++ { out = append(out, normalizeTimeValue(value.Index(i))) } return out case reflect.Map: if value.IsNil() { return nil } out := make(map[string]interface{}, value.Len()) iter := value.MapRange() for iter.Next() { out[mapKeyString(iter.Key())] = normalizeTimeValue(iter.Value()) } return out case reflect.Struct: return normalizeTimeStruct(value) default: if value.CanInterface() { return value.Interface() } return nil } } func normalizeTimeStruct(value reflect.Value) map[string]interface{} { out := make(map[string]interface{}, value.NumField()) valueType := value.Type() for i := 0; i < value.NumField(); i++ { field := valueType.Field(i) if field.PkgPath != "" { continue } name, skip, omitEmpty := jsonFieldName(field) if skip { continue } fieldValue := value.Field(i) if omitEmpty && fieldValue.IsZero() { continue } out[name] = normalizeTimeValue(fieldValue) } return out } func jsonFieldName(field reflect.StructField) (string, bool, bool) { tag := field.Tag.Get("json") if tag == "-" { return "", true, false } if tag == "" { return field.Name, false, false } parts := strings.Split(tag, ",") name := parts[0] if name == "" { name = field.Name } return name, false, hasTagOption(parts[1:], "omitempty") } func hasTagOption(options []string, target string) bool { for _, option := range options { if option == target { return true } } return false } func mapKeyString(value reflect.Value) string { if value.Kind() == reflect.String { return value.String() } if value.CanInterface() { return fmt.Sprint(value.Interface()) } return "" }