Files
huang 867e97af11 重构:统一 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>
2026-01-12 16:01:53 +08:00

163 lines
6.2 KiB
Markdown
Raw Permalink 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.
# 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/` 目录