fix(supply-api): 修复编译错误和测试问题
- 添加 ErrNotFound 和 ErrConcurrencyConflict 错误定义 - 修复 pgx.NullTime 替换为 *time.Time - 修复 db.go 事务类型 (pgx.Tx vs pgxpool.Tx) - 移除未使用的导入和变量 - 修复 NewSupplyAPI 调用参数 - 修复中间件链路 handler 类型问题 - 修复适配器类型引用 (storage.InMemoryAccountStore 等) - 所有测试通过 Test: go test ./...
This commit is contained in:
@@ -42,48 +42,48 @@ const (
|
||||
|
||||
// 账号
|
||||
type Account struct {
|
||||
ID int64 `json:"account_id"`
|
||||
SupplierID int64 `json:"supplier_id"`
|
||||
Provider Provider `json:"provider"`
|
||||
AccountType AccountType `json:"account_type"`
|
||||
CredentialHash string `json:"-"` // 不暴露
|
||||
KeyID string `json:"key_id,omitempty"` // 不暴露
|
||||
Alias string `json:"account_alias,omitempty"`
|
||||
Status AccountStatus `json:"status"`
|
||||
RiskLevel string `json:"risk_level"`
|
||||
TotalQuota float64 `json:"total_quota,omitempty"`
|
||||
AvailableQuota float64 `json:"available_quota,omitempty"`
|
||||
FrozenQuota float64 `json:"frozen_quota,omitempty"`
|
||||
IsVerified bool `json:"is_verified"`
|
||||
VerifiedAt *time.Time `json:"verified_at,omitempty"`
|
||||
LastCheckAt *time.Time `json:"last_check_at,omitempty"`
|
||||
TosCompliant bool `json:"tos_compliant"`
|
||||
TosCheckResult string `json:"tos_check_result,omitempty"`
|
||||
TotalRequests int64 `json:"total_requests"`
|
||||
TotalTokens int64 `json:"total_tokens"`
|
||||
TotalCost float64 `json:"total_cost"`
|
||||
SuccessRate float64 `json:"success_rate"`
|
||||
RiskScore int `json:"risk_score"`
|
||||
RiskReason string `json:"risk_reason,omitempty"`
|
||||
IsFrozen bool `json:"is_frozen"`
|
||||
FrozenReason string `json:"frozen_reason,omitempty"`
|
||||
ID int64 `json:"account_id"`
|
||||
SupplierID int64 `json:"supplier_id"`
|
||||
Provider Provider `json:"provider"`
|
||||
AccountType AccountType `json:"account_type"`
|
||||
CredentialHash string `json:"-"` // 不暴露
|
||||
KeyID string `json:"key_id,omitempty"` // 不暴露
|
||||
Alias string `json:"account_alias,omitempty"`
|
||||
Status AccountStatus `json:"status"`
|
||||
RiskLevel string `json:"risk_level"`
|
||||
TotalQuota float64 `json:"total_quota,omitempty"`
|
||||
AvailableQuota float64 `json:"available_quota,omitempty"`
|
||||
FrozenQuota float64 `json:"frozen_quota,omitempty"`
|
||||
IsVerified bool `json:"is_verified"`
|
||||
VerifiedAt *time.Time `json:"verified_at,omitempty"`
|
||||
LastCheckAt *time.Time `json:"last_check_at,omitempty"`
|
||||
TosCompliant bool `json:"tos_compliant"`
|
||||
TosCheckResult string `json:"tos_check_result,omitempty"`
|
||||
TotalRequests int64 `json:"total_requests"`
|
||||
TotalTokens int64 `json:"total_tokens"`
|
||||
TotalCost float64 `json:"total_cost"`
|
||||
SuccessRate float64 `json:"success_rate"`
|
||||
RiskScore int `json:"risk_score"`
|
||||
RiskReason string `json:"risk_reason,omitempty"`
|
||||
IsFrozen bool `json:"is_frozen"`
|
||||
FrozenReason string `json:"frozen_reason,omitempty"`
|
||||
|
||||
// 加密元数据字段 (XR-001)
|
||||
CredentialCipherAlgo string `json:"credential_cipher_algo,omitempty"`
|
||||
CredentialKMSKeyAlias string `json:"credential_kms_key_alias,omitempty"`
|
||||
CredentialKeyVersion int `json:"credential_key_version,omitempty"`
|
||||
CredentialFingerprint string `json:"credential_fingerprint,omitempty"`
|
||||
LastRotationAt *time.Time `json:"last_rotation_at,omitempty"`
|
||||
CredentialCipherAlgo string `json:"credential_cipher_algo,omitempty"`
|
||||
CredentialKMSKeyAlias string `json:"credential_kms_key_alias,omitempty"`
|
||||
CredentialKeyVersion int `json:"credential_key_version,omitempty"`
|
||||
CredentialFingerprint string `json:"credential_fingerprint,omitempty"`
|
||||
LastRotationAt *time.Time `json:"last_rotation_at,omitempty"`
|
||||
|
||||
// 单位与币种 (XR-001)
|
||||
QuotaUnit string `json:"quota_unit"`
|
||||
CurrencyCode string `json:"currency_code"`
|
||||
|
||||
// 审计字段 (XR-001)
|
||||
Version int `json:"version"`
|
||||
Version int `json:"version"`
|
||||
CreatedIP *netip.Addr `json:"created_ip,omitempty"`
|
||||
UpdatedIP *netip.Addr `json:"updated_ip,omitempty"`
|
||||
AuditTraceID string `json:"audit_trace_id,omitempty"`
|
||||
AuditTraceID string `json:"audit_trace_id,omitempty"`
|
||||
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
@@ -91,10 +91,10 @@ type Account struct {
|
||||
|
||||
// 验证结果
|
||||
type VerifyResult struct {
|
||||
VerifyStatus string `json:"verify_status"` // pass, review_required, reject
|
||||
AvailableQuota float64 `json:"available_quota,omitempty"`
|
||||
RiskScore int `json:"risk_score"`
|
||||
CheckItems []CheckItem `json:"check_items,omitempty"`
|
||||
VerifyStatus string `json:"verify_status"` // pass, review_required, reject
|
||||
AvailableQuota float64 `json:"available_quota,omitempty"`
|
||||
RiskScore int `json:"risk_score"`
|
||||
CheckItems []CheckItem `json:"check_items,omitempty"`
|
||||
}
|
||||
|
||||
type CheckItem struct {
|
||||
@@ -115,12 +115,12 @@ type AccountService interface {
|
||||
|
||||
// 创建账号请求
|
||||
type CreateAccountRequest struct {
|
||||
SupplierID int64
|
||||
Provider Provider
|
||||
AccountType AccountType
|
||||
Credential string
|
||||
Alias string
|
||||
RiskAck bool
|
||||
SupplierID int64
|
||||
Provider Provider
|
||||
AccountType AccountType
|
||||
Credential string
|
||||
Alias string
|
||||
RiskAck bool
|
||||
}
|
||||
|
||||
// 账号仓储接口
|
||||
@@ -133,7 +133,7 @@ type AccountStore interface {
|
||||
|
||||
// 账号服务实现
|
||||
type accountService struct {
|
||||
store AccountStore
|
||||
store AccountStore
|
||||
auditStore audit.AuditStore
|
||||
}
|
||||
|
||||
|
||||
@@ -191,9 +191,9 @@ func ValidateStateTransition(from, to AccountStatus) bool {
|
||||
// ValidatePackageStateTransition 验证套餐状态转换
|
||||
func ValidatePackageStateTransition(from, to PackageStatus) bool {
|
||||
validTransitions := map[PackageStatus][]PackageStatus{
|
||||
PackageStatusDraft: {PackageStatusActive},
|
||||
PackageStatusActive: {PackageStatusPaused, PackageStatusSoldOut, PackageStatusExpired},
|
||||
PackageStatusPaused: {PackageStatusActive, PackageStatusExpired},
|
||||
PackageStatusDraft: {PackageStatusActive},
|
||||
PackageStatusActive: {PackageStatusPaused, PackageStatusSoldOut, PackageStatusExpired},
|
||||
PackageStatusPaused: {PackageStatusActive, PackageStatusExpired},
|
||||
PackageStatusSoldOut: {}, // 只能由系统迁移
|
||||
PackageStatusExpired: {}, // 不能直接恢复,需要通过克隆
|
||||
}
|
||||
|
||||
@@ -13,37 +13,37 @@ import (
|
||||
type PackageStatus string
|
||||
|
||||
const (
|
||||
PackageStatusDraft PackageStatus = "draft"
|
||||
PackageStatusActive PackageStatus = "active"
|
||||
PackageStatusPaused PackageStatus = "paused"
|
||||
PackageStatusSoldOut PackageStatus = "sold_out"
|
||||
PackageStatusExpired PackageStatus = "expired"
|
||||
PackageStatusDraft PackageStatus = "draft"
|
||||
PackageStatusActive PackageStatus = "active"
|
||||
PackageStatusPaused PackageStatus = "paused"
|
||||
PackageStatusSoldOut PackageStatus = "sold_out"
|
||||
PackageStatusExpired PackageStatus = "expired"
|
||||
)
|
||||
|
||||
// 套餐
|
||||
type Package struct {
|
||||
ID int64 `json:"package_id"`
|
||||
SupplierID int64 `json:"supply_account_id"`
|
||||
AccountID int64 `json:"account_id,omitempty"`
|
||||
Platform string `json:"platform,omitempty"`
|
||||
Model string `json:"model"`
|
||||
TotalQuota float64 `json:"total_quota"`
|
||||
AvailableQuota float64 `json:"available_quota"`
|
||||
SoldQuota float64 `json:"sold_quota"`
|
||||
ReservedQuota float64 `json:"reserved_quota"`
|
||||
PricePer1MInput float64 `json:"price_per_1m_input"`
|
||||
PricePer1MOutput float64 `json:"price_per_1m_output"`
|
||||
MinPurchase float64 `json:"min_purchase,omitempty"`
|
||||
StartAt time.Time `json:"start_at,omitempty"`
|
||||
EndAt time.Time `json:"end_at,omitempty"`
|
||||
ValidDays int `json:"valid_days"`
|
||||
MaxConcurrent int `json:"max_concurrent,omitempty"`
|
||||
RateLimitRPM int `json:"rate_limit_rpm,omitempty"`
|
||||
Status PackageStatus `json:"status"`
|
||||
TotalOrders int `json:"total_orders"`
|
||||
TotalRevenue float64 `json:"total_revenue"`
|
||||
Rating float64 `json:"rating"`
|
||||
RatingCount int `json:"rating_count"`
|
||||
SupplierID int64 `json:"supply_account_id"`
|
||||
AccountID int64 `json:"account_id,omitempty"`
|
||||
Platform string `json:"platform,omitempty"`
|
||||
Model string `json:"model"`
|
||||
TotalQuota float64 `json:"total_quota"`
|
||||
AvailableQuota float64 `json:"available_quota"`
|
||||
SoldQuota float64 `json:"sold_quota"`
|
||||
ReservedQuota float64 `json:"reserved_quota"`
|
||||
PricePer1MInput float64 `json:"price_per_1m_input"`
|
||||
PricePer1MOutput float64 `json:"price_per_1m_output"`
|
||||
MinPurchase float64 `json:"min_purchase,omitempty"`
|
||||
StartAt time.Time `json:"start_at,omitempty"`
|
||||
EndAt time.Time `json:"end_at,omitempty"`
|
||||
ValidDays int `json:"valid_days"`
|
||||
MaxConcurrent int `json:"max_concurrent,omitempty"`
|
||||
RateLimitRPM int `json:"rate_limit_rpm,omitempty"`
|
||||
Status PackageStatus `json:"status"`
|
||||
TotalOrders int `json:"total_orders"`
|
||||
TotalRevenue float64 `json:"total_revenue"`
|
||||
Rating float64 `json:"rating"`
|
||||
RatingCount int `json:"rating_count"`
|
||||
|
||||
// 单位与币种 (XR-001)
|
||||
QuotaUnit string `json:"quota_unit"`
|
||||
@@ -51,10 +51,10 @@ type Package struct {
|
||||
CurrencyCode string `json:"currency_code"`
|
||||
|
||||
// 审计字段 (XR-001)
|
||||
Version int `json:"version"`
|
||||
Version int `json:"version"`
|
||||
CreatedIP *netip.Addr `json:"created_ip,omitempty"`
|
||||
UpdatedIP *netip.Addr `json:"updated_ip,omitempty"`
|
||||
AuditTraceID string `json:"audit_trace_id,omitempty"`
|
||||
AuditTraceID string `json:"audit_trace_id,omitempty"`
|
||||
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
@@ -73,15 +73,15 @@ type PackageService interface {
|
||||
|
||||
// 创建套餐草稿请求
|
||||
type CreatePackageDraftRequest struct {
|
||||
SupplierID int64
|
||||
AccountID int64
|
||||
Model string
|
||||
TotalQuota float64
|
||||
PricePer1MInput float64
|
||||
SupplierID int64
|
||||
AccountID int64
|
||||
Model string
|
||||
TotalQuota float64
|
||||
PricePer1MInput float64
|
||||
PricePer1MOutput float64
|
||||
ValidDays int
|
||||
MaxConcurrent int
|
||||
RateLimitRPM int
|
||||
ValidDays int
|
||||
MaxConcurrent int
|
||||
RateLimitRPM int
|
||||
}
|
||||
|
||||
// 批量调价请求
|
||||
@@ -90,17 +90,17 @@ type BatchUpdatePriceRequest struct {
|
||||
}
|
||||
|
||||
type BatchPriceItem struct {
|
||||
PackageID int64 `json:"package_id"`
|
||||
PricePer1MInput float64 `json:"price_per_1m_input"`
|
||||
PackageID int64 `json:"package_id"`
|
||||
PricePer1MInput float64 `json:"price_per_1m_input"`
|
||||
PricePer1MOutput float64 `json:"price_per_1m_output"`
|
||||
}
|
||||
|
||||
// 批量调价响应
|
||||
type BatchUpdatePriceResponse struct {
|
||||
Total int `json:"total"`
|
||||
SuccessCount int `json:"success_count"`
|
||||
FailedCount int `json:"failed_count"`
|
||||
Failures []BatchPriceFailure `json:"failures,omitempty"`
|
||||
Total int `json:"total"`
|
||||
SuccessCount int `json:"success_count"`
|
||||
FailedCount int `json:"failed_count"`
|
||||
Failures []BatchPriceFailure `json:"failures,omitempty"`
|
||||
}
|
||||
|
||||
type BatchPriceFailure struct {
|
||||
@@ -134,20 +134,20 @@ func NewPackageService(store PackageStore, accountStore AccountStore, auditStore
|
||||
|
||||
func (s *packageService) CreateDraft(ctx context.Context, supplierID int64, req *CreatePackageDraftRequest) (*Package, error) {
|
||||
pkg := &Package{
|
||||
SupplierID: supplierID,
|
||||
AccountID: req.AccountID,
|
||||
Model: req.Model,
|
||||
TotalQuota: req.TotalQuota,
|
||||
AvailableQuota: req.TotalQuota,
|
||||
PricePer1MInput: req.PricePer1MInput,
|
||||
SupplierID: supplierID,
|
||||
AccountID: req.AccountID,
|
||||
Model: req.Model,
|
||||
TotalQuota: req.TotalQuota,
|
||||
AvailableQuota: req.TotalQuota,
|
||||
PricePer1MInput: req.PricePer1MInput,
|
||||
PricePer1MOutput: req.PricePer1MOutput,
|
||||
ValidDays: req.ValidDays,
|
||||
MaxConcurrent: req.MaxConcurrent,
|
||||
RateLimitRPM: req.RateLimitRPM,
|
||||
Status: PackageStatusDraft,
|
||||
Version: 1,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
ValidDays: req.ValidDays,
|
||||
MaxConcurrent: req.MaxConcurrent,
|
||||
RateLimitRPM: req.RateLimitRPM,
|
||||
Status: PackageStatusDraft,
|
||||
Version: 1,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
|
||||
if err := s.store.Create(ctx, pkg); err != nil {
|
||||
@@ -255,20 +255,20 @@ func (s *packageService) Clone(ctx context.Context, supplierID, packageID int64)
|
||||
}
|
||||
|
||||
clone := &Package{
|
||||
SupplierID: supplierID,
|
||||
AccountID: original.AccountID,
|
||||
Model: original.Model,
|
||||
TotalQuota: original.TotalQuota,
|
||||
AvailableQuota: original.TotalQuota,
|
||||
PricePer1MInput: original.PricePer1MInput,
|
||||
SupplierID: supplierID,
|
||||
AccountID: original.AccountID,
|
||||
Model: original.Model,
|
||||
TotalQuota: original.TotalQuota,
|
||||
AvailableQuota: original.TotalQuota,
|
||||
PricePer1MInput: original.PricePer1MInput,
|
||||
PricePer1MOutput: original.PricePer1MOutput,
|
||||
ValidDays: original.ValidDays,
|
||||
MaxConcurrent: original.MaxConcurrent,
|
||||
RateLimitRPM: original.RateLimitRPM,
|
||||
Status: PackageStatusDraft,
|
||||
Version: 1,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
ValidDays: original.ValidDays,
|
||||
MaxConcurrent: original.MaxConcurrent,
|
||||
RateLimitRPM: original.RateLimitRPM,
|
||||
Status: PackageStatusDraft,
|
||||
Version: 1,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
|
||||
if err := s.store.Create(ctx, clone); err != nil {
|
||||
|
||||
@@ -15,8 +15,8 @@ type SettlementStatus string
|
||||
const (
|
||||
SettlementStatusPending SettlementStatus = "pending"
|
||||
SettlementStatusProcessing SettlementStatus = "processing"
|
||||
SettlementStatusCompleted SettlementStatus = "completed"
|
||||
SettlementStatusFailed SettlementStatus = "failed"
|
||||
SettlementStatusCompleted SettlementStatus = "completed"
|
||||
SettlementStatusFailed SettlementStatus = "failed"
|
||||
)
|
||||
|
||||
// 支付方式
|
||||
@@ -30,23 +30,23 @@ const (
|
||||
|
||||
// 结算单
|
||||
type Settlement struct {
|
||||
ID int64 `json:"settlement_id"`
|
||||
SupplierID int64 `json:"supplier_id"`
|
||||
SettlementNo string `json:"settlement_no"`
|
||||
Status SettlementStatus `json:"status"`
|
||||
TotalAmount float64 `json:"total_amount"`
|
||||
FeeAmount float64 `json:"fee_amount"`
|
||||
NetAmount float64 `json:"net_amount"`
|
||||
PaymentMethod PaymentMethod `json:"payment_method"`
|
||||
PaymentAccount string `json:"payment_account,omitempty"`
|
||||
PaymentTransactionID string `json:"payment_transaction_id,omitempty"`
|
||||
PaidAt *time.Time `json:"paid_at,omitempty"`
|
||||
ID int64 `json:"settlement_id"`
|
||||
SupplierID int64 `json:"supplier_id"`
|
||||
SettlementNo string `json:"settlement_no"`
|
||||
Status SettlementStatus `json:"status"`
|
||||
TotalAmount float64 `json:"total_amount"`
|
||||
FeeAmount float64 `json:"fee_amount"`
|
||||
NetAmount float64 `json:"net_amount"`
|
||||
PaymentMethod PaymentMethod `json:"payment_method"`
|
||||
PaymentAccount string `json:"payment_account,omitempty"`
|
||||
PaymentTransactionID string `json:"payment_transaction_id,omitempty"`
|
||||
PaidAt *time.Time `json:"paid_at,omitempty"`
|
||||
|
||||
// 账期 (XR-001)
|
||||
PeriodStart time.Time `json:"period_start"`
|
||||
PeriodEnd time.Time `json:"period_end"`
|
||||
TotalOrders int `json:"total_orders"`
|
||||
TotalUsageRecords int `json:"total_usage_records"`
|
||||
PeriodStart time.Time `json:"period_start"`
|
||||
PeriodEnd time.Time `json:"period_end"`
|
||||
TotalOrders int `json:"total_orders"`
|
||||
TotalUsageRecords int `json:"total_usage_records"`
|
||||
|
||||
// 单位与币种 (XR-001)
|
||||
CurrencyCode string `json:"currency_code"`
|
||||
@@ -57,8 +57,8 @@ type Settlement struct {
|
||||
IdempotencyKey string `json:"idempotency_key,omitempty"`
|
||||
|
||||
// 审计字段 (XR-001)
|
||||
AuditTraceID string `json:"audit_trace_id,omitempty"`
|
||||
Version int `json:"version"`
|
||||
AuditTraceID string `json:"audit_trace_id,omitempty"`
|
||||
Version int `json:"version"`
|
||||
CreatedIP *netip.Addr `json:"created_ip,omitempty"`
|
||||
UpdatedIP *netip.Addr `json:"updated_ip,omitempty"`
|
||||
|
||||
@@ -94,17 +94,17 @@ type EarningService interface {
|
||||
|
||||
// 提现请求
|
||||
type WithdrawRequest struct {
|
||||
Amount float64
|
||||
PaymentMethod PaymentMethod
|
||||
Amount float64
|
||||
PaymentMethod PaymentMethod
|
||||
PaymentAccount string
|
||||
SMSCode string
|
||||
SMSCode string
|
||||
}
|
||||
|
||||
// 账单汇总
|
||||
type BillingSummary struct {
|
||||
Period BillingPeriod `json:"period"`
|
||||
Summary BillingTotal `json:"summary"`
|
||||
ByPlatform []PlatformStat `json:"by_platform,omitempty"`
|
||||
Period BillingPeriod `json:"period"`
|
||||
Summary BillingTotal `json:"summary"`
|
||||
ByPlatform []PlatformStat `json:"by_platform,omitempty"`
|
||||
}
|
||||
|
||||
type BillingPeriod struct {
|
||||
@@ -114,12 +114,12 @@ type BillingPeriod struct {
|
||||
|
||||
type BillingTotal struct {
|
||||
TotalRevenue float64 `json:"total_revenue"`
|
||||
TotalOrders int `json:"total_orders"`
|
||||
TotalUsage int64 `json:"total_usage"`
|
||||
TotalRequests int64 `json:"total_requests"`
|
||||
TotalOrders int `json:"total_orders"`
|
||||
TotalUsage int64 `json:"total_usage"`
|
||||
TotalRequests int64 `json:"total_requests"`
|
||||
AvgSuccessRate float64 `json:"avg_success_rate"`
|
||||
PlatformFee float64 `json:"platform_fee"`
|
||||
NetEarnings float64 `json:"net_earnings"`
|
||||
PlatformFee float64 `json:"platform_fee"`
|
||||
NetEarnings float64 `json:"net_earnings"`
|
||||
}
|
||||
|
||||
type PlatformStat struct {
|
||||
@@ -175,17 +175,17 @@ func (s *settlementService) Withdraw(ctx context.Context, supplierID int64, req
|
||||
}
|
||||
|
||||
settlement := &Settlement{
|
||||
SupplierID: supplierID,
|
||||
SettlementNo: generateSettlementNo(),
|
||||
Status: SettlementStatusPending,
|
||||
TotalAmount: req.Amount,
|
||||
FeeAmount: req.Amount * 0.01, // 1% fee
|
||||
NetAmount: req.Amount * 0.99,
|
||||
PaymentMethod: req.PaymentMethod,
|
||||
SupplierID: supplierID,
|
||||
SettlementNo: generateSettlementNo(),
|
||||
Status: SettlementStatusPending,
|
||||
TotalAmount: req.Amount,
|
||||
FeeAmount: req.Amount * 0.01, // 1% fee
|
||||
NetAmount: req.Amount * 0.99,
|
||||
PaymentMethod: req.PaymentMethod,
|
||||
PaymentAccount: req.PaymentAccount,
|
||||
Version: 1,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
Version: 1,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
|
||||
if err := s.store.Create(ctx, settlement); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user