作者 何书鹏

新订单提醒

@@ -24,6 +24,15 @@ @@ -24,6 +24,15 @@
24 <div class="navbar-custom-menu"> 24 <div class="navbar-custom-menu">
25 <ul class="nav navbar-nav"> 25 <ul class="nav navbar-nav">
26 26
  27 + <!--新订单提醒-->
  28 + <li class="hidden-xs orders" data-uid="{$Think.session.admin.id}">
  29 + <a href="#" >
  30 + <i class="fa" style="font-size:14px;"></i>
  31 + 新订单
  32 + <span class="pull-right-container"> <small class="badge pull-right bg-red" id="order_nums">0</small></span>
  33 + </a>
  34 + </li>
  35 +
27 <li class="hidden-xs"> 36 <li class="hidden-xs">
28 <a href="__PUBLIC__" target="_blank"><i class="fa fa-home" style="font-size:14px;"></i> {:__('Home')}</a> 37 <a href="__PUBLIC__" target="_blank"><i class="fa fa-home" style="font-size:14px;"></i> {:__('Home')}</a>
29 </li> 38 </li>
@@ -17,7 +17,7 @@ use think\Validate; @@ -17,7 +17,7 @@ use think\Validate;
17 */ 17 */
18 class User extends Api 18 class User extends Api
19 { 19 {
20 - protected $noNeedLogin = ['third','joinUs','developLogin']; 20 + protected $noNeedLogin = ['third','joinUs','developLogin','ceshi'];
21 protected $noNeedRight = '*'; 21 protected $noNeedRight = '*';
22 22
23 public function _initialize() 23 public function _initialize()
@@ -815,4 +815,25 @@ class User extends Api @@ -815,4 +815,25 @@ class User extends Api
815 $this->error($this->auth->getError()); 815 $this->error($this->auth->getError());
816 } 816 }
817 } 817 }
  818 +
  819 + /**
  820 + * 测试
  821 + */
  822 + public function ceshi(){
  823 + $push_api_url = request()->domain().":2121/";
  824 + $post_data = array(
  825 + 'type' => 'publish',
  826 + 'content' => '这个是推送的测试数据',
  827 + 'to' => '',
  828 + );
  829 + $ch = curl_init ();
  830 + curl_setopt ( $ch, CURLOPT_URL, $push_api_url );
  831 + curl_setopt ( $ch, CURLOPT_POST, 1 );
  832 + curl_setopt ( $ch, CURLOPT_HEADER, 0 );
  833 + curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, 1 );
  834 + curl_setopt ( $ch, CURLOPT_POSTFIELDS, $post_data );
  835 + curl_setopt ( $ch, CURLOPT_HTTPHEADER, array("Expect:"));
  836 + $return = curl_exec ( $ch );
  837 + curl_close ( $ch );
  838 + }
