Java实现站内信案例

wen java案例 1

本文目录导读:

Java实现站内信案例

  1. 系统设计
  2. 实体类实现
  3. Mapper层实现
  4. Service层实现
  5. Controller层实现
  6. 使用示例
  7. 优化建议

这里为您提供一个完整的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;
    }
}

优化建议

  1. 索引优化sys_message表建议添加索引index_idx_title(title)等,sys_message_receiver表建议添加联合索引(receiver_id, is_read)

  2. 缓存优化:使用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;
}
  1. 异步发送:对于批量发送消息,使用消息队列(如RabbitMQ)

  2. 定时清理:定期清理已删除超过一定时间的消息

  3. 支持通知推送:可集成WebSocket或第三方推送实现实时通知

这个实现涵盖了站内信系统的核心功能,可以根据需求进行扩展和完善。

抱歉,评论功能暂时关闭!