Files
Genarrative/src/components/rpg-entry/rpgEntryProfileFundsViewModel.ts
T
kdletters 66178edc7c 统一泥点账户与每日免费额度
- 新增每日免费泥点独立余额桶,按北京时间零点固定重置20泥点
- 按每日免费、会员限时、永久顺序扣费并支持跨日退款按原分桶回补
- 统一主站与图片画板泥点入口,隐藏限时泥点、任务入口和会员购买入口
- 收敛四档充值商品与泥点购买弹窗,并接入后台动态商品配置
- 同步余额read model、SpacetimeDB迁移绑定、回归测试和工程文档
- 将泥点明细浮层圆角匹配首页卡片视觉规范
2026-07-12 19:35:22 +08:00

182 lines
5.2 KiB
TypeScript

import type {
ProfileMembership,
ProfileRechargeProduct,
ProfileWalletLedgerEntry,
ProfileWalletLedgerResponse,
} from '../../../packages/shared/src/contracts/runtime';
const PROFILE_WALLET_LEDGER_SOURCE_LABELS = {
new_user_registration_reward: '注册赠送',
points_recharge: '泥点充值',
invite_inviter_reward: '邀请奖励',
invite_invitee_reward: '填写邀请码奖励',
snapshot_sync: '账户同步',
membership_period_grant: '会员周期发放',
membership_period_reset: '会员周期重置',
daily_free_grant: '每日免费发放',
daily_free_reset: '每日免费重置',
asset_operation_consume: '资产操作消耗',
asset_operation_refund: '资产操作退回',
redeem_code_reward: '兑换码奖励',
puzzle_author_incentive_claim: '拼图作者奖励',
daily_task_reward: '每日任务奖励',
} satisfies Record<ProfileWalletLedgerEntry['sourceType'], string>;
export type ProfileWalletLedgerEntryPresentation = {
amountLabel: string;
balanceLabel: string;
createdAt: string;
id: string;
isIncome: boolean;
sourceLabel: string;
};
export type ProfileWalletLedgerPresentation = {
balance: number;
balanceLabel: string;
entries: ProfileWalletLedgerEntryPresentation[];
};
export function getWalletLedgerSourceLabel(
sourceType: string | null | undefined,
) {
const normalizedSourceType = sourceType?.trim() ?? '';
if (!normalizedSourceType) {
return '未知来源';
}
return (
PROFILE_WALLET_LEDGER_SOURCE_LABELS[
normalizedSourceType as ProfileWalletLedgerEntry['sourceType']
] ?? normalizedSourceType
);
}
export function formatWalletLedgerAmount(amountDelta: number) {
return amountDelta > 0 ? `+${amountDelta}` : `${amountDelta}`;
}
export function buildWalletLedgerEntryPresentation(
entry: ProfileWalletLedgerEntry,
): ProfileWalletLedgerEntryPresentation {
return {
amountLabel: formatWalletLedgerAmount(entry.amountDelta),
balanceLabel: `余额 ${entry.balanceAfter}`,
createdAt: entry.createdAt,
id: entry.id,
isIncome: entry.amountDelta > 0,
sourceLabel: getWalletLedgerSourceLabel(entry.sourceType),
};
}
export function buildWalletLedgerPresentation(
ledger: ProfileWalletLedgerResponse | null,
fallbackBalance: number,
): ProfileWalletLedgerPresentation {
const entries = ledger?.entries ?? [];
const balance = entries[0]?.balanceAfter ?? fallbackBalance;
return {
balance,
balanceLabel: `${balance}泥点`,
entries: entries.map(buildWalletLedgerEntryPresentation),
};
}
export function formatRechargePrice(priceCents: number) {
const yuan = priceCents / 100;
return ${Number.isInteger(yuan) ? yuan.toFixed(0) : yuan.toFixed(2)}`;
}
function formatMembershipPeriodPrefix(periodDays: number) {
if (periodDays === 30) {
return '每月';
}
if (periodDays === 7) {
return '每周';
}
if (periodDays > 0) {
return `每${periodDays}天`;
}
return '每周期';
}
export function buildRechargeProductValueLabel(product: ProfileRechargeProduct) {
if (product.kind === 'membership') {
const membershipPeriodPoints = product.membershipPeriodPoints;
const membershipPeriodDays =
product.membershipPeriodDays > 0
? product.membershipPeriodDays
: product.durationDays;
return membershipPeriodPoints > 0
? `${formatMembershipPeriodPrefix(membershipPeriodDays)}${membershipPeriodPoints}泥点`
: `${product.durationDays}天`;
}
return `${product.pointsAmount}${
product.bonusPoints > 0 ? `+${product.bonusPoints}` : ''
}泥点`;
}
export function getMembershipTierLabel(tier: ProfileMembership['tier']) {
switch (tier) {
case 'starter':
return 'Starter';
case 'basic':
return 'Basic';
case 'pro':
return 'Pro';
case 'ultimate':
return 'Ultimate';
case 'month':
return '月卡';
case 'season':
return '季卡';
case 'year':
return '年卡';
default:
return '普通用户';
}
}
export function buildMembershipLabel(
membership: ProfileMembership | null | undefined,
formatTime: (value: string) => string,
) {
if (membership?.status !== 'active') {
return '普通用户';
}
const tierLabel =
membership.tier === 'normal' ? '会员' : getMembershipTierLabel(membership.tier);
const resetLabel = membership.cycleResetsAt
? ` · ${formatTime(membership.cycleResetsAt)}重置`
: '';
if (membership.tier === 'normal') {
return membership.expiresAt
? `会员至 ${formatTime(membership.expiresAt)}${resetLabel}`
: `会员已生效${resetLabel}`;
}
return membership.expiresAt
? `${tierLabel}${formatTime(membership.expiresAt)}${resetLabel}`
: `${tierLabel} 已生效${resetLabel}`;
}
export function buildMembershipCycleLabel(
membership: ProfileMembership | null | undefined,
formatTime: (value: string) => string,
) {
if (membership?.status !== 'active') {
return '普通用户';
}
if (membership.cycleRemainingPoints > 0) {
const resetLabel = membership.cycleResetsAt
? ` · ${formatTime(membership.cycleResetsAt)}重置`
: '';
return `本周期剩余 ${membership.cycleRemainingPoints} 泥点${resetLabel}`;
}
return buildMembershipLabel(membership, formatTime);
}