使用 Spring Boot 和 Vue.js 实现实时通知系统
在这篇文章中,我们将教你如何从零开始实现一个简单的实时通知系统,使用 Spring Boot 作为后端,Vue.js 作为前端,结合 WebSocket 和 STOMP 协议实现实时推送通知,并通过电子邮件将通知内容发送给所有用户。
系统需求
- 后端(Spring Boot) 支持接收、存储和广播通知。
- 支持将通知发送到指定的用户邮箱。
- 使用 WebSocket 推送通知到前端。
- 前端(Vue.js) 使用 WebSocket 接收后端推送的通知。
- 显示通知内容。
- 提供输入框让用户发送新通知。
创建 Spring Boot 项目
1.1 初始化 Spring Boot 项目
首先,我们需要创建一个 Spring Boot 项目。如果你不熟悉 Spring Boot,可以通过 Spring Initializr 快速创建一个项目:
选择 Spring Boot 版本(如 2.7.x)。
选择依赖:Spring Web, Spring WebSocket, Spring Boot DevTools, Spring Data JPA, MySQL Driver, Spring Mail 等。
点击 Generate,下载项目并解压。
将解压后的项目导入到你的开发工具(如 IntelliJ IDEA 或 Eclipse)。
1.2 配置数据库
在 application.yml 或 application.properties 文件中配置数据库连接信息:
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
mail:
host: smtp.yeah.net
port: 465
username: your-email@yeah.net
password: your-email-password
from: your-email@yeah.net
websocket:
enabled: true
1.3 实现 WebSocket 配置
我们需要配置 WebSocket 来支持通知的实时推送。创建一个 WebSocketConfig 类:
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
// 处理客户端订阅消息的前缀
config.enableSimpleBroker("/topic");
// 设置应用程序消息的前缀
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/notifications").withSockJS(); // 设置 WebSocket 端点
}
}
1.4 通知服务
在后端,我们创建一个服务类 NotificationService 来处理通知的存储、广播和邮件发送。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
@Service
public class NotificationService {
@Autowired
private SimpMessagingTemplate messagingTemplate; // 用于 WebSocket 广播
@Autowired
private EmailService emailService; // 邮件服务
// 广播通知
public void broadcastNotification(Notification notification) {
messagingTemplate.convertAndSend("/topic/notifications", notification); // 将通知发送到所有订阅者
}
// 发送邮件给所有用户
public void sendNotificationToAll(String message) {
// 假设我们有一个获取所有用户邮箱的列表
List<String> emails = getAllEmails();
for (String email : emails) {
emailService.sendSimpleMail(new String[]{email}, "新通知", message);
}
}
// 获取所有用户邮箱的模拟方法
private List<String> getAllEmails() {
// 这里可以通过数据库查询获取邮箱
return Arrays.asList("user1@example.com", "user2@example.com");
}
}
1.5 邮件服务
EmailService 负责通过 SMTP 发送邮件:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;
@Service
public class EmailService {
@Autowired
private JavaMailSender javaMailSender;
@Value("${spring.mail.from}")
private String from;
public void sendSimpleMail(String[] to, String subject, String content) {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(from);
message.setTo(to);
message.setSubject(subject);
message.setText(content);
javaMailSender.send(message);
}
}
1.6 控制器
NotificationController 负责处理前端请求,接收新通知,并通过 WebSocket 广播和邮件发送通知。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/notifications")
public class NotificationController {
@Autowired
private NotificationService notificationService;
@PostMapping("/send")
public ResponseEntity<?> sendNotification(@RequestBody NotificationRequestDto request) {
try {
// 保存通知
notificationService.saveNotification(request.getEmailRequest().getContent());
// 广播通知
notificationService.broadcastNotification(request.getNotification());
// 发送邮件通知
notificationService.sendNotificationToAll(request.getEmailRequest().getContent());
return ResponseEntity.ok("通知已发送");
} catch (Exception e) {
return ResponseEntity.status(500).body("发送失败");
}
}
}
2. 创建 Vue.js 前端
2.1 创建 Vue 项目
我们通过 Vue CLI 创建一个新的 Vue 2 项目。你可以参考以下命令:
npm install -g @vue/cli
vue create notification-demo
cd notification-demo
npm run serve
2.2 安装 WebSocket 依赖
在 Vue 项目中,我们需要使用 sockjs-client 和 stomp.js 库来连接 WebSocket。你可以通过 npm 安装这些依赖:
npm install sockjs-client stompjs
2.3 Vue 实现通知
在 Vue 中,我们使用 WebSocket 接收后端推送的通知,并将其显示在页面上。以下是一个简单的 Vue 组件:
<template>
<div id="app">
<h1>通知系统</h1>
<ul>
<li v-for="notification in notifications" :key="notification.id">{{ notification.message }}</li>
</ul>
<form @submit.prevent="sendNotification">
<textarea v-model="newNotification.message" placeholder="输入通知" required></textarea>
<button type="submit">发送通知</button>
</form>
<audio ref="notificationSound" src="https://your-sound-file-url.com" preload="auto"></audio>
</div>
</template>
<script>
import SockJS from 'sockjs-client';
import Stomp from 'stompjs';
export default {
data() {
return {
notifications: [],
newNotification: { message: '' },
stompClient: null
};
},
mounted() {
this.setupWebSocket();
},
methods: {
setupWebSocket() {
const socket = new SockJS('http://localhost:8080/notifications');
this.stompClient = Stomp.over(socket);
this.stompClient.connect({}, (frame) => {
this.stompClient.subscribe('/topic/notifications', (messageOutput) => {
const notification = JSON.parse(messageOutput.body);
this.notifications.push(notification);
this.playNotificationSound();
});
});
},
sendNotification() {
const request = {
notification: this.newNotification,
emailRequest: { content: this.newNotification.message }
};
fetch('/api/notifications/send', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request)
});
},
playNotificationSound() {
this.$refs.notificationSound.play();
}
}
};
</script>
2.4 HTML 页面
前端页面显示接收到的通知,并允许用户发送新的通知。用户提交通知后,通知会通过 WebSocket 推送到其他客户端,同时通过邮件发送给所有用户。
<div id="app">
<h1>通知Demo</h1>
<ul>
<li v-for="notification in notifications" :key="notification.id">{{ notification.message }}</li>
</ul>
<div>
<h2>发送通知</h2>
<form @submit.prevent="sendNotification">
<textarea v-model="newNotification.message" placeholder="输入通知" required></textarea>
<button type="submit">发送通知</button>
</form>
</div>
<audio ref="notificationSound" src="your-notification-sound.mp3" preload="auto"></audio>
</div>
后面是加入将通知存入数据库的步骤。我们将使用 MySQL 数据库来存储通知信息,包括通知的内容和创建时间。
3. 创建数据库表
在 MySQL 中创建一个表来存储通知。你可以使用以下 SQL 语句来创建表:
CREATE TABLE notifications (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
message VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
id
:通知的唯一标识符。message
:通知的内容。created_at
:通知的创建时间。
4. 实现通知存储功能
我们需要在 NotificationService
中实现将通知保存到数据库的功能。通过使用 MyBatis 或 JPA(根据项目的配置),你可以将通知信息存储到数据库。
4.1 使用 MyBatis 存储通知
首先,我们创建一个 NotificationMapper
来与数据库交互:
4.1.1 创建 NotificationMapper
接口
import com.test.message.model.Notification;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface NotificationMapper {
@Select("SELECT * FROM notifications WHERE id = #{id}")
Notification findById(Long id);
@Insert("INSERT INTO notifications (message, created_at) VALUES (#{message}, #{createdAt})")
void save(Notification notification);
}
4.1.2 修改 NotificationService
以调用 Mapper 保存通知
在 NotificationService
中,我们调用 NotificationMapper
的 save
方法将通知信息存入数据库:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
import com.test.message.mapper.NotificationMapper;
import com.test.message.model.Notification;
import java.time.LocalDateTime;
@Service
public class NotificationService {
@Autowired
private SimpMessagingTemplate messagingTemplate;
@Autowired
private NotificationMapper notificationMapper; // 注入 NotificationMapper
@Autowired
private EmailService emailService;
// 保存通知到数据库
public void saveNotification(String message) {
Notification notification = new Notification();
notification.setMessage(message);
notification.setCreatedAt(LocalDateTime.now());
notificationMapper.save(notification); // 保存到数据库
}
// 广播通知
public void broadcastNotification(Notification notification) {
messagingTemplate.convertAndSend("/topic/notifications", notification);
}
// 发送通知给所有用户
public void sendNotificationToAll(String message) {
List<String> emails = getAllEmails();
for (String email : emails) {
emailService.sendSimpleMail(new String[]{email}, "新通知", message);
}
}
// 获取所有用户邮箱的模拟方法
private List<String> getAllEmails() {
return Arrays.asList("user1@example.com", "user2@example.com");
}
}
saveNotification
方法将通知的内容存储到数据库中,并且设置当前的创建时间。
4.2 更新控制器以调用保存通知的方法
我们需要更新 NotificationController
,使其能够调用 NotificationService
的 saveNotification
方法,并保存发送的通知。
4.2.1 修改 NotificationController
类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/notifications")
public class NotificationController {
@Autowired
private NotificationService notificationService;
// 获取所有通知
@GetMapping
public List<Notification> getNotifications() {
return notificationService.getAllNotifications();
}
// 发送通知
@PostMapping("/send")
public ResponseEntity<?> sendNotification(@RequestBody NotificationRequestDto request) {
try {
// 保存通知到数据库
notificationService.saveNotification(request.getEmailRequest().getContent());
// 广播通知
notificationService.broadcastNotification(request.getNotification());
// 发送邮件通知给所有用户
notificationService.sendNotificationToAll(request.getEmailRequest().getContent());
return ResponseEntity.ok("通知已发送");
} catch (Exception e) {
return ResponseEntity.status(500).body("发送失败");
}
}
}
sendNotification
方法会将通知内容保存到数据库,并将通知广播到所有客户端,同时通过邮件发送通知。
5. 完善前端 Vue.js 部分
现在,后端已经能够将通知保存到数据库并广播。接下来,我们来完善前端部分,让它能够接收后端发送的通知,并展示这些通知。
5.1 安装 WebSocket 依赖
如前所述,我们需要安装 WebSocket 客户端依赖:
npm install sockjs-client stompjs
5.2 Vue.js 前端实现
在 Vue.js 中,我们创建一个 WebSocket 客户端来接收通知,并将其显示在页面上。
5.2.1 修改 App.vue
组件
<template>
<div id="app">
<h1>通知系统</h1>
<ul>
<li v-for="notification in notifications" :key="notification.id">{{ notification.message }}</li>
</ul>
<form @submit.prevent="sendNotification">
<textarea v-model="newNotification.message" placeholder="输入通知" required></textarea>
<button type="submit">发送通知</button>
</form>
<audio ref="notificationSound" src="your-notification-sound.mp3" preload="auto"></audio>
</div>
</template>
<script>
import SockJS from 'sockjs-client';
import Stomp from 'stompjs';
export default {
data() {
return {
notifications: [],
newNotification: { message: '' },
stompClient: null
};
},
mounted() {
this.setupWebSocket();
},
methods: {
setupWebSocket() {
const socket = new SockJS('http://localhost:8080/notifications');
this.stompClient = Stomp.over(socket);
this.stompClient.connect({}, (frame) => {
this.stompClient.subscribe('/topic/notifications', (messageOutput) => {
const notification = JSON.parse(messageOutput.body);
this.notifications.push(notification);
this.playNotificationSound();
});
});
},
sendNotification() {
const request = {
notification: this.newNotification,
emailRequest: { content: this.newNotification.message }
};
fetch('/api/notifications/send', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request)
});
},
playNotificationSound() {
this.$refs.notificationSound.play();
}
}
};
</script>
setupWebSocket
方法连接到 WebSocket 端点,并订阅/topic/notifications
路径。sendNotification
方法将通知发送到后端,并触发广播。
6. 总结
通过本文的扩展,你已经成功实现了一个包含 数据库存储 和 WebSocket 实时通知推送的系统。系统功能如下:
- 存储通知:通知信息会被保存到 MySQL 数据库中。
- 实时广播:通知通过 WebSocket 推送到前端客户端。
- 邮件通知:通知内容会通过邮件发送到所有用户。