feat: 钱包系统分离 - 代理钱包与卡钱包完全隔离
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m17s

## 变更概述
将统一钱包系统拆分为代理钱包和卡钱包两个独立系统,实现数据表和代码层面的完全隔离。

## 数据库变更
- 新增 6 张表:tb_agent_wallet、tb_agent_wallet_transaction、tb_agent_recharge_record、tb_card_wallet、tb_card_wallet_transaction、tb_card_recharge_record
- 删除 3 张旧表:tb_wallet、tb_wallet_transaction、tb_recharge_record
- 代理钱包:按 (shop_id, wallet_type) 唯一标识,支持主钱包和分佣钱包
- 卡钱包:按 (resource_type, resource_id) 唯一标识,支持物联网卡和设备

## 代码变更
- Model 层:新增 AgentWallet、AgentWalletTransaction、AgentRechargeRecord、CardWallet、CardWalletTransaction、CardRechargeRecord 模型
- Store 层:新增 6 个独立 Store,支持事务、乐观锁、Redis 缓存
- Service 层:重构 commission_calculation、commission_withdrawal、order、recharge 等 8 个服务
- Bootstrap 层:更新 Store 和 Service 依赖注入
- 常量层:按钱包类型重新组织常量和 Redis Key 生成函数

## 技术特性
- 乐观锁:使用 version 字段防止并发冲突
- 多租户:支持 shop_id_tag 和 enterprise_id_tag 过滤
- 事务管理:所有余额变动使用事务保证 ACID
- 缓存策略:Cache-Aside 模式,余额变动后删除缓存

## 业务影响
- 代理钱包和卡钱包业务完全隔离,互不影响
- 为独立监控、优化、扩展打下基础
- 提升代理钱包的稳定性和独立性

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-25 09:51:00 +08:00
parent f32d32cd36
commit 18daeae65a
66 changed files with 4420 additions and 1090 deletions

View File

