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(); $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 (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不能为空'); } // 检查流量池是否存在且属于当前公司 $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 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)) { return ResponseHelper::error('流量包id不能为空'); } $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()->toArray(); $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'] = 2222; $v['msgCount'] = 2222; $v['tag'] = ['test', 'test2']; } unset($v); $data = ['list' => $list, 'total' => $total]; return ResponseHelper::success($data); } }