需求:坐席排队  五秒之内没请求接口认为用户不在排队 从内存移除

redis用作消息队列

1)实现过程: 用户请求接口  查询坐席有没有空闲,坐席没有空闲 继续排队   如果有 查询redis中有没有预先排队的用户  如果有 判断是否是队列中第一请求的用户,如果是分配坐席,如果不是 更新队列中的时间

2)添加一个定时任务类 每三百毫秒刷新一次  查询队列中有没有超时的用户 超时之后将其移除

检测坐席是否为空闲状态

如果有空闲坐席 通过websocket 发送给客户端消息 将坐席状态修改为2,请查看 ocatedseat 方法

比较麻烦的几点:

1)添加用户到队列中返回添加队列的值(这个是lpush返回的)然后吧当前的值 和用户请求过来的唯一id存到redis 中 以备 刷新时间用

long userindex = redisTemplate.opsForList().leftPush("GROUP_SEQUEUE_" + agent_group,JSONObject.toJSONString(map));

redisTemplate.opsForValue().set(uniqueid, String.valueOf(userindex));

2)刷新用户时间:取出队列的size 减去 第一步 存到redis中的值  然后修改队列中用户的时间

long size = redisTemplate.opsForList().size("GROUP_SEQUEUE_" + agent_group);

String index = redisTemplate.opsForValue().get(uniqueid);

long sub = size - Integer.parseInt(index);

redisTemplate.opsForList().set("GROUP_SEQUEUE_" + agent_group,sub, JSONObject.toJSONString(map));

3)坐席状态同步问题

weibsocket 心跳 必须与 状态分开来发送否则会出现, 服务端发送给客户端状态为分配中时 而 客户端在接受到的时候会有时间间隔,在这个间隔的时候 客户端会发送之前的状态给服务端 造成状态不一致问题

public void handleMessage(WebSocketSession session,WebSocketMessage>message){

//解析message 修改坐席状态

try{

User user = (User)session.getAttributes().get("user");

String state = message.getPayload().toString();

JSONObject json = JSONObject.parseObject(state);

String type = (String) json.get("type");

Object state1 = json.get("status");

String name = (String) json.get("name");

if("0".equals(type)){

//修改状态

if(Integer.parseInt(state1.toString())==0){

redisTemplate.opsForSet().remove("OCATED", user.getUserid().toString());

}

redisTemplate.opsForList().remove("GROUP_"+user.getGroupid(), 0, JSONObject.toJSONString(user));

user.setState(Integer.parseInt(state1.toString()));

user.setNext_hop("123.57.144.26:9060/udp");

user.setTo("");

redisTemplate.opsForList().leftPush("GROUP_"+user.getGroupid(), JSONObject.toJSONString(user));

logger.info("收到坐席{}发来得消息!当前状态为:{} ",user.getUserid(),state1,DateUtil.formateDate(new Date(), "yyyy-MM-dd HH:mm:ss"));

}else if("1".equals(type)){

//只检测坐席是否掉线

this.hart.put(user, new Date());

// session.sendMessage(new TextMessage(JSONObject.toJSONString(user)+" "+new Date()));

}else if("2".equals(type)){

//会话接通 保存到redis中 定时插入数据库

boolean flag = false;

Conversation con = new Conversation();

String reportnum = (String) json.get("reportnum");

String carnum = (String) json.get("carnum");

List str = redisTemplate.opsForList().range("conversation", 0, -1);

for(String s : str){

JSONObject j = JSONObject.parseObject(s);

con = JSONObject.toJavaObject(j,Conversation.class);

if(con.getCarnum().equals(carnum) && con.getReportnum().equals(reportnum)){

flag = true;

}

}

if(flag){

//已经保存过一次

Long endtime = (Long) json.get("endtime");

con.setEndtime(DateUtil.timestampToDate(endtime.toString()));;

}else{

Long starttime = (Long) json.get("starttime");

con.setUserid(user.getUserid());

con.setCarnum(carnum);

con.setStarttime(DateUtil.timestampToDate(starttime.toString()));

con.setReportnum(reportnum);

}

redisTemplate.opsForList().leftPush("conversation", JSONObject.toJSONString(con));

}else{

session.sendMessage(new TextMessage("参数错误!"));

}

}catch(Exception e){

logger.error(e.getMessage(),e);

}

}

下面奉上代码

1、controller

package com.cloudunicomm.controller;

import java.io.IOException;

import java.io.OutputStream;

