bindbox-mini/utils/activity.js

171 lines
4.9 KiB
JavaScript

/**
* 活动相关工具函数
* 从 yifanshang/duiduipeng/wuxianshang 页面中提取的公共逻辑
*/
/**
* 解包API返回的数据
* @param {any} list - API返回的数据
* @returns {Array} 数组
*/
export function unwrap(list) {
if (Array.isArray(list)) return list
const obj = list || {}
const data = obj.data || {}
const arr = obj.list || obj.items || data.list || data.items || data
return Array.isArray(arr) ? arr : []
}
/**
* 判断真值(支持多种格式)
* @param {any} v - 待判断的值
* @returns {boolean}
*/
export function truthy(v) {
if (typeof v === 'boolean') return v
const s = String(v || '').trim().toLowerCase()
if (!s) return false
return s === '1' || s === 'true' || s === 'yes' || s === 'y' || s === '是' || s === 'boss是真的' || s === 'boss' || s === '大boss'
}
/**
* 检测是否为BOSS奖
* @param {Object} item - 奖品对象
* @returns {boolean}
*/
export function detectBoss(item) {
const i = item || {}
return truthy(i.is_boss) || truthy(i.boss) || truthy(i.isBoss) || truthy(i.boss_true) || truthy(i.boss_is_true) || truthy(i.bossText) || truthy(i.tag)
}
/**
* 奖品等级映射 (与管理端保持一致)
*/
export const PRIZE_LEVEL_LABELS = {
1: 'S',
2: 'A',
3: 'B',
4: 'C',
5: 'D',
6: 'E',
7: 'F',
8: 'G',
9: 'H',
11: 'Last'
}
/**
* 等级数字转字母/标签
* @param {number|string} level - 等级
* @returns {string}
*/
export function levelToAlpha(level) {
if (level === 'BOSS') return 'BOSS'
const n = Number(level)
if (PRIZE_LEVEL_LABELS[n]) return PRIZE_LEVEL_LABELS[n]
if (isNaN(n) || n <= 0) return String(level || '赏')
// 兜底逻辑:如果超出定义的映射,使用 A, B, C...
return String.fromCharCode(64 + n)
}
/**
* 状态转文本
* @param {number} status - 状态码
* @returns {string}
*/
export function statusToText(status) {
if (status === 1) return '进行中'
if (status === 0) return '未开始'
if (status === 2) return '已结束'
return String(status || '')
}
/**
* 标准化期列表数据
* @param {any} list - API返回的期列表
* @returns {Array}
*/
export function normalizeIssues(list) {
const arr = unwrap(list)
return arr.map((i, idx) => ({
id: i.id ?? String(idx),
title: i.title ?? i.name ?? '',
no: i.no ?? i.index ?? i.issue_no ?? i.issue_number ?? null,
status_text: i.status_text ?? (i.status === 1 ? '进行中' : i.status === 0 ? '未开始' : i.status === 2 ? '已结束' : '')
}))
}
/**
* 标准化奖励列表数据
* @param {any} list - API返回的奖励列表
* @param {Function} cleanUrl - URL清理函数
* @returns {Array}
*/
export function normalizeRewards(list, cleanUrl = (u) => u) {
const arr = unwrap(list)
const items = arr.map((i, idx) => ({
id: i.product_id ?? i.id ?? String(idx),
title: i.name ?? i.title ?? '',
image: cleanUrl(i.product_image ?? i.image ?? i.img ?? i.pic ?? i.banner ?? ''),
weight: Number(i.weight) || 0,
boss: detectBoss(i),
level: levelToAlpha(i.prize_level ?? i.level ?? (detectBoss(i) ? 'BOSS' : '赏'))
}))
const total = items.reduce((acc, it) => acc + (it.weight > 0 ? it.weight : 0), 0)
const enriched = items.map(it => {
const rawPercent = total > 0 ? (it.weight / total) * 100 : 0
return {
...it,
percent: parseFloat(rawPercent.toFixed(2)) // 统一保留2位小数
}
})
enriched.sort((a, b) => (b.percent - a.percent))
return enriched
}
/**
* 查找最新的期ID
* @param {Array} list - 期列表
* @returns {string}
*/
export function pickLatestIssueId(list) {
const arr = Array.isArray(list) ? list : []
let latest = arr[arr.length - 1] && arr[arr.length - 1].id
let maxNo = -Infinity
arr.forEach(i => {
const n = Number(i.no)
if (!Number.isNaN(n) && Number.isFinite(n) && n > maxNo) {
maxNo = n
latest = i.id
}
})
return latest || (arr[0] && arr[0].id) || ''
}
/**
* 按等级分组奖励
* @param {Array} rewards - 奖励列表
* @returns {Array} 分组后的奖励
*/
export function groupRewardsByLevel(rewards) {
const groups = {}
; (rewards || []).forEach(item => {
const level = item.level || '赏'
if (!groups[level]) groups[level] = []
groups[level].push(item)
})
return Object.keys(groups).sort((a, b) => {
if (a === 'BOSS') return -1
if (b === 'BOSS') return 1
return a.localeCompare(b)
}).map(key => {
const levelRewards = groups[key]
const total = levelRewards.reduce((sum, item) => sum + (Number(item.percent) || 0), 0)
return {
level: key,
rewards: levelRewards,
totalPercent: total.toFixed(2) // 统一保留2位小数
}
})
}