Files
junhong_cmp_fiber/openspec/changes/archive/2026-01-12-fix-iot-models-violations/tasks.md
huang 2150fb6ab9 重构:完善 IoT 模型架构规范和数据库设计
- 完善 GORM 模型规范:货币字段使用 int64(分为单位)、JSONB 字段规范、模型结构规范
- 修复所有 IoT 模型的架构违规问题
- 更新 CLAUDE.md 开发指南,补充完整的数据库设计规范和模型示例
- 添加数据库迁移脚本(000006)用于架构重构
- 归档 OpenSpec 变更文档(2026-01-12-fix-iot-models-violations)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-12 17:43:12 +08:00

21 KiB
Raw Blame History

Tasks

本文档列出修复 IoT 模型架构违规所需的所有任务,按优先级和依赖关系排序。

阶段 1: 核心业务实体模型修复(必须优先完成)

Task 1.1: 修复 IoT 卡模型 (IotCard)

文件: internal/model/iot_card.go

修改内容:

  • 嵌入 gorm.ModelBaseModel
  • 移除手动定义的 IDCreatedAtUpdatedAt 字段
  • 所有字段显式指定 column 标签
  • 金额字段(CostPriceDistributePrice)从 float64 改为 int64,数据库类型从 decimal(10,2) 改为 bigint
  • 表名从 iot_cards 改为 tb_iot_card
  • ICCID 唯一索引添加 where:deleted_at IS NULL
  • 所有关联 ID 字段(CarrierIDOwnerID)添加 index 标签
  • 完善所有字段的中文注释

验证方法:

  • 运行 go build 确保编译通过
  • 使用 gofmt 格式化代码
  • Account 模型对比,确保风格一致

Task 1.2: 修复设备模型 (Device)

文件: internal/model/device.go

修改内容:

  • 嵌入 gorm.ModelBaseModel
  • 移除手动定义的 IDCreatedAtUpdatedAt 字段
  • 所有字段显式指定 column 标签
  • 表名从 devices 改为 tb_device
  • DeviceNo 唯一索引添加 where:deleted_at IS NULL
  • 所有关联 ID 字段(OwnerID)添加 index 标签
  • 完善所有字段的中文注释

验证方法:

  • 运行 go build 确保编译通过
  • 使用 gofmt 格式化代码

Task 1.3: 修复号卡模型 (NumberCard)

文件: internal/model/number_card.go

修改内容:

  • 嵌入 gorm.ModelBaseModel
  • 移除手动定义的 IDCreatedAtUpdatedAt 字段
  • 所有字段显式指定 column 标签
  • 金额字段(Price)从 float64 改为 int64,数据库类型从 decimal(10,2) 改为 bigint
  • 表名从 number_cards 改为 tb_number_card
  • VirtualProductCode 唯一索引添加 where:deleted_at IS NULL
  • 关联 ID 字段(AgentID)添加 index 标签
  • 完善所有字段的中文注释

验证方法:

  • 运行 go build 确保编译通过
  • 使用 gofmt 格式化代码

Task 1.4: 修复运营商模型 (Carrier)

文件: internal/model/carrier.go

修改内容:

  • 嵌入 gorm.ModelBaseModel
  • 移除手动定义的 IDCreatedAtUpdatedAt 字段
  • 所有字段显式指定 column 标签
  • 表名从 carriers 改为 tb_carrier
  • CarrierCode 唯一索引添加 where:deleted_at IS NULL
  • 完善所有字段的中文注释

验证方法:

  • 运行 go build 确保编译通过
  • 使用 gofmt 格式化代码

阶段 2: 套餐和订单模型修复

Task 2.1: 修复套餐系列模型 (PackageSeries)

文件: internal/model/package.go

修改内容:

  • 嵌入 gorm.ModelBaseModel
  • 移除手动定义的 IDCreatedAtUpdatedAt 字段
  • 所有字段显式指定 column 标签
  • 表名从 package_series 改为 tb_package_series
  • SeriesCode 唯一索引添加 where:deleted_at IS NULL
  • 完善所有字段的中文注释

