monitor/api/src/Controller/AgentController.php
fds2610-bot 9426406ee5 Sprachaufnahme: Agent-Kachel, Aufnahme-Webseite und API-Warteschlange
Setzt den in #84 skizzierten Workflow um: record.html nimmt im
Handy-Browser eine Sprachnotiz per MediaRecorder auf und laedt sie
unauthentifiziert zu POST /api/v1/agents/{id}/recordings hoch. Ein
Klick auf die Personen-Figur jedes registrierten Agenten in der
Buero-Visualisierung verlinkt dorthin, ein Zaehler am Schreibtisch
zeigt offene Aufnahmen. Der Agent selbst holt seine Warteschlange
ueber die bestehenden Api-Key-authentifizierten Endpunkte ab
(GET .../recordings, GET /recordings/{id}/audio) und markiert eine
Aufnahme nach der Transkription per PATCH als erledigt - die
eigentliche Transkription (whisper.cpp/faster-whisper) bleibt
agent-seitig, siehe fds2610/Agent#17.

Schliesst #85
2026-09-03 21:26:28 +02:00

78 lines
2.6 KiB
PHP

<?php
declare(strict_types=1);
namespace Monitor\Api\Controller;
use InvalidArgumentException;
use Monitor\Api\Http\Request;
use Monitor\Api\Http\Response;
use Monitor\Api\Repository\AgentRepository;
use Monitor\Api\Repository\VoiceRecordingRepository;
final class AgentController
{
public function __construct(
private readonly AgentRepository $agents,
private readonly VoiceRecordingRepository $recordings,
) {
}
/**
* Issue #85: jeder Agent traegt zusaetzlich pending_recordings - die Anzahl offener
* Sprachaufnahmen in seiner Warteschlange, Grundlage fuer den Zaehler auf seinem
* Schreibtisch in der Buero-Visualisierung.
*/
public function index(Request $request): Response
{
$pendingCounts = $this->recordings->pendingCountsByAgent();
$agents = array_map(
fn (array $agent) => $agent + ['pending_recordings' => $pendingCounts[(int) $agent['id']] ?? 0],
$this->agents->all()
);
return Response::json(['agents' => $agents]);
}
public function register(Request $request): Response
{
$displayName = trim((string) ($request->body['display_name'] ?? ''));
$role = (string) ($request->body['role'] ?? '');
if ($displayName === '') {
return Response::error('Feld "display_name" ist erforderlich.', 422);
}
try {
$result = $this->agents->register($displayName, $role);
} catch (InvalidArgumentException $e) {
return Response::error($e->getMessage(), 422);
}
// Der Klartext-API-Key wird nur bei der Registrierung einmalig zurueckgegeben.
return Response::json(['agent' => $result['agent'], 'api_key' => $result['api_key']], 201);
}
/**
* @param array<string, string> $params
* @param array<string, mixed> $authenticatedAgent
*/
public function activity(Request $request, array $params, array $authenticatedAgent): Response
{
if ((int) $params['id'] !== (int) $authenticatedAgent['id']) {
return Response::error('Ein Agent darf nur seine eigene Aktivitaet melden.', 403);
}
$state = (string) ($request->body['state'] ?? '');
if ($state === '') {
return Response::error('Feld "state" ist erforderlich.', 422);
}
$currentTicketId = isset($request->body['current_ticket']) ? (int) $request->body['current_ticket'] : null;
$agent = $this->agents->updateActivity((int) $params['id'], $state, $currentTicketId);
return Response::json(['agent' => $agent]);
}
}