fix channel price bug 2
This commit is contained in:
@@ -22,6 +22,8 @@ class PriceChannelBroadcastService
|
|||||||
|
|
||||||
private const SIGNATURE_CACHE_KEY = 'price_channel:last_signature';
|
private const SIGNATURE_CACHE_KEY = 'price_channel:last_signature';
|
||||||
|
|
||||||
|
private const SEND_LOCK_KEY = 'price_channel:send_lock';
|
||||||
|
|
||||||
private const SUPPORTED_MESSENGERS = ['bale', 'telegram', 'rubika'];
|
private const SUPPORTED_MESSENGERS = ['bale', 'telegram', 'rubika'];
|
||||||
|
|
||||||
/** وزن هر مثقال به گرم — ضریب تبدیل قیمت هر گرم به هر مثقال (مثقال = گرم × این عدد) */
|
/** وزن هر مثقال به گرم — ضریب تبدیل قیمت هر گرم به هر مثقال (مثقال = گرم × این عدد) */
|
||||||
@@ -48,60 +50,70 @@ class PriceChannelBroadcastService
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// شرط ۱ (ارزان): از آخرین «ارسال واقعی» حداقل price_channel_min_seconds گذشته باشد.
|
// قفل — دو فراخوان همزمان (مثلاً دو force_refresh پشتسرهم) را سریالی میکند تا
|
||||||
// چون فقط روی ارسال واقعی markSent میکنیم، یک بررسیِ بیارسال تایمر را ریست نمیکند
|
// هر دو قبل از ذخیرهٔ markSent/امضا، cooldown را «گذشته» و امضا را «قدیمی» نبینند و
|
||||||
// و ارسالِ بعدی را عقب نمیاندازد.
|
// پیام تکراری نفرستند. اگر قفل دست کسی است، همین حالا بیارسال برمیگردیم.
|
||||||
if (! $this->cooldownElapsed()) {
|
$lock = Cache::lock(self::SEND_LOCK_KEY, 30);
|
||||||
|
if (! $lock->get()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$products = $this->eligibleProducts();
|
try {
|
||||||
if ($products === []) {
|
// شرط ۱: از آخرین «ارسال واقعی» حداقل price_channel_min_seconds گذشته باشد.
|
||||||
Log::debug('PriceChannel: محصول واجد شرایطی نیست (active/قیمت/ساعت کاری)');
|
// چون فقط روی ارسال واقعی markSent میکنیم، یک بررسیِ بیارسال تایمر را ریست نمیکند.
|
||||||
return;
|
if (! $this->cooldownElapsed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$products = $this->eligibleProducts();
|
||||||
|
if ($products === []) {
|
||||||
|
Log::debug('PriceChannel: محصول واجد شرایطی نیست (active/قیمت/ساعت کاری)');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// شرط ۲: قیمتِ نمایشی نسبت به آخرین ارسال واقعاً تغییر کرده باشد؛ وگرنه چیزی نفرست
|
||||||
|
// و تایمر را هم دست نزن (تا سرِ اولین تغییرِ بعدی بلافاصله بفرستد).
|
||||||
|
$signature = $this->priceSignature($products);
|
||||||
|
if ($signature === Cache::get(self::SIGNATURE_CACHE_KEY)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$message = $this->buildMessage($products);
|
||||||
|
if ($message === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = [];
|
||||||
|
foreach ($targets as $messenger => $target) {
|
||||||
|
$results[$messenger] = $this->dispatchToMessenger($messenger, $target['token'], $target['chat_ids'], $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// فقط بعد از ارسال واقعی: تایمر cooldown و امضای قیمت بهروزرسانی میشوند
|
||||||
|
$this->markSent();
|
||||||
|
Cache::put(self::SIGNATURE_CACHE_KEY, $signature, now()->addDay());
|
||||||
|
|
||||||
|
Log::info('PriceChannel: قیمت به کانال ارسال شد', [
|
||||||
|
'targets' => array_keys($targets),
|
||||||
|
'products' => count($products),
|
||||||
|
'results' => $results,
|
||||||
|
]);
|
||||||
|
} finally {
|
||||||
|
$lock->release();
|
||||||
}
|
}
|
||||||
|
|
||||||
// شرط ۲: قیمت نسبت به آخرین ارسال واقعاً تغییر کرده باشد؛ وگرنه چیزی نفرست
|
|
||||||
// و تایمر را هم دست نزن (تا سرِ اولین تغییرِ بعدی بلافاصله بفرستد).
|
|
||||||
$signature = $this->priceSignature($products);
|
|
||||||
if ($signature === Cache::get(self::SIGNATURE_CACHE_KEY)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$message = $this->buildMessage($products);
|
|
||||||
if ($message === '') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$results = [];
|
|
||||||
foreach ($targets as $messenger => $target) {
|
|
||||||
$results[$messenger] = $this->dispatchToMessenger($messenger, $target['token'], $target['chat_ids'], $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// فقط بعد از ارسال واقعی: تایمر cooldown و امضای قیمت بهروزرسانی میشوند
|
|
||||||
$this->markSent();
|
|
||||||
Cache::put(self::SIGNATURE_CACHE_KEY, $signature, now()->addDay());
|
|
||||||
|
|
||||||
Log::info('PriceChannel: قیمت به کانال ارسال شد', [
|
|
||||||
'targets' => array_keys($targets),
|
|
||||||
'products' => count($products),
|
|
||||||
'results' => $results,
|
|
||||||
]);
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
Log::warning('PriceChannelBroadcast failed', ['message' => $e->getMessage()]);
|
Log::warning('PriceChannelBroadcast failed', ['message' => $e->getMessage()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** امضای قیمت محصولات واجد شرایط — برای تشخیص تغییر قیمت */
|
/**
|
||||||
|
* امضای قیمت — روی «بدنهٔ نمایشیِ» پیام (بدون هدر/ساعت) گرفته میشود تا دقیقاً همان چیزی
|
||||||
|
* که کاربر میبیند مبنای تشخیص تغییر باشد؛ اختلاف چند ریالیِ زیرِ آستانهٔ نمایش پیام تکراری نمیسازد.
|
||||||
|
*
|
||||||
|
* @param array<int, Product> $products
|
||||||
|
*/
|
||||||
private function priceSignature(array $products): string
|
private function priceSignature(array $products): string
|
||||||
{
|
{
|
||||||
$parts = [];
|
return md5($this->buildPriceBody($products));
|
||||||
foreach ($products as $product) {
|
|
||||||
$parts[] = $product->id . ':' . (int) ($product->price ?? 0)
|
|
||||||
. ':' . ((int) ($product->open_buy)) . ((int) ($product->open_sell));
|
|
||||||
}
|
|
||||||
|
|
||||||
return md5(implode('|', $parts));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -232,6 +244,23 @@ class PriceChannelBroadcastService
|
|||||||
public function buildMessage(?array $products = null): string
|
public function buildMessage(?array $products = null): string
|
||||||
{
|
{
|
||||||
$products = $products ?? $this->eligibleProducts();
|
$products = $products ?? $this->eligibleProducts();
|
||||||
|
$body = $this->buildPriceBody($products);
|
||||||
|
if ($body === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return trim($this->header() . "\n\n" . $body);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* بدنهٔ قیمت (بدون هدر/ساعت) — همان چیزی که «تغییر قیمت» را میسازد.
|
||||||
|
* امضای تشخیص تغییر روی همین بدنهی نمایشیِ رُندشده گرفته میشود تا اختلاف چند ریالی
|
||||||
|
* که به یک عدد نمایشیِ یکسان میرسد، پیام تکراری نفرستد.
|
||||||
|
*
|
||||||
|
* @param array<int, Product> $products
|
||||||
|
*/
|
||||||
|
private function buildPriceBody(array $products): string
|
||||||
|
{
|
||||||
if ($products === []) {
|
if ($products === []) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@@ -242,8 +271,6 @@ class PriceChannelBroadcastService
|
|||||||
$mesghal = $this->changeMesghal();
|
$mesghal = $this->changeMesghal();
|
||||||
|
|
||||||
$lines = [];
|
$lines = [];
|
||||||
$lines[] = $this->header();
|
|
||||||
$lines[] = '';
|
|
||||||
|
|
||||||
foreach ($products as $product) {
|
foreach ($products as $product) {
|
||||||
$base = (float) ($product->price ?? 0);
|
$base = (float) ($product->price ?? 0);
|
||||||
|
|||||||
Reference in New Issue
Block a user