mirror of
				https://github.com/continew-org/continew-admin.git
				synced 2025-10-31 10:57:13 +08:00 
			
		
		
		
	feat: 新增能力开放模块应用管理功能
This commit is contained in:
		
							
								
								
									
										23
									
								
								continew-admin-open/pom.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								continew-admin-open/pom.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | ||||
|          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
|          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||||
|     <modelVersion>4.0.0</modelVersion> | ||||
|     <parent> | ||||
|         <groupId>top.continew</groupId> | ||||
|         <artifactId>continew-admin</artifactId> | ||||
|         <version>${revision}</version> | ||||
|     </parent> | ||||
|  | ||||
|     <artifactId>continew-admin-open</artifactId> | ||||
|     <description>能力开放模块(包括应用管理、API开放授权、API开发等)</description> | ||||
|  | ||||
|     <dependencies> | ||||
|         <!-- 公共模块(存放公共工具类,公共配置等) --> | ||||
|         <dependency> | ||||
|             <groupId>top.continew</groupId> | ||||
|             <artifactId>continew-admin-common</artifactId> | ||||
|         </dependency> | ||||
|     </dependencies> | ||||
|  | ||||
| </project> | ||||
| @@ -0,0 +1,49 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * 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.admin.open.handler; | ||||
|  | ||||
| import cn.dev33.satoken.annotation.SaCheckPermission; | ||||
| import cn.dev33.satoken.annotation.handler.SaAnnotationHandlerInterface; | ||||
| import org.springframework.stereotype.Component; | ||||
| import top.continew.admin.open.util.ApiSignCheckUtils; | ||||
|  | ||||
| import java.lang.reflect.Method; | ||||
|  | ||||
| import static cn.dev33.satoken.annotation.handler.SaCheckPermissionHandler._checkMethod; | ||||
|  | ||||
| /** | ||||
|  * 重定义注解 SaCheckPermission 的处理器 | ||||
|  * | ||||
|  * @author chengzi | ||||
|  * @since 2024/10/25 12:03 | ||||
|  */ | ||||
| @Component | ||||
| public class SaCheckPermissionHandler implements SaAnnotationHandlerInterface<SaCheckPermission> { | ||||
|  | ||||
|     @Override | ||||
|     public Class<SaCheckPermission> getHandlerAnnotationClass() { | ||||
|         return SaCheckPermission.class; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void checkMethod(SaCheckPermission at, Method method) { | ||||
|         if (!ApiSignCheckUtils.isExistSignParam()) { | ||||
|             _checkMethod(at.type(), at.value(), at.mode(), at.orRole()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,28 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * 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.admin.open.mapper; | ||||
|  | ||||
| import top.continew.starter.data.mp.base.BaseMapper; | ||||
| import top.continew.admin.open.model.entity.AppDO; | ||||
|  | ||||
| /** | ||||
|  * 应用 Mapper | ||||
|  * | ||||
|  * @author chengzi | ||||
|  * @since 2024/10/17 16:03 | ||||
|  */ | ||||
| public interface AppMapper extends BaseMapper<AppDO> {} | ||||
| @@ -0,0 +1,80 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * 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.admin.open.model.entity; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.time.*; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import com.baomidou.mybatisplus.annotation.TableName; | ||||
|  | ||||
| import top.continew.starter.extension.crud.model.entity.BaseDO; | ||||
|  | ||||
| /** | ||||
|  * 应用实体 | ||||
|  * | ||||
|  * @author chengzi | ||||
|  * @since 2024/10/17 16:03 | ||||
|  */ | ||||
| @Data | ||||
| @TableName("sys_app") | ||||
| public class AppDO extends BaseDO { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * ID | ||||
|      */ | ||||
|     private Long id; | ||||
|  | ||||
|     /** | ||||
|      * 应用名称 | ||||
|      */ | ||||
|     private String name; | ||||
|  | ||||
|     /** | ||||
|      * APPKEY | ||||
|      */ | ||||
|     private String appKey; | ||||
|  | ||||
|     /** | ||||
|      * APPSECRET | ||||
|      */ | ||||
|     private String appSecret; | ||||
|  | ||||
|     /** | ||||
|      * 应用状态 | ||||
|      */ | ||||
|     private String status; | ||||
|  | ||||
|     /** | ||||
|      * 失效时间 | ||||
|      */ | ||||
|     private LocalDateTime expirationTime; | ||||
|  | ||||
|     /** | ||||
|      * 应用描述 | ||||
|      */ | ||||
|     private String appDesc; | ||||
|  | ||||
|     /** | ||||
|      * secret查看状态 | ||||
|      */ | ||||
|     private String secretStatus; | ||||
| } | ||||
| @@ -0,0 +1,55 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * 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.admin.open.model.query; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
|  | ||||
| import top.continew.starter.data.core.annotation.Query; | ||||
| import top.continew.starter.data.core.enums.QueryType; | ||||
|  | ||||
| /** | ||||
|  * 应用查询条件 | ||||
|  * | ||||
|  * @author chengzi | ||||
|  * @since 2024/10/17 16:03 | ||||
|  */ | ||||
| @Data | ||||
| @Schema(description = "应用查询条件") | ||||
| public class AppQuery implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 应用名称 | ||||
|      */ | ||||
|     @Schema(description = "应用名称") | ||||
|     @Query(type = QueryType.LIKE) | ||||
|     private String name; | ||||
|  | ||||
|     /** | ||||
|      * APPKEY | ||||
|      */ | ||||
|     @Schema(description = "APPKEY") | ||||
|     @Query(type = QueryType.LIKE) | ||||
|     private String appKey; | ||||
| } | ||||
| @@ -0,0 +1,82 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * 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.admin.open.model.req; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.time.*; | ||||
|  | ||||
| import jakarta.validation.constraints.*; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
|  | ||||
| import org.hibernate.validator.constraints.Length; | ||||
|  | ||||
| import top.continew.starter.extension.crud.model.req.BaseReq; | ||||
|  | ||||
| /** | ||||
|  * 创建或修改应用信息 | ||||
|  * | ||||
|  * @author chengzi | ||||
|  * @since 2024/10/17 16:03 | ||||
|  */ | ||||
| @Data | ||||
| @Schema(description = "创建或修改应用信息") | ||||
| public class AppReq extends BaseReq { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 应用名称 | ||||
|      */ | ||||
|     @Schema(description = "应用名称") | ||||
|     @NotBlank(message = "应用名称不能为空") | ||||
|     @Length(max = 255, message = "应用名称长度不能超过 {max} 个字符") | ||||
|     private String name; | ||||
|  | ||||
|     /** | ||||
|      * APPKEY | ||||
|      */ | ||||
|     @Schema(description = "应用密钥") | ||||
|     @NotBlank(message = "应用密钥不能为空") | ||||
|     @Length(max = 255, message = "应用密钥长度不能超过 {max} 个字符") | ||||
|     private String appKey; | ||||
|  | ||||
|     /** | ||||
|      * 应用状态 | ||||
|      */ | ||||
|     @Schema(description = "应用状态") | ||||
|     @NotBlank(message = "应用状态不能为空") | ||||
|     @Length(max = 255, message = "应用状态长度不能超过 {max} 个字符") | ||||
|     private String status; | ||||
|  | ||||
|     /** | ||||
|      * 失效时间 | ||||
|      */ | ||||
|     @Schema(description = "失效时间") | ||||
|     @NotNull(message = "失效时间不能为空") | ||||
|     private LocalDateTime expirationTime; | ||||
|  | ||||
|     /** | ||||
|      * 应用描述 | ||||
|      */ | ||||
|     @Schema(description = "应用描述") | ||||
|     @Length(max = 255, message = "应用描述长度不能超过 {max} 个字符") | ||||
|     private String appDesc; | ||||
| } | ||||
| @@ -0,0 +1,79 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * 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.admin.open.model.resp; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.time.*; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
|  | ||||
| import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; | ||||
| import com.alibaba.excel.annotation.ExcelProperty; | ||||
|  | ||||
| import top.continew.starter.extension.crud.model.resp.BaseDetailResp; | ||||
|  | ||||
| /** | ||||
|  * 应用详情信息 | ||||
|  * | ||||
|  * @author chengzi | ||||
|  * @since 2024/10/17 16:03 | ||||
|  */ | ||||
| @Data | ||||
| @ExcelIgnoreUnannotated | ||||
| @Schema(description = "应用详情信息") | ||||
| public class AppDetailResp extends BaseDetailResp { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 应用名称 | ||||
|      */ | ||||
|     @Schema(description = "应用名称") | ||||
|     @ExcelProperty(value = "应用名称") | ||||
|     private String name; | ||||
|  | ||||
|     /** | ||||
|      * 应用密钥 | ||||
|      */ | ||||
|     @Schema(description = "应用密钥") | ||||
|     @ExcelProperty(value = "应用密钥") | ||||
|     private String appKey; | ||||
|  | ||||
|     /** | ||||
|      * 应用状态 | ||||
|      */ | ||||
|     @Schema(description = "应用状态") | ||||
|     @ExcelProperty(value = "应用状态") | ||||
|     private String status; | ||||
|  | ||||
|     /** | ||||
|      * 失效时间 | ||||
|      */ | ||||
|     @Schema(description = "失效时间") | ||||
|     @ExcelProperty(value = "失效时间") | ||||
|     private LocalDateTime expirationTime; | ||||
|  | ||||
|     /** | ||||
|      * 应用描述 | ||||
|      */ | ||||
|     @Schema(description = "应用描述") | ||||
|     @ExcelProperty(value = "应用描述") | ||||
|     private String appDesc; | ||||
| } | ||||
| @@ -0,0 +1,71 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * 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.admin.open.model.resp; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.time.*; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
|  | ||||
| import top.continew.starter.extension.crud.model.resp.BaseResp; | ||||
|  | ||||
| /** | ||||
|  * 应用信息 | ||||
|  * | ||||
|  * @author chengzi | ||||
|  * @since 2024/10/17 16:03 | ||||
|  */ | ||||
| @Data | ||||
| @Schema(description = "应用信息") | ||||
| public class AppResp extends BaseResp { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 应用名称 | ||||
|      */ | ||||
|     @Schema(description = "应用名称") | ||||
|     private String name; | ||||
|  | ||||
|     /** | ||||
|      * APPKEY | ||||
|      */ | ||||
|     @Schema(description = "应用密钥") | ||||
|     private String appKey; | ||||
|  | ||||
|     /** | ||||
|      * 应用状态 | ||||
|      */ | ||||
|     @Schema(description = "应用状态") | ||||
|     private String status; | ||||
|  | ||||
|     /** | ||||
|      * 失效时间 | ||||
|      */ | ||||
|     @Schema(description = "失效时间") | ||||
|     private LocalDateTime expirationTime; | ||||
|  | ||||
|     /** | ||||
|      * 应用描述 | ||||
|      */ | ||||
|     @Schema(description = "应用描述") | ||||
|     private String appDesc; | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,47 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * 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.admin.open.model.resp; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import lombok.Data; | ||||
|  | ||||
| import java.io.Serial; | ||||
|  | ||||
| /** | ||||
|  * 应用密钥/密码信息 | ||||
|  * | ||||
|  * @author chengzi | ||||
|  * @since 2024/10/17 16:03 | ||||
|  */ | ||||
| @Data | ||||
| @Schema(description = "应用密钥/密码信息") | ||||
| public class AppSecretGetResp { | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 应用密钥 | ||||
|      */ | ||||
|     @Schema(description = "应用密钥") | ||||
|     private String appKey; | ||||
|  | ||||
|     /** | ||||
|      * 应用密码 | ||||
|      */ | ||||
|     @Schema(description = "应用密码") | ||||
|     private String appSecret; | ||||
| } | ||||
| @@ -0,0 +1,86 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * 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.admin.open.service; | ||||
|  | ||||
| import top.continew.admin.open.model.resp.AppSecretGetResp; | ||||
| import top.continew.starter.extension.crud.service.BaseService; | ||||
| import top.continew.admin.open.model.query.AppQuery; | ||||
| import top.continew.admin.open.model.req.AppReq; | ||||
| import top.continew.admin.open.model.resp.AppDetailResp; | ||||
| import top.continew.admin.open.model.resp.AppResp; | ||||
|  | ||||
| /** | ||||
|  * 应用业务接口 | ||||
|  * | ||||
|  * @author chengzi | ||||
|  * @since 2024/10/17 16:03 | ||||
|  */ | ||||
| public interface AppService extends BaseService<AppResp, AppDetailResp, AppQuery, AppReq> { | ||||
|     /** | ||||
|      * 根据ID查询应用密码 | ||||
|      * | ||||
|      * @param id ID | ||||
|      * @return 应用密码 | ||||
|      */ | ||||
|     AppSecretGetResp getAppSecretById(Long id); | ||||
|  | ||||
|     /** | ||||
|      * 根据ID重置应用密码查看状态 | ||||
|      * | ||||
|      * @param id ID | ||||
|      */ | ||||
|     void resetAppSecretStatusById(Long id, String status); | ||||
|  | ||||
|     /** | ||||
|      * 根据应用密钥重置应用密码查看状态 | ||||
|      * | ||||
|      * @param appKey 应用密钥 | ||||
|      */ | ||||
|     void resetAppSecretStatusByAppkey(String appKey, String status); | ||||
|  | ||||
|     /** | ||||
|      * 根据ID刷新应用密码 | ||||
|      * | ||||
|      * @param id ID | ||||
|      */ | ||||
|     void refreshAppSecretByID(Long id); | ||||
|  | ||||
|     /** | ||||
|      * 根据应用密钥获取应用密码 | ||||
|      * | ||||
|      * @param appKey 应用密钥 | ||||
|      * @return 应用密码 | ||||
|      */ | ||||
|     String getAppSecretByAppKey(String appKey); | ||||
|  | ||||
|     /** | ||||
|      * 判断应用密钥是否存在 | ||||
|      * | ||||
|      * @param appKey 应用密钥 | ||||
|      * @return 是否存在(true:存在;false:不存在) | ||||
|      */ | ||||
|     boolean isExistAppKey(String appKey); | ||||
|  | ||||
|     /** | ||||
|      * 判断应用密钥是否过期 | ||||
|      * | ||||
|      * @param appKey 应用密钥 | ||||
|      * @return 是否过期(true:已过期;false:未过期) | ||||
|      */ | ||||
|     boolean isExpireAppKey(String appKey); | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,102 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * 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.admin.open.service.impl; | ||||
|  | ||||
| import cn.hutool.core.date.DateUtil; | ||||
| import cn.hutool.core.date.LocalDateTimeUtil; | ||||
| import cn.hutool.core.util.IdUtil; | ||||
| import lombok.RequiredArgsConstructor; | ||||
|  | ||||
| import org.springframework.stereotype.Service; | ||||
|  | ||||
| import top.continew.admin.open.model.resp.AppSecretGetResp; | ||||
| import top.continew.starter.extension.crud.service.impl.BaseServiceImpl; | ||||
| import top.continew.admin.open.mapper.AppMapper; | ||||
| import top.continew.admin.open.model.entity.AppDO; | ||||
| import top.continew.admin.open.model.query.AppQuery; | ||||
| import top.continew.admin.open.model.req.AppReq; | ||||
| import top.continew.admin.open.model.resp.AppDetailResp; | ||||
| import top.continew.admin.open.model.resp.AppResp; | ||||
| import top.continew.admin.open.service.AppService; | ||||
|  | ||||
| import java.time.LocalDateTime; | ||||
|  | ||||
| /** | ||||
|  * 应用业务实现 | ||||
|  * | ||||
|  * @author chengzi | ||||
|  * @since 2024/10/17 16:03 | ||||
|  */ | ||||
| @Service | ||||
| @RequiredArgsConstructor | ||||
| public class AppServiceImpl extends BaseServiceImpl<AppMapper, AppDO, AppResp, AppDetailResp, AppQuery, AppReq> implements AppService { | ||||
|  | ||||
|     // 已激活 | ||||
|     private final static String APP_ENABLED_KEY = "1"; | ||||
|     // 未激活 | ||||
|     private final static String APP_DISABLED_KEY = "0"; | ||||
|  | ||||
|     @Override | ||||
|     public AppSecretGetResp getAppSecretById(Long id) { | ||||
|         AppDO app = baseMapper.lambdaQuery().eq(AppDO::getId, id).one(); | ||||
|         String appSecret = "********"; | ||||
|         if (app.getSecretStatus().equals(APP_DISABLED_KEY)) { | ||||
|             appSecret = app.getAppSecret(); | ||||
|             this.resetAppSecretStatusById(id, APP_ENABLED_KEY); | ||||
|         } | ||||
|         AppSecretGetResp appSecretGetResp = new AppSecretGetResp(); | ||||
|         appSecretGetResp.setAppKey(app.getAppKey()); | ||||
|         appSecretGetResp.setAppSecret(appSecret); | ||||
|         return appSecretGetResp; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void resetAppSecretStatusById(Long id, String status) { | ||||
|         baseMapper.lambdaUpdate().set(AppDO::getSecretStatus, status).eq(AppDO::getId, id).update(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void resetAppSecretStatusByAppkey(String appKey, String status) { | ||||
|         baseMapper.lambdaUpdate().set(AppDO::getSecretStatus, status).eq(AppDO::getAppKey, appKey).update(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void refreshAppSecretByID(Long id) { | ||||
|         baseMapper.lambdaUpdate().set(AppDO::getAppSecret, IdUtil.simpleUUID()).eq(AppDO::getId, id).update(); | ||||
|         this.resetAppSecretStatusById(id, APP_DISABLED_KEY); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String getAppSecretByAppKey(String appKey) { | ||||
|         return baseMapper.lambdaQuery().select(AppDO::getAppSecret).eq(AppDO::getAppKey, appKey).one().getAppSecret(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean isExistAppKey(String appKey) { | ||||
|         return baseMapper.lambdaQuery().eq(AppDO::getAppKey, appKey).exists(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean isExpireAppKey(String appKey) { | ||||
|         LocalDateTime expirationTime = baseMapper.lambdaQuery() | ||||
|             .select(AppDO::getExpirationTime) | ||||
|             .eq(AppDO::getAppKey, appKey) | ||||
|             .one() | ||||
|             .getExpirationTime(); | ||||
|         return expirationTime.isBefore(LocalDateTimeUtil.of(DateUtil.date())); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,92 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * 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.admin.open.sign; | ||||
|  | ||||
| import cn.dev33.satoken.error.SaErrorCode; | ||||
| import cn.dev33.satoken.exception.SaSignException; | ||||
| import cn.dev33.satoken.sign.SaSignTemplate; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import org.springframework.stereotype.Component; | ||||
| import top.continew.admin.open.service.AppService; | ||||
|  | ||||
| import java.util.Map; | ||||
| import java.util.TreeMap; | ||||
|  | ||||
| import static cn.dev33.satoken.SaManager.log; | ||||
|  | ||||
| @Component | ||||
| @RequiredArgsConstructor | ||||
| public class OpenSignTemplate extends SaSignTemplate { | ||||
|  | ||||
|     private final AppService appService; | ||||
|     public static String appKey = "appkey"; | ||||
|  | ||||
|     @Override | ||||
|     public void checkParamMap(Map<String, String> paramMap) { | ||||
|         // 获取必须的参数 | ||||
|         String timestampValue = paramMap.get(timestamp); | ||||
|         String nonceValue = paramMap.get(nonce); | ||||
|         String signValue = paramMap.get(sign); | ||||
|         String appKeyValue = paramMap.get(appKey); | ||||
|  | ||||
|         // 参数非空校验 | ||||
|         SaSignException.notEmpty(timestampValue, "缺少 timestamp 字段"); | ||||
|         SaSignException.notEmpty(nonceValue, "缺少 nonce 字段"); | ||||
|         SaSignException.notEmpty(signValue, "缺少 sign 字段"); | ||||
|         SaSignException.notEmpty(appKeyValue, "缺少 appkey 字段"); | ||||
|  | ||||
|         // 应用存在性校验 | ||||
|         SaSignException.notTrue(!appService.isExistAppKey(appKeyValue), "应用不存在"); | ||||
|  | ||||
|         // 应用是否过期校验 | ||||
|         SaSignException.notTrue(appService.isExpireAppKey(appKeyValue), "应用已过期"); | ||||
|  | ||||
|         // 依次校验三个参数 | ||||
|         checkTimestamp(Long.parseLong(timestampValue)); | ||||
|         checkNonce(nonceValue); | ||||
|         checkSign(paramMap, signValue); | ||||
|  | ||||
|         // 通过 √ | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String createSign(Map<String, ?> paramsMap) { | ||||
|         // 根据应用密钥获取对应的应用密码 | ||||
|         String appKey = (String)((Map)paramsMap).get("appkey"); | ||||
|         String secretKey = this.appService.getAppSecretByAppKey(appKey); | ||||
|         SaSignException.notEmpty(secretKey, "参与参数签名的秘钥不可为空", SaErrorCode.CODE_12201); | ||||
|  | ||||
|         // 如果调用者不小心传入了 sign 参数,则此处需要将 sign 参数排除在外 | ||||
|         if (paramsMap.containsKey(sign)) { | ||||
|             // 为了保证不影响原有的 paramsMap,此处需要再复制一份 | ||||
|             paramsMap = new TreeMap<>(paramsMap); | ||||
|             paramsMap.remove(sign); | ||||
|         } | ||||
|  | ||||
|         // 计算签名 | ||||
|         String paramsStr = joinParamsDictSort(paramsMap); | ||||
|         String fullStr = paramsStr + "&" + key + "=" + secretKey; | ||||
|         String signStr = abstractStr(fullStr); | ||||
|  | ||||
|         // 输入日志,方便调试 | ||||
|         log.debug("fullStr:{}", fullStr); | ||||
|         log.debug("signStr:{}", signStr); | ||||
|  | ||||
|         // 返回 | ||||
|         return signStr; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,46 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * 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.admin.open.util; | ||||
|  | ||||
| import cn.dev33.satoken.context.SaHolder; | ||||
| import cn.dev33.satoken.context.model.SaRequest; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
|  * API签名验证工具类 | ||||
|  * | ||||
|  * @author chengzi | ||||
|  * @since 2024/10/25 15:31 | ||||
|  */ | ||||
| public class ApiSignCheckUtils { | ||||
|  | ||||
|     private ApiSignCheckUtils() { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 判断请求是否包含sign参数 | ||||
|      * | ||||
|      * @return 是否包含sign参数(true:包含;false:不包含) | ||||
|      */ | ||||
|     public static boolean isExistSignParam() { | ||||
|         SaRequest saRequest = SaHolder.getRequest(); | ||||
|         List<String> paramNames = saRequest.getParamNames(); | ||||
|         return paramNames.stream().anyMatch(paramName -> paramName.equals("sign")); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -50,6 +50,12 @@ | ||||
|             <artifactId>continew-admin-generator</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- 能力开放模块(包括应用管理、API开放授权、API开发等) --> | ||||
|         <dependency> | ||||
|             <groupId>top.continew</groupId> | ||||
|             <artifactId>continew-admin-open</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- Liquibase(用于管理数据库版本,跟踪、管理和应用数据库变化) --> | ||||
|         <dependency> | ||||
|             <groupId>org.liquibase</groupId> | ||||
|   | ||||
| @@ -16,8 +16,12 @@ | ||||
|  | ||||
| package top.continew.admin.config.satoken; | ||||
|  | ||||
| import cn.dev33.satoken.SaManager; | ||||
| import cn.dev33.satoken.context.SaHolder; | ||||
| import cn.dev33.satoken.context.model.SaRequest; | ||||
| import cn.dev33.satoken.interceptor.SaInterceptor; | ||||
| import cn.dev33.satoken.router.SaRouter; | ||||
| import cn.dev33.satoken.sign.SaSignUtil; | ||||
| import cn.dev33.satoken.stp.StpInterface; | ||||
| import cn.dev33.satoken.stp.StpUtil; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| @@ -25,10 +29,13 @@ import org.springframework.context.annotation.Bean; | ||||
| import org.springframework.context.annotation.Configuration; | ||||
| import top.continew.admin.common.context.UserContext; | ||||
| import top.continew.admin.common.context.UserContextHolder; | ||||
| import top.continew.admin.open.sign.OpenSignTemplate; | ||||
| import top.continew.starter.auth.satoken.autoconfigure.SaTokenExtensionProperties; | ||||
| import top.continew.starter.core.constant.StringConstants; | ||||
| import top.continew.starter.core.util.validate.CheckUtils; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
|  * Sa-Token 配置 | ||||
|  * | ||||
| @@ -41,6 +48,7 @@ public class SaTokenConfiguration { | ||||
|  | ||||
|     private final SaTokenExtensionProperties properties; | ||||
|     private final LoginPasswordProperties loginPasswordProperties; | ||||
|     private final OpenSignTemplate openSignTemplate; | ||||
|  | ||||
|     /** | ||||
|      * Sa-Token 权限认证配置 | ||||
| @@ -55,15 +63,31 @@ public class SaTokenConfiguration { | ||||
|      */ | ||||
|     @Bean | ||||
|     public SaInterceptor saInterceptor() { | ||||
|         SaManager.setSaSignTemplate(openSignTemplate); | ||||
|         return new SaExtensionInterceptor(handle -> SaRouter.match(StringConstants.PATH_PATTERN) | ||||
|             .notMatch(properties.getSecurity().getExcludes()) | ||||
|             .check(r -> { | ||||
|                 StpUtil.checkLogin(); | ||||
|                 if (SaRouter.isMatchCurrURI(loginPasswordProperties.getExcludes())) { | ||||
|                     return; | ||||
|                 // 拦截验证sign | ||||
|                 // 判断是否包含sign参数 | ||||
|                 SaRequest saRequest = SaHolder.getRequest(); | ||||
|                 List<String> paramNames = saRequest.getParamNames(); | ||||
|                 boolean matchParamSign = paramNames.stream().anyMatch(paramName -> paramName.equals("sign")); | ||||
|                 // 如果包含sign参数走SaToken API接口参数签名验证 | ||||
|                 if (matchParamSign) { | ||||
|                     try { | ||||
|                         SaSignUtil.checkRequest(saRequest); | ||||
|                     } catch (Exception e) { | ||||
|                         CheckUtils.throwIf(true, e.getMessage()); | ||||
|                     } | ||||
|                 } else { | ||||
|                     // 如果不包含sign参数走登录token验证 | ||||
|                     StpUtil.checkLogin(); | ||||
|                     if (SaRouter.isMatchCurrURI(loginPasswordProperties.getExcludes())) { | ||||
|                         return; | ||||
|                     } | ||||
|                     UserContext userContext = UserContextHolder.getContext(); | ||||
|                     CheckUtils.throwIf(userContext.isPasswordExpired(), "密码已过期,请修改密码"); | ||||
|                 } | ||||
|                 UserContext userContext = UserContextHolder.getContext(); | ||||
|                 CheckUtils.throwIf(userContext.isPasswordExpired(), "密码已过期,请修改密码"); | ||||
|             })); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,79 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * 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.admin.controller.open; | ||||
|  | ||||
| import cn.dev33.satoken.annotation.SaCheckPermission; | ||||
| import io.swagger.v3.oas.annotations.Operation; | ||||
| import io.swagger.v3.oas.annotations.Parameter; | ||||
| import io.swagger.v3.oas.annotations.enums.ParameterIn; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import top.continew.admin.open.model.resp.AppSecretGetResp; | ||||
| import top.continew.starter.extension.crud.enums.Api; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.tags.Tag; | ||||
|  | ||||
| import org.springframework.web.bind.annotation.*; | ||||
|  | ||||
| import top.continew.starter.extension.crud.annotation.CrudRequestMapping; | ||||
| import top.continew.starter.extension.crud.controller.BaseController; | ||||
| import top.continew.admin.open.model.query.AppQuery; | ||||
| import top.continew.admin.open.model.req.AppReq; | ||||
| import top.continew.admin.open.model.resp.AppDetailResp; | ||||
| import top.continew.admin.open.model.resp.AppResp; | ||||
| import top.continew.admin.open.service.AppService; | ||||
| import top.continew.starter.extension.crud.model.resp.BaseIdResp; | ||||
|  | ||||
| /** | ||||
|  * 应用管理 API | ||||
|  * | ||||
|  * @author chengzi | ||||
|  * @since 2024/10/17 16:03 | ||||
|  */ | ||||
| @Tag(name = "应用管理 API") | ||||
| @RestController | ||||
| @RequiredArgsConstructor | ||||
| @CrudRequestMapping(value = "/open/app", api = {Api.PAGE, Api.GET, Api.ADD, Api.UPDATE, Api.DELETE, Api.EXPORT}) | ||||
| public class AppController extends BaseController<AppService, AppResp, AppDetailResp, AppQuery, AppReq> { | ||||
|  | ||||
|     private final AppService appService; | ||||
|     private final static String APP_DISABLED_KEY = "0"; | ||||
|  | ||||
|     @Operation(summary = "刷新应用密码", description = "刷新应用密码") | ||||
|     @Parameter(name = "id", description = "ID", example = "test", in = ParameterIn.PATH) | ||||
|     @SaCheckPermission("open:app:refreshas") | ||||
|     @GetMapping(value = "/{id}/refreshas") | ||||
|     public void refreshAppSecret(@PathVariable Long id) { | ||||
|         appService.refreshAppSecretByID(id); | ||||
|     } | ||||
|  | ||||
|     @Operation(summary = "获取应用密码", description = "获取应用密码") | ||||
|     @Parameter(name = "appKey", description = "应用密钥", example = "test", in = ParameterIn.PATH) | ||||
|     @SaCheckPermission("open:app:getas") | ||||
|     @GetMapping("/{id}/appsecret") | ||||
|     public AppSecretGetResp getAppSecret(@PathVariable Long id) { | ||||
|         return appService.getAppSecretById(id); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public BaseIdResp<Long> add(AppReq req) { | ||||
|         BaseIdResp<Long> baseIdResp = super.add(req); | ||||
|         Long appId = baseIdResp.getId(); | ||||
|         appService.refreshAppSecretByID(appId); | ||||
|         appService.resetAppSecretStatusById(appId, APP_DISABLED_KEY); | ||||
|         return baseIdResp; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,193 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * 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.admin.open.util.ApiSignCheckUtils; | ||||
| 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("/tree") | ||||
|     public List<Tree<Long>> tree(Q query, SortQuery sortQuery) { | ||||
|         this.checkPermission(Api.LIST); | ||||
|         return baseService.tree(query, sortQuery, false); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 查询列表 | ||||
|      * | ||||
|      * @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 id ID | ||||
|      * @return 详情信息 | ||||
|      */ | ||||
|     @Operation(summary = "查询详情", description = "查询详情") | ||||
|     @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH) | ||||
|     @ResponseBody | ||||
|     @GetMapping("/{id}") | ||||
|     public D get(@PathVariable("id") Long id) { | ||||
|         this.checkPermission(Api.LIST); | ||||
|         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 BaseIdResp.<Long>builder().id(baseService.add(req)).build(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 修改 | ||||
|      * | ||||
|      * @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) { | ||||
|         // 判断是否包含sign参数 | ||||
|         if (!ApiSignCheckUtils.isExistSignParam()) { | ||||
|             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())); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -4,3 +4,34 @@ ALTER TABLE sys_notice | ||||
|     ADD COLUMN notice_scope INT NOT NULL COMMENT '通知范围' AFTER terminate_time, | ||||
|     ADD COLUMN notice_users JSON DEFAULT NULL COMMENT '通知用户' AFTER notice_scope; | ||||
| COMMIT; | ||||
|  | ||||
| -- changeset chengzi | ||||
| -- comment 新增应用表 | ||||
| CREATE TABLE IF NOT EXISTS `sys_app`  ( | ||||
|     `id`              bigint(0) NOT NULL COMMENT 'ID', | ||||
|     `name`            varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '应用名称', | ||||
|     `app_key`         varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '应用密钥', | ||||
|     `app_secret`      varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '应用密码', | ||||
|     `status`          varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '应用状态(0:未激活;1:激活)', | ||||
|     `expiration_time` datetime(0) NULL DEFAULT NULL COMMENT '失效时间', | ||||
|     `app_desc`        varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '应用描述', | ||||
|     `secret_status`   varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '应用密码查看状态(0:未查看;1:已查看)', | ||||
|     `create_user`     bigint(0) NOT NULL COMMENT '创建人', | ||||
|     `create_time`     datetime(0) NOT NULL COMMENT '创建时间', | ||||
|     `update_user`     bigint(0) NULL DEFAULT NULL COMMENT '修改人', | ||||
|     `update_time`     datetime(0) NULL DEFAULT NULL COMMENT '修改时间', | ||||
|     PRIMARY KEY (`id`) USING BTREE | ||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用表'; | ||||
| INSERT INTO `sys_app` VALUES (639144999463690263, 'ContiNewAdmin', 'admin', '9efcf8859d754288941e61adc72cd250', '1', '2024-10-31 16:53:52', 'ContiNew Admin(Continue New Admin)持续迭代优化的前后端分离中后台管理系统框架。开箱即用,重视每一处代码规范,重视每一种解决方案细节,持续提供舒适的前、后端开发体验。', '1', 1, '2024-10-22 16:54:03', NULL, NULL); | ||||
| -- 应用管理菜单数据 | ||||
| INSERT INTO `sys_menu` VALUES (635516486647025735, '能力开放', 0, 1, '/open', 'Open', 'Layout', NULL, 'expand', b'0', b'0', b'0', NULL, 2, 1, 1, '2024-10-12 16:35:38', 1, '2024-10-12 16:35:53'); | ||||
| INSERT INTO `sys_menu` VALUES (635516794676711501, '应用管理', 635516486647025735, 2, '/open/app', 'OpenApp', 'open/app/index', NULL, 'common', b'0', b'0', b'0', NULL, 1, 1, 1, '2024-10-12 16:36:51', 1, '2024-10-12 16:49:11'); | ||||
| INSERT INTO `sys_menu` VALUES (636598391530328174, '新增', 635516794676711501, 3, NULL, NULL, NULL, NULL, NULL, b'0', b'0', b'0', 'open:app:add', 1, 1, 1, '2024-10-15 16:14:44', 1, '2024-10-15 16:45:36'); | ||||
| INSERT INTO `sys_menu` VALUES (636599310447808642, '查看', 635516794676711501, 3, NULL, NULL, NULL, NULL, NULL, b'0', b'0', b'0', 'open:app:list', 999, 1, 1, '2024-10-15 16:18:23', NULL, NULL); | ||||
| INSERT INTO `sys_menu` VALUES (636599448054534277, '导出', 635516794676711501, 3, NULL, NULL, NULL, NULL, NULL, b'0', b'0', b'0', 'open:app:export', 999, 1, 1, '2024-10-15 16:18:56', NULL, NULL); | ||||
| INSERT INTO `sys_menu` VALUES (637299919924760580, '删除', 635516794676711501, 3, NULL, NULL, NULL, NULL, NULL, b'0', b'0', b'0', 'open:app:delete', 1, 1, 1, '2024-10-17 14:42:21', NULL, NULL); | ||||
| INSERT INTO `sys_menu` VALUES (637300206014042119, '修改', 635516794676711501, 3, NULL, NULL, NULL, NULL, NULL, b'0', b'0', b'0', 'open:app:update', 1, 1, 1, '2024-10-17 14:43:30', NULL, NULL); | ||||
| -- 应用管理字典数据 | ||||
| INSERT INTO `sys_dict` VALUES (639152724557963332, '应用状态', 'app_type', NULL, b'0', 1, '2024-10-22 17:24:44', NULL, NULL); | ||||
| INSERT INTO `sys_dict_item` VALUES (639152915579150411, '禁用', '0', 'blue', 999, NULL, 1, 639152724557963332, 1, '2024-10-22 17:25:30', NULL, NULL); | ||||
| INSERT INTO `sys_dict_item` VALUES (639153003290435665, '启用', '1', 'green', 999, NULL, 1, 639152724557963332, 1, '2024-10-22 17:25:51', NULL, NULL); | ||||
							
								
								
									
										8
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								pom.xml
									
									
									
									
									
								
							| @@ -29,6 +29,7 @@ | ||||
|         <module>continew-admin-system</module> | ||||
|         <module>continew-admin-common</module> | ||||
|         <module>continew-admin-extension</module> | ||||
|         <module>continew-admin-open</module> | ||||
|     </modules> | ||||
|  | ||||
|     <properties> | ||||
| @@ -73,6 +74,13 @@ | ||||
|                 <artifactId>continew-admin-generator</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- 能力开放模块 --> | ||||
|             <dependency> | ||||
|                 <groupId>top.continew</groupId> | ||||
|                 <artifactId>continew-admin-open</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|         </dependencies> | ||||
|     </dependencyManagement> | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 cz176
					cz176