Thinkphp8-多因素认证-QQ邮箱通知器

Thinkphp8创建QQ邮箱通知器

准备项目

  1. Thinkphp8 需要php8上
  2. 需要QQ邮箱中生成的Key
  3. 需要安装驱动 phpmailer

申请QQ邮箱的key

QQ邮箱
点击设置到最低的账号与安全,点击安全设置后生成授权码,记住这个KEY,后面需要

准备Tp8的驱动以及配置文件

1
composer require phpmailer/phpmailer

创建 config\email.php,需要

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php

//+----------------------------------------------------------------------
//| qq邮箱配置
//+----------------------------------------------------------------------
return [

'Host' => 'smtp.qq.com', //不变
'Username' => '',//发件人邮箱'
'Password' => 'key(授权码)',
'Port' => 465,//不变
'From_address' => '2022471677@qq.com',//发件人地址及
'From_name' => '28.7Blog',//发件时显示的昵称
'Encryption' => 'ssl'//不变
];

创建控制器

这里用一个简单的测试页面发送邮箱

1
vim /etc/app/FeedBackController.php

代码如下(我的仅供参考,还是有很多需要更改的地方,首先这并不是最简单做法,我将数据保存到数据库了(这是我网站中代码的一部分),当然也不是最佳做法,我为了方便把模板放到php方法中了,不推荐!!!但是能用( ̄︶ ̄)↗ 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
<?php
namespace app\controller;

use think\facade\{Db, Request, Config, Cookie, Log};
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

class FeedBackController
{
private $mailer;
private $receiverEmail = "2022471677@qq.com";



public function __construct()
{
$this->mailer = new PHPMailer(true);
}

/**
* 处理用户反馈(API接口)
*/
public function feedback(): array
{
try {
if (Request()->method() === 'OPTIONS') {
return ['code' => 200, 'message' => 'OK'];
}

// 记录请求参数类型和值
$emailParam = Request()->param("email");//接收一个邮箱作为目的地,
这里目的地仍然是我
$suggestionParam = Request()->param("suggestion");

//准备需要发送的数据
$data = [
'email' => $emailParam,
'suggestion' => $suggestionParam
];

// 验证需要的字段
if (empty($data['email']) || empty($data['suggestion'])) {
return [
'code' => 400,
'message' => '邮箱和建议内容不能为空',
'data' => null
];
}

// 验证邮箱格式
if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
return [
'code' => 400,
'message' => '邮箱格式不正确',
'data' => null
];
}

// 保存到数据库
$feedbackId = $this->saveToDatabase($data['email'], $data['suggestion']);

// 发送邮件通知
$this->sendEmailNotification($data['email'], $data['suggestion']);

return [
'code' => 200,
'message' => '反馈提交成功,感谢您的建议!',
'data' => ['id' => $feedbackId]
];

} catch (\Exception $e) {
return [
'code' => 500,
'message' => '服务器内部错误:' . $e->getMessage(),
'data' => null
];
}
}

/**
* 保存反馈到数据库
*/
private function saveToDatabase(string $email, string $suggestion): int
{
$userLocation = $this->getUserLocation();

$data = [
'email' => $email,
'suggestion' => $suggestion,
'user_location' => $userLocation,
'ip_address' => Request::ip(),
'user_agent' => Request::server('HTTP_USER_AGENT', ''),
'created_at' => date('Y-m-d H:i:s')
];

// 检查数据库插入数据类型
foreach ($data as $key => $value) {
if (is_array($value)) {
throw new \RuntimeException("数据库字段{$key}不能是数组类型");
}
}

return Db::name('user_feedback')->strict(false)->insertGetId($data);
}

/**
* 获取用户地理位置信息
*/
private function getUserLocation(): string
{
//这是我预先GET到的地址
$location = Cookie::get("location_city") ?? "null";

// 确保返回字符串类型
if (is_array($location)) {
return 'invalid_location';
}

return (string)$location;
}

/**
* 发送邮件通知
*/
private function sendEmailNotification(string $email, string $suggestion): void
{
try {
$this->configureMailer();

// 检查收件人邮箱类型
if (is_array($this->receiverEmail)) {
throw new \RuntimeException('收件人邮箱是数组类型: ' . var_export($this->receiverEmail, true));
}

$this->mailer->addAddress($this->receiverEmail);
$this->mailer->Subject = '用户反馈通知 - ' . date('Y-m-d H:i:s');

$htmlContent = $this->generateEmailContent($email, $suggestion);
$this->mailer->isHTML(true);
$this->mailer->Body = $htmlContent;
$this->mailer->AltBody = strip_tags($htmlContent);

$this->mailer->send();

} catch (\Exception $e) {
}
}

/**
* 生成邮件内容
*/
private function generateEmailContent(string $email, string $suggestion): string
{
$userLocation = $this->getUserLocation();
$ipAddress = Request::ip();


return "
<!DOCTYPE html>
<html>
<head>
<meta charset=\"UTF-8\">
<title>用户反馈通知</title>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: #007bff; color: white; padding: 15px; text-align: center; }
.content { background: #f9f9f9; padding: 20px; border: 1px solid #ddd; }
.info { margin-bottom: 15px; }
.label { font-weight: bold; color: #007bff; }
.suggestion { background: white; padding: 15px; border-left: 4px solid #007bff; }
</style>
</head>
<body>
<div class=\"container\">
<div class=\"header\">
<h2>📧 用户反馈通知</h2>
</div>
<div class=\"content\">
<div class=\"info\">
<span class=\"label\">邮箱:</span> {$email}
</div>
<div class=\"info\">
<span class=\"label\">时间:</span> " . date('Y-m-d H:i:s') . "
</div>
<div class=\"info\">
<span class=\"label\">地理位置:</span> {$userLocation}
</div>
<div class=\"info\">
<span class=\"label\">IP地址:</span> {$ipAddress}
</div>
<div class=\"info\">
<span class=\"label\">建议内容:</span>
</div>
<div class=\"suggestion\">
" . nl2br(htmlspecialchars($suggestion)) . "
</div>
</div>
</div>
</body>
</html>
";
}

/**
* 邮件服务配置校验
*/
private function configureMailer(): void
{
$config = Config::get('email');



if (empty($config)) {
throw new \RuntimeException('邮件配置未找到');
}

// 确保配置是数组
if (!is_array($config)) {
throw new \RuntimeException('邮件配置格式错误,期望数组,实际是' . gettype($config));
}

// 检查必要的邮件配置项并验证类型
$requiredKeys = [
'Host' => 'string',
'Username' => 'string',
'Password' => 'string',
'Encryption' => 'string',
'Port' => 'integer'
];

foreach ($requiredKeys as $key => $expectedType) {
// 检查配置项是否存在
if (!isset($config[$key])) {
throw new \RuntimeException("邮件配置缺少必要项: {$key}");
}



// 检查端口号的特殊情况
if ($key === 'Port') {
// 允许端口号是字符串形式的数字,如"587"
if (!is_int($config[$key]) && (!is_string($config[$key]) || !ctype_digit($config[$key]))) {
throw new \RuntimeException("邮件配置项{$key}类型错误,期望整数,实际是" . gettype($config[$key]));
}
// 转换为整数
$config[$key] = (int)$config[$key];
} else {
// 检查其他配置项的类型
if (gettype($config[$key]) !== $expectedType) {
throw new \RuntimeException("邮件配置项{$key}类型错误,期望{$expectedType},实际是" . gettype($config[$key]));
}
}
}

// 设置发件人信息
$fromAddress = $config['From_address'] ?? $config['Username'];
$fromName = $config['From_name'] ?? '28.7Blog';


// 验证发件人邮箱格式
if (!filter_var($fromAddress, FILTER_VALIDATE_EMAIL)) {
throw new \RuntimeException("发件人邮箱格式不正确: {$fromAddress}");
}

try {
$this->mailer->isSMTP();
$this->mailer->Host = $config['Host'];
$this->mailer->SMTPAuth = true;
$this->mailer->Username = $config['Username'];
$this->mailer->Password = $config['Password'];
$this->mailer->SMTPSecure = $config['Encryption'];
$this->mailer->Port = $config['Port'];
$this->mailer->setFrom($fromAddress, $fromName);
$this->mailer->CharSet = 'UTF-8';
$this->mailer->SMTPDebug = 0;
} catch (\Exception $e) {
throw new \RuntimeException('邮件服务初始化失败: ' . $e->getMessage());

$this->mailer->SMTPDebug = 0;
} catch (\Exception $e) {
throw new \RuntimeException('邮件服务初始化失败: ' . $e->getMessage());
}

}
}

所以在刚才的代码中,最重要的配置,首先我们需要配置发送者以及接收,以及信息本身

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private function sendEmailNotification(string $email, string $suggestion): void
{
try {
$this->configureMailer();

// 检查收件人邮箱类型
if (is_array($this->receiverEmail)) {
throw new \RuntimeException('收件人邮箱是数组类型: ' . var_export($this->receiverEmail, true));
}

$this->mailer->addAddress($this->receiverEmail);//接收地址
$this->mailer->Subject = '用户反馈通知 - ' . date('Y-m-d H:i:s');

$htmlContent = $this->generateEmailContent($email, $suggestion);
$this->mailer->isHTML(true);//Html格式
$this->mailer->Body = $htmlContent;//指向邮件内容(HTML格式的)
$this->mailer->AltBody = strip_tags($htmlContent);//(清洗后的邮件内容)

$this->mailer->send();//发送数据

} catch (\Exception $e) {
//当然这个错误可以是降级之类的
}
}

如果上述代码正确的化,我会从前端接收用户的邮箱以及suggestion,将这些内容通过html格式化发送个我本人并记录到数据库

如果有什么不太明白的可以留言给我 https://www.kong.college/api/world/hot/news