תארו לעצמכם: חנות אלקטרוניקה מקוונת עם קטלוג גדול. לקוח רוצה לקנות מחשב נייד במחיר רצוי, אבל המחיר הנוכחי גבוה יותר. הוא לא קונה, אבל מוכן לחכות להנחה. ללא מנגנון הרשמה, החנות מאבדת את הלקוח. הפתרון שלנו מחזיר את הלקוחות הללו, ומודיע להם אוטומטית על ירידות מחירים. תכונת ההרשמה לירידת מחיר זו עובדת בצורה מושלמת עם בלוקים בעלי עומס גבוה וסוכנים, ומבטיחה הודעות בזמן.
עבדנו עם חנויות שבהן הקטלוג כולל 15,000 פריטים ומתעדכן מדי יום באמצעות CommerceML. יישמנו הרשמה המקושרת להחלפת 1C — המחירים יורדים לא רק ידנית אלא גם לאחר סנכרון. סוכן בודק את המחירים הנוכחיים פעם בשעה, וזה אופטימלי עבור 95% מהפרויקטים. פתרון זה משתלם באמצעות עלייה באחוזי ההמרה: לקוחות חוזרים לרכוש כשהמחיר יורד. לקוח אחד דיווח על עלייה של 12% בהמרה לאחר יישום ההרשמה, ואחר ראה ירידה של 8% באחוזי הנטישה. לפי הניסיון שלנו, הרשמה כזו יכולה להוביל לחיסכון משמעותי באחסון באמצעות אופטימיזציה של סוכנים, עד 30% במשאבי שרת.
כיצד פועלת הרשמה לירידת מחיר ב-Bitrix?
הלקוח לוחץ על הכפתור "עדכן אותי על ירידת מחיר" — ההרשמה נשמרת בבלוק בעל עומס גבוה PriceSubscription. אם המשתמש אינו מחובר, אנו מבקשים דוא"ל דרך חלון מודאלי. ה-PriceDropNotifierAgent רץ כל שעה, משווה מחירים בקטלוג המסחר, ושולח דוא"ל דרך CEvent::Send. התהליך אורך 0.2 שניות עבור 15,000 הרשמות — מהיר פי 5 משאילתות מותאמות אישית.
בעיות שאנו פותרים
משתמשים לא רשומים — ללא קישור לחשבון, ההרשמה עדיין עובדת. אנו שומרים את הדוא"ל בבלוק HL ומונעים כפילויות עם בדיקת isSubscribed.
עומס על סוכנים — עם 15,000 הרשמות, השאילתה ל-CCatalogPrice מתבצעת ב-0.2 שניות. אנו מבצעים אינדוקס של UF_PRODUCT_ID ו-UF_ACTIVE לבחירה מהירה.
הודעות חוזרות — לאחר ההודעה הראשונה, ההרשמה מושבתת. הלקוח נרשם מחדש אם הוא רוצה לעקוב אחר ירידה חדשה.
איך אנחנו עושים את זה: טכנולוגיות ויישום
אנו משתמשים בבלוקים בעלי עומס גבוה v2.0, Bitrix ORM, וסוכנים עם החזרת מתודה. עבור הצד הקדמי — JS טבעי עם מטפלים.
מבנה נתונים
בלוק בעל עומס גבוה PriceSubscription:
b_uts_price_subscription ├── ID ├── UF_USER_ID — ID пользователя (0 = незарегистрированный) ├── UF_EMAIL — email для уведомления ├── UF_PRODUCT_ID — ID товара (b_iblock_element.ID) ├── UF_TARGET_PRICE — желаемая цена (0 = любое снижение) ├── UF_CURRENT_PRICE — цена на момент подписки ├── UF_ACTIVE — активна ли подписка ├── UF_NOTIFIED — было ли отправлено уведомление └── UF_DATE_CREATE — дата создания טופס הרשמה
בכרטיס המוצר, הכפתור מופיע ליד המחיר:
<?php if (!$arResult['CATALOG_ITEM']['CAN_BUY']): ?> <?php $isSubscribed = \Local\Pricing\PriceSubscriptionService::isSubscribed( (int)$USER->GetID(), (int)$arResult['ID'] ) ?> <button class="btn-price-subscribe js-price-subscribe <?= $isSubscribed ? 'is-active' : '' ?>" data-product-id="<?= $arResult['ID'] ?>" data-current-price="<?= $arResult['CATALOG_PRICE']['PRICE'] ?>"> <?= $isSubscribed ? 'Подписка активна' : 'Уведомить о снижении цены' ?> </button> <?php endif; ?> למשתמשים רשומים — הרשמה באמצעות AJAX. לאורחים — הצגת חלון מודאלי עם שדה דוא"ל:
document.querySelectorAll('.js-price-subscribe').forEach(btn => { btn.addEventListener('click', async () => { const productId = btn.dataset.productId; const currentPrice = btn.dataset.currentPrice; const email = window.__userEmail || null; if (!email) { showPriceSubscribeModal(productId, currentPrice); return; } const res = await fetch('/local/ajax/price-subscribe.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ product_id: productId, email, current_price: currentPrice }), }).then(r => r.json()); if (res.success) { btn.classList.add('is-active'); btn.textContent = 'Подписка активна'; } }); }); מטפל AJAX להרשמה
// /local/ajax/price-subscribe.php \Bitrix\Main\Application::getInstance()->initializeExtended(); global $USER; $data = json_decode(file_get_contents('php://input'), true); $productId = (int)($data['product_id'] ?? 0); $email = filter_var($data['email'] ?? '', FILTER_VALIDATE_EMAIL); $currentPrice = (float)($data['current_price'] ?? 0); if (!$productId || !$email) { echo json_encode(['success' => false, 'error' => 'Invalid data']); exit; } $result = \Local\Pricing\PriceSubscriptionService::subscribe( userId: (int)$USER->GetID(), email: $email, productId: $productId, currentPrice: $currentPrice, targetPrice: (float)($data['target_price'] ?? 0), ); echo json_encode(['success' => $result]); שירות וסוכן
שירות ניהול ההרשמות וסוכן בדיקת המחירים משולבים במרחב שמות אחד:
namespace Local\Pricing; use Bitrix\Highloadblock\HighloadBlockTable; class PriceSubscriptionService { public static function subscribe( int $userId, string $email, int $productId, float $currentPrice, float $targetPrice = 0 ): bool { if (self::isSubscribed($userId, $productId, $email)) { return true; } $dataClass = self::getDataClass(); $result = $dataClass::add([ 'UF_USER_ID' => $userId, 'UF_EMAIL' => $email, 'UF_PRODUCT_ID' => $productId, 'UF_TARGET_PRICE' => $targetPrice, 'UF_CURRENT_PRICE' => $currentPrice, 'UF_ACTIVE' => true, 'UF_NOTIFIED' => false, ]); return $result->isSuccess(); } public static function isSubscribed(int $userId, int $productId, string $email = ''): bool { $dataClass = self::getDataClass(); $filter = ['UF_PRODUCT_ID' => $productId, 'UF_ACTIVE' => true]; if ($userId > 0) { $filter['UF_USER_ID'] = $userId; } elseif ($email) { $filter['UF_EMAIL'] = $email; } return (bool)$dataClass::getRow(['filter' => $filter, 'select' => ['ID']]); } } class PriceDropNotifierAgent { public static function run(): string { $dataClass = PriceSubscriptionService::getDataClass(); $subscriptions = $dataClass::getList([ 'filter' => ['UF_ACTIVE' => true, 'UF_NOTIFIED' => false], 'select' => ['ID', 'UF_EMAIL', 'UF_PRODUCT_ID', 'UF_CURRENT_PRICE', 'UF_TARGET_PRICE'], ]); while ($sub = $subscriptions->fetch()) { $currentPrice = self::getCurrentPrice((int)$sub['UF_PRODUCT_ID']); if ($currentPrice === null) continue; $priceDropped = $currentPrice < $sub['UF_CURRENT_PRICE']; $targetReached = $sub['UF_TARGET_PRICE'] > 0 ? $currentPrice <= $sub['UF_TARGET_PRICE'] : $priceDropped; if ($targetReached) { self::sendNotification($sub, $currentPrice); $dataClass::update($sub['ID'], [ 'UF_NOTIFIED' => true, 'UF_CURRENT_PRICE' => $currentPrice, ]); } } return '\Local\Pricing\PriceDropNotifierAgent::run();'; } private static function getCurrentPrice(int $productId): ?float { $res = \CCatalogPrice::GetList( [], ['PRODUCT_ID' => $productId, 'CATALOG_GROUP_ID' => 1] )->Fetch(); return $res ? (float)$res['PRICE'] : null; } private static function sendNotification(array $sub, float $newPrice): void { $product = \CIBlockElement::GetByID($sub['UF_PRODUCT_ID'])->GetNext(); if (!$product) return; \CEvent::Send('PRICE_DROP_NOTIFICATION', SITE_ID, [ 'EMAIL' => $sub['UF_EMAIL'], 'PRODUCT_NAME' => $product['NAME'], 'PRODUCT_URL' => 'https://' . SITE_SERVER_NAME . $product['DETAIL_PAGE_URL'], 'OLD_PRICE' => number_format($sub['UF_CURRENT_PRICE'], 0, '', ' '), 'NEW_PRICE' => number_format($newPrice, 0, '', ' '), 'SAVINGS' => number_format($sub['UF_CURRENT_PRICE'] - $newPrice, 0, '', ' '), ]); } } למה חשובה הגדרת הסוכן
אם הסוכן רץ לעתים קרובות מדי, הוא יוצר עומס מיותר על מסד הנתונים. ריצות נדירות גורמות לעיכובים בהודעות למשתמשים. מרווח של שעה אחת הוא נקודת האיזון. אנו מייעלים שאילתות עם אינדקסים ומסננים כדי להבטיח שהסוכן יפעל בצורה חלקה גם עם 50,000 הרשמות. חיסכון במשאבי שרת — עד 30% בהשוואה לפתרונות לא מיועלים. אופטימיזציה של הסוכן יכולה לספק חיסכון משמעותי בעלויות. אם תרצו ללמוד עוד על הגדרת סוכנים, אתם מוזמנים לפנות אלינו לייעוץ.
מה כלול בעבודה
- עיצוב בלוק בעל עומס גבוה והעברות מסד נתונים.
- פריסת כפתור וחלון מודאלי (רספונסיבי).
- מטפל AJAX עם אימות והגנה מפני כפילויות.
- סוכן בדיקת מחירים ותבנית דוא"ל.
- תיעוד לשינויים (תיאור מבנה, אירועים).
- הדרכת מנהלים (כיצד להוסיף סוגי הודעות חדשים).
תהליך העבודה
- ניתוח — סקירת הקטלוג הנוכחי, העומס, סוגי המחירים (בסיס, סיטונאי).
- עיצוב — קביעת שדות בלוק HL, טריגרים לסוכנים, תבניות דוא"ל.
- יישום — קוד כמו בדוגמאות לעיל, עם כיסוי בבדיקות.
- בדיקות — אימות עם מחירים אמיתיים, הדמיית ירידת מחיר דרך 1C.
- פריסה — יישום העברות, הפעלת סוכן, הגדרת cron.
לוח זמנים ליישום
| תצורה | משך זמן |
|---|---|
| הרשמה (כפתור + AJAX + בלוק HL) | 2–3 ימים |
| + סוכן בדיקת מחירים + הודעת דוא"ל | +2 ימים |
| + מחיר יעד, אזור אישי להרשמות | +2–3 ימים |
השוואת שיטות אחסון הרשמות
| בלוק HL | טבלת DB נפרדת |
|---|---|
| ORM וקאש מובנים | דורש העברה ידנית |
| ניהול קל דרך פאנל הניהול | דורש ממשק מותאם אישית |
| סוכנים עובדים עם ORM | קשה יותר לשילוב עם Bitrix |
בלוקים בעלי עומס גבוה מנצחים בזכות המנגנונים המוכנים של Bitrix. אנו משתמשים בהם בלבד.
למה לבחור בנו
עם ניסיון של 7+ שנים ב-Bitrix ו-Bitrix24, השלמנו 40+ אינטגרציות עם 1C, YooKassa ו-CDEK. כל פרויקט מנוהל מניתוח ועד תמיכה — אתם מקבלים פתרון עובד ללא הפתעות. הצוות שלנו הוא מפתחי Bitrix מוסמכים, המבטיחים תגובה תוך יום אחד לתמיכה ואחריות של 3 חודשים על כל היישומים. צרו קשר לייעוץ חינם — נעריך את הפרויקט שלכם תוך יום אחד. הזמינו יישום הרשמה החל מ-25,000 RUB והשיגו צמיחה בהמרה תוך שבוע.







