// Package crons // @Link https://github.com/bufanyun/hotgo // @Copyright Copyright (c) 2026 HotGo // @Author Ms <133814250@qq.com> // @License https://github.com/bufanyun/hotgo/blob/master/LICENSE package crons import ( "context" "hotgo/internal/consts" "hotgo/internal/dao" "hotgo/internal/library/cron" "hotgo/internal/library/imtenant" "hotgo/internal/model/entity" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/util/gconv" ) func init() { cron.Register(ImFileCleanupOrphan) } // ImFileCleanupOrphan 清理IM孤儿文件,参数:保留天数、单租户处理上限。 var ImFileCleanupOrphan = &cImFileCleanupOrphan{name: "imFileCleanupOrphan"} type cImFileCleanupOrphan struct { name string } func (c *cImFileCleanupOrphan) GetName() string { return c.name } func (c *cImFileCleanupOrphan) Execute(ctx context.Context, parser *cron.Parser) (err error) { days := 7 limit := 100 if len(parser.Args) > 0 { days = gconv.Int(parser.Args[0]) } if len(parser.Args) > 1 { limit = gconv.Int(parser.Args[1]) } if days <= 0 { days = 7 } if limit <= 0 || limit > 1000 { limit = 100 } var tenants []*entity.ImTenant if err = imtenant.RegistryDB().Model(dao.ImTenant.Table()).Safe().Ctx(ctx). Where(dao.ImTenant.Columns().State, consts.ImTenantStateActive). Scan(&tenants); err != nil { parser.Logger.Warningf(ctx, "cron ImFileCleanupOrphan load tenants err:%+v", err) return err } cutoff := gtime.Now().AddDate(0, 0, -days) for _, tenant := range tenants { db, _, dbErr := imtenant.DB(ctx, tenant.Id) if dbErr != nil { parser.Logger.Warningf(ctx, "cron ImFileCleanupOrphan tenant:%d db err:%+v", tenant.Id, dbErr) continue } rows, dbErr := db.Model("im_fileuploads fu").Safe().Ctx(ctx). LeftJoin("im_filemsglinks fml", "fml.fileid=fu.id"). Fields("fu.id"). Where("fml.id IS NULL"). WhereLT("fu.updatedat", cutoff). WhereNotIn("fu.status", []int{consts.ImFileStatusDeleted, consts.ImFileStatusQuarantined}). Group("fu.id"). OrderAsc("fu.updatedat"). Limit(limit). All() if dbErr != nil { parser.Logger.Warningf(ctx, "cron ImFileCleanupOrphan tenant:%d scan err:%+v", tenant.Id, dbErr) continue } if len(rows) == 0 { continue } ids := make([]int64, 0, len(rows)) for _, row := range rows { ids = append(ids, row["id"].Int64()) } if _, dbErr = db.Model("im_fileuploads").Safe().Ctx(ctx). WhereIn("id", ids). Data(g.Map{"status": consts.ImFileStatusDeleted, "updatedat": gtime.Now()}). Update(); dbErr != nil { parser.Logger.Warningf(ctx, "cron ImFileCleanupOrphan tenant:%d update err:%+v", tenant.Id, dbErr) continue } parser.Logger.Infof(ctx, "cron ImFileCleanupOrphan tenant:%d count:%d", tenant.Id, len(ids)) } return nil }