重构:统一 IoT 模型到 internal/model/ 目录

将所有 IoT 相关的数据模型从 internal/iot/model/ 迁移到 internal/model/,
实现全局统一的模型层架构,符合项目横向分层设计原则。

变更内容:
- 迁移 11 个 IoT 模型文件(carrier, iot_card, device, order, package 等)
- 删除 internal/iot/model/ 目录
- 更新文档中的模型路径引用(25 处)
- 创建重构总结文档
- 归档 OpenSpec 变更为 2026-01-12-refactor-iot-model-location
- 创建 model-organization 规格文档

验证结果:
- 编译通过(go build 成功)
- 静态分析通过(go vet 无错误)
- 代码格式通过(go fmt 无变更)
- 无 Go 代码引用旧路径

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-12 16:01:53 +08:00
parent 034f00e2e7
commit 867e97af11
30 changed files with 1158 additions and 75 deletions

View File

@@ -0,0 +1,162 @@
# model-organization Specification
## Purpose
TBD - created by archiving change refactor-iot-model-location. Update Purpose after archive.
## Requirements
### Requirement: 统一的模型目录
系统 SHALL 将所有数据模型GORM 模型、DTO统一放置在 `internal/model/` 目录下,不按业务模块分散。
**核心原则:**
- **横向分层**模型层Model是全局统一的不按业务模块纵向分割
- **扁平化组织**:所有模型文件直接放在 `internal/model/` 目录下,不创建子目录(除非文件数量超过 50 个)
- **统一导入路径**:所有代码引用模型时统一使用 `internal/model` 导入路径
- **统一包名**:所有模型文件的包名统一为 `package model`
**目录结构:**
```
internal/
├── model/ # 所有数据模型统一在这里
│ ├── account.go # 账户模型
│ ├── shop.go # 店铺模型
│ ├── enterprise.go # 企业模型
│ ├── personal_customer.go # 个人客户模型
│ ├── role.go # 角色模型
│ ├── permission.go # 权限模型
│ ├── carrier.go # 运营商模型IoT 相关)
│ ├── iot_card.go # IoT 卡模型IoT 相关)
│ ├── device.go # 设备模型IoT 相关)
│ ├── order.go # 订单模型IoT 相关)
│ ├── package.go # 套餐模型IoT 相关)
│ └── ... # 其他模型
├── handler/ # Handler 层(按功能分包)
├── service/ # Service 层(按功能分包)
└── store/ # Store 层(按功能分包)
```
#### Scenario: 创建新的 IoT 相关模型
- **WHEN** 开发者需要创建新的 IoT 相关数据模型(如 `SIMCard`
- **THEN** 系统要求开发者在 `internal/model/sim_card.go` 创建模型,而不是在 `internal/iot/model/sim_card.go`
#### Scenario: 引用 IoT 模型
- **WHEN** Service 层或 Store 层需要引用 IoT 卡模型
- **THEN** 系统使用统一的导入路径 `internal/model`,而不是 `internal/iot/model`
#### Scenario: 跨模块引用模型
- **WHEN** 用户模块User需要引用 IoT 卡模型IotCard进行关联查询
- **THEN** 系统允许直接从 `internal/model` 导入 `IotCard`,因为所有模型都在同一个包中
---
### Requirement: 模型文件命名规范
系统 SHALL 遵循统一的模型文件命名规范,确保文件名清晰、一致、易于查找。
**命名规则:**
- 文件名使用小写下划线命名法snake_case`user_account.go``iot_card.go``shop_order.go`
- 文件名应该清晰描述模型的业务含义,不使用缩写(除非是广泛认可的缩写如 `iot``http`
- 一个文件可以包含一个或多个相关模型(如 `iot_card.go` 可以包含 `IotCard``IotCardDTO`
- DTO 模型应该与主模型放在同一个文件中(如 `IotCard``IotCardDTO` 都在 `iot_card.go` 中)
**文件内容结构:**
```go
package model
// IotCard IoT 卡模型GORM 模型)
type IotCard struct {
ID uint `gorm:"column:id;primaryKey" json:"id"`
ICCID string `gorm:"column:iccid;uniqueIndex" json:"iccid"`
// ... 其他字段
}
// TableName 指定表名
func (IotCard) TableName() string {
return "iot_cards"
}
// IotCardDTO IoT 卡 DTO数据传输对象
type IotCardDTO struct {
ID uint `json:"id"`
ICCID string `json:"iccid"`
// ... 其他字段
}
```
#### Scenario: 创建新模型时命名文件
- **WHEN** 开发者创建新的数据模型 `DeviceBinding`
- **THEN** 系统要求文件名为 `device_binding.go`,而不是 `DeviceBinding.go``deviceBinding.go`
#### Scenario: DTO 模型放置位置
- **WHEN** 开发者为 `IotCard` 模型创建 DTO`IotCardDTO`
- **THEN** 系统要求 DTO 定义在同一个文件 `iot_card.go` 中,而不是创建新文件 `iot_card_dto.go`
---
### Requirement: 禁止按业务模块分割模型
系统 SHALL 禁止按业务模块(如 `iot``user``order`)创建独立的模型子目录,所有模型必须扁平化组织在 `internal/model/` 下。
**禁止的目录结构:**
```
internal/
├── model/
│ ├── user/ # ❌ 禁止按业务模块分子目录
│ │ └── user.go
│ ├── iot/ # ❌ 禁止按业务模块分子目录
│ │ └── iot_card.go
│ └── order/ # ❌ 禁止按业务模块分子目录
│ └── order.go
```
```
internal/
├── user/ # ❌ 禁止按业务模块纵向分层
│ ├── model.go
│ ├── handler.go
│ ├── service.go
│ └── store.go
├── iot/ # ❌ 禁止按业务模块纵向分层
│ ├── model/
│ │ └── iot_card.go
│ ├── handler.go
│ ├── service.go
│ └── store.go
```
**正确的目录结构(扁平化):**
```
internal/
├── model/ # ✅ 所有模型扁平化在一个目录
│ ├── user.go
│ ├── iot_card.go
│ └── order.go
├── handler/ # ✅ 横向分层
├── service/ # ✅ 横向分层
└── store/ # ✅ 横向分层
```
**设计理由:**
1. **符合横向分层架构**Handler → Service → Store → Model 是全局分层,不是模块分层
2. **简化导入路径**:所有模型统一使用 `internal/model`,不需要记忆不同模块的路径
3. **便于跨模块引用**:用户模块可以直接引用 IoT 模型,不需要跨包引用
4. **符合 Go 语言惯用设计**包应该按功能组织model而不是按业务模块组织user/model, iot/model
#### Scenario: 代码审查拒绝纵向分层
- **WHEN** 开发者提交 PR创建了 `internal/iot/model/` 目录
- **THEN** 系统要求代码审查拒绝该 PR并要求开发者将模型移动到 `internal/model/`
#### Scenario: 重构现有纵向分层的模型
- **WHEN** 项目中存在 `internal/iot/model/` 目录
- **THEN** 系统要求重构,将所有模型迁移到 `internal/model/`,删除 `internal/iot/model/` 目录