mirror of
https://github.com/201206030/novel-plus.git
synced 2025-07-29 22:48:46 +00:00
Merge branch '5.2.x'
This commit is contained in:
commit
99f1bc7859
@ -81,7 +81,7 @@ novel-plus 5.x 已集成 Spring 官方最新发布的 Spring AI 框架,并推
|
||||
|
||||
我们将持续关注 AI 技术的发展,并致力于将其与小说创作场景深度融合,为用户带来更智能、更便捷的创作工具。
|
||||
|
||||
novel-plus 项目默认使用的是第三方大模型服务平台[硅基流动](https://cloud.siliconflow.cn/i/DOgMRH9S)提供的 API(兼容 OpenAI 的相关接口,可直接通过 Spring AI 框架调用),采用的 AI 模型有对话模型`deepseek-ai/DeepSeek-R1-Distill-Llama-8B`(DeepSeek-R1 的蒸馏版本,免费使用)和生图模型`Kwai-Kolors/Kolors`(快手 Kolors 团队开发的文本到图像生成模型,免费使用)。只需注册一个硅基流动账号,创建一个 API 密钥,并将其添加到 novel-plus 项目 novel-front 模块的 yaml 配置文件中,即可体验 novel-plus 项目的 AI 写作功能。
|
||||
novel-plus 项目默认使用的是第三方大模型服务平台[硅基流动](https://cloud.siliconflow.cn/i/DOgMRH9S)提供的 API(兼容 OpenAI 的相关接口,可直接通过 Spring AI 框架调用),采用的 AI 模型有对话模型`deepseek-ai/DeepSeek-R1-0528-Qwen3-8B`(DeepSeek-R1 的蒸馏版本,免费使用)和生图模型`Kwai-Kolors/Kolors`(快手 Kolors 团队开发的文本到图像生成模型,免费使用)。只需注册一个硅基流动账号,创建一个 API 密钥,并将其添加到 novel-plus 项目 novel-front 模块的 yaml 配置文件中,即可体验 novel-plus 项目的 AI 写作功能。
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
@ -98,7 +98,7 @@ spring:
|
||||
base-url: https://api.siliconflow.cn
|
||||
chat:
|
||||
options:
|
||||
model: deepseek-ai/DeepSeek-R1-Distill-Llama-8B
|
||||
model: deepseek-ai/DeepSeek-R1-0528-Qwen3-8B
|
||||
```
|
||||
|
||||
⚠️ novel-plus 项目默认使用的都是免费 AI 模型,生成效果有限。如果对生成内容有更高的要求,建议选用付费的 AI 模型。
|
||||
|
@ -3174,4 +3174,43 @@ CREATE TABLE `book_comment_reply`
|
||||
`create_user_id` bigint(20) DEFAULT NULL COMMENT '回复时间',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT ='小说评论回复表';
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT ='小说评论回复表';
|
||||
|
||||
|
||||
INSERT INTO crawl_source (source_name, crawl_rule, source_status, create_time
|
||||
, update_time)
|
||||
VALUES ('飘天文学网(海外专用)', '{
|
||||
"bookListUrl": "https://www.piaotia.com/booksort{catId}/{page}.html",
|
||||
"catIdRule": {
|
||||
"catId1": "1/0",
|
||||
"catId2": "2/0",
|
||||
"catId3": "3/0",
|
||||
"catId4": "4/0",
|
||||
"catId5": "6/0",
|
||||
"catId6": "5/0"
|
||||
},
|
||||
"bookIdPatten": "href=\\"https://www.piaotia.com/bookinfo/(\\\\d+/\\\\d+).html\\"",
|
||||
"pagePatten": "<em\\\\s+id=\\"pagestats\\">(\\\\d+)/\\\\d+</em>",
|
||||
"totalPagePatten": "<em\\\\s+id=\\"pagestats\\">\\\\d+/(\\\\d+)</em>",
|
||||
"bookDetailUrl": "https://www.piaotia.com/bookinfo/{bookId}.html",
|
||||
"bookNamePatten": "<h1>([^/]+)</h1>",
|
||||
"authorNamePatten": "<td\\\\s+width=\\"\\\\d+%\\">作 者:([^/]+)<",
|
||||
"picUrlPatten": "<img\\\\s+src=\\"(https://www.piaotia.com/files/article/image/[^\\"]+)\\"",
|
||||
"statusPatten": "<td>文章状态:([^/]+)</td>",
|
||||
"bookStatusRule": {
|
||||
"连载中": 0,
|
||||
"已完成": 1
|
||||
},
|
||||
"descStart": " <span class=\\"hottext\\">内容简介:</span><br />",
|
||||
"descEnd": "</td>",
|
||||
"filterDesc": "",
|
||||
"bookIndexUrl": "https://www.piaotia.com/html/{bookId}/index.html",
|
||||
"indexIdPatten": "<li><a href=\\"(\\\\d+).html\\">[^/]+</a></li>",
|
||||
"indexNamePatten": "<li><a href=\\"\\\\d+.html\\">([^/]+)</a></li>",
|
||||
"bookContentUrl": "https://www.piaotia.com/html/{bookId}/{indexId}.html",
|
||||
"contentStart": "<br>",
|
||||
"contentEnd": "</div>",
|
||||
"filterContent": "",
|
||||
"charset": "gbk"
|
||||
}', 0, '2025-07-13 18:57:39'
|
||||
, '2025-07-13 18:57:39');
|
@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>com.java2nb</groupId>
|
||||
<artifactId>novel-admin</artifactId>
|
||||
<version>5.2.2</version>
|
||||
<version>5.2.3</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>novel-admin</name>
|
||||
|
@ -0,0 +1,14 @@
|
||||
package com.java2nb.common.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 标记某个方法参数需要进行 Map 字段的清理和标准化处理。
|
||||
*
|
||||
* <p>通常用于 DAO 接口中 list 方法的 Map 参数,用于防止非法排序字段或排序顺序。</p>
|
||||
*/
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface SanitizeMap {
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
package com.java2nb.common.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author xiongxiaoyang
|
||||
* @date 2025/7/17
|
||||
*/
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface ValidateSortOrder {
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.common.aspect;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
import com.java2nb.common.utils.SortWhitelistUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
@ -16,20 +16,33 @@ import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author xiongxiaoyang
|
||||
* @date 2025/7/17
|
||||
* 拦截所有 Mapper 接口的 list* 方法,对带有 @SanitizeMap 注解的 Map 参数进行排序字段和顺序的规范化处理。
|
||||
*
|
||||
* <p>主要防止 SQL 注入或非法排序字段、非法排序顺序的问题。
|
||||
* 例如对 sort 和 order 字段进行白名单过滤和标准化处理。</p>
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class SortOrderValidationAspect {
|
||||
public class MapSortValidationAspect {
|
||||
|
||||
/**
|
||||
* 拦截所有的mapper方法
|
||||
* 拦截所有 Mapper 接口的 list* 方法(如 list(), listByPage 等)。
|
||||
* 对带有 @SanitizeMap 注解的 Map 参数进行处理。
|
||||
*
|
||||
* <p>执行逻辑:</p>
|
||||
* <ol>
|
||||
* <li>获取方法参数及注解信息</li>
|
||||
* <li>遍历所有参数,检查是否带有 @SanitizeMap 注解</li>
|
||||
* <li>如果参数是 Map 类型且有注解,则进行字段清理</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param joinPoint 切点信息
|
||||
* @return 方法执行结果
|
||||
*/
|
||||
@SneakyThrows
|
||||
@Around("execution(* com.java2nb.*.dao.*Dao.*(..))")
|
||||
public Object validateSortAndOrder(ProceedingJoinPoint joinPoint) {
|
||||
@Around("execution(* com.java2nb.*.dao.*Dao.list*(..))")
|
||||
public Object sanitizeMapParameters(ProceedingJoinPoint joinPoint) {
|
||||
Object[] args = joinPoint.getArgs();
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
@ -38,7 +51,7 @@ public class SortOrderValidationAspect {
|
||||
|
||||
for (int i = 0; i < parameterAnnotations.length; i++) {
|
||||
boolean hasAnnotation = Arrays.stream(parameterAnnotations[i])
|
||||
.anyMatch(a -> a.annotationType().equals(ValidateSortOrder.class));
|
||||
.anyMatch(a -> a.annotationType().equals(SanitizeMap.class));
|
||||
|
||||
if (hasAnnotation && args[i] instanceof Map map) {
|
||||
if (map.get("sort") instanceof String sortStr) {
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.common.dao;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
import com.java2nb.common.domain.DictDO;
|
||||
|
||||
import java.util.List;
|
||||
@ -20,7 +20,7 @@ public interface DictDao {
|
||||
|
||||
DictDO get(Long id);
|
||||
|
||||
List<DictDO> list(@ValidateSortOrder Map<String, Object> map);
|
||||
List<DictDO> list(@SanitizeMap Map<String, Object> map);
|
||||
|
||||
int count(Map<String, Object> map);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.common.dao;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
import com.java2nb.common.domain.FileDO;
|
||||
|
||||
import java.util.List;
|
||||
@ -19,7 +19,7 @@ public interface FileDao {
|
||||
|
||||
FileDO get(Long id);
|
||||
|
||||
List<FileDO> list(@ValidateSortOrder Map<String,Object> map);
|
||||
List<FileDO> list(@SanitizeMap Map<String,Object> map);
|
||||
|
||||
int count(Map<String,Object> map);
|
||||
|
||||
|
@ -3,7 +3,7 @@ package com.java2nb.common.dao;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
import com.java2nb.common.domain.GenColumnsDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
@ -20,7 +20,7 @@ public interface GenColumnsDao {
|
||||
|
||||
GenColumnsDO get(Long id);
|
||||
|
||||
List<GenColumnsDO> list(@ValidateSortOrder Map<String,Object> map);
|
||||
List<GenColumnsDO> list(@SanitizeMap Map<String,Object> map);
|
||||
|
||||
int count(Map<String,Object> map);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.common.dao;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
import com.java2nb.common.domain.LogDO;
|
||||
|
||||
import java.util.List;
|
||||
@ -19,7 +19,7 @@ public interface LogDao {
|
||||
|
||||
LogDO get(Long id);
|
||||
|
||||
List<LogDO> list(@ValidateSortOrder Map<String,Object> map);
|
||||
List<LogDO> list(@SanitizeMap Map<String,Object> map);
|
||||
|
||||
int count(Map<String,Object> map);
|
||||
|
||||
|
@ -2,27 +2,42 @@ package com.java2nb.common.utils;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author xiongxiaoyang
|
||||
* @date 2025/7/17
|
||||
* 排序字段和排序顺序的白名单工具类。
|
||||
*/
|
||||
@UtilityClass
|
||||
public class SortWhitelistUtil {
|
||||
|
||||
private final Set<String> allowedColumns = new HashSet<>(
|
||||
Arrays.asList("id", "name", "create_time", "update_time", "order_num", "last_index_update_time", "word_count",
|
||||
"visit_count"));
|
||||
private final Set<String> allowedOrders = new HashSet<>(Arrays.asList("asc", "desc"));
|
||||
// 白名单字段
|
||||
private static final Set<String> ALLOWED_COLUMNS = Set.of("id", "name", "order_num");
|
||||
|
||||
public String sanitizeColumn(String input) {
|
||||
return allowedColumns.contains(input.toLowerCase()) ? input.toLowerCase() : "id";
|
||||
// 白名单排序方式
|
||||
private static final Set<String> ALLOWED_ORDERS = Set.of("asc", "desc");
|
||||
|
||||
/**
|
||||
* 对排序字段进行白名单过滤和标准化。
|
||||
*
|
||||
* @param column 原始字段名
|
||||
* @return 安全的字段名,若非法则返回 null
|
||||
*/
|
||||
public static String sanitizeColumn(String column) {
|
||||
if (column == null) return null;
|
||||
String lower = column.trim().toLowerCase();
|
||||
return ALLOWED_COLUMNS.contains(lower) ? lower : null;
|
||||
}
|
||||
|
||||
public String sanitizeOrder(String input) {
|
||||
return allowedOrders.contains(input.toLowerCase()) ? input.toLowerCase() : "asc";
|
||||
/**
|
||||
* 对排序方式进行白名单过滤和标准化。
|
||||
*
|
||||
* @param order 原始排序方式
|
||||
* @return 安全的排序方式("asc" 或 "desc"),若非法则返回 null
|
||||
*/
|
||||
public static String sanitizeOrder(String order) {
|
||||
if (order == null) return null;
|
||||
String lower = order.trim().toLowerCase();
|
||||
return ALLOWED_ORDERS.contains(lower) ? lower : null;
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.novel.dao;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
|
||||
import com.java2nb.novel.domain.AuthorCodeDO;
|
||||
|
||||
@ -20,7 +20,7 @@ public interface AuthorCodeDao {
|
||||
|
||||
AuthorCodeDO get(Long id);
|
||||
|
||||
List<AuthorCodeDO> list(@ValidateSortOrder Map<String,Object> map);
|
||||
List<AuthorCodeDO> list(@SanitizeMap Map<String,Object> map);
|
||||
|
||||
int count(Map<String,Object> map);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.novel.dao;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
import com.java2nb.novel.domain.AuthorDO;
|
||||
|
||||
import java.util.Date;
|
||||
@ -20,7 +20,7 @@ public interface AuthorDao {
|
||||
|
||||
AuthorDO get(Long id);
|
||||
|
||||
List<AuthorDO> list(@ValidateSortOrder Map<String,Object> map);
|
||||
List<AuthorDO> list(@SanitizeMap Map<String,Object> map);
|
||||
|
||||
int count(Map<String,Object> map);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.novel.dao;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
|
||||
import com.java2nb.novel.domain.BookCommentDO;
|
||||
|
||||
@ -20,7 +20,7 @@ public interface BookCommentDao {
|
||||
|
||||
BookCommentDO get(Long id);
|
||||
|
||||
List<BookCommentDO> list(@ValidateSortOrder Map<String,Object> map);
|
||||
List<BookCommentDO> list(@SanitizeMap Map<String,Object> map);
|
||||
|
||||
int count(Map<String,Object> map);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.novel.dao;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
|
||||
import com.java2nb.novel.domain.BookContentDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
@ -20,7 +20,7 @@ public interface BookContentDao {
|
||||
|
||||
BookContentDO get(Long id);
|
||||
|
||||
List<BookContentDO> list(@ValidateSortOrder Map<String, Object> map);
|
||||
List<BookContentDO> list(@SanitizeMap Map<String, Object> map);
|
||||
|
||||
int count(Map<String, Object> map);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.novel.dao;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
|
||||
import com.java2nb.novel.domain.BookDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
@ -21,7 +21,7 @@ public interface BookDao {
|
||||
|
||||
BookDO get(Long id);
|
||||
|
||||
List<BookDO> list(@ValidateSortOrder Map<String, Object> map);
|
||||
List<BookDO> list(@SanitizeMap Map<String, Object> map);
|
||||
|
||||
int count(Map<String, Object> map);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.novel.dao;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
|
||||
import com.java2nb.novel.domain.BookIndexDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
@ -20,7 +20,7 @@ public interface BookIndexDao {
|
||||
|
||||
BookIndexDO get(Long id);
|
||||
|
||||
List<BookIndexDO> list(@ValidateSortOrder Map<String, Object> map);
|
||||
List<BookIndexDO> list(@SanitizeMap Map<String, Object> map);
|
||||
|
||||
int count(Map<String, Object> map);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.novel.dao;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
|
||||
import com.java2nb.novel.domain.BookSettingDO;
|
||||
|
||||
@ -20,7 +20,7 @@ public interface BookSettingDao {
|
||||
|
||||
BookSettingDO get(Long id);
|
||||
|
||||
List<BookSettingDO> list(@ValidateSortOrder Map<String,Object> map);
|
||||
List<BookSettingDO> list(@SanitizeMap Map<String,Object> map);
|
||||
|
||||
int count(Map<String,Object> map);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.novel.dao;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
|
||||
import com.java2nb.novel.domain.CategoryDO;
|
||||
|
||||
@ -20,7 +20,7 @@ public interface CategoryDao {
|
||||
|
||||
CategoryDO get(Integer id);
|
||||
|
||||
List<CategoryDO> list(@ValidateSortOrder Map<String,Object> map);
|
||||
List<CategoryDO> list(@SanitizeMap Map<String,Object> map);
|
||||
|
||||
int count(Map<String,Object> map);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.novel.dao;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
|
||||
import com.java2nb.novel.domain.FriendLinkDO;
|
||||
|
||||
@ -20,7 +20,7 @@ public interface FriendLinkDao {
|
||||
|
||||
FriendLinkDO get(Integer id);
|
||||
|
||||
List<FriendLinkDO> list(@ValidateSortOrder Map<String,Object> map);
|
||||
List<FriendLinkDO> list(@SanitizeMap Map<String,Object> map);
|
||||
|
||||
int count(Map<String,Object> map);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.novel.dao;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
|
||||
import com.java2nb.novel.domain.NewsDO;
|
||||
|
||||
@ -20,7 +20,7 @@ public interface NewsDao {
|
||||
|
||||
NewsDO get(Long id);
|
||||
|
||||
List<NewsDO> list(@ValidateSortOrder Map<String,Object> map);
|
||||
List<NewsDO> list(@SanitizeMap Map<String,Object> map);
|
||||
|
||||
int count(Map<String,Object> map);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.novel.dao;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
|
||||
import com.java2nb.novel.domain.PayDO;
|
||||
|
||||
@ -21,7 +21,7 @@ public interface PayDao {
|
||||
|
||||
PayDO get(Long id);
|
||||
|
||||
List<PayDO> list(@ValidateSortOrder Map<String,Object> map);
|
||||
List<PayDO> list(@SanitizeMap Map<String,Object> map);
|
||||
|
||||
int count(Map<String,Object> map);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.novel.dao;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
|
||||
import com.java2nb.novel.domain.UserDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
@ -19,7 +19,7 @@ public interface UserDao {
|
||||
|
||||
UserDO get(Long id);
|
||||
|
||||
List<UserDO> list(@ValidateSortOrder Map<String, Object> map);
|
||||
List<UserDO> list(@SanitizeMap Map<String, Object> map);
|
||||
|
||||
int count(Map<String, Object> map);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.novel.dao;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
|
||||
import com.java2nb.novel.domain.UserFeedbackDO;
|
||||
|
||||
@ -20,7 +20,7 @@ public interface UserFeedbackDao {
|
||||
|
||||
UserFeedbackDO get(Long id);
|
||||
|
||||
List<UserFeedbackDO> list(@ValidateSortOrder Map<String,Object> map);
|
||||
List<UserFeedbackDO> list(@SanitizeMap Map<String,Object> map);
|
||||
|
||||
int count(Map<String,Object> map);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.novel.dao;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
|
||||
import com.java2nb.novel.domain.WebsiteInfoDO;
|
||||
|
||||
@ -20,7 +20,7 @@ public interface WebsiteInfoDao {
|
||||
|
||||
WebsiteInfoDO get(Long id);
|
||||
|
||||
List<WebsiteInfoDO> list(@ValidateSortOrder Map<String,Object> map);
|
||||
List<WebsiteInfoDO> list(@SanitizeMap Map<String,Object> map);
|
||||
|
||||
int count(Map<String,Object> map);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.system.dao;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
|
||||
import com.java2nb.system.domain.DataPermDO;
|
||||
|
||||
@ -21,7 +21,7 @@ public interface DataPermDao {
|
||||
|
||||
DataPermDO get(Long id);
|
||||
|
||||
List<DataPermDO> list(@ValidateSortOrder Map<String,Object> map);
|
||||
List<DataPermDO> list(@SanitizeMap Map<String,Object> map);
|
||||
|
||||
int count(Map<String,Object> map);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.system.dao;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
|
||||
import com.java2nb.system.domain.DeptDO;
|
||||
|
||||
@ -21,7 +21,7 @@ public interface DeptDao {
|
||||
|
||||
DeptDO get(Long deptId);
|
||||
|
||||
List<DeptDO> list(@ValidateSortOrder Map<String,Object> map);
|
||||
List<DeptDO> list(@SanitizeMap Map<String,Object> map);
|
||||
|
||||
int count(Map<String,Object> map);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.system.dao;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
|
||||
import com.java2nb.system.domain.MenuDO;
|
||||
|
||||
@ -20,7 +20,7 @@ public interface MenuDao {
|
||||
|
||||
MenuDO get(Long menuId);
|
||||
|
||||
List<MenuDO> list(@ValidateSortOrder Map<String,Object> map);
|
||||
List<MenuDO> list(@SanitizeMap Map<String,Object> map);
|
||||
|
||||
int count(Map<String,Object> map);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.system.dao;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
|
||||
import com.java2nb.system.domain.RoleDO;
|
||||
|
||||
@ -20,7 +20,7 @@ public interface RoleDao {
|
||||
|
||||
RoleDO get(Long roleId);
|
||||
|
||||
List<RoleDO> list(@ValidateSortOrder Map<String,Object> map);
|
||||
List<RoleDO> list(@SanitizeMap Map<String,Object> map);
|
||||
|
||||
int count(Map<String,Object> map);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.system.dao;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
|
||||
import com.java2nb.system.domain.RoleDataPermDO;
|
||||
|
||||
@ -20,7 +20,7 @@ public interface RoleDataPermDao {
|
||||
|
||||
RoleDataPermDO get(Long id);
|
||||
|
||||
List<RoleDataPermDO> list(@ValidateSortOrder Map<String,Object> map);
|
||||
List<RoleDataPermDO> list(@SanitizeMap Map<String,Object> map);
|
||||
|
||||
int count(Map<String,Object> map);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.system.dao;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
|
||||
import com.java2nb.system.domain.RoleMenuDO;
|
||||
|
||||
@ -20,7 +20,7 @@ public interface RoleMenuDao {
|
||||
|
||||
RoleMenuDO get(Long id);
|
||||
|
||||
List<RoleMenuDO> list(@ValidateSortOrder Map<String,Object> map);
|
||||
List<RoleMenuDO> list(@SanitizeMap Map<String,Object> map);
|
||||
|
||||
int count(Map<String,Object> map);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.system.dao;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
|
||||
import com.java2nb.system.domain.UserDO;
|
||||
|
||||
@ -8,10 +8,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author xiongxy
|
||||
* @email 1179705413@qq.com
|
||||
* @date 2019-10-03 09:45:11
|
||||
@ -19,23 +17,23 @@ import org.apache.ibatis.annotations.Param;
|
||||
@Mapper
|
||||
public interface SysUserDao {
|
||||
|
||||
UserDO get(Long userId);
|
||||
|
||||
List<UserDO> list(@ValidateSortOrder Map<String,Object> map);
|
||||
|
||||
int count(Map<String,Object> map);
|
||||
|
||||
int save(UserDO user);
|
||||
|
||||
int update(UserDO user);
|
||||
|
||||
int remove(Long userId);
|
||||
|
||||
int batchRemove(Long[] userIds);
|
||||
|
||||
Long[] listAllDept();
|
||||
UserDO get(Long userId);
|
||||
|
||||
List<UserDO> listByPerm(Map<String, Object> map);
|
||||
List<UserDO> list(@SanitizeMap Map<String, Object> map);
|
||||
|
||||
int countByPerm(Map<String,Object> map);
|
||||
int count(Map<String, Object> map);
|
||||
|
||||
int save(UserDO user);
|
||||
|
||||
int update(UserDO user);
|
||||
|
||||
int remove(Long userId);
|
||||
|
||||
int batchRemove(Long[] userIds);
|
||||
|
||||
Long[] listAllDept();
|
||||
|
||||
List<UserDO> listByPerm(@SanitizeMap Map<String, Object> map);
|
||||
|
||||
int countByPerm(Map<String, Object> map);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.system.dao;
|
||||
|
||||
import com.java2nb.common.annotation.ValidateSortOrder;
|
||||
import com.java2nb.common.annotation.SanitizeMap;
|
||||
|
||||
import com.java2nb.system.domain.UserRoleDO;
|
||||
|
||||
@ -21,7 +21,7 @@ public interface UserRoleDao {
|
||||
|
||||
UserRoleDO get(Long id);
|
||||
|
||||
List<UserRoleDO> list(@ValidateSortOrder Map<String, Object> map);
|
||||
List<UserRoleDO> list(@SanitizeMap Map<String, Object> map);
|
||||
|
||||
int count(Map<String, Object> map);
|
||||
|
||||
|
@ -18,7 +18,7 @@ public interface ${className}Dao {
|
||||
|
||||
${className}DO get(${pk.javaType} ${pk.attrname});
|
||||
|
||||
List<${className}DO> list(@ValidateSortOrder Map<String,Object> map);
|
||||
List<${className}DO> list(@SanitizeMap Map<String,Object> map);
|
||||
|
||||
int count(Map<String,Object> map);
|
||||
|
||||
|
@ -44,7 +44,7 @@ public interface ${className}Mapper {
|
||||
"limit #{offset}, #{limit}" +
|
||||
"</if>"+
|
||||
"</script>")
|
||||
List<${className}DO> list(@ValidateSortOrder Map<String,Object> map);
|
||||
List<${className}DO> list(@SanitizeMap Map<String,Object> map);
|
||||
|
||||
@Select("<script>" +
|
||||
"select count(*) from ${tableName} " +
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>novel</artifactId>
|
||||
<groupId>com.java2nb</groupId>
|
||||
<version>5.2.2</version>
|
||||
<version>5.2.3</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -1,13 +0,0 @@
|
||||
package com.java2nb.novel.core.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author xiongxiaoyang
|
||||
* @date 2025/7/17
|
||||
*/
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface ValidateSortOrder {
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
package com.java2nb.novel.core.aspect;
|
||||
|
||||
import com.java2nb.novel.core.annotation.ValidateSortOrder;
|
||||
import com.java2nb.novel.core.utils.SortWhitelistUtil;
|
||||
import com.java2nb.novel.core.vo.SortOrderVO;
|
||||
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.*;
|
||||
|
||||
/**
|
||||
* @author xiongxiaoyang
|
||||
* @date 2025/7/17
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class SortOrderValidationAspect {
|
||||
|
||||
/**
|
||||
* 拦截所有的mapper方法
|
||||
*/
|
||||
@SneakyThrows
|
||||
@Around("execution(* com.java2nb.novel.mapper.*Mapper.*(..))")
|
||||
public Object processSortOrderFields(ProceedingJoinPoint joinPoint) {
|
||||
Object[] args = joinPoint.getArgs();
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
|
||||
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void handleAnnotatedParameter(Object obj) {
|
||||
if (obj instanceof SortOrderVO sortOrderVO){
|
||||
processSortOrderVO(sortOrderVO);
|
||||
} else if (obj instanceof Map<?, ?> map) {
|
||||
processMap(map);
|
||||
} else {
|
||||
processGenericObject(obj);
|
||||
}
|
||||
}
|
||||
|
||||
private void processSortOrderVO(SortOrderVO sortOrderVO) {
|
||||
if(sortOrderVO.getSort() != null){
|
||||
sortOrderVO.setSort(SortWhitelistUtil.sanitizeColumn(sortOrderVO.getSort()));
|
||||
}
|
||||
if(sortOrderVO.getOrder() != null){
|
||||
sortOrderVO.setOrder(SortWhitelistUtil.sanitizeOrder(sortOrderVO.getOrder()));
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@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 -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
package com.java2nb.novel.core.utils;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author xiongxiaoyang
|
||||
* @date 2025/7/17
|
||||
*/
|
||||
@UtilityClass
|
||||
public class SortWhitelistUtil {
|
||||
|
||||
private final Set<String> allowedColumns = new HashSet<>(
|
||||
Arrays.asList("id", "name", "create_time", "update_time", "last_index_update_time", "word_count",
|
||||
"visit_count"));
|
||||
private final Set<String> allowedOrders = new HashSet<>(Arrays.asList("asc", "desc"));
|
||||
|
||||
public String sanitizeColumn(String input) {
|
||||
return allowedColumns.contains(input.toLowerCase()) ? input.toLowerCase() : "id";
|
||||
}
|
||||
|
||||
public String sanitizeOrder(String input) {
|
||||
return allowedOrders.contains(input.toLowerCase()) ? input.toLowerCase() : "asc";
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package com.java2nb.novel.core.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author xiongxiaoyang
|
||||
* @date 2025/7/17
|
||||
*/
|
||||
@Data
|
||||
public class SortOrderVO {
|
||||
|
||||
private String sort;
|
||||
|
||||
private String order;
|
||||
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>novel</artifactId>
|
||||
<groupId>com.java2nb</groupId>
|
||||
<version>5.2.2</version>
|
||||
<version>5.2.3</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>novel</artifactId>
|
||||
<groupId>com.java2nb</groupId>
|
||||
<version>5.2.2</version>
|
||||
<version>5.2.3</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@ -59,7 +59,7 @@
|
||||
<!-- AI -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
|
||||
<artifactId>spring-ai-starter-model-openai</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
@ -15,6 +15,7 @@ import io.github.xxyopen.model.resp.RestResult;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
@ -82,7 +83,7 @@ public class BookController extends BaseController {
|
||||
* 分页搜索
|
||||
*/
|
||||
@GetMapping("searchByPage")
|
||||
public RestResult<?> searchByPage(BookSpVO bookSP, @RequestParam(value = "curr", defaultValue = "1") int page,
|
||||
public RestResult<?> searchByPage(@Validated BookSpVO bookSP, @RequestParam(value = "curr", defaultValue = "1") int page,
|
||||
@RequestParam(value = "limit", defaultValue = "20") int pageSize) {
|
||||
return RestResult.ok(bookService.searchByPage(bookSP, page, pageSize));
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package com.java2nb.novel.mapper;
|
||||
|
||||
import com.java2nb.novel.core.annotation.ValidateSortOrder;
|
||||
import com.java2nb.novel.entity.Book;
|
||||
import com.java2nb.novel.vo.BookSpVO;
|
||||
import com.java2nb.novel.vo.BookVO;
|
||||
@ -14,7 +13,7 @@ import java.util.List;
|
||||
public interface FrontBookMapper extends BookMapper {
|
||||
|
||||
|
||||
List<BookVO> searchByPage(@ValidateSortOrder BookSpVO params);
|
||||
List<BookVO> searchByPage(BookSpVO params);
|
||||
|
||||
void addVisitCount(@Param("bookId") Long bookId, @Param("visitCount") Integer visitCount);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.java2nb.novel.vo;
|
||||
|
||||
import com.java2nb.novel.core.vo.SortOrderVO;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
@ -10,7 +10,7 @@ import java.util.Date;
|
||||
* @author 11797
|
||||
*/
|
||||
@Data
|
||||
public class BookSpVO extends SortOrderVO {
|
||||
public class BookSpVO {
|
||||
|
||||
private String keyword;
|
||||
|
||||
@ -30,5 +30,8 @@ public class BookSpVO extends SortOrderVO {
|
||||
|
||||
private Long updatePeriod;
|
||||
|
||||
@Pattern(regexp = "^(last_index_update_time|word_count|visit_count)$")
|
||||
private String sort;
|
||||
|
||||
|
||||
}
|
||||
|
@ -59,6 +59,6 @@ spring:
|
||||
base-url: https://api.siliconflow.cn
|
||||
chat:
|
||||
options:
|
||||
model: deepseek-ai/DeepSeek-R1-Distill-Llama-8B
|
||||
model: deepseek-ai/DeepSeek-R1-0528-Qwen3-8B
|
||||
|
||||
|
||||
|
4
pom.xml
4
pom.xml
@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>com.java2nb</groupId>
|
||||
<artifactId>novel</artifactId>
|
||||
<version>5.2.2</version>
|
||||
<version>5.2.3</version>
|
||||
<modules>
|
||||
<module>novel-common</module>
|
||||
<module>novel-front</module>
|
||||
@ -56,7 +56,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-bom</artifactId>
|
||||
<version>1.0.0-M6</version>
|
||||
<version>1.0.0</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
Loading…
Reference in New Issue
Block a user