首页 > 代码库 > 6_2_站内信

6_2_站内信

站内信

一 需求描述

站内信是当前用户与所有用户间对话的一个功能。

站内信模块需实现三个功能:

1. 互相发送站内信(写出接口,没有单独页面,用@ResponseBody直接在Postman进行验证);

2. 显示当前用户的所有站内信(与多个用户间);

3. 显示当前用户与某个指定用户间的对话消息。

 

二 具体实现

实现流程:DB>Model>Dao>Service>Controller>HTML

1.DB创建表Message

技术分享
 1 DROP TABLE IF EXISTS `message`;
 2 CREATE TABLE `message`(
 3   `id` INT NOT NULL AUTO_INCREMENT,
 4    `from_id` INT NOT NULL,
 5    `to_id` INT NOT NULL ,
 6     `content` TEXT NOT NULL ,
 7     `created_date` DATETIME NOT NULL ,
 8     `has_read` INT NULL ,
 9     `conversation_id` VARCHAR(45) NOT NULL ,
10     PRIMARY KEY (`id`),
11      INDEX `conversation_index` (`conversation_id` ASC),
12     INDEX `created_date` (`created_date` ASC)
13 ) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8;
View Code

2. Model:

技术分享
package com.nowcoder.model;

import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * Created by Administrator on 2017/4/29.
 */
@Component
public class Message {
    private int id;
    private int fromId;
    private int toId;
    private String content;
    private Date createdDate;
    private int hasRead;
    private String conversationId;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getFromId() {
        return fromId;
    }

    public void setFromId(int fromId) {
        this.fromId = fromId;
    }

    public int getToId() {
        return toId;
    }

    public void setToId(int toId) {
        this.toId = toId;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public Date getCreatedDate() {
        return createdDate;
    }

    public void setCreatedDate(Date createdDate) {
        this.createdDate = createdDate;
    }

    public int getHasRead() {
        return hasRead;
    }

    public void setHasRead(int hasRead) {
        this.hasRead = hasRead;
    }

    public String getConversationId() {
        return conversationId;
    }

    public void setConversationId(String conversationId) {
        this.conversationId = conversationId;
    }
}
View Code

3. Dao:

int addMessage(Message message);发送站内信
List<Message> getConversationDetail(@Param("conversationId") String conversationId,
@Param("offset") int offset,
@Param("limit") int limit); : 获取与某个具体用户的站内信
List<Message> getConversationList(@Param("userId") int userId,
@Param("offset") int offset,
@Param("limit") int limit); : 获取站内信列表
int getConvesationUnreadCount(@Param("userId") int userId,
@Param("conversationId") String conversationId); :统计未读信息
技术分享
package com.nowcoder.dao;

import com.nowcoder.model.Message;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;

/**
 * Created by Administrator on 2017/4/29.
 */
@Mapper
public interface MessageDao {

    String TABLE_NAME = "message";
    String INSERT_FIELDS = "from_id, to_id, content, created_date, has_read, conversation_id";
    String SELECT_FIELDS = "id," + INSERT_FIELDS;

    @Insert({"insert into", TABLE_NAME, "(", INSERT_FIELDS, ") " +
            " values(#{fromId}, #{toId}, #{content}, #{createdDate}, #{hasRead}, #{conversationId})"})
    int addMessage(Message message);

    @Select({"select ", SELECT_FIELDS, " from ", TABLE_NAME,
            " where conversation_id=#{conversationId} order by id desc limit #{offset}, #{limit}"})
    List<Message> getConversationDetail(@Param("conversationId") String conversationId,
                                        @Param("offset") int offset,
                                        @Param("limit") int limit);

    @Select({"select", INSERT_FIELDS, " ,count(conversation_id) AS id from (select * from ",TABLE_NAME,
             " where from_id=#{userId} or to_id=#{userId} order by id desc) tt group by conversation_id " +
                     " order by created_date limit #{offset}, #{limit}"})
    List<Message> getConversationList(@Param("userId") int userId,
                                      @Param("offset") int offset,
                                      @Param("limit") int limit);

    /**
     * 获取当前用户与指定用户间(conversationId)的对应未读信息
     * @param userId
     * @return
     */
    @Select({"select count(has_read) from ", TABLE_NAME, " where has_read=0 and to_id=#{userId} and conversation_id=#{conversationId}"})
    int getConvesationUnreadCount(@Param("userId") int userId,
                                  @Param("conversationId") String conversationId);


}
View Code

4.Service:

技术分享
package com.nowcoder.service;

import com.nowcoder.dao.MessageDao;
import com.nowcoder.model.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * Created by Administrator on 2017/4/29.
 */
@Service
public class MessageService {

    @Autowired
    MessageDao messageDao;

    /**
     * 用户之间发送信息
     * @param message
     * @return
     */
    public int addMessage(Message message){
        return messageDao.addMessage(message);
    }

    public List<Message> getConversationDetail(String conversationId, int offset, int limit){
        return messageDao.getConversationDetail(conversationId, offset, limit);
    }

    public List<Message> getConversationList(int userId, int offset, int limit){
        return messageDao.getConversationList(userId, offset, limit);
    }

    /**
     * 获取当前用户与指定用户间(conversationId)的对应未读信息
     * @param userId
     * @return
     */
    public int getConvesationUnreadCount(int userId, String conversationId){
        return messageDao.getConvesationUnreadCount(userId, conversationId);
    }



}
View Code

5. Controller: MessageController

(1)发送信息:

    /**
     * 发送信息
     * 发送那一方的id +  收到信息的那一方 id =  会话conversationId
     *          : 即a发送给b 和 b 发送给a  的消息在a,b之间的站内信中心都能看见
     * @param fromId 发送那一方的id
     * @param toId 收到信息的那一方id
     * @param content
     * @return
     */
    @RequestMapping(path = {"/msg/addMsg/"}, method = RequestMethod.POST)
    @ResponseBody
    public String addMessage(@RequestParam("fromId") int fromId,
                             @RequestParam("toId") int toId,
                             @RequestParam("content") String content){
        try {
            Message message = new Message();
            message.setFromId(fromId);
            message.setToId(toId);
            message.setContent(content);
            message.setCreatedDate(new Date());
            message.setHasRead(0);// 0 代表未读 1 代表已读
            message.setConversationId(fromId < toId ? String.format("%d_%d", fromId, toId) : String.format("%d_%d", toId, fromId));

            messageService.addMessage(message);
            return ToutiaoUtil.getJSONString(0);
        }catch (Exception e){
            logger.error("私信发送失败" + e.getMessage());
            return  ToutiaoUtil.getJSONString(1, "私信发送失败!");
        }

    }

(2)获取当前用户站内信列表:

/**
     * 当前用户的所有站内信,包括与所有用户的信息
     * @param model
     * @return
     */
    @RequestMapping(path = {"/msg/list"}, method = {RequestMethod.GET})
    public String conversationList(Model model){

        try{
            int localUserId = hostHolder.getUser().getId();
            List<Message> conversationListList =
                    messageService.getConversationList(localUserId, 0, 20);
            List<ViewObject> conversationVOs = new ArrayList<>();
            for(Message msg : conversationListList){

                ViewObject vo = new ViewObject();
                vo.set("conversation", msg);

                //获取与当前用户对话的user信息
                //如果是我方发送(msg.getFromId() == localUserId),则user.id为消息发送的toId
                //如果不是我方发送(即我方为接收方),则user.id 为消息发送的fromId
                int targetId = (msg.getFromId() == localUserId) ? msg.getToId() : msg.getFromId();
                User user = userService.getUser(targetId);
                vo.set("user", user);

                int unread = messageService.getConvesationUnreadCount(localUserId, msg.getConversationId());
                vo.set("unread", unread);

                conversationVOs.add(vo);
            }
            model.addAttribute("conversations", conversationVOs);
        }catch (Exception e){
            logger.error("获取站内信列表失败! " + e.getMessage());
        }
        return "letter";
    }

(3)获取与指定用户间的站内信:

    /**
     * 当前用户与指定用户间的对话消息;
     * @param model
     * @param conversationId 与指定用户对话的id
     * @return
     */
    @RequestMapping(path = {"/msg/detail"}, method = {RequestMethod.GET})
        public String conversationDetail(Model model, @RequestParam("conversationId") String conversationId){

        try{
            //当前用户与多个用户的所有的站内信
            List<Message> conversationList = messageService.getConversationDetail(conversationId, 0 ,10);
            List<ViewObject> messages = new ArrayList<>();
            for(Message msg : conversationList){
                ViewObject vo = new ViewObject();
                vo.set("message", msg);

                //获取当前用户收到信息方的user(即获取我方收到所有信息对应的用户)
                User user = userService.getUser(msg.getFromId());
                if(user == null){
                    continue;
                }
                vo.set("headUrl", user.getHeadUrl());
                vo.set("userId", user.getId());
                messages.add(vo);
            }
            model.addAttribute("messages", messages);
        }catch (Exception e){
            logger.error("获取与用户的对话消息失败 !" + e.getMessage());
        }
        return "letterDetail";

    }

Controller所有的code:

技术分享
package com.nowcoder.controller;

import com.nowcoder.model.HostHolder;
import com.nowcoder.model.Message;
import com.nowcoder.model.User;
import com.nowcoder.model.ViewObject;
import com.nowcoder.service.MessageService;
import com.nowcoder.service.UserService;
import com.nowcoder.util.ToutiaoUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by Administrator on 2017/4/29.
 */
@Controller
public class MessageController {

