request->isPost()) { return json(['code' => 400, 'msg' => '请求方式错误']); } // 获取登录用户信息 $userInfo = request()->userInfo; // 获取请求参数 $param = $this->request->post(); // 根据业务默认值补全参数 if ( isset($param['type']) && intval($param['type']) === self::TYPE_GROUP_PUSH ) { if (empty($param['startTime'])) { $param['startTime'] = '09:00'; } if (empty($param['endTime'])) { $param['endTime'] = '21:00'; } } // 验证数据 $validate = new WorkbenchValidate; if (!$validate->scene('create')->check($param)) { return json(['code' => 400, 'msg' => $validate->getError()]); } Db::startTrans(); try { // 创建工作台基本信息 $workbench = new Workbench; $workbench->name = $param['name']; $workbench->type = $param['type']; $workbench->status = !empty($param['status']) ? 1 : 0; $workbench->autoStart = !empty($param['autoStart']) ? 1 : 0; $workbench->userId = $userInfo['id']; $workbench->companyId = $userInfo['companyId']; $workbench->createTime = time(); $workbench->updateTime = time(); $workbench->save(); // 根据类型创建对应的配置 switch ($param['type']) { case self::TYPE_AUTO_LIKE: // 自动点赞 $config = new WorkbenchAutoLike; $config->workbenchId = $workbench->id; $config->interval = $param['interval']; $config->maxLikes = $param['maxLikes']; $config->startTime = $param['startTime']; $config->endTime = $param['endTime']; $config->contentTypes = json_encode($param['contentTypes']); $config->devices = json_encode($param['deviceGroups']); $config->friends = json_encode($param['wechatFriends']); // $config->targetGroups = json_encode($param['targetGroups']); // $config->tagOperator = $param['tagOperator']; $config->friendMaxLikes = $param['friendMaxLikes']; $config->friendTags = $param['friendTags']; $config->enableFriendTags = $param['enableFriendTags']; $config->createTime = time(); $config->updateTime = time(); $config->save(); break; case self::TYPE_MOMENTS_SYNC: // 朋友圈同步 $config = new WorkbenchMomentsSync; $config->workbenchId = $workbench->id; $config->syncInterval = $param['syncInterval']; $config->syncCount = $param['syncCount']; $config->syncType = $param['syncType']; $config->startTime = $param['startTime']; $config->endTime = $param['endTime']; $config->accountType = $param['accountType']; $config->devices = json_encode($param['deviceGroups']); $config->contentLibraries = json_encode($param['contentGroups'] ?? []); $config->createTime = time(); $config->updateTime = time(); $config->save(); break; case self::TYPE_GROUP_PUSH: // 群消息推送 $ownerWechatIds = $this->normalizeOwnerWechatIds($param['ownerWechatIds'] ?? []); $groupPushData = $this->prepareGroupPushData($param, $ownerWechatIds); $groupPushData['workbenchId'] = $workbench->id; $groupPushData['createTime'] = time(); $groupPushData['updateTime'] = time(); $config = new WorkbenchGroupPush; $config->save($groupPushData); break; case self::TYPE_GROUP_CREATE: // 自动建群 $config = new WorkbenchGroupCreate; $config->workbenchId = $workbench->id; $config->planType = !empty($param['planType']) ? $param['planType'] : 0; $config->executorId = !empty($param['executorId']) ? $param['executorId'] : 0; $config->devices = json_encode($param['deviceGroups'] ?? [], JSON_UNESCAPED_UNICODE); $config->startTime = $param['startTime'] ?? ''; $config->endTime = $param['endTime'] ?? ''; $config->groupSizeMin = intval($param['groupSizeMin'] ?? 3); $config->groupSizeMax = intval($param['groupSizeMax'] ?? 38); $config->maxGroupsPerDay = intval($param['maxGroupsPerDay'] ?? 20); $config->groupNameTemplate = $param['groupNameTemplate'] ?? ''; $config->groupDescription = $param['groupDescription'] ?? ''; $config->poolGroups = json_encode($param['poolGroups'] ?? [], JSON_UNESCAPED_UNICODE); $config->wechatGroups = json_encode($param['wechatGroups'] ?? [], JSON_UNESCAPED_UNICODE); // 处理群管理员:如果启用了群管理员且有指定管理员,则保存到admins字段 $admins = []; if (!empty($param['groupAdminEnabled']) && !empty($param['groupAdminWechatId'])) { // 如果groupAdminWechatId是数组,取第一个;如果是单个值,直接使用 $adminWechatId = is_array($param['groupAdminWechatId']) ? $param['groupAdminWechatId'][0] : $param['groupAdminWechatId']; // 如果是好友ID,直接添加到admins;如果是wechatId,需要转换为好友ID if (is_numeric($adminWechatId)) { $admins[] = intval($adminWechatId); } else { // 如果是wechatId字符串,需要查询对应的好友ID $friend = Db::table('s2_wechat_friend')->where('wechatId', $adminWechatId)->find(); if ($friend) { $admins[] = intval($friend['id']); } } } // 如果传入了admins参数,优先使用(兼容旧逻辑) if (!empty($param['admins']) && is_array($param['admins'])) { $admins = array_merge($admins, $param['admins']); } $config->admins = json_encode(array_unique($admins), JSON_UNESCAPED_UNICODE); $config->fixedWechatIds = json_encode($param['fixedWechatIds'] ?? [], JSON_UNESCAPED_UNICODE); $config->createTime = time(); $config->updateTime = time(); $config->save(); break; case self::TYPE_TRAFFIC_DISTRIBUTION: // 流量分发 $config = new WorkbenchTrafficConfig; $config->workbenchId = $workbench->id; $config->distributeType = $param['distributeType']; $config->maxPerDay = $param['maxPerDay']; $config->timeType = $param['timeType']; $config->startTime = $param['startTime']; $config->endTime = $param['endTime']; $config->devices = json_encode($param['deviceGroups'], JSON_UNESCAPED_UNICODE); $config->pools = json_encode($param['poolGroups'], JSON_UNESCAPED_UNICODE); $config->account = json_encode($param['accountGroups'], JSON_UNESCAPED_UNICODE); $config->createTime = time(); $config->updateTime = time(); $config->save(); break; case self::TYPE_IMPORT_CONTACT: //联系人导入 $config = new WorkbenchImportContact; $config->workbenchId = $workbench->id; $config->devices = json_encode($param['deviceGroups'], JSON_UNESCAPED_UNICODE); $config->pools = json_encode($param['poolGroups'], JSON_UNESCAPED_UNICODE); $config->num = $param['num']; $config->clearContact = $param['clearContact']; $config->remark = $param['remark']; $config->startTime = $param['startTime']; $config->endTime = $param['endTime']; $config->createTime = time(); $config->save(); break; } Db::commit(); return json(['code' => 200, 'msg' => '创建成功', 'data' => ['id' => $workbench->id]]); } catch (\Exception $e) { Db::rollback(); return json(['code' => 500, 'msg' => '创建失败:' . $e->getMessage()]); } } /** * 获取工作台列表 * @return \think\response\Json */ public function getList() { $page = $this->request->param('page', 1); $limit = $this->request->param('limit', 10); $type = $this->request->param('type', ''); $keyword = $this->request->param('keyword', ''); $where = [ ['companyId', '=', $this->request->userInfo['companyId']], ['isDel', '=', 0] ]; if (empty($this->request->userInfo['isAdmin'])) { $where[] = ['userId', '=', $this->request->userInfo['id']]; } // 添加类型筛选 if ($type !== '') { $where[] = ['type', '=', $type]; } // 添加名称模糊搜索 if ($keyword !== '') { $where[] = ['name', 'like', '%' . $keyword . '%']; } // 定义关联关系 $with = [ 'autoLike' => function ($query) { $query->field('workbenchId,interval,maxLikes,startTime,endTime,contentTypes,devices,friends'); }, 'momentsSync' => function ($query) { $query->field('workbenchId,syncInterval,syncCount,syncType,startTime,endTime,accountType,devices,contentLibraries'); }, 'trafficConfig' => function ($query) { $query->field('workbenchId,distributeType,maxPerDay,timeType,startTime,endTime,devices,pools,account'); }, 'groupPush' => function ($query) { $query->field('workbenchId,pushType,targetType,groupPushSubType,startTime,endTime,maxPerDay,pushOrder,isLoop,status,groups,friends,ownerWechatIds,trafficPools,contentLibraries,friendIntervalMin,friendIntervalMax,messageIntervalMin,messageIntervalMax,isRandomTemplate,postPushTags,announcementContent,enableAiRewrite,aiRewritePrompt'); }, 'groupCreate' => function ($query) { $query->field('workbenchId,devices,startTime,endTime,groupSizeMin,groupSizeMax,maxGroupsPerDay,groupNameTemplate,groupDescription,poolGroups,wechatGroups,admins'); }, 'importContact' => function ($query) { $query->field('workbenchId,devices,pools,num,remarkType,remark,clearContact,startTime,endTime'); }, 'user' => function ($query) { $query->field('id,username'); } ]; $list = Workbench::where($where) ->with($with) ->field('id,companyId,name,type,status,autoStart,userId,createTime,updateTime') ->order('id', 'desc') ->page($page, $limit) ->select() ->each(function ($item) { // 处理配置信息 switch ($item->type) { case self::TYPE_AUTO_LIKE: if (!empty($item->autoLike)) { $item->config = $item->autoLike; $item->config->devices = json_decode($item->config->devices, true); $item->config->contentTypes = json_decode($item->config->contentTypes, true); $item->config->friends = json_decode($item->config->friends, true); // 添加今日点赞数 $startTime = strtotime(date('Y-m-d') . ' 00:00:00'); $endTime = strtotime(date('Y-m-d') . ' 23:59:59'); $todayLikeCount = Db::name('workbench_auto_like_item') ->where('workbenchId', $item->id) ->whereTime('createTime', 'between', [$startTime, $endTime]) ->count(); // 添加总点赞数 $totalLikeCount = Db::name('workbench_auto_like_item') ->where('workbenchId', $item->id) ->count(); $item->config->todayLikeCount = $todayLikeCount; $item->config->totalLikeCount = $totalLikeCount; } unset($item->autoLike, $item->auto_like); break; case self::TYPE_MOMENTS_SYNC: if (!empty($item->momentsSync)) { $item->config = $item->momentsSync; $item->config->devices = json_decode($item->config->devices, true); $item->config->contentGroups = json_decode($item->config->contentLibraries, true); //同步记录 $sendNum = Db::name('workbench_moments_sync_item')->where(['workbenchId' => $item->id])->count(); $item->syncCount = $sendNum; $lastTime = Db::name('workbench_moments_sync_item')->where(['workbenchId' => $item->id])->order('id DESC')->value('createTime'); $item->lastSyncTime = !empty($lastTime) ? date('Y-m-d H:i', $lastTime) : '--'; // 获取内容库名称 if (!empty($item->config->contentGroups)) { $libraryNames = ContentLibrary::where('id', 'in', $item->config->contentGroups)->select(); $item->config->contentGroupsOptions = $libraryNames; } else { $item->config->contentGroupsOptions = []; } } unset($item->momentsSync, $item->moments_sync, $item->config->contentLibraries); break; case self::TYPE_GROUP_PUSH: if (!empty($item->groupPush)) { $item->config = $item->groupPush; $item->config->pushType = $item->config->pushType; $item->config->targetType = isset($item->config->targetType) ? intval($item->config->targetType) : 1; // 默认1=群推送 $item->config->groupPushSubType = isset($item->config->groupPushSubType) ? intval($item->config->groupPushSubType) : 1; // 默认1=群群发 $item->config->startTime = $item->config->startTime; $item->config->endTime = $item->config->endTime; $item->config->maxPerDay = $item->config->maxPerDay; $item->config->pushOrder = $item->config->pushOrder; $item->config->isLoop = $item->config->isLoop; $item->config->status = $item->config->status; $item->config->ownerWechatIds = json_decode($item->config->ownerWechatIds ?? '[]', true) ?: []; // 根据targetType解析不同的数据 if ($item->config->targetType == 1) { // 群推送 $item->config->wechatGroups = json_decode($item->config->groups, true) ?: []; $item->config->wechatFriends = []; // 群推送不需要devices字段 // 群公告相关字段 if ($item->config->groupPushSubType == 2) { $item->config->announcementContent = isset($item->config->announcementContent) ? $item->config->announcementContent : ''; $item->config->enableAiRewrite = isset($item->config->enableAiRewrite) ? intval($item->config->enableAiRewrite) : 0; $item->config->aiRewritePrompt = isset($item->config->aiRewritePrompt) ? $item->config->aiRewritePrompt : ''; } $item->config->trafficPools = []; } else { // 好友推送 $item->config->wechatFriends = json_decode($item->config->friends, true) ?: []; $item->config->wechatGroups = []; $item->config->trafficPools = json_decode($item->config->trafficPools ?? '[]', true) ?: []; } $item->config->contentLibraries = json_decode($item->config->contentLibraries, true); $item->config->postPushTags = json_decode($item->config->postPushTags ?? '[]', true) ?: []; $item->config->lastPushTime = ''; if (!empty($item->config->ownerWechatIds)) { $ownerWechatOptions = Db::name('wechat_account') ->whereIn('id', $item->config->ownerWechatIds) ->field('id,wechatId,nickName,avatar,alias') ->select(); $item->config->ownerWechatOptions = $ownerWechatOptions; } else { $item->config->ownerWechatOptions = []; } } unset($item->groupPush, $item->group_push); break; case self::TYPE_GROUP_CREATE: if (!empty($item->groupCreate)) { $item->config = $item->groupCreate; $item->config->devices = json_decode($item->config->devices, true); $item->config->poolGroups = json_decode($item->config->poolGroups, true); $item->config->wechatGroups = json_decode($item->config->wechatGroups, true); $item->config->admins = json_decode($item->config->admins ?? '[]', true) ?: []; // 处理群管理员相关字段 $item->config->groupAdminEnabled = !empty($item->config->admins) ? 1 : 0; if (!empty($item->config->admins)) { $adminOptions = Db::table('s2_wechat_friend')->alias('wf') ->join(['s2_wechat_account' => 'wa'], 'wa.id = wf.wechatAccountId', 'left') ->where('wf.id', 'in', $item->config->admins) ->order('wf.id', 'desc') ->field('wf.id,wf.wechatId,wf.nickname as friendName,wf.avatar as friendAvatar,wf.conRemark,wf.ownerWechatId,wa.nickName as accountName,wa.avatar as accountAvatar') ->select(); $item->config->adminsOptions = $adminOptions; // 如果有管理员,设置groupAdminWechatId为第一个管理员的ID(用于前端回显) $item->config->groupAdminWechatId = !empty($item->config->admins) ? $item->config->admins[0] : null; } else { $item->config->adminsOptions = []; $item->config->groupAdminWechatId = null; } } unset($item->groupCreate, $item->group_create); break; case self::TYPE_TRAFFIC_DISTRIBUTION: if (!empty($item->trafficConfig)) { $item->config = $item->trafficConfig; $item->config->devices = json_decode($item->config->devices, true); $item->config->poolGroups = json_decode($item->config->pools, true); $item->config->account = json_decode($item->config->account, true); $config_item = Db::name('workbench_traffic_config_item')->where(['workbenchId' => $item->id])->order('id DESC')->find(); $item->config->lastUpdated = !empty($config_item) ? date('Y-m-d H:i', $config_item['createTime']) : '--'; //统计 $labels = $item->config->poolGroups; $totalUsers = Db::table('s2_wechat_friend')->alias('wf') ->join(['s2_company_account' => 'sa'], 'sa.id = wf.accountId', 'left') ->join(['s2_wechat_account' => 'wa'], 'wa.id = wf.wechatAccountId', 'left') ->where([ ['wf.isDeleted', '=', 0], ['sa.departmentId', '=', $item->companyId] ]) ->whereIn('wa.currentDeviceId', $item->config->devices); if (!empty($labels) && count($labels) > 0) { $totalUsers = $totalUsers->where(function ($q) use ($labels) { foreach ($labels as $label) { $q->whereOrRaw("JSON_CONTAINS(wf.labels, '\"{$label}\"')"); } }); } $totalUsers = $totalUsers->count(); $totalAccounts = count($item->config->account); $dailyAverage = Db::name('workbench_traffic_config_item') ->where('workbenchId', $item->id) ->count(); $day = (time() - strtotime($item->createTime)) / 86400; $day = intval($day); if ($dailyAverage > 0 && $totalAccounts > 0 && $day > 0) { $dailyAverage = $dailyAverage / $totalAccounts / $day; } $item->config->total = [ 'dailyAverage' => intval($dailyAverage), 'totalAccounts' => $totalAccounts, 'deviceCount' => count($item->config->devices), 'poolCount' => !empty($item->config->poolGroups) ? count($item->config->poolGroups) : 'ALL', 'totalUsers' => $totalUsers >> 0 ]; } unset($item->trafficConfig, $item->traffic_config); break; case self::TYPE_IMPORT_CONTACT: if (!empty($item->importContact)) { $item->config = $item->importContact; $item->config->devices = json_decode($item->config->devices, true); $item->config->poolGroups = json_decode($item->config->pools, true); } unset($item->importContact, $item->import_contact); break; } // 添加创建人名称 $item['creatorName'] = $item->user ? $item->user->username : ''; unset($item['user']); // 移除关联数据 return $item; }); $total = Workbench::where($where)->count(); return json([ 'code' => 200, 'msg' => '获取成功', 'data' => [ 'list' => $list, 'total' => $total, 'page' => $page, 'limit' => $limit ] ]); } /** * 获取工作台详情 * @param int $id 工作台ID * @return \think\response\Json */ public function detail() { $id = $this->request->param('id', ''); if (empty($id)) { return json(['code' => 400, 'msg' => '参数错误']); } // 定义关联关系 $with = [ 'autoLike' => function ($query) { $query->field('workbenchId,interval,maxLikes,startTime,endTime,contentTypes,devices,friends,friendMaxLikes,friendTags,enableFriendTags'); }, 'momentsSync' => function ($query) { $query->field('workbenchId,syncInterval,syncCount,syncType,startTime,endTime,accountType,devices,contentLibraries'); }, 'trafficConfig' => function ($query) { $query->field('workbenchId,distributeType,maxPerDay,timeType,startTime,endTime,devices,pools,account'); }, 'groupPush' => function ($query) { $query->field('workbenchId,pushType,targetType,groupPushSubType,startTime,endTime,maxPerDay,pushOrder,isLoop,status,groups,friends,ownerWechatIds,trafficPools,contentLibraries,friendIntervalMin,friendIntervalMax,messageIntervalMin,messageIntervalMax,isRandomTemplate,postPushTags,announcementContent,enableAiRewrite,aiRewritePrompt'); }, 'groupCreate' => function ($query) { $query->field('workbenchId,devices,startTime,endTime,groupSizeMin,groupSizeMax,maxGroupsPerDay,groupNameTemplate,groupDescription,poolGroups,wechatGroups,admins'); }, 'importContact' => function ($query) { $query->field('workbenchId,devices,pools,num,remarkType,remark,clearContact,startTime,endTime'); }, ]; $where = [ ['id', '=', $id], ['companyId', '=', $this->request->userInfo['companyId']], ['isDel', '=', 0] ]; if (empty($this->request->userInfo['isAdmin'])) { $where[] = ['userId', '=', $this->request->userInfo['id']]; } $workbench = Workbench::where($where) ->field('id,name,type,status,autoStart,createTime,updateTime,companyId') ->with($with) ->find(); if (empty($workbench)) { return json(['code' => 404, 'msg' => '工作台不存在']); } // 处理配置信息 switch ($workbench->type) { //自动点赞 case self::TYPE_AUTO_LIKE: if (!empty($workbench->autoLike)) { $workbench->config = $workbench->autoLike; $workbench->config->deviceGroups = json_decode($workbench->config->devices, true); $workbench->config->wechatFriends = json_decode($workbench->config->friends, true); $workbench->config->targetType = 2; //$workbench->config->targetGroups = json_decode($workbench->config->targetGroups, true); $workbench->config->contentTypes = json_decode($workbench->config->contentTypes, true); // 添加今日点赞数 $startTime = strtotime(date('Y-m-d') . ' 00:00:00'); $endTime = strtotime(date('Y-m-d') . ' 23:59:59'); $todayLikeCount = Db::name('workbench_auto_like_item') ->where('workbenchId', $workbench->id) ->whereTime('createTime', 'between', [$startTime, $endTime]) ->count(); // 添加总点赞数 $totalLikeCount = Db::name('workbench_auto_like_item') ->where('workbenchId', $workbench->id) ->count(); $workbench->config->todayLikeCount = $todayLikeCount; $workbench->config->totalLikeCount = $totalLikeCount; unset($workbench->autoLike, $workbench->auto_like); } break; //自动同步朋友圈 case self::TYPE_MOMENTS_SYNC: if (!empty($workbench->momentsSync)) { $workbench->config = $workbench->momentsSync; $workbench->config->deviceGroups = json_decode($workbench->config->devices, true); $workbench->config->contentGroups = json_decode($workbench->config->contentLibraries, true); //同步记录 $sendNum = Db::name('workbench_moments_sync_item')->where(['workbenchId' => $workbench->id])->count(); $workbench->syncCount = $sendNum; $lastTime = Db::name('workbench_moments_sync_item')->where(['workbenchId' => $workbench->id])->order('id DESC')->value('createTime'); $workbench->lastSyncTime = !empty($lastTime) ? date('Y-m-d H:i', $lastTime) : '--'; unset($workbench->momentsSync, $workbench->moments_sync); } break; //群推送 case self::TYPE_GROUP_PUSH: if (!empty($workbench->groupPush)) { $workbench->config = $workbench->groupPush; $workbench->config->targetType = isset($workbench->config->targetType) ? intval($workbench->config->targetType) : 1; // 默认1=群推送 $workbench->config->groupPushSubType = isset($workbench->config->groupPushSubType) ? intval($workbench->config->groupPushSubType) : 1; // 默认1=群群发 $workbench->config->ownerWechatIds = json_decode($workbench->config->ownerWechatIds ?? '[]', true) ?: []; // 根据targetType解析不同的数据 if ($workbench->config->targetType == 1) { // 群推送 $workbench->config->wechatGroups = json_decode($workbench->config->groups, true) ?: []; $workbench->config->wechatFriends = []; $workbench->config->trafficPools = []; // 群推送不需要devices字段 // 群公告相关字段 if ($workbench->config->groupPushSubType == 2) { $workbench->config->announcementContent = isset($workbench->config->announcementContent) ? $workbench->config->announcementContent : ''; $workbench->config->enableAiRewrite = isset($workbench->config->enableAiRewrite) ? intval($workbench->config->enableAiRewrite) : 0; $workbench->config->aiRewritePrompt = isset($workbench->config->aiRewritePrompt) ? $workbench->config->aiRewritePrompt : ''; } } else { // 好友推送 $workbench->config->wechatFriends = json_decode($workbench->config->friends, true) ?: []; $workbench->config->wechatGroups = []; $workbench->config->trafficPools = json_decode($workbench->config->trafficPools ?? '[]', true) ?: []; } $workbench->config->contentLibraries = json_decode($workbench->config->contentLibraries, true); $workbench->config->postPushTags = json_decode($workbench->config->postPushTags ?? '[]', true) ?: []; unset($workbench->groupPush, $workbench->group_push); } break; //建群助手 case self::TYPE_GROUP_CREATE: if (!empty($workbench->groupCreate)) { $workbench->config = $workbench->groupCreate; $workbench->config->deviceGroups = json_decode($workbench->config->devices, true); $workbench->config->poolGroups = json_decode($workbench->config->poolGroups, true); $workbench->config->wechatGroups = json_decode($workbench->config->wechatGroups, true); $workbench->config->admins = json_decode($workbench->config->admins ?? '[]', true) ?: []; // 处理群管理员相关字段 $workbench->config->groupAdminEnabled = !empty($workbench->config->admins) ? 1 : 0; // 如果有管理员,设置groupAdminWechatId为第一个管理员的ID(用于前端回显) $workbench->config->groupAdminWechatId = !empty($workbench->config->admins) ? $workbench->config->admins[0] : null; // 统计已建群数(状态为成功且groupId不为空的记录,按groupId分组去重) $createdGroupsCount = Db::name('workbench_group_create_item') ->where('workbenchId', $workbench->id) ->where('status', 2) // STATUS_SUCCESS = 2 ->where('groupId', '<>', null) ->group('groupId') ->count(); // 统计总人数(该工作台的所有记录数) $totalMembersCount = Db::name('workbench_group_create_item') ->where('workbenchId', $workbench->id) ->count(); // 添加统计信息 $workbench->config->stats = [ 'createdGroupsCount' => $createdGroupsCount, 'totalMembersCount' => $totalMembersCount ]; unset($workbench->groupCreate, $workbench->group_create); } break; //流量分发 case self::TYPE_TRAFFIC_DISTRIBUTION: if (!empty($workbench->trafficConfig)) { $workbench->config = $workbench->trafficConfig; $workbench->config->deviceGroups = json_decode($workbench->config->devices, true); $workbench->config->accountGroups = json_decode($workbench->config->account, true); $workbench->config->poolGroups = json_decode($workbench->config->pools, true); $config_item = Db::name('workbench_traffic_config_item')->where(['workbenchId' => $workbench->id])->order('id DESC')->find(); $workbench->config->lastUpdated = !empty($config_item) ? date('Y-m-d H:i', $config_item['createTime']) : '--'; //统计 $labels = $workbench->config->poolGroups; $totalUsers = Db::table('s2_wechat_friend')->alias('wf') ->join(['s2_company_account' => 'sa'], 'sa.id = wf.accountId', 'left') ->join(['s2_wechat_account' => 'wa'], 'wa.id = wf.wechatAccountId', 'left') ->where([ ['wf.isDeleted', '=', 0], ['sa.departmentId', '=', $workbench->companyId] ]) ->whereIn('wa.currentDeviceId', $workbench->config->deviceGroups) ->field('wf.id,wf.wechatAccountId,wf.wechatId,wf.labels,sa.userName,wa.currentDeviceId as deviceId') ->where(function ($q) use ($labels) { foreach ($labels as $label) { $q->whereOrRaw("JSON_CONTAINS(wf.labels, '\"{$label}\"')"); } })->count(); $totalAccounts = Db::table('s2_company_account') ->alias('a') ->where(['a.departmentId' => $workbench->companyId, 'a.status' => 0]) ->whereNotLike('a.userName', '%_offline%') ->whereNotLike('a.userName', '%_delete%') ->group('a.id') ->count(); $dailyAverage = Db::name('workbench_traffic_config_item') ->where('workbenchId', $workbench->id) ->count(); $day = (time() - strtotime($workbench->createTime)) / 86400; $day = intval($day); if ($dailyAverage > 0) { $dailyAverage = $dailyAverage / $totalAccounts / $day; } $workbench->config->total = [ 'dailyAverage' => intval($dailyAverage), 'totalAccounts' => $totalAccounts, 'deviceCount' => count($workbench->config->deviceGroups), 'poolCount' => count($workbench->config->poolGroups), 'totalUsers' => $totalUsers >> 0 ]; unset($workbench->trafficConfig, $workbench->traffic_config); } break; case self::TYPE_IMPORT_CONTACT: if (!empty($workbench->importContact)) { $workbench->config = $workbench->importContact; $workbench->config->deviceGroups = json_decode($workbench->config->devices, true); $workbench->config->poolGroups = json_decode($workbench->config->pools, true); } unset($workbench->importContact, $workbench->import_contact); break; } unset( $workbench->autoLike, $workbench->momentsSync, $workbench->groupPush, $workbench->groupCreate, $workbench->config->devices, $workbench->config->friends, $workbench->config->groups, $workbench->config->contentLibraries, $workbench->config->account, ); //获取设备信息 if (!empty($workbench->config->deviceGroups)) { $deviceList = DeviceModel::alias('d') ->field([ 'd.id', 'd.imei', 'd.memo', 'd.alive', 'l.wechatId', 'a.nickname', 'a.alias', 'a.avatar', 'a.alias', '0 totalFriend' ]) ->leftJoin('device_wechat_login l', 'd.id = l.deviceId and l.alive =' . DeviceWechatLoginModel::ALIVE_WECHAT_ACTIVE . ' and l.companyId = d.companyId') ->leftJoin('wechat_account a', 'l.wechatId = a.wechatId') ->whereIn('d.id', $workbench->config->deviceGroups) ->order('d.id desc') ->select(); foreach ($deviceList as &$device) { $curstomer = WechatCustomerModel::field('friendShip')->where(['wechatId' => $device['wechatId']])->find(); $device['totalFriend'] = $curstomer->friendShip->totalFriend ?? 0; } unset($device); $workbench->config->deviceGroupsOptions = $deviceList; } else { $workbench->config->deviceGroupsOptions = []; } // 获取群(当targetType=1时) if (!empty($workbench->config->wechatGroups) && isset($workbench->config->targetType) && $workbench->config->targetType == 1) { $groupList = Db::name('wechat_group')->alias('wg') ->join('wechat_account wa', 'wa.wechatId = wg.ownerWechatId') ->where('wg.id', 'in', $workbench->config->wechatGroups) ->order('wg.id', 'desc') ->field('wg.id,wg.name as groupName,wg.ownerWechatId,wa.nickName,wa.avatar,wa.alias,wg.avatar as groupAvatar') ->select(); $workbench->config->wechatGroupsOptions = $groupList; } else { $workbench->config->wechatGroupsOptions = []; } // 获取好友(当targetType=2时) if (!empty($workbench->config->wechatFriends) && isset($workbench->config->targetType) && $workbench->config->targetType == 2) { $friendList = Db::table('s2_wechat_friend')->alias('wf') ->join(['s2_wechat_account' => 'wa'], 'wa.id = wf.wechatAccountId', 'left') ->where('wf.id', 'in', $workbench->config->wechatFriends) ->order('wf.id', 'desc') ->field('wf.id,wf.wechatId,wf.nickname as friendName,wf.avatar as friendAvatar,wf.conRemark,wf.ownerWechatId,wa.nickName as accountName,wa.avatar as accountAvatar') ->select(); $workbench->config->wechatFriendsOptions = $friendList; } else { $workbench->config->wechatFriendsOptions = []; } // 获取流量池(当targetType=2时) if (!empty($workbench->config->trafficPools) && isset($workbench->config->targetType) && $workbench->config->targetType == 2) { $poolList = Db::name('traffic_source_package')->alias('tsp') ->leftJoin('traffic_source_package_item tspi', 'tspi.packageId = tsp.id and tspi.isDel = 0') ->whereIn('tsp.id', $workbench->config->trafficPools) ->where('tsp.isDel', 0) ->whereIn('tsp.companyId', [$this->request->userInfo['companyId'], 0]) ->field('tsp.id,tsp.name,tsp.description,tsp.pic,COUNT(tspi.id) as itemCount') ->group('tsp.id') ->order('tsp.id', 'desc') ->select(); $workbench->config->trafficPoolsOptions = $poolList; } else { $workbench->config->trafficPoolsOptions = []; } // 获取内容库名称 if (!empty($workbench->config->contentGroups)) { $libraryNames = ContentLibrary::where('id', 'in', $workbench->config->contentGroups)->select(); $workbench->config->contentGroupsOptions = $libraryNames; } else { $workbench->config->contentGroupsOptions = []; } //账号 if (!empty($workbench->config->accountGroups)) { $account = Db::table('s2_company_account')->alias('a') ->where(['a.departmentId' => $this->request->userInfo['companyId'], 'a.status' => 0]) ->whereIn('a.id', $workbench->config->accountGroups) ->whereNotLike('a.userName', '%_offline%') ->whereNotLike('a.userName', '%_delete%') ->field('a.id,a.userName,a.realName,a.nickname,a.memo') ->select(); $workbench->config->accountGroupsOptions = $account; } else { $workbench->config->accountGroupsOptions = []; } if (!empty($workbench->config->poolGroups)) { $poolGroupsOptions = Db::name('traffic_source_package')->alias('tsp') ->join('traffic_source_package_item tspi', 'tspi.packageId=tsp.id', 'left') ->whereIn('tsp.companyId', [$this->request->userInfo['companyId'], 0]) ->whereIn('tsp.id', $workbench->config->poolGroups) ->field('tsp.id,tsp.name,tsp.description,tsp.createTime,count(tspi.id) as num') ->group('tsp.id') ->select(); $workbench->config->poolGroupsOptions = $poolGroupsOptions; } else { $workbench->config->poolGroupsOptions = []; } if (!empty($workbench->config->ownerWechatIds)) { $ownerWechatOptions = Db::name('wechat_account') ->whereIn('id', $workbench->config->ownerWechatIds) ->field('id,wechatId,nickName,avatar,alias') ->select(); $workbench->config->ownerWechatOptions = $ownerWechatOptions; } else { $workbench->config->ownerWechatOptions = []; } // 获取群组选项(自动建群) if ($workbench->type == self::TYPE_GROUP_CREATE && !empty($workbench->config->wechatGroups)) { // 分离数字ID(好友ID)和字符串ID(手动创建的群组) $friendIds = []; $manualGroupIds = []; foreach ($workbench->config->wechatGroups as $groupId) { if (is_numeric($groupId)) { $friendIds[] = intval($groupId); } else { $manualGroupIds[] = $groupId; } } $wechatGroupsOptions = []; // 查询好友信息(数字ID) if (!empty($friendIds)) { $friendList = Db::table('s2_wechat_friend')->alias('wf') ->join(['s2_wechat_account' => 'wa'], 'wa.id = wf.wechatAccountId', 'left') ->join(['s2_company_account' => 'ca'], 'ca.id = wf.accountId', 'left') ->where('wf.id', 'in', $friendIds) ->order('wf.id', 'desc') ->field('wf.id,wf.wechatId,wf.nickname,wf.avatar,wf.alias,wf.gender,wf.phone,wa.nickName as accountNickname,ca.userName as account,ca.realName as username,wf.createTime,wf.updateTime,wf.deleteTime,wf.ownerWechatId') ->select(); // 获取群主信息 foreach ($friendList as &$friend) { if (!empty($friend['ownerWechatId'])) { $owner = Db::name('wechat_account') ->where('wechatId', $friend['ownerWechatId']) ->field('nickName,alias') ->find(); $friend['ownerNickname'] = $owner['nickName'] ?? ''; $friend['ownerAlias'] = $owner['alias'] ?? ''; } else { $friend['ownerNickname'] = ''; $friend['ownerAlias'] = ''; } $friend['isManual'] = ''; // 格式化时间 $friend['createTime'] = !empty($friend['createTime']) ? date('Y-m-d H:i:s', $friend['createTime']) : ''; $friend['updateTime'] = !empty($friend['updateTime']) ? date('Y-m-d H:i:s', $friend['updateTime']) : ''; $friend['deleteTime'] = !empty($friend['deleteTime']) ? date('Y-m-d H:i:s', $friend['deleteTime']) : ''; } unset($friend); $wechatGroupsOptions = array_merge($wechatGroupsOptions, $friendList); } // 处理手动创建的群组(字符串ID) if (!empty($manualGroupIds)) { foreach ($manualGroupIds as $groupId) { // 手动创建的群组,只返回基本信息 $wechatGroupsOptions[] = [ 'id' => $groupId, 'wechatId' => $groupId, 'nickname' => $groupId, 'avatar' => '', 'isManual' => 1 ]; } } $workbench->config->wechatGroupsOptions = $wechatGroupsOptions; } else { $workbench->config->wechatGroupsOptions = []; } // 获取管理员选项(自动建群) if ($workbench->type == self::TYPE_GROUP_CREATE && !empty($workbench->config->admins)) { $adminOptions = Db::table('s2_wechat_friend')->alias('wf') ->join(['s2_wechat_account' => 'wa'], 'wa.id = wf.wechatAccountId', 'left') ->where('wf.id', 'in', $workbench->config->admins) ->order('wf.id', 'desc') ->field('wf.id,wf.wechatId,wf.nickname as friendName,wf.avatar as friendAvatar,wf.conRemark,wf.ownerWechatId,wa.nickName as accountName,wa.avatar as accountAvatar') ->select(); $workbench->config->adminsOptions = $adminOptions; } else { $workbench->config->adminsOptions = []; } return json(['code' => 200, 'msg' => '获取成功', 'data' => $workbench]); } /** * 更新工作台 * @return \think\response\Json */ public function update() { if (!$this->request->isPost()) { return json(['code' => 400, 'msg' => '请求方式错误']); } // 获取请求参数 $param = $this->request->post(); // 验证数据 $validate = new WorkbenchValidate; if (!$validate->scene('update')->check($param)) { return json(['code' => 400, 'msg' => $validate->getError()]); } $where = [ ['id', '=', $param['id']], ['companyId', '=', $this->request->userInfo['companyId']], ['isDel', '=', 0] ]; if (empty($this->request->userInfo['isAdmin'])) { $where[] = ['userId', '=', $this->request->userInfo['id']]; } // 查询工作台是否存在 $workbench = Workbench::where($where)->find(); if (!$workbench) { return json(['code' => 404, 'msg' => '工作台不存在']); } Db::startTrans(); try { // 更新工作台基本信息 $workbench->name = $param['name']; $workbench->status = !empty($param['status']) ? 1 : 0; $workbench->autoStart = !empty($param['autoStart']) ? 1 : 0; $workbench->updateTime = time(); $workbench->save(); // 根据类型更新对应的配置 switch ($workbench->type) { case self::TYPE_AUTO_LIKE: $config = WorkbenchAutoLike::where('workbenchId', $param['id'])->find(); if ($config) { $config->interval = $param['interval']; $config->maxLikes = $param['maxLikes']; $config->startTime = $param['startTime']; $config->endTime = $param['endTime']; $config->contentTypes = json_encode($param['contentTypes']); $config->devices = json_encode($param['deviceGroups']); $config->friends = json_encode($param['wechatFriends']); // $config->targetGroups = json_encode($param['targetGroups']); // $config->tagOperator = $param['tagOperator']; $config->friendMaxLikes = $param['friendMaxLikes']; $config->friendTags = $param['friendTags']; $config->enableFriendTags = $param['enableFriendTags']; $config->updateTime = time(); $config->save(); } break; case self::TYPE_MOMENTS_SYNC: $config = WorkbenchMomentsSync::where('workbenchId', $param['id'])->find(); if ($config) { if (!empty($param['contentGroups'])) { foreach ($param['contentGroups'] as $library) { if (isset($library['id']) && !empty($library['id'])) { $contentLibraries[] = $library['id']; } else { $contentLibraries[] = $library; } } } else { $contentLibraries = []; } $config->syncInterval = $param['syncInterval']; $config->syncCount = $param['syncCount']; $config->syncType = $param['syncType']; $config->startTime = $param['startTime']; $config->endTime = $param['endTime']; $config->accountType = $param['accountType']; $config->devices = json_encode($param['deviceGroups']); $config->contentLibraries = json_encode($contentLibraries); $config->updateTime = time(); $config->save(); } break; case self::TYPE_GROUP_PUSH: $config = WorkbenchGroupPush::where('workbenchId', $param['id'])->find(); if ($config) { $ownerWechatIds = $this->normalizeOwnerWechatIds($param['ownerWechatIds'] ?? null, $config); $groupPushData = $this->prepareGroupPushData($param, $ownerWechatIds, $config); $groupPushData['updateTime'] = time(); $config->save($groupPushData); } break; case self::TYPE_GROUP_CREATE: $config = WorkbenchGroupCreate::where('workbenchId', $param['id'])->find(); if ($config) { $config->devices = json_encode($param['deviceGroups'] ?? [], JSON_UNESCAPED_UNICODE); $config->startTime = $param['startTime'] ?? ''; $config->endTime = $param['endTime'] ?? ''; $config->groupSizeMin = intval($param['groupSizeMin'] ?? 3); $config->groupSizeMax = intval($param['groupSizeMax'] ?? 38); $config->maxGroupsPerDay = intval($param['maxGroupsPerDay'] ?? 20); $config->groupNameTemplate = $param['groupNameTemplate'] ?? ''; $config->groupDescription = $param['groupDescription'] ?? ''; $config->poolGroups = json_encode($param['poolGroups'] ?? [], JSON_UNESCAPED_UNICODE); $config->wechatGroups = json_encode($param['wechatGroups'] ?? [], JSON_UNESCAPED_UNICODE); // 处理群管理员:如果启用了群管理员且有指定管理员,则保存到admins字段 $admins = []; if (!empty($param['groupAdminEnabled']) && !empty($param['groupAdminWechatId'])) { // 如果groupAdminWechatId是数组,取第一个;如果是单个值,直接使用 $adminWechatId = is_array($param['groupAdminWechatId']) ? $param['groupAdminWechatId'][0] : $param['groupAdminWechatId']; // 如果是好友ID,直接添加到admins;如果是wechatId,需要转换为好友ID if (is_numeric($adminWechatId)) { $admins[] = intval($adminWechatId); } else { // 如果是wechatId字符串,需要查询对应的好友ID $friend = Db::table('s2_wechat_friend')->where('wechatId', $adminWechatId)->find(); if ($friend) { $admins[] = intval($friend['id']); } } } // 如果传入了admins参数,优先使用(兼容旧逻辑) if (!empty($param['admins']) && is_array($param['admins'])) { $admins = array_merge($admins, $param['admins']); } $config->admins = json_encode(array_unique($admins), JSON_UNESCAPED_UNICODE); $config->updateTime = time(); $config->save(); } break; case self::TYPE_TRAFFIC_DISTRIBUTION: $config = WorkbenchTrafficConfig::where('workbenchId', $param['id'])->find(); if ($config) { $config->distributeType = $param['distributeType']; $config->maxPerDay = $param['maxPerDay']; $config->timeType = $param['timeType']; $config->startTime = $param['startTime']; $config->endTime = $param['endTime']; $config->devices = json_encode($param['deviceGroups']); $config->pools = json_encode($param['poolGroups']); $config->account = json_encode($param['accountGroups']); $config->updateTime = time(); $config->save(); } break; case self::TYPE_IMPORT_CONTACT: //联系人导入 $config = WorkbenchImportContact::where('workbenchId', $param['id'])->find();; if ($config) { $config->devices = json_encode($param['deviceGroups']); $config->pools = json_encode($param['poolGroups']); $config->num = $param['num']; $config->clearContact = $param['clearContact']; $config->remark = $param['remark']; $config->startTime = $param['startTime']; $config->endTime = $param['endTime']; $config->save(); } break; } Db::commit(); return json(['code' => 200, 'msg' => '更新成功']); } catch (\Exception $e) { Db::rollback(); return json(['code' => 500, 'msg' => '更新失败:' . $e->getMessage()]); } } /** * 更新工作台状态 * @return \think\response\Json */ public function updateStatus() { if (!$this->request->isPost()) { return json(['code' => 400, 'msg' => '请求方式错误']); } $id = $this->request->param('id', ''); if (empty($id)) { return json(['code' => 400, 'msg' => '参数错误']); } $where = [ ['id', '=', $id], ['companyId', '=', $this->request->userInfo['companyId']], ['isDel', '=', 0] ]; if (empty($this->request->userInfo['isAdmin'])) { $where[] = ['userId', '=', $this->request->userInfo['id']]; } $workbench = Workbench::where($where)->find(); if (empty($workbench)) { return json(['code' => 404, 'msg' => '工作台不存在']); } $workbench->status = !$workbench['status']; $workbench->save(); return json(['code' => 200, 'msg' => '更新成功']); } /** * 删除工作台(软删除) */ public function delete() { $id = $this->request->param('id'); if (empty($id)) { return json(['code' => 400, 'msg' => '参数错误']); } $where = [ ['id', '=', $id], ['companyId', '=', $this->request->userInfo['companyId']], ['isDel', '=', 0] ]; if (empty($this->request->userInfo['isAdmin'])) { $where[] = ['userId', '=', $this->request->userInfo['id']]; } $workbench = Workbench::where($where)->find(); if (!$workbench) { return json(['code' => 404, 'msg' => '工作台不存在']); } // 软删除 $workbench->isDel = 1; $workbench->deleteTime = time(); $workbench->save(); return json(['code' => 200, 'msg' => '删除成功']); } /** * 拷贝工作台 * @return \think\response\Json */ public function copy() { if (!$this->request->isPost()) { return json(['code' => 400, 'msg' => '请求方式错误']); } $id = $this->request->post('id'); if (empty($id)) { return json(['code' => 400, 'msg' => '参数错误']); } // 验证权限并获取原数据 $workbench = Workbench::where([ ['id', '=', $id], ['userId', '=', $this->request->userInfo['id']] ])->find(); if (empty($workbench)) { return json(['code' => 404, 'msg' => '工作台不存在']); } Db::startTrans(); try { // 创建新的工作台基本信息 $newWorkbench = new Workbench; $newWorkbench->name = $workbench->name . ' copy'; $newWorkbench->type = $workbench->type; $newWorkbench->status = 1; // 新拷贝的默认启用 $newWorkbench->autoStart = $workbench->autoStart; $newWorkbench->userId = $this->request->userInfo['id']; $newWorkbench->companyId = $this->request->userInfo['companyId']; $newWorkbench->save(); // 根据类型拷贝对应的配置 switch ($workbench->type) { case self::TYPE_AUTO_LIKE: $config = WorkbenchAutoLike::where('workbenchId', $id)->find(); if ($config) { $newConfig = new WorkbenchAutoLike; $newConfig->workbenchId = $newWorkbench->id; $newConfig->interval = $config->interval; $newConfig->maxLikes = $config->maxLikes; $newConfig->startTime = $config->startTime; $newConfig->endTime = $config->endTime; $newConfig->contentTypes = $config->contentTypes; $newConfig->devices = $config->devices; $newConfig->friends = $config->friends; $newConfig->createTime = time(); $newConfig->updateTime = time(); $newConfig->save(); } break; case self::TYPE_MOMENTS_SYNC: $config = WorkbenchMomentsSync::where('workbenchId', $id)->find(); if ($config) { $newConfig = new WorkbenchMomentsSync; $newConfig->workbenchId = $newWorkbench->id; $newConfig->syncInterval = $config->syncInterval; $newConfig->syncCount = $config->syncCount; $newConfig->syncType = $config->syncType; $newConfig->startTime = $config->startTime; $newConfig->endTime = $config->endTime; $newConfig->accountType = $config->accountType; $newConfig->devices = $config->devices; $newConfig->contentLibraries = $config->contentLibraries; $newConfig->createTime = time(); $newConfig->updateTime = time(); $newConfig->save(); } break; case self::TYPE_GROUP_PUSH: $config = WorkbenchGroupPush::where('workbenchId', $id)->find(); if ($config) { $newConfig = new WorkbenchGroupPush; $newConfig->workbenchId = $newWorkbench->id; $newConfig->pushType = $config->pushType; $newConfig->targetType = isset($config->targetType) ? $config->targetType : 1; // 默认1=群推送 $newConfig->startTime = $config->startTime; $newConfig->endTime = $config->endTime; $newConfig->maxPerDay = $config->maxPerDay; $newConfig->pushOrder = $config->pushOrder; $newConfig->isLoop = $config->isLoop; $newConfig->status = $config->status; $newConfig->groups = $config->groups; $newConfig->friends = $config->friends; $newConfig->contentLibraries = $config->contentLibraries; $newConfig->trafficPools = property_exists($config, 'trafficPools') ? $config->trafficPools : json_encode([], JSON_UNESCAPED_UNICODE); $newConfig->socialMediaId = $config->socialMediaId; $newConfig->promotionSiteId = $config->promotionSiteId; $newConfig->ownerWechatIds = $config->ownerWechatIds; $newConfig->createTime = time(); $newConfig->updateTime = time(); $newConfig->save(); } break; case self::TYPE_GROUP_CREATE: $config = WorkbenchGroupCreate::where('workbenchId', $id)->find(); if ($config) { $newConfig = new WorkbenchGroupCreate; $newConfig->workbenchId = $newWorkbench->id; $newConfig->devices = $config->devices; $newConfig->startTime = $config->startTime; $newConfig->endTime = $config->endTime; $newConfig->groupSizeMin = $config->groupSizeMin; $newConfig->groupSizeMax = $config->groupSizeMax; $newConfig->maxGroupsPerDay = $config->maxGroupsPerDay; $newConfig->groupNameTemplate = $config->groupNameTemplate; $newConfig->groupDescription = $config->groupDescription; $newConfig->poolGroups = $config->poolGroups; $newConfig->wechatGroups = $config->wechatGroups; $newConfig->admins = $config->admins ?? json_encode([], JSON_UNESCAPED_UNICODE); $newConfig->createTime = time(); $newConfig->updateTime = time(); $newConfig->save(); } break; case self::TYPE_IMPORT_CONTACT: //联系人导入 $config = WorkbenchImportContact::where('workbenchId', $id)->find(); if ($config) { $newConfig = new WorkbenchImportContact; $newConfig->workbenchId = $newWorkbench->id; $newConfig->devices = $config->devices; $newConfig->pools = $config->pools; $newConfig->num = $config->num; $newConfig->clearContact = $config->clearContact; $newConfig->remark = $config->remark; $newConfig->startTime = $config->startTime; $newConfig->endTime = $config->endTime; $newConfig->createTime = time(); $newConfig->save(); } break; } Db::commit(); return json(['code' => 200, 'msg' => '拷贝成功', 'data' => ['id' => $newWorkbench->id]]); } catch (\Exception $e) { Db::rollback(); return json(['code' => 500, 'msg' => '拷贝失败:' . $e->getMessage()]); } } /** * 获取点赞记录列表 * @return \think\response\Json */ public function getLikeRecords() { $page = $this->request->param('page', 1); $limit = $this->request->param('limit', 10); $workbenchId = $this->request->param('workbenchId', 0); $where = [ ['wali.workbenchId', '=', $workbenchId] ]; // 查询点赞记录 $list = Db::name('workbench_auto_like_item')->alias('wali') ->join(['s2_wechat_moments' => 'wm'], 'wali.snsId = wm.snsId') ->field([ 'wali.id', 'wali.workbenchId', 'wali.momentsId', 'wali.snsId', 'wali.wechatAccountId', 'wali.wechatFriendId', 'wali.createTime as likeTime', 'wm.content', 'wm.resUrls', 'wm.createTime as momentTime', 'wm.userName', ]) ->where($where) ->order('wali.createTime', 'desc') ->group('wali.id') ->page($page, $limit) ->select(); // 处理数据 foreach ($list as &$item) { //处理用户信息 $friend = Db::table('s2_wechat_friend') ->where(['id' => $item['wechatFriendId']]) ->field('nickName,avatar') ->find(); if (!empty($friend)) { $item['friendName'] = $friend['nickName']; $item['friendAvatar'] = $friend['avatar']; } else { $item['friendName'] = ''; $item['friendAvatar'] = ''; } //处理客服 $friend = Db::table('s2_wechat_account') ->where(['id' => $item['wechatAccountId']]) ->field('nickName,avatar') ->find(); if (!empty($friend)) { $item['operatorName'] = $friend['nickName']; $item['operatorAvatar'] = $friend['avatar']; } else { $item['operatorName'] = ''; $item['operatorAvatar'] = ''; } // 处理时间格式 $item['likeTime'] = date('Y-m-d H:i:s', $item['likeTime']); $item['momentTime'] = !empty($item['momentTime']) ? date('Y-m-d H:i:s', $item['momentTime']) : ''; // 处理资源链接 if (!empty($item['resUrls'])) { $item['resUrls'] = json_decode($item['resUrls'], true); } else { $item['resUrls'] = []; } } // 获取总记录数 $total = Db::name('workbench_auto_like_item')->alias('wali') ->where($where) ->count(); return json([ 'code' => 200, 'msg' => '获取成功', 'data' => [ 'list' => $list, 'total' => $total, 'page' => $page, 'limit' => $limit ] ]); } /** * 获取朋友圈发布记录列表 * @return \think\response\Json */ public function getMomentsRecords() { $page = $this->request->param('page', 1); $limit = $this->request->param('limit', 10); $workbenchId = $this->request->param('workbenchId', 0); $where = [ ['wmsi.workbenchId', '=', $workbenchId] ]; // 查询发布记录 $list = Db::name('workbench_moments_sync_item')->alias('wmsi') ->join('content_item ci', 'ci.id = wmsi.contentId', 'left') ->join(['s2_wechat_account' => 'wa'], 'wa.id = wmsi.wechatAccountId', 'left') ->field([ 'wmsi.id', 'wmsi.workbenchId', 'wmsi.createTime as publishTime', 'ci.contentType', 'ci.content', 'ci.resUrls', 'ci.urls', 'wa.nickName as operatorName', 'wa.avatar as operatorAvatar' ]) ->where($where) ->order('wmsi.createTime', 'desc') ->page($page, $limit) ->select(); foreach ($list as &$item) { $item['resUrls'] = json_decode($item['resUrls'], true); $item['urls'] = json_decode($item['urls'], true); } // 获取总记录数 $total = Db::name('workbench_moments_sync_item')->alias('wmsi') ->where($where) ->count(); return json([ 'code' => 200, 'msg' => '获取成功', 'data' => [ 'list' => $list, 'total' => $total, 'page' => $page, 'limit' => $limit ] ]); } /** * 获取朋友圈发布统计 * @return \think\response\Json */ public function getMomentsStats() { $workbenchId = $this->request->param('workbenchId', 0); if (empty($workbenchId)) { return json(['code' => 400, 'msg' => '参数错误']); } // 获取今日数据 $todayStart = strtotime(date('Y-m-d') . ' 00:00:00'); $todayEnd = strtotime(date('Y-m-d') . ' 23:59:59'); $todayStats = Db::name('workbench_moments_sync_item') ->where([ ['workbenchId', '=', $workbenchId], ['createTime', 'between', [$todayStart, $todayEnd]] ]) ->field([ 'COUNT(*) as total', 'SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as success', 'SUM(CASE WHEN status = 2 THEN 1 ELSE 0 END) as failed' ]) ->find(); // 获取总数据 $totalStats = Db::name('workbench_moments_sync_item') ->where('workbenchId', $workbenchId) ->field([ 'COUNT(*) as total', 'SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as success', 'SUM(CASE WHEN status = 2 THEN 1 ELSE 0 END) as failed' ]) ->find(); return json([ 'code' => 200, 'msg' => '获取成功', 'data' => [ 'today' => [ 'total' => intval($todayStats['total']), 'success' => intval($todayStats['success']), 'failed' => intval($todayStats['failed']) ], 'total' => [ 'total' => intval($totalStats['total']), 'success' => intval($totalStats['success']), 'failed' => intval($totalStats['failed']) ] ] ]); } /** * 获取流量分发记录列表 * @return \think\response\Json */ public function getTrafficDistributionRecords() { $page = $this->request->param('page', 1); $limit = $this->request->param('limit', 10); $workbenchId = $this->request->param('workbenchId', 0); $where = [ ['wtdi.workbenchId', '=', $workbenchId] ]; // 查询分发记录 $list = Db::name('workbench_traffic_distribution_item')->alias('wtdi') ->join(['s2_wechat_account' => 'wa'], 'wa.id = wtdi.wechatAccountId', 'left') ->join(['s2_wechat_friend' => 'wf'], 'wf.id = wtdi.wechatFriendId', 'left') ->field([ 'wtdi.id', 'wtdi.workbenchId', 'wtdi.wechatAccountId', 'wtdi.wechatFriendId', 'wtdi.createTime as distributeTime', 'wtdi.status', 'wtdi.errorMsg', 'wa.nickName as operatorName', 'wa.avatar as operatorAvatar', 'wf.nickName as friendName', 'wf.avatar as friendAvatar', 'wf.gender', 'wf.province', 'wf.city' ]) ->where($where) ->order('wtdi.createTime', 'desc') ->page($page, $limit) ->select(); // 处理数据 foreach ($list as &$item) { // 处理时间格式 $item['distributeTime'] = date('Y-m-d H:i:s', $item['distributeTime']); // 处理性别 $genderMap = [ 0 => '未知', 1 => '男', 2 => '女' ]; $item['genderText'] = $genderMap[$item['gender']] ?? '未知'; // 处理状态文字 $statusMap = [ 0 => '待分发', 1 => '分发成功', 2 => '分发失败' ]; $item['statusText'] = $statusMap[$item['status']] ?? '未知状态'; } // 获取总记录数 $total = Db::name('workbench_traffic_distribution_item')->alias('wtdi') ->where($where) ->count(); return json([ 'code' => 200, 'msg' => '获取成功', 'data' => [ 'list' => $list, 'total' => $total, 'page' => $page, 'limit' => $limit ] ]); } /** * 获取流量分发统计 * @return \think\response\Json */ public function getTrafficDistributionStats() { $workbenchId = $this->request->param('workbenchId', 0); if (empty($workbenchId)) { return json(['code' => 400, 'msg' => '参数错误']); } // 获取今日数据 $todayStart = strtotime(date('Y-m-d') . ' 00:00:00'); $todayEnd = strtotime(date('Y-m-d') . ' 23:59:59'); $todayStats = Db::name('workbench_traffic_distribution_item') ->where([ ['workbenchId', '=', $workbenchId], ['createTime', 'between', [$todayStart, $todayEnd]] ]) ->field([ 'COUNT(*) as total', 'SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as success', 'SUM(CASE WHEN status = 2 THEN 1 ELSE 0 END) as failed' ]) ->find(); // 获取总数据 $totalStats = Db::name('workbench_traffic_distribution_item') ->where('workbenchId', $workbenchId) ->field([ 'COUNT(*) as total', 'SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as success', 'SUM(CASE WHEN status = 2 THEN 1 ELSE 0 END) as failed' ]) ->find(); return json([ 'code' => 200, 'msg' => '获取成功', 'data' => [ 'today' => [ 'total' => intval($todayStats['total']), 'success' => intval($todayStats['success']), 'failed' => intval($todayStats['failed']) ], 'total' => [ 'total' => intval($totalStats['total']), 'success' => intval($totalStats['success']), 'failed' => intval($totalStats['failed']) ] ] ]); } /** * 获取流量分发详情 * @return \think\response\Json */ public function getTrafficDistributionDetail() { $id = $this->request->param('id', 0); if (empty($id)) { return json(['code' => 400, 'msg' => '参数错误']); } $detail = Db::name('workbench_traffic_distribution_item')->alias('wtdi') ->join(['s2_wechat_account' => 'wa'], 'wa.id = wtdi.wechatAccountId', 'left') ->join(['s2_wechat_friend' => 'wf'], 'wf.id = wtdi.wechatFriendId', 'left') ->field([ 'wtdi.id', 'wtdi.workbenchId', 'wtdi.wechatAccountId', 'wtdi.wechatFriendId', 'wtdi.createTime as distributeTime', 'wtdi.status', 'wtdi.errorMsg', 'wa.nickName as operatorName', 'wa.avatar as operatorAvatar', 'wf.nickName as friendName', 'wf.avatar as friendAvatar', 'wf.gender', 'wf.province', 'wf.city', 'wf.signature', 'wf.remark' ]) ->where('wtdi.id', $id) ->find(); if (empty($detail)) { return json(['code' => 404, 'msg' => '记录不存在']); } // 处理数据 $detail['distributeTime'] = date('Y-m-d H:i:s', $detail['distributeTime']); // 处理性别 $genderMap = [ 0 => '未知', 1 => '男', 2 => '女' ]; $detail['genderText'] = $genderMap[$detail['gender']] ?? '未知'; // 处理状态文字 $statusMap = [ 0 => '待分发', 1 => '分发成功', 2 => '分发失败' ]; $detail['statusText'] = $statusMap[$detail['status']] ?? '未知状态'; return json([ 'code' => 200, 'msg' => '获取成功', 'data' => $detail ]); } /** * 创建流量分发计划 * @return \think\response\Json */ public function createTrafficPlan() { $controller = new \app\cunkebao\controller\workbench\WorkbenchTrafficController(); $controller->request = $this->request; return $controller->createTrafficPlan(); } /** * 获取流量列表 * @return \think\response\Json */ public function getTrafficList() { $controller = new \app\cunkebao\controller\workbench\WorkbenchTrafficController(); $controller->request = $this->request; return $controller->getTrafficList(); } /** * 获取所有微信好友标签及数量统计 * @return \think\response\Json */ public function getDeviceLabels() { $controller = new \app\cunkebao\controller\workbench\WorkbenchHelperController(); $controller->request = $this->request; return $controller->getDeviceLabels(); } /** * 获取群列表 * @return \think\response\Json */ public function getGroupList() { $controller = new \app\cunkebao\controller\workbench\WorkbenchHelperController(); $controller->request = $this->request; return $controller->getGroupList(); } /** * 获取流量池列表 * @return \think\response\Json */ public function getTrafficPoolList() { $controller = new \app\cunkebao\controller\workbench\WorkbenchHelperController(); $controller->request = $this->request; return $controller->getTrafficPoolList(); } /** * 获取账号列表 * @return \think\response\Json */ public function getAccountList() { $controller = new \app\cunkebao\controller\workbench\WorkbenchHelperController(); $controller->request = $this->request; return $controller->getAccountList(); } /** * 获取京东联盟导购媒体 * @return \think\response\Json */ public function getJdSocialMedia() { $controller = new \app\cunkebao\controller\workbench\WorkbenchHelperController(); $controller->request = $this->request; return $controller->getJdSocialMedia(); } /** * 获取京东联盟广告位 * @return \think\response\Json */ public function getJdPromotionSite() { $controller = new \app\cunkebao\controller\workbench\WorkbenchHelperController(); $controller->request = $this->request; return $controller->getJdPromotionSite(); } /** * 京东转链-京推推 * @param string $content * @param string $positionid * @return string */ public function changeLink($content = '', $positionid = '') { $controller = new \app\cunkebao\controller\workbench\WorkbenchHelperController(); $controller->request = $this->request; return $controller->changeLink($content, $positionid); } /** * 规范化客服微信ID列表 * @param mixed $ownerWechatIds * @param WorkbenchGroupPush|null $originalConfig * @return array * @throws \Exception */ private function normalizeOwnerWechatIds($ownerWechatIds, WorkbenchGroupPush $originalConfig = null): array { if ($ownerWechatIds === null) { $existing = $originalConfig ? $this->decodeJsonArray($originalConfig->ownerWechatIds ?? []) : []; if (empty($existing)) { throw new \Exception('请至少选择一个客服微信'); } return $existing; } if (!is_array($ownerWechatIds)) { throw new \Exception('客服参数格式错误'); } $normalized = $this->extractIdList($ownerWechatIds, '客服参数格式错误'); if (empty($normalized)) { throw new \Exception('请至少选择一个客服微信'); } return $normalized; } /** * 构建群推送配置数据 * @param array $param * @param array $ownerWechatIds * @param WorkbenchGroupPush|null $originalConfig * @return array * @throws \Exception */ private function prepareGroupPushData(array $param, array $ownerWechatIds, WorkbenchGroupPush $originalConfig = null): array { $targetTypeDefault = $originalConfig ? intval($originalConfig->targetType) : 1; $targetType = intval($this->getParamValue($param, 'targetType', $targetTypeDefault)) ?: 1; $groupPushSubTypeDefault = $originalConfig ? intval($originalConfig->groupPushSubType) : 1; $groupPushSubType = intval($this->getParamValue($param, 'groupPushSubType', $groupPushSubTypeDefault)) ?: 1; if (!in_array($groupPushSubType, [1, 2], true)) { $groupPushSubType = 1; } $data = [ 'pushType' => $this->toBoolInt($this->getParamValue($param, 'pushType', $originalConfig->pushType ?? 0)), 'targetType' => $targetType, 'startTime' => $this->getParamValue($param, 'startTime', $originalConfig->startTime ?? ''), 'endTime' => $this->getParamValue($param, 'endTime', $originalConfig->endTime ?? ''), 'maxPerDay' => intval($this->getParamValue($param, 'maxPerDay', $originalConfig->maxPerDay ?? 0)), 'pushOrder' => $this->getParamValue($param, 'pushOrder', $originalConfig->pushOrder ?? 1), 'groupPushSubType' => $groupPushSubType, 'status' => $this->toBoolInt($this->getParamValue($param, 'status', $originalConfig->status ?? 0)), 'socialMediaId' => $this->getParamValue($param, 'socialMediaId', $originalConfig->socialMediaId ?? ''), 'promotionSiteId' => $this->getParamValue($param, 'promotionSiteId', $originalConfig->promotionSiteId ?? ''), 'friendIntervalMin' => intval($this->getParamValue($param, 'friendIntervalMin', $originalConfig->friendIntervalMin ?? 10)), 'friendIntervalMax' => intval($this->getParamValue($param, 'friendIntervalMax', $originalConfig->friendIntervalMax ?? 20)), 'messageIntervalMin' => intval($this->getParamValue($param, 'messageIntervalMin', $originalConfig->messageIntervalMin ?? 1)), 'messageIntervalMax' => intval($this->getParamValue($param, 'messageIntervalMax', $originalConfig->messageIntervalMax ?? 12)), 'isRandomTemplate' => $this->toBoolInt($this->getParamValue($param, 'isRandomTemplate', $originalConfig->isRandomTemplate ?? 0)), 'ownerWechatIds' => json_encode($ownerWechatIds, JSON_UNESCAPED_UNICODE), ]; if ($data['friendIntervalMin'] > $data['friendIntervalMax']) { throw new \Exception('目标间最小间隔不能大于最大间隔'); } if ($data['messageIntervalMin'] > $data['messageIntervalMax']) { throw new \Exception('消息间最小间隔不能大于最大间隔'); } $contentGroupsExisting = $originalConfig ? $this->decodeJsonArray($originalConfig->contentLibraries ?? []) : []; $contentGroupsParam = $this->getParamValue($param, 'contentGroups', null); $contentGroups = $contentGroupsParam !== null ? $this->extractIdList($contentGroupsParam, '内容库参数格式错误') : $contentGroupsExisting; $data['contentLibraries'] = json_encode($contentGroups, JSON_UNESCAPED_UNICODE); $postPushTagsExisting = $originalConfig ? $this->decodeJsonArray($originalConfig->postPushTags ?? []) : []; $postPushTagsParam = $this->getParamValue($param, 'postPushTags', null); $postPushTags = $postPushTagsParam !== null ? $this->extractIdList($postPushTagsParam, '推送标签参数格式错误') : $postPushTagsExisting; $data['postPushTags'] = json_encode($postPushTags, JSON_UNESCAPED_UNICODE); if ($targetType === 1) { $data['isLoop'] = $this->toBoolInt($this->getParamValue($param, 'isLoop', $originalConfig->isLoop ?? 0)); $groupsExisting = $originalConfig ? $this->decodeJsonArray($originalConfig->groups ?? []) : []; $wechatGroups = array_key_exists('wechatGroups', $param) ? $this->extractIdList($param['wechatGroups'], '群参数格式错误') : $groupsExisting; if (empty($wechatGroups)) { throw new \Exception('群推送必须选择微信群'); } $data['groups'] = json_encode($wechatGroups, JSON_UNESCAPED_UNICODE); $data['friends'] = json_encode([], JSON_UNESCAPED_UNICODE); $data['trafficPools'] = json_encode([], JSON_UNESCAPED_UNICODE); if ($groupPushSubType === 2) { $announcementContent = $this->getParamValue($param, 'announcementContent', $originalConfig->announcementContent ?? ''); if (empty($announcementContent)) { throw new \Exception('群公告必须输入公告内容'); } $enableAiRewrite = $this->toBoolInt($this->getParamValue($param, 'enableAiRewrite', $originalConfig->enableAiRewrite ?? 0)); $aiRewritePrompt = trim((string)$this->getParamValue($param, 'aiRewritePrompt', $originalConfig->aiRewritePrompt ?? '')); if ($enableAiRewrite === 1 && $aiRewritePrompt === '') { throw new \Exception('启用AI智能话术改写时,必须输入改写提示词'); } $data['announcementContent'] = $announcementContent; $data['enableAiRewrite'] = $enableAiRewrite; $data['aiRewritePrompt'] = $aiRewritePrompt; } else { $data['groupPushSubType'] = 1; $data['announcementContent'] = ''; $data['enableAiRewrite'] = 0; $data['aiRewritePrompt'] = ''; } } else { $data['isLoop'] = 0; $friendsExisting = $originalConfig ? $this->decodeJsonArray($originalConfig->friends ?? []) : []; $trafficPoolsExisting = $originalConfig ? $this->decodeJsonArray($originalConfig->trafficPools ?? []) : []; $friendTargets = array_key_exists('wechatFriends', $param) ? $this->extractIdList($param['wechatFriends'], '好友参数格式错误') : $friendsExisting; $trafficPools = array_key_exists('trafficPools', $param) ? $this->extractIdList($param['trafficPools'], '流量池参数格式错误') : $trafficPoolsExisting; if (empty($friendTargets) && empty($trafficPools)) { throw new \Exception('好友推送需至少选择好友或流量池'); } $data['friends'] = json_encode($friendTargets, JSON_UNESCAPED_UNICODE); $data['trafficPools'] = json_encode($trafficPools, JSON_UNESCAPED_UNICODE); $data['groups'] = json_encode([], JSON_UNESCAPED_UNICODE); $data['groupPushSubType'] = 1; $data['announcementContent'] = ''; $data['enableAiRewrite'] = 0; $data['aiRewritePrompt'] = ''; } return $data; } /** * 获取参数值,若不存在则返回默认值 * @param array $param * @param string $key * @param mixed $default * @return mixed */ private function getParamValue(array $param, string $key, $default) { return array_key_exists($key, $param) ? $param[$key] : $default; } /** * 将值转换为整型布尔 * @param mixed $value * @return int */ private function toBoolInt($value): int { return empty($value) ? 0 : 1; } /** * 从参数中提取ID列表 * @param mixed $items * @param string $errorMessage * @return array * @throws \Exception */ private function extractIdList($items, string $errorMessage = '参数格式错误'): array { if (!is_array($items)) { throw new \Exception($errorMessage); } $ids = []; foreach ($items as $item) { if (is_array($item) && isset($item['id'])) { $item = $item['id']; } if ($item === '' || $item === null) { continue; } $ids[] = $item; } return array_values(array_unique($ids)); } /** * 解码JSON数组 * @param mixed $value * @return array */ private function decodeJsonArray($value): array { if (empty($value)) { return []; } if (is_array($value)) { return $value; } $decoded = json_decode($value, true); return is_array($decoded) ? $decoded : []; } /** * 验证内容是否包含链接 * @param string $content 要检测的内容 * @return bool */ private function containsLink($content) { // 定义各种链接的正则表达式模式 $patterns = [ // HTTP/HTTPS链接 '/https?:\/\/[^\s]+/i', // 京东商品链接 '/item\.jd\.com\/\d+/i', // 京东短链接 '/u\.jd\.com\/[a-zA-Z0-9]+/i', // 淘宝商品链接 '/item\.taobao\.com\/item\.htm\?id=\d+/i', // 天猫商品链接 '/detail\.tmall\.com\/item\.htm\?id=\d+/i', // 淘宝短链接 '/m\.tb\.cn\/[a-zA-Z0-9]+/i', // 拼多多链接 '/mobile\.yangkeduo\.com\/goods\.html\?goods_id=\d+/i', // 苏宁易购链接 '/product\.suning\.com\/\d+\/\d+\.html/i', // 通用域名模式(包含常见电商域名) '/(?:jd|taobao|tmall|yangkeduo|suning|amazon|dangdang)\.com[^\s]*/i', // 通用短链接模式 '/[a-zA-Z0-9-]+\.[a-zA-Z]{2,}\/[a-zA-Z0-9\-._~:\/?#\[\]@!$&\'()*+,;=]+/i' ]; // 遍历所有模式进行匹配 foreach ($patterns as $pattern) { if (preg_match($pattern, $content)) { return true; } } return false; } /** * 获取通讯录导入记录列表 * @return \think\response\Json */ public function getImportContact() { $page = $this->request->param('page', 1); $limit = $this->request->param('limit', 10); $workbenchId = $this->request->param('workbenchId', 0); $where = [ ['wici.workbenchId', '=', $workbenchId] ]; // 查询发布记录 $list = Db::name('workbench_import_contact_item')->alias('wici') ->join('traffic_pool tp', 'tp.id = wici.poolId', 'left') ->join('traffic_source tc', 'tc.identifier = tp.identifier', 'left') ->join('wechat_account wa', 'wa.wechatId = tp.wechatId', 'left') ->field([ 'wici.id', 'wici.workbenchId', 'wici.createTime', 'tp.identifier', 'tp.mobile', 'tp.wechatId', 'tc.name', 'wa.nickName', 'wa.avatar', 'wa.alias', ]) ->where($where) ->order('tc.name DESC,wici.createTime DESC') ->group('tp.identifier') ->page($page, $limit) ->select(); foreach ($list as &$item) { $item['createTime'] = date('Y-m-d H:i:s', $item['createTime']); } // 获取总记录数 $total = Db::name('workbench_import_contact_item')->alias('wici') ->where($where) ->count(); return json([ 'code' => 200, 'msg' => '获取成功', 'data' => [ 'list' => $list, 'total' => $total, ] ]); } /** * 获取群发统计数据 * @return \think\response\Json */ public function getGroupPushStats() { $controller = new \app\cunkebao\controller\workbench\WorkbenchGroupPushController(); $controller->request = $this->request; return $controller->getGroupPushStats(); } /** * 计算基础统计数据 */ private function calculateBasicStats($workbenchId, $where, $startTime, $endTime, $contentIds = null) { // 获取工作台配置,计算计划发送数 // 如果 workbenchId 为空,则查询所有工作台的配置 $configQuery = WorkbenchGroupPush::alias('wgp') ->join('workbench w', 'w.id = wgp.workbenchId', 'left') ->where('w.type', self::TYPE_GROUP_PUSH) ->where('w.isDel', 0); if (!empty($workbenchId)) { $configQuery->where('wgp.workbenchId', $workbenchId); } else { // 如果没有指定工作台ID,需要从 where 条件中获取 workbenchId 列表 $workbenchIdCondition = null; foreach ($where as $condition) { if (is_array($condition) && isset($condition[0]) && $condition[0] === 'wgpi.workbenchId') { if ($condition[1] === 'in' && is_array($condition[2])) { $workbenchIdCondition = $condition[2]; break; } elseif ($condition[1] === '=') { $workbenchIdCondition = [$condition[2]]; break; } } } if ($workbenchIdCondition) { $configQuery->whereIn('wgp.workbenchId', $workbenchIdCondition); } } $configs = $configQuery->select(); $targetType = 1; // 默认值 if (!empty($configs)) { // 如果只有一个配置,使用它的 targetType;如果有多个,默认使用1 $targetType = intval($configs[0]->targetType ?? 1); } // 计划发送数(根据配置计算) $plannedSend = 0; if (!empty($configs)) { $days = ceil(($endTime - $startTime) / 86400); foreach ($configs as $config) { $maxPerDay = intval($config->maxPerDay ?? 0); $configTargetType = intval($config->targetType ?? 1); if ($configTargetType == 1) { // 群推送:计划发送数 = 每日推送次数 * 天数 * 群数量 $groups = $this->decodeJsonArray($config->groups ?? []); $plannedSend += $maxPerDay * $days * count($groups); } else { // 好友推送:计划发送数 = 每日推送人数 * 天数 $plannedSend += $maxPerDay * $days; } } } // 构建查询条件 $queryWhere = $where; if ($contentIds !== null) { $queryWhere[] = ['wgpi.contentId', 'in', $contentIds]; } // 实际成功发送数(从推送记录表统计) $successSend = Db::name('workbench_group_push_item') ->alias('wgpi') ->where($queryWhere) ->count(); // 触达率 = 成功发送数 / 计划发送数 $reachRate = $plannedSend > 0 ? round(($successSend / $plannedSend) * 100, 1) : 0; // 获取发送记录列表,用于查询回复 $sentItemIds = Db::name('workbench_group_push_item') ->alias('wgpi') ->where($queryWhere) ->field('wgpi.id, wgpi.groupId, wgpi.friendId, wgpi.wechatAccountId, wgpi.createTime, wgpi.targetType, wgpi.contentId') ->select(); // 回复统计(通过消息表查询) $replyStats = $this->calculateReplyStats($sentItemIds, $targetType, $startTime, $endTime); // 链接点击统计 $clickStats = $this->calculateClickStats($sentItemIds, $targetType, $startTime, $endTime); // 计算本月对比数据(简化处理,实际应该查询上个月同期数据) $currentMonthStart = strtotime(date('Y-m-01 00:00:00')); $lastMonthStart = strtotime(date('Y-m-01 00:00:00', strtotime('-1 month'))); $lastMonthEnd = $currentMonthStart - 1; // 获取本月统计数据(避免递归调用) $currentMonthWhere = [ ['wgpi.createTime', '>=', $currentMonthStart] ]; // 复制 workbenchId 条件 foreach ($where as $condition) { if (is_array($condition) && isset($condition[0]) && $condition[0] === 'wgpi.workbenchId') { $currentMonthWhere[] = $condition; break; } } if ($contentIds !== null) { $currentMonthWhere[] = ['wgpi.contentId', 'in', $contentIds]; } $currentMonthSend = Db::name('workbench_group_push_item') ->alias('wgpi') ->where($currentMonthWhere) ->count(); // 获取本月配置 $currentMonthConfigQuery = WorkbenchGroupPush::alias('wgp') ->join('workbench w', 'w.id = wgp.workbenchId', 'left') ->where('w.type', self::TYPE_GROUP_PUSH) ->where('w.isDel', 0); if (!empty($workbenchId)) { $currentMonthConfigQuery->where('wgp.workbenchId', $workbenchId); } else { foreach ($where as $condition) { if (is_array($condition) && isset($condition[0]) && $condition[0] === 'wgpi.workbenchId' && $condition[1] === 'in') { $currentMonthConfigQuery->whereIn('wgp.workbenchId', $condition[2]); break; } } } $currentMonthConfigs = $currentMonthConfigQuery->select(); $currentMonthPlanned = 0; if (!empty($currentMonthConfigs)) { $currentMonthDays = ceil(($endTime - $currentMonthStart) / 86400); foreach ($currentMonthConfigs as $currentMonthConfig) { $currentMonthMaxPerDay = intval($currentMonthConfig->maxPerDay ?? 0); $currentMonthTargetType = intval($currentMonthConfig->targetType ?? 1); if ($currentMonthTargetType == 1) { $currentMonthGroups = $this->decodeJsonArray($currentMonthConfig->groups ?? []); $currentMonthPlanned += $currentMonthMaxPerDay * $currentMonthDays * count($currentMonthGroups); } else { $currentMonthPlanned += $currentMonthMaxPerDay * $currentMonthDays; } } } $currentMonthReachRate = $currentMonthPlanned > 0 ? round(($currentMonthSend / $currentMonthPlanned) * 100, 1) : 0; // 获取上个月统计数据 $lastMonthWhere = [ ['wgpi.createTime', '>=', $lastMonthStart], ['wgpi.createTime', '<=', $lastMonthEnd] ]; // 复制 workbenchId 条件 foreach ($where as $condition) { if (is_array($condition) && isset($condition[0]) && $condition[0] === 'wgpi.workbenchId') { $lastMonthWhere[] = $condition; break; } } if ($contentIds !== null) { $lastMonthWhere[] = ['wgpi.contentId', 'in', $contentIds]; } $lastMonthSend = Db::name('workbench_group_push_item') ->alias('wgpi') ->where($lastMonthWhere) ->count(); // 获取上个月配置 $lastMonthConfigQuery = WorkbenchGroupPush::alias('wgp') ->join('workbench w', 'w.id = wgp.workbenchId', 'left') ->where('w.type', self::TYPE_GROUP_PUSH) ->where('w.isDel', 0); if (!empty($workbenchId)) { $lastMonthConfigQuery->where('wgp.workbenchId', $workbenchId); } else { foreach ($where as $condition) { if (is_array($condition) && isset($condition[0]) && $condition[0] === 'wgpi.workbenchId' && $condition[1] === 'in') { $lastMonthConfigQuery->whereIn('wgp.workbenchId', $condition[2]); break; } } } $lastMonthConfigs = $lastMonthConfigQuery->select(); $lastMonthPlanned = 0; if (!empty($lastMonthConfigs)) { $lastMonthDays = ceil(($lastMonthEnd - $lastMonthStart) / 86400); foreach ($lastMonthConfigs as $lastMonthConfig) { $lastMonthMaxPerDay = intval($lastMonthConfig->maxPerDay ?? 0); $lastMonthTargetType = intval($lastMonthConfig->targetType ?? 1); if ($lastMonthTargetType == 1) { $lastMonthGroups = $this->decodeJsonArray($lastMonthConfig->groups ?? []); $lastMonthPlanned += $lastMonthMaxPerDay * $lastMonthDays * count($lastMonthGroups); } else { $lastMonthPlanned += $lastMonthMaxPerDay * $lastMonthDays; } } } $lastMonthReachRate = $lastMonthPlanned > 0 ? round(($lastMonthSend / $lastMonthPlanned) * 100, 1) : 0; // 获取上个月的回复和点击统计(简化处理) $lastMonthSentItems = Db::name('workbench_group_push_item') ->alias('wgpi') ->where($lastMonthWhere) ->field('wgpi.id, wgpi.groupId, wgpi.friendId, wgpi.wechatAccountId, wgpi.createTime, wgpi.targetType, wgpi.contentId') ->select(); $lastMonthReplyStats = $this->calculateReplyStats($lastMonthSentItems, $targetType, $lastMonthStart, $lastMonthEnd); $lastMonthClickStats = $this->calculateClickStats($lastMonthSentItems, $targetType, $lastMonthStart, $lastMonthEnd); return [ 'reachRate' => [ 'value' => $reachRate, 'trend' => round($reachRate - $lastMonthReachRate, 1), 'unit' => '%', 'description' => '成功发送/计划发送' ], 'replyRate' => [ 'value' => $replyStats['replyRate'], 'trend' => round($replyStats['replyRate'] - $lastMonthReplyStats['replyRate'], 1), 'unit' => '%', 'description' => '收到回复/成功发送' ], 'avgReplyTime' => [ 'value' => $replyStats['avgReplyTime'], 'trend' => round($lastMonthReplyStats['avgReplyTime'] - $replyStats['avgReplyTime'], 0), 'unit' => '分钟', 'description' => '从发送到回复的平均时长' ], 'clickRate' => [ 'value' => $clickStats['clickRate'], 'trend' => round($clickStats['clickRate'] - $lastMonthClickStats['clickRate'], 1), 'unit' => '%', 'description' => '点击链接/成功发送' ], 'plannedSend' => $plannedSend, 'successSend' => $successSend, 'replyCount' => $replyStats['replyCount'], 'clickCount' => $clickStats['clickCount'] ]; } /** * 计算回复统计 */ private function calculateReplyStats($sentItems, $targetType, $startTime, $endTime) { if (empty($sentItems)) { return ['replyRate' => 0, 'avgReplyTime' => 0, 'replyCount' => 0]; } $replyCount = 0; $totalReplyTime = 0; $replyTimes = []; foreach ($sentItems as $item) { $itemArray = is_array($item) ? $item : (array)$item; $sendTime = $itemArray['createTime'] ?? 0; $accountId = $itemArray['wechatAccountId'] ?? 0; if ($targetType == 1) { // 群推送:查找群内回复消息 $groupId = $itemArray['groupId'] ?? 0; $group = Db::name('wechat_group')->where('id', $groupId)->find(); if ($group) { $replyMsg = Db::table('s2_wechat_message') ->where('wechatChatroomId', $group['chatroomId']) ->where('wechatAccountId', $accountId) ->where('isSend', 0) // 接收的消息 ->where('wechatTime', '>', $sendTime) ->where('wechatTime', '<=', $sendTime + 86400) // 24小时内回复 ->order('wechatTime', 'asc') ->find(); if ($replyMsg) { $replyCount++; $replyTime = $replyMsg['wechatTime'] - $sendTime; $replyTimes[] = $replyTime; $totalReplyTime += $replyTime; } } } else { // 好友推送:查找好友回复消息 $friendId = $itemArray['friendId'] ?? 0; $friend = Db::table('s2_wechat_friend')->where('id', $friendId)->find(); if ($friend) { $replyMsg = Db::table('s2_wechat_message') ->where('wechatFriendId', $friendId) ->where('wechatAccountId', $accountId) ->where('isSend', 0) // 接收的消息 ->where('wechatTime', '>', $sendTime) ->where('wechatTime', '<=', $sendTime + 86400) // 24小时内回复 ->order('wechatTime', 'asc') ->find(); if ($replyMsg) { $replyCount++; $replyTime = $replyMsg['wechatTime'] - $sendTime; $replyTimes[] = $replyTime; $totalReplyTime += $replyTime; } } } } $successSend = count($sentItems); $replyRate = $successSend > 0 ? round(($replyCount / $successSend) * 100, 1) : 0; $avgReplyTime = $replyCount > 0 ? round(($totalReplyTime / $replyCount) / 60, 0) : 0; // 转换为分钟 return [ 'replyRate' => $replyRate, 'avgReplyTime' => $avgReplyTime, 'replyCount' => $replyCount ]; } /** * 计算链接点击统计 */ private function calculateClickStats($sentItems, $targetType, $startTime, $endTime) { if (empty($sentItems)) { return ['clickRate' => 0, 'clickCount' => 0]; } $clickCount = 0; $linkContentIds = []; // 获取所有发送的内容ID foreach ($sentItems as $item) { $itemArray = is_array($item) ? $item : (array)$item; $contentId = $itemArray['contentId'] ?? 0; if ($contentId > 0) { $linkContentIds[] = $contentId; } } if (empty($linkContentIds)) { return ['clickRate' => 0, 'clickCount' => 0]; } // 查询包含链接的内容 $linkContents = Db::name('content_item') ->whereIn('id', array_unique($linkContentIds)) ->where('contentType', 2) // 链接类型 ->column('id'); // 统计发送了链接内容的记录数 $linkSendCount = 0; foreach ($sentItems as $item) { $itemArray = is_array($item) ? $item : (array)$item; $contentId = $itemArray['contentId'] ?? 0; if (in_array($contentId, $linkContents)) { $linkSendCount++; } } // 简化处理:假设点击率基于链接消息的发送(实际应该从点击追踪系统获取) // 这里可以根据业务需求调整,比如通过消息中的链接点击事件统计 $clickCount = $linkSendCount; // 简化处理,实际需要真实的点击数据 $successSend = count($sentItems); $clickRate = $successSend > 0 ? round(($clickCount / $successSend) * 100, 1) : 0; return [ 'clickRate' => $clickRate, 'clickCount' => $clickCount ]; } /** * 获取话术组对比数据 */ private function getContentLibraryComparison($workbenchId, $where, $startTime, $endTime, $contentIds = null) { $queryWhere = $where; if ($contentIds !== null) { $queryWhere[] = ['wgpi.contentId', 'in', $contentIds]; } $comparison = Db::name('workbench_group_push_item') ->alias('wgpi') ->join('content_item ci', 'ci.id = wgpi.contentId', 'left') ->join('content_library cl', 'cl.id = ci.libraryId', 'left') ->where($queryWhere) ->where('cl.id', '<>', null) ->field([ 'cl.id as libraryId', 'cl.name as libraryName', 'COUNT(DISTINCT wgpi.id) as pushCount' ]) ->group('cl.id, cl.name') ->select(); $result = []; foreach ($comparison as $item) { $libraryId = $item['libraryId']; $pushCount = intval($item['pushCount']); // 获取该内容库的详细统计 $libraryContentIds = Db::name('content_item') ->where('libraryId', $libraryId) ->column('id'); if (empty($libraryContentIds)) { $libraryContentIds = [-1]; } $libraryWhere = array_merge($where, [['wgpi.contentId', 'in', $libraryContentIds]]); $librarySentItems = Db::name('workbench_group_push_item') ->alias('wgpi') ->where($libraryWhere) ->field('wgpi.id, wgpi.groupId, wgpi.friendId, wgpi.wechatAccountId, wgpi.createTime, wgpi.targetType, wgpi.contentId') ->select(); $config = WorkbenchGroupPush::where('workbenchId', $workbenchId)->find(); $targetType = $config ? intval($config->targetType) : 1; $replyStats = $this->calculateReplyStats($librarySentItems, $targetType, $startTime, $endTime); $clickStats = $this->calculateClickStats($librarySentItems, $targetType, $startTime, $endTime); // 计算转化率(简化处理,实际需要根据业务定义) $conversionRate = $pushCount > 0 ? round(($replyStats['replyCount'] / $pushCount) * 100, 1) : 0; $result[] = [ 'libraryId' => $libraryId, 'libraryName' => $item['libraryName'], 'pushCount' => $pushCount, 'reachRate' => 100, // 简化处理,实际应该计算 'replyRate' => $replyStats['replyRate'], 'clickRate' => $clickStats['clickRate'], 'conversionRate' => $conversionRate, 'avgReplyTime' => $replyStats['avgReplyTime'], 'level' => $this->getPerformanceLevel($replyStats['replyRate'], $clickStats['clickRate'], $conversionRate) ]; } // 按回复率排序 usort($result, function($a, $b) { return $b['replyRate'] <=> $a['replyRate']; }); return $result; } /** * 获取性能等级 */ private function getPerformanceLevel($replyRate, $clickRate, $conversionRate) { $score = ($replyRate * 0.4) + ($clickRate * 0.3) + ($conversionRate * 0.3); if ($score >= 40) { return '优秀'; } elseif ($score >= 25) { return '良好'; } elseif ($score >= 15) { return '一般'; } else { return '待提升'; } } /** * 获取时段分析数据 */ private function getTimePeriodAnalysis($workbenchId, $where, $startTime, $endTime, $contentIds = null) { $queryWhere = $where; if ($contentIds !== null) { $queryWhere[] = ['wgpi.contentId', 'in', $contentIds]; } $analysis = Db::name('workbench_group_push_item') ->alias('wgpi') ->where($queryWhere) ->field([ 'FROM_UNIXTIME(wgpi.createTime, "%H") as hour', 'COUNT(*) as count' ]) ->group('hour') ->order('hour', 'asc') ->select(); $result = []; foreach ($analysis as $item) { $result[] = [ 'hour' => intval($item['hour']), 'count' => intval($item['count']) ]; } return $result; } /** * 获取互动深度数据 */ private function getInteractionDepth($workbenchId, $where, $startTime, $endTime, $contentIds = null) { // 简化处理,实际需要更复杂的统计逻辑 return [ 'singleReply' => 0, // 单次回复 'multipleReply' => 0, // 多次回复 'deepInteraction' => 0 // 深度互动 ]; } /** * 获取推送历史记录列表 * @return \think\response\Json */ public function getGroupPushHistory() { $controller = new \app\cunkebao\controller\workbench\WorkbenchGroupPushController(); $controller->request = $this->request; return $controller->getGroupPushHistory(); } /** * 获取状态文本 * @param string $status 状态码 * @return string 状态文本 */ private function getStatusText($status) { $statusMap = [ 'success' => '已完成', 'partial' => '进行中', 'pending' => '进行中', 'failed' => '失败' ]; return $statusMap[$status] ?? '未知'; } /** * 获取已创建的群列表(自动建群) * @return \think\response\Json */ public function getCreatedGroupsList() { $workbenchId = $this->request->param('workbenchId', 0); $page = $this->request->param('page', 1); $limit = $this->request->param('limit', 100); $keyword = $this->request->param('keyword', ''); if (empty($workbenchId)) { return json(['code' => 400, 'msg' => '工作台ID不能为空']); } // 验证工作台权限 $where = [ ['id', '=', $workbenchId], ['companyId', '=', $this->request->userInfo['companyId']], ['type', '=', self::TYPE_GROUP_CREATE], ['isDel', '=', 0] ]; if (empty($this->request->userInfo['isAdmin'])) { $where[] = ['userId', '=', $this->request->userInfo['id']]; } $workbench = Workbench::where($where)->find(); if (empty($workbench)) { return json(['code' => 404, 'msg' => '工作台不存在或无权限']); } // 获取已创建的群ID列表(状态为成功且groupId不为空) $groupIds = Db::name('workbench_group_create_item') ->where('workbenchId', $workbenchId) ->where('status', 2) // STATUS_SUCCESS = 2 ->where('groupId', '<>', null) ->group('groupId') ->column('groupId'); if (empty($groupIds)) { return json([ 'code' => 200, 'msg' => '获取成功', 'data' => [ 'list' => [], 'total' => 0, 'page' => $page, 'limit' => $limit ] ]); } // 查询群组详细信息(从s2_wechat_chatroom表查询) $query = Db::table('s2_wechat_chatroom')->alias('wc') ->join('wechat_account wa', 'wa.wechatId = wc.wechatAccountWechatId', 'left') ->where('wc.id', 'in', $groupIds) ->where('wc.isDeleted', 0); // 关键字搜索 if (!empty($keyword)) { $query->where(function ($q) use ($keyword) { $q->where('wc.nickname', 'like', '%' . $keyword . '%') ->whereOr('wc.chatroomId', 'like', '%' . $keyword . '%') ->whereOr('wa.nickName', 'like', '%' . $keyword . '%'); }); } $total = $query->count(); $list = $query->field('wc.id,wc.nickname as groupName,wc.chatroomId,wc.chatroomAvatar as groupAvatar,wc.wechatAccountWechatId as ownerWechatId,wc.createTime,wc.chatroomOwnerNickname as ownerNickname,wc.chatroomOwnerAvatar as ownerAvatar,wa.alias as ownerAlias') ->order('wc.createTime', 'desc') ->page($page, $limit) ->select(); // 统计每个群的成员数量和成员信息 foreach ($list as &$item) { // 统计该群的成员数量(从workbench_group_create_item表统计) $memberCount = Db::name('workbench_group_create_item') ->where('workbenchId', $workbenchId) ->where('groupId', $item['id']) ->where('status', 'in', [2, 4]) // 创建成功和管理员好友已拉入 ->count(); // 获取成员列表(用于显示成员头像) $memberList = Db::name('workbench_group_create_item')->alias('wgci') ->join(['s2_wechat_friend' => 'wf'], 'wf.id = wgci.friendId', 'left') ->where('wgci.workbenchId', $workbenchId) ->where('wgci.groupId', $item['id']) ->where('wgci.status', 'in', [2, 4]) ->field('wf.avatar, wf.wechatId, wf.nickname') ->order('wgci.createTime', 'asc') ->limit(10) // 最多显示10个成员头像 ->select(); // 格式化成员头像列表 $memberAvatars = []; foreach ($memberList as $member) { if (!empty($member['avatar'])) { $memberAvatars[] = [ 'avatar' => $member['avatar'], 'wechatId' => $member['wechatId'] ?? '', 'nickname' => $member['nickname'] ?? '' ]; } } // 计算剩余成员数(用于显示"+XX") $remainingCount = $memberCount > count($memberAvatars) ? $memberCount - count($memberAvatars) : 0; // 格式化返回数据 $item['memberCount'] = $memberCount; $item['memberCountText'] = $memberCount . '人'; // 格式化为"XX人" $item['createTime'] = !empty($item['createTime']) ? date('Y-m-d', $item['createTime']) : ''; // 格式化为"YYYY-MM-DD" $item['memberAvatars'] = $memberAvatars; // 成员头像列表(最多10个) $item['remainingCount'] = $remainingCount; // 剩余成员数(用于显示"+XX") // 保留原有字段,但调整格式 $item['groupName'] = $item['groupName'] ?? ''; $item['groupAvatar'] = $item['groupAvatar'] ?? ''; } unset($item); return json([ 'code' => 200, 'msg' => '获取成功', 'data' => [ 'list' => $list, 'total' => $total, 'page' => $page, 'limit' => $limit ] ]); } /** * 获取已创建群的详情(自动建群) * @return \think\response\Json */ public function getCreatedGroupDetail() { $workbenchId = $this->request->param('workbenchId', 0); $groupId = $this->request->param('groupId', 0); if (empty($workbenchId)) { return json(['code' => 400, 'msg' => '工作台ID不能为空']); } if (empty($groupId)) { return json(['code' => 400, 'msg' => '群ID不能为空']); } // 验证工作台权限 $where = [ ['id', '=', $workbenchId], ['companyId', '=', $this->request->userInfo['companyId']], ['type', '=', self::TYPE_GROUP_CREATE], ['isDel', '=', 0] ]; if (empty($this->request->userInfo['isAdmin'])) { $where[] = ['userId', '=', $this->request->userInfo['id']]; } $workbench = Workbench::where($where)->find(); if (empty($workbench)) { return json(['code' => 404, 'msg' => '工作台不存在或无权限']); } // 验证该群是否属于该工作台 $groupItem = Db::name('workbench_group_create_item') ->where('workbenchId', $workbenchId) ->where('groupId', $groupId) ->where('status', 2) // STATUS_SUCCESS = 2 ->find(); if (empty($groupItem)) { return json(['code' => 404, 'msg' => '群不存在或不属于该工作台']); } // 查询群基本信息(从s2_wechat_chatroom表查询) $group = Db::table('s2_wechat_chatroom')->alias('wc') ->join('wechat_account wa', 'wa.wechatId = wc.wechatAccountWechatId', 'left') ->where('wc.id', $groupId) ->where('wc.isDeleted', 0) ->field('wc.id,wc.nickname as groupName,wc.chatroomId,wc.chatroomAvatar as groupAvatar,wc.wechatAccountWechatId as ownerWechatId,wc.createTime,wc.chatroomOwnerNickname as ownerNickname,wc.chatroomOwnerAvatar as ownerAvatar,wa.alias as ownerAlias,wc.announce') ->find(); if (empty($group)) { return json(['code' => 404, 'msg' => '群不存在']); } // 获取chatroomId $chatroomId = $group['chatroomId'] ?? ''; if (empty($chatroomId)) { return json(['code' => 400, 'msg' => '群聊ID不存在']); } // 从s2_wechat_chatroom_member表查询所有成员列表(不限制数量) $memberList = Db::table('s2_wechat_chatroom_member')->alias('wcm') ->join(['s2_wechat_friend' => 'wf'], 'wf.wechatId = wcm.wechatId', 'left') ->where('wcm.chatroomId', $chatroomId) ->field('wcm.wechatId,wcm.nickname as memberNickname,wcm.avatar as memberAvatar,wcm.conRemark as memberRemark,wcm.alias as memberAlias,wcm.createTime as joinTime,wcm.updateTime,wf.id as friendId,wf.nickname as friendNickname,wf.avatar as friendAvatar') ->order('wcm.createTime', 'asc') ->select(); // 去重:按wechatId去重,保留第一条记录 $memberMap = []; foreach ($memberList as $member) { $wechatId = $member['wechatId'] ?? ''; if (!empty($wechatId) && !isset($memberMap[$wechatId])) { $memberMap[$wechatId] = $member; } } $memberList = array_values($memberMap); // 重新索引数组 // 获取在群中的成员wechatId列表(用于判断是否已退群) $inGroupWechatIds = array_column($memberList, 'wechatId'); $inGroupWechatIds = array_filter($inGroupWechatIds); // 过滤空值 // 获取通过自动建群加入的成员信息(用于判断入群状态和已退群成员) $autoJoinMemberList = Db::name('workbench_group_create_item') ->alias('wgci') ->join(['s2_wechat_friend' => 'wf'], 'wf.id = wgci.friendId', 'left') ->where('wgci.workbenchId', $workbenchId) ->where('wgci.groupId', $groupId) ->where('wgci.status', 'in', [2, 4]) // 创建成功和管理员好友已拉入 ->field('wf.wechatId,wf.id as friendId,wf.nickname as friendNickname,wf.avatar as friendAvatar,wgci.createTime as autoJoinTime') ->select(); // 去重:按wechatId去重,保留第一条记录 $autoJoinMemberMap = []; foreach ($autoJoinMemberList as $autoMember) { $wechatId = $autoMember['wechatId'] ?? ''; if (!empty($wechatId) && !isset($autoJoinMemberMap[$wechatId])) { $autoJoinMemberMap[$wechatId] = $autoMember; } } $autoJoinMemberList = array_values($autoJoinMemberMap); // 重新索引数组 // 统计成员总数(包括在群中的成员和已退群的成员) // 先统计在群中的成员数 $inGroupCount = count($memberList); // 统计已退群的成员数(在自动建群记录中但不在群中的成员) $quitCount = 0; if (!empty($autoJoinMemberList)) { foreach ($autoJoinMemberList as $autoMember) { $wechatId = $autoMember['wechatId'] ?? ''; if (!empty($wechatId) && !in_array($wechatId, $inGroupWechatIds)) { $quitCount++; } } } // 总成员数 = 在群中的成员数 + 已退群的成员数 $memberCount = $inGroupCount + $quitCount; // 获取自动建群加入的成员wechatId列表(用于判断入群状态) $autoJoinWechatIds = array_column($autoJoinMemberList, 'wechatId'); $autoJoinWechatIds = array_filter($autoJoinWechatIds); // 过滤空值 // 格式化在群中的成员列表 $members = []; $addedWechatIds = []; // 用于记录已添加的成员wechatId,避免重复 $ownerWechatId = $group['ownerWechatId'] ?? ''; foreach ($memberList as $member) { $wechatId = $member['wechatId'] ?? ''; // 跳过空wechatId和已添加的成员 if (empty($wechatId) || in_array($wechatId, $addedWechatIds)) { continue; } // 标记为已添加 $addedWechatIds[] = $wechatId; // 根据wechatId判断是否为群主 $isOwner = (!empty($ownerWechatId) && $wechatId == $ownerWechatId) ? 1 : 0; // 判断入群状态:如果在自动建群记录中,说明是通过自动建群加入的;否则是其他方式加入的 $joinStatus = in_array($wechatId, $autoJoinWechatIds) ? 'auto' : 'manual'; // 判断是否已退群:如果成员在s2_wechat_chatroom_member表中存在,说明在群中;否则已退群 // 由于我们已经从s2_wechat_chatroom_member表查询,所以这里都是"在群中"状态 $isQuit = 0; // 0=在群中,1=已退群 // 优先使用friend表的昵称和头像,如果没有则使用member表的 $nickname = !empty($member['friendNickname']) ? $member['friendNickname'] : ($member['memberNickname'] ?? ''); $avatar = !empty($member['friendAvatar']) ? $member['friendAvatar'] : ($member['memberAvatar'] ?? ''); $members[] = [ 'friendId' => $member['friendId'] ?? 0, 'wechatId' => $wechatId, 'nickname' => $nickname, 'avatar' => $avatar, 'alias' => $member['memberAlias'] ?? '', 'remark' => $member['memberRemark'] ?? '', 'isOwner' => $isOwner, // 标记群主 'joinStatus' => $joinStatus, // 入群状态:auto=自动建群加入,manual=其他方式加入 'isQuit' => $isQuit, // 是否已退群:0=在群中,1=已退群 'joinTime' => !empty($member['joinTime']) ? date('Y-m-d H:i:s', $member['joinTime']) : '', // 入群时间 ]; } // 添加已退群的成员(在自动建群记录中但不在群中的成员) foreach ($autoJoinMemberList as $autoMember) { $wechatId = $autoMember['wechatId'] ?? ''; // 跳过空wechatId、已在群中的成员和已添加的成员 if (empty($wechatId) || in_array($wechatId, $inGroupWechatIds) || in_array($wechatId, $addedWechatIds)) { continue; } // 标记为已添加 $addedWechatIds[] = $wechatId; // 根据wechatId判断是否为群主 $isOwner = (!empty($ownerWechatId) && $wechatId == $ownerWechatId) ? 1 : 0; $members[] = [ 'friendId' => $autoMember['friendId'] ?? 0, 'wechatId' => $wechatId, 'nickname' => $autoMember['friendNickname'] ?? '', 'avatar' => $autoMember['friendAvatar'] ?? '', 'alias' => '', 'remark' => '', 'isOwner' => $isOwner, // 标记群主 'joinStatus' => 'auto', // 入群状态:auto=自动建群加入 'isQuit' => 1, // 是否已退群:1=已退群 'joinTime' => !empty($autoMember['autoJoinTime']) ? date('Y-m-d H:i:s', $autoMember['autoJoinTime']) : '', // 入群时间 ]; } // 将群主排在第一位 usort($members, function($a, $b) { if ($a['isOwner'] == $b['isOwner']) { return 0; } return $a['isOwner'] > $b['isOwner'] ? -1 : 1; }); // 格式化返回数据 $result = [ 'id' => $group['id'], 'groupName' => $group['groupName'] ?? '', 'chatroomId' => $group['chatroomId'] ?? '', 'groupAvatar' => $group['groupAvatar'] ?? '', 'ownerWechatId' => $group['ownerWechatId'] ?? '', 'ownerNickname' => $group['ownerNickname'] ?? '', 'ownerAvatar' => $group['ownerAvatar'] ?? '', 'ownerAlias' => $group['ownerAlias'] ?? '', 'announce' => $group['announce'] ?? '', 'createTime' => !empty($group['createTime']) ? date('Y-m-d H:i', $group['createTime']) : '', // 格式化为"YYYY-MM-DD HH:MM" 'memberCount' => $memberCount, 'memberCountText' => $memberCount . '人', // 格式化为"XX人" 'workbenchName' => $workbench->name ?? '', // 任务名称(工作台名称) 'members' => $members // 所有成员列表 ]; return json([ 'code' => 200, 'msg' => '获取成功', 'data' => $result ]); } /** * 同步群最新信息(包括群成员) * @return \think\response\Json */ public function syncGroupInfo() { $workbenchId = $this->request->param('workbenchId', 0); $groupId = $this->request->param('groupId', 0); if (empty($workbenchId)) { return json(['code' => 400, 'msg' => '工作台ID不能为空']); } if (empty($groupId)) { return json(['code' => 400, 'msg' => '群ID不能为空']); } // 验证工作台权限 $where = [ ['id', '=', $workbenchId], ['companyId', '=', $this->request->userInfo['companyId']], ['type', '=', self::TYPE_GROUP_CREATE], ['isDel', '=', 0] ]; if (empty($this->request->userInfo['isAdmin'])) { $where[] = ['userId', '=', $this->request->userInfo['id']]; } $workbench = Workbench::where($where)->find(); if (empty($workbench)) { return json(['code' => 404, 'msg' => '工作台不存在或无权限']); } // 验证该群是否属于该工作台 $groupItem = Db::name('workbench_group_create_item') ->where('workbenchId', $workbenchId) ->where('groupId', $groupId) ->where('status', 2) // STATUS_SUCCESS = 2 ->find(); if (empty($groupItem)) { return json(['code' => 404, 'msg' => '群不存在或不属于该工作台']); } // 查询群基本信息,获取chatroomId和wechatAccountWechatId $group = Db::table('s2_wechat_chatroom') ->where('id', $groupId) ->where('isDeleted', 0) ->field('id,chatroomId,wechatAccountWechatId') ->find(); if (empty($group)) { return json(['code' => 404, 'msg' => '群不存在']); } $chatroomId = $group['chatroomId'] ?? ''; $wechatAccountWechatId = $group['wechatAccountWechatId'] ?? ''; if (empty($chatroomId)) { return json(['code' => 400, 'msg' => '群聊ID不存在']); } try { // 实例化WechatChatroomController $chatroomController = new \app\api\controller\WechatChatroomController(); // 1. 同步群信息(调用getlist方法) $syncData = [ 'wechatChatroomId' => $chatroomId, // 使用chatroomId作为wechatChatroomId来指定要同步的群 'wechatAccountKeyword' => $wechatAccountWechatId, // 通过群主微信ID筛选 'isDeleted' => false, 'pageIndex' => 1, 'pageSize' => 100 // 获取足够多的数据 ]; $syncResult = $chatroomController->getlist($syncData, true, 0); // isInner = true $syncResponse = json_decode($syncResult, true); if (empty($syncResponse['code']) || $syncResponse['code'] != 200) { return json(['code' => 500, 'msg' => '同步群信息失败:' . ($syncResponse['msg'] ?? '未知错误')]); } // 2. 同步群成员信息(调用listChatroomMember方法) // wechatChatroomId 使用 s2_wechat_chatroom 表的 id(即groupId) // chatroomId 使用群聊ID(chatroomId) $wechatChatroomId = $groupId; // s2_wechat_chatroom表的id $memberSyncResult = $chatroomController->listChatroomMember($wechatChatroomId, $chatroomId, true); // isInner = true $memberSyncResponse = json_decode($memberSyncResult, true); if (empty($memberSyncResponse['code']) || $memberSyncResponse['code'] != 200) { // 成员同步失败不影响整体结果,记录警告即可 \think\facade\Log::warning("同步群成员失败。群ID: {$groupId}, 群聊ID: {$chatroomId}, 错误: " . ($memberSyncResponse['msg'] ?? '未知错误')); } return json([ 'code' => 200, 'msg' => '同步成功', 'data' => [ 'groupId' => $groupId, 'chatroomId' => $chatroomId, 'groupInfoSynced' => true, 'memberInfoSynced' => !empty($memberSyncResponse['code']) && $memberSyncResponse['code'] == 200 ] ]); } catch (\Exception $e) { \think\facade\Log::error("同步群信息异常。群ID: {$groupId}, 错误: " . $e->getMessage()); return json(['code' => 500, 'msg' => '同步失败:' . $e->getMessage()]); } } /** * 修改群名称、群公告 * @return \think\response\Json */ public function modifyGroupInfo() { $workbenchId = $this->request->param('workbenchId', 0); $groupId = $this->request->param('groupId', 0); $chatroomName = $this->request->param('chatroomName', ''); $announce = $this->request->param('announce', ''); if (empty($workbenchId)) { return json(['code' => 400, 'msg' => '工作台ID不能为空']); } if (empty($groupId)) { return json(['code' => 400, 'msg' => '群ID不能为空']); } // 至少需要提供一个修改项 if (empty($chatroomName) && empty($announce)) { return json(['code' => 400, 'msg' => '请至少提供群名称或群公告中的一个参数']); } // 查询群基本信息 $group = Db::table('s2_wechat_chatroom') ->where('id', $groupId) ->where('isDeleted', 0) ->field('id,chatroomId,wechatAccountWechatId,accountId,wechatAccountId') ->find(); if (empty($group)) { return json(['code' => 404, 'msg' => '群不存在']); } $chatroomId = $group['id'] ?? ''; if (empty($chatroomId)) { return json(['code' => 400, 'msg' => '群聊ID不存在']); } try { // 直接使用群表中的账号信息 $executeAccountId = $group['accountId'] ?? 0; $executeWechatAccountId = $group['wechatAccountId'] ?? 0; $executeWechatId = $group['wechatAccountWechatId'] ?? ''; // 确保 wechatId 不为空 if (empty($executeWechatId)) { return json(['code' => 400, 'msg' => '无法获取微信账号ID']); } // 调用 WebSocketController 修改群信息 // 获取系统API账号信息(用于WebSocket连接) $username = Env::get('api.username2', ''); $password = Env::get('api.password2', ''); if (empty($username) || empty($password)) { return json(['code' => 500, 'msg' => '系统API账号配置缺失']); } // 获取系统账号ID $systemAccountId = Db::name('users')->where('account', $username)->value('s2_accountId'); if (empty($systemAccountId)) { return json(['code' => 500, 'msg' => '未找到系统账号ID']); } $webSocketController = new WebSocketController([ 'userName' => $username, 'password' => $password, 'accountId' => $systemAccountId ]); // 构建修改参数 $modifyData = [ 'wechatChatroomId' => $chatroomId, 'wechatAccountId' => $executeWechatAccountId, ]; if (!empty($chatroomName)) { $modifyData['chatroomName'] = $chatroomName; } if (!empty($announce)) { $modifyData['announce'] = $announce; } $modifyResult = $webSocketController->CmdChatroomModifyInfo($modifyData); $modifyResponse = json_decode($modifyResult, true); if (empty($modifyResponse['code']) || $modifyResponse['code'] != 200) { return json(['code' => 500, 'msg' => '修改群信息失败:' . ($modifyResponse['msg'] ?? '未知错误')]); } // 修改成功后更新数据库 $updateData = [ 'updateTime' => time() ]; // 如果修改了群名称,更新数据库 if (!empty($chatroomName)) { $updateData['nickname'] = $chatroomName; } // 如果修改了群公告,更新数据库 if (!empty($announce)) { $updateData['announce'] = $announce; } // 更新数据库 Db::table('s2_wechat_chatroom') ->where('id', $groupId) ->update($updateData); return json([ 'code' => 200, 'msg' => '修改成功', 'data' => [ 'groupId' => $groupId, 'chatroomId' => $chatroomId, 'chatroomName' => $chatroomName, 'announce' => $announce ] ]); } catch (\Exception $e) { \think\facade\Log::error("修改群信息异常。群ID: {$groupId}, 错误: " . $e->getMessage()); return json(['code' => 500, 'msg' => '修改失败:' . $e->getMessage()]); } } /** * 转移群聊到指定账号 * @param int $groupId 群ID(s2_wechat_chatroom表的id) * @param string $chatroomId 群聊ID * @param int $toAccountId 目标账号ID(s2_company_account表的id) * @param int $toWechatAccountId 目标微信账号ID(s2_wechat_account表的id) * @return array ['success' => bool, 'msg' => string] */ protected function transferChatroomToAccount($groupId, $chatroomId, $toAccountId, $toWechatAccountId) { try { // 查询目标账号信息 $targetAccount = Db::table('s2_company_account') ->where('id', $toAccountId) ->field('id,userName,realName,nickname') ->find(); if (empty($targetAccount)) { return ['success' => false, 'msg' => '目标账号不存在']; } // 查询目标微信账号信息 $targetWechatAccount = Db::table('s2_wechat_account') ->where('id', $toWechatAccountId) ->field('id,wechatId,deviceAccountId') ->find(); if (empty($targetWechatAccount)) { return ['success' => false, 'msg' => '目标微信账号不存在']; } // 调用 AutomaticAssign 进行群聊转移 $automaticAssign = new \app\api\controller\AutomaticAssign(); // 构建转移参数(通过 API 调用) $transferData = [ 'wechatChatroomId' => $chatroomId, // 使用群聊ID 'toAccountId' => $toAccountId, 'wechatAccountKeyword' => $targetWechatAccount['wechatId'] ?? '', 'isDeleted' => false ]; // 直接更新数据库(因为 API 可能不支持指定单个群聊ID转移) // 更新 s2_wechat_chatroom 表的 accountId 和 wechatAccountId Db::table('s2_wechat_chatroom') ->where('id', $groupId) ->update([ 'accountId' => $toAccountId, 'accountUserName' => $targetAccount['userName'] ?? '', 'accountRealName' => $targetAccount['realName'] ?? '', 'accountNickname' => $targetAccount['nickname'] ?? '', 'wechatAccountId' => $toWechatAccountId, 'wechatAccountWechatId' => $targetWechatAccount['wechatId'] ?? '', 'updateTime' => time() ]); return ['success' => true, 'msg' => '转移成功']; } catch (\Exception $e) { \think\facade\Log::error("转移群聊异常。群ID: {$groupId}, 目标账号ID: {$toAccountId}, 错误: " . $e->getMessage()); return ['success' => false, 'msg' => $e->getMessage()]; } } /** * 退群功能(自动建群) * @return \think\response\Json */ public function quitGroup() { $controller = new \app\cunkebao\controller\workbench\WorkbenchGroupCreateController(); $controller->request = $this->request; return $controller->quitGroup(); } }