mirror of
https://github.com/iflytek/skillhub.git
synced 2026-08-28 11:25:00 +00:00
## Problem Audit log timestamps displayed 8 hours later than actual time when JVM default timezone != UTC. Root cause: `audit_log.created_at` was `TIMESTAMP without time zone`, and `rs.getTimestamp()` interprets bare values using JVM timezone. ## Solution ### Backend - **V42 migration**: Upgrade `audit_log.created_at` from `TIMESTAMP` to `TIMESTAMPTZ`, anchor historical data as UTC via `USING created_at AT TIME ZONE 'UTC'` (same pattern as V18/V19/V23/V25/V36) - **Read path**: `AdminAuditLogAppService.readInstant()` uses `rs.getObject(col, OffsetDateTime.class).toInstant()`, result independent of JVM timezone - **Write path (filter params)**: `startTime`/`endTime` binding changed from `Timestamp.from()` to `OffsetDateTime.ofInstant(instant, ZoneOffset.UTC)` via `toUtcOffsetDateTime()` helper, symmetric with read path ### Migration Safety - `SET LOCAL lock_timeout = '30s'` (transaction-scoped, won't leak to pool) - `DO $$ ... IF data_type = 'timestamp without time zone' THEN ... ELSE ... END $$` idempotent guard with dual-branch `RAISE NOTICE` - Safe retry: re-running won't double-apply `AT TIME ZONE 'UTC'` ### Test Coverage (10 tests, 477 total suite) - `rowMapper_readsCreatedAtAsInstant` — UTC offset regression - `rowMapper_normalisesNonUtcOffsetToInstant` — Non-UTC offset (+08:00) - `rowMapper_returnsNullTimestampWhenColumnIsNull` — Null path - `rowMapper_isIndependentOfJvmDefaultTimezone` — JVM TZ=Asia/Shanghai drift prevention with `verify(rs, never()).getTimestamp()` - `@ParameterizedTest buildWhereClause_bindsTimeRangeAsOffsetDateTime` — 3 cases (both/startOnly/endOnly) for filter param binding - `@BeforeEach setUp()` — Mock isolation to prevent cross-test stub accumulation ## Quality Gates - [x] `make test-backend-app` passes (477 tests, 0 failures) - [x] No Controller changes, `make generate-api` not needed - [x] No frontend changes, typecheck/lint/e2e not needed ## Deployment V42 must run before new code (guaranteed by Spring Boot startup sequence → Flyway executes before app accepts traffic). Rolling deployment: - New pod + migrated column: correct - Old pod + migrated column: old code reads TIMESTAMPTZ correctly (pgjdbc returns absolute instant) ## Related Docs - `docs/15-backend-time-governance-plan.md` §3.1: V42 progress registered - `docs/16-backend-time-inventory.md` §3.1: V42 listed - Same migration pattern: V18/V19/V23/V25/V36 |
||
|---|---|---|
| .. | ||
| .mvn | ||
| scripts | ||
| skillhub-app | ||
| skillhub-auth | ||
| skillhub-domain | ||
| skillhub-infra | ||
| skillhub-notification | ||
| skillhub-search | ||
| skillhub-storage | ||
| .dockerignore | ||
| Dockerfile | ||
| Dockerfile.dev | ||
| mvnw | ||
| mvnw.cmd | ||
| pom.xml | ||