java 基于servlet+maven+mybatis+websocket聊天,包括单聊和群聊

运行环境:jdk1.8

                  apache-tomcat-8.0.30

功能:发送文本、qq表情、图片、视频的组合消息,发送语音,语音支持(火狐,谷歌,ie,360等浏览器),发送附件消息并可以下载,消息撤回

对手机做单独的web页面适配。

运行效果图-电脑版:

单聊界面:


群聊界面:


手机端-效果图:


框架搭建:

1.新建maven项目


maven依赖
          <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
        </dependency>
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>taglibs</groupId>
            <artifactId>standard</artifactId>
            <version>1.1.2</version>
        </dependency>
        <!-- mybatis包 -->
       <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.0.18</version>
            </dependency>
        <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis</artifactId>
          <version>3.1.1</version>
        </dependency>
        <dependency>  
            <groupId>junit</groupId>  
            <artifactId>junit</artifactId>  
            <version>4.11</version>  
            <scope>test</scope>  
        </dependency>
        <dependency>
         <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
         <version>5.1.27</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.15</version>
        </dependency>
        <!-- log4j需要的jar包 -->  
          <dependency>  
              <groupId>log4j</groupId>  
              <artifactId>log4j</artifactId>
              <version>1.2.17</version>
          </dependency>
         <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.22</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.22</version>
        </dependency>
        <dependency>
            <groupId>net.sf.json-lib</groupId>
            <artifactId>json-lib</artifactId>
            <version>2.4</version>  
            <classifier>jdk15</classifier><!--指定jdk版本-->  
        </dependency>
        <!-- servlet -->
        <!--         文件上传 -->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.5</version>
        </dependency>
            <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.7</version>
        </dependency>

    需要在build path中单独引入tomcat  lib下面的websocket-api.jar  文件

2.在web.xml中配置session过滤器 ,用于登录超时跳转到登录页面

<filter>
    <filter-name>SessionFilter</filter-name>
    <filter-class>com.zkkj.chat.filter.SessionFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>SessionFilter</filter-name>

    <url-pattern>/group/*</url-pattern>
    <url-pattern>/loginSuccess/*</url-pattern>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>REQUEST</dispatcher>
  </filter-mapping>


SessionFilter.java

import java.io.IOException;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class SessionFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // TODO Auto-generated method stub

    }

    @Override
    public void doFilter(ServletRequest arg0, ServletResponse arg1,
            FilterChain arg2) throws IOException, ServletException {
        // TODO Auto-generated method stub
        HttpServletRequest request = (HttpServletRequest) arg0;
        HttpServletResponse response = (HttpServletResponse) arg1;
        HttpSession session = request.getSession();
        //判断sessin是否过期
        Map userMap = (Map) session.getAttribute("userSession");
        if(userMap == null){
            //跳转至登录页面
            response.sendRedirect("/chat/index");
        }else{
            arg2.doFilter(request, response);
        }
    }

    @Override
    public void destroy() {
        // TODO Auto-generated method stub

    }

}

3.在web.xml中配置监听器,用于项目启动后加载配置文件,启动定时器检测用户在线状态等操作

<listener>
    <description>ServletContextListener</description>
    <listener-class>com.zkkj.chat.listener.ContextInitListener</listener-class>
</listener>

ContextInitListener.java

import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

public class ContextInitListener implements ServletContextListener {

    private IUserService userService = new UserServiceImpl();
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        // TODO Auto-generated method stub
//        加载classpath下的配置文件
        InputStream in = getClass().getClassLoader().getResourceAsStream("config.properties");
        if(in == null){
            return;
        }
        Properties prop = new Properties();
        Map<String,String> propertiesMap=new HashMap<String, String>();
        try {    
             prop.load(in);    
             Iterator<String> it=prop.stringPropertyNames().iterator();
             while(it.hasNext()){
                 String key = it.next();
                 String value = prop.getProperty(key);
                 propertiesMap.put(key, value);
             }
             in.close();
        } catch (IOException e) {
              e.printStackTrace();    
        }
        PropertiesConfig.setPropertiesMap(propertiesMap);
    }
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        // TODO Auto-generated method stub
        
    }
}

项目启动后 进入ContextInitListener监听器 ,将config.properties文件内容读取到PropertiesConfig类中propertiesMap对象中,通过PropertiesConfig.get("");获取config.properties文件中的值

PropertiesConfig.java

import java.util.Map;

public class PropertiesConfig {
    private static Map<String,String> propertiesMap;
    public static String getProperties(String key){
        return propertiesMap.get(key);
    }
    public static Map<String, String> getPropertiesMap() {
        return propertiesMap;
    }
    public static void setPropertiesMap(Map<String, String> propertiesMap) {
        PropertiesConfig.propertiesMap = propertiesMap;
    }
}

config.properties

#websocket连接地址
websockt_url = wss://192.168.10.54:8443/chat/webSocket
#资源访问地址
return_path_z  = https://192.168.10.54:8443/

#资源下载地址

return_path_z1 = http://192.168.10.54:8088/

#tomcat虚拟路径

#base_upload_path = /usr/local/data/

base_upload_path = D:/data/
#servlet 上传缓存路径
cache_path = cache/
#图片保存路径
image_path = image/
#文件保存路径
attachment_path = file/
#视频保存路径
video_path = video/
#语音保存路径
audio_path = audio/

#mysql 数据库连接
jdbc.driver = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/chat?useUnicode=true&autoReconnect=true&characterEncoding=utf-8
jdbc.username = root
jdbc.password = 123456

log4j.properties

log4j.configuration= log4j.properties
log4j.rootCategory=INFO,DLOGFILE,CONSOLE,ERROR

log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=DEBUG
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=-%-d{yyyyMMdd-HH:mm:ss} %t %c %m%n

log4j.appender.DLOGFILE=org.apache.log4j.DailyRollingFileAppender
log4j.appender.DLOGFILE.File=../logs/log.log
log4j.appender.DLOGFILE.Append=true
log4j.appender.DLOGFILE.DatePattern='.'yyyy-MM-dd
log4j.appender.DLOGFILE.Threshold=ALL
log4j.appender.DLOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.DLOGFILE.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} %m%n

log4j.logger.com.mybatis=DEBUG  
log4j.logger.com.mybatis.common.jdbc.SimpleDataSource=DEBUG  
log4j.logger.com.mybatis.common.jdbc.ScriptRunner=DEBUG  
log4j.logger.com.mybatis.sqlmap.engine.impl.SqlMapClientDelegate=DEBUG  
log4j.logger.java.sql.Connection=DEBUG  
log4j.logger.java.sql.Statement=DEBUG  
log4j.logger.java.sql.PreparedStatement=DEBUG</strong>

4.集成mybatis数据库操作

4.1 封装mybatis通用dao接口和实现类

IBaseDao.java

import java.io.Serializable;
import java.util.List;
import java.util.Map;

import org.apache.poi.ss.formula.functions.T;

import com.zkkj.chat.util.Page;


public interface IBaseDao<T,PK extends Serializable> {
    /**  
     * 插入对象
     */
    int insert(String statementName,T obj);
    /**  
     * 根据主键删除对象
     */
    int delete(String statementName, PK primaryKey);
    /**  
     * 根据条件删除
     */    
    int deleteByParam(String statementName,Map obj);
    /**
     * 更新实体
     * @param entity
     * @return
     */
    int update(String statementName, T entity);
    /**  
     * 根据条件更新
     */
    int updateByParam(String statementName,Object obj);
    
    T get(String statementName,PK primaryKey);
    /**  
     * 查询单条数据
     */
    T selectOneByParam(String statementName,Map obj);
    /**
     * 查询全部
     * @param statementName
     * @return
     */
    List<T> selectList(String statementName);
    /**  
     * 查询多条不分页
     */
    List<T> selectList(String statementName,Map obj);
    /**  
     * 查询多条分页
     */
    List<T> selectList(String statementName,Map obj,Page page);
    /**  
     * 查询总记录数
     */
    int getCount(String statementName,Object obj);
     /**  
     * 批量插入  
     * @param list  
     */    
    int insertBatch(String statementName, final List<T> list);    
        
    /**  
     * 批量修改  
     * @param list  
     */    
    int updateBatch(String statementName, final List<T> list);    
        
    /**  
     * 批量删除  
     * @param list  
     */    
    int deleteBatch(String statementName, final List<PK> list);
}

BaseDaoImpl.java

import java.io.Serializable;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import com.zkkj.chat.util.MybatisUtil;
import com.zkkj.chat.util.Page;

public class BaseDaoImpl<T, PK extends Serializable> implements IBaseDao<T, PK> {
    
    @Override
    public int insert(String statementName, T obj) {
        // TODO Auto-generated method stub
        SqlSession session = MybatisUtil.getSqlSession();
        int count = 0;
        try {
            count = session.insert(statementName, obj);
            session.commit();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            System.out.println(e.getMessage());
            
        }finally{
            MybatisUtil.closeSqlSession();
        }
        return count;
    }
    @Override
    public int delete(String statementName, PK primaryKey) {
        // TODO Auto-generated method stub
        SqlSession session = MybatisUtil.getSqlSession();
        int count  = 0;
        try {
            count = session.delete(statementName, primaryKey);
            session.commit();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            System.out.println(e.getMessage());
        }finally{
            MybatisUtil.closeSqlSession();
        }
        return count;
    }
    @Override
    public int deleteByParam(String statementName, Map obj) {
        // TODO Auto-generated method stub
        SqlSession session = MybatisUtil.getSqlSession();
        int count  = 0;
        try {
            count = session.delete(statementName, obj);
            session.commit();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            System.out.println(e.getMessage());
        }finally{
            MybatisUtil.closeSqlSession();
        }
        return count;
    }


    @Override
    public int update(String statementName, T entity) {
        // TODO Auto-generated method stub
        SqlSession session = MybatisUtil.getSqlSession();
        int count  = 0;
        try {
            count = session.update(statementName, entity);
            session.commit();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            System.out.println(e.getMessage());
        }finally{
            MybatisUtil.closeSqlSession();
        }
        return count;
    }
    @Override
    public int updateByParam(String statementName, Object obj) {
        // TODO Auto-generated method stub
        SqlSession session = MybatisUtil.getSqlSession();
        int count  = 0;
        try {
            count = session.update(statementName, obj);
            session.commit();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            System.out.println(e.getMessage());
        }finally{
            MybatisUtil.closeSqlSession();
        }
        return count;
    }


