赞
踩
<!-- 添加Mysql依赖 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- 添加mybatis依赖 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <!-- 添加JDBC依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!-- 添加Druid依赖 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- 添加分页插件 --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> <version>2.0.4.RELEASE</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.35</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
import com.alibaba.fastjson.JSONObject; import com.example.constant.CustomSEnum; import com.example.pojo.dto.CustomSDTO; import com.example.pojo.vo.MessageVo; import com.example.tools.RedisTools; import com.example.tools.SpringUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * @Author: 凉白开不加冰 * @Version: 0.0.1V * @Date: 2018/12/19 * @Description: 客服websocket **/ @Slf4j @Component @ServerEndpoint("/websocketServer/{username}") public class WebSocketServer { private StringRedisTemplate stringRedisTemplate = SpringUtils.getBean(StringRedisTemplate.class); /** * 以用户的姓名为key,WebSocket为对象保存起来 */ private static Map<String, WebSocketServer> clients = new ConcurrentHashMap<>(); /** * 会话 */ private Session session; /** * 用户名 */ private String username; @OnOpen public void onOpen(@PathParam("username") String username, Session session) { log.info("登录用户会话 --> {}", session); log.info("登录用户名称 --> {}", username); this.username = username; this.session = session; clients.put(username, this); if (!RedisTools.existsHash(stringRedisTemplate, CustomSEnum.CUSTOM_S_LIST.getKey(), username)) { CustomSDTO customSDTO = new CustomSDTO(); customSDTO.setNumber(0); customSDTO.setServer(username); RedisTools.hset(stringRedisTemplate, CustomSEnum.CUSTOM_S_LIST.getKey(), username, JSONObject.toJSONString(customSDTO)); } } @OnError public void onError(Session session, Throwable error) { log.info("服务端发生了错误" + error.getMessage()); } @OnClose public void onClose() { log.info("关闭会话 --> {}", username); clients.remove(username); if (RedisTools.existsHash(stringRedisTemplate, CustomSEnum.CUSTOM_S_LIST.getKey(), username)){ RedisTools.hdel(stringRedisTemplate,CustomSEnum.CUSTOM_S_LIST.getKey(),username); } log.info("线上用户 --> {}", clients); } public void sendMessageToServer(MessageVo messageVo) throws IOException { for (WebSocketServer item : clients.values()) { if (item.username.equals(messageVo.getServer())) { item.session.getAsyncRemote().sendText(JSONObject.toJSONString(messageVo)); break; } } } }
import lombok.Getter; import lombok.Setter; /** * @Author: 凉白开不加冰 * @Version: 0.0.1V * @Date: 2018/12/19 * @Description: 客服信息枚举类 **/ public enum CustomSEnum { CUSTOM_S_LIST("customs:list"); @Setter @Getter private String key; CustomSEnum(String key) { this.key = key; } public static CustomSEnum get(String key) { for (CustomSEnum c : CustomSEnum.values()) { if (c.key == key) { return c; } } return null; } }
import org.springframework.beans.BeansException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.stereotype.Repository; /** * @Author: 凉白开不加冰 * @Version: 0.0.1V * @Date: 2018/12/19 * @Description: 上下文工具类 **/ @Repository public class SpringUtils implements BeanFactoryPostProcessor { //Spring应用上下文环境 private static ConfigurableListableBeanFactory beanFactory; @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { SpringUtils.beanFactory = beanFactory; } public static ConfigurableListableBeanFactory getBeanFactory() { return beanFactory; } /** * 获取对象 * * @param name * @return Object 一个以所给名字注册的bean的实例 * @throws org.springframework.beans.BeansException * */ @SuppressWarnings("unchecked") public static <T> T getBean(String name) throws BeansException { return (T) getBeanFactory().getBean(name); } /** * 获取类型为requiredType的对象 * * @param clz * @return * @throws org.springframework.beans.BeansException * */ public static <T> T getBean(Class<T> clz) throws BeansException { T result = (T) getBeanFactory().getBean(clz); return result; } /** * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true * * @param name * @return boolean */ public static boolean containsBean(String name) { return getBeanFactory().containsBean(name); } /** * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) * * @param name * @return boolean * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException * */ public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { return getBeanFactory().isSingleton(name); } /** * @param name * @return Class 注册对象的类型 * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException * */ public static Class<?> getType(String name) throws NoSuchBeanDefinitionException { return getBeanFactory().getType(name); } /** * 如果给定的bean名字在bean定义中有别名,则返回这些别名 * * @param name * @return * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException * */ public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { return getBeanFactory().getAliases(name); } }
import lombok.Data;
/**
* @Author: 凉白开不加冰
* @Version: 0.0.1V
* @Date: 2018/12/19
* @Description: 客服消息类
**/
@Data
public class CustomSDTO {
private String server;
private Integer number;
}
import com.alibaba.fastjson.JSON; import org.springframework.data.redis.core.StringRedisTemplate; import java.util.List; import java.util.concurrent.TimeUnit; /** * @Author: 凉白开不加冰 * @Version: 0.0.1V * @Date: 2018/11/20 * @Description: redis工具类 **/ public class RedisTools { public static <T> List<T> hget(StringRedisTemplate stringRedisTemplate,String key,Class<T> clazz){ List<Object> list = stringRedisTemplate.opsForHash().values(key); return JSON.parseArray(list.toString(), clazz); } public static <T> T hget(StringRedisTemplate stringRedisTemplate,String h,String key,Class<T> clazz){ Object o = stringRedisTemplate.opsForHash().get(h,key); return JSON.parseObject(o.toString(), clazz); } public static void hset(StringRedisTemplate stringRedisTemplate,String h,String key,String value){ stringRedisTemplate.opsForHash().put(h,key,value); } public static void expire(StringRedisTemplate stringRedisTemplate,String key,Integer time){ stringRedisTemplate.expire(key,time, TimeUnit.SECONDS); } public static Boolean existsHash(StringRedisTemplate stringRedisTemplate,String h,String key){ return stringRedisTemplate.opsForHash().hasKey(h,key); } public static Boolean existsValue(StringRedisTemplate stringRedisTemplate,String key){ return stringRedisTemplate.hasKey(key); } public static Long hdel(StringRedisTemplate stringRedisTemplate,String h,String key){ return stringRedisTemplate.opsForHash().delete(h,key); } public static void set(StringRedisTemplate stringRedisTemplate,String key,String value){ stringRedisTemplate.opsForValue().set(key,value); expire(stringRedisTemplate,key,60*60); } public static <T> T get(StringRedisTemplate stringRedisTemplate,String key,Class<T> clazz){ Object object = stringRedisTemplate.opsForValue().get(key); return JSON.parseObject(object.toString(), clazz); } }
import lombok.Data;
/**
* @Author: 凉白开不加冰
* @Version: 0.0.1V
* @Date: 2018/12/19
* @Description: 消息内容
**/
@Data
public class MessageVo {
private String client;
private String server;
private String message;
private String userId;
}
import com.example.pojo.vo.ClientMessageVo; import com.example.pojo.vo.MessageVo; import com.example.service.CustomSService; import com.example.websocket.WebSocketClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.io.IOException; /** * @Author: 凉白开不加冰 * @Version: 0.0.1V * @Date: 2018/12/19 * @Description: 发送回复消息Controller **/ @RestController public class SendMessageController { @Autowired private WebSocketClient webSocketClient; @Autowired private CustomSService customSService; /** * @Author: 凉白开不加冰 * @Date: 2018/12/19 13:39 * @Description: 用户发送消息给客服 **/ @RequestMapping("sendToCs") public void sendToCs(@RequestBody ClientMessageVo clientMessageVo) throws IOException { //分配客服 customSService.sendToCs(clientMessageVo); } /** * @Author: 凉白开不加冰 * @Date: 2018/12/19 13:39 * @Description: 客服发送消息给用户 **/ @RequestMapping("sendClient") public void sendClient(@RequestBody MessageVo messageVo) throws IOException { webSocketClient.sendMessageToClient(messageVo); } }
import com.example.pojo.vo.ClientMessageVo;
import java.io.IOException;
/**
* @Author: 凉白开不加冰
* @Version: 0.0.1V
* @Date: 2018/12/19
* @Description: 用户发送消息接口
**/
public interface CustomSService {
void sendToCs(ClientMessageVo clientMessageVo) throws IOException;
}
import com.alibaba.fastjson.JSONObject; import com.example.constant.ConversationEnum; import com.example.constant.CustomSEnum; import com.example.pojo.dto.ConversationDTO; import com.example.pojo.dto.CustomSDTO; import com.example.pojo.vo.ClientMessageVo; import com.example.pojo.vo.MessageVo; import com.example.service.CustomSService; import com.example.tools.RedisTools; import com.example.tools.SortListTools; import com.example.websocket.WebSocketServer; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import java.io.IOException; import java.util.List; /** * @Author: 凉白开不加冰 * @Version: 0.0.1V * @Date: 2018/12/19 * @Description: 用户发送消息实现类 **/ @Slf4j @Service public class CustomSServiceImpl implements CustomSService { @Autowired private StringRedisTemplate stringRedisTemplate; @Autowired private WebSocketServer webSocket; @Override public void sendToCs(ClientMessageVo clientMessageVo) throws IOException { log.info(clientMessageVo.getMessage()); //获取聊天会话 boolean flag = RedisTools.existsValue(stringRedisTemplate, ConversationEnum.CONVERSATION_LIST.getKey() + clientMessageVo.getUserId()); if (!flag) { //分配新的客服 List<CustomSDTO> lists = RedisTools.hget(stringRedisTemplate, CustomSEnum.CUSTOM_S_LIST.getKey(), CustomSDTO.class); List<CustomSDTO> customLists = (List<CustomSDTO>) SortListTools.sort(lists, "number", SortListTools.ASC); CustomSDTO customSDTO = customLists.get(0); //将消息发送给人数最小的客服 MessageVo messageVo = new MessageVo(); messageVo.setMessage(clientMessageVo.getMessage()); messageVo.setClient(clientMessageVo.getClient()); messageVo.setUserId(clientMessageVo.getUserId()); messageVo.setServer(customSDTO.getServer()); webSocket.sendMessageToServer(messageVo); //新增用户会话 ConversationDTO conversationDTO = new ConversationDTO(); conversationDTO.setServer(customSDTO.getServer()); conversationDTO.setUserId(clientMessageVo.getUserId()); RedisTools.set(stringRedisTemplate, ConversationEnum.CONVERSATION_LIST.getKey() + clientMessageVo.getUserId(), JSONObject.toJSONString(conversationDTO)); //新增客服服务人数 customSDTO.setNumber(customSDTO.getNumber() + 1); RedisTools.hset(stringRedisTemplate, CustomSEnum.CUSTOM_S_LIST.getKey(), customLists.get(0).getServer(), JSONObject.toJSONString(customSDTO)); return; } //获取会话 ConversationDTO conversationDTO = RedisTools.get(stringRedisTemplate, ConversationEnum.CONVERSATION_LIST.getKey() + clientMessageVo.getUserId(), ConversationDTO.class); MessageVo messageVo = new MessageVo(); messageVo.setMessage(clientMessageVo.getMessage()); messageVo.setClient(clientMessageVo.getClient()); messageVo.setUserId(clientMessageVo.getUserId()); messageVo.setServer(conversationDTO.getServer()); webSocket.sendMessageToServer(messageVo); } }
import lombok.Data;
/**
* @Author: 凉白开不加冰
* @Version: 0.0.1V
* @Date: 2018/12/19
* @Description: 用户发送消息参数
**/
@Data
public class ClientMessageVo {
private String message;
private String userId;
private String client;
}
import lombok.Getter; import lombok.Setter; /** * @Author: 凉白开不加冰 * @Version: 0.0.1V * @Date: 2018/12/19 * @Description: 客服用户会话枚举 **/ public enum ConversationEnum { CONVERSATION_LIST("conversation:list:"); @Setter @Getter private String key; ConversationEnum(String key) { this.key = key; } public static ConversationEnum get(String key) { for (ConversationEnum c : ConversationEnum.values()) { if (c.key == key) { return c; } } return null; } }
import lombok.Data;
/**
* @Author: 凉白开不加冰
* @Version: 0.0.1V
* @Date: 2018/12/19
* @Description: 客服用户会话参数
**/
@Data
public class ConversationDTO {
private String server;
private String userId;
}
import com.alibaba.fastjson.JSONObject; import com.example.pojo.vo.MessageVo; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * @Author: 凉白开不加冰 * @Version: 0.0.1V * @Date: 2018/12/19 * @Description: 客户端 **/ @Slf4j @Component @ServerEndpoint("/websocketClient/{username}") public class WebSocketClient { /** * 以用户的姓名为key,WebSocket为对象保存起来 */ private static Map<String, WebSocketClient> clients = new ConcurrentHashMap<>(); /** * 会话 */ private Session session; /** * 用户名 */ private String username; @OnOpen public void onOpen(@PathParam("username") String username, Session session) { log.info("登录用户会话 --> {}", session); log.info("登录用户名称 --> {}", username); this.username = username; this.session = session; clients.put(username, this); } @OnError public void onError(Session session, Throwable error) { log.info("服务端发生了错误" + error.getMessage()); } @OnClose public void onClose() { log.info("关闭会话 --> {}", username); clients.remove(username); log.info("线上用户 --> {}", clients); } public void sendMessageToClient(MessageVo messageVo) throws IOException { for (WebSocketClient item : clients.values()) { if (item.username.equals(messageVo.getClient())) { item.session.getAsyncRemote().sendText(JSONObject.toJSONString(messageVo)); break; } } } }
代码下载地址
https://gitee.com/huc003/hucheng-springcloud-v1
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。