import java.text.ParseException;

import java.util.Date;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import java.util.Map.Entry;

import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Bean;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.ResponseBody;

import org.springframework.web.socket.WebSocketSession;

import com.alibaba.fastjson.JSON;

import com.alibaba.fastjson.JSONObject;

import com.cloudunicomm.interceptor.MySocketHandler;

import com.cloudunicomm.utils.DateUtil;

import com.cloudunicomm.vo.User;

@Controller

public class FreeController {

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

@Autowired

private RedisTemplate redisTemplate;

@Bean

public MySocketHandler mySorketHandle() {

return new MySocketHandler();

}

@RequestMapping(value = "agent_service", method = RequestMethod.GET)

@ResponseBody

public void agentservice(HttpServletRequest request, HttpServletResponse response,

@RequestParam(name = "customer_id", required = false) String customer_id,

@RequestParam(name = "agent_group", required = false) String agent_group,

@RequestParam(name = "uniqueid", required = false) String uniqueid) throws IOException, ParseException {

//logger.info("组:{}---用户:{}----唯一id:{}过来请求接口!",agent_group,customer_id,uniqueid);

StringBuffer sb = new StringBuffer();

OutputStream out = response.getOutputStream();

response.setHeader("Content-Disposition", "attachment; filename=" + DateUtil.getDate() + ".ini");

sb.append("reply_code=200\r\n").append("reply_reason='OK'\r\n");

//查询有没有空闲坐席

List users = redisTemplate.opsForList().range("GROUP_" + agent_group, 0, -1);

for (String s : users) {

User user = (User) JSONObject.toJavaObject(JSONObject.parseObject(s), User.class);

Boolean aa = redisTemplate.opsForSet().isMember("OCATED", user.getUserid().toString());

if (user.getState() == 0 && aa==false) {

//查询有没有排队用户

List lists = redisTemplate.opsForList().range("GROUP_SEQUEUE_" + agent_group, 0, -1);

if (lists.size() > 0) {

String str = lists.get(lists.size() - 1);

Set> obj = JSON.parseObject(str).entrySet();

for (Entry s1 : obj) {

if (s1.getKey().split("_")[1].equals(uniqueid)) {

redisTemplate.opsForList().remove("GROUP_SEQUEUE_"+agent_group, 0, JSONObject.toJSONString(s1));

this.ocatedseat(agent_group, user, customer_id, uniqueid);

sb.append("next_hop="+user.getNext_hop()+"\r\n");

sb.append("To="+user.getTo());

out.write(sb.toString().getBytes());

out.flush();

out.close();

return ;

//return ResultMessage.getSuccess().setMessage("已分配作息!");

}

}

}else{

this.ocatedseat(agent_group, user, customer_id, uniqueid);

sb.append("next_hop="+user.getNext_hop()+"\r\n");

sb.append("To="+user.getTo());

out.write(sb.toString().getBytes());

out.flush();

out.close();

return ;

}

}

}

String exit = redisTemplate.opsForValue().get(agent_group+"_"+uniqueid);

if(null == exit){

//没有

this.adduserqueue(agent_group, uniqueid);

//return ResultMessage.getSuccess().setMessage("请等待!");

sb.append("retry_wait=5s");

out.write(sb.toString().getBytes());

out.flush();

out.close();

return;

}else{

//return ResultMessage.getSuccess().setMessage("继续等待");

this.resettime(agent_group, uniqueid);

sb.append("retry_wait=5s");

out.write(sb.toString().getBytes());

out.flush();

out.close();

return;

}

}

/**

* 重置时间

* @param agent_group

* @param uniqueid

*/

public void resettime(String agent_group,String uniqueid){

Map map = new HashMap<>();

map.put(agent_group + "_" + uniqueid, new Date());

long size = redisTemplate.opsForList().size("GROUP_SEQUEUE_" + agent_group);

String index = redisTemplate.opsForValue().get(agent_group+"_"+uniqueid);

long sub = size - Integer.parseInt(index);

redisTemplate.opsForList().set("GROUP_SEQUEUE_" + agent_group,sub, JSONObject.toJSONString(map));

}

/**

* 添加用户到队列中

* @param map

* @param agent_group

* @param uniqueid

*/

public void adduserqueue(String agent_group,String uniqueid){

logger.info("组{}唯一{} 加入队列",agent_group,uniqueid);

Map map = new HashMap<>();

map.put(agent_group + "_" + uniqueid, new Date());

long userindex = redisTemplate.opsForList().leftPush("GROUP_SEQUEUE_" + agent_group,JSONObject.toJSONString(map));

redisTemplate.opsForValue().set(agent_group+"_"+uniqueid, String.valueOf(userindex));

}

public void ocatedseat(String agent_group,User user ,String customer_id,String uniqueid) throws ParseException{

redisTemplate.opsForSet().add("OCATED", user.getUserid().toString());

user.setState(2);

WebSocketSession session = MySocketHandler.sessionid.get(user.getUserid().toString());

mySorketHandle().pushMsg(session.getId(), "2");

updateIndex(agent_group,uniqueid);

//sb.append(user.getNext_hop() + "\r\n").append(user.getTo());

logger.info("坐席{}正在被分配给{} ! {}", user.getUserid(),uniqueid,DateUtil.formateDate(new Date(), "yyyy-MM-dd HH:mm:ss"));

}

/**

* 更新索引值

* @param agent_group

* @param uniqueid

*/

private void updateIndex(String agent_group, String uniqueid) {

Set index = redisTemplate.keys(agent_group+"_*");

//比较的value

String comvalue = redisTemplate.opsForValue().get(agent_group+"_"+uniqueid);

for(String s : index ){

int l = Integer.parseInt(redisTemplate.opsForValue().get(s));

if(l>Integer.parseInt(comvalue)){

int reslu = l-1;

redisTemplate.opsForValue().set(s,String.valueOf(reslu));

}

}

redisTemplate.delete(agent_group+"_"+uniqueid);

}

}

