package admin import ( "strconv" "github.com/go-playground/validator/v10" "github.com/gofiber/fiber/v2" packagetrafficalertapp "github.com/break/junhong_cmp_fiber/internal/application/packagetrafficalert" "github.com/break/junhong_cmp_fiber/internal/handler/validation" "github.com/break/junhong_cmp_fiber/internal/model/dto" packagetrafficalertquery "github.com/break/junhong_cmp_fiber/internal/query/packagetrafficalert" exportTaskService "github.com/break/junhong_cmp_fiber/internal/service/export_task" "github.com/break/junhong_cmp_fiber/pkg/constants" "github.com/break/junhong_cmp_fiber/pkg/errors" "github.com/break/junhong_cmp_fiber/pkg/response" ) // PackageTrafficAlertHandler 套餐真流量预警 Handler。 // 规则维护与预警读取/导出只对超级管理员与平台账号开放,路由组已有角色门禁, // Handler 仍不做任何跳过业务校验的分支。 type PackageTrafficAlertHandler struct { ruleService *packagetrafficalertapp.RuleService query *packagetrafficalertquery.Query exportService *exportTaskService.Service validator *validator.Validate } // NewPackageTrafficAlertHandler 创建套餐真流量预警 Handler。 func NewPackageTrafficAlertHandler(ruleService *packagetrafficalertapp.RuleService, query *packagetrafficalertquery.Query, exportService *exportTaskService.Service, validator *validator.Validate) *PackageTrafficAlertHandler { return &PackageTrafficAlertHandler{ruleService: ruleService, query: query, exportService: exportService, validator: validator} } // CreateRule 创建套餐真流量预警规则。 // POST /api/admin/package-traffic-alert-rules func (h *PackageTrafficAlertHandler) CreateRule(c *fiber.Ctx) error { var req dto.CreatePackageTrafficAlertRuleRequest if err := c.BodyParser(&req); err != nil { return errors.New(errors.CodeInvalidParam, "请求参数格式不正确") } if err := h.validator.Struct(&req); err != nil { return errors.New(errors.CodeInvalidParam, validation.Message("创建套餐真流量预警规则参数不合法", &req, err)) } result, err := h.ruleService.Create(c.UserContext(), &req) if err != nil { return err } return response.Success(c, result) } // UpdateRule 修改套餐真流量预警规则的阈值、启停与备注。 // PUT /api/admin/package-traffic-alert-rules/:id func (h *PackageTrafficAlertHandler) UpdateRule(c *fiber.Ctx) error { ruleID, err := strconv.ParseUint(c.Params("id"), 10, 64) if err != nil || ruleID == 0 { return errors.New(errors.CodeInvalidParam, "无效的预警规则 ID") } var req dto.UpdatePackageTrafficAlertRuleRequest if err := c.BodyParser(&req); err != nil { return errors.New(errors.CodeInvalidParam, "请求参数格式不正确") } if err := h.validator.Struct(&req); err != nil { return errors.New(errors.CodeInvalidParam, validation.Message("修改套餐真流量预警规则参数不合法", &req, err)) } result, err := h.ruleService.Update(c.UserContext(), uint(ruleID), &req) if err != nil { return err } return response.Success(c, result) } // ListRules 查询套餐真流量预警规则列表。 // GET /api/admin/package-traffic-alert-rules func (h *PackageTrafficAlertHandler) ListRules(c *fiber.Ctx) error { var req dto.ListPackageTrafficAlertRuleRequest if err := c.QueryParser(&req); err != nil { return errors.New(errors.CodeInvalidParam, "请求参数格式不正确") } if err := h.validator.Struct(&req); err != nil { return errors.New(errors.CodeInvalidParam, validation.Message("查询套餐真流量预警规则参数不合法", &req, err)) } result, err := h.query.ListRules(c.UserContext(), req) if err != nil { return err } return response.SuccessWithPagination(c, result.Items, result.Total, result.Page, result.Size) } // ListAlerts 查询套餐真流量达量预警列表。 // GET /api/admin/package-traffic-alerts func (h *PackageTrafficAlertHandler) ListAlerts(c *fiber.Ctx) error { var req dto.ListPackageTrafficAlertRequest if err := c.QueryParser(&req); err != nil { return errors.New(errors.CodeInvalidParam, "请求参数格式不正确") } if err := h.validator.Struct(&req); err != nil { return errors.New(errors.CodeInvalidParam, validation.Message("查询套餐真流量达量预警参数不合法", &req, err)) } if req.NotificationStatus != nil && !constants.IsValidPackageTrafficAlertNotifyStatus(*req.NotificationStatus) { return errors.New(errors.CodeInvalidParam, "通知投递结果必须为 1/2/3/4/5 之一") } result, err := h.query.ListAlerts(c.UserContext(), req) if err != nil { return err } return response.SuccessWithPagination(c, result.Items, result.Total, result.Page, result.Size) } // GetAlert 查询套餐真流量达量预警详情。 // GET /api/admin/package-traffic-alerts/:id func (h *PackageTrafficAlertHandler) GetAlert(c *fiber.Ctx) error { alertID, err := strconv.ParseUint(c.Params("id"), 10, 64) if err != nil || alertID == 0 { return errors.New(errors.CodeInvalidParam, "无效的预警 ID") } result, err := h.query.GetAlert(c.UserContext(), uint(alertID)) if err != nil { return err } return response.Success(c, result) } // ExportAlerts 创建套餐真流量达量预警异步导出任务。 // POST /api/admin/package-traffic-alerts/export // 只暴露受控入口;导出任务创建时冻结操作者、筛选、时间范围与可见资产范围。 func (h *PackageTrafficAlertHandler) ExportAlerts(c *fiber.Ctx) error { var req dto.ExportPackageTrafficAlertRequest if err := c.BodyParser(&req); err != nil { return errors.New(errors.CodeInvalidParam, "请求参数格式不正确") } if err := h.validator.Struct(&req); err != nil { return errors.New(errors.CodeInvalidParam, validation.Message("导出套餐真流量达量预警参数不合法", &req, err)) } if req.NotificationStatus != nil && !constants.IsValidPackageTrafficAlertNotifyStatus(*req.NotificationStatus) { return errors.New(errors.CodeInvalidParam, "通知投递结果必须为 1/2/3/4/5 之一") } createRequest := dto.CreateExportTaskRequest{ Scene: constants.ExportTaskScenePackageTrafficAlert, Format: req.Format, Query: map[string]interface{}{"filters": exportFilters(req)}, } result, err := h.exportService.CreateTask(c.UserContext(), &createRequest) if err != nil { return err } return response.Success(c, result) } // exportFilters 把导出请求转换为导出任务的筛选快照。 // 时间范围在创建时冻结为 RFC3339 字符串,执行期按触发时间解析为闭区间。 func exportFilters(req dto.ExportPackageTrafficAlertRequest) map[string]interface{} { filters := make(map[string]interface{}) if req.PackageID != nil { filters["package_id"] = *req.PackageID } if req.ShopID != nil { filters["shop_id"] = *req.ShopID } if req.BusinessOwnerAccountID != nil { filters["business_owner_account_id"] = *req.BusinessOwnerAccountID } if req.AssetType != "" { filters["asset_type"] = req.AssetType } if req.AssetIdentifier != "" { filters["asset_identifier"] = req.AssetIdentifier } if req.ThresholdPercent != nil { filters["threshold_percent"] = *req.ThresholdPercent } if req.StartTime != nil { filters["start_time"] = req.StartTime.UTC().Format("2006-01-02T15:04:05Z07:00") } if req.EndTime != nil { filters["end_time"] = req.EndTime.UTC().Format("2006-01-02T15:04:05Z07:00") } if req.NotificationStatus != nil { filters["notification_status"] = *req.NotificationStatus } return filters }