133 lines
4.1 KiB
JavaScript
133 lines
4.1 KiB
JavaScript
const { updateDealVoteInRedis } = require("./redis/dealVote.service");
|
||
const { queueVoteUpdate, queueDealUpdate, queueNotificationCreate } = require("./redis/dbSync.service");
|
||
const { updateDealInRedis, getDealFromRedis } = require("./redis/dealCache.service");
|
||
const { publishNotification } = require("./redis/notificationPubsub.service");
|
||
const { trackUserCategoryInterest, USER_INTEREST_ACTIONS } = require("./userInterest.service");
|
||
const voteDb = require("../db/vote.db")
|
||
|
||
async function voteDeal({ dealId, userId, voteType }) {
|
||
if (!dealId || !userId || voteType === undefined) {
|
||
const err = new Error("Eksik veri");
|
||
err.statusCode = 400;
|
||
throw err;
|
||
}
|
||
|
||
if (![ -1, 0, 1 ].includes(voteType)) {
|
||
const err = new Error("voteType -1, 0 veya 1 olmalı");
|
||
err.statusCode = 400;
|
||
throw err;
|
||
}
|
||
|
||
const deal = await getDealFromRedis(Number(dealId));
|
||
if (!deal || !["ACTIVE", "EXPIRED"].includes(deal.status)) {
|
||
const err = new Error("deal sadece ACTIVE veya EXPIRED iken oylanabilir");
|
||
err.statusCode = 400;
|
||
throw err;
|
||
}
|
||
|
||
const createdAt = new Date();
|
||
|
||
const redisResult = await updateDealVoteInRedis({
|
||
dealId,
|
||
userId,
|
||
voteType,
|
||
}).catch((err) => {
|
||
console.error("Redis vote update failed:", err?.message || err);
|
||
return { updated: false, delta: 0, score: null, maxNotifiedMilestone: 0, dealUserId: null };
|
||
});
|
||
|
||
queueVoteUpdate({
|
||
dealId,
|
||
userId,
|
||
voteType,
|
||
createdAt: createdAt.toISOString(),
|
||
}).catch((err) => console.error("DB sync vote queue failed:", err?.message || err));
|
||
|
||
const scoreVal = Number(redisResult?.score)
|
||
const maxNotified = Number(redisResult?.maxNotifiedMilestone ?? 0)
|
||
const ownerId = Number(redisResult?.dealUserId)
|
||
if (Number.isFinite(scoreVal) && Number.isInteger(ownerId) && ownerId > 0) {
|
||
const milestone = 100
|
||
if (scoreVal >= milestone && maxNotified < milestone) {
|
||
const updatedAt = new Date()
|
||
await updateDealInRedis(dealId, { maxNotifiedMilestone: milestone }, { updatedAt }).catch((err) =>
|
||
console.error("Redis deal milestone update failed:", err?.message || err)
|
||
)
|
||
queueDealUpdate({
|
||
dealId,
|
||
data: { maxNotifiedMilestone: milestone },
|
||
updatedAt: updatedAt.toISOString(),
|
||
}).catch((err) => console.error("DB sync deal milestone update failed:", err?.message || err))
|
||
queueNotificationCreate({
|
||
userId: ownerId,
|
||
message: "Fırsatın 100 beğeniyi geçti!",
|
||
type: "MILESTONE",
|
||
extras: {
|
||
dealId: Number(dealId),
|
||
milestone,
|
||
},
|
||
createdAt: updatedAt.toISOString(),
|
||
}).catch((err) => console.error("DB sync notification queue failed:", err?.message || err))
|
||
publishNotification({
|
||
userId: ownerId,
|
||
message: "Fırsatın 100 beğeniyi geçti!",
|
||
type: "MILESTONE",
|
||
extras: {
|
||
dealId: Number(dealId),
|
||
milestone,
|
||
},
|
||
createdAt: updatedAt.toISOString(),
|
||
}).catch((err) => console.error("Notification publish failed:", err?.message || err))
|
||
}
|
||
}
|
||
|
||
// Fallback: Redis yoksa mevcut DB'den approx hesapla
|
||
let delta = redisResult?.delta ?? 0;
|
||
let score = redisResult?.score ?? null;
|
||
if (score === null) {
|
||
const current = await getDealFromRedis(Number(dealId));
|
||
score = current ? Number(current?.score ?? 0) : null;
|
||
delta = 0;
|
||
}
|
||
|
||
if (Number(voteType) === 1) {
|
||
trackUserCategoryInterest({
|
||
userId,
|
||
categoryId: deal.categoryId,
|
||
action: USER_INTEREST_ACTIONS.DEAL_HOT_VOTE,
|
||
}).catch((err) => console.error("User interest track failed:", err?.message || err))
|
||
}
|
||
|
||
return {
|
||
dealId,
|
||
voteType,
|
||
delta,
|
||
score,
|
||
};
|
||
}
|
||
|
||
async function getVotes(dealId) {
|
||
const votes = await voteDb.findVotes(
|
||
{ dealId },
|
||
{
|
||
select: {
|
||
id: true,
|
||
dealId: true,
|
||
userId: true,
|
||
voteType: true,
|
||
createdAt: true,
|
||
lastVotedAt: true,
|
||
},
|
||
orderBy: { createdAt: "desc" },
|
||
}
|
||
)
|
||
|
||
return votes.map((vote) => ({
|
||
...vote,
|
||
createdAt: vote.createdAt.toISOString(),
|
||
lastVotedAt: vote.lastVotedAt.toISOString(),
|
||
}))
|
||
}
|
||
|
||
module.exports = { voteDeal, getVotes };
|