金额使用整数, 不再扣押金, 价格每个人看到自己的-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 -8
View File
@@ -20,10 +20,12 @@ type OrderDTO struct {
LoginPlatform string `json:"login_platform"`
RentedAt *time.Time `json:"rented_at"`
EstimatedDurationHours int `json:"estimated_duration_hours"`
RentAmount float64 `json:"rent_amount"`
OwnerRentAmount float64 `json:"owner_rent_amount"`
PriceRole string `json:"price_role,omitempty"`
DisplayAmount float64 `json:"display_amount"`
RentAmount *float64 `json:"rent_amount,omitempty"`
OwnerRentAmount *float64 `json:"owner_rent_amount,omitempty"`
DepositAmount float64 `json:"deposit_amount"`
PlatformFee float64 `json:"platform_fee"`
PlatformFee *float64 `json:"platform_fee,omitempty"`
AccountSnapshot datatypes.JSON `json:"account_snapshot"`
Status string `json:"status"`
HandoffStatus string `json:"handoff_status"`
@@ -88,16 +90,18 @@ type CheckoutDTO struct {
OrderID uint64 `json:"order_id"`
InitiatedBy uint64 `json:"initiated_by"`
Status string `json:"status"`
RentAmount float64 `json:"rent_amount"`
OwnerRentAmount float64 `json:"owner_rent_amount"`
PlatformFee float64 `json:"platform_fee"`
PriceRole string `json:"price_role,omitempty"`
DisplayAmount float64 `json:"display_amount"`
RentAmount *float64 `json:"rent_amount,omitempty"`
OwnerRentAmount *float64 `json:"owner_rent_amount,omitempty"`
PlatformFee *float64 `json:"platform_fee,omitempty"`
DepositAmount float64 `json:"deposit_amount"`
ConsumableAmount float64 `json:"consumable_amount"`
CoinConsumedM float64 `json:"coin_consumed_m"`
OtherAmount float64 `json:"other_amount"`
DepositDeductAmount float64 `json:"deposit_deduct_amount"`
RenterRefundAmount float64 `json:"renter_refund_amount"`
OwnerIncomeAmount float64 `json:"owner_income_amount"`
RenterRefundAmount *float64 `json:"renter_refund_amount,omitempty"`
OwnerIncomeAmount *float64 `json:"owner_income_amount,omitempty"`
Content string `json:"content"`
EvidenceURLS []string `json:"evidence_urls"`
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 {
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
}
@@ -665,7 +670,7 @@ func (r *Repository) ListForUser(userID uint64) ([]OrderDTO, error) {
}
items := make([]OrderDTO, 0, len(rows))
for _, row := range rows {
items = append(items, row.toDTO())
items = append(items, row.toDTOForUser(userID))
}
return items, nil
}
@@ -681,7 +686,7 @@ func (r *Repository) ListAdmin() ([]OrderDTO, error) {
}
items := make([]OrderDTO, 0, len(rows))
for _, row := range rows {
items = append(items, row.toDTO())
items = append(items, row.toAdminDTO())
}
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 {
return nil, err
}
dto := row.toDTO()
dto.Checkout = r.latestCheckoutDTO(orderID)
dto := row.toAdminDTO()
dto.Checkout = r.latestCheckoutAdminDTO(orderID)
return &dto, nil
}
@@ -882,8 +887,8 @@ func (r *Repository) FindForUser(userID uint64, orderID uint64) (*OrderDTO, erro
First(&row).Error; err != nil {
return nil, err
}
dto := row.toDTO()
dto.Checkout = r.latestCheckoutDTO(orderID)
dto := row.toDTOForUser(userID)
dto.Checkout = r.latestCheckoutDTOForUser(orderID, userID, row.RentalOrder)
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 {
return model.OrderCheckout{}, ErrInvalidCheckoutAmount
}
deductAmount := consumableAmount + otherAmount
consumableAmount = roundMoney(consumableAmount)
otherAmount = roundMoney(otherAmount)
explicitDeduct = roundMoney(explicitDeduct)
deductAmount := otherAmount
if useExplicitDeduct {
deductAmount = explicitDeduct
}
@@ -1038,9 +1046,9 @@ func buildCheckout(order model.RentalOrder, initiatedBy uint64, status string, c
OwnerRentAmount: order.OwnerRentAmount,
PlatformFee: order.PlatformFee,
DepositAmount: order.DepositAmount,
ConsumableAmount: roundMoney(consumableAmount),
CoinConsumedM: roundMoney(coinConsumedM),
OtherAmount: roundMoney(otherAmount),
ConsumableAmount: consumableAmount,
CoinConsumedM: roundQuantity(coinConsumedM),
OtherAmount: otherAmount,
DepositDeductAmount: roundMoney(deductAmount),
RenterRefundAmount: roundMoney(renterRefund),
OwnerIncomeAmount: roundMoney(order.OwnerRentAmount + deductAmount),
@@ -1069,6 +1077,10 @@ func decodeStringList(raw datatypes.JSON) []string {
}
func roundMoney(value float64) float64 {
return math.Round(value)
}
func roundQuantity(value float64) float64 {
return math.Round(value*100) / 100
}
@@ -1080,12 +1092,21 @@ func hasOpenCheckout(tx *gorm.DB, orderID uint64) (bool, error) {
return count > 0, err
}
func (r *Repository) latestCheckoutDTO(orderID uint64) *CheckoutDTO {
func (r *Repository) latestCheckoutAdminDTO(orderID uint64) *CheckoutDTO {
var checkout model.OrderCheckout
if err := r.db.Where("order_id = ?", orderID).Order("id DESC").First(&checkout).Error; err != 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
}
@@ -1120,9 +1141,12 @@ type orderRow struct {
RenterPhone string
}
func (row orderRow) toDTO() OrderDTO {
func (row orderRow) toAdminDTO() OrderDTO {
rentedAt := row.RentedAt
durationHours := orderDurationHours(row.RentalOrder)
rentAmount := row.RentAmount
ownerRentAmount := row.OwnerRentAmount
platformFee := row.PlatformFee
return OrderDTO{
ID: row.ID,
OrderNo: row.OrderNo,
@@ -1137,10 +1161,12 @@ func (row orderRow) toDTO() OrderDTO {
LoginPlatform: row.LoginPlatform,
RentedAt: rentedAt,
EstimatedDurationHours: durationHours,
RentAmount: row.RentAmount,
OwnerRentAmount: row.OwnerRentAmount,
PriceRole: "admin",
DisplayAmount: row.RentAmount,
RentAmount: &rentAmount,
OwnerRentAmount: &ownerRentAmount,
DepositAmount: row.DepositAmount,
PlatformFee: row.PlatformFee,
PlatformFee: &platformFee,
AccountSnapshot: row.AccountSnapshot,
Status: row.Status,
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 {
return HandoffRecordDTO{
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{
ID: checkout.ID,
OrderID: checkout.OrderID,
InitiatedBy: checkout.InitiatedBy,
Status: checkout.Status,
RentAmount: checkout.RentAmount,
OwnerRentAmount: checkout.OwnerRentAmount,
PlatformFee: checkout.PlatformFee,
PriceRole: "admin",
DisplayAmount: checkout.RentAmount,
RentAmount: &rentAmount,
OwnerRentAmount: &ownerRentAmount,
PlatformFee: &platformFee,
DepositAmount: checkout.DepositAmount,
ConsumableAmount: checkout.ConsumableAmount,
CoinConsumedM: checkout.CoinConsumedM,
OtherAmount: checkout.OtherAmount,
DepositDeductAmount: checkout.DepositDeductAmount,
RenterRefundAmount: checkout.RenterRefundAmount,
OwnerIncomeAmount: checkout.OwnerIncomeAmount,
RenterRefundAmount: &renterRefundAmount,
OwnerIncomeAmount: &ownerIncomeAmount,
Content: checkout.Content,
EvidenceURLS: decodeStringList(checkout.EvidenceURLS),
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) {
payload := map[string]any{
"account_id": account.ID,