|
|
@ -1,4 +1,4 @@ |
|
|
<?php |
|
|
|
|
|
|
|
|
<?php |
|
|
namespace app\controller; |
|
|
namespace app\controller; |
|
|
|
|
|
|
|
|
use app\BaseController; |
|
|
use app\BaseController; |
|
|
@ -6,7 +6,6 @@ use app\model\Message as MessageModel; |
|
|
|
|
|
|
|
|
class Message extends BaseController |
|
|
class Message extends BaseController |
|
|
{ |
|
|
{ |
|
|
// BUG-03: 聊天记录读取保持原有逻辑
|
|
|
|
|
|
public function list() |
|
|
public function list() |
|
|
{ |
|
|
{ |
|
|
$cardNo = $this->request->get('card_no', ''); |
|
|
$cardNo = $this->request->get('card_no', ''); |
|
|
@ -30,15 +29,15 @@ class Message extends BaseController |
|
|
'cardNo' => $m['card_no'], |
|
|
'cardNo' => $m['card_no'], |
|
|
'senderType' => $m['sender_type'], |
|
|
'senderType' => $m['sender_type'], |
|
|
'content' => $m['content'], |
|
|
'content' => $m['content'], |
|
|
'time' => date('H:i', strtotime($m['created_at'])), |
|
|
|
|
|
|
|
|
'time' => date('Y-m-d H:i:s', strtotime($m['created_at'])), |
|
|
'staffId' => $m['staff_id'] ?? null, |
|
|
'staffId' => $m['staff_id'] ?? null, |
|
|
|
|
|
'isRead' => $m['is_read'] ?? 0, |
|
|
]; |
|
|
]; |
|
|
}, $messages); |
|
|
}, $messages); |
|
|
|
|
|
|
|
|
return json(['code' => 0, 'data' => $list, 'msg' => 'ok']); |
|
|
return json(['code' => 0, 'data' => $list, 'msg' => 'ok']); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// BUG-03: 增加输入校验 + XSS防护
|
|
|
|
|
|
public function send() |
|
|
public function send() |
|
|
{ |
|
|
{ |
|
|
$cardNo = $this->request->post('cardNo', ''); |
|
|
$cardNo = $this->request->post('cardNo', ''); |
|
|
@ -46,31 +45,92 @@ class Message extends BaseController |
|
|
$content = $this->request->post('content', ''); |
|
|
$content = $this->request->post('content', ''); |
|
|
$staffId = $this->request->post('staffId', null); |
|
|
$staffId = $this->request->post('staffId', null); |
|
|
|
|
|
|
|
|
// 必填校验
|
|
|
|
|
|
if (empty($cardNo) || empty($content)) { |
|
|
if (empty($cardNo) || empty($content)) { |
|
|
return json(['code' => -1, 'data' => null, 'msg' => '参数不完整']); |
|
|
return json(['code' => -1, 'data' => null, 'msg' => '参数不完整']); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// senderType 枚举校验
|
|
|
|
|
|
if (!in_array($senderType, ['customer', 'staff', 'system'])) { |
|
|
if (!in_array($senderType, ['customer', 'staff', 'system'])) { |
|
|
return json(['code' => -1, 'data' => null, 'msg' => '发送者类型无效']); |
|
|
return json(['code' => -1, 'data' => null, 'msg' => '发送者类型无效']); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 内容长度校验 (DB VARCHAR 500)
|
|
|
|
|
|
if (mb_strlen($content) > 500) { |
|
|
if (mb_strlen($content) > 500) { |
|
|
return json(['code' => -1, 'data' => null, 'msg' => '消息过长,最多500字']); |
|
|
return json(['code' => -1, 'data' => null, 'msg' => '消息过长,最多500字']); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// XSS 防护 — HTML 转义
|
|
|
|
|
|
$content = htmlspecialchars($content, ENT_QUOTES, 'UTF-8'); |
|
|
|
|
|
|
|
|
// 拒单禁聊+结单禁聊:订单已取消(status=3)或已完成(status=2)禁止发送
|
|
|
|
|
|
$order = \app\model\Order::where('card_no', $cardNo)->order('id', 'desc')->find(); |
|
|
|
|
|
if ($order && (intval($order->status) === 3 || intval($order->status) === 2)) { |
|
|
|
|
|
$msg = $order->status === 3 ? '订单已取消,会话已结束' : '订单已完成,会话已结束'; |
|
|
|
|
|
return json(['code' => -1, 'data' => null, 'msg' => $msg]); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 存储原始文本,Vue 模板 {{ }} 自动转义防 XSS
|
|
|
$msg = MessageModel::create([ |
|
|
$msg = MessageModel::create([ |
|
|
'card_no' => $cardNo, |
|
|
'card_no' => $cardNo, |
|
|
'sender_type' => $senderType, |
|
|
'sender_type' => $senderType, |
|
|
'staff_id' => $staffId, |
|
|
'staff_id' => $staffId, |
|
|
'content' => $content, |
|
|
'content' => $content, |
|
|
|
|
|
'is_read' => 0, // 默认未读
|
|
|
]); |
|
|
]); |
|
|
|
|
|
|
|
|
return json(['code' => 0, 'data' => ['id' => $msg->id], 'msg' => 'ok']); |
|
|
return json(['code' => 0, 'data' => ['id' => $msg->id], 'msg' => 'ok']); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 标记消息为已读
|
|
|
|
|
|
public function read() |
|
|
|
|
|
{ |
|
|
|
|
|
$cardNo = $this->request->post('card_no', ''); |
|
|
|
|
|
$staffId = $this->request->post('staff_id', null); |
|
|
|
|
|
|
|
|
|
|
|
if (empty($cardNo)) { |
|
|
|
|
|
return json(['code' => -1, 'data' => null, 'msg' => '缺少号码牌']); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 顾客端标记员工消息为已读
|
|
|
|
|
|
if ($staffId === null) { |
|
|
|
|
|
MessageModel::where('card_no', $cardNo) |
|
|
|
|
|
->where('sender_type', 'staff') |
|
|
|
|
|
->where('is_read', 0) |
|
|
|
|
|
->update(['is_read' => 1]); |
|
|
|
|
|
} else { |
|
|
|
|
|
// 员工端标记顾客消息为已读
|
|
|
|
|
|
MessageModel::where('card_no', $cardNo) |
|
|
|
|
|
->where('sender_type', 'customer') |
|
|
|
|
|
->where('is_read', 0) |
|
|
|
|
|
->update(['is_read' => 1]); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return json(['code' => 0, 'data' => null, 'msg' => 'ok']); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// GET api/message/unread?card_no=XXX 或 ?card_no=all&staff_id=1
|
|
|
|
|
|
// 顾客端:传 card_no,查 staff 发送的未读消息数
|
|
|
|
|
|
// 员工端:传 card_no=all + staff_id,统计所有顾客发来的未读消息数
|
|
|
|
|
|
public function unread() |
|
|
|
|
|
{ |
|
|
|
|
|
$cardNo = $this->request->get('card_no', ''); |
|
|
|
|
|
$staffId = $this->request->get('staff_id', null); |
|
|
|
|
|
|
|
|
|
|
|
if (empty($cardNo)) { |
|
|
|
|
|
return json(['code' => -1, 'data' => null, 'msg' => '缺少号码牌']); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
$query = MessageModel::where('is_read', 0); |
|
|
|
|
|
|
|
|
|
|
|
if ($cardNo === 'all' && $staffId !== null) { |
|
|
|
|
|
// 员工端:统计所有对话中顾客发来的未读
|
|
|
|
|
|
$query->where('sender_type', 'customer'); |
|
|
|
|
|
} elseif ($staffId !== null) { |
|
|
|
|
|
// 员工端指定号码牌
|
|
|
|
|
|
$query->where('card_no', $cardNo)->where('sender_type', 'customer'); |
|
|
|
|
|
} else { |
|
|
|
|
|
// 顾客端:统计该号码牌员工发来的未读
|
|
|
|
|
|
$query->where('card_no', $cardNo)->where('sender_type', 'staff'); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
$count = $query->count(); |
|
|
|
|
|
return json(['code' => 0, 'data' => ['count' => $count], 'msg' => 'ok']); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|