feat: implement V2 alert center and scoped alert governance
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
package business
|
||||
|
||||
import (
|
||||
"context"
|
||||
"kra/internal/biz/business"
|
||||
"kra/internal/service/dto"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type AlertGovernanceUsecase interface {
|
||||
Candidates(context.Context, string, int) ([]*business.AlertChannelOption, error)
|
||||
Gap(context.Context) (*business.AlertGap, error)
|
||||
SaveGap(context.Context, bool, int) error
|
||||
List(context.Context, business.AlertExemptionFilter, ...business.ListOption) ([]*business.AlertExemption, int64, error)
|
||||
Add(context.Context, business.ChannelKey, string) error
|
||||
Remove(context.Context, uint64) error
|
||||
}
|
||||
|
||||
func (s *AlertGovernanceService) Candidates(ctx context.Context, keyword string) ([]*dto.AlertChannelOption, error) {
|
||||
if len([]rune(keyword)) > 100 {
|
||||
return nil, business.ErrAlertRecordInput
|
||||
}
|
||||
rows, err := s.uc.Candidates(ctx, keyword, 30)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]*dto.AlertChannelOption, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
result = append(result, &dto.AlertChannelOption{ID: row.ID, SourceDB: row.SourceDB, Account: row.Name})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type AlertGovernanceService struct{ uc AlertGovernanceUsecase }
|
||||
|
||||
func NewAlertGovernanceService(uc *business.AlertGovernanceUsecase) *AlertGovernanceService {
|
||||
return &AlertGovernanceService{uc}
|
||||
}
|
||||
func (s *AlertGovernanceService) Gap(ctx context.Context) (*dto.AlertGapRequest, error) {
|
||||
v, err := s.uc.Gap(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &dto.AlertGapRequest{Enabled: v.Enabled, Days: v.Days}, nil
|
||||
}
|
||||
func (s *AlertGovernanceService) SaveGap(ctx context.Context, req *dto.AlertGapRequest) error {
|
||||
if req == nil || req.Days < 1 || req.Days > 2147483647 {
|
||||
return business.ErrAlertRuleInput
|
||||
}
|
||||
return s.uc.SaveGap(ctx, req.Enabled, req.Days)
|
||||
}
|
||||
func (s *AlertGovernanceService) List(ctx context.Context, req *dto.AlertExemptionQuery) (*dto.AlertExemptionPage, error) {
|
||||
if req == nil {
|
||||
return nil, business.ErrAlertRecordInput
|
||||
}
|
||||
page, size := max(req.Page, 1), req.PageSize
|
||||
if size < 1 {
|
||||
size = 10
|
||||
}
|
||||
size = min(size, 500)
|
||||
if page > int(^uint(0)>>1)/size {
|
||||
return nil, business.ErrAlertRecordInput
|
||||
}
|
||||
rows, total, err := s.uc.List(ctx, business.AlertExemptionFilter{Name: strings.TrimSpace(req.Name), Active: req.Active}, business.ListOffset((page-1)*size), business.ListLimit(size))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := &dto.AlertExemptionPage{Total: total, Rows: make([]*dto.AlertExemptionResponse, 0, len(rows))}
|
||||
for _, r := range rows {
|
||||
out.Rows = append(out.Rows, &dto.AlertExemptionResponse{ID: r.ID, SourceDB: r.SourceDB, PromoteID: r.ChannelKey.ID, ChannelName: r.ChannelName, AddedName: r.AddedName, AddedAt: localTime(&r.AddedAt), Reason: r.Reason, Active: r.RemovedAt == nil, RemovedName: r.RemovedName, RemovedAt: localTime(r.RemovedAt), RemoveReason: r.RemoveReason})
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
func (s *AlertGovernanceService) Add(ctx context.Context, req *dto.AlertExemptionRequest) error {
|
||||
if req == nil || len([]rune(req.Reason)) > 500 {
|
||||
return business.ErrAlertRecordInput
|
||||
}
|
||||
id, err := strconv.ParseUint(req.PromoteID.String(), 10, 64)
|
||||
if err != nil || id == 0 {
|
||||
return business.ErrAlertRecordInput
|
||||
}
|
||||
return s.uc.Add(ctx, business.ChannelKey{SourceDB: req.SourceDB, ID: id}, req.Reason)
|
||||
}
|
||||
func (s *AlertGovernanceService) Remove(ctx context.Context, id uint64) error {
|
||||
if id == 0 {
|
||||
return business.ErrAlertRecordInput
|
||||
}
|
||||
return s.uc.Remove(ctx, id)
|
||||
}
|
||||
|
||||
func (s *AlertRecordService) Ignore(ctx context.Context, req *dto.AlertIgnoreRequest) error {
|
||||
if req == nil || len(req.IDs) == 0 || len(req.IDs) > 500 {
|
||||
return business.ErrAlertRecordInput
|
||||
}
|
||||
ids := make([]uint64, 0, len(req.IDs))
|
||||
seen := map[uint64]bool{}
|
||||
for _, n := range req.IDs {
|
||||
id, err := strconv.ParseUint(n.String(), 10, 64)
|
||||
if err != nil || id == 0 {
|
||||
return business.ErrAlertRecordInput
|
||||
}
|
||||
if !seen[id] {
|
||||
ids = append(ids, id)
|
||||
seen[id] = true
|
||||
}
|
||||
}
|
||||
return s.uc.Ignore(ctx, ids)
|
||||
}
|
||||
@@ -11,12 +11,18 @@ import (
|
||||
)
|
||||
|
||||
type AlertRecordUsecase interface {
|
||||
Platforms(context.Context) ([]string, error)
|
||||
Ignore(context.Context, []uint64) error
|
||||
List(context.Context, business.AlertRecordFilter, ...business.ListOption) (*business.AlertRecordPage, error)
|
||||
Detail(context.Context, uint64) (*business.AlertRecordDetail, error)
|
||||
Process(context.Context, uint64, business.AlertAction, string) error
|
||||
AddNote(context.Context, uint64, string, []string) error
|
||||
}
|
||||
|
||||
func (s *AlertRecordService) Platforms(ctx context.Context) ([]string, error) {
|
||||
return s.uc.Platforms(ctx)
|
||||
}
|
||||
|
||||
type AlertRecordService struct{ uc AlertRecordUsecase }
|
||||
|
||||
func NewAlertRecordService(uc *business.AlertRecordUsecase) *AlertRecordService {
|
||||
@@ -28,7 +34,7 @@ func alertRecordResponse(r *business.AlertRecord) *dto.AlertRecordResponse {
|
||||
if r.MonitorMetric != nil {
|
||||
metricLabel = business.AlertMetricLabel(*r.MonitorMetric)
|
||||
}
|
||||
return &dto.AlertRecordResponse{ID: r.ID, RuleID: r.RuleID, PlatformCode: r.PlatformCode, PlatformName: r.PlatformName, RuleName: r.RuleName, RuleType: r.RuleType, RuleTypeLabel: business.AlertRuleTypeLabel(r.RuleType), MonitorMetric: r.MonitorMetric, MonitorMetricLabel: metricLabel, TargetPromoteID: r.TargetPromoteID, TargetPromoteName: r.TargetPromoteName, BusinessID: r.BusinessID, BusinessName: r.BusinessName, AlertContent: r.AlertContent, Status: r.Status, StatusLabel: business.AlertStatusLabel(r.Status), CreatedAt: localTime(&r.CreatedAt), UpdatedAt: localTime(&r.UpdatedAt)}
|
||||
return &dto.AlertRecordResponse{ID: r.ID, TotalPlayers: r.TotalPlayers, TotalPay: r.TotalPay, WarningColor: r.WarningColor, Lost: r.Lost, RuleID: r.RuleID, PlatformCode: r.PlatformCode, PlatformName: r.PlatformName, RuleName: r.RuleName, RuleType: r.RuleType, RuleTypeLabel: business.AlertRuleTypeLabel(r.RuleType), MonitorMetric: r.MonitorMetric, MonitorMetricLabel: metricLabel, TargetPromoteID: r.TargetPromoteID, TargetPromoteName: r.TargetPromoteName, BusinessID: r.BusinessID, BusinessName: r.BusinessName, AlertContent: r.AlertContent, Status: r.Status, StatusLabel: business.AlertStatusLabel(r.Status), CreatedAt: localTime(&r.CreatedAt), UpdatedAt: localTime(&r.UpdatedAt)}
|
||||
}
|
||||
|
||||
func (s *AlertRecordService) List(ctx context.Context, req *dto.AlertRecordQuery) (*dto.AlertRecordPage, error) {
|
||||
@@ -41,7 +47,7 @@ func (s *AlertRecordService) List(ctx context.Context, req *dto.AlertRecordQuery
|
||||
if req.MonitorMetric != nil && (*req.MonitorMetric < 1 || *req.MonitorMetric > 3) {
|
||||
return nil, business.ErrAlertRecordInput
|
||||
}
|
||||
if req.Status != nil && (*req.Status < 1 || *req.Status > 5) {
|
||||
if req.Status != nil && (*req.Status < 1 || *req.Status > 6) {
|
||||
return nil, business.ErrAlertRecordInput
|
||||
}
|
||||
page, size := req.Page, req.PageSize
|
||||
@@ -57,7 +63,7 @@ func (s *AlertRecordService) List(ctx context.Context, req *dto.AlertRecordQuery
|
||||
if page > int(^uint(0)>>1)/size {
|
||||
return nil, business.ErrAlertRecordInput
|
||||
}
|
||||
filter := business.AlertRecordFilter{TargetPromoteID: req.TargetPromoteID, BusinessID: req.BusinessID, TargetPromoteName: strings.TrimSpace(req.TargetPromoteName), RuleName: strings.TrimSpace(req.RuleName), RuleType: strings.TrimSpace(req.RuleType), BusinessName: strings.TrimSpace(req.BusinessName), MonitorMetric: req.MonitorMetric, Status: req.Status}
|
||||
filter := business.AlertRecordFilter{AlertContent: strings.TrimSpace(req.AlertContent), Lost: req.Lost, TargetPromoteID: req.TargetPromoteID, BusinessID: req.BusinessID, TargetPromoteName: strings.TrimSpace(req.TargetPromoteName), RuleName: strings.TrimSpace(req.RuleName), RuleType: strings.TrimSpace(req.RuleType), BusinessName: strings.TrimSpace(req.BusinessName), MonitorMetric: req.MonitorMetric, Status: req.Status}
|
||||
seen := map[string]bool{}
|
||||
filter.PlatformBusierID = req.PlatformBusierID
|
||||
for _, code := range strings.Split(req.PlatformCodes, ",") {
|
||||
|
||||
@@ -30,13 +30,13 @@ func alertRuleResponse(r *business.AlertRule) *dto.AlertRuleResponse {
|
||||
n := json.Number(*r.MomThreshold)
|
||||
threshold = &n
|
||||
}
|
||||
return &dto.AlertRuleResponse{ID: r.ID, RuleName: r.Name, RuleType: r.Type, RuleTypeLabel: business.AlertRuleTypeLabel(r.Type), MonitorMetric: r.Metric, MonitorMetricLabel: business.AlertMetricLabel(r.Metric), StatPeriodDays: r.StatPeriodDays, MomDirection: r.MomDirection, MomDirectionLabel: business.AlertDirectionLabel(r.MomDirection), MomThreshold: threshold, UnloginDays: r.UnloginDays, MonitorScope: r.MonitorScope, WarningColor: r.WarningColor, CreatorID: r.CreatorID, CreatorName: r.CreatorName, Enabled: r.Enabled, CreatedAt: localTime(&r.CreatedAt), UpdatedAt: localTime(&r.UpdatedAt), RuleDescription: r.Description()}
|
||||
return &dto.AlertRuleResponse{ID: r.ID, ConditionMode: r.ConditionMode, AmountDirection: r.AmountDirection, AmountThreshold: r.AmountThreshold, RuleName: r.Name, RuleType: r.Type, RuleTypeLabel: business.AlertRuleTypeLabel(r.Type), MonitorMetric: r.Metric, MonitorMetricLabel: business.AlertMetricLabel(r.Metric), StatPeriodDays: r.StatPeriodDays, MomDirection: r.MomDirection, MomDirectionLabel: business.AlertDirectionLabel(r.MomDirection), MomThreshold: threshold, UnloginDays: r.UnloginDays, MonitorScope: r.MonitorScope, WarningColor: r.WarningColor, CreatorID: r.CreatorID, CreatorName: r.CreatorName, Enabled: r.Enabled, CreatedAt: localTime(&r.CreatedAt), UpdatedAt: localTime(&r.UpdatedAt), RuleDescription: r.Description()}
|
||||
}
|
||||
func (s *AlertRuleService) List(ctx context.Context, req *dto.AlertRuleQuery) (*dto.AlertRulePage, error) {
|
||||
if req == nil {
|
||||
return nil, business.ErrAlertRuleInput
|
||||
}
|
||||
filter := business.AlertRuleFilter{Name: req.RuleName, Type: req.RuleType}
|
||||
filter := business.AlertRuleFilter{Name: strings.TrimSpace(req.RuleName), Type: strings.TrimSpace(req.RuleType)}
|
||||
if req.MonitorMetric != "" {
|
||||
n, err := strconv.Atoi(req.MonitorMetric)
|
||||
if err != nil || n < 1 || n > 3 {
|
||||
@@ -87,11 +87,23 @@ func (s *AlertRuleService) Save(ctx context.Context, req *dto.AlertRuleRequest)
|
||||
return 0, business.ErrAlertRuleInput
|
||||
}
|
||||
}
|
||||
rule := &business.AlertRule{ID: req.ID, Name: req.RuleName, Type: req.RuleType, Metric: req.MonitorMetric, StatPeriodDays: req.StatPeriodDays, MomDirection: req.MomDirection, UnloginDays: req.UnloginDays, WarningColor: req.WarningColor}
|
||||
var id uint64
|
||||
if req.ID != "" {
|
||||
var err error
|
||||
id, err = strconv.ParseUint(req.ID.String(), 10, 64)
|
||||
if err != nil {
|
||||
return 0, business.ErrAlertRuleInput
|
||||
}
|
||||
}
|
||||
rule := &business.AlertRule{ID: id, ConditionMode: req.ConditionMode, AmountDirection: req.AmountDirection, Name: strings.TrimSpace(req.RuleName), Type: req.RuleType, Metric: req.MonitorMetric, StatPeriodDays: req.StatPeriodDays, MomDirection: req.MomDirection, UnloginDays: req.UnloginDays, WarningColor: strings.TrimSpace(req.WarningColor)}
|
||||
if req.MomThreshold != nil {
|
||||
value := req.MomThreshold.String()
|
||||
rule.MomThreshold = &value
|
||||
}
|
||||
if req.AmountThreshold != nil {
|
||||
value := req.AmountThreshold.String()
|
||||
rule.AmountThreshold = &value
|
||||
}
|
||||
return s.uc.Save(ctx, rule)
|
||||
}
|
||||
func (s *AlertRuleService) Delete(ctx context.Context, id uint64) error {
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package dto
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type AlertChannelOption struct {
|
||||
ID uint64 `json:"id,string"`
|
||||
SourceDB string `json:"sourceDb"`
|
||||
Account string `json:"account"`
|
||||
}
|
||||
|
||||
type AlertIgnoreRequest struct {
|
||||
IDs []json.Number `json:"ids"`
|
||||
}
|
||||
type AlertGapRequest struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Days int `json:"days"`
|
||||
}
|
||||
type AlertExemptionQuery struct {
|
||||
Page int `form:"pageNum"`
|
||||
PageSize int `form:"pageSize"`
|
||||
Name string `form:"channelName"`
|
||||
Active *bool `form:"active"`
|
||||
}
|
||||
type AlertExemptionRequest struct {
|
||||
SourceDB string `json:"sourceDb"`
|
||||
PromoteID json.Number `json:"promoteId"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
type AlertExemptionResponse struct {
|
||||
ID uint64 `json:"id,string"`
|
||||
SourceDB string `json:"sourceDb"`
|
||||
PromoteID uint64 `json:"promoteId,string"`
|
||||
ChannelName string `json:"channelName"`
|
||||
AddedName string `json:"addedName"`
|
||||
AddedAt *string `json:"addedAt"`
|
||||
Reason string `json:"reason"`
|
||||
Active bool `json:"active"`
|
||||
RemovedName string `json:"removedName"`
|
||||
RemovedAt *string `json:"removedAt"`
|
||||
RemoveReason string `json:"removeReason"`
|
||||
}
|
||||
type AlertExemptionPage struct {
|
||||
Rows []*AlertExemptionResponse `json:"rows"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package dto
|
||||
import "encoding/json"
|
||||
|
||||
type AlertRecordQuery struct {
|
||||
AlertContent string `form:"alertContent"`
|
||||
Lost *bool `form:"lost"`
|
||||
Page int `form:"pageNum"`
|
||||
PageSize int `form:"pageSize"`
|
||||
PlatformCodes string `form:"platformCodes"`
|
||||
@@ -29,18 +31,21 @@ type AlertNoteRequest struct {
|
||||
}
|
||||
|
||||
type AlertRecordResponse struct {
|
||||
ID uint64 `json:"id,string"`
|
||||
PlatformCode string `json:"platformCode"`
|
||||
PlatformName string `json:"platformName"`
|
||||
RuleID uint64 `json:"ruleId,string"`
|
||||
RuleName string `json:"ruleName"`
|
||||
RuleType string `json:"ruleType"`
|
||||
RuleTypeLabel string `json:"ruleTypeLabel"`
|
||||
MonitorMetric *int `json:"monitorMetric"`
|
||||
MonitorMetricLabel string `json:"monitorMetricLabel"`
|
||||
// 旧视图声明了颜色,但记录没有对应快照字段,保留空值。
|
||||
TotalPlayers *int64 `json:"totalPlayers"`
|
||||
TotalPay *string `json:"totalPay"`
|
||||
Lost *bool `json:"lost"`
|
||||
ID uint64 `json:"id,string"`
|
||||
PlatformCode string `json:"platformCode"`
|
||||
PlatformName string `json:"platformName"`
|
||||
RuleID uint64 `json:"ruleId,string"`
|
||||
RuleName string `json:"ruleName"`
|
||||
RuleType string `json:"ruleType"`
|
||||
RuleTypeLabel string `json:"ruleTypeLabel"`
|
||||
MonitorMetric *int `json:"monitorMetric"`
|
||||
MonitorMetricLabel string `json:"monitorMetricLabel"`
|
||||
// 历史记录无快照时返回 null,不回填当前数值。
|
||||
WarningColor *string `json:"warningColor"`
|
||||
TargetPromoteID uint64 `json:"targetPromoteId"`
|
||||
TargetPromoteID uint64 `json:"targetPromoteId,string"`
|
||||
TargetPromoteName string `json:"targetPromoteName"`
|
||||
BusinessID uint `json:"businessId"`
|
||||
BusinessName string `json:"businessName"`
|
||||
|
||||
@@ -10,18 +10,24 @@ type AlertRuleQuery struct {
|
||||
MonitorMetric string `form:"monitorMetric"`
|
||||
}
|
||||
type AlertRuleRequest struct {
|
||||
ID uint64 `json:"id"`
|
||||
RuleName string `json:"ruleName"`
|
||||
RuleType string `json:"ruleType"`
|
||||
MonitorMetric int `json:"monitorMetric"`
|
||||
StatPeriodDays *int `json:"statPeriodDays"`
|
||||
MomDirection *int `json:"momDirection"`
|
||||
MomThreshold *json.Number `json:"momThreshold"`
|
||||
UnloginDays *int `json:"unloginDays"`
|
||||
WarningColor string `json:"warningColor"`
|
||||
ConditionMode int `json:"conditionMode"`
|
||||
AmountDirection int `json:"amountDirection"`
|
||||
AmountThreshold *json.Number `json:"amountThreshold"`
|
||||
ID json.Number `json:"id"`
|
||||
RuleName string `json:"ruleName"`
|
||||
RuleType string `json:"ruleType"`
|
||||
MonitorMetric int `json:"monitorMetric"`
|
||||
StatPeriodDays *int `json:"statPeriodDays"`
|
||||
MomDirection *int `json:"momDirection"`
|
||||
MomThreshold *json.Number `json:"momThreshold"`
|
||||
UnloginDays *int `json:"unloginDays"`
|
||||
WarningColor string `json:"warningColor"`
|
||||
}
|
||||
type AlertRuleResponse struct {
|
||||
ID uint64 `json:"id"`
|
||||
ConditionMode int `json:"conditionMode"`
|
||||
AmountDirection int `json:"amountDirection"`
|
||||
AmountThreshold *string `json:"amountThreshold"`
|
||||
ID uint64 `json:"id,string"`
|
||||
RuleName string `json:"ruleName"`
|
||||
RuleType string `json:"ruleType"`
|
||||
RuleTypeLabel string `json:"ruleTypeLabel"`
|
||||
|
||||
@@ -19,6 +19,7 @@ var ProviderSet = wire.NewSet(
|
||||
businessservice.NewBindingService,
|
||||
businessservice.NewPasswordResetService,
|
||||
businessservice.NewBusinessPromoteService,
|
||||
businessservice.NewAlertGovernanceService,
|
||||
businessservice.NewAlertRuleService,
|
||||
businessservice.NewAlertRecordService,
|
||||
businessservice.NewActivationService,
|
||||
|
||||
Reference in New Issue
Block a user