按实际纯币金额计算租客优惠与积分
This commit is contained in:
@@ -255,6 +255,9 @@ func applyCounterCheckoutUpdate(checkout *model.OrderCheckout, next model.OrderC
|
||||
checkout.OwnerRentAmountCent = next.OwnerRentAmountCent
|
||||
checkout.PlatformFeeCent = next.PlatformFeeCent
|
||||
checkout.DepositAmountCent = next.DepositAmountCent
|
||||
checkout.PureCoinAmountCent = next.PureCoinAmountCent
|
||||
checkout.PureCoinDiscountCent = next.PureCoinDiscountCent
|
||||
checkout.PureCoinPayableCent = next.PureCoinPayableCent
|
||||
checkout.ConsumableAmountCent = next.ConsumableAmountCent
|
||||
checkout.CoinConsumedM = next.CoinConsumedM
|
||||
checkout.OtherAmountCent = next.OtherAmountCent
|
||||
|
||||
@@ -40,10 +40,13 @@ func (r *Repository) finalizeCheckout(tx *gorm.DB, order *model.RentalOrder, che
|
||||
return nil, err
|
||||
}
|
||||
applyCheckoutSettlement(checkout, settlement)
|
||||
order.ActualCoinConsumedM = settlement.CoinConsumedM
|
||||
order.ActualPureCoinAmountCent = settlement.PureCoinAmountCent
|
||||
order.ActualPureCoinDiscountCent = settlement.PureCoinDiscountCent
|
||||
if err := appendCheckoutCompletedNotifications(tx, order, renterContent); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rentergrowth.AwardOrderCompleted(tx, order, settlement.ActualRentAmountCent, rentergrowth.SourceOrderCompleted); err != nil {
|
||||
if err := rentergrowth.AwardOrderCompleted(tx, order, settlement.PureCoinAmountCent, rentergrowth.SourceOrderCompleted); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := saveFinalizedCheckout(tx, order, checkout, listing, account); err != nil {
|
||||
@@ -110,6 +113,9 @@ func applyCheckoutSettlement(checkout *model.OrderCheckout, settlement checkoutS
|
||||
checkout.RentAmountCent = settlement.ActualRentAmountCent
|
||||
checkout.OwnerRentAmountCent = settlement.OwnerRentIncomeCent
|
||||
checkout.PlatformFeeCent = settlement.PlatformFeeCent
|
||||
checkout.PureCoinAmountCent = settlement.PureCoinAmountCent
|
||||
checkout.PureCoinDiscountCent = settlement.PureCoinDiscountCent
|
||||
checkout.PureCoinPayableCent = settlement.PureCoinPayableCent
|
||||
checkout.RenterRefundAmountCent = settlement.RenterRefundCent
|
||||
checkout.OwnerIncomeAmountCent = settlement.OwnerIncomeCent
|
||||
checkout.ShortfallCent = settlement.ShortfallCent
|
||||
|
||||
@@ -37,9 +37,16 @@ type OrderDTO struct {
|
||||
PlatformFeeCent *int64 `json:"platform_fee_cent,omitempty"`
|
||||
RentOriginalAmountCent int64 `json:"rent_original_amount_cent,omitempty"`
|
||||
RentDiscountAmountCent int64 `json:"rent_discount_amount_cent,omitempty"`
|
||||
PureCoinOriginalAmountCent int64 `json:"pure_coin_original_amount_cent,omitempty"`
|
||||
ExtraItemOriginalAmountCent int64 `json:"extra_item_original_amount_cent,omitempty"`
|
||||
ActualCoinConsumedM float64 `json:"actual_coin_consumed_m,omitempty"`
|
||||
ActualPureCoinAmountCent int64 `json:"actual_pure_coin_amount_cent,omitempty"`
|
||||
ActualPureCoinDiscountCent int64 `json:"actual_pure_coin_discount_cent,omitempty"`
|
||||
RenterGrowthLevel string `json:"renter_growth_level,omitempty"`
|
||||
RenterGrowthLevelName string `json:"renter_growth_level_name,omitempty"`
|
||||
RenterDiscountBps int `json:"renter_discount_bps,omitempty"`
|
||||
GrowthPointsBasisCent int64 `json:"growth_points_basis_cent,omitempty"`
|
||||
GrowthPointsPerYuan int64 `json:"growth_points_per_yuan,omitempty"`
|
||||
GrowthPointsAwarded int64 `json:"growth_points_awarded,omitempty"`
|
||||
GrowthPointsAwardedAt *time.Time `json:"growth_points_awarded_at,omitempty"`
|
||||
AccountSnapshot datatypes.JSON `json:"account_snapshot"`
|
||||
@@ -191,6 +198,9 @@ type CheckoutDTO struct {
|
||||
OwnerRentAmountCent *int64 `json:"owner_rent_amount_cent,omitempty"`
|
||||
PlatformFeeCent *int64 `json:"platform_fee_cent,omitempty"`
|
||||
DepositAmountCent int64 `json:"deposit_amount_cent"`
|
||||
PureCoinAmountCent int64 `json:"pure_coin_amount_cent"`
|
||||
PureCoinDiscountCent int64 `json:"pure_coin_discount_cent"`
|
||||
PureCoinPayableCent int64 `json:"pure_coin_payable_cent"`
|
||||
ConsumableAmountCent int64 `json:"consumable_amount_cent"`
|
||||
CoinConsumedM float64 `json:"coin_consumed_m"`
|
||||
OtherAmountCent int64 `json:"other_amount_cent"`
|
||||
|
||||
@@ -48,7 +48,7 @@ func (r *Repository) Create(ctx context.Context, renterID uint64, req CreateRequ
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rentDiscountAmountCent := rentergrowth.CalculateDiscountCent(rentOriginalAmountCent, pricing.PlatformFeeCent, growthSnapshot.DiscountBps)
|
||||
rentDiscountAmountCent := rentergrowth.CalculateDiscountCent(pricing.PureCoinAmountCent, pricing.PureCoinPlatformFeeCent, growthSnapshot.DiscountBps)
|
||||
if rentDiscountAmountCent > 0 {
|
||||
pricing.PlatformFeeCent -= rentDiscountAmountCent
|
||||
pricing.RentAmountCent -= rentDiscountAmountCent
|
||||
@@ -59,34 +59,36 @@ func (r *Repository) Create(ctx context.Context, renterID uint64, req CreateRequ
|
||||
return err
|
||||
}
|
||||
order := model.RentalOrder{
|
||||
OrderNo: orderNo,
|
||||
ListingID: listing.ID,
|
||||
AccountID: listing.AccountID,
|
||||
OwnerID: listing.OwnerID,
|
||||
RenterID: renterID,
|
||||
EstimatedDurationHours: rentHours,
|
||||
RentAmountCent: pricing.RentAmountCent,
|
||||
OwnerRentAmountCent: pricing.OwnerRentAmountCent,
|
||||
DepositAmountCent: depositWaiver.PaidCent,
|
||||
DepositOriginalAmountCent: depositOriginalAmountCent,
|
||||
DepositWaivedAmountCent: depositWaiver.WaivedCent,
|
||||
DepositFreeLevelQuotaCent: growthSnapshot.LevelDepositFreeQuotaCent,
|
||||
DepositFreeManualQuotaCent: growthSnapshot.ManualDepositFreeQuotaCent,
|
||||
DepositFreeUsedBeforeCent: depositWaiver.UsedBeforeCent,
|
||||
PlatformFeeCent: pricing.PlatformFeeCent,
|
||||
RentOriginalAmountCent: rentOriginalAmountCent,
|
||||
RentDiscountAmountCent: rentDiscountAmountCent,
|
||||
RenterGrowthLevel: growthSnapshot.LevelCode,
|
||||
RenterGrowthLevelName: growthSnapshot.LevelName,
|
||||
RenterDiscountBps: growthSnapshot.DiscountBps,
|
||||
AccountSnapshot: snapshot,
|
||||
Status: orderStatusPendingPayment,
|
||||
HandoffStatus: handoffStatusNone,
|
||||
HandoffMode: listingHandoffMode(listing),
|
||||
SettlementMode: listingSettlementMode(listing),
|
||||
ManagedAdminID: listing.ManagedAdminID,
|
||||
SettlementStatus: settlementStatusUnsettled,
|
||||
OfflineSettlementStatus: offlineSettlementStatusNone,
|
||||
OrderNo: orderNo,
|
||||
ListingID: listing.ID,
|
||||
AccountID: listing.AccountID,
|
||||
OwnerID: listing.OwnerID,
|
||||
RenterID: renterID,
|
||||
EstimatedDurationHours: rentHours,
|
||||
RentAmountCent: pricing.RentAmountCent,
|
||||
OwnerRentAmountCent: pricing.OwnerRentAmountCent,
|
||||
DepositAmountCent: depositWaiver.PaidCent,
|
||||
DepositOriginalAmountCent: depositOriginalAmountCent,
|
||||
DepositWaivedAmountCent: depositWaiver.WaivedCent,
|
||||
DepositFreeLevelQuotaCent: growthSnapshot.LevelDepositFreeQuotaCent,
|
||||
DepositFreeManualQuotaCent: growthSnapshot.ManualDepositFreeQuotaCent,
|
||||
DepositFreeUsedBeforeCent: depositWaiver.UsedBeforeCent,
|
||||
PlatformFeeCent: pricing.PlatformFeeCent,
|
||||
RentOriginalAmountCent: rentOriginalAmountCent,
|
||||
RentDiscountAmountCent: rentDiscountAmountCent,
|
||||
PureCoinOriginalAmountCent: pricing.PureCoinAmountCent,
|
||||
ExtraItemOriginalAmountCent: pricing.ExtraItemAmountCent,
|
||||
RenterGrowthLevel: growthSnapshot.LevelCode,
|
||||
RenterGrowthLevelName: growthSnapshot.LevelName,
|
||||
RenterDiscountBps: growthSnapshot.DiscountBps,
|
||||
AccountSnapshot: snapshot,
|
||||
Status: orderStatusPendingPayment,
|
||||
HandoffStatus: handoffStatusNone,
|
||||
HandoffMode: listingHandoffMode(listing),
|
||||
SettlementMode: listingSettlementMode(listing),
|
||||
ManagedAdminID: listing.ManagedAdminID,
|
||||
SettlementStatus: settlementStatusUnsettled,
|
||||
OfflineSettlementStatus: offlineSettlementStatusNone,
|
||||
}
|
||||
if err := tx.Create(&order).Error; err != nil {
|
||||
return err
|
||||
|
||||
@@ -87,9 +87,16 @@ func (row orderRow) toAdminDTO() OrderDTO {
|
||||
PlatformFeeCent: &platformFeeCent,
|
||||
RentOriginalAmountCent: effectiveRentOriginalAmountCent(row.RentalOrder),
|
||||
RentDiscountAmountCent: row.RentDiscountAmountCent,
|
||||
PureCoinOriginalAmountCent: orderPureCoinOriginalAmountCent(row.RentalOrder),
|
||||
ExtraItemOriginalAmountCent: orderExtraItemOriginalAmountCent(row.RentalOrder),
|
||||
ActualCoinConsumedM: row.ActualCoinConsumedM,
|
||||
ActualPureCoinAmountCent: row.ActualPureCoinAmountCent,
|
||||
ActualPureCoinDiscountCent: row.ActualPureCoinDiscountCent,
|
||||
RenterGrowthLevel: effectiveRenterGrowthLevel(row.RentalOrder),
|
||||
RenterGrowthLevelName: effectiveRenterGrowthLevelName(row.RentalOrder),
|
||||
RenterDiscountBps: effectiveRenterDiscountBps(row.RentalOrder),
|
||||
GrowthPointsBasisCent: row.GrowthPointsBasisCent,
|
||||
GrowthPointsPerYuan: row.GrowthPointsPerYuan,
|
||||
GrowthPointsAwarded: row.GrowthPointsAwarded,
|
||||
GrowthPointsAwardedAt: row.GrowthPointsAwardedAt,
|
||||
AccountSnapshot: row.AccountSnapshot,
|
||||
@@ -225,6 +232,9 @@ func toCheckoutAdminDTO(checkout model.OrderCheckout) CheckoutDTO {
|
||||
OwnerRentAmountCent: &ownerRentAmountCent,
|
||||
PlatformFeeCent: &platformFeeCent,
|
||||
DepositAmountCent: checkout.DepositAmountCent,
|
||||
PureCoinAmountCent: checkout.PureCoinAmountCent,
|
||||
PureCoinDiscountCent: checkout.PureCoinDiscountCent,
|
||||
PureCoinPayableCent: checkout.PureCoinPayableCent,
|
||||
ConsumableAmountCent: checkout.ConsumableAmountCent,
|
||||
CoinConsumedM: checkout.CoinConsumedM,
|
||||
OtherAmountCent: checkout.OtherAmountCent,
|
||||
@@ -315,11 +325,18 @@ func applyOrderPriceView(dto *OrderDTO, order model.RentalOrder, userID uint64)
|
||||
dto.OwnerRentAmountCent = &ownerAmountCent
|
||||
dto.RentOriginalAmountCent = 0
|
||||
dto.RentDiscountAmountCent = 0
|
||||
dto.PureCoinOriginalAmountCent = 0
|
||||
dto.ExtraItemOriginalAmountCent = 0
|
||||
dto.ActualCoinConsumedM = 0
|
||||
dto.ActualPureCoinAmountCent = 0
|
||||
dto.ActualPureCoinDiscountCent = 0
|
||||
dto.RenterGrowthLevel = ""
|
||||
dto.RenterGrowthLevelName = ""
|
||||
dto.RenterDiscountBps = 0
|
||||
dto.GrowthPointsAwarded = 0
|
||||
dto.GrowthPointsAwardedAt = nil
|
||||
dto.GrowthPointsBasisCent = 0
|
||||
dto.GrowthPointsPerYuan = 0
|
||||
sanitizeOrderSnapshot(&dto.AccountSnapshot, "owner")
|
||||
case userID == order.RenterID:
|
||||
rentAmountCent := order.RentAmountCent
|
||||
@@ -340,11 +357,18 @@ func applyOrderPriceView(dto *OrderDTO, order model.RentalOrder, userID uint64)
|
||||
dto.OwnerRentAmountCent = nil
|
||||
dto.RentOriginalAmountCent = 0
|
||||
dto.RentDiscountAmountCent = 0
|
||||
dto.PureCoinOriginalAmountCent = 0
|
||||
dto.ExtraItemOriginalAmountCent = 0
|
||||
dto.ActualCoinConsumedM = 0
|
||||
dto.ActualPureCoinAmountCent = 0
|
||||
dto.ActualPureCoinDiscountCent = 0
|
||||
dto.RenterGrowthLevel = ""
|
||||
dto.RenterGrowthLevelName = ""
|
||||
dto.RenterDiscountBps = 0
|
||||
dto.GrowthPointsAwarded = 0
|
||||
dto.GrowthPointsAwardedAt = nil
|
||||
dto.GrowthPointsBasisCent = 0
|
||||
dto.GrowthPointsPerYuan = 0
|
||||
sanitizeOrderSnapshot(&dto.AccountSnapshot, "")
|
||||
}
|
||||
}
|
||||
@@ -379,6 +403,9 @@ func applyCheckoutPriceView(dto *CheckoutDTO, order model.RentalOrder, userID ui
|
||||
dto.RentAmountCent = nil
|
||||
dto.OwnerRentAmountCent = &ownerAmountCent
|
||||
dto.OwnerIncomeAmountCent = &ownerIncomeAmountCent
|
||||
dto.PureCoinAmountCent = 0
|
||||
dto.PureCoinDiscountCent = 0
|
||||
dto.PureCoinPayableCent = 0
|
||||
case userID == order.RenterID:
|
||||
dto.PriceRole = "renter"
|
||||
dto.DisplayAmountCent = rentAmountCent
|
||||
@@ -390,6 +417,9 @@ func applyCheckoutPriceView(dto *CheckoutDTO, order model.RentalOrder, userID ui
|
||||
dto.DisplayAmountCent = 0
|
||||
dto.RentAmountCent = nil
|
||||
dto.OwnerRentAmountCent = nil
|
||||
dto.PureCoinAmountCent = 0
|
||||
dto.PureCoinDiscountCent = 0
|
||||
dto.PureCoinPayableCent = 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,15 +7,21 @@ import (
|
||||
"time"
|
||||
|
||||
"hfb_sys/backend/internal/model"
|
||||
"hfb_sys/backend/internal/modules/rentergrowth"
|
||||
"hfb_sys/backend/pkg/money"
|
||||
|
||||
"gorm.io/datatypes"
|
||||
)
|
||||
|
||||
type orderPricing struct {
|
||||
RentAmountCent int64
|
||||
OwnerRentAmountCent int64
|
||||
PlatformFeeCent int64
|
||||
RentAmountCent int64
|
||||
OwnerRentAmountCent int64
|
||||
PlatformFeeCent int64
|
||||
PureCoinAmountCent int64
|
||||
OwnerPureCoinAmountCent int64
|
||||
ExtraItemAmountCent int64
|
||||
OwnerExtraItemAmountCent int64
|
||||
PureCoinPlatformFeeCent int64
|
||||
}
|
||||
|
||||
type checkoutSettlement struct {
|
||||
@@ -27,10 +33,34 @@ type checkoutSettlement struct {
|
||||
RenterRefundCent int64
|
||||
PlatformFeeCent int64
|
||||
ActualRentAmountCent int64
|
||||
PureCoinAmountCent int64
|
||||
PureCoinDiscountCent int64
|
||||
PureCoinPayableCent int64
|
||||
ExtraItemAmountCent int64
|
||||
CoinConsumedM float64
|
||||
OvershootAmountCent int64
|
||||
ShortfallCent int64
|
||||
}
|
||||
|
||||
// ActualPureCoinAmounts 是跨正常结账与仲裁共用的最终纯币计价结果。
|
||||
type ActualPureCoinAmounts struct {
|
||||
AmountCent int64
|
||||
DiscountCent int64
|
||||
PayableCent int64
|
||||
ConsumedM float64
|
||||
}
|
||||
|
||||
// CalculateActualPureCoinAmounts 按订单价格快照和实际消耗 M 计算纯币原价及等级优惠。
|
||||
func CalculateActualPureCoinAmounts(order model.RentalOrder, coinConsumedM float64) ActualPureCoinAmounts {
|
||||
settlement := calculateCheckoutSettlement(order, 0, coinConsumedM, 0)
|
||||
return ActualPureCoinAmounts{
|
||||
AmountCent: settlement.PureCoinAmountCent,
|
||||
DiscountCent: settlement.PureCoinDiscountCent,
|
||||
PayableCent: settlement.PureCoinPayableCent,
|
||||
ConsumedM: settlement.CoinConsumedM,
|
||||
}
|
||||
}
|
||||
|
||||
func orderDurationHours(order model.RentalOrder) int {
|
||||
if order.EstimatedDurationHours > 0 {
|
||||
return order.EstimatedDurationHours
|
||||
@@ -85,10 +115,34 @@ func buildOrderPricing(listing model.RentalListing, account model.GameAccount) o
|
||||
if platformFeeCent < 0 {
|
||||
platformFeeCent = 0
|
||||
}
|
||||
pureCoinAmountCent := int64(math.Round(readSnapshotPrice(account.AssetSummary, "buyer_coin_base_price") * 100))
|
||||
extraItemSnapshotCent := int64(math.Round(readSnapshotPrice(account.AssetSummary, "consumable_price") * 100))
|
||||
if pureCoinAmountCent <= 0 {
|
||||
if extraItemSnapshotCent > 0 && extraItemSnapshotCent <= rentAmountCent {
|
||||
pureCoinAmountCent = rentAmountCent - extraItemSnapshotCent
|
||||
} else {
|
||||
pureCoinAmountCent = rentAmountCent
|
||||
}
|
||||
}
|
||||
if pureCoinAmountCent < 0 || pureCoinAmountCent > rentAmountCent {
|
||||
pureCoinAmountCent = rentAmountCent
|
||||
}
|
||||
extraItemAmountCent := maxCent(rentAmountCent-pureCoinAmountCent, 0)
|
||||
ownerPureCoinAmountCent := int64(math.Round(readSnapshotPrice(account.AssetSummary, "seller_coin_base_price") * 100))
|
||||
if ownerPureCoinAmountCent <= 0 || ownerPureCoinAmountCent > ownerRentAmountCent {
|
||||
ownerPureCoinAmountCent = maxCent(ownerRentAmountCent-minCent(extraItemAmountCent, ownerRentAmountCent), 0)
|
||||
}
|
||||
ownerExtraItemAmountCent := maxCent(ownerRentAmountCent-ownerPureCoinAmountCent, 0)
|
||||
pureCoinPlatformFeeCent := maxCent(pureCoinAmountCent-ownerPureCoinAmountCent, 0)
|
||||
return orderPricing{
|
||||
RentAmountCent: rentAmountCent,
|
||||
OwnerRentAmountCent: ownerRentAmountCent,
|
||||
PlatformFeeCent: platformFeeCent,
|
||||
RentAmountCent: rentAmountCent,
|
||||
OwnerRentAmountCent: ownerRentAmountCent,
|
||||
PlatformFeeCent: platformFeeCent,
|
||||
PureCoinAmountCent: pureCoinAmountCent,
|
||||
OwnerPureCoinAmountCent: ownerPureCoinAmountCent,
|
||||
ExtraItemAmountCent: extraItemAmountCent,
|
||||
OwnerExtraItemAmountCent: ownerExtraItemAmountCent,
|
||||
PureCoinPlatformFeeCent: pureCoinPlatformFeeCent,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,6 +216,9 @@ func buildCheckout(order model.RentalOrder, initiatedBy uint64, status string, c
|
||||
OwnerRentAmountCent: settlement.OwnerRentIncomeCent,
|
||||
PlatformFeeCent: settlement.PlatformFeeCent,
|
||||
DepositAmountCent: order.DepositAmountCent,
|
||||
PureCoinAmountCent: settlement.PureCoinAmountCent,
|
||||
PureCoinDiscountCent: settlement.PureCoinDiscountCent,
|
||||
PureCoinPayableCent: settlement.PureCoinPayableCent,
|
||||
ConsumableAmountCent: consumableAmountCent,
|
||||
CoinConsumedM: roundQuantity(coinConsumedM),
|
||||
OtherAmountCent: otherAmountCent,
|
||||
@@ -185,18 +242,13 @@ func calculateCheckoutSettlement(order model.RentalOrder, consumableAmountCent i
|
||||
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
|
||||
}
|
||||
buyerCoinBasePriceCent := orderPureCoinOriginalAmountCent(order)
|
||||
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)
|
||||
ownerExtraItemCent := minCent(orderExtraItemOriginalAmountCent(order), orderOwnerRentAmountCent)
|
||||
sellerCoinBasePriceCent = maxCent(orderOwnerRentAmountCent-ownerExtraItemCent, 0)
|
||||
}
|
||||
prepaidConsumablePriceCent := orderExtraItemOriginalAmountCent(order)
|
||||
prepaidOwnerConsumablePriceCent := maxCent(orderOwnerRentAmountCent-sellerCoinBasePriceCent, 0)
|
||||
|
||||
consumedM := roundQuantity(coinConsumedM)
|
||||
@@ -243,7 +295,10 @@ func calculateCheckoutSettlement(order model.RentalOrder, consumableAmountCent i
|
||||
usedOwnerConsumablePriceCent = minCent(usedOwnerConsumablePriceCent, prepaidOwnerConsumablePriceCent)
|
||||
}
|
||||
|
||||
actualBuyerRentCent := usedBuyerCoinPriceCent + usedBuyerConsumablePriceCent
|
||||
pureCoinPlatformFeeCent := maxCent(usedBuyerCoinPriceCent-usedOwnerCoinPriceCent, 0)
|
||||
pureCoinDiscountCent := rentergrowth.CalculateDiscountCent(usedBuyerCoinPriceCent, pureCoinPlatformFeeCent, effectiveRenterDiscountBps(order))
|
||||
pureCoinPayableCent := maxCent(usedBuyerCoinPriceCent-pureCoinDiscountCent, 0)
|
||||
actualBuyerRentCent := pureCoinPayableCent + usedBuyerConsumablePriceCent
|
||||
actualOwnerRentCent := usedOwnerCoinPriceCent + usedOwnerConsumablePriceCent
|
||||
if actualOwnerRentCent > actualBuyerRentCent {
|
||||
actualOwnerRentCent = actualBuyerRentCent
|
||||
@@ -278,11 +333,44 @@ func calculateCheckoutSettlement(order model.RentalOrder, consumableAmountCent i
|
||||
RenterRefundCent: rentRefundCent + depositRefundCent,
|
||||
PlatformFeeCent: platformFeeCent,
|
||||
ActualRentAmountCent: actualRentAmountCent,
|
||||
PureCoinAmountCent: usedBuyerCoinPriceCent,
|
||||
PureCoinDiscountCent: pureCoinDiscountCent,
|
||||
PureCoinPayableCent: pureCoinPayableCent,
|
||||
ExtraItemAmountCent: usedBuyerConsumablePriceCent,
|
||||
CoinConsumedM: consumedM,
|
||||
OvershootAmountCent: overshootCent,
|
||||
ShortfallCent: shortfallCent,
|
||||
}
|
||||
}
|
||||
|
||||
func orderPureCoinOriginalAmountCent(order model.RentalOrder) int64 {
|
||||
originalRentCent := effectiveRentOriginalAmountCent(order)
|
||||
if order.PureCoinOriginalAmountCent > 0 {
|
||||
return minCent(order.PureCoinOriginalAmountCent, originalRentCent)
|
||||
}
|
||||
if order.ExtraItemOriginalAmountCent > 0 {
|
||||
return maxCent(originalRentCent-minCent(order.ExtraItemOriginalAmountCent, originalRentCent), 0)
|
||||
}
|
||||
pureCoinCent := int64(math.Round(readOrderSnapshotPrice(order.AccountSnapshot, "buyer_coin_base_price") * 100))
|
||||
if pureCoinCent > 0 && pureCoinCent <= originalRentCent {
|
||||
return pureCoinCent
|
||||
}
|
||||
extraItemCent := int64(math.Round(readOrderSnapshotPrice(order.AccountSnapshot, "consumable_price") * 100))
|
||||
if extraItemCent > 0 && extraItemCent <= originalRentCent {
|
||||
return originalRentCent - extraItemCent
|
||||
}
|
||||
return originalRentCent
|
||||
}
|
||||
|
||||
func orderExtraItemOriginalAmountCent(order model.RentalOrder) int64 {
|
||||
originalRentCent := effectiveRentOriginalAmountCent(order)
|
||||
if order.ExtraItemOriginalAmountCent > 0 {
|
||||
return minCent(order.ExtraItemOriginalAmountCent, originalRentCent)
|
||||
}
|
||||
pureCoinCent := orderPureCoinOriginalAmountCent(order)
|
||||
return maxCent(originalRentCent-pureCoinCent, 0)
|
||||
}
|
||||
|
||||
func readOrderSnapshotCoinM(raw datatypes.JSON) float64 {
|
||||
if len(raw) == 0 {
|
||||
return 0
|
||||
|
||||
@@ -162,7 +162,15 @@ func TestRepositoryCreateAppliesRenterGrowthDiscount(t *testing.T) {
|
||||
ServerRegion: "国服",
|
||||
LoginPlatform: "steam",
|
||||
Title: "测试账号",
|
||||
AssetSummary: datatypes.JSON([]byte(`{"price_breakdown":{"seller_total_price":90,"platform_markup_amount":10}}`)),
|
||||
AssetSummary: datatypes.JSON([]byte(`{
|
||||
"price_breakdown": {
|
||||
"buyer_coin_base_price": 263,
|
||||
"seller_coin_base_price": 233,
|
||||
"consumable_price": 120,
|
||||
"seller_total_price": 353,
|
||||
"platform_markup_amount": 30
|
||||
}
|
||||
}`)),
|
||||
}
|
||||
if err := db.Create(&account).Error; err != nil {
|
||||
t.Fatalf("create account failed: %v", err)
|
||||
@@ -170,7 +178,7 @@ func TestRepositoryCreateAppliesRenterGrowthDiscount(t *testing.T) {
|
||||
listing := model.RentalListing{
|
||||
AccountID: account.ID,
|
||||
OwnerID: owner.ID,
|
||||
PriceCent: 10000,
|
||||
PriceCent: 38300,
|
||||
DepositAmountCent: 100000,
|
||||
Status: listingStatusPublished,
|
||||
ReviewStatus: listingReviewStatusApproved,
|
||||
@@ -183,11 +191,14 @@ func TestRepositoryCreateAppliesRenterGrowthDiscount(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Create() error = %v", err)
|
||||
}
|
||||
if dto.RentAmountCent == nil || *dto.RentAmountCent != 9900 {
|
||||
t.Fatalf("rent amount = %v, want 9900", dto.RentAmountCent)
|
||||
if dto.RentAmountCent == nil || *dto.RentAmountCent != 38037 {
|
||||
t.Fatalf("rent amount = %v, want 38037", dto.RentAmountCent)
|
||||
}
|
||||
if dto.RentOriginalAmountCent != 10000 || dto.RentDiscountAmountCent != 100 {
|
||||
t.Fatalf("discount snapshot = original %d discount %d, want 10000/100", dto.RentOriginalAmountCent, dto.RentDiscountAmountCent)
|
||||
if dto.RentOriginalAmountCent != 38300 || dto.RentDiscountAmountCent != 263 {
|
||||
t.Fatalf("discount snapshot = original %d discount %d, want 38300/263", dto.RentOriginalAmountCent, dto.RentDiscountAmountCent)
|
||||
}
|
||||
if dto.PureCoinOriginalAmountCent != 26300 || dto.ExtraItemOriginalAmountCent != 12000 {
|
||||
t.Fatalf("pure coin/extra snapshot = %d/%d, want 26300/12000", dto.PureCoinOriginalAmountCent, dto.ExtraItemOriginalAmountCent)
|
||||
}
|
||||
if dto.RenterGrowthLevel != "platinum" || dto.RenterGrowthLevelName != "铂金" || dto.RenterDiscountBps != 9900 {
|
||||
t.Fatalf("growth snapshot = %s/%s/%d, want platinum/铂金/9900", dto.RenterGrowthLevel, dto.RenterGrowthLevelName, dto.RenterDiscountBps)
|
||||
@@ -196,8 +207,8 @@ func TestRepositoryCreateAppliesRenterGrowthDiscount(t *testing.T) {
|
||||
if err := db.First(&saved, dto.ID).Error; err != nil {
|
||||
t.Fatalf("find saved order failed: %v", err)
|
||||
}
|
||||
if saved.OwnerRentAmountCent != 9000 || saved.PlatformFeeCent != 900 {
|
||||
t.Fatalf("pricing = owner %d platform %d, want 9000/900", saved.OwnerRentAmountCent, saved.PlatformFeeCent)
|
||||
if saved.OwnerRentAmountCent != 35300 || saved.PlatformFeeCent != 2737 {
|
||||
t.Fatalf("pricing = owner %d platform %d, want 35300/2737", saved.OwnerRentAmountCent, saved.PlatformFeeCent)
|
||||
}
|
||||
if saved.DepositAmountCent != 30000 || saved.DepositWaivedAmountCent != 70000 {
|
||||
t.Fatalf("deposit = paid %d waived %d, want 30000/70000", saved.DepositAmountCent, saved.DepositWaivedAmountCent)
|
||||
|
||||
@@ -5,11 +5,116 @@ import (
|
||||
"time"
|
||||
|
||||
"hfb_sys/backend/internal/model"
|
||||
"hfb_sys/backend/internal/modules/rentergrowth"
|
||||
"hfb_sys/backend/internal/timeutil"
|
||||
|
||||
"gorm.io/datatypes"
|
||||
)
|
||||
|
||||
func pureCoinDiscountOrder() model.RentalOrder {
|
||||
return model.RentalOrder{
|
||||
RentAmountCent: 38037,
|
||||
OwnerRentAmountCent: 35300,
|
||||
DepositAmountCent: 15000,
|
||||
RentOriginalAmountCent: 38300,
|
||||
RentDiscountAmountCent: 263,
|
||||
PureCoinOriginalAmountCent: 26300,
|
||||
ExtraItemOriginalAmountCent: 12000,
|
||||
RenterDiscountBps: 9900,
|
||||
AccountSnapshot: datatypes.JSON([]byte(`{
|
||||
"haf_coin_amount": 100000000,
|
||||
"asset_summary": {
|
||||
"price_breakdown": {
|
||||
"buyer_coin_base_price": 263,
|
||||
"seller_coin_base_price": 233,
|
||||
"consumable_price": 120
|
||||
}
|
||||
}
|
||||
}`)),
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateCheckoutSettlementUsesOnlyActualPureCoinForDiscount(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
consumableAmountCent int64
|
||||
coinConsumedM float64
|
||||
wantPureCoinCent int64
|
||||
wantDiscountCent int64
|
||||
wantActualRentCent int64
|
||||
wantOvershootCent int64
|
||||
}{
|
||||
{
|
||||
name: "全量纯币和额外物品",
|
||||
consumableAmountCent: 12000,
|
||||
coinConsumedM: 100,
|
||||
wantPureCoinCent: 26300,
|
||||
wantDiscountCent: 263,
|
||||
wantActualRentCent: 38037,
|
||||
wantOvershootCent: 0,
|
||||
},
|
||||
{
|
||||
name: "部分纯币和全量额外物品",
|
||||
consumableAmountCent: 12000,
|
||||
coinConsumedM: 50,
|
||||
wantPureCoinCent: 13150,
|
||||
wantDiscountCent: 132,
|
||||
wantActualRentCent: 25018,
|
||||
wantOvershootCent: 0,
|
||||
},
|
||||
{
|
||||
name: "只使用额外物品",
|
||||
consumableAmountCent: 12000,
|
||||
coinConsumedM: 0,
|
||||
wantPureCoinCent: 0,
|
||||
wantDiscountCent: 0,
|
||||
wantActualRentCent: 12000,
|
||||
wantOvershootCent: 0,
|
||||
},
|
||||
{
|
||||
name: "纯币打超仍参与优惠",
|
||||
consumableAmountCent: 12000,
|
||||
coinConsumedM: 110,
|
||||
wantPureCoinCent: 28930,
|
||||
wantDiscountCent: 290,
|
||||
wantActualRentCent: 40640,
|
||||
wantOvershootCent: 2603,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
settlement := calculateCheckoutSettlement(pureCoinDiscountOrder(), test.consumableAmountCent, test.coinConsumedM, 0)
|
||||
if settlement.PureCoinAmountCent != test.wantPureCoinCent || settlement.PureCoinDiscountCent != test.wantDiscountCent {
|
||||
t.Fatalf("纯币原价/优惠 = %d/%d, want %d/%d", settlement.PureCoinAmountCent, settlement.PureCoinDiscountCent, test.wantPureCoinCent, test.wantDiscountCent)
|
||||
}
|
||||
if settlement.ActualRentAmountCent != test.wantActualRentCent || settlement.OvershootAmountCent != test.wantOvershootCent {
|
||||
t.Fatalf("实际租金/打超 = %d/%d, want %d/%d", settlement.ActualRentAmountCent, settlement.OvershootAmountCent, test.wantActualRentCent, test.wantOvershootCent)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildOrderPricingDoesNotDiscountAllExtraItemOrder(t *testing.T) {
|
||||
pricing := buildOrderPricing(
|
||||
model.RentalListing{PriceCent: 12000},
|
||||
model.GameAccount{AssetSummary: datatypes.JSON([]byte(`{
|
||||
"price_breakdown": {
|
||||
"buyer_coin_base_price": 0,
|
||||
"seller_coin_base_price": 0,
|
||||
"consumable_price": 120,
|
||||
"seller_total_price": 120
|
||||
}
|
||||
}`))},
|
||||
)
|
||||
if pricing.PureCoinAmountCent != 0 || pricing.ExtraItemAmountCent != 12000 || pricing.PureCoinPlatformFeeCent != 0 {
|
||||
t.Fatalf("全额外物品订单纯币/额外物品/纯币差价 = %d/%d/%d, want 0/12000/0", pricing.PureCoinAmountCent, pricing.ExtraItemAmountCent, pricing.PureCoinPlatformFeeCent)
|
||||
}
|
||||
if discount := rentergrowth.CalculateDiscountCent(pricing.PureCoinAmountCent, pricing.PureCoinPlatformFeeCent, 9900); discount != 0 {
|
||||
t.Fatalf("全额外物品订单优惠 = %d, want 0", discount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateCheckoutSettlementRefundsUnusedRent(t *testing.T) {
|
||||
order := model.RentalOrder{
|
||||
RentAmountCent: 38300,
|
||||
|
||||
Reference in New Issue
Block a user