feat: complete remaining Go backend modules
This commit is contained in:
1 parent
cea2751dc5
commit
aef5a97165
145 files changed
+18376
-199
No files matched your search
@@ -0,0 +1,156 @@
|
||||
package billing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Wallet struct {
|
||||
OrganizationID string `json:"organizationId"`
|
||||
BalanceFen int64 `json:"balanceFen"`
|
||||
TotalRechargedFen int64 `json:"totalRechargedFen"`
|
||||
TotalChargedFen int64 `json:"totalChargedFen"`
|
||||
Currency string `json:"currency,omitempty"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type LedgerEntry struct {
|
||||
ID string `json:"id"`
|
||||
OrganizationID string `json:"organizationId"`
|
||||
AccountID string `json:"accountId,omitempty"`
|
||||
JobID string `json:"jobId,omitempty"`
|
||||
Kind string `json:"kind"`
|
||||
DeltaFen int64 `json:"deltaFen"`
|
||||
BalanceAfterFen int64 `json:"balanceAfterFen"`
|
||||
Currency string `json:"currency"`
|
||||
IdempotencyKey string `json:"idempotencyKey"`
|
||||
Description string `json:"description"`
|
||||
Metadata map[string]any `json:"metadata"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
}
|
||||
|
||||
type Summary struct {
|
||||
RechargeFen int64 `json:"rechargeFen"`
|
||||
ChargedFen int64 `json:"chargedFen"`
|
||||
RefundedFen int64 `json:"refundedFen"`
|
||||
NetConsumedFen int64 `json:"netConsumedFen"`
|
||||
}
|
||||
type AccountConfig struct {
|
||||
AccountName string `json:"accountName,omitempty"`
|
||||
BankName string `json:"bankName,omitempty"`
|
||||
AccountNumber string `json:"accountNumber,omitempty"`
|
||||
Contact string `json:"contact,omitempty"`
|
||||
}
|
||||
type Organization struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
ArchiveOwnerID string `json:"archiveOwnerId"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
Wallet Wallet `json:"wallet"`
|
||||
}
|
||||
type Member struct {
|
||||
ID string `json:"id"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Phone string `json:"phone"`
|
||||
Role string `json:"role"`
|
||||
OrganizationID string `json:"organizationId,omitempty"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
type Overview struct {
|
||||
Wallet Wallet `json:"wallet"`
|
||||
Ledger []LedgerEntry `json:"ledger"`
|
||||
BillingAccount AccountConfig `json:"billingAccount"`
|
||||
Summary Summary `json:"summary"`
|
||||
Personal Summary `json:"personal"`
|
||||
}
|
||||
type AdminOverview struct {
|
||||
BillingAccount AccountConfig `json:"billingAccount"`
|
||||
Organizations []Organization `json:"organizations"`
|
||||
Members []Member `json:"members"`
|
||||
Ledger []LedgerEntry `json:"ledger"`
|
||||
PriceRules []PriceRule `json:"priceRules"`
|
||||
}
|
||||
|
||||
type QuoteCommand struct {
|
||||
AccountID, OrganizationID, OrganizationName, Role string
|
||||
Provider, Capability, ReqKey string
|
||||
Parameters Parameters
|
||||
Payload map[string]any
|
||||
}
|
||||
type PricePatch struct {
|
||||
MarkupMultiplier float64
|
||||
DimensionKey, TierValue string
|
||||
}
|
||||
type AdjustmentCommand struct {
|
||||
OrganizationID, OperatorID, Direction, Note string
|
||||
AmountFen, DeltaFen int64
|
||||
}
|
||||
type AdjustmentResult struct {
|
||||
Wallet Wallet
|
||||
Entry LedgerEntry
|
||||
}
|
||||
|
||||
type AccountConfigStore interface {
|
||||
Load(context.Context) (AccountConfig, error)
|
||||
Save(context.Context, AccountConfig) error
|
||||
}
|
||||
|
||||
// ReadService is the deliberately cohesive persistence seam required by the
|
||||
// Billing HTTP module. Implementations can execute overview reads concurrently.
|
||||
type ReadService interface {
|
||||
Overview(context.Context, string, string) (Overview, error)
|
||||
AdminOverview(context.Context) (AdminOverview, error)
|
||||
ListPrices(context.Context) ([]PriceRule, error)
|
||||
GetPrice(context.Context, string) (*PriceRule, error)
|
||||
}
|
||||
type HTTPService interface {
|
||||
ReadService
|
||||
Quote(context.Context, QuoteCommand) (*Quote, error)
|
||||
UpdatePrice(context.Context, string, PricePatch) (*PriceRule, error)
|
||||
Adjust(context.Context, AdjustmentCommand) (AdjustmentResult, error)
|
||||
}
|
||||
|
||||
var ErrPriceNotFound = errors.New("billing price not found")
|
||||
|
||||
func ValidatePricePatch(rule *PriceRule, patch PricePatch) error {
|
||||
if patch.MarkupMultiplier < 1 || patch.MarkupMultiplier > 1000 || math.IsNaN(patch.MarkupMultiplier) || math.IsInf(patch.MarkupMultiplier, 0) {
|
||||
return errors.New("上浮倍率必须在 1.00 至 1000.00 之间。")
|
||||
}
|
||||
if (patch.DimensionKey == "") != (patch.TierValue == "") {
|
||||
return errors.New("参数档位倍率更新必须同时提供参数维度和档位。")
|
||||
}
|
||||
if rule == nil {
|
||||
return ErrPriceNotFound
|
||||
}
|
||||
if patch.DimensionKey == "" && len(rule.Dimensions) > 0 {
|
||||
return errors.New("当前服务包含参数档位,请指定要调整的参数档位倍率。")
|
||||
}
|
||||
if patch.DimensionKey != "" {
|
||||
for _, dimension := range rule.Dimensions {
|
||||
if dimension.Key != patch.DimensionKey {
|
||||
continue
|
||||
}
|
||||
for _, tier := range dimension.Tiers {
|
||||
if strings.TrimSpace(fmt.Sprint(tier.Value)) == patch.TierValue {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("计费参数档位不存在。")
|
||||
}
|
||||
return errors.New("计费参数维度不存在。")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func PostingResult(post WalletPosting, organizationID string) AdjustmentResult {
|
||||
return AdjustmentResult{
|
||||
Wallet: Wallet{OrganizationID: organizationID, BalanceFen: post.BalanceFen, TotalRechargedFen: post.TotalRechargedFen, TotalChargedFen: post.TotalChargedFen, Currency: CurrencyCNY, UpdatedAt: post.UpdatedAt.Format(time.RFC3339Nano)},
|
||||
Entry: LedgerEntry{ID: post.LedgerID, OrganizationID: organizationID, DeltaFen: post.DeltaFen, BalanceAfterFen: post.BalanceAfterFen, Currency: CurrencyCNY, CreatedAt: post.CreatedAt.Format(time.RFC3339Nano)},
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user