场景获客支持拉群
This commit is contained in:
@@ -505,7 +505,7 @@ class WorkbenchController extends Controller
|
||||
$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');
|
||||
$query->field('workbenchId,devices,startTime,endTime,groupSizeMin,groupSizeMax,maxGroupsPerDay,groupNameTemplate,groupDescription,poolGroups,wechatGroups,admins,executorId');
|
||||
},
|
||||
'importContact' => function ($query) {
|
||||
$query->field('workbenchId,devices,pools,num,remarkType,remark,clearContact,startTime,endTime');
|
||||
@@ -642,6 +642,70 @@ class WorkbenchController extends Controller
|
||||
'totalMembersCount' => $totalMembersCount
|
||||
];
|
||||
|
||||
// 如果 executorId 有值,查询设备详情(格式和 deviceGroupsOptions 一样,但返回一维数组)
|
||||
$executorId = !empty($workbench->config->executorId) ? intval($workbench->config->executorId) : 0;
|
||||
$executor = null;
|
||||
if (!empty($executorId)) {
|
||||
// 查询设备基本信息
|
||||
$device = Db::table('s2_device')
|
||||
->where('id', $executorId)
|
||||
->where('isDeleted', 0)
|
||||
->field('id,imei,memo,alive,wechatAccounts')
|
||||
->find();
|
||||
|
||||
if (!empty($device)) {
|
||||
// 查询关联的微信账号(通过 currentDeviceId)
|
||||
$wechatAccount = Db::table('s2_wechat_account')
|
||||
->where('currentDeviceId', $executorId)
|
||||
->field('wechatId,nickname,alias,avatar,totalFriend')
|
||||
->find();
|
||||
|
||||
// 解析 wechatAccounts JSON 字段
|
||||
$wechatAccountsJson = [];
|
||||
if (!empty($device['wechatAccounts'])) {
|
||||
$wechatAccountsJson = json_decode($device['wechatAccounts'], true);
|
||||
if (!is_array($wechatAccountsJson)) {
|
||||
$wechatAccountsJson = [];
|
||||
}
|
||||
}
|
||||
|
||||
// 优先使用 s2_wechat_account 表的数据,如果没有则使用 wechatAccounts JSON 中的第一个
|
||||
$wechatId = '';
|
||||
$nickname = '';
|
||||
$alias = '';
|
||||
$avatar = '';
|
||||
$totalFriend = 0;
|
||||
|
||||
if (!empty($wechatAccount)) {
|
||||
$wechatId = $wechatAccount['wechatId'] ?? '';
|
||||
$nickname = $wechatAccount['nickname'] ?? '';
|
||||
$alias = $wechatAccount['alias'] ?? '';
|
||||
$avatar = $wechatAccount['avatar'] ?? '';
|
||||
$totalFriend = intval($wechatAccount['totalFriend'] ?? 0);
|
||||
} elseif (!empty($wechatAccountsJson) && is_array($wechatAccountsJson) && count($wechatAccountsJson) > 0) {
|
||||
$firstWechat = $wechatAccountsJson[0];
|
||||
$wechatId = $firstWechat['wechatId'] ?? '';
|
||||
$nickname = $firstWechat['wechatNickname'] ?? '';
|
||||
$alias = $firstWechat['alias'] ?? '';
|
||||
$avatar = $firstWechat['wechatAvatar'] ?? '';
|
||||
$totalFriend = 0; // JSON 中没有 totalFriend 字段
|
||||
}
|
||||
|
||||
$executor = [
|
||||
'id' => $device['id'],
|
||||
'imei' => $device['imei'] ?? '',
|
||||
'memo' => $device['memo'] ?? '',
|
||||
'alive' => $device['alive'] ?? 0,
|
||||
'wechatId' => $wechatId,
|
||||
'nickname' => $nickname,
|
||||
'alias' => $alias,
|
||||
'avatar' => $avatar,
|
||||
'totalFriend' => $totalFriend
|
||||
];
|
||||
}
|
||||
}
|
||||
$workbench->config->executor = $executor;
|
||||
|
||||
unset($workbench->groupCreate, $workbench->group_create);
|
||||
}
|
||||
break;
|
||||
@@ -725,23 +789,83 @@ class WorkbenchController extends Controller
|
||||
|
||||
//获取设备信息
|
||||
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')
|
||||
// 查询设备基本信息(包含 wechatAccounts JSON 字段)
|
||||
$devices = Db::table('s2_device')
|
||||
->whereIn('id', $workbench->config->deviceGroups)
|
||||
->where('isDeleted', 0)
|
||||
->field('id,imei,memo,alive,wechatAccounts')
|
||||
->order('id desc')
|
||||
->select();
|
||||
|
||||
foreach ($deviceList as &$device) {
|
||||
$curstomer = WechatCustomerModel::field('friendShip')->where(['wechatId' => $device['wechatId']])->find();
|
||||
$device['totalFriend'] = $curstomer->friendShip->totalFriend ?? 0;
|
||||
|
||||
$deviceList = [];
|
||||
if (!empty($devices)) {
|
||||
// 批量查询关联的微信账号(通过 currentDeviceId)
|
||||
$deviceIds = array_column($devices, 'id');
|
||||
$wechatAccounts = Db::table('s2_wechat_account')
|
||||
->whereIn('currentDeviceId', $deviceIds)
|
||||
->field('currentDeviceId,wechatId,nickname,alias,avatar,totalFriend')
|
||||
->select();
|
||||
|
||||
// 将微信账号按设备ID分组
|
||||
$wechatAccountsMap = [];
|
||||
foreach ($wechatAccounts as $wa) {
|
||||
$deviceId = $wa['currentDeviceId'];
|
||||
if (!isset($wechatAccountsMap[$deviceId])) {
|
||||
$wechatAccountsMap[$deviceId] = $wa;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理每个设备
|
||||
foreach ($devices as $device) {
|
||||
$deviceId = $device['id'];
|
||||
|
||||
// 查询关联的微信账号(通过 currentDeviceId)
|
||||
$wechatAccount = $wechatAccountsMap[$deviceId] ?? null;
|
||||
|
||||
// 解析 wechatAccounts JSON 字段
|
||||
$wechatAccountsJson = [];
|
||||
if (!empty($device['wechatAccounts'])) {
|
||||
$wechatAccountsJson = json_decode($device['wechatAccounts'], true);
|
||||
if (!is_array($wechatAccountsJson)) {
|
||||
$wechatAccountsJson = [];
|
||||
}
|
||||
}
|
||||
|
||||
// 优先使用 s2_wechat_account 表的数据,如果没有则使用 wechatAccounts JSON 中的第一个
|
||||
$wechatId = '';
|
||||
$nickname = '';
|
||||
$alias = '';
|
||||
$avatar = '';
|
||||
$totalFriend = 0;
|
||||
|
||||
if (!empty($wechatAccount)) {
|
||||
$wechatId = $wechatAccount['wechatId'] ?? '';
|
||||
$nickname = $wechatAccount['nickname'] ?? '';
|
||||
$alias = $wechatAccount['alias'] ?? '';
|
||||
$avatar = $wechatAccount['avatar'] ?? '';
|
||||
$totalFriend = intval($wechatAccount['totalFriend'] ?? 0);
|
||||
} elseif (!empty($wechatAccountsJson) && is_array($wechatAccountsJson) && count($wechatAccountsJson) > 0) {
|
||||
$firstWechat = $wechatAccountsJson[0];
|
||||
$wechatId = $firstWechat['wechatId'] ?? '';
|
||||
$nickname = $firstWechat['wechatNickname'] ?? '';
|
||||
$alias = $firstWechat['alias'] ?? '';
|
||||
$avatar = $firstWechat['wechatAvatar'] ?? '';
|
||||
$totalFriend = 0; // JSON 中没有 totalFriend 字段
|
||||
}
|
||||
|
||||
$deviceList[] = [
|
||||
'id' => $device['id'],
|
||||
'imei' => $device['imei'] ?? '',
|
||||
'memo' => $device['memo'] ?? '',
|
||||
'alive' => $device['alive'] ?? 0,
|
||||
'wechatId' => $wechatId,
|
||||
'nickname' => $nickname,
|
||||
'alias' => $alias,
|
||||
'avatar' => $avatar,
|
||||
'totalFriend' => $totalFriend
|
||||
];
|
||||
}
|
||||
}
|
||||
unset($device);
|
||||
|
||||
$workbench->config->deviceGroupsOptions = $deviceList;
|
||||
} else {
|
||||
@@ -1071,6 +1195,7 @@ class WorkbenchController extends Controller
|
||||
$config->devices = json_encode($param['deviceGroups'] ?? [], JSON_UNESCAPED_UNICODE);
|
||||
$config->startTime = $param['startTime'] ?? '';
|
||||
$config->endTime = $param['endTime'] ?? '';
|
||||
$config->executorId = intval($param['executorId'] ?? 3);
|
||||
$config->groupSizeMin = intval($param['groupSizeMin'] ?? 3);
|
||||
$config->groupSizeMax = intval($param['groupSizeMax'] ?? 38);
|
||||
$config->maxGroupsPerDay = intval($param['maxGroupsPerDay'] ?? 20);
|
||||
@@ -1321,6 +1446,7 @@ class WorkbenchController extends Controller
|
||||
$newConfig->devices = $config->devices;
|
||||
$newConfig->startTime = $config->startTime;
|
||||
$newConfig->endTime = $config->endTime;
|
||||
$newConfig->executorId = $config->executorId;
|
||||
$newConfig->groupSizeMin = $config->groupSizeMin;
|
||||
$newConfig->groupSizeMax = $config->groupSizeMax;
|
||||
$newConfig->maxGroupsPerDay = $config->maxGroupsPerDay;
|
||||
@@ -2206,9 +2332,93 @@ class WorkbenchController extends Controller
|
||||
*/
|
||||
public function getGroupPushStats()
|
||||
{
|
||||
$controller = new \app\cunkebao\controller\workbench\WorkbenchGroupPushController();
|
||||
$controller->request = $this->request;
|
||||
return $controller->getGroupPushStats();
|
||||
$workbenchId = $this->request->param('workbenchId', 0);
|
||||
$timeRange = $this->request->param('timeRange', '7'); // 默认最近7天
|
||||
$contentLibraryIds = $this->request->param('contentLibraryIds', ''); // 话术组筛选
|
||||
$userId = $this->request->userInfo['id'];
|
||||
|
||||
// 如果指定了工作台ID,则验证权限
|
||||
if (!empty($workbenchId)) {
|
||||
$workbench = Workbench::where([
|
||||
['id', '=', $workbenchId],
|
||||
['userId', '=', $userId],
|
||||
['type', '=', self::TYPE_GROUP_PUSH],
|
||||
['isDel', '=', 0]
|
||||
])->find();
|
||||
|
||||
if (empty($workbench)) {
|
||||
return json(['code' => 404, 'msg' => '工作台不存在']);
|
||||
}
|
||||
}
|
||||
|
||||
// 计算时间范围
|
||||
$days = intval($timeRange);
|
||||
$startTime = strtotime(date('Y-m-d 00:00:00', strtotime("-{$days} days")));
|
||||
$endTime = time();
|
||||
|
||||
// 构建查询条件
|
||||
$where = [
|
||||
['wgpi.createTime', '>=', $startTime],
|
||||
['wgpi.createTime', '<=', $endTime]
|
||||
];
|
||||
|
||||
// 如果指定了工作台ID,则限制查询范围
|
||||
if (!empty($workbenchId)) {
|
||||
$where[] = ['wgpi.workbenchId', '=', $workbenchId];
|
||||
} else {
|
||||
// 如果没有指定工作台ID,则查询当前用户的所有群推送工作台
|
||||
$workbenchIds = Workbench::where([
|
||||
['userId', '=', $userId],
|
||||
['type', '=', self::TYPE_GROUP_PUSH],
|
||||
['isDel', '=', 0]
|
||||
])->column('id');
|
||||
|
||||
if (empty($workbenchIds)) {
|
||||
// 如果没有工作台,返回空结果
|
||||
$workbenchIds = [-1];
|
||||
}
|
||||
$where[] = ['wgpi.workbenchId', 'in', $workbenchIds];
|
||||
}
|
||||
|
||||
// 话术组筛选 - 先获取符合条件的内容ID列表
|
||||
$contentIds = null;
|
||||
if (!empty($contentLibraryIds)) {
|
||||
$libraryIds = is_array($contentLibraryIds) ? $contentLibraryIds : explode(',', $contentLibraryIds);
|
||||
$libraryIds = array_filter(array_map('intval', $libraryIds));
|
||||
if (!empty($libraryIds)) {
|
||||
// 查询符合条件的内容ID
|
||||
$contentIds = Db::name('content_item')
|
||||
->whereIn('libraryId', $libraryIds)
|
||||
->column('id');
|
||||
if (empty($contentIds)) {
|
||||
// 如果没有符合条件的内容,返回空结果
|
||||
$contentIds = [-1]; // 使用不存在的ID,确保查询结果为空
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1. 基础统计:触达率、回复率、平均回复时间、链接点击率
|
||||
$stats = $this->calculateBasicStats($workbenchId, $where, $startTime, $endTime, $contentIds);
|
||||
|
||||
// 2. 话术组对比
|
||||
$contentLibraryComparison = $this->getContentLibraryComparison($workbenchId, $where, $startTime, $endTime, $contentIds);
|
||||
|
||||
// 3. 时段分析
|
||||
$timePeriodAnalysis = $this->getTimePeriodAnalysis($workbenchId, $where, $startTime, $endTime, $contentIds);
|
||||
|
||||
// 4. 互动深度(可选,需要更多数据)
|
||||
$interactionDepth = $this->getInteractionDepth($workbenchId, $where, $startTime, $endTime, $contentIds);
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '获取成功',
|
||||
'data' => [
|
||||
'basicStats' => $stats,
|
||||
'contentLibraryComparison' => $contentLibraryComparison,
|
||||
'timePeriodAnalysis' => $timePeriodAnalysis,
|
||||
'interactionDepth' => $interactionDepth
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2718,9 +2928,291 @@ class WorkbenchController extends Controller
|
||||
*/
|
||||
public function getGroupPushHistory()
|
||||
{
|
||||
$controller = new \app\cunkebao\controller\workbench\WorkbenchGroupPushController();
|
||||
$controller->request = $this->request;
|
||||
return $controller->getGroupPushHistory();
|
||||
$page = $this->request->param('page', 1);
|
||||
$limit = $this->request->param('limit', 10);
|
||||
$workbenchId = $this->request->param('workbenchId', 0);
|
||||
$keyword = $this->request->param('keyword', '');
|
||||
$pushType = $this->request->param('pushType', ''); // 推送类型筛选:''=全部, 'friend'=好友消息, 'group'=群消息, 'announcement'=群公告
|
||||
$status = $this->request->param('status', ''); // 状态筛选:''=全部, 'success'=已完成, 'progress'=进行中, 'failed'=失败
|
||||
$userId = $this->request->userInfo['id'];
|
||||
|
||||
// 构建工作台查询条件
|
||||
$workbenchWhere = [
|
||||
['w.userId', '=', $userId],
|
||||
['w.type', '=', self::TYPE_GROUP_PUSH],
|
||||
['w.isDel', '=', 0]
|
||||
];
|
||||
|
||||
// 如果指定了工作台ID,则验证权限并限制查询范围
|
||||
if (!empty($workbenchId)) {
|
||||
$workbench = Workbench::where([
|
||||
['id', '=', $workbenchId],
|
||||
['userId', '=', $userId],
|
||||
['type', '=', self::TYPE_GROUP_PUSH],
|
||||
['isDel', '=', 0]
|
||||
])->find();
|
||||
|
||||
if (empty($workbench)) {
|
||||
return json(['code' => 404, 'msg' => '工作台不存在']);
|
||||
}
|
||||
$workbenchWhere[] = ['w.id', '=', $workbenchId];
|
||||
}
|
||||
|
||||
// 1. 先查询所有已执行的推送记录(按推送时间分组)
|
||||
$pushHistoryQuery = Db::name('workbench_group_push_item')
|
||||
->alias('wgpi')
|
||||
->join('workbench w', 'w.id = wgpi.workbenchId', 'left')
|
||||
->join('workbench_group_push wgp', 'wgp.workbenchId = wgpi.workbenchId', 'left')
|
||||
->join('content_item ci', 'ci.id = wgpi.contentId', 'left')
|
||||
->join('content_library cl', 'cl.id = ci.libraryId', 'left')
|
||||
->where($workbenchWhere)
|
||||
->field([
|
||||
'wgpi.workbenchId',
|
||||
'w.name as workbenchName',
|
||||
'wgpi.contentId',
|
||||
'FROM_UNIXTIME(wgpi.createTime, "%Y-%m-%d %H:00:00") as pushTime',
|
||||
'wgpi.targetType',
|
||||
'wgp.groupPushSubType',
|
||||
'MIN(wgpi.createTime) as createTime',
|
||||
'COUNT(DISTINCT wgpi.id) as totalCount',
|
||||
'cl.name as contentLibraryName'
|
||||
])
|
||||
->group('wgpi.workbenchId, wgpi.contentId, pushTime, wgpi.targetType, wgp.groupPushSubType');
|
||||
|
||||
if (!empty($keyword)) {
|
||||
$pushHistoryQuery->where('w.name|cl.name|ci.content', 'like', '%' . $keyword . '%');
|
||||
}
|
||||
|
||||
$pushHistoryList = $pushHistoryQuery->order('createTime', 'desc')->select();
|
||||
|
||||
// 2. 查询所有任务(包括未执行的)
|
||||
$allTasksQuery = Db::name('workbench')
|
||||
->alias('w')
|
||||
->join('workbench_group_push wgp', 'wgp.workbenchId = w.id', 'left')
|
||||
->where($workbenchWhere)
|
||||
->field([
|
||||
'w.id as workbenchId',
|
||||
'w.name as workbenchName',
|
||||
'w.createTime',
|
||||
'wgp.targetType',
|
||||
'wgp.groupPushSubType',
|
||||
'wgp.groups',
|
||||
'wgp.friends',
|
||||
'wgp.trafficPools'
|
||||
]);
|
||||
|
||||
if (!empty($keyword)) {
|
||||
$allTasksQuery->where('w.name', 'like', '%' . $keyword . '%');
|
||||
}
|
||||
|
||||
$allTasks = $allTasksQuery->select();
|
||||
|
||||
// 3. 合并数据:已执行的推送记录 + 未执行的任务
|
||||
$resultList = [];
|
||||
$executedWorkbenchIds = [];
|
||||
|
||||
// 处理已执行的推送记录
|
||||
foreach ($pushHistoryList as $item) {
|
||||
$itemWorkbenchId = $item['workbenchId'];
|
||||
$contentId = $item['contentId'];
|
||||
$pushTime = $item['pushTime'];
|
||||
$targetType = intval($item['targetType']);
|
||||
$groupPushSubType = isset($item['groupPushSubType']) ? intval($item['groupPushSubType']) : 1;
|
||||
|
||||
// 标记该工作台已有执行记录
|
||||
if (!in_array($itemWorkbenchId, $executedWorkbenchIds)) {
|
||||
$executedWorkbenchIds[] = $itemWorkbenchId;
|
||||
}
|
||||
|
||||
// 将时间字符串转换为时间戳范围(小时级别)
|
||||
$pushTimeStart = strtotime($pushTime);
|
||||
$pushTimeEnd = $pushTimeStart + 3600; // 一小时内
|
||||
// 获取该次推送的详细统计
|
||||
$pushWhere = [
|
||||
['wgpi.workbenchId', '=', $itemWorkbenchId],
|
||||
['wgpi.contentId', '=', $contentId],
|
||||
['wgpi.createTime', '>=', $pushTimeStart],
|
||||
['wgpi.createTime', '<', $pushTimeEnd],
|
||||
['wgpi.targetType', '=', $targetType]
|
||||
];
|
||||
|
||||
// 目标数量
|
||||
if ($targetType == 1) {
|
||||
// 群推送:统计群数量
|
||||
$targetCount = Db::name('workbench_group_push_item')
|
||||
->alias('wgpi')
|
||||
->where($pushWhere)
|
||||
->where('wgpi.groupId', '<>', null)
|
||||
->distinct(true)
|
||||
->count('wgpi.groupId');
|
||||
} else {
|
||||
// 好友推送:统计好友数量
|
||||
$targetCount = Db::name('workbench_group_push_item')
|
||||
->alias('wgpi')
|
||||
->where($pushWhere)
|
||||
->where('wgpi.friendId', '<>', null)
|
||||
->distinct(true)
|
||||
->count('wgpi.friendId');
|
||||
}
|
||||
|
||||
// 成功数和失败数(简化处理,实际需要根据发送状态判断)
|
||||
$successCount = intval($item['totalCount']); // 简化处理
|
||||
$failCount = 0; // 简化处理,实际需要从发送状态获取
|
||||
// 状态判断
|
||||
$itemStatus = $successCount > 0 ? 'success' : 'failed';
|
||||
if ($failCount > 0 && $successCount > 0) {
|
||||
$itemStatus = 'partial';
|
||||
}
|
||||
|
||||
// 推送类型判断
|
||||
$pushTypeText = '';
|
||||
$pushTypeCode = '';
|
||||
if ($targetType == 1) {
|
||||
// 群推送
|
||||
if ($groupPushSubType == 2) {
|
||||
$pushTypeText = '群公告';
|
||||
$pushTypeCode = 'announcement';
|
||||
} else {
|
||||
$pushTypeText = '群消息';
|
||||
$pushTypeCode = 'group';
|
||||
}
|
||||
} else {
|
||||
// 好友推送
|
||||
$pushTypeText = '好友消息';
|
||||
$pushTypeCode = 'friend';
|
||||
}
|
||||
|
||||
$resultList[] = [
|
||||
'workbenchId' => $itemWorkbenchId,
|
||||
'taskName' => $item['workbenchName'] ?? '',
|
||||
'pushType' => $pushTypeText,
|
||||
'pushTypeCode' => $pushTypeCode,
|
||||
'targetCount' => $targetCount,
|
||||
'successCount' => $successCount,
|
||||
'failCount' => $failCount,
|
||||
'status' => $itemStatus,
|
||||
'statusText' => $this->getStatusText($itemStatus),
|
||||
'createTime' => date('Y-m-d H:i:s', $item['createTime']),
|
||||
'contentLibraryName' => $item['contentLibraryName'] ?? ''
|
||||
];
|
||||
}
|
||||
|
||||
// 处理未执行的任务
|
||||
foreach ($allTasks as $task) {
|
||||
$taskWorkbenchId = $task['workbenchId'];
|
||||
|
||||
// 如果该任务已有执行记录,跳过(避免重复)
|
||||
if (in_array($taskWorkbenchId, $executedWorkbenchIds)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$targetType = isset($task['targetType']) ? intval($task['targetType']) : 1;
|
||||
$groupPushSubType = isset($task['groupPushSubType']) ? intval($task['groupPushSubType']) : 1;
|
||||
|
||||
// 计算目标数量(从配置中获取)
|
||||
$targetCount = 0;
|
||||
if ($targetType == 1) {
|
||||
// 群推送:统计配置的群数量
|
||||
$groups = json_decode($task['groups'] ?? '[]', true);
|
||||
$targetCount = is_array($groups) ? count($groups) : 0;
|
||||
} else {
|
||||
// 好友推送:统计配置的好友数量或流量池数量
|
||||
$friends = json_decode($task['friends'] ?? '[]', true);
|
||||
$trafficPools = json_decode($task['trafficPools'] ?? '[]', true);
|
||||
$friendCount = is_array($friends) ? count($friends) : 0;
|
||||
$poolCount = is_array($trafficPools) ? count($trafficPools) : 0;
|
||||
// 如果配置了流量池,目标数量暂时显示为流量池数量(实际数量需要从流量池中统计)
|
||||
$targetCount = $friendCount > 0 ? $friendCount : $poolCount;
|
||||
}
|
||||
|
||||
// 推送类型判断
|
||||
$pushTypeText = '';
|
||||
$pushTypeCode = '';
|
||||
if ($targetType == 1) {
|
||||
// 群推送
|
||||
if ($groupPushSubType == 2) {
|
||||
$pushTypeText = '群公告';
|
||||
$pushTypeCode = 'announcement';
|
||||
} else {
|
||||
$pushTypeText = '群消息';
|
||||
$pushTypeCode = 'group';
|
||||
}
|
||||
} else {
|
||||
// 好友推送
|
||||
$pushTypeText = '好友消息';
|
||||
$pushTypeCode = 'friend';
|
||||
}
|
||||
|
||||
$resultList[] = [
|
||||
'workbenchId' => $taskWorkbenchId,
|
||||
'taskName' => $task['workbenchName'] ?? '',
|
||||
'pushType' => $pushTypeText,
|
||||
'pushTypeCode' => $pushTypeCode,
|
||||
'targetCount' => $targetCount,
|
||||
'successCount' => 0,
|
||||
'failCount' => 0,
|
||||
'status' => 'pending',
|
||||
'statusText' => '进行中',
|
||||
'createTime' => date('Y-m-d H:i:s', $task['createTime']),
|
||||
'contentLibraryName' => ''
|
||||
];
|
||||
}
|
||||
|
||||
// 应用筛选条件
|
||||
$filteredList = [];
|
||||
foreach ($resultList as $item) {
|
||||
// 推送类型筛选
|
||||
if (!empty($pushType)) {
|
||||
if ($pushType === 'friend' && $item['pushTypeCode'] !== 'friend') {
|
||||
continue;
|
||||
}
|
||||
if ($pushType === 'group' && $item['pushTypeCode'] !== 'group') {
|
||||
continue;
|
||||
}
|
||||
if ($pushType === 'announcement' && $item['pushTypeCode'] !== 'announcement') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 状态筛选
|
||||
if (!empty($status)) {
|
||||
if ($status === 'success' && $item['status'] !== 'success') {
|
||||
continue;
|
||||
}
|
||||
if ($status === 'progress') {
|
||||
// 进行中:包括 partial 和 pending
|
||||
if ($item['status'] !== 'partial' && $item['status'] !== 'pending') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ($status === 'failed' && $item['status'] !== 'failed') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$filteredList[] = $item;
|
||||
}
|
||||
|
||||
// 按创建时间倒序排序
|
||||
usort($filteredList, function($a, $b) {
|
||||
return strtotime($b['createTime']) - strtotime($a['createTime']);
|
||||
});
|
||||
|
||||
// 分页处理
|
||||
$total = count($filteredList);
|
||||
$offset = ($page - 1) * $limit;
|
||||
$list = array_slice($filteredList, $offset, $limit);
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '获取成功',
|
||||
'data' => [
|
||||
'list' => $list,
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'limit' => $limit
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3037,7 +3529,7 @@ class WorkbenchController extends Controller
|
||||
'isOwner' => $isOwner, // 标记群主
|
||||
'joinStatus' => $joinStatus, // 入群状态:auto=自动建群加入,manual=其他方式加入
|
||||
'isQuit' => $isQuit, // 是否已退群:0=在群中,1=已退群
|
||||
'joinTime' => !empty($member['joinTime']) ? date('Y-m-d H:i:s', $member['joinTime']) : '', // 入群时间
|
||||
'joinTime' => (!empty($member['joinTime']) && is_numeric($member['joinTime']) && $member['joinTime'] > 0) ? date('Y-m-d H:i:s', intval($member['joinTime'])) : '', // 入群时间
|
||||
];
|
||||
}
|
||||
|
||||
@@ -3066,7 +3558,7 @@ class WorkbenchController extends Controller
|
||||
'isOwner' => $isOwner, // 标记群主
|
||||
'joinStatus' => 'auto', // 入群状态:auto=自动建群加入
|
||||
'isQuit' => 1, // 是否已退群:1=已退群
|
||||
'joinTime' => !empty($autoMember['autoJoinTime']) ? date('Y-m-d H:i:s', $autoMember['autoJoinTime']) : '', // 入群时间
|
||||
'joinTime' => (!empty($autoMember['autoJoinTime']) && is_numeric($autoMember['autoJoinTime']) && $autoMember['autoJoinTime'] > 0) ? date('Y-m-d H:i:s', intval($autoMember['autoJoinTime'])) : '', // 入群时间
|
||||
];
|
||||
}
|
||||
|
||||
@@ -3078,6 +3570,72 @@ class WorkbenchController extends Controller
|
||||
return $a['isOwner'] > $b['isOwner'] ? -1 : 1;
|
||||
});
|
||||
|
||||
// 获取工作台配置,检查是否有 executorId
|
||||
$groupCreateConfig = WorkbenchGroupCreate::where('workbenchId', $workbenchId)->find();
|
||||
$executorId = !empty($groupCreateConfig) ? intval($groupCreateConfig->executorId ?? 0) : 0;
|
||||
|
||||
// 如果 executorId 有值,查询设备详情(格式和 deviceGroupsOptions 一样,但返回一维数组)
|
||||
$executor = null;
|
||||
if (!empty($executorId)) {
|
||||
// 查询设备基本信息
|
||||
$device = Db::table('s2_device')
|
||||
->where('id', $executorId)
|
||||
->where('isDeleted', 0)
|
||||
->field('id,imei,memo,alive,wechatAccounts')
|
||||
->find();
|
||||
|
||||
if (!empty($device)) {
|
||||
// 查询关联的微信账号(通过 currentDeviceId)
|
||||
$wechatAccount = Db::table('s2_wechat_account')
|
||||
->where('currentDeviceId', $executorId)
|
||||
->field('wechatId,nickname,alias,avatar,totalFriend')
|
||||
->find();
|
||||
|
||||
// 解析 wechatAccounts JSON 字段
|
||||
$wechatAccountsJson = [];
|
||||
if (!empty($device['wechatAccounts'])) {
|
||||
$wechatAccountsJson = json_decode($device['wechatAccounts'], true);
|
||||
if (!is_array($wechatAccountsJson)) {
|
||||
$wechatAccountsJson = [];
|
||||
}
|
||||
}
|
||||
|
||||
// 优先使用 s2_wechat_account 表的数据,如果没有则使用 wechatAccounts JSON 中的第一个
|
||||
$wechatId = '';
|
||||
$nickname = '';
|
||||
$alias = '';
|
||||
$avatar = '';
|
||||
$totalFriend = 0;
|
||||
|
||||
if (!empty($wechatAccount)) {
|
||||
$wechatId = $wechatAccount['wechatId'] ?? '';
|
||||
$nickname = $wechatAccount['nickname'] ?? '';
|
||||
$alias = $wechatAccount['alias'] ?? '';
|
||||
$avatar = $wechatAccount['avatar'] ?? '';
|
||||
$totalFriend = intval($wechatAccount['totalFriend'] ?? 0);
|
||||
} elseif (!empty($wechatAccountsJson) && is_array($wechatAccountsJson) && count($wechatAccountsJson) > 0) {
|
||||
$firstWechat = $wechatAccountsJson[0];
|
||||
$wechatId = $firstWechat['wechatId'] ?? '';
|
||||
$nickname = $firstWechat['wechatNickname'] ?? '';
|
||||
$alias = $firstWechat['alias'] ?? '';
|
||||
$avatar = $firstWechat['wechatAvatar'] ?? '';
|
||||
$totalFriend = 0; // JSON 中没有 totalFriend 字段
|
||||
}
|
||||
|
||||
$executor = [
|
||||
'id' => $device['id'],
|
||||
'imei' => $device['imei'] ?? '',
|
||||
'memo' => $device['memo'] ?? '',
|
||||
'alive' => $device['alive'] ?? 0,
|
||||
'wechatId' => $wechatId,
|
||||
'nickname' => $nickname,
|
||||
'alias' => $alias,
|
||||
'avatar' => $avatar,
|
||||
'totalFriend' => $totalFriend
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化返回数据
|
||||
$result = [
|
||||
'id' => $group['id'],
|
||||
@@ -3089,11 +3647,12 @@ class WorkbenchController extends Controller
|
||||
'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"
|
||||
'createTime' => (!empty($group['createTime']) && is_numeric($group['createTime']) && $group['createTime'] > 0) ? date('Y-m-d H:i', intval($group['createTime'])) : '', // 格式化为"YYYY-MM-DD HH:MM"
|
||||
'memberCount' => $memberCount,
|
||||
'memberCountText' => $memberCount . '人', // 格式化为"XX人"
|
||||
'workbenchName' => $workbench->name ?? '', // 任务名称(工作台名称)
|
||||
'members' => $members // 所有成员列表
|
||||
'members' => $members, // 所有成员列表
|
||||
'executor' => $executor // 执行设备详情(当 executorId 有值时返回,格式和 deviceGroupsOptions 一样)
|
||||
];
|
||||
|
||||
return json([
|
||||
@@ -3408,9 +3967,112 @@ class WorkbenchController extends Controller
|
||||
*/
|
||||
public function quitGroup()
|
||||
{
|
||||
$controller = new \app\cunkebao\controller\workbench\WorkbenchGroupCreateController();
|
||||
$controller->request = $this->request;
|
||||
return $controller->quitGroup();
|
||||
$workbenchId = $this->request->param('workbenchId', 0);
|
||||
$groupId = $this->request->param('groupId', 0);
|
||||
|
||||
if (empty($groupId)) {
|
||||
return json(['code' => 400, 'msg' => '群ID不能为空']);
|
||||
}
|
||||
|
||||
// 查询群基本信息
|
||||
$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['chatroomId'] ?? '';
|
||||
if (empty($chatroomId)) {
|
||||
return json(['code' => 400, 'msg' => '群聊ID不存在']);
|
||||
}
|
||||
|
||||
try {
|
||||
// 直接使用群表中的账号信息
|
||||
$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
|
||||
]);
|
||||
|
||||
// 使用与 modifyGroupInfo 相同的方式,但操作类型改为4(退群)
|
||||
$params = [
|
||||
"chatroomOperateType" => 4, // 4 表示退群
|
||||
"cmdType" => "CmdChatroomOperate",
|
||||
"seq" => time(),
|
||||
"wechatAccountId" => $executeWechatId,
|
||||
"wechatChatroomId" => $chatroomId
|
||||
];
|
||||
|
||||
// 使用反射调用 protected 的 sendMessage 方法
|
||||
$reflection = new \ReflectionClass($webSocketController);
|
||||
$method = $reflection->getMethod('sendMessage');
|
||||
$method->setAccessible(true);
|
||||
$quitResult = $method->invoke($webSocketController, $params, false);
|
||||
|
||||
// sendMessage 返回的是数组
|
||||
if (empty($quitResult) || (isset($quitResult['code']) && $quitResult['code'] != 200)) {
|
||||
return json(['code' => 500, 'msg' => '退群失败:' . ($quitResult['msg'] ?? '未知错误')]);
|
||||
}
|
||||
|
||||
// 退群成功后更新数据库
|
||||
// 将群标记为已删除
|
||||
Db::table('s2_wechat_chatroom')
|
||||
->where('id', $groupId)
|
||||
->update([
|
||||
'isDeleted' => 1,
|
||||
'deleteTime' => time(),
|
||||
'updateTime' => time()
|
||||
]);
|
||||
|
||||
// 如果提供了 workbenchId,更新工作台建群记录的状态(标记为已退群)
|
||||
if (!empty($workbenchId)) {
|
||||
Db::name('workbench_group_create_item')
|
||||
->where('workbenchId', $workbenchId)
|
||||
->where('groupId', $groupId)
|
||||
->update([
|
||||
'status' => 5, // 可以定义一个新状态:5 = 已退群
|
||||
'updateTime' => time()
|
||||
]);
|
||||
}
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '退群成功',
|
||||
'data' => [
|
||||
'groupId' => $groupId,
|
||||
'chatroomId' => $chatroomId
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
\think\facade\Log::error("退群异常。群ID: {$groupId}, 错误: " . $e->getMessage());
|
||||
return json(['code' => 500, 'msg' => '退群失败:' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user