Initial commit: queue workspace

Made-with: Cursor
This commit is contained in:
apple
2026-03-21 02:55:24 +08:00
commit 78de918c37
12388 changed files with 1840126 additions and 0 deletions

View File

@@ -0,0 +1,333 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\webscoket;
use app\services\message\service\StoreServiceLogServices;
use app\services\message\service\StoreServiceRecordServices;
use app\services\message\service\StoreServiceServices;
use app\services\order\StoreOrderRefundServices;
use app\services\order\StoreOrderServices;
use app\services\product\product\StoreProductServices;
use app\services\user\UserServices;
use app\services\wechat\WechatUserServices;
use app\jobs\notice\template\WechatTemplateJob;
use crmeb\services\wechat\Messages;
use crmeb\services\wechat\OfficialAccount;
use crmeb\utils\Arr;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\facade\Log;
use think\response\Json;
/**
* socket 事件基础类
* Class BaseHandler
* @package app\webscoket
*/
abstract class BaseHandler
{
/**
* @var Manager
*/
protected $manager;
/**
* @var Room
*/
protected $room;
/**
* @var int
*/
protected $fd;
/**
* 用户聊天端
* @var int|null
*/
protected $formType;
/**
* 登陆
* @param array $data
* @param Response $response
* @return mixed
*/
abstract public function login(array $data, Response $response);
/**
* 事件入口
* @param $event
* @return |null
*/
public function handle($event)
{
[$method, $result, $manager, $room] = $event;
$this->manager = $manager;
$this->room = $room;
$this->fd = array_shift($result);
$this->formType = array_shift($result);
if (method_exists($this, $method)) {
if (($method == 'login' || $method == 'kefu_login') && is_string($result[0])) {
$result[0] = ['token' => $result[0]];
}
return $this->{$method}(...$result);
} else {
Log::error('socket 回调事件' . $method . '不存在,消息内容为:' . json_encode($result));
return null;
}
}
/**
* 聊天事件
* @param array $data
* @param Response $response
* @return bool|Json|null
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException|TransportExceptionInterface
*/
public function chat(array $data = [], Response $response)
{
$user = $this->room->get($this->fd);
if (!$user) {
return $response->fail('聊天用户不存在');
}
$to_uid = $data['to_uid'] ?? 0;
$msn_type = $data['type'] ?? 0;
$msn = $data['msn'] ?? '';
$formType = $this->formType ?? null;
//是否为游客
$isTourist = $data['is_tourist'] ?? 0;
$tourist_uid = $data['tourist_uid'] ?? 0;
$isTourist = $isTourist && $tourist_uid;
$tourist_avatar = $data['tourist_avatar'] ?? '';
$uid = $isTourist ? $tourist_uid : $user['uid'];
if (!$to_uid) {
return $response->message('err_tip', ['msg' => '用户不存在']);
}
if ($to_uid == $uid) {
return $response->message('err_tip', ['msg' => '不能和自己聊天']);
}
/** @var StoreServiceLogServices $logServices */
$logServices = app()->make(StoreServiceLogServices::class);
if (!in_array($msn_type, StoreServiceLogServices::MSN_TYPE)) {
return $response->message('err_tip', ['msg' => '格式错误']);
}
$msn = trim(strip_tags(str_replace(["\n", "\t", "\r", "&nbsp;"], '', htmlspecialchars_decode($msn))));
$data = compact('to_uid', 'msn_type', 'msn', 'uid');
$data['add_time'] = time();
$data['is_tourist'] = $data['is_tourist'] ?? 0;
//获取收消息人的所有fd信息
$toUserFd = $this->manager->userFd($user['type'] == 2 ? 1 : 2, $to_uid);
$toUser = ['to_uid' => -1];
$fremaData = [];
foreach ($toUserFd as $value) {
if ($frem = $this->room->get($value)) {
$fremaData[] = $frem;
//如果当收消息人在和当前发消息人对话中
if ($frem['to_uid'] == $user['uid']) {
$toUser = $frem;
}
}
}
//是否在线
//是否和当前用户对话
$online = $toUserFd && $toUser && $toUser['to_uid'] !== -1;
$data['type'] = $online ? 1 : 0;
$data = $logServices->save($data);
$data = $data->toArray();
$data['_add_time'] = $data['add_time'];
$data['add_time'] = strtotime($data['add_time']);
$_userInfo = [];
if (!$isTourist) {
/** @var StoreServiceServices $userService */
$userService = app()->make(StoreServiceServices::class);
$_userInfo = $userService->get(['uid' => $data['uid']], ['nickname', 'avatar']);
if (!$_userInfo) {
/** @var UserServices $userService */
$userService = app()->make(UserServices::class);
$_userInfo = $userService->getUserInfo($data['uid'], 'nickname,avatar');
}
}
if ($isTourist || !$_userInfo) {
$avatar = sys_config('tourist_avatar');
$_userInfo['avatar'] = $tourist_avatar ? $tourist_avatar : Arr::getArrayRandKey(is_array($avatar) ? $avatar : []);
$_userInfo['nickname'] = '游客' . $uid;
}
$data['nickname'] = $_userInfo['nickname'];
$data['avatar'] = $_userInfo['avatar'];
//商品消息类型
$data['productInfo'] = [];
if ($msn_type == StoreServiceLogServices::MSN_TYPE_GOODS && $msn) {
/** @var StoreProductServices $productServices */
$productServices = app()->make(StoreProductServices::class);
$productInfo = $productServices->get((int)$msn, ['id', 'store_name', 'IFNULL(sales,0) + IFNULL(ficti,0) as sales', 'image', 'slider_image', 'price', 'vip_price', 'ot_price', 'stock']);
$data['productInfo'] = $productInfo ? $productInfo->toArray() : [];
}
//订单消息类型
$data['orderInfo'] = [];
if ($msn_type == StoreServiceLogServices::MSN_TYPE_ORDER && $msn) {
/** @var StoreOrderServices $orderServices */
$orderServices = app()->make(StoreOrderServices::class);
$order = $orderServices->getUserOrderDetail($msn, $uid);
if ($order) {
$order = $orderServices->tidyOrder($order->toArray(), true, true);
$order['add_time_y'] = date('Y-m-d', $order['add_time']);
$order['add_time_h'] = date('H:i:s', $order['add_time']);
$data['orderInfo'] = $order;
}
}
if ($msn_type == StoreServiceLogServices::MSN_TYPE_REFUND_ORDER && $msn) {
/** @var StoreOrderRefundServices $orderRefundServices */
$orderRefundServices = app()->make(StoreOrderRefundServices::class);
$order = $orderRefundServices->refundDetail($msn);
if ($order) {
$order['add_time_y'] = date('Y-m-d', $order['add_time']);
$order['add_time_h'] = date('H:i:s', $order['add_time']);
$data['orderInfo'] = $order;
}
}
//用户向客服发送消息,判断当前客服是否在登录中
/** @var StoreServiceRecordServices $serviceRecored */
$serviceRecored = app()->make(StoreServiceRecordServices::class);
$unMessagesCount = $logServices->getMessageNum(['uid' => $uid, 'to_uid' => $to_uid, 'type' => 0, 'is_tourist' => $isTourist ? 1 : 0]);
//记录当前用户和他人聊天记录
$data['recored'] = $serviceRecored->saveRecord($uid, $to_uid, $msn, $formType ?? 0, $msn_type, $unMessagesCount, $isTourist, $data['nickname'], $data['avatar']);
//是否在线
if ($online) {
$this->manager->pushing($toUserFd, $response->message('reply', $data)->getData());
} else {
//用户在线,可是没有和当前用户进行聊天,给当前用户发送未读条数
if ($toUserFd && $toUser && isset($toUser['to_uid']) && $toUser['to_uid'] != $uid) {
$data['recored']['nickname'] = $_userInfo['nickname'];
$data['recored']['avatar'] = $_userInfo['avatar'];
$this->manager->pushing($toUserFd, $response->message('mssage_num', [
'uid' => $uid,
'num' => $unMessagesCount,
'recored' => $data['recored']
])->getData());
}
if (!$isTourist) {
//用户不在线
/** @var WechatUserServices $wechatUserServices */
$wechatUserServices = app()->make(WechatUserServices::class);
$userInfo = $wechatUserServices->getOne(['uid' => $to_uid, 'user_type' => 'wechat'], 'nickname,subscribe,openid,headimgurl');
if ($userInfo && $userInfo['subscribe'] && $userInfo['openid']) {
$description = '您有新的消息,请注意查收!';
if ($formType !== null) {
$head = '客服接待消息提醒';
$url = sys_config('site_url') . '/kefu/mobile_chat?toUid=' . $uid . '&nickname=' . $_userInfo['nickname'];
} else {
$head = '客服回复消息提醒';
$url = sys_config('site_url') . '/pages/extension/customer_list/chat?uid=' . $uid;
}
$message = Messages::newsMessage($head, $description, $url, $_userInfo['avatar']);
$userInfo = $userInfo->toArray();
try {
OfficialAccount::staffSend($message, $userInfo['openid']);
} catch (\Exception $e) {
if ($msn_type == 3) {
$msn = '[图片]';
} elseif ($msn_type == 4) {
$msn = '[语音]';
} elseif ($msn_type == 5) {
$msn = '[商品]';
} elseif ($msn_type == 6) {
$msn = '[订单]';
} elseif ($msn_type == 7) {
$msn = '[退款订单]';
}
// $res = WechatTemplateJob::dispatchDo('sendServiceNoticeNew', [$userInfo['openid'], [
// 'first' => $head,
// 'keyword1' => $formType !== null ? '客服接待' : '客服回复',
// 'keyword2' => '回复中',
// 'keyword3' => date('Y-m-d H:i:s', time()),
// 'remark' => '消息内容:' . $msn . ';点击查看更多消息!'
// ], $url]);
// if (!$res) Log::error($userInfo['nickname'] . '发送失败' . $e->getMessage());
}
}
}
}
return $response->message('chat', $data);
}
/**
* 切换用户聊天
* @param array $data
* @param Response $response
* @return Json
*/
public function to_chat(array $data = [], Response $response)
{
$toUid = $data['id'] ?? 0;
$res = $this->room->get($this->fd);
if ($res && $toUid) {
$uid = $res['uid'];
$this->manager->updateTabelField((int)$res['type'], (int)$uid, (int)$toUid);
//不是游客进入记录
if (!$res['tourist']) {
/** @var StoreServiceRecordServices $service */
$service = app()->make(StoreServiceRecordServices::class);
$service->update(['user_id' => $uid, 'to_uid' => $toUid], ['mssage_num' => 0]);
/** @var StoreServiceLogServices $logServices */
$logServices = app()->make(StoreServiceLogServices::class);
$logServices->update(['uid' => $toUid, 'to_uid' => $uid], ['type' => 1]);
}
return $response->message('mssage_num', ['uid' => $toUid, 'num' => 0, 'recored' => (object)[]]);
}
}
/**
* 测试原样返回
* @param array $data
* @param Response $response
* @return bool|Json|null
*/
public function test(array $data = [], Response $response)
{
return $response->success($data);
}
/**
* 关闭连接触发
* @param array $data
* @param Response $response
*/
public function close(array $data = [], Response $response)
{
$uid = $data['uid'] ?? 0;
if ($uid) {
/** @var StoreServiceRecordServices $service */
$service = app()->make(StoreServiceRecordServices::class);
$service->updateRecord(['to_uid' => $uid], ['online' => 0]);
$this->manager->pushing($this->manager->userFd(Manager::KEFU_TYPE_NUM), $response->message('online', [
'online' => 0,
'uid' => $uid
]), $this->fd);
}
}
}

