178 lines
6.3 KiB
TypeScript
178 lines
6.3 KiB
TypeScript
import {
|
|
defaultListingPublishOptions,
|
|
type ListingPublishOptions,
|
|
} from "@/api/listingOptions";
|
|
import type { Listing } from "@/api/listings";
|
|
|
|
const fallbackRegionOptions = ["广东", "上海", "江苏", "四川", "北京", "浙江"];
|
|
|
|
const coinWanOptions = [
|
|
1800, 3200, 5800, 9600, 12000, 18000, 26000, 36000, 52000, 68000,
|
|
];
|
|
const depositOptions = [60, 80, 100, 120, 160, 200];
|
|
const secretKdOptions = [1.2, 1.6, 2.1, 2.8, 3.4, 4.2];
|
|
const onlineWindows = [
|
|
["09:00", "23:30"],
|
|
["10:30", "01:00"],
|
|
["12:00", "24:00"],
|
|
["18:00", "02:00"],
|
|
] as const;
|
|
const resourceQuantityPresets: Record<string, number[]> = {
|
|
awmAmmo: [0, 20, 48, 80, 120, 180],
|
|
helmet6: [0, 2, 4, 6, 10, 16],
|
|
armor6: [0, 1, 3, 5, 8, 12],
|
|
kit5: [0, 3, 6, 10, 15, 24],
|
|
level6Ammo: [0, 120, 240, 360, 520, 800],
|
|
gridCard9: [0, 1, 2, 4, 6, 10],
|
|
};
|
|
|
|
function pick<T>(items: readonly T[], index: number, fallback: T): T {
|
|
if (!items.length) return fallback;
|
|
return items[index % items.length] ?? fallback;
|
|
}
|
|
|
|
function pickMany(items: string[], start: number, count: number) {
|
|
if (!items.length) return [];
|
|
const fallback = items[0] || "";
|
|
return Array.from({ length: Math.min(count, items.length) }, (_, idx) =>
|
|
pick(items, start + idx, fallback)
|
|
).filter(Boolean);
|
|
}
|
|
|
|
function buildResources(options: ListingPublishOptions, seed: number) {
|
|
return options.quantity_items.map((item, index) => ({
|
|
key: item.key,
|
|
label: item.label,
|
|
quantity: pick(
|
|
resourceQuantityPresets[item.key] || [0, 1, 3, 6],
|
|
seed + index,
|
|
0
|
|
),
|
|
mode: "收费",
|
|
}));
|
|
}
|
|
|
|
function buildSkinGroups(options: ListingPublishOptions, seed: number) {
|
|
return options.skin_groups.reduce<Record<string, string[]>>((groups, group, index) => {
|
|
const count = group.key === "melee" ? 2 : index % 2 === 0 ? 3 : 2;
|
|
groups[group.key] = pickMany(group.options, seed + index * 2, count);
|
|
return groups;
|
|
}, {});
|
|
}
|
|
|
|
function buildScreenshotMap(options: ListingPublishOptions, seed: number) {
|
|
return options.screenshot_slots.reduce<Record<string, string>>((screenshots, slot, index) => {
|
|
screenshots[slot.key] = `https://picsum.photos/seed/hfb-${slot.key}-${seed + index}/960/540`;
|
|
return screenshots;
|
|
}, {});
|
|
}
|
|
|
|
function flattenSkinGroups(groups: Record<string, string[]>) {
|
|
return Object.values(groups).flat();
|
|
}
|
|
|
|
function calculateRentDays(coinWan: number) {
|
|
return coinWan >= 30000 ? 7 : coinWan >= 10000 ? 3 : 1;
|
|
}
|
|
|
|
function calculateDailyLoss(coinWan: number) {
|
|
return coinWan >= 30000 ? "30M" : coinWan >= 10000 ? "20M" : "10M";
|
|
}
|
|
|
|
function roundMoney(value: number) {
|
|
return Math.round(value * 100) / 100;
|
|
}
|
|
|
|
function formatHafCoinM(coinWan: number) {
|
|
const coinM = Math.round((coinWan / 100) * 10) / 10;
|
|
return `${Number.isInteger(coinM) ? coinM : coinM.toFixed(1)}M`;
|
|
}
|
|
|
|
export function buildFallbackListings(
|
|
options: ListingPublishOptions = defaultListingPublishOptions
|
|
): Listing[] {
|
|
return Array.from({ length: 10 }, (_, index) => {
|
|
const seed = index + 1;
|
|
const server = pick(options.server_options, index, "QQ");
|
|
const loginMethod = pick(options.login_method_options, index + 1, "账号密码");
|
|
const rank = pick(options.rank_options, index + 3, "钻石");
|
|
const insurance = pick(options.insurance_options, index + 1, "2*2");
|
|
const stamina = pick(options.level_options, index + 4, "5级");
|
|
const load = pick(options.level_options, index + 5, "5级");
|
|
const faceOwner = pick(options.face_options, index, "是");
|
|
const coinWan = pick(coinWanOptions, index, 12000);
|
|
const ratio = 5 + (index % 4);
|
|
const rentDays = calculateRentDays(coinWan);
|
|
const finalPrice = roundMoney(coinWan / ratio);
|
|
const settlementAmount = finalPrice;
|
|
const dailyPrice = roundMoney(finalPrice / rentDays);
|
|
const hourlyPrice = Math.max(roundMoney(dailyPrice / 24), 0.01);
|
|
const deposit = pick(depositOptions, index, 100);
|
|
const skinGroups = buildSkinGroups(options, seed);
|
|
const screenshots = buildScreenshotMap(options, seed);
|
|
const commonRegions = pickMany(
|
|
options.region_options.length ? options.region_options : fallbackRegionOptions,
|
|
index * 2,
|
|
2
|
|
);
|
|
const [onlineStart, onlineEnd] = pick(onlineWindows, index, onlineWindows[0]);
|
|
|
|
return {
|
|
id: 9100 + seed,
|
|
account_id: 0,
|
|
owner_id: 0,
|
|
title: `${server} ${rank} ${formatHafCoinM(coinWan)}哈夫币`,
|
|
description: `${loginMethod}交接,${insurance}保险,体力${stamina}、负重${load},示例数据按发布表单随机生成。`,
|
|
game_name: "三角洲行动",
|
|
server_region: server,
|
|
login_platform: loginMethod,
|
|
rank_level: rank,
|
|
haf_coin_amount: coinWan * 10000,
|
|
asset_summary: {
|
|
face_owner: faceOwner,
|
|
haf_coin_wan: coinWan,
|
|
total_asset_wan: Math.round(coinWan * (1.08 + (index % 4) * 0.04)),
|
|
secret_kd: pick(secretKdOptions, index, 1.6),
|
|
fire_level: 38 + seed * 3,
|
|
season_insurance: insurance,
|
|
stamina_level: stamina,
|
|
load_level: load,
|
|
daily_loss: calculateDailyLoss(coinWan),
|
|
ratio,
|
|
login_method: loginMethod,
|
|
common_regions: commonRegions,
|
|
resources: buildResources(options, seed),
|
|
skins: flattenSkinGroups(skinGroups),
|
|
skin_groups: skinGroups,
|
|
online_time: {
|
|
start: onlineStart,
|
|
end: onlineEnd,
|
|
},
|
|
ban_record: index % 4 === 0 ? "无封禁记录,近期登录稳定" : "",
|
|
screenshots,
|
|
deposit_amount: deposit,
|
|
price: finalPrice,
|
|
settlement_amount: settlementAmount,
|
|
rent_days: rentDays,
|
|
remark:
|
|
index % 3 === 0
|
|
? "部分仓库材料请勿使用,下单后会在群聊再次提醒。"
|
|
: "",
|
|
},
|
|
screenshot_urls: Object.values(screenshots),
|
|
price_hourly: hourlyPrice,
|
|
price_daily: dailyPrice,
|
|
price_weekly: Math.round(dailyPrice * 7 * 10) / 10,
|
|
deposit_amount: deposit,
|
|
min_rent_hours: coinWan >= 10000 ? 24 : 8,
|
|
max_rent_hours: coinWan >= 30000 ? 168 : 72,
|
|
status: "published",
|
|
review_status: "approved",
|
|
review_reason: "",
|
|
published_at: new Date(Date.now() - seed * 18 * 60 * 1000).toISOString(),
|
|
created_at: new Date(Date.now() - seed * 20 * 60 * 1000).toISOString(),
|
|
updated_at: new Date(Date.now() - seed * 10 * 60 * 1000).toISOString(),
|
|
};
|
|
});
|
|
}
|