Files
links/application/common/service/SplitTicketRuleService.php
T

150 lines
5.0 KiB
PHP
Raw Normal View History

2026-06-09 03:36:30 +08:00
<?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';
}
}