    @Override
    public T get(String statementName, PK primaryKey) {
        // TODO Auto-generated method stub
        SqlSession session = MybatisUtil.getSqlSession();
        T object = null;
        try {
            object =  session.selectOne(statementName, primaryKey);
            session.commit();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            System.out.println(e.getMessage());
        }finally{
            MybatisUtil.closeSqlSession();
        }
        return object;
    }
    
    @Override
    public T selectOneByParam(String statementName, Map obj) {
        // TODO Auto-generated method stub
        SqlSession session = MybatisUtil.getSqlSession();
        T object = null;
        try {
            object =  session.selectOne(statementName, obj);
            session.commit();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            System.out.println(e.getMessage());
        }finally{
            MybatisUtil.closeSqlSession();
        }
        return object;
    }

    @Override
    public List<T> selectList(String statementName) {
        // TODO Auto-generated method stub
        SqlSession session = MybatisUtil.getSqlSession();
        List<T> list  = null;
        try {
            list = session.selectList(statementName);
            session.commit();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            System.out.println(e.getMessage());
        }finally{
            MybatisUtil.closeSqlSession();
        }
        return list;
    }
    @Override
    public List<T> selectList(String statementName, Map obj) {
        // TODO Auto-generated method stub
        SqlSession session = MybatisUtil.getSqlSession();
        List<T> list  = null;
        try {
            list = session.selectList(statementName, obj);
            session.commit();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            System.out.println(e.getMessage());
        }finally{
            MybatisUtil.closeSqlSession();
        }
        return list;
    }

    @Override
    public List<T> selectList(String statementName, Map obj,
            Page page) {
        // TODO Auto-generated method stub
        SqlSession session = MybatisUtil.getSqlSession();
        List<T> list  = null;
        try {
            list = session.selectList(statementName, obj, new RowBounds(page.getStartItem(),page.getNumPerPage()));
            session.commit();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            System.out.println(e.getMessage());
        }finally{
            MybatisUtil.closeSqlSession();
        }
        return list;
    }

    @Override
    public int getCount(String statementName, Object obj) {
        // TODO Auto-generated method stub
        SqlSession session = MybatisUtil.getSqlSession();
        int count = 0;
        try {
            count = session.selectOne(statementName,obj);
            session.commit();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            System.out.println(e.getMessage());
        }finally{
            MybatisUtil.closeSqlSession();
        }
        return count;
    }

    @Override
    public int insertBatch(String statementName, List<T> list) {
        // TODO Auto-generated method stub
        SqlSession session = MybatisUtil.getSqlSession();
        int count  = 0;
        try {
            count = session.insert(statementName, list);
            session.commit();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            System.out.println(e.getMessage());
        }finally{
            MybatisUtil.closeSqlSession();
        }
        return count;
    }

    @Override
    public int updateBatch(String statementName, List<T> list) {
        // TODO Auto-generated method stub
        SqlSession session = MybatisUtil.getSqlSession();
        int count  = 0;
        try {
            count = session.update(statementName, list);
            session.commit();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            System.out.println(e.getMessage());
        }finally{
            MybatisUtil.closeSqlSession();
        }
        return count;
    }

    @Override
    public int deleteBatch(String statementName, List<PK> list) {
        // TODO Auto-generated method stub
        SqlSession session = MybatisUtil.getSqlSession();
        int count  = 0;
        try {
            count = session.delete(statementName, list);
            session.commit();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            System.out.println(e.getMessage());
        }finally{
            MybatisUtil.closeSqlSession();
        }
        return count;
    }
}

MybatisUtil.java

import java.io.IOException;
import java.io.Reader;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class MybatisUtil {
    private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<SqlSession>();  
    private static SqlSessionFactory sqlSessionFactory;  
    /**
     * 加载位于src/mybatis.xml配置文件
     */  
    static{  
        try {  
            Reader reader = Resources.getResourceAsReader("chat/mybatis-config.xml");  
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);  
        } catch (IOException e) {  
            e.printStackTrace();  
            throw new RuntimeException(e);  
        }  
    }  
    /**
     * 禁止外界通过new方法创建  
     */  
    private MybatisUtil(){
    }
    /**
     * 获取SqlSession
     */  
    public static SqlSession getSqlSession(){  
        //从当前线程中获取SqlSession对象  
        SqlSession sqlSession = threadLocal.get();
        //如果SqlSession对象为空  
        if(sqlSession == null){  
            //在SqlSessionFactory非空的情况下,获取SqlSession对象  
            sqlSession = sqlSessionFactory.openSession();  
            //将SqlSession对象与当前线程绑定在一起  
            threadLocal.set(sqlSession);  
        }  
        //返回SqlSession对象  
        return sqlSession;  
    }  
    /**
     * 关闭SqlSession与当前线程分开
     */  
    public static void closeSqlSession(){  
        //从当前线程中获取SqlSession对象  
        SqlSession sqlSession = threadLocal.get();  
        //如果SqlSession对象非空  
        if(sqlSession != null){  
            //关闭SqlSession对象  
            sqlSession.close();  
            //分开当前线程与SqlSession对象的关系,目的是让GC尽早回收  
            threadLocal.remove();  
        }  
    }  
}

4.2  mybatis 配置数据源

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
 <properties resource="config.properties"></properties>
 <!-- 数据库配置信息 -->
 <environments default="development">
 <environment id="development">
  <transactionManager type="JDBC"/>
  <dataSource type="POOLED">
  <property name="driver" value="${jdbc.driver}"/>
  <property name="url" value="${jdbc.url}"/>
  <property name="username" value="${jdbc.username}"/>
  <property name="password" value="${jdbc.password}"/>
  <property name="poolPingEnabled" value="true"/>  
  <property name="poolPingQuery" value="select 1"/>
  <property name="poolPingConnectionsNotUsedFor" value="3600000"/>   
  </dataSource>
 </environment>
 </environments>
 
 <!-- 映射文件 -->
 <mappers>
     <mapper resource="com/zkkj/chat/dao/UserInfoMapper.xml"/>
     <mapper resource="com/zkkj/chat/dao/GroupInfoMapper.xml"/>
     <mapper resource="com/zkkj/chat/dao/MessageInfoMapper.xml"/>
 </mappers>
</configuration>


UserInfoMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="userInfo" >
    <!--     查询广告商列表 -->
    <select id="getUserList"  parameterType="java.util.Map" resultType="java.util.Map">
        select
        *
        from tb_user
        where 1 = 1
        <if test="account != null and account != ''">
        and account = #{account}
        </if>
        <if test="nickName != null and nickName != ''">
        and nickName = #{nickName}
        </if>
        <if test="password != null and password != ''">
        and password = md5(#{password})
        </if>
        <if test="onlineStatus != null and onlineStatus != ''">
        and onlineStatus = #{onlineStatus}
        </if>
    </select>
    <select id="selectUserById" parameterType="java.util.Map" resultType="java.util.Map">
        select * from tb_user
        where id = #{userId}
    </select>
    <!--     添加用户信息 -->
    <insert id="addUser"   parameterType="java.util.Map"  useGeneratedKeys="true" keyProperty="id">
        insert into tb_user(account,password,nickName,headUrl)
        values (#{account},#{password},#{nickName},#{headUrl})
    </insert>
    <!--     更新用户信息 -->
    <update id="updateUser" parameterType="java.util.Map">
        update tb_user
        <set >
          <if test="nickName != null and nickName != ''" >
            nickName = #{nickName,jdbcType=VARCHAR},
          </if>
          <if test="onlineStatus != null  and onlineStatus != ''">
            onlineStatus = #{onlineStatus},
          </if>
          <if test="password != null  and password != ''">
            password = #{password},
          </if>
        </set>
        where id = #{userId}
    </update>
    <!--     删除用户 -->
    <delete id="deleteUser" parameterType ="java.util.Map">
        delete from tb_user where  id = #{userId}
    </delete>
</mapper>


框架搭建完毕!

前端jsp

1.首页index.jsp

 项目启动后跳转到index.jsp页面,js判断浏览器的类型跳转到的不同的页面

 判断浏览器是pc端还是移动端的方法如下:

<script type="text/javascript">
function IsPC() {
    var userAgentInfo = navigator.userAgent;
    var Agents = ["Android", "iPhone",
                "SymbianOS", "Windows Phone",
                "iPad", "iPod"];
    var flag = true;
    for (var v = 0; v < Agents.length; v++) {
        if (userAgentInfo.indexOf(Agents[v]) > 0) {
            flag = false;
            break;
        }
    }
    return flag;
}
function toUrl(){
    if(IsPC()){
        //跳转到电脑登登录页面
        window.location.href="<%=path%>/index";
    }else{
        //跳转到手机登录页面
        window.location.href="<%=path%>/index?dev=mobile";
    }
}
</script>
</head>
<body οnlοad="toUrl()">
</body>

2.建立socket连接

websocket_url为配置文件配置的socket连接地址${fromUserId}为当前 用户登录的id。

jsp获取配置文件属性:

//引入java包的地址

<%@ page import="com.zkkj.chat.util.*,java.util.*,java.text.*"%>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://"
            + request.getServerName() + ":" + request.getServerPort()
            + request.getContextPath() + "/";
    //socket连接地址
    String websockt_url =  PropertiesConfig.getProperties("websockt_url");

    //资源访问地址

    String return_path_z =  PropertiesConfig.getProperties("return_path_z");
%>

//socket初始化方法:

var socket;

function initSocket(){
        console.log("dasdas");
        socket = new WebSocket("<%=websockt_url%>?userId=${fromUserId}");
        /*
         *监听三种状态的变化 。js会回调
         */
        socket.onopen = function(message) {
            //客户端连接成功,向后台发送心跳检测,每隔10秒发送一次
            
            setInterval(function (){
                 var json = {};
                  //定义json 发送消息  
                 var json = {};
                 json.message = "";
                 json.extra = "";
                 json.messageType = "4";//心跳消息
                 json.type = "${type}";//心跳消息
                 json.fromUserId = "${fromUserId}";
                 json.toUserId = "";
                 json.groupId = "";
                 socket.send(JSON.stringify(json));
            },3000);
        };
        socket.onclose = function(message) {
            socket.close();
        };
        socket.onmessage = function(message) {
            showMessage(message.data);
        };
        socket.onerror = function(message) {
            socket.close();
        };
    }


3.消息输入框的设计



首先想到的肯定是textarea  但是textarea没法插入图片和视频,所以我才用的是可编辑的div来实现,在div添加contenteditable="true"属性 就变成了可编辑的div了。

       <!--        功能输入区域 -->
       <div id="chatInputArea"  style="width:100%;min-height:18px;border:0px solid blue;margin:5px 0 3px 0;margin:10px 0 0 10px;">
               <div style="width:100%;min-height:30px;">
                    <div  id="textMsg" style ="width:100%;height:100px; border : 1px solid #ccc;overflow:auto;border-radius:5px;" contenteditable="true">
                    </div>
               </div>
       </div>


4.消息展示框的设计

聊天页面整体布局


群聊消息都是默认以此从上往下面排列好布局,现在说说单聊的左右布局方式

首先设置一个默认的用户id就是当前登录的用户id

<c:set value="${userSession.id }" var="curUserId"></c:set>

然后再循环后台消息记录的时候 判断用户id和当前id是否相同,如果相同 就排列在右侧 ,如果不同就排列在左侧


在页面初始化完成消息内容的时候,需要将滚动条滚动到底部,显示最新的消息

$('#showChatMessage').animate({scrollTop: $('#showChatMessage')[0].scrollHeight + 'px'}, 1000);

在每次发送完消息之后 还是调用这个方法 进行重新滚动滚动条

对于手机页面 其他的不变,只需要将最外层的元素的固定宽度设置为100%自适应就可以了。


5.功能图标的设计


点击功能图标就能打开文件选择器 ,而不是直接用比较难看的文件选择器,可以采用间接的方式,在图片下面隐藏一个文件选择元素,当点击图片的时候,调用隐藏文件选择器的click方法就可以了。

     <!-- 选择图片 -->
    <img class="image" src="<%=path %>/chat/image/image.png" style="cursor:pointer;border:0px solid gray;padding:2px;" title="图片消息"/>
    <div style="width:200px;height:30px;border:1px solid gray;background-color:white;position:absolute;margin:0 0 0 25px;display:none;">
         <div style="width:100%;min-height:10px;">
             <input id="file1"  name="file1" type="file" value="" οnchange="file1Upload()" style="height:30px;"/>
         </div>
    </div>

//当点击图片时触发 文件选择器
   $(".image,.video,.attchment").click(function(){
       $(this).next().find("input[type='file']").click();
   });


6.文件上传插件

文件上传采用ajaxfileupload.js插件,需要引入插件库文件,上传后台返回文件的数据库保存路径和完整的url路径,将url拼接成图片或者视频html追加到消息输入框中,以文本的方式发送消息。

引入文件上传插件

<script type="text/javascript" src="<%=path %>/chat/js/ajaxfileupload.js"></script>

上面的file1Upload方法如下

//图片文件上传
   function file1Upload(){
    $("#sendBn").val("文件上传中...");
    $("#sendBn").attr("disabled",true);
       $.ajaxFileUpload({
           url: '<%=path%>/resource?method=upload&fileType=image',
           type: 'get',
           async: 'false',
           data : {
           },
           secureuri: false, //一般设置为false
           fileElementId: 'file1', // 上传文件的name属性名
           dataType: 'JSON', //返回值类;型,一般设置为json、application/json  这里要用大写  不然会取不到返回的数据
           success: function(data, status){  
               var obj = $(data).removeAttr("style").prop("outerHTML");
               data =obj.replace("<PRE>", '').replace("</PRE>", '').replace("<pre>", '').replace("</pre>", '');  //ajaxFileUpload会对服务器响应回来的text内容加上<pre>text</pre>前后缀
               var json = JSON.parse(data);
               if(json.result == "0"){
                   alert(json.message);
                   $("#sendBn").val("发送");
                $("#sendBn").attr("disabled",false);
                   return false;
               }
               //在光标出添加上传的图片
               addImage($("#textMsg"),'<img src="'+json.url+'" style="width:100px;">');
               $(".image").next().slideUp(300);
               $("#file1").val("");
               /* $("#imageShow").html('<img src="'+json.url+'" style="width:100px;height:100px;">').show();
               //保存图片路径到文本域中
               $("#imageUrl").val(json.savePath); */
               $("#sendBn").val("发送");
               $("#sendBn").attr("disabled",false);
           },    
           error: function(data, status, e){
                   alert("网络错误,请刷新后重新尝试!");
                   $("#sendBn").val("发送");
                $("#sendBn").attr("disabled",false);
           }
       });
   }

  文件上传和下载代码的后台处理,servlet实现

ResourceServlet.java

 import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sf.json.JSONObject;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import com.zkkj.chat.util.PropertiesConfig;

/**
 * Servlet implementation class ResourceServlet
 */
@WebServlet("/resource")
public class ResourceServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    final long MAX_SIZE = 1024 * 1024 * 1024;// 设置上传文件最大为 10M
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public ResourceServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        Map resultMap = new HashMap();
        String method = request.getParameter("method");
        String fileType = request.getParameter("fileType");
        //资源上传
        if("upload".equals(method)){
            response.setContentType("application/json; charset=utf-8");
            // 设置字符编码为UTF-8, 这样支持汉字显示  
            response.setCharacterEncoding("UTF-8");  
            // 实例化一个硬盘文件工厂,用来配置上传组件ServletFileUpload  
            DiskFileItemFactory dfif = new DiskFileItemFactory();  
            dfif.setSizeThreshold(4096);// 设置上传文件时用于临时存放文件的内存大小,这里是4K.多于的部分将临时存在硬盘  

            ServletFileUpload sfu = new ServletFileUpload(dfif);
            // 设置最大上传尺寸  
            sfu.setSizeMax(MAX_SIZE);  
            PrintWriter out = response.getWriter();  
            // 从request得到 所有 上传域的列表  
            List fileList = null;  
            try {  
                fileList = sfu.parseRequest(request);  
            } catch (FileUploadException e) {// 处理文件尺寸过大异常  
                if (e instanceof SizeLimitExceededException) {  
                    resultMap.put("result", "0");
                    resultMap.put("message", "文件尺寸超过规定大小:"+MAX_SIZE+"字节");
                    out.write(JSONObject.fromObject(resultMap).toString());
                    return ;
                }  
                e.printStackTrace();  
            }  
            // 没有文件上传  
            if (fileList == null || fileList.size() == 0) {  
                resultMap.put("result", "0");
                resultMap.put("message", "请选择上传文件");
                out.write(JSONObject.fromObject(resultMap).toString());
                return ;
            }  
         // 得到所有上传的文件  
            Iterator fileItr = fileList.iterator();  
            // 循环处理所有文件  
            while (fileItr.hasNext()) {  
                FileItem fileItem = null;
                //文件大小
                long size = 0;  
                //文件原始名称
                String fileOldName = null;
                //文件后缀
                String suffix = null;
                //文件的上传名称
                String fileUploadName = null;
                //文件的保存路径
                String savePath = null;
                //文件的完整保存路径
                String path = null;
                // 得到当前文件  
                fileItem = (FileItem) fileItr.next();  
                // 忽略简单form字段而不是上传域的文件域(<input type="text" />等)  
                if (fileItem == null || fileItem.isFormField()) {  
                    continue;  
                }  
                //获取文件名称
                fileOldName = fileItem.getName();  
                // 得到文件的大小  
                size = fileItem.getSize();  
                //文件扩展名称
                 suffix = fileOldName.substring(fileOldName.lastIndexOf("."), fileOldName.length());
                //重新定义文件名称
                Random random = new Random();
                //文件的上传名称
                fileUploadName = random.nextInt(10000)+ System.currentTimeMillis()+suffix;
                //获取文件的上传的根目录
                String base_upload_path = PropertiesConfig.getProperties("base_upload_path");
                //资源的访问地址
                String return_path_z = PropertiesConfig.getProperties("return_path_z");
                switch (fileType) {
                case "image":
                {
                    if(
                        !".jpg".equals(suffix) && !".JPG".equals(suffix) &&  !".png".equals(suffix) && !".gif".equals(suffix)
                     ){
                            resultMap.put("result", "0");
                            resultMap.put("message","请选择jpg,png,gif格式的图片");
                            out.write(JSONObject.fromObject(resultMap).toString());
                            return ;
                    }
                    //获取图片资源的上传目录
                    String image_path = PropertiesConfig.getProperties("image_path");
                    //文件保存到数据库的名称
                    savePath = image_path + fileUploadName;
                    //文件的完整保存路径
                    path = base_upload_path + savePath;
                }
                break;
                case "attchment":
                {
                    //附件文件
                    String attachment_path = PropertiesConfig.getProperties("attachment_path");
                    //文件上传名称
                    savePath = attachment_path + fileUploadName;
                    //文件的完整保存路径
                    path = base_upload_path + savePath;
                }
                break;
                case "video":
                {
                    if(
                            !".mp4".equals(suffix)
                     ){
                            resultMap.put("result", "0");
                            resultMap.put("message","请选择mp4格式的视频");
                            out.write(JSONObject.fromObject(resultMap).toString());
                            return ;
                    }
                    //视频消息
                    String video_path = PropertiesConfig.getProperties("video_path");
                    //上传名称
                    savePath = video_path + fileOldName;
                    //文件完整的保存路径
                    path = base_upload_path + savePath;
                }
                break;
                
                default:
                    break;
                }
                try {  
                    File saveDir = new File(path);
                    if (!saveDir.getParentFile().exists())
                        saveDir.getParentFile().mkdirs();
                    // 保存文件  
                    fileItem.write(new File(path));  
                    response.setStatus(200);  
                    //返回成功的消息
                    resultMap.put("result", "1");
                    resultMap.put("savePath", savePath);
                    resultMap.put("fileOldName", fileOldName);
                    resultMap.put("url",return_path_z + savePath);
                    DecimalFormat df = new DecimalFormat("#.00");
                    resultMap.put("fileSize", df.format(size / 1024));
                    out.write(JSONObject.fromObject(resultMap).toString());
                    return ;
                } catch (Exception e) {  
                    e.printStackTrace();  
                }
                out.flush();  
                out.close();  
            }
        }else if("download".equals(method)){
            String path = request.getParameter("path");
            String name = request.getParameter("name");
            if(name.contains("(")){
                name = name.substring(0, name.indexOf("("));
            }
            String return_path_z =  PropertiesConfig.getProperties("return_path_z1");
            //文件下载
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/form-data");
            String fileName = new String(name.getBytes("GB2312"), "ISO8859-1");
            response.setHeader("Content-Disposition", "attachment;fileName=" + fileName);
            response.setBufferSize(1024);
            // 文件路径
            URL url = new URL(return_path_z + path);
            HttpURLConnection connection=(HttpURLConnection)url.openConnection();
            InputStream inputStream = connection.getInputStream();
            OutputStream os = response.getOutputStream();
            byte[] b = new byte[1024];
            int length;
            while ((length = inputStream.read(b)) > 0) {
                os.write(b, 0, length);
            }
            inputStream.close();
        }
        
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doPost(request, response);
    }

}


