日志发送逻辑及测试消息发送接口

This commit is contained in:
2025-10-24 21:24:48 +08:00
parent 9d6876684b
commit 3fd97aa43f
14 changed files with 576 additions and 9 deletions

93
config.example.yml Normal file
View File

@@ -0,0 +1,93 @@
# 应用基础配置
app:
name: "PigFarmController" # 应用名称
version: "1.0.0" # 应用版本
jwt_secret: "your_jwt_secret_key_here" # JWT 签名密钥,请务必修改为强密码
# 服务器配置
server:
port: 8080 # 服务器监听端口
mode: "debug" # 运行模式: debug, release, test
# 日志配置
log:
level: "info" # 日志级别: debug, info, warn, error, dpanic, panic, fatal
format: "console" # 日志输出格式: console, json
enable_file: true # 是否同时输出到文件
file_path: "app_logs/pig_farm_controller.log" # 日志文件路径
max_size: 100 # 单个日志文件最大大小 (MB)
max_backups: 7 # 最多保留的旧日志文件数量
max_age: 7 # 最多保留的旧日志文件天数
compress: true # 是否压缩旧日志文件
# 数据库配置
database:
host: "localhost" # 数据库主机地址
port: 5432 # 数据库端口
username: "postgres" # 数据库用户名
password: "your_db_password" # 数据库密码
dbname: "pig_farm_controller_db" # 数据库名称
sslmode: "disable" # SSL模式: disable, require, verify-ca, verify-full
is_timescaledb: false # 是否为 TimescaleDB
max_open_conns: 100 # 最大开放连接数
max_idle_conns: 10 # 最大空闲连接数
conn_max_lifetime: 300 # 连接最大生命周期 (秒)
# WebSocket配置
websocket:
timeout: 60 # WebSocket请求超时时间 (秒)
heartbeat_interval: 30 # 心跳检测间隔 (秒)
# 心跳配置
heartbeat:
interval: 10 # 心跳间隔 (秒)
concurrency: 5 # 请求并发数
# ChirpStack API 配置
chirp_stack:
api_host: "http://localhost:8080" # ChirpStack API 主机地址
api_token: "your_chirpstack_api_token" # ChirpStack API Token
fport: 10 # ChirpStack FPort
api_timeout: 5 # API 请求超时时间 (秒)
collection_request_timeout: 10 # 采集请求超时时间 (秒)
# 任务调度配置
task:
interval: 5 # 任务调度间隔 (秒)
num_workers: 5 # 任务执行器并发工作数量
# Lora 配置
lora:
mode: "lora_mesh" # Lora 运行模式: lora_wan, lora_mesh
# Lora Mesh 配置
lora_mesh:
uart_port: "/dev/ttyUSB0" # UART 串口路径
baud_rate: 115200 # 波特率
timeout: 5 # 超时时间 (秒)
lora_mesh_mode: "transparent" # Lora Mesh 模式: transparent, command
max_chunk_size: 200 # 最大数据块大小
reassembly_timeout: 10 # 重组超时时间 (秒)
# 通知服务配置
notify:
primary: "log" # 首选通知渠道: smtp, wechat, lark, log (如果其他渠道未启用log 会自动成为首选)
failureThreshold: 2 # 连续失败多少次后触发广播模式
smtp:
enabled: false # 是否启用 SMTP 邮件通知
host: "smtp.example.com" # SMTP 服务器地址
port: 587 # SMTP 服务器端口
username: "your_email@example.com" # 发件人邮箱地址
password: "your_email_password" # 发件人邮箱授权码或密码
sender: "PigFarm Alarm <no-reply@example.com>" # 发件人名称和地址
wechat:
enabled: false # 是否启用企业微信通知
corpID: "wwxxxxxxxxxxxx" # 企业ID (CorpID)
agentID: "1000001" # 应用ID (AgentID)
secret: "your_wechat_app_secret" # 应用密钥 (Secret)
lark:
enabled: false # 是否启用飞书通知
appID: "cli_xxxxxxxxxx" # 应用 ID
appSecret: "your_lark_app_secret" # 应用密钥

