Skip to content

feat/implementando tabela de última atividade para usuários#405

Open
mateusmrosa wants to merge 2 commits intohomolfrom
feat/SA10.G011-implementar-tabela-ultima-atividade-usuarios
Open

feat/implementando tabela de última atividade para usuários#405
mateusmrosa wants to merge 2 commits intohomolfrom
feat/SA10.G011-implementar-tabela-ultima-atividade-usuarios

Conversation

@mateusmrosa
Copy link
Contributor

@mateusmrosa mateusmrosa commented Aug 26, 2025

Summary by CodeRabbit

  • New Features

    • People lists now display a “Last activity” timestamp for each person (when available).
  • Chores

    • Backend now records and exposes per-person last-activity data and session links to support the new timestamp.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 26, 2025

Walkthrough

Adds a new table and Prisma model to track each person's last activity, introduces a standalone PostgreSQL function to upsert last-activity and conditionally log activity, removes an in-migration copy of the function, and surfaces last-activity timestamp in the Pessoa listing API.

Changes

Cohort / File(s) Summary of changes
Activity logging function (manual & migration)
backend/prisma/manual-copy/0055-f_insere_log_atividade.pgsql, backend/prisma/migrations/20231228211508_session/migration.sql
Adds f_insere_log_atividade(p_pessoa_id INT, p_ip INET, p_pessoa_sessao_id INT) as a standalone SQL file (upserts pessoa_ultima_atividade, conditionally inserts into pessoa_atividade_log once per minute). Removes the earlier inline definition of the same function from the 20231228 migration.
DB table for last activity (migration)
backend/prisma/migrations/20250826182728_add_pessoa_ultima_atividade/migration.sql
Creates pessoa_ultima_atividade table with columns (id, pessoa_id, pessoa_sessao_id, ip, ultima_atividade_em), primary key, unique on pessoa_id, index on ultima_atividade_em, and FKs to pessoa (ON DELETE CASCADE) and pessoa_sessao (ON DELETE SET NULL).
Prisma schema
backend/prisma/schema.prisma
Adds PessoaUltimaAtividade model, relations to Pessoa and PessoaSessao, unique constraint and index; adds Pessoa.ultimaAtividade? and PessoaSessao.PessoaUltimaAtividade[]; minor attribute-order change on Relatorio.resumo_saida.
API/entity changes
backend/src/pessoa/entities/list-pessoa.entity.ts, backend/src/pessoa/pessoa.service.ts
Adds `ultima_atividade_em?: Date

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant Service as PessoaService
  participant DB as PostgreSQL

  Client->>Service: Request list of pessoas
  Service->>DB: Prisma findMany (include: ultimaAtividade.ultima_atividade_em)
  DB-->>Service: Pessoas with optional ultima_atividade_em
  Note right of Service: Map p.ultimaAtividade?.ultima_atividade_em -> ListPessoa.ultima_atividade_em
  Service-->>Client: ListPessoa[] (includes ultima_atividade_em)
Loading
sequenceDiagram
  autonumber
  actor App as Application
  participant DB as PostgreSQL

  App->>DB: CALL f_insere_log_atividade(p_pessoa_id, p_ip, p_pessoa_sessao_id)
  rect rgba(230,245,255,0.6)
    Note over DB: Upsert last activity
    DB->>DB: INSERT INTO pessoa_ultima_atividade (...) ON CONFLICT(pessoa_id) DO UPDATE SET ...
  end
  rect rgba(240,255,230,0.6)
    Note over DB: Throttle activity log (1-minute window)
    DB->>DB: SELECT COUNT(*) FROM pessoa_atividade_log WHERE pessoa_sessao_id = ... AND criado_em > NOW() - INTERVAL '1 minute'
    alt count == 0
      DB->>DB: INSERT INTO pessoa_atividade_log (pessoa_id, ip, pessoa_sessao_id, criado_em = NOW())
    else count > 0
      Note right of DB: Skip insert
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • robsonsobral

Poem

I hop along the schema rows, a tiny rabbit bright,
I plant a timestamp in the grass and keep the logs polite.
One update for each person — neat, no noisy sprawl,
A minute keeps my scribbles calm, small footprints, not a brawl.
I twitch my nose and bound away — activity logged right. 🐇✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/SA10.G011-implementar-tabela-ultima-atividade-usuarios

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (6)
backend/prisma/migrations/20250826182728_add_pessoa_ultima_atividade/migration.sql (1)

12-16: Consider indexing pessoa_sessao_id for join performance.

If you fetch/update by session (e.g., on each request), an index on pessoa_sessao_id avoids seq scans.

Apply this diff to add the index:

 -- CreateIndex
 CREATE INDEX "pessoa_ultima_atividade_ultima_atividade_em_idx" ON "pessoa_ultima_atividade"("ultima_atividade_em");

+-- CreateIndex
+CREATE INDEX "pessoa_ultima_atividade_pessoa_sessao_id_idx" ON "pessoa_ultima_atividade"("pessoa_sessao_id");
backend/prisma/schema.prisma (3)

504-505: Add a brief doc comment to clarify the 1:1 relation semantics.

Minor clarity: note that this relation is a 1:1 pointer to the “last activity” row keyed by pessoa_id and is populated through DB upserts.

Apply this diff:

-    ultimaAtividade PessoaUltimaAtividade?
+    /// Última atividade do usuário (1:1 via pessoa_id). Atualizada por função SQL de upsert.
+    ultimaAtividade PessoaUltimaAtividade?

4858-4862: Optionally make the relation name explicit to avoid future ambiguity.

There’s a single relation between PessoaSessao and PessoaUltimaAtividade today, but being explicit prevents name inference issues if new relations are added later.

Apply this diff here and in the model below to name the relation:

-    PessoaUltimaAtividade PessoaUltimaAtividade[]
+    PessoaUltimaAtividade PessoaUltimaAtividade[] @relation("PessoaSessaoUltimaAtividade")

And mirror it in the PessoaUltimaAtividade.sessao field (see suggested diff on Lines 7836-7838).


7829-7842: Model maps cleanly to migration; add an index on pessoa_sessao_id and (optionally) an explicit relation name.

  • 1:1 via @@unique([pessoa_id]) matches the SQL unique index. Good.
  • Add @@index([pessoa_sessao_id]) to mirror the SQL index (and speed lookups by session).
  • Optional: name the relation to PessoaSessao to future-proof relation inference.

Apply this diff:

 model PessoaUltimaAtividade {
   id                  Int      @id @default(autoincrement())
   pessoa_id           Int
   pessoa_sessao_id    Int?
   ip                  String   @db.Inet
   ultima_atividade_em DateTime @default(now()) @db.Timestamptz(6)

-  pessoa Pessoa        @relation(fields: [pessoa_id], references: [id], onDelete: Cascade)
-  sessao PessoaSessao? @relation(fields: [pessoa_sessao_id], references: [id], onDelete: SetNull)
+  pessoa Pessoa        @relation(fields: [pessoa_id], references: [id], onDelete: Cascade)
+  sessao PessoaSessao? @relation("PessoaSessaoUltimaAtividade", fields: [pessoa_sessao_id], references: [id], onDelete: SetNull)

   @@unique([pessoa_id])
   @@index([ultima_atividade_em])
+  @@index([pessoa_sessao_id])
   @@map("pessoa_ultima_atividade")
 }
backend/prisma/manual-copy/0055-f_insere_log_atividade.pgsql (2)

21-33: Use EXISTS instead of COUNT and drop the unused variable; reduces work and contention.

COUNT(*) scans more than needed and you don’t use the exact count. EXISTS short-circuits on first match. Also remove the v_existing_count variable.

Apply this diff:

-DECLARE
-    v_existing_count INTEGER;
+-- no declarations needed

@@
-    SELECT COUNT(*)
-      INTO v_existing_count
-      FROM pessoa_atividade_log
-     WHERE pessoa_sessao_id = p_pessoa_sessao_id
-       AND criado_em > (NOW() - INTERVAL '1 minute');
-
-    IF v_existing_count = 0 THEN
-        INSERT INTO pessoa_atividade_log
-            (pessoa_id, ip, pessoa_sessao_id, criado_em)
-        VALUES
-            (p_pessoa_id, p_ip, p_pessoa_sessao_id, NOW());
-    END IF;
+    PERFORM 1
+      FROM pessoa_atividade_log
+     WHERE pessoa_sessao_id = p_pessoa_sessao_id
+       AND criado_em > (NOW() - INTERVAL '1 minute');
+
+    IF NOT FOUND THEN
+        INSERT INTO pessoa_atividade_log (pessoa_id, ip, pessoa_sessao_id, criado_em)
+        VALUES (p_pessoa_id, p_ip, p_pessoa_sessao_id, NOW());
+    END IF;

21-33: Consider concurrency-safe dedup and indexing for the 1-minute throttle.

Two concurrent calls can still race and insert twice. If that matters, enforce at-most-one-per-minute per session with a unique constraint on (pessoa_sessao_id, date_trunc('minute', criado_em)) and switch to ON CONFLICT DO NOTHING. Also index to accelerate the time-window check.

Add these (in a migration):

-- Speeds up the EXISTS/WHERE lookup
CREATE INDEX idx_pessoa_atividade_log_sessao_criado_em
  ON pessoa_atividade_log (pessoa_sessao_id, criado_em DESC);

-- Enforce one row per minute per session (optional).
CREATE UNIQUE INDEX idx_pessoa_atividade_log_unique_minute
  ON pessoa_atividade_log (pessoa_sessao_id, date_trunc('minute', criado_em));

-- Optionally, bind the unique index to a named constraint if you plan to use ON CONFLICT ON CONSTRAINT:
-- ALTER TABLE pessoa_atividade_log
--   ADD CONSTRAINT pessoa_atividade_log_unique_minute
--   UNIQUE USING INDEX idx_pessoa_atividade_log_unique_minute;

Then the insert can become:

INSERT INTO pessoa_atividade_log (pessoa_id, ip, pessoa_sessao_id, criado_em)
VALUES (p_pessoa_id, p_ip, p_pessoa_sessao_id, NOW())
ON CONFLICT ON CONSTRAINT pessoa_atividade_log_unique_minute DO NOTHING;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 8b87636 and d9c778d.

📒 Files selected for processing (6)
  • backend/prisma/manual-copy/0055-f_insere_log_atividade.pgsql (1 hunks)
  • backend/prisma/migrations/20231228211508_session/migration.sql (0 hunks)
  • backend/prisma/migrations/20250826182728_add_pessoa_ultima_atividade/migration.sql (1 hunks)
  • backend/prisma/schema.prisma (4 hunks)
  • backend/src/pessoa/entities/list-pessoa.entity.ts (1 hunks)
  • backend/src/pessoa/pessoa.service.ts (2 hunks)
💤 Files with no reviewable changes (1)
  • backend/prisma/migrations/20231228211508_session/migration.sql
🔇 Additional comments (7)
backend/prisma/migrations/20250826182728_add_pessoa_ultima_atividade/migration.sql (2)

2-10: Solid table design for last-activity tracking (1:1 via pessoa_id).

Schema choices look correct: INET supports IPv4/IPv6, TIMESTAMPTZ(6) with CURRENT_TIMESTAMP default is consistent with the Prisma mapping, and a surrogate PK keeps upsert patterns simple.


19-22: FK actions are appropriate.

ON DELETE CASCADE for pessoa ensures cleanup; ON DELETE SET NULL for pessoa_sessao preserves record if a session is pruned. Good balance for integrity vs. retention.

backend/prisma/schema.prisma (1)

3507-3507: Cosmetic attribute reordering only.

Changing to @default("{}") @db.Json is a no-op in Prisma and should not generate a migration. OK to keep for consistency.

backend/src/pessoa/pessoa.service.ts (2)

1419-1421: LGTM: selecting the relation to expose last activity is correct.

The Prisma select for ultimaAtividade with ultima_atividade_em keeps the payload lean and matches the intended surface.


1437-1439: LGTM: safe mapping of optional relation.

Using optional chaining with null fallback avoids runtime errors when there’s no last-activity row.

backend/prisma/manual-copy/0055-f_insere_log_atividade.pgsql (2)

1-7: No action needed: parameter order is correct in all call sites

  • Verified in backend/src/common/middleware/logger.middleware.ts (line 171) that the call is now
    SELECT f_insere_log_atividade(${user.id}::int, ${ip}::INET, ${user.session_id}::int);
    which aligns with the new signature (p_pessoa_id, p_ip, p_pessoa_sessao_id).
  • No other invocations of f_insere_log_atividade found elsewhere in the codebase.

All callers have been updated to match the new parameter order—no further changes required.


12-20: Upsert on pessoa_ultima_atividade validated

  • The migration at backend/prisma/migrations/20250826182728_add_pessoa_ultima_atividade/migration.sql defines
    • a PRIMARY KEY on “id” (pessoa_ultima_atividade_pkey)
    • a UNIQUE INDEX on “pessoa_id” (pessoa_ultima_atividade_pessoa_id_key), satisfying the ON CONFLICT target.

No further changes needed here.

@sonarqubecloud
Copy link

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/src/pessoa/pessoa.service.ts (1)

133-134: Remover log de senha em texto puro (exposição de segredo/PII).

A linha loga a nova senha e o objeto pessoa. Isso vaza credenciais e PII em logs. Remova imediatamente.

Aplique este diff:

-        this.logger.log(`new password: ${newPass}`, pessoa);
+        // Evitar logar senhas ou dados sensíveis; mantenha no máximo um trace sem segredos, se necessário.
+        // this.logger.debug(`Nova senha gerada para pessoaId=${pessoa.id}`);
🧹 Nitpick comments (2)
backend/src/pessoa/pessoa.service.ts (2)

1419-1421: Confirm 1:1 cardinalidade de ultimaAtividade ou ajuste para 1:N (senão o mapeamento pode quebrar).

A seleção e o mapeamento assumem que Pessoa.ultimaAtividade é uma relação 1:1. Se o modelo permitir múltiplos registros (1:N), o código deve ordenar/limitar e mapear o primeiro registro. Além disso, garanta uma restrição única em pessoa_ultima_atividade.pessoa_id para manter a consistência.

Se a relação for 1:N, aplique algo assim:

-                ultimaAtividade: { select: { ultima_atividade_em: true } },
+                ultimaAtividade: {
+                    orderBy: { ultima_atividade_em: 'desc' },
+                    take: 1,
+                    select: { ultima_atividade_em: true },
+                },

E no mapeamento:

-                ultima_atividade_em: p.ultimaAtividade?.ultima_atividade_em ?? null,
+                ultima_atividade_em: p.ultimaAtividade?.[0]?.ultima_atividade_em ?? null,

Caso a relação seja verdadeiramente 1:1, mantenha o código como está e apenas confirme que existe índice/unique key em pessoa_ultima_atividade(pessoa_id). Posso revisar o schema/migration se enviar o trecho relevante.

Also applies to: 1437-1438


1369-1372: Evitar console.log em produção; use o logger da aplicação.

Mantém padronização e facilita controle de nível/roteamento de logs.

Aplique este diff:

-        console.log('ehAdmin', ehAdmin);
+        this.logger.debug(`ehAdmin=${ehAdmin}`);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d9c778d and 9922fe9.

📒 Files selected for processing (1)
  • backend/src/pessoa/pessoa.service.ts (2 hunks)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant