package db import ( "bytes" "database/sql" "encoding/gob" "errors" "fmt" "strings" "text/template" ) // 用户校验操作 /* CREATE TABLE IF NOT EXISTS users ( username TEXT PRIMARY KEY, password TEXT NOT NULL, created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) );`) */ // AddUser 向 'users' 表中添加一个新用户. func (cdb *ConfigDB) AddUser(username, password string) error { insertSQL := ` INSERT INTO users (username, password) VALUES (?, ?); ` _, err := cdb.DB.Exec(insertSQL, username, password) if err != nil { return fmt.Errorf("db: failed to add user '%s': %w", username, err) } return nil } // GetUserByUsername 从 'users' 表中根据用户名获取用户信息. func (cdb *ConfigDB) GetUserByUsername(username string) (*UsersTable, error) { querySQL := `SELECT username, password, created_at, updated_at FROM users WHERE username = ?;` row := cdb.DB.QueryRow(querySQL, username) user := &UsersTable{} err := row.Scan(&user.UserName, &user.Password, &user.CreatedAt, &user.UpdatedAt) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, fmt.Errorf("db: user '%s' not found: %w", username, err) } return nil, fmt.Errorf("db: failed to get user '%s': %w", username, err) } return user, nil } // DeleteUser 从 'users' 表中删除一个用户. func (cdb *ConfigDB) DeleteUser(username string) error { _, err := cdb.DB.Exec(`DELETE FROM users WHERE username = ?;`, username) if err != nil { return fmt.Errorf("db: failed to delete user '%s': %w", username, err) } return nil } // UpdateUserPassword 更新用户的密码. func (cdb *ConfigDB) UpdateUserPassword(username, newPassword string) error { updateSQL := ` UPDATE users SET password = ?, updated_at = strftime('%s', 'now') WHERE username = ?; ` result, err := cdb.DB.Exec(updateSQL, newPassword, username) if err != nil { return fmt.Errorf("db: failed to update user '%s' password: %w", username, err) } rowsAffected, err := result.RowsAffected() if err != nil { return fmt.Errorf("db: failed to get rows affected by update: %w", err) } if rowsAffected == 0 { return fmt.Errorf("db: no user with username '%s' found to update", username) } return nil } // RangeUserNames 获取所有用户的用户名. func (cdb *ConfigDB) RangeUserNames() ([]string, error) { querySQL := `SELECT username FROM users;` rows, err := cdb.DB.Query(querySQL) if err != nil { return nil, fmt.Errorf("db: failed to get usernames from users: %w", err) } defer rows.Close() var usernames []string for rows.Next() { var username string if err := rows.Scan(&username); err != nil { return nil, fmt.Errorf("db: failed to scan username: %w", err) } usernames = append(usernames, username) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("db: error during user rows iteration: %w", err) } return usernames, nil } // HasAnyUser 检查 'users' 表中是否存在任何用户. func (cdb *ConfigDB) HasAnyUser() (bool, error) { querySQL := `SELECT EXISTS(SELECT 1 FROM users LIMIT 1);` var exists bool err := cdb.DB.QueryRow(querySQL).Scan(&exists) if err != nil { return false, fmt.Errorf("db: failed to check if any user exists: %w", err) } return exists, nil } // IsUserExists 检查指定用户名的用户是否存在. func (cdb *ConfigDB) IsUserExists(username string) (bool, error) { querySQL := `SELECT EXISTS(SELECT 1 FROM users WHERE username = ? LIMIT 1);` var exists bool err := cdb.DB.QueryRow(querySQL, username).Scan(&exists) if err != nil { return false, fmt.Errorf("db: failed to check if user '%s' exists: %w", username, err) } return exists, nil } // GetPasswordByUsername 从 'users' 表中根据用户名获取密码. func (cdb *ConfigDB) GetPasswordByUsername(username string) (string, error) { querySQL := `SELECT password FROM users WHERE username = ?;` var password string err := cdb.DB.QueryRow(querySQL, username).Scan(&password) if err != nil { if errors.Is(err, sql.ErrNoRows) { return "", fmt.Errorf("db: user '%s' not found: %w", username, err) } return "", fmt.Errorf("db: failed to get password for user '%s': %w", username, err) } return password, nil } // --- 模板操作 (Templates Table) --- // SaveTemplate 在 'templates' 表中保存或更新一个模板. func (cdb *ConfigDB) SaveTemplate(entry TemplateEntry) error { insertSQL := ` INSERT INTO templates (filename, template_type, content) VALUES (?, ?, ?) ON CONFLICT(filename) DO UPDATE SET template_type = EXCLUDED.template_type, content = EXCLUDED.content, updated_at = strftime('%s', 'now'); ` _, err := cdb.DB.Exec(insertSQL, entry.Filename, entry.TemplateType, entry.Content) if err != nil { return fmt.Errorf("db: failed to save template '%s': %w", entry.Filename, err) } return nil } // GetTemplate 从 'templates' 表中获取一个模板内容. func (cdb *ConfigDB) GetTemplate(filename string) (*TemplateEntry, error) { querySQL := `SELECT filename, template_type, content, created_at, updated_at FROM templates WHERE filename = ?;` row := cdb.DB.QueryRow(querySQL, filename) entry := &TemplateEntry{} err := row.Scan(&entry.Filename, &entry.TemplateType, &entry.Content, &entry.CreatedAt, &entry.UpdatedAt) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, fmt.Errorf("db: template '%s' not found: %w", filename, err) } return nil, fmt.Errorf("db: failed to get template '%s': %w", filename, err) } return entry, nil } // DeleteTemplate 从 'templates' 表中删除一个模板. // 请注意: 此操作不会级联删除 'config_params' 或 'rendered_configs' 中的关联数据; // 因为 'templates' 表不再是它们的外键父表. func (cdb *ConfigDB) DeleteTemplate(filename string) error { _, err := cdb.DB.Exec(`DELETE FROM templates WHERE filename = ?;`, filename) if err != nil { return fmt.Errorf("db: failed to delete template '%s': %w", filename, err) } return nil } // RangeTempaltes 获取所有模板的名称 func (cdb *ConfigDB) RangeTemplates() ([]string, error) { querySQL := `SELECT filename FROM templates;` rows, err := cdb.DB.Query(querySQL) if err != nil { return nil, fmt.Errorf("db: failed to get filenames from templates: %w", err) } defer rows.Close() var filenames []string for rows.Next() { var filename string if err := rows.Scan(&filename); err != nil { return nil, fmt.Errorf("db: failed to scan template filename: %w", err) } filenames = append(filenames, filename) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("db: error during template rows iteration: %w", err) } return filenames, nil } // GetAllTempaltes func (cdb *ConfigDB) GetAllTemplates() ([]TemplateEntry, error) { querySQL := `SELECT filename, template_type, content, created_at, updated_at FROM templates;` rows, err := cdb.DB.Query(querySQL) if err != nil { return nil, fmt.Errorf("db: failed to get all templates: %w", err) } defer rows.Close() var templates []TemplateEntry for rows.Next() { var entry TemplateEntry if err := rows.Scan(&entry.Filename, &entry.TemplateType, &entry.Content, &entry.CreatedAt, &entry.UpdatedAt); err != nil { return nil, fmt.Errorf("db: failed to scan template entry: %w", err) } templates = append(templates, entry) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("db: error during templates rows iteration: %w", err) } return templates, nil } // --- 参数操作 (Config_Params Table) --- /* filename TEXT PRIMARY KEY, template_type TEXT NOT NULL, params_gob BLOB NOT NULL, params_origin BLOB NOT NULL, created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) */ // SaveParams 在 'config_params' 表中保存或更新一个模板的参数. // entry.ParamsGOB 应该是一个经过 GOB 编码的字节切片. func (cdb *ConfigDB) SaveParams(entry ParamsEntry) error { insertSQL := ` INSERT INTO config_params (filename, template_type, params_gob, params_origin) VALUES (?, ?, ?, ?) ON CONFLICT(filename) DO UPDATE SET template_type = EXCLUDED.template_type, params_gob = EXCLUDED.params_gob, params_origin = EXCLUDED.params_origin, updated_at = strftime('%s', 'now'); ` _, err := cdb.DB.Exec(insertSQL, entry.Filename, entry.TemplateType, entry.ParamsGOB, entry.ParamsOrigin) if err != nil { return fmt.Errorf("db: failed to save params for '%s': %w", entry.Filename, err) } return nil } // GetParams 从 'config_params' 表中获取一个模板的参数. // 返回的 ParamsGOB 是 GOB 编码的字节切片; 调用方需要自行解码. func (cdb *ConfigDB) GetParams(filename string) (*ParamsEntry, error) { querySQL := `SELECT filename, template_type, params_gob, params_origin, created_at, updated_at FROM config_params WHERE filename = ?;` row := cdb.DB.QueryRow(querySQL, filename) entry := &ParamsEntry{} err := row.Scan(&entry.Filename, &entry.TemplateType, &entry.ParamsGOB, &entry.ParamsOrigin, &entry.CreatedAt, &entry.UpdatedAt) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, fmt.Errorf("db: params for '%s' not found: %w", filename, err) } return nil, fmt.Errorf("db: failed to get params for '%s': %w", filename, err) } return entry, nil } // DeleteParams 从 'config_params' 表中删除一个模板的参数. // 此操作将级联删除 'rendered_configs' 表中与该 filename 关联的所有渲染产物. func (cdb *ConfigDB) DeleteParams(filename string) error { _, err := cdb.DB.Exec(`DELETE FROM config_params WHERE filename = ?;`, filename) if err != nil { return fmt.Errorf("db: failed to delete params for '%s': %w", filename, err) } return nil } // GetFileNames func (cdb *ConfigDB) GetFileNames() ([]string, error) { querySQL := `SELECT filename FROM config_params;` rows, err := cdb.DB.Query(querySQL) if err != nil { return nil, fmt.Errorf("db: failed to get filenames from config_params: %w", err) } defer rows.Close() var filenames []string for rows.Next() { var filename string if err := rows.Scan(&filename); err != nil { return nil, fmt.Errorf("db: failed to scan filename: %w", err) } filenames = append(filenames, filename) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("db: error during rows iteration: %w", err) } return filenames, nil } // RangeAllParams func (cdb *ConfigDB) RangeAllParams() ([]ParamsEntry, error) { querySQL := `SELECT filename, template_type, params_gob, params_origin, created_at, updated_at FROM config_params;` rows, err := cdb.DB.Query(querySQL) if err != nil { return nil, fmt.Errorf("db: failed to get all params: %w", err) } defer rows.Close() var params []ParamsEntry for rows.Next() { var entry ParamsEntry if err := rows.Scan(&entry.Filename, &entry.TemplateType, &entry.ParamsGOB, &entry.CreatedAt, &entry.UpdatedAt); err != nil { return nil, fmt.Errorf("db: failed to scan params entry: %w", err) } params = append(params, entry) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("db: error during params rows iteration: %w", err) } return params, nil } // --- 渲染产物操作 (Rendered_Configs Table) --- // SaveRenderedConfig 在 'rendered_configs' 表中保存或更新一个渲染后的配置文件内容. // 注意: 该操作依赖于 'config_params' 表中已存在对应的 filename; 否则会违反外键约束. func (cdb *ConfigDB) SaveRenderedConfig(entry RenderedConfigEntry) error { insertSQL := ` INSERT INTO rendered_configs (filename, rendered_content, rendered_at) VALUES (?, ?, strftime('%s', 'now')) ON CONFLICT(filename) DO UPDATE SET rendered_content = EXCLUDED.rendered_content, rendered_at = strftime('%s', 'now'), updated_at = strftime('%s', 'now'); ` _, err := cdb.DB.Exec(insertSQL, entry.Filename, entry.RenderedContent) if err != nil { return fmt.Errorf("db: failed to save rendered config for '%s': %w", entry.Filename, err) } return nil } // GetRenderedConfig 从 'rendered_configs' 表中获取一个渲染后的配置文件内容. func (cdb *ConfigDB) GetRenderedConfig(filename string) (*RenderedConfigEntry, error) { querySQL := `SELECT filename, rendered_content, rendered_at, updated_at FROM rendered_configs WHERE filename = ?;` row := cdb.DB.QueryRow(querySQL, filename) entry := &RenderedConfigEntry{} err := row.Scan(&entry.Filename, &entry.RenderedContent, &entry.RenderedAt, &entry.UpdatedAt) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, fmt.Errorf("db: rendered config '%s' not found: %w", filename, err) } return nil, fmt.Errorf("db: failed to get rendered config '%s': %w", filename, err) } return entry, nil } // DeleteRenderedConfig 从 'rendered_configs' 表中删除一个渲染后的配置文件内容. func (cdb *ConfigDB) DeleteRenderedConfig(filename string) error { _, err := cdb.DB.Exec(`DELETE FROM rendered_configs WHERE filename = ?;`, filename) if err != nil { return fmt.Errorf("db: failed to delete rendered config for '%s': %w", filename, err) } return nil } // RangeAllReandered func (cdb *ConfigDB) RangeAllReandered() ([]RenderedConfigEntry, error) { querySQL := `SELECT filename, rendered_content, rendered_at, updated_at FROM rendered_configs;` rows, err := cdb.DB.Query(querySQL) if err != nil { return nil, fmt.Errorf("db: failed to get all rendered configs: %w", err) } defer rows.Close() var renderedConfigs []RenderedConfigEntry for rows.Next() { var entry RenderedConfigEntry if err := rows.Scan(&entry.Filename, &entry.RenderedContent, &entry.RenderedAt, &entry.UpdatedAt); err != nil { return nil, fmt.Errorf("db: failed to scan rendered config entry: %w", err) } renderedConfigs = append(renderedConfigs, entry) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("db: error during rendered configs rows iteration: %w", err) } return renderedConfigs, nil } // --- GOB 编码/解码辅助函数 --- // gobEncode 将 Go 值编码为 GOB 格式的字节切片. // 'data' 必须是可被 GOB 编码的值; 例如基本类型, 切片, 映射, 结构体. func gobEncode(data interface{}) ([]byte, error) { var buf bytes.Buffer enc := gob.NewEncoder(&buf) if err := enc.Encode(data); err != nil { return nil, fmt.Errorf("db: failed to GOB encode data: %w", err) } return buf.Bytes(), nil } // gobDecode 将 GOB 格式的字节切片解码到 Go 值. // 'data' 是 GOB 编码的字节切片. // 'valuePtr' 必须是指向目标 Go 值的指针; 其类型必须与编码时的数据类型兼容. func gobDecode(data []byte, valuePtr interface{}) error { buf := bytes.NewBuffer(data) dec := gob.NewDecoder(buf) if err := dec.Decode(valuePtr); err != nil { return fmt.Errorf("db: failed to GOB decode data: %w", err) } return nil } // --- 业务逻辑: 渲染并保存 (由使用者调用) --- // RenderAndSaveConfig 从数据库获取模板和参数; 渲染后保存到 'rendered_configs' 表. // 这是一个组合操作; 通常由应用程序逻辑在需要时调用. // filename: 要渲染的配置文件的唯一标识. // templateParser: 一个实现了 TemplateParser 接口的模板解析器实例. // dynamicParams: 运行时动态提供的参数; 它们会覆盖存储在数据库中的同名参数. func (cdb *ConfigDB) RenderAndSaveConfig(filename string, dynamicParams map[string]interface{}) error { // 1. 获取模板内容. tmplEntry, err := cdb.GetTemplate(filename) if err != nil { return fmt.Errorf("db: failed to get template '%s' for rendering: %w", filename, err) } // 2. 获取存储的参数. paramsEntry, err := cdb.GetParams(filename) var storedParams map[string]interface{} if err != nil { if errors.Is(err, sql.ErrNoRows) { storedParams = make(map[string]interface{}) // 参数不存在; 使用空 map. } else { return fmt.Errorf("db: failed to get parameters for '%s': %w", filename, err) } } else { // 解码 GOB 参数到 map. if err := gobDecode(paramsEntry.ParamsGOB, &storedParams); err != nil { return fmt.Errorf("db: failed to decode stored parameters for '%s': %w", filename, err) } } // 合并参数: 动态传入的参数覆盖存储的参数. if dynamicParams != nil { for k, v := range dynamicParams { storedParams[k] = v } } // 3. 渲染模板. var parsedTmpl *template.Template var parseErr error // 使用传入的 templateParser 实例来解析模板内容. // 注意: templateParser.Parse 是在提供的实例上调用; 以解析特定内容. //parsedTmpl, parseErr = templateParser.Parse(string(tmplEntry.Content)) parsedTmpl, parseErr = template.New(tmplEntry.Filename).Parse(string(tmplEntry.Content)) if parseErr != nil { return fmt.Errorf("db: failed to parse template content for '%s': %w", tmplEntry.Filename, parseErr) } var renderedContentBuilder strings.Builder if err := parsedTmpl.Execute(&renderedContentBuilder, storedParams); err != nil { return fmt.Errorf("db: failed to render template '%s': %w", tmplEntry.Filename, err) } // 4. 保存渲染结果. renderedEntry := RenderedConfigEntry{ Filename: filename, RenderedContent: []byte(renderedContentBuilder.String()), } if err := cdb.SaveRenderedConfig(renderedEntry); err != nil { return fmt.Errorf("db: failed to save rendered config for '%s': %w", filename, err) } return nil }