request->param('page', 1); $limit = $this->request->param('limit', 10); $keyword = $this->request->param('keyword', ''); $companyId = $this->getUserInfo('companyId'); $package = Db::name('traffic_source_package')->alias('tsp') ->join('traffic_source_package_item tspi', 'tspi.packageId=tsp.id', 'left') ->whereIn('tsp.companyId', [$companyId, 0]) ->field('tsp.id,tsp.name,tsp.description,tsp.pic,tsp.isSys as type,tsp.createTime,count(tspi.id) as num') ->group('tsp.id'); if (!empty($keyword)) { $package->where('tsp.name|tsp.description', 'like', '%' . $keyword . '%'); } $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) { $v['createTime'] = !empty($v['createTime']) ? formatRelativeTime($v['createTime']) : ''; } else { $v['createTime'] = ''; } // RFM 评分(示例:以创建时间近似最近活跃,num 近似频次;金额若无则为 0) $recencyDays = isset($v['createTime']) && is_numeric($v['createTime']) ? floor((time() - (int)$v['createTime']) / 86400) : null; // 如果上方被格式化为文本,则尝试从原始结果集取原值 if (!is_numeric($recencyDays) || $recencyDays === null) { $rawCreate = isset($list[$k]['createTime']) ? $list[$k]['createTime'] : null; $recencyDays = is_numeric($rawCreate) ? floor((time() - (int)$rawCreate) / 86400) : 9999; } $frequency = (int)($v['num'] ?? 0); $monetary = (float)($v['monetary'] ?? 0); $scores = RFMController::calcRfmScores($recencyDays, $frequency, $monetary); $v['R'] = $scores['R']; $v['F'] = $scores['F']; $v['M'] = $scores['M']; $v['RFM'] = $scores['R'] + $scores['F'] + $scores['M']; } unset($v); $data = [ 'total' => $total, 'list' => $list, ]; return ResponseHelper::success($data); } /** * 添加流量池 * @return \think\response\Json * @throws \Exception */ public function addPackage() { $packageName = $this->request->param('packageName', ''); $description = $this->request->param('description', ''); $pic = $this->request->param('pic', ''); $companyId = $this->getUserInfo('companyId'); $userId = $this->getUserInfo('id'); if (empty($packageName)) { return ResponseHelper::error('流量池名称不能为空'); } $package = TrafficSourcePackage::where(['isDel' => 0, 'name' => $packageName]) ->whereIn('companyId', [$companyId, 0]) ->field('id,name') ->find(); if (!empty($package)) { return ResponseHelper::error('该流量池名称已存在'); } $packageId = TrafficSourcePackage::insertGetId([ 'userId' => $userId, 'companyId' => $companyId, 'name' => $packageName, 'description' => $description, 'pic' => $pic, 'matchingRules' => json_encode([]), 'createTime' => time(), 'isDel' => 0, ]); if (!empty($packageId)) { return ResponseHelper::success($packageId, '该流量添加成功'); } else { return ResponseHelper::error('该流量添加失败'); } } /** * 编辑流量池 * @return \think\response\Json * @throws \Exception */ public function editPackage() { $packageId = $this->request->param('packageId', ''); $packageName = $this->request->param('packageName', ''); $description = $this->request->param('description', ''); $pic = $this->request->param('pic', ''); $companyId = $this->getUserInfo('companyId'); $userId = $this->getUserInfo('id'); if (empty($packageId)) { return ResponseHelper::error('流量池ID不能为空'); } // 禁止编辑"所有好友"特殊流量池 if ($packageId === '0' || $packageId === 0) { return ResponseHelper::error('"所有好友"流量池不允许编辑'); } if (empty($packageName)) { return ResponseHelper::error('流量池名称不能为空'); } // 检查流量池是否存在且属于当前公司 $package = TrafficSourcePackage::where(['id' => $packageId, 'isDel' => 0]) ->whereIn('companyId', [$companyId, 0]) ->find(); if (empty($package)) { return ResponseHelper::error('流量池不存在或已删除'); } // 检查系统流量池是否可编辑 if ($package['isSys'] == 1) { return ResponseHelper::error('系统流量池不允许编辑'); } // 检查名称是否重复(排除当前记录) $existPackage = TrafficSourcePackage::where(['isDel' => 0, 'name' => $packageName]) ->whereIn('companyId', [$companyId, 0]) ->where('id', '<>', $packageId) ->field('id,name') ->find(); if (!empty($existPackage)) { return ResponseHelper::error('该流量池名称已存在'); } // 更新流量池信息 $updateData = [ 'name' => $packageName, 'updateTime' => time(), ]; // 更新描述字段(允许为空) $updateData['description'] = $description; // 更新图片字段(允许为空) $updateData['pic'] = $pic; $result = TrafficSourcePackage::where('id', $packageId)->update($updateData); if ($result !== false) { return ResponseHelper::success($packageId, '流量池编辑成功'); } else { return ResponseHelper::error('流量池编辑失败'); } } /** * 删除流量池(假删除) * @return \think\response\Json * @throws \Exception */ public function deletePackage() { $packageId = $this->request->param('packageId', ''); $companyId = $this->getUserInfo('companyId'); if (empty($packageId)) { return ResponseHelper::error('流量池ID不能为空'); } // 禁止删除"所有好友"特殊流量池 if ($packageId === '0' || $packageId === 0) { return ResponseHelper::error('"所有好友"流量池不允许删除'); } // 检查流量池是否存在且属于当前公司 $package = TrafficSourcePackage::where(['id' => $packageId, 'isDel' => 0]) ->whereIn('companyId', [$companyId, 0]) ->find(); if (empty($package)) { return ResponseHelper::error('流量池不存在或已删除'); } // 检查系统流量池是否可删除 if ($package['isSys'] == 1) { return ResponseHelper::error('系统流量池不允许删除'); } // 开启事务 Db::startTrans(); try { // 执行流量池假删除 $result = TrafficSourcePackage::where('id', $packageId)->update([ 'isDel' => 1, 'deleteTime' => time() ]); if ($result === false) { throw new \Exception('流量池删除失败'); } // 删除流量池内容(TrafficSourcePackageItem)假删除 $itemResult = TrafficSourcePackageItem::where([ 'packageId' => $packageId, 'companyId' => $companyId, 'isDel' => 0 ])->update([ 'isDel' => 1, 'deleteTime' => time() ]); // 提交事务 Db::commit(); return ResponseHelper::success($packageId, '流量池及内容删除成功'); } catch (\Exception $e) { // 回滚事务 Db::rollback(); return ResponseHelper::error('删除失败:' . $e->getMessage()); } } /** * 获取流量池详情 * @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 * @throws \Exception */ public function getTrafficPoolList() { $page = $this->request->param('page', 1); $limit = $this->request->param('limit', 10); $keyword = $this->request->param('keyword', ''); $packageId = $this->request->param('packageId', ''); $companyId = $this->getUserInfo('companyId'); $userId = $this->getUserInfo('id'); 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('流量包不存在或已删除'); } $where = [ ['tspi.companyId', '=', $companyId], ['tspi.packageId', '=', $packageId], ]; if (!empty($keyword)) { $where[] = ['wa.nickname|wa.phone|wa.alias|wa.wechatId|p.mobile|p.identifier', 'like', '%' . $keyword . '%']; } $query = TrafficSourcePackageItem::alias('tspi') ->field( [ 'p.id', 'p.identifier', 'p.mobile', 'p.wechatId', 'tspi.companyId', 'wa.nickname', 'wa.avatar', 'wa.gender', 'wa.phone', 'wa.alias' ] ) ->join('traffic_pool p', 'p.identifier=tspi.identifier', 'left') ->join('wechat_account wa', 'tspi.identifier=wa.wechatId', 'left') ->where($where); $query->order('tspi.id DESC,p.id DESC')->group('p.identifier'); $list = $query->page($page, $limit)->select(); $total = $query->count(); 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, $v['companyId']]) ->column('p.name'); $v['packages'] = $package; $v['phone'] = !empty($v['phone']) ? $v['phone'] : $v['mobile']; unset($v['mobile']); $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'] = 3; $v['msgCount'] = 3 ; $v['tag'] = ['test', 'test2']; } unset($v); $data = ['list' => $list, 'total' => $total]; 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()); } } }