Files
iotlcm/admin-svr/server/internal/logic/warningnotify/content.go
T

449 lines
10 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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")
}