full
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable camelcase */
|
||||
const { ProvinceModel, CityModel } = require('../../../models/StateCity')
|
||||
const { CountryModel, ProvinceModel, CityModel } = require('../../../models/StateCity')
|
||||
const ProjectModel = require('../../../models/ProjectModel')
|
||||
const UserModel = require('../../../models/UserModel')
|
||||
const { check, validationResult } = require('express-validator')
|
||||
@@ -8,45 +8,78 @@ const {
|
||||
canCreateProject,
|
||||
respondProjectProfileIncomplete
|
||||
} = require('../../../utils/projectProfile')
|
||||
|
||||
async function activateFreeProjectIfEligible(user, project) {
|
||||
if (project.project_type !== 'free') return { activated: false }
|
||||
|
||||
if (!user.daily_free_request || user.daily_free_request <= 0) {
|
||||
return {
|
||||
activated: false,
|
||||
error: 'شما قبلاً از درخواست رایگان امروز استفاده کردهاید.'
|
||||
}
|
||||
}
|
||||
|
||||
user.daily_free_request -= 1
|
||||
await user.save()
|
||||
project.payment_status = 'done'
|
||||
project.status = 'temp_accept'
|
||||
await project.save()
|
||||
return { activated: true }
|
||||
}
|
||||
|
||||
function normalizeRoles(roles) {
|
||||
if (!Array.isArray(roles)) return []
|
||||
return roles.slice(0, 5).map((role) => ({
|
||||
expertise: role.expertise,
|
||||
sub_expertise: Array.isArray(role.sub_expertise)
|
||||
? role.sub_expertise
|
||||
: (role.subExpertise || []),
|
||||
number_of_person: Number(role.number_of_person || role.numberOfPerson || 1) || 1,
|
||||
height_min: role.height_min ?? role.heightMin ?? null,
|
||||
height_max: role.height_max ?? role.heightMax ?? null,
|
||||
weight_min: role.weight_min ?? role.weightMin ?? null,
|
||||
weight_max: role.weight_max ?? role.weightMax ?? null,
|
||||
size_min: role.size_min ?? role.sizeMin ?? null,
|
||||
size_max: role.size_max ?? role.sizeMax ?? null,
|
||||
eye_color: role.eye_color || role.eyeColor || null,
|
||||
hair_color: role.hair_color || role.hairColor || null,
|
||||
portfolio_required: Boolean(
|
||||
role.portfolio_required ?? role.portfolioRequired ?? false
|
||||
)
|
||||
})).filter((role) => role.expertise)
|
||||
}
|
||||
|
||||
function buildAgeLabel(age, ageMin, ageMax) {
|
||||
if (age) return age
|
||||
if (ageMin != null && ageMax != null) return `${ageMin}-${ageMax}`
|
||||
if (ageMin != null) return `${ageMin}+`
|
||||
if (ageMax != null) return `تا ${ageMax}`
|
||||
return 'all'
|
||||
}
|
||||
|
||||
const createProjectValidationRules = () => {
|
||||
return [
|
||||
check('title').notEmpty().withMessage('عنوان نمیتواند خالی باشد'),
|
||||
|
||||
check('expertise').notEmpty().withMessage('تخصص نمیتواند خالی باشد'),
|
||||
|
||||
check('gender')
|
||||
.isIn(['male', 'female'])
|
||||
.withMessage('جنسیت باید male یا female باشد'),
|
||||
|
||||
check('age').notEmpty().withMessage('سن نمیتواند خالی باشد'),
|
||||
|
||||
check('conversation_projects')
|
||||
.notEmpty()
|
||||
.withMessage('پروژههای مکالمه نمیتواند خالی باشد')
|
||||
.isBoolean()
|
||||
.withMessage('مقدار پروژههای مکالمه باید یک مقدار boolean باشد'),
|
||||
|
||||
check('province').notEmpty().withMessage('استان نمیتواند خالی باشد'),
|
||||
|
||||
check('city').notEmpty().withMessage('شهر نمیتواند خالی باشد'),
|
||||
|
||||
check('offer_time')
|
||||
.notEmpty()
|
||||
.withMessage('زمان پروژه نمیتواند خالی باشد'),
|
||||
|
||||
check('offer_price')
|
||||
.notEmpty()
|
||||
.withMessage('قیمت پیشنهادی نمیتواند خالی باشد'),
|
||||
|
||||
check('description')
|
||||
.notEmpty()
|
||||
.withMessage('توضیحات نمیتواند خالی باشد'),
|
||||
|
||||
check('province').notEmpty().withMessage('استان نمیتواند خالی باشد'),
|
||||
check('city').notEmpty().withMessage('شهر نمیتواند خالی باشد'),
|
||||
check('offer_time')
|
||||
.notEmpty()
|
||||
.withMessage('زمان پروژه نمیتواند خالی باشد'),
|
||||
check('offer_price')
|
||||
.notEmpty()
|
||||
.withMessage('قیمت پیشنهادی نمیتواند خالی باشد'),
|
||||
check('project_type')
|
||||
.notEmpty()
|
||||
.withMessage('نوع پروژه نمیتواند خالی باشد')
|
||||
.isIn(['free', 'normal', 'force', 'highlight'])
|
||||
.withMessage('نوع پروژه باید normal یا force یا highlight باشد')
|
||||
.withMessage('نوع پروژه باید normal یا force یا highlight باشد'),
|
||||
check('gender')
|
||||
.optional({ nullable: true, checkFalsy: true })
|
||||
.isIn(['male', 'female', 'any'])
|
||||
.withMessage('جنسیت باید male یا female یا any باشد')
|
||||
]
|
||||
}
|
||||
|
||||
@@ -66,33 +99,55 @@ const createProject = async (req, res, next) => {
|
||||
if (!canCreateProject(user)) {
|
||||
return respondProjectProfileIncomplete(res)
|
||||
}
|
||||
// اعتبارسنجی درخواست
|
||||
|
||||
const errors = validationResult(req)
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(422).json({ errors: errors.array() })
|
||||
}
|
||||
|
||||
const {
|
||||
title,
|
||||
expertise,
|
||||
sub_expertise,
|
||||
gender,
|
||||
age,
|
||||
age_min,
|
||||
age_max,
|
||||
conversation_projects,
|
||||
country,
|
||||
province,
|
||||
city,
|
||||
lat,
|
||||
lng,
|
||||
neighborhood,
|
||||
address,
|
||||
offer_time,
|
||||
offer_price,
|
||||
description,
|
||||
project_type,
|
||||
public_status,
|
||||
created_for_user,
|
||||
number_of_person
|
||||
number_of_person,
|
||||
roles,
|
||||
payment_method,
|
||||
start_date,
|
||||
work_hours_start,
|
||||
work_hours_end,
|
||||
collaboration_type,
|
||||
experience
|
||||
} = req.body
|
||||
|
||||
const normalizedRoles = normalizeRoles(roles)
|
||||
const primaryExpertise =
|
||||
expertise || (normalizedRoles[0] && normalizedRoles[0].expertise)
|
||||
const primarySubExpertise =
|
||||
sub_expertise ||
|
||||
(normalizedRoles[0] && normalizedRoles[0].sub_expertise) ||
|
||||
[]
|
||||
|
||||
if (
|
||||
!title ||
|
||||
!expertise ||
|
||||
!age ||
|
||||
conversation_projects === null ||
|
||||
!primaryExpertise ||
|
||||
!province ||
|
||||
!city ||
|
||||
!offer_time ||
|
||||
@@ -106,110 +161,137 @@ const createProject = async (req, res, next) => {
|
||||
})
|
||||
}
|
||||
|
||||
if (normalizedRoles.length === 0) {
|
||||
return res.status(422).json({
|
||||
error: true,
|
||||
message: 'حداقل یک نقش باید ثبت شود'
|
||||
})
|
||||
}
|
||||
|
||||
const countryFind = country
|
||||
? await CountryModel.findOne({ id: country }, { _id: 0 })
|
||||
: null
|
||||
const provinceFind = await ProvinceModel.findOne(
|
||||
{ id: province },
|
||||
{ _id: 0 }
|
||||
)
|
||||
const cityFind = await CityModel.findOne({ id: city })
|
||||
// اگر پروژه برای یک کاربر خاص باید ساخته شود
|
||||
|
||||
const ageLabel = buildAgeLabel(age, age_min, age_max)
|
||||
const genderValue = gender || 'any'
|
||||
const conversationValue =
|
||||
conversation_projects === undefined || conversation_projects === null
|
||||
? true
|
||||
: conversation_projects
|
||||
|
||||
const sharedFields = {
|
||||
title,
|
||||
expertise: primaryExpertise,
|
||||
sub_expertise: primarySubExpertise,
|
||||
gender: genderValue,
|
||||
age: ageLabel,
|
||||
age_min: age_min != null && age_min !== '' ? Number(age_min) : null,
|
||||
age_max: age_max != null && age_max !== '' ? Number(age_max) : null,
|
||||
conversation_projects: conversationValue,
|
||||
country: countryFind
|
||||
? { id: countryFind.id, name: countryFind.name_fa, iso2: countryFind.iso2, slug: countryFind.slug }
|
||||
: null,
|
||||
province: provinceFind || province,
|
||||
city: cityFind || city,
|
||||
lat: lat != null && lat !== '' ? Number(lat) : null,
|
||||
lng: lng != null && lng !== '' ? Number(lng) : null,
|
||||
neighborhood: neighborhood || null,
|
||||
address: address || null,
|
||||
offer_time,
|
||||
offer_price,
|
||||
description,
|
||||
...(payment_method ? { payment_method } : {}),
|
||||
start_date: start_date || null,
|
||||
work_hours_start: work_hours_start || null,
|
||||
work_hours_end: work_hours_end || null,
|
||||
...(collaboration_type ? { collaboration_type } : {}),
|
||||
experience: experience || null,
|
||||
roles: normalizedRoles,
|
||||
creator_id: user._id,
|
||||
public_status: public_status || 'public',
|
||||
created_for_user: created_for_user || null
|
||||
}
|
||||
|
||||
// private project for a specific user
|
||||
if (public_status && public_status === 'private' && created_for_user) {
|
||||
const createdUser = await UserModel.findById(created_for_user)
|
||||
if (!createdUser) {
|
||||
return res.status(422).json({
|
||||
error: true, message: 'کاربر مورد نظر برای ساخت پروژه یافت نشد'
|
||||
error: true,
|
||||
message: 'کاربر مورد نظر برای ساخت پروژه یافت نشد'
|
||||
})
|
||||
}
|
||||
// ایجاد شیء پروژه
|
||||
const projectData = {
|
||||
title,
|
||||
expertise,
|
||||
sub_expertise,
|
||||
gender,
|
||||
age,
|
||||
conversation_projects,
|
||||
province,
|
||||
city,
|
||||
offer_time,
|
||||
offer_price,
|
||||
description,
|
||||
project_type,
|
||||
creator_id: user._id, // ارتباط با کاربر
|
||||
public_status,
|
||||
created_for_user
|
||||
}
|
||||
|
||||
// ساخت شیء پروژه
|
||||
const project = new ProjectModel(projectData)
|
||||
|
||||
// ذخیره کردن رکورد در پایگاه داده
|
||||
const project = new ProjectModel({
|
||||
...sharedFields,
|
||||
project_type
|
||||
})
|
||||
await project.save()
|
||||
|
||||
res.status(201).json({ message: 'پروژه با موفقیت ساخته شد', id: project._id }
|
||||
)
|
||||
} else {
|
||||
if (number_of_person > 1) {
|
||||
for (let i = 1; i <= number_of_person; i++) {
|
||||
let currentProjectType
|
||||
const freeResult = await activateFreeProjectIfEligible(user, project)
|
||||
if (freeResult.error) {
|
||||
return res.status(403).json({ error: true, message: freeResult.error })
|
||||
}
|
||||
|
||||
if (project_type === 'free' && i === 1) {
|
||||
currentProjectType = 'free'
|
||||
} else if (project_type === 'highlight' || project_type === 'force') {
|
||||
currentProjectType = project_type
|
||||
} else {
|
||||
currentProjectType = 'normal'
|
||||
}
|
||||
return res.status(201).json({
|
||||
message: 'پروژه با موفقیت ساخته شد',
|
||||
id: project._id
|
||||
})
|
||||
}
|
||||
|
||||
const newProject = new ProjectModel({
|
||||
title: `${title}_${i}`,
|
||||
expertise,
|
||||
sub_expertise,
|
||||
gender,
|
||||
age,
|
||||
conversation_projects,
|
||||
province: provinceFind,
|
||||
city: cityFind,
|
||||
offer_time,
|
||||
offer_price,
|
||||
description,
|
||||
creator_id: user._id, // ارتباط با کاربر
|
||||
project_type: currentProjectType,
|
||||
public_status,
|
||||
created_for_user
|
||||
})
|
||||
|
||||
// ذخیره کردن رکورد در پایگاه داده
|
||||
await newProject.save()
|
||||
// Legacy: multiple projects by headcount when no multi-role payload intent
|
||||
const headcount = Number(number_of_person) || 1
|
||||
if (headcount > 1 && normalizedRoles.length <= 1) {
|
||||
for (let i = 1; i <= headcount; i++) {
|
||||
let currentProjectType
|
||||
if (project_type === 'free' && i === 1) {
|
||||
currentProjectType = 'free'
|
||||
} else if (project_type === 'highlight' || project_type === 'force') {
|
||||
currentProjectType = project_type
|
||||
} else {
|
||||
currentProjectType = 'normal'
|
||||
}
|
||||
|
||||
res.status(201).json({
|
||||
message: 'پروژهها با موفقیت ساخته شدند'
|
||||
})
|
||||
} else {
|
||||
const project = new ProjectModel({
|
||||
title,
|
||||
expertise,
|
||||
sub_expertise,
|
||||
gender,
|
||||
age,
|
||||
conversation_projects,
|
||||
province: provinceFind,
|
||||
city: cityFind,
|
||||
offer_time,
|
||||
offer_price,
|
||||
description,
|
||||
creator_id: user._id, // ارتباط با کاربر
|
||||
project_type
|
||||
const newProject = new ProjectModel({
|
||||
...sharedFields,
|
||||
title: `${title}_${i}`,
|
||||
project_type: currentProjectType
|
||||
})
|
||||
await newProject.save()
|
||||
|
||||
// ذخیره کردن رکورد در پایگاه داده
|
||||
await project.save()
|
||||
|
||||
res.status(201).json({
|
||||
message: 'پروژه با موفقیت ساخته شد',
|
||||
id: project._id
|
||||
})
|
||||
if (currentProjectType === 'free') {
|
||||
const freeResult = await activateFreeProjectIfEligible(user, newProject)
|
||||
if (freeResult.error) {
|
||||
return res.status(403).json({ error: true, message: freeResult.error })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res.status(201).json({
|
||||
message: 'پروژهها با موفقیت ساخته شدند'
|
||||
})
|
||||
}
|
||||
|
||||
const project = new ProjectModel({
|
||||
...sharedFields,
|
||||
project_type
|
||||
})
|
||||
await project.save()
|
||||
|
||||
const freeResult = await activateFreeProjectIfEligible(user, project)
|
||||
if (freeResult.error) {
|
||||
return res.status(403).json({ error: true, message: freeResult.error })
|
||||
}
|
||||
|
||||
return res.status(201).json({
|
||||
message: 'پروژه با موفقیت ساخته شد',
|
||||
id: project._id
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const TypeAndPriceModel = require('../../../models/TypeAndPriceModel')
|
||||
const jwt = require('jsonwebtoken')
|
||||
const UserModel = require('../../../models/UserModel')
|
||||
const { canUseFreeCreation } = require('../../../utils/freeCreationLimit')
|
||||
|
||||
const getProjectTypes = async (req, res, next) => {
|
||||
try {
|
||||
@@ -24,7 +25,7 @@ const getProjectTypes = async (req, res, next) => {
|
||||
return res.status(404).json({ message: 'انواع پروژه یافت نشد.' })
|
||||
}
|
||||
// اگر کاربر درخواست رایگان روزانه نداشته باشد، نوع پروژه رایگان را حذف کنید
|
||||
if (user.daily_free_request <= 0) {
|
||||
if (user.daily_free_request <= 0 || !canUseFreeCreation(user)) {
|
||||
projectTypes = projectTypes.filter(projectType => projectType.name !== 'free')
|
||||
}
|
||||
// ارسال انواع پروژه به کاربر
|
||||
|
||||
@@ -1,18 +1,56 @@
|
||||
/* eslint-disable camelcase */
|
||||
const { default: axios } = require('axios')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const NotificationModel = require('../../../models/NotificationModel')
|
||||
const ProjectModel = require('../../../models/ProjectModel')
|
||||
const RequestModel = require('../../../models/RequestModel')
|
||||
const UserModel = require('../../../models/UserModel')
|
||||
const { check, validationResult } = require('express-validator')
|
||||
const jwt = require('jsonwebtoken')
|
||||
|
||||
const portfolioStorageDir = path.join(
|
||||
process.cwd(),
|
||||
'public',
|
||||
'uploads',
|
||||
'project-requests'
|
||||
)
|
||||
|
||||
async function savePortfolioFiles(files, userId) {
|
||||
if (!Array.isArray(files) || files.length === 0) return []
|
||||
if (!fs.existsSync(portfolioStorageDir)) {
|
||||
fs.mkdirSync(portfolioStorageDir, { recursive: true })
|
||||
}
|
||||
|
||||
const saved = []
|
||||
const limited = files.slice(0, 5)
|
||||
for (let i = 0; i < limited.length; i++) {
|
||||
const file = limited[i]
|
||||
const raw = file?.data || file
|
||||
if (typeof raw !== 'string' || !raw.includes('base64')) continue
|
||||
|
||||
const isVideo =
|
||||
file?.type === 'video' ||
|
||||
String(file?.mime || '').startsWith('video/') ||
|
||||
raw.startsWith('data:video')
|
||||
const buffer = Buffer.from(raw.split(',')[1] || raw, 'base64')
|
||||
const ext = isVideo ? '.mp4' : '.webp'
|
||||
const filename = `${Date.now()}-${userId}-${i}${ext}`
|
||||
const filePath = path.join(portfolioStorageDir, filename)
|
||||
await fs.promises.writeFile(filePath, buffer)
|
||||
saved.push({
|
||||
path: `/uploads/project-requests/${filename}`,
|
||||
type: isVideo ? 'video' : 'image'
|
||||
})
|
||||
}
|
||||
return saved
|
||||
}
|
||||
|
||||
const requestProjectValidationRules = () => {
|
||||
return [
|
||||
check('project_id')
|
||||
.notEmpty().withMessage('آیدی پروژه نمیتواند خالی باشد'),
|
||||
check('request_time')
|
||||
.notEmpty().withMessage('زمان پروژه نمیتواند خالی باشد'),
|
||||
|
||||
check('offer_price')
|
||||
.notEmpty().withMessage('قیمت پیشنهادی نمیتواند خالی باشد')
|
||||
]
|
||||
@@ -29,19 +67,21 @@ const requestProject = async (req, res, next) => {
|
||||
error: true, message: 'شما دسترسی به این بخش ندارید'
|
||||
})
|
||||
}
|
||||
if (user.is_verified !== 'verified') {
|
||||
return res.status(422).json({
|
||||
error: true, message: 'مدارک شما تایید نشده است'
|
||||
})
|
||||
}
|
||||
// اعتبارسنجی درخواست
|
||||
|
||||
const errors = validationResult(req)
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(422).json({ errors: errors.array() })
|
||||
}
|
||||
const { request_time, offer_price, project_id } = req.body
|
||||
if (!project_id || !offer_price || !request_time
|
||||
) {
|
||||
|
||||
const {
|
||||
request_time,
|
||||
offer_price,
|
||||
project_id,
|
||||
role_id,
|
||||
portfolio
|
||||
} = req.body
|
||||
|
||||
if (!project_id || !offer_price || !request_time) {
|
||||
return res.status(422).json({
|
||||
error: true,
|
||||
message: 'اطلاعات ارسالی اشتباه است'
|
||||
@@ -62,26 +102,48 @@ const requestProject = async (req, res, next) => {
|
||||
})
|
||||
}
|
||||
|
||||
// چک کردن برای وجود درخواست قبلی
|
||||
const existingRequest = await RequestModel.findOne({ project: project_id, user: user._id })
|
||||
const existingRequest = await RequestModel.findOne({
|
||||
project: project_id,
|
||||
user: user._id,
|
||||
...(role_id ? { role_id } : {})
|
||||
})
|
||||
if (existingRequest) {
|
||||
return res.status(422).json({
|
||||
error: true,
|
||||
message: 'شما قبلاً برای این پروژه درخواست دادهاید'
|
||||
})
|
||||
}
|
||||
// ایجاد یک درخواست جدید
|
||||
|
||||
let selectedRole = null
|
||||
if (role_id && Array.isArray(project.roles) && project.roles.length) {
|
||||
selectedRole = project.roles.id(role_id) ||
|
||||
project.roles.find((r) => String(r._id) === String(role_id))
|
||||
}
|
||||
if (!selectedRole && Array.isArray(project.roles) && project.roles.length === 1) {
|
||||
selectedRole = project.roles[0]
|
||||
}
|
||||
|
||||
const portfolioRequired = Boolean(selectedRole?.portfolio_required)
|
||||
const portfolioFiles = await savePortfolioFiles(portfolio, user._id)
|
||||
if (portfolioRequired && portfolioFiles.length < 1) {
|
||||
return res.status(422).json({
|
||||
error: true,
|
||||
message: 'ارسال حداقل یک نمونه کار الزامی است'
|
||||
})
|
||||
}
|
||||
|
||||
const request = new RequestModel({
|
||||
project: project_id,
|
||||
user: user._id,
|
||||
time: request_time,
|
||||
price: offer_price
|
||||
price: offer_price,
|
||||
role_id: selectedRole?._id || null,
|
||||
role_expertise: selectedRole?.expertise || project.expertise || null,
|
||||
portfolio: portfolioFiles
|
||||
})
|
||||
await request.save()
|
||||
|
||||
// ثبت اعلان برای سازنده پروژه
|
||||
const creatorId = project.creator_id
|
||||
|
||||
const notification = new NotificationModel({
|
||||
user_id: creatorId,
|
||||
project_post_id: project_id,
|
||||
@@ -91,35 +153,6 @@ const requestProject = async (req, res, next) => {
|
||||
})
|
||||
await notification.save()
|
||||
|
||||
const userReciver = await UserModel.findById(project.creator_id)
|
||||
|
||||
const data = JSON.stringify({
|
||||
mobile: userReciver?.mobile,
|
||||
templateId: '876533',
|
||||
parameters: [
|
||||
{ name: 'EMPLOYER', value: userReciver?.first_name + ' ' + userReciver?.last_name },
|
||||
{ name: 'PROJECT', value: project?.title }
|
||||
]
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
// به روز رسانی پروژه با افزودن کاربر به لیست کاربران درخواست دهنده
|
||||
const updatedProject = await ProjectModel.findOneAndUpdate(
|
||||
{ _id: project_id },
|
||||
{ $push: { requested_users: user._id } },
|
||||
@@ -131,8 +164,7 @@ const requestProject = async (req, res, next) => {
|
||||
return res.status(404).json({ error: true, message: 'پروژه پیدا نشد' })
|
||||
}
|
||||
|
||||
res.status(201).json({ message: 'درخواست با موفقیت شد', id: project_id }
|
||||
)
|
||||
res.status(201).json({ message: 'درخواست با موفقیت شد', id: project_id })
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
@@ -149,38 +181,34 @@ const editRequestProject = async (req, res, next) => {
|
||||
error: true, message: 'شما دسترسی به این بخش ندارید'
|
||||
})
|
||||
}
|
||||
if (user.is_verified !== 'verified') {
|
||||
return res.status(422).json({
|
||||
error: true, message: 'مدارک شما تایید نشده است'
|
||||
})
|
||||
}
|
||||
// اعتبارسنجی درخواست
|
||||
|
||||
const errors = validationResult(req)
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(422).json({ errors: errors.array() })
|
||||
}
|
||||
const { request_time, offer_price, project_id } = req.body
|
||||
if (!project_id || !offer_price || !request_time
|
||||
) {
|
||||
if (!project_id || !offer_price || !request_time) {
|
||||
return res.status(422).json({
|
||||
error: true,
|
||||
message: 'اطلاعات ارسالی اشتباه است'
|
||||
})
|
||||
}
|
||||
// چک کردن برای وجود درخواست قبلی
|
||||
const existingRequest = await RequestModel.findOne({ project: project_id, user: user._id })
|
||||
|
||||
const existingRequest = await RequestModel.findOne({
|
||||
project: project_id,
|
||||
user: user._id
|
||||
})
|
||||
if (!existingRequest) {
|
||||
return res.status(422).json({
|
||||
error: true,
|
||||
message: 'شما قبلاً برای این پروژه درخواست ندادهاید'
|
||||
})
|
||||
}
|
||||
// اکنون میتوانید اطلاعات درخواست را ویرایش کنید
|
||||
|
||||
existingRequest.time = request_time
|
||||
existingRequest.price = offer_price
|
||||
await existingRequest.save()
|
||||
|
||||
// ثبت اعلان برای سازنده پروژه
|
||||
const project = await ProjectModel.findById(project_id)
|
||||
const creatorId = project.creator_id
|
||||
|
||||
@@ -200,6 +228,7 @@ const editRequestProject = async (req, res, next) => {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const acceptProject = async (req, res, next) => { }
|
||||
module.exports = {
|
||||
requestProjectValidationRules,
|
||||
|
||||
@@ -3,18 +3,18 @@ const ProjectModel = require('../../../models/ProjectModel')
|
||||
const jwt = require('jsonwebtoken')
|
||||
const UserModel = require('../../../models/UserModel')
|
||||
const { check, validationResult } = require('express-validator')
|
||||
const { ProvinceModel, CityModel } = require('../../../models/StateCity')
|
||||
const { CountryModel, ProvinceModel, CityModel } = require('../../../models/StateCity')
|
||||
const NotificationModel = require('../../../models/NotificationModel')
|
||||
const CommentModel = require('../../../models/CommentModel')
|
||||
const {
|
||||
resolveRatingForComment,
|
||||
recalculateUserRating
|
||||
} = require('../../../utils/commentRating')
|
||||
const { saveCommentWithModerationStatus } = require('../../../utils/commentStatusSave')
|
||||
const {
|
||||
canCreateProject,
|
||||
respondProjectProfileIncomplete
|
||||
} = require('../../../utils/projectProfile')
|
||||
|
||||
const saveProjectComment = async ({
|
||||
targetUserId,
|
||||
projectId,
|
||||
@@ -37,16 +37,18 @@ const saveProjectComment = async ({
|
||||
return { error: { status: 400, message: 'Rating must be between 1 and 5' } }
|
||||
}
|
||||
|
||||
const newComment = new CommentModel({
|
||||
user: targetUserId,
|
||||
project: projectId,
|
||||
creator: creatorId,
|
||||
rating: ratingValue,
|
||||
comment,
|
||||
comment_for: commentFor,
|
||||
status: 'pending'
|
||||
})
|
||||
await newComment.save()
|
||||
const newComment = await saveCommentWithModerationStatus(
|
||||
CommentModel,
|
||||
{
|
||||
user: targetUserId,
|
||||
project: projectId,
|
||||
creator: creatorId,
|
||||
rating: ratingValue,
|
||||
comment,
|
||||
comment_for: commentFor,
|
||||
},
|
||||
'comment'
|
||||
)
|
||||
|
||||
if (isNewRating && commentFor === 'user') {
|
||||
await recalculateUserRating(targetUserId)
|
||||
@@ -139,6 +141,10 @@ const doneProjectWeb = async (req, res, next) => {
|
||||
return res.status(403).send({ message: 'Access denied: You are not the creator of this project.' })
|
||||
}
|
||||
|
||||
if (project.selected_user && Number(project.final_price) > 0 && !project.completionShareCredited) {
|
||||
return res.status(422).send({ message: 'ابتدا باید مبلغ باقیمانده پروژه را پرداخت کنید' })
|
||||
}
|
||||
|
||||
project.status = 'done'
|
||||
await project.save()
|
||||
|
||||
@@ -218,36 +224,21 @@ const cancleProject = async (req, res, next) => {
|
||||
const editProjectValidationRules = () => {
|
||||
return [
|
||||
check('title').notEmpty().withMessage('عنوان نمیتواند خالی باشد'),
|
||||
|
||||
check('expertise').notEmpty().withMessage('تخصص نمیتواند خالی باشد'),
|
||||
|
||||
check('gender')
|
||||
.isIn(['male', 'female'])
|
||||
.withMessage('جنسیت باید male یا female باشد'),
|
||||
|
||||
check('age').notEmpty().withMessage('سن نمیتواند خالی باشد'),
|
||||
|
||||
check('conversation_projects')
|
||||
.notEmpty()
|
||||
.withMessage('پروژههای مکالمه نمیتواند خالی باشد')
|
||||
.isBoolean()
|
||||
.withMessage('مقدار پروژههای مکالمه باید یک مقدار boolean باشد'),
|
||||
|
||||
check('province').notEmpty().withMessage('استان نمیتواند خالی باشد'),
|
||||
|
||||
check('city').notEmpty().withMessage('شهر نمیتواند خالی باشد'),
|
||||
|
||||
check('offer_time')
|
||||
.notEmpty()
|
||||
.withMessage('زمان پروژه نمیتواند خالی باشد'),
|
||||
|
||||
check('offer_price')
|
||||
.notEmpty()
|
||||
.withMessage('قیمت پیشنهادی نمیتواند خالی باشد'),
|
||||
|
||||
check('description')
|
||||
.notEmpty()
|
||||
.withMessage('توضیحات نمیتواند خالی باشد')
|
||||
.withMessage('توضیحات نمیتواند خالی باشد'),
|
||||
check('gender')
|
||||
.optional({ nullable: true, checkFalsy: true })
|
||||
.isIn(['male', 'female', 'any'])
|
||||
.withMessage('جنسیت باید male یا female یا any باشد')
|
||||
]
|
||||
}
|
||||
|
||||
@@ -279,13 +270,28 @@ const editProject = async (req, res, next) => {
|
||||
sub_expertise,
|
||||
gender,
|
||||
age,
|
||||
age_min,
|
||||
age_max,
|
||||
conversation_projects,
|
||||
country,
|
||||
province,
|
||||
city,
|
||||
lat,
|
||||
lng,
|
||||
neighborhood,
|
||||
address,
|
||||
offer_time,
|
||||
offer_price,
|
||||
description,
|
||||
projectId
|
||||
projectId,
|
||||
roles,
|
||||
payment_method,
|
||||
start_date,
|
||||
work_hours_start,
|
||||
work_hours_end,
|
||||
collaboration_type,
|
||||
experience,
|
||||
project_type
|
||||
} = req.body
|
||||
|
||||
const project = await ProjectModel.findById(projectId)
|
||||
@@ -302,18 +308,36 @@ const editProject = async (req, res, next) => {
|
||||
message: 'شما نمیتوانید این پروژه را ویرایش کنید'
|
||||
})
|
||||
}
|
||||
if (project.status !== 'paid' && project.status !== 'pre_payment') {
|
||||
if (project.status !== 'paid' && project.status !== 'temp_accept' && project.status !== 'pre_payment') {
|
||||
return res.status(422).json({
|
||||
error: true,
|
||||
message: 'شما نمیتوانید این پروژه را ویرایش کنید'
|
||||
})
|
||||
}
|
||||
// اعتبارسنجی اطلاعات ویرایش شده
|
||||
|
||||
const normalizedRoles = Array.isArray(roles)
|
||||
? roles.slice(0, 5).map((role) => ({
|
||||
expertise: role.expertise,
|
||||
sub_expertise: Array.isArray(role.sub_expertise) ? role.sub_expertise : [],
|
||||
number_of_person: Number(role.number_of_person || 1) || 1,
|
||||
height_min: role.height_min ?? null,
|
||||
height_max: role.height_max ?? null,
|
||||
weight_min: role.weight_min ?? null,
|
||||
weight_max: role.weight_max ?? null,
|
||||
size_min: role.size_min ?? null,
|
||||
size_max: role.size_max ?? null,
|
||||
eye_color: role.eye_color || null,
|
||||
hair_color: role.hair_color || null,
|
||||
portfolio_required: Boolean(role.portfolio_required)
|
||||
})).filter((role) => role.expertise)
|
||||
: null
|
||||
|
||||
const primaryExpertise =
|
||||
expertise || (normalizedRoles && normalizedRoles[0] && normalizedRoles[0].expertise)
|
||||
|
||||
if (
|
||||
!title ||
|
||||
!expertise ||
|
||||
!age ||
|
||||
conversation_projects === null ||
|
||||
!primaryExpertise ||
|
||||
!province ||
|
||||
!city ||
|
||||
!offer_time ||
|
||||
@@ -325,24 +349,50 @@ const editProject = async (req, res, next) => {
|
||||
message: 'اطلاعات ارسالی اشتباه است'
|
||||
})
|
||||
}
|
||||
const countryFind = country
|
||||
? await CountryModel.findOne({ id: country }, { _id: 0 })
|
||||
: null
|
||||
const provinceFind = await ProvinceModel.findOne(
|
||||
{ id: province },
|
||||
{ _id: 0 }
|
||||
)
|
||||
const cityFind = await CityModel.findOne({ id: city })
|
||||
// ویرایش یک نسخه از پروژه
|
||||
|
||||
project.title = title
|
||||
project.expertise = expertise
|
||||
project.sub_expertise = sub_expertise
|
||||
project.gender = gender
|
||||
project.age = age
|
||||
project.conversation_projects = conversation_projects
|
||||
project.expertise = primaryExpertise
|
||||
project.sub_expertise =
|
||||
sub_expertise ||
|
||||
(normalizedRoles && normalizedRoles[0] && normalizedRoles[0].sub_expertise) ||
|
||||
[]
|
||||
project.gender = gender || 'any'
|
||||
project.age = age || 'all'
|
||||
if (age_min != null && age_min !== '') project.age_min = Number(age_min)
|
||||
if (age_max != null && age_max !== '') project.age_max = Number(age_max)
|
||||
project.conversation_projects =
|
||||
conversation_projects === undefined || conversation_projects === null
|
||||
? true
|
||||
: conversation_projects
|
||||
if (countryFind) {
|
||||
project.country = { id: countryFind.id, name: countryFind.name_fa, iso2: countryFind.iso2, slug: countryFind.slug }
|
||||
}
|
||||
project.province = provinceFind
|
||||
project.city = cityFind
|
||||
if (lat != null && lat !== '') project.lat = Number(lat)
|
||||
if (lng != null && lng !== '') project.lng = Number(lng)
|
||||
project.neighborhood = neighborhood || null
|
||||
project.address = address || null
|
||||
project.offer_time = offer_time
|
||||
project.offer_price = offer_price
|
||||
project.description = description
|
||||
// ذخیره کردن تغییرات در پایگاه داده
|
||||
if (payment_method) project.payment_method = payment_method
|
||||
project.start_date = start_date || null
|
||||
project.work_hours_start = work_hours_start || null
|
||||
project.work_hours_end = work_hours_end || null
|
||||
if (collaboration_type) project.collaboration_type = collaboration_type
|
||||
project.experience = experience || null
|
||||
if (normalizedRoles) project.roles = normalizedRoles
|
||||
if (project_type) project.project_type = project_type
|
||||
|
||||
await project.save()
|
||||
res.status(200).json({
|
||||
message: 'پروژه با موفقیت ویرایش شد',
|
||||
|
||||
Reference in New Issue
Block a user