重构:统一 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:
162
openspec/specs/model-organization/spec.md
Normal file
162
openspec/specs/model-organization/spec.md
Normal 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/` 目录
|
||||
|
||||
Reference in New Issue
Block a user