装柜
This commit is contained in:
+6
-5
@@ -2,7 +2,6 @@ package com.baiyue.firstleg.controller;
|
||||
|
||||
import com.baiyue.common.audit.BusinessAudit;
|
||||
import com.baiyue.common.security.AuthenticatedUser;
|
||||
import com.baiyue.firstleg.dto.LoadingListDtos.AdvanceRequest;
|
||||
import com.baiyue.firstleg.dto.LoadingListDtos.ConfirmedQuery;
|
||||
import com.baiyue.firstleg.dto.LoadingListDtos.PreloadQuery;
|
||||
import com.baiyue.firstleg.dto.LoadingListDtos.TransportUpdateRequest;
|
||||
@@ -27,8 +26,6 @@ class LoadingListControllerContractTest {
|
||||
authority("preloadDetail", "platform:firstleg:preload:view", AuthenticatedUser.class, long.class);
|
||||
authority("updateTransport", "platform:firstleg:preload:edit", AuthenticatedUser.class,
|
||||
long.class, String.class, TransportUpdateRequest.class);
|
||||
authority("complete", "platform:firstleg:preload:complete", AuthenticatedUser.class,
|
||||
long.class, String.class, AdvanceRequest.class);
|
||||
authority("confirmedPage", "platform:firstleg:loading-list:list", AuthenticatedUser.class,
|
||||
ConfirmedQuery.class);
|
||||
authority("confirmedDetail", "platform:firstleg:loading-list:view", AuthenticatedUser.class, long.class);
|
||||
@@ -38,8 +35,6 @@ class LoadingListControllerContractTest {
|
||||
void writesAudited() throws Exception {
|
||||
audit("updateTransport", "PRELOAD_TRANSPORT_UPDATE", long.class, long.class, String.class,
|
||||
String.class, TransportUpdateRequest.class);
|
||||
audit("complete", "PRELOAD_COMPLETE", long.class, long.class, String.class,
|
||||
String.class, AdvanceRequest.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -69,6 +64,12 @@ class LoadingListControllerContractTest {
|
||||
.doesNotContain("/items/{itemId}");
|
||||
}
|
||||
|
||||
@Test
|
||||
void doesNotExposeManualCompletion() {
|
||||
assertThat(PlatformLoadingListController.class.getDeclaredMethods())
|
||||
.noneMatch(method -> method.getName().equals("complete"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void yamlParses() throws Exception {
|
||||
String yaml = new ClassPathResource("openapi/loading-lists-v1.yaml")
|
||||
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
package com.baiyue.firstleg.repository;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@DisplayName("主子入仓单SQL契约")
|
||||
class InboundParentChildSqlContractTest {
|
||||
|
||||
private final String sql = loadSql();
|
||||
|
||||
@Test
|
||||
void definesParentChildRelation() {
|
||||
assertThat(sql)
|
||||
.contains("'fl_inbound_order', 'inbound_role'")
|
||||
.contains("'fl_inbound_order', 'parent_inbound_id'")
|
||||
.contains("'fl_inbound_order', 'root_inbound_id'")
|
||||
.contains("'fl_inbound_order', 'package_count'")
|
||||
.contains("GENERATED ALWAYS AS (CASE WHEN inbound_role = 'MASTER'")
|
||||
.contains("DROP INDEX uk_inbound_tenant_order")
|
||||
.contains("uk_inbound_master_order")
|
||||
.contains("uk_inbound_parent_sequence");
|
||||
}
|
||||
|
||||
@Test
|
||||
void definesSplitLedger() {
|
||||
assertThat(sql)
|
||||
.contains("CREATE TABLE IF NOT EXISTS fl_inbound_split_batch")
|
||||
.contains("CREATE TABLE IF NOT EXISTS fl_inbound_split_item")
|
||||
.contains("CREATE TABLE IF NOT EXISTS fl_inbound_split_history")
|
||||
.contains("package_count INT UNSIGNED NOT NULL")
|
||||
.doesNotContain("quantity INT", "item_count INT", "carton_count INT");
|
||||
}
|
||||
|
||||
@Test
|
||||
void protectsOwnershipAndTotals() {
|
||||
assertThat(sql)
|
||||
.contains("子入仓单必须绑定同一客户的有效主单")
|
||||
.contains("parent_order.package_count = 0")
|
||||
.contains("parent_order.split_status = 'FULLY_SPLIT'")
|
||||
.contains("parent_order.version > NEW.expected_parent_version")
|
||||
.contains("主单可用箱数未归零或子单箱数合计不一致")
|
||||
.contains("SUM(split_item.package_count) > source_item.package_count")
|
||||
.contains("子单承接箱数超过来源测量箱规箱数")
|
||||
.contains("child_order.package_count = NEW.package_count")
|
||||
.contains("拆分数量台账不可修改")
|
||||
.contains("拆分数量台账不可删除")
|
||||
.contains("拆分历史不可修改")
|
||||
.contains("拆分历史不可删除");
|
||||
}
|
||||
|
||||
@Test
|
||||
void tracesMeasurements() {
|
||||
assertThat(sql)
|
||||
.contains("'fl_inbound_measurement', 'split_batch_id'")
|
||||
.contains("'fl_inbound_measurement', 'source_measurement_id'")
|
||||
.contains("'fl_inbound_measurement_item', 'split_item_id'")
|
||||
.contains("'fl_inbound_measurement_item', 'source_measurement_item_id'")
|
||||
.contains("source_measurement_id BIGINT UNSIGNED NULL")
|
||||
.contains("fk_inbound_split_item_measurement")
|
||||
.contains("fk_measure_split_batch")
|
||||
.contains("fk_measure_source_batch")
|
||||
.contains("fk_measure_item_split_item")
|
||||
.contains("fk_measure_item_source_item")
|
||||
.contains("idx_measure_split_source")
|
||||
.contains("idx_measure_item_split_source");
|
||||
}
|
||||
|
||||
@Test
|
||||
void syncsUnifiedMigration() throws Exception {
|
||||
String module = normalize(sql).trim();
|
||||
String unified = normalize(Files.readString(unifiedPath(), StandardCharsets.UTF_8));
|
||||
assertThat(unified)
|
||||
.contains("-- 来源:firstleg/src/main/resources/db/phase3-inbound-parent-child-schema.sql")
|
||||
.contains(module);
|
||||
}
|
||||
|
||||
private static String loadSql() {
|
||||
try {
|
||||
return new ClassPathResource("db/phase3-inbound-parent-child-schema.sql")
|
||||
.getContentAsString(StandardCharsets.UTF_8);
|
||||
} catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static Path unifiedPath() {
|
||||
Path working = Path.of("").toAbsolutePath();
|
||||
Path path = working.resolve("deploy/sql/头程数据库统一增量-MySQL8.sql");
|
||||
if (!Files.exists(path)) {
|
||||
path = working.resolve("../deploy/sql/头程数据库统一增量-MySQL8.sql").normalize();
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
private static String normalize(String value) {
|
||||
return value.replace("\r\n", "\n").replace("\uFEFF", "");
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.baiyue.firstleg.repository;
|
||||
|
||||
import com.baiyue.firstleg.mapper.LoadingPickItemMapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class LoadingPickItemMapperContractTest {
|
||||
@Test
|
||||
void completionCheckCoversAllActivePlanItems() throws Exception {
|
||||
Method method = LoadingPickItemMapper.class.getDeclaredMethod("unfinishedCount", long.class);
|
||||
String sql = method.getAnnotation(Select.class).value()[0];
|
||||
|
||||
assertThat(sql)
|
||||
.contains("plan_item.loading_plan_id = #{planId}")
|
||||
.contains("plan_item.deleted = 0")
|
||||
.contains("pick_task.loading_plan_id = #{planId}")
|
||||
.contains("pick_task.task_status = 'LOADING'")
|
||||
.contains("pick_item.picked_package_count = plan_item.package_count");
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -20,6 +20,8 @@ class SmartLoadingCandidateMapperContractTest {
|
||||
.contains("SELECT MAX(m2.id)")
|
||||
.contains("m2.status='APPROVED'")
|
||||
.contains("i.inbound_detail_id IS NOT NULL")
|
||||
.contains("SELECT SUM(li.package_count)")
|
||||
.contains("li.deleted=0")
|
||||
.contains("ORDER BY o.inbounded_at ASC, o.id ASC, i.id ASC");
|
||||
}
|
||||
|
||||
@@ -32,6 +34,8 @@ class SmartLoadingCandidateMapperContractTest {
|
||||
assertThat(sql)
|
||||
.contains("o.destination_warehouse_id destinationWarehouseId")
|
||||
.contains("m.status='APPROVED'")
|
||||
.contains("SELECT MAX(m2.id)");
|
||||
.contains("SELECT MAX(m2.id)")
|
||||
.contains("SELECT SUM(li.package_count)")
|
||||
.contains("FOR UPDATE");
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -64,7 +64,8 @@ class SmartLoadingSqlContractTest {
|
||||
assertThat(migration())
|
||||
.contains("active_inbound_detail_id BIGINT UNSIGNED GENERATED ALWAYS AS")
|
||||
.contains("CASE WHEN deleted = 0 THEN inbound_detail_id ELSE NULL END")
|
||||
.contains("UNIQUE KEY uk_loading_item_active_detail (account_id, active_inbound_detail_id)")
|
||||
.contains("UNIQUE KEY uk_loading_item_plan_detail (loading_plan_id, active_inbound_detail_id)")
|
||||
.contains("KEY idx_loading_item_active_detail (account_id, active_inbound_detail_id)")
|
||||
.contains("UNIQUE KEY uk_container_booking_no (booking_no)")
|
||||
.contains("UNIQUE KEY uk_container_booking_cabinet (cabinet_no)")
|
||||
.contains("UNIQUE KEY uk_loading_plan_push_key (push_idempotency_key)")
|
||||
|
||||
@@ -3,13 +3,13 @@ package com.baiyue.firstleg.service;
|
||||
import com.baiyue.common.core.api.PageResult;
|
||||
import com.baiyue.common.core.api.ResultCode;
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import com.baiyue.firstleg.dto.LoadingListDtos.AdvanceRequest;
|
||||
import com.baiyue.firstleg.dto.LoadingListDtos.ConfirmedQuery;
|
||||
import com.baiyue.firstleg.dto.LoadingListDtos.PreloadQuery;
|
||||
import com.baiyue.firstleg.dto.LoadingListDtos.TransportUpdateRequest;
|
||||
import com.baiyue.firstleg.mapper.LoadingListQueryMapper.AggregateRow;
|
||||
import com.baiyue.firstleg.mapper.LoadingListQueryMapper.HistoryRow;
|
||||
import com.baiyue.firstleg.repository.LoadingListRepository;
|
||||
import com.baiyue.firstleg.repository.LoadingPickRepository;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
@@ -36,6 +36,8 @@ import static org.mockito.Mockito.when;
|
||||
class LoadingListServiceTest {
|
||||
@MockBean
|
||||
private LoadingListRepository repository;
|
||||
@MockBean
|
||||
private LoadingPickRepository pickRepository;
|
||||
@Resource
|
||||
private LoadingListService service;
|
||||
|
||||
@@ -95,19 +97,32 @@ class LoadingListServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAdvanceOnlyLoadingToCompleted() {
|
||||
void shouldCompleteLoadingAfterAllPdaItemsPicked() {
|
||||
AggregateRow before = row("LOADING", 2);
|
||||
AggregateRow after = row("COMPLETED", 3);
|
||||
when(pickRepository.allPlanItemsPicked(10L)).thenReturn(true);
|
||||
when(repository.history(anyString())).thenReturn(null);
|
||||
when(repository.aggregate(10L)).thenReturn(before, after);
|
||||
when(repository.aggregate(10L)).thenReturn(before);
|
||||
when(repository.advance(10L, "LOADING", "COMPLETED", 2, 9L, "operator"))
|
||||
.thenReturn(1);
|
||||
when(repository.preloadDetail(after)).thenReturn(
|
||||
new com.baiyue.firstleg.dto.LoadingListDtos.PreloadDetail(null, List.of(), List.of()));
|
||||
|
||||
service.complete(10L, 9L, "operator", "complete-key", new AdvanceRequest(2, null));
|
||||
service.finishFromPda(10L, 21L, 9L, "operator");
|
||||
|
||||
verify(repository).advance(10L, "LOADING", "COMPLETED", 2, 9L, "operator");
|
||||
verify(repository).appendHistory(eq(10L), eq("PDA-COMPLETE-10-21"),
|
||||
eq("PDA_COMPLETE_LOADING"), eq("LOADING"), eq("COMPLETED"), anyString(), anyString(),
|
||||
eq(9L), eq("operator"), eq("全部箱数已完成PDA拣货"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectAutomaticCompletionWhenPdaItemsRemain() {
|
||||
when(pickRepository.allPlanItemsPicked(10L)).thenReturn(false);
|
||||
|
||||
assertThatThrownBy(() -> service.finishFromPda(10L, 21L, 9L, "operator"))
|
||||
.isInstanceOf(BizException.class)
|
||||
.extracting(error -> ((BizException) error).getResultCode())
|
||||
.isEqualTo(ResultCode.CONFLICT);
|
||||
verify(repository, never()).advance(eq(10L), anyString(), anyString(),
|
||||
org.mockito.ArgumentMatchers.anyInt(), eq(9L), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.baiyue.firstleg.service;
|
||||
|
||||
import com.baiyue.firstleg.dto.LoadingPickDtos.PickRequest;
|
||||
import com.baiyue.firstleg.entity.LoadingPickHistoryEntity;
|
||||
import com.baiyue.firstleg.entity.LoadingPickItemEntity;
|
||||
import com.baiyue.firstleg.entity.LoadingPickTaskEntity;
|
||||
import com.baiyue.firstleg.repository.LoadingPickRepository;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@SpringJUnitConfig(LoadingPickService.class)
|
||||
class LoadingPickServiceTest {
|
||||
@MockBean
|
||||
private LoadingPickRepository repository;
|
||||
@MockBean
|
||||
private LoadingListService loadingListService;
|
||||
@Resource
|
||||
private LoadingPickService service;
|
||||
|
||||
@Test
|
||||
void keepsPlanLoadingWhenItemsRemain() {
|
||||
LoadingPickTaskEntity task = task();
|
||||
LoadingPickItemEntity item = item();
|
||||
when(repository.history("pick-1")).thenReturn(null);
|
||||
when(repository.task(20L)).thenReturn(task);
|
||||
when(repository.item(20L, 30L)).thenReturn(item);
|
||||
when(repository.pick(30L, 2, 0)).thenReturn(1);
|
||||
when(repository.allPlanItemsPicked(10L)).thenReturn(false);
|
||||
when(repository.items(20L)).thenReturn(List.of(item));
|
||||
|
||||
service.pick(20L, 30L, new PickRequest(2, List.of(), 0, "pick-1"), 9L, "operator");
|
||||
|
||||
verify(loadingListService, never()).finishFromPda(10L, 20L, 9L, "operator");
|
||||
}
|
||||
|
||||
@Test
|
||||
void completesPlanWhenLastItemIsPicked() {
|
||||
LoadingPickTaskEntity task = task();
|
||||
LoadingPickItemEntity item = item();
|
||||
when(repository.history("pick-2")).thenReturn(null);
|
||||
when(repository.task(20L)).thenReturn(task);
|
||||
when(repository.item(20L, 30L)).thenReturn(item);
|
||||
when(repository.pick(30L, 2, 0)).thenReturn(1);
|
||||
when(repository.allPlanItemsPicked(10L)).thenReturn(true);
|
||||
when(repository.items(20L)).thenReturn(List.of(item));
|
||||
|
||||
service.pick(20L, 30L, new PickRequest(2, List.of(), 0, "pick-2"), 9L, "operator");
|
||||
|
||||
verify(loadingListService).finishFromPda(10L, 20L, 9L, "operator");
|
||||
verify(repository).insert(any(LoadingPickHistoryEntity.class));
|
||||
}
|
||||
|
||||
private static LoadingPickTaskEntity task() {
|
||||
LoadingPickTaskEntity task = new LoadingPickTaskEntity();
|
||||
task.setId(20L);
|
||||
task.setLoadingPlanId(10L);
|
||||
task.setContainerNo("CSLU1234567");
|
||||
task.setTaskStatus("LOADING");
|
||||
task.setVersion(0);
|
||||
return task;
|
||||
}
|
||||
|
||||
private static LoadingPickItemEntity item() {
|
||||
LoadingPickItemEntity item = new LoadingPickItemEntity();
|
||||
item.setId(30L);
|
||||
item.setPickTaskId(20L);
|
||||
item.setLoadingPlanItemId(40L);
|
||||
item.setInboundId(50L);
|
||||
item.setInboundDetailId(60L);
|
||||
item.setPlannedPackageCount(2);
|
||||
item.setPickedPackageCount(0);
|
||||
item.setItemStatus("PENDING");
|
||||
item.setVersion(0);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
@@ -95,10 +95,10 @@ class SmartLoadingServiceTest {
|
||||
LoadingPlanEntity plan = plan("EDITING", 0);
|
||||
when(repository.plan(1)).thenReturn(plan);
|
||||
when(repository.candidate(9, 10, 20)).thenReturn(candidate(5));
|
||||
when(repository.activeItem(9, 20)).thenReturn(item(7, 9, 20, 4));
|
||||
when(repository.activeItem(1L, 9L, 20L)).thenReturn(item(7, 9, 20, 4));
|
||||
|
||||
BizException error = catchThrowableOfType(() -> service.assign(1,
|
||||
new AssignRequest(9L, 10L, 20L, 2, 0, "assign-1"), 7, "u"), BizException.class);
|
||||
new AssignRequest(9L, 10L, 20L, 6, 0, "assign-1"), 7, "u"), BizException.class);
|
||||
|
||||
assertThat(error.getResultCode()).isEqualTo(ResultCode.VALIDATION_FAILED);
|
||||
verify(repository, never()).touch(anyLong(), anyInt(), anyLong(), any());
|
||||
@@ -297,7 +297,7 @@ class SmartLoadingServiceTest {
|
||||
when(repository.booking(99)).thenReturn(booking());
|
||||
when(repository.items(1)).thenReturn(List.of());
|
||||
when(repository.candidate(9, 10, 20)).thenReturn(candidate(10));
|
||||
when(repository.activeItem(9, 20)).thenReturn(null);
|
||||
when(repository.activeItem(1L, 9L, 20L)).thenReturn(null);
|
||||
when(repository.touch(1, 0, 7, "u")).thenReturn(1);
|
||||
}
|
||||
|
||||
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
package com.baiyue.firstleg.split.service;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class SplitPreloadSqlContractTest {
|
||||
@Test
|
||||
void supportsPartialPackageLock() throws Exception {
|
||||
assertPartialContract(moduleMigration());
|
||||
}
|
||||
|
||||
@Test
|
||||
void limitsSecondaryInboundDepth() throws Exception {
|
||||
String sql = moduleMigration();
|
||||
assertThat(sql)
|
||||
.contains("parent_inbound_id")
|
||||
.contains("split_level")
|
||||
.contains("split_status")
|
||||
.contains("UNIQUE KEY uk_inbound_one_secondary (tenant_id, parent_inbound_id)")
|
||||
.contains("CONSTRAINT fk_inbound_parent FOREIGN KEY (parent_inbound_id)")
|
||||
.contains("二级子入库单不能继续拆分")
|
||||
.contains("入库单主子关系创建后不可修改")
|
||||
.contains("已完成拆单不能回退");
|
||||
}
|
||||
|
||||
@Test
|
||||
void remainsRepeatable() throws Exception {
|
||||
String sql = moduleMigration();
|
||||
assertThat(sql)
|
||||
.contains("CREATE TABLE IF NOT EXISTS fl_inbound_split_detail")
|
||||
.contains("information_schema.columns")
|
||||
.contains("information_schema.statistics")
|
||||
.contains("information_schema.table_constraints")
|
||||
.contains("DROP TRIGGER IF EXISTS trg_inbound_split_before_insert")
|
||||
.contains("DROP TRIGGER IF EXISTS trg_inbound_split_detail_before_delete")
|
||||
.contains("CREATE PROCEDURE phase3_split_assert_contract()")
|
||||
.contains("拆单迁移失败:唯一子单或部分锁定幂等索引缺失")
|
||||
.contains("拆单迁移失败:主子层级或部分锁定触发器未完整创建");
|
||||
}
|
||||
|
||||
@Test
|
||||
void syncsUnifiedMigration() throws Exception {
|
||||
assertPartialContract(unifiedMigration());
|
||||
}
|
||||
|
||||
private static void assertPartialContract(String sql) {
|
||||
assertThat(sql)
|
||||
.contains("CREATE TABLE IF NOT EXISTS fl_inbound_split_detail")
|
||||
.contains("remaining_package_count INT UNSIGNED GENERATED ALWAYS AS")
|
||||
.contains("source_package_count - locked_package_count")
|
||||
.contains("locked_package_count < source_package_count")
|
||||
.contains("status = 'LOCKED' AND child_inbound_id IS NULL")
|
||||
.contains("status = 'COMPLETED' AND child_inbound_id IS NOT NULL")
|
||||
.contains("UNIQUE KEY uk_inbound_split_parent_detail")
|
||||
.contains("UNIQUE KEY uk_inbound_split_operation (operation_id)")
|
||||
.contains("部分锁定的归属和箱数创建后不可修改")
|
||||
.contains("部分锁定与拆单明细不可删除")
|
||||
.contains("剩余箱数必须绑定主单唯一二级子入库单");
|
||||
}
|
||||
|
||||
private static String moduleMigration() throws Exception {
|
||||
return new ClassPathResource("db/phase3-split-preload-schema.sql")
|
||||
.getContentAsString(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private static String unifiedMigration() throws Exception {
|
||||
Path working = Path.of("").toAbsolutePath();
|
||||
Path file = working.resolve("deploy/sql/头程数据库统一增量-MySQL8.sql");
|
||||
if (!Files.exists(file)) {
|
||||
file = working.resolve("../deploy/sql/头程数据库统一增量-MySQL8.sql").normalize();
|
||||
}
|
||||
return Files.readString(file, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user