diff --git a/backend/unispeaking-server/src/main/java/com/unispeaking/infrastructure/ai/iflytek/IflytekScoringProvider.java b/backend/unispeaking-server/src/main/java/com/unispeaking/infrastructure/ai/iflytek/IflytekScoringProvider.java index 2dbcafc..4ff737d 100644 --- a/backend/unispeaking-server/src/main/java/com/unispeaking/infrastructure/ai/iflytek/IflytekScoringProvider.java +++ b/backend/unispeaking-server/src/main/java/com/unispeaking/infrastructure/ai/iflytek/IflytekScoringProvider.java @@ -11,12 +11,13 @@ import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.time.Duration; +import java.time.Instant; import java.time.ZoneOffset; -import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.Base64; import java.util.LinkedHashMap; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -52,6 +53,11 @@ public class IflytekScoringProvider extends ScoringProvider { "cn-east-1.ws-api.xf-yun.com"; private static final String SUNTONE_PATH = "/v1/private/s8e098720"; + private static final DateTimeFormatter HTTP_DATE_FORMATTER = + DateTimeFormatter.ofPattern( + "EEE, dd MMM yyyy HH:mm:ss 'GMT'", + Locale.US) + .withZone(ZoneOffset.UTC); private final ObjectMapper objectMapper; private final WebSocketConnector connector; @@ -444,8 +450,7 @@ private long remainingNanos(long deadlineNanos) private URI signedEndpoint(String signingApiKey) { try { String host = endpoint.getHost(); - String date = ZonedDateTime.now(ZoneOffset.UTC) - .format(DateTimeFormatter.RFC_1123_DATE_TIME); + String date = formatHttpDate(Instant.now()); String requestLine = "GET " + endpoint.getRawPath() + " HTTP/1.1"; String signatureOrigin = @@ -482,6 +487,12 @@ private URI signedEndpoint(String signingApiKey) { } } + static String formatHttpDate(Instant instant) { + return HTTP_DATE_FORMATTER.format(Objects.requireNonNull( + instant, + "iFlytek signature instant is required")); + } + private byte[] wavPayload(byte[] wav) { if (wav.length < 12 || wav[0] != 'R' diff --git a/backend/unispeaking-server/src/test/java/com/unispeaking/infrastructure/ai/iflytek/IflytekScoringProviderDateTest.java b/backend/unispeaking-server/src/test/java/com/unispeaking/infrastructure/ai/iflytek/IflytekScoringProviderDateTest.java new file mode 100644 index 0000000..9208d14 --- /dev/null +++ b/backend/unispeaking-server/src/test/java/com/unispeaking/infrastructure/ai/iflytek/IflytekScoringProviderDateTest.java @@ -0,0 +1,25 @@ +package com.unispeaking.infrastructure.ai.iflytek; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.time.Instant; +import org.junit.jupiter.api.Test; + +class IflytekScoringProviderDateTest { + + @Test + void formatsSingleDigitDayWithTheTwoDigitsRequiredByIflytek() { + assertEquals( + "Sat, 01 Aug 2026 04:54:00 GMT", + IflytekScoringProvider.formatHttpDate( + Instant.parse("2026-08-01T04:54:00Z"))); + } + + @Test + void preservesTwoDigitDayInIflytekSignatureDate() { + assertEquals( + "Tue, 11 Aug 2026 04:54:00 GMT", + IflytekScoringProvider.formatHttpDate( + Instant.parse("2026-08-11T04:54:00Z"))); + } +}