package template import ( "fmt" "log/slog" "github.com/spf13/viper" ) // ConfigLoader loads template configuration from YAML files type ConfigLoader struct { configPath string logger *slog.Logger } // NewConfigLoader creates a new template config loader func NewConfigLoader(configPath string) *ConfigLoader { return &ConfigLoader{ configPath: configPath, logger: slog.With("component", "template_config"), } } // TemplateConfigFile represents the structure of template configuration file type TemplateConfigFile struct { Templates map[string]struct { Name string `yaml:"name"` Type string `yaml:"type"` Description string `yaml:"description"` Fields map[string]string `yaml:"fields"` Config map[string]interface{} `yaml:"config"` } `yaml:"templates"` TemplateTypes map[string]struct { Layout string `yaml:"layout"` Fields map[string]string `yaml:"fields"` Options map[string]interface{} `yaml:"options"` } `yaml:"template_types"` } // LoadTemplateConfig loads template configuration from YAML files func (cl *ConfigLoader) LoadTemplateConfig() (*TemplateConfigFile, error) { viper.SetConfigName("templates") viper.SetConfigType("yaml") // Add search paths viper.AddConfigPath(cl.configPath) viper.AddConfigPath("./config/templates") viper.AddConfigPath("./web/templates") viper.AddConfigPath("/etc/database-render/templates") // Set default values viper.SetDefault("templates", map[string]interface{}{}) viper.SetDefault("template_types", map[string]interface{}{ "list": map[string]interface{}{ "layout": "table", "fields": map[string]string{ "default": "raw", "time": "time", "tag": "tag", }, "options": map[string]interface{}{ "striped": true, "hover": true, }, }, "card": map[string]interface{}{ "layout": "grid", "fields": map[string]string{ "default": "raw", "time": "relative", "tag": "badge", }, "options": map[string]interface{}{ "columns": "3", "spacing": "md", }, }, }) // Read configuration if err := viper.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); ok { cl.logger.Warn("template config file not found, using defaults") return cl.createDefaultConfig(), nil } return nil, fmt.Errorf("failed to read template config: %w", err) } var config TemplateConfigFile if err := viper.Unmarshal(&config); err != nil { return nil, fmt.Errorf("failed to unmarshal template config: %w", err) } cl.logger.Info("template configuration loaded successfully", "config_file", viper.ConfigFileUsed(), "templates_count", len(config.Templates), "types_count", len(config.TemplateTypes)) return &config, nil } // createDefaultConfig creates default template configuration func (cl *ConfigLoader) createDefaultConfig() *TemplateConfigFile { return &TemplateConfigFile{ Templates: map[string]struct { Name string `yaml:"name"` Type string `yaml:"type"` Description string `yaml:"description"` Fields map[string]string `yaml:"fields"` Config map[string]interface{} `yaml:"config"` }{ "articles": { Name: "文章列表", Type: "list", Description: "技术文章列表视图", Fields: map[string]string{ "title": "string", "content": "markdown", "category": "category", "tags": "tag", "created_at": "time", }, Config: map[string]interface{}{ "page_size": 15, "show_pagination": true, }, }, "logs": { Name: "系统日志", Type: "table", Description: "系统日志表格视图", Fields: map[string]string{ "level": "tag", "message": "string", "timestamp": "time", }, Config: map[string]interface{}{ "page_size": 50, "show_filter": true, }, }, }, TemplateTypes: map[string]struct { Layout string `yaml:"layout"` Fields map[string]string `yaml:"fields"` Options map[string]interface{} `yaml:"options"` }{ "list": { Layout: "table", Fields: map[string]string{ "default": "raw", "time": "time", "tag": "tag", }, Options: map[string]interface{}{ "striped": true, "hover": true, }, }, "card": { Layout: "grid", Fields: map[string]string{ "default": "raw", "time": "relative", "tag": "badge", }, Options: map[string]interface{}{ "columns": "3", "spacing": "md", }, }, }, } } // ValidateTemplateConfig validates template configuration func (cl *ConfigLoader) ValidateTemplateConfig(config *TemplateConfigFile) error { for name, template := range config.Templates { if template.Name == "" { return fmt.Errorf("template %s: name cannot be empty", name) } if template.Type == "" { return fmt.Errorf("template %s: type cannot be empty", name) } } for name, templateType := range config.TemplateTypes { if templateType.Layout == "" { return fmt.Errorf("template type %s: layout cannot be empty", name) } } return nil } // GetTemplateConfig returns template configuration for a specific table func (cl *ConfigLoader) GetTemplateConfig(tableName string) (interface{}, bool) { config, err := cl.LoadTemplateConfig() if err != nil { cl.logger.Error("failed to load template config", "error", err) return nil, false } template, exists := config.Templates[tableName] return template, exists } // GetTemplateType returns template type configuration func (cl *ConfigLoader) GetTemplateType(typeName string) (interface{}, bool) { config, err := cl.LoadTemplateConfig() if err != nil { cl.logger.Error("failed to load template config", "error", err) return nil, false } typeConfig, exists := config.TemplateTypes[typeName] return typeConfig, exists } // GetAvailableTemplates returns all available templates func (cl *ConfigLoader) GetAvailableTemplates() map[string]string { config, err := cl.LoadTemplateConfig() if err != nil { cl.logger.Error("failed to load template config", "error", err) return map[string]string{"list": "默认列表"} } result := make(map[string]string) for key, template := range config.Templates { result[key] = template.Description } // Add built-in templates if not overridden if _, exists := result["list"]; !exists { result["list"] = "列表视图" } if _, exists := result["card"]; !exists { result["card"] = "卡片视图" } if _, exists := result["timeline"]; !exists { result["timeline"] = "时间轴视图" } return result }