request->param('id', 0)); $token = trim((string)$this->request->param('t', '')); $expert = $id > 0 ? Db::name('expert')->where('expert_id', $id)->find() : null; if (!$expert) { return $this->html($this->pageInvalid(), 404); } if (!UnsubscribeService::verifyToken($id, (string)$expert['email'], $token)) { return $this->html($this->pageInvalid(), 403); } if (!empty($expert['unsubscribed'])) { return $this->html($this->pageAlreadyDone((string)$expert['email'])); } return $this->html($this->pageConfirm( $id, $token, (string)$expert['email'], (string)($expert['name'] ?? '') )); } /** * 真正执行退订(POST 推荐;GET 也允许,以兼容部分邮件客户端禁止表单提交) */ public function confirm() { $id = intval($this->request->param('id', 0)); $token = trim((string)$this->request->param('t', '')); $expert = $id > 0 ? Db::name('expert')->where('expert_id', $id)->find() : null; if (!$expert) { return $this->html($this->pageInvalid(), 404); } if (!UnsubscribeService::verifyToken($id, (string)$expert['email'], $token)) { return $this->html($this->pageInvalid(), 403); } if (empty($expert['unsubscribed'])) { Db::name('expert')->where('expert_id', $id)->update([ 'unsubscribed' => 1, ]); } return $this->html($this->pageSuccess((string)$expert['email'])); } // ==================== HTML 页面 ==================== private function html($html, $status = 200) { $resp = Response::create($html, 'html', $status); $resp->header('Content-Type', 'text/html; charset=utf-8'); return $resp; } private function pageShell($title, $bodyHtml) { $titleSafe = htmlspecialchars($title, ENT_QUOTES, 'UTF-8'); $css = " body{margin:0;padding:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:#f5f7fa;color:#1f2937;} .wrap{max-width:520px;margin:64px auto;padding:32px;background:#fff;border-radius:12px;box-shadow:0 2px 16px rgba(0,0,0,.06);} h1{font-size:22px;margin:0 0 16px;color:#111827;} p{font-size:15px;line-height:1.6;margin:0 0 16px;color:#374151;} .email{font-weight:600;color:#111827;word-break:break-all;} .btn{display:inline-block;padding:10px 24px;border-radius:6px;border:0;cursor:pointer;font-size:15px;text-decoration:none;} .btn-primary{background:#dc2626;color:#fff;} .btn-primary:hover{background:#b91c1c;} .btn-secondary{background:#e5e7eb;color:#374151;margin-left:8px;} .muted{color:#6b7280;font-size:13px;margin-top:24px;} .ok{color:#16a34a;font-weight:600;} .warn{color:#d97706;font-weight:600;} .err{color:#dc2626;font-weight:600;} "; return '
' . '' . '' . 'Hi ' . $nameSafe . ',
' . 'You are about to unsubscribe ' . $emailSafe . ' from all promotion and invitation emails sent by TMR Journals. ' . 'After unsubscribing you will no longer receive marketing emails from us.
' . '' . 'If you didn\'t expect this email, you can safely close this page.
'; return $this->pageShell('Confirm unsubscribe - TMR Journals', $body); } private function pageSuccess($email) { $emailSafe = htmlspecialchars($email, ENT_QUOTES, 'UTF-8'); $body = '' . $emailSafe . ' will no longer receive promotion or invitation emails from TMR Journals.
' . 'If this was a mistake, please contact us and we will be happy to resubscribe you.
' . 'Thank you for your past interest in our journals.
'; return $this->pageShell('Unsubscribed - TMR Journals', $body); } private function pageAlreadyDone($email) { $emailSafe = htmlspecialchars($email, ENT_QUOTES, 'UTF-8'); $body = '' . $emailSafe . ' is already unsubscribed from our promotion emails.
' . 'No further action is needed.
'; return $this->pageShell('Already unsubscribed - TMR Journals', $body); } private function pageInvalid() { $body = 'This unsubscribe link is invalid or has expired.
' . 'Please use the most recent unsubscribe link in our emails, or contact us for help.
'; return $this->pageShell('Invalid link - TMR Journals', $body); } }