252 lines
8.2 KiB
Go
252 lines
8.2 KiB
Go
package order
|
|
|
|
import (
|
|
"encoding/json"
|
|
"math"
|
|
"strconv"
|
|
|
|
"hfb_sys/backend/internal/model"
|
|
"hfb_sys/backend/pkg/money"
|
|
|
|
"gorm.io/datatypes"
|
|
)
|
|
|
|
type orderPricing struct {
|
|
RentAmountCent int64
|
|
OwnerRentAmountCent int64
|
|
PlatformFeeCent int64
|
|
}
|
|
|
|
type checkoutSettlement struct {
|
|
OwnerRentIncomeCent int64
|
|
DepositCompensationCent int64
|
|
OwnerIncomeCent int64
|
|
RentRefundCent int64
|
|
DepositRefundCent int64
|
|
RenterRefundCent int64
|
|
PlatformFeeCent int64
|
|
ActualRentAmountCent int64
|
|
}
|
|
|
|
func orderDurationHours(order model.RentalOrder) int {
|
|
if order.EstimatedDurationHours > 0 {
|
|
return order.EstimatedDurationHours
|
|
}
|
|
return internalOrderHours
|
|
}
|
|
|
|
func buildOrderPricing(listing model.RentalListing, account model.GameAccount) orderPricing {
|
|
rentAmountCent := listing.PriceCent
|
|
ownerRentAmountCent := int64(math.Round(readSnapshotPrice(account.AssetSummary, "seller_total_price") * 100))
|
|
if ownerRentAmountCent <= 0 || ownerRentAmountCent > rentAmountCent {
|
|
ownerRentAmountCent = rentAmountCent
|
|
}
|
|
platformFeeCent := int64(math.Round(readSnapshotPrice(account.AssetSummary, "platform_markup_amount") * 100))
|
|
if platformFeeCent <= 0 || ownerRentAmountCent+platformFeeCent != rentAmountCent {
|
|
platformFeeCent = rentAmountCent - ownerRentAmountCent
|
|
}
|
|
if platformFeeCent < 0 {
|
|
platformFeeCent = 0
|
|
}
|
|
return orderPricing{
|
|
RentAmountCent: rentAmountCent,
|
|
OwnerRentAmountCent: ownerRentAmountCent,
|
|
PlatformFeeCent: platformFeeCent,
|
|
}
|
|
}
|
|
|
|
func readSnapshotPrice(raw datatypes.JSON, key string) float64 {
|
|
return readOrderSnapshotPrice(raw, key)
|
|
}
|
|
|
|
func readOrderSnapshotPrice(raw datatypes.JSON, key string) float64 {
|
|
if len(raw) == 0 {
|
|
return 0
|
|
}
|
|
var snapshot map[string]any
|
|
if err := json.Unmarshal(raw, &snapshot); err != nil {
|
|
return 0
|
|
}
|
|
if assetSummary, ok := snapshot["asset_summary"].(map[string]any); ok {
|
|
snapshot = assetSummary
|
|
}
|
|
breakdown, ok := snapshot["price_breakdown"].(map[string]any)
|
|
if !ok {
|
|
return 0
|
|
}
|
|
return readJSONNumber(breakdown[key])
|
|
}
|
|
|
|
func readJSONNumber(value any) float64 {
|
|
switch typed := value.(type) {
|
|
case float64:
|
|
return typed
|
|
case float32:
|
|
return float64(typed)
|
|
case int:
|
|
return float64(typed)
|
|
case int64:
|
|
return float64(typed)
|
|
case json.Number:
|
|
number, err := typed.Float64()
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
return number
|
|
case string:
|
|
number, err := strconv.ParseFloat(typed, 64)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
return number
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func buildCheckout(order model.RentalOrder, initiatedBy uint64, status string, content string, evidenceURLS []string, consumableAmountCent int64, coinConsumedM float64, otherAmountCent int64, explicitDeductCent int64, useExplicitDeduct bool) (model.OrderCheckout, error) {
|
|
if consumableAmountCent < 0 || coinConsumedM < 0 || otherAmountCent < 0 || explicitDeductCent < 0 {
|
|
return model.OrderCheckout{}, ErrInvalidCheckoutAmount
|
|
}
|
|
deductAmountCent := otherAmountCent
|
|
if useExplicitDeduct {
|
|
deductAmountCent = explicitDeductCent
|
|
}
|
|
if deductAmountCent > order.DepositAmountCent {
|
|
return model.OrderCheckout{}, ErrInvalidCheckoutAmount
|
|
}
|
|
settlement := calculateCheckoutSettlement(order, consumableAmountCent, roundQuantity(coinConsumedM), deductAmountCent)
|
|
evidence, err := marshalStringList(evidenceURLS)
|
|
if err != nil {
|
|
return model.OrderCheckout{}, err
|
|
}
|
|
return model.OrderCheckout{
|
|
OrderID: order.ID,
|
|
InitiatedBy: initiatedBy,
|
|
Status: status,
|
|
RentAmountCent: settlement.ActualRentAmountCent,
|
|
OwnerRentAmountCent: settlement.OwnerRentIncomeCent,
|
|
PlatformFeeCent: settlement.PlatformFeeCent,
|
|
DepositAmountCent: order.DepositAmountCent,
|
|
ConsumableAmountCent: consumableAmountCent,
|
|
CoinConsumedM: roundQuantity(coinConsumedM),
|
|
OtherAmountCent: otherAmountCent,
|
|
DepositDeductAmountCent: settlement.DepositCompensationCent,
|
|
RenterRefundAmountCent: settlement.RenterRefundCent,
|
|
OwnerIncomeAmountCent: settlement.OwnerIncomeCent,
|
|
Content: content,
|
|
EvidenceURLS: evidence,
|
|
}, nil
|
|
}
|
|
|
|
func buildCheckoutSettlement(order model.RentalOrder, checkout *model.OrderCheckout) checkoutSettlement {
|
|
return calculateCheckoutSettlement(order, checkout.ConsumableAmountCent, checkout.CoinConsumedM, checkout.DepositDeductAmountCent)
|
|
}
|
|
|
|
func calculateCheckoutSettlement(order model.RentalOrder, consumableAmountCent int64, coinConsumedM float64, depositDeductAmountCent int64) checkoutSettlement {
|
|
orderRentAmountCent := order.RentAmountCent
|
|
orderOwnerRentAmountCent := order.OwnerRentAmountCent
|
|
orderDepositAmountCent := order.DepositAmountCent
|
|
|
|
buyerCoinBasePriceCent := int64(math.Round(readOrderSnapshotPrice(order.AccountSnapshot, "buyer_coin_base_price") * 100))
|
|
if buyerCoinBasePriceCent <= 0 || buyerCoinBasePriceCent > orderRentAmountCent {
|
|
buyerCoinBasePriceCent = orderRentAmountCent
|
|
}
|
|
sellerCoinBasePriceCent := int64(math.Round(readOrderSnapshotPrice(order.AccountSnapshot, "seller_coin_base_price") * 100))
|
|
if sellerCoinBasePriceCent <= 0 || sellerCoinBasePriceCent > orderOwnerRentAmountCent {
|
|
sellerCoinBasePriceCent = orderOwnerRentAmountCent
|
|
}
|
|
prepaidConsumablePriceCent := int64(math.Round(readOrderSnapshotPrice(order.AccountSnapshot, "consumable_price") * 100))
|
|
if prepaidConsumablePriceCent <= 0 || prepaidConsumablePriceCent > orderRentAmountCent-buyerCoinBasePriceCent {
|
|
prepaidConsumablePriceCent = maxCent(orderRentAmountCent-buyerCoinBasePriceCent, 0)
|
|
}
|
|
prepaidOwnerConsumablePriceCent := maxCent(orderOwnerRentAmountCent-sellerCoinBasePriceCent, 0)
|
|
totalCoinM := readOrderSnapshotCoinM(order.AccountSnapshot)
|
|
coinUseRatio := 1.0
|
|
if totalCoinM > 0 {
|
|
coinUseRatio = minRatio(maxRatio(roundQuantity(coinConsumedM)/totalCoinM, 0), 1)
|
|
}
|
|
usedBuyerCoinPriceCent := int64(math.Round(float64(buyerCoinBasePriceCent) * coinUseRatio))
|
|
usedOwnerCoinPriceCent := int64(math.Round(float64(sellerCoinBasePriceCent) * coinUseRatio))
|
|
usedBuyerConsumablePriceCent := minCent(consumableAmountCent, prepaidConsumablePriceCent)
|
|
consumableUseRatio := 1.0
|
|
if prepaidConsumablePriceCent > 0 {
|
|
consumableUseRatio = minRatio(maxRatio(float64(usedBuyerConsumablePriceCent)/float64(prepaidConsumablePriceCent), 0), 1)
|
|
}
|
|
usedOwnerConsumablePriceCent := int64(math.Round(float64(prepaidOwnerConsumablePriceCent) * consumableUseRatio))
|
|
actualRentAmountCent := minCent(usedBuyerCoinPriceCent+usedBuyerConsumablePriceCent, orderRentAmountCent)
|
|
ownerRentIncomeCent := minCent(usedOwnerCoinPriceCent+usedOwnerConsumablePriceCent, orderOwnerRentAmountCent)
|
|
depositCompensationCent := minCent(depositDeductAmountCent, orderDepositAmountCent)
|
|
rentRefundCent := maxCent(orderRentAmountCent-actualRentAmountCent, 0)
|
|
depositRefundCent := maxCent(orderDepositAmountCent-depositCompensationCent, 0)
|
|
|
|
return checkoutSettlement{
|
|
OwnerRentIncomeCent: ownerRentIncomeCent,
|
|
DepositCompensationCent: depositCompensationCent,
|
|
OwnerIncomeCent: ownerRentIncomeCent + depositCompensationCent,
|
|
RentRefundCent: rentRefundCent,
|
|
DepositRefundCent: depositRefundCent,
|
|
RenterRefundCent: rentRefundCent + depositRefundCent,
|
|
PlatformFeeCent: maxCent(actualRentAmountCent-ownerRentIncomeCent, 0),
|
|
ActualRentAmountCent: actualRentAmountCent,
|
|
}
|
|
}
|
|
|
|
func readOrderSnapshotCoinM(raw datatypes.JSON) float64 {
|
|
if len(raw) == 0 {
|
|
return 0
|
|
}
|
|
var snapshot map[string]any
|
|
if err := json.Unmarshal(raw, &snapshot); err != nil {
|
|
return 0
|
|
}
|
|
return roundQuantity(readJSONNumber(snapshot["haf_coin_amount"]) / 1000000)
|
|
}
|
|
|
|
// roundMoney 使用统一的角精度(0.1元)
|
|
func roundMoney(value float64) float64 {
|
|
return money.Round(value)
|
|
}
|
|
|
|
func roundQuantity(value float64) float64 {
|
|
return math.Round(value*100) / 100
|
|
}
|
|
|
|
// minMoney 返回较小金额(角精度)
|
|
func minMoney(a float64, b float64) float64 {
|
|
return money.Min(a, b)
|
|
}
|
|
|
|
// maxMoney 返回较大金额(角精度)
|
|
func maxMoney(a float64, b float64) float64 {
|
|
return money.Max(a, b)
|
|
}
|
|
|
|
func minCent(a int64, b int64) int64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
func maxCent(a int64, b int64) int64 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
func minRatio(a float64, b float64) float64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
func maxRatio(a float64, b float64) float64 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|