|
@@ -10,32 +10,43 @@ import (
|
|
|
"server/dao"
|
|
|
"server/logger"
|
|
|
"strings"
|
|
|
+ "sync"
|
|
|
"time"
|
|
|
)
|
|
|
|
|
|
const dataFilePath = "static/data.json"
|
|
|
|
|
|
-
|
|
|
-func LoadData() ([]dao.Region, error) {
|
|
|
+var InitRegionData []dao.Region
|
|
|
+
|
|
|
+
|
|
|
+func LoadData1() {
|
|
|
var regions []dao.Region
|
|
|
|
|
|
|
|
|
data, err := os.ReadFile(dataFilePath)
|
|
|
if err != nil {
|
|
|
if !os.IsNotExist(err) {
|
|
|
- return nil, fmt.Errorf("读取文件失败: %v", err)
|
|
|
+ logger.Get().Errorf("读取文件失败: %v", err)
|
|
|
+ return
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if len(data) > 0 {
|
|
|
if err := json.Unmarshal(data, ®ions); err != nil {
|
|
|
- return nil, fmt.Errorf("解析 JSON 失败 (主文件): %v, 原始数据: %s", err, string(data))
|
|
|
+ logger.Get().Errorf("解析 JSON 失败 (主文件): %v, 原始数据: %s", err, string(data))
|
|
|
+ return
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return regions, nil
|
|
|
+ InitRegionData = regions
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func LoadData() ([]dao.Region, error) {
|
|
|
+
|
|
|
+ return InitRegionData, nil
|
|
|
}
|
|
|
|
|
|
func GetOnlineDevices() (devices []dao.Device, err error) {
|
|
@@ -103,6 +114,46 @@ func SaveRegionOnData(data dao.Region) ([]dao.Region, error) {
|
|
|
}
|
|
|
|
|
|
return regions, nil
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+var s sync.Mutex
|
|
|
+
|
|
|
+func SaveRegions(regions []dao.Region) {
|
|
|
+ s.Lock()
|
|
|
+
|
|
|
+ regionMap := make(map[string]dao.Region)
|
|
|
+ orderSlice := make([]string, 0, len(InitRegionData))
|
|
|
+ for _, region := range InitRegionData {
|
|
|
+ regionMap[region.Name] = region
|
|
|
+ orderSlice = append(orderSlice, region.Name)
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ for _, newRegion := range regions {
|
|
|
+ existingRegion, exists := regionMap[newRegion.Name]
|
|
|
+ if exists {
|
|
|
+ if existingRegion.Version != newRegion.Version {
|
|
|
+ logger.Get().Errorf("版本冲突: 区域 '%s' 的当前版本=%d, 提交版本=%d", newRegion.Name, existingRegion.Version, newRegion.Version)
|
|
|
+ }
|
|
|
+
|
|
|
+ newRegion.Version++
|
|
|
+ regionMap[newRegion.Name] = newRegion
|
|
|
+ } else {
|
|
|
+
|
|
|
+ newRegion.Version = 1
|
|
|
+ regionMap[newRegion.Name] = newRegion
|
|
|
+ orderSlice = append(orderSlice, newRegion.Name)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ updatedRegions := make([]dao.Region, 0, len(regionMap))
|
|
|
+ for _, name := range orderSlice {
|
|
|
+ updatedRegions = append(updatedRegions, regionMap[name])
|
|
|
+ }
|
|
|
+ InitRegionData = updatedRegions
|
|
|
+ s.Unlock()
|
|
|
}
|
|
|
|
|
|
|
|
@@ -175,7 +226,20 @@ func SaveData(path string, newData []dao.Region) error {
|
|
|
return fmt.Errorf("关闭临时文件失败: %v", err)
|
|
|
}
|
|
|
|
|
|
-
|
|
|
+
|
|
|
+ if _, err := os.Stat(path); err == nil {
|
|
|
+
|
|
|
+ if writable, err := checkWritable(path); err != nil || !writable {
|
|
|
+ return fmt.Errorf("目标文件不可写: %v", err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if err = os.Remove(path); err != nil {
|
|
|
+ if !os.IsNotExist(err) {
|
|
|
+ return fmt.Errorf("删除目标文件失败: %w", err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
if err := os.Rename(tempFile.Name(), path); err != nil {
|
|
|
return fmt.Errorf("替换文件失败: %v", err)
|
|
|
}
|
|
@@ -183,6 +247,16 @@ func SaveData(path string, newData []dao.Region) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+func checkWritable(filename string) (bool, error) {
|
|
|
+ file, err := os.OpenFile(filename, os.O_WRONLY, 0)
|
|
|
+ if err != nil {
|
|
|
+ return false, err
|
|
|
+ }
|
|
|
+ file.Close()
|
|
|
+ return true, nil
|
|
|
+}
|
|
|
+
|
|
|
const (
|
|
|
maxRetries = 3
|
|
|
readTimeout = 5 * time.Second
|