金额使用整数, 不再扣押金, 价格每个人看到自己的-1

This commit is contained in:
yml2213
2026-05-27 00:22:26 +08:00
parent e270ce8bfc
commit 89f4dabfb3
27 changed files with 351 additions and 119 deletions
+12 -6
View File
@@ -3,6 +3,7 @@ package dispute
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"math"
"time" "time"
"hfb_sys/backend/internal/model" "hfb_sys/backend/internal/model"
@@ -302,10 +303,10 @@ type arbitrationSettlement struct {
} }
func buildArbitrationSettlement(order model.RentalOrder, req ArbitrateRequest) (arbitrationSettlement, error) { func buildArbitrationSettlement(order model.RentalOrder, req ArbitrateRequest) (arbitrationSettlement, error) {
total := order.RentAmount + order.DepositAmount total := roundMoney(order.RentAmount + order.DepositAmount)
ownerRentAmount := order.OwnerRentAmount ownerRentAmount := roundMoney(order.OwnerRentAmount)
if ownerRentAmount <= 0 || ownerRentAmount > order.RentAmount { if ownerRentAmount <= 0 || ownerRentAmount > order.RentAmount {
ownerRentAmount = order.RentAmount ownerRentAmount = roundMoney(order.RentAmount)
} }
settlement := arbitrationSettlement{} settlement := arbitrationSettlement{}
orderID := order.ID orderID := order.ID
@@ -359,6 +360,7 @@ func buildArbitrationSettlement(order model.RentalOrder, req ArbitrateRequest) (
case "full_refund": case "full_refund":
addRenterRefund(total, "仲裁全额退款") addRenterRefund(total, "仲裁全额退款")
case "partial_refund": case "partial_refund":
req.Amount = roundMoney(req.Amount)
if req.Amount <= 0 || req.Amount > total { if req.Amount <= 0 || req.Amount > total {
return settlement, ErrInvalidDispute return settlement, ErrInvalidDispute
} }
@@ -368,7 +370,7 @@ func buildArbitrationSettlement(order model.RentalOrder, req ArbitrateRequest) (
addOwnerIncome(ownerRentAmount, "仲裁确认订单金额结算给号主") addOwnerIncome(ownerRentAmount, "仲裁确认订单金额结算给号主")
addRenterRefund(order.DepositAmount, "仲裁释放押金给租客") addRenterRefund(order.DepositAmount, "仲裁释放押金给租客")
case "deduct_deposit", "compensate_owner": case "deduct_deposit", "compensate_owner":
deductAmount := req.Amount deductAmount := roundMoney(req.Amount)
if deductAmount <= 0 { if deductAmount <= 0 {
deductAmount = order.DepositAmount deductAmount = order.DepositAmount
} }
@@ -390,9 +392,13 @@ func buildArbitrationSettlement(order model.RentalOrder, req ArbitrateRequest) (
func minMoney(a float64, b float64) float64 { func minMoney(a float64, b float64) float64 {
if a < b { if a < b {
return a return roundMoney(a)
} }
return b return roundMoney(b)
}
func roundMoney(value float64) float64 {
return math.Round(value)
} }
func (r *Repository) baseQuery() *gorm.DB { func (r *Repository) baseQuery() *gorm.DB {
+21 -3
View File
@@ -61,11 +61,12 @@ func (r *Repository) Create(ownerID uint64, req CreateRequest, reviewRequired bo
return err return err
} }
price := normalizedListingPrice(req) price := normalizedListingPrice(req)
depositAmount := roundMoney(req.DepositAmount)
listing := model.RentalListing{ listing := model.RentalListing{
AccountID: account.ID, AccountID: account.ID,
OwnerID: ownerID, OwnerID: ownerID,
Price: price, Price: price,
DepositAmount: req.DepositAmount, DepositAmount: depositAmount,
Status: listingStatus, Status: listingStatus,
ReviewStatus: reviewStatus, ReviewStatus: reviewStatus,
PublishedAt: publishedAt, PublishedAt: publishedAt,
@@ -109,7 +110,7 @@ func (r *Repository) Update(ownerID uint64, listingID uint64, req UpdateRequest,
price := normalizedListingPrice(req) price := normalizedListingPrice(req)
listing.Price = price listing.Price = price
listing.DepositAmount = req.DepositAmount listing.DepositAmount = roundMoney(req.DepositAmount)
listingStatus, reviewStatus, publishedAt := initialPublishState(reviewRequired) listingStatus, reviewStatus, publishedAt := initialPublishState(reviewRequired)
listing.Status = listingStatus listing.Status = listingStatus
listing.ReviewStatus = reviewStatus listing.ReviewStatus = reviewStatus
@@ -488,7 +489,15 @@ func rowsToDTO(rows []listingRow) []ListingDTO {
} }
func normalizedListingPrice(req CreateRequest) float64 { func normalizedListingPrice(req CreateRequest) float64 {
return req.Price if req.AssetSummary != nil {
if breakdown, ok := req.AssetSummary["price_breakdown"].(map[string]any); ok {
buyerPrice := readSummaryNumber(breakdown["buyer_total_price"])
if buyerPrice > 0 {
return roundMoney(buyerPrice)
}
}
}
return roundMoney(req.Price)
} }
func publicListings(items []ListingDTO) []ListingDTO { func publicListings(items []ListingDTO) []ListingDTO {
@@ -524,6 +533,15 @@ func applySellerListingPrice(item *ListingDTO) {
if sellerPrice > 0 { if sellerPrice > 0 {
item.Price = sellerPrice item.Price = sellerPrice
} }
sellerRatio := readSummaryNumber(breakdown["seller_ratio"])
if sellerRatio > 0 {
item.AssetSummary["publish_ratio"] = sellerRatio
}
delete(breakdown, "buyer_coin_base_price")
delete(breakdown, "buyer_total_price")
delete(breakdown, "buyer_ratio")
delete(breakdown, "platform_markup_amount")
delete(breakdown, "platform_rule_type")
} }
func (row listingRow) toDTO() ListingDTO { func (row listingRow) toDTO() ListingDTO {
+1 -1
View File
@@ -313,7 +313,7 @@ func readUnitPrice(priceText string) float64 {
} }
func roundMoney(value float64) float64 { func roundMoney(value float64) float64 {
return math.Round(value*100) / 100 return math.Round(value)
} }
func readFireLevel(summary map[string]any) (int, bool) { func readFireLevel(summary map[string]any) (int, bool) {
@@ -11,8 +11,8 @@ func TestConsumableValueOnlyCountsChargedResources(t *testing.T) {
}, },
}) })
if value != 1.5 { if value != 2 {
t.Fatalf("expected 1.5, got %.2f", value) t.Fatalf("expected 2, got %.2f", value)
} }
} }
@@ -21,7 +21,7 @@ func TestValidateRequestRequiresDepositAboveConsumables(t *testing.T) {
Title: "测试账号", Title: "测试账号",
ServerRegion: "烽火地带", ServerRegion: "烽火地带",
Price: 100, Price: 100,
DepositAmount: 1.5, DepositAmount: 2,
HafCoinAmount: 1000000, HafCoinAmount: 1000000,
ScreenshotURLS: []string{"https://example.com/a.png"}, ScreenshotURLS: []string{"https://example.com/a.png"},
AssetSummary: map[string]any{ AssetSummary: map[string]any{
@@ -35,7 +35,7 @@ func TestValidateRequestRequiresDepositAboveConsumables(t *testing.T) {
t.Fatalf("expected ErrDepositTooLow, got %v", err) t.Fatalf("expected ErrDepositTooLow, got %v", err)
} }
req.DepositAmount = 2.01 req.DepositAmount = 3
if err := validateRequest(req, publishRules{}); err != nil { if err := validateRequest(req, publishRules{}); err != nil {
t.Fatalf("expected valid request, got %v", err) t.Fatalf("expected valid request, got %v", err)
} }
+12 -8
View File
@@ -20,10 +20,12 @@ type OrderDTO struct {
LoginPlatform string `json:"login_platform"` LoginPlatform string `json:"login_platform"`
RentedAt *time.Time `json:"rented_at"` RentedAt *time.Time `json:"rented_at"`
EstimatedDurationHours int `json:"estimated_duration_hours"` EstimatedDurationHours int `json:"estimated_duration_hours"`
RentAmount float64 `json:"rent_amount"` PriceRole string `json:"price_role,omitempty"`
OwnerRentAmount float64 `json:"owner_rent_amount"` DisplayAmount float64 `json:"display_amount"`
RentAmount *float64 `json:"rent_amount,omitempty"`
OwnerRentAmount *float64 `json:"owner_rent_amount,omitempty"`
DepositAmount float64 `json:"deposit_amount"` DepositAmount float64 `json:"deposit_amount"`
PlatformFee float64 `json:"platform_fee"` PlatformFee *float64 `json:"platform_fee,omitempty"`
AccountSnapshot datatypes.JSON `json:"account_snapshot"` AccountSnapshot datatypes.JSON `json:"account_snapshot"`
Status string `json:"status"` Status string `json:"status"`
HandoffStatus string `json:"handoff_status"` HandoffStatus string `json:"handoff_status"`
@@ -88,16 +90,18 @@ type CheckoutDTO struct {
OrderID uint64 `json:"order_id"` OrderID uint64 `json:"order_id"`
InitiatedBy uint64 `json:"initiated_by"` InitiatedBy uint64 `json:"initiated_by"`
Status string `json:"status"` Status string `json:"status"`
RentAmount float64 `json:"rent_amount"` PriceRole string `json:"price_role,omitempty"`
OwnerRentAmount float64 `json:"owner_rent_amount"` DisplayAmount float64 `json:"display_amount"`
PlatformFee float64 `json:"platform_fee"` RentAmount *float64 `json:"rent_amount,omitempty"`
OwnerRentAmount *float64 `json:"owner_rent_amount,omitempty"`
PlatformFee *float64 `json:"platform_fee,omitempty"`
DepositAmount float64 `json:"deposit_amount"` DepositAmount float64 `json:"deposit_amount"`
ConsumableAmount float64 `json:"consumable_amount"` ConsumableAmount float64 `json:"consumable_amount"`
CoinConsumedM float64 `json:"coin_consumed_m"` CoinConsumedM float64 `json:"coin_consumed_m"`
OtherAmount float64 `json:"other_amount"` OtherAmount float64 `json:"other_amount"`
DepositDeductAmount float64 `json:"deposit_deduct_amount"` DepositDeductAmount float64 `json:"deposit_deduct_amount"`
RenterRefundAmount float64 `json:"renter_refund_amount"` RenterRefundAmount *float64 `json:"renter_refund_amount,omitempty"`
OwnerIncomeAmount float64 `json:"owner_income_amount"` OwnerIncomeAmount *float64 `json:"owner_income_amount,omitempty"`
Content string `json:"content"` Content string `json:"content"`
EvidenceURLS []string `json:"evidence_urls"` EvidenceURLS []string `json:"evidence_urls"`
OwnerAdjustmentReason string `json:"owner_adjustment_reason"` OwnerAdjustmentReason string `json:"owner_adjustment_reason"`
+179 -23
View File
@@ -624,7 +624,12 @@ func (r *Repository) CounterCheckout(userID uint64, orderID uint64, req CounterC
if err != nil { if err != nil {
return nil, err return nil, err
} }
dto := toCheckoutDTO(*checkout) dto := toCheckoutDTOForUser(*checkout, userID, model.RentalOrder{
OwnerID: userID,
RentAmount: checkout.RentAmount,
OwnerRentAmount: checkout.OwnerRentAmount,
DepositAmount: checkout.DepositAmount,
})
return &dto, nil return &dto, nil
} }
@@ -665,7 +670,7 @@ func (r *Repository) ListForUser(userID uint64) ([]OrderDTO, error) {
} }
items := make([]OrderDTO, 0, len(rows)) items := make([]OrderDTO, 0, len(rows))
for _, row := range rows { for _, row := range rows {
items = append(items, row.toDTO()) items = append(items, row.toDTOForUser(userID))
} }
return items, nil return items, nil
} }
@@ -681,7 +686,7 @@ func (r *Repository) ListAdmin() ([]OrderDTO, error) {
} }
items := make([]OrderDTO, 0, len(rows)) items := make([]OrderDTO, 0, len(rows))
for _, row := range rows { for _, row := range rows {
items = append(items, row.toDTO()) items = append(items, row.toAdminDTO())
} }
return items, nil return items, nil
} }
@@ -691,8 +696,8 @@ func (r *Repository) FindAdmin(orderID uint64) (*OrderDTO, error) {
if err := r.adminQuery().Where("o.id = ?", orderID).First(&row).Error; err != nil { if err := r.adminQuery().Where("o.id = ?", orderID).First(&row).Error; err != nil {
return nil, err return nil, err
} }
dto := row.toDTO() dto := row.toAdminDTO()
dto.Checkout = r.latestCheckoutDTO(orderID) dto.Checkout = r.latestCheckoutAdminDTO(orderID)
return &dto, nil return &dto, nil
} }
@@ -882,8 +887,8 @@ func (r *Repository) FindForUser(userID uint64, orderID uint64) (*OrderDTO, erro
First(&row).Error; err != nil { First(&row).Error; err != nil {
return nil, err return nil, err
} }
dto := row.toDTO() dto := row.toDTOForUser(userID)
dto.Checkout = r.latestCheckoutDTO(orderID) dto.Checkout = r.latestCheckoutDTOForUser(orderID, userID, row.RentalOrder)
return &dto, nil return &dto, nil
} }
@@ -1018,7 +1023,10 @@ func buildCheckout(order model.RentalOrder, initiatedBy uint64, status string, c
if consumableAmount < 0 || coinConsumedM < 0 || otherAmount < 0 || explicitDeduct < 0 { if consumableAmount < 0 || coinConsumedM < 0 || otherAmount < 0 || explicitDeduct < 0 {
return model.OrderCheckout{}, ErrInvalidCheckoutAmount return model.OrderCheckout{}, ErrInvalidCheckoutAmount
} }
deductAmount := consumableAmount + otherAmount consumableAmount = roundMoney(consumableAmount)
otherAmount = roundMoney(otherAmount)
explicitDeduct = roundMoney(explicitDeduct)
deductAmount := otherAmount
if useExplicitDeduct { if useExplicitDeduct {
deductAmount = explicitDeduct deductAmount = explicitDeduct
} }
@@ -1038,9 +1046,9 @@ func buildCheckout(order model.RentalOrder, initiatedBy uint64, status string, c
OwnerRentAmount: order.OwnerRentAmount, OwnerRentAmount: order.OwnerRentAmount,
PlatformFee: order.PlatformFee, PlatformFee: order.PlatformFee,
DepositAmount: order.DepositAmount, DepositAmount: order.DepositAmount,
ConsumableAmount: roundMoney(consumableAmount), ConsumableAmount: consumableAmount,
CoinConsumedM: roundMoney(coinConsumedM), CoinConsumedM: roundQuantity(coinConsumedM),
OtherAmount: roundMoney(otherAmount), OtherAmount: otherAmount,
DepositDeductAmount: roundMoney(deductAmount), DepositDeductAmount: roundMoney(deductAmount),
RenterRefundAmount: roundMoney(renterRefund), RenterRefundAmount: roundMoney(renterRefund),
OwnerIncomeAmount: roundMoney(order.OwnerRentAmount + deductAmount), OwnerIncomeAmount: roundMoney(order.OwnerRentAmount + deductAmount),
@@ -1069,6 +1077,10 @@ func decodeStringList(raw datatypes.JSON) []string {
} }
func roundMoney(value float64) float64 { func roundMoney(value float64) float64 {
return math.Round(value)
}
func roundQuantity(value float64) float64 {
return math.Round(value*100) / 100 return math.Round(value*100) / 100
} }
@@ -1080,12 +1092,21 @@ func hasOpenCheckout(tx *gorm.DB, orderID uint64) (bool, error) {
return count > 0, err return count > 0, err
} }
func (r *Repository) latestCheckoutDTO(orderID uint64) *CheckoutDTO { func (r *Repository) latestCheckoutAdminDTO(orderID uint64) *CheckoutDTO {
var checkout model.OrderCheckout var checkout model.OrderCheckout
if err := r.db.Where("order_id = ?", orderID).Order("id DESC").First(&checkout).Error; err != nil { if err := r.db.Where("order_id = ?", orderID).Order("id DESC").First(&checkout).Error; err != nil {
return nil return nil
} }
dto := toCheckoutDTO(checkout) dto := toCheckoutAdminDTO(checkout)
return &dto
}
func (r *Repository) latestCheckoutDTOForUser(orderID uint64, userID uint64, order model.RentalOrder) *CheckoutDTO {
var checkout model.OrderCheckout
if err := r.db.Where("order_id = ?", orderID).Order("id DESC").First(&checkout).Error; err != nil {
return nil
}
dto := toCheckoutDTOForUser(checkout, userID, order)
return &dto return &dto
} }
@@ -1120,9 +1141,12 @@ type orderRow struct {
RenterPhone string RenterPhone string
} }
func (row orderRow) toDTO() OrderDTO { func (row orderRow) toAdminDTO() OrderDTO {
rentedAt := row.RentedAt rentedAt := row.RentedAt
durationHours := orderDurationHours(row.RentalOrder) durationHours := orderDurationHours(row.RentalOrder)
rentAmount := row.RentAmount
ownerRentAmount := row.OwnerRentAmount
platformFee := row.PlatformFee
return OrderDTO{ return OrderDTO{
ID: row.ID, ID: row.ID,
OrderNo: row.OrderNo, OrderNo: row.OrderNo,
@@ -1137,10 +1161,12 @@ func (row orderRow) toDTO() OrderDTO {
LoginPlatform: row.LoginPlatform, LoginPlatform: row.LoginPlatform,
RentedAt: rentedAt, RentedAt: rentedAt,
EstimatedDurationHours: durationHours, EstimatedDurationHours: durationHours,
RentAmount: row.RentAmount, PriceRole: "admin",
OwnerRentAmount: row.OwnerRentAmount, DisplayAmount: row.RentAmount,
RentAmount: &rentAmount,
OwnerRentAmount: &ownerRentAmount,
DepositAmount: row.DepositAmount, DepositAmount: row.DepositAmount,
PlatformFee: row.PlatformFee, PlatformFee: &platformFee,
AccountSnapshot: row.AccountSnapshot, AccountSnapshot: row.AccountSnapshot,
Status: row.Status, Status: row.Status,
HandoffStatus: row.HandoffStatus, HandoffStatus: row.HandoffStatus,
@@ -1150,6 +1176,12 @@ func (row orderRow) toDTO() OrderDTO {
} }
} }
func (row orderRow) toDTOForUser(userID uint64) OrderDTO {
dto := row.toAdminDTO()
applyOrderPriceView(&dto, row.RentalOrder, userID)
return dto
}
func toHandoffDTO(record model.HandoffRecord) HandoffRecordDTO { func toHandoffDTO(record model.HandoffRecord) HandoffRecordDTO {
return HandoffRecordDTO{ return HandoffRecordDTO{
ID: record.ID, ID: record.ID,
@@ -1164,22 +1196,29 @@ func toHandoffDTO(record model.HandoffRecord) HandoffRecordDTO {
} }
} }
func toCheckoutDTO(checkout model.OrderCheckout) CheckoutDTO { func toCheckoutAdminDTO(checkout model.OrderCheckout) CheckoutDTO {
rentAmount := checkout.RentAmount
ownerRentAmount := checkout.OwnerRentAmount
platformFee := checkout.PlatformFee
renterRefundAmount := checkout.RenterRefundAmount
ownerIncomeAmount := checkout.OwnerIncomeAmount
return CheckoutDTO{ return CheckoutDTO{
ID: checkout.ID, ID: checkout.ID,
OrderID: checkout.OrderID, OrderID: checkout.OrderID,
InitiatedBy: checkout.InitiatedBy, InitiatedBy: checkout.InitiatedBy,
Status: checkout.Status, Status: checkout.Status,
RentAmount: checkout.RentAmount, PriceRole: "admin",
OwnerRentAmount: checkout.OwnerRentAmount, DisplayAmount: checkout.RentAmount,
PlatformFee: checkout.PlatformFee, RentAmount: &rentAmount,
OwnerRentAmount: &ownerRentAmount,
PlatformFee: &platformFee,
DepositAmount: checkout.DepositAmount, DepositAmount: checkout.DepositAmount,
ConsumableAmount: checkout.ConsumableAmount, ConsumableAmount: checkout.ConsumableAmount,
CoinConsumedM: checkout.CoinConsumedM, CoinConsumedM: checkout.CoinConsumedM,
OtherAmount: checkout.OtherAmount, OtherAmount: checkout.OtherAmount,
DepositDeductAmount: checkout.DepositDeductAmount, DepositDeductAmount: checkout.DepositDeductAmount,
RenterRefundAmount: checkout.RenterRefundAmount, RenterRefundAmount: &renterRefundAmount,
OwnerIncomeAmount: checkout.OwnerIncomeAmount, OwnerIncomeAmount: &ownerIncomeAmount,
Content: checkout.Content, Content: checkout.Content,
EvidenceURLS: decodeStringList(checkout.EvidenceURLS), EvidenceURLS: decodeStringList(checkout.EvidenceURLS),
OwnerAdjustmentReason: checkout.OwnerAdjustmentReason, OwnerAdjustmentReason: checkout.OwnerAdjustmentReason,
@@ -1191,6 +1230,123 @@ func toCheckoutDTO(checkout model.OrderCheckout) CheckoutDTO {
} }
} }
func toCheckoutDTOForUser(checkout model.OrderCheckout, userID uint64, order model.RentalOrder) CheckoutDTO {
dto := toCheckoutAdminDTO(checkout)
applyCheckoutPriceView(&dto, order, userID)
return dto
}
func applyOrderPriceView(dto *OrderDTO, order model.RentalOrder, userID uint64) {
if dto == nil {
return
}
dto.PlatformFee = nil
switch {
case userID == order.OwnerID:
ownerAmount := order.OwnerRentAmount
if ownerAmount <= 0 {
ownerAmount = order.RentAmount
}
dto.PriceRole = "owner"
dto.DisplayAmount = ownerAmount
dto.RentAmount = nil
dto.OwnerRentAmount = &ownerAmount
sanitizeOrderSnapshot(&dto.AccountSnapshot, "owner")
case userID == order.RenterID:
rentAmount := order.RentAmount
dto.PriceRole = "renter"
dto.DisplayAmount = rentAmount
dto.RentAmount = &rentAmount
dto.OwnerRentAmount = nil
sanitizeOrderSnapshot(&dto.AccountSnapshot, "renter")
default:
dto.PriceRole = ""
dto.DisplayAmount = 0
dto.RentAmount = nil
dto.OwnerRentAmount = nil
sanitizeOrderSnapshot(&dto.AccountSnapshot, "")
}
}
func applyCheckoutPriceView(dto *CheckoutDTO, order model.RentalOrder, userID uint64) {
if dto == nil {
return
}
dto.PlatformFee = nil
dto.RenterRefundAmount = nil
dto.OwnerIncomeAmount = nil
switch {
case userID == order.OwnerID:
ownerAmount := order.OwnerRentAmount
if ownerAmount <= 0 {
ownerAmount = order.RentAmount
}
ownerIncomeAmount := dto.DepositDeductAmount + ownerAmount
dto.PriceRole = "owner"
dto.DisplayAmount = ownerAmount
dto.RentAmount = nil
dto.OwnerRentAmount = &ownerAmount
dto.OwnerIncomeAmount = &ownerIncomeAmount
case userID == order.RenterID:
rentAmount := order.RentAmount
renterRefundAmount := order.DepositAmount - dto.DepositDeductAmount
if renterRefundAmount < 0 {
renterRefundAmount = 0
}
dto.PriceRole = "renter"
dto.DisplayAmount = rentAmount
dto.RentAmount = &rentAmount
dto.OwnerRentAmount = nil
dto.RenterRefundAmount = &renterRefundAmount
default:
dto.PriceRole = ""
dto.DisplayAmount = 0
dto.RentAmount = nil
dto.OwnerRentAmount = nil
}
}
func sanitizeOrderSnapshot(snapshot *datatypes.JSON, role string) {
if snapshot == nil || len(*snapshot) == 0 {
return
}
var payload map[string]any
if err := json.Unmarshal(*snapshot, &payload); err != nil {
return
}
rawSummary, ok := payload["asset_summary"]
if !ok {
return
}
var summary map[string]any
switch typed := rawSummary.(type) {
case map[string]any:
summary = typed
case string:
if err := json.Unmarshal([]byte(typed), &summary); err != nil {
return
}
default:
raw, err := json.Marshal(typed)
if err != nil || json.Unmarshal(raw, &summary) != nil {
return
}
}
breakdown, _ := summary["price_breakdown"].(map[string]any)
if role == "owner" && breakdown != nil {
if sellerRatio := readJSONNumber(breakdown["seller_ratio"]); sellerRatio > 0 {
summary["publish_ratio"] = sellerRatio
}
}
delete(summary, "price_breakdown")
payload["asset_summary"] = summary
raw, err := json.Marshal(payload)
if err != nil {
return
}
*snapshot = datatypes.JSON(raw)
}
func makeAccountSnapshot(account model.GameAccount) (datatypes.JSON, error) { func makeAccountSnapshot(account model.GameAccount) (datatypes.JSON, error) {
payload := map[string]any{ payload := map[string]any{
"account_id": account.ID, "account_id": account.ID,
@@ -202,7 +202,7 @@ func applyEntry(account *model.WalletAccount, entry Entry) (float64, error) {
} }
func roundWalletMoney(value float64) float64 { func roundWalletMoney(value float64) float64 {
return math.Round(value*100) / 100 return math.Round(value)
} }
func toAccountDTO(account model.WalletAccount) *AccountDTO { func toAccountDTO(account model.WalletAccount) *AccountDTO {
+12 -8
View File
@@ -16,10 +16,12 @@ export interface Order {
login_platform: string login_platform: string
rented_at?: string rented_at?: string
estimated_duration_hours: number estimated_duration_hours: number
rent_amount: number price_role?: 'renter' | 'owner' | 'admin' | string
owner_rent_amount: number display_amount: number
rent_amount?: number
owner_rent_amount?: number
deposit_amount: number deposit_amount: number
platform_fee: number platform_fee?: number
account_snapshot?: Record<string, unknown> account_snapshot?: Record<string, unknown>
status: OrderStatus status: OrderStatus
handoff_status: HandoffStatus handoff_status: HandoffStatus
@@ -34,16 +36,18 @@ export interface Checkout {
order_id: number order_id: number
initiated_by: number initiated_by: number
status: SettlementStatus status: SettlementStatus
rent_amount: number price_role?: 'renter' | 'owner' | 'admin' | string
owner_rent_amount: number display_amount: number
platform_fee: number rent_amount?: number
owner_rent_amount?: number
platform_fee?: number
deposit_amount: number deposit_amount: number
consumable_amount: number consumable_amount: number
coin_consumed_m: number coin_consumed_m: number
other_amount: number other_amount: number
deposit_deduct_amount: number deposit_deduct_amount: number
renter_refund_amount: number renter_refund_amount?: number
owner_income_amount: number owner_income_amount?: number
content: string content: string
evidence_urls: string[] evidence_urls: string[]
owner_adjustment_reason: string owner_adjustment_reason: string
+1 -1
View File
@@ -13,7 +13,7 @@ export const dailyLossOptions = [10, 20, 30, 40, 50]
export const commonOnlineTimes = ['00:00', '08:00', '10:00', '12:00', '14:00', '18:00', '20:00', '22:00', '23:59'] export const commonOnlineTimes = ['00:00', '08:00', '10:00', '12:00', '14:00', '18:00', '20:00', '22:00', '23:59']
export function roundMoney(value: number) { export function roundMoney(value: number) {
return Math.round(value * 100) / 100 return Math.round(value)
} }
export function roundRatio(value: number) { export function roundRatio(value: number) {
+33 -22
View File
@@ -64,6 +64,7 @@ const disputeEvidenceText = ref('')
const isOwner = computed(() => order.value?.owner_id === session.userId) const isOwner = computed(() => order.value?.owner_id === session.userId)
const isRenter = computed(() => order.value?.renter_id === session.userId) const isRenter = computed(() => order.value?.renter_id === session.userId)
const orderAmountLabel = computed(() => (isOwner.value ? '我的租金' : '订单金额'))
const canOpenDispute = computed(() => { const canOpenDispute = computed(() => {
if (!order.value || (!isOwner.value && !isRenter.value)) return false if (!order.value || (!isOwner.value && !isRenter.value)) return false
return !['completed', 'cancelled', 'closed', 'disputing', 'checkout_disputing', 'abnormal'].includes(order.value.status) return !['completed', 'cancelled', 'closed', 'disputing', 'checkout_disputing', 'abnormal'].includes(order.value.status)
@@ -86,10 +87,10 @@ const resourceChargeAmount = computed(() => {
}) })
const snapshotHafCoinM = computed(() => { const snapshotHafCoinM = computed(() => {
const snapshot = readSnapshot() const snapshot = readSnapshot()
return roundMoney(readNumber(snapshot?.haf_coin_amount) / 1000000) return roundQuantity(readNumber(snapshot?.haf_coin_amount) / 1000000)
}) })
const remainingHafCoinM = computed(() => { const remainingHafCoinM = computed(() => {
return roundMoney(Math.max(snapshotHafCoinM.value - Number(checkoutForm.value.coin_consumed_m || 0), 0)) return roundQuantity(Math.max(snapshotHafCoinM.value - Number(checkoutForm.value.coin_consumed_m || 0), 0))
}) })
onMounted(loadOrder) onMounted(loadOrder)
@@ -387,12 +388,12 @@ function checkoutContentWithSummary() {
if (usedResources.length) { if (usedResources.length) {
lines.push( lines.push(
`额外消耗品:${usedResources `额外消耗品:${usedResources
.map((item) => `${item.label} ${readResourceUsage(item.key)}/${item.quantity}${isChargedResource(item) ? `扣款¥${resourceLineAmount(item).toFixed(2)}` : ',赠送不扣款'}`) .map((item) => `${item.label} ${readResourceUsage(item.key)}/${item.quantity}${isChargedResource(item) ? `金额¥${money(resourceLineAmount(item))}` : ',赠送不扣款'}`)
.join('')}`, .join('')}`,
) )
} }
if (Number(checkoutForm.value.coin_consumed_m || 0) > 0) { if (Number(checkoutForm.value.coin_consumed_m || 0) > 0) {
lines.push(`哈夫币消耗:${Number(checkoutForm.value.coin_consumed_m).toFixed(2)}M,预计剩余${remainingHafCoinM.value.toFixed(2)}M`) lines.push(`哈夫币消耗:${quantity(Number(checkoutForm.value.coin_consumed_m))}M,预计剩余${quantity(remainingHafCoinM.value)}M`)
} }
if (lines.length === 0) { if (lines.length === 0) {
lines.push('租客发起结账。') lines.push('租客发起结账。')
@@ -415,9 +416,22 @@ function readUnitPrice(priceText: string) {
} }
function roundMoney(value: number) { function roundMoney(value: number) {
return Math.round(value)
}
function roundQuantity(value: number) {
return Math.round(value * 100) / 100 return Math.round(value * 100) / 100
} }
function money(value: unknown) {
return `${roundMoney(readNumber(value))}`
}
function quantity(value: unknown) {
const rounded = roundQuantity(readNumber(value))
return Number.isInteger(rounded) ? `${rounded}` : rounded.toFixed(2)
}
function readNumber(value: unknown) { function readNumber(value: unknown) {
const number = Number(value || 0) const number = Number(value || 0)
return Number.isFinite(number) ? number : 0 return Number.isFinite(number) ? number : 0
@@ -462,16 +476,12 @@ function linesToList(value: string) {
<strong>{{ handoffStatusLabel(order.handoff_status) }}</strong> <strong>{{ handoffStatusLabel(order.handoff_status) }}</strong>
</div> </div>
<div class="metric-card"> <div class="metric-card">
<span>订单金额</span> <span>{{ orderAmountLabel }}</span>
<strong>¥{{ order.rent_amount }}</strong> <strong>¥{{ money(order.display_amount) }}</strong>
</div>
<div class="metric-card">
<span>平台费用</span>
<strong>¥{{ order.platform_fee }}</strong>
</div> </div>
<div class="metric-card"> <div class="metric-card">
<span>押金</span> <span>押金</span>
<strong>¥{{ order.deposit_amount }}</strong> <strong>¥{{ money(order.deposit_amount) }}</strong>
</div> </div>
</div> </div>
@@ -518,7 +528,7 @@ function linesToList(value: string) {
<div class="checkout-resource-panel panel-action"> <div class="checkout-resource-panel panel-action">
<div class="checkout-resource-head"> <div class="checkout-resource-head">
<strong>额外消耗品</strong> <strong>额外消耗品</strong>
<span>扣款合计¥{{ resourceChargeAmount.toFixed(2) }}</span> <span>金额合计¥{{ money(resourceChargeAmount) }}</span>
</div> </div>
<el-empty v-if="checkoutResources.length === 0" description="订单快照中暂无额外消耗品" /> <el-empty v-if="checkoutResources.length === 0" description="订单快照中暂无额外消耗品" />
<div v-for="item in checkoutResources" v-else :key="item.key" class="checkout-resource-row"> <div v-for="item in checkoutResources" v-else :key="item.key" class="checkout-resource-row">
@@ -533,16 +543,16 @@ function linesToList(value: string) {
:precision="0" :precision="0"
controls-position="right" controls-position="right"
/> />
<span class="checkout-resource-amount">¥{{ resourceLineAmount(item).toFixed(2) }}</span> <span class="checkout-resource-amount">¥{{ money(resourceLineAmount(item)) }}</span>
</div> </div>
</div> </div>
<el-form class="form-grid panel-action" label-position="top"> <el-form class="form-grid panel-action" label-position="top">
<el-form-item label="消耗哈夫币(M"> <el-form-item label="消耗哈夫币(M">
<el-input-number v-model="checkoutForm.coin_consumed_m" class="full-control" :min="0" :max="snapshotHafCoinM" :precision="2" controls-position="right" /> <el-input-number v-model="checkoutForm.coin_consumed_m" class="full-control" :min="0" :max="snapshotHafCoinM" :precision="2" controls-position="right" />
<span class="field-hint">订单快照 {{ snapshotHafCoinM.toFixed(2) }}M预计剩余 {{ remainingHafCoinM.toFixed(2) }}M</span> <span class="field-hint">订单快照 {{ quantity(snapshotHafCoinM) }}M预计剩余 {{ quantity(remainingHafCoinM) }}M</span>
</el-form-item> </el-form-item>
<el-form-item label="其他扣款(元)"> <el-form-item label="其他扣款(元)">
<el-input-number v-model="checkoutForm.other_amount" class="full-control" :min="0" :precision="2" controls-position="right" /> <el-input-number v-model="checkoutForm.other_amount" class="full-control" :min="0" :precision="0" controls-position="right" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-input <el-input
@@ -557,9 +567,10 @@ function linesToList(value: string) {
<div v-if="order && order.checkout && ['pending_checkout_confirm', 'pending_checkout_accept'].includes(order.status)" class="order-panel"> <div v-if="order && order.checkout && ['pending_checkout_confirm', 'pending_checkout_accept'].includes(order.status)" class="order-panel">
<h2>结账明细</h2> <h2>结账明细</h2>
<p>买家租金¥{{ order.checkout.rent_amount }}号主租金¥{{ order.checkout.owner_rent_amount }}平台费用¥{{ order.checkout.platform_fee }}</p> <p>{{ orderAmountLabel }}¥{{ money(order.checkout.display_amount) }}押金¥{{ money(order.checkout.deposit_amount) }}</p>
<p>押金¥{{ order.checkout.deposit_amount }}</p> <p>额外消耗品金额¥{{ money(order.checkout.consumable_amount) }}押金扣除¥{{ money(order.checkout.deposit_deduct_amount) }}</p>
<p>扣除¥{{ order.checkout.deposit_deduct_amount }}退还租客¥{{ order.checkout.renter_refund_amount }}号主收入¥{{ order.checkout.owner_income_amount }}</p> <p v-if="isRenter">退还租客¥{{ money(order.checkout.renter_refund_amount) }}</p>
<p v-if="isOwner">号主收入¥{{ money(order.checkout.owner_income_amount) }}</p>
<p v-if="order.checkout.content">说明{{ order.checkout.content }}</p> <p v-if="order.checkout.content">说明{{ order.checkout.content }}</p>
<p v-if="order.checkout.owner_adjustment_reason">修正原因{{ order.checkout.owner_adjustment_reason }}</p> <p v-if="order.checkout.owner_adjustment_reason">修正原因{{ order.checkout.owner_adjustment_reason }}</p>
</div> </div>
@@ -571,17 +582,17 @@ function linesToList(value: string) {
<h2 class="panel-action">修改结账</h2> <h2 class="panel-action">修改结账</h2>
<el-form class="form-grid" label-position="top"> <el-form class="form-grid" label-position="top">
<el-form-item label="消耗扣款(元"> <el-form-item label="额外消耗品金额(已含租金">
<el-input-number v-model="counterForm.consumable_amount" class="full-control" :min="0" :precision="2" controls-position="right" /> <el-input-number v-model="counterForm.consumable_amount" class="full-control" :min="0" :precision="0" controls-position="right" />
</el-form-item> </el-form-item>
<el-form-item label="消耗哈夫币(M"> <el-form-item label="消耗哈夫币(M">
<el-input-number v-model="counterForm.coin_consumed_m" class="full-control" :min="0" :precision="2" controls-position="right" /> <el-input-number v-model="counterForm.coin_consumed_m" class="full-control" :min="0" :precision="2" controls-position="right" />
</el-form-item> </el-form-item>
<el-form-item label="其他扣款(元)"> <el-form-item label="其他扣款(元)">
<el-input-number v-model="counterForm.other_amount" class="full-control" :min="0" :precision="2" controls-position="right" /> <el-input-number v-model="counterForm.other_amount" class="full-control" :min="0" :precision="0" controls-position="right" />
</el-form-item> </el-form-item>
<el-form-item label="押金扣除(元)"> <el-form-item label="押金扣除(元)">
<el-input-number v-model="counterForm.deposit_deduct_amount" class="full-control" :min="0" :max="order.deposit_amount" :precision="2" controls-position="right" /> <el-input-number v-model="counterForm.deposit_deduct_amount" class="full-control" :min="0" :max="order.deposit_amount" :precision="0" controls-position="right" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-input v-model="counterForm.reason" class="panel-action" type="textarea" :rows="3" placeholder="填写修改原因" /> <el-input v-model="counterForm.reason" class="panel-action" type="textarea" :rows="3" placeholder="填写修改原因" />
+10 -2
View File
@@ -48,6 +48,10 @@ function orderRole(order: Order) {
if (order.owner_id === session.userId) return '号主' if (order.owner_id === session.userId) return '号主'
return '-' return '-'
} }
function money(value: unknown) {
return Math.round(Number(value || 0))
}
</script> </script>
<template> <template>
@@ -61,8 +65,12 @@ function orderRole(order: Order) {
<el-table v-loading="loading" class="table-panel" :data="orders"> <el-table v-loading="loading" class="table-panel" :data="orders">
<el-table-column prop="order_no" label="订单号" min-width="210" /> <el-table-column prop="order_no" label="订单号" min-width="210" />
<el-table-column prop="title" label="账号" min-width="180" /> <el-table-column prop="title" label="账号" min-width="180" />
<el-table-column prop="rent_amount" label="订单金额" width="100" /> <el-table-column label="我的金额" width="100">
<el-table-column prop="deposit_amount" label="押金" width="100" /> <template #default="{ row }">¥{{ money(row.display_amount) }}</template>
</el-table-column>
<el-table-column label="押金" width="100">
<template #default="{ row }">¥{{ money(row.deposit_amount) }}</template>
</el-table-column>
<el-table-column label="身份" width="80"> <el-table-column label="身份" width="80">
<template #default="{ row }">{{ orderRole(row) }}</template> <template #default="{ row }">{{ orderRole(row) }}</template>
</el-table-column> </el-table-column>
+1 -1
View File
@@ -85,7 +85,7 @@ function readError(error: unknown, fallback: string) {
<div class="table-panel recharge-panel"> <div class="table-panel recharge-panel">
<h2>开发充值</h2> <h2>开发充值</h2>
<el-input-number v-model="rechargeAmount" :min="0.01" :precision="2" controls-position="right" /> <el-input-number v-model="rechargeAmount" :min="1" :precision="0" controls-position="right" />
<el-button type="primary" :loading="recharging" @click="handleRecharge">充值</el-button> <el-button type="primary" :loading="recharging" @click="handleRecharge">充值</el-button>
</div> </div>
@@ -20,7 +20,7 @@ async function loadDashboard() {
} }
function money(value?: number) { function money(value?: number) {
return `¥${Number(value || 0).toFixed(2)}` return `¥${Math.round(Number(value || 0))}`
} }
</script> </script>
@@ -162,7 +162,7 @@ function readError(error: unknown, fallback: string) {
v-model="amount" v-model="amount"
class="full-control panel-action" class="full-control panel-action"
:min="0" :min="0"
:precision="2" :precision="0"
:step="10" :step="10"
placeholder="裁决金额" placeholder="裁决金额"
/> />
@@ -54,7 +54,7 @@ async function submitAction() {
} }
function money(value: number) { function money(value: number) {
return `¥${Number(value || 0).toFixed(2)}` return `¥${Math.round(Number(value || 0))}`
} }
function listingPrice(row: Listing) { function listingPrice(row: Listing) {
@@ -63,7 +63,7 @@ function openEvidence(row: Listing) {
} }
function listingPrice(row: Listing) { function listingPrice(row: Listing) {
return `¥${Number(row.price || 0).toFixed(2)}` return `¥${Math.round(Number(row.price || 0))}`
} }
function extractObjectKey(url: string) { function extractObjectKey(url: string) {
@@ -39,7 +39,7 @@ function resetFilters() {
} }
function money(value: number) { function money(value: number) {
return `¥${Number(value || 0).toFixed(2)}` return `¥${Math.round(Number(value || 0))}`
} }
function listingPrice(row: Listing) { function listingPrice(row: Listing) {
@@ -75,6 +75,10 @@ function orderEstimatedEndAt() {
if (!rentedAt || durationHours <= 0) return undefined if (!rentedAt || durationHours <= 0) return undefined
return new Date(new Date(rentedAt).getTime() + durationHours * 60 * 60 * 1000).toISOString() return new Date(new Date(rentedAt).getTime() + durationHours * 60 * 60 * 1000).toISOString()
} }
function money(value: unknown) {
return Math.round(Number(value || 0))
}
</script> </script>
<template> <template>
@@ -105,15 +109,15 @@ function orderEstimatedEndAt() {
</div> </div>
<div class="metric-card"> <div class="metric-card">
<span>订单金额</span> <span>订单金额</span>
<strong>¥{{ order.rent_amount }}</strong> <strong>¥{{ money(order.rent_amount) }}</strong>
</div> </div>
<div class="metric-card"> <div class="metric-card">
<span>平台费用</span> <span>平台费用</span>
<strong>¥{{ order.platform_fee }}</strong> <strong>¥{{ money(order.platform_fee) }}</strong>
</div> </div>
<div class="metric-card"> <div class="metric-card">
<span>押金</span> <span>押金</span>
<strong>¥{{ order.deposit_amount }}</strong> <strong>¥{{ money(order.deposit_amount) }}</strong>
</div> </div>
</div> </div>
@@ -56,7 +56,7 @@ function handleSizeChange() {
} }
function money(value: number) { function money(value: number) {
return `¥${value.toFixed(2)}` return `¥${Math.round(Number(value || 0))}`
} }
function directionType(direction: string) { function directionType(direction: string) {
@@ -42,8 +42,8 @@ onMounted(async () => {
}); });
const orderTotal = computed(() => { const orderTotal = computed(() => {
if (!listing.value) return "0.00"; if (!listing.value) return "0";
return getListingDisplayPrice(listing.value).toFixed(2); return `${Math.round(getListingDisplayPrice(listing.value))}`;
}); });
const detailMetrics = computed(() => { const detailMetrics = computed(() => {
@@ -72,6 +72,7 @@ const showRejectPopup = ref(false);
const isOwner = computed(() => order.value?.owner_id === session.userId); const isOwner = computed(() => order.value?.owner_id === session.userId);
const isRenter = computed(() => order.value?.renter_id === session.userId); const isRenter = computed(() => order.value?.renter_id === session.userId);
const orderAmountLabel = computed(() => (isOwner.value ? "我的租金" : "订单金额"));
const canOpenDispute = computed(() => { const canOpenDispute = computed(() => {
if (!order.value || (!isOwner.value && !isRenter.value)) return false; if (!order.value || (!isOwner.value && !isRenter.value)) return false;
return !["completed", "cancelled", "closed", "disputing", "checkout_disputing", "abnormal"].includes(order.value.status); return !["completed", "cancelled", "closed", "disputing", "checkout_disputing", "abnormal"].includes(order.value.status);
@@ -94,10 +95,10 @@ const resourceChargeAmount = computed(() => {
}); });
const snapshotHafCoinM = computed(() => { const snapshotHafCoinM = computed(() => {
const snapshot = readSnapshot(); const snapshot = readSnapshot();
return roundMoney(readNumber(snapshot?.haf_coin_amount) / 1000000); return roundQuantity(readNumber(snapshot?.haf_coin_amount) / 1000000);
}); });
const remainingHafCoinM = computed(() => { const remainingHafCoinM = computed(() => {
return roundMoney(Math.max(snapshotHafCoinM.value - Number(checkoutForm.value.coin_consumed_m || 0), 0)); return roundQuantity(Math.max(snapshotHafCoinM.value - Number(checkoutForm.value.coin_consumed_m || 0), 0));
}); });
onMounted(loadOrder); onMounted(loadOrder);
@@ -450,14 +451,14 @@ function checkoutContentWithSummary() {
.map( .map(
(item) => (item) =>
`${item.label} ${readResourceUsage(item.key)}/${item.quantity}${ `${item.label} ${readResourceUsage(item.key)}/${item.quantity}${
isChargedResource(item) ? `扣款¥${resourceLineAmount(item).toFixed(2)}` : ",赠送不扣款" isChargedResource(item) ? `金额¥${money(resourceLineAmount(item))}` : ",赠送不扣款"
}` }`
) )
.join("")}` .join("")}`
); );
} }
if (Number(checkoutForm.value.coin_consumed_m || 0) > 0) { if (Number(checkoutForm.value.coin_consumed_m || 0) > 0) {
lines.push(`哈夫币消耗:${Number(checkoutForm.value.coin_consumed_m).toFixed(2)}M,预计剩余${remainingHafCoinM.value.toFixed(2)}M`); lines.push(`哈夫币消耗:${quantity(Number(checkoutForm.value.coin_consumed_m))}M,预计剩余${quantity(remainingHafCoinM.value)}M`);
} }
if (lines.length === 0) { if (lines.length === 0) {
lines.push("租客发起结账。"); lines.push("租客发起结账。");
@@ -480,9 +481,22 @@ function readUnitPrice(priceText: string) {
} }
function roundMoney(value: number) { function roundMoney(value: number) {
return Math.round(value);
}
function roundQuantity(value: number) {
return Math.round(value * 100) / 100; return Math.round(value * 100) / 100;
} }
function money(value: unknown) {
return `${roundMoney(readNumber(value))}`;
}
function quantity(value: unknown) {
const rounded = roundQuantity(readNumber(value));
return Number.isInteger(rounded) ? `${rounded}` : rounded.toFixed(2);
}
function readNumber(value: unknown) { function readNumber(value: unknown) {
const number = Number(value || 0); const number = Number(value || 0);
return Number.isFinite(number) ? number : 0; return Number.isFinite(number) ? number : 0;
@@ -560,12 +574,12 @@ function getStatusTagType(status: string) {
<h2 class="order-title">{{ order.title }}</h2> <h2 class="order-title">{{ order.title }}</h2>
<div class="order-meta-grid"> <div class="order-meta-grid">
<div class="meta-item"> <div class="meta-item">
<span class="meta-label">订单金额</span> <span class="meta-label">{{ orderAmountLabel }}</span>
<strong class="meta-value accent">¥{{ order.rent_amount }}</strong> <strong class="meta-value accent">¥{{ money(order.display_amount) }}</strong>
</div> </div>
<div class="meta-item"> <div class="meta-item">
<span class="meta-label">押金</span> <span class="meta-label">押金</span>
<strong class="meta-value">¥{{ order.deposit_amount }}</strong> <strong class="meta-value">¥{{ money(order.deposit_amount) }}</strong>
</div> </div>
<div class="meta-item"> <div class="meta-item">
<span class="meta-label">交接状态</span> <span class="meta-label">交接状态</span>
@@ -711,12 +725,12 @@ function getStatusTagType(status: string) {
min="0" min="0"
:max="item.quantity" :max="item.quantity"
/> />
<span class="resource-line-amount">¥{{ resourceLineAmount(item).toFixed(2) }}</span> <span class="resource-line-amount">¥{{ money(resourceLineAmount(item)) }}</span>
</div> </div>
</div> </div>
<div class="resource-panel-footer"> <div class="resource-panel-footer">
<span>物资费用扣款合计</span> <span>物资金额合计</span>
<strong>¥{{ resourceChargeAmount.toFixed(2) }}</strong> <strong>¥{{ money(resourceChargeAmount) }}</strong>
</div> </div>
</div> </div>
@@ -735,7 +749,7 @@ function getStatusTagType(status: string) {
</template> </template>
</van-field> </van-field>
<div class="coin-hint"> <div class="coin-hint">
订单快照 {{ snapshotHafCoinM.toFixed(2) }}M预计剩余 {{ remainingHafCoinM.toFixed(2) }}M 订单快照 {{ quantity(snapshotHafCoinM) }}M预计剩余 {{ quantity(remainingHafCoinM) }}M
</div> </div>
<van-field label="其他费用" label-width="100px"> <van-field label="其他费用" label-width="100px">
<template #input> <template #input>
@@ -778,13 +792,12 @@ function getStatusTagType(status: string) {
<section v-if="order.checkout && ['pending_checkout_confirm', 'pending_checkout_accept'].includes(order.status)" class="card-section"> <section v-if="order.checkout && ['pending_checkout_confirm', 'pending_checkout_accept'].includes(order.status)" class="card-section">
<h3 class="section-title">结账结算账单</h3> <h3 class="section-title">结账结算账单</h3>
<van-cell-group inset :border="false"> <van-cell-group inset :border="false">
<van-cell title="买家租金" :value="`¥${order.checkout.rent_amount}`" /> <van-cell :title="orderAmountLabel" :value="`¥${money(order.checkout.display_amount)}`" />
<van-cell title="号主实收租金" :value="`¥${order.checkout.owner_rent_amount}`" /> <van-cell title="押金总额" :value="`¥${money(order.checkout.deposit_amount)}`" />
<van-cell title="平台服务费" :value="`¥${order.checkout.platform_fee}`" /> <van-cell title="额外消耗品金额" :value="`¥${money(order.checkout.consumable_amount)}`" />
<van-cell title="押金总额" :value="`¥${order.checkout.deposit_amount}`" /> <van-cell title="押金扣除" :value="`¥${money(order.checkout.deposit_deduct_amount)}`" value-class="red-text" />
<van-cell title="扣款总计" :value="`¥${order.checkout.deposit_deduct_amount}`" value-class="red-text" /> <van-cell v-if="isRenter" title="退还租客押金" :value="`¥${money(order.checkout.renter_refund_amount)}`" value-class="green-text" />
<van-cell title="退还租客押金" :value="`¥${order.checkout.renter_refund_amount}`" value-class="green-text" /> <van-cell v-if="isOwner" title="号主最终收益" :value="`¥${money(order.checkout.owner_income_amount)}`" value-class="green-text" />
<van-cell title="号主最终收益" :value="`¥${order.checkout.owner_income_amount}`" value-class="green-text" />
<van-cell v-if="order.checkout.content" title="结账备注" :label="order.checkout.content" /> <van-cell v-if="order.checkout.content" title="结账备注" :label="order.checkout.content" />
<van-cell v-if="order.checkout.owner_adjustment_reason" title="号主修正原因" :label="order.checkout.owner_adjustment_reason" /> <van-cell v-if="order.checkout.owner_adjustment_reason" title="号主修正原因" :label="order.checkout.owner_adjustment_reason" />
</van-cell-group> </van-cell-group>
@@ -93,6 +93,10 @@ function readError(error: unknown, fallback: string) {
} }
return fallback; return fallback;
} }
function money(value: unknown) {
return Math.round(Number(value || 0));
}
</script> </script>
<template> <template>
@@ -164,12 +168,12 @@ function readError(error: unknown, fallback: string) {
<div class="price-row"> <div class="price-row">
<div class="price-item"> <div class="price-item">
<span class="price-label">买家实付租金</span> <span class="price-label">我的金额</span>
<span class="price-val">¥{{ order.rent_amount }}</span> <span class="price-val">¥{{ money(order.display_amount) }}</span>
</div> </div>
<div class="price-item"> <div class="price-item">
<span class="price-label">押金金额</span> <span class="price-label">押金金额</span>
<span class="price-val deposit">¥{{ order.deposit_amount }}</span> <span class="price-val deposit">¥{{ money(order.deposit_amount) }}</span>
</div> </div>
</div> </div>
</div> </div>
@@ -276,7 +276,7 @@ function resolveAvatarURL(url: string | undefined | null) {
<div class="balance-row"> <div class="balance-row">
<div class="balance-info"> <div class="balance-info">
<span class="balance-label">账户可用余额()</span> <span class="balance-label">账户可用余额()</span>
<strong class="balance-value">¥{{ balance.toFixed(2) }}</strong> <strong class="balance-value">¥{{ Math.round(Number(balance || 0)) }}</strong>
</div> </div>
<button class="withdraw-btn" @click="handleWithdraw">提现</button> <button class="withdraw-btn" @click="handleWithdraw">提现</button>
</div> </div>
@@ -375,7 +375,7 @@ function resolveAvatarURL(url: string | undefined | null) {
<span class="ledger-time">{{ formatDateMinute(item.created_at) }}</span> <span class="ledger-time">{{ formatDateMinute(item.created_at) }}</span>
</div> </div>
<div class="ledger-right" :class="item.direction === 'in' ? 'in-color' : 'out-color'"> <div class="ledger-right" :class="item.direction === 'in' ? 'in-color' : 'out-color'">
{{ item.direction === 'in' ? '+' : '-' }}¥{{ item.amount.toFixed(2) }} {{ item.direction === 'in' ? '+' : '-' }}¥{{ Math.round(Number(item.amount || 0)) }}
</div> </div>
</div> </div>
</div> </div>
@@ -129,7 +129,7 @@ function getStatusText(item: Listing) {
} }
function listingPrice(item: Listing) { function listingPrice(item: Listing) {
return getListingSellerPrice(item).toFixed(2) return `${Math.round(getListingSellerPrice(item))}`
} }
function goBack() { function goBack() {
@@ -197,7 +197,7 @@ function goBack() {
<div class="price-row"> <div class="price-row">
<span class="price-val">¥{{ listingPrice(item) }}</span> <span class="price-val">¥{{ listingPrice(item) }}</span>
<span class="deposit-val">押金: ¥{{ item.deposit_amount.toFixed(2) }}</span> <span class="deposit-val">押金: ¥{{ Math.round(Number(item.deposit_amount || 0)) }}</span>
</div> </div>
</div> </div>
</div> </div>
@@ -45,7 +45,7 @@ function readError(error: unknown, fallback: string) {
} }
function listingPrice(item: Listing) { function listingPrice(item: Listing) {
return Number(item.price || 0).toFixed(2); return `${Math.round(Number(item.price || 0))}`;
} }
</script> </script>
@@ -59,6 +59,10 @@ function actionText(order: Order) {
if (['overdue', 'checkout_disputing', 'disputing', 'abnormal'].includes(order.status)) return '查看处理' if (['overdue', 'checkout_disputing', 'disputing', 'abnormal'].includes(order.status)) return '查看处理'
return '详情' return '详情'
} }
function money(value: unknown) {
return Math.round(Number(value || 0))
}
</script> </script>
<template> <template>
@@ -103,7 +107,7 @@ function actionText(order: Order) {
<el-table-column prop="order_no" label="订单号" min-width="220" /> <el-table-column prop="order_no" label="订单号" min-width="220" />
<el-table-column prop="title" label="账号" min-width="180" /> <el-table-column prop="title" label="账号" min-width="180" />
<el-table-column label="金额" width="120"> <el-table-column label="金额" width="120">
<template #default="{ row }">¥{{ row.rent_amount }}</template> <template #default="{ row }">¥{{ money(row.display_amount) }}</template>
</el-table-column> </el-table-column>
<el-table-column label="订单状态" width="150"> <el-table-column label="订单状态" width="150">
<template #default="{ row }">{{ orderStatusLabel(row.status) }}</template> <template #default="{ row }">{{ orderStatusLabel(row.status) }}</template>
@@ -37,7 +37,7 @@ async function offline(id: number) {
} }
function listingPrice(row: Listing) { function listingPrice(row: Listing) {
return `¥${getListingSellerPrice(row).toFixed(2)}` return `¥${Math.round(getListingSellerPrice(row))}`
} }
</script> </script>