Files
pagodera/lib/prisma/queries/purchase-intentions.ts
2025-11-11 03:08:36 -03:00

491 lines
11 KiB
TypeScript

/**
* Queries de Purchase Intentions usando Prisma
*/
import { Prisma } from "@prisma/client";
import { prisma } from "../app";
export type PurchaseIntentionStatus =
| "pending"
| "accepted"
| "rejected"
| "expired"
| "completed";
export type PaymentMethod = "pix" | "tibia_coins" | "gold_coins" | "trade";
export interface CreatePurchaseIntentionData {
listing_id: number;
buyer_id: number;
payment_method: PaymentMethod;
buyer_message?: string;
expires_at?: Date;
custom_offer_amount?: number | null;
custom_offer_currency?: PaymentMethod | null;
}
export interface PurchaseIntention {
intention_id: number;
listing_id: number;
buyer_id: number;
payment_method: string;
payment_method_change_count: number;
custom_offer_amount: number | null;
custom_offer_currency: string | null;
custom_offer_created_at: Date | null;
status: PurchaseIntentionStatus;
buyer_message: string | null;
seller_response: string | null;
created_at: Date;
updated_at: Date;
expires_at: Date | null;
accepted_at: Date | null;
rejected_at: Date | null;
// Relacionamentos
listing?: {
listing_id: number;
item_name: string;
seller_id: number;
};
buyer?: {
user_id: number;
username: string;
email: string;
};
}
function serializePurchaseIntention(intention: any): PurchaseIntention {
if (!intention) return intention;
return {
...intention,
custom_offer_amount:
intention.custom_offer_amount !== null &&
intention.custom_offer_amount !== undefined
? Number(intention.custom_offer_amount)
: null,
custom_offer_currency: intention.custom_offer_currency ?? null,
custom_offer_created_at: intention.custom_offer_created_at ?? null,
};
}
/**
* Cria uma nova intenção de compra
*/
export async function createPurchaseIntention(
data: CreatePurchaseIntentionData,
): Promise<PurchaseIntention> {
const result = await prisma.purchaseIntention.create({
data: {
listing_id: data.listing_id,
buyer_id: data.buyer_id,
payment_method: data.payment_method,
buyer_message: data.buyer_message || null,
expires_at: data.expires_at || null,
status: "pending",
custom_offer_amount:
data.custom_offer_amount !== undefined &&
data.custom_offer_amount !== null
? new Prisma.Decimal(data.custom_offer_amount)
: null,
custom_offer_currency: data.custom_offer_currency || null,
custom_offer_created_at:
data.custom_offer_amount !== undefined &&
data.custom_offer_amount !== null
? new Date()
: null,
},
include: {
listing: {
select: {
listing_id: true,
item_name: true,
seller_id: true,
},
},
buyer: {
select: {
user_id: true,
username: true,
email: true,
},
},
},
});
return serializePurchaseIntention(result);
}
/**
* Busca intenções de um anúncio específico
*/
export async function getPurchaseIntentionsByListing(
listingId: number,
filters: { status?: PurchaseIntentionStatus; limit?: number } = {},
): Promise<PurchaseIntention[]> {
const { status, limit = 50 } = filters;
const where: any = { listing_id: listingId };
if (status) {
where.status = status;
}
const intentions = await prisma.purchaseIntention.findMany({
where,
include: {
listing: {
select: {
listing_id: true,
item_name: true,
seller_id: true,
},
},
buyer: {
select: {
user_id: true,
username: true,
email: true,
},
},
},
orderBy: { created_at: "desc" },
take: limit,
});
return intentions.map(serializePurchaseIntention);
}
/**
* Busca intenções de um comprador
*/
export async function getPurchaseIntentionsByBuyer(
buyerId: number,
filters: { status?: PurchaseIntentionStatus; limit?: number } = {},
): Promise<PurchaseIntention[]> {
const { status, limit = 50 } = filters;
const where: any = { buyer_id: buyerId };
if (status) {
where.status = status;
}
const intentions = await prisma.purchaseIntention.findMany({
where,
include: {
listing: {
select: {
listing_id: true,
item_name: true,
seller_id: true,
},
},
buyer: {
select: {
user_id: true,
username: true,
email: true,
},
},
},
orderBy: { created_at: "desc" },
take: limit,
});
return intentions.map(serializePurchaseIntention);
}
/**
* Busca uma intenção específica por ID
*/
export async function getPurchaseIntentionById(
intentionId: number,
): Promise<PurchaseIntention | null> {
const intention = await prisma.purchaseIntention.findUnique({
where: { intention_id: intentionId },
include: {
listing: {
select: {
listing_id: true,
item_name: true,
seller_id: true,
},
},
buyer: {
select: {
user_id: true,
username: true,
email: true,
},
},
},
});
return serializePurchaseIntention(intention);
}
/**
* Atualiza o status de uma intenção
*/
export async function updatePurchaseIntentionStatus(
intentionId: number,
status: PurchaseIntentionStatus,
sellerResponse?: string,
): Promise<PurchaseIntention> {
const updateData: any = {
status,
updated_at: new Date(),
};
if (status === "accepted") {
updateData.accepted_at = new Date();
updateData.seller_response = sellerResponse || null;
} else if (status === "rejected") {
updateData.rejected_at = new Date();
updateData.seller_response = sellerResponse || null;
} else if (sellerResponse) {
updateData.seller_response = sellerResponse;
}
const updated = await prisma.purchaseIntention.update({
where: { intention_id: intentionId },
data: updateData,
include: {
listing: {
select: {
listing_id: true,
item_name: true,
seller_id: true,
},
},
buyer: {
select: {
user_id: true,
username: true,
email: true,
},
},
},
});
return serializePurchaseIntention(updated);
}
/**
* Conta intenções pendentes de um anúncio
*/
export async function getPendingIntentionsCount(
listingId: number,
): Promise<number> {
return await prisma.purchaseIntention.count({
where: {
listing_id: listingId,
status: "pending",
},
});
}
/**
* Verifica se já existe uma intenção pendente para um listing+buyer
*/
export async function hasPendingIntention(
listingId: number,
buyerId: number,
): Promise<boolean> {
const count = await prisma.purchaseIntention.count({
where: {
listing_id: listingId,
buyer_id: buyerId,
status: "pending",
},
});
return count > 0;
}
/**
* Busca intenção pendente de um comprador para um listing específico
*/
export async function getPendingIntentionByBuyerAndListing(
listingId: number,
buyerId: number,
): Promise<PurchaseIntention | null> {
const intention = await prisma.purchaseIntention.findFirst({
where: {
listing_id: listingId,
buyer_id: buyerId,
status: "pending",
},
include: {
listing: {
select: {
listing_id: true,
item_name: true,
seller_id: true,
},
},
buyer: {
select: {
user_id: true,
username: true,
email: true,
},
},
},
orderBy: {
created_at: "desc",
},
});
return serializePurchaseIntention(intention);
}
/**
* Atualiza o método de pagamento de uma intenção
*/
export async function updatePurchaseIntentionPaymentMethod(
intentionId: number,
newPaymentMethod: PaymentMethod,
): Promise<PurchaseIntention> {
// Buscar intenção atual
const intention = await prisma.purchaseIntention.findUnique({
where: { intention_id: intentionId },
});
if (!intention) {
throw new Error("Intenção não encontrada");
}
// Verificar se pode trocar (máximo 1 troca)
if (intention.payment_method_change_count >= 1) {
throw new Error(
"Você já atingiu o limite de trocas de método de pagamento",
);
}
// Verificar se o novo método é diferente
if (intention.payment_method === newPaymentMethod) {
throw new Error("O novo método de pagamento deve ser diferente do atual");
}
// Atualizar
const updated = await prisma.purchaseIntention.update({
where: { intention_id: intentionId },
data: {
payment_method: newPaymentMethod,
payment_method_change_count: intention.payment_method_change_count + 1,
updated_at: new Date(),
},
include: {
listing: {
select: {
listing_id: true,
item_name: true,
seller_id: true,
},
},
buyer: {
select: {
user_id: true,
username: true,
email: true,
},
},
},
});
return serializePurchaseIntention(updated);
}
/**
* Retorna os IDs de anúncios com intenções pendentes para um comprador
*/
export async function getPendingIntentionListingIdsForBuyer(
buyerId: number,
listingIds: number[],
): Promise<number[]> {
if (listingIds.length === 0) {
return [];
}
const intentions = await prisma.purchaseIntention.findMany({
where: {
buyer_id: buyerId,
status: "pending",
listing_id: {
in: listingIds,
},
},
select: {
listing_id: true,
},
});
const uniqueIds = new Set<number>();
intentions.forEach((intention) => {
uniqueIds.add(intention.listing_id);
});
return Array.from(uniqueIds);
}
export async function getHighestCustomOfferForListing(
listingId: number,
): Promise<{ amount: number; currency: string } | null> {
const offer = await prisma.purchaseIntention.findFirst({
where: {
listing_id: listingId,
status: { in: ["pending", "accepted"] },
custom_offer_amount: { not: null },
custom_offer_currency: { not: null },
},
orderBy: { custom_offer_amount: "desc" },
select: {
custom_offer_amount: true,
custom_offer_currency: true,
},
});
if (!offer) {
return null;
}
return {
amount: Number(offer.custom_offer_amount),
currency: offer.custom_offer_currency!,
};
}
export async function getHighestCustomOffersForListings(
listingIds: number[],
): Promise<Map<number, { amount: number; currency: string }>> {
if (listingIds.length === 0) {
return new Map();
}
const offers = await prisma.purchaseIntention.findMany({
where: {
listing_id: { in: listingIds },
status: { in: ["pending", "accepted"] },
custom_offer_amount: { not: null },
custom_offer_currency: { not: null },
},
select: {
listing_id: true,
custom_offer_amount: true,
custom_offer_currency: true,
},
orderBy: [{ listing_id: "asc" }, { custom_offer_amount: "desc" }],
});
const result = new Map<number, { amount: number; currency: string }>();
for (const offer of offers) {
if (!result.has(offer.listing_id)) {
result.set(offer.listing_id, {
amount: Number(offer.custom_offer_amount),
currency: offer.custom_offer_currency!,
});
}
}
return result;
}