196 lines
6.2 KiB
PHP
196 lines
6.2 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->applyTicketStatusRules($ticket, $completeCount);
|
|
$fresh = Ticket::get((int) $ticket['id']);
|
|
if ($fresh) {
|
|
if ((string) $fresh['status'] === 'hidden') {
|
|
$this->cascadeTicketClosedToNumbers($fresh);
|
|
}
|
|
$this->applyNumberRules($fresh);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 同步流程:工单因时间/完成量等原因关闭时,联动关闭非手动号码
|
|
*/
|
|
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');
|
|
if ($ticketStatus === 'hidden') {
|
|
$this->cascadeTicketClosedToNumbers($ticket);
|
|
return;
|
|
}
|
|
$this->applyNumberRules($ticket);
|
|
}
|
|
|
|
/**
|
|
* 工单处于开启状态时,将云控在线的非手动号码设为开启
|
|
*
|
|
* @deprecated 请使用 applyNumberRules(会校验单号上限/下号比率)
|
|
*/
|
|
public function syncOnlineNumbersWhenTicketOpen(Ticket $ticket): void
|
|
{
|
|
$this->applyNumberRules($ticket);
|
|
}
|
|
|
|
/**
|
|
* 单号上限、下号比率、云控在线状态、工单开关综合决定号码状态
|
|
*/
|
|
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'])
|
|
->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;
|
|
}
|
|
|
|
$update = [
|
|
'no_inbound_click_streak' => $streak,
|
|
'last_sync_visit_count' => $visitCount,
|
|
'last_sync_inbound_count' => $inboundCount,
|
|
'updatetime' => time(),
|
|
];
|
|
|
|
// 手动管理(含用户手动关闭)的号码:仅更新统计字段,不改状态
|
|
if ((int) $number['manual_manage'] === 1) {
|
|
Number::where('id', (int) $number['id'])->update($update);
|
|
continue;
|
|
}
|
|
|
|
$update['status'] = $this->resolveAutomatedStatus(
|
|
$ticket,
|
|
(string) ($number['platform_status'] ?? 'unknown'),
|
|
$inboundCount,
|
|
$streak,
|
|
$orderLimit,
|
|
$assignRatio
|
|
);
|
|
|
|
Number::where('id', (int) $number['id'])->update($update);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 非手动管理号码的自动开关判定
|
|
*/
|
|
private function resolveAutomatedStatus(
|
|
Ticket $ticket,
|
|
string $platformStatus,
|
|
int $inboundCount,
|
|
int $streak,
|
|
int $orderLimit,
|
|
int $assignRatio
|
|
): string {
|
|
if ((string) ($ticket['status'] ?? 'hidden') !== 'normal') {
|
|
return 'hidden';
|
|
}
|
|
if ($platformStatus !== 'online') {
|
|
return 'hidden';
|
|
}
|
|
if ($orderLimit > 0 && $inboundCount >= $orderLimit) {
|
|
return 'hidden';
|
|
}
|
|
if ($assignRatio > 0 && $streak >= $assignRatio) {
|
|
return 'hidden';
|
|
}
|
|
|
|
return 'normal';
|
|
}
|
|
|
|
/**
|
|
* 完成量、时间窗口决定工单开关(定时与手动同步均适用)
|
|
*/
|
|
public function applyTicketStatusRules(Ticket $ticket, int $completeCount): void
|
|
{
|
|
$status = $this->resolveTicketStatus($ticket, $completeCount);
|
|
if ($status !== (string) ($ticket['status'] ?? 'hidden')) {
|
|
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';
|
|
}
|
|
}
|