import { consumeUserPoints, getPointsSystemConfig, getUserById, } from "@/lib/db/queries"; import type { PointsSystemConfig } from "@/lib/points/config"; type FeaturePointsChargeInput = { userId: string; unitCost: number; reason: string; modelId?: string | null; messageId?: string | null; note: string; pointsConfig?: PointsSystemConfig; }; type FeaturePointsChargeOk = { ok: true; chargeable: boolean; unitCost: number; consume: (overrides?: { modelId?: string | null; messageId?: string | null; note?: string; reason?: string; }) => Promise<{ ok: boolean; balance: number | null; skipped: boolean; } | null>; }; type FeaturePointsChargeBlocked = { ok: false; status: number; payload: { success: false; error: string; code: string; message: string; balance: number; expectedCost: number; unitCost: number; }; }; function normalizeRuntimeCost(value: unknown) { const parsedValue = Number(value); return Number.isFinite(parsedValue) && parsedValue > 0 ? Math.max(0, Math.round(parsedValue)) : 0; } export async function prepareFeaturePointsCharge( input: FeaturePointsChargeInput ): Promise { const unitCost = normalizeRuntimeCost(input.unitCost); const pointsConfig = input.pointsConfig || (await getPointsSystemConfig()); const chargeable = pointsConfig.enabled === true && unitCost > 0; if (chargeable && pointsConfig.blockOnInsufficient !== false) { const userData = await getUserById({ id: input.userId }); if (!userData?.isHighVip) { const currentBalance = Math.max(0, Number(userData?.pointsBalance || 0)); if (currentBalance < unitCost) { return { ok: false, status: 402, payload: { success: false, error: "积分不足", code: "insufficient_points", message: `积分不足,当前余额 ${currentBalance},本次预计消耗 ${unitCost}。`, balance: currentBalance, expectedCost: unitCost, unitCost, }, }; } } } return { ok: true, chargeable, unitCost, consume: async (overrides = {}) => { if (!chargeable) return null; return consumeUserPoints({ userId: input.userId, amount: unitCost, reason: overrides.reason || input.reason, modelId: overrides.modelId ?? input.modelId ?? null, messageId: overrides.messageId ?? input.messageId ?? null, note: overrides.note || input.note, }); }, }; }