modelClass = new EmailSubscribeModel(); $this->assign('statusList', $this->modelClass->getStatusList()); $this->assign('confirmStatusList', $this->modelClass->getConfirmStatusList()); $this->assign('sendStatusList', $this->modelClass->getSendStatusList()); $this->assign('logStatusList', (new EmailSubscribeLogModel())->getStatusList()); if (strtolower($this->request->action()) === 'log') { $config = $this->view->config ?: []; $config['jsname'] = 'backend/emailsubscribe'; $this->view->config = $config; $this->assign('config', $config); } $this->assign('broadcastUrl', url('Emailbroadcast/index')); } protected function sendSubscribeEmail(string $email, string $subject, string $content): array { try { $config = get_addon_config('phpmailer'); if (!$config) { return [false, 'NO_CONFIG', '未找到 phpmailer 插件配置']; } $mailer = new \addons\phpmailer\library\Mailer($config); $result = $mailer->to($email)->subject($subject)->message($content, true)->send(); if ($result) { return [true, 'SUCCESS', '邮件发送成功']; } return [false, 'FAIL', $mailer->getError() ?: '邮件发送失败']; } catch (\Throwable $e) { return [false, 'EXCEPTION', $e->getMessage()]; } } protected function buildMailContext(array $data): array { $context = []; foreach ($data as $key => $value) { $context[$key] = is_scalar($value) || $value === null ? (string)$value : json_encode($value, JSON_UNESCAPED_UNICODE); } return $context; } protected function renderTemplateContent(string $content, array $context): string { $replace = []; foreach ($context as $key => $value) { $replace['{{' . $key . '}}'] = (string)$value; $replace['{{ ' . $key . ' }}'] = (string)$value; } return strtr($content, $replace); } protected function getTemplateByType(string $type, string $lang): ?EmailTemplateModel { $lang = strtolower(trim($lang)); $lang = $lang !== '' ? $lang : 'cn'; $template = EmailTemplateModel::where(['type' => $type, 'lang' => $lang, 'status' => 1]) ->order('listorder', 'asc') ->order('id', 'asc') ->find(); if ($template) { return $template; } if ($lang !== 'cn') { return EmailTemplateModel::where(['type' => $type, 'lang' => 'cn', 'status' => 1]) ->order('listorder', 'asc') ->order('id', 'asc') ->find(); } return null; } protected function buildMailByType(string $type, string $lang, array $context = []): array { $template = $this->getTemplateByType($type, $lang); if ($template) { return [ 'subject' => $this->renderTemplateContent((string)$template->subject, $context), 'content' => $this->renderTemplateContent((string)$template->content, $context), ]; } $isCn = in_array(strtolower(trim($lang)), ['cn', 'zh', 'zh-cn'], true); return [ 'subject' => $isCn ? '感谢订阅 XTU 最新动态' : 'Thanks for subscribing to XTU updates', 'content' => $isCn ? '您已成功订阅 XTU 的新品发布与品牌动态邮件通知,后续如有更新我们会第一时间通过邮件发送给您。' : 'You have successfully subscribed to XTU news, new arrivals, and brand updates. We will email you when new updates are available.', ]; } public function index() { if ($this->request->isAjax()) { $page = (int)$this->request->param('page/d', 1); $limit = (int)$this->request->param('limit/d', 20); $sort = (string)$this->request->param('sort/s', 'id'); $order = (string)$this->request->param('order/s', 'desc'); $email = trim((string)$this->request->param('email/s', '')); $lang = trim((string)$this->request->param('lang/s', '')); $sourceKey = trim((string)$this->request->param('source_key/s', '')); $status = $this->request->param('status/s', ''); $confirmStatus = $this->request->param('confirm_status/s', ''); $lastSendStatus = $this->request->param('last_send_status/s', ''); $createtimeStart = trim((string)$this->request->param('createtime_start/s', '')); $createtimeEnd = trim((string)$this->request->param('createtime_end/s', '')); $query = $this->modelClass; if ($email !== '') { $query = $query->where('email', 'like', '%' . $email . '%'); } if ($lang !== '') { $query = $query->where('lang', $lang); } if ($sourceKey !== '') { $query = $query->where('source_key', $sourceKey); } if ($status !== '') { $query = $query->where('status', (int)$status); } if ($confirmStatus !== '') { $query = $query->where('confirm_status', (int)$confirmStatus); } if ($lastSendStatus !== '') { $query = $query->where('last_send_status', (int)$lastSendStatus); } if ($createtimeStart !== '') { $query = $query->where('createtime', '>=', strtotime($createtimeStart)); } if ($createtimeEnd !== '') { $query = $query->where('createtime', '<=', strtotime($createtimeEnd)); } $count = $query->count(); $data = $query->order($sort, $order)->page($page, $limit)->select(); return json(['code' => 0, 'count' => $count, 'data' => $data]); } return $this->fetch(); } public function log() { if ($this->request->isAjax()) { $page = (int)$this->request->param('page/d', 1); $limit = (int)$this->request->param('limit/d', 20); $sort = (string)$this->request->param('sort/s', 'id'); $order = (string)$this->request->param('order/s', 'desc'); $email = trim((string)$this->request->param('email/s', '')); $subscribeId = (int)$this->request->param('subscribe_id/d', 0); $mailType = trim((string)$this->request->param('mail_type/s', '')); $status = $this->request->param('status/s', ''); $sendTimeStart = trim((string)$this->request->param('send_time_start/s', '')); $sendTimeEnd = trim((string)$this->request->param('send_time_end/s', '')); $query = new EmailSubscribeLogModel(); if ($email !== '') { $query = $query->where('email', 'like', '%' . $email . '%'); } if ($subscribeId > 0) { $query = $query->where('subscribe_id', $subscribeId); } if ($mailType !== '') { $query = $query->where('mail_type', $mailType); } if ($status !== '') { $query = $query->where('status', (int)$status); } if ($sendTimeStart !== '') { $query = $query->where('send_time', '>=', strtotime($sendTimeStart)); } if ($sendTimeEnd !== '') { $query = $query->where('send_time', '<=', strtotime($sendTimeEnd)); } $count = $query->count(); $data = $query->order($sort, $order)->page($page, $limit)->select(); return json(['code' => 0, 'count' => $count, 'data' => $data]); } return $this->fetch(); } public function resend() { if (!$this->request->isPost()) { $this->error('请求方式错误'); } $id = (int)$this->request->post('id/d', 0); if ($id <= 0) { $this->error('缺少订阅记录ID'); } $row = $this->modelClass->find($id); if (!$row) { $this->error('订阅记录不存在'); } if (!$row->email || !Validate::is($row->email, 'email')) { $this->error('邮箱为空或格式不正确'); } if ((int)$row->status !== 1) { $this->error('仅已订阅状态允许补发邮件'); } $mail = $this->buildMailByType('subscribe_manual', (string)$row->lang, $this->buildMailContext([ 'email' => (string)$row->email, 'lang' => (string)$row->lang, 'source_page' => (string)$row->source_page, 'source_key' => (string)$row->source_key, 'remark' => (string)$row->remark, 'ip' => (string)$row->ip, 'user_agent' => (string)$row->user_agent, 'unsubscribe_url' => url('index/emailsubscribe/unsubscribe', ['token' => (string)$row->unsubscribe_token], true, true), ])); $subject = $mail['subject']; $content = $mail['content']; $now = time(); [$sendResult, $responseCode, $responseMsg] = $this->sendSubscribeEmail((string)$row->email, $subject, $content); EmailSubscribeLogModel::create([ 'subscribe_id' => $row->id, 'email' => $row->email, 'mail_type' => 'subscribe_manual', 'subject' => $subject, 'content' => $content, 'status' => $sendResult ? 1 : 2, 'response_code' => $responseCode, 'response_msg' => $responseMsg, 'retry_count' => $sendResult ? 0 : 1, 'send_time' => $sendResult ? $now : 0, 'createtime' => $now, 'updatetime' => $now, ]); $row->last_send_status = $sendResult ? 1 : 2; $row->last_send_time = $sendResult ? $now : (int)$row->last_send_time; $row->updatetime = $now; $row->save(); $this->success($sendResult ? '补发邮件成功' : '补发邮件失败:' . $responseMsg); } public function changeStatus() { if (!$this->request->isPost()) { $this->error('请求方式错误'); } $id = (int)$this->request->post('id/d', 0); $status = (int)$this->request->post('status/d', 1); if ($id <= 0) { $this->error('缺少订阅记录ID'); } if (!in_array($status, [0, 1, 9], true)) { $this->error('状态值不正确'); } $row = $this->modelClass->find($id); if (!$row) { $this->error('订阅记录不存在'); } $row->status = $status; $row->updatetime = time(); $row->save(); $this->success('状态更新成功'); } public function del() { if (!$this->request->isPost()) { $this->error('请求方式错误'); } $ids = $this->request->post('ids/a', []); if (!$ids) { $this->error('请先选择订阅记录'); } Db::startTrans(); try { Db::name('email_subscribe_log')->where('subscribe_id', 'in', $ids)->delete(); $this->modelClass->where('id', 'in', $ids)->delete(); Db::commit(); } catch (\Throwable $e) { Db::rollback(); $this->error('删除失败:' . $e->getMessage()); } $this->success('删除成功'); } public function delLog() { if (!$this->request->isPost()) { $this->error('请求方式错误'); } $ids = $this->request->post('ids/a', []); if (!$ids) { $this->error('请先选择邮件日志'); } (new EmailSubscribeLogModel())->where('id', 'in', $ids)->delete(); $this->success('删除成功'); } }