View File

@@ -0,0 +1,268 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\webscoket;
use think\Config;
use crmeb\services\CacheService;
use Swoole\Websocket\Frame;
use think\Event;
use think\response\Json;
use think\swoole\Websocket;
use think\swoole\websocket\Room;
use app\webscoket\Room as NowRoom;
use think\swoole\websocket\socketio\Handler;
/**
* Class Manager
* @package app\webscoket
*/
class Manager extends Handler
{
/**
* @var
*/
protected $manager;
/**
* @var int
*/
protected $cache_timeout;
/**
* @var Response
*/
protected $response;
/**
* @var \Redis
*/
protected $cache;
/**
* @var NowRoom
*/
protected $nowRoom;
const USER_TYPE = ['admin', 'user', 'kefu', 'supplier'];
const KEFU_TYPE_NUM = 2;
const USER_TYPE_NUM = 1;
/**
* Manager constructor.
* @param Websocket $websocket
* @param Config $config
* @param Room $room
* @param Event $event
* @param Response $response
* @param \app\webscoket\Room $nowRoom
*/
public function __construct(Event $event, Config $config, Websocket $websocket, Response $response, NowRoom $nowRoom)
{
parent::__construct($event, $config, $websocket);
$this->response = $response;
$this->nowRoom = $nowRoom;
$this->cache = CacheService::redisHandler();
$this->nowRoom->setCache($this->cache);
$this->cache_timeout = intval(app()->config->get('swoole.websocket.ping_timeout', 60000) / 1000) + 2;
}
/**
* @param \think\Request $request
* @return bool|void
*/
public function onOpen(\think\Request $request)
{
$fd = $this->websocket->getSender();
$type = $request->get('type');
$token = $request->get('token');
$touristUid = $request->get('tourist_uid', '');
$tourist = !!$touristUid;
if (!$token || !in_array($type, self::USER_TYPE)) {
return $this->websocket->close();
}
// 只有用户模式下才能使用游客模式
if ($type !== self::USER_TYPE[1] && $tourist) {
return $this->websocket->close();
}
$types = self::USER_TYPE;
$this->nowRoom->type(array_flip($types)[$type]);
try {
$data = $this->exec($type, 'login', [$fd, $request->get('form_type', null), ['token' => $token, 'tourist' => $tourist], $this->response])->getData();
} catch (\Throwable $e) {
return $this->websocket->close();
}
if ($tourist) {
$data['status'] = 200;
$data['data']['uid'] = $touristUid;
}
if ($data['status'] != 200 || !($data['data']['uid'] ?? null)) {
return $this->websocket->close();
}
$this->resetPingTimeout($this->pingInterval + $this->pingTimeout);
$uid = $data['data']['uid'];
$type = array_search($type, self::USER_TYPE);
$this->login($type, $uid, $fd);
$this->nowRoom->add((string)$fd, $uid, 0, $tourist ? 1 : 0);
$this->send($fd, $this->response->message('ping', ['now' => time()]));
return $this->send($fd, $this->response->success());
}
public function login($type, $uid, $fd)
{
$key = '_ws_' . $type;
$this->cache->sadd($key, $fd);
$this->cache->sadd($key . $uid, $fd);
$this->refresh($type, $uid);
}
public function refresh($type, $uid)
{
$key = '_ws_' . $type;
$this->cache->expire($key, 1800);
$this->cache->expire($key . $uid, 1800);
}
public function logout($type, $uid, $fd)
{
$key = '_ws_' . $type;
$this->cache->srem($key, $fd);
$this->cache->srem($key . $uid, $fd);
}
/**
* 获取当前用户所有的fd
* @param $type
* @param string $uid
* @return array
*/
public static function userFd($type, $uid = '')
{
$key = '_ws_' . $type . $uid;
return CacheService::redisHandler()->smembers($key) ?: [];
}
/**
* 执行事件调度
* @param $type
* @param $method
* @param $result
* @return null|Json
*/
protected function exec($type, $method, $result)
{
if (!in_array($type, self::USER_TYPE)) {
return null;
}
if (!is_array($result)) {
return null;
}
/** @var Json $response */
return $this->event->until('swoole.websocket.' . $type, [$method, $result, $this, $this->nowRoom]);
}
/**
* @param Frame $frame
* @return bool
*/
public function onMessage(Frame $frame)
{
$fd = $this->websocket->getSender();
$info = $this->nowRoom->get($fd);
$result = json_decode($frame->data, true) ?: [];
if (!isset($result['type']) || !$result['type']) return true;
$this->resetPingTimeout($this->pingInterval + $this->pingTimeout);
$this->refresh($info['type'], $info['uid']);
if ($result['type'] == 'ping') {
return $this->send($fd, $this->response->message('ping', ['now' => time()]));
}
$data = $result['data'] ?? [];
$frame->uid = $info['uid'];
/** @var Response $res */
$res = $this->exec(self::USER_TYPE[$info['type']], $result['type'], [$fd, $result['form_type'] ?? null, $data, $this->response]);
if ($res) return $this->send($fd, $res);
return true;
}
/**
* @param int $type
* @param int $userId
* @param int $toUserId
* @param string $field
*/
public function updateTabelField(int $type, int $userId, int $toUserId, string $field = 'to_uid')
{
$fds = self::userFd($type, $userId);
foreach ($fds as $fd) {
$this->nowRoom->update($fd, $field, $toUserId);
}
}
/**
* 发送文本响应
* @param $fd
* @param Json $json
* @return bool
*/
public function send($fd, \think\response\Json $json)
{
return $this->pushing($fd, $json->getData());
}
/**
* 发送
* @param $data
* @return bool
*/
public function pushing($fds, $data, $exclude = null)
{
if ($data instanceof \think\response\Json) {
$data = $data->getData();
}
$data = is_array($data) ? json_encode($data) : $data;
$fds = is_array($fds) ? $fds : [$fds];
foreach ($fds as $fd) {
if (!$fd) {
continue;
}
if ($exclude && is_array($exclude) && !in_array($fd, $exclude)) {
continue;
} elseif ($exclude && $exclude == $fd) {
continue;
}
$this->websocket->to($fd)->push($data);
}
return true;
}
/**
* 关闭连接
*/
public function onClose()
{
$fd = $this->websocket->getSender();
$tabfd = (string)$fd;
if ($this->nowRoom->exist($fd)) {
$data = $this->nowRoom->get($tabfd);
$this->logout($data['type'], $data['uid'], $fd);
$this->nowRoom->type($data['type'])->del($tabfd);
$this->exec(self::USER_TYPE[$data['type']], 'close', [$fd, null, ['data' => $data], $this->response]);
}
parent::onClose();
}
}

