237 lines
4.3 KiB
Go
237 lines
4.3 KiB
Go
package handler
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
"xboard-go/internal/database"
|
|
"xboard-go/internal/model"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
func Success(c *gin.Context, data any) {
|
|
c.JSON(http.StatusOK, gin.H{"data": data})
|
|
}
|
|
|
|
func SuccessMessage(c *gin.Context, message string, data any) {
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"message": message,
|
|
"data": data,
|
|
})
|
|
}
|
|
|
|
func Fail(c *gin.Context, status int, message string) {
|
|
c.JSON(status, gin.H{"message": message})
|
|
}
|
|
|
|
func NotImplemented(endpoint string) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
c.JSON(http.StatusNotImplemented, gin.H{
|
|
"message": "not implemented yet",
|
|
"endpoint": endpoint,
|
|
})
|
|
}
|
|
}
|
|
|
|
func intFromAny(value any) int {
|
|
switch typed := value.(type) {
|
|
case int:
|
|
return typed
|
|
case int8:
|
|
return int(typed)
|
|
case int16:
|
|
return int(typed)
|
|
case int32:
|
|
return int(typed)
|
|
case int64:
|
|
return int(typed)
|
|
case float32:
|
|
return int(typed)
|
|
case float64:
|
|
return int(typed)
|
|
case json.Number:
|
|
parsed, _ := typed.Int64()
|
|
return int(parsed)
|
|
case string:
|
|
parsed, _ := strconv.Atoi(strings.TrimSpace(typed))
|
|
return parsed
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func stringFromAny(value any) string {
|
|
switch typed := value.(type) {
|
|
case string:
|
|
return typed
|
|
case json.Number:
|
|
return typed.String()
|
|
case float64:
|
|
return strconv.FormatFloat(typed, 'f', -1, 64)
|
|
case float32:
|
|
return strconv.FormatFloat(float64(typed), 'f', -1, 32)
|
|
case int:
|
|
return strconv.Itoa(typed)
|
|
case int64:
|
|
return strconv.FormatInt(typed, 10)
|
|
case bool:
|
|
if typed {
|
|
return "true"
|
|
}
|
|
return "false"
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
func boolFromAny(value any) bool {
|
|
switch typed := value.(type) {
|
|
case bool:
|
|
return typed
|
|
case int:
|
|
return typed != 0
|
|
case int64:
|
|
return typed != 0
|
|
case float64:
|
|
return typed != 0
|
|
case string:
|
|
text := strings.TrimSpace(strings.ToLower(typed))
|
|
return text == "1" || text == "true" || text == "yes" || text == "on"
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func intValue(value *int) any {
|
|
if value == nil {
|
|
return nil
|
|
}
|
|
return *value
|
|
}
|
|
|
|
func int64Value(value *int64) any {
|
|
if value == nil {
|
|
return nil
|
|
}
|
|
return *value
|
|
}
|
|
|
|
func stringValue(value *string) string {
|
|
if value == nil {
|
|
return ""
|
|
}
|
|
return *value
|
|
}
|
|
|
|
func unixTimeValue(value *time.Time) any {
|
|
if value == nil {
|
|
return nil
|
|
}
|
|
return value.Unix()
|
|
}
|
|
|
|
func marshalJSON(value any, fallbackEmptyArray bool) (*string, error) {
|
|
if value == nil {
|
|
if fallbackEmptyArray {
|
|
empty := "[]"
|
|
return &empty, nil
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
if text, ok := value.(string); ok {
|
|
text = strings.TrimSpace(text)
|
|
if text == "" {
|
|
if fallbackEmptyArray {
|
|
empty := "[]"
|
|
return &empty, nil
|
|
}
|
|
return nil, nil
|
|
}
|
|
if json.Valid([]byte(text)) {
|
|
return &text, nil
|
|
}
|
|
}
|
|
|
|
payload, err := json.Marshal(value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
text := string(payload)
|
|
return &text, nil
|
|
}
|
|
|
|
func sanitizePositiveIDs(ids []int) []int {
|
|
unique := make(map[int]struct{}, len(ids))
|
|
result := make([]int, 0, len(ids))
|
|
for _, id := range ids {
|
|
if id <= 0 {
|
|
continue
|
|
}
|
|
if _, exists := unique[id]; exists {
|
|
continue
|
|
}
|
|
unique[id] = struct{}{}
|
|
result = append(result, id)
|
|
}
|
|
sort.Ints(result)
|
|
return result
|
|
}
|
|
|
|
func loadServerGroupNameMap() map[int]string {
|
|
var groups []model.ServerGroup
|
|
_ = database.DB.Find(&groups).Error
|
|
|
|
result := make(map[int]string, len(groups))
|
|
for _, group := range groups {
|
|
result[group.ID] = group.Name
|
|
}
|
|
return result
|
|
}
|
|
|
|
func loadUserEmailMap(userIDs []int) map[int]string {
|
|
result := make(map[int]string)
|
|
if len(userIDs) == 0 {
|
|
return result
|
|
}
|
|
|
|
var users []model.User
|
|
if err := database.DB.Select("id", "email").Where("id IN ?", sanitizePositiveIDs(userIDs)).Find(&users).Error; err != nil {
|
|
return result
|
|
}
|
|
|
|
for _, user := range users {
|
|
result[user.ID] = user.Email
|
|
}
|
|
return result
|
|
}
|
|
|
|
func loadTicketMessageCountMap(ticketIDs []int) map[int]int64 {
|
|
result := make(map[int]int64)
|
|
if len(ticketIDs) == 0 {
|
|
return result
|
|
}
|
|
|
|
type ticketCount struct {
|
|
TicketID int
|
|
Total int64
|
|
}
|
|
var counts []ticketCount
|
|
if err := database.DB.Model(&model.TicketMessage{}).
|
|
Select("ticket_id, COUNT(*) AS total").
|
|
Where("ticket_id IN ?", sanitizePositiveIDs(ticketIDs)).
|
|
Group("ticket_id").
|
|
Scan(&counts).Error; err != nil {
|
|
return result
|
|
}
|
|
|
|
for _, item := range counts {
|
|
result[item.TicketID] = item.Total
|
|
}
|
|
return result
|
|
}
|