package service import ( "fmt" "time" "git.x2erp.com/qdy/go-base/ctx" "git.x2erp.com/qdy/go-base/logger" "git.x2erp.com/qdy/go-base/model/request" "git.x2erp.com/qdy/go-base/model/response" "git.x2erp.com/qdy/go-base/util" "git.x2erp.com/qdy/go-db/factory/database" "git.x2erp.com/qdy/go-svc-configure/models" "github.com/jmoiron/sqlx" ) func InitConfigTemplates(dbFactory *database.DBFactory, req request.QueryRequest, reqCtx *ctx.RequestContext) *response.QueryResult[map[string]interface{}] { return initConfigTemplates(dbFactory.GetDB(), reqCtx) } // InitConfigTemplates 初始化默认配置信息 func initConfigTemplates(db *sqlx.DB, reqCtx *ctx.RequestContext) *response.QueryResult[map[string]interface{}] { builder := models.NewConfigTemplateBuilder() templates := builder. // Service模板 StartTemplate("service", "Service配置", "服务基础配置"). AddInt("port", "服务监听端口").Default("8080").Min(1024).Max(65535).Required().FinishDetail(). AddString("service_name", "服务名称").Default("svc-configure").Pattern("^[a-zA-Z][a-zA-Z0-9_-]*$").Required().FinishDetail(). AddString("instance_name", "实例名称").Default("svc-configure-01").Required().FinishDetail(). AddInt("read_timeout", "读取超时时间(秒)").Default("30").Min(1).Max(300).Required().FinishDetail(). AddInt("write_timeout", "写入超时时间(秒)").Default("30").Min(1).Max(300).Required().FinishDetail(). AddInt("idle_timeout", "空闲超时时间(秒)").Default("60").Min(10).Max(600).Required().FinishDetail(). FinishTemplate(). // 通用数据库配置 StartTemplate("database", "通用数据库配置", "关系型数据库通用连接配置"). AddEnum("type", "数据库类型", []string{"postgresql", "mysql", "oracle"}).Default("postgresql").Required().FinishDetail(). AddString("host", "数据库主机地址").Default("localhost").Required().FinishDetail(). AddInt("port", "数据库端口").Default("5432").Min(1).Max(65535).Required().FinishDetail(). AddString("database", "数据库名称").Default("").Required().FinishDetail(). AddString("username", "用户名").Default("").Required().FinishDetail(). AddString("password", "密码").Default("").Sensitive().Required().FinishDetail(). AddInt("max_conns", "最大连接数").Default("100").Min(1).Max(1000).FinishDetail(). AddInt("min_conns", "最小连接数").Default("5").Min(0).Max(100).FinishDetail(). AddInt("max_lifetime", "连接最大生存时间(秒)").Default("3600").Min(60).Max(86400).FinishDetail(). AddInt("max_idle_time", "连接最大空闲时间(秒)").Default("1800").Min(30).Max(3600).FinishDetail(). AddInt("connect_timeout", "连接超时时间(秒)").Default("5").Min(1).Max(30).FinishDetail(). AddInt("read_timeout", "读取超时时间(秒)").Default("30").Min(1).Max(300).FinishDetail(). AddInt("write_timeout", "写入超时时间(秒)").Default("30").Min(1).Max(300).FinishDetail(). AddBoolean("ssl_enabled", "启用SSL加密").Default("false").FinishDetail(). AddString("ssl_ca", "CA证书路径").Default("").FinishDetail(). AddString("ssl_cert", "客户端证书路径").Default("").FinishDetail(). AddString("ssl_key", "客户端密钥路径").Default("").FinishDetail(). AddString("charset", "字符集").Default("utf8mb4").FinishDetail(). AddString("timezone", "时区").Default("Asia/Shanghai").FinishDetail(). AddBoolean("parse_time", "解析时间字段").Default("true").FinishDetail(). AddString("loc", "时区位置").Default("Asia/Shanghai").FinishDetail(). AddInt("max_prepared_stmt_count", "最大预处理语句数").Default("128").Min(0).Max(1024).FinishDetail(). AddInt("max_allowed_packet", "最大允许数据包大小(MB)").Default("64").Min(1).Max(1024).FinishDetail(). AddInt("timeout", "全局超时时间(秒)").Default("30").Min(1).Max(300).FinishDetail(). AddBoolean("debug_mode", "调试模式").Default("false").FinishDetail(). FinishTemplate(). // Log模板 StartTemplate("log", "日志增强配置", "增强版日志配置"). AddEnum("level", "日志级别", []string{"debug", "info", "warn", "error", "fatal"}).Default("debug").Required().FinishDetail(). AddString("output", "日志输出目标").Default("console,es").FinishDetail(). //AddBoolean("json_format", "JSON格式输出").Default("true").FinishDetail(). AddString("file_path", "日志文件路径").Default("./logs/app-%s.log").FinishDetail(). AddInt("max_size", "最大文件大小(MB)").Default("100").FinishDetail(). AddInt("max_backups", "最大备份文件数").Default("30").FinishDetail(). AddInt("max_age", "最大保存天数").Default("7").FinishDetail(). AddBoolean("compress", "压缩备份文件").Default("true").FinishDetail(). AddString("es_path", "ES地址").Default("http://x3cloudb.x2erp.com:9200").FinishDetail(). AddString("es_username", "ES用户名").Default("").FinishDetail(). AddString("es_password", "ES密码").Default("").Sensitive().FinishDetail(). FinishTemplate(). // Doris模板 StartTemplate("doris", "Doris配置", "Doris数据库连接配置"). AddString("fe_host", "Doris FE节点主机地址").Default("161.189.89.196").Pattern(`^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$`).Required().FinishDetail(). AddInt("fe_port", "Doris FE端口号").Default("8040").Min(1).Max(65535).Required().FinishDetail(). AddString("fe_username", "Doris FE用户名").Default("root").Required().FinishDetail(). AddString("fe_password", "Doris FE密码").Default("").Sensitive().Required().MinLength(6).MaxLength(100).FinishDetail(). AddInt("stream_load_timeout", "Stream Load超时时间(秒)").Default("300").Min(10).Max(3600).FinishDetail(). AddInt("batch_size", "批量大小").Default("1000").Min(1).Max(10000).FinishDetail(). FinishTemplate(). // micro 注册发现配置 StartTemplate("micro", "Micro服务配置", "Micro服务基础配置"). AddString("service_name", "服务名称").Default("svc-worker").Required().FinishDetail(). AddString("service_address", "服务地址").Default(":7070").Required().FinishDetail(). AddString("registry_address", "注册中心地址").Default("localhost:8500").Required().FinishDetail(). AddString("registry_type", "注册中心类型").Default("consul").Required().FinishDetail(). AddInt("registry_timeout", "注册中心超时时间").Default("10").Required().FinishDetail(). FinishTemplate(). // rabbitmq StartTemplate("rabbitmq", "RabbitMQ配置", "RabbitMQ消息队列配置"). AddString("host", "主机地址").Default("localhost").Required().FinishDetail(). AddInt("port", "端口").Default("5672").Required().FinishDetail(). AddString("username", "用户名").Default("guest").Required().FinishDetail(). AddString("password", "密码").Default("guest").Sensitive().Required().FinishDetail(). AddString("vhost", "虚拟主机").Default("/").FinishDetail(). AddBoolean("use_tls", "启用TLS").Default("false").FinishDetail(). AddString("ca_cert", "CA证书").Default("").FinishDetail(). AddString("cert_file", "证书文件").Default("").FinishDetail(). AddString("key_file", "密钥文件").Default("").FinishDetail(). AddInt("max_open_channels", "最大通道数").Default("10").FinishDetail(). AddInt("reconnect_delay", "重连延迟(ms)").Default("5000").FinishDetail(). AddInt("max_reconnect_attempts", "最大重试次数").Default("10").FinishDetail(). AddInt("heartbeat", "心跳间隔(s)").Default("30").FinishDetail(). AddInt("channel_size", "通道大小").Default("100").FinishDetail(). AddString("default_exchange", "默认交换机").Default("amq.direct").FinishDetail(). AddString("default_queue", "默认队列").Default("").FinishDetail(). AddBoolean("auto_ack", "自动确认").Default("false").FinishDetail(). AddBoolean("mandatory", "强制路由").Default("false").FinishDetail(). AddBoolean("immediate", "立即发送").Default("false").FinishDetail(). AddInt("prefetch_count", "预取数量").Default("1").FinishDetail(). AddInt("prefetch_size", "预取大小").Default("0").FinishDetail(). AddBoolean("global", "全局设置").Default("false").FinishDetail(). AddBoolean("publisher_confirms", "发布确认").Default("false").FinishDetail(). AddInt("confirm_timeout", "确认超时(s)").Default("5").FinishDetail(). FinishTemplate(). GetTemplates() return createOrUpdateConfigTemplates(db, templates, reqCtx) } func CreateOrUpdateConfigTemplates(dbFactory *database.DBFactory, req *models.ConfigTemplate, reqCtx *ctx.RequestContext) *response.QueryResult[map[string]interface{}] { configTemplates := []*models.ConfigTemplate{req} return createOrUpdateConfigTemplates(dbFactory.GetDB(), configTemplates, reqCtx) } // CreateOrUpdateConfigTemplates 创建或更新配置模板(支持部分更新) func createOrUpdateConfigTemplates(db *sqlx.DB, templates []*models.ConfigTemplate, reqCtx *ctx.RequestContext) *response.QueryResult[map[string]interface{}] { if db == nil { return util.CreateErrorResult[map[string]interface{}]("database connection is nil", reqCtx) } // 开始事务 tx, err := db.Beginx() if err != nil { return util.CreateErrorResult[map[string]interface{}](fmt.Sprintf("begin transaction failed: %v", err), reqCtx) } defer func() { if err != nil { tx.Rollback() } }() now := time.Now() for _, template := range templates { // 设置时间 if template.CreatedAt.IsZero() { template.CreatedAt = now } template.UpdatedAt = now // 1. 插入或更新模板主表 err := upsertTemplate(tx, template, now, reqCtx) if err != nil { return util.CreateErrorResult[map[string]interface{}](fmt.Sprintf("upsertTemplate: %v", err), reqCtx) } // 2. 处理模板详情(部分更新) if len(template.Details) > 0 { err = upsertTemplateDetails(tx, template.Details, now) if err != nil { return util.CreateErrorResult[map[string]interface{}](fmt.Sprintf("upsertTemplateDetails: %v", err), reqCtx) } } } errCommit := tx.Commit() if errCommit != nil { return util.CreateErrorResult[map[string]interface{}](fmt.Sprintf("db commit: %v", errCommit), reqCtx) } return util.CreateSuccessResult[map[string]interface{}](reqCtx) } // upsertTemplate 插入或更新模板主表 func upsertTemplate(tx *sqlx.Tx, template *models.ConfigTemplate, now time.Time, reqCtx *ctx.RequestContext) error { sqlStr := ` INSERT INTO config_template ( config_template_id, template_name, is_default, sort_order, description, creator, created_at, updated_at ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) ON CONFLICT (config_template_id) DO UPDATE SET is_default = EXCLUDED.is_default, sort_order = EXCLUDED.sort_order, description = EXCLUDED.description, creator = EXCLUDED.creator, updated_at = $8 RETURNING config_template_id` var templateID string err := tx.QueryRow(sqlStr, template.ConfigTemplateID, template.TemplateName, template.IsDefault, template.SortOrder, template.Description, template.Creator, template.CreatedAt, now, // 使用事务中的时间 ).Scan(&templateID) if err != nil { return logger.ErrorCf(reqCtx, "upsert template failed: %v", err) } logger.DebugC(reqCtx, "save data:%s", templateID) return nil } // upsertTemplateDetails 插入或更新模板详情(基于config_key唯一性) func upsertTemplateDetails(tx *sqlx.Tx, details []models.ConfigTemplateDetail, now time.Time) error { for _, detail := range details { // 设置时间和模板ID if detail.CreatedAt.IsZero() { detail.CreatedAt = now } detail.UpdatedAt = now //detail.ConfigTemplateID = templateID sqlStr := ` INSERT INTO config_template_detail ( config_template_detail_id, config_template_id, config_key, config_value, value_type, data_type, is_required, default_value, validation_rules, description, sort_order, is_sensitive, is_readonly, creator, created_at, updated_at ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15,$16) ON CONFLICT (config_template_detail_id) DO UPDATE SET config_value = EXCLUDED.config_value, value_type = EXCLUDED.value_type, data_type = EXCLUDED.data_type, is_required = EXCLUDED.is_required, default_value = EXCLUDED.default_value, validation_rules = EXCLUDED.validation_rules, description = EXCLUDED.description, sort_order = EXCLUDED.sort_order, is_sensitive = EXCLUDED.is_sensitive, is_readonly = EXCLUDED.is_readonly, creator = EXCLUDED.creator, updated_at = $15` _, err := tx.Exec(sqlStr, detail.ConfigTemplateDetailID, detail.ConfigTemplateID, detail.ConfigKey, detail.ConfigValue, detail.ValueType, detail.DataType, detail.IsRequired, detail.DefaultValue, detail.ValidationRules, detail.Description, detail.SortOrder, detail.IsSensitive, detail.IsReadonly, detail.Creator, detail.CreatedAt, now, ) if err != nil { return fmt.Errorf("upsert template detail failed (key: %s): %v", detail.ConfigKey, err) } } return nil } // 批量版本(如果需要更高性能) func batchUpsertTemplateDetails(tx *sqlx.Tx, templateID int64, details []models.ConfigTemplateDetail, now time.Time) error { if len(details) == 0 { return nil } // 使用临时表进行批量操作 _, err := tx.Exec(` CREATE TEMP TABLE temp_config_details ( config_key VARCHAR(100), config_value TEXT, value_type VARCHAR(20), data_type VARCHAR(50), is_required BOOLEAN, default_value TEXT, validation_rules JSONB, description TEXT, sort_order INTEGER, is_sensitive BOOLEAN, is_readonly BOOLEAN, creator VARCHAR(100) ) ON COMMIT DROP`) if err != nil { return fmt.Errorf("create temp table failed: %v", err) } // 批量插入到临时表 stmt, err := tx.Prepare(` INSERT INTO temp_config_details VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`) if err != nil { return err } defer stmt.Close() for _, detail := range details { _, err = stmt.Exec( detail.ConfigKey, detail.ConfigValue, detail.ValueType, detail.DataType, detail.IsRequired, detail.DefaultValue, detail.ValidationRules, detail.Description, detail.SortOrder, detail.IsSensitive, detail.IsReadonly, detail.Creator, ) if err != nil { return err } } // 使用临时表进行批量UPSERT _, err = tx.Exec(` INSERT INTO config_template_detail ( config_template_id, config_key, config_value, value_type, data_type, is_required, default_value, validation_rules, description, sort_order, is_sensitive, is_readonly, creator, created_at, updated_at ) SELECT $1, config_key, config_value, value_type, data_type, is_required, default_value, validation_rules, description, sort_order, is_sensitive, is_readonly, creator, $2, $3 FROM temp_config_details ON CONFLICT (config_template_id, config_key) DO UPDATE SET config_value = EXCLUDED.config_value, value_type = EXCLUDED.value_type, data_type = EXCLUDED.data_type, is_required = EXCLUDED.is_required, default_value = EXCLUDED.default_value, validation_rules = EXCLUDED.validation_rules, description = EXCLUDED.description, sort_order = EXCLUDED.sort_order, is_sensitive = EXCLUDED.is_sensitive, is_readonly = EXCLUDED.is_readonly, creator = EXCLUDED.creator, updated_at = EXCLUDED.updated_at`, templateID, now, now) return err }