    private static final Logger logger = LoggerFactory.getLogger(MessageController.class);

    @Autowired
    MessageService messageService;
    @Autowired
    UserService userService;
    @Autowired
    HostHolder hostHolder;

    /**
     * 当前用户的所有站内信,包括与所有用户的信息
     * @param model
     * @return
     */
    @RequestMapping(path = {"/msg/list"}, method = {RequestMethod.GET})
    public String conversationList(Model model){

        try{
            int localUserId = hostHolder.getUser().getId();
            List<Message> conversationListList =
                    messageService.getConversationList(localUserId, 0, 20);
            List<ViewObject> conversationVOs = new ArrayList<>();
            for(Message msg : conversationListList){

                ViewObject vo = new ViewObject();
                vo.set("conversation", msg);

                //获取与当前用户对话的user信息
                //如果是我方发送(msg.getFromId() == localUserId),则user.id为消息发送的toId
                //如果不是我方发送(即我方为接收方),则user.id 为消息发送的fromId
                int targetId = (msg.getFromId() == localUserId) ? msg.getToId() : msg.getFromId();
                User user = userService.getUser(targetId);
                vo.set("user", user);

                int unread = messageService.getConvesationUnreadCount(localUserId, msg.getConversationId());
                vo.set("unread", unread);

                conversationVOs.add(vo);
            }
            model.addAttribute("conversations", conversationVOs);
        }catch (Exception e){
            logger.error("获取站内信列表失败! " + e.getMessage());
        }
        return "letter";
    }
    /**
     * 当前用户与指定用户间的对话消息;
     * @param model
     * @param conversationId 与指定用户对话的id
     * @return
     */
    @RequestMapping(path = {"/msg/detail"}, method = {RequestMethod.GET})
        public String conversationDetail(Model model, @RequestParam("conversationId") String conversationId){

        try{
            //当前用户与多个用户的所有的站内信
            List<Message> conversationList = messageService.getConversationDetail(conversationId, 0 ,10);
            List<ViewObject> messages = new ArrayList<>();
            for(Message msg : conversationList){
                ViewObject vo = new ViewObject();
                vo.set("message", msg);

                //获取当前用户收到信息方的user(即获取我方收到所有信息对应的用户)
                User user = userService.getUser(msg.getFromId());
                if(user == null){
                    continue;
                }
                vo.set("headUrl", user.getHeadUrl());
                vo.set("userId", user.getId());
                messages.add(vo);
            }
            model.addAttribute("messages", messages);
        }catch (Exception e){
            logger.error("获取与用户的对话消息失败 !" + e.getMessage());
        }
        return "letterDetail";

    }

    /**
     * 发送信息
     * 发送那一方的id +  收到信息的那一方 id =  会话conversationId
     *          : 即a发送给b 和 b 发送给a  的消息在a,b之间的站内信中心都能看见
     * @param fromId 发送那一方的id
     * @param toId 收到信息的那一方id
     * @param content
     * @return
     */
    @RequestMapping(path = {"/msg/addMsg/"}, method = RequestMethod.POST)
    @ResponseBody
    public String addMessage(@RequestParam("fromId") int fromId,
                             @RequestParam("toId") int toId,
                             @RequestParam("content") String content){
        try {
            Message message = new Message();
            message.setFromId(fromId);
            message.setToId(toId);
            message.setContent(content);
            message.setCreatedDate(new Date());
            message.setHasRead(0);// 0 代表未读 1 代表已读
            message.setConversationId(fromId < toId ? String.format("%d_%d", fromId, toId) : String.format("%d_%d", toId, fromId));

            messageService.addMessage(message);
            return ToutiaoUtil.getJSONString(0);
        }catch (Exception e){
            logger.error("私信发送失败" + e.getMessage());
            return  ToutiaoUtil.getJSONString(1, "私信发送失败!");
        }

    }

}
View Code

对应HTML:

所有站内信letter.html:

技术分享
#parse("header.html")
    <div id="main">
        <div class="container">
            <ul class="letter-list">
                #foreach($conversation in $conversations)
                <li id="conversation-item-10005_622873">
                    <a class="letter-link" href="/msg/detail?conversationId=$!{conversation.conversation.conversationId}"></a>
                    <div class="letter-info">
                        <span class="l-time">$!date.format(‘yyyy-MM-dd HH:mm:ss‘, $!{conversation.conversation.createdDate})</span>
                        <div class="l-operate-bar">
                            <a href="javascript:void(0);" class="sns-action-del" data-id="10005_622873">
                            删除
                            </a>
                            <a href="/msg/detail?conversationId=$!{conversation.conversation.conversationId}">
                                共$!{conversation.conversation.id}条会话
                            </a>
                        </div>
                    </div>
                    <div class="chat-headbox">
                        <span class="msg-num">
                            $!{conversation.unread}
                        </span>
                        <a class="list-head" href="/user/$!{conversation.user.id}">
                            <img alt="头像" src="$!{conversation.user.headUrl}">
                        </a>
                    </div>
                    <div class="letter-detail">
                        <a title="$!{conversation.user.name}" class="letter-name level-color-1">
                            $!{conversation.user.name}
                        </a>
                        <p class="letter-brief">
                            <a href="/msg/detail?conversationId=$!{conversation.conversation.conversationId}">
                                $!{conversation.conversation.content}
                            </a>
                        </p>
                    </div>
                </li>
                #end
            </ul>