资源上传到固定的目录,而不是tomcat项目下的目录,需要配置虚拟路径,参考地址tomcat配置虚拟路径

7.发送语音

<!-- 引入录音文件 -->
<script type="text/javascript" src="<%=path %>/chat/plug/record/js/recordmp3.js"></script>

var mp3Blob;//录音后的MP3blob文件

var recorder = null;//全局的录音对象

//页面加载完成初始话录音插件

$(function(){

          //初始化录音插件
        recorder = new MP3Recorder({
           debug:true,
           funOk: function () {
               
           },
           funCancel: function (msg) {
               recorder = null;
           }
       });

})

//开始录音

function funStart() {
       if(recorder != null){
           try{
               recorder.start();
               return true;
           }catch(e){
               alert(e);
               return false;
           }
       }else{
           alert("录音插件初始化失败");
           return false;
       }
    }
   //录音结束
   function funStop() {
       recorder.stop();
       recorder.getMp3Blob(function (blob) {
           mp3Blob = blob;
           var reader = new FileReader();
           reader.readAsDataURL(mp3Blob);
           reader.onload = function(e){
               funUpload(this.result);
           }
//            var url = URL.createObjectURL(mp3Blob);
//            funUpload();
       });
   }

   //录音文件base64格式上传
   function funUpload(mp3Base64) {
       var fd = new FormData();
       fd.append('file', mp3Base64);
       fd.append("name","张三");
       $.ajax({
           async: false,
           url : "<%=path%>/base64Upload",  
           type : 'post',  
           data :fd,  
           processData : false,  //必须false才会避开jQuery对 formdata 的默认处理   
           contentType : false,  //必须false才会自动加上正确的Content-Type
           dataType:"text",
           success : function(result) {  
               //直接发送语音消息
               var text = '<audio src="'+result+'"  controls=""></audio>' ;
               var json = {};
               json.message = text;
               json.extra = "";
               json.messageType = "1";
               json.type = "${type}";
               json.fromUserId = "${fromUserId}";
               json.toUserId = "${toUserId}";
               json.groupId = "${groupId}";
               socket.send(JSON.stringify(json));
           },  
           error : function(result) {  
             
           }  
       });
   }

//自己可以在点击语音图标开始录音的时候,添加动态录音gif图和读秒的效果,文件发送成功后隐藏读秒显示


//录音插件文件上传的后台处理,将base64转换成文件流保存到指定位置后,返回文件的访问地址,ajax返回前端在拿着地址拼接audio标签 发送消息。

FormDataServlet.java

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Random;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.codec.binary.Base64;

import com.zkkj.chat.util.PropertiesConfig;

/**
 * Servlet implementation class FormDataServlet
 */
@WebServlet("/base64Upload")
@MultipartConfig
public class FormDataServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public FormDataServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doPost(request, response);
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        response.setContentType("application/text; charset=utf-8");
        // 设置字符编码为UTF-8, 这样支持汉字显示  
        response.setCharacterEncoding("UTF-8");
        PrintWriter out = response.getWriter();
        String file = new String (request.getParameter("file").getBytes("ISO8859-1"),"UTF-8"); //二进制流上传需要转码
        file = file.replace("data:audio/mp3;base64,", "");//去掉base64前面的部分
        //文件上传根目录
        String base_upload_path = PropertiesConfig.getProperties("base_upload_path");
        //文件访问路径
        String  return_path_z = PropertiesConfig.getProperties("return_path_z");
        //文件保存路径
        String audio_path = PropertiesConfig.getProperties("audio_path");
        //重新定义文件名称
        Random random = new Random();
        //文件的上传名称
        String fileUploadName = random.nextInt(10000)+ System.currentTimeMillis()+".mp3";
        //文件保存路径
        String savePath = audio_path + fileUploadName;
        //文件完整路径
        String filePath = base_upload_path + savePath;
        base64ToIo(file,filePath);
        //            返回文件地址  
        out.write(return_path_z+ savePath);
    }
    
    public void base64ToIo(String strBase64,String path) throws IOException {
        File saveDir = new File(path);
        if (!saveDir.getParentFile().exists())
            saveDir.getParentFile().mkdirs();
        
        String string = strBase64;
        String fileName = path; //生成的新文件
        try {
            // 解码,然后将字节转换为文件
            boolean flag = Base64.isBase64(strBase64);
            byte[] bytes = Base64.decodeBase64(string);   //将字符串转换为byte数组
            ByteArrayInputStream in = new ByteArrayInputStream(bytes);
            byte[] buffer = new byte[1024];
            for (int i = 0; i < buffer.length; ++i) {  
                if (buffer[i] < 0) {  
                    // 调整异常数据  
                    buffer[i] += 256;  
                }  
            }
            FileOutputStream out = new FileOutputStream(fileName);
            int bytesum = 0;
            int byteread = 0;
            while ((byteread = in.read(buffer)) != -1) {
                bytesum += byteread;
                out.write(buffer, 0, byteread); //文件写操作
            }
            out.flush();
            out.close();
            in.close();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }

}

说明:浏览器为了安全考虑 有的限制了录音功能,没有限制的可能会每次进入页面都会提示获取权限,为了解决这个问题的最好办法 就是采用https的访问访问项目

,tomcat配置https的方法可以参考这个链接tomcat配置https

8.在输入框的光标出插入元素

在光标出添加内容的方法如下:

//在对象光标出插入元素,obj是要插入元素的jquery对象
    function addImage(obj,text){
        var range, node;
        if(!obj.hasfocus) {
            obj.focus();
        }
        if (window.getSelection && window.getSelection().getRangeAt) {
            range = window.getSelection().getRangeAt(0);
            range.collapse(false);
            node = range.createContextualFragment(text);
            var c = node.lastChild;
            range.insertNode(node);
            if(c){
                range.setEndAfter(c);
                range.setStartAfter(c)
            }
            var j = window.getSelection();
            j.removeAllRanges();
            j.addRange(range);
        } else if (document.selection && document.selection.createRange) {
            document.selection.createRange().pasteHTML(text);
        }
    }

  如果不需要使用 可编辑的div方式的消息的输入框,使用简单的textarea文本输入框的话,在光标出插入的方法如下

 //在光标出插入元素,t 为textarea 对象,例如document.getElementById("textMsg");

    function addTextAtFocus(t, txt){   
        var val = t.value;
          if(document.selection){
              t.focus()
              document.selection.createRange().text = txt;
          } else {
           var cp = t.selectionStart;
           var ubbLength = t.value.length;
           var s = t.scrollTop;
          // document.getElementById('aaa').innerHTML += s + '<br />';
           t.value = t.value.slice(0,t.selectionStart) + txt + t.value.slice(t.selectionStart, ubbLength);
           this.setCursorPosition(t, cp + txt.length);
          // document.getElementById('aaa').innerHTML += t.scrollTop + '<br />';
           firefox=navigator.userAgent.toLowerCase().match(/firefox\/([\d\.]+)/) && setTimeout(function(){
            if(t.scrollTop != s) t.scrollTop=s;
           },0)

        };
    }

    9.发送消息

   //发送文本消息
    function sendText() {
        if (!socket || socket.readyState != 1) {
            initSocket();
        }

        //获取消息输入框输入的内容并且去除掉空格

        var text = $("#textMsg").html().trim();
        if(text == null || text == ""){
            alert("请输入消息内容!");
            return false;
        }
        
        //定义json 发送消息  
        var json = {};
        json.message = text;//消息内容
        json.extra = "";//消息附加的信息
        json.messageType = "1";//消息类型,1.文本消息,2.附件消息,3.语音消息    等等
        json.type = "${type}";  //聊天类型,1.单聊,2.群聊
        json.fromUserId = "${fromUserId}";//消息发送者的id
        json.toUserId = "${toUserId}";//消息接受者的id ,如果type = 2群聊为空,

        json.groupId = "${groupId}";//群组id,当type=1单聊 为空
        socket.send(JSON.stringify(json));  //将json转换为字符串并发送给服务端
        $("#textMsg").html("");//清空输入框的内容
    }

  按ENTER键发送消息
   $("body").keydown(function(event) {
        if (event.keyCode == "13") {//keyCode=13是回车键并且是文本消息的时候触发
            sendText();
               //避免换行
            event.preventDefault();
        }
    });

10.接受服务端发送的消息

 // 接收到服务器发送的消息方法

 function showMessage(message) {

       //解析消息json

       var json = eval('(' + message + ')');

        var text = '';//用于显示的消息html 内容
        if(json.chatType == '${type}'){//后台传过来的聊天类型和当前聊天类型一样的时候才显示消息,因为单聊和群聊是一个页面 当然也可以分开为两个
            text = getMsgContent(json);//getMsgContent方法为拼接消息内容的方法 ,自行处理
            $("#showChatMessage").append(text);//将消息追加到显示区域下面
            document.getElementById('showChatMessage').scrollTop = 100000;//滚动div的滚动条
            //初始化消息撤回事件,鼠标放上面显示撤回按钮
            initMsgRecall();
        }

}

11.消息撤回

消息撤回的实现原理 ,当点击消息撤回的按钮 向服务器发送消息 消息类型为撤回的  然后根据聊天的类型是单聊还是群聊,给单个人或者群组所有的人发送系统消息为消息撤回的消息,前端收到系统消息后移除队形的消息内容并且显示系统消息内容。

 //初始化消息撤回按钮事件
  function initMsgRecall(){

       //对未绑定hover的消息元素添加hover事件

      $(".msgContent[initHover='0']").hover(function(){
           //消息发送的时间
           var msgDate = $(this).attr("msgDate");
           //消息id
           var msgId = $(this).attr("msgId");
           //消息发送者
           var fromUserId = $(this).attr("fromUserId");
           //取到当前时间
           var now = new Date();
           var t = (new Date()).format("yyyy-MM-dd hh:mm:ss")
           //当前时间与消息发送时间的间隔
           var diff = GetDateDiff(msgDate,t, "minute");
           //消息发送时间 小于三分钟,并且是自己发送的消息可以撤销
           if(fromUserId == "${fromUserId}" && diff <= 3){
                 $(this).find("div:last").append('<span class="recall" style="margin-left:10px;"><a href="javascript:void();" οnclick="recallMsg('+msgId+')">撤回</a></span>');
           }
       },function(){
           //移除撤回按钮
           $(this).find(".recall").remove();
       });
      //标记为已经初始化了hover事件
      $(".msgContent").attr("initHover","1");
   }

 

后台相关逻辑

Websocket.java

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import net.sf.json.JSONObject;

