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'; } }