chore: 并入 iotlcm-admin-svr 历史到 admin-svr/(原提交 49818c6)
This commit is contained in:
@@ -0,0 +1,448 @@
|
||||
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")
|
||||
}
|
||||
Reference in New Issue
Block a user