import com.zkkj.chat.service.IGroupService;
import com.zkkj.chat.service.IMessageService;
import com.zkkj.chat.service.IUserService;
import com.zkkj.chat.service.impl.GroupServiceImpl;
import com.zkkj.chat.service.impl.MessageServiceImpl;
import com.zkkj.chat.service.impl.UserServiceImpl;
import com.zkkj.chat.util.DateUtil;
import com.zkkj.chat.util.ParamsUtil;
import com.zkkj.chat.util.TimeFormatUtil;

@ServerEndpoint(value="/webSocket")
public class Websocket {
    
    private IUserService service = new UserServiceImpl();
    
    private IGroupService groupService = new GroupServiceImpl();
    
    private IMessageService messageService = new MessageServiceImpl();
    
    //保存所有的连接的客户端对象
    static Map<String, Session> clientsMap = new ConcurrentHashMap<String, Session>();
    //心跳检测 map
    static Map<String, Long> heartMap = new HashMap<String, Long>();
    // 与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;
    /*
     *使用@Onopen注解的表示当客户端链接成功后的回掉。参数Session是可选参数
        这个Session是WebSocket规范中的会话,表示一次会话。并非HttpSession
     */
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        Map params = session.getRequestParameterMap();
        String userId = "";
        Object userIdObject = params.get("userId");
        if (userIdObject != null) {
            userId = ((List<String>)userIdObject).get(0);
            clientsMap.put(userId, session);
            session.getUserProperties().put("userId", userId);    
        }
        try {
            //修改用户的在线状态
            Map userUpdateParamsMap = new HashMap();
            userUpdateParamsMap.put("onlineStatus", "1");
            userUpdateParamsMap.put("userId", userId);
             service.updateUser(userUpdateParamsMap);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    /*
    *用户断开链接后的回调,注意这个方法必须是客户端调用了断开链接方法后才会回调
    */
    @OnClose
    public void onClose(Session session) {
         Map params = session.getRequestParameterMap();
         String userId = "";
         Object userIdObject = params.get("userId");
         if (userIdObject != null) {
             userId = ((List<String>)userIdObject).get(0);
          }
         
         clientsMap.remove(userId);
         
         try {
            session.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
         
        
         try {
             //修改用户的在线状态
             Map userUpdateParamsMap = new HashMap();
             userUpdateParamsMap.put("onlineStatus", "0");
             userUpdateParamsMap.put("userId", userId);
             service.updateUser(userUpdateParamsMap);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    @OnMessage
    public void onMessage(String message,Session session){
        JSONObject json = JSONObject.fromObject(message);
        String type = json.getString("type");
        String message_ = json.getString("message");
        String fromUserId = json.getString("fromUserId");
        String toUserId = json.getString("toUserId");
        String goupId = json.getString("groupId");
        String messageType = json.getString("messageType");//消息类型
        String extra = json.getString("extra");//额外属性
        if("1".equals(type)){
            //单聊
            try {
                if("1".equals(messageType) || "3".equals(messageType)){
                    sendMessageToOne("",fromUserId,toUserId,message_,false,messageType,extra);
                }else if("4".equals(messageType)){
                    //更新用户id的心跳时间
                    heartMap.put(fromUserId,new Date().getTime());
                }else if("5".equals(messageType)){
                    //撤回消息
                    String msgId = json.getString("msgId");
                    //撤回 消息
                    sendMessageToOne(msgId,fromUserId,toUserId,message_,true,messageType,extra);
                }
                
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                System.out.println("单聊失败==========================" + e.getMessage());
            }
        }else if("2".equals(type)){
            //群聊
            try {
                if("1".equals(messageType) || "3".equals(messageType)){
                    sendGroupMessage("",fromUserId, goupId, message_,false,messageType,extra);
                }else if("4".equals(messageType)){
                    //更新用户id的心跳时间
                    heartMap.put(fromUserId,new Date().getTime());
                }else if("5".equals(messageType)){
                    //撤回消息
                    String msgId = json.getString("msgId");
                    sendGroupMessage(msgId,fromUserId, goupId, message_,true,messageType,extra);
                }
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                System.out.println("群聊失败==========================" + e.getMessage());
            }
        }
    }
    //点对点消息
    public void sendMessageToOne(String msgId_,String fromUserId, String toUserId,String message,boolean isSystem,String messageType,String extra) throws Exception{
        if(!"".equals(toUserId) && toUserId != null){
            //
            Session fromUserSession = clientsMap.get(fromUserId);
            Session toUserSession = clientsMap.get(toUserId);
            //查询消息发送者的用户信息
            Map params = new HashMap();
            params.put("userId", fromUserId);
            Map fromUser = service.getUserById(params);
            //消息发送者的昵称
            String fromUserNickName = "";
            String fromUserHeadUrl = "";
            if(fromUser != null){
                fromUserNickName = ParamsUtil.nullDeal(fromUser, "nickName", "");
                fromUserHeadUrl = ParamsUtil.nullDeal(fromUser, "headUrl", "");
            }
            
            JSONObject messageJson = new JSONObject();
            messageJson.put("message", message);
            messageJson.put("fromUserId", fromUserId);
            messageJson.put("toUserId", toUserId);
            messageJson.put("headUrl", fromUserHeadUrl);
            messageJson.put("nickName", fromUserNickName);
            messageJson.put("chatType", "1");//单聊
            messageJson.put("messageType",messageType);//消息类型
            messageJson.put("isSystem", isSystem);//是否是系统消息
            messageJson.put("extra", extra);//附加属性
            messageJson.put("msgDate", DateUtil.getStringDate());//消息发送的日期
            messageJson.put("dateShow", TimeFormatUtil.getInterval(DateUtil.strToDateLong(DateUtil.getStringDate())));//消息发送时间,用于显示
            String msgId = "";
            try {
                if(!"4".equals(messageType)){
                    //保存消息到数据库
                    Map addMessageParamsMap = new HashMap();
                    addMessageParamsMap.put("fromUserId", fromUserId);
                    addMessageParamsMap.put("toUserId", toUserId);
                    addMessageParamsMap.put("chatType", "1");
                    addMessageParamsMap.put("messageType",messageType);
                    addMessageParamsMap.put("content", message);
                    addMessageParamsMap.put("extra", extra);
                    messageService.addMessage(addMessageParamsMap);
                    //得到消息的id
                    msgId = String.valueOf(addMessageParamsMap.get("id"));
                }
                
                if("5".equals(messageType)){
                    //消息撤回,返回消息撤回的id
                    msgId = msgId_;
                    //删除消息内容
                    Map deleteMessageParamsMap = new HashMap();
                    deleteMessageParamsMap.put("msgId", msgId);
                    messageService.deleteMessage(deleteMessageParamsMap);
                }
                
                //设置消息id
                messageJson.put("msgId", msgId);
                
                //发送消息
                if(fromUserSession != null){
                    //给发送消息着推送消息
                    fromUserSession.getBasicRemote().sendText(messageJson.toString());
                }
                if(toUserSession != null){
                    //给接受消息着推送消息
                    toUserSession.getBasicRemote().sendText(messageJson.toString());
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    //群聊消息
    public void sendGroupMessage(String msgId_,String fromUserId,String groupId,String message,boolean isSystem,String messageType,String extra) throws Exception{
        //查询消息发送者的用户信息
        Map fromUserParams = new HashMap();
        fromUserParams.put("userId", fromUserId);
        Map fromUser = service.getUserById(fromUserParams);
        //消息发送者的昵称
        String fromUserNickName = "";
        String fromUserHeadUrl = "";
        if(fromUser != null){
            fromUserNickName = ParamsUtil.nullDeal(fromUser, "nickName", "");
            fromUserHeadUrl = ParamsUtil.nullDeal(fromUser, "headUrl", "");
        }
        
        JSONObject messageJson = new JSONObject();
        messageJson.put("message", message);//消息内容
        messageJson.put("fromUserId", fromUserId);//消息发送者
        messageJson.put("groupId", groupId);//群组id
        messageJson.put("headUrl", fromUserHeadUrl);//消息发送者的用户头像
        messageJson.put("nickName", fromUserNickName);//消息发送者的用户昵称
        messageJson.put("chatType", "2");//群聊
        messageJson.put("messageType",messageType);//消息类型
        messageJson.put("isSystem", isSystem);//是否是系统消息
        messageJson.put("extra", extra);//附加属性
        messageJson.put("msgDate", DateUtil.getStringDate());//消息发送的日期
        messageJson.put("dateShow", TimeFormatUtil.getInterval(DateUtil.strToDateLong(DateUtil.getStringDate())));//消息发送时间,用于显示
        
        //保存消息内容到数据库
        String msgId = "";
        
        if(!"4".equals(messageType)){
            //保存消息到数据库
            Map addMessageParamsMap = new HashMap();
            addMessageParamsMap.put("fromUserId", fromUserId);
            addMessageParamsMap.put("chatType", "2");
            addMessageParamsMap.put("messageType",messageType);
            addMessageParamsMap.put("groupId",groupId);
            addMessageParamsMap.put("content", message);
            addMessageParamsMap.put("extra", extra);
            messageService.addMessage(addMessageParamsMap);
            
            msgId = String.valueOf(addMessageParamsMap.get("id"));
        }
        
        if("5".equals(messageType)){
            //消息撤回
            msgId = msgId_;
            //删除消息内容
            Map deleteMessageParamsMap = new HashMap();
            deleteMessageParamsMap.put("msgId", msgId);
            messageService.deleteMessage(deleteMessageParamsMap);
        }
        
        //设置消息id
        messageJson.put("msgId", msgId);
        
        //根据群组id查询群成员
        Map params = new HashMap();
        params.put("groupId", groupId);
        List<Map> userList = groupService.getGroupUserList(params);
        
        //循环给群成员发送消息
        for(Map user_ : userList){
            Session session =clientsMap.get(String.valueOf(user_.get("id")));
            if(session != null){
                session.getAsyncRemote().sendText(messageJson.toString());
            }
        }
    }
    public static Map<String, Long> getHeartMap() {
        return heartMap;
    }
    public static void setHeartMap(Map<String, Long> heartMap) {
        Websocket.heartMap = heartMap;
    }
}


DateUtil.java

    import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Random;

import org.apache.commons.lang.time.DateUtils;

/**
 *@Title:
 *@Description:日期处理类
 *@Author:赵亚龙
 *@Since:2016年2月28日
 *@Version:1.1.0
 */
public class DateUtil {
    
    /**
     * yyyy-MM-dd HH:mm:ss
     */
    public final static String FORMAT_GENERAL = "yyyy-MM-dd HH:mm:ss";
    /**
     * yyyy-MM-dd
     */
    public final static String FORMAT_GENERAL1 = "yyyy-MM-dd";
    /**
     * MM/dd
     */
    public final static String FORMAT_GENERAL2 = "MM/dd";
    /**
     * HH:mm
     */
    public final static String FORMAT_GENERAL3 = "HH:mm";
    /**
     * yyyy-MM-dd HH:mm
     */
    public final static String FORMAT_GENERAL4 = "yyyy-MM-dd";
    
    /**
     * MM-dd
     */
    public final static String FORMAT_GENERAL5 = "MM-dd";
    /**
     * HH:mm MM-dd
     */
    public final static String FORMAT_GENERAL6 = "HH:mm MM-dd";
    
    /**
     * MM-dd
     */
    public final static String FORMAT_FLIGHT_PLAN = "HHmm";
    
    /**
     * 早上8点到下午16点,每隔半小时(一共是16个半小时)
     */
    public final static int EIGHTH_TO_SIXTEEN = 16;
    
    /**
     * 获取12小时制当前日期字符串
     *
     * @return
     */
    public static String getStrDate_12() {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        Date currentTime = new Date();
        String strDate = formatter.format(currentTime);
        return strDate;
    }

    /**
     * 获取24小时制当前日期字符串
     *
     * @return
     */
    public static String getStrDate_24() {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date currentTime = new Date();
        String strDate = formatter.format(currentTime);
        return strDate;
    }

    /**
     * 获取格式化当前时间、毫秒字符串
     *
     * @return
     */
    public static String getStrDateS() {
        SimpleDateFormat formatter = new SimpleDateFormat(
                "yyyy-MM-dd HH:mm:ss:SS");
        Date currentTime = new Date();
        String strDate = formatter.format(currentTime);
        return strDate;
    }

    /**
     * 转换日期为字符串格式
     * <p>
     *
     * @param Date
     * @return
     */
    public static String DateToStr(java.util.Date Date) {
        if(Date != null){
            SimpleDateFormat formatter = new SimpleDateFormat(FORMAT_GENERAL);
            String strDate = formatter.format(Date);
            return strDate;
        }
        return "";
    }

    /**
     * 奖date类型的日期转换为指定格式
     */
    public static Date DateToDate(java.util.Date Date, String format) {
        SimpleDateFormat formatter = new SimpleDateFormat(format);
        Date d = new Date();
        String dd = formatter.format(d);
        Date ddd = null;
        try {
            ddd = formatter.parse(dd);
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return ddd;
    }

    /**
     * 转换日期为格式化字符串
     *
     * @param Date
     * @param format
     * @return
     */
    public static String DateToFormatStr(java.util.Date Date, String format) {
        SimpleDateFormat formatter = new SimpleDateFormat(format);
        String strDate = formatter.format(Date);
        return strDate;
    }

    /**
     * 获取当前日期 格式为 yyyy-MM-dd
     *
     * @return strDate
     */
    public static String getNowStrDate() {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        Date date = new Date();
        String strDate = formatter.format(date);
        return strDate;
    }

    /**
     * 获取当间时间字符串 yyyyMMddHHmmss
     *
     * @return
     */
    public static String getLongDate() {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
        Date date = new Date();
        String strDate = formatter.format(date);
        return strDate;
    }

    /**
     * 获取当间时间字符串 yyyyMMddHHmmssSS
     *
     * @return
     */
    public static String getLongDateS() {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmssSS");
        Date date = new Date();
        String strDate = formatter.format(date);
        return strDate;
    }

    /**
     * 比较二个日期
     *
     * @param date1
     * @param date2
     * @return
     */
    public static boolean Compare_Date(java.util.Date date1,
            java.util.Date date2) {
        return date1.equals(date2);
    }

    /**
     * 将字符串类型的时间转化为Date型
     *
     * @param strDate
     * @param formatDate
     * @return Date
     * @throws ParseException
     */
    public static Date str2Date(String strDate, String formatDate)
            throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat(formatDate);
        return sdf.parse(strDate);
    }

    /**
     * 将字符串类型的时间转化为Date型,并将在此时间上进行增加或减少相应天
     *
     * @param strDate
     * @param formatDate
     * @return Date
     * @throws ParseException
     */
    public static Date otherDate(String strDate, String formatDate, int num)
            throws ParseException {
        Calendar c = new GregorianCalendar();

        Date date = str2Date(strDate, formatDate);

        c.setTime(date);

        c.add(Calendar.DATE, num);

        SimpleDateFormat sdf = new SimpleDateFormat(formatDate);

        return str2Date(sdf.format(c.getTime()), formatDate);
    }

    /**
     * 获取现在时间
     *
     * @return 返回时间类型 yyyy-MM-dd HH:mm:ss
     */
    public static Date getNowDate() {
        Date currentTime = new Date();
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateString = formatter.format(currentTime);
        ParsePosition pos = new ParsePosition(8);
        Date currentTime_2 = formatter.parse(dateString, pos);
        return currentTime_2;
    }

    /**
     * 获取现在时间
     *
     * @return返回短时间格式 yyyy-MM-dd
     */
    public static Date getNowDateShort() {
        Date currentTime = new Date();
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        String dateString = formatter.format(currentTime);
        ParsePosition pos = new ParsePosition(8);
        Date currentTime_2 = formatter.parse(dateString, pos);
        return currentTime_2;
    }

    /**
     * 获取现在时间
     *
     * @return返回字符串格式 yyyy-MM-dd HH:mm:ss
     */
    public static String getStringDate() {
        Date currentTime = new Date();
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateString = formatter.format(currentTime);
        return dateString;
    }

    /**
     * 获取现在时间
     *
     * @return 返回短时间字符串格式yyyy-MM-dd
     */
    public static String getStringDateShort() {
        Date currentTime = new Date();
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        String dateString = formatter.format(currentTime);
        return dateString;
    }

    /**
     * 获取时间 小时:分;秒 HH:mm:ss
     *
     * @return
     */
    public static String getTimeShort() {
        SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
        Date currentTime = new Date();
        String dateString = formatter.format(currentTime);
        return dateString;
    }

    /**
     * 将长时间格式字符串转换为时间 yyyy-MM-dd HH:mm:ss
     *
     * @param strDate
     * @return
     */
    public static Date strToDateLong(String strDate) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        ParsePosition pos = new ParsePosition(0);
        Date strtodate = formatter.parse(strDate, pos);
        return strtodate;
    }

    /**
     * 将长时间格式时间转换为字符串 yyyy-MM-dd HH:mm:ss
     *
     * @param dateDate
     * @return
     */
    public static String dateToStrLong(java.util.Date dateDate) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateString = formatter.format(dateDate);
        return dateString;
    }

    /**
     * 将短时间格式时间转换为字符串 yyyy-MM-dd
     *
     * @param dateDate
     * @param k
     * @return
     */
    public static String dateToStr(java.util.Date dateDate) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        String dateString = formatter.format(dateDate);
        return dateString;
    }

    /**
     * 将短时间格式字符串转换为时间 yyyy-MM-dd
     *
     * @param strDate
     * @return
     */
    public static Date strToDate(String strDate) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        ParsePosition pos = new ParsePosition(0);
        Date strtodate = formatter.parse(strDate, pos);
        return strtodate;
    }

    /**
     * 得到现在时间
     *
     * @return
     */
    public static Date getNow() {
        Date currentTime = new Date();
        return currentTime;
    }

    /**
     * 提取一个月中的最后一天
     *
     * @param day
     * @return
     */
    public static Date getLastDate(long day) {
        Date date = new Date();
        long date_3_hm = date.getTime() - 3600000 * 34 * day;
        Date date_3_hm_date = new Date(date_3_hm);
        return date_3_hm_date;
    }

    /**
     * 得到现在时间
     *
     * @return 字符串 yyyyMMdd HHmmss
     */
    public static String getStringToday() {
        Date currentTime = new Date();
        SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd HHmmss");
        String dateString = formatter.format(currentTime);
        return dateString;
    }

    /**
     * 得到现在小时
     */
    public static String getHour() {
        Date currentTime = new Date();
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateString = formatter.format(currentTime);
        String hour;
        hour = dateString.substring(11, 13);
        return hour;
    }

    /**
     * 得到现在分钟
     *
     * @return
     */
    public static String getTime() {
        Date currentTime = new Date();
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateString = formatter.format(currentTime);
        String min;
        min = dateString.substring(14, 16);
        return min;
    }

    /**
     * 根据用户传入的时间表示格式,返回当前时间的格式 如果是yyyyMMdd,注意字母y不能大写。
     *
     * @param sformat
     *            yyyyMMddhhmmss
     * @return
     */
    public static String getUserDate(String sformat) {
        Date currentTime = new Date();
        SimpleDateFormat formatter = new SimpleDateFormat(sformat);
        String dateString = formatter.format(currentTime);
        return dateString;
    }

    /**
     * 二个小时时间间的差值,必须保证二个时间都是"HH:MM"的格式,返回字符型的分钟
     */
    public static String getTwoHour(String st1, String st2) {
        String[] kk = null;
        String[] jj = null;
        kk = st1.split(":");
        jj = st2.split(":");
        if (Integer.parseInt(kk[0]) < Integer.parseInt(jj[0]))
            return "0";
        else {
            double y = Double.parseDouble(kk[0]) + Double.parseDouble(kk[1])
                    / 60;
            double u = Double.parseDouble(jj[0]) + Double.parseDouble(jj[1])
                    / 60;
            if ((y - u) > 0)
                return y - u + "";
            else
                return "0";
        }
    }

    /**
     * 得到二个日期间的间隔天数
     */
    public static String getTwoDay(String sj1, String sj2) {
        SimpleDateFormat myFormatter = new SimpleDateFormat("yyyy-MM-dd");
        long day = 0;
        try {
            java.util.Date date = myFormatter.parse(sj1);
            java.util.Date mydate = myFormatter.parse(sj2);
            day = (date.getTime() - mydate.getTime()) / (24 * 60 * 60 * 1000);
        } catch (Exception e) {
            return "";
        }
        return day + "";
    }

    /**
     * 时间前推或后推分钟,其中JJ表示分钟.
     */
    public static String getPreTime(String sj1, String jj) {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String mydate1 = "";
        try {
            Date date1 = format.parse(sj1);
            long Time = (date1.getTime() / 1000) + Integer.parseInt(jj) * 60;
            date1.setTime(Time * 1000);
            mydate1 = format.format(date1);
        } catch (Exception e) {
        }
        return mydate1;
    }

    /**
     * 得到一个时间延后或前移几天的时间,nowdate为时间,delay为前移或后延的天数
     */
    public static String getNextDay(String nowdate, String delay) {
        try {
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
            String mdate = "";
            Date d = strToDate(nowdate);
            long myTime = (d.getTime() / 1000) + Integer.parseInt(delay) * 24
                    * 60 * 60;
            d.setTime(myTime * 1000);
            mdate = format.format(d);
            return mdate;
        } catch (Exception e) {
            return "";
        }
    }

    /**
     * 判断是否润年
     *
     * @param ddate
     * @return
     */
    public static boolean isLeapYear(String ddate) {

        /**
         * 详细设计: 1.被400整除是闰年,否则: 2.不能被4整除则不是闰年 3.能被4整除同时不能被100整除则是闰年
         * 3.能被4整除同时能被100整除则不是闰年
         */
        Date d = strToDate(ddate);
        GregorianCalendar gc = (GregorianCalendar) Calendar.getInstance();
        gc.setTime(d);
        int year = gc.get(Calendar.YEAR);
        if ((year % 400) == 0)
            return true;
        else if ((year % 4) == 0) {
            if ((year % 100) == 0)
                return false;
            else
                return true;
        } else
            return false;
    }

    /**
     * 返回美国时间格式 26 Apr 2006
     *
     * @param str
     * @return
     */
    public static String getEDate(String str) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        ParsePosition pos = new ParsePosition(0);
        Date strtodate = formatter.parse(str, pos);
        String j = strtodate.toString();
        String[] k = j.split(" ");
        return k[2] + k[1].toUpperCase() + k[5].substring(2, 4);
    }

    /**
     * 获取一个月的最后一天
     *
     * @param dat
     * @return
     */
    public static String getEndDateOfMonth(String dat) {// yyyy-MM-dd
        String str = dat.substring(0, 8);
        String month = dat.substring(5, 7);
        int mon = Integer.parseInt(month);
        if (mon == 1 || mon == 3 || mon == 5 || mon == 7 || mon == 8
                || mon == 10 || mon == 12) {
            str += "31";
        } else if (mon == 4 || mon == 6 || mon == 9 || mon == 11) {
            str += "30";
        } else {
            if (isLeapYear(dat)) {
                str += "29";
            } else {
                str += "28";
            }
        }
        return str;
    }

    /**
     * 判断二个时间是否在同一个周
     *
     * @param date1
     * @param date2
     * @return
     */
    public static boolean isSameWeekDates(Date date1, Date date2) {
        Calendar cal1 = Calendar.getInstance();
        Calendar cal2 = Calendar.getInstance();
        cal1.setTime(date1);
        cal2.setTime(date2);
        int subYear = cal1.get(Calendar.YEAR) - cal2.get(Calendar.YEAR);
        if (0 == subYear) {
            if (cal1.get(Calendar.WEEK_OF_YEAR) == cal2
                    .get(Calendar.WEEK_OF_YEAR))
                return true;
        } else if (1 == subYear && 11 == cal2.get(Calendar.MONTH)) {
            // 如果12月的最后一周横跨来年第一周的话则最后一周即算做来年的第一周
            if (cal1.get(Calendar.WEEK_OF_YEAR) == cal2
                    .get(Calendar.WEEK_OF_YEAR))
                return true;
        } else if (-1 == subYear && 11 == cal1.get(Calendar.MONTH)) {
            if (cal1.get(Calendar.WEEK_OF_YEAR) == cal2
                    .get(Calendar.WEEK_OF_YEAR))
                return true;
        }
        return false;
    }

    /**
     * 获得一个日期所在的周的星期几的日期,如要找出2002年2月3日所在周的星期一是几号
     *
     * @param sdate
     * @param num
     * @return
     */
    public static String getWeek(String sdate, String num) {
        // 再转换为时间
        Date dd = DateUtil.strToDate(sdate);
        Calendar c = Calendar.getInstance();
        c.setTime(dd);
        if (num.equals("1")) // 返回星期一所在的日期
            c.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
        else if (num.equals("2")) // 返回星期二所在的日期
            c.set(Calendar.DAY_OF_WEEK, Calendar.TUESDAY);
        else if (num.equals("3")) // 返回星期三所在的日期
            c.set(Calendar.DAY_OF_WEEK, Calendar.WEDNESDAY);
        else if (num.equals("4")) // 返回星期四所在的日期
            c.set(Calendar.DAY_OF_WEEK, Calendar.THURSDAY);
        else if (num.equals("5")) // 返回星期五所在的日期
            c.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
        else if (num.equals("6")) // 返回星期六所在的日期
            c.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY);
        else if (num.equals("0")) // 返回星期日所在的日期
            c.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
        return new SimpleDateFormat("yyyy-MM-dd").format(c.getTime());
    }

    /**
     * 根据一个日期,返回是星期几的字符串
     *
     * @param sdate
     * @return
     */
    public static String getWeek(String sdate) {
        // 再转换为时间
        Date date = DateUtil.strToDate(sdate);
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        // int hour=c.get(Calendar.DAY_OF_WEEK);
        // hour中存的就是星期几了,其范围 1~7
        // 1=星期日 7=星期六,其他类推
        return new SimpleDateFormat("EEEE").format(c.getTime());
    }

    public static String getWeekStr(String sdate) {
        String str = "";
        str = DateUtil.getWeek(sdate);
        if ("1".equals(str)) {
            str = "星期日";
        } else if ("2".equals(str)) {
            str = "星期一";
        } else if ("3".equals(str)) {
            str = "星期二";
        } else if ("4".equals(str)) {
            str = "星期三";
        } else if ("5".equals(str)) {
            str = "星期四";
        } else if ("6".equals(str)) {
            str = "星期五";
        } else if ("7".equals(str)) {
            str = "星期六";
        }
        return str;
    }

    /**
     * 两个时间之间的天数
     *
     * @param date1
     * @param date2
     * @return
     */
    public static long getDays(String date1, String date2) {
        if (date1 == null || date1.equals(""))
            return 0;
        if (date2 == null || date2.equals(""))
            return 0;
        // 转换为标准时间
        SimpleDateFormat myFormatter = new SimpleDateFormat("yyyy-MM-dd");
        java.util.Date date = null;
        java.util.Date mydate = null;
        try {
            date = myFormatter.parse(date1);
            mydate = myFormatter.parse(date2);
        } catch (Exception e) {
        }
        long day = (date.getTime() - mydate.getTime()) / (24 * 60 * 60 * 1000);
        return day;
    }

    /**
     * 形成如下的日历 , 根据传入的一个时间返回一个结构 星期日 星期一 星期二 星期三 星期四 星期五 星期六 下面是当月的各个时间
     * 此函数返回该日历第一行星期日所在的日期
     *
     * @param sdate
     * @return
     */
    public static String getNowMonth(String sdate) {
        // 取该时间所在月的一号
        sdate = sdate.substring(0, 8) + "01";

        // 得到这个月的1号是星期几
        Date date = DateUtil.strToDate(sdate);
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        int u = c.get(Calendar.DAY_OF_WEEK);
        String newday = DateUtil.getNextDay(sdate, (1 - u) + "");
        return newday;
    }

    /**
     * 取得数据库主键 生成格式为yyyymmddhhmmss+k位随机数
     *
     * @param k
     *            表示是取几位随机数,可以自己定
     */

    public static String getNo(int k) {

        return getUserDate("yyyyMMddhhmmss") + getRandom(k);
    }

    /**
     *
     * 获得当前时间的后多少天
     *
     * @param curr
     * @param count
     * @return
     */
    public static Date getNextDate(Date curr, int count) {
        Calendar  calendar = Calendar.getInstance();
        calendar.setTime(curr);
        calendar.add(Calendar.DAY_OF_MONTH, count);  
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date date=null;
        try {
            date= str2Date(sdf.format(calendar.getTime()),"yyyy-MM-dd");
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }

    /**
     * 返回一个随机数
     *
     * @param i
     * @return
     */
    public static String getRandom(int i) {
        Random jjj = new Random();
        // int suiJiShu = jjj.nextInt(9);
        if (i == 0)
            return "";
        String jj = "";
        for (int k = 0; k < i; k++) {
            jj = jj + jjj.nextInt(9);
        }
        return jj;
    }

    /**
     *
     * @param args
     */
    public static boolean RightDate(String date) {

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        ;
        if (date == null)
            return false;
        if (date.length() > 10) {
            sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        } else {
            sdf = new SimpleDateFormat("yyyy-MM-dd");
        }
        try {
            sdf.parse(date);
        } catch (ParseException pe) {
            return false;
        }
        return true;
    }

    /**
     * 返回两个年份的差值,多少年保留一位小数 prram yyyy-MM-dd
     */
    public static String timeToTime(String startdate, String enddate) {
        String date1[] = startdate.split("-");
        int year1 = 365;
        int month1 = 31;
        int day1 = Integer.parseInt(date1[2]);
        Long totalday1;
        String date2[] = enddate.split("-");
        int year2 = 365;
        int month2 = 31;
        int day2 = Integer.parseInt(date2[2]);
        Long totalday2;
        float time;
        if ("04".equals(date1[2]) || "06".equals(date1[2])
                || "09".equals(date1[2]) || "11".equals(date1[2])) {

            month1 = 30;
        }
        if ("02".equals(date1[2])) {
            if (isLeapYear(startdate)) {
                month1 = 29;
            } else {
                month1 = 28;
            }
        }
        if ("04".equals(date2[2]) || "06".equals(date2[2])
                || "09".equals(date2[2]) || "11".equals(date2[2])) {

            month2 = 30;
        }
        if ("02".equals(date2[2])) {
            if (isLeapYear(enddate)) {
                month2 = 29;
            } else {
                month2 = 28;
            }
        }
        totalday1 = Long.parseLong(date1[0]) * year1
                + Integer.parseInt(date1[1]) * month1 + day1;
        totalday2 = Long.parseLong(date2[0]) * year2
                + Integer.parseInt(date2[1]) * month2 + day2;
        time = (float) (totalday1 - totalday2) / 365;
        DecimalFormat df = new DecimalFormat("0.0");// 格式化小数,不足的补0
        return df.format(time);// 返回的是String类型的
    }
    /**   
     * @param date1 需要比较的时间 不能为空(null),需要正确的日期格式 ,如:2009-09-12  
     * @param date2 被比较的时间  为空(null)则为当前时间   
     * @param stype 返回值类型   0为多少天,1为多少个月,2为多少年   
     * @return   
     * 举例:
     * compareDate("2009-09-12", null, 0);//比较天
     * compareDate("2009-09-12", null, 1);//比较月
     * compareDate("2009-09-12", null, 2);//比较年
     */   
public static int compareDate(String startDay,String endDay,int stype){    
    int n = 0;    
    String[] u = {"天","月","年"};    
    String formatStyle = stype==1?"yyyy-MM":"yyyy-MM-dd";    
        
    endDay = endDay==null?getNowStrDate():endDay;    
        
    DateFormat df = new SimpleDateFormat(formatStyle);    
    Calendar c1 = Calendar.getInstance();    
    Calendar c2 = Calendar.getInstance();    
    try {    
        c1.setTime(df.parse(startDay));    
        c2.setTime(df.parse(endDay));    
    } catch (Exception e3) {    
        System.out.println("wrong occured");    
    }    
    //List list = new ArrayList();    
    while (!c1.after(c2)) {                   // 循环对比,直到相等,n 就是所要的结果    
        //list.add(df.format(c1.getTime()));    // 这里可以把间隔的日期存到数组中 打印出来    
        n++;    
        if(stype==1){    
            c1.add(Calendar.MONTH, 1);          // 比较月份,月份+1    
        }    
        else{    
            c1.add(Calendar.DATE, 1);           // 比较天数,日期+1    
        }    
    }    
    n = n-1;    
    if(stype==2){    
        n = (int)n/365;    
    }       
    System.out.println(startDay+" -- "+endDay+" 相差多少"+u[stype]+":"+n);          
    return n;    
}     
    /**
     * 向前或者向后推几个月
     *
     * @param date
     * @param month
     * @return
     */
    public static Date timeToBeforeOrAfter(String strDate, String formatDate,
            int num) {
        try {
            Calendar c = new GregorianCalendar();
            Date d = str2Date(strDate, "yyyy-MM-dd");
            c.setTime(d);
            c.add(Calendar.MONTH, num);
            SimpleDateFormat sdf = new SimpleDateFormat(formatDate);
            return str2Date(sdf.format(c.getTime()), formatDate);
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return null;
        }

    }

    /**
     * 得到两日期相差几个月
     *
     * @param String
     * @return
     */
    public static long getMonthDiff(String startDate, String endDate)
            throws ParseException {
        long monthday;
        SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
        Date startDate1 = fmt.parse(startDate);

        Calendar starCal = Calendar.getInstance();
        starCal.setTime(startDate1);

        int sYear = starCal.get(Calendar.YEAR);
        int sMonth = starCal.get(Calendar.MONTH);
        int sDay = starCal.get(Calendar.DAY_OF_MONTH);

        Date endDate1 = fmt.parse(endDate);
        Calendar endCal = Calendar.getInstance();
        endCal.setTime(endDate1);
        int eYear = endCal.get(Calendar.YEAR);
        int eMonth = endCal.get(Calendar.MONTH);
        int eDay = endCal.get(Calendar.DAY_OF_MONTH);

        monthday = ((eYear - sYear) * 12 + (eMonth - sMonth));

        // 这里计算零头的情况,根据实际确定是否要加1 还是要减1
        if (sDay < eDay) {
            monthday = monthday + 1;
        }
        return monthday;
    }
    
    /**
     * 奖string类型的日期转换为指定格式
     */
    public static Date stringToDate(String dateStr) {
        SimpleDateFormat formatter = new SimpleDateFormat(FORMAT_GENERAL1);
        Date ddd = null;
        try {
            ddd = formatter.parse(dateStr);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return ddd;
    }
    
    /**
     * 奖date类型的日期转换为指定格式
     */
    public static String dateToString(Date date) {
        SimpleDateFormat formatter = new SimpleDateFormat(FORMAT_GENERAL1);
        String ddd = null;
        ddd = formatter.format(date);
        return ddd;
    }
    
    public static String dateToString(Date date, String format) {
        SimpleDateFormat formatter = new SimpleDateFormat(format);
        String ddd = null;
        ddd = formatter.format(date);
        return ddd;
    }
    
    /**
     * 将string类型的日期转换为指定格式:返回格式1的日期
     * 错误将返回当前时间
     */
    public static Date stringToDate1(String dateStr) {
        SimpleDateFormat formatter = new SimpleDateFormat(FORMAT_GENERAL);
        Date ddd = new Date();
        try {
            ddd = formatter.parse(dateStr);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return ddd;
    }
    
    /**
     *
     * 获得当前时间加上或者减去几天的时间的字符串
     *
     * @param curr
     * @param count
     * @return
     */
    public static String getAddDateStr(String dateStr, int addDate) {
        Calendar  calendar = Calendar.getInstance();
        calendar.setTime(stringToDate(dateStr));
        calendar.add(Calendar.DATE, addDate);  
        return dateToString(calendar.getTime());
    }
    
    /**
     *
     * 获得当前时间加上或者减去几天的时间的字符串
     *
     * @param curr
     * @param count
     * @return
     */
    public static String getAddDateStr(Date date, int addDate) {
        Calendar  calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DATE, addDate);  
        return dateToString(calendar.getTime());
    }
    
    public static Date getAddDate(Date date, int addType, int addDate) {
        Calendar  calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(addType, addDate);  
        return calendar.getTime();
    }
    
    public static String formatDate(Date date, String format) {
        SimpleDateFormat formatter = new SimpleDateFormat(format);
        return formatter.format(date);
    }
    
    public static Date parseDate(String dateStr, String format) throws ParseException {
        SimpleDateFormat formatter = new SimpleDateFormat(format);
        return formatter.parse(dateStr);
    }
    
    public static Date parseDate(Date date, String format) throws ParseException {
        SimpleDateFormat formatter = new SimpleDateFormat(format);
        return formatter.parse(formatter.format(date));
    }
    
    /**
     * 定位到早上8点
     */
    public static Date goToEighth(Date date){
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 8);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }
    
    /**
     * 定位到0点
     */
    public static Date goToZero(Date date){
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }
    
    /**
     * @param d1 被减时间
     * @param d2 减时间
     * @return 两个时间相差的天数(忽略小时分钟秒)
     */
    public static int getDaysDiffer(Date d1, Date d2){
        double millisPerDay = DateUtils.MILLIS_PER_DAY;
        return (int) (d2.getTime()/millisPerDay-d1.getTime()/millisPerDay);
    }
    
    public static long getMinuteDIffer(String d1,String d2){
        try {
        DateFormat df_parseDate = new SimpleDateFormat(FORMAT_FLIGHT_PLAN);
        Date date1;
    
            date1 = (Date) df_parseDate.parse(d1);
    
        Date date2= (Date) df_parseDate.parse(d2);
        return (date1.getTime()-date2.getTime())/(1000*60);//得到分钟数
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return 0;
        
    }
    /**
     * @param d1
     * @return 返回日期是星期几
     */
    public static int getDateWeek(Date d1){
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(d1);
        int i = calendar.get(Calendar.DAY_OF_WEEK);
        if(i == 1){
            return 8;
        }
        return i;
    }
    
    public static void main(String[] args) throws ParseException {
//        System.out.println(parseDate("2015-08-28 14:10", FORMAT_GENERAL4));
//        System.out.println(getDateWeek(parseDate("2015-09-28", FORMAT_GENERAL1)));
//        System.out.println(getDateWeek(new Date()));
        Calendar c = Calendar.getInstance();
        c.add(Calendar.DATE, 0);
        System.out.println(getDaysDiffer(goToZero(new Date()), goToZero(c.getTime())));
    }
    
}


ParamsUtil.java


import java.util.Map;

import org.apache.commons.lang.StringUtils;


public class ParamsUtil {
    public static String nullDeal(Map params,String key,String defaultValue){
        if(params == null || params.get(key)==null || "".equals(params.get(key))){
            return StringUtils.isNotBlank(defaultValue) ? defaultValue : "";
        }
        return String.valueOf(params.get(key));
    }
}

TimeFormatUtil.java

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class TimeFormatUtil {
    public static String getInterval(Date createAt) {
        // 定义最终返回的结果字符串。
        String interval = null;

        long millisecond = new Date().getTime() - createAt.getTime();

        long second = millisecond / 1000;

        if (second <= 0) {
            second = 0;
        }
        //*--------------微博体(标准)
        if (second == 0) {
            interval = "刚刚";
        } else if (second < 30) {
            interval = second + "秒以前";
        } else if (second >= 30 && second < 60) {
            interval = "半分钟前";
        } else if (second >= 60 && second < 60 * 60) {//大于1分钟 小于1小时
            long minute = second / 60;
            interval = minute + "分钟前";
        } else if (second >= 60 * 60 && second < 60 * 60 * 24) {//大于1小时 小于24小时
            long hour = (second / 60) / 60;
            if (hour <= 3) {
                interval = hour + "小时前";
            } else {
                interval = "今天" + getFormatTime(createAt, "HH:mm");
            }
        } else if (second >= 60 * 60 * 24 && second <= 60 * 60 * 24 * 2) {//大于1D 小于2D
            interval = "昨天" + getFormatTime(createAt, "HH:mm");
        } else if (second >= 60 * 60 * 24 * 2 && second <= 60 * 60 * 24 * 7) {//大于2D小时 小于 7天
            long day = ((second / 60) / 60) / 24;
            interval = day + "天前";
        } else if ( second <= 60 * 60 * 24 * 365 && second >= 60 * 60 * 24 * 7) {//大于7天小于365天
            interval = getFormatTime(createAt, "MM-dd HH:mm");
        } else if (second >= 60 * 60 * 24 * 365) {//大于365天
            interval = getFormatTime(createAt, "yyyy-MM-dd HH:mm");
        } else {
            interval = "0";
        }
        return interval;
    }    
    public static String getFormatTime(Date date, String Sdf) {
        return (new SimpleDateFormat(Sdf)).format(date);
    }
    public static void main(String[] args) {
        try {
            System.out.println(getInterval(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2017-05-15 12:12:12")));
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}


数据库业务操作类

 继承BaseDaoImpl  可调用增删改查的方法

 UserInfoServiceImpl.java

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.zkkj.chat.dao.base.BaseDaoImpl;
import com.zkkj.chat.service.IUserService;

public class UserServiceImpl extends BaseDaoImpl implements IUserService {

    @Override
    public List<Map> getUserList(Map params) throws Exception {
        // TODO Auto-generated method stub
        return selectList("userInfo.getUserList",params);
    }

    @Override
    public Map getUserById(Map params) throws Exception {
        // TODO Auto-generated method stub
        return (Map) selectOneByParam("userInfo.selectUserById", params);
    }

    @Override
    public int updateUser(Map params) throws Exception {
        // TODO Auto-generated method stub
        return updateByParam("userInfo.updateUser", params);
    }
    
        @Override
    public int addUser(Map params) throws Exception {
        // TODO Auto-generated method stub
        int count =0;
        count =insert("userInfo.addUser", params);
        return count;
   }

}

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值