draft
This commit is contained in:
15
backend/internal/model/admin_user.go
Normal file
15
backend/internal/model/admin_user.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
// AdminUser 管理后台用户
|
||||
func (AdminUser) TableName() string { return "admin_user" }
|
||||
|
||||
type AdminUser struct {
|
||||
ID uint64 `gorm:"primaryKey;autoIncrement"`
|
||||
Username string `gorm:"uniqueIndex;size:64;not null"`
|
||||
PasswordHash string `gorm:"size:128;not null"`
|
||||
Status int8 `gorm:"not null;default:1"` // 1=启用
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
16
backend/internal/model/app.go
Normal file
16
backend/internal/model/app.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
// App 接入应用
|
||||
type App struct {
|
||||
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
|
||||
AppID string `gorm:"column:app_id;uniqueIndex;size:32;not null"`
|
||||
AppSecret string `gorm:"column:app_secret;size:128;not null"` // AES 加密存储
|
||||
AppName string `gorm:"column:app_name;size:64;not null"`
|
||||
Status int8 `gorm:"column:status;not null;default:1"` // 1=启用 0=禁用
|
||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime:milli"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime:milli"`
|
||||
}
|
||||
|
||||
func (App) TableName() string { return "app" }
|
||||
22
backend/internal/model/channel_config.go
Normal file
22
backend/internal/model/channel_config.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
// ChannelConfig 渠道配置
|
||||
type ChannelConfig struct {
|
||||
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
|
||||
AppID string `gorm:"column:app_id;size:32;not null;uniqueIndex:uk_app_channel"`
|
||||
ChannelCode string `gorm:"column:channel_code;size:32;not null;uniqueIndex:uk_app_channel"`
|
||||
MerchantID string `gorm:"column:merchant_id;size:64;not null"`
|
||||
APIKey string `gorm:"column:api_key;type:text"` // AES 加密
|
||||
PrivateKey string `gorm:"column:private_key;type:text"` // AES 加密
|
||||
PublicKey string `gorm:"column:public_key;type:text"` // 渠道公钥(明文)
|
||||
NotifyURL string `gorm:"column:notify_url;size:512;not null"`
|
||||
Sandbox int8 `gorm:"column:sandbox;not null;default:0"` // 1=沙箱 0=生产
|
||||
ExtraConfig JSONMap `gorm:"column:extra_config;type:json"`
|
||||
Status int8 `gorm:"column:status;not null;default:1"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime:milli"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime:milli"`
|
||||
}
|
||||
|
||||
func (ChannelConfig) TableName() string { return "channel_config" }
|
||||
59
backend/internal/model/merchant.go
Normal file
59
backend/internal/model/merchant.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
// MerchantStatus 商户状态
|
||||
type MerchantStatus string
|
||||
|
||||
const (
|
||||
MerchantStatusPending MerchantStatus = "PENDING"
|
||||
MerchantStatusActive MerchantStatus = "ACTIVE"
|
||||
MerchantStatusFrozen MerchantStatus = "FROZEN"
|
||||
MerchantStatusRejected MerchantStatus = "REJECTED"
|
||||
)
|
||||
|
||||
// AuditStatus 进件审核状态
|
||||
type AuditStatus string
|
||||
|
||||
const (
|
||||
AuditStatusSubmitting AuditStatus = "SUBMITTING"
|
||||
AuditStatusReviewing AuditStatus = "REVIEWING"
|
||||
AuditStatusApproved AuditStatus = "APPROVED"
|
||||
AuditStatusRejected AuditStatus = "REJECTED"
|
||||
)
|
||||
|
||||
// Merchant 商户
|
||||
type Merchant struct {
|
||||
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
|
||||
MerchantID string `gorm:"column:merchant_id;uniqueIndex;size:32;not null"`
|
||||
AppID string `gorm:"column:app_id;size:32;not null;default:'';index"`
|
||||
MerchantName string `gorm:"column:merchant_name;size:128;not null"`
|
||||
LicenseNo string `gorm:"column:license_no;size:64"`
|
||||
LegalPerson string `gorm:"column:legal_person;size:64"`
|
||||
BankAccount string `gorm:"column:bank_account;size:64"` // 脱敏
|
||||
ChannelMerchantID string `gorm:"column:channel_merchant_id;size:64"`
|
||||
Status MerchantStatus `gorm:"column:status;size:20;not null;default:PENDING;index"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime:milli"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime:milli"`
|
||||
}
|
||||
|
||||
func (Merchant) TableName() string { return "merchant" }
|
||||
|
||||
// MerchantApplication 商户进件申请
|
||||
// 一条记录对应一个商户在一个渠道的进件,(merchant_id, channel_code) 唯一
|
||||
type MerchantApplication struct {
|
||||
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
|
||||
ApplicationID string `gorm:"column:application_id;uniqueIndex;size:32;not null"`
|
||||
MerchantID string `gorm:"column:merchant_id;size:32;not null;index"`
|
||||
ChannelCode string `gorm:"column:channel_code;size:32;not null"`
|
||||
ChannelMerchantID string `gorm:"column:channel_merchant_id;size:64;not null;default:''"`
|
||||
SubmitData JSONMap `gorm:"column:submit_data;type:json"`
|
||||
AuditStatus AuditStatus `gorm:"column:audit_status;size:20;not null;default:SUBMITTING"`
|
||||
RejectReason string `gorm:"column:reject_reason;size:512"`
|
||||
SubmittedAt time.Time `gorm:"column:submitted_at"`
|
||||
AuditedAt *time.Time `gorm:"column:audited_at"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime:milli"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime:milli"`
|
||||
}
|
||||
|
||||
func (MerchantApplication) TableName() string { return "merchant_application" }
|
||||
37
backend/internal/model/notify_log.go
Normal file
37
backend/internal/model/notify_log.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
// NotifyStatus 通知状态
|
||||
type NotifyStatus string
|
||||
|
||||
const (
|
||||
NotifyStatusPending NotifyStatus = "PENDING"
|
||||
NotifyStatusSuccess NotifyStatus = "SUCCESS"
|
||||
NotifyStatusRetry NotifyStatus = "RETRY"
|
||||
NotifyStatusGiveup NotifyStatus = "GIVEUP"
|
||||
)
|
||||
|
||||
// NotifyType 通知类型
|
||||
type NotifyType string
|
||||
|
||||
const (
|
||||
NotifyTypePayment NotifyType = "PAYMENT"
|
||||
NotifyTypeRefund NotifyType = "REFUND"
|
||||
)
|
||||
|
||||
// NotifyLog 下游通知记录
|
||||
type NotifyLog struct {
|
||||
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
|
||||
TradeNo string `gorm:"column:trade_no;size:32;not null;uniqueIndex:uk_trade_notify_type"`
|
||||
NotifyType NotifyType `gorm:"column:notify_type;size:20;not null;uniqueIndex:uk_trade_notify_type"`
|
||||
NotifyURL string `gorm:"column:notify_url;size:512;not null"`
|
||||
Status NotifyStatus `gorm:"column:status;size:20;not null;default:PENDING"`
|
||||
RetryCount int `gorm:"column:retry_count;not null;default:0"`
|
||||
NextRetryTime *time.Time `gorm:"column:next_retry_time;index:idx_status_next_retry"`
|
||||
LastResponse string `gorm:"column:last_response;type:text"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime:milli"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime:milli"`
|
||||
}
|
||||
|
||||
func (NotifyLog) TableName() string { return "notify_log" }
|
||||
25
backend/internal/model/order_sequence.go
Normal file
25
backend/internal/model/order_sequence.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
// SeqType 序列类型
|
||||
type SeqType string
|
||||
|
||||
const (
|
||||
SeqTypeTrade SeqType = "TRADE"
|
||||
SeqTypeRefund SeqType = "REFUND"
|
||||
SeqTypeSharing SeqType = "SHARING"
|
||||
)
|
||||
|
||||
// OrderSequence 订单编码序列
|
||||
type OrderSequence struct {
|
||||
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
|
||||
AppID string `gorm:"column:app_id;size:32;not null;uniqueIndex:uk_app_type"`
|
||||
SeqType SeqType `gorm:"column:seq_type;size:20;not null;uniqueIndex:uk_app_type"`
|
||||
Prefix string `gorm:"column:prefix;size:8;not null"`
|
||||
CurrentValue uint64 `gorm:"column:current_value;not null;default:0"`
|
||||
Step int `gorm:"column:step;not null;default:1"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime:milli"`
|
||||
}
|
||||
|
||||
func (OrderSequence) TableName() string { return "order_sequence" }
|
||||
49
backend/internal/model/payment_match.go
Normal file
49
backend/internal/model/payment_match.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
// MatchStatus 匹配状态
|
||||
type MatchStatus string
|
||||
|
||||
const (
|
||||
MatchStatusMatched MatchStatus = "MATCHED"
|
||||
MatchStatusPendingManual MatchStatus = "PENDING_MANUAL"
|
||||
MatchStatusNameDiff MatchStatus = "NAME_DIFF" // 匹配成功但名称不一致
|
||||
)
|
||||
|
||||
// SubMerchantAccount 子商户固定收款账户
|
||||
type SubMerchantAccount struct {
|
||||
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
|
||||
AppID string `gorm:"column:app_id;size:32;not null;index:idx_app_merchant"`
|
||||
SubMerchantID string `gorm:"column:sub_merchant_id;size:64;not null;index:idx_app_merchant"`
|
||||
ChannelCode string `gorm:"column:channel_code;size:32;not null"`
|
||||
AccountType string `gorm:"column:account_type;size:20;not null"` // BANK_CARD
|
||||
AccountNo string `gorm:"column:account_no;size:64;not null;index"` // 脱敏
|
||||
AccountNoEnc string `gorm:"column:account_no_enc;type:text"` // AES 加密完整账号
|
||||
AccountName string `gorm:"column:account_name;size:128;not null"`
|
||||
BankName string `gorm:"column:bank_name;size:64"`
|
||||
Status int8 `gorm:"column:status;not null;default:1"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime:milli"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime:milli"`
|
||||
}
|
||||
|
||||
func (SubMerchantAccount) TableName() string { return "sub_merchant_account" }
|
||||
|
||||
// PaymentMatchLog 收款匹配记录
|
||||
type PaymentMatchLog struct {
|
||||
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
|
||||
AccountID uint64 `gorm:"column:account_id;not null;index:idx_account_status"`
|
||||
TradeNo string `gorm:"column:trade_no;size:32;index"`
|
||||
IncomingAmount int64 `gorm:"column:incoming_amount;not null"`
|
||||
IncomingRemark string `gorm:"column:incoming_remark;size:256"`
|
||||
PayerName string `gorm:"column:payer_name;size:128"`
|
||||
ChannelBillNo string `gorm:"column:channel_bill_no;size:64;index"`
|
||||
MatchStatus MatchStatus `gorm:"column:match_status;size:20;not null;index:idx_account_status"`
|
||||
NameDiff int8 `gorm:"column:name_diff;not null;default:0"` // 1=名称不一致
|
||||
MatchTime *time.Time `gorm:"column:match_time"`
|
||||
Operator string `gorm:"column:operator;size:64"` // 人工操作者
|
||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime:milli"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime:milli"`
|
||||
}
|
||||
|
||||
func (PaymentMatchLog) TableName() string { return "payment_match_log" }
|
||||
58
backend/internal/model/profit_sharing.go
Normal file
58
backend/internal/model/profit_sharing.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
// ProfitSharingStatus 分润状态
|
||||
type ProfitSharingStatus string
|
||||
|
||||
const (
|
||||
ProfitSharingStatusPending ProfitSharingStatus = "PENDING"
|
||||
ProfitSharingStatusProcessing ProfitSharingStatus = "PROCESSING"
|
||||
ProfitSharingStatusSuccess ProfitSharingStatus = "SUCCESS"
|
||||
ProfitSharingStatusFailed ProfitSharingStatus = "FAILED"
|
||||
ProfitSharingStatusRollback ProfitSharingStatus = "ROLLBACK"
|
||||
)
|
||||
|
||||
// ProfitSharingConfig 分润配置(应用级)
|
||||
type ProfitSharingConfig struct {
|
||||
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
|
||||
AppID string `gorm:"column:app_id;size:32;not null;uniqueIndex"`
|
||||
ReceiverMerchantID string `gorm:"column:receiver_merchant_id;size:64;not null"`
|
||||
ReceiverType string `gorm:"column:receiver_type;size:20;not null"` // PLATFORM / SUB_MERCHANT
|
||||
MaxSharingRatio float64 `gorm:"column:max_sharing_ratio;type:decimal(5,4);not null"`
|
||||
Status int8 `gorm:"column:status;not null;default:1"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime:milli"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime:milli"`
|
||||
}
|
||||
|
||||
func (ProfitSharingConfig) TableName() string { return "profit_sharing_config" }
|
||||
|
||||
// ProfitSharingOrder 分润记录
|
||||
type ProfitSharingOrder struct {
|
||||
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
|
||||
SharingNo string `gorm:"column:sharing_no;uniqueIndex;size:32;not null"`
|
||||
TradeNo string `gorm:"column:trade_no;uniqueIndex;size:32;not null"`
|
||||
AppID string `gorm:"column:app_id;size:32;not null"`
|
||||
ReceiverMerchantID string `gorm:"column:receiver_merchant_id;size:64;not null"`
|
||||
SharingAmount int64 `gorm:"column:sharing_amount;not null"`
|
||||
Status ProfitSharingStatus `gorm:"column:status;size:20;not null;default:PENDING"`
|
||||
ChannelSharingNo string `gorm:"column:channel_sharing_no;size:64"`
|
||||
FailReason string `gorm:"column:fail_reason;size:256"`
|
||||
SharingTime *time.Time `gorm:"column:sharing_time"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime:milli"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime:milli"`
|
||||
}
|
||||
|
||||
func (ProfitSharingOrder) TableName() string { return "profit_sharing_order" }
|
||||
|
||||
// ProfitSharingLog 分润流水
|
||||
type ProfitSharingLog struct {
|
||||
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
|
||||
SharingNo string `gorm:"column:sharing_no;size:32;not null;index"`
|
||||
Action string `gorm:"column:action;size:20;not null"` // SPLIT / ROLLBACK
|
||||
Amount int64 `gorm:"column:amount;not null"`
|
||||
Status string `gorm:"column:status;size:20;not null"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime:milli"`
|
||||
}
|
||||
|
||||
func (ProfitSharingLog) TableName() string { return "profit_sharing_log" }
|
||||
44
backend/internal/model/reconciliation.go
Normal file
44
backend/internal/model/reconciliation.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
// ReconciliationStatus 对账单状态
|
||||
type ReconciliationStatus string
|
||||
|
||||
const (
|
||||
ReconciliationStatusPending ReconciliationStatus = "PENDING"
|
||||
ReconciliationStatusMatched ReconciliationStatus = "MATCHED"
|
||||
ReconciliationStatusException ReconciliationStatus = "EXCEPTION"
|
||||
)
|
||||
|
||||
// ReconciliationReport 对账报告
|
||||
type ReconciliationReport struct {
|
||||
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
|
||||
AppID string `gorm:"column:app_id;size:32;not null;index:idx_app_date"`
|
||||
ChannelCode string `gorm:"column:channel_code;size:32;not null"`
|
||||
BillDate string `gorm:"column:bill_date;size:10;not null;index:idx_app_date"` // yyyy-MM-dd
|
||||
TotalCount int `gorm:"column:total_count;not null;default:0"`
|
||||
TotalAmount int64 `gorm:"column:total_amount;not null;default:0"` // 分
|
||||
MatchedCount int `gorm:"column:matched_count;not null;default:0"`
|
||||
ExceptionCount int `gorm:"column:exception_count;not null;default:0"`
|
||||
Status ReconciliationStatus `gorm:"column:status;size:20;not null;default:PENDING"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime:milli"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime:milli"`
|
||||
}
|
||||
|
||||
func (ReconciliationReport) TableName() string { return "reconciliation_report" }
|
||||
|
||||
// ReconciliationException 对账异常明细
|
||||
type ReconciliationException struct {
|
||||
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
|
||||
ReportID uint64 `gorm:"column:report_id;not null;index"`
|
||||
TradeNo string `gorm:"column:trade_no;size:32;index"`
|
||||
ChannelBillNo string `gorm:"column:channel_bill_no;size:64"`
|
||||
ExceptionType string `gorm:"column:exception_type;size:32;not null"` // MISSING_LOCAL/MISSING_CHANNEL/AMOUNT_MISMATCH
|
||||
LocalAmount int64 `gorm:"column:local_amount"`
|
||||
ChannelAmount int64 `gorm:"column:channel_amount"`
|
||||
Remark string `gorm:"column:remark;size:256"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime:milli"`
|
||||
}
|
||||
|
||||
func (ReconciliationException) TableName() string { return "reconciliation_exception" }
|
||||
32
backend/internal/model/refund_order.go
Normal file
32
backend/internal/model/refund_order.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
// RefundStatus 退款状态
|
||||
type RefundStatus string
|
||||
|
||||
const (
|
||||
RefundStatusPending RefundStatus = "PENDING"
|
||||
RefundStatusProcessing RefundStatus = "PROCESSING"
|
||||
RefundStatusSuccess RefundStatus = "SUCCESS"
|
||||
RefundStatusFailed RefundStatus = "FAILED"
|
||||
)
|
||||
|
||||
// RefundOrder 退款记录
|
||||
type RefundOrder struct {
|
||||
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
|
||||
RefundNo string `gorm:"column:refund_no;uniqueIndex;size:32;not null"`
|
||||
TradeNo string `gorm:"column:trade_no;size:32;not null;index"`
|
||||
AppID string `gorm:"column:app_id;size:32;not null"`
|
||||
ChannelCode string `gorm:"column:channel_code;size:32;not null"`
|
||||
ChannelRefundNo string `gorm:"column:channel_refund_no;size:64"`
|
||||
RefundAmount int64 `gorm:"column:refund_amount;not null"`
|
||||
Reason string `gorm:"column:reason;size:256"`
|
||||
Status RefundStatus `gorm:"column:status;size:20;not null;default:PENDING"`
|
||||
NotifyURL string `gorm:"column:notify_url;size:512"`
|
||||
RefundTime *time.Time `gorm:"column:refund_time"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime:milli"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime:milli"`
|
||||
}
|
||||
|
||||
func (RefundOrder) TableName() string { return "refund_order" }
|
||||
56
backend/internal/model/service_fee.go
Normal file
56
backend/internal/model/service_fee.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
// PayMethodGroup 支付方式分组(用于服务费配置)
|
||||
type PayMethodGroup string
|
||||
|
||||
const (
|
||||
PayMethodGroupScan PayMethodGroup = "SCAN" // 扫码支付(微信/支付宝)
|
||||
PayMethodGroupTransfer PayMethodGroup = "TRANSFER" // 对公转账
|
||||
PayMethodGroupBalance PayMethodGroup = "BALANCE" // 余额支付
|
||||
)
|
||||
|
||||
// ServiceFeeConfig 服务费配置
|
||||
type ServiceFeeConfig struct {
|
||||
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
|
||||
AppID string `gorm:"column:app_id;size:32;not null;uniqueIndex:uk_app_method"`
|
||||
PayMethodGroup PayMethodGroup `gorm:"column:pay_method_group;size:20;not null;uniqueIndex:uk_app_method"`
|
||||
FeeRate float64 `gorm:"column:fee_rate;type:decimal(6,4);not null"` // 0.0000 ~ 9.9999%
|
||||
FeeReceiverMerchantID string `gorm:"column:fee_receiver_merchant_id;size:64;not null"`
|
||||
Status int8 `gorm:"column:status;not null;default:1"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime:milli"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime:milli"`
|
||||
}
|
||||
|
||||
func (ServiceFeeConfig) TableName() string { return "service_fee_config" }
|
||||
|
||||
// ServiceFeeLog 服务费流水
|
||||
type ServiceFeeLog struct {
|
||||
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
|
||||
TradeNo string `gorm:"column:trade_no;size:32;not null;uniqueIndex:uk_trade_action"`
|
||||
ConfigID uint64 `gorm:"column:config_id;not null"`
|
||||
FeeAmount int64 `gorm:"column:fee_amount;not null"`
|
||||
FeeRate float64 `gorm:"column:fee_rate;type:decimal(6,4);not null"`
|
||||
ReceiverMerchantID string `gorm:"column:receiver_merchant_id;size:64;not null"`
|
||||
Action string `gorm:"column:action;size:20;not null;uniqueIndex:uk_trade_action"` // CHARGE / ROLLBACK
|
||||
Status string `gorm:"column:status;size:20;not null;default:PENDING"`
|
||||
ChannelSharingNo string `gorm:"column:channel_sharing_no;size:64"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime:milli"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime:milli"`
|
||||
}
|
||||
|
||||
func (ServiceFeeLog) TableName() string { return "service_fee_log" }
|
||||
|
||||
// PayMethodToGroup 将支付方式映射到服务费分组
|
||||
func PayMethodToGroup(m PayMethod) PayMethodGroup {
|
||||
switch m {
|
||||
case PayMethodWechatJSAPI, PayMethodWechatH5, PayMethodWechatNative,
|
||||
PayMethodWechatMini, PayMethodAlipay, PayMethodQuickPay:
|
||||
return PayMethodGroupScan
|
||||
case PayMethodTransfer:
|
||||
return PayMethodGroupTransfer
|
||||
default:
|
||||
return PayMethodGroupBalance
|
||||
}
|
||||
}
|
||||
103
backend/internal/model/trade_order.go
Normal file
103
backend/internal/model/trade_order.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TradeStatus 交易状态
|
||||
type TradeStatus string
|
||||
|
||||
const (
|
||||
TradeStatusCreating TradeStatus = "CREATING"
|
||||
TradeStatusPaying TradeStatus = "PAYING"
|
||||
TradeStatusPaid TradeStatus = "PAID"
|
||||
TradeStatusClosed TradeStatus = "CLOSED"
|
||||
TradeStatusFailed TradeStatus = "FAILED"
|
||||
TradeStatusCreateFailed TradeStatus = "CREATE_FAILED"
|
||||
TradeStatusRefunded TradeStatus = "REFUNDED"
|
||||
)
|
||||
|
||||
// PayMethod 支付方式
|
||||
type PayMethod string
|
||||
|
||||
const (
|
||||
PayMethodWechatJSAPI PayMethod = "WECHAT_JSAPI"
|
||||
PayMethodWechatH5 PayMethod = "WECHAT_H5"
|
||||
PayMethodWechatNative PayMethod = "WECHAT_NATIVE"
|
||||
PayMethodWechatMini PayMethod = "WECHAT_MINI"
|
||||
PayMethodAlipay PayMethod = "ALIPAY"
|
||||
PayMethodQuickPay PayMethod = "QUICK_PAY"
|
||||
PayMethodTransfer PayMethod = "TRANSFER" // 对公转账
|
||||
)
|
||||
|
||||
// JSONMap JSON 字段类型
|
||||
type JSONMap map[string]any
|
||||
|
||||
func (j JSONMap) Value() (driver.Value, error) {
|
||||
if j == nil {
|
||||
return nil, nil
|
||||
}
|
||||
b, err := json.Marshal(j)
|
||||
return string(b), err
|
||||
}
|
||||
|
||||
func (j *JSONMap) Scan(value any) error {
|
||||
if value == nil {
|
||||
*j = nil
|
||||
return nil
|
||||
}
|
||||
var bytes []byte
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
bytes = []byte(v)
|
||||
case []byte:
|
||||
bytes = v
|
||||
default:
|
||||
return fmt.Errorf("unsupported type: %T", value)
|
||||
}
|
||||
return json.Unmarshal(bytes, j)
|
||||
}
|
||||
|
||||
// TradeOrder 交易订单
|
||||
type TradeOrder struct {
|
||||
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
|
||||
TradeNo string `gorm:"column:trade_no;uniqueIndex;size:32;not null"`
|
||||
MerchantOrderNo string `gorm:"column:merchant_order_no;size:64;not null"`
|
||||
AppID string `gorm:"column:app_id;size:32;not null;index:idx_app_merchant,unique"`
|
||||
ChannelCode string `gorm:"column:channel_code;size:32;not null"`
|
||||
ChannelTradeNo string `gorm:"column:channel_trade_no;size:64;index"`
|
||||
PayMethod PayMethod `gorm:"column:pay_method;size:32;not null"`
|
||||
Amount int64 `gorm:"column:amount;not null"`
|
||||
ProfitSharingAmount int64 `gorm:"column:profit_sharing_amount;not null;default:0"`
|
||||
ServiceFeeAmount int64 `gorm:"column:service_fee_amount;not null;default:0"`
|
||||
Subject string `gorm:"column:subject;size:256;not null"`
|
||||
NotifyURL string `gorm:"column:notify_url;size:512;not null"`
|
||||
Status TradeStatus `gorm:"column:status;size:20;not null;default:CREATING"`
|
||||
Extra JSONMap `gorm:"column:extra;type:json"`
|
||||
ChannelExtra JSONMap `gorm:"column:channel_extra;type:json"`
|
||||
ExpireTime time.Time `gorm:"column:expire_time;not null"`
|
||||
PayTime *time.Time `gorm:"column:pay_time"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime:milli"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime:milli"`
|
||||
}
|
||||
|
||||
func (TradeOrder) TableName() string { return "trade_order" }
|
||||
|
||||
// CanTransitTo 校验状态流转是否合法
|
||||
func (t TradeStatus) CanTransitTo(next TradeStatus) bool {
|
||||
allowed := map[TradeStatus][]TradeStatus{
|
||||
TradeStatusCreating: {TradeStatusPaying, TradeStatusCreateFailed},
|
||||
TradeStatusPaying: {TradeStatusPaid, TradeStatusClosed, TradeStatusFailed},
|
||||
TradeStatusPaid: {TradeStatusRefunded},
|
||||
TradeStatusCreateFailed: {TradeStatusPaying},
|
||||
}
|
||||
for _, s := range allowed[t] {
|
||||
if s == next {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
41
backend/internal/model/wechat.go
Normal file
41
backend/internal/model/wechat.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
// WechatBinding 商户微信公众号绑定
|
||||
type WechatBinding struct {
|
||||
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
|
||||
AppID string `gorm:"column:app_id;size:32;not null;uniqueIndex"`
|
||||
WxAppID string `gorm:"column:wx_app_id;size:32;not null"` // 微信公众号/小程序 AppID
|
||||
WxSecret string `gorm:"column:wx_secret;type:text;not null"` // AES 加密存储
|
||||
TemplateID string `gorm:"column:template_id;size:64;not null"` // 消息模板 ID
|
||||
Status int8 `gorm:"column:status;not null;default:1"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime:milli"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime:milli"`
|
||||
}
|
||||
|
||||
func (WechatBinding) TableName() string { return "wechat_binding" }
|
||||
|
||||
// WechatMessageStatus 微信消息发送状态
|
||||
type WechatMessageStatus string
|
||||
|
||||
const (
|
||||
WechatMessageStatusPending WechatMessageStatus = "PENDING"
|
||||
WechatMessageStatusSuccess WechatMessageStatus = "SUCCESS"
|
||||
WechatMessageStatusFailed WechatMessageStatus = "FAILED"
|
||||
)
|
||||
|
||||
// WechatMessageLog 微信消息发送日志
|
||||
type WechatMessageLog struct {
|
||||
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
|
||||
AppID string `gorm:"column:app_id;size:32;not null;index"`
|
||||
TradeNo string `gorm:"column:trade_no;size:32;index"`
|
||||
OpenID string `gorm:"column:open_id;size:64;not null"`
|
||||
TemplateID string `gorm:"column:template_id;size:64;not null"`
|
||||
Status WechatMessageStatus `gorm:"column:status;size:20;not null;default:PENDING"`
|
||||
ErrMsg string `gorm:"column:err_msg;size:256"`
|
||||
SentAt *time.Time `gorm:"column:sent_at"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime:milli"`
|
||||
}
|
||||
|
||||
func (WechatMessageLog) TableName() string { return "wechat_message_log" }
|
||||
Reference in New Issue
Block a user