From b2e84a225923f9cd10856cce3ce5d719f9328498 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Wed, 7 Jan 2026 10:41:39 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E6=96=B0=E5=A2=9E=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E6=89=80=E6=9C=89=E5=A5=BD=E5=8F=8B=E7=9A=84=E6=B5=81=E9=87=8F?= =?UTF-8?q?=E6=B1=A0=202=E3=80=81=E6=97=A7=E7=89=88=E5=9C=BA=E6=99=AF?= =?UTF-8?q?=E8=8E=B7=E5=AE=A2=E6=95=B0=E6=8D=AE=E8=BF=81=E7=A7=BB=203?= =?UTF-8?q?=E3=80=81=E5=9C=BA=E6=99=AF=E8=8E=B7=E5=AE=A2=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=85=BC=E5=AE=B9=E6=97=A7=E7=89=88=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + .../mobile/mine/traffic-pool/userList/api.ts | 2 +- Server/application/cunkebao/config/route.php | 5 +- .../cunkebao/controller/TrafficController.php | 215 +++++++++++++++++- .../plan/PosterWeChatMiniProgram.php | 33 ++- .../workbench/WorkbenchAutoLikeController.php | 1 + .../workbench/WorkbenchController.php | 46 +++- .../WorkbenchGroupPushController.php | 10 +- .../workbench/WorkbenchHelperController.php | 1 + .../WorkbenchImportContactController.php | 1 + .../workbench/WorkbenchMomentsController.php | 1 + .../workbench/WorkbenchTrafficController.php | 1 + .../job/WorkbenchGroupCreateJob.php | 58 ++++- .../application/job/WorkbenchGroupPushJob.php | 99 +++++++- .../job/WorkbenchImportContactJob.php | 99 ++++++-- 15 files changed, 516 insertions(+), 57 deletions(-) diff --git a/.gitignore b/.gitignore index 1f8964da..3048ae35 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ Cunkebao/dist Touchkebao/.specstory/ Serverruntime/ Moncter/提示词/ +*.log diff --git a/Cunkebao/src/pages/mobile/mine/traffic-pool/userList/api.ts b/Cunkebao/src/pages/mobile/mine/traffic-pool/userList/api.ts index 7d4efe7d..d36d3532 100644 --- a/Cunkebao/src/pages/mobile/mine/traffic-pool/userList/api.ts +++ b/Cunkebao/src/pages/mobile/mine/traffic-pool/userList/api.ts @@ -7,5 +7,5 @@ export function fetchTrafficPoolList(params: { keyword?: string; packageId?: string; }) { - return request("/v1/traffic/pool/users", params, "GET"); + return request("/v1/traffic/pool/user-list", params, "GET"); } diff --git a/Server/application/cunkebao/config/route.php b/Server/application/cunkebao/config/route.php index 07a9e8f8..f1c7964e 100644 --- a/Server/application/cunkebao/config/route.php +++ b/Server/application/cunkebao/config/route.php @@ -63,12 +63,13 @@ Route::group('v1/', function () { // 流量池相关 Route::group('traffic/pool', function () { - Route::get('getPackage', 'app\cunkebao\controller\TrafficController@getPackage'); + Route::get('getPackage', 'app\cunkebao\controller\TrafficController@getPackage'); // 获取流量池包列表 + Route::get('getPackageDetail', 'app\cunkebao\controller\TrafficController@getPackageDetail'); // 获取流量池详情(元数据) Route::post('addPackage', 'app\cunkebao\controller\TrafficController@addPackage'); Route::post('editPackage', 'app\cunkebao\controller\TrafficController@editPackage'); Route::delete('deletePackage', 'app\cunkebao\controller\TrafficController@deletePackage'); - Route::get('', 'app\cunkebao\controller\TrafficController@getTrafficPoolList'); + Route::get('user-list', 'app\cunkebao\controller\TrafficController@getTrafficPoolList'); // 获取流量池用户列表(数据列表) diff --git a/Server/application/cunkebao/controller/TrafficController.php b/Server/application/cunkebao/controller/TrafficController.php index 4ceefc9e..548db171 100644 --- a/Server/application/cunkebao/controller/TrafficController.php +++ b/Server/application/cunkebao/controller/TrafficController.php @@ -38,6 +38,41 @@ class TrafficController extends BaseController $list = $package->page($page, $limit)->order('isSys ASC,id DESC')->select(); $total = $package->count(); + // 添加"所有好友"特殊流量池(ID为0) + $allFriendsPackage = [ + 'id' => 0, + 'name' => '所有好友', + 'description' => '展示公司下所有设备的好友', + 'pic' => '', + 'type' => 1, // 系统类型 + 'createTime' => '', + 'num' => 0, // 数量将在下面计算 + ]; + + // 计算所有好友数量 + try { + $companyId = $this->getUserInfo('companyId'); + $wechatIds = Db::name('device')->alias('d') + ->join('(SELECT MAX(id) AS id, deviceId FROM ck_device_wechat_login WHERE companyId='.$companyId.' GROUP BY deviceId) dwl_max', 'dwl_max.deviceId = d.id') + ->join('device_wechat_login dwl', 'dwl.id = dwl_max.id') + ->where(['d.companyId' => $companyId, 'd.deleteTime' => 0]) + ->column('dwl.wechatId'); + + if (!empty($wechatIds)) { + $allFriendsCount = Db::table('s2_wechat_friend') + ->where('ownerWechatId', 'in', $wechatIds) + ->where('isDeleted', 0) + ->count(); + $allFriendsPackage['num'] = $allFriendsCount; + } + } catch (\Exception $e) { + // 如果查询失败,保持num为0 + } + + // 将"所有好友"添加到列表最前面 + array_unshift($list, $allFriendsPackage); + $total = $total + 1; // 总数加1 + $rfmRule = 'default'; foreach ($list as $k => &$v) { if ($v['type'] != 1) { @@ -132,6 +167,11 @@ class TrafficController extends BaseController return ResponseHelper::error('流量池ID不能为空'); } + // 禁止编辑"所有好友"特殊流量池 + if ($packageId === '0' || $packageId === 0) { + return ResponseHelper::error('"所有好友"流量池不允许编辑'); + } + if (empty($packageName)) { return ResponseHelper::error('流量池名称不能为空'); } @@ -194,6 +234,11 @@ class TrafficController extends BaseController return ResponseHelper::error('流量池ID不能为空'); } + // 禁止删除"所有好友"特殊流量池 + if ($packageId === '0' || $packageId === 0) { + return ResponseHelper::error('"所有好友"流量池不允许删除'); + } + // 检查流量池是否存在且属于当前公司 $package = TrafficSourcePackage::where(['id' => $packageId, 'isDel' => 0]) ->whereIn('companyId', [$companyId, 0]) @@ -243,6 +288,87 @@ class TrafficController extends BaseController } + /** + * 获取流量池详情 + * @return \think\response\Json + * @throws \Exception + */ + public function getPackageDetail() + { + $packageId = $this->request->param('packageId', ''); + $companyId = $this->getUserInfo('companyId'); + + if (empty($packageId) && $packageId !== '0' && $packageId !== 0) { + return ResponseHelper::error('流量池ID不能为空'); + } + + // 特殊处理:packageId为0时,返回"所有好友"的详情 + if ($packageId === '0' || $packageId === 0) { + $data = [ + 'id' => 0, + 'name' => '所有好友', + 'description' => '展示公司下所有设备的好友', + 'pic' => '', + 'type' => 1, // 系统类型 + 'isSys' => 1, + 'createTime' => '', + 'updateTime' => '', + 'num' => 0, + ]; + + // 计算所有好友数量 + try { + $wechatIds = Db::name('device')->alias('d') + ->join('(SELECT MAX(id) AS id, deviceId FROM ck_device_wechat_login WHERE companyId='.$companyId.' GROUP BY deviceId) dwl_max', 'dwl_max.deviceId = d.id') + ->join('device_wechat_login dwl', 'dwl.id = dwl_max.id') + ->where(['d.companyId' => $companyId, 'd.deleteTime' => 0]) + ->column('dwl.wechatId'); + + if (!empty($wechatIds)) { + $allFriendsCount = Db::table('s2_wechat_friend') + ->where('ownerWechatId', 'in', $wechatIds) + ->where('isDeleted', 0) + ->count(); + $data['num'] = $allFriendsCount; + } + } catch (\Exception $e) { + // 如果查询失败,保持num为0 + } + + return ResponseHelper::success($data); + } + + // 查询普通流量池详情 + $package = TrafficSourcePackage::where(['id' => $packageId, 'isDel' => 0]) + ->whereIn('companyId', [$companyId, 0]) + ->find(); + + if (empty($package)) { + return ResponseHelper::error('流量池不存在或已删除'); + } + + // 统计流量池中的数量 + $itemCount = TrafficSourcePackageItem::where([ + 'packageId' => $packageId, + 'companyId' => $companyId, + 'isDel' => 0 + ])->count(); + + $data = [ + 'id' => $package['id'], + 'name' => $package['name'], + 'description' => $package['description'] ?? '', + 'pic' => $package['pic'] ?? '', + 'type' => $package['isSys'] ?? 0, + 'isSys' => $package['isSys'] ?? 0, + 'createTime' => !empty($package['createTime']) ? formatRelativeTime($package['createTime']) : '', + 'updateTime' => !empty($package['updateTime']) ? formatRelativeTime($package['updateTime']) : '', + 'num' => $itemCount, + ]; + + return ResponseHelper::success($data); + } + /** * 流量池列表 * @return \think\response\Json @@ -257,10 +383,15 @@ class TrafficController extends BaseController $companyId = $this->getUserInfo('companyId'); $userId = $this->getUserInfo('id'); - if (empty($packageId)) { + if (empty($packageId) && $packageId !== '0' && $packageId !== 0) { return ResponseHelper::error('流量包id不能为空'); } + // 特殊处理:packageId为0时,查询所有好友 + if ($packageId === '0' || $packageId === 0) { + return $this->getAllFriendsList($page, $limit, $keyword, $companyId); + } + $trafficSourcePackage = TrafficSourcePackage::where(['id' => $packageId, 'isDel' => 0])->whereIn('companyId', [$companyId, 0])->find(); if (empty($trafficSourcePackage)) { return ResponseHelper::error('流量包不存在或已删除'); @@ -270,7 +401,7 @@ class TrafficController extends BaseController ['tspi.packageId', '=', $packageId], ]; - if (empty($keyword)) { + if (!empty($keyword)) { $where[] = ['wa.nickname|wa.phone|wa.alias|wa.wechatId|p.mobile|p.identifier', 'like', '%' . $keyword . '%']; } @@ -287,7 +418,7 @@ class TrafficController extends BaseController $query->order('tspi.id DESC,p.id DESC')->group('p.identifier'); - $list = $query->page($page, $limit)->select()->toArray(); + $list = $query->page($page, $limit)->select(); $total = $query->count(); foreach ($list as $k => &$v) { @@ -307,8 +438,8 @@ class TrafficController extends BaseController $v['F'] = $scores['F']; $v['M'] = $scores['M']; $v['RFM'] = $scores['R'] + $scores['F'] + $scores['M']; - $v['money'] = 2222; - $v['msgCount'] = 2222; + $v['money'] = 3; + $v['msgCount'] = 3 ; $v['tag'] = ['test', 'test2']; } unset($v); @@ -318,4 +449,78 @@ class TrafficController extends BaseController return ResponseHelper::success($data); } + /** + * 获取所有好友列表(特殊流量池) + * @param int $page + * @param int $limit + * @param string $keyword + * @param int $companyId + * @return \think\response\Json + */ + private function getAllFriendsList($page, $limit, $keyword, $companyId) + { + try { + // 获取公司下所有设备的微信ID + $wechatIds = Db::name('device')->alias('d') + ->join('(SELECT MAX(id) AS id, deviceId FROM ck_device_wechat_login WHERE companyId='.$companyId.' GROUP BY deviceId) dwl_max', 'dwl_max.deviceId = d.id') + ->join('device_wechat_login dwl', 'dwl.id = dwl_max.id') + ->where(['d.companyId' => $companyId, 'd.deleteTime' => 0]) + ->column('dwl.wechatId'); + + if (empty($wechatIds)) { + return ResponseHelper::success(['list' => [], 'total' => 0]); + } + + // 构建查询条件 + $where = [ + ['wf.ownerWechatId', 'in', $wechatIds], + ['wf.isDeleted', '=', 0], + ]; + + // 关键字搜索 + if (!empty($keyword)) { + $where[] = ['wf.nickname|wf.alias|wf.wechatId|wf.conRemark', 'like', '%' . $keyword . '%']; + } + + // 查询好友列表 + $query = Db::table('s2_wechat_friend')->alias('wf') + ->join(['s2_wechat_account' => 'wa'], 'wa.wechatId = wf.ownerWechatId', 'left') + ->field([ + 'wf.id', 'wf.wechatId as identifier', 'wf.wechatId', + Db::raw($companyId . ' as companyId'), 'wf.nickname', 'wf.avatar', 'wf.gender', 'wf.phone', 'wf.alias' + ]) + ->where($where); + + $total = $query->count(); + $list = $query->order('wf.id DESC')->page($page, $limit)->select(); + + foreach ($list as $k => &$v) { + // 获取好友所属的流量池包 + $package = TrafficSourcePackageItem::alias('tspi') + ->join('traffic_source_package p', 'tspi.packageId=p.id AND tspi.companyId=p.companyId') + ->where(['tspi.identifier' => $v['identifier']]) + ->whereIn('tspi.companyId', [0, $companyId]) + ->column('p.name'); + $v['packages'] = $package; + $v['phone'] = !empty($v['phone']) ? $v['phone'] : ''; + + // RFM评分(示例数据,实际应该从业务数据计算) + $scores = RFMController::calcRfmScores(30, 30, 30); + $v['R'] = $scores['R']; + $v['F'] = $scores['F']; + $v['M'] = $scores['M']; + $v['RFM'] = $scores['R'] + $scores['F'] + $scores['M']; + $v['money'] = 2222; + $v['msgCount'] = 2222; + $v['tag'] = ['test', 'test2']; + } + unset($v); + + $data = ['list' => $list, 'total' => $total]; + return ResponseHelper::success($data); + } catch (\Exception $e) { + return ResponseHelper::error('获取好友列表失败:' . $e->getMessage()); + } + } + } \ No newline at end of file diff --git a/Server/application/cunkebao/controller/plan/PosterWeChatMiniProgram.php b/Server/application/cunkebao/controller/plan/PosterWeChatMiniProgram.php index a9b5d4f6..4ef33f1a 100644 --- a/Server/application/cunkebao/controller/plan/PosterWeChatMiniProgram.php +++ b/Server/application/cunkebao/controller/plan/PosterWeChatMiniProgram.php @@ -347,10 +347,31 @@ class PosterWeChatMiniProgram extends Controller function getPosterTaskData() { $id = request()->param('id'); - $task = Db::name('customer_acquisition_task') - ->where(['id' => $id, 'deleteTime' => 0]) - ->field('id,name,sceneConf,status') - ->find(); + $oldId = request()->param('oldId'); + + // 兼容旧数据:如果传了 oldId,通过 legacyId 和 isLegacy 查找 + if (!empty($oldId)) { + $task = Db::name('customer_acquisition_task') + ->where([ + 'legacyId' => $oldId, + 'isLegacy' => 1, + 'deleteTime' => 0 + ]) + ->field('id,name,sceneConf,status') + ->find(); + } elseif (!empty($id)) { + // 新数据:直接用 id 查找 + $task = Db::name('customer_acquisition_task') + ->where(['id' => $id, 'deleteTime' => 0]) + ->field('id,name,sceneConf,status') + ->find(); + } else { + return json([ + 'code' => 400, + 'message' => '任务ID不能为空' + ]); + } + if (!$task) { return json([ 'code' => 400, @@ -367,8 +388,8 @@ class PosterWeChatMiniProgram extends Controller $sceneConf = json_decode($task['sceneConf'], true); - if (isset($sceneConf['posters']['url'])) { - $posterUrl = !empty($sceneConf['posters']['url']); + if (isset($sceneConf['posters']['url']) && !empty($sceneConf['posters']['url'])) { + $posterUrl = $sceneConf['posters']['url']; } else { $posterUrl = 'https://hebbkx1anhila5yf.public.blob.vercel-storage.com/%E7%82%B9%E5%87%BB%E5%92%A8%E8%AF%A2-FTiyAMAPop2g9LvjLOLDz0VwPg3KVu.gif'; } diff --git a/Server/application/cunkebao/controller/workbench/WorkbenchAutoLikeController.php b/Server/application/cunkebao/controller/workbench/WorkbenchAutoLikeController.php index 351d7350..f9d7cb40 100644 --- a/Server/application/cunkebao/controller/workbench/WorkbenchAutoLikeController.php +++ b/Server/application/cunkebao/controller/workbench/WorkbenchAutoLikeController.php @@ -107,3 +107,4 @@ class WorkbenchAutoLikeController extends Controller } + diff --git a/Server/application/cunkebao/controller/workbench/WorkbenchController.php b/Server/application/cunkebao/controller/workbench/WorkbenchController.php index b88e484d..f15f6428 100644 --- a/Server/application/cunkebao/controller/workbench/WorkbenchController.php +++ b/Server/application/cunkebao/controller/workbench/WorkbenchController.php @@ -777,15 +777,55 @@ class WorkbenchController extends Controller // 获取流量池(当targetType=2时) if (!empty($workbench->config->trafficPools) && isset($workbench->config->targetType) && $workbench->config->targetType == 2) { - $poolList = Db::name('traffic_source_package')->alias('tsp') + $poolList = []; + $companyId = $this->request->userInfo['companyId']; + + // 检查是否包含"所有好友"(packageId=0) + $hasAllFriends = in_array(0, $workbench->config->trafficPools) || in_array('0', $workbench->config->trafficPools); + $normalPools = array_filter($workbench->config->trafficPools, function($id) { + return $id !== 0 && $id !== '0'; + }); + + // 处理"所有好友"特殊流量池 + if ($hasAllFriends) { + // 计算所有好友数量 + $wechatIds = Db::name('device')->alias('d') + ->join('(SELECT MAX(id) AS id, deviceId FROM ck_device_wechat_login WHERE companyId='.$companyId.' GROUP BY deviceId) dwl_max', 'dwl_max.deviceId = d.id') + ->join('device_wechat_login dwl', 'dwl.id = dwl_max.id') + ->where(['d.companyId' => $companyId, 'd.deleteTime' => 0]) + ->column('dwl.wechatId'); + + $allFriendsCount = 0; + if (!empty($wechatIds)) { + $allFriendsCount = Db::table('s2_wechat_friend') + ->where('ownerWechatId', 'in', $wechatIds) + ->where('isDeleted', 0) + ->count(); + } + + $poolList[] = [ + 'id' => 0, + 'name' => '所有好友', + 'description' => '展示公司下所有设备的好友', + 'pic' => '', + 'itemCount' => $allFriendsCount, + ]; + } + + // 处理普通流量池 + if (!empty($normalPools)) { + $normalPoolList = 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) + ->whereIn('tsp.id', $normalPools) ->where('tsp.isDel', 0) - ->whereIn('tsp.companyId', [$this->request->userInfo['companyId'], 0]) + ->whereIn('tsp.companyId', [$companyId, 0]) ->field('tsp.id,tsp.name,tsp.description,tsp.pic,COUNT(tspi.id) as itemCount') ->group('tsp.id') ->order('tsp.id', 'desc') ->select(); + $poolList = array_merge($poolList, $normalPoolList ?: []); + } + $workbench->config->trafficPoolsOptions = $poolList; } else { $workbench->config->trafficPoolsOptions = []; diff --git a/Server/application/cunkebao/controller/workbench/WorkbenchGroupPushController.php b/Server/application/cunkebao/controller/workbench/WorkbenchGroupPushController.php index c49356e1..e7a30d42 100644 --- a/Server/application/cunkebao/controller/workbench/WorkbenchGroupPushController.php +++ b/Server/application/cunkebao/controller/workbench/WorkbenchGroupPushController.php @@ -1591,11 +1591,11 @@ class WorkbenchGroupPushController extends Controller } } else { // 其他情况,正常处理并验证 - $contentGroupsExisting = $originalConfig ? $this->decodeJsonArray($originalConfig->contentLibraries ?? []) : []; - $contentGroupsParam = $this->getParamValue($param, 'contentGroups', null); - $contentGroups = $contentGroupsParam !== null - ? $this->extractIdList($contentGroupsParam, '内容库参数格式错误') - : $contentGroupsExisting; + $contentGroupsExisting = $originalConfig ? $this->decodeJsonArray($originalConfig->contentLibraries ?? []) : []; + $contentGroupsParam = $this->getParamValue($param, 'contentGroups', null); + $contentGroups = $contentGroupsParam !== null + ? $this->extractIdList($contentGroupsParam, '内容库参数格式错误') + : $contentGroupsExisting; // 其他情况,内容库为必填 if (empty($contentGroups)) { throw new \Exception('请至少选择一个内容库'); diff --git a/Server/application/cunkebao/controller/workbench/WorkbenchHelperController.php b/Server/application/cunkebao/controller/workbench/WorkbenchHelperController.php index 11ffdc4b..7cf8b8e0 100644 --- a/Server/application/cunkebao/controller/workbench/WorkbenchHelperController.php +++ b/Server/application/cunkebao/controller/workbench/WorkbenchHelperController.php @@ -312,3 +312,4 @@ class WorkbenchHelperController extends Controller } + diff --git a/Server/application/cunkebao/controller/workbench/WorkbenchImportContactController.php b/Server/application/cunkebao/controller/workbench/WorkbenchImportContactController.php index 6dc3b900..1956d38d 100644 --- a/Server/application/cunkebao/controller/workbench/WorkbenchImportContactController.php +++ b/Server/application/cunkebao/controller/workbench/WorkbenchImportContactController.php @@ -68,3 +68,4 @@ class WorkbenchImportContactController extends Controller } + diff --git a/Server/application/cunkebao/controller/workbench/WorkbenchMomentsController.php b/Server/application/cunkebao/controller/workbench/WorkbenchMomentsController.php index b359d9e7..eeecb142 100644 --- a/Server/application/cunkebao/controller/workbench/WorkbenchMomentsController.php +++ b/Server/application/cunkebao/controller/workbench/WorkbenchMomentsController.php @@ -124,3 +124,4 @@ class WorkbenchMomentsController extends Controller } + diff --git a/Server/application/cunkebao/controller/workbench/WorkbenchTrafficController.php b/Server/application/cunkebao/controller/workbench/WorkbenchTrafficController.php index b6c5c4ff..8f037538 100644 --- a/Server/application/cunkebao/controller/workbench/WorkbenchTrafficController.php +++ b/Server/application/cunkebao/controller/workbench/WorkbenchTrafficController.php @@ -308,3 +308,4 @@ class WorkbenchTrafficController extends Controller } + diff --git a/Server/application/job/WorkbenchGroupCreateJob.php b/Server/application/job/WorkbenchGroupCreateJob.php index 5359ab88..dd846fe2 100644 --- a/Server/application/job/WorkbenchGroupCreateJob.php +++ b/Server/application/job/WorkbenchGroupCreateJob.php @@ -162,11 +162,31 @@ class WorkbenchGroupCreateJob // 获取流量池用户(如果配置了流量池) $poolItem = []; if (!empty($config['poolGroups'])) { - $poolItem = Db::name('traffic_source_package_item') - ->whereIn('packageId', $config['poolGroups']) - ->where('isDel', 0) - ->group('identifier') - ->column('identifier'); + // 检查是否包含"所有好友"(packageId=0) + $hasAllFriends = in_array(0, $config['poolGroups']) || in_array('0', $config['poolGroups']); + $normalPools = array_filter($config['poolGroups'], function($id) { + return $id !== 0 && $id !== '0'; + }); + + // 处理"所有好友"特殊流量池 + if ($hasAllFriends) { + $companyId = $workbench->companyId ?? 0; + $allFriendsIdentifiers = $this->getAllFriendsIdentifiersByCompany($companyId); + $poolItem = array_merge($poolItem, $allFriendsIdentifiers); + } + + // 处理普通流量池 + if (!empty($normalPools)) { + $normalIdentifiers = Db::name('traffic_source_package_item') + ->whereIn('packageId', $normalPools) + ->where('isDel', 0) + ->group('identifier') + ->column('identifier'); + $poolItem = array_merge($poolItem, $normalIdentifiers); + } + + // 去重 + $poolItem = array_unique($poolItem); } // 如果既没有流量池也没有指定群组,跳过 @@ -802,6 +822,34 @@ class WorkbenchGroupCreateJob } + /** + * 获取公司下所有好友的identifier列表(特殊流量池 packageId=0) + * @param int $companyId + * @return array + */ + protected function getAllFriendsIdentifiersByCompany($companyId) + { + // 获取公司下所有设备的微信ID + $wechatIds = Db::name('device')->alias('d') + ->join('(SELECT MAX(id) AS id, deviceId FROM ck_device_wechat_login WHERE companyId='.$companyId.' GROUP BY deviceId) dwl_max', 'dwl_max.deviceId = d.id') + ->join('device_wechat_login dwl', 'dwl.id = dwl_max.id') + ->where(['d.companyId' => $companyId, 'd.deleteTime' => 0]) + ->column('dwl.wechatId'); + + if (empty($wechatIds)) { + return []; + } + + // 获取所有好友的wechatId作为identifier + $identifiers = Db::table('s2_wechat_friend') + ->where('ownerWechatId', 'in', $wechatIds) + ->where('isDeleted', 0) + ->group('wechatId') + ->column('wechatId'); + + return $identifiers ?: []; + } + /** * 记录任务开始 * @param string $jobId diff --git a/Server/application/job/WorkbenchGroupPushJob.php b/Server/application/job/WorkbenchGroupPushJob.php index b14c266b..771afc1f 100644 --- a/Server/application/job/WorkbenchGroupPushJob.php +++ b/Server/application/job/WorkbenchGroupPushJob.php @@ -515,14 +515,100 @@ class WorkbenchGroupPushJob } $companyId = $workbench->companyId ?? 0; + + // 检查是否包含"所有好友"(packageId=0) + $hasAllFriends = in_array(0, $trafficPools) || in_array('0', $trafficPools); + $normalPools = array_filter($trafficPools, function($id) { + return $id !== 0 && $id !== '0'; + }); + + $friends = []; + + // 处理"所有好友"特殊流量池 + if ($hasAllFriends) { + $allFriends = $this->getAllFriendsByCompany($companyId, $ownerWechatIds); + $friends = array_merge($friends, $allFriends); + } + + // 处理普通流量池 + if (!empty($normalPools)) { + $normalFriends = $this->getFriendsByNormalPools($normalPools, $companyId, $ownerWechatIds); + $friends = array_merge($friends, $normalFriends); + } + // 去重 + $uniqueFriends = []; + $seenIds = []; + foreach ($friends as $friend) { + $friendId = $friend['id'] ?? null; + if ($friendId && !in_array($friendId, $seenIds)) { + $seenIds[] = $friendId; + $uniqueFriends[] = $friend; + } + } + + if (empty($uniqueFriends)) { + Log::info('好友推送:流量池未匹配到好友'); + return []; + } + + return $uniqueFriends; + } + + /** + * 获取公司下所有好友(特殊流量池 packageId=0) + * @param int $companyId + * @param array $ownerWechatIds + * @return array + */ + protected function getAllFriendsByCompany($companyId, array $ownerWechatIds = []) + { + // 获取公司下所有设备的微信ID + $wechatIds = Db::name('device')->alias('d') + ->join('(SELECT MAX(id) AS id, deviceId FROM ck_device_wechat_login WHERE companyId='.$companyId.' GROUP BY deviceId) dwl_max', 'dwl_max.deviceId = d.id') + ->join('device_wechat_login dwl', 'dwl.id = dwl_max.id') + ->where(['d.companyId' => $companyId, 'd.deleteTime' => 0]) + ->column('dwl.wechatId'); + + if (empty($wechatIds)) { + return []; + } + + $query = Db::table('s2_wechat_friend')->alias('wf') + ->join(['s2_wechat_account' => 'wa'], 'wa.id = wf.wechatAccountId', 'left') + ->where('wf.ownerWechatId', 'in', $wechatIds) + ->where('wf.isDeleted', 0) + ->whereNotNull('wf.id') + ->whereNotNull('wf.wechatAccountId'); + + if (!empty($ownerWechatIds)) { + $query->whereIn('wf.wechatAccountId', $ownerWechatIds); + } + + $friends = $query + ->field('wf.id,wf.wechatAccountId,wf.wechatId,wf.ownerWechatId') + ->group('wf.id') + ->select(); + + return $friends ?: []; + } + + /** + * 根据普通流量池获取好友信息 + * @param array $packageIds + * @param int $companyId + * @param array $ownerWechatIds + * @return array + */ + protected function getFriendsByNormalPools(array $packageIds, $companyId, array $ownerWechatIds = []) + { $query = Db::name('traffic_source_package_item') ->alias('tspi') ->leftJoin('traffic_source_package tsp', 'tsp.id = tspi.packageId') ->leftJoin('traffic_pool tp', 'tp.identifier = tspi.identifier') ->leftJoin(['s2_wechat_friend' => 'wf'], 'wf.wechatId = tp.wechatId') ->leftJoin(['s2_wechat_account' => 'wa'], 'wa.id = wf.wechatAccountId') - ->whereIn('tspi.packageId', $trafficPools) + ->whereIn('tspi.packageId', $packageIds) ->where('tsp.isDel', 0) ->where('wf.isDeleted', 0) ->whereNotNull('wf.id') @@ -543,16 +629,7 @@ class WorkbenchGroupPushJob ->group('wf.id') ->select(); - if (empty($friends)) { - Log::info('好友推送:流量池未匹配到好友'); - return []; - } - - if ($friends === false) { - return []; - } - - return $friends; + return $friends ?: []; } /** diff --git a/Server/application/job/WorkbenchImportContactJob.php b/Server/application/job/WorkbenchImportContactJob.php index a8edfba7..95400d59 100644 --- a/Server/application/job/WorkbenchImportContactJob.php +++ b/Server/application/job/WorkbenchImportContactJob.php @@ -322,30 +322,91 @@ class WorkbenchImportContactJob if (empty($contactNum)) { return false; } - //过滤已删除的数据 - $packageIds = Db::name('traffic_source_package') - ->where(['isDel' => 0]) - ->whereIn('id', $pools) - ->column('id'); + // 检查是否包含"所有好友"(packageId=0) + $hasAllFriends = in_array(0, $pools) || in_array('0', $pools); + $normalPools = array_filter($pools, function($id) { + return $id !== 0 && $id !== '0'; + }); + + $data = []; + + // 处理"所有好友"特殊流量池 + if ($hasAllFriends) { + $allFriendsData = $this->getAllFriendsForImportContact($workbench, $contactNum); + $data = array_merge($data, $allFriendsData); + } + + // 处理普通流量池 + if (!empty($normalPools)) { + //过滤已删除的数据 + $packageIds = Db::name('traffic_source_package') + ->where(['isDel' => 0]) + ->whereIn('id', $normalPools) + ->column('id'); - if (empty($packageIds)) { + if (!empty($packageIds)) { + $normalData = Db::name('traffic_source_package_item')->alias('tpi') + ->join('traffic_pool tp', 'tp.identifier = tpi.identifier') + ->join('traffic_source ts', 'ts.identifier = tpi.identifier','left') + ->join('workbench_import_contact_item wici', 'wici.poolId = tp.id AND wici.workbenchId = '.$workbench->id,'left') + ->where('tp.mobile', '>',0) + ->where('wici.id','null') + ->whereIn('tpi.packageId',$packageIds) + ->field('tp.id,tpi.packageId,tp.mobile as phone,ts.name') + ->order('tp.id DESC') + ->group('tpi.identifier') + ->limit($contactNum) + ->select(); + $data = array_merge($data, $normalData ?: []); + } + } + + if (empty($data)) { return false; } - - $data = Db::name('traffic_source_package_item')->alias('tpi') - ->join('traffic_pool tp', 'tp.identifier = tpi.identifier') - ->join('traffic_source ts', 'ts.identifier = tpi.identifier','left') - ->join('workbench_import_contact_item wici', 'wici.poolId = tp.id AND wici.workbenchId = '.$workbench->id,'left') - ->where('tp.mobile', '>',0) - ->where('wici.id','null') - ->whereIn('tpi.packageId',$packageIds) - ->field('tp.id,tpi.packageId,tp.mobile as phone,ts.name') - ->order('tp.id DESC') - ->group('tpi.identifier') - ->limit($contactNum) - ->select(); + return $data; } + + /** + * 获取"所有好友"流量池的联系人数据(用于通讯录导入) + * @param Workbench $workbench + * @param int $limit + * @return array + */ + protected function getAllFriendsForImportContact($workbench, $limit) + { + $companyId = $workbench->companyId ?? 0; + + // 获取公司下所有设备的微信ID + $wechatIds = Db::name('device')->alias('d') + ->join('(SELECT MAX(id) AS id, deviceId FROM ck_device_wechat_login WHERE companyId='.$companyId.' GROUP BY deviceId) dwl_max', 'dwl_max.deviceId = d.id') + ->join('device_wechat_login dwl', 'dwl.id = dwl_max.id') + ->where(['d.companyId' => $companyId, 'd.deleteTime' => 0]) + ->column('dwl.wechatId'); + + if (empty($wechatIds)) { + return []; + } + + // 从 s2_wechat_friend 表获取好友,然后关联 traffic_pool 表获取手机号 + $data = Db::table('s2_wechat_friend')->alias('wf') + ->join('traffic_pool tp', 'tp.wechatId = wf.wechatId', 'left') + ->join('traffic_source ts', 'ts.identifier = tp.identifier', 'left') + ->join('workbench_import_contact_item wici', 'wici.poolId = tp.id AND wici.workbenchId = '.$workbench->id, 'left') + ->where('wf.ownerWechatId', 'in', $wechatIds) + ->where('wf.isDeleted', 0) + ->where('tp.mobile', '>', 0) + ->where('wici.id', 'null') + ->field('tp.id,tp.mobile as phone,ts.name') + ->field(Db::raw('0 as packageId')) // 标记为"所有好友"流量池 + ->order('tp.id DESC') + ->group('tp.identifier') + ->limit($limit) + ->select(); + + return $data ?: []; + } /** * 记录任务开始