♻️ Refactor configuration service
This commit is contained in:
@@ -16,29 +16,24 @@ const (
|
||||
TabTypeTab TabType = "tab"
|
||||
)
|
||||
|
||||
// SaveOptions 保存选项
|
||||
type SaveOptions struct {
|
||||
// 自动保存延迟(毫秒)- 内容变更后多久自动保存
|
||||
AutoSaveDelay int `json:"autoSaveDelay"`
|
||||
// 变更字符阈值,超过此阈值立即触发保存
|
||||
ChangeThreshold int `json:"changeThreshold"`
|
||||
// 最小保存间隔(毫秒)- 两次保存之间的最小时间间隔,避免频繁IO
|
||||
MinSaveInterval int `json:"minSaveInterval"`
|
||||
}
|
||||
|
||||
// DocumentConfig 定义文档配置
|
||||
type DocumentConfig struct {
|
||||
SaveOptions SaveOptions `json:"saveOptions"` // 详细保存选项
|
||||
// 自动保存延迟(毫秒)- 内容变更后多久自动保存
|
||||
AutoSaveDelay int `json:"autoSaveDelay" yaml:"auto_save_delay" mapstructure:"auto_save_delay"`
|
||||
// 变更字符阈值,超过此阈值立即触发保存
|
||||
ChangeThreshold int `json:"changeThreshold" yaml:"change_threshold" mapstructure:"change_threshold"`
|
||||
// 最小保存间隔(毫秒)- 两次保存之间的最小时间间隔,避免频繁IO
|
||||
MinSaveInterval int `json:"minSaveInterval" yaml:"min_save_interval" mapstructure:"min_save_interval"`
|
||||
}
|
||||
|
||||
// EditorConfig 定义编辑器配置
|
||||
type EditorConfig struct {
|
||||
FontSize int `json:"fontSize"` // 字体大小
|
||||
EnableTabIndent bool `json:"enableTabIndent"` // 是否启用Tab缩进
|
||||
TabSize int `json:"tabSize"` // Tab大小
|
||||
TabType TabType `json:"tabType"` // Tab类型(空格或Tab)
|
||||
Language LanguageType `json:"language"` // 界面语言
|
||||
AlwaysOnTop bool `json:"alwaysOnTop"` // 窗口是否置顶
|
||||
FontSize int `json:"fontSize" yaml:"font_size" mapstructure:"font_size"` // 字体大小
|
||||
EnableTabIndent bool `json:"enableTabIndent" yaml:"enable_tab_indent" mapstructure:"enable_tab_indent"` // 是否启用Tab缩进
|
||||
TabSize int `json:"tabSize" yaml:"tab_size" mapstructure:"tab_size"` // Tab大小
|
||||
TabType TabType `json:"tabType" yaml:"tab_type" mapstructure:"tab_type"` // Tab类型(空格或Tab)
|
||||
Language LanguageType `json:"language" yaml:"language" mapstructure:"language"` // 界面语言
|
||||
AlwaysOnTop bool `json:"alwaysOnTop" yaml:"always_on_top" mapstructure:"always_on_top"` // 窗口是否置顶
|
||||
}
|
||||
|
||||
// LanguageType 语言类型定义
|
||||
@@ -53,35 +48,33 @@ const (
|
||||
|
||||
// PathsConfig 路径配置集合
|
||||
type PathsConfig struct {
|
||||
LogPath string `json:"logPath"` // 日志文件路径
|
||||
DataPath string `json:"dataPath"` // 数据存储路径
|
||||
DataPath string `json:"dataPath" yaml:"data_path" mapstructure:"data_path"` // 数据存储路径
|
||||
}
|
||||
|
||||
// AppConfig 应用配置 - 包含业务配置和路径配置
|
||||
type AppConfig struct {
|
||||
Editor EditorConfig `json:"editor"` // 编辑器配置
|
||||
Document DocumentConfig `json:"document"` // 文档配置
|
||||
Paths PathsConfig `json:"paths"` // 路径配置
|
||||
Metadata ConfigMetadata `json:"metadata"` // 配置元数据
|
||||
Editor EditorConfig `json:"editor" yaml:"editor" mapstructure:"editor"` // 编辑器配置
|
||||
Document DocumentConfig `json:"document" yaml:"document" mapstructure:"document"` // 文档配置
|
||||
Paths PathsConfig `json:"paths" yaml:"paths" mapstructure:"paths"` // 路径配置
|
||||
Metadata ConfigMetadata `json:"metadata" yaml:"metadata" mapstructure:"metadata"` // 配置元数据
|
||||
}
|
||||
|
||||
// ConfigMetadata 配置元数据
|
||||
type ConfigMetadata struct {
|
||||
Version string `json:"version"` // 配置版本
|
||||
LastUpdated time.Time `json:"lastUpdated"` // 最后更新时间
|
||||
Version string `json:"version" yaml:"version" mapstructure:"version"` // 配置版本
|
||||
LastUpdated time.Time `json:"lastUpdated" yaml:"last_updated" mapstructure:"last_updated"` // 最后更新时间
|
||||
}
|
||||
|
||||
// NewDefaultAppConfig 创建默认应用配置
|
||||
func NewDefaultAppConfig() *AppConfig {
|
||||
// 获取用户主目录
|
||||
homePath, err := os.UserHomeDir()
|
||||
// 获取当前工作目录
|
||||
currentDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
homePath = "."
|
||||
currentDir = "."
|
||||
}
|
||||
|
||||
// 默认路径配置
|
||||
rootDir := filepath.Join(homePath, ".voidraft")
|
||||
dataDir := filepath.Join(rootDir, "data")
|
||||
// 默认路径配置 - 使用当前目录
|
||||
dataDir := filepath.Join(currentDir, "data")
|
||||
|
||||
return &AppConfig{
|
||||
Editor: EditorConfig{
|
||||
@@ -93,14 +86,11 @@ func NewDefaultAppConfig() *AppConfig {
|
||||
AlwaysOnTop: false,
|
||||
},
|
||||
Document: DocumentConfig{
|
||||
SaveOptions: SaveOptions{
|
||||
AutoSaveDelay: 5000, // 5秒后自动保存
|
||||
ChangeThreshold: 500, // 500个字符变更触发保存
|
||||
MinSaveInterval: 1000, // 最小间隔1000毫秒
|
||||
},
|
||||
AutoSaveDelay: 5000, // 5秒后自动保存
|
||||
ChangeThreshold: 500, // 500个字符变更触发保存
|
||||
MinSaveInterval: 1000, // 最小间隔1000毫秒
|
||||
},
|
||||
Paths: PathsConfig{
|
||||
LogPath: filepath.Join(rootDir, "logs"),
|
||||
DataPath: dataDir,
|
||||
},
|
||||
Metadata: ConfigMetadata{
|
||||
|
@@ -3,48 +3,22 @@ package services
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/wailsapp/wails/v3/pkg/services/log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
"voidraft/internal/models"
|
||||
|
||||
"dario.cat/mergo"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/wailsapp/wails/v3/pkg/services/log"
|
||||
)
|
||||
|
||||
// ConfigPath 配置路径提供接口
|
||||
type ConfigPath interface {
|
||||
// GetConfigPath 获取配置文件路径
|
||||
GetConfigPath() string
|
||||
}
|
||||
|
||||
// DefaultConfigPathProvider 默认配置路径提供者
|
||||
type DefaultConfigPathProvider struct{}
|
||||
|
||||
// GetConfigPath 获取默认配置路径
|
||||
func (p *DefaultConfigPathProvider) GetConfigPath() string {
|
||||
// 获取用户主目录
|
||||
homePath, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
homePath = "."
|
||||
}
|
||||
// 返回固定的配置路径
|
||||
return filepath.Join(homePath, ".voidraft", "config", "config.json")
|
||||
}
|
||||
|
||||
// ConfigOption 配置服务选项
|
||||
type ConfigOption struct {
|
||||
Logger *log.LoggerService // 日志服务
|
||||
PathProvider ConfigPath // 路径提供者
|
||||
}
|
||||
|
||||
// ConfigService 提供配置管理功能
|
||||
// ConfigService 提供基于 Viper 的配置管理功能
|
||||
type ConfigService struct {
|
||||
store *Store[models.AppConfig] // 配置存储
|
||||
logger *log.LoggerService // 日志服务
|
||||
cache *models.AppConfig // 配置缓存
|
||||
cacheMu sync.RWMutex // 缓存锁
|
||||
viper *viper.Viper // Viper 实例
|
||||
logger *log.LoggerService // 日志服务
|
||||
mu sync.RWMutex // 读写锁
|
||||
}
|
||||
|
||||
// ConfigError 配置错误
|
||||
@@ -65,280 +39,391 @@ func (e *ConfigError) Unwrap() error {
|
||||
|
||||
// Is 实现错误匹配
|
||||
func (e *ConfigError) Is(target error) bool {
|
||||
_, ok := target.(*ConfigError)
|
||||
var configError *ConfigError
|
||||
ok := errors.As(target, &configError)
|
||||
return ok
|
||||
}
|
||||
|
||||
// NewConfigService 创建新的配置服务实例
|
||||
func NewConfigService(opt ...ConfigOption) *ConfigService {
|
||||
var option ConfigOption
|
||||
// ConfigLimits 配置限制定义
|
||||
type ConfigLimits struct {
|
||||
FontSizeMin int
|
||||
FontSizeMax int
|
||||
TabSizeMin int
|
||||
TabSizeMax int
|
||||
}
|
||||
|
||||
// 使用第一个选项
|
||||
if len(opt) > 0 {
|
||||
option = opt[0]
|
||||
// getConfigLimits 获取配置限制
|
||||
func getConfigLimits() ConfigLimits {
|
||||
return ConfigLimits{
|
||||
FontSizeMin: 12,
|
||||
FontSizeMax: 28,
|
||||
TabSizeMin: 2,
|
||||
TabSizeMax: 8,
|
||||
}
|
||||
}
|
||||
|
||||
// validateAndFixValue 验证并修正配置值
|
||||
func (cs *ConfigService) validateAndFixValue(key string, value interface{}) (interface{}, bool) {
|
||||
limits := getConfigLimits()
|
||||
fixed := false
|
||||
|
||||
switch key {
|
||||
case "editor.font_size":
|
||||
if intVal, ok := value.(int); ok {
|
||||
if intVal < limits.FontSizeMin {
|
||||
cs.logger.Warning("Config: Font size too small, fixing", "original", intVal, "fixed", limits.FontSizeMin)
|
||||
return limits.FontSizeMin, true
|
||||
}
|
||||
if intVal > limits.FontSizeMax {
|
||||
cs.logger.Warning("Config: Font size too large, fixing", "original", intVal, "fixed", limits.FontSizeMax)
|
||||
return limits.FontSizeMax, true
|
||||
}
|
||||
}
|
||||
|
||||
case "editor.tab_size":
|
||||
if intVal, ok := value.(int); ok {
|
||||
if intVal < limits.TabSizeMin {
|
||||
cs.logger.Warning("Config: Tab size too small, fixing", "original", intVal, "fixed", limits.TabSizeMin)
|
||||
return limits.TabSizeMin, true
|
||||
}
|
||||
if intVal > limits.TabSizeMax {
|
||||
cs.logger.Warning("Config: Tab size too large, fixing", "original", intVal, "fixed", limits.TabSizeMax)
|
||||
return limits.TabSizeMax, true
|
||||
}
|
||||
}
|
||||
|
||||
case "editor.tab_type":
|
||||
if strVal, ok := value.(string); ok {
|
||||
validTypes := []string{string(models.TabTypeSpaces), string(models.TabTypeTab)}
|
||||
isValid := false
|
||||
for _, validType := range validTypes {
|
||||
if strVal == validType {
|
||||
isValid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isValid {
|
||||
cs.logger.Warning("Config: Invalid tab type, fixing", "original", strVal, "fixed", string(models.TabTypeSpaces))
|
||||
return string(models.TabTypeSpaces), true
|
||||
}
|
||||
}
|
||||
|
||||
case "editor.language":
|
||||
if strVal, ok := value.(string); ok {
|
||||
validLanguages := []string{string(models.LangZhCN), string(models.LangEnUS)}
|
||||
isValid := false
|
||||
for _, validLang := range validLanguages {
|
||||
if strVal == validLang {
|
||||
isValid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isValid {
|
||||
cs.logger.Warning("Config: Invalid language, fixing", "original", strVal, "fixed", string(models.LangZhCN))
|
||||
return string(models.LangZhCN), true
|
||||
}
|
||||
}
|
||||
|
||||
case "document.auto_save_delay":
|
||||
if intVal, ok := value.(int); ok {
|
||||
if intVal < 1000 {
|
||||
cs.logger.Warning("Config: Auto save delay too small, fixing", "original", intVal, "fixed", 1000)
|
||||
return 1000, true
|
||||
}
|
||||
if intVal > 30000 {
|
||||
cs.logger.Warning("Config: Auto save delay too large, fixing", "original", intVal, "fixed", 30000)
|
||||
return 30000, true
|
||||
}
|
||||
}
|
||||
|
||||
case "document.change_threshold":
|
||||
if intVal, ok := value.(int); ok {
|
||||
if intVal < 10 {
|
||||
cs.logger.Warning("Config: Change threshold too small, fixing", "original", intVal, "fixed", 10)
|
||||
return 10, true
|
||||
}
|
||||
if intVal > 10000 {
|
||||
cs.logger.Warning("Config: Change threshold too large, fixing", "original", intVal, "fixed", 10000)
|
||||
return 10000, true
|
||||
}
|
||||
}
|
||||
|
||||
case "document.min_save_interval":
|
||||
if intVal, ok := value.(int); ok {
|
||||
if intVal < 100 {
|
||||
cs.logger.Warning("Config: Min save interval too small, fixing", "original", intVal, "fixed", 100)
|
||||
return 100, true
|
||||
}
|
||||
if intVal > 10000 {
|
||||
cs.logger.Warning("Config: Min save interval too large, fixing", "original", intVal, "fixed", 10000)
|
||||
return 10000, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value, fixed
|
||||
}
|
||||
|
||||
// validateAllConfig 验证并修正所有配置
|
||||
func (cs *ConfigService) validateAllConfig() error {
|
||||
hasChanges := false
|
||||
|
||||
// 获取当前配置
|
||||
var config models.AppConfig
|
||||
if err := cs.viper.Unmarshal(&config); err != nil {
|
||||
return &ConfigError{Operation: "unmarshal_for_validation", Err: err}
|
||||
}
|
||||
|
||||
// 验证编辑器配置
|
||||
if fixedValue, fixed := cs.validateAndFixValue("editor.font_size", config.Editor.FontSize); fixed {
|
||||
cs.viper.Set("editor.font_size", fixedValue)
|
||||
hasChanges = true
|
||||
}
|
||||
|
||||
if fixedValue, fixed := cs.validateAndFixValue("editor.tab_size", config.Editor.TabSize); fixed {
|
||||
cs.viper.Set("editor.tab_size", fixedValue)
|
||||
hasChanges = true
|
||||
}
|
||||
|
||||
if fixedValue, fixed := cs.validateAndFixValue("editor.tab_type", string(config.Editor.TabType)); fixed {
|
||||
cs.viper.Set("editor.tab_type", fixedValue)
|
||||
hasChanges = true
|
||||
}
|
||||
|
||||
if fixedValue, fixed := cs.validateAndFixValue("editor.language", string(config.Editor.Language)); fixed {
|
||||
cs.viper.Set("editor.language", fixedValue)
|
||||
hasChanges = true
|
||||
}
|
||||
|
||||
// 验证文档配置
|
||||
if fixedValue, fixed := cs.validateAndFixValue("document.auto_save_delay", config.Document.AutoSaveDelay); fixed {
|
||||
cs.viper.Set("document.auto_save_delay", fixedValue)
|
||||
hasChanges = true
|
||||
}
|
||||
|
||||
if fixedValue, fixed := cs.validateAndFixValue("document.change_threshold", config.Document.ChangeThreshold); fixed {
|
||||
cs.viper.Set("document.change_threshold", fixedValue)
|
||||
hasChanges = true
|
||||
}
|
||||
|
||||
if fixedValue, fixed := cs.validateAndFixValue("document.min_save_interval", config.Document.MinSaveInterval); fixed {
|
||||
cs.viper.Set("document.min_save_interval", fixedValue)
|
||||
hasChanges = true
|
||||
}
|
||||
|
||||
// 如果有修正,保存配置
|
||||
if hasChanges {
|
||||
if err := cs.viper.WriteConfig(); err != nil {
|
||||
return &ConfigError{Operation: "save_validated_config", Err: err}
|
||||
}
|
||||
cs.logger.Info("Config: Configuration validated and fixed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewConfigService 创建新的配置服务实例
|
||||
func NewConfigService(logger *log.LoggerService) *ConfigService {
|
||||
// 设置日志服务
|
||||
logger := option.Logger
|
||||
if logger == nil {
|
||||
logger = log.New()
|
||||
}
|
||||
|
||||
// 设置路径提供者
|
||||
pathProvider := option.PathProvider
|
||||
if pathProvider == nil {
|
||||
pathProvider = &DefaultConfigPathProvider{}
|
||||
// 获取当前工作目录
|
||||
currentDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
currentDir = "."
|
||||
}
|
||||
|
||||
// 获取配置路径
|
||||
configPath := pathProvider.GetConfigPath()
|
||||
logger.Info("Config: Using config path", "path", configPath)
|
||||
// 固定配置路径和文件名
|
||||
configPath := filepath.Join(currentDir, "config")
|
||||
configName := "config"
|
||||
|
||||
// 创建存储
|
||||
store := NewStore[models.AppConfig](StoreOption{
|
||||
FilePath: configPath,
|
||||
Logger: logger,
|
||||
})
|
||||
// 创建 Viper 实例
|
||||
v := viper.New()
|
||||
|
||||
// 配置 Viper
|
||||
v.SetConfigName(configName)
|
||||
v.SetConfigType("yaml")
|
||||
v.AddConfigPath(configPath)
|
||||
|
||||
// 设置环境变量前缀
|
||||
v.SetEnvPrefix("VOIDRAFT")
|
||||
v.AutomaticEnv()
|
||||
|
||||
// 设置默认值
|
||||
setDefaults(v)
|
||||
|
||||
// 构造配置服务实例
|
||||
service := &ConfigService{
|
||||
store: store,
|
||||
viper: v,
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
// 初始化加载配置
|
||||
// 初始化配置
|
||||
if err := service.initConfig(); err != nil {
|
||||
service.logger.Error("Config: Failed to initialize config", "error", err)
|
||||
}
|
||||
|
||||
// 验证并修正配置
|
||||
if err := service.validateAllConfig(); err != nil {
|
||||
service.logger.Error("Config: Failed to validate config", "error", err)
|
||||
}
|
||||
|
||||
// 启动配置文件监听
|
||||
service.startWatching()
|
||||
|
||||
return service
|
||||
}
|
||||
|
||||
// setDefaults 设置默认配置值
|
||||
func setDefaults(v *viper.Viper) {
|
||||
defaultConfig := models.NewDefaultAppConfig()
|
||||
|
||||
// 编辑器配置默认值
|
||||
v.SetDefault("editor.font_size", defaultConfig.Editor.FontSize)
|
||||
v.SetDefault("editor.enable_tab_indent", defaultConfig.Editor.EnableTabIndent)
|
||||
v.SetDefault("editor.tab_size", defaultConfig.Editor.TabSize)
|
||||
v.SetDefault("editor.tab_type", defaultConfig.Editor.TabType)
|
||||
v.SetDefault("editor.language", defaultConfig.Editor.Language)
|
||||
v.SetDefault("editor.always_on_top", defaultConfig.Editor.AlwaysOnTop)
|
||||
|
||||
// 文档配置默认值
|
||||
v.SetDefault("document.auto_save_delay", defaultConfig.Document.AutoSaveDelay)
|
||||
v.SetDefault("document.change_threshold", defaultConfig.Document.ChangeThreshold)
|
||||
v.SetDefault("document.min_save_interval", defaultConfig.Document.MinSaveInterval)
|
||||
|
||||
// 路径配置默认值
|
||||
v.SetDefault("paths.data_path", defaultConfig.Paths.DataPath)
|
||||
|
||||
// 元数据默认值
|
||||
v.SetDefault("metadata.version", defaultConfig.Metadata.Version)
|
||||
v.SetDefault("metadata.last_updated", defaultConfig.Metadata.LastUpdated)
|
||||
}
|
||||
|
||||
// initConfig 初始化配置
|
||||
func (cs *ConfigService) initConfig() error {
|
||||
config, err := cs.loadConfig()
|
||||
cs.mu.Lock()
|
||||
defer cs.mu.Unlock()
|
||||
|
||||
if err != nil {
|
||||
// 如果加载失败,使用默认配置
|
||||
defaultConfig := models.NewDefaultAppConfig()
|
||||
cs.logger.Info("Config: Using default config")
|
||||
|
||||
// 保存默认配置并更新缓存
|
||||
if err := cs.saveConfigWithCache(*defaultConfig); err != nil {
|
||||
return &ConfigError{Operation: "init_save_default", Err: err}
|
||||
// 尝试读取配置文件
|
||||
if err := cs.viper.ReadInConfig(); err != nil {
|
||||
var configFileNotFoundError viper.ConfigFileNotFoundError
|
||||
if errors.As(err, &configFileNotFoundError) {
|
||||
// 配置文件不存在,创建默认配置文件
|
||||
cs.logger.Info("Config: Config file not found, creating default config")
|
||||
return cs.createDefaultConfig()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 合并默认配置
|
||||
if err := cs.mergeWithDefaults(&config); err != nil {
|
||||
return &ConfigError{Operation: "init_merge_defaults", Err: err}
|
||||
// 配置文件存在但读取失败
|
||||
return &ConfigError{Operation: "read_config", Err: err}
|
||||
}
|
||||
|
||||
cs.logger.Info("Config: Successfully loaded config file", "file", cs.viper.ConfigFileUsed())
|
||||
return nil
|
||||
}
|
||||
|
||||
// mergeWithDefaults 将默认配置合并到现有配置中
|
||||
func (cs *ConfigService) mergeWithDefaults(config *models.AppConfig) error {
|
||||
defaultConfig := models.NewDefaultAppConfig()
|
||||
// createDefaultConfig 创建默认配置文件
|
||||
func (cs *ConfigService) createDefaultConfig() error {
|
||||
// 获取配置目录路径
|
||||
currentDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
currentDir = "."
|
||||
}
|
||||
configDir := filepath.Join(currentDir, "config")
|
||||
configPath := filepath.Join(configDir, "config.yaml")
|
||||
|
||||
// 使用mergo库合并配置
|
||||
if err := mergo.Merge(config, defaultConfig, mergo.WithOverrideEmptySlice); err != nil {
|
||||
return err
|
||||
// 确保配置目录存在
|
||||
if err := os.MkdirAll(configDir, 0755); err != nil {
|
||||
return &ConfigError{Operation: "create_config_dir", Err: err}
|
||||
}
|
||||
|
||||
// 更新最后修改时间
|
||||
config.Metadata.LastUpdated = time.Now()
|
||||
// 设置当前时间为最后更新时间
|
||||
cs.viper.Set("metadata.last_updated", time.Now())
|
||||
|
||||
// 保存合并后的配置
|
||||
return cs.saveConfigWithCache(*config)
|
||||
}
|
||||
|
||||
// loadConfig 加载配置
|
||||
func (cs *ConfigService) loadConfig() (models.AppConfig, error) {
|
||||
// 尝试从缓存获取
|
||||
cs.cacheMu.RLock()
|
||||
cachedConfig := cs.cache
|
||||
cs.cacheMu.RUnlock()
|
||||
|
||||
if cachedConfig != nil {
|
||||
return *cachedConfig, nil
|
||||
}
|
||||
|
||||
// 从存储加载
|
||||
config := cs.store.Get()
|
||||
|
||||
// 检查配置是否有效
|
||||
if !isValidConfig(config) {
|
||||
return models.AppConfig{}, errors.New("invalid or empty configuration")
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// isValidConfig 检查配置是否有效
|
||||
func isValidConfig(config models.AppConfig) bool {
|
||||
// 检查关键字段
|
||||
if config.Metadata.Version == "" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// saveConfigWithCache 保存配置并更新缓存
|
||||
func (cs *ConfigService) saveConfigWithCache(config models.AppConfig) error {
|
||||
// 更新缓存
|
||||
cs.cacheMu.Lock()
|
||||
cs.cache = &config
|
||||
cs.cacheMu.Unlock()
|
||||
|
||||
// 保存到存储
|
||||
if err := cs.store.Set(config); err != nil {
|
||||
return err
|
||||
// 使用 SafeWriteConfigAs 写入配置文件(如果文件不存在则创建)
|
||||
if err := cs.viper.SafeWriteConfigAs(configPath); err != nil {
|
||||
return &ConfigError{Operation: "write_default_config", Err: err}
|
||||
}
|
||||
|
||||
cs.logger.Info("Config: Created default config file", "path", configPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
// startWatching 启动配置文件监听
|
||||
func (cs *ConfigService) startWatching() {
|
||||
// 设置配置变化回调
|
||||
cs.viper.OnConfigChange(func(e fsnotify.Event) {
|
||||
cs.logger.Info("Config: Config file changed", "file", e.Name, "operation", e.Op.String())
|
||||
// 注释掉自动更新时间戳,避免无限循环
|
||||
// err := cs.Set("metadata.last_updated", time.Now())
|
||||
// if err != nil {
|
||||
// cs.logger.Error("Config: Failed to update last_updated field", "error", err)
|
||||
// }
|
||||
})
|
||||
|
||||
// 启动配置文件监听
|
||||
cs.viper.WatchConfig()
|
||||
cs.logger.Info("Config: Started watching config file for changes")
|
||||
}
|
||||
|
||||
// GetConfig 获取完整应用配置
|
||||
func (cs *ConfigService) GetConfig() (*models.AppConfig, error) {
|
||||
// 优先使用缓存
|
||||
cs.cacheMu.RLock()
|
||||
if cs.cache != nil {
|
||||
config := *cs.cache
|
||||
cs.cacheMu.RUnlock()
|
||||
return &config, nil
|
||||
cs.mu.RLock()
|
||||
defer cs.mu.RUnlock()
|
||||
|
||||
var config models.AppConfig
|
||||
if err := cs.viper.Unmarshal(&config); err != nil {
|
||||
return nil, &ConfigError{Operation: "unmarshal_config", Err: err}
|
||||
}
|
||||
cs.cacheMu.RUnlock()
|
||||
|
||||
// 加载配置
|
||||
config, err := cs.loadConfig()
|
||||
if err != nil {
|
||||
// 加载失败,使用默认配置
|
||||
defaultConfig := models.NewDefaultAppConfig()
|
||||
|
||||
// 保存默认配置
|
||||
if saveErr := cs.saveConfigWithCache(*defaultConfig); saveErr != nil {
|
||||
cs.logger.Error("Config: Failed to save default config", "error", saveErr)
|
||||
return nil, &ConfigError{Operation: "get_save_default", Err: saveErr}
|
||||
}
|
||||
|
||||
return defaultConfig, nil
|
||||
}
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// SaveConfig 保存完整应用配置
|
||||
func (cs *ConfigService) SaveConfig(config *models.AppConfig) error {
|
||||
if config == nil {
|
||||
return errors.New("cannot save nil config")
|
||||
// Set 设置配置项
|
||||
func (cs *ConfigService) Set(key string, value interface{}) error {
|
||||
cs.mu.Lock()
|
||||
defer cs.mu.Unlock()
|
||||
|
||||
// 验证并修正配置值
|
||||
validatedValue, wasFixed := cs.validateAndFixValue(key, value)
|
||||
|
||||
// 设置验证后的值
|
||||
cs.viper.Set(key, validatedValue)
|
||||
|
||||
// 使用 WriteConfig 写入配置文件(会触发文件监听)
|
||||
if err := cs.viper.WriteConfig(); err != nil {
|
||||
return &ConfigError{Operation: "set_config", Err: err}
|
||||
}
|
||||
|
||||
// 更新配置元数据
|
||||
config.Metadata.LastUpdated = time.Now()
|
||||
if wasFixed {
|
||||
cs.logger.Debug("Config: Successfully set config with validation", "key", key, "original", value, "fixed", validatedValue)
|
||||
} else {
|
||||
cs.logger.Debug("Config: Successfully set config", "key", key, "value", value)
|
||||
}
|
||||
|
||||
// 保存配置
|
||||
return cs.saveConfigWithCache(*config)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateEditorConfig 更新编辑器配置
|
||||
func (cs *ConfigService) UpdateEditorConfig(editorConfig models.EditorConfig) error {
|
||||
config, err := cs.GetConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 保存当前的语言设置
|
||||
currentLanguage := config.Editor.Language
|
||||
|
||||
// 更新编辑器配置
|
||||
config.Editor = editorConfig
|
||||
|
||||
// 如果更新后的语言设置为空,则使用之前的语言设置
|
||||
if editorConfig.Language == "" {
|
||||
config.Editor.Language = currentLanguage
|
||||
}
|
||||
|
||||
return cs.SaveConfig(config)
|
||||
}
|
||||
|
||||
// GetEditorConfig 获取编辑器配置
|
||||
func (cs *ConfigService) GetEditorConfig() (models.EditorConfig, error) {
|
||||
config, err := cs.GetConfig()
|
||||
if err != nil {
|
||||
return models.EditorConfig{}, err
|
||||
}
|
||||
return config.Editor, nil
|
||||
}
|
||||
|
||||
// SetLanguage 设置语言
|
||||
func (cs *ConfigService) SetLanguage(language models.LanguageType) error {
|
||||
// 验证语言类型有效
|
||||
if language != models.LangZhCN && language != models.LangEnUS {
|
||||
return errors.New("invalid language type")
|
||||
}
|
||||
|
||||
config, err := cs.GetConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config.Editor.Language = language
|
||||
return cs.SaveConfig(config)
|
||||
}
|
||||
|
||||
// GetLanguage 获取当前语言设置
|
||||
func (cs *ConfigService) GetLanguage() (models.LanguageType, error) {
|
||||
editorConfig, err := cs.GetEditorConfig()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return editorConfig.Language, nil
|
||||
}
|
||||
|
||||
// UpdatePaths 更新路径配置
|
||||
func (cs *ConfigService) UpdatePaths(paths models.PathsConfig) error {
|
||||
config, err := cs.GetConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config.Paths = paths
|
||||
return cs.SaveConfig(config)
|
||||
}
|
||||
|
||||
// GetPaths 获取路径配置
|
||||
func (cs *ConfigService) GetPaths() (models.PathsConfig, error) {
|
||||
config, err := cs.GetConfig()
|
||||
if err != nil {
|
||||
return models.PathsConfig{}, err
|
||||
}
|
||||
return config.Paths, nil
|
||||
}
|
||||
|
||||
// UpdateMetadata 更新配置元数据
|
||||
func (cs *ConfigService) UpdateMetadata(metadata models.ConfigMetadata) error {
|
||||
config, err := cs.GetConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config.Metadata = metadata
|
||||
return cs.SaveConfig(config)
|
||||
}
|
||||
|
||||
// GetMetadata 获取配置元数据
|
||||
func (cs *ConfigService) GetMetadata() (models.ConfigMetadata, error) {
|
||||
config, err := cs.GetConfig()
|
||||
if err != nil {
|
||||
return models.ConfigMetadata{}, err
|
||||
}
|
||||
return config.Metadata, nil
|
||||
// Get 获取配置项
|
||||
func (cs *ConfigService) Get(key string) interface{} {
|
||||
cs.mu.RLock()
|
||||
defer cs.mu.RUnlock()
|
||||
return cs.viper.Get(key)
|
||||
}
|
||||
|
||||
// ResetConfig 重置为默认配置
|
||||
func (cs *ConfigService) ResetConfig() error {
|
||||
defaultConfig := models.NewDefaultAppConfig()
|
||||
return cs.SaveConfig(defaultConfig)
|
||||
cs.mu.Lock()
|
||||
defer cs.mu.Unlock()
|
||||
|
||||
// 重新设置默认值
|
||||
setDefaults(cs.viper)
|
||||
|
||||
// 使用 WriteConfig 写入配置文件(会触发文件监听)
|
||||
if err := cs.viper.WriteConfig(); err != nil {
|
||||
return &ConfigError{Operation: "reset_config", Err: err}
|
||||
}
|
||||
|
||||
cs.logger.Info("Config: Successfully reset to default configuration")
|
||||
return nil
|
||||
}
|
||||
|
@@ -147,9 +147,9 @@ func (ds *DocumentService) scheduleAutoSave() {
|
||||
|
||||
// 打印保存设置,便于调试
|
||||
ds.logger.Debug("Document: Auto save settings",
|
||||
"autoSaveDelay", config.Document.SaveOptions.AutoSaveDelay,
|
||||
"changeThreshold", config.Document.SaveOptions.ChangeThreshold,
|
||||
"minSaveInterval", config.Document.SaveOptions.MinSaveInterval)
|
||||
"autoSaveDelay", config.Document.AutoSaveDelay,
|
||||
"changeThreshold", config.Document.ChangeThreshold,
|
||||
"minSaveInterval", config.Document.MinSaveInterval)
|
||||
|
||||
ds.lock.Lock()
|
||||
defer ds.lock.Unlock()
|
||||
@@ -160,7 +160,7 @@ func (ds *DocumentService) scheduleAutoSave() {
|
||||
}
|
||||
|
||||
// 创建新的自动保存定时器
|
||||
autoSaveDelay := config.Document.SaveOptions.AutoSaveDelay
|
||||
autoSaveDelay := config.Document.AutoSaveDelay
|
||||
ds.logger.Debug("Document: Scheduling auto save", "delay", autoSaveDelay)
|
||||
ds.scheduleTimerWithDelay(autoSaveDelay)
|
||||
}
|
||||
@@ -197,7 +197,7 @@ func (ds *DocumentService) saveToStore(trigger SaveTrigger) {
|
||||
|
||||
// 如果成功获取了配置,使用配置值
|
||||
if err == nil && config != nil {
|
||||
minInterval = config.Document.SaveOptions.MinSaveInterval
|
||||
minInterval = config.Document.MinSaveInterval
|
||||
}
|
||||
|
||||
// 如果是自动保存,检查最小保存间隔
|
||||
@@ -405,7 +405,7 @@ func (ds *DocumentService) UpdateActiveDocumentContent(content string) error {
|
||||
|
||||
// 如果成功获取了配置,使用配置值
|
||||
if err == nil && config != nil {
|
||||
threshold = config.Document.SaveOptions.ChangeThreshold
|
||||
threshold = config.Document.ChangeThreshold
|
||||
}
|
||||
|
||||
ds.lock.Lock()
|
||||
@@ -530,22 +530,20 @@ func (ds *DocumentService) GetSaveSettings() (*models.DocumentConfig, error) {
|
||||
|
||||
// UpdateSaveSettings 更新文档保存设置
|
||||
func (ds *DocumentService) UpdateSaveSettings(docConfig models.DocumentConfig) error {
|
||||
// 获取当前配置
|
||||
config, err := ds.configService.GetConfig()
|
||||
if err != nil {
|
||||
return &DocumentError{Operation: "update_save_settings", Err: err}
|
||||
// 使用配置服务的 Set 方法更新文档配置
|
||||
if err := ds.configService.Set("document.auto_save_delay", docConfig.AutoSaveDelay); err != nil {
|
||||
return &DocumentError{Operation: "update_save_settings_auto_save_delay", Err: err}
|
||||
}
|
||||
|
||||
// 更新保存设置
|
||||
config.Document = docConfig
|
||||
|
||||
// 保存配置
|
||||
err = ds.configService.SaveConfig(config)
|
||||
if err != nil {
|
||||
return &DocumentError{Operation: "update_save_settings_save", Err: err}
|
||||
if err := ds.configService.Set("document.change_threshold", docConfig.ChangeThreshold); err != nil {
|
||||
return &DocumentError{Operation: "update_save_settings_change_threshold", Err: err}
|
||||
}
|
||||
|
||||
// 安排自动保存(不再需要检查保存模式)
|
||||
if err := ds.configService.Set("document.min_save_interval", docConfig.MinSaveInterval); err != nil {
|
||||
return &DocumentError{Operation: "update_save_settings_min_save_interval", Err: err}
|
||||
}
|
||||
|
||||
// 安排自动保存
|
||||
ds.scheduleAutoSave()
|
||||
|
||||
ds.logger.Info("Document: Updated save settings")
|
||||
|
@@ -17,11 +17,8 @@ func NewServiceManager() *ServiceManager {
|
||||
// 初始化日志服务
|
||||
logger := log.New()
|
||||
|
||||
// 初始化配置服务
|
||||
configService := NewConfigService(ConfigOption{
|
||||
Logger: logger,
|
||||
PathProvider: nil,
|
||||
})
|
||||
// 初始化配置服务 - 使用固定配置(当前目录下的 config/config.yaml)
|
||||
configService := NewConfigService(logger)
|
||||
|
||||
// 初始化文档服务
|
||||
documentService := NewDocumentService(configService, logger)
|
||||
|
Reference in New Issue
Block a user