重构:统一 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:
@@ -78,7 +78,7 @@ INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
|
||||
('CTCC', '中国电信', 1);
|
||||
```
|
||||
|
||||
**GORM 模型**: `internal/iot/model/carrier.go:5`
|
||||
**GORM 模型**: `internal/model/carrier.go:5`
|
||||
|
||||
---
|
||||
|
||||
@@ -132,7 +132,7 @@ INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
|
||||
- `owner_type` 和 `owner_id` 组合表示所有权
|
||||
- `enable_polling = false` 的卡不参与轮询检查
|
||||
|
||||
**GORM 模型**: `internal/iot/model/iot_card.go:8`
|
||||
**GORM 模型**: `internal/model/iot_card.go:8`
|
||||
|
||||
---
|
||||
|
||||
@@ -167,7 +167,7 @@ INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
|
||||
- `sim_slots` 取值范围 1-4
|
||||
- 通过 `device_sim_bindings` 表管理设备与 IoT 卡的绑定关系
|
||||
|
||||
**GORM 模型**: `internal/iot/model/device.go:5`
|
||||
**GORM 模型**: `internal/model/device.go:5`
|
||||
|
||||
---
|
||||
|
||||
@@ -203,7 +203,7 @@ INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
|
||||
- 使用虚拟商品编码映射运营商订单
|
||||
- 从上游平台下单,不涉及本地库存管理
|
||||
|
||||
**GORM 模型**: `internal/iot/model/number_card.go:8`
|
||||
**GORM 模型**: `internal/model/number_card.go:8`
|
||||
|
||||
---
|
||||
|
||||
@@ -235,7 +235,7 @@ INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
|
||||
- 套餐系列用于分组管理套餐
|
||||
- 分佣规则可以按套餐系列统一配置
|
||||
|
||||
**GORM 模型**: `internal/iot/model/package.go:7`
|
||||
**GORM 模型**: `internal/model/package.go:7`
|
||||
|
||||
---
|
||||
|
||||
@@ -274,7 +274,7 @@ INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
|
||||
- 停机判断基于 `virtual_data_mb` (虚流量用完即停机)
|
||||
- `package_type = 'formal'` 表示正式套餐,`'addon'` 表示附加套餐
|
||||
|
||||
**GORM 模型**: `internal/iot/model/package.go:23`
|
||||
**GORM 模型**: `internal/model/package.go:23`
|
||||
|
||||
---
|
||||
|
||||
@@ -307,7 +307,7 @@ INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
|
||||
- `retail_price` 是代理的零售价格
|
||||
- 佣金 = `retail_price - cost_price`
|
||||
|
||||
**GORM 模型**: `internal/iot/model/package.go:48`
|
||||
**GORM 模型**: `internal/model/package.go:48`
|
||||
|
||||
---
|
||||
|
||||
@@ -341,7 +341,7 @@ INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
|
||||
- `slot_position` 取值范围 1-4
|
||||
- 同一设备的同一插槽位置只能绑定一张激活状态的 IoT 卡
|
||||
|
||||
**GORM 模型**: `internal/iot/model/package.go:66`
|
||||
**GORM 模型**: `internal/model/package.go:66`
|
||||
|
||||
---
|
||||
|
||||
@@ -385,7 +385,7 @@ INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
|
||||
- `data_usage_mb = real_data_usage_mb + virtual_data_usage_mb`
|
||||
- 轮询检查时更新 `last_package_check_at`
|
||||
|
||||
**GORM 模型**: `internal/iot/model/package.go:85`
|
||||
**GORM 模型**: `internal/model/package.go:85`
|
||||
|
||||
---
|
||||
|
||||
@@ -426,7 +426,7 @@ INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
|
||||
- 可以按卡状态和运营商配置不同的轮询策略
|
||||
- 优先级控制多个配置的执行顺序
|
||||
|
||||
**GORM 模型**: `internal/iot/model/polling.go:7`
|
||||
**GORM 模型**: `internal/model/polling.go:7`
|
||||
|
||||
---
|
||||
|
||||
@@ -459,7 +459,7 @@ INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
|
||||
- `carrier_sync_data` 存储从运营商 Gateway 同步的原始数据
|
||||
- 用于历史查询和统计分析
|
||||
|
||||
**GORM 模型**: `internal/iot/model/data_usage.go:5`
|
||||
**GORM 模型**: `internal/model/data_usage.go:5`
|
||||
|
||||
---
|
||||
|
||||
@@ -510,7 +510,7 @@ INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
|
||||
- `number_card_id` 必须有值
|
||||
- `package_id` 为空
|
||||
|
||||
**GORM 模型**: `internal/iot/model/order.go:11`
|
||||
**GORM 模型**: `internal/model/order.go:11`
|
||||
|
||||
---
|
||||
|
||||
@@ -544,7 +544,7 @@ INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
|
||||
- `agent_path` 存储完整的代理路径,便于查询上级链
|
||||
- 一级代理的 `parent_agent_id` 为 NULL
|
||||
|
||||
**GORM 模型**: `internal/iot/model/commission.go:8`
|
||||
**GORM 模型**: `internal/model/commission.go:8`
|
||||
|
||||
---
|
||||
|
||||
@@ -585,7 +585,7 @@ INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
|
||||
- `unfreeze_mode = 'auto'`: 时间到期 OR 流量达标,满足其一即可自动解冻
|
||||
- `unfreeze_mode = 'manual'`: 需要人工审批解冻
|
||||
|
||||
**GORM 模型**: `internal/iot/model/commission.go:24`
|
||||
**GORM 模型**: `internal/model/commission.go:24`
|
||||
|
||||
---
|
||||
|
||||
@@ -619,7 +619,7 @@ INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
|
||||
51+ 单: 20元/单
|
||||
```
|
||||
|
||||
**GORM 模型**: `internal/iot/model/commission.go:47`
|
||||
**GORM 模型**: `internal/model/commission.go:47`
|
||||
|
||||
---
|
||||
|
||||
@@ -652,7 +652,7 @@ INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
|
||||
- 一次性条件立即发放
|
||||
- 长期条件冻结后按规则解冻
|
||||
|
||||
**GORM 模型**: `internal/iot/model/commission.go:68`
|
||||
**GORM 模型**: `internal/model/commission.go:68`
|
||||
|
||||
---
|
||||
|
||||
@@ -697,7 +697,7 @@ INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
|
||||
```
|
||||
- 满足时间到期 **OR** 流量达标,任一条件满足即可自动解冻
|
||||
|
||||
**GORM 模型**: `internal/iot/model/commission.go:88`
|
||||
**GORM 模型**: `internal/model/commission.go:88`
|
||||
|
||||
---
|
||||
|
||||
@@ -731,7 +731,7 @@ INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
|
||||
- 当分佣规则的 `unfreeze_mode = 'manual'` 时,需要创建审批记录
|
||||
- 审批通过后才能解冻和发放佣金
|
||||
|
||||
**GORM 模型**: `internal/iot/model/commission.go:116`
|
||||
**GORM 模型**: `internal/model/commission.go:116`
|
||||
|
||||
---
|
||||
|
||||
@@ -760,7 +760,7 @@ INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
|
||||
- `template_data` 存储分佣规则的完整配置
|
||||
- 可以基于模板快速创建新的分佣规则
|
||||
|
||||
**GORM 模型**: `internal/iot/model/commission.go:137`
|
||||
**GORM 模型**: `internal/model/commission.go:137`
|
||||
|
||||
---
|
||||
|
||||
@@ -794,7 +794,7 @@ INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
|
||||
- 按月统计与运营商的交易情况
|
||||
- 支持结算和支付状态跟踪
|
||||
|
||||
**GORM 模型**: `internal/iot/model/commission.go:155`
|
||||
**GORM 模型**: `internal/model/commission.go:155`
|
||||
|
||||
---
|
||||
|
||||
@@ -838,7 +838,7 @@ INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
|
||||
}
|
||||
```
|
||||
|
||||
**GORM 模型**: `internal/iot/model/financial.go:8`
|
||||
**GORM 模型**: `internal/model/financial.go:8`
|
||||
|
||||
---
|
||||
|
||||
@@ -869,7 +869,7 @@ INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
|
||||
- 每个代理可以有独立的提现规则
|
||||
- `withdrawal_fee_rate` 为 0.005 表示 0.5% 手续费
|
||||
|
||||
**GORM 模型**: `internal/iot/model/financial.go:31`
|
||||
**GORM 模型**: `internal/model/financial.go:31`
|
||||
|
||||
---
|
||||
|
||||
@@ -907,7 +907,7 @@ INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
|
||||
}
|
||||
```
|
||||
|
||||
**GORM 模型**: `internal/iot/model/financial.go:51`
|
||||
**GORM 模型**: `internal/model/financial.go:51`
|
||||
|
||||
---
|
||||
|
||||
@@ -940,7 +940,7 @@ INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
|
||||
- 用于配置系统的开发能力开关
|
||||
- `capability_config` 存储能力的详细配置
|
||||
|
||||
**GORM 模型**: `internal/iot/model/system.go:5`
|
||||
**GORM 模型**: `internal/model/system.go:5`
|
||||
|
||||
---
|
||||
|
||||
@@ -977,7 +977,7 @@ INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
|
||||
2. 管理员审核 (status=2/3)
|
||||
3. 审核通过后完成换卡 (status=4)
|
||||
|
||||
**GORM 模型**: `internal/iot/model/system.go:25`
|
||||
**GORM 模型**: `internal/model/system.go:25`
|
||||
|
||||
---
|
||||
|
||||
|
||||
252
docs/refactor-iot-model-location/重构总结.md
Normal file
252
docs/refactor-iot-model-location/重构总结.md
Normal file
@@ -0,0 +1,252 @@
|
||||
# IoT 模型位置重构 - 总结文档
|
||||
|
||||
## 📋 变更概述
|
||||
|
||||
**变更 ID**: `refactor-iot-model-location`
|
||||
|
||||
**变更类型**: 架构重构
|
||||
|
||||
**实施日期**: 2026-01-12
|
||||
|
||||
**状态**: ✅ 已完成
|
||||
|
||||
## 🎯 重构动机
|
||||
|
||||
### 问题背景
|
||||
|
||||
在实施 IoT SIM 管理模块时,IoT 相关的数据模型被放置在 `internal/iot/model/` 目录下,这与项目的其他模型(Shop、Account、Enterprise 等)不一致。其他模型都统一放在 `internal/model/` 目录下。
|
||||
|
||||
### 存在的问题
|
||||
|
||||
1. **目录结构不一致**
|
||||
- IoT 模型使用 `internal/iot/model/`
|
||||
- 其他模型使用 `internal/model/`
|
||||
- 导致项目结构混乱,违反统一性原则
|
||||
|
||||
2. **违反项目架构约定**
|
||||
- 项目采用严格的横向四层架构:Handler → Service → Store → Model
|
||||
- Model 层应该是全局统一的,不应该按业务模块分散
|
||||
- 当前的 `internal/iot/model/` 违反了横向分层原则
|
||||
|
||||
3. **导入路径冗余**
|
||||
- 引用 IoT 模型时需要写 `internal/iot/model`
|
||||
- 引用其他模型只需要写 `internal/model`
|
||||
- 增加了开发者的认知负担
|
||||
|
||||
4. **违反 Go 语言惯用设计**
|
||||
- Go 推荐扁平化包结构,避免深层嵌套
|
||||
- 包应该按功能组织,不是按业务模块分散
|
||||
- `internal/iot/model` 是不必要的复杂性
|
||||
|
||||
## 🔧 实施过程
|
||||
|
||||
### 迁移文件清单
|
||||
|
||||
共迁移 11 个 IoT 模型文件:
|
||||
|
||||
| 序号 | 文件名 | 说明 |
|
||||
|------|--------|------|
|
||||
| 1 | carrier.go | 运营商模型 |
|
||||
| 2 | commission.go | 佣金模型 |
|
||||
| 3 | data_usage.go | 流量使用记录模型 |
|
||||
| 4 | device.go | 设备模型 |
|
||||
| 5 | financial.go | 财务记录模型 |
|
||||
| 6 | iot_card.go | IoT 卡模型 |
|
||||
| 7 | number_card.go | 号卡模型 |
|
||||
| 8 | order.go | 订单模型 |
|
||||
| 9 | package.go | 套餐模型 |
|
||||
| 10 | polling.go | 轮询配置模型 |
|
||||
| 11 | system.go | 系统配置模型 |
|
||||
|
||||
### 实施步骤
|
||||
|
||||
1. **准备工作**
|
||||
- ✅ 确认 `internal/model/` 目录存在且可写
|
||||
- ✅ 确认 `internal/iot/model/` 包含 11 个模型文件
|
||||
- ✅ 搜索并确认无 Go 代码引用旧路径
|
||||
|
||||
2. **迁移执行**
|
||||
- ✅ 使用 `git mv` 命令移动所有 11 个文件到 `internal/model/`
|
||||
- ✅ Git 正确识别了所有文件的重命名(保留文件历史)
|
||||
|
||||
3. **清理工作**
|
||||
- ✅ 验证 `internal/iot/model/` 目录为空
|
||||
- ✅ 删除空的 `internal/iot/model/` 目录
|
||||
- ✅ 保留 `internal/iot/` 目录(用于未来的 Handler/Service/Store 层)
|
||||
|
||||
4. **验证工作**
|
||||
- ✅ 确认项目中无 `internal/iot/model` 的 Go 代码引用
|
||||
- ✅ 验证所有模型文件包名统一为 `package model`
|
||||
- ✅ 运行 `go fmt ./...` 格式检查通过
|
||||
- ✅ 运行 `go vet ./...` 静态分析通过
|
||||
- ✅ 运行 `go build` 编译成功
|
||||
|
||||
5. **文档更新**
|
||||
- ✅ 更新 `docs/iot-sim-management/表结构详细说明.md` 中的 25 处引用
|
||||
- ✅ 更新 OpenSpec 变更文档(tasks.md、proposal.md)
|
||||
|
||||
## 📊 影响范围
|
||||
|
||||
### 代码影响
|
||||
|
||||
- **Go 代码**: 零影响
|
||||
- 经过搜索,项目中没有任何 Go 代码引用 `internal/iot/model`
|
||||
- IoT 模块尚未完全实现业务逻辑层,因此无代码依赖
|
||||
|
||||
- **文档影响**: 25 处引用更新
|
||||
- `docs/iot-sim-management/表结构详细说明.md` 中的模型路径引用已全部更新
|
||||
|
||||
- **数据库影响**: 无影响
|
||||
- 模型位置变更不影响数据库表结构
|
||||
- 数据库迁移脚本不受影响
|
||||
|
||||
- **API 影响**: 无影响
|
||||
- 模型位置变更不影响 API 接口定义
|
||||
|
||||
### 架构改进
|
||||
|
||||
**重构前的目录结构:**
|
||||
```
|
||||
internal/
|
||||
├── model/ # 大部分模型
|
||||
│ ├── account.go
|
||||
│ ├── shop.go
|
||||
│ └── ...
|
||||
└── iot/
|
||||
└── model/ # IoT 模型单独存放 ❌
|
||||
├── carrier.go
|
||||
└── ...
|
||||
```
|
||||
|
||||
**重构后的目录结构:**
|
||||
```
|
||||
internal/
|
||||
├── model/ # 统一的模型层 ✅
|
||||
│ ├── account.go
|
||||
│ ├── shop.go
|
||||
│ ├── carrier.go # IoT 模型已迁移
|
||||
│ ├── iot_card.go # IoT 模型已迁移
|
||||
│ └── ...
|
||||
└── iot/ # 保留用于未来的业务逻辑层
|
||||
├── handler.go # (未来)IoT Handler 层
|
||||
├── service.go # (未来)IoT Service 层
|
||||
└── store.go # (未来)IoT Store 层
|
||||
```
|
||||
|
||||
## ✅ 验证结果
|
||||
|
||||
### 验收标准验证
|
||||
|
||||
| 验收标准 | 状态 | 验证方法 |
|
||||
|----------|------|----------|
|
||||
| 所有 IoT 模型文件已迁移到 `internal/model/` | ✅ | `ls internal/model/` 包含全部 11 个文件 |
|
||||
| `internal/iot/model/` 目录已删除 | ✅ | `test ! -d internal/iot/model/` 返回成功 |
|
||||
| 项目可以正常编译 | ✅ | `go build ./cmd/api` 编译成功 |
|
||||
| 所有测试通过 | ✅ | 项目暂无测试 |
|
||||
| 项目中不存在 `internal/iot/model` 的引用 | ✅ | 已更新文档中的引用 |
|
||||
|
||||
### 代码质量验证
|
||||
|
||||
| 验证项 | 状态 | 结果 |
|
||||
|--------|------|------|
|
||||
| Go 格式检查 | ✅ | `go fmt ./...` 通过 |
|
||||
| 静态分析 | ✅ | `go vet ./...` 无错误 |
|
||||
| 编译验证 | ✅ | `go build` 成功 |
|
||||
| 包名一致性 | ✅ | 所有文件包名统一为 `package model` |
|
||||
|
||||
### Git 历史保留
|
||||
|
||||
- ✅ 使用 `git mv` 命令移动文件,保留了完整的 Git 历史
|
||||
- ✅ 可以使用 `git log --follow <file>` 追踪文件的完整历史
|
||||
|
||||
## 📈 收益总结
|
||||
|
||||
### 直接收益
|
||||
|
||||
1. **架构一致性** ✅
|
||||
- 所有模型统一放在 `internal/model/` 目录
|
||||
- 符合项目横向四层架构约定
|
||||
- 提高了代码组织的可预测性
|
||||
|
||||
2. **简化导入路径** ✅
|
||||
- 所有模型统一使用 `internal/model` 导入
|
||||
- 减少了认知负担和开发成本
|
||||
|
||||
3. **符合 Go 惯用设计** ✅
|
||||
- 扁平化包结构
|
||||
- 按功能组织,不是按业务模块分散
|
||||
- 提高了 Go 代码的地道性(idiomatic Go)
|
||||
|
||||
4. **提高可维护性** ✅
|
||||
- 开发者只需要在一个地方查找所有数据模型
|
||||
- 降低了新开发者的上手难度
|
||||
- 避免了"模型应该放在哪里"的困惑
|
||||
|
||||
### 长期收益
|
||||
|
||||
1. **可扩展性**
|
||||
- 为未来的 IoT 业务逻辑层(Handler/Service/Store)预留了清晰的位置
|
||||
- 符合项目长期架构演进方向
|
||||
|
||||
2. **代码质量**
|
||||
- 统一的架构约定有助于保持代码质量
|
||||
- 减少了技术债务的累积
|
||||
|
||||
3. **团队协作**
|
||||
- 明确的架构约定减少了团队内部的讨论成本
|
||||
- 提高了代码审查的效率
|
||||
|
||||
## 📝 经验教训
|
||||
|
||||
### 做得好的地方
|
||||
|
||||
1. **提前搜索引用**
|
||||
- 在迁移前搜索了所有引用,确认无代码依赖
|
||||
- 降低了重构风险
|
||||
|
||||
2. **使用 Git 工具**
|
||||
- 使用 `git mv` 保留了文件历史
|
||||
- 便于未来追溯和回滚
|
||||
|
||||
3. **完整的验证流程**
|
||||
- 编译、静态分析、格式检查一个都没少
|
||||
- 确保了重构的安全性
|
||||
|
||||
4. **文档同步更新**
|
||||
- 及时更新了相关文档中的引用
|
||||
- 避免了文档与代码不一致的问题
|
||||
|
||||
### 可以改进的地方
|
||||
|
||||
1. **更早发现问题**
|
||||
- 如果在 IoT 模块设计初期就遵循统一的架构约定,就不需要这次重构
|
||||
- 建议:在代码审查时严格检查架构约定
|
||||
|
||||
2. **自动化检查**
|
||||
- 可以添加 CI 检查,防止模型被放在错误的位置
|
||||
- 建议:添加 linter 规则检查目录结构
|
||||
|
||||
## 🔗 相关文档
|
||||
|
||||
- **变更提案**: `openspec/changes/refactor-iot-model-location/proposal.md`
|
||||
- **设计文档**: `openspec/changes/refactor-iot-model-location/design.md`
|
||||
- **任务清单**: `openspec/changes/refactor-iot-model-location/tasks.md`
|
||||
- **项目架构规范**: `openspec/project.md`
|
||||
- **IoT SIM 管理规格**: `openspec/specs/iot-card/spec.md`
|
||||
|
||||
## 🎉 总结
|
||||
|
||||
本次重构成功地将 11 个 IoT 模型文件从 `internal/iot/model/` 迁移到 `internal/model/`,实现了项目架构的统一性和一致性。
|
||||
|
||||
**重构特点:**
|
||||
- ✅ 零代码影响(无 Go 代码依赖)
|
||||
- ✅ 完整的验证流程(编译、静态分析、格式检查)
|
||||
- ✅ 保留了完整的 Git 历史
|
||||
- ✅ 同步更新了相关文档
|
||||
|
||||
**架构改进:**
|
||||
- ✅ 符合横向四层架构约定
|
||||
- ✅ 符合 Go 语言惯用设计
|
||||
- ✅ 提高了代码可维护性和可扩展性
|
||||
|
||||
本次重构为项目的长期健康发展奠定了坚实的基础。
|
||||
Reference in New Issue
Block a user