验证方法:

  • 运行 go build 确保编译通过

Task 2.2: 修复套餐模型 (Package)

文件: internal/model/package.go

修改内容:

  • 嵌入 gorm.ModelBaseModel
  • 移除手动定义的 IDCreatedAtUpdatedAt 字段
  • 所有字段显式指定 column 标签
  • 金额字段(Price)从 float64 改为 int64,数据库类型从 decimal(10,2) 改为 bigint
  • 表名从 packages 改为 tb_package
  • PackageCode 唯一索引添加 where:deleted_at IS NULL
  • 关联 ID 字段(SeriesID)添加 index 标签
  • 完善所有字段的中文注释

验证方法:

  • 运行 go build 确保编译通过

Task 2.3: 修复代理套餐分配模型 (AgentPackageAllocation)

文件: internal/model/package.go

修改内容:

  • 嵌入 gorm.ModelBaseModel
  • 移除手动定义的 IDCreatedAtUpdatedAt 字段
  • 所有字段显式指定 column 标签
  • 金额字段(CostPriceRetailPrice)从 float64 改为 int64
  • 表名从 agent_package_allocations 改为 tb_agent_package_allocation
  • 关联 ID 字段(AgentIDPackageID)添加 index 标签
  • 完善所有字段的中文注释

验证方法:

  • 运行 go build 确保编译通过

Task 2.4: 修复套餐使用情况模型 (PackageUsage)

文件: internal/model/package.go

修改内容:

  • 嵌入 gorm.ModelBaseModel
  • 移除手动定义的 IDCreatedAtUpdatedAt 字段
  • 所有字段显式指定 column 标签
  • 表名从 package_usages 改为 tb_package_usage
  • 关联 ID 字段(OrderIDPackageIDIotCardIDDeviceID)添加 index 标签
  • 完善所有字段的中文注释

验证方法:

  • 运行 go build 确保编译通过

Task 2.5: 修复设备-SIM 卡绑定模型 (DeviceSimBinding)

文件: internal/model/package.go

修改内容:

  • 嵌入 gorm.ModelBaseModel
  • 移除手动定义的 IDCreatedAtUpdatedAt 字段
  • 所有字段显式指定 column 标签
  • 表名从 device_sim_bindings 改为 tb_device_sim_binding
  • 添加复合索引:DeviceIDSlotPosition 使用 index:idx_device_slot
  • 关联 ID 字段(IotCardID)添加独立 index 标签
  • 完善所有字段的中文注释

验证方法:

  • 运行 go build 确保编译通过

Task 2.6: 修复订单模型 (Order)

文件: internal/model/order.go

修改内容:

  • 嵌入 gorm.ModelBaseModel
  • 移除手动定义的 IDCreatedAtUpdatedAt 字段
  • 所有字段显式指定 column 标签
  • 金额字段(Amount)从 float64 改为 int64
  • CarrierOrderDatapq.StringArray 改为 datatypes.JSON,添加 import "gorm.io/datatypes"
  • 表名从 orders 改为 tb_order
  • OrderNo 唯一索引添加 where:deleted_at IS NULL
  • 关联 ID 字段(IotCardIDDeviceIDNumberCardIDPackageIDUserIDAgentID)添加 index 标签
  • 完善所有字段的中文注释

验证方法:

  • 运行 go build 确保编译通过
  • 检查 datatypes.JSON 导入是否正确

阶段 3: 分佣系统模型修复

Task 3.1: 修复代理层级模型 (AgentHierarchy)

文件: internal/model/commission.go

修改内容:

  • 嵌入 gorm.ModelBaseModel
  • 移除手动定义的 IDCreatedAtUpdatedAt 字段
  • 所有字段显式指定 column 标签
  • 表名从 agent_hierarchies 改为 tb_agent_hierarchy
  • AgentID 唯一索引添加 where:deleted_at IS NULL
  • 关联 ID 字段(ParentAgentID)添加 index 标签
  • 完善所有字段的中文注释

验证方法:

  • 运行 go build 确保编译通过

