48889a1c90
- 上传新文件时删除同类型旧文件,避免文件堆积 - 文件名带时间戳,每次上传生成新 URL - 缓存时间设为 1 年 + immutable,文件更新后 URL 变化自动重新请求
860 lines
22 KiB
Go
860 lines
22 KiB
Go
package admin
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
"verification-platform-backend/internal/database"
|
|
"verification-platform-backend/internal/model"
|
|
"verification-platform-backend/internal/scheduler"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
func SetupSystemSettingsRoutes(r *gin.RouterGroup) {
|
|
settings := r.Group("/system-settings")
|
|
{
|
|
settings.GET("", handleGetSystemSettings)
|
|
settings.PUT("", handleUpdateSystemSettings)
|
|
settings.POST("/upload", handleUploadSystemImage)
|
|
settings.GET("/payment", handleGetPaymentSettings)
|
|
settings.PUT("/payment", handleUpdatePaymentSettings)
|
|
settings.GET("/email", handleGetEmailSettings)
|
|
settings.PUT("/email", handleUpdateEmailSettings)
|
|
settings.GET("/cleanup", handleGetCleanupSettings)
|
|
settings.PUT("/cleanup", handleUpdateCleanupSettings)
|
|
settings.POST("/cleanup/run", handleRunCleanup)
|
|
}
|
|
|
|
paymentChannels := r.Group("/payment-channels")
|
|
{
|
|
paymentChannels.GET("", handleGetPaymentChannels)
|
|
paymentChannels.POST("", handleCreatePaymentChannel)
|
|
paymentChannels.GET("/:id", handleGetPaymentChannel)
|
|
paymentChannels.PUT("/:id", handleUpdatePaymentChannel)
|
|
paymentChannels.DELETE("/:id", handleDeletePaymentChannel)
|
|
paymentChannels.PUT("/:id/status", handleUpdatePaymentChannelStatus)
|
|
paymentChannels.PUT("/batch/status", handleBatchUpdatePaymentChannelStatus)
|
|
paymentChannels.DELETE("/batch", handleBatchDeletePaymentChannels)
|
|
}
|
|
}
|
|
|
|
type SystemSettingsResponse struct {
|
|
SiteName string `json:"site_name"`
|
|
SiteLogo string `json:"site_logo"`
|
|
SiteFavicon string `json:"site_favicon"`
|
|
SiteFooter string `json:"site_footer"`
|
|
|
|
EnableCaptcha bool `json:"enable_captcha"`
|
|
LoginFailLockCount int `json:"login_fail_lock_count"`
|
|
LoginFailLockMinutes int `json:"login_fail_lock_minutes"`
|
|
PasswordMinLength int `json:"password_min_length"`
|
|
SessionTimeout int `json:"session_timeout"`
|
|
|
|
EnableBackup bool `json:"enable_backup"`
|
|
BackupInterval int `json:"backup_interval"`
|
|
BackupRetention int `json:"backup_retention"`
|
|
BackupStorageType string `json:"backup_storage_type"`
|
|
|
|
EnableTicketSystem bool `json:"enable_ticket_system"`
|
|
DefaultTheme string `json:"default_theme"`
|
|
EnableMultiLang bool `json:"enable_multi_lang"`
|
|
|
|
EnableNotification bool `json:"enable_notification"`
|
|
AdminNotifyEmail string `json:"admin_notify_email"`
|
|
NotifyOnLogin bool `json:"notify_on_login"`
|
|
NotifyOnRecharge bool `json:"notify_on_recharge"`
|
|
NotifyOnTicket bool `json:"notify_on_ticket"`
|
|
}
|
|
|
|
type PaymentSettingsResponse struct {
|
|
EnabledPaymentTypes []string `json:"enabled_payment_types"`
|
|
PaymentChannels []PaymentChannel `json:"payment_channels"`
|
|
}
|
|
|
|
type PaymentChannel struct {
|
|
ID uint `json:"id"`
|
|
Name string `json:"name"`
|
|
Type string `json:"type"`
|
|
Icon string `json:"icon"`
|
|
Status string `json:"status"`
|
|
Sort int `json:"sort"`
|
|
}
|
|
|
|
type EmailSettingsResponse struct {
|
|
SMTPHost string `json:"smtp_host"`
|
|
SMTPPort int `json:"smtp_port"`
|
|
SMTPUser string `json:"smtp_user"`
|
|
SMTPPassword string `json:"smtp_password"`
|
|
SMTPFrom string `json:"smtp_from"`
|
|
Enabled bool `json:"enabled"`
|
|
}
|
|
|
|
func handleGetSystemSettings(c *gin.Context) {
|
|
var settings []model.Setting
|
|
database.DB.Where("category = ?", "system").Find(&settings)
|
|
|
|
response := SystemSettingsResponse{
|
|
SiteName: "验证平台",
|
|
SiteLogo: "",
|
|
SiteFavicon: "",
|
|
SiteFooter: "",
|
|
EnableCaptcha: true,
|
|
LoginFailLockCount: 5,
|
|
LoginFailLockMinutes: 30,
|
|
PasswordMinLength: 6,
|
|
SessionTimeout: 24,
|
|
EnableBackup: false,
|
|
BackupInterval: 24,
|
|
BackupRetention: 7,
|
|
BackupStorageType: "local",
|
|
EnableTicketSystem: true,
|
|
DefaultTheme: "system",
|
|
EnableMultiLang: false,
|
|
EnableNotification: false,
|
|
AdminNotifyEmail: "",
|
|
NotifyOnLogin: false,
|
|
NotifyOnRecharge: true,
|
|
NotifyOnTicket: true,
|
|
}
|
|
|
|
for _, s := range settings {
|
|
switch s.Key {
|
|
case "site_name":
|
|
response.SiteName = s.Value
|
|
case "site_logo":
|
|
response.SiteLogo = s.Value
|
|
case "site_favicon":
|
|
response.SiteFavicon = s.Value
|
|
case "site_footer":
|
|
response.SiteFooter = s.Value
|
|
case "enable_captcha":
|
|
response.EnableCaptcha = s.Value == "true"
|
|
case "login_fail_lock_count":
|
|
response.LoginFailLockCount = parseSettingInt(s.Value, 5)
|
|
case "login_fail_lock_minutes":
|
|
response.LoginFailLockMinutes = parseSettingInt(s.Value, 30)
|
|
case "password_min_length":
|
|
response.PasswordMinLength = parseSettingInt(s.Value, 6)
|
|
case "session_timeout":
|
|
response.SessionTimeout = parseSettingInt(s.Value, 24)
|
|
case "enable_backup":
|
|
response.EnableBackup = s.Value == "true"
|
|
case "backup_interval":
|
|
response.BackupInterval = parseSettingInt(s.Value, 24)
|
|
case "backup_retention":
|
|
response.BackupRetention = parseSettingInt(s.Value, 7)
|
|
case "backup_storage_type":
|
|
response.BackupStorageType = s.Value
|
|
case "enable_ticket_system":
|
|
response.EnableTicketSystem = s.Value == "true"
|
|
case "default_theme":
|
|
response.DefaultTheme = s.Value
|
|
case "enable_multi_lang":
|
|
response.EnableMultiLang = s.Value == "true"
|
|
case "enable_notification":
|
|
response.EnableNotification = s.Value == "true"
|
|
case "admin_notify_email":
|
|
response.AdminNotifyEmail = s.Value
|
|
case "notify_on_login":
|
|
response.NotifyOnLogin = s.Value == "true"
|
|
case "notify_on_recharge":
|
|
response.NotifyOnRecharge = s.Value == "true"
|
|
case "notify_on_ticket":
|
|
response.NotifyOnTicket = s.Value == "true"
|
|
}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"code": 200,
|
|
"data": response,
|
|
})
|
|
}
|
|
|
|
type CleanupSettingsResponse struct {
|
|
EnableAutoCleanup bool `json:"enable_auto_cleanup"`
|
|
CleanupIntervalHours int `json:"cleanup_interval_hours"`
|
|
CaptchaRetentionDays int `json:"captcha_retention_days"`
|
|
VerifyCodeRetentionDays int `json:"verify_code_retention_days"`
|
|
ApiUsageRetentionDays int `json:"api_usage_retention_days"`
|
|
WebhookLogRetentionDays int `json:"webhook_log_retention_days"`
|
|
DeviceSessionRetentionDays int `json:"device_session_retention_days"`
|
|
}
|
|
|
|
func handleGetCleanupSettings(c *gin.Context) {
|
|
cfg := scheduler.GetCleanupConfig()
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"code": 200,
|
|
"data": CleanupSettingsResponse{
|
|
EnableAutoCleanup: cfg.EnableAutoCleanup,
|
|
CleanupIntervalHours: cfg.CleanupIntervalHours,
|
|
CaptchaRetentionDays: cfg.CaptchaRetentionDays,
|
|
VerifyCodeRetentionDays: cfg.VerifyCodeRetentionDays,
|
|
ApiUsageRetentionDays: cfg.ApiUsageRetentionDays,
|
|
WebhookLogRetentionDays: cfg.WebhookLogRetentionDays,
|
|
DeviceSessionRetentionDays: cfg.DeviceSessionRetentionDays,
|
|
},
|
|
})
|
|
}
|
|
|
|
func handleUpdateCleanupSettings(c *gin.Context) {
|
|
var req CleanupSettingsResponse
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
"code": 400,
|
|
"message": "无效的请求数据",
|
|
})
|
|
return
|
|
}
|
|
|
|
settings := []struct {
|
|
Key string
|
|
Value string
|
|
}{
|
|
{"enable_auto_cleanup", fmt.Sprintf("%v", req.EnableAutoCleanup)},
|
|
{"cleanup_interval_hours", fmt.Sprintf("%d", req.CleanupIntervalHours)},
|
|
{"captcha_retention_days", fmt.Sprintf("%d", req.CaptchaRetentionDays)},
|
|
{"verify_code_retention_days", fmt.Sprintf("%d", req.VerifyCodeRetentionDays)},
|
|
{"api_usage_retention_days", fmt.Sprintf("%d", req.ApiUsageRetentionDays)},
|
|
{"webhook_log_retention_days", fmt.Sprintf("%d", req.WebhookLogRetentionDays)},
|
|
{"device_session_retention_days", fmt.Sprintf("%d", req.DeviceSessionRetentionDays)},
|
|
}
|
|
|
|
for _, s := range settings {
|
|
var setting model.Setting
|
|
result := database.DB.Where("category = ? AND key = ?", "cleanup", s.Key).First(&setting)
|
|
if result.Error == nil {
|
|
setting.Value = s.Value
|
|
database.DB.Save(&setting)
|
|
} else {
|
|
setting = model.Setting{
|
|
Category: "cleanup",
|
|
Key: s.Key,
|
|
Value: s.Value,
|
|
}
|
|
database.DB.Create(&setting)
|
|
}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"code": 200,
|
|
"message": "保存成功",
|
|
})
|
|
}
|
|
|
|
func handleRunCleanup(c *gin.Context) {
|
|
cfg := scheduler.GetCleanupConfig()
|
|
scheduler.RunCleanup(cfg)
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"code": 200,
|
|
"message": "清理完成",
|
|
})
|
|
}
|
|
|
|
func parseSettingInt(value string, defaultValue int) int {
|
|
if value == "" {
|
|
return defaultValue
|
|
}
|
|
var result int
|
|
if _, err := fmt.Sscanf(value, "%d", &result); err != nil {
|
|
return defaultValue
|
|
}
|
|
return result
|
|
}
|
|
|
|
func handleUpdateSystemSettings(c *gin.Context) {
|
|
var req SystemSettingsResponse
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
"code": 400,
|
|
"message": "无效的请求数据",
|
|
})
|
|
return
|
|
}
|
|
|
|
settings := []struct {
|
|
Key string
|
|
Value string
|
|
}{
|
|
{"site_name", req.SiteName},
|
|
{"site_logo", req.SiteLogo},
|
|
{"site_favicon", req.SiteFavicon},
|
|
{"site_footer", req.SiteFooter},
|
|
{"enable_captcha", fmt.Sprintf("%v", req.EnableCaptcha)},
|
|
{"login_fail_lock_count", fmt.Sprintf("%d", req.LoginFailLockCount)},
|
|
{"login_fail_lock_minutes", fmt.Sprintf("%d", req.LoginFailLockMinutes)},
|
|
{"password_min_length", fmt.Sprintf("%d", req.PasswordMinLength)},
|
|
{"session_timeout", fmt.Sprintf("%d", req.SessionTimeout)},
|
|
{"enable_backup", fmt.Sprintf("%v", req.EnableBackup)},
|
|
{"backup_interval", fmt.Sprintf("%d", req.BackupInterval)},
|
|
{"backup_retention", fmt.Sprintf("%d", req.BackupRetention)},
|
|
{"backup_storage_type", req.BackupStorageType},
|
|
{"enable_ticket_system", fmt.Sprintf("%v", req.EnableTicketSystem)},
|
|
{"default_theme", req.DefaultTheme},
|
|
{"enable_multi_lang", fmt.Sprintf("%v", req.EnableMultiLang)},
|
|
{"enable_notification", fmt.Sprintf("%v", req.EnableNotification)},
|
|
{"admin_notify_email", req.AdminNotifyEmail},
|
|
{"notify_on_login", fmt.Sprintf("%v", req.NotifyOnLogin)},
|
|
{"notify_on_recharge", fmt.Sprintf("%v", req.NotifyOnRecharge)},
|
|
{"notify_on_ticket", fmt.Sprintf("%v", req.NotifyOnTicket)},
|
|
}
|
|
|
|
for _, s := range settings {
|
|
var setting model.Setting
|
|
result := database.DB.Where("category = ? AND key = ?", "system", s.Key).First(&setting)
|
|
if result.Error == nil {
|
|
setting.Value = s.Value
|
|
database.DB.Save(&setting)
|
|
} else {
|
|
setting = model.Setting{
|
|
Category: "system",
|
|
Key: s.Key,
|
|
Value: s.Value,
|
|
}
|
|
database.DB.Create(&setting)
|
|
}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"code": 200,
|
|
"message": "保存成功",
|
|
})
|
|
}
|
|
|
|
func handleUploadSystemImage(c *gin.Context) {
|
|
file, header, err := c.Request.FormFile("file")
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
"code": 400,
|
|
"message": "请选择要上传的文件",
|
|
})
|
|
return
|
|
}
|
|
defer file.Close()
|
|
|
|
uploadType := c.PostForm("type")
|
|
if uploadType != "logo" && uploadType != "favicon" {
|
|
uploadType = "logo"
|
|
}
|
|
|
|
ext := strings.ToLower(filepath.Ext(header.Filename))
|
|
allowedExts := map[string]bool{
|
|
".jpg": true,
|
|
".jpeg": true,
|
|
".png": true,
|
|
".gif": true,
|
|
".webp": true,
|
|
".svg": true,
|
|
".ico": true,
|
|
}
|
|
|
|
if !allowedExts[ext] {
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
"code": 400,
|
|
"message": "不支持的文件格式",
|
|
})
|
|
return
|
|
}
|
|
|
|
const maxSize = 2 * 1024 * 1024
|
|
if header.Size > maxSize {
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
"code": 400,
|
|
"message": "文件大小不能超过2MB",
|
|
})
|
|
return
|
|
}
|
|
|
|
uploadDir := "uploads/system"
|
|
if err := os.MkdirAll(uploadDir, 0755); err != nil {
|
|
log.Printf("创建上传目录失败: %v", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{
|
|
"code": 500,
|
|
"message": "创建上传目录失败",
|
|
})
|
|
return
|
|
}
|
|
|
|
pattern := filepath.Join(uploadDir, uploadType+"_*")
|
|
oldFiles, _ := filepath.Glob(pattern)
|
|
for _, oldFile := range oldFiles {
|
|
os.Remove(oldFile)
|
|
}
|
|
|
|
filename := fmt.Sprintf("%s_%d%s", uploadType, time.Now().UnixNano(), ext)
|
|
filePath := filepath.Join(uploadDir, filename)
|
|
|
|
dst, err := os.Create(filePath)
|
|
if err != nil {
|
|
log.Printf("创建文件失败: %v", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{
|
|
"code": 500,
|
|
"message": "创建文件失败",
|
|
})
|
|
return
|
|
}
|
|
defer dst.Close()
|
|
|
|
if _, err := io.Copy(dst, file); err != nil {
|
|
log.Printf("保存文件失败: %v", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{
|
|
"code": 500,
|
|
"message": "保存文件失败",
|
|
})
|
|
return
|
|
}
|
|
|
|
imageURL := "/uploads/system/" + filename
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"code": 200,
|
|
"data": gin.H{
|
|
"url": imageURL,
|
|
},
|
|
})
|
|
}
|
|
|
|
func handleGetPaymentSettings(c *gin.Context) {
|
|
var channels []model.PaymentChannel
|
|
database.DB.Order("sort asc").Find(&channels)
|
|
|
|
var paymentChannels []PaymentChannel
|
|
for _, ch := range channels {
|
|
paymentChannels = append(paymentChannels, PaymentChannel{
|
|
ID: ch.ID,
|
|
Name: ch.Name,
|
|
Type: ch.Type,
|
|
Icon: ch.Icon,
|
|
Status: ch.Status,
|
|
Sort: ch.Sort,
|
|
})
|
|
}
|
|
|
|
var enabledTypes []string
|
|
var setting model.Setting
|
|
if err := database.DB.Where("category = ? AND key = ?", "payment", "enabled_types").First(&setting).Error; err == nil {
|
|
if setting.Value != "" {
|
|
enabledTypes = []string{setting.Value}
|
|
}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"code": 200,
|
|
"data": gin.H{
|
|
"enabled_payment_types": enabledTypes,
|
|
"payment_channels": paymentChannels,
|
|
},
|
|
})
|
|
}
|
|
|
|
func handleUpdatePaymentSettings(c *gin.Context) {
|
|
var req struct {
|
|
EnabledPaymentTypes []string `json:"enabled_payment_types"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
"code": 400,
|
|
"message": "无效的请求数据",
|
|
})
|
|
return
|
|
}
|
|
|
|
enabledTypes := ""
|
|
if len(req.EnabledPaymentTypes) > 0 {
|
|
enabledTypes = req.EnabledPaymentTypes[0]
|
|
}
|
|
|
|
var setting model.Setting
|
|
result := database.DB.Where("category = ? AND key = ?", "payment", "enabled_types").First(&setting)
|
|
if result.Error == nil {
|
|
setting.Value = enabledTypes
|
|
database.DB.Save(&setting)
|
|
} else {
|
|
setting = model.Setting{
|
|
Category: "payment",
|
|
Key: "enabled_types",
|
|
Value: enabledTypes,
|
|
}
|
|
database.DB.Create(&setting)
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"code": 200,
|
|
"message": "保存成功",
|
|
})
|
|
}
|
|
|
|
func handleGetEmailSettings(c *gin.Context) {
|
|
var settings []model.Setting
|
|
database.DB.Where("category = ?", "email").Find(&settings)
|
|
|
|
response := EmailSettingsResponse{
|
|
SMTPHost: "",
|
|
SMTPPort: 587,
|
|
SMTPUser: "",
|
|
SMTPPassword: "",
|
|
SMTPFrom: "",
|
|
Enabled: false,
|
|
}
|
|
|
|
for _, s := range settings {
|
|
switch s.Key {
|
|
case "smtp_host":
|
|
response.SMTPHost = s.Value
|
|
case "smtp_port":
|
|
var port int
|
|
if _, err := fmt.Sscanf(s.Value, "%d", &port); err == nil {
|
|
response.SMTPPort = port
|
|
}
|
|
case "smtp_user":
|
|
response.SMTPUser = s.Value
|
|
case "smtp_password":
|
|
response.SMTPPassword = s.Value
|
|
case "smtp_from":
|
|
response.SMTPFrom = s.Value
|
|
case "enabled":
|
|
response.Enabled = s.Value == "true"
|
|
}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"code": 200,
|
|
"data": response,
|
|
})
|
|
}
|
|
|
|
func handleUpdateEmailSettings(c *gin.Context) {
|
|
var req EmailSettingsRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
"code": 400,
|
|
"message": "无效的请求数据",
|
|
})
|
|
return
|
|
}
|
|
|
|
settings := []struct {
|
|
Key string
|
|
Value string
|
|
}{
|
|
{"smtp_host", req.SMTPHost},
|
|
{"smtp_port", fmt.Sprintf("%d", req.SMTPPort)},
|
|
{"smtp_user", req.SMTPUser},
|
|
{"smtp_password", req.SMTPPassword},
|
|
{"smtp_from", req.SMTPFrom},
|
|
{"enabled", fmt.Sprintf("%v", req.Enabled)},
|
|
}
|
|
|
|
for _, s := range settings {
|
|
var setting model.Setting
|
|
result := database.DB.Where("category = ? AND key = ?", "email", s.Key).First(&setting)
|
|
if result.Error == nil {
|
|
setting.Value = s.Value
|
|
database.DB.Save(&setting)
|
|
} else {
|
|
setting = model.Setting{
|
|
Category: "email",
|
|
Key: s.Key,
|
|
Value: s.Value,
|
|
}
|
|
database.DB.Create(&setting)
|
|
}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"code": 200,
|
|
"message": "保存成功",
|
|
})
|
|
}
|
|
|
|
type EmailSettingsRequest struct {
|
|
SMTPHost string `json:"smtp_host"`
|
|
SMTPPort int `json:"smtp_port"`
|
|
SMTPUser string `json:"smtp_user"`
|
|
SMTPPassword string `json:"smtp_password"`
|
|
SMTPFrom string `json:"smtp_from"`
|
|
Enabled bool `json:"enabled"`
|
|
}
|
|
|
|
func handleGetPaymentChannels(c *gin.Context) {
|
|
var channels []model.PaymentChannel
|
|
database.DB.Order("sort asc").Find(&channels)
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"code": 200,
|
|
"data": channels,
|
|
})
|
|
}
|
|
|
|
type CreatePaymentChannelRequest struct {
|
|
Name string `json:"name" binding:"required"`
|
|
Type string `json:"type" binding:"required"`
|
|
Icon string `json:"icon"`
|
|
Config string `json:"config"`
|
|
Sort int `json:"sort"`
|
|
Remark string `json:"remark"`
|
|
}
|
|
|
|
func handleCreatePaymentChannel(c *gin.Context) {
|
|
var req CreatePaymentChannelRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
"code": 400,
|
|
"message": "无效的请求数据",
|
|
})
|
|
return
|
|
}
|
|
|
|
channel := model.PaymentChannel{
|
|
Name: req.Name,
|
|
Type: req.Type,
|
|
Icon: req.Icon,
|
|
Config: req.Config,
|
|
Sort: req.Sort,
|
|
Status: "active",
|
|
Remark: req.Remark,
|
|
}
|
|
|
|
if err := database.DB.Create(&channel).Error; err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{
|
|
"code": 500,
|
|
"message": "创建失败",
|
|
})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"code": 200,
|
|
"message": "创建成功",
|
|
"data": channel,
|
|
})
|
|
}
|
|
|
|
func handleGetPaymentChannel(c *gin.Context) {
|
|
id := c.Param("id")
|
|
|
|
var channel model.PaymentChannel
|
|
if err := database.DB.First(&channel, id).Error; err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{
|
|
"code": 404,
|
|
"message": "支付渠道不存在",
|
|
})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"code": 200,
|
|
"data": channel,
|
|
})
|
|
}
|
|
|
|
type UpdatePaymentChannelRequest struct {
|
|
Name string `json:"name"`
|
|
Type string `json:"type"`
|
|
Icon string `json:"icon"`
|
|
Config string `json:"config"`
|
|
Sort int `json:"sort"`
|
|
Remark string `json:"remark"`
|
|
}
|
|
|
|
func handleUpdatePaymentChannel(c *gin.Context) {
|
|
id := c.Param("id")
|
|
|
|
var channel model.PaymentChannel
|
|
if err := database.DB.First(&channel, id).Error; err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{
|
|
"code": 404,
|
|
"message": "支付渠道不存在",
|
|
})
|
|
return
|
|
}
|
|
|
|
var req UpdatePaymentChannelRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
"code": 400,
|
|
"message": "无效的请求数据",
|
|
})
|
|
return
|
|
}
|
|
|
|
if req.Name != "" {
|
|
channel.Name = req.Name
|
|
}
|
|
if req.Type != "" {
|
|
channel.Type = req.Type
|
|
}
|
|
channel.Icon = req.Icon
|
|
channel.Config = req.Config
|
|
channel.Sort = req.Sort
|
|
channel.Remark = req.Remark
|
|
|
|
if err := database.DB.Save(&channel).Error; err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{
|
|
"code": 500,
|
|
"message": "更新失败",
|
|
})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"code": 200,
|
|
"message": "更新成功",
|
|
"data": channel,
|
|
})
|
|
}
|
|
|
|
func handleDeletePaymentChannel(c *gin.Context) {
|
|
id := c.Param("id")
|
|
|
|
var channel model.PaymentChannel
|
|
if err := database.DB.First(&channel, id).Error; err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{
|
|
"code": 404,
|
|
"message": "支付渠道不存在",
|
|
})
|
|
return
|
|
}
|
|
|
|
if err := database.DB.Delete(&channel).Error; err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{
|
|
"code": 500,
|
|
"message": "删除失败",
|
|
})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"code": 200,
|
|
"message": "删除成功",
|
|
})
|
|
}
|
|
|
|
type UpdatePaymentChannelStatusRequest struct {
|
|
Status string `json:"status" binding:"required"`
|
|
}
|
|
|
|
func handleUpdatePaymentChannelStatus(c *gin.Context) {
|
|
id := c.Param("id")
|
|
|
|
var channel model.PaymentChannel
|
|
if err := database.DB.First(&channel, id).Error; err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{
|
|
"code": 404,
|
|
"message": "支付渠道不存在",
|
|
})
|
|
return
|
|
}
|
|
|
|
var req UpdatePaymentChannelStatusRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
"code": 400,
|
|
"message": "无效的请求数据",
|
|
})
|
|
return
|
|
}
|
|
|
|
channel.Status = req.Status
|
|
if err := database.DB.Save(&channel).Error; err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{
|
|
"code": 500,
|
|
"message": "更新失败",
|
|
})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"code": 200,
|
|
"message": "更新成功",
|
|
})
|
|
}
|
|
|
|
type BatchUpdatePaymentChannelStatusRequest struct {
|
|
IDs []uint `json:"ids"`
|
|
Status string `json:"status"`
|
|
}
|
|
|
|
func handleBatchUpdatePaymentChannelStatus(c *gin.Context) {
|
|
var req BatchUpdatePaymentChannelStatusRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
"code": 400,
|
|
"message": "参数错误",
|
|
})
|
|
return
|
|
}
|
|
|
|
if req.Status != "active" && req.Status != "inactive" {
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
"code": 400,
|
|
"message": "无效的状态",
|
|
})
|
|
return
|
|
}
|
|
|
|
if len(req.IDs) == 0 {
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
"code": 400,
|
|
"message": "请选择要更新的支付渠道",
|
|
})
|
|
return
|
|
}
|
|
|
|
if err := database.DB.Model(&model.PaymentChannel{}).Where("id IN ?", req.IDs).Update("status", req.Status).Error; err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{
|
|
"code": 500,
|
|
"message": "批量更新失败",
|
|
})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"code": 200,
|
|
"message": "批量更新成功",
|
|
})
|
|
}
|
|
|
|
type BatchDeletePaymentChannelsRequest struct {
|
|
IDs []uint `json:"ids"`
|
|
}
|
|
|
|
func handleBatchDeletePaymentChannels(c *gin.Context) {
|
|
var req BatchDeletePaymentChannelsRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
"code": 400,
|
|
"message": "参数错误",
|
|
})
|
|
return
|
|
}
|
|
|
|
if len(req.IDs) == 0 {
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
"code": 400,
|
|
"message": "请选择要删除的支付渠道",
|
|
})
|
|
return
|
|
}
|
|
|
|
if err := database.DB.Where("id IN ?", req.IDs).Delete(&model.PaymentChannel{}).Error; err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{
|
|
"code": 500,
|
|
"message": "批量删除失败",
|
|
})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"code": 200,
|
|
"message": "批量删除成功",
|
|
})
|
|
}
|