View File

@@ -0,0 +1,111 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\webscoket;
use think\response\Json;
/**
* socket Response
* Class Response
* @package app\webscoket
* @mixin Json
*/
class Response
{
/**
*
* @var Json
*/
protected $response;
/**
* Response constructor.
*/
public function __construct(Json $response)
{
$this->response = $response;
}
/**
* 设置返回参数
* @param string $type
* @param array|null $data
* @param int $status
* @param array $other
* @return Json
*/
public function send(string $type, ?array $data = null, int $status = 200, array $other = [])
{
$res = compact('type', 'status');
if (!is_null($data)) {
$res['data'] = $data;
}
$data = array_merge($res, $other);
$this->response->data($data);
return $this->response;
}
/**
* 成功
* @param string $message
* @param array|null $data
* @return bool|null
*/
public function success($type = 'success', ?array $data = null, int $status = 200)
{
if (is_array($type)) {
$data = $type;
$type = 'success';
}
return $this->send($type, $data, $status);
}
/**
* 失败
* @param string $message
* @param array|null $data
* @return bool|null
*/
public function fail($type = 'error', ?array $data = null, int $status = 400)
{
if (is_array($type)) {
$data = $type;
$type = 'error';
}
return $this->send($type, $data, $status);
}
/**
* 设置返只有类型没有状态的返回数据
* @param string $type
* @param $data
* @return Json
*/
public function message(string $type, $data)
{
$this->response->data(compact('type', 'data'));
return $this->response;
}
/**
* @param $name
* @param $arguments
* @return mixed
*/
public function __call($name, $arguments)
{
return call_user_func_array([$this->response, $name], $arguments);
}
}

