diff --git a/docs/docs.go b/docs/docs.go index 7be6316..5147fa8 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -23,6 +23,339 @@ const docTemplate = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { + "/api/v1/alarm/threshold/active-alarms": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "根据过滤条件和分页参数查询活跃告警列表", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "告警管理" + ], + "summary": "批量查询活跃告警", + "parameters": [ + { + "type": "string", + "description": "告警触发时间范围 - 结束时间", + "name": "end_time", + "in": "query" + }, + { + "type": "boolean", + "description": "按是否被忽略过滤", + "name": "is_ignored", + "in": "query" + }, + { + "enum": [ + "Debug", + "Info", + "Warn", + "Error", + "DPanic", + "Panic", + "Fatal" + ], + "type": "string", + "x-enum-varnames": [ + "DebugLevel", + "InfoLevel", + "WarnLevel", + "ErrorLevel", + "DPanicLevel", + "PanicLevel", + "FatalLevel" + ], + "description": "按告警严重性等级过滤", + "name": "level", + "in": "query" + }, + { + "type": "string", + "description": "排序字段,例如 \"trigger_time DESC\"", + "name": "order_by", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "page_size", + "in": "query" + }, + { + "type": "integer", + "description": "按告警来源ID过滤", + "name": "source_id", + "in": "query" + }, + { + "enum": [ + "普通设备", + "区域主控", + "系统" + ], + "type": "string", + "x-enum-varnames": [ + "AlarmSourceTypeDevice", + "AlarmSourceTypeAreaController", + "AlarmSourceTypeSystem" + ], + "description": "按告警来源类型过滤", + "name": "source_type", + "in": "query" + }, + { + "type": "string", + "description": "告警触发时间范围 - 开始时间", + "name": "trigger_time", + "in": "query" + } + ], + "responses": { + "200": { + "description": "成功获取活跃告警列表", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.ListActiveAlarmResponse" + } + } + } + ] + } + } + } + } + }, + "/api/v1/alarm/threshold/historical-alarms": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "根据过滤条件和分页参数查询历史告警列表", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "告警管理" + ], + "summary": "批量查询历史告警", + "parameters": [ + { + "enum": [ + "Debug", + "Info", + "Warn", + "Error", + "DPanic", + "Panic", + "Fatal" + ], + "type": "string", + "x-enum-varnames": [ + "DebugLevel", + "InfoLevel", + "WarnLevel", + "ErrorLevel", + "DPanicLevel", + "PanicLevel", + "FatalLevel" + ], + "description": "按告警严重性等级过滤", + "name": "level", + "in": "query" + }, + { + "type": "string", + "description": "排序字段,例如 \"trigger_time DESC\"", + "name": "order_by", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "page_size", + "in": "query" + }, + { + "type": "string", + "description": "告警解决时间范围 - 结束时间", + "name": "resolve_time_end", + "in": "query" + }, + { + "type": "string", + "description": "告警解决时间范围 - 开始时间", + "name": "resolve_time_start", + "in": "query" + }, + { + "type": "integer", + "description": "按告警来源ID过滤", + "name": "source_id", + "in": "query" + }, + { + "enum": [ + "普通设备", + "区域主控", + "系统" + ], + "type": "string", + "x-enum-varnames": [ + "AlarmSourceTypeDevice", + "AlarmSourceTypeAreaController", + "AlarmSourceTypeSystem" + ], + "description": "按告警来源类型过滤", + "name": "source_type", + "in": "query" + }, + { + "type": "string", + "description": "告警触发时间范围 - 结束时间", + "name": "trigger_time_end", + "in": "query" + }, + { + "type": "string", + "description": "告警触发时间范围 - 开始时间", + "name": "trigger_time_start", + "in": "query" + } + ], + "responses": { + "200": { + "description": "成功获取历史告警列表", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.ListHistoricalAlarmResponse" + } + } + } + ] + } + } + } + } + }, + "/api/v1/alarm/threshold/{id}/cancel-snooze": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "根据告警ID取消对一个阈值告警的忽略状态", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "告警管理" + ], + "summary": "取消忽略阈值告警", + "parameters": [ + { + "type": "string", + "description": "告警ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "成功取消忽略告警", + "schema": { + "$ref": "#/definitions/controller.Response" + } + } + } + } + }, + "/api/v1/alarm/threshold/{id}/snooze": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "根据告警ID忽略一个活跃的阈值告警,或更新其忽略时间", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "告警管理" + ], + "summary": "忽略阈值告警", + "parameters": [ + { + "type": "string", + "description": "告警ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "忽略告警请求体", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.SnoozeAlarmRequest" + } + } + ], + "responses": { + "200": { + "description": "成功忽略告警", + "schema": { + "$ref": "#/definitions/controller.Response" + } + } + } + } + }, "/api/v1/area-controllers": { "get": { "security": [ @@ -4170,6 +4503,50 @@ const docTemplate = `{ "CodeServiceUnavailable" ] }, + "dto.ActiveAlarmDTO": { + "type": "object", + "properties": { + "alarm_code": { + "$ref": "#/definitions/models.AlarmCode" + }, + "alarm_details": { + "type": "string" + }, + "alarm_summary": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "ignored_until": { + "type": "string" + }, + "is_ignored": { + "type": "boolean" + }, + "last_notified_at": { + "type": "string" + }, + "level": { + "$ref": "#/definitions/models.SeverityLevel" + }, + "source_id": { + "type": "integer" + }, + "source_type": { + "$ref": "#/definitions/models.AlarmSourceType" + }, + "trigger_time": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, "dto.AreaControllerResponse": { "type": "object", "properties": { @@ -4589,6 +4966,58 @@ const docTemplate = `{ } } }, + "dto.HistoricalAlarmDTO": { + "type": "object", + "properties": { + "alarm_code": { + "$ref": "#/definitions/models.AlarmCode" + }, + "alarm_details": { + "type": "string" + }, + "alarm_summary": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "level": { + "$ref": "#/definitions/models.SeverityLevel" + }, + "resolve_method": { + "type": "string" + }, + "resolve_time": { + "type": "string" + }, + "resolved_by": { + "type": "integer" + }, + "source_id": { + "type": "integer" + }, + "source_type": { + "$ref": "#/definitions/models.AlarmSourceType" + }, + "trigger_time": { + "type": "string" + } + } + }, + "dto.ListActiveAlarmResponse": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.ActiveAlarmDTO" + } + }, + "pagination": { + "$ref": "#/definitions/dto.PaginationDTO" + } + } + }, "dto.ListDeviceCommandLogResponse": { "type": "object", "properties": { @@ -4617,6 +5046,20 @@ const docTemplate = `{ } } }, + "dto.ListHistoricalAlarmResponse": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.HistoricalAlarmDTO" + } + }, + "pagination": { + "$ref": "#/definitions/dto.PaginationDTO" + } + } + }, "dto.ListMedicationLogResponse": { "type": "object", "properties": { @@ -4984,13 +5427,13 @@ const docTemplate = `{ "type": "integer" }, "level": { - "$ref": "#/definitions/zapcore.Level" + "$ref": "#/definitions/models.SeverityLevel" }, "message": { "type": "string" }, "notifier_type": { - "$ref": "#/definitions/notify.NotifierType" + "$ref": "#/definitions/models.NotifierType" }, "status": { "$ref": "#/definitions/models.NotificationStatus" @@ -5882,7 +6325,7 @@ const docTemplate = `{ "description": "Type 指定要测试的通知渠道", "allOf": [ { - "$ref": "#/definitions/notify.NotifierType" + "$ref": "#/definitions/models.NotifierType" } ] } @@ -5911,6 +6354,19 @@ const docTemplate = `{ } } }, + "dto.SnoozeAlarmRequest": { + "type": "object", + "required": [ + "duration_minutes" + ], + "properties": { + "duration_minutes": { + "description": "忽略时长,单位分钟", + "type": "integer", + "minimum": 1 + } + } + }, "dto.SubPlanResponse": { "type": "object", "properties": { @@ -6412,6 +6868,40 @@ const docTemplate = `{ } } }, + "models.AlarmCode": { + "type": "string", + "enum": [ + "温度阈值", + "湿度阈值", + "重量阈值", + "电池电量阈值", + "信号强度阈值", + "设备离线", + "区域主控离线" + ], + "x-enum-varnames": [ + "AlarmCodeTemperature", + "AlarmCodeHumidity", + "AlarmCodeWeight", + "AlarmCodeBatteryLevel", + "AlarmCodeSignalMetrics", + "AlarmCodeDeviceOffline", + "AlarmCodeAreaControllerOffline" + ] + }, + "models.AlarmSourceType": { + "type": "string", + "enum": [ + "普通设备", + "区域主控", + "系统" + ], + "x-enum-varnames": [ + "AlarmSourceTypeDevice", + "AlarmSourceTypeAreaController", + "AlarmSourceTypeSystem" + ] + }, "models.AuditStatus": { "type": "string", "enum": [ @@ -6522,6 +7012,21 @@ const docTemplate = `{ "NotificationStatusSkipped" ] }, + "models.NotifierType": { + "type": "string", + "enum": [ + "邮件", + "企业微信", + "飞书", + "日志" + ], + "x-enum-varnames": [ + "NotifierTypeSMTP", + "NotifierTypeWeChat", + "NotifierTypeLark", + "NotifierTypeLog" + ] + }, "models.PenStatus": { "type": "string", "enum": [ @@ -6805,6 +7310,27 @@ const docTemplate = `{ "SensorTypeWeight" ] }, + "models.SeverityLevel": { + "type": "string", + "enum": [ + "Debug", + "Info", + "Warn", + "Error", + "DPanic", + "Panic", + "Fatal" + ], + "x-enum-varnames": [ + "DebugLevel", + "InfoLevel", + "WarnLevel", + "ErrorLevel", + "DPanicLevel", + "PanicLevel", + "FatalLevel" + ] + }, "models.StockLogSourceType": { "type": "string", "enum": [ @@ -6830,10 +7356,12 @@ const docTemplate = `{ "计划分析", "等待", "下料", - "全量采集" + "全量采集", + "告警通知" ], "x-enum-comments": { "TaskPlanAnalysis": "解析Plan的Task列表并添加到待执行队列的特殊任务", + "TaskTypeAlarmNotification": "告警通知任务", "TaskTypeFullCollection": "新增的全量采集任务", "TaskTypeReleaseFeedWeight": "下料口释放指定重量任务", "TaskTypeWaiting": "等待任务" @@ -6842,13 +7370,15 @@ const docTemplate = `{ "解析Plan的Task列表并添加到待执行队列的特殊任务", "等待任务", "下料口释放指定重量任务", - "新增的全量采集任务" + "新增的全量采集任务", + "告警通知任务" ], "x-enum-varnames": [ "TaskPlanAnalysis", "TaskTypeWaiting", "TaskTypeReleaseFeedWeight", - "TaskTypeFullCollection" + "TaskTypeFullCollection", + "TaskTypeAlarmNotification" ] }, "models.ValueDescriptor": { @@ -6867,21 +7397,6 @@ const docTemplate = `{ } } }, - "notify.NotifierType": { - "type": "string", - "enum": [ - "邮件", - "企业微信", - "飞书", - "日志" - ], - "x-enum-varnames": [ - "NotifierTypeSMTP", - "NotifierTypeWeChat", - "NotifierTypeLark", - "NotifierTypeLog" - ] - }, "repository.PlanTypeFilter": { "type": "string", "enum": [ diff --git a/docs/swagger.json b/docs/swagger.json index 9d1a871..8fa4a88 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -15,6 +15,339 @@ "version": "1.0" }, "paths": { + "/api/v1/alarm/threshold/active-alarms": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "根据过滤条件和分页参数查询活跃告警列表", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "告警管理" + ], + "summary": "批量查询活跃告警", + "parameters": [ + { + "type": "string", + "description": "告警触发时间范围 - 结束时间", + "name": "end_time", + "in": "query" + }, + { + "type": "boolean", + "description": "按是否被忽略过滤", + "name": "is_ignored", + "in": "query" + }, + { + "enum": [ + "Debug", + "Info", + "Warn", + "Error", + "DPanic", + "Panic", + "Fatal" + ], + "type": "string", + "x-enum-varnames": [ + "DebugLevel", + "InfoLevel", + "WarnLevel", + "ErrorLevel", + "DPanicLevel", + "PanicLevel", + "FatalLevel" + ], + "description": "按告警严重性等级过滤", + "name": "level", + "in": "query" + }, + { + "type": "string", + "description": "排序字段,例如 \"trigger_time DESC\"", + "name": "order_by", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "page_size", + "in": "query" + }, + { + "type": "integer", + "description": "按告警来源ID过滤", + "name": "source_id", + "in": "query" + }, + { + "enum": [ + "普通设备", + "区域主控", + "系统" + ], + "type": "string", + "x-enum-varnames": [ + "AlarmSourceTypeDevice", + "AlarmSourceTypeAreaController", + "AlarmSourceTypeSystem" + ], + "description": "按告警来源类型过滤", + "name": "source_type", + "in": "query" + }, + { + "type": "string", + "description": "告警触发时间范围 - 开始时间", + "name": "trigger_time", + "in": "query" + } + ], + "responses": { + "200": { + "description": "成功获取活跃告警列表", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.ListActiveAlarmResponse" + } + } + } + ] + } + } + } + } + }, + "/api/v1/alarm/threshold/historical-alarms": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "根据过滤条件和分页参数查询历史告警列表", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "告警管理" + ], + "summary": "批量查询历史告警", + "parameters": [ + { + "enum": [ + "Debug", + "Info", + "Warn", + "Error", + "DPanic", + "Panic", + "Fatal" + ], + "type": "string", + "x-enum-varnames": [ + "DebugLevel", + "InfoLevel", + "WarnLevel", + "ErrorLevel", + "DPanicLevel", + "PanicLevel", + "FatalLevel" + ], + "description": "按告警严重性等级过滤", + "name": "level", + "in": "query" + }, + { + "type": "string", + "description": "排序字段,例如 \"trigger_time DESC\"", + "name": "order_by", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "page_size", + "in": "query" + }, + { + "type": "string", + "description": "告警解决时间范围 - 结束时间", + "name": "resolve_time_end", + "in": "query" + }, + { + "type": "string", + "description": "告警解决时间范围 - 开始时间", + "name": "resolve_time_start", + "in": "query" + }, + { + "type": "integer", + "description": "按告警来源ID过滤", + "name": "source_id", + "in": "query" + }, + { + "enum": [ + "普通设备", + "区域主控", + "系统" + ], + "type": "string", + "x-enum-varnames": [ + "AlarmSourceTypeDevice", + "AlarmSourceTypeAreaController", + "AlarmSourceTypeSystem" + ], + "description": "按告警来源类型过滤", + "name": "source_type", + "in": "query" + }, + { + "type": "string", + "description": "告警触发时间范围 - 结束时间", + "name": "trigger_time_end", + "in": "query" + }, + { + "type": "string", + "description": "告警触发时间范围 - 开始时间", + "name": "trigger_time_start", + "in": "query" + } + ], + "responses": { + "200": { + "description": "成功获取历史告警列表", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.ListHistoricalAlarmResponse" + } + } + } + ] + } + } + } + } + }, + "/api/v1/alarm/threshold/{id}/cancel-snooze": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "根据告警ID取消对一个阈值告警的忽略状态", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "告警管理" + ], + "summary": "取消忽略阈值告警", + "parameters": [ + { + "type": "string", + "description": "告警ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "成功取消忽略告警", + "schema": { + "$ref": "#/definitions/controller.Response" + } + } + } + } + }, + "/api/v1/alarm/threshold/{id}/snooze": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "根据告警ID忽略一个活跃的阈值告警,或更新其忽略时间", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "告警管理" + ], + "summary": "忽略阈值告警", + "parameters": [ + { + "type": "string", + "description": "告警ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "忽略告警请求体", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.SnoozeAlarmRequest" + } + } + ], + "responses": { + "200": { + "description": "成功忽略告警", + "schema": { + "$ref": "#/definitions/controller.Response" + } + } + } + } + }, "/api/v1/area-controllers": { "get": { "security": [ @@ -4162,6 +4495,50 @@ "CodeServiceUnavailable" ] }, + "dto.ActiveAlarmDTO": { + "type": "object", + "properties": { + "alarm_code": { + "$ref": "#/definitions/models.AlarmCode" + }, + "alarm_details": { + "type": "string" + }, + "alarm_summary": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "ignored_until": { + "type": "string" + }, + "is_ignored": { + "type": "boolean" + }, + "last_notified_at": { + "type": "string" + }, + "level": { + "$ref": "#/definitions/models.SeverityLevel" + }, + "source_id": { + "type": "integer" + }, + "source_type": { + "$ref": "#/definitions/models.AlarmSourceType" + }, + "trigger_time": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, "dto.AreaControllerResponse": { "type": "object", "properties": { @@ -4581,6 +4958,58 @@ } } }, + "dto.HistoricalAlarmDTO": { + "type": "object", + "properties": { + "alarm_code": { + "$ref": "#/definitions/models.AlarmCode" + }, + "alarm_details": { + "type": "string" + }, + "alarm_summary": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "level": { + "$ref": "#/definitions/models.SeverityLevel" + }, + "resolve_method": { + "type": "string" + }, + "resolve_time": { + "type": "string" + }, + "resolved_by": { + "type": "integer" + }, + "source_id": { + "type": "integer" + }, + "source_type": { + "$ref": "#/definitions/models.AlarmSourceType" + }, + "trigger_time": { + "type": "string" + } + } + }, + "dto.ListActiveAlarmResponse": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.ActiveAlarmDTO" + } + }, + "pagination": { + "$ref": "#/definitions/dto.PaginationDTO" + } + } + }, "dto.ListDeviceCommandLogResponse": { "type": "object", "properties": { @@ -4609,6 +5038,20 @@ } } }, + "dto.ListHistoricalAlarmResponse": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.HistoricalAlarmDTO" + } + }, + "pagination": { + "$ref": "#/definitions/dto.PaginationDTO" + } + } + }, "dto.ListMedicationLogResponse": { "type": "object", "properties": { @@ -4976,13 +5419,13 @@ "type": "integer" }, "level": { - "$ref": "#/definitions/zapcore.Level" + "$ref": "#/definitions/models.SeverityLevel" }, "message": { "type": "string" }, "notifier_type": { - "$ref": "#/definitions/notify.NotifierType" + "$ref": "#/definitions/models.NotifierType" }, "status": { "$ref": "#/definitions/models.NotificationStatus" @@ -5874,7 +6317,7 @@ "description": "Type 指定要测试的通知渠道", "allOf": [ { - "$ref": "#/definitions/notify.NotifierType" + "$ref": "#/definitions/models.NotifierType" } ] } @@ -5903,6 +6346,19 @@ } } }, + "dto.SnoozeAlarmRequest": { + "type": "object", + "required": [ + "duration_minutes" + ], + "properties": { + "duration_minutes": { + "description": "忽略时长,单位分钟", + "type": "integer", + "minimum": 1 + } + } + }, "dto.SubPlanResponse": { "type": "object", "properties": { @@ -6404,6 +6860,40 @@ } } }, + "models.AlarmCode": { + "type": "string", + "enum": [ + "温度阈值", + "湿度阈值", + "重量阈值", + "电池电量阈值", + "信号强度阈值", + "设备离线", + "区域主控离线" + ], + "x-enum-varnames": [ + "AlarmCodeTemperature", + "AlarmCodeHumidity", + "AlarmCodeWeight", + "AlarmCodeBatteryLevel", + "AlarmCodeSignalMetrics", + "AlarmCodeDeviceOffline", + "AlarmCodeAreaControllerOffline" + ] + }, + "models.AlarmSourceType": { + "type": "string", + "enum": [ + "普通设备", + "区域主控", + "系统" + ], + "x-enum-varnames": [ + "AlarmSourceTypeDevice", + "AlarmSourceTypeAreaController", + "AlarmSourceTypeSystem" + ] + }, "models.AuditStatus": { "type": "string", "enum": [ @@ -6514,6 +7004,21 @@ "NotificationStatusSkipped" ] }, + "models.NotifierType": { + "type": "string", + "enum": [ + "邮件", + "企业微信", + "飞书", + "日志" + ], + "x-enum-varnames": [ + "NotifierTypeSMTP", + "NotifierTypeWeChat", + "NotifierTypeLark", + "NotifierTypeLog" + ] + }, "models.PenStatus": { "type": "string", "enum": [ @@ -6797,6 +7302,27 @@ "SensorTypeWeight" ] }, + "models.SeverityLevel": { + "type": "string", + "enum": [ + "Debug", + "Info", + "Warn", + "Error", + "DPanic", + "Panic", + "Fatal" + ], + "x-enum-varnames": [ + "DebugLevel", + "InfoLevel", + "WarnLevel", + "ErrorLevel", + "DPanicLevel", + "PanicLevel", + "FatalLevel" + ] + }, "models.StockLogSourceType": { "type": "string", "enum": [ @@ -6822,10 +7348,12 @@ "计划分析", "等待", "下料", - "全量采集" + "全量采集", + "告警通知" ], "x-enum-comments": { "TaskPlanAnalysis": "解析Plan的Task列表并添加到待执行队列的特殊任务", + "TaskTypeAlarmNotification": "告警通知任务", "TaskTypeFullCollection": "新增的全量采集任务", "TaskTypeReleaseFeedWeight": "下料口释放指定重量任务", "TaskTypeWaiting": "等待任务" @@ -6834,13 +7362,15 @@ "解析Plan的Task列表并添加到待执行队列的特殊任务", "等待任务", "下料口释放指定重量任务", - "新增的全量采集任务" + "新增的全量采集任务", + "告警通知任务" ], "x-enum-varnames": [ "TaskPlanAnalysis", "TaskTypeWaiting", "TaskTypeReleaseFeedWeight", - "TaskTypeFullCollection" + "TaskTypeFullCollection", + "TaskTypeAlarmNotification" ] }, "models.ValueDescriptor": { @@ -6859,21 +7389,6 @@ } } }, - "notify.NotifierType": { - "type": "string", - "enum": [ - "邮件", - "企业微信", - "飞书", - "日志" - ], - "x-enum-varnames": [ - "NotifierTypeSMTP", - "NotifierTypeWeChat", - "NotifierTypeLark", - "NotifierTypeLog" - ] - }, "repository.PlanTypeFilter": { "type": "string", "enum": [ diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 2392e19..ebfb500 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -53,6 +53,35 @@ definitions: - CodeConflict - CodeInternalError - CodeServiceUnavailable + dto.ActiveAlarmDTO: + properties: + alarm_code: + $ref: '#/definitions/models.AlarmCode' + alarm_details: + type: string + alarm_summary: + type: string + created_at: + type: string + id: + type: integer + ignored_until: + type: string + is_ignored: + type: boolean + last_notified_at: + type: string + level: + $ref: '#/definitions/models.SeverityLevel' + source_id: + type: integer + source_type: + $ref: '#/definitions/models.AlarmSourceType' + trigger_time: + type: string + updated_at: + type: string + type: object dto.AreaControllerResponse: properties: created_at: @@ -340,6 +369,40 @@ definitions: remarks: type: string type: object + dto.HistoricalAlarmDTO: + properties: + alarm_code: + $ref: '#/definitions/models.AlarmCode' + alarm_details: + type: string + alarm_summary: + type: string + id: + type: integer + level: + $ref: '#/definitions/models.SeverityLevel' + resolve_method: + type: string + resolve_time: + type: string + resolved_by: + type: integer + source_id: + type: integer + source_type: + $ref: '#/definitions/models.AlarmSourceType' + trigger_time: + type: string + type: object + dto.ListActiveAlarmResponse: + properties: + list: + items: + $ref: '#/definitions/dto.ActiveAlarmDTO' + type: array + pagination: + $ref: '#/definitions/dto.PaginationDTO' + type: object dto.ListDeviceCommandLogResponse: properties: list: @@ -358,6 +421,15 @@ definitions: pagination: $ref: '#/definitions/dto.PaginationDTO' type: object + dto.ListHistoricalAlarmResponse: + properties: + list: + items: + $ref: '#/definitions/dto.HistoricalAlarmDTO' + type: array + pagination: + $ref: '#/definitions/dto.PaginationDTO' + type: object dto.ListMedicationLogResponse: properties: list: @@ -600,11 +672,11 @@ definitions: id: type: integer level: - $ref: '#/definitions/zapcore.Level' + $ref: '#/definitions/models.SeverityLevel' message: type: string notifier_type: - $ref: '#/definitions/notify.NotifierType' + $ref: '#/definitions/models.NotifierType' status: $ref: '#/definitions/models.NotificationStatus' title: @@ -1199,7 +1271,7 @@ definitions: properties: type: allOf: - - $ref: '#/definitions/notify.NotifierType' + - $ref: '#/definitions/models.NotifierType' description: Type 指定要测试的通知渠道 required: - type @@ -1219,6 +1291,15 @@ definitions: time: type: string type: object + dto.SnoozeAlarmRequest: + properties: + duration_minutes: + description: 忽略时长,单位分钟 + minimum: 1 + type: integer + required: + - duration_minutes + type: object dto.SubPlanResponse: properties: child_plan: @@ -1558,6 +1639,34 @@ definitions: weight: type: number type: object + models.AlarmCode: + enum: + - 温度阈值 + - 湿度阈值 + - 重量阈值 + - 电池电量阈值 + - 信号强度阈值 + - 设备离线 + - 区域主控离线 + type: string + x-enum-varnames: + - AlarmCodeTemperature + - AlarmCodeHumidity + - AlarmCodeWeight + - AlarmCodeBatteryLevel + - AlarmCodeSignalMetrics + - AlarmCodeDeviceOffline + - AlarmCodeAreaControllerOffline + models.AlarmSourceType: + enum: + - 普通设备 + - 区域主控 + - 系统 + type: string + x-enum-varnames: + - AlarmSourceTypeDevice + - AlarmSourceTypeAreaController + - AlarmSourceTypeSystem models.AuditStatus: enum: - 成功 @@ -1646,6 +1755,18 @@ definitions: - NotificationStatusSuccess - NotificationStatusFailed - NotificationStatusSkipped + models.NotifierType: + enum: + - 邮件 + - 企业微信 + - 飞书 + - 日志 + type: string + x-enum-varnames: + - NotifierTypeSMTP + - NotifierTypeWeChat + - NotifierTypeLark + - NotifierTypeLog models.PenStatus: enum: - 空闲 @@ -1877,6 +1998,24 @@ definitions: - SensorTypeTemperature - SensorTypeHumidity - SensorTypeWeight + models.SeverityLevel: + enum: + - Debug + - Info + - Warn + - Error + - DPanic + - Panic + - Fatal + type: string + x-enum-varnames: + - DebugLevel + - InfoLevel + - WarnLevel + - ErrorLevel + - DPanicLevel + - PanicLevel + - FatalLevel models.StockLogSourceType: enum: - 采购入库 @@ -1899,9 +2038,11 @@ definitions: - 等待 - 下料 - 全量采集 + - 告警通知 type: string x-enum-comments: TaskPlanAnalysis: 解析Plan的Task列表并添加到待执行队列的特殊任务 + TaskTypeAlarmNotification: 告警通知任务 TaskTypeFullCollection: 新增的全量采集任务 TaskTypeReleaseFeedWeight: 下料口释放指定重量任务 TaskTypeWaiting: 等待任务 @@ -1910,11 +2051,13 @@ definitions: - 等待任务 - 下料口释放指定重量任务 - 新增的全量采集任务 + - 告警通知任务 x-enum-varnames: - TaskPlanAnalysis - TaskTypeWaiting - TaskTypeReleaseFeedWeight - TaskTypeFullCollection + - TaskTypeAlarmNotification models.ValueDescriptor: properties: multiplier: @@ -1926,18 +2069,6 @@ definitions: type: $ref: '#/definitions/models.SensorType' type: object - notify.NotifierType: - enum: - - 邮件 - - 企业微信 - - 飞书 - - 日志 - type: string - x-enum-varnames: - - NotifierTypeSMTP - - NotifierTypeWeChat - - NotifierTypeLark - - NotifierTypeLog repository.PlanTypeFilter: enum: - 所有任务 @@ -1987,6 +2118,224 @@ info: title: 猪场管理系统 API version: "1.0" paths: + /api/v1/alarm/threshold/{id}/cancel-snooze: + post: + consumes: + - application/json + description: 根据告警ID取消对一个阈值告警的忽略状态 + parameters: + - description: 告警ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: 成功取消忽略告警 + schema: + $ref: '#/definitions/controller.Response' + security: + - BearerAuth: [] + summary: 取消忽略阈值告警 + tags: + - 告警管理 + /api/v1/alarm/threshold/{id}/snooze: + post: + consumes: + - application/json + description: 根据告警ID忽略一个活跃的阈值告警,或更新其忽略时间 + parameters: + - description: 告警ID + in: path + name: id + required: true + type: string + - description: 忽略告警请求体 + in: body + name: request + required: true + schema: + $ref: '#/definitions/dto.SnoozeAlarmRequest' + produces: + - application/json + responses: + "200": + description: 成功忽略告警 + schema: + $ref: '#/definitions/controller.Response' + security: + - BearerAuth: [] + summary: 忽略阈值告警 + tags: + - 告警管理 + /api/v1/alarm/threshold/active-alarms: + get: + consumes: + - application/json + description: 根据过滤条件和分页参数查询活跃告警列表 + parameters: + - description: 告警触发时间范围 - 结束时间 + in: query + name: end_time + type: string + - description: 按是否被忽略过滤 + in: query + name: is_ignored + type: boolean + - description: 按告警严重性等级过滤 + enum: + - Debug + - Info + - Warn + - Error + - DPanic + - Panic + - Fatal + in: query + name: level + type: string + x-enum-varnames: + - DebugLevel + - InfoLevel + - WarnLevel + - ErrorLevel + - DPanicLevel + - PanicLevel + - FatalLevel + - description: 排序字段,例如 "trigger_time DESC" + in: query + name: order_by + type: string + - in: query + name: page + type: integer + - in: query + name: page_size + type: integer + - description: 按告警来源ID过滤 + in: query + name: source_id + type: integer + - description: 按告警来源类型过滤 + enum: + - 普通设备 + - 区域主控 + - 系统 + in: query + name: source_type + type: string + x-enum-varnames: + - AlarmSourceTypeDevice + - AlarmSourceTypeAreaController + - AlarmSourceTypeSystem + - description: 告警触发时间范围 - 开始时间 + in: query + name: trigger_time + type: string + produces: + - application/json + responses: + "200": + description: 成功获取活跃告警列表 + schema: + allOf: + - $ref: '#/definitions/controller.Response' + - properties: + data: + $ref: '#/definitions/dto.ListActiveAlarmResponse' + type: object + security: + - BearerAuth: [] + summary: 批量查询活跃告警 + tags: + - 告警管理 + /api/v1/alarm/threshold/historical-alarms: + get: + consumes: + - application/json + description: 根据过滤条件和分页参数查询历史告警列表 + parameters: + - description: 按告警严重性等级过滤 + enum: + - Debug + - Info + - Warn + - Error + - DPanic + - Panic + - Fatal + in: query + name: level + type: string + x-enum-varnames: + - DebugLevel + - InfoLevel + - WarnLevel + - ErrorLevel + - DPanicLevel + - PanicLevel + - FatalLevel + - description: 排序字段,例如 "trigger_time DESC" + in: query + name: order_by + type: string + - in: query + name: page + type: integer + - in: query + name: page_size + type: integer + - description: 告警解决时间范围 - 结束时间 + in: query + name: resolve_time_end + type: string + - description: 告警解决时间范围 - 开始时间 + in: query + name: resolve_time_start + type: string + - description: 按告警来源ID过滤 + in: query + name: source_id + type: integer + - description: 按告警来源类型过滤 + enum: + - 普通设备 + - 区域主控 + - 系统 + in: query + name: source_type + type: string + x-enum-varnames: + - AlarmSourceTypeDevice + - AlarmSourceTypeAreaController + - AlarmSourceTypeSystem + - description: 告警触发时间范围 - 结束时间 + in: query + name: trigger_time_end + type: string + - description: 告警触发时间范围 - 开始时间 + in: query + name: trigger_time_start + type: string + produces: + - application/json + responses: + "200": + description: 成功获取历史告警列表 + schema: + allOf: + - $ref: '#/definitions/controller.Response' + - properties: + data: + $ref: '#/definitions/dto.ListHistoricalAlarmResponse' + type: object + security: + - BearerAuth: [] + summary: 批量查询历史告警 + tags: + - 告警管理 /api/v1/area-controllers: get: description: 获取系统中所有区域主控的列表 diff --git a/internal/app/api/router.go b/internal/app/api/router.go index b816f7d..5929249 100644 --- a/internal/app/api/router.go +++ b/internal/app/api/router.go @@ -195,6 +195,8 @@ func (a *API) setupRoutes() { { thresholdGroup.POST("/:id/snooze", a.alarmController.SnoozeThresholdAlarm) // 忽略阈值告警 thresholdGroup.POST("/:id/cancel-snooze", a.alarmController.CancelSnoozeThresholdAlarm) // 取消忽略阈值告警 + thresholdGroup.GET("/active-alarms", a.alarmController.ListActiveAlarms) // 获取活跃告警 + thresholdGroup.GET("/historical-alarms", a.alarmController.ListHistoricalAlarms) // 获取历史告警 } } logger.Debug("告警相关接口注册成功 (需要认证和审计)") diff --git a/internal/app/controller/alarm/threshold_alarm_controller.go b/internal/app/controller/alarm/threshold_alarm_controller.go index 4310221..06167f1 100644 --- a/internal/app/controller/alarm/threshold_alarm_controller.go +++ b/internal/app/controller/alarm/threshold_alarm_controller.go @@ -9,6 +9,7 @@ import ( "git.huangwc.com/pig/pig-farm-controller/internal/app/dto" "git.huangwc.com/pig/pig-farm-controller/internal/app/service" "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" + "git.huangwc.com/pig/pig-farm-controller/internal/infra/repository" "github.com/labstack/echo/v4" "gorm.io/gorm" @@ -107,3 +108,73 @@ func (t *ThresholdAlarmController) CancelSnoozeThresholdAlarm(ctx echo.Context) logger.Infof("%s: 告警忽略状态已成功取消, ID: %d", actionType, alarmID) return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "告警忽略状态已成功取消", nil, actionType, "告警忽略状态已成功取消", alarmID) } + +// ListActiveAlarms godoc +// @Summary 批量查询活跃告警 +// @Description 根据过滤条件和分页参数查询活跃告警列表 +// @Tags 告警管理 +// @Security BearerAuth +// @Accept json +// @Produce json +// @Param query query dto.ListActiveAlarmRequest true "查询参数" +// @Success 200 {object} controller.Response{data=dto.ListActiveAlarmResponse} "成功获取活跃告警列表" +// @Router /api/v1/alarm/threshold/active-alarms [get] +func (t *ThresholdAlarmController) ListActiveAlarms(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), t.ctx, "ListActiveAlarms") + + const actionType = "批量查询活跃告警" + var req dto.ListActiveAlarmRequest + if err := ctx.Bind(&req); err != nil { + logger.Errorf("%s: 参数绑定失败: %v", actionType, err) + return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求参数: "+err.Error(), actionType, "请求参数绑定失败", nil) + } + + resp, err := t.thresholdAlarmService.ListActiveAlarms(reqCtx, &req) + if err != nil { + // 捕获 ErrInvalidPagination 错误,并返回 Bad Request + if errors.Is(err, repository.ErrInvalidPagination) { + logger.Warnf("%s: 无效的分页参数: %v", actionType, err) + return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的分页参数: "+err.Error(), actionType, "无效分页参数", req) + } + logger.Errorf("%s: 服务层查询活跃告警失败: %v", actionType, err) + return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "查询活跃告警失败: "+err.Error(), actionType, "服务层查询活跃告警失败", req) + } + + logger.Infof("%s: 成功, 获取到 %d 条记录, 总计 %d 条", actionType, len(resp.List), resp.Pagination.Total) + return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "成功获取活跃告警列表", resp, actionType, "成功获取活跃告警列表", req) +} + +// ListHistoricalAlarms godoc +// @Summary 批量查询历史告警 +// @Description 根据过滤条件和分页参数查询历史告警列表 +// @Tags 告警管理 +// @Security BearerAuth +// @Accept json +// @Produce json +// @Param query query dto.ListHistoricalAlarmRequest true "查询参数" +// @Success 200 {object} controller.Response{data=dto.ListHistoricalAlarmResponse} "成功获取历史告警列表" +// @Router /api/v1/alarm/threshold/historical-alarms [get] +func (t *ThresholdAlarmController) ListHistoricalAlarms(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), t.ctx, "ListHistoricalAlarms") + + const actionType = "批量查询历史告警" + var req dto.ListHistoricalAlarmRequest + if err := ctx.Bind(&req); err != nil { + logger.Errorf("%s: 参数绑定失败: %v", actionType, err) + return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求参数: "+err.Error(), actionType, "请求参数绑定失败", nil) + } + + resp, err := t.thresholdAlarmService.ListHistoricalAlarms(reqCtx, &req) + if err != nil { + // 捕获 ErrInvalidPagination 错误,并返回 Bad Request + if errors.Is(err, repository.ErrInvalidPagination) { + logger.Warnf("%s: 无效的分页参数: %v", actionType, err) + return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的分页参数: "+err.Error(), actionType, "无效分页参数", req) + } + logger.Errorf("%s: 服务层查询历史告警失败: %v", actionType, err) + return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "查询历史告警失败: "+err.Error(), actionType, "服务层查询历史告警失败", req) + } + + logger.Infof("%s: 成功, 获取到 %d 条记录, 总计 %d 条", actionType, len(resp.List), resp.Pagination.Total) + return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "成功获取历史告警列表", resp, actionType, "成功获取历史告警列表", req) +} diff --git a/internal/app/dto/alarm_converter.go b/internal/app/dto/alarm_converter.go new file mode 100644 index 0000000..90a3b33 --- /dev/null +++ b/internal/app/dto/alarm_converter.go @@ -0,0 +1,65 @@ +package dto + +import ( + "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" +) + +// NewListActiveAlarmResponse 从模型数据创建活跃告警列表响应 DTO +func NewListActiveAlarmResponse(data []models.ActiveAlarm, total int64, page, pageSize int) *ListActiveAlarmResponse { + dtos := make([]ActiveAlarmDTO, len(data)) + for i, item := range data { + dtos[i] = ActiveAlarmDTO{ + ID: item.ID, + CreatedAt: item.CreatedAt, + UpdatedAt: item.UpdatedAt, + SourceType: item.SourceType, + SourceID: item.SourceID, + AlarmCode: item.AlarmCode, + AlarmSummary: item.AlarmSummary, + Level: item.Level, + AlarmDetails: item.AlarmDetails, + TriggerTime: item.TriggerTime, + IsIgnored: item.IsIgnored, + IgnoredUntil: item.IgnoredUntil, + LastNotifiedAt: item.LastNotifiedAt, + } + } + + return &ListActiveAlarmResponse{ + List: dtos, + Pagination: PaginationDTO{ + Total: total, + Page: page, + PageSize: pageSize, + }, + } +} + +// NewListHistoricalAlarmResponse 从模型数据创建历史告警列表响应 DTO +func NewListHistoricalAlarmResponse(data []models.HistoricalAlarm, total int64, page, pageSize int) *ListHistoricalAlarmResponse { + dtos := make([]HistoricalAlarmDTO, len(data)) + for i, item := range data { + dtos[i] = HistoricalAlarmDTO{ + ID: item.ID, + SourceType: item.SourceType, + SourceID: item.SourceID, + AlarmCode: item.AlarmCode, + AlarmSummary: item.AlarmSummary, + Level: item.Level, + AlarmDetails: item.AlarmDetails, + TriggerTime: item.TriggerTime, + ResolveTime: item.ResolveTime, + ResolveMethod: item.ResolveMethod, + ResolvedBy: item.ResolvedBy, + } + } + + return &ListHistoricalAlarmResponse{ + List: dtos, + Pagination: PaginationDTO{ + Total: total, + Page: page, + PageSize: pageSize, + }, + } +} diff --git a/internal/app/dto/alarm_dto.go b/internal/app/dto/alarm_dto.go index 1239cdf..7cb2b09 100644 --- a/internal/app/dto/alarm_dto.go +++ b/internal/app/dto/alarm_dto.go @@ -1,6 +1,83 @@ package dto +import ( + "time" + + "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" +) + // SnoozeAlarmRequest 定义了忽略告警的请求体 type SnoozeAlarmRequest struct { DurationMinutes uint `json:"duration_minutes" validate:"required,min=1"` // 忽略时长,单位分钟 } + +// ListActiveAlarmRequest 定义了获取活跃告警列表的请求参数 +type ListActiveAlarmRequest struct { + Page int `json:"page" query:"page"` + PageSize int `json:"page_size" query:"page_size"` + SourceType *models.AlarmSourceType `json:"source_type" query:"source_type"` // 按告警来源类型过滤 + SourceID *uint `json:"source_id" query:"source_id"` // 按告警来源ID过滤 + Level *models.SeverityLevel `json:"level" query:"level"` // 按告警严重性等级过滤 + IsIgnored *bool `json:"is_ignored" query:"is_ignored"` // 按是否被忽略过滤 + TriggerTime *time.Time `json:"trigger_time" query:"trigger_time"` // 告警触发时间范围 - 开始时间 + EndTime *time.Time `json:"end_time" query:"end_time"` // 告警触发时间范围 - 结束时间 + OrderBy string `json:"order_by" query:"order_by"` // 排序字段,例如 "trigger_time DESC" +} + +// ActiveAlarmDTO 是用于API响应的活跃告警结构 +type ActiveAlarmDTO struct { + ID uint `json:"id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + SourceType models.AlarmSourceType `json:"source_type"` + SourceID uint `json:"source_id"` + AlarmCode models.AlarmCode `json:"alarm_code"` + AlarmSummary string `json:"alarm_summary"` + Level models.SeverityLevel `json:"level"` + AlarmDetails string `json:"alarm_details"` + TriggerTime time.Time `json:"trigger_time"` + IsIgnored bool `json:"is_ignored"` + IgnoredUntil *time.Time `json:"ignored_until"` + LastNotifiedAt *time.Time `json:"last_notified_at"` +} + +// ListActiveAlarmResponse 是获取活跃告警列表的响应结构 +type ListActiveAlarmResponse struct { + List []ActiveAlarmDTO `json:"list"` + Pagination PaginationDTO `json:"pagination"` +} + +// ListHistoricalAlarmRequest 定义了获取历史告警列表的请求参数 +type ListHistoricalAlarmRequest struct { + Page int `json:"page" query:"page"` + PageSize int `json:"page_size" query:"page_size"` + SourceType *models.AlarmSourceType `json:"source_type" query:"source_type"` // 按告警来源类型过滤 + SourceID *uint `json:"source_id" query:"source_id"` // 按告警来源ID过滤 + Level *models.SeverityLevel `json:"level" query:"level"` // 按告警严重性等级过滤 + TriggerTimeStart *time.Time `json:"trigger_time_start" query:"trigger_time_start"` // 告警触发时间范围 - 开始时间 + TriggerTimeEnd *time.Time `json:"trigger_time_end" query:"trigger_time_end"` // 告警触发时间范围 - 结束时间 + ResolveTimeStart *time.Time `json:"resolve_time_start" query:"resolve_time_start"` // 告警解决时间范围 - 开始时间 + ResolveTimeEnd *time.Time `json:"resolve_time_end" query:"resolve_time_end"` // 告警解决时间范围 - 结束时间 + OrderBy string `json:"order_by" query:"order_by"` // 排序字段,例如 "trigger_time DESC" +} + +// HistoricalAlarmDTO 是用于API响应的历史告警结构 +type HistoricalAlarmDTO struct { + ID uint `json:"id"` + SourceType models.AlarmSourceType `json:"source_type"` + SourceID uint `json:"source_id"` + AlarmCode models.AlarmCode `json:"alarm_code"` + AlarmSummary string `json:"alarm_summary"` + Level models.SeverityLevel `json:"level"` + AlarmDetails string `json:"alarm_details"` + TriggerTime time.Time `json:"trigger_time"` + ResolveTime time.Time `json:"resolve_time"` + ResolveMethod string `json:"resolve_method"` + ResolvedBy *uint `json:"resolved_by"` +} + +// ListHistoricalAlarmResponse 是获取历史告警列表的响应结构 +type ListHistoricalAlarmResponse struct { + List []HistoricalAlarmDTO `json:"list"` + Pagination PaginationDTO `json:"pagination"` +} diff --git a/internal/app/dto/dto.go b/internal/app/dto/dto.go new file mode 100644 index 0000000..ac02148 --- /dev/null +++ b/internal/app/dto/dto.go @@ -0,0 +1,10 @@ +package dto + +// --- General --- + +// PaginationDTO 定义了分页信息的标准结构 +type PaginationDTO struct { + Total int64 `json:"total"` + Page int `json:"page"` + PageSize int `json:"page_size"` +} diff --git a/internal/app/dto/monitor_dto.go b/internal/app/dto/monitor_dto.go index bfb135f..0eeee8f 100644 --- a/internal/app/dto/monitor_dto.go +++ b/internal/app/dto/monitor_dto.go @@ -7,15 +7,6 @@ import ( "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" ) -// --- General --- - -// PaginationDTO 定义了分页信息的标准结构 -type PaginationDTO struct { - Total int64 `json:"total"` - Page int `json:"page"` - PageSize int `json:"page_size"` -} - // --- SensorData --- // ListSensorDataRequest 定义了获取传感器数据列表的请求参数 diff --git a/internal/app/service/threshold_alarm_service.go b/internal/app/service/threshold_alarm_service.go index 5825ffb..f841f6e 100644 --- a/internal/app/service/threshold_alarm_service.go +++ b/internal/app/service/threshold_alarm_service.go @@ -4,8 +4,10 @@ import ( "context" "time" - domainAlarm "git.huangwc.com/pig/pig-farm-controller/internal/domain/alarm" // 引入领域层的 AlarmService + "git.huangwc.com/pig/pig-farm-controller/internal/app/dto" + domainAlarm "git.huangwc.com/pig/pig-farm-controller/internal/domain/alarm" "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" + "git.huangwc.com/pig/pig-farm-controller/internal/infra/repository" ) // ThresholdAlarmService 定义了阈值告警配置服务的接口。 @@ -15,19 +17,25 @@ type ThresholdAlarmService interface { SnoozeThresholdAlarm(ctx context.Context, alarmID uint, durationMinutes uint) error // CancelSnoozeThresholdAlarm 取消对一个阈值告警的忽略状态。 CancelSnoozeThresholdAlarm(ctx context.Context, alarmID uint) error + // ListActiveAlarms 批量查询活跃告警。 + ListActiveAlarms(ctx context.Context, req *dto.ListActiveAlarmRequest) (*dto.ListActiveAlarmResponse, error) + // ListHistoricalAlarms 批量查询历史告警。 + ListHistoricalAlarms(ctx context.Context, req *dto.ListHistoricalAlarmRequest) (*dto.ListHistoricalAlarmResponse, error) } // thresholdAlarmService 是 ThresholdAlarmService 接口的具体实现。 type thresholdAlarmService struct { ctx context.Context - alarmService domainAlarm.AlarmService // 注入领域层的 AlarmService + alarmService domainAlarm.AlarmService + alarmRepo repository.AlarmRepository } // NewThresholdAlarmService 创建一个新的 ThresholdAlarmService 实例。 -func NewThresholdAlarmService(ctx context.Context, alarmService domainAlarm.AlarmService) ThresholdAlarmService { +func NewThresholdAlarmService(ctx context.Context, alarmService domainAlarm.AlarmService, alarmRepo repository.AlarmRepository) ThresholdAlarmService { return &thresholdAlarmService{ ctx: ctx, alarmService: alarmService, + alarmRepo: alarmRepo, } } @@ -42,3 +50,48 @@ func (s *thresholdAlarmService) CancelSnoozeThresholdAlarm(ctx context.Context, serviceCtx := logs.AddFuncName(ctx, s.ctx, "CancelSnoozeThresholdAlarm") return s.alarmService.CancelAlarmSnooze(serviceCtx, alarmID) } + +// ListActiveAlarms 实现了批量查询活跃告警的逻辑。 +func (s *thresholdAlarmService) ListActiveAlarms(ctx context.Context, req *dto.ListActiveAlarmRequest) (*dto.ListActiveAlarmResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListActiveAlarms") + + opts := repository.ActiveAlarmListOptions{ + SourceType: req.SourceType, + SourceID: req.SourceID, + Level: req.Level, + IsIgnored: req.IsIgnored, + TriggerTime: req.TriggerTime, + EndTime: req.EndTime, + OrderBy: req.OrderBy, + } + + alarms, total, err := s.alarmRepo.ListActiveAlarms(serviceCtx, opts, req.Page, req.PageSize) + if err != nil { + return nil, err + } + + return dto.NewListActiveAlarmResponse(alarms, total, req.Page, req.PageSize), nil +} + +// ListHistoricalAlarms 实现了批量查询历史告警的逻辑。 +func (s *thresholdAlarmService) ListHistoricalAlarms(ctx context.Context, req *dto.ListHistoricalAlarmRequest) (*dto.ListHistoricalAlarmResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListHistoricalAlarms") + + opts := repository.HistoricalAlarmListOptions{ + SourceType: req.SourceType, + SourceID: req.SourceID, + Level: req.Level, + TriggerTimeStart: req.TriggerTimeStart, + TriggerTimeEnd: req.TriggerTimeEnd, + ResolveTimeStart: req.ResolveTimeStart, + ResolveTimeEnd: req.ResolveTimeEnd, + OrderBy: req.OrderBy, + } + + alarms, total, err := s.alarmRepo.ListHistoricalAlarms(serviceCtx, opts, req.Page, req.PageSize) + if err != nil { + return nil, err + } + + return dto.NewListHistoricalAlarmResponse(alarms, total, req.Page, req.PageSize), nil +} diff --git a/internal/core/component_initializers.go b/internal/core/component_initializers.go index 17f3d64..af77dec 100644 --- a/internal/core/component_initializers.go +++ b/internal/core/component_initializers.go @@ -269,6 +269,7 @@ func initAppServices(ctx context.Context, infra *Infrastructure, domainServices thresholdAlarmService := service.NewThresholdAlarmService( logs.AddCompName(baseCtx, "ThresholdAlarmService"), domainServices.alarmService, + infra.repos.alarmRepo, ) return &AppServices{ diff --git a/internal/infra/repository/alarm_repository.go b/internal/infra/repository/alarm_repository.go index 2b91609..f513778 100644 --- a/internal/infra/repository/alarm_repository.go +++ b/internal/infra/repository/alarm_repository.go @@ -23,6 +23,18 @@ type ActiveAlarmListOptions struct { OrderBy string // 排序字段,例如 "trigger_time DESC" } +// HistoricalAlarmListOptions 定义了查询历史告警列表时的可选参数 +type HistoricalAlarmListOptions struct { + SourceType *models.AlarmSourceType // 按告警来源类型过滤 + SourceID *uint // 按告警来源ID过滤 + Level *models.SeverityLevel // 按告警严重性等级过滤 + TriggerTimeStart *time.Time // 告警触发时间范围 - 开始时间 + TriggerTimeEnd *time.Time // 告警触发时间范围 - 结束时间 + ResolveTimeStart *time.Time // 告警解决时间范围 - 开始时间 (对应 models.HistoricalAlarm.ResolveTime) + ResolveTimeEnd *time.Time // 告警解决时间范围 - 结束时间 (对应 models.HistoricalAlarm.ResolveTime) + OrderBy string // 排序字段,例如 "trigger_time DESC" +} + // AlarmRepository 定义了对告警模型的数据库操作接口 type AlarmRepository interface { // CreateActiveAlarm 创建一条新的活跃告警记录 @@ -47,6 +59,10 @@ type AlarmRepository interface { // 返回活跃告警列表、总记录数和错误。 ListActiveAlarms(ctx context.Context, opts ActiveAlarmListOptions, page, pageSize int) ([]models.ActiveAlarm, int64, error) + // ListHistoricalAlarms 支持分页和过滤的历史告警列表查询。 + // 返回历史告警列表、总记录数和错误。 + ListHistoricalAlarms(ctx context.Context, opts HistoricalAlarmListOptions, page, pageSize int) ([]models.HistoricalAlarm, int64, error) + // UpdateAlarmNotificationStatus 显式更新告警的通知相关状态字段。 // lastNotifiedAt: 传入具体的发送时间。 // isIgnored: 告警新的忽略状态。 @@ -148,7 +164,7 @@ func (r *gormAlarmRepository) ListActiveAlarms(ctx context.Context, opts ActiveA repoCtx := logs.AddFuncName(ctx, r.ctx, "ListActiveAlarms") // --- 校验分页参数 --- if page <= 0 || pageSize <= 0 { - return nil, 0, ErrInvalidPagination // 复用已定义的错误 + return nil, 0, ErrInvalidPagination } var results []models.ActiveAlarm @@ -195,6 +211,61 @@ func (r *gormAlarmRepository) ListActiveAlarms(ctx context.Context, opts ActiveA return results, total, err } +// ListHistoricalAlarms 实现了分页和过滤查询历史告警记录的功能 +func (r *gormAlarmRepository) ListHistoricalAlarms(ctx context.Context, opts HistoricalAlarmListOptions, page, pageSize int) ([]models.HistoricalAlarm, int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "ListHistoricalAlarms") + // --- 校验分页参数 --- + if page <= 0 || pageSize <= 0 { + return nil, 0, ErrInvalidPagination + } + + var results []models.HistoricalAlarm + var total int64 + + query := r.db.WithContext(repoCtx).Model(&models.HistoricalAlarm{}) + + // --- 应用过滤条件 --- + if opts.SourceType != nil { + query = query.Where("source_type = ?", *opts.SourceType) + } + if opts.SourceID != nil { + query = query.Where("source_id = ?", *opts.SourceID) + } + if opts.Level != nil { + query = query.Where("level = ?", *opts.Level) + } + if opts.TriggerTimeStart != nil { + query = query.Where("trigger_time >= ?", *opts.TriggerTimeStart) + } + if opts.TriggerTimeEnd != nil { + query = query.Where("trigger_time <= ?", *opts.TriggerTimeEnd) + } + if opts.ResolveTimeStart != nil { // 修改字段名 + query = query.Where("resolve_time >= ?", *opts.ResolveTimeStart) // 修改查询字段名 + } + if opts.ResolveTimeEnd != nil { // 修改字段名 + query = query.Where("resolve_time <= ?", *opts.ResolveTimeEnd) // 修改查询字段名 + } + + // --- 计算总数 --- + if err := query.Count(&total).Error; err != nil { + return nil, 0, err + } + + // --- 应用排序条件 --- + orderBy := "trigger_time DESC" // 默认按触发时间倒序 + if opts.OrderBy != "" { + orderBy = opts.OrderBy + } + query = query.Order(orderBy) + + // --- 分页 --- + offset := (page - 1) * pageSize + err := query.Limit(pageSize).Offset(offset).Find(&results).Error + + return results, total, err +} + func (r *gormAlarmRepository) UpdateAlarmNotificationStatus(ctx context.Context, alarmID uint, lastNotifiedAt time.Time, isIgnored bool, ignoredUntil *time.Time) error { repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdateAlarmNotificationStatus") diff --git a/project_structure.txt b/project_structure.txt index f56100c..5f20ab7 100644 --- a/project_structure.txt +++ b/project_structure.txt @@ -54,9 +54,11 @@ internal/app/controller/monitor/monitor_controller.go internal/app/controller/plan/plan_controller.go internal/app/controller/response.go internal/app/controller/user/user_controller.go +internal/app/dto/alarm_converter.go internal/app/dto/alarm_dto.go internal/app/dto/device_converter.go internal/app/dto/device_dto.go +internal/app/dto/dto.go internal/app/dto/monitor_converter.go internal/app/dto/monitor_dto.go internal/app/dto/notification_converter.go