Files
voidraft/internal/services/store_service.go

255 lines
4.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package services
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"sync"
"sync/atomic"
"github.com/wailsapp/wails/v3/pkg/services/log"
)
// StoreOption 存储服务配置选项
type StoreOption struct {
FilePath string
AutoSave bool
Logger *log.Service
}
// Store 泛型存储服务
type Store[T any] struct {
option StoreOption
data atomic.Value // stores T
dataMap sync.Map // thread-safe map
unsaved atomic.Bool
initOnce sync.Once
logger *log.Service
}
// NewStore 存储服务
func NewStore[T any](option StoreOption) *Store[T] {
logger := option.Logger
if logger == nil {
logger = log.New()
}
store := &Store[T]{
option: option,
logger: logger,
}
// 异步初始化
store.initOnce.Do(func() {
store.initialize()
})
return store
}
// initialize 初始化存储
func (s *Store[T]) initialize() {
// 确保目录存在
if s.option.FilePath != "" {
if err := os.MkdirAll(filepath.Dir(s.option.FilePath), 0755); err != nil {
s.logger.Error("store: failed to create directory", "error", err)
return
}
}
// 加载数据
s.load()
}
// load 加载数据
func (s *Store[T]) load() {
if s.option.FilePath == "" {
return
}
// 检查文件是否存在
if _, err := os.Stat(s.option.FilePath); os.IsNotExist(err) {
return
}
data, err := os.ReadFile(s.option.FilePath)
if err != nil {
s.logger.Error("store: failed to read file", "error", err)
return
}
if len(data) == 0 {
return
}
var value T
if err := json.Unmarshal(data, &value); err != nil {
// 尝试加载为map格式
var mapData map[string]any
if err := json.Unmarshal(data, &mapData); err != nil {
s.logger.Error("store: failed to parse data", "error", err)
return
}
// 将map数据存储到sync.Map中
for k, v := range mapData {
s.dataMap.Store(k, v)
}
return
}
s.data.Store(value)
}
// Save 保存数据
func (s *Store[T]) Save() error {
if !s.unsaved.Load() {
return nil // 没有未保存的更改
}
if err := s.saveInternal(); err != nil {
return fmt.Errorf("store: failed to save: %w", err)
}
s.unsaved.Store(false)
return nil
}
// saveInternal 内部保存实现
func (s *Store[T]) saveInternal() error {
if s.option.FilePath == "" {
return fmt.Errorf("store: filepath not set")
}
// 获取要保存的数据
var data []byte
var err error
if value := s.data.Load(); value != nil {
data, err = json.MarshalIndent(value, "", " ")
} else {
// 如果没有结构化数据保存map数据
mapData := make(map[string]any)
s.dataMap.Range(func(key, value any) bool {
if k, ok := key.(string); ok {
mapData[k] = value
}
return true
})
data, err = json.MarshalIndent(mapData, "", " ")
}
if err != nil {
return fmt.Errorf("failed to serialize data: %w", err)
}
// 原子写入
return s.atomicWrite(data)
}
// atomicWrite 原子写入文件
func (s *Store[T]) atomicWrite(data []byte) error {
dir := filepath.Dir(s.option.FilePath)
// 创建临时文件
tempFile, err := os.CreateTemp(dir, "store-*.tmp")
if err != nil {
return fmt.Errorf("failed to create temp file: %w", err)
}
tempPath := tempFile.Name()
defer func() {
tempFile.Close()
if err != nil {
os.Remove(tempPath)
}
}()
// 写入数据并同步
if _, err = tempFile.Write(data); err != nil {
return fmt.Errorf("failed to write data: %w", err)
}
if err = tempFile.Sync(); err != nil {
return fmt.Errorf("failed to sync file: %w", err)
}
if err = tempFile.Close(); err != nil {
return fmt.Errorf("failed to close temp file: %w", err)
}
// 原子替换
if err = os.Rename(tempPath, s.option.FilePath); err != nil {
return fmt.Errorf("failed to rename file: %w", err)
}
return nil
}
// Get 获取数据
func (s *Store[T]) Get() T {
if value := s.data.Load(); value != nil {
return value.(T)
}
var zero T
return zero
}
// GetProperty 获取指定属性
func (s *Store[T]) GetProperty(key string) any {
if key == "" {
// 返回所有map数据
result := make(map[string]any)
s.dataMap.Range(func(k, v any) bool {
if str, ok := k.(string); ok {
result[str] = v
}
return true
})
return result
}
if value, ok := s.dataMap.Load(key); ok {
return value
}
return nil
}
// Set 设置数据
func (s *Store[T]) Set(data T) error {
s.data.Store(data)
s.unsaved.Store(true)
if s.option.AutoSave {
return s.saveInternal()
}
return nil
}
// SetProperty 设置指定属性
func (s *Store[T]) SetProperty(key string, value any) error {
s.dataMap.Store(key, value)
s.unsaved.Store(true)
if s.option.AutoSave {
return s.saveInternal()
}
return nil
}
// Delete 删除指定属性
func (s *Store[T]) Delete(key string) error {
s.dataMap.Delete(key)
s.unsaved.Store(true)
if s.option.AutoSave {
return s.saveInternal()
}
return nil
}
// HasUnsavedChanges 是否有未保存的更改
func (s *Store[T]) HasUnsavedChanges() bool {
return s.unsaved.Load()
}