package admin import ( "github.com/gofiber/fiber/v2" "github.com/break/junhong_cmp_fiber/pkg/middleware" "github.com/break/junhong_cmp_fiber/internal/model" "github.com/break/junhong_cmp_fiber/internal/model/dto" "github.com/break/junhong_cmp_fiber/internal/service/polling" "github.com/break/junhong_cmp_fiber/pkg/errors" "github.com/break/junhong_cmp_fiber/pkg/response" ) // PollingCleanupHandler 轮询数据清理处理器 type PollingCleanupHandler struct { service *polling.CleanupService } // NewPollingCleanupHandler 创建轮询数据清理处理器 func NewPollingCleanupHandler(service *polling.CleanupService) *PollingCleanupHandler { return &PollingCleanupHandler{service: service} } // CreateConfig 创建清理配置 // @Summary 创建数据清理配置 // @Description 创建新的数据清理配置 // @Tags 轮询管理-数据清理 // @Accept json // @Produce json // @Security BearerAuth // @Param request body dto.CreateDataCleanupConfigReq true "创建请求" // @Success 200 {object} response.Response{data=dto.DataCleanupConfigResp} // @Router /api/admin/data-cleanup-configs [post] func (h *PollingCleanupHandler) CreateConfig(c *fiber.Ctx) error { ctx := c.UserContext() var req dto.CreateDataCleanupConfigReq if err := c.BodyParser(&req); err != nil { return errors.New(errors.CodeInvalidParam) } config := &model.DataCleanupConfig{ TargetTable: req.TargetTable, RetentionDays: req.RetentionDays, BatchSize: req.BatchSize, Description: req.Description, } if err := h.service.CreateConfig(ctx, config); err != nil { return err } return response.Success(c, h.toConfigResp(config)) } // ListConfigs 获取清理配置列表 // @Summary 获取数据清理配置列表 // @Description 获取所有数据清理配置 // @Tags 轮询管理-数据清理 // @Accept json // @Produce json // @Security BearerAuth // @Success 200 {object} response.Response{data=dto.DataCleanupConfigListResp} // @Router /api/admin/data-cleanup-configs [get] func (h *PollingCleanupHandler) ListConfigs(c *fiber.Ctx) error { ctx := c.UserContext() configs, err := h.service.ListConfigs(ctx) if err != nil { return err } items := make([]*dto.DataCleanupConfigResp, 0, len(configs)) for _, config := range configs { items = append(items, h.toConfigResp(config)) } return response.Success(c, &dto.DataCleanupConfigListResp{Items: items}) } // GetConfig 获取清理配置详情 // @Summary 获取数据清理配置详情 // @Description 获取指定数据清理配置的详细信息 // @Tags 轮询管理-数据清理 // @Accept json // @Produce json // @Security BearerAuth // @Param id path int true "配置ID" // @Success 200 {object} response.Response{data=dto.DataCleanupConfigResp} // @Router /api/admin/data-cleanup-configs/{id} [get] func (h *PollingCleanupHandler) GetConfig(c *fiber.Ctx) error { ctx := c.UserContext() id, err := c.ParamsInt("id") if err != nil || id <= 0 { return errors.New(errors.CodeInvalidParam, "无效的配置ID") } config, err := h.service.GetConfig(ctx, uint(id)) if err != nil { return err } return response.Success(c, h.toConfigResp(config)) } // UpdateConfig 更新清理配置 // @Summary 更新数据清理配置 // @Description 更新指定数据清理配置 // @Tags 轮询管理-数据清理 // @Accept json // @Produce json // @Security BearerAuth // @Param id path int true "配置ID" // @Param request body dto.UpdateDataCleanupConfigReq true "更新请求" // @Success 200 {object} response.Response // @Router /api/admin/data-cleanup-configs/{id} [put] func (h *PollingCleanupHandler) UpdateConfig(c *fiber.Ctx) error { ctx := c.UserContext() id, err := c.ParamsInt("id") if err != nil || id <= 0 { return errors.New(errors.CodeInvalidParam, "无效的配置ID") } var req dto.UpdateDataCleanupConfigReq if err := c.BodyParser(&req); err != nil { return errors.New(errors.CodeInvalidParam) } updates := make(map[string]any) if req.RetentionDays != nil { updates["retention_days"] = *req.RetentionDays } if req.BatchSize != nil { updates["batch_size"] = *req.BatchSize } if req.Enabled != nil { updates["enabled"] = *req.Enabled } if req.Description != nil { updates["description"] = *req.Description } // 获取当前用户ID userID := middleware.GetUserIDFromContext(ctx) if userID > 0 { updates["updated_by"] = userID } if err := h.service.UpdateConfig(ctx, uint(id), updates); err != nil { return err } return response.Success(c, nil) } // DeleteConfig 删除清理配置 // @Summary 删除数据清理配置 // @Description 删除指定数据清理配置 // @Tags 轮询管理-数据清理 // @Accept json // @Produce json // @Security BearerAuth // @Param id path int true "配置ID" // @Success 200 {object} response.Response // @Router /api/admin/data-cleanup-configs/{id} [delete] func (h *PollingCleanupHandler) DeleteConfig(c *fiber.Ctx) error { ctx := c.UserContext() id, err := c.ParamsInt("id") if err != nil || id <= 0 { return errors.New(errors.CodeInvalidParam, "无效的配置ID") } if err := h.service.DeleteConfig(ctx, uint(id)); err != nil { return err } return response.Success(c, nil) } // ListLogs 获取清理日志列表 // @Summary 获取数据清理日志列表 // @Description 获取数据清理日志记录 // @Tags 轮询管理-数据清理 // @Accept json // @Produce json // @Security BearerAuth // @Param table_name query string false "表名筛选" // @Param page query int false "页码" // @Param page_size query int false "每页数量" // @Success 200 {object} response.Response{data=dto.DataCleanupLogListResp} // @Router /api/admin/data-cleanup-logs [get] func (h *PollingCleanupHandler) ListLogs(c *fiber.Ctx) error { ctx := c.UserContext() var req dto.ListDataCleanupLogReq if err := c.QueryParser(&req); err != nil { return errors.New(errors.CodeInvalidParam) } if req.Page < 1 { req.Page = 1 } if req.PageSize < 1 || req.PageSize > 100 { req.PageSize = 20 } logs, total, err := h.service.ListLogs(ctx, req.Page, req.PageSize, req.TableName) if err != nil { return err } items := make([]*dto.DataCleanupLogResp, 0, len(logs)) for _, log := range logs { items = append(items, h.toLogResp(log)) } totalPages := int(total) / req.PageSize if int(total)%req.PageSize > 0 { totalPages++ } return response.Success(c, &dto.DataCleanupLogListResp{ Items: items, Total: total, Page: req.Page, PageSize: req.PageSize, TotalPages: totalPages, }) } // Preview 预览待清理数据 // @Summary 预览待清理数据 // @Description 预览各表待清理的数据量 // @Tags 轮询管理-数据清理 // @Accept json // @Produce json // @Security BearerAuth // @Success 200 {object} response.Response{data=dto.DataCleanupPreviewResp} // @Router /api/admin/data-cleanup/preview [get] func (h *PollingCleanupHandler) Preview(c *fiber.Ctx) error { ctx := c.UserContext() previews, err := h.service.Preview(ctx) if err != nil { return err } items := make([]*dto.DataCleanupPreviewItem, 0, len(previews)) for _, p := range previews { items = append(items, &dto.DataCleanupPreviewItem{ TableName: p.TableName, RetentionDays: p.RetentionDays, RecordCount: p.RecordCount, Description: p.Description, }) } return response.Success(c, &dto.DataCleanupPreviewResp{Items: items}) } // GetProgress 获取清理进度 // @Summary 获取数据清理进度 // @Description 获取当前数据清理任务的进度 // @Tags 轮询管理-数据清理 // @Accept json // @Produce json // @Security BearerAuth // @Success 200 {object} response.Response{data=dto.DataCleanupProgressResp} // @Router /api/admin/data-cleanup/progress [get] func (h *PollingCleanupHandler) GetProgress(c *fiber.Ctx) error { ctx := c.UserContext() progress, err := h.service.GetProgress(ctx) if err != nil { return err } resp := &dto.DataCleanupProgressResp{ IsRunning: progress.IsRunning, CurrentTable: progress.CurrentTable, TotalTables: progress.TotalTables, ProcessedTables: progress.ProcessedTables, TotalDeleted: progress.TotalDeleted, StartedAt: progress.StartedAt, } if progress.LastLog != nil { resp.LastLog = h.toLogResp(progress.LastLog) } return response.Success(c, resp) } // TriggerCleanup 手动触发清理 // @Summary 手动触发数据清理 // @Description 手动触发数据清理任务 // @Tags 轮询管理-数据清理 // @Accept json // @Produce json // @Security BearerAuth // @Param request body dto.TriggerDataCleanupReq true "触发请求" // @Success 200 {object} response.Response // @Router /api/admin/data-cleanup/trigger [post] func (h *PollingCleanupHandler) TriggerCleanup(c *fiber.Ctx) error { ctx := c.UserContext() var req dto.TriggerDataCleanupReq if err := c.BodyParser(&req); err != nil { return errors.New(errors.CodeInvalidParam) } // 获取当前用户ID userID := middleware.GetUserIDFromContext(ctx) // 异步执行清理 go func() { _ = h.service.TriggerCleanup(ctx, req.TableName, userID) }() return response.Success(c, nil) } func (h *PollingCleanupHandler) toConfigResp(config *model.DataCleanupConfig) *dto.DataCleanupConfigResp { return &dto.DataCleanupConfigResp{ ID: config.ID, TargetTable: config.TargetTable, RetentionDays: config.RetentionDays, BatchSize: config.BatchSize, Enabled: int(config.Enabled), Description: config.Description, CreatedAt: config.CreatedAt, UpdatedAt: config.UpdatedAt, UpdatedBy: config.UpdatedBy, } } func (h *PollingCleanupHandler) toLogResp(log *model.DataCleanupLog) *dto.DataCleanupLogResp { return &dto.DataCleanupLogResp{ ID: log.ID, TargetTable: log.TargetTable, CleanupType: log.CleanupType, RetentionDays: log.RetentionDays, DeletedCount: log.DeletedCount, DurationMs: log.DurationMs, Status: log.Status, ErrorMessage: log.ErrorMessage, StartedAt: log.StartedAt, CompletedAt: log.CompletedAt, TriggeredBy: log.TriggeredBy, } }