场景获客支持拉群
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()]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user