162 lines
5.1 KiB
PHP
162 lines
5.1 KiB
PHP
<?php
|
||
|
||
namespace app\command;
|
||
|
||
use Symfony\Component\Console\Command\Command;
|
||
use Symfony\Component\Console\Input\InputInterface;
|
||
use Symfony\Component\Console\Output\OutputInterface;
|
||
use support\Db;
|
||
use support\Redis;
|
||
use support\exception\BusinessException;
|
||
use Webman\RedisQueue\Redis as RedisQueueRedis;
|
||
|
||
/**
|
||
* 轮询分配 inquiry_log 里的询盘给业务员,并推送到 CRM 队列
|
||
*/
|
||
class InquiryAssignInquiryCommand extends Command
|
||
{
|
||
protected static $defaultName = 'iq-assign';
|
||
protected static $defaultDescription = '轮询分配询盘给业务员,并推送到 CRM 队列';
|
||
|
||
/**
|
||
* 参与轮询的业务员 ID 列表(请按你想要的顺序填写)
|
||
* 比如:销售1=186,销售2=233...
|
||
*/
|
||
protected const SALES_USER_IDS = [
|
||
63,
|
||
64,
|
||
67,
|
||
93,
|
||
108,
|
||
132,
|
||
143,
|
||
145,
|
||
157,
|
||
160,
|
||
168,
|
||
172,
|
||
173,
|
||
191,
|
||
193,
|
||
194,
|
||
195,
|
||
196,
|
||
197
|
||
];
|
||
|
||
/**
|
||
* 轮询分配的分组 key(同一分组共用一个轮询指针)
|
||
*/
|
||
protected const ROUND_ROBIN_GROUP_KEY = 'fb_inquiry';
|
||
|
||
/**
|
||
* 每次脚本最多分配多少条
|
||
*/
|
||
protected const BATCH_LIMIT = 10;
|
||
|
||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||
{
|
||
// 业务员列表检查
|
||
$userIds = array_values(array_filter(
|
||
array_map('intval', static::SALES_USER_IDS),
|
||
fn($v) => $v > 0
|
||
));
|
||
|
||
if (empty($userIds)) {
|
||
throw new BusinessException('SALES_USER_IDS 业务员列表不能为空,请先在命令中配置.');
|
||
}
|
||
|
||
// 取出“待分配”的询盘(你可以根据自己系统调整条件)
|
||
// 当前策略:status = 0 且 recipient = 0 的视为未分配
|
||
$inquiries = Db::table('inquiry_log')
|
||
->where('enter_user_id', 186)
|
||
->where('channel', 10)
|
||
->where('status', 0)
|
||
->where('recipient', 0)
|
||
->where('id', '>', 34426)
|
||
->orderBy('id')
|
||
->limit(static::BATCH_LIMIT)
|
||
->get();
|
||
|
||
if ($inquiries->isEmpty()) {
|
||
$output->writeln('<comment>没有需要分配的询盘(status=0 且 recipient=0)</comment>');
|
||
return self::SUCCESS;
|
||
}
|
||
|
||
$now = time();
|
||
$count = 0;
|
||
|
||
foreach ($inquiries as $row) {
|
||
$inquiryId = (int)$row->id;
|
||
sleep(1);
|
||
// 轮询取出一个业务员 ID
|
||
$recipientUserId = $this->nextRecipientId($userIds, static::ROUND_ROBIN_GROUP_KEY);
|
||
|
||
// 对每一条记录单独事务,避免单条异常拖垮全部
|
||
Db::transaction(function () use ($inquiryId, $recipientUserId, $now, $output, &$count) {
|
||
// 1)更新 inquiry_log
|
||
$affected = Db::table('inquiry_log')
|
||
->where('id', $inquiryId)
|
||
->update([
|
||
'recipient' => $recipientUserId,
|
||
'status' => 2,
|
||
'r_time' => $now,
|
||
't_time' => $now,
|
||
'updated_at' => $now,
|
||
]);
|
||
|
||
if ($affected === 0) {
|
||
// 被其他进程抢先更新也有可能,这里只打印提示不中断整个批次
|
||
$output->writeln("<comment>询盘 {$inquiryId} 未更新(可能已被处理)</comment>");
|
||
return;
|
||
}
|
||
|
||
// 2)构造队列 payload(后续 CRM 消费时至少知道 ID)
|
||
$res = [
|
||
'id' => $inquiryId,
|
||
'recipient' => $recipientUserId,
|
||
'dispatched_at' => $now,
|
||
'source' => 'fb_leads_inquiry',
|
||
];
|
||
|
||
// 3)推送到 CRM 队列
|
||
RedisQueueRedis::send('inquiry-to-crm-new', $res);
|
||
|
||
$count++;
|
||
$output->writeln("<info>已分配询盘 {$inquiryId} 给业务员 {$recipientUserId},并推送到队列</info>");
|
||
});
|
||
}
|
||
|
||
$output->writeln("<info>本次共分配成功 {$count} 条询盘</info>");
|
||
|
||
return self::SUCCESS;
|
||
}
|
||
|
||
/**
|
||
* 轮询获取“下一位业务员 ID”
|
||
*
|
||
* @param int[] $userIds
|
||
* @param string $groupKey
|
||
* @return int
|
||
* @throws BusinessException
|
||
*/
|
||
protected function nextRecipientId(array $userIds, string $groupKey = 'default'): int
|
||
{
|
||
$userIds = array_values(array_filter(array_map('intval', $userIds), fn($v) => $v > 0));
|
||
if (empty($userIds)) {
|
||
throw new BusinessException('轮询分配失败:业务员列表为空');
|
||
}
|
||
|
||
$count = count($userIds);
|
||
$key = 'inquiry:assign:round_robin:' . $groupKey;
|
||
|
||
// Redis INCR 是原子操作,适合并发下使用
|
||
$val = Redis::incr($key); // 第一次会从 0 变成 1
|
||
|
||
// 换算为从 0 开始的下标
|
||
$index = ($val - 1) % $count;
|
||
|
||
return $userIds[$index];
|
||
}
|
||
}
|