mirror of
https://github.com/ctwj/urldb.git
synced 2025-11-25 19:37:33 +08:00
fix: 修复有可能配置丢失的问题
This commit is contained in:
@@ -97,37 +97,100 @@ func RequestToSystemConfig(req *dto.SystemConfigRequest) []entity.SystemConfig {
|
||||
}
|
||||
|
||||
var configs []entity.SystemConfig
|
||||
var updatedKeys []string
|
||||
|
||||
// 字符串字段 - 处理所有字段,包括空值
|
||||
// 对于广告相关字段,允许空值以便清空配置
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeySiteTitle, Value: req.SiteTitle, Type: entity.ConfigTypeString})
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeySiteDescription, Value: req.SiteDescription, Type: entity.ConfigTypeString})
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyKeywords, Value: req.Keywords, Type: entity.ConfigTypeString})
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyAuthor, Value: req.Author, Type: entity.ConfigTypeString})
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyCopyright, Value: req.Copyright, Type: entity.ConfigTypeString})
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeySiteLogo, Value: req.SiteLogo, Type: entity.ConfigTypeString})
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyApiToken, Value: req.ApiToken, Type: entity.ConfigTypeString})
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyForbiddenWords, Value: req.ForbiddenWords, Type: entity.ConfigTypeString})
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyAdKeywords, Value: req.AdKeywords, Type: entity.ConfigTypeString})
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyAutoInsertAd, Value: req.AutoInsertAd, Type: entity.ConfigTypeString})
|
||||
// 字符串字段 - 只处理被设置的字段
|
||||
if req.SiteTitle != nil {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeySiteTitle, Value: *req.SiteTitle, Type: entity.ConfigTypeString})
|
||||
updatedKeys = append(updatedKeys, entity.ConfigKeySiteTitle)
|
||||
}
|
||||
if req.SiteDescription != nil {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeySiteDescription, Value: *req.SiteDescription, Type: entity.ConfigTypeString})
|
||||
updatedKeys = append(updatedKeys, entity.ConfigKeySiteDescription)
|
||||
}
|
||||
if req.Keywords != nil {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyKeywords, Value: *req.Keywords, Type: entity.ConfigTypeString})
|
||||
updatedKeys = append(updatedKeys, entity.ConfigKeyKeywords)
|
||||
}
|
||||
if req.Author != nil {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyAuthor, Value: *req.Author, Type: entity.ConfigTypeString})
|
||||
updatedKeys = append(updatedKeys, entity.ConfigKeyAuthor)
|
||||
}
|
||||
if req.Copyright != nil {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyCopyright, Value: *req.Copyright, Type: entity.ConfigTypeString})
|
||||
updatedKeys = append(updatedKeys, entity.ConfigKeyCopyright)
|
||||
}
|
||||
if req.SiteLogo != nil {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeySiteLogo, Value: *req.SiteLogo, Type: entity.ConfigTypeString})
|
||||
updatedKeys = append(updatedKeys, entity.ConfigKeySiteLogo)
|
||||
}
|
||||
if req.ApiToken != nil {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyApiToken, Value: *req.ApiToken, Type: entity.ConfigTypeString})
|
||||
updatedKeys = append(updatedKeys, entity.ConfigKeyApiToken)
|
||||
}
|
||||
if req.ForbiddenWords != nil {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyForbiddenWords, Value: *req.ForbiddenWords, Type: entity.ConfigTypeString})
|
||||
updatedKeys = append(updatedKeys, entity.ConfigKeyForbiddenWords)
|
||||
}
|
||||
if req.AdKeywords != nil {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyAdKeywords, Value: *req.AdKeywords, Type: entity.ConfigTypeString})
|
||||
updatedKeys = append(updatedKeys, entity.ConfigKeyAdKeywords)
|
||||
}
|
||||
if req.AutoInsertAd != nil {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyAutoInsertAd, Value: *req.AutoInsertAd, Type: entity.ConfigTypeString})
|
||||
updatedKeys = append(updatedKeys, entity.ConfigKeyAutoInsertAd)
|
||||
}
|
||||
|
||||
// 布尔值字段 - 只处理实际提交的字段
|
||||
// 注意:由于 Go 的零值机制,我们需要通过其他方式判断字段是否被提交
|
||||
// 这里暂时保持原样,但建议前端只提交有变化的字段
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyAutoProcessReadyResources, Value: strconv.FormatBool(req.AutoProcessReadyResources), Type: entity.ConfigTypeBool})
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyAutoTransferEnabled, Value: strconv.FormatBool(req.AutoTransferEnabled), Type: entity.ConfigTypeBool})
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyAutoFetchHotDramaEnabled, Value: strconv.FormatBool(req.AutoFetchHotDramaEnabled), Type: entity.ConfigTypeBool})
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyMaintenanceMode, Value: strconv.FormatBool(req.MaintenanceMode), Type: entity.ConfigTypeBool})
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyEnableRegister, Value: strconv.FormatBool(req.EnableRegister), Type: entity.ConfigTypeBool})
|
||||
// 布尔值字段 - 只处理被设置的字段
|
||||
if req.AutoProcessReadyResources != nil {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyAutoProcessReadyResources, Value: strconv.FormatBool(*req.AutoProcessReadyResources), Type: entity.ConfigTypeBool})
|
||||
updatedKeys = append(updatedKeys, entity.ConfigKeyAutoProcessReadyResources)
|
||||
}
|
||||
if req.AutoTransferEnabled != nil {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyAutoTransferEnabled, Value: strconv.FormatBool(*req.AutoTransferEnabled), Type: entity.ConfigTypeBool})
|
||||
updatedKeys = append(updatedKeys, entity.ConfigKeyAutoTransferEnabled)
|
||||
}
|
||||
if req.AutoFetchHotDramaEnabled != nil {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyAutoFetchHotDramaEnabled, Value: strconv.FormatBool(*req.AutoFetchHotDramaEnabled), Type: entity.ConfigTypeBool})
|
||||
updatedKeys = append(updatedKeys, entity.ConfigKeyAutoFetchHotDramaEnabled)
|
||||
}
|
||||
if req.MaintenanceMode != nil {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyMaintenanceMode, Value: strconv.FormatBool(*req.MaintenanceMode), Type: entity.ConfigTypeBool})
|
||||
updatedKeys = append(updatedKeys, entity.ConfigKeyMaintenanceMode)
|
||||
}
|
||||
if req.EnableRegister != nil {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyEnableRegister, Value: strconv.FormatBool(*req.EnableRegister), Type: entity.ConfigTypeBool})
|
||||
updatedKeys = append(updatedKeys, entity.ConfigKeyEnableRegister)
|
||||
}
|
||||
|
||||
// 整数字段 - 添加所有提交的字段,包括0值
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyAutoProcessInterval, Value: strconv.Itoa(req.AutoProcessInterval), Type: entity.ConfigTypeInt})
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyAutoTransferLimitDays, Value: strconv.Itoa(req.AutoTransferLimitDays), Type: entity.ConfigTypeInt})
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyAutoTransferMinSpace, Value: strconv.Itoa(req.AutoTransferMinSpace), Type: entity.ConfigTypeInt})
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyPageSize, Value: strconv.Itoa(req.PageSize), Type: entity.ConfigTypeInt})
|
||||
// 整数字段 - 只处理被设置的字段
|
||||
if req.AutoProcessInterval != nil {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyAutoProcessInterval, Value: strconv.Itoa(*req.AutoProcessInterval), Type: entity.ConfigTypeInt})
|
||||
updatedKeys = append(updatedKeys, entity.ConfigKeyAutoProcessInterval)
|
||||
}
|
||||
if req.AutoTransferLimitDays != nil {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyAutoTransferLimitDays, Value: strconv.Itoa(*req.AutoTransferLimitDays), Type: entity.ConfigTypeInt})
|
||||
updatedKeys = append(updatedKeys, entity.ConfigKeyAutoTransferLimitDays)
|
||||
}
|
||||
if req.AutoTransferMinSpace != nil {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyAutoTransferMinSpace, Value: strconv.Itoa(*req.AutoTransferMinSpace), Type: entity.ConfigTypeInt})
|
||||
updatedKeys = append(updatedKeys, entity.ConfigKeyAutoTransferMinSpace)
|
||||
}
|
||||
if req.PageSize != nil {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyPageSize, Value: strconv.Itoa(*req.PageSize), Type: entity.ConfigTypeInt})
|
||||
updatedKeys = append(updatedKeys, entity.ConfigKeyPageSize)
|
||||
}
|
||||
|
||||
// 三方统计配置
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyThirdPartyStatsCode, Value: req.ThirdPartyStatsCode, Type: entity.ConfigTypeString})
|
||||
// 三方统计配置 - 只处理被设置的字段
|
||||
if req.ThirdPartyStatsCode != nil {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyThirdPartyStatsCode, Value: *req.ThirdPartyStatsCode, Type: entity.ConfigTypeString})
|
||||
updatedKeys = append(updatedKeys, entity.ConfigKeyThirdPartyStatsCode)
|
||||
}
|
||||
|
||||
// 记录更新的配置项
|
||||
if len(updatedKeys) > 0 {
|
||||
utils.Info("配置更新 - 被修改的配置项: %v", updatedKeys)
|
||||
}
|
||||
|
||||
return configs
|
||||
}
|
||||
|
||||
@@ -3,38 +3,38 @@ package dto
|
||||
// SystemConfigRequest 系统配置请求
|
||||
type SystemConfigRequest struct {
|
||||
// SEO 配置
|
||||
SiteTitle string `json:"site_title"`
|
||||
SiteDescription string `json:"site_description"`
|
||||
Keywords string `json:"keywords"`
|
||||
Author string `json:"author"`
|
||||
Copyright string `json:"copyright"`
|
||||
SiteLogo string `json:"site_logo"`
|
||||
SiteTitle *string `json:"site_title,omitempty"`
|
||||
SiteDescription *string `json:"site_description,omitempty"`
|
||||
Keywords *string `json:"keywords,omitempty"`
|
||||
Author *string `json:"author,omitempty"`
|
||||
Copyright *string `json:"copyright,omitempty"`
|
||||
SiteLogo *string `json:"site_logo,omitempty"`
|
||||
|
||||
// 自动处理配置组
|
||||
AutoProcessReadyResources bool `json:"auto_process_ready_resources"` // 自动处理待处理资源
|
||||
AutoProcessInterval int `json:"auto_process_interval"` // 自动处理间隔(分钟)
|
||||
AutoTransferEnabled bool `json:"auto_transfer_enabled"` // 开启自动转存
|
||||
AutoTransferLimitDays int `json:"auto_transfer_limit_days"` // 自动转存限制天数(0表示不限制)
|
||||
AutoTransferMinSpace int `json:"auto_transfer_min_space"` // 最小存储空间(GB)
|
||||
AutoFetchHotDramaEnabled bool `json:"auto_fetch_hot_drama_enabled"` // 自动拉取热播剧名字
|
||||
AutoProcessReadyResources *bool `json:"auto_process_ready_resources,omitempty"` // 自动处理待处理资源
|
||||
AutoProcessInterval *int `json:"auto_process_interval,omitempty"` // 自动处理间隔(分钟)
|
||||
AutoTransferEnabled *bool `json:"auto_transfer_enabled,omitempty"` // 开启自动转存
|
||||
AutoTransferLimitDays *int `json:"auto_transfer_limit_days,omitempty"` // 自动转存限制天数(0表示不限制)
|
||||
AutoTransferMinSpace *int `json:"auto_transfer_min_space,omitempty"` // 最小存储空间(GB)
|
||||
AutoFetchHotDramaEnabled *bool `json:"auto_fetch_hot_drama_enabled,omitempty"` // 自动拉取热播剧名字
|
||||
|
||||
// API配置
|
||||
ApiToken string `json:"api_token"` // 公开API访问令牌
|
||||
ApiToken *string `json:"api_token,omitempty"` // 公开API访问令牌
|
||||
|
||||
// 违禁词配置
|
||||
ForbiddenWords string `json:"forbidden_words"` // 违禁词列表,用逗号分隔
|
||||
ForbiddenWords *string `json:"forbidden_words,omitempty"` // 违禁词列表,用逗号分隔
|
||||
|
||||
// 广告配置
|
||||
AdKeywords string `json:"ad_keywords"` // 广告关键词列表,用逗号分隔
|
||||
AutoInsertAd string `json:"auto_insert_ad"` // 自动插入广告内容
|
||||
AdKeywords *string `json:"ad_keywords,omitempty"` // 广告关键词列表,用逗号分隔
|
||||
AutoInsertAd *string `json:"auto_insert_ad,omitempty"` // 自动插入广告内容
|
||||
|
||||
// 其他配置
|
||||
PageSize int `json:"page_size"`
|
||||
MaintenanceMode bool `json:"maintenance_mode"`
|
||||
EnableRegister bool `json:"enable_register"` // 开启注册功能
|
||||
PageSize *int `json:"page_size,omitempty"`
|
||||
MaintenanceMode *bool `json:"maintenance_mode,omitempty"`
|
||||
EnableRegister *bool `json:"enable_register,omitempty"` // 开启注册功能
|
||||
|
||||
// 三方统计配置
|
||||
ThirdPartyStatsCode string `json:"third_party_stats_code"` // 三方统计代码
|
||||
ThirdPartyStatsCode *string `json:"third_party_stats_code,omitempty"` // 三方统计代码
|
||||
}
|
||||
|
||||
// SystemConfigResponse 系统配置响应
|
||||
@@ -66,7 +66,7 @@ type SystemConfigResponse struct {
|
||||
ForbiddenWords string `json:"forbidden_words"` // 违禁词列表,用逗号分隔
|
||||
|
||||
// 广告配置
|
||||
AdKeywords string `json:"ad_keywords"` // 广告关键词列表,用逗号分隔
|
||||
AdKeywords string `json:"ad_keywords"` // 广告关键词列表,用逗号分隔
|
||||
AutoInsertAd string `json:"auto_insert_ad"` // 自动插入广告内容
|
||||
|
||||
// 其他配置
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/ctwj/urldb/db/entity"
|
||||
"github.com/ctwj/urldb/utils"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@@ -21,6 +22,8 @@ type SystemConfigRepository interface {
|
||||
GetConfigInt(key string) (int, error)
|
||||
GetCachedConfigs() map[string]string
|
||||
ClearConfigCache()
|
||||
SafeRefreshConfigCache() error
|
||||
ValidateConfigIntegrity() error
|
||||
}
|
||||
|
||||
// SystemConfigRepositoryImpl 系统配置Repository实现
|
||||
@@ -60,27 +63,39 @@ func (r *SystemConfigRepositoryImpl) FindByKey(key string) (*entity.SystemConfig
|
||||
|
||||
// UpsertConfigs 批量创建或更新配置
|
||||
func (r *SystemConfigRepositoryImpl) UpsertConfigs(configs []entity.SystemConfig) error {
|
||||
for _, config := range configs {
|
||||
var existingConfig entity.SystemConfig
|
||||
err := r.db.Where("key = ?", config.Key).First(&existingConfig).Error
|
||||
// 使用事务确保数据一致性
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
// 在更新前备份当前配置
|
||||
var existingConfigs []entity.SystemConfig
|
||||
if err := tx.Find(&existingConfigs).Error; err != nil {
|
||||
utils.Error("备份配置失败: %v", err)
|
||||
// 不返回错误,继续执行更新
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
// 如果不存在,则创建
|
||||
if err := r.db.Create(&config).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// 如果存在,则更新
|
||||
config.ID = existingConfig.ID
|
||||
if err := r.db.Save(&config).Error; err != nil {
|
||||
return err
|
||||
for _, config := range configs {
|
||||
var existingConfig entity.SystemConfig
|
||||
err := tx.Where("key = ?", config.Key).First(&existingConfig).Error
|
||||
|
||||
if err != nil {
|
||||
// 如果不存在,则创建
|
||||
if err := tx.Create(&config).Error; err != nil {
|
||||
utils.Error("创建配置失败 [%s]: %v", config.Key, err)
|
||||
return fmt.Errorf("创建配置失败 [%s]: %v", config.Key, err)
|
||||
}
|
||||
} else {
|
||||
// 如果存在,则更新
|
||||
config.ID = existingConfig.ID
|
||||
if err := tx.Save(&config).Error; err != nil {
|
||||
utils.Error("更新配置失败 [%s]: %v", config.Key, err)
|
||||
return fmt.Errorf("更新配置失败 [%s]: %v", config.Key, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新配置后刷新缓存
|
||||
r.refreshConfigCache()
|
||||
return nil
|
||||
// 更新成功后刷新缓存
|
||||
r.refreshConfigCache()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// GetOrCreateDefault 获取配置或创建默认配置
|
||||
@@ -92,6 +107,7 @@ func (r *SystemConfigRepositoryImpl) GetOrCreateDefault() ([]entity.SystemConfig
|
||||
|
||||
// 如果没有配置,创建默认配置
|
||||
if len(configs) == 0 {
|
||||
utils.Info("未找到任何配置,创建默认配置")
|
||||
defaultConfigs := []entity.SystemConfig{
|
||||
{Key: entity.ConfigKeySiteTitle, Value: entity.ConfigDefaultSiteTitle, Type: entity.ConfigTypeString},
|
||||
{Key: entity.ConfigKeySiteDescription, Value: entity.ConfigDefaultSiteDescription, Type: entity.ConfigTypeString},
|
||||
@@ -105,10 +121,10 @@ func (r *SystemConfigRepositoryImpl) GetOrCreateDefault() ([]entity.SystemConfig
|
||||
{Key: entity.ConfigKeyAutoTransferMinSpace, Value: entity.ConfigDefaultAutoTransferMinSpace, Type: entity.ConfigTypeInt},
|
||||
{Key: entity.ConfigKeyAutoFetchHotDramaEnabled, Value: entity.ConfigDefaultAutoFetchHotDramaEnabled, Type: entity.ConfigTypeBool},
|
||||
{Key: entity.ConfigKeyApiToken, Value: entity.ConfigDefaultApiToken, Type: entity.ConfigTypeString},
|
||||
{Key: entity.ConfigKeyPageSize, Value: entity.ConfigDefaultPageSize, Type: entity.ConfigTypeInt},
|
||||
{Key: entity.ConfigKeyMaintenanceMode, Value: entity.ConfigDefaultMaintenanceMode, Type: entity.ConfigTypeBool},
|
||||
{Key: entity.ConfigKeyEnableRegister, Value: entity.ConfigDefaultEnableRegister, Type: entity.ConfigTypeBool},
|
||||
{Key: entity.ConfigKeyThirdPartyStatsCode, Value: entity.ConfigDefaultThirdPartyStatsCode, Type: entity.ConfigTypeString},
|
||||
{Key: entity.ConfigKeyPageSize, Value: entity.ConfigDefaultPageSize, Type: entity.ConfigTypeInt},
|
||||
{Key: entity.ConfigKeyMaintenanceMode, Value: entity.ConfigDefaultMaintenanceMode, Type: entity.ConfigTypeBool},
|
||||
{Key: entity.ConfigKeyEnableRegister, Value: entity.ConfigDefaultEnableRegister, Type: entity.ConfigTypeBool},
|
||||
{Key: entity.ConfigKeyThirdPartyStatsCode, Value: entity.ConfigDefaultThirdPartyStatsCode, Type: entity.ConfigTypeString},
|
||||
}
|
||||
|
||||
err = r.UpsertConfigs(defaultConfigs)
|
||||
@@ -208,6 +224,66 @@ func (r *SystemConfigRepositoryImpl) refreshConfigCache() {
|
||||
r.initConfigCache()
|
||||
}
|
||||
|
||||
// SafeRefreshConfigCache 安全的刷新配置缓存(带错误处理)
|
||||
func (r *SystemConfigRepositoryImpl) SafeRefreshConfigCache() error {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
utils.Error("配置缓存刷新时发生panic: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
r.refreshConfigCache()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateConfigIntegrity 验证配置完整性
|
||||
func (r *SystemConfigRepositoryImpl) ValidateConfigIntegrity() error {
|
||||
configs, err := r.FindAll()
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取配置失败: %v", err)
|
||||
}
|
||||
|
||||
// 检查关键配置是否存在
|
||||
requiredKeys := []string{
|
||||
entity.ConfigKeySiteTitle,
|
||||
entity.ConfigKeySiteDescription,
|
||||
entity.ConfigKeyKeywords,
|
||||
entity.ConfigKeyAuthor,
|
||||
entity.ConfigKeyCopyright,
|
||||
entity.ConfigKeyAutoProcessReadyResources,
|
||||
entity.ConfigKeyAutoProcessInterval,
|
||||
entity.ConfigKeyAutoTransferEnabled,
|
||||
entity.ConfigKeyAutoTransferLimitDays,
|
||||
entity.ConfigKeyAutoTransferMinSpace,
|
||||
entity.ConfigKeyAutoFetchHotDramaEnabled,
|
||||
entity.ConfigKeyApiToken,
|
||||
entity.ConfigKeyPageSize,
|
||||
entity.ConfigKeyMaintenanceMode,
|
||||
entity.ConfigKeyEnableRegister,
|
||||
entity.ConfigKeyThirdPartyStatsCode,
|
||||
}
|
||||
|
||||
existingKeys := make(map[string]bool)
|
||||
for _, config := range configs {
|
||||
existingKeys[config.Key] = true
|
||||
}
|
||||
|
||||
var missingKeys []string
|
||||
for _, key := range requiredKeys {
|
||||
if !existingKeys[key] {
|
||||
missingKeys = append(missingKeys, key)
|
||||
}
|
||||
}
|
||||
|
||||
if len(missingKeys) > 0 {
|
||||
utils.Error("发现缺失的配置项: %v", missingKeys)
|
||||
return fmt.Errorf("配置不完整,缺失: %v", missingKeys)
|
||||
}
|
||||
|
||||
utils.Info("配置完整性检查通过")
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetConfigValue 获取配置值(字符串)
|
||||
func (r *SystemConfigRepositoryImpl) GetConfigValue(key string) (string, error) {
|
||||
// 初始化缓存
|
||||
|
||||
@@ -28,6 +28,20 @@ func NewSystemConfigHandler(systemConfigRepo repo.SystemConfigRepository) *Syste
|
||||
|
||||
// GetConfig 获取系统配置
|
||||
func (h *SystemConfigHandler) GetConfig(c *gin.Context) {
|
||||
// 先验证配置完整性
|
||||
if err := h.systemConfigRepo.ValidateConfigIntegrity(); err != nil {
|
||||
utils.Error("配置完整性检查失败: %v", err)
|
||||
// 如果配置不完整,尝试重新创建默认配置
|
||||
configs, err := h.systemConfigRepo.GetOrCreateDefault()
|
||||
if err != nil {
|
||||
ErrorResponse(c, "获取系统配置失败", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
configResponse := converter.SystemConfigToResponse(configs)
|
||||
SuccessResponse(c, configResponse)
|
||||
return
|
||||
}
|
||||
|
||||
configs, err := h.systemConfigRepo.GetOrCreateDefault()
|
||||
if err != nil {
|
||||
ErrorResponse(c, "获取系统配置失败", http.StatusInternalServerError)
|
||||
@@ -47,22 +61,22 @@ func (h *SystemConfigHandler) UpdateConfig(c *gin.Context) {
|
||||
}
|
||||
|
||||
// 验证参数 - 只验证提交的字段
|
||||
if req.SiteTitle != "" && (len(req.SiteTitle) < 1 || len(req.SiteTitle) > 100) {
|
||||
if req.SiteTitle != nil && (len(*req.SiteTitle) < 1 || len(*req.SiteTitle) > 100) {
|
||||
ErrorResponse(c, "网站标题长度必须在1-100字符之间", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if req.AutoProcessInterval > 0 && (req.AutoProcessInterval < 1 || req.AutoProcessInterval > 1440) {
|
||||
if req.AutoProcessInterval != nil && (*req.AutoProcessInterval < 1 || *req.AutoProcessInterval > 1440) {
|
||||
ErrorResponse(c, "自动处理间隔必须在1-1440分钟之间", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if req.PageSize > 0 && (req.PageSize < 10 || req.PageSize > 500) {
|
||||
if req.PageSize != nil && (*req.PageSize < 10 || *req.PageSize > 500) {
|
||||
ErrorResponse(c, "每页显示数量必须在10-500之间", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if req.AutoTransferMinSpace > 0 && (req.AutoTransferMinSpace < 100 || req.AutoTransferMinSpace > 1024) {
|
||||
if req.AutoTransferMinSpace != nil && (*req.AutoTransferMinSpace < 100 || *req.AutoTransferMinSpace > 1024) {
|
||||
ErrorResponse(c, "最小存储空间必须在100-1024GB之间", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
@@ -118,29 +132,37 @@ func UpdateSystemConfig(c *gin.Context) {
|
||||
// 调试信息
|
||||
utils.Info("接收到的配置请求: %+v", req)
|
||||
|
||||
// 获取当前配置作为备份
|
||||
currentConfigs, err := repoManager.SystemConfigRepository.FindAll()
|
||||
if err != nil {
|
||||
utils.Error("获取当前配置失败: %v", err)
|
||||
} else {
|
||||
utils.Info("当前配置数量: %d", len(currentConfigs))
|
||||
}
|
||||
|
||||
// 验证参数 - 只验证提交的字段
|
||||
if req.SiteTitle != "" && (len(req.SiteTitle) < 1 || len(req.SiteTitle) > 100) {
|
||||
if req.SiteTitle != nil && (len(*req.SiteTitle) < 1 || len(*req.SiteTitle) > 100) {
|
||||
ErrorResponse(c, "网站标题长度必须在1-100字符之间", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if req.AutoProcessInterval != 0 && (req.AutoProcessInterval < 1 || req.AutoProcessInterval > 1440) {
|
||||
if req.AutoProcessInterval != nil && (*req.AutoProcessInterval < 1 || *req.AutoProcessInterval > 1440) {
|
||||
ErrorResponse(c, "自动处理间隔必须在1-1440分钟之间", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if req.PageSize != 0 && (req.PageSize < 10 || req.PageSize > 500) {
|
||||
if req.PageSize != nil && (*req.PageSize < 10 || *req.PageSize > 500) {
|
||||
ErrorResponse(c, "每页显示数量必须在10-500之间", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证自动转存配置
|
||||
if req.AutoTransferLimitDays != 0 && (req.AutoTransferLimitDays < 0 || req.AutoTransferLimitDays > 365) {
|
||||
if req.AutoTransferLimitDays != nil && (*req.AutoTransferLimitDays < 0 || *req.AutoTransferLimitDays > 365) {
|
||||
ErrorResponse(c, "自动转存限制天数必须在0-365之间", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if req.AutoTransferMinSpace != 0 && (req.AutoTransferMinSpace < 100 || req.AutoTransferMinSpace > 1024) {
|
||||
if req.AutoTransferMinSpace != nil && (*req.AutoTransferMinSpace < 100 || *req.AutoTransferMinSpace > 1024) {
|
||||
ErrorResponse(c, "最小存储空间必须在100-1024GB之间", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
@@ -152,13 +174,24 @@ func UpdateSystemConfig(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
utils.Info("准备更新配置,配置项数量: %d", len(configs))
|
||||
|
||||
// 保存配置
|
||||
err := repoManager.SystemConfigRepository.UpsertConfigs(configs)
|
||||
err = repoManager.SystemConfigRepository.UpsertConfigs(configs)
|
||||
if err != nil {
|
||||
utils.Error("保存系统配置失败: %v", err)
|
||||
ErrorResponse(c, "保存系统配置失败", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
utils.Info("配置保存成功")
|
||||
|
||||
// 安全刷新系统配置缓存
|
||||
if err := repoManager.SystemConfigRepository.SafeRefreshConfigCache(); err != nil {
|
||||
utils.Error("刷新配置缓存失败: %v", err)
|
||||
// 不返回错误,因为配置已经保存成功
|
||||
}
|
||||
|
||||
// 刷新系统配置缓存
|
||||
pan.RefreshSystemConfigCache()
|
||||
|
||||
@@ -174,16 +207,30 @@ func UpdateSystemConfig(c *gin.Context) {
|
||||
repoManager.CategoryRepository,
|
||||
)
|
||||
if scheduler != nil {
|
||||
scheduler.UpdateSchedulerStatusWithAutoTransfer(req.AutoFetchHotDramaEnabled, req.AutoProcessReadyResources, req.AutoTransferEnabled)
|
||||
// 只更新被设置的配置
|
||||
var autoFetchHotDrama, autoProcessReady, autoTransfer bool
|
||||
if req.AutoFetchHotDramaEnabled != nil {
|
||||
autoFetchHotDrama = *req.AutoFetchHotDramaEnabled
|
||||
}
|
||||
if req.AutoProcessReadyResources != nil {
|
||||
autoProcessReady = *req.AutoProcessReadyResources
|
||||
}
|
||||
if req.AutoTransferEnabled != nil {
|
||||
autoTransfer = *req.AutoTransferEnabled
|
||||
}
|
||||
scheduler.UpdateSchedulerStatusWithAutoTransfer(autoFetchHotDrama, autoProcessReady, autoTransfer)
|
||||
}
|
||||
|
||||
// 返回更新后的配置
|
||||
updatedConfigs, err := repoManager.SystemConfigRepository.FindAll()
|
||||
if err != nil {
|
||||
utils.Error("获取更新后的配置失败: %v", err)
|
||||
ErrorResponse(c, "获取更新后的配置失败", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
utils.Info("配置更新完成,当前配置数量: %d", len(updatedConfigs))
|
||||
|
||||
configResponse := converter.SystemConfigToResponse(updatedConfigs)
|
||||
SuccessResponse(c, configResponse)
|
||||
}
|
||||
@@ -199,6 +246,36 @@ func GetPublicSystemConfig(c *gin.Context) {
|
||||
SuccessResponse(c, configResponse)
|
||||
}
|
||||
|
||||
// 新增:配置监控端点
|
||||
func GetConfigStatus(c *gin.Context) {
|
||||
// 获取配置统计信息
|
||||
configs, err := repoManager.SystemConfigRepository.FindAll()
|
||||
if err != nil {
|
||||
ErrorResponse(c, "获取配置状态失败", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证配置完整性
|
||||
integrityErr := repoManager.SystemConfigRepository.ValidateConfigIntegrity()
|
||||
|
||||
// 获取缓存状态
|
||||
cachedConfigs := repoManager.SystemConfigRepository.GetCachedConfigs()
|
||||
|
||||
status := map[string]interface{}{
|
||||
"total_configs": len(configs),
|
||||
"cached_configs": len(cachedConfigs),
|
||||
"integrity_check": integrityErr == nil,
|
||||
"integrity_error": "",
|
||||
"last_check_time": utils.GetCurrentTimeString(),
|
||||
}
|
||||
|
||||
if integrityErr != nil {
|
||||
status["integrity_error"] = integrityErr.Error()
|
||||
}
|
||||
|
||||
SuccessResponse(c, status)
|
||||
}
|
||||
|
||||
// 新增:切换自动处理配置
|
||||
func ToggleAutoProcess(c *gin.Context) {
|
||||
var req struct {
|
||||
|
||||
1
main.go
1
main.go
@@ -215,6 +215,7 @@ func main() {
|
||||
// 系统配置路由
|
||||
api.GET("/system/config", middleware.AuthMiddleware(), middleware.AdminMiddleware(), handlers.GetSystemConfig)
|
||||
api.POST("/system/config", middleware.AuthMiddleware(), middleware.AdminMiddleware(), handlers.UpdateSystemConfig)
|
||||
api.GET("/system/config/status", middleware.AuthMiddleware(), middleware.AdminMiddleware(), handlers.GetConfigStatus)
|
||||
api.POST("/system/config/toggle-auto-process", middleware.AuthMiddleware(), middleware.AdminMiddleware(), handlers.ToggleAutoProcess)
|
||||
api.GET("/public/system-config", handlers.GetPublicSystemConfig)
|
||||
|
||||
|
||||
1
web/components.d.ts
vendored
1
web/components.d.ts
vendored
@@ -41,6 +41,7 @@ declare module 'vue' {
|
||||
NSpace: typeof import('naive-ui')['NSpace']
|
||||
NSpin: typeof import('naive-ui')['NSpin']
|
||||
NSwitch: typeof import('naive-ui')['NSwitch']
|
||||
NTable: typeof import('naive-ui')['NTable']
|
||||
NTabPane: typeof import('naive-ui')['NTabPane']
|
||||
NTabs: typeof import('naive-ui')['NTabs']
|
||||
NTag: typeof import('naive-ui')['NTag']
|
||||
|
||||
@@ -170,8 +170,9 @@ export const useSearchStatsApi = () => {
|
||||
export const useSystemConfigApi = () => {
|
||||
const getSystemConfig = () => useApiFetch('/system/config').then(parseApiResponse)
|
||||
const updateSystemConfig = (data: any) => useApiFetch('/system/config', { method: 'POST', body: data }).then(parseApiResponse)
|
||||
const getConfigStatus = () => useApiFetch('/system/config/status').then(parseApiResponse)
|
||||
const toggleAutoProcess = (enabled: boolean) => useApiFetch('/system/config/toggle-auto-process', { method: 'POST', body: { auto_process_ready_resources: enabled } }).then(parseApiResponse)
|
||||
return { getSystemConfig, updateSystemConfig, toggleAutoProcess }
|
||||
return { getSystemConfig, updateSystemConfig, getConfigStatus, toggleAutoProcess }
|
||||
}
|
||||
|
||||
export const useHotDramaApi = () => {
|
||||
|
||||
307
web/composables/useConfigChangeDetection.ts
Normal file
307
web/composables/useConfigChangeDetection.ts
Normal file
@@ -0,0 +1,307 @@
|
||||
import { ref } from 'vue'
|
||||
import type { Ref } from 'vue'
|
||||
|
||||
export interface ConfigChangeDetectionOptions {
|
||||
// 是否启用自动检测
|
||||
autoDetect?: boolean
|
||||
// 是否在控制台输出调试信息
|
||||
debug?: boolean
|
||||
// 自定义比较函数
|
||||
customCompare?: (key: string, currentValue: any, originalValue: any) => boolean
|
||||
// 配置项映射(前端字段名 -> 后端字段名)
|
||||
fieldMapping?: Record<string, string>
|
||||
}
|
||||
|
||||
export interface ConfigSubmitOptions {
|
||||
// 是否只提交改动的字段
|
||||
onlyChanged?: boolean
|
||||
// 是否包含所有配置项(用于后端识别)
|
||||
includeAllFields?: boolean
|
||||
// 自定义提交数据转换
|
||||
transformSubmitData?: (data: any) => any
|
||||
}
|
||||
|
||||
export const useConfigChangeDetection = <T extends Record<string, any>>(
|
||||
options: ConfigChangeDetectionOptions = {}
|
||||
) => {
|
||||
const { autoDetect = true, debug = false, customCompare, fieldMapping = {} } = options
|
||||
|
||||
// 原始配置数据
|
||||
const originalConfig = ref<T>({} as T)
|
||||
|
||||
// 当前配置数据
|
||||
const currentConfig = ref<T>({} as T)
|
||||
|
||||
// 是否已初始化
|
||||
const isInitialized = ref(false)
|
||||
|
||||
/**
|
||||
* 设置原始配置数据
|
||||
*/
|
||||
const setOriginalConfig = (config: T) => {
|
||||
originalConfig.value = { ...config }
|
||||
currentConfig.value = { ...config }
|
||||
isInitialized.value = true
|
||||
|
||||
if (debug) {
|
||||
console.log('useConfigChangeDetection - 设置原始配置:', config)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新当前配置数据
|
||||
*/
|
||||
const updateCurrentConfig = (config: Partial<T>) => {
|
||||
currentConfig.value = { ...currentConfig.value, ...config }
|
||||
|
||||
if (debug) {
|
||||
console.log('useConfigChangeDetection - 更新当前配置:', config)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测配置改动
|
||||
*/
|
||||
const getChangedConfig = (): Partial<T> => {
|
||||
if (!isInitialized.value) {
|
||||
if (debug) {
|
||||
console.warn('useConfigChangeDetection - 配置未初始化')
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
const changedConfig: Partial<T> = {}
|
||||
|
||||
// 遍历所有配置项
|
||||
for (const key in currentConfig.value) {
|
||||
const currentValue = currentConfig.value[key]
|
||||
const originalValue = originalConfig.value[key]
|
||||
|
||||
// 使用自定义比较函数或默认比较
|
||||
const hasChanged = customCompare
|
||||
? customCompare(key, currentValue, originalValue)
|
||||
: currentValue !== originalValue
|
||||
|
||||
if (hasChanged) {
|
||||
changedConfig[key as keyof T] = currentValue
|
||||
}
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
console.log('useConfigChangeDetection - 检测到的改动:', changedConfig)
|
||||
}
|
||||
|
||||
return changedConfig
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否有改动
|
||||
*/
|
||||
const hasChanges = (): boolean => {
|
||||
const changedConfig = getChangedConfig()
|
||||
return Object.keys(changedConfig).length > 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取改动的字段列表
|
||||
*/
|
||||
const getChangedFields = (): string[] => {
|
||||
const changedConfig = getChangedConfig()
|
||||
return Object.keys(changedConfig)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取改动的详细信息
|
||||
*/
|
||||
const getChangedDetails = (): Array<{
|
||||
key: string
|
||||
originalValue: any
|
||||
currentValue: any
|
||||
}> => {
|
||||
if (!isInitialized.value) {
|
||||
return []
|
||||
}
|
||||
|
||||
const details: Array<{
|
||||
key: string
|
||||
originalValue: any
|
||||
currentValue: any
|
||||
}> = []
|
||||
|
||||
for (const key in currentConfig.value) {
|
||||
const currentValue = currentConfig.value[key]
|
||||
const originalValue = originalConfig.value[key]
|
||||
|
||||
const hasChanged = customCompare
|
||||
? customCompare(key, currentValue, originalValue)
|
||||
: currentValue !== originalValue
|
||||
|
||||
if (hasChanged) {
|
||||
details.push({
|
||||
key,
|
||||
originalValue,
|
||||
currentValue
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return details
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置为原始配置
|
||||
*/
|
||||
const resetToOriginal = () => {
|
||||
currentConfig.value = { ...originalConfig.value }
|
||||
|
||||
if (debug) {
|
||||
console.log('useConfigChangeDetection - 重置为原始配置')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新原始配置(通常在保存成功后调用)
|
||||
*/
|
||||
const updateOriginalConfig = () => {
|
||||
originalConfig.value = { ...currentConfig.value }
|
||||
|
||||
if (debug) {
|
||||
console.log('useConfigChangeDetection - 更新原始配置')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置快照
|
||||
*/
|
||||
const getSnapshot = () => {
|
||||
return {
|
||||
original: { ...originalConfig.value },
|
||||
current: { ...currentConfig.value },
|
||||
changed: getChangedConfig(),
|
||||
hasChanges: hasChanges()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 准备提交数据
|
||||
*/
|
||||
const prepareSubmitData = (submitOptions: ConfigSubmitOptions = {}): any => {
|
||||
const { onlyChanged = true, includeAllFields = true, transformSubmitData } = submitOptions
|
||||
|
||||
let submitData: any = {}
|
||||
|
||||
if (onlyChanged) {
|
||||
// 只提交改动的字段
|
||||
submitData = getChangedConfig()
|
||||
} else {
|
||||
// 提交所有字段
|
||||
submitData = { ...currentConfig.value }
|
||||
}
|
||||
|
||||
// 应用字段映射
|
||||
if (Object.keys(fieldMapping).length > 0) {
|
||||
const mappedData: any = {}
|
||||
for (const [frontendKey, backendKey] of Object.entries(fieldMapping)) {
|
||||
if (submitData[frontendKey] !== undefined) {
|
||||
mappedData[backendKey] = submitData[frontendKey]
|
||||
}
|
||||
}
|
||||
submitData = mappedData
|
||||
}
|
||||
|
||||
// 如果包含所有字段,添加未改动的字段(值为undefined,让后端知道这些字段存在但未改动)
|
||||
if (includeAllFields && onlyChanged) {
|
||||
for (const key in originalConfig.value) {
|
||||
if (submitData[key] === undefined) {
|
||||
submitData[key] = undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 应用自定义转换
|
||||
if (transformSubmitData) {
|
||||
submitData = transformSubmitData(submitData)
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
console.log('useConfigChangeDetection - 准备提交数据:', submitData)
|
||||
}
|
||||
|
||||
return submitData
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用配置保存函数
|
||||
*/
|
||||
const saveConfig = async (
|
||||
apiFunction: (data: any) => Promise<any>,
|
||||
submitOptions: ConfigSubmitOptions = {},
|
||||
onSuccess?: () => void,
|
||||
onError?: (error: any) => void
|
||||
) => {
|
||||
try {
|
||||
// 检测是否有改动
|
||||
if (!hasChanges()) {
|
||||
if (debug) {
|
||||
console.log('useConfigChangeDetection - 没有检测到改动,跳过保存')
|
||||
}
|
||||
return { success: true, message: '没有检测到任何改动' }
|
||||
}
|
||||
|
||||
// 准备提交数据
|
||||
const submitData = prepareSubmitData(submitOptions)
|
||||
|
||||
if (debug) {
|
||||
console.log('useConfigChangeDetection - 提交数据:', submitData)
|
||||
}
|
||||
|
||||
// 调用API
|
||||
const response = await apiFunction(submitData)
|
||||
|
||||
// 更新原始配置
|
||||
updateOriginalConfig()
|
||||
|
||||
if (debug) {
|
||||
console.log('useConfigChangeDetection - 保存成功')
|
||||
}
|
||||
|
||||
// 调用成功回调
|
||||
if (onSuccess) {
|
||||
onSuccess()
|
||||
}
|
||||
|
||||
return { success: true, response }
|
||||
} catch (error) {
|
||||
if (debug) {
|
||||
console.error('useConfigChangeDetection - 保存失败:', error)
|
||||
}
|
||||
|
||||
// 调用错误回调
|
||||
if (onError) {
|
||||
onError(error)
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
// 响应式数据
|
||||
originalConfig: originalConfig as Ref<T>,
|
||||
currentConfig: currentConfig as Ref<T>,
|
||||
isInitialized,
|
||||
|
||||
// 方法
|
||||
setOriginalConfig,
|
||||
updateCurrentConfig,
|
||||
getChangedConfig,
|
||||
hasChanges,
|
||||
getChangedFields,
|
||||
getChangedDetails,
|
||||
resetToOriginal,
|
||||
updateOriginalConfig,
|
||||
getSnapshot,
|
||||
prepareSubmitData,
|
||||
saveConfig
|
||||
}
|
||||
}
|
||||
@@ -189,12 +189,34 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useConfigChangeDetection } from '~/composables/useConfigChangeDetection'
|
||||
|
||||
// 设置页面布局
|
||||
definePageMeta({
|
||||
layout: 'admin',
|
||||
ssr: false
|
||||
})
|
||||
|
||||
// 定义配置表单类型
|
||||
interface BotConfigForm {
|
||||
api_token: string
|
||||
}
|
||||
|
||||
// 使用配置改动检测
|
||||
const {
|
||||
setOriginalConfig,
|
||||
updateCurrentConfig,
|
||||
getChangedConfig,
|
||||
hasChanges,
|
||||
updateOriginalConfig,
|
||||
saveConfig: saveConfigWithDetection
|
||||
} = useConfigChangeDetection<BotConfigForm>({
|
||||
debug: true,
|
||||
fieldMapping: {
|
||||
api_token: 'api_token'
|
||||
}
|
||||
})
|
||||
|
||||
const notification = useNotification()
|
||||
const activeTab = ref('qq')
|
||||
|
||||
@@ -215,8 +237,13 @@ const fetchApiToken = async () => {
|
||||
const systemConfigApi = useSystemConfigApi()
|
||||
const response = await systemConfigApi.getSystemConfig()
|
||||
|
||||
if (response && (response as any).api_token) {
|
||||
apiToken.value = (response as any).api_token
|
||||
if (response) {
|
||||
const configData = {
|
||||
api_token: (response as any).api_token || ''
|
||||
}
|
||||
|
||||
apiToken.value = configData.api_token || '未配置API Token'
|
||||
setOriginalConfig(configData)
|
||||
} else {
|
||||
apiToken.value = '未配置API Token'
|
||||
}
|
||||
|
||||
@@ -76,17 +76,39 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useConfigChangeDetection } from '~/composables/useConfigChangeDetection'
|
||||
|
||||
// 设置页面布局
|
||||
definePageMeta({
|
||||
layout: 'admin',
|
||||
ssr: false
|
||||
})
|
||||
|
||||
// 定义配置表单类型
|
||||
interface DevConfigForm {
|
||||
api_token: string
|
||||
}
|
||||
|
||||
// 使用配置改动检测
|
||||
const {
|
||||
setOriginalConfig,
|
||||
updateCurrentConfig,
|
||||
getChangedConfig,
|
||||
hasChanges,
|
||||
updateOriginalConfig,
|
||||
saveConfig: saveConfigWithDetection
|
||||
} = useConfigChangeDetection<DevConfigForm>({
|
||||
debug: true,
|
||||
fieldMapping: {
|
||||
api_token: 'api_token'
|
||||
}
|
||||
})
|
||||
|
||||
const notification = useNotification()
|
||||
const saving = ref(false)
|
||||
|
||||
// 配置表单数据
|
||||
const configForm = ref({
|
||||
const configForm = ref<DevConfigForm>({
|
||||
api_token: ''
|
||||
})
|
||||
|
||||
@@ -98,9 +120,12 @@ const fetchConfig = async () => {
|
||||
const response = await systemConfigApi.getSystemConfig()
|
||||
|
||||
if (response) {
|
||||
configForm.value = {
|
||||
api_token: response.api_token || ''
|
||||
const configData = {
|
||||
api_token: (response as any).api_token || ''
|
||||
}
|
||||
|
||||
configForm.value = { ...configData }
|
||||
setOriginalConfig(configData)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取系统配置失败:', error)
|
||||
@@ -116,28 +141,50 @@ const saveConfig = async () => {
|
||||
try {
|
||||
saving.value = true
|
||||
|
||||
const { useSystemConfigApi } = await import('~/composables/useApi')
|
||||
const systemConfigApi = useSystemConfigApi()
|
||||
|
||||
await systemConfigApi.updateSystemConfig({
|
||||
// 更新当前配置数据
|
||||
updateCurrentConfig({
|
||||
api_token: configForm.value.api_token
|
||||
})
|
||||
|
||||
notification.success({
|
||||
content: '开发配置保存成功',
|
||||
duration: 3000
|
||||
})
|
||||
const { useSystemConfigApi } = await import('~/composables/useApi')
|
||||
const systemConfigApi = useSystemConfigApi()
|
||||
|
||||
// 刷新系统配置状态,确保顶部导航同步更新
|
||||
const { useSystemConfigStore } = await import('~/stores/systemConfig')
|
||||
const systemConfigStore = useSystemConfigStore()
|
||||
await systemConfigStore.initConfig(true, true) // 强制刷新,使用管理员API
|
||||
} catch (error) {
|
||||
console.error('保存开发配置失败:', error)
|
||||
notification.error({
|
||||
content: '保存开发配置失败',
|
||||
duration: 3000
|
||||
})
|
||||
// 使用通用保存函数
|
||||
const result = await saveConfigWithDetection(
|
||||
systemConfigApi.updateSystemConfig,
|
||||
{
|
||||
onlyChanged: true,
|
||||
includeAllFields: true
|
||||
},
|
||||
// 成功回调
|
||||
async () => {
|
||||
notification.success({
|
||||
content: '开发配置保存成功',
|
||||
duration: 3000
|
||||
})
|
||||
|
||||
// 刷新系统配置状态,确保顶部导航同步更新
|
||||
const { useSystemConfigStore } = await import('~/stores/systemConfig')
|
||||
const systemConfigStore = useSystemConfigStore()
|
||||
await systemConfigStore.initConfig(true, true)
|
||||
},
|
||||
// 错误回调
|
||||
(error) => {
|
||||
console.error('保存开发配置失败:', error)
|
||||
notification.error({
|
||||
content: '保存开发配置失败',
|
||||
duration: 3000
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
// 如果没有改动,显示提示
|
||||
if (result && result.message === '没有检测到任何改动') {
|
||||
notification.info({
|
||||
content: '没有检测到任何改动',
|
||||
duration: 3000
|
||||
})
|
||||
}
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
|
||||
@@ -165,18 +165,55 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useNotification } from 'naive-ui'
|
||||
import { useConfigChangeDetection } from '~/composables/useConfigChangeDetection'
|
||||
|
||||
// 设置页面布局
|
||||
definePageMeta({
|
||||
layout: 'admin',
|
||||
ssr: false
|
||||
})
|
||||
|
||||
// 配置表单数据类型
|
||||
interface FeatureConfigForm {
|
||||
auto_process_enabled: boolean
|
||||
auto_process_interval: string
|
||||
auto_transfer_enabled: boolean
|
||||
auto_transfer_min_space: string
|
||||
ad_keywords: string
|
||||
auto_insert_ad: string
|
||||
hot_drama_auto_fetch: boolean
|
||||
}
|
||||
|
||||
// 使用配置改动检测
|
||||
const {
|
||||
setOriginalConfig,
|
||||
updateCurrentConfig,
|
||||
getChangedConfig,
|
||||
hasChanges,
|
||||
updateOriginalConfig,
|
||||
saveConfig: saveConfigWithDetection
|
||||
} = useConfigChangeDetection<FeatureConfigForm>({
|
||||
debug: true,
|
||||
// 字段映射:前端字段名 -> 后端字段名
|
||||
fieldMapping: {
|
||||
auto_process_enabled: 'auto_process_ready_resources',
|
||||
auto_process_interval: 'auto_process_interval',
|
||||
auto_transfer_enabled: 'auto_transfer_enabled',
|
||||
auto_transfer_min_space: 'auto_transfer_min_space',
|
||||
ad_keywords: 'ad_keywords',
|
||||
auto_insert_ad: 'auto_insert_ad',
|
||||
hot_drama_auto_fetch: 'auto_fetch_hot_drama_enabled'
|
||||
}
|
||||
})
|
||||
|
||||
const notification = useNotification()
|
||||
const saving = ref(false)
|
||||
const activeTab = ref('resource')
|
||||
|
||||
// 配置表单数据
|
||||
const configForm = ref({
|
||||
const configForm = ref<FeatureConfigForm>({
|
||||
auto_process_enabled: false,
|
||||
auto_process_interval: '30',
|
||||
auto_transfer_enabled: false,
|
||||
@@ -197,7 +234,7 @@ const fetchConfig = async () => {
|
||||
const response = await systemConfigApi.getSystemConfig() as any
|
||||
|
||||
if (response) {
|
||||
configForm.value = {
|
||||
const configData = {
|
||||
auto_process_enabled: response.auto_process_ready_resources || false,
|
||||
auto_process_interval: String(response.auto_process_interval || 30),
|
||||
auto_transfer_enabled: response.auto_transfer_enabled || false,
|
||||
@@ -206,6 +243,9 @@ const fetchConfig = async () => {
|
||||
auto_insert_ad: response.auto_insert_ad || '',
|
||||
hot_drama_auto_fetch: response.auto_fetch_hot_drama_enabled || false
|
||||
}
|
||||
|
||||
configForm.value = { ...configData }
|
||||
setOriginalConfig(configData)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取系统配置失败:', error)
|
||||
@@ -221,34 +261,67 @@ const saveConfig = async () => {
|
||||
try {
|
||||
saving.value = true
|
||||
|
||||
// 更新当前配置数据
|
||||
updateCurrentConfig({
|
||||
auto_process_enabled: configForm.value.auto_process_enabled,
|
||||
auto_process_interval: configForm.value.auto_process_interval,
|
||||
auto_transfer_enabled: configForm.value.auto_transfer_enabled,
|
||||
auto_transfer_min_space: configForm.value.auto_transfer_min_space,
|
||||
ad_keywords: configForm.value.ad_keywords,
|
||||
auto_insert_ad: configForm.value.auto_insert_ad,
|
||||
hot_drama_auto_fetch: configForm.value.hot_drama_auto_fetch
|
||||
})
|
||||
|
||||
const { useSystemConfigApi } = await import('~/composables/useApi')
|
||||
const systemConfigApi = useSystemConfigApi()
|
||||
|
||||
await systemConfigApi.updateSystemConfig({
|
||||
auto_process_ready_resources: configForm.value.auto_process_enabled,
|
||||
auto_process_interval: parseInt(configForm.value.auto_process_interval) || 30,
|
||||
auto_transfer_enabled: configForm.value.auto_transfer_enabled,
|
||||
auto_transfer_min_space: parseInt(configForm.value.auto_transfer_min_space) || 500,
|
||||
ad_keywords: configForm.value.ad_keywords,
|
||||
auto_insert_ad: configForm.value.auto_insert_ad,
|
||||
auto_fetch_hot_drama_enabled: configForm.value.hot_drama_auto_fetch
|
||||
})
|
||||
// 使用通用保存函数
|
||||
const result = await saveConfigWithDetection(
|
||||
systemConfigApi.updateSystemConfig,
|
||||
{
|
||||
onlyChanged: true,
|
||||
includeAllFields: true,
|
||||
// 自定义数据转换
|
||||
transformSubmitData: (data) => {
|
||||
// 转换字符串为数字
|
||||
if (data.auto_process_interval !== undefined) {
|
||||
data.auto_process_interval = parseInt(data.auto_process_interval) || 30
|
||||
}
|
||||
if (data.auto_transfer_min_space !== undefined) {
|
||||
data.auto_transfer_min_space = parseInt(data.auto_transfer_min_space) || 500
|
||||
}
|
||||
return data
|
||||
}
|
||||
},
|
||||
// 成功回调
|
||||
async () => {
|
||||
notification.success({
|
||||
content: '功能配置保存成功',
|
||||
duration: 3000
|
||||
})
|
||||
|
||||
notification.success({
|
||||
content: '功能配置保存成功',
|
||||
duration: 3000
|
||||
})
|
||||
// 刷新系统配置状态,确保顶部导航同步更新
|
||||
const { useSystemConfigStore } = await import('~/stores/systemConfig')
|
||||
const systemConfigStore = useSystemConfigStore()
|
||||
await systemConfigStore.initConfig(true, true)
|
||||
},
|
||||
// 错误回调
|
||||
(error) => {
|
||||
console.error('保存功能配置失败:', error)
|
||||
notification.error({
|
||||
content: '保存功能配置失败',
|
||||
duration: 3000
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
// 刷新系统配置状态,确保顶部导航同步更新
|
||||
const { useSystemConfigStore } = await import('~/stores/systemConfig')
|
||||
const systemConfigStore = useSystemConfigStore()
|
||||
await systemConfigStore.initConfig(true, true) // 强制刷新,使用管理员API
|
||||
} catch (error) {
|
||||
console.error('保存功能配置失败:', error)
|
||||
notification.error({
|
||||
content: '保存功能配置失败',
|
||||
duration: 3000
|
||||
})
|
||||
// 如果没有改动,显示提示
|
||||
if (result && result.message === '没有检测到任何改动') {
|
||||
notification.info({
|
||||
content: '没有检测到任何改动',
|
||||
duration: 3000
|
||||
})
|
||||
}
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
|
||||
@@ -282,6 +282,7 @@ definePageMeta({
|
||||
|
||||
|
||||
import { useImageUrl } from '~/composables/useImageUrl'
|
||||
import { useConfigChangeDetection } from '~/composables/useConfigChangeDetection'
|
||||
|
||||
const notification = useNotification()
|
||||
const { getImageUrl } = useImageUrl()
|
||||
@@ -304,8 +305,8 @@ const pagination = ref({
|
||||
pageSizes: [10, 20, 50, 100]
|
||||
})
|
||||
|
||||
// 配置表单数据
|
||||
const configForm = ref<{
|
||||
// 配置表单数据类型
|
||||
interface SiteConfigForm {
|
||||
site_title: string
|
||||
site_description: string
|
||||
keywords: string
|
||||
@@ -316,14 +317,43 @@ const configForm = ref<{
|
||||
forbidden_words: string
|
||||
enable_sitemap: boolean
|
||||
sitemap_update_frequency: string
|
||||
}>({
|
||||
}
|
||||
|
||||
// 使用配置改动检测
|
||||
const {
|
||||
setOriginalConfig,
|
||||
updateCurrentConfig,
|
||||
getChangedConfig,
|
||||
hasChanges,
|
||||
getChangedDetails,
|
||||
updateOriginalConfig,
|
||||
saveConfig: saveConfigWithDetection
|
||||
} = useConfigChangeDetection<SiteConfigForm>({
|
||||
debug: true,
|
||||
// 字段映射:前端字段名 -> 后端字段名
|
||||
fieldMapping: {
|
||||
site_title: 'site_title',
|
||||
site_description: 'site_description',
|
||||
keywords: 'keywords',
|
||||
copyright: 'copyright',
|
||||
site_logo: 'site_logo',
|
||||
maintenance_mode: 'maintenance_mode',
|
||||
enable_register: 'enable_register',
|
||||
forbidden_words: 'forbidden_words',
|
||||
enable_sitemap: 'enable_sitemap',
|
||||
sitemap_update_frequency: 'sitemap_update_frequency'
|
||||
}
|
||||
})
|
||||
|
||||
// 配置表单数据
|
||||
const configForm = ref<SiteConfigForm>({
|
||||
site_title: '',
|
||||
site_description: '',
|
||||
keywords: '',
|
||||
copyright: '',
|
||||
site_logo: '',
|
||||
maintenance_mode: false,
|
||||
enable_register: false, // 新增:开启注册开关
|
||||
enable_register: false,
|
||||
forbidden_words: '',
|
||||
enable_sitemap: false,
|
||||
sitemap_update_frequency: 'daily'
|
||||
@@ -353,18 +383,22 @@ const fetchConfig = async () => {
|
||||
const response = await systemConfigApi.getSystemConfig() as any
|
||||
|
||||
if (response) {
|
||||
configForm.value = {
|
||||
const configData = {
|
||||
site_title: response.site_title || '',
|
||||
site_description: response.site_description || '',
|
||||
keywords: response.keywords || '',
|
||||
copyright: response.copyright || '',
|
||||
site_logo: response.site_logo || '',
|
||||
maintenance_mode: response.maintenance_mode || false,
|
||||
enable_register: response.enable_register || false, // 新增:获取开启注册开关
|
||||
enable_register: response.enable_register || false,
|
||||
forbidden_words: response.forbidden_words || '',
|
||||
enable_sitemap: response.enable_sitemap || false,
|
||||
sitemap_update_frequency: response.sitemap_update_frequency || 'daily'
|
||||
}
|
||||
|
||||
// 设置表单数据和原始数据
|
||||
configForm.value = { ...configData }
|
||||
setOriginalConfig(configData)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取系统配置失败:', error)
|
||||
@@ -375,43 +409,68 @@ const fetchConfig = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 保存配置
|
||||
const saveConfig = async () => {
|
||||
try {
|
||||
await formRef.value?.validate()
|
||||
|
||||
saving.value = true
|
||||
|
||||
const { useSystemConfigApi } = await import('~/composables/useApi')
|
||||
const systemConfigApi = useSystemConfigApi()
|
||||
|
||||
await systemConfigApi.updateSystemConfig({
|
||||
// 更新当前配置数据
|
||||
updateCurrentConfig({
|
||||
site_title: configForm.value.site_title,
|
||||
site_description: configForm.value.site_description,
|
||||
keywords: configForm.value.keywords,
|
||||
copyright: configForm.value.copyright,
|
||||
site_logo: configForm.value.site_logo,
|
||||
maintenance_mode: configForm.value.maintenance_mode,
|
||||
enable_register: configForm.value.enable_register, // 新增:保存开启注册开关
|
||||
enable_register: configForm.value.enable_register,
|
||||
forbidden_words: configForm.value.forbidden_words,
|
||||
enable_sitemap: configForm.value.enable_sitemap,
|
||||
sitemap_update_frequency: configForm.value.sitemap_update_frequency
|
||||
})
|
||||
|
||||
notification.success({
|
||||
content: '站点配置保存成功',
|
||||
duration: 3000
|
||||
})
|
||||
const { useSystemConfigApi } = await import('~/composables/useApi')
|
||||
const systemConfigApi = useSystemConfigApi()
|
||||
|
||||
// 刷新系统配置状态,确保顶部导航同步更新
|
||||
const { useSystemConfigStore } = await import('~/stores/systemConfig')
|
||||
const systemConfigStore = useSystemConfigStore()
|
||||
await systemConfigStore.initConfig(true, true) // 强制刷新,使用管理员API
|
||||
} catch (error) {
|
||||
console.error('保存站点配置失败:', error)
|
||||
notification.error({
|
||||
content: '保存站点配置失败',
|
||||
duration: 3000
|
||||
})
|
||||
// 使用通用保存函数
|
||||
const result = await saveConfigWithDetection(
|
||||
systemConfigApi.updateSystemConfig,
|
||||
{
|
||||
onlyChanged: true,
|
||||
includeAllFields: true
|
||||
},
|
||||
// 成功回调
|
||||
async () => {
|
||||
notification.success({
|
||||
content: '站点配置保存成功',
|
||||
duration: 3000
|
||||
})
|
||||
|
||||
// 刷新系统配置状态,确保顶部导航同步更新
|
||||
const { useSystemConfigStore } = await import('~/stores/systemConfig')
|
||||
const systemConfigStore = useSystemConfigStore()
|
||||
await systemConfigStore.initConfig(true, true)
|
||||
},
|
||||
// 错误回调
|
||||
(error) => {
|
||||
console.error('保存站点配置失败:', error)
|
||||
notification.error({
|
||||
content: '保存站点配置失败',
|
||||
duration: 3000
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
// 如果没有改动,显示提示
|
||||
if (result && result.message === '没有检测到任何改动') {
|
||||
notification.info({
|
||||
content: '没有检测到任何改动',
|
||||
duration: 3000
|
||||
})
|
||||
}
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
|
||||
@@ -320,12 +320,33 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useMessage } from 'naive-ui'
|
||||
import { useConfigChangeDetection } from '~/composables/useConfigChangeDetection'
|
||||
|
||||
// 页面配置
|
||||
definePageMeta({
|
||||
layout: 'admin'
|
||||
})
|
||||
|
||||
// 定义配置表单类型
|
||||
interface ThirdPartyStatsForm {
|
||||
third_party_stats_code: string
|
||||
}
|
||||
|
||||
// 使用配置改动检测
|
||||
const {
|
||||
setOriginalConfig,
|
||||
updateCurrentConfig,
|
||||
getChangedConfig,
|
||||
hasChanges,
|
||||
updateOriginalConfig,
|
||||
saveConfig: saveConfigWithDetection
|
||||
} = useConfigChangeDetection<ThirdPartyStatsForm>({
|
||||
debug: true,
|
||||
fieldMapping: {
|
||||
third_party_stats_code: 'third_party_stats_code'
|
||||
}
|
||||
})
|
||||
|
||||
// 状态管理
|
||||
const message = useMessage()
|
||||
const statsCode = ref('')
|
||||
@@ -338,8 +359,13 @@ const fetchConfig = async () => {
|
||||
const systemConfigApi = useSystemConfigApi()
|
||||
const response = await systemConfigApi.getSystemConfig()
|
||||
|
||||
if (response && response.third_party_stats_code) {
|
||||
statsCode.value = response.third_party_stats_code
|
||||
if (response) {
|
||||
const configData = {
|
||||
third_party_stats_code: (response as any).third_party_stats_code || ''
|
||||
}
|
||||
|
||||
statsCode.value = configData.third_party_stats_code
|
||||
setOriginalConfig(configData)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取配置失败:', error)
|
||||
@@ -349,18 +375,39 @@ const fetchConfig = async () => {
|
||||
|
||||
// 保存配置
|
||||
const saveCode = async () => {
|
||||
saving.value = true
|
||||
try {
|
||||
const { useSystemConfigApi } = await import('~/composables/useApi')
|
||||
const systemConfigApi = useSystemConfigApi()
|
||||
await systemConfigApi.updateSystemConfig({
|
||||
saving.value = true
|
||||
|
||||
// 更新当前配置
|
||||
updateCurrentConfig({
|
||||
third_party_stats_code: statsCode.value
|
||||
})
|
||||
|
||||
message.success('配置保存成功')
|
||||
} catch (error) {
|
||||
console.error('保存配置失败:', error)
|
||||
message.error('保存配置失败')
|
||||
const { useSystemConfigApi } = await import('~/composables/useApi')
|
||||
const systemConfigApi = useSystemConfigApi()
|
||||
|
||||
// 使用通用保存函数
|
||||
const result = await saveConfigWithDetection(
|
||||
systemConfigApi.updateSystemConfig,
|
||||
{
|
||||
onlyChanged: true,
|
||||
includeAllFields: true
|
||||
},
|
||||
// 成功回调
|
||||
() => {
|
||||
message.success('配置保存成功')
|
||||
},
|
||||
// 错误回调
|
||||
(error) => {
|
||||
console.error('保存配置失败:', error)
|
||||
message.error('保存配置失败')
|
||||
}
|
||||
)
|
||||
|
||||
// 如果没有改动,显示提示
|
||||
if (result && result.message === '没有检测到任何改动') {
|
||||
message.info('没有检测到任何改动')
|
||||
}
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user