diff --git a/src/app/(user)/subscriptions/push/_components/subscription-push-form.tsx b/src/app/(user)/subscriptions/push/_components/subscription-push-form.tsx index 13e83f5..8535a21 100644 --- a/src/app/(user)/subscriptions/push/_components/subscription-push-form.tsx +++ b/src/app/(user)/subscriptions/push/_components/subscription-push-form.tsx @@ -39,6 +39,12 @@ function remainingTrafficLabel(subscription: PushSubscriptionOption) { return `剩余 ${formatBytes(remaining)}`; } +function pushUsageLabel(subscription: PushSubscriptionOption) { + const usage = subscription.pushUsage; + if (usage.limit <= 0) return "本周期不可 Push"; + return `本周期剩余 ${usage.remaining}/${usage.limit} 次`; +} + export function SubscriptionPushForm({ subscriptions, config, @@ -60,6 +66,8 @@ export function SubscriptionPushForm({ () => subscriptions.find((item) => item.id === subscriptionId) ?? null, [subscriptionId, subscriptions], ); + const selectedRemaining = selectedSubscription?.pushUsage.remaining ?? 0; + const transferUnavailable = Boolean(selectedSubscription) && selectedRemaining <= 0; return (
); diff --git a/src/app/(user)/subscriptions/push/push-data.ts b/src/app/(user)/subscriptions/push/push-data.ts index 5c1b443..34a8fdd 100644 --- a/src/app/(user)/subscriptions/push/push-data.ts +++ b/src/app/(user)/subscriptions/push/push-data.ts @@ -30,14 +30,67 @@ const transferInclude = { recipient: { select: { id: true, email: true, name: true } }, } satisfies Prisma.SubscriptionTransferInclude; -export type PushSubscriptionOption = Prisma.UserSubscriptionGetPayload<{ +type PushSubscriptionBaseOption = Prisma.UserSubscriptionGetPayload<{ include: typeof pushSubscriptionInclude; }>; +export type PushSubscriptionOption = PushSubscriptionBaseOption & { + pushUsage: { + limit: number; + used: number; + remaining: number; + cycleStartedAt: Date; + }; +}; + export type UserSubscriptionTransferRow = Prisma.SubscriptionTransferGetPayload<{ include: typeof transferInclude; }>; +async function getSubscriptionCycleStartedAt(subscription: PushSubscriptionBaseOption) { + const latestRenewal = await prisma.order.findFirst({ + where: { + targetSubscriptionId: subscription.id, + kind: "RENEWAL", + status: "PAID", + }, + select: { createdAt: true }, + orderBy: { createdAt: "desc" }, + }); + + return latestRenewal?.createdAt ?? subscription.startDate; +} + +async function attachPushUsage( + subscriptions: PushSubscriptionBaseOption[], + limitPerCycle: number, +): Promise