skip points charging for vip users
This commit is contained in:
parent
a77bea08b9
commit
fcfcbe1860
@ -2360,6 +2360,12 @@ async function saveGeneratedImages(
|
||||
const pointsConfig = await getPointsSystemConfig();
|
||||
if (!pointsConfig.enabled) return;
|
||||
|
||||
const userData = await getUserById({ id: userId }).catch(() => undefined);
|
||||
if (userData?.isHighVip) {
|
||||
console.log(`[Points] Skip image points for VIP user=${userId} model=${modelId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const modelConfig = await resolveBillingModelByReference(modelId);
|
||||
const unitCost = getModelCreditCost(modelConfig, "image");
|
||||
if (unitCost <= 0) return;
|
||||
@ -2379,6 +2385,10 @@ async function saveGeneratedImages(
|
||||
console.warn(
|
||||
`[Points] Failed to consume points after generation. user=${userId} model=${modelId} cost=${totalCost}`
|
||||
);
|
||||
} else if (consumeResult.skipped) {
|
||||
console.log(
|
||||
`[Points] Skipped ${totalCost} image points for VIP user=${userId} model=${modelId}`
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
`[Points] Consumed ${totalCost} points (${unitCost} x ${savedImageCount}). balance=${consumeResult.balance}`
|
||||
@ -2426,6 +2436,12 @@ async function consumeGeneratedVideoPoints(params: {
|
||||
const pointsConfig = await getPointsSystemConfig();
|
||||
if (!pointsConfig.enabled) return;
|
||||
|
||||
const userData = await getUserById({ id: params.userId }).catch(() => undefined);
|
||||
if (userData?.isHighVip) {
|
||||
console.log(`[Points] Skip video points for VIP user=${params.userId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
let billingModel: any | undefined;
|
||||
for (const ref of params.modelRefs) {
|
||||
if (!ref) continue;
|
||||
@ -2454,6 +2470,10 @@ async function consumeGeneratedVideoPoints(params: {
|
||||
console.warn(
|
||||
`[Points] Failed to consume video points. user=${params.userId} model=${billingModel.id} cost=${unitCost}`
|
||||
);
|
||||
} else if (consumeResult.skipped) {
|
||||
console.log(
|
||||
`[Points] Skipped ${unitCost} video points for VIP user=${params.userId} model=${billingModel.id}`
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
`[Points] Consumed ${unitCost} points for video. balance=${consumeResult.balance}`
|
||||
@ -4837,19 +4857,21 @@ export async function POST(request: Request) {
|
||||
);
|
||||
const expectedCost = normalizedUnitCost * requestedImageCount;
|
||||
const userData = await getUserById({ id: session.user.id });
|
||||
const currentBalance = Math.max(0, Number(userData?.pointsBalance || 0));
|
||||
if (!userData?.isHighVip) {
|
||||
const currentBalance = Math.max(0, Number(userData?.pointsBalance || 0));
|
||||
|
||||
if (currentBalance < expectedCost) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: "insufficient_points",
|
||||
message: `积分不足,当前余额 ${currentBalance},本次预计消耗 ${expectedCost}(${normalizedUnitCost} x ${requestedImageCount} 张)。`,
|
||||
balance: currentBalance,
|
||||
expectedCost,
|
||||
unitCost: normalizedUnitCost,
|
||||
},
|
||||
{ status: 402 }
|
||||
);
|
||||
if (currentBalance < expectedCost) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: "insufficient_points",
|
||||
message: `积分不足,当前余额 ${currentBalance},本次预计消耗 ${expectedCost}(${normalizedUnitCost} x ${requestedImageCount} 张)。`,
|
||||
balance: currentBalance,
|
||||
expectedCost,
|
||||
unitCost: normalizedUnitCost,
|
||||
},
|
||||
{ status: 402 }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4875,19 +4897,21 @@ export async function POST(request: Request) {
|
||||
) {
|
||||
const expectedCost = normalizedVideoUnitCost;
|
||||
const userData = await getUserById({ id: session.user.id });
|
||||
const currentBalance = Math.max(0, Number(userData?.pointsBalance || 0));
|
||||
if (!userData?.isHighVip) {
|
||||
const currentBalance = Math.max(0, Number(userData?.pointsBalance || 0));
|
||||
|
||||
if (currentBalance < expectedCost) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: "insufficient_points",
|
||||
message: `积分不足,当前余额 ${currentBalance},本次预计消耗 ${expectedCost}(${normalizedVideoUnitCost} x 1 条视频)。`,
|
||||
balance: currentBalance,
|
||||
expectedCost,
|
||||
unitCost: normalizedVideoUnitCost,
|
||||
},
|
||||
{ status: 402 }
|
||||
);
|
||||
if (currentBalance < expectedCost) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: "insufficient_points",
|
||||
message: `积分不足,当前余额 ${currentBalance},本次预计消耗 ${expectedCost}(${normalizedVideoUnitCost} x 1 条视频)。`,
|
||||
balance: currentBalance,
|
||||
expectedCost,
|
||||
unitCost: normalizedVideoUnitCost,
|
||||
},
|
||||
{ status: 402 }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -352,21 +352,23 @@ export async function POST(request: Request) {
|
||||
pointsConfig.blockOnInsufficient !== false
|
||||
) {
|
||||
const userData = await getUserById({ id: session.user.id });
|
||||
const currentBalance = Math.max(0, Number(userData?.pointsBalance || 0));
|
||||
if (!userData?.isHighVip) {
|
||||
const currentBalance = Math.max(0, Number(userData?.pointsBalance || 0));
|
||||
|
||||
if (currentBalance < unitCost) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "积分不足",
|
||||
code: "insufficient_points",
|
||||
message: `积分不足,当前余额 ${currentBalance},本次预计消耗 ${unitCost}。`,
|
||||
balance: currentBalance,
|
||||
expectedCost: unitCost,
|
||||
unitCost,
|
||||
},
|
||||
{ status: 402 }
|
||||
);
|
||||
if (currentBalance < unitCost) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "积分不足",
|
||||
code: "insufficient_points",
|
||||
message: `积分不足,当前余额 ${currentBalance},本次预计消耗 ${unitCost}。`,
|
||||
balance: currentBalance,
|
||||
expectedCost: unitCost,
|
||||
unitCost,
|
||||
},
|
||||
{ status: 402 }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -404,6 +406,10 @@ export async function POST(request: Request) {
|
||||
console.warn(
|
||||
`[Points] Failed to consume 3D points. user=${session.user.id} task=${taskId} cost=${unitCost}`
|
||||
);
|
||||
} else if (consumeResult.skipped) {
|
||||
console.log(
|
||||
`[Points] Skipped ${unitCost} 3D points for VIP user=${session.user.id} task=${taskId}`
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
`[Points] Consumed ${unitCost} points for 3D generation. user=${session.user.id} task=${taskId} balance=${consumeResult.balance}`
|
||||
|
||||
@ -72,6 +72,7 @@ export async function GET() {
|
||||
return NextResponse.json({
|
||||
config,
|
||||
pointsSystemConfig: pointsConfig,
|
||||
isHighVip: Boolean(dbUser?.isHighVip),
|
||||
currentPlanId,
|
||||
currentPlan,
|
||||
summary: {
|
||||
|
||||
@ -2472,6 +2472,7 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
|
||||
fetcher
|
||||
);
|
||||
const { data: membershipSummary } = useSWR<{
|
||||
isHighVip?: boolean;
|
||||
pointsSystemConfig?: {
|
||||
enabled?: boolean;
|
||||
blockOnInsufficient?: boolean;
|
||||
@ -6173,6 +6174,7 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
|
||||
|
||||
const ensureImagePointsBeforeSend = useCallback(() => {
|
||||
if (isVideoModel || activeComposerMode === "agent") return true;
|
||||
if (membershipSummary?.isHighVip) return true;
|
||||
|
||||
const pointsEnabled = membershipSummary?.pointsSystemConfig?.enabled === true;
|
||||
const blockOnInsufficient =
|
||||
@ -6213,6 +6215,7 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
|
||||
currentModel?.imageGenConfig?.creditCostPerImage,
|
||||
imageParams?.imageCount,
|
||||
isVideoModel,
|
||||
membershipSummary?.isHighVip,
|
||||
membershipSummary?.pointsSystemConfig?.blockOnInsufficient,
|
||||
membershipSummary?.pointsSystemConfig?.enabled,
|
||||
membershipSummary?.summary?.currentCredits,
|
||||
|
||||
@ -36,6 +36,7 @@ type DynamicModel = {
|
||||
};
|
||||
|
||||
type PointsSummaryResponse = {
|
||||
isHighVip?: boolean;
|
||||
pointsSystemConfig?: {
|
||||
enabled?: boolean;
|
||||
blockOnInsufficient?: boolean;
|
||||
@ -372,6 +373,7 @@ function PureMultimodalInput({
|
||||
|
||||
const ensureImagePointsBeforeSend = useCallback(async () => {
|
||||
if (!isImageModel || isVideoModel) return true;
|
||||
if (membershipSummary?.isHighVip) return true;
|
||||
|
||||
const pointsEnabled = membershipSummary?.pointsSystemConfig?.enabled === true;
|
||||
const blockOnInsufficient =
|
||||
@ -412,6 +414,7 @@ function PureMultimodalInput({
|
||||
imageParams?.imageCount,
|
||||
isImageModel,
|
||||
isVideoModel,
|
||||
membershipSummary?.isHighVip,
|
||||
membershipSummary?.pointsSystemConfig?.blockOnInsufficient,
|
||||
membershipSummary?.pointsSystemConfig?.enabled,
|
||||
membershipSummary?.summary?.currentCredits,
|
||||
|
||||
@ -1755,11 +1755,33 @@ export async function consumeUserPoints({
|
||||
note?: string;
|
||||
}) {
|
||||
if (!Number.isFinite(amount) || amount <= 0) {
|
||||
return { ok: true, balance: null as number | null };
|
||||
return { ok: true, balance: null as number | null, skipped: false };
|
||||
}
|
||||
|
||||
try {
|
||||
return await db.transaction(async (tx) => {
|
||||
const currentUsers = await tx
|
||||
.select({
|
||||
balance: user.pointsBalance,
|
||||
isHighVip: user.isHighVip,
|
||||
})
|
||||
.from(user)
|
||||
.where(eq(user.id, userId))
|
||||
.limit(1);
|
||||
|
||||
const currentUser = currentUsers[0];
|
||||
if (!currentUser) {
|
||||
return { ok: false, balance: null as number | null, skipped: false };
|
||||
}
|
||||
|
||||
if (currentUser.isHighVip) {
|
||||
return {
|
||||
ok: true,
|
||||
balance: Math.max(0, Number(currentUser.balance || 0)),
|
||||
skipped: true,
|
||||
};
|
||||
}
|
||||
|
||||
const updated = await tx
|
||||
.update(user)
|
||||
.set({
|
||||
@ -1772,7 +1794,7 @@ export async function consumeUserPoints({
|
||||
});
|
||||
|
||||
if (updated.length === 0) {
|
||||
return { ok: false, balance: null as number | null };
|
||||
return { ok: false, balance: null as number | null, skipped: false };
|
||||
}
|
||||
|
||||
await tx.insert(pointsLedger).values({
|
||||
@ -1785,7 +1807,7 @@ export async function consumeUserPoints({
|
||||
note: note || "图片生成消耗积分",
|
||||
});
|
||||
|
||||
return { ok: true, balance: updated[0]?.balance ?? null };
|
||||
return { ok: true, balance: updated[0]?.balance ?? null, skipped: false };
|
||||
});
|
||||
} catch (_error) {
|
||||
throw new ChatSDKError("bad_request:database", "Failed to consume user points");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user