550 changed files with 44 additions and 45071 deletions
@ -1,60 +0,0 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<parent> |
|||
<groupId>cn.iocoder.boot</groupId> |
|||
<artifactId>yudao-framework</artifactId> |
|||
<version>${revision}</version> |
|||
</parent> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
<artifactId>yudao-spring-boot-starter-test</artifactId> |
|||
<packaging>jar</packaging> |
|||
|
|||
<name>${project.artifactId}</name> |
|||
<description>测试组件,用于单元测试、集成测试</description> |
|||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url> |
|||
|
|||
<dependencies> |
|||
<dependency> |
|||
<groupId>cn.iocoder.boot</groupId> |
|||
<artifactId>yudao-common</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- DB 相关 --> |
|||
<dependency> |
|||
<groupId>cn.iocoder.boot</groupId> |
|||
<artifactId>yudao-spring-boot-starter-mybatis</artifactId> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>cn.iocoder.boot</groupId> |
|||
<artifactId>yudao-spring-boot-starter-redis</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- Test 测试相关 --> |
|||
<dependency> |
|||
<groupId>org.mockito</groupId> |
|||
<artifactId>mockito-inline</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-test</artifactId> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.h2database</groupId> <!-- 单元测试,我们采用 H2 作为数据库 --> |
|||
<artifactId>h2</artifactId> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.github.fppt</groupId> <!-- 单元测试,我们采用内嵌的 Redis 数据库 --> |
|||
<artifactId>jedis-mock</artifactId> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>uk.co.jemos.podam</groupId> <!-- 单元测试,随机生成 POJO 类 --> |
|||
<artifactId>podam</artifactId> |
|||
</dependency> |
|||
</dependencies> |
|||
</project> |
@ -1,35 +0,0 @@ |
|||
package cn.iocoder.yudao.framework.test.config; |
|||
|
|||
import com.github.fppt.jedismock.RedisServer; |
|||
import org.springframework.boot.autoconfigure.data.redis.RedisProperties; |
|||
import org.springframework.boot.context.properties.EnableConfigurationProperties; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.context.annotation.Lazy; |
|||
|
|||
import java.io.IOException; |
|||
|
|||
/** |
|||
* Redis 测试 Configuration,主要实现内嵌 Redis 的启动 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@Configuration(proxyBeanMethods = false) |
|||
@Lazy(false) // 禁止延迟加载
|
|||
@EnableConfigurationProperties(RedisProperties.class) |
|||
public class RedisTestConfiguration { |
|||
|
|||
/** |
|||
* 创建模拟的 Redis Server 服务器 |
|||
*/ |
|||
@Bean |
|||
public RedisServer redisServer(RedisProperties properties) throws IOException { |
|||
RedisServer redisServer = new RedisServer(properties.getPort()); |
|||
// 一次执行多个单元测试时,貌似创建多个 spring 容器,导致不进行 stop。这样,就导致端口被占用,无法启动。。。
|
|||
try { |
|||
redisServer.start(); |
|||
} catch (Exception ignore) {} |
|||
return redisServer; |
|||
} |
|||
|
|||
} |
@ -1,52 +0,0 @@ |
|||
package cn.iocoder.yudao.framework.test.config; |
|||
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; |
|||
import org.springframework.boot.autoconfigure.sql.init.SqlInitializationProperties; |
|||
import org.springframework.boot.context.properties.EnableConfigurationProperties; |
|||
import org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer; |
|||
import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer; |
|||
import org.springframework.boot.sql.init.DatabaseInitializationSettings; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.context.annotation.Lazy; |
|||
|
|||
import javax.sql.DataSource; |
|||
|
|||
/** |
|||
* SQL 初始化的测试 Configuration |
|||
* |
|||
* 为什么不使用 org.springframework.boot.autoconfigure.sql.init.DataSourceInitializationConfiguration 呢? |
|||
* 因为我们在单元测试会使用 spring.main.lazy-initialization 为 true,开启延迟加载。此时,会导致 DataSourceInitializationConfiguration 初始化 |
|||
* 不过呢,当前类的实现代码,基本是复制 DataSourceInitializationConfiguration 的哈! |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@Configuration(proxyBeanMethods = false) |
|||
@ConditionalOnMissingBean(AbstractScriptDatabaseInitializer.class) |
|||
@ConditionalOnSingleCandidate(DataSource.class) |
|||
@ConditionalOnClass(name = "org.springframework.jdbc.datasource.init.DatabasePopulator") |
|||
@Lazy(value = false) // 禁止延迟加载
|
|||
@EnableConfigurationProperties(SqlInitializationProperties.class) |
|||
public class SqlInitializationTestConfiguration { |
|||
|
|||
@Bean |
|||
public DataSourceScriptDatabaseInitializer dataSourceScriptDatabaseInitializer(DataSource dataSource, |
|||
SqlInitializationProperties initializationProperties) { |
|||
DatabaseInitializationSettings settings = createFrom(initializationProperties); |
|||
return new DataSourceScriptDatabaseInitializer(dataSource, settings); |
|||
} |
|||
|
|||
static DatabaseInitializationSettings createFrom(SqlInitializationProperties properties) { |
|||
DatabaseInitializationSettings settings = new DatabaseInitializationSettings(); |
|||
settings.setSchemaLocations(properties.getSchemaLocations()); |
|||
settings.setDataLocations(properties.getDataLocations()); |
|||
settings.setContinueOnError(properties.isContinueOnError()); |
|||
settings.setSeparator(properties.getSeparator()); |
|||
settings.setEncoding(properties.getEncoding()); |
|||
settings.setMode(properties.getMode()); |
|||
return settings; |
|||
} |
|||
|
|||
} |
@ -1,55 +0,0 @@ |
|||
package cn.iocoder.yudao.framework.test.core.ut; |
|||
|
|||
import cn.hutool.extra.spring.SpringUtil; |
|||
import cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration; |
|||
import cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration; |
|||
import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration; |
|||
import cn.iocoder.yudao.framework.test.config.RedisTestConfiguration; |
|||
import cn.iocoder.yudao.framework.test.config.SqlInitializationTestConfiguration; |
|||
import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure; |
|||
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; |
|||
import org.redisson.spring.starter.RedissonAutoConfiguration; |
|||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; |
|||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; |
|||
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; |
|||
import org.springframework.boot.test.context.SpringBootTest; |
|||
import org.springframework.context.annotation.Import; |
|||
import org.springframework.test.context.ActiveProfiles; |
|||
import org.springframework.test.context.jdbc.Sql; |
|||
|
|||
/** |
|||
* 依赖内存 DB + Redis 的单元测试 |
|||
* |
|||
* 相比 {@link BaseDbUnitTest} 来说,额外增加了内存 Redis |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbAndRedisUnitTest.Application.class) |
|||
@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件
|
|||
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后,清理 DB
|
|||
public class BaseDbAndRedisUnitTest { |
|||
|
|||
@Import({ |
|||
// DB 配置类
|
|||
YudaoDataSourceAutoConfiguration.class, // 自己的 DB 配置类
|
|||
DataSourceAutoConfiguration.class, // Spring DB 自动配置类
|
|||
DataSourceTransactionManagerAutoConfiguration.class, // Spring 事务自动配置类
|
|||
DruidDataSourceAutoConfigure.class, // Druid 自动配置类
|
|||
SqlInitializationTestConfiguration.class, // SQL 初始化
|
|||
// MyBatis 配置类
|
|||
YudaoMybatisAutoConfiguration.class, // 自己的 MyBatis 配置类
|
|||
MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类
|
|||
|
|||
// Redis 配置类
|
|||
RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer
|
|||
YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类
|
|||
RedisAutoConfiguration.class, // Spring Redis 自动配置类
|
|||
RedissonAutoConfiguration.class, // Redisson 自动配置类
|
|||
|
|||
// 其它配置类
|
|||
SpringUtil.class |
|||
}) |
|||
public static class Application { |
|||
} |
|||
|
|||
} |
@ -1,47 +0,0 @@ |
|||
package cn.iocoder.yudao.framework.test.core.ut; |
|||
|
|||
import cn.hutool.extra.spring.SpringUtil; |
|||
import cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration; |
|||
import cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration; |
|||
import cn.iocoder.yudao.framework.test.config.SqlInitializationTestConfiguration; |
|||
import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure; |
|||
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; |
|||
import com.github.yulichang.autoconfigure.MybatisPlusJoinAutoConfiguration; |
|||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; |
|||
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; |
|||
import org.springframework.boot.test.context.SpringBootTest; |
|||
import org.springframework.context.annotation.Import; |
|||
import org.springframework.test.context.ActiveProfiles; |
|||
import org.springframework.test.context.jdbc.Sql; |
|||
|
|||
/** |
|||
* 依赖内存 DB 的单元测试 |
|||
* |
|||
* 注意,Service 层同样适用。对于 Service 层的单元测试,我们针对自己模块的 Mapper 走的是 H2 内存数据库,针对别的模块的 Service 走的是 Mock 方法 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbUnitTest.Application.class) |
|||
@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件
|
|||
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后,清理 DB
|
|||
public class BaseDbUnitTest { |
|||
|
|||
@Import({ |
|||
// DB 配置类
|
|||
YudaoDataSourceAutoConfiguration.class, // 自己的 DB 配置类
|
|||
DataSourceAutoConfiguration.class, // Spring DB 自动配置类
|
|||
DataSourceTransactionManagerAutoConfiguration.class, // Spring 事务自动配置类
|
|||
DruidDataSourceAutoConfigure.class, // Druid 自动配置类
|
|||
SqlInitializationTestConfiguration.class, // SQL 初始化
|
|||
// MyBatis 配置类
|
|||
YudaoMybatisAutoConfiguration.class, // 自己的 MyBatis 配置类
|
|||
MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类
|
|||
MybatisPlusJoinAutoConfiguration.class, // MyBatis 的Join配置类
|
|||
|
|||
// 其它配置类
|
|||
SpringUtil.class |
|||
}) |
|||
public static class Application { |
|||
} |
|||
|
|||
} |
@ -1,13 +0,0 @@ |
|||
package cn.iocoder.yudao.framework.test.core.ut; |
|||
|
|||
import org.junit.jupiter.api.extension.ExtendWith; |
|||
import org.mockito.junit.jupiter.MockitoExtension; |
|||
|
|||
/** |
|||
* 纯 Mockito 的单元测试 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@ExtendWith(MockitoExtension.class) |
|||
public class BaseMockitoUnitTest { |
|||
} |
@ -1,36 +0,0 @@ |
|||
package cn.iocoder.yudao.framework.test.core.ut; |
|||
|
|||
import cn.hutool.extra.spring.SpringUtil; |
|||
import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration; |
|||
import cn.iocoder.yudao.framework.test.config.RedisTestConfiguration; |
|||
import org.redisson.spring.starter.RedissonAutoConfiguration; |
|||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; |
|||
import org.springframework.boot.test.context.SpringBootTest; |
|||
import org.springframework.context.annotation.Import; |
|||
import org.springframework.test.context.ActiveProfiles; |
|||
|
|||
/** |
|||
* 依赖内存 Redis 的单元测试 |
|||
* |
|||
* 相比 {@link BaseDbUnitTest} 来说,从内存 DB 改成了内存 Redis |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseRedisUnitTest.Application.class) |
|||
@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件
|
|||
public class BaseRedisUnitTest { |
|||
|
|||
@Import({ |
|||
// Redis 配置类
|
|||
RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer
|
|||
RedisAutoConfiguration.class, // Spring Redis 自动配置类
|
|||
YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类
|
|||
RedissonAutoConfiguration.class, // Redisson 自动配置类
|
|||
|
|||
// 其它配置类
|
|||
SpringUtil.class |
|||
}) |
|||
public static class Application { |
|||
} |
|||
|
|||
} |
@ -1,4 +0,0 @@ |
|||
/** |
|||
* 提供单元测试 Unit Test 的基类 |
|||
*/ |
|||
package cn.iocoder.yudao.framework.test.core.ut; |
@ -1,101 +0,0 @@ |
|||
package cn.iocoder.yudao.framework.test.core.util; |
|||
|
|||
import cn.hutool.core.util.ArrayUtil; |
|||
import cn.hutool.core.util.ReflectUtil; |
|||
import cn.iocoder.yudao.framework.common.exception.ErrorCode; |
|||
import cn.iocoder.yudao.framework.common.exception.ServiceException; |
|||
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; |
|||
import org.junit.jupiter.api.Assertions; |
|||
import org.junit.jupiter.api.function.Executable; |
|||
|
|||
import java.lang.reflect.Field; |
|||
import java.util.Arrays; |
|||
import java.util.Objects; |
|||
|
|||
import static org.junit.jupiter.api.Assertions.assertThrows; |
|||
|
|||
/** |
|||
* 单元测试,assert 断言工具类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class AssertUtils { |
|||
|
|||
/** |
|||
* 比对两个对象的属性是否一致 |
|||
* |
|||
* 注意,如果 expected 存在的属性,actual 不存在的时候,会进行忽略 |
|||
* |
|||
* @param expected 期望对象 |
|||
* @param actual 实际对象 |
|||
* @param ignoreFields 忽略的属性数组 |
|||
*/ |
|||
public static void assertPojoEquals(Object expected, Object actual, String... ignoreFields) { |
|||
Field[] expectedFields = ReflectUtil.getFields(expected.getClass()); |
|||
Arrays.stream(expectedFields).forEach(expectedField -> { |
|||
// 忽略 jacoco 自动生成的 $jacocoData 属性的情况
|
|||
if (expectedField.isSynthetic()) { |
|||
return; |
|||
} |
|||
// 如果是忽略的属性,则不进行比对
|
|||
if (ArrayUtil.contains(ignoreFields, expectedField.getName())) { |
|||
return; |
|||
} |
|||
// 忽略不存在的属性
|
|||
Field actualField = ReflectUtil.getField(actual.getClass(), expectedField.getName()); |
|||
if (actualField == null) { |
|||
return; |
|||
} |
|||
// 比对
|
|||
Assertions.assertEquals( |
|||
ReflectUtil.getFieldValue(expected, expectedField), |
|||
ReflectUtil.getFieldValue(actual, actualField), |
|||
String.format("Field(%s) 不匹配", expectedField.getName()) |
|||
); |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* 比对两个对象的属性是否一致 |
|||
* |
|||
* 注意,如果 expected 存在的属性,actual 不存在的时候,会进行忽略 |
|||
* |
|||
* @param expected 期望对象 |
|||
* @param actual 实际对象 |
|||
* @param ignoreFields 忽略的属性数组 |
|||
* @return 是否一致 |
|||
*/ |
|||
public static boolean isPojoEquals(Object expected, Object actual, String... ignoreFields) { |
|||
Field[] expectedFields = ReflectUtil.getFields(expected.getClass()); |
|||
return Arrays.stream(expectedFields).allMatch(expectedField -> { |
|||
// 如果是忽略的属性,则不进行比对
|
|||
if (ArrayUtil.contains(ignoreFields, expectedField.getName())) { |
|||
return true; |
|||
} |
|||
// 忽略不存在的属性
|
|||
Field actualField = ReflectUtil.getField(actual.getClass(), expectedField.getName()); |
|||
if (actualField == null) { |
|||
return true; |
|||
} |
|||
return Objects.equals(ReflectUtil.getFieldValue(expected, expectedField), |
|||
ReflectUtil.getFieldValue(actual, actualField)); |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* 执行方法,校验抛出的 Service 是否符合条件 |
|||
* |
|||
* @param executable 业务异常 |
|||
* @param errorCode 错误码对象 |
|||
* @param messageParams 消息参数 |
|||
*/ |
|||
public static void assertServiceException(Executable executable, ErrorCode errorCode, Object... messageParams) { |
|||
// 调用方法
|
|||
ServiceException serviceException = assertThrows(ServiceException.class, executable); |
|||
// 校验错误码
|
|||
Assertions.assertEquals(errorCode.getCode(), serviceException.getCode(), "错误码不匹配"); |
|||
String message = ServiceExceptionUtil.doFormat(errorCode.getCode(), errorCode.getMsg(), messageParams); |
|||
Assertions.assertEquals(message, serviceException.getMessage(), "错误提示不匹配"); |
|||
} |
|||
|
|||
} |
@ -1,146 +0,0 @@ |
|||
package cn.iocoder.yudao.framework.test.core.util; |
|||
|
|||
import cn.hutool.core.date.LocalDateTimeUtil; |
|||
import cn.hutool.core.util.ArrayUtil; |
|||
import cn.hutool.core.util.RandomUtil; |
|||
import cn.hutool.core.util.StrUtil; |
|||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
|||
import uk.co.jemos.podam.api.PodamFactory; |
|||
import uk.co.jemos.podam.api.PodamFactoryImpl; |
|||
|
|||
import java.lang.reflect.Type; |
|||
import java.time.LocalDateTime; |
|||
import java.util.Arrays; |
|||
import java.util.Date; |
|||
import java.util.List; |
|||
import java.util.Set; |
|||
import java.util.function.Consumer; |
|||
import java.util.stream.Collectors; |
|||
import java.util.stream.Stream; |
|||
|
|||
/** |
|||
* 随机工具类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class RandomUtils { |
|||
|
|||
private static final int RANDOM_STRING_LENGTH = 10; |
|||
|
|||
private static final int TINYINT_MAX = 127; |
|||
|
|||
private static final int RANDOM_DATE_MAX = 30; |
|||
|
|||
private static final int RANDOM_COLLECTION_LENGTH = 5; |
|||
|
|||
private static final PodamFactory PODAM_FACTORY = new PodamFactoryImpl(); |
|||
|
|||
static { |
|||
// 字符串
|
|||
PODAM_FACTORY.getStrategy().addOrReplaceTypeManufacturer(String.class, |
|||
(dataProviderStrategy, attributeMetadata, map) -> randomString()); |
|||
// Integer
|
|||
PODAM_FACTORY.getStrategy().addOrReplaceTypeManufacturer(Integer.class, (dataProviderStrategy, attributeMetadata, map) -> { |
|||
// 如果是 status 的字段,返回 0 或 1
|
|||
if ("status".equals(attributeMetadata.getAttributeName())) { |
|||
return RandomUtil.randomEle(CommonStatusEnum.values()).getStatus(); |
|||
} |
|||
// 如果是 type、status 结尾的字段,返回 tinyint 范围
|
|||
if (StrUtil.endWithAnyIgnoreCase(attributeMetadata.getAttributeName(), |
|||
"type", "status", "category", "scope", "result")) { |
|||
return RandomUtil.randomInt(0, TINYINT_MAX + 1); |
|||
} |
|||
return RandomUtil.randomInt(); |
|||
}); |
|||
// LocalDateTime
|
|||
PODAM_FACTORY.getStrategy().addOrReplaceTypeManufacturer(LocalDateTime.class, |
|||
(dataProviderStrategy, attributeMetadata, map) -> randomLocalDateTime()); |
|||
// Boolean
|
|||
PODAM_FACTORY.getStrategy().addOrReplaceTypeManufacturer(Boolean.class, (dataProviderStrategy, attributeMetadata, map) -> { |
|||
// 如果是 deleted 的字段,返回非删除
|
|||
if ("deleted".equals(attributeMetadata.getAttributeName())) { |
|||
return false; |
|||
} |
|||
return RandomUtil.randomBoolean(); |
|||
}); |
|||
} |
|||
|
|||
public static String randomString() { |
|||
return RandomUtil.randomString(RANDOM_STRING_LENGTH); |
|||
} |
|||
|
|||
public static Long randomLongId() { |
|||
return RandomUtil.randomLong(0, Long.MAX_VALUE); |
|||
} |
|||
|
|||
public static Integer randomInteger() { |
|||
return RandomUtil.randomInt(0, Integer.MAX_VALUE); |
|||
} |
|||
|
|||
public static Date randomDate() { |
|||
return RandomUtil.randomDay(0, RANDOM_DATE_MAX); |
|||
} |
|||
|
|||
public static LocalDateTime randomLocalDateTime() { |
|||
// 设置 Nano 为零的原因,避免 MySQL、H2 存储不到时间戳
|
|||
return LocalDateTimeUtil.of(randomDate()).withNano(0); |
|||
} |
|||
|
|||
public static Short randomShort() { |
|||
return (short) RandomUtil.randomInt(0, Short.MAX_VALUE); |
|||
} |
|||
|
|||
public static <T> Set<T> randomSet(Class<T> clazz) { |
|||
return Stream.iterate(0, i -> i).limit(RandomUtil.randomInt(1, RANDOM_COLLECTION_LENGTH)) |
|||
.map(i -> randomPojo(clazz)).collect(Collectors.toSet()); |
|||
} |
|||
|
|||
public static Integer randomCommonStatus() { |
|||
return RandomUtil.randomEle(CommonStatusEnum.values()).getStatus(); |
|||
} |
|||
|
|||
public static String randomEmail() { |
|||
return randomString() + "@qq.com"; |
|||
} |
|||
|
|||
public static String randomMobile() { |
|||
return "13800138" + RandomUtil.randomNumbers(3); |
|||
} |
|||
|
|||
public static String randomURL() { |
|||
return "https://www.iocoder.cn/" + randomString(); |
|||
} |
|||
|
|||
@SafeVarargs |
|||
public static <T> T randomPojo(Class<T> clazz, Consumer<T>... consumers) { |
|||
T pojo = PODAM_FACTORY.manufacturePojo(clazz); |
|||
// 非空时,回调逻辑。通过它,可以实现 Pojo 的进一步处理
|
|||
if (ArrayUtil.isNotEmpty(consumers)) { |
|||
Arrays.stream(consumers).forEach(consumer -> consumer.accept(pojo)); |
|||
} |
|||
return pojo; |
|||
} |
|||
|
|||
@SafeVarargs |
|||
public static <T> T randomPojo(Class<T> clazz, Type type, Consumer<T>... consumers) { |
|||
T pojo = PODAM_FACTORY.manufacturePojo(clazz, type); |
|||
// 非空时,回调逻辑。通过它,可以实现 Pojo 的进一步处理
|
|||
if (ArrayUtil.isNotEmpty(consumers)) { |
|||
Arrays.stream(consumers).forEach(consumer -> consumer.accept(pojo)); |
|||
} |
|||
return pojo; |
|||
} |
|||
|
|||
@SafeVarargs |
|||
public static <T> List<T> randomPojoList(Class<T> clazz, Consumer<T>... consumers) { |
|||
int size = RandomUtil.randomInt(1, RANDOM_COLLECTION_LENGTH); |
|||
return randomPojoList(clazz, size, consumers); |
|||
} |
|||
|
|||
@SafeVarargs |
|||
public static <T> List<T> randomPojoList(Class<T> clazz, int size, Consumer<T>... consumers) { |
|||
return Stream.iterate(0, i -> i).limit(size).map(o -> randomPojo(clazz, consumers)) |
|||
.collect(Collectors.toList()); |
|||
} |
|||
|
|||
} |
@ -1,4 +0,0 @@ |
|||
/** |
|||
* 测试组件,用于单元测试、集成测试等等 |
|||
*/ |
|||
package cn.iocoder.yudao.framework.test; |
@ -1 +0,0 @@ |
|||
<https://www.iocoder.cn/Spring-Boot/Unit-Test/?yudao> |
@ -1,56 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.framework.file.core.ftp; |
|||
|
|||
import cn.hutool.core.io.resource.ResourceUtil; |
|||
import cn.hutool.core.util.IdUtil; |
|||
import cn.hutool.extra.ftp.FtpMode; |
|||
import cn.iocoder.yudao.module.infra.framework.file.core.client.ftp.FtpFileClient; |
|||
import cn.iocoder.yudao.module.infra.framework.file.core.client.ftp.FtpFileClientConfig; |
|||
import org.junit.jupiter.api.Disabled; |
|||
import org.junit.jupiter.api.Test; |
|||
|
|||
/** |
|||
* {@link FtpFileClient} 集成测试 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class FtpFileClientTest { |
|||
|
|||
// docker run -d \
|
|||
// -p 2121:21 -p 30000-30009:30000-30009 \
|
|||
// -e FTP_USER=foo \
|
|||
// -e FTP_PASS=pass \
|
|||
// -e PASV_ADDRESS=127.0.0.1 \
|
|||
// -e PASV_MIN_PORT=30000 \
|
|||
// -e PASV_MAX_PORT=30009 \
|
|||
// -v $(pwd)/ftp-data:/home/vsftpd \
|
|||
// fauria/vsftpd
|
|||
|
|||
@Test |
|||
@Disabled |
|||
public void test() { |
|||
// 创建客户端
|
|||
FtpFileClientConfig config = new FtpFileClientConfig(); |
|||
config.setDomain("http://127.0.0.1:48080"); |
|||
config.setBasePath("/home/ftp"); |
|||
config.setHost("127.0.0.1"); |
|||
config.setPort(2121); |
|||
config.setUsername("foo"); |
|||
config.setPassword("pass"); |
|||
config.setMode(FtpMode.Passive.name()); |
|||
FtpFileClient client = new FtpFileClient(0L, config); |
|||
client.init(); |
|||
// 上传文件
|
|||
String path = IdUtil.fastSimpleUUID() + ".jpg"; |
|||
byte[] content = ResourceUtil.readBytes("file/erweima.jpg"); |
|||
String fullPath = client.upload(content, path, "image/jpeg"); |
|||
System.out.println("访问地址:" + fullPath); |
|||
if (false) { |
|||
byte[] bytes = client.getContent(path); |
|||
System.out.println("文件内容:" + bytes); |
|||
} |
|||
if (false) { |
|||
client.delete(path); |
|||
} |
|||
} |
|||
|
|||
} |
@ -1,29 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.framework.file.core.local; |
|||
|
|||
import cn.hutool.core.io.resource.ResourceUtil; |
|||
import cn.hutool.core.util.IdUtil; |
|||
import cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClient; |
|||
import cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClientConfig; |
|||
import org.junit.jupiter.api.Disabled; |
|||
import org.junit.jupiter.api.Test; |
|||
|
|||
public class LocalFileClientTest { |
|||
|
|||
@Test |
|||
@Disabled |
|||
public void test() { |
|||
// 创建客户端
|
|||
LocalFileClientConfig config = new LocalFileClientConfig(); |
|||
config.setDomain("http://127.0.0.1:48080"); |
|||
config.setBasePath("/Users/yunai/file_test"); |
|||
LocalFileClient client = new LocalFileClient(0L, config); |
|||
client.init(); |
|||
// 上传文件
|
|||
String path = IdUtil.fastSimpleUUID() + ".jpg"; |
|||
byte[] content = ResourceUtil.readBytes("file/erweima.jpg"); |
|||
String fullPath = client.upload(content, path, "image/jpeg"); |
|||
System.out.println("访问地址:" + fullPath); |
|||
client.delete(path); |
|||
} |
|||
|
|||
} |
@ -1,118 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.framework.file.core.s3; |
|||
|
|||
import cn.hutool.core.io.resource.ResourceUtil; |
|||
import cn.hutool.core.util.IdUtil; |
|||
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; |
|||
import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClient; |
|||
import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig; |
|||
import jakarta.validation.Validation; |
|||
import org.junit.jupiter.api.Disabled; |
|||
import org.junit.jupiter.api.Test; |
|||
|
|||
public class S3FileClientTest { |
|||
|
|||
@Test |
|||
@Disabled // MinIO,如果要集成测试,可以注释本行
|
|||
public void testMinIO() throws Exception { |
|||
S3FileClientConfig config = new S3FileClientConfig(); |
|||
// 配置成你自己的
|
|||
config.setAccessKey("admin"); |
|||
config.setAccessSecret("password"); |
|||
config.setBucket("yudaoyuanma"); |
|||
config.setDomain(null); |
|||
// 默认 9000 endpoint
|
|||
config.setEndpoint("http://127.0.0.1:9000"); |
|||
|
|||
// 执行上传
|
|||
testExecuteUpload(config); |
|||
} |
|||
|
|||
@Test |
|||
@Disabled // 阿里云 OSS,如果要集成测试,可以注释本行
|
|||
public void testAliyun() throws Exception { |
|||
S3FileClientConfig config = new S3FileClientConfig(); |
|||
// 配置成你自己的
|
|||
config.setAccessKey(System.getenv("ALIYUN_ACCESS_KEY")); |
|||
config.setAccessSecret(System.getenv("ALIYUN_SECRET_KEY")); |
|||
config.setBucket("yunai-aoteman"); |
|||
config.setDomain(null); // 如果有自定义域名,则可以设置。http://ali-oss.iocoder.cn
|
|||
// 默认北京的 endpoint
|
|||
config.setEndpoint("oss-cn-beijing.aliyuncs.com"); |
|||
|
|||
// 执行上传
|
|||
testExecuteUpload(config); |
|||
} |
|||
|
|||
@Test |
|||
@Disabled // 腾讯云 COS,如果要集成测试,可以注释本行
|
|||
public void testQCloud() throws Exception { |
|||
S3FileClientConfig config = new S3FileClientConfig(); |
|||
// 配置成你自己的
|
|||
config.setAccessKey(System.getenv("QCLOUD_ACCESS_KEY")); |
|||
config.setAccessSecret(System.getenv("QCLOUD_SECRET_KEY")); |
|||
config.setBucket("aoteman-1255880240"); |
|||
config.setDomain(null); // 如果有自定义域名,则可以设置。http://tengxun-oss.iocoder.cn
|
|||
// 默认上海的 endpoint
|
|||
config.setEndpoint("cos.ap-shanghai.myqcloud.com"); |
|||
|
|||
// 执行上传
|
|||
testExecuteUpload(config); |
|||
} |
|||
|
|||
@Test |
|||
@Disabled // 七牛云存储,如果要集成测试,可以注释本行
|
|||
public void testQiniu() throws Exception { |
|||
S3FileClientConfig config = new S3FileClientConfig(); |
|||
// 配置成你自己的
|
|||
// config.setAccessKey(System.getenv("QINIU_ACCESS_KEY"));
|
|||
// config.setAccessSecret(System.getenv("QINIU_SECRET_KEY"));
|
|||
config.setAccessKey("b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8"); |
|||
config.setAccessSecret("kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP"); |
|||
config.setBucket("ruoyi-vue-pro"); |
|||
config.setDomain("http://test.yudao.iocoder.cn"); // 如果有自定义域名,则可以设置。http://static.yudao.iocoder.cn
|
|||
// 默认上海的 endpoint
|
|||
config.setEndpoint("s3-cn-south-1.qiniucs.com"); |
|||
|
|||
// 执行上传
|
|||
testExecuteUpload(config); |
|||
} |
|||
|
|||
@Test |
|||
@Disabled // 华为云存储,如果要集成测试,可以注释本行
|
|||
public void testHuaweiCloud() throws Exception { |
|||
S3FileClientConfig config = new S3FileClientConfig(); |
|||
// 配置成你自己的
|
|||
// config.setAccessKey(System.getenv("HUAWEI_CLOUD_ACCESS_KEY"));
|
|||
// config.setAccessSecret(System.getenv("HUAWEI_CLOUD_SECRET_KEY"));
|
|||
config.setBucket("yudao"); |
|||
config.setDomain(null); // 如果有自定义域名,则可以设置。
|
|||
// 默认上海的 endpoint
|
|||
config.setEndpoint("obs.cn-east-3.myhuaweicloud.com"); |
|||
|
|||
// 执行上传
|
|||
testExecuteUpload(config); |
|||
} |
|||
|
|||
private void testExecuteUpload(S3FileClientConfig config) throws Exception { |
|||
// 校验配置
|
|||
ValidationUtils.validate(Validation.buildDefaultValidatorFactory().getValidator(), config); |
|||
// 创建 Client
|
|||
S3FileClient client = new S3FileClient(0L, config); |
|||
client.init(); |
|||
// 上传文件
|
|||
String path = IdUtil.fastSimpleUUID() + ".jpg"; |
|||
byte[] content = ResourceUtil.readBytes("file/erweima.jpg"); |
|||
String fullPath = client.upload(content, path, "image/jpeg"); |
|||
System.out.println("访问地址:" + fullPath); |
|||
// 读取文件
|
|||
if (true) { |
|||
byte[] bytes = client.getContent(path); |
|||
System.out.println("文件内容:" + bytes.length); |
|||
} |
|||
// 删除文件
|
|||
if (false) { |
|||
client.delete(path); |
|||
} |
|||
} |
|||
|
|||
} |
@ -1,49 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.framework.file.core.sftp; |
|||
|
|||
import cn.hutool.core.io.resource.ResourceUtil; |
|||
import cn.hutool.core.util.IdUtil; |
|||
import cn.iocoder.yudao.module.infra.framework.file.core.client.sftp.SftpFileClient; |
|||
import cn.iocoder.yudao.module.infra.framework.file.core.client.sftp.SftpFileClientConfig; |
|||
import org.junit.jupiter.api.Disabled; |
|||
import org.junit.jupiter.api.Test; |
|||
|
|||
/** |
|||
* {@link SftpFileClient} 集成测试 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public class SftpFileClientTest { |
|||
|
|||
// docker run -p 2222:22 -d \
|
|||
// -v $(pwd)/sftp-data:/home/foo/upload \
|
|||
// atmoz/sftp \
|
|||
// foo:pass:1001
|
|||
|
|||
@Test |
|||
@Disabled |
|||
public void test() { |
|||
// 创建客户端
|
|||
SftpFileClientConfig config = new SftpFileClientConfig(); |
|||
config.setDomain("http://127.0.0.1:48080"); |
|||
config.setBasePath("/upload"); // 注意,这个是相对路径,不是实际 linux 上的路径!!!
|
|||
config.setHost("127.0.0.1"); |
|||
config.setPort(2222); |
|||
config.setUsername("foo"); |
|||
config.setPassword("pass"); |
|||
SftpFileClient client = new SftpFileClient(0L, config); |
|||
client.init(); |
|||
// 上传文件
|
|||
String path = IdUtil.fastSimpleUUID() + ".jpg"; |
|||
byte[] content = ResourceUtil.readBytes("file/erweima.jpg"); |
|||
String fullPath = client.upload(content, path, "image/jpeg"); |
|||
System.out.println("访问地址:" + fullPath); |
|||
if (false) { |
|||
byte[] bytes = client.getContent(path); |
|||
System.out.println("文件内容:" + bytes); |
|||
} |
|||
if (false) { |
|||
client.delete(path); |
|||
} |
|||
} |
|||
|
|||
} |
@ -1,37 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.service; |
|||
|
|||
import cn.hutool.core.util.StrUtil; |
|||
import com.baomidou.mybatisplus.generator.query.DefaultQuery; |
|||
import com.baomidou.mybatisplus.generator.config.DataSourceConfig; |
|||
import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder; |
|||
import com.baomidou.mybatisplus.generator.config.po.TableInfo; |
|||
|
|||
import java.util.List; |
|||
|
|||
public class DefaultDatabaseQueryTest { |
|||
|
|||
public static void main(String[] args) { |
|||
// DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder("jdbc:oracle:thin:@127.0.0.1:1521:xe",
|
|||
// "root", "123456").build();
|
|||
DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder("jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro", |
|||
"root", "123456").build(); |
|||
// StrategyConfig strategyConfig = new StrategyConfig.Builder().build();
|
|||
|
|||
ConfigBuilder builder = new ConfigBuilder(null, dataSourceConfig, null, null, null, null); |
|||
|
|||
DefaultQuery query = new DefaultQuery(builder); |
|||
|
|||
long time = System.currentTimeMillis(); |
|||
List<TableInfo> tableInfos = query.queryTables(); |
|||
for (TableInfo tableInfo : tableInfos) { |
|||
if (StrUtil.startWithAny(tableInfo.getName().toLowerCase(), "act_", "flw_", "qrtz_")) { |
|||
continue; |
|||
} |
|||
System.out.println(String.format("CREATE SEQUENCE %s_seq MINVALUE 1;", tableInfo.getName())); |
|||
// System.out.println(String.format("DELETE FROM %s WHERE deleted = '1';", tableInfo.getName()));
|
|||
} |
|||
System.out.println(tableInfos.size()); |
|||
System.out.println(System.currentTimeMillis() - time); |
|||
} |
|||
|
|||
} |
@ -1,552 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.service.codegen; |
|||
|
|||
import cn.hutool.core.collection.ListUtil; |
|||
import cn.hutool.core.map.MapUtil; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
|||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
|||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenCreateListReqVO; |
|||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenUpdateReqVO; |
|||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.column.CodegenColumnSaveReqVO; |
|||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO; |
|||
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.DatabaseTableRespVO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO; |
|||
import cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenColumnMapper; |
|||
import cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenTableMapper; |
|||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum; |
|||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum; |
|||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum; |
|||
import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties; |
|||
import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenBuilder; |
|||
import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenEngine; |
|||
import cn.iocoder.yudao.module.infra.service.db.DatabaseTableService; |
|||
import com.baomidou.mybatisplus.generator.config.po.TableField; |
|||
import com.baomidou.mybatisplus.generator.config.po.TableInfo; |
|||
import jakarta.annotation.Resource; |
|||
import org.junit.jupiter.api.Test; |
|||
import org.springframework.boot.test.mock.mockito.MockBean; |
|||
import org.springframework.context.annotation.Import; |
|||
|
|||
import java.util.Arrays; |
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; |
|||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; |
|||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; |
|||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; |
|||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; |
|||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; |
|||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; |
|||
import static org.junit.jupiter.api.Assertions.*; |
|||
import static org.mockito.ArgumentMatchers.*; |
|||
import static org.mockito.Mockito.mock; |
|||
import static org.mockito.Mockito.when; |
|||
|
|||
/** |
|||
* {@link CodegenServiceImpl} 的单元测试类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@Import(CodegenServiceImpl.class) |
|||
public class CodegenServiceImplTest extends BaseDbUnitTest { |
|||
|
|||
@Resource |
|||
private CodegenServiceImpl codegenService; |
|||
|
|||
@Resource |
|||
private CodegenTableMapper codegenTableMapper; |
|||
@Resource |
|||
private CodegenColumnMapper codegenColumnMapper; |
|||
|
|||
@MockBean |
|||
private DatabaseTableService databaseTableService; |
|||
|
|||
@MockBean |
|||
private CodegenBuilder codegenBuilder; |
|||
@MockBean |
|||
private CodegenEngine codegenEngine; |
|||
|
|||
@MockBean |
|||
private CodegenProperties codegenProperties; |
|||
|
|||
@Test |
|||
public void testCreateCodegenList() { |
|||
// 准备参数
|
|||
String author = randomString(); |
|||
CodegenCreateListReqVO reqVO = randomPojo(CodegenCreateListReqVO.class, |
|||
o -> o.setDataSourceConfigId(1L).setTableNames(Collections.singletonList("t_yunai"))); |
|||
// mock 方法(TableInfo)
|
|||
TableInfo tableInfo = mock(TableInfo.class); |
|||
when(databaseTableService.getTable(eq(1L), eq("t_yunai"))) |
|||
.thenReturn(tableInfo); |
|||
when(tableInfo.getComment()).thenReturn("芋艿"); |
|||
// mock 方法(TableInfo fields)
|
|||
TableField field01 = mock(TableField.class); |
|||
when(field01.getComment()).thenReturn("主键"); |
|||
TableField field02 = mock(TableField.class); |
|||
when(field02.getComment()).thenReturn("名字"); |
|||
List<TableField> fields = Arrays.asList(field01, field02); |
|||
when(tableInfo.getFields()).thenReturn(fields); |
|||
// mock 方法(CodegenTableDO)
|
|||
CodegenTableDO table = randomPojo(CodegenTableDO.class); |
|||
when(codegenBuilder.buildTable(same(tableInfo))).thenReturn(table); |
|||
// mock 方法(CodegenColumnDO)
|
|||
List<CodegenColumnDO> columns = randomPojoList(CodegenColumnDO.class); |
|||
when(codegenBuilder.buildColumns(eq(table.getId()), same(fields))) |
|||
.thenReturn(columns); |
|||
// mock 方法(CodegenProperties)
|
|||
when(codegenProperties.getFrontType()).thenReturn(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType()); |
|||
|
|||
// 调用
|
|||
List<Long> result = codegenService.createCodegenList(author, reqVO); |
|||
// 断言
|
|||
assertEquals(1, result.size()); |
|||
// 断言(CodegenTableDO)
|
|||
CodegenTableDO dbTable = codegenTableMapper.selectList().get(0); |
|||
assertPojoEquals(table, dbTable); |
|||
assertEquals(1L, dbTable.getDataSourceConfigId()); |
|||
assertEquals(CodegenSceneEnum.ADMIN.getScene(), dbTable.getScene()); |
|||
assertEquals(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), dbTable.getFrontType()); |
|||
assertEquals(author, dbTable.getAuthor()); |
|||
// 断言(CodegenColumnDO)
|
|||
List<CodegenColumnDO> dbColumns = codegenColumnMapper.selectList(); |
|||
assertEquals(columns.size(), dbColumns.size()); |
|||
assertTrue(dbColumns.get(0).getPrimaryKey()); |
|||
for (int i = 0; i < dbColumns.size(); i++) { |
|||
assertPojoEquals(columns.get(i), dbColumns.get(i)); |
|||
} |
|||
} |
|||
|
|||
@Test |
|||
public void testValidateTableInfo() { |
|||
// 情况一
|
|||
assertServiceException(() -> codegenService.validateTableInfo(null), |
|||
CODEGEN_IMPORT_TABLE_NULL); |
|||
// 情况二
|
|||
TableInfo tableInfo = mock(TableInfo.class); |
|||
assertServiceException(() -> codegenService.validateTableInfo(tableInfo), |
|||
CODEGEN_TABLE_INFO_TABLE_COMMENT_IS_NULL); |
|||
// 情况三
|
|||
when(tableInfo.getComment()).thenReturn("芋艿"); |
|||
assertServiceException(() -> codegenService.validateTableInfo(tableInfo), |
|||
CODEGEN_IMPORT_COLUMNS_NULL); |
|||
// 情况四
|
|||
TableField field = mock(TableField.class); |
|||
when(field.getName()).thenReturn("name"); |
|||
when(tableInfo.getFields()).thenReturn(Collections.singletonList(field)); |
|||
assertServiceException(() -> codegenService.validateTableInfo(tableInfo), |
|||
CODEGEN_TABLE_INFO_COLUMN_COMMENT_IS_NULL, field.getName()); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateCodegen_notExists() { |
|||
// 准备参数
|
|||
CodegenUpdateReqVO updateReqVO = randomPojo(CodegenUpdateReqVO.class); |
|||
// mock 方法
|
|||
|
|||
// 调用,并断言
|
|||
assertServiceException(() -> codegenService.updateCodegen(updateReqVO), |
|||
CODEGEN_TABLE_NOT_EXISTS); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateCodegen_sub_masterNotExists() { |
|||
// mock 数据
|
|||
CodegenTableDO table = randomPojo(CodegenTableDO.class, |
|||
o -> o.setTemplateType(CodegenTemplateTypeEnum.SUB.getType()) |
|||
.setScene(CodegenSceneEnum.ADMIN.getScene())); |
|||
codegenTableMapper.insert(table); |
|||
// 准备参数
|
|||
CodegenUpdateReqVO updateReqVO = randomPojo(CodegenUpdateReqVO.class, |
|||
o -> o.getTable().setId(table.getId()) |
|||
.setTemplateType(CodegenTemplateTypeEnum.SUB.getType())); |
|||
|
|||
// 调用,并断言
|
|||
assertServiceException(() -> codegenService.updateCodegen(updateReqVO), |
|||
CODEGEN_MASTER_TABLE_NOT_EXISTS, updateReqVO.getTable().getMasterTableId()); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateCodegen_sub_columnNotExists() { |
|||
// mock 数据
|
|||
CodegenTableDO subTable = randomPojo(CodegenTableDO.class, |
|||
o -> o.setTemplateType(CodegenTemplateTypeEnum.SUB.getType()) |
|||
.setScene(CodegenSceneEnum.ADMIN.getScene())); |
|||
codegenTableMapper.insert(subTable); |
|||
// mock 数据(master)
|
|||
CodegenTableDO masterTable = randomPojo(CodegenTableDO.class, |
|||
o -> o.setTemplateType(CodegenTemplateTypeEnum.MASTER_ERP.getType()) |
|||
.setScene(CodegenSceneEnum.ADMIN.getScene())); |
|||
codegenTableMapper.insert(masterTable); |
|||
// 准备参数
|
|||
CodegenUpdateReqVO updateReqVO = randomPojo(CodegenUpdateReqVO.class, |
|||
o -> o.getTable().setId(subTable.getId()) |
|||
.setTemplateType(CodegenTemplateTypeEnum.SUB.getType()) |
|||
.setMasterTableId(masterTable.getId())); |
|||
|
|||
// 调用,并断言
|
|||
assertServiceException(() -> codegenService.updateCodegen(updateReqVO), |
|||
CODEGEN_SUB_COLUMN_NOT_EXISTS, updateReqVO.getTable().getSubJoinColumnId()); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateCodegen_success() { |
|||
// mock 数据
|
|||
CodegenTableDO table = randomPojo(CodegenTableDO.class, |
|||
o -> o.setTemplateType(CodegenTemplateTypeEnum.ONE.getType()) |
|||
.setScene(CodegenSceneEnum.ADMIN.getScene())); |
|||
codegenTableMapper.insert(table); |
|||
CodegenColumnDO column01 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId())); |
|||
codegenColumnMapper.insert(column01); |
|||
CodegenColumnDO column02 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId())); |
|||
codegenColumnMapper.insert(column02); |
|||
// 准备参数
|
|||
CodegenUpdateReqVO updateReqVO = randomPojo(CodegenUpdateReqVO.class, |
|||
o -> o.getTable().setId(table.getId()) |
|||
.setTemplateType(CodegenTemplateTypeEnum.ONE.getType()) |
|||
.setScene(CodegenSceneEnum.ADMIN.getScene())); |
|||
CodegenColumnSaveReqVO columnVO01 = randomPojo(CodegenColumnSaveReqVO.class, |
|||
o -> o.setId(column01.getId()).setTableId(table.getId())); |
|||
CodegenColumnSaveReqVO columnVO02 = randomPojo(CodegenColumnSaveReqVO.class, |
|||
o -> o.setId(column02.getId()).setTableId(table.getId())); |
|||
updateReqVO.setColumns(Arrays.asList(columnVO01, columnVO02)); |
|||
|
|||
// 调用
|
|||
codegenService.updateCodegen(updateReqVO); |
|||
// 断言
|
|||
CodegenTableDO dbTable = codegenTableMapper.selectById(table.getId()); |
|||
assertPojoEquals(updateReqVO.getTable(), dbTable); |
|||
List<CodegenColumnDO> dbColumns = codegenColumnMapper.selectList(); |
|||
assertEquals(2, dbColumns.size()); |
|||
assertPojoEquals(columnVO01, dbColumns.get(0)); |
|||
assertPojoEquals(columnVO02, dbColumns.get(1)); |
|||
} |
|||
|
|||
@Test |
|||
public void testSyncCodegenFromDB() { |
|||
// mock 数据(CodegenTableDO)
|
|||
CodegenTableDO table = randomPojo(CodegenTableDO.class, o -> o.setTableName("t_yunai") |
|||
.setDataSourceConfigId(1L).setScene(CodegenSceneEnum.ADMIN.getScene())); |
|||
codegenTableMapper.insert(table); |
|||
CodegenColumnDO column01 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId()) |
|||
.setColumnName("id").setPrimaryKey(true).setOrdinalPosition(0)); |
|||
codegenColumnMapper.insert(column01); |
|||
CodegenColumnDO column02 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId()) |
|||
.setColumnName("name").setOrdinalPosition(1)); |
|||
codegenColumnMapper.insert(column02); |
|||
// 准备参数
|
|||
Long tableId = table.getId(); |
|||
// mock 方法(TableInfo)
|
|||
TableInfo tableInfo = mock(TableInfo.class); |
|||
when(databaseTableService.getTable(eq(1L), eq("t_yunai"))) |
|||
.thenReturn(tableInfo); |
|||
when(tableInfo.getComment()).thenReturn("芋艿"); |
|||
// mock 方法(TableInfo fields)
|
|||
TableField field01 = mock(TableField.class); |
|||
when(field01.getComment()).thenReturn("主键"); |
|||
TableField field03 = mock(TableField.class); |
|||
when(field03.getComment()).thenReturn("分类"); |
|||
List<TableField> fields = Arrays.asList(field01, field03); |
|||
when(tableInfo.getFields()).thenReturn(fields); |
|||
when(databaseTableService.getTable(eq(1L), eq("t_yunai"))) |
|||
.thenReturn(tableInfo); |
|||
// mock 方法(CodegenTableDO)
|
|||
List<CodegenColumnDO> newColumns = randomPojoList(CodegenColumnDO.class, 2); |
|||
when(codegenBuilder.buildColumns(eq(table.getId()), argThat(tableFields -> { |
|||
assertEquals(2, tableFields.size()); |
|||
assertSame(tableInfo.getFields(), tableFields); |
|||
return true; |
|||
}))).thenReturn(newColumns); |
|||
|
|||
// 调用
|
|||
codegenService.syncCodegenFromDB(tableId); |
|||
// 断言
|
|||
List<CodegenColumnDO> dbColumns = codegenColumnMapper.selectList(); |
|||
assertEquals(newColumns.size(), dbColumns.size()); |
|||
assertPojoEquals(newColumns.get(0), dbColumns.get(0)); |
|||
assertPojoEquals(newColumns.get(1), dbColumns.get(1)); |
|||
} |
|||
|
|||
@Test |
|||
public void testDeleteCodegen_notExists() { |
|||
assertServiceException(() -> codegenService.deleteCodegen(randomLongId()), |
|||
CODEGEN_TABLE_NOT_EXISTS); |
|||
} |
|||
|
|||
@Test |
|||
public void testDeleteCodegen_success() { |
|||
// mock 数据
|
|||
CodegenTableDO table = randomPojo(CodegenTableDO.class, |
|||
o -> o.setScene(CodegenSceneEnum.ADMIN.getScene())); |
|||
codegenTableMapper.insert(table); |
|||
CodegenColumnDO column = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId())); |
|||
codegenColumnMapper.insert(column); |
|||
// 准备参数
|
|||
Long tableId = table.getId(); |
|||
|
|||
// 调用
|
|||
codegenService.deleteCodegen(tableId); |
|||
// 断言
|
|||
assertNull(codegenTableMapper.selectById(tableId)); |
|||
assertEquals(0, codegenColumnMapper.selectList().size()); |
|||
} |
|||
|
|||
@Test |
|||
public void testGetCodegenTableList() { |
|||
// mock 数据
|
|||
CodegenTableDO table01 = randomPojo(CodegenTableDO.class, |
|||
o -> o.setScene(CodegenSceneEnum.ADMIN.getScene())); |
|||
codegenTableMapper.insert(table01); |
|||
CodegenTableDO table02 = randomPojo(CodegenTableDO.class, |
|||
o -> o.setScene(CodegenSceneEnum.ADMIN.getScene())); |
|||
codegenTableMapper.insert(table02); |
|||
// 准备参数
|
|||
Long dataSourceConfigId = table01.getDataSourceConfigId(); |
|||
|
|||
// 调用
|
|||
List<CodegenTableDO> result = codegenService.getCodegenTableList(dataSourceConfigId); |
|||
// 断言
|
|||
assertEquals(1, result.size()); |
|||
assertPojoEquals(table01, result.get(0)); |
|||
} |
|||
|
|||
@Test |
|||
public void testGetCodegenTablePage() { |
|||
// mock 数据
|
|||
CodegenTableDO tableDO = randomPojo(CodegenTableDO.class, o -> { |
|||
o.setTableName("t_yunai"); |
|||
o.setTableComment("芋艿"); |
|||
o.setClassName("SystemYunai"); |
|||
o.setCreateTime(buildTime(2021, 3, 10)); |
|||
}).setScene(CodegenSceneEnum.ADMIN.getScene()); |
|||
codegenTableMapper.insert(tableDO); |
|||
// 测试 tableName 不匹配
|
|||
codegenTableMapper.insert(cloneIgnoreId(tableDO, o -> o.setTableName(randomString()))); |
|||
// 测试 tableComment 不匹配
|
|||
codegenTableMapper.insert(cloneIgnoreId(tableDO, o -> o.setTableComment(randomString()))); |
|||
// 测试 className 不匹配
|
|||
codegenTableMapper.insert(cloneIgnoreId(tableDO, o -> o.setClassName(randomString()))); |
|||
// 测试 createTime 不匹配
|
|||
codegenTableMapper.insert(cloneIgnoreId(tableDO, logDO -> logDO.setCreateTime(buildTime(2021, 4, 10)))); |
|||
// 准备参数
|
|||
CodegenTablePageReqVO reqVO = new CodegenTablePageReqVO(); |
|||
reqVO.setTableName("yunai"); |
|||
reqVO.setTableComment("芋"); |
|||
reqVO.setClassName("Yunai"); |
|||
reqVO.setCreateTime(buildBetweenTime(2021, 3, 1, 2021, 3, 31)); |
|||
|
|||
// 调用
|
|||
PageResult<CodegenTableDO> pageResult = codegenService.getCodegenTablePage(reqVO); |
|||
// 断言,只查到了一条符合条件的
|
|||
assertEquals(1, pageResult.getTotal()); |
|||
assertEquals(1, pageResult.getList().size()); |
|||
assertPojoEquals(tableDO, pageResult.getList().get(0)); |
|||
} |
|||
|
|||
@Test |
|||
public void testGetCodegenTable() { |
|||
// mock 数据
|
|||
CodegenTableDO tableDO = randomPojo(CodegenTableDO.class, o -> o.setScene(CodegenSceneEnum.ADMIN.getScene())); |
|||
codegenTableMapper.insert(tableDO); |
|||
// 准备参数
|
|||
Long id = tableDO.getId(); |
|||
|
|||
// 调用
|
|||
CodegenTableDO result = codegenService.getCodegenTable(id); |
|||
// 断言
|
|||
assertPojoEquals(tableDO, result); |
|||
} |
|||
|
|||
@Test |
|||
public void testGetCodegenColumnListByTableId() { |
|||
// mock 数据
|
|||
CodegenColumnDO column01 = randomPojo(CodegenColumnDO.class); |
|||
codegenColumnMapper.insert(column01); |
|||
CodegenColumnDO column02 = randomPojo(CodegenColumnDO.class); |
|||
codegenColumnMapper.insert(column02); |
|||
// 准备参数
|
|||
Long tableId = column01.getTableId(); |
|||
|
|||
// 调用
|
|||
List<CodegenColumnDO> result = codegenService.getCodegenColumnListByTableId(tableId); |
|||
// 断言
|
|||
assertEquals(1, result.size()); |
|||
assertPojoEquals(column01, result.get(0)); |
|||
} |
|||
|
|||
@Test |
|||
public void testGenerationCodes_tableNotExists() { |
|||
assertServiceException(() -> codegenService.generationCodes(randomLongId()), |
|||
CODEGEN_TABLE_NOT_EXISTS); |
|||
} |
|||
|
|||
@Test |
|||
public void testGenerationCodes_columnNotExists() { |
|||
// mock 数据(CodegenTableDO)
|
|||
CodegenTableDO table = randomPojo(CodegenTableDO.class, |
|||
o -> o.setScene(CodegenSceneEnum.ADMIN.getScene()) |
|||
.setTemplateType(CodegenTemplateTypeEnum.MASTER_NORMAL.getType())); |
|||
codegenTableMapper.insert(table); |
|||
// 准备参数
|
|||
Long tableId = table.getId(); |
|||
|
|||
// 调用,并断言
|
|||
assertServiceException(() -> codegenService.generationCodes(tableId), |
|||
CODEGEN_COLUMN_NOT_EXISTS); |
|||
} |
|||
|
|||
@Test |
|||
public void testGenerationCodes_sub_tableNotExists() { |
|||
// mock 数据(CodegenTableDO)
|
|||
CodegenTableDO table = randomPojo(CodegenTableDO.class, |
|||
o -> o.setScene(CodegenSceneEnum.ADMIN.getScene()) |
|||
.setTemplateType(CodegenTemplateTypeEnum.MASTER_NORMAL.getType())); |
|||
codegenTableMapper.insert(table); |
|||
// mock 数据(CodegenColumnDO)
|
|||
CodegenColumnDO column01 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId())); |
|||
codegenColumnMapper.insert(column01); |
|||
// 准备参数
|
|||
Long tableId = table.getId(); |
|||
|
|||
// 调用,并断言
|
|||
assertServiceException(() -> codegenService.generationCodes(tableId), |
|||
CODEGEN_MASTER_GENERATION_FAIL_NO_SUB_TABLE); |
|||
} |
|||
|
|||
@Test |
|||
public void testGenerationCodes_sub_columnNotExists() { |
|||
// mock 数据(CodegenTableDO)
|
|||
CodegenTableDO table = randomPojo(CodegenTableDO.class, |
|||
o -> o.setScene(CodegenSceneEnum.ADMIN.getScene()) |
|||
.setTemplateType(CodegenTemplateTypeEnum.MASTER_NORMAL.getType())); |
|||
codegenTableMapper.insert(table); |
|||
// mock 数据(CodegenColumnDO)
|
|||
CodegenColumnDO column01 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId())); |
|||
codegenColumnMapper.insert(column01); |
|||
// mock 数据(sub CodegenTableDO)
|
|||
CodegenTableDO subTable = randomPojo(CodegenTableDO.class, |
|||
o -> o.setScene(CodegenSceneEnum.ADMIN.getScene()) |
|||
.setTemplateType(CodegenTemplateTypeEnum.SUB.getType()) |
|||
.setMasterTableId(table.getId())); |
|||
codegenTableMapper.insert(subTable); |
|||
// 准备参数
|
|||
Long tableId = table.getId(); |
|||
|
|||
// 调用,并断言
|
|||
assertServiceException(() -> codegenService.generationCodes(tableId), |
|||
CODEGEN_SUB_COLUMN_NOT_EXISTS, subTable.getId()); |
|||
} |
|||
|
|||
@Test |
|||
public void testGenerationCodes_one_success() { |
|||
// mock 数据(CodegenTableDO)
|
|||
CodegenTableDO table = randomPojo(CodegenTableDO.class, |
|||
o -> o.setScene(CodegenSceneEnum.ADMIN.getScene()) |
|||
.setTemplateType(CodegenTemplateTypeEnum.ONE.getType())); |
|||
codegenTableMapper.insert(table); |
|||
// mock 数据(CodegenColumnDO)
|
|||
CodegenColumnDO column01 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId()) |
|||
.setOrdinalPosition(1)); |
|||
codegenColumnMapper.insert(column01); |
|||
CodegenColumnDO column02 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId()) |
|||
.setOrdinalPosition(2)); |
|||
codegenColumnMapper.insert(column02); |
|||
// mock 执行生成
|
|||
Map<String, String> codes = MapUtil.of(randomString(), randomString()); |
|||
when(codegenEngine.execute(eq(table), argThat(columns -> { |
|||
assertEquals(2, columns.size()); |
|||
assertEquals(column01, columns.get(0)); |
|||
assertEquals(column02, columns.get(1)); |
|||
return true; |
|||
}), isNull(), isNull())).thenReturn(codes); |
|||
// 准备参数
|
|||
Long tableId = table.getId(); |
|||
|
|||
// 调用
|
|||
Map<String, String> result = codegenService.generationCodes(tableId); |
|||
// 断言
|
|||
assertSame(codes, result); |
|||
} |
|||
|
|||
@Test |
|||
public void testGenerationCodes_master_success() { |
|||
// mock 数据(CodegenTableDO)
|
|||
CodegenTableDO table = randomPojo(CodegenTableDO.class, |
|||
o -> o.setScene(CodegenSceneEnum.ADMIN.getScene()) |
|||
.setTemplateType(CodegenTemplateTypeEnum.MASTER_NORMAL.getType())); |
|||
codegenTableMapper.insert(table); |
|||
// mock 数据(CodegenColumnDO)
|
|||
CodegenColumnDO column01 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId()) |
|||
.setOrdinalPosition(1)); |
|||
codegenColumnMapper.insert(column01); |
|||
CodegenColumnDO column02 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId()) |
|||
.setOrdinalPosition(2)); |
|||
codegenColumnMapper.insert(column02); |
|||
// mock 数据(sub CodegenTableDO)
|
|||
CodegenTableDO subTable = randomPojo(CodegenTableDO.class, |
|||
o -> o.setScene(CodegenSceneEnum.ADMIN.getScene()) |
|||
.setTemplateType(CodegenTemplateTypeEnum.SUB.getType()) |
|||
.setMasterTableId(table.getId()) |
|||
.setSubJoinColumnId(1024L)); |
|||
codegenTableMapper.insert(subTable); |
|||
// mock 数据(sub CodegenColumnDO)
|
|||
CodegenColumnDO subColumn01 = randomPojo(CodegenColumnDO.class, o -> o.setId(1024L).setTableId(subTable.getId())); |
|||
codegenColumnMapper.insert(subColumn01); |
|||
// mock 执行生成
|
|||
Map<String, String> codes = MapUtil.of(randomString(), randomString()); |
|||
when(codegenEngine.execute(eq(table), argThat(columns -> { |
|||
assertEquals(2, columns.size()); |
|||
assertEquals(column01, columns.get(0)); |
|||
assertEquals(column02, columns.get(1)); |
|||
return true; |
|||
}), argThat(tables -> { |
|||
assertEquals(1, tables.size()); |
|||
assertPojoEquals(subTable, tables.get(0)); |
|||
return true; |
|||
}), argThat(columns -> { |
|||
assertEquals(1, columns.size()); |
|||
assertPojoEquals(subColumn01, columns.size()); |
|||
return true; |
|||
}))).thenReturn(codes); |
|||
// 准备参数
|
|||
Long tableId = table.getId(); |
|||
|
|||
// 调用
|
|||
Map<String, String> result = codegenService.generationCodes(tableId); |
|||
// 断言
|
|||
assertSame(codes, result); |
|||
} |
|||
|
|||
@Test |
|||
public void testGetDatabaseTableList() { |
|||
// 准备参数
|
|||
Long dataSourceConfigId = randomLongId(); |
|||
String name = randomString(); |
|||
String comment = randomString(); |
|||
// mock 方法
|
|||
TableInfo tableInfo01 = mock(TableInfo.class); |
|||
when(tableInfo01.getName()).thenReturn("t_yunai"); |
|||
when(tableInfo01.getComment()).thenReturn("芋艿"); |
|||
TableInfo tableInfo02 = mock(TableInfo.class); |
|||
when(tableInfo02.getName()).thenReturn("t_yunai_02"); |
|||
when(tableInfo02.getComment()).thenReturn("芋艿_02"); |
|||
when(databaseTableService.getTableList(eq(dataSourceConfigId), eq(name), eq(comment))) |
|||
.thenReturn(ListUtil.toList(tableInfo01, tableInfo02)); |
|||
// mock 数据
|
|||
CodegenTableDO tableDO = randomPojo(CodegenTableDO.class, |
|||
o -> o.setScene(CodegenSceneEnum.ADMIN.getScene()) |
|||
.setTableName("t_yunai_02") |
|||
.setDataSourceConfigId(dataSourceConfigId)); |
|||
codegenTableMapper.insert(tableDO); |
|||
|
|||
// 调用
|
|||
List<DatabaseTableRespVO> result = codegenService.getDatabaseTableList(dataSourceConfigId, name, comment); |
|||
// 断言
|
|||
assertEquals(1, result.size()); |
|||
assertEquals("t_yunai", result.get(0).getName()); |
|||
assertEquals("芋艿", result.get(0).getComment()); |
|||
} |
|||
|
|||
} |
@ -1,87 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.service.codegen.inner; |
|||
|
|||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO; |
|||
import com.baomidou.mybatisplus.generator.config.po.TableField; |
|||
import com.baomidou.mybatisplus.generator.config.po.TableInfo; |
|||
import com.baomidou.mybatisplus.generator.config.rules.IColumnType; |
|||
import org.apache.ibatis.type.JdbcType; |
|||
import org.junit.jupiter.api.Test; |
|||
import org.mockito.InjectMocks; |
|||
|
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
|
|||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; |
|||
import static org.junit.jupiter.api.Assertions.*; |
|||
import static org.mockito.Mockito.mock; |
|||
import static org.mockito.Mockito.when; |
|||
|
|||
public class CodegenBuilderTest extends BaseMockitoUnitTest { |
|||
|
|||
@InjectMocks |
|||
private CodegenBuilder codegenBuilder; |
|||
|
|||
@Test |
|||
public void testBuildTable() { |
|||
// 准备参数
|
|||
TableInfo tableInfo = mock(TableInfo.class); |
|||
// mock 方法
|
|||
when(tableInfo.getName()).thenReturn("system_user"); |
|||
when(tableInfo.getComment()).thenReturn("用户"); |
|||
|
|||
// 调用
|
|||
CodegenTableDO table = codegenBuilder.buildTable(tableInfo); |
|||
// 断言
|
|||
assertEquals("system_user", table.getTableName()); |
|||
assertEquals("用户", table.getTableComment()); |
|||
assertEquals("system", table.getModuleName()); |
|||
assertEquals("user", table.getBusinessName()); |
|||
assertEquals("User", table.getClassName()); |
|||
assertEquals("用户", table.getClassComment()); |
|||
} |
|||
|
|||
@Test |
|||
public void testBuildColumns() { |
|||
// 准备参数
|
|||
Long tableId = randomLongId(); |
|||
TableField tableField = mock(TableField.class); |
|||
List<TableField> tableFields = Collections.singletonList(tableField); |
|||
// mock 方法
|
|||
TableField.MetaInfo metaInfo = mock(TableField.MetaInfo.class); |
|||
when(tableField.getMetaInfo()).thenReturn(metaInfo); |
|||
when(metaInfo.getJdbcType()).thenReturn(JdbcType.BIGINT); |
|||
when(tableField.getComment()).thenReturn("编号"); |
|||
when(tableField.isKeyFlag()).thenReturn(true); |
|||
IColumnType columnType = mock(IColumnType.class); |
|||
when(tableField.getColumnType()).thenReturn(columnType); |
|||
when(columnType.getType()).thenReturn("Long"); |
|||
when(tableField.getName()).thenReturn("id2"); |
|||
when(tableField.getPropertyName()).thenReturn("id"); |
|||
|
|||
// 调用
|
|||
List<CodegenColumnDO> columns = codegenBuilder.buildColumns(tableId, tableFields); |
|||
// 断言
|
|||
assertEquals(1, columns.size()); |
|||
CodegenColumnDO column = columns.get(0); |
|||
assertEquals(tableId, column.getTableId()); |
|||
assertEquals("id2", column.getColumnName()); |
|||
assertEquals("BIGINT", column.getDataType()); |
|||
assertEquals("编号", column.getColumnComment()); |
|||
assertFalse(column.getNullable()); |
|||
assertTrue(column.getPrimaryKey()); |
|||
assertEquals(1, column.getOrdinalPosition()); |
|||
assertEquals("Long", column.getJavaType()); |
|||
assertEquals("id", column.getJavaField()); |
|||
assertNull(column.getDictType()); |
|||
assertNotNull(column.getExample()); |
|||
assertFalse(column.getCreateOperation()); |
|||
assertTrue(column.getUpdateOperation()); |
|||
assertFalse(column.getListOperation()); |
|||
assertEquals("=", column.getListOperationCondition()); |
|||
assertTrue(column.getListOperationResult()); |
|||
assertEquals("input", column.getHtmlType()); |
|||
} |
|||
|
|||
} |
@ -1,138 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.service.codegen.inner; |
|||
|
|||
import cn.hutool.core.io.FileUtil; |
|||
import cn.hutool.core.io.IoUtil; |
|||
import cn.hutool.core.io.resource.ResourceUtil; |
|||
import cn.hutool.core.map.MapUtil; |
|||
import cn.hutool.core.util.StrUtil; |
|||
import cn.hutool.core.util.ZipUtil; |
|||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
|||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO; |
|||
import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties; |
|||
import org.junit.jupiter.api.BeforeEach; |
|||
import org.mockito.InjectMocks; |
|||
import org.mockito.Spy; |
|||
|
|||
import java.io.ByteArrayInputStream; |
|||
import java.io.ByteArrayOutputStream; |
|||
import java.util.ArrayList; |
|||
import java.util.HashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
import static org.junit.jupiter.api.Assertions.assertEquals; |
|||
|
|||
/** |
|||
* {@link CodegenEngine} 的单元测试抽象基类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public abstract class CodegenEngineAbstractTest extends BaseMockitoUnitTest { |
|||
|
|||
/** |
|||
* 测试文件资源目录 |
|||
*/ |
|||
private String resourcesPath = ""; |
|||
|
|||
@InjectMocks |
|||
protected CodegenEngine codegenEngine; |
|||
|
|||
@Spy |
|||
protected CodegenProperties codegenProperties = new CodegenProperties() |
|||
.setBasePackage("cn.iocoder.yudao"); |
|||
|
|||
@BeforeEach |
|||
public void setUp() { |
|||
codegenEngine.setJakartaEnable(true); // 强制使用 jakarta,保证单测可以基于 jakarta 断言
|
|||
codegenEngine.initGlobalBindingMap(); |
|||
// 单测强制使用
|
|||
// 获取测试文件 resources 路径
|
|||
String absolutePath = FileUtil.getAbsolutePath("application-unit-test.yaml"); |
|||
// 系统不一样生成的文件也有差异,那就各自生成各自的
|
|||
resourcesPath = absolutePath.split("/target")[0] + "/src/test/resources/codegen/"; |
|||
} |
|||
|
|||
protected static CodegenTableDO getTable(String name) { |
|||
String content = ResourceUtil.readUtf8Str("codegen/table/" + name + ".json"); |
|||
return JsonUtils.parseObject(content, "table", CodegenTableDO.class); |
|||
} |
|||
|
|||
protected static List<CodegenColumnDO> getColumnList(String name) { |
|||
String content = ResourceUtil.readUtf8Str("codegen/table/" + name + ".json"); |
|||
List<CodegenColumnDO> list = JsonUtils.parseArray(content, "columns", CodegenColumnDO.class); |
|||
list.forEach(column -> { |
|||
if (column.getNullable() == null) { |
|||
column.setNullable(false); |
|||
} |
|||
if (column.getCreateOperation() == null) { |
|||
column.setCreateOperation(false); |
|||
} |
|||
if (column.getUpdateOperation() == null) { |
|||
column.setUpdateOperation(false); |
|||
} |
|||
if (column.getListOperation() == null) { |
|||
column.setListOperation(false); |
|||
} |
|||
if (column.getListOperationResult() == null) { |
|||
column.setListOperationResult(false); |
|||
} |
|||
}); |
|||
return list; |
|||
} |
|||
|
|||
@SuppressWarnings("rawtypes") |
|||
protected static void assertResult(Map<String, String> result, String path) { |
|||
String assertContent = ResourceUtil.readUtf8Str("codegen/" + path + "/assert.json"); |
|||
List<HashMap> asserts = JsonUtils.parseArray(assertContent, HashMap.class); |
|||
assertEquals(asserts.size(), result.size()); |
|||
// 校验每个文件
|
|||
asserts.forEach(assertMap -> { |
|||
String contentPath = (String) assertMap.get("contentPath"); |
|||
String filePath = (String) assertMap.get("filePath"); |
|||
String content = ResourceUtil.readUtf8Str("codegen/" + path + "/" + contentPath); |
|||
assertEquals(content, result.get(filePath), filePath + ":不匹配"); |
|||
}); |
|||
} |
|||
|
|||
// ==================== 调试专用 ====================
|
|||
|
|||
/** |
|||
* 【调试使用】将生成的代码,写入到文件 |
|||
* |
|||
* @param result 生成的代码 |
|||
* @param path 写入文件的路径 |
|||
*/ |
|||
protected void writeFile(Map<String, String> result, String path) { |
|||
// 生成压缩包
|
|||
String[] paths = result.keySet().toArray(new String[0]); |
|||
ByteArrayInputStream[] ins = result.values().stream().map(IoUtil::toUtf8Stream).toArray(ByteArrayInputStream[]::new); |
|||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); |
|||
ZipUtil.zip(outputStream, paths, ins); |
|||
// 写入文件
|
|||
FileUtil.writeBytes(outputStream.toByteArray(), path); |
|||
} |
|||
|
|||
/** |
|||
* 【调试使用】将生成的结果,写入到文件 |
|||
* |
|||
* @param result 生成的代码 |
|||
* @param basePath 写入文件的路径(绝对路径) |
|||
*/ |
|||
protected void writeResult(Map<String, String> result, String basePath) { |
|||
// 写入文件内容
|
|||
List<Map<String, String>> asserts = new ArrayList<>(); |
|||
result.forEach((filePath, fileContent) -> { |
|||
String lastFilePath = StrUtil.subAfter(filePath, '/', true); |
|||
String contentPath = StrUtil.subAfter(lastFilePath, '.', true) |
|||
+ '/' + StrUtil.subBefore(lastFilePath, '.', true); |
|||
asserts.add(MapUtil.<String, String>builder().put("filePath", filePath) |
|||
.put("contentPath", contentPath).build()); |
|||
FileUtil.writeUtf8String(fileContent, basePath + "/" + contentPath); |
|||
}); |
|||
// 写入 assert.json 文件
|
|||
FileUtil.writeUtf8String(JsonUtils.toJsonPrettyString(asserts), basePath + "/assert.json"); |
|||
} |
|||
|
|||
} |
@ -1,100 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.service.codegen.inner; |
|||
|
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO; |
|||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum; |
|||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum; |
|||
import org.junit.jupiter.api.Disabled; |
|||
import org.junit.jupiter.api.Test; |
|||
|
|||
import java.util.Arrays; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* {@link CodegenEngine} 的 Vue2 + Element UI 单元测试 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@Disabled |
|||
public class CodegenEngineVue2Test extends CodegenEngineAbstractTest { |
|||
|
|||
@Test |
|||
public void testExecute_vue2_one() { |
|||
// 准备参数
|
|||
CodegenTableDO table = getTable("student") |
|||
.setFrontType(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType()) |
|||
.setTemplateType(CodegenTemplateTypeEnum.ONE.getType()); |
|||
List<CodegenColumnDO> columns = getColumnList("student"); |
|||
|
|||
// 调用
|
|||
Map<String, String> result = codegenEngine.execute(table, columns, null, null); |
|||
// 生成测试文件
|
|||
//writeResult(result, resourcesPath + "/vue2_one");
|
|||
// 断言
|
|||
assertResult(result, "/vue2_one"); |
|||
} |
|||
|
|||
@Test |
|||
public void testExecute_vue2_tree() { |
|||
// 准备参数
|
|||
CodegenTableDO table = getTable("category") |
|||
.setFrontType(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType()) |
|||
.setTemplateType(CodegenTemplateTypeEnum.TREE.getType()); |
|||
List<CodegenColumnDO> columns = getColumnList("category"); |
|||
|
|||
// 调用
|
|||
Map<String, String> result = codegenEngine.execute(table, columns, null, null); |
|||
// 生成测试文件
|
|||
//writeResult(result, resourcesPath + "/vue2_tree");
|
|||
// 断言
|
|||
assertResult(result, "/vue2_tree"); |
|||
// writeFile(result, "/Users/yunai/test/demo66.zip");
|
|||
} |
|||
|
|||
@Test |
|||
public void testExecute_vue2_master_normal() { |
|||
testExecute_vue2_master(CodegenTemplateTypeEnum.MASTER_NORMAL, "/vue2_master_normal"); |
|||
} |
|||
|
|||
@Test |
|||
public void testExecute_vue2_master_erp() { |
|||
testExecute_vue2_master(CodegenTemplateTypeEnum.MASTER_ERP, "/vue2_master_erp"); |
|||
} |
|||
|
|||
@Test |
|||
public void testExecute_vue2_master_inner() { |
|||
testExecute_vue2_master(CodegenTemplateTypeEnum.MASTER_INNER, "/vue2_master_inner"); |
|||
} |
|||
|
|||
private void testExecute_vue2_master(CodegenTemplateTypeEnum templateType, |
|||
String path) { |
|||
// 准备参数
|
|||
CodegenTableDO table = getTable("student") |
|||
.setFrontType(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType()) |
|||
.setTemplateType(templateType.getType()); |
|||
List<CodegenColumnDO> columns = getColumnList("student"); |
|||
// 准备参数(子表)
|
|||
CodegenTableDO contactTable = getTable("contact") |
|||
.setTemplateType(CodegenTemplateTypeEnum.SUB.getType()) |
|||
.setFrontType(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType()) |
|||
.setSubJoinColumnId(100L).setSubJoinMany(true); |
|||
List<CodegenColumnDO> contactColumns = getColumnList("contact"); |
|||
// 准备参数(班主任)
|
|||
CodegenTableDO teacherTable = getTable("teacher") |
|||
.setTemplateType(CodegenTemplateTypeEnum.SUB.getType()) |
|||
.setFrontType(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType()) |
|||
.setSubJoinColumnId(200L).setSubJoinMany(false); |
|||
List<CodegenColumnDO> teacherColumns = getColumnList("teacher"); |
|||
|
|||
// 调用
|
|||
Map<String, String> result = codegenEngine.execute(table, columns, |
|||
Arrays.asList(contactTable, teacherTable), Arrays.asList(contactColumns, teacherColumns)); |
|||
// 生成测试文件
|
|||
//writeResult(result, resourcesPath + path);
|
|||
// 断言
|
|||
assertResult(result, path); |
|||
// writeFile(result, "/Users/yunai/test/demo11.zip");
|
|||
} |
|||
|
|||
} |
@ -1,100 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.service.codegen.inner; |
|||
|
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO; |
|||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum; |
|||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum; |
|||
import org.junit.jupiter.api.Disabled; |
|||
import org.junit.jupiter.api.Test; |
|||
|
|||
import java.util.Arrays; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* {@link CodegenEngine} 的 Vue2 + Element Plus 单元测试 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@Disabled |
|||
public class CodegenEngineVue3Test extends CodegenEngineAbstractTest { |
|||
|
|||
@Test |
|||
public void testExecute_vue3_one() { |
|||
// 准备参数
|
|||
CodegenTableDO table = getTable("student") |
|||
.setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType()) |
|||
.setTemplateType(CodegenTemplateTypeEnum.ONE.getType()); |
|||
List<CodegenColumnDO> columns = getColumnList("student"); |
|||
|
|||
// 调用
|
|||
Map<String, String> result = codegenEngine.execute(table, columns, null, null); |
|||
// 生成测试文件
|
|||
//writeResult(result, resourcesPath + "/vue3_one");
|
|||
// 断言
|
|||
assertResult(result, "/vue3_one"); |
|||
} |
|||
|
|||
@Test |
|||
public void testExecute_vue3_tree() { |
|||
// 准备参数
|
|||
CodegenTableDO table = getTable("category") |
|||
.setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType()) |
|||
.setTemplateType(CodegenTemplateTypeEnum.TREE.getType()); |
|||
List<CodegenColumnDO> columns = getColumnList("category"); |
|||
|
|||
// 调用
|
|||
Map<String, String> result = codegenEngine.execute(table, columns, null, null); |
|||
// 生成测试文件
|
|||
//writeResult(result, resourcesPath + "/vue3_tree");
|
|||
// 断言
|
|||
assertResult(result, "/vue3_tree"); |
|||
// writeFile(result, "/Users/yunai/test/demo66.zip");
|
|||
} |
|||
|
|||
@Test |
|||
public void testExecute_vue3_master_normal() { |
|||
testExecute_vue3_master(CodegenTemplateTypeEnum.MASTER_NORMAL, "/vue3_master_normal"); |
|||
} |
|||
|
|||
@Test |
|||
public void testExecute_vue3_master_erp() { |
|||
testExecute_vue3_master(CodegenTemplateTypeEnum.MASTER_ERP, "/vue3_master_erp"); |
|||
} |
|||
|
|||
@Test |
|||
public void testExecute_vue3_master_inner() { |
|||
testExecute_vue3_master(CodegenTemplateTypeEnum.MASTER_INNER, "/vue3_master_inner"); |
|||
} |
|||
|
|||
private void testExecute_vue3_master(CodegenTemplateTypeEnum templateType, |
|||
String path) { |
|||
// 准备参数
|
|||
CodegenTableDO table = getTable("student") |
|||
.setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType()) |
|||
.setTemplateType(templateType.getType()); |
|||
List<CodegenColumnDO> columns = getColumnList("student"); |
|||
// 准备参数(子表)
|
|||
CodegenTableDO contactTable = getTable("contact") |
|||
.setTemplateType(CodegenTemplateTypeEnum.SUB.getType()) |
|||
.setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType()) |
|||
.setSubJoinColumnId(100L).setSubJoinMany(true); |
|||
List<CodegenColumnDO> contactColumns = getColumnList("contact"); |
|||
// 准备参数(班主任)
|
|||
CodegenTableDO teacherTable = getTable("teacher") |
|||
.setTemplateType(CodegenTemplateTypeEnum.SUB.getType()) |
|||
.setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType()) |
|||
.setSubJoinColumnId(200L).setSubJoinMany(false); |
|||
List<CodegenColumnDO> teacherColumns = getColumnList("teacher"); |
|||
|
|||
// 调用
|
|||
Map<String, String> result = codegenEngine.execute(table, columns, |
|||
Arrays.asList(contactTable, teacherTable), Arrays.asList(contactColumns, teacherColumns)); |
|||
// 生成测试文件
|
|||
//writeResult(result, resourcesPath + path);
|
|||
// 断言
|
|||
assertResult(result, path); |
|||
// writeFile(result, "/Users/yunai/test/demo11.zip");
|
|||
} |
|||
|
|||
} |
@ -1,219 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.service.config; |
|||
|
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
|||
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; |
|||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
|||
import cn.iocoder.yudao.framework.test.core.util.RandomUtils; |
|||
import cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigPageReqVO; |
|||
import cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigSaveReqVO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.config.ConfigDO; |
|||
import cn.iocoder.yudao.module.infra.dal.mysql.config.ConfigMapper; |
|||
import cn.iocoder.yudao.module.infra.enums.config.ConfigTypeEnum; |
|||
import org.junit.jupiter.api.Test; |
|||
import org.springframework.context.annotation.Import; |
|||
|
|||
import jakarta.annotation.Resource; |
|||
import java.util.function.Consumer; |
|||
|
|||
import static cn.hutool.core.util.RandomUtil.randomEle; |
|||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; |
|||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; |
|||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; |
|||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; |
|||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; |
|||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; |
|||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; |
|||
import static org.junit.jupiter.api.Assertions.*; |
|||
|
|||
@Import(ConfigServiceImpl.class) |
|||
public class ConfigServiceImplTest extends BaseDbUnitTest { |
|||
|
|||
@Resource |
|||
private ConfigServiceImpl configService; |
|||
|
|||
@Resource |
|||
private ConfigMapper configMapper; |
|||
|
|||
@Test |
|||
public void testCreateConfig_success() { |
|||
// 准备参数
|
|||
ConfigSaveReqVO reqVO = randomPojo(ConfigSaveReqVO.class) |
|||
.setId(null); // 防止 id 被赋值,导致唯一性校验失败
|
|||
|
|||
// 调用
|
|||
Long configId = configService.createConfig(reqVO); |
|||
// 断言
|
|||
assertNotNull(configId); |
|||
// 校验记录的属性是否正确
|
|||
ConfigDO config = configMapper.selectById(configId); |
|||
assertPojoEquals(reqVO, config, "id"); |
|||
assertEquals(ConfigTypeEnum.CUSTOM.getType(), config.getType()); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateConfig_success() { |
|||
// mock 数据
|
|||
ConfigDO dbConfig = randomConfigDO(); |
|||
configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据
|
|||
// 准备参数
|
|||
ConfigSaveReqVO reqVO = randomPojo(ConfigSaveReqVO.class, o -> { |
|||
o.setId(dbConfig.getId()); // 设置更新的 ID
|
|||
}); |
|||
|
|||
// 调用
|
|||
configService.updateConfig(reqVO); |
|||
// 校验是否更新正确
|
|||
ConfigDO config = configMapper.selectById(reqVO.getId()); // 获取最新的
|
|||
assertPojoEquals(reqVO, config); |
|||
} |
|||
|
|||
@Test |
|||
public void testDeleteConfig_success() { |
|||
// mock 数据
|
|||
ConfigDO dbConfig = randomConfigDO(o -> { |
|||
o.setType(ConfigTypeEnum.CUSTOM.getType()); // 只能删除 CUSTOM 类型
|
|||
}); |
|||
configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据
|
|||
// 准备参数
|
|||
Long id = dbConfig.getId(); |
|||
|
|||
// 调用
|
|||
configService.deleteConfig(id); |
|||
// 校验数据不存在了
|
|||
assertNull(configMapper.selectById(id)); |
|||
} |
|||
|
|||
@Test |
|||
public void testDeleteConfig_canNotDeleteSystemType() { |
|||
// mock 数据
|
|||
ConfigDO dbConfig = randomConfigDO(o -> { |
|||
o.setType(ConfigTypeEnum.SYSTEM.getType()); // SYSTEM 不允许删除
|
|||
}); |
|||
configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据
|
|||
// 准备参数
|
|||
Long id = dbConfig.getId(); |
|||
|
|||
// 调用, 并断言异常
|
|||
assertServiceException(() -> configService.deleteConfig(id), CONFIG_CAN_NOT_DELETE_SYSTEM_TYPE); |
|||
} |
|||
|
|||
@Test |
|||
public void testValidateConfigExists_success() { |
|||
// mock 数据
|
|||
ConfigDO dbConfigDO = randomConfigDO(); |
|||
configMapper.insert(dbConfigDO);// @Sql: 先插入出一条存在的数据
|
|||
|
|||
// 调用成功
|
|||
configService.validateConfigExists(dbConfigDO.getId()); |
|||
} |
|||
|
|||
@Test |
|||
public void testValidateConfigExist_notExists() { |
|||
assertServiceException(() -> configService.validateConfigExists(randomLongId()), CONFIG_NOT_EXISTS); |
|||
} |
|||
|
|||
@Test |
|||
public void testValidateConfigKeyUnique_success() { |
|||
// 调用,成功
|
|||
configService.validateConfigKeyUnique(randomLongId(), randomString()); |
|||
} |
|||
|
|||
@Test |
|||
public void testValidateConfigKeyUnique_keyDuplicateForCreate() { |
|||
// 准备参数
|
|||
String key = randomString(); |
|||
// mock 数据
|
|||
configMapper.insert(randomConfigDO(o -> o.setConfigKey(key))); |
|||
|
|||
// 调用,校验异常
|
|||
assertServiceException(() -> configService.validateConfigKeyUnique(null, key), |
|||
CONFIG_KEY_DUPLICATE); |
|||
} |
|||
|
|||
@Test |
|||
public void testValidateConfigKeyUnique_keyDuplicateForUpdate() { |
|||
// 准备参数
|
|||
Long id = randomLongId(); |
|||
String key = randomString(); |
|||
// mock 数据
|
|||
configMapper.insert(randomConfigDO(o -> o.setConfigKey(key))); |
|||
|
|||
// 调用,校验异常
|
|||
assertServiceException(() -> configService.validateConfigKeyUnique(id, key), |
|||
CONFIG_KEY_DUPLICATE); |
|||
} |
|||
|
|||
@Test |
|||
public void testGetConfigPage() { |
|||
// mock 数据
|
|||
ConfigDO dbConfig = randomConfigDO(o -> { // 等会查询到
|
|||
o.setName("芋艿"); |
|||
o.setConfigKey("yunai"); |
|||
o.setType(ConfigTypeEnum.SYSTEM.getType()); |
|||
o.setCreateTime(buildTime(2021, 2, 1)); |
|||
}); |
|||
configMapper.insert(dbConfig); |
|||
// 测试 name 不匹配
|
|||
configMapper.insert(cloneIgnoreId(dbConfig, o -> o.setName("土豆"))); |
|||
// 测试 key 不匹配
|
|||
configMapper.insert(cloneIgnoreId(dbConfig, o -> o.setConfigKey("tudou"))); |
|||
// 测试 type 不匹配
|
|||
configMapper.insert(cloneIgnoreId(dbConfig, o -> o.setType(ConfigTypeEnum.CUSTOM.getType()))); |
|||
// 测试 createTime 不匹配
|
|||
configMapper.insert(cloneIgnoreId(dbConfig, o -> o.setCreateTime(buildTime(2021, 1, 1)))); |
|||
// 准备参数
|
|||
ConfigPageReqVO reqVO = new ConfigPageReqVO(); |
|||
reqVO.setName("艿"); |
|||
reqVO.setKey("nai"); |
|||
reqVO.setType(ConfigTypeEnum.SYSTEM.getType()); |
|||
reqVO.setCreateTime(buildBetweenTime(2021, 1, 15, 2021, 2, 15)); |
|||
|
|||
// 调用
|
|||
PageResult<ConfigDO> pageResult = configService.getConfigPage(reqVO); |
|||
// 断言
|
|||
assertEquals(1, pageResult.getTotal()); |
|||
assertEquals(1, pageResult.getList().size()); |
|||
assertPojoEquals(dbConfig, pageResult.getList().get(0)); |
|||
} |
|||
|
|||
@Test |
|||
public void testGetConfig() { |
|||
// mock 数据
|
|||
ConfigDO dbConfig = randomConfigDO(); |
|||
configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据
|
|||
// 准备参数
|
|||
Long id = dbConfig.getId(); |
|||
|
|||
// 调用
|
|||
ConfigDO config = configService.getConfig(id); |
|||
// 断言
|
|||
assertNotNull(config); |
|||
assertPojoEquals(dbConfig, config); |
|||
} |
|||
|
|||
@Test |
|||
public void testGetConfigByKey() { |
|||
// mock 数据
|
|||
ConfigDO dbConfig = randomConfigDO(); |
|||
configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据
|
|||
// 准备参数
|
|||
String key = dbConfig.getConfigKey(); |
|||
|
|||
// 调用
|
|||
ConfigDO config = configService.getConfigByKey(key); |
|||
// 断言
|
|||
assertNotNull(config); |
|||
assertPojoEquals(dbConfig, config); |
|||
} |
|||
|
|||
// ========== 随机对象 ==========
|
|||
|
|||
@SafeVarargs |
|||
private static ConfigDO randomConfigDO(Consumer<ConfigDO>... consumers) { |
|||
Consumer<ConfigDO> consumer = (o) -> { |
|||
o.setType(randomEle(ConfigTypeEnum.values()).getType()); // 保证 key 的范围
|
|||
}; |
|||
return RandomUtils.randomPojo(ConfigDO.class, ArrayUtils.append(consumer, consumers)); |
|||
} |
|||
|
|||
} |
@ -1,208 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.service.db; |
|||
|
|||
import cn.hutool.core.map.MapUtil; |
|||
import cn.hutool.core.util.ReflectUtil; |
|||
import cn.hutool.crypto.symmetric.AES; |
|||
import cn.iocoder.yudao.framework.mybatis.core.type.EncryptTypeHandler; |
|||
import cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils; |
|||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
|||
import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigSaveReqVO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO; |
|||
import cn.iocoder.yudao.module.infra.dal.mysql.db.DataSourceConfigMapper; |
|||
import com.baomidou.dynamic.datasource.creator.DataSourceProperty; |
|||
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties; |
|||
import org.junit.jupiter.api.BeforeEach; |
|||
import org.junit.jupiter.api.Test; |
|||
import org.mockito.MockedStatic; |
|||
import org.mockito.stubbing.Answer; |
|||
import org.springframework.boot.test.mock.mockito.MockBean; |
|||
import org.springframework.context.annotation.Import; |
|||
|
|||
import jakarta.annotation.Resource; |
|||
import java.util.List; |
|||
|
|||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; |
|||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; |
|||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; |
|||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
|||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_NOT_EXISTS; |
|||
import static org.junit.jupiter.api.Assertions.*; |
|||
import static org.mockito.ArgumentMatchers.anyString; |
|||
import static org.mockito.ArgumentMatchers.eq; |
|||
import static org.mockito.Mockito.mockStatic; |
|||
import static org.mockito.Mockito.when; |
|||
|
|||
/** |
|||
* {@link DataSourceConfigServiceImpl} 的单元测试类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@Import(DataSourceConfigServiceImpl.class) |
|||
public class DataSourceConfigServiceImplTest extends BaseDbUnitTest { |
|||
|
|||
@Resource |
|||
private DataSourceConfigServiceImpl dataSourceConfigService; |
|||
|
|||
@Resource |
|||
private DataSourceConfigMapper dataSourceConfigMapper; |
|||
|
|||
@MockBean |
|||
private AES aes; |
|||
|
|||
@MockBean |
|||
private DynamicDataSourceProperties dynamicDataSourceProperties; |
|||
|
|||
@BeforeEach |
|||
public void setUp() { |
|||
// mock 一个空实现的 StringEncryptor,避免 EncryptTypeHandler 报错
|
|||
ReflectUtil.setFieldValue(EncryptTypeHandler.class, "aes", aes); |
|||
when(aes.encryptBase64(anyString())).then((Answer<String>) invocation -> invocation.getArgument(0)); |
|||
when(aes.decryptStr(anyString())).then((Answer<String>) invocation -> invocation.getArgument(0)); |
|||
|
|||
// mock DynamicDataSourceProperties
|
|||
when(dynamicDataSourceProperties.getPrimary()).thenReturn("primary"); |
|||
DataSourceProperty dataSourceProperty = new DataSourceProperty(); |
|||
dataSourceProperty.setUrl("http://localhost:3306"); |
|||
dataSourceProperty.setUsername("yunai"); |
|||
dataSourceProperty.setPassword("tudou"); |
|||
when(dynamicDataSourceProperties.getDatasource()).thenReturn(MapUtil.of("primary", dataSourceProperty)); |
|||
} |
|||
|
|||
@Test |
|||
public void testCreateDataSourceConfig_success() { |
|||
try (MockedStatic<JdbcUtils> databaseUtilsMock = mockStatic(JdbcUtils.class)) { |
|||
// 准备参数
|
|||
DataSourceConfigSaveReqVO reqVO = randomPojo(DataSourceConfigSaveReqVO.class) |
|||
.setId(null); // 避免 id 被设置
|
|||
// mock 方法
|
|||
databaseUtilsMock.when(() -> JdbcUtils.isConnectionOK(eq(reqVO.getUrl()), |
|||
eq(reqVO.getUsername()), eq(reqVO.getPassword()))).thenReturn(true); |
|||
|
|||
// 调用
|
|||
Long dataSourceConfigId = dataSourceConfigService.createDataSourceConfig(reqVO); |
|||
// 断言
|
|||
assertNotNull(dataSourceConfigId); |
|||
// 校验记录的属性是否正确
|
|||
DataSourceConfigDO dataSourceConfig = dataSourceConfigMapper.selectById(dataSourceConfigId); |
|||
assertPojoEquals(reqVO, dataSourceConfig, "id"); |
|||
} |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateDataSourceConfig_success() { |
|||
try (MockedStatic<JdbcUtils> databaseUtilsMock = mockStatic(JdbcUtils.class)) { |
|||
// mock 数据
|
|||
DataSourceConfigDO dbDataSourceConfig = randomPojo(DataSourceConfigDO.class); |
|||
dataSourceConfigMapper.insert(dbDataSourceConfig);// @Sql: 先插入出一条存在的数据
|
|||
// 准备参数
|
|||
DataSourceConfigSaveReqVO reqVO = randomPojo(DataSourceConfigSaveReqVO.class, o -> { |
|||
o.setId(dbDataSourceConfig.getId()); // 设置更新的 ID
|
|||
}); |
|||
// mock 方法
|
|||
databaseUtilsMock.when(() -> JdbcUtils.isConnectionOK(eq(reqVO.getUrl()), |
|||
eq(reqVO.getUsername()), eq(reqVO.getPassword()))).thenReturn(true); |
|||
|
|||
// 调用
|
|||
dataSourceConfigService.updateDataSourceConfig(reqVO); |
|||
// 校验是否更新正确
|
|||
DataSourceConfigDO dataSourceConfig = dataSourceConfigMapper.selectById(reqVO.getId()); // 获取最新的
|
|||
assertPojoEquals(reqVO, dataSourceConfig); |
|||
} |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateDataSourceConfig_notExists() { |
|||
// 准备参数
|
|||
DataSourceConfigSaveReqVO reqVO = randomPojo(DataSourceConfigSaveReqVO.class); |
|||
|
|||
// 调用, 并断言异常
|
|||
assertServiceException(() -> dataSourceConfigService.updateDataSourceConfig(reqVO), DATA_SOURCE_CONFIG_NOT_EXISTS); |
|||
} |
|||
|
|||
@Test |
|||
public void testDeleteDataSourceConfig_success() { |
|||
// mock 数据
|
|||
DataSourceConfigDO dbDataSourceConfig = randomPojo(DataSourceConfigDO.class); |
|||
dataSourceConfigMapper.insert(dbDataSourceConfig);// @Sql: 先插入出一条存在的数据
|
|||
// 准备参数
|
|||
Long id = dbDataSourceConfig.getId(); |
|||
|
|||
// 调用
|
|||
dataSourceConfigService.deleteDataSourceConfig(id); |
|||
// 校验数据不存在了
|
|||
assertNull(dataSourceConfigMapper.selectById(id)); |
|||
} |
|||
|
|||
@Test |
|||
public void testDeleteDataSourceConfig_notExists() { |
|||
// 准备参数
|
|||
Long id = randomLongId(); |
|||
|
|||
// 调用, 并断言异常
|
|||
assertServiceException(() -> dataSourceConfigService.deleteDataSourceConfig(id), DATA_SOURCE_CONFIG_NOT_EXISTS); |
|||
} |
|||
|
|||
@Test // 测试使用 password 查询,可以查询到数据
|
|||
public void testSelectPassword() { |
|||
// mock 数据
|
|||
DataSourceConfigDO dbDataSourceConfig = randomPojo(DataSourceConfigDO.class); |
|||
dataSourceConfigMapper.insert(dbDataSourceConfig);// @Sql: 先插入出一条存在的数据
|
|||
|
|||
// 调用
|
|||
DataSourceConfigDO result = dataSourceConfigMapper.selectOne(DataSourceConfigDO::getPassword, |
|||
EncryptTypeHandler.encrypt(dbDataSourceConfig.getPassword())); |
|||
assertPojoEquals(dbDataSourceConfig, result); |
|||
} |
|||
|
|||
@Test |
|||
public void testGetDataSourceConfig_master() { |
|||
// 准备参数
|
|||
Long id = 0L; |
|||
// mock 方法
|
|||
|
|||
// 调用
|
|||
DataSourceConfigDO dataSourceConfig = dataSourceConfigService.getDataSourceConfig(id); |
|||
// 断言
|
|||
assertEquals(id, dataSourceConfig.getId()); |
|||
assertEquals("primary", dataSourceConfig.getName()); |
|||
assertEquals("http://localhost:3306", dataSourceConfig.getUrl()); |
|||
assertEquals("yunai", dataSourceConfig.getUsername()); |
|||
assertEquals("tudou", dataSourceConfig.getPassword()); |
|||
} |
|||
|
|||
@Test |
|||
public void testGetDataSourceConfig_normal() { |
|||
// mock 数据
|
|||
DataSourceConfigDO dbDataSourceConfig = randomPojo(DataSourceConfigDO.class); |
|||
dataSourceConfigMapper.insert(dbDataSourceConfig);// @Sql: 先插入出一条存在的数据
|
|||
// 准备参数
|
|||
Long id = dbDataSourceConfig.getId(); |
|||
|
|||
// 调用
|
|||
DataSourceConfigDO dataSourceConfig = dataSourceConfigService.getDataSourceConfig(id); |
|||
// 断言
|
|||
assertPojoEquals(dbDataSourceConfig, dataSourceConfig); |
|||
} |
|||
|
|||
@Test |
|||
public void testGetDataSourceConfigList() { |
|||
// mock 数据
|
|||
DataSourceConfigDO dbDataSourceConfig = randomPojo(DataSourceConfigDO.class); |
|||
dataSourceConfigMapper.insert(dbDataSourceConfig);// @Sql: 先插入出一条存在的数据
|
|||
// 准备参数
|
|||
|
|||
// 调用
|
|||
List<DataSourceConfigDO> dataSourceConfigList = dataSourceConfigService.getDataSourceConfigList(); |
|||
// 断言
|
|||
assertEquals(2, dataSourceConfigList.size()); |
|||
// master
|
|||
assertEquals(0L, dataSourceConfigList.get(0).getId()); |
|||
assertEquals("primary", dataSourceConfigList.get(0).getName()); |
|||
assertEquals("http://localhost:3306", dataSourceConfigList.get(0).getUrl()); |
|||
assertEquals("yunai", dataSourceConfigList.get(0).getUsername()); |
|||
assertEquals("tudou", dataSourceConfigList.get(0).getPassword()); |
|||
// normal
|
|||
assertPojoEquals(dbDataSourceConfig, dataSourceConfigList.get(1)); |
|||
} |
|||
|
|||
} |
@ -1,89 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.service.db; |
|||
|
|||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO; |
|||
import com.baomidou.mybatisplus.generator.config.po.TableField; |
|||
import com.baomidou.mybatisplus.generator.config.po.TableInfo; |
|||
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType; |
|||
import org.apache.ibatis.type.JdbcType; |
|||
import org.junit.jupiter.api.Test; |
|||
import org.springframework.boot.test.mock.mockito.MockBean; |
|||
import org.springframework.context.annotation.Import; |
|||
|
|||
import jakarta.annotation.Resource; |
|||
import java.util.List; |
|||
|
|||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; |
|||
import static org.junit.jupiter.api.Assertions.*; |
|||
import static org.mockito.ArgumentMatchers.eq; |
|||
import static org.mockito.Mockito.when; |
|||
|
|||
@Import(DatabaseTableServiceImpl.class) |
|||
public class DatabaseTableServiceImplTest extends BaseDbUnitTest { |
|||
|
|||
@Resource |
|||
private DatabaseTableServiceImpl databaseTableService; |
|||
|
|||
@MockBean |
|||
private DataSourceConfigService dataSourceConfigService; |
|||
|
|||
@Test |
|||
public void testGetTableList() { |
|||
// 准备参数
|
|||
Long dataSourceConfigId = randomLongId(); |
|||
// mock 方法
|
|||
DataSourceConfigDO dataSourceConfig = new DataSourceConfigDO().setUsername("sa").setPassword("") |
|||
.setUrl("jdbc:h2:mem:testdb"); |
|||
when(dataSourceConfigService.getDataSourceConfig(eq(dataSourceConfigId))) |
|||
.thenReturn(dataSourceConfig); |
|||
|
|||
// 调用
|
|||
List<TableInfo> tables = databaseTableService.getTableList(dataSourceConfigId, |
|||
"config", "参数"); |
|||
// 断言
|
|||
assertEquals(1, tables.size()); |
|||
assertTableInfo(tables.get(0)); |
|||
} |
|||
|
|||
@Test |
|||
public void testGetTable() { |
|||
// 准备参数
|
|||
Long dataSourceConfigId = randomLongId(); |
|||
// mock 方法
|
|||
DataSourceConfigDO dataSourceConfig = new DataSourceConfigDO().setUsername("sa").setPassword("") |
|||
.setUrl("jdbc:h2:mem:testdb"); |
|||
when(dataSourceConfigService.getDataSourceConfig(eq(dataSourceConfigId))) |
|||
.thenReturn(dataSourceConfig); |
|||
|
|||
// 调用
|
|||
TableInfo tableInfo = databaseTableService.getTable(dataSourceConfigId, "infra_config"); |
|||
// 断言
|
|||
assertTableInfo(tableInfo); |
|||
} |
|||
|
|||
private void assertTableInfo(TableInfo tableInfo) { |
|||
assertEquals("infra_config", tableInfo.getName()); |
|||
assertEquals("参数配置表", tableInfo.getComment()); |
|||
assertEquals(13, tableInfo.getFields().size()); |
|||
// id 字段
|
|||
TableField idField = tableInfo.getFields().get(0); |
|||
assertEquals("id", idField.getName()); |
|||
assertEquals(JdbcType.BIGINT, idField.getMetaInfo().getJdbcType()); |
|||
assertEquals("编号", idField.getComment()); |
|||
assertFalse(idField.getMetaInfo().isNullable()); |
|||
assertTrue(idField.isKeyFlag()); |
|||
assertTrue(idField.isKeyIdentityFlag()); |
|||
assertEquals(DbColumnType.LONG, idField.getColumnType()); |
|||
assertEquals("id", idField.getPropertyName()); |
|||
// name 字段
|
|||
TableField nameField = tableInfo.getFields().get(3); |
|||
assertEquals("name", nameField.getName()); |
|||
assertEquals(JdbcType.VARCHAR, nameField.getMetaInfo().getJdbcType()); |
|||
assertEquals("名字", nameField.getComment()); |
|||
assertFalse(nameField.getMetaInfo().isNullable()); |
|||
assertFalse(nameField.isKeyFlag()); |
|||
assertFalse(nameField.isKeyIdentityFlag()); |
|||
assertEquals(DbColumnType.STRING, nameField.getColumnType()); |
|||
assertEquals("name", nameField.getPropertyName()); |
|||
} |
|||
} |
@ -1,281 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.service.file; |
|||
|
|||
import cn.hutool.core.date.DatePattern; |
|||
import cn.hutool.core.date.LocalDateTimeUtil; |
|||
import cn.hutool.core.map.MapUtil; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
|||
import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient; |
|||
import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig; |
|||
import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientFactory; |
|||
import cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClient; |
|||
import cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClientConfig; |
|||
import cn.iocoder.yudao.module.infra.framework.file.core.enums.FileStorageEnum; |
|||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
|||
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigPageReqVO; |
|||
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigSaveReqVO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO; |
|||
import cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper; |
|||
import lombok.Data; |
|||
import org.junit.jupiter.api.Test; |
|||
import org.springframework.boot.test.mock.mockito.MockBean; |
|||
import org.springframework.context.annotation.Import; |
|||
|
|||
import jakarta.annotation.Resource; |
|||
import jakarta.validation.Validator; |
|||
import java.io.Serializable; |
|||
import java.time.LocalDateTime; |
|||
import java.util.Map; |
|||
|
|||
import static cn.hutool.core.util.RandomUtil.randomEle; |
|||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; |
|||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; |
|||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; |
|||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; |
|||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; |
|||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
|||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_CONFIG_DELETE_FAIL_MASTER; |
|||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_CONFIG_NOT_EXISTS; |
|||
import static org.junit.jupiter.api.Assertions.*; |
|||
import static org.mockito.ArgumentMatchers.eq; |
|||
import static org.mockito.Mockito.*; |
|||
|
|||
/** |
|||
* {@link FileConfigServiceImpl} 的单元测试类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@Import(FileConfigServiceImpl.class) |
|||
public class FileConfigServiceImplTest extends BaseDbUnitTest { |
|||
|
|||
@Resource |
|||
private FileConfigServiceImpl fileConfigService; |
|||
|
|||
@Resource |
|||
private FileConfigMapper fileConfigMapper; |
|||
|
|||
@MockBean |
|||
private Validator validator; |
|||
@MockBean |
|||
private FileClientFactory fileClientFactory; |
|||
|
|||
@Test |
|||
public void testCreateFileConfig_success() { |
|||
// 准备参数
|
|||
Map<String, Object> config = MapUtil.<String, Object>builder().put("basePath", "/yunai") |
|||
.put("domain", "https://www.iocoder.cn").build(); |
|||
FileConfigSaveReqVO reqVO = randomPojo(FileConfigSaveReqVO.class, |
|||
o -> o.setStorage(FileStorageEnum.LOCAL.getStorage()).setConfig(config)) |
|||
.setId(null); // 避免 id 被赋值
|
|||
|
|||
// 调用
|
|||
Long fileConfigId = fileConfigService.createFileConfig(reqVO); |
|||
// 断言
|
|||
assertNotNull(fileConfigId); |
|||
// 校验记录的属性是否正确
|
|||
FileConfigDO fileConfig = fileConfigMapper.selectById(fileConfigId); |
|||
assertPojoEquals(reqVO, fileConfig, "id", "config"); |
|||
assertFalse(fileConfig.getMaster()); |
|||
assertEquals("/yunai", ((LocalFileClientConfig) fileConfig.getConfig()).getBasePath()); |
|||
assertEquals("https://www.iocoder.cn", ((LocalFileClientConfig) fileConfig.getConfig()).getDomain()); |
|||
// 验证 cache
|
|||
assertNull(fileConfigService.getClientCache().getIfPresent(fileConfigId)); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateFileConfig_success() { |
|||
// mock 数据
|
|||
FileConfigDO dbFileConfig = randomPojo(FileConfigDO.class, o -> o.setStorage(FileStorageEnum.LOCAL.getStorage()) |
|||
.setConfig(new LocalFileClientConfig().setBasePath("/yunai").setDomain("https://www.iocoder.cn"))); |
|||
fileConfigMapper.insert(dbFileConfig);// @Sql: 先插入出一条存在的数据
|
|||
// 准备参数
|
|||
FileConfigSaveReqVO reqVO = randomPojo(FileConfigSaveReqVO.class, o -> { |
|||
o.setId(dbFileConfig.getId()); // 设置更新的 ID
|
|||
o.setStorage(FileStorageEnum.LOCAL.getStorage()); |
|||
Map<String, Object> config = MapUtil.<String, Object>builder().put("basePath", "/yunai2") |
|||
.put("domain", "https://doc.iocoder.cn").build(); |
|||
o.setConfig(config); |
|||
}); |
|||
|
|||
// 调用
|
|||
fileConfigService.updateFileConfig(reqVO); |
|||
// 校验是否更新正确
|
|||
FileConfigDO fileConfig = fileConfigMapper.selectById(reqVO.getId()); // 获取最新的
|
|||
assertPojoEquals(reqVO, fileConfig, "config"); |
|||
assertEquals("/yunai2", ((LocalFileClientConfig) fileConfig.getConfig()).getBasePath()); |
|||
assertEquals("https://doc.iocoder.cn", ((LocalFileClientConfig) fileConfig.getConfig()).getDomain()); |
|||
// 验证 cache
|
|||
assertNull(fileConfigService.getClientCache().getIfPresent(fileConfig.getId())); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateFileConfig_notExists() { |
|||
// 准备参数
|
|||
FileConfigSaveReqVO reqVO = randomPojo(FileConfigSaveReqVO.class); |
|||
|
|||
// 调用, 并断言异常
|
|||
assertServiceException(() -> fileConfigService.updateFileConfig(reqVO), FILE_CONFIG_NOT_EXISTS); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateFileConfigMaster_success() { |
|||
// mock 数据
|
|||
FileConfigDO dbFileConfig = randomFileConfigDO().setMaster(false); |
|||
fileConfigMapper.insert(dbFileConfig);// @Sql: 先插入出一条存在的数据
|
|||
FileConfigDO masterFileConfig = randomFileConfigDO().setMaster(true); |
|||
fileConfigMapper.insert(masterFileConfig);// @Sql: 先插入出一条存在的数据
|
|||
|
|||
// 调用
|
|||
fileConfigService.updateFileConfigMaster(dbFileConfig.getId()); |
|||
// 断言数据
|
|||
assertTrue(fileConfigMapper.selectById(dbFileConfig.getId()).getMaster()); |
|||
assertFalse(fileConfigMapper.selectById(masterFileConfig.getId()).getMaster()); |
|||
// 验证 cache
|
|||
assertNull(fileConfigService.getClientCache().getIfPresent(0L)); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateFileConfigMaster_notExists() { |
|||
// 调用, 并断言异常
|
|||
assertServiceException(() -> fileConfigService.updateFileConfigMaster(randomLongId()), FILE_CONFIG_NOT_EXISTS); |
|||
} |
|||
|
|||
@Test |
|||
public void testDeleteFileConfig_success() { |
|||
// mock 数据
|
|||
FileConfigDO dbFileConfig = randomFileConfigDO().setMaster(false); |
|||
fileConfigMapper.insert(dbFileConfig);// @Sql: 先插入出一条存在的数据
|
|||
// 准备参数
|
|||
Long id = dbFileConfig.getId(); |
|||
|
|||
// 调用
|
|||
fileConfigService.deleteFileConfig(id); |
|||
// 校验数据不存在了
|
|||
assertNull(fileConfigMapper.selectById(id)); |
|||
// 验证 cache
|
|||
assertNull(fileConfigService.getClientCache().getIfPresent(id)); |
|||
} |
|||
|
|||
@Test |
|||
public void testDeleteFileConfig_notExists() { |
|||
// 准备参数
|
|||
Long id = randomLongId(); |
|||
|
|||
// 调用, 并断言异常
|
|||
assertServiceException(() -> fileConfigService.deleteFileConfig(id), FILE_CONFIG_NOT_EXISTS); |
|||
} |
|||
|
|||
@Test |
|||
public void testDeleteFileConfig_master() { |
|||
// mock 数据
|
|||
FileConfigDO dbFileConfig = randomFileConfigDO().setMaster(true); |
|||
fileConfigMapper.insert(dbFileConfig);// @Sql: 先插入出一条存在的数据
|
|||
// 准备参数
|
|||
Long id = dbFileConfig.getId(); |
|||
|
|||
// 调用, 并断言异常
|
|||
assertServiceException(() -> fileConfigService.deleteFileConfig(id), FILE_CONFIG_DELETE_FAIL_MASTER); |
|||
} |
|||
|
|||
@Test |
|||
public void testGetFileConfigPage() { |
|||
// mock 数据
|
|||
FileConfigDO dbFileConfig = randomFileConfigDO().setName("芋道源码") |
|||
.setStorage(FileStorageEnum.LOCAL.getStorage()); |
|||
dbFileConfig.setCreateTime(LocalDateTimeUtil.parse("2020-01-23", DatePattern.NORM_DATE_PATTERN));// 等会查询到
|
|||
fileConfigMapper.insert(dbFileConfig); |
|||
// 测试 name 不匹配
|
|||
fileConfigMapper.insert(cloneIgnoreId(dbFileConfig, o -> o.setName("源码"))); |
|||
// 测试 storage 不匹配
|
|||
fileConfigMapper.insert(cloneIgnoreId(dbFileConfig, o -> o.setStorage(FileStorageEnum.DB.getStorage()))); |
|||
// 测试 createTime 不匹配
|
|||
fileConfigMapper.insert(cloneIgnoreId(dbFileConfig, o -> o.setCreateTime(LocalDateTimeUtil.parse("2020-11-23", DatePattern.NORM_DATE_PATTERN)))); |
|||
// 准备参数
|
|||
FileConfigPageReqVO reqVO = new FileConfigPageReqVO(); |
|||
reqVO.setName("芋道"); |
|||
reqVO.setStorage(FileStorageEnum.LOCAL.getStorage()); |
|||
reqVO.setCreateTime((new LocalDateTime[]{buildTime(2020, 1, 1), |
|||
buildTime(2020, 1, 24)})); |
|||
|
|||
// 调用
|
|||
PageResult<FileConfigDO> pageResult = fileConfigService.getFileConfigPage(reqVO); |
|||
// 断言
|
|||
assertEquals(1, pageResult.getTotal()); |
|||
assertEquals(1, pageResult.getList().size()); |
|||
assertPojoEquals(dbFileConfig, pageResult.getList().get(0)); |
|||
} |
|||
|
|||
@Test |
|||
public void testFileConfig() throws Exception { |
|||
// mock 数据
|
|||
FileConfigDO dbFileConfig = randomFileConfigDO().setMaster(false); |
|||
fileConfigMapper.insert(dbFileConfig);// @Sql: 先插入出一条存在的数据
|
|||
// 准备参数
|
|||
Long id = dbFileConfig.getId(); |
|||
// mock 获得 Client
|
|||
FileClient fileClient = mock(FileClient.class); |
|||
when(fileClientFactory.getFileClient(eq(id))).thenReturn(fileClient); |
|||
when(fileClient.upload(any(), any(), any())).thenReturn("https://www.iocoder.cn"); |
|||
|
|||
// 调用,并断言
|
|||
assertEquals("https://www.iocoder.cn", fileConfigService.testFileConfig(id)); |
|||
} |
|||
|
|||
@Test |
|||
public void testGetFileConfig() { |
|||
// mock 数据
|
|||
FileConfigDO dbFileConfig = randomFileConfigDO().setMaster(false); |
|||
fileConfigMapper.insert(dbFileConfig);// @Sql: 先插入出一条存在的数据
|
|||
// 准备参数
|
|||
Long id = dbFileConfig.getId(); |
|||
|
|||
// 调用,并断言
|
|||
assertPojoEquals(dbFileConfig, fileConfigService.getFileConfig(id)); |
|||
} |
|||
|
|||
@Test |
|||
public void testGetFileClient() { |
|||
// mock 数据
|
|||
FileConfigDO fileConfig = randomFileConfigDO().setMaster(false); |
|||
fileConfigMapper.insert(fileConfig); |
|||
// 准备参数
|
|||
Long id = fileConfig.getId(); |
|||
// mock 获得 Client
|
|||
FileClient fileClient = new LocalFileClient(id, new LocalFileClientConfig()); |
|||
when(fileClientFactory.getFileClient(eq(id))).thenReturn(fileClient); |
|||
|
|||
// 调用,并断言
|
|||
assertSame(fileClient, fileConfigService.getFileClient(id)); |
|||
// 断言缓存
|
|||
verify(fileClientFactory).createOrUpdateFileClient(eq(id), eq(fileConfig.getStorage()), |
|||
eq(fileConfig.getConfig())); |
|||
} |
|||
|
|||
@Test |
|||
public void testGetMasterFileClient() { |
|||
// mock 数据
|
|||
FileConfigDO fileConfig = randomFileConfigDO().setMaster(true); |
|||
fileConfigMapper.insert(fileConfig); |
|||
// 准备参数
|
|||
Long id = fileConfig.getId(); |
|||
// mock 获得 Client
|
|||
FileClient fileClient = new LocalFileClient(id, new LocalFileClientConfig()); |
|||
when(fileClientFactory.getFileClient(eq(fileConfig.getId()))).thenReturn(fileClient); |
|||
|
|||
// 调用,并断言
|
|||
assertSame(fileClient, fileConfigService.getMasterFileClient()); |
|||
// 断言缓存
|
|||
verify(fileClientFactory).createOrUpdateFileClient(eq(fileConfig.getId()), eq(fileConfig.getStorage()), |
|||
eq(fileConfig.getConfig())); |
|||
} |
|||
|
|||
private FileConfigDO randomFileConfigDO() { |
|||
return randomPojo(FileConfigDO.class).setStorage(randomEle(FileStorageEnum.values()).getStorage()) |
|||
.setConfig(new EmptyFileClientConfig()); |
|||
} |
|||
|
|||
@Data |
|||
public static class EmptyFileClientConfig implements FileClientConfig, Serializable { |
|||
|
|||
} |
|||
|
|||
} |
@ -1,309 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.service.file; |
|||
|
|||
import cn.hutool.core.io.resource.ResourceUtil; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
|||
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; |
|||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
|||
import cn.iocoder.yudao.framework.test.core.util.AssertUtils; |
|||
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO; |
|||
import cn.iocoder.yudao.module.infra.dal.mysql.file.FileMapper; |
|||
import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient; |
|||
import jakarta.annotation.Resource; |
|||
import org.junit.jupiter.api.BeforeEach; |
|||
import org.junit.jupiter.api.Test; |
|||
import org.springframework.boot.test.mock.mockito.MockBean; |
|||
import org.springframework.context.annotation.Import; |
|||
|
|||
import java.time.LocalDateTime; |
|||
import java.util.concurrent.atomic.AtomicReference; |
|||
|
|||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; |
|||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; |
|||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; |
|||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_NOT_EXISTS; |
|||
import static org.junit.jupiter.api.Assertions.*; |
|||
import static org.mockito.ArgumentMatchers.same; |
|||
import static org.mockito.Mockito.*; |
|||
|
|||
@Import({FileServiceImpl.class}) |
|||
public class FileServiceImplTest extends BaseDbUnitTest { |
|||
|
|||
@Resource |
|||
private FileServiceImpl fileService; |
|||
|
|||
@Resource |
|||
private FileMapper fileMapper; |
|||
|
|||
@MockBean |
|||
private FileConfigService fileConfigService; |
|||
|
|||
@BeforeEach |
|||
public void setUp() { |
|||
FileServiceImpl.PATH_PREFIX_DATE_ENABLE = true; |
|||
FileServiceImpl.PATH_SUFFIX_TIMESTAMP_ENABLE = true; |
|||
} |
|||
|
|||
@Test |
|||
public void testGetFilePage() { |
|||
// mock 数据
|
|||
FileDO dbFile = randomPojo(FileDO.class, o -> { // 等会查询到
|
|||
o.setPath("yunai"); |
|||
o.setType("image/jpg"); |
|||
o.setCreateTime(buildTime(2021, 1, 15)); |
|||
}); |
|||
fileMapper.insert(dbFile); |
|||
// 测试 path 不匹配
|
|||
fileMapper.insert(ObjectUtils.cloneIgnoreId(dbFile, o -> o.setPath("tudou"))); |
|||
// 测试 type 不匹配
|
|||
fileMapper.insert(ObjectUtils.cloneIgnoreId(dbFile, o -> { |
|||
o.setType("image/png"); |
|||
})); |
|||
// 测试 createTime 不匹配
|
|||
fileMapper.insert(ObjectUtils.cloneIgnoreId(dbFile, o -> { |
|||
o.setCreateTime(buildTime(2020, 1, 15)); |
|||
})); |
|||
// 准备参数
|
|||
FilePageReqVO reqVO = new FilePageReqVO(); |
|||
reqVO.setPath("yunai"); |
|||
reqVO.setType("jp"); |
|||
reqVO.setCreateTime((new LocalDateTime[]{buildTime(2021, 1, 10), buildTime(2021, 1, 20)})); |
|||
|
|||
// 调用
|
|||
PageResult<FileDO> pageResult = fileService.getFilePage(reqVO); |
|||
// 断言
|
|||
assertEquals(1, pageResult.getTotal()); |
|||
assertEquals(1, pageResult.getList().size()); |
|||
AssertUtils.assertPojoEquals(dbFile, pageResult.getList().get(0)); |
|||
} |
|||
|
|||
/** |
|||
* content、name、directory、type 都非空 |
|||
*/ |
|||
@Test |
|||
public void testCreateFile_success_01() throws Exception { |
|||
// 准备参数
|
|||
byte[] content = ResourceUtil.readBytes("file/erweima.jpg"); |
|||
String name = "单测文件名"; |
|||
String directory = randomString(); |
|||
String type = "image/jpeg"; |
|||
// mock Master 文件客户端
|
|||
FileClient client = mock(FileClient.class); |
|||
when(fileConfigService.getMasterFileClient()).thenReturn(client); |
|||
String url = randomString(); |
|||
AtomicReference<String> pathRef = new AtomicReference<>(); |
|||
when(client.upload(same(content), argThat(path -> { |
|||
assertTrue(path.matches(directory + "/\\d{8}/" + name + "_\\d+.jpg")); |
|||
pathRef.set(path); |
|||
return true; |
|||
}), eq(type))).thenReturn(url); |
|||
when(client.getId()).thenReturn(10L); |
|||
// 调用
|
|||
String result = fileService.createFile(content, name, directory, type); |
|||
// 断言
|
|||
assertEquals(result, url); |
|||
// 校验数据
|
|||
FileDO file = fileMapper.selectOne(FileDO::getUrl, url); |
|||
assertEquals(10L, file.getConfigId()); |
|||
assertEquals(pathRef.get(), file.getPath()); |
|||
assertEquals(url, file.getUrl()); |
|||
assertEquals(type, file.getType()); |
|||
assertEquals(content.length, file.getSize()); |
|||
} |
|||
|
|||
/** |
|||
* content 非空,其它都空 |
|||
*/ |
|||
@Test |
|||
public void testCreateFile_success_02() throws Exception { |
|||
// 准备参数
|
|||
byte[] content = ResourceUtil.readBytes("file/erweima.jpg"); |
|||
// mock Master 文件客户端
|
|||
String type = "image/jpeg"; |
|||
FileClient client = mock(FileClient.class); |
|||
when(fileConfigService.getMasterFileClient()).thenReturn(client); |
|||
String url = randomString(); |
|||
AtomicReference<String> pathRef = new AtomicReference<>(); |
|||
when(client.upload(same(content), argThat(path -> { |
|||
assertTrue(path.matches("\\d{8}/6318848e882d8a7e7e82789d87608f684ee52d41966bfc8cad3ce15aad2b970e_\\d+\\.jpg")); |
|||
pathRef.set(path); |
|||
return true; |
|||
}), eq(type))).thenReturn(url); |
|||
when(client.getId()).thenReturn(10L); |
|||
// 调用
|
|||
String result = fileService.createFile(content, null, null, null); |
|||
// 断言
|
|||
assertEquals(result, url); |
|||
// 校验数据
|
|||
FileDO file = fileMapper.selectOne(FileDO::getUrl, url); |
|||
assertEquals(10L, file.getConfigId()); |
|||
assertEquals(pathRef.get(), file.getPath()); |
|||
assertEquals(url, file.getUrl()); |
|||
assertEquals(type, file.getType()); |
|||
assertEquals(content.length, file.getSize()); |
|||
} |
|||
|
|||
@Test |
|||
public void testDeleteFile_success() throws Exception { |
|||
// mock 数据
|
|||
FileDO dbFile = randomPojo(FileDO.class, o -> o.setConfigId(10L).setPath("tudou.jpg")); |
|||
fileMapper.insert(dbFile);// @Sql: 先插入出一条存在的数据
|
|||
// mock Master 文件客户端
|
|||
FileClient client = mock(FileClient.class); |
|||
when(fileConfigService.getFileClient(eq(10L))).thenReturn(client); |
|||
// 准备参数
|
|||
Long id = dbFile.getId(); |
|||
|
|||
// 调用
|
|||
fileService.deleteFile(id); |
|||
// 校验数据不存在了
|
|||
assertNull(fileMapper.selectById(id)); |
|||
// 校验调用
|
|||
verify(client).delete(eq("tudou.jpg")); |
|||
} |
|||
|
|||
@Test |
|||
public void testDeleteFile_notExists() { |
|||
// 准备参数
|
|||
Long id = randomLongId(); |
|||
|
|||
// 调用, 并断言异常
|
|||
assertServiceException(() -> fileService.deleteFile(id), FILE_NOT_EXISTS); |
|||
} |
|||
|
|||
@Test |
|||
public void testGetFileContent() throws Exception { |
|||
// 准备参数
|
|||
Long configId = 10L; |
|||
String path = "tudou.jpg"; |
|||
// mock 方法
|
|||
FileClient client = mock(FileClient.class); |
|||
when(fileConfigService.getFileClient(eq(10L))).thenReturn(client); |
|||
byte[] content = new byte[]{}; |
|||
when(client.getContent(eq("tudou.jpg"))).thenReturn(content); |
|||
|
|||
// 调用
|
|||
byte[] result = fileService.getFileContent(configId, path); |
|||
// 断言
|
|||
assertSame(result, content); |
|||
} |
|||
|
|||
@Test |
|||
public void testGenerateUploadPath_AllEnabled() { |
|||
// 准备参数
|
|||
String name = "test.jpg"; |
|||
String directory = "avatar"; |
|||
FileServiceImpl.PATH_PREFIX_DATE_ENABLE = true; |
|||
FileServiceImpl.PATH_SUFFIX_TIMESTAMP_ENABLE = true; |
|||
|
|||
// 调用
|
|||
String path = fileService.generateUploadPath(name, directory); |
|||
|
|||
// 断言
|
|||
// 格式为:avatar/yyyyMMdd/test_timestamp.jpg
|
|||
assertTrue(path.startsWith(directory + "/")); |
|||
// 包含日期格式:8 位数字,如 20240517
|
|||
assertTrue(path.matches(directory + "/\\d{8}/test_\\d+\\.jpg")); |
|||
} |
|||
|
|||
@Test |
|||
public void testGenerateUploadPath_PrefixEnabled_SuffixDisabled() { |
|||
// 准备参数
|
|||
String name = "test.jpg"; |
|||
String directory = "avatar"; |
|||
FileServiceImpl.PATH_PREFIX_DATE_ENABLE = true; |
|||
FileServiceImpl.PATH_SUFFIX_TIMESTAMP_ENABLE = false; |
|||
|
|||
// 调用
|
|||
String path = fileService.generateUploadPath(name, directory); |
|||
|
|||
// 断言
|
|||
// 格式为:avatar/yyyyMMdd/test.jpg
|
|||
assertTrue(path.startsWith(directory + "/")); |
|||
// 包含日期格式:8 位数字,如 20240517
|
|||
assertTrue(path.matches(directory + "/\\d{8}/test\\.jpg")); |
|||
} |
|||
|
|||
@Test |
|||
public void testGenerateUploadPath_PrefixDisabled_SuffixEnabled() { |
|||
// 准备参数
|
|||
String name = "test.jpg"; |
|||
String directory = "avatar"; |
|||
FileServiceImpl.PATH_PREFIX_DATE_ENABLE = false; |
|||
FileServiceImpl.PATH_SUFFIX_TIMESTAMP_ENABLE = true; |
|||
|
|||
// 调用
|
|||
String path = fileService.generateUploadPath(name, directory); |
|||
|
|||
// 断言
|
|||
// 格式为:avatar/test_timestamp.jpg
|
|||
assertTrue(path.startsWith(directory + "/")); |
|||
assertTrue(path.matches(directory + "/test_\\d+\\.jpg")); |
|||
} |
|||
|
|||
@Test |
|||
public void testGenerateUploadPath_AllDisabled() { |
|||
// 准备参数
|
|||
String name = "test.jpg"; |
|||
String directory = "avatar"; |
|||
FileServiceImpl.PATH_PREFIX_DATE_ENABLE = false; |
|||
FileServiceImpl.PATH_SUFFIX_TIMESTAMP_ENABLE = false; |
|||
|
|||
// 调用
|
|||
String path = fileService.generateUploadPath(name, directory); |
|||
|
|||
// 断言
|
|||
// 格式为:avatar/test.jpg
|
|||
assertEquals(directory + "/" + name, path); |
|||
} |
|||
|
|||
@Test |
|||
public void testGenerateUploadPath_NoExtension() { |
|||
// 准备参数
|
|||
String name = "test"; |
|||
String directory = "avatar"; |
|||
FileServiceImpl.PATH_PREFIX_DATE_ENABLE = true; |
|||
FileServiceImpl.PATH_SUFFIX_TIMESTAMP_ENABLE = true; |
|||
|
|||
// 调用
|
|||
String path = fileService.generateUploadPath(name, directory); |
|||
|
|||
// 断言
|
|||
// 格式为:avatar/yyyyMMdd/test_timestamp
|
|||
assertTrue(path.startsWith(directory + "/")); |
|||
assertTrue(path.matches(directory + "/\\d{8}/test_\\d+")); |
|||
} |
|||
|
|||
@Test |
|||
public void testGenerateUploadPath_DirectoryNull() { |
|||
// 准备参数
|
|||
String name = "test.jpg"; |
|||
String directory = null; |
|||
FileServiceImpl.PATH_PREFIX_DATE_ENABLE = true; |
|||
FileServiceImpl.PATH_SUFFIX_TIMESTAMP_ENABLE = true; |
|||
|
|||
// 调用
|
|||
String path = fileService.generateUploadPath(name, directory); |
|||
|
|||
// 断言
|
|||
// 格式为:yyyyMMdd/test_timestamp.jpg
|
|||
assertTrue(path.matches("\\d{8}/test_\\d+\\.jpg")); |
|||
} |
|||
|
|||
@Test |
|||
public void testGenerateUploadPath_DirectoryEmpty() { |
|||
// 准备参数
|
|||
String name = "test.jpg"; |
|||
String directory = ""; |
|||
FileServiceImpl.PATH_PREFIX_DATE_ENABLE = true; |
|||
FileServiceImpl.PATH_SUFFIX_TIMESTAMP_ENABLE = true; |
|||
|
|||
// 调用
|
|||
String path = fileService.generateUploadPath(name, directory); |
|||
|
|||
// 断言
|
|||
// 格式为:yyyyMMdd/test_timestamp.jpg
|
|||
assertTrue(path.matches("\\d{8}/test_\\d+\\.jpg")); |
|||
} |
|||
|
|||
} |
@ -1,172 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.service.job; |
|||
|
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
|||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
|||
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.log.JobLogPageReqVO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.job.JobLogDO; |
|||
import cn.iocoder.yudao.module.infra.dal.mysql.job.JobLogMapper; |
|||
import cn.iocoder.yudao.module.infra.enums.job.JobLogStatusEnum; |
|||
import org.junit.jupiter.api.Test; |
|||
import org.springframework.context.annotation.Import; |
|||
|
|||
import jakarta.annotation.Resource; |
|||
import java.time.Duration; |
|||
import java.time.LocalDateTime; |
|||
import java.util.List; |
|||
|
|||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime; |
|||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; |
|||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; |
|||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; |
|||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; |
|||
import static org.junit.jupiter.api.Assertions.assertEquals; |
|||
import static org.junit.jupiter.api.Assertions.assertNotNull; |
|||
|
|||
@Import(JobLogServiceImpl.class) |
|||
public class JobLogServiceImplTest extends BaseDbUnitTest { |
|||
|
|||
@Resource |
|||
private JobLogServiceImpl jobLogService; |
|||
@Resource |
|||
private JobLogMapper jobLogMapper; |
|||
|
|||
@Test |
|||
public void testCreateJobLog() { |
|||
// 准备参数
|
|||
JobLogDO reqVO = randomPojo(JobLogDO.class, o -> o.setExecuteIndex(1)); |
|||
|
|||
// 调用
|
|||
Long id = jobLogService.createJobLog(reqVO.getJobId(), reqVO.getBeginTime(), |
|||
reqVO.getHandlerName(), reqVO.getHandlerParam(), reqVO.getExecuteIndex()); |
|||
// 断言
|
|||
assertNotNull(id); |
|||
// 校验记录的属性是否正确
|
|||
JobLogDO job = jobLogMapper.selectById(id); |
|||
assertEquals(JobLogStatusEnum.RUNNING.getStatus(), job.getStatus()); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateJobLogResultAsync_success() { |
|||
// mock 数据
|
|||
JobLogDO log = randomPojo(JobLogDO.class, o -> { |
|||
o.setExecuteIndex(1); |
|||
o.setStatus(JobLogStatusEnum.RUNNING.getStatus()); |
|||
}); |
|||
jobLogMapper.insert(log); |
|||
// 准备参数
|
|||
Long logId = log.getId(); |
|||
LocalDateTime endTime = randomLocalDateTime(); |
|||
Integer duration = randomInteger(); |
|||
boolean success = true; |
|||
String result = randomString(); |
|||
|
|||
// 调用
|
|||
jobLogService.updateJobLogResultAsync(logId, endTime, duration, success, result); |
|||
// 校验记录的属性是否正确
|
|||
JobLogDO dbLog = jobLogMapper.selectById(log.getId()); |
|||
assertEquals(endTime, dbLog.getEndTime()); |
|||
assertEquals(duration, dbLog.getDuration()); |
|||
assertEquals(JobLogStatusEnum.SUCCESS.getStatus(), dbLog.getStatus()); |
|||
assertEquals(result, dbLog.getResult()); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateJobLogResultAsync_failure() { |
|||
// mock 数据
|
|||
JobLogDO log = randomPojo(JobLogDO.class, o -> { |
|||
o.setExecuteIndex(1); |
|||
o.setStatus(JobLogStatusEnum.RUNNING.getStatus()); |
|||
}); |
|||
jobLogMapper.insert(log); |
|||
// 准备参数
|
|||
Long logId = log.getId(); |
|||
LocalDateTime endTime = randomLocalDateTime(); |
|||
Integer duration = randomInteger(); |
|||
boolean success = false; |
|||
String result = randomString(); |
|||
|
|||
// 调用
|
|||
jobLogService.updateJobLogResultAsync(logId, endTime, duration, success, result); |
|||
// 校验记录的属性是否正确
|
|||
JobLogDO dbLog = jobLogMapper.selectById(log.getId()); |
|||
assertEquals(endTime, dbLog.getEndTime()); |
|||
assertEquals(duration, dbLog.getDuration()); |
|||
assertEquals(JobLogStatusEnum.FAILURE.getStatus(), dbLog.getStatus()); |
|||
assertEquals(result, dbLog.getResult()); |
|||
} |
|||
|
|||
@Test |
|||
public void testCleanJobLog() { |
|||
// mock 数据
|
|||
JobLogDO log01 = randomPojo(JobLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-3)))) |
|||
.setExecuteIndex(1); |
|||
jobLogMapper.insert(log01); |
|||
JobLogDO log02 = randomPojo(JobLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-1)))) |
|||
.setExecuteIndex(1); |
|||
jobLogMapper.insert(log02); |
|||
// 准备参数
|
|||
Integer exceedDay = 2; |
|||
Integer deleteLimit = 1; |
|||
|
|||
// 调用
|
|||
Integer count = jobLogService.cleanJobLog(exceedDay, deleteLimit); |
|||
// 断言
|
|||
assertEquals(1, count); |
|||
List<JobLogDO> logs = jobLogMapper.selectList(); |
|||
assertEquals(1, logs.size()); |
|||
// TODO @芋艿:createTime updateTime 被屏蔽,仅 win11 会复现,建议后续修复。
|
|||
assertPojoEquals(log02, logs.get(0), "createTime", "updateTime"); |
|||
} |
|||
|
|||
@Test |
|||
public void testGetJobLog() { |
|||
// mock 数据
|
|||
JobLogDO dbJobLog = randomPojo(JobLogDO.class, o -> o.setExecuteIndex(1)); |
|||
jobLogMapper.insert(dbJobLog); |
|||
// 准备参数
|
|||
Long id = dbJobLog.getId(); |
|||
|
|||
// 调用
|
|||
JobLogDO jobLog = jobLogService.getJobLog(id); |
|||
// 断言
|
|||
assertPojoEquals(dbJobLog, jobLog); |
|||
} |
|||
|
|||
@Test |
|||
public void testGetJobPage() { |
|||
// mock 数据
|
|||
JobLogDO dbJobLog = randomPojo(JobLogDO.class, o -> { |
|||
o.setExecuteIndex(1); |
|||
o.setHandlerName("handlerName 单元测试"); |
|||
o.setStatus(JobLogStatusEnum.SUCCESS.getStatus()); |
|||
o.setBeginTime(buildTime(2021, 1, 8)); |
|||
o.setEndTime(buildTime(2021, 1, 8)); |
|||
}); |
|||
jobLogMapper.insert(dbJobLog); |
|||
// 测试 jobId 不匹配
|
|||
jobLogMapper.insert(cloneIgnoreId(dbJobLog, o -> o.setJobId(randomLongId()))); |
|||
// 测试 handlerName 不匹配
|
|||
jobLogMapper.insert(cloneIgnoreId(dbJobLog, o -> o.setHandlerName(randomString()))); |
|||
// 测试 beginTime 不匹配
|
|||
jobLogMapper.insert(cloneIgnoreId(dbJobLog, o -> o.setBeginTime(buildTime(2021, 1, 7)))); |
|||
// 测试 endTime 不匹配
|
|||
jobLogMapper.insert(cloneIgnoreId(dbJobLog, o -> o.setEndTime(buildTime(2021, 1, 9)))); |
|||
// 测试 status 不匹配
|
|||
jobLogMapper.insert(cloneIgnoreId(dbJobLog, o -> o.setStatus(JobLogStatusEnum.FAILURE.getStatus()))); |
|||
// 准备参数
|
|||
JobLogPageReqVO reqVo = new JobLogPageReqVO(); |
|||
reqVo.setJobId(dbJobLog.getJobId()); |
|||
reqVo.setHandlerName("单元"); |
|||
reqVo.setBeginTime(dbJobLog.getBeginTime()); |
|||
reqVo.setEndTime(dbJobLog.getEndTime()); |
|||
reqVo.setStatus(JobLogStatusEnum.SUCCESS.getStatus()); |
|||
|
|||
// 调用
|
|||
PageResult<JobLogDO> pageResult = jobLogService.getJobLogPage(reqVo); |
|||
// 断言
|
|||
assertEquals(1, pageResult.getTotal()); |
|||
assertEquals(1, pageResult.getList().size()); |
|||
assertPojoEquals(dbJobLog, pageResult.getList().get(0)); |
|||
} |
|||
|
|||
} |
@ -1,257 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.service.job; |
|||
|
|||
import cn.hutool.extra.spring.SpringUtil; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
|||
import cn.iocoder.yudao.framework.quartz.core.scheduler.SchedulerManager; |
|||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
|||
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobPageReqVO; |
|||
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobSaveReqVO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.job.JobDO; |
|||
import cn.iocoder.yudao.module.infra.dal.mysql.job.JobMapper; |
|||
import cn.iocoder.yudao.module.infra.enums.job.JobStatusEnum; |
|||
import cn.iocoder.yudao.module.infra.job.job.JobLogCleanJob; |
|||
import org.junit.jupiter.api.Test; |
|||
import org.mockito.MockedStatic; |
|||
import org.quartz.SchedulerException; |
|||
import org.springframework.boot.test.mock.mockito.MockBean; |
|||
import org.springframework.context.annotation.Import; |
|||
|
|||
import jakarta.annotation.Resource; |
|||
|
|||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; |
|||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; |
|||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; |
|||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
|||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; |
|||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; |
|||
import static org.junit.jupiter.api.Assertions.*; |
|||
import static org.mockito.ArgumentMatchers.eq; |
|||
import static org.mockito.Mockito.mockStatic; |
|||
import static org.mockito.Mockito.verify; |
|||
|
|||
@Import(JobServiceImpl.class) |
|||
public class JobServiceImplTest extends BaseDbUnitTest { |
|||
|
|||
@Resource |
|||
private JobServiceImpl jobService; |
|||
@Resource |
|||
private JobMapper jobMapper; |
|||
@MockBean |
|||
private SchedulerManager schedulerManager; |
|||
|
|||
@MockBean |
|||
private JobLogCleanJob jobLogCleanJob; |
|||
|
|||
@Test |
|||
public void testCreateJob_cronExpressionValid() { |
|||
// 准备参数。Cron 表达式为 String 类型,默认随机字符串。
|
|||
JobSaveReqVO reqVO = randomPojo(JobSaveReqVO.class); |
|||
|
|||
// 调用,并断言异常
|
|||
assertServiceException(() -> jobService.createJob(reqVO), JOB_CRON_EXPRESSION_VALID); |
|||
} |
|||
|
|||
@Test |
|||
public void testCreateJob_jobHandlerExists() throws SchedulerException { |
|||
// 准备参数 指定 Cron 表达式
|
|||
JobSaveReqVO reqVO = randomPojo(JobSaveReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *")); |
|||
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) { |
|||
springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(reqVO.getHandlerName()))) |
|||
.thenReturn(jobLogCleanJob); |
|||
|
|||
// 调用
|
|||
jobService.createJob(reqVO); |
|||
// 调用,并断言异常
|
|||
assertServiceException(() -> jobService.createJob(reqVO), JOB_HANDLER_EXISTS); |
|||
} |
|||
} |
|||
|
|||
@Test |
|||
public void testCreateJob_success() throws SchedulerException { |
|||
// 准备参数 指定 Cron 表达式
|
|||
JobSaveReqVO reqVO = randomPojo(JobSaveReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *")) |
|||
.setId(null); |
|||
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) { |
|||
springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(reqVO.getHandlerName()))) |
|||
.thenReturn(jobLogCleanJob); |
|||
|
|||
// 调用
|
|||
Long jobId = jobService.createJob(reqVO); |
|||
// 断言
|
|||
assertNotNull(jobId); |
|||
// 校验记录的属性是否正确
|
|||
JobDO job = jobMapper.selectById(jobId); |
|||
assertPojoEquals(reqVO, job, "id"); |
|||
assertEquals(JobStatusEnum.NORMAL.getStatus(), job.getStatus()); |
|||
// 校验调用
|
|||
verify(schedulerManager).addJob(eq(job.getId()), eq(job.getHandlerName()), eq(job.getHandlerParam()), |
|||
eq(job.getCronExpression()), eq(reqVO.getRetryCount()), eq(reqVO.getRetryInterval())); |
|||
} |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateJob_jobNotExists(){ |
|||
// 准备参数
|
|||
JobSaveReqVO reqVO = randomPojo(JobSaveReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *")); |
|||
|
|||
// 调用,并断言异常
|
|||
assertServiceException(() -> jobService.updateJob(reqVO), JOB_NOT_EXISTS); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateJob_onlyNormalStatus(){ |
|||
// mock 数据
|
|||
JobDO job = randomPojo(JobDO.class, o -> o.setStatus(JobStatusEnum.INIT.getStatus())); |
|||
jobMapper.insert(job); |
|||
// 准备参数
|
|||
JobSaveReqVO updateReqVO = randomPojo(JobSaveReqVO.class, o -> { |
|||
o.setId(job.getId()); |
|||
o.setCronExpression("0 0/1 * * * ? *"); |
|||
}); |
|||
|
|||
// 调用,并断言异常
|
|||
assertServiceException(() -> jobService.updateJob(updateReqVO), |
|||
JOB_UPDATE_ONLY_NORMAL_STATUS); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateJob_success() throws SchedulerException { |
|||
// mock 数据
|
|||
JobDO job = randomPojo(JobDO.class, o -> o.setStatus(JobStatusEnum.NORMAL.getStatus())); |
|||
jobMapper.insert(job); |
|||
// 准备参数
|
|||
JobSaveReqVO updateReqVO = randomPojo(JobSaveReqVO.class, o -> { |
|||
o.setId(job.getId()); |
|||
o.setCronExpression("0 0/1 * * * ? *"); |
|||
}); |
|||
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) { |
|||
springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(updateReqVO.getHandlerName()))) |
|||
.thenReturn(jobLogCleanJob); |
|||
|
|||
// 调用
|
|||
jobService.updateJob(updateReqVO); |
|||
// 校验记录的属性是否正确
|
|||
JobDO updateJob = jobMapper.selectById(updateReqVO.getId()); |
|||
assertPojoEquals(updateReqVO, updateJob); |
|||
// 校验调用
|
|||
verify(schedulerManager).updateJob(eq(job.getHandlerName()), eq(updateReqVO.getHandlerParam()), |
|||
eq(updateReqVO.getCronExpression()), eq(updateReqVO.getRetryCount()), eq(updateReqVO.getRetryInterval())); |
|||
} |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateJobStatus_changeStatusInvalid() { |
|||
// 调用,并断言异常
|
|||
assertServiceException(() -> jobService.updateJobStatus(1L, JobStatusEnum.INIT.getStatus()), |
|||
JOB_CHANGE_STATUS_INVALID); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateJobStatus_changeStatusEquals() { |
|||
// mock 数据
|
|||
JobDO job = randomPojo(JobDO.class, o -> o.setStatus(JobStatusEnum.NORMAL.getStatus())); |
|||
jobMapper.insert(job); |
|||
|
|||
// 调用,并断言异常
|
|||
assertServiceException(() -> jobService.updateJobStatus(job.getId(), job.getStatus()), |
|||
JOB_CHANGE_STATUS_EQUALS); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateJobStatus_stopSuccess() throws SchedulerException { |
|||
// mock 数据
|
|||
JobDO job = randomPojo(JobDO.class, o -> o.setStatus(JobStatusEnum.NORMAL.getStatus())); |
|||
jobMapper.insert(job); |
|||
|
|||
// 调用
|
|||
jobService.updateJobStatus(job.getId(), JobStatusEnum.STOP.getStatus()); |
|||
// 校验记录的属性是否正确
|
|||
JobDO dbJob = jobMapper.selectById(job.getId()); |
|||
assertEquals(JobStatusEnum.STOP.getStatus(), dbJob.getStatus()); |
|||
// 校验调用
|
|||
verify(schedulerManager).pauseJob(eq(job.getHandlerName())); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateJobStatus_normalSuccess() throws SchedulerException { |
|||
// mock 数据
|
|||
JobDO job = randomPojo(JobDO.class, o -> o.setStatus(JobStatusEnum.STOP.getStatus())); |
|||
jobMapper.insert(job); |
|||
|
|||
// 调用
|
|||
jobService.updateJobStatus(job.getId(), JobStatusEnum.NORMAL.getStatus()); |
|||
// 校验记录的属性是否正确
|
|||
JobDO dbJob = jobMapper.selectById(job.getId()); |
|||
assertEquals(JobStatusEnum.NORMAL.getStatus(), dbJob.getStatus()); |
|||
// 校验调用
|
|||
verify(schedulerManager).resumeJob(eq(job.getHandlerName())); |
|||
} |
|||
|
|||
@Test |
|||
public void testTriggerJob_success() throws SchedulerException { |
|||
// mock 数据
|
|||
JobDO job = randomPojo(JobDO.class); |
|||
jobMapper.insert(job); |
|||
|
|||
// 调用
|
|||
jobService.triggerJob(job.getId()); |
|||
// 校验调用
|
|||
verify(schedulerManager).triggerJob(eq(job.getId()), |
|||
eq(job.getHandlerName()), eq(job.getHandlerParam())); |
|||
} |
|||
|
|||
@Test |
|||
public void testDeleteJob_success() throws SchedulerException { |
|||
// mock 数据
|
|||
JobDO job = randomPojo(JobDO.class); |
|||
jobMapper.insert(job); |
|||
|
|||
// 调用
|
|||
jobService.deleteJob(job.getId()); |
|||
// 校验不存在
|
|||
assertNull(jobMapper.selectById(job.getId())); |
|||
// 校验调用
|
|||
verify(schedulerManager).deleteJob(eq(job.getHandlerName())); |
|||
} |
|||
|
|||
@Test |
|||
public void testGetJobPage() { |
|||
// mock 数据
|
|||
JobDO dbJob = randomPojo(JobDO.class, o -> { |
|||
o.setName("定时任务测试"); |
|||
o.setHandlerName("handlerName 单元测试"); |
|||
o.setStatus(JobStatusEnum.INIT.getStatus()); |
|||
}); |
|||
jobMapper.insert(dbJob); |
|||
// 测试 name 不匹配
|
|||
jobMapper.insert(cloneIgnoreId(dbJob, o -> o.setName("土豆"))); |
|||
// 测试 status 不匹配
|
|||
jobMapper.insert(cloneIgnoreId(dbJob, o -> o.setStatus(JobStatusEnum.NORMAL.getStatus()))); |
|||
// 测试 handlerName 不匹配
|
|||
jobMapper.insert(cloneIgnoreId(dbJob, o -> o.setHandlerName(randomString()))); |
|||
// 准备参数
|
|||
JobPageReqVO reqVo = new JobPageReqVO(); |
|||
reqVo.setName("定时"); |
|||
reqVo.setStatus(JobStatusEnum.INIT.getStatus()); |
|||
reqVo.setHandlerName("单元"); |
|||
|
|||
// 调用
|
|||
PageResult<JobDO> pageResult = jobService.getJobPage(reqVo); |
|||
// 断言
|
|||
assertEquals(1, pageResult.getTotal()); |
|||
assertEquals(1, pageResult.getList().size()); |
|||
assertPojoEquals(dbJob, pageResult.getList().get(0)); |
|||
} |
|||
|
|||
@Test |
|||
public void testGetJob() { |
|||
// mock 数据
|
|||
JobDO dbJob = randomPojo(JobDO.class); |
|||
jobMapper.insert(dbJob); |
|||
// 调用
|
|||
JobDO job = jobService.getJob(dbJob.getId()); |
|||
// 断言
|
|||
assertPojoEquals(dbJob, job); |
|||
} |
|||
|
|||
} |
@ -1,110 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.service.logger; |
|||
|
|||
import cn.iocoder.yudao.framework.common.biz.infra.logger.dto.ApiAccessLogCreateReqDTO; |
|||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; |
|||
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
|||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
|||
import cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogPageReqVO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiAccessLogDO; |
|||
import cn.iocoder.yudao.module.infra.dal.mysql.logger.ApiAccessLogMapper; |
|||
import jakarta.annotation.Resource; |
|||
import org.junit.jupiter.api.Test; |
|||
import org.springframework.context.annotation.Import; |
|||
|
|||
import java.time.Duration; |
|||
import java.util.List; |
|||
|
|||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; |
|||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; |
|||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; |
|||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
|||
import static org.junit.jupiter.api.Assertions.assertEquals; |
|||
|
|||
@Import(ApiAccessLogServiceImpl.class) |
|||
public class ApiAccessLogServiceImplTest extends BaseDbUnitTest { |
|||
|
|||
@Resource |
|||
private ApiAccessLogServiceImpl apiAccessLogService; |
|||
|
|||
@Resource |
|||
private ApiAccessLogMapper apiAccessLogMapper; |
|||
|
|||
@Test |
|||
public void testGetApiAccessLogPage() { |
|||
ApiAccessLogDO apiAccessLogDO = randomPojo(ApiAccessLogDO.class, o -> { |
|||
o.setUserId(2233L); |
|||
o.setUserType(UserTypeEnum.ADMIN.getValue()); |
|||
o.setApplicationName("yudao-test"); |
|||
o.setRequestUrl("foo"); |
|||
o.setBeginTime(buildTime(2021, 3, 13)); |
|||
o.setDuration(1000); |
|||
o.setResultCode(GlobalErrorCodeConstants.SUCCESS.getCode()); |
|||
}); |
|||
apiAccessLogMapper.insert(apiAccessLogDO); |
|||
// 测试 userId 不匹配
|
|||
apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setUserId(3344L))); |
|||
// 测试 userType 不匹配
|
|||
apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); |
|||
// 测试 applicationName 不匹配
|
|||
apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setApplicationName("test"))); |
|||
// 测试 requestUrl 不匹配
|
|||
apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setRequestUrl("bar"))); |
|||
// 测试 beginTime 不匹配:构造一个早期时间 2021-02-06 00:00:00
|
|||
apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setBeginTime(buildTime(2021, 2, 6)))); |
|||
// 测试 duration 不匹配
|
|||
apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setDuration(100))); |
|||
// 测试 resultCode 不匹配
|
|||
apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setResultCode(2))); |
|||
// 准备参数
|
|||
ApiAccessLogPageReqVO reqVO = new ApiAccessLogPageReqVO(); |
|||
reqVO.setUserId(2233L); |
|||
reqVO.setUserType(UserTypeEnum.ADMIN.getValue()); |
|||
reqVO.setApplicationName("yudao-test"); |
|||
reqVO.setRequestUrl("foo"); |
|||
reqVO.setBeginTime(buildBetweenTime(2021, 3, 13, 2021, 3, 13)); |
|||
reqVO.setDuration(1000); |
|||
reqVO.setResultCode(GlobalErrorCodeConstants.SUCCESS.getCode()); |
|||
|
|||
// 调用
|
|||
PageResult<ApiAccessLogDO> pageResult = apiAccessLogService.getApiAccessLogPage(reqVO); |
|||
// 断言,只查到了一条符合条件的
|
|||
assertEquals(1, pageResult.getTotal()); |
|||
assertEquals(1, pageResult.getList().size()); |
|||
assertPojoEquals(apiAccessLogDO, pageResult.getList().get(0)); |
|||
} |
|||
|
|||
@Test |
|||
public void testCleanJobLog() { |
|||
// mock 数据
|
|||
ApiAccessLogDO log01 = randomPojo(ApiAccessLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-3)))); |
|||
apiAccessLogMapper.insert(log01); |
|||
ApiAccessLogDO log02 = randomPojo(ApiAccessLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-1)))); |
|||
apiAccessLogMapper.insert(log02); |
|||
// 准备参数
|
|||
Integer exceedDay = 2; |
|||
Integer deleteLimit = 1; |
|||
|
|||
// 调用
|
|||
Integer count = apiAccessLogService.cleanAccessLog(exceedDay, deleteLimit); |
|||
// 断言
|
|||
assertEquals(1, count); |
|||
List<ApiAccessLogDO> logs = apiAccessLogMapper.selectList(); |
|||
assertEquals(1, logs.size()); |
|||
// TODO @芋艿:createTime updateTime 被屏蔽,仅 win11 会复现,建议后续修复。
|
|||
assertPojoEquals(log02, logs.get(0), "createTime", "updateTime"); |
|||
} |
|||
|
|||
@Test |
|||
public void testCreateApiAccessLog() { |
|||
// 准备参数
|
|||
ApiAccessLogCreateReqDTO createDTO = randomPojo(ApiAccessLogCreateReqDTO.class); |
|||
|
|||
// 调用
|
|||
apiAccessLogService.createApiAccessLog(createDTO); |
|||
// 断言
|
|||
ApiAccessLogDO apiAccessLogDO = apiAccessLogMapper.selectOne(null); |
|||
assertPojoEquals(createDTO, apiAccessLogDO); |
|||
} |
|||
|
|||
} |
@ -1,164 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.service.logger; |
|||
|
|||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
|||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
|||
import cn.iocoder.yudao.framework.common.biz.infra.logger.dto.ApiErrorLogCreateReqDTO; |
|||
import cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apierrorlog.ApiErrorLogPageReqVO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiErrorLogDO; |
|||
import cn.iocoder.yudao.module.infra.dal.mysql.logger.ApiErrorLogMapper; |
|||
import cn.iocoder.yudao.module.infra.enums.logger.ApiErrorLogProcessStatusEnum; |
|||
import org.junit.jupiter.api.Test; |
|||
import org.springframework.context.annotation.Import; |
|||
|
|||
import jakarta.annotation.Resource; |
|||
import java.time.Duration; |
|||
import java.util.List; |
|||
|
|||
import static cn.hutool.core.util.RandomUtil.randomEle; |
|||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; |
|||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; |
|||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; |
|||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; |
|||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; |
|||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
|||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.API_ERROR_LOG_NOT_FOUND; |
|||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.API_ERROR_LOG_PROCESSED; |
|||
import static org.junit.jupiter.api.Assertions.assertEquals; |
|||
import static org.junit.jupiter.api.Assertions.assertNotNull; |
|||
|
|||
@Import(ApiErrorLogServiceImpl.class) |
|||
public class ApiErrorLogServiceImplTest extends BaseDbUnitTest { |
|||
|
|||
@Resource |
|||
private ApiErrorLogServiceImpl apiErrorLogService; |
|||
|
|||
@Resource |
|||
private ApiErrorLogMapper apiErrorLogMapper; |
|||
|
|||
@Test |
|||
public void testGetApiErrorLogPage() { |
|||
// mock 数据
|
|||
ApiErrorLogDO apiErrorLogDO = randomPojo(ApiErrorLogDO.class, o -> { |
|||
o.setUserId(2233L); |
|||
o.setUserType(UserTypeEnum.ADMIN.getValue()); |
|||
o.setApplicationName("yudao-test"); |
|||
o.setRequestUrl("foo"); |
|||
o.setExceptionTime(buildTime(2021, 3, 13)); |
|||
o.setProcessStatus(ApiErrorLogProcessStatusEnum.INIT.getStatus()); |
|||
}); |
|||
apiErrorLogMapper.insert(apiErrorLogDO); |
|||
// 测试 userId 不匹配
|
|||
apiErrorLogMapper.insert(cloneIgnoreId(apiErrorLogDO, o -> o.setUserId(3344L))); |
|||
// 测试 userType 不匹配
|
|||
apiErrorLogMapper.insert(cloneIgnoreId(apiErrorLogDO, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); |
|||
// 测试 applicationName 不匹配
|
|||
apiErrorLogMapper.insert(cloneIgnoreId(apiErrorLogDO, o -> o.setApplicationName("test"))); |
|||
// 测试 requestUrl 不匹配
|
|||
apiErrorLogMapper.insert(cloneIgnoreId(apiErrorLogDO, o -> o.setRequestUrl("bar"))); |
|||
// 测试 exceptionTime 不匹配:构造一个早期时间 2021-02-06 00:00:00
|
|||
apiErrorLogMapper.insert(cloneIgnoreId(apiErrorLogDO, o -> o.setExceptionTime(buildTime(2021, 2, 6)))); |
|||
// 测试 progressStatus 不匹配
|
|||
apiErrorLogMapper.insert(cloneIgnoreId(apiErrorLogDO, logDO -> logDO.setProcessStatus(ApiErrorLogProcessStatusEnum.DONE.getStatus()))); |
|||
// 准备参数
|
|||
ApiErrorLogPageReqVO reqVO = new ApiErrorLogPageReqVO(); |
|||
reqVO.setUserId(2233L); |
|||
reqVO.setUserType(UserTypeEnum.ADMIN.getValue()); |
|||
reqVO.setApplicationName("yudao-test"); |
|||
reqVO.setRequestUrl("foo"); |
|||
reqVO.setExceptionTime(buildBetweenTime(2021, 3, 1, 2021, 3, 31)); |
|||
reqVO.setProcessStatus(ApiErrorLogProcessStatusEnum.INIT.getStatus()); |
|||
|
|||
// 调用
|
|||
PageResult<ApiErrorLogDO> pageResult = apiErrorLogService.getApiErrorLogPage(reqVO); |
|||
// 断言,只查到了一条符合条件的
|
|||
assertEquals(1, pageResult.getTotal()); |
|||
assertEquals(1, pageResult.getList().size()); |
|||
assertPojoEquals(apiErrorLogDO, pageResult.getList().get(0)); |
|||
} |
|||
|
|||
@Test |
|||
public void testCreateApiErrorLog() { |
|||
// 准备参数
|
|||
ApiErrorLogCreateReqDTO createDTO = randomPojo(ApiErrorLogCreateReqDTO.class); |
|||
|
|||
// 调用
|
|||
apiErrorLogService.createApiErrorLog(createDTO); |
|||
// 断言
|
|||
ApiErrorLogDO apiErrorLogDO = apiErrorLogMapper.selectOne(null); |
|||
assertPojoEquals(createDTO, apiErrorLogDO); |
|||
assertEquals(ApiErrorLogProcessStatusEnum.INIT.getStatus(), apiErrorLogDO.getProcessStatus()); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateApiErrorLogProcess_success() { |
|||
// 准备参数
|
|||
ApiErrorLogDO apiErrorLogDO = randomPojo(ApiErrorLogDO.class, |
|||
o -> o.setProcessStatus(ApiErrorLogProcessStatusEnum.INIT.getStatus())); |
|||
apiErrorLogMapper.insert(apiErrorLogDO); |
|||
// 准备参数
|
|||
Long id = apiErrorLogDO.getId(); |
|||
Integer processStatus = randomEle(ApiErrorLogProcessStatusEnum.values()).getStatus(); |
|||
Long processUserId = randomLongId(); |
|||
|
|||
// 调用
|
|||
apiErrorLogService.updateApiErrorLogProcess(id, processStatus, processUserId); |
|||
// 断言
|
|||
ApiErrorLogDO dbApiErrorLogDO = apiErrorLogMapper.selectById(apiErrorLogDO.getId()); |
|||
assertEquals(processStatus, dbApiErrorLogDO.getProcessStatus()); |
|||
assertEquals(processUserId, dbApiErrorLogDO.getProcessUserId()); |
|||
assertNotNull(dbApiErrorLogDO.getProcessTime()); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateApiErrorLogProcess_processed() { |
|||
// 准备参数
|
|||
ApiErrorLogDO apiErrorLogDO = randomPojo(ApiErrorLogDO.class, |
|||
o -> o.setProcessStatus(ApiErrorLogProcessStatusEnum.DONE.getStatus())); |
|||
apiErrorLogMapper.insert(apiErrorLogDO); |
|||
// 准备参数
|
|||
Long id = apiErrorLogDO.getId(); |
|||
Integer processStatus = randomEle(ApiErrorLogProcessStatusEnum.values()).getStatus(); |
|||
Long processUserId = randomLongId(); |
|||
|
|||
// 调用,并断言异常
|
|||
assertServiceException(() -> |
|||
apiErrorLogService.updateApiErrorLogProcess(id, processStatus, processUserId), |
|||
API_ERROR_LOG_PROCESSED); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateApiErrorLogProcess_notFound() { |
|||
// 准备参数
|
|||
Long id = randomLongId(); |
|||
Integer processStatus = randomEle(ApiErrorLogProcessStatusEnum.values()).getStatus(); |
|||
Long processUserId = randomLongId(); |
|||
|
|||
// 调用,并断言异常
|
|||
assertServiceException(() -> |
|||
apiErrorLogService.updateApiErrorLogProcess(id, processStatus, processUserId), |
|||
API_ERROR_LOG_NOT_FOUND); |
|||
} |
|||
|
|||
@Test |
|||
public void testCleanJobLog() { |
|||
// mock 数据
|
|||
ApiErrorLogDO log01 = randomPojo(ApiErrorLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-3)))); |
|||
apiErrorLogMapper.insert(log01); |
|||
ApiErrorLogDO log02 = randomPojo(ApiErrorLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-1)))); |
|||
apiErrorLogMapper.insert(log02); |
|||
// 准备参数
|
|||
Integer exceedDay = 2; |
|||
Integer deleteLimit = 1; |
|||
|
|||
// 调用
|
|||
Integer count = apiErrorLogService.cleanErrorLog(exceedDay, deleteLimit); |
|||
// 断言
|
|||
assertEquals(1, count); |
|||
List<ApiErrorLogDO> logs = apiErrorLogMapper.selectList(); |
|||
assertEquals(1, logs.size()); |
|||
// TODO @芋艿:createTime updateTime 被屏蔽,仅 win11 会复现,建议后续修复。
|
|||
assertPojoEquals(log02, logs.get(0), "createTime", "updateTime"); |
|||
} |
|||
|
|||
} |
@ -1,50 +0,0 @@ |
|||
spring: |
|||
main: |
|||
lazy-initialization: true # 开启懒加载,加快速度 |
|||
banner-mode: off # 单元测试,禁用 Banner |
|||
|
|||
--- #################### 数据库相关配置 #################### |
|||
|
|||
spring: |
|||
# 数据源配置项 |
|||
datasource: |
|||
name: ruoyi-vue-pro |
|||
url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写 |
|||
driver-class-name: org.h2.Driver |
|||
username: sa |
|||
password: |
|||
druid: |
|||
async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度 |
|||
initial-size: 1 # 单元测试,配置为 1,提升启动速度 |
|||
sql: |
|||
init: |
|||
schema-locations: classpath:/sql/create_tables.sql |
|||
encoding: UTF-8 |
|||
|
|||
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 |
|||
data: |
|||
redis: |
|||
host: 127.0.0.1 # 地址 |
|||
port: 16379 # 端口(单元测试,使用 16379 端口) |
|||
database: 0 # 数据库索引 |
|||
|
|||
mybatis-plus: |
|||
lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试 |
|||
type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject |
|||
|
|||
--- #################### 定时任务相关配置 #################### |
|||
|
|||
--- #################### 配置中心相关配置 #################### |
|||
|
|||
--- #################### 服务保障相关配置 #################### |
|||
|
|||
# Lock4j 配置项(单元测试,禁用 Lock4j) |
|||
|
|||
--- #################### 监控相关配置 #################### |
|||
|
|||
--- #################### 芋道相关配置 #################### |
|||
|
|||
# 芋道配置项,设置当前项目所有自定义的配置 |
|||
yudao: |
|||
info: |
|||
base-package: cn.iocoder.yudao |
@ -1,52 +0,0 @@ |
|||
{ |
|||
"table": { |
|||
"id": 10, |
|||
"scene" : 1, |
|||
"parentMenuId" : 888, |
|||
"tableName" : "infra_category", |
|||
"tableComment" : "分类表", |
|||
"moduleName" : "infra", |
|||
"businessName" : "demo", |
|||
"className" : "InfraCategory", |
|||
"classComment" : "分类", |
|||
"author" : "芋道源码", |
|||
"treeParentColumnId" : 22, |
|||
"treeNameColumnId" : 11 |
|||
}, |
|||
"columns": [ { |
|||
"columnName" : "id", |
|||
"dataType" : "BIGINT", |
|||
"columnComment" : "编号", |
|||
"primaryKey" : true, |
|||
"javaType" : "Long", |
|||
"javaField" : "id", |
|||
"example" : "1024", |
|||
"updateOperation" : true, |
|||
"listOperationResult" : true |
|||
}, { |
|||
"id" : 11, |
|||
"columnName" : "name", |
|||
"dataType" : "VARCHAR", |
|||
"columnComment" : "名字", |
|||
"javaType" : "String", |
|||
"javaField" : "name", |
|||
"example" : "芋头", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperation" : true, |
|||
"listOperationCondition" : "LIKE", |
|||
"listOperationResult" : true, |
|||
"htmlType" : "input" |
|||
}, { |
|||
"id" : 22, |
|||
"columnName" : "description", |
|||
"dataType" : "VARCHAR", |
|||
"columnComment" : "父编号", |
|||
"javaType" : "Long", |
|||
"javaField" : "parentId", |
|||
"example" : "2048", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperationResult" : true |
|||
} ] |
|||
} |
@ -1,143 +0,0 @@ |
|||
{ |
|||
"table": { |
|||
"scene" : 1, |
|||
"tableName" : "infra_student_contact", |
|||
"tableComment" : "学生联系人表", |
|||
"moduleName" : "infra", |
|||
"businessName" : "demo", |
|||
"className" : "InfraStudentContact", |
|||
"classComment" : "学生联系人", |
|||
"author" : "芋道源码" |
|||
}, |
|||
"columns": [ { |
|||
"columnName" : "id", |
|||
"dataType" : "BIGINT", |
|||
"columnComment" : "编号", |
|||
"primaryKey" : true, |
|||
"javaType" : "Long", |
|||
"javaField" : "id", |
|||
"example" : "1024", |
|||
"updateOperation" : true, |
|||
"listOperationResult" : true |
|||
}, { |
|||
"id" : 100, |
|||
"columnName" : "student_id", |
|||
"dataType" : "BIGINT", |
|||
"columnComment" : "学生编号", |
|||
"javaType" : "Long", |
|||
"javaField" : "studentId", |
|||
"example" : "2048", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperationResult" : true |
|||
}, { |
|||
"columnName" : "name", |
|||
"dataType" : "VARCHAR", |
|||
"columnComment" : "名字", |
|||
"javaType" : "String", |
|||
"javaField" : "name", |
|||
"example" : "芋头", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperation" : true, |
|||
"listOperationCondition" : "LIKE", |
|||
"listOperationResult" : true, |
|||
"htmlType" : "input" |
|||
}, { |
|||
"columnName" : "description", |
|||
"dataType" : "VARCHAR", |
|||
"columnComment" : "简介", |
|||
"javaType" : "String", |
|||
"javaField" : "description", |
|||
"example" : "我是介绍", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperationResult" : true, |
|||
"htmlType" : "textarea" |
|||
}, { |
|||
"columnName" : "birthday", |
|||
"dataType" : "DATE", |
|||
"columnComment" : "出生日期", |
|||
"javaType" : "LocalDateTime", |
|||
"javaField" : "birthday", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperation" : true, |
|||
"listOperationCondition" : "=", |
|||
"listOperationResult" : true, |
|||
"htmlType" : "datetime" |
|||
}, { |
|||
"columnName" : "sex", |
|||
"dataType" : "INTEGER", |
|||
"columnComment" : "性别", |
|||
"javaType" : "Integer", |
|||
"javaField" : "sex", |
|||
"dictType" : "system_user_sex", |
|||
"example" : "1", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperation" : true, |
|||
"listOperationCondition" : "=", |
|||
"listOperationResult" : true, |
|||
"htmlType" : "select" |
|||
}, { |
|||
"columnName" : "enabled", |
|||
"dataType" : "BOOLEAN", |
|||
"columnComment" : "是否有效", |
|||
"javaType" : "Boolean", |
|||
"javaField" : "enabled", |
|||
"dictType" : "infra_boolean_string", |
|||
"example" : "true", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperation" : true, |
|||
"listOperationCondition" : "=", |
|||
"listOperationResult" : true, |
|||
"htmlType" : "radio" |
|||
}, { |
|||
"columnName" : "avatar", |
|||
"dataType" : "VARCHAR", |
|||
"columnComment" : "头像", |
|||
"javaType" : "String", |
|||
"javaField" : "avatar", |
|||
"example" : "https://www.iocoder.cn/1.png", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperationResult" : true, |
|||
"htmlType" : "imageUpload" |
|||
}, { |
|||
"columnName" : "video", |
|||
"dataType" : "VARCHAR", |
|||
"columnComment" : "附件", |
|||
"nullable" : true, |
|||
"javaType" : "String", |
|||
"javaField" : "video", |
|||
"example" : "https://www.iocoder.cn/1.mp4", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperationResult" : true, |
|||
"htmlType" : "fileUpload" |
|||
}, { |
|||
"columnName" : "memo", |
|||
"dataType" : "VARCHAR", |
|||
"columnComment" : "备注", |
|||
"javaType" : "String", |
|||
"javaField" : "memo", |
|||
"example" : "我是备注", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperationResult" : true, |
|||
"htmlType" : "editor" |
|||
}, { |
|||
"columnName" : "create_time", |
|||
"dataType" : "DATE", |
|||
"columnComment" : "创建时间", |
|||
"nullable" : true, |
|||
"javaType" : "LocalDateTime", |
|||
"javaField" : "createTime", |
|||
"listOperation" : true, |
|||
"listOperationCondition" : "BETWEEN", |
|||
"listOperationResult" : true, |
|||
"htmlType" : "datetime" |
|||
} ] |
|||
} |
@ -1,134 +0,0 @@ |
|||
{ |
|||
"table": { |
|||
"id": 1, |
|||
"scene" : 1, |
|||
"parentMenuId" : 888, |
|||
"tableName" : "infra_student", |
|||
"tableComment" : "学生表", |
|||
"moduleName" : "infra", |
|||
"businessName" : "demo", |
|||
"className" : "InfraStudent", |
|||
"classComment" : "学生", |
|||
"author" : "芋道源码" |
|||
}, |
|||
"columns": [ { |
|||
"id" : 100, |
|||
"columnName" : "id", |
|||
"dataType" : "BIGINT", |
|||
"columnComment" : "编号", |
|||
"primaryKey" : true, |
|||
"javaType" : "Long", |
|||
"javaField" : "id", |
|||
"example" : "1024", |
|||
"updateOperation" : true, |
|||
"listOperationResult" : true |
|||
}, { |
|||
"columnName" : "name", |
|||
"dataType" : "VARCHAR", |
|||
"columnComment" : "名字", |
|||
"javaType" : "String", |
|||
"javaField" : "name", |
|||
"example" : "芋头", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperation" : true, |
|||
"listOperationCondition" : "LIKE", |
|||
"listOperationResult" : true, |
|||
"htmlType" : "input" |
|||
}, { |
|||
"columnName" : "description", |
|||
"dataType" : "VARCHAR", |
|||
"columnComment" : "简介", |
|||
"javaType" : "String", |
|||
"javaField" : "description", |
|||
"example" : "我是介绍", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperationResult" : true, |
|||
"htmlType" : "textarea" |
|||
}, { |
|||
"columnName" : "birthday", |
|||
"dataType" : "DATE", |
|||
"columnComment" : "出生日期", |
|||
"javaType" : "LocalDateTime", |
|||
"javaField" : "birthday", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperation" : true, |
|||
"listOperationCondition" : "=", |
|||
"listOperationResult" : true, |
|||
"htmlType" : "datetime" |
|||
}, { |
|||
"columnName" : "sex", |
|||
"dataType" : "INTEGER", |
|||
"columnComment" : "性别", |
|||
"javaType" : "Integer", |
|||
"javaField" : "sex", |
|||
"dictType" : "system_user_sex", |
|||
"example" : "1", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperation" : true, |
|||
"listOperationCondition" : "=", |
|||
"listOperationResult" : true, |
|||
"htmlType" : "select" |
|||
}, { |
|||
"columnName" : "enabled", |
|||
"dataType" : "BOOLEAN", |
|||
"columnComment" : "是否有效", |
|||
"javaType" : "Boolean", |
|||
"javaField" : "enabled", |
|||
"dictType" : "infra_boolean_string", |
|||
"example" : "true", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperation" : true, |
|||
"listOperationCondition" : "=", |
|||
"listOperationResult" : true, |
|||
"htmlType" : "radio" |
|||
}, { |
|||
"columnName" : "avatar", |
|||
"dataType" : "VARCHAR", |
|||
"columnComment" : "头像", |
|||
"javaType" : "String", |
|||
"javaField" : "avatar", |
|||
"example" : "https://www.iocoder.cn/1.png", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperationResult" : true, |
|||
"htmlType" : "imageUpload" |
|||
}, { |
|||
"columnName" : "video", |
|||
"dataType" : "VARCHAR", |
|||
"columnComment" : "附件", |
|||
"javaType" : "String", |
|||
"javaField" : "video", |
|||
"example" : "https://www.iocoder.cn/1.mp4", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperationResult" : true, |
|||
"htmlType" : "fileUpload" |
|||
}, { |
|||
"columnName" : "memo", |
|||
"dataType" : "VARCHAR", |
|||
"columnComment" : "备注", |
|||
"javaType" : "String", |
|||
"javaField" : "memo", |
|||
"example" : "我是备注", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperationResult" : true, |
|||
"htmlType" : "editor" |
|||
}, { |
|||
"columnName" : "create_time", |
|||
"dataType" : "DATE", |
|||
"columnComment" : "创建时间", |
|||
"nullable" : true, |
|||
"javaType" : "LocalDateTime", |
|||
"javaField" : "createTime", |
|||
"listOperation" : true, |
|||
"listOperationCondition" : "BETWEEN", |
|||
"listOperationResult" : true, |
|||
"htmlType" : "datetime" |
|||
} ] |
|||
} |
@ -1,143 +0,0 @@ |
|||
{ |
|||
"table": { |
|||
"scene" : 1, |
|||
"tableName" : "infra_student_teacher", |
|||
"tableComment" : "学生班主任表", |
|||
"moduleName" : "infra", |
|||
"businessName" : "demo", |
|||
"className" : "InfraStudentTeacher", |
|||
"classComment" : "学生班主任", |
|||
"author" : "芋道源码" |
|||
}, |
|||
"columns": [ { |
|||
"columnName" : "id", |
|||
"dataType" : "BIGINT", |
|||
"columnComment" : "编号", |
|||
"primaryKey" : true, |
|||
"javaType" : "Long", |
|||
"javaField" : "id", |
|||
"example" : "1024", |
|||
"updateOperation" : true, |
|||
"listOperationResult" : true |
|||
}, { |
|||
"id" : 200, |
|||
"columnName" : "student_id", |
|||
"dataType" : "BIGINT", |
|||
"columnComment" : "学生编号", |
|||
"javaType" : "Long", |
|||
"javaField" : "studentId", |
|||
"example" : "2048", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperationResult" : true |
|||
}, { |
|||
"columnName" : "name", |
|||
"dataType" : "VARCHAR", |
|||
"columnComment" : "名字", |
|||
"javaType" : "String", |
|||
"javaField" : "name", |
|||
"example" : "芋头", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperation" : true, |
|||
"listOperationCondition" : "LIKE", |
|||
"listOperationResult" : true, |
|||
"htmlType" : "input" |
|||
}, { |
|||
"columnName" : "description", |
|||
"dataType" : "VARCHAR", |
|||
"columnComment" : "简介", |
|||
"javaType" : "String", |
|||
"javaField" : "description", |
|||
"example" : "我是介绍", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperationResult" : true, |
|||
"htmlType" : "textarea" |
|||
}, { |
|||
"columnName" : "birthday", |
|||
"dataType" : "DATE", |
|||
"columnComment" : "出生日期", |
|||
"javaType" : "LocalDateTime", |
|||
"javaField" : "birthday", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperation" : true, |
|||
"listOperationCondition" : "=", |
|||
"listOperationResult" : true, |
|||
"htmlType" : "datetime" |
|||
}, { |
|||
"columnName" : "sex", |
|||
"dataType" : "INTEGER", |
|||
"columnComment" : "性别", |
|||
"javaType" : "Integer", |
|||
"javaField" : "sex", |
|||
"dictType" : "system_user_sex", |
|||
"example" : "1", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperation" : true, |
|||
"listOperationCondition" : "=", |
|||
"listOperationResult" : true, |
|||
"htmlType" : "select" |
|||
}, { |
|||
"columnName" : "enabled", |
|||
"dataType" : "BOOLEAN", |
|||
"columnComment" : "是否有效", |
|||
"javaType" : "Boolean", |
|||
"javaField" : "enabled", |
|||
"dictType" : "infra_boolean_string", |
|||
"example" : "true", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperation" : true, |
|||
"listOperationCondition" : "=", |
|||
"listOperationResult" : true, |
|||
"htmlType" : "radio" |
|||
}, { |
|||
"columnName" : "avatar", |
|||
"dataType" : "VARCHAR", |
|||
"columnComment" : "头像", |
|||
"javaType" : "String", |
|||
"javaField" : "avatar", |
|||
"example" : "https://www.iocoder.cn/1.png", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperationResult" : true, |
|||
"htmlType" : "imageUpload" |
|||
}, { |
|||
"columnName" : "video", |
|||
"dataType" : "VARCHAR", |
|||
"columnComment" : "附件", |
|||
"nullable" : true, |
|||
"javaType" : "String", |
|||
"javaField" : "video", |
|||
"example" : "https://www.iocoder.cn/1.mp4", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperationResult" : true, |
|||
"htmlType" : "fileUpload" |
|||
}, { |
|||
"columnName" : "memo", |
|||
"dataType" : "VARCHAR", |
|||
"columnComment" : "备注", |
|||
"javaType" : "String", |
|||
"javaField" : "memo", |
|||
"example" : "我是备注", |
|||
"createOperation" : true, |
|||
"updateOperation" : true, |
|||
"listOperationResult" : true, |
|||
"htmlType" : "editor" |
|||
}, { |
|||
"columnName" : "create_time", |
|||
"dataType" : "DATE", |
|||
"columnComment" : "创建时间", |
|||
"nullable" : true, |
|||
"javaType" : "LocalDateTime", |
|||
"javaField" : "createTime", |
|||
"listOperation" : true, |
|||
"listOperationCondition" : "BETWEEN", |
|||
"listOperationResult" : true, |
|||
"htmlType" : "datetime" |
|||
} ] |
|||
} |
@ -1,73 +0,0 @@ |
|||
[ { |
|||
"contentPath" : "java/InfraStudentPageReqVO", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentRespVO", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentSaveReqVO", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentController", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentDO", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentContactDO", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentTeacherDO", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentMapper", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentContactMapper", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentTeacherMapper", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java" |
|||
}, { |
|||
"contentPath" : "xml/InfraStudentMapper", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentServiceImpl", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentService", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentServiceImplTest", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java" |
|||
}, { |
|||
"contentPath" : "java/ErrorCodeConstants_手动操作", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java" |
|||
}, { |
|||
"contentPath" : "sql/sql", |
|||
"filePath" : "sql/sql.sql" |
|||
}, { |
|||
"contentPath" : "sql/h2", |
|||
"filePath" : "sql/h2.sql" |
|||
}, { |
|||
"contentPath" : "vue/index", |
|||
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue" |
|||
}, { |
|||
"contentPath": "js/index", |
|||
"filePath": "yudao-ui-admin-vue2/src/api/infra/demo/index.js" |
|||
}, { |
|||
"contentPath" : "vue/StudentForm", |
|||
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/StudentForm.vue" |
|||
}, { |
|||
"contentPath" : "vue/StudentContactForm", |
|||
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactForm.vue" |
|||
}, { |
|||
"contentPath" : "vue/StudentTeacherForm", |
|||
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherForm.vue" |
|||
}, { |
|||
"contentPath" : "vue/StudentContactList", |
|||
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactList.vue" |
|||
}, { |
|||
"contentPath" : "vue/StudentTeacherList", |
|||
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherList.vue" |
|||
} ] |
@ -1,6 +0,0 @@ |
|||
// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!! |
|||
// ========== 学生 TODO 补充编号 ========== |
|||
ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在"); |
|||
ErrorCode STUDENT_CONTACT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生联系人不存在"); |
|||
ErrorCode STUDENT_TEACHER_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生班主任不存在"); |
|||
ErrorCode STUDENT_TEACHER_EXISTS = new ErrorCode(TODO 补充编号, "学生班主任已存在"); |
@ -1,71 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.dal.dataobject.demo; |
|||
|
|||
import lombok.*; |
|||
import java.util.*; |
|||
import java.time.LocalDateTime; |
|||
import java.time.LocalDateTime; |
|||
import com.baomidou.mybatisplus.annotation.*; |
|||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
|||
|
|||
/** |
|||
* 学生联系人 DO |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@TableName("infra_student_contact") |
|||
@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 |
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
@Builder |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class InfraStudentContactDO extends BaseDO { |
|||
|
|||
/** |
|||
* 编号 |
|||
*/ |
|||
@TableId |
|||
private Long id; |
|||
/** |
|||
* 学生编号 |
|||
*/ |
|||
private Long studentId; |
|||
/** |
|||
* 名字 |
|||
*/ |
|||
private String name; |
|||
/** |
|||
* 简介 |
|||
*/ |
|||
private String description; |
|||
/** |
|||
* 出生日期 |
|||
*/ |
|||
private LocalDateTime birthday; |
|||
/** |
|||
* 性别 |
|||
* |
|||
* 枚举 {@link TODO system_user_sex 对应的类} |
|||
*/ |
|||
private Integer sex; |
|||
/** |
|||
* 是否有效 |
|||
* |
|||
* 枚举 {@link TODO infra_boolean_string 对应的类} |
|||
*/ |
|||
private Boolean enabled; |
|||
/** |
|||
* 头像 |
|||
*/ |
|||
private String avatar; |
|||
/** |
|||
* 附件 |
|||
*/ |
|||
private String video; |
|||
/** |
|||
* 备注 |
|||
*/ |
|||
private String memo; |
|||
|
|||
} |
@ -1,30 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.dal.mysql.demo; |
|||
|
|||
import java.util.*; |
|||
|
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageParam; |
|||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
|||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; |
|||
import org.apache.ibatis.annotations.Mapper; |
|||
|
|||
/** |
|||
* 学生联系人 Mapper |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@Mapper |
|||
public interface InfraStudentContactMapper extends BaseMapperX<InfraStudentContactDO> { |
|||
|
|||
default PageResult<InfraStudentContactDO> selectPage(PageParam reqVO, Long studentId) { |
|||
return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentContactDO>() |
|||
.eq(InfraStudentContactDO::getStudentId, studentId) |
|||
.orderByDesc(InfraStudentContactDO::getId)); |
|||
} |
|||
|
|||
default int deleteByStudentId(Long studentId) { |
|||
return delete(InfraStudentContactDO::getStudentId, studentId); |
|||
} |
|||
|
|||
} |
@ -1,183 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.controller.admin.demo; |
|||
|
|||
import org.springframework.web.bind.annotation.*; |
|||
import javax.annotation.Resource; |
|||
import org.springframework.validation.annotation.Validated; |
|||
import org.springframework.security.access.prepost.PreAuthorize; |
|||
import io.swagger.v3.oas.annotations.tags.Tag; |
|||
import io.swagger.v3.oas.annotations.Parameter; |
|||
import io.swagger.v3.oas.annotations.Operation; |
|||
|
|||
import javax.validation.constraints.*; |
|||
import javax.validation.*; |
|||
import javax.servlet.http.*; |
|||
import java.util.*; |
|||
import java.io.IOException; |
|||
|
|||
import cn.iocoder.yudao.framework.common.pojo.PageParam; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
|||
import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
|||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
|||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
|||
|
|||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; |
|||
|
|||
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; |
|||
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; |
|||
|
|||
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; |
|||
import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService; |
|||
|
|||
@Tag(name = "管理后台 - 学生") |
|||
@RestController |
|||
@RequestMapping("/infra/student") |
|||
@Validated |
|||
public class InfraStudentController { |
|||
|
|||
@Resource |
|||
private InfraStudentService studentService; |
|||
|
|||
@PostMapping("/create") |
|||
@Operation(summary = "创建学生") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:create')") |
|||
public CommonResult<Long> createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) { |
|||
return success(studentService.createStudent(createReqVO)); |
|||
} |
|||
|
|||
@PutMapping("/update") |
|||
@Operation(summary = "更新学生") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:update')") |
|||
public CommonResult<Boolean> updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) { |
|||
studentService.updateStudent(updateReqVO); |
|||
return success(true); |
|||
} |
|||
|
|||
@DeleteMapping("/delete") |
|||
@Operation(summary = "删除学生") |
|||
@Parameter(name = "id", description = "编号", required = true) |
|||
@PreAuthorize("@ss.hasPermission('infra:student:delete')") |
|||
public CommonResult<Boolean> deleteStudent(@RequestParam("id") Long id) { |
|||
studentService.deleteStudent(id); |
|||
return success(true); |
|||
} |
|||
|
|||
@GetMapping("/get") |
|||
@Operation(summary = "获得学生") |
|||
@Parameter(name = "id", description = "编号", required = true, example = "1024") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:query')") |
|||
public CommonResult<InfraStudentRespVO> getStudent(@RequestParam("id") Long id) { |
|||
InfraStudentDO student = studentService.getStudent(id); |
|||
return success(BeanUtils.toBean(student, InfraStudentRespVO.class)); |
|||
} |
|||
|
|||
@GetMapping("/page") |
|||
@Operation(summary = "获得学生分页") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:query')") |
|||
public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) { |
|||
PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO); |
|||
return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class)); |
|||
} |
|||
|
|||
@GetMapping("/export-excel") |
|||
@Operation(summary = "导出学生 Excel") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:export')") |
|||
@OperateLog(type = EXPORT) |
|||
public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO, |
|||
HttpServletResponse response) throws IOException { |
|||
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); |
|||
List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList(); |
|||
// 导出 Excel |
|||
ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class, |
|||
BeanUtils.toBean(list, InfraStudentRespVO.class)); |
|||
} |
|||
|
|||
// ==================== 子表(学生联系人) ==================== |
|||
|
|||
@GetMapping("/student-contact/page") |
|||
@Operation(summary = "获得学生联系人分页") |
|||
@Parameter(name = "studentId", description = "学生编号") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:query')") |
|||
public CommonResult<PageResult<InfraStudentContactDO>> getStudentContactPage(PageParam pageReqVO, |
|||
@RequestParam("studentId") Long studentId) { |
|||
return success(studentService.getStudentContactPage(pageReqVO, studentId)); |
|||
} |
|||
|
|||
@PostMapping("/student-contact/create") |
|||
@Operation(summary = "创建学生联系人") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:create')") |
|||
public CommonResult<Long> createStudentContact(@Valid @RequestBody InfraStudentContactDO studentContact) { |
|||
return success(studentService.createStudentContact(studentContact)); |
|||
} |
|||
|
|||
@PutMapping("/student-contact/update") |
|||
@Operation(summary = "更新学生联系人") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:update')") |
|||
public CommonResult<Boolean> updateStudentContact(@Valid @RequestBody InfraStudentContactDO studentContact) { |
|||
studentService.updateStudentContact(studentContact); |
|||
return success(true); |
|||
} |
|||
|
|||
@DeleteMapping("/student-contact/delete") |
|||
@Parameter(name = "id", description = "编号", required = true) |
|||
@Operation(summary = "删除学生联系人") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:delete')") |
|||
public CommonResult<Boolean> deleteStudentContact(@RequestParam("id") Long id) { |
|||
studentService.deleteStudentContact(id); |
|||
return success(true); |
|||
} |
|||
|
|||
@GetMapping("/student-contact/get") |
|||
@Operation(summary = "获得学生联系人") |
|||
@Parameter(name = "id", description = "编号", required = true) |
|||
@PreAuthorize("@ss.hasPermission('infra:student:query')") |
|||
public CommonResult<InfraStudentContactDO> getStudentContact(@RequestParam("id") Long id) { |
|||
return success(studentService.getStudentContact(id)); |
|||
} |
|||
|
|||
// ==================== 子表(学生班主任) ==================== |
|||
|
|||
@GetMapping("/student-teacher/page") |
|||
@Operation(summary = "获得学生班主任分页") |
|||
@Parameter(name = "studentId", description = "学生编号") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:query')") |
|||
public CommonResult<PageResult<InfraStudentTeacherDO>> getStudentTeacherPage(PageParam pageReqVO, |
|||
@RequestParam("studentId") Long studentId) { |
|||
return success(studentService.getStudentTeacherPage(pageReqVO, studentId)); |
|||
} |
|||
|
|||
@PostMapping("/student-teacher/create") |
|||
@Operation(summary = "创建学生班主任") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:create')") |
|||
public CommonResult<Long> createStudentTeacher(@Valid @RequestBody InfraStudentTeacherDO studentTeacher) { |
|||
return success(studentService.createStudentTeacher(studentTeacher)); |
|||
} |
|||
|
|||
@PutMapping("/student-teacher/update") |
|||
@Operation(summary = "更新学生班主任") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:update')") |
|||
public CommonResult<Boolean> updateStudentTeacher(@Valid @RequestBody InfraStudentTeacherDO studentTeacher) { |
|||
studentService.updateStudentTeacher(studentTeacher); |
|||
return success(true); |
|||
} |
|||
|
|||
@DeleteMapping("/student-teacher/delete") |
|||
@Parameter(name = "id", description = "编号", required = true) |
|||
@Operation(summary = "删除学生班主任") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:delete')") |
|||
public CommonResult<Boolean> deleteStudentTeacher(@RequestParam("id") Long id) { |
|||
studentService.deleteStudentTeacher(id); |
|||
return success(true); |
|||
} |
|||
|
|||
@GetMapping("/student-teacher/get") |
|||
@Operation(summary = "获得学生班主任") |
|||
@Parameter(name = "id", description = "编号", required = true) |
|||
@PreAuthorize("@ss.hasPermission('infra:student:query')") |
|||
public CommonResult<InfraStudentTeacherDO> getStudentTeacher(@RequestParam("id") Long id) { |
|||
return success(studentService.getStudentTeacher(id)); |
|||
} |
|||
|
|||
} |
@ -1,67 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.dal.dataobject.demo; |
|||
|
|||
import lombok.*; |
|||
import java.util.*; |
|||
import java.time.LocalDateTime; |
|||
import java.time.LocalDateTime; |
|||
import com.baomidou.mybatisplus.annotation.*; |
|||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
|||
|
|||
/** |
|||
* 学生 DO |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@TableName("infra_student") |
|||
@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 |
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
@Builder |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class InfraStudentDO extends BaseDO { |
|||
|
|||
/** |
|||
* 编号 |
|||
*/ |
|||
@TableId |
|||
private Long id; |
|||
/** |
|||
* 名字 |
|||
*/ |
|||
private String name; |
|||
/** |
|||
* 简介 |
|||
*/ |
|||
private String description; |
|||
/** |
|||
* 出生日期 |
|||
*/ |
|||
private LocalDateTime birthday; |
|||
/** |
|||
* 性别 |
|||
* |
|||
* 枚举 {@link TODO system_user_sex 对应的类} |
|||
*/ |
|||
private Integer sex; |
|||
/** |
|||
* 是否有效 |
|||
* |
|||
* 枚举 {@link TODO infra_boolean_string 对应的类} |
|||
*/ |
|||
private Boolean enabled; |
|||
/** |
|||
* 头像 |
|||
*/ |
|||
private String avatar; |
|||
/** |
|||
* 附件 |
|||
*/ |
|||
private String video; |
|||
/** |
|||
* 备注 |
|||
*/ |
|||
private String memo; |
|||
|
|||
} |
@ -1,30 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.dal.mysql.demo; |
|||
|
|||
import java.util.*; |
|||
|
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
|||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
|||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; |
|||
import org.apache.ibatis.annotations.Mapper; |
|||
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; |
|||
|
|||
/** |
|||
* 学生 Mapper |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@Mapper |
|||
public interface InfraStudentMapper extends BaseMapperX<InfraStudentDO> { |
|||
|
|||
default PageResult<InfraStudentDO> selectPage(InfraStudentPageReqVO reqVO) { |
|||
return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentDO>() |
|||
.likeIfPresent(InfraStudentDO::getName, reqVO.getName()) |
|||
.eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday()) |
|||
.eqIfPresent(InfraStudentDO::getSex, reqVO.getSex()) |
|||
.eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled()) |
|||
.betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime()) |
|||
.orderByDesc(InfraStudentDO::getId)); |
|||
} |
|||
|
|||
} |
@ -1,34 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; |
|||
|
|||
import lombok.*; |
|||
import java.util.*; |
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageParam; |
|||
import org.springframework.format.annotation.DateTimeFormat; |
|||
import java.time.LocalDateTime; |
|||
|
|||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; |
|||
|
|||
@Schema(description = "管理后台 - 学生分页 Request VO") |
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
public class InfraStudentPageReqVO extends PageParam { |
|||
|
|||
@Schema(description = "名字", example = "芋头") |
|||
private String name; |
|||
|
|||
@Schema(description = "出生日期") |
|||
private LocalDateTime birthday; |
|||
|
|||
@Schema(description = "性别", example = "1") |
|||
private Integer sex; |
|||
|
|||
@Schema(description = "是否有效", example = "true") |
|||
private Boolean enabled; |
|||
|
|||
@Schema(description = "创建时间") |
|||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) |
|||
private LocalDateTime[] createTime; |
|||
|
|||
} |
@ -1,60 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; |
|||
|
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import lombok.*; |
|||
import java.util.*; |
|||
import java.util.*; |
|||
import org.springframework.format.annotation.DateTimeFormat; |
|||
import java.time.LocalDateTime; |
|||
import com.alibaba.excel.annotation.*; |
|||
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; |
|||
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; |
|||
|
|||
@Schema(description = "管理后台 - 学生 Response VO") |
|||
@Data |
|||
@ExcelIgnoreUnannotated |
|||
public class InfraStudentRespVO { |
|||
|
|||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") |
|||
@ExcelProperty("编号") |
|||
private Long id; |
|||
|
|||
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") |
|||
@ExcelProperty("名字") |
|||
private String name; |
|||
|
|||
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") |
|||
@ExcelProperty("简介") |
|||
private String description; |
|||
|
|||
@Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) |
|||
@ExcelProperty("出生日期") |
|||
private LocalDateTime birthday; |
|||
|
|||
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
|||
@ExcelProperty(value = "性别", converter = DictConvert.class) |
|||
@DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 |
|||
private Integer sex; |
|||
|
|||
@Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") |
|||
@ExcelProperty(value = "是否有效", converter = DictConvert.class) |
|||
@DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 |
|||
private Boolean enabled; |
|||
|
|||
@Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") |
|||
@ExcelProperty("头像") |
|||
private String avatar; |
|||
|
|||
@Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") |
|||
@ExcelProperty("附件") |
|||
private String video; |
|||
|
|||
@Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") |
|||
@ExcelProperty("备注") |
|||
private String memo; |
|||
|
|||
@Schema(description = "创建时间") |
|||
@ExcelProperty("创建时间") |
|||
private LocalDateTime createTime; |
|||
|
|||
} |
@ -1,52 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; |
|||
|
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import lombok.*; |
|||
import java.util.*; |
|||
import javax.validation.constraints.*; |
|||
import java.util.*; |
|||
import org.springframework.format.annotation.DateTimeFormat; |
|||
import java.time.LocalDateTime; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; |
|||
|
|||
@Schema(description = "管理后台 - 学生新增/修改 Request VO") |
|||
@Data |
|||
public class InfraStudentSaveReqVO { |
|||
|
|||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") |
|||
private Long id; |
|||
|
|||
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") |
|||
@NotEmpty(message = "名字不能为空") |
|||
private String name; |
|||
|
|||
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") |
|||
@NotEmpty(message = "简介不能为空") |
|||
private String description; |
|||
|
|||
@Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) |
|||
@NotNull(message = "出生日期不能为空") |
|||
private LocalDateTime birthday; |
|||
|
|||
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
|||
@NotNull(message = "性别不能为空") |
|||
private Integer sex; |
|||
|
|||
@Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") |
|||
@NotNull(message = "是否有效不能为空") |
|||
private Boolean enabled; |
|||
|
|||
@Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") |
|||
@NotEmpty(message = "头像不能为空") |
|||
private String avatar; |
|||
|
|||
@Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") |
|||
@NotEmpty(message = "附件不能为空") |
|||
private String video; |
|||
|
|||
@Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") |
|||
@NotEmpty(message = "备注不能为空") |
|||
private String memo; |
|||
|
|||
} |
@ -1,139 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.service.demo; |
|||
|
|||
import java.util.*; |
|||
import javax.validation.*; |
|||
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageParam; |
|||
|
|||
/** |
|||
* 学生 Service 接口 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public interface InfraStudentService { |
|||
|
|||
/** |
|||
* 创建学生 |
|||
* |
|||
* @param createReqVO 创建信息 |
|||
* @return 编号 |
|||
*/ |
|||
Long createStudent(@Valid InfraStudentSaveReqVO createReqVO); |
|||
|
|||
/** |
|||
* 更新学生 |
|||
* |
|||
* @param updateReqVO 更新信息 |
|||
*/ |
|||
void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO); |
|||
|
|||
/** |
|||
* 删除学生 |
|||
* |
|||
* @param id 编号 |
|||
*/ |
|||
void deleteStudent(Long id); |
|||
|
|||
/** |
|||
* 获得学生 |
|||
* |
|||
* @param id 编号 |
|||
* @return 学生 |
|||
*/ |
|||
InfraStudentDO getStudent(Long id); |
|||
|
|||
/** |
|||
* 获得学生分页 |
|||
* |
|||
* @param pageReqVO 分页查询 |
|||
* @return 学生分页 |
|||
*/ |
|||
PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO); |
|||
|
|||
// ==================== 子表(学生联系人) ==================== |
|||
|
|||
/** |
|||
* 获得学生联系人分页 |
|||
* |
|||
* @param pageReqVO 分页查询 |
|||
* @param studentId 学生编号 |
|||
* @return 学生联系人分页 |
|||
*/ |
|||
PageResult<InfraStudentContactDO> getStudentContactPage(PageParam pageReqVO, Long studentId); |
|||
|
|||
/** |
|||
* 创建学生联系人 |
|||
* |
|||
* @param studentContact 创建信息 |
|||
* @return 编号 |
|||
*/ |
|||
Long createStudentContact(@Valid InfraStudentContactDO studentContact); |
|||
|
|||
/** |
|||
* 更新学生联系人 |
|||
* |
|||
* @param studentContact 更新信息 |
|||
*/ |
|||
void updateStudentContact(@Valid InfraStudentContactDO studentContact); |
|||
|
|||
/** |
|||
* 删除学生联系人 |
|||
* |
|||
* @param id 编号 |
|||
*/ |
|||
void deleteStudentContact(Long id); |
|||
|
|||
/** |
|||
* 获得学生联系人 |
|||
* |
|||
* @param id 编号 |
|||
* @return 学生联系人 |
|||
*/ |
|||
InfraStudentContactDO getStudentContact(Long id); |
|||
|
|||
// ==================== 子表(学生班主任) ==================== |
|||
|
|||
/** |
|||
* 获得学生班主任分页 |
|||
* |
|||
* @param pageReqVO 分页查询 |
|||
* @param studentId 学生编号 |
|||
* @return 学生班主任分页 |
|||
*/ |
|||
PageResult<InfraStudentTeacherDO> getStudentTeacherPage(PageParam pageReqVO, Long studentId); |
|||
|
|||
/** |
|||
* 创建学生班主任 |
|||
* |
|||
* @param studentTeacher 创建信息 |
|||
* @return 编号 |
|||
*/ |
|||
Long createStudentTeacher(@Valid InfraStudentTeacherDO studentTeacher); |
|||
|
|||
/** |
|||
* 更新学生班主任 |
|||
* |
|||
* @param studentTeacher 更新信息 |
|||
*/ |
|||
void updateStudentTeacher(@Valid InfraStudentTeacherDO studentTeacher); |
|||
|
|||
/** |
|||
* 删除学生班主任 |
|||
* |
|||
* @param id 编号 |
|||
*/ |
|||
void deleteStudentTeacher(Long id); |
|||
|
|||
/** |
|||
* 获得学生班主任 |
|||
* |
|||
* @param id 编号 |
|||
* @return 学生班主任 |
|||
*/ |
|||
InfraStudentTeacherDO getStudentTeacher(Long id); |
|||
|
|||
} |
@ -1,180 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.service.demo; |
|||
|
|||
import org.springframework.stereotype.Service; |
|||
import javax.annotation.Resource; |
|||
import org.springframework.validation.annotation.Validated; |
|||
import org.springframework.transaction.annotation.Transactional; |
|||
|
|||
import java.util.*; |
|||
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageParam; |
|||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
|||
|
|||
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; |
|||
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper; |
|||
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper; |
|||
|
|||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
|||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; |
|||
|
|||
/** |
|||
* 学生 Service 实现类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@Service |
|||
@Validated |
|||
public class InfraStudentServiceImpl implements InfraStudentService { |
|||
|
|||
@Resource |
|||
private InfraStudentMapper studentMapper; |
|||
@Resource |
|||
private InfraStudentContactMapper studentContactMapper; |
|||
@Resource |
|||
private InfraStudentTeacherMapper studentTeacherMapper; |
|||
|
|||
@Override |
|||
public Long createStudent(InfraStudentSaveReqVO createReqVO) { |
|||
// 插入 |
|||
InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class); |
|||
studentMapper.insert(student); |
|||
// 返回 |
|||
return student.getId(); |
|||
} |
|||
|
|||
@Override |
|||
public void updateStudent(InfraStudentSaveReqVO updateReqVO) { |
|||
// 校验存在 |
|||
validateStudentExists(updateReqVO.getId()); |
|||
// 更新 |
|||
InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class); |
|||
studentMapper.updateById(updateObj); |
|||
} |
|||
|
|||
@Override |
|||
@Transactional(rollbackFor = Exception.class) |
|||
public void deleteStudent(Long id) { |
|||
// 校验存在 |
|||
validateStudentExists(id); |
|||
// 删除 |
|||
studentMapper.deleteById(id); |
|||
|
|||
// 删除子表 |
|||
deleteStudentContactByStudentId(id); |
|||
deleteStudentTeacherByStudentId(id); |
|||
} |
|||
|
|||
private void validateStudentExists(Long id) { |
|||
if (studentMapper.selectById(id) == null) { |
|||
throw exception(STUDENT_NOT_EXISTS); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public InfraStudentDO getStudent(Long id) { |
|||
return studentMapper.selectById(id); |
|||
} |
|||
|
|||
@Override |
|||
public PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO) { |
|||
return studentMapper.selectPage(pageReqVO); |
|||
} |
|||
|
|||
// ==================== 子表(学生联系人) ==================== |
|||
|
|||
@Override |
|||
public PageResult<InfraStudentContactDO> getStudentContactPage(PageParam pageReqVO, Long studentId) { |
|||
return studentContactMapper.selectPage(pageReqVO, studentId); |
|||
} |
|||
|
|||
@Override |
|||
public Long createStudentContact(InfraStudentContactDO studentContact) { |
|||
studentContactMapper.insert(studentContact); |
|||
return studentContact.getId(); |
|||
} |
|||
|
|||
@Override |
|||
public void updateStudentContact(InfraStudentContactDO studentContact) { |
|||
// 校验存在 |
|||
validateStudentContactExists(studentContact.getId()); |
|||
// 更新 |
|||
studentContactMapper.updateById(studentContact); |
|||
} |
|||
|
|||
@Override |
|||
public void deleteStudentContact(Long id) { |
|||
// 校验存在 |
|||
validateStudentContactExists(id); |
|||
// 删除 |
|||
studentContactMapper.deleteById(id); |
|||
} |
|||
|
|||
@Override |
|||
public InfraStudentContactDO getStudentContact(Long id) { |
|||
return studentContactMapper.selectById(id); |
|||
} |
|||
|
|||
private void validateStudentContactExists(Long id) { |
|||
if (studentContactMapper.selectById(id) == null) { |
|||
throw exception(STUDENT_CONTACT_NOT_EXISTS); |
|||
} |
|||
} |
|||
|
|||
private void deleteStudentContactByStudentId(Long studentId) { |
|||
studentContactMapper.deleteByStudentId(studentId); |
|||
} |
|||
|
|||
// ==================== 子表(学生班主任) ==================== |
|||
|
|||
@Override |
|||
public PageResult<InfraStudentTeacherDO> getStudentTeacherPage(PageParam pageReqVO, Long studentId) { |
|||
return studentTeacherMapper.selectPage(pageReqVO, studentId); |
|||
} |
|||
|
|||
@Override |
|||
public Long createStudentTeacher(InfraStudentTeacherDO studentTeacher) { |
|||
// 校验是否已经存在 |
|||
if (studentTeacherMapper.selectByStudentId(studentTeacher.getStudentId()) != null) { |
|||
throw exception(STUDENT_TEACHER_EXISTS); |
|||
} |
|||
// 插入 |
|||
studentTeacherMapper.insert(studentTeacher); |
|||
return studentTeacher.getId(); |
|||
} |
|||
|
|||
@Override |
|||
public void updateStudentTeacher(InfraStudentTeacherDO studentTeacher) { |
|||
// 校验存在 |
|||
validateStudentTeacherExists(studentTeacher.getId()); |
|||
// 更新 |
|||
studentTeacherMapper.updateById(studentTeacher); |
|||
} |
|||
|
|||
@Override |
|||
public void deleteStudentTeacher(Long id) { |
|||
// 校验存在 |
|||
validateStudentTeacherExists(id); |
|||
// 删除 |
|||
studentTeacherMapper.deleteById(id); |
|||
} |
|||
|
|||
@Override |
|||
public InfraStudentTeacherDO getStudentTeacher(Long id) { |
|||
return studentTeacherMapper.selectById(id); |
|||
} |
|||
|
|||
private void validateStudentTeacherExists(Long id) { |
|||
if (studentTeacherMapper.selectById(id) == null) { |
|||
throw exception(STUDENT_TEACHER_NOT_EXISTS); |
|||
} |
|||
} |
|||
|
|||
private void deleteStudentTeacherByStudentId(Long studentId) { |
|||
studentTeacherMapper.deleteByStudentId(studentId); |
|||
} |
|||
|
|||
} |
@ -1,146 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.service.demo; |
|||
|
|||
import org.junit.jupiter.api.Disabled; |
|||
import org.junit.jupiter.api.Test; |
|||
import org.springframework.boot.test.mock.mockito.MockBean; |
|||
|
|||
import javax.annotation.Resource; |
|||
|
|||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
|||
|
|||
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; |
|||
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
|||
|
|||
import javax.annotation.Resource; |
|||
import org.springframework.context.annotation.Import; |
|||
import java.util.*; |
|||
import java.time.LocalDateTime; |
|||
|
|||
import static cn.hutool.core.util.RandomUtil.*; |
|||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; |
|||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; |
|||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; |
|||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; |
|||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; |
|||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; |
|||
import static org.junit.jupiter.api.Assertions.*; |
|||
import static org.mockito.Mockito.*; |
|||
|
|||
/** |
|||
* {@link InfraStudentServiceImpl} 的单元测试类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@Import(InfraStudentServiceImpl.class) |
|||
public class InfraStudentServiceImplTest extends BaseDbUnitTest { |
|||
|
|||
@Resource |
|||
private InfraStudentServiceImpl studentService; |
|||
|
|||
@Resource |
|||
private InfraStudentMapper studentMapper; |
|||
|
|||
@Test |
|||
public void testCreateStudent_success() { |
|||
// 准备参数 |
|||
InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null); |
|||
|
|||
// 调用 |
|||
Long studentId = studentService.createStudent(createReqVO); |
|||
// 断言 |
|||
assertNotNull(studentId); |
|||
// 校验记录的属性是否正确 |
|||
InfraStudentDO student = studentMapper.selectById(studentId); |
|||
assertPojoEquals(createReqVO, student, "id"); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateStudent_success() { |
|||
// mock 数据 |
|||
InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); |
|||
studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 |
|||
// 准备参数 |
|||
InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> { |
|||
o.setId(dbStudent.getId()); // 设置更新的 ID |
|||
}); |
|||
|
|||
// 调用 |
|||
studentService.updateStudent(updateReqVO); |
|||
// 校验是否更新正确 |
|||
InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的 |
|||
assertPojoEquals(updateReqVO, student); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateStudent_notExists() { |
|||
// 准备参数 |
|||
InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class); |
|||
|
|||
// 调用, 并断言异常 |
|||
assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS); |
|||
} |
|||
|
|||
@Test |
|||
public void testDeleteStudent_success() { |
|||
// mock 数据 |
|||
InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); |
|||
studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 |
|||
// 准备参数 |
|||
Long id = dbStudent.getId(); |
|||
|
|||
// 调用 |
|||
studentService.deleteStudent(id); |
|||
// 校验数据不存在了 |
|||
assertNull(studentMapper.selectById(id)); |
|||
} |
|||
|
|||
@Test |
|||
public void testDeleteStudent_notExists() { |
|||
// 准备参数 |
|||
Long id = randomLongId(); |
|||
|
|||
// 调用, 并断言异常 |
|||
assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS); |
|||
} |
|||
|
|||
@Test |
|||
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 |
|||
public void testGetStudentPage() { |
|||
// mock 数据 |
|||
InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到 |
|||
o.setName(null); |
|||
o.setBirthday(null); |
|||
o.setSex(null); |
|||
o.setEnabled(null); |
|||
o.setCreateTime(null); |
|||
}); |
|||
studentMapper.insert(dbStudent); |
|||
// 测试 name 不匹配 |
|||
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null))); |
|||
// 测试 birthday 不匹配 |
|||
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null))); |
|||
// 测试 sex 不匹配 |
|||
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null))); |
|||
// 测试 enabled 不匹配 |
|||
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null))); |
|||
// 测试 createTime 不匹配 |
|||
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null))); |
|||
// 准备参数 |
|||
InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO(); |
|||
reqVO.setName(null); |
|||
reqVO.setBirthday(null); |
|||
reqVO.setSex(null); |
|||
reqVO.setEnabled(null); |
|||
reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); |
|||
|
|||
// 调用 |
|||
PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(reqVO); |
|||
// 断言 |
|||
assertEquals(1, pageResult.getTotal()); |
|||
assertEquals(1, pageResult.getList().size()); |
|||
assertPojoEquals(dbStudent, pageResult.getList().get(0)); |
|||
} |
|||
|
|||
} |
@ -1,71 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.dal.dataobject.demo; |
|||
|
|||
import lombok.*; |
|||
import java.util.*; |
|||
import java.time.LocalDateTime; |
|||
import java.time.LocalDateTime; |
|||
import com.baomidou.mybatisplus.annotation.*; |
|||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
|||
|
|||
/** |
|||
* 学生班主任 DO |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@TableName("infra_student_teacher") |
|||
@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 |
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
@Builder |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class InfraStudentTeacherDO extends BaseDO { |
|||
|
|||
/** |
|||
* 编号 |
|||
*/ |
|||
@TableId |
|||
private Long id; |
|||
/** |
|||
* 学生编号 |
|||
*/ |
|||
private Long studentId; |
|||
/** |
|||
* 名字 |
|||
*/ |
|||
private String name; |
|||
/** |
|||
* 简介 |
|||
*/ |
|||
private String description; |
|||
/** |
|||
* 出生日期 |
|||
*/ |
|||
private LocalDateTime birthday; |
|||
/** |
|||
* 性别 |
|||
* |
|||
* 枚举 {@link TODO system_user_sex 对应的类} |
|||
*/ |
|||
private Integer sex; |
|||
/** |
|||
* 是否有效 |
|||
* |
|||
* 枚举 {@link TODO infra_boolean_string 对应的类} |
|||
*/ |
|||
private Boolean enabled; |
|||
/** |
|||
* 头像 |
|||
*/ |
|||
private String avatar; |
|||
/** |
|||
* 附件 |
|||
*/ |
|||
private String video; |
|||
/** |
|||
* 备注 |
|||
*/ |
|||
private String memo; |
|||
|
|||
} |
@ -1,30 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.dal.mysql.demo; |
|||
|
|||
import java.util.*; |
|||
|
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageParam; |
|||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
|||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; |
|||
import org.apache.ibatis.annotations.Mapper; |
|||
|
|||
/** |
|||
* 学生班主任 Mapper |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@Mapper |
|||
public interface InfraStudentTeacherMapper extends BaseMapperX<InfraStudentTeacherDO> { |
|||
|
|||
default PageResult<InfraStudentTeacherDO> selectPage(PageParam reqVO, Long studentId) { |
|||
return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentTeacherDO>() |
|||
.eq(InfraStudentTeacherDO::getStudentId, studentId) |
|||
.orderByDesc(InfraStudentTeacherDO::getId)); |
|||
} |
|||
|
|||
default int deleteByStudentId(Long studentId) { |
|||
return delete(InfraStudentTeacherDO::getStudentId, studentId); |
|||
} |
|||
|
|||
} |
@ -1,141 +0,0 @@ |
|||
import request from '@/utils/request' |
|||
|
|||
// 创建学生 |
|||
export function createStudent(data) { |
|||
return request({ |
|||
url: '/infra/student/create', |
|||
method: 'post', |
|||
data: data |
|||
}) |
|||
} |
|||
|
|||
// 更新学生 |
|||
export function updateStudent(data) { |
|||
return request({ |
|||
url: '/infra/student/update', |
|||
method: 'put', |
|||
data: data |
|||
}) |
|||
} |
|||
|
|||
// 删除学生 |
|||
export function deleteStudent(id) { |
|||
return request({ |
|||
url: '/infra/student/delete?id=' + id, |
|||
method: 'delete' |
|||
}) |
|||
} |
|||
|
|||
// 获得学生 |
|||
export function getStudent(id) { |
|||
return request({ |
|||
url: '/infra/student/get?id=' + id, |
|||
method: 'get' |
|||
}) |
|||
} |
|||
|
|||
// 获得学生分页 |
|||
export function getStudentPage(params) { |
|||
return request({ |
|||
url: '/infra/student/page', |
|||
method: 'get', |
|||
params |
|||
}) |
|||
} |
|||
// 导出学生 Excel |
|||
export function exportStudentExcel(params) { |
|||
return request({ |
|||
url: '/infra/student/export-excel', |
|||
method: 'get', |
|||
params, |
|||
responseType: 'blob' |
|||
}) |
|||
} |
|||
|
|||
// ==================== 子表(学生联系人) ==================== |
|||
|
|||
// 获得学生联系人分页 |
|||
export function getStudentContactPage(params) { |
|||
return request({ |
|||
url: '/infra/student/student-contact/page', |
|||
method: 'get', |
|||
params |
|||
}) |
|||
} |
|||
// 新增学生联系人 |
|||
export function createStudentContact(data) { |
|||
return request({ |
|||
url: `/infra/student/student-contact/create`, |
|||
method: 'post', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
// 修改学生联系人 |
|||
export function updateStudentContact(data) { |
|||
return request({ |
|||
url: `/infra/student/student-contact/update`, |
|||
method: 'post', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
// 删除学生联系人 |
|||
export function deleteStudentContact(id) { |
|||
return request({ |
|||
url: `/infra/student/student-contact/delete?id=` + id, |
|||
method: 'delete' |
|||
}) |
|||
} |
|||
|
|||
// 获得学生联系人 |
|||
export function getStudentContact(id) { |
|||
return request({ |
|||
url: `/infra/student/student-contact/get?id=` + id, |
|||
method: 'get' |
|||
}) |
|||
} |
|||
|
|||
// ==================== 子表(学生班主任) ==================== |
|||
|
|||
// 获得学生班主任分页 |
|||
export function getStudentTeacherPage(params) { |
|||
return request({ |
|||
url: '/infra/student/student-teacher/page', |
|||
method: 'get', |
|||
params |
|||
}) |
|||
} |
|||
// 新增学生班主任 |
|||
export function createStudentTeacher(data) { |
|||
return request({ |
|||
url: `/infra/student/student-teacher/create`, |
|||
method: 'post', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
// 修改学生班主任 |
|||
export function updateStudentTeacher(data) { |
|||
return request({ |
|||
url: `/infra/student/student-teacher/update`, |
|||
method: 'post', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
// 删除学生班主任 |
|||
export function deleteStudentTeacher(id) { |
|||
return request({ |
|||
url: `/infra/student/student-teacher/delete?id=` + id, |
|||
method: 'delete' |
|||
}) |
|||
} |
|||
|
|||
// 获得学生班主任 |
|||
export function getStudentTeacher(id) { |
|||
return request({ |
|||
url: `/infra/student/student-teacher/get?id=` + id, |
|||
method: 'get' |
|||
}) |
|||
} |
@ -1,17 +0,0 @@ |
|||
-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里 |
|||
CREATE TABLE IF NOT EXISTS "infra_student" ( |
|||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, |
|||
"name" varchar NOT NULL, |
|||
"description" varchar NOT NULL, |
|||
"birthday" varchar NOT NULL, |
|||
"sex" int NOT NULL, |
|||
"enabled" bit NOT NULL, |
|||
"avatar" varchar NOT NULL, |
|||
"video" varchar NOT NULL, |
|||
"memo" varchar NOT NULL, |
|||
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, |
|||
PRIMARY KEY ("id") |
|||
) COMMENT '学生表'; |
|||
|
|||
-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里 |
|||
DELETE FROM "infra_student"; |
@ -1,55 +0,0 @@ |
|||
-- 菜单 SQL |
|||
INSERT INTO system_menu( |
|||
name, permission, type, sort, parent_id, |
|||
path, icon, component, status, component_name |
|||
) |
|||
VALUES ( |
|||
'学生管理', '', 2, 0, 888, |
|||
'student', '', 'infra/demo/index', 0, 'InfraStudent' |
|||
); |
|||
|
|||
-- 按钮父菜单ID |
|||
-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码 |
|||
SELECT @parentId := LAST_INSERT_ID(); |
|||
|
|||
-- 按钮 SQL |
|||
INSERT INTO system_menu( |
|||
name, permission, type, sort, parent_id, |
|||
path, icon, component, status |
|||
) |
|||
VALUES ( |
|||
'学生查询', 'infra:student:query', 3, 1, @parentId, |
|||
'', '', '', 0 |
|||
); |
|||
INSERT INTO system_menu( |
|||
name, permission, type, sort, parent_id, |
|||
path, icon, component, status |
|||
) |
|||
VALUES ( |
|||
'学生创建', 'infra:student:create', 3, 2, @parentId, |
|||
'', '', '', 0 |
|||
); |
|||
INSERT INTO system_menu( |
|||
name, permission, type, sort, parent_id, |
|||
path, icon, component, status |
|||
) |
|||
VALUES ( |
|||
'学生更新', 'infra:student:update', 3, 3, @parentId, |
|||
'', '', '', 0 |
|||
); |
|||
INSERT INTO system_menu( |
|||
name, permission, type, sort, parent_id, |
|||
path, icon, component, status |
|||
) |
|||
VALUES ( |
|||
'学生删除', 'infra:student:delete', 3, 4, @parentId, |
|||
'', '', '', 0 |
|||
); |
|||
INSERT INTO system_menu( |
|||
name, permission, type, sort, parent_id, |
|||
path, icon, component, status |
|||
) |
|||
VALUES ( |
|||
'学生导出', 'infra:student:export', 3, 5, @parentId, |
|||
'', '', '', 0 |
|||
); |
@ -1,151 +0,0 @@ |
|||
<template> |
|||
<div class="app-container"> |
|||
<!-- 对话框(添加 / 修改) --> |
|||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="45%" v-dialogDrag append-to-body> |
|||
<el-form ref="formRef" :model="formData" :rules="formRules" v-loading="formLoading" label-width="100px"> |
|||
<el-form-item label="名字" prop="name"> |
|||
<el-input v-model="formData.name" placeholder="请输入名字" /> |
|||
</el-form-item> |
|||
<el-form-item label="简介" prop="description"> |
|||
<el-input v-model="formData.description" type="textarea" placeholder="请输入内容" /> |
|||
</el-form-item> |
|||
<el-form-item label="出生日期" prop="birthday"> |
|||
<el-date-picker clearable v-model="formData.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" /> |
|||
</el-form-item> |
|||
<el-form-item label="性别" prop="sex"> |
|||
<el-select v-model="formData.sex" placeholder="请选择性别"> |
|||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)" |
|||
:key="dict.value" :label="dict.label" :value="parseInt(dict.value)" /> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item label="是否有效" prop="enabled"> |
|||
<el-radio-group v-model="formData.enabled"> |
|||
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)" |
|||
:key="dict.value" :label="dict.value">{{dict.label}}</el-radio> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
<el-form-item label="头像" prop="avatar"> |
|||
<ImageUpload v-model="formData.avatar"/> |
|||
</el-form-item> |
|||
<el-form-item label="附件" prop="video"> |
|||
<FileUpload v-model="formData.video"/> |
|||
</el-form-item> |
|||
<el-form-item label="备注" prop="memo"> |
|||
<editor v-model="formData.memo" :min-height="192"/> |
|||
</el-form-item> |
|||
</el-form> |
|||
<div slot="footer" class="dialog-footer"> |
|||
<el-button type="primary" @click="submitForm" :disabled="formLoading">确 定</el-button> |
|||
<el-button @click="dialogVisible = false">取 消</el-button> |
|||
</div> |
|||
</el-dialog> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import * as StudentApi from '@/api/infra/demo'; |
|||
import ImageUpload from '@/components/ImageUpload'; |
|||
import FileUpload from '@/components/FileUpload'; |
|||
import Editor from '@/components/Editor'; |
|||
export default { |
|||
name: "StudentContactForm", |
|||
components: { |
|||
ImageUpload, |
|||
FileUpload, |
|||
Editor, |
|||
}, |
|||
data() { |
|||
return { |
|||
// 弹出层标题 |
|||
dialogTitle: "", |
|||
// 是否显示弹出层 |
|||
dialogVisible: false, |
|||
// 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 |
|||
formLoading: false, |
|||
// 表单参数 |
|||
formData: { |
|||
id: undefined, |
|||
studentId: undefined, |
|||
name: undefined, |
|||
description: undefined, |
|||
birthday: undefined, |
|||
sex: undefined, |
|||
enabled: undefined, |
|||
avatar: undefined, |
|||
video: undefined, |
|||
memo: undefined, |
|||
}, |
|||
// 表单校验 |
|||
formRules: { |
|||
studentId: [{ required: true, message: "学生编号不能为空", trigger: "blur" }], |
|||
name: [{ required: true, message: "名字不能为空", trigger: "blur" }], |
|||
description: [{ required: true, message: "简介不能为空", trigger: "blur" }], |
|||
birthday: [{ required: true, message: "出生日期不能为空", trigger: "blur" }], |
|||
sex: [{ required: true, message: "性别不能为空", trigger: "change" }], |
|||
enabled: [{ required: true, message: "是否有效不能为空", trigger: "blur" }], |
|||
avatar: [{ required: true, message: "头像不能为空", trigger: "blur" }], |
|||
memo: [{ required: true, message: "备注不能为空", trigger: "blur" }], |
|||
}, |
|||
}; |
|||
}, |
|||
methods: { |
|||
/** 打开弹窗 */ |
|||
async open(id, studentId) { |
|||
this.dialogVisible = true; |
|||
this.reset(); |
|||
this.formData.studentId = studentId; |
|||
// 修改时,设置数据 |
|||
if (id) { |
|||
this.formLoading = true; |
|||
try { |
|||
const res = await StudentApi.getStudentContact(id); |
|||
this.formData = res.data; |
|||
this.dialogTitle = "修改学生联系人"; |
|||
} finally { |
|||
this.formLoading = false; |
|||
} |
|||
} |
|||
this.dialogTitle = "新增学生联系人"; |
|||
}, |
|||
/** 提交按钮 */ |
|||
async submitForm() { |
|||
await this.$refs["formRef"].validate(); |
|||
this.formLoading = true; |
|||
try { |
|||
const data = this.formData; |
|||
// 修改的提交 |
|||
if (data.id) { |
|||
await StudentApi.updateStudentContact(data); |
|||
this.$modal.msgSuccess("修改成功"); |
|||
this.dialogVisible = false; |
|||
this.$emit('success'); |
|||
return; |
|||
} |
|||
// 添加的提交 |
|||
await StudentApi.createStudentContact(data); |
|||
this.$modal.msgSuccess("新增成功"); |
|||
this.dialogVisible = false; |
|||
this.$emit('success'); |
|||
}finally { |
|||
this.formLoading = false; |
|||
} |
|||
}, |
|||
/** 表单重置 */ |
|||
reset() { |
|||
this.formData = { |
|||
id: undefined, |
|||
studentId: undefined, |
|||
name: undefined, |
|||
description: undefined, |
|||
birthday: undefined, |
|||
sex: undefined, |
|||
enabled: undefined, |
|||
avatar: undefined, |
|||
video: undefined, |
|||
memo: undefined, |
|||
}; |
|||
this.resetForm("formRef"); |
|||
}, |
|||
} |
|||
}; |
|||
</script> |
@ -1,129 +0,0 @@ |
|||
<template> |
|||
<div class="app-container"> |
|||
<!-- 操作工具栏 --> |
|||
<el-row :gutter="10" class="mb8"> |
|||
<el-col :span="1.5"> |
|||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)" |
|||
v-hasPermi="['infra:student:create']">新增</el-button> |
|||
</el-col> |
|||
</el-row> |
|||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> |
|||
<el-table-column label="编号" align="center" prop="id" /> |
|||
<el-table-column label="名字" align="center" prop="name" /> |
|||
<el-table-column label="简介" align="center" prop="description" /> |
|||
<el-table-column label="出生日期" align="center" prop="birthday" width="180"> |
|||
<template v-slot="scope"> |
|||
<span>{{ parseTime(scope.row.birthday) }}</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="性别" align="center" prop="sex"> |
|||
<template v-slot="scope"> |
|||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="是否有效" align="center" prop="enabled"> |
|||
<template v-slot="scope"> |
|||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.enabled" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="头像" align="center" prop="avatar" /> |
|||
<el-table-column label="附件" align="center" prop="video" /> |
|||
<el-table-column label="备注" align="center" prop="memo" /> |
|||
<el-table-column label="创建时间" align="center" prop="createTime" width="180"> |
|||
<template v-slot="scope"> |
|||
<span>{{ parseTime(scope.row.createTime) }}</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> |
|||
<template v-slot="scope"> |
|||
<el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)" |
|||
v-hasPermi="['infra:student:update']">修改</el-button> |
|||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" |
|||
v-hasPermi="['infra:student:delete']">删除</el-button> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
<!-- 分页组件 --> |
|||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" |
|||
@pagination="getList"/> |
|||
<!-- 对话框(添加 / 修改) --> |
|||
<StudentContactForm ref="formRef" @success="getList" /> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import * as StudentApi from '@/api/infra/demo'; |
|||
import StudentContactForm from './StudentContactForm.vue'; |
|||
export default { |
|||
name: "StudentContactList", |
|||
components: { |
|||
StudentContactForm |
|||
}, |
|||
props:[ |
|||
'studentId' |
|||
],// 学生编号(主表的关联字段) |
|||
data() { |
|||
return { |
|||
// 遮罩层 |
|||
loading: true, |
|||
// 列表的数据 |
|||
list: [], |
|||
// 列表的总页数 |
|||
total: 0, |
|||
// 查询参数 |
|||
queryParams: { |
|||
pageNo: 1, |
|||
pageSize: 10, |
|||
studentId: undefined |
|||
} |
|||
}; |
|||
}, |
|||
watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */ |
|||
studentId:{ |
|||
handler(val) { |
|||
this.queryParams.studentId = val; |
|||
if (val){ |
|||
this.handleQuery(); |
|||
} |
|||
}, |
|||
immediate: true |
|||
} |
|||
}, |
|||
methods: { |
|||
/** 查询列表 */ |
|||
async getList() { |
|||
try { |
|||
this.loading = true; |
|||
const res = await StudentApi.getStudentContactPage(this.queryParams); |
|||
this.list = res.data.list; |
|||
this.total = res.data.total; |
|||
} finally { |
|||
this.loading = false; |
|||
} |
|||
}, |
|||
/** 搜索按钮操作 */ |
|||
handleQuery() { |
|||
this.queryParams.pageNo = 1; |
|||
this.getList(); |
|||
}, |
|||
/** 添加/修改操作 */ |
|||
openForm(id) { |
|||
if (!this.studentId) { |
|||
this.$modal.msgError('请选择一个学生'); |
|||
return; |
|||
} |
|||
this.$refs["formRef"].open(id, this.studentId); |
|||
}, |
|||
/** 删除按钮操作 */ |
|||
async handleDelete(row) { |
|||
const id = row.id; |
|||
await this.$modal.confirm('是否确认删除学生编号为"' + id + '"的数据项?'); |
|||
try { |
|||
await StudentApi.deleteStudentContact(id); |
|||
await this.getList(); |
|||
this.$modal.msgSuccess("删除成功"); |
|||
} catch {} |
|||
}, |
|||
} |
|||
}; |
|||
</script> |
@ -1,149 +0,0 @@ |
|||
<template> |
|||
<div class="app-container"> |
|||
<!-- 对话框(添加 / 修改) --> |
|||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="45%" v-dialogDrag append-to-body> |
|||
<el-form ref="formRef" :model="formData" :rules="formRules" v-loading="formLoading" label-width="100px"> |
|||
<el-form-item label="名字" prop="name"> |
|||
<el-input v-model="formData.name" placeholder="请输入名字" /> |
|||
</el-form-item> |
|||
<el-form-item label="简介" prop="description"> |
|||
<el-input v-model="formData.description" type="textarea" placeholder="请输入内容" /> |
|||
</el-form-item> |
|||
<el-form-item label="出生日期" prop="birthday"> |
|||
<el-date-picker clearable v-model="formData.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" /> |
|||
</el-form-item> |
|||
<el-form-item label="性别" prop="sex"> |
|||
<el-select v-model="formData.sex" placeholder="请选择性别"> |
|||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)" |
|||
:key="dict.value" :label="dict.label" :value="parseInt(dict.value)" /> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item label="是否有效" prop="enabled"> |
|||
<el-radio-group v-model="formData.enabled"> |
|||
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)" |
|||
:key="dict.value" :label="dict.value">{{dict.label}}</el-radio> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
<el-form-item label="头像"> |
|||
<ImageUpload v-model="formData.avatar"/> |
|||
</el-form-item> |
|||
<el-form-item label="附件"> |
|||
<FileUpload v-model="formData.video"/> |
|||
</el-form-item> |
|||
<el-form-item label="备注"> |
|||
<Editor v-model="formData.memo" :min-height="192"/> |
|||
</el-form-item> |
|||
</el-form> |
|||
<div slot="footer" class="dialog-footer"> |
|||
<el-button type="primary" @click="submitForm" :disabled="formLoading">确 定</el-button> |
|||
<el-button @click="dialogVisible = false">取 消</el-button> |
|||
</div> |
|||
</el-dialog> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import * as StudentApi from '@/api/infra/demo'; |
|||
import ImageUpload from '@/components/ImageUpload'; |
|||
import FileUpload from '@/components/FileUpload'; |
|||
import Editor from '@/components/Editor'; |
|||
export default { |
|||
name: "StudentForm", |
|||
components: { |
|||
ImageUpload, |
|||
FileUpload, |
|||
Editor, |
|||
}, |
|||
data() { |
|||
return { |
|||
// 弹出层标题 |
|||
dialogTitle: "", |
|||
// 是否显示弹出层 |
|||
dialogVisible: false, |
|||
// 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 |
|||
formLoading: false, |
|||
// 表单参数 |
|||
formData: { |
|||
id: undefined, |
|||
name: undefined, |
|||
description: undefined, |
|||
birthday: undefined, |
|||
sex: undefined, |
|||
enabled: undefined, |
|||
avatar: undefined, |
|||
video: undefined, |
|||
memo: undefined, |
|||
}, |
|||
// 表单校验 |
|||
formRules: { |
|||
name: [{ required: true, message: '名字不能为空', trigger: 'blur' }], |
|||
description: [{ required: true, message: '简介不能为空', trigger: 'blur' }], |
|||
birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }], |
|||
sex: [{ required: true, message: '性别不能为空', trigger: 'change' }], |
|||
enabled: [{ required: true, message: '是否有效不能为空', trigger: 'blur' }], |
|||
avatar: [{ required: true, message: '头像不能为空', trigger: 'blur' }], |
|||
video: [{ required: true, message: '附件不能为空', trigger: 'blur' }], |
|||
memo: [{ required: true, message: '备注不能为空', trigger: 'blur' }], |
|||
}, |
|||
}; |
|||
}, |
|||
methods: { |
|||
/** 打开弹窗 */ |
|||
async open(id) { |
|||
this.dialogVisible = true; |
|||
this.reset(); |
|||
// 修改时,设置数据 |
|||
if (id) { |
|||
this.formLoading = true; |
|||
try { |
|||
const res = await StudentApi.getStudent(id); |
|||
this.formData = res.data; |
|||
this.title = "修改学生"; |
|||
} finally { |
|||
this.formLoading = false; |
|||
} |
|||
} |
|||
this.title = "新增学生"; |
|||
}, |
|||
/** 提交按钮 */ |
|||
async submitForm() { |
|||
// 校验主表 |
|||
await this.$refs["formRef"].validate(); |
|||
this.formLoading = true; |
|||
try { |
|||
const data = this.formData; |
|||
// 修改的提交 |
|||
if (data.id) { |
|||
await StudentApi.updateStudent(data); |
|||
this.$modal.msgSuccess("修改成功"); |
|||
this.dialogVisible = false; |
|||
this.$emit('success'); |
|||
return; |
|||
} |
|||
// 添加的提交 |
|||
await StudentApi.createStudent(data); |
|||
this.$modal.msgSuccess("新增成功"); |
|||
this.dialogVisible = false; |
|||
this.$emit('success'); |
|||
} finally { |
|||
this.formLoading = false; |
|||
} |
|||
}, |
|||
/** 表单重置 */ |
|||
reset() { |
|||
this.formData = { |
|||
id: undefined, |
|||
name: undefined, |
|||
description: undefined, |
|||
birthday: undefined, |
|||
sex: undefined, |
|||
enabled: undefined, |
|||
avatar: undefined, |
|||
video: undefined, |
|||
memo: undefined, |
|||
}; |
|||
this.resetForm("formRef"); |
|||
} |
|||
} |
|||
}; |
|||
</script> |
@ -1,151 +0,0 @@ |
|||
<template> |
|||
<div class="app-container"> |
|||
<!-- 对话框(添加 / 修改) --> |
|||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="45%" v-dialogDrag append-to-body> |
|||
<el-form ref="formRef" :model="formData" :rules="formRules" v-loading="formLoading" label-width="100px"> |
|||
<el-form-item label="名字" prop="name"> |
|||
<el-input v-model="formData.name" placeholder="请输入名字" /> |
|||
</el-form-item> |
|||
<el-form-item label="简介" prop="description"> |
|||
<el-input v-model="formData.description" type="textarea" placeholder="请输入内容" /> |
|||
</el-form-item> |
|||
<el-form-item label="出生日期" prop="birthday"> |
|||
<el-date-picker clearable v-model="formData.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" /> |
|||
</el-form-item> |
|||
<el-form-item label="性别" prop="sex"> |
|||
<el-select v-model="formData.sex" placeholder="请选择性别"> |
|||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)" |
|||
:key="dict.value" :label="dict.label" :value="parseInt(dict.value)" /> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item label="是否有效" prop="enabled"> |
|||
<el-radio-group v-model="formData.enabled"> |
|||
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)" |
|||
:key="dict.value" :label="dict.value">{{dict.label}}</el-radio> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
<el-form-item label="头像" prop="avatar"> |
|||
<ImageUpload v-model="formData.avatar"/> |
|||
</el-form-item> |
|||
<el-form-item label="附件" prop="video"> |
|||
<FileUpload v-model="formData.video"/> |
|||
</el-form-item> |
|||
<el-form-item label="备注" prop="memo"> |
|||
<editor v-model="formData.memo" :min-height="192"/> |
|||
</el-form-item> |
|||
</el-form> |
|||
<div slot="footer" class="dialog-footer"> |
|||
<el-button type="primary" @click="submitForm" :disabled="formLoading">确 定</el-button> |
|||
<el-button @click="dialogVisible = false">取 消</el-button> |
|||
</div> |
|||
</el-dialog> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import * as StudentApi from '@/api/infra/demo'; |
|||
import ImageUpload from '@/components/ImageUpload'; |
|||
import FileUpload from '@/components/FileUpload'; |
|||
import Editor from '@/components/Editor'; |
|||
export default { |
|||
name: "StudentTeacherForm", |
|||
components: { |
|||
ImageUpload, |
|||
FileUpload, |
|||
Editor, |
|||
}, |
|||
data() { |
|||
return { |
|||
// 弹出层标题 |
|||
dialogTitle: "", |
|||
// 是否显示弹出层 |
|||
dialogVisible: false, |
|||
// 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 |
|||
formLoading: false, |
|||
// 表单参数 |
|||
formData: { |
|||
id: undefined, |
|||
studentId: undefined, |
|||
name: undefined, |
|||
description: undefined, |
|||
birthday: undefined, |
|||
sex: undefined, |
|||
enabled: undefined, |
|||
avatar: undefined, |
|||
video: undefined, |
|||
memo: undefined, |
|||
}, |
|||
// 表单校验 |
|||
formRules: { |
|||
studentId: [{ required: true, message: "学生编号不能为空", trigger: "blur" }], |
|||
name: [{ required: true, message: "名字不能为空", trigger: "blur" }], |
|||
description: [{ required: true, message: "简介不能为空", trigger: "blur" }], |
|||
birthday: [{ required: true, message: "出生日期不能为空", trigger: "blur" }], |
|||
sex: [{ required: true, message: "性别不能为空", trigger: "change" }], |
|||
enabled: [{ required: true, message: "是否有效不能为空", trigger: "blur" }], |
|||
avatar: [{ required: true, message: "头像不能为空", trigger: "blur" }], |
|||
memo: [{ required: true, message: "备注不能为空", trigger: "blur" }], |
|||
}, |
|||
}; |
|||
}, |
|||
methods: { |
|||
/** 打开弹窗 */ |
|||
async open(id, studentId) { |
|||
this.dialogVisible = true; |
|||
this.reset(); |
|||
this.formData.studentId = studentId; |
|||
// 修改时,设置数据 |
|||
if (id) { |
|||
this.formLoading = true; |
|||
try { |
|||
const res = await StudentApi.getStudentTeacher(id); |
|||
this.formData = res.data; |
|||
this.dialogTitle = "修改学生班主任"; |
|||
} finally { |
|||
this.formLoading = false; |
|||
} |
|||
} |
|||
this.dialogTitle = "新增学生班主任"; |
|||
}, |
|||
/** 提交按钮 */ |
|||
async submitForm() { |
|||
await this.$refs["formRef"].validate(); |
|||
this.formLoading = true; |
|||
try { |
|||
const data = this.formData; |
|||
// 修改的提交 |
|||
if (data.id) { |
|||
await StudentApi.updateStudentTeacher(data); |
|||
this.$modal.msgSuccess("修改成功"); |
|||
this.dialogVisible = false; |
|||
this.$emit('success'); |
|||
return; |
|||
} |
|||
// 添加的提交 |
|||
await StudentApi.createStudentTeacher(data); |
|||
this.$modal.msgSuccess("新增成功"); |
|||
this.dialogVisible = false; |
|||
this.$emit('success'); |
|||
}finally { |
|||
this.formLoading = false; |
|||
} |
|||
}, |
|||
/** 表单重置 */ |
|||
reset() { |
|||
this.formData = { |
|||
id: undefined, |
|||
studentId: undefined, |
|||
name: undefined, |
|||
description: undefined, |
|||
birthday: undefined, |
|||
sex: undefined, |
|||
enabled: undefined, |
|||
avatar: undefined, |
|||
video: undefined, |
|||
memo: undefined, |
|||
}; |
|||
this.resetForm("formRef"); |
|||
}, |
|||
} |
|||
}; |
|||
</script> |
@ -1,129 +0,0 @@ |
|||
<template> |
|||
<div class="app-container"> |
|||
<!-- 操作工具栏 --> |
|||
<el-row :gutter="10" class="mb8"> |
|||
<el-col :span="1.5"> |
|||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)" |
|||
v-hasPermi="['infra:student:create']">新增</el-button> |
|||
</el-col> |
|||
</el-row> |
|||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> |
|||
<el-table-column label="编号" align="center" prop="id" /> |
|||
<el-table-column label="名字" align="center" prop="name" /> |
|||
<el-table-column label="简介" align="center" prop="description" /> |
|||
<el-table-column label="出生日期" align="center" prop="birthday" width="180"> |
|||
<template v-slot="scope"> |
|||
<span>{{ parseTime(scope.row.birthday) }}</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="性别" align="center" prop="sex"> |
|||
<template v-slot="scope"> |
|||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="是否有效" align="center" prop="enabled"> |
|||
<template v-slot="scope"> |
|||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.enabled" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="头像" align="center" prop="avatar" /> |
|||
<el-table-column label="附件" align="center" prop="video" /> |
|||
<el-table-column label="备注" align="center" prop="memo" /> |
|||
<el-table-column label="创建时间" align="center" prop="createTime" width="180"> |
|||
<template v-slot="scope"> |
|||
<span>{{ parseTime(scope.row.createTime) }}</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> |
|||
<template v-slot="scope"> |
|||
<el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)" |
|||
v-hasPermi="['infra:student:update']">修改</el-button> |
|||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" |
|||
v-hasPermi="['infra:student:delete']">删除</el-button> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
<!-- 分页组件 --> |
|||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" |
|||
@pagination="getList"/> |
|||
<!-- 对话框(添加 / 修改) --> |
|||
<StudentTeacherForm ref="formRef" @success="getList" /> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import * as StudentApi from '@/api/infra/demo'; |
|||
import StudentTeacherForm from './StudentTeacherForm.vue'; |
|||
export default { |
|||
name: "StudentTeacherList", |
|||
components: { |
|||
StudentTeacherForm |
|||
}, |
|||
props:[ |
|||
'studentId' |
|||
],// 学生编号(主表的关联字段) |
|||
data() { |
|||
return { |
|||
// 遮罩层 |
|||
loading: true, |
|||
// 列表的数据 |
|||
list: [], |
|||
// 列表的总页数 |
|||
total: 0, |
|||
// 查询参数 |
|||
queryParams: { |
|||
pageNo: 1, |
|||
pageSize: 10, |
|||
studentId: undefined |
|||
} |
|||
}; |
|||
}, |
|||
watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */ |
|||
studentId:{ |
|||
handler(val) { |
|||
this.queryParams.studentId = val; |
|||
if (val){ |
|||
this.handleQuery(); |
|||
} |
|||
}, |
|||
immediate: true |
|||
} |
|||
}, |
|||
methods: { |
|||
/** 查询列表 */ |
|||
async getList() { |
|||
try { |
|||
this.loading = true; |
|||
const res = await StudentApi.getStudentTeacherPage(this.queryParams); |
|||
this.list = res.data.list; |
|||
this.total = res.data.total; |
|||
} finally { |
|||
this.loading = false; |
|||
} |
|||
}, |
|||
/** 搜索按钮操作 */ |
|||
handleQuery() { |
|||
this.queryParams.pageNo = 1; |
|||
this.getList(); |
|||
}, |
|||
/** 添加/修改操作 */ |
|||
openForm(id) { |
|||
if (!this.studentId) { |
|||
this.$modal.msgError('请选择一个学生'); |
|||
return; |
|||
} |
|||
this.$refs["formRef"].open(id, this.studentId); |
|||
}, |
|||
/** 删除按钮操作 */ |
|||
async handleDelete(row) { |
|||
const id = row.id; |
|||
await this.$modal.confirm('是否确认删除学生编号为"' + id + '"的数据项?'); |
|||
try { |
|||
await StudentApi.deleteStudentTeacher(id); |
|||
await this.getList(); |
|||
this.$modal.msgSuccess("删除成功"); |
|||
} catch {} |
|||
}, |
|||
} |
|||
}; |
|||
</script> |
@ -1,233 +0,0 @@ |
|||
<template> |
|||
<div class="app-container"> |
|||
<!-- 搜索工作栏 --> |
|||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px"> |
|||
<el-form-item label="名字" prop="name"> |
|||
<el-input v-model="queryParams.name" placeholder="请输入名字" clearable @keyup.enter.native="handleQuery"/> |
|||
</el-form-item> |
|||
<el-form-item label="出生日期" prop="birthday"> |
|||
<el-date-picker clearable v-model="queryParams.birthday" type="date" value-format="yyyy-MM-dd" placeholder="选择出生日期" /> |
|||
</el-form-item> |
|||
<el-form-item label="性别" prop="sex"> |
|||
<el-select v-model="queryParams.sex" placeholder="请选择性别" clearable size="small"> |
|||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)" |
|||
:key="dict.value" :label="dict.label" :value="dict.value"/> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item label="是否有效" prop="enabled"> |
|||
<el-select v-model="queryParams.enabled" placeholder="请选择是否有效" clearable size="small"> |
|||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)" |
|||
:key="dict.value" :label="dict.label" :value="dict.value"/> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item label="创建时间" prop="createTime"> |
|||
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange" |
|||
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" /> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button> |
|||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
|
|||
<!-- 操作工具栏 --> |
|||
<el-row :gutter="10" class="mb8"> |
|||
<el-col :span="1.5"> |
|||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)" |
|||
v-hasPermi="['infra:student:create']">新增</el-button> |
|||
</el-col> |
|||
<el-col :span="1.5"> |
|||
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading" |
|||
v-hasPermi="['infra:student:export']">导出</el-button> |
|||
</el-col> |
|||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> |
|||
</el-row> |
|||
|
|||
<el-table |
|||
v-loading="loading" |
|||
:data="list" |
|||
:stripe="true" |
|||
:highlight-current-row="true" |
|||
:show-overflow-tooltip="true" |
|||
@current-change="handleCurrentChange" |
|||
> |
|||
<el-table-column label="编号" align="center" prop="id"> |
|||
<template v-slot="scope"> |
|||
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.id" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="名字" align="center" prop="name"> |
|||
<template v-slot="scope"> |
|||
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.name" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="简介" align="center" prop="description"> |
|||
<template v-slot="scope"> |
|||
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.description" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="出生日期" align="center" prop="birthday" width="180"> |
|||
<template v-slot="scope"> |
|||
<span>{{ parseTime(scope.row.birthday) }}</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="性别" align="center" prop="sex"> |
|||
<template v-slot="scope"> |
|||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="是否有效" align="center" prop="enabled"> |
|||
<template v-slot="scope"> |
|||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.enabled" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="头像" align="center" prop="avatar"> |
|||
<template v-slot="scope"> |
|||
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.avatar" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="附件" align="center" prop="video"> |
|||
<template v-slot="scope"> |
|||
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.video" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="备注" align="center" prop="memo"> |
|||
<template v-slot="scope"> |
|||
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.memo" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="创建时间" align="center" prop="createTime" width="180"> |
|||
<template v-slot="scope"> |
|||
<span>{{ parseTime(scope.row.createTime) }}</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> |
|||
<template v-slot="scope"> |
|||
<el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)" |
|||
v-hasPermi="['infra:student:update']">修改</el-button> |
|||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" |
|||
v-hasPermi="['infra:student:delete']">删除</el-button> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
<!-- 分页组件 --> |
|||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" |
|||
@pagination="getList"/> |
|||
<!-- 对话框(添加 / 修改) --> |
|||
<StudentForm ref="formRef" @success="getList" /> |
|||
<!-- 子表的列表 --> |
|||
<el-tabs v-model="subTabsName"> |
|||
<el-tab-pane label="学生联系人" name="studentContact"> |
|||
<StudentContactList v-if="currentRow.id" :student-id="currentRow.id" /> |
|||
</el-tab-pane> |
|||
<el-tab-pane label="学生班主任" name="studentTeacher"> |
|||
<StudentTeacherList v-if="currentRow.id" :student-id="currentRow.id" /> |
|||
</el-tab-pane> |
|||
</el-tabs> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import * as StudentApi from '@/api/infra/demo'; |
|||
import StudentForm from './StudentForm.vue'; |
|||
import StudentContactList from './components/StudentContactList.vue'; |
|||
import StudentTeacherList from './components/StudentTeacherList.vue'; |
|||
export default { |
|||
name: "Student", |
|||
components: { |
|||
StudentForm, |
|||
StudentContactList, |
|||
StudentTeacherList, |
|||
}, |
|||
data() { |
|||
return { |
|||
// 遮罩层 |
|||
loading: true, |
|||
// 导出遮罩层 |
|||
exportLoading: false, |
|||
// 显示搜索条件 |
|||
showSearch: true, |
|||
// 总条数 |
|||
total: 0, |
|||
// 学生列表 |
|||
list: [], |
|||
// 是否展开,默认全部展开 |
|||
isExpandAll: true, |
|||
// 重新渲染表格状态 |
|||
refreshTable: true, |
|||
// 选中行 |
|||
currentRow: {}, |
|||
// 查询参数 |
|||
queryParams: { |
|||
pageNo: 1, |
|||
pageSize: 10, |
|||
name: null, |
|||
birthday: null, |
|||
sex: null, |
|||
enabled: null, |
|||
createTime: [], |
|||
}, |
|||
/** 子表的列表 */ |
|||
subTabsName: 'studentContact' |
|||
}; |
|||
}, |
|||
created() { |
|||
this.getList(); |
|||
}, |
|||
methods: { |
|||
/** 查询列表 */ |
|||
async getList() { |
|||
try { |
|||
this.loading = true; |
|||
const res = await StudentApi.getStudentPage(this.queryParams); |
|||
this.list = res.data.list; |
|||
this.total = res.data.total; |
|||
} finally { |
|||
this.loading = false; |
|||
} |
|||
}, |
|||
/** 搜索按钮操作 */ |
|||
handleQuery() { |
|||
this.queryParams.pageNo = 1; |
|||
this.getList(); |
|||
}, |
|||
/** 重置按钮操作 */ |
|||
resetQuery() { |
|||
this.resetForm("queryForm"); |
|||
this.handleQuery(); |
|||
}, |
|||
/** 添加/修改操作 */ |
|||
openForm(id) { |
|||
this.$refs["formRef"].open(id); |
|||
}, |
|||
/** 删除按钮操作 */ |
|||
async handleDelete(row) { |
|||
const id = row.id; |
|||
await this.$modal.confirm('是否确认删除学生编号为"' + id + '"的数据项?') |
|||
try { |
|||
await StudentApi.deleteStudent(id); |
|||
await this.getList(); |
|||
this.$modal.msgSuccess("删除成功"); |
|||
} catch {} |
|||
}, |
|||
/** 导出按钮操作 */ |
|||
async handleExport() { |
|||
await this.$modal.confirm('是否确认导出所有学生数据项?'); |
|||
try { |
|||
this.exportLoading = true; |
|||
const res = await StudentApi.exportStudentExcel(this.queryParams); |
|||
this.$download.excel(res.data, '学生.xls'); |
|||
} catch { |
|||
} finally { |
|||
this.exportLoading = false; |
|||
} |
|||
}, |
|||
/** 选中行操作 */ |
|||
handleCurrentChange(row) { |
|||
this.currentRow = row; |
|||
/** 子表的列表 */ |
|||
this.subTabsName = 'studentContact'; |
|||
}, |
|||
} |
|||
}; |
|||
</script> |
@ -1,12 +0,0 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
|||
<mapper namespace="cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper"> |
|||
|
|||
<!-- |
|||
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。 |
|||
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。 |
|||
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。 |
|||
文档可见:https://www.iocoder.cn/MyBatis/x-plugins/ |
|||
--> |
|||
|
|||
</mapper> |
@ -1,73 +0,0 @@ |
|||
[ { |
|||
"contentPath" : "java/InfraStudentPageReqVO", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentRespVO", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentSaveReqVO", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentController", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentDO", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentContactDO", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentTeacherDO", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentMapper", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentContactMapper", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentTeacherMapper", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java" |
|||
}, { |
|||
"contentPath" : "xml/InfraStudentMapper", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentServiceImpl", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentService", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentServiceImplTest", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java" |
|||
}, { |
|||
"contentPath" : "java/ErrorCodeConstants_手动操作", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java" |
|||
}, { |
|||
"contentPath" : "sql/sql", |
|||
"filePath" : "sql/sql.sql" |
|||
}, { |
|||
"contentPath" : "sql/h2", |
|||
"filePath" : "sql/h2.sql" |
|||
}, { |
|||
"contentPath" : "vue/index", |
|||
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue" |
|||
}, { |
|||
"contentPath": "js/index", |
|||
"filePath": "yudao-ui-admin-vue2/src/api/infra/demo/index.js" |
|||
}, { |
|||
"contentPath" : "vue/StudentForm", |
|||
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/StudentForm.vue" |
|||
}, { |
|||
"contentPath" : "vue/StudentContactForm", |
|||
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactForm.vue" |
|||
}, { |
|||
"contentPath" : "vue/StudentTeacherForm", |
|||
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherForm.vue" |
|||
}, { |
|||
"contentPath" : "vue/StudentContactList", |
|||
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactList.vue" |
|||
}, { |
|||
"contentPath" : "vue/StudentTeacherList", |
|||
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherList.vue" |
|||
} ] |
@ -1,3 +0,0 @@ |
|||
// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!! |
|||
// ========== 学生 TODO 补充编号 ========== |
|||
ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在"); |
@ -1,71 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.dal.dataobject.demo; |
|||
|
|||
import lombok.*; |
|||
import java.util.*; |
|||
import java.time.LocalDateTime; |
|||
import java.time.LocalDateTime; |
|||
import com.baomidou.mybatisplus.annotation.*; |
|||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
|||
|
|||
/** |
|||
* 学生联系人 DO |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@TableName("infra_student_contact") |
|||
@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 |
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
@Builder |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class InfraStudentContactDO extends BaseDO { |
|||
|
|||
/** |
|||
* 编号 |
|||
*/ |
|||
@TableId |
|||
private Long id; |
|||
/** |
|||
* 学生编号 |
|||
*/ |
|||
private Long studentId; |
|||
/** |
|||
* 名字 |
|||
*/ |
|||
private String name; |
|||
/** |
|||
* 简介 |
|||
*/ |
|||
private String description; |
|||
/** |
|||
* 出生日期 |
|||
*/ |
|||
private LocalDateTime birthday; |
|||
/** |
|||
* 性别 |
|||
* |
|||
* 枚举 {@link TODO system_user_sex 对应的类} |
|||
*/ |
|||
private Integer sex; |
|||
/** |
|||
* 是否有效 |
|||
* |
|||
* 枚举 {@link TODO infra_boolean_string 对应的类} |
|||
*/ |
|||
private Boolean enabled; |
|||
/** |
|||
* 头像 |
|||
*/ |
|||
private String avatar; |
|||
/** |
|||
* 附件 |
|||
*/ |
|||
private String video; |
|||
/** |
|||
* 备注 |
|||
*/ |
|||
private String memo; |
|||
|
|||
} |
@ -1,28 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.dal.mysql.demo; |
|||
|
|||
import java.util.*; |
|||
|
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageParam; |
|||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
|||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; |
|||
import org.apache.ibatis.annotations.Mapper; |
|||
|
|||
/** |
|||
* 学生联系人 Mapper |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@Mapper |
|||
public interface InfraStudentContactMapper extends BaseMapperX<InfraStudentContactDO> { |
|||
|
|||
default List<InfraStudentContactDO> selectListByStudentId(Long studentId) { |
|||
return selectList(InfraStudentContactDO::getStudentId, studentId); |
|||
} |
|||
|
|||
default int deleteByStudentId(Long studentId) { |
|||
return delete(InfraStudentContactDO::getStudentId, studentId); |
|||
} |
|||
|
|||
} |
@ -1,117 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.controller.admin.demo; |
|||
|
|||
import org.springframework.web.bind.annotation.*; |
|||
import javax.annotation.Resource; |
|||
import org.springframework.validation.annotation.Validated; |
|||
import org.springframework.security.access.prepost.PreAuthorize; |
|||
import io.swagger.v3.oas.annotations.tags.Tag; |
|||
import io.swagger.v3.oas.annotations.Parameter; |
|||
import io.swagger.v3.oas.annotations.Operation; |
|||
|
|||
import javax.validation.constraints.*; |
|||
import javax.validation.*; |
|||
import javax.servlet.http.*; |
|||
import java.util.*; |
|||
import java.io.IOException; |
|||
|
|||
import cn.iocoder.yudao.framework.common.pojo.PageParam; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
|||
import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
|||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
|||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
|||
|
|||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; |
|||
|
|||
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; |
|||
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; |
|||
|
|||
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; |
|||
import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService; |
|||
|
|||
@Tag(name = "管理后台 - 学生") |
|||
@RestController |
|||
@RequestMapping("/infra/student") |
|||
@Validated |
|||
public class InfraStudentController { |
|||
|
|||
@Resource |
|||
private InfraStudentService studentService; |
|||
|
|||
@PostMapping("/create") |
|||
@Operation(summary = "创建学生") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:create')") |
|||
public CommonResult<Long> createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) { |
|||
return success(studentService.createStudent(createReqVO)); |
|||
} |
|||
|
|||
@PutMapping("/update") |
|||
@Operation(summary = "更新学生") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:update')") |
|||
public CommonResult<Boolean> updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) { |
|||
studentService.updateStudent(updateReqVO); |
|||
return success(true); |
|||
} |
|||
|
|||
@DeleteMapping("/delete") |
|||
@Operation(summary = "删除学生") |
|||
@Parameter(name = "id", description = "编号", required = true) |
|||
@PreAuthorize("@ss.hasPermission('infra:student:delete')") |
|||
public CommonResult<Boolean> deleteStudent(@RequestParam("id") Long id) { |
|||
studentService.deleteStudent(id); |
|||
return success(true); |
|||
} |
|||
|
|||
@GetMapping("/get") |
|||
@Operation(summary = "获得学生") |
|||
@Parameter(name = "id", description = "编号", required = true, example = "1024") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:query')") |
|||
public CommonResult<InfraStudentRespVO> getStudent(@RequestParam("id") Long id) { |
|||
InfraStudentDO student = studentService.getStudent(id); |
|||
return success(BeanUtils.toBean(student, InfraStudentRespVO.class)); |
|||
} |
|||
|
|||
@GetMapping("/page") |
|||
@Operation(summary = "获得学生分页") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:query')") |
|||
public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) { |
|||
PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO); |
|||
return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class)); |
|||
} |
|||
|
|||
@GetMapping("/export-excel") |
|||
@Operation(summary = "导出学生 Excel") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:export')") |
|||
@OperateLog(type = EXPORT) |
|||
public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO, |
|||
HttpServletResponse response) throws IOException { |
|||
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); |
|||
List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList(); |
|||
// 导出 Excel |
|||
ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class, |
|||
BeanUtils.toBean(list, InfraStudentRespVO.class)); |
|||
} |
|||
|
|||
// ==================== 子表(学生联系人) ==================== |
|||
|
|||
@GetMapping("/student-contact/list-by-student-id") |
|||
@Operation(summary = "获得学生联系人列表") |
|||
@Parameter(name = "studentId", description = "学生编号") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:query')") |
|||
public CommonResult<List<InfraStudentContactDO>> getStudentContactListByStudentId(@RequestParam("studentId") Long studentId) { |
|||
return success(studentService.getStudentContactListByStudentId(studentId)); |
|||
} |
|||
|
|||
// ==================== 子表(学生班主任) ==================== |
|||
|
|||
@GetMapping("/student-teacher/get-by-student-id") |
|||
@Operation(summary = "获得学生班主任") |
|||
@Parameter(name = "studentId", description = "学生编号") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:query')") |
|||
public CommonResult<InfraStudentTeacherDO> getStudentTeacherByStudentId(@RequestParam("studentId") Long studentId) { |
|||
return success(studentService.getStudentTeacherByStudentId(studentId)); |
|||
} |
|||
|
|||
} |
@ -1,67 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.dal.dataobject.demo; |
|||
|
|||
import lombok.*; |
|||
import java.util.*; |
|||
import java.time.LocalDateTime; |
|||
import java.time.LocalDateTime; |
|||
import com.baomidou.mybatisplus.annotation.*; |
|||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
|||
|
|||
/** |
|||
* 学生 DO |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@TableName("infra_student") |
|||
@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 |
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
@Builder |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class InfraStudentDO extends BaseDO { |
|||
|
|||
/** |
|||
* 编号 |
|||
*/ |
|||
@TableId |
|||
private Long id; |
|||
/** |
|||
* 名字 |
|||
*/ |
|||
private String name; |
|||
/** |
|||
* 简介 |
|||
*/ |
|||
private String description; |
|||
/** |
|||
* 出生日期 |
|||
*/ |
|||
private LocalDateTime birthday; |
|||
/** |
|||
* 性别 |
|||
* |
|||
* 枚举 {@link TODO system_user_sex 对应的类} |
|||
*/ |
|||
private Integer sex; |
|||
/** |
|||
* 是否有效 |
|||
* |
|||
* 枚举 {@link TODO infra_boolean_string 对应的类} |
|||
*/ |
|||
private Boolean enabled; |
|||
/** |
|||
* 头像 |
|||
*/ |
|||
private String avatar; |
|||
/** |
|||
* 附件 |
|||
*/ |
|||
private String video; |
|||
/** |
|||
* 备注 |
|||
*/ |
|||
private String memo; |
|||
|
|||
} |
@ -1,30 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.dal.mysql.demo; |
|||
|
|||
import java.util.*; |
|||
|
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
|||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
|||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; |
|||
import org.apache.ibatis.annotations.Mapper; |
|||
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; |
|||
|
|||
/** |
|||
* 学生 Mapper |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@Mapper |
|||
public interface InfraStudentMapper extends BaseMapperX<InfraStudentDO> { |
|||
|
|||
default PageResult<InfraStudentDO> selectPage(InfraStudentPageReqVO reqVO) { |
|||
return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentDO>() |
|||
.likeIfPresent(InfraStudentDO::getName, reqVO.getName()) |
|||
.eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday()) |
|||
.eqIfPresent(InfraStudentDO::getSex, reqVO.getSex()) |
|||
.eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled()) |
|||
.betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime()) |
|||
.orderByDesc(InfraStudentDO::getId)); |
|||
} |
|||
|
|||
} |
@ -1,34 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; |
|||
|
|||
import lombok.*; |
|||
import java.util.*; |
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageParam; |
|||
import org.springframework.format.annotation.DateTimeFormat; |
|||
import java.time.LocalDateTime; |
|||
|
|||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; |
|||
|
|||
@Schema(description = "管理后台 - 学生分页 Request VO") |
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
public class InfraStudentPageReqVO extends PageParam { |
|||
|
|||
@Schema(description = "名字", example = "芋头") |
|||
private String name; |
|||
|
|||
@Schema(description = "出生日期") |
|||
private LocalDateTime birthday; |
|||
|
|||
@Schema(description = "性别", example = "1") |
|||
private Integer sex; |
|||
|
|||
@Schema(description = "是否有效", example = "true") |
|||
private Boolean enabled; |
|||
|
|||
@Schema(description = "创建时间") |
|||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) |
|||
private LocalDateTime[] createTime; |
|||
|
|||
} |
@ -1,60 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; |
|||
|
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import lombok.*; |
|||
import java.util.*; |
|||
import java.util.*; |
|||
import org.springframework.format.annotation.DateTimeFormat; |
|||
import java.time.LocalDateTime; |
|||
import com.alibaba.excel.annotation.*; |
|||
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; |
|||
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; |
|||
|
|||
@Schema(description = "管理后台 - 学生 Response VO") |
|||
@Data |
|||
@ExcelIgnoreUnannotated |
|||
public class InfraStudentRespVO { |
|||
|
|||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") |
|||
@ExcelProperty("编号") |
|||
private Long id; |
|||
|
|||
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") |
|||
@ExcelProperty("名字") |
|||
private String name; |
|||
|
|||
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") |
|||
@ExcelProperty("简介") |
|||
private String description; |
|||
|
|||
@Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) |
|||
@ExcelProperty("出生日期") |
|||
private LocalDateTime birthday; |
|||
|
|||
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
|||
@ExcelProperty(value = "性别", converter = DictConvert.class) |
|||
@DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 |
|||
private Integer sex; |
|||
|
|||
@Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") |
|||
@ExcelProperty(value = "是否有效", converter = DictConvert.class) |
|||
@DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 |
|||
private Boolean enabled; |
|||
|
|||
@Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") |
|||
@ExcelProperty("头像") |
|||
private String avatar; |
|||
|
|||
@Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") |
|||
@ExcelProperty("附件") |
|||
private String video; |
|||
|
|||
@Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") |
|||
@ExcelProperty("备注") |
|||
private String memo; |
|||
|
|||
@Schema(description = "创建时间") |
|||
@ExcelProperty("创建时间") |
|||
private LocalDateTime createTime; |
|||
|
|||
} |
@ -1,58 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; |
|||
|
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import lombok.*; |
|||
import java.util.*; |
|||
import javax.validation.constraints.*; |
|||
import java.util.*; |
|||
import org.springframework.format.annotation.DateTimeFormat; |
|||
import java.time.LocalDateTime; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; |
|||
|
|||
@Schema(description = "管理后台 - 学生新增/修改 Request VO") |
|||
@Data |
|||
public class InfraStudentSaveReqVO { |
|||
|
|||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") |
|||
private Long id; |
|||
|
|||
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") |
|||
@NotEmpty(message = "名字不能为空") |
|||
private String name; |
|||
|
|||
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") |
|||
@NotEmpty(message = "简介不能为空") |
|||
private String description; |
|||
|
|||
@Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) |
|||
@NotNull(message = "出生日期不能为空") |
|||
private LocalDateTime birthday; |
|||
|
|||
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
|||
@NotNull(message = "性别不能为空") |
|||
private Integer sex; |
|||
|
|||
@Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") |
|||
@NotNull(message = "是否有效不能为空") |
|||
private Boolean enabled; |
|||
|
|||
@Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") |
|||
@NotEmpty(message = "头像不能为空") |
|||
private String avatar; |
|||
|
|||
@Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") |
|||
@NotEmpty(message = "附件不能为空") |
|||
private String video; |
|||
|
|||
@Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") |
|||
@NotEmpty(message = "备注不能为空") |
|||
private String memo; |
|||
|
|||
@Schema(description = "学生联系人列表") |
|||
private List<InfraStudentContactDO> studentContacts; |
|||
|
|||
@Schema(description = "学生班主任") |
|||
private InfraStudentTeacherDO studentTeacher; |
|||
|
|||
} |
@ -1,77 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.service.demo; |
|||
|
|||
import java.util.*; |
|||
import javax.validation.*; |
|||
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageParam; |
|||
|
|||
/** |
|||
* 学生 Service 接口 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
public interface InfraStudentService { |
|||
|
|||
/** |
|||
* 创建学生 |
|||
* |
|||
* @param createReqVO 创建信息 |
|||
* @return 编号 |
|||
*/ |
|||
Long createStudent(@Valid InfraStudentSaveReqVO createReqVO); |
|||
|
|||
/** |
|||
* 更新学生 |
|||
* |
|||
* @param updateReqVO 更新信息 |
|||
*/ |
|||
void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO); |
|||
|
|||
/** |
|||
* 删除学生 |
|||
* |
|||
* @param id 编号 |
|||
*/ |
|||
void deleteStudent(Long id); |
|||
|
|||
/** |
|||
* 获得学生 |
|||
* |
|||
* @param id 编号 |
|||
* @return 学生 |
|||
*/ |
|||
InfraStudentDO getStudent(Long id); |
|||
|
|||
/** |
|||
* 获得学生分页 |
|||
* |
|||
* @param pageReqVO 分页查询 |
|||
* @return 学生分页 |
|||
*/ |
|||
PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO); |
|||
|
|||
// ==================== 子表(学生联系人) ==================== |
|||
|
|||
/** |
|||
* 获得学生联系人列表 |
|||
* |
|||
* @param studentId 学生编号 |
|||
* @return 学生联系人列表 |
|||
*/ |
|||
List<InfraStudentContactDO> getStudentContactListByStudentId(Long studentId); |
|||
|
|||
// ==================== 子表(学生班主任) ==================== |
|||
|
|||
/** |
|||
* 获得学生班主任 |
|||
* |
|||
* @param studentId 学生编号 |
|||
* @return 学生班主任 |
|||
*/ |
|||
InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId); |
|||
|
|||
} |
@ -1,147 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.service.demo; |
|||
|
|||
import org.springframework.stereotype.Service; |
|||
import javax.annotation.Resource; |
|||
import org.springframework.validation.annotation.Validated; |
|||
import org.springframework.transaction.annotation.Transactional; |
|||
|
|||
import java.util.*; |
|||
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageParam; |
|||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
|||
|
|||
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; |
|||
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper; |
|||
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper; |
|||
|
|||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
|||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; |
|||
|
|||
/** |
|||
* 学生 Service 实现类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@Service |
|||
@Validated |
|||
public class InfraStudentServiceImpl implements InfraStudentService { |
|||
|
|||
@Resource |
|||
private InfraStudentMapper studentMapper; |
|||
@Resource |
|||
private InfraStudentContactMapper studentContactMapper; |
|||
@Resource |
|||
private InfraStudentTeacherMapper studentTeacherMapper; |
|||
|
|||
@Override |
|||
@Transactional(rollbackFor = Exception.class) |
|||
public Long createStudent(InfraStudentSaveReqVO createReqVO) { |
|||
// 插入 |
|||
InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class); |
|||
studentMapper.insert(student); |
|||
|
|||
// 插入子表 |
|||
createStudentContactList(student.getId(), createReqVO.getStudentContacts()); |
|||
createStudentTeacher(student.getId(), createReqVO.getStudentTeacher()); |
|||
// 返回 |
|||
return student.getId(); |
|||
} |
|||
|
|||
@Override |
|||
@Transactional(rollbackFor = Exception.class) |
|||
public void updateStudent(InfraStudentSaveReqVO updateReqVO) { |
|||
// 校验存在 |
|||
validateStudentExists(updateReqVO.getId()); |
|||
// 更新 |
|||
InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class); |
|||
studentMapper.updateById(updateObj); |
|||
|
|||
// 更新子表 |
|||
updateStudentContactList(updateReqVO.getId(), updateReqVO.getStudentContacts()); |
|||
updateStudentTeacher(updateReqVO.getId(), updateReqVO.getStudentTeacher()); |
|||
} |
|||
|
|||
@Override |
|||
@Transactional(rollbackFor = Exception.class) |
|||
public void deleteStudent(Long id) { |
|||
// 校验存在 |
|||
validateStudentExists(id); |
|||
// 删除 |
|||
studentMapper.deleteById(id); |
|||
|
|||
// 删除子表 |
|||
deleteStudentContactByStudentId(id); |
|||
deleteStudentTeacherByStudentId(id); |
|||
} |
|||
|
|||
private void validateStudentExists(Long id) { |
|||
if (studentMapper.selectById(id) == null) { |
|||
throw exception(STUDENT_NOT_EXISTS); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public InfraStudentDO getStudent(Long id) { |
|||
return studentMapper.selectById(id); |
|||
} |
|||
|
|||
@Override |
|||
public PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO) { |
|||
return studentMapper.selectPage(pageReqVO); |
|||
} |
|||
|
|||
// ==================== 子表(学生联系人) ==================== |
|||
|
|||
@Override |
|||
public List<InfraStudentContactDO> getStudentContactListByStudentId(Long studentId) { |
|||
return studentContactMapper.selectListByStudentId(studentId); |
|||
} |
|||
|
|||
private void createStudentContactList(Long studentId, List<InfraStudentContactDO> list) { |
|||
list.forEach(o -> o.setStudentId(studentId)); |
|||
studentContactMapper.insertBatch(list); |
|||
} |
|||
|
|||
private void updateStudentContactList(Long studentId, List<InfraStudentContactDO> list) { |
|||
deleteStudentContactByStudentId(studentId); |
|||
list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下:1)id 冲突;2)updateTime 不更新 |
|||
createStudentContactList(studentId, list); |
|||
} |
|||
|
|||
private void deleteStudentContactByStudentId(Long studentId) { |
|||
studentContactMapper.deleteByStudentId(studentId); |
|||
} |
|||
|
|||
// ==================== 子表(学生班主任) ==================== |
|||
|
|||
@Override |
|||
public InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId) { |
|||
return studentTeacherMapper.selectByStudentId(studentId); |
|||
} |
|||
|
|||
private void createStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) { |
|||
if (studentTeacher == null) { |
|||
return; |
|||
} |
|||
studentTeacher.setStudentId(studentId); |
|||
studentTeacherMapper.insert(studentTeacher); |
|||
} |
|||
|
|||
private void updateStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) { |
|||
if (studentTeacher == null) { |
|||
return; |
|||
} |
|||
studentTeacher.setStudentId(studentId); |
|||
studentTeacher.setUpdater(null).setUpdateTime(null); // 解决更新情况下:updateTime 不更新 |
|||
studentTeacherMapper.insertOrUpdate(studentTeacher); |
|||
} |
|||
|
|||
private void deleteStudentTeacherByStudentId(Long studentId) { |
|||
studentTeacherMapper.deleteByStudentId(studentId); |
|||
} |
|||
|
|||
} |
@ -1,146 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.service.demo; |
|||
|
|||
import org.junit.jupiter.api.Disabled; |
|||
import org.junit.jupiter.api.Test; |
|||
import org.springframework.boot.test.mock.mockito.MockBean; |
|||
|
|||
import javax.annotation.Resource; |
|||
|
|||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
|||
|
|||
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; |
|||
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
|||
|
|||
import javax.annotation.Resource; |
|||
import org.springframework.context.annotation.Import; |
|||
import java.util.*; |
|||
import java.time.LocalDateTime; |
|||
|
|||
import static cn.hutool.core.util.RandomUtil.*; |
|||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; |
|||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; |
|||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; |
|||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; |
|||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; |
|||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; |
|||
import static org.junit.jupiter.api.Assertions.*; |
|||
import static org.mockito.Mockito.*; |
|||
|
|||
/** |
|||
* {@link InfraStudentServiceImpl} 的单元测试类 |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@Import(InfraStudentServiceImpl.class) |
|||
public class InfraStudentServiceImplTest extends BaseDbUnitTest { |
|||
|
|||
@Resource |
|||
private InfraStudentServiceImpl studentService; |
|||
|
|||
@Resource |
|||
private InfraStudentMapper studentMapper; |
|||
|
|||
@Test |
|||
public void testCreateStudent_success() { |
|||
// 准备参数 |
|||
InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null); |
|||
|
|||
// 调用 |
|||
Long studentId = studentService.createStudent(createReqVO); |
|||
// 断言 |
|||
assertNotNull(studentId); |
|||
// 校验记录的属性是否正确 |
|||
InfraStudentDO student = studentMapper.selectById(studentId); |
|||
assertPojoEquals(createReqVO, student, "id"); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateStudent_success() { |
|||
// mock 数据 |
|||
InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); |
|||
studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 |
|||
// 准备参数 |
|||
InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> { |
|||
o.setId(dbStudent.getId()); // 设置更新的 ID |
|||
}); |
|||
|
|||
// 调用 |
|||
studentService.updateStudent(updateReqVO); |
|||
// 校验是否更新正确 |
|||
InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的 |
|||
assertPojoEquals(updateReqVO, student); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateStudent_notExists() { |
|||
// 准备参数 |
|||
InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class); |
|||
|
|||
// 调用, 并断言异常 |
|||
assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS); |
|||
} |
|||
|
|||
@Test |
|||
public void testDeleteStudent_success() { |
|||
// mock 数据 |
|||
InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); |
|||
studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 |
|||
// 准备参数 |
|||
Long id = dbStudent.getId(); |
|||
|
|||
// 调用 |
|||
studentService.deleteStudent(id); |
|||
// 校验数据不存在了 |
|||
assertNull(studentMapper.selectById(id)); |
|||
} |
|||
|
|||
@Test |
|||
public void testDeleteStudent_notExists() { |
|||
// 准备参数 |
|||
Long id = randomLongId(); |
|||
|
|||
// 调用, 并断言异常 |
|||
assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS); |
|||
} |
|||
|
|||
@Test |
|||
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 |
|||
public void testGetStudentPage() { |
|||
// mock 数据 |
|||
InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到 |
|||
o.setName(null); |
|||
o.setBirthday(null); |
|||
o.setSex(null); |
|||
o.setEnabled(null); |
|||
o.setCreateTime(null); |
|||
}); |
|||
studentMapper.insert(dbStudent); |
|||
// 测试 name 不匹配 |
|||
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null))); |
|||
// 测试 birthday 不匹配 |
|||
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null))); |
|||
// 测试 sex 不匹配 |
|||
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null))); |
|||
// 测试 enabled 不匹配 |
|||
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null))); |
|||
// 测试 createTime 不匹配 |
|||
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null))); |
|||
// 准备参数 |
|||
InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO(); |
|||
reqVO.setName(null); |
|||
reqVO.setBirthday(null); |
|||
reqVO.setSex(null); |
|||
reqVO.setEnabled(null); |
|||
reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); |
|||
|
|||
// 调用 |
|||
PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(reqVO); |
|||
// 断言 |
|||
assertEquals(1, pageResult.getTotal()); |
|||
assertEquals(1, pageResult.getList().size()); |
|||
assertPojoEquals(dbStudent, pageResult.getList().get(0)); |
|||
} |
|||
|
|||
} |
@ -1,71 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.dal.dataobject.demo; |
|||
|
|||
import lombok.*; |
|||
import java.util.*; |
|||
import java.time.LocalDateTime; |
|||
import java.time.LocalDateTime; |
|||
import com.baomidou.mybatisplus.annotation.*; |
|||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
|||
|
|||
/** |
|||
* 学生班主任 DO |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@TableName("infra_student_teacher") |
|||
@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 |
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
@Builder |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class InfraStudentTeacherDO extends BaseDO { |
|||
|
|||
/** |
|||
* 编号 |
|||
*/ |
|||
@TableId |
|||
private Long id; |
|||
/** |
|||
* 学生编号 |
|||
*/ |
|||
private Long studentId; |
|||
/** |
|||
* 名字 |
|||
*/ |
|||
private String name; |
|||
/** |
|||
* 简介 |
|||
*/ |
|||
private String description; |
|||
/** |
|||
* 出生日期 |
|||
*/ |
|||
private LocalDateTime birthday; |
|||
/** |
|||
* 性别 |
|||
* |
|||
* 枚举 {@link TODO system_user_sex 对应的类} |
|||
*/ |
|||
private Integer sex; |
|||
/** |
|||
* 是否有效 |
|||
* |
|||
* 枚举 {@link TODO infra_boolean_string 对应的类} |
|||
*/ |
|||
private Boolean enabled; |
|||
/** |
|||
* 头像 |
|||
*/ |
|||
private String avatar; |
|||
/** |
|||
* 附件 |
|||
*/ |
|||
private String video; |
|||
/** |
|||
* 备注 |
|||
*/ |
|||
private String memo; |
|||
|
|||
} |
@ -1,28 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.dal.mysql.demo; |
|||
|
|||
import java.util.*; |
|||
|
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageParam; |
|||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
|||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; |
|||
import org.apache.ibatis.annotations.Mapper; |
|||
|
|||
/** |
|||
* 学生班主任 Mapper |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@Mapper |
|||
public interface InfraStudentTeacherMapper extends BaseMapperX<InfraStudentTeacherDO> { |
|||
|
|||
default InfraStudentTeacherDO selectByStudentId(Long studentId) { |
|||
return selectOne(InfraStudentTeacherDO::getStudentId, studentId); |
|||
} |
|||
|
|||
default int deleteByStudentId(Long studentId) { |
|||
return delete(InfraStudentTeacherDO::getStudentId, studentId); |
|||
} |
|||
|
|||
} |
@ -1,74 +0,0 @@ |
|||
import request from '@/utils/request' |
|||
|
|||
// 创建学生 |
|||
export function createStudent(data) { |
|||
return request({ |
|||
url: '/infra/student/create', |
|||
method: 'post', |
|||
data: data |
|||
}) |
|||
} |
|||
|
|||
// 更新学生 |
|||
export function updateStudent(data) { |
|||
return request({ |
|||
url: '/infra/student/update', |
|||
method: 'put', |
|||
data: data |
|||
}) |
|||
} |
|||
|
|||
// 删除学生 |
|||
export function deleteStudent(id) { |
|||
return request({ |
|||
url: '/infra/student/delete?id=' + id, |
|||
method: 'delete' |
|||
}) |
|||
} |
|||
|
|||
// 获得学生 |
|||
export function getStudent(id) { |
|||
return request({ |
|||
url: '/infra/student/get?id=' + id, |
|||
method: 'get' |
|||
}) |
|||
} |
|||
|
|||
// 获得学生分页 |
|||
export function getStudentPage(params) { |
|||
return request({ |
|||
url: '/infra/student/page', |
|||
method: 'get', |
|||
params |
|||
}) |
|||
} |
|||
// 导出学生 Excel |
|||
export function exportStudentExcel(params) { |
|||
return request({ |
|||
url: '/infra/student/export-excel', |
|||
method: 'get', |
|||
params, |
|||
responseType: 'blob' |
|||
}) |
|||
} |
|||
|
|||
// ==================== 子表(学生联系人) ==================== |
|||
|
|||
// 获得学生联系人列表 |
|||
export function getStudentContactListByStudentId(studentId) { |
|||
return request({ |
|||
url: `/infra/student/student-contact/list-by-student-id?studentId=` + studentId, |
|||
method: 'get' |
|||
}) |
|||
} |
|||
|
|||
// ==================== 子表(学生班主任) ==================== |
|||
|
|||
// 获得学生班主任 |
|||
export function getStudentTeacherByStudentId(studentId) { |
|||
return request({ |
|||
url: `/infra/student/student-teacher/get-by-student-id?studentId=` + studentId, |
|||
method: 'get' |
|||
}) |
|||
} |
|||
|
@ -1,17 +0,0 @@ |
|||
-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里 |
|||
CREATE TABLE IF NOT EXISTS "infra_student" ( |
|||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, |
|||
"name" varchar NOT NULL, |
|||
"description" varchar NOT NULL, |
|||
"birthday" varchar NOT NULL, |
|||
"sex" int NOT NULL, |
|||
"enabled" bit NOT NULL, |
|||
"avatar" varchar NOT NULL, |
|||
"video" varchar NOT NULL, |
|||
"memo" varchar NOT NULL, |
|||
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, |
|||
PRIMARY KEY ("id") |
|||
) COMMENT '学生表'; |
|||
|
|||
-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里 |
|||
DELETE FROM "infra_student"; |
@ -1,55 +0,0 @@ |
|||
-- 菜单 SQL |
|||
INSERT INTO system_menu( |
|||
name, permission, type, sort, parent_id, |
|||
path, icon, component, status, component_name |
|||
) |
|||
VALUES ( |
|||
'学生管理', '', 2, 0, 888, |
|||
'student', '', 'infra/demo/index', 0, 'InfraStudent' |
|||
); |
|||
|
|||
-- 按钮父菜单ID |
|||
-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码 |
|||
SELECT @parentId := LAST_INSERT_ID(); |
|||
|
|||
-- 按钮 SQL |
|||
INSERT INTO system_menu( |
|||
name, permission, type, sort, parent_id, |
|||
path, icon, component, status |
|||
) |
|||
VALUES ( |
|||
'学生查询', 'infra:student:query', 3, 1, @parentId, |
|||
'', '', '', 0 |
|||
); |
|||
INSERT INTO system_menu( |
|||
name, permission, type, sort, parent_id, |
|||
path, icon, component, status |
|||
) |
|||
VALUES ( |
|||
'学生创建', 'infra:student:create', 3, 2, @parentId, |
|||
'', '', '', 0 |
|||
); |
|||
INSERT INTO system_menu( |
|||
name, permission, type, sort, parent_id, |
|||
path, icon, component, status |
|||
) |
|||
VALUES ( |
|||
'学生更新', 'infra:student:update', 3, 3, @parentId, |
|||
'', '', '', 0 |
|||
); |
|||
INSERT INTO system_menu( |
|||
name, permission, type, sort, parent_id, |
|||
path, icon, component, status |
|||
) |
|||
VALUES ( |
|||
'学生删除', 'infra:student:delete', 3, 4, @parentId, |
|||
'', '', '', 0 |
|||
); |
|||
INSERT INTO system_menu( |
|||
name, permission, type, sort, parent_id, |
|||
path, icon, component, status |
|||
) |
|||
VALUES ( |
|||
'学生导出', 'infra:student:export', 3, 5, @parentId, |
|||
'', '', '', 0 |
|||
); |
@ -1,177 +0,0 @@ |
|||
<template> |
|||
<div class="app-container"> |
|||
<el-form |
|||
ref="formRef" |
|||
:model="formData" |
|||
:rules="formRules" |
|||
v-loading="formLoading" |
|||
label-width="0px" |
|||
:inline-message="true" |
|||
> |
|||
<el-table :data="formData" class="-mt-10px"> |
|||
<el-table-column label="序号" type="index" width="100" /> |
|||
<el-table-column label="名字" min-width="150"> |
|||
<template v-slot="{ row, $index }"> |
|||
<el-form-item :prop="`${$index}.name`" :rules="formRules.name" class="mb-0px!"> |
|||
<el-input v-model="row.name" placeholder="请输入名字" /> |
|||
</el-form-item> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="简介" min-width="200"> |
|||
<template v-slot="{ row, $index }"> |
|||
<el-form-item :prop="`${$index}.description`" :rules="formRules.description" class="mb-0px!"> |
|||
<el-input v-model="row.description" type="textarea" placeholder="请输入简介" /> |
|||
</el-form-item> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="出生日期" min-width="150"> |
|||
<template v-slot="{ row, $index }"> |
|||
<el-form-item :prop="`${$index}.birthday`" :rules="formRules.birthday" class="mb-0px!"> |
|||
<el-date-picker clearable v-model="row.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" /> |
|||
</el-form-item> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="性别" min-width="150"> |
|||
<template v-slot="{ row, $index }"> |
|||
<el-form-item :prop="`${$index}.sex`" :rules="formRules.sex" class="mb-0px!"> |
|||
<el-select v-model="row.sex" placeholder="请选择性别"> |
|||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)" |
|||
:key="dict.value" :label="dict.label" :value="parseInt(dict.value)" /> |
|||
</el-select> |
|||
</el-form-item> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="是否有效" min-width="150"> |
|||
<template v-slot="{ row, $index }"> |
|||
<el-form-item :prop="`${$index}.enabled`" :rules="formRules.enabled" class="mb-0px!"> |
|||
<el-radio-group v-model="row.enabled"> |
|||
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)" |
|||
:key="dict.value" :label="dict.value">{{dict.label}}</el-radio> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="头像" min-width="200"> |
|||
<template v-slot="{ row, $index }"> |
|||
<el-form-item :prop="`${$index}.avatar`" :rules="formRules.avatar" class="mb-0px!"> |
|||
<ImageUpload v-model="row.avatar"/> |
|||
</el-form-item> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="附件" min-width="200"> |
|||
<template v-slot="{ row, $index }"> |
|||
<el-form-item :prop="`${$index}.video`" :rules="formRules.video" class="mb-0px!"> |
|||
<FileUpload v-model="row.video"/> |
|||
</el-form-item> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="备注" min-width="400"> |
|||
<template v-slot="{ row, $index }"> |
|||
<el-form-item :prop="`${$index}.memo`" :rules="formRules.memo" class="mb-0px!"> |
|||
<Editor v-model="row.memo" :min-height="192"/> |
|||
</el-form-item> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center" fixed="right" label="操作" width="60"> |
|||
<template v-slot="{ $index }"> |
|||
<el-link @click="handleDelete($index)">—</el-link> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
</el-form> |
|||
<el-row justify="center" class="mt-3"> |
|||
<el-button @click="handleAdd" round>+ 添加学生联系人</el-button> |
|||
</el-row> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import * as StudentApi from '@/api/infra/demo'; |
|||
import ImageUpload from '@/components/ImageUpload'; |
|||
import FileUpload from '@/components/FileUpload'; |
|||
import Editor from '@/components/Editor'; |
|||
export default { |
|||
name: "StudentContactForm", |
|||
components: { |
|||
ImageUpload, |
|||
FileUpload, |
|||
Editor, |
|||
}, |
|||
props:[ |
|||
'studentId' |
|||
],// 学生编号(主表的关联字段) |
|||
data() { |
|||
return { |
|||
// 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 |
|||
formLoading: false, |
|||
// 表单参数 |
|||
formData: [], |
|||
// 表单校验 |
|||
formRules: { |
|||
studentId: [{ required: true, message: "学生编号不能为空", trigger: "blur" }], |
|||
name: [{ required: true, message: "名字不能为空", trigger: "blur" }], |
|||
description: [{ required: true, message: "简介不能为空", trigger: "blur" }], |
|||
birthday: [{ required: true, message: "出生日期不能为空", trigger: "blur" }], |
|||
sex: [{ required: true, message: "性别不能为空", trigger: "change" }], |
|||
enabled: [{ required: true, message: "是否有效不能为空", trigger: "blur" }], |
|||
avatar: [{ required: true, message: "头像不能为空", trigger: "blur" }], |
|||
memo: [{ required: true, message: "备注不能为空", trigger: "blur" }], |
|||
}, |
|||
}; |
|||
}, |
|||
watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */ |
|||
studentId:{ |
|||
handler(val) { |
|||
// 1. 重置表单 |
|||
this.formData = [] |
|||
// 2. val 非空,则加载数据 |
|||
if (!val) { |
|||
return; |
|||
} |
|||
try { |
|||
this.formLoading = true; |
|||
// 这里还是需要获取一下 this 的不然取不到 formData |
|||
const that = this; |
|||
StudentApi.getStudentContactListByStudentId(val).then(function (res){ |
|||
that.formData = res.data; |
|||
}) |
|||
} finally { |
|||
this.formLoading = false; |
|||
} |
|||
}, |
|||
immediate: true |
|||
} |
|||
}, |
|||
methods: { |
|||
/** 新增按钮操作 */ |
|||
handleAdd() { |
|||
const row = { |
|||
id: undefined, |
|||
studentId: undefined, |
|||
name: undefined, |
|||
description: undefined, |
|||
birthday: undefined, |
|||
sex: undefined, |
|||
enabled: undefined, |
|||
avatar: undefined, |
|||
video: undefined, |
|||
memo: undefined, |
|||
} |
|||
row.studentId = this.studentId; |
|||
this.formData.push(row); |
|||
}, |
|||
/** 删除按钮操作 */ |
|||
handleDelete(index) { |
|||
this.formData.splice(index, 1); |
|||
}, |
|||
/** 表单校验 */ |
|||
validate(){ |
|||
return this.$refs["formRef"].validate(); |
|||
}, |
|||
/** 表单值 */ |
|||
getData(){ |
|||
return this.formData; |
|||
} |
|||
} |
|||
}; |
|||
</script> |
@ -1,89 +0,0 @@ |
|||
<template> |
|||
<div class="app-container"> |
|||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> |
|||
<el-table-column label="编号" align="center" prop="id" /> |
|||
<el-table-column label="名字" align="center" prop="name" /> |
|||
<el-table-column label="简介" align="center" prop="description" /> |
|||
<el-table-column label="出生日期" align="center" prop="birthday" width="180"> |
|||
<template v-slot="scope"> |
|||
<span>{{ parseTime(scope.row.birthday) }}</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="性别" align="center" prop="sex"> |
|||
<template v-slot="scope"> |
|||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="是否有效" align="center" prop="enabled"> |
|||
<template v-slot="scope"> |
|||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.enabled" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="头像" align="center" prop="avatar" /> |
|||
<el-table-column label="附件" align="center" prop="video" /> |
|||
<el-table-column label="备注" align="center" prop="memo" /> |
|||
<el-table-column label="创建时间" align="center" prop="createTime" width="180"> |
|||
<template v-slot="scope"> |
|||
<span>{{ parseTime(scope.row.createTime) }}</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> |
|||
<template v-slot="scope"> |
|||
<el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)" |
|||
v-hasPermi="['infra:student:update']">修改</el-button> |
|||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" |
|||
v-hasPermi="['infra:student:delete']">删除</el-button> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import * as StudentApi from '@/api/infra/demo'; |
|||
export default { |
|||
name: "StudentContactList", |
|||
props:[ |
|||
'studentId' |
|||
],// 学生编号(主表的关联字段) |
|||
data() { |
|||
return { |
|||
// 遮罩层 |
|||
loading: true, |
|||
// 列表的数据 |
|||
list: [], |
|||
}; |
|||
}, |
|||
created() { |
|||
this.getList(); |
|||
}, |
|||
watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */ |
|||
studentId:{ |
|||
handler(val) { |
|||
this.queryParams.studentId = val; |
|||
if (val){ |
|||
this.handleQuery(); |
|||
} |
|||
}, |
|||
immediate: true |
|||
} |
|||
}, |
|||
methods: { |
|||
/** 查询列表 */ |
|||
async getList() { |
|||
try { |
|||
this.loading = true; |
|||
const res = await StudentApi.getStudentContactListByStudentId(this.studentId); |
|||
this.list = res.data; |
|||
} finally { |
|||
this.loading = false; |
|||
} |
|||
}, |
|||
/** 搜索按钮操作 */ |
|||
handleQuery() { |
|||
this.queryParams.pageNo = 1; |
|||
this.getList(); |
|||
}, |
|||
} |
|||
}; |
|||
</script> |
@ -1,180 +0,0 @@ |
|||
<template> |
|||
<div class="app-container"> |
|||
<!-- 对话框(添加 / 修改) --> |
|||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="45%" v-dialogDrag append-to-body> |
|||
<el-form ref="formRef" :model="formData" :rules="formRules" v-loading="formLoading" label-width="100px"> |
|||
<el-form-item label="名字" prop="name"> |
|||
<el-input v-model="formData.name" placeholder="请输入名字" /> |
|||
</el-form-item> |
|||
<el-form-item label="简介" prop="description"> |
|||
<el-input v-model="formData.description" type="textarea" placeholder="请输入内容" /> |
|||
</el-form-item> |
|||
<el-form-item label="出生日期" prop="birthday"> |
|||
<el-date-picker clearable v-model="formData.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" /> |
|||
</el-form-item> |
|||
<el-form-item label="性别" prop="sex"> |
|||
<el-select v-model="formData.sex" placeholder="请选择性别"> |
|||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)" |
|||
:key="dict.value" :label="dict.label" :value="parseInt(dict.value)" /> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item label="是否有效" prop="enabled"> |
|||
<el-radio-group v-model="formData.enabled"> |
|||
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)" |
|||
:key="dict.value" :label="dict.value">{{dict.label}}</el-radio> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
<el-form-item label="头像"> |
|||
<ImageUpload v-model="formData.avatar"/> |
|||
</el-form-item> |
|||
<el-form-item label="附件"> |
|||
<FileUpload v-model="formData.video"/> |
|||
</el-form-item> |
|||
<el-form-item label="备注"> |
|||
<Editor v-model="formData.memo" :min-height="192"/> |
|||
</el-form-item> |
|||
</el-form> |
|||
<!-- 子表的表单 --> |
|||
<el-tabs v-model="subTabsName"> |
|||
<el-tab-pane label="学生联系人" name="studentContact"> |
|||
<StudentContactForm ref="studentContactFormRef" :student-id="formData.id" /> |
|||
</el-tab-pane> |
|||
<el-tab-pane label="学生班主任" name="studentTeacher"> |
|||
<StudentTeacherForm ref="studentTeacherFormRef" :student-id="formData.id" /> |
|||
</el-tab-pane> |
|||
</el-tabs> |
|||
<div slot="footer" class="dialog-footer"> |
|||
<el-button type="primary" @click="submitForm" :disabled="formLoading">确 定</el-button> |
|||
<el-button @click="dialogVisible = false">取 消</el-button> |
|||
</div> |
|||
</el-dialog> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import * as StudentApi from '@/api/infra/demo'; |
|||
import ImageUpload from '@/components/ImageUpload'; |
|||
import FileUpload from '@/components/FileUpload'; |
|||
import Editor from '@/components/Editor'; |
|||
import StudentContactForm from './components/StudentContactForm.vue' |
|||
import StudentTeacherForm from './components/StudentTeacherForm.vue' |
|||
export default { |
|||
name: "StudentForm", |
|||
components: { |
|||
ImageUpload, |
|||
FileUpload, |
|||
Editor, |
|||
StudentContactForm, |
|||
StudentTeacherForm, |
|||
}, |
|||
data() { |
|||
return { |
|||
// 弹出层标题 |
|||
dialogTitle: "", |
|||
// 是否显示弹出层 |
|||
dialogVisible: false, |
|||
// 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 |
|||
formLoading: false, |
|||
// 表单参数 |
|||
formData: { |
|||
id: undefined, |
|||
name: undefined, |
|||
description: undefined, |
|||
birthday: undefined, |
|||
sex: undefined, |
|||
enabled: undefined, |
|||
avatar: undefined, |
|||
video: undefined, |
|||
memo: undefined, |
|||
}, |
|||
// 表单校验 |
|||
formRules: { |
|||
name: [{ required: true, message: '名字不能为空', trigger: 'blur' }], |
|||
description: [{ required: true, message: '简介不能为空', trigger: 'blur' }], |
|||
birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }], |
|||
sex: [{ required: true, message: '性别不能为空', trigger: 'change' }], |
|||
enabled: [{ required: true, message: '是否有效不能为空', trigger: 'blur' }], |
|||
avatar: [{ required: true, message: '头像不能为空', trigger: 'blur' }], |
|||
video: [{ required: true, message: '附件不能为空', trigger: 'blur' }], |
|||
memo: [{ required: true, message: '备注不能为空', trigger: 'blur' }], |
|||
}, |
|||
/** 子表的表单 */ |
|||
subTabsName: 'studentContact' |
|||
}; |
|||
}, |
|||
methods: { |
|||
/** 打开弹窗 */ |
|||
async open(id) { |
|||
this.dialogVisible = true; |
|||
this.reset(); |
|||
// 修改时,设置数据 |
|||
if (id) { |
|||
this.formLoading = true; |
|||
try { |
|||
const res = await StudentApi.getStudent(id); |
|||
this.formData = res.data; |
|||
this.title = "修改学生"; |
|||
} finally { |
|||
this.formLoading = false; |
|||
} |
|||
} |
|||
this.title = "新增学生"; |
|||
}, |
|||
/** 提交按钮 */ |
|||
async submitForm() { |
|||
// 校验主表 |
|||
await this.$refs["formRef"].validate(); |
|||
// 校验子表 |
|||
try { |
|||
await this.$refs['studentContactFormRef'].validate(); |
|||
} catch (e) { |
|||
this.subTabsName = 'studentContact'; |
|||
return; |
|||
} |
|||
try { |
|||
await this.$refs['studentTeacherFormRef'].validate(); |
|||
} catch (e) { |
|||
this.subTabsName = 'studentTeacher'; |
|||
return; |
|||
} |
|||
this.formLoading = true; |
|||
try { |
|||
const data = this.formData; |
|||
// 拼接子表的数据 |
|||
data.studentContacts = this.$refs['studentContactFormRef'].getData(); |
|||
data.studentTeacher = this.$refs['studentTeacherFormRef'].getData(); |
|||
// 修改的提交 |
|||
if (data.id) { |
|||
await StudentApi.updateStudent(data); |
|||
this.$modal.msgSuccess("修改成功"); |
|||
this.dialogVisible = false; |
|||
this.$emit('success'); |
|||
return; |
|||
} |
|||
// 添加的提交 |
|||
await StudentApi.createStudent(data); |
|||
this.$modal.msgSuccess("新增成功"); |
|||
this.dialogVisible = false; |
|||
this.$emit('success'); |
|||
} finally { |
|||
this.formLoading = false; |
|||
} |
|||
}, |
|||
/** 表单重置 */ |
|||
reset() { |
|||
this.formData = { |
|||
id: undefined, |
|||
name: undefined, |
|||
description: undefined, |
|||
birthday: undefined, |
|||
sex: undefined, |
|||
enabled: undefined, |
|||
avatar: undefined, |
|||
video: undefined, |
|||
memo: undefined, |
|||
}; |
|||
this.resetForm("formRef"); |
|||
} |
|||
} |
|||
}; |
|||
</script> |
@ -1,127 +0,0 @@ |
|||
<template> |
|||
<div class="app-container"> |
|||
<el-form |
|||
ref="formRef" |
|||
:model="formData" |
|||
:rules="formRules" |
|||
label-width="100px" |
|||
v-loading="formLoading" |
|||
> |
|||
<el-form-item label="名字" prop="name"> |
|||
<el-input v-model="formData.name" placeholder="请输入名字" /> |
|||
</el-form-item> |
|||
<el-form-item label="简介" prop="description"> |
|||
<el-input v-model="formData.description" type="textarea" placeholder="请输入简介" /> |
|||
</el-form-item> |
|||
<el-form-item label="出生日期" prop="birthday"> |
|||
<el-date-picker clearable v-model="formData.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" /> |
|||
</el-form-item> |
|||
<el-form-item label="性别" prop="sex"> |
|||
<el-select v-model="formData.sex" placeholder="请选择性别"> |
|||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)" |
|||
:key="dict.value" :label="dict.label" :value="parseInt(dict.value)" /> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item label="是否有效" prop="enabled"> |
|||
<el-radio-group v-model="formData.enabled"> |
|||
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)" |
|||
:key="dict.value" :label="dict.value">{{dict.label}}</el-radio> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
<el-form-item label="头像"> |
|||
<ImageUpload v-model="formData.avatar"/> |
|||
</el-form-item> |
|||
<el-form-item label="附件"> |
|||
<FileUpload v-model="formData.video"/> |
|||
</el-form-item> |
|||
<el-form-item label="备注"> |
|||
<Editor v-model="formData.memo" :min-height="192"/> |
|||
</el-form-item> |
|||
</el-form> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import * as StudentApi from '@/api/infra/demo'; |
|||
import ImageUpload from '@/components/ImageUpload'; |
|||
import FileUpload from '@/components/FileUpload'; |
|||
import Editor from '@/components/Editor'; |
|||
export default { |
|||
name: "StudentTeacherForm", |
|||
components: { |
|||
ImageUpload, |
|||
FileUpload, |
|||
Editor, |
|||
}, |
|||
props:[ |
|||
'studentId' |
|||
],// 学生编号(主表的关联字段) |
|||
data() { |
|||
return { |
|||
// 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 |
|||
formLoading: false, |
|||
// 表单参数 |
|||
formData: [], |
|||
// 表单校验 |
|||
formRules: { |
|||
studentId: [{ required: true, message: "学生编号不能为空", trigger: "blur" }], |
|||
name: [{ required: true, message: "名字不能为空", trigger: "blur" }], |
|||
description: [{ required: true, message: "简介不能为空", trigger: "blur" }], |
|||
birthday: [{ required: true, message: "出生日期不能为空", trigger: "blur" }], |
|||
sex: [{ required: true, message: "性别不能为空", trigger: "change" }], |
|||
enabled: [{ required: true, message: "是否有效不能为空", trigger: "blur" }], |
|||
avatar: [{ required: true, message: "头像不能为空", trigger: "blur" }], |
|||
memo: [{ required: true, message: "备注不能为空", trigger: "blur" }], |
|||
}, |
|||
}; |
|||
}, |
|||
watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */ |
|||
studentId:{ |
|||
handler(val) { |
|||
// 1. 重置表单 |
|||
this.formData = { |
|||
id: undefined, |
|||
studentId: undefined, |
|||
name: undefined, |
|||
description: undefined, |
|||
birthday: undefined, |
|||
sex: undefined, |
|||
enabled: undefined, |
|||
avatar: undefined, |
|||
video: undefined, |
|||
memo: undefined, |
|||
} |
|||
// 2. val 非空,则加载数据 |
|||
if (!val) { |
|||
return; |
|||
} |
|||
try { |
|||
this.formLoading = true; |
|||
// 这里还是需要获取一下 this 的不然取不到 formData |
|||
const that = this; |
|||
StudentApi.getStudentTeacherByStudentId(val).then(function (res){ |
|||
const data = res.data; |
|||
if (!data) { |
|||
return |
|||
} |
|||
that.formData = data; |
|||
}) |
|||
} finally { |
|||
this.formLoading = false; |
|||
} |
|||
}, |
|||
immediate: true |
|||
} |
|||
}, |
|||
methods: { |
|||
/** 表单校验 */ |
|||
validate(){ |
|||
return this.$refs["formRef"].validate(); |
|||
}, |
|||
/** 表单值 */ |
|||
getData(){ |
|||
return this.formData; |
|||
} |
|||
} |
|||
}; |
|||
</script> |
@ -1,93 +0,0 @@ |
|||
<template> |
|||
<div class="app-container"> |
|||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> |
|||
<el-table-column label="编号" align="center" prop="id" /> |
|||
<el-table-column label="名字" align="center" prop="name" /> |
|||
<el-table-column label="简介" align="center" prop="description" /> |
|||
<el-table-column label="出生日期" align="center" prop="birthday" width="180"> |
|||
<template v-slot="scope"> |
|||
<span>{{ parseTime(scope.row.birthday) }}</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="性别" align="center" prop="sex"> |
|||
<template v-slot="scope"> |
|||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="是否有效" align="center" prop="enabled"> |
|||
<template v-slot="scope"> |
|||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.enabled" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="头像" align="center" prop="avatar" /> |
|||
<el-table-column label="附件" align="center" prop="video" /> |
|||
<el-table-column label="备注" align="center" prop="memo" /> |
|||
<el-table-column label="创建时间" align="center" prop="createTime" width="180"> |
|||
<template v-slot="scope"> |
|||
<span>{{ parseTime(scope.row.createTime) }}</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> |
|||
<template v-slot="scope"> |
|||
<el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)" |
|||
v-hasPermi="['infra:student:update']">修改</el-button> |
|||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" |
|||
v-hasPermi="['infra:student:delete']">删除</el-button> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import * as StudentApi from '@/api/infra/demo'; |
|||
export default { |
|||
name: "StudentTeacherList", |
|||
props:[ |
|||
'studentId' |
|||
],// 学生编号(主表的关联字段) |
|||
data() { |
|||
return { |
|||
// 遮罩层 |
|||
loading: true, |
|||
// 列表的数据 |
|||
list: [], |
|||
}; |
|||
}, |
|||
created() { |
|||
this.getList(); |
|||
}, |
|||
watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */ |
|||
studentId:{ |
|||
handler(val) { |
|||
this.queryParams.studentId = val; |
|||
if (val){ |
|||
this.handleQuery(); |
|||
} |
|||
}, |
|||
immediate: true |
|||
} |
|||
}, |
|||
methods: { |
|||
/** 查询列表 */ |
|||
async getList() { |
|||
try { |
|||
this.loading = true; |
|||
const res = await StudentApi.getStudentTeacherByStudentId(this.studentId); |
|||
const data = res.data; |
|||
if (!data) { |
|||
return; |
|||
} |
|||
this.list.push(data); |
|||
} finally { |
|||
this.loading = false; |
|||
} |
|||
}, |
|||
/** 搜索按钮操作 */ |
|||
handleQuery() { |
|||
this.queryParams.pageNo = 1; |
|||
this.getList(); |
|||
}, |
|||
} |
|||
}; |
|||
</script> |
@ -1,222 +0,0 @@ |
|||
<template> |
|||
<div class="app-container"> |
|||
<!-- 搜索工作栏 --> |
|||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px"> |
|||
<el-form-item label="名字" prop="name"> |
|||
<el-input v-model="queryParams.name" placeholder="请输入名字" clearable @keyup.enter.native="handleQuery"/> |
|||
</el-form-item> |
|||
<el-form-item label="出生日期" prop="birthday"> |
|||
<el-date-picker clearable v-model="queryParams.birthday" type="date" value-format="yyyy-MM-dd" placeholder="选择出生日期" /> |
|||
</el-form-item> |
|||
<el-form-item label="性别" prop="sex"> |
|||
<el-select v-model="queryParams.sex" placeholder="请选择性别" clearable size="small"> |
|||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)" |
|||
:key="dict.value" :label="dict.label" :value="dict.value"/> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item label="是否有效" prop="enabled"> |
|||
<el-select v-model="queryParams.enabled" placeholder="请选择是否有效" clearable size="small"> |
|||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)" |
|||
:key="dict.value" :label="dict.label" :value="dict.value"/> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item label="创建时间" prop="createTime"> |
|||
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange" |
|||
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" /> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button> |
|||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
|
|||
<!-- 操作工具栏 --> |
|||
<el-row :gutter="10" class="mb8"> |
|||
<el-col :span="1.5"> |
|||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)" |
|||
v-hasPermi="['infra:student:create']">新增</el-button> |
|||
</el-col> |
|||
<el-col :span="1.5"> |
|||
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading" |
|||
v-hasPermi="['infra:student:export']">导出</el-button> |
|||
</el-col> |
|||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> |
|||
</el-row> |
|||
|
|||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> |
|||
<!-- 子表的列表 --> |
|||
<el-table-column type="expand"> |
|||
<template #default="scope"> |
|||
<el-tabs value="studentContact"> |
|||
<el-tab-pane label="学生联系人" name="studentContact"> |
|||
<StudentContactList :student-id="scope.row.id" /> |
|||
</el-tab-pane> |
|||
<el-tab-pane label="学生班主任" name="studentTeacher"> |
|||
<StudentTeacherList :student-id="scope.row.id" /> |
|||
</el-tab-pane> |
|||
</el-tabs> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="编号" align="center" prop="id"> |
|||
<template v-slot="scope"> |
|||
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.id" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="名字" align="center" prop="name"> |
|||
<template v-slot="scope"> |
|||
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.name" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="简介" align="center" prop="description"> |
|||
<template v-slot="scope"> |
|||
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.description" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="出生日期" align="center" prop="birthday" width="180"> |
|||
<template v-slot="scope"> |
|||
<span>{{ parseTime(scope.row.birthday) }}</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="性别" align="center" prop="sex"> |
|||
<template v-slot="scope"> |
|||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="是否有效" align="center" prop="enabled"> |
|||
<template v-slot="scope"> |
|||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.enabled" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="头像" align="center" prop="avatar"> |
|||
<template v-slot="scope"> |
|||
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.avatar" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="附件" align="center" prop="video"> |
|||
<template v-slot="scope"> |
|||
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.video" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="备注" align="center" prop="memo"> |
|||
<template v-slot="scope"> |
|||
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.memo" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="创建时间" align="center" prop="createTime" width="180"> |
|||
<template v-slot="scope"> |
|||
<span>{{ parseTime(scope.row.createTime) }}</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> |
|||
<template v-slot="scope"> |
|||
<el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)" |
|||
v-hasPermi="['infra:student:update']">修改</el-button> |
|||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" |
|||
v-hasPermi="['infra:student:delete']">删除</el-button> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
<!-- 分页组件 --> |
|||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" |
|||
@pagination="getList"/> |
|||
<!-- 对话框(添加 / 修改) --> |
|||
<StudentForm ref="formRef" @success="getList" /> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import * as StudentApi from '@/api/infra/demo'; |
|||
import StudentForm from './StudentForm.vue'; |
|||
import StudentContactList from './components/StudentContactList.vue'; |
|||
import StudentTeacherList from './components/StudentTeacherList.vue'; |
|||
export default { |
|||
name: "Student", |
|||
components: { |
|||
StudentForm, |
|||
StudentContactList, |
|||
StudentTeacherList, |
|||
}, |
|||
data() { |
|||
return { |
|||
// 遮罩层 |
|||
loading: true, |
|||
// 导出遮罩层 |
|||
exportLoading: false, |
|||
// 显示搜索条件 |
|||
showSearch: true, |
|||
// 总条数 |
|||
total: 0, |
|||
// 学生列表 |
|||
list: [], |
|||
// 是否展开,默认全部展开 |
|||
isExpandAll: true, |
|||
// 重新渲染表格状态 |
|||
refreshTable: true, |
|||
// 选中行 |
|||
currentRow: {}, |
|||
// 查询参数 |
|||
queryParams: { |
|||
pageNo: 1, |
|||
pageSize: 10, |
|||
name: null, |
|||
birthday: null, |
|||
sex: null, |
|||
enabled: null, |
|||
createTime: [], |
|||
}, |
|||
}; |
|||
}, |
|||
created() { |
|||
this.getList(); |
|||
}, |
|||
methods: { |
|||
/** 查询列表 */ |
|||
async getList() { |
|||
try { |
|||
this.loading = true; |
|||
const res = await StudentApi.getStudentPage(this.queryParams); |
|||
this.list = res.data.list; |
|||
this.total = res.data.total; |
|||
} finally { |
|||
this.loading = false; |
|||
} |
|||
}, |
|||
/** 搜索按钮操作 */ |
|||
handleQuery() { |
|||
this.queryParams.pageNo = 1; |
|||
this.getList(); |
|||
}, |
|||
/** 重置按钮操作 */ |
|||
resetQuery() { |
|||
this.resetForm("queryForm"); |
|||
this.handleQuery(); |
|||
}, |
|||
/** 添加/修改操作 */ |
|||
openForm(id) { |
|||
this.$refs["formRef"].open(id); |
|||
}, |
|||
/** 删除按钮操作 */ |
|||
async handleDelete(row) { |
|||
const id = row.id; |
|||
await this.$modal.confirm('是否确认删除学生编号为"' + id + '"的数据项?') |
|||
try { |
|||
await StudentApi.deleteStudent(id); |
|||
await this.getList(); |
|||
this.$modal.msgSuccess("删除成功"); |
|||
} catch {} |
|||
}, |
|||
/** 导出按钮操作 */ |
|||
async handleExport() { |
|||
await this.$modal.confirm('是否确认导出所有学生数据项?'); |
|||
try { |
|||
this.exportLoading = true; |
|||
const res = await StudentApi.exportStudentExcel(this.queryParams); |
|||
this.$download.excel(res.data, '学生.xls'); |
|||
} catch { |
|||
} finally { |
|||
this.exportLoading = false; |
|||
} |
|||
}, |
|||
} |
|||
}; |
|||
</script> |
@ -1,12 +0,0 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
|||
<mapper namespace="cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper"> |
|||
|
|||
<!-- |
|||
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。 |
|||
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。 |
|||
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。 |
|||
文档可见:https://www.iocoder.cn/MyBatis/x-plugins/ |
|||
--> |
|||
|
|||
</mapper> |
@ -1,67 +0,0 @@ |
|||
[ { |
|||
"contentPath" : "java/InfraStudentPageReqVO", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentRespVO", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentSaveReqVO", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentController", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentDO", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentContactDO", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentTeacherDO", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentMapper", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentContactMapper", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentTeacherMapper", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java" |
|||
}, { |
|||
"contentPath" : "xml/InfraStudentMapper", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentServiceImpl", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentService", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java" |
|||
}, { |
|||
"contentPath" : "java/InfraStudentServiceImplTest", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java" |
|||
}, { |
|||
"contentPath" : "java/ErrorCodeConstants_手动操作", |
|||
"filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java" |
|||
}, { |
|||
"contentPath" : "sql/sql", |
|||
"filePath" : "sql/sql.sql" |
|||
}, { |
|||
"contentPath" : "sql/h2", |
|||
"filePath" : "sql/h2.sql" |
|||
}, { |
|||
"contentPath" : "vue/index", |
|||
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue" |
|||
}, { |
|||
"contentPath": "js/index", |
|||
"filePath": "yudao-ui-admin-vue2/src/api/infra/demo/index.js" |
|||
}, { |
|||
"contentPath" : "vue/StudentForm", |
|||
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/StudentForm.vue" |
|||
}, { |
|||
"contentPath" : "vue/StudentContactForm", |
|||
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactForm.vue" |
|||
}, { |
|||
"contentPath" : "vue/StudentTeacherForm", |
|||
"filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherForm.vue" |
|||
} ] |
@ -1,3 +0,0 @@ |
|||
// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!! |
|||
// ========== 学生 TODO 补充编号 ========== |
|||
ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在"); |
@ -1,71 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.dal.dataobject.demo; |
|||
|
|||
import lombok.*; |
|||
import java.util.*; |
|||
import java.time.LocalDateTime; |
|||
import java.time.LocalDateTime; |
|||
import com.baomidou.mybatisplus.annotation.*; |
|||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
|||
|
|||
/** |
|||
* 学生联系人 DO |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@TableName("infra_student_contact") |
|||
@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 |
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
@Builder |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class InfraStudentContactDO extends BaseDO { |
|||
|
|||
/** |
|||
* 编号 |
|||
*/ |
|||
@TableId |
|||
private Long id; |
|||
/** |
|||
* 学生编号 |
|||
*/ |
|||
private Long studentId; |
|||
/** |
|||
* 名字 |
|||
*/ |
|||
private String name; |
|||
/** |
|||
* 简介 |
|||
*/ |
|||
private String description; |
|||
/** |
|||
* 出生日期 |
|||
*/ |
|||
private LocalDateTime birthday; |
|||
/** |
|||
* 性别 |
|||
* |
|||
* 枚举 {@link TODO system_user_sex 对应的类} |
|||
*/ |
|||
private Integer sex; |
|||
/** |
|||
* 是否有效 |
|||
* |
|||
* 枚举 {@link TODO infra_boolean_string 对应的类} |
|||
*/ |
|||
private Boolean enabled; |
|||
/** |
|||
* 头像 |
|||
*/ |
|||
private String avatar; |
|||
/** |
|||
* 附件 |
|||
*/ |
|||
private String video; |
|||
/** |
|||
* 备注 |
|||
*/ |
|||
private String memo; |
|||
|
|||
} |
@ -1,28 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.dal.mysql.demo; |
|||
|
|||
import java.util.*; |
|||
|
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageParam; |
|||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
|||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; |
|||
import org.apache.ibatis.annotations.Mapper; |
|||
|
|||
/** |
|||
* 学生联系人 Mapper |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@Mapper |
|||
public interface InfraStudentContactMapper extends BaseMapperX<InfraStudentContactDO> { |
|||
|
|||
default List<InfraStudentContactDO> selectListByStudentId(Long studentId) { |
|||
return selectList(InfraStudentContactDO::getStudentId, studentId); |
|||
} |
|||
|
|||
default int deleteByStudentId(Long studentId) { |
|||
return delete(InfraStudentContactDO::getStudentId, studentId); |
|||
} |
|||
|
|||
} |
@ -1,117 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.controller.admin.demo; |
|||
|
|||
import org.springframework.web.bind.annotation.*; |
|||
import javax.annotation.Resource; |
|||
import org.springframework.validation.annotation.Validated; |
|||
import org.springframework.security.access.prepost.PreAuthorize; |
|||
import io.swagger.v3.oas.annotations.tags.Tag; |
|||
import io.swagger.v3.oas.annotations.Parameter; |
|||
import io.swagger.v3.oas.annotations.Operation; |
|||
|
|||
import javax.validation.constraints.*; |
|||
import javax.validation.*; |
|||
import javax.servlet.http.*; |
|||
import java.util.*; |
|||
import java.io.IOException; |
|||
|
|||
import cn.iocoder.yudao.framework.common.pojo.PageParam; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
|||
import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
|||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
|||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
|||
|
|||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; |
|||
|
|||
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; |
|||
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; |
|||
|
|||
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; |
|||
import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService; |
|||
|
|||
@Tag(name = "管理后台 - 学生") |
|||
@RestController |
|||
@RequestMapping("/infra/student") |
|||
@Validated |
|||
public class InfraStudentController { |
|||
|
|||
@Resource |
|||
private InfraStudentService studentService; |
|||
|
|||
@PostMapping("/create") |
|||
@Operation(summary = "创建学生") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:create')") |
|||
public CommonResult<Long> createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) { |
|||
return success(studentService.createStudent(createReqVO)); |
|||
} |
|||
|
|||
@PutMapping("/update") |
|||
@Operation(summary = "更新学生") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:update')") |
|||
public CommonResult<Boolean> updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) { |
|||
studentService.updateStudent(updateReqVO); |
|||
return success(true); |
|||
} |
|||
|
|||
@DeleteMapping("/delete") |
|||
@Operation(summary = "删除学生") |
|||
@Parameter(name = "id", description = "编号", required = true) |
|||
@PreAuthorize("@ss.hasPermission('infra:student:delete')") |
|||
public CommonResult<Boolean> deleteStudent(@RequestParam("id") Long id) { |
|||
studentService.deleteStudent(id); |
|||
return success(true); |
|||
} |
|||
|
|||
@GetMapping("/get") |
|||
@Operation(summary = "获得学生") |
|||
@Parameter(name = "id", description = "编号", required = true, example = "1024") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:query')") |
|||
public CommonResult<InfraStudentRespVO> getStudent(@RequestParam("id") Long id) { |
|||
InfraStudentDO student = studentService.getStudent(id); |
|||
return success(BeanUtils.toBean(student, InfraStudentRespVO.class)); |
|||
} |
|||
|
|||
@GetMapping("/page") |
|||
@Operation(summary = "获得学生分页") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:query')") |
|||
public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) { |
|||
PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO); |
|||
return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class)); |
|||
} |
|||
|
|||
@GetMapping("/export-excel") |
|||
@Operation(summary = "导出学生 Excel") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:export')") |
|||
@OperateLog(type = EXPORT) |
|||
public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO, |
|||
HttpServletResponse response) throws IOException { |
|||
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); |
|||
List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList(); |
|||
// 导出 Excel |
|||
ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class, |
|||
BeanUtils.toBean(list, InfraStudentRespVO.class)); |
|||
} |
|||
|
|||
// ==================== 子表(学生联系人) ==================== |
|||
|
|||
@GetMapping("/student-contact/list-by-student-id") |
|||
@Operation(summary = "获得学生联系人列表") |
|||
@Parameter(name = "studentId", description = "学生编号") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:query')") |
|||
public CommonResult<List<InfraStudentContactDO>> getStudentContactListByStudentId(@RequestParam("studentId") Long studentId) { |
|||
return success(studentService.getStudentContactListByStudentId(studentId)); |
|||
} |
|||
|
|||
// ==================== 子表(学生班主任) ==================== |
|||
|
|||
@GetMapping("/student-teacher/get-by-student-id") |
|||
@Operation(summary = "获得学生班主任") |
|||
@Parameter(name = "studentId", description = "学生编号") |
|||
@PreAuthorize("@ss.hasPermission('infra:student:query')") |
|||
public CommonResult<InfraStudentTeacherDO> getStudentTeacherByStudentId(@RequestParam("studentId") Long studentId) { |
|||
return success(studentService.getStudentTeacherByStudentId(studentId)); |
|||
} |
|||
|
|||
} |
@ -1,67 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.dal.dataobject.demo; |
|||
|
|||
import lombok.*; |
|||
import java.util.*; |
|||
import java.time.LocalDateTime; |
|||
import java.time.LocalDateTime; |
|||
import com.baomidou.mybatisplus.annotation.*; |
|||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
|||
|
|||
/** |
|||
* 学生 DO |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@TableName("infra_student") |
|||
@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 |
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
@Builder |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class InfraStudentDO extends BaseDO { |
|||
|
|||
/** |
|||
* 编号 |
|||
*/ |
|||
@TableId |
|||
private Long id; |
|||
/** |
|||
* 名字 |
|||
*/ |
|||
private String name; |
|||
/** |
|||
* 简介 |
|||
*/ |
|||
private String description; |
|||
/** |
|||
* 出生日期 |
|||
*/ |
|||
private LocalDateTime birthday; |
|||
/** |
|||
* 性别 |
|||
* |
|||
* 枚举 {@link TODO system_user_sex 对应的类} |
|||
*/ |
|||
private Integer sex; |
|||
/** |
|||
* 是否有效 |
|||
* |
|||
* 枚举 {@link TODO infra_boolean_string 对应的类} |
|||
*/ |
|||
private Boolean enabled; |
|||
/** |
|||
* 头像 |
|||
*/ |
|||
private String avatar; |
|||
/** |
|||
* 附件 |
|||
*/ |
|||
private String video; |
|||
/** |
|||
* 备注 |
|||
*/ |
|||
private String memo; |
|||
|
|||
} |
@ -1,30 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.dal.mysql.demo; |
|||
|
|||
import java.util.*; |
|||
|
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult; |
|||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
|||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
|||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; |
|||
import org.apache.ibatis.annotations.Mapper; |
|||
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; |
|||
|
|||
/** |
|||
* 学生 Mapper |
|||
* |
|||
* @author 芋道源码 |
|||
*/ |
|||
@Mapper |
|||
public interface InfraStudentMapper extends BaseMapperX<InfraStudentDO> { |
|||
|
|||
default PageResult<InfraStudentDO> selectPage(InfraStudentPageReqVO reqVO) { |
|||
return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentDO>() |
|||
.likeIfPresent(InfraStudentDO::getName, reqVO.getName()) |
|||
.eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday()) |
|||
.eqIfPresent(InfraStudentDO::getSex, reqVO.getSex()) |
|||
.eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled()) |
|||
.betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime()) |
|||
.orderByDesc(InfraStudentDO::getId)); |
|||
} |
|||
|
|||
} |
@ -1,34 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; |
|||
|
|||
import lombok.*; |
|||
import java.util.*; |
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import cn.iocoder.yudao.framework.common.pojo.PageParam; |
|||
import org.springframework.format.annotation.DateTimeFormat; |
|||
import java.time.LocalDateTime; |
|||
|
|||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; |
|||
|
|||
@Schema(description = "管理后台 - 学生分页 Request VO") |
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
public class InfraStudentPageReqVO extends PageParam { |
|||
|
|||
@Schema(description = "名字", example = "芋头") |
|||
private String name; |
|||
|
|||
@Schema(description = "出生日期") |
|||
private LocalDateTime birthday; |
|||
|
|||
@Schema(description = "性别", example = "1") |
|||
private Integer sex; |
|||
|
|||
@Schema(description = "是否有效", example = "true") |
|||
private Boolean enabled; |
|||
|
|||
@Schema(description = "创建时间") |
|||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) |
|||
private LocalDateTime[] createTime; |
|||
|
|||
} |
@ -1,60 +0,0 @@ |
|||
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; |
|||
|
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import lombok.*; |
|||
import java.util.*; |
|||
import java.util.*; |
|||
import org.springframework.format.annotation.DateTimeFormat; |
|||
import java.time.LocalDateTime; |
|||
import com.alibaba.excel.annotation.*; |
|||
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; |
|||
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; |
|||
|
|||
@Schema(description = "管理后台 - 学生 Response VO") |
|||
@Data |
|||
@ExcelIgnoreUnannotated |
|||
public class InfraStudentRespVO { |
|||
|
|||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") |
|||
@ExcelProperty("编号") |
|||
private Long id; |
|||
|
|||
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") |
|||
@ExcelProperty("名字") |
|||
private String name; |
|||
|
|||
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") |
|||
@ExcelProperty("简介") |
|||
private String description; |
|||
|
|||
@Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) |
|||
@ExcelProperty("出生日期") |
|||
private LocalDateTime birthday; |
|||
|
|||
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
|||
@ExcelProperty(value = "性别", converter = DictConvert.class) |
|||
@DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 |
|||
private Integer sex; |
|||
|
|||
@Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") |
|||
@ExcelProperty(value = "是否有效", converter = DictConvert.class) |
|||
@DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 |
|||
private Boolean enabled; |
|||
|
|||
@Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") |
|||
@ExcelProperty("头像") |
|||
private String avatar; |
|||
|
|||
@Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") |
|||
@ExcelProperty("附件") |
|||
private String video; |
|||
|
|||
@Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") |
|||
@ExcelProperty("备注") |
|||
private String memo; |
|||
|
|||
@Schema(description = "创建时间") |
|||
@ExcelProperty("创建时间") |
|||
private LocalDateTime createTime; |
|||
|
|||
} |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue