where('id', $admin_id)->field('login_token')->find(); $login_token = $admin['login_token'] ?? ''; // 如果没有login_token,生成一个新的 if (empty($login_token)) { $login_token = md5($admin_id . time() . uniqid()); Db::name('admin')->where('id', $admin_id)->update(['login_token' => $login_token]); } // 获取客服配置 $admin_status = Db::name('chat_admin_status')->where('admin_id', $admin_id)->find(); if (!$admin_status) { // 创建默认配置 Db::name('chat_admin_status')->insert([ 'admin_id' => $admin_id, 'max_sessions' => 10, 'is_enabled' => 1, 'create_time' => time(), 'update_time' => time(), ]); $admin_status = ['max_sessions' => 10, 'is_enabled' => 1]; } $this->assign('admin_status', $admin_status); $this->assign('admin_id', $admin_id); $this->assign('login_token', $login_token); return $this->fetch(); } /** * 获取会话列表 (AJAX) */ public function sessions() { $user_info = Session::get('user_info'); $admin_id = $user_info['id']; $status = Request::instance()->get('status', 1); // 默认进行中 $where = ['admin_id' => $admin_id]; if ($status !== 'all') { $where['status'] = (int)$status; } $sessions = Db::name('chat_session') ->where($where) ->order('last_msg_time desc, create_time desc') ->select(); foreach ($sessions as &$session) { // 获取用户信息 $user = Db::name('user')->where('id', $session['user_id'])->find(); $session['user_info'] = [ 'username' => $user['username'] ?? '', 'nickname' => $user['nickname'] ?? $user['username'] ?? '', 'money' => $user['money'] ?? 0, ]; // 未读消息数 $session['unread_count'] = Db::name('chat_message') ->where('session_id', $session['id']) ->where('sender_type', 1) // 用户发送 ->where('status', '<', 3) // 未读 ->count(); // 格式化时间 $session['create_time_fmt'] = date('Y-m-d H:i:s', $session['create_time']); $session['last_msg_time_fmt'] = $session['last_msg_time'] ? date('H:i', $session['last_msg_time']) : ''; // 来源 $sourceMap = [1 => 'PC', 2 => 'Game', 3 => 'Portal']; $session['source_name'] = $sourceMap[$session['source']] ?? 'Unknown'; } return json(['code' => 0, 'data' => $sessions]); } /** * 获取待分配会话列表 (AJAX) */ public function pending() { $sessions = Db::name('chat_session') ->where('status', 0) // 待分配 ->order('create_time asc') ->select(); foreach ($sessions as &$session) { $user = Db::name('user')->where('id', $session['user_id'])->find(); $session['user_info'] = [ 'username' => $user['username'] ?? '', 'nickname' => $user['nickname'] ?? $user['username'] ?? '', 'money' => $user['money'] ?? 0, ]; $session['create_time_fmt'] = date('Y-m-d H:i:s', $session['create_time']); $sourceMap = [1 => 'PC', 2 => 'Game', 3 => 'Portal']; $session['source_name'] = $sourceMap[$session['source']] ?? 'Unknown'; } return json(['code' => 0, 'data' => $sessions]); } /** * 获取消息历史 (AJAX) */ public function messages() { $session_id = Request::instance()->get('session_id'); $last_id = Request::instance()->get('last_id', 0); $limit = Request::instance()->get('limit', 50); if (!$session_id) { return json(['code' => 1, 'msg' => '参数错误']); } $query = Db::name('chat_message')->where('session_id', $session_id); if ($last_id > 0) { $query->where('id', '<', $last_id); } $messages = $query->order('id desc')->limit($limit)->select(); $messages = array_reverse($messages); // 按时间正序 foreach ($messages as &$msg) { $msg['create_time_fmt'] = date('H:i:s', $msg['create_time']); } return json(['code' => 0, 'data' => $messages]); } /** * 结束会话 (AJAX) */ public function endSession() { $session_id = Request::instance()->post('session_id'); $user_info = Session::get('user_info'); if (!$session_id) { return json(['code' => 1, 'msg' => '参数错误']); } $session = Db::name('chat_session')->where('id', $session_id)->find(); if (!$session || $session['admin_id'] != $user_info['id']) { return json(['code' => 1, 'msg' => '无权操作']); } Db::name('chat_session')->where('id', $session_id)->update([ 'status' => 2, 'end_time' => time(), 'update_time' => time(), ]); insertAdminLog('结束客服会话', '会话ID: ' . $session_id); return json(['code' => 0, 'msg' => '操作成功']); } /** * 手动接入会话 (AJAX) */ public function acceptSession() { $session_id = Request::instance()->post('session_id'); $user_info = Session::get('user_info'); $admin_id = $user_info['id']; if (!$session_id) { return json(['code' => 1, 'msg' => '参数错误']); } // 检查会话是否存在且状态为待分配 $session = Db::name('chat_session')->where('id', $session_id)->find(); if (!$session) { return json(['code' => 1, 'msg' => '会话不存在']); } if ($session['status'] != 0) { return json(['code' => 1, 'msg' => '该会话已被接入']); } // 检查客服当前会话数是否已满 $admin_status = Db::name('chat_admin_status')->where('admin_id', $admin_id)->find(); $max_sessions = $admin_status['max_sessions'] ?? 10; $current_sessions = Db::name('chat_session') ->where('admin_id', $admin_id) ->where('status', 1) ->count(); if ($current_sessions >= $max_sessions) { return json(['code' => 1, 'msg' => '已达最大会话数限制(' . $max_sessions . ')']); } // 接入会话 $result = Db::name('chat_session')->where('id', $session_id)->where('status', 0)->update([ 'admin_id' => $admin_id, 'status' => 1, 'update_time' => time(), ]); if ($result) { insertAdminLog('接入客服会话', '会话ID: ' . $session_id); return json(['code' => 0, 'msg' => '接入成功']); } else { return json(['code' => 1, 'msg' => '接入失败,可能已被其他客服接入']); } } /** * 转接会话 (AJAX) */ public function transfer() { $session_id = Request::instance()->post('session_id'); $target_admin_id = Request::instance()->post('target_admin_id'); $user_info = Session::get('user_info'); if (!$session_id || !$target_admin_id) { return json(['code' => 1, 'msg' => '参数错误']); } $session = Db::name('chat_session')->where('id', $session_id)->find(); if (!$session || $session['admin_id'] != $user_info['id']) { return json(['code' => 1, 'msg' => '无权操作']); } Db::name('chat_session')->where('id', $session_id)->update([ 'admin_id' => $target_admin_id, 'update_time' => time(), ]); insertAdminLog('转接客服会话', '会话ID: ' . $session_id . ', 转接给: ' . $target_admin_id); return json(['code' => 0, 'msg' => '转接成功']); } /** * 获取在线客服列表 (AJAX) */ public function onlineAgents() { $user_info = Session::get('user_info'); // 获取启用客服功能的管理员 $agents = Db::name('chat_admin_status') ->alias('s') ->join('admin a', 'a.id = s.admin_id') ->where('s.is_enabled', 1) ->where('a.id', '<>', $user_info['id']) ->field('a.id, a.admin as username, a.admin as nickname, s.max_sessions') ->select(); return json(['code' => 0, 'data' => $agents]); } /** * 聊天记录查询 */ public function record() { $get = Request::instance()->get(); $this->assign('get', $get); $username = Request::instance()->get('username'); $admin_id = Request::instance()->get('admin_id'); $startDate = Request::instance()->get('startDate'); $endDate = Request::instance()->get('endDate'); $export = Request::instance()->get('export'); $where = []; $startTime = 0; $endTime = time(); if ($startDate) $startTime = strtotime($startDate); if ($endDate) $endTime = strtotime($endDate) + 86400; $where['s.create_time'] = ['between', [$startTime, $endTime]]; if ($admin_id) { $where['s.admin_id'] = $admin_id; } $query = Db::name('chat_session') ->alias('s') ->join('user u', 'u.id = s.user_id', 'left') ->join('admin a', 'a.id = s.admin_id', 'left') ->where($where); if ($username) { $query->where('u.username', 'like', "%{$username}%"); } if ($export == 1) { $list = $query->field('s.*, u.username, u.nickname as user_nickname, a.admin as admin_username') ->order('s.create_time desc') ->select(); $excelData = []; foreach ($list as $k => $v) { $excelData[$k][0] = $v['id']; $excelData[$k][1] = $v['username']; $excelData[$k][2] = $v['admin_username'] ?? '未分配'; $excelData[$k][3] = ['PC', 'Game', 'Portal'][$v['source'] - 1] ?? ''; $excelData[$k][4] = ['待分配', '进行中', '已结束'][$v['status']] ?? ''; $excelData[$k][5] = $v['rating'] ?? '-'; $excelData[$k][6] = date('Y-m-d H:i:s', $v['create_time']); $excelData[$k][7] = $v['end_time'] ? date('Y-m-d H:i:s', $v['end_time']) : '-'; } $title = ['会话ID', '用户名', '客服', '来源', '状态', '评分', '开始时间', '结束时间']; $this->exportExcelCore($excelData, '聊天记录-' . date('Ymd'), $title); exit; } $list = $query->field('s.*, u.username, u.nickname as user_nickname, a.admin as admin_username') ->order('s.create_time desc') ->paginate(15, false, ['query' => $get]); // 获取客服列表 $admins = Db::name('admin')->where('status', 1)->field('id, admin as username, admin as nickname')->select(); $this->assign('list', $list); $this->assign('admins', $admins); return $this->fetch(); } /** * 查看会话详情 */ public function detail() { $session_id = Request::instance()->get('session_id'); $session = Db::name('chat_session') ->alias('s') ->join('user u', 'u.id = s.user_id', 'left') ->join('admin a', 'a.id = s.admin_id', 'left') ->where('s.id', $session_id) ->field('s.*, u.username, u.nickname as user_nickname, u.money, a.admin as admin_username') ->find(); if (!$session) { $this->error('会话不存在'); } $messages = Db::name('chat_message') ->where('session_id', $session_id) ->order('id asc') ->select(); $this->assign('session', $session); $this->assign('messages', $messages); return $this->fetch(); } /** * 客服统计 */ public function stats() { $user_info = Session::get('user_info'); $admin_id = Request::instance()->get('admin_id', $user_info['id']); $startDate = Request::instance()->get('startDate', date('Y-m-d', strtotime('-7 days'))); $endDate = Request::instance()->get('endDate', date('Y-m-d')); $startTime = strtotime($startDate); $endTime = strtotime($endDate) + 86400; $where = [ 'admin_id' => $admin_id, 'create_time' => ['between', [$startTime, $endTime]], ]; // 统计数据 $stats = [ 'total_sessions' => Db::name('chat_session')->where($where)->count(), 'ended_sessions' => Db::name('chat_session')->where($where)->where('status', 2)->count(), 'avg_rating' => Db::name('chat_session')->where($where)->whereNotNull('rating')->avg('rating') ?: 0, 'total_messages' => Db::name('chat_message') ->alias('m') ->join('chat_session s', 's.id = m.session_id') ->where('s.admin_id', $admin_id) ->where('m.create_time', 'between', [$startTime, $endTime]) ->count(), ]; $this->assign('stats', $stats); $this->assign('startDate', $startDate); $this->assign('endDate', $endDate); return $this->fetch(); } /** * 获取快捷回复列表 (AJAX) */ public function quickReplies() { $category = Request::instance()->get('category'); $query = Db::name('chat_quick_reply')->where('status', 1); if ($category) { $query->where('category', $category); } $list = $query->order('sort asc, id asc')->select(); $categories = Db::name('chat_quick_reply') ->where('status', 1) ->whereNotNull('category') ->where('category', '<>', '') ->group('category') ->column('category'); return json(['code' => 0, 'data' => $list, 'categories' => $categories]); } /** * 图片上传 */ public function upload() { $file = Request::instance()->file('image'); if (!$file) { return json(['code' => 1, 'msg' => '请选择文件']); } // 验证文件 $info = $file->validate([ 'size' => 2097152, // 2MB 'ext' => 'jpg,png,gif,jpeg' ])->move(ROOT_PATH . 'public/uploads/chat/' . date('Ymd')); if ($info) { $url = '/uploads/chat/' . date('Ymd') . '/' . $info->getSaveName(); return json(['code' => 0, 'url' => $url]); } else { return json(['code' => 1, 'msg' => $file->getError()]); } } }