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;

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;
    }
}

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);

}

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);
    }

}

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, "私信发送失败!");
        }

    }

}

对应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")

与某个用户的站内信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")

效果图:

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

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

时间: 2024-08-25 17:51:34

6_2_站内信的相关文章

开源 免费 java CMS - FreeCMS1.9 站内信

项目地址:http://www.freeteam.cn/ 站内信 管理站内信数据. 1. 站内信 从左侧管理菜单点击站内信进入. 2. 发送 系统管理员可以直接发送站内信给会员. 点击"发送"按钮. 选择需要发送的会员,输入标题内容后点击"发送"按钮. 3. 查看 在站内信列表中直接点击标题可以查看站内信的详细信息. 4. 删除 选择需要删除的站内信,然后点击"删除"按钮. 提示:同时可以删除多个站内信. 为了防止误操作,系统会提示用户是否删除,

ASP.NET 实现站内信功能(点对点发送,管理员群发)

正好这段时间在研究这个功能,还是得感谢这位大神,没有他的引路,我就不可能把站内信做出来. http://www.cnblogs.com/grenet/archive/2010/03/08/1680655.html 哈哈,再次感谢. 我们先来说说单点传送站内信,所谓的单点传送,就是用户与用户之间的短信发送,这里的用户可以是一个人,也可以是多个人, 上面的文章大家如果看了的话,想必有一个疑问,就是用户的阅读状态怎么定义? 我这里给大家一种解决方案,再建立一张表,也就是说,建立一张用户阅读状态表,因为

2015/05/13~研发站内信中遇到的问题

1.业务场景 管理员向其他管理员.商家.用户发送站内信,商家向用户发送站内信. 在用户基数百万级时,每次全站的站内信,都面临百万级数据的插入:用户每次访问都面临大量数据的查询操作... 2.调研 mysql单库最大容量: mysql单表最大容量: mongodb性能测试: 3.设计方案一 消息表: 消息id,消息主题,消息内容,发送时间 消息用户表

开源 免费 java CMS - FreeCMS2.1 会员站内信

项目地址:http://www.freeteam.cn/ 站内信 1.1.1 写信 从左侧管理菜单点击写信进入. 输入收信人.标题.内容后点击发送按钮. 1.1.2 收件箱 从左侧管理菜单点击收件箱进入. 查看站内信 点击标题可以查看站内信详细内容. 删除站内信 选择站内信然后点击删除按钮可以完成删除操作. 为了防止误操作,系统会提示用户是否删除,点击"确定"完成删除操作. 1.1.3 发件箱 从左侧管理菜单点击发件箱进入. 查看站内信 点击标题可以查看站内信详细内容. 删除站内信 选

开源 java CMS - FreeCMS2.3 站内信

原文地址:http://javaz.cn/site/javaz/site_study/info/2015/25048.html 项目地址:http://www.freeteam.cn/ 站内信 管理站内信数据. 站内信 从左侧管理菜单点击站内信进入. 发送 系统管理员可以直接发送站内信给会员. 点击"发送"按钮. 选择需要发送的会员,输入标题内容后点击"发送"按钮. 查看 在站内信列表中直接点击标题可以查看站内信的详细信息. 删除 选择需要删除的站内信,然后点击&q

开源 java CMS - FreeCMS2.3会员站内信

原文地址:http://javaz.cn/site/javaz/site_study/info/2015/30531.html 项目地址:http://www.freeteam.cn/ 站内信 写信 从左侧管理菜单点击写信进入. 输入收信人.标题.内容后点击发送按钮. 收件箱 从左侧管理菜单点击收件箱进入. 查看站内信 点击标题可以查看站内信详细内容. 删除站内信 选择站内信然后点击删除按钮可以完成删除操作. 为了防止误操作,系统会提示用户是否删除,点击"确定"完成删除操作. 发件箱

【设计用例】站内信

[站内信]也被称作[站内消息],如果验证它呢?需要考虑的无非是2个方面: 功能验证 - 是否覆盖了所有的场景(开发人员会在代码里注释出来 会触发站内消息的场景 把这些沟通并记录下来 以免设计用例有所遗漏) 样式验证 - 站内消息里面包含的文字描述是否得到了正常的展示

站内信

http://daihaixiang.blog.163.com/blog/static/3830134201111155381735/ 如果一个网站到了百万级的用户量了,那我不得不膜拜该网站和网站经营者了,因为经营这样的网站一直是笔者的梦想:)好了,回归正题,如果这样的系统 放你面前,让你设计一个站内信群发数据库,你该何去何从,总之,上面两种常规的办法肯定是行不通了的,因为庞大的数据量会让消息表撑爆,即使你分区也无济 于事.这时候作为一个系统架构师的你,可能不仅仅要从技术的角度去考虑这个问题,更

“站内信”的实现

站内信分为"点到点"和"点到面","点到点"属于私信,用户之间传递的信息,一对一传递."点到面",属于系统消息或者公共信息,属于一对多发送. 站内信的设计既要考虑到投递的准确性(也就是该收到的人能收到信息),也要考虑信息持久化存储空间占用问题,在他的第一篇博文中详细进行了介绍. 我们在此仅把第三种情况拿出来说明,也就是用户量为百万级,活跃用户只占其中的一部分. 数据库的设计: 表名:Message ID:编号:SendID:发