重构dto
This commit is contained in:
@@ -3,12 +3,11 @@ package device
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller"
|
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller"
|
||||||
|
"git.huangwc.com/pig/pig-farm-controller/internal/app/dto"
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
|
||||||
@@ -34,243 +33,11 @@ func NewController(
|
|||||||
return &Controller{
|
return &Controller{
|
||||||
deviceRepo: deviceRepo,
|
deviceRepo: deviceRepo,
|
||||||
areaControllerRepo: areaControllerRepo,
|
areaControllerRepo: areaControllerRepo,
|
||||||
deviceTemplateRepo: deviceTemplateRepo, // 初始化设备模板仓库
|
deviceTemplateRepo: deviceTemplateRepo,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Request DTOs ---
|
|
||||||
|
|
||||||
// CreateDeviceRequest 定义了创建设备时需要传入的参数
|
|
||||||
type CreateDeviceRequest struct {
|
|
||||||
Name string `json:"name" binding:"required"`
|
|
||||||
DeviceTemplateID uint `json:"device_template_id" binding:"required"`
|
|
||||||
AreaControllerID uint `json:"area_controller_id" binding:"required"`
|
|
||||||
Location string `json:"location,omitempty"`
|
|
||||||
Properties map[string]interface{} `json:"properties,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateDeviceRequest 定义了更新设备时需要传入的参数
|
|
||||||
type UpdateDeviceRequest struct {
|
|
||||||
Name string `json:"name" binding:"required"`
|
|
||||||
DeviceTemplateID uint `json:"device_template_id" binding:"required"`
|
|
||||||
AreaControllerID uint `json:"area_controller_id" binding:"required"`
|
|
||||||
Location string `json:"location,omitempty"`
|
|
||||||
Properties map[string]interface{} `json:"properties,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateAreaControllerRequest 定义了创建区域主控时需要传入的参数
|
|
||||||
type CreateAreaControllerRequest struct {
|
|
||||||
Name string `json:"name" binding:"required"`
|
|
||||||
NetworkID string `json:"network_id" binding:"required"`
|
|
||||||
Location string `json:"location,omitempty"`
|
|
||||||
Properties map[string]interface{} `json:"properties,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateAreaControllerRequest 定义了更新区域主控时需要传入的参数
|
|
||||||
type UpdateAreaControllerRequest struct {
|
|
||||||
Name string `json:"name" binding:"required"`
|
|
||||||
NetworkID string `json:"network_id" binding:"required"`
|
|
||||||
Location string `json:"location,omitempty"`
|
|
||||||
Properties map[string]interface{} `json:"properties,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateDeviceTemplateRequest 定义了创建设备模板时需要传入的参数
|
|
||||||
type CreateDeviceTemplateRequest struct {
|
|
||||||
Name string `json:"name" binding:"required"`
|
|
||||||
Manufacturer string `json:"manufacturer,omitempty"`
|
|
||||||
Description string `json:"description,omitempty"`
|
|
||||||
Category models.DeviceCategory `json:"category" binding:"required"`
|
|
||||||
Commands map[string]interface{} `json:"commands" binding:"required"`
|
|
||||||
Values []models.ValueDescriptor `json:"values,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateDeviceTemplateRequest 定义了更新设备模板时需要传入的参数
|
|
||||||
type UpdateDeviceTemplateRequest struct {
|
|
||||||
Name string `json:"name" binding:"required"`
|
|
||||||
Manufacturer string `json:"manufacturer,omitempty"`
|
|
||||||
Description string `json:"description,omitempty"`
|
|
||||||
Category models.DeviceCategory `json:"category" binding:"required"`
|
|
||||||
Commands map[string]interface{} `json:"commands" binding:"required"`
|
|
||||||
Values []models.ValueDescriptor `json:"values,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Response DTOs ---
|
|
||||||
|
|
||||||
// DeviceResponse 定义了返回给客户端的单个设备信息的结构
|
|
||||||
type DeviceResponse struct {
|
|
||||||
ID uint `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
DeviceTemplateID uint `json:"device_template_id"`
|
|
||||||
DeviceTemplateName string `json:"device_template_name"`
|
|
||||||
AreaControllerID uint `json:"area_controller_id"`
|
|
||||||
AreaControllerName string `json:"area_controller_name"`
|
|
||||||
Location string `json:"location"`
|
|
||||||
Properties map[string]interface{} `json:"properties"`
|
|
||||||
CreatedAt string `json:"created_at"`
|
|
||||||
UpdatedAt string `json:"updated_at"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AreaControllerResponse 定义了返回给客户端的单个区域主控信息的结构
|
|
||||||
type AreaControllerResponse struct {
|
|
||||||
ID uint `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
NetworkID string `json:"network_id"`
|
|
||||||
Location string `json:"location"`
|
|
||||||
Status string `json:"status"`
|
|
||||||
Properties map[string]interface{} `json:"properties"`
|
|
||||||
CreatedAt string `json:"created_at"`
|
|
||||||
UpdatedAt string `json:"updated_at"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeviceTemplateResponse 定义了返回给客户端的单个设备模板信息的结构
|
|
||||||
type DeviceTemplateResponse struct {
|
|
||||||
ID uint `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Manufacturer string `json:"manufacturer"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Category models.DeviceCategory `json:"category"`
|
|
||||||
Commands map[string]interface{} `json:"commands"`
|
|
||||||
Values []models.ValueDescriptor `json:"values"`
|
|
||||||
CreatedAt string `json:"created_at"`
|
|
||||||
UpdatedAt string `json:"updated_at"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- DTO 转换函数 ---
|
|
||||||
|
|
||||||
// newDeviceResponse 从数据库模型创建一个新的设备响应 DTO
|
|
||||||
func newDeviceResponse(device *models.Device) (*DeviceResponse, error) {
|
|
||||||
if device == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var props map[string]interface{}
|
|
||||||
if len(device.Properties) > 0 && string(device.Properties) != "null" {
|
|
||||||
if err := device.ParseProperties(&props); err != nil {
|
|
||||||
return nil, fmt.Errorf("解析设备属性失败 (ID: %d): %w", device.ID, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 确保 DeviceTemplate 和 AreaController 已预加载
|
|
||||||
deviceTemplateName := ""
|
|
||||||
if device.DeviceTemplate.ID != 0 {
|
|
||||||
deviceTemplateName = device.DeviceTemplate.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
areaControllerName := ""
|
|
||||||
if device.AreaController.ID != 0 {
|
|
||||||
areaControllerName = device.AreaController.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
return &DeviceResponse{
|
|
||||||
ID: device.ID,
|
|
||||||
Name: device.Name,
|
|
||||||
DeviceTemplateID: device.DeviceTemplateID,
|
|
||||||
DeviceTemplateName: deviceTemplateName,
|
|
||||||
AreaControllerID: device.AreaControllerID,
|
|
||||||
AreaControllerName: areaControllerName,
|
|
||||||
Location: device.Location,
|
|
||||||
Properties: props,
|
|
||||||
CreatedAt: device.CreatedAt.Format(time.RFC3339),
|
|
||||||
UpdatedAt: device.UpdatedAt.Format(time.RFC3339),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newListDeviceResponse 从数据库模型切片创建一个新的设备列表响应 DTO 切片
|
|
||||||
func newListDeviceResponse(devices []*models.Device) ([]*DeviceResponse, error) {
|
|
||||||
list := make([]*DeviceResponse, 0, len(devices))
|
|
||||||
for _, device := range devices {
|
|
||||||
resp, err := newDeviceResponse(device)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
list = append(list, resp)
|
|
||||||
}
|
|
||||||
return list, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newAreaControllerResponse 从数据库模型创建一个新的区域主控响应 DTO
|
|
||||||
func newAreaControllerResponse(ac *models.AreaController) (*AreaControllerResponse, error) {
|
|
||||||
if ac == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var props map[string]interface{}
|
|
||||||
if len(ac.Properties) > 0 && string(ac.Properties) != "null" {
|
|
||||||
if err := json.Unmarshal(ac.Properties, &props); err != nil {
|
|
||||||
return nil, fmt.Errorf("解析区域主控属性失败 (ID: %d): %w", ac.ID, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &AreaControllerResponse{
|
|
||||||
ID: ac.ID,
|
|
||||||
Name: ac.Name,
|
|
||||||
NetworkID: ac.NetworkID,
|
|
||||||
Location: ac.Location,
|
|
||||||
Status: ac.Status,
|
|
||||||
Properties: props,
|
|
||||||
CreatedAt: ac.CreatedAt.Format(time.RFC3339),
|
|
||||||
UpdatedAt: ac.UpdatedAt.Format(time.RFC3339),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newListAreaControllerResponse 从数据库模型切片创建一个新的区域主控列表响应 DTO 切片
|
|
||||||
func newListAreaControllerResponse(acs []*models.AreaController) ([]*AreaControllerResponse, error) {
|
|
||||||
list := make([]*AreaControllerResponse, 0, len(acs))
|
|
||||||
for _, ac := range acs {
|
|
||||||
resp, err := newAreaControllerResponse(ac)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
list = append(list, resp)
|
|
||||||
}
|
|
||||||
return list, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newDeviceTemplateResponse 从数据库模型创建一个新的设备模板响应 DTO
|
|
||||||
func newDeviceTemplateResponse(dt *models.DeviceTemplate) (*DeviceTemplateResponse, error) {
|
|
||||||
if dt == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var commands map[string]interface{}
|
|
||||||
if err := dt.ParseCommands(&commands); err != nil {
|
|
||||||
return nil, fmt.Errorf("解析设备模板命令失败 (ID: %d): %w", dt.ID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var values []models.ValueDescriptor
|
|
||||||
if dt.Category == models.CategorySensor {
|
|
||||||
if err := dt.ParseValues(&values); err != nil {
|
|
||||||
return nil, fmt.Errorf("解析设备模板值描述符失败 (ID: %d): %w", dt.ID, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &DeviceTemplateResponse{
|
|
||||||
ID: dt.ID,
|
|
||||||
Name: dt.Name,
|
|
||||||
Manufacturer: dt.Manufacturer,
|
|
||||||
Description: dt.Description,
|
|
||||||
Category: dt.Category,
|
|
||||||
Commands: commands,
|
|
||||||
Values: values,
|
|
||||||
CreatedAt: dt.CreatedAt.Format(time.RFC3339),
|
|
||||||
UpdatedAt: dt.UpdatedAt.Format(time.RFC3339),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newListDeviceTemplateResponse 从数据库模型切片创建一个新的设备模板列表响应 DTO 切片
|
|
||||||
func newListDeviceTemplateResponse(dts []*models.DeviceTemplate) ([]*DeviceTemplateResponse, error) {
|
|
||||||
list := make([]*DeviceTemplateResponse, 0, len(dts))
|
|
||||||
for _, dt := range dts {
|
|
||||||
resp, err := newDeviceTemplateResponse(dt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
list = append(list, resp)
|
|
||||||
}
|
|
||||||
return list, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Controller Methods: Devices ---
|
// --- Controller Methods: Devices ---
|
||||||
|
|
||||||
// CreateDevice godoc
|
// CreateDevice godoc
|
||||||
@@ -279,12 +46,12 @@ func newListDeviceTemplateResponse(dts []*models.DeviceTemplate) ([]*DeviceTempl
|
|||||||
// @Tags 设备管理
|
// @Tags 设备管理
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param device body CreateDeviceRequest true "设备信息"
|
// @Param device body dto.CreateDeviceRequest true "设备信息"
|
||||||
// @Success 200 {object} controller.Response{data=DeviceResponse}
|
// @Success 200 {object} controller.Response{data=dto.DeviceResponse}
|
||||||
// @Router /api/v1/devices [post]
|
// @Router /api/v1/devices [post]
|
||||||
func (c *Controller) CreateDevice(ctx *gin.Context) {
|
func (c *Controller) CreateDevice(ctx *gin.Context) {
|
||||||
const actionType = "创建设备"
|
const actionType = "创建设备"
|
||||||
var req CreateDeviceRequest
|
var req dto.CreateDeviceRequest
|
||||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||||
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
|
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
|
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
|
||||||
@@ -325,9 +92,9 @@ func (c *Controller) CreateDevice(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := newDeviceResponse(createdDevice)
|
resp, err := dto.NewDeviceResponse(createdDevice)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Errorf("%s: 序列化响应失败: %v", actionType, err)
|
c.logger.Errorf("%s: 序列化响应失败: %v, Device: %+v", actionType, err, createdDevice)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "设备创建成功,但响应生成失败", actionType, "响应序列化失败", createdDevice)
|
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "设备创建成功,但响应生成失败", actionType, "响应序列化失败", createdDevice)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -342,7 +109,7 @@ func (c *Controller) CreateDevice(ctx *gin.Context) {
|
|||||||
// @Tags 设备管理
|
// @Tags 设备管理
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path string true "设备ID"
|
// @Param id path string true "设备ID"
|
||||||
// @Success 200 {object} controller.Response{data=DeviceResponse}
|
// @Success 200 {object} controller.Response{data=dto.DeviceResponse}
|
||||||
// @Router /api/v1/devices/{id} [get]
|
// @Router /api/v1/devices/{id} [get]
|
||||||
func (c *Controller) GetDevice(ctx *gin.Context) {
|
func (c *Controller) GetDevice(ctx *gin.Context) {
|
||||||
const actionType = "获取设备"
|
const actionType = "获取设备"
|
||||||
@@ -371,7 +138,7 @@ func (c *Controller) GetDevice(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := newDeviceResponse(device)
|
resp, err := dto.NewDeviceResponse(device)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Errorf("%s: 序列化响应失败: %v, Device: %+v", actionType, err, device)
|
c.logger.Errorf("%s: 序列化响应失败: %v, Device: %+v", actionType, err, device)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备信息失败: 内部数据格式错误", actionType, "响应序列化失败", device)
|
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备信息失败: 内部数据格式错误", actionType, "响应序列化失败", device)
|
||||||
@@ -387,7 +154,7 @@ func (c *Controller) GetDevice(ctx *gin.Context) {
|
|||||||
// @Description 获取系统中所有设备的列表
|
// @Description 获取系统中所有设备的列表
|
||||||
// @Tags 设备管理
|
// @Tags 设备管理
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} controller.Response{data=[]DeviceResponse}
|
// @Success 200 {object} controller.Response{data=[]dto.DeviceResponse}
|
||||||
// @Router /api/v1/devices [get]
|
// @Router /api/v1/devices [get]
|
||||||
func (c *Controller) ListDevices(ctx *gin.Context) {
|
func (c *Controller) ListDevices(ctx *gin.Context) {
|
||||||
const actionType = "获取设备列表"
|
const actionType = "获取设备列表"
|
||||||
@@ -398,7 +165,7 @@ func (c *Controller) ListDevices(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := newListDeviceResponse(devices)
|
resp, err := dto.NewListDeviceResponse(devices)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Errorf("%s: 序列化响应失败: %v, Devices: %+v", actionType, err, devices)
|
c.logger.Errorf("%s: 序列化响应失败: %v, Devices: %+v", actionType, err, devices)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备列表失败: 内部数据格式错误", actionType, "响应序列化失败", devices)
|
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备列表失败: 内部数据格式错误", actionType, "响应序列化失败", devices)
|
||||||
@@ -416,8 +183,8 @@ func (c *Controller) ListDevices(ctx *gin.Context) {
|
|||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path string true "设备ID"
|
// @Param id path string true "设备ID"
|
||||||
// @Param device body UpdateDeviceRequest true "要更新的设备信息"
|
// @Param device body dto.UpdateDeviceRequest true "要更新的设备信息"
|
||||||
// @Success 200 {object} controller.Response{data=DeviceResponse}
|
// @Success 200 {object} controller.Response{data=dto.DeviceResponse}
|
||||||
// @Router /api/v1/devices/{id} [put]
|
// @Router /api/v1/devices/{id} [put]
|
||||||
func (c *Controller) UpdateDevice(ctx *gin.Context) {
|
func (c *Controller) UpdateDevice(ctx *gin.Context) {
|
||||||
const actionType = "更新设备"
|
const actionType = "更新设备"
|
||||||
@@ -440,7 +207,7 @@ func (c *Controller) UpdateDevice(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var req UpdateDeviceRequest
|
var req dto.UpdateDeviceRequest
|
||||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||||
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
|
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
|
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
|
||||||
@@ -479,7 +246,7 @@ func (c *Controller) UpdateDevice(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := newDeviceResponse(updatedDevice)
|
resp, err := dto.NewDeviceResponse(updatedDevice)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Errorf("%s: 序列化响应失败: %v, Device: %+v", actionType, err, updatedDevice)
|
c.logger.Errorf("%s: 序列化响应失败: %v, Device: %+v", actionType, err, updatedDevice)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "设备更新成功,但响应生成失败", actionType, "响应序列化失败", updatedDevice)
|
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "设备更新成功,但响应生成失败", actionType, "响应序列化失败", updatedDevice)
|
||||||
@@ -539,12 +306,12 @@ func (c *Controller) DeleteDevice(ctx *gin.Context) {
|
|||||||
// @Tags 区域主控管理
|
// @Tags 区域主控管理
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param areaController body CreateAreaControllerRequest true "区域主控信息"
|
// @Param areaController body dto.CreateAreaControllerRequest true "区域主控信息"
|
||||||
// @Success 200 {object} controller.Response{data=AreaControllerResponse}
|
// @Success 200 {object} controller.Response{data=dto.AreaControllerResponse}
|
||||||
// @Router /api/v1/area-controllers [post]
|
// @Router /api/v1/area-controllers [post]
|
||||||
func (c *Controller) CreateAreaController(ctx *gin.Context) {
|
func (c *Controller) CreateAreaController(ctx *gin.Context) {
|
||||||
const actionType = "创建区域主控"
|
const actionType = "创建区域主控"
|
||||||
var req CreateAreaControllerRequest
|
var req dto.CreateAreaControllerRequest
|
||||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||||
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
|
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
|
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
|
||||||
@@ -577,7 +344,7 @@ func (c *Controller) CreateAreaController(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := newAreaControllerResponse(ac)
|
resp, err := dto.NewAreaControllerResponse(ac)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Errorf("%s: 序列化响应失败: %v", actionType, err)
|
c.logger.Errorf("%s: 序列化响应失败: %v", actionType, err)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "区域主控创建成功,但响应生成失败", actionType, "响应序列化失败", ac)
|
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "区域主控创建成功,但响应生成失败", actionType, "响应序列化失败", ac)
|
||||||
@@ -594,7 +361,7 @@ func (c *Controller) CreateAreaController(ctx *gin.Context) {
|
|||||||
// @Tags 区域主控管理
|
// @Tags 区域主控管理
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path string true "区域主控ID"
|
// @Param id path string true "区域主控ID"
|
||||||
// @Success 200 {object} controller.Response{data=AreaControllerResponse}
|
// @Success 200 {object} controller.Response{data=dto.AreaControllerResponse}
|
||||||
// @Router /api/v1/area-controllers/{id} [get]
|
// @Router /api/v1/area-controllers/{id} [get]
|
||||||
func (c *Controller) GetAreaController(ctx *gin.Context) {
|
func (c *Controller) GetAreaController(ctx *gin.Context) {
|
||||||
const actionType = "获取区域主控"
|
const actionType = "获取区域主控"
|
||||||
@@ -619,7 +386,7 @@ func (c *Controller) GetAreaController(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := newAreaControllerResponse(ac)
|
resp, err := dto.NewAreaControllerResponse(ac)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Errorf("%s: 序列化响应失败: %v, AreaController: %+v", actionType, err, ac)
|
c.logger.Errorf("%s: 序列化响应失败: %v, AreaController: %+v", actionType, err, ac)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取区域主控信息失败: 内部数据格式错误", actionType, "响应序列化失败", ac)
|
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取区域主控信息失败: 内部数据格式错误", actionType, "响应序列化失败", ac)
|
||||||
@@ -635,7 +402,7 @@ func (c *Controller) GetAreaController(ctx *gin.Context) {
|
|||||||
// @Description 获取系统中所有区域主控的列表
|
// @Description 获取系统中所有区域主控的列表
|
||||||
// @Tags 区域主控管理
|
// @Tags 区域主控管理
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} controller.Response{data=[]AreaControllerResponse}
|
// @Success 200 {object} controller.Response{data=[]dto.AreaControllerResponse}
|
||||||
// @Router /api/v1/area-controllers [get]
|
// @Router /api/v1/area-controllers [get]
|
||||||
func (c *Controller) ListAreaControllers(ctx *gin.Context) {
|
func (c *Controller) ListAreaControllers(ctx *gin.Context) {
|
||||||
const actionType = "获取区域主控列表"
|
const actionType = "获取区域主控列表"
|
||||||
@@ -646,7 +413,7 @@ func (c *Controller) ListAreaControllers(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := newListAreaControllerResponse(acs)
|
resp, err := dto.NewListAreaControllerResponse(acs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Errorf("%s: 序列化响应失败: %v, AreaControllers: %+v", actionType, err, acs)
|
c.logger.Errorf("%s: 序列化响应失败: %v, AreaControllers: %+v", actionType, err, acs)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取区域主控列表失败: 内部数据格式错误", actionType, "响应序列化失败", acs)
|
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取区域主控列表失败: 内部数据格式错误", actionType, "响应序列化失败", acs)
|
||||||
@@ -664,8 +431,8 @@ func (c *Controller) ListAreaControllers(ctx *gin.Context) {
|
|||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path string true "区域主控ID"
|
// @Param id path string true "区域主控ID"
|
||||||
// @Param areaController body UpdateAreaControllerRequest true "要更新的区域主控信息"
|
// @Param areaController body dto.UpdateAreaControllerRequest true "要更新的区域主控信息"
|
||||||
// @Success 200 {object} controller.Response{data=AreaControllerResponse}
|
// @Success 200 {object} controller.Response{data=dto.AreaControllerResponse}
|
||||||
// @Router /api/v1/area-controllers/{id} [put]
|
// @Router /api/v1/area-controllers/{id} [put]
|
||||||
func (c *Controller) UpdateAreaController(ctx *gin.Context) {
|
func (c *Controller) UpdateAreaController(ctx *gin.Context) {
|
||||||
const actionType = "更新区域主控"
|
const actionType = "更新区域主控"
|
||||||
@@ -690,7 +457,7 @@ func (c *Controller) UpdateAreaController(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var req UpdateAreaControllerRequest
|
var req dto.UpdateAreaControllerRequest
|
||||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||||
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
|
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
|
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
|
||||||
@@ -721,7 +488,7 @@ func (c *Controller) UpdateAreaController(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := newAreaControllerResponse(existingAC)
|
resp, err := dto.NewAreaControllerResponse(existingAC)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Errorf("%s: 序列化响应失败: %v, AreaController: %+v", actionType, err, existingAC)
|
c.logger.Errorf("%s: 序列化响应失败: %v, AreaController: %+v", actionType, err, existingAC)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "区域主控更新成功,但响应生成失败", actionType, "响应序列化失败", existingAC)
|
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "区域主控更新成功,但响应生成失败", actionType, "响应序列化失败", existingAC)
|
||||||
@@ -781,12 +548,12 @@ func (c *Controller) DeleteAreaController(ctx *gin.Context) {
|
|||||||
// @Tags 设备模板管理
|
// @Tags 设备模板管理
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param deviceTemplate body CreateDeviceTemplateRequest true "设备模板信息"
|
// @Param deviceTemplate body dto.CreateDeviceTemplateRequest true "设备模板信息"
|
||||||
// @Success 200 {object} controller.Response{data=DeviceTemplateResponse}
|
// @Success 200 {object} controller.Response{data=dto.DeviceTemplateResponse}
|
||||||
// @Router /api/v1/device-templates [post]
|
// @Router /api/v1/device-templates [post]
|
||||||
func (c *Controller) CreateDeviceTemplate(ctx *gin.Context) {
|
func (c *Controller) CreateDeviceTemplate(ctx *gin.Context) {
|
||||||
const actionType = "创建设备模板"
|
const actionType = "创建设备模板"
|
||||||
var req CreateDeviceTemplateRequest
|
var req dto.CreateDeviceTemplateRequest
|
||||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||||
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
|
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
|
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
|
||||||
@@ -828,7 +595,7 @@ func (c *Controller) CreateDeviceTemplate(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := newDeviceTemplateResponse(deviceTemplate)
|
resp, err := dto.NewDeviceTemplateResponse(deviceTemplate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Errorf("%s: 序列化响应失败: %v", actionType, err)
|
c.logger.Errorf("%s: 序列化响应失败: %v", actionType, err)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "设备模板创建成功,但响应生成失败", actionType, "响应序列化失败", deviceTemplate)
|
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "设备模板创建成功,但响应生成失败", actionType, "响应序列化失败", deviceTemplate)
|
||||||
@@ -845,7 +612,7 @@ func (c *Controller) CreateDeviceTemplate(ctx *gin.Context) {
|
|||||||
// @Tags 设备模板管理
|
// @Tags 设备模板管理
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path string true "设备模板ID"
|
// @Param id path string true "设备模板ID"
|
||||||
// @Success 200 {object} controller.Response{data=DeviceTemplateResponse}
|
// @Success 200 {object} controller.Response{data=dto.DeviceTemplateResponse}
|
||||||
// @Router /api/v1/device-templates/{id} [get]
|
// @Router /api/v1/device-templates/{id} [get]
|
||||||
func (c *Controller) GetDeviceTemplate(ctx *gin.Context) {
|
func (c *Controller) GetDeviceTemplate(ctx *gin.Context) {
|
||||||
const actionType = "获取设备模板"
|
const actionType = "获取设备模板"
|
||||||
@@ -870,7 +637,7 @@ func (c *Controller) GetDeviceTemplate(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := newDeviceTemplateResponse(deviceTemplate)
|
resp, err := dto.NewDeviceTemplateResponse(deviceTemplate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Errorf("%s: 序列化响应失败: %v, DeviceTemplate: %+v", actionType, err, deviceTemplate)
|
c.logger.Errorf("%s: 序列化响应失败: %v, DeviceTemplate: %+v", actionType, err, deviceTemplate)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备模板信息失败: 内部数据格式错误", actionType, "响应序列化失败", deviceTemplate)
|
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备模板信息失败: 内部数据格式错误", actionType, "响应序列化失败", deviceTemplate)
|
||||||
@@ -886,7 +653,7 @@ func (c *Controller) GetDeviceTemplate(ctx *gin.Context) {
|
|||||||
// @Description 获取系统中所有设备模板的列表
|
// @Description 获取系统中所有设备模板的列表
|
||||||
// @Tags 设备模板管理
|
// @Tags 设备模板管理
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} controller.Response{data=[]DeviceTemplateResponse}
|
// @Success 200 {object} controller.Response{data=[]dto.DeviceTemplateResponse}
|
||||||
// @Router /api/v1/device-templates [get]
|
// @Router /api/v1/device-templates [get]
|
||||||
func (c *Controller) ListDeviceTemplates(ctx *gin.Context) {
|
func (c *Controller) ListDeviceTemplates(ctx *gin.Context) {
|
||||||
const actionType = "获取设备模板列表"
|
const actionType = "获取设备模板列表"
|
||||||
@@ -897,7 +664,7 @@ func (c *Controller) ListDeviceTemplates(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := newListDeviceTemplateResponse(deviceTemplates)
|
resp, err := dto.NewListDeviceTemplateResponse(deviceTemplates)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Errorf("%s: 序列化响应失败: %v, DeviceTemplates: %+v", actionType, err, deviceTemplates)
|
c.logger.Errorf("%s: 序列化响应失败: %v, DeviceTemplates: %+v", actionType, err, deviceTemplates)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备模板列表失败: 内部数据格式错误", actionType, "响应序列化失败", deviceTemplates)
|
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备模板列表失败: 内部数据格式错误", actionType, "响应序列化失败", deviceTemplates)
|
||||||
@@ -915,8 +682,8 @@ func (c *Controller) ListDeviceTemplates(ctx *gin.Context) {
|
|||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path string true "设备模板ID"
|
// @Param id path string true "设备模板ID"
|
||||||
// @Param deviceTemplate body UpdateDeviceTemplateRequest true "要更新的设备模板信息"
|
// @Param deviceTemplate body dto.UpdateDeviceTemplateRequest true "要更新的设备模板信息"
|
||||||
// @Success 200 {object} controller.Response{data=DeviceTemplateResponse}
|
// @Success 200 {object} controller.Response{data=dto.DeviceTemplateResponse}
|
||||||
// @Router /api/v1/device-templates/{id} [put]
|
// @Router /api/v1/device-templates/{id} [put]
|
||||||
func (c *Controller) UpdateDeviceTemplate(ctx *gin.Context) {
|
func (c *Controller) UpdateDeviceTemplate(ctx *gin.Context) {
|
||||||
const actionType = "更新设备模板"
|
const actionType = "更新设备模板"
|
||||||
@@ -941,7 +708,7 @@ func (c *Controller) UpdateDeviceTemplate(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var req UpdateDeviceTemplateRequest
|
var req dto.UpdateDeviceTemplateRequest
|
||||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||||
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
|
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
|
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
|
||||||
@@ -981,7 +748,7 @@ func (c *Controller) UpdateDeviceTemplate(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := newDeviceTemplateResponse(existingDeviceTemplate)
|
resp, err := dto.NewDeviceTemplateResponse(existingDeviceTemplate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Errorf("%s: 序列化响应失败: %v, DeviceTemplate: %+v", actionType, err, existingDeviceTemplate)
|
c.logger.Errorf("%s: 序列化响应失败: %v, DeviceTemplate: %+v", actionType, err, existingDeviceTemplate)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "设备模板更新成功,但响应生成失败", actionType, "响应序列化失败", existingDeviceTemplate)
|
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "设备模板更新成功,但响应生成失败", actionType, "响应序列化失败", existingDeviceTemplate)
|
||||||
|
|||||||
@@ -5,60 +5,13 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller"
|
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller"
|
||||||
|
"git.huangwc.com/pig/pig-farm-controller/internal/app/dto"
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/app/service"
|
"git.huangwc.com/pig/pig-farm-controller/internal/app/service"
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// --- 数据传输对象 (DTOs) ---
|
|
||||||
|
|
||||||
// PigHouseResponse 定义了猪舍信息的响应结构
|
|
||||||
type PigHouseResponse struct {
|
|
||||||
ID uint `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// PenResponse 定义了猪栏信息的响应结构
|
|
||||||
type PenResponse struct {
|
|
||||||
ID uint `json:"id"`
|
|
||||||
PenNumber string `json:"pen_number"`
|
|
||||||
HouseID uint `json:"house_id"`
|
|
||||||
Capacity int `json:"capacity"`
|
|
||||||
Status models.PenStatus `json:"status"`
|
|
||||||
PigBatchID uint `json:"pig_batch_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreatePigHouseRequest 定义了创建猪舍的请求结构
|
|
||||||
type CreatePigHouseRequest struct {
|
|
||||||
Name string `json:"name" binding:"required"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdatePigHouseRequest 定义了更新猪舍的请求结构
|
|
||||||
type UpdatePigHouseRequest struct {
|
|
||||||
Name string `json:"name" binding:"required"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreatePenRequest 定义了创建猪栏的请求结构
|
|
||||||
type CreatePenRequest struct {
|
|
||||||
PenNumber string `json:"pen_number" binding:"required"`
|
|
||||||
HouseID uint `json:"house_id" binding:"required"`
|
|
||||||
Capacity int `json:"capacity" binding:"required"`
|
|
||||||
Status models.PenStatus `json:"status" binding:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdatePenRequest 定义了更新猪栏的请求结构
|
|
||||||
type UpdatePenRequest struct {
|
|
||||||
PenNumber string `json:"pen_number" binding:"required"`
|
|
||||||
HouseID uint `json:"house_id" binding:"required"`
|
|
||||||
Capacity int `json:"capacity" binding:"required"`
|
|
||||||
Status models.PenStatus `json:"status" binding:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- 控制器定义 ---
|
// --- 控制器定义 ---
|
||||||
|
|
||||||
// PigFarmController 负责处理猪舍和猪栏相关的API请求
|
// PigFarmController 负责处理猪舍和猪栏相关的API请求
|
||||||
@@ -83,12 +36,12 @@ func NewPigFarmController(logger *logs.Logger, service service.PigFarmService) *
|
|||||||
// @Tags 猪场管理
|
// @Tags 猪场管理
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param body body CreatePigHouseRequest true "猪舍信息"
|
// @Param body body dto.CreatePigHouseRequest true "猪舍信息"
|
||||||
// @Success 201 {object} controller.Response{data=PigHouseResponse} "创建成功"
|
// @Success 201 {object} controller.Response{data=dto.PigHouseResponse} "创建成功"
|
||||||
// @Router /api/v1/pighouses [post]
|
// @Router /api/v1/pighouses [post]
|
||||||
func (c *PigFarmController) CreatePigHouse(ctx *gin.Context) {
|
func (c *PigFarmController) CreatePigHouse(ctx *gin.Context) {
|
||||||
const action = "创建猪舍"
|
const action = "创建猪舍"
|
||||||
var req CreatePigHouseRequest
|
var req dto.CreatePigHouseRequest
|
||||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", req)
|
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", req)
|
||||||
return
|
return
|
||||||
@@ -101,7 +54,7 @@ func (c *PigFarmController) CreatePigHouse(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := PigHouseResponse{
|
resp := dto.PigHouseResponse{
|
||||||
ID: house.ID,
|
ID: house.ID,
|
||||||
Name: house.Name,
|
Name: house.Name,
|
||||||
Description: house.Description,
|
Description: house.Description,
|
||||||
@@ -115,7 +68,7 @@ func (c *PigFarmController) CreatePigHouse(ctx *gin.Context) {
|
|||||||
// @Tags 猪场管理
|
// @Tags 猪场管理
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path int true "猪舍ID"
|
// @Param id path int true "猪舍ID"
|
||||||
// @Success 200 {object} controller.Response{data=PigHouseResponse} "获取成功"
|
// @Success 200 {object} controller.Response{data=dto.PigHouseResponse} "获取成功"
|
||||||
// @Router /api/v1/pighouses/{id} [get]
|
// @Router /api/v1/pighouses/{id} [get]
|
||||||
func (c *PigFarmController) GetPigHouse(ctx *gin.Context) {
|
func (c *PigFarmController) GetPigHouse(ctx *gin.Context) {
|
||||||
const action = "获取猪舍"
|
const action = "获取猪舍"
|
||||||
@@ -136,7 +89,7 @@ func (c *PigFarmController) GetPigHouse(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := PigHouseResponse{
|
resp := dto.PigHouseResponse{
|
||||||
ID: house.ID,
|
ID: house.ID,
|
||||||
Name: house.Name,
|
Name: house.Name,
|
||||||
Description: house.Description,
|
Description: house.Description,
|
||||||
@@ -149,7 +102,7 @@ func (c *PigFarmController) GetPigHouse(ctx *gin.Context) {
|
|||||||
// @Description 获取所有猪舍的列表
|
// @Description 获取所有猪舍的列表
|
||||||
// @Tags 猪场管理
|
// @Tags 猪场管理
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} controller.Response{data=[]PigHouseResponse} "获取成功"
|
// @Success 200 {object} controller.Response{data=[]dto.PigHouseResponse} "获取成功"
|
||||||
// @Router /api/v1/pighouses [get]
|
// @Router /api/v1/pighouses [get]
|
||||||
func (c *PigFarmController) ListPigHouses(ctx *gin.Context) {
|
func (c *PigFarmController) ListPigHouses(ctx *gin.Context) {
|
||||||
const action = "获取猪舍列表"
|
const action = "获取猪舍列表"
|
||||||
@@ -160,9 +113,9 @@ func (c *PigFarmController) ListPigHouses(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp []PigHouseResponse
|
var resp []dto.PigHouseResponse
|
||||||
for _, house := range houses {
|
for _, house := range houses {
|
||||||
resp = append(resp, PigHouseResponse{
|
resp = append(resp, dto.PigHouseResponse{
|
||||||
ID: house.ID,
|
ID: house.ID,
|
||||||
Name: house.Name,
|
Name: house.Name,
|
||||||
Description: house.Description,
|
Description: house.Description,
|
||||||
@@ -179,8 +132,8 @@ func (c *PigFarmController) ListPigHouses(ctx *gin.Context) {
|
|||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path int true "猪舍ID"
|
// @Param id path int true "猪舍ID"
|
||||||
// @Param body body UpdatePigHouseRequest true "猪舍信息"
|
// @Param body body dto.UpdatePigHouseRequest true "猪舍信息"
|
||||||
// @Success 200 {object} controller.Response{data=PigHouseResponse} "更新成功"
|
// @Success 200 {object} controller.Response{data=dto.PigHouseResponse} "更新成功"
|
||||||
// @Router /api/v1/pighouses/{id} [put]
|
// @Router /api/v1/pighouses/{id} [put]
|
||||||
func (c *PigFarmController) UpdatePigHouse(ctx *gin.Context) {
|
func (c *PigFarmController) UpdatePigHouse(ctx *gin.Context) {
|
||||||
const action = "更新猪舍"
|
const action = "更新猪舍"
|
||||||
@@ -190,7 +143,7 @@ func (c *PigFarmController) UpdatePigHouse(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var req UpdatePigHouseRequest
|
var req dto.UpdatePigHouseRequest
|
||||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", req)
|
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", req)
|
||||||
return
|
return
|
||||||
@@ -207,7 +160,7 @@ func (c *PigFarmController) UpdatePigHouse(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := PigHouseResponse{
|
resp := dto.PigHouseResponse{
|
||||||
ID: house.ID,
|
ID: house.ID,
|
||||||
Name: house.Name,
|
Name: house.Name,
|
||||||
Description: house.Description,
|
Description: house.Description,
|
||||||
@@ -252,12 +205,12 @@ func (c *PigFarmController) DeletePigHouse(ctx *gin.Context) {
|
|||||||
// @Tags 猪场管理
|
// @Tags 猪场管理
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param body body CreatePenRequest true "猪栏信息"
|
// @Param body body dto.CreatePenRequest true "猪栏信息"
|
||||||
// @Success 201 {object} controller.Response{data=PenResponse} "创建成功"
|
// @Success 201 {object} controller.Response{data=dto.PenResponse} "创建成功"
|
||||||
// @Router /api/v1/pens [post]
|
// @Router /api/v1/pens [post]
|
||||||
func (c *PigFarmController) CreatePen(ctx *gin.Context) {
|
func (c *PigFarmController) CreatePen(ctx *gin.Context) {
|
||||||
const action = "创建猪栏"
|
const action = "创建猪栏"
|
||||||
var req CreatePenRequest
|
var req dto.CreatePenRequest
|
||||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", req)
|
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", req)
|
||||||
return
|
return
|
||||||
@@ -270,7 +223,7 @@ func (c *PigFarmController) CreatePen(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := PenResponse{
|
resp := dto.PenResponse{
|
||||||
ID: pen.ID,
|
ID: pen.ID,
|
||||||
PenNumber: pen.PenNumber,
|
PenNumber: pen.PenNumber,
|
||||||
HouseID: pen.HouseID,
|
HouseID: pen.HouseID,
|
||||||
@@ -287,7 +240,7 @@ func (c *PigFarmController) CreatePen(ctx *gin.Context) {
|
|||||||
// @Tags 猪场管理
|
// @Tags 猪场管理
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path int true "猪栏ID"
|
// @Param id path int true "猪栏ID"
|
||||||
// @Success 200 {object} controller.Response{data=PenResponse} "获取成功"
|
// @Success 200 {object} controller.Response{data=dto.PenResponse} "获取成功"
|
||||||
// @Router /api/v1/pens/{id} [get]
|
// @Router /api/v1/pens/{id} [get]
|
||||||
func (c *PigFarmController) GetPen(ctx *gin.Context) {
|
func (c *PigFarmController) GetPen(ctx *gin.Context) {
|
||||||
const action = "获取猪栏"
|
const action = "获取猪栏"
|
||||||
@@ -308,7 +261,7 @@ func (c *PigFarmController) GetPen(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := PenResponse{
|
resp := dto.PenResponse{
|
||||||
ID: pen.ID,
|
ID: pen.ID,
|
||||||
PenNumber: pen.PenNumber,
|
PenNumber: pen.PenNumber,
|
||||||
HouseID: pen.HouseID,
|
HouseID: pen.HouseID,
|
||||||
@@ -324,7 +277,7 @@ func (c *PigFarmController) GetPen(ctx *gin.Context) {
|
|||||||
// @Description 获取所有猪栏的列表
|
// @Description 获取所有猪栏的列表
|
||||||
// @Tags 猪场管理
|
// @Tags 猪场管理
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} controller.Response{data=[]PenResponse} "获取成功"
|
// @Success 200 {object} controller.Response{data=[]dto.PenResponse} "获取成功"
|
||||||
// @Router /api/v1/pens [get]
|
// @Router /api/v1/pens [get]
|
||||||
func (c *PigFarmController) ListPens(ctx *gin.Context) {
|
func (c *PigFarmController) ListPens(ctx *gin.Context) {
|
||||||
const action = "获取猪栏列表"
|
const action = "获取猪栏列表"
|
||||||
@@ -335,9 +288,9 @@ func (c *PigFarmController) ListPens(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp []PenResponse
|
var resp []dto.PenResponse
|
||||||
for _, pen := range pens {
|
for _, pen := range pens {
|
||||||
resp = append(resp, PenResponse{
|
resp = append(resp, dto.PenResponse{
|
||||||
ID: pen.ID,
|
ID: pen.ID,
|
||||||
PenNumber: pen.PenNumber,
|
PenNumber: pen.PenNumber,
|
||||||
HouseID: pen.HouseID,
|
HouseID: pen.HouseID,
|
||||||
@@ -357,8 +310,8 @@ func (c *PigFarmController) ListPens(ctx *gin.Context) {
|
|||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path int true "猪栏ID"
|
// @Param id path int true "猪栏ID"
|
||||||
// @Param body body UpdatePenRequest true "猪栏信息"
|
// @Param body body dto.UpdatePenRequest true "猪栏信息"
|
||||||
// @Success 200 {object} controller.Response{data=PenResponse} "更新成功"
|
// @Success 200 {object} controller.Response{data=dto.PenResponse} "更新成功"
|
||||||
// @Router /api/v1/pens/{id} [put]
|
// @Router /api/v1/pens/{id} [put]
|
||||||
func (c *PigFarmController) UpdatePen(ctx *gin.Context) {
|
func (c *PigFarmController) UpdatePen(ctx *gin.Context) {
|
||||||
const action = "更新猪栏"
|
const action = "更新猪栏"
|
||||||
@@ -368,7 +321,7 @@ func (c *PigFarmController) UpdatePen(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var req UpdatePenRequest
|
var req dto.UpdatePenRequest
|
||||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", req)
|
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", req)
|
||||||
return
|
return
|
||||||
@@ -385,7 +338,7 @@ func (c *PigFarmController) UpdatePen(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := PenResponse{
|
resp := dto.PenResponse{
|
||||||
ID: pen.ID,
|
ID: pen.ID,
|
||||||
PenNumber: pen.PenNumber,
|
PenNumber: pen.PenNumber,
|
||||||
HouseID: pen.HouseID,
|
HouseID: pen.HouseID,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller"
|
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller"
|
||||||
|
"git.huangwc.com/pig/pig-farm-controller/internal/app/dto"
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/domain/task"
|
"git.huangwc.com/pig/pig-farm-controller/internal/domain/task"
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
||||||
@@ -13,80 +14,6 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// --- 请求和响应 DTO 定义 ---
|
|
||||||
|
|
||||||
// CreatePlanRequest 定义创建计划请求的结构体
|
|
||||||
type CreatePlanRequest struct {
|
|
||||||
Name string `json:"name" binding:"required" example:"猪舍温度控制计划"`
|
|
||||||
Description string `json:"description" example:"根据温度自动调节风扇和加热器"`
|
|
||||||
ExecutionType models.PlanExecutionType `json:"execution_type" binding:"required" example:"自动"`
|
|
||||||
ExecuteNum uint `json:"execute_num,omitempty" example:"10"`
|
|
||||||
CronExpression string `json:"cron_expression" example:"0 0 6 * * *"`
|
|
||||||
SubPlanIDs []uint `json:"sub_plan_ids,omitempty"`
|
|
||||||
Tasks []TaskRequest `json:"tasks,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// PlanResponse 定义计划详情响应的结构体
|
|
||||||
type PlanResponse struct {
|
|
||||||
ID uint `json:"id" example:"1"`
|
|
||||||
Name string `json:"name" example:"猪舍温度控制计划"`
|
|
||||||
Description string `json:"description" example:"根据温度自动调节风扇和加热器"`
|
|
||||||
ExecutionType models.PlanExecutionType `json:"execution_type" example:"自动"`
|
|
||||||
Status models.PlanStatus `json:"status" example:"已启用"`
|
|
||||||
ExecuteNum uint `json:"execute_num" example:"10"`
|
|
||||||
ExecuteCount uint `json:"execute_count" example:"0"`
|
|
||||||
CronExpression string `json:"cron_expression" example:"0 0 6 * * *"`
|
|
||||||
ContentType models.PlanContentType `json:"content_type" example:"任务"`
|
|
||||||
SubPlans []SubPlanResponse `json:"sub_plans,omitempty"`
|
|
||||||
Tasks []TaskResponse `json:"tasks,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListPlansResponse 定义获取计划列表响应的结构体
|
|
||||||
type ListPlansResponse struct {
|
|
||||||
Plans []PlanResponse `json:"plans"`
|
|
||||||
Total int `json:"total" example:"100"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdatePlanRequest 定义更新计划请求的结构体
|
|
||||||
type UpdatePlanRequest struct {
|
|
||||||
Name string `json:"name" example:"猪舍温度控制计划V2"`
|
|
||||||
Description string `json:"description" example:"更新后的描述"`
|
|
||||||
ExecutionType models.PlanExecutionType `json:"execution_type" example:"自动"`
|
|
||||||
ExecuteNum uint `json:"execute_num,omitempty" example:"10"`
|
|
||||||
CronExpression string `json:"cron_expression" example:"0 0 6 * * *"`
|
|
||||||
SubPlanIDs []uint `json:"sub_plan_ids,omitempty"`
|
|
||||||
Tasks []TaskRequest `json:"tasks,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// SubPlanResponse 定义子计划响应结构体
|
|
||||||
type SubPlanResponse struct {
|
|
||||||
ID uint `json:"id" example:"1"`
|
|
||||||
ParentPlanID uint `json:"parent_plan_id" example:"1"`
|
|
||||||
ChildPlanID uint `json:"child_plan_id" example:"2"`
|
|
||||||
ExecutionOrder int `json:"execution_order" example:"1"`
|
|
||||||
ChildPlan *PlanResponse `json:"child_plan,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TaskRequest 定义任务请求结构体
|
|
||||||
type TaskRequest struct {
|
|
||||||
Name string `json:"name" example:"打开风扇"`
|
|
||||||
Description string `json:"description" example:"打开1号风扇"`
|
|
||||||
ExecutionOrder int `json:"execution_order" example:"1"`
|
|
||||||
Type models.TaskType `json:"type" example:"等待"`
|
|
||||||
Parameters map[string]interface{} `json:"parameters,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TaskResponse 定义任务响应结构体
|
|
||||||
type TaskResponse struct {
|
|
||||||
ID int `json:"id" example:"1"`
|
|
||||||
PlanID uint `json:"plan_id" example:"1"`
|
|
||||||
Name string `json:"name" example:"打开风扇"`
|
|
||||||
Description string `json:"description" example:"打开1号风扇"`
|
|
||||||
ExecutionOrder int `json:"execution_order" example:"1"`
|
|
||||||
Type models.TaskType `json:"type" example:"等待"`
|
|
||||||
Parameters map[string]interface{} `json:"parameters,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Controller 定义 ---
|
// --- Controller 定义 ---
|
||||||
|
|
||||||
// Controller 定义了计划相关的控制器
|
// Controller 定义了计划相关的控制器
|
||||||
@@ -113,11 +40,11 @@ func NewController(logger *logs.Logger, planRepo repository.PlanRepository, anal
|
|||||||
// @Tags 计划管理
|
// @Tags 计划管理
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param plan body CreatePlanRequest true "计划信息"
|
// @Param plan body dto.CreatePlanRequest true "计划信息"
|
||||||
// @Success 200 {object} controller.Response{data=plan.PlanResponse} "业务码为201代表创建成功"
|
// @Success 200 {object} controller.Response{data=dto.PlanResponse} "业务码为201代表创建成功"
|
||||||
// @Router /api/v1/plans [post]
|
// @Router /api/v1/plans [post]
|
||||||
func (c *Controller) CreatePlan(ctx *gin.Context) {
|
func (c *Controller) CreatePlan(ctx *gin.Context) {
|
||||||
var req CreatePlanRequest
|
var req dto.CreatePlanRequest
|
||||||
const actionType = "创建计划"
|
const actionType = "创建计划"
|
||||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||||
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
|
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
|
||||||
@@ -126,7 +53,7 @@ func (c *Controller) CreatePlan(ctx *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 使用已有的转换函数,它已经包含了验证和重排逻辑
|
// 使用已有的转换函数,它已经包含了验证和重排逻辑
|
||||||
planToCreate, err := PlanFromCreateRequest(&req)
|
planToCreate, err := dto.NewPlanFromCreateRequest(&req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Errorf("%s: 计划数据校验失败: %v", actionType, err)
|
c.logger.Errorf("%s: 计划数据校验失败: %v", actionType, err)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "计划数据校验失败: "+err.Error(), actionType, "计划数据校验失败", req)
|
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "计划数据校验失败: "+err.Error(), actionType, "计划数据校验失败", req)
|
||||||
@@ -155,7 +82,7 @@ func (c *Controller) CreatePlan(ctx *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 使用已有的转换函数将创建后的模型转换为响应对象
|
// 使用已有的转换函数将创建后的模型转换为响应对象
|
||||||
resp, err := PlanToResponse(planToCreate)
|
resp, err := dto.NewPlanToResponse(planToCreate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Errorf("%s: 序列化响应失败: %v", actionType, err)
|
c.logger.Errorf("%s: 序列化响应失败: %v", actionType, err)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "计划创建成功,但响应生成失败", actionType, "响应序列化失败", planToCreate)
|
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "计划创建成功,但响应生成失败", actionType, "响应序列化失败", planToCreate)
|
||||||
@@ -173,7 +100,7 @@ func (c *Controller) CreatePlan(ctx *gin.Context) {
|
|||||||
// @Tags 计划管理
|
// @Tags 计划管理
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path int true "计划ID"
|
// @Param id path int true "计划ID"
|
||||||
// @Success 200 {object} controller.Response{data=plan.PlanResponse} "业务码为200代表成功获取"
|
// @Success 200 {object} controller.Response{data=dto.PlanResponse} "业务码为200代表成功获取"
|
||||||
// @Router /api/v1/plans/{id} [get]
|
// @Router /api/v1/plans/{id} [get]
|
||||||
func (c *Controller) GetPlan(ctx *gin.Context) {
|
func (c *Controller) GetPlan(ctx *gin.Context) {
|
||||||
const actionType = "获取计划详情"
|
const actionType = "获取计划详情"
|
||||||
@@ -202,7 +129,7 @@ func (c *Controller) GetPlan(ctx *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3. 将模型转换为响应 DTO
|
// 3. 将模型转换为响应 DTO
|
||||||
resp, err := PlanToResponse(plan)
|
resp, err := dto.NewPlanToResponse(plan)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Errorf("%s: 序列化响应失败: %v, Plan: %+v", actionType, err, plan)
|
c.logger.Errorf("%s: 序列化响应失败: %v, Plan: %+v", actionType, err, plan)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划详情失败: 内部数据格式错误", actionType, "响应序列化失败", plan)
|
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划详情失败: 内部数据格式错误", actionType, "响应序列化失败", plan)
|
||||||
@@ -219,7 +146,7 @@ func (c *Controller) GetPlan(ctx *gin.Context) {
|
|||||||
// @Description 获取所有计划的列表
|
// @Description 获取所有计划的列表
|
||||||
// @Tags 计划管理
|
// @Tags 计划管理
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} controller.Response{data=plan.ListPlansResponse} "业务码为200代表成功获取列表"
|
// @Success 200 {object} controller.Response{data=[]dto.PlanResponse} "业务码为200代表成功获取列表"
|
||||||
// @Router /api/v1/plans [get]
|
// @Router /api/v1/plans [get]
|
||||||
func (c *Controller) ListPlans(ctx *gin.Context) {
|
func (c *Controller) ListPlans(ctx *gin.Context) {
|
||||||
const actionType = "获取计划列表"
|
const actionType = "获取计划列表"
|
||||||
@@ -232,9 +159,9 @@ func (c *Controller) ListPlans(ctx *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. 将模型转换为响应 DTO
|
// 2. 将模型转换为响应 DTO
|
||||||
planResponses := make([]PlanResponse, 0, len(plans))
|
planResponses := make([]dto.PlanResponse, 0, len(plans))
|
||||||
for _, p := range plans {
|
for _, p := range plans {
|
||||||
resp, err := PlanToResponse(&p)
|
resp, err := dto.NewPlanToResponse(&p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Errorf("%s: 序列化响应失败: %v, Plan: %+v", actionType, err, p)
|
c.logger.Errorf("%s: 序列化响应失败: %v, Plan: %+v", actionType, err, p)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划列表失败: 内部数据格式错误", actionType, "响应序列化失败", p)
|
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划列表失败: 内部数据格式错误", actionType, "响应序列化失败", p)
|
||||||
@@ -244,7 +171,7 @@ func (c *Controller) ListPlans(ctx *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3. 构造并发送成功响应
|
// 3. 构造并发送成功响应
|
||||||
resp := ListPlansResponse{
|
resp := dto.ListPlansResponse{
|
||||||
Plans: planResponses,
|
Plans: planResponses,
|
||||||
Total: len(planResponses),
|
Total: len(planResponses),
|
||||||
}
|
}
|
||||||
@@ -259,8 +186,8 @@ func (c *Controller) ListPlans(ctx *gin.Context) {
|
|||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path int true "计划ID"
|
// @Param id path int true "计划ID"
|
||||||
// @Param plan body UpdatePlanRequest true "更新后的计划信息"
|
// @Param plan body dto.UpdatePlanRequest true "更新后的计划信息"
|
||||||
// @Success 200 {object} controller.Response{data=plan.PlanResponse} "业务码为200代表更新成功"
|
// @Success 200 {object} controller.Response{data=dto.PlanResponse} "业务码为200代表更新成功"
|
||||||
// @Router /api/v1/plans/{id} [put]
|
// @Router /api/v1/plans/{id} [put]
|
||||||
func (c *Controller) UpdatePlan(ctx *gin.Context) {
|
func (c *Controller) UpdatePlan(ctx *gin.Context) {
|
||||||
const actionType = "更新计划"
|
const actionType = "更新计划"
|
||||||
@@ -274,7 +201,7 @@ func (c *Controller) UpdatePlan(ctx *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. 绑定请求体
|
// 2. 绑定请求体
|
||||||
var req UpdatePlanRequest
|
var req dto.UpdatePlanRequest
|
||||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||||
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
|
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
|
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
|
||||||
@@ -282,7 +209,7 @@ func (c *Controller) UpdatePlan(ctx *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3. 将请求转换为模型(转换函数带校验)
|
// 3. 将请求转换为模型(转换函数带校验)
|
||||||
planToUpdate, err := PlanFromUpdateRequest(&req)
|
planToUpdate, err := dto.NewPlanFromUpdateRequest(&req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Errorf("%s: 计划数据校验失败: %v", actionType, err)
|
c.logger.Errorf("%s: 计划数据校验失败: %v", actionType, err)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "计划数据校验失败: "+err.Error(), actionType, "计划数据校验失败", req)
|
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "计划数据校验失败: "+err.Error(), actionType, "计划数据校验失败", req)
|
||||||
@@ -306,8 +233,8 @@ func (c *Controller) UpdatePlan(ctx *gin.Context) {
|
|||||||
controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "计划不存在", actionType, "计划不存在", id)
|
controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "计划不存在", actionType, "计划不存在", id)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.logger.Errorf("%s: 获取计划详情失败: %v, ID: %d", actionType, err, id)
|
c.logger.Errorf("%s: 获取计划信息失败: %v, ID: %d", actionType, err, id)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划详情时发生内部错误", actionType, "数据库查询失败", id)
|
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划信息时发生内部错误", actionType, "数据库查询失败", id)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,7 +264,7 @@ func (c *Controller) UpdatePlan(ctx *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 7. 将模型转换为响应 DTO
|
// 7. 将模型转换为响应 DTO
|
||||||
resp, err := PlanToResponse(updatedPlan)
|
resp, err := dto.NewPlanToResponse(updatedPlan)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Errorf("%s: 序列化响应失败: %v, Updated Plan: %+v", actionType, err, updatedPlan)
|
c.logger.Errorf("%s: 序列化响应失败: %v, Updated Plan: %+v", actionType, err, updatedPlan)
|
||||||
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "计划更新成功,但响应生成失败", actionType, "响应序列化失败", updatedPlan)
|
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "计划更新成功,但响应生成失败", actionType, "响应序列化失败", updatedPlan)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller"
|
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller"
|
||||||
|
"git.huangwc.com/pig/pig-farm-controller/internal/app/dto"
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/domain/token"
|
"git.huangwc.com/pig/pig-farm-controller/internal/domain/token"
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
||||||
@@ -31,50 +32,6 @@ func NewController(userRepo repository.UserRepository, auditRepo repository.User
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- DTOs ---
|
|
||||||
|
|
||||||
// CreateUserRequest 定义创建用户请求的结构体
|
|
||||||
type CreateUserRequest struct {
|
|
||||||
Username string `json:"username" binding:"required" example:"newuser"`
|
|
||||||
Password string `json:"password" binding:"required" example:"password123"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoginRequest 定义登录请求的结构体
|
|
||||||
type LoginRequest struct {
|
|
||||||
// Identifier 可以是用户名、邮箱、手机号、微信号或飞书账号
|
|
||||||
Identifier string `json:"identifier" binding:"required" example:"testuser"`
|
|
||||||
Password string `json:"password" binding:"required" example:"password123"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateUserResponse 定义创建用户成功响应的结构体
|
|
||||||
type CreateUserResponse struct {
|
|
||||||
Username string `json:"username" example:"newuser"`
|
|
||||||
ID uint `json:"id" example:"1"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoginResponse 定义登录成功响应的结构体
|
|
||||||
type LoginResponse struct {
|
|
||||||
Username string `json:"username" example:"testuser"`
|
|
||||||
ID uint `json:"id" example:"1"`
|
|
||||||
Token string `json:"token" example:"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."`
|
|
||||||
}
|
|
||||||
|
|
||||||
// HistoryResponse 定义单条操作历史的响应结构体
|
|
||||||
type HistoryResponse struct {
|
|
||||||
UserID uint `json:"user_id" example:"101"`
|
|
||||||
Username string `json:"username" example:"testuser"`
|
|
||||||
ActionType string `json:"action_type" example:"更新设备"`
|
|
||||||
Description string `json:"description" example:"设备更新成功"`
|
|
||||||
TargetResource interface{} `json:"target_resource"`
|
|
||||||
Time string `json:"time"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListHistoryResponse 定义操作历史列表的响应结构体
|
|
||||||
type ListHistoryResponse struct {
|
|
||||||
History []HistoryResponse `json:"history"`
|
|
||||||
Total int64 `json:"total" example:"100"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Controller Methods ---
|
// --- Controller Methods ---
|
||||||
|
|
||||||
// CreateUser godoc
|
// CreateUser godoc
|
||||||
@@ -83,11 +40,11 @@ type ListHistoryResponse struct {
|
|||||||
// @Tags 用户管理
|
// @Tags 用户管理
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param user body CreateUserRequest true "用户信息"
|
// @Param user body dto.CreateUserRequest true "用户信息"
|
||||||
// @Success 200 {object} controller.Response{data=user.CreateUserResponse} "业务码为201代表创建成功"
|
// @Success 200 {object} controller.Response{data=dto.CreateUserResponse} "业务码为201代表创建成功"
|
||||||
// @Router /api/v1/users [post]
|
// @Router /api/v1/users [post]
|
||||||
func (c *Controller) CreateUser(ctx *gin.Context) {
|
func (c *Controller) CreateUser(ctx *gin.Context) {
|
||||||
var req CreateUserRequest
|
var req dto.CreateUserRequest
|
||||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||||
c.logger.Errorf("创建用户: 参数绑定失败: %v", err)
|
c.logger.Errorf("创建用户: 参数绑定失败: %v", err)
|
||||||
controller.SendErrorResponse(ctx, controller.CodeBadRequest, err.Error())
|
controller.SendErrorResponse(ctx, controller.CodeBadRequest, err.Error())
|
||||||
@@ -114,7 +71,7 @@ func (c *Controller) CreateUser(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
controller.SendResponse(ctx, controller.CodeCreated, "用户创建成功", CreateUserResponse{
|
controller.SendResponse(ctx, controller.CodeCreated, "用户创建成功", dto.CreateUserResponse{
|
||||||
Username: user.Username,
|
Username: user.Username,
|
||||||
ID: user.ID,
|
ID: user.ID,
|
||||||
})
|
})
|
||||||
@@ -126,11 +83,11 @@ func (c *Controller) CreateUser(ctx *gin.Context) {
|
|||||||
// @Tags 用户管理
|
// @Tags 用户管理
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param credentials body LoginRequest true "登录凭证"
|
// @Param credentials body dto.LoginRequest true "登录凭证"
|
||||||
// @Success 200 {object} controller.Response{data=user.LoginResponse} "业务码为200代表登录成功"
|
// @Success 200 {object} controller.Response{data=dto.LoginResponse} "业务码为200代表登录成功"
|
||||||
// @Router /api/v1/users/login [post]
|
// @Router /api/v1/users/login [post]
|
||||||
func (c *Controller) Login(ctx *gin.Context) {
|
func (c *Controller) Login(ctx *gin.Context) {
|
||||||
var req LoginRequest
|
var req dto.LoginRequest
|
||||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||||
c.logger.Errorf("登录: 参数绑定失败: %v", err)
|
c.logger.Errorf("登录: 参数绑定失败: %v", err)
|
||||||
controller.SendErrorResponse(ctx, controller.CodeBadRequest, err.Error())
|
controller.SendErrorResponse(ctx, controller.CodeBadRequest, err.Error())
|
||||||
@@ -162,7 +119,7 @@ func (c *Controller) Login(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
controller.SendResponse(ctx, controller.CodeSuccess, "登录成功", LoginResponse{
|
controller.SendResponse(ctx, controller.CodeSuccess, "登录成功", dto.LoginResponse{
|
||||||
Username: user.Username,
|
Username: user.Username,
|
||||||
ID: user.ID,
|
ID: user.ID,
|
||||||
Token: tokenString,
|
Token: tokenString,
|
||||||
@@ -178,7 +135,7 @@ func (c *Controller) Login(ctx *gin.Context) {
|
|||||||
// @Param page query int false "页码" default(1)
|
// @Param page query int false "页码" default(1)
|
||||||
// @Param page_size query int false "每页大小" default(10)
|
// @Param page_size query int false "每页大小" default(10)
|
||||||
// @Param action_type query string false "按操作类型过滤"
|
// @Param action_type query string false "按操作类型过滤"
|
||||||
// @Success 200 {object} controller.Response{data=user.ListHistoryResponse} "业务码为200代表成功获取"
|
// @Success 200 {object} controller.Response{data=dto.ListHistoryResponse} "业务码为200代表成功获取"
|
||||||
// @Router /api/v1/users/{id}/history [get]
|
// @Router /api/v1/users/{id}/history [get]
|
||||||
func (c *Controller) ListUserHistory(ctx *gin.Context) {
|
func (c *Controller) ListUserHistory(ctx *gin.Context) {
|
||||||
const actionType = "获取用户操作历史"
|
const actionType = "获取用户操作历史"
|
||||||
@@ -221,9 +178,9 @@ func (c *Controller) ListUserHistory(ctx *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4. 将数据库模型转换为响应 DTO
|
// 4. 将数据库模型转换为响应 DTO
|
||||||
historyResponses := make([]HistoryResponse, 0, len(logs))
|
historyResponses := make([]dto.HistoryResponse, 0, len(logs))
|
||||||
for _, log := range logs {
|
for _, log := range logs {
|
||||||
historyResponses = append(historyResponses, HistoryResponse{
|
historyResponses = append(historyResponses, dto.HistoryResponse{
|
||||||
UserID: log.UserID,
|
UserID: log.UserID,
|
||||||
Username: log.Username,
|
Username: log.Username,
|
||||||
ActionType: log.ActionType,
|
ActionType: log.ActionType,
|
||||||
@@ -234,7 +191,7 @@ func (c *Controller) ListUserHistory(ctx *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 5. 发送成功响应
|
// 5. 发送成功响应
|
||||||
resp := ListHistoryResponse{
|
resp := dto.ListHistoryResponse{
|
||||||
History: historyResponses,
|
History: historyResponses,
|
||||||
Total: total,
|
Total: total,
|
||||||
}
|
}
|
||||||
|
|||||||
142
internal/app/dto/device_converter.go
Normal file
142
internal/app/dto/device_converter.go
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewDeviceResponse 从数据库模型创建一个新的设备响应 DTO
|
||||||
|
func NewDeviceResponse(device *models.Device) (*DeviceResponse, error) {
|
||||||
|
if device == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var props map[string]interface{}
|
||||||
|
if len(device.Properties) > 0 && string(device.Properties) != "null" {
|
||||||
|
if err := device.ParseProperties(&props); err != nil {
|
||||||
|
return nil, fmt.Errorf("解析设备属性失败 (ID: %d): %w", device.ID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保 DeviceTemplate 和 AreaController 已预加载
|
||||||
|
deviceTemplateName := ""
|
||||||
|
if device.DeviceTemplate.ID != 0 {
|
||||||
|
deviceTemplateName = device.DeviceTemplate.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
areaControllerName := ""
|
||||||
|
if device.AreaController.ID != 0 {
|
||||||
|
areaControllerName = device.AreaController.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DeviceResponse{
|
||||||
|
ID: device.ID,
|
||||||
|
Name: device.Name,
|
||||||
|
DeviceTemplateID: device.DeviceTemplateID,
|
||||||
|
DeviceTemplateName: deviceTemplateName,
|
||||||
|
AreaControllerID: device.AreaControllerID,
|
||||||
|
AreaControllerName: areaControllerName,
|
||||||
|
Location: device.Location,
|
||||||
|
Properties: props,
|
||||||
|
CreatedAt: device.CreatedAt.Format(time.RFC3339),
|
||||||
|
UpdatedAt: device.UpdatedAt.Format(time.RFC3339),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewListDeviceResponse 从数据库模型切片创建一个新的设备列表响应 DTO 切片
|
||||||
|
func NewListDeviceResponse(devices []*models.Device) ([]*DeviceResponse, error) {
|
||||||
|
list := make([]*DeviceResponse, 0, len(devices))
|
||||||
|
for _, device := range devices {
|
||||||
|
resp, err := NewDeviceResponse(device)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
list = append(list, resp)
|
||||||
|
}
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAreaControllerResponse 从数据库模型创建一个新的区域主控响应 DTO
|
||||||
|
func NewAreaControllerResponse(ac *models.AreaController) (*AreaControllerResponse, error) {
|
||||||
|
if ac == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var props map[string]interface{}
|
||||||
|
if len(ac.Properties) > 0 && string(ac.Properties) != "null" {
|
||||||
|
if err := json.Unmarshal(ac.Properties, &props); err != nil {
|
||||||
|
return nil, fmt.Errorf("解析区域主控属性失败 (ID: %d): %w", ac.ID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &AreaControllerResponse{
|
||||||
|
ID: ac.ID,
|
||||||
|
Name: ac.Name,
|
||||||
|
NetworkID: ac.NetworkID,
|
||||||
|
Location: ac.Location,
|
||||||
|
Status: ac.Status,
|
||||||
|
Properties: props,
|
||||||
|
CreatedAt: ac.CreatedAt.Format(time.RFC3339),
|
||||||
|
UpdatedAt: ac.UpdatedAt.Format(time.RFC3339),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewListAreaControllerResponse 从数据库模型切片创建一个新的区域主控列表响应 DTO 切片
|
||||||
|
func NewListAreaControllerResponse(acs []*models.AreaController) ([]*AreaControllerResponse, error) {
|
||||||
|
list := make([]*AreaControllerResponse, 0, len(acs))
|
||||||
|
for _, ac := range acs {
|
||||||
|
resp, err := NewAreaControllerResponse(ac)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
list = append(list, resp)
|
||||||
|
}
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDeviceTemplateResponse 从数据库模型创建一个新的设备模板响应 DTO
|
||||||
|
func NewDeviceTemplateResponse(dt *models.DeviceTemplate) (*DeviceTemplateResponse, error) {
|
||||||
|
if dt == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var commands map[string]interface{}
|
||||||
|
if err := dt.ParseCommands(&commands); err != nil {
|
||||||
|
return nil, fmt.Errorf("解析设备模板命令失败 (ID: %d): %w", dt.ID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var values []models.ValueDescriptor
|
||||||
|
if dt.Category == models.CategorySensor {
|
||||||
|
if err := dt.ParseValues(&values); err != nil {
|
||||||
|
return nil, fmt.Errorf("解析设备模板值描述符失败 (ID: %d): %w", dt.ID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DeviceTemplateResponse{
|
||||||
|
ID: dt.ID,
|
||||||
|
Name: dt.Name,
|
||||||
|
Manufacturer: dt.Manufacturer,
|
||||||
|
Description: dt.Description,
|
||||||
|
Category: dt.Category,
|
||||||
|
Commands: commands,
|
||||||
|
Values: values,
|
||||||
|
CreatedAt: dt.CreatedAt.Format(time.RFC3339),
|
||||||
|
UpdatedAt: dt.UpdatedAt.Format(time.RFC3339),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewListDeviceTemplateResponse 从数据库模型切片创建一个新的设备模板列表响应 DTO 切片
|
||||||
|
func NewListDeviceTemplateResponse(dts []*models.DeviceTemplate) ([]*DeviceTemplateResponse, error) {
|
||||||
|
list := make([]*DeviceTemplateResponse, 0, len(dts))
|
||||||
|
for _, dt := range dts {
|
||||||
|
resp, err := NewDeviceTemplateResponse(dt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
list = append(list, resp)
|
||||||
|
}
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
96
internal/app/dto/device_dto.go
Normal file
96
internal/app/dto/device_dto.go
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
import "git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
||||||
|
|
||||||
|
// CreateDeviceRequest 定义了创建设备时需要传入的参数
|
||||||
|
type CreateDeviceRequest struct {
|
||||||
|
Name string `json:"name" binding:"required"`
|
||||||
|
DeviceTemplateID uint `json:"device_template_id" binding:"required"`
|
||||||
|
AreaControllerID uint `json:"area_controller_id" binding:"required"`
|
||||||
|
Location string `json:"location,omitempty"`
|
||||||
|
Properties map[string]interface{} `json:"properties,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateDeviceRequest 定义了更新设备时需要传入的参数
|
||||||
|
type UpdateDeviceRequest struct {
|
||||||
|
Name string `json:"name" binding:"required"`
|
||||||
|
DeviceTemplateID uint `json:"device_template_id" binding:"required"`
|
||||||
|
AreaControllerID uint `json:"area_controller_id" binding:"required"`
|
||||||
|
Location string `json:"location,omitempty"`
|
||||||
|
Properties map[string]interface{} `json:"properties,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateAreaControllerRequest 定义了创建区域主控时需要传入的参数
|
||||||
|
type CreateAreaControllerRequest struct {
|
||||||
|
Name string `json:"name" binding:"required"`
|
||||||
|
NetworkID string `json:"network_id" binding:"required"`
|
||||||
|
Location string `json:"location,omitempty"`
|
||||||
|
Properties map[string]interface{} `json:"properties,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateAreaControllerRequest 定义了更新区域主控时需要传入的参数
|
||||||
|
type UpdateAreaControllerRequest struct {
|
||||||
|
Name string `json:"name" binding:"required"`
|
||||||
|
NetworkID string `json:"network_id" binding:"required"`
|
||||||
|
Location string `json:"location,omitempty"`
|
||||||
|
Properties map[string]interface{} `json:"properties,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateDeviceTemplateRequest 定义了创建设备模板时需要传入的参数
|
||||||
|
type CreateDeviceTemplateRequest struct {
|
||||||
|
Name string `json:"name" binding:"required"`
|
||||||
|
Manufacturer string `json:"manufacturer,omitempty"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
Category models.DeviceCategory `json:"category" binding:"required"`
|
||||||
|
Commands map[string]interface{} `json:"commands" binding:"required"`
|
||||||
|
Values []models.ValueDescriptor `json:"values,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateDeviceTemplateRequest 定义了更新设备模板时需要传入的参数
|
||||||
|
type UpdateDeviceTemplateRequest struct {
|
||||||
|
Name string `json:"name" binding:"required"`
|
||||||
|
Manufacturer string `json:"manufacturer,omitempty"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
Category models.DeviceCategory `json:"category" binding:"required"`
|
||||||
|
Commands map[string]interface{} `json:"commands" binding:"required"`
|
||||||
|
Values []models.ValueDescriptor `json:"values,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeviceResponse 定义了返回给客户端的单个设备信息的结构
|
||||||
|
type DeviceResponse struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
DeviceTemplateID uint `json:"device_template_id"`
|
||||||
|
DeviceTemplateName string `json:"device_template_name"`
|
||||||
|
AreaControllerID uint `json:"area_controller_id"`
|
||||||
|
AreaControllerName string `json:"area_controller_name"`
|
||||||
|
Location string `json:"location"`
|
||||||
|
Properties map[string]interface{} `json:"properties"`
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
UpdatedAt string `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AreaControllerResponse 定义了返回给客户端的单个区域主控信息的结构
|
||||||
|
type AreaControllerResponse struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
NetworkID string `json:"network_id"`
|
||||||
|
Location string `json:"location"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Properties map[string]interface{} `json:"properties"`
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
UpdatedAt string `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeviceTemplateResponse 定义了返回给客户端的单个设备模板信息的结构
|
||||||
|
type DeviceTemplateResponse struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Manufacturer string `json:"manufacturer"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Category models.DeviceCategory `json:"category"`
|
||||||
|
Commands map[string]interface{} `json:"commands"`
|
||||||
|
Values []models.ValueDescriptor `json:"values"`
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
UpdatedAt string `json:"updated_at"`
|
||||||
|
}
|
||||||
48
internal/app/dto/pig_farm_dto.go
Normal file
48
internal/app/dto/pig_farm_dto.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
import "git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
||||||
|
|
||||||
|
// PigHouseResponse 定义了猪舍信息的响应结构
|
||||||
|
type PigHouseResponse struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PenResponse 定义了猪栏信息的响应结构
|
||||||
|
type PenResponse struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
PenNumber string `json:"pen_number"`
|
||||||
|
HouseID uint `json:"house_id"`
|
||||||
|
Capacity int `json:"capacity"`
|
||||||
|
Status models.PenStatus `json:"status"`
|
||||||
|
PigBatchID uint `json:"pig_batch_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatePigHouseRequest 定义了创建猪舍的请求结构
|
||||||
|
type CreatePigHouseRequest struct {
|
||||||
|
Name string `json:"name" binding:"required"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatePigHouseRequest 定义了更新猪舍的请求结构
|
||||||
|
type UpdatePigHouseRequest struct {
|
||||||
|
Name string `json:"name" binding:"required"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatePenRequest 定义了创建猪栏的请求结构
|
||||||
|
type CreatePenRequest struct {
|
||||||
|
PenNumber string `json:"pen_number" binding:"required"`
|
||||||
|
HouseID uint `json:"house_id" binding:"required"`
|
||||||
|
Capacity int `json:"capacity" binding:"required"`
|
||||||
|
Status models.PenStatus `json:"status" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatePenRequest 定义了更新猪栏的请求结构
|
||||||
|
type UpdatePenRequest struct {
|
||||||
|
PenNumber string `json:"pen_number" binding:"required"`
|
||||||
|
HouseID uint `json:"house_id" binding:"required"`
|
||||||
|
Capacity int `json:"capacity" binding:"required"`
|
||||||
|
Status models.PenStatus `json:"status" binding:"required"`
|
||||||
|
}
|
||||||
145
internal/app/dto/plan_converter.go
Normal file
145
internal/app/dto/plan_converter.go
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewPlanFromCreateRequest 将 CreatePlanRequest 转换为 models.Plan
|
||||||
|
func NewPlanFromCreateRequest(req *CreatePlanRequest) (*models.Plan, error) {
|
||||||
|
plan := &models.Plan{
|
||||||
|
Name: req.Name,
|
||||||
|
Description: req.Description,
|
||||||
|
ExecutionType: req.ExecutionType,
|
||||||
|
ExecuteNum: req.ExecuteNum,
|
||||||
|
CronExpression: req.CronExpression,
|
||||||
|
Status: models.PlanStatusDisabled, // 默认创建时为禁用状态
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(req.SubPlanIDs) > 0 {
|
||||||
|
plan.ContentType = models.PlanContentTypeSubPlans
|
||||||
|
for _, subPlanID := range req.SubPlanIDs {
|
||||||
|
plan.SubPlans = append(plan.SubPlans, models.SubPlan{
|
||||||
|
ChildPlanID: subPlanID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if len(req.Tasks) > 0 {
|
||||||
|
plan.ContentType = models.PlanContentTypeTasks
|
||||||
|
for _, taskReq := range req.Tasks {
|
||||||
|
parametersJSON, err := json.Marshal(taskReq.Parameters)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("序列化任务参数失败: %w", err)
|
||||||
|
}
|
||||||
|
plan.Tasks = append(plan.Tasks, models.Task{
|
||||||
|
Name: taskReq.Name,
|
||||||
|
Description: taskReq.Description,
|
||||||
|
ExecutionOrder: taskReq.ExecutionOrder,
|
||||||
|
Type: taskReq.Type,
|
||||||
|
Parameters: parametersJSON,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("计划必须包含子计划或任务")
|
||||||
|
}
|
||||||
|
|
||||||
|
return plan, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPlanFromUpdateRequest 将 UpdatePlanRequest 转换为 models.Plan
|
||||||
|
func NewPlanFromUpdateRequest(req *UpdatePlanRequest) (*models.Plan, error) {
|
||||||
|
plan := &models.Plan{
|
||||||
|
Name: req.Name,
|
||||||
|
Description: req.Description,
|
||||||
|
ExecutionType: req.ExecutionType,
|
||||||
|
ExecuteNum: req.ExecuteNum,
|
||||||
|
CronExpression: req.CronExpression,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(req.SubPlanIDs) > 0 {
|
||||||
|
plan.ContentType = models.PlanContentTypeSubPlans
|
||||||
|
for _, subPlanID := range req.SubPlanIDs {
|
||||||
|
plan.SubPlans = append(plan.SubPlans, models.SubPlan{
|
||||||
|
ChildPlanID: subPlanID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if len(req.Tasks) > 0 {
|
||||||
|
plan.ContentType = models.PlanContentTypeTasks
|
||||||
|
for _, taskReq := range req.Tasks {
|
||||||
|
parametersJSON, err := json.Marshal(taskReq.Parameters)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("序列化任务参数失败: %w", err)
|
||||||
|
}
|
||||||
|
plan.Tasks = append(plan.Tasks, models.Task{
|
||||||
|
Name: taskReq.Name,
|
||||||
|
Description: taskReq.Description,
|
||||||
|
ExecutionOrder: taskReq.ExecutionOrder,
|
||||||
|
Type: taskReq.Type,
|
||||||
|
Parameters: parametersJSON,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("计划必须包含子计划或任务")
|
||||||
|
}
|
||||||
|
|
||||||
|
return plan, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPlanToResponse 将 models.Plan 转换为 PlanResponse
|
||||||
|
func NewPlanToResponse(plan *models.Plan) (*PlanResponse, error) {
|
||||||
|
if plan == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := &PlanResponse{
|
||||||
|
ID: plan.ID,
|
||||||
|
Name: plan.Name,
|
||||||
|
Description: plan.Description,
|
||||||
|
ExecutionType: plan.ExecutionType,
|
||||||
|
Status: plan.Status,
|
||||||
|
ExecuteNum: plan.ExecuteNum,
|
||||||
|
ExecuteCount: plan.ExecuteCount,
|
||||||
|
CronExpression: plan.CronExpression,
|
||||||
|
ContentType: plan.ContentType,
|
||||||
|
}
|
||||||
|
|
||||||
|
if plan.ContentType == models.PlanContentTypeSubPlans && len(plan.SubPlans) > 0 {
|
||||||
|
resp.SubPlans = make([]SubPlanResponse, 0, len(plan.SubPlans))
|
||||||
|
for _, sp := range plan.SubPlans {
|
||||||
|
childPlanResp, err := NewPlanToResponse(sp.ChildPlan) // 递归调用
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp.SubPlans = append(resp.SubPlans, SubPlanResponse{
|
||||||
|
ID: sp.ID,
|
||||||
|
ParentPlanID: sp.ParentPlanID,
|
||||||
|
ChildPlanID: sp.ChildPlanID,
|
||||||
|
ExecutionOrder: sp.ExecutionOrder,
|
||||||
|
ChildPlan: childPlanResp,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if plan.ContentType == models.PlanContentTypeTasks && len(plan.Tasks) > 0 {
|
||||||
|
resp.Tasks = make([]TaskResponse, 0, len(plan.Tasks))
|
||||||
|
for _, task := range plan.Tasks {
|
||||||
|
var parameters map[string]interface{}
|
||||||
|
if len(task.Parameters) > 0 && string(task.Parameters) != "null" {
|
||||||
|
if err := json.Unmarshal(task.Parameters, ¶meters); err != nil {
|
||||||
|
return nil, fmt.Errorf("解析任务参数失败 (ID: %d): %w", task.ID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resp.Tasks = append(resp.Tasks, TaskResponse{
|
||||||
|
ID: task.ID,
|
||||||
|
PlanID: task.PlanID,
|
||||||
|
Name: task.Name,
|
||||||
|
Description: task.Description,
|
||||||
|
ExecutionOrder: task.ExecutionOrder,
|
||||||
|
Type: task.Type,
|
||||||
|
Parameters: parameters,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
75
internal/app/dto/plan_dto.go
Normal file
75
internal/app/dto/plan_dto.go
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
import "git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
||||||
|
|
||||||
|
// CreatePlanRequest 定义创建计划请求的结构体
|
||||||
|
type CreatePlanRequest struct {
|
||||||
|
Name string `json:"name" binding:"required" example:"猪舍温度控制计划"`
|
||||||
|
Description string `json:"description" example:"根据温度自动调节风扇和加热器"`
|
||||||
|
ExecutionType models.PlanExecutionType `json:"execution_type" binding:"required" example:"自动"`
|
||||||
|
ExecuteNum uint `json:"execute_num,omitempty" example:"10"`
|
||||||
|
CronExpression string `json:"cron_expression" example:"0 0 6 * * *"`
|
||||||
|
SubPlanIDs []uint `json:"sub_plan_ids,omitempty"`
|
||||||
|
Tasks []TaskRequest `json:"tasks,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PlanResponse 定义计划详情响应的结构体
|
||||||
|
type PlanResponse struct {
|
||||||
|
ID uint `json:"id" example:"1"`
|
||||||
|
Name string `json:"name" example:"猪舍温度控制计划"`
|
||||||
|
Description string `json:"description" example:"根据温度自动调节风扇和加热器"`
|
||||||
|
ExecutionType models.PlanExecutionType `json:"execution_type" example:"自动"`
|
||||||
|
Status models.PlanStatus `json:"status" example:"已启用"`
|
||||||
|
ExecuteNum uint `json:"execute_num" example:"10"`
|
||||||
|
ExecuteCount uint `json:"execute_count" example:"0"`
|
||||||
|
CronExpression string `json:"cron_expression" example:"0 0 6 * * *"`
|
||||||
|
ContentType models.PlanContentType `json:"content_type" example:"任务"`
|
||||||
|
SubPlans []SubPlanResponse `json:"sub_plans,omitempty"`
|
||||||
|
Tasks []TaskResponse `json:"tasks,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPlansResponse 定义获取计划列表响应的结构体
|
||||||
|
type ListPlansResponse struct {
|
||||||
|
Plans []PlanResponse `json:"plans"`
|
||||||
|
Total int `json:"total" example:"100"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatePlanRequest 定义更新计划请求的结构体
|
||||||
|
type UpdatePlanRequest struct {
|
||||||
|
Name string `json:"name" example:"猪舍温度控制计划V2"`
|
||||||
|
Description string `json:"description" example:"更新后的描述"`
|
||||||
|
ExecutionType models.PlanExecutionType `json:"execution_type" binding:"required" example:"自动"`
|
||||||
|
ExecuteNum uint `json:"execute_num,omitempty" example:"10"`
|
||||||
|
CronExpression string `json:"cron_expression" example:"0 0 6 * * *"`
|
||||||
|
SubPlanIDs []uint `json:"sub_plan_ids,omitempty"`
|
||||||
|
Tasks []TaskRequest `json:"tasks,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubPlanResponse 定义子计划响应结构体
|
||||||
|
type SubPlanResponse struct {
|
||||||
|
ID uint `json:"id" example:"1"`
|
||||||
|
ParentPlanID uint `json:"parent_plan_id" example:"1"`
|
||||||
|
ChildPlanID uint `json:"child_plan_id" example:"2"`
|
||||||
|
ExecutionOrder int `json:"execution_order" example:"1"`
|
||||||
|
ChildPlan *PlanResponse `json:"child_plan,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaskRequest 定义任务请求结构体
|
||||||
|
type TaskRequest struct {
|
||||||
|
Name string `json:"name" example:"打开风扇"`
|
||||||
|
Description string `json:"description" example:"打开1号风扇"`
|
||||||
|
ExecutionOrder int `json:"execution_order" example:"1"`
|
||||||
|
Type models.TaskType `json:"type" example:"等待"`
|
||||||
|
Parameters map[string]interface{} `json:"parameters,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaskResponse 定义任务响应结构体
|
||||||
|
type TaskResponse struct {
|
||||||
|
ID int `json:"id" example:"1"`
|
||||||
|
PlanID uint `json:"plan_id" example:"1"`
|
||||||
|
Name string `json:"name" example:"打开风扇"`
|
||||||
|
Description string `json:"description" example:"打开1号风扇"`
|
||||||
|
ExecutionOrder int `json:"execution_order" example:"1"`
|
||||||
|
Type models.TaskType `json:"type" example:"等待"`
|
||||||
|
Parameters map[string]interface{} `json:"parameters,omitempty"`
|
||||||
|
}
|
||||||
43
internal/app/dto/user_dto.go
Normal file
43
internal/app/dto/user_dto.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
// CreateUserRequest 定义创建用户请求的结构体
|
||||||
|
type CreateUserRequest struct {
|
||||||
|
Username string `json:"username" binding:"required" example:"newuser"`
|
||||||
|
Password string `json:"password" binding:"required" example:"password123"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginRequest 定义登录请求的结构体
|
||||||
|
type LoginRequest struct {
|
||||||
|
// Identifier 可以是用户名、邮箱、手机号、微信号或飞书账号
|
||||||
|
Identifier string `json:"identifier" binding:"required" example:"testuser"`
|
||||||
|
Password string `json:"password" binding:"required" example:"password123"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateUserResponse 定义创建用户成功响应的结构体
|
||||||
|
type CreateUserResponse struct {
|
||||||
|
Username string `json:"username" example:"newuser"`
|
||||||
|
ID uint `json:"id" example:"1"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginResponse 定义登录成功响应的结构体
|
||||||
|
type LoginResponse struct {
|
||||||
|
Username string `json:"username" example:"testuser"`
|
||||||
|
ID uint `json:"id" example:"1"`
|
||||||
|
Token string `json:"token" example:"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HistoryResponse 定义单条操作历史的响应结构体
|
||||||
|
type HistoryResponse struct {
|
||||||
|
UserID uint `json:"user_id" example:"101"`
|
||||||
|
Username string `json:"username" example:"testuser"`
|
||||||
|
ActionType string `json:"action_type" example:"更新设备"`
|
||||||
|
Description string `json:"description" example:"设备更新成功"`
|
||||||
|
TargetResource interface{} `json:"target_resource"`
|
||||||
|
Time string `json:"time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListHistoryResponse 定义操作历史列表的响应结构体
|
||||||
|
type ListHistoryResponse struct {
|
||||||
|
History []HistoryResponse `json:"history"`
|
||||||
|
Total int64 `json:"total" example:"100"`
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user