228 lines
6.6 KiB
Go
228 lines
6.6 KiB
Go
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
|
||
}
|