449 lines
10 KiB
Go
449 lines
10 KiB
Go
package warningnotify
|
||
|
||
import (
|
||
"fmt"
|
||
"html"
|
||
"strings"
|
||
"time"
|
||
)
|
||
|
||
const warningEmailHTMLSignature = `data-warning-email="iotlcm"`
|
||
|
||
func applyContent(batch *Batch) {
|
||
if batch == nil {
|
||
return
|
||
}
|
||
switch batch.Method {
|
||
case MethodSMS:
|
||
batch.SMSContent = buildSMSContent(*batch)
|
||
case MethodEmail:
|
||
batch.EmailSubject = buildEmailSubject(*batch)
|
||
batch.EmailBody = buildEmailBody(*batch)
|
||
case MethodPlatform:
|
||
batch.PlatformTitle = buildPlatformTitle(*batch)
|
||
batch.PlatformContent = buildPlatformContent(*batch)
|
||
}
|
||
}
|
||
|
||
func buildSMSContent(batch Batch) string {
|
||
count := batchItemCount(batch)
|
||
if count == 0 {
|
||
return ""
|
||
}
|
||
|
||
summary := batchObjectSummary(batch)
|
||
title := warningTitle(batch)
|
||
action := ""
|
||
if batchHasAffectOperation(batch) {
|
||
action = ",涉及停机或断网操作"
|
||
}
|
||
|
||
return fmt.Sprintf(
|
||
"【物联网】%s触发%s%s,请登录控制台查看。",
|
||
summary,
|
||
title,
|
||
action,
|
||
)
|
||
}
|
||
|
||
func buildEmailSubject(batch Batch) string {
|
||
count := batchItemCount(batch)
|
||
if count == 0 {
|
||
return "物联网预警提醒"
|
||
}
|
||
return fmt.Sprintf("%s(%d条)", warningTitle(batch), count)
|
||
}
|
||
|
||
func buildEmailBody(batch Batch) string {
|
||
count := batchItemCount(batch)
|
||
if count == 0 {
|
||
return ""
|
||
}
|
||
|
||
var builder strings.Builder
|
||
builder.WriteString(`<div ` + warningEmailHTMLSignature + ` style="font-family:Arial,'Microsoft YaHei',sans-serif;font-size:14px;line-height:1.6;color:#1f2937;">`)
|
||
fmt.Fprintf(&builder, `<p style="margin:0 0 12px;">%s,共 %d 条,最高级别:%s。</p>`, htmlEscape(warningTitle(batch)), count, htmlEscape(levelText(batchHighestLevel(batch))))
|
||
if batchHasAffectOperation(batch) {
|
||
builder.WriteString(`<p style="margin:0 0 12px;color:#b42318;">本次预警包含停机或断网类操作,请重点确认。</p>`)
|
||
}
|
||
builder.WriteString(`<table style="width:100%;border-collapse:collapse;border:1px solid #d0d5dd;">`)
|
||
builder.WriteString(`<thead><tr style="background:#f2f4f7;">`)
|
||
builder.WriteString(emailHeaderCell(emailObjectHeader(batch)))
|
||
builder.WriteString(emailHeaderCell("预警类型"))
|
||
builder.WriteString(emailHeaderCell("预警原因"))
|
||
builder.WriteString(emailHeaderCell("预警时间"))
|
||
builder.WriteString(`</tr></thead><tbody>`)
|
||
for _, item := range batch.Items {
|
||
builder.WriteString(`<tr>`)
|
||
builder.WriteString(emailBodyCell(displayObjectCode(item)))
|
||
builder.WriteString(emailBodyCell(emailWarningType(item, batch)))
|
||
builder.WriteString(emailBodyCell(emailWarningReason(item)))
|
||
builder.WriteString(emailBodyCell(emailWarnTime(item)))
|
||
builder.WriteString(`</tr>`)
|
||
}
|
||
builder.WriteString(`</tbody></table></div>`)
|
||
|
||
return strings.TrimSpace(builder.String())
|
||
}
|
||
|
||
func buildPlatformTitle(batch Batch) string {
|
||
if len(batch.Items) == 0 {
|
||
return "预警提醒"
|
||
}
|
||
if title := expireDayPlatformTitle(batch); title != "" {
|
||
return title
|
||
}
|
||
return warningTitle(batch)
|
||
}
|
||
|
||
func buildPlatformContent(batch Batch) string {
|
||
count := batchItemCount(batch)
|
||
if count == 0 {
|
||
return ""
|
||
}
|
||
|
||
content := buildPlatformSummaryContent(batch)
|
||
if batchHasAffectOperation(batch) {
|
||
content += "本次提醒包含停机或断网类操作,请及时确认。"
|
||
}
|
||
|
||
return content
|
||
}
|
||
|
||
func buildPlatformSummaryContent(batch Batch) string {
|
||
if isExpireDayBatch(batch) {
|
||
return fmt.Sprintf(
|
||
"%s有%d张卡临近到期,最高档位:%s,请登录控制台查看。",
|
||
firstObjectCode(batch),
|
||
batchItemCount(batch),
|
||
buildPlatformTitle(batch),
|
||
)
|
||
}
|
||
return fmt.Sprintf(
|
||
"%s触发%s,最高级别:%s,请登录控制台查看。",
|
||
platformObjectSummary(batch),
|
||
buildPlatformTitle(batch),
|
||
levelText(batchHighestLevel(batch)),
|
||
)
|
||
}
|
||
|
||
func warningTitle(batch Batch) string {
|
||
category := warningCategory(batch)
|
||
return categoryStageTitle(category, batch.WarnStage)
|
||
}
|
||
|
||
func warningCategory(batch Batch) string {
|
||
category := strings.TrimSpace(batch.Category)
|
||
if category == "" {
|
||
category = "预警"
|
||
}
|
||
return category
|
||
}
|
||
|
||
func categoryStageTitle(category string, stage int) string {
|
||
category = strings.TrimSpace(category)
|
||
if category == "" {
|
||
category = "预警"
|
||
}
|
||
if stage > 0 {
|
||
return category + stageText(stage)
|
||
}
|
||
if strings.HasSuffix(category, "提醒") {
|
||
return category
|
||
}
|
||
return category + "提醒"
|
||
}
|
||
|
||
func expireDayPlatformTitle(batch Batch) string {
|
||
if !isExpireDayBatch(batch) {
|
||
return ""
|
||
}
|
||
threshold := expireDayPlatformThreshold(batch)
|
||
if threshold <= 0 {
|
||
return "距离到期时间预警"
|
||
}
|
||
return fmt.Sprintf("距离到期时间小于%.0f天", threshold)
|
||
}
|
||
|
||
func isExpireDayBatch(batch Batch) bool {
|
||
category := strings.TrimSpace(batch.Category)
|
||
for _, item := range batch.Items {
|
||
if strings.TrimSpace(item.Kind) == "expire_day" {
|
||
return true
|
||
}
|
||
if strings.Contains(category, "到期") && strings.TrimSpace(item.UnitText) == "天" {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
func expireDayPlatformThreshold(batch Batch) float64 {
|
||
for _, item := range batch.Items {
|
||
if item.WarnStage == batch.WarnStage && item.Threshold > 0 {
|
||
return item.Threshold
|
||
}
|
||
}
|
||
for _, item := range batch.Items {
|
||
if item.Threshold > 0 {
|
||
return item.Threshold
|
||
}
|
||
}
|
||
switch batch.WarnStage {
|
||
case 1:
|
||
return 30
|
||
case 2:
|
||
return 15
|
||
case 3:
|
||
return 7
|
||
default:
|
||
return 0
|
||
}
|
||
}
|
||
|
||
func itemDetailSummary(item Event) string {
|
||
return itemSummary(item)
|
||
}
|
||
|
||
func batchObjectSummary(batch Batch) string {
|
||
count := batchItemCount(batch)
|
||
if count == 0 {
|
||
return ""
|
||
}
|
||
code := firstObjectCode(batch)
|
||
if count == 1 {
|
||
return code
|
||
}
|
||
return fmt.Sprintf("%s等%d%s", code, count, objectCountUnit(batch.Items[0]))
|
||
}
|
||
|
||
func firstObjectCode(batch Batch) string {
|
||
if len(batch.Items) == 0 {
|
||
return "--"
|
||
}
|
||
return displayObjectCode(batch.Items[0])
|
||
}
|
||
|
||
func objectCountUnit(item Event) string {
|
||
switch item.ItemType {
|
||
case 2:
|
||
return "个流量池"
|
||
default:
|
||
return "张卡"
|
||
}
|
||
}
|
||
|
||
func metricSummary(item Event) string {
|
||
unit := strings.TrimSpace(item.UnitText)
|
||
switch unit {
|
||
case "天", "月":
|
||
parts := []string{
|
||
"剩余" + formatMetric(item.Actual, unit),
|
||
}
|
||
if item.Threshold > 0 {
|
||
parts = append(parts, "阈值"+formatMetric(item.Threshold, unit))
|
||
}
|
||
return strings.Join(parts, ",")
|
||
case "%":
|
||
parts := []string{
|
||
"阈值" + formatMetric(item.Threshold, unit),
|
||
"实际" + formatMetric(item.Actual, unit),
|
||
}
|
||
if shouldShowRemain(item) {
|
||
parts = append(parts, "剩余"+remainMetric(item))
|
||
}
|
||
if item.TotalFlowMB > 0 {
|
||
parts = append(parts, "总量"+formatMetric(item.TotalFlowMB, "MB"))
|
||
}
|
||
return strings.Join(parts, ",")
|
||
default:
|
||
parts := []string{
|
||
"阈值" + formatMetric(item.Threshold, item.UnitText),
|
||
"实际" + formatMetric(item.Actual, item.UnitText),
|
||
}
|
||
if shouldShowRemain(item) {
|
||
parts = append(parts, "剩余"+remainMetric(item))
|
||
}
|
||
if item.TotalFlowMB > 0 {
|
||
parts = append(parts, "总量"+formatMetric(item.TotalFlowMB, "MB"))
|
||
}
|
||
return strings.Join(parts, ",")
|
||
}
|
||
}
|
||
|
||
func itemSummary(item Event) string {
|
||
parts := make([]string, 0, 1)
|
||
parts = append(parts, fmt.Sprintf("%s:%s", itemLabel(item), displayObjectCode(item)))
|
||
return strings.Join(parts, ",")
|
||
}
|
||
|
||
func itemLabel(item Event) string {
|
||
switch item.ItemType {
|
||
case 2:
|
||
return "流量池编号"
|
||
default:
|
||
return "ICCID"
|
||
}
|
||
}
|
||
|
||
func displayObjectCode(item Event) string {
|
||
itemCode := strings.TrimSpace(item.ItemCode)
|
||
if itemCode == "" {
|
||
return "--"
|
||
}
|
||
return itemCode
|
||
}
|
||
|
||
func stageText(stage int) string {
|
||
if stage <= 0 {
|
||
return ""
|
||
}
|
||
return fmt.Sprintf("第%d次提醒", stage)
|
||
}
|
||
|
||
func levelText(level int) string {
|
||
switch level {
|
||
case 1:
|
||
return "提示"
|
||
case 2:
|
||
return "注意"
|
||
case 3:
|
||
return "警告"
|
||
case 4:
|
||
return "危险"
|
||
case 5:
|
||
return "紧急"
|
||
default:
|
||
return "提醒"
|
||
}
|
||
}
|
||
|
||
func highestLevel(items []Event) int {
|
||
level := 0
|
||
for _, item := range items {
|
||
if item.WarnLevel > level {
|
||
level = item.WarnLevel
|
||
}
|
||
}
|
||
return level
|
||
}
|
||
|
||
func hasAffectOperation(items []Event) bool {
|
||
for _, item := range items {
|
||
if item.AffectOperation {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
func shouldShowRemain(item Event) bool {
|
||
if item.TotalFlowMB > 0 {
|
||
return true
|
||
}
|
||
unit := strings.TrimSpace(item.UnitText)
|
||
return unit == "MB" || unit == "%"
|
||
}
|
||
|
||
func remainMetric(item Event) string {
|
||
if !shouldShowRemain(item) {
|
||
return "--"
|
||
}
|
||
return formatMetric(item.RemainFlowMB, "MB")
|
||
}
|
||
|
||
func formatMetric(value float64, unit string) string {
|
||
unit = strings.TrimSpace(unit)
|
||
switch unit {
|
||
case "%":
|
||
return fmt.Sprintf("%.2f%%", value)
|
||
case "天", "月":
|
||
return fmt.Sprintf("%.0f%s", value, unit)
|
||
case "MB":
|
||
return fmt.Sprintf("%.3f MB", value)
|
||
default:
|
||
if unit == "" {
|
||
return fmt.Sprintf("%.3f", value)
|
||
}
|
||
return fmt.Sprintf("%.3f %s", value, unit)
|
||
}
|
||
}
|
||
|
||
func formatTime(value time.Time) string {
|
||
return value.Format("2006-01-02 15:04:05")
|
||
}
|
||
|
||
func emailObjectHeader(batch Batch) string {
|
||
if len(batch.Items) == 0 {
|
||
return "ICCID"
|
||
}
|
||
for _, item := range batch.Items {
|
||
if item.ItemType != 2 {
|
||
return "ICCID"
|
||
}
|
||
}
|
||
return "流量池编号"
|
||
}
|
||
|
||
func emailWarningType(item Event, batch Batch) string {
|
||
category := strings.TrimSpace(item.Category)
|
||
if category != "" {
|
||
return category
|
||
}
|
||
return warningCategory(batch)
|
||
}
|
||
|
||
func emailWarningReason(item Event) string {
|
||
parts := make([]string, 0, 3)
|
||
if item.WarnStage > 0 {
|
||
parts = append(parts, stageText(item.WarnStage))
|
||
}
|
||
if summary := metricSummary(item); summary != "" {
|
||
parts = append(parts, summary)
|
||
}
|
||
if item.OperationText != "" {
|
||
parts = append(parts, item.OperationText)
|
||
}
|
||
if len(parts) == 0 && item.Remark != "" {
|
||
parts = append(parts, item.Remark)
|
||
}
|
||
return strings.Join(parts, ",")
|
||
}
|
||
|
||
func emailWarnTime(item Event) string {
|
||
if item.WarnTime.IsZero() {
|
||
return "--"
|
||
}
|
||
return formatTime(item.WarnTime)
|
||
}
|
||
|
||
func emailHeaderCell(value string) string {
|
||
return `<th style="padding:8px 10px;border:1px solid #d0d5dd;text-align:left;font-weight:600;color:#344054;">` + htmlEscape(value) + `</th>`
|
||
}
|
||
|
||
func emailBodyCell(value string) string {
|
||
return `<td style="padding:8px 10px;border:1px solid #d0d5dd;vertical-align:top;color:#1f2937;">` + htmlEscape(value) + `</td>`
|
||
}
|
||
|
||
func htmlEscape(value string) string {
|
||
value = strings.TrimSpace(value)
|
||
if value == "" {
|
||
return "--"
|
||
}
|
||
return html.EscapeString(value)
|
||
}
|
||
|
||
func platformObjectSummary(batch Batch) string {
|
||
if len(batch.Items) == 0 {
|
||
return ""
|
||
}
|
||
if batchItemCount(batch) > 5 {
|
||
return fmt.Sprintf("\u5171%d%s", batchItemCount(batch), objectCountUnit(batch.Items[0]))
|
||
}
|
||
codes := make([]string, 0, len(batch.Items))
|
||
for _, item := range batch.Items {
|
||
codes = append(codes, displayObjectCode(item))
|
||
}
|
||
return strings.Join(codes, "\u3001")
|
||
}
|