Compare commits
	
		
			6 Commits
		
	
	
		
			bd8729d473
			...
			43c1839345
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 43c1839345 | |||
| f62cc1c4a9 | |||
| f6d2069e1a | |||
| f33e14f60f | |||
| d6f275b2d1 | |||
| d8de5a68eb | 
							
								
								
									
										259
									
								
								docs/docs.go
									
									
									
									
									
								
							
							
						
						
									
										259
									
								
								docs/docs.go
									
									
									
									
									
								
							| @@ -975,6 +975,149 @@ const docTemplate = `{ | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "/api/v1/monitor/notifications": { | ||||||
|  |             "get": { | ||||||
|  |                 "security": [ | ||||||
|  |                     { | ||||||
|  |                         "BearerAuth": [] | ||||||
|  |                     } | ||||||
|  |                 ], | ||||||
|  |                 "description": "根据提供的过滤条件,分页获取通知列表", | ||||||
|  |                 "produces": [ | ||||||
|  |                     "application/json" | ||||||
|  |                 ], | ||||||
|  |                 "tags": [ | ||||||
|  |                     "数据监控" | ||||||
|  |                 ], | ||||||
|  |                 "summary": "批量查询通知", | ||||||
|  |                 "parameters": [ | ||||||
|  |                     { | ||||||
|  |                         "type": "string", | ||||||
|  |                         "name": "end_time", | ||||||
|  |                         "in": "query" | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         "enum": [ | ||||||
|  |                             7, | ||||||
|  |                             -1, | ||||||
|  |                             0, | ||||||
|  |                             1, | ||||||
|  |                             2, | ||||||
|  |                             3, | ||||||
|  |                             4, | ||||||
|  |                             5, | ||||||
|  |                             -1, | ||||||
|  |                             5, | ||||||
|  |                             6 | ||||||
|  |                         ], | ||||||
|  |                         "type": "integer", | ||||||
|  |                         "format": "int32", | ||||||
|  |                         "x-enum-varnames": [ | ||||||
|  |                             "_numLevels", | ||||||
|  |                             "DebugLevel", | ||||||
|  |                             "InfoLevel", | ||||||
|  |                             "WarnLevel", | ||||||
|  |                             "ErrorLevel", | ||||||
|  |                             "DPanicLevel", | ||||||
|  |                             "PanicLevel", | ||||||
|  |                             "FatalLevel", | ||||||
|  |                             "_minLevel", | ||||||
|  |                             "_maxLevel", | ||||||
|  |                             "InvalidLevel" | ||||||
|  |                         ], | ||||||
|  |                         "name": "level", | ||||||
|  |                         "in": "query" | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         "enum": [ | ||||||
|  |                             "邮件", | ||||||
|  |                             "企业微信", | ||||||
|  |                             "飞书", | ||||||
|  |                             "日志" | ||||||
|  |                         ], | ||||||
|  |                         "type": "string", | ||||||
|  |                         "x-enum-varnames": [ | ||||||
|  |                             "NotifierTypeSMTP", | ||||||
|  |                             "NotifierTypeWeChat", | ||||||
|  |                             "NotifierTypeLark", | ||||||
|  |                             "NotifierTypeLog" | ||||||
|  |                         ], | ||||||
|  |                         "name": "notifier_type", | ||||||
|  |                         "in": "query" | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         "type": "string", | ||||||
|  |                         "name": "order_by", | ||||||
|  |                         "in": "query" | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         "type": "integer", | ||||||
|  |                         "name": "page", | ||||||
|  |                         "in": "query" | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         "type": "integer", | ||||||
|  |                         "name": "pageSize", | ||||||
|  |                         "in": "query" | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         "type": "string", | ||||||
|  |                         "name": "start_time", | ||||||
|  |                         "in": "query" | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         "enum": [ | ||||||
|  |                             "发送成功", | ||||||
|  |                             "发送失败", | ||||||
|  |                             "已跳过" | ||||||
|  |                         ], | ||||||
|  |                         "type": "string", | ||||||
|  |                         "x-enum-comments": { | ||||||
|  |                             "NotificationStatusFailed": "通知发送失败", | ||||||
|  |                             "NotificationStatusSkipped": "通知因某些原因被跳过(例如:用户未配置联系方式)", | ||||||
|  |                             "NotificationStatusSuccess": "通知已成功发送" | ||||||
|  |                         }, | ||||||
|  |                         "x-enum-descriptions": [ | ||||||
|  |                             "通知已成功发送", | ||||||
|  |                             "通知发送失败", | ||||||
|  |                             "通知因某些原因被跳过(例如:用户未配置联系方式)" | ||||||
|  |                         ], | ||||||
|  |                         "x-enum-varnames": [ | ||||||
|  |                             "NotificationStatusSuccess", | ||||||
|  |                             "NotificationStatusFailed", | ||||||
|  |                             "NotificationStatusSkipped" | ||||||
|  |                         ], | ||||||
|  |                         "name": "status", | ||||||
|  |                         "in": "query" | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         "type": "integer", | ||||||
|  |                         "name": "user_id", | ||||||
|  |                         "in": "query" | ||||||
|  |                     } | ||||||
|  |                 ], | ||||||
|  |                 "responses": { | ||||||
|  |                     "200": { | ||||||
|  |                         "description": "OK", | ||||||
|  |                         "schema": { | ||||||
|  |                             "allOf": [ | ||||||
|  |                                 { | ||||||
|  |                                     "$ref": "#/definitions/controller.Response" | ||||||
|  |                                 }, | ||||||
|  |                                 { | ||||||
|  |                                     "type": "object", | ||||||
|  |                                     "properties": { | ||||||
|  |                                         "data": { | ||||||
|  |                                             "$ref": "#/definitions/dto.ListNotificationResponse" | ||||||
|  |                                         } | ||||||
|  |                                     } | ||||||
|  |                                 } | ||||||
|  |                             ] | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "/api/v1/monitor/pending-collections": { |         "/api/v1/monitor/pending-collections": { | ||||||
|             "get": { |             "get": { | ||||||
|                 "security": [ |                 "security": [ | ||||||
| @@ -4489,6 +4632,20 @@ const docTemplate = `{ | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "dto.ListNotificationResponse": { | ||||||
|  |             "type": "object", | ||||||
|  |             "properties": { | ||||||
|  |                 "list": { | ||||||
|  |                     "type": "array", | ||||||
|  |                     "items": { | ||||||
|  |                         "$ref": "#/definitions/dto.NotificationDTO" | ||||||
|  |                     } | ||||||
|  |                 }, | ||||||
|  |                 "pagination": { | ||||||
|  |                     "$ref": "#/definitions/dto.PaginationDTO" | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "dto.ListPendingCollectionResponse": { |         "dto.ListPendingCollectionResponse": { | ||||||
|             "type": "object", |             "type": "object", | ||||||
|             "properties": { |             "properties": { | ||||||
| @@ -4797,6 +4954,47 @@ const docTemplate = `{ | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "dto.NotificationDTO": { | ||||||
|  |             "type": "object", | ||||||
|  |             "properties": { | ||||||
|  |                 "alarm_timestamp": { | ||||||
|  |                     "type": "string" | ||||||
|  |                 }, | ||||||
|  |                 "created_at": { | ||||||
|  |                     "type": "string" | ||||||
|  |                 }, | ||||||
|  |                 "error_message": { | ||||||
|  |                     "type": "string" | ||||||
|  |                 }, | ||||||
|  |                 "id": { | ||||||
|  |                     "type": "integer" | ||||||
|  |                 }, | ||||||
|  |                 "level": { | ||||||
|  |                     "$ref": "#/definitions/zapcore.Level" | ||||||
|  |                 }, | ||||||
|  |                 "message": { | ||||||
|  |                     "type": "string" | ||||||
|  |                 }, | ||||||
|  |                 "notifier_type": { | ||||||
|  |                     "$ref": "#/definitions/notify.NotifierType" | ||||||
|  |                 }, | ||||||
|  |                 "status": { | ||||||
|  |                     "$ref": "#/definitions/models.NotificationStatus" | ||||||
|  |                 }, | ||||||
|  |                 "title": { | ||||||
|  |                     "type": "string" | ||||||
|  |                 }, | ||||||
|  |                 "to_address": { | ||||||
|  |                     "type": "string" | ||||||
|  |                 }, | ||||||
|  |                 "updated_at": { | ||||||
|  |                     "type": "string" | ||||||
|  |                 }, | ||||||
|  |                 "user_id": { | ||||||
|  |                     "type": "integer" | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "dto.PaginationDTO": { |         "dto.PaginationDTO": { | ||||||
|             "type": "object", |             "type": "object", | ||||||
|             "properties": { |             "properties": { | ||||||
| @@ -6275,6 +6473,29 @@ const docTemplate = `{ | |||||||
|                 "ReasonTypeHealthCare" |                 "ReasonTypeHealthCare" | ||||||
|             ] |             ] | ||||||
|         }, |         }, | ||||||
|  |         "models.NotificationStatus": { | ||||||
|  |             "type": "string", | ||||||
|  |             "enum": [ | ||||||
|  |                 "发送成功", | ||||||
|  |                 "发送失败", | ||||||
|  |                 "已跳过" | ||||||
|  |             ], | ||||||
|  |             "x-enum-comments": { | ||||||
|  |                 "NotificationStatusFailed": "通知发送失败", | ||||||
|  |                 "NotificationStatusSkipped": "通知因某些原因被跳过(例如:用户未配置联系方式)", | ||||||
|  |                 "NotificationStatusSuccess": "通知已成功发送" | ||||||
|  |             }, | ||||||
|  |             "x-enum-descriptions": [ | ||||||
|  |                 "通知已成功发送", | ||||||
|  |                 "通知发送失败", | ||||||
|  |                 "通知因某些原因被跳过(例如:用户未配置联系方式)" | ||||||
|  |             ], | ||||||
|  |             "x-enum-varnames": [ | ||||||
|  |                 "NotificationStatusSuccess", | ||||||
|  |                 "NotificationStatusFailed", | ||||||
|  |                 "NotificationStatusSkipped" | ||||||
|  |             ] | ||||||
|  |         }, | ||||||
|         "models.PenStatus": { |         "models.PenStatus": { | ||||||
|             "type": "string", |             "type": "string", | ||||||
|             "enum": [ |             "enum": [ | ||||||
| @@ -6608,10 +6829,10 @@ const docTemplate = `{ | |||||||
|         "notify.NotifierType": { |         "notify.NotifierType": { | ||||||
|             "type": "string", |             "type": "string", | ||||||
|             "enum": [ |             "enum": [ | ||||||
|                 "smtp", |                 "邮件", | ||||||
|                 "wechat", |                 "企业微信", | ||||||
|                 "lark", |                 "飞书", | ||||||
|                 "log" |                 "日志" | ||||||
|             ], |             ], | ||||||
|             "x-enum-varnames": [ |             "x-enum-varnames": [ | ||||||
|                 "NotifierTypeSMTP", |                 "NotifierTypeSMTP", | ||||||
| @@ -6619,6 +6840,36 @@ const docTemplate = `{ | |||||||
|                 "NotifierTypeLark", |                 "NotifierTypeLark", | ||||||
|                 "NotifierTypeLog" |                 "NotifierTypeLog" | ||||||
|             ] |             ] | ||||||
|  |         }, | ||||||
|  |         "zapcore.Level": { | ||||||
|  |             "type": "integer", | ||||||
|  |             "format": "int32", | ||||||
|  |             "enum": [ | ||||||
|  |                 7, | ||||||
|  |                 -1, | ||||||
|  |                 0, | ||||||
|  |                 1, | ||||||
|  |                 2, | ||||||
|  |                 3, | ||||||
|  |                 4, | ||||||
|  |                 5, | ||||||
|  |                 -1, | ||||||
|  |                 5, | ||||||
|  |                 6 | ||||||
|  |             ], | ||||||
|  |             "x-enum-varnames": [ | ||||||
|  |                 "_numLevels", | ||||||
|  |                 "DebugLevel", | ||||||
|  |                 "InfoLevel", | ||||||
|  |                 "WarnLevel", | ||||||
|  |                 "ErrorLevel", | ||||||
|  |                 "DPanicLevel", | ||||||
|  |                 "PanicLevel", | ||||||
|  |                 "FatalLevel", | ||||||
|  |                 "_minLevel", | ||||||
|  |                 "_maxLevel", | ||||||
|  |                 "InvalidLevel" | ||||||
|  |             ] | ||||||
|         } |         } | ||||||
|     }, |     }, | ||||||
|     "securityDefinitions": { |     "securityDefinitions": { | ||||||
|   | |||||||
| @@ -967,6 +967,149 @@ | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "/api/v1/monitor/notifications": { | ||||||
|  |             "get": { | ||||||
|  |                 "security": [ | ||||||
|  |                     { | ||||||
|  |                         "BearerAuth": [] | ||||||
|  |                     } | ||||||
|  |                 ], | ||||||
|  |                 "description": "根据提供的过滤条件,分页获取通知列表", | ||||||
|  |                 "produces": [ | ||||||
|  |                     "application/json" | ||||||
|  |                 ], | ||||||
|  |                 "tags": [ | ||||||
|  |                     "数据监控" | ||||||
|  |                 ], | ||||||
|  |                 "summary": "批量查询通知", | ||||||
|  |                 "parameters": [ | ||||||
|  |                     { | ||||||
|  |                         "type": "string", | ||||||
|  |                         "name": "end_time", | ||||||
|  |                         "in": "query" | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         "enum": [ | ||||||
|  |                             7, | ||||||
|  |                             -1, | ||||||
|  |                             0, | ||||||
|  |                             1, | ||||||
|  |                             2, | ||||||
|  |                             3, | ||||||
|  |                             4, | ||||||
|  |                             5, | ||||||
|  |                             -1, | ||||||
|  |                             5, | ||||||
|  |                             6 | ||||||
|  |                         ], | ||||||
|  |                         "type": "integer", | ||||||
|  |                         "format": "int32", | ||||||
|  |                         "x-enum-varnames": [ | ||||||
|  |                             "_numLevels", | ||||||
|  |                             "DebugLevel", | ||||||
|  |                             "InfoLevel", | ||||||
|  |                             "WarnLevel", | ||||||
|  |                             "ErrorLevel", | ||||||
|  |                             "DPanicLevel", | ||||||
|  |                             "PanicLevel", | ||||||
|  |                             "FatalLevel", | ||||||
|  |                             "_minLevel", | ||||||
|  |                             "_maxLevel", | ||||||
|  |                             "InvalidLevel" | ||||||
|  |                         ], | ||||||
|  |                         "name": "level", | ||||||
|  |                         "in": "query" | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         "enum": [ | ||||||
|  |                             "邮件", | ||||||
|  |                             "企业微信", | ||||||
|  |                             "飞书", | ||||||
|  |                             "日志" | ||||||
|  |                         ], | ||||||
|  |                         "type": "string", | ||||||
|  |                         "x-enum-varnames": [ | ||||||
|  |                             "NotifierTypeSMTP", | ||||||
|  |                             "NotifierTypeWeChat", | ||||||
|  |                             "NotifierTypeLark", | ||||||
|  |                             "NotifierTypeLog" | ||||||
|  |                         ], | ||||||
|  |                         "name": "notifier_type", | ||||||
|  |                         "in": "query" | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         "type": "string", | ||||||
|  |                         "name": "order_by", | ||||||
|  |                         "in": "query" | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         "type": "integer", | ||||||
|  |                         "name": "page", | ||||||
|  |                         "in": "query" | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         "type": "integer", | ||||||
|  |                         "name": "pageSize", | ||||||
|  |                         "in": "query" | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         "type": "string", | ||||||
|  |                         "name": "start_time", | ||||||
|  |                         "in": "query" | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         "enum": [ | ||||||
|  |                             "发送成功", | ||||||
|  |                             "发送失败", | ||||||
|  |                             "已跳过" | ||||||
|  |                         ], | ||||||
|  |                         "type": "string", | ||||||
|  |                         "x-enum-comments": { | ||||||
|  |                             "NotificationStatusFailed": "通知发送失败", | ||||||
|  |                             "NotificationStatusSkipped": "通知因某些原因被跳过(例如:用户未配置联系方式)", | ||||||
|  |                             "NotificationStatusSuccess": "通知已成功发送" | ||||||
|  |                         }, | ||||||
|  |                         "x-enum-descriptions": [ | ||||||
|  |                             "通知已成功发送", | ||||||
|  |                             "通知发送失败", | ||||||
|  |                             "通知因某些原因被跳过(例如:用户未配置联系方式)" | ||||||
|  |                         ], | ||||||
|  |                         "x-enum-varnames": [ | ||||||
|  |                             "NotificationStatusSuccess", | ||||||
|  |                             "NotificationStatusFailed", | ||||||
|  |                             "NotificationStatusSkipped" | ||||||
|  |                         ], | ||||||
|  |                         "name": "status", | ||||||
|  |                         "in": "query" | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         "type": "integer", | ||||||
|  |                         "name": "user_id", | ||||||
|  |                         "in": "query" | ||||||
|  |                     } | ||||||
|  |                 ], | ||||||
|  |                 "responses": { | ||||||
|  |                     "200": { | ||||||
|  |                         "description": "OK", | ||||||
|  |                         "schema": { | ||||||
|  |                             "allOf": [ | ||||||
|  |                                 { | ||||||
|  |                                     "$ref": "#/definitions/controller.Response" | ||||||
|  |                                 }, | ||||||
|  |                                 { | ||||||
|  |                                     "type": "object", | ||||||
|  |                                     "properties": { | ||||||
|  |                                         "data": { | ||||||
|  |                                             "$ref": "#/definitions/dto.ListNotificationResponse" | ||||||
|  |                                         } | ||||||
|  |                                     } | ||||||
|  |                                 } | ||||||
|  |                             ] | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "/api/v1/monitor/pending-collections": { |         "/api/v1/monitor/pending-collections": { | ||||||
|             "get": { |             "get": { | ||||||
|                 "security": [ |                 "security": [ | ||||||
| @@ -4481,6 +4624,20 @@ | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "dto.ListNotificationResponse": { | ||||||
|  |             "type": "object", | ||||||
|  |             "properties": { | ||||||
|  |                 "list": { | ||||||
|  |                     "type": "array", | ||||||
|  |                     "items": { | ||||||
|  |                         "$ref": "#/definitions/dto.NotificationDTO" | ||||||
|  |                     } | ||||||
|  |                 }, | ||||||
|  |                 "pagination": { | ||||||
|  |                     "$ref": "#/definitions/dto.PaginationDTO" | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "dto.ListPendingCollectionResponse": { |         "dto.ListPendingCollectionResponse": { | ||||||
|             "type": "object", |             "type": "object", | ||||||
|             "properties": { |             "properties": { | ||||||
| @@ -4789,6 +4946,47 @@ | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "dto.NotificationDTO": { | ||||||
|  |             "type": "object", | ||||||
|  |             "properties": { | ||||||
|  |                 "alarm_timestamp": { | ||||||
|  |                     "type": "string" | ||||||
|  |                 }, | ||||||
|  |                 "created_at": { | ||||||
|  |                     "type": "string" | ||||||
|  |                 }, | ||||||
|  |                 "error_message": { | ||||||
|  |                     "type": "string" | ||||||
|  |                 }, | ||||||
|  |                 "id": { | ||||||
|  |                     "type": "integer" | ||||||
|  |                 }, | ||||||
|  |                 "level": { | ||||||
|  |                     "$ref": "#/definitions/zapcore.Level" | ||||||
|  |                 }, | ||||||
|  |                 "message": { | ||||||
|  |                     "type": "string" | ||||||
|  |                 }, | ||||||
|  |                 "notifier_type": { | ||||||
|  |                     "$ref": "#/definitions/notify.NotifierType" | ||||||
|  |                 }, | ||||||
|  |                 "status": { | ||||||
|  |                     "$ref": "#/definitions/models.NotificationStatus" | ||||||
|  |                 }, | ||||||
|  |                 "title": { | ||||||
|  |                     "type": "string" | ||||||
|  |                 }, | ||||||
|  |                 "to_address": { | ||||||
|  |                     "type": "string" | ||||||
|  |                 }, | ||||||
|  |                 "updated_at": { | ||||||
|  |                     "type": "string" | ||||||
|  |                 }, | ||||||
|  |                 "user_id": { | ||||||
|  |                     "type": "integer" | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "dto.PaginationDTO": { |         "dto.PaginationDTO": { | ||||||
|             "type": "object", |             "type": "object", | ||||||
|             "properties": { |             "properties": { | ||||||
| @@ -6267,6 +6465,29 @@ | |||||||
|                 "ReasonTypeHealthCare" |                 "ReasonTypeHealthCare" | ||||||
|             ] |             ] | ||||||
|         }, |         }, | ||||||
|  |         "models.NotificationStatus": { | ||||||
|  |             "type": "string", | ||||||
|  |             "enum": [ | ||||||
|  |                 "发送成功", | ||||||
|  |                 "发送失败", | ||||||
|  |                 "已跳过" | ||||||
|  |             ], | ||||||
|  |             "x-enum-comments": { | ||||||
|  |                 "NotificationStatusFailed": "通知发送失败", | ||||||
|  |                 "NotificationStatusSkipped": "通知因某些原因被跳过(例如:用户未配置联系方式)", | ||||||
|  |                 "NotificationStatusSuccess": "通知已成功发送" | ||||||
|  |             }, | ||||||
|  |             "x-enum-descriptions": [ | ||||||
|  |                 "通知已成功发送", | ||||||
|  |                 "通知发送失败", | ||||||
|  |                 "通知因某些原因被跳过(例如:用户未配置联系方式)" | ||||||
|  |             ], | ||||||
|  |             "x-enum-varnames": [ | ||||||
|  |                 "NotificationStatusSuccess", | ||||||
|  |                 "NotificationStatusFailed", | ||||||
|  |                 "NotificationStatusSkipped" | ||||||
|  |             ] | ||||||
|  |         }, | ||||||
|         "models.PenStatus": { |         "models.PenStatus": { | ||||||
|             "type": "string", |             "type": "string", | ||||||
|             "enum": [ |             "enum": [ | ||||||
| @@ -6600,10 +6821,10 @@ | |||||||
|         "notify.NotifierType": { |         "notify.NotifierType": { | ||||||
|             "type": "string", |             "type": "string", | ||||||
|             "enum": [ |             "enum": [ | ||||||
|                 "smtp", |                 "邮件", | ||||||
|                 "wechat", |                 "企业微信", | ||||||
|                 "lark", |                 "飞书", | ||||||
|                 "log" |                 "日志" | ||||||
|             ], |             ], | ||||||
|             "x-enum-varnames": [ |             "x-enum-varnames": [ | ||||||
|                 "NotifierTypeSMTP", |                 "NotifierTypeSMTP", | ||||||
| @@ -6611,6 +6832,36 @@ | |||||||
|                 "NotifierTypeLark", |                 "NotifierTypeLark", | ||||||
|                 "NotifierTypeLog" |                 "NotifierTypeLog" | ||||||
|             ] |             ] | ||||||
|  |         }, | ||||||
|  |         "zapcore.Level": { | ||||||
|  |             "type": "integer", | ||||||
|  |             "format": "int32", | ||||||
|  |             "enum": [ | ||||||
|  |                 7, | ||||||
|  |                 -1, | ||||||
|  |                 0, | ||||||
|  |                 1, | ||||||
|  |                 2, | ||||||
|  |                 3, | ||||||
|  |                 4, | ||||||
|  |                 5, | ||||||
|  |                 -1, | ||||||
|  |                 5, | ||||||
|  |                 6 | ||||||
|  |             ], | ||||||
|  |             "x-enum-varnames": [ | ||||||
|  |                 "_numLevels", | ||||||
|  |                 "DebugLevel", | ||||||
|  |                 "InfoLevel", | ||||||
|  |                 "WarnLevel", | ||||||
|  |                 "ErrorLevel", | ||||||
|  |                 "DPanicLevel", | ||||||
|  |                 "PanicLevel", | ||||||
|  |                 "FatalLevel", | ||||||
|  |                 "_minLevel", | ||||||
|  |                 "_maxLevel", | ||||||
|  |                 "InvalidLevel" | ||||||
|  |             ] | ||||||
|         } |         } | ||||||
|     }, |     }, | ||||||
|     "securityDefinitions": { |     "securityDefinitions": { | ||||||
|   | |||||||
| @@ -349,6 +349,15 @@ definitions: | |||||||
|       pagination: |       pagination: | ||||||
|         $ref: '#/definitions/dto.PaginationDTO' |         $ref: '#/definitions/dto.PaginationDTO' | ||||||
|     type: object |     type: object | ||||||
|  |   dto.ListNotificationResponse: | ||||||
|  |     properties: | ||||||
|  |       list: | ||||||
|  |         items: | ||||||
|  |           $ref: '#/definitions/dto.NotificationDTO' | ||||||
|  |         type: array | ||||||
|  |       pagination: | ||||||
|  |         $ref: '#/definitions/dto.PaginationDTO' | ||||||
|  |     type: object | ||||||
|   dto.ListPendingCollectionResponse: |   dto.ListPendingCollectionResponse: | ||||||
|     properties: |     properties: | ||||||
|       list: |       list: | ||||||
| @@ -552,6 +561,33 @@ definitions: | |||||||
|     - quantity |     - quantity | ||||||
|     - toPenID |     - toPenID | ||||||
|     type: object |     type: object | ||||||
|  |   dto.NotificationDTO: | ||||||
|  |     properties: | ||||||
|  |       alarm_timestamp: | ||||||
|  |         type: string | ||||||
|  |       created_at: | ||||||
|  |         type: string | ||||||
|  |       error_message: | ||||||
|  |         type: string | ||||||
|  |       id: | ||||||
|  |         type: integer | ||||||
|  |       level: | ||||||
|  |         $ref: '#/definitions/zapcore.Level' | ||||||
|  |       message: | ||||||
|  |         type: string | ||||||
|  |       notifier_type: | ||||||
|  |         $ref: '#/definitions/notify.NotifierType' | ||||||
|  |       status: | ||||||
|  |         $ref: '#/definitions/models.NotificationStatus' | ||||||
|  |       title: | ||||||
|  |         type: string | ||||||
|  |       to_address: | ||||||
|  |         type: string | ||||||
|  |       updated_at: | ||||||
|  |         type: string | ||||||
|  |       user_id: | ||||||
|  |         type: integer | ||||||
|  |     type: object | ||||||
|   dto.PaginationDTO: |   dto.PaginationDTO: | ||||||
|     properties: |     properties: | ||||||
|       page: |       page: | ||||||
| @@ -1557,6 +1593,24 @@ definitions: | |||||||
|     - ReasonTypePreventive |     - ReasonTypePreventive | ||||||
|     - ReasonTypeTreatment |     - ReasonTypeTreatment | ||||||
|     - ReasonTypeHealthCare |     - ReasonTypeHealthCare | ||||||
|  |   models.NotificationStatus: | ||||||
|  |     enum: | ||||||
|  |     - 发送成功 | ||||||
|  |     - 发送失败 | ||||||
|  |     - 已跳过 | ||||||
|  |     type: string | ||||||
|  |     x-enum-comments: | ||||||
|  |       NotificationStatusFailed: 通知发送失败 | ||||||
|  |       NotificationStatusSkipped: 通知因某些原因被跳过(例如:用户未配置联系方式) | ||||||
|  |       NotificationStatusSuccess: 通知已成功发送 | ||||||
|  |     x-enum-descriptions: | ||||||
|  |     - 通知已成功发送 | ||||||
|  |     - 通知发送失败 | ||||||
|  |     - 通知因某些原因被跳过(例如:用户未配置联系方式) | ||||||
|  |     x-enum-varnames: | ||||||
|  |     - NotificationStatusSuccess | ||||||
|  |     - NotificationStatusFailed | ||||||
|  |     - NotificationStatusSkipped | ||||||
|   models.PenStatus: |   models.PenStatus: | ||||||
|     enum: |     enum: | ||||||
|     - 空闲 |     - 空闲 | ||||||
| @@ -1827,16 +1881,43 @@ definitions: | |||||||
|     type: object |     type: object | ||||||
|   notify.NotifierType: |   notify.NotifierType: | ||||||
|     enum: |     enum: | ||||||
|     - smtp |     - 邮件 | ||||||
|     - wechat |     - 企业微信 | ||||||
|     - lark |     - 飞书 | ||||||
|     - log |     - 日志 | ||||||
|     type: string |     type: string | ||||||
|     x-enum-varnames: |     x-enum-varnames: | ||||||
|     - NotifierTypeSMTP |     - NotifierTypeSMTP | ||||||
|     - NotifierTypeWeChat |     - NotifierTypeWeChat | ||||||
|     - NotifierTypeLark |     - NotifierTypeLark | ||||||
|     - NotifierTypeLog |     - NotifierTypeLog | ||||||
|  |   zapcore.Level: | ||||||
|  |     enum: | ||||||
|  |     - 7 | ||||||
|  |     - -1 | ||||||
|  |     - 0 | ||||||
|  |     - 1 | ||||||
|  |     - 2 | ||||||
|  |     - 3 | ||||||
|  |     - 4 | ||||||
|  |     - 5 | ||||||
|  |     - -1 | ||||||
|  |     - 5 | ||||||
|  |     - 6 | ||||||
|  |     format: int32 | ||||||
|  |     type: integer | ||||||
|  |     x-enum-varnames: | ||||||
|  |     - _numLevels | ||||||
|  |     - DebugLevel | ||||||
|  |     - InfoLevel | ||||||
|  |     - WarnLevel | ||||||
|  |     - ErrorLevel | ||||||
|  |     - DPanicLevel | ||||||
|  |     - PanicLevel | ||||||
|  |     - FatalLevel | ||||||
|  |     - _minLevel | ||||||
|  |     - _maxLevel | ||||||
|  |     - InvalidLevel | ||||||
| info: | info: | ||||||
|   contact: |   contact: | ||||||
|     email: divano@example.com |     email: divano@example.com | ||||||
| @@ -2400,6 +2481,105 @@ paths: | |||||||
|       summary: 获取用药记录列表 |       summary: 获取用药记录列表 | ||||||
|       tags: |       tags: | ||||||
|       - 数据监控 |       - 数据监控 | ||||||
|  |   /api/v1/monitor/notifications: | ||||||
|  |     get: | ||||||
|  |       description: 根据提供的过滤条件,分页获取通知列表 | ||||||
|  |       parameters: | ||||||
|  |       - in: query | ||||||
|  |         name: end_time | ||||||
|  |         type: string | ||||||
|  |       - enum: | ||||||
|  |         - 7 | ||||||
|  |         - -1 | ||||||
|  |         - 0 | ||||||
|  |         - 1 | ||||||
|  |         - 2 | ||||||
|  |         - 3 | ||||||
|  |         - 4 | ||||||
|  |         - 5 | ||||||
|  |         - -1 | ||||||
|  |         - 5 | ||||||
|  |         - 6 | ||||||
|  |         format: int32 | ||||||
|  |         in: query | ||||||
|  |         name: level | ||||||
|  |         type: integer | ||||||
|  |         x-enum-varnames: | ||||||
|  |         - _numLevels | ||||||
|  |         - DebugLevel | ||||||
|  |         - InfoLevel | ||||||
|  |         - WarnLevel | ||||||
|  |         - ErrorLevel | ||||||
|  |         - DPanicLevel | ||||||
|  |         - PanicLevel | ||||||
|  |         - FatalLevel | ||||||
|  |         - _minLevel | ||||||
|  |         - _maxLevel | ||||||
|  |         - InvalidLevel | ||||||
|  |       - enum: | ||||||
|  |         - 邮件 | ||||||
|  |         - 企业微信 | ||||||
|  |         - 飞书 | ||||||
|  |         - 日志 | ||||||
|  |         in: query | ||||||
|  |         name: notifier_type | ||||||
|  |         type: string | ||||||
|  |         x-enum-varnames: | ||||||
|  |         - NotifierTypeSMTP | ||||||
|  |         - NotifierTypeWeChat | ||||||
|  |         - NotifierTypeLark | ||||||
|  |         - NotifierTypeLog | ||||||
|  |       - in: query | ||||||
|  |         name: order_by | ||||||
|  |         type: string | ||||||
|  |       - in: query | ||||||
|  |         name: page | ||||||
|  |         type: integer | ||||||
|  |       - in: query | ||||||
|  |         name: pageSize | ||||||
|  |         type: integer | ||||||
|  |       - in: query | ||||||
|  |         name: start_time | ||||||
|  |         type: string | ||||||
|  |       - enum: | ||||||
|  |         - 发送成功 | ||||||
|  |         - 发送失败 | ||||||
|  |         - 已跳过 | ||||||
|  |         in: query | ||||||
|  |         name: status | ||||||
|  |         type: string | ||||||
|  |         x-enum-comments: | ||||||
|  |           NotificationStatusFailed: 通知发送失败 | ||||||
|  |           NotificationStatusSkipped: 通知因某些原因被跳过(例如:用户未配置联系方式) | ||||||
|  |           NotificationStatusSuccess: 通知已成功发送 | ||||||
|  |         x-enum-descriptions: | ||||||
|  |         - 通知已成功发送 | ||||||
|  |         - 通知发送失败 | ||||||
|  |         - 通知因某些原因被跳过(例如:用户未配置联系方式) | ||||||
|  |         x-enum-varnames: | ||||||
|  |         - NotificationStatusSuccess | ||||||
|  |         - NotificationStatusFailed | ||||||
|  |         - NotificationStatusSkipped | ||||||
|  |       - in: query | ||||||
|  |         name: user_id | ||||||
|  |         type: integer | ||||||
|  |       produces: | ||||||
|  |       - application/json | ||||||
|  |       responses: | ||||||
|  |         "200": | ||||||
|  |           description: OK | ||||||
|  |           schema: | ||||||
|  |             allOf: | ||||||
|  |             - $ref: '#/definitions/controller.Response' | ||||||
|  |             - properties: | ||||||
|  |                 data: | ||||||
|  |                   $ref: '#/definitions/dto.ListNotificationResponse' | ||||||
|  |               type: object | ||||||
|  |       security: | ||||||
|  |       - BearerAuth: [] | ||||||
|  |       summary: 批量查询通知 | ||||||
|  |       tags: | ||||||
|  |       - 数据监控 | ||||||
|   /api/v1/monitor/pending-collections: |   /api/v1/monitor/pending-collections: | ||||||
|     get: |     get: | ||||||
|       description: 根据提供的过滤条件,分页获取待采集请求 |       description: 根据提供的过滤条件,分页获取待采集请求 | ||||||
|   | |||||||
| @@ -176,6 +176,7 @@ func (a *API) setupRoutes() { | |||||||
| 			monitorGroup.GET("/pig-sick-logs", a.monitorController.ListPigSickLogs) | 			monitorGroup.GET("/pig-sick-logs", a.monitorController.ListPigSickLogs) | ||||||
| 			monitorGroup.GET("/pig-purchases", a.monitorController.ListPigPurchases) | 			monitorGroup.GET("/pig-purchases", a.monitorController.ListPigPurchases) | ||||||
| 			monitorGroup.GET("/pig-sales", a.monitorController.ListPigSales) | 			monitorGroup.GET("/pig-sales", a.monitorController.ListPigSales) | ||||||
|  | 			monitorGroup.GET("/notifications", a.monitorController.ListNotifications) | ||||||
| 		} | 		} | ||||||
| 		a.logger.Info("数据监控相关接口注册成功 (需要认证和审计)") | 		a.logger.Info("数据监控相关接口注册成功 (需要认证和审计)") | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -839,3 +839,50 @@ func (c *Controller) ListPigSales(ctx *gin.Context) { | |||||||
| 	c.logger.Infof("%s: 成功, 获取到 %d 条记录, 总计 %d 条", actionType, len(data), total) | 	c.logger.Infof("%s: 成功, 获取到 %d 条记录, 总计 %d 条", actionType, len(data), total) | ||||||
| 	controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取猪只售卖记录成功", resp, actionType, "获取猪只售卖记录成功", req) | 	controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取猪只售卖记录成功", resp, actionType, "获取猪只售卖记录成功", req) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // ListNotifications godoc | ||||||
|  | // @Summary      批量查询通知 | ||||||
|  | // @Description  根据提供的过滤条件,分页获取通知列表 | ||||||
|  | // @Tags         数据监控 | ||||||
|  | // @Security     BearerAuth | ||||||
|  | // @Produce      json | ||||||
|  | // @Param        query query dto.ListNotificationRequest true "查询参数" | ||||||
|  | // @Success      200 {object} controller.Response{data=dto.ListNotificationResponse} | ||||||
|  | // @Router       /api/v1/monitor/notifications [get] | ||||||
|  | func (c *Controller) ListNotifications(ctx *gin.Context) { | ||||||
|  | 	const actionType = "批量查询通知" | ||||||
|  |  | ||||||
|  | 	var req dto.ListNotificationRequest | ||||||
|  | 	if err := ctx.ShouldBindQuery(&req); err != nil { | ||||||
|  | 		c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err) | ||||||
|  | 		controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "参数绑定失败", req) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	opts := repository.NotificationListOptions{ | ||||||
|  | 		UserID:       req.UserID, | ||||||
|  | 		NotifierType: req.NotifierType, | ||||||
|  | 		Level:        req.Level, | ||||||
|  | 		StartTime:    req.StartTime, | ||||||
|  | 		EndTime:      req.EndTime, | ||||||
|  | 		OrderBy:      req.OrderBy, | ||||||
|  | 		Status:       req.Status, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	data, total, err := c.monitorService.ListNotifications(opts, req.Page, req.PageSize) | ||||||
|  | 	if err != nil { | ||||||
|  | 		if errors.Is(err, repository.ErrInvalidPagination) { | ||||||
|  | 			c.logger.Warnf("%s: 无效的分页参数: %v", actionType, err) | ||||||
|  | 			controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的分页参数: "+err.Error(), actionType, "无效分页参数", req) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		c.logger.Errorf("%s: 服务层查询失败: %v", actionType, err) | ||||||
|  | 		controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "批量查询通知失败: "+err.Error(), actionType, "服务层查询失败", req) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	resp := dto.NewListNotificationResponse(data, total, req.Page, req.PageSize) | ||||||
|  | 	c.logger.Infof("%s: 成功, 获取到 %d 条记录, 总计 %d 条", actionType, len(data), total) | ||||||
|  | 	controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "批量查询通知成功", resp, actionType, "批量查询通知成功", req) | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										35
									
								
								internal/app/dto/notification_converter.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								internal/app/dto/notification_converter.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | package dto | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"git.huangwc.com/pig/pig-farm-controller/internal/infra/models" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // NewListNotificationResponse 从模型数据创建通知列表响应 DTO | ||||||
|  | func NewListNotificationResponse(data []models.Notification, total int64, page, pageSize int) *ListNotificationResponse { | ||||||
|  | 	dtos := make([]NotificationDTO, len(data)) | ||||||
|  | 	for i, item := range data { | ||||||
|  | 		dtos[i] = NotificationDTO{ | ||||||
|  | 			ID:             item.ID, | ||||||
|  | 			CreatedAt:      item.CreatedAt, | ||||||
|  | 			UpdatedAt:      item.UpdatedAt, | ||||||
|  | 			NotifierType:   item.NotifierType, | ||||||
|  | 			UserID:         item.UserID, | ||||||
|  | 			Title:          item.Title, | ||||||
|  | 			Message:        item.Message, | ||||||
|  | 			Level:          item.Level, | ||||||
|  | 			AlarmTimestamp: item.AlarmTimestamp, | ||||||
|  | 			ToAddress:      item.ToAddress, | ||||||
|  | 			Status:         item.Status, | ||||||
|  | 			ErrorMessage:   item.ErrorMessage, | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &ListNotificationResponse{ | ||||||
|  | 		List: dtos, | ||||||
|  | 		Pagination: PaginationDTO{ | ||||||
|  | 			Total:    total, | ||||||
|  | 			Page:     page, | ||||||
|  | 			PageSize: pageSize, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -1,9 +1,50 @@ | |||||||
| package dto | package dto | ||||||
|  |  | ||||||
| import "git.huangwc.com/pig/pig-farm-controller/internal/infra/notify" | 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" | ||||||
|  | ) | ||||||
|  |  | ||||||
| // SendTestNotificationRequest 定义了发送测试通知请求的 JSON 结构 | // SendTestNotificationRequest 定义了发送测试通知请求的 JSON 结构 | ||||||
| type SendTestNotificationRequest struct { | type SendTestNotificationRequest struct { | ||||||
| 	// Type 指定要测试的通知渠道 | 	// Type 指定要测试的通知渠道 | ||||||
| 	Type notify.NotifierType `json:"type" binding:"required"` | 	Type notify.NotifierType `json:"type" binding:"required"` | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // ListNotificationRequest 定义了获取通知列表的请求参数 | ||||||
|  | type ListNotificationRequest struct { | ||||||
|  | 	Page         int                        `form:"page,default=1"` | ||||||
|  | 	PageSize     int                        `form:"pageSize,default=10"` | ||||||
|  | 	UserID       *uint                      `form:"user_id"` | ||||||
|  | 	NotifierType *notify.NotifierType       `form:"notifier_type"` | ||||||
|  | 	Status       *models.NotificationStatus `form:"status"` | ||||||
|  | 	Level        *zapcore.Level             `form:"level"` | ||||||
|  | 	StartTime    *time.Time                 `form:"start_time"` | ||||||
|  | 	EndTime      *time.Time                 `form:"end_time"` | ||||||
|  | 	OrderBy      string                     `form:"order_by"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotificationDTO 是用于API响应的通知结构 | ||||||
|  | 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"` | ||||||
|  | 	UserID         uint                      `json:"user_id"` | ||||||
|  | 	Title          string                    `json:"title"` | ||||||
|  | 	Message        string                    `json:"message"` | ||||||
|  | 	Level          zapcore.Level             `json:"level"` | ||||||
|  | 	AlarmTimestamp time.Time                 `json:"alarm_timestamp"` | ||||||
|  | 	ToAddress      string                    `json:"to_address"` | ||||||
|  | 	Status         models.NotificationStatus `json:"status"` | ||||||
|  | 	ErrorMessage   string                    `json:"error_message"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ListNotificationResponse 是获取通知列表的响应结构 | ||||||
|  | type ListNotificationResponse struct { | ||||||
|  | 	List       []NotificationDTO `json:"list"` | ||||||
|  | 	Pagination PaginationDTO     `json:"pagination"` | ||||||
|  | } | ||||||
|   | |||||||
| @@ -24,6 +24,7 @@ type MonitorService interface { | |||||||
| 	ListPigSickLogs(opts repository.PigSickLogListOptions, page, pageSize int) ([]models.PigSickLog, int64, error) | 	ListPigSickLogs(opts repository.PigSickLogListOptions, page, pageSize int) ([]models.PigSickLog, int64, error) | ||||||
| 	ListPigPurchases(opts repository.PigPurchaseListOptions, page, pageSize int) ([]models.PigPurchase, int64, error) | 	ListPigPurchases(opts repository.PigPurchaseListOptions, page, pageSize int) ([]models.PigPurchase, int64, error) | ||||||
| 	ListPigSales(opts repository.PigSaleListOptions, page, pageSize int) ([]models.PigSale, int64, error) | 	ListPigSales(opts repository.PigSaleListOptions, page, pageSize int) ([]models.PigSale, int64, error) | ||||||
|  | 	ListNotifications(opts repository.NotificationListOptions, page, pageSize int) ([]models.Notification, int64, error) | ||||||
| } | } | ||||||
|  |  | ||||||
| // monitorService 是 MonitorService 接口的具体实现 | // monitorService 是 MonitorService 接口的具体实现 | ||||||
| @@ -40,6 +41,7 @@ type monitorService struct { | |||||||
| 	pigTransferLogRepo    repository.PigTransferLogRepository | 	pigTransferLogRepo    repository.PigTransferLogRepository | ||||||
| 	pigSickLogRepo        repository.PigSickLogRepository | 	pigSickLogRepo        repository.PigSickLogRepository | ||||||
| 	pigTradeRepo          repository.PigTradeRepository | 	pigTradeRepo          repository.PigTradeRepository | ||||||
|  | 	notificationRepo      repository.NotificationRepository | ||||||
| } | } | ||||||
|  |  | ||||||
| // NewMonitorService 创建一个新的 MonitorService 实例 | // NewMonitorService 创建一个新的 MonitorService 实例 | ||||||
| @@ -56,6 +58,7 @@ func NewMonitorService( | |||||||
| 	pigTransferLogRepo repository.PigTransferLogRepository, | 	pigTransferLogRepo repository.PigTransferLogRepository, | ||||||
| 	pigSickLogRepo repository.PigSickLogRepository, | 	pigSickLogRepo repository.PigSickLogRepository, | ||||||
| 	pigTradeRepo repository.PigTradeRepository, | 	pigTradeRepo repository.PigTradeRepository, | ||||||
|  | 	notificationRepo repository.NotificationRepository, | ||||||
| ) MonitorService { | ) MonitorService { | ||||||
| 	return &monitorService{ | 	return &monitorService{ | ||||||
| 		sensorDataRepo:        sensorDataRepo, | 		sensorDataRepo:        sensorDataRepo, | ||||||
| @@ -70,6 +73,7 @@ func NewMonitorService( | |||||||
| 		pigTransferLogRepo:    pigTransferLogRepo, | 		pigTransferLogRepo:    pigTransferLogRepo, | ||||||
| 		pigSickLogRepo:        pigSickLogRepo, | 		pigSickLogRepo:        pigSickLogRepo, | ||||||
| 		pigTradeRepo:          pigTradeRepo, | 		pigTradeRepo:          pigTradeRepo, | ||||||
|  | 		notificationRepo:      notificationRepo, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -157,3 +161,8 @@ func (s *monitorService) ListPigPurchases(opts repository.PigPurchaseListOptions | |||||||
| func (s *monitorService) ListPigSales(opts repository.PigSaleListOptions, page, pageSize int) ([]models.PigSale, int64, error) { | func (s *monitorService) ListPigSales(opts repository.PigSaleListOptions, page, pageSize int) ([]models.PigSale, int64, error) { | ||||||
| 	return s.pigTradeRepo.ListPigSales(opts, page, pageSize) | 	return s.pigTradeRepo.ListPigSales(opts, page, pageSize) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // ListNotifications 负责处理查询通知列表的业务逻辑 | ||||||
|  | func (s *monitorService) ListNotifications(opts repository.NotificationListOptions, page, pageSize int) ([]models.Notification, int64, error) { | ||||||
|  | 	return s.notificationRepo.List(opts, page, pageSize) | ||||||
|  | } | ||||||
|   | |||||||
| @@ -90,6 +90,7 @@ func NewApplication(configPath string) (*Application, error) { | |||||||
| 	pigSickPigLogRepo := repository.NewGormPigSickLogRepository(storage.GetDB()) | 	pigSickPigLogRepo := repository.NewGormPigSickLogRepository(storage.GetDB()) | ||||||
| 	medicationLogRepo := repository.NewGormMedicationLogRepository(storage.GetDB()) | 	medicationLogRepo := repository.NewGormMedicationLogRepository(storage.GetDB()) | ||||||
| 	rawMaterialRepo := repository.NewGormRawMaterialRepository(storage.GetDB()) | 	rawMaterialRepo := repository.NewGormRawMaterialRepository(storage.GetDB()) | ||||||
|  | 	notificationRepo := repository.NewGormNotificationRepository(storage.GetDB()) | ||||||
|  |  | ||||||
| 	// 初始化事务管理器 | 	// 初始化事务管理器 | ||||||
| 	unitOfWork := repository.NewGormUnitOfWork(storage.GetDB(), logger) | 	unitOfWork := repository.NewGormUnitOfWork(storage.GetDB(), logger) | ||||||
| @@ -117,13 +118,14 @@ func NewApplication(configPath string) (*Application, error) { | |||||||
| 		pigTransferLogRepo, | 		pigTransferLogRepo, | ||||||
| 		pigSickPigLogRepo, | 		pigSickPigLogRepo, | ||||||
| 		pigTradeRepo, | 		pigTradeRepo, | ||||||
|  | 		notificationRepo, | ||||||
| 	) | 	) | ||||||
|  |  | ||||||
| 	// 初始化审计服务 | 	// 初始化审计服务 | ||||||
| 	auditService := audit.NewService(userActionLogRepo, logger) | 	auditService := audit.NewService(userActionLogRepo, logger) | ||||||
|  |  | ||||||
| 	// 初始化通知服务 | 	// 初始化通知服务 | ||||||
| 	notifyService, err := initNotifyService(cfg.Notify, logger, userRepo) | 	notifyService, err := initNotifyService(cfg.Notify, logger, userRepo, notificationRepo) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, fmt.Errorf("初始化通知服务失败: %w", err) | 		return nil, fmt.Errorf("初始化通知服务失败: %w", err) | ||||||
| 	} | 	} | ||||||
| @@ -220,6 +222,7 @@ func initNotifyService( | |||||||
| 	cfg config.NotifyConfig, | 	cfg config.NotifyConfig, | ||||||
| 	log *logs.Logger, | 	log *logs.Logger, | ||||||
| 	userRepo repository.UserRepository, | 	userRepo repository.UserRepository, | ||||||
|  | 	notificationRepo repository.NotificationRepository, | ||||||
| ) (domain_notify.Service, error) { | ) (domain_notify.Service, error) { | ||||||
| 	var availableNotifiers []notify.Notifier | 	var availableNotifiers []notify.Notifier | ||||||
|  |  | ||||||
| @@ -278,13 +281,14 @@ func initNotifyService( | |||||||
| 		log.Warnf("配置的首选渠道 '%s' 未启用或未指定,已自动降级使用 '%s' 作为首选渠道。", cfg.Primary, primaryNotifier.Type()) | 		log.Warnf("配置的首选渠道 '%s' 未启用或未指定,已自动降级使用 '%s' 作为首选渠道。", cfg.Primary, primaryNotifier.Type()) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// 4. 使用创建的 Notifier 列表来组装领域服务 | 	// 4. 使用创建的 Notifier 列表和 notificationRepo 来组装领域服务 | ||||||
| 	notifyService, err := domain_notify.NewFailoverService( | 	notifyService, err := domain_notify.NewFailoverService( | ||||||
| 		log, | 		log, | ||||||
| 		userRepo, | 		userRepo, | ||||||
| 		availableNotifiers, | 		availableNotifiers, | ||||||
| 		primaryNotifier.Type(), | 		primaryNotifier.Type(), | ||||||
| 		cfg.FailureThreshold, | 		cfg.FailureThreshold, | ||||||
|  | 		notificationRepo, | ||||||
| 	) | 	) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, fmt.Errorf("创建故障转移通知服务失败: %w", err) | 		return nil, fmt.Errorf("创建故障转移通知服务失败: %w", err) | ||||||
|   | |||||||
| @@ -33,6 +33,7 @@ type failoverService struct { | |||||||
| 	primaryNotifier  notify.Notifier | 	primaryNotifier  notify.Notifier | ||||||
| 	failureThreshold int | 	failureThreshold int | ||||||
| 	failureCounters  *sync.Map // 使用 sync.Map 来安全地并发读写失败计数, key: userID (uint), value: counter (int) | 	failureCounters  *sync.Map // 使用 sync.Map 来安全地并发读写失败计数, key: userID (uint), value: counter (int) | ||||||
|  | 	notificationRepo repository.NotificationRepository | ||||||
| } | } | ||||||
|  |  | ||||||
| // NewFailoverService 创建一个新的故障转移通知服务 | // NewFailoverService 创建一个新的故障转移通知服务 | ||||||
| @@ -42,6 +43,7 @@ func NewFailoverService( | |||||||
| 	notifiers []notify.Notifier, | 	notifiers []notify.Notifier, | ||||||
| 	primaryNotifierType notify.NotifierType, | 	primaryNotifierType notify.NotifierType, | ||||||
| 	failureThreshold int, | 	failureThreshold int, | ||||||
|  | 	notificationRepo repository.NotificationRepository, | ||||||
| ) (Service, error) { | ) (Service, error) { | ||||||
| 	notifierMap := make(map[notify.NotifierType]notify.Notifier) | 	notifierMap := make(map[notify.NotifierType]notify.Notifier) | ||||||
| 	for _, n := range notifiers { | 	for _, n := range notifiers { | ||||||
| @@ -60,6 +62,7 @@ func NewFailoverService( | |||||||
| 		primaryNotifier:  primaryNotifier, | 		primaryNotifier:  primaryNotifier, | ||||||
| 		failureThreshold: failureThreshold, | 		failureThreshold: failureThreshold, | ||||||
| 		failureCounters:  &sync.Map{}, | 		failureCounters:  &sync.Map{}, | ||||||
|  | 		notificationRepo: notificationRepo, | ||||||
| 	}, nil | 	}, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -128,11 +131,15 @@ func (s *failoverService) sendAlarmToUser(userID uint, content notify.AlarmConte | |||||||
| 		primaryType := s.primaryNotifier.Type() | 		primaryType := s.primaryNotifier.Type() | ||||||
| 		addr := getAddressForNotifier(primaryType, user.Contact) | 		addr := getAddressForNotifier(primaryType, user.Contact) | ||||||
| 		if addr == "" { | 		if addr == "" { | ||||||
|  | 			// 记录跳过通知 | ||||||
|  | 			s.recordNotificationAttempt(userID, primaryType, content, "", models.NotificationStatusSkipped, fmt.Errorf("用户未配置首选通知方式 '%s' 的地址", primaryType)) | ||||||
| 			return fmt.Errorf("用户未配置首选通知方式 '%s' 的地址", primaryType) | 			return fmt.Errorf("用户未配置首选通知方式 '%s' 的地址", primaryType) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		err = s.primaryNotifier.Send(content, addr) | 		err = s.primaryNotifier.Send(content, addr) | ||||||
| 		if err == nil { | 		if err == nil { | ||||||
|  | 			// 记录成功通知 | ||||||
|  | 			s.recordNotificationAttempt(userID, primaryType, content, addr, models.NotificationStatusSuccess, nil) | ||||||
| 			if failureCount > 0 { | 			if failureCount > 0 { | ||||||
| 				s.log.Infow("首选渠道发送恢复正常", "userID", userID, "notifierType", primaryType) | 				s.log.Infow("首选渠道发送恢复正常", "userID", userID, "notifierType", primaryType) | ||||||
| 				s.failureCounters.Store(userID, 0) | 				s.failureCounters.Store(userID, 0) | ||||||
| @@ -140,6 +147,8 @@ func (s *failoverService) sendAlarmToUser(userID uint, content notify.AlarmConte | |||||||
| 			return nil | 			return nil | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		// 记录失败通知 | ||||||
|  | 		s.recordNotificationAttempt(userID, primaryType, content, addr, models.NotificationStatusFailed, err) | ||||||
| 		newFailureCount := failureCount + 1 | 		newFailureCount := failureCount + 1 | ||||||
| 		s.failureCounters.Store(userID, newFailureCount) | 		s.failureCounters.Store(userID, newFailureCount) | ||||||
| 		s.log.Warnw("首选渠道发送失败", "userID", userID, "notifierType", primaryType, "error", err, "failureCount", newFailureCount) | 		s.log.Warnw("首选渠道发送失败", "userID", userID, "notifierType", primaryType, "error", err, "failureCount", newFailureCount) | ||||||
| @@ -152,13 +161,19 @@ func (s *failoverService) sendAlarmToUser(userID uint, content notify.AlarmConte | |||||||
| 		for _, notifier := range s.notifiers { | 		for _, notifier := range s.notifiers { | ||||||
| 			addr := getAddressForNotifier(notifier.Type(), user.Contact) | 			addr := getAddressForNotifier(notifier.Type(), user.Contact) | ||||||
| 			if addr == "" { | 			if addr == "" { | ||||||
|  | 				// 记录跳过通知 | ||||||
|  | 				s.recordNotificationAttempt(userID, notifier.Type(), content, "", models.NotificationStatusSkipped, fmt.Errorf("用户未配置通知方式 '%s' 的地址", notifier.Type())) | ||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
| 			if err := notifier.Send(content, addr); err == nil { | 			if err := notifier.Send(content, addr); err == nil { | ||||||
|  | 				// 记录成功通知 | ||||||
|  | 				s.recordNotificationAttempt(userID, notifier.Type(), content, addr, models.NotificationStatusSuccess, nil) | ||||||
| 				s.log.Infow("广播通知成功", "userID", userID, "notifierType", notifier.Type()) | 				s.log.Infow("广播通知成功", "userID", userID, "notifierType", notifier.Type()) | ||||||
| 				s.failureCounters.Store(userID, 0) | 				s.failureCounters.Store(userID, 0) | ||||||
| 				return nil | 				return nil | ||||||
| 			} | 			} | ||||||
|  | 			// 记录失败通知 | ||||||
|  | 			s.recordNotificationAttempt(userID, notifier.Type(), content, addr, models.NotificationStatusFailed, err) | ||||||
| 			lastErr = err | 			lastErr = err | ||||||
| 			s.log.Warnw("广播通知:渠道发送失败", "userID", userID, "notifierType", notifier.Type(), "error", err) | 			s.log.Warnw("广播通知:渠道发送失败", "userID", userID, "notifierType", notifier.Type(), "error", err) | ||||||
| 		} | 		} | ||||||
| @@ -185,6 +200,13 @@ func (s *failoverService) SendTestMessage(userID uint, notifierType notify.Notif | |||||||
| 	addr := getAddressForNotifier(notifierType, user.Contact) | 	addr := getAddressForNotifier(notifierType, user.Contact) | ||||||
| 	if addr == "" { | 	if addr == "" { | ||||||
| 		s.log.Warnw("发送测试消息失败:缺少地址", "userID", userID, "notifierType", notifierType) | 		s.log.Warnw("发送测试消息失败:缺少地址", "userID", userID, "notifierType", notifierType) | ||||||
|  | 		// 记录跳过通知 | ||||||
|  | 		s.recordNotificationAttempt(userID, notifierType, notify.AlarmContent{ | ||||||
|  | 			Title:     "通知服务测试", | ||||||
|  | 			Message:   fmt.Sprintf("这是一条来自【%s】渠道的测试消息。如果您收到此消息,说明您的配置正确。", notifierType), | ||||||
|  | 			Level:     zap.InfoLevel, | ||||||
|  | 			Timestamp: time.Now(), | ||||||
|  | 		}, "", models.NotificationStatusFailed, fmt.Errorf("用户未配置通知方式 '%s' 的地址", notifierType)) | ||||||
| 		return fmt.Errorf("用户未配置通知方式 '%s' 的地址", notifierType) | 		return fmt.Errorf("用户未配置通知方式 '%s' 的地址", notifierType) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -199,10 +221,14 @@ func (s *failoverService) SendTestMessage(userID uint, notifierType notify.Notif | |||||||
| 	err = notifier.Send(testContent, addr) | 	err = notifier.Send(testContent, addr) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		s.log.Errorw("发送测试消息失败", "userID", userID, "notifierType", notifierType, "error", err) | 		s.log.Errorw("发送测试消息失败", "userID", userID, "notifierType", notifierType, "error", err) | ||||||
|  | 		// 记录失败通知 | ||||||
|  | 		s.recordNotificationAttempt(userID, notifierType, testContent, addr, models.NotificationStatusFailed, err) | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	s.log.Infow("发送测试消息成功", "userID", userID, "notifierType", notifierType) | 	s.log.Infow("发送测试消息成功", "userID", userID, "notifierType", notifierType) | ||||||
|  | 	// 记录成功通知 | ||||||
|  | 	s.recordNotificationAttempt(userID, notifierType, testContent, addr, models.NotificationStatusSuccess, nil) | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -221,3 +247,46 @@ func getAddressForNotifier(notifierType notify.NotifierType, contact models.Cont | |||||||
| 		return "" | 		return "" | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // recordNotificationAttempt 记录一次通知发送尝试的结果 | ||||||
|  | // userID: 接收通知的用户ID | ||||||
|  | // notifierType: 使用的通知器类型 | ||||||
|  | // content: 通知内容 | ||||||
|  | // toAddress: 实际发送到的地址 | ||||||
|  | // status: 发送尝试的状态 (成功、失败、跳过) | ||||||
|  | // err: 如果发送失败,记录的错误信息 | ||||||
|  | func (s *failoverService) recordNotificationAttempt( | ||||||
|  | 	userID uint, | ||||||
|  | 	notifierType notify.NotifierType, | ||||||
|  | 	content notify.AlarmContent, | ||||||
|  | 	toAddress string, | ||||||
|  | 	status models.NotificationStatus, | ||||||
|  | 	err error, | ||||||
|  | ) { | ||||||
|  | 	errorMessage := "" | ||||||
|  | 	if err != nil { | ||||||
|  | 		errorMessage = err.Error() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	notification := &models.Notification{ | ||||||
|  | 		NotifierType:   notifierType, | ||||||
|  | 		UserID:         userID, | ||||||
|  | 		Title:          content.Title, | ||||||
|  | 		Message:        content.Message, | ||||||
|  | 		Level:          content.Level, | ||||||
|  | 		AlarmTimestamp: content.Timestamp, | ||||||
|  | 		ToAddress:      toAddress, | ||||||
|  | 		Status:         status, | ||||||
|  | 		ErrorMessage:   errorMessage, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if saveErr := s.notificationRepo.Create(notification); saveErr != nil { | ||||||
|  | 		s.log.Errorw("无法保存通知发送记录到数据库", | ||||||
|  | 			"userID", userID, | ||||||
|  | 			"notifierType", notifierType, | ||||||
|  | 			"status", status, | ||||||
|  | 			"originalError", errorMessage, | ||||||
|  | 			"saveError", saveErr, | ||||||
|  | 		) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -171,6 +171,7 @@ func (ps *PostgresStorage) creatingHyperTable() error { | |||||||
| 		{models.PigSickLog{}, "happened_at"}, | 		{models.PigSickLog{}, "happened_at"}, | ||||||
| 		{models.PigPurchase{}, "purchase_date"}, | 		{models.PigPurchase{}, "purchase_date"}, | ||||||
| 		{models.PigSale{}, "sale_date"}, | 		{models.PigSale{}, "sale_date"}, | ||||||
|  | 		{models.Notification{}, "alarm_timestamp"}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for _, table := range tablesToConvert { | 	for _, table := range tablesToConvert { | ||||||
| @@ -211,6 +212,7 @@ func (ps *PostgresStorage) applyCompressionPolicies() error { | |||||||
| 		{models.PigSickLog{}, "pig_batch_id"}, | 		{models.PigSickLog{}, "pig_batch_id"}, | ||||||
| 		{models.PigPurchase{}, "pig_batch_id"}, | 		{models.PigPurchase{}, "pig_batch_id"}, | ||||||
| 		{models.PigSale{}, "pig_batch_id"}, | 		{models.PigSale{}, "pig_batch_id"}, | ||||||
|  | 		{models.Notification{}, "user_id"}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for _, policy := range policies { | 	for _, policy := range policies { | ||||||
|   | |||||||
| @@ -59,6 +59,9 @@ func GetAllModels() []interface{} { | |||||||
| 		// Medication Models | 		// Medication Models | ||||||
| 		&Medication{}, | 		&Medication{}, | ||||||
| 		&MedicationLog{}, | 		&MedicationLog{}, | ||||||
|  |  | ||||||
|  | 		// Notification Models | ||||||
|  | 		&Notification{}, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										47
									
								
								internal/infra/models/notify.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								internal/infra/models/notify.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | |||||||
|  | package models | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"git.huangwc.com/pig/pig-farm-controller/internal/infra/notify" | ||||||
|  | 	"go.uber.org/zap/zapcore" | ||||||
|  | 	"gorm.io/gorm" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // NotificationStatus 定义了通知发送尝试的状态枚举。 | ||||||
|  | type NotificationStatus string | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	NotificationStatusSuccess NotificationStatus = "发送成功" // 通知已成功发送 | ||||||
|  | 	NotificationStatusFailed  NotificationStatus = "发送失败" // 通知发送失败 | ||||||
|  | 	NotificationStatusSkipped NotificationStatus = "已跳过"  // 通知因某些原因被跳过(例如:用户未配置联系方式) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Notification 表示已发送或尝试发送的通知记录。 | ||||||
|  | type Notification struct { | ||||||
|  | 	gorm.Model | ||||||
|  |  | ||||||
|  | 	// NotifierType 通知器类型 (例如:"邮件", "企业微信", "飞书", "日志") | ||||||
|  | 	NotifierType notify.NotifierType `gorm:"type:varchar(20);not null;index" json:"notifier_type"` | ||||||
|  | 	// UserID 接收通知的用户ID,用于追溯通知记录到特定用户 | ||||||
|  | 	UserID uint `gorm:"index" json:"user_id"` // 增加 UserID 字段,并添加索引 | ||||||
|  | 	// Title 通知标题 | ||||||
|  | 	Title string `gorm:"type:varchar(255);not null" json:"title"` | ||||||
|  | 	// Message 通知内容 | ||||||
|  | 	Message string `gorm:"type:text;not null" json:"message"` | ||||||
|  | 	// Level 通知级别 (例如:INFO, WARN, ERROR) | ||||||
|  | 	Level zapcore.Level `gorm:"type:varchar(10);not null" json:"level"` | ||||||
|  | 	// AlarmTimestamp 通知内容生成时的时间戳,与 ID 构成复合主键 | ||||||
|  | 	AlarmTimestamp time.Time `gorm:"primaryKey;not null" json:"alarm_timestamp"` | ||||||
|  | 	// ToAddress 接收地址 (例如:邮箱地址, 企业微信ID, 日志标识符) | ||||||
|  | 	ToAddress string `gorm:"type:varchar(255);not null" json:"to_address"` | ||||||
|  | 	// Status 通知发送尝试的状态 (例如:"待发送", "发送成功", "发送失败", "已跳过") | ||||||
|  | 	Status NotificationStatus `gorm:"type:varchar(20);not null;default:'待发送'" json:"status"` | ||||||
|  | 	// ErrorMessage 如果通知发送失败,此字段存储错误信息 | ||||||
|  | 	ErrorMessage string `gorm:"type:text" json:"error_message"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TableName 指定 Notification 模型的表名。 | ||||||
|  | func (Notification) TableName() string { | ||||||
|  | 	return "notifications" | ||||||
|  | } | ||||||
							
								
								
									
										111
									
								
								internal/infra/repository/notification_repository.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								internal/infra/repository/notification_repository.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | |||||||
|  | package repository | ||||||
|  |  | ||||||
|  | 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" | ||||||
|  | 	"gorm.io/gorm" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // NotificationListOptions 定义了查询通知列表时的可选参数 | ||||||
|  | type NotificationListOptions struct { | ||||||
|  | 	UserID       *uint                      // 按用户ID过滤 | ||||||
|  | 	NotifierType *notify.NotifierType       // 按通知器类型过滤 | ||||||
|  | 	Status       *models.NotificationStatus // 按通知状态过滤 (例如:"success", "failed") | ||||||
|  | 	Level        *zapcore.Level             // 按通知等级过滤 (例如:"info", "warning", "error") | ||||||
|  | 	StartTime    *time.Time                 // 通知内容生成时间范围 - 开始时间 (对应 AlarmTimestamp) | ||||||
|  | 	EndTime      *time.Time                 // 通知内容生成时间范围 - 结束时间 (对应 AlarmTimestamp) | ||||||
|  | 	OrderBy      string                     // 排序字段,例如 "alarm_timestamp DESC" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NotificationRepository 定义了与通知记录相关的数据库操作接口。 | ||||||
|  | type NotificationRepository interface { | ||||||
|  | 	// Create 将一条新的通知记录插入数据库。 | ||||||
|  | 	Create(notification *models.Notification) error | ||||||
|  | 	// CreateInTx 在给定的事务中插入一条新的通知记录。 | ||||||
|  | 	CreateInTx(tx *gorm.DB, notification *models.Notification) error | ||||||
|  | 	// BatchCreate 批量插入多条通知记录。 | ||||||
|  | 	BatchCreate(notifications []*models.Notification) error | ||||||
|  | 	// List 支持分页和过滤的通知列表查询。 | ||||||
|  | 	// 返回通知列表、总记录数和错误。 | ||||||
|  | 	List(opts NotificationListOptions, page, pageSize int) ([]models.Notification, int64, error) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // gormNotificationRepository 是 NotificationRepository 的 GORM 实现。 | ||||||
|  | type gormNotificationRepository struct { | ||||||
|  | 	db *gorm.DB | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewGormNotificationRepository 创建一个新的 NotificationRepository GORM 实现实例。 | ||||||
|  | func NewGormNotificationRepository(db *gorm.DB) NotificationRepository { | ||||||
|  | 	return &gormNotificationRepository{db: db} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Create 将一条新的通知记录插入数据库。 | ||||||
|  | func (r *gormNotificationRepository) Create(notification *models.Notification) error { | ||||||
|  | 	return r.db.Create(notification).Error | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CreateInTx 在给定的事务中插入一条新的通知记录。 | ||||||
|  | func (r *gormNotificationRepository) CreateInTx(tx *gorm.DB, notification *models.Notification) error { | ||||||
|  | 	return tx.Create(notification).Error | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // BatchCreate 批量插入多条通知记录。 | ||||||
|  | func (r *gormNotificationRepository) BatchCreate(notifications []*models.Notification) error { | ||||||
|  | 	// GORM 的 Create 方法在传入切片时会自动进行批量插入 | ||||||
|  | 	return r.db.Create(¬ifications).Error | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // List 实现了分页和过滤查询通知记录的功能 | ||||||
|  | func (r *gormNotificationRepository) List(opts NotificationListOptions, page, pageSize int) ([]models.Notification, int64, error) { | ||||||
|  | 	// --- 校验分页参数 --- | ||||||
|  | 	if page <= 0 || pageSize <= 0 { | ||||||
|  | 		return nil, 0, ErrInvalidPagination // 复用已定义的错误 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var results []models.Notification | ||||||
|  | 	var total int64 | ||||||
|  |  | ||||||
|  | 	query := r.db.Model(&models.Notification{}) | ||||||
|  |  | ||||||
|  | 	// --- 应用过滤条件 --- | ||||||
|  | 	if opts.UserID != nil { | ||||||
|  | 		query = query.Where("user_id = ?", *opts.UserID) | ||||||
|  | 	} | ||||||
|  | 	if opts.NotifierType != nil { | ||||||
|  | 		query = query.Where("notifier_type = ?", *opts.NotifierType) | ||||||
|  | 	} | ||||||
|  | 	if opts.Status != nil { | ||||||
|  | 		query = query.Where("status = ?", *opts.Status) | ||||||
|  | 	} | ||||||
|  | 	if opts.Level != nil { | ||||||
|  | 		query = query.Where("level = ?", opts.Level.String()) | ||||||
|  | 	} | ||||||
|  | 	if opts.StartTime != nil { | ||||||
|  | 		query = query.Where("alarm_timestamp >= ?", *opts.StartTime) | ||||||
|  | 	} | ||||||
|  | 	if opts.EndTime != nil { | ||||||
|  | 		query = query.Where("alarm_timestamp <= ?", *opts.EndTime) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// --- 计算总数 --- | ||||||
|  | 	if err := query.Count(&total).Error; err != nil { | ||||||
|  | 		return nil, 0, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// --- 应用排序条件 --- | ||||||
|  | 	orderBy := "alarm_timestamp 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 | ||||||
|  | } | ||||||
							
								
								
									
										6
									
								
								internal/infra/repository/repository.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								internal/infra/repository/repository.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | package repository | ||||||
|  |  | ||||||
|  | import "errors" | ||||||
|  |  | ||||||
|  | // ErrInvalidPagination 表示分页参数无效 | ||||||
|  | var ErrInvalidPagination = errors.New("无效的分页参数:page和pageSize必须为大于0") | ||||||
| @@ -1,16 +1,12 @@ | |||||||
| package repository | package repository | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"errors" |  | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"git.huangwc.com/pig/pig-farm-controller/internal/infra/models" | 	"git.huangwc.com/pig/pig-farm-controller/internal/infra/models" | ||||||
| 	"gorm.io/gorm" | 	"gorm.io/gorm" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // ErrInvalidPagination 表示分页参数无效 |  | ||||||
| var ErrInvalidPagination = errors.New("无效的分页参数:page和pageSize必须为大于0") |  | ||||||
|  |  | ||||||
| // SensorDataListOptions 定义了查询传感器数据列表时的可选参数 | // SensorDataListOptions 定义了查询传感器数据列表时的可选参数 | ||||||
| type SensorDataListOptions struct { | type SensorDataListOptions struct { | ||||||
| 	DeviceID   *uint | 	DeviceID   *uint | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user