重构:完善 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>
This commit is contained in:
124
CLAUDE.md
124
CLAUDE.md
@@ -310,28 +310,126 @@ internal/
|
|||||||
- 短文本(名称、标题等):`VARCHAR(255)` 或 `VARCHAR(100)`
|
- 短文本(名称、标题等):`VARCHAR(255)` 或 `VARCHAR(100)`
|
||||||
- 中等文本(描述、备注等):`VARCHAR(500)` 或 `VARCHAR(1000)`
|
- 中等文本(描述、备注等):`VARCHAR(500)` 或 `VARCHAR(1000)`
|
||||||
- 长文本(内容、详情等):`TEXT` 类型
|
- 长文本(内容、详情等):`TEXT` 类型
|
||||||
|
- **货币金额字段必须使用整数类型存储(分为单位)**:
|
||||||
|
- Go 类型:`int64`(不是 `float64`)
|
||||||
|
- 数据库类型:`BIGINT`(不是 `DECIMAL` 或 `NUMERIC`)
|
||||||
|
- 示例:`CostPrice int64 gorm:"column:cost_price;type:bigint;default:0;comment:成本价(分为单位)" json:"cost_price"`
|
||||||
|
- 理由:避免浮点数精度问题,确保货币计算的准确性
|
||||||
|
- 显示时转换:金额除以 100 转换为元(如 10000 分 = 100.00 元)
|
||||||
- 数值字段精度必须明确定义:
|
- 数值字段精度必须明确定义:
|
||||||
- 货币金额:`DECIMAL(10, 2)` 或 `DECIMAL(18, 2)`(根据业务需求)
|
- 百分比:使用 `int64` 存储千分比或万分比(如 2000 表示 20%,避免浮点精度问题)
|
||||||
- 百分比:`DECIMAL(5, 2)` 或 `DECIMAL(5, 4)`
|
|
||||||
- 计数器:`INTEGER` 或 `BIGINT`
|
- 计数器:`INTEGER` 或 `BIGINT`
|
||||||
|
- 流量数据:`BIGINT`(如 MB、KB 为单位的流量使用量)
|
||||||
- 所有字段必须添加中文注释,说明字段用途和业务含义
|
- 所有字段必须添加中文注释,说明字段用途和业务含义
|
||||||
- 必填字段必须在 GORM 标签中指定 `not null`
|
- 必填字段必须在 GORM 标签中指定 `not null`
|
||||||
- 唯一字段必须在 GORM 标签中指定 `unique` 或通过数据库索引保证唯一性
|
- 唯一字段必须在 GORM 标签中指定 `unique` 或通过数据库索引保证唯一性
|
||||||
- 枚举字段应该使用 `VARCHAR` 或 `INTEGER` 类型,并在代码中定义常量映射
|
- 枚举字段应该使用 `VARCHAR` 或 `INTEGER` 类型,并在代码中定义常量映射
|
||||||
|
- JSONB 字段必须使用 `datatypes.JSON` 类型(从 `gorm.io/datatypes` 包导入)
|
||||||
|
- 示例:`AccountInfo datatypes.JSON gorm:"column:account_info;type:jsonb;comment:收款账户信息" json:"account_info"`
|
||||||
|
- 不使用 `pq.StringArray` 或其他 PostgreSQL 特定类型
|
||||||
|
|
||||||
**字段命名示例:**
|
**GORM 模型结构规范:**
|
||||||
|
|
||||||
|
- **所有业务实体模型必须嵌入 `gorm.Model` 和 `BaseModel`**:
|
||||||
|
- `gorm.Model` 提供:`ID`(主键)、`CreatedAt`、`UpdatedAt`、`DeletedAt`(软删除支持)
|
||||||
|
- `BaseModel` 提供:`Creator`、`Updater`(审计字段)
|
||||||
|
- 禁止手动定义 `ID`、`CreatedAt`、`UpdatedAt`、`DeletedAt` 字段
|
||||||
|
- 示例:
|
||||||
|
```go
|
||||||
|
type IotCard struct {
|
||||||
|
gorm.Model
|
||||||
|
BaseModel `gorm:"embedded"`
|
||||||
|
// 业务字段...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **日志表和只追加(append-only)表不需要软删除和审计字段**:
|
||||||
|
- 这类表只定义 `ID` 和 `CreatedAt`,不嵌入 `gorm.Model` 或 `BaseModel`
|
||||||
|
- 示例:`DataUsageRecord`(流量使用记录)
|
||||||
|
- 示例:
|
||||||
|
```go
|
||||||
|
type DataUsageRecord struct {
|
||||||
|
ID uint `gorm:"column:id;primaryKey;comment:流量使用记录ID" json:"id"`
|
||||||
|
IotCardID uint `gorm:"column:iot_card_id;index;not null;comment:IoT卡ID" json:"iot_card_id"`
|
||||||
|
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**表名命名规范:**
|
||||||
|
|
||||||
|
- **所有表名必须使用 `tb_` 前缀 + 单数形式**:
|
||||||
|
- 示例:`tb_iot_card`(不是 `iot_cards`)
|
||||||
|
- 示例:`tb_package`(不是 `packages`)
|
||||||
|
- 示例:`tb_order`(不是 `orders`)
|
||||||
|
- **必须实现 `TableName()` 方法显式指定表名**:
|
||||||
|
```go
|
||||||
|
func (IotCard) TableName() string {
|
||||||
|
return "tb_iot_card"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- 禁止依赖 GORM 的自动表名推断(避免复数形式导致的不一致)
|
||||||
|
|
||||||
|
**索引和约束规范:**
|
||||||
|
|
||||||
|
- **外键字段必须添加 `index` 标签**:
|
||||||
|
- 示例:`CarrierID uint gorm:"column:carrier_id;index;not null;comment:运营商ID" json:"carrier_id"`
|
||||||
|
- 提高关联查询性能
|
||||||
|
- **唯一索引必须支持软删除兼容性**:
|
||||||
|
- 添加 `where:deleted_at IS NULL` 条件,确保软删除后的记录不影响唯一性约束
|
||||||
|
- 示例:`gorm:"column:iccid;type:varchar(50);uniqueIndex:idx_iot_card_iccid,where:deleted_at IS NULL;not null;comment:ICCID" json:"iccid"`
|
||||||
|
- 这样同一个 ICCID 的卡可以多次创建/删除而不违反唯一性约束
|
||||||
|
- **复合索引命名规范**:
|
||||||
|
- 使用 `idx_{table}_{field1}_{field2}` 格式
|
||||||
|
- 示例:`uniqueIndex:idx_device_sim_binding_device_slot,where:deleted_at IS NULL`
|
||||||
|
- 禁止定义数据库级别的外键约束(Foreign Key Constraints)
|
||||||
|
|
||||||
|
**完整模型示例:**
|
||||||
|
|
||||||
|
标准业务实体模型(带软删除和审计字段):
|
||||||
```go
|
```go
|
||||||
type User struct {
|
type IotCard struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:用户 ID" json:"id"`
|
gorm.Model // 提供 ID, CreatedAt, UpdatedAt, DeletedAt
|
||||||
UserID string `gorm:"column:user_id;type:varchar(100);uniqueIndex;not null;comment:用户唯一标识" json:"user_id"`
|
BaseModel `gorm:"embedded"` // 提供 Creator, Updater
|
||||||
Email string `gorm:"column:email;type:varchar(255);uniqueIndex;not null;comment:用户邮箱" json:"email"`
|
ICCID string `gorm:"column:iccid;type:varchar(50);uniqueIndex:idx_iot_card_iccid,where:deleted_at IS NULL;not null;comment:ICCID(唯一标识)" json:"iccid"`
|
||||||
Phone string `gorm:"column:phone;type:varchar(20);comment:手机号码" json:"phone"`
|
CarrierID uint `gorm:"column:carrier_id;index;not null;comment:运营商ID" json:"carrier_id"`
|
||||||
Nickname string `gorm:"column:nickname;type:varchar(100);comment:用户昵称" json:"nickname"`
|
CostPrice int64 `gorm:"column:cost_price;type:bigint;default:0;comment:成本价(分为单位)" json:"cost_price"`
|
||||||
Balance int64 `gorm:"column:balance;type:bigint;default:0;comment:账户余额(分为单位)" json:"balance"`
|
DistributePrice int64 `gorm:"column:distribute_price;type:bigint;default:0;comment:分销价(分为单位)" json:"distribute_price"`
|
||||||
Status int `gorm:"column:status;type:int;default:1;comment:用户状态 1-正常 2-禁用" json:"status"`
|
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-未激活 2-已激活 3-已停用" json:"status"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
}
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
|
||||||
|
func (IotCard) TableName() string {
|
||||||
|
return "tb_iot_card"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
日志表模型(不需要软删除和审计):
|
||||||
|
```go
|
||||||
|
type DataUsageRecord struct {
|
||||||
|
ID uint `gorm:"column:id;primaryKey;comment:流量使用记录ID" json:"id"`
|
||||||
|
IotCardID uint `gorm:"column:iot_card_id;index;not null;comment:IoT卡ID" json:"iot_card_id"`
|
||||||
|
DataUsageMB int64 `gorm:"column:data_usage_mb;type:bigint;not null;comment:流量使用量(MB)" json:"data_usage_mb"`
|
||||||
|
DataIncreaseMB int64 `gorm:"column:data_increase_mb;type:bigint;default:0;comment:相比上次的增量(MB)" json:"data_increase_mb"`
|
||||||
|
CheckTime time.Time `gorm:"column:check_time;not null;comment:检查时间" json:"check_time"`
|
||||||
|
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (DataUsageRecord) TableName() string {
|
||||||
|
return "tb_data_usage_record"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
包含 JSONB 字段的模型:
|
||||||
|
```go
|
||||||
|
type Order struct {
|
||||||
|
gorm.Model
|
||||||
|
BaseModel `gorm:"embedded"`
|
||||||
|
OrderNo string `gorm:"column:order_no;type:varchar(100);uniqueIndex:idx_order_no,where:deleted_at IS NULL;not null;comment:订单号" json:"order_no"`
|
||||||
|
Amount int64 `gorm:"column:amount;type:bigint;not null;comment:订单金额(分为单位)" json:"amount"`
|
||||||
|
CarrierOrderData datatypes.JSON `gorm:"column:carrier_order_data;type:jsonb;comment:运营商订单原始数据" json:"carrier_order_data"`
|
||||||
|
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-待支付 2-已支付 3-已完成" json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Order) TableName() string {
|
||||||
|
return "tb_order"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
4
go.mod
4
go.mod
@@ -32,6 +32,7 @@ require (
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
dario.cat/mergo v1.0.2 // indirect
|
dario.cat/mergo v1.0.2 // indirect
|
||||||
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||||
@@ -59,6 +60,7 @@ require (
|
|||||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
|
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
@@ -129,4 +131,6 @@ require (
|
|||||||
google.golang.org/grpc v1.75.1 // indirect
|
google.golang.org/grpc v1.75.1 // indirect
|
||||||
google.golang.org/protobuf v1.36.10 // indirect
|
google.golang.org/protobuf v1.36.10 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
gorm.io/datatypes v1.2.7 // indirect
|
||||||
|
gorm.io/driver/mysql v1.5.6 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
10
go.sum
10
go.sum
@@ -1,5 +1,7 @@
|
|||||||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||||
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
|
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
|
||||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
||||||
@@ -80,6 +82,9 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
|||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
|
github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
|
||||||
github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
|
github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
|
||||||
|
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
|
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||||
|
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
github.com/gofiber/fiber/v2 v2.52.9 h1:YjKl5DOiyP3j0mO61u3NTmK7or8GzzWzCFzkboyP5cw=
|
github.com/gofiber/fiber/v2 v2.52.9 h1:YjKl5DOiyP3j0mO61u3NTmK7or8GzzWzCFzkboyP5cw=
|
||||||
@@ -324,10 +329,15 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gorm.io/datatypes v1.2.7 h1:ww9GAhF1aGXZY3EB3cJPJ7//JiuQo7DlQA7NNlVaTdk=
|
||||||
|
gorm.io/datatypes v1.2.7/go.mod h1:M2iO+6S3hhi4nAyYe444Pcb0dcIiOMJ7QHaUXxyiNZY=
|
||||||
|
gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8=
|
||||||
|
gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
|
||||||
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
|
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
|
||||||
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
|
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
|
||||||
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
|
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
|
||||||
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
|
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
|
||||||
|
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||||
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
|
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
|
||||||
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
|
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
|
||||||
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
// Carrier 运营商模型
|
// Carrier 运营商模型
|
||||||
// 存储运营商基础信息(中国移动、中国联通、中国电信)
|
// 存储运营商基础信息(中国移动、中国联通、中国电信)
|
||||||
type Carrier struct {
|
type Carrier struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:运营商ID" json:"id"`
|
gorm.Model
|
||||||
CarrierCode string `gorm:"column:carrier_code;type:varchar(50);uniqueIndex;not null;comment:运营商编码(CMCC/CUCC/CTCC)" json:"carrier_code"`
|
BaseModel `gorm:"embedded"`
|
||||||
CarrierName string `gorm:"column:carrier_name;type:varchar(100);not null;comment:运营商名称(中国移动/中国联通/中国电信)" json:"carrier_name"`
|
CarrierCode string `gorm:"column:carrier_code;type:varchar(50);uniqueIndex:idx_carrier_code,where:deleted_at IS NULL;not null;comment:运营商编码(CMCC/CUCC/CTCC)" json:"carrier_code"`
|
||||||
Description string `gorm:"column:description;type:varchar(500);comment:运营商描述" json:"description"`
|
CarrierName string `gorm:"column:carrier_name;type:varchar(100);not null;comment:运营商名称(中国移动/中国联通/中国电信)" json:"carrier_name"`
|
||||||
Status int `gorm:"column:status;type:int;default:1;comment:状态 1-启用 2-禁用" json:"status"`
|
Description string `gorm:"column:description;type:varchar(500);comment:运营商描述" json:"description"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
Status int `gorm:"column:status;type:int;default:1;comment:状态 1-启用 2-禁用" json:"status"`
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (Carrier) TableName() string {
|
func (Carrier) TableName() string {
|
||||||
return "carriers"
|
return "tb_carrier"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,164 +1,160 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
// AgentHierarchy 代理层级关系模型
|
// AgentHierarchy 代理层级关系模型
|
||||||
// 树形代理关系(每个代理只有一个上级)
|
// 树形代理关系(每个代理只有一个上级)
|
||||||
type AgentHierarchy struct {
|
type AgentHierarchy struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:代理层级ID" json:"id"`
|
gorm.Model
|
||||||
AgentID uint `gorm:"column:agent_id;type:bigint;uniqueIndex;not null;comment:代理用户ID" json:"agent_id"`
|
BaseModel `gorm:"embedded"`
|
||||||
ParentAgentID uint `gorm:"column:parent_agent_id;type:bigint;comment:上级代理用户ID(NULL表示顶级代理)" json:"parent_agent_id"`
|
AgentID uint `gorm:"column:agent_id;uniqueIndex:idx_agent_hierarchy_agent,where:deleted_at IS NULL;not null;comment:代理用户ID" json:"agent_id"`
|
||||||
Level int `gorm:"column:level;type:int;not null;comment:代理层级(1, 2, 3...)" json:"level"`
|
ParentAgentID uint `gorm:"column:parent_agent_id;index;comment:上级代理用户ID(NULL表示顶级代理)" json:"parent_agent_id"`
|
||||||
Path string `gorm:"column:path;type:varchar(500);comment:代理路径(如: 1/5/12)" json:"path"`
|
Level int `gorm:"column:level;type:int;not null;comment:代理层级(1, 2, 3...)" json:"level"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
Path string `gorm:"column:path;type:varchar(500);comment:代理路径(如: 1/5/12)" json:"path"`
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (AgentHierarchy) TableName() string {
|
func (AgentHierarchy) TableName() string {
|
||||||
return "agent_hierarchies"
|
return "tb_agent_hierarchy"
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommissionRule 分佣规则模型
|
// CommissionRule 分佣规则模型
|
||||||
// 三种分佣类型:一次性/长期/组合
|
// 三种分佣类型:一次性/长期/组合
|
||||||
type CommissionRule struct {
|
type CommissionRule struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:分佣规则ID" json:"id"`
|
gorm.Model
|
||||||
AgentID uint `gorm:"column:agent_id;type:bigint;not null;comment:代理用户ID" json:"agent_id"`
|
BaseModel `gorm:"embedded"`
|
||||||
BusinessType string `gorm:"column:business_type;type:varchar(50);not null;comment:业务类型" json:"business_type"`
|
AgentID uint `gorm:"column:agent_id;index;not null;comment:代理用户ID" json:"agent_id"`
|
||||||
CardType string `gorm:"column:card_type;type:varchar(50);not null;comment:卡类型 number_card-号卡 iot_card-IoT卡" json:"card_type"`
|
BusinessType string `gorm:"column:business_type;type:varchar(50);not null;comment:业务类型" json:"business_type"`
|
||||||
SeriesID uint `gorm:"column:series_id;type:bigint;comment:套餐系列ID(一次性分佣时用)" json:"series_id"`
|
CardType string `gorm:"column:card_type;type:varchar(50);not null;comment:卡类型 number_card-号卡 iot_card-IoT卡" json:"card_type"`
|
||||||
PackageID uint `gorm:"column:package_id;type:bigint;comment:套餐ID(长期分佣时用)" json:"package_id"`
|
SeriesID uint `gorm:"column:series_id;index;comment:套餐系列ID(一次性分佣时用)" json:"series_id"`
|
||||||
CommissionType string `gorm:"column:commission_type;type:varchar(50);not null;comment:分佣类型 one_time-一次性 long_term-长期 combined-组合" json:"commission_type"`
|
PackageID uint `gorm:"column:package_id;index;comment:套餐ID(长期分佣时用)" json:"package_id"`
|
||||||
CommissionMode string `gorm:"column:commission_mode;type:varchar(20);not null;comment:分佣模式 fixed-固定金额 percent-百分比" json:"commission_mode"`
|
CommissionType string `gorm:"column:commission_type;type:varchar(50);not null;comment:分佣类型 one_time-一次性 long_term-长期 combined-组合" json:"commission_type"`
|
||||||
CommissionValue float64 `gorm:"column:commission_value;type:decimal(10,2);not null;comment:分佣值" json:"commission_value"`
|
CommissionMode string `gorm:"column:commission_mode;type:varchar(20);not null;comment:分佣模式 fixed-固定金额 percent-百分比" json:"commission_mode"`
|
||||||
UnfreezeDays int `gorm:"column:unfreeze_days;type:int;default:0;comment:解冻天数" json:"unfreeze_days"`
|
CommissionValue int64 `gorm:"column:commission_value;type:bigint;not null;comment:分佣值(分为单位,百分比时为千分比如2000表示20%)" json:"commission_value"`
|
||||||
MinActivationForUnfreeze int `gorm:"column:min_activation_for_unfreeze;type:int;default:0;comment:解冻最小激活量" json:"min_activation_for_unfreeze"`
|
UnfreezeDays int `gorm:"column:unfreeze_days;type:int;default:0;comment:解冻天数" json:"unfreeze_days"`
|
||||||
ApprovalType string `gorm:"column:approval_type;type:varchar(20);default:'auto';comment:审批类型 auto-自动 manual-人工" json:"approval_type"`
|
MinActivationForUnfreeze int `gorm:"column:min_activation_for_unfreeze;type:int;default:0;comment:解冻最小激活量" json:"min_activation_for_unfreeze"`
|
||||||
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-启用 2-禁用" json:"status"`
|
ApprovalType string `gorm:"column:approval_type;type:varchar(20);default:'auto';comment:审批类型 auto-自动 manual-人工" json:"approval_type"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-启用 2-禁用" json:"status"`
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (CommissionRule) TableName() string {
|
func (CommissionRule) TableName() string {
|
||||||
return "commission_rules"
|
return "tb_commission_rule"
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommissionLadder 阶梯分佣配置模型
|
// CommissionLadder 阶梯分佣配置模型
|
||||||
// 支持按激活量、提货量、充值量设置阶梯佣金
|
// 支持按激活量、提货量、充值量设置阶梯佣金
|
||||||
type CommissionLadder struct {
|
type CommissionLadder struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:阶梯分佣配置ID" json:"id"`
|
gorm.Model
|
||||||
RuleID uint `gorm:"column:rule_id;type:bigint;not null;comment:分佣规则ID" json:"rule_id"`
|
BaseModel `gorm:"embedded"`
|
||||||
LadderType string `gorm:"column:ladder_type;type:varchar(50);not null;comment:阶梯类型 activation-激活量 pickup-提货量 deposit-充值量" json:"ladder_type"`
|
RuleID uint `gorm:"column:rule_id;index;not null;comment:分佣规则ID" json:"rule_id"`
|
||||||
ThresholdValue int `gorm:"column:threshold_value;type:int;not null;comment:阈值" json:"threshold_value"`
|
LadderType string `gorm:"column:ladder_type;type:varchar(50);not null;comment:阶梯类型 activation-激活量 pickup-提货量 deposit-充值量" json:"ladder_type"`
|
||||||
CommissionMode string `gorm:"column:commission_mode;type:varchar(20);not null;comment:分佣模式 fixed-固定金额 percent-百分比" json:"commission_mode"`
|
ThresholdValue int `gorm:"column:threshold_value;type:int;not null;comment:阈值" json:"threshold_value"`
|
||||||
CommissionValue float64 `gorm:"column:commission_value;type:decimal(10,2);not null;comment:分佣值" json:"commission_value"`
|
CommissionMode string `gorm:"column:commission_mode;type:varchar(20);not null;comment:分佣模式 fixed-固定金额 percent-百分比" json:"commission_mode"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
CommissionValue int64 `gorm:"column:commission_value;type:bigint;not null;comment:分佣值(分为单位,百分比时为千分比如2000表示20%)" json:"commission_value"`
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (CommissionLadder) TableName() string {
|
func (CommissionLadder) TableName() string {
|
||||||
return "commission_ladder"
|
return "tb_commission_ladder"
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommissionCombinedCondition 组合分佣条件模型
|
// CommissionCombinedCondition 组合分佣条件模型
|
||||||
// 支持时间点 OR 套餐周期阈值的 OR 条件解冻
|
// 支持时间点 OR 套餐周期阈值的 OR 条件解冻
|
||||||
type CommissionCombinedCondition struct {
|
type CommissionCombinedCondition struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:组合分佣条件ID" json:"id"`
|
gorm.Model
|
||||||
RuleID uint `gorm:"column:rule_id;type:bigint;uniqueIndex;not null;comment:分佣规则ID" json:"rule_id"`
|
BaseModel `gorm:"embedded"`
|
||||||
|
RuleID uint `gorm:"column:rule_id;uniqueIndex:idx_commission_combined_rule,where:deleted_at IS NULL;not null;comment:分佣规则ID" json:"rule_id"`
|
||||||
OneTimeCommissionMode string `gorm:"column:one_time_commission_mode;type:varchar(20);comment:一次性分佣模式 fixed-固定金额 percent-百分比" json:"one_time_commission_mode"`
|
OneTimeCommissionMode string `gorm:"column:one_time_commission_mode;type:varchar(20);comment:一次性分佣模式 fixed-固定金额 percent-百分比" json:"one_time_commission_mode"`
|
||||||
OneTimeCommissionValue float64 `gorm:"column:one_time_commission_value;type:decimal(10,2);comment:一次性分佣值" json:"one_time_commission_value"`
|
OneTimeCommissionValue int64 `gorm:"column:one_time_commission_value;type:bigint;comment:一次性分佣值(分为单位,百分比时为千分比如2000表示20%)" json:"one_time_commission_value"`
|
||||||
LongTermCommissionMode string `gorm:"column:long_term_commission_mode;type:varchar(20);comment:长期分佣模式 fixed-固定金额 percent-百分比" json:"long_term_commission_mode"`
|
LongTermCommissionMode string `gorm:"column:long_term_commission_mode;type:varchar(20);comment:长期分佣模式 fixed-固定金额 percent-百分比" json:"long_term_commission_mode"`
|
||||||
LongTermCommissionValue float64 `gorm:"column:long_term_commission_value;type:decimal(10,2);comment:长期分佣值" json:"long_term_commission_value"`
|
LongTermCommissionValue int64 `gorm:"column:long_term_commission_value;type:bigint;comment:长期分佣值(分为单位,百分比时为千分比如2000表示20%)" json:"long_term_commission_value"`
|
||||||
LongTermTriggerTimePoint *time.Time `gorm:"column:long_term_trigger_time_point;comment:长期分佣触发时间点(如实名后3个月)" json:"long_term_trigger_time_point"`
|
LongTermTriggerTimePoint *time.Time `gorm:"column:long_term_trigger_time_point;comment:长期分佣触发时间点(如实名后3个月)" json:"long_term_trigger_time_point"`
|
||||||
LongTermTriggerPackageCycles int `gorm:"column:long_term_trigger_package_cycles;type:int;comment:长期分佣触发套餐周期数(如10个套餐周期)" json:"long_term_trigger_package_cycles"`
|
LongTermTriggerPackageCycles int `gorm:"column:long_term_trigger_package_cycles;type:int;comment:长期分佣触发套餐周期数(如10个套餐周期)" json:"long_term_trigger_package_cycles"`
|
||||||
LongTermTriggerNetworkMonths int `gorm:"column:long_term_trigger_network_months;type:int;comment:长期分佣触发在网月数(号卡专用)" json:"long_term_trigger_network_months"`
|
LongTermTriggerNetworkMonths int `gorm:"column:long_term_trigger_network_months;type:int;comment:长期分佣触发在网月数(号卡专用)" json:"long_term_trigger_network_months"`
|
||||||
LongTermUnfreezeDays int `gorm:"column:long_term_unfreeze_days;type:int;default:0;comment:长期分佣解冻天数" json:"long_term_unfreeze_days"`
|
LongTermUnfreezeDays int `gorm:"column:long_term_unfreeze_days;type:int;default:0;comment:长期分佣解冻天数" json:"long_term_unfreeze_days"`
|
||||||
LongTermMinActivation int `gorm:"column:long_term_min_activation;type:int;default:0;comment:长期分佣解冻最小激活量" json:"long_term_min_activation"`
|
LongTermMinActivation int `gorm:"column:long_term_min_activation;type:int;default:0;comment:长期分佣解冻最小激活量" json:"long_term_min_activation"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (CommissionCombinedCondition) TableName() string {
|
func (CommissionCombinedCondition) TableName() string {
|
||||||
return "commission_combined_conditions"
|
return "tb_commission_combined_condition"
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommissionRecord 分佣记录模型
|
// CommissionRecord 分佣记录模型
|
||||||
// 记录分佣的冻结、解冻、发放状态
|
// 记录分佣的冻结、解冻、发放状态
|
||||||
type CommissionRecord struct {
|
type CommissionRecord struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:分佣记录ID" json:"id"`
|
gorm.Model
|
||||||
AgentID uint `gorm:"column:agent_id;type:bigint;not null;comment:代理用户ID" json:"agent_id"`
|
BaseModel `gorm:"embedded"`
|
||||||
OrderID uint `gorm:"column:order_id;type:bigint;not null;comment:订单ID" json:"order_id"`
|
AgentID uint `gorm:"column:agent_id;index;not null;comment:代理用户ID" json:"agent_id"`
|
||||||
RuleID uint `gorm:"column:rule_id;type:bigint;not null;comment:分佣规则ID" json:"rule_id"`
|
OrderID uint `gorm:"column:order_id;index;not null;comment:订单ID" json:"order_id"`
|
||||||
|
RuleID uint `gorm:"column:rule_id;index;not null;comment:分佣规则ID" json:"rule_id"`
|
||||||
CommissionType string `gorm:"column:commission_type;type:varchar(50);not null;comment:分佣类型 one_time-一次性 long_term-长期" json:"commission_type"`
|
CommissionType string `gorm:"column:commission_type;type:varchar(50);not null;comment:分佣类型 one_time-一次性 long_term-长期" json:"commission_type"`
|
||||||
Amount float64 `gorm:"column:amount;type:decimal(10,2);not null;comment:分佣金额(元)" json:"amount"`
|
Amount int64 `gorm:"column:amount;type:bigint;not null;comment:分佣金额(分为单位)" json:"amount"`
|
||||||
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-已冻结 2-解冻中 3-已发放 4-已失效" json:"status"`
|
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-已冻结 2-解冻中 3-已发放 4-已失效" json:"status"`
|
||||||
UnfrozenAt *time.Time `gorm:"column:unfrozen_at;comment:解冻时间" json:"unfrozen_at"`
|
UnfrozenAt *time.Time `gorm:"column:unfrozen_at;comment:解冻时间" json:"unfrozen_at"`
|
||||||
ReleasedAt *time.Time `gorm:"column:released_at;comment:发放时间" json:"released_at"`
|
ReleasedAt *time.Time `gorm:"column:released_at;comment:发放时间" json:"released_at"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (CommissionRecord) TableName() string {
|
func (CommissionRecord) TableName() string {
|
||||||
return "commission_records"
|
return "tb_commission_record"
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommissionApproval 分佣审批模型
|
// CommissionApproval 分佣审批模型
|
||||||
// 分佣解冻审批流程
|
// 分佣解冻审批流程
|
||||||
type CommissionApproval struct {
|
type CommissionApproval struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:分佣审批ID" json:"id"`
|
gorm.Model
|
||||||
CommissionRecordID uint `gorm:"column:commission_record_id;type:bigint;not null;comment:分佣记录ID" json:"commission_record_id"`
|
BaseModel `gorm:"embedded"`
|
||||||
ApproverID uint `gorm:"column:approver_id;type:bigint;comment:审批人用户ID" json:"approver_id"`
|
CommissionRecordID uint `gorm:"column:commission_record_id;index;not null;comment:分佣记录ID" json:"commission_record_id"`
|
||||||
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-待审批 2-已通过 3-已拒绝" json:"status"`
|
ApproverID uint `gorm:"column:approver_id;index;comment:审批人用户ID" json:"approver_id"`
|
||||||
Reason string `gorm:"column:reason;type:text;comment:原因" json:"reason"`
|
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-待审批 2-已通过 3-已拒绝" json:"status"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
Reason string `gorm:"column:reason;type:text;comment:原因" json:"reason"`
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (CommissionApproval) TableName() string {
|
func (CommissionApproval) TableName() string {
|
||||||
return "commission_approvals"
|
return "tb_commission_approval"
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommissionTemplate 分佣模板模型
|
// CommissionTemplate 分佣模板模型
|
||||||
// 创建和管理分佣模板,快速为代理分配产品时设置佣金规则
|
// 创建和管理分佣模板,快速为代理分配产品时设置佣金规则
|
||||||
type CommissionTemplate struct {
|
type CommissionTemplate struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:分佣模板ID" json:"id"`
|
gorm.Model
|
||||||
TemplateName string `gorm:"column:template_name;type:varchar(255);uniqueIndex;not null;comment:模板名称" json:"template_name"`
|
BaseModel `gorm:"embedded"`
|
||||||
BusinessType string `gorm:"column:business_type;type:varchar(50);not null;comment:业务类型" json:"business_type"`
|
TemplateName string `gorm:"column:template_name;type:varchar(255);uniqueIndex:idx_commission_template_name,where:deleted_at IS NULL;not null;comment:模板名称" json:"template_name"`
|
||||||
CardType string `gorm:"column:card_type;type:varchar(50);not null;comment:卡类型 number_card-号卡 iot_card-IoT卡" json:"card_type"`
|
BusinessType string `gorm:"column:business_type;type:varchar(50);not null;comment:业务类型" json:"business_type"`
|
||||||
CommissionType string `gorm:"column:commission_type;type:varchar(50);not null;comment:分佣类型 one_time-一次性 long_term-长期 combined-组合" json:"commission_type"`
|
CardType string `gorm:"column:card_type;type:varchar(50);not null;comment:卡类型 number_card-号卡 iot_card-IoT卡" json:"card_type"`
|
||||||
CommissionMode string `gorm:"column:commission_mode;type:varchar(20);not null;comment:分佣模式 fixed-固定金额 percent-百分比" json:"commission_mode"`
|
CommissionType string `gorm:"column:commission_type;type:varchar(50);not null;comment:分佣类型 one_time-一次性 long_term-长期 combined-组合" json:"commission_type"`
|
||||||
CommissionValue float64 `gorm:"column:commission_value;type:decimal(10,2);not null;comment:分佣值" json:"commission_value"`
|
CommissionMode string `gorm:"column:commission_mode;type:varchar(20);not null;comment:分佣模式 fixed-固定金额 percent-百分比" json:"commission_mode"`
|
||||||
UnfreezeDays int `gorm:"column:unfreeze_days;type:int;default:0;comment:解冻天数" json:"unfreeze_days"`
|
CommissionValue int64 `gorm:"column:commission_value;type:bigint;not null;comment:分佣值(分为单位,百分比时为千分比如2000表示20%)" json:"commission_value"`
|
||||||
MinActivationForUnfreeze int `gorm:"column:min_activation_for_unfreeze;type:int;default:0;comment:解冻最小激活量" json:"min_activation_for_unfreeze"`
|
UnfreezeDays int `gorm:"column:unfreeze_days;type:int;default:0;comment:解冻天数" json:"unfreeze_days"`
|
||||||
ApprovalType string `gorm:"column:approval_type;type:varchar(20);default:'auto';comment:审批类型 auto-自动 manual-人工" json:"approval_type"`
|
MinActivationForUnfreeze int `gorm:"column:min_activation_for_unfreeze;type:int;default:0;comment:解冻最小激活量" json:"min_activation_for_unfreeze"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
ApprovalType string `gorm:"column:approval_type;type:varchar(20);default:'auto';comment:审批类型 auto-自动 manual-人工" json:"approval_type"`
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (CommissionTemplate) TableName() string {
|
func (CommissionTemplate) TableName() string {
|
||||||
return "commission_templates"
|
return "tb_commission_template"
|
||||||
}
|
}
|
||||||
|
|
||||||
// CarrierSettlement 号卡运营商结算模型
|
// CarrierSettlement 号卡运营商结算模型
|
||||||
// 运营商周期性结算的佣金总额,再分配给代理
|
// 运营商周期性结算的佣金总额,再分配给代理
|
||||||
type CarrierSettlement struct {
|
type CarrierSettlement struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:运营商结算ID" json:"id"`
|
gorm.Model
|
||||||
CommissionRecordID uint `gorm:"column:commission_record_id;type:bigint;uniqueIndex;not null;comment:分佣记录ID" json:"commission_record_id"`
|
BaseModel `gorm:"embedded"`
|
||||||
AgentID uint `gorm:"column:agent_id;type:bigint;not null;comment:代理用户ID" json:"agent_id"`
|
CommissionRecordID uint `gorm:"column:commission_record_id;uniqueIndex:idx_carrier_settlement_record,where:deleted_at IS NULL;not null;comment:分佣记录ID" json:"commission_record_id"`
|
||||||
SettlementMonth string `gorm:"column:settlement_month;type:varchar(20);not null;comment:结算月份(如 2026-01)" json:"settlement_month"`
|
AgentID uint `gorm:"column:agent_id;index;not null;comment:代理用户ID" json:"agent_id"`
|
||||||
SettlementAmount float64 `gorm:"column:settlement_amount;type:decimal(18,2);not null;comment:结算金额(元)" json:"settlement_amount"`
|
SettlementMonth string `gorm:"column:settlement_month;type:varchar(20);not null;comment:结算月份(如 2026-01)" json:"settlement_month"`
|
||||||
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-待结算 2-已结算" json:"status"`
|
SettlementAmount int64 `gorm:"column:settlement_amount;type:bigint;not null;comment:结算金额(分为单位)" json:"settlement_amount"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-待结算 2-已结算" json:"status"`
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (CarrierSettlement) TableName() string {
|
func (CarrierSettlement) TableName() string {
|
||||||
return "carrier_settlements"
|
return "tb_carrier_settlement"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
// DataUsageRecord 流量使用记录模型
|
// DataUsageRecord 流量使用记录模型
|
||||||
// 记录卡的流量历史,支持流量查询和分析
|
// 记录卡的流量历史,支持流量查询和分析
|
||||||
|
// 注意:此模型是日志表,不需要软删除和审计字段
|
||||||
type DataUsageRecord struct {
|
type DataUsageRecord struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:流量使用记录ID" json:"id"`
|
ID uint `gorm:"column:id;primaryKey;comment:流量使用记录ID" json:"id"`
|
||||||
IotCardID uint `gorm:"column:iot_card_id;type:bigint;not null;comment:IoT卡ID" json:"iot_card_id"`
|
IotCardID uint `gorm:"column:iot_card_id;index;not null;comment:IoT卡ID" json:"iot_card_id"`
|
||||||
DataUsageMB int64 `gorm:"column:data_usage_mb;type:bigint;not null;comment:流量使用量(MB)" json:"data_usage_mb"`
|
DataUsageMB int64 `gorm:"column:data_usage_mb;type:bigint;not null;comment:流量使用量(MB)" json:"data_usage_mb"`
|
||||||
DataIncreaseMB int64 `gorm:"column:data_increase_mb;type:bigint;default:0;comment:相比上次的增量(MB)" json:"data_increase_mb"`
|
DataIncreaseMB int64 `gorm:"column:data_increase_mb;type:bigint;default:0;comment:相比上次的增量(MB)" json:"data_increase_mb"`
|
||||||
CheckTime time.Time `gorm:"column:check_time;not null;comment:检查时间" json:"check_time"`
|
CheckTime time.Time `gorm:"column:check_time;not null;comment:检查时间" json:"check_time"`
|
||||||
@@ -16,5 +19,5 @@ type DataUsageRecord struct {
|
|||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (DataUsageRecord) TableName() string {
|
func (DataUsageRecord) TableName() string {
|
||||||
return "data_usage_records"
|
return "tb_data_usage_record"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
// Device 设备模型
|
// Device 设备模型
|
||||||
// 用户的物联网设备(如 GPS 追踪器、智能传感器)
|
// 用户的物联网设备(如 GPS 追踪器、智能传感器)
|
||||||
// 可绑定 1-4 张 IoT 卡,主要用于批量管理和设备操作
|
// 可绑定 1-4 张 IoT 卡,主要用于批量管理和设备操作
|
||||||
type Device struct {
|
type Device struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:设备ID" json:"id"`
|
gorm.Model
|
||||||
DeviceNo string `gorm:"column:device_no;type:varchar(100);uniqueIndex;not null;comment:设备编号(唯一标识)" json:"device_no"`
|
BaseModel `gorm:"embedded"`
|
||||||
|
DeviceNo string `gorm:"column:device_no;type:varchar(100);uniqueIndex:idx_device_no,where:deleted_at IS NULL;not null;comment:设备编号(唯一标识)" json:"device_no"`
|
||||||
DeviceName string `gorm:"column:device_name;type:varchar(255);comment:设备名称" json:"device_name"`
|
DeviceName string `gorm:"column:device_name;type:varchar(255);comment:设备名称" json:"device_name"`
|
||||||
DeviceModel string `gorm:"column:device_model;type:varchar(100);comment:设备型号" json:"device_model"`
|
DeviceModel string `gorm:"column:device_model;type:varchar(100);comment:设备型号" json:"device_model"`
|
||||||
DeviceType string `gorm:"column:device_type;type:varchar(50);comment:设备类型" json:"device_type"`
|
DeviceType string `gorm:"column:device_type;type:varchar(50);comment:设备类型" json:"device_type"`
|
||||||
@@ -15,17 +20,15 @@ type Device struct {
|
|||||||
Manufacturer string `gorm:"column:manufacturer;type:varchar(255);comment:制造商" json:"manufacturer"`
|
Manufacturer string `gorm:"column:manufacturer;type:varchar(255);comment:制造商" json:"manufacturer"`
|
||||||
BatchNo string `gorm:"column:batch_no;type:varchar(100);comment:批次号" json:"batch_no"`
|
BatchNo string `gorm:"column:batch_no;type:varchar(100);comment:批次号" json:"batch_no"`
|
||||||
OwnerType string `gorm:"column:owner_type;type:varchar(20);default:'platform';not null;comment:所有者类型 platform-平台 agent-代理 user-用户" json:"owner_type"`
|
OwnerType string `gorm:"column:owner_type;type:varchar(20);default:'platform';not null;comment:所有者类型 platform-平台 agent-代理 user-用户" json:"owner_type"`
|
||||||
OwnerID uint `gorm:"column:owner_id;type:bigint;default:0;not null;comment:所有者ID" json:"owner_id"`
|
OwnerID uint `gorm:"column:owner_id;index;default:0;not null;comment:所有者ID" json:"owner_id"`
|
||||||
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-在库 2-已分销 3-已激活 4-已停用" json:"status"`
|
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-在库 2-已分销 3-已激活 4-已停用" json:"status"`
|
||||||
ActivatedAt *time.Time `gorm:"column:activated_at;comment:激活时间" json:"activated_at"`
|
ActivatedAt *time.Time `gorm:"column:activated_at;comment:激活时间" json:"activated_at"`
|
||||||
DeviceUsername string `gorm:"column:device_username;type:varchar(100);comment:设备登录用户名" json:"device_username"`
|
DeviceUsername string `gorm:"column:device_username;type:varchar(100);comment:设备登录用户名" json:"device_username"`
|
||||||
DevicePasswordEncrypted string `gorm:"column:device_password_encrypted;type:varchar(255);comment:设备登录密码(加密)" json:"device_password_encrypted"`
|
DevicePasswordEncrypted string `gorm:"column:device_password_encrypted;type:varchar(255);comment:设备登录密码(加密)" json:"device_password_encrypted"`
|
||||||
DeviceAPIEndpoint string `gorm:"column:device_api_endpoint;type:varchar(500);comment:设备API端点" json:"device_api_endpoint"`
|
DeviceAPIEndpoint string `gorm:"column:device_api_endpoint;type:varchar(500);comment:设备API端点" json:"device_api_endpoint"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (Device) TableName() string {
|
func (Device) TableName() string {
|
||||||
return "devices"
|
return "tb_device"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,68 +3,66 @@ package model
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/lib/pq"
|
"gorm.io/datatypes"
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CommissionWithdrawalRequest 佣金提现申请模型
|
// CommissionWithdrawalRequest 佣金提现申请模型
|
||||||
// 代理佣金提现申请、审批流程、提现记录查询
|
// 代理佣金提现申请、审批流程、提现记录查询
|
||||||
type CommissionWithdrawalRequest struct {
|
type CommissionWithdrawalRequest struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:提现申请ID" json:"id"`
|
gorm.Model
|
||||||
AgentID uint `gorm:"column:agent_id;type:bigint;not null;comment:代理用户ID" json:"agent_id"`
|
BaseModel `gorm:"embedded"`
|
||||||
Amount float64 `gorm:"column:amount;type:decimal(18,2);not null;comment:提现金额(元)" json:"amount"`
|
AgentID uint `gorm:"column:agent_id;index;not null;comment:代理用户ID" json:"agent_id"`
|
||||||
Fee float64 `gorm:"column:fee;type:decimal(18,2);default:0;comment:手续费(元)" json:"fee"`
|
Amount int64 `gorm:"column:amount;type:bigint;not null;comment:提现金额(分为单位)" json:"amount"`
|
||||||
ActualAmount float64 `gorm:"column:actual_amount;type:decimal(18,2);comment:实际到账金额(元)" json:"actual_amount"`
|
Fee int64 `gorm:"column:fee;type:bigint;default:0;comment:手续费(分为单位)" json:"fee"`
|
||||||
|
ActualAmount int64 `gorm:"column:actual_amount;type:bigint;comment:实际到账金额(分为单位)" json:"actual_amount"`
|
||||||
WithdrawalMethod string `gorm:"column:withdrawal_method;type:varchar(20);comment:提现方式 alipay-支付宝 wechat-微信 bank-银行卡" json:"withdrawal_method"`
|
WithdrawalMethod string `gorm:"column:withdrawal_method;type:varchar(20);comment:提现方式 alipay-支付宝 wechat-微信 bank-银行卡" json:"withdrawal_method"`
|
||||||
AccountInfo pq.StringArray `gorm:"column:account_info;type:jsonb;comment:收款账户信息(姓名、账号等)" json:"account_info"`
|
AccountInfo datatypes.JSON `gorm:"column:account_info;type:jsonb;comment:收款账户信息(姓名、账号等)" json:"account_info"`
|
||||||
Status int `gorm:"column:status;type:int;default:1;comment:状态 1-待审核 2-已通过 3-已拒绝 4-已到账" json:"status"`
|
Status int `gorm:"column:status;type:int;default:1;comment:状态 1-待审核 2-已通过 3-已拒绝 4-已到账" json:"status"`
|
||||||
ApprovedBy uint `gorm:"column:approved_by;type:bigint;comment:审批人用户ID" json:"approved_by"`
|
ApprovedBy uint `gorm:"column:approved_by;index;comment:审批人用户ID" json:"approved_by"`
|
||||||
ApprovedAt *time.Time `gorm:"column:approved_at;comment:审批时间" json:"approved_at"`
|
ApprovedAt *time.Time `gorm:"column:approved_at;comment:审批时间" json:"approved_at"`
|
||||||
PaidAt *time.Time `gorm:"column:paid_at;comment:到账时间" json:"paid_at"`
|
PaidAt *time.Time `gorm:"column:paid_at;comment:到账时间" json:"paid_at"`
|
||||||
RejectReason string `gorm:"column:reject_reason;type:text;comment:拒绝原因" json:"reject_reason"`
|
RejectReason string `gorm:"column:reject_reason;type:text;comment:拒绝原因" json:"reject_reason"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (CommissionWithdrawalRequest) TableName() string {
|
func (CommissionWithdrawalRequest) TableName() string {
|
||||||
return "commission_withdrawal_requests"
|
return "tb_commission_withdrawal_request"
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommissionWithdrawalSetting 佣金提现设置模型
|
// CommissionWithdrawalSetting 佣金提现设置模型
|
||||||
// 提现参数配置(最低金额、手续费率、到账时间等)
|
// 提现参数配置(最低金额、手续费率、到账时间等)
|
||||||
type CommissionWithdrawalSetting struct {
|
type CommissionWithdrawalSetting struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:提现设置ID" json:"id"`
|
gorm.Model
|
||||||
MinWithdrawalAmount float64 `gorm:"column:min_withdrawal_amount;type:decimal(10,2);comment:最低提现金额(元)" json:"min_withdrawal_amount"`
|
BaseModel `gorm:"embedded"`
|
||||||
FeeRate float64 `gorm:"column:fee_rate;type:decimal(5,4);comment:手续费率(如 0.01 表示 1%)" json:"fee_rate"`
|
MinWithdrawalAmount int64 `gorm:"column:min_withdrawal_amount;type:bigint;comment:最低提现金额(分为单位)" json:"min_withdrawal_amount"`
|
||||||
ArrivalDays int `gorm:"column:arrival_days;type:int;comment:到账天数" json:"arrival_days"`
|
FeeRate int64 `gorm:"column:fee_rate;type:bigint;comment:手续费率(万分比,如100表示1%)" json:"fee_rate"`
|
||||||
IsActive bool `gorm:"column:is_active;type:boolean;default:true;comment:是否生效(最新一条)" json:"is_active"`
|
ArrivalDays int `gorm:"column:arrival_days;type:int;comment:到账天数" json:"arrival_days"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
IsActive bool `gorm:"column:is_active;type:boolean;default:true;comment:是否生效(最新一条)" json:"is_active"`
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (CommissionWithdrawalSetting) TableName() string {
|
func (CommissionWithdrawalSetting) TableName() string {
|
||||||
return "commission_withdrawal_settings"
|
return "tb_commission_withdrawal_setting"
|
||||||
}
|
}
|
||||||
|
|
||||||
// PaymentMerchantSetting 收款商户设置模型
|
// PaymentMerchantSetting 收款商户设置模型
|
||||||
// 配置支付参数(支付宝、微信等收款账户)
|
// 配置支付参数(支付宝、微信等收款账户)
|
||||||
type PaymentMerchantSetting struct {
|
type PaymentMerchantSetting struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:收款商户ID" json:"id"`
|
gorm.Model
|
||||||
UserID uint `gorm:"column:user_id;type:bigint;not null;comment:用户ID" json:"user_id"`
|
BaseModel `gorm:"embedded"`
|
||||||
MerchantType string `gorm:"column:merchant_type;type:varchar(20);comment:商户类型 alipay-支付宝 wechat-微信 bank-银行卡" json:"merchant_type"`
|
UserID uint `gorm:"column:user_id;index;not null;comment:用户ID" json:"user_id"`
|
||||||
AccountName string `gorm:"column:account_name;type:varchar(255);comment:账户名称" json:"account_name"`
|
MerchantType string `gorm:"column:merchant_type;type:varchar(20);comment:商户类型 alipay-支付宝 wechat-微信 bank-银行卡" json:"merchant_type"`
|
||||||
AccountNumber string `gorm:"column:account_number;type:varchar(255);comment:账号" json:"account_number"`
|
AccountName string `gorm:"column:account_name;type:varchar(255);comment:账户名称" json:"account_name"`
|
||||||
BankName string `gorm:"column:bank_name;type:varchar(255);comment:银行名称(仅银行卡)" json:"bank_name"`
|
AccountNumber string `gorm:"column:account_number;type:varchar(255);comment:账号" json:"account_number"`
|
||||||
BankBranch string `gorm:"column:bank_branch;type:varchar(255);comment:开户行(仅银行卡)" json:"bank_branch"`
|
BankName string `gorm:"column:bank_name;type:varchar(255);comment:银行名称(仅银行卡)" json:"bank_name"`
|
||||||
IsVerified bool `gorm:"column:is_verified;type:boolean;default:false;comment:是否已验证" json:"is_verified"`
|
BankBranch string `gorm:"column:bank_branch;type:varchar(255);comment:开户行(仅银行卡)" json:"bank_branch"`
|
||||||
IsDefault bool `gorm:"column:is_default;type:boolean;default:false;comment:是否默认账户" json:"is_default"`
|
IsVerified bool `gorm:"column:is_verified;type:boolean;default:false;comment:是否已验证" json:"is_verified"`
|
||||||
Status int `gorm:"column:status;type:int;default:1;comment:状态 1-启用 2-禁用" json:"status"`
|
IsDefault bool `gorm:"column:is_default;type:boolean;default:false;comment:是否默认账户" json:"is_default"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
Status int `gorm:"column:status;type:int;default:1;comment:状态 1-启用 2-禁用" json:"status"`
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (PaymentMerchantSetting) TableName() string {
|
func (PaymentMerchantSetting) TableName() string {
|
||||||
return "payment_merchant_settings"
|
return "tb_payment_merchant_setting"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,30 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
// IotCard IoT 卡模型
|
// IotCard IoT 卡模型
|
||||||
// 物联网卡/流量卡的统一管理实体
|
// 物联网卡/流量卡的统一管理实体
|
||||||
// 支持平台自营、代理分销、用户购买等所有权模式
|
// 支持平台自营、代理分销、用户购买等所有权模式
|
||||||
type IotCard struct {
|
type IotCard struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:IoT 卡 ID" json:"id"`
|
gorm.Model
|
||||||
ICCID string `gorm:"column:iccid;type:varchar(50);uniqueIndex;not null;comment:ICCID(唯一标识)" json:"iccid"`
|
BaseModel `gorm:"embedded"`
|
||||||
|
ICCID string `gorm:"column:iccid;type:varchar(50);uniqueIndex:idx_iot_card_iccid,where:deleted_at IS NULL;not null;comment:ICCID(唯一标识)" json:"iccid"`
|
||||||
CardType string `gorm:"column:card_type;type:varchar(50);not null;comment:卡类型" json:"card_type"`
|
CardType string `gorm:"column:card_type;type:varchar(50);not null;comment:卡类型" json:"card_type"`
|
||||||
CardCategory string `gorm:"column:card_category;type:varchar(20);default:'normal';not null;comment:卡业务类型 normal-普通卡 industry-行业卡" json:"card_category"`
|
CardCategory string `gorm:"column:card_category;type:varchar(20);default:'normal';not null;comment:卡业务类型 normal-普通卡 industry-行业卡" json:"card_category"`
|
||||||
CarrierID uint `gorm:"column:carrier_id;type:bigint;not null;comment:运营商ID" json:"carrier_id"`
|
CarrierID uint `gorm:"column:carrier_id;index;not null;comment:运营商ID" json:"carrier_id"`
|
||||||
IMSI string `gorm:"column:imsi;type:varchar(50);comment:IMSI" json:"imsi"`
|
IMSI string `gorm:"column:imsi;type:varchar(50);comment:IMSI" json:"imsi"`
|
||||||
MSISDN string `gorm:"column:msisdn;type:varchar(20);comment:MSISDN(手机号码)" json:"msisdn"`
|
MSISDN string `gorm:"column:msisdn;type:varchar(20);comment:MSISDN(手机号码)" json:"msisdn"`
|
||||||
BatchNo string `gorm:"column:batch_no;type:varchar(100);comment:批次号" json:"batch_no"`
|
BatchNo string `gorm:"column:batch_no;type:varchar(100);comment:批次号" json:"batch_no"`
|
||||||
Supplier string `gorm:"column:supplier;type:varchar(255);comment:供应商" json:"supplier"`
|
Supplier string `gorm:"column:supplier;type:varchar(255);comment:供应商" json:"supplier"`
|
||||||
CostPrice float64 `gorm:"column:cost_price;type:decimal(10,2);default:0;comment:成本价(元)" json:"cost_price"`
|
CostPrice int64 `gorm:"column:cost_price;type:bigint;default:0;comment:成本价(分为单位)" json:"cost_price"`
|
||||||
DistributePrice float64 `gorm:"column:distribute_price;type:decimal(10,2);default:0;comment:分销价(元)" json:"distribute_price"`
|
DistributePrice int64 `gorm:"column:distribute_price;type:bigint;default:0;comment:分销价(分为单位)" json:"distribute_price"`
|
||||||
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-在库 2-已分销 3-已激活 4-已停用" json:"status"`
|
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-在库 2-已分销 3-已激活 4-已停用" json:"status"`
|
||||||
OwnerType string `gorm:"column:owner_type;type:varchar(20);default:'platform';not null;comment:所有者类型 platform-平台 agent-代理 user-用户 device-设备" json:"owner_type"`
|
OwnerType string `gorm:"column:owner_type;type:varchar(20);default:'platform';not null;comment:所有者类型 platform-平台 agent-代理 user-用户 device-设备" json:"owner_type"`
|
||||||
OwnerID uint `gorm:"column:owner_id;type:bigint;default:0;not null;comment:所有者ID" json:"owner_id"`
|
OwnerID uint `gorm:"column:owner_id;index;default:0;not null;comment:所有者ID" json:"owner_id"`
|
||||||
ActivatedAt *time.Time `gorm:"column:activated_at;comment:激活时间" json:"activated_at"`
|
ActivatedAt *time.Time `gorm:"column:activated_at;comment:激活时间" json:"activated_at"`
|
||||||
ActivationStatus int `gorm:"column:activation_status;type:int;default:0;not null;comment:激活状态 0-未激活 1-已激活" json:"activation_status"`
|
ActivationStatus int `gorm:"column:activation_status;type:int;default:0;not null;comment:激活状态 0-未激活 1-已激活" json:"activation_status"`
|
||||||
RealNameStatus int `gorm:"column:real_name_status;type:int;default:0;not null;comment:实名状态 0-未实名 1-已实名(行业卡可以保持0)" json:"real_name_status"`
|
RealNameStatus int `gorm:"column:real_name_status;type:int;default:0;not null;comment:实名状态 0-未实名 1-已实名(行业卡可以保持0)" json:"real_name_status"`
|
||||||
@@ -29,11 +34,9 @@ type IotCard struct {
|
|||||||
LastDataCheckAt *time.Time `gorm:"column:last_data_check_at;comment:最后一次流量检查时间" json:"last_data_check_at"`
|
LastDataCheckAt *time.Time `gorm:"column:last_data_check_at;comment:最后一次流量检查时间" json:"last_data_check_at"`
|
||||||
LastRealNameCheckAt *time.Time `gorm:"column:last_real_name_check_at;comment:最后一次实名检查时间" json:"last_real_name_check_at"`
|
LastRealNameCheckAt *time.Time `gorm:"column:last_real_name_check_at;comment:最后一次实名检查时间" json:"last_real_name_check_at"`
|
||||||
LastSyncTime *time.Time `gorm:"column:last_sync_time;comment:最后一次与Gateway同步时间" json:"last_sync_time"`
|
LastSyncTime *time.Time `gorm:"column:last_sync_time;comment:最后一次与Gateway同步时间" json:"last_sync_time"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (IotCard) TableName() string {
|
func (IotCard) TableName() string {
|
||||||
return "iot_cards"
|
return "tb_iot_card"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,26 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
// NumberCard 号卡模型
|
// NumberCard 号卡模型
|
||||||
// 完全独立的业务线,从上游平台下单
|
// 完全独立的业务线,从上游平台下单
|
||||||
// 使用虚拟商品编码映射运营商订单
|
// 使用虚拟商品编码映射运营商订单
|
||||||
type NumberCard struct {
|
type NumberCard struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:号卡ID" json:"id"`
|
gorm.Model
|
||||||
VirtualProductCode string `gorm:"column:virtual_product_code;type:varchar(100);uniqueIndex;not null;comment:虚拟商品编码(用于对应运营商订单)" json:"virtual_product_code"`
|
BaseModel `gorm:"embedded"`
|
||||||
CardName string `gorm:"column:card_name;type:varchar(255);not null;comment:号卡名称" json:"card_name"`
|
VirtualProductCode string `gorm:"column:virtual_product_code;type:varchar(100);uniqueIndex:idx_number_card_code,where:deleted_at IS NULL;not null;comment:虚拟商品编码(用于对应运营商订单)" json:"virtual_product_code"`
|
||||||
CardType string `gorm:"column:card_type;type:varchar(50);comment:号卡类型" json:"card_type"`
|
CardName string `gorm:"column:card_name;type:varchar(255);not null;comment:号卡名称" json:"card_name"`
|
||||||
Carrier string `gorm:"column:carrier;type:varchar(50);comment:运营商" json:"carrier"`
|
CardType string `gorm:"column:card_type;type:varchar(50);comment:号卡类型" json:"card_type"`
|
||||||
DataAmountMB int64 `gorm:"column:data_amount_mb;type:bigint;comment:流量额度(MB)" json:"data_amount_mb"`
|
Carrier string `gorm:"column:carrier;type:varchar(50);comment:运营商" json:"carrier"`
|
||||||
Price float64 `gorm:"column:price;type:decimal(10,2);comment:价格(元)" json:"price"`
|
DataAmountMB int64 `gorm:"column:data_amount_mb;type:bigint;comment:流量额度(MB)" json:"data_amount_mb"`
|
||||||
AgentID uint `gorm:"column:agent_id;type:bigint;comment:代理用户ID" json:"agent_id"`
|
Price int64 `gorm:"column:price;type:bigint;comment:价格(分为单位)" json:"price"`
|
||||||
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-在售 2-下架" json:"status"`
|
AgentID uint `gorm:"column:agent_id;index;comment:代理用户ID" json:"agent_id"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-在售 2-下架" json:"status"`
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (NumberCard) TableName() string {
|
func (NumberCard) TableName() string {
|
||||||
return "number_cards"
|
return "tb_number_card"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,33 +3,33 @@ package model
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/lib/pq"
|
"gorm.io/datatypes"
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Order 订单模型
|
// Order 订单模型
|
||||||
// 支持两种订单类型:套餐订单(单卡/设备级)、号卡订单
|
// 支持两种订单类型:套餐订单(单卡/设备级)、号卡订单
|
||||||
type Order struct {
|
type Order struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:订单ID" json:"id"`
|
gorm.Model
|
||||||
OrderNo string `gorm:"column:order_no;type:varchar(100);uniqueIndex;not null;comment:订单号(唯一标识)" json:"order_no"`
|
BaseModel `gorm:"embedded"`
|
||||||
|
OrderNo string `gorm:"column:order_no;type:varchar(100);uniqueIndex:idx_order_no,where:deleted_at IS NULL;not null;comment:订单号(唯一标识)" json:"order_no"`
|
||||||
OrderType int `gorm:"column:order_type;type:int;not null;comment:订单类型 1-套餐订单 2-号卡订单" json:"order_type"`
|
OrderType int `gorm:"column:order_type;type:int;not null;comment:订单类型 1-套餐订单 2-号卡订单" json:"order_type"`
|
||||||
IotCardID uint `gorm:"column:iot_card_id;type:bigint;comment:IoT卡ID(单卡套餐订单时有值)" json:"iot_card_id"`
|
IotCardID uint `gorm:"column:iot_card_id;index;comment:IoT卡ID(单卡套餐订单时有值)" json:"iot_card_id"`
|
||||||
DeviceID uint `gorm:"column:device_id;type:bigint;comment:设备ID(设备级套餐订单时有值)" json:"device_id"`
|
DeviceID uint `gorm:"column:device_id;index;comment:设备ID(设备级套餐订单时有值)" json:"device_id"`
|
||||||
NumberCardID uint `gorm:"column:number_card_id;type:bigint;comment:号卡ID(号卡订单时有值)" json:"number_card_id"`
|
NumberCardID uint `gorm:"column:number_card_id;index;comment:号卡ID(号卡订单时有值)" json:"number_card_id"`
|
||||||
PackageID uint `gorm:"column:package_id;type:bigint;comment:套餐ID(套餐订单时有值)" json:"package_id"`
|
PackageID uint `gorm:"column:package_id;index;comment:套餐ID(套餐订单时有值)" json:"package_id"`
|
||||||
UserID uint `gorm:"column:user_id;type:bigint;not null;comment:用户ID" json:"user_id"`
|
UserID uint `gorm:"column:user_id;index;not null;comment:用户ID" json:"user_id"`
|
||||||
AgentID uint `gorm:"column:agent_id;type:bigint;comment:代理用户ID" json:"agent_id"`
|
AgentID uint `gorm:"column:agent_id;index;comment:代理用户ID" json:"agent_id"`
|
||||||
Amount float64 `gorm:"column:amount;type:decimal(10,2);not null;comment:订单金额(元)" json:"amount"`
|
Amount int64 `gorm:"column:amount;type:bigint;not null;comment:订单金额(分为单位)" json:"amount"`
|
||||||
PaymentMethod string `gorm:"column:payment_method;type:varchar(20);comment:支付方式 wallet-钱包 online-在线支付 carrier-运营商支付" json:"payment_method"`
|
PaymentMethod string `gorm:"column:payment_method;type:varchar(20);comment:支付方式 wallet-钱包 online-在线支付 carrier-运营商支付" json:"payment_method"`
|
||||||
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-待支付 2-已支付 3-已完成 4-已取消 5-已退款" json:"status"`
|
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-待支付 2-已支付 3-已完成 4-已取消 5-已退款" json:"status"`
|
||||||
CarrierOrderID string `gorm:"column:carrier_order_id;type:varchar(255);comment:运营商订单ID" json:"carrier_order_id"`
|
CarrierOrderID string `gorm:"column:carrier_order_id;type:varchar(255);comment:运营商订单ID" json:"carrier_order_id"`
|
||||||
CarrierOrderData pq.StringArray `gorm:"column:carrier_order_data;type:jsonb;comment:运营商订单原始数据(JSON)" json:"carrier_order_data"`
|
CarrierOrderData datatypes.JSON `gorm:"column:carrier_order_data;type:jsonb;comment:运营商订单原始数据(JSON)" json:"carrier_order_data"`
|
||||||
PaidAt *time.Time `gorm:"column:paid_at;comment:支付时间" json:"paid_at"`
|
PaidAt *time.Time `gorm:"column:paid_at;comment:支付时间" json:"paid_at"`
|
||||||
CompletedAt *time.Time `gorm:"column:completed_at;comment:完成时间" json:"completed_at"`
|
CompletedAt *time.Time `gorm:"column:completed_at;comment:完成时间" json:"completed_at"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (Order) TableName() string {
|
func (Order) TableName() string {
|
||||||
return "orders"
|
return "tb_order"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,94 +1,95 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
// PackageSeries 套餐系列模型
|
// PackageSeries 套餐系列模型
|
||||||
// 套餐的分组,用于一次性分佣规则配置
|
// 套餐的分组,用于一次性分佣规则配置
|
||||||
type PackageSeries struct {
|
type PackageSeries struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:套餐系列ID" json:"id"`
|
gorm.Model
|
||||||
SeriesCode string `gorm:"column:series_code;type:varchar(100);uniqueIndex;not null;comment:系列编码" json:"series_code"`
|
BaseModel `gorm:"embedded"`
|
||||||
SeriesName string `gorm:"column:series_name;type:varchar(255);not null;comment:系列名称" json:"series_name"`
|
SeriesCode string `gorm:"column:series_code;type:varchar(100);uniqueIndex:idx_package_series_code,where:deleted_at IS NULL;not null;comment:系列编码" json:"series_code"`
|
||||||
Description string `gorm:"column:description;type:text;comment:描述" json:"description"`
|
SeriesName string `gorm:"column:series_name;type:varchar(255);not null;comment:系列名称" json:"series_name"`
|
||||||
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-启用 2-禁用" json:"status"`
|
Description string `gorm:"column:description;type:text;comment:描述" json:"description"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-启用 2-禁用" json:"status"`
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (PackageSeries) TableName() string {
|
func (PackageSeries) TableName() string {
|
||||||
return "package_series"
|
return "tb_package_series"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Package 套餐模型
|
// Package 套餐模型
|
||||||
// 只适用于 IoT 卡,支持真流量/虚流量共存机制
|
// 只适用于 IoT 卡,支持真流量/虚流量共存机制
|
||||||
type Package struct {
|
type Package struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:套餐ID" json:"id"`
|
gorm.Model
|
||||||
PackageCode string `gorm:"column:package_code;type:varchar(100);uniqueIndex;not null;comment:套餐编码" json:"package_code"`
|
BaseModel `gorm:"embedded"`
|
||||||
PackageName string `gorm:"column:package_name;type:varchar(255);not null;comment:套餐名称" json:"package_name"`
|
PackageCode string `gorm:"column:package_code;type:varchar(100);uniqueIndex:idx_package_code,where:deleted_at IS NULL;not null;comment:套餐编码" json:"package_code"`
|
||||||
SeriesID uint `gorm:"column:series_id;type:bigint;comment:套餐系列ID" json:"series_id"`
|
PackageName string `gorm:"column:package_name;type:varchar(255);not null;comment:套餐名称" json:"package_name"`
|
||||||
PackageType string `gorm:"column:package_type;type:varchar(50);not null;comment:套餐类型 formal-正式套餐 addon-附加套餐" json:"package_type"`
|
SeriesID uint `gorm:"column:series_id;index;comment:套餐系列ID" json:"series_id"`
|
||||||
DurationMonths int `gorm:"column:duration_months;type:int;not null;comment:套餐时长(月数) 1-月套餐 12-年套餐" json:"duration_months"`
|
PackageType string `gorm:"column:package_type;type:varchar(50);not null;comment:套餐类型 formal-正式套餐 addon-附加套餐" json:"package_type"`
|
||||||
DataType string `gorm:"column:data_type;type:varchar(20);comment:流量类型 real-真流量 virtual-虚流量" json:"data_type"`
|
DurationMonths int `gorm:"column:duration_months;type:int;not null;comment:套餐时长(月数) 1-月套餐 12-年套餐" json:"duration_months"`
|
||||||
RealDataMB int64 `gorm:"column:real_data_mb;type:bigint;default:0;comment:真流量额度(MB)" json:"real_data_mb"`
|
DataType string `gorm:"column:data_type;type:varchar(20);comment:流量类型 real-真流量 virtual-虚流量" json:"data_type"`
|
||||||
VirtualDataMB int64 `gorm:"column:virtual_data_mb;type:bigint;default:0;comment:虚流量额度(MB,用于停机判断)" json:"virtual_data_mb"`
|
RealDataMB int64 `gorm:"column:real_data_mb;type:bigint;default:0;comment:真流量额度(MB)" json:"real_data_mb"`
|
||||||
DataAmountMB int64 `gorm:"column:data_amount_mb;type:bigint;default:0;comment:总流量额度(MB)" json:"data_amount_mb"`
|
VirtualDataMB int64 `gorm:"column:virtual_data_mb;type:bigint;default:0;comment:虚流量额度(MB,用于停机判断)" json:"virtual_data_mb"`
|
||||||
Price float64 `gorm:"column:price;type:decimal(10,2);not null;comment:套餐价格(元)" json:"price"`
|
DataAmountMB int64 `gorm:"column:data_amount_mb;type:bigint;default:0;comment:总流量额度(MB)" json:"data_amount_mb"`
|
||||||
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-启用 2-禁用" json:"status"`
|
Price int64 `gorm:"column:price;type:bigint;not null;comment:套餐价格(分为单位)" json:"price"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-启用 2-禁用" json:"status"`
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (Package) TableName() string {
|
func (Package) TableName() string {
|
||||||
return "packages"
|
return "tb_package"
|
||||||
}
|
}
|
||||||
|
|
||||||
// AgentPackageAllocation 代理套餐分配模型
|
// AgentPackageAllocation 代理套餐分配模型
|
||||||
// 为直属下级代理分配套餐,设置佣金模式
|
// 为直属下级代理分配套餐,设置佣金模式
|
||||||
type AgentPackageAllocation struct {
|
type AgentPackageAllocation struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:分配ID" json:"id"`
|
gorm.Model
|
||||||
AgentID uint `gorm:"column:agent_id;type:bigint;not null;comment:代理用户ID" json:"agent_id"`
|
BaseModel `gorm:"embedded"`
|
||||||
PackageID uint `gorm:"column:package_id;type:bigint;not null;comment:套餐ID" json:"package_id"`
|
AgentID uint `gorm:"column:agent_id;index;not null;comment:代理用户ID" json:"agent_id"`
|
||||||
CostPrice float64 `gorm:"column:cost_price;type:decimal(10,2);not null;comment:成本价(元)" json:"cost_price"`
|
PackageID uint `gorm:"column:package_id;index;not null;comment:套餐ID" json:"package_id"`
|
||||||
RetailPrice float64 `gorm:"column:retail_price;type:decimal(10,2);not null;comment:零售价(元)" json:"retail_price"`
|
CostPrice int64 `gorm:"column:cost_price;type:bigint;not null;comment:成本价(分为单位)" json:"cost_price"`
|
||||||
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-启用 2-禁用" json:"status"`
|
RetailPrice int64 `gorm:"column:retail_price;type:bigint;not null;comment:零售价(分为单位)" json:"retail_price"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-启用 2-禁用" json:"status"`
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (AgentPackageAllocation) TableName() string {
|
func (AgentPackageAllocation) TableName() string {
|
||||||
return "agent_package_allocations"
|
return "tb_agent_package_allocation"
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeviceSimBinding 设备-IoT卡绑定关系模型
|
// DeviceSimBinding 设备-IoT卡绑定关系模型
|
||||||
// 管理设备与 IoT 卡的多对多绑定关系(1 设备绑定 1-4 张 IoT 卡)
|
// 管理设备与 IoT 卡的多对多绑定关系(1 设备绑定 1-4 张 IoT 卡)
|
||||||
type DeviceSimBinding struct {
|
type DeviceSimBinding struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:绑定ID" json:"id"`
|
gorm.Model
|
||||||
DeviceID uint `gorm:"column:device_id;type:bigint;not null;comment:设备ID" json:"device_id"`
|
BaseModel `gorm:"embedded"`
|
||||||
IotCardID uint `gorm:"column:iot_card_id;type:bigint;not null;comment:IoT卡ID" json:"iot_card_id"`
|
DeviceID uint `gorm:"column:device_id;index:idx_device_slot;not null;comment:设备ID" json:"device_id"`
|
||||||
SlotPosition int `gorm:"column:slot_position;type:int;comment:插槽位置(1, 2, 3, 4)" json:"slot_position"`
|
IotCardID uint `gorm:"column:iot_card_id;index;not null;comment:IoT卡ID" json:"iot_card_id"`
|
||||||
|
SlotPosition int `gorm:"column:slot_position;type:int;index:idx_device_slot;comment:插槽位置(1, 2, 3, 4)" json:"slot_position"`
|
||||||
BindStatus int `gorm:"column:bind_status;type:int;default:1;comment:绑定状态 1-已绑定 2-已解绑" json:"bind_status"`
|
BindStatus int `gorm:"column:bind_status;type:int;default:1;comment:绑定状态 1-已绑定 2-已解绑" json:"bind_status"`
|
||||||
BindTime *time.Time `gorm:"column:bind_time;comment:绑定时间" json:"bind_time"`
|
BindTime *time.Time `gorm:"column:bind_time;comment:绑定时间" json:"bind_time"`
|
||||||
UnbindTime *time.Time `gorm:"column:unbind_time;comment:解绑时间" json:"unbind_time"`
|
UnbindTime *time.Time `gorm:"column:unbind_time;comment:解绑时间" json:"unbind_time"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (DeviceSimBinding) TableName() string {
|
func (DeviceSimBinding) TableName() string {
|
||||||
return "device_sim_bindings"
|
return "tb_device_sim_binding"
|
||||||
}
|
}
|
||||||
|
|
||||||
// PackageUsage 套餐使用情况模型
|
// PackageUsage 套餐使用情况模型
|
||||||
// 跟踪单卡套餐和设备级套餐的流量使用
|
// 跟踪单卡套餐和设备级套餐的流量使用
|
||||||
type PackageUsage struct {
|
type PackageUsage struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:套餐使用ID" json:"id"`
|
gorm.Model
|
||||||
OrderID uint `gorm:"column:order_id;type:bigint;not null;comment:订单ID" json:"order_id"`
|
BaseModel `gorm:"embedded"`
|
||||||
PackageID uint `gorm:"column:package_id;type:bigint;not null;comment:套餐ID" json:"package_id"`
|
OrderID uint `gorm:"column:order_id;index;not null;comment:订单ID" json:"order_id"`
|
||||||
|
PackageID uint `gorm:"column:package_id;index;not null;comment:套餐ID" json:"package_id"`
|
||||||
UsageType string `gorm:"column:usage_type;type:varchar(20);not null;comment:使用类型 single_card-单卡套餐 device-设备级套餐" json:"usage_type"`
|
UsageType string `gorm:"column:usage_type;type:varchar(20);not null;comment:使用类型 single_card-单卡套餐 device-设备级套餐" json:"usage_type"`
|
||||||
IotCardID uint `gorm:"column:iot_card_id;type:bigint;comment:IoT卡ID(单卡套餐时有值)" json:"iot_card_id"`
|
IotCardID uint `gorm:"column:iot_card_id;index;comment:IoT卡ID(单卡套餐时有值)" json:"iot_card_id"`
|
||||||
DeviceID uint `gorm:"column:device_id;type:bigint;comment:设备ID(设备级套餐时有值)" json:"device_id"`
|
DeviceID uint `gorm:"column:device_id;index;comment:设备ID(设备级套餐时有值)" json:"device_id"`
|
||||||
DataLimitMB int64 `gorm:"column:data_limit_mb;type:bigint;not null;comment:流量限额(MB)" json:"data_limit_mb"`
|
DataLimitMB int64 `gorm:"column:data_limit_mb;type:bigint;not null;comment:流量限额(MB)" json:"data_limit_mb"`
|
||||||
DataUsageMB int64 `gorm:"column:data_usage_mb;type:bigint;default:0;comment:已使用流量(MB)" json:"data_usage_mb"`
|
DataUsageMB int64 `gorm:"column:data_usage_mb;type:bigint;default:0;comment:已使用流量(MB)" json:"data_usage_mb"`
|
||||||
RealDataUsageMB int64 `gorm:"column:real_data_usage_mb;type:bigint;default:0;comment:真流量使用(MB)" json:"real_data_usage_mb"`
|
RealDataUsageMB int64 `gorm:"column:real_data_usage_mb;type:bigint;default:0;comment:真流量使用(MB)" json:"real_data_usage_mb"`
|
||||||
@@ -97,11 +98,9 @@ type PackageUsage struct {
|
|||||||
ExpiresAt time.Time `gorm:"column:expires_at;not null;comment:套餐过期时间" json:"expires_at"`
|
ExpiresAt time.Time `gorm:"column:expires_at;not null;comment:套餐过期时间" json:"expires_at"`
|
||||||
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-生效中 2-已用完 3-已过期" json:"status"`
|
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-生效中 2-已用完 3-已过期" json:"status"`
|
||||||
LastPackageCheckAt *time.Time `gorm:"column:last_package_check_at;comment:最后一次套餐流量检查时间" json:"last_package_check_at"`
|
LastPackageCheckAt *time.Time `gorm:"column:last_package_check_at;comment:最后一次套餐流量检查时间" json:"last_package_check_at"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (PackageUsage) TableName() string {
|
func (PackageUsage) TableName() string {
|
||||||
return "package_usages"
|
return "tb_package_usage"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,29 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
// PollingConfig 轮询配置模型
|
// PollingConfig 轮询配置模型
|
||||||
// 支持梯度轮询策略(实名检查、卡流量检查、套餐流量检查)
|
// 支持梯度轮询策略(实名检查、卡流量检查、套餐流量检查)
|
||||||
type PollingConfig struct {
|
type PollingConfig struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:轮询配置ID" json:"id"`
|
gorm.Model
|
||||||
ConfigName string `gorm:"column:config_name;type:varchar(100);uniqueIndex;not null;comment:配置名称(如 未实名卡、实名卡)" json:"config_name"`
|
BaseModel `gorm:"embedded"`
|
||||||
Description string `gorm:"column:description;type:varchar(500);comment:配置描述" json:"description"`
|
ConfigName string `gorm:"column:config_name;type:varchar(100);uniqueIndex:idx_polling_config_name,where:deleted_at IS NULL;not null;comment:配置名称(如 未实名卡、实名卡)" json:"config_name"`
|
||||||
CardCondition string `gorm:"column:card_condition;type:varchar(50);comment:卡状态条件 not_real_name-未实名 real_name-已实名 activated-已激活 suspended-已停用" json:"card_condition"`
|
Description string `gorm:"column:description;type:varchar(500);comment:配置描述" json:"description"`
|
||||||
CarrierID uint `gorm:"column:carrier_id;type:bigint;comment:运营商ID(NULL表示所有运营商)" json:"carrier_id"`
|
CardCondition string `gorm:"column:card_condition;type:varchar(50);comment:卡状态条件 not_real_name-未实名 real_name-已实名 activated-已激活 suspended-已停用" json:"card_condition"`
|
||||||
RealNameCheckEnabled bool `gorm:"column:real_name_check_enabled;type:boolean;default:false;comment:是否启用实名检查" json:"real_name_check_enabled"`
|
CarrierID uint `gorm:"column:carrier_id;index;comment:运营商ID(NULL表示所有运营商)" json:"carrier_id"`
|
||||||
RealNameCheckInterval int `gorm:"column:real_name_check_interval;type:int;default:60;comment:实名检查间隔(秒)" json:"real_name_check_interval"`
|
RealNameCheckEnabled bool `gorm:"column:real_name_check_enabled;type:boolean;default:false;comment:是否启用实名检查" json:"real_name_check_enabled"`
|
||||||
CardDataCheckEnabled bool `gorm:"column:card_data_check_enabled;type:boolean;default:false;comment:是否启用卡流量检查" json:"card_data_check_enabled"`
|
RealNameCheckInterval int `gorm:"column:real_name_check_interval;type:int;default:60;comment:实名检查间隔(秒)" json:"real_name_check_interval"`
|
||||||
CardDataCheckInterval int `gorm:"column:card_data_check_interval;type:int;default:60;comment:卡流量检查间隔(秒)" json:"card_data_check_interval"`
|
CardDataCheckEnabled bool `gorm:"column:card_data_check_enabled;type:boolean;default:false;comment:是否启用卡流量检查" json:"card_data_check_enabled"`
|
||||||
PackageCheckEnabled bool `gorm:"column:package_check_enabled;type:boolean;default:false;comment:是否启用套餐流量检查" json:"package_check_enabled"`
|
CardDataCheckInterval int `gorm:"column:card_data_check_interval;type:int;default:60;comment:卡流量检查间隔(秒)" json:"card_data_check_interval"`
|
||||||
PackageCheckInterval int `gorm:"column:package_check_interval;type:int;default:60;comment:套餐流量检查间隔(秒)" json:"package_check_interval"`
|
PackageCheckEnabled bool `gorm:"column:package_check_enabled;type:boolean;default:false;comment:是否启用套餐流量检查" json:"package_check_enabled"`
|
||||||
Priority int `gorm:"column:priority;type:int;default:100;not null;comment:优先级(数字越小优先级越高)" json:"priority"`
|
PackageCheckInterval int `gorm:"column:package_check_interval;type:int;default:60;comment:套餐流量检查间隔(秒)" json:"package_check_interval"`
|
||||||
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-启用 2-禁用" json:"status"`
|
Priority int `gorm:"column:priority;type:int;default:100;not null;comment:优先级(数字越小优先级越高)" json:"priority"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-启用 2-禁用" json:"status"`
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (PollingConfig) TableName() string {
|
func (PollingConfig) TableName() string {
|
||||||
return "polling_configs"
|
return "tb_polling_config"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,45 +1,47 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
// DevCapabilityConfig 开发能力配置模型
|
// DevCapabilityConfig 开发能力配置模型
|
||||||
// 管理 API 对接参数(AppID、AppSecret、回调地址等)
|
// 管理 API 对接参数(AppID、AppSecret、回调地址等)
|
||||||
type DevCapabilityConfig struct {
|
type DevCapabilityConfig struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:开发能力配置ID" json:"id"`
|
gorm.Model
|
||||||
UserID uint `gorm:"column:user_id;type:bigint;not null;comment:用户ID(平台或代理)" json:"user_id"`
|
BaseModel `gorm:"embedded"`
|
||||||
AppName string `gorm:"column:app_name;type:varchar(255);comment:应用名称" json:"app_name"`
|
UserID uint `gorm:"column:user_id;index;not null;comment:用户ID(平台或代理)" json:"user_id"`
|
||||||
AppID string `gorm:"column:app_id;type:varchar(100);uniqueIndex;comment:应用ID" json:"app_id"`
|
AppName string `gorm:"column:app_name;type:varchar(255);comment:应用名称" json:"app_name"`
|
||||||
AppSecret string `gorm:"column:app_secret;type:varchar(255);comment:应用密钥" json:"app_secret"`
|
AppID string `gorm:"column:app_id;type:varchar(100);uniqueIndex:idx_dev_capability_app,where:deleted_at IS NULL;comment:应用ID" json:"app_id"`
|
||||||
CallbackURL string `gorm:"column:callback_url;type:varchar(500);comment:回调地址" json:"callback_url"`
|
AppSecret string `gorm:"column:app_secret;type:varchar(255);comment:应用密钥" json:"app_secret"`
|
||||||
IPWhitelist string `gorm:"column:ip_whitelist;type:text;comment:IP白名单(多个IP用逗号分隔)" json:"ip_whitelist"`
|
CallbackURL string `gorm:"column:callback_url;type:varchar(500);comment:回调地址" json:"callback_url"`
|
||||||
Status int `gorm:"column:status;type:int;default:1;comment:状态 1-启用 2-禁用" json:"status"`
|
IPWhitelist string `gorm:"column:ip_whitelist;type:text;comment:IP白名单(多个IP用逗号分隔)" json:"ip_whitelist"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
Status int `gorm:"column:status;type:int;default:1;comment:状态 1-启用 2-禁用" json:"status"`
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (DevCapabilityConfig) TableName() string {
|
func (DevCapabilityConfig) TableName() string {
|
||||||
return "dev_capability_configs"
|
return "tb_dev_capability_config"
|
||||||
}
|
}
|
||||||
|
|
||||||
// CardReplacementRequest 换卡申请模型
|
// CardReplacementRequest 换卡申请模型
|
||||||
// 客户提交的换卡申请管理,处理换卡申请
|
// 客户提交的换卡申请管理,处理换卡申请
|
||||||
type CardReplacementRequest struct {
|
type CardReplacementRequest struct {
|
||||||
ID uint `gorm:"column:id;primaryKey;comment:换卡申请ID" json:"id"`
|
gorm.Model
|
||||||
UserID uint `gorm:"column:user_id;type:bigint;not null;comment:申请用户ID" json:"user_id"`
|
BaseModel `gorm:"embedded"`
|
||||||
|
UserID uint `gorm:"column:user_id;index;not null;comment:申请用户ID" json:"user_id"`
|
||||||
OldICCID string `gorm:"column:old_iccid;type:varchar(50);not null;comment:旧卡ICCID" json:"old_iccid"`
|
OldICCID string `gorm:"column:old_iccid;type:varchar(50);not null;comment:旧卡ICCID" json:"old_iccid"`
|
||||||
NewICCID string `gorm:"column:new_iccid;type:varchar(50);comment:新卡ICCID(审批时填充)" json:"new_iccid"`
|
NewICCID string `gorm:"column:new_iccid;type:varchar(50);comment:新卡ICCID(审批时填充)" json:"new_iccid"`
|
||||||
Reason string `gorm:"column:reason;type:text;comment:换卡原因" json:"reason"`
|
Reason string `gorm:"column:reason;type:text;comment:换卡原因" json:"reason"`
|
||||||
Status int `gorm:"column:status;type:int;default:1;comment:状态 1-待处理 2-已通过 3-已拒绝 4-已完成" json:"status"`
|
Status int `gorm:"column:status;type:int;default:1;comment:状态 1-待处理 2-已通过 3-已拒绝 4-已完成" json:"status"`
|
||||||
ApprovedBy uint `gorm:"column:approved_by;type:bigint;comment:处理人用户ID" json:"approved_by"`
|
ApprovedBy uint `gorm:"column:approved_by;index;comment:处理人用户ID" json:"approved_by"`
|
||||||
ApprovedAt *time.Time `gorm:"column:approved_at;comment:处理时间" json:"approved_at"`
|
ApprovedAt *time.Time `gorm:"column:approved_at;comment:处理时间" json:"approved_at"`
|
||||||
CompletedAt *time.Time `gorm:"column:completed_at;comment:完成时间(新卡激活时间)" json:"completed_at"`
|
CompletedAt *time.Time `gorm:"column:completed_at;comment:完成时间(新卡激活时间)" json:"completed_at"`
|
||||||
RejectReason string `gorm:"column:reject_reason;type:text;comment:拒绝原因" json:"reject_reason"`
|
RejectReason string `gorm:"column:reject_reason;type:text;comment:拒绝原因" json:"reject_reason"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
func (CardReplacementRequest) TableName() string {
|
func (CardReplacementRequest) TableName() string {
|
||||||
return "card_replacement_requests"
|
return "tb_card_replacement_request"
|
||||||
}
|
}
|
||||||
|
|||||||
251
migrations/000006_refactor_iot_models_architecture.down.sql
Normal file
251
migrations/000006_refactor_iot_models_architecture.down.sql
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
-- IoT 模型架构重构回滚脚本
|
||||||
|
-- 创建时间: 2026-01-12
|
||||||
|
-- 说明: 回滚所有架构重构变更
|
||||||
|
-- 1. 恢复唯一索引(移除软删除支持)
|
||||||
|
-- 2. 恢复金额字段为 DECIMAL 类型
|
||||||
|
-- 3. 删除软删除字段和审计字段
|
||||||
|
-- 4. 恢复表名为复数形式
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- 阶段 1: 恢复唯一约束(移除软删除支持)
|
||||||
|
-- ========================================
|
||||||
|
|
||||||
|
-- 1.1 运营商表
|
||||||
|
DROP INDEX IF EXISTS idx_carrier_code;
|
||||||
|
ALTER TABLE tb_carrier ADD CONSTRAINT carriers_carrier_code_key UNIQUE (carrier_code);
|
||||||
|
|
||||||
|
-- 1.2 IoT 卡表
|
||||||
|
DROP INDEX IF EXISTS idx_iot_card_iccid;
|
||||||
|
ALTER TABLE tb_iot_card ADD CONSTRAINT iot_cards_iccid_key UNIQUE (iccid);
|
||||||
|
|
||||||
|
-- 1.3 设备表
|
||||||
|
DROP INDEX IF EXISTS idx_device_no;
|
||||||
|
ALTER TABLE tb_device ADD CONSTRAINT devices_device_no_key UNIQUE (device_no);
|
||||||
|
|
||||||
|
-- 1.4 号卡表
|
||||||
|
DROP INDEX IF EXISTS idx_number_card_code;
|
||||||
|
ALTER TABLE tb_number_card ADD CONSTRAINT number_cards_virtual_product_code_key UNIQUE (virtual_product_code);
|
||||||
|
|
||||||
|
-- 1.5 套餐系列表
|
||||||
|
DROP INDEX IF EXISTS idx_package_series_code;
|
||||||
|
ALTER TABLE tb_package_series ADD CONSTRAINT package_series_series_code_key UNIQUE (series_code);
|
||||||
|
|
||||||
|
-- 1.6 套餐表
|
||||||
|
DROP INDEX IF EXISTS idx_package_code;
|
||||||
|
ALTER TABLE tb_package ADD CONSTRAINT packages_package_code_key UNIQUE (package_code);
|
||||||
|
|
||||||
|
-- 1.7 代理套餐分配表
|
||||||
|
DROP INDEX IF EXISTS idx_agent_package_allocation_agent_package;
|
||||||
|
ALTER TABLE tb_agent_package_allocation ADD CONSTRAINT uk_agent_package UNIQUE (agent_id, package_id);
|
||||||
|
|
||||||
|
-- 1.8 设备-SIM绑定表:跳过,保持原样
|
||||||
|
|
||||||
|
-- 1.9 订单表
|
||||||
|
DROP INDEX IF EXISTS idx_order_no;
|
||||||
|
ALTER TABLE tb_order ADD CONSTRAINT orders_order_no_key UNIQUE (order_no);
|
||||||
|
|
||||||
|
-- 1.10 轮询配置表
|
||||||
|
DROP INDEX IF EXISTS idx_polling_config_name;
|
||||||
|
ALTER TABLE tb_polling_config ADD CONSTRAINT polling_configs_config_name_key UNIQUE (config_name);
|
||||||
|
|
||||||
|
-- 1.11 代理层级表
|
||||||
|
DROP INDEX IF EXISTS idx_agent_hierarchy_agent;
|
||||||
|
ALTER TABLE tb_agent_hierarchy ADD CONSTRAINT agent_hierarchies_agent_id_key UNIQUE (agent_id);
|
||||||
|
|
||||||
|
-- 1.12 组合分佣条件表
|
||||||
|
DROP INDEX IF EXISTS idx_commission_combined_rule;
|
||||||
|
ALTER TABLE tb_commission_combined_condition ADD CONSTRAINT commission_combined_conditions_rule_id_key UNIQUE (rule_id);
|
||||||
|
|
||||||
|
-- 1.13 分佣模板表
|
||||||
|
DROP INDEX IF EXISTS idx_commission_template_name;
|
||||||
|
ALTER TABLE tb_commission_template ADD CONSTRAINT commission_templates_template_name_key UNIQUE (template_name);
|
||||||
|
|
||||||
|
-- 1.14 运营商结算表
|
||||||
|
DROP INDEX IF EXISTS idx_carrier_settlement_record;
|
||||||
|
ALTER TABLE tb_carrier_settlement ADD CONSTRAINT carrier_settlements_commission_record_id_key UNIQUE (commission_record_id);
|
||||||
|
|
||||||
|
-- 1.15 开发能力配置表
|
||||||
|
DROP INDEX IF EXISTS idx_dev_capability_app;
|
||||||
|
ALTER TABLE tb_dev_capability_config ADD CONSTRAINT dev_capability_configs_app_id_key UNIQUE (app_id);
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- 阶段 2: 恢复金额字段为 DECIMAL 类型(分 / 100 = 元)
|
||||||
|
-- ========================================
|
||||||
|
|
||||||
|
-- 2.1 IoT 卡表
|
||||||
|
ALTER TABLE tb_iot_card
|
||||||
|
ALTER COLUMN cost_price TYPE DECIMAL(10,2) USING (cost_price / 100.0)::DECIMAL(10,2),
|
||||||
|
ALTER COLUMN distribute_price TYPE DECIMAL(10,2) USING (distribute_price / 100.0)::DECIMAL(10,2);
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_iot_card.cost_price IS '成本价(元)';
|
||||||
|
COMMENT ON COLUMN tb_iot_card.distribute_price IS '分销价(元)';
|
||||||
|
|
||||||
|
-- 2.2 号卡表
|
||||||
|
ALTER TABLE tb_number_card
|
||||||
|
ALTER COLUMN price TYPE DECIMAL(10,2) USING (price / 100.0)::DECIMAL(10,2);
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_number_card.price IS '价格(元)';
|
||||||
|
|
||||||
|
-- 2.3 套餐表
|
||||||
|
ALTER TABLE tb_package
|
||||||
|
ALTER COLUMN price TYPE DECIMAL(10,2) USING (price / 100.0)::DECIMAL(10,2);
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_package.price IS '套餐价格(元)';
|
||||||
|
|
||||||
|
-- 2.4 代理套餐分配表
|
||||||
|
ALTER TABLE tb_agent_package_allocation
|
||||||
|
ALTER COLUMN cost_price TYPE DECIMAL(10,2) USING (cost_price / 100.0)::DECIMAL(10,2),
|
||||||
|
ALTER COLUMN retail_price TYPE DECIMAL(10,2) USING (retail_price / 100.0)::DECIMAL(10,2);
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_agent_package_allocation.cost_price IS '成本价(元)';
|
||||||
|
COMMENT ON COLUMN tb_agent_package_allocation.retail_price IS '零售价(元)';
|
||||||
|
|
||||||
|
-- 2.5 订单表
|
||||||
|
ALTER TABLE tb_order
|
||||||
|
ALTER COLUMN amount TYPE DECIMAL(10,2) USING (amount / 100.0)::DECIMAL(10,2);
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_order.amount IS '订单金额(元)';
|
||||||
|
|
||||||
|
-- 2.6 分佣规则表
|
||||||
|
ALTER TABLE tb_commission_rule
|
||||||
|
ALTER COLUMN commission_value TYPE DECIMAL(10,2) USING (commission_value / 100.0)::DECIMAL(10,2);
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_commission_rule.commission_value IS '分佣值(元或百分比)';
|
||||||
|
|
||||||
|
-- 2.7 阶梯分佣配置表
|
||||||
|
ALTER TABLE tb_commission_ladder
|
||||||
|
ALTER COLUMN commission_value TYPE DECIMAL(10,2) USING (commission_value / 100.0)::DECIMAL(10,2);
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_commission_ladder.commission_value IS '分佣值(元或百分比)';
|
||||||
|
|
||||||
|
-- 2.8 组合分佣条件表
|
||||||
|
ALTER TABLE tb_commission_combined_condition
|
||||||
|
ALTER COLUMN one_time_commission_value TYPE DECIMAL(10,2) USING (one_time_commission_value / 100.0)::DECIMAL(10,2),
|
||||||
|
ALTER COLUMN long_term_commission_value TYPE DECIMAL(10,2) USING (long_term_commission_value / 100.0)::DECIMAL(10,2);
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_commission_combined_condition.one_time_commission_value IS '一次性分佣值(元或百分比)';
|
||||||
|
COMMENT ON COLUMN tb_commission_combined_condition.long_term_commission_value IS '长期分佣值(元或百分比)';
|
||||||
|
|
||||||
|
-- 2.9 分佣记录表
|
||||||
|
ALTER TABLE tb_commission_record
|
||||||
|
ALTER COLUMN amount TYPE DECIMAL(10,2) USING (amount / 100.0)::DECIMAL(10,2);
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_commission_record.amount IS '分佣金额(元)';
|
||||||
|
|
||||||
|
-- 2.10 分佣模板表
|
||||||
|
ALTER TABLE tb_commission_template
|
||||||
|
ALTER COLUMN commission_value TYPE DECIMAL(10,2) USING (commission_value / 100.0)::DECIMAL(10,2);
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_commission_template.commission_value IS '分佣值(元或百分比)';
|
||||||
|
|
||||||
|
-- 2.11 运营商结算表
|
||||||
|
ALTER TABLE tb_carrier_settlement
|
||||||
|
ALTER COLUMN settlement_amount TYPE DECIMAL(10,2) USING (settlement_amount / 100.0)::DECIMAL(10,2);
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_carrier_settlement.settlement_amount IS '结算金额(元)';
|
||||||
|
|
||||||
|
-- 2.12 佣金提现申请表
|
||||||
|
ALTER TABLE tb_commission_withdrawal_request
|
||||||
|
ALTER COLUMN amount TYPE DECIMAL(10,2) USING (amount / 100.0)::DECIMAL(10,2),
|
||||||
|
ALTER COLUMN fee TYPE DECIMAL(10,2) USING (fee / 100.0)::DECIMAL(10,2),
|
||||||
|
ALTER COLUMN actual_amount TYPE DECIMAL(10,2) USING (actual_amount / 100.0)::DECIMAL(10,2);
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_commission_withdrawal_request.amount IS '提现金额(元)';
|
||||||
|
COMMENT ON COLUMN tb_commission_withdrawal_request.fee IS '手续费(元)';
|
||||||
|
COMMENT ON COLUMN tb_commission_withdrawal_request.actual_amount IS '实际到账金额(元)';
|
||||||
|
|
||||||
|
-- 2.13 佣金提现设置表
|
||||||
|
ALTER TABLE tb_commission_withdrawal_setting
|
||||||
|
ALTER COLUMN min_withdrawal_amount TYPE DECIMAL(10,2) USING (min_withdrawal_amount / 100.0)::DECIMAL(10,2);
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_commission_withdrawal_setting.min_withdrawal_amount IS '最低提现金额(元)';
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- 阶段 3: 删除软删除字段和审计字段
|
||||||
|
-- ========================================
|
||||||
|
|
||||||
|
ALTER TABLE tb_carrier DROP COLUMN deleted_at, DROP COLUMN creator, DROP COLUMN updater;
|
||||||
|
ALTER TABLE tb_iot_card DROP COLUMN deleted_at, DROP COLUMN creator, DROP COLUMN updater;
|
||||||
|
ALTER TABLE tb_device DROP COLUMN deleted_at, DROP COLUMN creator, DROP COLUMN updater;
|
||||||
|
ALTER TABLE tb_number_card DROP COLUMN deleted_at, DROP COLUMN creator, DROP COLUMN updater;
|
||||||
|
ALTER TABLE tb_package_series DROP COLUMN deleted_at, DROP COLUMN creator, DROP COLUMN updater;
|
||||||
|
ALTER TABLE tb_package DROP COLUMN deleted_at, DROP COLUMN creator, DROP COLUMN updater;
|
||||||
|
ALTER TABLE tb_agent_package_allocation DROP COLUMN deleted_at, DROP COLUMN creator, DROP COLUMN updater;
|
||||||
|
ALTER TABLE tb_device_sim_binding DROP COLUMN deleted_at, DROP COLUMN creator, DROP COLUMN updater;
|
||||||
|
ALTER TABLE tb_order DROP COLUMN deleted_at, DROP COLUMN creator, DROP COLUMN updater;
|
||||||
|
ALTER TABLE tb_package_usage DROP COLUMN deleted_at, DROP COLUMN creator, DROP COLUMN updater;
|
||||||
|
ALTER TABLE tb_polling_config DROP COLUMN deleted_at, DROP COLUMN creator, DROP COLUMN updater;
|
||||||
|
-- 注意: tb_data_usage_record 没有添加这些字段,所以不需要删除
|
||||||
|
ALTER TABLE tb_agent_hierarchy DROP COLUMN deleted_at, DROP COLUMN creator, DROP COLUMN updater;
|
||||||
|
ALTER TABLE tb_commission_rule DROP COLUMN deleted_at, DROP COLUMN creator, DROP COLUMN updater;
|
||||||
|
ALTER TABLE tb_commission_ladder DROP COLUMN deleted_at, DROP COLUMN creator, DROP COLUMN updater;
|
||||||
|
ALTER TABLE tb_commission_combined_condition DROP COLUMN deleted_at, DROP COLUMN creator, DROP COLUMN updater;
|
||||||
|
ALTER TABLE tb_commission_record DROP COLUMN deleted_at, DROP COLUMN creator, DROP COLUMN updater;
|
||||||
|
ALTER TABLE tb_commission_approval DROP COLUMN deleted_at, DROP COLUMN creator, DROP COLUMN updater;
|
||||||
|
ALTER TABLE tb_commission_template DROP COLUMN deleted_at, DROP COLUMN creator, DROP COLUMN updater;
|
||||||
|
ALTER TABLE tb_carrier_settlement DROP COLUMN deleted_at, DROP COLUMN creator, DROP COLUMN updater;
|
||||||
|
ALTER TABLE tb_commission_withdrawal_request DROP COLUMN deleted_at, DROP COLUMN creator, DROP COLUMN updater;
|
||||||
|
ALTER TABLE tb_commission_withdrawal_setting DROP COLUMN deleted_at, DROP COLUMN creator, DROP COLUMN updater;
|
||||||
|
ALTER TABLE tb_payment_merchant_setting DROP COLUMN deleted_at, DROP COLUMN creator, DROP COLUMN updater;
|
||||||
|
ALTER TABLE tb_dev_capability_config DROP COLUMN deleted_at, DROP COLUMN creator, DROP COLUMN updater;
|
||||||
|
ALTER TABLE tb_card_replacement_request DROP COLUMN deleted_at, DROP COLUMN creator, DROP COLUMN updater;
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- 阶段 4: 恢复表名为复数形式
|
||||||
|
-- ========================================
|
||||||
|
|
||||||
|
ALTER TABLE tb_carrier RENAME TO carriers;
|
||||||
|
ALTER TABLE tb_iot_card RENAME TO iot_cards;
|
||||||
|
ALTER TABLE tb_device RENAME TO devices;
|
||||||
|
ALTER TABLE tb_number_card RENAME TO number_cards;
|
||||||
|
ALTER TABLE tb_package_series RENAME TO package_series;
|
||||||
|
ALTER TABLE tb_package RENAME TO packages;
|
||||||
|
ALTER TABLE tb_agent_package_allocation RENAME TO agent_package_allocations;
|
||||||
|
ALTER TABLE tb_device_sim_binding RENAME TO device_sim_bindings;
|
||||||
|
ALTER TABLE tb_order RENAME TO orders;
|
||||||
|
ALTER TABLE tb_package_usage RENAME TO package_usages;
|
||||||
|
ALTER TABLE tb_polling_config RENAME TO polling_configs;
|
||||||
|
ALTER TABLE tb_data_usage_record RENAME TO data_usage_records;
|
||||||
|
ALTER TABLE tb_agent_hierarchy RENAME TO agent_hierarchies;
|
||||||
|
ALTER TABLE tb_commission_rule RENAME TO commission_rules;
|
||||||
|
ALTER TABLE tb_commission_ladder RENAME TO commission_ladder;
|
||||||
|
ALTER TABLE tb_commission_combined_condition RENAME TO commission_combined_conditions;
|
||||||
|
ALTER TABLE tb_commission_record RENAME TO commission_records;
|
||||||
|
ALTER TABLE tb_commission_approval RENAME TO commission_approvals;
|
||||||
|
ALTER TABLE tb_commission_template RENAME TO commission_templates;
|
||||||
|
ALTER TABLE tb_carrier_settlement RENAME TO carrier_settlements;
|
||||||
|
ALTER TABLE tb_commission_withdrawal_request RENAME TO commission_withdrawal_requests;
|
||||||
|
ALTER TABLE tb_commission_withdrawal_setting RENAME TO commission_withdrawal_settings;
|
||||||
|
ALTER TABLE tb_payment_merchant_setting RENAME TO payment_merchant_settings;
|
||||||
|
ALTER TABLE tb_dev_capability_config RENAME TO dev_capability_configs;
|
||||||
|
ALTER TABLE tb_card_replacement_request RENAME TO card_replacement_requests;
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- 阶段 5: 恢复表注释
|
||||||
|
-- ========================================
|
||||||
|
|
||||||
|
COMMENT ON TABLE carriers IS '运营商表';
|
||||||
|
COMMENT ON TABLE iot_cards IS 'IoT 卡表(物联网卡/流量卡)';
|
||||||
|
COMMENT ON TABLE devices IS '设备表(可容纳1-4张SIM卡)';
|
||||||
|
COMMENT ON TABLE number_cards IS '号卡表(虚拟商品)';
|
||||||
|
COMMENT ON TABLE package_series IS '套餐系列表';
|
||||||
|
COMMENT ON TABLE packages IS '套餐表';
|
||||||
|
COMMENT ON TABLE agent_package_allocations IS '代理套餐分配表';
|
||||||
|
COMMENT ON TABLE device_sim_bindings IS '设备-SIM卡绑定表';
|
||||||
|
COMMENT ON TABLE orders IS '订单表';
|
||||||
|
COMMENT ON TABLE package_usages IS '套餐使用表';
|
||||||
|
COMMENT ON TABLE polling_configs IS '轮询配置表';
|
||||||
|
COMMENT ON TABLE data_usage_records IS '流量使用记录表';
|
||||||
|
COMMENT ON TABLE agent_hierarchies IS '代理层级关系表';
|
||||||
|
COMMENT ON TABLE commission_rules IS '分佣规则表';
|
||||||
|
COMMENT ON TABLE commission_ladder IS '阶梯分佣配置表';
|
||||||
|
COMMENT ON TABLE commission_combined_conditions IS '组合分佣条件表';
|
||||||
|
COMMENT ON TABLE commission_records IS '分佣记录表';
|
||||||
|
COMMENT ON TABLE commission_approvals IS '分佣审批表';
|
||||||
|
COMMENT ON TABLE commission_templates IS '分佣模板表';
|
||||||
|
COMMENT ON TABLE carrier_settlements IS '号卡运营商结算表';
|
||||||
|
COMMENT ON TABLE commission_withdrawal_requests IS '佣金提现申请表';
|
||||||
|
COMMENT ON TABLE commission_withdrawal_settings IS '佣金提现设置表';
|
||||||
|
COMMENT ON TABLE payment_merchant_settings IS '收款商户设置表';
|
||||||
|
COMMENT ON TABLE dev_capability_configs IS '开发能力配置表';
|
||||||
|
COMMENT ON TABLE card_replacement_requests IS '换卡申请表';
|
||||||
349
migrations/000006_refactor_iot_models_architecture.up.sql
Normal file
349
migrations/000006_refactor_iot_models_architecture.up.sql
Normal file
@@ -0,0 +1,349 @@
|
|||||||
|
-- IoT 模型架构重构迁移脚本
|
||||||
|
-- 创建时间: 2026-01-12
|
||||||
|
-- 说明: 修改所有 IoT 相关表以符合项目架构规范
|
||||||
|
-- 1. 表名改为 tb_ 前缀 + 单数形式
|
||||||
|
-- 2. 添加软删除字段 deleted_at
|
||||||
|
-- 3. 添加审计字段 creator, updater
|
||||||
|
-- 4. 金额字段从 DECIMAL 改为 BIGINT(分为单位)
|
||||||
|
-- 5. 更新唯一索引以支持软删除
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- 阶段 1: 重命名表(复数 -> tb_ + 单数)
|
||||||
|
-- ========================================
|
||||||
|
|
||||||
|
ALTER TABLE carriers RENAME TO tb_carrier;
|
||||||
|
ALTER TABLE iot_cards RENAME TO tb_iot_card;
|
||||||
|
ALTER TABLE devices RENAME TO tb_device;
|
||||||
|
ALTER TABLE number_cards RENAME TO tb_number_card;
|
||||||
|
ALTER TABLE package_series RENAME TO tb_package_series;
|
||||||
|
ALTER TABLE packages RENAME TO tb_package;
|
||||||
|
ALTER TABLE agent_package_allocations RENAME TO tb_agent_package_allocation;
|
||||||
|
ALTER TABLE device_sim_bindings RENAME TO tb_device_sim_binding;
|
||||||
|
ALTER TABLE orders RENAME TO tb_order;
|
||||||
|
ALTER TABLE package_usages RENAME TO tb_package_usage;
|
||||||
|
ALTER TABLE polling_configs RENAME TO tb_polling_config;
|
||||||
|
ALTER TABLE data_usage_records RENAME TO tb_data_usage_record;
|
||||||
|
ALTER TABLE agent_hierarchies RENAME TO tb_agent_hierarchy;
|
||||||
|
ALTER TABLE commission_rules RENAME TO tb_commission_rule;
|
||||||
|
ALTER TABLE commission_ladder RENAME TO tb_commission_ladder;
|
||||||
|
ALTER TABLE commission_combined_conditions RENAME TO tb_commission_combined_condition;
|
||||||
|
ALTER TABLE commission_records RENAME TO tb_commission_record;
|
||||||
|
ALTER TABLE commission_approvals RENAME TO tb_commission_approval;
|
||||||
|
ALTER TABLE commission_templates RENAME TO tb_commission_template;
|
||||||
|
ALTER TABLE carrier_settlements RENAME TO tb_carrier_settlement;
|
||||||
|
ALTER TABLE commission_withdrawal_requests RENAME TO tb_commission_withdrawal_request;
|
||||||
|
ALTER TABLE commission_withdrawal_settings RENAME TO tb_commission_withdrawal_setting;
|
||||||
|
ALTER TABLE payment_merchant_settings RENAME TO tb_payment_merchant_setting;
|
||||||
|
ALTER TABLE dev_capability_configs RENAME TO tb_dev_capability_config;
|
||||||
|
ALTER TABLE card_replacement_requests RENAME TO tb_card_replacement_request;
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- 阶段 2: 添加软删除字段和审计字段
|
||||||
|
-- ========================================
|
||||||
|
|
||||||
|
-- 2.1 运营商表
|
||||||
|
ALTER TABLE tb_carrier ADD COLUMN deleted_at TIMESTAMP;
|
||||||
|
ALTER TABLE tb_carrier ADD COLUMN creator BIGINT NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE tb_carrier ADD COLUMN updater BIGINT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- 2.2 IoT 卡表
|
||||||
|
ALTER TABLE tb_iot_card ADD COLUMN deleted_at TIMESTAMP;
|
||||||
|
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;
|
||||||
|
|
||||||
|
-- 2.3 设备表
|
||||||
|
ALTER TABLE tb_device ADD COLUMN deleted_at TIMESTAMP;
|
||||||
|
ALTER TABLE tb_device ADD COLUMN creator BIGINT NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE tb_device ADD COLUMN updater BIGINT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- 2.4 号卡表
|
||||||
|
ALTER TABLE tb_number_card ADD COLUMN deleted_at TIMESTAMP;
|
||||||
|
ALTER TABLE tb_number_card ADD COLUMN creator BIGINT NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE tb_number_card ADD COLUMN updater BIGINT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- 2.5 套餐系列表
|
||||||
|
ALTER TABLE tb_package_series ADD COLUMN deleted_at TIMESTAMP;
|
||||||
|
ALTER TABLE tb_package_series ADD COLUMN creator BIGINT NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE tb_package_series ADD COLUMN updater BIGINT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- 2.6 套餐表
|
||||||
|
ALTER TABLE tb_package ADD COLUMN deleted_at TIMESTAMP;
|
||||||
|
ALTER TABLE tb_package ADD COLUMN creator BIGINT NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE tb_package ADD COLUMN updater BIGINT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- 2.7 代理套餐分配表
|
||||||
|
ALTER TABLE tb_agent_package_allocation ADD COLUMN deleted_at TIMESTAMP;
|
||||||
|
ALTER TABLE tb_agent_package_allocation ADD COLUMN creator BIGINT NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE tb_agent_package_allocation ADD COLUMN updater BIGINT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- 2.8 设备-SIM绑定表
|
||||||
|
ALTER TABLE tb_device_sim_binding ADD COLUMN deleted_at TIMESTAMP;
|
||||||
|
ALTER TABLE tb_device_sim_binding ADD COLUMN creator BIGINT NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE tb_device_sim_binding ADD COLUMN updater BIGINT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- 2.9 订单表
|
||||||
|
ALTER TABLE tb_order ADD COLUMN deleted_at TIMESTAMP;
|
||||||
|
ALTER TABLE tb_order ADD COLUMN creator BIGINT NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE tb_order ADD COLUMN updater BIGINT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- 2.10 套餐使用表
|
||||||
|
ALTER TABLE tb_package_usage ADD COLUMN deleted_at TIMESTAMP;
|
||||||
|
ALTER TABLE tb_package_usage ADD COLUMN creator BIGINT NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE tb_package_usage ADD COLUMN updater BIGINT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- 2.11 轮询配置表
|
||||||
|
ALTER TABLE tb_polling_config ADD COLUMN deleted_at TIMESTAMP;
|
||||||
|
ALTER TABLE tb_polling_config ADD COLUMN creator BIGINT NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE tb_polling_config ADD COLUMN updater BIGINT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- 注意: tb_data_usage_record 是日志表,不需要软删除和审计字段
|
||||||
|
|
||||||
|
-- 2.12 代理层级表
|
||||||
|
ALTER TABLE tb_agent_hierarchy ADD COLUMN deleted_at TIMESTAMP;
|
||||||
|
ALTER TABLE tb_agent_hierarchy ADD COLUMN creator BIGINT NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE tb_agent_hierarchy ADD COLUMN updater BIGINT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- 2.13 分佣规则表
|
||||||
|
ALTER TABLE tb_commission_rule ADD COLUMN deleted_at TIMESTAMP;
|
||||||
|
ALTER TABLE tb_commission_rule ADD COLUMN creator BIGINT NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE tb_commission_rule ADD COLUMN updater BIGINT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- 2.14 阶梯分佣配置表
|
||||||
|
ALTER TABLE tb_commission_ladder ADD COLUMN deleted_at TIMESTAMP;
|
||||||
|
ALTER TABLE tb_commission_ladder ADD COLUMN creator BIGINT NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE tb_commission_ladder ADD COLUMN updater BIGINT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- 2.15 组合分佣条件表
|
||||||
|
ALTER TABLE tb_commission_combined_condition ADD COLUMN deleted_at TIMESTAMP;
|
||||||
|
ALTER TABLE tb_commission_combined_condition ADD COLUMN creator BIGINT NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE tb_commission_combined_condition ADD COLUMN updater BIGINT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- 2.16 分佣记录表
|
||||||
|
ALTER TABLE tb_commission_record ADD COLUMN deleted_at TIMESTAMP;
|
||||||
|
ALTER TABLE tb_commission_record ADD COLUMN creator BIGINT NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE tb_commission_record ADD COLUMN updater BIGINT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- 2.17 分佣审批表
|
||||||
|
ALTER TABLE tb_commission_approval ADD COLUMN deleted_at TIMESTAMP;
|
||||||
|
ALTER TABLE tb_commission_approval ADD COLUMN creator BIGINT NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE tb_commission_approval ADD COLUMN updater BIGINT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- 2.18 分佣模板表
|
||||||
|
ALTER TABLE tb_commission_template ADD COLUMN deleted_at TIMESTAMP;
|
||||||
|
ALTER TABLE tb_commission_template ADD COLUMN creator BIGINT NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE tb_commission_template ADD COLUMN updater BIGINT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- 2.19 运营商结算表
|
||||||
|
ALTER TABLE tb_carrier_settlement ADD COLUMN deleted_at TIMESTAMP;
|
||||||
|
ALTER TABLE tb_carrier_settlement ADD COLUMN creator BIGINT NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE tb_carrier_settlement ADD COLUMN updater BIGINT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- 2.20 佣金提现申请表
|
||||||
|
ALTER TABLE tb_commission_withdrawal_request ADD COLUMN deleted_at TIMESTAMP;
|
||||||
|
ALTER TABLE tb_commission_withdrawal_request ADD COLUMN creator BIGINT NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE tb_commission_withdrawal_request ADD COLUMN updater BIGINT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- 2.21 佣金提现设置表
|
||||||
|
ALTER TABLE tb_commission_withdrawal_setting ADD COLUMN deleted_at TIMESTAMP;
|
||||||
|
ALTER TABLE tb_commission_withdrawal_setting ADD COLUMN creator BIGINT NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE tb_commission_withdrawal_setting ADD COLUMN updater BIGINT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- 2.22 收款商户设置表
|
||||||
|
ALTER TABLE tb_payment_merchant_setting ADD COLUMN deleted_at TIMESTAMP;
|
||||||
|
ALTER TABLE tb_payment_merchant_setting ADD COLUMN creator BIGINT NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE tb_payment_merchant_setting ADD COLUMN updater BIGINT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- 2.23 开发能力配置表
|
||||||
|
ALTER TABLE tb_dev_capability_config ADD COLUMN deleted_at TIMESTAMP;
|
||||||
|
ALTER TABLE tb_dev_capability_config ADD COLUMN creator BIGINT NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE tb_dev_capability_config ADD COLUMN updater BIGINT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- 2.24 换卡申请表
|
||||||
|
ALTER TABLE tb_card_replacement_request ADD COLUMN deleted_at TIMESTAMP;
|
||||||
|
ALTER TABLE tb_card_replacement_request ADD COLUMN creator BIGINT NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE tb_card_replacement_request ADD COLUMN updater BIGINT NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- 阶段 3: 修改金额字段从 DECIMAL 改为 BIGINT(分为单位)
|
||||||
|
-- ========================================
|
||||||
|
|
||||||
|
-- 3.1 IoT 卡表:成本价、分销价(元 * 100 = 分)
|
||||||
|
ALTER TABLE tb_iot_card
|
||||||
|
ALTER COLUMN cost_price TYPE BIGINT USING (cost_price * 100)::BIGINT,
|
||||||
|
ALTER COLUMN distribute_price TYPE BIGINT USING (distribute_price * 100)::BIGINT;
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_iot_card.cost_price IS '成本价(分为单位)';
|
||||||
|
COMMENT ON COLUMN tb_iot_card.distribute_price IS '分销价(分为单位)';
|
||||||
|
|
||||||
|
-- 3.2 号卡表:价格
|
||||||
|
ALTER TABLE tb_number_card
|
||||||
|
ALTER COLUMN price TYPE BIGINT USING (price * 100)::BIGINT;
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_number_card.price IS '价格(分为单位)';
|
||||||
|
|
||||||
|
-- 3.3 套餐表:价格
|
||||||
|
ALTER TABLE tb_package
|
||||||
|
ALTER COLUMN price TYPE BIGINT USING (price * 100)::BIGINT;
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_package.price IS '套餐价格(分为单位)';
|
||||||
|
|
||||||
|
-- 3.4 代理套餐分配表:成本价和零售价
|
||||||
|
ALTER TABLE tb_agent_package_allocation
|
||||||
|
ALTER COLUMN cost_price TYPE BIGINT USING (cost_price * 100)::BIGINT,
|
||||||
|
ALTER COLUMN retail_price TYPE BIGINT USING (retail_price * 100)::BIGINT;
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_agent_package_allocation.cost_price IS '成本价(分为单位)';
|
||||||
|
COMMENT ON COLUMN tb_agent_package_allocation.retail_price IS '零售价(分为单位)';
|
||||||
|
|
||||||
|
-- 3.5 订单表:订单金额
|
||||||
|
ALTER TABLE tb_order
|
||||||
|
ALTER COLUMN amount TYPE BIGINT USING (amount * 100)::BIGINT;
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_order.amount IS '订单金额(分为单位)';
|
||||||
|
|
||||||
|
-- 3.6 分佣规则表:分佣值
|
||||||
|
ALTER TABLE tb_commission_rule
|
||||||
|
ALTER COLUMN commission_value TYPE BIGINT USING (commission_value * 100)::BIGINT;
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_commission_rule.commission_value IS '分佣值(分为单位,百分比时为千分比如2000表示20%)';
|
||||||
|
|
||||||
|
-- 3.7 阶梯分佣配置表:分佣值
|
||||||
|
ALTER TABLE tb_commission_ladder
|
||||||
|
ALTER COLUMN commission_value TYPE BIGINT USING (commission_value * 100)::BIGINT;
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_commission_ladder.commission_value IS '分佣值(分为单位,百分比时为千分比如2000表示20%)';
|
||||||
|
|
||||||
|
-- 3.8 组合分佣条件表:一次性分佣值、长期分佣值
|
||||||
|
ALTER TABLE tb_commission_combined_condition
|
||||||
|
ALTER COLUMN one_time_commission_value TYPE BIGINT USING (one_time_commission_value * 100)::BIGINT,
|
||||||
|
ALTER COLUMN long_term_commission_value TYPE BIGINT USING (long_term_commission_value * 100)::BIGINT;
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_commission_combined_condition.one_time_commission_value IS '一次性分佣值(分为单位,百分比时为千分比如2000表示20%)';
|
||||||
|
COMMENT ON COLUMN tb_commission_combined_condition.long_term_commission_value IS '长期分佣值(分为单位,百分比时为千分比如2000表示20%)';
|
||||||
|
|
||||||
|
-- 3.9 分佣记录表:分佣金额
|
||||||
|
ALTER TABLE tb_commission_record
|
||||||
|
ALTER COLUMN amount TYPE BIGINT USING (amount * 100)::BIGINT;
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_commission_record.amount IS '分佣金额(分为单位)';
|
||||||
|
|
||||||
|
-- 3.10 分佣模板表:分佣值
|
||||||
|
ALTER TABLE tb_commission_template
|
||||||
|
ALTER COLUMN commission_value TYPE BIGINT USING (commission_value * 100)::BIGINT;
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_commission_template.commission_value IS '分佣值(分为单位,百分比时为千分比如2000表示20%)';
|
||||||
|
|
||||||
|
-- 3.11 运营商结算表:结算金额
|
||||||
|
ALTER TABLE tb_carrier_settlement
|
||||||
|
ALTER COLUMN settlement_amount TYPE BIGINT USING (settlement_amount * 100)::BIGINT;
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_carrier_settlement.settlement_amount IS '结算金额(分为单位)';
|
||||||
|
|
||||||
|
-- 3.12 佣金提现申请表:提现金额、手续费、实际到账金额
|
||||||
|
ALTER TABLE tb_commission_withdrawal_request
|
||||||
|
ALTER COLUMN amount TYPE BIGINT USING (amount * 100)::BIGINT,
|
||||||
|
ALTER COLUMN fee TYPE BIGINT USING (fee * 100)::BIGINT,
|
||||||
|
ALTER COLUMN actual_amount TYPE BIGINT USING (actual_amount * 100)::BIGINT;
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_commission_withdrawal_request.amount IS '提现金额(分为单位)';
|
||||||
|
COMMENT ON COLUMN tb_commission_withdrawal_request.fee IS '手续费(分为单位)';
|
||||||
|
COMMENT ON COLUMN tb_commission_withdrawal_request.actual_amount IS '实际到账金额(分为单位)';
|
||||||
|
|
||||||
|
-- 3.13 佣金提现设置表:最低提现金额
|
||||||
|
ALTER TABLE tb_commission_withdrawal_setting
|
||||||
|
ALTER COLUMN min_withdrawal_amount TYPE BIGINT USING (min_withdrawal_amount * 100)::BIGINT;
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_commission_withdrawal_setting.min_withdrawal_amount IS '最低提现金额(分为单位)';
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- 阶段 4: 更新唯一索引以支持软删除
|
||||||
|
-- ========================================
|
||||||
|
|
||||||
|
-- 4.1 运营商表
|
||||||
|
ALTER TABLE tb_carrier DROP CONSTRAINT IF EXISTS carriers_carrier_code_key;
|
||||||
|
CREATE UNIQUE INDEX idx_carrier_code ON tb_carrier(carrier_code) WHERE deleted_at IS NULL;
|
||||||
|
|
||||||
|
-- 4.2 IoT 卡表
|
||||||
|
ALTER TABLE tb_iot_card DROP CONSTRAINT IF EXISTS iot_cards_iccid_key;
|
||||||
|
CREATE UNIQUE INDEX idx_iot_card_iccid ON tb_iot_card(iccid) WHERE deleted_at IS NULL;
|
||||||
|
|
||||||
|
-- 4.3 设备表
|
||||||
|
ALTER TABLE tb_device DROP CONSTRAINT IF EXISTS devices_device_no_key;
|
||||||
|
CREATE UNIQUE INDEX idx_device_no ON tb_device(device_no) WHERE deleted_at IS NULL;
|
||||||
|
|
||||||
|
-- 4.4 号卡表
|
||||||
|
ALTER TABLE tb_number_card DROP CONSTRAINT IF EXISTS number_cards_virtual_product_code_key;
|
||||||
|
CREATE UNIQUE INDEX idx_number_card_code ON tb_number_card(virtual_product_code) WHERE deleted_at IS NULL;
|
||||||
|
|
||||||
|
-- 4.5 套餐系列表
|
||||||
|
ALTER TABLE tb_package_series DROP CONSTRAINT IF EXISTS package_series_series_code_key;
|
||||||
|
CREATE UNIQUE INDEX idx_package_series_code ON tb_package_series(series_code) WHERE deleted_at IS NULL;
|
||||||
|
|
||||||
|
-- 4.6 套餐表
|
||||||
|
ALTER TABLE tb_package DROP CONSTRAINT IF EXISTS packages_package_code_key;
|
||||||
|
CREATE UNIQUE INDEX idx_package_code ON tb_package(package_code) WHERE deleted_at IS NULL;
|
||||||
|
|
||||||
|
-- 4.7 代理套餐分配表(复合唯一索引)
|
||||||
|
ALTER TABLE tb_agent_package_allocation DROP CONSTRAINT IF EXISTS uk_agent_package;
|
||||||
|
CREATE UNIQUE INDEX idx_agent_package_allocation_agent_package ON tb_agent_package_allocation(agent_id, package_id) WHERE deleted_at IS NULL;
|
||||||
|
|
||||||
|
-- 4.8 设备-SIM绑定表:暂时跳过,因为没有约束需要修改
|
||||||
|
-- 原始表使用条件唯一索引 idx_device_sim_bindings_active_card,不需要修改
|
||||||
|
|
||||||
|
-- 4.9 订单表
|
||||||
|
ALTER TABLE tb_order DROP CONSTRAINT IF EXISTS orders_order_no_key;
|
||||||
|
CREATE UNIQUE INDEX idx_order_no ON tb_order(order_no) WHERE deleted_at IS NULL;
|
||||||
|
|
||||||
|
-- 4.10 轮询配置表
|
||||||
|
ALTER TABLE tb_polling_config DROP CONSTRAINT IF EXISTS polling_configs_config_name_key;
|
||||||
|
CREATE UNIQUE INDEX idx_polling_config_name ON tb_polling_config(config_name) WHERE deleted_at IS NULL;
|
||||||
|
|
||||||
|
-- 4.11 代理层级表
|
||||||
|
ALTER TABLE tb_agent_hierarchy DROP CONSTRAINT IF EXISTS agent_hierarchies_agent_id_key;
|
||||||
|
CREATE UNIQUE INDEX idx_agent_hierarchy_agent ON tb_agent_hierarchy(agent_id) WHERE deleted_at IS NULL;
|
||||||
|
|
||||||
|
-- 4.12 组合分佣条件表
|
||||||
|
ALTER TABLE tb_commission_combined_condition DROP CONSTRAINT IF EXISTS commission_combined_conditions_rule_id_key;
|
||||||
|
CREATE UNIQUE INDEX idx_commission_combined_rule ON tb_commission_combined_condition(rule_id) WHERE deleted_at IS NULL;
|
||||||
|
|
||||||
|
-- 4.13 分佣模板表
|
||||||
|
ALTER TABLE tb_commission_template DROP CONSTRAINT IF EXISTS commission_templates_template_name_key;
|
||||||
|
CREATE UNIQUE INDEX idx_commission_template_name ON tb_commission_template(template_name) WHERE deleted_at IS NULL;
|
||||||
|
|
||||||
|
-- 4.14 运营商结算表
|
||||||
|
ALTER TABLE tb_carrier_settlement DROP CONSTRAINT IF EXISTS carrier_settlements_commission_record_id_key;
|
||||||
|
CREATE UNIQUE INDEX idx_carrier_settlement_record ON tb_carrier_settlement(commission_record_id) WHERE deleted_at IS NULL;
|
||||||
|
|
||||||
|
-- 4.15 开发能力配置表
|
||||||
|
ALTER TABLE tb_dev_capability_config DROP CONSTRAINT IF EXISTS dev_capability_configs_app_id_key;
|
||||||
|
CREATE UNIQUE INDEX idx_dev_capability_app ON tb_dev_capability_config(app_id) WHERE deleted_at IS NULL;
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- 阶段 5: 更新表注释
|
||||||
|
-- ========================================
|
||||||
|
|
||||||
|
COMMENT ON TABLE tb_carrier IS '运营商表';
|
||||||
|
COMMENT ON TABLE tb_iot_card IS 'IoT卡表(物联网卡/流量卡)';
|
||||||
|
COMMENT ON TABLE tb_device IS '设备表(可容纳1-4张SIM卡)';
|
||||||
|
COMMENT ON TABLE tb_number_card IS '号卡表(虚拟商品)';
|
||||||
|
COMMENT ON TABLE tb_package_series IS '套餐系列表';
|
||||||
|
COMMENT ON TABLE tb_package IS '套餐表';
|
||||||
|
COMMENT ON TABLE tb_agent_package_allocation IS '代理套餐分配表';
|
||||||
|
COMMENT ON TABLE tb_device_sim_binding IS '设备-SIM卡绑定表';
|
||||||
|
COMMENT ON TABLE tb_order IS '订单表';
|
||||||
|
COMMENT ON TABLE tb_package_usage IS '套餐使用表';
|
||||||
|
COMMENT ON TABLE tb_polling_config IS '轮询配置表';
|
||||||
|
COMMENT ON TABLE tb_data_usage_record IS '流量使用记录表';
|
||||||
|
COMMENT ON TABLE tb_agent_hierarchy IS '代理层级关系表';
|
||||||
|
COMMENT ON TABLE tb_commission_rule IS '分佣规则表';
|
||||||
|
COMMENT ON TABLE tb_commission_ladder IS '阶梯分佣配置表';
|
||||||
|
COMMENT ON TABLE tb_commission_combined_condition IS '组合分佣条件表';
|
||||||
|
COMMENT ON TABLE tb_commission_record IS '分佣记录表';
|
||||||
|
COMMENT ON TABLE tb_commission_approval IS '分佣审批表';
|
||||||
|
COMMENT ON TABLE tb_commission_template IS '分佣模板表';
|
||||||
|
COMMENT ON TABLE tb_carrier_settlement IS '号卡运营商结算表';
|
||||||
|
COMMENT ON TABLE tb_commission_withdrawal_request IS '佣金提现申请表';
|
||||||
|
COMMENT ON TABLE tb_commission_withdrawal_setting IS '佣金提现设置表';
|
||||||
|
COMMENT ON TABLE tb_payment_merchant_setting IS '收款商户设置表';
|
||||||
|
COMMENT ON TABLE tb_dev_capability_config IS '开发能力配置表';
|
||||||
|
COMMENT ON TABLE tb_card_replacement_request IS '换卡申请表';
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-01-12
|
||||||
@@ -0,0 +1,537 @@
|
|||||||
|
# 设计文档:修复 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 文档
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
## Why
|
||||||
|
|
||||||
|
在之前的 IoT SIM 管理系统提案(2026-01-12-iot-sim-management)中创建的所有数据模型存在严重的架构违规问题,完全没有遵循项目的核心开发规范。这些违规导致代码不一致、可维护性差、违背项目设计原则。
|
||||||
|
|
||||||
|
**核心问题:**
|
||||||
|
|
||||||
|
1. **未使用基础模型**:所有 IoT 模型都没有嵌入 `BaseModel`,缺少统一的 `creator` 和 `updater` 字段
|
||||||
|
2. **未使用 gorm.Model**:部分模型没有嵌入 `gorm.Model`,缺少标准的 `ID`、`CreatedAt`、`UpdatedAt`、`DeletedAt` 字段
|
||||||
|
3. **字段命名不规范**:未显式指定 `column` 标签,依赖 GORM 自动转换(违反规范)
|
||||||
|
4. **字段定义不完整**:缺少必要的数据库约束标签(`not null`、`uniqueIndex`、索引等)
|
||||||
|
5. **数据类型不一致**:
|
||||||
|
- 货币字段使用 `float64` 而不是整数(分为单位)
|
||||||
|
- ID 字段类型不一致(`uint` vs `bigint`)
|
||||||
|
- 时间字段缺少 `autoCreateTime`/`autoUpdateTime` 标签
|
||||||
|
6. **表名不符合规范**:使用复数形式(`iot_cards`)而不是项目约定的 `tb_` 前缀单数形式
|
||||||
|
7. **缺少中文注释**:部分字段缺少清晰的中文注释说明业务含义
|
||||||
|
8. **软删除支持不一致**:某些应该支持软删除的模型缺少 `gorm.Model` 嵌入
|
||||||
|
|
||||||
|
**对比现有规范模型(Account、PersonalCustomer):**
|
||||||
|
|
||||||
|
✅ **正确示例(Account 模型):**
|
||||||
|
```go
|
||||||
|
type Account struct {
|
||||||
|
gorm.Model // ✅ 嵌入标准模型(ID、CreatedAt、UpdatedAt、DeletedAt)
|
||||||
|
BaseModel `gorm:"embedded"` // ✅ 嵌入基础模型(Creator、Updater)
|
||||||
|
Username string `gorm:"column:username;type:varchar(50);uniqueIndex:idx_account_username,where:deleted_at IS NULL;not null;comment:用户名" json:"username"`
|
||||||
|
// ✅ 显式 column 标签
|
||||||
|
// ✅ 明确类型和长度
|
||||||
|
// ✅ 唯一索引 + 软删除过滤
|
||||||
|
// ✅ not null 约束
|
||||||
|
// ✅ 中文注释
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Account) TableName() string {
|
||||||
|
return "tb_account" // ✅ tb_ 前缀 + 单数
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
❌ **错误示例(IotCard 模型):**
|
||||||
|
```go
|
||||||
|
type IotCard struct {
|
||||||
|
ID uint `gorm:"column:id;primaryKey;comment:IoT 卡 ID" json:"id"`
|
||||||
|
// ❌ 没有 gorm.Model
|
||||||
|
// ❌ 没有 BaseModel
|
||||||
|
// ❌ 手动定义 ID(应该由 gorm.Model 提供)
|
||||||
|
// ❌ 没有 DeletedAt(无法软删除)
|
||||||
|
|
||||||
|
CostPrice float64 `gorm:"column:cost_price;type:decimal(10,2);default:0;comment:成本价(元)" json:"cost_price"`
|
||||||
|
// ❌ 使用 float64 而不是整数(分为单位)
|
||||||
|
|
||||||
|
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
||||||
|
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at"`
|
||||||
|
// ❌ 手动定义(应该由 gorm.Model 提供)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (IotCard) TableName() string {
|
||||||
|
return "iot_cards" // ❌ 复数形式,没有 tb_ 前缀
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**影响范围:**
|
||||||
|
|
||||||
|
需要修复以下所有 IoT 相关模型(约 25 个模型文件):
|
||||||
|
- `internal/model/iot_card.go`(IotCard)
|
||||||
|
- `internal/model/device.go`(Device、DeviceSimBinding)
|
||||||
|
- `internal/model/number_card.go`(NumberCard)
|
||||||
|
- `internal/model/package.go`(PackageSeries、Package、AgentPackageAllocation、PackageUsage)
|
||||||
|
- `internal/model/order.go`(Order)
|
||||||
|
- `internal/model/commission.go`(AgentHierarchy、CommissionRule、CommissionLadder、CommissionCombinedCondition、CommissionRecord、CommissionApproval、CommissionTemplate、CarrierSettlement)
|
||||||
|
- `internal/model/financial.go`(CommissionWithdrawalRequest、CommissionWithdrawalSetting、PaymentMerchantSetting)
|
||||||
|
- `internal/model/system.go`(DevCapabilityConfig、CardReplacementRequest)
|
||||||
|
- `internal/model/carrier.go`(Carrier)
|
||||||
|
- `internal/model/data_usage.go`(DataUsageRecord)
|
||||||
|
- `internal/model/polling.go`(PollingConfig)
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
- 重构所有 IoT 相关数据模型,使其完全符合项目开发规范
|
||||||
|
- 统一所有模型的字段定义、类型、约束、注释格式
|
||||||
|
- 确保所有模型与现有用户体系模型(Account、PersonalCustomer)保持一致的架构风格
|
||||||
|
- 更新数据库迁移脚本以反映模型变更
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
|
||||||
|
#### 核心数据模型规范化
|
||||||
|
|
||||||
|
- `iot-card`: 修改 IoT 卡业务模型 - 统一字段定义,嵌入 BaseModel 和 gorm.Model,修正表名为 `tb_iot_card`,使用整数存储金额,完善索引和约束
|
||||||
|
- `iot-device`: 修改设备业务模型 - 统一字段定义,嵌入 BaseModel 和 gorm.Model,修正表名为 `tb_device`,规范化所有关联字段
|
||||||
|
- `iot-number-card`: 修改号卡业务模型 - 统一字段定义,嵌入 BaseModel 和 gorm.Model,修正表名为 `tb_number_card`,使用整数存储金额
|
||||||
|
- `iot-package`: 修改套餐管理模型 - 统一字段定义,嵌入 BaseModel 和 gorm.Model,修正表名(`tb_package_series`、`tb_package`、`tb_agent_package_allocation`、`tb_package_usage`),使用整数存储金额
|
||||||
|
- `iot-order`: 修改订单管理模型 - 统一字段定义,嵌入 BaseModel 和 gorm.Model,修正表名为 `tb_order`,使用整数存储金额,规范化 JSONB 字段
|
||||||
|
- `iot-agent-commission`: 修改代理分佣模型 - 统一所有分佣相关模型字段定义,嵌入 BaseModel 和 gorm.Model,修正表名(添加 `tb_` 前缀),使用整数存储金额
|
||||||
|
|
||||||
|
#### 财务和系统模型规范化
|
||||||
|
|
||||||
|
- 修改财务相关模型(CommissionWithdrawalRequest、CommissionWithdrawalSetting、PaymentMerchantSetting)- 统一字段定义,使用整数存储金额,完善索引和约束
|
||||||
|
- 修改系统配置模型(DevCapabilityConfig、CardReplacementRequest)- 统一字段定义,嵌入 BaseModel 和 gorm.Model
|
||||||
|
- 修改运营商模型(Carrier)- 统一字段定义,嵌入 BaseModel 和 gorm.Model,修正表名为 `tb_carrier`
|
||||||
|
- 修改流量记录模型(DataUsageRecord)- 统一字段定义,嵌入 gorm.Model,修正表名为 `tb_data_usage_record`
|
||||||
|
- 修改轮询配置模型(PollingConfig)- 统一字段定义,嵌入 BaseModel 和 gorm.Model,修正表名为 `tb_polling_config`
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
**代码变更:**
|
||||||
|
- 重构约 25 个 GORM 模型文件(`internal/model/`)
|
||||||
|
- 所有模型的字段定义将发生变化(字段名、类型、标签)
|
||||||
|
- 所有表名将从复数变为 `tb_` 前缀单数形式
|
||||||
|
|
||||||
|
**数据库变更:**
|
||||||
|
- 需要生成新的数据库迁移脚本以反映模型变更
|
||||||
|
- 表名变更(如 `iot_cards` → `tb_iot_card`)
|
||||||
|
- 字段变更(如 `cost_price DECIMAL` → `cost_price BIGINT`,金额从元改为分)
|
||||||
|
- 新增字段(`creator`、`updater`、`deleted_at`)
|
||||||
|
- 新增索引和约束
|
||||||
|
|
||||||
|
**向后兼容性:**
|
||||||
|
- ❌ **不兼容变更**:此次修复涉及破坏性变更(表名、字段类型)
|
||||||
|
- 由于 IoT 模块尚未实际部署到生产环境,可以直接修改而无需数据迁移
|
||||||
|
- 如果已有测试数据,需要编写数据迁移脚本
|
||||||
|
|
||||||
|
**业务影响:**
|
||||||
|
- 不影响现有用户体系(Account、Role、Permission 等)
|
||||||
|
- 不影响个人客户模块(PersonalCustomer)
|
||||||
|
- IoT 模块的 Service 层和 Handler 层代码需要相应调整(字段类型变化)
|
||||||
|
|
||||||
|
**依赖关系:**
|
||||||
|
- 必须在实现 IoT 业务逻辑(Handlers、Services、Stores)之前修复
|
||||||
|
- 修复后才能生成正确的数据库迁移脚本
|
||||||
|
- 修复后才能生成准确的 API 文档
|
||||||
|
|
||||||
|
**文档变更:**
|
||||||
|
- 更新 `CLAUDE.md` 中的数据库设计原则和 GORM 模型字段规范
|
||||||
|
- 补充完整的字段定义规范(显式 column 标签、类型定义、注释要求)
|
||||||
|
- 添加金额字段整数存储的详细说明和示例
|
||||||
|
- 完善表名命名规范和 BaseModel 使用说明
|
||||||
|
- 确保全局规范文档与实际实现保持一致
|
||||||
|
|
||||||
|
**明确排除的范围**(本次不涉及):
|
||||||
|
- Handler 层代码修改(将在后续任务中处理)
|
||||||
|
- Service 层代码修改(将在后续任务中处理)
|
||||||
|
- Store 层代码修改(将在后续任务中处理)
|
||||||
|
- DTO 模型调整(请求/响应结构体)
|
||||||
|
- 单元测试和集成测试
|
||||||
|
- API 文档更新
|
||||||
|
|
||||||
|
**风险和注意事项:**
|
||||||
|
- 所有金额字段从 `float64` 改为 `int64`(分为单位),需要在业务逻辑中进行单位转换
|
||||||
|
- 表名变更需要确保迁移脚本正确执行
|
||||||
|
- 新增的 `creator` 和 `updater` 字段需要在业务逻辑中正确赋值
|
||||||
|
- 软删除(`DeletedAt`)的引入可能需要调整查询逻辑(GORM 会自动处理)
|
||||||
@@ -0,0 +1,458 @@
|
|||||||
|
# Capability: model-organization
|
||||||
|
|
||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Data models MUST follow unified structure conventions
|
||||||
|
|
||||||
|
All IoT data models MUST follow unified structure conventions. 所有 IoT 相关数据模型必须与现有用户体系模型(Account、PersonalCustomer)保持一致的架构风格和字段定义规范。
|
||||||
|
|
||||||
|
#### Scenario: IoT 卡模型结构规范化
|
||||||
|
|
||||||
|
**Given** 系统存在 IoT 卡数据模型
|
||||||
|
**When** 开发者定义或修改 IoT 卡模型
|
||||||
|
**Then** 模型必须:
|
||||||
|
- 嵌入 `gorm.Model`(提供 ID、CreatedAt、UpdatedAt、DeletedAt 字段)
|
||||||
|
- 嵌入 `BaseModel`(提供 Creator、Updater 审计字段)
|
||||||
|
- 所有字段显式指定 `gorm:"column:字段名"` 标签
|
||||||
|
- 所有字符串字段显式指定类型和长度(如 `type:varchar(100)`)
|
||||||
|
- 所有金额字段使用 `int64` 类型和 `type:bigint`,单位为"分"
|
||||||
|
- 所有必填字段添加 `not null` 约束
|
||||||
|
- 所有字段添加中文 `comment` 注释
|
||||||
|
- 所有唯一字段添加 `uniqueIndex:索引名,where:deleted_at IS NULL`
|
||||||
|
- 所有关联 ID 字段添加 `index` 索引
|
||||||
|
- 表名使用 `tb_iot_card`(`tb_` 前缀 + 单数)
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```go
|
||||||
|
package model
|
||||||
|
|
||||||
|
import "gorm.io/gorm"
|
||||||
|
|
||||||
|
type IotCard struct {
|
||||||
|
gorm.Model // ID, CreatedAt, UpdatedAt, DeletedAt
|
||||||
|
BaseModel `gorm:"embedded"` // Creator, Updater
|
||||||
|
|
||||||
|
ICCID string `gorm:"column:iccid;type:varchar(50);uniqueIndex:idx_iccid,where:deleted_at IS NULL;not null;comment:ICCID(唯一标识)" json:"iccid"`
|
||||||
|
CardType string `gorm:"column:card_type;type:varchar(50);not null;comment:卡类型" json:"card_type"`
|
||||||
|
CardCategory string `gorm:"column:card_category;type:varchar(20);default:'normal';not null;comment:卡业务类型 normal-普通卡 industry-行业卡" json:"card_category"`
|
||||||
|
CarrierID uint `gorm:"column:carrier_id;type:bigint;not null;index;comment:运营商ID" json:"carrier_id"`
|
||||||
|
CostPrice int64 `gorm:"column:cost_price;type:bigint;default:0;not null;comment:成本价(分)" json:"cost_price"`
|
||||||
|
DistributePrice int64 `gorm:"column:distribute_price;type:bigint;default:0;not null;comment:分销价(分)" json:"distribute_price"`
|
||||||
|
Status int `gorm:"column:status;type:int;default:1;not null;index;comment:状态 1-在库 2-已分销 3-已激活 4-已停用" json:"status"`
|
||||||
|
OwnerType string `gorm:"column:owner_type;type:varchar(20);default:'platform';not null;comment:所有者类型 platform-平台 agent-代理 user-用户 device-设备" json:"owner_type"`
|
||||||
|
OwnerID uint `gorm:"column:owner_id;type:bigint;default:0;not null;index;comment:所有者ID" json:"owner_id"`
|
||||||
|
ActivatedAt *time.Time `gorm:"column:activated_at;comment:激活时间" json:"activated_at,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (IotCard) TableName() string {
|
||||||
|
return "tb_iot_card"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Scenario: 设备模型结构规范化
|
||||||
|
|
||||||
|
**Given** 系统存在设备数据模型
|
||||||
|
**When** 开发者定义或修改设备模型
|
||||||
|
**Then** 模型必须遵循与 IoT 卡模型相同的规范(gorm.Model + BaseModel + 字段标签)
|
||||||
|
**And** 表名使用 `tb_device`
|
||||||
|
|
||||||
|
#### Scenario: 号卡模型结构规范化
|
||||||
|
|
||||||
|
**Given** 系统存在号卡数据模型
|
||||||
|
**When** 开发者定义或修改号卡模型
|
||||||
|
**Then** 模型必须遵循与 IoT 卡模型相同的规范
|
||||||
|
**And** 表名使用 `tb_number_card`
|
||||||
|
**And** 价格字段使用 `int64` 类型(分为单位)
|
||||||
|
|
||||||
|
#### Scenario: 套餐相关模型结构规范化
|
||||||
|
|
||||||
|
**Given** 系统存在套餐系列、套餐、代理套餐分配、套餐使用情况等模型
|
||||||
|
**When** 开发者定义或修改套餐相关模型
|
||||||
|
**Then** 所有套餐相关模型必须遵循统一规范:
|
||||||
|
- 套餐系列:`tb_package_series`
|
||||||
|
- 套餐:`tb_package`
|
||||||
|
- 代理套餐分配:`tb_agent_package_allocation`
|
||||||
|
- 套餐使用情况:`tb_package_usage`
|
||||||
|
**And** 所有价格字段使用 `int64` 类型(分为单位)
|
||||||
|
|
||||||
|
#### Scenario: 订单模型结构规范化
|
||||||
|
|
||||||
|
**Given** 系统存在订单数据模型
|
||||||
|
**When** 开发者定义或修改订单模型
|
||||||
|
**Then** 模型必须遵循统一规范
|
||||||
|
**And** 表名使用 `tb_order`
|
||||||
|
**And** 金额字段使用 `int64` 类型(分为单位)
|
||||||
|
**And** JSONB 字段使用 `gorm.io/datatypes.JSON` 类型(不是 `pq.StringArray`)
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"gorm.io/datatypes"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Order struct {
|
||||||
|
gorm.Model
|
||||||
|
BaseModel `gorm:"embedded"`
|
||||||
|
|
||||||
|
OrderNo string `gorm:"column:order_no;type:varchar(100);uniqueIndex:idx_order_no,where:deleted_at IS NULL;not null;comment:订单号(唯一标识)" json:"order_no"`
|
||||||
|
OrderType int `gorm:"column:order_type;type:int;not null;index;comment:订单类型 1-套餐订单 2-号卡订单" json:"order_type"`
|
||||||
|
Amount int64 `gorm:"column:amount;type:bigint;not null;comment:订单金额(分)" json:"amount"`
|
||||||
|
CarrierOrderData datatypes.JSON `gorm:"column:carrier_order_data;type:jsonb;comment:运营商订单原始数据" json:"carrier_order_data,omitempty"`
|
||||||
|
Status int `gorm:"column:status;type:int;default:1;not null;index;comment:状态 1-待支付 2-已支付 3-已完成 4-已取消 5-已退款" json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Order) TableName() string {
|
||||||
|
return "tb_order"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Scenario: 分佣相关模型结构规范化
|
||||||
|
|
||||||
|
**Given** 系统存在代理层级、分佣规则、分佣记录等模型
|
||||||
|
**When** 开发者定义或修改分佣相关模型
|
||||||
|
**Then** 所有分佣相关模型必须遵循统一规范:
|
||||||
|
- 代理层级:`tb_agent_hierarchy`
|
||||||
|
- 分佣规则:`tb_commission_rule`
|
||||||
|
- 阶梯分佣配置:`tb_commission_ladder`
|
||||||
|
- 组合分佣条件:`tb_commission_combined_condition`
|
||||||
|
- 分佣记录:`tb_commission_record`
|
||||||
|
- 分佣审批:`tb_commission_approval`
|
||||||
|
- 分佣模板:`tb_commission_template`
|
||||||
|
- 运营商结算:`tb_carrier_settlement`
|
||||||
|
**And** 所有金额字段使用 `int64` 类型(分为单位)
|
||||||
|
|
||||||
|
#### Scenario: 财务相关模型结构规范化
|
||||||
|
|
||||||
|
**Given** 系统存在佣金提现申请、提现设置、收款商户设置等模型
|
||||||
|
**When** 开发者定义或修改财务相关模型
|
||||||
|
**Then** 所有财务相关模型必须遵循统一规范:
|
||||||
|
- 佣金提现申请:`tb_commission_withdrawal_request`
|
||||||
|
- 佣金提现设置:`tb_commission_withdrawal_setting`
|
||||||
|
- 收款商户设置:`tb_payment_merchant_setting`
|
||||||
|
**And** 所有金额字段使用 `int64` 类型(分为单位)
|
||||||
|
**And** JSONB 字段使用 `gorm.io/datatypes.JSON` 类型
|
||||||
|
|
||||||
|
#### Scenario: 系统配置和日志模型规范化
|
||||||
|
|
||||||
|
**Given** 系统存在运营商、轮询配置、流量记录、开发能力配置等模型
|
||||||
|
**When** 开发者定义或修改系统配置和日志模型
|
||||||
|
**Then** 模型必须遵循统一规范:
|
||||||
|
- 运营商:`tb_carrier`(gorm.Model + BaseModel)
|
||||||
|
- 轮询配置:`tb_polling_config`(gorm.Model + BaseModel)
|
||||||
|
- 流量记录:`tb_data_usage_record`(仅 ID + CreatedAt,不需要 UpdatedAt 和 DeletedAt)
|
||||||
|
- 开发能力配置:`tb_dev_capability_config`(gorm.Model + BaseModel)
|
||||||
|
- 换卡申请:`tb_card_replacement_request`(gorm.Model + BaseModel)
|
||||||
|
|
||||||
|
**Example (流量记录 - 简化模型):**
|
||||||
|
```go
|
||||||
|
type DataUsageRecord struct {
|
||||||
|
ID uint `gorm:"column:id;primaryKey;comment:流量使用记录ID" json:"id"`
|
||||||
|
IotCardID uint `gorm:"column:iot_card_id;type:bigint;not null;index;comment:IoT卡ID" json:"iot_card_id"`
|
||||||
|
DataUsageMB int64 `gorm:"column:data_usage_mb;type:bigint;not null;comment:流量使用量(MB)" json:"data_usage_mb"`
|
||||||
|
CheckTime time.Time `gorm:"column:check_time;not null;comment:检查时间" json:"check_time"`
|
||||||
|
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (DataUsageRecord) TableName() string {
|
||||||
|
return "tb_data_usage_record"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Scenario: 设备-SIM 卡绑定关系模型规范化
|
||||||
|
|
||||||
|
**Given** 系统存在设备-IoT 卡绑定关系模型
|
||||||
|
**When** 开发者定义或修改绑定关系模型
|
||||||
|
**Then** 模型必须遵循统一规范
|
||||||
|
**And** 表名使用 `tb_device_sim_binding`
|
||||||
|
**And** 支持复合索引(`device_id` + `slot_position`)
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```go
|
||||||
|
type DeviceSimBinding struct {
|
||||||
|
gorm.Model
|
||||||
|
BaseModel `gorm:"embedded"`
|
||||||
|
|
||||||
|
DeviceID uint `gorm:"column:device_id;type:bigint;not null;index:idx_device_slot;comment:设备ID" json:"device_id"`
|
||||||
|
IotCardID uint `gorm:"column:iot_card_id;type:bigint;not null;index;comment:IoT卡ID" json:"iot_card_id"`
|
||||||
|
SlotPosition int `gorm:"column:slot_position;type:int;index:idx_device_slot;comment:插槽位置(1, 2, 3, 4)" json:"slot_position"`
|
||||||
|
BindStatus int `gorm:"column:bind_status;type:int;default:1;not null;comment:绑定状态 1-已绑定 2-已解绑" json:"bind_status"`
|
||||||
|
BindTime *time.Time `gorm:"column:bind_time;comment:绑定时间" json:"bind_time,omitempty"`
|
||||||
|
UnbindTime *time.Time `gorm:"column:unbind_time;comment:解绑时间" json:"unbind_time,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (DeviceSimBinding) TableName() string {
|
||||||
|
return "tb_device_sim_binding"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Requirement: Currency amount fields MUST use integer type (unit: cents)
|
||||||
|
|
||||||
|
All currency amount fields MUST use integer type (unit: cents). 所有货币金额字段必须使用 `int64` 类型存储,单位为"分"(1 元 = 100 分),避免浮点精度问题。
|
||||||
|
|
||||||
|
#### Scenario: 金额字段定义规范
|
||||||
|
|
||||||
|
**Given** 模型包含货币金额字段(如价格、成本、佣金、提现金额等)
|
||||||
|
**When** 开发者定义金额字段
|
||||||
|
**Then** 字段必须:
|
||||||
|
- 使用 `int64` Go 类型(不是 `float64`)
|
||||||
|
- 数据库类型为 `bigint`(不是 `decimal` 或 `numeric`)
|
||||||
|
- 默认值为 `0`
|
||||||
|
- 添加 `not null` 约束
|
||||||
|
- 注释中明确标注"(分)"单位
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```go
|
||||||
|
CostPrice int64 `gorm:"column:cost_price;type:bigint;default:0;not null;comment:成本价(分)" json:"cost_price"`
|
||||||
|
Amount int64 `gorm:"column:amount;type:bigint;not null;comment:订单金额(分)" json:"amount"`
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Scenario: API 层金额单位转换
|
||||||
|
|
||||||
|
**Given** API 接收或返回金额数据
|
||||||
|
**When** Handler 层处理请求或响应
|
||||||
|
**Then** 必须进行单位转换:
|
||||||
|
- 输入:API 接收 `float64`(元) → 业务层使用 `int64`(分)
|
||||||
|
- 输出:业务层返回 `int64`(分) → API 返回 `float64`(元)
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```go
|
||||||
|
// 输入转换(Handler 层)
|
||||||
|
type CreateOrderRequest struct {
|
||||||
|
Amount float64 `json:"amount"` // 元
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *OrderHandler) CreateOrder(c *fiber.Ctx) error {
|
||||||
|
var req CreateOrderRequest
|
||||||
|
// ... 解析请求 ...
|
||||||
|
|
||||||
|
// 转换:元 → 分
|
||||||
|
amountInCents := int64(req.Amount * 100)
|
||||||
|
|
||||||
|
// 调用 Service 层
|
||||||
|
order, err := h.orderService.CreateOrder(ctx, amountInCents, ...)
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输出转换(Handler 层)
|
||||||
|
type OrderResponse struct {
|
||||||
|
Amount float64 `json:"amount"` // 元
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *OrderHandler) GetOrder(c *fiber.Ctx) error {
|
||||||
|
order, err := h.orderService.GetOrder(ctx, orderID)
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// 转换:分 → 元
|
||||||
|
resp := OrderResponse{
|
||||||
|
Amount: float64(order.Amount) / 100.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.Success(c, resp)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Requirement: Table names MUST follow unified naming conventions
|
||||||
|
|
||||||
|
All database table names MUST follow unified naming conventions. 所有数据库表名必须遵循项目约定的 `tb_` 前缀 + 单数形式。
|
||||||
|
|
||||||
|
#### Scenario: 表名命名规范
|
||||||
|
|
||||||
|
**Given** 开发者定义数据模型
|
||||||
|
**When** 实现 `TableName()` 方法
|
||||||
|
**Then** 表名必须:
|
||||||
|
- 使用 `tb_` 前缀
|
||||||
|
- 使用单数形式(不是复数)
|
||||||
|
- 使用下划线命名法(snake_case)
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```go
|
||||||
|
// ✅ 正确
|
||||||
|
func (IotCard) TableName() string {
|
||||||
|
return "tb_iot_card"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Device) TableName() string {
|
||||||
|
return "tb_device"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Order) TableName() string {
|
||||||
|
return "tb_order"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ 错误
|
||||||
|
func (IotCard) TableName() string {
|
||||||
|
return "iot_cards" // 缺少 tb_ 前缀,使用复数
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Device) TableName() string {
|
||||||
|
return "devices" // 缺少 tb_ 前缀,使用复数
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Scenario: 关联表和中间表命名
|
||||||
|
|
||||||
|
**Given** 模型表示多对多关系或绑定关系
|
||||||
|
**When** 定义关联表或中间表
|
||||||
|
**Then** 表名必须使用 `tb_` 前缀 + 完整描述性名称(单数)
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```go
|
||||||
|
// 设备-SIM 卡绑定
|
||||||
|
func (DeviceSimBinding) TableName() string {
|
||||||
|
return "tb_device_sim_binding" // 不是 tb_device_sim_bindings
|
||||||
|
}
|
||||||
|
|
||||||
|
// 代理套餐分配
|
||||||
|
func (AgentPackageAllocation) TableName() string {
|
||||||
|
return "tb_agent_package_allocation" // 不是 tb_agent_package_allocations
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Requirement: All fields MUST explicitly specify database column names and types
|
||||||
|
|
||||||
|
All model fields MUST explicitly specify database column names and types. 模型字段定义必须清晰明确,不依赖 GORM 的自动转换和推断。
|
||||||
|
|
||||||
|
#### Scenario: 字段 GORM 标签完整性检查
|
||||||
|
|
||||||
|
**Given** 模型包含业务字段
|
||||||
|
**When** 开发者定义字段
|
||||||
|
**Then** 每个字段必须包含:
|
||||||
|
- `column:字段名`(显式指定数据库列名)
|
||||||
|
- `type:数据类型`(显式指定数据库类型)
|
||||||
|
- `comment:中文注释`(说明业务含义)
|
||||||
|
- 可选:`not null`、`default:值`、`index`、`uniqueIndex` 等约束
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```go
|
||||||
|
// ✅ 完整的字段定义
|
||||||
|
Username string `gorm:"column:username;type:varchar(50);uniqueIndex:idx_username,where:deleted_at IS NULL;not null;comment:用户名" json:"username"`
|
||||||
|
Status int `gorm:"column:status;type:int;default:1;not null;index;comment:状态 1-启用 2-禁用" json:"status"`
|
||||||
|
Phone string `gorm:"column:phone;type:varchar(20);comment:手机号码" json:"phone,omitempty"`
|
||||||
|
|
||||||
|
// ❌ 不完整的字段定义
|
||||||
|
Username string `gorm:"comment:用户名" json:"username"` // 缺少 column 和 type
|
||||||
|
Status int `gorm:"default:1" json:"status"` // 缺少 column、type 和 comment
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Scenario: 唯一索引软删除兼容
|
||||||
|
|
||||||
|
**Given** 字段需要全局唯一(如 ICCID、订单号、虚拟商品编码)
|
||||||
|
**When** 模型支持软删除(嵌入 `gorm.Model`)
|
||||||
|
**Then** 唯一索引必须包含 `where:deleted_at IS NULL` 过滤条件
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```go
|
||||||
|
ICCID string `gorm:"column:iccid;type:varchar(50);uniqueIndex:idx_iccid,where:deleted_at IS NULL;not null;comment:ICCID(唯一标识)" json:"iccid"`
|
||||||
|
OrderNo string `gorm:"column:order_no;type:varchar(100);uniqueIndex:idx_order_no,where:deleted_at IS NULL;not null;comment:订单号" json:"order_no"`
|
||||||
|
```
|
||||||
|
|
||||||
|
**Explanation:**
|
||||||
|
- 软删除后,`deleted_at` 不为 NULL
|
||||||
|
- 索引只对 `deleted_at IS NULL` 的记录生效
|
||||||
|
- 允许软删除后重新使用相同的唯一值
|
||||||
|
|
||||||
|
### Requirement: All models MUST support audit tracking (Creator and Updater)
|
||||||
|
|
||||||
|
All business data models MUST support audit tracking (Creator and Updater). 所有业务数据模型必须记录创建人和更新人,便于审计和追溯。
|
||||||
|
|
||||||
|
#### Scenario: 嵌入 BaseModel 提供审计字段
|
||||||
|
|
||||||
|
**Given** 模型表示业务数据实体
|
||||||
|
**When** 开发者定义模型
|
||||||
|
**Then** 模型必须嵌入 `BaseModel`
|
||||||
|
**And** `BaseModel` 提供 `Creator` 和 `Updater` 字段
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```go
|
||||||
|
type IotCard struct {
|
||||||
|
gorm.Model
|
||||||
|
BaseModel `gorm:"embedded"` // 提供 Creator 和 Updater
|
||||||
|
|
||||||
|
// 业务字段...
|
||||||
|
}
|
||||||
|
|
||||||
|
// BaseModel 定义在 internal/model/base.go
|
||||||
|
type BaseModel struct {
|
||||||
|
Creator uint `gorm:"column:creator;not null;comment:创建人ID" json:"creator"`
|
||||||
|
Updater uint `gorm:"column:updater;not null;comment:更新人ID" json:"updater"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Scenario: 业务逻辑层自动填充审计字段
|
||||||
|
|
||||||
|
**Given** Service 层或 Store 层创建或更新数据
|
||||||
|
**When** 执行数据库插入或更新操作
|
||||||
|
**Then** 必须自动填充 `Creator` 和 `Updater` 字段(从上下文获取当前用户 ID)
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```go
|
||||||
|
// Service 层或 Store 层
|
||||||
|
func (s *IotCardService) CreateIotCard(ctx context.Context, req CreateIotCardRequest) (*IotCard, error) {
|
||||||
|
// 从上下文获取当前用户 ID
|
||||||
|
currentUserID := middleware.GetUserIDFromContext(ctx)
|
||||||
|
|
||||||
|
card := &IotCard{
|
||||||
|
BaseModel: BaseModel{
|
||||||
|
Creator: currentUserID,
|
||||||
|
Updater: currentUserID,
|
||||||
|
},
|
||||||
|
ICCID: req.ICCID,
|
||||||
|
CardType: req.CardType,
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.db.Create(card).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return card, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *IotCardService) UpdateIotCard(ctx context.Context, id uint, req UpdateIotCardRequest) error {
|
||||||
|
currentUserID := middleware.GetUserIDFromContext(ctx)
|
||||||
|
|
||||||
|
updates := map[string]interface{}{
|
||||||
|
"updater": currentUserID,
|
||||||
|
"card_type": req.CardType,
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.db.Model(&IotCard{}).Where("id = ?", id).Updates(updates).Error
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Requirement: Log and record tables MUST use appropriate model structure
|
||||||
|
|
||||||
|
Append-only log and record tables MUST use simplified model structure. 对于只追加、不更新的日志表(如流量记录),必须使用简化的模型结构,不需要 `UpdatedAt` 和 `DeletedAt`。
|
||||||
|
|
||||||
|
#### Scenario: 流量记录简化模型
|
||||||
|
|
||||||
|
**Given** 模型表示只追加的日志数据(不会被修改或删除)
|
||||||
|
**When** 开发者定义日志模型
|
||||||
|
**Then** 模型可以:
|
||||||
|
- 手动定义 `ID`(不嵌入 `gorm.Model`)
|
||||||
|
- 只包含 `CreatedAt`(不需要 `UpdatedAt` 和 `DeletedAt`)
|
||||||
|
- 不嵌入 `BaseModel`(如果不需要审计)
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```go
|
||||||
|
type DataUsageRecord struct {
|
||||||
|
ID uint `gorm:"column:id;primaryKey;comment:流量使用记录ID" json:"id"`
|
||||||
|
IotCardID uint `gorm:"column:iot_card_id;type:bigint;not null;index;comment:IoT卡ID" json:"iot_card_id"`
|
||||||
|
DataUsageMB int64 `gorm:"column:data_usage_mb;type:bigint;not null;comment:流量使用量(MB)" json:"data_usage_mb"`
|
||||||
|
DataIncreaseMB int64 `gorm:"column:data_increase_mb;type:bigint;default:0;comment:相比上次的增量(MB)" json:"data_increase_mb"`
|
||||||
|
CheckTime time.Time `gorm:"column:check_time;not null;comment:检查时间" json:"check_time"`
|
||||||
|
Source string `gorm:"column:source;type:varchar(50);default:'polling';comment:数据来源 polling-轮询 manual-手动 gateway-回调" json:"source"`
|
||||||
|
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (DataUsageRecord) TableName() string {
|
||||||
|
return "tb_data_usage_record"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Explanation:**
|
||||||
|
- 流量记录只追加,不修改,不需要 `UpdatedAt`
|
||||||
|
- 流量记录不删除(或物理删除),不需要 `DeletedAt`
|
||||||
|
- 简化模型结构减少存储开销和查询复杂度
|
||||||
@@ -0,0 +1,643 @@
|
|||||||
|
# Tasks
|
||||||
|
|
||||||
|
本文档列出修复 IoT 模型架构违规所需的所有任务,按优先级和依赖关系排序。
|
||||||
|
|
||||||
|
## 阶段 1: 核心业务实体模型修复(必须优先完成)
|
||||||
|
|
||||||
|
### Task 1.1: 修复 IoT 卡模型 (IotCard)
|
||||||
|
|
||||||
|
**文件**: `internal/model/iot_card.go`
|
||||||
|
|
||||||
|
**修改内容:**
|
||||||
|
- 嵌入 `gorm.Model` 和 `BaseModel`
|
||||||
|
- 移除手动定义的 `ID`、`CreatedAt`、`UpdatedAt` 字段
|
||||||
|
- 所有字段显式指定 `column` 标签
|
||||||
|
- 金额字段(`CostPrice`、`DistributePrice`)从 `float64` 改为 `int64`,数据库类型从 `decimal(10,2)` 改为 `bigint`
|
||||||
|
- 表名从 `iot_cards` 改为 `tb_iot_card`
|
||||||
|
- `ICCID` 唯一索引添加 `where:deleted_at IS NULL`
|
||||||
|
- 所有关联 ID 字段(`CarrierID`、`OwnerID`)添加 `index` 标签
|
||||||
|
- 完善所有字段的中文注释
|
||||||
|
|
||||||
|
**验证方法:**
|
||||||
|
- 运行 `go build` 确保编译通过
|
||||||
|
- 使用 `gofmt` 格式化代码
|
||||||
|
- 与 `Account` 模型对比,确保风格一致
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1.2: 修复设备模型 (Device)
|
||||||
|
|
||||||
|
**文件**: `internal/model/device.go`
|
||||||
|
|
||||||
|
**修改内容:**
|
||||||
|
- 嵌入 `gorm.Model` 和 `BaseModel`
|
||||||
|
- 移除手动定义的 `ID`、`CreatedAt`、`UpdatedAt` 字段
|
||||||
|
- 所有字段显式指定 `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.Model` 和 `BaseModel`
|
||||||
|
- 移除手动定义的 `ID`、`CreatedAt`、`UpdatedAt` 字段
|
||||||
|
- 所有字段显式指定 `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.Model` 和 `BaseModel`
|
||||||
|
- 移除手动定义的 `ID`、`CreatedAt`、`UpdatedAt` 字段
|
||||||
|
- 所有字段显式指定 `column` 标签
|
||||||
|
- 表名从 `carriers` 改为 `tb_carrier`
|
||||||
|
- `CarrierCode` 唯一索引添加 `where:deleted_at IS NULL`
|
||||||
|
- 完善所有字段的中文注释
|
||||||
|
|
||||||
|
**验证方法:**
|
||||||
|
- 运行 `go build` 确保编译通过
|
||||||
|
- 使用 `gofmt` 格式化代码
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 阶段 2: 套餐和订单模型修复
|
||||||
|
|
||||||
|
### Task 2.1: 修复套餐系列模型 (PackageSeries)
|
||||||
|
|
||||||
|
**文件**: `internal/model/package.go`
|
||||||
|
|
||||||
|
**修改内容:**
|
||||||
|
- 嵌入 `gorm.Model` 和 `BaseModel`
|
||||||
|
- 移除手动定义的 `ID`、`CreatedAt`、`UpdatedAt` 字段
|
||||||
|
- 所有字段显式指定 `column` 标签
|
||||||
|
- 表名从 `package_series` 改为 `tb_package_series`
|
||||||
|
- `SeriesCode` 唯一索引添加 `where:deleted_at IS NULL`
|
||||||
|
- 完善所有字段的中文注释
|
||||||
|
|
||||||
|
**验证方法:**
|
||||||
|
- 运行 `go build` 确保编译通过
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 2.2: 修复套餐模型 (Package)
|
||||||
|
|
||||||
|
**文件**: `internal/model/package.go`
|
||||||
|
|
||||||
|
**修改内容:**
|
||||||
|
- 嵌入 `gorm.Model` 和 `BaseModel`
|
||||||
|
- 移除手动定义的 `ID`、`CreatedAt`、`UpdatedAt` 字段
|
||||||
|
- 所有字段显式指定 `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.Model` 和 `BaseModel`
|
||||||
|
- 移除手动定义的 `ID`、`CreatedAt`、`UpdatedAt` 字段
|
||||||
|
- 所有字段显式指定 `column` 标签
|
||||||
|
- 金额字段(`CostPrice`、`RetailPrice`)从 `float64` 改为 `int64`
|
||||||
|
- 表名从 `agent_package_allocations` 改为 `tb_agent_package_allocation`
|
||||||
|
- 关联 ID 字段(`AgentID`、`PackageID`)添加 `index` 标签
|
||||||
|
- 完善所有字段的中文注释
|
||||||
|
|
||||||
|
**验证方法:**
|
||||||
|
- 运行 `go build` 确保编译通过
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 2.4: 修复套餐使用情况模型 (PackageUsage)
|
||||||
|
|
||||||
|
**文件**: `internal/model/package.go`
|
||||||
|
|
||||||
|
**修改内容:**
|
||||||
|
- 嵌入 `gorm.Model` 和 `BaseModel`
|
||||||
|
- 移除手动定义的 `ID`、`CreatedAt`、`UpdatedAt` 字段
|
||||||
|
- 所有字段显式指定 `column` 标签
|
||||||
|
- 表名从 `package_usages` 改为 `tb_package_usage`
|
||||||
|
- 关联 ID 字段(`OrderID`、`PackageID`、`IotCardID`、`DeviceID`)添加 `index` 标签
|
||||||
|
- 完善所有字段的中文注释
|
||||||
|
|
||||||
|
**验证方法:**
|
||||||
|
- 运行 `go build` 确保编译通过
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 2.5: 修复设备-SIM 卡绑定模型 (DeviceSimBinding)
|
||||||
|
|
||||||
|
**文件**: `internal/model/package.go`
|
||||||
|
|
||||||
|
**修改内容:**
|
||||||
|
- 嵌入 `gorm.Model` 和 `BaseModel`
|
||||||
|
- 移除手动定义的 `ID`、`CreatedAt`、`UpdatedAt` 字段
|
||||||
|
- 所有字段显式指定 `column` 标签
|
||||||
|
- 表名从 `device_sim_bindings` 改为 `tb_device_sim_binding`
|
||||||
|
- 添加复合索引:`DeviceID` 和 `SlotPosition` 使用 `index:idx_device_slot`
|
||||||
|
- 关联 ID 字段(`IotCardID`)添加独立 `index` 标签
|
||||||
|
- 完善所有字段的中文注释
|
||||||
|
|
||||||
|
**验证方法:**
|
||||||
|
- 运行 `go build` 确保编译通过
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 2.6: 修复订单模型 (Order)
|
||||||
|
|
||||||
|
**文件**: `internal/model/order.go`
|
||||||
|
|
||||||
|
**修改内容:**
|
||||||
|
- 嵌入 `gorm.Model` 和 `BaseModel`
|
||||||
|
- 移除手动定义的 `ID`、`CreatedAt`、`UpdatedAt` 字段
|
||||||
|
- 所有字段显式指定 `column` 标签
|
||||||
|
- 金额字段(`Amount`)从 `float64` 改为 `int64`
|
||||||
|
- `CarrierOrderData` 从 `pq.StringArray` 改为 `datatypes.JSON`,添加 `import "gorm.io/datatypes"`
|
||||||
|
- 表名从 `orders` 改为 `tb_order`
|
||||||
|
- `OrderNo` 唯一索引添加 `where:deleted_at IS NULL`
|
||||||
|
- 关联 ID 字段(`IotCardID`、`DeviceID`、`NumberCardID`、`PackageID`、`UserID`、`AgentID`)添加 `index` 标签
|
||||||
|
- 完善所有字段的中文注释
|
||||||
|
|
||||||
|
**验证方法:**
|
||||||
|
- 运行 `go build` 确保编译通过
|
||||||
|
- 检查 `datatypes.JSON` 导入是否正确
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 阶段 3: 分佣系统模型修复
|
||||||
|
|
||||||
|
### Task 3.1: 修复代理层级模型 (AgentHierarchy)
|
||||||
|
|
||||||
|
**文件**: `internal/model/commission.go`
|
||||||
|
|
||||||
|
**修改内容:**
|
||||||
|
- 嵌入 `gorm.Model` 和 `BaseModel`
|
||||||
|
- 移除手动定义的 `ID`、`CreatedAt`、`UpdatedAt` 字段
|
||||||
|
- 所有字段显式指定 `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.Model` 和 `BaseModel`
|
||||||
|
- 移除手动定义的 `ID`、`CreatedAt`、`UpdatedAt` 字段
|
||||||
|
- 所有字段显式指定 `column` 标签
|
||||||
|
- 金额字段(`CommissionValue`)从 `float64` 改为 `int64`
|
||||||
|
- 表名从 `commission_rules` 改为 `tb_commission_rule`
|
||||||
|
- 关联 ID 字段(`AgentID`、`SeriesID`、`PackageID`)添加 `index` 标签
|
||||||
|
- 完善所有字段的中文注释
|
||||||
|
|
||||||
|
**验证方法:**
|
||||||
|
- 运行 `go build` 确保编译通过
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 3.3: 修复阶梯分佣配置模型 (CommissionLadder)
|
||||||
|
|
||||||
|
**文件**: `internal/model/commission.go`
|
||||||
|
|
||||||
|
**修改内容:**
|
||||||
|
- 嵌入 `gorm.Model` 和 `BaseModel`
|
||||||
|
- 移除手动定义的 `ID`、`CreatedAt`、`UpdatedAt` 字段
|
||||||
|
- 所有字段显式指定 `column` 标签
|
||||||
|
- 金额字段(`CommissionValue`)从 `float64` 改为 `int64`
|
||||||
|
- 表名从 `commission_ladder` 改为 `tb_commission_ladder`
|
||||||
|
- 关联 ID 字段(`RuleID`)添加 `index` 标签
|
||||||
|
- 完善所有字段的中文注释
|
||||||
|
|
||||||
|
**验证方法:**
|
||||||
|
- 运行 `go build` 确保编译通过
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 3.4: 修复组合分佣条件模型 (CommissionCombinedCondition)
|
||||||
|
|
||||||
|
**文件**: `internal/model/commission.go`
|
||||||
|
|
||||||
|
**修改内容:**
|
||||||
|
- 嵌入 `gorm.Model` 和 `BaseModel`
|
||||||
|
- 移除手动定义的 `ID`、`CreatedAt`、`UpdatedAt` 字段
|
||||||
|
- 所有字段显式指定 `column` 标签
|
||||||
|
- 金额字段(`OneTimeCommissionValue`、`LongTermCommissionValue`)从 `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.Model` 和 `BaseModel`
|
||||||
|
- 移除手动定义的 `ID`、`CreatedAt`、`UpdatedAt` 字段
|
||||||
|
- 所有字段显式指定 `column` 标签
|
||||||
|
- 金额字段(`Amount`)从 `float64` 改为 `int64`
|
||||||
|
- 表名从 `commission_records` 改为 `tb_commission_record`
|
||||||
|
- 关联 ID 字段(`AgentID`、`OrderID`、`RuleID`)添加 `index` 标签
|
||||||
|
- 完善所有字段的中文注释
|
||||||
|
|
||||||
|
**验证方法:**
|
||||||
|
- 运行 `go build` 确保编译通过
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 3.6: 修复分佣审批模型 (CommissionApproval)
|
||||||
|
|
||||||
|
**文件**: `internal/model/commission.go`
|
||||||
|
|
||||||
|
**修改内容:**
|
||||||
|
- 嵌入 `gorm.Model` 和 `BaseModel`
|
||||||
|
- 移除手动定义的 `ID`、`CreatedAt`、`UpdatedAt` 字段
|
||||||
|
- 所有字段显式指定 `column` 标签
|
||||||
|
- 表名从 `commission_approvals` 改为 `tb_commission_approval`
|
||||||
|
- 关联 ID 字段(`CommissionRecordID`、`ApproverID`)添加 `index` 标签
|
||||||
|
- 完善所有字段的中文注释
|
||||||
|
|
||||||
|
**验证方法:**
|
||||||
|
- 运行 `go build` 确保编译通过
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 3.7: 修复分佣模板模型 (CommissionTemplate)
|
||||||
|
|
||||||
|
**文件**: `internal/model/commission.go`
|
||||||
|
|
||||||
|
**修改内容:**
|
||||||
|
- 嵌入 `gorm.Model` 和 `BaseModel`
|
||||||
|
- 移除手动定义的 `ID`、`CreatedAt`、`UpdatedAt` 字段
|
||||||
|
- 所有字段显式指定 `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.Model` 和 `BaseModel`
|
||||||
|
- 移除手动定义的 `ID`、`CreatedAt`、`UpdatedAt` 字段
|
||||||
|
- 所有字段显式指定 `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.Model` 和 `BaseModel`
|
||||||
|
- 移除手动定义的 `ID`、`CreatedAt`、`UpdatedAt` 字段
|
||||||
|
- 所有字段显式指定 `column` 标签
|
||||||
|
- 金额字段(`Amount`、`Fee`、`ActualAmount`)从 `float64` 改为 `int64`,数据库类型从 `decimal(18,2)` 改为 `bigint`
|
||||||
|
- `AccountInfo` 从 `pq.StringArray` 改为 `datatypes.JSON`
|
||||||
|
- 表名从 `commission_withdrawal_requests` 改为 `tb_commission_withdrawal_request`
|
||||||
|
- 关联 ID 字段(`AgentID`、`ApprovedBy`)添加 `index` 标签
|
||||||
|
- 完善所有字段的中文注释
|
||||||
|
|
||||||
|
**验证方法:**
|
||||||
|
- 运行 `go build` 确保编译通过
|
||||||
|
- 检查 `datatypes.JSON` 导入是否正确
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 4.2: 修复佣金提现设置模型 (CommissionWithdrawalSetting)
|
||||||
|
|
||||||
|
**文件**: `internal/model/financial.go`
|
||||||
|
|
||||||
|
**修改内容:**
|
||||||
|
- 嵌入 `gorm.Model` 和 `BaseModel`
|
||||||
|
- 移除手动定义的 `ID`、`CreatedAt`、`UpdatedAt` 字段
|
||||||
|
- 所有字段显式指定 `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.Model` 和 `BaseModel`
|
||||||
|
- 移除手动定义的 `ID`、`CreatedAt`、`UpdatedAt` 字段
|
||||||
|
- 所有字段显式指定 `column` 标签
|
||||||
|
- 表名从 `payment_merchant_settings` 改为 `tb_payment_merchant_setting`
|
||||||
|
- 关联 ID 字段(`UserID`)添加 `index` 标签
|
||||||
|
- 完善所有字段的中文注释
|
||||||
|
|
||||||
|
**验证方法:**
|
||||||
|
- 运行 `go build` 确保编译通过
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 4.4: 修复开发能力配置模型 (DevCapabilityConfig)
|
||||||
|
|
||||||
|
**文件**: `internal/model/system.go`
|
||||||
|
|
||||||
|
**修改内容:**
|
||||||
|
- 嵌入 `gorm.Model` 和 `BaseModel`
|
||||||
|
- 移除手动定义的 `ID`、`CreatedAt`、`UpdatedAt` 字段
|
||||||
|
- 所有字段显式指定 `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.Model` 和 `BaseModel`
|
||||||
|
- 移除手动定义的 `ID`、`CreatedAt`、`UpdatedAt` 字段
|
||||||
|
- 所有字段显式指定 `column` 标签
|
||||||
|
- 表名从 `card_replacement_requests` 改为 `tb_card_replacement_request`
|
||||||
|
- 关联 ID 字段(`UserID`、`ApprovedBy`)添加 `index` 标签
|
||||||
|
- 完善所有字段的中文注释
|
||||||
|
|
||||||
|
**验证方法:**
|
||||||
|
- 运行 `go build` 确保编译通过
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 4.6: 修复轮询配置模型 (PollingConfig)
|
||||||
|
|
||||||
|
**文件**: `internal/model/polling.go`
|
||||||
|
|
||||||
|
**修改内容:**
|
||||||
|
- 嵌入 `gorm.Model` 和 `BaseModel`
|
||||||
|
- 移除手动定义的 `ID`、`CreatedAt`、`UpdatedAt` 字段
|
||||||
|
- 所有字段显式指定 `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`(日志表不需要审计)
|
||||||
|
- 保留 `ID`、`CreatedAt` 字段,移除 `UpdatedAt`
|
||||||
|
- 所有字段显式指定 `column` 标签
|
||||||
|
- 表名从 `data_usage_records` 改为 `tb_data_usage_record`
|
||||||
|
- 关联 ID 字段(`IotCardID`)添加 `index` 标签
|
||||||
|
- 完善所有字段的中文注释
|
||||||
|
|
||||||
|
**验证方法:**
|
||||||
|
- 运行 `go build` 确保编译通过
|
||||||
|
- 确认模型不包含 `UpdatedAt` 和 `DeletedAt`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 阶段 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 模型字段规范** 新增小节:
|
||||||
|
```markdown
|
||||||
|
**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,确保符合项目规范
|
||||||
Reference in New Issue
Block a user