本文目录导读:

我来为你详细介绍PHP项目实现用户生日祝福的几种方案。
方案概述
生日祝福功能核心流程:检测生日 → 发送祝福 → 记录状态
基础实现方案
1 数据库设计
-- 用户表
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50),
email VARCHAR(100),
birthday DATE,
last_birthday_wish DATETIME,
wish_sent_today TINYINT(1) DEFAULT 0
);
2 基础检测脚本
<?php
class BirthdayWish {
private $db;
public function __construct($db) {
$this->db = $db;
}
/**
* 检测并发送生日祝福
*/
public function checkAndSendWishes() {
$today = date('m-d');
$currentYear = date('Y');
// 查询今天生日的用户
$sql = "SELECT * FROM users
WHERE DATE_FORMAT(birthday, '%m-%d') = ?
AND (last_birthday_wish IS NULL
OR DATE(last_birthday_wish) != CURDATE())";
$stmt = $this->db->prepare($sql);
$stmt->execute([$today]);
$users = $stmt->fetchAll();
foreach ($users as $user) {
$this->sendBirthdayWish($user);
}
}
/**
* 发送祝福
*/
private function sendBirthdayWish($user) {
// 计算年龄
$age = date('Y') - date('Y', strtotime($user['birthday']));
// 准备祝福内容
$message = "亲爱的{$user['username']},祝你{$age}岁生日快乐!";
// 发送方式:邮件
$this->sendEmail($user['email'], '生日祝福', $message);
// 更新发送记录
$this->updateWishRecord($user['id']);
}
/**
* 更新祝福记录
*/
private function updateWishRecord($userId) {
$sql = "UPDATE users
SET last_birthday_wish = NOW(),
wish_sent_today = 1
WHERE id = ?";
$stmt = $this->db->prepare($sql);
$stmt->execute([$userId]);
}
}
多种发送方式
1 邮件发送
<?php
use PHPMailer\PHPMailer\PHPMailer;
class EmailSender {
public function sendBirthdayEmail($to, $name, $age) {
$mail = new PHPMailer(true);
try {
$mail->isSMTP();
$mail->Host = 'smtp.gmail.com';
$mail->SMTPAuth = true;
$mail->Username = 'your@email.com';
$mail->Password = 'your-password';
$mail->SMTPSecure = 'tls';
$mail->Port = 587;
$mail->setFrom('noreply@yourdomain.com', '生日祝福');
$mail->addAddress($to, $name);
$mail->Subject = '🎂 生日快乐!';
// HTML模板
$mail->isHTML(true);
$mail->Body = "
<div style='max-width:600px;margin:0 auto;padding:20px;text-align:center;'>
<h1>🎉 生日快乐!</h1>
<p>亲爱的 {$name}:</p>
<p>今天是你的{$age}岁生日,</p>
<p>祝你生日快乐,万事如意!</p>
<div style='font-size:48px;'>🎂🎁🎊</div>
</div>
";
$mail->send();
return true;
} catch (Exception $e) {
error_log("邮件发送失败: " . $mail->ErrorInfo);
return false;
}
}
}
2 短信发送(使用阿里云)
<?php
use AlibabaCloud\Client\AlibabaCloud;
class SMSSender {
public function sendBirthdaySMS($phone, $name) {
AlibabaCloud::accessKeyClient('your-access-key', 'your-access-secret')
->regionId('cn-hangzhou')
->asDefaultClient();
try {
$result = AlibabaCloud::rpc()
->product('Dysmsapi')
->version('2017-05-25')
->action('SendSms')
->method('POST')
->host('dysmsapi.aliyuncs.com')
->options([
'query' => [
'PhoneNumbers' => $phone,
'SignName' => '你的签名',
'TemplateCode' => 'SMS_123456789',
'TemplateParam' => json_encode([
'name' => $name
])
]
])
->request();
return $result->toArray();
} catch (Exception $e) {
error_log("短信发送失败: " . $e->getMessage());
return false;
}
}
}
定时任务实现
1 Linux Crontab
# 每天早上8点执行 0 8 * * * /usr/bin/php /path/to/birthday_wish.php # 每小时执行一次 0 * * * * /usr/bin/php /path/to/birthday_wish.php
2 完整的定时任务脚本
#!/usr/bin/php
<?php
// birthday_wish.php - 定时任务入口
require_once 'config/database.php';
require_once 'vendor/autoload.php';
class BirthdayWishCron {
private $birthdayWish;
private $emailSender;
public function __construct() {
$db = new PDO("mysql:host=localhost;dbname=test", "root", "");
$this->birthdayWish = new BirthdayWish($db);
$this->emailSender = new EmailSender();
}
public function execute() {
echo "[" . date('Y-m-d H:i:s') . "] 开始检测生日用户...\n";
$today = date('m-d');
$db = $this->getDb();
// 查询今天生日的用户
$stmt = $db->prepare("
SELECT * FROM users
WHERE DATE_FORMAT(birthday, '%m-%d') = ?
AND (last_birthday_wish IS NULL
OR DATE(last_birthday_wish) != CURDATE())
");
$stmt->execute([$today]);
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo "发现 " . count($users) . " 位生日用户\n";
foreach ($users as $user) {
$this->processUser($user);
}
echo "[" . date('Y-m-d H:i:s') . "] 处理完成\n";
}
private function processUser($user) {
$age = date('Y') - date('Y', strtotime($user['birthday']));
echo "发送祝福给: {$user['username']} ({$age}岁)\n";
// 发送邮件
$emailResult = $this->emailSender->sendBirthdayEmail(
$user['email'],
$user['username'],
$age
);
if ($emailResult) {
echo "✓ 邮件发送成功\n";
$this->updateWishRecord($user['id']);
} else {
echo "✗ 邮件发送失败\n";
}
}
}
// 执行
$cron = new BirthdayWishCron();
$cron->execute();
高级功能实现
1 个性化祝福模板
<?php
class BirthdayTemplate {
private $templates = [
'friend' => "亲爱的{name},{age}岁生日快乐!愿你永远年轻,永远热泪盈眶!",
'colleague' => "尊敬的{name},祝您{age}岁生日快乐,工作顺利,家庭幸福!",
'vip' => "尊贵的VIP用户{name},感谢您的一路相伴,祝您{age}岁生日快乐!我们还为您准备了专属优惠券:{coupon}"
];
public function getPersonalizedWish($user) {
$template = $this->templates[$user['type']] ?? $this->templates['friend'];
$replacements = [
'{name}' => $user['username'],
'{age}' => $this->calculateAge($user['birthday']),
'{coupon}' => $this->generateCoupon($user['id'])
];
return str_replace(array_keys($replacements), $replacements, $template);
}
private function calculateAge($birthday) {
$birth = new DateTime($birthday);
$now = new DateTime();
return $birth->diff($now)->y;
}
private function generateCoupon($userId) {
return "BIRTHDAY" . date('Ymd') . $userId;
}
}
2 批量发送优化
<?php
class BatchBirthdaySender {
private $messageQueue = [];
private $maxBatchSize = 50;
public function addToQueue($user, $message) {
$this->messageQueue[] = [
'user' => $user,
'message' => $message
];
if (count($this->messageQueue) >= $this->maxBatchSize) {
$this->flushQueue();
}
}
public function flushQueue() {
if (empty($this->messageQueue)) {
return;
}
// 批量插入发送记录
$this->batchInsertRecords($this->messageQueue);
// 批量发送邮件(使用队列系统)
foreach ($this->messageQueue as $item) {
// 推送到消息队列
Queue::push('sendEmail', $item);
}
$this->messageQueue = [];
}
private function batchInsertRecords($records) {
$sql = "INSERT INTO birthday_wish_log (user_id, sent_at, status) VALUES ";
$values = [];
$params = [];
foreach ($records as $index => $record) {
$values[] = "(?, NOW(), 'pending')";
$params[] = $record['user']['id'];
}
$sql .= implode(', ', $values);
// 执行批量插入
}
}
最佳实践建议
1 时区处理
date_default_timezone_set('Asia/Shanghai');
2 防重复机制
// 使用Redis防止重复发送
$redisKey = "birthday:send:{$userId}:" . date('Ymd');
if ($redis->exists($redisKey)) {
return; // 今天已发送
}
// 发送逻辑...
$redis->setex($redisKey, 86400, 1);
3 异常处理
try {
// 发送逻辑
} catch (Exception $e) {
// 记录日志
error_log("生日祝福发送失败: " . $e->getMessage());
// 重试机制
$retryCount = 0;
while ($retryCount < 3) {
if ($this->retrySend($user)) {
break;
}
$retryCount++;
sleep(5);
}
}
部署建议
- 使用消息队列:Redis、RabbitMQ处理大量发送请求
- 日志记录:记录每次发送的状态便于排查
- 监控告警:设置发送失败告警
- 限流控制:避免短时间内大量发送被封
- 用户隐私:注意用户隐私保护,提供退订功能
这个方案可以根据项目规模灵活调整,从小型项目到大型系统都可以适用。