From 7a204961de2d0847f610196a410fb9620cd42c61 Mon Sep 17 00:00:00 2001 From: wangjinlei <751475802@qq.com> Date: Fri, 24 Apr 2026 14:23:52 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=87=AA=E5=8A=A8=E6=8E=A8?= =?UTF-8?q?=E5=B9=BF=E7=9A=84=E7=9B=B8=E5=85=B3=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/api/controller/EmailClient.php | 7 ++- .../api/controller/PromotionFactory.php | 2 + application/common/PromotionService.php | 58 ++++++++++--------- 3 files changed, 38 insertions(+), 29 deletions(-) diff --git a/application/api/controller/EmailClient.php b/application/api/controller/EmailClient.php index 274d685..2e4bbcc 100644 --- a/application/api/controller/EmailClient.php +++ b/application/api/controller/EmailClient.php @@ -1704,8 +1704,13 @@ class EmailClient extends Base } /** - * 为单个任务预生成邮件(可手动或测试用) + * 为单个任务预生成邮件:异步入队 * Params: task_id + * + * 返回: + * queued 是否已入队(false 表示当前没有待准备邮件) + * task_id 任务ID + * pending 需要准备的邮件数(大约值;实际以队列执行为准) */ public function prepareTask() { diff --git a/application/api/controller/PromotionFactory.php b/application/api/controller/PromotionFactory.php index 95c0167..36f067c 100644 --- a/application/api/controller/PromotionFactory.php +++ b/application/api/controller/PromotionFactory.php @@ -53,6 +53,8 @@ class PromotionFactory extends Base ->page($page, $perPage) ->select(); + + foreach ($list as &$item) { $item['ctime_text'] = $item['ctime'] ? date('Y-m-d H:i:s', $item['ctime']) : ''; $item['fetch_fields'] = $this->resolveFetchFields($item['fetch_ids']); diff --git a/application/common/PromotionService.php b/application/common/PromotionService.php index f86d00e..d77b5f8 100644 --- a/application/common/PromotionService.php +++ b/application/common/PromotionService.php @@ -435,48 +435,50 @@ class PromotionService } /** - * 兼容旧接口:同步为 task 内所有邮件执行准备(不建议在 HTTP 请求中直接调用, - * 大邮件量场景请走 dispatchPrepareEmails)。 + * 为单个 task 准备邮件:走消息队列异步执行。 + * + * 流程: + * 1. 推一条 PromotionPrepareTask 到 promotion 队列(调度层)。 + * 2. 调度 Job 再拆成 N 条 PromotionPrepareEmail 到 promotion_email 队列(LLM 层)。 + * + * HTTP 调用方拿到的是"已入队"的结果,不再阻塞等待全部邮件准备完毕, + * 避免邮件量大时请求超时。task 的 state=5 由最后一封邮件在队列中触发 + * tryFinalizeTask 时完成。 * * @param int $taskId - * @return array ['prepared' => int, 'failed' => int, 'error' => string|null] + * @return array ['queued' => bool, 'task_id' => int, 'pending' => int, 'error' => string|null] */ public function prepareTask($taskId) { + $taskId = intval($taskId); $task = Db::name('promotion_task')->where('task_id', $taskId)->find(); if (!$task) { - return ['prepared' => 0, 'failed' => 0, 'error' => 'task_not_found']; + return ['queued' => false, 'task_id' => $taskId, 'pending' => 0, 'error' => 'task_not_found']; } if ($task['state'] != 0) { - return ['prepared' => 0, 'failed' => 0, 'error' => 'task_state_not_draft']; + return ['queued' => false, 'task_id' => $taskId, 'pending' => 0, 'error' => 'task_state_not_draft']; } - $logIds = Db::name('promotion_email_log') + $pending = Db::name('promotion_email_log') ->where('task_id', $taskId) ->where('state', 0) ->where('prepared_at', 0) - ->order('log_id asc') - ->column('log_id'); + ->count(); - $prepared = 0; - $failed = 0; - foreach ($logIds as $logId) { - $r = $this->prepareSingleEmail(intval($logId)); - if ($r['code'] === 0) { - $prepared++; - } else { - $failed++; - } + // 没有待准备邮件时,直接把 task 置为已准备并返回 + if ($pending == 0) { + Db::name('promotion_task')->where('task_id', $taskId)->where('state', 0)->update([ + 'state' => 5, + 'utime' => time(), + ]); + $this->log("prepareTask task_id={$taskId} no_pending -> state=5"); + return ['queued' => false, 'task_id' => $taskId, 'pending' => 0, 'error' => null]; } - // 兜底:即使 tryFinalizeTask 已触发,这里再保证 state 置为 5 - Db::name('promotion_task')->where('task_id', $taskId)->where('state', 0)->update([ - 'state' => 5, - 'utime' => time(), - ]); - $this->log("prepareTask task_id={$taskId} prepared={$prepared} failed={$failed}"); + $this->enqueuePrepareTask($taskId); + $this->log("prepareTask task_id={$taskId} queued pending={$pending}"); - return ['prepared' => $prepared, 'failed' => $failed, 'error' => null]; + return ['queued' => true, 'task_id' => $taskId, 'pending' => $pending, 'error' => null]; } /** @@ -519,9 +521,9 @@ class PromotionService $data = ['task_id' => intval($taskId)]; if ($delay > 0) { - Queue::later($delay, $jobClass, $data, 'promotionSend'); + Queue::later($delay, $jobClass, $data, 'promotionPrepareTask'); } else { - Queue::push($jobClass, $data, 'promotionSend'); + Queue::push($jobClass, $data, 'promotionPrepareTask'); } } @@ -537,9 +539,9 @@ class PromotionService $data = ['log_id' => intval($logId)]; if ($delay > 0) { - Queue::later($delay, $jobClass, $data, 'promotion_email'); + Queue::later($delay, $jobClass, $data, 'promotionPrepareEmail'); } else { - Queue::push($jobClass, $data, 'promotion_email'); + Queue::push($jobClass, $data, 'promotionPrepareEmail'); } }