feat(listings): 订单和账号详情展示可租时长与每日消耗
租客在订单列表和详情标题能看到每个号的租期、消耗。账号详情把这两项拆成独立格子。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -96,6 +96,8 @@ type UserOrderListItemDTO struct {
|
||||
Title string `json:"title"`
|
||||
ServerRegion string `json:"server_region"`
|
||||
LoginPlatform string `json:"login_platform"`
|
||||
RentalDays int `json:"rental_days,omitempty"`
|
||||
DailyLossM float64 `json:"daily_loss_m,omitempty"`
|
||||
DisplayAmountCent int64 `json:"display_amount_cent"`
|
||||
DepositAmountCent int64 `json:"deposit_amount_cent"`
|
||||
DepositWaivedAmountCent int64 `json:"deposit_waived_amount_cent"`
|
||||
|
||||
@@ -179,6 +179,7 @@ func (row orderRow) toUserListItem(userID uint64, paymentDeadlineAt *time.Time)
|
||||
if role == "owner" && row.OwnerRentAmountCent > 0 {
|
||||
displayAmountCent = row.OwnerRentAmountCent
|
||||
}
|
||||
rentalDays, dailyLossM := orderListingTerms(row.AccountSnapshot)
|
||||
return UserOrderListItemDTO{
|
||||
ID: row.ID,
|
||||
OrderNo: row.OrderNo,
|
||||
@@ -188,6 +189,8 @@ func (row orderRow) toUserListItem(userID uint64, paymentDeadlineAt *time.Time)
|
||||
Title: row.Title,
|
||||
ServerRegion: row.ServerRegion,
|
||||
LoginPlatform: row.LoginPlatform,
|
||||
RentalDays: rentalDays,
|
||||
DailyLossM: dailyLossM,
|
||||
DisplayAmountCent: displayAmountCent,
|
||||
DepositAmountCent: row.DepositAmountCent,
|
||||
DepositWaivedAmountCent: row.DepositWaivedAmountCent,
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"time"
|
||||
|
||||
"hfb_sys/backend/internal/model"
|
||||
|
||||
"gorm.io/datatypes"
|
||||
)
|
||||
|
||||
func TestOrderDTOForUserHidesDepositHoldFields(t *testing.T) {
|
||||
@@ -72,6 +74,44 @@ func TestUserOrderListItemDoesNotSerializeDetailFields(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserOrderListItemIncludesRentalTerms(t *testing.T) {
|
||||
row := orderRow{
|
||||
RentalOrder: model.RentalOrder{
|
||||
AccountSnapshot: datatypes.JSON([]byte(`{"haf_coin_amount":100000000,"asset_summary":{"daily_loss_m":10}}`)),
|
||||
},
|
||||
Title: "微信 三角洲巅峰 100M哈夫币",
|
||||
}
|
||||
|
||||
item := row.toUserListItem(20, nil)
|
||||
if item.RentalDays != 10 {
|
||||
t.Fatalf("rental days = %d, want 10", item.RentalDays)
|
||||
}
|
||||
if item.DailyLossM != 10 {
|
||||
t.Fatalf("daily loss = %v, want 10", item.DailyLossM)
|
||||
}
|
||||
|
||||
raw, err := json.Marshal(item)
|
||||
if err != nil {
|
||||
t.Fatalf("Marshal() error = %v", err)
|
||||
}
|
||||
text := string(raw)
|
||||
if !strings.Contains(text, `"rental_days":10`) || !strings.Contains(text, `"daily_loss_m":10`) {
|
||||
t.Fatalf("list response missing rental terms: %s", text)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserOrderListItemOmitsRentalTermsWithoutSnapshot(t *testing.T) {
|
||||
item := orderRow{Title: "测试账号"}.toUserListItem(20, nil)
|
||||
raw, err := json.Marshal(item)
|
||||
if err != nil {
|
||||
t.Fatalf("Marshal() error = %v", err)
|
||||
}
|
||||
text := string(raw)
|
||||
if strings.Contains(text, "rental_days") || strings.Contains(text, "daily_loss_m") {
|
||||
t.Fatalf("empty snapshot should omit rental terms: %s", text)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdminOrderListItemShowsPhonesAndOmitsSnapshot(t *testing.T) {
|
||||
row := orderRow{
|
||||
RentalOrder: model.RentalOrder{ID: 1, OrderNo: "ORD-001", Status: orderStatusPendingPayment},
|
||||
|
||||
@@ -124,6 +124,20 @@ func orderEstimatedEndAt(order model.RentalOrder) *time.Time {
|
||||
return &endAt
|
||||
}
|
||||
|
||||
// orderListingTerms 用下单快照里的哈夫币和日损耗估算租期,和首页「可租 / 消耗」同一口径。
|
||||
func orderListingTerms(snapshot datatypes.JSON) (days int, dailyLossM float64) {
|
||||
dailyLossM = readOrderSnapshotAssetNumber(snapshot, "daily_loss_m")
|
||||
coinM := readOrderSnapshotCoinM(snapshot)
|
||||
if dailyLossM <= 0 || coinM <= 0 {
|
||||
return 0, 0
|
||||
}
|
||||
days = int(math.Ceil(coinM / dailyLossM))
|
||||
if days < 1 {
|
||||
days = 1
|
||||
}
|
||||
return days, roundQuantity(dailyLossM)
|
||||
}
|
||||
|
||||
func estimateOrderDurationHours(snapshot datatypes.JSON) int {
|
||||
hafCoinM := readOrderSnapshotCoinM(snapshot)
|
||||
if hafCoinM <= 0 {
|
||||
|
||||
Reference in New Issue
Block a user