// +---------------------------------------------------------------------- namespace App\Services\Api; use App\Models\ImChatModel; use App\Models\LiveChatModel; use App\Models\MerchantModel; use App\Models\MessageModel; use App\Services\BaseService; use App\Services\ConfigService; use App\Services\RedisService; /** * 站内消息服务管理-服务类 * @author laravel开发员 * @since 2020/11/11 * Class MessageService * @package App\Services\Api */ class MessageService extends BaseService { // 静态对象 protected static $instance = null; /** * 构造函数 * @author laravel开发员 * @since 2020/11/11 * MessageService constructor. */ public function __construct() { $this->model = new MessageModel(); } /** * 静态入口 * @return static|null */ public static function make() { if (!self::$instance) { self::$instance = (new static()); } return self::$instance; } /** * 消息列表 * @param $userId * @param $params * @param int $pageSize * @return array */ public function getDataList($userId, $params, $pageSize = 0) { $page = request()->post('page', 1); $cacheKey = "caches:messages:history_{$page}_" . md5($userId . json_encode($params) . $pageSize); $datas = RedisService::get($cacheKey); if ($datas) { return $datas; } $where = ['a.status' => 1, 'a.mark' => 1]; $field = ['a.id', 'a.title', 'a.type', 'a.msg_type', 'a.chat_type', 'a.description', 'a.content', 'a.from_user_name', 'a.from_user_avatar', 'a.from_uid', 'a.to_user_name', 'a.to_user_avatar', 'a.to_uid', 'a.create_time', 'a.is_read', 'a.pages', 'a.status']; $model = $this->model->from('message as a') ->leftJoin('member as b', 'b.id', '=', 'a.from_uid') ->leftJoin('member as c', 'c.id', '=', 'a.to_uid') ->where($where) ->where(function ($query) use ($params, $userId) { $fromUid = isset($params['from_uid']) ? intval($params['from_uid']) : 0; if ($fromUid) { $query->where('a.from_uid', $fromUid); } $type = isset($params['type']) ? intval($params['type']) : 0; if ($type) { $query->where('a.type', $type); } if ($type != 9) { $query->where('a.to_uid', $userId); }else { $query->where(function ($query) use ($userId) { $query->where(function($query) use($userId){ $query->where(['a.from_uid' => $userId,'a.from_show'=>1]); })->orWhere(function($query) use($userId){ $query->where(['a.to_uid' => $userId,'a.to_show'=>1]); }); }); } $chatType = isset($params['chat_type']) ? intval($params['chat_type']) : 0; if ($chatType) { $query->where('a.chat_type', $chatType); } $chatKey = isset($params['chat_key']) ? trim($params['chat_key']) : ''; if ($chatKey) { $query->where('a.chat_key', $chatKey); } }); $countModel = clone $model; $datas = $model->select($field) ->orderBy('a.create_time', 'desc') ->orderBy('a.id', 'desc') ->paginate($pageSize > 0 ? $pageSize : 9999999); $datas = $datas ? $datas->toArray() : []; if ($datas) { $ids = []; foreach ($datas['data'] as &$item) { // 已读 if($item['to_uid'] == $userId){ $ids[] = $item['id']; } $item['time_text'] = isset($item['create_time']) && $item['create_time'] ? dateForWeek($item['create_time']) : ''; $item['content'] = $item['msg_type'] == 2 ? get_images_preview(json_decode($item['content'], true), '') : $item['content']; $item['from_user_avatar'] = $item['from_user_avatar'] ? get_image_url($item['from_user_avatar']) : get_image_url('/images/member/logo.png'); $item['to_user_avatar'] = $item['to_user_avatar'] ? get_image_url($item['to_user_avatar']) : get_image_url('/images/member/logo.png'); } unset($item); // 更新已读 if ($ids) { $this->model->whereIn('id', $ids)->update(['is_read' => 1, 'update_time' => time()]); RedisService::clear("caches:message:topList_{$userId}"); RedisService::keyDel("caches:messages:un_*"); } $datas['unread'] = $countModel->where(['a.is_read'=>2])->count('a.id'); arsort($datas['data'], true); $sort = isset($params['sort'])? $params['sort'] : 1; if($sort != 2){ $datas['data'] = array_reverse($datas['data'], false); } RedisService::set($cacheKey, $datas, rand(3, 5)); } return [ 'list' => isset($datas['data']) ? $datas['data'] : [], 'total' => isset($datas['total']) ? $datas['total'] : 0, 'unread' => isset($datas['unread']) ? $datas['unread'] : 0, 'pageSize' => $pageSize ]; } /** * 直播间消息列表 * @param $userId * @param $params * @param int $pageSize * @return array */ public function getLiveDataList($userId, $params, $pageSize = 0) { $page = request()->post('page', 1); $liveId = request()->post('live_id', 0); $cacheKey = "caches:messages:live_{$liveId}_{$page}_" . md5(json_encode($params) . $pageSize); $datas = RedisService::get($cacheKey); if ($datas) { return $datas; } $where = ['a.status' => 1, 'a.mark' => 1]; $field = ['a.id', 'a.msg_type','a.chat_key','a.live_id', 'a.description', 'a.message','b.nickname', 'a.from_uid','b.avatar as from_user_avatar', 'a.to_uid', 'a.create_time', 'a.status']; $datas = LiveChatModel::with(['member'])->from('live_chat as a') ->leftJoin('member as b', 'b.id', '=', 'a.from_uid') ->leftJoin('member as c', 'c.id', '=', 'a.to_uid') ->where($where) ->where(function ($query) use ($params, $userId) { $fromUid = isset($params['from_uid']) ? intval($params['from_uid']) : 0; if ($fromUid) { $query->where('a.from_uid', $fromUid); } $liveId = isset($params['live_id']) ? intval($params['live_id']) : 0; if ($liveId) { $query->where('a.live_id', $liveId); } $chatKey = isset($params['chat_key']) ? trim($params['chat_key']) : ''; if ($chatKey) { $query->where('a.chat_key', $chatKey); } })->select($field) ->orderBy('a.create_time', 'desc') ->orderBy('a.id', 'desc') ->paginate($pageSize > 0 ? $pageSize : 9999999); $datas = $datas ? $datas->toArray() : []; if ($datas) { foreach ($datas['data'] as &$item) { $item['time_text'] = isset($item['create_time']) && $item['create_time'] ? dateForWeek($item['create_time']) : ''; $item['member']['avatar'] = $item['from_user_avatar']? get_image_url($item['from_user_avatar']) : '/images/member/logo.png'; } unset($item); arsort($datas['data'], true); $datas['data'] = array_reverse($datas['data'], false); RedisService::set($cacheKey, $datas, rand(3, 5)); } return [ 'list' => isset($datas['data']) ? $datas['data'] : [], 'total' => isset($datas['total']) ? $datas['total'] : 0, 'pageSize' => $pageSize ]; } /** * 获取站内消息窗口列表 * @param $userId * @return array|mixed */ public function getGroupList($userId, $cache = false) { $cachekey = "caches:message:topList_{$userId}"; $datas = RedisService::get($cachekey); if ($datas && $cache) { $datas['cache'] = true; return $datas; } $types = [1, 4, 5]; $setting = MemberSettingService::make()->getSetting($userId); $pushStatus = false; if ($setting) { foreach ($setting as $k => $v) { if ($v == 1) { if ($k == 'receive_app') { $pushStatus = true; } else if ($k == 'receive_order') { $types[] = 2; } else if ($k == 'receive_account') { $types[] = 3; } } } asort($types); } else { $pushStatus = true; $types = [1, 2, 3, 4, 5]; } // 关闭系统APP消息 if (!$pushStatus) { return ['list' => [], 'total' => 0, 'types' => []]; } $field = ['title', 'type', 'to_uid', 'description', 'is_read', 'create_time']; $datas = $this->model->where(['to_uid' => $userId, 'status' => 1, 'mark' => 1]) ->whereIn('type', $types) ->groupBy('type') ->orderBy('type', 'asc') ->orderBy('is_read', 'desc') ->orderBy('create_time', 'desc') ->select($field) ->get(); $datas = $datas ? $datas->toArray() : []; $total = 0; if ($datas) { $titles = [1 => '公告通知', 2 => '订单通知', 3 => '账户通知', 4 => '客服通知', 5 => '互动消息']; foreach ($datas as &$item) { $item['title'] = isset($titles[$item['type']]) ? $titles[$item['type']] : '消息通知'; $data = $this->getNewMessage($item['type'], 0, $userId); $item['description'] = isset($data['description']) && $data['description'] ? $data['description'] : (isset($data['content']) ? mb_substr($data['content'], 0, 56, 'utf-8') . '...' : lang('有新消息')); $item['create_time'] = isset($data['create_time']) && $data['create_time'] ? $data['create_time'] : ''; $item['time_text'] = isset($item['create_time']) && $item['create_time'] ? dateFormat($item['create_time']) : ''; $item['create_time'] = isset($item['create_time']) ? datetime($item['create_time'], 'Y-m-d H.i.s') : ''; $unread = $this->getUnreadCount($userId, 0, $item['type']); $item['unread'] = intval($unread); $total += intval($unread); } RedisService::set($cachekey, ['list' => $datas, 'total' => $total, 'types' => $types], rand(2, 3)); } return ['list' => $datas, 'total' => $total, 'types' => $types]; } /** * 聊天分组消息 * @param $userId * @param $params * @param int $pageSize * @return array */ public function getDataListFromatKey($userId, $params, $pageSize = 0) { $page = request()->post('page', 1); $cacheKey = "caches:m_chat:{$page}_" . md5($userId . json_encode($params) . $pageSize); $datas = RedisService::get($cacheKey); $data = isset($datas['data']) ? $datas['data'] : []; if ($datas && $data) { return [ 'unread' => isset($datas['unReadCount']) ? $datas['unReadCount'] : 0, 'total' => isset($datas['total']) ? $datas['total'] : 0, 'list' => $data, 'pageSize' => $pageSize, 'cache' => true, ]; } // 不接收则不显示聊天消息 $receiveCustom = MemberSettingService::make()->getSetting($userId, 'receive_custom', 1); if ($receiveCustom != 1) { return [ 'unread' => 0, 'total' => 0, 'list' => [], 'pageSize' => $pageSize, 'close' => true, ]; } $where = ['a.type' => 9, 'a.status' => 1, 'a.mark' => 1]; $expire = ConfigService::make()->getConfigByCode('chat_log_expire'); $expire = $expire ? $expire * 86400 : 60 * 86400; $field = ['a.id', 'a.title', 'a.type', 'a.msg_type','a.to_show', 'a.chat_key', 'a.chat_type', 'a.description', 'a.content', 'a.from_user_name', 'a.from_user_avatar', 'a.from_uid', 'a.to_user_name', 'a.to_user_avatar', 'a.to_uid', 'a.create_time', 'a.is_read', 'a.status']; $datas = $this->model->from('message as a') ->where($where) ->where('a.chat_key', '>', 0) ->where('a.create_time', '>=', time() - $expire) ->where(function ($query) use ($params, $userId) { $chatKey = isset($params['chat_key']) ? trim($params['chat_key']) : ''; if ($chatKey) { $query->where('a.chat_key', $chatKey); } $isRead = isset($params['is_read']) ? intval($params['is_read']) : 0; if ($isRead) { $query->where('a.is_read', $isRead); } $chatType = isset($params['chat_type']) ? intval($params['chat_type']) : 0; if ($chatType) { $query->where('a.chat_type', $chatType); } if ($userId) { $query->where(function ($query) use ($userId) { $query->where(function($query) use($userId){ $query->where(['a.from_uid' => $userId,'a.from_show'=>1]); })->orWhere(function($query) use($userId){ $query->where(['a.to_uid' => $userId,'a.to_show'=>1]); }); }); } }) ->select($field) ->groupBy('chat_key') ->orderBy('a.create_time', 'desc') ->orderBy('a.id', 'desc') ->paginate($pageSize > 0 ? $pageSize : 9999999); $datas = $datas ? $datas->toArray() : []; $unReadCount = 0; if ($datas) { foreach ($datas['data'] as &$item) { $item['from_user_avatar'] = isset($item['from_user_avatar']) && $item['from_user_avatar'] ? get_image_url($item['from_user_avatar']) : get_image_url('/images/member/logo.png'); $item['to_user_avatar'] = isset($item['to_user_avatar']) && $item['to_user_avatar'] ? get_image_url($item['to_user_avatar']) : get_image_url('/images/member/logo.png'); $data = $this->getNewMessage(0, $item['chat_key']); $item['description'] = isset($data['description']) && $data['description'] ? $data['description'] : (isset($data['content']) ? mb_substr($data['content'], 0, 30, 'utf-8') : lang('有新消息')); $item['create_time'] = isset($data['create_time']) && $data['create_time'] ? $data['create_time'] : ''; $item['time_text'] = isset($item['create_time']) && $item['create_time'] ? dateFormat($item['create_time']) : ''; $item['create_time'] = isset($item['create_time']) ? datetime($item['create_time'], 'Y-m-d H.i.s') : ''; $item['unread'] = $this->getUnreadCount($userId, $item['chat_key'], 0); if ($item['from_uid'] == $userId) { $item['from_user_name'] = $item['to_user_name']; $item['from_user_avatar'] = $item['to_user_avatar']; $item['tuid'] = $item['to_uid']; } else { $item['tuid'] = $item['from_uid']; } $unReadCount += intval($item['unread']); } unset($item); $datas['unReadCount'] = $unReadCount; RedisService::set($cacheKey, $datas, rand(3, 5)); } return [ 'unread' => $unReadCount, 'total' => isset($datas['total']) ? $datas['total'] : 0, 'list' => isset($datas['data']) ? $datas['data'] : [], 'pageSize' => $pageSize, 'cache' => false, ]; } /** * 获取最新消息 * @param $chatKey * @return mixed */ public function getNewMessage($type = 0, $chatKey = 0, $userId = 0) { $cacheKey = "caches:messages:new_{$type}_{$chatKey}_{$userId}"; $data = RedisService::get($cacheKey); if ($data) { return $data; } $where = ['status' => 1, 'mark' => 1]; if ($type) { $where['type'] = $type; } if ($chatKey) { $where['chat_key'] = $chatKey; } if ($userId) { $where['to_uid'] = $userId; } $data = $this->model->where($where)->select('id', 'title', 'description', 'msg_type', 'content', 'create_time') ->orderBy('create_time', 'desc') ->orderBy('id', 'desc') ->first(); $data = $data ? $data->toArray() : []; if ($data) { if ($data['msg_type'] == 2) { $data['description'] = '[图片]'; } else if ($data['msg_type'] == 3) { $data['description'] = '[视频聊天]'; } else if ($data['msg_type'] == 4) { $data['description'] = '[直播间分享]'; } RedisService::set($cacheKey, $data, rand(3, 5)); } return $data; } /** * 获取未读消息数量 * @param $userId * @return array|mixed */ public function getBarCount($userId, $type = 0) { $cacheKey = "caches:barCount:{$userId}_{$type}"; $data = RedisService::get($cacheKey); if ($data) { return $data; } $data = $this->getUnreadCount($userId, 0, $type); RedisService::set($cacheKey, $data, rand(3, 5)); return $data; } /** * 推送站内消息 * @param $userId 用户ID * @param $title 标题 * @param $message 消息内容 * @param int $type 类型:1-公告,2-订单,3-账户,4-客服,5-动态 * @return false */ public function pushMessage($userId, $title, $message, $type = 1, $fromUid = 0, $pageUrl='') { $types = [4, 5]; $pushStatus = false; $datas = MemberSettingService::make()->getSetting($userId); if ($datas) { foreach ($datas as $k => $v) { if ($v == 1) { if ($k == 'receive_app') { $pushStatus = true; $types[] = 1; } else if ($k == 'receive_order') { $types[] = 2; } else if ($k == 'receive_account') { $types[] = 3; } } } } else { $pushStatus = true; $types = [1, 2, 3, 4, 5]; } if ($userId && $pushStatus && in_array($type, $types)) { $appName = ConfigService::make()->getConfigByCode('app_name', '星链社交'); $log = [ 'from_uid' => $fromUid, 'to_uid' => $userId, 'from_user_name' => $appName, 'chat_type' => 0, 'msg_type' => 1, 'type' => $type, 'title' => $title, 'description' => $title, 'content' => $message, 'pages' => $pageUrl?$pageUrl:'', 'chat_key' => "0{$userId}", 'create_time' => time(), 'status' => 1, 'mark' => 1, ]; return MessageModel::insert($log); } return false; } /** * 获取 * @param $userId * @return array|mixed */ public function getUnreadCount($userId, $chatKey = 0, $type = 0) { $cacheKey = "caches:messages:un_{$userId}_{$chatKey}_{$type}"; $data = RedisService::get($cacheKey); if (RedisService::exists($cacheKey)) { return $data; } $where = ['to_uid' => $userId,'to_show'=>1, 'status' => 1, 'is_read' => 2, 'mark' => 1]; if ($type > 0) { $where['type'] = $type; } if ($chatKey) { $where['chat_key'] = $chatKey; } $data = $this->model->where($where)->count('id'); RedisService::set($cacheKey, $data, rand(3, 5)); return $data; } /** * 验证发送消息或者内容是否有屏蔽词 * @param $message 消息或内容 * @param $type 是否返回屏蔽后内容:1-是 * @return bool */ public function filterMessage($message, $type = 1) { $filter = ConfigService::make()->getConfigByCode('chat_sensitive'); $filter = !empty($filter) ? explode('、', $filter) : []; $filter = array_filter($filter); if ($filter) { if ($type != 2) { foreach ($filter as $kw) { $message = preg_replace("/{$kw}/", '***', $message); } } else { foreach ($filter as $kw) { if (preg_match("/{$kw}/", $message)) { return false; } } } // 手机号、邮箱、网址 if ($type == 1) { $message = preg_replace("/^(1[3-9][0-9]{9})$/", '***', $message); $message = preg_replace("/([a-z0-9&\-_.]+@[\w\-_]+([\w\-.]+)?\.[\w\-]+)/is", '***', $message); $message = str_replace(['https:', 'http:', '.com', '.cn', '.net', '.top', 'www.'], '***', $message); $message = preg_replace("/([a-zA-Z0-9][a-zA-Z0-9\_]{6,20})/", '***', $message); $message = preg_replace("/\*{3}\*{1,}/", '***', $message); } } return $type == 3 && $message === '***' ? '' : $message; } /** * 已读 * @param $userId 用户ID * @param $chatKey 聊天窗口ID * @return bool */ public function setRead($userId, $type = 0, $chatKey = '') { $where = ['to_uid' => $userId]; if ($type) { $where['type'] = $type; } if ($chatKey) { $where['chat_key'] = $chatKey; } $this->model->where($where)->update(['is_read' => 1, 'update_time' => time()]); // 清除缓存 RedisService::keyDel("caches:messages:bar*"); RedisService::keyDel("caches:messages:new_*"); RedisService::keyDel("caches:messages:un_*"); RedisService::keyDel("caches:messages:topList_{$userId}"); return true; } /** * 隐藏 * @param $userId * @param int $expire * @return mixed */ public function setHide($userId, $type) { $this->model->where(['to_uid' => $userId, 'type' => $type, 'status' => 1, 'mark' => 1]) ->update(['update_time' => time(),'to_show'=>2, 'is_read' => 1, 'status' => 3]); // 清除缓存 RedisService::keyDel("caches:messages:bar*"); RedisService::keyDel("caches:messages:new_*"); RedisService::keyDel("caches:messages:un_*"); RedisService::keyDel("caches:messages:topList_{$userId}"); return true; } /** * 清除历史 * @param $userId * @param int $expire * @return mixed */ public function clear($userId, $chatKey = 0, $type = 0) { $where = ['to_uid' => $userId, 'mark' => 1]; if ($type) { $where['type'] = $type; } if ($chatKey) { $where['chat_key'] = $chatKey; } $this->model->where($where) ->update(['update_time' => time(), 'mark' => 0]); // 清除缓存 RedisService::keyDel("caches:messages:bar*"); RedisService::keyDel("caches:messages:new_*"); RedisService::keyDel("caches:messages:un_*"); RedisService::keyDel("caches:messages:topList_{$userId}"); return true; } /** * 清除历史 * @param $userId * @param int $expire * @return mixed */ public function clearAll($userId, $type=0) { $expire = ConfigService::make()->getConfigByCode('chat_log_expire'); $expire = $expire > 0 ? $expire : 60; $this->model->where(['to_uid' => $userId, 'mark' => 0]) ->where('update_time', '<', time() - $expire * 86400) ->delete(); // 清除缓存 RedisService::keyDel("caches:messages:bar*"); RedisService::keyDel("caches:messages:new_*"); RedisService::keyDel("caches:messages:topList_{$userId}"); // 站内消息发给我的全删 $this->model->whereNotIn('type',[9])->where(function($query) use($userId, $type){ $query->where(['to_uid'=> $userId,'mark'=>1]); if($type>0){ $query->where('type', $type); } })->update(['update_time' => time(), 'mark' => 0]); if($type==9 || !$type){ // 聊天消息我发的全删 $this->model->where(function($query) use($userId){ $query->where(['type'=>9,'from_uid'=> $userId,'mark'=>1]); })->update(['update_time' => time(), 'mark' => 0]); // 聊天消息发给我的,接收方全隐藏 $this->model->where(function($query) use($userId){ $query->where(['type'=>9,'to_uid'=> $userId,'mark'=>1]); })->update(['update_time' => time(),'is_read'=>1, 'to_show' => 2]); } return true; } }