修复随机号码前先按权重随机工单,并修复降权号码筛选与清理
This commit is contained in:
@@ -6,14 +6,17 @@ namespace app\common\service;
|
||||
|
||||
use app\admin\model\split\Link;
|
||||
use app\admin\model\split\Number;
|
||||
use app\admin\model\split\Ticket;
|
||||
use think\Collection;
|
||||
|
||||
/**
|
||||
* 分流链接落地页:查链、跨工单合并选号、拼接跳转 URL、访问计数
|
||||
* 分流链接落地页:查链、选号、拼接跳转 URL、访问计数
|
||||
*
|
||||
* 号码池为同一 split_link_id 下全部 status=normal 的号码;
|
||||
* 优先选用 ratio_deferred=0 的号码,全部触线降权时回退至降权池仍可轮转。
|
||||
* random_shuffle 关闭时严格轮转,开启时每次访问随机选号。
|
||||
* 号码池:同一 split_link_id 下 status=normal,且所属工单未关闭(ticket.status≠hidden)的号码;
|
||||
* 优先选用 ratio_deferred=0,全部触线降权时回退至降权池。
|
||||
*
|
||||
* random_shuffle=0:跨工单合并池,全局严格轮转;
|
||||
* random_shuffle=1:先按各工单开启号码数量比例随机选工单,再在该工单内随机选号。
|
||||
*/
|
||||
class SplitRedirectService
|
||||
{
|
||||
@@ -55,20 +58,17 @@ class SplitRedirectService
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var Collection<int, Number>|array<int, Number> $numbers */
|
||||
$numbers = Number::where('split_link_id', (int) $link['id'])
|
||||
->where('status', 'normal')
|
||||
->order('id', 'asc')
|
||||
->field('id,number,number_type,number_type_custom,ratio_deferred')
|
||||
->select();
|
||||
|
||||
$list = $numbers instanceof Collection ? $numbers->all() : (array) $numbers;
|
||||
$linkId = (int) $link->getAttr('id');
|
||||
$list = $this->loadEligibleNumbers($linkId);
|
||||
if ($list === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$linkId = (int) $link->getAttr('id');
|
||||
$picked = $this->pickNumberRow($linkId, $list);
|
||||
if (SplitNumberWeighService::isRandomShuffleEnabled($linkId)) {
|
||||
$picked = $this->pickNumberRowRandomByTicketWeight($linkId, $list);
|
||||
} else {
|
||||
$picked = $this->pickNumberRow($linkId, $list);
|
||||
}
|
||||
if ($picked === null) {
|
||||
return null;
|
||||
}
|
||||
@@ -103,10 +103,117 @@ class SplitRedirectService
|
||||
return $redirectUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 可参与选号的号码:status=normal,且 ticket_name 不属于已关闭工单
|
||||
*
|
||||
* @return list<Number|array<string, mixed>>
|
||||
*/
|
||||
private function loadEligibleNumbers(int $linkId): array
|
||||
{
|
||||
if ($linkId <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/** @var list<string> $hiddenTicketNames */
|
||||
$hiddenTicketNames = Ticket::where('split_link_id', $linkId)
|
||||
->where('status', 'hidden')
|
||||
->column('ticket_name');
|
||||
$hiddenTicketNames = array_values(array_unique(array_filter(array_map(
|
||||
static function ($name): string {
|
||||
return trim((string) $name);
|
||||
},
|
||||
$hiddenTicketNames
|
||||
))));
|
||||
|
||||
$query = Number::where('split_link_id', $linkId)
|
||||
->where('status', 'normal')
|
||||
->order('id', 'asc')
|
||||
->field('id,number,number_type,number_type_custom,ratio_deferred,ticket_name');
|
||||
|
||||
if ($hiddenTicketNames !== []) {
|
||||
$query->where('ticket_name', 'not in', $hiddenTicketNames);
|
||||
}
|
||||
|
||||
$numbers = $query->select();
|
||||
return $numbers instanceof Collection ? $numbers->all() : (array) $numbers;
|
||||
}
|
||||
|
||||
/**
|
||||
* random_shuffle=1:先按工单开启号码数加权随机选工单,再在该工单内选号
|
||||
*
|
||||
* @param list<Number|array<string, mixed>> $rows
|
||||
* @return array<string, mixed>|Number|null
|
||||
*/
|
||||
private function pickNumberRowRandomByTicketWeight(int $linkId, array $rows)
|
||||
{
|
||||
$groups = $this->groupNumbersByTicket($rows);
|
||||
if ($groups === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$ticketKey = $this->pickTicketGroupKeyWeighted($groups);
|
||||
if ($ticketKey === null || !isset($groups[$ticketKey])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->pickNumberRow($linkId, $groups[$ticketKey]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<Number|array<string, mixed>> $rows
|
||||
* @return array<string, list<Number|array<string, mixed>>>
|
||||
*/
|
||||
private function groupNumbersByTicket(array $rows): array
|
||||
{
|
||||
/** @var array<string, list<Number|array<string, mixed>>> $groups */
|
||||
$groups = [];
|
||||
foreach ($rows as $row) {
|
||||
$name = is_array($row)
|
||||
? trim((string) ($row['ticket_name'] ?? ''))
|
||||
: trim((string) $row->getAttr('ticket_name'));
|
||||
$key = $name !== '' ? $name : '__manual__';
|
||||
$groups[$key][] = $row;
|
||||
}
|
||||
|
||||
return $groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按各分组号码数量加权随机选一个工单(分组 key)
|
||||
*
|
||||
* @param array<string, list<Number|array<string, mixed>>> $groups
|
||||
*/
|
||||
private function pickTicketGroupKeyWeighted(array $groups): ?string
|
||||
{
|
||||
$total = 0;
|
||||
foreach ($groups as $rows) {
|
||||
$total += count($rows);
|
||||
}
|
||||
if ($total <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$keys = array_keys($groups);
|
||||
if (count($keys) === 1) {
|
||||
return $keys[0];
|
||||
}
|
||||
|
||||
$r = random_int(1, $total);
|
||||
$acc = 0;
|
||||
foreach ($groups as $key => $rows) {
|
||||
$acc += count($rows);
|
||||
if ($r <= $acc) {
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
|
||||
return $keys[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* 双层选号:优先 ratio_deferred=0,否则回退至降权池
|
||||
*
|
||||
* @param array<int, array<string, mixed>|Number> $rows
|
||||
* @param list<Number|array<string, mixed>> $rows
|
||||
* @return array<string, mixed>|Number|null
|
||||
*/
|
||||
private function pickNumberRow(int $linkId, array $rows)
|
||||
|
||||
Reference in New Issue
Block a user