Compare commits

...

11 Commits

26 changed files with 350 additions and 443 deletions

View File

@@ -1,3 +1,16 @@
## [v2.7.5](https://github.com/continew-org/continew-starter/compare/v2.7.4...v2.7.5) (2024-12-06)
### 💎 功能优化
- 【extension/crud】重构 BaseController 内权限校验BaseController => AbstractBaseController ([3edf79c](https://github.com/continew-org/continew-starter/commit/3edf79cf3bdba91010776c95902414fa7481c39e)) ([15f8706](https://github.com/continew-org/continew-starter/commit/15f87068c65e4afd83ee7020bde2707870012fb9)) ([16da470](https://github.com/continew-org/continew-starter/commit/16da470008d44ebf37cbe9e292ac8db8fc04ceb5)) ([2a5ace0](https://github.com/continew-org/continew-starter/commit/2a5ace003329422589af834431ac2bd8fa30ac28))
- 【extension/crud】调整 BaseController、BaseService 到 crud-core 模块 ([3a0c3e0](https://github.com/continew-org/continew-starter/commit/3a0c3e02b03837789bfc433a335b062ec5dab511))
- 【extension/crud】优化部分代码ValidateGroup => CrudValidationGroup ([f2a30e8](https://github.com/continew-org/continew-starter/commit/f2a30e8b74b828644970be0b05920a32d6eb514a)) ([6a6c559](https://github.com/continew-org/continew-starter/commit/6a6c559d2f53f25888259a7e5d020281408aaa9f))
- 【data】使用 Hutool 工具方法替换反射 API以解决扫描问题 ([93ab4e5](https://github.com/continew-org/continew-starter/commit/93ab4e50cc1957ab772b31c9c433f9fc3d29da33))
### 🐛 问题修复
- 【data/mp】修复 Query 范围查询数组类型数据解析错误 ([6b3bc83](https://github.com/continew-org/continew-starter/commit/6b3bc832de25acdb2be418ad7626e9b6e234adde))
## [v2.7.4](https://github.com/continew-org/continew-starter/compare/v2.7.3...v2.7.4) (2024-11-18) ## [v2.7.4](https://github.com/continew-org/continew-starter/compare/v2.7.3...v2.7.4) (2024-11-18)
### 💎 功能优化 ### 💎 功能优化

View File

@@ -68,8 +68,8 @@ public class DataPermissionDialect extends CommonsDialectImpl {
* 构建自定义数据权限表达式 * 构建自定义数据权限表达式
* *
* <p> * <p>
* 处理完后的 SQL 示例:<br /> select t1.* from table as t1 where t1.dept_id in (select dept_id from sys_role_dept * 处理完后的 SQL 示例:<br /> select t1.* from table as t1 where t1.dept_id in (select dept_id from sys_role_dept where
* where role_id = xxx); * role_id = xxx);
* </p> * </p>
* *
* @param dataPermission 数据权限 * @param dataPermission 数据权限
@@ -124,8 +124,8 @@ public class DataPermissionDialect extends CommonsDialectImpl {
* 构建本部门及以下数据权限表达式 * 构建本部门及以下数据权限表达式
* *
* <p> * <p>
* 处理完后的 SQL 示例:<br /> select t1.* from table as t1 where t1.dept_id in (select id from sys_dept where id = * 处理完后的 SQL 示例:<br /> select t1.* from table as t1 where t1.dept_id in (select id from sys_dept where id = xxx or
* xxx or find_in_set(xxx, ancestors)); * find_in_set(xxx, ancestors));
* </p> * </p>
* *
* @param dataPermission 数据权限 * @param dataPermission 数据权限

View File

@@ -16,10 +16,12 @@
package top.continew.starter.data.mf.util; package top.continew.starter.data.mf.util;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil; import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import com.mybatisflex.core.query.QueryWrapper; import com.mybatisflex.core.query.QueryWrapper;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -133,23 +135,21 @@ public class QueryWrapperHelper {
* @return QueryWrapper Consumer * @return QueryWrapper Consumer
*/ */
private static <Q, R> List<Consumer<QueryWrapper>> buildWrapperConsumer(Q query, Field field) { private static <Q, R> List<Consumer<QueryWrapper>> buildWrapperConsumer(Q query, Field field) {
boolean accessible = field.canAccess(query);
try { try {
field.setAccessible(true);
// 如果字段值为空,直接返回 // 如果字段值为空,直接返回
Object fieldValue = field.get(query); Object fieldValue = ReflectUtil.getFieldValue(query, field);
if (ObjectUtil.isEmpty(fieldValue)) { if (ObjectUtil.isEmpty(fieldValue)) {
return Collections.emptyList(); return Collections.emptyList();
} }
// 设置了 @QueryIgnore 注解,直接忽略 // 设置了 @QueryIgnore 注解,直接忽略
QueryIgnore queryIgnoreAnnotation = field.getAnnotation(QueryIgnore.class); QueryIgnore queryIgnoreAnnotation = AnnotationUtil.getAnnotation(field, QueryIgnore.class);
if (null != queryIgnoreAnnotation) { if (null != queryIgnoreAnnotation) {
return Collections.emptyList(); return Collections.emptyList();
} }
// 建议:数据库表列建议采用下划线连接法命名,程序变量建议采用驼峰法命名 // 建议:数据库表列建议采用下划线连接法命名,程序变量建议采用驼峰法命名
String fieldName = field.getName(); String fieldName = ReflectUtil.getFieldName(field);
// 没有 @Query 注解,默认等值查询 // 没有 @Query 注解,默认等值查询
Query queryAnnotation = field.getAnnotation(Query.class); Query queryAnnotation = AnnotationUtil.getAnnotation(field, Query.class);
if (null == queryAnnotation) { if (null == queryAnnotation) {
return Collections.singletonList(q -> q.eq(CharSequenceUtil.toUnderlineCase(fieldName), fieldValue)); return Collections.singletonList(q -> q.eq(CharSequenceUtil.toUnderlineCase(fieldName), fieldValue));
} }
@@ -173,8 +173,6 @@ public class QueryWrapperHelper {
} catch (Exception e) { } catch (Exception e) {
log.error("Build query wrapper occurred an error: {}. Query: {}, Field: {}.", e log.error("Build query wrapper occurred an error: {}. Query: {}, Field: {}.", e
.getMessage(), query, field, e); .getMessage(), query, field, e);
} finally {
field.setAccessible(accessible);
} }
return Collections.emptyList(); return Collections.emptyList();
} }

View File

@@ -16,10 +16,12 @@
package top.continew.starter.data.mp.util; package top.continew.starter.data.mp.util;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil; import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -127,23 +129,21 @@ public class QueryWrapperHelper {
* @return QueryWrapper Consumer * @return QueryWrapper Consumer
*/ */
private static <Q, R> List<Consumer<QueryWrapper<R>>> buildWrapperConsumer(Q query, Field field) { private static <Q, R> List<Consumer<QueryWrapper<R>>> buildWrapperConsumer(Q query, Field field) {
boolean accessible = field.canAccess(query);
try { try {
field.setAccessible(true);
// 如果字段值为空,直接返回 // 如果字段值为空,直接返回
Object fieldValue = field.get(query); Object fieldValue = ReflectUtil.getFieldValue(query, field);
if (ObjectUtil.isEmpty(fieldValue)) { if (ObjectUtil.isEmpty(fieldValue)) {
return Collections.emptyList(); return Collections.emptyList();
} }
// 设置了 @QueryIgnore 注解,直接忽略 // 设置了 @QueryIgnore 注解,直接忽略
QueryIgnore queryIgnoreAnnotation = field.getAnnotation(QueryIgnore.class); QueryIgnore queryIgnoreAnnotation = AnnotationUtil.getAnnotation(field, QueryIgnore.class);
if (null != queryIgnoreAnnotation) { if (null != queryIgnoreAnnotation) {
return Collections.emptyList(); return Collections.emptyList();
} }
// 建议:数据库表列建议采用下划线连接法命名,程序变量建议采用驼峰法命名 // 建议:数据库表列建议采用下划线连接法命名,程序变量建议采用驼峰法命名
String fieldName = field.getName(); String fieldName = ReflectUtil.getFieldName(field);
// 没有 @Query 注解,默认等值查询 // 没有 @Query 注解,默认等值查询
Query queryAnnotation = field.getAnnotation(Query.class); Query queryAnnotation = AnnotationUtil.getAnnotation(field, Query.class);
if (null == queryAnnotation) { if (null == queryAnnotation) {
return Collections.singletonList(q -> q.eq(CharSequenceUtil.toUnderlineCase(fieldName), fieldValue)); return Collections.singletonList(q -> q.eq(CharSequenceUtil.toUnderlineCase(fieldName), fieldValue));
} }
@@ -167,8 +167,6 @@ public class QueryWrapperHelper {
} catch (Exception e) { } catch (Exception e) {
log.error("Build query wrapper occurred an error: {}. Query: {}, Field: {}.", e log.error("Build query wrapper occurred an error: {}. Query: {}, Field: {}.", e
.getMessage(), query, field, e); .getMessage(), query, field, e);
} finally {
field.setAccessible(accessible);
} }
return Collections.emptyList(); return Collections.emptyList();
} }
@@ -193,8 +191,9 @@ public class QueryWrapperHelper {
case LT -> consumers.add(q -> q.lt(columnName, fieldValue)); case LT -> consumers.add(q -> q.lt(columnName, fieldValue));
case LE -> consumers.add(q -> q.le(columnName, fieldValue)); case LE -> consumers.add(q -> q.le(columnName, fieldValue));
case BETWEEN -> { case BETWEEN -> {
// 数组转集合
List<Object> between = new ArrayList<>(ArrayUtil.isArray(fieldValue) List<Object> between = new ArrayList<>(ArrayUtil.isArray(fieldValue)
? CollUtil.toList(fieldValue) ? List.of((Object[])fieldValue)
: (List<Object>)fieldValue); : (List<Object>)fieldValue);
ValidationUtils.throwIf(between.size() != 2, "[{}] 必须是一个范围", columnName); ValidationUtils.throwIf(between.size() != 2, "[{}] 必须是一个范围", columnName);
consumers.add(q -> q.between(columnName, between.get(0), between.get(1))); consumers.add(q -> q.between(columnName, between.get(0), between.get(1)));
@@ -205,13 +204,13 @@ public class QueryWrapperHelper {
case IN -> { case IN -> {
ValidationUtils.throwIfEmpty(fieldValue, "[{}] 不能为空", columnName); ValidationUtils.throwIfEmpty(fieldValue, "[{}] 不能为空", columnName);
consumers.add(q -> q.in(columnName, ArrayUtil.isArray(fieldValue) consumers.add(q -> q.in(columnName, ArrayUtil.isArray(fieldValue)
? CollUtil.toList(fieldValue) ? List.of((Object[])fieldValue)
: (Collection<Object>)fieldValue)); : (Collection<Object>)fieldValue));
} }
case NOT_IN -> { case NOT_IN -> {
ValidationUtils.throwIfEmpty(fieldValue, "[{}] 不能为空", columnName); ValidationUtils.throwIfEmpty(fieldValue, "[{}] 不能为空", columnName);
consumers.add(q -> q.notIn(columnName, ArrayUtil.isArray(fieldValue) consumers.add(q -> q.notIn(columnName, ArrayUtil.isArray(fieldValue)
? CollUtil.toList(fieldValue) ? List.of((Object[])fieldValue)
: (Collection<Object>)fieldValue)); : (Collection<Object>)fieldValue));
} }
case IS_NULL -> consumers.add(q -> q.isNull(columnName)); case IS_NULL -> consumers.add(q -> q.isNull(columnName));

View File

@@ -43,7 +43,7 @@
<properties> <properties>
<!-- 项目版本号 --> <!-- 项目版本号 -->
<revision>2.7.4</revision> <revision>2.7.5</revision>
<snail-job.version>1.1.2</snail-job.version> <snail-job.version>1.1.2</snail-job.version>
<sa-token.version>1.39.0</sa-token.version> <sa-token.version>1.39.0</sa-token.version>
<just-auth.version>1.16.6</just-auth.version> <just-auth.version>1.16.6</just-auth.version>

View File

@@ -30,18 +30,6 @@
<artifactId>continew-starter-web</artifactId> <artifactId>continew-starter-web</artifactId>
</dependency> </dependency>
<!-- 认证模块 - SaToken -->
<dependency>
<groupId>top.continew</groupId>
<artifactId>continew-starter-auth-satoken</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 数据访问模块 - 核心模块 --> <!-- 数据访问模块 - 核心模块 -->
<dependency> <dependency>
<groupId>top.continew</groupId> <groupId>top.continew</groupId>

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.starter.extension.crud.annotation;
import top.continew.starter.extension.crud.enums.Api;
import java.lang.annotation.*;
/**
* CRUD增删改查API
*
* @author Charles7c
* @since 2.7.5
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrudApi {
/**
* API 类型
*/
Api value() default Api.LIST;
}

View File

@@ -17,6 +17,7 @@
package top.continew.starter.extension.crud.annotation; package top.continew.starter.extension.crud.annotation;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import top.continew.starter.extension.crud.autoconfigure.CrudRequestMappingAutoConfiguration;
import top.continew.starter.extension.crud.autoconfigure.CrudRestControllerAutoConfiguration; import top.continew.starter.extension.crud.autoconfigure.CrudRestControllerAutoConfiguration;
import java.lang.annotation.*; import java.lang.annotation.*;
@@ -30,5 +31,5 @@ import java.lang.annotation.*;
@Target({ElementType.TYPE}) @Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Documented @Documented
@Import({CrudRestControllerAutoConfiguration.class}) @Import({CrudRequestMappingAutoConfiguration.class, CrudRestControllerAutoConfiguration.class})
public @interface EnableCrudRestController {} public @interface EnableCrudRestController {}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.starter.extension.crud.aop;
import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractPointcutAdvisor;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import java.lang.annotation.Annotation;
/**
* CRUD API 注解通知
*
* @author Charles7c
* @since 2.7.5
*/
public class CrudApiAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
private final Advice advice;
private final Pointcut pointcut;
public CrudApiAnnotationAdvisor(CrudApiAnnotationInterceptor advice, Class<? extends Annotation> annotation) {
this.advice = advice;
this.pointcut = new ComposablePointcut(AnnotationMatchingPointcut.forMethodAnnotation(annotation));
}
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
@Override
public Advice getAdvice() {
return this.advice;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (this.advice instanceof BeanFactoryAware beanFactoryAware) {
beanFactoryAware.setBeanFactory(beanFactory);
}
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.starter.extension.crud.aop;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.util.ClassUtils;
import top.continew.starter.extension.crud.annotation.CrudApi;
import top.continew.starter.extension.crud.controller.AbstractBaseController;
import java.lang.reflect.Method;
import java.util.Objects;
/**
* CRUD API 注解拦截器
*
* @author Charles7c
* @since 2.7.5
*/
public class CrudApiAnnotationInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// 获取目标类
Class<?> targetClass = AopUtils.getTargetClass(Objects.requireNonNull(invocation.getThis()));
// 获取目标方法
Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
Method targetMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
// 获取 @CrudApi 注解
CrudApi crudApi = AnnotatedElementUtils.findMergedAnnotation(targetMethod, CrudApi.class);
// 执行处理
AbstractBaseController controller = (AbstractBaseController)invocation.getThis();
controller.preHandle(crudApi, invocation.getArguments(), targetMethod, targetClass);
return invocation.proceed();
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.starter.extension.crud.autoconfigure;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.format.support.FormattingConversionService;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.resource.ResourceUrlProvider;
/**
* CRUD Request Mapping 自动配置
*
* @author Charles7c
* @since 1.0.0
*/
@Configuration
@EnableConfigurationProperties(CrudProperties.class)
public class CrudRequestMappingAutoConfiguration extends DelegatingWebMvcConfiguration {
/**
* CRUD 请求映射器处理器映射器(覆盖默认 RequestMappingHandlerMapping
*/
@Override
public RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
return new CrudRequestMappingHandlerMapping();
}
@Bean
@Primary
@Override
public RequestMappingHandlerMapping requestMappingHandlerMapping(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService, resourceUrlProvider);
}
}

View File

@@ -53,7 +53,7 @@ public class CrudRequestMappingHandlerMapping extends RequestMappingHandlerMappi
// 过滤 API如果非本类中定义且 API 列表中不包含,则忽略 // 过滤 API如果非本类中定义且 API 列表中不包含,则忽略
Api[] apiArr = crudRequestMapping.api(); Api[] apiArr = crudRequestMapping.api();
Api api = ExceptionUtils.exToNull(() -> Api.valueOf(method.getName().toUpperCase())); Api api = ExceptionUtils.exToNull(() -> Api.valueOf(method.getName().toUpperCase()));
if (method.getDeclaringClass() != handlerType && !ArrayUtil.containsAny(apiArr, Api.ALL, api)) { if (method.getDeclaringClass() != handlerType && !ArrayUtil.contains(apiArr, api)) {
return null; return null;
} }
// 拼接路径(合并了 @RequestMapping 的部分能力) // 拼接路径(合并了 @RequestMapping 的部分能力)

View File

@@ -19,44 +19,42 @@ package top.continew.starter.extension.crud.autoconfigure;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import top.continew.starter.extension.crud.annotation.CrudApi;
import org.springframework.context.annotation.Primary; import top.continew.starter.extension.crud.aop.CrudApiAnnotationAdvisor;
import org.springframework.format.support.FormattingConversionService; import top.continew.starter.extension.crud.aop.CrudApiAnnotationInterceptor;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.resource.ResourceUrlProvider;
/** /**
* CRUD REST Controller 自动配置 * CRUD REST Controller 自动配置
* *
* @author Charles7c * @author Charles7c
* @since 1.0.0 * @since 2.7.5
*/ */
@Configuration @AutoConfiguration
@EnableConfigurationProperties(CrudProperties.class) @EnableConfigurationProperties(CrudProperties.class)
public class CrudRestControllerAutoConfiguration extends DelegatingWebMvcConfiguration { public class CrudRestControllerAutoConfiguration {
private static final Logger log = LoggerFactory.getLogger(CrudRestControllerAutoConfiguration.class); private static final Logger log = LoggerFactory.getLogger(CrudRestControllerAutoConfiguration.class);
/** /**
* CRUD 请求映射器处理器映射器(覆盖默认 RequestMappingHandlerMapping * CRUD API 注解通知
*/ */
@Override @Bean
public RequestMappingHandlerMapping createRequestMappingHandlerMapping() { @ConditionalOnMissingBean
return new CrudRequestMappingHandlerMapping(); public CrudApiAnnotationAdvisor crudApiAnnotationAdvisor(CrudApiAnnotationInterceptor crudApiAnnotationInterceptor) {
return new CrudApiAnnotationAdvisor(crudApiAnnotationInterceptor, CrudApi.class);
} }
/**
* CRUD API 注解拦截器
*/
@Bean @Bean
@Primary @ConditionalOnMissingBean
@Override public CrudApiAnnotationInterceptor crudApiAnnotationInterceptor() {
public RequestMappingHandlerMapping requestMappingHandlerMapping(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager, return new CrudApiAnnotationInterceptor();
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService, resourceUrlProvider);
} }
@PostConstruct @PostConstruct

View File

@@ -35,25 +35,21 @@ public class CrudTreeProperties {
/** /**
* 父 ID 字段名 * 父 ID 字段名
*
*/ */
private String parentIdKey = "parentId"; private String parentIdKey = "parentId";
/** /**
* 名称字段名 * 名称字段名
*
*/ */
private String nameKey = "name"; private String nameKey = "name";
/** /**
* 排序字段名 * 排序字段名
*
*/ */
private String weightKey = "weight"; private String weightKey = "weight";
/** /**
* 子列表字段名 * 子列表字段名
*
*/ */
private String childrenKey = "children"; private String childrenKey = "children";

View File

@@ -16,9 +16,7 @@
package top.continew.starter.extension.crud.controller; package top.continew.starter.extension.crud.controller;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.lang.tree.Tree; import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.text.CharSequenceUtil;
import com.feiniaojin.gracefulresponse.api.ExcludeFromGracefulResponse; import com.feiniaojin.gracefulresponse.api.ExcludeFromGracefulResponse;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
@@ -27,31 +25,31 @@ import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import top.continew.starter.core.constant.StringConstants; import top.continew.starter.extension.crud.annotation.CrudApi;
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
import top.continew.starter.extension.crud.enums.Api; import top.continew.starter.extension.crud.enums.Api;
import top.continew.starter.extension.crud.handler.CrudApiHandler;
import top.continew.starter.extension.crud.model.query.PageQuery; import top.continew.starter.extension.crud.model.query.PageQuery;
import top.continew.starter.extension.crud.model.query.SortQuery; import top.continew.starter.extension.crud.model.query.SortQuery;
import top.continew.starter.extension.crud.model.req.BaseReq; import top.continew.starter.extension.crud.model.req.BaseReq;
import top.continew.starter.extension.crud.model.resp.BaseIdResp; import top.continew.starter.extension.crud.model.resp.BaseIdResp;
import top.continew.starter.extension.crud.model.resp.PageResp; import top.continew.starter.extension.crud.model.resp.BasePageResp;
import top.continew.starter.extension.crud.service.BaseService; import top.continew.starter.extension.crud.service.BaseService;
import top.continew.starter.extension.crud.util.ValidateGroup; import top.continew.starter.extension.crud.validation.CrudValidationGroup;
import java.util.List; import java.util.List;
/** /**
* 控制器基类 * 控制器抽象基类
* *
* @param <S> 业务接口 * @param <S> 业务接口
* @param <L> 列表类型 * @param <L> 列表类型
* @param <D> 详情类型 * @param <D> 详情类型
* @param <Q> 查询条件 * @param <Q> 查询条件
* @param <C> 创建或修改类型 * @param <C> 创建或修改参数类型
* @author Charles7c * @author Charles7c
* @since 1.0.0 * @since 1.0.0
*/ */
public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q, C extends BaseReq> { public abstract class AbstractBaseController<S extends BaseService<L, D, Q, C>, L, D, Q, C extends BaseReq> implements CrudApiHandler {
@Autowired @Autowired
protected S baseService; protected S baseService;
@@ -63,11 +61,11 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q,
* @param pageQuery 分页查询条件 * @param pageQuery 分页查询条件
* @return 分页信息 * @return 分页信息
*/ */
@CrudApi(Api.PAGE)
@Operation(summary = "分页查询列表", description = "分页查询列表") @Operation(summary = "分页查询列表", description = "分页查询列表")
@ResponseBody @ResponseBody
@GetMapping @GetMapping
public PageResp<L> page(Q query, @Validated PageQuery pageQuery) { public BasePageResp<L> page(Q query, @Validated PageQuery pageQuery) {
this.checkPermission(Api.LIST);
return baseService.page(query, pageQuery); return baseService.page(query, pageQuery);
} }
@@ -78,11 +76,11 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q,
* @param sortQuery 排序查询条件 * @param sortQuery 排序查询条件
* @return 列表信息 * @return 列表信息
*/ */
@CrudApi(Api.LIST)
@Operation(summary = "查询列表", description = "查询列表") @Operation(summary = "查询列表", description = "查询列表")
@ResponseBody @ResponseBody
@GetMapping("/list") @GetMapping("/list")
public List<L> list(Q query, SortQuery sortQuery) { public List<L> list(Q query, SortQuery sortQuery) {
this.checkPermission(Api.LIST);
return baseService.list(query, sortQuery); return baseService.list(query, sortQuery);
} }
@@ -93,11 +91,11 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q,
* @param sortQuery 排序查询条件 * @param sortQuery 排序查询条件
* @return 树列表信息 * @return 树列表信息
*/ */
@CrudApi(Api.TREE)
@Operation(summary = "查询树列表", description = "查询树列表") @Operation(summary = "查询树列表", description = "查询树列表")
@ResponseBody @ResponseBody
@GetMapping("/tree") @GetMapping("/tree")
public List<Tree<Long>> tree(Q query, SortQuery sortQuery) { public List<Tree<Long>> tree(Q query, SortQuery sortQuery) {
this.checkPermission(Api.LIST);
return baseService.tree(query, sortQuery, false); return baseService.tree(query, sortQuery, false);
} }
@@ -107,41 +105,41 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q,
* @param id ID * @param id ID
* @return 详情信息 * @return 详情信息
*/ */
@CrudApi(Api.DETAIL)
@Operation(summary = "查询详情", description = "查询详情") @Operation(summary = "查询详情", description = "查询详情")
@Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH) @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
@ResponseBody @ResponseBody
@GetMapping("/{id}") @GetMapping("/{id}")
public D detail(@PathVariable("id") Long id) { public D detail(@PathVariable("id") Long id) {
this.checkPermission(Api.DETAIL);
return baseService.get(id); return baseService.get(id);
} }
/** /**
* 新增 * 新增
* *
* @param req 创建信息 * @param req 创建参数
* @return 自增 ID * @return ID
*/ */
@CrudApi(Api.ADD)
@Operation(summary = "新增数据", description = "新增数据") @Operation(summary = "新增数据", description = "新增数据")
@ResponseBody @ResponseBody
@PostMapping @PostMapping
public BaseIdResp<Long> add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody C req) { public BaseIdResp<Long> add(@Validated(CrudValidationGroup.Add.class) @RequestBody C req) {
this.checkPermission(Api.ADD);
return new BaseIdResp<>(baseService.add(req)); return new BaseIdResp<>(baseService.add(req));
} }
/** /**
* 修改 * 修改
* *
* @param req 修改信息 * @param req 修改参数
* @param id ID * @param id ID
*/ */
@CrudApi(Api.UPDATE)
@Operation(summary = "修改数据", description = "修改数据") @Operation(summary = "修改数据", description = "修改数据")
@Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH) @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
@ResponseBody @ResponseBody
@PutMapping("/{id}") @PutMapping("/{id}")
public void update(@Validated(ValidateGroup.Crud.Update.class) @RequestBody C req, @PathVariable("id") Long id) { public void update(@Validated(CrudValidationGroup.Update.class) @RequestBody C req, @PathVariable("id") Long id) {
this.checkPermission(Api.UPDATE);
baseService.update(req, id); baseService.update(req, id);
} }
@@ -150,12 +148,12 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q,
* *
* @param ids ID 列表 * @param ids ID 列表
*/ */
@CrudApi(Api.DELETE)
@Operation(summary = "删除数据", description = "删除数据") @Operation(summary = "删除数据", description = "删除数据")
@Parameter(name = "ids", description = "ID 列表", example = "1,2", in = ParameterIn.PATH) @Parameter(name = "ids", description = "ID 列表", example = "1,2", in = ParameterIn.PATH)
@ResponseBody @ResponseBody
@DeleteMapping("/{ids}") @DeleteMapping("/{ids}")
public void delete(@PathVariable("ids") List<Long> ids) { public void delete(@PathVariable("ids") List<Long> ids) {
this.checkPermission(Api.DELETE);
baseService.delete(ids); baseService.delete(ids);
} }
@@ -166,24 +164,11 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q,
* @param sortQuery 排序查询条件 * @param sortQuery 排序查询条件
* @param response 响应对象 * @param response 响应对象
*/ */
@CrudApi(Api.EXPORT)
@ExcludeFromGracefulResponse @ExcludeFromGracefulResponse
@Operation(summary = "导出数据", description = "导出数据") @Operation(summary = "导出数据", description = "导出数据")
@GetMapping("/export") @GetMapping("/export")
public void export(Q query, SortQuery sortQuery, HttpServletResponse response) { public void export(Q query, SortQuery sortQuery, HttpServletResponse response) {
this.checkPermission(Api.EXPORT);
baseService.export(query, sortQuery, response); baseService.export(query, sortQuery, response);
} }
/**
* 根据 API 类型进行权限验证
*
* @param api API 类型
*/
protected void checkPermission(Api api) {
CrudRequestMapping crudRequestMapping = this.getClass().getDeclaredAnnotation(CrudRequestMapping.class);
String path = crudRequestMapping.value();
String permissionPrefix = String.join(StringConstants.COLON, CharSequenceUtil
.splitTrim(path, StringConstants.SLASH));
StpUtil.checkPermission("%s:%s".formatted(permissionPrefix, api.name().toLowerCase()));
}
} }

View File

@@ -24,11 +24,6 @@ package top.continew.starter.extension.crud.enums;
*/ */
public enum Api { public enum Api {
/**
* 所有 API
*/
ALL,
/** /**
* 分页 * 分页
*/ */

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.starter.extension.crud.handler;
import top.continew.starter.extension.crud.annotation.CrudApi;
import java.lang.reflect.Method;
/**
* CRUD API 处理器
*
* @author Charles7c
* @since 2.7.5
*/
public interface CrudApiHandler {
/**
* 前置处理
*
* @param crudApi CRUD API 注解
* @param args 方法参数
* @param targetMethod 目标方法
* @param targetClass 目标类
* @throws Exception 处理异常
*/
void preHandle(CrudApi crudApi, Object[] args, Method targetMethod, Class<?> targetClass) throws Exception;
}

View File

@@ -20,8 +20,8 @@ import cn.hutool.core.lang.tree.Tree;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import top.continew.starter.extension.crud.model.query.PageQuery; import top.continew.starter.extension.crud.model.query.PageQuery;
import top.continew.starter.extension.crud.model.query.SortQuery; import top.continew.starter.extension.crud.model.query.SortQuery;
import top.continew.starter.extension.crud.model.resp.BasePageResp;
import top.continew.starter.extension.crud.model.resp.LabelValueResp; import top.continew.starter.extension.crud.model.resp.LabelValueResp;
import top.continew.starter.extension.crud.model.resp.PageResp;
import java.util.List; import java.util.List;
@@ -31,7 +31,7 @@ import java.util.List;
* @param <L> 列表类型 * @param <L> 列表类型
* @param <D> 详情类型 * @param <D> 详情类型
* @param <Q> 查询条件 * @param <Q> 查询条件
* @param <C> 创建或修改类型 * @param <C> 创建或修改参数类型
* @author Charles7c * @author Charles7c
* @since 1.0.0 * @since 1.0.0
*/ */
@@ -44,7 +44,7 @@ public interface BaseService<L, D, Q, C> {
* @param pageQuery 分页查询条件 * @param pageQuery 分页查询条件
* @return 分页列表信息 * @return 分页列表信息
*/ */
PageResp<L> page(Q query, PageQuery pageQuery); BasePageResp<L> page(Q query, PageQuery pageQuery);
/** /**
* 查询列表 * 查询列表

View File

@@ -14,30 +14,25 @@
* limitations under the License. * limitations under the License.
*/ */
package top.continew.starter.extension.crud.util; package top.continew.starter.extension.crud.validation;
import jakarta.validation.groups.Default; import jakarta.validation.groups.Default;
/** /**
* 分组校验 * CRUD 分组校验
* *
* @author Charles7c * @author Charles7c
* @since 1.0.0 * @since 1.0.0
*/ */
public interface ValidateGroup extends Default { public interface CrudValidationGroup extends Default {
/** /**
* 分组校验-删改查 * CRUD 分组校验-
*/ */
interface Crud extends ValidateGroup { interface Add extends CrudValidationGroup {}
/**
* 分组校验-创建
*/
interface Add extends Crud {}
/** /**
* 分组校验-修改 * CRUD 分组校验-修改
*/ */
interface Update extends Crud {} interface Update extends CrudValidationGroup {}
}
} }

View File

@@ -1,189 +0,0 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.starter.extension.crud.controller;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.text.CharSequenceUtil;
import com.feiniaojin.gracefulresponse.api.ExcludeFromGracefulResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
import top.continew.starter.extension.crud.enums.Api;
import top.continew.starter.extension.crud.model.query.PageQuery;
import top.continew.starter.extension.crud.model.query.SortQuery;
import top.continew.starter.extension.crud.model.req.BaseReq;
import top.continew.starter.extension.crud.model.resp.BaseIdResp;
import top.continew.starter.extension.crud.model.resp.PageResp;
import top.continew.starter.extension.crud.service.BaseService;
import top.continew.starter.extension.crud.util.ValidateGroup;
import java.util.List;
/**
* 控制器基类
*
* @param <S> 业务接口
* @param <L> 列表类型
* @param <D> 详情类型
* @param <Q> 查询条件
* @param <C> 创建或修改类型
* @author Charles7c
* @since 1.0.0
*/
public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q, C extends BaseReq> {
@Autowired
protected S baseService;
/**
* 分页查询列表
*
* @param query 查询条件
* @param pageQuery 分页查询条件
* @return 分页信息
*/
@Operation(summary = "分页查询列表", description = "分页查询列表")
@ResponseBody
@GetMapping
public PageResp<L> page(Q query, @Validated PageQuery pageQuery) {
this.checkPermission(Api.LIST);
return baseService.page(query, pageQuery);
}
/**
* 查询列表
*
* @param query 查询条件
* @param sortQuery 排序查询条件
* @return 列表信息
*/
@Operation(summary = "查询列表", description = "查询列表")
@ResponseBody
@GetMapping("/list")
public List<L> list(Q query, SortQuery sortQuery) {
this.checkPermission(Api.LIST);
return baseService.list(query, sortQuery);
}
/**
* 查询树列表
*
* @param query 查询条件
* @param sortQuery 排序查询条件
* @return 树列表信息
*/
@Operation(summary = "查询树列表", description = "查询树列表")
@ResponseBody
@GetMapping("/tree")
public List<Tree<Long>> tree(Q query, SortQuery sortQuery) {
this.checkPermission(Api.LIST);
return baseService.tree(query, sortQuery, false);
}
/**
* 查询详情
*
* @param id ID
* @return 详情信息
*/
@Operation(summary = "查询详情", description = "查询详情")
@Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
@ResponseBody
@GetMapping("/{id}")
public D detail(@PathVariable("id") Long id) {
this.checkPermission(Api.DETAIL);
return baseService.get(id);
}
/**
* 新增
*
* @param req 创建信息
* @return 自增 ID
*/
@Operation(summary = "新增数据", description = "新增数据")
@ResponseBody
@PostMapping
public BaseIdResp<Long> add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody C req) {
this.checkPermission(Api.ADD);
return new BaseIdResp<>(baseService.add(req));
}
/**
* 修改
*
* @param req 修改信息
* @param id ID
*/
@Operation(summary = "修改数据", description = "修改数据")
@Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
@ResponseBody
@PutMapping("/{id}")
public void update(@Validated(ValidateGroup.Crud.Update.class) @RequestBody C req, @PathVariable("id") Long id) {
this.checkPermission(Api.UPDATE);
baseService.update(req, id);
}
/**
* 删除
*
* @param ids ID 列表
*/
@Operation(summary = "删除数据", description = "删除数据")
@Parameter(name = "ids", description = "ID 列表", example = "1,2", in = ParameterIn.PATH)
@ResponseBody
@DeleteMapping("/{ids}")
public void delete(@PathVariable("ids") List<Long> ids) {
this.checkPermission(Api.DELETE);
baseService.delete(ids);
}
/**
* 导出
*
* @param query 查询条件
* @param sortQuery 排序查询条件
* @param response 响应对象
*/
@ExcludeFromGracefulResponse
@Operation(summary = "导出数据", description = "导出数据")
@GetMapping("/export")
public void export(Q query, SortQuery sortQuery, HttpServletResponse response) {
this.checkPermission(Api.EXPORT);
baseService.export(query, sortQuery, response);
}
/**
* 根据 API 类型进行权限验证
*
* @param api API 类型
*/
protected void checkPermission(Api api) {
CrudRequestMapping crudRequestMapping = this.getClass().getDeclaredAnnotation(CrudRequestMapping.class);
String path = crudRequestMapping.value();
String permissionPrefix = String.join(StringConstants.COLON, CharSequenceUtil
.splitTrim(path, StringConstants.SLASH));
StpUtil.checkPermission("%s:%s".formatted(permissionPrefix, api.name().toLowerCase()));
}
}

View File

@@ -1,120 +0,0 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.starter.extension.crud.service;
import cn.hutool.core.lang.tree.Tree;
import jakarta.servlet.http.HttpServletResponse;
import top.continew.starter.extension.crud.model.query.PageQuery;
import top.continew.starter.extension.crud.model.query.SortQuery;
import top.continew.starter.extension.crud.model.resp.LabelValueResp;
import top.continew.starter.extension.crud.model.resp.PageResp;
import java.util.List;
/**
* 业务接口基类
*
* @param <L> 列表类型
* @param <D> 详情类型
* @param <Q> 查询条件
* @param <C> 创建或修改类型
* @author Charles7c
* @since 1.0.0
*/
public interface BaseService<L, D, Q, C> {
/**
* 分页查询列表
*
* @param query 查询条件
* @param pageQuery 分页查询条件
* @return 分页列表信息
*/
PageResp<L> page(Q query, PageQuery pageQuery);
/**
* 查询列表
*
* @param query 查询条件
* @param sortQuery 排序查询条件
* @return 列表信息
*/
List<L> list(Q query, SortQuery sortQuery);
/**
* 查询树列表
* <p>
* 虽然提供了查询条件,但不建议使用,容易因缺失根节点导致树节点丢失。
* 建议在前端进行查询过滤,如需使用建议重写方法。
* </p>
*
* @param query 查询条件
* @param sortQuery 排序查询条件
* @param isSimple 是否为简单树结构(不包含基本树结构之外的扩展字段,简单树(下拉列表)使用全局配置结构,复杂树(表格)使用 @DictField 局部配置)
* @return 树列表信息
*/
List<Tree<Long>> tree(Q query, SortQuery sortQuery, boolean isSimple);
/**
* 查看详情
*
* @param id ID
* @return 详情信息
*/
D get(Long id);
/**
* 查询字典列表
*
* @param query 查询条件
* @param sortQuery 排序查询条件
* @return 字典列表信息
*/
List<LabelValueResp> listDict(Q query, SortQuery sortQuery);
/**
* 新增
*
* @param req 创建信息
* @return 自增 ID
*/
Long add(C req);
/**
* 修改
*
* @param req 修改信息
* @param id ID
*/
void update(C req, Long id);
/**
* 删除
*
* @param ids ID 列表
*/
void delete(List<Long> ids);
/**
* 导出
*
* @param query 查询条件
* @param sortQuery 排序查询条件
* @param response 响应对象
*/
void export(Q query, SortQuery sortQuery, HttpServletResponse response);
}

View File

@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package top.continew.starter.extension.crud.service.impl; package top.continew.starter.extension.crud.service;
import cn.crane4j.core.support.OperateTemplate; import cn.crane4j.core.support.OperateTemplate;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
@@ -44,7 +44,6 @@ import top.continew.starter.extension.crud.model.entity.BaseIdDO;
import top.continew.starter.extension.crud.model.query.PageQuery; import top.continew.starter.extension.crud.model.query.PageQuery;
import top.continew.starter.extension.crud.model.query.SortQuery; import top.continew.starter.extension.crud.model.query.SortQuery;
import top.continew.starter.extension.crud.model.resp.PageResp; import top.continew.starter.extension.crud.model.resp.PageResp;
import top.continew.starter.extension.crud.service.BaseService;
import top.continew.starter.file.excel.util.ExcelUtils; import top.continew.starter.file.excel.util.ExcelUtils;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@@ -60,7 +59,7 @@ import java.util.Optional;
* @param <L> 列表类型 * @param <L> 列表类型
* @param <D> 详情类型 * @param <D> 详情类型
* @param <Q> 查询条件 * @param <Q> 查询条件
* @param <C> 创建或修改类型 * @param <C> 创建或修改参数类型
* @author Charles7c * @author Charles7c
* @since 1.0.0 * @since 1.0.0
*/ */

View File

@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package top.continew.starter.extension.crud.service.impl; package top.continew.starter.extension.crud.service;
import cn.crane4j.core.support.OperateTemplate; import cn.crane4j.core.support.OperateTemplate;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
@@ -51,7 +51,6 @@ import top.continew.starter.extension.crud.model.query.PageQuery;
import top.continew.starter.extension.crud.model.query.SortQuery; import top.continew.starter.extension.crud.model.query.SortQuery;
import top.continew.starter.extension.crud.model.resp.LabelValueResp; import top.continew.starter.extension.crud.model.resp.LabelValueResp;
import top.continew.starter.extension.crud.model.resp.PageResp; import top.continew.starter.extension.crud.model.resp.PageResp;
import top.continew.starter.extension.crud.service.BaseService;
import top.continew.starter.file.excel.util.ExcelUtils; import top.continew.starter.file.excel.util.ExcelUtils;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@@ -65,7 +64,7 @@ import java.util.*;
* @param <L> 列表类型 * @param <L> 列表类型
* @param <D> 详情类型 * @param <D> 详情类型
* @param <Q> 查询条件 * @param <Q> 查询条件
* @param <C> 创建或修改类型 * @param <C> 创建或修改参数类型
* @author Charles7c * @author Charles7c
* @since 1.0.0 * @since 1.0.0
*/ */

View File

@@ -117,8 +117,8 @@ public class DefaultDataPermissionHandler implements DataPermissionHandler {
* 构建本部门及以下数据权限表达式 * 构建本部门及以下数据权限表达式
* *
* <p> * <p>
* 处理完后的 SQL 示例:<br /> select t1.* from table as t1 where t1.dept_id in (select id from sys_dept where id = * 处理完后的 SQL 示例:<br /> select t1.* from table as t1 where t1.dept_id in (select id from sys_dept where id = xxx or
* xxx or find_in_set(xxx, ancestors)); * find_in_set(xxx, ancestors));
* </p> * </p>
* *
* @param dataPermission 数据权限 * @param dataPermission 数据权限
@@ -194,8 +194,8 @@ public class DefaultDataPermissionHandler implements DataPermissionHandler {
* 构建自定义数据权限表达式 * 构建自定义数据权限表达式
* *
* <p> * <p>
* 处理完后的 SQL 示例:<br /> select t1.* from table as t1 where t1.dept_id in (select dept_id from sys_role_dept * 处理完后的 SQL 示例:<br /> select t1.* from table as t1 where t1.dept_id in (select dept_id from sys_role_dept where
* where role_id = xxx); * role_id = xxx);
* </p> * </p>
* *
* @param dataPermission 数据权限 * @param dataPermission 数据权限

View File

@@ -17,7 +17,7 @@
package top.continew.starter.file.excel.converter; package top.continew.starter.file.excel.converter;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.text.CharSequenceUtil;
import com.alibaba.excel.converters.Converter; import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration; import com.alibaba.excel.metadata.GlobalConfiguration;
@@ -57,7 +57,7 @@ public class ExcelListConverter implements Converter<List> {
ExcelContentProperty contentProperty, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) { GlobalConfiguration globalConfiguration) {
String stringValue = cellData.getStringValue(); String stringValue = cellData.getStringValue();
return StrUtil.split(stringValue, StringConstants.COMMA); return CharSequenceUtil.split(stringValue, StringConstants.COMMA);
} }
@Override @Override