View File

@@ -3923,6 +3923,64 @@ const docTemplate = `{
}
}
}
},
"/api/v1/users/{id}/notifications/test": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "为指定用户发送一条特定渠道的测试消息,以验证其配置是否正确。",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"用户管理"
],
"summary": "发送测试通知",
"parameters": [
{
"type": "integer",
"description": "用户ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "请求体",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.SendTestNotificationRequest"
}
}
],
"responses": {
"200": {
"description": "成功响应",
"schema": {
"allOf": [
{
"$ref": "#/definitions/controller.Response"
},
{
"type": "object",
"properties": {
"data": {
"type": "string"
}
}
}
]
}
}
}
}
}
},
"definitions": {
@@ -5591,6 +5649,22 @@ const docTemplate = `{
}
}
},
"dto.SendTestNotificationRequest": {
"type": "object",
"required": [
"type"
],
"properties": {
"type": {
"description": "Type 指定要测试的通知渠道\n@enum(smtp, wechat, lark, log)",
"allOf": [
{
"$ref": "#/definitions/notify.NotifierType"
}
]
}
}
},
"dto.SensorDataDTO": {
"type": "object",
"properties": {
@@ -6530,6 +6604,23 @@ const docTemplate = `{
"$ref": "#/definitions/models.SensorType"
}
}
},
"notify.NotifierType": {
"type": "string",
"enum": [
"smtp",
"wechat",
"lark",
"sms",
"log"
],
"x-enum-varnames": [
"NotifierTypeSMTP",
"NotifierTypeWeChat",
"NotifierTypeLark",
"NotifierTypeSMS",
"NotifierTypeLog"
]
}
},
"securityDefinitions": {

View File

@@ -3915,6 +3915,64 @@
}
}
}
},
"/api/v1/users/{id}/notifications/test": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "为指定用户发送一条特定渠道的测试消息,以验证其配置是否正确。",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"用户管理"
],
"summary": "发送测试通知",
"parameters": [
{
"type": "integer",
"description": "用户ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "请求体",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.SendTestNotificationRequest"
}
}
],
"responses": {
"200": {
"description": "成功响应",
"schema": {
"allOf": [
{
"$ref": "#/definitions/controller.Response"
},
{
"type": "object",
"properties": {
"data": {
"type": "string"
}
}
}
]
}
}
}
}
}
},
"definitions": {
@@ -5583,6 +5641,22 @@
}
}
},
"dto.SendTestNotificationRequest": {
"type": "object",
"required": [
"type"
],
"properties": {
"type": {
"description": "Type 指定要测试的通知渠道\n@enum(smtp, wechat, lark, log)",
"allOf": [
{
"$ref": "#/definitions/notify.NotifierType"
}
]
}
}
},
"dto.SensorDataDTO": {
"type": "object",
"properties": {
@@ -6522,6 +6596,23 @@
"$ref": "#/definitions/models.SensorType"
}
}
},
"notify.NotifierType": {
"type": "string",
"enum": [
"smtp",
"wechat",
"lark",
"sms",
"log"
],
"x-enum-varnames": [
"NotifierTypeSMTP",
"NotifierTypeWeChat",
"NotifierTypeLark",
"NotifierTypeSMS",
"NotifierTypeLog"
]
}
},
"securityDefinitions": {

View File

@@ -1125,6 +1125,17 @@ definitions:
- traderName
- unitPrice
type: object
dto.SendTestNotificationRequest:
properties:
type:
allOf:
- $ref: '#/definitions/notify.NotifierType'
description: |-
Type 指定要测试的通知渠道
@enum(smtp, wechat, lark, log)
required:
- type
type: object
dto.SensorDataDTO:
properties:
data:
@@ -1816,6 +1827,20 @@ definitions:
type:
$ref: '#/definitions/models.SensorType'
type: object
notify.NotifierType:
enum:
- smtp
- wechat
- lark
- sms
- log
type: string
x-enum-varnames:
- NotifierTypeSMTP
- NotifierTypeWeChat
- NotifierTypeLark
- NotifierTypeSMS
- NotifierTypeLog
info:
contact:
email: divano@example.com
@@ -4086,6 +4111,40 @@ paths:
summary: 获取指定用户的操作历史
tags:
- 用户管理
/api/v1/users/{id}/notifications/test:
post:
consumes:
- application/json
description: 为指定用户发送一条特定渠道的测试消息,以验证其配置是否正确。
parameters:
- description: 用户ID
in: path
name: id
required: true
type: integer
- description: 请求体
in: body
name: body
required: true
schema:
$ref: '#/definitions/dto.SendTestNotificationRequest'
produces:
- application/json
responses:
"200":
description: 成功响应
schema:
allOf:
- $ref: '#/definitions/controller.Response'
- properties:
data:
type: string
type: object
security:
- BearerAuth: []
summary: 发送测试通知
tags:
- 用户管理
/api/v1/users/login:
post:
consumes:

View File

@@ -28,6 +28,7 @@ import (
"git.huangwc.com/pig/pig-farm-controller/internal/app/webhook"
"git.huangwc.com/pig/pig-farm-controller/internal/domain/audit"
domain_device "git.huangwc.com/pig/pig-farm-controller/internal/domain/device"
domain_notify "git.huangwc.com/pig/pig-farm-controller/internal/domain/notify"
"git.huangwc.com/pig/pig-farm-controller/internal/domain/task"
"git.huangwc.com/pig/pig-farm-controller/internal/domain/token"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/config"
@@ -68,9 +69,9 @@ func NewAPI(cfg config.ServerConfig,
pigFarmService service.PigFarmService,
pigBatchService service.PigBatchService,
monitorService service.MonitorService,
userActionLogRepository repository.UserActionLogRepository,
tokenService token.TokenService,
auditService audit.Service,
notifyService domain_notify.Service,
deviceService domain_device.Service,
listenHandler webhook.ListenHandler,
analysisTaskManager *task.AnalysisPlanTaskManager) *API {
@@ -96,7 +97,7 @@ func NewAPI(cfg config.ServerConfig,
config: cfg,
listenHandler: listenHandler,
// 在 NewAPI 中初始化用户控制器,并将其作为 API 结构体的成员
userController: user.NewController(userRepo, monitorService, logger, tokenService),
userController: user.NewController(userRepo, monitorService, logger, tokenService, notifyService),
// 在 NewAPI 中初始化设备控制器,并将其作为 API 结构体的成员
deviceController: device.NewController(deviceRepository, areaControllerRepository, deviceTemplateRepository, deviceService, logger),
// 在 NewAPI 中初始化计划控制器,并将其作为 API 结构体的成员

View File

@@ -57,6 +57,7 @@ func (a *API) setupRoutes() {
userGroup := authGroup.Group("/users")
{
userGroup.GET("/:id/history", a.userController.ListUserHistory) // 获取用户操作历史
userGroup.POST("/:id/notifications/test", a.userController.SendTestNotification)
}
a.logger.Info("用户相关接口注册成功 (需要认证和审计)")

View File

@@ -7,6 +7,7 @@ import (
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller"
"git.huangwc.com/pig/pig-farm-controller/internal/app/dto"
"git.huangwc.com/pig/pig-farm-controller/internal/app/service"
domain_notify "git.huangwc.com/pig/pig-farm-controller/internal/domain/notify"
"git.huangwc.com/pig/pig-farm-controller/internal/domain/token"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
@@ -19,16 +20,24 @@ import (
type Controller struct {
userRepo repository.UserRepository
monitorService service.MonitorService
tokenService token.TokenService // 注入 token 服务
tokenService token.TokenService
notifyService domain_notify.Service
logger *logs.Logger
}
// NewController 创建用户控制器实例
func NewController(userRepo repository.UserRepository, monitorService service.MonitorService, logger *logs.Logger, tokenService token.TokenService) *Controller {
func NewController(
userRepo repository.UserRepository,
monitorService service.MonitorService,
logger *logs.Logger,
tokenService token.TokenService,
notifyService domain_notify.Service,
) *Controller {
return &Controller{
userRepo: userRepo,
monitorService: monitorService,
tokenService: tokenService,
notifyService: notifyService,
logger: logger,
}
}
@@ -192,3 +201,46 @@ func (c *Controller) ListUserHistory(ctx *gin.Context) {
c.logger.Infof("%s: 成功获取用户 %d 的操作历史, 数量: %d", actionType, userID, len(data))
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取用户操作历史成功", resp, actionType, "获取用户操作历史成功", opts)
}
// SendTestNotification godoc
// @Summary 发送测试通知
// @Description 为指定用户发送一条特定渠道的测试消息,以验证其配置是否正确。
// @Tags 用户管理
// @Security BearerAuth
// @Accept json
// @Produce json
// @Param id path int true "用户ID"
// @Param body body dto.SendTestNotificationRequest true "请求体"
// @Success 200 {object} controller.Response{data=string} "成功响应"
// @Router /api/v1/users/{id}/notifications/test [post]
func (c *Controller) SendTestNotification(ctx *gin.Context) {
const actionType = "发送测试通知"
// 1. 从 URL 中获取用户 ID
userID, err := strconv.ParseUint(ctx.Param("id"), 10, 32)
if err != nil {
c.logger.Errorf("%s: 无效的用户ID格式: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的用户ID格式", actionType, "无效的用户ID格式", ctx.Param("id"))
return
}
// 2. 从请求体 (JSON Body) 中获取要测试的通知类型
var req dto.SendTestNotificationRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "请求体格式错误或缺少 'type' 字段: "+err.Error(), actionType, "请求体绑定失败", req)
return
}
// 3. 调用领域服务
err = c.notifyService.SendTestMessage(uint(userID), req.Type)
if err != nil {
c.logger.Errorf("%s: 服务层调用失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "发送测试消息失败: "+err.Error(), actionType, "服务层调用失败", gin.H{"userID": userID, "type": req.Type})
return
}
// 4. 返回成功响应
c.logger.Infof("%s: 成功为用户 %d 发送类型为 %s 的测试消息", actionType, userID, req.Type)
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "测试消息已发送,请检查您的接收端。", nil, actionType, "测试消息发送成功", gin.H{"userID": userID, "type": req.Type})
}

View File

@@ -0,0 +1,10 @@
package dto
import "git.huangwc.com/pig/pig-farm-controller/internal/infra/notify"
// SendTestNotificationRequest 定义了发送测试通知请求的 JSON 结构
type SendTestNotificationRequest struct {
// Type 指定要测试的通知渠道
// @enum(smtp, wechat, lark, log)
Type notify.NotifierType `json:"type" binding:"required"`
}

View File

@@ -12,6 +12,7 @@ import (
"git.huangwc.com/pig/pig-farm-controller/internal/app/webhook"
"git.huangwc.com/pig/pig-farm-controller/internal/domain/audit"
"git.huangwc.com/pig/pig-farm-controller/internal/domain/device"
domain_notify "git.huangwc.com/pig/pig-farm-controller/internal/domain/notify"
"git.huangwc.com/pig/pig-farm-controller/internal/domain/pig"
"git.huangwc.com/pig/pig-farm-controller/internal/domain/task"
"git.huangwc.com/pig/pig-farm-controller/internal/domain/token"
@@ -19,6 +20,7 @@ import (
"git.huangwc.com/pig/pig-farm-controller/internal/infra/database"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/notify"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/transport"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/transport/lora"
@@ -41,6 +43,9 @@ type Application struct {
// Lora Mesh 监听器
loraMeshCommunicator transport.Listener
// 通知服务
NotifyService domain_notify.Service
}
// NewApplication 创建并初始化一个新的 Application 实例。
@@ -117,6 +122,12 @@ func NewApplication(configPath string) (*Application, error) {
// 初始化审计服务
auditService := audit.NewService(userActionLogRepo, logger)
// 初始化通知服务
notifyService, err := initNotifyService(cfg.Notify, logger, userRepo)
if err != nil {
return nil, fmt.Errorf("初始化通知服务失败: %w", err)
}
// --- 初始化 LoRa 相关组件 ---
var listenHandler webhook.ListenHandler
var comm transport.Communicator
@@ -176,9 +187,9 @@ func NewApplication(configPath string) (*Application, error) {
pigFarmService,
pigBatchService,
monitorService,
userActionLogRepo,
tokenService,
auditService,
notifyService,
generalDeviceService,
listenHandler,
analysisPlanTaskManager,
@@ -197,11 +208,92 @@ func NewApplication(configPath string) (*Application, error) {
pendingCollectionRepo: pendingCollectionRepo,
analysisPlanTaskManager: analysisPlanTaskManager,
loraMeshCommunicator: loraListener,
NotifyService: notifyService,
}
return app, nil
}
// initNotifyService 根据配置初始化并返回一个通知领域服务。
// 它确保至少有一个 LogNotifier 总是可用,并根据配置启用其他通知器。
func initNotifyService(
cfg config.NotifyConfig,
log *logs.Logger,
userRepo repository.UserRepository,
) (domain_notify.Service, error) {
var availableNotifiers []notify.Notifier
// 1. 总是创建 LogNotifier 作为所有告警的最终记录渠道
logNotifier := notify.NewLogNotifier(log)
availableNotifiers = append(availableNotifiers, logNotifier)
log.Info("Log通知器已启用 (作为所有告警的最终记录渠道)")
// 2. 根据配置,按需创建并收集所有启用的其他 Notifier 实例
if cfg.SMTP.Enabled {
smtpNotifier := notify.NewSMTPNotifier(
cfg.SMTP.Host,
cfg.SMTP.Port,
cfg.SMTP.Username,
cfg.SMTP.Password,
cfg.SMTP.Sender,
)
availableNotifiers = append(availableNotifiers, smtpNotifier)
log.Info("SMTP通知器已启用")
}
if cfg.WeChat.Enabled {
wechatNotifier := notify.NewWechatNotifier(
cfg.WeChat.CorpID,
cfg.WeChat.AgentID,
cfg.WeChat.Secret,
)
availableNotifiers = append(availableNotifiers, wechatNotifier)
log.Info("企业微信通知器已启用")
}
if cfg.Lark.Enabled {
larkNotifier := notify.NewLarkNotifier(
cfg.Lark.AppID,
cfg.Lark.AppSecret,
)
availableNotifiers = append(availableNotifiers, larkNotifier)
log.Info("飞书通知器已启用")
}
// 3. 动态确定首选通知器
var primaryNotifier notify.Notifier
primaryNotifierType := notify.NotifierType(cfg.Primary)
// 检查用户指定的主渠道是否已启用
for _, n := range availableNotifiers {
if n.Type() == primaryNotifierType {
primaryNotifier = n
break
}
}
// 如果用户指定的主渠道未启用或未指定,则自动选择第一个可用的 (这将是 LogNotifier如果其他都未启用)
if primaryNotifier == nil {
primaryNotifier = availableNotifiers[0] // 确保总能找到一个,因为 LogNotifier 总是存在的
log.Warnf("配置的首选渠道 '%s' 未启用或未指定,已自动降级使用 '%s' 作为首选渠道。", cfg.Primary, primaryNotifier.Type())
}
// 4. 使用创建的 Notifier 列表来组装领域服务
notifyService, err := domain_notify.NewFailoverService(
log,
userRepo,
availableNotifiers,
primaryNotifier.Type(),
cfg.FailureThreshold,
)
if err != nil {
return nil, fmt.Errorf("创建故障转移通知服务失败: %w", err)
}
log.Infof("通知服务初始化成功,首选渠道: %s, 故障阈值: %d", primaryNotifier.Type(), cfg.FailureThreshold)
return notifyService, nil
}
// Start 启动应用的所有组件并阻塞,直到接收到关闭信号。
func (app *Application) Start() error {
app.Logger.Info("应用启动中...")

View File

@@ -215,6 +215,8 @@ func getAddressForNotifier(notifierType notify.NotifierType, contact models.Cont
return contact.WeChat
case notify.NotifierTypeLark:
return contact.Feishu
case notify.NotifierTypeLog:
return "log" // LogNotifier不需要具体的地址但为了函数签名一致性返回一个无意义的非空字符串以绕过配置存在检查
default:
return ""
}

View File

@@ -41,6 +41,9 @@ type Config struct {
// LoraMesh LoraMesh配置
LoraMesh LoraMeshConfig `yaml:"lora_mesh"`
// Notify 通知服务配置
Notify NotifyConfig `yaml:"notify"`
}
// AppConfig 代表应用基础配置
@@ -158,6 +161,40 @@ type LoraMeshConfig struct {
ReassemblyTimeout int `yaml:"reassembly_timeout"`
}
// NotifyConfig 包含了所有与通知服务相关的配置
type NotifyConfig struct {
Primary string `yaml:"primary"` // 首选通知渠道 (e.g., "smtp", "wechat", "lark", "log")
FailureThreshold int `yaml:"failureThreshold"` // 连续失败多少次后触发广播模式
SMTP SMTPConfig `yaml:"smtp"`
WeChat WeChatConfig `yaml:"wechat"`
Lark LarkConfig `yaml:"lark"`
}
// SMTPConfig SMTP邮件配置
type SMTPConfig struct {
Enabled bool `yaml:"enabled"`
Host string `yaml:"host"`
Port int `yaml:"port"`
Username string `yaml:"username"`
Password string `yaml:"password"`
Sender string `yaml:"sender"`
}
// WeChatConfig 企业微信应用配置
type WeChatConfig struct {
Enabled bool `yaml:"enabled"`
CorpID string `yaml:"corpID"`
AgentID string `yaml:"agentID"`
Secret string `yaml:"secret"`
}
// LarkConfig 飞书应用配置
type LarkConfig struct {
Enabled bool `yaml:"enabled"`
AppID string `yaml:"appID"`
AppSecret string `yaml:"appSecret"`
}
// NewConfig 创建并返回一个新的配置实例
func NewConfig() *Config {
// 默认值可以在这里设置,但我们优先使用配置文件中的值

View File

@@ -5,7 +5,6 @@ import (
"encoding/json"
"fmt"
"net/http"
"strings"
"sync"
"time"
)

View File

@@ -0,0 +1,37 @@
package notify
import (
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
)
// logNotifier 实现了 Notifier 接口,用于将告警信息记录到日志中。
type logNotifier struct {
logger *logs.Logger
}
// NewLogNotifier 创建一个新的 logNotifier 实例。
// 它接收一个日志记录器,用于实际的日志输出。
func NewLogNotifier(logger *logs.Logger) Notifier {
return &logNotifier{
logger: logger,
}
}
// Send 将告警内容以结构化的方式记录到日志中。
// toAddr 参数在这里表示告警的预期接收者地址,也会被记录。
func (l *logNotifier) Send(content AlarmContent, toAddr string) error {
l.logger.Infow("告警已记录到日志",
"notifierType", NotifierTypeLog,
"title", content.Title,
"message", content.Message,
"level", content.Level.String(),
"timestamp", content.Timestamp.Format(DefaultTimeFormat),
"toAddr", toAddr,
)
return nil // 记录日志操作本身不应失败
}
// Type 返回通知器的类型。
func (l *logNotifier) Type() NotifierType {
return NotifierTypeLog
}

View File

@@ -14,11 +14,13 @@ type NotifierType string
const (
// NotifierTypeSMTP 表示 SMTP 邮件通知器。
NotifierTypeSMTP NotifierType = "邮件"
NotifierTypeSMTP NotifierType = "smtp"
// NotifierTypeWeChat 表示企业微信通知器。
NotifierTypeWeChat NotifierType = "企业微信"
NotifierTypeWeChat NotifierType = "wechat"
// NotifierTypeLark 表示飞书通知器。
NotifierTypeLark NotifierType = "飞书"
NotifierTypeLark NotifierType = "lark"
// NotifierTypeLog 表示日志通知器,作为最终的告警记录渠道。
NotifierTypeLog NotifierType = "log"
)
// AlarmContent 定义了通知的内容