Task 3.2: 修复分佣规则模型 (CommissionRule)

文件: internal/model/commission.go

修改内容:

  • 嵌入 gorm.ModelBaseModel
  • 移除手动定义的 IDCreatedAtUpdatedAt 字段
  • 所有字段显式指定 column 标签
  • 金额字段(CommissionValue)从 float64 改为 int64
  • 表名从 commission_rules 改为 tb_commission_rule
  • 关联 ID 字段(AgentIDSeriesIDPackageID)添加 index 标签
  • 完善所有字段的中文注释

验证方法:

  • 运行 go build 确保编译通过

Task 3.3: 修复阶梯分佣配置模型 (CommissionLadder)

文件: internal/model/commission.go

修改内容:

  • 嵌入 gorm.ModelBaseModel
  • 移除手动定义的 IDCreatedAtUpdatedAt 字段
  • 所有字段显式指定 column 标签
  • 金额字段(CommissionValue)从 float64 改为 int64
  • 表名从 commission_ladder 改为 tb_commission_ladder
  • 关联 ID 字段(RuleID)添加 index 标签
  • 完善所有字段的中文注释

验证方法:

  • 运行 go build 确保编译通过

Task 3.4: 修复组合分佣条件模型 (CommissionCombinedCondition)

文件: internal/model/commission.go

修改内容:

  • 嵌入 gorm.ModelBaseModel
  • 移除手动定义的 IDCreatedAtUpdatedAt 字段
  • 所有字段显式指定 column 标签
  • 金额字段(OneTimeCommissionValueLongTermCommissionValue)从 float64 改为 int64
  • 表名从 commission_combined_conditions 改为 tb_commission_combined_condition
  • RuleID 唯一索引添加 where:deleted_at IS NULL
  • 完善所有字段的中文注释

验证方法:

  • 运行 go build 确保编译通过

Task 3.5: 修复分佣记录模型 (CommissionRecord)

文件: internal/model/commission.go

修改内容:

  • 嵌入 gorm.ModelBaseModel
  • 移除手动定义的 IDCreatedAtUpdatedAt 字段
  • 所有字段显式指定 column 标签
  • 金额字段(Amount)从 float64 改为 int64
  • 表名从 commission_records 改为 tb_commission_record
  • 关联 ID 字段(AgentIDOrderIDRuleID)添加 index 标签
  • 完善所有字段的中文注释

验证方法:

  • 运行 go build 确保编译通过

Task 3.6: 修复分佣审批模型 (CommissionApproval)

文件: internal/model/commission.go

修改内容:

  • 嵌入 gorm.ModelBaseModel
  • 移除手动定义的 IDCreatedAtUpdatedAt 字段
  • 所有字段显式指定 column 标签
  • 表名从 commission_approvals 改为 tb_commission_approval
  • 关联 ID 字段(CommissionRecordIDApproverID)添加 index 标签
  • 完善所有字段的中文注释

验证方法:

  • 运行 go build 确保编译通过

Task 3.7: 修复分佣模板模型 (CommissionTemplate)

文件: internal/model/commission.go

修改内容:

  • 嵌入 gorm.ModelBaseModel
  • 移除手动定义的 IDCreatedAtUpdatedAt 字段
  • 所有字段显式指定 column 标签
  • 金额字段(CommissionValue)从 float64 改为 int64
  • 表名从 commission_templates 改为 tb_commission_template
  • TemplateName 唯一索引添加 where:deleted_at IS NULL
  • 完善所有字段的中文注释

验证方法:

  • 运行 go build 确保编译通过

Task 3.8: 修复运营商结算模型 (CarrierSettlement)

文件: internal/model/commission.go

修改内容:

  • 嵌入 gorm.ModelBaseModel
  • 移除手动定义的 IDCreatedAtUpdatedAt 字段
  • 所有字段显式指定 column 标签
  • 金额字段(SettlementAmount)从 float64 改为 int64,数据库类型从 decimal(18,2) 改为 bigint
  • 表名从 carrier_settlements 改为 tb_carrier_settlement
  • CommissionRecordID 唯一索引添加 where:deleted_at IS NULL
  • 关联 ID 字段(AgentID)添加 index 标签
  • 完善所有字段的中文注释

