Files
cunkebao_v3/Server/application/cunkebao/controller/TrafficController.php
wong b2e84a2259 1、新增一个所有好友的流量池
2、旧版场景获客数据迁移
3、场景获客功能兼容旧版数据
2026-01-07 10:41:39 +08:00

526 lines
19 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace app\cunkebao\controller;
use app\common\model\TrafficSourcePackage;
use app\common\model\TrafficSourcePackageItem;
use library\ResponseHelper;
use think\Db;
use app\cunkebao\controller\RFMController;
class TrafficController extends BaseController
{
/**
* 流量池包
* @return \think\response\Json
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function getPackage()
{
$page = $this->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());
}
}
}