Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
40 / 40
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
RecordDocumentViewAction
100.00% covered (success)
100.00%
40 / 40
100.00% covered (success)
100.00%
2 / 2
9
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 execute
100.00% covered (success)
100.00%
39 / 39
100.00% covered (success)
100.00%
1 / 1
8
1<?php
2
3declare(strict_types=1);
4
5namespace ImovelZapAi\Documents\Application\Actions;
6
7use ImovelZapAi\Documents\Application\Services\DocumentHistoryWriter;
8use ImovelZapAi\Documents\Application\Services\DocumentShareTokenService;
9use ImovelZapAi\Documents\Domain\Enums\DocumentHistoryEvent;
10use ImovelZapAi\Documents\Domain\Events\DocumentViewed;
11use ImovelZapAi\Documents\Infrastructure\Persistence\Eloquent\BusinessDocumentModel;
12use ImovelZapAi\Documents\Infrastructure\Persistence\Eloquent\DocumentHistoryModel;
13use ImovelZapAi\Documents\Infrastructure\Persistence\Eloquent\DocumentViewModel;
14
15final class RecordDocumentViewAction
16{
17    public function __construct(
18        private readonly DocumentShareTokenService $tokens,
19        private readonly DocumentHistoryWriter $history,
20    ) {}
21
22    /** @return array{document: BusinessDocumentModel, view: DocumentViewModel} */
23    public function execute(string $plainToken, ?string $ip = null, ?string $userAgent = null): array
24    {
25        $resolved = $this->tokens->resolve($plainToken);
26        $document = $resolved['document'];
27        $share = $resolved['share'];
28
29        $device = 'desktop';
30        $ua = strtolower((string) $userAgent);
31        if (str_contains($ua, 'mobile') || str_contains($ua, 'android') || str_contains($ua, 'iphone')) {
32            $device = 'mobile';
33        } elseif (str_contains($ua, 'tablet') || str_contains($ua, 'ipad')) {
34            $device = 'tablet';
35        }
36
37        $view = DocumentViewModel::query()->create([
38            'tenant_id' => $document->tenant_id,
39            'business_document_id' => $document->id,
40            'document_share_id' => $share->id,
41            'document_version_id' => $share->document_version_id,
42            'ip' => $ip,
43            'user_agent' => $userAgent ? substr($userAgent, 0, 512) : null,
44            'device' => $device,
45            'started_at' => now(),
46        ]);
47
48        $document->forceFill([
49            'last_viewed_at' => now(),
50            'view_count' => $document->view_count + 1,
51        ])->save();
52
53        $recent = DocumentHistoryModel::query()
54            ->where('business_document_id', $document->id)
55            ->where('event', DocumentHistoryEvent::Viewed)
56            ->where('created_at', '>=', now()->subHour())
57            ->exists();
58
59        if (! $recent) {
60            $this->history->write(
61                $document->tenant_id,
62                $document->id,
63                DocumentHistoryEvent::Viewed,
64                actorType: 'client',
65                payload: ['device' => $device, 'ip' => $ip],
66                message: 'Cliente abriu o documento.',
67            );
68            DocumentViewed::dispatch($document->tenant_id, $document->id);
69        }
70
71        return ['document' => $document->fresh(['currentVersion', 'comments']), 'view' => $view];
72    }
73}