feat: enhance purchase intentions with chat integration and offer tracking

- Added MongoDB chat/message integration for seller responses
- Improved seller response validation and default messaging
- Integrated automatic chat creation/update when responding to intentions
- Added highest custom offer fields to listing queries and models
- Enhanced listing data with top offer information for buyers
This commit is contained in:
Evandro Fonseca Junior committed 2025-11-08 11:51:21 -03:00
1 parent d27526e026
commit d8e7d5d7e6
19 files changed
+806 -58

No files matched your search

+122 -14
View File
@@ -3,6 +3,7 @@
*/
import { prisma } from "../app";
import { Prisma } from "@prisma/client";
export type PurchaseIntentionStatus = "pending" | "accepted" | "rejected" | "expired" | "completed";
export type PaymentMethod = "pix" | "tibia_coins" | "gold_coins" | "trade";
@@ -13,6 +14,8 @@ export interface CreatePurchaseIntentionData {
payment_method: PaymentMethod;
buyer_message?: string;
expires_at?: Date;
custom_offer_amount?: number | null;
custom_offer_currency?: PaymentMethod | null;
}
export interface PurchaseIntention {
@@ -21,6 +24,9 @@ export interface PurchaseIntention {
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;
@@ -42,13 +48,27 @@ export interface PurchaseIntention {
};
}
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> {
return await prisma.purchaseIntention.create({
const result = await prisma.purchaseIntention.create({
data: {
listing_id: data.listing_id,
buyer_id: data.buyer_id,
@@ -56,6 +76,15 @@ export async function createPurchaseIntention(
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: {
@@ -73,7 +102,9 @@ export async function createPurchaseIntention(
},
},
},
}) as PurchaseIntention;
});
return serializePurchaseIntention(result);
}
/**
@@ -90,7 +121,7 @@ export async function getPurchaseIntentionsByListing(
where.status = status;
}
return await prisma.purchaseIntention.findMany({
const intentions = await prisma.purchaseIntention.findMany({
where,
include: {
listing: {
@@ -110,7 +141,9 @@ export async function getPurchaseIntentionsByListing(
},
orderBy: { created_at: "desc" },
take: limit,
}) as PurchaseIntention[];
});
return intentions.map(serializePurchaseIntention);
}
/**
@@ -127,7 +160,7 @@ export async function getPurchaseIntentionsByBuyer(
where.status = status;
}
return await prisma.purchaseIntention.findMany({
const intentions = await prisma.purchaseIntention.findMany({
where,
include: {
listing: {
@@ -147,7 +180,9 @@ export async function getPurchaseIntentionsByBuyer(
},
orderBy: { created_at: "desc" },
take: limit,
}) as PurchaseIntention[];
});
return intentions.map(serializePurchaseIntention);
}
/**
@@ -156,7 +191,7 @@ export async function getPurchaseIntentionsByBuyer(
export async function getPurchaseIntentionById(
intentionId: number
): Promise<PurchaseIntention | null> {
return await prisma.purchaseIntention.findUnique({
const intention = await prisma.purchaseIntention.findUnique({
where: { intention_id: intentionId },
include: {
listing: {
@@ -174,7 +209,9 @@ export async function getPurchaseIntentionById(
},
},
},
}) as PurchaseIntention | null;
});
return serializePurchaseIntention(intention);
}
/**
@@ -200,7 +237,7 @@ export async function updatePurchaseIntentionStatus(
updateData.seller_response = sellerResponse;
}
return await prisma.purchaseIntention.update({
const updated = await prisma.purchaseIntention.update({
where: { intention_id: intentionId },
data: updateData,
include: {
@@ -219,7 +256,9 @@ export async function updatePurchaseIntentionStatus(
},
},
},
}) as PurchaseIntention;
});
return serializePurchaseIntention(updated);
}
/**
@@ -259,7 +298,7 @@ export async function getPendingIntentionByBuyerAndListing(
listingId: number,
buyerId: number
): Promise<PurchaseIntention | null> {
return await prisma.purchaseIntention.findFirst({
const intention = await prisma.purchaseIntention.findFirst({
where: {
listing_id: listingId,
buyer_id: buyerId,
@@ -284,7 +323,9 @@ export async function getPendingIntentionByBuyerAndListing(
orderBy: {
created_at: "desc",
},
}) as PurchaseIntention | null;
});
return serializePurchaseIntention(intention);
}
/**
@@ -314,7 +355,7 @@ export async function updatePurchaseIntentionPaymentMethod(
}
// Atualizar
return await prisma.purchaseIntention.update({
const updated = await prisma.purchaseIntention.update({
where: { intention_id: intentionId },
data: {
payment_method: newPaymentMethod,
@@ -337,7 +378,9 @@ export async function updatePurchaseIntentionPaymentMethod(
},
},
},
}) as PurchaseIntention;
});
return serializePurchaseIntention(updated);
}
/**
@@ -372,3 +415,68 @@ export async function getPendingIntentionListingIdsForBuyer(
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;
}