ChangeType); $params['corpid'] = trim($msgObj->ToUserName); $params['chat_id'] = trim($msgObj->ChatId); $params['change_type'] = $changeType; if(empty($params['corpid']) || empty($params['chat_id']) || empty($params['change_type'])) { Log::logError('ChatGroupService.templateCallBack', [ 'params' => $msgStr, 'err_msg' => '回调参数异常', ], 'interface'); return false; } $params['update_detail'] = isset($msgObj->UpdateDetail) ? trim($msgObj->UpdateDetail) : null; $params['create_time'] = isset($msgObj->CreateTime) ? trim($msgObj->CreateTime) : null; $params['join_scene'] = isset($msgObj->JoinScene) ? trim($msgObj->JoinScene) : null; $params['quit_scene'] = isset($msgObj->QuitScene) ? trim($msgObj->QuitScene) : null; $params['mem_change_cnt'] = isset($msgObj->MemChangeCnt) ? trim($msgObj->MemChangeCnt) : null; $params['request_time'] = time()+5; $params['status'] = null; $params['mem_change_list'] = isset($msgObj->MemChangeList->Item) ? json_decode(json_encode($msgObj->MemChangeList->Item), true) : []; $params['last_mem_ver'] = isset($msgObj->LastMemVer) ? trim($msgObj->LastMemVer) : ''; $params['cur_mem_ver'] = isset($msgObj->CurMemVer) ? trim($msgObj->CurMemVer) : ''; Log::logInfo('群回调数据日志', [ 'params' => $params, 'msg' => $msgStr ], 'ChatGroupCallback'); switch($changeType) { case 'create': // 客户群创建 $result = RedisModel::lPush(ChatGroup::CHAT_GROUP_DETAIL_RDS, json_encode($params)); break; case 'update': // 客户群变更 // 比对版本号 $chatGroupInfo = ChatGroup::where('corpid', $params['corpid'])->where('chat_id', $params['chat_id'])->first(); if(!empty($chatGroupInfo) && $chatGroupInfo->member_version == $params['last_mem_ver']) { switch($params['update_detail']) { case 'add_member': // 成员入群 if($params['update_detail'] == 'add_member') { $res = RedisModel::lPush(QrcodeChatGroup::CHECK_CHAT_GROUP_USER_LIMIT_RDS, json_encode($params)); if(!$res) { Log::logError('ChatGroupService.templateCallBack', [ 'params' => $msgStr, 'err_msg' => '客户群回调事件入群活码队列处理失败', ], 'interface'); } } # 获取新进群成员信息 $customerList = CustomerDetails::suffix($params['corpid']) ->select('external_userid', 'name')->where('corpid', $params['corpid']) ->whereIn('external_userid', $params['mem_change_list'])->get(); $userList = DjUser::select('user_id', 'name')->where('corpid', $params['corpid']) ->whereIn('user_id', $params['mem_change_list']) ->get(); # 插入新进群成员信息到ES $groupData = []; foreach($params['mem_change_list'] as $item) { $userInfo = $customerList->where('external_userid', $item)->first(); if(empty($userInfo)) { $userInfo = $userList->where('user_id', $item)->first(); } $insertData = []; $insertData['corpid'] = $params['corpid']; $insertData['chat_id'] = $params['chat_id']; $insertData['status'] = 1; $insertData['user_id'] = $item; $insertData['type'] = ''; $insertData['join_time'] = date('Y-m-d H:i:s', $params['create_time']); $insertData['update_time'] = date('Y-m-d H:i:s'); $insertData['join_scene'] = $params['join_scene']; $insertData['invitor_user_id'] = null; $insertData['group_nickname'] = $userInfo->name ?? null; $insertData['name'] = $userInfo->name ?? null; $insertData['member_name'] = $userInfo->name ?? null; $insertData['state'] = null; // 数据格式处理 $groupData['body'][] = [ "index" => [ "_index" => ChatGroupMember::TABLE, "_id" => $params['corpid'].'@@'.$params['chat_id'].'@@'.$item ] ]; $groupData['body'][] = $insertData; } if(!empty($groupData)) { $res = EsModel::bulkDoc($groupData); Log::logInfo('新成员入群处理:', [ 'groupData' => $groupData, 'msg' => $msgStr, 'res' => $res ], 'ChatGroupCallback'); if (!isset($res['errors']) || ($res['errors'] != false)) { Log::logError('客户群回调处理过程成员信息入ES失败', [ 'groupData' => $groupData, 'msg_str' => $msgStr, 'res' => $res, ], 'ChatGroupCallback'); EmailQueue::rPush('客户群成员信息入ES异常', '获取客户群信息入ES异常:'.json_encode($res), ['xiaohua.hou@kuxuan-inc.com'], '客户群成员信息入ES异常'); $result = false; } else { $result = true; } } break; case 'del_member': // 成员退群 $result = ChatGroupMember::delMember( $params['corpid'], $params['chat_id'], $params['mem_change_list'], $params['create_time'] ); break; case 'change_owner': // 群主变更 case 'change_name': // 群名变更 case 'change_notice': $result = false; break; } if($result === true) { ChatGroup::where('corpid', $params['corpid'])->where('chat_id', $params['chat_id']) ->update(['member_version' => $params['cur_mem_ver']]); } else { RedisModel::lPush(ChatGroup::CHAT_GROUP_DETAIL_RDS, json_encode($params)); } } else { $result = RedisModel::lPush(ChatGroup::CHAT_GROUP_DETAIL_RDS, json_encode($params)); } break; case 'dismiss': // 客户群解散 $result = ChatGroup::where('corpid', $params['corpid'])->where('chat_id', $params['chat_id']) ->update(['status' => 2, 'updated_at' => date('Y-m-d H:i:s')]); # 更新对应群的群成员状态 ChatGroupMember::UpdateDismissChatGroupMemberStatus($params['corpid'], $params['chat_id']); break; default: Log::logError('ChatGroupService.templateCallBack', [ 'params' => $msgStr, 'err_msg' => '事件类型不合法', ], 'interface'); return false; } if(!$result) { Log::logError('ChatGroupService.templateCallBack', [ 'params' => $msgStr, 'err_msg' => '客户群回调事件处理失败', ], 'interface'); return false; } return true; } catch (\Exception $exception){ Log::logError('ChatGroupService.templateCallBack', [ 'params' => $msgStr, 'line' => $exception->getLine(), 'message' => $exception->getMessage(), ], 'interface'); EmailQueue::rPush('客户群变更回调处理异常', json_encode([ 'params' => $msgStr, 'line' => $exception->getLine(), 'trace' => $exception->getTraceAsString(), ], 256), ['xiaohua.hou@kuxuan-inc.com'], []); return false; } } public static function ownerList($corpid, $userName) { $ownerList = ChatGroup::where('corpid', $corpid)->where('status', 1)->pluck('owner')->toArray(); if(empty($ownerList)) { return ['list' => [], 'count' => 0]; } // 查询部门列表 $departmentList = DjDepartment::selectRaw('department_id, name as department_name') ->where('enable', 1) ->where('corpid', $corpid) ->get() ->keyBy('department_id') ->toArray(); $userList = DjUser::select(['user_id', 'name', 'open_user_id', 'avatar', 'department', 'status', 'is_active']) ->where('corpid', $corpid) ->where('enable', 1) ->where('name', 'like', '%' . $userName . '%') ->where('status', 1) ->whereIn('user_id', $ownerList) ->get() ->toArray(); $departmentData = []; $userData = []; $total = 0; foreach ($userList as $user) { $departmentIdList = explode(',', $user['department']); foreach ($departmentIdList as $departmentId) { if (!isset($departmentData[$departmentId])) { $departmentData[$departmentId] = isset($departmentList[$departmentId]) ? $departmentList[$departmentId] : []; } $user['department_list'][] = isset($departmentList[$departmentId]['department_name']) ? $departmentList[$departmentId]['department_name'] : ''; $departmentData[$departmentId]['user_list'][] = $user; if (!in_array($user['user_id'], $userData)) { $userData[] = $user['user_id']; $total += 1; } } } foreach($departmentData as $k=>$departmentInfo) { if(empty($departmentInfo['user_list'])) { unset($departmentData[$k]); } } return ['list' => array_values($departmentData), 'count' => $total]; } /** * 获取账号下各企微的群主列表 * */ public static function ownerListOfCompany($userName, $sysGroupId) { try { # 获取公司账号下绑定的企微 $corpIds = AdminManageCorp::where('sys_user_id', $sysGroupId)->where('is_delete', 0)->pluck('corpid'); $accountList = AuthorizeCorp::select('corpid', 'corp_name')->where('enable', 1) ->whereIn('id', $corpIds)->get(); if(empty($accountList)) return []; $corpidList = $accountList->pluck('corpid'); $ownerList = ChatGroup::select('corpid', 'owner')->whereIn('corpid', $corpidList)->where('status', 1) ->groupBy(['corpid', 'owner']) ->get(); $data = []; foreach ($accountList as $account) { $corpid = $account->corpid; $corpName = $account->corp_name; # 整理数据 $userIdList = $ownerList->where('corpid', $corpid)->pluck('owner'); $userList = DjUser::select(['user_id', 'name', 'avatar', 'status', 'is_active']) ->where('corpid', $corpid)->where('enable', 1)->where('status', 1) ->whereIn('user_id', $userIdList) ->get(); $data[] = [ 'corpid' => $corpid, 'corp_name' => $corpName, 'user_list' => $userList ]; } } catch (\Exception $e) { Log::logError('获取账号下各企微的群主列表出现异常', [ 'line' => $e->getLine(), 'msg' => $e->getTraceAsString() ], 'ownerListOfCompany'); return []; } return $data; } public static function ownerIndex($corpid, $userName) { $ownerList = ChatGroup::where('corpid', $corpid)->where('status', 1)->pluck('owner')->toArray(); if(empty($ownerList)) { return []; } $userList = DjUser::select(['user_id', 'name', 'avatar'])->where('corpid', $corpid) ->where('enable', 1)->where('name', 'like', '%' . $userName . '%') ->where('status', 1)->whereIn('user_id', $ownerList) ->get()->toArray(); return $userList; } /* * 检索框群列表 * */ public static function chatGroupList($corpid, $keyword, $startTime, $endTime, $owner, $isActive) { $groupList = ChatGroup::select(['chat_id', 'name', 'notice', 'owner', 'create_time']) ->where('corpid', $corpid) ->where(function($query) use ($keyword, $owner, $startTime, $endTime) { if($keyword) $query->where('name', 'like', '%'.$keyword.'%'); if($owner) $query->where('owner', $owner); if($startTime) $query->where('create_time', '>=', $startTime); if($endTime) $query->where('create_time', '<=', $endTime); }) ->where('enable', 1) ->where('status', 1) ->get(); # 提取群主 $ownerIdList = $groupList->isNotEmpty() ? array_column($groupList->toArray(), 'owner') : []; $ownerData = null; if(!empty($ownerIdList)) { $ownerIdList = array_unique($ownerIdList); $ownerData = DjUser::select(['user_id', 'name']) ->where('corpid', $corpid) ->whereIn('user_id', $ownerIdList) ->where('status', 1) ->get(); } foreach ($groupList as $key => $group) { $owner = $group->owner; $ownerInfo = !empty($ownerData) ? $ownerData->where('user_id', $owner)->first(): ''; $group->owner_name = isset($ownerInfo->name) ? $ownerInfo->name : ''; if($isActive && empty($group->owner_name)) { unset($groupList[$key]); } } return array_values($groupList->toArray()); } /** * 配置邀请入群规则 * */ public static function setInviteRule($ruleId, $params) { try { # 详细配置信息入dj_invite_chat_group_detail表 $configDetail = json_decode($params['invite_config'], true); if(empty($configDetail)) {return 4823;} $chatIdUsed = array_column($configDetail, 'chat_id'); # 判断选定的客服或者群主是否已经绑定了设备信息 $error = self::checkUserBindDevice($params, $chatIdUsed); if($error) return $error; if($ruleId) { $ruleInfo = InviteChatGroupRule::getInfo($ruleId); if(empty($ruleInfo)) {return 4821;} # 已经开启客户续拉配置的规则不允许再次编辑 if(1 == $ruleInfo->continuously_attract_groups) {return 4818;} } # 选择了持续拉取客户入群(首次设置) if(1 == $params['continuously_attract_groups']) { if(empty($params['upper_limit'])) {return 4819;} // 单次创建群聊上限必填 if($params['invite_type'] == 1) {return 4817;}// 仅支持定时邀请 } # 选择了客户补拉,仅执行当前客服群聊以及定时邀请 if(1 == $params['supplementary_invite']) { if($params['invite_type'] == 1) {return 4817;}// 仅支持定时邀请 // if($params['owner_type'] == 2) {return 4816;}// 仅支持当前客服群聊 } DB::beginTransaction(); # 计算下次邀请入群的时间点 $datetimeNow = date('Y-m-d H:i:s'); if($params['invite_type'] == 2) { // 定时邀请 $params['next_invite_time'] = CircleMassMsgService::getNextSendTime(1, $params['invite_time'], 0, $datetimeNow); if(1 == $params['supplementary_invite']) { $params['next_supplementary_invite_time'] = CircleMassMsgService::getNextSupplementaryInviteTime( 1, $params['supplementary_invite_time'], $params['supplementary_invite_cycle'], $datetimeNow); } } else { $params['next_invite_time'] = $datetimeNow; $params['next_supplementary_invite_time'] = $datetimeNow; } # 基础数据入dj_invite_chat_group_rule表 $errno = InviteChatGroupRule::editRule($ruleId, $params); if($errno) { DB::rollback(); return $errno; } # 禁用删除掉的配置 InviteChatGroupDetail::whereNotIn('chat_id', $chatIdUsed)->where('rule_id', $ruleId)->update(['enable' => 0]); foreach ($configDetail as $index=>$config) { $chatId = $config['chat_id'] ?? null; if(!$chatId) continue; $status = $config['status'] ?? 1; # 数据处理 InviteChatGroupDetail::updateOrCreate([ 'chat_id' => $chatId, 'rule_id' => $ruleId, 'corpid' => $params['corpid'], ], [ 'user_limit' => $config['user_limit'], 'status' => $status, 'sort' => $index, 'enable' => 1 ]); } DB::commit(); } catch (\Exception $e) { DB::rollback(); EmailQueue::rPush('邀请入群规则配置过程发生异常', $e->getTraceAsString(), ['xiaohua.hou@kuxuan-inc.com'], '猎羽'); Log::logError('邀请入群规则配置失败', [ 'rule_id' => $ruleId, 'params' => $params, 'line' => $e->getLine(), 'msg' => $e->getMessage() ], 'SetInviteRule'); return 4824; } return 0; } public static function checkSingleUserChatGroup($params, $chatIdList) { if(1 == $params['is_for_all']) { return 0; } $usersArr = explode(',', $params['users']); if(count($usersArr) > 1) { return 0; } # 查询选中的群聊信息 $pageSize = count($chatIdList); list($chatGroupInfoList, $count) = ChatGroup::getGroupListByCorp([$params['corpid']], null, $chatIdList , null, null, null, 1, $pageSize); # 提取群主id $ownerList = array_unique(array_column($chatGroupInfoList->toArray(), 'owner')); # 判断群主id与选择客服的关系 if(1 == $params['owner_type']) { if(count($ownerList) > 1 || count($ownerList) < 1 || $params['users'] != $ownerList[0]) { return 4828; } } else if(2 == $params['owner_type']) { if(in_array($params['users'], $ownerList)) { return 4829; } } return 0; } public static function checkUserBindDevice($params, $chatIdList) { $usersArr = explode(',', $params['users']); if(isset($params['owner_type']) && 2 == $params['owner_type']) { $pageSize = count($chatIdList); list($chatGroupInfoList, $count) = ChatGroup::getGroupListByCorp([$params['corpid']], null, $chatIdList , null, null, null, 1, $pageSize); # 提取群主id if($chatGroupInfoList->isNotEmpty()) { $ownerList = array_unique(array_column($chatGroupInfoList->toArray(), 'owner')); $usersArr = array_unique(array_merge($usersArr, $ownerList)); } } $userCount = count($usersArr); $deviceInfo = AndroidBindCorp::getDeviceIdListByUser($params['corpid'], $usersArr); if($deviceInfo->isNotEmpty()) { $deviceUserCount = count(array_unique(array_column($deviceInfo->toArray(), 'user_id'))); } else { $deviceUserCount = 0; } if($deviceUserCount < $userCount) { return 4820; } return 0; } /** * 获取邀请入群规则列表 * */ public static function getInviteRuleList($corpid, $userId, $sysGroupId, $keyword, $status, $page, $pageSize) { list($list, $count) = InviteChatGroupRule::inviteRuleList($corpid, $userId, $sysGroupId, $keyword, $status, $page, $pageSize); if(!$count) return [$list, $count]; # 获取操作人员的信息 $adminIdList = $list->pluck('admin_id'); $adminList = Users::select('id', 'name')->whereIn('id', $adminIdList)->get(); foreach ($list as $item) { # 操作人员信息处理 $userInfo = $adminList->where('id', $item->admin_id)->first(); $item->user_name = $userInfo->name ?? null; } return [$list, $count]; } /** * 邀请入群规则详情 * */ public static function inviteRuleDetail($ruleId, $sysGroupId) { $ruleInfo = InviteChatGroupRule::selectRaw("id as rule_id, title, is_for_all, users, join_type, status" .", corpid, customer_filter, filter_type, add_time_later, add_time_st, add_time_et, invite_type, invite_time" .", owner_type, announcement, gender, tag_screen_type, tag_list, pay_status, pay_num_min, pay_num_max, upper_limit" .", continuously_attract_groups, supplementary_invite_cycle, supplementary_invite, supplementary_invite_time," ."last_supplementary_invite_time, next_supplementary_invite_time") ->where('enable', 1)->where('sys_group_id', $sysGroupId) ->where('id', $ruleId)->first(); if(empty($ruleInfo)) return []; if(!$ruleInfo->is_for_all) $ruleInfo->users = explode(',', $ruleInfo->users); # 获取配置详情 $ruleDetail = InviteChatGroupDetail::select('chat_id', 'sort', 'user_limit', 'status') ->where('rule_id', $ruleId)->where('enable', 1) ->orderBy('sort')->get(); # 获取群信息 $chatIdList = $ruleDetail->pluck('chat_id'); $chatList = ChatGroup::select('chat_id', 'name')->whereIn('chat_id', $chatIdList)->where('corpid', $ruleInfo->corpid)->get(); foreach ($ruleDetail as $item) { # 获取群名称 $chatGroupInfo = $chatList->where('chat_id', $item->chat_id)->first(); $item->group_name = $chatGroupInfo->name ?? ''; } $ruleInfo->invite_config = $ruleDetail; return $ruleInfo; } /** * 删除邀请入群配置 * */ public static function inviteRuleDel($ruleId, $sysGroupId, $adminId) { try { DB::beginTransaction(); $isExist = InviteChatGroupRule::where('enable', 1)->where('sys_group_id', $sysGroupId) ->where('id', $ruleId)->exists(); if(!$isExist) { DB::rollback(); return 4821; } # 执行删除 $result = InviteChatGroupRule::where('id', $ruleId)->where('enable', 1)->where('sys_group_id', $sysGroupId) ->update(['enable' => 0, 'admin_id' => $adminId]); if(!$result) { DB::rollback(); return 4826; } InviteChatGroupDetail::where('rule_id', $ruleId)->where('enable', 1)->update(['enable' => 0]); DB::commit(); } catch (\Exception $e) { DB::rollback(); EmailQueue::rPush('删除邀请入群规则过程发生异常', $e->getTraceAsString(), ['xiaohua.hou@kuxuan-inc.com'], '猎羽'); Log::logError('删除邀请入群规则异常', [ 'rule_id' => $ruleId, 'line' => $e->getLine(), 'msg' => $e->getMessage() ], 'SetInviteRule'); return 4825; } return 0; } /** * 修改邀请入群规则状态 * */ public static function updateRuleStatus($ruleId, $adminId, $status, $sysGroupId) { # 验证规则是否存在 $inviteConfig = InviteChatGroupRule::where('enable', 1)->where('sys_group_id', $sysGroupId) ->where('id', $ruleId)->first(); if(empty($inviteConfig)) return 4821; # 变更规则状态和下次邀请入群时间 $datetimeNow = date('Y-m-d H:i:s'); $nextInviteTime = $datetimeNow; if($inviteConfig->invite_type == 2) { $nextInviteTime = CircleMassMsgService::getNextSendTime(1, $inviteConfig->invite_time, 0, $datetimeNow); } $result = InviteChatGroupRule::where('sys_group_id', $sysGroupId)->where('id', $ruleId) ->update(['status' => $status, 'admin_id' => $adminId, 'next_invite_time' => $nextInviteTime]); if(!$result) return 4827; return 0; } public static function resetSupplementaryInviteTime($ruleId, $adminId, $sysGroupId) { # 验证规则是否存在 $inviteConfig = InviteChatGroupRule::where('enable', 1)->where('sys_group_id', $sysGroupId) ->where('id', $ruleId)->first(); if(empty($inviteConfig)) return 4821;# 邀请入群配置不存在 if(1 != $inviteConfig->supplementary_invite) { return 4850;# 客户补拉未开启 } # 计算下次邀请入群的时间点 $datetimeNow = date('Y-m-d H:i:s'); $nextSupplementaryInviteTime = CircleMassMsgService::getNextSupplementaryInviteTime( 1, $inviteConfig->supplementary_invite_time, $inviteConfig->supplementary_invite_cycle, $datetimeNow); $result = InviteChatGroupRule::where('sys_group_id', $sysGroupId)->where('id', $ruleId) ->update(['last_supplementary_invite_time' => null, 'next_supplementary_invite_time' => $nextSupplementaryInviteTime , 'admin_id' => $adminId]); if(!$result) return 4851; return 0; } /** * 客户群数据--按客户群统计 * */ public static function chatGroupStatistic( $sysGroupId, $owner, $ownerList, $keyword, $corpid, $createTimeSt, $createTimeEt, $minMember, $maxMember, $page, $pageSize ) { $corpIds = AdminManageCorp::where('sys_user_id', $sysGroupId) ->where('is_delete', 0) ->pluck('corpid'); $corpList = AuthorizeCorp::select('corpid', 'corp_name')->whereIn('id', $corpIds) ->where(function($query) use($corpid) { if($corpid) $query->where('corpid', $corpid); }) ->where('enable', 1) ->get(); $corpidList = $corpList->pluck('corpid'); if(empty($corpidList)) return [[], 0]; # 根据多企微客服筛选获取对应的chat_id集合 if(!is_array($ownerList)) { $ownerList = json_decode($ownerList, true); } $chatIdList = []; if(!empty($ownerList)) { foreach ($ownerList as $ownerInfo) { $corpid = $ownerInfo['corpid'] ?? null; $senderList = $ownerInfo['user_list'] ?? null; $chatIds = ChatGroup::where('corpid', $corpid)->whereIn('owner', $senderList)->pluck('chat_id')->toArray(); $chatIdList = array_merge($chatIdList, $chatIds); } } # 根据筛选条件搜索群聊 if($keyword || $owner) { $chatIdList = ChatGroup::select('chat_id')->where('enable', 1)->whereIn('corpid', $corpList) ->where(function($query) use($keyword) { if($keyword) $query->where('name', 'like', '%'.$keyword.'%'); })->where(function($query) use($chatIdList) { if(!empty($chatIdList)) $query->whereIn('chat_id', $chatIdList); })->where(function($query) use($owner) { if($owner) $query->where('owner', $owner); })->pluck('chat_id')->toArray(); } # 统计客户群数据 list($groupList, $count) = ChatGroupMember::chatGroupMemberDataList( $chatIdList, $corpidList, $createTimeSt, $createTimeEt, $minMember, $maxMember, $page, $pageSize ); if(empty($groupList)) return [[], 0]; $chatIds = array_column($groupList, 'chat_id'); $chatGroupData = ChatGroup::getChatGroupInfo($chatIds); # 提取群主 $ownerIdList = $chatGroupData->pluck('owner')->unique(); # 获取群主信息 $ownerList = DjUser::select('user_id', 'name')->whereIn('user_id', $ownerIdList)->get(); foreach ($groupList as &$group) { $chatInfo = $chatGroupData->where('chat_id', $group['chat_id'])->first(); if(!isset($chatInfo->owner)) { continue; } # 处理群主信息 $ownerInfo = $ownerList->where('user_id', $chatInfo->owner)->first(); $group['owner_name'] = $ownerInfo->name ?? ''; # 处理群所属主体信息 $corpInfo = $corpList->where('corpid', $chatInfo->corpid)->first(); $group['corp_name'] = $corpInfo->corp_name ?? ''; # 获取群累计人数和累计退群人数 $group['member_total'] = $group['member_count'] + $group['member_quit_count']; $group['name'] = $chatInfo->name ?? ''; $group['create_time'] = $chatInfo->create_time ?? ''; $group['status'] = $chatInfo->status ?? ''; } return [$groupList, $count]; } /** * 获取系统组的群数据汇总 * */ public static function chatGroupCondition( $sysGroupId, $owner, $ownerList, $corpid, $createTimeSt, $createTimeEt, $minMember, $maxMember ) { $corpIds = AdminManageCorp::where('sys_user_id', $sysGroupId) ->where('is_delete', 0) ->pluck('corpid'); $corpList = AuthorizeCorp::select('corpid', 'corp_name')->whereIn('id', $corpIds) ->where(function($query) use($corpid) { if($corpid) $query->where('corpid', $corpid); }) ->where('enable', 1) ->get(); $corpidList = $corpList->pluck('corpid'); if(empty($corpidList)) return [[], 0]; $chatIdList = []; if(!empty($ownerList)) { foreach ($ownerList as $ownerInfo) { $corpid = $ownerInfo['corpid'] ?? null; $senderList = $ownerInfo['user_list'] ?? null; $chatIds = ChatGroup::where('corpid', $corpid)->whereIn('owner', $senderList)->pluck('chat_id')->toArray(); $chatIdList = array_merge($chatIdList, $chatIds); } } # 当有群人数条件筛选时,筛选符合条件的客户群id if(is_numeric($minMember) && is_numeric($maxMember)) { $chatIdList = ChatGroupMember::getChatGroupIdList( $chatIdList, $corpidList, $createTimeSt, $createTimeEt, $minMember, $maxMember ); } # 群聊总数 $queryModel = ChatGroup::whereIn('corpid', $corpidList) ->where(function($query) use($owner) { if($owner) $query->where('owner', $owner); }) ->where(function($query) use($chatIdList, $createTimeSt, $createTimeEt) { if($chatIdList) $query->whereIn('chat_id', $chatIdList); }) ->where('enable', 1); $groupCount = $queryModel->count(); # 今日新增群聊数 $groupTodayCount = $queryModel->where('create_time', '>=', date('Y-m-d') . ' 00:00:00')->count(); # 汇总群人数数据 list($joinTodayCount, $memberCount, $memberQuitCount) = ChatGroupMember::getChatGroupCondition( $corpidList, $chatIdList, $createTimeSt, $createTimeEt, $minMember, $maxMember ); # 整理数据 $data = [ 'group_count' => $groupCount, // 群聊总数 'member_total' => $memberQuitCount + $memberCount, // 累计群人数 'member_count' => $memberCount, // 留存群人数 'member_quit_count' => $memberQuitCount, // 流失群人数 'today_join_count' => $joinTodayCount, // 今日新增群人数 'today_create_count' => $groupTodayCount // 今日新增群聊数 ]; return $data; } public static function syncCorpChatGroup($corpid) { // 判断键是否存在 $redisKey = 'Playlet::getCorChatGroupList-'.$corpid; $value = RedisModel::get($redisKey); if(!empty($value)) { Log::logInfo('getCorChatGroupList 操作太快 request:'.json_encode(['corpid' => $corpid], JSON_UNESCAPED_UNICODE), [], 'interface'); return ['操作太快', 2007]; // return [['error' => '操作太快'], 2007]; } Log::logInfo('start', [time()], 'syncCorpChatGroup'); try{ # owner_filter参数处理 $userIdList = DjUser::select(['user_id'])->where('corpid', $corpid)->where('enable', 1) ->where('status', 1)->orderBy('id') ->distinct()->pluck('user_id')->toArray(); if(empty($userIdList)) { return ['待同步成员列表为空', 2014]; } # 获取队列长度,超过6000报警 $length = RedisModel::lLen(ChatGroup::CHAT_GROUP_DETAIL_RDS); if($length > ChatGroup::CHAT_GROUP_DETAIL_RDS_LIMIT) { EmailQueue::rPush('同步企微客户群队列长度超过阈值', json_encode([ 'redis_key' => ChatGroup::CHAT_GROUP_DETAIL_RDS, 'length' => $length ]), ['song.shen@kuxuan-inc.com'], '猎羽'); } $userCount = count($userIdList); # 需要截取的段数 $spliceCount = ceil($userCount / 100); for ($i=1; $i<=$spliceCount; $i++) { $start = ($i - 1) * 100; $ownerFilter = array_slice($userIdList, $start, 100); # 循环获取企微下的客户群列表 list($res, $code) = self::getCorpChatGroupList($ownerFilter, $corpid); if(0 != $code) return [$res, $code]; } } catch (\Exception $exception){ Log::logError('更新企微客户群列表异常', [ 'corpid' => $corpid, 'file' => $exception->getFile(), 'line' => $exception->getLine(), 'msg' => $exception->getMessage(), 'trace'=> $exception->getTraceAsString() ], 'syncCorpChatGroup'); return ['同步失败,请联系管理员', 500]; } RedisModel::set($redisKey, time()); RedisModel::expire($redisKey, 600); Log::logInfo('end', [time()], 'syncCorpChatGroup'); return ['同步中', 0]; } public static function getCorpChatGroupList($ownerFilter, $corpid) { $flag = true; $nextCursor = null; while($flag) { # 获取群基本信息 $responseData = QyCommon::getGroupChatList($corpid, $ownerFilter, 1000, $nextCursor); if(isset($responseData['errcode']) && $responseData['errcode']) { if(!in_array($responseData['errcode'], [701008])) { EmailQueue::rPush('群基本信息获取失败', json_encode($responseData, JSON_UNESCAPED_UNICODE) , ['song.shen@kuxuan-inc.com'], '群基本信息获取失败'); } Log::logError('群基本信息获取失败', [ 'corpid' => $corpid, 'ownerFilter' => $ownerFilter, 'cursor' => $nextCursor, 'data' => $responseData, ], 'GroupBasicInfo'); return ['群基本信息获取失败', 2015]; } $chatGroupList = $responseData['group_chat_list'] ?? []; # 翻页查询外部联系人所有的关联员工 if(!empty($responseData['next_cursor'])) { $nextCursor = $responseData['next_cursor']; } else { $flag = false; } foreach ($chatGroupList as $chatGroupInfo) { list($res, $code) = self::getCorpChatGroupDetail($corpid, $ownerFilter, $chatGroupInfo); if(0 != $code) return [$res, $code]; } } } public static function getCorpChatGroupDetail($corpid, $ownerFilter, $chatGroupInfo, $retry = 0) { # 将chat_id和status置入队列供客户群详情脚本消费 $chatId = $chatGroupInfo['chat_id'] ?? ''; $status = $chatGroupInfo['status'] ?? null; $result = RedisModel::lPush(ChatGroup::CHAT_GROUP_DETAIL_RDS, json_encode([ 'corpid' => $corpid, 'chat_id' => $chatId, 'status' => $status ])); if(!$result) { if($retry >= 3) { Log::logError('群基本信息解析入队列失败', [ 'corpid' => $corpid, 'ownerFilter' => $ownerFilter, 'data' => $chatGroupInfo, ], 'GroupBasicInfo'); return ['群基本信息解析入队列失败', 2016]; } else { $retry++; return self::getCorpChatGroupDetail($corpid, $ownerFilter, $chatGroupInfo, $retry); } } return ['处理成功', 0]; } public static function receiveCreateChatGroupData($deviceId, $corpid, $userName, $groupList) { # 验证企微id,客服名称以及客户群聊id的合法性 $corpInfo = AuthorizeCorp::getCorpInfo($corpid); if(empty($corpInfo)) { Log::logError('接收创建群聊任务-企微信息查询异常', [ 'corpid' => $corpid, 'device_id' => $deviceId, 'corp_info' => $corpInfo ], 'receiveCreateChatGroupData'); return ['企微id不合法', 1102]; } $userInfoList = DjUser::getUserBySearch(['corpid' => $corpid, 'user_name' => $userName]); if($userInfoList->isEmpty()) { Log::logError('接收创建群聊任务异常-客服信息查询异常', [ 'corpid' => $corpid, 'device_id' => $deviceId, 'user_name' => $userName, 'user_info' => $userInfoList ], 'receiveCreateChatGroupData'); } # 根据企微以及群主查询对应的设备信息 $dataList = []; $groupList = json_decode($groupList, 1); $ownerIdList = array_unique(array_column($groupList, 'owner')); $deviceInfoList = AndroidBindCorp::getDeviceIdListByUser($corpid, $ownerIdList); # 根据群聊id查询对应的群主,并按照群主对群聊进行分组处理 foreach($groupList as $groupInfo) { $deviceInfo = $deviceInfoList->where('corpid', $corpid)->where('user_id', $groupInfo['owner']) ->first(); $deviceId = $deviceInfo->device_id ?? null; if(empty($deviceId)) { Log::logError('接收创建群聊任务-群聊对应群主设备信息查询异常', [ 'corpid' => $corpid, 'device_id' => $deviceId, 'group_list' => $groupList, 'owner' => $groupInfo['owner'], 'device_info_list' => $deviceInfoList, 'device_info' => $deviceInfo ], 'receiveCreateChatGroupData'); continue; } $dataList[$deviceId][] = [ 'corpid' => $corpid, 'owner' => $groupInfo['owner'], 'groupName' => $groupInfo['groupName'], 'manager' => $userName, 'announcement' => $groupInfo['announcement'] ?? '', ]; } # 将待创建的群聊id,群聊名称以及管理员信息保存入待执行队列 foreach($dataList as $deviceId => $data) { $res = RedisModel::lPush(self::CREATE_GROUP_WAIT_RDS.'_'.$deviceId, json_encode([ 'companyName' => $corpInfo->corp_name, 'groupBeans' => $data, 'request_time'=> time(), ],256)); if(!$res) { Log::logError('接收创建群聊任务-创建群任务入缓存失败', [ 'corpid' => $corpid, 'device_id' => $deviceId, 'group_list' => $groupList, 'redis_key' => self::CREATE_GROUP_WAIT_RDS.'_'.$deviceId, 'redis_value' => json_encode([ 'companyName' => $corpInfo->corp_name, 'groupBeans' => $data ],256) ], 'receiveCreateChatGroupData'); return ['创建群任务入缓存失败', 500]; } } return ['', 0]; } public static function getCreateGroupTask($deviceId) { $response = RedisModel::rPop(self::CREATE_GROUP_WAIT_RDS.'_'.$deviceId); if(!empty($response)) { $responseData = json_decode($response, 1); $requestTime = $responseData['request_time'] ?? time(); if(time() - $requestTime >= 43200) { // 超过12个小时的任务不再下发 return self::getCreateGroupTask($deviceId); } return $responseData; } else { return [ 'companyName' => '', 'groupBeans' => [] ]; } } public static function setWelcomeTemplate($params, &$error) { $attachmentsArr = !is_array($params['attachments']) ? json_decode($params['attachments'], 1) : $params['attachments']; # 素材内容验证 $verifyRes = ChatGroupMassMsgService::welcomeTemplateAttachmentVerify($attachmentsArr); if($verifyRes) { Log::logError('添加客户入群欢迎语素材接口数据格式不合法', [ 'corpid' => $params['corpid'], 'text' => $params['content'], 'attachments' => $attachmentsArr, 'errcode' => $verifyRes ], 'setWelcomeTemplateTrace'); $error = $verifyRes; return ; } # 素材ID替换 $newAttachments = MaterialService::chatGroupWelcomeTemplateMediaIdReplace($attachmentsArr, $params['corpid'], 'setWelcomeTemplateTrace'); # 处理包含链接,H5推广链接的附件信息 $newAttachments = MaterialService::promoteAttachments($params['corpid'], $newAttachments, 'setWelcomeTemplate'); if(!empty($params['template_id'])) { $templateId = $params['template_id']; # 请求企微创建素材接口 $responseData = QyCommon::editChatGroupWelcomeTemplate($params['corpid'], $templateId, $params['content'], $newAttachments); if(isset($responseData['errcode']) && $responseData['errcode']) { $errMsg = $responseData['errmsg'] ?? $responseData['errcode']; EmailQueue::rPush('编辑客户入群欢迎语素材接口返回错误码', $errMsg, ['song.shen@kuxuan-inc.com'], '猎羽'); Log::logError('编辑客户入群欢迎语素材接口返回错误码', [ 'corpid' => $params['corpid'], 'template_id' => $templateId, 'text' => $params['content'], 'attachments' => $newAttachments, 'response' => $responseData ], 'setWelcomeTemplateTrace'); $error = 5240; return ; } } else { # 请求企微创建素材接口 $responseData = QyCommon::addChatGroupWelcomeTemplate($params['corpid'], $params['content'], $newAttachments); if(isset($responseData['errcode']) && $responseData['errcode']) { $errMsg = $responseData['errmsg'] ?? $responseData['errcode']; EmailQueue::rPush('添加客户入群欢迎语素材接口返回错误码', $errMsg, ['song.shen@kuxuan-inc.com'], '猎羽'); Log::logError('添加客户入群欢迎语素材接口返回错误码', [ 'corpid' => $params['corpid'], 'text' => $params['content'], 'attachments' => $newAttachments, 'response' => $responseData ], 'setWelcomeTemplateTrace'); $error = 5240; return ; } $templateId = $responseData['template_id'] ?? ''; } # 将数据保存 $res = ChatGroupWelcomeTemplate::saveData($params['sys_group_id'], $params['admin_id'], $params['corpid'] , $params['name'], $params['content'], $attachmentsArr, $templateId); if(!$res) { $error = 500; return ; } } public static function welcomeTemplateList($corpid, $sysGroupId, $keyword, $creatorId, $page, $pageSize) { list($list, $count) = ChatGroupWelcomeTemplate::getList($corpid, $sysGroupId, $keyword, $creatorId, $page, $pageSize); # 获取创建人信息 $adminIdList = array_unique(array_column($list->toArray(),'admin_id')); $adminData = Users::query()->whereIn('id', $adminIdList)->select(['id','name'])->get(); foreach($list as $info) { $adminInfo = $adminData->where('id', $info->admin_id)->first(); $info->creator = isset($adminInfo->name) ? $adminInfo->name : ''; # 文本内容中客户昵称信息替换 $info->content = str_replace('%NICKNAME%', '「客户昵称」', $info->content); # 通过素材ID获取素材链接 $attachments = json_decode($info->attachments, 1); if(isset($attachments['image']['media_id'])) { $materialInfo = Material::select(['oss_url']) ->where('id', $attachments['image']['media_id'])->where('type', 1)->first(); $attachments['image']['media_url'] = $materialInfo->oss_url ?? ''; } if(isset($attachments['miniprogram']['pic_media_id'])) { $materialInfo = Material::select(['oss_url']) ->where('id', $attachments['miniprogram']['pic_media_id'])->where('type', 1)->first(); $attachments['miniprogram']['pic_media_url'] = $materialInfo->oss_url ?? ''; } if(isset($attachments['file']['media_id'])) { $materialInfo = Material::select(['oss_url']) ->where('id', $attachments['file']['media_id'])->where('type', 4)->first(); $attachments['file']['media_url'] = $materialInfo->oss_url ?? ''; } if(isset($attachments['video']['media_id'])) { $materialInfo = Material::select(['oss_url']) ->where('id', $attachments['video']['media_id'])->where('type', 3)->first(); $attachments['video']['media_url'] = $materialInfo->oss_url ?? ''; } $info->attachments = $attachments; } return [$list, $count]; } public static function welcomeTemplateDetail($sysGroupId, $corpid, $templateId) { # 获取素材信息 $templateInfo = ChatGroupWelcomeTemplate::getInfo($sysGroupId, $corpid, $templateId); $templateInfo = json_decode(json_encode($templateInfo), 1); if(empty($templateInfo)) { return []; } # 素材信息格式化 # 获取创建人信息 $adminInfo = Users::query()->where('id', $templateInfo['admin_id'])->select(['id','name'])->first(); $templateInfo['creator'] = isset($adminInfo->name) ? $adminInfo->name : ''; # 通过素材ID获取素材链接 $attachments = json_decode($templateInfo['attachments'], 1); if(isset($attachments['image']['media_id'])) { $materialInfo = Material::select(['oss_url']) ->where('id', $attachments['image']['media_id'])->where('type', 1)->first(); $attachments['image']['media_url'] = $materialInfo->oss_url ?? ''; } if(isset($attachments['miniprogram']['pic_media_id'])) { $materialInfo = Material::select(['oss_url']) ->where('id', $attachments['miniprogram']['pic_media_id'])->where('type', 1)->first(); $attachments['miniprogram']['pic_media_url'] = $materialInfo->oss_url ?? ''; } if(isset($attachments['file']['media_id'])) { $materialInfo = Material::select(['oss_url']) ->where('id', $attachments['file']['media_id'])->where('type', 4)->first(); $attachments['file']['media_url'] = $materialInfo->oss_url ?? ''; } if(isset($attachments['video']['media_id'])) { $materialInfo = Material::select(['oss_url']) ->where('id', $attachments['video']['media_id'])->where('type', 3)->first(); $attachments['video']['media_url'] = $materialInfo->oss_url ?? ''; } $templateInfo['attachments'] = $attachments; return $templateInfo; } public static function delWelcomeTemplate($sysGroupId, $corpid, $templateId, &$error) { # 请求企微创建素材接口 $responseData = QyCommon::delChatGroupWelcomeTemplate($corpid, $templateId); if(isset($responseData['errcode']) && $responseData['errcode']) { $errMsg = $responseData['errmsg'] ?? $responseData['errcode']; EmailQueue::rPush('删除客户入群欢迎语素材接口返回错误码', $errMsg, ['song.shen@kuxuan-inc.com'], '猎羽'); Log::logError('删除客户入群欢迎语素材接口返回错误码', [ 'sys_group_id' => $sysGroupId, 'corpid' => $corpid, 'template_id' => $templateId, 'response' => $responseData ], 'delWelcomeTemplateTrace'); $error = 5241; return ; } $res = ChatGroupWelcomeTemplate::updateStatus($sysGroupId, $corpid, $templateId); if(!$res) { $error = 500; return ; } } public static function setForwardChatGroupCreateRule($ruleId, $params) { try { if(1 == $params['is_for_all']) { $params['users'] = ''; # 判断当前企微是否有绑定设备客服 list($bindUserList, $bindUserCount) = AndroidBindCorp::getDeviceList(null, $params['corpid'], null, $params['sys_group_id'], 1, 20); if(0 == $bindUserCount) return 4857;// 当前企微下已绑定设备的客服列表为空 } else { # 判断选定的群主是否已经绑定了设备信息 $error = self::checkUserBindDevice($params, null); if($error > 0) return 4856;// 所选群主含未绑定设备客服 } if($ruleId) { $ruleInfo = ForwardChatGroupRule::getInfo($ruleId); if(empty($ruleInfo)) {return 4852;}// 创建客户群规则配置不存在 } DB::beginTransaction(); # 基础数据入dj_invite_chat_group_rule表 $errno = ForwardChatGroupRule::editRule($ruleId, $params); if($errno) { DB::rollback(); return $errno; } DB::commit(); } catch (\Exception $e) { DB::rollback(); EmailQueue::rPush('转发消息客服入群规则配置过程发生异常', $e->getTraceAsString(), ['song.shen@kuxuan-inc.com'], '猎羽'); Log::logError('转发消息客服入群规则配置失败', [ 'rule_id' => $ruleId, 'params' => $params, 'line' => $e->getLine(), 'msg' => $e->getMessage() ], 'setForwardChatGroupCreateRule'); return 4853; } return 0; } public static function forwardChatGroupCreateRuleList($corpid, $userId, $sysGroupId, $keyword, $status, $page, $pageSize) { list($list, $count) = ForwardChatGroupRule::ruleList($corpid, $userId, $sysGroupId, $keyword, $status, $page, $pageSize); if(!$count) return [$list, $count]; # 获取操作人员的信息 $adminIdList = $list->pluck('admin_id'); $adminList = Users::query()->select('id', 'name')->whereIn('id', $adminIdList)->get(); foreach ($list as $item) { # 操作人员信息处理 $adminInfo = $adminList->where('id', $item->admin_id)->first(); $item->admin_name = $adminInfo->name ?? null; // $item->forward_users = json_decode($item->forward_users, 1); $item->users = !empty($item->users) ? explode(',', $item->users) : []; } return [$list, $count]; } public static function forwardChatGroupCreateRuleDetail($ruleId) { $ruleInfo = ForwardChatGroupRule::getInfo($ruleId); if(empty($ruleInfo)) return []; $ruleInfo->users = !empty($ruleInfo->users) ? explode(',', $ruleInfo->users) : []; return $ruleInfo; } public static function forwardChatGroupCreateRuleDelete($ruleId, $sysGroupId, $adminId) { try { DB::beginTransaction(); $isExist = ForwardChatGroupRule::getInfo($ruleId); if(empty($isExist)) { DB::rollback(); return 4852; } # 执行删除 $result = ForwardChatGroupRule::delRule($ruleId, $adminId); if(!$result) { DB::rollback(); return 4854; } DB::commit(); } catch (\Exception $e) { DB::rollback(); EmailQueue::rPush('删除转发消息客服入群规则过程发生异常', $e->getTraceAsString(), ['song.shen@kuxuan-inc.com'], '猎羽'); Log::logError('删除转发消息客服入群规则异常', [ 'rule_id' => $ruleId, 'line' => $e->getLine(), 'msg' => $e->getMessage() ], 'forwardChatGroupCreateRuleDelete'); return 4854; } return 0; } public static function forwardChatGroupCreateRuleChangeStatus($ruleId, $adminId, $status, $sysGroupId) { # 验证规则是否存在 $inviteConfig = ForwardChatGroupRule::getInfo($ruleId); if(empty($inviteConfig)) return 4852; $update = ['status' => $status, 'admin_id' => $adminId]; $result = ForwardChatGroupRule::updateStatus($ruleId, $sysGroupId, $update); if(!$result) return 4855; return 0; } }