Files
Pro/application/admin/controller/Chat.php
T
2026-02-25 01:50:31 +08:00

464 lines
15 KiB
PHP
Executable File

<?php
namespace app\admin\controller;
use think\Db;
use think\Request;
use think\Session;
/**
* 客服管理控制器
* Class Chat
* @package app\admin\controller
*/
class Chat extends Common
{
/**
* 客服工作台
*/
public function index()
{
$user_info = Session::get('user_info');
$admin_id = $user_info['id'];
// 从数据库获取最新的login_token(避免session中没有的情况)
$admin = Db::name('admin')->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()]);
}
}
}