View File

@@ -0,0 +1,178 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\webscoket;
use Swoole\Table as SwooleTable;
use think\swoole\Table;
/**
* 房间管理
* Class Room
* @package app\webscoket
*/
class Room
{
/**
* 类型 只有kefu和admin有区别
* @var string
*/
protected $type = '';
/**
* fd前缀
* @var string
*/
protected $tableFdPrefix = 'ws_fd_';
/**
*
* @var array
*/
protected $room = [];
/**
* @var \Redis
*/
protected $cache;
/**
*
*/
const USER_INFO_FD_PRE = 'socket_user_list';
const TYPE_NAME = 'socket_user_type';
/**
* 设置缓存
* @param $cache
* @return $this
*/
public function setCache($cache)
{
$this->cache = $cache;
return $this;
}
/**
* 设置表
* @param string $type
* @return $this
*/
public function type(string $type)
{
$this->type = $type;
return $this;
}
/**
* 获取表实例
* @return SwooleTable
*/
public function getTable()
{
return app()->make(Table::class)->get('user');
}
/**
* 添加fd
* @param string $key fd
* @param int $uid 用户uid
* @param int $to_uid 当前聊天人的uid
* @param int $tourist 是否为游客
* @return mixed
*/
public function add(string $key, int $uid, int $to_uid = 0, int $tourist = 0)
{
$nowkey = $this->tableFdPrefix . $key;
$data = ['fd' => $key, 'type' => $this->type ?: 'user', 'uid' => $uid, 'to_uid' => $to_uid, 'tourist' => $tourist];
$res = $this->getTable()->set($nowkey, $data);
return $res;
}
/**
* 修改数据
* @param string $key
* @param null $field
* @param null $value
* @return bool|mixed
*/
public function update(string $key, $field = null, $value = null)
{
$nowkey = $this->tableFdPrefix . $key;
$res = true;
if (is_array($field)) {
$res = $this->getTable()->set($nowkey, $field);
} else if (!is_array($field) && $value !== null) {
$data = $this->getTable()->get($nowkey);
if (!$data) {
return false;
}
$data[$field] = $value;
$res = $this->getTable()->set($nowkey, $data);
}
return $res;
}
/**
* 重置
* @return $this
*/
public function reset()
{
$this->type = $this->typeReset;
return $this;
}
/**
* 删除
* @param string $key
* @return mixed
*/
public function del(string $key)
{
$nowkey = $this->tableFdPrefix . $key;
return $this->getTable()->del($nowkey);
}
/**
* 是否存在
* @param string $key
* @return mixed
*/
public function exist(string $key)
{
return $this->getTable()->exist($this->tableFdPrefix . $key);
}
/**
* 获取fd的所有信息
* @param string $key
* @return array|bool|mixed
*/
public function get(string $key, string $field = null)
{
return $this->getTable()->get($this->tableFdPrefix . $key, $field);
}
/**
* fd 获取 uid
* @param $key
* @return mixed
*/
public function fdByUid($key)
{
return $this->getTable()->get($this->tableFdPrefix . $key, 'uid');
}
}

Binary file not shown.

View File

@@ -0,0 +1,55 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\webscoket\handler;
use app\services\system\admin\AdminAuthServices;
use app\webscoket\BaseHandler;
use app\webscoket\Response;
use crmeb\exceptions\AuthException;
/**
* Class AdminHandler
* @package app\webscoket\handler
*/
class AdminHandler extends BaseHandler
{
/**
* 后台登陆
* @param array $data
* @param Response $response
* @return bool|\think\response\Json|null
* @throws \Psr\SimpleCache\InvalidArgumentException
*/
public function login(array $data, Response $response)
{
if (!isset($data['token']) || !$token = $data['token']) {
return $response->fail('授权失败!');
}
try {
/** @var AdminAuthServices $adminAuthService */
$adminAuthService = app()->make(AdminAuthServices::class);
$authInfo = $adminAuthService->parseToken($token);
} catch (AuthException $e) {
return $response->fail($e->getMessage());
}
if (!$authInfo || !isset($authInfo['id'])) {
return $response->fail('授权失败!');
}
return $response->success(['uid' => $authInfo['id']]);
}
}

View File

@@ -0,0 +1,146 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\webscoket\handler;
use app\services\kefu\LoginServices;
use app\services\message\service\StoreServiceRecordServices;
use app\services\message\service\StoreServiceServices;
use app\services\user\UserServices;
use app\webscoket\BaseHandler;
use app\webscoket\Manager;
use app\webscoket\Response;
use crmeb\exceptions\AuthException;
/**
* Class KefuHandler
* @package app\webscoket\handler
*/
class KefuHandler extends BaseHandler
{
/**
* 客服登录
* @param array $data
* @param Response $response
* @return bool|mixed|\think\response\Json|null
* @throws \Psr\SimpleCache\InvalidArgumentException
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function login(array $data, Response $response)
{
if (!isset($data['token']) || !$token = $data['token']) {
return $response->fail('授权失败!');
}
try {
/** @var LoginServices $services */
$services = app()->make(LoginServices::class);
$kefuInfo = $services->parseToken($token);
} catch (AuthException $e) {
return $response->fail($e->getMessage());
}
/** @var UserServices $userService */
$userService = app()->make(UserServices::class);
$user = $userService->get($kefuInfo['uid'], ['uid', 'nickname']);
if (!isset($user['uid'])) {
return $response->fail('您登录的客服用户不存在');
}
/** @var StoreServiceRecordServices $service */
$service = app()->make(StoreServiceRecordServices::class);
$service->updateRecord(['to_uid' => $user['uid']], ['online' => 1]);
/** @var StoreServiceServices $service */
$service = app()->make(StoreServiceServices::class);
$service->update(['uid' => $user['uid']], ['online' => 1]);
return $response->success($user->toArray());
}
/**
* 兼容前端方法
* @param array $data
* @param Response $response
*/
public function kefu_login(array $data = [], Response $response)
{
return $this->login($data, $response);
}
/**
* 上下线
* @param array $data
* @param Response $response
*/
public function online(array $data = [], Response $response)
{
$online = $data['online'] ?? 0;
$user = $this->room->get($this->fd);
if ($user) {
/** @var StoreServiceServices $service */
$service = app()->make(StoreServiceServices::class);
$service->update(['uid' => $user['uid']], ['online' => $online]);
if ($user['to_uid']) {
$fd = $this->manager->userFd('', $user['to_uid']);
//给当前正在聊天的用户发送上下线消息
$this->manager->pushing($fd, $response->message('online', [
'online' => $online,
'uid' => $user['uid']
])->getData());
}
}
}
/**
* 客服转接
* @param array $data
* @param Response $response
* @return \think\response\Json
*/
public function transfer(array $data, Response $response)
{
$data = $data['data'] ?? [];
$uid = $data['recored']['uid'] ?? 0;
if ($uid && $this->manager->userFd('', $uid)) {
$data['recored']['online'] = 1;
} else {
$data['recored']['online'] = 0;
}
return $response->message('transfer', $data);
}
/**
* 退出登录
* @param array $data
* @param Response $response
*/
public function logout(array $data = [], Response $response)
{
$user = $this->room->get($this->fd);
$uid = $user['uid'] ?? 0;
if ($uid) {
/** @var StoreServiceServices $service */
$service = app()->make(StoreServiceServices::class);
$service->update(['uid' => $user['uid']], ['online' => 0]);
/** @var StoreServiceRecordServices $service */
$service = app()->make(StoreServiceRecordServices::class);
$service->updateRecord(['to_uid' => $uid], ['online' => 0]);
$this->manager->pushing($this->manager->userFd(Manager::KEFU_TYPE_NUM), $response->message('online', [
'online' => 0,
'uid' => $uid
]), $this->fd);
}
}
}

