add
This commit is contained in:
@@ -0,0 +1,326 @@
|
||||
<?php
|
||||
|
||||
namespace app\services\chat;
|
||||
|
||||
use app\models\chat\ChatSession;
|
||||
use app\models\chat\ChatMessage;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Db;
|
||||
|
||||
/**
|
||||
* 会话服务
|
||||
* Class SessionService
|
||||
* @package app\services\chat
|
||||
*/
|
||||
class SessionService
|
||||
{
|
||||
// Redis Key 前缀
|
||||
private const USER_ACTIVE_SESSION_PREFIX = 'cs:user:active_session:';
|
||||
private const CONN_USER_PREFIX = 'cs:conn:user:';
|
||||
private const CONN_AGENT_PREFIX = 'cs:conn:agent:';
|
||||
|
||||
private AssignService $assignService;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->assignService = new AssignService();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建会话
|
||||
* @param int $userId 用户ID
|
||||
* @param int $source 来源 1=PC 2=Game 3=Portal
|
||||
* @return array 会话数据
|
||||
*/
|
||||
public function createSession(int $userId, int $source): array
|
||||
{
|
||||
$redis = $this->getRedis();
|
||||
|
||||
// 检查是否已有活跃会话
|
||||
$existingSession = ChatSession::getActiveByUserId($userId);
|
||||
if ($existingSession) {
|
||||
return $existingSession;
|
||||
}
|
||||
|
||||
// 创建新会话
|
||||
$session = [
|
||||
'user_id' => $userId,
|
||||
'source' => $source,
|
||||
'status' => ChatSession::STATUS_PENDING,
|
||||
'create_time' => time(),
|
||||
'update_time' => time(),
|
||||
];
|
||||
|
||||
$sessionId = ChatSession::insertGetId($session);
|
||||
$session['id'] = $sessionId;
|
||||
|
||||
// 缓存用户活跃会话
|
||||
$redis->set(self::USER_ACTIVE_SESSION_PREFIX . $userId, $sessionId);
|
||||
|
||||
// 尝试分配客服
|
||||
$adminId = $this->assignService->assignSession($userId, $sessionId);
|
||||
if ($adminId !== null) {
|
||||
$session['admin_id'] = $adminId;
|
||||
$session['status'] = ChatSession::STATUS_ACTIVE;
|
||||
}
|
||||
|
||||
return $session;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户活跃会话
|
||||
*/
|
||||
public function getActiveSession(int $userId): ?array
|
||||
{
|
||||
return ChatSession::getActiveByUserId($userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会话详情(含用户信息)
|
||||
*/
|
||||
public function getSessionDetail(int $sessionId): ?array
|
||||
{
|
||||
$session = ChatSession::find($sessionId);
|
||||
if (!$session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$session = $session->toArray();
|
||||
|
||||
// 获取用户信息
|
||||
$user = Db::name('user')->where('id', $session['user_id'])->find();
|
||||
if ($user) {
|
||||
$session['user_info'] = [
|
||||
'id' => $user['id'],
|
||||
'username' => $user['username'],
|
||||
'nickname' => $user['nickname'] ?? $user['username'],
|
||||
'money' => $user['money'],
|
||||
'agent_id' => $user['agent_id'] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
return $session;
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束会话
|
||||
*/
|
||||
public function endSession(int $sessionId, int $operatorId): bool
|
||||
{
|
||||
$session = ChatSession::find($sessionId);
|
||||
if (!$session) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$session = $session->toArray();
|
||||
|
||||
// 只能结束进行中的会话
|
||||
if ($session['status'] === ChatSession::STATUS_ENDED) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 更新会话状态
|
||||
ChatSession::where('id', $sessionId)->update([
|
||||
'status' => ChatSession::STATUS_ENDED,
|
||||
'end_time' => time(),
|
||||
'update_time' => time(),
|
||||
]);
|
||||
|
||||
// 清理Redis缓存
|
||||
$redis = $this->getRedis();
|
||||
$redis->del(self::USER_ACTIVE_SESSION_PREFIX . $session['user_id']);
|
||||
|
||||
// 释放客服会话配额
|
||||
if ($session['admin_id']) {
|
||||
$this->assignService->releaseSession($sessionId, $session['admin_id']);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转接会话
|
||||
*/
|
||||
public function transferSession(int $sessionId, int $newAdminId): bool
|
||||
{
|
||||
$session = ChatSession::find($sessionId);
|
||||
if (!$session || $session['status'] !== ChatSession::STATUS_ACTIVE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$oldAdminId = $session['admin_id'];
|
||||
|
||||
// 更新会话归属
|
||||
ChatSession::where('id', $sessionId)->update([
|
||||
'admin_id' => $newAdminId,
|
||||
'update_time' => time(),
|
||||
]);
|
||||
|
||||
// 更新Redis
|
||||
$redis = $this->getRedis();
|
||||
$redis->set('cs:session:owner:' . $sessionId, $newAdminId);
|
||||
|
||||
// 调整负载计数
|
||||
if ($oldAdminId) {
|
||||
$oldLoad = (int)$redis->get('cs:agent:load:' . $oldAdminId) ?: 0;
|
||||
if ($oldLoad > 0) {
|
||||
$redis->decr('cs:agent:load:' . $oldAdminId);
|
||||
}
|
||||
}
|
||||
$redis->incr('cs:agent:load:' . $newAdminId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话评价
|
||||
*/
|
||||
public function rateSession(int $sessionId, int $rating, ?string $content = null): bool
|
||||
{
|
||||
$session = ChatSession::find($sessionId);
|
||||
if (!$session) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 评分范围 1-5
|
||||
$rating = max(1, min(5, $rating));
|
||||
|
||||
ChatSession::where('id', $sessionId)->update([
|
||||
'rating' => $rating,
|
||||
'rating_content' => $content ? mb_substr($content, 0, 500) : null,
|
||||
'update_time' => time(),
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册用户连接
|
||||
*/
|
||||
public function registerUserConnection(int $userId, int $fd): void
|
||||
{
|
||||
$redis = $this->getRedis();
|
||||
$redis->setex(self::CONN_USER_PREFIX . $userId, 60, $fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册客服连接
|
||||
*/
|
||||
public function registerAgentConnection(int $adminId, int $fd): void
|
||||
{
|
||||
$redis = $this->getRedis();
|
||||
$redis->setex(self::CONN_AGENT_PREFIX . $adminId, 60, $fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新用户连接TTL
|
||||
*/
|
||||
public function refreshUserConnection(int $userId): void
|
||||
{
|
||||
$redis = $this->getRedis();
|
||||
$redis->expire(self::CONN_USER_PREFIX . $userId, 60);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新客服连接TTL
|
||||
*/
|
||||
public function refreshAgentConnection(int $adminId): void
|
||||
{
|
||||
$redis = $this->getRedis();
|
||||
$redis->expire(self::CONN_AGENT_PREFIX . $adminId, 60);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户连接FD
|
||||
*/
|
||||
public function getUserFd(int $userId): ?int
|
||||
{
|
||||
$redis = $this->getRedis();
|
||||
$fd = $redis->get(self::CONN_USER_PREFIX . $userId);
|
||||
return $fd ? (int)$fd : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客服连接FD
|
||||
*/
|
||||
public function getAgentFd(int $adminId): ?int
|
||||
{
|
||||
$redis = $this->getRedis();
|
||||
$fd = $redis->get(self::CONN_AGENT_PREFIX . $adminId);
|
||||
return $fd ? (int)$fd : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理用户连接
|
||||
*/
|
||||
public function clearUserConnection(int $userId): void
|
||||
{
|
||||
$redis = $this->getRedis();
|
||||
$redis->del(self::CONN_USER_PREFIX . $userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理客服连接
|
||||
*/
|
||||
public function clearAgentConnection(int $adminId): void
|
||||
{
|
||||
$redis = $this->getRedis();
|
||||
$redis->del(self::CONN_AGENT_PREFIX . $adminId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客服会话列表
|
||||
*/
|
||||
public function getAgentSessions(int $adminId): array
|
||||
{
|
||||
$sessions = ChatSession::getActiveByAdminId($adminId);
|
||||
|
||||
// 附加用户信息和未读数
|
||||
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'] = ChatMessage::where('session_id', $session['id'])
|
||||
->where('sender_type', ChatMessage::SENDER_USER)
|
||||
->where('status', '<', ChatMessage::STATUS_READ)
|
||||
->count();
|
||||
}
|
||||
|
||||
return $sessions;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待分配会话列表
|
||||
*/
|
||||
public function getPendingSessions(): array
|
||||
{
|
||||
$sessions = ChatSession::where('status', ChatSession::STATUS_PENDING)
|
||||
->order('create_time', 'asc')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
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,
|
||||
];
|
||||
}
|
||||
|
||||
return $sessions;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Redis实例
|
||||
*/
|
||||
private function getRedis(): \Redis
|
||||
{
|
||||
return Cache::store('redis')->handler();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user