基于ssm+freemaker+WebSocket实现的购物商城抢单功能
2017-08-23 16:07
671 查看
业务流程描述
1. websocket-api.jar包引入
tomcat安装目录lib下的websocket-api.jar包2. web.xml配置
在web.xml配置一个监听,作用为定时清除抢单缓存信息。<listener> <listener-class>com.chw.webSocket.QdWebSocketListener</listener-class> </listener>
3. 线程类定义
public class QdClearMapThread extends Thread { @Override public void run() { while (!this.isInterrupted()) {// 线程未中断执行循环 try { //每隔1分钟执行一次 Thread.sleep(1000*60); if(!QdWebSocketUtil.webSocketMap.isEmpty()){ try { QdWebSocketUtil.onMessage("errorQd", null); } catch (IOException e) { e.printStackTrace(); } //清除客户端webSocket缓存信息 QdWebSocketUtil.webSocketMap.clear(); } } catch (InterruptedException e) { e.printStackTrace(); //错误日志打印 ChwLogUtil.getTradeLogger().info(System.currentTimeMillis()+"删除webSocketMap失败!"); } } } }
4. 监听类定义
主要监听线程类,规定时间间隔(1分钟)执行线程的run()方法public class QdWebSocketListener implements ServletContextListener { private QdClearMapThread thread; @Override public void contextDestroyed(ServletContextEvent arg0) { if (thread != null && thread.isInterrupted()) { thread.interrupt(); } } @Override public void contextInitialized(ServletContextEvent arg0) { String str = null; if (str == null && thread == null) { thread = new QdClearMapThread(); thread.start(); } } }
5. 买家发布抢单(jsp页面省略,只显示基本js内容)
<script type="text/javascript"> var websocket = null; //判断当前浏览器是否支持WebSocket if ('WebSocket' in window) { websocket = new WebSocket('${websocketUrl}'); }else { alert('当前浏览器 Not support websocket') } //接收到消息的回调方法 websocket.onmessage = function (event) { var data = event.data; if(data.indexOf("无商家抢单")!=-1){ alert(event.data); }else if(data.indexOf("有商家抢单")!=-1){ alert(event.data); window.location.href="${context_path}/lpt/qd/payQd"; } } //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。 window.onbeforeunload = function () { closeWebSocket(); } //关闭WebSocket连接 function closeWebSocket() { websocket.close(); } //发送消息 function send() { //确定抢单,封装数据(运费、服务费、地址、到达时间) var message = submitQd(); //校验是否重复提交 var url = encodeURI(encodeURI("${context_path}/lpt/qd/checkSendMessage/"+message)); $.ajax({ type:"get", url:url, cache: false, success : function(msg){ if(msg.type == "success"){ websocket.send(message);//调用websocket的send()方法发送信息 alert(msg.content); } else { alert(msg.content); } } }); } </script>
6. 买家发布逻辑控制
从页面接收到的抢单信息会存在全局变量中。public static Map<Session,String> webSocketMap = new HashMap<Session,String>();
如新发布的抢单信息存在于webSocketMap中,返回’该抢单已发布,请等待商家抢单‘。
如买家所发布过的抢单信息在时间(在监听中配置)内无卖家抢单,线程会清除webSocketMap全部信息并返回’您发布的抢单无商家抢单,请回购物车重新发布抢单‘。
如买家所发布过的抢单信息在时间(在监听中配置)有卖家抢单,线程会清除webSocketMap中已被卖家抢中的订单信息并返回’您发布的抢单无商家抢单,请回购物车重新发布抢单‘。
如校验通过,抢单信息会在卖家端的抢单列表中显示该信息。
@RequestMapping(value="/checkSendMessage/{message}",method = RequestMethod.GET) @ResponseBody public Message checkSendMessage(@PathVariable("message")String message) throws UnsupportedEncodingException{ String sendInfo = java.net.URLDecoder.decode(message, "utf-8"); if(QdWebSocketUtil.webSocketMap.size() > 0){ for(Session key:QdWebSocketUtil.webSocketMap.keySet()){ if(sendInfo.equals(QdWebSocketUtil.webSocketMap.get(key))){ return Message.error("该抢单已发布,请等待商家抢单!"); } } } return Message.success("发布抢单成功!"); }
@OnMessage public static void onMessage(String message, Session session) throws IOException { System.out.println("来自客户端的消息session="+session+ message); for(QdWebSocketUtil item: webSocketSet){ if(null !=session){//群发 webSocketMap.put(session, message); for(Session key:webSocketMap.keySet()){ item.sendMessage(webSocketMap.get(key)); } }else if(null ==session && "errorQd".equals(message)){//返回客户端提示信息--抢单失败 for(Session key:webSocketMap.keySet()){ if(item.session.getId().equals(key.getId())){ item.sendMessage("您发布的抢单无商家抢单,请回购物车重新发布抢单!"); } } }else if(null ==session && "successQd".equals(message.split(";")[0].toString())){//返回客户端提示信息--抢单成功 for(Session key:webSocketMap.keySet()){ if(webSocketMap.get(key).contains(message.split(";")[1].toString())){ item.sendMessage("您发布的抢单已有商家抢单,请及时查看!"); webSocketMap.remove(key); } } } } }
7. 卖家抢单页面
查看页面省略。抢单页面js(在抢单列表中可查看当前买家发布的抢单内容)。
var websocket = null; //判断当前浏览器是否支持WebSocket if ('WebSocket' in window) { websocket = new WebSocket('${websocketUrl}'); }else { alert('当前浏览器 Not support websocket') } //接收到消息的回调方法 websocket.onmessage = function (event) { $.ajax({ type:"get", url:$("form").attr("action"), cache: false, success : function(message){ window.location.reload(); } }); } //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。 window.onbeforeunload = function () { closeWebSocket(); } //关闭WebSocket连接 function closeWebSocket() { websocket.close(); } //抢单 function grabSingle(infoStr){ $.ajax({ type:"get", url:"${context_path}/lpt/qd/grabSingle/"+encodeURI(encodeURI(infoStr)), cache: false, success : function(message){ if(message.type == "success"){ alert(message.content); } else { alert(message.content); } window.location.reload(); } }); }
8. 卖家逻辑控制
抢单信息校验- 如webSocketMap中为空,返回’对不起!该订单已失效’。
- 如webSocketMap中不为空,但根据session获取不到信息,返回’对不起!该订单已被其他商家抢单’。
商品校验
- ‘对不起!您的店铺内没有【”+buyDpspInfo.getSpmc()+”】商品,不能抢单’。
- ‘对不起!您的店铺内商品【”+buyDpspInfo.getSpmc()+”】库存不满足要求,不能抢单’.
- ‘对不起!您的店铺内没有商品,不能抢单’。
@RequestMapping(value="/grabSingle/{infoStr}",method = RequestMethod.GET) @ResponseBody public Message grabSingle(@PathVariable("infoStr")String infoStr,HttpServletRequest request) throws IOException{ String info = java.net.URLDecoder.decode(infoStr, "utf-8"); //获取缓存信息判断抢单是否已被其他商家抢单 if(QdWebSocketUtil.webSocketMap.isEmpty()){ return Message.error("对不起!该订单已失效"); }else{ for(Session key:QdWebSocketUtil.webSocketMap.keySet()){ if(!QdWebSocketUtil.webSocketMap.get(key).equals(info)){ return Message.error("对不起!该订单已被其他商家抢单"); } } } //买家购物车 List<LptDpSp> buyDpspInfos = new ArrayList<LptDpSp>(); String[] allArray = info.split(";");//全部信息 for(String str:allArray){ if(str.split(",").length == 3){ LptDpSp dpsp = new LptDpSp(); LptGwcMx gwcmx = lptGwcService.getGwcmxById(str.split(",")[0]); LptDpSp buyDpspInfo = lptDpSpService.findById(gwcmx.getDpspid()); dpsp.setKcsl(gwcmx.getSl()); dpsp.setSpid(buyDpspInfo.getSpid()); dpsp.setSpmc(buyDpspInfo.getSpmc()); buyDpspInfos.add(dpsp); } } //卖家店铺商品详情 Map<String,Object> map = new HashMap<String,Object>(); map.put("orgid", ShiroUtil.getCurentLoginUser().getOrganizationid()); map.put("spid", "is not null"); List<LptDpSp> sellDpspInfos = lptDpSpService.findList(map); if(!sellDpspInfos.isEmpty()&&null!=sellDpspInfos){ for(LptDpSp sellDpspInfo:sellDpspInfos){ for(LptDpSp buyDpspInfo:buyDpspInfos){ if(sellDpspInfo.getSpid().equals(buyDpspInfo.getSpid())&& sellDpspInfo.getKcsl() >= buyDpspInfo.getKcsl()){ //1、给前台返回message信息,清除webSocketMap对应信息, QdWebSocketUtil.onMessage("successQd"+";"+info, null); //2、添加抢单数据 lptDdSpService.insertQdData(info); //3、清空购物车对应信息 lptGwcService.deleteGwcmxAndGwcmxSpSx(info); }else if(!sellDpspInfo.getSpid().equals(buyDpspInfo.getSpid())){ return Message.error("对不起!您的店铺内没有【"+buyDpspInfo.getSpmc()+"】商品,不能抢单"); }else if(sellDpspInfo.getSpid().equals(buyDpspInfo.getSpid())&& sellDpspInfo.getKcsl() < buyDpspInfo.getKcsl()){ return Message.error("对不起!您的店铺内商品【"+buyDpspInfo.getSpmc()+"】库存不满足要求,不能抢单"); } } } }else{ return Message.error("对不起!您的店铺内没有商品,不能抢单"); } return Message.success("抢单成功!"); }
9. 完整的websocketUtil代码
@ServerEndpoint("/websocket")
public class QdWebSocketUtil {
//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static int onlineCount = 0;
//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
private static CopyOnWriteArraySet<QdWebSocketUtil> webSocketSet = new CopyOnWriteArraySet<QdWebSocketUtil>();
//客户端提交的数据
public static Map<Session,String> webSocketMap = new HashMap<Session,String>();
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
/**
* 连接建立成功调用的方法
* @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
@OnOpen
public void onOpen(Session session){
this.session = session;
webSocketSet.add(this); //加入set中
addOnlineCount(); //在线数加1
System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose(){
webSocketSet.remove(this); //从set中删除
subOnlineCount(); //在线数减1
System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
* @param message 客户端发送过来的消息
* @param session 可选的参数
* @throws IOException
*/
@OnMessage public static void onMessage(String message, Session session) throws IOException { System.out.println("来自客户端的消息session="+session+ message); for(QdWebSocketUtil item: webSocketSet){ if(null !=session){//群发 webSocketMap.put(session, message); for(Session key:webSocketMap.keySet()){ item.sendMessage(webSocketMap.get(key)); } }else if(null ==session && "errorQd".equals(message)){//返回客户端提示信息--抢单失败 for(Session key:webSocketMap.keySet()){ if(item.session.getId().equals(key.getId())){ item.sendMessage("您发布的抢单无商家抢单,请回购物车重新发布抢单!"); } } }else if(null ==session && "successQd".equals(message.split(";")[0].toString())){//返回客户端提示信息--抢单成功 for(Session key:webSocketMap.keySet()){ if(webSocketMap.get(key).contains(message.split(";")[1].toString())){ item.sendMessage("您发布的抢单已有商家抢单,请及时查看!"); webSocketMap.remove(key); } } } } }
/**
* 发生错误时调用
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error){
System.out.println("发生错误");
error.printStackTrace();
}
/**
* 发送信息
* @param message
* @throws IOException
*/
public void sendMessage(String message) throws IOException{
this.session.getBasicRemote().sendText(message);
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
QdWebSocketUtil.onlineCount++;
}
public static synchronized void subOnlineCount() {
QdWebSocketUtil.onlineCount--;
}
}
WebSocket程序部署到服务器上时出错
在Tomcat8部署的项目不要导入catalina.jar和websocket-api.jar这两个包,因为Tomcat8自带有这两个包。自己再导入的话会冲突。IP要写服务器的Ip地址,不要写localhost.
.调试远程服务器的webSocket程序时,要关闭本地的Tomcat服务器。
WebSocket部署到服务器时,出现连接失败的问题解决与分析
相关文章推荐
- 在 SSM 中基于 MyBatis-PageHelper 分页插件的分页功能实现
- 基于JQuery实现的类似购物商城的购物车
- 【SSH网上商城项目实战18】过滤器实现购物登录功能的判断
- SSH网上商城项目实战 过滤器实现购物车购物登陆功能的判断。
- 基于SSM框架web搜索功能的实现
- 基于SSM框架实现数据库的基本功能(一)
- 基于spring websocket实现广播及点对点推送功能
- 基于JQuery实现的类似购物商城的购物车
- 基于javascript实现的购物商城商品倒计时实例
- ionic+cordova基于websocket实现的实时通报提醒功能
- 基于Vue、Vuex、Vue-router实现的购物商城(原生切换动画)效果
- JavaWeb学习记录(十四)——商城购物之字符串拼接实现最近浏览商品和购物车的功能
- 基于SSM与WebSocket的聊天项目(实现白板演示)
- SSH框架网上商城项目第18战之过滤器实现购物登录功能的判断
- 基于SSM使用ueditor编辑框文本、图片上传功能实现
- Spring 学习——基于Spring WebSocket 和STOMP实现简单的聊天功能
- Spring 学习——基于Spring WebSocket 和STOMP实现简单的聊天功能
- 【SSH网上商城项目实战18】过滤器实现购物登录功能的判断
- nodejs基于WS模块实现WebSocket聊天功能的方法
- 【SSH网上商城项目实战18】过滤器实现购物登录功能的判断