feat(automations): add run-now test endpoint and sends log table
Enable immediate automation test runs without waiting for the scheduled hour, expose send history for debugging, and return all SuperAdmin overlay keys on refresh instead of only enable_automation. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -9,6 +9,7 @@ use App\Models\Product;
|
||||
use App\Services\ConfigService;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
||||
|
||||
class AutomationEngine
|
||||
{
|
||||
@@ -87,6 +88,53 @@ class AutomationEngine
|
||||
return $this->conditions->apply($this->conditions->baseQuery(), $conditions)->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* اجرای فوری برای تست ادمین — بدون محدودیت ساعت، بدون touch کردن last_sms_run_at.
|
||||
*
|
||||
* @return array{audience: int, sent: int, failed: int, skipped: int}
|
||||
*/
|
||||
public function runNow(Automation $auto, bool $force = true): array
|
||||
{
|
||||
if (! app(ConfigService::class)->isAutomationEnabled()) {
|
||||
throw new UnprocessableEntityHttpException('قابلیت اتوماسیون خاموش است.');
|
||||
}
|
||||
if (! $auto->is_active) {
|
||||
throw new UnprocessableEntityHttpException('اتوماسیون غیرفعال است.');
|
||||
}
|
||||
if (! $this->inWindow($auto)) {
|
||||
throw new UnprocessableEntityHttpException('اتوماسیون خارج از بازهٔ شروع/پایان است.');
|
||||
}
|
||||
|
||||
$stats = ['audience' => 0, 'sent' => 0, 'failed' => 0, 'skipped' => 0];
|
||||
$audienceQuery = $this->conditions->apply(
|
||||
$this->conditions->baseQuery(),
|
||||
$auto->conditions ?? []
|
||||
);
|
||||
$stats['audience'] = (int) $audienceQuery->count();
|
||||
|
||||
foreach ($this->channels($auto) as $channel) {
|
||||
$this->conditions
|
||||
->apply($this->conditions->baseQuery(), $auto->conditions ?? [])
|
||||
->select(['id', 'name', 'fname', 'lname', 'mobile', 'type', 'is_archive'])
|
||||
->chunkById(80, function ($customers) use ($auto, $channel, $force, &$stats) {
|
||||
foreach ($customers as $customer) {
|
||||
if (! $force && ! $this->sender->maySend($auto, $customer, $channel)) {
|
||||
$stats['skipped']++;
|
||||
|
||||
continue;
|
||||
}
|
||||
if ($this->sender->send($auto, $customer, $channel, $force)) {
|
||||
$stats['sent']++;
|
||||
} else {
|
||||
$stats['failed']++;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
private function tryPriceFire(Automation $auto, array $payload): void
|
||||
{
|
||||
if (! $this->inWindow($auto)) {
|
||||
|
||||
@@ -21,9 +21,9 @@ class AutomationSender
|
||||
) {
|
||||
}
|
||||
|
||||
public function send(Automation $auto, Customer $customer, string $channel): bool
|
||||
public function send(Automation $auto, Customer $customer, string $channel, bool $force = false): bool
|
||||
{
|
||||
if (! $this->maySend($auto, $customer, $channel)) {
|
||||
if (! $this->maySend($auto, $customer, $channel, $force)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -71,12 +71,16 @@ class AutomationSender
|
||||
}
|
||||
}
|
||||
|
||||
public function maySend(Automation $auto, Customer $customer, string $channel): bool
|
||||
public function maySend(Automation $auto, Customer $customer, string $channel, bool $force = false): bool
|
||||
{
|
||||
if (! $auto->usesChannel($channel)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($force) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$max = $auto->max_sends;
|
||||
if ($max !== null && (int) $max > 0) {
|
||||
$count = AutomationSend::query()
|
||||
|
||||
@@ -86,7 +86,7 @@ class SuperAdminProgramSettingsOverlay
|
||||
/**
|
||||
* کش را پاک میکند و دوباره از سوپرادمین میخواند.
|
||||
*
|
||||
* @return array{ok: bool, count: int, enable_automation: string|null, source: string}
|
||||
* @return array{ok: bool, count: int, items: array<string, string>, source: string}
|
||||
*/
|
||||
public function refreshFromSuperAdmin(): array
|
||||
{
|
||||
@@ -96,17 +96,27 @@ class SuperAdminProgramSettingsOverlay
|
||||
return [
|
||||
'ok' => false,
|
||||
'count' => 0,
|
||||
'enable_automation' => null,
|
||||
'items' => [],
|
||||
'source' => 'unavailable',
|
||||
];
|
||||
}
|
||||
|
||||
$auto = $map['enable_automation']['value'] ?? null;
|
||||
$items = [];
|
||||
foreach ($map as $key => $row) {
|
||||
if (! is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
$value = $row['value'] ?? null;
|
||||
if ($value === null || $value === '') {
|
||||
continue;
|
||||
}
|
||||
$items[(string) $key] = (string) $value;
|
||||
}
|
||||
|
||||
return [
|
||||
'ok' => true,
|
||||
'count' => count($map),
|
||||
'enable_automation' => $auto === null || $auto === '' ? null : (string) $auto,
|
||||
'count' => count($items),
|
||||
'items' => $items,
|
||||
'source' => 'superadmin',
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user