818 } 839 }
  1 +<?php
  2 +namespace app\socketio\controller;
  3 +
  4 +use Workerman\Worker;
  5 +use Workerman\Timer;
  6 +use PHPSocketIO\SocketIO;
  7 +use Workerman\Protocols\Http\Request;
  8 +use Workerman\Connection\TcpConnection;
  9 +
  10 +class Server
  11 +{
  12 + // 全局数组保存uid在线数据
  13 + private $uidConnectionMap = array();
  14 + // 记录最后一次广播的在线用户数
  15 + private $last_online_count = 0;
  16 + // 记录最后一次广播的在线页面数
  17 + private $last_online_page_count = 0;
  18 +
  19 + public function index(){
  20 + header("Access-Control-Allow-Origin: *");
  21 + // PHPSocketIO服务
  22 + $sender_io = new SocketIO(2120);
  23 + // 客户端发起连接事件时,设置连接socket的各种事件回调
  24 + $sender_io->on('connection', function($socket){
  25 + // 当客户端发来登录事件时触发
  26 + $socket->on('login', function ($uid)use($socket){
  27 + // global $uidConnectionMap, $last_online_count, $last_online_page_count;
  28 + // 已经登录过了
  29 + if(isset($socket->uid)){
  30 + return;
  31 + }
  32 + // 更新对应uid的在线数据
  33 + $uid = (string)$uid;
  34 + if(!isset($this->uidConnectionMap[$uid]))
  35 + {
  36 + $this->uidConnectionMap[$uid] = 0;
  37 + }
  38 + // 这个uid有++$uidConnectionMap[$uid]个socket连接
  39 + ++$this->uidConnectionMap[$uid];
  40 + // 将这个连接加入到uid分组,方便针对uid推送数据
  41 + $socket->join($uid);
  42 + $socket->uid = $uid;
  43 + // 更新这个socket对应页面的在线数据
  44 + $socket->emit('update_online_count', "当前<b>{$this->last_online_count}</b>人在线,共打开<b>{$this->last_online_page_count}</b>个页面");
  45 + });
  46 +
  47 + // 当客户端断开连接是触发(一般是关闭网页或者跳转刷新导致)
  48 + $socket->on('disconnect', function () use($socket) {
  49 + if(!isset($socket->uid))
  50 + {
  51 + return;
  52 + }
  53 + // global $uidConnectionMap, $sender_io;
  54 + // 将uid的在线socket数减一
  55 + if(--$this->uidConnectionMap[$socket->uid] <= 0)
  56 + {
  57 + unset($this->uidConnectionMap[$socket->uid]);
  58 + }
  59 + });
  60 + });
  61 +
  62 + // 当$sender_io启动后监听一个http端口,通过这个端口可以给任意uid或者所有uid推送数据
  63 + $sender_io->on('workerStart', function() use ($sender_io) {
  64 + // 监听一个http端口
  65 + $inner_http_worker = new Worker('http://0.0.0.0:2121');
  66 + // 当http客户端发来数据时触发
  67 + $inner_http_worker->onMessage = function(TcpConnection $http_connection, Request $request) use ($sender_io) {
  68 + // global $uidConnectionMap;
  69 + $post = $request->post();
  70 + $post = $post ? $post : $request->get();
  71 + // 推送数据的url格式 type=publish&to=uid&content=xxxx
  72 + switch(@$post['type']){
  73 + case 'publish':
  74 + // global $sender_io;
  75 + $to = @$post['to'];
  76 + $post['content'] = htmlspecialchars(@$post['content']);
  77 + // 有指定uid则向uid所在socket组发送数据
  78 + if($to){
  79 + $sender_io->to($to)->emit('new_msg', $post['content']);
  80 + // 否则向所有uid推送数据
  81 + }else{
  82 + $sender_io->emit('new_msg', @$post['content']);
  83 + }
  84 + // http接口返回,如果用户离线socket返回fail
  85 + if($to && !isset($this->uidConnectionMap[$to])){
  86 + return $http_connection->send('offline');
  87 + }else{
  88 + return $http_connection->send('ok');
  89 + }
  90 + }
  91 + return $http_connection->send('fail');
  92 + };
  93 + // 执行监听
  94 + $inner_http_worker->listen();
  95 +
  96 + // 一个定时器,定时向所有uid推送当前uid在线数及在线页面数
  97 + Timer::add(1, function() use ($sender_io) {
  98 + // global $uidConnectionMap, $sender_io, $last_online_count, $last_online_page_count;
  99 + $online_count_now = count($this->uidConnectionMap);
  100 + $online_page_count_now = array_sum($this->uidConnectionMap);
  101 + // 只有在客户端在线数变化了才广播,减少不必要的客户端通讯
  102 + if($this->last_online_count != $online_count_now || $this->last_online_page_count != $online_page_count_now)
  103 + {
  104 + $sender_io->emit('update_online_count', "当前<b>{$online_count_now}</b>人在线,共打开<b>{$online_page_count_now}</b>个页面");
  105 + $this->last_online_count = $online_count_now;
  106 + $this->last_online_page_count = $online_page_count_now;
  107 + }
  108 + });
  109 + });
  110 +
  111 + if(!defined('GLOBAL_START'))
  112 + {
  113 + Worker::runAll();
  114 + }
  115 + }
  116 +}
1 -define(['jquery', 'bootstrap', 'backend', 'addtabs', 'adminlte', 'form'], function ($, undefined, Backend, undefined, AdminLTE, Form) { 1 +define(['jquery', 'bootstrap', 'backend', 'addtabs', 'adminlte', 'form','https://cdn.bootcss.com/socket.io/2.1.1/socket.io.js'], function ($, undefined, Backend, undefined, AdminLTE, Form,io) {
2 var Controller = { 2 var Controller = {
3 index: function () { 3 index: function () {
4 //双击重新加载页面 4 //双击重新加载页面
@@ -359,6 +359,26 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'adminlte', 'form'], functi @@ -359,6 +359,26 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'adminlte', 'form'], functi
359 359
360 $(window).resize(); 360 $(window).resize();
361 361
  362 + // 新订单提醒
  363 + $('.orders').click(function(){
  364 + $('#order_nums').html(0);
  365 + Backend.api.addtabs('litestore/litestoreorder');
  366 + })
  367 + $(document).ready(function () {
  368 + var uid=$('.orders').attr('data-uid');
  369 + // 连接服务端
  370 + var socket = io('http://'+document.domain+':2120');
  371 + // 连接后登录
  372 + socket.on('connect', function(){
  373 + socket.emit('login', uid);
  374 + });
  375 + // 后端推送来消息时
  376 + socket.on('new_msg', function(msg){
  377 + Toastr.success(msg);
  378 + $('#order_nums').html($('#order_nums').html()*1+1);
  379 + });
  380 + });
  381 +
362 }, 382 },
363 login: function () { 383 login: function () {
364 var lastlogin = localStorage.getItem("lastlogin"); 384 var lastlogin = localStorage.getItem("lastlogin");
  1 +<?php
  2 +define('APP_PATH', __DIR__ . '/../application/');
  3 +
  4 +define('BIND_MODULE','socketio/Server/index');
  5 +
  6 +// 加载框架引导文件
  7 +require __DIR__ . '/../thinkphp/start.php';