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