insert($row); return (int)DB::getPdo()->lastInsertId(); } public function updateBatch(int $batchId, array $row): int { return DB::table('admin_mail_batches')->where('id', $batchId)->update($row); } public function findBatch(int $batchId): ?object { $b = DB::table('admin_mail_batches')->where('id',$batchId)->first(); return $b ?: null; } public function listBatches(array $filters, int $perPage) { $q = DB::table('admin_mail_batches'); if (!empty($filters['status'])) $q->where('status', $filters['status']); if (!empty($filters['send_mode'])) $q->where('send_mode', $filters['send_mode']); if (!empty($filters['date_from'])) $q->whereDate('created_at','>=',$filters['date_from']); if (!empty($filters['date_to'])) $q->whereDate('created_at','<=',$filters['date_to']); if (!empty($filters['q'])) { $kw = '%'.$filters['q'].'%'; $q->where(function($w) use ($kw){ $w->where('subject_raw','like',$kw) ->orWhere('body_raw','like',$kw) ->orWhere('from_email','like',$kw) ->orWhere('admin_name','like',$kw) ->orWhere('last_error','like',$kw); }); } return $q->orderByDesc('id')->paginate($perPage)->withQueryString(); } public function bulkInsertItems(int $batchId, array $rows): void { // rows: [ [batch_id, seq, to_email, ...], ... ] DB::table('admin_mail_batch_items')->insert($rows); } public function listItems(int $batchId, array $filters, int $perPage) { $q = DB::table('admin_mail_batch_items')->where('batch_id',$batchId); if (!empty($filters['status'])) $q->where('status',$filters['status']); if (!empty($filters['to'])) $q->where('to_email','like','%'.$filters['to'].'%'); if (!empty($filters['q'])) { $kw = '%'.$filters['q'].'%'; $q->where(function($w) use ($kw){ $w->where('subject_final','like',$kw)->orWhere('body_final','like',$kw)->orWhere('last_error','like',$kw); }); } return $q->orderBy('seq')->paginate($perPage)->withQueryString(); } public function markBatchSending(int $batchId): void { DB::table('admin_mail_batches')->where('id',$batchId)->update([ 'status' => 'sending', 'started_at' => now()->format('Y-m-d H:i:s'), ]); } public function pickDueScheduledBatches(int $limit=20): array { $rows = DB::table('admin_mail_batches') ->where('status','scheduled') ->whereNotNull('scheduled_at') ->where('scheduled_at','<=', now()->format('Y-m-d H:i:s')) ->orderBy('scheduled_at') ->limit($limit) ->get(); return $rows ? $rows->all() : []; } public function setBatchQueued(int $batchId): void { DB::table('admin_mail_batches')->where('id',$batchId)->update(['status'=>'queued']); } public function countItemsByStatus(int $batchId): array { $rows = DB::table('admin_mail_batch_items') ->select('status', DB::raw('COUNT(*) as c')) ->where('batch_id',$batchId) ->groupBy('status')->get(); $map = ['sent'=>0,'failed'=>0,'canceled'=>0,'skipped'=>0,'queued'=>0]; foreach ($rows as $r) $map[$r->status] = (int)$r->c; return $map; } public function updateBatchCounts(int $batchId, array $counts, ?string $status=null, ?string $lastError=null): void { $row = [ 'sent_count' => (int)($counts['sent'] ?? 0), 'failed_count' => (int)($counts['failed'] ?? 0), 'canceled_count' => (int)(($counts['canceled'] ?? 0) + ($counts['skipped'] ?? 0)), ]; if ($status) $row['status'] = $status; if ($lastError !== null) $row['last_error'] = $lastError; if (in_array($status, ['sent','partial','failed','canceled'], true)) { $row['finished_at'] = now()->format('Y-m-d H:i:s'); } DB::table('admin_mail_batches')->where('id',$batchId)->update($row); } public function cancelBatch(int $batchId): void { DB::table('admin_mail_batches')->where('id',$batchId)->update([ 'status'=>'canceled', 'finished_at'=>now()->format('Y-m-d H:i:s'), ]); DB::table('admin_mail_batch_items') ->where('batch_id',$batchId) ->whereIn('status',['queued']) ->update(['status'=>'canceled','last_error'=>'batch_canceled']); } public function resetFailedToQueued(int $batchId): int { return DB::table('admin_mail_batch_items') ->where('batch_id',$batchId) ->where('status','failed') ->update(['status'=>'queued','last_error'=>'','attempts'=>0]); } public function nextQueuedItems(int $batchId, int $limit=200): array { $rows = DB::table('admin_mail_batch_items') ->where('batch_id',$batchId) ->where('status','queued') ->orderBy('seq') ->limit($limit) ->get(); return $rows ? $rows->all() : []; } public function updateItem(int $id, array $row): void { DB::table('admin_mail_batch_items')->where('id',$id)->update($row); } }