验证方法:

  • 运行 go build 确保编译通过

阶段 4: 财务和系统模型修复

Task 4.1: 修复佣金提现申请模型 (CommissionWithdrawalRequest)

文件: internal/model/financial.go

修改内容:

  • 嵌入 gorm.ModelBaseModel
  • 移除手动定义的 IDCreatedAtUpdatedAt 字段
  • 所有字段显式指定 column 标签
  • 金额字段(AmountFeeActualAmount)从 float64 改为 int64,数据库类型从 decimal(18,2) 改为 bigint
  • AccountInfopq.StringArray 改为 datatypes.JSON
  • 表名从 commission_withdrawal_requests 改为 tb_commission_withdrawal_request
  • 关联 ID 字段(AgentIDApprovedBy)添加 index 标签
  • 完善所有字段的中文注释

验证方法:

  • 运行 go build 确保编译通过
  • 检查 datatypes.JSON 导入是否正确

Task 4.2: 修复佣金提现设置模型 (CommissionWithdrawalSetting)

文件: internal/model/financial.go

修改内容:

  • 嵌入 gorm.ModelBaseModel
  • 移除手动定义的 IDCreatedAtUpdatedAt 字段
  • 所有字段显式指定 column 标签
  • 金额字段(MinWithdrawalAmount)从 float64 改为 int64,数据库类型从 decimal(10,2) 改为 bigint
  • 表名从 commission_withdrawal_settings 改为 tb_commission_withdrawal_setting
  • 完善所有字段的中文注释

验证方法:

  • 运行 go build 确保编译通过

Task 4.3: 修复收款商户设置模型 (PaymentMerchantSetting)

文件: internal/model/financial.go

修改内容:

  • 嵌入 gorm.ModelBaseModel
  • 移除手动定义的 IDCreatedAtUpdatedAt 字段
  • 所有字段显式指定 column 标签
  • 表名从 payment_merchant_settings 改为 tb_payment_merchant_setting
  • 关联 ID 字段(UserID)添加 index 标签
  • 完善所有字段的中文注释

验证方法:

  • 运行 go build 确保编译通过

Task 4.4: 修复开发能力配置模型 (DevCapabilityConfig)

文件: internal/model/system.go

修改内容:

  • 嵌入 gorm.ModelBaseModel
  • 移除手动定义的 IDCreatedAtUpdatedAt 字段
  • 所有字段显式指定 column 标签
  • 表名从 dev_capability_configs 改为 tb_dev_capability_config
  • AppID 唯一索引添加 where:deleted_at IS NULL
  • 关联 ID 字段(UserID)添加 index 标签
  • 完善所有字段的中文注释

验证方法:

  • 运行 go build 确保编译通过

Task 4.5: 修复换卡申请模型 (CardReplacementRequest)

文件: internal/model/system.go

修改内容:

  • 嵌入 gorm.ModelBaseModel
  • 移除手动定义的 IDCreatedAtUpdatedAt 字段
  • 所有字段显式指定 column 标签
  • 表名从 card_replacement_requests 改为 tb_card_replacement_request
  • 关联 ID 字段(UserIDApprovedBy)添加 index 标签
  • 完善所有字段的中文注释

验证方法:

  • 运行 go build 确保编译通过

Task 4.6: 修复轮询配置模型 (PollingConfig)

文件: internal/model/polling.go

修改内容:

  • 嵌入 gorm.ModelBaseModel
  • 移除手动定义的 IDCreatedAtUpdatedAt 字段
  • 所有字段显式指定 column 标签
  • 表名从 polling_configs 改为 tb_polling_config
  • ConfigName 唯一索引添加 where:deleted_at IS NULL
  • 关联 ID 字段(CarrierID)添加 index 标签
  • 完善所有字段的中文注释

验证方法:

  • 运行 go build 确保编译通过

Task 4.7: 修复流量使用记录模型 (DataUsageRecord)

