package sys import ( "context" "hotgo/internal/consts" "hotgo/internal/dao" "hotgo/internal/library/imtenant" "hotgo/internal/model/entity" "hotgo/internal/model/input/sysin" "hotgo/internal/service" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/os/gtime" ) type sSysImTaskDashboard struct{} func NewSysImTaskDashboard() *sSysImTaskDashboard { return &sSysImTaskDashboard{} } func init() { service.RegisterSysImTaskDashboard(NewSysImTaskDashboard()) } func (s *sSysImTaskDashboard) List(ctx context.Context, in *sysin.ImTaskDashboardListInp) (list []*sysin.ImTaskDashboardListModel, totalCount int, err error) { if err = in.Filter(ctx); err != nil { return } scope, err := resolveImAdminScopeWithAll(ctx, in.TenantId, true) if err != nil { return } mod := imtenant.RegistryDB().Model(dao.ImTenant.Table()).Safe().Ctx(ctx) if scope.TenantId > 0 { mod = mod.WherePri(scope.TenantId) } if in.TenantId > 0 { mod = mod.WherePri(in.TenantId) } mod = mod.Page(in.Page, in.PerPage).OrderAsc(dao.ImTenant.Columns().Id) var tenants []*entity.ImTenant if err = mod.ScanAndCount(&tenants, &totalCount, false); err != nil { err = gerror.Wrap(err, "获取IM任务看板租户失败,请稍后重试!") return } list = make([]*sysin.ImTaskDashboardListModel, 0, len(tenants)) for _, tenant := range tenants { item := s.buildTenantSummary(ctx, tenant) if in.Status != "" && item.Status != in.Status { continue } list = append(list, item) } if in.Status != "" { totalCount = len(list) } return } func (s *sSysImTaskDashboard) buildTenantSummary(ctx context.Context, tenant *entity.ImTenant) *sysin.ImTaskDashboardListModel { item := &sysin.ImTaskDashboardListModel{ TenantId: tenant.Id, TenantName: tenant.Name, TenantCode: tenant.Code, TenantState: tenant.State, DbStatus: "ok", Status: consts.ImTaskDashboardStatusHealthy, RecommendedNextAction: "无需处理", } db, _, err := imtenant.DB(ctx, tenant.Id) if err != nil { item.DbStatus = "error" item.Status = consts.ImTaskDashboardStatusError item.RecommendedNextAction = "检查租户数据库连接" return item } item.OutboxPendingCount, _ = s.count(ctx, db, "im_business_events_outbox", "status='pending'") item.OutboxFailedCount, _ = s.count(ctx, db, "im_business_events_outbox", "status='failed'") item.GroupOutboxPending, _ = s.count(ctx, db, "im_group_events_outbox", "status='pending'") item.GroupOutboxFailed, _ = s.count(ctx, db, "im_group_events_outbox", "status='failed'") item.OrphanFileCount, _ = s.countOrphanFiles(ctx, db) item.DeletedFileCount, _ = s.count(ctx, db, "im_fileuploads", "status=3") item.LastOutboxCreatedAt, _ = s.maxTime(ctx, db, "im_business_events_outbox", "created_at") item.LastGroupEventAt, _ = s.maxTime(ctx, db, "im_group_events_outbox", "created_at") item.LastFileUpdatedAt, _ = s.maxTime(ctx, db, "im_fileuploads", "updatedat") if item.OutboxFailedCount > 0 || item.GroupOutboxFailed > 0 { item.Status = consts.ImTaskDashboardStatusError item.RecommendedNextAction = "处理失败Outbox事件" } else if item.OutboxPendingCount > 0 || item.GroupOutboxPending > 0 || item.OrphanFileCount > 0 || item.DeletedFileCount > 0 { item.Status = consts.ImTaskDashboardStatusWarning item.RecommendedNextAction = "检查待处理任务和文件清理" } return item } func (s *sSysImTaskDashboard) count(ctx context.Context, db gdb.DB, table string, where string) (int, error) { return db.Model(table).Safe().Ctx(ctx).Where(where).Count() } func (s *sSysImTaskDashboard) countOrphanFiles(ctx context.Context, db gdb.DB) (int, error) { return db.Model("im_fileuploads fu").Safe().Ctx(ctx). LeftJoin("im_filemsglinks fml", "fml.fileid=fu.id"). Where("fml.id IS NULL"). WhereNotIn("fu.status", []int{consts.ImFileStatusDeleted, consts.ImFileStatusQuarantined}). Count() } func (s *sSysImTaskDashboard) maxTime(ctx context.Context, db gdb.DB, table string, field string) (res *gtime.Time, err error) { var row struct { Value *gtime.Time `orm:"value"` } if err = db.Model(table).Safe().Ctx(ctx).Fields("MAX(" + field + ") value").Scan(&row); err != nil { return nil, err } return row.Value, nil }