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
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -41,27 +42,39 @@ type ConditionRange struct {
|
||||
}
|
||||
|
||||
type ParameterTier struct {
|
||||
Value any
|
||||
Match any
|
||||
StandardFactor, MarkupMultiplier float64
|
||||
Enabled bool
|
||||
Value any `json:"value"`
|
||||
Label string `json:"label,omitempty"`
|
||||
Match any `json:"match,omitempty"`
|
||||
StandardFactor float64 `json:"standardFactor"`
|
||||
MarkupMultiplier float64 `json:"markupMultiplier"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Note string `json:"note,omitempty"`
|
||||
}
|
||||
type ParameterDimension struct {
|
||||
Key string
|
||||
DefaultValue any
|
||||
BaselineValue any
|
||||
Tiers []ParameterTier
|
||||
Key string `json:"key"`
|
||||
Label string `json:"label,omitempty"`
|
||||
DefaultValue any `json:"defaultValue,omitempty"`
|
||||
BaselineValue any `json:"baselineValue"`
|
||||
Tiers []ParameterTier `json:"tiers"`
|
||||
}
|
||||
type PriceRule struct {
|
||||
ID, Provider, Capability, ReqKey string
|
||||
Unit Unit
|
||||
QuantitySource QuantitySource
|
||||
StandardUnitPriceFen int64
|
||||
MarkupMultiplier float64
|
||||
Enabled bool
|
||||
Conditions Conditions
|
||||
Priority int
|
||||
Dimensions []ParameterDimension
|
||||
ID string `json:"id"`
|
||||
Provider string `json:"provider"`
|
||||
Capability string `json:"capability"`
|
||||
ReqKey string `json:"reqKey,omitempty"`
|
||||
VariantKey string `json:"variantKey,omitempty"`
|
||||
Unit Unit `json:"unit"`
|
||||
QuantitySource QuantitySource `json:"quantitySource,omitempty"`
|
||||
StandardUnitPriceFen int64 `json:"standardUnitPriceFen"`
|
||||
MarkupMultiplier float64 `json:"markupMultiplier"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Conditions Conditions `json:"conditions,omitempty"`
|
||||
Priority int `json:"priority,omitempty"`
|
||||
Note string `json:"note,omitempty"`
|
||||
Source map[string]any `json:"source,omitempty"`
|
||||
Dimensions []ParameterDimension `json:"parameterDimensions,omitempty"`
|
||||
CreatedAt string `json:"createdAt,omitempty"`
|
||||
UpdatedAt string `json:"updatedAt,omitempty"`
|
||||
}
|
||||
type QuoteInput struct {
|
||||
Provider, Capability, ReqKey, Source, Role string
|
||||
@@ -69,13 +82,23 @@ type QuoteInput struct {
|
||||
BillingDisabled bool
|
||||
}
|
||||
type Quote struct {
|
||||
PriceRuleID string
|
||||
Unit Unit
|
||||
Quantity float64
|
||||
StandardUnitPriceFen, AmountFen int64
|
||||
MarkupMultiplier float64
|
||||
Currency string
|
||||
QuotaExempt bool
|
||||
PriceRuleID string `json:"priceRuleId"`
|
||||
Provider string `json:"provider,omitempty"`
|
||||
Capability string `json:"capability,omitempty"`
|
||||
ReqKey string `json:"reqKey,omitempty"`
|
||||
VariantKey string `json:"variantKey,omitempty"`
|
||||
Unit Unit `json:"unit"`
|
||||
Quantity float64 `json:"quantity"`
|
||||
StandardUnitPriceFen int64 `json:"standardUnitPriceFen"`
|
||||
AmountFen int64 `json:"amountFen"`
|
||||
MarkupMultiplier float64 `json:"markupMultiplier"`
|
||||
Currency string `json:"currency"`
|
||||
Conditions Conditions `json:"conditions,omitempty"`
|
||||
QuantitySource QuantitySource `json:"quantitySource,omitempty"`
|
||||
Parameters Parameters `json:"parameters,omitempty"`
|
||||
QuotaExempt bool `json:"quotaExempt,omitempty"`
|
||||
ReservedAmountFen int64 `json:"reservedAmountFen,omitempty"`
|
||||
SettlementStatus string `json:"settlementStatus,omitempty"`
|
||||
}
|
||||
type Catalog struct{ Rules []PriceRule }
|
||||
|
||||
@@ -89,14 +112,16 @@ func (c Catalog) Quote(input QuoteInput) (*Quote, error) {
|
||||
}
|
||||
var matches []candidate
|
||||
for _, rule := range c.Rules {
|
||||
if !rule.Enabled || rule.Provider != input.Provider || rule.Capability != input.Capability || rule.ReqKey != "" && rule.ReqKey != input.ReqKey || !conditionsMatch(rule.Conditions, input.Parameters) {
|
||||
conditions := effectiveConditions(rule)
|
||||
if !rule.Enabled || rule.Provider != input.Provider || rule.Capability != input.Capability || rule.ReqKey != "" && rule.ReqKey != input.ReqKey || !conditionsMatch(conditions, input.Parameters) {
|
||||
continue
|
||||
}
|
||||
req := 0
|
||||
if rule.ReqKey != "" {
|
||||
req = 1
|
||||
}
|
||||
matches = append(matches, candidate{rule, req, len(rule.Conditions), rule.Priority})
|
||||
rule.Conditions = conditions
|
||||
matches = append(matches, candidate{rule, req, len(conditions), rule.Priority})
|
||||
}
|
||||
if len(matches) == 0 {
|
||||
return nil, ErrPriceRuleNotFound
|
||||
@@ -123,7 +148,40 @@ func (c Catalog) Quote(input QuoteInput) (*Quote, error) {
|
||||
return nil, err
|
||||
}
|
||||
quantity := quantityFor(w.rule, input.Parameters)
|
||||
return &Quote{PriceRuleID: w.rule.ID, Unit: w.rule.Unit, Quantity: quantity, StandardUnitPriceFen: price, MarkupMultiplier: markup, AmountFen: int64(math.Ceil(float64(price) * quantity * markup)), Currency: CurrencyCNY, QuotaExempt: input.Source == "platform" && input.Role == "super_admin"}, nil
|
||||
return &Quote{PriceRuleID: w.rule.ID, Provider: input.Provider, Capability: input.Capability, ReqKey: input.ReqKey, VariantKey: w.rule.VariantKey, Unit: w.rule.Unit, Quantity: quantity, StandardUnitPriceFen: price, MarkupMultiplier: markup, AmountFen: int64(math.Ceil(float64(price) * quantity * markup)), Currency: CurrencyCNY, Conditions: w.rule.Conditions, QuantitySource: w.rule.QuantitySource, Parameters: input.Parameters, QuotaExempt: input.Source == "platform" && input.Role == "super_admin"}, nil
|
||||
}
|
||||
|
||||
func effectiveConditions(rule PriceRule) Conditions {
|
||||
conditions := parseVariantKey(rule.VariantKey)
|
||||
for key, value := range rule.Conditions {
|
||||
conditions[key] = value
|
||||
}
|
||||
return conditions
|
||||
}
|
||||
|
||||
func parseVariantKey(value string) Conditions {
|
||||
conditions := Conditions{}
|
||||
for _, part := range strings.FieldsFunc(value, func(r rune) bool { return r == ';' || r == ',' }) {
|
||||
key, raw, ok := strings.Cut(part, "=")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
key, raw = strings.TrimSpace(key), strings.TrimSpace(raw)
|
||||
if key == "ratio" {
|
||||
key = "aspectRatio"
|
||||
}
|
||||
switch key {
|
||||
case "model", "resolution", "size", "aspectRatio", "quality":
|
||||
if raw != "" {
|
||||
conditions[key] = strings.ToLower(raw)
|
||||
}
|
||||
case "duration", "imageCount", "referenceImageCount", "scale":
|
||||
if number, err := strconv.ParseFloat(raw, 64); err == nil && !math.IsNaN(number) && !math.IsInf(number, 0) {
|
||||
conditions[key] = number
|
||||
}
|
||||
}
|
||||
}
|
||||
return conditions
|
||||
}
|
||||
|
||||
func tierPrice(rule PriceRule, params Parameters) (int64, float64, error) {
|
||||
|
||||
Reference in new issue
Block a user