mirror of
				https://github.com/continew-org/continew-admin.git
				synced 2025-10-31 22:57:17 +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> |             <artifactId>continew-admin-generator</artifactId> | ||||||
|         </dependency> |         </dependency> | ||||||
|  |  | ||||||
|  |         <!-- 能力开放模块(包括应用管理、API开放授权、API开发等) --> | ||||||
|  |         <dependency> | ||||||
|  |             <groupId>top.continew</groupId> | ||||||
|  |             <artifactId>continew-admin-open</artifactId> | ||||||
|  |         </dependency> | ||||||
|  |  | ||||||
|         <!-- Liquibase(用于管理数据库版本,跟踪、管理和应用数据库变化) --> |         <!-- Liquibase(用于管理数据库版本,跟踪、管理和应用数据库变化) --> | ||||||
|         <dependency> |         <dependency> | ||||||
|             <groupId>org.liquibase</groupId> |             <groupId>org.liquibase</groupId> | ||||||
|   | |||||||
| @@ -16,8 +16,12 @@ | |||||||
|  |  | ||||||
| package top.continew.admin.config.satoken; | 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.interceptor.SaInterceptor; | ||||||
| import cn.dev33.satoken.router.SaRouter; | import cn.dev33.satoken.router.SaRouter; | ||||||
|  | import cn.dev33.satoken.sign.SaSignUtil; | ||||||
| import cn.dev33.satoken.stp.StpInterface; | import cn.dev33.satoken.stp.StpInterface; | ||||||
| import cn.dev33.satoken.stp.StpUtil; | import cn.dev33.satoken.stp.StpUtil; | ||||||
| import lombok.RequiredArgsConstructor; | import lombok.RequiredArgsConstructor; | ||||||
| @@ -25,10 +29,13 @@ import org.springframework.context.annotation.Bean; | |||||||
| import org.springframework.context.annotation.Configuration; | import org.springframework.context.annotation.Configuration; | ||||||
| import top.continew.admin.common.context.UserContext; | import top.continew.admin.common.context.UserContext; | ||||||
| import top.continew.admin.common.context.UserContextHolder; | 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.auth.satoken.autoconfigure.SaTokenExtensionProperties; | ||||||
| import top.continew.starter.core.constant.StringConstants; | import top.continew.starter.core.constant.StringConstants; | ||||||
| import top.continew.starter.core.util.validate.CheckUtils; | import top.continew.starter.core.util.validate.CheckUtils; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Sa-Token 配置 |  * Sa-Token 配置 | ||||||
|  * |  * | ||||||
| @@ -41,6 +48,7 @@ public class SaTokenConfiguration { | |||||||
|  |  | ||||||
|     private final SaTokenExtensionProperties properties; |     private final SaTokenExtensionProperties properties; | ||||||
|     private final LoginPasswordProperties loginPasswordProperties; |     private final LoginPasswordProperties loginPasswordProperties; | ||||||
|  |     private final OpenSignTemplate openSignTemplate; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Sa-Token 权限认证配置 |      * Sa-Token 权限认证配置 | ||||||
| @@ -55,15 +63,31 @@ public class SaTokenConfiguration { | |||||||
|      */ |      */ | ||||||
|     @Bean |     @Bean | ||||||
|     public SaInterceptor saInterceptor() { |     public SaInterceptor saInterceptor() { | ||||||
|  |         SaManager.setSaSignTemplate(openSignTemplate); | ||||||
|         return new SaExtensionInterceptor(handle -> SaRouter.match(StringConstants.PATH_PATTERN) |         return new SaExtensionInterceptor(handle -> SaRouter.match(StringConstants.PATH_PATTERN) | ||||||
|             .notMatch(properties.getSecurity().getExcludes()) |             .notMatch(properties.getSecurity().getExcludes()) | ||||||
|             .check(r -> { |             .check(r -> { | ||||||
|                 StpUtil.checkLogin(); |                 // 拦截验证sign | ||||||
|                 if (SaRouter.isMatchCurrURI(loginPasswordProperties.getExcludes())) { |                 // 判断是否包含sign参数 | ||||||
|                     return; |                 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_scope INT NOT NULL COMMENT '通知范围' AFTER terminate_time, | ||||||
|     ADD COLUMN notice_users JSON DEFAULT NULL COMMENT '通知用户' AFTER notice_scope; |     ADD COLUMN notice_users JSON DEFAULT NULL COMMENT '通知用户' AFTER notice_scope; | ||||||
| COMMIT; | 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-system</module> | ||||||
|         <module>continew-admin-common</module> |         <module>continew-admin-common</module> | ||||||
|         <module>continew-admin-extension</module> |         <module>continew-admin-extension</module> | ||||||
|  |         <module>continew-admin-open</module> | ||||||
|     </modules> |     </modules> | ||||||
|  |  | ||||||
|     <properties> |     <properties> | ||||||
| @@ -73,6 +74,13 @@ | |||||||
|                 <artifactId>continew-admin-generator</artifactId> |                 <artifactId>continew-admin-generator</artifactId> | ||||||
|                 <version>${revision}</version> |                 <version>${revision}</version> | ||||||
|             </dependency> |             </dependency> | ||||||
|  |  | ||||||
|  |             <!-- 能力开放模块 --> | ||||||
|  |             <dependency> | ||||||
|  |                 <groupId>top.continew</groupId> | ||||||
|  |                 <artifactId>continew-admin-open</artifactId> | ||||||
|  |                 <version>${revision}</version> | ||||||
|  |             </dependency> | ||||||
|         </dependencies> |         </dependencies> | ||||||
|     </dependencyManagement> |     </dependencyManagement> | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 cz176
					cz176