273 lines
6.0 KiB
Go
273 lines
6.0 KiB
Go
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
|
||
}
|