优化仲裁

This commit is contained in:
yml2213
2026-07-29 18:59:07 +08:00
parent c9d0803b53
commit 8c2431c4f2
15 changed files with 387 additions and 142 deletions
+168 -48
View File
@@ -53,7 +53,7 @@ func (r *Repository) Arbitrate(ctx context.Context, adminID uint64, id uint64, r
if err != nil {
return err
}
settlement, err := buildArbitrationSettlement(order, req, frozenBalance)
settlement, actualSettlement, linkedCheckout, depositDeductAmountCent, err := buildArbitrationDecision(tx, row, order, req, frozenBalance)
if err != nil {
return err
}
@@ -114,23 +114,16 @@ func (r *Repository) Arbitrate(ctx context.Context, adminID uint64, id uint64, r
return err
}
if order.Status == "completed" {
coinConsumedM, linkedCheckout, err := arbitrationCoinConsumedM(tx, row, req)
if err != nil {
return err
if actualSettlement == nil {
return ErrInvalidDispute
}
pureCoin := ordermodule.CalculateActualPureCoinAmounts(order, coinConsumedM)
order.ActualCoinConsumedM = pureCoin.ConsumedM
order.ActualPureCoinAmountCent = pureCoin.AmountCent
order.ActualPureCoinDiscountCent = pureCoin.DiscountCent
applyArbitrationActualSettlement(&order, linkedCheckout, *actualSettlement, depositDeductAmountCent)
if linkedCheckout != nil {
linkedCheckout.PureCoinAmountCent = pureCoin.AmountCent
linkedCheckout.PureCoinDiscountCent = pureCoin.DiscountCent
linkedCheckout.PureCoinPayableCent = pureCoin.PayableCent
if err := tx.Save(linkedCheckout).Error; err != nil {
return err
}
}
if err := rentergrowth.AwardOrderCompleted(tx, &order, pureCoin.AmountCent, rentergrowth.SourceOrderCompleted); err != nil {
if err := rentergrowth.AwardOrderCompleted(tx, &order, actualSettlement.PureCoinAmountCent, rentergrowth.SourceOrderCompleted); err != nil {
return err
}
}
@@ -166,6 +159,7 @@ func (r *Repository) Arbitrate(ctx context.Context, adminID uint64, id uint64, r
"deposit_deduct_amount_cent": settlement.DepositDeductAmountCent,
"actual_coin_consumed_m": order.ActualCoinConsumedM,
"actual_pure_coin_cent": order.ActualPureCoinAmountCent,
"actual_extra_item_cent": order.ActualExtraItemAmountCent,
"before_order_status": beforeOrderStatus,
"after_order_status": order.Status,
"before_handoff_status": beforeHandoffStatus,
@@ -220,22 +214,131 @@ func (r *Repository) Arbitrate(ctx context.Context, adminID uint64, id uint64, r
return &dto, nil
}
func arbitrationCoinConsumedM(tx *gorm.DB, row model.Dispute, req ArbitrateRequest) (float64, *model.OrderCheckout, error) {
var checkout *model.OrderCheckout
if row.CheckoutID != nil && *row.CheckoutID > 0 {
var saved model.OrderCheckout
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&saved, *row.CheckoutID).Error; err != nil {
return 0, nil, err
}
checkout = &saved
func buildArbitrationDecision(tx *gorm.DB, row model.Dispute, order model.RentalOrder, req ArbitrateRequest, renterFrozenBalanceCent int64) (arbitrationSettlement, *ordermodule.ActualCheckoutSettlement, *model.OrderCheckout, int64, error) {
if arbitrationUsesActualCheckout(req.Result) {
return buildActualCheckoutArbitrationSettlement(tx, row, order, req, renterFrozenBalanceCent)
}
settlement, err := buildArbitrationSettlement(order, req, renterFrozenBalanceCent)
return settlement, nil, nil, 0, err
}
func arbitrationUsesActualCheckout(result string) bool {
switch result {
case "release_deposit", "deduct_deposit", "compensate_owner":
return true
default:
return false
}
}
func buildActualCheckoutArbitrationSettlement(tx *gorm.DB, row model.Dispute, order model.RentalOrder, req ArbitrateRequest, renterFrozenBalanceCent int64) (arbitrationSettlement, *ordermodule.ActualCheckoutSettlement, *model.OrderCheckout, int64, error) {
settlement := newArbitrationSettlement(order, renterFrozenBalanceCent)
linkedCheckout, err := lockArbitrationCheckout(tx, row)
if err != nil {
return settlement, nil, nil, 0, err
}
coinConsumedM, err := arbitrationCoinConsumedM(linkedCheckout, req)
if err != nil {
return settlement, nil, linkedCheckout, 0, err
}
consumableAmountCent, err := arbitrationConsumableAmountCent(order, linkedCheckout, req)
if err != nil {
return settlement, nil, linkedCheckout, 0, err
}
depositDeductAmountCent, err := arbitrationDepositDeductAmountCent(order, req)
if err != nil {
return settlement, nil, linkedCheckout, 0, err
}
actualSettlement := ordermodule.CalculateActualCheckoutSettlement(order, consumableAmountCent, coinConsumedM, depositDeductAmountCent)
if actualSettlement.ShortfallCent > 0 {
return settlement, nil, linkedCheckout, 0, ErrInvalidDispute
}
if actualSettlement.OwnerIncomeCent > 0 {
settlement.addOwnerIncome(order, actualSettlement.OwnerIncomeCent, "仲裁按实际消耗结算给号主")
}
if actualSettlement.RenterRefundCent > 0 {
settlement.RenterRefundAmountCent = actualSettlement.RenterRefundCent
settlement.RenterDepositRefundCent = actualSettlement.DepositRefundCent
}
settlement.DepositDeductAmountCent = depositDeductAmountCent + actualSettlement.OvershootAmountCent
settlement.ActualExtraItemAmountCent = actualSettlement.ExtraItemAmountCent
return settlement, &actualSettlement, linkedCheckout, depositDeductAmountCent, nil
}
func lockArbitrationCheckout(tx *gorm.DB, row model.Dispute) (*model.OrderCheckout, error) {
if row.CheckoutID == nil || *row.CheckoutID == 0 {
return nil, nil
}
var checkout model.OrderCheckout
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&checkout, *row.CheckoutID).Error; err != nil {
return nil, err
}
return &checkout, nil
}
func arbitrationCoinConsumedM(checkout *model.OrderCheckout, req ArbitrateRequest) (float64, error) {
if req.ActualCoinConsumedM != nil {
return *req.ActualCoinConsumedM, checkout, nil
return *req.ActualCoinConsumedM, nil
}
if checkout != nil {
return checkout.CoinConsumedM, checkout, nil
return checkout.CoinConsumedM, nil
}
return 0, nil, ErrInvalidDispute
return 0, ErrInvalidDispute
}
func arbitrationConsumableAmountCent(order model.RentalOrder, checkout *model.OrderCheckout, req ArbitrateRequest) (int64, error) {
if req.ActualConsumableAmountCent != nil {
return *req.ActualConsumableAmountCent, nil
}
if checkout != nil {
return checkout.ConsumableAmountCent, nil
}
if order.ExtraItemOriginalAmountCent <= 0 {
return 0, nil
}
return 0, ErrInvalidDispute
}
func arbitrationDepositDeductAmountCent(order model.RentalOrder, req ArbitrateRequest) (int64, error) {
switch req.Result {
case "release_deposit":
return 0, nil
case "deduct_deposit", "compensate_owner":
deductAmountCent := req.AmountCent
if deductAmountCent <= 0 {
deductAmountCent = order.DepositAmountCent
}
if deductAmountCent > order.DepositAmountCent {
return 0, ErrInvalidDispute
}
return deductAmountCent, nil
default:
return 0, ErrInvalidDispute
}
}
func applyArbitrationActualSettlement(order *model.RentalOrder, checkout *model.OrderCheckout, settlement ordermodule.ActualCheckoutSettlement, depositDeductAmountCent int64) {
order.ActualCoinConsumedM = settlement.CoinConsumedM
order.ActualPureCoinAmountCent = settlement.PureCoinAmountCent
order.ActualPureCoinDiscountCent = settlement.PureCoinDiscountCent
order.ActualExtraItemAmountCent = settlement.ExtraItemAmountCent
if checkout == nil {
return
}
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.ConsumableAmountCent = settlement.ExtraItemAmountCent
checkout.CoinConsumedM = settlement.CoinConsumedM
checkout.DepositDeductAmountCent = depositDeductAmountCent
checkout.OtherAmountCent = depositDeductAmountCent
checkout.RenterRefundAmountCent = settlement.RenterRefundCent
checkout.OwnerIncomeAmountCent = settlement.OwnerIncomeCent
checkout.ShortfallCent = settlement.ShortfallCent
checkout.OvershootAmountCent = settlement.OvershootAmountCent
}
func isPlatformManagedOrder(order model.RentalOrder) bool {
@@ -275,24 +378,18 @@ func applyPlatformManagedSettlement(order *model.RentalOrder, settlement *arbitr
}
type arbitrationSettlement struct {
Entries []wallet.Entry
RenterRefundAmountCent int64
RenterDepositRefundCent int64
OwnerIncomeAmountCent int64
DepositDeductAmountCent int64
Entries []wallet.Entry
RenterRefundAmountCent int64
RenterDepositRefundCent int64
OwnerIncomeAmountCent int64
DepositDeductAmountCent int64
ActualExtraItemAmountCent int64
}
func buildArbitrationSettlement(order model.RentalOrder, req ArbitrateRequest, renterFrozenBalanceCent int64) (arbitrationSettlement, error) {
rentAmountCent := order.RentAmountCent
depositAmountCent := order.DepositAmountCent
ownerRentAmountCent := order.OwnerRentAmountCent
if ownerRentAmountCent <= 0 || ownerRentAmountCent > rentAmountCent {
ownerRentAmountCent = rentAmountCent
}
totalCent := rentAmountCent + depositAmountCent
func newArbitrationSettlement(order model.RentalOrder, renterFrozenBalanceCent int64) arbitrationSettlement {
settlement := arbitrationSettlement{}
orderID := order.ID
releaseFrozenAmountCent := money.MinCent(totalCent, renterFrozenBalanceCent)
releaseFrozenAmountCent := money.MinCent(order.RentAmountCent+order.DepositAmountCent, renterFrozenBalanceCent)
if releaseFrozenAmountCent > 0 {
settlement.Entries = append(settlement.Entries, wallet.Entry{
UserID: order.RenterID,
@@ -305,6 +402,36 @@ func buildArbitrationSettlement(order model.RentalOrder, req ArbitrateRequest, r
Remark: "仲裁释放冻结金额",
})
}
return settlement
}
func (settlement *arbitrationSettlement) addOwnerIncome(order model.RentalOrder, amountCent int64, remark string) {
if settlement == nil || amountCent <= 0 {
return
}
orderID := order.ID
settlement.OwnerIncomeAmountCent += amountCent
settlement.Entries = append(settlement.Entries, wallet.Entry{
UserID: order.OwnerID,
OrderID: &orderID,
Direction: "in",
AmountCent: amountCent,
BalanceType: "available",
BizType: "arbitration_owner_income",
BizNo: order.OrderNo,
Remark: remark,
})
}
func buildArbitrationSettlement(order model.RentalOrder, req ArbitrateRequest, renterFrozenBalanceCent int64) (arbitrationSettlement, error) {
rentAmountCent := order.RentAmountCent
depositAmountCent := order.DepositAmountCent
ownerRentAmountCent := order.OwnerRentAmountCent
if ownerRentAmountCent <= 0 || ownerRentAmountCent > rentAmountCent {
ownerRentAmountCent = rentAmountCent
}
totalCent := rentAmountCent + depositAmountCent
settlement := newArbitrationSettlement(order, renterFrozenBalanceCent)
addRenterRefund := func(amountCent int64, remark string) {
if amountCent <= 0 {
@@ -316,17 +443,7 @@ func buildArbitrationSettlement(order model.RentalOrder, req ArbitrateRequest, r
if amountCent <= 0 {
return
}
settlement.OwnerIncomeAmountCent += amountCent
settlement.Entries = append(settlement.Entries, wallet.Entry{
UserID: order.OwnerID,
OrderID: &orderID,
Direction: "in",
AmountCent: amountCent,
BalanceType: "available",
BizType: "arbitration_owner_income",
BizNo: order.OrderNo,
Remark: remark,
})
settlement.addOwnerIncome(order, amountCent, remark)
}
switch req.Result {
@@ -503,6 +620,9 @@ func buildArbitrationHandoffContent(req ArbitrateRequest, settlement arbitration
if settlement.DepositDeductAmountCent > 0 {
content += "\n押金赔付扣除:" + money.FormatWithSymbol(settlement.DepositDeductAmountCent)
}
if settlement.ActualExtraItemAmountCent > 0 {
content += "\n额外物品消耗:" + money.FormatWithSymbol(settlement.ActualExtraItemAmountCent)
}
return content
}