2、定时任务

package com.cloudunicomm.task;

import java.text.ParseException;

import java.util.Date;

import java.util.List;

import java.util.Map;

import java.util.Map.Entry;

import java.util.Set;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.scheduling.annotation.Scheduled;

import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;

/**

* 检测redis中过期的key

* @Title: CheckRedisKeyExpireTask.java

* @date: 2018年6月29日

* @author: zhangdong

* @version: v1.0

* @Description

*/

@Component

public class CheckRedisKeyExpireTask {

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

@Autowired

private RedisTemplate redisTemplate;

@Scheduled(cron="0/2 * * * * ? ") //每秒执行一次

//@Scheduled(fixedRate=3000)

public void job() throws ParseException {

//logger.info("开始检测有没有过期的key "+DateUtil.formateDate(new Date(), "yyyy-MM-dd HH:mm:ss"));

Set set = redisTemplate.keys("GROUP_SEQUEUE_*");

for(String s:set){

List sss = (List) redisTemplate.opsForList().range(s, 0, -1);

for(String m : sss){

Map maps = (Map)JSON.parse(m);

for(Entry entry : maps.entrySet()){

long start = entry.getValue();

long end = new Date().getTime();

if(end-entry.getValue()>500000){

//超时 从redis中移除

String[] keys = entry.getKey().toString().split("_");

redisTemplate.opsForList().remove("GROUP_SEQUEUE_"+keys[0], 0, maps.toString());

redisTemplate.delete(entry.getKey());

logger.info("组{}中的值{}已超过{}秒,将其移除!","GROUP_SEQUEUE_"+keys[0],maps.toString(),50);

}

}

}

}

}

}

