自动推广
This commit is contained in:
@@ -80,33 +80,43 @@ class PromotionService
|
||||
return ['done' => false, 'reason' => 'no_smtp_quota', 'retry_in' => 600];
|
||||
}
|
||||
|
||||
$journal = Db::name('journal')->where('journal_id', $task['journal_id'])->find();
|
||||
$expertVars = $this->buildExpertVars($expert);
|
||||
$journalVars = $this->buildJournalVars($journal);
|
||||
$vars = array_merge($journalVars, $expertVars);
|
||||
// 优先使用预生成内容;无则现场渲染
|
||||
$subject = '';
|
||||
$body = '';
|
||||
$hasPrepared = !empty($logEntry['subject_prepared']) && !empty($logEntry['body_prepared']);
|
||||
|
||||
$rendered = $this->renderFromTemplate(
|
||||
$task['template_id'],
|
||||
$task['journal_id'],
|
||||
json_encode($vars, JSON_UNESCAPED_UNICODE),
|
||||
$task['style_id']
|
||||
);
|
||||
if ($hasPrepared) {
|
||||
$subject = $logEntry['subject_prepared'];
|
||||
$body = $logEntry['body_prepared'];
|
||||
} else {
|
||||
$journal = Db::name('journal')->where('journal_id', $task['journal_id'])->find();
|
||||
$expertVars = $this->buildExpertVars($expert);
|
||||
$journalVars = $this->buildJournalVars($journal);
|
||||
$vars = array_merge($journalVars, $expertVars);
|
||||
|
||||
if ($rendered['code'] !== 0) {
|
||||
Db::name('promotion_email_log')->where('log_id', $logEntry['log_id'])->update([
|
||||
'state' => 2,
|
||||
'error_msg' => 'Template render failed: ' . $rendered['msg'],
|
||||
'send_time' => time(),
|
||||
]);
|
||||
Db::name('promotion_task')->where('task_id', $taskId)->setInc('fail_count');
|
||||
Db::name('promotion_task')->where('task_id', $taskId)->update(['utime' => time()]);
|
||||
$this->enqueueNextEmail($taskId, 2);
|
||||
return ['done' => false, 'failed' => $logEntry['email_to'], 'reason' => 'template_error'];
|
||||
$rendered = $this->renderFromTemplate(
|
||||
$task['template_id'],
|
||||
$task['journal_id'],
|
||||
json_encode($vars, JSON_UNESCAPED_UNICODE),
|
||||
$task['style_id']
|
||||
);
|
||||
|
||||
if ($rendered['code'] !== 0) {
|
||||
Db::name('promotion_email_log')->where('log_id', $logEntry['log_id'])->update([
|
||||
'state' => 2,
|
||||
'error_msg' => 'Template render failed: ' . $rendered['msg'],
|
||||
'send_time' => time(),
|
||||
]);
|
||||
Db::name('promotion_task')->where('task_id', $taskId)->setInc('fail_count');
|
||||
Db::name('promotion_task')->where('task_id', $taskId)->update(['utime' => time()]);
|
||||
$this->enqueueNextEmail($taskId, 2);
|
||||
return ['done' => false, 'failed' => $logEntry['email_to'], 'reason' => 'template_error'];
|
||||
}
|
||||
|
||||
$subject = $rendered['data']['subject'];
|
||||
$body = $rendered['data']['body'];
|
||||
}
|
||||
|
||||
$subject = $rendered['data']['subject'];
|
||||
$body = $rendered['data']['body'];
|
||||
|
||||
$result = $this->doSendEmail($account, $logEntry['email_to'], $subject, $body);
|
||||
|
||||
$now = time();
|
||||
@@ -144,6 +154,165 @@ class PromotionService
|
||||
];
|
||||
}
|
||||
|
||||
// ==================== 准备与触发(今日准备、明日发送) ====================
|
||||
|
||||
/**
|
||||
* 为指定任务预生成所有待发邮件的 subject/body,写入 log;完成后将任务置为 state=5(已准备)
|
||||
* @param int $taskId
|
||||
* @return array ['prepared' => int, 'failed' => int, 'error' => string|null]
|
||||
*/
|
||||
public function prepareTask($taskId)
|
||||
{
|
||||
$task = Db::name('promotion_task')->where('task_id', $taskId)->find();
|
||||
if (!$task) {
|
||||
return ['prepared' => 0, 'failed' => 0, 'error' => 'task_not_found'];
|
||||
}
|
||||
if ($task['state'] != 0) {
|
||||
return ['prepared' => 0, 'failed' => 0, 'error' => 'task_state_not_draft'];
|
||||
}
|
||||
|
||||
$journal = Db::name('journal')->where('journal_id', $task['journal_id'])->find();
|
||||
$logs = Db::name('promotion_email_log')
|
||||
->where('task_id', $taskId)
|
||||
->where('state', 0)
|
||||
->where('prepared_at', 0)
|
||||
->order('log_id asc')
|
||||
->select();
|
||||
|
||||
$prepared = 0;
|
||||
$failed = 0;
|
||||
$now = time();
|
||||
|
||||
foreach ($logs as $log) {
|
||||
$expert = Db::name('expert')->where('expert_id', $log['expert_id'])->find();
|
||||
if (!$expert) {
|
||||
Db::name('promotion_email_log')->where('log_id', $log['log_id'])->update([
|
||||
'state' => 2,
|
||||
'error_msg' => 'Expert not found',
|
||||
'send_time' => $now,
|
||||
]);
|
||||
$failed++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$expertVars = $this->buildExpertVars($expert);
|
||||
$journalVars = $this->buildJournalVars($journal);
|
||||
$vars = array_merge($journalVars, $expertVars);
|
||||
$rendered = $this->renderFromTemplate(
|
||||
$task['template_id'],
|
||||
$task['journal_id'],
|
||||
json_encode($vars, JSON_UNESCAPED_UNICODE),
|
||||
$task['style_id']
|
||||
);
|
||||
|
||||
if ($rendered['code'] !== 0) {
|
||||
Db::name('promotion_email_log')->where('log_id', $log['log_id'])->update([
|
||||
'state' => 2,
|
||||
'error_msg' => 'Prepare failed: ' . $rendered['msg'],
|
||||
'send_time' => $now,
|
||||
]);
|
||||
$failed++;
|
||||
continue;
|
||||
}
|
||||
|
||||
Db::name('promotion_email_log')->where('log_id', $log['log_id'])->update([
|
||||
'subject_prepared' => mb_substr($rendered['data']['subject'], 0, 512),
|
||||
'body_prepared' => $rendered['data']['body'],
|
||||
'prepared_at' => $now,
|
||||
]);
|
||||
$prepared++;
|
||||
}
|
||||
|
||||
Db::name('promotion_task')->where('task_id', $taskId)->update([
|
||||
'state' => 5,
|
||||
'utime' => $now,
|
||||
]);
|
||||
$this->log("prepareTask task_id={$taskId} prepared={$prepared} failed={$failed}");
|
||||
|
||||
return ['prepared' => $prepared, 'failed' => $failed, 'error' => null];
|
||||
}
|
||||
|
||||
/**
|
||||
* 为指定日期的任务批量预生成邮件(供定时任务调用,如每天 22:00 准备明天的)
|
||||
* @param string $date Y-m-d,如 2026-03-12
|
||||
* @return array ['tasks' => int, 'prepared' => int, 'failed' => int, 'details' => []]
|
||||
*/
|
||||
public function prepareTasksForDate($date)
|
||||
{
|
||||
$tasks = Db::name('promotion_task')
|
||||
->where('send_date', $date)
|
||||
->where('state', 0)
|
||||
->select();
|
||||
|
||||
$totalPrepared = 0;
|
||||
$totalFailed = 0;
|
||||
$details = [];
|
||||
|
||||
foreach ($tasks as $task) {
|
||||
$ret = $this->prepareTask($task['task_id']);
|
||||
$totalPrepared += $ret['prepared'];
|
||||
$totalFailed += $ret['failed'];
|
||||
$details[] = [
|
||||
'task_id' => $task['task_id'],
|
||||
'task_name' => $task['task_name'],
|
||||
'prepared' => $ret['prepared'],
|
||||
'failed' => $ret['failed'],
|
||||
'error' => $ret['error'],
|
||||
];
|
||||
}
|
||||
|
||||
$this->log("prepareTasksForDate date={$date} tasks=" . count($tasks) . " prepared={$totalPrepared} failed={$totalFailed}");
|
||||
return [
|
||||
'tasks' => count($tasks),
|
||||
'prepared' => $totalPrepared,
|
||||
'failed' => $totalFailed,
|
||||
'details' => $details,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发指定日期的已准备任务开始发送(供定时任务调用,如每天 8:00 触发今天的)
|
||||
* 会先对 send_date=date 且 state=0 的任务做一次补准备,再启动所有 state=5 的任务
|
||||
* @param string $date Y-m-d
|
||||
* @return array ['prepared' => int, 'started' => int, 'task_ids' => []]
|
||||
*/
|
||||
public function startTasksForDate($date)
|
||||
{
|
||||
// 补准备:当天日期但尚未准备的任务(如 22:00 后创建)
|
||||
$catchUpTasks = Db::name('promotion_task')
|
||||
->where('send_date', $date)
|
||||
->where('state', 0)
|
||||
->select();
|
||||
foreach ($catchUpTasks as $t) {
|
||||
$this->prepareTask($t['task_id']);
|
||||
}
|
||||
|
||||
$tasks = Db::name('promotion_task')
|
||||
->where('send_date', $date)
|
||||
->where('state', 5)
|
||||
->select();
|
||||
|
||||
$started = 0;
|
||||
$taskIds = [];
|
||||
|
||||
foreach ($tasks as $task) {
|
||||
Db::name('promotion_task')->where('task_id', $task['task_id'])->update([
|
||||
'state' => 1,
|
||||
'utime' => time(),
|
||||
]);
|
||||
$this->enqueueNextEmail($task['task_id'], 0);
|
||||
$started++;
|
||||
$taskIds[] = $task['task_id'];
|
||||
}
|
||||
|
||||
$this->log("startTasksForDate date={$date} started={$started} task_ids=" . implode(',', $taskIds));
|
||||
return [
|
||||
'prepared' => count($catchUpTasks),
|
||||
'started' => $started,
|
||||
'task_ids' => $taskIds,
|
||||
];
|
||||
}
|
||||
|
||||
// ==================== Queue ====================
|
||||
|
||||
public function enqueueNextEmail($taskId, $delay = 0)
|
||||
|
||||
Reference in New Issue
Block a user