- 完善 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>
538 lines
17 KiB
Markdown
538 lines
17 KiB
Markdown
# 设计文档:修复 IoT 模型架构违规
|
||
|
||
## 1. 设计目标
|
||
|
||
将所有 IoT 相关数据模型重构为符合项目开发规范的标准模型,确保代码一致性、可维护性和长期可扩展性。
|
||
|
||
## 2. 核心设计原则
|
||
|
||
### 2.1 统一模型结构
|
||
|
||
所有数据模型必须遵循以下标准结构:
|
||
|
||
```go
|
||
type ModelName struct {
|
||
gorm.Model // 标准字段:ID, CreatedAt, UpdatedAt, DeletedAt
|
||
BaseModel `gorm:"embedded"` // 基础字段:Creator, Updater
|
||
|
||
// 业务字段(按字母顺序排列)
|
||
Field1 Type `gorm:"column:field1;..." json:"field1"`
|
||
Field2 Type `gorm:"column:field2;..." json:"field2"`
|
||
}
|
||
|
||
func (ModelName) TableName() string {
|
||
return "tb_model_name" // tb_ 前缀 + 单数
|
||
}
|
||
```
|
||
|
||
**设计理由:**
|
||
- `gorm.Model`:提供标准的主键、时间戳、软删除支持
|
||
- `BaseModel`:提供审计字段,记录创建人和更新人
|
||
- 显式 `column` 标签:明确 Go 字段和数据库列的映射关系,避免依赖 GORM 自动转换
|
||
- `tb_` 前缀单数表名:项目统一规范,便于识别业务表
|
||
|
||
### 2.2 字段定义规范
|
||
|
||
**字符串字段:**
|
||
```go
|
||
Name string `gorm:"column:name;type:varchar(100);not null;comment:名称" json:"name"`
|
||
```
|
||
- 必须显式指定 `column` 标签
|
||
- 必须指定 `type:varchar(N)` 和长度
|
||
- 必须指定 `not null`(如果必填)
|
||
- 必须添加中文 `comment`
|
||
|
||
**货币金额字段:**
|
||
```go
|
||
Amount int64 `gorm:"column:amount;type:bigint;default:0;not null;comment:金额(分)" json:"amount"`
|
||
```
|
||
- 使用 `int64` 类型(不是 `float64`)
|
||
- 单位为"分"(1元 = 100分)
|
||
- 必须指定 `type:bigint`
|
||
- 必须指定 `default:0` 和 `not null`
|
||
- 注释中明确标注"(分)"
|
||
|
||
**设计理由:**
|
||
- 整数存储避免浮点精度问题(金融领域最佳实践)
|
||
- 分为单位便于精确计算和货币转换
|
||
|
||
**枚举字段:**
|
||
```go
|
||
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-启用 2-禁用" json:"status"`
|
||
```
|
||
- 使用 `int` 类型(不是 `string`)
|
||
- 必须在注释中列举所有枚举值
|
||
- 必须指定 `default` 和 `not null`
|
||
|
||
**关联 ID 字段:**
|
||
```go
|
||
UserID uint `gorm:"column:user_id;type:bigint;not null;index;comment:用户ID" json:"user_id"`
|
||
```
|
||
- 使用 `uint` 类型(与 `gorm.Model` 的 ID 类型一致)
|
||
- 数据库类型使用 `bigint`(PostgreSQL)
|
||
- 必须添加 `index` 索引
|
||
- 禁止使用 GORM 关联标签(`foreignKey`、`references`)
|
||
|
||
**可选关联 ID 字段:**
|
||
```go
|
||
ShopID *uint `gorm:"column:shop_id;type:bigint;index;comment:店铺ID(可选)" json:"shop_id,omitempty"`
|
||
```
|
||
- 使用指针类型 `*uint`(可为 NULL)
|
||
- 不指定 `not null`
|
||
- 仍需添加 `index` 索引
|
||
- JSON 标签使用 `omitempty`
|
||
|
||
**唯一索引字段:**
|
||
```go
|
||
ICCID string `gorm:"column:iccid;type:varchar(50);uniqueIndex:idx_iccid,where:deleted_at IS NULL;not null;comment:ICCID" json:"iccid"`
|
||
```
|
||
- 使用 `uniqueIndex` 标签
|
||
- 对于支持软删除的表,必须添加 `where:deleted_at IS NULL` 过滤条件
|
||
- 索引名命名规范:`idx_{table}_{field}` 或 `idx_{field}`
|
||
|
||
**时间字段:**
|
||
```go
|
||
ActivatedAt *time.Time `gorm:"column:activated_at;comment:激活时间" json:"activated_at,omitempty"`
|
||
```
|
||
- 可选时间字段使用指针类型 `*time.Time`
|
||
- 不使用 `autoCreateTime` 或 `autoUpdateTime`(这些由 gorm.Model 提供)
|
||
- JSON 标签使用 `omitempty`
|
||
|
||
**JSONB 字段(PostgreSQL):**
|
||
```go
|
||
Metadata datatypes.JSON `gorm:"column:metadata;type:jsonb;comment:元数据" json:"metadata,omitempty"`
|
||
```
|
||
- 使用 `gorm.io/datatypes.JSON` 类型
|
||
- 数据库类型使用 `jsonb`(PostgreSQL 优化存储)
|
||
- 使用 `omitempty`
|
||
|
||
### 2.3 表名和索引命名规范
|
||
|
||
**表名:**
|
||
- 格式:`tb_{model_name}`(单数)
|
||
- 示例:`tb_iot_card`、`tb_device`、`tb_order`
|
||
|
||
**索引名:**
|
||
- 普通索引:`idx_{table}_{field}`
|
||
- 唯一索引:`idx_{table}_{field}` 或 `uniq_{table}_{field}`
|
||
- 复合索引:`idx_{table}_{field1}_{field2}`
|
||
|
||
**设计理由:**
|
||
- 统一前缀便于识别业务表(与系统表区分)
|
||
- 单数形式符合 Go 惯用命名(类型名为单数)
|
||
- 索引名清晰表达用途和字段
|
||
|
||
### 2.4 软删除支持
|
||
|
||
所有业务数据表都应支持软删除:
|
||
|
||
```go
|
||
type BusinessModel struct {
|
||
gorm.Model // 包含 DeletedAt 字段
|
||
// ...
|
||
}
|
||
```
|
||
|
||
**不需要软删除的表:**
|
||
- 纯配置表(如 `PollingConfig`、`CommissionWithdrawalSetting`)
|
||
- 日志表(如 `DataUsageRecord`)
|
||
- 中间表(如 `DeviceSimBinding` 可选支持)
|
||
|
||
对于不需要软删除的表,可以手动定义字段:
|
||
|
||
```go
|
||
type ConfigModel struct {
|
||
ID uint `gorm:"column:id;primaryKey;comment:ID" json:"id"`
|
||
BaseModel `gorm:"embedded"`
|
||
// ...
|
||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
||
}
|
||
```
|
||
|
||
## 3. 模型分类和修复策略
|
||
|
||
### 3.1 核心业务实体(必须支持软删除)
|
||
|
||
**完整模型结构(gorm.Model + BaseModel):**
|
||
- `IotCard`(IoT 卡)
|
||
- `Device`(设备)
|
||
- `NumberCard`(号卡)
|
||
- `PackageSeries`(套餐系列)
|
||
- `Package`(套餐)
|
||
- `AgentPackageAllocation`(代理套餐分配)
|
||
- `Order`(订单)
|
||
- `AgentHierarchy`(代理层级)
|
||
- `CommissionRule`(分佣规则)
|
||
- `CommissionTemplate`(分佣模板)
|
||
- `Carrier`(运营商)
|
||
|
||
### 3.2 关联和绑定表(可选软删除)
|
||
|
||
**完整模型结构(gorm.Model + BaseModel):**
|
||
- `DeviceSimBinding`(设备-SIM 卡绑定)
|
||
|
||
### 3.3 使用记录和日志表(仅时间戳,不需要软删除)
|
||
|
||
**简化模型结构(手动定义 ID + BaseModel + CreatedAt/UpdatedAt):**
|
||
- `PackageUsage`(套餐使用)- 保留 gorm.Model(需要软删除和更新)
|
||
- `DataUsageRecord`(流量记录)- 仅需 ID + CreatedAt(不需要 UpdatedAt 和 DeletedAt)
|
||
|
||
### 3.4 财务和审批表(必须支持软删除)
|
||
|
||
**完整模型结构(gorm.Model + BaseModel):**
|
||
- `CommissionRecord`(分佣记录)
|
||
- `CommissionApproval`(分佣审批)
|
||
- `CommissionWithdrawalRequest`(佣金提现申请)
|
||
- `PaymentMerchantSetting`(收款商户设置)
|
||
- `CarrierSettlement`(运营商结算)
|
||
- `CardReplacementRequest`(换卡申请)
|
||
|
||
### 3.5 阶梯和条件配置表(可选软删除)
|
||
|
||
**完整模型结构(gorm.Model + BaseModel):**
|
||
- `CommissionLadder`(阶梯分佣配置)
|
||
- `CommissionCombinedCondition`(组合分佣条件)
|
||
|
||
### 3.6 系统配置表(可选软删除)
|
||
|
||
**完整模型结构(gorm.Model + BaseModel):**
|
||
- `CommissionWithdrawalSetting`(提现设置)
|
||
- `PollingConfig`(轮询配置)
|
||
- `DevCapabilityConfig`(开发能力配置)
|
||
|
||
## 4. 货币金额处理策略
|
||
|
||
### 4.1 金额字段映射
|
||
|
||
所有货币金额从 `float64`(元)改为 `int64`(分):
|
||
|
||
| 原字段类型 | 新字段类型 | 原数据库类型 | 新数据库类型 | 说明 |
|
||
|-----------|-----------|------------|------------|-----|
|
||
| `float64` | `int64` | `DECIMAL(10,2)` | `BIGINT` | 金额单位从元改为分 |
|
||
|
||
**影响的字段:**
|
||
- `IotCard.CostPrice`、`IotCard.DistributePrice`
|
||
- `NumberCard.Price`
|
||
- `Package.Price`
|
||
- `AgentPackageAllocation.CostPrice`、`AgentPackageAllocation.RetailPrice`
|
||
- `Order.Amount`
|
||
- `CommissionRule.CommissionValue`
|
||
- `CommissionLadder.CommissionValue`
|
||
- `CommissionCombinedCondition.OneTimeCommissionValue`、`CommissionCombinedCondition.LongTermCommissionValue`
|
||
- `CommissionRecord.Amount`
|
||
- `CommissionTemplate.CommissionValue`
|
||
- `CarrierSettlement.SettlementAmount`
|
||
- `CommissionWithdrawalRequest.Amount`、`CommissionWithdrawalRequest.Fee`、`CommissionWithdrawalRequest.ActualAmount`
|
||
- `CommissionWithdrawalSetting.MinWithdrawalAmount`
|
||
|
||
### 4.2 业务逻辑调整
|
||
|
||
**API 输入输出:**
|
||
- API 接收的金额仍为 `float64`(元)
|
||
- Handler 层负责单位转换:元 → 分(乘以 100)
|
||
- 响应时转换回:分 → 元(除以 100)
|
||
|
||
**示例:**
|
||
```go
|
||
// 输入:10.50 元
|
||
inputAmount := 10.50 // float64 (元)
|
||
dbAmount := int64(inputAmount * 100) // 1050 分
|
||
|
||
// 输出:10.50 元
|
||
dbAmount := int64(1050) // 分
|
||
outputAmount := float64(dbAmount) / 100.0 // 10.50 元
|
||
```
|
||
|
||
### 4.3 数据库迁移
|
||
|
||
对于已有测试数据:
|
||
```sql
|
||
-- 金额从 DECIMAL(元) 转为 BIGINT(分)
|
||
ALTER TABLE iot_cards RENAME COLUMN cost_price TO cost_price_old;
|
||
ALTER TABLE iot_cards ADD COLUMN cost_price BIGINT NOT NULL DEFAULT 0;
|
||
UPDATE iot_cards SET cost_price = CAST(cost_price_old * 100 AS BIGINT);
|
||
ALTER TABLE iot_cards DROP COLUMN cost_price_old;
|
||
```
|
||
|
||
## 5. JSONB 字段处理
|
||
|
||
### 5.1 问题
|
||
|
||
原模型使用 `pq.StringArray` 类型存储 JSONB:
|
||
```go
|
||
CarrierOrderData pq.StringArray `gorm:"column:carrier_order_data;type:jsonb;..."`
|
||
```
|
||
|
||
这是类型不匹配的:`pq.StringArray` 是 PostgreSQL 数组类型,不是 JSONB。
|
||
|
||
### 5.2 解决方案
|
||
|
||
使用 GORM 的 `datatypes.JSON` 类型:
|
||
|
||
```go
|
||
import "gorm.io/datatypes"
|
||
|
||
type Order struct {
|
||
// ...
|
||
CarrierOrderData datatypes.JSON `gorm:"column:carrier_order_data;type:jsonb;comment:运营商订单原始数据" json:"carrier_order_data,omitempty"`
|
||
// ...
|
||
}
|
||
```
|
||
|
||
**业务层使用:**
|
||
```go
|
||
// 写入
|
||
data := map[string]interface{}{
|
||
"order_id": "123",
|
||
"status": "paid",
|
||
}
|
||
order.CarrierOrderData, _ = json.Marshal(data)
|
||
|
||
// 读取
|
||
var data map[string]interface{}
|
||
json.Unmarshal(order.CarrierOrderData, &data)
|
||
```
|
||
|
||
## 6. 索引策略
|
||
|
||
### 6.1 唯一索引(Unique Index)
|
||
|
||
对于需要全局唯一的字段(如 ICCID、订单号、虚拟商品编码):
|
||
|
||
```go
|
||
ICCID string `gorm:"column:iccid;type:varchar(50);uniqueIndex:idx_iccid,where:deleted_at IS NULL;not null;comment:ICCID" json:"iccid"`
|
||
```
|
||
|
||
**关键点:**
|
||
- 必须添加 `where:deleted_at IS NULL` 过滤已软删除的记录
|
||
- 否则软删除后无法重新使用相同的唯一值
|
||
|
||
### 6.2 普通索引(Index)
|
||
|
||
对于频繁查询和过滤的字段(如状态、类型、关联 ID):
|
||
|
||
```go
|
||
Status int `gorm:"column:status;type:int;default:1;not null;index;comment:状态" json:"status"`
|
||
UserID uint `gorm:"column:user_id;type:bigint;not null;index;comment:用户ID" json:"user_id"`
|
||
```
|
||
|
||
### 6.3 复合索引(Composite Index)
|
||
|
||
对于联合查询的字段组合:
|
||
|
||
```go
|
||
type DeviceSimBinding struct {
|
||
// ...
|
||
DeviceID uint `gorm:"column:device_id;type:bigint;not null;index:idx_device_slot;comment:设备ID" json:"device_id"`
|
||
SlotPosition int `gorm:"column:slot_position;type:int;index:idx_device_slot;comment:插槽位置" json:"slot_position"`
|
||
// ...
|
||
}
|
||
```
|
||
|
||
**复合索引命名:**
|
||
- `idx_device_slot`:表示 `device_id` 和 `slot_position` 的联合索引
|
||
|
||
## 7. 迁移路径
|
||
|
||
### 7.1 代码修改顺序
|
||
|
||
1. 修改所有模型文件(`internal/model/*.go`)
|
||
2. 更新模型的单元测试(如有)
|
||
3. 生成新的数据库迁移脚本
|
||
4. 在开发环境测试迁移脚本
|
||
5. 验证所有模型定义正确
|
||
|
||
### 7.2 数据库迁移策略
|
||
|
||
**场景 1:IoT 模块尚未部署(推荐)**
|
||
- 删除旧的迁移脚本(如果已创建)
|
||
- 生成新的初始迁移脚本
|
||
- 重新运行迁移
|
||
|
||
**场景 2:IoT 模块已有测试数据**
|
||
- 保留旧的迁移脚本
|
||
- 生成新的迁移脚本(包含表重命名、字段修改)
|
||
- 编写数据转换脚本(金额单位转换等)
|
||
|
||
### 7.3 迁移脚本示例
|
||
|
||
```sql
|
||
-- 1. 重命名表(复数 → tb_ 前缀单数)
|
||
ALTER TABLE iot_cards RENAME TO tb_iot_card;
|
||
ALTER TABLE devices RENAME TO tb_device;
|
||
-- ...
|
||
|
||
-- 2. 添加新字段
|
||
ALTER TABLE tb_iot_card ADD COLUMN creator BIGINT NOT NULL DEFAULT 0;
|
||
ALTER TABLE tb_iot_card ADD COLUMN updater BIGINT NOT NULL DEFAULT 0;
|
||
ALTER TABLE tb_iot_card ADD COLUMN deleted_at TIMESTAMP;
|
||
|
||
-- 3. 修改金额字段(DECIMAL → BIGINT)
|
||
ALTER TABLE tb_iot_card RENAME COLUMN cost_price TO cost_price_old;
|
||
ALTER TABLE tb_iot_card ADD COLUMN cost_price BIGINT NOT NULL DEFAULT 0;
|
||
UPDATE tb_iot_card SET cost_price = CAST(cost_price_old * 100 AS BIGINT);
|
||
ALTER TABLE tb_iot_card DROP COLUMN cost_price_old;
|
||
|
||
-- 4. 添加索引
|
||
CREATE UNIQUE INDEX idx_iccid ON tb_iot_card(iccid) WHERE deleted_at IS NULL;
|
||
CREATE INDEX idx_status ON tb_iot_card(status);
|
||
CREATE INDEX idx_carrier_id ON tb_iot_card(carrier_id);
|
||
```
|
||
|
||
## 8. 验证清单
|
||
|
||
修复完成后需验证:
|
||
|
||
- [ ] 所有模型嵌入 `gorm.Model` 或手动定义 `ID`、`CreatedAt`、`UpdatedAt`
|
||
- [ ] 所有业务模型嵌入 `BaseModel`(`Creator`、`Updater`)
|
||
- [ ] 所有字段显式指定 `column` 标签
|
||
- [ ] 所有字符串字段指定类型和长度(`type:varchar(N)`)
|
||
- [ ] 所有金额字段使用 `int64` 类型和 `type:bigint`
|
||
- [ ] 所有必填字段指定 `not null`
|
||
- [ ] 所有字段添加中文 `comment`
|
||
- [ ] 所有唯一字段添加 `uniqueIndex` 并包含 `where:deleted_at IS NULL`
|
||
- [ ] 所有关联字段添加 `index`
|
||
- [ ] 所有表名使用 `tb_` 前缀 + 单数
|
||
- [ ] 所有 JSONB 字段使用 `datatypes.JSON` 类型
|
||
- [ ] 所有模型与现有 `Account`、`PersonalCustomer` 模型风格一致
|
||
|
||
## 9. 风险和注意事项
|
||
|
||
### 9.1 破坏性变更
|
||
|
||
- 表名变更会导致旧代码无法运行
|
||
- 金额单位变更需要业务逻辑适配
|
||
- 新增字段需要在业务逻辑中赋值
|
||
|
||
### 9.2 迁移风险
|
||
|
||
- 表重命名可能导致迁移失败(需谨慎测试)
|
||
- 金额转换可能出现精度问题(需验证)
|
||
- 索引重建可能耗时(大表需评估)
|
||
|
||
### 9.3 开发流程影响
|
||
|
||
- 修复期间 IoT 模块功能开发需暂停
|
||
- 所有依赖 IoT 模型的代码需同步修改
|
||
- 需要重新生成数据库迁移脚本
|
||
|
||
## 10. 全局规范文档更新
|
||
|
||
### 10.1 更新目标
|
||
|
||
确保项目规范文档(CLAUDE.md)与实际实现的模型完全一致,为未来开发提供清晰、准确的指导。
|
||
|
||
### 10.2 CLAUDE.md 更新内容
|
||
|
||
**1. 补充 GORM 模型字段规范**
|
||
|
||
在"数据库设计原则"部分添加详细的字段定义规范:
|
||
|
||
```markdown
|
||
**GORM 模型字段规范:**
|
||
|
||
**字段命名:**
|
||
- 数据库字段名必须使用下划线命名法(snake_case):`user_id`、`email_address`、`created_at`
|
||
- Go 结构体字段名必须使用驼峰命名法(PascalCase):`UserID`、`EmailAddress`、`CreatedAt`
|
||
|
||
**字段标签要求:**
|
||
- **所有字段必须显式指定数据库列名**:使用 `gorm:"column:字段名"` 标签
|
||
- 示例:`UserID uint gorm:"column:user_id;not null" json:"user_id"`
|
||
- 禁止省略 `column:` 标签,即使 GORM 能自动推断字段名
|
||
- 这确保了 Go 字段名和数据库字段名的映射关系清晰可见,避免命名歧义
|
||
- **所有字符串字段必须显式指定类型和长度**:
|
||
- 短文本:`type:varchar(100)` 或 `type:varchar(255)`
|
||
- 中等文本:`type:varchar(500)` 或 `type:varchar(1000)`
|
||
- 长文本:`type:text`
|
||
- **所有字段必须添加中文注释**:`comment:字段用途说明`
|
||
|
||
**货币金额字段规范:**
|
||
- **必须使用整数类型**:Go 类型 `int64`,数据库类型 `bigint`
|
||
- **单位必须为"分"**(1 元 = 100 分)
|
||
- **注释中必须明确标注单位**:`comment:金额(分)`
|
||
- **理由**:避免浮点精度问题,符合金融系统最佳实践
|
||
|
||
示例:
|
||
```go
|
||
Amount int64 `gorm:"column:amount;type:bigint;not null;comment:订单金额(分)" json:"amount"`
|
||
```
|
||
|
||
**唯一索引软删除兼容性:**
|
||
- 对于支持软删除的表(嵌入 `gorm.Model`),唯一索引必须包含 `where:deleted_at IS NULL` 过滤条件
|
||
- 示例:
|
||
```go
|
||
ICCID string `gorm:"column:iccid;type:varchar(50);uniqueIndex:idx_iccid,where:deleted_at IS NULL;not null;comment:ICCID" json:"iccid"`
|
||
```
|
||
- 理由:允许软删除后重新使用相同的唯一值
|
||
|
||
**JSONB 字段规范(PostgreSQL):**
|
||
- 必须使用 `gorm.io/datatypes.JSON` 类型
|
||
- 数据库类型为 `jsonb`
|
||
- 示例:
|
||
```go
|
||
import "gorm.io/datatypes"
|
||
|
||
Metadata datatypes.JSON `gorm:"column:metadata;type:jsonb;comment:元数据" json:"metadata,omitempty"`
|
||
```
|
||
```
|
||
|
||
**2. 更新模型示例代码**
|
||
|
||
将现有的模型示例(如 Account)更新为包含完整字段标签的版本,确保所有示例都遵循规范。
|
||
|
||
**3. 添加金额单位转换说明**
|
||
|
||
在"API 设计规范"或"错误处理规范"附近添加:
|
||
|
||
```markdown
|
||
**API 层金额单位转换:**
|
||
|
||
- API 接收和返回的金额使用 `float64` 类型(元)
|
||
- 业务层和数据库使用 `int64` 类型(分)
|
||
- Handler 层负责单位转换
|
||
|
||
**输入转换(API → 业务层):**
|
||
```go
|
||
// API 接收 10.50 元
|
||
inputAmount := 10.50 // float64 (元)
|
||
dbAmount := int64(inputAmount * 100) // 1050 分
|
||
```
|
||
|
||
**输出转换(业务层 → API):**
|
||
```go
|
||
// 数据库存储 1050 分
|
||
dbAmount := int64(1050) // 分
|
||
outputAmount := float64(dbAmount) / 100.0 // 10.50 元
|
||
```
|
||
|
||
**注意事项:**
|
||
- 转换时注意四舍五入和边界情况
|
||
- 建议封装转换函数,避免重复代码
|
||
- 在金额字段的 DTO 注释中明确单位(元)
|
||
```
|
||
|
||
### 10.3 验证清单
|
||
|
||
更新完成后需验证:
|
||
|
||
- [ ] CLAUDE.md 中的所有模型示例包含完整的字段标签
|
||
- [ ] 所有字段定义规范清晰、完整、无歧义
|
||
- [ ] 金额字段整数存储的说明详细且易懂
|
||
- [ ] 唯一索引软删除兼容性规范已添加
|
||
- [ ] JSONB 字段使用规范已添加
|
||
- [ ] API 层金额单位转换说明已添加
|
||
- [ ] 规范文档与实际实现的模型完全一致
|
||
|
||
## 11. 后续任务
|
||
|
||
模型修复和规范文档更新完成后,需要:
|
||
|
||
1. 更新 DTO 模型(请求/响应结构体)
|
||
2. 调整 Store 层(数据访问层)
|
||
3. 调整 Service 层(业务逻辑层)- 金额单位转换
|
||
4. 调整 Handler 层(API 层)- 金额单位转换
|
||
5. 生成数据库迁移脚本
|
||
6. 编写单元测试验证模型定义
|
||
7. 更新 API 文档
|