update
This commit is contained in:
164
src/Modules/Automation/OrderStatusAgedService.php
Normal file
164
src/Modules/Automation/OrderStatusAgedService.php
Normal file
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Modules\Automation;
|
||||
|
||||
use PDO;
|
||||
use Throwable;
|
||||
|
||||
final class OrderStatusAgedService
|
||||
{
|
||||
private const MAX_ORDERS_PER_RULE = 100;
|
||||
|
||||
public function __construct(
|
||||
private readonly AutomationRepository $repository,
|
||||
private readonly AutomationService $automation,
|
||||
private readonly PDO $db
|
||||
) {
|
||||
}
|
||||
|
||||
public function scan(): int
|
||||
{
|
||||
$rules = $this->repository->findActiveByEvent('order.status_aged');
|
||||
if ($rules === []) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$totalTriggered = 0;
|
||||
|
||||
foreach ($rules as $rule) {
|
||||
try {
|
||||
$totalTriggered += $this->processRule($rule);
|
||||
} catch (Throwable) {
|
||||
// Blad jednej reguly nie blokuje kolejnych
|
||||
}
|
||||
}
|
||||
|
||||
return $totalTriggered;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $rule
|
||||
*/
|
||||
private function processRule(array $rule): int
|
||||
{
|
||||
$conditions = is_array($rule['conditions'] ?? null) ? $rule['conditions'] : [];
|
||||
$statusCodes = $this->extractStatusCodes($conditions);
|
||||
$days = $this->extractDays($conditions);
|
||||
|
||||
if ($statusCodes === [] || $days < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$orders = $this->findAgedOrders($statusCodes, $days);
|
||||
$triggered = 0;
|
||||
|
||||
foreach ($orders as $order) {
|
||||
$orderId = (int) ($order['id'] ?? 0);
|
||||
if ($orderId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$currentStatus = strtolower(trim((string) ($order['external_status_id'] ?? '')));
|
||||
$lastChanged = (string) ($order['last_changed'] ?? '');
|
||||
$actualDays = $lastChanged !== '' ? $this->daysSince($lastChanged) : $days;
|
||||
|
||||
$this->automation->trigger('order.status_aged', $orderId, [
|
||||
'current_status' => $currentStatus,
|
||||
'days_in_status' => $actualDays,
|
||||
'status_changed_at' => $lastChanged,
|
||||
]);
|
||||
$triggered++;
|
||||
} catch (Throwable) {
|
||||
// Blad jednego zamowienia nie blokuje kolejnych
|
||||
}
|
||||
}
|
||||
|
||||
return $triggered;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<array<string, mixed>> $conditions
|
||||
* @return list<string>
|
||||
*/
|
||||
private function extractStatusCodes(array $conditions): array
|
||||
{
|
||||
foreach ($conditions as $condition) {
|
||||
$type = (string) ($condition['condition_type'] ?? '');
|
||||
$value = is_array($condition['condition_value'] ?? null) ? $condition['condition_value'] : [];
|
||||
|
||||
if ($type === 'order_status') {
|
||||
$codes = is_array($value['order_status_codes'] ?? null) ? $value['order_status_codes'] : [];
|
||||
return array_values(array_filter(
|
||||
array_map(static fn (mixed $c): string => strtolower(trim((string) $c)), $codes),
|
||||
static fn (string $c): bool => $c !== ''
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<array<string, mixed>> $conditions
|
||||
*/
|
||||
private function extractDays(array $conditions): int
|
||||
{
|
||||
foreach ($conditions as $condition) {
|
||||
$type = (string) ($condition['condition_type'] ?? '');
|
||||
$value = is_array($condition['condition_value'] ?? null) ? $condition['condition_value'] : [];
|
||||
|
||||
if ($type === 'days_in_status') {
|
||||
return max(0, (int) ($value['days'] ?? 0));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<string> $statusCodes
|
||||
* @return list<array<string, mixed>>
|
||||
*/
|
||||
private function findAgedOrders(array $statusCodes, int $days): array
|
||||
{
|
||||
if ($statusCodes === [] || $days < 1) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$placeholders = implode(', ', array_fill(0, count($statusCodes), '?'));
|
||||
|
||||
$sql = "SELECT o.id, o.external_status_id, MAX(h.changed_at) AS last_changed
|
||||
FROM orders o
|
||||
INNER JOIN order_status_history h ON h.order_id = o.id
|
||||
AND LOWER(h.to_status_id) = LOWER(o.external_status_id)
|
||||
WHERE LOWER(COALESCE(o.external_status_id, '')) IN ({$placeholders})
|
||||
GROUP BY o.id, o.external_status_id
|
||||
HAVING MAX(h.changed_at) <= DATE_SUB(NOW(), INTERVAL ? DAY)
|
||||
LIMIT " . self::MAX_ORDERS_PER_RULE;
|
||||
|
||||
try {
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$params = $statusCodes;
|
||||
$params[] = $days;
|
||||
$stmt->execute($params);
|
||||
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
return is_array($rows) ? $rows : [];
|
||||
} catch (Throwable) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
private function daysSince(string $datetime): int
|
||||
{
|
||||
$timestamp = strtotime($datetime);
|
||||
if ($timestamp === false) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$diff = time() - $timestamp;
|
||||
return max(0, (int) floor($diff / 86400));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user