本文目录导读:

我来详细讲解如何在PHP项目中实现用户自定义通知系统。
系统架构设计
数据库设计
-- 通知类型表
CREATE TABLE notification_types (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
code VARCHAR(50) UNIQUE NOT NULL,
description TEXT,
is_system TINYINT(1) DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 用户通知偏好表
CREATE TABLE user_notification_preferences (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
notification_type_id INT NOT NULL,
email_enabled TINYINT(1) DEFAULT 1,
sms_enabled TINYINT(1) DEFAULT 0,
push_enabled TINYINT(1) DEFAULT 1,
in_app_enabled TINYINT(1) DEFAULT 1,
custom_settings JSON,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (notification_type_id) REFERENCES notification_types(id),
UNIQUE KEY (user_id, notification_type_id)
);
-- 通知记录表
CREATE TABLE notifications (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
type_id INT NOT NULL,VARCHAR(255) NOT NULL,
message TEXT NOT NULL,
data JSON,
read_at TIMESTAMP NULL,
sent_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (type_id) REFERENCES notification_types(id),
INDEX idx_user_notification (user_id, read_at)
);
-- 通知通道表
CREATE TABLE notification_channels (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
code VARCHAR(50) UNIQUE NOT NULL,
handler_class VARCHAR(255) NOT NULL,
config_schema JSON,
is_active TINYINT(1) DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
核心通知系统实现
通知管理器类
<?php
namespace App\Notifications;
class NotificationManager {
private $db;
private $channels = [];
private $templates = [];
public function __construct($db) {
$this->db = $db;
$this->loadChannels();
}
/**
* 加载可用的通知通道
*/
private function loadChannels() {
$query = "SELECT * FROM notification_channels WHERE is_active = 1";
$result = $this->db->query($query);
while ($channel = $result->fetch_assoc()) {
if (class_exists($channel['handler_class'])) {
$handler = new $channel['handler_class']($channel);
$this->channels[$channel['code']] = $handler;
}
}
}
/**
* 发送通知
*/
public function send($userId, $typeCode, $data = []) {
// 获取通知类型
$type = $this->getNotificationTypeByCode($typeCode);
if (!$type) {
throw new \Exception("Notification type not found: {$typeCode}");
}
// 获取用户偏好
$preferences = $this->getUserPreferences($userId, $type['id']);
// 根据偏好发送通知
$sentChannels = [];
foreach ($preferences as $channelCode => $enabled) {
if ($enabled && isset($this->channels[$channelCode])) {
try {
$this->channels[$channelCode]->send($userId, $type, $data);
$sentChannels[] = $channelCode;
} catch (\Exception $e) {
// 记录错误但继续发送其他通道
error_log("Failed to send notification via {$channelCode}: " . $e->getMessage());
}
}
}
// 记录通知到数据库
$this->saveNotification($userId, $type, $data, $sentChannels);
return $sentChannels;
}
/**
* 批量发送通知
*/
public function sendBulk($userIds, $typeCode, $data = []) {
$results = [];
foreach ($userIds as $userId) {
try {
$channels = $this->send($userId, $typeCode, $data);
$results[$userId] = ['success' => true, 'channels' => $channels];
} catch (\Exception $e) {
$results[$userId] = ['success' => false, 'error' => $e->getMessage()];
}
}
return $results;
}
/**
* 获取用户通知偏好
*/
private function getUserPreferences($userId, $typeId) {
$query = "SELECT * FROM user_notification_preferences
WHERE user_id = ? AND notification_type_id = ?";
$stmt = $this->db->prepare($query);
$stmt->bind_param('ii', $userId, $typeId);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
$pref = $result->fetch_assoc();
return [
'email' => $pref['email_enabled'],
'sms' => $pref['sms_enabled'],
'push' => $pref['push_enabled'],
'in_app' => $pref['in_app_enabled']
];
}
// 默认偏好
return [
'email' => true,
'in_app' => true,
'push' => true,
'sms' => false
];
}
/**
* 保存通知到数据库
*/
private function saveNotification($userId, $type, $data, $channels) {
$title = $this->renderTemplate($type['name'] . '_title', $data);
$message = $this->renderTemplate($type['name'] . '_message', $data);
$query = "INSERT INTO notifications (user_id, type_id, title, message, data)
VALUES (?, ?, ?, ?, ?)";
$stmt = $this->db->prepare($query);
$jsonData = json_encode([
'data' => $data,
'channels' => $channels
]);
$stmt->bind_param('iisss', $userId, $type['id'], $title, $message, $jsonData);
$stmt->execute();
}
/**
* 获取通知类型
*/
private function getNotificationTypeByCode($code) {
$query = "SELECT * FROM notification_types WHERE code = ?";
$stmt = $this->db->prepare($query);
$stmt->bind_param('s', $code);
$stmt->execute();
return $stmt->get_result()->fetch_assoc();
}
/**
* 渲染模板
*/
private function renderTemplate($templateCode, $data) {
// 从数据库或文件加载模板
$template = $this->loadTemplate($templateCode);
// 替换变量
foreach ($data as $key => $value) {
$template = str_replace("{{{$key}}}", $value, $template);
}
return $template;
}
}
通知通道接口
<?php
namespace App\Notifications\Channels;
interface NotificationChannelInterface {
public function send($userId, $type, $data);
public function validateConfig();
}
邮件通道实现
<?php
namespace App\Notifications\Channels;
use App\Notifications\Channels\NotificationChannelInterface;
class EmailChannel implements NotificationChannelInterface {
private $config;
public function __construct($channelData) {
$this->config = json_decode($channelData['config_schema'], true);
}
public function send($userId, $type, $data) {
// 获取用户邮箱
$email = $this->getUserEmail($userId);
// 渲染邮件内容
$subject = $this->renderSubject($type, $data);
$body = $this->renderBody($type, $data);
// 发送邮件
return mail($email, $subject, $body, $this->getHeaders());
}
private function getUserEmail($userId) {
// 从数据库获取用户邮箱
return "user{$userId}@example.com";
}
private function renderSubject($type, $data) {
return "[{$type['name']}] " . ($data['subject'] ?? 'Notification');
}
private function renderBody($type, $data) {
$html = "<h1>{$type['name']}</h1>";
$html .= "<p>" . ($data['message'] ?? 'You have a new notification') . "</p>";
if (isset($data['action_url'])) {
$html .= "<a href='{$data['action_url']}' style='padding: 10px 20px; background: #007bff; color: white; text-decoration: none;'>View Details</a>";
}
return $html;
}
private function getHeaders() {
return "MIME-Version: 1.0\r\n" .
"Content-type: text/html; charset=UTF-8\r\n" .
"From: notifications@example.com\r\n";
}
public function validateConfig() {
return isset($this->config['smtp_host']) &&
isset($this->config['smtp_port']);
}
}
用户通知偏好管理
偏好设置控制器
<?php
namespace App\Controllers;
class NotificationPreferencesController {
private $db;
private $notificationManager;
public function __construct($db) {
$this->db = $db;
$this->notificationManager = new \App\Notifications\NotificationManager($db);
}
/**
* 获取用户所有通知偏好
*/
public function getPreferences($userId) {
$query = "SELECT nt.*, unp.*
FROM notification_types nt
LEFT JOIN user_notification_preferences unp
ON nt.id = unp.notification_type_id AND unp.user_id = ?
ORDER BY nt.name";
$stmt = $this->db->prepare($query);
$stmt->bind_param('i', $userId);
$stmt->execute();
$preferences = [];
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
$preferences[] = [
'type_id' => $row['id'],
'type_name' => $row['name'],
'type_code' => $row['code'],
'description' => $row['description'],
'channels' => [
'email' => $row['email_enabled'] ?? 1,
'sms' => $row['sms_enabled'] ?? 0,
'push' => $row['push_enabled'] ?? 1,
'in_app' => $row['in_app_enabled'] ?? 1
],
'custom_settings' => json_decode($row['custom_settings'] ?? '{}', true)
];
}
return $preferences;
}
/**
* 更新用户通知偏好
*/
public function updatePreference($userId, $typeId, $settings) {
// 检查是否已存在偏好设置
$query = "SELECT id FROM user_notification_preferences
WHERE user_id = ? AND notification_type_id = ?";
$stmt = $this->db->prepare($query);
$stmt->bind_param('ii', $userId, $typeId);
$stmt->execute();
if ($stmt->get_result()->num_rows > 0) {
// 更新现有偏好
$query = "UPDATE user_notification_preferences SET
email_enabled = ?, sms_enabled = ?,
push_enabled = ?, in_app_enabled = ?,
custom_settings = ?,
updated_at = NOW()
WHERE user_id = ? AND notification_type_id = ?";
} else {
// 创建新偏好
$query = "INSERT INTO user_notification_preferences
(user_id, notification_type_id, email_enabled, sms_enabled,
push_enabled, in_app_enabled, custom_settings)
VALUES (?, ?, ?, ?, ?, ?, ?)";
}
$stmt = $this->db->prepare($query);
$customSettings = json_encode($settings['custom_settings'] ?? []);
$email = $settings['email'] ?? 1;
$sms = $settings['sms'] ?? 0;
$push = $settings['push'] ?? 1;
$inApp = $settings['in_app'] ?? 1;
$stmt->bind_param('iiiiisi',
$email, $sms, $push, $inApp,
$customSettings, $userId, $typeId
);
return $stmt->execute();
}
/**
* 批量更新通知偏好
*/
public function batchUpdatePreferences($userId, $preferences) {
$success = true;
foreach ($preferences as $typeId => $settings) {
if (!$this->updatePreference($userId, $typeId, $settings)) {
$success = false;
}
}
return $success;
}
}
自定义通知规则
通知规则引擎
<?php
namespace App\Notifications;
class NotificationRuleEngine {
private $db;
private $rules = [];
public function __construct($db) {
$this->db = $db;
$this->loadRules();
}
/**
* 加载用户自定义规则
*/
private function loadRules() {
$query = "SELECT * FROM notification_rules WHERE is_active = 1";
$result = $this->db->query($query);
while ($rule = $result->fetch_assoc()) {
$this->rules[] = $this->parseRule($rule);
}
}
/**
* 解析规则
*/
private function parseRule($rule) {
return [
'id' => $rule['id'],
'user_id' => $rule['user_id'],
'conditions' => json_decode($rule['conditions'], true),
'actions' => json_decode($rule['actions'], true),
'priority' => $rule['priority']
];
}
/**
* 评估规则
*/
public function evaluate($event, $context) {
$matchedRules = [];
foreach ($this->rules as $rule) {
if ($this->matchesUser($rule, $context['user_id'])) {
if ($this->evaluateConditions($rule['conditions'], $event, $context)) {
$matchedRules[] = $rule;
}
}
}
// 按优先级排序
usort($matchedRules, function($a, $b) {
return $b['priority'] - $a['priority'];
});
return $matchedRules;
}
/**
* 检查规则是否匹配用户
*/
private function matchesUser($rule, $userId) {
return $rule['user_id'] === null || $rule['user_id'] === $userId;
}
/**
* 评估条件
*/
private function evaluateConditions($conditions, $event, $context) {
foreach ($conditions as $condition) {
if (!$this->evaluateSingleCondition($condition, $event, $context)) {
return false;
}
}
return true;
}
/**
* 评估单个条件
*/
private function evaluateSingleCondition($condition, $event, $context) {
$field = $condition['field'];
$operator = $condition['operator'];
$value = $condition['value'];
$actualValue = $context[$field] ?? null;
switch ($operator) {
case 'equals':
return $actualValue === $value;
case 'not_equals':
return $actualValue !== $value;
case 'contains':
return strpos($actualValue, $value) !== false;
case 'greater_than':
return $actualValue > $value;
case 'less_than':
return $actualValue < $value;
case 'in':
return in_array($actualValue, $value);
case 'not_in':
return !in_array($actualValue, $value);
default:
return true;
}
}
}
使用示例
发送通知示例
<?php
// 初始化通知管理器
$notificationManager = new \App\Notifications\NotificationManager($db);
// 发送新订单通知
$notificationManager->send(
$userId = 123,
$typeCode = 'new_order',
$data = [
'order_id' => 45678,
'total' => '$199.99',
'items' => 3,
'action_url' => '/orders/45678'
]
);
// 批量发送通知
$userIds = [123, 456, 789];
$results = $notificationManager->sendBulk(
$userIds,
'promotion',
[
'promotion_name' => 'Summer Sale',
'discount' => '20%',
'expiry' => '2024-08-31'
]
);
用户偏好设置示例
<?php
// 获取用户通知偏好
$prefsController = new \App\Controllers\NotificationPreferencesController($db);
$preferences = $prefsController->getPreferences($userId);
// 更新特定通知类型的偏好
$prefsController->updatePreference($userId, $typeId = 1, [
'email' => true,
'sms' => false,
'push' => true,
'in_app' => true,
'custom_settings' => [
'quiet_hours_start' => '22:00',
'quiet_hours_end' => '08:00',
'digest_frequency' => 'daily'
]
]);
自定义规则示例
<?php
// 创建自定义规则
$ruleEngine = new \App\Notifications\NotificationRuleEngine($db);
// 定义规则
$rule = [
'user_id' => $userId,
'conditions' => [
[
'field' => 'amount',
'operator' => 'greater_than',
'value' => 1000
],
[
'field' => 'type',
'operator' => 'equals',
'value' => 'purchase'
]
],
'actions' => [
'send_email' => true,
'send_sms' => true,
'priority' => 1
]
];
// 保存规则到数据库
$query = "INSERT INTO notification_rules (user_id, conditions, actions, priority)
VALUES (?, ?, ?, ?)";
$stmt = $db->prepare($query);
$stmt->bind_param('issi',
$rule['user_id'],
json_encode($rule['conditions']),
json_encode($rule['actions']),
$rule['priority']
);
$stmt->execute();
// 评估规则
$matchedRules = $ruleEngine->evaluate('order_completed', [
'user_id' => $userId,
'amount' => 1500,
'type' => 'purchase'
]);
前端实现示例
通知偏好设置页面
<!-- notification-preferences.html -->
<div class="notification-settings">
<h2>Notification Preferences</h2>
<div class="notification-types">
<div class="notification-type" v-for="pref in preferences">
<div class="type-header">
<h3>{{ pref.type_name }}</h3>
<p>{{ pref.description }}</p>
</div>
<div class="channel-settings">
<label class="channel-toggle">
<input type="checkbox"
v-model="pref.channels.email"
@change="updatePreference(pref)">
Email
</label>
<label class="channel-toggle">
<input type="checkbox"
v-model="pref.channels.sms"
@change="updatePreference(pref)">
SMS
</label>
<label class="channel-toggle">
<input type="checkbox"
v-model="pref.channels.push"
@change="updatePreference(pref)">
Push Notification
</label>
<label class="channel-toggle">
<input type="checkbox"
v-model="pref.channels.in_app"
@change="updatePreference(pref)">
In-App
</label>
</div>
<div class="custom-settings" v-if="pref.custom_settings">
<h4>Custom Settings</h4>
<!-- 自定义设置内容 -->
</div>
</div>
</div>
</div>
<script>
new Vue({
el: '.notification-settings',
data: {
preferences: []
},
mounted() {
this.loadPreferences();
},
methods: {
loadPreferences() {
fetch('/api/notifications/preferences')
.then(response => response.json())
.then(data => {
this.preferences = data;
});
},
updatePreference(pref) {
fetch(`/api/notifications/preferences/${pref.type_id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
channels: pref.channels,
custom_settings: pref.custom_settings
})
});
}
}
});
</script>
这样就实现了一个完整的自定义通知系统,包括:
- 灵活的通道管理
- 用户偏好设置
- 自定义通知规则
- 完整的数据库支持
- 前后端分离的实现
你可以根据实际需求进一步扩展,比如添加WebSocket实时推送、邮件队列处理等功能。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播