73 lines
4.4 KiB
JavaScript
73 lines
4.4 KiB
JavaScript
const mongoose = require('mongoose')
|
|
const timestamp = require('mongoose-timestamp')
|
|
const mongoosePaginate = require('mongoose-paginate-v2')
|
|
|
|
// تبلیغِ کاربری — نوعش تعیین میکنه محتواش کجا تزریق میشه: first_post (اول فید)،
|
|
// first_story (اول نوار استوری)، notification (پوش مستقیم به کاربرهای هدف)، project/
|
|
// product/shop/billboard (اول لیستِ مربوطه، از utils/adDelivery.js:findActiveAd با همون
|
|
// ad_type resolve میشه). هدفگذاری (لوکیشن/تخصص/جنسیت) روی همون فیلدهایی که
|
|
// User/Post/exploreAlgorithm.js از قبل استفاده میکنن سوار شده (province.id عددیه، نه
|
|
// ObjectId — utils/adPricing.js همین رو resolve میکنه)
|
|
const AD_TYPES = ['first_post', 'first_story', 'notification', 'project', 'product', 'shop', 'billboard']
|
|
|
|
const adSchema = new mongoose.Schema({
|
|
user_id: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
|
|
ad_type: { type: String, enum: AD_TYPES, required: true },
|
|
|
|
// محتوا — بسته به ad_type فقط یکی از این پر میشه
|
|
content_post_id: { type: mongoose.Schema.Types.ObjectId, ref: 'Post', default: null },
|
|
content_project_id: { type: mongoose.Schema.Types.ObjectId, ref: 'Project', default: null },
|
|
content_shop_product_id: { type: mongoose.Schema.Types.ObjectId, ref: 'ShopProductListing', default: null },
|
|
content_shop_id: { type: mongoose.Schema.Types.ObjectId, ref: 'Shop', default: null },
|
|
content_billboard_id: { type: mongoose.Schema.Types.ObjectId, ref: 'Advertising', default: null },
|
|
// استوریهای واقعی معمولاً ۲۴ساعته منقضی میشن (StoryModel: expires_at) و از فیدِ
|
|
// استوریِ معمولی حذف میشن، ولی یه کمپینِ تبلیغاتی میتونه چند روز فعال بمونه — پس
|
|
// مدیا/اورلیها رو موقعِ ساختِ تبلیغ اینجا کپی/اسنپشات میکنیم تا مستقل از عمرِ
|
|
// طبیعیِ خودِ استوری باشه (content_story_id فقط برای رفرنسِ منبع نگه داشته میشه)
|
|
content_story_id: { type: mongoose.Schema.Types.ObjectId, ref: 'Story', default: null },
|
|
content_media_path: { type: String, default: null },
|
|
content_media_type: { type: String, default: null },
|
|
content_overlays: { type: mongoose.Schema.Types.Mixed, default: null },
|
|
notification_title: { type: String, trim: true, default: null },
|
|
notification_body: { type: String, trim: true, default: null },
|
|
|
|
// هدفگذاری
|
|
target_country_id: { type: Number, default: null },
|
|
target_province_id: { type: Number, default: null },
|
|
target_city_id: { type: Number, default: null },
|
|
target_expertise: { type: String, trim: true, default: null },
|
|
target_sub_expertise: { type: [String], default: [] },
|
|
target_gender: { type: String, enum: ['female', 'male', 'any'], default: 'any' },
|
|
|
|
// مالی
|
|
price: { type: Number, required: true },
|
|
walletAmountUsed: { type: Number, default: 0 },
|
|
payment_authority: { type: String, default: null },
|
|
|
|
status: {
|
|
type: String,
|
|
enum: ['pending_payment', 'active', 'expired', 'failed'],
|
|
default: 'pending_payment'
|
|
},
|
|
started_at: { type: Date, default: null },
|
|
// عین بقیهی کمپینهای اپ، یه مدتزمانِ ثابت (AD_CAMPAIGN_DURATION_DAYS توی
|
|
// adsController.js) بعد از فعالشدن — بعدش دیگه توی تزریقِ فید/استوری/نوتیف لحاظ نمیشه
|
|
expires_at: { type: Date, default: null },
|
|
|
|
views_count: { type: Number, default: 0 },
|
|
clicks_count: { type: Number, default: 0 }
|
|
})
|
|
|
|
// تزریق (سرچِ یه تبلیغِ فعالِ منطبق روی فید/استوری) و لیستِ «تبلیغهای من» دو مصرفِ
|
|
// اصلیِ ایندکسن
|
|
adSchema.index({ status: 1, ad_type: 1, expires_at: 1 })
|
|
adSchema.index({ user_id: 1, createdAt: -1 })
|
|
|
|
adSchema.plugin(timestamp)
|
|
adSchema.plugin(mongoosePaginate)
|
|
const AdModel = mongoose.model('Ad', adSchema)
|
|
// روی خودِ مدل بهعنوان استاتیک — تا adsController.js/adsPanelController.js بدونِ
|
|
// require جدا به لیستِ معتبرِ ad_type دسترسی داشته باشن (module.exports همون مدله، نه یه آبجکت)
|
|
AdModel.AD_TYPES = AD_TYPES
|
|
module.exports = AdModel
|