EasyVQD/internal/core/vqdsdk/clean.go
2026-01-17 16:19:36 +08:00

228 lines
6.6 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 vqdsdk
import (
"fmt"
"io/fs"
"log/slog"
"os"
"path/filepath"
"time"
)
// 配置参数
const (
// 要清理的目标目录(请替换为你实际的目录路径)
cleanDir = "/snap"
// 定时任务执行间隔(每天执行一次)
interval = 24 * time.Hour
// 批量删除大小(避免单次删除过多锁表)
batchSize = 1000
// 日期目录的格式(如 20260117
dateDirLayout = "20060102"
// 定时任务首次执行时间每天凌晨1点
executeHour = 1
)
// scheduleCleanFile 定时执行清理任务
func (c Core) scheduleCleanTask() {
// 计算首次执行时间(今天/明天的凌晨1点
now := time.Now()
nextExec := time.Date(now.Year(), now.Month(), now.Day(), executeHour, 0, 0, 0, now.Location())
if nextExec.Before(now) {
nextExec = nextExec.Add(24 * time.Hour)
}
// 计算首次执行的等待时间
initialDelay := nextExec.Sub(now)
slog.Info(fmt.Sprintf("定时任务已启动,首次执行时间:%s等待 %v", nextExec.Format(time.RFC3339), initialDelay))
// 首次执行等待
time.Sleep(initialDelay)
// 执行首次清理
if err := c.cleanExpiredFiles(); err != nil {
slog.Error("首次清理任务执行失败 Files", "err", err)
}
time.Sleep(20 * time.Minute)
if err := c.cleanExpiredDbs(); err != nil {
slog.Error("首次清理任务执行失败 Dbs", "err", err)
}
// 循环执行定时任务
ticker := time.NewTicker(interval)
defer ticker.Stop()
for range ticker.C {
if err := c.cleanExpiredFiles(); err != nil {
slog.Error("定时清理任务执行失败 Files", "err", err)
}
time.Sleep(20 * time.Minute)
if err := c.cleanExpiredDbs(); err != nil {
slog.Error("定时清理任务执行失败 Dbs", "err", err)
}
}
}
// cleanExpiredDbs 清理超过expireDays天的数据
func (c Core) cleanExpiredDbs() error {
// 计算过期时间阈值
expireDays := time.Duration(c.Cfg.VqdConfig.SaveDay)
if expireDays < 1 {
expireDays = 1
}
expireTime := time.Now().Add(-expireDays * 24 * time.Hour)
slog.Info(fmt.Sprintf("开始清理中超过 %d 天的数据,过期时间阈值:%s", expireDays, expireTime.Format(time.RFC3339)))
totalDeleted := 0
for {
deletedCount, err := c.VqdTaskCore.DelVqdTaskAlarmAll(expireTime, batchSize)
if err != nil {
slog.Error("数据清理失败", "err", err)
return err
}
// 获取本次删除的行数
totalDeleted += deletedCount
// 无更多数据则退出循环
if deletedCount < batchSize {
break
}
// 批量删除间隔,降低数据库压力
time.Sleep(100 * time.Millisecond)
}
slog.Info("本次数据清理任务执行完成")
return nil
}
// deleteDirContents 删除目录下的所有文件(保留目录结构,仅删文件)
func deleteTaskDirContents(dir string, expireTime time.Time) error {
// 遍历目录
err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
// 处理遍历过程中的错误(如权限问题)
if err != nil {
slog.Error("访问路径失败", "path", path, "err", err)
return nil
}
// 只处理目录(跳过文件)
if !d.IsDir() {
return nil
}
// 获取当前目录的名称(如 20260117
dirName := filepath.Base(path)
dirDate, err := time.Parse(dateDirLayout, dirName)
if err != nil {
slog.Error("解析目录日期失败", "path", path, "err", err)
return nil
}
// 判断日期目录是否早于阈值(即过期)
isExpired := dirDate.Before(expireTime)
// 非日期目录,继续遍历子目录
if dirDate.IsZero() {
return nil
}
// 日期目录未过期,跳过
if !isExpired {
slog.Error("目录日期未过期跳过", "path", path, "dirDate", dirDate.Format(dateDirLayout))
return nil
}
// 过期日期目录:先删除目录内所有文件
slog.Error("目录已过期开始清理其中文件", "path", path, "dirDate", dirDate.Format(dateDirLayout))
if err := deleteDirContents(path, expireTime); err != nil {
slog.Error("清理目录内容失败", "path", path, "err", err)
return nil
}
// 删除空的日期目录
if err := os.Remove(path); err != nil {
slog.Error("删除空目录失败(可能非空)", "path", path, "err", err)
} else {
slog.Info("成功删除过期目录", "path", path)
}
// 跳过已删除目录的子目录遍历(避免无效操作)
return fs.SkipDir
})
if err != nil {
slog.Error("遍历目录失败", "dir", dir, "err", err)
return fmt.Errorf("遍历目录 [%s] 失败:%w", dir, err)
}
return nil
}
func deleteDirContents(dir string, expireTime time.Time) error {
err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
slog.Error("访问路径失败", "path", path, "err", err)
return nil // 跳过错误路径,继续处理
}
// 只删除文件,跳过目录
if !d.IsDir() {
// 获取文件信息(包含修改时间)
fileInfo, err := d.Info()
if err != nil {
slog.Error("获取文件失败", "path", path, "err", err)
return nil
}
// 判断文件是否过期
if fileInfo.ModTime().Before(expireTime) {
// 删除过期文件
if errs := os.Remove(path); errs != nil {
slog.Error("删除文件失败", "path", path, "err", err)
} else {
slog.Info("成功删除文件", "path", path)
}
}
}
return nil
})
if err != nil {
return fmt.Errorf("遍历目录 [%s] 失败:%w", dir, err)
}
return nil
}
// cleanExpiredFiles 清理指定目录下超过expireDays天未修改的文件
func (c Core) cleanExpiredFiles() error {
// 计算过期时间阈值
expireDays := time.Duration(c.Cfg.VqdConfig.SaveDay)
if expireDays < 1 {
expireDays = 1
}
expireTime := time.Now().Add(-expireDays * 24 * time.Hour)
slog.Info(fmt.Sprintf("开始清理目录 [%s] 中超过 %d 天的文件,过期时间阈值:%s", cleanDir, expireDays, expireTime.Format(time.RFC3339)))
dir, _ := os.Getwd()
rootDir := filepath.Join(dir, cleanDir)
dateDirs, err := os.ReadDir(rootDir)
if err != nil {
slog.Error("访问根目录路径失败", "path", rootDir, "err", err)
return nil
}
for _, d := range dateDirs {
// 只处理目录(跳过文件)
if !d.IsDir() {
return nil
}
path := filepath.Join(rootDir, d.Name())
if err := deleteTaskDirContents(path, expireTime); err != nil {
slog.Error("清理目录内容失败", "path", path, "err", err)
return nil
}
// 删除空的日期目录
if err := os.Remove(path); err != nil {
slog.Error("删除空目录失败(可能非空)", "path", path, "err", err)
} else {
slog.Info("成功删除过期目录", "path", path)
}
}
slog.Info("本次文件清理任务执行完成")
return nil
}