Files
assetx/webapp-back/admin/common.go

132 lines
3.5 KiB
Go
Raw Permalink Normal View History

package admin
import (
"encoding/json"
"io"
"net/http"
"strconv"
"time"
"github.com/gin-gonic/gin"
"github.com/gothinkster/golang-gin-realworld-example-app/common"
"github.com/gothinkster/golang-gin-realworld-example-app/middleware"
"github.com/gothinkster/golang-gin-realworld-example-app/models"
)
// PaginationParams holds parsed ProTable pagination query params
type PaginationParams struct {
Current int
PageSize int
}
// ParsePagination reads ?current=N&pageSize=M from the request
func ParsePagination(c *gin.Context) PaginationParams {
current, _ := strconv.Atoi(c.DefaultQuery("current", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "20"))
if current < 1 {
current = 1
}
if pageSize < 1 {
pageSize = 20
}
if pageSize > 100 {
pageSize = 100
}
return PaginationParams{Current: current, PageSize: pageSize}
}
// Offset returns the DB offset for GORM queries
func (p PaginationParams) Offset() int {
return (p.Current - 1) * p.PageSize
}
// OK responds with {success: true, data: ...}
func OK(c *gin.Context, data interface{}) {
c.JSON(http.StatusOK, gin.H{"success": true, "data": data})
}
// OKList responds with {success: true, data: [...], total: N} — used by ProTable
func OKList(c *gin.Context, data interface{}, total int64) {
c.JSON(http.StatusOK, gin.H{"success": true, "data": data, "total": total})
}
// Fail responds with {success: false, message: ...}
func Fail(c *gin.Context, status int, msg string) {
c.JSON(status, gin.H{"success": false, "message": msg})
}
// flexTimeLayouts is the set of time formats accepted from the frontend.
var flexTimeLayouts = []string{
"2006-01-02T15:04:05",
"2006-01-02 15:04:05",
"2006-01-02T15:04",
"2006-01-02 15:04",
"2006-01-02",
}
// BindJSONFlexTime works like c.ShouldBindJSON but also accepts non-RFC3339
// time strings such as "2026-02-28 00:00:00" that Ant Design's DatePicker sends.
// It reads the raw body, converts any date-like strings to RFC3339, then unmarshals.
func BindJSONFlexTime(c *gin.Context, obj interface{}) error {
body, err := io.ReadAll(c.Request.Body)
if err != nil {
return err
}
var raw map[string]json.RawMessage
if err := json.Unmarshal(body, &raw); err != nil {
return json.Unmarshal(body, obj) // fallback: let normal errors surface
}
for key, val := range raw {
var s string
if err := json.Unmarshal(val, &s); err != nil {
continue // not a string, skip
}
for _, layout := range flexTimeLayouts {
if t, err := time.Parse(layout, s); err == nil {
converted, _ := json.Marshal(t) // produces RFC3339
raw[key] = converted
break
}
}
}
fixed, err := json.Marshal(raw)
if err != nil {
return err
}
return json.Unmarshal(fixed, obj)
}
// LogOp writes a record to operation_logs table
func LogOp(c *gin.Context, opType, targetType, action string, targetID *uint, payload interface{}) {
db := common.GetDB()
user, _ := middleware.GetCurrentUser(c)
var userID *uint
if user != nil {
uid := uint(user.UserID)
userID = &uid
}
var changesStr string
if payload != nil {
b, _ := json.Marshal(payload)
changesStr = string(b)
}
db.Create(&models.OperationLog{
UserID: userID,
OperationType: opType,
TargetType: targetType,
Action: action,
TargetID: targetID,
Changes: changesStr,
IPAddress: c.ClientIP(),
UserAgent: c.GetHeader("User-Agent"),
Status: "success",
CreatedAt: time.Now(),
})
}