HotTRDealsBackend/services/comment.service.js
2026-01-29 00:45:52 +00:00

179 lines
5.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const dealDB = require("../db/deal.db")
const commentDB = require("../db/comment.db")
const prisma = require("../db/client")
function assertPositiveInt(v, name = "id") {
const n = Number(v)
if (!Number.isInteger(n) || n <= 0) throw new Error(`Geçersiz ${name}.`)
return n
}
const DEFAULT_LIMIT = 20
const MAX_LIMIT = 50
const MAX_SKIP = 5000
function clampPagination({ page, limit }) {
const rawPage = Number(page)
const rawLimit = Number(limit)
const normalizedPage = Number.isFinite(rawPage) ? Math.max(1, Math.floor(rawPage)) : 1
let normalizedLimit = Number.isFinite(rawLimit) ? Math.max(1, Math.floor(rawLimit)) : DEFAULT_LIMIT
normalizedLimit = Math.min(MAX_LIMIT, normalizedLimit)
const skip = (normalizedPage - 1) * normalizedLimit
if (skip > MAX_SKIP) throw new Error("PAGE_TOO_DEEP")
return { page: normalizedPage, limit: normalizedLimit, skip }
}
function parseParentId(value) {
if (value === undefined) return null
if (value === null) return null
if (value === "" || value === "null") return null
const pid = Number(value)
if (!Number.isInteger(pid) || pid <= 0) throw new Error("Geçersiz parentId.")
return pid
}
function normalizeSort(value) {
const normalized = String(value || "new").trim().toLowerCase()
if (["top", "best", "liked"].includes(normalized)) return "TOP"
return "NEW"
}
async function getCommentsByDealId(dealId, { parentId, page, limit, sort, viewer } = {}) {
const id = Number(dealId)
const deal = await dealDB.findDeal({ id })
if (!deal) throw new Error("Deal bulunamadı.")
const include = {
user: { select: { id: true, username: true, avatarUrl: true } },
_count: { select: { replies: true } },
}
const pagination = clampPagination({ page, limit })
const parsedParentId = parseParentId(parentId)
const sortMode = normalizeSort(sort)
const orderBy =
sortMode === "TOP"
? [{ likeCount: "desc" }, { createdAt: "desc" }]
: [{ createdAt: "desc" }]
const where = { dealId: id, parentId: parsedParentId }
const [results, total] = await Promise.all([
commentDB.findComments(where, {
include,
orderBy,
skip: pagination.skip,
take: pagination.limit,
}),
commentDB.countComments(where),
])
let likedIds = new Set()
if (viewer?.userId && results.length > 0) {
const commentLikeDb = require("../db/commentLike.db")
const likes = await commentLikeDb.findLikesByUserAndCommentIds(
viewer.userId,
results.map((c) => c.id)
)
likedIds = new Set(likes.map((l) => l.commentId))
}
const enriched = results.map((comment) => ({
...comment,
myLike: likedIds.has(comment.id),
}))
return {
page: pagination.page,
total,
totalPages: Math.ceil(total / pagination.limit),
results: enriched,
}
}
async function createComment({ dealId, userId, text, parentId = null }) {
if (!text || typeof text !== "string" || !text.trim()) {
throw new Error("Yorum boş olamaz.")
}
const trimmed = text.trim()
const include = { user: { select: { id: true, username: true, avatarUrl: true } } }
return prisma.$transaction(async (tx) => {
const deal = await dealDB.findDeal({ id: dealId }, { select: { id: true, status: true } }, tx)
if (!deal) throw new Error("Deal bulunamadı.")
if (deal.status !== "ACTIVE" && deal.status !== "EXPIRED") {
throw new Error("Bu deal için yorum açılamaz.")
}
// ✅ Reply ise parent doğrula
let parent = null
if (parentId != null) {
const pid = Number(parentId)
if (!Number.isFinite(pid) || pid <= 0) throw new Error("Geçersiz parentId.")
parent = await commentDB.findComment({ id: pid }, { select: { id: true, dealId: true } }, tx)
if (!parent) throw new Error("Yanıtlanan yorum bulunamadı.")
if (parent.dealId !== dealId) throw new Error("Yanıtlanan yorum bu deal'a ait değil.")
}
const comment = await commentDB.createComment(
{
text: trimmed,
userId,
dealId,
parentId: parent ? parent.id : null,
},
{ include },
tx
)
await dealDB.updateDeal(
{ id: dealId },
{ commentCount: { increment: 1 } },
{},
tx
)
return comment
})
}
async function deleteComment(commentId, userId) {
const comment = await commentDB.findComment(
{ id: commentId },
{ select: { userId: true, dealId: true, deletedAt: true } }
)
if (!comment || comment.deletedAt) throw new Error("Yorum bulunamadı.")
if (comment.userId !== userId) throw new Error("Bu yorumu silme yetkin yok.")
await prisma.$transaction(async (tx) => {
const result = await commentDB.softDeleteComment({ id: commentId, deletedAt: null }, tx)
if (result.count > 0) {
await dealDB.updateDeal(
{ id: comment.dealId },
{ commentCount: { decrement: 1 } },
{},
tx
)
}
})
return { message: "Yorum silindi." }
}
async function commentChange(length,dealId){
}
module.exports = {
getCommentsByDealId,
createComment,
deleteComment,
}