坐席排序java_坐席排队功能 - osc_sd6j22mg的个人空间 - OSCHINA - 中文开源技术交流社区...相关推荐

  1. android加入聊天功能,app实现聊天功能 - houwanmin的个人空间 - OSCHINA - 中文开源技术交流社区...

    .  OpenIM(Android)主体功能集成 1.1  前置准备 如果您单纯是想体验OpenIM的功能,建议直接跳过这一步.直接查看快速集成. 在这个集成教程中,我们使用已创建的Demo应用,向您 ...

  2. python日常工作_python日常工作 - osc_sd6j22mg的个人空间 - OSCHINA - 中文开源技术交流社区...

    分析nginx日志,得出用户ip及用户相关信息.(访问次,流量量大,相应时间) logip = {'1.1.1.1':[200,20M,1S],} 'please' enter your want i ...

  3. C语言十个字母用冒泡法排序,冒泡排序法(C语言) - osc_wq8j2a9a的个人空间 - OSCHINA - 中文开源技术交流社区...

    常用的排序方法有冒泡排序法,选择排序法,插入排序法以及希尔排序法等.本文着重讲解如何利用C代码,实现冒泡排序. 首先,要了解什么是冒泡排序.冒泡排序是常用的一种排序方法,其基本方法就是逐次比较.即一次 ...

  4. java线程归并排序_Java-归并排序 - FeanLau的个人空间 - OSCHINA - 中文开源技术交流社区...

    public class MergeSort { static int number=0; public static void main(String[] args) { int[] a = {26 ...

  5. java 实现按规则自增功能_java运算符详解 - osc_74vaali6的个人空间 - OSCHINA - 中文开源技术交流社区...

    java基础(2)--运算符详解 自增自减规则 自增自减就是常用的 a = a++ 或者 a = ++a;前者是后缀式,先把a赋值给a,再加一:后者是后缀式,先加一,在赋值给a;而且a++,++a并不 ...

  6. java选择排序不稳定_选择排序就这么简单 - Java3y的个人空间 - OSCHINA - 中文开源技术交流社区...

    选择排序就这么简单 从上一篇已经讲解了冒泡排序了,本章主要讲解的是选择排序,希望大家看完能够理解并手写出选择排序的代码,然后就通过面试了!如果我写得有错误的地方也请大家在评论下指出. 选择排序介绍和稳 ...

  7. 错误票据java_【蓝桥杯】错误票据 - osc_bskh1wlw的个人空间 - OSCHINA - 中文开源技术交流社区...

    错误票据 某涉密单位下发了某种票据,并要在年终全部收回. 每张票据有唯一的ID号.全年所有票据的ID号是连续的,但ID的开始数码是随机选定的. 因为工作人员疏忽,在录入ID号的时候发生了一处错误,造成 ...

  8. java把abcedf字符串进行排序_字符串合并处理 - 一贱书生的个人空间 - OSCHINA - 中文开源技术交流社区...

    题目描述 按照指定规则对输入的字符串进行处理. 详细描述: 将输入的两个字符串合并. 对合并后的字符串进行排序,要求为:下标为奇数的字符和下标为偶数的字符分别从小到大排序.这里的下标意思是字符在字符串 ...

  9. c语言选择排序输出指定趟数结果,C语言之选择排序 - 杨源鑫的个人空间 - OSCHINA - 中文开源技术交流社区...

    选择法排序是相对好理解的排序算法.假设要对含有n个数的序列进行升序排列,算法步骤是: 1.从数组存放的n个数中找出最小数的下标(算法见下面的"求最值"),然后将最小数与第1个数交换 ...

最新文章

  1. iphone adb android,通过ADB获取Android手机信息
  2. 1.1 Friday the Thirteenth
  3. centos7+samba 安装与调试记录
  4. 机器学里面的一些概念-召回率,精确度等的介绍
  5. vector删除第i个元素_LeetCode每日一题 Q215数组中的第K个最大元素
  6. 物联网、车联网、工业互联网大数据平台,为什么推荐使用TDengine?
  7. Java 使用poi导入excel,结合xml文件进行数据验证的例子(增加了jar包)
  8. 数据科学 IPython 笔记本 9.2 NumPy 简介
  9. Vue中,在<template>内进行页面链接跳转
  10. WebSocket跨域问题解决
  11. Java8初体验(一)lambda表达式语法
  12. 铁三角- 倒数348天
  13. 1.5.12、面向对象下__接口Implement,接口的使用
  14. 工程学导论-第四章-创造力
  15. 倒计时 妙味课堂_妙味课堂——JavaScript基础课程笔记
  16. XYplorer 21.50.0100多语言版,资源管理器件之一
  17. 海行Newlifest M1骨传导耳机开箱,这音质真的碉堡了
  18. java画太极加图片_Android 画一个太极图实例代码
  19. android高德定位sdk,android ------ 实现高德定位并获取相应信息 ( 最新版高德SDK 和 Android SDK版本)...
  20. 数论数学:所有自然数之和为-1/12的证明

热门文章

  1. 【Python】【pywin32】【指定窗口截图】
  2. 华为鸿蒙系统下载猫薄荷,鸿蒙2.0官网版下载-华为鸿蒙2.0系统下载-ROM之家
  3. n行由字符c构成的倒直角三角形图案
  4. Introduction to JavaScript(JavaScript简介)
  5. Java架构师Day02-源码分析之Spring5
  6. 合资车、国产车 和 新能源汽车都有哪些品牌?
  7. Financials
  8. 网络安全学习Day3
  9. 外呼系统《智能录音外呼IVR和智能外呼机器人》
  10. extern “C” 陷阱