diff --git a/internal/app/api/api.go b/internal/app/api/api.go index 598e1ea..9f9c6f2 100644 --- a/internal/app/api/api.go +++ b/internal/app/api/api.go @@ -42,30 +42,32 @@ import ( // API 结构体定义了 HTTP 服务器及其依赖 type API struct { - echo *echo.Echo // Echo 引擎实例,用于处理 HTTP 请求 - Ctx context.Context // API 组件的上下文,包含日志记录器 - userRepo repository.UserRepository // 用户数据仓库接口,用于用户数据操作 - tokenGenerator token.Generator // Token 服务接口,用于 JWT token 的生成和解析 - auditService service.AuditService // 审计服务,用于记录用户操作 - httpServer *http.Server // 标准库的 HTTP 服务器实例,用于启动和停止服务 - config config.ServerConfig // API 服务器的配置,使用 infra/config 包中的 ServerConfig - userController *user.Controller // 用户控制器实例 - deviceController *device.Controller // 设备控制器实例 - planController *plan.Controller // 计划控制器实例 - pigFarmController *management.PigFarmController // 猪场管理控制器实例 - pigBatchController *management.PigBatchController // 猪群控制器实例 - monitorController *monitor.Controller // 数据监控控制器实例 - healthController *health.Controller // 健康检查控制器实例 - alarmController *alarm.ThresholdAlarmController // 阈值告警控制器 - nutrientController *feed.NutrientController // 营养控制器实例 - pigAgeStageController *feed.PigAgeStageController // 猪龄阶段控制器实例 - pigBreedController *feed.PigBreedController // 猪品种控制器实例 - pigTypeController *feed.PigTypeController // 猪种类控制器实例 - rawMaterialController *feed.RawMaterialController // 原料控制器实例 - recipeController *feed.RecipeController // 配方控制器实例 - inventoryController *inventory.InventoryController // 库存控制器实例 - listenHandler listener.ListenHandler // 设备上行事件监听器 - analysisTaskManager *domain_plan.AnalysisPlanTaskManager // 计划触发器管理器实例 + echo *echo.Echo // Echo 引擎实例,用于处理 HTTP 请求 + Ctx context.Context // API 组件的上下文,包含日志记录器 + userRepo repository.UserRepository // 用户数据仓库接口,用于用户数据操作 + tokenGenerator token.Generator // Token 服务接口,用于 JWT token 的生成和解析 + auditService service.AuditService // 审计服务,用于记录用户操作 + httpServer *http.Server // 标准库的 HTTP 服务器实例,用于启动和停止服务 + config config.ServerConfig // API 服务器的配置,使用 infra/config 包中的 ServerConfig + userController *user.Controller // 用户控制器实例 + deviceController *device.DeviceController // 设备控制器实例 + deviceTemplateController *device.DeviceTemplateController // 设备模板控制器实例 + areaControllerController *device.AreaControllerController // 区域主控控制器实例 + planController *plan.Controller // 计划控制器实例 + pigFarmController *management.PigFarmController // 猪场管理控制器实例 + pigBatchController *management.PigBatchController // 猪群控制器实例 + monitorController *monitor.Controller // 数据监控控制器实例 + healthController *health.Controller // 健康检查控制器实例 + alarmController *alarm.ThresholdAlarmController // 阈值告警控制器 + nutrientController *feed.NutrientController // 营养控制器实例 + pigAgeStageController *feed.PigAgeStageController // 猪龄阶段控制器实例 + pigBreedController *feed.PigBreedController // 猪品种控制器实例 + pigTypeController *feed.PigTypeController // 猪种类控制器实例 + rawMaterialController *feed.RawMaterialController // 原料控制器实例 + recipeController *feed.RecipeController // 配方控制器实例 + inventoryController *inventory.InventoryController // 库存控制器实例 + listenHandler listener.ListenHandler // 设备上行事件监听器 + analysisTaskManager *domain_plan.AnalysisPlanTaskManager // 计划触发器管理器实例 } // NewAPI 创建并返回一个新的 API 实例 @@ -77,6 +79,8 @@ func NewAPI(cfg config.ServerConfig, pigBatchService service.PigBatchService, monitorService service.MonitorService, deviceService service.DeviceService, + deviceTemplateService service.DeviceTemplateService, + areaControllerService service.AreaControllerService, planService service.PlanService, userService service.UserService, auditService service.AuditService, @@ -104,28 +108,30 @@ func NewAPI(cfg config.ServerConfig, // 初始化 API 结构体 baseCtx := context.Background() api := &API{ - echo: e, - Ctx: ctx, - userRepo: userRepo, - tokenGenerator: tokenGenerator, - auditService: auditService, - config: cfg, - listenHandler: listenHandler, - userController: user.NewController(logs.AddCompName(baseCtx, "UserController"), userService), - deviceController: device.NewController(logs.AddCompName(baseCtx, "DeviceController"), deviceService), - planController: plan.NewController(logs.AddCompName(baseCtx, "PlanController"), planService), - pigFarmController: management.NewPigFarmController(logs.AddCompName(baseCtx, "PigFarmController"), pigFarmService), - pigBatchController: management.NewPigBatchController(logs.AddCompName(baseCtx, "PigBatchController"), pigBatchService), - monitorController: monitor.NewController(logs.AddCompName(baseCtx, "MonitorController"), monitorService), - healthController: health.NewController(logs.AddCompName(baseCtx, "HealthController")), - alarmController: alarm.NewThresholdAlarmController(logs.AddCompName(baseCtx, "ThresholdAlarmController"), alarmService), - nutrientController: feed.NewNutrientController(logs.AddCompName(baseCtx, "NutrientController"), nutrientService), - pigAgeStageController: feed.NewPigAgeStageController(logs.AddCompName(baseCtx, "PigAgeStageController"), pigAgeStageService), - pigBreedController: feed.NewPigBreedController(logs.AddCompName(baseCtx, "PigBreedController"), pigBreedService), - pigTypeController: feed.NewPigTypeController(logs.AddCompName(baseCtx, "PigTypeController"), pigTypeService), - rawMaterialController: feed.NewRawMaterialController(logs.AddCompName(baseCtx, "RawMaterialController"), rawMaterialService), - recipeController: feed.NewRecipeController(logs.AddCompName(baseCtx, "RecipeController"), recipeService), - inventoryController: inventory.NewInventoryController(logs.AddCompName(baseCtx, "InventoryController"), inventoryService), + echo: e, + Ctx: ctx, + userRepo: userRepo, + tokenGenerator: tokenGenerator, + auditService: auditService, + config: cfg, + listenHandler: listenHandler, + userController: user.NewController(logs.AddCompName(baseCtx, "UserController"), userService), + deviceController: device.NewDeviceController(logs.AddCompName(baseCtx, "DeviceController"), deviceService), + deviceTemplateController: device.NewDeviceTemplateController(logs.AddCompName(baseCtx, "DeviceTemplateController"), deviceTemplateService), + areaControllerController: device.NewAreaControllerController(logs.AddCompName(baseCtx, "AreaControllerController"), areaControllerService), + planController: plan.NewController(logs.AddCompName(baseCtx, "PlanController"), planService), + pigFarmController: management.NewPigFarmController(logs.AddCompName(baseCtx, "PigFarmController"), pigFarmService), + pigBatchController: management.NewPigBatchController(logs.AddCompName(baseCtx, "PigBatchController"), pigBatchService), + monitorController: monitor.NewController(logs.AddCompName(baseCtx, "MonitorController"), monitorService), + healthController: health.NewController(logs.AddCompName(baseCtx, "HealthController")), + alarmController: alarm.NewThresholdAlarmController(logs.AddCompName(baseCtx, "ThresholdAlarmController"), alarmService), + nutrientController: feed.NewNutrientController(logs.AddCompName(baseCtx, "NutrientController"), nutrientService), + pigAgeStageController: feed.NewPigAgeStageController(logs.AddCompName(baseCtx, "PigAgeStageController"), pigAgeStageService), + pigBreedController: feed.NewPigBreedController(logs.AddCompName(baseCtx, "PigBreedController"), pigBreedService), + pigTypeController: feed.NewPigTypeController(logs.AddCompName(baseCtx, "PigTypeController"), pigTypeService), + rawMaterialController: feed.NewRawMaterialController(logs.AddCompName(baseCtx, "RawMaterialController"), rawMaterialService), + recipeController: feed.NewRecipeController(logs.AddCompName(baseCtx, "RecipeController"), recipeService), + inventoryController: inventory.NewInventoryController(logs.AddCompName(baseCtx, "InventoryController"), inventoryService), } api.setupRoutes() // 设置所有路由 diff --git a/internal/app/api/router.go b/internal/app/api/router.go index fd60b70..3575a62 100644 --- a/internal/app/api/router.go +++ b/internal/app/api/router.go @@ -84,22 +84,22 @@ func (a *API) setupRoutes() { // 区域主控相关路由组 areaControllerGroup := authGroup.Group("/area-controllers") { - areaControllerGroup.POST("", a.deviceController.CreateAreaController) // 创建区域主控 - areaControllerGroup.GET("", a.deviceController.ListAreaControllers) // 获取区域主控列表 - areaControllerGroup.GET("/:id", a.deviceController.GetAreaController) // 获取单个区域主控 - areaControllerGroup.PUT("/:id", a.deviceController.UpdateAreaController) // 更新区域主控 - areaControllerGroup.DELETE("/:id", a.deviceController.DeleteAreaController) // 删除区域主控 + areaControllerGroup.POST("", a.areaControllerController.CreateAreaController) // 创建区域主控 + areaControllerGroup.GET("", a.areaControllerController.ListAreaControllers) // 获取区域主控列表 + areaControllerGroup.GET("/:id", a.areaControllerController.GetAreaController) // 获取单个区域主控 + areaControllerGroup.PUT("/:id", a.areaControllerController.UpdateAreaController) // 更新区域主控 + areaControllerGroup.DELETE("/:id", a.areaControllerController.DeleteAreaController) // 删除区域主控 } logger.Debug("区域主控相关接口注册成功 (需要认证和审计)") // 设备模板相关路由组 deviceTemplateGroup := authGroup.Group("/device-templates") { - deviceTemplateGroup.POST("", a.deviceController.CreateDeviceTemplate) // 创建设备模板 - deviceTemplateGroup.GET("", a.deviceController.ListDeviceTemplates) // 获取设备模板列表 - deviceTemplateGroup.GET("/:id", a.deviceController.GetDeviceTemplate) // 获取单个设备模板 - deviceTemplateGroup.PUT("/:id", a.deviceController.UpdateDeviceTemplate) // 更新设备模板 - deviceTemplateGroup.DELETE("/:id", a.deviceController.DeleteDeviceTemplate) // 删除设备模板 + deviceTemplateGroup.POST("", a.deviceTemplateController.CreateDeviceTemplate) // 创建设备模板 + deviceTemplateGroup.GET("", a.deviceTemplateController.ListDeviceTemplates) // 获取设备模板列表 + deviceTemplateGroup.GET("/:id", a.deviceTemplateController.GetDeviceTemplate) // 获取单个设备模板 + deviceTemplateGroup.PUT("/:id", a.deviceTemplateController.UpdateDeviceTemplate) // 更新设备模板 + deviceTemplateGroup.DELETE("/:id", a.deviceTemplateController.DeleteDeviceTemplate) // 删除设备模板 } logger.Debug("设备模板相关接口注册成功 (需要认证和审计)") diff --git a/internal/app/controller/device/area_controller_controller.go b/internal/app/controller/device/area_controller_controller.go new file mode 100644 index 0000000..f0edf06 --- /dev/null +++ b/internal/app/controller/device/area_controller_controller.go @@ -0,0 +1,200 @@ +package device + +import ( + "context" + "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" + "gorm.io/gorm" +) + +// AreaControllerController 区域主控控制器 +type AreaControllerController struct { + ctx context.Context + areaControllerService service.AreaControllerService +} + +// NewAreaControllerController 创建一个新的区域主控控制器实例 +func NewAreaControllerController( + ctx context.Context, + areaControllerService service.AreaControllerService, +) *AreaControllerController { + return &AreaControllerController{ + ctx: ctx, + areaControllerService: areaControllerService, + } +} + +// CreateAreaController godoc +// @Summary 创建新区域主控 +// @Description 根据提供的信息创建一个新区域主控 +// @Tags 区域主控管理 +// @Security BearerAuth +// @Accept json +// @Produce json +// @Param areaController body dto.CreateAreaControllerRequest true "区域主控信息" +// @Success 200 {object} controller.Response{data=dto.AreaControllerResponse} +// @Router /api/v1/area-controllers [post] +func (c *AreaControllerController) CreateAreaController(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "CreateAreaController") + + const actionType = "创建区域主控" + var req dto.CreateAreaControllerRequest + if err := ctx.Bind(&req); err != nil { + logger.Errorf("%s: 参数绑定失败: %v", actionType, err) + return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req) + } + + resp, err := c.areaControllerService.CreateAreaController(reqCtx, &req) + if err != nil { + logger.Errorf("%s: 服务层创建失败: %v", actionType, err) + return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "创建区域主控失败: "+err.Error(), actionType, "服务层创建失败", req) + } + + logger.Infof("%s: 区域主控创建成功, ID: %d", actionType, resp.ID) + return controller.SendSuccessWithAudit(ctx, controller.CodeCreated, "区域主控创建成功", resp, actionType, "区域主控创建成功", resp) +} + +// GetAreaController godoc +// @Summary 获取区域主控信息 +// @Description 根据ID获取单个区域主控的详细信息 +// @Tags 区域主控管理 +// @Security BearerAuth +// @Produce json +// @Param id path string true "区域主控ID" +// @Success 200 {object} controller.Response{data=dto.AreaControllerResponse} +// @Router /api/v1/area-controllers/{id} [get] +func (c *AreaControllerController) GetAreaController(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "GetAreaController") + + const actionType = "获取区域主控" + acID := ctx.Param("id") + + id, err := strconv.ParseUint(acID, 10, 64) + if err != nil { + logger.Errorf("%s: 无效的ID: %s", actionType, acID) + return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的ID: "+acID, actionType, "无效的ID", acID) + } + + resp, err := c.areaControllerService.GetAreaController(reqCtx, uint32(id)) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + logger.Warnf("%s: 区域主控不存在, ID: %s", actionType, acID) + return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "区域主控未找到", actionType, "区域主控不存在", acID) + } + logger.Errorf("%s: 服务层获取失败: %v, ID: %s", actionType, err, acID) + return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取区域主控信息失败: "+err.Error(), actionType, "服务层获取失败", acID) + } + + logger.Infof("%s: 获取区域主控信息成功, ID: %d", actionType, resp.ID) + return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取区域主控信息成功", resp, actionType, "获取区域主控信息成功", resp) +} + +// ListAreaControllers godoc +// @Summary 获取所有区域主控列表 +// @Description 获取系统中所有区域主控的列表 +// @Tags 区域主控管理 +// @Security BearerAuth +// @Produce json +// @Success 200 {object} controller.Response{data=[]dto.AreaControllerResponse} +// @Router /api/v1/area-controllers [get] +func (c *AreaControllerController) ListAreaControllers(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "ListAreaControllers") + + const actionType = "获取区域主控列表" + resp, err := c.areaControllerService.ListAreaControllers(reqCtx) + if err != nil { + logger.Errorf("%s: 服务层获取列表失败: %v", actionType, err) + return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取区域主控列表失败: "+err.Error(), actionType, "服务层获取列表失败", nil) + } + + logger.Infof("%s: 获取区域主控列表成功, 数量: %d", actionType, len(resp)) + return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取区域主控列表成功", resp, actionType, "获取区域主控列表成功", resp) +} + +// UpdateAreaController godoc +// @Summary 更新区域主控信息 +// @Description 根据ID更新一个已存在的区域主控信息 +// @Tags 区域主控管理 +// @Security BearerAuth +// @Accept json +// @Produce json +// @Param id path string true "区域主控ID" +// @Param areaController body dto.UpdateAreaControllerRequest true "要更新的区域主控信息" +// @Success 200 {object} controller.Response{data=dto.AreaControllerResponse} +// @Router /api/v1/area-controllers/{id} [put] +func (c *AreaControllerController) UpdateAreaController(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "UpdateAreaController") + + const actionType = "更新区域主控" + acID := ctx.Param("id") + + var req dto.UpdateAreaControllerRequest + if err := ctx.Bind(&req); err != nil { + logger.Errorf("%s: 参数绑定失败: %v", actionType, err) + return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req) + } + id, err := strconv.ParseUint(acID, 10, 64) + if err != nil { + logger.Errorf("%s: 无效的ID: %s", actionType, acID) + return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的ID: "+acID, actionType, "无效的ID", acID) + } + + resp, err := c.areaControllerService.UpdateAreaController(reqCtx, uint32(id), &req) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + logger.Warnf("%s: 区域主控不存在, ID: %s", actionType, acID) + return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "区域主控未找到", actionType, "区域主控不存在", acID) + } + logger.Errorf("%s: 服务层更新失败: %v, ID: %s", actionType, err, acID) + return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "更新区域主控失败: "+err.Error(), actionType, "服务层更新失败", acID) + } + + logger.Infof("%s: 区域主控更新成功, ID: %d", actionType, resp.ID) + return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "区域主控更新成功", resp, actionType, "区域主控更新成功", resp) +} + +// DeleteAreaController godoc +// @Summary 删除区域主控 +// @Description 根据ID删除一个区域主控(软删除) +// @Tags 区域主控管理 +// @Security BearerAuth +// @Produce json +// @Param id path string true "区域主控ID" +// @Success 200 {object} controller.Response +// @Router /api/v1/area-controllers/{id} [delete] +func (c *AreaControllerController) DeleteAreaController(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "DeleteAreaController") + + const actionType = "删除区域主控" + acID := ctx.Param("id") + + id, err := strconv.ParseUint(acID, 10, 64) + if err != nil { + logger.Errorf("%s: 无效的ID: %s", actionType, acID) + return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的ID: "+acID, actionType, "无效的ID", acID) + } + + if err := c.areaControllerService.DeleteAreaController(reqCtx, uint32(id)); err != nil { + switch { + case errors.Is(err, gorm.ErrRecordNotFound): + logger.Warnf("%s: 区域主控不存在, ID: %s", actionType, acID) + return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "区域主控未找到", actionType, "区域主控不存在", acID) + case errors.Is(err, service.ErrAreaControllerInUse): + logger.Warnf("%s: 尝试删除正在被使用的主控, ID: %s", actionType, acID) + return controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), actionType, "主控正在被使用", acID) + default: + logger.Errorf("%s: 服务层删除失败: %v, ID: %s", actionType, err, acID) + return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "删除区域主控失败: "+err.Error(), actionType, "服务层删除失败", acID) + } + } + + logger.Infof("%s: 区域主控删除成功, ID: %s", actionType, acID) + return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "区域主控删除成功", nil, actionType, "区域主控删除成功", acID) +} diff --git a/internal/app/controller/device/device_controller.go b/internal/app/controller/device/device_controller.go index 448fecc..3a0a53e 100644 --- a/internal/app/controller/device/device_controller.go +++ b/internal/app/controller/device/device_controller.go @@ -14,25 +14,23 @@ import ( "gorm.io/gorm" ) -// Controller 设备控制器,封装了所有与设备和区域主控相关的业务逻辑 -type Controller struct { +// DeviceController 设备控制器 +type DeviceController struct { ctx context.Context deviceService service.DeviceService } -// NewController 创建一个新的设备控制器实例 -func NewController( +// NewDeviceController 创建一个新的设备控制器实例 +func NewDeviceController( ctx context.Context, deviceService service.DeviceService, -) *Controller { - return &Controller{ +) *DeviceController { + return &DeviceController{ ctx: ctx, deviceService: deviceService, } } -// --- Controller Methods: Devices --- - // CreateDevice godoc // @Summary 创建新设备 // @Description 根据提供的信息创建一个新设备 @@ -43,7 +41,7 @@ func NewController( // @Param device body dto.CreateDeviceRequest true "设备信息" // @Success 200 {object} controller.Response{data=dto.DeviceResponse} // @Router /api/v1/devices [post] -func (c *Controller) CreateDevice(ctx echo.Context) error { +func (c *DeviceController) CreateDevice(ctx echo.Context) error { reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "CreateDevice") const actionType = "创建设备" @@ -72,7 +70,7 @@ func (c *Controller) CreateDevice(ctx echo.Context) error { // @Param id path string true "设备ID" // @Success 200 {object} controller.Response{data=dto.DeviceResponse} // @Router /api/v1/devices/{id} [get] -func (c *Controller) GetDevice(ctx echo.Context) error { +func (c *DeviceController) GetDevice(ctx echo.Context) error { reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "GetDevice") const actionType = "获取设备" @@ -106,7 +104,7 @@ func (c *Controller) GetDevice(ctx echo.Context) error { // @Produce json // @Success 200 {object} controller.Response{data=[]dto.DeviceResponse} // @Router /api/v1/devices [get] -func (c *Controller) ListDevices(ctx echo.Context) error { +func (c *DeviceController) ListDevices(ctx echo.Context) error { reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "ListDevices") const actionType = "获取设备列表" @@ -131,7 +129,7 @@ func (c *Controller) ListDevices(ctx echo.Context) error { // @Param device body dto.UpdateDeviceRequest true "要更新的设备信息" // @Success 200 {object} controller.Response{data=dto.DeviceResponse} // @Router /api/v1/devices/{id} [put] -func (c *Controller) UpdateDevice(ctx echo.Context) error { +func (c *DeviceController) UpdateDevice(ctx echo.Context) error { reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "UpdateDevice") const actionType = "更新设备" @@ -172,7 +170,7 @@ func (c *Controller) UpdateDevice(ctx echo.Context) error { // @Param id path string true "设备ID" // @Success 200 {object} controller.Response // @Router /api/v1/devices/{id} [delete] -func (c *Controller) DeleteDevice(ctx echo.Context) error { +func (c *DeviceController) DeleteDevice(ctx echo.Context) error { reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "DeleteDevice") const actionType = "删除设备" @@ -214,7 +212,7 @@ func (c *Controller) DeleteDevice(ctx echo.Context) error { // @Param manualControl body dto.ManualControlDeviceRequest true "手动控制指令" // @Success 200 {object} controller.Response // @Router /api/v1/devices/manual-control/{id} [post] -func (c *Controller) ManualControl(ctx echo.Context) error { +func (c *DeviceController) ManualControl(ctx echo.Context) error { reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "ManualControlDevice") const actionType = "手动控制设备" @@ -243,344 +241,3 @@ func (c *Controller) ManualControl(ctx echo.Context) error { return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "指令已发送", nil, actionType, "指令发送成功", nil) } - -// --- Controller Methods: Area Controllers --- - -// CreateAreaController godoc -// @Summary 创建新区域主控 -// @Description 根据提供的信息创建一个新区域主控 -// @Tags 区域主控管理 -// @Security BearerAuth -// @Accept json -// @Produce json -// @Param areaController body dto.CreateAreaControllerRequest true "区域主控信息" -// @Success 200 {object} controller.Response{data=dto.AreaControllerResponse} -// @Router /api/v1/area-controllers [post] -func (c *Controller) CreateAreaController(ctx echo.Context) error { - reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "CreateAreaController") - - const actionType = "创建区域主控" - var req dto.CreateAreaControllerRequest - if err := ctx.Bind(&req); err != nil { - logger.Errorf("%s: 参数绑定失败: %v", actionType, err) - return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req) - } - - resp, err := c.deviceService.CreateAreaController(reqCtx, &req) - if err != nil { - logger.Errorf("%s: 服务层创建失败: %v", actionType, err) - return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "创建区域主控失败: "+err.Error(), actionType, "服务层创建失败", req) - } - - logger.Infof("%s: 区域主控创建成功, ID: %d", actionType, resp.ID) - return controller.SendSuccessWithAudit(ctx, controller.CodeCreated, "区域主控创建成功", resp, actionType, "区域主控创建成功", resp) -} - -// GetAreaController godoc -// @Summary 获取区域主控信息 -// @Description 根据ID获取单个区域主控的详细信息 -// @Tags 区域主控管理 -// @Security BearerAuth -// @Produce json -// @Param id path string true "区域主控ID" -// @Success 200 {object} controller.Response{data=dto.AreaControllerResponse} -// @Router /api/v1/area-controllers/{id} [get] -func (c *Controller) GetAreaController(ctx echo.Context) error { - reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "GetAreaController") - - const actionType = "获取区域主控" - acID := ctx.Param("id") - - id, err := strconv.ParseUint(acID, 10, 64) - if err != nil { - logger.Errorf("%s: 无效的ID: %s", actionType, acID) - return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的ID: "+acID, actionType, "无效的ID", acID) - } - - resp, err := c.deviceService.GetAreaController(reqCtx, uint32(id)) - if err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - logger.Warnf("%s: 区域主控不存在, ID: %s", actionType, acID) - return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "区域主控未找到", actionType, "区域主控不存在", acID) - } - logger.Errorf("%s: 服务层获取失败: %v, ID: %s", actionType, err, acID) - return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取区域主控信息失败: "+err.Error(), actionType, "服务层获取失败", acID) - } - - logger.Infof("%s: 获取区域主控信息成功, ID: %d", actionType, resp.ID) - return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取区域主控信息成功", resp, actionType, "获取区域主控信息成功", resp) -} - -// ListAreaControllers godoc -// @Summary 获取所有区域主控列表 -// @Description 获取系统中所有区域主控的列表 -// @Tags 区域主控管理 -// @Security BearerAuth -// @Produce json -// @Success 200 {object} controller.Response{data=[]dto.AreaControllerResponse} -// @Router /api/v1/area-controllers [get] -func (c *Controller) ListAreaControllers(ctx echo.Context) error { - reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "ListAreaControllers") - - const actionType = "获取区域主控列表" - resp, err := c.deviceService.ListAreaControllers(reqCtx) - if err != nil { - logger.Errorf("%s: 服务层获取列表失败: %v", actionType, err) - return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取区域主控列表失败: "+err.Error(), actionType, "服务层获取列表失败", nil) - } - - logger.Infof("%s: 获取区域主控列表成功, 数量: %d", actionType, len(resp)) - return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取区域主控列表成功", resp, actionType, "获取区域主控列表成功", resp) -} - -// UpdateAreaController godoc -// @Summary 更新区域主控信息 -// @Description 根据ID更新一个已存在的区域主控信息 -// @Tags 区域主控管理 -// @Security BearerAuth -// @Accept json -// @Produce json -// @Param id path string true "区域主控ID" -// @Param areaController body dto.UpdateAreaControllerRequest true "要更新的区域主控信息" -// @Success 200 {object} controller.Response{data=dto.AreaControllerResponse} -// @Router /api/v1/area-controllers/{id} [put] -func (c *Controller) UpdateAreaController(ctx echo.Context) error { - reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "UpdateAreaController") - - const actionType = "更新区域主控" - acID := ctx.Param("id") - - var req dto.UpdateAreaControllerRequest - if err := ctx.Bind(&req); err != nil { - logger.Errorf("%s: 参数绑定失败: %v", actionType, err) - return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req) - } - id, err := strconv.ParseUint(acID, 10, 64) - if err != nil { - logger.Errorf("%s: 无效的ID: %s", actionType, acID) - return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的ID: "+acID, actionType, "无效的ID", acID) - } - - resp, err := c.deviceService.UpdateAreaController(reqCtx, uint32(id), &req) - if err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - logger.Warnf("%s: 区域主控不存在, ID: %s", actionType, acID) - return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "区域主控未找到", actionType, "区域主控不存在", acID) - } - logger.Errorf("%s: 服务层更新失败: %v, ID: %s", actionType, err, acID) - return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "更新区域主控失败: "+err.Error(), actionType, "服务层更新失败", acID) - } - - logger.Infof("%s: 区域主控更新成功, ID: %d", actionType, resp.ID) - return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "区域主控更新成功", resp, actionType, "区域主控更新成功", resp) -} - -// DeleteAreaController godoc -// @Summary 删除区域主控 -// @Description 根据ID删除一个区域主控(软删除) -// @Tags 区域主控管理 -// @Security BearerAuth -// @Produce json -// @Param id path string true "区域主控ID" -// @Success 200 {object} controller.Response -// @Router /api/v1/area-controllers/{id} [delete] -func (c *Controller) DeleteAreaController(ctx echo.Context) error { - reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "DeleteAreaController") - - const actionType = "删除区域主控" - acID := ctx.Param("id") - - id, err := strconv.ParseUint(acID, 10, 64) - if err != nil { - logger.Errorf("%s: 无效的ID: %s", actionType, acID) - return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的ID: "+acID, actionType, "无效的ID", acID) - } - - if err := c.deviceService.DeleteAreaController(reqCtx, uint32(id)); err != nil { - switch { - case errors.Is(err, gorm.ErrRecordNotFound): - logger.Warnf("%s: 区域主控不存在, ID: %s", actionType, acID) - return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "区域主控未找到", actionType, "区域主控不存在", acID) - case errors.Is(err, service.ErrAreaControllerInUse): - logger.Warnf("%s: 尝试删除正在被使用的主控, ID: %s", actionType, acID) - return controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), actionType, "主控正在被使用", acID) - default: - logger.Errorf("%s: 服务层删除失败: %v, ID: %s", actionType, err, acID) - return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "删除区域主控失败: "+err.Error(), actionType, "服务层删除失败", acID) - } - } - - logger.Infof("%s: 区域主控删除成功, ID: %s", actionType, acID) - return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "区域主控删除成功", nil, actionType, "区域主控删除成功", acID) -} - -// --- Controller Methods: Device Templates --- - -// CreateDeviceTemplate godoc -// @Summary 创建新设备模板 -// @Description 根据提供的信息创建一个新设备模板 -// @Tags 设备模板管理 -// @Security BearerAuth -// @Accept json -// @Produce json -// @Param deviceTemplate body dto.CreateDeviceTemplateRequest true "设备模板信息" -// @Success 200 {object} controller.Response{data=dto.DeviceTemplateResponse} -// @Router /api/v1/device-templates [post] -func (c *Controller) CreateDeviceTemplate(ctx echo.Context) error { - reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "CreateDeviceTemplate") - - const actionType = "创建设备模板" - var req dto.CreateDeviceTemplateRequest - if err := ctx.Bind(&req); err != nil { - logger.Errorf("%s: 参数绑定失败: %v", actionType, err) - return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req) - } - - resp, err := c.deviceService.CreateDeviceTemplate(reqCtx, &req) - if err != nil { - logger.Errorf("%s: 服务层创建失败: %v", actionType, err) - return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "创建设备模板失败: "+err.Error(), actionType, "服务层创建失败", req) - } - - logger.Infof("%s: 设备模板创建成功, ID: %d", actionType, resp.ID) - return controller.SendSuccessWithAudit(ctx, controller.CodeCreated, "设备模板创建成功", resp, actionType, "设备模板创建成功", resp) -} - -// GetDeviceTemplate godoc -// @Summary 获取设备模板信息 -// @Description 根据设备模板ID获取单个设备模板的详细信息 -// @Tags 设备模板管理 -// @Security BearerAuth -// @Produce json -// @Param id path string true "设备模板ID" -// @Success 200 {object} controller.Response{data=dto.DeviceTemplateResponse} -// @Router /api/v1/device-templates/{id} [get] -func (c *Controller) GetDeviceTemplate(ctx echo.Context) error { - reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "GetDeviceTemplate") - - const actionType = "获取设备模板" - dtID := ctx.Param("id") - - id, err := strconv.ParseUint(dtID, 10, 64) - if err != nil { - logger.Errorf("%s: 无效的ID: %s", actionType, dtID) - return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的ID: "+dtID, actionType, "无效的ID", dtID) - } - - resp, err := c.deviceService.GetDeviceTemplate(reqCtx, uint32(id)) - if err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - logger.Warnf("%s: 设备模板不存在, ID: %s", actionType, dtID) - return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "设备模板未找到", actionType, "设备模板不存在", dtID) - } - logger.Errorf("%s: 服务层获取失败: %v, ID: %s", actionType, err, dtID) - return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备模板信息失败: "+err.Error(), actionType, "服务层获取失败", dtID) - } - - logger.Infof("%s: 获取设备模板信息成功, ID: %d", actionType, resp.ID) - return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取设备模板信息成功", resp, actionType, "获取设备模板信息成功", resp) -} - -// ListDeviceTemplates godoc -// @Summary 获取设备模板列表 -// @Description 获取系统中所有设备模板的列表 -// @Tags 设备模板管理 -// @Security BearerAuth -// @Produce json -// @Success 200 {object} controller.Response{data=[]dto.DeviceTemplateResponse} -// @Router /api/v1/device-templates [get] -func (c *Controller) ListDeviceTemplates(ctx echo.Context) error { - reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "ListDeviceTemplates") - - const actionType = "获取设备模板列表" - resp, err := c.deviceService.ListDeviceTemplates(reqCtx) - if err != nil { - logger.Errorf("%s: 服务层获取列表失败: %v", actionType, err) - return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备模板列表失败: "+err.Error(), actionType, "服务层获取列表失败", nil) - } - - logger.Infof("%s: 获取设备模板列表成功, 数量: %d", actionType, len(resp)) - return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取设备模板列表成功", resp, actionType, "获取设备模板列表成功", resp) -} - -// UpdateDeviceTemplate godoc -// @Summary 更新设备模板信息 -// @Description 根据设备模板ID更新一个已存在的设备模板信息 -// @Tags 设备模板管理 -// @Security BearerAuth -// @Accept json -// @Produce json -// @Param id path string true "设备模板ID" -// @Param deviceTemplate body dto.UpdateDeviceTemplateRequest true "要更新的设备模板信息" -// @Success 200 {object} controller.Response{data=dto.DeviceTemplateResponse} -// @Router /api/v1/device-templates/{id} [put] -func (c *Controller) UpdateDeviceTemplate(ctx echo.Context) error { - reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "UpdateDeviceTemplate") - - const actionType = "更新设备模板" - dtID := ctx.Param("id") - - var req dto.UpdateDeviceTemplateRequest - if err := ctx.Bind(&req); err != nil { - logger.Errorf("%s: 参数绑定失败: %v", actionType, err) - return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req) - } - - id, err := strconv.ParseUint(dtID, 10, 64) - if err != nil { - logger.Errorf("%s: 无效的ID: %s", actionType, dtID) - return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的ID: "+dtID, actionType, "无效的ID", dtID) - } - - resp, err := c.deviceService.UpdateDeviceTemplate(reqCtx, uint32(id), &req) - if err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - logger.Warnf("%s: 设备模板不存在, ID: %s", actionType, dtID) - return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "设备模板未找到", actionType, "设备模板不存在", dtID) - } - logger.Errorf("%s: 服务层更新失败: %v, ID: %s", actionType, err, dtID) - return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "更新设备模板失败: "+err.Error(), actionType, "服务层更新失败", dtID) - } - - logger.Infof("%s: 设备模板更新成功, ID: %d", actionType, resp.ID) - return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "设备模板更新成功", resp, actionType, "设备模板更新成功", resp) -} - -// DeleteDeviceTemplate godoc -// @Summary 删除设备模板 -// @Description 根据设备模板ID删除一个设备模板(软删除) -// @Tags 设备模板管理 -// @Security BearerAuth -// @Produce json -// @Param id path string true "设备模板ID" -// @Success 200 {object} controller.Response -// @Router /api/v1/device-templates/{id} [delete] -func (c *Controller) DeleteDeviceTemplate(ctx echo.Context) error { - reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "DeleteDeviceTemplate") - - const actionType = "删除设备模板" - dtID := ctx.Param("id") - - id, err := strconv.ParseUint(dtID, 10, 64) - if err != nil { - logger.Errorf("%s: 无效的ID: %s", actionType, dtID) - return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的ID: "+dtID, actionType, "无效的ID", dtID) - } - - if err := c.deviceService.DeleteDeviceTemplate(reqCtx, uint32(id)); err != nil { - switch { - case errors.Is(err, gorm.ErrRecordNotFound): - logger.Warnf("%s: 设备模板不存在, ID: %s", actionType, dtID) - return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "设备模板未找到", actionType, "设备模板不存在", dtID) - case errors.Is(err, service.ErrDeviceTemplateInUse): - logger.Warnf("%s: 尝试删除正在被使用的模板, ID: %s", actionType, dtID) - return controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), actionType, "模板正在被使用", dtID) - default: - logger.Errorf("%s: 服务层删除失败: %v, ID: %s", actionType, err, dtID) - return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "删除设备模板失败: "+err.Error(), actionType, "服务层删除失败", dtID) - } - } - - logger.Infof("%s: 设备模板删除成功, ID: %s", actionType, dtID) - return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "设备模板删除成功", nil, actionType, "设备模板删除成功", dtID) -} diff --git a/internal/app/controller/device/device_template_controller.go b/internal/app/controller/device/device_template_controller.go new file mode 100644 index 0000000..705d064 --- /dev/null +++ b/internal/app/controller/device/device_template_controller.go @@ -0,0 +1,201 @@ +package device + +import ( + "context" + "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" + "gorm.io/gorm" +) + +// DeviceTemplateController 设备模板控制器 +type DeviceTemplateController struct { + ctx context.Context + deviceTemplateService service.DeviceTemplateService +} + +// NewDeviceTemplateController 创建一个新的设备模板控制器实例 +func NewDeviceTemplateController( + ctx context.Context, + deviceTemplateService service.DeviceTemplateService, +) *DeviceTemplateController { + return &DeviceTemplateController{ + ctx: ctx, + deviceTemplateService: deviceTemplateService, + } +} + +// CreateDeviceTemplate godoc +// @Summary 创建新设备模板 +// @Description 根据提供的信息创建一个新设备模板 +// @Tags 设备模板管理 +// @Security BearerAuth +// @Accept json +// @Produce json +// @Param deviceTemplate body dto.CreateDeviceTemplateRequest true "设备模板信息" +// @Success 200 {object} controller.Response{data=dto.DeviceTemplateResponse} +// @Router /api/v1/device-templates [post] +func (c *DeviceTemplateController) CreateDeviceTemplate(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "CreateDeviceTemplate") + + const actionType = "创建设备模板" + var req dto.CreateDeviceTemplateRequest + if err := ctx.Bind(&req); err != nil { + logger.Errorf("%s: 参数绑定失败: %v", actionType, err) + return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req) + } + + resp, err := c.deviceTemplateService.CreateDeviceTemplate(reqCtx, &req) + if err != nil { + logger.Errorf("%s: 服务层创建失败: %v", actionType, err) + return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "创建设备模板失败: "+err.Error(), actionType, "服务层创建失败", req) + } + + logger.Infof("%s: 设备模板创建成功, ID: %d", actionType, resp.ID) + return controller.SendSuccessWithAudit(ctx, controller.CodeCreated, "设备模板创建成功", resp, actionType, "设备模板创建成功", resp) +} + +// GetDeviceTemplate godoc +// @Summary 获取设备模板信息 +// @Description 根据设备模板ID获取单个设备模板的详细信息 +// @Tags 设备模板管理 +// @Security BearerAuth +// @Produce json +// @Param id path string true "设备模板ID" +// @Success 200 {object} controller.Response{data=dto.DeviceTemplateResponse} +// @Router /api/v1/device-templates/{id} [get] +func (c *DeviceTemplateController) GetDeviceTemplate(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "GetDeviceTemplate") + + const actionType = "获取设备模板" + dtID := ctx.Param("id") + + id, err := strconv.ParseUint(dtID, 10, 64) + if err != nil { + logger.Errorf("%s: 无效的ID: %s", actionType, dtID) + return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的ID: "+dtID, actionType, "无效的ID", dtID) + } + + resp, err := c.deviceTemplateService.GetDeviceTemplate(reqCtx, uint32(id)) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + logger.Warnf("%s: 设备模板不存在, ID: %s", actionType, dtID) + return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "设备模板未找到", actionType, "设备模板不存在", dtID) + } + logger.Errorf("%s: 服务层获取失败: %v, ID: %s", actionType, err, dtID) + return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备模板信息失败: "+err.Error(), actionType, "服务层获取失败", dtID) + } + + logger.Infof("%s: 获取设备模板信息成功, ID: %d", actionType, resp.ID) + return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取设备模板信息成功", resp, actionType, "获取设备模板信息成功", resp) +} + +// ListDeviceTemplates godoc +// @Summary 获取设备模板列表 +// @Description 获取系统中所有设备模板的列表 +// @Tags 设备模板管理 +// @Security BearerAuth +// @Produce json +// @Success 200 {object} controller.Response{data=[]dto.DeviceTemplateResponse} +// @Router /api/v1/device-templates [get] +func (c *DeviceTemplateController) ListDeviceTemplates(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "ListDeviceTemplates") + + const actionType = "获取设备模板列表" + resp, err := c.deviceTemplateService.ListDeviceTemplates(reqCtx) + if err != nil { + logger.Errorf("%s: 服务层获取列表失败: %v", actionType, err) + return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备模板列表失败: "+err.Error(), actionType, "服务层获取列表失败", nil) + } + + logger.Infof("%s: 获取设备模板列表成功, 数量: %d", actionType, len(resp)) + return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取设备模板列表成功", resp, actionType, "获取设备模板列表成功", resp) +} + +// UpdateDeviceTemplate godoc +// @Summary 更新设备模板信息 +// @Description 根据设备模板ID更新一个已存在的设备模板信息 +// @Tags 设备模板管理 +// @Security BearerAuth +// @Accept json +// @Produce json +// @Param id path string true "设备模板ID" +// @Param deviceTemplate body dto.UpdateDeviceTemplateRequest true "要更新的设备模板信息" +// @Success 200 {object} controller.Response{data=dto.DeviceTemplateResponse} +// @Router /api/v1/device-templates/{id} [put] +func (c *DeviceTemplateController) UpdateDeviceTemplate(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "UpdateDeviceTemplate") + + const actionType = "更新设备模板" + dtID := ctx.Param("id") + + var req dto.UpdateDeviceTemplateRequest + if err := ctx.Bind(&req); err != nil { + logger.Errorf("%s: 参数绑定失败: %v", actionType, err) + return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req) + } + + id, err := strconv.ParseUint(dtID, 10, 64) + if err != nil { + logger.Errorf("%s: 无效的ID: %s", actionType, dtID) + return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的ID: "+dtID, actionType, "无效的ID", dtID) + } + + resp, err := c.deviceTemplateService.UpdateDeviceTemplate(reqCtx, uint32(id), &req) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + logger.Warnf("%s: 设备模板不存在, ID: %s", actionType, dtID) + return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "设备模板未找到", actionType, "设备模板不存在", dtID) + } + logger.Errorf("%s: 服务层更新失败: %v, ID: %s", actionType, err, dtID) + return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "更新设备模板失败: "+err.Error(), actionType, "服务层更新失败", dtID) + } + + logger.Infof("%s: 设备模板更新成功, ID: %d", actionType, resp.ID) + return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "设备模板更新成功", resp, actionType, "设备模板更新成功", resp) +} + +// DeleteDeviceTemplate godoc +// @Summary 删除设备模板 +// @Description 根据设备模板ID删除一个设备模板(软删除) +// @Tags 设备模板管理 +// @Security BearerAuth +// @Produce json +// @Param id path string true "设备模板ID" +// @Success 200 {object} controller.Response +// @Router /api/v1/device-templates/{id} [delete] +func (c *DeviceTemplateController) DeleteDeviceTemplate(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "DeleteDeviceTemplate") + + const actionType = "删除设备模板" + dtID := ctx.Param("id") + + id, err := strconv.ParseUint(dtID, 10, 64) + if err != nil { + logger.Errorf("%s: 无效的ID: %s", actionType, dtID) + return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的ID: "+dtID, actionType, "无效的ID", dtID) + } + + if err := c.deviceTemplateService.DeleteDeviceTemplate(reqCtx, uint32(id)); err != nil { + switch { + case errors.Is(err, gorm.ErrRecordNotFound): + logger.Warnf("%s: 设备模板不存在, ID: %s", actionType, dtID) + return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "设备模板未找到", actionType, "设备模板不存在", dtID) + case errors.Is(err, service.ErrDeviceTemplateInUse): + logger.Warnf("%s: 尝试删除正在被使用的模板, ID: %s", actionType, dtID) + return controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), actionType, "模板正在被使用", dtID) + default: + logger.Errorf("%s: 服务层删除失败: %v, ID: %s", actionType, err, dtID) + return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "删除设备模板失败: "+err.Error(), actionType, "服务层删除失败", dtID) + } + } + + logger.Infof("%s: 设备模板删除成功, ID: %s", actionType, dtID) + return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "设备模板删除成功", nil, actionType, "设备模板删除成功", dtID) +} diff --git a/internal/app/service/area_controller_service.go b/internal/app/service/area_controller_service.go new file mode 100644 index 0000000..b18fdb5 --- /dev/null +++ b/internal/app/service/area_controller_service.go @@ -0,0 +1,140 @@ +package service + +import ( + "context" + "encoding/json" + "fmt" + + "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/models" + "git.huangwc.com/pig/pig-farm-controller/internal/infra/repository" +) + +// AreaControllerService 定义了应用层的区域主控服务接口。 +type AreaControllerService interface { + CreateAreaController(ctx context.Context, req *dto.CreateAreaControllerRequest) (*dto.AreaControllerResponse, error) + GetAreaController(ctx context.Context, id uint32) (*dto.AreaControllerResponse, error) + ListAreaControllers(ctx context.Context) ([]*dto.AreaControllerResponse, error) + UpdateAreaController(ctx context.Context, id uint32, req *dto.UpdateAreaControllerRequest) (*dto.AreaControllerResponse, error) + DeleteAreaController(ctx context.Context, id uint32) error +} + +// areaControllerService 是 AreaControllerService 接口的具体实现。 +type areaControllerService struct { + ctx context.Context + areaControllerRepo repository.AreaControllerRepository + thresholdAlarmService ThresholdAlarmService +} + +// NewAreaControllerService 创建一个新的 AreaControllerService 实例。 +func NewAreaControllerService( + ctx context.Context, + areaControllerRepo repository.AreaControllerRepository, + thresholdAlarmService ThresholdAlarmService, +) AreaControllerService { + return &areaControllerService{ + ctx: ctx, + areaControllerRepo: areaControllerRepo, + thresholdAlarmService: thresholdAlarmService, + } +} + +func (s *areaControllerService) CreateAreaController(ctx context.Context, req *dto.CreateAreaControllerRequest) (*dto.AreaControllerResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "CreateAreaController") + propertiesJSON, err := json.Marshal(req.Properties) + if err != nil { + return nil, err + } + + ac := &models.AreaController{ + Name: req.Name, + NetworkID: req.NetworkID, + Location: req.Location, + Properties: propertiesJSON, + } + + if err := ac.SelfCheck(); err != nil { + return nil, err + } + + if err := s.areaControllerRepo.Create(serviceCtx, ac); err != nil { + return nil, err + } + + return dto.NewAreaControllerResponse(ac) +} + +func (s *areaControllerService) GetAreaController(ctx context.Context, id uint32) (*dto.AreaControllerResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "GetAreaController") + ac, err := s.areaControllerRepo.FindByID(serviceCtx, id) + if err != nil { + return nil, err + } + return dto.NewAreaControllerResponse(ac) +} + +func (s *areaControllerService) ListAreaControllers(ctx context.Context) ([]*dto.AreaControllerResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListAreaControllers") + acs, err := s.areaControllerRepo.ListAll(serviceCtx) + if err != nil { + return nil, err + } + return dto.NewListAreaControllerResponse(acs) +} + +func (s *areaControllerService) UpdateAreaController(ctx context.Context, id uint32, req *dto.UpdateAreaControllerRequest) (*dto.AreaControllerResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdateAreaController") + existingAC, err := s.areaControllerRepo.FindByID(serviceCtx, id) + if err != nil { + return nil, err + } + + propertiesJSON, err := json.Marshal(req.Properties) + if err != nil { + return nil, err + } + + existingAC.Name = req.Name + existingAC.NetworkID = req.NetworkID + existingAC.Location = req.Location + existingAC.Properties = propertiesJSON + + if err := existingAC.SelfCheck(); err != nil { + return nil, err + } + + if err := s.areaControllerRepo.Update(serviceCtx, existingAC); err != nil { + return nil, err + } + + return dto.NewAreaControllerResponse(existingAC) +} + +func (s *areaControllerService) DeleteAreaController(ctx context.Context, id uint32) error { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "DeleteAreaController") + + // 1. 检查是否存在 + _, err := s.areaControllerRepo.FindByID(serviceCtx, id) + if err != nil { + return err // 如果未找到,gorm会返回 ErrRecordNotFound + } + + // 2. 检查是否被使用(业务逻辑) + inUse, err := s.areaControllerRepo.IsAreaControllerUsedByTasks(serviceCtx, id, []models.TaskType{models.TaskTypeAreaCollectorThresholdCheck}) + if err != nil { + return err // 返回数据库检查错误 + } + if inUse { + return ErrAreaControllerInUse // 返回业务错误 + } + + // TODO 这个应该用事务处理 + err = s.thresholdAlarmService.DeleteAreaThresholdAlarmByAreaControllerID(serviceCtx, id) + if err != nil { + return fmt.Errorf("删除区域阈值告警失败: %w", err) + } + + // 3. 执行删除 + return s.areaControllerRepo.Delete(serviceCtx, id) +} diff --git a/internal/app/service/device_service.go b/internal/app/service/device_service.go index 262c493..3e878e6 100644 --- a/internal/app/service/device_service.go +++ b/internal/app/service/device_service.go @@ -24,7 +24,6 @@ var ( ErrDeviceTemplateInUse = errors.New("设备模板正在被一个或多个设备使用,无法删除") ) -// DeviceService 定义了应用层的设备服务接口,用于协调设备相关的业务逻辑。 // DeviceService 定义了应用层的设备服务接口,用于协调设备相关的业务逻辑。 type DeviceService interface { CreateDevice(ctx context.Context, req *dto.CreateDeviceRequest) (*dto.DeviceResponse, error) @@ -33,26 +32,12 @@ type DeviceService interface { UpdateDevice(ctx context.Context, id uint32, req *dto.UpdateDeviceRequest) (*dto.DeviceResponse, error) DeleteDevice(ctx context.Context, id uint32) error ManualControl(ctx context.Context, id uint32, req *dto.ManualControlDeviceRequest) error - - CreateAreaController(ctx context.Context, req *dto.CreateAreaControllerRequest) (*dto.AreaControllerResponse, error) - GetAreaController(ctx context.Context, id uint32) (*dto.AreaControllerResponse, error) - ListAreaControllers(ctx context.Context) ([]*dto.AreaControllerResponse, error) - UpdateAreaController(ctx context.Context, id uint32, req *dto.UpdateAreaControllerRequest) (*dto.AreaControllerResponse, error) - DeleteAreaController(ctx context.Context, id uint32) error - - CreateDeviceTemplate(ctx context.Context, req *dto.CreateDeviceTemplateRequest) (*dto.DeviceTemplateResponse, error) - GetDeviceTemplate(ctx context.Context, id uint32) (*dto.DeviceTemplateResponse, error) - ListDeviceTemplates(ctx context.Context) ([]*dto.DeviceTemplateResponse, error) - UpdateDeviceTemplate(ctx context.Context, id uint32, req *dto.UpdateDeviceTemplateRequest) (*dto.DeviceTemplateResponse, error) - DeleteDeviceTemplate(ctx context.Context, id uint32) error } // deviceService 是 DeviceService 接口的具体实现。 type deviceService struct { ctx context.Context deviceRepo repository.DeviceRepository - areaControllerRepo repository.AreaControllerRepository - deviceTemplateRepo repository.DeviceTemplateRepository deviceDomainSvc device.DeviceOperator thresholdAlarmService ThresholdAlarmService } @@ -61,16 +46,12 @@ type deviceService struct { func NewDeviceService( ctx context.Context, deviceRepo repository.DeviceRepository, - areaControllerRepo repository.AreaControllerRepository, - deviceTemplateRepo repository.DeviceTemplateRepository, deviceDomainSvc device.DeviceOperator, thresholdAlarmService ThresholdAlarmService, ) DeviceService { return &deviceService{ ctx: ctx, deviceRepo: deviceRepo, - areaControllerRepo: areaControllerRepo, - deviceTemplateRepo: deviceTemplateRepo, deviceDomainSvc: deviceDomainSvc, thresholdAlarmService: thresholdAlarmService, } @@ -213,213 +194,3 @@ func (s *deviceService) ManualControl(ctx context.Context, id uint32, req *dto.M return s.deviceDomainSvc.Switch(serviceCtx, dev, action) } } - -// --- Area Controllers --- - -func (s *deviceService) CreateAreaController(ctx context.Context, req *dto.CreateAreaControllerRequest) (*dto.AreaControllerResponse, error) { - serviceCtx := logs.AddFuncName(ctx, s.ctx, "CreateAreaController") - propertiesJSON, err := json.Marshal(req.Properties) - if err != nil { - return nil, err - } - - ac := &models.AreaController{ - Name: req.Name, - NetworkID: req.NetworkID, - Location: req.Location, - Properties: propertiesJSON, - } - - if err := ac.SelfCheck(); err != nil { - return nil, err - } - - if err := s.areaControllerRepo.Create(serviceCtx, ac); err != nil { - return nil, err - } - - return dto.NewAreaControllerResponse(ac) -} - -func (s *deviceService) GetAreaController(ctx context.Context, id uint32) (*dto.AreaControllerResponse, error) { - serviceCtx := logs.AddFuncName(ctx, s.ctx, "GetAreaController") - ac, err := s.areaControllerRepo.FindByID(serviceCtx, id) - if err != nil { - return nil, err - } - return dto.NewAreaControllerResponse(ac) -} - -func (s *deviceService) ListAreaControllers(ctx context.Context) ([]*dto.AreaControllerResponse, error) { - serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListAreaControllers") - acs, err := s.areaControllerRepo.ListAll(serviceCtx) - if err != nil { - return nil, err - } - return dto.NewListAreaControllerResponse(acs) -} - -func (s *deviceService) UpdateAreaController(ctx context.Context, id uint32, req *dto.UpdateAreaControllerRequest) (*dto.AreaControllerResponse, error) { - serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdateAreaController") - existingAC, err := s.areaControllerRepo.FindByID(serviceCtx, id) - if err != nil { - return nil, err - } - - propertiesJSON, err := json.Marshal(req.Properties) - if err != nil { - return nil, err - } - - existingAC.Name = req.Name - existingAC.NetworkID = req.NetworkID - existingAC.Location = req.Location - existingAC.Properties = propertiesJSON - - if err := existingAC.SelfCheck(); err != nil { - return nil, err - } - - if err := s.areaControllerRepo.Update(serviceCtx, existingAC); err != nil { - return nil, err - } - - return dto.NewAreaControllerResponse(existingAC) -} - -func (s *deviceService) DeleteAreaController(ctx context.Context, id uint32) error { - serviceCtx := logs.AddFuncName(ctx, s.ctx, "DeleteAreaController") - - // 1. 检查是否存在 - _, err := s.areaControllerRepo.FindByID(serviceCtx, id) - if err != nil { - return err // 如果未找到,gorm会返回 ErrRecordNotFound - } - - // 2. 检查是否被使用(业务逻辑) - inUse, err := s.areaControllerRepo.IsAreaControllerUsedByTasks(serviceCtx, id, []models.TaskType{models.TaskTypeAreaCollectorThresholdCheck}) - if err != nil { - return err // 返回数据库检查错误 - } - if inUse { - return ErrAreaControllerInUse // 返回业务错误 - } - - // TODO 这个应该用事务处理 - err = s.thresholdAlarmService.DeleteAreaThresholdAlarmByAreaControllerID(serviceCtx, id) - if err != nil { - return fmt.Errorf("删除区域阈值告警失败: %w", err) - } - - // 3. 执行删除 - return s.areaControllerRepo.Delete(serviceCtx, id) -} - -// --- Device Templates --- - -func (s *deviceService) CreateDeviceTemplate(ctx context.Context, req *dto.CreateDeviceTemplateRequest) (*dto.DeviceTemplateResponse, error) { - serviceCtx := logs.AddFuncName(ctx, s.ctx, "CreateDeviceTemplate") - commandsJSON, err := json.Marshal(req.Commands) - if err != nil { - return nil, err - } - - valuesJSON, err := json.Marshal(req.Values) - if err != nil { - return nil, err - } - - deviceTemplate := &models.DeviceTemplate{ - Name: req.Name, - Manufacturer: req.Manufacturer, - Description: req.Description, - Category: req.Category, - Commands: commandsJSON, - Values: valuesJSON, - } - - if err := deviceTemplate.SelfCheck(); err != nil { - return nil, err - } - - if err := s.deviceTemplateRepo.Create(serviceCtx, deviceTemplate); err != nil { - return nil, err - } - - return dto.NewDeviceTemplateResponse(deviceTemplate) -} - -func (s *deviceService) GetDeviceTemplate(ctx context.Context, id uint32) (*dto.DeviceTemplateResponse, error) { - serviceCtx := logs.AddFuncName(ctx, s.ctx, "GetDeviceTemplate") - deviceTemplate, err := s.deviceTemplateRepo.FindByID(serviceCtx, id) - if err != nil { - return nil, err - } - return dto.NewDeviceTemplateResponse(deviceTemplate) -} - -func (s *deviceService) ListDeviceTemplates(ctx context.Context) ([]*dto.DeviceTemplateResponse, error) { - serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListDeviceTemplates") - deviceTemplates, err := s.deviceTemplateRepo.ListAll(serviceCtx) - if err != nil { - return nil, err - } - return dto.NewListDeviceTemplateResponse(deviceTemplates) -} - -func (s *deviceService) UpdateDeviceTemplate(ctx context.Context, id uint32, req *dto.UpdateDeviceTemplateRequest) (*dto.DeviceTemplateResponse, error) { - serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdateDeviceTemplate") - existingDeviceTemplate, err := s.deviceTemplateRepo.FindByID(serviceCtx, id) - if err != nil { - return nil, err - } - - commandsJSON, err := json.Marshal(req.Commands) - if err != nil { - return nil, err - } - - valuesJSON, err := json.Marshal(req.Values) - if err != nil { - return nil, err - } - - existingDeviceTemplate.Name = req.Name - existingDeviceTemplate.Manufacturer = req.Manufacturer - existingDeviceTemplate.Description = req.Description - existingDeviceTemplate.Category = req.Category - existingDeviceTemplate.Commands = commandsJSON - existingDeviceTemplate.Values = valuesJSON - - if err := existingDeviceTemplate.SelfCheck(); err != nil { - return nil, err - } - - if err := s.deviceTemplateRepo.Update(serviceCtx, existingDeviceTemplate); err != nil { - return nil, err - } - - return dto.NewDeviceTemplateResponse(existingDeviceTemplate) -} - -func (s *deviceService) DeleteDeviceTemplate(ctx context.Context, id uint32) error { - serviceCtx := logs.AddFuncName(ctx, s.ctx, "DeleteDeviceTemplate") - - // 1. 检查是否存在 - _, err := s.deviceTemplateRepo.FindByID(serviceCtx, id) - if err != nil { - return err - } - - // 2. 检查是否被使用(业务逻辑) - inUse, err := s.deviceTemplateRepo.IsInUse(serviceCtx, id) - if err != nil { - return err - } - if inUse { - return ErrDeviceTemplateInUse // 返回业务错误 - } - - // 3. 执行删除 - return s.deviceTemplateRepo.Delete(serviceCtx, id) -} diff --git a/internal/app/service/device_template_service.go b/internal/app/service/device_template_service.go new file mode 100644 index 0000000..0628523 --- /dev/null +++ b/internal/app/service/device_template_service.go @@ -0,0 +1,144 @@ +package service + +import ( + "context" + "encoding/json" + + "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/models" + "git.huangwc.com/pig/pig-farm-controller/internal/infra/repository" +) + +// DeviceTemplateService 定义了应用层的设备模板服务接口。 +type DeviceTemplateService interface { + CreateDeviceTemplate(ctx context.Context, req *dto.CreateDeviceTemplateRequest) (*dto.DeviceTemplateResponse, error) + GetDeviceTemplate(ctx context.Context, id uint32) (*dto.DeviceTemplateResponse, error) + ListDeviceTemplates(ctx context.Context) ([]*dto.DeviceTemplateResponse, error) + UpdateDeviceTemplate(ctx context.Context, id uint32, req *dto.UpdateDeviceTemplateRequest) (*dto.DeviceTemplateResponse, error) + DeleteDeviceTemplate(ctx context.Context, id uint32) error +} + +// deviceTemplateService 是 DeviceTemplateService 接口的具体实现。 +type deviceTemplateService struct { + ctx context.Context + deviceTemplateRepo repository.DeviceTemplateRepository +} + +// NewDeviceTemplateService 创建一个新的 DeviceTemplateService 实例。 +func NewDeviceTemplateService( + ctx context.Context, + deviceTemplateRepo repository.DeviceTemplateRepository, +) DeviceTemplateService { + return &deviceTemplateService{ + ctx: ctx, + deviceTemplateRepo: deviceTemplateRepo, + } +} + +func (s *deviceTemplateService) CreateDeviceTemplate(ctx context.Context, req *dto.CreateDeviceTemplateRequest) (*dto.DeviceTemplateResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "CreateDeviceTemplate") + commandsJSON, err := json.Marshal(req.Commands) + if err != nil { + return nil, err + } + + valuesJSON, err := json.Marshal(req.Values) + if err != nil { + return nil, err + } + + deviceTemplate := &models.DeviceTemplate{ + Name: req.Name, + Manufacturer: req.Manufacturer, + Description: req.Description, + Category: req.Category, + Commands: commandsJSON, + Values: valuesJSON, + } + + if err := deviceTemplate.SelfCheck(); err != nil { + return nil, err + } + + if err := s.deviceTemplateRepo.Create(serviceCtx, deviceTemplate); err != nil { + return nil, err + } + + return dto.NewDeviceTemplateResponse(deviceTemplate) +} + +func (s *deviceTemplateService) GetDeviceTemplate(ctx context.Context, id uint32) (*dto.DeviceTemplateResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "GetDeviceTemplate") + deviceTemplate, err := s.deviceTemplateRepo.FindByID(serviceCtx, id) + if err != nil { + return nil, err + } + return dto.NewDeviceTemplateResponse(deviceTemplate) +} + +func (s *deviceTemplateService) ListDeviceTemplates(ctx context.Context) ([]*dto.DeviceTemplateResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListDeviceTemplates") + deviceTemplates, err := s.deviceTemplateRepo.ListAll(serviceCtx) + if err != nil { + return nil, err + } + return dto.NewListDeviceTemplateResponse(deviceTemplates) +} + +func (s *deviceTemplateService) UpdateDeviceTemplate(ctx context.Context, id uint32, req *dto.UpdateDeviceTemplateRequest) (*dto.DeviceTemplateResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdateDeviceTemplate") + existingDeviceTemplate, err := s.deviceTemplateRepo.FindByID(serviceCtx, id) + if err != nil { + return nil, err + } + + commandsJSON, err := json.Marshal(req.Commands) + if err != nil { + return nil, err + } + + valuesJSON, err := json.Marshal(req.Values) + if err != nil { + return nil, err + } + + existingDeviceTemplate.Name = req.Name + existingDeviceTemplate.Manufacturer = req.Manufacturer + existingDeviceTemplate.Description = req.Description + existingDeviceTemplate.Category = req.Category + existingDeviceTemplate.Commands = commandsJSON + existingDeviceTemplate.Values = valuesJSON + + if err := existingDeviceTemplate.SelfCheck(); err != nil { + return nil, err + } + + if err := s.deviceTemplateRepo.Update(serviceCtx, existingDeviceTemplate); err != nil { + return nil, err + } + + return dto.NewDeviceTemplateResponse(existingDeviceTemplate) +} + +func (s *deviceTemplateService) DeleteDeviceTemplate(ctx context.Context, id uint32) error { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "DeleteDeviceTemplate") + + // 1. 检查是否存在 + _, err := s.deviceTemplateRepo.FindByID(serviceCtx, id) + if err != nil { + return err + } + + // 2. 检查是否被使用(业务逻辑) + inUse, err := s.deviceTemplateRepo.IsInUse(serviceCtx, id) + if err != nil { + return err + } + if inUse { + return ErrDeviceTemplateInUse // 返回业务错误 + } + + // 3. 执行删除 + return s.deviceTemplateRepo.Delete(serviceCtx, id) +} diff --git a/internal/core/application.go b/internal/core/application.go index 38c0bfd..6216da6 100644 --- a/internal/core/application.go +++ b/internal/core/application.go @@ -59,6 +59,8 @@ func NewApplication(configPath string) (*Application, error) { appServices.pigBatchService, appServices.monitorService, appServices.deviceService, + appServices.deviceTemplateService, + appServices.areaControllerService, appServices.planService, appServices.userService, appServices.auditService, diff --git a/internal/core/component_initializers.go b/internal/core/component_initializers.go index da28d41..78008c8 100644 --- a/internal/core/component_initializers.go +++ b/internal/core/component_initializers.go @@ -285,6 +285,8 @@ type AppServices struct { pigBatchService service.PigBatchService monitorService service.MonitorService deviceService service.DeviceService + deviceTemplateService service.DeviceTemplateService + areaControllerService service.AreaControllerService planService service.PlanService userService service.UserService auditService service.AuditService @@ -334,11 +336,15 @@ func initAppServices(ctx context.Context, infra *Infrastructure, domainServices deviceService := service.NewDeviceService( logs.AddCompName(baseCtx, "DeviceService"), infra.repos.deviceRepo, - infra.repos.areaControllerRepo, - infra.repos.deviceTemplateRepo, domainServices.deviceOperator, thresholdAlarmService, ) + deviceTemplateService := service.NewDeviceTemplateService(logs.AddCompName(baseCtx, "DeviceTemplateService"), infra.repos.deviceTemplateRepo) + areaControllerService := service.NewAreaControllerService( + logs.AddCompName(baseCtx, "AreaControllerService"), + infra.repos.areaControllerRepo, + thresholdAlarmService, + ) auditService := service.NewAuditService(logs.AddCompName(baseCtx, "AuditService"), infra.repos.userActionLogRepo) planService := service.NewPlanService(logs.AddCompName(baseCtx, "AppPlanService"), domainServices.planService) @@ -356,6 +362,8 @@ func initAppServices(ctx context.Context, infra *Infrastructure, domainServices pigBatchService: pigBatchService, monitorService: monitorService, deviceService: deviceService, + deviceTemplateService: deviceTemplateService, + areaControllerService: areaControllerService, auditService: auditService, planService: planService, userService: userService, diff --git a/internal/domain/device/ota_service.go b/internal/domain/device/ota_service.go index 76a9bfa..076d18e 100644 --- a/internal/domain/device/ota_service.go +++ b/internal/domain/device/ota_service.go @@ -1 +1,37 @@ package device + +import ( + "context" + + "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" + "git.huangwc.com/pig/pig-farm-controller/internal/infra/repository" +) + +// otaServiceImpl 是 OtaService 接口的实现。 +type otaServiceImpl struct { + otaRepo repository.OtaRepository + deviceRepo repository.DeviceRepository +} + +// NewOtaService 创建一个新的 OtaService 实例。 +func NewOtaService(otaRepo repository.OtaRepository, deviceRepo repository.DeviceRepository) OtaService { + return &otaServiceImpl{ + otaRepo: otaRepo, + deviceRepo: deviceRepo, + } +} + +func (o *otaServiceImpl) StartUpgrade(ctx context.Context, areaControllerID uint32, firmwarePath, targetVersion string) (uint32, error) { + //TODO implement me + panic("implement me") +} + +func (o *otaServiceImpl) GetUpgradeProgress(ctx context.Context, taskID uint32) (status models.OTATaskStatus, err error) { + //TODO implement me + panic("implement me") +} + +func (o *otaServiceImpl) StopUpgrade(ctx context.Context, taskID uint32) error { + //TODO implement me + panic("implement me") +} diff --git a/project_structure.txt b/project_structure.txt index 4d4766d..b57b277 100644 --- a/project_structure.txt +++ b/project_structure.txt @@ -50,7 +50,9 @@ internal/app/api/api.go internal/app/api/router.go internal/app/controller/alarm/threshold_alarm_controller.go internal/app/controller/auth_utils.go +internal/app/controller/device/area_controller_controller.go internal/app/controller/device/device_controller.go +internal/app/controller/device/device_template_controller.go internal/app/controller/feed/nutrient_controller.go internal/app/controller/feed/pig_age_stage_controller.go internal/app/controller/feed/pig_breed_controller.go @@ -94,8 +96,10 @@ internal/app/listener/lora_listener.go internal/app/listener/transport.go internal/app/middleware/audit.go internal/app/middleware/auth.go +internal/app/service/area_controller_service.go internal/app/service/audit_service.go internal/app/service/device_service.go +internal/app/service/device_template_service.go internal/app/service/inventory_service.go internal/app/service/monitor_service.go internal/app/service/nutrient_service.go