文件: internal/model/data_usage.go

修改内容:

  • 不嵌入 gorm.Model(简化模型,只包含 ID 和 CreatedAt
  • 不嵌入 BaseModel(日志表不需要审计)
  • 保留 IDCreatedAt 字段,移除 UpdatedAt
  • 所有字段显式指定 column 标签
  • 表名从 data_usage_records 改为 tb_data_usage_record
  • 关联 ID 字段(IotCardID)添加 index 标签
  • 完善所有字段的中文注释

验证方法:

  • 运行 go build 确保编译通过
  • 确认模型不包含 UpdatedAtDeletedAt

阶段 5: 验证和测试

Task 5.1: 编译验证

内容:

  • 运行 go build ./... 确保所有模型文件编译通过
  • 运行 gofmt -w internal/model/ 格式化所有模型文件
  • 运行 go vet ./internal/model/ 静态分析检查

依赖: 所有模型修复任务完成

验证方法:

  • 无编译错误
  • 无静态分析警告

Task 5.2: 模型定义一致性检查

内容:

  • 手动检查所有模型是否遵循规范(参考验证清单)
  • 对比 Account 模型,确保风格一致
  • 检查所有金额字段是否使用 int64 类型
  • 检查所有表名是否使用 tb_ 前缀 + 单数
  • 检查所有唯一索引是否包含 where:deleted_at IS NULL

依赖: Task 5.1

验证方法:

  • 完成验证清单(设计文档第 8 节)

Task 5.3: 生成数据库迁移脚本(可选)

内容:

  • 如果 IoT 模块尚未创建迁移脚本,跳过此任务
  • 如果已有迁移脚本,生成新的迁移脚本或修改现有脚本
  • 包含表重命名、字段修改、索引创建等 SQL 语句

依赖: Task 5.2

验证方法:

  • 在开发环境测试迁移脚本
  • 确认所有表和字段正确创建

Task 5.4: 文档更新

内容:

  • 更新 IoT SIM 管理提案(openspec/changes/archive/2026-01-12-iot-sim-management/)的模型定义部分(可选)
  • docs/ 目录创建模型修复总结文档(可选)
  • 更新 README.md 添加模型规范说明(可选)

依赖: Task 5.2

验证方法:

  • 文档清晰易懂,准确反映当前实现

Task 5.5: 更新全局规范文档

内容:

  • 更新 CLAUDE.md 中的数据库设计原则和模型规范部分
  • 确保 CLAUDE.md 中的示例代码与修复后的模型风格完全一致
  • 如果需要,更新 openspec/AGENTS.md(如果其中包含模型相关指导)
  • 添加或完善以下规范内容:
    • GORM 模型字段规范(显式 column 标签、类型定义、注释要求)
    • 金额字段使用整数类型(分为单位)的详细说明和示例
    • 表名命名规范(tb_ 前缀 + 单数)
    • BaseModel 嵌入和审计字段使用说明
    • 唯一索引软删除兼容性(where:deleted_at IS NULL
    • JSONB 字段使用 datatypes.JSON 类型的说明

具体修改位置CLAUDE.md:

  1. 数据库设计原则 部分:

    • 补充完整的 GORM 模型字段定义规范
    • 添加金额字段整数存储的要求和理由
    • 添加字段标签完整性要求(显式 column、type、comment
  2. GORM 模型字段规范 新增小节:

    **GORM 模型字段规范:**
    - 数据库字段名必须使用下划线命名法snake_case`user_id``email_address``created_at`
    - Go 结构体字段名必须使用驼峰命名法PascalCase`UserID``EmailAddress``CreatedAt`
    - **所有字段必须显式指定数据库列名**:使用 `gorm:"column:字段名"` 标签明确指定数据库字段名,不依赖 GORM 的自动转换
      - 示例:`UserID uint gorm:"column:user_id;not null" json:"user_id"`
      - 禁止省略 `column:` 标签,即使 GORM 能自动推断字段名
      - 这确保了 Go 字段名和数据库字段名的映射关系清晰可见,避免命名歧义
    - 字符串字段长度必须明确定义且保持一致性:
      - 短文本(名称、标题等):`VARCHAR(255)``VARCHAR(100)`
      - 中等文本(描述、备注等):`VARCHAR(500)``VARCHAR(1000)`
      - 长文本(内容、详情等):`TEXT` 类型
    - 货币金额字段必须使用 `int64` 类型,数据库类型为 `bigint`,单位为"分"1元 = 100分
    - 所有字段必须添加中文注释,说明字段用途和业务含义
    
  3. 示例代码更新

    • 将现有的模型示例(如果有)更新为包含完整字段标签的版本

依赖: Task 5.2

验证方法:

  • CLAUDE.md 中的规范描述与实际实现的模型完全一致
  • 所有示例代码可以直接复制使用,无需修改
  • 规范描述清晰、完整、无歧义
  • 运行 git diff CLAUDE.md 检查修改内容

依赖关系图

阶段 1 (核心模型)
  ├─ Task 1.1: IotCard
  ├─ Task 1.2: Device
  ├─ Task 1.3: NumberCard
  └─ Task 1.4: Carrier
      ↓
阶段 2 (套餐和订单)
  ├─ Task 2.1: PackageSeries
  ├─ Task 2.2: Package (依赖 Task 2.1)
  ├─ Task 2.3: AgentPackageAllocation (依赖 Task 2.2)
  ├─ Task 2.4: PackageUsage (依赖 Task 2.2)
  ├─ Task 2.5: DeviceSimBinding (依赖 Task 1.1, Task 1.2)
  └─ Task 2.6: Order (依赖 Task 1.1, Task 1.2, Task 1.3, Task 2.2)
      ↓
阶段 3 (分佣系统)
  ├─ Task 3.1: AgentHierarchy
  ├─ Task 3.2: CommissionRule
  ├─ Task 3.3: CommissionLadder (依赖 Task 3.2)
  ├─ Task 3.4: CommissionCombinedCondition (依赖 Task 3.2)
  ├─ Task 3.5: CommissionRecord (依赖 Task 3.2)
  ├─ Task 3.6: CommissionApproval (依赖 Task 3.5)
  ├─ Task 3.7: CommissionTemplate
  └─ Task 3.8: CarrierSettlement (依赖 Task 3.5)
      ↓
阶段 4 (财务和系统)
  ├─ Task 4.1: CommissionWithdrawalRequest
  ├─ Task 4.2: CommissionWithdrawalSetting
  ├─ Task 4.3: PaymentMerchantSetting
  ├─ Task 4.4: DevCapabilityConfig
  ├─ Task 4.5: CardReplacementRequest
  ├─ Task 4.6: PollingConfig
  └─ Task 4.7: DataUsageRecord (依赖 Task 1.1)
      ↓
阶段 5 (验证和测试)
  ├─ Task 5.1: 编译验证
  ├─ Task 5.2: 一致性检查 (依赖 Task 5.1)
  ├─ Task 5.3: 生成迁移脚本 (依赖 Task 5.2, 可选)
  ├─ Task 5.4: 文档更新 (依赖 Task 5.2, 可选)
  └─ Task 5.5: 更新全局规范文档 (依赖 Task 5.2, 必需)

估算工作量

  • 阶段 1: 约 2-3 小时4 个核心模型)
  • 阶段 2: 约 3-4 小时6 个套餐和订单模型)
  • 阶段 3: 约 4-5 小时8 个分佣系统模型)
  • 阶段 4: 约 3-4 小时7 个财务和系统模型)
  • 阶段 5: 约 2-3 小时(验证、测试和全局规范文档更新)

总计: 约 14-19 小时(~2-3 个工作日)

注意事项

  1. 并行执行: 阶段内的任务可以并行执行(除非明确依赖)
  2. 增量提交: 建议每完成一个阶段提交一次 Git commit
  3. 回归测试: 修复完成后需要运行完整的单元测试套件(如有)
  4. 代码审查: 修复完成后需要进行 Code Review确保符合项目规范