@@ -0,0 +1,91 @@
package model
import (
"time"
"gorm.io/gorm"
)
// AgentWallet 代理钱包模型
// 管理店铺级别的主钱包和分佣钱包
type AgentWallet struct {
ID uint `gorm:"column:id;primaryKey" json:"id"`
ShopID uint `gorm:"column:shop_id;not null;index;comment:店铺ID" json:"shop_id"`
WalletType string `gorm:"column:wallet_type;type:varchar(20);not null;comment:钱包类型(main-主钱包 | commission-分佣钱包)" json:"wallet_type"`
Balance int64 `gorm:"column:balance;type:bigint;not null;default:0;comment:余额(单位:分)" json:"balance"`
FrozenBalance int64 `gorm:"column:frozen_balance;type:bigint;not null;default:0;comment:冻结余额(单位:分)" json:"frozen_balance"`
Currency string `gorm:"column:currency;type:varchar(10);not null;default:'CNY';comment:币种" json:"currency"`
Status int `gorm:"column:status;type:int;not null;default:1;comment:钱包状态(1-正常 2-冻结 3-关闭)" json:"status"`
Version int `gorm:"column:version;type:int;not null;default:0;comment:版本号(乐观锁)" json:"version"`
ShopIDTag uint `gorm:"column:shop_id_tag;not null;index;comment:店铺ID标签(多租户过滤)" json:"shop_id_tag"`
EnterpriseIDTag *uint `gorm:"column:enterprise_id_tag;index;comment:企业ID标签(多租户过滤)" json:"enterprise_id_tag,omitempty"`
CreatedAt time.Time `gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;not null;default:CURRENT_TIMESTAMP" json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;index" json:"deleted_at,omitempty"`
}
// TableName 指定表名
func (AgentWallet) TableName() string {
return "tb_agent_wallet"
}
// GetAvailableBalance 获取可用余额 = balance - frozen_balance
func (w *AgentWallet) GetAvailableBalance() int64 {
return w.Balance - w.FrozenBalance
}
// AgentWalletTransaction 代理钱包交易记录模型
// 记录所有代理钱包余额变动
type AgentWalletTransaction struct {
ID uint `gorm:"column:id;primaryKey" json:"id"`
AgentWalletID uint `gorm:"column:agent_wallet_id;not null;index;comment:代理钱包ID" json:"agent_wallet_id"`
ShopID uint `gorm:"column:shop_id;not null;index;comment:店铺ID(冗余字段,便于查询)" json:"shop_id"`
UserID uint `gorm:"column:user_id;not null;comment:操作人用户ID" json:"user_id"`
TransactionType string `gorm:"column:transaction_type;type:varchar(20);not null;comment:交易类型(recharge-充值 | deduct-扣款 | refund-退款 | commission-分佣 | withdrawal-提现)" json:"transaction_type"`
Amount int64 `gorm:"column:amount;type:bigint;not null;comment:变动金额(单位:分,正数为增加,负数为减少)" json:"amount"`
BalanceBefore int64 `gorm:"column:balance_before;type:bigint;not null;comment:变动前余额(单位:分)" json:"balance_before"`
BalanceAfter int64 `gorm:"column:balance_after;type:bigint;not null;comment:变动后余额(单位:分)" json:"balance_after"`
Status int `gorm:"column:status;type:int;not null;default:1;comment:交易状态(1-成功 2-失败 3-处理中)" json:"status"`
ReferenceType *string `gorm:"column:reference_type;type:varchar(50);comment:关联业务类型(order | commission | withdrawal | topup)" json:"reference_type,omitempty"`
ReferenceID *uint `gorm:"column:reference_id;comment:关联业务ID" json:"reference_id,omitempty"`
Remark *string `gorm:"column:remark;type:text;comment:备注" json:"remark,omitempty"`
Metadata *string `gorm:"column:metadata;type:jsonb;comment:扩展信息(如手续费、支付方式等)" json:"metadata,omitempty"`
Creator uint `gorm:"column:creator;not null;comment:创建人ID" json:"creator"`
ShopIDTag uint `gorm:"column:shop_id_tag;not null;index;comment:店铺ID标签(多租户过滤)" json:"shop_id_tag"`
EnterpriseIDTag *uint `gorm:"column:enterprise_id_tag;index;comment:企业ID标签(多租户过滤)" json:"enterprise_id_tag,omitempty"`
CreatedAt time.Time `gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;not null;default:CURRENT_TIMESTAMP" json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;index" json:"deleted_at,omitempty"`
}
// TableName 指定表名
func (AgentWalletTransaction) TableName() string {
return "tb_agent_wallet_transaction"
}
// AgentRechargeRecord 代理充值记录模型
// 记录所有代理充值操作
type AgentRechargeRecord struct {
ID uint `gorm:"column:id;primaryKey" json:"id"`
UserID uint `gorm:"column:user_id;not null;index;comment:操作人用户ID" json:"user_id"`
AgentWalletID uint `gorm:"column:agent_wallet_id;not null;comment:代理钱包ID" json:"agent_wallet_id"`
ShopID uint `gorm:"column:shop_id;not null;index;comment:店铺ID(冗余字段,便于查询)" json:"shop_id"`
RechargeNo string `gorm:"column:recharge_no;type:varchar(50);not null;uniqueIndex;comment:充值订单号(格式:ARCH+时间戳+随机数)" json:"recharge_no"`
Amount int64 `gorm:"column:amount;type:bigint;not null;comment:充值金额(单位:分,最小10000分=100元)" json:"amount"`
PaymentMethod string `gorm:"column:payment_method;type:varchar(20);not null;comment:支付方式(alipay-支付宝 | wechat-微信 | bank-银行转账 | offline-线下)" json:"payment_method"`
PaymentChannel *string `gorm:"column:payment_channel;type:varchar(50);comment:支付渠道" json:"payment_channel,omitempty"`
PaymentTransactionID *string `gorm:"column:payment_transaction_id;type:varchar(100);comment:第三方支付交易号" json:"payment_transaction_id,omitempty"`
Status int `gorm:"column:status;type:int;not null;default:1;comment:充值状态(1-待支付 2-已支付 3-已完成 4-已关闭 5-已退款)" json:"status"`
PaidAt *time.Time `gorm:"column:paid_at;comment:支付时间" json:"paid_at,omitempty"`
CompletedAt *time.Time `gorm:"column:completed_at;comment:完成时间" json:"completed_at,omitempty"`
ShopIDTag uint `gorm:"column:shop_id_tag;not null;index;comment:店铺ID标签(多租户过滤)" json:"shop_id_tag"`
EnterpriseIDTag *uint `gorm:"column:enterprise_id_tag;index;comment:企业ID标签(多租户过滤)" json:"enterprise_id_tag,omitempty"`
CreatedAt time.Time `gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;not null;default:CURRENT_TIMESTAMP" json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;index" json:"deleted_at,omitempty"`
}
// TableName 指定表名
func (AgentRechargeRecord) TableName() string {
return "tb_agent_recharge_record"
}

View File

