统一金额分制重构

This commit is contained in:
yml
2026-06-09 19:04:11 +08:00
parent 87673288ef
commit 5cd3ead4bd
69 changed files with 886 additions and 1277 deletions
+63 -83
View File
@@ -4,7 +4,6 @@ import (
"encoding/json"
"errors"
"fmt"
"math"
"time"
"hfb_sys/backend/internal/auditlog"
@@ -249,9 +248,8 @@ func (r *Repository) Arbitrate(adminID uint64, id uint64, req ArbitrateRequest,
if err := wallet.AppendEntries(tx, settlement.Entries...); err != nil {
return err
}
if settlement.RenterRefundAmount > 0 {
refundCent := int64(math.Round(settlement.RenterRefundAmount * 100))
action, err := r.prepareRefund(&order, refundCent, "arbitration_refund", "仲裁退款原路退还")
if settlement.RenterRefundAmountCent > 0 {
action, err := r.prepareRefund(&order, settlement.RenterRefundAmountCent, "arbitration_refund", "仲裁退款原路退还")
if err != nil {
return err
}
@@ -281,25 +279,25 @@ func (r *Repository) Arbitrate(adminID uint64, id uint64, req ArbitrateRequest,
}
disputeID := row.ID
if err := appendAuditLog(tx, adminID, "dispute.arbitrate", "dispute", row.ID, meta, map[string]any{
"dispute_id": row.ID,
"order_id": order.ID,
"order_no": order.OrderNo,
"result": req.Result,
"remark": req.Remark,
"input_amount": req.Amount,
"renter_refund_amount": settlement.RenterRefundAmount,
"owner_income_amount": settlement.OwnerIncomeAmount,
"deposit_deduct_amount": settlement.DepositDeductAmount,
"before_order_status": beforeOrderStatus,
"after_order_status": order.Status,
"before_handoff_status": beforeHandoffStatus,
"after_handoff_status": order.HandoffStatus,
"before_settlement_status": beforeSettlementStatus,
"after_settlement_status": order.SettlementStatus,
"before_listing_status": beforeListingStatus,
"after_listing_status": listing.Status,
"before_account_status": beforeAccountStatus,
"after_account_status": account.Status,
"dispute_id": row.ID,
"order_id": order.ID,
"order_no": order.OrderNo,
"result": req.Result,
"remark": req.Remark,
"input_amount_cent": req.AmountCent,
"renter_refund_amount_cent": settlement.RenterRefundAmountCent,
"owner_income_amount_cent": settlement.OwnerIncomeAmountCent,
"deposit_deduct_amount_cent": settlement.DepositDeductAmountCent,
"before_order_status": beforeOrderStatus,
"after_order_status": order.Status,
"before_handoff_status": beforeHandoffStatus,
"after_handoff_status": order.HandoffStatus,
"before_settlement_status": beforeSettlementStatus,
"after_settlement_status": order.SettlementStatus,
"before_listing_status": beforeListingStatus,
"after_listing_status": listing.Status,
"before_account_status": beforeAccountStatus,
"after_account_status": account.Status,
}); err != nil {
return err
}
@@ -338,36 +336,29 @@ func (r *Repository) Arbitrate(adminID uint64, id uint64, req ArbitrateRequest,
}
type arbitrationSettlement struct {
Entries []wallet.Entry
RenterRefundAmount float64
OwnerIncomeAmount float64
DepositDeductAmount float64
Entries []wallet.Entry
RenterRefundAmountCent int64
OwnerIncomeAmountCent int64
DepositDeductAmountCent int64
}
func buildArbitrationSettlement(order model.RentalOrder, req ArbitrateRequest, renterFrozenBalance float64) (arbitrationSettlement, error) {
rentAmount := float64(order.RentAmountCent) / 100
if rentAmount <= 0 {
rentAmount = order.RentAmount
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
}
depositAmount := float64(order.DepositAmountCent) / 100
if depositAmount <= 0 {
depositAmount = order.DepositAmount
}
ownerRentAmount := float64(order.OwnerRentAmountCent) / 100
if ownerRentAmount <= 0 || ownerRentAmount > rentAmount {
ownerRentAmount = rentAmount
}
total := roundMoney(rentAmount + depositAmount)
ownerRentAmount = roundMoney(ownerRentAmount)
totalCent := rentAmountCent + depositAmountCent
settlement := arbitrationSettlement{}
orderID := order.ID
releaseFrozenAmount := minMoney(total, roundMoney(renterFrozenBalance))
if releaseFrozenAmount > 0 {
releaseFrozenAmountCent := money.MinCent(totalCent, renterFrozenBalanceCent)
if releaseFrozenAmountCent > 0 {
settlement.Entries = append(settlement.Entries, wallet.Entry{
UserID: order.RenterID,
OrderID: &orderID,
Direction: "out",
AmountCent: int64(math.Round(releaseFrozenAmount * 100)),
AmountCent: releaseFrozenAmountCent,
BalanceType: "frozen",
BizType: "arbitration_release_frozen",
BizNo: order.OrderNo,
@@ -375,22 +366,22 @@ func buildArbitrationSettlement(order model.RentalOrder, req ArbitrateRequest, r
})
}
addRenterRefund := func(amount float64, remark string) {
if amount <= 0 {
addRenterRefund := func(amountCent int64, remark string) {
if amountCent <= 0 {
return
}
settlement.RenterRefundAmount += amount
settlement.RenterRefundAmountCent += amountCent
}
addOwnerIncome := func(amount float64, remark string) {
if amount <= 0 {
addOwnerIncome := func(amountCent int64, remark string) {
if amountCent <= 0 {
return
}
settlement.OwnerIncomeAmount += amount
settlement.OwnerIncomeAmountCent += amountCent
settlement.Entries = append(settlement.Entries, wallet.Entry{
UserID: order.OwnerID,
OrderID: &orderID,
Direction: "in",
AmountCent: int64(math.Round(amount * 100)),
AmountCent: amountCent,
BalanceType: "available",
BizType: "arbitration_owner_income",
BizNo: order.OrderNo,
@@ -400,28 +391,27 @@ func buildArbitrationSettlement(order model.RentalOrder, req ArbitrateRequest, r
switch req.Result {
case "full_refund":
addRenterRefund(total, "仲裁全额退款")
addRenterRefund(totalCent, "仲裁全额退款")
case "partial_refund":
req.Amount = roundMoney(req.Amount)
if req.Amount <= 0 || req.Amount > total {
if req.AmountCent <= 0 || req.AmountCent > totalCent {
return settlement, ErrInvalidDispute
}
addRenterRefund(req.Amount, "仲裁部分退款")
addOwnerIncome(minMoney(total-req.Amount, ownerRentAmount+depositAmount), "仲裁剩余金额结算给号主")
addRenterRefund(req.AmountCent, "仲裁部分退款")
addOwnerIncome(money.MinCent(totalCent-req.AmountCent, ownerRentAmountCent+depositAmountCent), "仲裁剩余金额结算给号主")
case "release_deposit":
addOwnerIncome(ownerRentAmount, "仲裁确认订单金额结算给号主")
addRenterRefund(depositAmount, "仲裁释放押金给租客")
addOwnerIncome(ownerRentAmountCent, "仲裁确认订单金额结算给号主")
addRenterRefund(depositAmountCent, "仲裁释放押金给租客")
case "deduct_deposit", "compensate_owner":
deductAmount := roundMoney(req.Amount)
if deductAmount <= 0 {
deductAmount = depositAmount
deductAmountCent := req.AmountCent
if deductAmountCent <= 0 {
deductAmountCent = depositAmountCent
}
if deductAmount > depositAmount {
if deductAmountCent > depositAmountCent {
return settlement, ErrInvalidDispute
}
settlement.DepositDeductAmount = deductAmount
addOwnerIncome(ownerRentAmount+deductAmount, "仲裁订单金额及押金赔付结算给号主")
addRenterRefund(depositAmount-deductAmount, "仲裁退回剩余押金给租客")
settlement.DepositDeductAmountCent = deductAmountCent
addOwnerIncome(ownerRentAmountCent+deductAmountCent, "仲裁订单金额及押金赔付结算给号主")
addRenterRefund(depositAmountCent-deductAmountCent, "仲裁退回剩余押金给租客")
case "order_close":
// Only release frozen funds. No available-balance settlement happens in development mode.
case "mark_abnormal":
@@ -457,7 +447,7 @@ func (r *Repository) startRefundBestEffort(action *refundAction) {
_, _ = r.refundFunc(action.OrderID, action.RefundAmountCent, action.BizType, action.Remark)
}
func renterFrozenBalance(tx *gorm.DB, renterID uint64) (float64, error) {
func renterFrozenBalance(tx *gorm.DB, renterID uint64) (int64, error) {
var account model.WalletAccount
err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
Where("user_id = ?", renterID).
@@ -468,17 +458,7 @@ func renterFrozenBalance(tx *gorm.DB, renterID uint64) (float64, error) {
if err != nil {
return 0, err
}
return account.FrozenBalance, nil
}
// minMoney 返回较小金额(角精度)
func minMoney(a float64, b float64) float64 {
return money.Min(a, b)
}
// roundMoney 使用统一的角精度(0.1元)
func roundMoney(value float64) float64 {
return money.Round(value)
return account.FrozenBalanceCent, nil
}
func (r *Repository) baseQuery() *gorm.DB {
@@ -550,14 +530,14 @@ func buildArbitrationHandoffContent(req ArbitrateRequest, settlement arbitration
if req.Remark != "" {
content += "\n处理说明:" + req.Remark
}
if settlement.RenterRefundAmount > 0 {
content += fmt.Sprintf("\n退款给租客:¥%.2f", settlement.RenterRefundAmount)
if settlement.RenterRefundAmountCent > 0 {
content += "\n退款给租客:" + money.FormatWithSymbol(settlement.RenterRefundAmountCent)
}
if settlement.OwnerIncomeAmount > 0 {
content += fmt.Sprintf("\n结算给号主:¥%.2f", settlement.OwnerIncomeAmount)
if settlement.OwnerIncomeAmountCent > 0 {
content += "\n结算给号主:" + money.FormatWithSymbol(settlement.OwnerIncomeAmountCent)
}
if settlement.DepositDeductAmount > 0 {
content += fmt.Sprintf("\n押金扣除:¥%.2f", settlement.DepositDeductAmount)
if settlement.DepositDeductAmountCent > 0 {
content += "\n押金扣除:" + money.FormatWithSymbol(settlement.DepositDeductAmountCent)
}
return content
}