首次推送
build / build-and-test (push) Failing after 1m24s

This commit is contained in:
2026-10-18 19:44:38 +08:00
commit f047662129
64 changed files with 10454 additions and 0 deletions
+113
View File
@@ -0,0 +1,113 @@
package store
import (
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"time"
)
// SetBackupDir 设置备份目录(来自配置文件)。
func (db *DB) SetBackupDir(dir string) { db.BackupPath = dir }
// BackupDir 返回备份目录。
func (db *DB) BackupDir() string {
if db.BackupPath != "" {
return db.BackupPath
}
return filepath.Join(filepath.Dir(db.Path), "backup")
}
// BackupDaily 当天首次调用时生成一份备份;已有今日备份则跳过。
// 空库(还没有客户)不生成备份,避免第一天的备份是空壳。
func (db *DB) BackupDaily(keep int) (string, error) {
dir := db.BackupDir()
if err := os.MkdirAll(dir, 0o755); err != nil {
return "", err
}
now := time.Now()
prefix := "daili-" + now.Format("20060102") + "-"
entries, err := os.ReadDir(dir)
if err != nil {
return "", err
}
for _, e := range entries {
if strings.HasPrefix(e.Name(), prefix) {
return "", nil
}
}
if n, err := db.CustomerCount(); err != nil || n == 0 {
return "", err
}
target := filepath.Join(dir, fmt.Sprintf("daili-%s.db", now.Format("20060102-150405")))
return db.BackupTo(target, keep)
}
// BackupNow 立即生成一份备份。
func (db *DB) BackupNow(keep int) (string, error) {
dir := db.BackupDir()
if err := os.MkdirAll(dir, 0o755); err != nil {
return "", err
}
target := filepath.Join(dir, fmt.Sprintf("daili-%s.db", time.Now().Format("20060102-150405")))
return db.BackupTo(target, keep)
}
// BackupTo 用 VACUUM INTO 生成一致性备份,并按保留份数清理。
func (db *DB) BackupTo(target string, keep int) (string, error) {
abs, err := filepath.Abs(target)
if err != nil {
return "", err
}
if err := os.MkdirAll(filepath.Dir(abs), 0o755); err != nil {
return "", fmt.Errorf("创建备份目录失败: %w", err)
}
if _, err := os.Stat(abs); err == nil {
return abs, fmt.Errorf("备份文件已存在: %s", filepath.Base(abs))
}
escaped := strings.ReplaceAll(strings.ReplaceAll(abs, "\\", "/"), "'", "''")
if _, err := db.sql.Exec("VACUUM INTO '" + escaped + "'"); err != nil {
return "", err
}
if keep > 0 {
_ = db.pruneBackups(keep)
}
return abs, nil
}
// ListBackups 列出备份(新→旧)。
func (db *DB) ListBackups() ([]string, error) {
dir := db.BackupDir()
entries, err := os.ReadDir(dir)
if err != nil {
if os.IsNotExist(err) {
return nil, nil
}
return nil, err
}
var out []string
for _, e := range entries {
if !e.IsDir() && strings.HasSuffix(e.Name(), ".db") {
out = append(out, e.Name())
}
}
sort.Sort(sort.Reverse(sort.StringSlice(out)))
return out, nil
}
func (db *DB) pruneBackups(keep int) error {
list, err := db.ListBackups()
if err != nil {
return err
}
if len(list) <= keep {
return nil
}
dir := db.BackupDir()
for _, name := range list[keep:] {
_ = os.Remove(filepath.Join(dir, name))
}
return nil
}