mirror of
https://github.com/201206030/novel.git
synced 2025-07-19 10:26:39 +00:00
Compare commits
10 Commits
v3.5.0
...
dependabot
Author | SHA1 | Date | |
---|---|---|---|
e800836045 | |||
8de6a865cb | |||
df0c6b84e2 | |||
a23f4b202e | |||
cd3a7206a9 | |||
ab166a392a | |||
9d8709ed2d | |||
60488258f5 | |||
b7bb98db16 | |||
e54b656799 |
@ -40,7 +40,7 @@ novel 是一套基于时下**最新** Java 技术栈 Spring Boot 3 + Vue 3 开
|
|||||||
| 技术 | 版本 | 说明 | 官网 | 学习 |
|
| 技术 | 版本 | 说明 | 官网 | 学习 |
|
||||||
|---------------------|:------------:|-------------------------| ------------------------------------ |:------------------------------------------------------------------------------------------------------------------------:|
|
|---------------------|:------------:|-------------------------| ------------------------------------ |:------------------------------------------------------------------------------------------------------------------------:|
|
||||||
| Spring Boot | 3.3.0 | 容器 + MVC 框架 | [进入](https://spring.io/projects/spring-boot) | [进入](https://docs.spring.io/spring-boot/docs/3.0.0/reference/html) |
|
| Spring Boot | 3.3.0 | 容器 + MVC 框架 | [进入](https://spring.io/projects/spring-boot) | [进入](https://docs.spring.io/spring-boot/docs/3.0.0/reference/html) |
|
||||||
| Spring AI | 1.0.0-SNAPSHOT | Spring 官方 AI 框架 | [进入](https://spring.io/projects/spring-ai) | [进入](https://docs.spring.io/spring-ai/reference/) |
|
| Spring AI | 1.0.0-M6 | Spring 官方 AI 框架 | [进入](https://spring.io/projects/spring-ai) | [进入](https://docs.spring.io/spring-ai/reference/) |
|
||||||
| MyBatis | 3.5.9 | ORM 框架 | [进入](http://www.mybatis.org) | [进入](https://mybatis.org/mybatis-3/zh/index.html) |
|
| MyBatis | 3.5.9 | ORM 框架 | [进入](http://www.mybatis.org) | [进入](https://mybatis.org/mybatis-3/zh/index.html) |
|
||||||
| MyBatis-Plus | 3.5.3 | MyBatis 增强工具 | [进入](https://baomidou.com/) | [进入](https://baomidou.com/pages/24112f/) |
|
| MyBatis-Plus | 3.5.3 | MyBatis 增强工具 | [进入](https://baomidou.com/) | [进入](https://baomidou.com/pages/24112f/) |
|
||||||
| JJWT | 0.11.5 | JWT 登录支持 | [进入](https://github.com/jwtk/jjwt) | - |
|
| JJWT | 0.11.5 | JWT 登录支持 | [进入](https://github.com/jwtk/jjwt) | - |
|
||||||
|
25
pom.xml
25
pom.xml
@ -11,7 +11,7 @@
|
|||||||
</parent>
|
</parent>
|
||||||
<groupId>io.github.xxyopen</groupId>
|
<groupId>io.github.xxyopen</groupId>
|
||||||
<artifactId>novel</artifactId>
|
<artifactId>novel</artifactId>
|
||||||
<version>3.5.0</version>
|
<version>3.5.1-SNAPSHOT</version>
|
||||||
<name>novel</name>
|
<name>novel</name>
|
||||||
<description>Spring Boot 3 + Vue 3 构建的前后端分离小说系统</description>
|
<description>Spring Boot 3 + Vue 3 构建的前后端分离小说系统</description>
|
||||||
<properties>
|
<properties>
|
||||||
@ -19,13 +19,14 @@
|
|||||||
<mybatis-plus.version>3.5.6</mybatis-plus.version>
|
<mybatis-plus.version>3.5.6</mybatis-plus.version>
|
||||||
<mybatis-plus-generator.version>3.5.1</mybatis-plus-generator.version>
|
<mybatis-plus-generator.version>3.5.1</mybatis-plus-generator.version>
|
||||||
<jjwt.version>0.11.5</jjwt.version>
|
<jjwt.version>0.11.5</jjwt.version>
|
||||||
<xxl-job.version>2.3.1</xxl-job.version>
|
<xxl-job.version>2.4.2</xxl-job.version>
|
||||||
<sentinel.version>1.8.4</sentinel.version>
|
<sentinel.version>1.8.4</sentinel.version>
|
||||||
<shardingsphere-jdbc.version>5.5.1</shardingsphere-jdbc.version>
|
<shardingsphere-jdbc.version>5.5.1</shardingsphere-jdbc.version>
|
||||||
<redisson.version>3.19.1</redisson.version>
|
<redisson.version>3.19.1</redisson.version>
|
||||||
<spring-boot-admin.version>3.0.0-M1</spring-boot-admin.version>
|
<spring-boot-admin.version>3.0.0-M1</spring-boot-admin.version>
|
||||||
<springdoc-openapi.version>2.5.0</springdoc-openapi.version>
|
<springdoc-openapi.version>2.5.0</springdoc-openapi.version>
|
||||||
<logbook.version>3.9.0</logbook.version>
|
<logbook.version>3.9.0</logbook.version>
|
||||||
|
<spring-ai.version>1.0.0</spring-ai.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@ -180,7 +181,7 @@
|
|||||||
<!-- AI -->
|
<!-- AI -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.ai</groupId>
|
<groupId>org.springframework.ai</groupId>
|
||||||
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
|
<artifactId>spring-ai-starter-model-openai</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -223,7 +224,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.ai</groupId>
|
<groupId>org.springframework.ai</groupId>
|
||||||
<artifactId>spring-ai-bom</artifactId>
|
<artifactId>spring-ai-bom</artifactId>
|
||||||
<version>1.0.0-SNAPSHOT</version>
|
<version>${spring-ai.version}</version>
|
||||||
<type>pom</type>
|
<type>pom</type>
|
||||||
<scope>import</scope>
|
<scope>import</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
@ -266,22 +267,6 @@
|
|||||||
<enabled>false</enabled>
|
<enabled>false</enabled>
|
||||||
</snapshots>
|
</snapshots>
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
|
||||||
<id>spring-milestones</id>
|
|
||||||
<name>Spring Milestones</name>
|
|
||||||
<url>https://repo.spring.io/milestone</url>
|
|
||||||
<snapshots>
|
|
||||||
<enabled>false</enabled>
|
|
||||||
</snapshots>
|
|
||||||
</repository>
|
|
||||||
<repository>
|
|
||||||
<id>spring-snapshots</id>
|
|
||||||
<name>Spring Snapshots</name>
|
|
||||||
<url>https://repo.spring.io/snapshot</url>
|
|
||||||
<releases>
|
|
||||||
<enabled>false</enabled>
|
|
||||||
</releases>
|
|
||||||
</repository>
|
|
||||||
</repositories>
|
</repositories>
|
||||||
<pluginRepositories>
|
<pluginRepositories>
|
||||||
<pluginRepository>
|
<pluginRepository>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package io.github.xxyopen.novel;
|
package io.github.xxyopen.novel;
|
||||||
|
|
||||||
|
import com.zaxxer.hikari.HikariDataSource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.mybatis.spring.annotation.MapperScan;
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
import org.springframework.boot.CommandLineRunner;
|
import org.springframework.boot.CommandLineRunner;
|
||||||
@ -14,6 +15,8 @@ import org.springframework.scheduling.annotation.EnableScheduling;
|
|||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
import java.sql.Connection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@ -28,7 +31,7 @@ public class NovelApplication {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public CommandLineRunner commandLineRunner(ApplicationContext context) {
|
public CommandLineRunner commandLineRunner(ApplicationContext context, DataSource dataSource) {
|
||||||
return args -> {
|
return args -> {
|
||||||
Map<String, CacheManager> beans = context.getBeansOfType(CacheManager.class);
|
Map<String, CacheManager> beans = context.getBeansOfType(CacheManager.class);
|
||||||
log.info("加载了如下缓存管理器:");
|
log.info("加载了如下缓存管理器:");
|
||||||
@ -36,7 +39,17 @@ public class NovelApplication {
|
|||||||
log.info("{}:{}", k, v.getClass().getName());
|
log.info("{}:{}", k, v.getClass().getName());
|
||||||
log.info("缓存:{}", v.getCacheNames());
|
log.info("缓存:{}", v.getCacheNames());
|
||||||
});
|
});
|
||||||
|
if(dataSource instanceof HikariDataSource hikariDataSource) {
|
||||||
|
// 如果使用的是HikariDataSource,需要提前创建连接池,而不是在第一次访问数据库时才创建,提高第一次访问接口的速度
|
||||||
|
log.info("创建连接池...");
|
||||||
|
try (Connection connection = dataSource.getConnection()) {
|
||||||
|
log.info("最小空闲连接数:{}", hikariDataSource.getMinimumIdle());
|
||||||
|
log.info("最大连接数:{}", hikariDataSource.getMaximumPoolSize());
|
||||||
|
log.info("创建连接池完成.");
|
||||||
|
log.info("数据库:{}", connection.getMetaData().getDatabaseProductName());
|
||||||
|
log.info("数据库版本:{}", connection.getMetaData().getDatabaseProductVersion());
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
package io.github.xxyopen.novel.core.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义注解,用于标记需要进行排序字段(sort)和排序方式(order)校验的方法参数。
|
||||||
|
* <p>
|
||||||
|
* 在接收到请求参数时,可通过 AOP 对标注了该注解的参数进行统一处理, 校验并防止 SQL 注入等安全问题。
|
||||||
|
*
|
||||||
|
* @author xiongxiaoyang
|
||||||
|
* @date 2025/7/17
|
||||||
|
*/
|
||||||
|
@Target(ElementType.PARAMETER) // 表示该注解只能用于方法参数上
|
||||||
|
@Retention(RetentionPolicy.RUNTIME) // 注解在运行时依然可用,便于 AOP 或其他框架读取
|
||||||
|
@Documented // 该注解将被包含在生成的 Javadoc 中
|
||||||
|
public @interface ValidateSortOrder {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,130 @@
|
|||||||
|
package io.github.xxyopen.novel.core.aspect;
|
||||||
|
|
||||||
|
import io.github.xxyopen.novel.core.annotation.ValidateSortOrder;
|
||||||
|
import io.github.xxyopen.novel.core.common.req.PageReqDto;
|
||||||
|
import io.github.xxyopen.novel.core.common.util.SortWhitelistUtil;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.Around;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.aspectj.lang.reflect.MethodSignature;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序字段和排序方式的安全校验切面类
|
||||||
|
* <p>
|
||||||
|
* 该切面用于拦截所有 Mapper 方法的调用,对带有 @ValidateSortOrder 注解的参数进行统一处理,
|
||||||
|
* 校验并清理其中的排序字段(sort)和排序方式(order)参数,防止 SQL 注入攻击。
|
||||||
|
* <p>
|
||||||
|
* 支持处理以下类型的参数:
|
||||||
|
* - PageReqDto 类型对象
|
||||||
|
* - Map 类型参数
|
||||||
|
* - 任意带有 sort/order 字段的 POJO 对象
|
||||||
|
*
|
||||||
|
* @author xiongxiaoyang
|
||||||
|
* @date 2025/7/17
|
||||||
|
*/
|
||||||
|
@Aspect
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SortOrderValidationAspect {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拦截所有 Mapper 方法的调用,检查参数中是否包含 @ValidateSortOrder 注解。
|
||||||
|
* 如果有,则对参数中的 sort 和 order 字段进行安全校验和清理。
|
||||||
|
*/
|
||||||
|
@SneakyThrows
|
||||||
|
@Around("execution(* io.github.xxyopen.*.dao.mapper.*Mapper.*(..))")
|
||||||
|
public Object processSortOrderFields(ProceedingJoinPoint joinPoint) {
|
||||||
|
Object[] args = joinPoint.getArgs();
|
||||||
|
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||||
|
Method method = signature.getMethod();
|
||||||
|
|
||||||
|
// 获取方法参数上的所有注解
|
||||||
|
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
|
||||||
|
|
||||||
|
// 遍历所有参数,检查是否有 @ValidateSortOrder 注解
|
||||||
|
for (int i = 0; i < parameterAnnotations.length; i++) {
|
||||||
|
boolean hasAnnotation = Arrays.stream(parameterAnnotations[i])
|
||||||
|
.anyMatch(a -> a.annotationType().equals(ValidateSortOrder.class));
|
||||||
|
|
||||||
|
if (hasAnnotation && args[i] != null) {
|
||||||
|
// 对带注解的参数进行处理
|
||||||
|
handleAnnotatedParameter(args[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 继续执行原方法
|
||||||
|
return joinPoint.proceed(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据参数类型,分别处理不同形式的 sort/order 字段。
|
||||||
|
*/
|
||||||
|
@SneakyThrows
|
||||||
|
private void handleAnnotatedParameter(Object obj) {
|
||||||
|
if (obj instanceof PageReqDto dto) {
|
||||||
|
processPageReqDto(dto);
|
||||||
|
} else if (obj instanceof Map<?, ?> map) {
|
||||||
|
processMap(map);
|
||||||
|
} else {
|
||||||
|
processGenericObject(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理 PageReqDto 类型参数中的 sort 和 order 字段。
|
||||||
|
*/
|
||||||
|
private void processPageReqDto(PageReqDto dto) {
|
||||||
|
if (dto.getSort() != null) {
|
||||||
|
dto.setSort(SortWhitelistUtil.sanitizeColumn(dto.getSort()));
|
||||||
|
}
|
||||||
|
if (dto.getOrder() != null) {
|
||||||
|
dto.setOrder(SortWhitelistUtil.sanitizeOrder(dto.getOrder()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理 Map 类型参数中的 sort 和 order 字段。
|
||||||
|
*/
|
||||||
|
private void processMap(Map map) {
|
||||||
|
if (map.get("sort") instanceof String sortStr) {
|
||||||
|
map.put("sort", SortWhitelistUtil.sanitizeColumn(sortStr));
|
||||||
|
}
|
||||||
|
if (map.get("order") instanceof String orderStr) {
|
||||||
|
map.put("order", SortWhitelistUtil.sanitizeOrder(orderStr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用反射处理任意对象中的 sort 和 order 字段。
|
||||||
|
* 支持任何带有这两个字段的 POJO。
|
||||||
|
*/
|
||||||
|
@SneakyThrows
|
||||||
|
private void processGenericObject(Object obj) {
|
||||||
|
for (Field field : obj.getClass().getDeclaredFields()) {
|
||||||
|
switch (field.getName()) {
|
||||||
|
case "sort", "order" -> {
|
||||||
|
field.setAccessible(true);
|
||||||
|
Object value = field.get(obj);
|
||||||
|
if (value instanceof String strValue) {
|
||||||
|
String sanitized = "sort".equals(field.getName())
|
||||||
|
? SortWhitelistUtil.sanitizeColumn(strValue)
|
||||||
|
: SortWhitelistUtil.sanitizeOrder(strValue);
|
||||||
|
field.set(obj, sanitized);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
// 忽略其他字段
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,9 +3,12 @@ package io.github.xxyopen.novel.core.common.exception;
|
|||||||
import io.github.xxyopen.novel.core.common.constant.ErrorCodeEnum;
|
import io.github.xxyopen.novel.core.common.constant.ErrorCodeEnum;
|
||||||
import io.github.xxyopen.novel.core.common.resp.RestResp;
|
import io.github.xxyopen.novel.core.common.resp.RestResp;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.validation.BindException;
|
import org.springframework.validation.BindException;
|
||||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
import org.springframework.web.servlet.resource.NoResourceFoundException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通用的异常处理器
|
* 通用的异常处理器
|
||||||
@ -17,6 +20,15 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
|
|||||||
@RestControllerAdvice
|
@RestControllerAdvice
|
||||||
public class CommonExceptionHandler {
|
public class CommonExceptionHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理404异常
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(NoResourceFoundException.class)
|
||||||
|
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||||
|
public String handlerNotFound() {
|
||||||
|
return "404";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理数据校验异常
|
* 处理数据校验异常
|
||||||
*/
|
*/
|
||||||
|
@ -30,4 +30,16 @@ public class PageReqDto {
|
|||||||
@Parameter(hidden = true)
|
@Parameter(hidden = true)
|
||||||
private boolean fetchAll = false;
|
private boolean fetchAll = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序字段
|
||||||
|
*/
|
||||||
|
@Parameter(description = "排序字段")
|
||||||
|
private String sort;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序方式
|
||||||
|
*/
|
||||||
|
@Parameter(description = "排序方式")
|
||||||
|
private String order;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,63 @@
|
|||||||
|
package io.github.xxyopen.novel.core.common.util;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序字段和排序方式的安全校验工具类
|
||||||
|
* <p>
|
||||||
|
* 用于对请求参数中的排序字段(sort)和排序方式(order)进行白名单校验,
|
||||||
|
* 防止 SQL 注入攻击,确保传入的字段名和排序方式是系统允许的合法值。
|
||||||
|
* <p>
|
||||||
|
* 该工具类使用 Lombok 的 @UtilityClass 注解,确保:
|
||||||
|
* - 无法被实例化
|
||||||
|
* - 所有方法为静态方法
|
||||||
|
*
|
||||||
|
* @author xiongxiaoyang
|
||||||
|
* @date 2025/7/17
|
||||||
|
*/
|
||||||
|
@UtilityClass
|
||||||
|
public class SortWhitelistUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 允许的排序字段白名单集合
|
||||||
|
* 包含系统中允许作为排序依据的数据库字段名。
|
||||||
|
*/
|
||||||
|
private final Set<String> allowedColumns = new HashSet<>(
|
||||||
|
Arrays.asList( "last_chapter_update_time", "word_count", "visit_count"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 允许的排序方式白名单集合
|
||||||
|
* 支持升序(asc)和降序(desc)两种排序方式。
|
||||||
|
*/
|
||||||
|
private final Set<String> allowedOrders = new HashSet<>(Arrays.asList("asc", "desc"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验并清理排序字段
|
||||||
|
* <p>
|
||||||
|
* 如果输入的字段在白名单中,则返回小写形式;
|
||||||
|
* 否则返回默认字段 "id"。
|
||||||
|
*
|
||||||
|
* @param input 用户输入的排序字段
|
||||||
|
* @return 安全的排序字段名
|
||||||
|
*/
|
||||||
|
public String sanitizeColumn(String input) {
|
||||||
|
return allowedColumns.contains(input.toLowerCase()) ? input.toLowerCase() : "id";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验并清理排序方式
|
||||||
|
* <p>
|
||||||
|
* 如果输入的排序方式是 "asc" 或 "desc",则返回其小写形式;
|
||||||
|
* 否则返回默认排序方式 "asc"。
|
||||||
|
*
|
||||||
|
* @param input 用户输入的排序方式
|
||||||
|
* @return 安全的排序方式(asc 或 desc)
|
||||||
|
*/
|
||||||
|
public String sanitizeOrder(String input) {
|
||||||
|
return allowedOrders.contains(input.toLowerCase()) ? input.toLowerCase() : "asc";
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package io.github.xxyopen.novel.dao.mapper;
|
package io.github.xxyopen.novel.dao.mapper;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import io.github.xxyopen.novel.core.annotation.ValidateSortOrder;
|
||||||
import io.github.xxyopen.novel.dao.entity.BookInfo;
|
import io.github.xxyopen.novel.dao.entity.BookInfo;
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import io.github.xxyopen.novel.dto.req.BookSearchReqDto;
|
import io.github.xxyopen.novel.dto.req.BookSearchReqDto;
|
||||||
@ -32,6 +33,6 @@ public interface BookInfoMapper extends BaseMapper<BookInfo> {
|
|||||||
* @param condition 搜索条件
|
* @param condition 搜索条件
|
||||||
* @return 返回结果
|
* @return 返回结果
|
||||||
* */
|
* */
|
||||||
List<BookInfo> searchBooks(IPage<BookInfoRespDto> page, BookSearchReqDto condition);
|
List<BookInfo> searchBooks(IPage<BookInfoRespDto> page, @ValidateSortOrder BookSearchReqDto condition);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -70,9 +70,4 @@ public class BookSearchReqDto extends PageReqDto {
|
|||||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||||
private Date updateTimeMin;
|
private Date updateTimeMin;
|
||||||
|
|
||||||
/**
|
|
||||||
* 排序字段
|
|
||||||
*/
|
|
||||||
@Parameter(description = "排序字段")
|
|
||||||
private String sort;
|
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
and last_chapter_update_time >= #{condition.updateTimeMin}
|
and last_chapter_update_time >= #{condition.updateTimeMin}
|
||||||
</if>
|
</if>
|
||||||
<if test="condition.sort != null">
|
<if test="condition.sort != null">
|
||||||
order by ${condition.sort}
|
order by ${condition.sort} desc
|
||||||
</if>
|
</if>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user