150 lines
5.0 KiB
PHP
150 lines
5.0 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
namespace app\common\service;
|
||
|
|
|
||
|
|
use app\admin\model\split\Number;
|
||
|
|
use app\admin\model\split\Ticket;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 工单与号码业务规则(单号上限、下号比率、时间窗口、完成量自动开关)
|
||
|
|
*/
|
||
|
|
class SplitTicketRuleService
|
||
|
|
{
|
||
|
|
/**
|
||
|
|
* 同步后应用全部规则并写回工单/号码
|
||
|
|
*/
|
||
|
|
public function applyAfterSync(Ticket $ticket, int $completeCount): void
|
||
|
|
{
|
||
|
|
$this->applyNumberRules($ticket);
|
||
|
|
$this->applyTicketStatusRules($ticket, $completeCount);
|
||
|
|
$this->cascadeTicketClosedToNumbers($ticket);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 同步流程:工单因时间/完成量等原因关闭时,联动关闭非手动号码
|
||
|
|
*/
|
||
|
|
public function cascadeTicketClosedToNumbers(Ticket $ticket): void
|
||
|
|
{
|
||
|
|
if ((string) ($ticket['status'] ?? 'hidden') !== 'hidden') {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
Number::where('admin_id', (int) $ticket['admin_id'])
|
||
|
|
->where('split_link_id', (int) $ticket['split_link_id'])
|
||
|
|
->where('ticket_name', (string) $ticket['ticket_name'])
|
||
|
|
->where('manual_manage', 0)
|
||
|
|
->update([
|
||
|
|
'status' => 'hidden',
|
||
|
|
'updatetime' => time(),
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 手动切换工单状态时,联动非手动管理的号码(开→全开,关→全关)
|
||
|
|
*/
|
||
|
|
public function syncNumbersWithTicketStatus(Ticket $ticket): void
|
||
|
|
{
|
||
|
|
$ticketStatus = (string) ($ticket['status'] ?? 'hidden');
|
||
|
|
Number::where('admin_id', (int) $ticket['admin_id'])
|
||
|
|
->where('split_link_id', (int) $ticket['split_link_id'])
|
||
|
|
->where('ticket_name', (string) $ticket['ticket_name'])
|
||
|
|
->where('manual_manage', 0)
|
||
|
|
->update([
|
||
|
|
'status' => $ticketStatus,
|
||
|
|
'updatetime' => time(),
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 单号上限、下号比率
|
||
|
|
*/
|
||
|
|
public function applyNumberRules(Ticket $ticket): void
|
||
|
|
{
|
||
|
|
$orderLimit = (int) ($ticket['order_limit'] ?? 0);
|
||
|
|
$assignRatio = (int) ($ticket['assign_ratio'] ?? 0);
|
||
|
|
|
||
|
|
$numbers = Number::where('admin_id', (int) $ticket['admin_id'])
|
||
|
|
->where('split_link_id', (int) $ticket['split_link_id'])
|
||
|
|
->where('ticket_name', (string) $ticket['ticket_name'])
|
||
|
|
->where('manual_manage', 0)
|
||
|
|
->select();
|
||
|
|
|
||
|
|
foreach ($numbers as $number) {
|
||
|
|
$visitCount = (int) $number['visit_count'];
|
||
|
|
$inboundCount = (int) $number['inbound_count'];
|
||
|
|
$lastVisit = (int) ($number['last_sync_visit_count'] ?? 0);
|
||
|
|
$lastInbound = (int) ($number['last_sync_inbound_count'] ?? 0);
|
||
|
|
$streak = (int) ($number['no_inbound_click_streak'] ?? 0);
|
||
|
|
|
||
|
|
if ($visitCount > $lastVisit && $inboundCount <= $lastInbound) {
|
||
|
|
$streak += ($visitCount - $lastVisit);
|
||
|
|
} elseif ($inboundCount > $lastInbound) {
|
||
|
|
$streak = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
$status = (string) $number['status'];
|
||
|
|
if ($orderLimit > 0 && $inboundCount > $orderLimit) {
|
||
|
|
$status = 'hidden';
|
||
|
|
}
|
||
|
|
if ($assignRatio > 0 && $streak >= $assignRatio) {
|
||
|
|
$status = 'hidden';
|
||
|
|
}
|
||
|
|
|
||
|
|
Number::where('id', (int) $number['id'])->update([
|
||
|
|
'no_inbound_click_streak' => $streak,
|
||
|
|
'last_sync_visit_count' => $visitCount,
|
||
|
|
'last_sync_inbound_count' => $inboundCount,
|
||
|
|
'status' => $status,
|
||
|
|
'updatetime' => time(),
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 完成量、时间窗口决定工单开关
|
||
|
|
*/
|
||
|
|
public function applyTicketStatusRules(Ticket $ticket, int $completeCount): void
|
||
|
|
{
|
||
|
|
$status = $this->resolveTicketStatus($ticket, $completeCount);
|
||
|
|
if ($status !== (string) $ticket['status']) {
|
||
|
|
Ticket::where('id', (int) $ticket['id'])->update([
|
||
|
|
'status' => $status,
|
||
|
|
'updatetime' => time(),
|
||
|
|
]);
|
||
|
|
$ticket['status'] = $status;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 是否处于允许同步/开启的时间窗口
|
||
|
|
*/
|
||
|
|
public function isWithinTimeWindow(Ticket $ticket, ?int $now = null): bool
|
||
|
|
{
|
||
|
|
$now = $now ?? time();
|
||
|
|
$start = $ticket['start_time'] ?? null;
|
||
|
|
$end = $ticket['end_time'] ?? null;
|
||
|
|
if ($start !== null && $start !== '' && (int) $start > 0 && $now < (int) $start) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if ($end !== null && $end !== '' && (int) $end > 0 && $now > (int) $end) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
private function resolveTicketStatus(Ticket $ticket, int $completeCount): string
|
||
|
|
{
|
||
|
|
if (!$this->isWithinTimeWindow($ticket)) {
|
||
|
|
return 'hidden';
|
||
|
|
}
|
||
|
|
|
||
|
|
$ticketTotal = (int) ($ticket['ticket_total'] ?? 0);
|
||
|
|
if ($ticketTotal > 0) {
|
||
|
|
return $completeCount >= $ticketTotal ? 'hidden' : 'normal';
|
||
|
|
}
|
||
|
|
|
||
|
|
return 'normal';
|
||
|
|
}
|
||
|
|
}
|