Files
pig-farm-controller/internal/app/controller/plan/plan_controller.go
2025-10-31 16:28:26 +08:00

284 lines
13 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package plan
import (
"errors"
"strconv"
"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/infra/logs"
"github.com/labstack/echo/v4"
)
// --- 控制器定义 ---
// Controller 定义了计划相关的控制器
type Controller struct {
logger *logs.Logger
planService service.PlanService
}
// NewController 创建一个新的 Controller 实例
func NewController(logger *logs.Logger, planService service.PlanService) *Controller {
return &Controller{
logger: logger,
planService: planService,
}
}
// --- 接口方法实现 ---
// CreatePlan godoc
// @Summary 创建计划
// @Description 创建一个新的计划,包括其基本信息和所有关联的子计划/任务。
// @Tags 计划管理
// @Security BearerAuth
// @Accept json
// @Produce json
// @Param plan body dto.CreatePlanRequest true "计划信息"
// @Success 200 {object} controller.Response{data=dto.PlanResponse} "业务码为201代表创建成功"
// @Router /api/v1/plans [post]
func (c *Controller) CreatePlan(ctx echo.Context) error {
var req dto.CreatePlanRequest
const actionType = "创建计划"
if err := ctx.Bind(&req); err != nil {
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
}
// 调用服务层创建计划
resp, err := c.planService.CreatePlan(&req)
if err != nil {
c.logger.Errorf("%s: 服务层创建计划失败: %v", actionType, err)
// 根据服务层返回的错误类型转换为相应的HTTP状态码
if errors.Is(err, service.ErrPlanNotFound) {
return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), actionType, "计划数据校验失败或关联计划不存在", req)
}
return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "创建计划失败: "+err.Error(), actionType, "服务层创建计划失败", req)
}
// 使用统一的成功响应函数
c.logger.Infof("%s: 计划创建成功, ID: %d", actionType, resp.ID)
return controller.SendSuccessWithAudit(ctx, controller.CodeCreated, "计划创建成功", resp, actionType, "计划创建成功", resp)
}
// GetPlan godoc
// @Summary 获取计划详情
// @Description 根据计划ID获取单个计划的详细信息。
// @Tags 计划管理
// @Security BearerAuth
// @Produce json
// @Param id path int true "计划ID"
// @Success 200 {object} controller.Response{data=dto.PlanResponse} "业务码为200代表成功获取"
// @Router /api/v1/plans/{id} [get]
func (c *Controller) GetPlan(ctx echo.Context) error {
const actionType = "获取计划详情"
// 1. 从 URL 路径中获取 ID
idStr := ctx.Param("id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
c.logger.Errorf("%s: 计划ID格式错误: %v, ID: %s", actionType, err, idStr)
return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的计划ID格式", actionType, "计划ID格式错误", idStr)
}
// 调用服务层获取计划详情
resp, err := c.planService.GetPlanByID(uint(id))
if err != nil {
c.logger.Errorf("%s: 服务层获取计划详情失败: %v, ID: %d", actionType, err, id)
if errors.Is(err, service.ErrPlanNotFound) {
return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), actionType, "计划不存在", id)
}
return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划详情失败: "+err.Error(), actionType, "服务层获取计划详情失败", id)
}
// 4. 发送成功响应
c.logger.Infof("%s: 获取计划详情成功, ID: %d", actionType, id)
return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取计划详情成功", resp, actionType, "获取计划详情成功", resp)
}
// ListPlans godoc
// @Summary 获取计划列表
// @Description 获取所有计划的列表,支持按类型过滤和分页
// @Tags 计划管理
// @Security BearerAuth
// @Produce json
// @Param query query dto.ListPlansQuery false "查询参数"
// @Success 200 {object} controller.Response{data=dto.ListPlansResponse} "业务码为200代表成功获取列表"
// @Router /api/v1/plans [get]
func (c *Controller) ListPlans(ctx echo.Context) error {
const actionType = "获取计划列表"
var query dto.ListPlansQuery
if err := ctx.Bind(&query); err != nil {
c.logger.Errorf("%s: 查询参数绑定失败: %v", actionType, err)
return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "查询参数绑定失败", query)
}
// 调用服务层获取计划列表
resp, err := c.planService.ListPlans(&query)
if err != nil {
c.logger.Errorf("%s: 服务层获取计划列表失败: %v", actionType, err)
return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划列表失败: "+err.Error(), actionType, "服务层获取计划列表失败", nil)
}
c.logger.Infof("%s: 获取计划列表成功, 数量: %d", actionType, len(resp.Plans))
return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取计划列表成功", resp, actionType, "获取计划列表成功", resp)
}
// UpdatePlan godoc
// @Summary 更新计划
// @Description 根据计划ID更新计划的详细信息。系统计划不允许修改。
// @Tags 计划管理
// @Security BearerAuth
// @Accept json
// @Produce json
// @Param id path int true "计划ID"
// @Param plan body dto.UpdatePlanRequest true "更新后的计划信息"
// @Success 200 {object} controller.Response{data=dto.PlanResponse} "业务码为200代表更新成功"
// @Router /api/v1/plans/{id} [put]
func (c *Controller) UpdatePlan(ctx echo.Context) error {
const actionType = "更新计划"
// 1. 从 URL 路径中获取 ID
idStr := ctx.Param("id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
c.logger.Errorf("%s: 计划ID格式错误: %v, ID: %s", actionType, err, idStr)
return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的计划ID格式", actionType, "计划ID格式错误", idStr)
}
// 2. 绑定请求体
var req dto.UpdatePlanRequest
if err := ctx.Bind(&req); err != nil {
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
}
// 调用服务层更新计划
resp, err := c.planService.UpdatePlan(uint(id), &req)
if err != nil {
c.logger.Errorf("%s: 服务层更新计划失败: %v, ID: %d", actionType, err, id)
if errors.Is(err, service.ErrPlanNotFound) {
return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), actionType, "计划不存在", id)
} else if errors.Is(err, service.ErrPlanCannotBeModified) {
return controller.SendErrorWithAudit(ctx, controller.CodeForbidden, err.Error(), actionType, "系统计划不允许修改", id)
}
return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "更新计划失败: "+err.Error(), actionType, "服务层更新计划失败", req)
}
// 9. 发送成功响应
c.logger.Infof("%s: 计划更新成功, ID: %d", actionType, resp.ID)
return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "计划更新成功", resp, actionType, "计划更新成功", resp)
}
// DeletePlan godoc
// @Summary 删除计划
// @Description 根据计划ID删除计划。软删除系统计划不允许删除。
// @Tags 计划管理
// @Security BearerAuth
// @Produce json
// @Param id path int true "计划ID"
// @Success 200 {object} controller.Response "业务码为200代表删除成功"
// @Router /api/v1/plans/{id} [delete]
func (c *Controller) DeletePlan(ctx echo.Context) error {
const actionType = "删除计划"
// 1. 从 URL 路径中获取 ID
idStr := ctx.Param("id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
c.logger.Errorf("%s: 计划ID格式错误: %v, ID: %s", actionType, err, idStr)
return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的计划ID格式", actionType, "计划ID格式错误", idStr)
}
// 调用服务层删除计划
err = c.planService.DeletePlan(uint(id))
if err != nil {
c.logger.Errorf("%s: 服务层删除计划失败: %v, ID: %d", actionType, err, id)
if errors.Is(err, service.ErrPlanNotFound) {
return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), actionType, "计划不存在", id)
} else if errors.Is(err, service.ErrPlanCannotBeDeleted) {
return controller.SendErrorWithAudit(ctx, controller.CodeForbidden, err.Error(), actionType, "系统计划不允许删除", id)
}
return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "删除计划失败: "+err.Error(), actionType, "服务层删除计划失败", id)
}
// 6. 发送成功响应
c.logger.Infof("%s: 计划删除成功, ID: %d", actionType, id)
return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "计划删除成功", nil, actionType, "计划删除成功", id)
}
// StartPlan godoc
// @Summary 启动计划
// @Description 根据计划ID启动一个计划的执行。系统计划不允许手动启动。
// @Tags 计划管理
// @Security BearerAuth
// @Produce json
// @Param id path int true "计划ID"
// @Success 200 {object} controller.Response "业务码为200代表成功启动计划"
// @Router /api/v1/plans/{id}/start [post]
func (c *Controller) StartPlan(ctx echo.Context) error {
const actionType = "启动计划"
// 1. 从 URL 路径中获取 ID
idStr := ctx.Param("id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
c.logger.Errorf("%s: 计划ID格式错误: %v, ID: %s", actionType, err, idStr)
return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的计划ID格式", actionType, "计划ID格式错误", idStr)
}
// 调用服务层启动计划
err = c.planService.StartPlan(uint(id))
if err != nil {
c.logger.Errorf("%s: 服务层启动计划失败: %v, ID: %d", actionType, err, id)
if errors.Is(err, service.ErrPlanNotFound) {
return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), actionType, "计划不存在", id)
} else if errors.Is(err, service.ErrPlanCannotBeStarted) {
return controller.SendErrorWithAudit(ctx, controller.CodeForbidden, err.Error(), actionType, "系统计划不允许手动启动", id)
} else if errors.Is(err, service.ErrPlanAlreadyEnabled) {
return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, err.Error(), actionType, "计划已处于启动状态", id)
}
return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "启动计划失败: "+err.Error(), actionType, "服务层启动计划失败", id)
}
// 6. 发送成功响应
c.logger.Infof("%s: 计划已成功启动, ID: %d", actionType, id)
return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "计划已成功启动", nil, actionType, "计划已成功启动", id)
}
// StopPlan godoc
// @Summary 停止计划
// @Description 根据计划ID停止一个正在执行的计划。系统计划不能被停止。
// @Tags 计划管理
// @Security BearerAuth
// @Produce json
// @Param id path int true "计划ID"
// @Success 200 {object} controller.Response "业务码为200代表成功停止计划"
// @Router /api/v1/plans/{id}/stop [post]
func (c *Controller) StopPlan(ctx echo.Context) error {
const actionType = "停止计划"
// 1. 从 URL 路径中获取 ID
idStr := ctx.Param("id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
c.logger.Errorf("%s: 计划ID格式错误: %v, ID: %s", actionType, err, idStr)
return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的计划ID格式", actionType, "计划ID格式错误", idStr)
}
// 调用服务层停止计划
err = c.planService.StopPlan(uint(id))
if err != nil {
c.logger.Errorf("%s: 服务层停止计划失败: %v, ID: %d", actionType, err, id)
if errors.Is(err, service.ErrPlanNotFound) {
return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), actionType, "计划不存在", id)
} else if errors.Is(err, service.ErrPlanCannotBeStopped) {
return controller.SendErrorWithAudit(ctx, controller.CodeForbidden, err.Error(), actionType, "系统计划不允许停止", id)
} else if errors.Is(err, service.ErrPlanNotEnabled) {
return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, err.Error(), actionType, "计划未启用", id)
}
return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "停止计划失败: "+err.Error(), actionType, "服务层停止计划失败", id)
}
// 6. 发送成功响应
c.logger.Infof("%s: 计划已成功停止, ID: %d", actionType, id)
return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "计划已成功停止", nil, actionType, "计划已成功停止", id)
}