diff --git a/1-sql/2026-01-19/pg-/file_storage.sql b/1-sql/2026-01-19/pg-/file_storage.sql
new file mode 100644
index 00000000..b7e57bd4
--- /dev/null
+++ b/1-sql/2026-01-19/pg-/file_storage.sql
@@ -0,0 +1,60 @@
+-- 1. 创建表结构
+CREATE TABLE "file_storage" (
+ -- [兼容模式] 支持 AUTO_INCREMENT
+ "id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
+
+ "fileName" VARCHAR(255) NOT NULL DEFAULT '',
+ "fileId" BIGINT NOT NULL,
+ "userGroupId" BIGINT DEFAULT NULL,
+ "tenantId" BIGINT DEFAULT NULL,
+ "userId" BIGINT DEFAULT NULL,
+ "dirId" BIGINT NOT NULL,
+ "fileBizType" INTEGER DEFAULT NULL,
+ "fileSuffix" VARCHAR(50) NOT NULL DEFAULT '',
+
+ -- [兼容模式] 去掉 UNSIGNED,使用 BIGINT (范围足够覆盖)
+ "fileSize" BIGINT NOT NULL,
+
+ -- [兼容模式] 支持 DATETIME 类型
+ "createTime" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+
+ -- [兼容模式] 支持 ON UPDATE 语法,无需触发器实现自动更新
+ "updateTime" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+
+ "fullPath" VARCHAR(1024) NOT NULL DEFAULT '',
+
+ -- [修复核心报错]
+ -- 将 date_format 替换为标准的提取拼接逻辑,确保是 IMMUTABLE (不可变) 表达式
+ "createYearMonth" VARCHAR(7) GENERATED ALWAYS AS (
+ CAST(EXTRACT(YEAR FROM "createTime") AS VARCHAR) || '-' ||
+ LPAD(CAST(EXTRACT(MONTH FROM "createTime") AS VARCHAR), 2, '0')
+ ) STORED
+);
+
+-- 2. 创建索引
+CREATE INDEX "idx_dirid_size" ON "file_storage" ("dirId", "fileSize");
+CREATE INDEX "idx_userid_size" ON "file_storage" ("userId", "fileSize");
+CREATE INDEX "idx_dirid_createtime_size" ON "file_storage" ("dirId", "createTime", "fileSize");
+CREATE INDEX "idx_userid_createtime_size" ON "file_storage" ("userId", "createTime", "fileSize");
+CREATE INDEX "idx_dirid_createmonth_size" ON "file_storage" ("dirId", "createYearMonth", "fileSize");
+CREATE INDEX "idx_userid_createmonth_size" ON "file_storage" ("userId", "createYearMonth", "fileSize");
+CREATE INDEX "idx_dir_file_suffix_biz_createtime_size" ON "file_storage" ("dirId", "fileName", "fileSuffix", "fileBizType", "createTime", "fileSize");
+CREATE INDEX "idx_filename" ON "file_storage" ("fileName");
+
+-- 3. 添加字段注释
+COMMENT ON TABLE "file_storage" IS '文件存储统计主表(支持项目/学科/用户维度的存储占用统计)';
+COMMENT ON COLUMN "file_storage"."id" IS '主键ID(自增)';
+COMMENT ON COLUMN "file_storage"."fileName" IS '文件名(含后缀)';
+COMMENT ON COLUMN "file_storage"."fileId" IS '文件唯一标识ID(关联文件元数据表)';
+COMMENT ON COLUMN "file_storage"."userGroupId" IS '用户组ID';
+COMMENT ON COLUMN "file_storage"."tenantId" IS '租户ID';
+COMMENT ON COLUMN "file_storage"."userId" IS '文件所属用户ID';
+COMMENT ON COLUMN "file_storage"."dirId" IS '保存所有父目录ID(项目/学科等目录的唯一标识)';
+COMMENT ON COLUMN "file_storage"."fileBizType" IS '文件业务类型(1:模型文件 2:仿真报告、3:计算文件、4:曲线文件、5:云图文件,6:网格文件,7:计算过程文件)';
+COMMENT ON COLUMN "file_storage"."fileSuffix" IS '文件后缀(如txt、jpg、pdf)';
+COMMENT ON COLUMN "file_storage"."fileSize" IS '文件大小(字节数,存储占用计算依据)';
+COMMENT ON COLUMN "file_storage"."createTime" IS '文件创建时间';
+COMMENT ON COLUMN "file_storage"."updateTime" IS '记录更新时间(自动更新)';
+COMMENT ON COLUMN "file_storage"."fullPath" IS '文件完整路径(冗余字段,优化查询)';
+COMMENT ON COLUMN "file_storage"."createYearMonth" IS '创建年月(自动生成)';
+
diff --git a/data/pom.xml b/data/pom.xml
index 7cefee39..63def4ba 100644
--- a/data/pom.xml
+++ b/data/pom.xml
@@ -118,6 +118,18 @@
${org.mapstruct.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.postgresql
+ postgresql
+ test
+
+
diff --git a/data/src/test/java/com/sdm/data/dao/MapperCompatibilityTest.java b/data/src/test/java/com/sdm/data/dao/MapperCompatibilityTest.java
new file mode 100644
index 00000000..e718850c
--- /dev/null
+++ b/data/src/test/java/com/sdm/data/dao/MapperCompatibilityTest.java
@@ -0,0 +1,447 @@
+package com.sdm.data.dao;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.sdm.data.model.dto.NodeSizeDTO;
+import com.sdm.data.model.dto.UserTotalFileSizeDTO;
+import com.sdm.data.model.entity.*;
+import com.sdm.data.model.req.QueryBigFileReq;
+import org.junit.jupiter.api.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.Rollback;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Mapper层兼容性测试 - MySQL转PostgreSQL验证
+ * 覆盖所有Mapper接口的基础CRUD和自定义SQL方法
+ */
+@SpringBootTest
+@ActiveProfiles("test")
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+@Transactional
+@Rollback(true) // 确保测试后回滚,不污染数据库
+public class MapperCompatibilityTest {
+
+ @Autowired
+ private FileStorageMapper fileStorageMapper;
+
+ @Autowired
+ private FileMetadataInfoMapper fileMetadataInfoMapper;
+
+ @Autowired
+ private DimensionTemplateMapper dimensionTemplateMapper;
+
+ @Autowired
+ private DimensionTemplateHierarchyMapper dimensionTemplateHierarchyMapper;
+
+ @Autowired
+ private FileStorageQuotaMapper fileStorageQuotaMapper;
+
+ @Autowired
+ private SimulationParameterLibraryMapper simulationParameterLibraryMapper;
+
+ @Autowired
+ private SimulationParameterLibraryCategoryMapper simulationParameterLibraryCategoryMapper;
+
+ @Autowired
+ private SimulationParameterLibraryCategoryObjectMapper simulationParameterLibraryCategoryObjectMapper;
+
+ @Autowired
+ private TrainingModelMapper trainingModelMapper;
+
+ @Autowired
+ private TrainingModelAlgorithmParamMapper trainingModelAlgorithmParamMapper;
+
+ @Autowired
+ private FileMetadataExtensionMapper fileMetadataExtensionMapper;
+
+ @Autowired
+ private FilePermissionDictMapper filePermissionDictMapper;
+
+ @Autowired
+ private FileUserPermissionMapper fileUserPermissionMapper;
+
+ @Autowired
+ private FileSimulationMappingMapper fileSimulationMappingMapper;
+
+ // ==================== FileStorageMapper Tests ====================
+
+ @Test
+ @Order(1)
+ @DisplayName("FileStorageMapper - 基础CRUD测试")
+ void testFileStorageMapperCRUD() {
+ // INSERT
+ FileStorage entity = new FileStorage();
+ entity.setFileName("mapper_test.txt");
+ entity.setFileId(100L);
+ entity.setTenantId(1L);
+ entity.setUserId(1L);
+ entity.setDirId(1L);
+ entity.setFileSuffix("txt");
+ entity.setFileBizType(1);
+ entity.setFileSize(2048L);
+ entity.setCreateTime(LocalDateTime.now());
+ entity.setUpdateTime(LocalDateTime.now());
+ entity.setCreateYearMonth("2026-01");
+
+ int insertResult = fileStorageMapper.insert(entity);
+ assertEquals(1, insertResult);
+ assertNotNull(entity.getId());
+
+ // SELECT BY ID
+ FileStorage selected = fileStorageMapper.selectById(entity.getId());
+ assertNotNull(selected);
+ assertEquals("mapper_test.txt", selected.getFileName());
+
+ // UPDATE
+ selected.setFileName("mapper_test_updated.txt");
+ selected.setFileSize(4096L);
+ int updateResult = fileStorageMapper.updateById(selected);
+ assertEquals(1, updateResult);
+
+ // VERIFY UPDATE
+ FileStorage updated = fileStorageMapper.selectById(entity.getId());
+ assertEquals("mapper_test_updated.txt", updated.getFileName());
+ assertEquals(4096L, updated.getFileSize());
+
+ // DELETE
+ int deleteResult = fileStorageMapper.deleteById(entity.getId());
+ assertEquals(1, deleteResult);
+
+ // VERIFY DELETE
+ FileStorage deleted = fileStorageMapper.selectById(entity.getId());
+ assertNull(deleted);
+ }
+
+ @Test
+ @Order(2)
+ @DisplayName("FileStorageMapper - selectNodeSizeByNodeType自定义SQL测试")
+ void testFileStorageMapperSelectNodeSizeByNodeType() {
+ List result = fileStorageMapper.selectNodeSizeByNodeType(
+ Arrays.asList(1L, 2L, 3L),
+ 3,
+ 1L
+ );
+ assertNotNull(result);
+ // 验证SQL执行不报错,结果可能为空但不应该抛异常
+ }
+
+ @Test
+ @Order(3)
+ @DisplayName("FileStorageMapper - statDirStorageByTargetYm自定义SQL测试")
+ void testFileStorageMapperStatDirStorageByTargetYm() {
+ List result = fileStorageMapper.statDirStorageByTargetYm(
+ Arrays.asList(1L, 2L),
+ "2026-01",
+ 1L
+ );
+ assertNotNull(result);
+ }
+
+ @Test
+ @Order(4)
+ @DisplayName("FileStorageMapper - getTotalFileSizeByCreator自定义SQL测试")
+ void testFileStorageMapperGetTotalFileSizeByCreator() {
+ List result = fileStorageMapper.getTotalFileSizeByCreator(
+ Arrays.asList(1L),
+ 6,
+ 1L
+ );
+ assertNotNull(result);
+ }
+
+ @Test
+ @Order(5)
+ @DisplayName("FileStorageMapper - getTotalFileSizeByCreatorAndTargetYm自定义SQL测试")
+ void testFileStorageMapperGetTotalFileSizeByCreatorAndTargetYm() {
+ List result = fileStorageMapper.getTotalFileSizeByCreatorAndTargetYm(
+ Arrays.asList(1L),
+ "2026-01",
+ 1L
+ );
+ assertNotNull(result);
+ }
+
+ @Test
+ @Order(6)
+ @DisplayName("FileStorageMapper - selectBigFiles自定义SQL测试")
+ void testFileStorageMapperSelectBigFiles() {
+ QueryBigFileReq req = new QueryBigFileReq();
+ req.setIsLatest(true);
+ req.setDirIds(Arrays.asList(1L));
+
+ List result = fileStorageMapper.selectBigFiles(req, 1000L, 1L);
+ assertNotNull(result);
+ }
+
+ // ==================== FileMetadataInfoMapper Tests ====================
+
+ @Test
+ @Order(10)
+ @DisplayName("FileMetadataInfoMapper - 基础CRUD测试")
+ void testFileMetadataInfoMapperCRUD() {
+ // INSERT
+ FileMetadataInfo entity = new FileMetadataInfo();
+ entity.setRelatedResourceUuid("test-uuid-mapper");
+ entity.setRelatedResourceUuidOwnType("node");
+ entity.setOriginalName("mapper_metadata.txt");
+ entity.setDataType(2);
+ entity.setIsLatest(true);
+ entity.setTenantId(1L);
+ entity.setCreatorId(1L);
+ entity.setCreateTime(LocalDateTime.now());
+ entity.setUpdateTime(LocalDateTime.now());
+
+ int insertResult = fileMetadataInfoMapper.insert(entity);
+ assertEquals(1, insertResult);
+ assertNotNull(entity.getId());
+
+ // SELECT
+ FileMetadataInfo selected = fileMetadataInfoMapper.selectById(entity.getId());
+ assertNotNull(selected);
+ assertEquals("mapper_metadata.txt", selected.getOriginalName());
+
+ // UPDATE
+ selected.setOriginalName("mapper_metadata_updated.txt");
+ fileMetadataInfoMapper.updateById(selected);
+
+ // DELETE
+ fileMetadataInfoMapper.deleteById(entity.getId());
+ }
+
+ @Test
+ @Order(11)
+ @DisplayName("FileMetadataInfoMapper - listSimulationNodeDir自定义SQL测试")
+ void testFileMetadataInfoMapperListSimulationNodeDir() {
+ List result = fileMetadataInfoMapper.listSimulationNodeDir(
+ Arrays.asList(1L, 2L),
+ false,
+ 1L
+ );
+ assertNotNull(result);
+ }
+
+ @Test
+ @Order(12)
+ @DisplayName("FileMetadataInfoMapper - listSimulationNodeFiles自定义SQL测试")
+ void testFileMetadataInfoMapperListSimulationNodeFiles() {
+ List result = fileMetadataInfoMapper.listSimulationNodeFiles(
+ Arrays.asList(1L),
+ Arrays.asList(1L),
+ false,
+ 1L
+ );
+ assertNotNull(result);
+ }
+
+ // ==================== DimensionTemplateMapper Tests ====================
+
+ @Test
+ @Order(20)
+ @DisplayName("DimensionTemplateMapper - 基础CRUD测试")
+ void testDimensionTemplateMapperCRUD() {
+ // INSERT
+ DimensionTemplate entity = new DimensionTemplate();
+ entity.setTemplateName("Mapper测试模板");
+ entity.setTemplateType(1);
+ entity.setDescription("用于Mapper测试");
+ entity.setTenantId(1L);
+ entity.setCreatedBy(1L);
+ entity.setCreatedAt(LocalDateTime.now());
+
+ int insertResult = dimensionTemplateMapper.insert(entity);
+ assertEquals(1, insertResult);
+
+ // SELECT
+ DimensionTemplate selected = dimensionTemplateMapper.selectById(entity.getId());
+ assertNotNull(selected);
+ assertEquals("Mapper测试模板", selected.getTemplateName());
+
+ // UPDATE & DELETE
+ selected.setTemplateName("Mapper测试模板_更新");
+ dimensionTemplateMapper.updateById(selected);
+ dimensionTemplateMapper.deleteById(entity.getId());
+ }
+
+ // ==================== FileStorageQuotaMapper Tests ====================
+
+ @Test
+ @Order(30)
+ @DisplayName("FileStorageQuotaMapper - 基础CRUD测试")
+ void testFileStorageQuotaMapperCRUD() {
+ // INSERT
+ FileStorageQuota entity = new FileStorageQuota();
+ entity.setUserId(100L);
+ entity.setTenantId(1L);
+ entity.setQuotaValue(5368709120L); // 5GB
+ entity.setQuotaUnit("GB");
+ entity.setUsedValue(0L);
+ entity.setStatus("NORMAL");
+
+ int insertResult = fileStorageQuotaMapper.insert(entity);
+ assertEquals(1, insertResult);
+
+ // SELECT
+ FileStorageQuota selected = fileStorageQuotaMapper.selectById(entity.getId());
+ assertNotNull(selected);
+ assertEquals("GB", selected.getQuotaUnit());
+
+ // UPDATE & DELETE
+ selected.setStatus("WARNING");
+ fileStorageQuotaMapper.updateById(selected);
+ fileStorageQuotaMapper.deleteById(entity.getId());
+ }
+
+ // ==================== SimulationParameterLibraryMapper Tests ====================
+
+ @Test
+ @Order(40)
+ @DisplayName("SimulationParameterLibraryMapper - 基础CRUD测试")
+ void testSimulationParameterLibraryMapperCRUD() {
+ // INSERT
+ SimulationParameterLibrary entity = new SimulationParameterLibrary();
+ entity.setParameterLibraryName("Mapper测试参数库");
+ entity.setTenantId(1L);
+ entity.setCreatorId(1L);
+ entity.setCreateTime(LocalDateTime.now());
+
+ int insertResult = simulationParameterLibraryMapper.insert(entity);
+ assertEquals(1, insertResult);
+
+ // SELECT
+ SimulationParameterLibrary selected = simulationParameterLibraryMapper.selectById(entity.getId());
+ assertNotNull(selected);
+
+ // DELETE
+ simulationParameterLibraryMapper.deleteById(entity.getId());
+ }
+
+ // ==================== TrainingModelMapper Tests ====================
+
+ @Test
+ @Order(50)
+ @DisplayName("TrainingModelMapper - 基础CRUD测试")
+ void testTrainingModelMapperCRUD() {
+ // INSERT
+ TrainingModel entity = new TrainingModel();
+ entity.setModelName("Mapper测试模型");
+ entity.setAlgorithmType("SVM");
+ entity.setTrainer("tester");
+ entity.setHandleStatus("待开始");
+ entity.setTrainingStatus("待开始");
+ entity.setPredStatus("待开始");
+ entity.setTenantId(1L);
+ entity.setCreator(1L);
+ entity.setCreateTime(LocalDateTime.now());
+
+ int insertResult = trainingModelMapper.insert(entity);
+ assertEquals(1, insertResult);
+
+ // SELECT
+ TrainingModel selected = trainingModelMapper.selectById(entity.getId());
+ assertNotNull(selected);
+ assertEquals("Mapper测试模型", selected.getModelName());
+
+ // UPDATE & DELETE
+ selected.setModelName("Mapper测试模型_更新");
+ trainingModelMapper.updateById(selected);
+ trainingModelMapper.deleteById(entity.getId());
+ }
+
+ // ==================== 条件查询测试 ====================
+
+ @Test
+ @Order(60)
+ @DisplayName("LambdaQueryWrapper条件查询测试")
+ void testLambdaQueryWrapper() {
+ // 测试MyBatis-Plus的LambdaQueryWrapper在PostgreSQL下的兼容性
+ LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
+ wrapper.eq(FileStorage::getTenantId, 1L)
+ .like(FileStorage::getFileName, "test")
+ .orderByDesc(FileStorage::getCreateTime);
+
+ List result = fileStorageMapper.selectList(wrapper);
+ assertNotNull(result);
+ }
+
+ @Test
+ @Order(61)
+ @DisplayName("批量查询测试")
+ void testBatchSelect() {
+ List result = fileStorageMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));
+ assertNotNull(result);
+ }
+
+ // ==================== 其他Mapper基础测试 ====================
+
+ @Test
+ @Order(70)
+ @DisplayName("FileMetadataExtensionMapper - 基础测试")
+ void testFileMetadataExtensionMapper() {
+ List result = fileMetadataExtensionMapper.selectList(null);
+ assertNotNull(result);
+ }
+
+ @Test
+ @Order(71)
+ @DisplayName("FilePermissionDictMapper - 基础测试")
+ void testFilePermissionDictMapper() {
+ List result = filePermissionDictMapper.selectList(null);
+ assertNotNull(result);
+ }
+
+ @Test
+ @Order(72)
+ @DisplayName("FileUserPermissionMapper - 基础测试")
+ void testFileUserPermissionMapper() {
+ List result = fileUserPermissionMapper.selectList(null);
+ assertNotNull(result);
+ }
+
+ @Test
+ @Order(73)
+ @DisplayName("FileSimulationMappingMapper - 基础测试")
+ void testFileSimulationMappingMapper() {
+ List result = fileSimulationMappingMapper.selectList(null);
+ assertNotNull(result);
+ }
+
+ @Test
+ @Order(74)
+ @DisplayName("DimensionTemplateHierarchyMapper - 基础测试")
+ void testDimensionTemplateHierarchyMapper() {
+ List result = dimensionTemplateHierarchyMapper.selectList(null);
+ assertNotNull(result);
+ }
+
+ @Test
+ @Order(75)
+ @DisplayName("SimulationParameterLibraryCategoryMapper - 基础测试")
+ void testSimulationParameterLibraryCategoryMapper() {
+ List result = simulationParameterLibraryCategoryMapper.selectList(null);
+ assertNotNull(result);
+ }
+
+ @Test
+ @Order(76)
+ @DisplayName("SimulationParameterLibraryCategoryObjectMapper - 基础测试")
+ void testSimulationParameterLibraryCategoryObjectMapper() {
+ List result = simulationParameterLibraryCategoryObjectMapper.selectList(null);
+ assertNotNull(result);
+ }
+
+ @Test
+ @Order(77)
+ @DisplayName("TrainingModelAlgorithmParamMapper - 基础测试")
+ void testTrainingModelAlgorithmParamMapper() {
+ List result = trainingModelAlgorithmParamMapper.selectList(null);
+ assertNotNull(result);
+ }
+}
diff --git a/data/src/test/java/com/sdm/data/dao/PostgreSQLCompatibilityTest.java b/data/src/test/java/com/sdm/data/dao/PostgreSQLCompatibilityTest.java
new file mode 100644
index 00000000..1e18f4c0
--- /dev/null
+++ b/data/src/test/java/com/sdm/data/dao/PostgreSQLCompatibilityTest.java
@@ -0,0 +1,351 @@
+package com.sdm.data.dao;
+
+import com.sdm.data.model.dto.NodeSizeDTO;
+import com.sdm.data.model.dto.UserTotalFileSizeDTO;
+import com.sdm.data.model.entity.FileMetadataInfo;
+import com.sdm.data.model.entity.FileStorage;
+import com.sdm.data.model.req.QueryBigFileReq;
+import org.junit.jupiter.api.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.Rollback;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * PostgreSQL SQL语法兼容性专项测试
+ *
+ * 重点验证以下MySQL到PostgreSQL的语法差异:
+ * 1. DATE_SUB函数 -> PostgreSQL的INTERVAL语法
+ * 2. LIMIT在UNION中的使用
+ * 3. CONCAT函数
+ * 4. Boolean字段处理
+ * 5. 字段名大小写敏感性
+ */
+@SpringBootTest
+@ActiveProfiles("test")
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+@Transactional
+@Rollback(true) // 确保测试后回滚,不污染数据库
+public class PostgreSQLCompatibilityTest {
+
+ @Autowired
+ private FileStorageMapper fileStorageMapper;
+
+ @Autowired
+ private FileMetadataInfoMapper fileMetadataInfoMapper;
+
+ // ==================== DATE_SUB/INTERVAL 语法测试 ====================
+
+ @Test
+ @Order(1)
+ @DisplayName("测试时间间隔查询 - selectNodeSizeByNodeType (MySQL: DATE_SUB)")
+ void testDateIntervalQuery() {
+ // 准备测试数据
+ FileStorage entity = createTestFileStorage("interval_test.txt");
+ entity.setCreateTime(LocalDateTime.now().minusMonths(1)); // 1个月前
+ fileStorageMapper.insert(entity);
+
+ // 测试3个月内的数据查询
+ List result = fileStorageMapper.selectNodeSizeByNodeType(
+ Arrays.asList(entity.getDirId()),
+ 3, // 3个月
+ 1L
+ );
+
+ assertNotNull(result);
+ // 验证能够查询到1个月前的数据
+ }
+
+ @Test
+ @Order(2)
+ @DisplayName("测试时间间隔查询 - getTotalFileSizeByCreator (MySQL: DATE_SUB)")
+ void testDateIntervalQueryForUser() {
+ // 测试用户文件大小统计的时间间隔查询
+ List result = fileStorageMapper.getTotalFileSizeByCreator(
+ Arrays.asList(1L),
+ 6, // 6个月
+ 1L
+ );
+
+ assertNotNull(result);
+ }
+
+ // ==================== UNION ALL + LIMIT 语法测试 ====================
+
+ @Test
+ @Order(10)
+ @DisplayName("测试UNION ALL查询 - statDirStorageByTargetYm")
+ void testUnionAllQuery() {
+ // 准备不同月份的测试数据
+ FileStorage entity1 = createTestFileStorage("union_test_1.txt");
+ entity1.setCreateYearMonth("2026-01");
+ fileStorageMapper.insert(entity1);
+
+ FileStorage entity2 = createTestFileStorage("union_test_2.txt");
+ entity2.setCreateYearMonth("2025-12");
+ fileStorageMapper.insert(entity2);
+
+ // 测试UNION ALL查询(历史累计 + 当月增量)
+ List result = fileStorageMapper.statDirStorageByTargetYm(
+ Arrays.asList(entity1.getDirId()),
+ "2026-01",
+ 1L
+ );
+
+ assertNotNull(result);
+ // 应该返回BEFORE和INCREMENT两种类型的统计
+ }
+
+ @Test
+ @Order(11)
+ @DisplayName("测试带LIMIT的UNION查询 - getTotalFileSizeByCreatorAndTargetYm (CTE)")
+ void testUnionWithLimitAndCTE() {
+ // 测试使用CTE(WITH)的复杂查询
+ List result = fileStorageMapper.getTotalFileSizeByCreatorAndTargetYm(
+ null, // 不传userIds,测试LIMIT 10的分支
+ "2026-01",
+ 1L
+ );
+
+ assertNotNull(result);
+ }
+
+ @Test
+ @Order(12)
+ @DisplayName("测试带LIMIT的查询 - getdefaultNodeNameByNodeSize")
+ void testLimitQuery() {
+ List result = fileStorageMapper.getdefaultNodeNameByNodeSize(
+ "node",
+ 5 // LIMIT 5
+ );
+
+ assertNotNull(result);
+ assertTrue(result.size() <= 5);
+ }
+
+ // ==================== CONCAT 函数测试 ====================
+
+ @Test
+ @Order(20)
+ @DisplayName("测试CONCAT函数 - selectBigFiles的LIKE查询")
+ void testConcatInLikeQuery() {
+ // 准备测试数据
+ FileStorage fileStorage = createTestFileStorage("concat_test_file.txt");
+ fileStorageMapper.insert(fileStorage);
+
+ FileMetadataInfo metadata = new FileMetadataInfo();
+ metadata.setId(fileStorage.getFileId());
+ metadata.setOriginalName("concat_test_file.txt");
+ metadata.setIsLatest(true);
+ metadata.setTenantId(1L);
+ metadata.setDataType(2);
+ metadata.setApproveType(0);
+ metadata.setCreateTime(LocalDateTime.now());
+ metadata.setUpdateTime(LocalDateTime.now());
+ fileMetadataInfoMapper.insert(metadata);
+
+ // 测试CONCAT在LIKE中的使用
+ QueryBigFileReq req = new QueryBigFileReq();
+ req.setIsLatest(true);
+ req.setFileName("concat"); // 模糊匹配
+
+ List result = fileStorageMapper.selectBigFiles(req, 0L, 1L);
+
+ assertNotNull(result);
+ }
+
+ // ==================== Boolean 字段测试 ====================
+
+ @Test
+ @Order(30)
+ @DisplayName("测试Boolean字段 - isLatest = true")
+ void testBooleanField() {
+ // 测试PostgreSQL的Boolean类型处理
+ FileMetadataInfo entityTrue = new FileMetadataInfo();
+ entityTrue.setOriginalName("boolean_true.txt");
+ entityTrue.setIsLatest(true);
+ entityTrue.setIsRoot(false);
+ entityTrue.setTenantId(1L);
+ entityTrue.setDataType(2);
+ entityTrue.setCreateTime(LocalDateTime.now());
+ entityTrue.setUpdateTime(LocalDateTime.now());
+ fileMetadataInfoMapper.insert(entityTrue);
+
+ FileMetadataInfo entityFalse = new FileMetadataInfo();
+ entityFalse.setOriginalName("boolean_false.txt");
+ entityFalse.setIsLatest(false);
+ entityFalse.setIsRoot(true);
+ entityFalse.setTenantId(1L);
+ entityFalse.setDataType(2);
+ entityFalse.setCreateTime(LocalDateTime.now());
+ entityFalse.setUpdateTime(LocalDateTime.now());
+ fileMetadataInfoMapper.insert(entityFalse);
+
+ // 测试listSimulationNodeFiles中的 isLatest = true
+ List result = fileMetadataInfoMapper.listSimulationNodeFiles(
+ Arrays.asList(entityTrue.getParentId() != null ? entityTrue.getParentId() : 0L),
+ Collections.emptyList(),
+ false,
+ 1L
+ );
+
+ assertNotNull(result);
+ }
+
+ // ==================== 字段名大小写敏感性测试 ====================
+
+ @Test
+ @Order(40)
+ @DisplayName("测试驼峰字段名映射 - fileName/fileSize等")
+ void testCamelCaseFieldMapping() {
+ FileStorage entity = new FileStorage();
+ entity.setFileName("camelCase_test.txt");
+ entity.setFileId(System.currentTimeMillis());
+ entity.setFileSize(2048L);
+ entity.setFileBizType(1);
+ entity.setFileSuffix("txt");
+ entity.setTenantId(1L);
+ entity.setUserId(1L);
+ entity.setDirId(1L);
+ entity.setCreateYearMonth("2026-01");
+ entity.setCreateTime(LocalDateTime.now());
+ entity.setUpdateTime(LocalDateTime.now());
+ entity.setFullPath("/test/camelCase_test.txt");
+
+ // 插入
+ int insertResult = fileStorageMapper.insert(entity);
+ assertEquals(1, insertResult);
+
+ // 查询并验证所有驼峰命名字段
+ FileStorage selected = fileStorageMapper.selectById(entity.getId());
+ assertNotNull(selected);
+ assertEquals("camelCase_test.txt", selected.getFileName());
+ assertEquals(2048L, selected.getFileSize());
+ assertEquals(1, selected.getFileBizType());
+ assertEquals("txt", selected.getFileSuffix());
+ assertEquals("2026-01", selected.getCreateYearMonth());
+ assertEquals("/test/camelCase_test.txt", selected.getFullPath());
+ }
+
+ // ==================== NULL值处理测试 ====================
+
+ @Test
+ @Order(50)
+ @DisplayName("测试NULL值条件查询")
+ void testNullConditionQuery() {
+ // 测试IS NULL查询
+ List result = fileMetadataInfoMapper.listSimulationNodeDir(
+ Arrays.asList(1L),
+ false, // 不过滤空数据
+ 1L
+ );
+
+ assertNotNull(result);
+ }
+
+ // ==================== 空集合参数测试 ====================
+
+ @Test
+ @Order(60)
+ @DisplayName("测试空集合参数处理")
+ void testEmptyCollectionParameter() {
+ // 测试传入空列表时的处理
+ List result = fileStorageMapper.getTotalFileSizeByCreator(
+ Collections.emptyList(), // 空列表
+ 6,
+ 1L
+ );
+
+ assertNotNull(result);
+ // 应该返回前10个用户(根据SQL逻辑)
+ }
+
+ // ==================== ORDER BY 测试 ====================
+
+ @Test
+ @Order(70)
+ @DisplayName("测试ORDER BY子句")
+ void testOrderByClause() {
+ // 准备多条测试数据
+ for (int i = 1; i <= 3; i++) {
+ FileStorage entity = createTestFileStorage("order_test_" + i + ".txt");
+ entity.setFileSize(i * 1000L);
+ entity.setUpdateTime(LocalDateTime.now().minusHours(i));
+ fileStorageMapper.insert(entity);
+ }
+
+ // 测试selectBigFiles中的ORDER BY updateTime DESC
+ QueryBigFileReq req = new QueryBigFileReq();
+ req.setIsLatest(true);
+ req.setFileName("order_test");
+
+ List result = fileStorageMapper.selectBigFiles(req, 0L, 1L);
+
+ assertNotNull(result);
+ // 验证是否按updateTime降序排列
+ if (result.size() >= 2) {
+ assertTrue(
+ result.get(0).getUpdateTime().isAfter(result.get(1).getUpdateTime()) ||
+ result.get(0).getUpdateTime().isEqual(result.get(1).getUpdateTime())
+ );
+ }
+ }
+
+ // ==================== 聚合函数测试 ====================
+
+ @Test
+ @Order(80)
+ @DisplayName("测试SUM聚合函数")
+ void testSumAggregation() {
+ // 准备测试数据
+ Long testDirId = 99999L;
+ FileStorage entity1 = createTestFileStorage("sum_test_1.txt");
+ entity1.setDirId(testDirId);
+ entity1.setFileSize(1000L);
+ fileStorageMapper.insert(entity1);
+
+ FileStorage entity2 = createTestFileStorage("sum_test_2.txt");
+ entity2.setDirId(testDirId);
+ entity2.setFileSize(2000L);
+ fileStorageMapper.insert(entity2);
+
+ // 测试SUM聚合
+ List result = fileStorageMapper.selectNodeSizeByNodeType(
+ Arrays.asList(testDirId),
+ 12,
+ 1L
+ );
+
+ assertNotNull(result);
+ if (!result.isEmpty()) {
+ // 验证总大小 = 1000 + 2000 = 3000
+ assertEquals(3000L, result.get(0).getTotalSize());
+ }
+ }
+
+ // ==================== Helper Methods ====================
+
+ private FileStorage createTestFileStorage(String fileName) {
+ FileStorage entity = new FileStorage();
+ entity.setFileName(fileName);
+ entity.setFileId(System.currentTimeMillis());
+ entity.setTenantId(1L);
+ entity.setUserId(1L);
+ entity.setDirId(1L);
+ entity.setFileSuffix("txt");
+ entity.setFileBizType(1);
+ entity.setFileSize(1024L);
+ entity.setCreateTime(LocalDateTime.now());
+ entity.setUpdateTime(LocalDateTime.now());
+ entity.setCreateYearMonth("2026-01");
+ return entity;
+ }
+}
diff --git a/data/src/test/java/com/sdm/data/service/ServiceCrudTest.java b/data/src/test/java/com/sdm/data/service/ServiceCrudTest.java
new file mode 100644
index 00000000..9247e226
--- /dev/null
+++ b/data/src/test/java/com/sdm/data/service/ServiceCrudTest.java
@@ -0,0 +1,517 @@
+package com.sdm.data.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.sdm.data.model.entity.*;
+import org.junit.jupiter.api.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.Rollback;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Service层CRUD测试 - MySQL转PostgreSQL验证
+ * 覆盖所有Service的基础CRUD操作
+ */
+@SpringBootTest
+@ActiveProfiles("test")
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+@Transactional
+@Rollback(true) // 确保测试后回滚,不污染数据库
+public class ServiceCrudTest {
+
+ @Autowired
+ private IFileStorageService fileStorageService;
+
+ @Autowired
+ private IFileMetadataInfoService fileMetadataInfoService;
+
+ @Autowired
+ private IDimensionTemplateService dimensionTemplateService;
+
+ @Autowired
+ private IFileStorageQuotaService fileStorageQuotaService;
+
+ @Autowired
+ private ISimulationParameterLibraryService simulationParameterLibraryService;
+
+ @Autowired
+ private ITrainingModelService trainingModelService;
+
+ @Autowired
+ private IFileMetadataExtensionService fileMetadataExtensionService;
+
+ @Autowired
+ private IFileSimulationMappingService fileSimulationMappingService;
+
+ @Autowired
+ private IFileUserPermissionService fileUserPermissionService;
+
+ @Autowired
+ private IDimensionTemplateHierarchyService dimensionTemplateHierarchyService;
+
+ @Autowired
+ private ISimulationParameterLibraryCategoryService simulationParameterLibraryCategoryService;
+
+ @Autowired
+ private ISimulationParameterLibraryCategoryObjectService simulationParameterLibraryCategoryObjectService;
+
+ @Autowired
+ private ITrainingModelAlgorithmParamService trainingModelAlgorithmParamService;
+
+ // ==================== IFileStorageService Tests ====================
+
+ @Test
+ @Order(1)
+ @DisplayName("IFileStorageService - save保存测试")
+ void testFileStorageServiceSave() {
+ FileStorage entity = createFileStorageEntity("service_test.txt");
+
+ boolean result = fileStorageService.save(entity);
+
+ assertTrue(result);
+ assertNotNull(entity.getId());
+ }
+
+ @Test
+ @Order(2)
+ @DisplayName("IFileStorageService - getById查询测试")
+ void testFileStorageServiceGetById() {
+ // 先插入
+ FileStorage entity = createFileStorageEntity("service_get_test.txt");
+ fileStorageService.save(entity);
+
+ // 再查询
+ FileStorage result = fileStorageService.getById(entity.getId());
+
+ assertNotNull(result);
+ assertEquals("service_get_test.txt", result.getFileName());
+ }
+
+ @Test
+ @Order(3)
+ @DisplayName("IFileStorageService - updateById更新测试")
+ void testFileStorageServiceUpdate() {
+ FileStorage entity = createFileStorageEntity("service_update_test.txt");
+ fileStorageService.save(entity);
+
+ entity.setFileName("service_update_test_modified.txt");
+ entity.setFileSize(8192L);
+ boolean result = fileStorageService.updateById(entity);
+
+ assertTrue(result);
+
+ FileStorage updated = fileStorageService.getById(entity.getId());
+ assertEquals("service_update_test_modified.txt", updated.getFileName());
+ assertEquals(8192L, updated.getFileSize());
+ }
+
+ @Test
+ @Order(4)
+ @DisplayName("IFileStorageService - removeById删除测试")
+ void testFileStorageServiceRemove() {
+ FileStorage entity = createFileStorageEntity("service_remove_test.txt");
+ fileStorageService.save(entity);
+
+ boolean result = fileStorageService.removeById(entity.getId());
+
+ assertTrue(result);
+ assertNull(fileStorageService.getById(entity.getId()));
+ }
+
+ @Test
+ @Order(5)
+ @DisplayName("IFileStorageService - list列表查询测试")
+ void testFileStorageServiceList() {
+ List result = fileStorageService.list();
+ assertNotNull(result);
+ }
+
+ @Test
+ @Order(6)
+ @DisplayName("IFileStorageService - 条件查询测试")
+ void testFileStorageServiceListWithWrapper() {
+ FileStorage entity = createFileStorageEntity("service_wrapper_test.txt");
+ entity.setFileBizType(99);
+ fileStorageService.save(entity);
+
+ LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
+ wrapper.eq(FileStorage::getFileBizType, 99);
+
+ List result = fileStorageService.list(wrapper);
+
+ assertNotNull(result);
+ assertFalse(result.isEmpty());
+ }
+
+ @Test
+ @Order(7)
+ @DisplayName("IFileStorageService - 分页查询测试")
+ void testFileStorageServicePage() {
+ Page page = new Page<>(1, 10);
+
+ Page result = fileStorageService.page(page);
+
+ assertNotNull(result);
+ assertNotNull(result.getRecords());
+ }
+
+ @Test
+ @Order(8)
+ @DisplayName("IFileStorageService - 批量保存测试")
+ void testFileStorageServiceSaveBatch() {
+ List entities = Arrays.asList(
+ createFileStorageEntity("batch_1.txt"),
+ createFileStorageEntity("batch_2.txt"),
+ createFileStorageEntity("batch_3.txt")
+ );
+
+ boolean result = fileStorageService.saveBatch(entities);
+
+ assertTrue(result);
+ entities.forEach(e -> assertNotNull(e.getId()));
+ }
+
+ @Test
+ @Order(9)
+ @DisplayName("IFileStorageService - 自定义方法selectNodeSizeByNodeType测试")
+ void testFileStorageServiceSelectNodeSizeByNodeType() {
+ var result = fileStorageService.selectNodeSizeByNodeType(
+ Arrays.asList(1L, 2L),
+ 3,
+ 1L
+ );
+ assertNotNull(result);
+ }
+
+ // ==================== IFileMetadataInfoService Tests ====================
+
+ @Test
+ @Order(20)
+ @DisplayName("IFileMetadataInfoService - CRUD完整测试")
+ void testFileMetadataInfoServiceCRUD() {
+ // CREATE
+ FileMetadataInfo entity = createFileMetadataInfoEntity("service_metadata.txt");
+ boolean saveResult = fileMetadataInfoService.save(entity);
+ assertTrue(saveResult);
+
+ // READ
+ FileMetadataInfo read = fileMetadataInfoService.getById(entity.getId());
+ assertNotNull(read);
+
+ // UPDATE
+ read.setOriginalName("service_metadata_updated.txt");
+ boolean updateResult = fileMetadataInfoService.updateById(read);
+ assertTrue(updateResult);
+
+ // DELETE
+ boolean deleteResult = fileMetadataInfoService.removeById(entity.getId());
+ assertTrue(deleteResult);
+ }
+
+ @Test
+ @Order(21)
+ @DisplayName("IFileMetadataInfoService - listSimulationNodeDir测试")
+ void testFileMetadataInfoServiceListSimulationNodeDir() {
+ var result = fileMetadataInfoService.listSimulationNodeDir(
+ Arrays.asList(1L, 2L),
+ false,
+ 1L
+ );
+ assertNotNull(result);
+ }
+
+ // ==================== IDimensionTemplateService Tests ====================
+
+ @Test
+ @Order(30)
+ @DisplayName("IDimensionTemplateService - CRUD完整测试")
+ void testDimensionTemplateServiceCRUD() {
+ // CREATE
+ DimensionTemplate entity = createDimensionTemplateEntity("Service测试模板");
+ boolean saveResult = dimensionTemplateService.save(entity);
+ assertTrue(saveResult);
+
+ // READ
+ DimensionTemplate read = dimensionTemplateService.getById(entity.getId());
+ assertNotNull(read);
+ assertEquals("Service测试模板", read.getTemplateName());
+
+ // UPDATE
+ read.setTemplateName("Service测试模板_更新");
+ boolean updateResult = dimensionTemplateService.updateById(read);
+ assertTrue(updateResult);
+
+ // DELETE
+ boolean deleteResult = dimensionTemplateService.removeById(entity.getId());
+ assertTrue(deleteResult);
+ }
+
+ // ==================== IFileStorageQuotaService Tests ====================
+
+ @Test
+ @Order(40)
+ @DisplayName("IFileStorageQuotaService - CRUD完整测试")
+ void testFileStorageQuotaServiceCRUD() {
+ // CREATE
+ FileStorageQuota entity = createFileStorageQuotaEntity(200L);
+ boolean saveResult = fileStorageQuotaService.save(entity);
+ assertTrue(saveResult);
+
+ // READ
+ FileStorageQuota read = fileStorageQuotaService.getById(entity.getId());
+ assertNotNull(read);
+
+ // UPDATE
+ read.setStatus("WARNING");
+ boolean updateResult = fileStorageQuotaService.updateById(read);
+ assertTrue(updateResult);
+
+ // DELETE
+ boolean deleteResult = fileStorageQuotaService.removeById(entity.getId());
+ assertTrue(deleteResult);
+ }
+
+ // ==================== ISimulationParameterLibraryService Tests ====================
+
+ @Test
+ @Order(50)
+ @DisplayName("ISimulationParameterLibraryService - CRUD完整测试")
+ void testSimulationParameterLibraryServiceCRUD() {
+ // CREATE
+ SimulationParameterLibrary entity = createSimulationParameterLibraryEntity("Service测试参数库");
+ boolean saveResult = simulationParameterLibraryService.save(entity);
+ assertTrue(saveResult);
+
+ // READ
+ SimulationParameterLibrary read = simulationParameterLibraryService.getById(entity.getId());
+ assertNotNull(read);
+
+ // UPDATE
+ read.setParameterLibraryName("Service测试参数库_更新");
+ boolean updateResult = simulationParameterLibraryService.updateById(read);
+ assertTrue(updateResult);
+
+ // DELETE
+ boolean deleteResult = simulationParameterLibraryService.removeById(entity.getId());
+ assertTrue(deleteResult);
+ }
+
+ // ==================== ITrainingModelService Tests ====================
+
+ @Test
+ @Order(60)
+ @DisplayName("ITrainingModelService - CRUD完整测试")
+ void testTrainingModelServiceCRUD() {
+ // CREATE
+ TrainingModel entity = createTrainingModelEntity("Service测试模型");
+ boolean saveResult = trainingModelService.save(entity);
+ assertTrue(saveResult);
+
+ // READ
+ TrainingModel read = trainingModelService.getById(entity.getId());
+ assertNotNull(read);
+ assertEquals("Service测试模型", read.getModelName());
+
+ // UPDATE
+ read.setModelName("Service测试模型_更新");
+ boolean updateResult = trainingModelService.updateById(read);
+ assertTrue(updateResult);
+
+ // DELETE
+ boolean deleteResult = trainingModelService.removeById(entity.getId());
+ assertTrue(deleteResult);
+ }
+
+ // ==================== 其他Service基础CRUD测试 ====================
+
+ @Test
+ @Order(70)
+ @DisplayName("IFileMetadataExtensionService - 基础测试")
+ void testFileMetadataExtensionService() {
+ List result = fileMetadataExtensionService.list();
+ assertNotNull(result);
+ }
+
+ @Test
+ @Order(71)
+ @DisplayName("IFileSimulationMappingService - 基础测试")
+ void testFileSimulationMappingService() {
+ List result = fileSimulationMappingService.list();
+ assertNotNull(result);
+ }
+
+ @Test
+ @Order(72)
+ @DisplayName("IFileUserPermissionService - 基础测试")
+ void testFileUserPermissionService() {
+ List result = fileUserPermissionService.list();
+ assertNotNull(result);
+ }
+
+ @Test
+ @Order(73)
+ @DisplayName("IDimensionTemplateHierarchyService - 基础测试")
+ void testDimensionTemplateHierarchyService() {
+ List result = dimensionTemplateHierarchyService.list();
+ assertNotNull(result);
+ }
+
+ @Test
+ @Order(74)
+ @DisplayName("ISimulationParameterLibraryCategoryService - 基础测试")
+ void testSimulationParameterLibraryCategoryService() {
+ List result = simulationParameterLibraryCategoryService.list();
+ assertNotNull(result);
+ }
+
+ @Test
+ @Order(75)
+ @DisplayName("ISimulationParameterLibraryCategoryObjectService - 基础测试")
+ void testSimulationParameterLibraryCategoryObjectService() {
+ List result = simulationParameterLibraryCategoryObjectService.list();
+ assertNotNull(result);
+ }
+
+ @Test
+ @Order(76)
+ @DisplayName("ITrainingModelAlgorithmParamService - 基础测试")
+ void testTrainingModelAlgorithmParamService() {
+ List result = trainingModelAlgorithmParamService.list();
+ assertNotNull(result);
+ }
+
+ // ==================== 复杂查询测试 ====================
+
+ @Test
+ @Order(80)
+ @DisplayName("复杂条件组合查询测试")
+ void testComplexQuery() {
+ // 准备测试数据
+ FileStorage entity1 = createFileStorageEntity("complex_1.txt");
+ entity1.setFileBizType(100);
+ entity1.setFileSize(1000L);
+ fileStorageService.save(entity1);
+
+ FileStorage entity2 = createFileStorageEntity("complex_2.txt");
+ entity2.setFileBizType(100);
+ entity2.setFileSize(2000L);
+ fileStorageService.save(entity2);
+
+ // 复杂查询
+ LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
+ wrapper.eq(FileStorage::getFileBizType, 100)
+ .ge(FileStorage::getFileSize, 1000L)
+ .orderByDesc(FileStorage::getFileSize);
+
+ List result = fileStorageService.list(wrapper);
+
+ assertNotNull(result);
+ assertEquals(2, result.size());
+ // 验证排序
+ assertTrue(result.get(0).getFileSize() >= result.get(1).getFileSize());
+ }
+
+ @Test
+ @Order(81)
+ @DisplayName("统计count测试")
+ void testCount() {
+ long count = fileStorageService.count();
+ assertTrue(count >= 0);
+ }
+
+ @Test
+ @Order(82)
+ @DisplayName("条件统计count测试")
+ void testCountWithWrapper() {
+ LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
+ wrapper.eq(FileStorage::getTenantId, 1L);
+
+ long count = fileStorageService.count(wrapper);
+ assertTrue(count >= 0);
+ }
+
+ // ==================== Helper Methods ====================
+
+ private FileStorage createFileStorageEntity(String fileName) {
+ FileStorage entity = new FileStorage();
+ entity.setFileName(fileName);
+ entity.setFileId(System.currentTimeMillis());
+ entity.setTenantId(1L);
+ entity.setUserId(1L);
+ entity.setDirId(1L);
+ entity.setFileSuffix("txt");
+ entity.setFileBizType(1);
+ entity.setFileSize(1024L);
+ entity.setCreateTime(LocalDateTime.now());
+ entity.setUpdateTime(LocalDateTime.now());
+ entity.setCreateYearMonth("2026-01");
+ return entity;
+ }
+
+ private FileMetadataInfo createFileMetadataInfoEntity(String originalName) {
+ FileMetadataInfo entity = new FileMetadataInfo();
+ entity.setRelatedResourceUuid("uuid-" + System.currentTimeMillis());
+ entity.setRelatedResourceUuidOwnType("node");
+ entity.setOriginalName(originalName);
+ entity.setDataType(2);
+ entity.setIsLatest(true);
+ entity.setTenantId(1L);
+ entity.setCreatorId(1L);
+ entity.setCreateTime(LocalDateTime.now());
+ entity.setUpdateTime(LocalDateTime.now());
+ return entity;
+ }
+
+ private DimensionTemplate createDimensionTemplateEntity(String templateName) {
+ DimensionTemplate entity = new DimensionTemplate();
+ entity.setTemplateName(templateName);
+ entity.setTemplateType(1);
+ entity.setDescription("测试描述");
+ entity.setTenantId(1L);
+ entity.setCreatedBy(1L);
+ entity.setCreatedAt(LocalDateTime.now());
+ return entity;
+ }
+
+ private FileStorageQuota createFileStorageQuotaEntity(Long userId) {
+ FileStorageQuota entity = new FileStorageQuota();
+ entity.setUserId(userId);
+ entity.setTenantId(1L);
+ entity.setQuotaValue(5368709120L);
+ entity.setQuotaUnit("GB");
+ entity.setUsedValue(0L);
+ entity.setStatus("NORMAL");
+ return entity;
+ }
+
+ private SimulationParameterLibrary createSimulationParameterLibraryEntity(String name) {
+ SimulationParameterLibrary entity = new SimulationParameterLibrary();
+ entity.setParameterLibraryName(name);
+ entity.setTenantId(1L);
+ entity.setCreatorId(1L);
+ entity.setCreateTime(LocalDateTime.now());
+ return entity;
+ }
+
+ private TrainingModel createTrainingModelEntity(String modelName) {
+ TrainingModel entity = new TrainingModel();
+ entity.setModelName(modelName);
+ entity.setAlgorithmType("SVM");
+ entity.setTrainer("tester");
+ entity.setHandleStatus("待开始");
+ entity.setTrainingStatus("待开始");
+ entity.setPredStatus("待开始");
+ entity.setTenantId(1L);
+ entity.setCreator(1L);
+ entity.setCreateTime(LocalDateTime.now());
+ return entity;
+ }
+}
diff --git a/data/src/test/resources/application-test.yml b/data/src/test/resources/application-test.yml
new file mode 100644
index 00000000..8f46148f
--- /dev/null
+++ b/data/src/test/resources/application-test.yml
@@ -0,0 +1,28 @@
+spring:
+ datasource:
+ # 直接连接PostgreSQL数据库进行单元测试
+ url: jdbc:postgresql://192.168.65.161:25432/spdm?currentSchema=public&stringtype=unspecified
+ driver-class-name: org.postgresql.Driver
+ username: spdm
+ password: Spdm@2026
+ hikari:
+ maximum-pool-size: 5
+ minimum-idle: 2
+ connection-timeout: 30000
+
+mybatis-plus:
+ mapper-locations: classpath*:/mapper/**/*.xml
+ configuration:
+ map-underscore-to-camel-case: false
+ log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+ global-config:
+ db-config:
+ id-type: auto
+ logic-delete-value: 1
+ logic-not-delete-value: 0
+
+logging:
+ level:
+ com.sdm.data: DEBUG
+ org.springframework.jdbc: DEBUG
+ org.apache.ibatis: DEBUG