Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | import { Injectable, BadRequestException } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model, Types } from 'mongoose'; import { AgentAvailability, SupportGroup, SupportQueue, Ticket } from '../schemas'; import { EventBusService } from '../../../platform/events/event-bus.service'; @Injectable() export class HelpdeskRoutingService { constructor( @InjectModel(Ticket.name) private readonly ticketModel: Model<Ticket>, @InjectModel(SupportGroup.name) private readonly groupModel: Model<SupportGroup>, @InjectModel(SupportQueue.name) private readonly queueModel: Model<SupportQueue>, @InjectModel(AgentAvailability.name) private readonly availabilityModel: Model<AgentAvailability>, private readonly eventBus: EventBusService ) {} async registerAgent(tenantId: string, agentId: string, skills: string[], maxCapacity = 5): Promise<AgentAvailability> { let avail = await this.availabilityModel.findOne({ tenantId, agentId }).exec(); if (!avail) { avail = new this.availabilityModel({ tenantId, agentId, skills, maxCapacity, isOnline: true, activeTicketsCount: 0 }); } else { avail.skills = skills; avail.maxCapacity = maxCapacity; } return avail.save(); } async getEligibleAgents(tenantId: string, queueId: string, requiredSkills: string[] = []): Promise<AgentAvailability[]> { const queue = await this.queueModel.findOne({ _id: queueId, tenantId }).exec(); if (!queue || !queue.routingGroupId) return []; const group = await this.groupModel.findById(queue.routingGroupId).exec(); if (!group) return []; // Find online members within capacity const agents = await this.availabilityModel.find({ tenantId, agentId: { $in: group.memberAgentIds }, isOnline: true, $expr: { $lt: ['$activeTicketsCount', '$maxCapacity'] } }).exec(); // Filter by required skills if (requiredSkills.length > 0) { return agents.filter(agent => requiredSkills.every(skill => agent.skills.includes(skill)) ); } return agents; } async assignTicket(tenantId: string, ticketId: string, requiredSkills: string[] = []): Promise<Ticket> { const ticket = await this.ticketModel.findOne({ _id: ticketId, tenantId }).exec(); if (!ticket) throw new BadRequestException('Ticket not found'); if (!ticket.queueId) throw new BadRequestException('Ticket has no queue assigned'); const eligible = await this.getEligibleAgents(tenantId, ticket.queueId.toString(), requiredSkills); if (eligible.length === 0) { // Fallback: remains unassigned for manual dispatch return ticket; } // Least assigned logic eligible.sort((a, b) => a.activeTicketsCount - b.activeTicketsCount); const assignedAgent = eligible[0]; ticket.assignedAgentId = assignedAgent.agentId; ticket.status = 'assigned'; await ticket.save(); // Increment agent active count assignedAgent.activeTicketsCount += 1; await assignedAgent.save(); await this.eventBus.publish('helpdesk.ticket.assigned.v1', { ticketId: ticketId, ticketNumber: ticket.ticketNumber, tenantId, agentId: assignedAgent.agentId }, tenantId); return ticket; } async setAgentAvailability(tenantId: string, agentId: string, isOnline: boolean): Promise<AgentAvailability> { const avail = await this.availabilityModel.findOne({ tenantId, agentId }).exec(); if (!avail) throw new BadRequestException('Agent profile not found'); avail.isOnline = isOnline; return avail.save(); } } |