重构部分枚举, 让models包不依赖其他项目中的包

This commit is contained in:
2025-11-07 21:39:24 +08:00
parent 375af57afe
commit 2796d9bad7
17 changed files with 188 additions and 148 deletions

View File

@@ -121,3 +121,9 @@
* 区域主控告警配置接口: `/api/v1/alarm/region-config`
* 普通设备告警配置接口: `/api/v1/alarm/device-config`
2. **接口职责**: 接口负责接收前端请求,调用应用服务层的阈值告警服务来完成实际的业务逻辑。
# 实现记录
1. 定义告警表和告警历史表
2. 重构部分枚举, 让models包不依赖其他项目中的包

View File

@@ -2,8 +2,6 @@ package dto
import (
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
"go.uber.org/zap/zapcore"
)
// NewListNotificationResponse 从模型数据创建通知列表响应 DTO
@@ -18,7 +16,7 @@ func NewListNotificationResponse(data []models.Notification, total int64, page,
UserID: item.UserID,
Title: item.Title,
Message: item.Message,
Level: zapcore.Level(item.Level),
Level: item.Level,
AlarmTimestamp: item.AlarmTimestamp,
ToAddress: item.ToAddress,
Status: item.Status,

View File

@@ -4,7 +4,6 @@ import (
"time"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/notify"
"go.uber.org/zap/zapcore"
)
@@ -12,7 +11,7 @@ import (
// SendTestNotificationRequest 定义了发送测试通知请求的 JSON 结构
type SendTestNotificationRequest struct {
// Type 指定要测试的通知渠道
Type notify.NotifierType `json:"type" validate:"required"`
Type models.NotifierType `json:"type" validate:"required"`
}
// ListNotificationRequest 定义了获取通知列表的请求参数
@@ -20,7 +19,7 @@ type ListNotificationRequest struct {
Page int `json:"page" query:"page"`
PageSize int `json:"page_size" query:"page_size"`
UserID *uint `json:"user_id" query:"user_id"`
NotifierType *notify.NotifierType `json:"notifier_type" query:"notifier_type"`
NotifierType *models.NotifierType `json:"notifier_type" query:"notifier_type"`
Status *models.NotificationStatus `json:"status" query:"status"`
Level *zapcore.Level `json:"level" query:"level"`
StartTime *time.Time `json:"start_time" query:"start_time"`
@@ -33,11 +32,11 @@ type NotificationDTO struct {
ID uint `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
NotifierType notify.NotifierType `json:"notifier_type"`
NotifierType models.NotifierType `json:"notifier_type"`
UserID uint `json:"user_id"`
Title string `json:"title"`
Message string `json:"message"`
Level zapcore.Level `json:"level"`
Level models.SeverityLevel `json:"level"`
AlarmTimestamp time.Time `json:"alarm_timestamp"`
ToAddress string `json:"to_address"`
Status models.NotificationStatus `json:"status"`

View File

@@ -353,7 +353,7 @@ func initNotifyService(
// 3. 动态确定首选通知器
var primaryNotifier notify.Notifier
primaryNotifierType := notify.NotifierType(cfg.Primary)
primaryNotifierType := models.NotifierType(cfg.Primary)
// 检查用户指定的主渠道是否已启用
for _, n := range availableNotifiers {

View File

@@ -11,8 +11,6 @@ import (
"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"
"go.uber.org/zap"
)
// Service 定义了通知领域的核心业务逻辑接口
@@ -24,14 +22,14 @@ type Service interface {
BroadcastAlarm(ctx context.Context, content notify.AlarmContent) error
// SendTestMessage 向指定用户发送一条测试消息,用于手动验证特定通知渠道的配置。
SendTestMessage(ctx context.Context, userID uint, notifierType notify.NotifierType) error
SendTestMessage(ctx context.Context, userID uint, notifierType models.NotifierType) error
}
// failoverService 是 Service 接口的实现,提供了故障转移功能
type failoverService struct {
ctx context.Context
userRepo repository.UserRepository
notifiers map[notify.NotifierType]notify.Notifier
notifiers map[models.NotifierType]notify.Notifier
primaryNotifier notify.Notifier
failureThreshold int
failureCounters *sync.Map // 使用 sync.Map 来安全地并发读写失败计数, key: userID (uint), value: counter (int)
@@ -43,11 +41,11 @@ func NewFailoverService(
ctx context.Context,
userRepo repository.UserRepository,
notifiers []notify.Notifier,
primaryNotifierType notify.NotifierType,
primaryNotifierType models.NotifierType,
failureThreshold int,
notificationRepo repository.NotificationRepository,
) (Service, error) {
notifierMap := make(map[notify.NotifierType]notify.Notifier)
notifierMap := make(map[models.NotifierType]notify.Notifier)
for _, n := range notifiers {
notifierMap[n.Type()] = n
}
@@ -189,7 +187,7 @@ func (s *failoverService) sendAlarmToUser(ctx context.Context, userID uint, cont
}
// SendTestMessage 实现了手动发送测试消息的功能
func (s *failoverService) SendTestMessage(ctx context.Context, userID uint, notifierType notify.NotifierType) error {
func (s *failoverService) SendTestMessage(ctx context.Context, userID uint, notifierType models.NotifierType) error {
serviceCtx, logger := logs.Trace(ctx, s.ctx, "SendTestMessage")
user, err := s.userRepo.FindByID(serviceCtx, userID)
if err != nil {
@@ -210,7 +208,7 @@ func (s *failoverService) SendTestMessage(ctx context.Context, userID uint, noti
s.recordNotificationAttempt(serviceCtx, userID, notifierType, notify.AlarmContent{
Title: "通知服务测试",
Message: fmt.Sprintf("这是一条来自【%s】渠道的测试消息。如果您收到此消息说明您的配置正确。", notifierType),
Level: zap.InfoLevel,
Level: models.InfoLevel,
Timestamp: time.Now(),
}, "", models.NotificationStatusFailed, fmt.Errorf("用户未配置通知方式 '%s' 的地址", notifierType))
return fmt.Errorf("用户未配置通知方式 '%s' 的地址", notifierType)
@@ -219,7 +217,7 @@ func (s *failoverService) SendTestMessage(ctx context.Context, userID uint, noti
testContent := notify.AlarmContent{
Title: "通知服务测试",
Message: fmt.Sprintf("这是一条来自【%s】渠道的测试消息。如果您收到此消息说明您的配置正确。", notifierType),
Level: zap.InfoLevel,
Level: models.InfoLevel,
Timestamp: time.Now(),
}
@@ -239,15 +237,15 @@ func (s *failoverService) SendTestMessage(ctx context.Context, userID uint, noti
}
// getAddressForNotifier 是一个辅助函数,根据通知器类型从 ContactInfo 中获取对应的地址
func getAddressForNotifier(notifierType notify.NotifierType, contact models.ContactInfo) string {
func getAddressForNotifier(notifierType models.NotifierType, contact models.ContactInfo) string {
switch notifierType {
case notify.NotifierTypeSMTP:
case models.NotifierTypeSMTP:
return contact.Email
case notify.NotifierTypeWeChat:
case models.NotifierTypeWeChat:
return contact.WeChat
case notify.NotifierTypeLark:
case models.NotifierTypeLark:
return contact.Feishu
case notify.NotifierTypeLog:
case models.NotifierTypeLog:
return "log" // LogNotifier不需要具体的地址但为了函数签名一致性返回一个无意义的非空字符串以绕过配置存在检查
default:
return ""
@@ -264,7 +262,7 @@ func getAddressForNotifier(notifierType notify.NotifierType, contact models.Cont
func (s *failoverService) recordNotificationAttempt(
ctx context.Context,
userID uint,
notifierType notify.NotifierType,
notifierType models.NotifierType,
content notify.AlarmContent,
toAddress string,
status models.NotificationStatus,
@@ -281,7 +279,7 @@ func (s *failoverService) recordNotificationAttempt(
UserID: userID,
Title: content.Title,
Message: content.Message,
Level: models.LogLevel(content.Level),
Level: content.Level,
AlarmTimestamp: content.Timestamp,
ToAddress: toAddress,
Status: status,

View File

@@ -13,7 +13,7 @@ type ActiveAlarm struct {
FarmID uint `gorm:"not null;comment:猪场ID" json:"farm_id,omitempty"`
DeviceID uint `gorm:"not null;comment:设备ID" json:"device_id,omitempty"`
AlarmType string `gorm:"comment:告警类型" json:"alarm_type,omitempty"`
AlarmLevel string `gorm:"comment:告警级别" json:"alarm_level,omitempty"`
AlarmLevel SeverityLevel `gorm:"comment:告警级别" json:"alarm_level,omitempty"`
AlarmContent string `gorm:"comment:告警内容描述" json:"alarm_content,omitempty"`
TriggerTime time.Time `gorm:"not null;comment:告警触发时间" json:"trigger_time"`
IsAcknowledged bool `gorm:"default:false;comment:是否已确认" json:"is_acknowledged,omitempty"`
@@ -36,7 +36,7 @@ type HistoricalAlarm struct {
FarmID uint `gorm:"not null;comment:猪场ID" json:"farm_id,omitempty"`
DeviceID uint `gorm:"not null;comment:设备ID" json:"device_id,omitempty"`
AlarmType string `gorm:"comment:告警类型" json:"alarm_type,omitempty"`
AlarmLevel string `gorm:"comment:告警级别" json:"alarm_level,omitempty"`
AlarmLevel SeverityLevel `gorm:"comment:告警级别" json:"alarm_level,omitempty"`
AlarmContent string `gorm:"comment:告警内容描述" json:"alarm_content,omitempty"`
TriggerTime time.Time `gorm:"not null;comment:告警触发时间" json:"trigger_time"`
ResolveTime time.Time `gorm:"not null;comment:告警解决时间" json:"resolve_time"`

View File

@@ -5,12 +5,41 @@ import (
"errors"
"fmt"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/utils/command_generater"
"gorm.io/datatypes"
"gorm.io/gorm"
)
// ModbusFunctionCode 定义Modbus功能码的枚举类型
type ModbusFunctionCode byte
// 定义常用的Modbus功能码常量及其应用场景
const (
// ReadCoils 读取线圈状态 (0x01)
// 场景: 用于读取数字量输出DO或内部标志位的当前状态这些状态通常是开关量。
ReadCoils ModbusFunctionCode = 0x01
// ReadDiscreteInputs 读取离散输入状态 (0x02)
// 场景: 用于读取数字量输入DI的当前状态这些状态通常是外部传感器的开关量信号。
ReadDiscreteInputs ModbusFunctionCode = 0x02
// ReadHoldingRegisters 读取保持寄存器 (0x03)
// 场景: 用于读取设备内部可读写的参数或数据,例如温度设定值、电机速度等模拟量或配置数据。
ReadHoldingRegisters ModbusFunctionCode = 0x03
// ReadInputRegisters 读取输入寄存器 (0x04)
// 场景: 用于读取设备的模拟量输入AI数据这些数据通常是只读的例如当前温度、压力、电压等实时测量值。
ReadInputRegisters ModbusFunctionCode = 0x04
// WriteSingleCoil 写入单个线圈 (0x05)
// 场景: 用于控制单个数字量输出DO例如打开或关闭一个继电器、指示灯等。
WriteSingleCoil ModbusFunctionCode = 0x05
// WriteSingleRegister 写入单个保持寄存器 (0x06)
// 场景: 用于修改设备内部的单个可写参数,例如设置一个温度控制器的目标温度、调整一个阀门的开度等。
WriteSingleRegister ModbusFunctionCode = 0x06
// WriteMultipleCoils 写入多个线圈 (0x0F)
// 场景: 用于批量控制多个数字量输出DO例如同时打开或关闭一组继电器。
WriteMultipleCoils ModbusFunctionCode = 0x0F
// WriteMultipleRegisters 写入多个保持寄存器 (0x10)
// 场景: 用于批量修改设备内部的多个可写参数,例如一次性更新多个配置参数或模拟量输出值。
WriteMultipleRegisters ModbusFunctionCode = 0x10
)
// DeviceCategory 定义了设备模板的宽泛类别
type DeviceCategory string
@@ -51,7 +80,7 @@ func (sc *SwitchCommands) SelfCheck() error {
// SensorCommands 定义了传感器读取指令所需的Modbus参数
type SensorCommands struct {
// ModbusFunctionCode 记录Modbus功能码例如 ReadHoldingRegisters。(一般是第二字节)
ModbusFunctionCode command_generater.ModbusFunctionCode `json:"modbus_function_code"`
ModbusFunctionCode ModbusFunctionCode `json:"modbus_function_code"`
// ModbusStartAddress 记录Modbus寄存器的起始地址用于生成指令。(一般是第三到四字节)
ModbusStartAddress uint16 `json:"modbus_start_address"`
// ModbusQuantity 记录Modbus寄存器的数量用于生成指令。(一般是五到六字节)
@@ -62,7 +91,7 @@ type SensorCommands struct {
func (sc *SensorCommands) SelfCheck() error {
// 校验ModbusFunctionCode是否为读取类型
switch sc.ModbusFunctionCode {
case command_generater.ReadCoils, command_generater.ReadDiscreteInputs, command_generater.ReadHoldingRegisters, command_generater.ReadInputRegisters:
case ReadCoils, ReadDiscreteInputs, ReadHoldingRegisters, ReadInputRegisters:
// 支持的读取功能码
default:
return fmt.Errorf("'sensor' 指令集 ModbusFunctionCode %X 无效或不是读取类型", sc.ModbusFunctionCode)

View File

@@ -154,7 +154,6 @@ func (PendingCollection) TableName() string {
}
// --- 用户审计日志 ---
// TODO 这些变量放这个包合适吗?
// --- 审计日志状态常量 ---
type AuditStatus string

View File

@@ -6,6 +6,8 @@ import (
"fmt"
"strconv"
"strings"
"go.uber.org/zap/zapcore"
)
// GetAllModels 返回一个包含所有数据库模型实例的切片。
@@ -129,3 +131,68 @@ func (a *UintArray) Scan(src interface{}) error {
*a = arr
return nil
}
// SeverityLevel 定义了系统中告警、通知、日志的统一级别枚举。
// 它以中文形式存储在数据库中,提高了可读性。
type SeverityLevel string
const (
// DebugLevel 调试级别,用于开发和诊断问题。
DebugLevel SeverityLevel = "Debug"
// InfoLevel 信息级别,用于记录常规操作。
InfoLevel SeverityLevel = "Info"
// WarnLevel 警告级别,表示出现潜在问题,需要关注。
WarnLevel SeverityLevel = "Warn"
// ErrorLevel 错误级别,表示发生了需要处理的错误。
ErrorLevel SeverityLevel = "Error"
// DPanicLevel 开发时崩溃级别,在开发模式下会触发 panic。
DPanicLevel SeverityLevel = "DPanic"
// PanicLevel 崩溃级别,记录日志后会立即触发 panic。
PanicLevel SeverityLevel = "Panic"
// FatalLevel 致命级别,记录日志后会调用 os.Exit(1) 退出程序。
FatalLevel SeverityLevel = "Fatal"
)
// ToZapLevel 将我们的自定义级别转换为 zapcore.Level以便与日志记录器兼容。
func (al SeverityLevel) ToZapLevel() zapcore.Level {
switch al {
case DebugLevel:
return zapcore.DebugLevel
case InfoLevel:
return zapcore.InfoLevel
case WarnLevel:
return zapcore.WarnLevel
case ErrorLevel:
return zapcore.ErrorLevel
case DPanicLevel:
return zapcore.DPanicLevel
case PanicLevel:
return zapcore.PanicLevel
case FatalLevel:
return zapcore.FatalLevel
default:
// 默认情况下返回 Info 级别,保证程序健壮性
return zapcore.InfoLevel
}
}
// Scan 实现了 sql.Scanner 接口GORM 在从数据库读取数据时会调用此方法。
func (al *SeverityLevel) Scan(value interface{}) error {
bytes, ok := value.([]byte)
if !ok {
// 尝试处理其他可能的类型,例如字符串
s, ok := value.(string)
if !ok {
return fmt.Errorf("无法将值 %v (类型 %T) 扫描为 SeverityLevel", value, value)
}
*al = SeverityLevel(s)
return nil
}
*al = SeverityLevel(bytes)
return nil
}
// Value 实现了 driver.Valuer 接口GORM 在将数据写入数据库时会调用此方法。
func (al SeverityLevel) Value() (driver.Value, error) {
return string(al), nil
}

View File

@@ -1,15 +1,25 @@
package models
import (
"database/sql/driver"
"errors"
"time"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/notify"
"go.uber.org/zap/zapcore"
"gorm.io/gorm"
)
// NotifierType 定义了通知器的类型。
type NotifierType string
const (
// NotifierTypeSMTP 表示 SMTP 邮件通知器。
NotifierTypeSMTP NotifierType = "邮件"
// NotifierTypeWeChat 表示企业微信通知器。
NotifierTypeWeChat NotifierType = "企业微信"
// NotifierTypeLark 表示飞书通知器。
NotifierTypeLark NotifierType = "飞书"
// NotifierTypeLog 表示日志通知器,作为最终的告警记录渠道。
NotifierTypeLog NotifierType = "日志"
)
// NotificationStatus 定义了通知发送尝试的状态枚举。
type NotificationStatus string
@@ -19,40 +29,12 @@ const (
NotificationStatusSkipped NotificationStatus = "已跳过" // 通知因某些原因被跳过(例如:用户未配置联系方式)
)
// LogLevel is a custom type for zapcore.Level to handle database scanning and valuing.
type LogLevel zapcore.Level
// Scan implements the sql.Scanner interface.
func (l *LogLevel) Scan(value interface{}) error {
var s string
switch v := value.(type) {
case []byte:
s = string(v)
case string:
s = v
default:
return errors.New("LogLevel的类型无效")
}
var zl zapcore.Level
if err := zl.UnmarshalText([]byte(s)); err != nil {
return err
}
*l = LogLevel(zl)
return nil
}
// Value implements the driver.Valuer interface.
func (l LogLevel) Value() (driver.Value, error) {
return (zapcore.Level)(l).String(), nil
}
// Notification 表示已发送或尝试发送的通知记录。
type Notification struct {
gorm.Model
// NotifierType 通知器类型 (例如:"邮件", "企业微信", "飞书", "日志")
NotifierType notify.NotifierType `gorm:"type:varchar(20);not null;index" json:"notifier_type"`
NotifierType NotifierType `gorm:"type:varchar(20);not null;index" json:"notifier_type"`
// UserID 接收通知的用户ID用于追溯通知记录到特定用户
UserID uint `gorm:"index" json:"user_id"` // 增加 UserID 字段,并添加索引
// Title 通知标题
@@ -60,7 +42,7 @@ type Notification struct {
// Message 通知内容
Message string `gorm:"type:text;not null" json:"message"`
// Level 通知级别 (例如INFO, WARN, ERROR)
Level LogLevel `gorm:"type:varchar(10);not null" json:"level"`
Level SeverityLevel `gorm:"type:varchar(10);not null" json:"level"`
// AlarmTimestamp 通知内容生成时的时间戳,与 ID 构成复合主键
AlarmTimestamp time.Time `gorm:"primaryKey;not null" json:"alarm_timestamp"`
// ToAddress 接收地址 (例如:邮箱地址, 企业微信ID, 日志标识符)

View File

@@ -10,6 +10,7 @@ import (
"time"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
)
const (
@@ -65,7 +66,7 @@ func (l *larkNotifier) Send(ctx context.Context, content AlarmContent, toAddr st
"tag": "lark_md",
"content": fmt.Sprintf("## %s\n**级别**: %s\n**时间**: %s\n\n%s",
content.Title,
content.Level.String(),
content.Level,
content.Timestamp.Format(DefaultTimeFormat),
content.Message,
),
@@ -171,8 +172,8 @@ func (l *larkNotifier) getAccessToken(ctx context.Context) (string, error) {
}
// Type 返回通知器的类型
func (l *larkNotifier) Type() NotifierType {
return NotifierTypeLark
func (l *larkNotifier) Type() models.NotifierType {
return models.NotifierTypeLark
}
// --- API 数据结构 ---

View File

@@ -4,6 +4,7 @@ import (
"context"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
)
// logNotifier 实现了 Notifier 接口,用于将告警信息记录到日志中。
@@ -24,10 +25,10 @@ func NewLogNotifier(ctx context.Context) Notifier {
func (l *logNotifier) Send(ctx context.Context, content AlarmContent, toAddr string) error {
logger := logs.TraceLogger(ctx, l.ctx, "Send")
logger.Infow("告警已记录到日志",
"notifierType", NotifierTypeLog,
"notifierType", models.NotifierTypeLog,
"title", content.Title,
"message", content.Message,
"level", content.Level.String(),
"level", content.Level,
"timestamp", content.Timestamp.Format(DefaultTimeFormat),
"toAddr", toAddr,
)
@@ -35,6 +36,6 @@ func (l *logNotifier) Send(ctx context.Context, content AlarmContent, toAddr str
}
// Type 返回通知器的类型。
func (l *logNotifier) Type() NotifierType {
return NotifierTypeLog
func (l *logNotifier) Type() models.NotifierType {
return models.NotifierTypeLog
}

View File

@@ -4,26 +4,12 @@ import (
"context"
"time"
"go.uber.org/zap/zapcore"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
)
// DefaultTimeFormat 定义了所有通知中统一使用的时间格式。
const DefaultTimeFormat = "2006-01-02 15:04:05"
// NotifierType 定义了通知器的类型。
type NotifierType string
const (
// NotifierTypeSMTP 表示 SMTP 邮件通知器。
NotifierTypeSMTP NotifierType = "邮件"
// NotifierTypeWeChat 表示企业微信通知器。
NotifierTypeWeChat NotifierType = "企业微信"
// NotifierTypeLark 表示飞书通知器。
NotifierTypeLark NotifierType = "飞书"
// NotifierTypeLog 表示日志通知器,作为最终的告警记录渠道。
NotifierTypeLog NotifierType = "日志"
)
// AlarmContent 定义了通知的内容
type AlarmContent struct {
// 通知标题
@@ -31,7 +17,7 @@ type AlarmContent struct {
// 通知信息
Message string
// 通知级别
Level zapcore.Level
Level models.SeverityLevel
// 通知时间
Timestamp time.Time
}
@@ -41,5 +27,5 @@ type Notifier interface {
// Send 发送通知
Send(ctx context.Context, content AlarmContent, toAddr string) error
// Type 返回通知器的类型
Type() NotifierType
Type() models.NotifierType
}

View File

@@ -5,6 +5,8 @@ import (
"fmt"
"net/smtp"
"strings"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
)
// smtpNotifier 实现了 Notifier 接口,用于通过 SMTP 发送邮件通知。
@@ -45,7 +47,7 @@ func (s *smtpNotifier) Send(ctx context.Context, content AlarmContent, toAddr st
// 邮件正文
body := fmt.Sprintf("级别: %s\n时间: %s\n\n%s",
content.Level.String(),
content.Level,
content.Timestamp.Format(DefaultTimeFormat),
content.Message,
)
@@ -71,6 +73,6 @@ func (s *smtpNotifier) Send(ctx context.Context, content AlarmContent, toAddr st
}
// Type 返回通知器的类型
func (s *smtpNotifier) Type() NotifierType {
return NotifierTypeSMTP
func (s *smtpNotifier) Type() models.NotifierType {
return models.NotifierTypeSMTP
}

View File

@@ -9,6 +9,8 @@ import (
"strings"
"sync"
"time"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
)
const (
@@ -55,7 +57,7 @@ func (w *wechatNotifier) Send(ctx context.Context, content AlarmContent, toAddr
// 2. 构建 markdown 内容
markdownContent := fmt.Sprintf("## %s\n> 级别: <font color=\"warning\">%s</font>\n> 时间: %s\n\n%s",
content.Title,
content.Level.String(),
content.Level,
content.Timestamp.Format(DefaultTimeFormat),
content.Message,
)
@@ -142,8 +144,8 @@ func (w *wechatNotifier) getAccessToken() (string, error) {
}
// Type 返回通知器的类型
func (w *wechatNotifier) Type() NotifierType {
return NotifierTypeWeChat
func (w *wechatNotifier) Type() models.NotifierType {
return models.NotifierTypeWeChat
}
// --- API 数据结构 ---

View File

@@ -6,7 +6,6 @@ import (
"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"
"go.uber.org/zap/zapcore"
"gorm.io/gorm"
@@ -15,7 +14,7 @@ import (
// NotificationListOptions 定义了查询通知列表时的可选参数
type NotificationListOptions struct {
UserID *uint // 按用户ID过滤
NotifierType *notify.NotifierType // 按通知器类型过滤
NotifierType *models.NotifierType // 按通知器类型过滤
Status *models.NotificationStatus // 按通知状态过滤 (例如:"success", "failed")
Level *zapcore.Level // 按通知等级过滤 (例如:"info", "warning", "error")
StartTime *time.Time // 通知内容生成时间范围 - 开始时间 (对应 AlarmTimestamp)

View File

@@ -3,37 +3,8 @@ package command_generater
import (
"encoding/binary"
"fmt"
)
// ModbusFunctionCode 定义Modbus功能码的枚举类型
type ModbusFunctionCode byte
// 定义常用的Modbus功能码常量及其应用场景
const (
// ReadCoils 读取线圈状态 (0x01)
// 场景: 用于读取数字量输出DO或内部标志位的当前状态这些状态通常是开关量。
ReadCoils ModbusFunctionCode = 0x01
// ReadDiscreteInputs 读取离散输入状态 (0x02)
// 场景: 用于读取数字量输入DI的当前状态这些状态通常是外部传感器的开关量信号。
ReadDiscreteInputs ModbusFunctionCode = 0x02
// ReadHoldingRegisters 读取保持寄存器 (0x03)
// 场景: 用于读取设备内部可读写的参数或数据,例如温度设定值、电机速度等模拟量或配置数据。
ReadHoldingRegisters ModbusFunctionCode = 0x03
// ReadInputRegisters 读取输入寄存器 (0x04)
// 场景: 用于读取设备的模拟量输入AI数据这些数据通常是只读的例如当前温度、压力、电压等实时测量值。
ReadInputRegisters ModbusFunctionCode = 0x04
// WriteSingleCoil 写入单个线圈 (0x05)
// 场景: 用于控制单个数字量输出DO例如打开或关闭一个继电器、指示灯等。
WriteSingleCoil ModbusFunctionCode = 0x05
// WriteSingleRegister 写入单个保持寄存器 (0x06)
// 场景: 用于修改设备内部的单个可写参数,例如设置一个温度控制器的目标温度、调整一个阀门的开度等。
WriteSingleRegister ModbusFunctionCode = 0x06
// WriteMultipleCoils 写入多个线圈 (0x0F)
// 场景: 用于批量控制多个数字量输出DO例如同时打开或关闭一组继电器。
WriteMultipleCoils ModbusFunctionCode = 0x0F
// WriteMultipleRegisters 写入多个保持寄存器 (0x10)
// 场景: 用于批量修改设备内部的多个可写参数,例如一次性更新多个配置参数或模拟量输出值。
WriteMultipleRegisters ModbusFunctionCode = 0x10
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
)
// GenerateModbusRTUReadCommand 生成Modbus RTU读取指令
@@ -52,7 +23,7 @@ const (
//
// []byte: 完整的Modbus RTU指令字节切片。
// error: 如果参数无效或生成过程中出现错误,则返回错误信息。
func GenerateModbusRTUReadCommand(slaveAddress uint8, functionCode ModbusFunctionCode, startAddress uint16, quantity uint16) ([]byte, error) {
func GenerateModbusRTUReadCommand(slaveAddress uint8, functionCode models.ModbusFunctionCode, startAddress uint16, quantity uint16) ([]byte, error) {
// 1. 校验输入参数
if slaveAddress == 0 || slaveAddress > 247 {
return nil, fmt.Errorf("从站地址无效: %d, 必须在1-247之间", slaveAddress)
@@ -60,9 +31,9 @@ func GenerateModbusRTUReadCommand(slaveAddress uint8, functionCode ModbusFunctio
// 校验功能码是否为读取类型
switch functionCode {
case ReadCoils, ReadDiscreteInputs, ReadHoldingRegisters, ReadInputRegisters:
case models.ReadCoils, models.ReadDiscreteInputs, models.ReadHoldingRegisters, models.ReadInputRegisters:
// 这些是支持的读取功能码
case WriteSingleCoil, WriteSingleRegister, WriteMultipleCoils, WriteMultipleRegisters:
case models.WriteSingleCoil, models.WriteSingleRegister, models.WriteMultipleCoils, models.WriteMultipleRegisters:
return nil, fmt.Errorf("功能码 %X 是写入操作,请使用 GenerateModbusRTUWriteCoilCommand 或其他写入函数", functionCode)
default:
return nil, fmt.Errorf("不支持的功能码: %X", functionCode)
@@ -130,7 +101,7 @@ func GenerateModbusRTUSwitchCommand(slaveAddress uint8, coilAddress uint16, onOf
// 2. 构建PDU (协议数据单元) for WriteSingleCoil (0x05)
// PDU结构: 功能码 (1字节) + 线圈地址 (2字节) + 写入值 (2字节)
pdu := make([]byte, 5)
pdu[0] = byte(WriteSingleCoil)
pdu[0] = byte(models.WriteSingleCoil)
// Modbus协议中地址和值都是大端字节序 (高位在前)
binary.BigEndian.PutUint16(pdu[1:3], coilAddress)
binary.BigEndian.PutUint16(pdu[3:5], writeValue)