This commit is contained in:
payacom
2026-08-31 20:45:41 +03:30
parent fa9f23c61d
commit 3f416a7a60
367 changed files with 621895 additions and 6443 deletions

View File

@@ -6,13 +6,17 @@ const UserModel = require('../../../models/UserModel')
const OfferTypeModel = require('../../../models/OfferTypeModel')
const PaymentModel = require('../../../models/PaymentModel')
const NotificationModel = require('../../../models/NotificationModel')
const { default: axios } = require('axios')
const CommentModel = require('../../../models/CommentModel')
const {
resolveRatingForComment,
recalculateUserRating
} = require('../../../utils/commentRating')
const { resolveCommentModerationStatus } = require('../../../utils/contentModeration')
const {
canUseFreeCreation,
consumeFreeCreation,
freeCreationLimitResponse
} = require('../../../utils/freeCreationLimit')
const getOfferPriceFromDatabase = async (itemName) => {
const item = await OfferTypeModel.findOne({ name: itemName })
if (!item) {
@@ -23,8 +27,12 @@ const getOfferPriceFromDatabase = async (itemName) => {
const getOfferTypes = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const authHeader = req.header('Authorization')
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ message: 'Access Denied' })
}
const token = authHeader.split(' ')[1]
if (!token) return res.status(401).json({ message: 'Access Denied' })
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
const userId = decodedToken.id
@@ -35,22 +43,41 @@ const getOfferTypes = async (req, res, next) => {
message: 'کاربر یافت نشد'
})
}
let offerTypes = await OfferTypeModel.find({}, 'name price')
let offerTypes = await OfferTypeModel.find({}, 'name price').lean()
// بررسی برای مواردی مانند پیدا نشدن انواع پروژه
if (!offerTypes) {
return res.status(404).json({ message: 'انواع پروژه یافت نشد.' })
// اگر در دیتابیس نوع‌ها خالی باشد، پیش‌فرض رایگان/پولی
if (!offerTypes || offerTypes.length === 0) {
offerTypes = [
{ name: 'free', price: 0 },
{ name: 'normal', price: 50000 }
]
}
// اگر کاربر درخواست ماهانه نداشته باشد، نوع پروژه ماهانه را حذف کنید
if (user.monthly_free_offer <= 0) {
offerTypes = offerTypes.filter(projectType => projectType.name !== 'free')
}
// ارسال انواع پروژه به کاربر
res.status(200).json({ offerTypes })
const canFree =
(user.monthly_free_offer || 0) > 0 && canUseFreeCreation(user)
// رایگان را حذف نکن — اگر مجاز نیست، با فلگ disabled برگردان تا UI نشان دهد
offerTypes = offerTypes.map((t) => {
const plain = t.toObject ? t.toObject() : { ...t }
if (plain.name === 'free') {
plain.disabled = !canFree
plain.unavailableReason = canFree
? null
: (user.monthly_free_offer || 0) <= 0
? 'سهمیه رایگان ماهانه شما تمام شده است'
: 'سقف ثبت رایگان شما به پایان رسیده است'
}
return plain
})
res.status(200).json({
offerTypes,
canUseFree: canFree,
monthly_free_offer: user.monthly_free_offer || 0
})
} catch (error) {
// در صورت بروز خطا، ارسال پیام خطا به کاربر
console.error('Error in getOfferTypes:', error)
res.status(500).json({ message: 'خطا در دریافت انواع پروژه.' })
res.status(500).json({ message: 'خطا در دریافت انواع درخواست.' })
}
}
const getUserOffers = async (req, res, next) => {
@@ -122,7 +149,6 @@ const updateOfferStatus = async (req, res, next) => {
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
const userId = decodedToken.id // ID کاربری که درخواست را به‌روزرسانی می‌کند
const reciverUser = await UserModel.findById(userId)
const { offerId, action } = req.body // آیدی آفر و اکشن (accept یا reject)
@@ -133,7 +159,6 @@ const updateOfferStatus = async (req, res, next) => {
// یافتن آفر موردنظر
const offer = await OfferModel.findById(offerId)
const senderUser = await UserModel.findById(offer?.sender)
if (!offer) {
return res.status(404).json({ error: 'Offer not found' })
@@ -153,59 +178,6 @@ const updateOfferStatus = async (req, res, next) => {
offer.status = action === 'accept' ? 'accepted' : 'rejected'
await offer.save()
if (action === 'accept') {
const data = JSON.stringify({
mobile: senderUser?.mobile,
templateId: '633448',
parameters: [
{ name: 'USER', value: reciverUser?.first_name + ' ' + reciverUser?.last_name },
{ name: 'EMPLOYER', value: senderUser?.first_name + ' ' + senderUser?.last_name }
]
})
const config = {
method: 'post',
url: 'https://api.sms.ir/v1/send/verify',
headers: {
'Content-Type': 'application/json',
Accept: 'text/plain',
'x-api-key': '2JKxGbTzE64t0YTkjip6RYHnm9mq1haMlidF8WWwQunwubHFVW6iaMmfFQbMXEDt'
},
data
}
axios(config)
.then(function (response) {
})
.catch(function (error) {
console.log(error)
})
} else {
const data = JSON.stringify({
mobile: senderUser?.mobile,
templateId: '682678',
parameters: [
{ name: 'USER', value: reciverUser?.first_name + ' ' + reciverUser?.last_name },
{ name: 'EMPLOYER', value: senderUser?.first_name + ' ' + senderUser?.last_name }
]
})
const config = {
method: 'post',
url: 'https://api.sms.ir/v1/send/verify',
headers: {
'Content-Type': 'application/json',
Accept: 'text/plain',
'x-api-key': '2JKxGbTzE64t0YTkjip6RYHnm9mq1haMlidF8WWwQunwubHFVW6iaMmfFQbMXEDt'
},
data
}
axios(config)
.then(function (response) {
})
.catch(function (error) {
console.log(error)
})
}
res.status(200).json({
message: `Offer has been ${offer.status} successfully`,
offerId: offer._id,
@@ -232,12 +204,28 @@ const handleSuccessfulOfferPayment = async (req, res) => {
const reciverUser = await UserModel.findById(reciverId)
if (!user) return res.status(404).json({ error: 'User not found' })
// اگر گیرنده «دریافت پرداختی از چت» رو غیرفعال کرده باشه، درخواست همکاری براش
// همیشه رایگانه — مستقل از سهمیه‌ی رایگان ماهانه‌ی فرستنده
const receiverOffersFree = reciverUser?.offer_payment_enabled === false
let price
if (user.monthly_free_offer > 0 && offerType === 'free') {
// Use free offer and decrement counter
if (receiverOffersFree) {
price = 0
} else if (user.monthly_free_offer > 0 && offerType === 'free' && canUseFreeCreation(user)) {
const consumed = await consumeFreeCreation(user)
if (!consumed.ok) {
return res.status(403).json(freeCreationLimitResponse())
}
price = 0
user.monthly_free_offer -= 1
await user.save()
} else if (offerType === 'free') {
return res.status(403).json({
error: true,
message: canUseFreeCreation(user)
? 'سهمیه درخواست رایگان ماهانه شما تمام شده است.'
: freeCreationLimitResponse().message,
})
} else {
// Retrieve offer price based on type
price = await getOfferPriceFromDatabase(offerType)
@@ -283,34 +271,6 @@ const handleSuccessfulOfferPayment = async (req, res) => {
})
await notification.save()
// Send Sms
const data = JSON.stringify({
mobile: reciverUser?.mobile,
templateId: '569006',
parameters: [
{ name: 'FIRSTNAME', value: reciverUser?.first_name },
{ name: 'LASTNAME', value: reciverUser?.last_name }
]
})
const config = {
method: 'post',
url: 'https://api.sms.ir/v1/send/verify',
headers: {
'Content-Type': 'application/json',
Accept: 'text/plain',
'x-api-key': '2JKxGbTzE64t0YTkjip6RYHnm9mq1haMlidF8WWwQunwubHFVW6iaMmfFQbMXEDt'
},
data
}
axios(config)
.then(function (response) {
})
.catch(function (error) {
console.log(error)
})
// Send event based on payment status
// Emit a success event
@@ -392,6 +352,8 @@ const createOfferComment = async (req, res, next) => {
return res.status(400).send({ message: 'Rating must be between 1 and 5' })
}
const commentStatus = resolveCommentModerationStatus(comment)
const newComment = new CommentModel({
user: user_id,
offer: offerId,
@@ -399,7 +361,7 @@ const createOfferComment = async (req, res, next) => {
rating: ratingValue,
comment,
comment_for: 'user',
status: 'pending'
status: commentStatus
})
await newComment.save()