fix(audit): sentinel timestamps вместо :from IS NULL в JPQL
PostgreSQL не может вывести тип параметра OffsetDateTime когда оба боковых выражения IS NULL ⇒ 500 на /api/v1/admin/audit. Фикс — controller подставляет 0001-01-01/9999-12-31 если from/to не указаны, JPQL использует обычное сравнение eventTime BETWEEN :from AND :to. Строковые фильтры (dictionary, user, action, businessKey) остаются с :p IS NULL OR x = :p — они работают потому что VARCHAR PG может вывести из правой стороны сравнения.
This commit is contained in:
+6
-2
@@ -23,6 +23,10 @@ public interface AuditLogRepository extends JpaRepository<AuditLog, Long> {
|
|||||||
/**
|
/**
|
||||||
* Гибкий запрос для admin UI: фильтры nullable, NULL = «не фильтруем».
|
* Гибкий запрос для admin UI: фильтры nullable, NULL = «не фильтруем».
|
||||||
* Сортировка задаётся {@link Pageable}.
|
* Сортировка задаётся {@link Pageable}.
|
||||||
|
*
|
||||||
|
* <p>Для {@code from}/{@code to} (OffsetDateTime) NULL не передаём — PostgreSQL не
|
||||||
|
* может вывести тип параметра из {@code IS NULL} проверки. Вместо этого
|
||||||
|
* controller подставляет {@link OffsetDateTime#MIN} / {@link OffsetDateTime#MAX}.
|
||||||
*/
|
*/
|
||||||
@Query("""
|
@Query("""
|
||||||
SELECT a FROM AuditLog a
|
SELECT a FROM AuditLog a
|
||||||
@@ -30,8 +34,8 @@ public interface AuditLogRepository extends JpaRepository<AuditLog, Long> {
|
|||||||
AND (:userId IS NULL OR a.userId = :userId)
|
AND (:userId IS NULL OR a.userId = :userId)
|
||||||
AND (:action IS NULL OR a.action = :action)
|
AND (:action IS NULL OR a.action = :action)
|
||||||
AND (:businessKey IS NULL OR a.businessKey = :businessKey)
|
AND (:businessKey IS NULL OR a.businessKey = :businessKey)
|
||||||
AND (:from IS NULL OR a.eventTime >= :from)
|
AND a.eventTime >= :from
|
||||||
AND (:to IS NULL OR a.eventTime <= :to)
|
AND a.eventTime <= :to
|
||||||
""")
|
""")
|
||||||
Page<AuditLog> search(
|
Page<AuditLog> search(
|
||||||
@Param("dictionaryName") String dictionaryName,
|
@Param("dictionaryName") String dictionaryName,
|
||||||
|
|||||||
+4
-4
@@ -52,8 +52,8 @@ public class AuditAdminController {
|
|||||||
nullIfBlank(userId),
|
nullIfBlank(userId),
|
||||||
nullIfBlank(action),
|
nullIfBlank(action),
|
||||||
nullIfBlank(businessKey),
|
nullIfBlank(businessKey),
|
||||||
from,
|
from == null ? OffsetDateTime.parse("0001-01-01T00:00:00Z") : from,
|
||||||
to,
|
to == null ? OffsetDateTime.parse("9999-12-31T23:59:59Z") : to,
|
||||||
PageRequest.of(page, Math.min(size, 200), Sort.by(Sort.Direction.DESC, "eventTime")));
|
PageRequest.of(page, Math.min(size, 200), Sort.by(Sort.Direction.DESC, "eventTime")));
|
||||||
return result.map(AuditEntry::from);
|
return result.map(AuditEntry::from);
|
||||||
}
|
}
|
||||||
@@ -71,8 +71,8 @@ public class AuditAdminController {
|
|||||||
nullIfBlank(userId),
|
nullIfBlank(userId),
|
||||||
nullIfBlank(action),
|
nullIfBlank(action),
|
||||||
nullIfBlank(businessKey),
|
nullIfBlank(businessKey),
|
||||||
from,
|
from == null ? OffsetDateTime.parse("0001-01-01T00:00:00Z") : from,
|
||||||
to,
|
to == null ? OffsetDateTime.parse("9999-12-31T23:59:59Z") : to,
|
||||||
PageRequest.of(0, MAX_EXPORT_ROWS, Sort.by(Sort.Direction.DESC, "eventTime")));
|
PageRequest.of(0, MAX_EXPORT_ROWS, Sort.by(Sort.Direction.DESC, "eventTime")));
|
||||||
|
|
||||||
StringBuilder csv = new StringBuilder(
|
StringBuilder csv = new StringBuilder(
|
||||||
|
|||||||
Reference in New Issue
Block a user