本文目录导读:

这里为您提供一个完整的Java站内信(站内消息)系统实现案例,包含核心功能、代码示例和数据库设计。
系统设计
1 功能模块
- 发送站内信
- 接收站内信
- 已读/未读状态管理
- 删除消息
- 消息列表查询
- 未读数量统计
2 数据库表设计
-- 站内信主表 CREATE TABLE `sys_message` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, varchar(200) DEFAULT NULL COMMENT '标题', `content` text COMMENT '内容', `sender_id` bigint(20) DEFAULT NULL COMMENT '发送者用户ID', `sender_name` varchar(100) DEFAULT NULL COMMENT '发送者名称', `msg_type` tinyint(4) DEFAULT '0' COMMENT '消息类型:0系统消息,1用户消息,2通知', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '发送时间', `status` tinyint(4) DEFAULT '0' COMMENT '状态:0正常,1删除', PRIMARY KEY (`id`), KEY `idx_sender_id` (`sender_id`), KEY `idx_create_time` (`create_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 消息接收表 CREATE TABLE `sys_message_receiver` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `message_id` bigint(20) NOT NULL COMMENT '消息ID', `receiver_id` bigint(20) NOT NULL COMMENT '接收者用户ID', `is_read` tinyint(1) DEFAULT '0' COMMENT '是否已读:0未读,1已读', `read_time` datetime DEFAULT NULL COMMENT '阅读时间', `receiver_delete` tinyint(1) DEFAULT '0' COMMENT '接收者是否删除', PRIMARY KEY (`id`), UNIQUE KEY `uk_message_receiver` (`message_id`,`receiver_id`), KEY `idx_receiver_id` (`receiver_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
实体类实现
// 消息实体类
package com.example.message.entity;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class SysMessage {
private Long id;
private String title;
private String content;
private Long senderId;
private String senderName;
private Integer msgType; // 0系统 1用户 2通知
private LocalDateTime createTime;
private Integer status;
// 发送给指定用户
private Long receiverId;
}
// 消息接收实体类
package com.example.message.entity;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class MessageReceiver {
private Long id;
private Long messageId;
private Long receiverId;
private Integer isRead;
private LocalDateTime readTime;
private Integer receiverDelete;
}
// 消息传输对象
package com.example.message.dto;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Data
public class MessageDTO {
private Long messageId;
private String title;
private String content;
private Long senderId;
private String senderName;
private Integer msgType;
private LocalDateTime createTime;
private Integer isRead;
private Long receiverId;
// 请求参数
private Long userId; // 当前用户ID
private List<Long> targets; // 接收者ID列表
}
// 分页查询参数
@Data
public class MessageQueryDTO {
private Long userId;
private Integer isRead; // null查全部,0未读,1已读
private Integer msgType;
private Integer pageNum = 1;
private Integer pageSize = 10;
}
Mapper层实现
package com.example.message.mapper;
import com.example.message.dto.MessageDTO;
import com.example.message.dto.MessageQueryDTO;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface MessageMapper {
// 插入消息主表
@Insert("INSERT INTO sys_message(title, content, sender_id, sender_name, msg_type, create_time) " +
"VALUES(#{title}, #{content}, #{senderId}, #{senderName}, #{msgType}, NOW())")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insertMessage(MessageDTO message);
// 批量插入接收人
@Insert("<script>" +
"INSERT INTO sys_message_receiver(message_id, receiver_id, is_read) VALUES " +
"<foreach collection='targets' item='target' separator=','>" +
"(#{messageId}, #{target}, 0)" +
"</foreach>" +
"</script>")
int insertReceivers(@Param("messageId") Long messageId, @Param("targets") List<Long> targets);
// 查询用户的消息列表
@Select("<script>" +
"SELECT m.id as messageId, m.title, m.content, m.sender_id as senderId, " +
"m.sender_name as senderName, m.msg_type as msgType, m.create_time as createTime, " +
"r.is_read as isRead, r.receiver_id as receiverId " +
"FROM sys_message m " +
"INNER JOIN sys_message_receiver r ON m.id = r.message_id " +
"WHERE r.receiver_id = #{userId} AND r.receiver_delete = 0 AND m.status = 0 " +
"<if test='isRead != null'> AND r.is_read = #{isRead}</if>" +
"<if test='msgType != null'> AND m.msg_type = #{msgType}</if>" +
"ORDER BY m.create_time DESC " +
"LIMIT #{offset}, #{pageSize}" +
"</script>")
List<MessageDTO> selectMessageList(MessageQueryDTO queryDTO);
// 查询未读数量
@Select("SELECT COUNT(*) FROM sys_message_receiver r " +
"INNER JOIN sys_message m ON r.message_id = m.id " +
"WHERE r.receiver_id = #{userId} AND r.is_read = 0 " +
"AND r.receiver_delete = 0 AND m.status = 0")
int selectUnreadCount(Long userId);
// 标记已读
@Update("UPDATE sys_message_receiver SET is_read = 1, read_time = NOW() " +
"WHERE message_id = #{messageId} AND receiver_id = #{userId} AND is_read = 0")
int markAsRead(@Param("messageId") Long messageId, @Param("userId") Long userId);
// 批量标记已读
@Update("UPDATE sys_message_receiver SET is_read = 1, read_time = NOW() " +
"WHERE receiver_id = #{userId} AND is_read = 0")
int markAllAsRead(Long userId);
// 删除消息(接收者)
@Update("UPDATE sys_message_receiver SET receiver_delete = 1 " +
"WHERE message_id = #{messageId} AND receiver_id = #{userId}")
int deleteReceiverMessage(@Param("messageId") Long messageId, @Param("userId") Long userId);
// 删除消息(发送者)
@Update("UPDATE sys_message SET status = 1 WHERE id = #{messageId} AND sender_id = #{userId}")
int deleteSenderMessage(@Param("messageId") Long messageId, @Param("userId") Long userId);
}
Service层实现
package com.example.message.service;
import com.example.message.dto.MessageDTO;
import com.example.message.dto.MessageQueryDTO;
import com.example.message.mapper.MessageMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
@Service
public class MessageService {
@Autowired
private MessageMapper messageMapper;
/**
* 发送站内信
*/
@Transactional(rollbackFor = Exception.class)
public boolean sendMessage(MessageDTO messageDTO) {
// 1. 验证参数
if (messageDTO.getTargets() == null || messageDTO.getTargets().isEmpty()) {
throw new IllegalArgumentException("接收人不能为空");
}
// 2. 插入消息主表
messageDTO.setCreateTime(null);
int result = messageMapper.insertMessage(messageDTO);
if (result > 0) {
// 3. 批量插入接收人
result = messageMapper.insertReceivers(messageDTO.getId(), messageDTO.getTargets());
return result == messageDTO.getTargets().size();
}
return false;
}
/**
* 批量发送消息给指定用户
*/
public boolean batchSend(Integer msgType, String title, String content,
Long senderId, String senderName, List<Long> targets) {
MessageDTO messageDTO = new MessageDTO();
messageDTO.setMsgType(msgType);
messageDTO.setTitle(title);
messageDTO.setContent(content);
messageDTO.setSenderId(senderId);
messageDTO.setSenderName(senderName);
messageDTO.setTargets(targets);
return sendMessage(messageDTO);
}
/**
* 发送系统消息
*/
public boolean sendSystemMessage(String title, String content, List<Long> targets) {
return batchSend(0, title, content, 0L, "系统", targets);
}
/**
* 获取消息列表
*/
public List<MessageDTO> getMessageList(MessageQueryDTO queryDTO) {
queryDTO.setOffset((queryDTO.getPageNum() - 1) * queryDTO.getPageSize());
return messageMapper.selectMessageList(queryDTO);
}
/**
* 获取未读消息数量
*/
public int getUnreadCount(Long userId) {
return messageMapper.selectUnreadCount(userId);
}
/**
* 标记消息为已读
*/
public boolean markAsRead(Long messageId, Long userId) {
return messageMapper.markAsRead(messageId, userId) > 0;
}
/**
* 批量标记已读
*/
public boolean markAllAsRead(Long userId) {
return messageMapper.markAllAsRead(userId) > 0;
}
/**
* 删除接收者消息
*/
public boolean deleteMessage(Long messageId, Long userId) {
return messageMapper.deleteReceiverMessage(messageId, userId) > 0;
}
/**
* 获取消息详情(带已读标记)
*/
public MessageDTO getMessageDetail(Long messageId, Long userId) {
// 标记为已读
markAsRead(messageId, userId);
// 查询消息详情
MessageQueryDTO queryDTO = new MessageQueryDTO();
queryDTO.setUserId(userId);
queryDTO.setPageNum(1);
queryDTO.setPageSize(1);
List<MessageDTO> messages = messageMapper.selectMessageList(queryDTO);
return messages.isEmpty() ? null : messages.get(0);
}
}
Controller层实现
package com.example.message.controller;
import com.example.message.dto.MessageDTO;
import com.example.message.dto.MessageQueryDTO;
import com.example.message.service.MessageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/message")
public class MessageController {
@Autowired
private MessageService messageService;
/**
* 发送消息
*/
@PostMapping("/send")
public Result sendMessage(@RequestBody MessageDTO messageDTO) {
boolean success = messageService.sendMessage(messageDTO);
return success ? Result.success() : Result.error("发送失败");
}
/**
* 获取消息列表
*/
@GetMapping("/list")
public Result getMessageList(@RequestParam Long userId,
@RequestParam(required = false) Integer isRead,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
MessageQueryDTO queryDTO = new MessageQueryDTO();
queryDTO.setUserId(userId);
queryDTO.setIsRead(isRead);
queryDTO.setPageNum(pageNum);
queryDTO.setPageSize(pageSize);
List<MessageDTO> list = messageService.getMessageList(queryDTO);
return Result.success(list);
}
/**
* 获取未读消息数量
*/
@GetMapping("/unread-count")
public Result getUnreadCount(@RequestParam Long userId) {
int count = messageService.getUnreadCount(userId);
return Result.success(count);
}
/**
* 标记为已读
*/
@PostMapping("/read")
public Result markAsRead(@RequestParam Long messageId,
@RequestParam Long userId) {
boolean success = messageService.markAsRead(messageId, userId);
return success ? Result.success() : Result.error("标记失败");
}
/**
* 批量标记已读
*/
@PostMapping("/read-all")
public Result markAllAsRead(@RequestParam Long userId) {
boolean success = messageService.markAllAsRead(userId);
return success ? Result.success() : Result.error("标记失败");
}
/**
* 删除消息
*/
@DeleteMapping("/delete")
public Result deleteMessage(@RequestParam Long messageId,
@RequestParam Long userId) {
boolean success = messageService.deleteMessage(messageId, userId);
return success ? Result.success() : Result.error("删除失败");
}
}
使用示例
@RestController
public class TestController {
@Autowired
private MessageService messageService;
@PostMapping("/test/send")
public String testSend() {
// 发送系统通知
List<Long> targets = Arrays.asList(1L, 2L, 3L);
messageService.sendSystemMessage("系统升级通知",
"系统将于今晚进行升级,请提前保存数据。", targets);
// 发送用户消息
MessageDTO dto = new MessageDTO();
dto.setMsgType(1);
dto.setTitle("你好");
dto.setContent("用户消息测试");
dto.setSenderId(100L);
dto.setSenderName("用户A");
dto.setTargets(Collections.singletonList(1L));
messageService.sendMessage(dto);
// 查询未读数量
int unreadCount = messageService.getUnreadCount(1L);
System.out.println("未读消息数: " + unreadCount);
// 获取消息列表
MessageQueryDTO query = new MessageQueryDTO();
query.setUserId(1L);
query.setPageNum(1);
query.setPageSize(10);
List<MessageDTO> list = messageService.getMessageList(query);
return "发送成功,未读数量: " + unreadCount;
}
}
优化建议
-
索引优化:
sys_message表建议添加索引index_idx_title(title)等,sys_message_receiver表建议添加联合索引(receiver_id, is_read) -
缓存优化:使用Redis缓存用户未读数量
public int getUnreadCountCached(Long userId) {
String key = "message:unread:" + userId;
Integer count = redisTemplate.opsForValue().get(key);
if (count == null) {
count = messageMapper.selectUnreadCount(userId);
redisTemplate.opsForValue().set(key, count, 5, TimeUnit.MINUTES);
}
return count;
}
-
异步发送:对于批量发送消息,使用消息队列(如RabbitMQ)
-
定时清理:定期清理已删除超过一定时间的消息
-
支持通知推送:可集成WebSocket或第三方推送实现实时通知
这个实现涵盖了站内信系统的核心功能,可以根据需求进行扩展和完善。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播