删除原有食物逻辑和模型

新增原料和营养价值表和原料库存日志和营养表定义
This commit is contained in:
2025-11-18 22:22:31 +08:00
parent 200a358933
commit e1399be538
15 changed files with 289 additions and 1898 deletions

View File

@@ -0,0 +1,47 @@
# 需求
饲料配方管理及自动生成配方
## issue
http://git.huangwc.com/pig/pig-farm-controller/issues/66
# 开发计划
1. 原料营养价值管理
- 增删改查
- 内置60+条常用原料玉米、豆粕43、豆粕46、发酵豆粕、麸皮、次粉、DDGS、乳清粉、鱼粉、膨化大豆、各种氨基酸、预混料、油脂等
- 每种原料固定营养值消化能、粗蛋白、赖氨酸、钙、磷等15项左右
2. 饲料库存管理(代替批次)
- 字段:饲料名、当前原料种类、当前剩余量(吨)、上次入料日期、保质期剩余天数(手动填)、是否发酵料(勾选)
- 发酵料塔额外字段:
- 发酵状态(未发酵 / 正在发酵 / 已发酵可用)
- 发酵开始日期
- 发酵几天默认37天
- 水分增加比例(默认+1020%
- 营养折损系数(可调,粗蛋白-5%、能量-3%之类)
3. 猪只阶段营养需求管理
- 预设10个常用阶段教槽、仔猪、小猪、中猪、大猪、后备、怀孕前中后、哺乳
- 每个阶段维护营养需求上下限消化能、粗蛋白、赖氨酸、钙、有效磷等12项
4. 配方管理
- 按阶段建配方
- 支持增删改查 + 复制上个配方快速新建
- 配方明细:原料 + 配比(%
5. 自动生成配方(核心功能)
- 选择阶段 → 点击“自动计算最低成本配方”
- 自动读取当前所有料塔的:
- 剩余量(不够的原料自动降配比)
- 保质期剩余天数越快过期的优先用权重×1.5
- 发酵料塔如果状态是“已发酵可用”则按发酵后营养值参与计算
- 输出:总成本、营养达标情况、发酵料占比、即将过期原料使用提示
6. 配方下发与记录
- 一键下发到喂料站/料线(生成下料曲线)
- 自动记录今天用了哪个配方
7. 简单查看功能
- 两个配方对比页面(营养+成本对比)

View File

@@ -56,13 +56,13 @@ const docTemplate = `{
}, },
{ {
"enum": [ "enum": [
"Debug", "debug",
"Info", "info",
"Warn", "warn",
"Error", "error",
"DPanic", "dpanic",
"Panic", "panic",
"Fatal" "fatal"
], ],
"type": "string", "type": "string",
"x-enum-varnames": [ "x-enum-varnames": [
@@ -172,13 +172,13 @@ const docTemplate = `{
}, },
{ {
"enum": [ "enum": [
"Debug", "debug",
"Info", "info",
"Warn", "warn",
"Error", "error",
"DPanic", "dpanic",
"Panic", "panic",
"Fatal" "fatal"
], ],
"type": "string", "type": "string",
"x-enum-varnames": [ "x-enum-varnames": [
@@ -453,13 +453,13 @@ const docTemplate = `{
}, },
{ {
"enum": [ "enum": [
"Debug", "debug",
"Info", "info",
"Warn", "warn",
"Error", "error",
"DPanic", "dpanic",
"Panic", "panic",
"Fatal" "fatal"
], ],
"type": "string", "type": "string",
"x-enum-varnames": [ "x-enum-varnames": [
@@ -740,13 +740,13 @@ const docTemplate = `{
"parameters": [ "parameters": [
{ {
"enum": [ "enum": [
"Debug", "debug",
"Info", "info",
"Warn", "warn",
"Error", "error",
"DPanic", "dpanic",
"Panic", "panic",
"Fatal" "fatal"
], ],
"type": "string", "type": "string",
"x-enum-varnames": [ "x-enum-varnames": [
@@ -1719,85 +1719,6 @@ const docTemplate = `{
} }
} }
}, },
"/api/v1/monitor/feed-usage-records": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "根据提供的过滤条件,分页获取饲料使用记录",
"produces": [
"application/json"
],
"tags": [
"数据监控"
],
"summary": "获取饲料使用记录列表",
"parameters": [
{
"type": "string",
"name": "end_time",
"in": "query"
},
{
"type": "integer",
"name": "feed_formula_id",
"in": "query"
},
{
"type": "integer",
"name": "operator_id",
"in": "query"
},
{
"type": "string",
"name": "order_by",
"in": "query"
},
{
"type": "integer",
"name": "page",
"in": "query"
},
{
"type": "integer",
"name": "page_size",
"in": "query"
},
{
"type": "integer",
"name": "pen_id",
"in": "query"
},
{
"type": "string",
"name": "start_time",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/controller.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/dto.ListFeedUsageRecordResponse"
}
}
}
]
}
}
}
}
},
"/api/v1/monitor/medication-logs": { "/api/v1/monitor/medication-logs": {
"get": { "get": {
"security": [ "security": [
@@ -1905,6 +1826,7 @@ const docTemplate = `{
}, },
{ {
"enum": [ "enum": [
7,
-1, -1,
0, 0,
1, 1,
@@ -1914,12 +1836,12 @@ const docTemplate = `{
5, 5,
-1, -1,
5, 5,
6, 6
7
], ],
"type": "integer", "type": "integer",
"format": "int32", "format": "int32",
"x-enum-varnames": [ "x-enum-varnames": [
"_numLevels",
"DebugLevel", "DebugLevel",
"InfoLevel", "InfoLevel",
"WarnLevel", "WarnLevel",
@@ -1929,8 +1851,7 @@ const docTemplate = `{
"FatalLevel", "FatalLevel",
"_minLevel", "_minLevel",
"_maxLevel", "_maxLevel",
"InvalidLevel", "InvalidLevel"
"_numLevels"
], ],
"name": "level", "name": "level",
"in": "query" "in": "query"
@@ -2588,159 +2509,6 @@ const docTemplate = `{
} }
} }
}, },
"/api/v1/monitor/raw-material-purchases": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "根据提供的过滤条件,分页获取原料采购记录",
"produces": [
"application/json"
],
"tags": [
"数据监控"
],
"summary": "获取原料采购记录列表",
"parameters": [
{
"type": "string",
"name": "end_time",
"in": "query"
},
{
"type": "string",
"name": "order_by",
"in": "query"
},
{
"type": "integer",
"name": "page",
"in": "query"
},
{
"type": "integer",
"name": "page_size",
"in": "query"
},
{
"type": "integer",
"name": "raw_material_id",
"in": "query"
},
{
"type": "string",
"name": "start_time",
"in": "query"
},
{
"type": "string",
"name": "supplier",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/controller.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/dto.ListRawMaterialPurchaseResponse"
}
}
}
]
}
}
}
}
},
"/api/v1/monitor/raw-material-stock-logs": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "根据提供的过滤条件,分页获取原料库存日志",
"produces": [
"application/json"
],
"tags": [
"数据监控"
],
"summary": "获取原料库存日志列表",
"parameters": [
{
"type": "string",
"name": "end_time",
"in": "query"
},
{
"type": "string",
"name": "order_by",
"in": "query"
},
{
"type": "integer",
"name": "page",
"in": "query"
},
{
"type": "integer",
"name": "page_size",
"in": "query"
},
{
"type": "integer",
"name": "raw_material_id",
"in": "query"
},
{
"type": "integer",
"name": "source_id",
"in": "query"
},
{
"type": "string",
"name": "source_type",
"in": "query"
},
{
"type": "string",
"name": "start_time",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/controller.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/dto.ListRawMaterialStockLogResponse"
}
}
}
]
}
}
}
}
},
"/api/v1/monitor/sensor-data": { "/api/v1/monitor/sensor-data": {
"get": { "get": {
"security": [ "security": [
@@ -5645,49 +5413,6 @@ const docTemplate = `{
} }
} }
}, },
"dto.FeedFormulaDTO": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
}
},
"dto.FeedUsageRecordDTO": {
"type": "object",
"properties": {
"amount": {
"type": "number"
},
"feed_formula": {
"$ref": "#/definitions/dto.FeedFormulaDTO"
},
"feed_formula_id": {
"type": "integer"
},
"id": {
"type": "integer"
},
"operator_id": {
"type": "integer"
},
"pen": {
"$ref": "#/definitions/dto.PenDTO"
},
"pen_id": {
"type": "integer"
},
"recorded_at": {
"type": "string"
},
"remarks": {
"type": "string"
}
}
},
"dto.HistoricalAlarmDTO": { "dto.HistoricalAlarmDTO": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -5782,20 +5507,6 @@ const docTemplate = `{
} }
} }
}, },
"dto.ListFeedUsageRecordResponse": {
"type": "object",
"properties": {
"list": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.FeedUsageRecordDTO"
}
},
"pagination": {
"$ref": "#/definitions/dto.PaginationDTO"
}
}
},
"dto.ListHistoricalAlarmResponse": { "dto.ListHistoricalAlarmResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -5951,34 +5662,6 @@ const docTemplate = `{
} }
} }
}, },
"dto.ListRawMaterialPurchaseResponse": {
"type": "object",
"properties": {
"list": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.RawMaterialPurchaseDTO"
}
},
"pagination": {
"$ref": "#/definitions/dto.PaginationDTO"
}
}
},
"dto.ListRawMaterialStockLogResponse": {
"type": "object",
"properties": {
"list": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.RawMaterialStockLogDTO"
}
},
"pagination": {
"$ref": "#/definitions/dto.PaginationDTO"
}
}
},
"dto.ListSensorDataResponse": { "dto.ListSensorDataResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -6216,17 +5899,6 @@ const docTemplate = `{
} }
} }
}, },
"dto.PenDTO": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
}
},
"dto.PenResponse": { "dto.PenResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -6733,75 +6405,6 @@ const docTemplate = `{
} }
} }
}, },
"dto.RawMaterialDTO": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
}
},
"dto.RawMaterialPurchaseDTO": {
"type": "object",
"properties": {
"amount": {
"type": "number"
},
"created_at": {
"type": "string"
},
"id": {
"type": "integer"
},
"purchase_date": {
"type": "string"
},
"raw_material": {
"$ref": "#/definitions/dto.RawMaterialDTO"
},
"raw_material_id": {
"type": "integer"
},
"supplier": {
"type": "string"
},
"total_price": {
"type": "number"
},
"unit_price": {
"type": "number"
}
}
},
"dto.RawMaterialStockLogDTO": {
"type": "object",
"properties": {
"change_amount": {
"type": "number"
},
"happened_at": {
"type": "string"
},
"id": {
"type": "integer"
},
"raw_material_id": {
"type": "integer"
},
"remarks": {
"type": "string"
},
"source_id": {
"type": "integer"
},
"source_type": {
"$ref": "#/definitions/models.StockLogSourceType"
}
}
},
"dto.ReclassifyPenToNewBatchRequest": { "dto.ReclassifyPenToNewBatchRequest": {
"type": "object", "type": "object",
"required": [ "required": [
@@ -8140,13 +7743,13 @@ const docTemplate = `{
"models.SeverityLevel": { "models.SeverityLevel": {
"type": "string", "type": "string",
"enum": [ "enum": [
"Debug", "debug",
"Info", "info",
"Warn", "warn",
"Error", "error",
"DPanic", "dpanic",
"Panic", "panic",
"Fatal" "fatal"
], ],
"x-enum-varnames": [ "x-enum-varnames": [
"DebugLevel", "DebugLevel",
@@ -8158,25 +7761,6 @@ const docTemplate = `{
"FatalLevel" "FatalLevel"
] ]
}, },
"models.StockLogSourceType": {
"type": "string",
"enum": [
"采购入库",
"饲喂出库",
"变质出库",
"售卖出库",
"杂用领取",
"手动盘点"
],
"x-enum-varnames": [
"StockLogSourcePurchase",
"StockLogSourceFeeding",
"StockLogSourceDeteriorate",
"StockLogSourceSale",
"StockLogSourceMiscellaneous",
"StockLogSourceManual"
]
},
"models.TaskType": { "models.TaskType": {
"type": "string", "type": "string",
"enum": [ "enum": [
@@ -8185,6 +7769,7 @@ const docTemplate = `{
"下料", "下料",
"全量采集", "全量采集",
"告警通知", "告警通知",
"通知刷新",
"设备阈值检查", "设备阈值检查",
"区域阈值检查" "区域阈值检查"
], ],
@@ -8194,6 +7779,7 @@ const docTemplate = `{
"TaskTypeAreaCollectorThresholdCheck": "区域阈值检查任务", "TaskTypeAreaCollectorThresholdCheck": "区域阈值检查任务",
"TaskTypeDeviceThresholdCheck": "设备阈值检查任务", "TaskTypeDeviceThresholdCheck": "设备阈值检查任务",
"TaskTypeFullCollection": "新增的全量采集任务", "TaskTypeFullCollection": "新增的全量采集任务",
"TaskTypeNotificationRefresh": "通知刷新任务",
"TaskTypeReleaseFeedWeight": "下料口释放指定重量任务", "TaskTypeReleaseFeedWeight": "下料口释放指定重量任务",
"TaskTypeWaiting": "等待任务" "TaskTypeWaiting": "等待任务"
}, },
@@ -8203,6 +7789,7 @@ const docTemplate = `{
"下料口释放指定重量任务", "下料口释放指定重量任务",
"新增的全量采集任务", "新增的全量采集任务",
"告警通知任务", "告警通知任务",
"通知刷新任务",
"设备阈值检查任务", "设备阈值检查任务",
"区域阈值检查任务" "区域阈值检查任务"
], ],
@@ -8212,6 +7799,7 @@ const docTemplate = `{
"TaskTypeReleaseFeedWeight", "TaskTypeReleaseFeedWeight",
"TaskTypeFullCollection", "TaskTypeFullCollection",
"TaskTypeAlarmNotification", "TaskTypeAlarmNotification",
"TaskTypeNotificationRefresh",
"TaskTypeDeviceThresholdCheck", "TaskTypeDeviceThresholdCheck",
"TaskTypeAreaCollectorThresholdCheck" "TaskTypeAreaCollectorThresholdCheck"
] ]
@@ -8249,6 +7837,7 @@ const docTemplate = `{
"type": "integer", "type": "integer",
"format": "int32", "format": "int32",
"enum": [ "enum": [
7,
-1, -1,
0, 0,
1, 1,
@@ -8258,10 +7847,10 @@ const docTemplate = `{
5, 5,
-1, -1,
5, 5,
6, 6
7
], ],
"x-enum-varnames": [ "x-enum-varnames": [
"_numLevels",
"DebugLevel", "DebugLevel",
"InfoLevel", "InfoLevel",
"WarnLevel", "WarnLevel",
@@ -8271,8 +7860,7 @@ const docTemplate = `{
"FatalLevel", "FatalLevel",
"_minLevel", "_minLevel",
"_maxLevel", "_maxLevel",
"InvalidLevel", "InvalidLevel"
"_numLevels"
] ]
} }
}, },

View File

@@ -48,13 +48,13 @@
}, },
{ {
"enum": [ "enum": [
"Debug", "debug",
"Info", "info",
"Warn", "warn",
"Error", "error",
"DPanic", "dpanic",
"Panic", "panic",
"Fatal" "fatal"
], ],
"type": "string", "type": "string",
"x-enum-varnames": [ "x-enum-varnames": [
@@ -164,13 +164,13 @@
}, },
{ {
"enum": [ "enum": [
"Debug", "debug",
"Info", "info",
"Warn", "warn",
"Error", "error",
"DPanic", "dpanic",
"Panic", "panic",
"Fatal" "fatal"
], ],
"type": "string", "type": "string",
"x-enum-varnames": [ "x-enum-varnames": [
@@ -445,13 +445,13 @@
}, },
{ {
"enum": [ "enum": [
"Debug", "debug",
"Info", "info",
"Warn", "warn",
"Error", "error",
"DPanic", "dpanic",
"Panic", "panic",
"Fatal" "fatal"
], ],
"type": "string", "type": "string",
"x-enum-varnames": [ "x-enum-varnames": [
@@ -732,13 +732,13 @@
"parameters": [ "parameters": [
{ {
"enum": [ "enum": [
"Debug", "debug",
"Info", "info",
"Warn", "warn",
"Error", "error",
"DPanic", "dpanic",
"Panic", "panic",
"Fatal" "fatal"
], ],
"type": "string", "type": "string",
"x-enum-varnames": [ "x-enum-varnames": [
@@ -1711,85 +1711,6 @@
} }
} }
}, },
"/api/v1/monitor/feed-usage-records": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "根据提供的过滤条件,分页获取饲料使用记录",
"produces": [
"application/json"
],
"tags": [
"数据监控"
],
"summary": "获取饲料使用记录列表",
"parameters": [
{
"type": "string",
"name": "end_time",
"in": "query"
},
{
"type": "integer",
"name": "feed_formula_id",
"in": "query"
},
{
"type": "integer",
"name": "operator_id",
"in": "query"
},
{
"type": "string",
"name": "order_by",
"in": "query"
},
{
"type": "integer",
"name": "page",
"in": "query"
},
{
"type": "integer",
"name": "page_size",
"in": "query"
},
{
"type": "integer",
"name": "pen_id",
"in": "query"
},
{
"type": "string",
"name": "start_time",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/controller.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/dto.ListFeedUsageRecordResponse"
}
}
}
]
}
}
}
}
},
"/api/v1/monitor/medication-logs": { "/api/v1/monitor/medication-logs": {
"get": { "get": {
"security": [ "security": [
@@ -1897,6 +1818,7 @@
}, },
{ {
"enum": [ "enum": [
7,
-1, -1,
0, 0,
1, 1,
@@ -1906,12 +1828,12 @@
5, 5,
-1, -1,
5, 5,
6, 6
7
], ],
"type": "integer", "type": "integer",
"format": "int32", "format": "int32",
"x-enum-varnames": [ "x-enum-varnames": [
"_numLevels",
"DebugLevel", "DebugLevel",
"InfoLevel", "InfoLevel",
"WarnLevel", "WarnLevel",
@@ -1921,8 +1843,7 @@
"FatalLevel", "FatalLevel",
"_minLevel", "_minLevel",
"_maxLevel", "_maxLevel",
"InvalidLevel", "InvalidLevel"
"_numLevels"
], ],
"name": "level", "name": "level",
"in": "query" "in": "query"
@@ -2580,159 +2501,6 @@
} }
} }
}, },
"/api/v1/monitor/raw-material-purchases": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "根据提供的过滤条件,分页获取原料采购记录",
"produces": [
"application/json"
],
"tags": [
"数据监控"
],
"summary": "获取原料采购记录列表",
"parameters": [
{
"type": "string",
"name": "end_time",
"in": "query"
},
{
"type": "string",
"name": "order_by",
"in": "query"
},
{
"type": "integer",
"name": "page",
"in": "query"
},
{
"type": "integer",
"name": "page_size",
"in": "query"
},
{
"type": "integer",
"name": "raw_material_id",
"in": "query"
},
{
"type": "string",
"name": "start_time",
"in": "query"
},
{
"type": "string",
"name": "supplier",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/controller.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/dto.ListRawMaterialPurchaseResponse"
}
}
}
]
}
}
}
}
},
"/api/v1/monitor/raw-material-stock-logs": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "根据提供的过滤条件,分页获取原料库存日志",
"produces": [
"application/json"
],
"tags": [
"数据监控"
],
"summary": "获取原料库存日志列表",
"parameters": [
{
"type": "string",
"name": "end_time",
"in": "query"
},
{
"type": "string",
"name": "order_by",
"in": "query"
},
{
"type": "integer",
"name": "page",
"in": "query"
},
{
"type": "integer",
"name": "page_size",
"in": "query"
},
{
"type": "integer",
"name": "raw_material_id",
"in": "query"
},
{
"type": "integer",
"name": "source_id",
"in": "query"
},
{
"type": "string",
"name": "source_type",
"in": "query"
},
{
"type": "string",
"name": "start_time",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/controller.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/dto.ListRawMaterialStockLogResponse"
}
}
}
]
}
}
}
}
},
"/api/v1/monitor/sensor-data": { "/api/v1/monitor/sensor-data": {
"get": { "get": {
"security": [ "security": [
@@ -5637,49 +5405,6 @@
} }
} }
}, },
"dto.FeedFormulaDTO": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
}
},
"dto.FeedUsageRecordDTO": {
"type": "object",
"properties": {
"amount": {
"type": "number"
},
"feed_formula": {
"$ref": "#/definitions/dto.FeedFormulaDTO"
},
"feed_formula_id": {
"type": "integer"
},
"id": {
"type": "integer"
},
"operator_id": {
"type": "integer"
},
"pen": {
"$ref": "#/definitions/dto.PenDTO"
},
"pen_id": {
"type": "integer"
},
"recorded_at": {
"type": "string"
},
"remarks": {
"type": "string"
}
}
},
"dto.HistoricalAlarmDTO": { "dto.HistoricalAlarmDTO": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -5774,20 +5499,6 @@
} }
} }
}, },
"dto.ListFeedUsageRecordResponse": {
"type": "object",
"properties": {
"list": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.FeedUsageRecordDTO"
}
},
"pagination": {
"$ref": "#/definitions/dto.PaginationDTO"
}
}
},
"dto.ListHistoricalAlarmResponse": { "dto.ListHistoricalAlarmResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -5943,34 +5654,6 @@
} }
} }
}, },
"dto.ListRawMaterialPurchaseResponse": {
"type": "object",
"properties": {
"list": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.RawMaterialPurchaseDTO"
}
},
"pagination": {
"$ref": "#/definitions/dto.PaginationDTO"
}
}
},
"dto.ListRawMaterialStockLogResponse": {
"type": "object",
"properties": {
"list": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.RawMaterialStockLogDTO"
}
},
"pagination": {
"$ref": "#/definitions/dto.PaginationDTO"
}
}
},
"dto.ListSensorDataResponse": { "dto.ListSensorDataResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -6208,17 +5891,6 @@
} }
} }
}, },
"dto.PenDTO": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
}
},
"dto.PenResponse": { "dto.PenResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -6725,75 +6397,6 @@
} }
} }
}, },
"dto.RawMaterialDTO": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
}
},
"dto.RawMaterialPurchaseDTO": {
"type": "object",
"properties": {
"amount": {
"type": "number"
},
"created_at": {
"type": "string"
},
"id": {
"type": "integer"
},
"purchase_date": {
"type": "string"
},
"raw_material": {
"$ref": "#/definitions/dto.RawMaterialDTO"
},
"raw_material_id": {
"type": "integer"
},
"supplier": {
"type": "string"
},
"total_price": {
"type": "number"
},
"unit_price": {
"type": "number"
}
}
},
"dto.RawMaterialStockLogDTO": {
"type": "object",
"properties": {
"change_amount": {
"type": "number"
},
"happened_at": {
"type": "string"
},
"id": {
"type": "integer"
},
"raw_material_id": {
"type": "integer"
},
"remarks": {
"type": "string"
},
"source_id": {
"type": "integer"
},
"source_type": {
"$ref": "#/definitions/models.StockLogSourceType"
}
}
},
"dto.ReclassifyPenToNewBatchRequest": { "dto.ReclassifyPenToNewBatchRequest": {
"type": "object", "type": "object",
"required": [ "required": [
@@ -8132,13 +7735,13 @@
"models.SeverityLevel": { "models.SeverityLevel": {
"type": "string", "type": "string",
"enum": [ "enum": [
"Debug", "debug",
"Info", "info",
"Warn", "warn",
"Error", "error",
"DPanic", "dpanic",
"Panic", "panic",
"Fatal" "fatal"
], ],
"x-enum-varnames": [ "x-enum-varnames": [
"DebugLevel", "DebugLevel",
@@ -8150,25 +7753,6 @@
"FatalLevel" "FatalLevel"
] ]
}, },
"models.StockLogSourceType": {
"type": "string",
"enum": [
"采购入库",
"饲喂出库",
"变质出库",
"售卖出库",
"杂用领取",
"手动盘点"
],
"x-enum-varnames": [
"StockLogSourcePurchase",
"StockLogSourceFeeding",
"StockLogSourceDeteriorate",
"StockLogSourceSale",
"StockLogSourceMiscellaneous",
"StockLogSourceManual"
]
},
"models.TaskType": { "models.TaskType": {
"type": "string", "type": "string",
"enum": [ "enum": [
@@ -8177,6 +7761,7 @@
"下料", "下料",
"全量采集", "全量采集",
"告警通知", "告警通知",
"通知刷新",
"设备阈值检查", "设备阈值检查",
"区域阈值检查" "区域阈值检查"
], ],
@@ -8186,6 +7771,7 @@
"TaskTypeAreaCollectorThresholdCheck": "区域阈值检查任务", "TaskTypeAreaCollectorThresholdCheck": "区域阈值检查任务",
"TaskTypeDeviceThresholdCheck": "设备阈值检查任务", "TaskTypeDeviceThresholdCheck": "设备阈值检查任务",
"TaskTypeFullCollection": "新增的全量采集任务", "TaskTypeFullCollection": "新增的全量采集任务",
"TaskTypeNotificationRefresh": "通知刷新任务",
"TaskTypeReleaseFeedWeight": "下料口释放指定重量任务", "TaskTypeReleaseFeedWeight": "下料口释放指定重量任务",
"TaskTypeWaiting": "等待任务" "TaskTypeWaiting": "等待任务"
}, },
@@ -8195,6 +7781,7 @@
"下料口释放指定重量任务", "下料口释放指定重量任务",
"新增的全量采集任务", "新增的全量采集任务",
"告警通知任务", "告警通知任务",
"通知刷新任务",
"设备阈值检查任务", "设备阈值检查任务",
"区域阈值检查任务" "区域阈值检查任务"
], ],
@@ -8204,6 +7791,7 @@
"TaskTypeReleaseFeedWeight", "TaskTypeReleaseFeedWeight",
"TaskTypeFullCollection", "TaskTypeFullCollection",
"TaskTypeAlarmNotification", "TaskTypeAlarmNotification",
"TaskTypeNotificationRefresh",
"TaskTypeDeviceThresholdCheck", "TaskTypeDeviceThresholdCheck",
"TaskTypeAreaCollectorThresholdCheck" "TaskTypeAreaCollectorThresholdCheck"
] ]
@@ -8241,6 +7829,7 @@
"type": "integer", "type": "integer",
"format": "int32", "format": "int32",
"enum": [ "enum": [
7,
-1, -1,
0, 0,
1, 1,
@@ -8250,10 +7839,10 @@
5, 5,
-1, -1,
5, 5,
6, 6
7
], ],
"x-enum-varnames": [ "x-enum-varnames": [
"_numLevels",
"DebugLevel", "DebugLevel",
"InfoLevel", "InfoLevel",
"WarnLevel", "WarnLevel",
@@ -8263,8 +7852,7 @@
"FatalLevel", "FatalLevel",
"_minLevel", "_minLevel",
"_maxLevel", "_maxLevel",
"InvalidLevel", "InvalidLevel"
"_numLevels"
] ]
} }
}, },

View File

@@ -432,34 +432,6 @@ definitions:
thresholds: thresholds:
type: number type: number
type: object type: object
dto.FeedFormulaDTO:
properties:
id:
type: integer
name:
type: string
type: object
dto.FeedUsageRecordDTO:
properties:
amount:
type: number
feed_formula:
$ref: '#/definitions/dto.FeedFormulaDTO'
feed_formula_id:
type: integer
id:
type: integer
operator_id:
type: integer
pen:
$ref: '#/definitions/dto.PenDTO'
pen_id:
type: integer
recorded_at:
type: string
remarks:
type: string
type: object
dto.HistoricalAlarmDTO: dto.HistoricalAlarmDTO:
properties: properties:
alarm_code: alarm_code:
@@ -521,15 +493,6 @@ definitions:
pagination: pagination:
$ref: '#/definitions/dto.PaginationDTO' $ref: '#/definitions/dto.PaginationDTO'
type: object type: object
dto.ListFeedUsageRecordResponse:
properties:
list:
items:
$ref: '#/definitions/dto.FeedUsageRecordDTO'
type: array
pagination:
$ref: '#/definitions/dto.PaginationDTO'
type: object
dto.ListHistoricalAlarmResponse: dto.ListHistoricalAlarmResponse:
properties: properties:
list: list:
@@ -630,24 +593,6 @@ definitions:
example: 100 example: 100
type: integer type: integer
type: object type: object
dto.ListRawMaterialPurchaseResponse:
properties:
list:
items:
$ref: '#/definitions/dto.RawMaterialPurchaseDTO'
type: array
pagination:
$ref: '#/definitions/dto.PaginationDTO'
type: object
dto.ListRawMaterialStockLogResponse:
properties:
list:
items:
$ref: '#/definitions/dto.RawMaterialStockLogDTO'
type: array
pagination:
$ref: '#/definitions/dto.PaginationDTO'
type: object
dto.ListSensorDataResponse: dto.ListSensorDataResponse:
properties: properties:
list: list:
@@ -806,13 +751,6 @@ definitions:
total: total:
type: integer type: integer
type: object type: object
dto.PenDTO:
properties:
id:
type: integer
name:
type: string
type: object
dto.PenResponse: dto.PenResponse:
properties: properties:
capacity: capacity:
@@ -1142,51 +1080,6 @@ definitions:
$ref: '#/definitions/dto.TaskResponse' $ref: '#/definitions/dto.TaskResponse'
type: array type: array
type: object type: object
dto.RawMaterialDTO:
properties:
id:
type: integer
name:
type: string
type: object
dto.RawMaterialPurchaseDTO:
properties:
amount:
type: number
created_at:
type: string
id:
type: integer
purchase_date:
type: string
raw_material:
$ref: '#/definitions/dto.RawMaterialDTO'
raw_material_id:
type: integer
supplier:
type: string
total_price:
type: number
unit_price:
type: number
type: object
dto.RawMaterialStockLogDTO:
properties:
change_amount:
type: number
happened_at:
type: string
id:
type: integer
raw_material_id:
type: integer
remarks:
type: string
source_id:
type: integer
source_type:
$ref: '#/definitions/models.StockLogSourceType'
type: object
dto.ReclassifyPenToNewBatchRequest: dto.ReclassifyPenToNewBatchRequest:
properties: properties:
pen_id: pen_id:
@@ -2159,13 +2052,13 @@ definitions:
- SensorTypeWeight - SensorTypeWeight
models.SeverityLevel: models.SeverityLevel:
enum: enum:
- Debug - debug
- Info - info
- Warn - warn
- Error - error
- DPanic - dpanic
- Panic - panic
- Fatal - fatal
type: string type: string
x-enum-varnames: x-enum-varnames:
- DebugLevel - DebugLevel
@@ -2175,22 +2068,6 @@ definitions:
- DPanicLevel - DPanicLevel
- PanicLevel - PanicLevel
- FatalLevel - FatalLevel
models.StockLogSourceType:
enum:
- 采购入库
- 饲喂出库
- 变质出库
- 售卖出库
- 杂用领取
- 手动盘点
type: string
x-enum-varnames:
- StockLogSourcePurchase
- StockLogSourceFeeding
- StockLogSourceDeteriorate
- StockLogSourceSale
- StockLogSourceMiscellaneous
- StockLogSourceManual
models.TaskType: models.TaskType:
enum: enum:
- 计划分析 - 计划分析
@@ -2198,6 +2075,7 @@ definitions:
- 下料 - 下料
- 全量采集 - 全量采集
- 告警通知 - 告警通知
- 通知刷新
- 设备阈值检查 - 设备阈值检查
- 区域阈值检查 - 区域阈值检查
type: string type: string
@@ -2207,6 +2085,7 @@ definitions:
TaskTypeAreaCollectorThresholdCheck: 区域阈值检查任务 TaskTypeAreaCollectorThresholdCheck: 区域阈值检查任务
TaskTypeDeviceThresholdCheck: 设备阈值检查任务 TaskTypeDeviceThresholdCheck: 设备阈值检查任务
TaskTypeFullCollection: 新增的全量采集任务 TaskTypeFullCollection: 新增的全量采集任务
TaskTypeNotificationRefresh: 通知刷新任务
TaskTypeReleaseFeedWeight: 下料口释放指定重量任务 TaskTypeReleaseFeedWeight: 下料口释放指定重量任务
TaskTypeWaiting: 等待任务 TaskTypeWaiting: 等待任务
x-enum-descriptions: x-enum-descriptions:
@@ -2215,6 +2094,7 @@ definitions:
- 下料口释放指定重量任务 - 下料口释放指定重量任务
- 新增的全量采集任务 - 新增的全量采集任务
- 告警通知任务 - 告警通知任务
- 通知刷新任务
- 设备阈值检查任务 - 设备阈值检查任务
- 区域阈值检查任务 - 区域阈值检查任务
x-enum-varnames: x-enum-varnames:
@@ -2223,6 +2103,7 @@ definitions:
- TaskTypeReleaseFeedWeight - TaskTypeReleaseFeedWeight
- TaskTypeFullCollection - TaskTypeFullCollection
- TaskTypeAlarmNotification - TaskTypeAlarmNotification
- TaskTypeNotificationRefresh
- TaskTypeDeviceThresholdCheck - TaskTypeDeviceThresholdCheck
- TaskTypeAreaCollectorThresholdCheck - TaskTypeAreaCollectorThresholdCheck
models.ValueDescriptor: models.ValueDescriptor:
@@ -2248,6 +2129,7 @@ definitions:
- PlanTypeFilterSystem - PlanTypeFilterSystem
zapcore.Level: zapcore.Level:
enum: enum:
- 7
- -1 - -1
- 0 - 0
- 1 - 1
@@ -2258,10 +2140,10 @@ definitions:
- -1 - -1
- 5 - 5
- 6 - 6
- 7
format: int32 format: int32
type: integer type: integer
x-enum-varnames: x-enum-varnames:
- _numLevels
- DebugLevel - DebugLevel
- InfoLevel - InfoLevel
- WarnLevel - WarnLevel
@@ -2272,7 +2154,6 @@ definitions:
- _minLevel - _minLevel
- _maxLevel - _maxLevel
- InvalidLevel - InvalidLevel
- _numLevels
info: info:
contact: contact:
email: divano@example.com email: divano@example.com
@@ -2353,13 +2234,13 @@ paths:
type: boolean type: boolean
- description: 按告警严重性等级过滤 - description: 按告警严重性等级过滤
enum: enum:
- Debug - debug
- Info - info
- Warn - warn
- Error - error
- DPanic - dpanic
- Panic - panic
- Fatal - fatal
in: query in: query
name: level name: level
type: string type: string
@@ -2430,13 +2311,13 @@ paths:
type: integer type: integer
- description: 按告警等级过滤 - description: 按告警等级过滤
enum: enum:
- Debug - debug
- Info - info
- Warn - warn
- Error - error
- DPanic - dpanic
- Panic - panic
- Fatal - fatal
in: query in: query
name: level name: level
type: string type: string
@@ -2612,13 +2493,13 @@ paths:
type: integer type: integer
- description: 按告警等级过滤 - description: 按告警等级过滤
enum: enum:
- Debug - debug
- Info - info
- Warn - warn
- Error - error
- DPanic - dpanic
- Panic - panic
- Fatal - fatal
in: query in: query
name: level name: level
type: string type: string
@@ -2798,13 +2679,13 @@ paths:
parameters: parameters:
- description: 按告警严重性等级过滤 - description: 按告警严重性等级过滤
enum: enum:
- Debug - debug
- Info - info
- Warn - warn
- Error - error
- DPanic - dpanic
- Panic - panic
- Fatal - fatal
in: query in: query
name: level name: level
type: string type: string
@@ -3333,51 +3214,6 @@ paths:
summary: 获取设备命令日志列表 summary: 获取设备命令日志列表
tags: tags:
- 数据监控 - 数据监控
/api/v1/monitor/feed-usage-records:
get:
description: 根据提供的过滤条件,分页获取饲料使用记录
parameters:
- in: query
name: end_time
type: string
- in: query
name: feed_formula_id
type: integer
- in: query
name: operator_id
type: integer
- in: query
name: order_by
type: string
- in: query
name: page
type: integer
- in: query
name: page_size
type: integer
- in: query
name: pen_id
type: integer
- in: query
name: start_time
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/controller.Response'
- properties:
data:
$ref: '#/definitions/dto.ListFeedUsageRecordResponse'
type: object
security:
- BearerAuth: []
summary: 获取饲料使用记录列表
tags:
- 数据监控
/api/v1/monitor/medication-logs: /api/v1/monitor/medication-logs:
get: get:
description: 根据提供的过滤条件,分页获取用药记录 description: 根据提供的过滤条件,分页获取用药记录
@@ -3434,6 +3270,7 @@ paths:
name: end_time name: end_time
type: string type: string
- enum: - enum:
- 7
- -1 - -1
- 0 - 0
- 1 - 1
@@ -3444,12 +3281,12 @@ paths:
- -1 - -1
- 5 - 5
- 6 - 6
- 7
format: int32 format: int32
in: query in: query
name: level name: level
type: integer type: integer
x-enum-varnames: x-enum-varnames:
- _numLevels
- DebugLevel - DebugLevel
- InfoLevel - InfoLevel
- WarnLevel - WarnLevel
@@ -3460,7 +3297,6 @@ paths:
- _minLevel - _minLevel
- _maxLevel - _maxLevel
- InvalidLevel - InvalidLevel
- _numLevels
- enum: - enum:
- 邮件 - 邮件
- 企业微信 - 企业微信
@@ -3846,93 +3682,6 @@ paths:
summary: 获取计划执行日志列表 summary: 获取计划执行日志列表
tags: tags:
- 数据监控 - 数据监控
/api/v1/monitor/raw-material-purchases:
get:
description: 根据提供的过滤条件,分页获取原料采购记录
parameters:
- in: query
name: end_time
type: string
- in: query
name: order_by
type: string
- in: query
name: page
type: integer
- in: query
name: page_size
type: integer
- in: query
name: raw_material_id
type: integer
- in: query
name: start_time
type: string
- in: query
name: supplier
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/controller.Response'
- properties:
data:
$ref: '#/definitions/dto.ListRawMaterialPurchaseResponse'
type: object
security:
- BearerAuth: []
summary: 获取原料采购记录列表
tags:
- 数据监控
/api/v1/monitor/raw-material-stock-logs:
get:
description: 根据提供的过滤条件,分页获取原料库存日志
parameters:
- in: query
name: end_time
type: string
- in: query
name: order_by
type: string
- in: query
name: page
type: integer
- in: query
name: page_size
type: integer
- in: query
name: raw_material_id
type: integer
- in: query
name: source_id
type: integer
- in: query
name: source_type
type: string
- in: query
name: start_time
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/controller.Response'
- properties:
data:
$ref: '#/definitions/dto.ListRawMaterialStockLogResponse'
type: object
security:
- BearerAuth: []
summary: 获取原料库存日志列表
tags:
- 数据监控
/api/v1/monitor/sensor-data: /api/v1/monitor/sensor-data:
get: get:
description: 根据提供的过滤条件,分页获取传感器数据 description: 根据提供的过滤条件,分页获取传感器数据

View File

@@ -173,9 +173,6 @@ func (a *API) setupRoutes() {
monitorGroup.GET("/task-execution-logs", a.monitorController.ListTaskExecutionLogs) monitorGroup.GET("/task-execution-logs", a.monitorController.ListTaskExecutionLogs)
monitorGroup.GET("/pending-collections", a.monitorController.ListPendingCollections) monitorGroup.GET("/pending-collections", a.monitorController.ListPendingCollections)
monitorGroup.GET("/user-action-logs", a.monitorController.ListUserActionLogs) monitorGroup.GET("/user-action-logs", a.monitorController.ListUserActionLogs)
monitorGroup.GET("/raw-material-purchases", a.monitorController.ListRawMaterialPurchases)
monitorGroup.GET("raw-material-stock-logs", a.monitorController.ListRawMaterialStockLogs)
monitorGroup.GET("/feed-usage-records", a.monitorController.ListFeedUsageRecords)
monitorGroup.GET("/medication-logs", a.monitorController.ListMedicationLogs) monitorGroup.GET("/medication-logs", a.monitorController.ListMedicationLogs)
monitorGroup.GET("/pig-batch-logs", a.monitorController.ListPigBatchLogs) monitorGroup.GET("/pig-batch-logs", a.monitorController.ListPigBatchLogs)
monitorGroup.GET("/weighing-batches", a.monitorController.ListWeighingBatches) monitorGroup.GET("/weighing-batches", a.monitorController.ListWeighingBatches)

View File

@@ -231,108 +231,6 @@ func (c *Controller) ListUserActionLogs(ctx echo.Context) error {
return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取用户操作日志成功", resp, actionType, "获取用户操作日志成功", req) return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取用户操作日志成功", resp, actionType, "获取用户操作日志成功", req)
} }
// ListRawMaterialPurchases godoc
// @Summary 获取原料采购记录列表
// @Description 根据提供的过滤条件,分页获取原料采购记录
// @Tags 数据监控
// @Security BearerAuth
// @Produce json
// @Param query query dto.ListRawMaterialPurchaseRequest true "查询参数"
// @Success 200 {object} controller.Response{data=dto.ListRawMaterialPurchaseResponse}
// @Router /api/v1/monitor/raw-material-purchases [get]
func (c *Controller) ListRawMaterialPurchases(ctx echo.Context) error {
reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "ListRawMaterialPurchases")
const actionType = "获取原料采购记录列表"
var req dto.ListRawMaterialPurchaseRequest
if err := ctx.Bind(&req); err != nil {
logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "参数绑定失败", req)
}
resp, err := c.monitorService.ListRawMaterialPurchases(reqCtx, &req)
if err != nil {
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)
}
// ListRawMaterialStockLogs godoc
// @Summary 获取原料库存日志列表
// @Description 根据提供的过滤条件,分页获取原料库存日志
// @Tags 数据监控
// @Security BearerAuth
// @Produce json
// @Param query query dto.ListRawMaterialStockLogRequest true "查询参数"
// @Success 200 {object} controller.Response{data=dto.ListRawMaterialStockLogResponse}
// @Router /api/v1/monitor/raw-material-stock-logs [get]
func (c *Controller) ListRawMaterialStockLogs(ctx echo.Context) error {
reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "ListRawMaterialStockLogs")
const actionType = "获取原料库存日志列表"
var req dto.ListRawMaterialStockLogRequest
if err := ctx.Bind(&req); err != nil {
logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "参数绑定失败", req)
}
resp, err := c.monitorService.ListRawMaterialStockLogs(reqCtx, &req)
if err != nil {
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)
}
// ListFeedUsageRecords godoc
// @Summary 获取饲料使用记录列表
// @Description 根据提供的过滤条件,分页获取饲料使用记录
// @Tags 数据监控
// @Security BearerAuth
// @Produce json
// @Param query query dto.ListFeedUsageRecordRequest true "查询参数"
// @Success 200 {object} controller.Response{data=dto.ListFeedUsageRecordResponse}
// @Router /api/v1/monitor/feed-usage-records [get]
func (c *Controller) ListFeedUsageRecords(ctx echo.Context) error {
reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "ListFeedUsageRecords")
const actionType = "获取饲料使用记录列表"
var req dto.ListFeedUsageRecordRequest
if err := ctx.Bind(&req); err != nil {
logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "参数绑定失败", req)
}
resp, err := c.monitorService.ListFeedUsageRecords(reqCtx, &req)
if err != nil {
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)
}
// ListMedicationLogs godoc // ListMedicationLogs godoc
// @Summary 获取用药记录列表 // @Summary 获取用药记录列表
// @Description 根据提供的过滤条件,分页获取用药记录 // @Description 根据提供的过滤条件,分页获取用药记录

View File

@@ -170,94 +170,6 @@ func NewListUserActionLogResponse(data []models.UserActionLog, total int64, page
} }
} }
// NewListRawMaterialPurchaseResponse 从模型数据创建列表响应 DTO
func NewListRawMaterialPurchaseResponse(data []models.RawMaterialPurchase, total int64, page, pageSize int) *ListRawMaterialPurchaseResponse {
dtos := make([]RawMaterialPurchaseDTO, len(data))
for i, item := range data {
dtos[i] = RawMaterialPurchaseDTO{
ID: item.ID,
RawMaterialID: item.RawMaterialID,
RawMaterial: RawMaterialDTO{
ID: item.RawMaterial.ID,
Name: item.RawMaterial.Name,
},
Supplier: item.Supplier,
Amount: item.Amount,
UnitPrice: item.UnitPrice,
TotalPrice: item.TotalPrice,
PurchaseDate: item.PurchaseDate,
CreatedAt: item.CreatedAt,
}
}
return &ListRawMaterialPurchaseResponse{
List: dtos,
Pagination: PaginationDTO{
Total: total,
Page: page,
PageSize: pageSize,
},
}
}
// NewListRawMaterialStockLogResponse 从模型数据创建列表响应 DTO
func NewListRawMaterialStockLogResponse(data []models.RawMaterialStockLog, total int64, page, pageSize int) *ListRawMaterialStockLogResponse {
dtos := make([]RawMaterialStockLogDTO, len(data))
for i, item := range data {
dtos[i] = RawMaterialStockLogDTO{
ID: item.ID,
RawMaterialID: item.RawMaterialID,
ChangeAmount: item.ChangeAmount,
SourceType: item.SourceType,
SourceID: item.SourceID,
HappenedAt: item.HappenedAt,
Remarks: item.Remarks,
}
}
return &ListRawMaterialStockLogResponse{
List: dtos,
Pagination: PaginationDTO{
Total: total,
Page: page,
PageSize: pageSize,
},
}
}
// NewListFeedUsageRecordResponse 从模型数据创建列表响应 DTO
func NewListFeedUsageRecordResponse(data []models.FeedUsageRecord, total int64, page, pageSize int) *ListFeedUsageRecordResponse {
dtos := make([]FeedUsageRecordDTO, len(data))
for i, item := range data {
dtos[i] = FeedUsageRecordDTO{
ID: item.ID,
PenID: item.PenID,
Pen: PenDTO{
ID: item.Pen.ID,
Name: item.Pen.PenNumber,
},
FeedFormulaID: item.FeedFormulaID,
FeedFormula: FeedFormulaDTO{
ID: item.FeedFormula.ID,
Name: item.FeedFormula.Name,
},
Amount: item.Amount,
RecordedAt: item.RecordedAt,
OperatorID: item.OperatorID,
Remarks: item.Remarks,
}
}
return &ListFeedUsageRecordResponse{
List: dtos,
Pagination: PaginationDTO{
Total: total,
Page: page,
PageSize: pageSize,
},
}
}
// NewListMedicationLogResponse 从模型数据创建列表响应 DTO // NewListMedicationLogResponse 从模型数据创建列表响应 DTO
func NewListMedicationLogResponse(data []models.MedicationLog, total int64, page, pageSize int) *ListMedicationLogResponse { func NewListMedicationLogResponse(data []models.MedicationLog, total int64, page, pageSize int) *ListMedicationLogResponse {
dtos := make([]MedicationLogDTO, len(data)) dtos := make([]MedicationLogDTO, len(data))

View File

@@ -202,120 +202,6 @@ type ListUserActionLogResponse struct {
Pagination PaginationDTO `json:"pagination"` Pagination PaginationDTO `json:"pagination"`
} }
// --- RawMaterialPurchase ---
// ListRawMaterialPurchaseRequest 定义了获取原料采购列表的请求参数
type ListRawMaterialPurchaseRequest struct {
Page int `json:"page" query:"page"`
PageSize int `json:"page_size" query:"page_size"`
RawMaterialID *uint32 `json:"raw_material_id" query:"raw_material_id"`
Supplier *string `json:"supplier" query:"supplier"`
StartTime *time.Time `json:"start_time" query:"start_time"`
EndTime *time.Time `json:"end_time" query:"end_time"`
OrderBy string `json:"order_by" query:"order_by"`
}
// RawMaterialDTO 是用于API响应的简化版原料结构
type RawMaterialDTO struct {
ID uint32 `json:"id"`
Name string `json:"name"`
}
// RawMaterialPurchaseDTO 是用于API响应的原料采购结构
type RawMaterialPurchaseDTO struct {
ID uint32 `json:"id"`
RawMaterialID uint32 `json:"raw_material_id"`
RawMaterial RawMaterialDTO `json:"raw_material"`
Supplier string `json:"supplier"`
Amount float32 `json:"amount"`
UnitPrice float32 `json:"unit_price"`
TotalPrice float32 `json:"total_price"`
PurchaseDate time.Time `json:"purchase_date"`
CreatedAt time.Time `json:"created_at"`
}
// ListRawMaterialPurchaseResponse 是获取原料采购列表的响应结构
type ListRawMaterialPurchaseResponse struct {
List []RawMaterialPurchaseDTO `json:"list"`
Pagination PaginationDTO `json:"pagination"`
}
// --- RawMaterialStockLog ---
// ListRawMaterialStockLogRequest 定义了获取原料库存日志列表的请求参数
type ListRawMaterialStockLogRequest struct {
Page int `json:"page" query:"page"`
PageSize int `json:"page_size" query:"page_size"`
RawMaterialID *uint32 `json:"raw_material_id" query:"raw_material_id"`
SourceType *string `json:"source_type" query:"source_type"`
SourceID *uint32 `json:"source_id" query:"source_id"`
StartTime *time.Time `json:"start_time" query:"start_time"`
EndTime *time.Time `json:"end_time" query:"end_time"`
OrderBy string `json:"order_by" query:"order_by"`
}
// RawMaterialStockLogDTO 是用于API响应的原料库存日志结构
type RawMaterialStockLogDTO struct {
ID uint32 `json:"id"`
RawMaterialID uint32 `json:"raw_material_id"`
ChangeAmount float32 `json:"change_amount"`
SourceType models.StockLogSourceType `json:"source_type"`
SourceID uint32 `json:"source_id"`
HappenedAt time.Time `json:"happened_at"`
Remarks string `json:"remarks"`
}
// ListRawMaterialStockLogResponse 是获取原料库存日志列表的响应结构
type ListRawMaterialStockLogResponse struct {
List []RawMaterialStockLogDTO `json:"list"`
Pagination PaginationDTO `json:"pagination"`
}
// --- FeedUsageRecord ---
// ListFeedUsageRecordRequest 定义了获取饲料使用记录列表的请求参数
type ListFeedUsageRecordRequest struct {
Page int `json:"page" query:"page"`
PageSize int `json:"page_size" query:"page_size"`
PenID *uint32 `json:"pen_id" query:"pen_id"`
FeedFormulaID *uint32 `json:"feed_formula_id" query:"feed_formula_id"`
OperatorID *uint32 `json:"operator_id" query:"operator_id"`
StartTime *time.Time `json:"start_time" query:"start_time"`
EndTime *time.Time `json:"end_time" query:"end_time"`
OrderBy string `json:"order_by" query:"order_by"`
}
// PenDTO 是用于API响应的简化版猪栏结构
type PenDTO struct {
ID uint32 `json:"id"`
Name string `json:"name"`
}
// FeedFormulaDTO 是用于API响应的简化版饲料配方结构
type FeedFormulaDTO struct {
ID uint32 `json:"id"`
Name string `json:"name"`
}
// FeedUsageRecordDTO 是用于API响应的饲料使用记录结构
type FeedUsageRecordDTO struct {
ID uint32 `json:"id"`
PenID uint32 `json:"pen_id"`
Pen PenDTO `json:"pen"`
FeedFormulaID uint32 `json:"feed_formula_id"`
FeedFormula FeedFormulaDTO `json:"feed_formula"`
Amount float32 `json:"amount"`
RecordedAt time.Time `json:"recorded_at"`
OperatorID uint32 `json:"operator_id"`
Remarks string `json:"remarks"`
}
// ListFeedUsageRecordResponse 是获取饲料使用记录列表的响应结构
type ListFeedUsageRecordResponse struct {
List []FeedUsageRecordDTO `json:"list"`
Pagination PaginationDTO `json:"pagination"`
}
// --- MedicationLog --- // --- MedicationLog ---
// ListMedicationLogRequest 定义了获取用药记录列表的请求参数 // ListMedicationLogRequest 定义了获取用药记录列表的请求参数

View File

@@ -17,9 +17,6 @@ type MonitorService interface {
ListTaskExecutionLogs(ctx context.Context, req *dto.ListTaskExecutionLogRequest) (*dto.ListTaskExecutionLogResponse, error) ListTaskExecutionLogs(ctx context.Context, req *dto.ListTaskExecutionLogRequest) (*dto.ListTaskExecutionLogResponse, error)
ListPendingCollections(ctx context.Context, req *dto.ListPendingCollectionRequest) (*dto.ListPendingCollectionResponse, error) ListPendingCollections(ctx context.Context, req *dto.ListPendingCollectionRequest) (*dto.ListPendingCollectionResponse, error)
ListUserActionLogs(ctx context.Context, req *dto.ListUserActionLogRequest) (*dto.ListUserActionLogResponse, error) ListUserActionLogs(ctx context.Context, req *dto.ListUserActionLogRequest) (*dto.ListUserActionLogResponse, error)
ListRawMaterialPurchases(ctx context.Context, req *dto.ListRawMaterialPurchaseRequest) (*dto.ListRawMaterialPurchaseResponse, error)
ListRawMaterialStockLogs(ctx context.Context, req *dto.ListRawMaterialStockLogRequest) (*dto.ListRawMaterialStockLogResponse, error)
ListFeedUsageRecords(ctx context.Context, req *dto.ListFeedUsageRecordRequest) (*dto.ListFeedUsageRecordResponse, error)
ListMedicationLogs(ctx context.Context, req *dto.ListMedicationLogRequest) (*dto.ListMedicationLogResponse, error) ListMedicationLogs(ctx context.Context, req *dto.ListMedicationLogRequest) (*dto.ListMedicationLogResponse, error)
ListPigBatchLogs(ctx context.Context, req *dto.ListPigBatchLogRequest) (*dto.ListPigBatchLogResponse, error) ListPigBatchLogs(ctx context.Context, req *dto.ListPigBatchLogRequest) (*dto.ListPigBatchLogResponse, error)
ListWeighingBatches(ctx context.Context, req *dto.ListWeighingBatchRequest) (*dto.ListWeighingBatchResponse, error) ListWeighingBatches(ctx context.Context, req *dto.ListWeighingBatchRequest) (*dto.ListWeighingBatchResponse, error)
@@ -40,7 +37,6 @@ type monitorService struct {
planRepository repository.PlanRepository planRepository repository.PlanRepository
pendingCollectionRepo repository.PendingCollectionRepository pendingCollectionRepo repository.PendingCollectionRepository
userActionLogRepo repository.UserActionLogRepository userActionLogRepo repository.UserActionLogRepository
rawMaterialRepo repository.RawMaterialRepository
medicationRepo repository.MedicationLogRepository medicationRepo repository.MedicationLogRepository
pigBatchRepo repository.PigBatchRepository pigBatchRepo repository.PigBatchRepository
pigBatchLogRepo repository.PigBatchLogRepository pigBatchLogRepo repository.PigBatchLogRepository
@@ -59,7 +55,6 @@ func NewMonitorService(
planRepository repository.PlanRepository, planRepository repository.PlanRepository,
pendingCollectionRepo repository.PendingCollectionRepository, pendingCollectionRepo repository.PendingCollectionRepository,
userActionLogRepo repository.UserActionLogRepository, userActionLogRepo repository.UserActionLogRepository,
rawMaterialRepo repository.RawMaterialRepository,
medicationRepo repository.MedicationLogRepository, medicationRepo repository.MedicationLogRepository,
pigBatchRepo repository.PigBatchRepository, pigBatchRepo repository.PigBatchRepository,
pigBatchLogRepo repository.PigBatchLogRepository, pigBatchLogRepo repository.PigBatchLogRepository,
@@ -76,7 +71,6 @@ func NewMonitorService(
planRepository: planRepository, planRepository: planRepository,
pendingCollectionRepo: pendingCollectionRepo, pendingCollectionRepo: pendingCollectionRepo,
userActionLogRepo: userActionLogRepo, userActionLogRepo: userActionLogRepo,
rawMaterialRepo: rawMaterialRepo,
medicationRepo: medicationRepo, medicationRepo: medicationRepo,
pigBatchRepo: pigBatchRepo, pigBatchRepo: pigBatchRepo,
pigBatchLogRepo: pigBatchLogRepo, pigBatchLogRepo: pigBatchLogRepo,
@@ -236,68 +230,6 @@ func (s *monitorService) ListUserActionLogs(ctx context.Context, req *dto.ListUs
return dto.NewListUserActionLogResponse(data, total, req.Page, req.PageSize), nil return dto.NewListUserActionLogResponse(data, total, req.Page, req.PageSize), nil
} }
// ListRawMaterialPurchases 负责处理查询原料采购记录列表的业务逻辑
func (s *monitorService) ListRawMaterialPurchases(ctx context.Context, req *dto.ListRawMaterialPurchaseRequest) (*dto.ListRawMaterialPurchaseResponse, error) {
serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListRawMaterialPurchases")
opts := repository.RawMaterialPurchaseListOptions{
RawMaterialID: req.RawMaterialID,
Supplier: req.Supplier,
OrderBy: req.OrderBy,
StartTime: req.StartTime,
EndTime: req.EndTime,
}
data, total, err := s.rawMaterialRepo.ListRawMaterialPurchases(serviceCtx, opts, req.Page, req.PageSize)
if err != nil {
return nil, err
}
return dto.NewListRawMaterialPurchaseResponse(data, total, req.Page, req.PageSize), nil
}
// ListRawMaterialStockLogs 负责处理查询原料库存日志列表的业务逻辑
func (s *monitorService) ListRawMaterialStockLogs(ctx context.Context, req *dto.ListRawMaterialStockLogRequest) (*dto.ListRawMaterialStockLogResponse, error) {
serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListRawMaterialStockLogs")
opts := repository.RawMaterialStockLogListOptions{
RawMaterialID: req.RawMaterialID,
SourceID: req.SourceID,
OrderBy: req.OrderBy,
StartTime: req.StartTime,
EndTime: req.EndTime,
}
if req.SourceType != nil {
sourceType := models.StockLogSourceType(*req.SourceType)
opts.SourceType = &sourceType
}
data, total, err := s.rawMaterialRepo.ListRawMaterialStockLogs(serviceCtx, opts, req.Page, req.PageSize)
if err != nil {
return nil, err
}
return dto.NewListRawMaterialStockLogResponse(data, total, req.Page, req.PageSize), nil
}
// ListFeedUsageRecords 负责处理查询饲料使用记录列表的业务逻辑
func (s *monitorService) ListFeedUsageRecords(ctx context.Context, req *dto.ListFeedUsageRecordRequest) (*dto.ListFeedUsageRecordResponse, error) {
serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListFeedUsageRecords")
opts := repository.FeedUsageRecordListOptions{
PenID: req.PenID,
FeedFormulaID: req.FeedFormulaID,
OperatorID: req.OperatorID,
OrderBy: req.OrderBy,
StartTime: req.StartTime,
EndTime: req.EndTime,
}
data, total, err := s.rawMaterialRepo.ListFeedUsageRecords(serviceCtx, opts, req.Page, req.PageSize)
if err != nil {
return nil, err
}
return dto.NewListFeedUsageRecordResponse(data, total, req.Page, req.PageSize), nil
}
// ListMedicationLogs 负责处理查询用药记录列表的业务逻辑 // ListMedicationLogs 负责处理查询用药记录列表的业务逻辑
func (s *monitorService) ListMedicationLogs(ctx context.Context, req *dto.ListMedicationLogRequest) (*dto.ListMedicationLogResponse, error) { func (s *monitorService) ListMedicationLogs(ctx context.Context, req *dto.ListMedicationLogRequest) (*dto.ListMedicationLogResponse, error) {
serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListMedicationLogs") serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListMedicationLogs")

View File

@@ -79,7 +79,6 @@ type Repositories struct {
pigTradeRepo repository.PigTradeRepository pigTradeRepo repository.PigTradeRepository
pigSickPigLogRepo repository.PigSickLogRepository pigSickPigLogRepo repository.PigSickLogRepository
medicationLogRepo repository.MedicationLogRepository medicationLogRepo repository.MedicationLogRepository
rawMaterialRepo repository.RawMaterialRepository
notificationRepo repository.NotificationRepository notificationRepo repository.NotificationRepository
alarmRepo repository.AlarmRepository alarmRepo repository.AlarmRepository
unitOfWork repository.UnitOfWork unitOfWork repository.UnitOfWork
@@ -108,7 +107,6 @@ func initRepositories(ctx context.Context, db *gorm.DB) *Repositories {
pigTradeRepo: repository.NewGormPigTradeRepository(logs.AddCompName(baseCtx, "PigTradeRepo"), db), pigTradeRepo: repository.NewGormPigTradeRepository(logs.AddCompName(baseCtx, "PigTradeRepo"), db),
pigSickPigLogRepo: repository.NewGormPigSickLogRepository(logs.AddCompName(baseCtx, "PigSickPigLogRepo"), db), pigSickPigLogRepo: repository.NewGormPigSickLogRepository(logs.AddCompName(baseCtx, "PigSickPigLogRepo"), db),
medicationLogRepo: repository.NewGormMedicationLogRepository(logs.AddCompName(baseCtx, "MedicationLogRepo"), db), medicationLogRepo: repository.NewGormMedicationLogRepository(logs.AddCompName(baseCtx, "MedicationLogRepo"), db),
rawMaterialRepo: repository.NewGormRawMaterialRepository(logs.AddCompName(baseCtx, "RawMaterialRepo"), db),
notificationRepo: repository.NewGormNotificationRepository(logs.AddCompName(baseCtx, "NotificationRepo"), db), notificationRepo: repository.NewGormNotificationRepository(logs.AddCompName(baseCtx, "NotificationRepo"), db),
alarmRepo: repository.NewGormAlarmRepository(logs.AddCompName(baseCtx, "AlarmRepo"), db), alarmRepo: repository.NewGormAlarmRepository(logs.AddCompName(baseCtx, "AlarmRepo"), db),
unitOfWork: repository.NewGormUnitOfWork(logs.AddCompName(baseCtx, "UnitOfWork"), db), unitOfWork: repository.NewGormUnitOfWork(logs.AddCompName(baseCtx, "UnitOfWork"), db),
@@ -247,7 +245,6 @@ func initAppServices(ctx context.Context, infra *Infrastructure, domainServices
infra.repos.planRepo, infra.repos.planRepo,
infra.repos.pendingCollectionRepo, infra.repos.pendingCollectionRepo,
infra.repos.userActionLogRepo, infra.repos.userActionLogRepo,
infra.repos.rawMaterialRepo,
infra.repos.medicationLogRepo, infra.repos.medicationLogRepo,
infra.repos.pigBatchRepo, infra.repos.pigBatchRepo,
infra.repos.pigBatchLogRepo, infra.repos.pigBatchLogRepo,

View File

@@ -167,9 +167,6 @@ func (ps *PostgresStorage) creatingHyperTable(ctx context.Context) error {
{models.TaskExecutionLog{}, "created_at"}, {models.TaskExecutionLog{}, "created_at"},
{models.PendingCollection{}, "created_at"}, {models.PendingCollection{}, "created_at"},
{models.UserActionLog{}, "time"}, {models.UserActionLog{}, "time"},
{models.RawMaterialPurchase{}, "purchase_date"},
{models.RawMaterialStockLog{}, "happened_at"},
{models.FeedUsageRecord{}, "recorded_at"},
{models.MedicationLog{}, "happened_at"}, {models.MedicationLog{}, "happened_at"},
{models.PigBatchLog{}, "happened_at"}, {models.PigBatchLog{}, "happened_at"},
{models.WeighingBatch{}, "weighing_time"}, {models.WeighingBatch{}, "weighing_time"},
@@ -180,6 +177,7 @@ func (ps *PostgresStorage) creatingHyperTable(ctx context.Context) error {
{models.PigSale{}, "sale_date"}, {models.PigSale{}, "sale_date"},
{models.Notification{}, "alarm_timestamp"}, {models.Notification{}, "alarm_timestamp"},
{models.HistoricalAlarm{}, "trigger_time"}, {models.HistoricalAlarm{}, "trigger_time"},
{models.RawMaterialStockLog{}, "happened_at"},
} }
for _, table := range tablesToConvert { for _, table := range tablesToConvert {
@@ -210,9 +208,6 @@ func (ps *PostgresStorage) applyCompressionPolicies(ctx context.Context) error {
{models.TaskExecutionLog{}, "task_id"}, {models.TaskExecutionLog{}, "task_id"},
{models.PendingCollection{}, "device_id"}, {models.PendingCollection{}, "device_id"},
{models.UserActionLog{}, "user_id"}, {models.UserActionLog{}, "user_id"},
{models.RawMaterialPurchase{}, "raw_material_id"},
{models.RawMaterialStockLog{}, "raw_material_id"},
{models.FeedUsageRecord{}, "pen_id"},
{models.MedicationLog{}, "pig_batch_id"}, {models.MedicationLog{}, "pig_batch_id"},
{models.PigBatchLog{}, "pig_batch_id"}, {models.PigBatchLog{}, "pig_batch_id"},
{models.WeighingBatch{}, "pig_batch_id"}, {models.WeighingBatch{}, "pig_batch_id"},
@@ -223,6 +218,7 @@ func (ps *PostgresStorage) applyCompressionPolicies(ctx context.Context) error {
{models.PigSale{}, "pig_batch_id"}, {models.PigSale{}, "pig_batch_id"},
{models.Notification{}, "user_id"}, {models.Notification{}, "user_id"},
{models.HistoricalAlarm{}, "source_id"}, {models.HistoricalAlarm{}, "source_id"},
{models.RawMaterialStockLog{}, "raw_material_id"},
} }
for _, policy := range policies { for _, policy := range policies {
@@ -258,6 +254,15 @@ func (ps *PostgresStorage) creatingIndex(ctx context.Context) error {
// 使用 IF NOT EXISTS 保证幂等性 // 使用 IF NOT EXISTS 保证幂等性
// 如果索引已存在,此命令不会报错 // 如果索引已存在,此命令不会报错
// 为 raw_material_nutrients 表创建部分唯一索引,以兼容软删除
logger.Debug("正在为 raw_material_nutrients 表创建部分唯一索引")
partialIndexSQL := "CREATE UNIQUE INDEX IF NOT EXISTS idx_raw_material_nutrients_unique_when_not_deleted ON raw_material_nutrients (raw_material_id, nutrient_id) WHERE deleted_at IS NULL;"
if err := ps.db.WithContext(storageCtx).Exec(partialIndexSQL).Error; err != nil {
logger.Errorw("为 raw_material_nutrients 创建部分唯一索引失败", "error", err)
return fmt.Errorf("为 raw_material_nutrients 创建部分唯一索引失败: %w", err)
}
logger.Debug("成功为 raw_material_nutrients 创建部分唯一索引 (或已存在)")
// 为 sensor_data 表的 data 字段创建 GIN 索引 // 为 sensor_data 表的 data 字段创建 GIN 索引
logger.Debug("正在为 sensor_data 表的 data 字段创建 GIN 索引") logger.Debug("正在为 sensor_data 表的 data 字段创建 GIN 索引")
ginSensorDataIndexSQL := "CREATE INDEX IF NOT EXISTS idx_sensor_data_data_gin ON sensor_data USING GIN (data);" ginSensorDataIndexSQL := "CREATE INDEX IF NOT EXISTS idx_sensor_data_data_gin ON sensor_data USING GIN (data);"

View File

@@ -1,111 +0,0 @@
package models
import (
"time"
)
/*
饲料和饲喂相关的模型
*/
// RawMaterial 代表饲料的原料。
// 建议:所有重量单位统一存储 (例如, 全部使用 'g'),便于计算和避免转换错误。
type RawMaterial struct {
Model
Name string `gorm:"size:100;unique;not null;comment:原料名称"`
Description string `gorm:"size:255;comment:描述"`
Quantity float32 `gorm:"not null;comment:库存总量, 单位: g"`
}
func (RawMaterial) TableName() string {
return "raw_materials"
}
// RawMaterialPurchase 记录了原料的每一次采购。
type RawMaterialPurchase struct {
Model
RawMaterialID uint32 `gorm:"not null;index;comment:关联的原料ID"`
RawMaterial RawMaterial `gorm:"foreignKey:RawMaterialID"`
Supplier string `gorm:"size:100;comment:供应商"`
Amount float32 `gorm:"not null;comment:采购数量, 单位: g"`
UnitPrice float32 `gorm:"comment:单价"`
TotalPrice float32 `gorm:"comment:总价"`
PurchaseDate time.Time `gorm:"primaryKey;comment:采购日期"`
CreatedAt time.Time
}
func (RawMaterialPurchase) TableName() string {
return "raw_material_purchases"
}
// StockLogSourceType 定义了库存日志来源的类型
type StockLogSourceType string
const (
StockLogSourcePurchase StockLogSourceType = "采购入库"
StockLogSourceFeeding StockLogSourceType = "饲喂出库"
StockLogSourceDeteriorate StockLogSourceType = "变质出库"
StockLogSourceSale StockLogSourceType = "售卖出库"
StockLogSourceMiscellaneous StockLogSourceType = "杂用领取"
StockLogSourceManual StockLogSourceType = "手动盘点"
)
// RawMaterialStockLog 记录了原料库存的所有变动,提供了完整的追溯链。
type RawMaterialStockLog struct {
Model
RawMaterialID uint32 `gorm:"not null;index;comment:关联的原料ID"`
ChangeAmount float32 `gorm:"not null;comment:变动数量, 正数为入库, 负数为出库"`
SourceType StockLogSourceType `gorm:"size:50;not null;index;comment:库存变动来源类型"`
SourceID uint32 `gorm:"not null;index;comment:来源记录的ID (如 RawMaterialPurchase.ID 或 FeedUsageRecord.ID)"`
HappenedAt time.Time `gorm:"primaryKey;comment:业务发生时间"`
Remarks string `gorm:"comment:备注, 如主动领取的理由等"`
}
func (RawMaterialStockLog) TableName() string {
return "raw_material_stock_logs"
}
// FeedFormula 代表饲料配方。
// 对于没有配方的外购饲料,可以将其视为一种特殊的 RawMaterial, 并为其创建一个仅包含它自己的 FeedFormula。
type FeedFormula struct {
Model
Name string `gorm:"size:100;unique;not null;comment:配方名称"`
Description string `gorm:"size:255;comment:描述"`
Components []FeedFormulaComponent `gorm:"foreignKey:FeedFormulaID"`
}
func (FeedFormula) TableName() string {
return "feed_formulas"
}
// FeedFormulaComponent 代表配方中的一种原料及其占比。
type FeedFormulaComponent struct {
Model
FeedFormulaID uint32 `gorm:"not null;index;comment:外键到 FeedFormula"`
RawMaterialID uint32 `gorm:"not null;index;comment:外键到 RawMaterial"`
RawMaterial RawMaterial `gorm:"foreignKey:RawMaterialID"`
Percentage float32 `gorm:"not null;comment:该原料在配方中的百分比 (0-1.0)"`
}
func (FeedFormulaComponent) TableName() string {
return "feed_formula_components"
}
// FeedUsageRecord 代表饲料使用记录。
// 应用层逻辑:当一条使用记录被创建时,应根据其使用的 FeedFormula,
// 计算出每种 RawMaterial 的消耗量,并在 RawMaterialStockLog 中创建对应的出库记录。
type FeedUsageRecord struct {
Model
PenID uint32 `gorm:"not null;index;comment:关联的猪栏ID"`
Pen Pen `gorm:"foreignKey:PenID"`
FeedFormulaID uint32 `gorm:"not null;index;comment:使用的饲料配方ID"`
FeedFormula FeedFormula `gorm:"foreignKey:FeedFormulaID"`
Amount float32 `gorm:"not null;comment:使用数量, 单位: g"`
RecordedAt time.Time `gorm:"primaryKey;comment:记录时间"`
OperatorID uint32 `gorm:"not null;comment:操作员"`
Remarks string `gorm:"comment:备注, 如 '例行喂料, 弱猪补料' 等"`
}
func (FeedUsageRecord) TableName() string {
return "feed_usage_records"
}

View File

@@ -63,11 +63,9 @@ func GetAllModels() []interface{} {
// Feed Models // Feed Models
&RawMaterial{}, &RawMaterial{},
&RawMaterialPurchase{}, &Nutrient{},
&RawMaterialNutrient{},
&RawMaterialStockLog{}, &RawMaterialStockLog{},
&FeedFormula{},
&FeedFormulaComponent{},
&FeedUsageRecord{},
// Medication Models // Medication Models
&Medication{}, &Medication{},

View File

@@ -0,0 +1,92 @@
package models
import (
"time"
)
// StockLogSourceType 定义了库存日志来源的类型
type StockLogSourceType string
const (
StockLogSourcePurchase StockLogSourceType = "采购入库"
StockLogSourceFeeding StockLogSourceType = "饲喂出库"
StockLogSourceDeteriorate StockLogSourceType = "变质出库"
StockLogSourceSale StockLogSourceType = "售卖出库"
StockLogSourceMiscellaneous StockLogSourceType = "杂用领取"
StockLogSourceManual StockLogSourceType = "手动盘点"
StockLogSourceFermentStart StockLogSourceType = "发酵出库" // 原料投入发酵,从库存中扣除
StockLogSourceFermentEnd StockLogSourceType = "发酵入库" // 发酵料产出,作为新原料计入库存
)
// NutrientType 定义了营养素的分类,用于配方优化和成本控制。
type NutrientType string
const (
PositiveNutrient NutrientType = "正面营养" // 希望在配方中最大化的营养素,如蛋白质、能量
NegativeNutrient NutrientType = "负面营养" // 需要控制上限的营养素,如粗纤维、霉菌毒素
)
// RawMaterial 代表一种原料的静态定义,是系统中的原料字典。
type RawMaterial struct {
Model
Name string `gorm:"size:100;unique;not null;comment:原料名称"`
Description string `gorm:"size:255;comment:描述"`
// Quantity 是当前库存的快照值,用于提供高性能的库存查询。
// 注意:此字段的值必须在数据库事务中与 RawMaterialStockLog 同步更新,以保证数据一致性。
Quantity float32 `gorm:"not null;default:0;comment:当前库存快照, 单位: g"`
}
func (RawMaterial) TableName() string {
return "raw_materials"
}
// Nutrient 代表一种营养素的静态定义,是系统中的营养素字典。
// 注意本系统强制统一营养单位不再单独设置Unit字段。
// 约定:宏量营养素(粗蛋白等)单位为百分比(%),微量元素(氨基酸等)单位为毫克/千克(mg/kg)。
type Nutrient struct {
Model
Name string `gorm:"size:100;unique;not null;comment:营养素名称"`
Type NutrientType `gorm:"size:50;not null;comment:营养素类型 (正面营养/负面营养)"`
Description string `gorm:"size:255;comment:描述"`
}
func (Nutrient) TableName() string {
return "nutrients"
}
// RawMaterialNutrient 存储了特定原料的特定营养素的含量值。
// 这是连接原料和营养素的“营养价值表”。
// 注意:其唯一性由 postgres.go 中的部分唯一索引保证,以兼容软删除。
type RawMaterialNutrient struct {
Model
RawMaterialID uint32 `gorm:"not null;comment:关联的原料ID"`
RawMaterial RawMaterial `gorm:"foreignKey:RawMaterialID"`
NutrientID uint32 `gorm:"not null;comment:关联的营养素ID"`
Nutrient Nutrient `gorm:"foreignKey:NutrientID"`
// Value 存储营养价值含量。单位遵循 Nutrient 表中定义的系统级约定。
Value float32 `gorm:"not null;comment:营养价值含量"`
}
func (RawMaterialNutrient) TableName() string {
return "raw_material_nutrients"
}
// RawMaterialStockLog 记录了原料库存的所有变动,提供了完整的追溯链。
// 它是保证数据一致性和可审计性的核心。
type RawMaterialStockLog struct {
Model
RawMaterialID uint32 `gorm:"not null;index;comment:关联的原料ID"`
RawMaterial RawMaterial `gorm:"foreignKey:RawMaterialID"`
ChangeAmount float32 `gorm:"not null;comment:变动数量, 正数为入库, 负数为出库, 单位: g"`
// SourceType 告知 SourceID 关联的是哪种类型的业务单据。
SourceType StockLogSourceType `gorm:"size:50;not null;index;comment:库存变动来源类型"`
// SourceID 是一个多态外键关联到触发此次变动的业务单据ID (如采购单ID)。
// 对于无单据的业务(如手动盘点)此字段可为NULL。
SourceID *uint32 `gorm:"index;comment:来源业务单据的ID"`
HappenedAt time.Time `gorm:"primaryKey;comment:业务发生时间"`
Remarks string `gorm:"comment:备注"`
}
func (RawMaterialStockLog) TableName() string {
return "raw_material_stock_logs"
}

View File

@@ -1,187 +0,0 @@
package repository
import (
"context"
"time"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
"gorm.io/gorm"
)
// RawMaterialPurchaseListOptions 定义了查询原料采购记录时的可选参数
type RawMaterialPurchaseListOptions struct {
RawMaterialID *uint32
Supplier *string
StartTime *time.Time // 基于 purchase_date 字段
EndTime *time.Time // 基于 purchase_date 字段
OrderBy string // 例如 "purchase_date asc"
}
// RawMaterialStockLogListOptions 定义了查询原料库存日志时的可选参数
type RawMaterialStockLogListOptions struct {
RawMaterialID *uint32
SourceType *models.StockLogSourceType
SourceID *uint32
StartTime *time.Time // 基于 happened_at 字段
EndTime *time.Time // 基于 happened_at 字段
OrderBy string // 例如 "happened_at asc"
}
// FeedUsageRecordListOptions 定义了查询饲料使用记录时的可选参数
type FeedUsageRecordListOptions struct {
PenID *uint32
FeedFormulaID *uint32
OperatorID *uint32
StartTime *time.Time // 基于 recorded_at 字段
EndTime *time.Time // 基于 recorded_at 字段
OrderBy string // 例如 "recorded_at asc"
}
// RawMaterialRepository 定义了与原料相关的数据库操作接口
type RawMaterialRepository interface {
ListRawMaterialPurchases(ctx context.Context, opts RawMaterialPurchaseListOptions, page, pageSize int) ([]models.RawMaterialPurchase, int64, error)
ListRawMaterialStockLogs(ctx context.Context, opts RawMaterialStockLogListOptions, page, pageSize int) ([]models.RawMaterialStockLog, int64, error)
ListFeedUsageRecords(ctx context.Context, opts FeedUsageRecordListOptions, page, pageSize int) ([]models.FeedUsageRecord, int64, error)
}
// gormRawMaterialRepository 是 RawMaterialRepository 的 GORM 实现
type gormRawMaterialRepository struct {
ctx context.Context
db *gorm.DB
}
// NewGormRawMaterialRepository 创建一个新的 RawMaterialRepository GORM 实现实例
func NewGormRawMaterialRepository(ctx context.Context, db *gorm.DB) RawMaterialRepository {
return &gormRawMaterialRepository{ctx: ctx, db: db}
}
// ListRawMaterialPurchases 实现了分页和过滤查询原料采购记录的功能
func (r *gormRawMaterialRepository) ListRawMaterialPurchases(ctx context.Context, opts RawMaterialPurchaseListOptions, page, pageSize int) ([]models.RawMaterialPurchase, int64, error) {
repoCtx := logs.AddFuncName(ctx, r.ctx, "ListRawMaterialPurchases")
if page <= 0 || pageSize <= 0 {
return nil, 0, ErrInvalidPagination
}
var results []models.RawMaterialPurchase
var total int64
query := r.db.WithContext(repoCtx).Model(&models.RawMaterialPurchase{})
if opts.RawMaterialID != nil {
query = query.Where("raw_material_id = ?", *opts.RawMaterialID)
}
if opts.Supplier != nil {
query = query.Where("supplier LIKE ?", "%"+*opts.Supplier+"%")
}
if opts.StartTime != nil {
query = query.Where("purchase_date >= ?", *opts.StartTime)
}
if opts.EndTime != nil {
query = query.Where("purchase_date <= ?", *opts.EndTime)
}
if err := query.Count(&total).Error; err != nil {
return nil, 0, err
}
orderBy := "purchase_date DESC"
if opts.OrderBy != "" {
orderBy = opts.OrderBy
}
query = query.Order(orderBy).Preload("RawMaterial")
offset := (page - 1) * pageSize
err := query.Limit(pageSize).Offset(offset).Find(&results).Error
return results, total, err
}
// ListRawMaterialStockLogs 实现了分页和过滤查询原料库存日志的功能
func (r *gormRawMaterialRepository) ListRawMaterialStockLogs(ctx context.Context, opts RawMaterialStockLogListOptions, page, pageSize int) ([]models.RawMaterialStockLog, int64, error) {
repoCtx := logs.AddFuncName(ctx, r.ctx, "ListRawMaterialStockLogs")
if page <= 0 || pageSize <= 0 {
return nil, 0, ErrInvalidPagination
}
var results []models.RawMaterialStockLog
var total int64
query := r.db.WithContext(repoCtx).Model(&models.RawMaterialStockLog{})
if opts.RawMaterialID != nil {
query = query.Where("raw_material_id = ?", *opts.RawMaterialID)
}
if opts.SourceType != nil {
query = query.Where("source_type = ?", *opts.SourceType)
}
if opts.SourceID != nil {
query = query.Where("source_id = ?", *opts.SourceID)
}
if opts.StartTime != nil {
query = query.Where("happened_at >= ?", *opts.StartTime)
}
if opts.EndTime != nil {
query = query.Where("happened_at <= ?", *opts.EndTime)
}
if err := query.Count(&total).Error; err != nil {
return nil, 0, err
}
orderBy := "happened_at 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
}
// ListFeedUsageRecords 实现了分页和过滤查询饲料使用记录的功能
func (r *gormRawMaterialRepository) ListFeedUsageRecords(ctx context.Context, opts FeedUsageRecordListOptions, page, pageSize int) ([]models.FeedUsageRecord, int64, error) {
repoCtx := logs.AddFuncName(ctx, r.ctx, "ListFeedUsageRecords")
if page <= 0 || pageSize <= 0 {
return nil, 0, ErrInvalidPagination
}
var results []models.FeedUsageRecord
var total int64
query := r.db.WithContext(repoCtx).Model(&models.FeedUsageRecord{})
if opts.PenID != nil {
query = query.Where("pen_id = ?", *opts.PenID)
}
if opts.FeedFormulaID != nil {
query = query.Where("feed_formula_id = ?", *opts.FeedFormulaID)
}
if opts.OperatorID != nil {
query = query.Where("operator_id = ?", *opts.OperatorID)
}
if opts.StartTime != nil {
query = query.Where("recorded_at >= ?", *opts.StartTime)
}
if opts.EndTime != nil {
query = query.Where("recorded_at <= ?", *opts.EndTime)
}
if err := query.Count(&total).Error; err != nil {
return nil, 0, err
}
orderBy := "recorded_at DESC"
if opts.OrderBy != "" {
orderBy = opts.OrderBy
}
query = query.Order(orderBy).Preload("Pen").Preload("FeedFormula")
offset := (page - 1) * pageSize
err := query.Limit(pageSize).Offset(offset).Find(&results).Error
return results, total, err
}