因为数据库连接问题导致的插入数据库异常

This commit is contained in:
2026-05-15 23:30:40 +08:00
parent 42b1555c80
commit d66daf375c
25 changed files with 1371 additions and 4 deletions

View File

@@ -0,0 +1,178 @@
package com.rj.integration;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.rj.dto.hxr.HxrOrderRow;
import com.rj.dto.hxr.HxrOrderSelectResponse;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UncheckedIOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Properties;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* 手动调用 hxrd 后台「订单列表」JSON 接口(需有效 PHPSID
* <p>
* 推荐:将 {@code src/test/resources/hxr-order-integration.example.properties} 复制为
* {@code hxr-order-integration.properties},填写 {@code hxr.order.cookie} 或 {@code hxr.order.phpsid}
* (该文件已加入 .gitignore
* <p>
* 覆盖优先级从高到低JVM 系统属性({@code -Dhxr.order.cookie} / {@code -Dhxr.order.phpsid}
* &gt; 环境变量 {@code HXR_ORDER_COOKIE} &gt; classpath 配置文件 {@code hxr-order-integration.properties}。
* <p>
* 不要手动设置含 {@code br}、{@code zstd} 的 {@code Accept-Encoding}{@link HttpClient} 只会对默认协商的编码自动解压,
* 否则服务端可能返回 Brotli/zstd响应体会变成乱码。
*/
class HxrAdminOrderSelectHttpTest {
private static final String CONFIG_RESOURCE = "hxr-order-integration.properties";
private static final String PROP_COOKIE = "hxr.order.cookie";
private static final String PROP_PHPSID = "hxr.order.phpsid";
private static final String ENV_COOKIE = "HXR_ORDER_COOKIE";
private static final String BASE_URL = "https://hxrdhoutai.hxrdsm.cn/app/admin/order/select";
private static final String QUERY =
"?page=1&limit=20"
+ "&order_sn%5B0%5D=like&order_sn%5B1%5D="
+ "&seller_id=&buyer_id=&status=&is_resell=0&is_show="
+ "&buy_time%5B0%5D=&buy_time%5B1%5D=&confirm_time%5B0%5D=&confirm_time%5B1%5D=";
private static volatile Properties integrationProps;
private static final ObjectMapper HXR_ORDER_JSON = new ObjectMapper()
.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
@Test
void getOrderSelectJson() throws Exception {
String cookie = resolveCookie();
Assumptions.assumeFalse(
cookie == null || cookie.isBlank(),
"跳过:请在 src/test/resources/ 下复制 "
+ "hxr-order-integration.example.properties 为 "
+ CONFIG_RESOURCE
+ " 并填写 hxr.order.cookie 或 hxr.order.phpsid或设置环境变量 "
+ ENV_COOKIE
+ " / VM 选项 -D"
+ PROP_COOKIE
+ " / -D"
+ PROP_PHPSID);
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(15))
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(BASE_URL + QUERY))
.timeout(Duration.ofSeconds(60))
.header("Accept", "application/json, text/javascript, */*; q=0.01")
.header("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
.header("Cookie", cookie)
.header("Priority", "u=1, i")
.header("Referer", "https://hxrdhoutai.hxrdsm.cn/app/admin/order/index")
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36 Edg/148.0.0.0")
.GET()
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("HTTP " + response.statusCode());
assertTrue(
response.statusCode() >= 200 && response.statusCode() < 300,
"unexpected status: " + response.statusCode());
HxrOrderSelectResponse body = HXR_ORDER_JSON.readValue(response.body(), HxrOrderSelectResponse.class);
assertEquals(0, body.code(), () -> "api code != 0, msg=" + body.msg() + ", body=" + response.body());
for (HxrOrderRow row : body.data()) {
System.out.println(HXR_ORDER_JSON.writeValueAsString(row));
}
}
@Test
void parseSampleJson_printsOneLinePerOrder() throws Exception {
String json =
"""
{"code":0,"msg":"ok","count":7,"data":[{"id":358078,"old_id":null,"seller_id":97154,"buyer_id":97374,"order_sn":"47379117788104684482","total_money":"23423.95","pay_time":"2026-05-15 10:04:32","pay_img":"\\/upload\\/image\\/20260515\\/ec791c7a9ed2f5ade9450c0f50efd819_6a067f2f9317d.jpeg","status":2,"is_resell":0,"is_show":1,"consignee":"陈科亦","phone":"18810005315","province":"河北省","city":"廊坊市","area":"三河市","address":"燕郊开发区金谷爱舒荷","merchandise_id":344196,"confirm_time":"2026-05-15 10:08:04","buy_time":"2026-05-15 10:01:08","created_at":"2026-05-15 10:01:08","updated_at":"2026-05-15 10:08:04"},{"id":357968,"old_id":null,"seller_id":98719,"buyer_id":96030,"order_sn":"03069117788104035736","total_money":"27037.65","pay_time":"2026-05-15 10:02:48","pay_img":"\\/upload\\/image\\/20260515\\/37d6d7d09447d6c8ce41abd0727a39d5_6a067ec77a519.jpeg","status":1,"is_resell":0,"is_show":1,"consignee":"陈晓琴","phone":"17733627528","province":"河北省","city":"廊坊市","area":"三河市","address":"燕郊镇金谷爰舒荷十号楼","merchandise_id":344269,"confirm_time":null,"buy_time":"2026-05-15 10:00:03","created_at":"2026-05-15 10:00:03","updated_at":"2026-05-15 10:02:48"}],"all_money":"182851.70"}
""";
HxrOrderSelectResponse body = HXR_ORDER_JSON.readValue(json.trim(), HxrOrderSelectResponse.class);
assertEquals(0, body.code());
assertEquals(2, body.data().size());
for (HxrOrderRow row : body.data()) {
System.out.println(HXR_ORDER_JSON.writeValueAsString(row));
}
}
private static String resolveCookie() {
String fromFileCookie = firstNonBlank(integrationProps().getProperty(PROP_COOKIE));
String fromFilePhpsid = firstNonBlank(integrationProps().getProperty(PROP_PHPSID));
String fromProp = firstNonBlank(System.getProperty(PROP_COOKIE));
String phpsidProp = firstNonBlank(System.getProperty(PROP_PHPSID));
String fromEnv = firstNonBlank(System.getenv(ENV_COOKIE));
if (fromProp != null) {
return fromProp;
}
if (phpsidProp != null) {
return "PHPSID=" + phpsidProp;
}
if (fromEnv != null) {
return fromEnv;
}
if (fromFileCookie != null) {
return fromFileCookie;
}
if (fromFilePhpsid != null) {
return "PHPSID=" + fromFilePhpsid;
}
return "";
}
private static String firstNonBlank(String s) {
if (s == null) {
return null;
}
String t = s.trim();
return t.isEmpty() ? null : t;
}
private static Properties integrationProps() {
if (integrationProps != null) {
return integrationProps;
}
synchronized (HxrAdminOrderSelectHttpTest.class) {
if (integrationProps != null) {
return integrationProps;
}
Properties p = new Properties();
try (InputStream in =
HxrAdminOrderSelectHttpTest.class.getClassLoader().getResourceAsStream(CONFIG_RESOURCE)) {
if (in != null) {
try (InputStreamReader reader = new InputStreamReader(in, StandardCharsets.UTF_8)) {
p.load(reader);
}
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
integrationProps = p;
return integrationProps;
}
}
}

View File

@@ -0,0 +1,94 @@
package com.rj.scheduler;
import com.rj.service.HxrAdminOrderSelectService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
/**
* {@link LBAdminPullScheduler#pullAdminOrders()} 集成测试:全量 Spring 上下文 + {@link LBAdminPullSchedulerTestClockConfiguration}
* HTTP 拉单由 {@link MockitoBean} 替身,避免外网与真实 Cookie。
*
* <p>另见:{@link LBAdminPullSchedulerWhenHxrDisabledIntegrationTest}、{@link LBAdminPullSchedulerWhenGlobalSchedulerOffIntegrationTest}。
*/
@SpringBootTest
@TestPropertySource(
properties = {
"spring.task.scheduling.enabled=false",
"hxr.admin.pull-initial-delay-ms=604800000",
"app.scheduler.start=true",
"hxr.admin.enabled=true",
"app.timezone=Asia/Shanghai"
})
@Import(LBAdminPullSchedulerTestClockConfiguration.class)
@ActiveProfiles("test")
class LBAdminPullSchedulerIntegrationTest {
@Autowired
private LBAdminPullScheduler lbAdminPullScheduler;
@Autowired
private LBAdminPullSchedulerTestClockConfiguration.MutableClock mutableClock;
@MockitoBean
private HxrAdminOrderSelectService hxrAdminOrderSelectService;
@BeforeEach
void resetInvocationHistory() {
Mockito.clearInvocations(hxrAdminOrderSelectService);
}
@Test
void pullAdminOrders_eveningWindow_invokesOrderSelectService() throws Exception {
mutableClock.setTo(
ZonedDateTime.of(LocalDate.of(2026, 5, 15), LocalTime.of(20, 0), ZoneId.of("Asia/Shanghai")));
lbAdminPullScheduler.pullAdminOrders();
verify(hxrAdminOrderSelectService).fetchAndLogEachOrder();
}
@Test
void pullAdminOrders_exactly1810_invokesOrderSelectService() throws Exception {
mutableClock.setTo(
ZonedDateTime.of(LocalDate.of(2026, 5, 15), LocalTime.of(18, 10), ZoneId.of("Asia/Shanghai")));
lbAdminPullScheduler.pullAdminOrders();
verify(hxrAdminOrderSelectService).fetchAndLogEachOrder();
}
@Test
void pullAdminOrders_beforeWindow_doesNotInvoke() throws Exception {
mutableClock.setTo(
ZonedDateTime.of(LocalDate.of(2026, 5, 15), LocalTime.of(12, 0), ZoneId.of("Asia/Shanghai")));
lbAdminPullScheduler.pullAdminOrders();
verify(hxrAdminOrderSelectService, never()).fetchAndLogEachOrder();
}
@Test
void pullAdminOrders_saturdayEveningInTimeWindow_doesNotInvoke() throws Exception {
mutableClock.setTo(
ZonedDateTime.of(LocalDate.of(2026, 5, 16), LocalTime.of(20, 0), ZoneId.of("Asia/Shanghai")));
lbAdminPullScheduler.pullAdminOrders();
verify(hxrAdminOrderSelectService, never()).fetchAndLogEachOrder();
}
}

View File

@@ -0,0 +1,48 @@
package com.rj.scheduler;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
/**
* 为 {@link LBAdminPullScheduler} 集成测试提供可拨 {@link Clock}{@link MutableClock})。
*/
@TestConfiguration
public class LBAdminPullSchedulerTestClockConfiguration {
@Bean
@Primary
MutableClock lbAdminPullTestMutableClock() {
return new MutableClock();
}
/** 测试中可切换时刻的 {@link Clock},用于固定 18:1023:59 时间窗口。 */
public static final class MutableClock extends Clock {
private volatile Clock delegate = Clock.systemDefaultZone();
public void setTo(ZonedDateTime zdt) {
delegate = Clock.fixed(zdt.toInstant(), zdt.getZone());
}
@Override
public ZoneId getZone() {
return delegate.getZone();
}
@Override
public Clock withZone(ZoneId zone) {
return delegate.withZone(zone);
}
@Override
public Instant instant() {
return delegate.instant();
}
}
}

View File

@@ -0,0 +1,51 @@
package com.rj.scheduler;
import com.rj.service.HxrAdminOrderSelectService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@SpringBootTest
@TestPropertySource(
properties = {
"spring.task.scheduling.enabled=false",
"hxr.admin.pull-initial-delay-ms=604800000",
"app.scheduler.start=false",
"hxr.admin.enabled=true",
"app.timezone=Asia/Shanghai"
})
@Import(LBAdminPullSchedulerTestClockConfiguration.class)
@ActiveProfiles("test")
class LBAdminPullSchedulerWhenGlobalSchedulerOffIntegrationTest {
@Autowired
private LBAdminPullScheduler lbAdminPullScheduler;
@Autowired
private LBAdminPullSchedulerTestClockConfiguration.MutableClock mutableClock;
@MockitoBean
private HxrAdminOrderSelectService hxrAdminOrderSelectService;
@Test
void pullAdminOrders_skipsWhenAppSchedulerStartFalse() throws Exception {
mutableClock.setTo(
ZonedDateTime.of(LocalDate.of(2026, 5, 15), LocalTime.of(20, 0), ZoneId.of("Asia/Shanghai")));
lbAdminPullScheduler.pullAdminOrders();
verify(hxrAdminOrderSelectService, never()).fetchAndLogEachOrder();
}
}

View File

@@ -0,0 +1,51 @@
package com.rj.scheduler;
import com.rj.service.HxrAdminOrderSelectService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@SpringBootTest
@TestPropertySource(
properties = {
"spring.task.scheduling.enabled=false",
"hxr.admin.pull-initial-delay-ms=604800000",
"app.scheduler.start=true",
"hxr.admin.enabled=false",
"app.timezone=Asia/Shanghai"
})
@Import(LBAdminPullSchedulerTestClockConfiguration.class)
@ActiveProfiles("test")
class LBAdminPullSchedulerWhenHxrDisabledIntegrationTest {
@Autowired
private LBAdminPullScheduler lbAdminPullScheduler;
@Autowired
private LBAdminPullSchedulerTestClockConfiguration.MutableClock mutableClock;
@MockitoBean
private HxrAdminOrderSelectService hxrAdminOrderSelectService;
@Test
void pullAdminOrders_eveningWindow_stillSkips() throws Exception {
mutableClock.setTo(
ZonedDateTime.of(LocalDate.of(2026, 5, 15), LocalTime.of(20, 0), ZoneId.of("Asia/Shanghai")));
lbAdminPullScheduler.pullAdminOrders();
verify(hxrAdminOrderSelectService, never()).fetchAndLogEachOrder();
}
}

View File

@@ -0,0 +1,7 @@
# 复制本文件为 hxr-order-integration.properties 后填写(后者已加入 .gitignore勿提交真实 Cookie
# 二选一:
# hxr.order.cookie=PHPSID=你的会话值
# hxr.order.phpsid=仅填PHPSID的值不含 PHPSID= 前缀)
hxr.order.cookie=
# hxr.order.phpsid=