ifms_go/pkg/util/gin.go
2025-06-10 17:50:46 +08:00

273 lines
6.0 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package util
import (
"net/http"
"reflect"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
jsontime "github.com/liamylian/jsontime/v2/v2"
"ifms/pkg/errors"
)
func init() {
jsontime.SetDefaultTimeFormat(time.DateTime, time.Local)
}
// Get access token from header or query parameter
func GetToken(c *gin.Context) string {
var token string
auth := c.GetHeader("Authorization")
prefix := "Bearer "
if auth != "" && strings.HasPrefix(auth, prefix) {
token = auth[len(prefix):]
} else {
token = auth
}
if token == "" {
token = c.Query("accessToken")
}
return token
}
// Get body data from context
func GetBodyData(c *gin.Context) []byte {
if v, ok := c.Get(ReqBodyKey); ok {
if b, ok := v.([]byte); ok {
return b
}
}
return nil
}
// Parse body json data to struct
func ParseJSON(c *gin.Context, obj interface{}) error {
if err := c.ShouldBindJSON(obj); err != nil {
return errors.BadRequest("", "Failed to parse json: %s", err.Error())
}
return nil
}
// Parse query parameter to struct
func ParseQuery(c *gin.Context, obj interface{}) error {
if err := c.ShouldBindQuery(obj); err != nil {
return errors.BadRequest("", "Failed to parse query: %s", err.Error())
}
return nil
}
// Parse body form data to struct
func ParseForm(c *gin.Context, obj interface{}) error {
if err := c.ShouldBindWith(obj, binding.Form); err != nil {
return errors.BadRequest("", "Failed to parse form: %s", err.Error())
}
return nil
}
// Response json data with status code
func ResJSON(c *gin.Context, status int, v interface{}) {
//buf, err := MarshalWithTimeFormat(v, "2006-01-02 15:04:05")
json := jsontime.ConfigWithCustomTimeFormat
buf, err := json.Marshal(v)
if err != nil {
panic(err)
}
c.Set(ResBodyKey, buf)
c.Data(status, "application/json; charset=utf-8", buf)
c.Abort()
}
func ResJSONFail(c *gin.Context, status int, v interface{}) {
json := jsontime.ConfigWithCustomTimeFormat
buf, err := json.Marshal(v)
if err != nil {
panic(err)
}
c.Set(ResBodyKey, buf)
c.Data(status, "application/json; charset=utf-8", buf)
c.Abort()
}
func ResSuccess(c *gin.Context, v interface{}) {
ResJSON(c, http.StatusOK, ResponseResult{
Success: true,
Code: ResSuccessCode,
Data: v,
})
}
func ResOK(c *gin.Context) {
ResJSON(c, http.StatusOK, ResponseResult{
Code: ResSuccessCode,
Success: true,
})
}
func ResPage(c *gin.Context, v interface{}, pr *PaginationResult) {
var total int64
if pr != nil {
total = pr.Total
}
// 检查 v 是否为 nil
if v == nil {
v = make([]interface{}, 0)
} else {
reflectValue := reflect.ValueOf(v)
// 检查反射值是否有效
if reflectValue.IsValid() {
// 检查是否是指针类型
if reflectValue.Kind() == reflect.Ptr && reflectValue.IsNil() {
v = make([]interface{}, 0)
}
} else {
// 如果反射值无效,设置为空切片
v = make([]interface{}, 0)
}
}
ResJSON(c, http.StatusOK, ResponseResult{
Success: true,
Data: v,
Total: total,
})
}
func ResError(c *gin.Context, err error, status ...int) {
var ierr *errors.Error
if e, ok := errors.As(err); ok {
ierr = e
} else {
ierr = errors.FromError(errors.InternalServerError("", err.Error()))
}
code := int(ierr.Code)
if len(status) > 0 {
code = status[0]
}
//if code >= 500 {
// ctx := c.Request.Context()
// ctx = logging.NewTag(ctx, logging.TagKeySystem)
// ctx = logging.NewStack(ctx, fmt.Sprintf("%+v", err))
// logging.Context(ctx).Error("Internal server error", zap.Error(err))
// ierr.Detail = http.StatusText(http.StatusInternalServerError)
//}
ierr.Code = int32(code)
ResJSONFail(c, http.StatusOK, ResponseResult{Code: ResFailedCode, Data: ierr.Detail})
}
func MarshalWithTimeFormat(v interface{}, timeLayout string) ([]byte, error) {
rv := reflect.ValueOf(v)
json := jsontime.ConfigWithCustomTimeFormat
formatted, err := formatTimeFields(rv, timeLayout)
if err != nil {
return nil, err
}
return json.Marshal(formatted)
}
func formatTimeFields(val reflect.Value, layout string) (interface{}, error) {
if !val.IsValid() {
return nil, nil
}
if val.Kind() == reflect.Ptr {
if val.IsNil() {
return nil, nil
}
val = val.Elem()
}
switch val.Kind() {
case reflect.Struct:
out := make(map[string]interface{})
t := val.Type()
for i := 0; i < val.NumField(); i++ {
field := val.Field(i)
fieldType := t.Field(i)
if !field.IsValid() || !field.CanInterface() {
continue
}
if field.Kind() == reflect.Ptr && field.IsNil() {
continue
}
// 获取 json key
jsonTag := fieldType.Tag.Get("json")
jsonKey := parseJSONTag(jsonTag)
if jsonKey == "-" || jsonKey == "" {
jsonKey = fieldType.Name
}
// 判断是否为 time.Time
if ft, ok := field.Interface().(time.Time); ok {
out[jsonKey] = ft.Format(layout)
continue
}
// 递归格式化
formattedField, err := formatTimeFields(field, layout)
if err != nil {
return nil, err
}
out[jsonKey] = formattedField
}
return out, nil
case reflect.Slice, reflect.Array:
//特殊处理 []byte避免被逐个元素遍历
if val.Type().Elem().Kind() == reflect.Uint8 {
// 是 []byte
bs := val.Bytes()
return string(bs), nil
}
out := make([]interface{}, val.Len())
for i := 0; i < val.Len(); i++ {
formatted, err := formatTimeFields(val.Index(i), layout)
if err != nil {
return nil, err
}
out[i] = formatted
}
return out, nil
case reflect.Map:
out := make(map[interface{}]interface{})
for _, key := range val.MapKeys() {
v := val.MapIndex(key)
formattedVal, err := formatTimeFields(v, layout)
if err != nil {
return nil, err
}
out[key.Interface()] = formattedVal
}
return out, nil
case reflect.Interface:
if val.IsNil() {
return nil, nil
}
return formatTimeFields(val.Elem(), layout)
default:
return val.Interface(), nil
}
}
// 解析 json tag只取前半部分例如 "data,omitempty" -> "data"
func parseJSONTag(tag string) string {
if idx := strings.Index(tag, ","); idx != -1 {
return tag[:idx]
}
return tag
}