137 lines
3.5 KiB
Go
137 lines
3.5 KiB
Go
// 将 data/*.json(站点与分组)导入 MySQL。建议空库首次使用,或加 -wipe 清空业务表后再导。
|
||
//
|
||
// go run ./cmd/importjson -data ./data -wipe
|
||
package main
|
||
|
||
import (
|
||
"encoding/json"
|
||
"flag"
|
||
"fmt"
|
||
"log"
|
||
"os"
|
||
"path/filepath"
|
||
|
||
"gorm.io/datatypes"
|
||
"gorm.io/driver/mysql"
|
||
"gorm.io/gorm"
|
||
"gorm.io/gorm/logger"
|
||
|
||
"mengyaping-backend/config"
|
||
"mengyaping-backend/models"
|
||
"mengyaping-backend/storage"
|
||
)
|
||
|
||
func main() {
|
||
dataDir := flag.String("data", "./data", "JSON 目录(websites.json、groups.json)")
|
||
wipe := flag.Bool("wipe", false, "导入前 TRUNCATE 所有 monitor_* 表(慎用)")
|
||
flag.Parse()
|
||
|
||
cfg := config.GetConfig()
|
||
// 大批量 INSERT 时单条超过 200ms 会触发 GORM 默认「SLOW SQL」提示,易被误认为报错;导入工具改为静默 SQL 日志
|
||
db, err := gorm.Open(mysql.Open(cfg.DatabaseDSN()), &gorm.Config{
|
||
Logger: logger.Default.LogMode(logger.Silent),
|
||
})
|
||
if err != nil {
|
||
log.Fatal(err)
|
||
}
|
||
|
||
if err := db.AutoMigrate(
|
||
&storage.MonitorGroup{},
|
||
&storage.MonitorWebsite{},
|
||
&storage.MonitorWebsiteURL{},
|
||
&storage.MonitorWebsiteGroup{},
|
||
&storage.MonitorProbeLatest{},
|
||
&storage.MonitorProbeHour{},
|
||
&storage.MonitorProbeDay{},
|
||
&storage.MonitorKV{},
|
||
); err != nil {
|
||
log.Fatal(err)
|
||
}
|
||
|
||
if *wipe {
|
||
_ = db.Exec("SET FOREIGN_KEY_CHECKS = 0").Error
|
||
for _, tbl := range []string{
|
||
"monitor_probe_latest",
|
||
"monitor_probe_hour",
|
||
"monitor_probe_day",
|
||
"monitor_kv",
|
||
"monitor_checks",
|
||
"monitor_website_urls",
|
||
"monitor_website_groups",
|
||
"monitor_websites",
|
||
"monitor_groups",
|
||
} {
|
||
_ = db.Exec("TRUNCATE TABLE " + tbl).Error
|
||
}
|
||
_ = db.Exec("SET FOREIGN_KEY_CHECKS = 1").Error
|
||
log.Println("已清空 monitor_* 表")
|
||
}
|
||
|
||
websitesPath := filepath.Join(*dataDir, "websites.json")
|
||
raw, err := os.ReadFile(websitesPath)
|
||
if err != nil {
|
||
log.Fatalf("读取 websites.json: %v", err)
|
||
}
|
||
var websites []models.Website
|
||
if err := json.Unmarshal(raw, &websites); err != nil {
|
||
log.Fatal(err)
|
||
}
|
||
|
||
groupsPath := filepath.Join(*dataDir, "groups.json")
|
||
var fileGroups []models.Group
|
||
if g, err := os.ReadFile(groupsPath); err == nil {
|
||
_ = json.Unmarshal(g, &fileGroups)
|
||
}
|
||
if len(fileGroups) == 0 {
|
||
fileGroups = models.DefaultGroups
|
||
}
|
||
for i, g := range fileGroups {
|
||
if err := db.Create(&storage.MonitorGroup{
|
||
ID: g.ID, Name: g.Name, SortOrder: i + 1,
|
||
}).Error; err != nil {
|
||
log.Printf("导入分组 %s: %v", g.ID, err)
|
||
}
|
||
}
|
||
|
||
for _, w := range websites {
|
||
if len(w.Groups) == 0 && w.Group != "" {
|
||
w.Groups = []string{w.Group}
|
||
}
|
||
row := &storage.MonitorWebsite{
|
||
ID: w.ID,
|
||
Name: w.Name,
|
||
LegacyGroup: "",
|
||
Favicon: w.Favicon,
|
||
Title: w.Title,
|
||
IPAddresses: datatypes.JSON(mustJSON(w.IPAddresses)),
|
||
CreatedAt: w.CreatedAt,
|
||
UpdatedAt: w.UpdatedAt,
|
||
}
|
||
if err := db.Create(row).Error; err != nil {
|
||
log.Printf("网站 %s: %v", w.ID, err)
|
||
continue
|
||
}
|
||
for i, u := range w.URLs {
|
||
_ = db.Create(&storage.MonitorWebsiteURL{
|
||
WebsiteID: w.ID, URLID: u.ID, URL: u.URL, Remark: u.Remark, SortOrder: i,
|
||
})
|
||
}
|
||
for _, gid := range w.Groups {
|
||
_ = db.Create(&storage.MonitorWebsiteGroup{WebsiteID: w.ID, GroupID: gid})
|
||
}
|
||
}
|
||
fmt.Printf("已导入 %d 个网站\n", len(websites))
|
||
log.Println("探测历史由运行时写入 monitor_probe_* 汇总表,不再从 records.json 导入")
|
||
}
|
||
|
||
func mustJSON(v []string) []byte {
|
||
if v == nil {
|
||
return []byte("[]")
|
||
}
|
||
b, err := json.Marshal(v)
|
||
if err != nil {
|
||
return []byte("[]")
|
||
}
|
||
return b
|
||
}
|