@@ -0,0 +1,93 @@
package model
import (
"time"
"gorm.io/gorm"
)
// CardWallet 卡钱包模型
// 管理物联网卡和设备级别的钱包
type CardWallet struct {
ID uint `gorm:"column:id;primaryKey" json:"id"`
ResourceType string `gorm:"column:resource_type;type:varchar(20);not null;index;comment:资源类型(iot_card-物联网卡 | device-设备)" json:"resource_type"`
ResourceID uint `gorm:"column:resource_id;not null;index;comment:资源ID(关联tb_iot_card.id或tb_device.id)" json:"resource_id"`
Balance int64 `gorm:"column:balance;type:bigint;not null;default:0;comment:余额(单位:分)" json:"balance"`
FrozenBalance int64 `gorm:"column:frozen_balance;type:bigint;not null;default:0;comment:冻结余额(单位:分)" json:"frozen_balance"`
Currency string `gorm:"column:currency;type:varchar(10);not null;default:'CNY';comment:币种" json:"currency"`
Status int `gorm:"column:status;type:int;not null;default:1;comment:钱包状态(1-正常 2-冻结 3-关闭)" json:"status"`
Version int `gorm:"column:version;type:int;not null;default:0;comment:版本号(乐观锁)" json:"version"`
ShopIDTag uint `gorm:"column:shop_id_tag;not null;index;comment:店铺ID标签(多租户过滤)" json:"shop_id_tag"`
EnterpriseIDTag *uint `gorm:"column:enterprise_id_tag;index;comment:企业ID标签(多租户过滤)" json:"enterprise_id_tag,omitempty"`
CreatedAt time.Time `gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;not null;default:CURRENT_TIMESTAMP" json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;index" json:"deleted_at,omitempty"`
}
// TableName 指定表名
func (CardWallet) TableName() string {
return "tb_card_wallet"
}
// GetAvailableBalance 获取可用余额 = balance - frozen_balance
func (w *CardWallet) GetAvailableBalance() int64 {
return w.Balance - w.FrozenBalance
}
// CardWalletTransaction 卡钱包交易记录模型
// 记录所有卡钱包余额变动
type CardWalletTransaction struct {
ID uint `gorm:"column:id;primaryKey" json:"id"`
CardWalletID uint `gorm:"column:card_wallet_id;not null;index;comment:卡钱包ID" json:"card_wallet_id"`
ResourceType string `gorm:"column:resource_type;type:varchar(20);not null;index;comment:资源类型(冗余字段,便于查询)" json:"resource_type"`
ResourceID uint `gorm:"column:resource_id;not null;index;comment:资源ID(冗余字段,便于查询)" json:"resource_id"`
UserID uint `gorm:"column:user_id;not null;comment:操作人用户ID" json:"user_id"`
TransactionType string `gorm:"column:transaction_type;type:varchar(20);not null;comment:交易类型(recharge-充值 | deduct-扣款 | refund-退款)" json:"transaction_type"`
Amount int64 `gorm:"column:amount;type:bigint;not null;comment:变动金额(单位:分,正数为增加,负数为减少)" json:"amount"`
BalanceBefore int64 `gorm:"column:balance_before;type:bigint;not null;comment:变动前余额(单位:分)" json:"balance_before"`
BalanceAfter int64 `gorm:"column:balance_after;type:bigint;not null;comment:变动后余额(单位:分)" json:"balance_after"`
Status int `gorm:"column:status;type:int;not null;default:1;comment:交易状态(1-成功 2-失败 3-处理中)" json:"status"`
ReferenceType *string `gorm:"column:reference_type;type:varchar(50);comment:关联业务类型(order | topup)" json:"reference_type,omitempty"`
ReferenceID *uint `gorm:"column:reference_id;comment:关联业务ID" json:"reference_id,omitempty"`
Remark *string `gorm:"column:remark;type:text;comment:备注" json:"remark,omitempty"`
Metadata *string `gorm:"column:metadata;type:jsonb;comment:扩展信息(如套餐信息、支付方式等)" json:"metadata,omitempty"`
Creator uint `gorm:"column:creator;not null;comment:创建人ID" json:"creator"`
ShopIDTag uint `gorm:"column:shop_id_tag;not null;index;comment:店铺ID标签(多租户过滤)" json:"shop_id_tag"`
EnterpriseIDTag *uint `gorm:"column:enterprise_id_tag;index;comment:企业ID标签(多租户过滤)" json:"enterprise_id_tag,omitempty"`
CreatedAt time.Time `gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;not null;default:CURRENT_TIMESTAMP" json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;index" json:"deleted_at,omitempty"`
}
// TableName 指定表名
func (CardWalletTransaction) TableName() string {
return "tb_card_wallet_transaction"
}
// CardRechargeRecord 卡充值记录模型
// 记录所有卡充值操作
type CardRechargeRecord struct {
ID uint `gorm:"column:id;primaryKey" json:"id"`
UserID uint `gorm:"column:user_id;not null;index;comment:操作人用户ID" json:"user_id"`
CardWalletID uint `gorm:"column:card_wallet_id;not null;comment:卡钱包ID" json:"card_wallet_id"`
ResourceType string `gorm:"column:resource_type;type:varchar(20);not null;index;comment:资源类型(冗余字段)" json:"resource_type"`
ResourceID uint `gorm:"column:resource_id;not null;index;comment:资源ID(冗余字段)" json:"resource_id"`
RechargeNo string `gorm:"column:recharge_no;type:varchar(50);not null;uniqueIndex;comment:充值订单号(格式:CRCH+时间戳+随机数)" json:"recharge_no"`
Amount int64 `gorm:"column:amount;type:bigint;not null;comment:充值金额(单位:分,最小100分=1元)" json:"amount"`
PaymentMethod string `gorm:"column:payment_method;type:varchar(20);not null;comment:支付方式(alipay-支付宝 | wechat-微信)" json:"payment_method"`
PaymentChannel *string `gorm:"column:payment_channel;type:varchar(50);comment:支付渠道" json:"payment_channel,omitempty"`
PaymentTransactionID *string `gorm:"column:payment_transaction_id;type:varchar(100);comment:第三方支付交易号" json:"payment_transaction_id,omitempty"`
Status int `gorm:"column:status;type:int;not null;default:1;comment:充值状态(1-待支付 2-已支付 3-已完成 4-已关闭 5-已退款)" json:"status"`
PaidAt *time.Time `gorm:"column:paid_at;comment:支付时间" json:"paid_at,omitempty"`
CompletedAt *time.Time `gorm:"column:completed_at;comment:完成时间" json:"completed_at,omitempty"`
ShopIDTag uint `gorm:"column:shop_id_tag;not null;index;comment:店铺ID标签(多租户过滤)" json:"shop_id_tag"`
EnterpriseIDTag *uint `gorm:"column:enterprise_id_tag;index;comment:企业ID标签(多租户过滤)" json:"enterprise_id_tag,omitempty"`
CreatedAt time.Time `gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;not null;default:CURRENT_TIMESTAMP" json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;index" json:"deleted_at,omitempty"`
}
// TableName 指定表名
func (CardRechargeRecord) TableName() string {
return "tb_card_recharge_record"
}

