package order import ( "encoding/json" "hfb_sys/backend/internal/model" "gorm.io/datatypes" ) func adminActionsForOrder(order model.RentalOrder) *AdminActionsDTO { target, ok := resetTargetForOrder(order) if !ok { return nil } return &AdminActionsDTO{ ResetHandoff: &AdminActionDTO{ Enabled: true, Label: target.Label, }, } } func (row orderRow) toAdminDTO() OrderDTO { rentedAt := row.RentedAt durationHours := orderDurationHours(row.RentalOrder) estimatedEndAt := orderEstimatedEndAt(row.RentalOrder) rentAmountCent := row.RentAmountCent ownerRentAmountCent := row.OwnerRentAmountCent platformFeeCent := row.PlatformFeeCent return OrderDTO{ ID: row.ID, OrderNo: row.OrderNo, ListingID: row.ListingID, ListingNo: row.ListingNo, AccountID: row.AccountID, OwnerID: row.OwnerID, RenterID: row.RenterID, OwnerPhone: row.OwnerPhone, RenterPhone: row.RenterPhone, Title: row.Title, ServerRegion: row.ServerRegion, LoginPlatform: row.LoginPlatform, RentedAt: rentedAt, EstimatedDurationHours: durationHours, EstimatedEndAt: estimatedEndAt, PriceRole: "admin", DisplayAmountCent: row.RentAmountCent, RentAmountCent: &rentAmountCent, OwnerRentAmountCent: &ownerRentAmountCent, DepositAmountCent: row.DepositAmountCent, DepositOriginalAmountCent: effectiveDepositOriginalAmountCent(row.RentalOrder), DepositWaivedAmountCent: row.DepositWaivedAmountCent, PlatformFeeCent: &platformFeeCent, AccountSnapshot: row.AccountSnapshot, Status: row.Status, HandoffStatus: row.HandoffStatus, SettlementStatus: row.SettlementStatus, RefundStatus: row.RefundStatus, RefundAmountCent: row.RefundAmountCent, DepositHoldStatus: row.DepositHoldStatus, DepositHoldAmountCent: row.DepositHoldAmountCent, DepositHoldReason: row.DepositHoldReason, DepositHeldAt: row.DepositHeldAt, DepositHoldReleasedAt: row.DepositHoldReleasedAt, AdminActions: adminActionsForOrder(row.RentalOrder), CreatedAt: row.CreatedAt, UpdatedAt: row.UpdatedAt, } } func (row orderRow) toDTOForUser(userID uint64) OrderDTO { dto := row.toAdminDTO() dto.AdminActions = nil // 押金暂扣属于客服内部处置,不向租客/号主暴露。 dto.DepositHoldStatus = "" dto.DepositHoldAmountCent = 0 dto.DepositHoldReason = "" dto.DepositHeldAt = nil dto.DepositHoldReleasedAt = nil applyOrderPriceView(&dto, row.RentalOrder, userID) return dto } func effectiveDepositOriginalAmountCent(order model.RentalOrder) int64 { if order.DepositOriginalAmountCent > 0 { return order.DepositOriginalAmountCent } return order.DepositAmountCent } func toCheckoutAdminDTO(checkout model.OrderCheckout) CheckoutDTO { rentAmountCent := checkout.RentAmountCent ownerRentAmountCent := checkout.OwnerRentAmountCent platformFeeCent := checkout.PlatformFeeCent renterRefundAmountCent := checkout.RenterRefundAmountCent ownerIncomeAmountCent := checkout.OwnerIncomeAmountCent return CheckoutDTO{ ID: checkout.ID, OrderID: checkout.OrderID, InitiatedBy: checkout.InitiatedBy, Status: checkout.Status, PriceRole: "admin", DisplayAmountCent: rentAmountCent, RentAmountCent: &rentAmountCent, OwnerRentAmountCent: &ownerRentAmountCent, PlatformFeeCent: &platformFeeCent, DepositAmountCent: checkout.DepositAmountCent, ConsumableAmountCent: checkout.ConsumableAmountCent, CoinConsumedM: checkout.CoinConsumedM, OtherAmountCent: checkout.OtherAmountCent, DepositDeductAmountCent: checkout.DepositDeductAmountCent, RenterRefundAmountCent: &renterRefundAmountCent, OwnerIncomeAmountCent: &ownerIncomeAmountCent, Content: checkout.Content, EvidenceURLS: decodeStringList(checkout.EvidenceURLS), OwnerAdjustmentReason: checkout.OwnerAdjustmentReason, OwnerAdjustedAt: checkout.OwnerAdjustedAt, RenterConfirmedAt: checkout.RenterConfirmedAt, RenterRejectedAt: checkout.RenterRejectedAt, CreatedAt: checkout.CreatedAt, UpdatedAt: checkout.UpdatedAt, } } 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.PlatformFeeCent = nil switch { case userID == order.OwnerID: ownerAmountCent := order.OwnerRentAmountCent if ownerAmountCent <= 0 { ownerAmountCent = order.RentAmountCent } dto.PriceRole = "owner" dto.DisplayAmountCent = ownerAmountCent dto.RentAmountCent = nil dto.OwnerRentAmountCent = &ownerAmountCent sanitizeOrderSnapshot(&dto.AccountSnapshot, "owner") case userID == order.RenterID: rentAmountCent := order.RentAmountCent dto.PriceRole = "renter" dto.DisplayAmountCent = rentAmountCent dto.RentAmountCent = &rentAmountCent dto.OwnerRentAmountCent = nil sanitizeOrderSnapshot(&dto.AccountSnapshot, "renter") default: dto.PriceRole = "" dto.DisplayAmountCent = 0 dto.RentAmountCent = nil dto.OwnerRentAmountCent = nil sanitizeOrderSnapshot(&dto.AccountSnapshot, "") } } func applyCheckoutPriceView(dto *CheckoutDTO, order model.RentalOrder, userID uint64) { if dto == nil { return } ownerAmountCent := int64(0) if dto.OwnerRentAmountCent != nil { ownerAmountCent = *dto.OwnerRentAmountCent } ownerIncomeAmountCent := int64(0) if dto.OwnerIncomeAmountCent != nil { ownerIncomeAmountCent = *dto.OwnerIncomeAmountCent } rentAmountCent := int64(0) if dto.RentAmountCent != nil { rentAmountCent = *dto.RentAmountCent } renterRefundAmountCent := int64(0) if dto.RenterRefundAmountCent != nil { renterRefundAmountCent = *dto.RenterRefundAmountCent } dto.PlatformFeeCent = nil dto.RenterRefundAmountCent = nil dto.OwnerIncomeAmountCent = nil switch { case userID == order.OwnerID: dto.PriceRole = "owner" dto.DisplayAmountCent = ownerAmountCent dto.RentAmountCent = nil dto.OwnerRentAmountCent = &ownerAmountCent dto.OwnerIncomeAmountCent = &ownerIncomeAmountCent case userID == order.RenterID: dto.PriceRole = "renter" dto.DisplayAmountCent = rentAmountCent dto.RentAmountCent = &rentAmountCent dto.OwnerRentAmountCent = nil dto.RenterRefundAmountCent = &renterRefundAmountCent default: dto.PriceRole = "" dto.DisplayAmountCent = 0 dto.RentAmountCent = nil dto.OwnerRentAmountCent = 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 marshalStringList(items []string) (datatypes.JSON, error) { if items == nil { items = []string{} } raw, err := json.Marshal(items) return datatypes.JSON(raw), err } func decodeStringList(raw datatypes.JSON) []string { if len(raw) == 0 { return []string{} } var items []string if err := json.Unmarshal(raw, &items); err != nil { return []string{} } return items } func makeAccountSnapshot(account model.GameAccount, listing model.RentalListing) (datatypes.JSON, error) { payload := map[string]any{ "listing_id": listing.ID, "listing_no": listing.ListingNo, "account_id": account.ID, "title": account.Title, "game_name": account.GameName, "server_region": account.ServerRegion, "login_platform": account.LoginPlatform, "rank_level": account.RankLevel, "haf_coin_amount": account.HafCoinAmount, "asset_summary": account.AssetSummary, "season_tags": account.SeasonTags, "screenshot_urls": account.ScreenshotURLS, "snapshot_version": 1, } raw, err := json.Marshal(payload) return datatypes.JSON(raw), err }