feat(queue): CRUD, dedup, retention, status counters
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Cf7stg;
|
||||
|
||||
final class Queue
|
||||
{
|
||||
public const STATUS_PENDING = 'pending';
|
||||
public const STATUS_SENT = 'sent';
|
||||
public const STATUS_FAILED = 'failed';
|
||||
|
||||
public static function table_name(): string
|
||||
{
|
||||
global $wpdb;
|
||||
return $wpdb->prefix . 'cf7stg_queue';
|
||||
}
|
||||
|
||||
public static function enqueue(array $row): int
|
||||
{
|
||||
global $wpdb;
|
||||
$defaults = [
|
||||
'flamingo_post_id' => null,
|
||||
'form_id' => null,
|
||||
'source_key' => 'cf7',
|
||||
'payload_json' => '',
|
||||
'label' => 'unclear',
|
||||
'is_retro' => 0,
|
||||
'status' => self::STATUS_PENDING,
|
||||
'attempts' => 0,
|
||||
'last_error' => null,
|
||||
'next_try_at' => current_time('mysql'),
|
||||
'created_at' => current_time('mysql'),
|
||||
'sent_at' => null,
|
||||
];
|
||||
$data = array_merge($defaults, $row);
|
||||
$wpdb->insert(self::table_name(), $data);
|
||||
return (int)$wpdb->insert_id;
|
||||
}
|
||||
|
||||
public static function exists_for_flamingo(int $post_id, bool $is_retro): bool
|
||||
{
|
||||
global $wpdb;
|
||||
$t = self::table_name();
|
||||
$sql = $wpdb->prepare(
|
||||
"SELECT id FROM {$t} WHERE flamingo_post_id = %d AND is_retro = %d LIMIT 1",
|
||||
$post_id,
|
||||
$is_retro ? 1 : 0
|
||||
);
|
||||
return (bool)$wpdb->get_var($sql);
|
||||
}
|
||||
|
||||
/** @return array<int,object> */
|
||||
public static function fetch_pending(int $limit = 5): array
|
||||
{
|
||||
global $wpdb;
|
||||
$t = self::table_name();
|
||||
$sql = $wpdb->prepare(
|
||||
"SELECT * FROM {$t}
|
||||
WHERE status = %s
|
||||
AND (next_try_at IS NULL OR next_try_at <= %s)
|
||||
ORDER BY id ASC
|
||||
LIMIT %d",
|
||||
self::STATUS_PENDING,
|
||||
current_time('mysql'),
|
||||
$limit
|
||||
);
|
||||
$rows = $wpdb->get_results($sql);
|
||||
return is_array($rows) ? $rows : [];
|
||||
}
|
||||
|
||||
public static function mark_sent(int $id): void
|
||||
{
|
||||
global $wpdb;
|
||||
$wpdb->update(
|
||||
self::table_name(),
|
||||
['status' => self::STATUS_SENT, 'sent_at' => current_time('mysql'), 'last_error' => null],
|
||||
['id' => $id]
|
||||
);
|
||||
}
|
||||
|
||||
public static function reschedule(int $id, int $attempts, string $error, int $backoff_seconds): void
|
||||
{
|
||||
global $wpdb;
|
||||
$next = gmdate('Y-m-d H:i:s', time() + $backoff_seconds);
|
||||
$wpdb->update(
|
||||
self::table_name(),
|
||||
[
|
||||
'attempts' => $attempts,
|
||||
'last_error' => $error,
|
||||
'next_try_at' => $next,
|
||||
],
|
||||
['id' => $id]
|
||||
);
|
||||
}
|
||||
|
||||
public static function mark_failed(int $id, string $error, int $attempts): void
|
||||
{
|
||||
global $wpdb;
|
||||
$wpdb->update(
|
||||
self::table_name(),
|
||||
[
|
||||
'status' => self::STATUS_FAILED,
|
||||
'last_error' => $error,
|
||||
'attempts' => $attempts,
|
||||
],
|
||||
['id' => $id]
|
||||
);
|
||||
}
|
||||
|
||||
public static function count_by_status(string $status): int
|
||||
{
|
||||
global $wpdb;
|
||||
$t = self::table_name();
|
||||
return (int)$wpdb->get_var($wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM {$t} WHERE status = %s", $status
|
||||
));
|
||||
}
|
||||
|
||||
public static function count_failed(): int
|
||||
{
|
||||
return self::count_by_status(self::STATUS_FAILED);
|
||||
}
|
||||
|
||||
public static function count_sent_last_24h(): int
|
||||
{
|
||||
global $wpdb;
|
||||
$t = self::table_name();
|
||||
$cutoff = gmdate('Y-m-d H:i:s', time() - 86400);
|
||||
return (int)$wpdb->get_var($wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM {$t} WHERE status = %s AND sent_at >= %s",
|
||||
self::STATUS_SENT, $cutoff
|
||||
));
|
||||
}
|
||||
|
||||
/** @return array<int,object> */
|
||||
public static function list_failed(int $limit = 10): array
|
||||
{
|
||||
global $wpdb;
|
||||
$t = self::table_name();
|
||||
$rows = $wpdb->get_results($wpdb->prepare(
|
||||
"SELECT id, created_at, last_error FROM {$t}
|
||||
WHERE status = %s
|
||||
ORDER BY id DESC LIMIT %d",
|
||||
self::STATUS_FAILED, $limit
|
||||
));
|
||||
return is_array($rows) ? $rows : [];
|
||||
}
|
||||
|
||||
public static function retry_all_failed(): int
|
||||
{
|
||||
global $wpdb;
|
||||
$t = self::table_name();
|
||||
$now = current_time('mysql');
|
||||
$count = $wpdb->query($wpdb->prepare(
|
||||
"UPDATE {$t}
|
||||
SET status = %s, attempts = 0, last_error = NULL, next_try_at = %s
|
||||
WHERE status = %s",
|
||||
self::STATUS_PENDING, $now, self::STATUS_FAILED
|
||||
));
|
||||
return (int)$count;
|
||||
}
|
||||
|
||||
public static function purge_sent(): int
|
||||
{
|
||||
global $wpdb;
|
||||
$t = self::table_name();
|
||||
return (int)$wpdb->query($wpdb->prepare(
|
||||
"DELETE FROM {$t} WHERE status = %s", self::STATUS_SENT
|
||||
));
|
||||
}
|
||||
|
||||
public static function cleanup_old_sent(int $days = 30): int
|
||||
{
|
||||
global $wpdb;
|
||||
$t = self::table_name();
|
||||
$cutoff = gmdate('Y-m-d H:i:s', time() - $days * 86400);
|
||||
return (int)$wpdb->query($wpdb->prepare(
|
||||
"DELETE FROM {$t} WHERE status = %s AND sent_at < %s",
|
||||
self::STATUS_SENT, $cutoff
|
||||
));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user