View File

@@ -20,24 +20,24 @@ type ListStandaloneIotCardRequest struct {
}
type StandaloneIotCardResponse struct {
ID uint `json:"id" description:"卡ID"`
ICCID string `json:"iccid" description:"ICCID"`
CardCategory string `json:"card_category" description:"卡业务类型 (normal:普通卡, industry:行业卡)"`
CarrierID uint `json:"carrier_id" description:"运营商ID"`
CarrierType string `json:"carrier_type,omitempty" description:"运营商类型 (CMCC:中国移动, CUCC:中国联通, CTCC:中国电信, CBN:中国广电)"`
CarrierName string `json:"carrier_name,omitempty" description:"运营商名称"`
IMSI string `json:"imsi,omitempty" description:"IMSI"`
MSISDN string `json:"msisdn,omitempty" description:"卡接入号"`
BatchNo string `json:"batch_no,omitempty" description:"批次号"`
Supplier string `json:"supplier,omitempty" description:"供应商"`
CostPrice int64 `json:"cost_price" description:"成本价(分)"`
DistributePrice int64 `json:"distribute_price" description:"分销价(分)"`
Status int `json:"status" description:"状态 (1:在库, 2:已分销, 3:已激活, 4:已停用)"`
ShopID *uint `json:"shop_id,omitempty" description:"店铺ID"`
ShopName string `json:"shop_name,omitempty" description:"店铺名称"`
ActivatedAt *time.Time `json:"activated_at,omitempty" description:"激活时间"`
ActivationStatus int `json:"activation_status" description:"激活状态 (0:未激活, 1:已激活)"`
RealNameStatus int `json:"real_name_status" description:"实名状态 (0:未实名, 1:已实名)"`
ID uint `json:"id" description:"卡ID"`
ICCID string `json:"iccid" description:"ICCID"`
CardCategory string `json:"card_category" description:"卡业务类型 (normal:普通卡, industry:行业卡)"`
CarrierID uint `json:"carrier_id" description:"运营商ID"`
CarrierType string `json:"carrier_type,omitempty" description:"运营商类型 (CMCC:中国移动, CUCC:中国联通, CTCC:中国电信, CBN:中国广电)"`
CarrierName string `json:"carrier_name,omitempty" description:"运营商名称"`
IMSI string `json:"imsi,omitempty" description:"IMSI"`
MSISDN string `json:"msisdn,omitempty" description:"卡接入号"`
BatchNo string `json:"batch_no,omitempty" description:"批次号"`
Supplier string `json:"supplier,omitempty" description:"供应商"`
CostPrice int64 `json:"cost_price" description:"成本价(分)"`
DistributePrice int64 `json:"distribute_price" description:"分销价(分)"`
Status int `json:"status" description:"状态 (1:在库, 2:已分销, 3:已激活, 4:已停用)"`
ShopID *uint `json:"shop_id,omitempty" description:"店铺ID"`
ShopName string `json:"shop_name,omitempty" description:"店铺名称"`
ActivatedAt *time.Time `json:"activated_at,omitempty" description:"激活时间"`
ActivationStatus int `json:"activation_status" description:"激活状态 (0:未激活, 1:已激活)"`
RealNameStatus int `json:"real_name_status" description:"实名状态 (0:未实名, 1:已实名)"`
NetworkStatus int `json:"network_status" description:"网络状态 (0:停机, 1:开机)"`
DataUsageMB int64 `json:"data_usage_mb" description:"累计流量使用(MB)"`
CurrentMonthUsageMB float64 `json:"current_month_usage_mb" description:"本月已用流量(MB)"`
@@ -47,11 +47,11 @@ type StandaloneIotCardResponse struct {
LastRealNameCheckAt *time.Time `json:"last_real_name_check_at,omitempty" description:"最后实名检查时间"`
EnablePolling bool `json:"enable_polling" description:"是否参与轮询"`
SeriesID *uint `json:"series_id,omitempty" description:"套餐系列ID"`
SeriesName string `json:"series_name,omitempty" description:"套餐系列名称"`
FirstCommissionPaid bool `json:"first_commission_paid" description:"一次性佣金是否已发放"`
AccumulatedRecharge int64 `json:"accumulated_recharge" description:"累计充值金额(分)"`
CreatedAt time.Time `json:"created_at" description:"创建时间"`
UpdatedAt time.Time `json:"updated_at" description:"更新时间"`
SeriesName string `json:"series_name,omitempty" description:"套餐系列名称"`
FirstCommissionPaid bool `json:"first_commission_paid" description:"一次性佣金是否已发放"`
AccumulatedRecharge int64 `json:"accumulated_recharge" description:"累计充值金额(分)"`
CreatedAt time.Time `json:"created_at" description:"创建时间"`
UpdatedAt time.Time `json:"updated_at" description:"更新时间"`
}
type ListStandaloneIotCardResponse struct {

View File

@@ -26,15 +26,15 @@ type UpdateDataCleanupConfigParams struct {
// DataCleanupConfigResp 数据清理配置响应
type DataCleanupConfigResp struct {
ID uint `json:"id" description:"配置ID"`
TargetTable string `json:"table_name" description:"表名"`
RetentionDays int `json:"retention_days" description:"保留天数"`
BatchSize int `json:"batch_size" description:"每批删除条数"`
Enabled int `json:"enabled" description:"是否启用0-禁用1-启用"`
Description string `json:"description" description:"配置说明"`
CreatedAt time.Time `json:"created_at" description:"创建时间"`
UpdatedAt time.Time `json:"updated_at" description:"更新时间"`
UpdatedBy *uint `json:"updated_by,omitempty" description:"更新人ID"`
ID uint `json:"id" description:"配置ID"`
TargetTable string `json:"table_name" description:"表名"`
RetentionDays int `json:"retention_days" description:"保留天数"`
BatchSize int `json:"batch_size" description:"每批删除条数"`
Enabled int `json:"enabled" description:"是否启用0-禁用1-启用"`
Description string `json:"description" description:"配置说明"`
CreatedAt time.Time `json:"created_at" description:"创建时间"`
UpdatedAt time.Time `json:"updated_at" description:"更新时间"`
UpdatedBy *uint `json:"updated_by,omitempty" description:"更新人ID"`
}
// DataCleanupConfigListResp 数据清理配置列表响应

View File

@@ -57,27 +57,27 @@ func (Package) TableName() string {
// 跟踪单卡套餐和设备级套餐的流量使用
type PackageUsage struct {
gorm.Model
BaseModel `gorm:"embedded"`
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"`
IotCardID uint `gorm:"column:iot_card_id;index;comment:IoT卡ID(单卡套餐时有值)" json:"iot_card_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"`
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"`
VirtualDataUsageMB int64 `gorm:"column:virtual_data_usage_mb;type:bigint;default:0;comment:虚流量使用(MB)" json:"virtual_data_usage_mb"`
ActivatedAt time.Time `gorm:"column:activated_at;not null;comment:套餐生效时间" json:"activated_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:状态 0-待生效 1-生效中 2-已用完 3-已过期 4-已失效" json:"status"`
LastPackageCheckAt *time.Time `gorm:"column:last_package_check_at;comment:最后一次套餐流量检查时间" json:"last_package_check_at"`
Priority int `gorm:"column:priority;type:int;default:1;index:idx_package_usage_priority;comment:优先级(主套餐和加油包按此字段排队,数字越小优先级越高)" json:"priority"`
MasterUsageID *uint `gorm:"column:master_usage_id;type:bigint;index:idx_package_usage_master_usage_id;comment:主套餐使用记录ID(加油包关联主套餐,主套餐此字段为NULL)" json:"master_usage_id"`
HasIndependentExpiry bool `gorm:"column:has_independent_expiry;type:boolean;default:false;comment:加油包是否有独立有效期(true-有独立到期时间 false-跟随主套餐)" json:"has_independent_expiry"`
PendingRealnameActivation bool `gorm:"column:pending_realname_activation;type:boolean;default:false;comment:是否等待实名激活(true-待实名后激活 false-已激活或不需实名)" json:"pending_realname_activation"`
DataResetCycle string `gorm:"column:data_reset_cycle;type:varchar(20);comment:流量重置周期(从Package复制,用于历史记录)" json:"data_reset_cycle"`
LastResetAt *time.Time `gorm:"column:last_reset_at;comment:最后一次流量重置时间" json:"last_reset_at"`
NextResetAt *time.Time `gorm:"column:next_reset_at;index:idx_package_usage_next_reset_at;comment:下次流量重置时间(用于定时任务查询)" json:"next_reset_at"`
BaseModel `gorm:"embedded"`
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"`
IotCardID uint `gorm:"column:iot_card_id;index;comment:IoT卡ID(单卡套餐时有值)" json:"iot_card_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"`
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"`
VirtualDataUsageMB int64 `gorm:"column:virtual_data_usage_mb;type:bigint;default:0;comment:虚流量使用(MB)" json:"virtual_data_usage_mb"`
ActivatedAt time.Time `gorm:"column:activated_at;not null;comment:套餐生效时间" json:"activated_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:状态 0-待生效 1-生效中 2-已用完 3-已过期 4-已失效" json:"status"`
LastPackageCheckAt *time.Time `gorm:"column:last_package_check_at;comment:最后一次套餐流量检查时间" json:"last_package_check_at"`
Priority int `gorm:"column:priority;type:int;default:1;index:idx_package_usage_priority;comment:优先级(主套餐和加油包按此字段排队,数字越小优先级越高)" json:"priority"`
MasterUsageID *uint `gorm:"column:master_usage_id;type:bigint;index:idx_package_usage_master_usage_id;comment:主套餐使用记录ID(加油包关联主套餐,主套餐此字段为NULL)" json:"master_usage_id"`
HasIndependentExpiry bool `gorm:"column:has_independent_expiry;type:boolean;default:false;comment:加油包是否有独立有效期(true-有独立到期时间 false-跟随主套餐)" json:"has_independent_expiry"`
PendingRealnameActivation bool `gorm:"column:pending_realname_activation;type:boolean;default:false;comment:是否等待实名激活(true-待实名后激活 false-已激活或不需实名)" json:"pending_realname_activation"`
DataResetCycle string `gorm:"column:data_reset_cycle;type:varchar(20);comment:流量重置周期(从Package复制,用于历史记录)" json:"data_reset_cycle"`
LastResetAt *time.Time `gorm:"column:last_reset_at;comment:最后一次流量重置时间" json:"last_reset_at"`
NextResetAt *time.Time `gorm:"column:next_reset_at;index:idx_package_usage_next_reset_at;comment:下次流量重置时间(用于定时任务查询)" json:"next_reset_at"`
}
// TableName 指定表名
@@ -88,13 +88,13 @@ func (PackageUsage) TableName() string {
// PackageUsageDailyRecord 套餐流量日记录模型
// 记录每个套餐每天的流量使用情况,用于流量详单查询
type PackageUsageDailyRecord struct {
ID uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
PackageUsageID uint `gorm:"column:package_usage_id;not null;uniqueIndex:idx_package_usage_daily_record_unique;index:idx_package_usage_daily_record_date;comment:套餐使用记录ID" json:"package_usage_id"`
Date time.Time `gorm:"column:date;type:date;not null;uniqueIndex:idx_package_usage_daily_record_unique;comment:日期" json:"date"`
DailyUsageMB int `gorm:"column:daily_usage_mb;type:int;default:0;comment:当日流量使用量(MB)" json:"daily_usage_mb"`
CumulativeUsageMB int64 `gorm:"column:cumulative_usage_mb;type:bigint;default:0;comment:截止当日的累计流量(MB)" json:"cumulative_usage_mb"`
CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP" json:"updated_at"`
ID uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
PackageUsageID uint `gorm:"column:package_usage_id;not null;uniqueIndex:idx_package_usage_daily_record_unique;index:idx_package_usage_daily_record_date;comment:套餐使用记录ID" json:"package_usage_id"`
Date time.Time `gorm:"column:date;type:date;not null;uniqueIndex:idx_package_usage_daily_record_unique;comment:日期" json:"date"`
DailyUsageMB int `gorm:"column:daily_usage_mb;type:int;default:0;comment:当日流量使用量(MB)" json:"daily_usage_mb"`
CumulativeUsageMB int64 `gorm:"column:cumulative_usage_mb;type:bigint;default:0;comment:截止当日的累计流量(MB)" json:"cumulative_usage_mb"`
CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP" json:"updated_at"`
}
// TableName 指定表名

View File

@@ -6,21 +6,21 @@ import (
// PollingConfig 轮询配置表
type PollingConfig struct {
ID uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
ConfigName string `gorm:"column:config_name;type:varchar(100);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"`
CardCategory string `gorm:"column:card_category;type:varchar(50);comment:卡业务类型normal/industry" json:"card_category"`
CarrierID *uint `gorm:"column:carrier_id;comment:运营商ID可选精确匹配" json:"carrier_id"`
Priority int `gorm:"column:priority;not null;default:100;comment:优先级(数字越小优先级越高)" json:"priority"`
RealnameCheckInterval *int `gorm:"column:realname_check_interval;comment:实名检查间隔NULL表示不检查" json:"realname_check_interval"`
CarddataCheckInterval *int `gorm:"column:carddata_check_interval;comment:流量检查间隔NULL表示不检查" json:"carddata_check_interval"`
PackageCheckInterval *int `gorm:"column:package_check_interval;comment:套餐检查间隔NULL表示不检查" json:"package_check_interval"`
Status int16 `gorm:"column:status;type:smallint;not null;default:1;comment:状态0-禁用1-启用" json:"status"`
Description string `gorm:"column:description;type:text;comment:配置说明" json:"description"`
CreatedAt time.Time `gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;not null;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"`
CreatedBy *uint `gorm:"column:created_by;comment:创建人ID" json:"created_by"`
UpdatedBy *uint `gorm:"column:updated_by;comment:更新人ID" json:"updated_by"`
ID uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
ConfigName string `gorm:"column:config_name;type:varchar(100);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"`
CardCategory string `gorm:"column:card_category;type:varchar(50);comment:卡业务类型normal/industry" json:"card_category"`
CarrierID *uint `gorm:"column:carrier_id;comment:运营商ID可选精确匹配" json:"carrier_id"`
Priority int `gorm:"column:priority;not null;default:100;comment:优先级(数字越小优先级越高)" json:"priority"`
RealnameCheckInterval *int `gorm:"column:realname_check_interval;comment:实名检查间隔NULL表示不检查" json:"realname_check_interval"`
CarddataCheckInterval *int `gorm:"column:carddata_check_interval;comment:流量检查间隔NULL表示不检查" json:"carddata_check_interval"`
PackageCheckInterval *int `gorm:"column:package_check_interval;comment:套餐检查间隔NULL表示不检查" json:"package_check_interval"`
Status int16 `gorm:"column:status;type:smallint;not null;default:1;comment:状态0-禁用1-启用" json:"status"`
Description string `gorm:"column:description;type:text;comment:配置说明" json:"description"`
CreatedAt time.Time `gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;not null;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"`
CreatedBy *uint `gorm:"column:created_by;comment:创建人ID" json:"created_by"`
UpdatedBy *uint `gorm:"column:updated_by;comment:更新人ID" json:"updated_by"`
}
// TableName 指定表名

View File

@@ -1,99 +0,0 @@
package model
import (
"database/sql/driver"
"encoding/json"
"time"
"gorm.io/gorm"
)
// Wallet 钱包模型
// 个人客户和代理商的资金账户,支持充值、消费、提现等操作
// 使用乐观锁version字段防止并发余额冲突
// 钱包归属资源(卡/设备/店铺),支持资源转手场景
type Wallet struct {
gorm.Model
BaseModel `gorm:"embedded"`
ResourceType string `gorm:"column:resource_type;type:varchar(20);not null;uniqueIndex:idx_wallet_resource_type_currency,priority:1;index:idx_wallet_resource,priority:1;comment:资源类型 iot_card-物联网卡 device-设备 shop-店铺" json:"resource_type"`
ResourceID uint `gorm:"column:resource_id;not null;uniqueIndex:idx_wallet_resource_type_currency,priority:2;index:idx_wallet_resource,priority:2;comment:资源ID" json:"resource_id"`
WalletType string `gorm:"column:wallet_type;type:varchar(20);not null;uniqueIndex:idx_wallet_resource_type_currency,priority:3;comment:钱包类型 main-主钱包 commission-分佣钱包" json:"wallet_type"`
Balance int64 `gorm:"column:balance;type:bigint;not null;default:0;comment:余额(分)" json:"balance"`
FrozenBalance int64 `gorm:"column:frozen_balance;type:bigint;not null;default:0;comment:冻结余额(分)" json:"frozen_balance"`
Currency string `gorm:"column:currency;type:varchar(10);not null;default:'CNY';uniqueIndex:idx_wallet_resource_type_currency,priority:4;comment:币种" json:"currency"`
Status int `gorm:"column:status;type:int;not null;default:1;index:idx_wallet_status;comment:钱包状态 1-正常 2-冻结 3-关闭" json:"status"`
Version int `gorm:"column:version;type:int;not null;default:0;comment:版本号(乐观锁)" json:"version"`
}
// TableName 指定表名
func (Wallet) TableName() string {
return "tb_wallet"
}
// WalletMetadata 钱包交易扩展信息
// 用于存储交易相关的额外数据JSONB格式
type WalletMetadata map[string]interface{}
// Value 实现 driver.Valuer 接口
func (m WalletMetadata) Value() (driver.Value, error) {
return json.Marshal(m)
}
// Scan 实现 sql.Scanner 接口
func (m *WalletMetadata) Scan(value interface{}) error {
if value == nil {
*m = make(WalletMetadata)
return nil
}
bytes, ok := value.([]byte)
if !ok {
return nil
}
return json.Unmarshal(bytes, m)
}
// WalletTransaction 钱包交易记录模型
// 记录所有钱包余额变动,包含变动前后余额用于对账
// 支持关联业务对象(订单、分佣、提现等)
type WalletTransaction struct {
gorm.Model
WalletID uint `gorm:"column:wallet_id;not null;index:idx_wallet_tx_wallet;comment:钱包ID" json:"wallet_id"`
UserID uint `gorm:"column:user_id;not null;index:idx_wallet_tx_user;comment:用户ID" json:"user_id"`
TransactionType string `gorm:"column:transaction_type;type:varchar(20);not null;comment:交易类型 recharge-充值 deduct-扣款 refund-退款 commission-分佣 withdrawal-提现" json:"transaction_type"`
Amount int64 `gorm:"column:amount;type:bigint;not null;comment:变动金额(分)正数为增加 负数为减少" json:"amount"`
BalanceBefore int64 `gorm:"column:balance_before;type:bigint;not null;comment:变动前余额(分)" json:"balance_before"`
BalanceAfter int64 `gorm:"column:balance_after;type:bigint;not null;comment:变动后余额(分)" json:"balance_after"`
Status int `gorm:"column:status;type:int;not null;default:1;comment:交易状态 1-成功 2-失败 3-处理中" json:"status"`
ReferenceType *string `gorm:"column:reference_type;type:varchar(50);index:idx_wallet_tx_ref,priority:1;comment:关联业务类型 order/commission/withdrawal/topup" json:"reference_type,omitempty"`
ReferenceID *uint `gorm:"column:reference_id;type:bigint;index:idx_wallet_tx_ref,priority:2;comment:关联业务ID" json:"reference_id,omitempty"`
Remark *string `gorm:"column:remark;type:text;comment:备注" json:"remark,omitempty"`
Metadata WalletMetadata `gorm:"column:metadata;type:jsonb;comment:扩展信息" json:"metadata,omitempty"`
Creator uint `gorm:"column:creator;comment:创建人ID" json:"creator"`
}
// TableName 指定表名
func (WalletTransaction) TableName() string {
return "tb_wallet_transaction"
}
// RechargeRecord 充值记录模型
// 用户和代理的钱包充值订单,记录支付流程和状态
type RechargeRecord struct {
gorm.Model
BaseModel `gorm:"embedded"`
UserID uint `gorm:"column:user_id;not null;index:idx_recharge_user;comment:用户ID" json:"user_id"`
WalletID uint `gorm:"column:wallet_id;not null;comment:钱包ID" json:"wallet_id"`
RechargeNo string `gorm:"column:recharge_no;type:varchar(50);not null;uniqueIndex:idx_recharge_no;comment:充值订单号" json:"recharge_no"`
Amount int64 `gorm:"column:amount;type:bigint;not null;comment:充值金额(分)" json:"amount"`
PaymentMethod string `gorm:"column:payment_method;type:varchar(20);not null;comment:支付方式 alipay-支付宝 wechat-微信 bank-银行转账 offline-线下" json:"payment_method"`
PaymentChannel *string `gorm:"column:payment_channel;type:varchar(50);comment:支付渠道" json:"payment_channel,omitempty"`
PaymentTransactionID *string `gorm:"column:payment_transaction_id;type:varchar(100);comment:第三方支付交易号" json:"payment_transaction_id,omitempty"`
Status int `gorm:"column:status;type:int;not null;default:1;index:idx_recharge_status;comment:充值状态 1-待支付 2-已支付 3-已完成 4-已关闭 5-已退款" json:"status"`
PaidAt *time.Time `gorm:"column:paid_at;comment:支付时间" json:"paid_at,omitempty"`
CompletedAt *time.Time `gorm:"column:completed_at;comment:完成时间" json:"completed_at,omitempty"`
}
// TableName 指定表名
func (RechargeRecord) TableName() string {
return "tb_recharge_record"
}