export const moneyOperators = [ { id: "mina-chen", label: "Mina Chen", department: "平台运营组" }, { id: "sarah-ali", label: "Sarah Ali", department: "中东运营组" }, { id: "leo-wang", label: "Leo Wang", department: "拉美拓展组" }, { id: "diego-costa", label: "Diego Costa", department: "直播增长组" }, ]; export const moneyCountries = [ { code: "AE", currency: "AED", label: "阿联酋", region: "MENA" }, { code: "SA", currency: "SAR", label: "沙特", region: "MENA" }, { code: "BR", currency: "BRL", label: "巴西", region: "LATAM" }, { code: "MX", currency: "MXN", label: "墨西哥", region: "LATAM" }, { code: "ID", currency: "IDR", label: "印尼", region: "SEA" }, { code: "TR", currency: "TRY", label: "土耳其", region: "EMEA" }, ]; export const moneyAppOptions = [ ["", "全部 App"], ["hy-live", "HY Live"], ["voice-party", "Voice Party"], ["social-pro", "Social Pro"], ["spark-chat", "Spark Chat"], ]; export const moneyScopeOptions = [ ["", "全部模块"], ["app", "App 模块"], ["region", "App 地区模块"], ]; export const moneyProviderOptions = [ { code: "mifapay", label: "MiFaPay", countries: ["AE", "SA", "TR"] }, { code: "v5pay", label: "V5Pay", countries: ["BR", "MX", "ID"] }, { code: "stripe", label: "Stripe", countries: ["AE", "SA", "BR", "MX", "ID", "TR"] }, ]; export const trendLabels = ["06/21", "06/22", "06/23", "06/24", "06/25", "06/26", "06/27"]; export const moneyModules = [ { id: "hy-live-app", appCode: "hy-live", appName: "HY Live", moduleName: "HY Live 全站", scope: "app", countryCode: "ALL", countryName: "全部国家", region: "Global", operatorId: "mina-chen", operatorName: "Mina Chen", department: "平台运营组", rechargeUsd: 148420, revenueUsd: 316800, salaryUsd: 64100, paidUsers: 7620, newUsers: 42850, dau: 960400, dayTargetUsd: 150000, monthActualUsd: 3410000, monthTargetUsd: 3800000, retention1: 44.8, retention7: 21.4, retention30: 9.2, linkCount: 46, paidLinkCount: 31, coinIssued: 15180000, rechargeTrend: [112000, 124500, 118900, 133400, 141200, 139800, 148420], revenueTrend: [246000, 258400, 249900, 281200, 297800, 301200, 316800], }, { id: "voice-party-ksa", appCode: "voice-party", appName: "Voice Party", moduleName: "Voice Party / 沙特", scope: "region", countryCode: "SA", countryName: "沙特", region: "MENA-KSA", operatorId: "sarah-ali", operatorName: "Sarah Ali", department: "中东运营组", rechargeUsd: 76500, revenueUsd: 164200, salaryUsd: 29800, paidUsers: 3090, newUsers: 12800, dau: 286400, dayTargetUsd: 72000, monthActualUsd: 1820000, monthTargetUsd: 1750000, retention1: 47.5, retention7: 24.1, retention30: 10.8, linkCount: 29, paidLinkCount: 22, coinIssued: 7820000, rechargeTrend: [54800, 61200, 59400, 68000, 70400, 73900, 76500], revenueTrend: [126000, 132100, 129600, 145800, 151200, 158000, 164200], }, { id: "voice-party-uae", appCode: "voice-party", appName: "Voice Party", moduleName: "Voice Party / 阿联酋", scope: "region", countryCode: "AE", countryName: "阿联酋", region: "MENA-UAE", operatorId: "sarah-ali", operatorName: "Sarah Ali", department: "中东运营组", rechargeUsd: 43800, revenueUsd: 88200, salaryUsd: 17200, paidUsers: 2020, newUsers: 7100, dau: 155300, dayTargetUsd: 52000, monthActualUsd: 930000, monthTargetUsd: 1200000, retention1: 41.6, retention7: 18.7, retention30: 7.9, linkCount: 18, paidLinkCount: 10, coinIssued: 3980000, rechargeTrend: [39100, 40200, 38800, 42100, 43700, 43000, 43800], revenueTrend: [76900, 80200, 78800, 83500, 86100, 87300, 88200], }, { id: "social-pro-brazil", appCode: "social-pro", appName: "Social Pro", moduleName: "Social Pro / 巴西", scope: "region", countryCode: "BR", countryName: "巴西", region: "LATAM-BR", operatorId: "leo-wang", operatorName: "Leo Wang", department: "拉美拓展组", rechargeUsd: 58200, revenueUsd: 126300, salaryUsd: 22100, paidUsers: 4200, newUsers: 21100, dau: 418700, dayTargetUsd: 50000, monthActualUsd: 1260000, monthTargetUsd: 1080000, retention1: 39.2, retention7: 16.5, retention30: 6.8, linkCount: 34, paidLinkCount: 26, coinIssued: 6200000, rechargeTrend: [42100, 43800, 47200, 50100, 52700, 54100, 58200], revenueTrend: [89200, 93400, 102200, 109800, 115600, 118500, 126300], }, { id: "social-pro-mexico", appCode: "social-pro", appName: "Social Pro", moduleName: "Social Pro / 墨西哥", scope: "region", countryCode: "MX", countryName: "墨西哥", region: "LATAM-MX", operatorId: "leo-wang", operatorName: "Leo Wang", department: "拉美拓展组", rechargeUsd: 31600, revenueUsd: 70600, salaryUsd: 14300, paidUsers: 2380, newUsers: 9400, dau: 180200, dayTargetUsd: 36000, monthActualUsd: 720000, monthTargetUsd: 820000, retention1: 36.4, retention7: 14.8, retention30: 5.9, linkCount: 15, paidLinkCount: 9, coinIssued: 3320000, rechargeTrend: [27600, 28100, 29400, 30200, 31800, 30500, 31600], revenueTrend: [60400, 62100, 64100, 66600, 68900, 68200, 70600], }, { id: "spark-chat-indonesia", appCode: "spark-chat", appName: "Spark Chat", moduleName: "Spark Chat / 印尼", scope: "region", countryCode: "ID", countryName: "印尼", region: "SEA-ID", operatorId: "diego-costa", operatorName: "Diego Costa", department: "直播增长组", rechargeUsd: 39800, revenueUsd: 90200, salaryUsd: 18600, paidUsers: 3510, newUsers: 18400, dau: 322600, dayTargetUsd: 42000, monthActualUsd: 860000, monthTargetUsd: 940000, retention1: 43.1, retention7: 19.3, retention30: 8.1, linkCount: 21, paidLinkCount: 14, coinIssued: 4460000, rechargeTrend: [33800, 35600, 36100, 38200, 39100, 38800, 39800], revenueTrend: [76400, 80100, 81200, 84900, 87600, 88400, 90200], }, ]; export const initialPaymentLinks = [ createPaymentLinkRecord({ amountUsd: 299, countryCode: "SA", moduleId: "voice-party-ksa", now: new Date("2026-06-27T08:32:00+08:00"), operatorName: "Sarah Ali", providerCode: "mifapay", status: "paid", }), createPaymentLinkRecord({ amountUsd: 129, countryCode: "BR", moduleId: "social-pro-brazil", now: new Date("2026-06-27T10:18:00+08:00"), operatorName: "Leo Wang", providerCode: "v5pay", status: "redirected", }), createPaymentLinkRecord({ amountUsd: 499, countryCode: "AE", moduleId: "voice-party-uae", now: new Date("2026-06-27T11:04:00+08:00"), operatorName: "Sarah Ali", providerCode: "mifapay", status: "redirected", }), ]; export function filterMoneyModules({ activeOperatorId = "", appCode = "", countryCode = "", mode = "finance", modules = moneyModules, operatorKeyword = "", scope = "", } = {}) { const normalizedKeyword = operatorKeyword.trim().toLowerCase(); return modules.filter((module) => { const matchesRole = mode !== "operator" || module.operatorId === activeOperatorId; const matchesApp = !appCode || module.appCode === appCode; const matchesScope = !scope || module.scope === scope; const matchesCountry = !countryCode || module.countryCode === countryCode || (module.scope === "app" && module.countryCode === "ALL"); const matchesOperator = !normalizedKeyword || module.operatorName.toLowerCase().includes(normalizedKeyword) || module.department.toLowerCase().includes(normalizedKeyword); return matchesRole && matchesApp && matchesScope && matchesCountry && matchesOperator; }); } export function summarizeMoneyModules(modules = []) { const rechargeUsd = sumBy(modules, "rechargeUsd"); const revenueUsd = sumBy(modules, "revenueUsd"); const salaryUsd = sumBy(modules, "salaryUsd"); const paidUsers = sumBy(modules, "paidUsers"); const newUsers = sumBy(modules, "newUsers"); const dau = sumBy(modules, "dau"); const dayTargetUsd = sumBy(modules, "dayTargetUsd"); const monthActualUsd = sumBy(modules, "monthActualUsd"); const monthTargetUsd = sumBy(modules, "monthTargetUsd"); return { arppuUsd: paidUsers > 0 ? rechargeUsd / paidUsers : 0, coinIssued: sumBy(modules, "coinIssued"), dau, dayProgress: percentage(rechargeUsd, dayTargetUsd), dayTargetUsd, grossProfitUsd: revenueUsd - salaryUsd, linkConversion: percentage(sumBy(modules, "paidLinkCount"), sumBy(modules, "linkCount")), monthActualUsd, monthProgress: percentage(monthActualUsd, monthTargetUsd), monthTargetUsd, newUsers, paidLinkCount: sumBy(modules, "paidLinkCount"), paidUsers, rechargeTrend: sumTrend(modules, "rechargeTrend"), rechargeUsd, retention1: averageBy(modules, "retention1"), retention7: averageBy(modules, "retention7"), retention30: averageBy(modules, "retention30"), revenueTrend: sumTrend(modules, "revenueTrend"), revenueUsd, salaryUsd, totalLinkCount: sumBy(modules, "linkCount"), }; } export function createPaymentLinkRecord({ amountUsd, countryCode, moduleId, now = new Date(), operatorName, providerCode, status = "redirected", }) { const module = moneyModules.find((item) => item.id === moduleId) || moneyModules[0]; const country = moneyCountries.find((item) => item.code === countryCode) || moneyCountries[0]; const provider = moneyProviderOptions.find((item) => item.code === providerCode) || moneyProviderOptions[0]; const createdAtMs = now.getTime(); const suffix = compactTimestamp(now); const orderId = `MNY-${country.code}-${suffix}`; return { amountUsd: Number(amountUsd) || 0, appName: module.appName, countryCode: country.code, countryName: country.label, createdAtMs, currency: country.currency, id: orderId, moduleId: module.id, moduleName: module.moduleName, operatorName: operatorName || module.operatorName, payUrl: `https://pay.hyapp.example/${country.code.toLowerCase()}/${orderId.toLowerCase()}`, providerCode: provider.code, providerName: provider.label, region: country.region, status, }; } export function availableCountriesForModule(moduleId) { const module = moneyModules.find((item) => item.id === moduleId); if (!module || module.scope === "app" || module.countryCode === "ALL") { return moneyCountries; } return moneyCountries.filter((country) => country.code === module.countryCode); } export function availableProvidersForCountry(countryCode) { return moneyProviderOptions.filter((provider) => provider.countries.includes(countryCode)); } export function paymentStatusLabel(status) { if (status === "paid") { return "已支付"; } if (status === "redirected") { return "已生成"; } if (status === "failed") { return "失败"; } return status || "-"; } function sumBy(items, key) { return items.reduce((total, item) => total + Number(item[key] || 0), 0); } function averageBy(items, key) { if (!items.length) { return 0; } return sumBy(items, key) / items.length; } function sumTrend(items, key) { return trendLabels.map((_, index) => items.reduce((total, item) => total + Number(item[key]?.[index] || 0), 0)); } function percentage(value, total) { if (!total) { return 0; } return (Number(value) / Number(total)) * 100; } function compactTimestamp(date) { const pad = (value) => String(value).padStart(2, "0"); return `${date.getFullYear()}${pad(date.getMonth() + 1)}${pad(date.getDate())}${pad(date.getHours())}${pad( date.getMinutes(), )}${pad(date.getSeconds())}`; }