一、設(shè)計(jì)思路
直播上課的時(shí)候如何對(duì)學(xué)生進(jìn)行考勤是個(gè)問題,本文提供了一個(gè)方法僅供參考,就是在直播過程中不定期進(jìn)行點(diǎn)名,點(diǎn)到的學(xué)生必須在30秒(可自定義)內(nèi)進(jìn)行回復(fù),否則視為缺席。
點(diǎn)名使用websocket來發(fā)送和接收消息。
二、搭建websocket服務(wù)器
使用workerman作為websocket服務(wù)端。
Linux下啟動(dòng)workerman
啟動(dòng) php start.php start -d
停止 php start.php stop
查看狀態(tài) php start.php status
Windows下啟動(dòng)workerman
首先需要把PHP添加到系統(tǒng)環(huán)境變量里面。
啟動(dòng) 右鍵點(diǎn)擊start_for_win.bat,選擇【以管理員身份運(yùn)行】
停止 直接關(guān)閉進(jìn)程
添加websocket事件
修改Applications\Chat\Events.php
添加以下事件
// 直播點(diǎn)名
case 'dianming':
// 非法請(qǐng)求
if(!isset($_SESSION['room_id']))
{
throw new \Exception("\$_SESSION['room_id'] not set. client_ip:{$_SERVER['REMOTE_ADDR']}");
}
$room_id = $_SESSION['room_id'];
$client_name = $_SESSION['client_name'];
$new_message = array(
'type'=>'dianming',
'from_client_id'=>$client_id,
'from_client_name' =>$client_name,
'to_client_id'=>'all',
'content'=>nl2br(htmlspecialchars($message_data['content'])),
'time'=>date('Y-m-d H:i:s')
);
return Gateway::sendToGroup($room_id ,json_encode($new_message));
// 直播點(diǎn)名學(xué)員回應(yīng)
case 'dianming_reply':
// 非法請(qǐng)求
if(!isset($_SESSION['room_id']))
{
throw new \Exception("\$_SESSION['room_id'] not set. client_ip:{$_SERVER['REMOTE_ADDR']}");
}
$room_id = $_SESSION['room_id'];
$client_name = $_SESSION['client_name'];
$new_message = array(
'type'=>'dianming_reply',
'from_client_id'=>$client_id,
'from_client_name' =>$client_name,
'to_client_id'=>'all',
'content'=>nl2br(htmlspecialchars($message_data['content'])),
'time'=>date('Y-m-d H:i:s')
);
return Gateway::sendToGroup($room_id ,json_encode($new_message));
然后重啟workerman
三、控制端程序設(shè)計(jì)
效果如下圖所示

點(diǎn)擊【點(diǎn)名】按鈕后通過websocket發(fā)送一條消息,消息內(nèi)容包含被點(diǎn)名的學(xué)員ID或者其他標(biāo)識(shí),主要JS代碼如下
//批量點(diǎn)名
function dianmingsome(){
var checkednum = $("#list_form").find("input[name='memberid[]']:checked").length;
if(checkednum==0){
layer.msg("您還沒有選擇", {icon: 2});
return false;
}
layer.load(0, {shade: [0.2, '#393D49']});
var useridstr = ",";
$("#list_form").find("input[name='memberid[]']:checked").each(function(index, elem){
useridstr += elem.value +",";
});
var to_client_id = "all";
var to_client_name = "所有人";
ws.send('{"type":"dianming","to_client_id":"'+to_client_id+'","to_client_name":"'+to_client_name+'","content":"'+useridstr+'"}');
layer.msg("點(diǎn)名消息已發(fā)送,請(qǐng)等待學(xué)員回應(yīng)...", {icon: 1});
layer.closeAll('loading');
return false;
}
四、被點(diǎn)名端程序設(shè)計(jì)
效果如下圖所示

被點(diǎn)名的學(xué)員收到消息后自動(dòng)彈出提示框,并開始倒計(jì)時(shí)。
倒計(jì)時(shí)結(jié)束后提示框消失,無法再進(jìn)行回應(yīng)。
主要JS代碼如下
//彈出層索引
var index11, index12;
//點(diǎn)名倒計(jì)時(shí)
var reply_timer, reply_timerb;
var leftseconds = 30;
//被點(diǎn)名時(shí)
function dianming(from_client_id, from_client_name, content, time){
if(content===undefined){
return false;
}
if(content.indexOf(","+ client_userid +",")>=0){
leftseconds = 30;
clearInterval(reply_timer);
clearInterval(reply_timerb);
index11 = layer.confirm('你被點(diǎn)名了,請(qǐng)?jiān)?lt;span class="layui-badge">'+ leftseconds +'</span>秒內(nèi)回應(yīng),否則視為缺席。',
{title:'點(diǎn)名時(shí)間', btn: ['確定回應(yīng)','取消']},
function(){
clearInterval(reply_timer);
clearInterval(reply_timerb);
dianming_reply();
dianming_close();
},
function(){
clearInterval(reply_timer);
clearInterval(reply_timerb);
dianming_close();
}
);
reply_timer = setTimeout(function(){
dianming_close();
}, 30000);
reply_timerb = setInterval(function(){
leftseconds--;
if(leftseconds<=0){
clearInterval(reply_timerb);
dianming_close();
}
$(".layui-layer-content").html('你被點(diǎn)名了,請(qǐng)?jiān)?lt;span class="layui-badge">'+ leftseconds +'</span>秒內(nèi)回應(yīng),否則視為缺席。');
}, 1000);
}
}
//回應(yīng)點(diǎn)名
function dianming_reply(){
var to_client_id = "all";
var to_client_name = "所有人";
$.ajax({
url: '/zhibod/dianming/replyok',
type: 'POST',
dataType: "json",
data: {"zhiboid":zhiboid},
beforeSend: function(){
layer.load(0, {shade: [0.2, '#393D49']});
},
success: function(result){
if(result.code=="success"){
ws.send('{"type":"dianming_reply","to_client_id":"'+to_client_id+'","to_client_name":"'+to_client_name+'","content":"'+client_userid+'"}');
layer.msg("回應(yīng)成功", {"icon":1});
}else{
layer.msg(result.msg, {"icon":2});
}
},
error: function(){
layer.msg("系統(tǒng)錯(cuò)誤,請(qǐng)聯(lián)系管理員", {"icon":2});
},
complete: function(){
layer.closeAll('loading');
}
});
}
//關(guān)閉點(diǎn)名
function dianming_close(){
layer.close(index11);
}
回應(yīng)成功后控制端的點(diǎn)名狀態(tài)會(huì)自動(dòng)更新。