326 lines
8.0 KiB
Go
326 lines
8.0 KiB
Go
package services
|
||
|
||
import (
|
||
"context"
|
||
"database/sql"
|
||
"errors"
|
||
"fmt"
|
||
"time"
|
||
"voidraft/internal/models"
|
||
|
||
"github.com/wailsapp/wails/v3/pkg/application"
|
||
"github.com/wailsapp/wails/v3/pkg/services/log"
|
||
)
|
||
|
||
// ThemeService 主题服务
|
||
type ThemeService struct {
|
||
databaseService *DatabaseService
|
||
logger *log.LogService
|
||
ctx context.Context
|
||
}
|
||
|
||
// NewThemeService 创建新的主题服务
|
||
func NewThemeService(databaseService *DatabaseService, logger *log.LogService) *ThemeService {
|
||
if logger == nil {
|
||
logger = log.New()
|
||
}
|
||
|
||
return &ThemeService{
|
||
databaseService: databaseService,
|
||
logger: logger,
|
||
}
|
||
}
|
||
|
||
// ServiceStartup 服务启动时初始化
|
||
func (ts *ThemeService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error {
|
||
ts.ctx = ctx
|
||
|
||
// 初始化默认主题
|
||
return ts.initializeDefaultThemes()
|
||
}
|
||
|
||
// getDB 获取数据库连接
|
||
func (ts *ThemeService) getDB() *sql.DB {
|
||
return ts.databaseService.db
|
||
}
|
||
|
||
// initializeDefaultThemes 初始化所有预设主题
|
||
func (ts *ThemeService) initializeDefaultThemes() error {
|
||
db := ts.getDB()
|
||
if db == nil {
|
||
return fmt.Errorf("database not available")
|
||
}
|
||
|
||
// 获取所有已存在的主题名称
|
||
existingThemes := make(map[string]bool)
|
||
rows, err := db.Query("SELECT name FROM themes")
|
||
if err != nil {
|
||
return fmt.Errorf("failed to query existing themes: %w", err)
|
||
}
|
||
defer rows.Close()
|
||
|
||
for rows.Next() {
|
||
var name string
|
||
if err := rows.Scan(&name); err != nil {
|
||
return fmt.Errorf("failed to scan theme name: %w", err)
|
||
}
|
||
existingThemes[name] = true
|
||
}
|
||
|
||
// 定义所有预设主题配置
|
||
now := time.Now().Format("2006-01-02 15:04:05")
|
||
presetThemes := []struct {
|
||
config *models.ThemeColorConfig
|
||
themeType models.ThemeType
|
||
isDefault bool
|
||
}{
|
||
// 默认主题
|
||
{models.NewDefaultDarkTheme(), models.ThemeTypeDark, true},
|
||
{models.NewDefaultLightTheme(), models.ThemeTypeLight, true},
|
||
|
||
// 深色主题预设
|
||
{models.NewDraculaTheme(), models.ThemeTypeDark, false},
|
||
{models.NewAuraTheme(), models.ThemeTypeDark, false},
|
||
{models.NewGitHubDarkTheme(), models.ThemeTypeDark, false},
|
||
{models.NewMaterialDarkTheme(), models.ThemeTypeDark, false},
|
||
{models.NewOneDarkTheme(), models.ThemeTypeDark, false},
|
||
{models.NewSolarizedDarkTheme(), models.ThemeTypeDark, false},
|
||
{models.NewTokyoNightTheme(), models.ThemeTypeDark, false},
|
||
{models.NewTokyoNightStormTheme(), models.ThemeTypeDark, false},
|
||
|
||
// 浅色主题预设
|
||
{models.NewGitHubLightTheme(), models.ThemeTypeLight, false},
|
||
{models.NewMaterialLightTheme(), models.ThemeTypeLight, false},
|
||
{models.NewSolarizedLightTheme(), models.ThemeTypeLight, false},
|
||
{models.NewTokyoNightDayTheme(), models.ThemeTypeLight, false},
|
||
}
|
||
|
||
// 筛选出需要创建的主题
|
||
var themesToCreate []*models.Theme
|
||
for _, preset := range presetThemes {
|
||
if !existingThemes[preset.config.Name] {
|
||
themesToCreate = append(themesToCreate, &models.Theme{
|
||
Name: preset.config.Name,
|
||
Type: preset.themeType,
|
||
Colors: *preset.config,
|
||
IsDefault: preset.isDefault,
|
||
CreatedAt: now,
|
||
UpdatedAt: now,
|
||
})
|
||
}
|
||
}
|
||
|
||
if len(themesToCreate) == 0 {
|
||
return nil
|
||
}
|
||
|
||
// 批量插入主题
|
||
tx, err := db.Begin()
|
||
if err != nil {
|
||
return fmt.Errorf("failed to begin transaction: %w", err)
|
||
}
|
||
defer tx.Rollback()
|
||
|
||
stmt, err := tx.Prepare(`
|
||
INSERT INTO themes (name, type, colors, is_default, created_at, updated_at)
|
||
VALUES (?, ?, ?, ?, ?, ?)
|
||
`)
|
||
if err != nil {
|
||
return fmt.Errorf("failed to prepare statement: %w", err)
|
||
}
|
||
defer stmt.Close()
|
||
|
||
for _, theme := range themesToCreate {
|
||
_, err := stmt.Exec(
|
||
theme.Name,
|
||
theme.Type,
|
||
theme.Colors,
|
||
theme.IsDefault,
|
||
theme.CreatedAt,
|
||
theme.UpdatedAt,
|
||
)
|
||
if err != nil {
|
||
return fmt.Errorf("failed to insert theme %s: %w", theme.Name, err)
|
||
}
|
||
}
|
||
|
||
if err := tx.Commit(); err != nil {
|
||
return fmt.Errorf("failed to commit transaction: %w", err)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// GetThemeByID 根据ID或名称获取主题
|
||
// 如果 id > 0,按ID查询;如果 id = 0,按名称查询
|
||
func (ts *ThemeService) GetThemeByIdOrName(id int, name ...string) (*models.Theme, error) {
|
||
var query string
|
||
var args []interface{}
|
||
|
||
if id > 0 {
|
||
query = `
|
||
SELECT id, name, type, colors, is_default, created_at, updated_at
|
||
FROM themes
|
||
WHERE id = ?
|
||
LIMIT 1
|
||
`
|
||
args = []interface{}{id}
|
||
} else if len(name) > 0 && name[0] != "" {
|
||
query = `
|
||
SELECT id, name, type, colors, is_default, created_at, updated_at
|
||
FROM themes
|
||
WHERE name = ?
|
||
LIMIT 1
|
||
`
|
||
args = []interface{}{name[0]}
|
||
} else {
|
||
return nil, fmt.Errorf("either id or name must be provided")
|
||
}
|
||
|
||
theme := &models.Theme{}
|
||
db := ts.getDB()
|
||
err := db.QueryRow(query, args...).Scan(
|
||
&theme.ID,
|
||
&theme.Name,
|
||
&theme.Type,
|
||
&theme.Colors,
|
||
&theme.IsDefault,
|
||
&theme.CreatedAt,
|
||
&theme.UpdatedAt,
|
||
)
|
||
|
||
if err != nil {
|
||
if errors.Is(err, sql.ErrNoRows) {
|
||
if id > 0 {
|
||
return nil, fmt.Errorf("theme not found with id: %d", id)
|
||
}
|
||
return nil, fmt.Errorf("theme not found with name: %s", name[0])
|
||
}
|
||
return nil, fmt.Errorf("failed to get theme: %w", err)
|
||
}
|
||
|
||
return theme, nil
|
||
}
|
||
|
||
// UpdateTheme 更新主题
|
||
func (ts *ThemeService) UpdateTheme(id int, colors models.ThemeColorConfig) error {
|
||
query := `
|
||
UPDATE themes
|
||
SET colors = ?, updated_at = ?
|
||
WHERE id = ?
|
||
`
|
||
|
||
db := ts.getDB()
|
||
result, err := db.Exec(query, colors, time.Now().Format("2006-01-02 15:04:05"), id)
|
||
if err != nil {
|
||
return fmt.Errorf("failed to update theme: %w", err)
|
||
}
|
||
|
||
rowsAffected, err := result.RowsAffected()
|
||
if err != nil {
|
||
return fmt.Errorf("failed to get rows affected: %w", err)
|
||
}
|
||
|
||
if rowsAffected == 0 {
|
||
return fmt.Errorf("theme not found with id: %d", id)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// ResetTheme 重置主题为预设配置
|
||
func (ts *ThemeService) ResetTheme(id int, name ...string) error {
|
||
// 先获取主题信息
|
||
theme, err := ts.GetThemeByIdOrName(id, name...)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 根据主题名称获取预设配置
|
||
var presetConfig *models.ThemeColorConfig
|
||
switch theme.Name {
|
||
// 默认主题
|
||
case "default-dark":
|
||
presetConfig = models.NewDefaultDarkTheme()
|
||
case "default-light":
|
||
presetConfig = models.NewDefaultLightTheme()
|
||
|
||
// 深色主题预设
|
||
case "dracula":
|
||
presetConfig = models.NewDraculaTheme()
|
||
case "aura":
|
||
presetConfig = models.NewAuraTheme()
|
||
case "github-dark":
|
||
presetConfig = models.NewGitHubDarkTheme()
|
||
case "material-dark":
|
||
presetConfig = models.NewMaterialDarkTheme()
|
||
case "one-dark":
|
||
presetConfig = models.NewOneDarkTheme()
|
||
case "solarized-dark":
|
||
presetConfig = models.NewSolarizedDarkTheme()
|
||
case "tokyo-night":
|
||
presetConfig = models.NewTokyoNightTheme()
|
||
case "tokyo-night-storm":
|
||
presetConfig = models.NewTokyoNightStormTheme()
|
||
|
||
// 浅色主题预设
|
||
case "github-light":
|
||
presetConfig = models.NewGitHubLightTheme()
|
||
case "material-light":
|
||
presetConfig = models.NewMaterialLightTheme()
|
||
case "solarized-light":
|
||
presetConfig = models.NewSolarizedLightTheme()
|
||
case "tokyo-night-day":
|
||
presetConfig = models.NewTokyoNightDayTheme()
|
||
|
||
default:
|
||
return fmt.Errorf("no preset configuration found for theme: %s", theme.Name)
|
||
}
|
||
|
||
return ts.UpdateTheme(id, *presetConfig)
|
||
}
|
||
|
||
// GetAllThemes 获取所有主题
|
||
func (ts *ThemeService) GetAllThemes() ([]*models.Theme, error) {
|
||
query := `
|
||
SELECT id, name, type, colors, is_default, created_at, updated_at
|
||
FROM themes
|
||
ORDER BY is_default DESC, type DESC, name ASC
|
||
`
|
||
|
||
db := ts.getDB()
|
||
rows, err := db.Query(query)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to query themes: %w", err)
|
||
}
|
||
defer rows.Close()
|
||
|
||
var themes []*models.Theme
|
||
for rows.Next() {
|
||
theme := &models.Theme{}
|
||
err := rows.Scan(
|
||
&theme.ID,
|
||
&theme.Name,
|
||
&theme.Type,
|
||
&theme.Colors,
|
||
&theme.IsDefault,
|
||
&theme.CreatedAt,
|
||
&theme.UpdatedAt,
|
||
)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to scan theme: %w", err)
|
||
}
|
||
themes = append(themes, theme)
|
||
}
|
||
|
||
if err := rows.Err(); err != nil {
|
||
return nil, fmt.Errorf("failed to iterate themes: %w", err)
|
||
}
|
||
|
||
return themes, nil
|
||
}
|
||
|
||
// ServiceShutdown 服务关闭
|
||
func (ts *ThemeService) ServiceShutdown() error {
|
||
return nil
|
||
}
|