        </div>
        <script type="text/javascript">
          $(function(){

            // If really is weixin
            $(document).on(WeixinJSBridgeReady, function() {

              $(.weixin-qrcode-dropdown).show();

              var options = {
                "img_url": "",
                "link": "http://nowcoder.com/j/wt2rwy",
                "desc": "",
                "title": "读《Web 全栈工程师的自我修养》"
              };

              WeixinJSBridge.on(menu:share:appmessage, function (argv){
                WeixinJSBridge.invoke(sendAppMessage, options, function (res) {
                  // _report(‘send_msg‘, res.err_msg)
                });
              });

              WeixinJSBridge.on(menu:share:timeline, function (argv) {
                WeixinJSBridge.invoke(shareTimeline, options, function (res) {
                  // _report(‘send_msg‘, res.err_msg)
                });
              });

              // $(window).on(‘touchmove scroll‘, function() {
              //   if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) {
              //     $(‘div.backdrop‘).show();
              //     $(‘div.share-help‘).show();
              //   } else {
              //     $(‘div.backdrop‘).hide();
              //     $(‘div.share-help‘).hide();
              //   }
              // });

            });

          })
        </script>
    </div>
#parse("footer.html")
View Code

与某个用户的站内信letterDetail.html:

技术分享
#parse("header.html")
    <div id="main">
        <div class="container">
            <ul class="letter-chatlist">
                #foreach($msg in $messages)
                    <li id="msg-item-4009580">
                        <a class="list-head" href="/user/$!{msg.userId}">
                            <img alt="头像" src="$!{msg.headUrl}">
                        </a>
                        <div class="tooltip fade right in">
                        <div class="tooltip-arrow"></div>
                        <div class="tooltip-inner letter-chat clearfix">
                        <div class="letter-info">
                        <p class="letter-time">$date.format(‘yyyy-MM-dd HH:mm:ss‘, $!{msg.message.createdDate})</p>
                        <a href="javascript:void(0);" id="del-link" name="4009580">删除</a>
                        </div>
                        <p class="chat-content">
                            $!{msg.message.content}
                        </p>
                        </div>
                        </div>
                    </li>
                #end
            </ul>

        </div>
        <script type="text/javascript">
          $(function(){

            // If really is weixin
            $(document).on(WeixinJSBridgeReady, function() {

              $(.weixin-qrcode-dropdown).show();

              var options = {
                "img_url": "",
                "link": "http://nowcoder.com/j/wt2rwy",
                "desc": "",
                "title": "读《Web 全栈工程师的自我修养》"
              };

              WeixinJSBridge.on(menu:share:appmessage, function (argv){
                WeixinJSBridge.invoke(sendAppMessage, options, function (res) {
                  // _report(‘send_msg‘, res.err_msg)
                });
              });

              WeixinJSBridge.on(menu:share:timeline, function (argv) {
                WeixinJSBridge.invoke(shareTimeline, options, function (res) {
                  // _report(‘send_msg‘, res.err_msg)
                });
              });

              // $(window).on(‘touchmove scroll‘, function() {
              //   if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) {
              //     $(‘div.backdrop‘).show();
              //     $(‘div.share-help‘).show();
              //   } else {
              //     $(‘div.backdrop‘).hide();
              //     $(‘div.share-help‘).hide();
              //   }
              // });

            });

          })
        </script>
    </div>
#parse("footer.html")
View Code

效果图:

当前用户所有的站内信列表:

技术分享

 

 与某个具体用户的站内信:

技术分享

 

 

 

 

 

 

 

 

 

 

 

6_2_站内信