Monthly bandwidth report for the admin console

Java · Spring Boot · intermediate · greenfield

Adds the bandwidth report the support team asked for: one query sums each tenant's `transfer_logs` by traffic kind for the month, and the service adds the per-kind sums into the overall `totalBytes` next to the breakdown. Admin-only through the existing `/api/admin/**` rule.

Hosting platform backend on Spring Boot 3.3 (Java 21, Maven), Spring Data JPA with Hibernate 6.5 on PostgreSQL. Existing `TransferLog` entity (table `transfer_logs`): `Long id`, `String tenantId`, `String kind` (one of `DOWNLOAD`, `API`, `STREAM`), `long bytes` (>= 0, one row per served response, at most 4 GiB each), `Instant servedAt`, all non-null. `SecurityConfig` already restricts `/api/admin/**` to `ROLE_ADMIN`, and Hibernate's `sum` over a `long` column returns `Long`. Tenant ids are exact, case-sensitive ASCII strings (letters, digits and hyphens, 1 to 64 characters). `spring-boot-starter-validation` is already a dependency. The `byKind` figures are used elsewhere as `long`.

Requirements

Files touched

--- src/main/java/com/acme/hosting/usage/TransferLogRepository.java
+package com.acme.hosting.usage;
+
+import java.time.Instant;
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+public interface TransferLogRepository extends JpaRepository<TransferLog, Long> {
+
+    @Query("""
+            select t.kind as kind, sum(t.bytes) as bytes
+            from TransferLog t
+            where t.tenantId = :tenantId and t.servedAt >= :from and t.servedAt < :to
+            group by t.kind
+            order by t.kind""")
+    List<KindTotal> totalsByKind(@Param("tenantId") String tenantId,
+                                 @Param("from") Instant from,
+                                 @Param("to") Instant to);
+
+    interface KindTotal {
+
+        String getKind();
+
+        Long getBytes();
+    }
+}
+

Review this PR

Java practice