🎨 Refactor save service
This commit is contained in:
@@ -1,340 +1,150 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"voidraft/internal/models"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/services/log"
|
||||
)
|
||||
|
||||
// ConfigService 提供配置管理功能
|
||||
type ConfigService struct {
|
||||
fileService *FileService
|
||||
configPath string
|
||||
rootDir string
|
||||
homePath string
|
||||
config *models.AppConfig
|
||||
lastModTime time.Time // 上次读取配置文件的修改时间
|
||||
store *Store[models.AppConfig]
|
||||
homePath string
|
||||
configPath string
|
||||
logger *log.LoggerService
|
||||
}
|
||||
type Service struct{}
|
||||
|
||||
// NewConfigService 创建新的配置服务实例
|
||||
func NewConfigService(fileService *FileService) *ConfigService {
|
||||
func NewConfigService() *ConfigService {
|
||||
// 初始化日志服务
|
||||
logger := log.New()
|
||||
|
||||
// 获取用户主目录
|
||||
homePath, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
log.Printf("Failed to get user home directory: %v", err)
|
||||
logger.Error("Config: Failed to get user home directory", "error", err)
|
||||
homePath = "."
|
||||
}
|
||||
|
||||
log.Printf("User home directory: %s", homePath)
|
||||
|
||||
// 创建默认配置
|
||||
defaultConfig := models.NewDefaultAppConfig()
|
||||
|
||||
// 构造服务实例
|
||||
service := &ConfigService{
|
||||
fileService: fileService,
|
||||
rootDir: defaultConfig.Paths.RootDir,
|
||||
configPath: defaultConfig.Paths.ConfigPath,
|
||||
homePath: homePath,
|
||||
config: defaultConfig,
|
||||
// 构建完整的配置文件路径
|
||||
configFilePath := filepath.Join(homePath, defaultConfig.Paths.RootDir, defaultConfig.Paths.ConfigPath)
|
||||
|
||||
// 创建Store选项
|
||||
storeOption := StoreOption{
|
||||
FilePath: configFilePath,
|
||||
AutoSave: true,
|
||||
Logger: logger,
|
||||
}
|
||||
|
||||
// 初始化配置目录和文件
|
||||
service.initializeConfig()
|
||||
// 创建存储服务
|
||||
store := NewStore[models.AppConfig](storeOption)
|
||||
|
||||
// 构造配置服务实例
|
||||
service := &ConfigService{
|
||||
store: store,
|
||||
homePath: homePath,
|
||||
configPath: configFilePath,
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
// 检查是否需要设置默认配置
|
||||
config := store.Get()
|
||||
if isEmptyConfig(config) {
|
||||
err := store.Set(*defaultConfig)
|
||||
if err != nil {
|
||||
logger.Error("Config: Failed to set default config", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
return service
|
||||
}
|
||||
|
||||
// initializeConfig 初始化配置目录和文件
|
||||
func (cs *ConfigService) initializeConfig() {
|
||||
// 确保配置目录存在
|
||||
dirPath := filepath.Join(cs.homePath, cs.rootDir)
|
||||
log.Printf("Creating config directory: %s", dirPath)
|
||||
|
||||
// 确保主目录存在
|
||||
if err := cs.fileService.EnsureDir(dirPath); err != nil {
|
||||
log.Printf("Failed to create config directory: %v", err)
|
||||
return
|
||||
// isEmptyConfig 检查配置是否为空
|
||||
func isEmptyConfig(config models.AppConfig) bool {
|
||||
// 检查基本字段
|
||||
if config.Editor.FontSize == 0 && config.Paths.RootDir == "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 确保配置文件所在目录存在
|
||||
configDir := filepath.Dir(cs.GetFullConfigPath())
|
||||
if configDir != dirPath {
|
||||
if err := cs.fileService.EnsureDir(configDir); err != nil {
|
||||
log.Printf("Failed to create config file directory: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
// GetConfig 获取完整应用配置
|
||||
func (cs *ConfigService) GetConfig() (*models.AppConfig, error) {
|
||||
config := cs.store.Get()
|
||||
|
||||
// 检查配置文件是否存在
|
||||
configFilePath := cs.GetFullConfigPath()
|
||||
log.Printf("Config file path: %s", configFilePath)
|
||||
|
||||
if !cs.fileService.FileExists(configFilePath) {
|
||||
log.Printf("Config file not found, creating default config")
|
||||
// 创建默认配置文件
|
||||
// 如果配置为空,返回默认配置
|
||||
if isEmptyConfig(config) {
|
||||
defaultConfig := models.NewDefaultAppConfig()
|
||||
if err := cs.saveAppConfig(defaultConfig); err != nil {
|
||||
log.Printf("Failed to save default config: %v", err)
|
||||
}
|
||||
} else {
|
||||
// 加载现有配置
|
||||
log.Printf("Loading existing config file")
|
||||
existingConfig, modTime, err := cs.loadAppConfigWithModTime()
|
||||
if err != nil {
|
||||
log.Printf("Failed to load existing config, using default: %v", err)
|
||||
} else {
|
||||
cs.config = existingConfig
|
||||
cs.lastModTime = modTime
|
||||
if err := cs.store.Set(*defaultConfig); err != nil {
|
||||
cs.logger.Error("Config: Failed to save default config", "error", err)
|
||||
}
|
||||
return defaultConfig, nil
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// GetFullConfigPath 获取完整的配置文件路径
|
||||
func (cs *ConfigService) GetFullConfigPath() string {
|
||||
return filepath.Join(cs.homePath, cs.rootDir, cs.configPath)
|
||||
}
|
||||
|
||||
// mergeConfigs 合并两个配置,实现智能合并策略
|
||||
func (cs *ConfigService) mergeConfigs(currentConfig *models.AppConfig, existingData []byte) (*models.AppConfig, error) {
|
||||
// 解析磁盘配置
|
||||
var diskConfig models.AppConfig
|
||||
if err := json.Unmarshal(existingData, &diskConfig); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse existing config: %w", err)
|
||||
}
|
||||
|
||||
// 创建合并后的配置(基于磁盘配置)
|
||||
mergedConfig := diskConfig
|
||||
|
||||
// ===== 1. 编辑器配置合并策略 =====
|
||||
// 编辑器配置是用户最直接的操作,总是保留当前配置
|
||||
log.Printf("Merge: Preserving current editor config")
|
||||
mergedConfig.Editor = currentConfig.Editor
|
||||
|
||||
// ===== 2. 路径配置合并策略 =====
|
||||
// 如果当前实例已经修改了根目录(非默认值),保留当前配置
|
||||
defaultConfig := models.NewDefaultAppConfig()
|
||||
|
||||
if currentConfig.Paths.RootDir != defaultConfig.Paths.RootDir &&
|
||||
currentConfig.Paths.RootDir != diskConfig.Paths.RootDir {
|
||||
log.Printf("Merge: Using custom root directory from current config: %s", currentConfig.Paths.RootDir)
|
||||
mergedConfig.Paths.RootDir = currentConfig.Paths.RootDir
|
||||
} else {
|
||||
log.Printf("Merge: Using root directory from disk config: %s", diskConfig.Paths.RootDir)
|
||||
}
|
||||
|
||||
// 对配置路径采用相同策略
|
||||
if currentConfig.Paths.ConfigPath != defaultConfig.Paths.ConfigPath &&
|
||||
currentConfig.Paths.ConfigPath != diskConfig.Paths.ConfigPath {
|
||||
log.Printf("Merge: Using custom config path from current config: %s", currentConfig.Paths.ConfigPath)
|
||||
mergedConfig.Paths.ConfigPath = currentConfig.Paths.ConfigPath
|
||||
} else {
|
||||
log.Printf("Merge: Using config path from disk config: %s", diskConfig.Paths.ConfigPath)
|
||||
}
|
||||
|
||||
// ===== 3. 元数据合并策略 =====
|
||||
// 版本号使用较高的版本
|
||||
if compareVersions(currentConfig.Metadata.Version, diskConfig.Metadata.Version) > 0 {
|
||||
log.Printf("Merge: Using higher version from current config: %s", currentConfig.Metadata.Version)
|
||||
mergedConfig.Metadata.Version = currentConfig.Metadata.Version
|
||||
} else {
|
||||
log.Printf("Merge: Using version from disk config: %s", diskConfig.Metadata.Version)
|
||||
}
|
||||
|
||||
// 更新时间总是使用当前时间
|
||||
mergedConfig.Metadata.LastUpdated = time.Now()
|
||||
|
||||
// ===== 4. 其他配置项 =====
|
||||
|
||||
log.Printf("Merge: Completed using comprehensive merge strategy")
|
||||
return &mergedConfig, nil
|
||||
}
|
||||
|
||||
// compareVersions 比较两个版本号
|
||||
// 返回: 1 如果v1>v2, 0 如果v1=v2, -1 如果v1<v2
|
||||
func compareVersions(v1, v2 string) int {
|
||||
// 分割版本号
|
||||
parts1 := strings.Split(v1, ".")
|
||||
parts2 := strings.Split(v2, ".")
|
||||
|
||||
// 确保两个版本号有相同数量的部分
|
||||
for len(parts1) < len(parts2) {
|
||||
parts1 = append(parts1, "0")
|
||||
}
|
||||
for len(parts2) < len(parts1) {
|
||||
parts2 = append(parts2, "0")
|
||||
}
|
||||
|
||||
// 逐个比较各部分
|
||||
for i := 0; i < len(parts1); i++ {
|
||||
num1, err1 := strconv.Atoi(parts1[i])
|
||||
num2, err2 := strconv.Atoi(parts2[i])
|
||||
|
||||
// 如果有无法解析为数字的部分,按字符串比较
|
||||
if err1 != nil || err2 != nil {
|
||||
if parts1[i] > parts2[i] {
|
||||
return 1
|
||||
} else if parts1[i] < parts2[i] {
|
||||
return -1
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// 数值比较
|
||||
if num1 > num2 {
|
||||
return 1
|
||||
} else if num1 < num2 {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// 所有部分都相等
|
||||
return 0
|
||||
}
|
||||
|
||||
// loadAppConfigWithModTime 从文件加载配置,同时返回文件修改时间
|
||||
func (cs *ConfigService) loadAppConfigWithModTime() (*models.AppConfig, time.Time, error) {
|
||||
config := &models.AppConfig{}
|
||||
configPath := cs.GetFullConfigPath()
|
||||
|
||||
// 先获取文件修改时间
|
||||
modTime, err := cs.fileService.GetFileModTime(configPath)
|
||||
if err != nil {
|
||||
return nil, time.Time{}, fmt.Errorf("failed to get config file modification time: %w", err)
|
||||
}
|
||||
|
||||
// 然后加载文件内容
|
||||
if err := cs.fileService.LoadJSON(configPath, config); err != nil {
|
||||
return nil, time.Time{}, fmt.Errorf("failed to load config file: %w", err)
|
||||
}
|
||||
|
||||
return config, modTime, nil
|
||||
}
|
||||
|
||||
// handleConfigConflict 处理配置冲突的回调函数
|
||||
func (cs *ConfigService) handleConfigConflict(existingData []byte) (interface{}, error) {
|
||||
// 如果没有当前配置,无法合并
|
||||
if cs.config == nil {
|
||||
return nil, fmt.Errorf("no current config to merge")
|
||||
}
|
||||
|
||||
// 合并配置
|
||||
mergedConfig, err := cs.mergeConfigs(cs.config, existingData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 更新内存中的配置
|
||||
cs.config = mergedConfig
|
||||
|
||||
return mergedConfig, nil
|
||||
}
|
||||
|
||||
// 内部方法:从文件加载配置
|
||||
func (cs *ConfigService) loadAppConfig() (*models.AppConfig, error) {
|
||||
config, modTime, err := cs.loadAppConfigWithModTime()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 更新最后修改时间
|
||||
cs.lastModTime = modTime
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// 内部方法:保存配置到文件
|
||||
func (cs *ConfigService) saveAppConfig(config *models.AppConfig) error {
|
||||
// SaveConfig 保存完整应用配置
|
||||
func (cs *ConfigService) SaveConfig(config *models.AppConfig) error {
|
||||
// 更新配置元数据
|
||||
config.Metadata.LastUpdated = time.Now()
|
||||
|
||||
// 保存到文件,使用乐观并发控制
|
||||
configPath := cs.GetFullConfigPath()
|
||||
log.Printf("saveAppConfig: Saving to %s with last mod time: %s",
|
||||
configPath, cs.lastModTime.Format(time.RFC3339))
|
||||
return cs.store.Set(*config)
|
||||
}
|
||||
|
||||
err := cs.fileService.SaveJSONWithCheck(
|
||||
configPath,
|
||||
config,
|
||||
cs.lastModTime,
|
||||
cs.handleConfigConflict,
|
||||
)
|
||||
// ResetConfig 重置为默认配置
|
||||
func (cs *ConfigService) ResetConfig() error {
|
||||
defaultConfig := models.NewDefaultAppConfig()
|
||||
|
||||
err := cs.store.Set(*defaultConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to save config file: %w", err)
|
||||
}
|
||||
|
||||
// 更新内存中的配置和最后修改时间
|
||||
cs.config = config
|
||||
|
||||
// 获取最新的修改时间
|
||||
newModTime, err := cs.fileService.GetFileModTime(configPath)
|
||||
if err == nil {
|
||||
cs.lastModTime = newModTime
|
||||
cs.logger.Error("Config: Failed to save default config", "error", err)
|
||||
return fmt.Errorf("failed to reset config: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAppConfig 获取应用配置
|
||||
func (cs *ConfigService) GetAppConfig() (*models.AppConfig, error) {
|
||||
// 返回内存中的配置副本
|
||||
if cs.config != nil {
|
||||
return cs.config, nil
|
||||
}
|
||||
|
||||
// 从文件加载
|
||||
config, err := cs.loadAppConfig()
|
||||
// GetEditorConfig 获取编辑器配置
|
||||
func (cs *ConfigService) GetEditorConfig() (models.EditorConfig, error) {
|
||||
config, err := cs.GetConfig()
|
||||
if err != nil {
|
||||
log.Printf("GetAppConfig: Failed to load config: %v", err)
|
||||
// 使用默认配置
|
||||
defaultConfig := models.NewDefaultAppConfig()
|
||||
cs.config = defaultConfig
|
||||
|
||||
// 保存默认配置到文件
|
||||
if err := cs.saveAppConfig(defaultConfig); err != nil {
|
||||
log.Printf("GetAppConfig: Failed to save default config: %v", err)
|
||||
}
|
||||
|
||||
return defaultConfig, nil
|
||||
return models.EditorConfig{}, err
|
||||
}
|
||||
|
||||
// 更新内存中的配置
|
||||
cs.config = config
|
||||
return config, nil
|
||||
return config.Editor, nil
|
||||
}
|
||||
|
||||
// SaveAppConfig 保存应用配置
|
||||
func (cs *ConfigService) SaveAppConfig(config *models.AppConfig) error {
|
||||
return cs.saveAppConfig(config)
|
||||
// UpdateEditorConfig 更新编辑器配置
|
||||
func (cs *ConfigService) UpdateEditorConfig(editorConfig models.EditorConfig) error {
|
||||
// 获取当前配置
|
||||
config, err := cs.GetConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新编辑器配置
|
||||
config.Editor = editorConfig
|
||||
config.Metadata.LastUpdated = time.Now()
|
||||
|
||||
// 保存更新后的配置
|
||||
return cs.store.Set(*config)
|
||||
}
|
||||
|
||||
// GetLanguage 获取当前语言设置
|
||||
func (cs *ConfigService) GetLanguage() (models.LanguageType, error) {
|
||||
// 如果内存中已有配置,直接返回
|
||||
if cs.config != nil {
|
||||
return cs.config.Language, nil
|
||||
}
|
||||
|
||||
// 否则从文件加载
|
||||
config, err := cs.loadAppConfig()
|
||||
editorConfig, err := cs.GetEditorConfig()
|
||||
if err != nil {
|
||||
log.Printf("GetLanguage: Failed to load config: %v", err)
|
||||
// 使用默认配置
|
||||
defaultConfig := models.NewDefaultAppConfig()
|
||||
cs.config = defaultConfig
|
||||
return defaultConfig.Language, nil
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 更新内存中的配置
|
||||
cs.config = config
|
||||
log.Printf("GetLanguage: Retrieved language: %s", config.Language)
|
||||
return config.Language, nil
|
||||
return editorConfig.Language, nil
|
||||
}
|
||||
|
||||
// SetLanguage 设置语言
|
||||
@@ -344,89 +154,75 @@ func (cs *ConfigService) SetLanguage(language models.LanguageType) error {
|
||||
return fmt.Errorf("unsupported language: %s", language)
|
||||
}
|
||||
|
||||
// 如果内存中已有配置,直接更新
|
||||
if cs.config != nil {
|
||||
log.Printf("SetLanguage: Updating language to: %s", language)
|
||||
cs.config.Language = language
|
||||
return cs.saveAppConfig(cs.config)
|
||||
}
|
||||
|
||||
// 没有内存中的配置,需要先加载
|
||||
config, err := cs.loadAppConfig()
|
||||
// 获取当前配置
|
||||
config, err := cs.GetConfig()
|
||||
if err != nil {
|
||||
log.Printf("SetLanguage: Failed to load config: %v", err)
|
||||
// 使用默认配置
|
||||
config = models.NewDefaultAppConfig()
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新语言配置
|
||||
config.Language = language
|
||||
// 更新语言设置
|
||||
config.Editor.Language = language
|
||||
config.Metadata.LastUpdated = time.Now()
|
||||
|
||||
// 保存到文件
|
||||
return cs.saveAppConfig(config)
|
||||
// 保存更新后的配置
|
||||
return cs.store.Set(*config)
|
||||
}
|
||||
|
||||
// UpdateEditorConfig 更新编辑器配置
|
||||
func (cs *ConfigService) UpdateEditorConfig(editorConfig models.EditorConfig) error {
|
||||
// 如果内存中已有配置,直接更新
|
||||
if cs.config != nil {
|
||||
log.Printf("UpdateEditorConfig: Updating in-memory editor config: %+v", editorConfig)
|
||||
cs.config.Editor = editorConfig
|
||||
return cs.saveAppConfig(cs.config)
|
||||
}
|
||||
|
||||
// 没有内存中的配置,需要先加载
|
||||
config, err := cs.loadAppConfig()
|
||||
// GetPathConfig 获取路径配置
|
||||
func (cs *ConfigService) GetPathConfig() (models.PathConfig, error) {
|
||||
config, err := cs.GetConfig()
|
||||
if err != nil {
|
||||
log.Printf("UpdateEditorConfig: Failed to load config: %v", err)
|
||||
// 使用默认配置
|
||||
config = models.NewDefaultAppConfig()
|
||||
return models.PathConfig{}, err
|
||||
}
|
||||
|
||||
// 更新编辑器配置
|
||||
config.Editor = editorConfig
|
||||
|
||||
// 保存到文件
|
||||
return cs.saveAppConfig(config)
|
||||
return config.Paths, nil
|
||||
}
|
||||
|
||||
// GetEditorConfig 获取编辑器配置
|
||||
func (cs *ConfigService) GetEditorConfig() (models.EditorConfig, error) {
|
||||
// 如果内存中已有配置,直接返回
|
||||
if cs.config != nil {
|
||||
return cs.config.Editor, nil
|
||||
}
|
||||
|
||||
// 否则从文件加载
|
||||
config, err := cs.loadAppConfig()
|
||||
// UpdatePathConfig 更新路径配置
|
||||
func (cs *ConfigService) UpdatePathConfig(pathConfig models.PathConfig) error {
|
||||
// 获取当前配置
|
||||
config, err := cs.GetConfig()
|
||||
if err != nil {
|
||||
log.Printf("GetEditorConfig: Failed to load config: %v", err)
|
||||
// 使用默认配置
|
||||
defaultConfig := models.NewDefaultAppConfig()
|
||||
cs.config = defaultConfig
|
||||
return defaultConfig.Editor, nil
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新内存中的配置
|
||||
cs.config = config
|
||||
log.Printf("GetEditorConfig: Retrieved editor config: %+v", config.Editor)
|
||||
return config.Editor, nil
|
||||
// 更新路径配置
|
||||
config.Paths = pathConfig
|
||||
config.Metadata.LastUpdated = time.Now()
|
||||
|
||||
// 保存更新后的配置
|
||||
return cs.store.Set(*config)
|
||||
}
|
||||
|
||||
// ResetToDefault 重置为默认配置
|
||||
func (cs *ConfigService) ResetToDefault() error {
|
||||
// 创建默认配置
|
||||
defaultConfig := models.NewDefaultAppConfig()
|
||||
|
||||
log.Printf("ResetToDefault: Resetting to default config")
|
||||
|
||||
// 保存到文件
|
||||
err := cs.saveAppConfig(defaultConfig)
|
||||
// GetMetadata 获取配置元数据
|
||||
func (cs *ConfigService) GetMetadata() (models.ConfigMetadata, error) {
|
||||
config, err := cs.GetConfig()
|
||||
if err != nil {
|
||||
log.Printf("ResetToDefault: Failed to save default config: %v", err)
|
||||
return fmt.Errorf("failed to save default config: %w", err)
|
||||
return models.ConfigMetadata{}, err
|
||||
}
|
||||
return config.Metadata, nil
|
||||
}
|
||||
|
||||
// UpdateMetadata 更新配置元数据
|
||||
func (cs *ConfigService) UpdateMetadata(metadata models.ConfigMetadata) error {
|
||||
// 获取当前配置
|
||||
config, err := cs.GetConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("ResetToDefault: Successfully reset to default config")
|
||||
// 更新元数据
|
||||
config.Metadata = metadata
|
||||
config.Metadata.LastUpdated = time.Now()
|
||||
|
||||
// 保存更新后的配置
|
||||
return cs.store.Set(*config)
|
||||
}
|
||||
|
||||
// OnShutdown 服务关闭时调用
|
||||
func (cs *ConfigService) OnShutdown() error {
|
||||
// 如果有未保存的更改,保存数据
|
||||
if cs.store.HasUnsavedChanges() {
|
||||
return cs.store.Save()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user