View File

@@ -0,0 +1,55 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\webscoket\handler;
use app\services\supplier\LoginServices;
use app\webscoket\BaseHandler;
use app\webscoket\Response;
use crmeb\exceptions\AuthException;
/**
* Class SupplierHandler
* @package app\webscoket\handler
*/
class SupplierHandler extends BaseHandler
{
/**
* 供应商登录
* @param array $data
* @param Response $response
* @return bool|mixed|\think\response\Json|null
* @throws \Psr\SimpleCache\InvalidArgumentException
*/
public function login(array $data, Response $response)
{
if (!isset($data['token']) || !$token = $data['token']) {
return $response->fail('授权失败!');
}
try {
/** @var LoginServices $services */
$services = app()->make(LoginServices::class);
$authInfo = $services->parseToken($token);
} catch (AuthException $e) {
return $response->fail($e->getMessage());
}
if (!$authInfo || !isset($authInfo['id'])) {
return $response->fail('授权失败!');
}
return $response->success(['uid' => $authInfo['id']]);
}
}

View File

@@ -0,0 +1,68 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\webscoket\handler;
use app\services\message\service\StoreServiceRecordServices;
use app\services\user\UserAuthServices;
use app\webscoket\BaseHandler;
use app\webscoket\Manager;
use app\webscoket\Response;
use crmeb\exceptions\AuthException;
/**
* Class UserHandler
* @package app\webscoket\handler
*/
class UserHandler extends BaseHandler
{
/**
* 用户登陆
* @param array $data
* @param Response $response
* @return bool|mixed|\think\response\Json|null
*/
public function login(array $data, Response $response)
{
// 游客登陆
if (isset($data['tourist']) && $data['tourist']) {
return $response->success();
}
if (!isset($data['token']) || !$token = $data['token']) {
return $response->fail('授权失败!');
}
try {
/** @var UserAuthServices $services */
$services = app()->make(UserAuthServices::class);
$authInfo = $services->parseToken($token);
} catch (AuthException $e) {
return $response->fail($e->getMessage());
}
$user = $authInfo['user'];
/** @var StoreServiceRecordServices $service */
$service = app()->make(StoreServiceRecordServices::class);
$service->updateRecord(['to_uid' => $user->uid], ['online' => 1, 'type' => $res['form_type'] ?? 1]);
//给所有在线客服人员发送当前用户上线消息
$this->manager->pushing($this->manager->userFd(Manager::KEFU_TYPE_NUM), $response->message('user_online', [
'uid' => $user->uid,
'online' => 1
])->getData(), $this->fd);
return $response->success('login', $user->toArray());
}
}