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
- `GET /api/admin/tenants/{tenantId}/bandwidth?year=&month=` returns the tenant's served traffic for that calendar month in UTC: `{tenantId, month, totalBytes, byKind: [{kind, bytes}]}`. `month` is `YYYY-MM`. `tenantId` is non-blank and at most 64 characters, `year` is 2000 to 2100, `month` is 1 to 12; anything else gets Spring's default 400.
- `byKind` has one entry per traffic kind (`DOWNLOAD`, `API`, `STREAM`) that has at least one log row in the month, ordered by kind. `bytes` is the exact sum of that kind's `transfer_logs.bytes` for rows with `servedAt` in `[first of month 00:00 UTC, first of next month 00:00 UTC)`.
- `totalBytes` is the exact sum of all of the tenant's `bytes` in that month. A month without traffic answers 200 with `totalBytes` 0 and an empty `byKind`.
- Sizes are exact byte counts. A busy tenant serves up to 50 TiB in a month, and the billing team reads `totalBytes` to invoice overage.
Files touched
- src/main/java/com/acme/hosting/usage/TransferLogRepository.java
- src/main/java/com/acme/hosting/usage/BandwidthReport.java
- src/main/java/com/acme/hosting/usage/BandwidthReportService.java
- src/main/java/com/acme/hosting/usage/BandwidthReportController.java
--- 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();
+ }
+}
+