重构dto

This commit is contained in:
2025-10-03 23:02:43 +08:00
parent fadc1e2535
commit d273932693
10 changed files with 647 additions and 494 deletions

View 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
}

View 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"`
}

View 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"`
}

View 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, &parameters); 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
}

View 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"`
}

View 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"`
}