项目1在线交流平台-3.开发交流社区核心功能模块-7.显示私信信息

参考牛客网高级项目教程

功能需求及处理策略

在这里插入图片描述

  • 1.点击消息,能够显示与当前用户所有的会话列表-分为朋友私信和系统私信两个模块,本功能显示前者
    • 按照会话列表显示,每个会话列表显示模块一样
    • 每个会话中,内容显示两个用户最新的一条私信
  • 2.点击每个会话列表的内容,可以显示该会话中两个用户的所有私信列表
  • 3.要能显示出未读消息
    • 包括总的未读消息
    • 和每个会话中,两个用户之间的未读私信

1. dao层处理会话私信数据

数据库设计

  • from_id:一个会话中,私信发送者
  • to_id: 一个会话中,私信接收者
  • conversation_id:冗余字段:会话的id
    • 是为了方便查询会话列表,由from_id和to_id组成
    • 没有前后顺序,A私信给B,B私信给A,都属于一种会话
    • 为表示方便,一般,小数字在前,大数字在后
  • **status:**用此字段管理私信处理结果,已读、未读、删除
CREATE TABLE `message` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `from_id` int(11) DEFAULT NULL,
  `to_id` int(11) DEFAULT NULL,
  `conversation_id` varchar(45) NOT NULL,
  `content` text,
  `status` int(11) DEFAULT NULL COMMENT '0-未读;1-已读;2-删除;',
  `create_time` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_from_id` (`from_id`),
  KEY `index_to_id` (`to_id`),
  KEY `index_conversation_id` (`conversation_id`)
) ENGINE=InnoDB AUTO_INCREMENT=355 DEFAULT CHARSET=utf8

接口方法声明,sql编写

接口方法
@Mapper
public interface MessageMapper {
    /** 查询指定用户的会话列表 -内容只显示最新一条私信*/
    List<Message> selectConversationList(int userId, int offset, int limit);
    
    /** 查询指定用户会话总数 */
    int selectConversationCount(int userId);
    
    /** 查询指定会话的两个用户之间的私信列表 */
    List<Message> selectLetterList(int conversationId, int offset, int limit);
    
    /** 查询指定会话的两个用户之间的私信总数 */
    int selectLetterCount(int conversationId);
    
    /** 查询未读私信数量-动态条件查询-当前用户总的未读私信数量-当前会话的未读私信数量 */
    int selectUnreadCount(int userId, int conversationId);
}
sql-子查询语句
组函数与分组查询
  • 查询指定用户的会话列表 -内容只显示最新一条私信

  • 先进行组查询,查询到每组会话conversationId中的所有消息中最新的一条消息

    • 要进行过滤掉删除的私信、系统发来的私信

    • 以conversationId分组,可以查到一组私信消息

    • 对这组消息,取id最大,因为id是自增生成,最新的消息id在当前组中是最大的

      SELECT max(id) from message
      where status != 2
      and from_id != 1
      and (from_id = 111 or to_id = 111)
      group by conversation_id;
      
      在这里插入图片描述
根据分组结果进行子查询
  • 再根据组查询的id结果,进行会话列表的条件子查询

    在这里插入图片描述

  • 1.查询指定用户的会话列表 -内容只显示最新一条私信

<!--    定义复用代码块-->
    <sql id="selectFields">
        select id, from_id, to_id, conversation_id, content, status, create_time from message
    </sql>
    <sql id="insertFields">
        insert into message(from_id, to_id, conversation_id, content, status, create_time)
          values (#{fromId}, #{toId}, #{conversationId}, #{content}, #{status}, #{createTime})
    </sql>

<!--    查询指定用户的会话列表 -内容只显示最新一条私信-->
    <select id="selectConversationList" resultType="message">
        <include refid="selectFields"></include>
        where id in(
            select max(id) from message
            where status != 2
            and from_id != 1
            and (from_id = #{userId} or to_id = #{userId})
            group by conversation_id
        )
        order by id desc
        limit #{offset}, #{limit}
    </select>
  • 2.查询指定用户会话总数
<!--    查询指定用户会话总数-->
    <select id="selectConversationCount" resultType="int">
        select count(id) from message
        where id in (
            select max(id) from message
            where status != 2
            and from_id != 1
            and (from_id = #{userId} or to_id = #{userId})
            group by conversation_id
        )
    </select>
  • 3.查询指定会话的两个用户之间的私信列表

    <!--    查询指定会话的两个用户之间的私信列表-->
        <select id="selectLetterList" resultType="message">
            <include refid="selectFields"></include>
            where status != 2
            and from_id != 1
            and conversation_id = #{conversationId}
            order by id desc
            limit #{offset}, #{limit}
        </select>
        
    
  • 4.查询指定会话的两个用户之间的私信总数

    <!--    查询指定会话的两个用户之间的私信总数-->
        <select id="selectLetterCount" resultType="int">
            select count(id) from message
            where status != 2
            and from_id != 1
            and conversation_id = #{conversationId}
        </select>
    
  • 5.询未读私信数量-动态条件查询-当前用户总的未读私信数量-当前会话的未读私信数量

    <!--    询未读私信数量-动态条件查询-当前用户总的未读私信数量-当前会话的未读私信数量-->
        <select id="selectUnreadCount" resultType="int">
            select count(id)
            from message
            where status = 0
            and from_id != 1
            and to_id = #{userId}
            <if test="conversationId != null">
                and conversation_id = #{conversationId}
            </if>
        </select>
    

测试

 @Test
    public void testSelectLetters() {
        int i = 0;
        List<Message> list = messageMapper.selectConversationList(111, 0, 20);
        for(Message message : list) {
            System.out.println((++i) + " : " + message);
        }

        System.out.println("111会话列表总数 " + messageMapper.selectConversationCount(111));

        list = messageMapper.selectLetterList("111_112", 0, 10);
        i = 0;
        for(Message message : list) {
            System.out.println((++i) + " : " + message);
        }

        System.out.println("111与112会话列表总数 " + messageMapper.selectLetterCount("111_112"));

        System.out.println("111未读私信总数 " + messageMapper.selectUnreadCount(131, null));
        System.out.println("111_131未读私信总数 " + messageMapper.selectUnreadCount(131, "111_131"));

    }
}
部分打印内容:
111会话列表总数 14
111与112会话列表总数 8
111未读私信总数 2
111_131未读私信总数 2

2.service层业务处理

@Service
public class MessageService {
    @Autowired
    MessageMapper messageMapper;

    // 查询会话列表
    public List<Message> findConversations(int userId, int offset, int limit) {
        return messageMapper.selectConversationList(userId, offset, limit);
    }

    // 查询会话总数
    public int findConversationCount(int userId) {
        return messageMapper.selectConversationCount(userId);
    }

    // 查询私信列表
    public List<Message> findLetters(String conversationId, int offset, int limit) {
        return messageMapper.selectLetterList(conversationId, offset, limit);
    }

    // 查询私信总数
    public int findLetterCount(String conversationId) {
        return messageMapper.selectLetterCount(conversationId);
    }

    // 查询未读私信总数
    public int findLetterUnreadCount(int userId, String conversationId) {
        return messageMapper.selectUnreadCount(userId, conversationId);
    }
}

3. Controller层处理请求

访问会话列表的请求

分页设置
// 分页信息
page.setLimit(5);
page.setPath("/letter/list");
page.setRows(messageService.findConversationCount(user.getId()));
拿到每条会话列表信息-封装显示
// 会话列表
// 拿到每条会话,同样,与前面帖子,评论类似,将与用户等信息封装在一块,用到List,Map结构
List<Message> conversationList = messageService.findConversations(
        user.getId(), page.getOffset(), page.getLimit());
List<Map<String, Object>> conversationVo = new ArrayList<>();
if(conversationList != null) {
    for(Message message : conversationList) {
        Map<String, Object> map = new HashMap<>();
        map.put("conversation", message);
        map.put("letterCount", messageService.findLetterCount(message.getConversationId()));
        map.put("unreadCount", messageService.findLetterUnreadCount(user.getId(), message.getConversationId()));
        // 查询用户,注意,要查到会话列表中的用户,不是当前用户
        int targetId = user.getId() == message.getFromId() ? message.getToId() : message.getFromId();
        map.put("target", userService.findUserById(targetId));
        conversationVo.add(map);
    }
    model.addAttribute("conversations", conversationVo);

整体代码如下:

/** 会话列表显示 */
@RequestMapping(path = "/letter/list", method = RequestMethod.GET)
public String getLetterList(Model model, Page page) {
    User user = hostHolder.getUser();

    // 分页信息
    page.setLimit(5);
    page.setPath("/letter/list");
    page.setRows(messageService.findConversationCount(user.getId()));

    // 会话列表
    // 拿到每条会话,同样,与前面帖子,评论类似,将与用户等信息封装在一块,用到List,Map结构
    List<Message> conversationList = messageService.findConversations(
            user.getId(), page.getOffset(), page.getLimit());
    List<Map<String, Object>> conversationVo = new ArrayList<>();
    if(conversationList != null) {
        for(Message message : conversationList) {
            Map<String, Object> map = new HashMap<>();
            map.put("conversation", message);
            map.put("letterCount", messageService.findLetterCount(message.getConversationId()));
            map.put("unreadCount", messageService.findLetterUnreadCount(user.getId(), message.getConversationId()));
            // 查询用户,注意,要查到会话列表中的用户,不是当前用户
            int targetId = user.getId() == message.getFromId() ? message.getToId() : message.getFromId();
            map.put("target", userService.findUserById(targetId));
            conversationVo.add(map);
        }
        model.addAttribute("conversations", conversationVo);

        // 查询所有未读消息数量
        int letterUnreadCount = messageService.findLetterUnreadCount(user.getId(), null);
        model.addAttribute("letterUnreadCount", letterUnreadCount);
    }

    return "/site/letter";
}

访问私信列表的请求

split("_")
  • 返回分割后的字符串数组
/**每个会话中的所有私信显示 */
@RequestMapping(path = "/letter/detail/{conversationId}", method = RequestMethod.GET)
public String getLetterDetail(@PathVariable("conversationId") String conversationId, Page page, Model model) {
    // 分页信息
    page.setLimit(5);
    page.setPath("/letter/detail/" + conversationId);
    page.setRows(messageService.findLetterCount(conversationId));

    // 私信列表
    List<Message> letterList = messageService.findLetters(conversationId,page.getOffset(), page.getLimit());
    List<Map<String, Object>> letterVo = new ArrayList<>();
    if(letterList != null) {
        // 拿到二级私信列表信息
        for(Message letter : letterList) {
            Map<String, Object> map = new HashMap<>();
            map.put("letter", letter);
            map.put("fromUser", userService.findUserById(letter.getFromId()));
            letterVo.add(map);
        }
        model.addAttribute("letters", letterVo);
        model.addAttribute("letterCount", messageService.findLetterCount(conversationId));

        // 私信目标,已经遍历完了,可以从之前遍历的里面取一条,如之前操作那样,也可以在遍历外从conversationId中获取
        model.addAttribute("target", getLetterTarget(conversationId));
    }

    return "site/letter-detail";
}

private User getLetterTarget(String conversationId) {
    String[] ids = conversationId.split("_");
    int id0 = Integer.parseInt(ids[0]);
    int id1 = Integer.parseInt(ids[1]);

    if(hostHolder.getUser().getId() == id0) {
        return userService.findUserById(id1);
    } else {
        return userService.findUserById(id0);
    }
}

4. View视图模板处理

首页-动态链接

  • 消息数目,由于index请求处理中没有查询私信列表,故,先不处理
<a class="nav-link position-relative" th:href="@{/letter/list}">消息<span class="badge badge-danger">12</span></a>

每个用户的所有私信会话列表

在这里插入图片描述

<!-- 选项 -->
<ul class="nav nav-tabs mb-3">
   <li class="nav-item">
      <a class="nav-link position-relative active" th:href="@{/letter/list}">朋友私信		<span class="badge badge-danger" th:text="${letterUnreadCount}" 			            th:if="${letterUnreadCount!=0}">3</span></a>
   </li>
   <li class="nav-item">
      <a class="nav-link position-relative" href="notice.html">系统通知<span class="badge badge-danger">27</span></a>
   </li>
</ul>
<!-- 私信会话列表 -->
<ul class="list-unstyled">
   <li class="media pb-3 pt-3 mb-3 border-bottom position-relative" th:each="map:${conversations}">
      <span class="badge badge-danger" th:text="${map.unreadCount}" th:if="${map.unreadCount!=0}">3</span>
      <a href="profile.html">
         <img th:src="${map.target.headerUrl}" class="mr-4 rounded-circle user-header" alt="用户头像" >
      </a>
      <div class="media-body">
         <h6 class="mt-0 mb-3">
            <span class="text-success" th:utext="${map.target.username}">落基山脉下的闲人</span>
            <span class="float-right text-muted font-size-12" th:text="${#dates.format(map.conversation.createTime,'yyyy-MM-dd HH:mm:ss')}">2019-04-28 14:13:25</span>
         </h6>
         <div>
            <a th:href="@{|/letter/detail/${map.conversation.conversationId}|}" th:utext="${map.conversation.content}">米粉车, 你来吧!</a>
            <ul class="d-inline font-size-12 float-right">
               <li class="d-inline ml-2"><a href="#" class="text-primary"><i th:text="${map.letterCount}">5</i>条会话</a></li>
            </ul>
         </div>
      </div>
   </li>
</ul>

每个会话的私信列表

定义返回的js函数
<div class="col-8">
   <h6><b class="square"></b><i class="text-success" th:utext="${target.username}">落基山脉下的闲人</i><i th:text="${letterCount}">1</i>条私信</h6>
</div>
<div class="col-4 text-right">
   <button type="button" class="btn btn-secondary btn-sm" onclick="back()">返回</button>
   <button type="button" class="btn btn-primary btn-sm" data-toggle="modal" data-target="#sendModal">给TA私信</button>
</div>
<script>
		function back() {
			location.href = CONTEXT_PATH + "/letter/list";
		}
</script>
私信列表
<li class="media pb-3 pt-3 mb-2" th:each="map:${letters}">
	<a href="profile.html">
	<img th:src="${map.fromUser.headerUrl}" class="mr-4 rounded-circle user-header" alt="用户头像" ></a>
	<div class="toast show d-lg-block" role="alert" aria-live="assertive" aria-atomic="true">
        <div class="toast-header">
            <strong class="mr-auto" th:utext="${map.fromUser.username}">落基山脉下的闲人</strong>
            <small th:text="${#dates.format(map.letter.createTime, 'yyyy-MM-dd HH:mm:ss')}">2019-04-25 15:49:32</small>
            <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
                <span aria-hidden="true">&times;</span>
            </button>
        </div>
        <div class="toast-body" th:utext="${map.letter.content}">
            君不见, 黄河之水天上来, 奔流到海不复回!
        </div>
	</div>
</li>

测试结果:

所有私信会话列表显示

在这里插入图片描述

一个会话的两个用户的私信列表

在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值