Files
pig-farm-controller/design/verification-before-device-deletion/refactor_id_conversion.md

4.2 KiB
Raw Blame History

重构方案:将 ID 类型转换逻辑迁移至 Controller 层

1. 目标

将所有通过 URL 路径传入的 idstring 类型),其到 uint 类型的转换和验证逻辑,从 Service业务逻辑层统一迁移至 Controller控制器层。

2. 动机

当前实现中Controller 将从 URL 获取的 string 类型的 ID 直接传递给 Service 层,由 Service 层负责使用 strconv.ParseUint 进行类型转换。

这种模式存在以下问题:

  • 职责不清Service 层被迫处理了本应属于输入验证和转换的逻辑,而这部分工作更贴近 Controller 的职责。
  • Service 不纯粹:业务核心逻辑与原始输入(字符串)耦合,降低了 Service 的可复用性。理想情况下Service 的接口应该只处理内部定义的、类型安全的数据。
  • 延迟的错误处理:对于一个无效的 ID如 "abc"),请求会穿透到 Service 层才会失败,而这种格式错误在 Controller 层就应该被拦截。

通过本次重构,我们将实现:

  • 职责分离Controller 负责处理 HTTP 请求的原始数据验证、转换Service 负责处理核心业务。
  • 接口清晰Service 层的所有方法将只接受类型安全的 uint 作为 ID使其意图更加明确。
  • 快速失败:无效的 ID 将在 Controller 层被立即拒绝,并返回 400 Bad Request,提高了系统的健壮性。

3. 详细实施步骤

第 1 步:修改 device_service.go

3.1 修改 DeviceService 接口

所有接收 id string 参数的方法签名,全部修改为接收 id uint

受影响的方法列表:

  • GetDevice(id string) -> GetDevice(id uint)
  • UpdateDevice(id string, ...) -> UpdateDevice(id uint, ...)
  • DeleteDevice(id string) -> DeleteDevice(id uint)
  • ManualControl(id string, ...) -> ManualControl(id uint, ...)
  • GetAreaController(id string) -> GetAreaController(id uint)
  • UpdateAreaController(id string, ...) -> UpdateAreaController(id uint, ...)
  • DeleteAreaController(id string) -> DeleteAreaController(id uint)
  • GetDeviceTemplate(id string) -> GetDeviceTemplate(id uint)
  • UpdateDeviceTemplate(id string, ...) -> UpdateDeviceTemplate(id uint, ...)
  • DeleteDeviceTemplate(id string) -> DeleteDeviceTemplate(id uint)

3.2 修改 deviceService 实现

deviceService 的方法实现中,移除所有 strconv.ParseUint 的调用,直接使用传入的 uint 类型的 ID。

示例 (DeleteDeviceTemplate):

修改前:

func (s *deviceService) DeleteDeviceTemplate(id string) error {
    idUint, err := strconv.ParseUint(id, 10, 64)
    if err != nil {
        return fmt.Errorf("无效的ID格式: %w", err)
    }
    dtID := uint(idUint)
    // ... 业务逻辑
}

修改后:

func (s *deviceService) DeleteDeviceTemplate(id uint) error {
    // 直接使用 id
    // ... 业务逻辑
}

第 2 步:修改 device_controller.go

在所有调用受影响 Service 方法的 Controller 方法中,增加 ID 的转换和错误处理逻辑。

示例 (DeleteDeviceTemplate):

修改前:

func (c *Controller) DeleteDeviceTemplate(ctx echo.Context) error {
    const actionType = "删除设备模板"
    dtID := ctx.Param("id") // dtID is a string

    if err := c.deviceService.DeleteDeviceTemplate(dtID); err != nil {
        // ... 错误处理
    }
    // ... 成功处理
}

修改后:

func (c *Controller) DeleteDeviceTemplate(ctx echo.Context) error {
    const actionType = "删除设备模板"
    idStr := ctx.Param("id")

    // 在 Controller 层进行转换和验证
    idUint, err := strconv.ParseUint(idStr, 10, 64)
    if err != nil {
        c.logger.Warnf("%s: 无效的ID格式: %s", actionType, idStr)
        return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的ID格式", actionType, "ID格式错误", idStr)
    }
    dtID := uint(idUint)

    // 调用 Service传入 uint 类型的 ID
    if err := c.deviceService.DeleteDeviceTemplate(dtID); err != nil {
        // ... 错误处理 (保持不变)
    }

    // ... 成功处理
}

此模式将应用于所有受影响的 Controller 方法。