🎨 Optimize code
This commit is contained in:
@@ -3,29 +3,29 @@ package services
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/services/log"
|
||||
)
|
||||
|
||||
// StoreOption 存储服务配置选项
|
||||
type StoreOption struct {
|
||||
FilePath string // 存储文件路径
|
||||
AutoSave bool // 是否自动保存
|
||||
FilePath string
|
||||
AutoSave bool
|
||||
Logger *log.LoggerService
|
||||
}
|
||||
|
||||
// Store 泛型存储服务
|
||||
type Store[T any] struct {
|
||||
option StoreOption
|
||||
data T
|
||||
dataMap map[string]any
|
||||
unsaved bool
|
||||
lock sync.RWMutex
|
||||
logger *log.LoggerService
|
||||
option StoreOption
|
||||
data atomic.Value // stores T
|
||||
dataMap sync.Map // thread-safe map
|
||||
unsaved atomic.Bool
|
||||
initOnce sync.Once
|
||||
logger *log.LoggerService
|
||||
}
|
||||
|
||||
// NewStore 存储服务
|
||||
@@ -35,127 +35,152 @@ func NewStore[T any](option StoreOption) *Store[T] {
|
||||
logger = log.New()
|
||||
}
|
||||
|
||||
// 确保目录存在
|
||||
if option.FilePath != "" {
|
||||
dir := filepath.Dir(option.FilePath)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
logger.Error("store: Failed to create directory", "path", dir, "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
store := &Store[T]{
|
||||
option: option,
|
||||
dataMap: make(map[string]any),
|
||||
logger: logger,
|
||||
option: option,
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
// 加载数据
|
||||
if err := store.load(); err != nil {
|
||||
logger.Error("store: Failed to load data", "error", err)
|
||||
}
|
||||
// 异步初始化
|
||||
store.initOnce.Do(func() {
|
||||
store.initialize()
|
||||
})
|
||||
|
||||
return store
|
||||
}
|
||||
|
||||
// load 加载数据
|
||||
func (s *Store[T]) load() error {
|
||||
if s.option.FilePath == "" {
|
||||
return fmt.Errorf("store: FilePath not set")
|
||||
}
|
||||
|
||||
// 如果文件不存在
|
||||
if _, err := os.Stat(s.option.FilePath); os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
file, err := os.Open(s.option.FilePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("store: Failed to open file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
bytes, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("store: Failed to read file: %w", err)
|
||||
}
|
||||
|
||||
if len(bytes) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
if err := json.Unmarshal(bytes, &s.data); err != nil {
|
||||
// 尝试加载为map格式
|
||||
if err := json.Unmarshal(bytes, &s.dataMap); err != nil {
|
||||
return fmt.Errorf("store: Failed to parse data: %w", err)
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
// 加载数据
|
||||
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 {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
err := s.saveInternal()
|
||||
if err != nil {
|
||||
s.logger.Error("store: Failed to save", "error", err)
|
||||
return fmt.Errorf("store: Failed to save: %w", err)
|
||||
if !s.unsaved.Load() {
|
||||
return nil // 没有未保存的更改
|
||||
}
|
||||
|
||||
s.unsaved = false
|
||||
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")
|
||||
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("store: Failed to create temp file: %w", err)
|
||||
return fmt.Errorf("failed to create temp file: %w", err)
|
||||
}
|
||||
tempFilePath := tempFile.Name()
|
||||
|
||||
tempPath := tempFile.Name()
|
||||
defer func() {
|
||||
_ = tempFile.Close()
|
||||
// 如果出错,删除临时文件
|
||||
tempFile.Close()
|
||||
if err != nil {
|
||||
_ = os.Remove(tempFilePath)
|
||||
os.Remove(tempPath)
|
||||
}
|
||||
}()
|
||||
|
||||
// 序列化数据
|
||||
bytes, err := json.MarshalIndent(s.data, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("store: Failed to serialize data: %w", err)
|
||||
// 写入数据并同步
|
||||
if _, err = tempFile.Write(data); err != nil {
|
||||
return fmt.Errorf("failed to write data: %w", err)
|
||||
}
|
||||
|
||||
// 写入临时文件
|
||||
if _, err = tempFile.Write(bytes); err != nil {
|
||||
return fmt.Errorf("store: Failed to write temp file: %w", err)
|
||||
}
|
||||
|
||||
// 确保所有数据已写入磁盘
|
||||
if err = tempFile.Sync(); err != nil {
|
||||
return fmt.Errorf("store: Failed to sync file: %w", err)
|
||||
return fmt.Errorf("failed to sync file: %w", err)
|
||||
}
|
||||
|
||||
// 关闭临时文件
|
||||
if err = tempFile.Close(); err != nil {
|
||||
return fmt.Errorf("store: Failed to close temp file: %w", err)
|
||||
return fmt.Errorf("failed to close temp file: %w", err)
|
||||
}
|
||||
|
||||
// 原子替换文件
|
||||
if err = os.Rename(tempFilePath, s.option.FilePath); err != nil {
|
||||
return fmt.Errorf("store: Failed to rename file: %w", err)
|
||||
// 原子替换
|
||||
if err = os.Rename(tempPath, s.option.FilePath); err != nil {
|
||||
return fmt.Errorf("failed to rename file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -163,70 +188,67 @@ func (s *Store[T]) saveInternal() error {
|
||||
|
||||
// Get 获取数据
|
||||
func (s *Store[T]) Get() T {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
return s.data
|
||||
if value := s.data.Load(); value != nil {
|
||||
return value.(T)
|
||||
}
|
||||
var zero T
|
||||
return zero
|
||||
}
|
||||
|
||||
// GetProperty 获取指定属性
|
||||
func (s *Store[T]) GetProperty(key string) any {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
|
||||
if key == "" {
|
||||
return s.dataMap
|
||||
// 返回所有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
|
||||
}
|
||||
return s.dataMap[key]
|
||||
|
||||
if value, ok := s.dataMap.Load(key); ok {
|
||||
return value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set 设置数据
|
||||
func (s *Store[T]) Set(data T) error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
s.data = data
|
||||
s.unsaved = true
|
||||
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.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
s.dataMap[key] = value
|
||||
s.unsaved = true
|
||||
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.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
delete(s.dataMap, key)
|
||||
s.unsaved = true
|
||||
s.dataMap.Delete(key)
|
||||
s.unsaved.Store(true)
|
||||
|
||||
if s.option.AutoSave {
|
||||
return s.saveInternal()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasUnsavedChanges 是否有未保存的更改
|
||||
func (s *Store[T]) HasUnsavedChanges() bool {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
return s.unsaved
|
||||
return s.unsaved.Load()
|
||||
}
|
||||
|
Reference in New Issue
Block a user