diff --git a/README.md b/README.md index f8d1410..1512d79 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ ./scripts/dev.sh ``` -脚本会自动启动 MySQL、Redis、MinIO,首次初始化数据库结构,刷新开发测试数据,并同时启动后端和前端。 +脚本会自动启动 MySQL、Redis、MinIO,首次初始化数据库结构,并同时启动后端和前端。 前端地址:`http://localhost:5173` @@ -28,7 +28,6 @@ docker compose -f deploy/docker-compose.dev.yml up -d docker exec -i hfb-mysql mysql -uhfb -psecret hfb_sys < backend/migrations/000001_init.sql -docker exec -i hfb-mysql mysql -uhfb -psecret hfb_sys < backend/seeds/dev_orders.sql cd backend cp .env.example .env diff --git a/backend/internal/modules/listing/dto.go b/backend/internal/modules/listing/dto.go index 1b67d9c..8b1b918 100644 --- a/backend/internal/modules/listing/dto.go +++ b/backend/internal/modules/listing/dto.go @@ -22,6 +22,7 @@ type ListingDTO struct { PriceDaily float64 `json:"price_daily"` PriceWeekly float64 `json:"price_weekly"` DepositAmount float64 `json:"deposit_amount"` + IsAccelerated bool `json:"is_accelerated_sale"` Status string `json:"status"` ReviewStatus string `json:"review_status"` ReviewReason string `json:"review_reason"` diff --git a/backend/internal/modules/listing/repository.go b/backend/internal/modules/listing/repository.go index 79c066e..1592a13 100644 --- a/backend/internal/modules/listing/repository.go +++ b/backend/internal/modules/listing/repository.go @@ -521,6 +521,7 @@ func (row listingRow) toDTO() ListingDTO { PriceDaily: row.PriceDaily, PriceWeekly: row.PriceWeekly, DepositAmount: row.DepositAmount, + IsAccelerated: isAcceleratedSale(assetSummary), Status: row.Status, ReviewStatus: row.ReviewStatus, ReviewReason: row.ReviewReason, @@ -551,6 +552,7 @@ func toDTO(account model.GameAccount, listing model.RentalListing) *ListingDTO { PriceDaily: listing.PriceDaily, PriceWeekly: listing.PriceWeekly, DepositAmount: listing.DepositAmount, + IsAccelerated: isAcceleratedSale(assetSummary), Status: listing.Status, ReviewStatus: listing.ReviewStatus, ReviewReason: listing.ReviewReason, @@ -605,6 +607,50 @@ func decodeAssetSummary(raw datatypes.JSON) map[string]any { return summary } +func isAcceleratedSale(summary map[string]any) bool { + if summary == nil { + return false + } + breakdown, ok := summary["price_breakdown"].(map[string]any) + if !ok { + return false + } + referenceRatio := readSummaryNumber(breakdown["seller_reference_ratio"]) + sellerRatio := readSummaryNumber(breakdown["seller_ratio"]) + acceleratedRatio := readSummaryNumber(breakdown["accelerated_sale_ratio"]) + if referenceRatio <= 0 { + return false + } + return sellerRatio > referenceRatio || acceleratedRatio > referenceRatio +} + +func readSummaryNumber(value any) float64 { + switch typed := value.(type) { + case float64: + return typed + case float32: + return float64(typed) + case int: + return float64(typed) + case int64: + return float64(typed) + case json.Number: + number, err := typed.Float64() + if err != nil { + return 0 + } + return number + case string: + number, err := strconv.ParseFloat(strings.TrimSpace(typed), 64) + if err != nil { + return 0 + } + return number + default: + return 0 + } +} + func cleanScreenshotURLs(urls []string) []string { cleaned := make([]string, 0, len(urls)) seen := make(map[string]struct{}, len(urls)) diff --git a/backend/seeds/dev_orders.sql b/backend/seeds/dev_orders.sql deleted file mode 100644 index d22fb18..0000000 --- a/backend/seeds/dev_orders.sql +++ /dev/null @@ -1,392 +0,0 @@ --- 开发环境测试数据:清空旧业务数据并生成带测试图片的已发布账号。 --- 用法: --- mysql -uhfb -psecret hfb_sys < backend/seeds/dev_orders.sql --- 或: --- docker exec -i hfb-mysql mysql -uhfb -psecret hfb_sys < backend/seeds/dev_orders.sql - -SET NAMES utf8mb4; - -SET @old_safe_updates = @@SQL_SAFE_UPDATES; -SET SQL_SAFE_UPDATES = 0; - -DELETE FROM disputes; -DELETE FROM handoff_records; -DELETE FROM wallet_ledger; -DELETE FROM notifications WHERE biz_type IN ('order', 'listing', 'listing_review', 'listing_admin') OR biz_type = ''; -DELETE FROM rental_orders; -DELETE FROM rental_listings; -DELETE FROM game_accounts; - -DELETE FROM user_realname WHERE user_id IN (SELECT id FROM users WHERE phone IN ('13800000001', '13800000002', '13800000003', '13900000001')); -DELETE FROM wallet_accounts WHERE user_id IN (SELECT id FROM users WHERE phone IN ('13800000001', '13800000002', '13800000003', '13900000001')); -DELETE FROM users WHERE phone IN ('13800000001', '13800000002', '13800000003', '13900000001'); - -ALTER TABLE game_accounts AUTO_INCREMENT = 1; -ALTER TABLE rental_listings AUTO_INCREMENT = 1; -ALTER TABLE rental_orders AUTO_INCREMENT = 1; -ALTER TABLE handoff_records AUTO_INCREMENT = 1; -ALTER TABLE disputes AUTO_INCREMENT = 1; -ALTER TABLE wallet_ledger AUTO_INCREMENT = 1; -ALTER TABLE notifications AUTO_INCREMENT = 1; - -SET SQL_SAFE_UPDATES = @old_safe_updates; - -INSERT INTO users (phone, nickname, realname_status, risk_status, credit_score, status) -VALUES - ('13800000001', '测试号主A', 'verified', 'normal', 100, 'active'), - ('13800000002', '测试号主B', 'verified', 'normal', 100, 'active'), - ('13800000003', '测试号主C', 'verified', 'normal', 100, 'active'), - ('13900000001', '测试租客A', 'verified', 'normal', 100, 'active'); - -SELECT id INTO @owner_a FROM users WHERE phone = '13800000001'; -SELECT id INTO @owner_b FROM users WHERE phone = '13800000002'; -SELECT id INTO @owner_c FROM users WHERE phone = '13800000003'; -SELECT id INTO @renter_a FROM users WHERE phone = '13900000001'; - -INSERT INTO user_realname (user_id, provider, provider_order_no, status, masked_name, masked_id_no, verified_at) -VALUES - (@owner_a, 'mock', 'seed-owner-a', 'verified', '测*A', '110***********0001', NOW()), - (@owner_b, 'mock', 'seed-owner-b', 'verified', '测*B', '110***********0002', NOW()), - (@owner_c, 'mock', 'seed-owner-c', 'verified', '测*C', '110***********0003', NOW()), - (@renter_a, 'mock', 'seed-renter-a', 'verified', '测*客', '110***********1001', NOW()); - -INSERT INTO wallet_accounts (user_id, available_balance, frozen_balance, status) -VALUES - (@owner_a, 0.00, 0.00, 'active'), - (@owner_b, 0.00, 0.00, 'active'), - (@owner_c, 0.00, 0.00, 'active'), - (@renter_a, 5000.00, 0.00, 'active'); - -INSERT INTO game_accounts - (owner_id, game_name, server_region, login_platform, title, description, rank_level, haf_coin_amount, asset_summary, screenshot_urls, status, created_at, updated_at) -VALUES - ( - @owner_a, '三角洲行动', 'QQ', '扫码', - '纯币300M/6格/6体/6负/4六甲/6六头', - '扫码交接,保险 2*3,资源充足,适合高强度体验。', - '黑鹰', 300000000, - JSON_OBJECT( - 'face_owner', '是', - 'total_asset_wan', 33700, - 'secret_kd', 2.8, - 'fire_level', 58, - 'season_insurance', '2*3', - 'stamina_level', '6级', - 'load_level', '6级', - 'common_regions', JSON_ARRAY('广东', '上海'), - 'online_time', JSON_OBJECT('start', '10:00', 'end', '23:00'), - 'ban_record', '无封禁记录', - 'resources', JSON_ARRAY( - JSON_OBJECT('key', 'awmAmmo', 'label', 'AWM子弹', 'quantity', 80, 'mode', '收费'), - JSON_OBJECT('key', 'helmet6', 'label', '6头', 'quantity', 6, 'mode', '收费'), - JSON_OBJECT('key', 'armor6', 'label', '6甲', 'quantity', 4, 'mode', '收费'), - JSON_OBJECT('key', 'level6Ammo', 'label', '6级弹', 'quantity', 360, 'mode', '收费') - ), - 'skin_groups', JSON_OBJECT( - 'melee', JSON_ARRAY('龙牙', '信条'), - 'operatorGold', JSON_ARRAY(), - 'operatorRed', JSON_ARRAY('凌霄成卫', '蚀金玫瑰'), - 'weapon', JSON_ARRAY('M4A1棱镜攻势', 'K416命运') - ), - 'remark', '部分仓库材料请勿使用,下单后会再次提醒。' - ), - JSON_ARRAY( - 'https://picsum.photos/seed/hfb-listing-001-coin/960/540', - 'https://picsum.photos/seed/hfb-listing-001-id/960/540', - 'https://picsum.photos/seed/hfb-listing-001-asset/960/540', - 'https://picsum.photos/seed/hfb-listing-001-security/960/540', - 'https://picsum.photos/seed/hfb-listing-001-skin/960/540' - ), - 'published', NOW() - INTERVAL 80 MINUTE, NOW() - INTERVAL 80 MINUTE - ), - ( - @owner_a, '三角洲行动', '微信', '账号密码', - '纯币120M/4格/5体/5负/2六头', - '账密登录,低押金,保险 2*2,常用登录地江苏。', - '钻石', 120000000, - JSON_OBJECT( - 'face_owner', '否', - 'total_asset_wan', 13800, - 'secret_kd', 1.9, - 'fire_level', 46, - 'season_insurance', '2*2', - 'stamina_level', '5级', - 'load_level', '5级', - 'common_regions', JSON_ARRAY('江苏', '浙江'), - 'online_time', JSON_OBJECT('start', '12:00', 'end', '24:00'), - 'ban_record', '无封禁记录', - 'resources', JSON_ARRAY( - JSON_OBJECT('key', 'awmAmmo', 'label', 'AWM子弹', 'quantity', 30, 'mode', '收费'), - JSON_OBJECT('key', 'helmet6', 'label', '6头', 'quantity', 2, 'mode', '收费') - ), - 'skin_groups', JSON_OBJECT( - 'melee', JSON_ARRAY('怜悯'), - 'operatorGold', JSON_ARRAY(), - 'operatorRed', JSON_ARRAY('天际线'), - 'weapon', JSON_ARRAY('SCAR-H电玩高手') - ), - 'remark', '' - ), - JSON_ARRAY( - 'https://picsum.photos/seed/hfb-listing-002-coin/960/540', - 'https://picsum.photos/seed/hfb-listing-002-id/960/540', - 'https://picsum.photos/seed/hfb-listing-002-asset/960/540', - 'https://picsum.photos/seed/hfb-listing-002-security/960/540', - 'https://picsum.photos/seed/hfb-listing-002-skin/960/540' - ), - 'published', NOW() - INTERVAL 70 MINUTE, NOW() - INTERVAL 70 MINUTE - ), - ( - @owner_b, '三角洲行动', 'Steam', '扫码', - '纯币60M/2格/4体/4负/1体验卡', - '可包夜,保险 2*1,适合短租。', - '铂金', 60000000, - JSON_OBJECT( - 'face_owner', '是', - 'total_asset_wan', 7200, - 'secret_kd', 1.4, - 'fire_level', 39, - 'season_insurance', '2*1', - 'stamina_level', '4级', - 'load_level', '4级', - 'common_regions', JSON_ARRAY('四川', '重庆'), - 'online_time', JSON_OBJECT('start', '18:00', 'end', '02:00'), - 'ban_record', '无封禁记录', - 'resources', JSON_ARRAY( - JSON_OBJECT('key', 'awmAmmo', 'label', 'AWM子弹', 'quantity', 12, 'mode', '收费'), - JSON_OBJECT('key', 'gridCard9', 'label', '9格体验卡', 'quantity', 1, 'mode', '收费') - ), - 'skin_groups', JSON_OBJECT( - 'melee', JSON_ARRAY('暗星'), - 'operatorGold', JSON_ARRAY(), - 'operatorRed', JSON_ARRAY(), - 'weapon', JSON_ARRAY('Vector美杜莎') - ), - 'remark', '' - ), - JSON_ARRAY( - 'https://picsum.photos/seed/hfb-listing-003-coin/960/540', - 'https://picsum.photos/seed/hfb-listing-003-id/960/540', - 'https://picsum.photos/seed/hfb-listing-003-asset/960/540', - 'https://picsum.photos/seed/hfb-listing-003-security/960/540' - ), - 'published', NOW() - INTERVAL 60 MINUTE, NOW() - INTERVAL 60 MINUTE - ), - ( - @owner_b, '三角洲行动', 'QQ', '账号密码', - '纯币500M/9格/7体/7负/10六头', - '账密交接,高段位,保险 3*3,皮肤资源较多。', - '三角洲巅峰', 500000000, - JSON_OBJECT( - 'face_owner', '是', - 'total_asset_wan', 58600, - 'secret_kd', 4.2, - 'fire_level', 72, - 'season_insurance', '3*3', - 'stamina_level', '7级', - 'load_level', '7级', - 'common_regions', JSON_ARRAY('北京', '浙江'), - 'online_time', JSON_OBJECT('start', '09:00', 'end', '23:30'), - 'ban_record', '无封禁记录', - 'resources', JSON_ARRAY( - JSON_OBJECT('key', 'awmAmmo', 'label', 'AWM子弹', 'quantity', 120, 'mode', '收费'), - JSON_OBJECT('key', 'helmet6', 'label', '6头', 'quantity', 10, 'mode', '收费'), - JSON_OBJECT('key', 'armor6', 'label', '6甲', 'quantity', 8, 'mode', '收费'), - JSON_OBJECT('key', 'level6Ammo', 'label', '6级弹', 'quantity', 800, 'mode', '收费') - ), - 'skin_groups', JSON_OBJECT( - 'melee', JSON_ARRAY('坠星者', '处刑者'), - 'operatorGold', JSON_ARRAY('威龙-铁面判官'), - 'operatorRed', JSON_ARRAY('蚀金玫瑰'), - 'weapon', JSON_ARRAY('M7棱镜攻势S2', 'KC17-造物纪元') - ), - 'remark', '高价值账号,需按平台流程交接。' - ), - JSON_ARRAY( - 'https://picsum.photos/seed/hfb-listing-004-coin/960/540', - 'https://picsum.photos/seed/hfb-listing-004-id/960/540', - 'https://picsum.photos/seed/hfb-listing-004-asset/960/540', - 'https://picsum.photos/seed/hfb-listing-004-security/960/540', - 'https://picsum.photos/seed/hfb-listing-004-skin/960/540' - ), - 'published', NOW() - INTERVAL 50 MINUTE, NOW() - INTERVAL 50 MINUTE - ), - ( - @owner_c, '三角洲行动', '微信', '扫码', - '纯币180M/6格/6体/5负/80发AWM', - '扫码交接,资源适中,适合短期使用。', - '黑鹰', 180000000, - JSON_OBJECT( - 'face_owner', '否', - 'total_asset_wan', 21600, - 'secret_kd', 2.1, - 'fire_level', 53, - 'season_insurance', '2*3', - 'stamina_level', '6级', - 'load_level', '5级', - 'common_regions', JSON_ARRAY('上海', '安徽'), - 'online_time', JSON_OBJECT('start', '11:00', 'end', '23:00'), - 'ban_record', '无封禁记录', - 'resources', JSON_ARRAY( - JSON_OBJECT('key', 'awmAmmo', 'label', 'AWM子弹', 'quantity', 80, 'mode', '赠送'), - JSON_OBJECT('key', 'redPacket45', 'label', '45格大红包', 'quantity', 6, 'mode', '收费') - ), - 'skin_groups', JSON_OBJECT( - 'melee', JSON_ARRAY('影锋'), - 'operatorGold', JSON_ARRAY(), - 'operatorRed', JSON_ARRAY('水墨云图'), - 'weapon', JSON_ARRAY('QBZ95王牌之剑', 'AUG气象感应') - ), - 'remark', 'AWM 子弹赠送,其他消耗品按规则扣费。' - ), - JSON_ARRAY( - 'https://picsum.photos/seed/hfb-listing-005-coin/960/540', - 'https://picsum.photos/seed/hfb-listing-005-id/960/540', - 'https://picsum.photos/seed/hfb-listing-005-asset/960/540', - 'https://picsum.photos/seed/hfb-listing-005-security/960/540' - ), - 'published', NOW() - INTERVAL 40 MINUTE, NOW() - INTERVAL 40 MINUTE - ), - ( - @owner_c, '三角洲行动', 'QQ', '扫码', - '纯币90M/4格/5体/4负/3五级套', - '短租低价,保险 2*2,适合试号。', - '铂金', 90000000, - JSON_OBJECT( - 'face_owner', '是', - 'total_asset_wan', 10200, - 'secret_kd', 1.7, - 'fire_level', 44, - 'season_insurance', '2*2', - 'stamina_level', '5级', - 'load_level', '4级', - 'common_regions', JSON_ARRAY('河南', '山东'), - 'online_time', JSON_OBJECT('start', '14:00', 'end', '01:00'), - 'ban_record', '无封禁记录', - 'resources', JSON_ARRAY( - JSON_OBJECT('key', 'coffeeBean', 'label', '高级咖啡豆', 'quantity', 3, 'mode', '收费'), - JSON_OBJECT('key', 'helmet6', 'label', '6头', 'quantity', 1, 'mode', '收费') - ), - 'skin_groups', JSON_OBJECT( - 'melee', JSON_ARRAY('北极星'), - 'operatorGold', JSON_ARRAY('蜂医-送葬人'), - 'operatorRed', JSON_ARRAY(), - 'weapon', JSON_ARRAY('MP7电玩高手S2') - ), - 'remark', '' - ), - JSON_ARRAY( - 'https://picsum.photos/seed/hfb-listing-006-coin/960/540', - 'https://picsum.photos/seed/hfb-listing-006-id/960/540', - 'https://picsum.photos/seed/hfb-listing-006-asset/960/540', - 'https://picsum.photos/seed/hfb-listing-006-security/960/540' - ), - 'published', NOW() - INTERVAL 30 MINUTE, NOW() - INTERVAL 30 MINUTE - ), - ( - @owner_a, '三角洲行动', 'Steam', '账号密码', - '纯币260M/6格/6体/6负/皮肤多', - 'Steam 账密,常用地北京/天津,皮肤组合较完整。', - '黑鹰', 260000000, - JSON_OBJECT( - 'face_owner', '否', - 'total_asset_wan', 30100, - 'secret_kd', 3.2, - 'fire_level', 61, - 'season_insurance', '2*3', - 'stamina_level', '6级', - 'load_level', '6级', - 'common_regions', JSON_ARRAY('北京', '天津'), - 'online_time', JSON_OBJECT('start', '09:30', 'end', '22:30'), - 'ban_record', '无封禁记录', - 'resources', JSON_ARRAY( - JSON_OBJECT('key', 'awmAmmo', 'label', 'AWM子弹', 'quantity', 48, 'mode', '收费'), - JSON_OBJECT('key', 'armor6', 'label', '6甲', 'quantity', 3, 'mode', '收费'), - JSON_OBJECT('key', 'helmet6', 'label', '6头', 'quantity', 4, 'mode', '收费') - ), - 'skin_groups', JSON_OBJECT( - 'melee', JSON_ARRAY('赤枭', '黑海'), - 'operatorGold', JSON_ARRAY('威龙-壮志凌云', '蜂医-危险物质'), - 'operatorRed', JSON_ARRAY(), - 'weapon', JSON_ARRAY('AS Val悬赏令', 'K416命运', 'AUG气象感应') - ), - 'remark', '' - ), - JSON_ARRAY( - 'https://picsum.photos/seed/hfb-listing-007-coin/960/540', - 'https://picsum.photos/seed/hfb-listing-007-id/960/540', - 'https://picsum.photos/seed/hfb-listing-007-asset/960/540', - 'https://picsum.photos/seed/hfb-listing-007-security/960/540', - 'https://picsum.photos/seed/hfb-listing-007-skin/960/540' - ), - 'published', NOW() - INTERVAL 20 MINUTE, NOW() - INTERVAL 20 MINUTE - ), - ( - @owner_b, '三角洲行动', '微信', '账号密码', - '纯币360M/9格/7体/6负/赠送六级弹', - '账密登录,保险 3*3,赠送部分六级弹。', - '三角洲巅峰', 360000000, - JSON_OBJECT( - 'face_owner', '是', - 'total_asset_wan', 42100, - 'secret_kd', 3.8, - 'fire_level', 68, - 'season_insurance', '3*3', - 'stamina_level', '7级', - 'load_level', '6级', - 'common_regions', JSON_ARRAY('福建', '广东'), - 'online_time', JSON_OBJECT('start', '10:00', 'end', '24:00'), - 'ban_record', '无封禁记录', - 'resources', JSON_ARRAY( - JSON_OBJECT('key', 'level6Ammo', 'label', '6级弹', 'quantity', 520, 'mode', '赠送'), - JSON_OBJECT('key', 'awmAmmo', 'label', 'AWM子弹', 'quantity', 100, 'mode', '收费'), - JSON_OBJECT('key', 'gridCard9', 'label', '9格体验卡', 'quantity', 2, 'mode', '收费') - ), - 'skin_groups', JSON_OBJECT( - 'melee', JSON_ARRAY('电锯惊魂', '处刑者'), - 'operatorGold', JSON_ARRAY('露娜-古墓丽影联动', '红狼-电锯惊魂'), - 'operatorRed', JSON_ARRAY(), - 'weapon', JSON_ARRAY('M250电玩高手S2', '腾龙气象感应') - ), - 'remark', '六级弹赠送数量以下单截图为准。' - ), - JSON_ARRAY( - 'https://picsum.photos/seed/hfb-listing-008-coin/960/540', - 'https://picsum.photos/seed/hfb-listing-008-id/960/540', - 'https://picsum.photos/seed/hfb-listing-008-asset/960/540', - 'https://picsum.photos/seed/hfb-listing-008-security/960/540', - 'https://picsum.photos/seed/hfb-listing-008-skin/960/540' - ), - 'published', NOW() - INTERVAL 10 MINUTE, NOW() - INTERVAL 10 MINUTE - ); - -INSERT INTO rental_listings - (account_id, owner_id, price_hourly, price_daily, price_weekly, deposit_amount, status, review_status, published_at, created_at, updated_at) -SELECT id, owner_id, 29.76, 714.29, 5000.00, 120.00, 'published', 'approved', NOW() - INTERVAL 80 MINUTE, created_at, updated_at -FROM game_accounts WHERE title = '纯币300M/6格/6体/6负/4六甲/6六头' -UNION ALL -SELECT id, owner_id, 33.33, 800.00, 2400.00, 60.00, 'published', 'approved', NOW() - INTERVAL 70 MINUTE, created_at, updated_at -FROM game_accounts WHERE title = '纯币120M/4格/5体/5负/2六头' -UNION ALL -SELECT id, owner_id, 62.50, 1500.00, 1500.00, 80.00, 'published', 'approved', NOW() - INTERVAL 60 MINUTE, created_at, updated_at -FROM game_accounts WHERE title = '纯币60M/2格/4体/4负/1体验卡' -UNION ALL -SELECT id, owner_id, 42.52, 1020.41, 7142.86, 180.00, 'published', 'approved', NOW() - INTERVAL 50 MINUTE, created_at, updated_at -FROM game_accounts WHERE title = '纯币500M/9格/7体/7负/10六头' -UNION ALL -SELECT id, owner_id, 50.00, 1200.00, 3600.00, 100.00, 'published', 'approved', NOW() - INTERVAL 40 MINUTE, created_at, updated_at -FROM game_accounts WHERE title = '纯币180M/6格/6体/5负/80发AWM' -UNION ALL -SELECT id, owner_id, 31.25, 750.00, 750.00, 50.00, 'published', 'approved', NOW() - INTERVAL 30 MINUTE, created_at, updated_at -FROM game_accounts WHERE title = '纯币90M/4格/5体/4负/3五级套' -UNION ALL -SELECT id, owner_id, 25.79, 619.05, 4333.33, 120.00, 'published', 'approved', NOW() - INTERVAL 20 MINUTE, created_at, updated_at -FROM game_accounts WHERE title = '纯币260M/6格/6体/6负/皮肤多' -UNION ALL -SELECT id, owner_id, 35.71, 857.14, 6000.00, 160.00, 'published', 'approved', NOW() - INTERVAL 10 MINUTE, created_at, updated_at -FROM game_accounts WHERE title = '纯币360M/9格/7体/6负/赠送六级弹'; - -INSERT INTO notifications (user_id, type, title, content, biz_type, biz_id, created_at) -SELECT rental_listings.owner_id, 'listing_review', '发布审核通过', CONCAT(game_accounts.title, ' 已上架。'), 'listing', rental_listings.id, rental_listings.updated_at -FROM rental_listings -JOIN game_accounts ON game_accounts.id = rental_listings.account_id; diff --git a/frontend/src/api/listings.ts b/frontend/src/api/listings.ts index 5e84c7a..19705c0 100644 --- a/frontend/src/api/listings.ts +++ b/frontend/src/api/listings.ts @@ -20,6 +20,7 @@ export interface Listing { price_daily: number price_weekly: number deposit_amount: number + is_accelerated_sale?: boolean status: string review_status: string review_reason: string diff --git a/frontend/src/views/mobile/MobileHomeView.css b/frontend/src/views/mobile/MobileHomeView.css index dcbf836..0251000 100644 --- a/frontend/src/views/mobile/MobileHomeView.css +++ b/frontend/src/views/mobile/MobileHomeView.css @@ -401,10 +401,17 @@ object-position: center; } -.card-cover em { +.card-cover-labels { position: absolute; top: 7px; right: 7px; + display: flex; + align-items: flex-end; + flex-direction: column; + gap: 5px; +} + +.card-cover-labels em { border-radius: 999px; background: #ffd21e; color: #1f2937; diff --git a/frontend/src/views/mobile/MobileHomeView.vue b/frontend/src/views/mobile/MobileHomeView.vue index 964da3c..6a686a5 100644 --- a/frontend/src/views/mobile/MobileHomeView.vue +++ b/frontend/src/views/mobile/MobileHomeView.vue @@ -30,6 +30,7 @@ import { getResourceQuantity, getServerRegion, getSkinGroup, + hasAcceleratedSaleRatio, hasGiftResources, readAssetNumber, readAssetString, @@ -450,7 +451,13 @@ function uniqueOptions(values: string[]) { :alt="getListingTitle(item)" /> - 有赠送 +
+ 有赠送 + 特惠 +
diff --git a/frontend/src/views/mobile/listingDisplay.ts b/frontend/src/views/mobile/listingDisplay.ts index b4a4379..9ba93c2 100644 --- a/frontend/src/views/mobile/listingDisplay.ts +++ b/frontend/src/views/mobile/listingDisplay.ts @@ -122,6 +122,21 @@ export function hasGiftResources(item: Listing) { return getListingResources(item).some((resource) => resource.mode === "赠送"); } +export function hasAcceleratedSaleRatio(item: Listing) { + if (item.is_accelerated_sale) return true; + + const priceBreakdown = item.asset_summary?.price_breakdown; + if (typeof priceBreakdown !== "object" || priceBreakdown === null) return false; + + const row = priceBreakdown as Record; + const referenceRatio = readUnknownNumber(row.seller_reference_ratio); + const sellerRatio = readUnknownNumber(row.seller_ratio); + const acceleratedRatio = readUnknownNumber(row.accelerated_sale_ratio); + + if (referenceRatio <= 0) return false; + return sellerRatio > referenceRatio || acceleratedRatio > referenceRatio; +} + export function getSkinGroup(item: Listing, groupKey: string) { const skinGroups = item.asset_summary?.skin_groups; if ( diff --git a/frontend/src/views/public/HomeView.vue b/frontend/src/views/public/HomeView.vue index df09f1b..60c25ad 100644 --- a/frontend/src/views/public/HomeView.vue +++ b/frontend/src/views/public/HomeView.vue @@ -30,6 +30,7 @@ import { getListingTitle, getLoginMethod, getServerRegion, + hasAcceleratedSaleRatio, hasGiftResources, } from "@/views/mobile/listingDisplay"; @@ -322,7 +323,13 @@ function uniqueOptions(values: string[]) { :alt="getListingTitle(item)" /> HFB - 有赠送 +
+ 有赠送 + 特惠 +
@@ -720,10 +727,17 @@ function uniqueOptions(values: string[]) { object-fit: cover; } -.desktop-cover em { +.desktop-cover-labels { position: absolute; top: 8px; right: 8px; + display: flex; + align-items: flex-end; + flex-direction: column; + gap: 5px; +} + +.desktop-cover-labels em { border-radius: 999px; background: #ff6a00; color: #ffffff; diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 48720aa..bfd16fb 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -23,7 +23,10 @@ export default defineConfig({ "/api": "http://127.0.0.1:8080", }, allowedHosts: [ - '221329.cc.cd' + '221329.cc.cd', + "d4aa-77-93-89-84.ngrok-free.app", + "frp-bus.com", + "www.u499731.nyat.app" ], }, }); diff --git a/scripts/dev.sh b/scripts/dev.sh index e54636d..28a76cc 100755 --- a/scripts/dev.sh +++ b/scripts/dev.sh @@ -70,8 +70,6 @@ init_database() { log "数据库结构已存在,跳过迁移" fi - log "刷新开发测试数据..." - docker exec -i hfb-mysql mysql -uhfb -psecret hfb_sys < "${ROOT_DIR}/backend/seeds/dev_orders.sql" } load_env_file() {