mirror of
https://github.com/cool-team-official/cool-admin-java.git
synced 2026-10-06 14:33:10 +08:00
release v7.1
This commit is contained in:
+37
@@ -0,0 +1,37 @@
|
|||||||
|
HELP.md
|
||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**/target/
|
||||||
|
!**/src/test/**/target/
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
assets/
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
.DS_Store
|
||||||
|
lib
|
||||||
|
plugin
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
# 使用 GraalVM 17 作为基础镜像
|
||||||
|
FROM ghcr.io/graalvm/graalvm-ce:latest
|
||||||
|
|
||||||
|
# 设置容器内的工作目录
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 将可执行的jar文件复制到容器内
|
||||||
|
COPY target/cool-admin-7.1.0.jar /app/cool-admin-7.1.0.jar
|
||||||
|
|
||||||
|
# 暴露Spring Boot应用程序运行的端口
|
||||||
|
EXPOSE 8001
|
||||||
|
|
||||||
|
# 运行Spring Boot应用程序的命令
|
||||||
|
ENTRYPOINT ["java", "-jar", "/app/cool-admin-7.1.0.jar", "--spring.profiles.active=prod"]
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 cool-team-official
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -1,2 +1,92 @@
|
|||||||
# cool-admin-java
|
|
||||||
🔥 cool-admin(java版)一个很酷的后台权限管理框架,Ai编码、流程编排、模块化、插件化、CRUD极速开发,永久开源免费,基于springboot3、typescript、vue3、vite、element-ui等构建
|
<p align="center">
|
||||||
|
<a href="https://midwayjs.org/" target="blank"><img src="https://cool-show.oss-cn-shanghai.aliyuncs.com/admin/logo.png" width="200" alt="Midway Logo" /></a>
|
||||||
|
</p>
|
||||||
|
<p align="center">cool-admin(java版)后台权限管理系统,开源免费,Ai编码、流程编排、模块化、插件化,用于快速构建后台应用程序,详情可到<a href="https://cool-admin.com" target="_blank">官网</a> 进一步了解。
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://github.com/cool-team-official/cool-admin-midway/blob/master/LICENSE" target="_blank"><img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="GitHub license" />
|
||||||
|
<a href=""><img src="https://img.shields.io/github/package-json/v/cool-team-official/cool-admin-midway?style=flat-square" alt="GitHub tag"></a>
|
||||||
|
<img src="https://img.shields.io/github/last-commit/cool-team-official/cool-admin-midway?style=flat-square" alt="GitHub tag"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## 技术栈
|
||||||
|
|
||||||
|
- 后端:**`Springboot3` `Mybatis-Flex`**
|
||||||
|
- 前端:**`Vue3` `Vite` `Element-Ui` `Typescript`**
|
||||||
|
- 数据库:**`Mysql` `Postgresql(适配中)` `Sqlite(适配中)` `...`**
|
||||||
|
|
||||||
|
## 特性
|
||||||
|
|
||||||
|
Ai时代,很多老旧的框架已经无法满足现代化的开发需求,Cool-Admin开发了一系列的功能,让开发变得更简单、更快速、更高效。
|
||||||
|
|
||||||
|
- **Ai编码**:通过微调大模型学习框架特有写法,实现简单功能从Api接口到前端页面的一键生成
|
||||||
|
- **流程编排**:通过拖拽编排方式,即可实现类似像智能客服这样的功能
|
||||||
|
- **模块化**:代码是模块化的,清晰明了,方便维护
|
||||||
|
- **插件化**:插件化的设计,可以通过安装插件的方式扩展如:支付、短信、邮件等功能
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 地址
|
||||||
|
|
||||||
|
- 官网:[https://cool-admin.com](https://cool-admin.com)
|
||||||
|
- 文档:[https://java.cool-admin.com](https://java.cool-admin.com)
|
||||||
|
|
||||||
|
## 演示
|
||||||
|
|
||||||
|
[https://show.cool-admin.com](https://show.cool-admin.com)
|
||||||
|
|
||||||
|
- 账户:admin
|
||||||
|
- 密码:123456
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### 项目前端
|
||||||
|
|
||||||
|
系统是前后端分离的,启动完成后,还需要启动前端项目,前端项目地址:
|
||||||
|
|
||||||
|
[https://github.com/cool-team-official/cool-admin-vue](https://github.com/cool-team-official/cool-admin-vue)
|
||||||
|
|
||||||
|
或
|
||||||
|
|
||||||
|
[https://gitee.com/cool-team-official/cool-admin-vue](https://gitee.com/cool-team-official/cool-admin-vue)
|
||||||
|
|
||||||
|
## 微信群
|
||||||
|
|
||||||
|
<img width="260" src="https://cool-show.oss-cn-shanghai.aliyuncs.com/admin/wechat.jpeg?v=1" alt="Admin Wechat"></a>
|
||||||
|
|
||||||
|
## 运行
|
||||||
|
|
||||||
|
### 环境要求
|
||||||
|
|
||||||
|
- Java Graalvm 17+
|
||||||
|
- Maven 3.6+
|
||||||
|
|
||||||
|
### 配置
|
||||||
|
|
||||||
|
修改数据库配置,配置文件位于`src/resources/application-local.yml`
|
||||||
|
|
||||||
|
以 Mysql 为例,其他数据库请参考[数据库配置文档](https://cool-js.com/admin/node/quick.html#%E6%95%B0%E6%8D%AE%E5%BA%93%E9%85%8D%E7%BD%AE)
|
||||||
|
|
||||||
|
Mysql(`>=5.7版本`),建议 8.0,首次启动会自动初始化并导入数据
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# mysql,驱动已经内置,无需安装
|
||||||
|
spring:
|
||||||
|
datasource:
|
||||||
|
url: jdbc:mysql://127.0.0.1:3306/cool?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8
|
||||||
|
username: root
|
||||||
|
password: 123456
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
```
|
||||||
|
|
||||||
|
### 启动
|
||||||
|
|
||||||
|
注:项目使用到了[Mybatis-Flex 的Apt功能](https://mybatis-flex.com/zh/others/apt.html),如果启动报错,请先执行`mvn compile`编译
|
||||||
|
|
||||||
|
1、启动文件:`src/main/java/com/cool/CoolApplication.java`
|
||||||
|
|
||||||
|
2、启动完成后,访问:[http://localhost:8001](http://localhost:8001)
|
||||||
|
|
||||||
|
3、如果看到以下界面,说明启动成功。这时候再启动前端项目即可,数据库会自动初始化,默认账号:admin,密码:123456
|
||||||
|
|
||||||
|

|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module version="4">
|
||||||
|
<component name="AdditionalModuleElements">
|
||||||
|
<content url="file://$MODULE_DIR$" dumb="true">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/target/generated-sources/annotations/src/main/java" isTestSource="false" />
|
||||||
|
</content>
|
||||||
|
<content url="file://$MODULE_DIR$" dumb="true">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/target/generated-sources/annotations" isTestSource="false" />
|
||||||
|
</content>
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# 本地数据库环境
|
||||||
|
# 数据存放在当前目录下的 data里
|
||||||
|
# 推荐使用安装了docker扩展的vscode打开目录 在本文件上右键可以快速启动,停止
|
||||||
|
# 如不需要相关容器开机自启动,可注释掉 restart: always
|
||||||
|
# 如遇端口冲突 可调整ports下 :前面的端口号
|
||||||
|
version: "3.1"
|
||||||
|
|
||||||
|
services:
|
||||||
|
coolDB:
|
||||||
|
image: mysql
|
||||||
|
command:
|
||||||
|
--default-authentication-plugin=mysql_native_password
|
||||||
|
--sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
|
||||||
|
--group_concat_max_len=102400
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- ./data/mysql/:/var/lib/mysql/
|
||||||
|
environment:
|
||||||
|
TZ: Asia/Shanghai # 指定时区
|
||||||
|
MYSQL_ROOT_PASSWORD: "123456" # 配置root用户密码
|
||||||
|
MYSQL_DATABASE: "cool" # 业务库名
|
||||||
|
MYSQL_USER: "root" # 业务库用户名
|
||||||
|
MYSQL_PASSWORD: "123456" # 业务库密码
|
||||||
|
networks:
|
||||||
|
- cool
|
||||||
|
ports:
|
||||||
|
- 3306:3306
|
||||||
|
|
||||||
|
coolRedis:
|
||||||
|
image: redis
|
||||||
|
#command: --requirepass "12345678" # redis库密码,不需要密码注释本行
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
TZ: Asia/Shanghai # 指定时区
|
||||||
|
volumes:
|
||||||
|
- ./data/redis/:/data/
|
||||||
|
networks:
|
||||||
|
- cool
|
||||||
|
ports:
|
||||||
|
- 6379:6379
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>3.2.5</version>
|
||||||
|
<relativePath/>
|
||||||
|
<!-- lookup parent from repository -->
|
||||||
|
</parent>
|
||||||
|
<groupId>com.cool</groupId>
|
||||||
|
<artifactId>cool-admin</artifactId>
|
||||||
|
<version>7.1.0</version>
|
||||||
|
<name>cool-admin</name>
|
||||||
|
<description>cool admin for java</description>
|
||||||
|
<properties>
|
||||||
|
<java.version>17</java.version>
|
||||||
|
<lombok.version>1.18.34</lombok.version>
|
||||||
|
<mybatis-flex.version>1.9.3</mybatis-flex.version>
|
||||||
|
<mybatis-flex.ext.version>1.9.3.021-beta</mybatis-flex.ext.version>
|
||||||
|
<hutool.version>5.8.26</hutool.version>
|
||||||
|
<ognl.version>3.3.2</ognl.version>
|
||||||
|
<fastjson2.version>2.0.51</fastjson2.version>
|
||||||
|
<springdoc-openapi.version>2.5.0</springdoc-openapi.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- springframework start -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-cache</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-quartz</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring Boot Starter Data Redis -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring Boot Configuration Processor -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-aop</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- springframework end -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||||
|
<artifactId>caffeine</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-j</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.zaxxer</groupId>
|
||||||
|
<artifactId>HikariCP</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-all</artifactId>
|
||||||
|
<version>${hutool.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ognl</groupId>
|
||||||
|
<artifactId>ognl</artifactId>
|
||||||
|
<version>${ognl.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.fastjson2</groupId>
|
||||||
|
<artifactId>fastjson2</artifactId>
|
||||||
|
<version>${fastjson2.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- mybatis-flex-ext -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.tangzc</groupId>
|
||||||
|
<artifactId>mybatis-flex-ext-spring-boot3-starter</artifactId>
|
||||||
|
<version>${mybatis-flex.ext.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springdoc</groupId>
|
||||||
|
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||||
|
<version>${springdoc-openapi.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jakarta.servlet</groupId>
|
||||||
|
<artifactId>jakarta.servlet-api</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.graalvm.buildtools</groupId>
|
||||||
|
<artifactId>native-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<excludes>
|
||||||
|
<exclude>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
</exclude>
|
||||||
|
</excludes>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<annotationProcessorPaths>
|
||||||
|
<path>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok.version}</version>
|
||||||
|
</path>
|
||||||
|
<path>
|
||||||
|
<groupId>com.mybatis-flex</groupId>
|
||||||
|
<artifactId>mybatis-flex-processor</artifactId>
|
||||||
|
<version>${mybatis-flex.version}</version>
|
||||||
|
</path>
|
||||||
|
</annotationProcessorPaths>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>local</id>
|
||||||
|
<activation>
|
||||||
|
<activeByDefault>true</activeByDefault>
|
||||||
|
</activation>
|
||||||
|
<properties>
|
||||||
|
<spring.active>local</spring.active>
|
||||||
|
</properties>
|
||||||
|
</profile>
|
||||||
|
<profile>
|
||||||
|
<id>prod</id>
|
||||||
|
<properties>
|
||||||
|
<spring.active>prod</spring.active>
|
||||||
|
</properties>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.cool;
|
||||||
|
|
||||||
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.cache.annotation.EnableCaching;
|
||||||
|
import org.springframework.scheduling.annotation.EnableAsync;
|
||||||
|
|
||||||
|
import com.tangzc.autotable.springboot.EnableAutoTable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CoolApplication - 应用程序的主类
|
||||||
|
* 该类配置并运行应用程序。
|
||||||
|
*/
|
||||||
|
@EnableAutoTable // 开启自动建表
|
||||||
|
@EnableAsync // 开启异步处理
|
||||||
|
@EnableCaching // 开启缓存
|
||||||
|
@SpringBootApplication
|
||||||
|
@MapperScan("com.cool.modules.*.mapper") // 扫描指定包中的MyBatis映射器
|
||||||
|
public class CoolApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(CoolApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.cool.core.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
public @interface CoolPlugin {
|
||||||
|
String value() default "";
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.cool.core.annotation;
|
||||||
|
|
||||||
|
import org.springframework.core.annotation.AliasFor;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义路由注解
|
||||||
|
*/
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@RestController
|
||||||
|
@RequestMapping
|
||||||
|
public @interface CoolRestController {
|
||||||
|
|
||||||
|
@AliasFor(annotation = RequestMapping.class)
|
||||||
|
String name() default "";
|
||||||
|
|
||||||
|
@AliasFor(annotation = RequestMapping.class)
|
||||||
|
String[] value() default {};
|
||||||
|
|
||||||
|
String[] api() default {};
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.cool.core.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface IgnoreRecycleData {
|
||||||
|
String value() default "";
|
||||||
|
}
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
package com.cool.core.base;
|
||||||
|
|
||||||
|
import cn.hutool.core.convert.Convert;
|
||||||
|
import cn.hutool.core.lang.Dict;
|
||||||
|
import cn.hutool.core.util.TypeUtil;
|
||||||
|
import cn.hutool.json.JSONArray;
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.cool.core.exception.CoolPreconditions;
|
||||||
|
import com.cool.core.request.CrudOption;
|
||||||
|
import com.cool.core.request.R;
|
||||||
|
import com.cool.core.util.SpringContextUtils;
|
||||||
|
import com.mybatisflex.core.paginate.Page;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestAttribute;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 控制层基类
|
||||||
|
*
|
||||||
|
* @param <S>
|
||||||
|
* @param <T>
|
||||||
|
*/
|
||||||
|
public abstract class BaseController<S extends BaseService<T>, T extends BaseEntity<T>> {
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
protected S service;
|
||||||
|
protected Class<T> entityClass;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
private void init() {
|
||||||
|
if (entityClass == null) {
|
||||||
|
this.entityClass = currentEntityClass();
|
||||||
|
}
|
||||||
|
if (service == null) {
|
||||||
|
this.service = (S) SpringContextUtils.getBean(currentServiceClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String COOL_PAGE_OP = "COOL_PAGE_OP";
|
||||||
|
private final String COOL_LIST_OP = "COOL_LIST_OP";
|
||||||
|
|
||||||
|
private final ThreadLocal<CrudOption<T>> pageOption = new ThreadLocal<>();
|
||||||
|
private final ThreadLocal<CrudOption<T>> listOption = new ThreadLocal<>();
|
||||||
|
private final ThreadLocal<JSONObject> requestParams = new ThreadLocal<>();
|
||||||
|
|
||||||
|
@ModelAttribute
|
||||||
|
protected void preHandle(HttpServletRequest request,
|
||||||
|
@RequestAttribute JSONObject requestParams) {
|
||||||
|
this.pageOption.set(new CrudOption<>(requestParams));
|
||||||
|
this.listOption.set(new CrudOption<>(requestParams));
|
||||||
|
this.requestParams.set(requestParams);
|
||||||
|
init(request, requestParams);
|
||||||
|
request.setAttribute(COOL_PAGE_OP, this.pageOption.get());
|
||||||
|
request.setAttribute(COOL_LIST_OP, this.listOption.get());
|
||||||
|
removeThreadLocal();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手动移除变量
|
||||||
|
*/
|
||||||
|
private void removeThreadLocal() {
|
||||||
|
this.listOption.remove();
|
||||||
|
this.pageOption.remove();
|
||||||
|
this.requestParams.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CrudOption<T> createOp() {
|
||||||
|
return new CrudOption<>(this.requestParams.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setListOption(CrudOption<T> listOption) {
|
||||||
|
this.listOption.set(listOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPageOption(CrudOption<T> pageOption) {
|
||||||
|
this.pageOption.set(pageOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void init(HttpServletRequest request, JSONObject requestParams);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增
|
||||||
|
* <p>
|
||||||
|
* // * @param t 实体对象
|
||||||
|
*/
|
||||||
|
@Operation(summary = "新增", description = "新增信息,对应后端的实体类")
|
||||||
|
@PostMapping("/add")
|
||||||
|
protected R add(@RequestAttribute() JSONObject requestParams) {
|
||||||
|
String body = requestParams.getStr("body");
|
||||||
|
if (JSONUtil.isTypeJSONArray(body)) {
|
||||||
|
JSONArray array = JSONUtil.parseArray(body);
|
||||||
|
return R.ok(Dict.create()
|
||||||
|
.set("ids", service.addBatch(requestParams, array.toList(currentEntityClass()))));
|
||||||
|
} else {
|
||||||
|
return R.ok(Dict.create().set("id",
|
||||||
|
service.add(requestParams, JSONUtil.parseObj(body).toBean(currentEntityClass()))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除
|
||||||
|
*
|
||||||
|
* @param params 请求参数 ids 数组 或者按","隔开
|
||||||
|
*/
|
||||||
|
@Operation(summary = "删除", description = "支持批量删除 请求参数 ids 数组 或者按\",\"隔开")
|
||||||
|
@PostMapping("/delete")
|
||||||
|
protected R delete(HttpServletRequest request, @RequestBody Map<String, Object> params,
|
||||||
|
@RequestAttribute() JSONObject requestParams) {
|
||||||
|
service.delete(requestParams, Convert.toLongArray(getIds(params)));
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改
|
||||||
|
*
|
||||||
|
* @param t 修改对象
|
||||||
|
*/
|
||||||
|
@Operation(summary = "修改", description = "根据ID修改")
|
||||||
|
@PostMapping("/update")
|
||||||
|
protected R update(@RequestBody T t, @RequestAttribute() JSONObject requestParams) {
|
||||||
|
Long id = t.getId();
|
||||||
|
JSONObject info = JSONUtil.parseObj(JSONUtil.toJsonStr(service.info(id)));
|
||||||
|
requestParams.forEach(info::set);
|
||||||
|
info.set("updateTime", new Date());
|
||||||
|
service.update(requestParams, JSONUtil.toBean(info, currentEntityClass()));
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 信息
|
||||||
|
*
|
||||||
|
* @param id ID
|
||||||
|
*/
|
||||||
|
@Operation(summary = "信息", description = "根据ID查询单个信息")
|
||||||
|
@GetMapping("/info")
|
||||||
|
protected R info(@RequestAttribute() JSONObject requestParams,
|
||||||
|
@RequestParam() Long id) {
|
||||||
|
return R.ok(service.info(requestParams, id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表查询
|
||||||
|
*
|
||||||
|
* @param requestParams 请求参数
|
||||||
|
*/
|
||||||
|
@Operation(summary = "查询", description = "查询多个信息")
|
||||||
|
@PostMapping("/list")
|
||||||
|
protected R list(@RequestAttribute() JSONObject requestParams,
|
||||||
|
@RequestAttribute(COOL_LIST_OP) CrudOption<T> option) {
|
||||||
|
return R.ok(service.list(requestParams, option.getQueryWrapper(entityClass)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询
|
||||||
|
*
|
||||||
|
* @param requestParams 请求参数
|
||||||
|
*/
|
||||||
|
@Operation(summary = "分页", description = "分页查询多个信息")
|
||||||
|
@PostMapping("/page")
|
||||||
|
protected R page(@RequestAttribute() JSONObject requestParams,
|
||||||
|
@RequestAttribute(COOL_PAGE_OP) CrudOption<T> option) {
|
||||||
|
Integer page = requestParams.getInt("page", 1);
|
||||||
|
Integer size = requestParams.getInt("size", 20);
|
||||||
|
return R.ok(
|
||||||
|
pageResult((Page<T>) service.page(requestParams, new Page<>(page, size),
|
||||||
|
option.getQueryWrapper(entityClass))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页结果
|
||||||
|
*
|
||||||
|
* @param page 分页返回数据
|
||||||
|
*/
|
||||||
|
protected Map<String, Object> pageResult(Page<T> page) {
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
Map<String, Object> pagination = new HashMap<>();
|
||||||
|
pagination.put("size", page.getPageSize());
|
||||||
|
pagination.put("page", page.getPageNumber());
|
||||||
|
pagination.put("total", page.getTotalRow());
|
||||||
|
result.put("list", page.getRecords());
|
||||||
|
result.put("pagination", pagination);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<T> currentEntityClass() {
|
||||||
|
// 使用 获取泛型参数类型
|
||||||
|
Type type = TypeUtil.getTypeArgument(this.getClass(), 1); // 获取第二个泛型参数
|
||||||
|
if (type instanceof Class<?>) {
|
||||||
|
return (Class<T>) type;
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("Unable to determine entity class type");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<T> currentServiceClass() {
|
||||||
|
// 使用 获取泛型参数类型
|
||||||
|
Type type = TypeUtil.getTypeArgument(this.getClass(), 0); // 获取第一个泛型参数
|
||||||
|
if (type instanceof Class<?>) {
|
||||||
|
return (Class<T>) type;
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("Unable to determine entity class type");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<Long> getIds(Map<String, Object> params) {
|
||||||
|
Object ids = params.get("ids");
|
||||||
|
CoolPreconditions.checkEmpty(ids, "ids 参数错误");
|
||||||
|
if (!(ids instanceof ArrayList)) {
|
||||||
|
ids = ids.toString().split(",");
|
||||||
|
}
|
||||||
|
return Convert.toList(Long.class, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.cool.core.base;
|
||||||
|
|
||||||
|
import com.mybatisflex.annotation.Column;
|
||||||
|
import com.mybatisflex.annotation.Id;
|
||||||
|
import com.mybatisflex.annotation.KeyType;
|
||||||
|
import com.mybatisflex.core.activerecord.Model;
|
||||||
|
import com.mybatisflex.core.query.QueryWrapper;
|
||||||
|
import com.tangzc.autotable.annotation.Ignore;
|
||||||
|
import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基础实体类
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public abstract class BaseEntity<T extends Model<T>> extends Model<T> implements Serializable {
|
||||||
|
|
||||||
|
@Id(keyType = KeyType.Auto, comment = "ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(onInsertValue = "now()")
|
||||||
|
@ColumnDefine(comment = "创建时间")
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
@Column(onInsertValue = "now()", onUpdateValue = "now()")
|
||||||
|
@ColumnDefine(comment = "更新时间")
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
@Ignore
|
||||||
|
@Column(ignore = true)
|
||||||
|
private QueryWrapper queryWrapper;
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
package com.cool.core.base;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.mybatisflex.core.paginate.Page;
|
||||||
|
import com.mybatisflex.core.query.QueryWrapper;
|
||||||
|
import com.mybatisflex.core.service.IService;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基础service类
|
||||||
|
*
|
||||||
|
* @param <T> 实体
|
||||||
|
*/
|
||||||
|
public interface BaseService<T> extends IService<T> {
|
||||||
|
/**
|
||||||
|
* 新增
|
||||||
|
*
|
||||||
|
* @param entity 对应的实体
|
||||||
|
*/
|
||||||
|
Long add(T entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增
|
||||||
|
*
|
||||||
|
* @param requestParams 请求参数
|
||||||
|
* @param entity 对应的实体
|
||||||
|
* @return ID
|
||||||
|
*/
|
||||||
|
Object add(JSONObject requestParams, T entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量添加
|
||||||
|
*
|
||||||
|
* @param requestParams 请求参数
|
||||||
|
* @param entitys 请求参数
|
||||||
|
* @return ID 集合
|
||||||
|
*/
|
||||||
|
Object addBatch(JSONObject requestParams, List<T> entitys);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除, 支持单个或者批量删除
|
||||||
|
*
|
||||||
|
* @param ids ID数组
|
||||||
|
*/
|
||||||
|
boolean delete(Long... ids);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 多个删除,带请求参数
|
||||||
|
*
|
||||||
|
* @param requestParams 请求参数
|
||||||
|
* @param ids ID数组
|
||||||
|
*/
|
||||||
|
boolean delete(JSONObject requestParams, Long... ids);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新
|
||||||
|
*
|
||||||
|
* @param entity 实体
|
||||||
|
*/
|
||||||
|
boolean update(T entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新
|
||||||
|
*
|
||||||
|
* @param requestParams 请求参数
|
||||||
|
* @param entity 实体
|
||||||
|
*/
|
||||||
|
boolean update(JSONObject requestParams, T entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询所有
|
||||||
|
*
|
||||||
|
* @param requestParams 请求参数
|
||||||
|
* @param queryWrapper 查询条件
|
||||||
|
* @return 列表信息
|
||||||
|
*/
|
||||||
|
Object list(JSONObject requestParams, QueryWrapper queryWrapper);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询
|
||||||
|
*
|
||||||
|
* @param requestParams 请求参数
|
||||||
|
* @param page 分页信息
|
||||||
|
* @param queryWrapper 查询条件
|
||||||
|
* @return 分页信息
|
||||||
|
*/
|
||||||
|
Object page(JSONObject requestParams, Page<T> page, QueryWrapper queryWrapper);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询信息
|
||||||
|
*
|
||||||
|
* @param id ID
|
||||||
|
*/
|
||||||
|
Object info(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询信息
|
||||||
|
*
|
||||||
|
* @param requestParams 请求参数
|
||||||
|
* @param id ID
|
||||||
|
*/
|
||||||
|
Object info(JSONObject requestParams, Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改之后
|
||||||
|
*
|
||||||
|
* @param requestParams 请求参数
|
||||||
|
* @param t 对应实体
|
||||||
|
*/
|
||||||
|
void modifyAfter(JSONObject requestParams, T t);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改之后
|
||||||
|
*
|
||||||
|
* @param requestParams 请求参数
|
||||||
|
* @param t 对应实体
|
||||||
|
* @param type 修改类型
|
||||||
|
*/
|
||||||
|
void modifyAfter(JSONObject requestParams, T t, ModifyEnum type);
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package com.cool.core.base;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.mybatisflex.core.BaseMapper;
|
||||||
|
import com.mybatisflex.core.paginate.Page;
|
||||||
|
import com.mybatisflex.core.query.QueryWrapper;
|
||||||
|
import com.mybatisflex.spring.service.impl.ServiceImpl;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基础service实现类
|
||||||
|
*
|
||||||
|
* @param <M> Mapper 类
|
||||||
|
* @param <T> 实体
|
||||||
|
*/
|
||||||
|
public class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseEntity<T>> extends
|
||||||
|
ServiceImpl<M, T>
|
||||||
|
implements BaseService<T> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long add(T entity) {
|
||||||
|
mapper.insert(entity);
|
||||||
|
return entity.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object add(JSONObject requestParams, T entity) {
|
||||||
|
this.add(entity);
|
||||||
|
this.modifyAfter(requestParams, entity, ModifyEnum.ADD);
|
||||||
|
return entity.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object addBatch(JSONObject requestParams, List<T> entitys) {
|
||||||
|
List<Long> ids = new ArrayList<>();
|
||||||
|
entitys.forEach(e -> ids.add(this.add(e)));
|
||||||
|
requestParams.set("ids", ids);
|
||||||
|
this.modifyAfter(requestParams, null, ModifyEnum.ADD);
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean delete(Long... ids) {
|
||||||
|
return mapper.deleteBatchByIds(Arrays.asList(ids)) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean delete(JSONObject requestParams, Long... ids) {
|
||||||
|
boolean flag = this.delete(ids);
|
||||||
|
if (flag) {
|
||||||
|
this.modifyAfter(requestParams, null, ModifyEnum.DELETE);
|
||||||
|
}
|
||||||
|
return flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean update(T entity) {
|
||||||
|
return mapper.update(entity) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean update(JSONObject requestParams, T entity) {
|
||||||
|
boolean flag = this.update(entity);
|
||||||
|
if (flag) {
|
||||||
|
this.modifyAfter(requestParams, entity, ModifyEnum.UPDATE);
|
||||||
|
}
|
||||||
|
return flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object list(JSONObject requestParams, QueryWrapper queryWrapper) {
|
||||||
|
return this.list(queryWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object page(JSONObject requestParams, Page<T> page, QueryWrapper queryWrapper) {
|
||||||
|
return this.page(page, queryWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object info(JSONObject requestParams, Long id) {
|
||||||
|
return info(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object info(Long id) {
|
||||||
|
return mapper.selectOneById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void modifyAfter(JSONObject requestParams, T t) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void modifyAfter(JSONObject requestParams, T t, ModifyEnum type) {
|
||||||
|
modifyAfter(requestParams, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.cool.core.base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改枚举
|
||||||
|
*/
|
||||||
|
public enum ModifyEnum {
|
||||||
|
// 新增
|
||||||
|
ADD,
|
||||||
|
// 修改
|
||||||
|
UPDATE,
|
||||||
|
// 删除
|
||||||
|
DELETE
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package com.cool.core.base.controller;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjUtil;
|
||||||
|
import com.cool.core.plugin.service.CoolPluginService;
|
||||||
|
import com.cool.core.util.ConvertUtil;
|
||||||
|
import com.cool.core.util.CoolPluginInvokers;
|
||||||
|
import java.io.File;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CommonController {
|
||||||
|
|
||||||
|
final private CoolPluginService coolPluginService;
|
||||||
|
|
||||||
|
@RequestMapping("/")
|
||||||
|
public String welcome() {
|
||||||
|
return "welcome";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/testPlugin/invokeMethod")
|
||||||
|
@ResponseBody
|
||||||
|
public String invokeMethod(@RequestParam String key, @RequestParam String methodName) {
|
||||||
|
Object result = null;
|
||||||
|
if (ObjUtil.isEmpty(methodName)) {
|
||||||
|
result = CoolPluginInvokers.invokePlugin(key);
|
||||||
|
} else {
|
||||||
|
result = CoolPluginInvokers.invokePlugin(key, methodName);
|
||||||
|
}
|
||||||
|
System.out.println(result);
|
||||||
|
return "invokeMethod Result: " + result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定目录加载插件
|
||||||
|
*/
|
||||||
|
@PostMapping("/testPlugin/reload")
|
||||||
|
@ResponseBody
|
||||||
|
public String reload() {
|
||||||
|
// 替换掉自己插件编译的路径,无需在页面上上传
|
||||||
|
File file = new File(
|
||||||
|
"/Users/mac/work/cool_new/cool-admin-java-plugin/target/my_cool_plugin.cool");
|
||||||
|
MultipartFile multipartFile = ConvertUtil.convertToMultipartFile(file);
|
||||||
|
coolPluginService.install(multipartFile, true);
|
||||||
|
return "reload Success";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package com.cool.core.base.service;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjUtil;
|
||||||
|
import cn.hutool.core.util.TypeUtil;
|
||||||
|
import com.cool.core.util.SpringContextUtils;
|
||||||
|
import com.mybatisflex.core.BaseMapper;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class MapperProviderService {
|
||||||
|
|
||||||
|
private Map<Class<?>, BaseMapper<?>> mapperMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化mapperMap,key 为entityClass,value 为 mapper
|
||||||
|
*/
|
||||||
|
private void init() {
|
||||||
|
// 获取所有BaseMapper类型的Bean
|
||||||
|
Map<String, BaseMapper> beansOfType = SpringContextUtils.getBeansOfType(BaseMapper.class);
|
||||||
|
mapperMap = new HashMap<>();
|
||||||
|
for (BaseMapper mapper : beansOfType.values()) {
|
||||||
|
// 通过反射获取泛型参数,即实体类
|
||||||
|
Class<?> entityClass = getGenericType(mapper);
|
||||||
|
if (entityClass != null) {
|
||||||
|
mapperMap.put(entityClass, mapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过entity类获取 对应的mapper接口
|
||||||
|
*/
|
||||||
|
public <T> BaseMapper<T> getMapperByEntityClass(Class<T> entityClass) {
|
||||||
|
if (ObjUtil.isEmpty(mapperMap)) {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
return (BaseMapper<T>) mapperMap.get(entityClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取mapper对应的entity对象
|
||||||
|
*/
|
||||||
|
private Class<?> getGenericType(BaseMapper<?> mapper) {
|
||||||
|
// 使用 获取泛型参数类型
|
||||||
|
Type[] types = mapper.getClass().getGenericInterfaces();
|
||||||
|
Type typeArgument = TypeUtil.getTypeArgument(types[0], 0);
|
||||||
|
return ObjUtil.isEmpty(typeArgument) ? null : (Class<?>) typeArgument;
|
||||||
|
}
|
||||||
|
}
|
||||||
+159
@@ -0,0 +1,159 @@
|
|||||||
|
package com.cool.core.cache;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjUtil;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.cool.core.util.ConvertUtil;
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.autoconfigure.cache.CacheType;
|
||||||
|
import org.springframework.cache.Cache;
|
||||||
|
import org.springframework.cache.CacheManager;
|
||||||
|
import org.springframework.cache.annotation.EnableCaching;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.redis.cache.RedisCacheWriter;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存工具类
|
||||||
|
*/
|
||||||
|
@EnableCaching
|
||||||
|
@Configuration
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CoolCache {
|
||||||
|
|
||||||
|
// 缓存类型
|
||||||
|
@Value("${spring.cache.type}")
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
// redis
|
||||||
|
public RedisCacheWriter redisCache;
|
||||||
|
|
||||||
|
private Cache cache;
|
||||||
|
|
||||||
|
@Value("${cool.cacheName}")
|
||||||
|
private String cacheName;
|
||||||
|
|
||||||
|
private final static String NULL_VALUE = "@_NULL_VALUE$@";
|
||||||
|
|
||||||
|
final private CacheManager cacheManager;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
private void init() {
|
||||||
|
cache = cacheManager.getCache(cacheName);
|
||||||
|
this.type = type.toLowerCase();
|
||||||
|
assert cache != null : "Cache not found: " + cacheName; // Ensure cache is not null
|
||||||
|
if (type.equalsIgnoreCase(CacheType.REDIS.name())) {
|
||||||
|
redisCache = (RedisCacheWriter) cache.getNativeCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除缓存
|
||||||
|
*
|
||||||
|
* @param keys 一个或多个key
|
||||||
|
*/
|
||||||
|
public void del(String... keys) {
|
||||||
|
if (type.equalsIgnoreCase(CacheType.CAFFEINE.name()) || type.equalsIgnoreCase(
|
||||||
|
CacheType.JCACHE.name())) {
|
||||||
|
Arrays.stream(keys).forEach(o -> cache.evict(o));
|
||||||
|
}
|
||||||
|
if (type.equalsIgnoreCase(CacheType.REDIS.name())) {
|
||||||
|
Arrays.stream(keys).forEach(key -> redisCache.remove(cacheName, key.getBytes()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 普通缓存获取
|
||||||
|
*
|
||||||
|
* @param key 键
|
||||||
|
*/
|
||||||
|
public Object get(String key) {
|
||||||
|
Object ifNullValue = getIfNullValue(key);
|
||||||
|
if (ObjUtil.equals(ifNullValue, NULL_VALUE)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ifNullValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getIfNullValue(String key) {
|
||||||
|
if (type.equalsIgnoreCase(CacheType.CAFFEINE.name()) || type.equalsIgnoreCase(
|
||||||
|
CacheType.JCACHE.name())) {
|
||||||
|
Cache.ValueWrapper valueWrapper = cache.get(key);
|
||||||
|
if (valueWrapper != null) {
|
||||||
|
return valueWrapper.get(); // 获取实际的缓存值
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type.equalsIgnoreCase(CacheType.REDIS.name())) {
|
||||||
|
byte[] bytes = redisCache.get(cacheName, key.getBytes());
|
||||||
|
if (bytes != null && bytes.length > 0) {
|
||||||
|
return ConvertUtil.toObject(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得对象
|
||||||
|
*
|
||||||
|
* @param key 键
|
||||||
|
* @param valueType 值类型
|
||||||
|
*/
|
||||||
|
public <T> T get(String key, Class<T> valueType) {
|
||||||
|
Object result = get(key);
|
||||||
|
if (result != null && JSONUtil.isTypeJSONObject(result.toString())) {
|
||||||
|
return JSONUtil.parseObj(result).toBean(valueType);
|
||||||
|
}
|
||||||
|
return result != null ? (T) result : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得缓存类型
|
||||||
|
*/
|
||||||
|
public String getMode() {
|
||||||
|
return this.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得原生缓存实例
|
||||||
|
*/
|
||||||
|
public Object getMetaCache() {
|
||||||
|
return this.cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 普通缓存放入
|
||||||
|
*
|
||||||
|
* @param key 键
|
||||||
|
* @param value 值
|
||||||
|
*/
|
||||||
|
public void set(String key, Object value) {
|
||||||
|
set(key, value, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 普通缓存放入并设置时间
|
||||||
|
*
|
||||||
|
* @param key 键
|
||||||
|
* @param value 值
|
||||||
|
* @param ttl 时间(秒) time要大于0 如果time小于等于0 将设置无限期
|
||||||
|
*/
|
||||||
|
public void set(String key, Object value, long ttl) {
|
||||||
|
if (ObjUtil.isNull(value)) {
|
||||||
|
value = NULL_VALUE;
|
||||||
|
}
|
||||||
|
if (type.equalsIgnoreCase(CacheType.CAFFEINE.name()) || type.equalsIgnoreCase(
|
||||||
|
CacheType.JCACHE.name())) {
|
||||||
|
// 放入缓存
|
||||||
|
cache.put(key, value);
|
||||||
|
}
|
||||||
|
if (type.equalsIgnoreCase(CacheType.REDIS.name())) {
|
||||||
|
redisCache.put(cacheName, key.getBytes(), ObjectUtil.serialize(value),
|
||||||
|
java.time.Duration.ofSeconds(ttl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package com.cool.core.code;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.file.FileWriter;
|
||||||
|
import cn.hutool.core.lang.Dict;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.extra.template.Template;
|
||||||
|
import cn.hutool.extra.template.TemplateConfig;
|
||||||
|
import cn.hutool.extra.template.TemplateEngine;
|
||||||
|
import cn.hutool.extra.template.TemplateUtil;
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代码生成器
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class CodeGenerator {
|
||||||
|
|
||||||
|
private TemplateEngine templateEngine;
|
||||||
|
|
||||||
|
private String baseSrcPath;
|
||||||
|
|
||||||
|
private String baseResPath;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
templateEngine = coolTemplateEngine();
|
||||||
|
baseSrcPath = System.getProperty("user.dir") + "/src/main/java/com/cool/modules/";
|
||||||
|
baseResPath = System.getProperty("user.dir") + "/src/main/resources/";
|
||||||
|
}
|
||||||
|
|
||||||
|
public TemplateEngine coolTemplateEngine() {
|
||||||
|
return TemplateUtil.createEngine(
|
||||||
|
new TemplateConfig("cool/code", TemplateConfig.ResourceMode.CLASSPATH));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String filePath(CodeModel codeModel, String type) {
|
||||||
|
if (type.equals("controller")) {
|
||||||
|
return StrUtil.isEmpty(codeModel.getSubModule())
|
||||||
|
? baseSrcPath + codeModel.getModule() + "/" + type + "/" + codeModel.getType()
|
||||||
|
.value()
|
||||||
|
: baseSrcPath + codeModel.getModule() + "/" + type + "/" + codeModel.getType()
|
||||||
|
.value() + "/"
|
||||||
|
+ codeModel.getSubModule();
|
||||||
|
}
|
||||||
|
if (type.equals("xmlMapper")) {
|
||||||
|
return StrUtil.isEmpty(codeModel.getSubModule()) ? baseResPath + "mapper/"
|
||||||
|
+ codeModel.getModule()
|
||||||
|
: baseResPath + "mapper/" + codeModel.getModule() + "/" + codeModel.getSubModule();
|
||||||
|
}
|
||||||
|
return StrUtil.isEmpty(codeModel.getSubModule()) ? baseSrcPath + codeModel.getModule() + "/"
|
||||||
|
+ type
|
||||||
|
: baseSrcPath + codeModel.getModule() + "/" + type + "/" + codeModel.getSubModule();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成Mapper
|
||||||
|
*
|
||||||
|
* @param codeModel 代码模型
|
||||||
|
*/
|
||||||
|
public void mapper(CodeModel codeModel) {
|
||||||
|
Template template = templateEngine.getTemplate("/mapper/interface.th");
|
||||||
|
String result = template.render(Dict.parse(codeModel));
|
||||||
|
FileWriter writer = new FileWriter(
|
||||||
|
filePath(codeModel, "mapper") + "/" + codeModel.getEntity() + "Mapper.java");
|
||||||
|
writer.write(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成Service
|
||||||
|
*
|
||||||
|
* @param codeModel 代码模型
|
||||||
|
*/
|
||||||
|
public void service(CodeModel codeModel) {
|
||||||
|
Template interfaceTemplate = templateEngine.getTemplate("/service/interface.th");
|
||||||
|
String interfaceResult = interfaceTemplate.render(Dict.parse(codeModel));
|
||||||
|
FileWriter interfaceWriter = new FileWriter(
|
||||||
|
filePath(codeModel, "service") + "/" + codeModel.getEntity() + "Service.java");
|
||||||
|
interfaceWriter.write(interfaceResult);
|
||||||
|
|
||||||
|
Template template = templateEngine.getTemplate("/service/impl.th");
|
||||||
|
String result = template.render(Dict.parse(codeModel));
|
||||||
|
FileWriter writer = new FileWriter(
|
||||||
|
filePath(codeModel, "service") + "/impl/" + codeModel.getEntity() + "ServiceImpl.java");
|
||||||
|
writer.write(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成Controller
|
||||||
|
*
|
||||||
|
* @param codeModel 代码模型
|
||||||
|
*/
|
||||||
|
public void controller(CodeModel codeModel) {
|
||||||
|
Template template = templateEngine.getTemplate("controller.th");
|
||||||
|
System.out.println(codeModel.getType().value());
|
||||||
|
Dict data = Dict.create().set("upperType", StrUtil.upperFirst(codeModel.getType().value()))
|
||||||
|
.set("url",
|
||||||
|
"/" + codeModel.getType() + "/" + StrUtil.toUnderlineCase(codeModel.getEntity())
|
||||||
|
.replace("_", "/"));
|
||||||
|
data.putAll(Dict.parse(codeModel));
|
||||||
|
data.set("type", codeModel.getType().value());
|
||||||
|
String result = template.render(data);
|
||||||
|
FileWriter writer = new FileWriter(filePath(codeModel, "controller") + "/"
|
||||||
|
+ StrUtil.upperFirst(codeModel.getType().value()) + codeModel.getEntity()
|
||||||
|
+ "Controller.java");
|
||||||
|
writer.write(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.cool.core.code;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代码模型
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CodeModel {
|
||||||
|
/**
|
||||||
|
* 类型 后台还是对外的接口 admin app
|
||||||
|
*/
|
||||||
|
private CodeTypeEnum type;
|
||||||
|
/**
|
||||||
|
* 名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
/**
|
||||||
|
* 模块
|
||||||
|
*/
|
||||||
|
private String module;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 子模块
|
||||||
|
*/
|
||||||
|
private String subModule;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实体类
|
||||||
|
*/
|
||||||
|
private String entity;
|
||||||
|
|
||||||
|
public void setEntity(Class entity) {
|
||||||
|
this.entity = entity.getSimpleName().replace("Entity", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.cool.core.code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代码类型
|
||||||
|
*/
|
||||||
|
public enum CodeTypeEnum {
|
||||||
|
ADMIN("admin", "后端接口"), APP("app", "对外接口");
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
private String des;
|
||||||
|
|
||||||
|
CodeTypeEnum(String value, String des) {
|
||||||
|
this.value = value;
|
||||||
|
this.des = des;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String value() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.cool.core.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cool的配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "cool")
|
||||||
|
public class CoolProperties {
|
||||||
|
// 是否自动导入数据
|
||||||
|
private Boolean initData = false;
|
||||||
|
// token配置
|
||||||
|
@NestedConfigurationProperty
|
||||||
|
private TokenProperties token;
|
||||||
|
// 文件配置
|
||||||
|
@NestedConfigurationProperty
|
||||||
|
private FileProperties file;
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.cool.core.config;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.springdoc.core.customizers.SpringDocCustomizers;
|
||||||
|
import org.springdoc.core.properties.SpringDocConfigProperties;
|
||||||
|
import org.springdoc.core.providers.SpringDocProviders;
|
||||||
|
import org.springdoc.core.service.AbstractRequestService;
|
||||||
|
import org.springdoc.core.service.GenericResponseService;
|
||||||
|
import org.springdoc.core.service.OpenAPIService;
|
||||||
|
import org.springdoc.core.service.OperationService;
|
||||||
|
import org.springdoc.webmvc.api.OpenApiResource;
|
||||||
|
import org.springframework.beans.factory.ObjectFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义 OpenApiResource
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class CustomOpenApiResource extends OpenApiResource {
|
||||||
|
|
||||||
|
public CustomOpenApiResource(ObjectFactory<OpenAPIService> openAPIBuilderObjectFactory, AbstractRequestService requestBuilder, GenericResponseService responseBuilder, OperationService operationParser, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders, SpringDocCustomizers springDocCustomizers) {
|
||||||
|
super("springdocDefault", openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, springDocConfigProperties, springDocProviders, springDocCustomizers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getServerUrl(HttpServletRequest request, String apiDocsUrl) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getOpenApiJson() throws JsonProcessingException {
|
||||||
|
return writeJsonValue(getOpenApi(Locale.getDefault()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.cool.core.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件模式
|
||||||
|
*/
|
||||||
|
public enum FileModeEnum {
|
||||||
|
LOCAL("local", "local", "本地"), CLOUD("cloud", "oss", "云存储"), OTHER("other", "other", "其他");
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
private String des;
|
||||||
|
|
||||||
|
FileModeEnum(String value, String type, String des) {
|
||||||
|
this.value = value;
|
||||||
|
this.type = type;
|
||||||
|
this.des = des;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String value() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String type() {
|
||||||
|
return this.type;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.cool.core.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "cool.file")
|
||||||
|
public class FileProperties {
|
||||||
|
// 上传模式
|
||||||
|
private FileModeEnum mode;
|
||||||
|
// 上传类型
|
||||||
|
private String type;
|
||||||
|
// 本地文件上传
|
||||||
|
@NestedConfigurationProperty
|
||||||
|
private LocalFileProperties local;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.cool.core.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "cool.file.local")
|
||||||
|
public class LocalFileProperties {
|
||||||
|
|
||||||
|
// 跟域名
|
||||||
|
private String baseUrl;
|
||||||
|
|
||||||
|
private String uploadPath = "assets/public/upload";
|
||||||
|
|
||||||
|
public String getAbsoluteUploadFolder() {
|
||||||
|
return System.getProperty("user.dir") + "/" + uploadPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.cool.core.config;
|
||||||
|
|
||||||
|
import com.mybatisflex.core.FlexGlobalConfig;
|
||||||
|
import com.mybatisflex.spring.boot.MyBatisFlexCustomizer;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class MyBatisFlexConfiguration implements MyBatisFlexCustomizer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void customize(FlexGlobalConfig globalConfig) {
|
||||||
|
// 我们可以在这里进行一些列的初始化配置
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.cool.core.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "cool.file.oss")
|
||||||
|
public class OssFileProperties {
|
||||||
|
// accessKeyId
|
||||||
|
private String accessKeyId;
|
||||||
|
// accessKeySecret
|
||||||
|
private String accessKeySecret;
|
||||||
|
// 文件空间
|
||||||
|
private String bucket;
|
||||||
|
// 地址
|
||||||
|
private String endpoint;
|
||||||
|
// 超时时间
|
||||||
|
private Long timeout;
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package com.cool.core.config;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class PluginJson {
|
||||||
|
/**
|
||||||
|
* 插件名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
/**
|
||||||
|
* 插件标识
|
||||||
|
*/
|
||||||
|
private String key;
|
||||||
|
/**
|
||||||
|
* 插件钩子,比如替换系统的上传组件,upload
|
||||||
|
*/
|
||||||
|
private String hook;
|
||||||
|
/**
|
||||||
|
* 版本号
|
||||||
|
*/
|
||||||
|
private String version;
|
||||||
|
/**
|
||||||
|
* 插件描述
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
/**
|
||||||
|
* 作者
|
||||||
|
*/
|
||||||
|
private String author;
|
||||||
|
/**
|
||||||
|
* 插件 logo,建议尺寸 256x256
|
||||||
|
*/
|
||||||
|
private String logo;
|
||||||
|
/**
|
||||||
|
* 插件介绍,会展示在插件的详情中
|
||||||
|
*/
|
||||||
|
private String readme;
|
||||||
|
/**
|
||||||
|
* 插件配置, 每个插件的配置各不相同
|
||||||
|
*/
|
||||||
|
private Map<String, Object> config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* jar包存放路径
|
||||||
|
*/
|
||||||
|
private String jarPath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同名hook id
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
private Long sameHookId;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.cool.core.config;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.ExternalDocumentation;
|
||||||
|
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||||
|
import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
|
||||||
|
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
|
||||||
|
import io.swagger.v3.oas.annotations.info.Contact;
|
||||||
|
import io.swagger.v3.oas.annotations.info.Info;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityScheme;
|
||||||
|
|
||||||
|
@OpenAPIDefinition(info = @Info(title = "COOL-ADMIN", version = "4.0", description = "一个很酷的后台权限管理系统开发框架", contact = @Contact(name = "闪酷科技")), security = @SecurityRequirement(name = "Authorization"), externalDocs = @ExternalDocumentation(description = "参考文档", url = "https://cool-js.com"))
|
||||||
|
@SecurityScheme(type = SecuritySchemeType.APIKEY, name = "Authorization", in = SecuritySchemeIn.HEADER)
|
||||||
|
public class SwaggerConfig {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.cool.core.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* token配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "cool.token")
|
||||||
|
public class TokenProperties {
|
||||||
|
// token 过期时间
|
||||||
|
private Long expire;
|
||||||
|
// refreshToken 过期时间
|
||||||
|
private Long refreshExpire;
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
package com.cool.core.config.cache;
|
||||||
|
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.cache.Cache;
|
||||||
|
import org.springframework.cache.annotation.EnableCaching;
|
||||||
|
import org.springframework.cache.caffeine.CaffeineCacheManager;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.event.ContextClosedEvent;
|
||||||
|
import org.springframework.context.event.EventListener;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Configuration
|
||||||
|
@EnableCaching
|
||||||
|
@ConditionalOnProperty(name = "spring.cache.type", havingValue = "CAFFEINE")
|
||||||
|
public class CaffeineConfig {
|
||||||
|
|
||||||
|
@Value("${spring.cache.file}")
|
||||||
|
private String cacheFile;
|
||||||
|
|
||||||
|
@Value("${cool.cacheName}")
|
||||||
|
private String cacheName;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Caffeine<Object, Object> caffeine() {
|
||||||
|
return Caffeine.newBuilder().maximumSize(10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public CaffeineCacheManager cacheManager(Caffeine<Object, Object> caffeine) {
|
||||||
|
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
|
||||||
|
cacheManager.setCaffeine(caffeine);
|
||||||
|
loadCache(cacheManager);
|
||||||
|
return cacheManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
File cacheDir = new File(cacheFile).getParentFile();
|
||||||
|
if (!cacheDir.exists()) {
|
||||||
|
if (cacheDir.mkdirs()) {
|
||||||
|
log.info("Created directory: " + cacheDir.getAbsolutePath());
|
||||||
|
} else {
|
||||||
|
log.error("Failed to create directory: " + cacheDir.getAbsolutePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadCache(CaffeineCacheManager cacheManager) {
|
||||||
|
if (cacheManager == null) {
|
||||||
|
log.error("CacheManager is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cacheFile == null || cacheFile.isEmpty()) {
|
||||||
|
log.error("Cache file path is null or empty");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File file = new File(cacheFile);
|
||||||
|
if (!file.exists()) {
|
||||||
|
log.warn("Cache file does not exist: " + cacheFile);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try (ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(file))) {
|
||||||
|
Map<Object, Object> cacheMap = (Map<Object, Object>) inputStream.readObject();
|
||||||
|
com.github.benmanes.caffeine.cache.Cache<Object, Object> caffeineCache = Caffeine.newBuilder()
|
||||||
|
.build();
|
||||||
|
caffeineCache.putAll(cacheMap);
|
||||||
|
cacheManager.registerCustomCache(cacheName, caffeineCache);
|
||||||
|
} catch (IOException | ClassNotFoundException e) {
|
||||||
|
log.error("loadCacheErr", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public CacheLoader cacheLoader(CaffeineCacheManager cacheManager) {
|
||||||
|
return new CacheLoader(cacheManager, cacheFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
class CacheLoader {
|
||||||
|
|
||||||
|
private final CaffeineCacheManager cacheManager;
|
||||||
|
private final String cacheFile;
|
||||||
|
|
||||||
|
public CacheLoader(CaffeineCacheManager cacheManager, String cacheFile) {
|
||||||
|
this.cacheManager = cacheManager;
|
||||||
|
this.cacheFile = cacheFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventListener(ContextClosedEvent.class)
|
||||||
|
@Scheduled(fixedRate = 10000)
|
||||||
|
public void persistCache() {
|
||||||
|
Cache cache = cacheManager.getCache(cacheName);
|
||||||
|
if (cache != null
|
||||||
|
&& cache.getNativeCache() instanceof com.github.benmanes.caffeine.cache.Cache) {
|
||||||
|
Map<Object, Object> cacheMap = ((com.github.benmanes.caffeine.cache.Cache<Object, Object>) cache
|
||||||
|
.getNativeCache()).asMap();
|
||||||
|
try (ObjectOutputStream outputStream = new ObjectOutputStream(
|
||||||
|
new FileOutputStream(cacheFile))) {
|
||||||
|
outputStream.writeObject(new HashMap<>(cacheMap));
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("persistCacheErr", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.cool.core.config.cache;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.cache.CacheManager;
|
||||||
|
import org.springframework.cache.annotation.EnableCaching;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.redis.cache.RedisCacheManager;
|
||||||
|
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableCaching
|
||||||
|
@ConditionalOnProperty(name = "spring.cache.type", havingValue = "redis")
|
||||||
|
public class RedisConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RedisTemplate<Object, Object> redisTemplate(
|
||||||
|
RedisConnectionFactory redisConnectionFactory) {
|
||||||
|
RedisTemplate<Object, Object> template = new RedisTemplate<>();
|
||||||
|
template.setConnectionFactory(redisConnectionFactory);
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
|
||||||
|
return RedisCacheManager.create(redisConnectionFactory);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,331 @@
|
|||||||
|
package com.cool.core.eps;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.lang.Dict;
|
||||||
|
import cn.hutool.core.thread.ThreadUtil;
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import cn.hutool.core.util.ClassUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.cool.core.config.CustomOpenApiResource;
|
||||||
|
import com.mybatisflex.annotation.Table;
|
||||||
|
import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.method.HandlerMethod;
|
||||||
|
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实体信息与路径
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CoolEps {
|
||||||
|
|
||||||
|
@Value("${server.port}")
|
||||||
|
private int serverPort;
|
||||||
|
|
||||||
|
private Dict entityInfo;
|
||||||
|
|
||||||
|
private JSONObject swaggerInfo;
|
||||||
|
|
||||||
|
@Value("${springdoc.api-docs.enabled}")
|
||||||
|
private boolean apiDocsEnabled;
|
||||||
|
|
||||||
|
public Dict admin;
|
||||||
|
|
||||||
|
public Dict app;
|
||||||
|
|
||||||
|
final private RequestMappingHandlerMapping requestMappingHandlerMapping;
|
||||||
|
|
||||||
|
final private CustomOpenApiResource customOpenApiResource;
|
||||||
|
|
||||||
|
@Async
|
||||||
|
public void init() {
|
||||||
|
if (!apiDocsEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
entityInfo = Dict.create();
|
||||||
|
swaggerInfo = swaggerInfo();
|
||||||
|
Runnable task = () -> {
|
||||||
|
entity();
|
||||||
|
urls();
|
||||||
|
log.info("初始化eps完成,服务启动成功,端口:{}", serverPort);
|
||||||
|
};
|
||||||
|
// ThreadUtil.safeSleep(3000);
|
||||||
|
ThreadUtil.execute(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建所有的url
|
||||||
|
*/
|
||||||
|
private void urls() {
|
||||||
|
Dict admin = Dict.create();
|
||||||
|
Dict app = Dict.create();
|
||||||
|
Map<RequestMappingInfo, HandlerMethod> map = requestMappingHandlerMapping.getHandlerMethods();
|
||||||
|
for (Map.Entry<RequestMappingInfo, HandlerMethod> methodEntry : map.entrySet()) {
|
||||||
|
RequestMappingInfo info = methodEntry.getKey();
|
||||||
|
HandlerMethod method = methodEntry.getValue();
|
||||||
|
String module = getModule(method);
|
||||||
|
if (StrUtil.isNotEmpty(module)) {
|
||||||
|
String entityName = getEntity(method.getBeanType());
|
||||||
|
String methodPath = getMethodUrl(method);
|
||||||
|
String prefix = Objects.requireNonNull(getUrl(info))
|
||||||
|
.replaceFirst("(?s)(.*)" + methodPath, "$1");
|
||||||
|
Dict result = Dict.create();
|
||||||
|
int type = 0;
|
||||||
|
if (prefix.startsWith("/admin")) {
|
||||||
|
result = admin;
|
||||||
|
}
|
||||||
|
if (prefix.startsWith("/app")) {
|
||||||
|
result = app;
|
||||||
|
type = 1;
|
||||||
|
}
|
||||||
|
if (result.get(module) == null) {
|
||||||
|
result.set(module, new ArrayList<Dict>());
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Dict> urls = result.getBean(module);
|
||||||
|
Dict item = CollUtil.findOne(urls, dict -> {
|
||||||
|
if (dict != null) {
|
||||||
|
return dict.getStr("module").equals(module)
|
||||||
|
&& dict.getStr("controller")
|
||||||
|
.equals(method.getBeanType().getSimpleName());
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (item != null) {
|
||||||
|
item.set("api", apis(prefix, methodPath, item.getBean("api")));
|
||||||
|
} else {
|
||||||
|
item = Dict.create();
|
||||||
|
item.set("controller", method.getBeanType().getSimpleName());
|
||||||
|
item.set("module", module);
|
||||||
|
item.set("name", entityName);
|
||||||
|
item.set("api", new ArrayList<Dict>());
|
||||||
|
item.set("prefix", prefix);
|
||||||
|
item.set("columns", entityInfo.get(entityName));
|
||||||
|
item.set("api", apis(prefix, methodPath, item.getBean("api")));
|
||||||
|
urls.add(item);
|
||||||
|
}
|
||||||
|
if (type == 0) {
|
||||||
|
admin.set(module, urls);
|
||||||
|
}
|
||||||
|
if (type == 1) {
|
||||||
|
app.set(module, urls);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.admin = admin;
|
||||||
|
this.app = app;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置所有的api
|
||||||
|
*
|
||||||
|
* @param prefix 路由前缀
|
||||||
|
* @param methodPath 方法路由
|
||||||
|
* @param list api列表
|
||||||
|
* @return api列表
|
||||||
|
*/
|
||||||
|
private List<Dict> apis(String prefix, String methodPath, List<Dict> list) {
|
||||||
|
Dict item = Dict.create();
|
||||||
|
item.set("method", "");
|
||||||
|
item.set("path", methodPath);
|
||||||
|
item.set("summary", "");
|
||||||
|
item.set("tag", "");
|
||||||
|
item.set("dts", new Object());
|
||||||
|
setSwaggerInfo(item, prefix + methodPath);
|
||||||
|
list.add(item);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置swagger相关信息
|
||||||
|
*
|
||||||
|
* @param item 信息载体
|
||||||
|
* @param url url地址
|
||||||
|
*/
|
||||||
|
private void setSwaggerInfo(Dict item, String url) {
|
||||||
|
JSONObject paths = swaggerInfo.getJSONObject("paths");
|
||||||
|
JSONObject urlInfo = paths.getJSONObject(url);
|
||||||
|
String method = urlInfo.keySet().iterator().next();
|
||||||
|
JSONObject methodInfo = urlInfo.getJSONObject(method);
|
||||||
|
item.set("dts", methodInfo);
|
||||||
|
item.set("method", method);
|
||||||
|
item.set("summary", methodInfo.getStr("summary"));
|
||||||
|
item.set("description", methodInfo.get("description"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得方法的url地址
|
||||||
|
*
|
||||||
|
* @param handlerMethod 方法
|
||||||
|
* @return 方法url地址
|
||||||
|
*/
|
||||||
|
private String getMethodUrl(HandlerMethod handlerMethod) {
|
||||||
|
String url = null;
|
||||||
|
Method method = handlerMethod.getMethod();
|
||||||
|
Annotation[] annotations = method.getDeclaredAnnotations();
|
||||||
|
|
||||||
|
for (Annotation annotation : annotations) {
|
||||||
|
Class<? extends Annotation> annotationType = annotation.annotationType();
|
||||||
|
if (annotationType.getName().contains("org.springframework.web.bind.annotation")) {
|
||||||
|
Map<String, Object> attributes = Arrays.stream(annotationType.getDeclaredMethods())
|
||||||
|
.collect(Collectors.toMap(Method::getName, m -> {
|
||||||
|
try {
|
||||||
|
return m.invoke(annotation);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalStateException("Failed to access annotation attribute",
|
||||||
|
e);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (attributes.containsKey("value")) {
|
||||||
|
url = ((String[]) attributes.get("value"))[0];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得url地址
|
||||||
|
*
|
||||||
|
* @param info 路由信息
|
||||||
|
* @return url地址
|
||||||
|
*/
|
||||||
|
private String getUrl(RequestMappingInfo info) {
|
||||||
|
if (info.getPathPatternsCondition() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Set<String> paths = info.getPathPatternsCondition().getPatternValues();
|
||||||
|
return paths.iterator().next();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得模块
|
||||||
|
*
|
||||||
|
* @param method 方法
|
||||||
|
* @return 模块
|
||||||
|
*/
|
||||||
|
private String getModule(HandlerMethod method) {
|
||||||
|
String beanName = method.getBeanType().getName();
|
||||||
|
String[] beanNames = beanName.split("[.]");
|
||||||
|
int index = ArrayUtil.indexOf(beanNames, "modules");
|
||||||
|
if (index > 0) {
|
||||||
|
return beanNames[index + 1];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得swagger的json信息
|
||||||
|
*/
|
||||||
|
private JSONObject swaggerInfo() {
|
||||||
|
try {
|
||||||
|
byte[] bytes = customOpenApiResource.getOpenApiJson();
|
||||||
|
return JSONUtil.parseObj(new String(bytes));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return new JSONObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得Controller上的实体类型
|
||||||
|
*
|
||||||
|
* @param controller Controller类
|
||||||
|
* @return 实体名称
|
||||||
|
*/
|
||||||
|
private String getEntity(Class<?> controller) {
|
||||||
|
try {
|
||||||
|
Type type = ((ParameterizedType) SpringUtil.getBean(controller).getClass()
|
||||||
|
.getGenericSuperclass())
|
||||||
|
.getActualTypeArguments()[1];
|
||||||
|
String[] names = type.getTypeName().split("[.]");
|
||||||
|
return names[names.length - 1];
|
||||||
|
} catch (Exception e) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void entity() {
|
||||||
|
// 扫描所有的实体类
|
||||||
|
Set<Class<?>> classes = ClassUtil.scanPackageByAnnotation("", Table.class);
|
||||||
|
classes.forEach(e -> {
|
||||||
|
// 获得属性
|
||||||
|
Field[] fields = ClassUtil.getDeclaredFields(e);
|
||||||
|
List<Dict> columns = columns(fields);
|
||||||
|
entityInfo.set(e.getSimpleName(), columns);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得所有的列
|
||||||
|
*
|
||||||
|
* @param fields 字段名
|
||||||
|
* @return 所有的列
|
||||||
|
*/
|
||||||
|
private List<Dict> columns(Field[] fields) {
|
||||||
|
List<Dict> dictList = new ArrayList<>();
|
||||||
|
for (Field field : fields) {
|
||||||
|
Dict dict = Dict.create();
|
||||||
|
ColumnDefine columnInfo = AnnotatedElementUtils.findMergedAnnotation(field,
|
||||||
|
ColumnDefine.class);
|
||||||
|
if (columnInfo == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
dict.set("comment", columnInfo.comment());
|
||||||
|
dict.set("length", columnInfo.length());
|
||||||
|
dict.set("propertyName", field.getName());
|
||||||
|
dict.set("type", matchType(field.getType().getName()));
|
||||||
|
dict.set("nullable", !columnInfo.notNull());
|
||||||
|
dictList.add(dict);
|
||||||
|
}
|
||||||
|
return dictList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* java类型转换成JavaScript对应的类型
|
||||||
|
*
|
||||||
|
* @param type 类型
|
||||||
|
* @return JavaScript类型
|
||||||
|
*/
|
||||||
|
private String matchType(String type) {
|
||||||
|
return switch (type) {
|
||||||
|
case "java.lang.Boolean" -> "boolean";
|
||||||
|
case "java.lang.Long", "java.lang.Integer", "java.lang.Short", "java.lang.Float",
|
||||||
|
"java.lang.Double" -> "number";
|
||||||
|
case "java.util.Date" -> "date";
|
||||||
|
default -> "string";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.cool.core.eps;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
|
import org.springframework.context.event.EventListener;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 事件监听
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@Profile({"local"})
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class EpsEvent {
|
||||||
|
|
||||||
|
final private CoolEps coolEps;
|
||||||
|
|
||||||
|
@EventListener
|
||||||
|
public void onApplicationEvent(ApplicationReadyEvent event) {
|
||||||
|
coolEps.init();
|
||||||
|
log.info("构建eps信息");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.cool.core.exception;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义异常处理
|
||||||
|
*/
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Data
|
||||||
|
public class CoolException extends RuntimeException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private String msg;
|
||||||
|
private int code = 500;
|
||||||
|
private Object data;
|
||||||
|
|
||||||
|
public CoolException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
this.msg = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CoolException(String msg, Throwable e) {
|
||||||
|
super(msg, e);
|
||||||
|
this.msg = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CoolException(String msg, int code) {
|
||||||
|
super(msg);
|
||||||
|
this.msg = msg;
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CoolException(String msg, int code, Throwable e) {
|
||||||
|
super(msg, e);
|
||||||
|
this.msg = msg;
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CoolException(Object data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package com.cool.core.exception;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjUtil;
|
||||||
|
import com.cool.core.request.R;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.dao.DuplicateKeyException;
|
||||||
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
|
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异常处理器
|
||||||
|
*/
|
||||||
|
@RestControllerAdvice
|
||||||
|
@Slf4j
|
||||||
|
public class CoolExceptionHandler {
|
||||||
|
|
||||||
|
@ExceptionHandler(CoolException.class)
|
||||||
|
public R handleRRException(CoolException e) {
|
||||||
|
R r = new R();
|
||||||
|
if (ObjUtil.isNotEmpty(e.getData())) {
|
||||||
|
r.put("data", e.getData());
|
||||||
|
} else {
|
||||||
|
r.put("code", e.getCode());
|
||||||
|
r.put("message", e.getMessage());
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(DuplicateKeyException.class)
|
||||||
|
public R handleDuplicateKeyException(DuplicateKeyException e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
return R.error("已存在该记录或值不能重复");
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(BadCredentialsException.class)
|
||||||
|
public R handleBadCredentialsException(BadCredentialsException e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
return R.error("账户密码不正确");
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
|
||||||
|
public R handleHttpRequestMethodNotSupportedException(
|
||||||
|
HttpRequestMethodNotSupportedException e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
return R.error("不支持该请求方式,请区分POST、GET等请求方式是否正确");
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(IllegalArgumentException.class)
|
||||||
|
public R handleIllegalArgumentException(IllegalArgumentException e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
return R.error(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(Exception.class)
|
||||||
|
public R handleException(Exception e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
return R.error();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package com.cool.core.exception;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验处理
|
||||||
|
*/
|
||||||
|
public class CoolPreconditions {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 条件如果为真 就抛异常 如 CoolPreconditions.check(StrUtil.isEmptyIfStr(name), 500,
|
||||||
|
* "名称不能为空"); name 字段如果为 null或空字符串,就抛异常
|
||||||
|
*/
|
||||||
|
public static void check(boolean flag, int code, String message, Object... arguments) {
|
||||||
|
if (flag) {
|
||||||
|
throw new CoolException(formatMessage(message, arguments), code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void check(boolean flag, String message, Object... arguments) {
|
||||||
|
if (flag) {
|
||||||
|
throw new CoolException(formatMessage(message, arguments));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void alwaysThrow(String message, Object... arguments) {
|
||||||
|
throw new CoolException(formatMessage(message, arguments));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回data
|
||||||
|
*/
|
||||||
|
public static void returnData(boolean flag, Object data) {
|
||||||
|
if (flag) {
|
||||||
|
throw new CoolException(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void returnData(Object data) {
|
||||||
|
returnData(true, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对象如果为空 就抛异常
|
||||||
|
*/
|
||||||
|
public static void checkEmpty(Object object, String message, Object... arguments) {
|
||||||
|
check(ObjectUtil.isEmpty(object), formatMessage(message, arguments));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String formatMessage(String messagePattern, Object... arguments) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
int argumentIndex = 0;
|
||||||
|
int placeholderIndex = messagePattern.indexOf("{}");
|
||||||
|
while (placeholderIndex != -1) {
|
||||||
|
sb.append(messagePattern, 0, placeholderIndex);
|
||||||
|
if (argumentIndex < arguments.length) {
|
||||||
|
sb.append(arguments[argumentIndex++]);
|
||||||
|
} else {
|
||||||
|
sb.append("{}"); // 如果参数不足,保留原样
|
||||||
|
}
|
||||||
|
messagePattern = messagePattern.substring(placeholderIndex + 2);
|
||||||
|
placeholderIndex = messagePattern.indexOf("{}");
|
||||||
|
}
|
||||||
|
sb.append(messagePattern); // 添加剩余部分
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
public static class ReturnData {
|
||||||
|
private Integer type;
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
public ReturnData(Integer type, String message, Object... arguments) {
|
||||||
|
this.type = type;
|
||||||
|
this.message = formatMessage(message, arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package com.cool.core.file;
|
||||||
|
|
||||||
|
import static com.cool.core.plugin.consts.PluginConsts.uploadHook;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Dict;
|
||||||
|
import cn.hutool.core.util.ObjUtil;
|
||||||
|
import com.cool.core.exception.CoolPreconditions;
|
||||||
|
import com.cool.core.file.strategy.FileUploadStrategy;
|
||||||
|
import com.cool.core.plugin.service.CoolPluginService;
|
||||||
|
import com.cool.modules.plugin.entity.PluginInfoEntity;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import java.io.IOException;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class FileUploadStrategyFactory {
|
||||||
|
|
||||||
|
final private ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
final private CoolPluginService coolPluginService;
|
||||||
|
|
||||||
|
public FileUploadStrategy getStrategy() {
|
||||||
|
PluginInfoEntity pluginInfoEntity = coolPluginService.getPluginInfoEntityByHook(uploadHook);
|
||||||
|
return getStrategy(pluginInfoEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private FileUploadStrategy getStrategy(PluginInfoEntity pluginInfoEntity) {
|
||||||
|
if (ObjUtil.isEmpty(pluginInfoEntity)) {
|
||||||
|
return applicationContext.getBean("localFileUploadStrategy", FileUploadStrategy.class);
|
||||||
|
}
|
||||||
|
return applicationContext.getBean("ossFileUploadStrategy", FileUploadStrategy.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object upload(MultipartFile[] files, HttpServletRequest request) {
|
||||||
|
PluginInfoEntity pluginInfoEntity = coolPluginService.getPluginInfoEntityByHook(uploadHook);
|
||||||
|
try {
|
||||||
|
return getStrategy(pluginInfoEntity).upload(files, request, pluginInfoEntity);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("上传文件失败", e);
|
||||||
|
CoolPreconditions.alwaysThrow("上传文件失败 {}", e.getMessage());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getMode() {
|
||||||
|
FileUploadStrategy strategy = getStrategy();
|
||||||
|
UpLoadModeType upLoadModeType = strategy.getMode();
|
||||||
|
return Dict.create().set("mode", upLoadModeType.getMode().value())
|
||||||
|
.set("type", upLoadModeType.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.cool.core.file;
|
||||||
|
|
||||||
|
import com.cool.core.config.FileModeEnum;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传模式类型
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class UpLoadModeType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模式
|
||||||
|
*/
|
||||||
|
private FileModeEnum mode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型
|
||||||
|
*/
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
public UpLoadModeType(FileModeEnum mode) {
|
||||||
|
this.mode = mode;
|
||||||
|
this.type = mode.type();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.cool.core.file.strategy;
|
||||||
|
|
||||||
|
import com.cool.core.file.UpLoadModeType;
|
||||||
|
import com.cool.modules.plugin.entity.PluginInfoEntity;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
public interface FileUploadStrategy {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传
|
||||||
|
*/
|
||||||
|
Object upload(MultipartFile[] files, HttpServletRequest request, PluginInfoEntity pluginInfoEntity)
|
||||||
|
throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传模式
|
||||||
|
*
|
||||||
|
* @return 上传模式
|
||||||
|
*/
|
||||||
|
UpLoadModeType getMode();
|
||||||
|
|
||||||
|
default boolean isAbsolutePath(String pathStr) {
|
||||||
|
Path path = Paths.get(pathStr);
|
||||||
|
return path.isAbsolute();
|
||||||
|
}
|
||||||
|
|
||||||
|
default String getExtensionName(String fileName) {
|
||||||
|
if (fileName.contains(".")) {
|
||||||
|
String[] names = fileName.split("[.]");
|
||||||
|
return "." + names[names.length - 1];
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package com.cool.core.file.strategy;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.DatePattern;
|
||||||
|
import cn.hutool.core.date.DateUtil;
|
||||||
|
import cn.hutool.core.io.FileUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.cool.core.config.FileModeEnum;
|
||||||
|
import com.cool.core.config.LocalFileProperties;
|
||||||
|
import com.cool.core.exception.CoolException;
|
||||||
|
import com.cool.core.exception.CoolPreconditions;
|
||||||
|
import com.cool.core.file.UpLoadModeType;
|
||||||
|
import com.cool.modules.plugin.entity.PluginInfoEntity;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
@Component("localFileUploadStrategy")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class LocalFileUploadStrategy implements FileUploadStrategy {
|
||||||
|
|
||||||
|
final private LocalFileProperties localFileProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传文件
|
||||||
|
*
|
||||||
|
* @param files 上传的文件
|
||||||
|
* @return 文件路径
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object upload(MultipartFile[] files, HttpServletRequest request,
|
||||||
|
PluginInfoEntity pluginInfoEntity) {
|
||||||
|
CoolPreconditions.check(StrUtil.isEmpty(localFileProperties.getBaseUrl()),
|
||||||
|
"filePath 或 baseUrl 未配置");
|
||||||
|
try {
|
||||||
|
List<String> fileUrls = new ArrayList<>();
|
||||||
|
String baseUrl = localFileProperties.getBaseUrl();
|
||||||
|
// 绝对路径
|
||||||
|
String filePath = System.getProperty("user.dir");
|
||||||
|
String date = DateUtil.format(new Date(),
|
||||||
|
DatePattern.PURE_DATE_PATTERN);
|
||||||
|
String relativePath =
|
||||||
|
"/" + localFileProperties.getUploadPath() + "/" + date;
|
||||||
|
|
||||||
|
FileUtil.mkdir(filePath + relativePath);
|
||||||
|
for (MultipartFile file : files) {
|
||||||
|
// 保存文件
|
||||||
|
String fileName = StrUtil.uuid().replaceAll("-", "") + getExtensionName(
|
||||||
|
Objects.requireNonNull(file.getOriginalFilename()));
|
||||||
|
file.transferTo(new File(filePath + "/" + relativePath
|
||||||
|
+ "/" + fileName));
|
||||||
|
fileUrls.add(baseUrl + "/" + date + "/" + fileName);
|
||||||
|
}
|
||||||
|
if (fileUrls.size() == 1) {
|
||||||
|
return fileUrls.get(0);
|
||||||
|
}
|
||||||
|
return fileUrls;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new CoolException("文件上传失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传模式
|
||||||
|
*
|
||||||
|
* @return 上传模式
|
||||||
|
*/
|
||||||
|
public UpLoadModeType getMode() {
|
||||||
|
return new UpLoadModeType(FileModeEnum.LOCAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.cool.core.file.strategy;
|
||||||
|
|
||||||
|
import com.cool.core.config.FileModeEnum;
|
||||||
|
import com.cool.core.file.UpLoadModeType;
|
||||||
|
import com.cool.core.util.CoolPluginInvokers;
|
||||||
|
import com.cool.modules.plugin.entity.PluginInfoEntity;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Component("ossFileUploadStrategy")
|
||||||
|
public class OssFileUploadStrategy implements FileUploadStrategy {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object upload(MultipartFile[] files, HttpServletRequest request, PluginInfoEntity pluginInfoEntity)
|
||||||
|
throws IOException {
|
||||||
|
return CoolPluginInvokers.invokePlugin(pluginInfoEntity.getKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UpLoadModeType getMode() {
|
||||||
|
return new UpLoadModeType(FileModeEnum.CLOUD);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.cool.core.init;
|
||||||
|
|
||||||
|
import com.cool.core.plugin.service.CoolPluginService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.boot.ApplicationArguments;
|
||||||
|
import org.springframework.boot.ApplicationRunner;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 历史安装过的插件执行初始化
|
||||||
|
**/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CoolPluginInit implements ApplicationRunner {
|
||||||
|
|
||||||
|
final private CoolPluginService coolPluginService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(ApplicationArguments args) {
|
||||||
|
coolPluginService.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,218 @@
|
|||||||
|
package com.cool.core.init;
|
||||||
|
|
||||||
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
|
import cn.hutool.core.io.IoUtil;
|
||||||
|
import cn.hutool.core.util.ObjUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.json.JSONArray;
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.cool.core.base.service.MapperProviderService;
|
||||||
|
import com.cool.core.util.EntityUtils;
|
||||||
|
import com.cool.modules.base.entity.sys.BaseSysConfEntity;
|
||||||
|
import com.cool.modules.base.entity.sys.BaseSysMenuEntity;
|
||||||
|
import com.cool.modules.base.service.sys.BaseSysConfService;
|
||||||
|
import com.cool.modules.base.service.sys.BaseSysMenuService;
|
||||||
|
import com.mybatisflex.core.BaseMapper;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.ApplicationArguments;
|
||||||
|
import org.springframework.boot.ApplicationRunner;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库初始数据初始化 在 classpath:cool/data/db 目录下创建.json文件 并定义表数据, 由该类统一执行初始化
|
||||||
|
**/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class DBFromJsonInit implements ApplicationRunner {
|
||||||
|
|
||||||
|
final private BaseSysConfService baseSysConfService;
|
||||||
|
|
||||||
|
final private BaseSysMenuService baseSysMenuService;
|
||||||
|
|
||||||
|
final private MapperProviderService mapperProviderService;
|
||||||
|
|
||||||
|
final private ApplicationEventPublisher eventPublisher;
|
||||||
|
|
||||||
|
@Value("${cool.initData}")
|
||||||
|
private boolean initData;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(ApplicationArguments args) {
|
||||||
|
if (!initData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 初始化自定义的数据
|
||||||
|
extractedDb();
|
||||||
|
// 初始化菜单数据
|
||||||
|
extractedMenu();
|
||||||
|
// 发送数据库初始化完成事件
|
||||||
|
eventPublisher.publishEvent(new DbInitCompleteEvent(this));
|
||||||
|
log.info("数据初始化完成!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public static class DbInitCompleteEvent {
|
||||||
|
private final Object source;
|
||||||
|
|
||||||
|
public DbInitCompleteEvent(Object source) {
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析插入业务数据
|
||||||
|
*/
|
||||||
|
private void extractedDb() {
|
||||||
|
try {
|
||||||
|
// 加载 JSON 文件
|
||||||
|
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
||||||
|
Resource[] resources = resolver.getResources("classpath:cool/data/db/*.json");
|
||||||
|
// 遍历所有.json文件
|
||||||
|
analysisResources(resources);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to initialize data", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void analysisResources(Resource[] resources)
|
||||||
|
throws IOException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
String prefix = "db_";
|
||||||
|
for (Resource resource : resources) {
|
||||||
|
File resourceFile = new File(resource.getURL().getFile());
|
||||||
|
String fileName = prefix + resourceFile.getName();
|
||||||
|
String value = baseSysConfService.getValue(fileName);
|
||||||
|
if (StrUtil.isNotEmpty(value)) {
|
||||||
|
log.info("{} 业务数据已初始化过...", fileName);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String jsonStr = IoUtil.read(resource.getInputStream(), StandardCharsets.UTF_8);
|
||||||
|
JSONObject jsonObject = JSONUtil.parseObj(jsonStr);
|
||||||
|
// 遍历 JSON 文件中的数据
|
||||||
|
analysisJson(jsonObject);
|
||||||
|
|
||||||
|
BaseSysConfEntity baseSysUserEntity = new BaseSysConfEntity();
|
||||||
|
baseSysUserEntity.setCKey(fileName);
|
||||||
|
baseSysUserEntity.setCValue("success");
|
||||||
|
// 当前文件已加载
|
||||||
|
baseSysConfService.add(baseSysUserEntity);
|
||||||
|
log.info("{} 业务数据初始化成功...", fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void analysisJson(JSONObject jsonObject)
|
||||||
|
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
Map<String, Class<?>> tableMap = EntityUtils.findTableMap();
|
||||||
|
for (String tableName : jsonObject.keySet()) {
|
||||||
|
JSONArray records = jsonObject.getJSONArray(tableName);
|
||||||
|
// 根据表名生成实体类名和 Mapper 接口名
|
||||||
|
Class<?> entityClass = tableMap.get(tableName);
|
||||||
|
BaseMapper<?> baseMapper = mapperProviderService.getMapperByEntityClass(entityClass);
|
||||||
|
// 插入
|
||||||
|
insertList(baseMapper, entityClass, records);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插入列表数据
|
||||||
|
*/
|
||||||
|
private void insertList(BaseMapper<?> baseMapper, Class<?> entityClass,
|
||||||
|
JSONArray records)
|
||||||
|
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
// 插入数据
|
||||||
|
List list = new ArrayList<>();
|
||||||
|
for (int i = 0; i < records.size(); i++) {
|
||||||
|
JSONObject record = records.getJSONObject(i);
|
||||||
|
Object entity = JSONUtil.toBean(record, entityClass);
|
||||||
|
Method getIdMethod = entityClass.getMethod("getId");
|
||||||
|
Object id = getIdMethod.invoke(entity);
|
||||||
|
if (ObjUtil.isNotEmpty(id) && ObjUtil.isNotEmpty(
|
||||||
|
baseMapper.selectOneById((Long) id))) {
|
||||||
|
// 数据库已经有值了
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
list.add(entity);
|
||||||
|
}
|
||||||
|
baseMapper.insertBatch(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析插入菜单数据
|
||||||
|
*/
|
||||||
|
public void extractedMenu() {
|
||||||
|
try {
|
||||||
|
String prefix = "menu_";
|
||||||
|
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
||||||
|
Resource[] resources = resolver.getResources("classpath:cool/data/menu/*.json");
|
||||||
|
// 遍历所有.json文件
|
||||||
|
for (Resource resource : resources) {
|
||||||
|
File resourceFile = new File(resource.getURL().getFile());
|
||||||
|
String fileName = prefix + resourceFile.getName();
|
||||||
|
String value = baseSysConfService.getValue(fileName);
|
||||||
|
if (StrUtil.isNotEmpty(value)) {
|
||||||
|
log.info("{} 菜单数据已初始化过...", fileName);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
analysisResources(resource, fileName);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to initialize data", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void analysisResources(Resource resource, String fileName) throws IOException {
|
||||||
|
String jsonStr = IoUtil.read(resource.getInputStream(), StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
// 使用 解析 JSON 字符串
|
||||||
|
JSONArray jsonArray = JSONUtil.parseArray(jsonStr);
|
||||||
|
|
||||||
|
// 遍历 JSON 数组
|
||||||
|
for (Object obj : jsonArray) {
|
||||||
|
JSONObject jsonObj = (JSONObject) obj;
|
||||||
|
// 将 JSON 对象转换为 Menu 对象
|
||||||
|
parseMenu(jsonObj, null);
|
||||||
|
}
|
||||||
|
BaseSysConfEntity baseSysUserEntity = new BaseSysConfEntity();
|
||||||
|
baseSysUserEntity.setCKey(fileName);
|
||||||
|
baseSysUserEntity.setCValue("success");
|
||||||
|
// 当前文件已加载
|
||||||
|
baseSysConfService.add(baseSysUserEntity);
|
||||||
|
log.info("{} 菜单数据初始化成功...", fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归解析 JSON 对象为 Menu 对象
|
||||||
|
private void parseMenu(JSONObject jsonObj, BaseSysMenuEntity parentMenuEntity) {
|
||||||
|
BaseSysMenuEntity menuEntity = BeanUtil.copyProperties(jsonObj, BaseSysMenuEntity.class);
|
||||||
|
if (ObjUtil.isNotEmpty(parentMenuEntity)) {
|
||||||
|
menuEntity.setParentName(parentMenuEntity.getName());
|
||||||
|
menuEntity.setParentId(parentMenuEntity.getId());
|
||||||
|
}
|
||||||
|
baseSysMenuService.add(menuEntity);
|
||||||
|
// 递归处理子菜单
|
||||||
|
JSONArray childMenus = jsonObj.getJSONArray("childMenus");
|
||||||
|
if (childMenus != null) {
|
||||||
|
for (Object obj : childMenus) {
|
||||||
|
JSONObject childObj = (JSONObject) obj;
|
||||||
|
parseMenu(childObj, menuEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package com.cool.core.plugin.config;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * 自定义类加载器
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class DynamicJarClassLoader extends URLClassLoader {
|
||||||
|
|
||||||
|
private final Map<String, Class<?>> loadedClasses = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public DynamicJarClassLoader(URL[] urls, ClassLoader parent) {
|
||||||
|
super(urls, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||||
|
// 从已加载的类集合中获取指定名称的类
|
||||||
|
Class<?> clazz = loadedClasses.get(name);
|
||||||
|
if (clazz != null) {
|
||||||
|
return clazz;
|
||||||
|
}
|
||||||
|
clazz = super.findClass(name);
|
||||||
|
// 将加载的类添加到已加载的类集合中
|
||||||
|
loadedClasses.put(name, clazz);
|
||||||
|
return clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unload() {
|
||||||
|
try {
|
||||||
|
for (Map.Entry<String, Class<?>> entry : loadedClasses.entrySet()) {
|
||||||
|
// 从已加载的类集合中移除该类
|
||||||
|
String className = entry.getKey();
|
||||||
|
loadedClasses.remove(className);
|
||||||
|
try {
|
||||||
|
// 调用该类的destory方法,回收资源
|
||||||
|
Class<?> clazz = entry.getValue();
|
||||||
|
Method destory = clazz.getDeclaredMethod("destory");
|
||||||
|
destory.invoke(clazz);
|
||||||
|
} catch (NoClassDefFoundError | Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 从其父类加载器的加载器层次结构中移除该类加载器
|
||||||
|
close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("unload error", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.cool.core.plugin.consts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 常量工具
|
||||||
|
*/
|
||||||
|
public interface PluginConsts {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传文件hook
|
||||||
|
*/
|
||||||
|
String uploadHook = "upload";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件调用入口方法
|
||||||
|
*/
|
||||||
|
String invokePluginMethodName = "invokePlugin";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置插件信息
|
||||||
|
*/
|
||||||
|
String setPluginJson = "setPluginJson";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置 ApplicationContext,使得插件能够调用主应用spring容器中的bean
|
||||||
|
*/
|
||||||
|
String setApplicationContext = "setApplicationContext";
|
||||||
|
}
|
||||||
@@ -0,0 +1,273 @@
|
|||||||
|
package com.cool.core.plugin.service;
|
||||||
|
|
||||||
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
|
import cn.hutool.core.bean.copier.CopyOptions;
|
||||||
|
import cn.hutool.core.codec.Base64Encoder;
|
||||||
|
import cn.hutool.core.io.FileUtil;
|
||||||
|
import cn.hutool.core.io.IoUtil;
|
||||||
|
import cn.hutool.core.util.ObjUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.cool.core.config.PluginJson;
|
||||||
|
import com.cool.core.exception.CoolException;
|
||||||
|
import com.cool.core.exception.CoolPreconditions;
|
||||||
|
import com.cool.core.plugin.config.DynamicJarClassLoader;
|
||||||
|
import com.cool.core.util.CoolPluginInvokers;
|
||||||
|
import com.cool.core.util.MapExtUtil;
|
||||||
|
import com.cool.core.util.PathUtils;
|
||||||
|
import com.cool.modules.plugin.entity.PluginInfoEntity;
|
||||||
|
import com.cool.modules.plugin.service.PluginInfoService;
|
||||||
|
import com.mybatisflex.core.query.QueryWrapper;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件服务类
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CoolPluginService {
|
||||||
|
|
||||||
|
final private DynamicJarLoaderService dynamicJarLoaderService;
|
||||||
|
|
||||||
|
final private PluginInfoService pluginInfoService;
|
||||||
|
|
||||||
|
@Value("${cool.pluginPath}")
|
||||||
|
private String pluginPath;
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
List<PluginInfoEntity> list = pluginInfoService
|
||||||
|
.list(QueryWrapper
|
||||||
|
.create().select(PluginInfoEntity::getId, PluginInfoEntity::getPluginJson,
|
||||||
|
PluginInfoEntity::getKey, PluginInfoEntity::getName)
|
||||||
|
.eq(PluginInfoEntity::getStatus, 1));
|
||||||
|
if (ObjUtil.isEmpty(list)) {
|
||||||
|
log.info("没有可初始化的插件");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
list.forEach(this::initInstall);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统启动初始化安装插件
|
||||||
|
*/
|
||||||
|
private void initInstall(PluginInfoEntity entity) {
|
||||||
|
PluginJson pluginJson = entity.getPluginJson();
|
||||||
|
File file = new File(pluginJson.getJarPath());
|
||||||
|
// 检查路径是否存在
|
||||||
|
if (!file.exists()) {
|
||||||
|
PluginInfoEntity pluginInfoEntity = pluginInfoService.getById(entity.getId());
|
||||||
|
FileUtil.writeBytes(pluginInfoEntity.getJarFile(), file);
|
||||||
|
}
|
||||||
|
file = new File(pluginJson.getJarPath());
|
||||||
|
if (file.exists()) {
|
||||||
|
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
try {
|
||||||
|
dynamicJarLoaderService.install(pluginJson.getJarPath(), true);
|
||||||
|
// 设置配置
|
||||||
|
CoolPluginInvokers.setPluginJson(entity.getKey(), entity);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("初始化{}插件失败", entity.getName(), e);
|
||||||
|
} finally {
|
||||||
|
Thread.currentThread().setContextClassLoader(originalClassLoader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安装jar
|
||||||
|
*/
|
||||||
|
public void install(MultipartFile file, boolean force) {
|
||||||
|
String fileName;
|
||||||
|
File jarFile = null;
|
||||||
|
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
try {
|
||||||
|
String pathStr = pluginPath;
|
||||||
|
if (!PathUtils.isAbsolutePath(pluginPath)) {
|
||||||
|
// 相对路径
|
||||||
|
pathStr = System.getProperty("user.dir") + "/" + pluginPath;
|
||||||
|
}
|
||||||
|
// 将路径字符串转换为 Path 对象
|
||||||
|
Path path = Paths.get(pathStr);
|
||||||
|
// 检查路径是否存在
|
||||||
|
if (!Files.exists(path)) {
|
||||||
|
// 如果路径不存在,则创建目录(包括父目录)
|
||||||
|
Files.createDirectories(path);
|
||||||
|
}
|
||||||
|
fileName =
|
||||||
|
path + "/" + System.currentTimeMillis() + "_" + file.getOriginalFilename() + ".jar";
|
||||||
|
jarFile = new File(fileName);
|
||||||
|
file.transferTo(jarFile);
|
||||||
|
// 加载jar
|
||||||
|
PluginJson pluginJson = dynamicJarLoaderService.install(fileName, force);
|
||||||
|
// 保存插件信息入库
|
||||||
|
savePluginInfo(pluginJson, fileName, jarFile, force);
|
||||||
|
// 把 ApplicationContext 对象传递打插件类中,使其在插件中也能正常使用spring bean对象
|
||||||
|
CoolPluginInvokers.setApplicationContext(pluginJson.getKey());
|
||||||
|
} catch (CoolException e) {
|
||||||
|
FileUtil.del(jarFile);
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("插件安装失败", e);
|
||||||
|
CoolPreconditions.alwaysThrow("插件安装失败", e);
|
||||||
|
} finally {
|
||||||
|
Thread.currentThread().setContextClassLoader(originalClassLoader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 卸载
|
||||||
|
*/
|
||||||
|
public void uninstall(Long id) {
|
||||||
|
PluginInfoEntity pluginInfoEntity = pluginInfoService.getPluginInfoEntityByIdNoJarFile(id);
|
||||||
|
CoolPreconditions.checkEmpty(pluginInfoEntity, "插件不存在");
|
||||||
|
if (dynamicJarLoaderService.uninstall(pluginInfoEntity.getKey())) {
|
||||||
|
boolean flag = pluginInfoEntity.removeById();
|
||||||
|
if (flag) {
|
||||||
|
FileUtil.del(pluginInfoEntity.getPluginJson().getJarPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存插件信息
|
||||||
|
*/
|
||||||
|
private void savePluginInfo(PluginJson pluginJson, String fileName, File jarFile,
|
||||||
|
boolean force) {
|
||||||
|
CoolPreconditions.checkEmpty(pluginJson, "插件安装失败");
|
||||||
|
pluginJson.setJarPath(fileName);
|
||||||
|
PluginInfoEntity pluginInfo = new PluginInfoEntity();
|
||||||
|
BeanUtil.copyProperties(pluginJson, pluginInfo);
|
||||||
|
if (ObjUtil.isNotEmpty(pluginJson.getLogo())) {
|
||||||
|
DynamicJarClassLoader dynamicJarClassLoader = dynamicJarLoaderService
|
||||||
|
.getDynamicJarClassLoader(pluginJson.getKey());
|
||||||
|
InputStream inputStream = dynamicJarClassLoader.getResourceAsStream(
|
||||||
|
pluginJson.getLogo());
|
||||||
|
if (ObjUtil.isNotEmpty(inputStream)) {
|
||||||
|
pluginInfo.setLogo(Base64Encoder.encode(IoUtil.readBytes(inputStream)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ObjUtil.isNotEmpty(pluginJson.getReadme())) {
|
||||||
|
DynamicJarClassLoader dynamicJarClassLoader = dynamicJarLoaderService
|
||||||
|
.getDynamicJarClassLoader(pluginJson.getKey());
|
||||||
|
InputStream inputStream = dynamicJarClassLoader.getResourceAsStream(
|
||||||
|
pluginJson.getReadme());
|
||||||
|
if (ObjUtil.isNotEmpty(inputStream)) {
|
||||||
|
pluginInfo.setReadme(StrUtil.str(IoUtil.readBytes(inputStream), "UTF-8"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pluginInfo.setKey(pluginJson.getKey());
|
||||||
|
pluginInfo.setPluginJson(pluginJson);
|
||||||
|
// 转二进制
|
||||||
|
pluginInfo.setJarFile(FileUtil.readBytes(jarFile));
|
||||||
|
|
||||||
|
if (force) {
|
||||||
|
CopyOptions options = CopyOptions.create().setIgnoreNullValue(true);
|
||||||
|
if (ObjUtil.isNotEmpty(pluginJson.getSameHookId())) {
|
||||||
|
// 存在同名,已强制安装,需将原插件关闭
|
||||||
|
PluginInfoEntity sameHookPlugin = new PluginInfoEntity();
|
||||||
|
sameHookPlugin.setStatus(0);
|
||||||
|
sameHookPlugin.setId(pluginJson.getSameHookId());
|
||||||
|
updatePlugin(sameHookPlugin);
|
||||||
|
}
|
||||||
|
// 通过key 找到id
|
||||||
|
PluginInfoEntity one = pluginInfoService.getByKeyNoJarFile(pluginJson.getKey());
|
||||||
|
if (ObjUtil.isNotEmpty(one)) {
|
||||||
|
// 重新加载配置不更新
|
||||||
|
pluginInfo.setConfig(one.getConfig());
|
||||||
|
pluginInfo.getPluginJson().setConfig(one.getConfig());
|
||||||
|
BeanUtil.copyProperties(pluginInfo, one, options);
|
||||||
|
one.updateById();
|
||||||
|
// 重新安装了调用设置插件历史配置信息
|
||||||
|
CoolPluginInvokers.setPluginJson(pluginJson.getKey(), one);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pluginInfo.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updatePlugin(PluginInfoEntity entity) {
|
||||||
|
PluginInfoEntity dbPluginInfoEntity = pluginInfoService.getPluginInfoEntityByIdNoJarFile(
|
||||||
|
entity.getId());
|
||||||
|
boolean updateConfig = false;
|
||||||
|
if (!MapExtUtil.compareMaps(entity.getConfig(), dbPluginInfoEntity.getConfig())) {
|
||||||
|
// 不一致,说明更新了配置
|
||||||
|
entity.getPluginJson().setConfig(entity.getConfig());
|
||||||
|
updateConfig = true;
|
||||||
|
}
|
||||||
|
if (!ObjUtil.equals(entity.getStatus(), dbPluginInfoEntity.getStatus())) {
|
||||||
|
// 更新状态
|
||||||
|
updateConfig = updateStatus(entity, dbPluginInfoEntity, updateConfig);
|
||||||
|
}
|
||||||
|
if (updateConfig) {
|
||||||
|
// 更新配置
|
||||||
|
CoolPluginInvokers.setPluginJson(getKeyById(entity.getId()), entity);
|
||||||
|
}
|
||||||
|
pluginInfoService.update(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新插件状态
|
||||||
|
*/
|
||||||
|
private boolean updateStatus(PluginInfoEntity entity, PluginInfoEntity dbPluginInfoEntity,
|
||||||
|
boolean updateConfig) {
|
||||||
|
// 更新状态
|
||||||
|
Integer status = entity.getStatus();
|
||||||
|
if (ObjUtil.equals(status, 1)) {
|
||||||
|
if (ObjUtil.isNotEmpty(dbPluginInfoEntity.getHook())) {
|
||||||
|
// 查找是否有同名hook,有同名hook,如果状态为开启不允许在开启,需先关闭原来
|
||||||
|
PluginInfoEntity hookPlugin = pluginInfoService
|
||||||
|
.getPluginInfoEntityByHookNoJarFile(dbPluginInfoEntity.getHook());
|
||||||
|
if (ObjUtil.isNotEmpty(hookPlugin)) {
|
||||||
|
CoolPreconditions.check(
|
||||||
|
!ObjUtil.equals(hookPlugin.getKey(), dbPluginInfoEntity.getKey())
|
||||||
|
&& ObjUtil.equals(hookPlugin.getStatus(), 1),
|
||||||
|
"插件已存在相同hook: {},请选关闭{}插件,在开启当前插件(同名hook,只能有一个插件开启)",
|
||||||
|
hookPlugin.getHook(),
|
||||||
|
hookPlugin.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 充关闭置为开启,触发重新加载jar
|
||||||
|
initInstall(dbPluginInfoEntity);
|
||||||
|
updateConfig = false;
|
||||||
|
} else if (ObjUtil.equals(status, 0)) {
|
||||||
|
// 插件关闭 卸载jar
|
||||||
|
dynamicJarLoaderService.uninstall(dbPluginInfoEntity.getKey());
|
||||||
|
}
|
||||||
|
return updateConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过hook获取插件
|
||||||
|
*/
|
||||||
|
public PluginInfoEntity getPluginInfoEntityByHook(String hook) {
|
||||||
|
return pluginInfoService.getPluginInfoEntityByHookNoJarFile(hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过id 获取key
|
||||||
|
*/
|
||||||
|
private String getKeyById(Long id) {
|
||||||
|
PluginInfoEntity one = pluginInfoService.getPluginInfoEntityByIdNoJarFile(id);
|
||||||
|
if (ObjUtil.isNotEmpty(one)) {
|
||||||
|
return one.getKey();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取插件实例对象
|
||||||
|
*/
|
||||||
|
public Object getInstance(String key) {
|
||||||
|
return dynamicJarLoaderService.getBeanInstance(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
package com.cool.core.plugin.service;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.IoUtil;
|
||||||
|
import cn.hutool.core.util.ObjUtil;
|
||||||
|
import cn.hutool.core.util.ReflectUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.cool.core.config.PluginJson;
|
||||||
|
import com.cool.core.exception.CoolPreconditions;
|
||||||
|
import com.cool.core.plugin.config.DynamicJarClassLoader;
|
||||||
|
import com.cool.core.util.AnnotationUtils;
|
||||||
|
import com.cool.modules.plugin.entity.PluginInfoEntity;
|
||||||
|
import com.cool.modules.plugin.service.PluginInfoService;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.JarURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.jar.JarEntry;
|
||||||
|
import java.util.jar.JarFile;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态加载jar包
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class DynamicJarLoaderService {
|
||||||
|
|
||||||
|
final private PluginInfoService pluginInfoService;
|
||||||
|
|
||||||
|
private final Map<String, DynamicJarClassLoader> dynamicJarClassLoaderMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private final Map<String, Object> pluginMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public PluginJson install(String jarFilePath, boolean force) throws Exception {
|
||||||
|
URL jarUrl = new URL("jar:file:" + new File(jarFilePath).getAbsolutePath() + "!/");
|
||||||
|
DynamicJarClassLoader dynamicJarClassLoader = new DynamicJarClassLoader(new URL[]{jarUrl},
|
||||||
|
Thread.currentThread().getContextClassLoader());
|
||||||
|
Thread.currentThread().setContextClassLoader(dynamicJarClassLoader);
|
||||||
|
InputStream inputStream = dynamicJarClassLoader.getResourceAsStream("plugin.json");
|
||||||
|
CoolPreconditions.check(ObjUtil.isEmpty(inputStream), "不合规插件:未找到plugin.json文件");
|
||||||
|
String pluginJsonStr = StrUtil.str(IoUtil.readBytes(inputStream), "UTF-8");
|
||||||
|
PluginJson pluginJson = JSONUtil.toBean(pluginJsonStr, PluginJson.class);
|
||||||
|
CoolPreconditions.check(ObjUtil.isEmpty(pluginJson.getKey()), "该插件缺少唯一标识");
|
||||||
|
if (ObjUtil.isNotEmpty(pluginJson.getHook())) {
|
||||||
|
// 查找hook是否已经存在,提示是否要替换,原hook将关闭
|
||||||
|
PluginInfoEntity pluginInfoEntity = pluginInfoService
|
||||||
|
.getPluginInfoEntityByHookNoJarFile(pluginJson.getHook());
|
||||||
|
if (!force) {
|
||||||
|
CoolPreconditions.returnData(
|
||||||
|
ObjUtil.isNotEmpty(pluginInfoEntity)
|
||||||
|
&& !ObjUtil.equals(pluginInfoEntity.getKey(), pluginJson.getKey()),
|
||||||
|
new CoolPreconditions.ReturnData(1,
|
||||||
|
"插件已存在相同hook: {},继续安装将关闭原来插件(同名hook,只能有一个状态开启)",
|
||||||
|
pluginJson.getHook()));
|
||||||
|
|
||||||
|
} else if (ObjUtil.isNotEmpty(pluginInfoEntity)
|
||||||
|
&& !ObjUtil.equals(pluginInfoEntity.getKey(), pluginJson.getKey())) {
|
||||||
|
// 存在同名hook,需将原hook修改为关闭
|
||||||
|
pluginJson.setSameHookId(pluginInfoEntity.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载类
|
||||||
|
List<Class<?>> plugins = new ArrayList<>();
|
||||||
|
try (JarFile jarFile = ((JarURLConnection) jarUrl.openConnection()).getJarFile()) {
|
||||||
|
Enumeration<JarEntry> entries = jarFile.entries();
|
||||||
|
while (entries.hasMoreElements()) {
|
||||||
|
JarEntry jarEntry = entries.nextElement();
|
||||||
|
if (jarEntry.getName().endsWith(".class")) {
|
||||||
|
String className = jarEntry.getName().replace('/', '.').substring(0,
|
||||||
|
jarEntry.getName().length() - 6);
|
||||||
|
try {
|
||||||
|
// 加载类
|
||||||
|
Class<?> clazz = dynamicJarClassLoader.loadClass(className);
|
||||||
|
if (AnnotationUtils.hasCoolPluginAnnotation(clazz)) {
|
||||||
|
plugins.add(clazz);
|
||||||
|
}
|
||||||
|
} catch (NoClassDefFoundError ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验插件
|
||||||
|
checkPlugin(plugins);
|
||||||
|
registerPlugin(pluginJson.getKey(), plugins.get(0), dynamicJarClassLoader, force);
|
||||||
|
dynamicJarClassLoaderMap.put(pluginJson.getKey(), dynamicJarClassLoader);
|
||||||
|
|
||||||
|
log.info("插件{}初始化成功.", pluginJson.getKey());
|
||||||
|
return pluginJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册插件
|
||||||
|
*/
|
||||||
|
private void registerPlugin(String key, Class<?> pluginClazz,
|
||||||
|
DynamicJarClassLoader dynamicJarClassLoader,
|
||||||
|
boolean force) {
|
||||||
|
if (!force && pluginMap.containsKey(key)) {
|
||||||
|
dynamicJarClassLoader.unload();
|
||||||
|
CoolPreconditions.returnData(
|
||||||
|
new CoolPreconditions.ReturnData(1, "插件已存在,继续安装将覆盖"));
|
||||||
|
}
|
||||||
|
if (ObjUtil.isNotEmpty(key)) {
|
||||||
|
pluginMap.remove(key);
|
||||||
|
pluginMap.put(key, ReflectUtil.newInstance(pluginClazz));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 卸载
|
||||||
|
*/
|
||||||
|
public boolean uninstall(String key) {
|
||||||
|
DynamicJarClassLoader dynamicJarClassLoader = getDynamicJarClassLoader(key);
|
||||||
|
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
try {
|
||||||
|
Thread.currentThread().setContextClassLoader(dynamicJarClassLoader);
|
||||||
|
pluginMap.remove(key);
|
||||||
|
if (dynamicJarClassLoader != null) {
|
||||||
|
dynamicJarClassLoader.unload();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("uninstall {}失败", key, e);
|
||||||
|
CoolPreconditions.alwaysThrow("卸载失败");
|
||||||
|
} finally {
|
||||||
|
Thread.currentThread().setContextClassLoader(originalClassLoader);
|
||||||
|
}
|
||||||
|
log.info("卸载插件{}", key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验插件是否合法
|
||||||
|
*/
|
||||||
|
private void checkPlugin(List<Class<?>> plugins) {
|
||||||
|
CoolPreconditions.checkEmpty(plugins, "未找到插件程序");
|
||||||
|
int size = plugins.size();
|
||||||
|
CoolPreconditions.check(size == 0,
|
||||||
|
"没找到符合规范的插件,插件需有@CoolPlugin注解,且以Plugin结尾(如:DemoPlugin)");
|
||||||
|
CoolPreconditions.check(size > 1,
|
||||||
|
"识别到当前安装包有多个插件(只能有一个@CoolPlugin注解),一次只支持一个插件导入");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取插件实例对象
|
||||||
|
*/
|
||||||
|
public Object getBeanInstance(String key) {
|
||||||
|
CoolPreconditions.checkEmpty(key, "插件key is null");
|
||||||
|
if (pluginMap.containsKey(key)) {
|
||||||
|
return pluginMap.get(key);
|
||||||
|
}
|
||||||
|
CoolPreconditions.alwaysThrow("插件 {} 未找到", key);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取自定义类加载器
|
||||||
|
*/
|
||||||
|
public DynamicJarClassLoader getDynamicJarClassLoader(String key) {
|
||||||
|
return dynamicJarClassLoaderMap.get(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package com.cool.core.request;
|
||||||
|
|
||||||
|
import cn.hutool.core.annotation.AnnotationUtil;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.mybatisflex.annotation.Table;
|
||||||
|
import com.mybatisflex.core.query.QueryColumn;
|
||||||
|
import com.mybatisflex.core.query.QueryCondition;
|
||||||
|
import com.mybatisflex.core.query.QueryWrapper;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询构建器
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CrudOption<T> {
|
||||||
|
|
||||||
|
private QueryWrapper queryWrapper;
|
||||||
|
private QueryColumn[] fieldEq;
|
||||||
|
private QueryColumn[] keyWordLikeFields;
|
||||||
|
private QueryColumn[] select;
|
||||||
|
private JSONObject requestParams;
|
||||||
|
|
||||||
|
private Environment evn;
|
||||||
|
|
||||||
|
public CrudOption(JSONObject requestParams) {
|
||||||
|
this.requestParams = requestParams;
|
||||||
|
this.queryWrapper = new QueryWrapper();
|
||||||
|
this.evn = SpringUtil.getBean(Environment.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CrudOption<T> fieldEq(QueryColumn... fields) {
|
||||||
|
this.fieldEq = fields;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueryWrapper getQueryWrapper(Class<T> entityClass) {
|
||||||
|
return build(this.queryWrapper, entityClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CrudOption<T> queryWrapper(QueryWrapper queryWrapper) {
|
||||||
|
this.queryWrapper = queryWrapper;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CrudOption<T> keyWordLikeFields(QueryColumn... fields) {
|
||||||
|
this.keyWordLikeFields = fields;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CrudOption<T> select(QueryColumn... selects) {
|
||||||
|
this.select = selects;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建查询条件
|
||||||
|
*
|
||||||
|
* @return QueryWrapper
|
||||||
|
*/
|
||||||
|
private QueryWrapper build(QueryWrapper queryWrapper, Class<T> entityClass) {
|
||||||
|
if (ObjectUtil.isNotEmpty(fieldEq)) {
|
||||||
|
Arrays.stream(fieldEq).toList().forEach(filed -> queryWrapper.and(
|
||||||
|
filed.eq(requestParams.get(StrUtil.toCamelCase(filed.getName())))));
|
||||||
|
}
|
||||||
|
Object keyWord = requestParams.get("keyWord");
|
||||||
|
if (ObjectUtil.isNotEmpty(this.keyWordLikeFields) && ObjectUtil.isNotEmpty(keyWord)) {
|
||||||
|
QueryCondition queryCondition = new QueryCondition();
|
||||||
|
Arrays.stream(keyWordLikeFields).toList().forEach(likeQueryColumn -> {
|
||||||
|
if (ObjectUtil.isEmpty(queryCondition.getColumn())) {
|
||||||
|
queryCondition.setColumn(likeQueryColumn);
|
||||||
|
queryCondition.setLogic(" LIKE ");
|
||||||
|
queryCondition.setValue("%" + keyWord + "%");
|
||||||
|
} else {
|
||||||
|
queryCondition.or(likeQueryColumn.like(keyWord));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
queryWrapper.and(queryCondition);
|
||||||
|
}
|
||||||
|
if (ObjectUtil.isNotEmpty(select)) {
|
||||||
|
queryWrapper.select(select);
|
||||||
|
}
|
||||||
|
// 排序
|
||||||
|
order(queryWrapper, entityClass);
|
||||||
|
return queryWrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void order(QueryWrapper queryWrapper, Class<T> entityClass) {
|
||||||
|
Table tableAnnotation = AnnotationUtil.getAnnotation(entityClass, Table.class);
|
||||||
|
if (ObjectUtil.isEmpty(tableAnnotation)) {
|
||||||
|
// 该对象没有@Table注解,非Entity对象
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String order = requestParams.getStr("order",
|
||||||
|
tableAnnotation.camelToUnderline() ? "create_time" : "createTime");
|
||||||
|
String sort = requestParams.getStr("sort", "desc");
|
||||||
|
if (StrUtil.isNotEmpty(order) && StrUtil.isNotEmpty(sort)) {
|
||||||
|
queryWrapper.orderBy(
|
||||||
|
tableAnnotation.camelToUnderline() ? StrUtil.toUnderlineCase(order) : order,
|
||||||
|
sort.equals("asc"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package com.cool.core.request;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回信息
|
||||||
|
*/
|
||||||
|
public class R extends HashMap<String, Object> {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public R() {
|
||||||
|
put("code", 1000);
|
||||||
|
put("message", "success");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static R error() {
|
||||||
|
return error(1001, "请求方式不正确或服务出现异常");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static R error(String msg) {
|
||||||
|
return error(1001, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static R error(int code, String msg) {
|
||||||
|
R r = new R();
|
||||||
|
r.put("code", code);
|
||||||
|
r.put("message", msg);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static R okMsg(String msg) {
|
||||||
|
R r = new R();
|
||||||
|
r.put("message", msg);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static R ok() {
|
||||||
|
return new R();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static R ok(Object data) {
|
||||||
|
return new R().put("data", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R put(String key, Object value) {
|
||||||
|
super.put(key, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package com.cool.core.request;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import cn.hutool.jwt.JWT;
|
||||||
|
import com.cool.core.util.BodyReaderHttpServletRequestWrapper;
|
||||||
|
import jakarta.servlet.*;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 封装请求参数 URL参数 和 body JSON 到同一个 JSONObject 方便读取
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Order(2)
|
||||||
|
public class RequestParamsFilter implements Filter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(FilterConfig filterConfig) throws ServletException {
|
||||||
|
Filter.super.init(filterConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
// 防止流读取一次后就没有了, 所以需要将流继续写出去
|
||||||
|
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||||
|
JSONObject requestParams = new JSONObject();
|
||||||
|
if (StrUtil.isNotEmpty(request.getContentType()) && request.getContentType().contains("multipart/form-data")) {
|
||||||
|
servletRequest.setAttribute("requestParams", requestParams);
|
||||||
|
filterChain.doFilter(servletRequest, servletResponse);
|
||||||
|
} else {
|
||||||
|
BodyReaderHttpServletRequestWrapper requestWrapper = new BodyReaderHttpServletRequestWrapper(request);
|
||||||
|
String body = requestWrapper.getBodyString(requestWrapper);
|
||||||
|
if (StrUtil.isNotEmpty(body) && JSONUtil.isJson(body) && !JSONUtil.isJsonArray(body)) {
|
||||||
|
requestParams = JSONUtil.parseObj(body);
|
||||||
|
}
|
||||||
|
requestParams.set("body", body);
|
||||||
|
requestParams.putAll(getAllRequestParam(request));
|
||||||
|
|
||||||
|
Object jwtObj = request.getAttribute("tokenInfo");
|
||||||
|
if (jwtObj != null) {
|
||||||
|
requestParams.set("tokenInfo", ((JWT) jwtObj).getPayload().getClaimsJson());
|
||||||
|
}
|
||||||
|
requestWrapper.setAttribute("requestParams", requestParams);
|
||||||
|
|
||||||
|
filterChain.doFilter(requestWrapper, servletResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取客户端请求参数中所有的信息
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private Map<String, Object> getAllRequestParam(final HttpServletRequest request) {
|
||||||
|
Map<String, Object> res = new HashMap<>();
|
||||||
|
Enumeration<?> temp = request.getParameterNames();
|
||||||
|
if (null != temp) {
|
||||||
|
while (temp.hasMoreElements()) {
|
||||||
|
String en = (String) temp.nextElement();
|
||||||
|
String value = request.getParameter(en);
|
||||||
|
res.put(en, value);
|
||||||
|
// 如果字段的值为空,判断若值为空,则删除这个字段>
|
||||||
|
if (null == res.get(en) || "".equals(res.get(en))) {
|
||||||
|
res.remove(en);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
Filter.super.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.cool.core.request;
|
||||||
|
|
||||||
|
import com.cool.core.annotation.CoolRestController;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.method.HandlerMethod;
|
||||||
|
import org.springframework.web.servlet.HandlerInterceptor;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用方法rest接口
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class RestInterceptor implements HandlerInterceptor {
|
||||||
|
private final static String[] rests = { "add", "delete", "update", "info", "list", "page" };
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||||
|
// 判断有无通用方法
|
||||||
|
if (handler instanceof HandlerMethod) {
|
||||||
|
HandlerMethod handlerMethod = (HandlerMethod) handler;
|
||||||
|
CoolRestController coolRestController = handlerMethod.getBeanType().getAnnotation(CoolRestController.class);
|
||||||
|
if (null != coolRestController) {
|
||||||
|
String[] urls = request.getRequestURI().split("/");
|
||||||
|
String rest = urls[urls.length - 1];
|
||||||
|
if (Arrays.asList(rests).contains(rest)) {
|
||||||
|
if (!Arrays.asList(coolRestController.api()).contains(rest)) {
|
||||||
|
response.setStatus(HttpStatus.NOT_FOUND.value());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.cool.core.request.prefix;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义路由规则
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class AutoPrefixConfiguration implements WebMvcRegistrations {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
|
||||||
|
return new AutoPrefixUrlMapping();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package com.cool.core.request.prefix;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import com.cool.core.annotation.CoolRestController;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动配置模块的路由
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class AutoPrefixUrlMapping extends RequestMappingHandlerMapping {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
|
||||||
|
CoolRestController[] annotations = handlerType.getAnnotationsByType(CoolRestController.class);
|
||||||
|
RequestMappingInfo info = super.getMappingForMethod(method, handlerType);
|
||||||
|
String packageName = handlerType.getPackage().getName();
|
||||||
|
if (info != null && annotations.length > 0 && annotations[0].value().length == 0
|
||||||
|
&& packageName.contains("modules")) {
|
||||||
|
if (!checkApis(annotations, info)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String prefix = getPrefix(handlerType, packageName);
|
||||||
|
String cName = getCName(handlerType, prefix);
|
||||||
|
info = info.mutate().paths(prefix + "/" + cName).build().combine(info);
|
||||||
|
}
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据配置检查是否构建路由
|
||||||
|
*
|
||||||
|
* @param annotations 注解
|
||||||
|
* @param info 路由信息
|
||||||
|
* @return 是否需要构建路由
|
||||||
|
*/
|
||||||
|
private boolean checkApis(CoolRestController[] annotations, RequestMappingInfo info) {
|
||||||
|
String[] apis = new String[] { "add", "delete", "update", "page", "list", "info" };
|
||||||
|
if (info.getPathPatternsCondition() == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
List<String> setApis;
|
||||||
|
if (ArrayUtil.isNotEmpty(annotations)) {
|
||||||
|
CoolRestController coolRestController = annotations[0];
|
||||||
|
setApis = CollUtil.toList(coolRestController.api());
|
||||||
|
|
||||||
|
Set<String> methodPaths = info.getPathPatternsCondition().getPatternValues();
|
||||||
|
String methodPath = methodPaths.iterator().next().replace("/", "");
|
||||||
|
if (!CollUtil.toList(apis).contains(methodPath)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return setApis.contains(methodPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据Controller名称构建路由地址
|
||||||
|
*
|
||||||
|
* @param handlerType 类
|
||||||
|
* @param prefix 路由前缀
|
||||||
|
* @return url地址
|
||||||
|
*/
|
||||||
|
private String getCName(Class<?> handlerType, String prefix) {
|
||||||
|
String name = handlerType.getName();
|
||||||
|
String[] names = name.split("[.]");
|
||||||
|
name = names[names.length - 1];
|
||||||
|
return name.toLowerCase().replace("controller", "").replace(prefix.replace("/", ""), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建路由前缀
|
||||||
|
*
|
||||||
|
* @param handlerType 类
|
||||||
|
* @param packageName 包名
|
||||||
|
* @return 返回路由前缀
|
||||||
|
*/
|
||||||
|
private String getPrefix(Class<?> handlerType, String packageName) {
|
||||||
|
String dotPath = packageName.split("modules")[1]; // 将包路径中多于的部分截取掉
|
||||||
|
String[] dotPaths = dotPath.replace(".controller", "").split("[.]");
|
||||||
|
List<String> paths = CollUtil.toList(dotPaths);
|
||||||
|
paths.removeIf(String::isEmpty);
|
||||||
|
// 第一和第二位互换位置
|
||||||
|
String p0 = paths.get(0);
|
||||||
|
String p1 = paths.get(1);
|
||||||
|
paths.set(0, p1);
|
||||||
|
paths.set(1, p0);
|
||||||
|
dotPath = "/" + CollUtil.join(paths, "/");
|
||||||
|
return dotPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.cool.core.security;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定401返回值
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class EntryPointUnauthorizedHandler implements AuthenticationEntryPoint {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e)
|
||||||
|
throws IOException {
|
||||||
|
response.setHeader("Access-Control-Allow-Origin", "*");
|
||||||
|
response.setCharacterEncoding("UTF-8");
|
||||||
|
response.setContentType("application/json; charset=utf-8");
|
||||||
|
response.getWriter().write(JSONUtil.toJsonStr(new HashMap<String, Object>() {
|
||||||
|
{
|
||||||
|
put("code", "401");
|
||||||
|
put("message", "未登录");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
response.setStatus(401);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.cool.core.security;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 忽略token地址配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "ignored")
|
||||||
|
public class IgnoredUrlsProperties {
|
||||||
|
// 忽略权限列表
|
||||||
|
private List<String> urls = new ArrayList<>();
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package com.cool.core.security;
|
||||||
|
|
||||||
|
import com.cool.modules.base.security.JwtAuthenticationTokenFilter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.authentication.AuthenticationProvider;
|
||||||
|
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||||
|
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
|
||||||
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
|
import org.springframework.util.DigestUtils;
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
@Configuration
|
||||||
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class JwtSecurityConfig {
|
||||||
|
|
||||||
|
// 用户详情
|
||||||
|
final private UserDetailsService userDetailsService;
|
||||||
|
|
||||||
|
final private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
|
||||||
|
// 401
|
||||||
|
final private EntryPointUnauthorizedHandler entryPointUnauthorizedHandler;
|
||||||
|
// 403
|
||||||
|
final private RestAccessDeniedHandler restAccessDeniedHandler;
|
||||||
|
// 忽略权限控制的地址
|
||||||
|
final private IgnoredUrlsProperties ignoredUrlsProperties;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
|
||||||
|
return httpSecurity
|
||||||
|
.authorizeHttpRequests(
|
||||||
|
conf -> conf.requestMatchers(
|
||||||
|
ignoredUrlsProperties.getUrls().toArray(String[]::new))
|
||||||
|
.permitAll().anyRequest().authenticated())
|
||||||
|
.headers(config -> config.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
|
||||||
|
// 允许网页iframe
|
||||||
|
.csrf(AbstractHttpConfigurer::disable)
|
||||||
|
.sessionManagement(conf -> conf.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||||
|
.addFilterBefore(jwtAuthenticationTokenFilter,
|
||||||
|
UsernamePasswordAuthenticationFilter.class)
|
||||||
|
.exceptionHandling(config -> {
|
||||||
|
config.authenticationEntryPoint(entryPointUnauthorizedHandler);
|
||||||
|
config.accessDeniedHandler(restAccessDeniedHandler);
|
||||||
|
}).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public PasswordEncoder passwordEncoder() {
|
||||||
|
return new PasswordEncoder() {
|
||||||
|
@Override
|
||||||
|
public String encode(CharSequence rawPassword) {
|
||||||
|
return DigestUtils.md5DigestAsHex(((String) rawPassword).getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||||
|
return encodedPassword.equals(
|
||||||
|
DigestUtils.md5DigestAsHex(((String) rawPassword).getBytes()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public AuthenticationProvider authenticationProvider() {
|
||||||
|
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
|
||||||
|
authProvider.setUserDetailsService(userDetailsService);
|
||||||
|
authProvider.setPasswordEncoder(passwordEncoder());
|
||||||
|
return authProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public AuthenticationManager authenticationManager(AuthenticationConfiguration config)
|
||||||
|
throws Exception {
|
||||||
|
return config.getAuthenticationManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.cool.core.security;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.security.access.AccessDecisionManager;
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
|
import org.springframework.security.access.ConfigAttribute;
|
||||||
|
import org.springframework.security.authentication.InsufficientAuthenticationException;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限管理决断器 判断用户拥有的权限或角色是否有资源访问权限
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class MyAccessDecisionManager implements AccessDecisionManager {
|
||||||
|
@Override
|
||||||
|
public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> configAttributes)
|
||||||
|
throws AccessDeniedException, InsufficientAuthenticationException {
|
||||||
|
if (configAttributes == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Iterator<ConfigAttribute> iterator = configAttributes.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
ConfigAttribute c = iterator.next();
|
||||||
|
String needPerm = c.getAttribute();
|
||||||
|
for (GrantedAuthority ga : authentication.getAuthorities()) {
|
||||||
|
// 匹配用户拥有的ga 和 系统中的needPerm
|
||||||
|
if (needPerm.trim().equals(ga.getAuthority())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new AccessDeniedException("抱歉,您没有访问权限");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(ConfigAttribute configAttribute) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(Class<?> aClass) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package com.cool.core.security;
|
||||||
|
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.Filter;
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.FilterConfig;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.ServletRequest;
|
||||||
|
import jakarta.servlet.ServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.security.access.SecurityMetadataSource;
|
||||||
|
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
|
||||||
|
import org.springframework.security.access.intercept.InterceptorStatusToken;
|
||||||
|
import org.springframework.security.web.FilterInvocation;
|
||||||
|
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限管理拦截器 监控用户行为
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
|
||||||
|
|
||||||
|
final private FilterInvocationSecurityMetadataSource securityMetadataSource;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
|
||||||
|
super.setAccessDecisionManager(myAccessDecisionManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(FilterConfig filterConfig) throws ServletException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
FilterInvocation fi = new FilterInvocation(request, response, chain);
|
||||||
|
invoke(fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void invoke(FilterInvocation fi) throws IOException, ServletException {
|
||||||
|
InterceptorStatusToken token = super.beforeInvocation(fi);
|
||||||
|
try {
|
||||||
|
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
|
||||||
|
} finally {
|
||||||
|
super.afterInvocation(token, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getSecureObjectClass() {
|
||||||
|
return FilterInvocation.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecurityMetadataSource obtainSecurityMetadataSource() {
|
||||||
|
return this.securityMetadataSource;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.cool.core.security;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
|
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定403返回值
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class RestAccessDeniedHandler implements AccessDeniedHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e)
|
||||||
|
throws IOException {
|
||||||
|
response.setHeader("Access-Control-Allow-Origin", "*");
|
||||||
|
response.setCharacterEncoding("UTF-8");
|
||||||
|
response.setContentType("application/json; charset=utf-8");
|
||||||
|
response.getWriter().write(JSONUtil.toJsonStr(new HashMap<String, Object>() {
|
||||||
|
{
|
||||||
|
put("code", "403");
|
||||||
|
put("message", "无权限");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
response.setStatus(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
package com.cool.core.security.jwt;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.DateUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.jwt.JWT;
|
||||||
|
import cn.hutool.jwt.JWTUtil;
|
||||||
|
import cn.hutool.jwt.JWTValidator;
|
||||||
|
import com.cool.core.config.CoolProperties;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.cool.modules.base.service.sys.BaseSysConfService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JWT工具类
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class JwtTokenUtil implements Serializable {
|
||||||
|
|
||||||
|
final private CoolProperties coolProperties;
|
||||||
|
final private BaseSysConfService baseSysConfService;
|
||||||
|
final String key = "JWT_SECRET";
|
||||||
|
|
||||||
|
public long getExpire() {
|
||||||
|
return this.coolProperties.getToken().getExpire();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getRefreshExpire() {
|
||||||
|
return this.coolProperties.getToken().getRefreshExpire();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSecret() {
|
||||||
|
String secret = baseSysConfService.getValueWithCache(key);
|
||||||
|
if (StrUtil.isBlank(secret)) {
|
||||||
|
secret = StrUtil.uuid().replaceAll("-", "");
|
||||||
|
baseSysConfService.setValue(key, secret);
|
||||||
|
}
|
||||||
|
return secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成令牌
|
||||||
|
*
|
||||||
|
* @param tokenInfo 保存的用户信息
|
||||||
|
* @return 令牌
|
||||||
|
*/
|
||||||
|
public String generateToken(Map<String, Object> tokenInfo) {
|
||||||
|
tokenInfo.put("isRefresh", false);
|
||||||
|
Date expirationDate = new Date(System.currentTimeMillis() + getExpire() * 1000);
|
||||||
|
JWT jwt = JWT.create().setExpiresAt(expirationDate).setKey(getSecret().getBytes())
|
||||||
|
.setPayload("created", new Date());
|
||||||
|
tokenInfo.forEach(jwt::setPayload);
|
||||||
|
return jwt.sign();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成令牌
|
||||||
|
*
|
||||||
|
* @param tokenInfo 保存的用户信息
|
||||||
|
* @return 令牌
|
||||||
|
*/
|
||||||
|
public String generateRefreshToken(Map<String, Object> tokenInfo) {
|
||||||
|
tokenInfo.put("isRefresh", true);
|
||||||
|
Date expirationDate = new Date(System.currentTimeMillis() + getRefreshExpire() * 1000);
|
||||||
|
JWT jwt = JWT.create().setExpiresAt(expirationDate).setKey(getSecret().getBytes())
|
||||||
|
.setPayload("created", new Date());
|
||||||
|
tokenInfo.forEach(jwt::setPayload);
|
||||||
|
return jwt.sign();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从令牌中获取用户名
|
||||||
|
*
|
||||||
|
* @param token 令牌
|
||||||
|
* @return 用户名
|
||||||
|
*/
|
||||||
|
public String getUsernameFromToken(String token) {
|
||||||
|
JWT jwt = JWT.of(token);
|
||||||
|
return jwt.getPayload("username").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得token信息
|
||||||
|
*
|
||||||
|
* @param token 令牌
|
||||||
|
* @return token信息
|
||||||
|
*/
|
||||||
|
public JWT getTokenInfo(String token) {
|
||||||
|
return JWT.of(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断令牌是否过期
|
||||||
|
*
|
||||||
|
* @param token 令牌
|
||||||
|
* @return 是否过期
|
||||||
|
*/
|
||||||
|
public Boolean isTokenExpired(String token) {
|
||||||
|
try {
|
||||||
|
JWTValidator.of(token).validateDate(DateUtil.date());
|
||||||
|
return false;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证令牌
|
||||||
|
*
|
||||||
|
* @param token 令牌
|
||||||
|
* @param username 用户
|
||||||
|
* @return 是否有效
|
||||||
|
*/
|
||||||
|
public Boolean validateToken(String token, String username) {
|
||||||
|
String tokenUsername = getUsernameFromToken(token);
|
||||||
|
String secret = getSecret();
|
||||||
|
boolean isValidSignature = JWTUtil.verify(token, secret.getBytes());
|
||||||
|
return (tokenUsername.equals(username) && !isTokenExpired(token) && isValidSignature);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package com.cool.core.security.jwt;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户信息
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class JwtUser implements UserDetails {
|
||||||
|
|
||||||
|
public JwtUser(String username, String password, List<GrantedAuthority> perms, Boolean status) {
|
||||||
|
this.username = username;
|
||||||
|
this.password = password;
|
||||||
|
this.perms = perms;
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
|
private Boolean status;
|
||||||
|
private List<GrantedAuthority> perms;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||||
|
return perms;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAccountNonExpired() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAccountNonLocked() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCredentialsNonExpired() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package com.cool.core.util;
|
||||||
|
|
||||||
|
import com.cool.core.annotation.CoolPlugin;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class AnnotationUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断一个类是否有 Spring 核心注解
|
||||||
|
*
|
||||||
|
* @param clazz 要检查的类
|
||||||
|
* @return true 如果该类上添加了相应的 Spring 注解;否则返回 false
|
||||||
|
*/
|
||||||
|
public static boolean hasSpringAnnotation(Class<?> clazz) {
|
||||||
|
if (clazz == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 是否是接口
|
||||||
|
if (clazz.isInterface()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 是否是抽象类
|
||||||
|
if (Modifier.isAbstract(clazz.getModifiers())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (clazz.getAnnotation(Component.class) != null || clazz.getAnnotation(Repository.class) != null
|
||||||
|
|| clazz.getAnnotation(Service.class) != null || clazz.getAnnotation(Controller.class) != null
|
||||||
|
|| clazz.getAnnotation(Configuration.class) != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("出现异常:{}", e.getMessage());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件
|
||||||
|
*/
|
||||||
|
public static boolean hasCoolPluginAnnotation(Class<?> clazz) {
|
||||||
|
if (clazz == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 是否是接口
|
||||||
|
if (clazz.isInterface()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 是否是抽象类
|
||||||
|
if (Modifier.isAbstract(clazz.getModifiers())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// if (clazz.getAnnotation(CoolPlugin.class) != null) {
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
if (clazz.getAnnotation(
|
||||||
|
(Class<? extends Annotation>) contextClassLoader.loadClass(CoolPlugin.class.getName())) != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("出现异常:{}", e.getMessage());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
package com.cool.core.util;
|
||||||
|
|
||||||
|
import jakarta.servlet.ReadListener;
|
||||||
|
import jakarta.servlet.ServletInputStream;
|
||||||
|
import jakarta.servlet.ServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletRequestWrapper;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存流
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
|
||||||
|
|
||||||
|
private final byte[] body;
|
||||||
|
|
||||||
|
public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
|
||||||
|
super(request);
|
||||||
|
String sessionStream = getBodyString(request);
|
||||||
|
body = sessionStream.getBytes(Charset.forName("UTF-8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求Body
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getBodyString(final ServletRequest request) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
InputStream inputStream = null;
|
||||||
|
BufferedReader reader = null;
|
||||||
|
try {
|
||||||
|
inputStream = cloneInputStream(request.getInputStream());
|
||||||
|
reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
|
||||||
|
String line = "";
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
sb.append(line);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("err", e);
|
||||||
|
} finally {
|
||||||
|
if (inputStream != null) {
|
||||||
|
try {
|
||||||
|
inputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("err", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (reader != null) {
|
||||||
|
try {
|
||||||
|
reader.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("err", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description: 复制输入流</br>
|
||||||
|
*
|
||||||
|
* @param inputStream
|
||||||
|
* @return</br>
|
||||||
|
*/
|
||||||
|
public InputStream cloneInputStream(ServletInputStream inputStream) {
|
||||||
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int len;
|
||||||
|
try {
|
||||||
|
while ((len = inputStream.read(buffer)) > -1) {
|
||||||
|
byteArrayOutputStream.write(buffer, 0, len);
|
||||||
|
}
|
||||||
|
byteArrayOutputStream.flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("err", e);
|
||||||
|
}
|
||||||
|
InputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
|
||||||
|
return byteArrayInputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BufferedReader getReader() throws IOException {
|
||||||
|
return new BufferedReader(new InputStreamReader(getInputStream()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServletInputStream getInputStream() throws IOException {
|
||||||
|
|
||||||
|
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
|
||||||
|
|
||||||
|
return new ServletInputStream() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
return bais.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFinished() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isReady() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setReadListener(ReadListener readListener) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package com.cool.core.util;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.FileUtil;
|
||||||
|
import com.mybatisflex.processor.MybatisFlexProcessor;
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.annotation.processing.Processor;
|
||||||
|
import javax.tools.JavaCompiler;
|
||||||
|
import javax.tools.JavaFileObject;
|
||||||
|
import javax.tools.StandardJavaFileManager;
|
||||||
|
import javax.tools.ToolProvider;
|
||||||
|
|
||||||
|
public class CompilerUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建文件, 先删除在创建
|
||||||
|
*/
|
||||||
|
public static void createFile(String content, String filePathStr) {
|
||||||
|
FileUtil.del(filePathStr);
|
||||||
|
File file = FileUtil.touch(filePathStr);
|
||||||
|
FileUtil.appendString(content, file, StandardCharsets.UTF_8.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void createMapper(String actModulePath, String fileName, String mapper) {
|
||||||
|
String pathStr = actModulePath + "/mapper/";
|
||||||
|
String filePathStr = pathStr + fileName + "Mapper.java";
|
||||||
|
createFile(mapper, filePathStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void createServiceImpl(String actModulePath, String fileName,
|
||||||
|
String serviceImpl) {
|
||||||
|
String pathStr = actModulePath + "/service/impl/";
|
||||||
|
String filePathStr = pathStr + fileName + "ServiceImpl.java";
|
||||||
|
createFile(serviceImpl, filePathStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void createService(String actModulePath, String fileName, String service) {
|
||||||
|
String pathStr = actModulePath + "/service/";
|
||||||
|
String filePathStr = pathStr + fileName + "Service.java";
|
||||||
|
createFile(service, filePathStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String createEntity(String actModulePath, String fileName, String entity) {
|
||||||
|
String pathStr = actModulePath + "/entity/";
|
||||||
|
String filePathStr = pathStr + fileName + "Entity.java";
|
||||||
|
createFile(entity, filePathStr);
|
||||||
|
return filePathStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void createController(String actModulePath, String fileName, String controller) {
|
||||||
|
String pathStr = actModulePath + "/controller/admin/";
|
||||||
|
String filePathStr = pathStr + "Admin" + fileName + "Controller.java";
|
||||||
|
createFile(controller, filePathStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String createModule(String modulesPath, String module) {
|
||||||
|
String pathStr = modulesPath + "/" + module;
|
||||||
|
PathUtils.noExistsMk(pathStr);
|
||||||
|
return pathStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void compilerEntityTableDef(String path) {
|
||||||
|
// JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
|
||||||
|
// StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
|
||||||
|
//
|
||||||
|
// Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects(
|
||||||
|
// path);
|
||||||
|
//
|
||||||
|
// // 设置注解处理器
|
||||||
|
// Iterable<? extends Processor> processors = Arrays.asList(new MybatisFlexProcessor());
|
||||||
|
// // 添加 -proc:only 选项
|
||||||
|
// List<String> options = Arrays.asList("-proc:only");
|
||||||
|
// JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, options,
|
||||||
|
// null, compilationUnits);
|
||||||
|
// task.setProcessors(processors);
|
||||||
|
//
|
||||||
|
// boolean success = task.call();
|
||||||
|
// if (success) {
|
||||||
|
// System.out.println("Compilation and annotation processing completed successfully.");
|
||||||
|
// } else {
|
||||||
|
// System.out.println("Compilation and annotation processing failed.");
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
package com.cool.core.util;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换
|
||||||
|
*/
|
||||||
|
public class ConvertUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对象转数组
|
||||||
|
*
|
||||||
|
* @param obj
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static byte[] toByteArray(Object obj) {
|
||||||
|
byte[] bytes = null;
|
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
try {
|
||||||
|
ObjectOutputStream oos = new ObjectOutputStream(bos);
|
||||||
|
oos.writeObject(obj);
|
||||||
|
oos.flush();
|
||||||
|
bytes = bos.toByteArray();
|
||||||
|
oos.close();
|
||||||
|
bos.close();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数组转对象
|
||||||
|
*
|
||||||
|
* @param bytes
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Object toObject(byte[] bytes) {
|
||||||
|
Object obj = null;
|
||||||
|
try {
|
||||||
|
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
|
||||||
|
ObjectInputStream ois = new ObjectInputStream(bis);
|
||||||
|
obj = ois.readObject();
|
||||||
|
ois.close();
|
||||||
|
bis.close();
|
||||||
|
} catch (IOException | ClassNotFoundException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MultipartFile convertToMultipartFile(File file) {
|
||||||
|
FileInputStream inputStream = null;
|
||||||
|
try {
|
||||||
|
inputStream = new FileInputStream(file);
|
||||||
|
return new SimpleMultipartFile(file.getName(), inputStream);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
if (inputStream != null) {
|
||||||
|
try {
|
||||||
|
inputStream.close();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 简单的MultipartFile实现,用于模拟Spring中的MultipartFile对象
|
||||||
|
static class SimpleMultipartFile implements MultipartFile {
|
||||||
|
|
||||||
|
private String filename;
|
||||||
|
private InputStream inputStream;
|
||||||
|
|
||||||
|
public SimpleMultipartFile(String filename, InputStream inputStream) {
|
||||||
|
this.filename = filename;
|
||||||
|
this.inputStream = inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getOriginalFilename() {
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getContentType() {
|
||||||
|
return "application/octet-stream";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getSize() {
|
||||||
|
try {
|
||||||
|
return inputStream.available();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getBytes() throws IOException {
|
||||||
|
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int len;
|
||||||
|
while ((len = inputStream.read(buffer)) != -1) {
|
||||||
|
output.write(buffer, 0, len);
|
||||||
|
}
|
||||||
|
return output.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream() throws IOException {
|
||||||
|
return inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transferTo(File dest) throws IOException, IllegalStateException {
|
||||||
|
try (FileOutputStream outputStream = new FileOutputStream(dest)) {
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int len;
|
||||||
|
while ((len = inputStream.read(buffer)) != -1) {
|
||||||
|
outputStream.write(buffer, 0, len);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (inputStream != null) {
|
||||||
|
inputStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
package com.cool.core.util;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.cool.core.exception.CoolPreconditions;
|
||||||
|
import com.cool.core.plugin.consts.PluginConsts;
|
||||||
|
import com.cool.core.plugin.service.DynamicJarLoaderService;
|
||||||
|
import com.cool.modules.plugin.entity.PluginInfoEntity;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件调用封装
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class CoolPluginInvokers {
|
||||||
|
|
||||||
|
private static final DynamicJarLoaderService dynamicJarLoaderService = SpringContextUtils
|
||||||
|
.getBean(DynamicJarLoaderService.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件默认调用入口
|
||||||
|
*/
|
||||||
|
public static Object invokePlugin(String key, String... params) {
|
||||||
|
return invoke(key, PluginConsts.invokePluginMethodName, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置插件配置信息
|
||||||
|
*/
|
||||||
|
public static void setPluginJson(String key, PluginInfoEntity entity) {
|
||||||
|
invoke(key, PluginConsts.setPluginJson, JSONUtil.toJsonStr(entity.getPluginJson()));
|
||||||
|
setApplicationContext(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置 ApplicationContext 到插件类中
|
||||||
|
*/
|
||||||
|
public static void setApplicationContext(String key) {
|
||||||
|
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
try {
|
||||||
|
Thread.currentThread()
|
||||||
|
.setContextClassLoader(dynamicJarLoaderService.getDynamicJarClassLoader(key));
|
||||||
|
Object beanInstance = dynamicJarLoaderService.getBeanInstance(key);
|
||||||
|
Method method = beanInstance.getClass().getSuperclass()
|
||||||
|
.getMethod(PluginConsts.setApplicationContext,
|
||||||
|
ApplicationContext.class);
|
||||||
|
method.invoke(beanInstance, SpringContextUtils.applicationContext);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("setApplicationContext err", e);
|
||||||
|
} finally {
|
||||||
|
Thread.currentThread().setContextClassLoader(originalClassLoader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反射调用插件
|
||||||
|
*
|
||||||
|
* @param key 插件key
|
||||||
|
* @param methodName 插件方法
|
||||||
|
* @param params 参数
|
||||||
|
*/
|
||||||
|
public static Object invoke(String key, String methodName, Object... params) {
|
||||||
|
Object beanInstance = dynamicJarLoaderService.getBeanInstance(key);
|
||||||
|
CoolPreconditions.checkEmpty(beanInstance, "未找到该插件:" + key);
|
||||||
|
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
try {
|
||||||
|
// 设置当前线程的上下文类加载器为插件的类加载器
|
||||||
|
Thread.currentThread()
|
||||||
|
.setContextClassLoader(dynamicJarLoaderService.getDynamicJarClassLoader(key));
|
||||||
|
log.info("调用插件类: {}, 方法: {} 参数: {}", key, methodName, params);
|
||||||
|
return invoke(beanInstance, methodName, params);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("调用插件{}.{}失败", key, methodName, e);
|
||||||
|
CoolPreconditions.alwaysThrow("调用插件{}.{}失败 {}", key, methodName, e.getMessage());
|
||||||
|
} finally {
|
||||||
|
Thread.currentThread().setContextClassLoader(originalClassLoader);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反射调用插件
|
||||||
|
*
|
||||||
|
* @param beanInstance 插件实例对象
|
||||||
|
* @param methodName 插件方法
|
||||||
|
* @param params 参数
|
||||||
|
*/
|
||||||
|
private static Object invoke(Object beanInstance, String methodName, Object[] params)
|
||||||
|
throws InvocationTargetException, IllegalAccessException {
|
||||||
|
Class<?>[] paramTypes = Arrays.stream(params).map(Object::getClass)
|
||||||
|
.toArray(Class<?>[]::new);
|
||||||
|
Method method = findMethod(beanInstance.getClass(), methodName, paramTypes);
|
||||||
|
CoolPreconditions.check(method == null, "No such method: {} with parameters {}", methodName,
|
||||||
|
Arrays.toString(paramTypes));
|
||||||
|
if (method.isVarArgs()) {
|
||||||
|
// 处理可变参数调用
|
||||||
|
int varArgIndex = method.getParameterTypes().length - 1;
|
||||||
|
Object[] varArgs = (Object[]) java.lang.reflect.Array.newInstance(
|
||||||
|
method.getParameterTypes()[varArgIndex].getComponentType(),
|
||||||
|
params.length - varArgIndex);
|
||||||
|
System.arraycopy(params, varArgIndex, varArgs, 0, varArgs.length);
|
||||||
|
Object[] methodArgs = new Object[varArgIndex + 1];
|
||||||
|
System.arraycopy(params, 0, methodArgs, 0, varArgIndex);
|
||||||
|
methodArgs[varArgIndex] = varArgs;
|
||||||
|
return method.invoke(beanInstance, methodArgs);
|
||||||
|
} else {
|
||||||
|
// 正常调用
|
||||||
|
return method.invoke(beanInstance, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找方法,包括处理可变参数
|
||||||
|
private static Method findMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) {
|
||||||
|
try {
|
||||||
|
return clazz.getMethod(methodName, paramTypes);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
// Try to find a varargs method
|
||||||
|
for (Method method : clazz.getMethods()) {
|
||||||
|
if (method.getName().equals(methodName) && isAssignable(paramTypes,
|
||||||
|
method.getParameterTypes(), method.isVarArgs())) {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If not found, try to find in superclass
|
||||||
|
if (clazz.getSuperclass() != null) {
|
||||||
|
return findMethod(clazz.getSuperclass(), methodName, paramTypes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isAssignable(Class<?>[] paramTypes, Class<?>[] methodParamTypes,
|
||||||
|
boolean isVarArgs) {
|
||||||
|
if (isVarArgs) {
|
||||||
|
if (paramTypes.length < methodParamTypes.length - 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < methodParamTypes.length - 1; i++) {
|
||||||
|
if (!methodParamTypes[i].isAssignableFrom(paramTypes[i])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Class<?> varArgType = methodParamTypes[methodParamTypes.length - 1].getComponentType();
|
||||||
|
for (int i = methodParamTypes.length - 1; i < paramTypes.length; i++) {
|
||||||
|
if (!varArgType.isAssignableFrom(paramTypes[i])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (paramTypes.length != methodParamTypes.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < paramTypes.length; i++) {
|
||||||
|
if (!methodParamTypes[i].isAssignableFrom(paramTypes[i])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package com.cool.core.util;
|
||||||
|
|
||||||
|
import cn.hutool.core.annotation.AnnotationUtil;
|
||||||
|
import cn.hutool.core.util.ObjUtil;
|
||||||
|
import com.mybatisflex.annotation.Table;
|
||||||
|
import com.mybatisflex.core.query.QueryColumn;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||||
|
|
||||||
|
public class EntityUtils {
|
||||||
|
|
||||||
|
private static Map<String, Class<?>> TABLE_MAP;
|
||||||
|
|
||||||
|
public static Set<String> findEntityClassName() {
|
||||||
|
Set<String> entitySet = new HashSet<>();
|
||||||
|
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
||||||
|
Resource[] resources = null;
|
||||||
|
try {
|
||||||
|
resources = resolver.getResources("classpath*:com/cool/modules/**/entity/**/*.class");
|
||||||
|
for (Resource r : resources) {
|
||||||
|
String path = r.getURL().getPath();
|
||||||
|
String className = path.substring(path.indexOf("com/cool/modules"),
|
||||||
|
path.lastIndexOf('.')).replace('/', '.');
|
||||||
|
entitySet.add(className);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return entitySet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, Class<?>> findTableMap() {
|
||||||
|
if (ObjUtil.isEmpty(TABLE_MAP)) {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
return TABLE_MAP;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void init() {
|
||||||
|
Set<String> classNames = EntityUtils.findEntityClassName();
|
||||||
|
TABLE_MAP = new HashMap<>();
|
||||||
|
classNames.forEach(className -> {
|
||||||
|
Class<?> entityClass;
|
||||||
|
try {
|
||||||
|
entityClass = Class.forName(className);
|
||||||
|
Table tableAnnotation = AnnotationUtil.getAnnotation(entityClass, Table.class);
|
||||||
|
// key表名,value 实体对象
|
||||||
|
TABLE_MAP.put(tableAnnotation.value(), entityClass);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取实体类及其父类的字段名数组(排除指定字段)
|
||||||
|
*
|
||||||
|
* @return 字段名数组
|
||||||
|
*/
|
||||||
|
public static QueryColumn[] getFieldNamesWithSuperClass(QueryColumn[] queryColumns,
|
||||||
|
String... excludeNames) {
|
||||||
|
return getFieldNamesListWithSuperClass(queryColumns, excludeNames).toArray(
|
||||||
|
new QueryColumn[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<QueryColumn> getFieldNamesListWithSuperClass(QueryColumn[] queryColumns,
|
||||||
|
String... excludeNames) {
|
||||||
|
return Arrays.stream(queryColumns).toList().stream()
|
||||||
|
.filter(o -> ObjUtil.equals(o.getName(), excludeNames)).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查字段名是否在排除列表中
|
||||||
|
*
|
||||||
|
* @param fieldName 要检查的字段名
|
||||||
|
* @param excludeNames 排除的字段名数组
|
||||||
|
* @return 是否在排除列表中
|
||||||
|
*/
|
||||||
|
private static boolean isExcluded(String fieldName, String[] excludeNames) {
|
||||||
|
for (String excludeName : excludeNames) {
|
||||||
|
if (fieldName.equals(excludeName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.cool.core.util;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IP地址
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class IPUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取IP地址
|
||||||
|
* <p>
|
||||||
|
* 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址
|
||||||
|
* 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址
|
||||||
|
*/
|
||||||
|
public String getIpAddr(HttpServletRequest request) {
|
||||||
|
String ip = null;
|
||||||
|
try {
|
||||||
|
ip = request.getHeader("x-forwarded-for");
|
||||||
|
if (StrUtil.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getHeader("Proxy-Client-IP");
|
||||||
|
}
|
||||||
|
if (StrUtil.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||||
|
}
|
||||||
|
if (StrUtil.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getHeader("HTTP_CLIENT_IP");
|
||||||
|
}
|
||||||
|
if (StrUtil.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
|
||||||
|
}
|
||||||
|
if (StrUtil.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getRemoteAddr();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("IP extraction error", e);
|
||||||
|
}
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.cool.core.util;
|
||||||
|
|
||||||
|
import cn.hutool.core.map.MapUtil;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class MapExtUtil extends MapUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 比较两个map key 和 value 是否一致
|
||||||
|
*/
|
||||||
|
public static boolean compareMaps(Map<String, Object> map1, Map<String, Object> map2) {
|
||||||
|
if (ObjectUtil.isEmpty(map1) || ObjectUtil.isEmpty(map2)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (map1.size() != map2.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (Map.Entry<String, Object> entry : map1.entrySet()) {
|
||||||
|
if (!map2.containsKey(entry.getKey())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!ObjectUtil.equal(entry.getValue(), map2.get(entry.getKey()))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.cool.core.util;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.file.PathUtil;
|
||||||
|
import com.cool.CoolApplication;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
public class PathUtils {
|
||||||
|
|
||||||
|
public static boolean isAbsolutePath(String pathStr) {
|
||||||
|
Path path = Paths.get(pathStr);
|
||||||
|
return path.isAbsolute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getUserDir() {
|
||||||
|
return System.getProperty("user.dir");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getModulesPath() {
|
||||||
|
return getUserDir() + "/src/main/java/" + CoolApplication.class.getPackageName()
|
||||||
|
.replace(".", "/") + "/modules";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 路径不存在创建
|
||||||
|
*/
|
||||||
|
public static void noExistsMk(String pathStr) {
|
||||||
|
Path path = Paths.get(pathStr);
|
||||||
|
if (PathUtil.exists(path, false)) {
|
||||||
|
PathUtil.mkParentDirs(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package com.cool.core.util;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.ApplicationContextAware;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring Context 工具类
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class SpringContextUtils implements ApplicationContextAware {
|
||||||
|
|
||||||
|
public static ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||||
|
SpringContextUtils.applicationContext = applicationContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object getBean(String name) {
|
||||||
|
return applicationContext.getBean(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T getBean(Class<T> requiredType) {
|
||||||
|
return applicationContext.getBean(requiredType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean containsBean(String name) {
|
||||||
|
return applicationContext.containsBean(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isSingleton(String name) {
|
||||||
|
return applicationContext.isSingleton(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Class<? extends Object> getType(String name) {
|
||||||
|
return applicationContext.getType(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Map<String, T> getBeansOfType(Class<T> requiredType) {
|
||||||
|
return applicationContext.getBeansOfType(requiredType);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package com.cool.modules.base.controller.admin;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Dict;
|
||||||
|
import com.cool.core.annotation.CoolRestController;
|
||||||
|
import com.cool.core.eps.CoolEps;
|
||||||
|
import com.cool.core.file.FileUploadStrategyFactory;
|
||||||
|
import com.cool.core.request.R;
|
||||||
|
import com.cool.modules.base.entity.sys.BaseSysUserEntity;
|
||||||
|
import com.cool.modules.base.service.sys.BaseSysLoginService;
|
||||||
|
import com.cool.modules.base.service.sys.BaseSysPermsService;
|
||||||
|
import com.cool.modules.base.service.sys.BaseSysUserService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestAttribute;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestPart;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统通用接口, 每个人都有权限操作
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Tag(name = "系统通用", description = "系统通用")
|
||||||
|
@CoolRestController()
|
||||||
|
public class AdminBaseCommController {
|
||||||
|
|
||||||
|
final private BaseSysPermsService baseSysPermsService;
|
||||||
|
|
||||||
|
final private BaseSysUserService baseSysUserService;
|
||||||
|
|
||||||
|
final private BaseSysLoginService baseSysLoginService;
|
||||||
|
|
||||||
|
final private CoolEps coolEps;
|
||||||
|
|
||||||
|
final private FileUploadStrategyFactory fileUploadStrategyFactory;
|
||||||
|
|
||||||
|
@Operation(summary = "实体信息与路径", description = "系统所有的实体信息与路径,供前端自动生成代码与服务")
|
||||||
|
@GetMapping("/eps")
|
||||||
|
public R eps() {
|
||||||
|
return R.ok(coolEps.getAdmin());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "个人信息")
|
||||||
|
@GetMapping("/person")
|
||||||
|
public R person(@RequestAttribute() Long adminUserId) {
|
||||||
|
BaseSysUserEntity baseSysUserEntity = baseSysUserService.getById(adminUserId);
|
||||||
|
baseSysUserEntity.setPassword(null);
|
||||||
|
baseSysUserEntity.setPasswordV(null);
|
||||||
|
return R.ok(baseSysUserEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "修改个人信息")
|
||||||
|
@PostMapping("/personUpdate")
|
||||||
|
public R personUpdate(@RequestAttribute Long adminUserId, @RequestBody Dict body) {
|
||||||
|
baseSysUserService.personUpdate(adminUserId, body);
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "权限与菜单")
|
||||||
|
@GetMapping("/permmenu")
|
||||||
|
public R permmenu(@RequestAttribute() Long adminUserId) {
|
||||||
|
return R.ok(baseSysPermsService.permmenu(adminUserId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "文件上传")
|
||||||
|
@PostMapping(value = "/upload", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE,
|
||||||
|
MediaType.ALL_VALUE})
|
||||||
|
public R upload(
|
||||||
|
@RequestPart(value = "file", required = false) @Parameter(description = "文件") MultipartFile[] files,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
return R.ok(fileUploadStrategyFactory.upload(files, request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "文件上传模式")
|
||||||
|
@GetMapping("/uploadMode")
|
||||||
|
public R uploadMode() {
|
||||||
|
return R.ok(fileUploadStrategyFactory.getMode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "退出")
|
||||||
|
@PostMapping("/logout")
|
||||||
|
public R logout(@RequestAttribute Long adminUserId, @RequestAttribute String adminUsername) {
|
||||||
|
baseSysLoginService.logout(adminUserId, adminUsername);
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "编程")
|
||||||
|
@GetMapping("/program")
|
||||||
|
public R program() {
|
||||||
|
return R.ok("Java");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package com.cool.modules.base.controller.admin;
|
||||||
|
|
||||||
|
import com.cool.core.annotation.CoolRestController;
|
||||||
|
import com.cool.core.eps.CoolEps;
|
||||||
|
import com.cool.core.file.FileUploadStrategyFactory;
|
||||||
|
import com.cool.core.request.R;
|
||||||
|
import com.cool.modules.base.dto.sys.BaseSysLoginDto;
|
||||||
|
import com.cool.modules.base.service.sys.BaseSysLoginService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统开放接口,无需权限校验
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Tag(name = "系统开放", description = "系统开放")
|
||||||
|
@CoolRestController()
|
||||||
|
public class AdminBaseOpenController {
|
||||||
|
|
||||||
|
final private BaseSysLoginService baseSysLoginService;
|
||||||
|
|
||||||
|
final private CoolEps coolEps;
|
||||||
|
|
||||||
|
final private FileUploadStrategyFactory fileUploadStrategyFactory;
|
||||||
|
|
||||||
|
@Operation(summary = "实体信息与路径", description = "系统所有的实体信息与路径,供前端自动生成代码与服务")
|
||||||
|
@GetMapping("/eps")
|
||||||
|
public R eps() {
|
||||||
|
return R.ok(coolEps.getAdmin());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获得网页内容的参数值")
|
||||||
|
@GetMapping("/html")
|
||||||
|
public R html() {
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "登录")
|
||||||
|
@PostMapping("/login")
|
||||||
|
public R login(@RequestBody BaseSysLoginDto baseSysLoginDto) {
|
||||||
|
return R.ok(baseSysLoginService.login(baseSysLoginDto));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "验证码")
|
||||||
|
@GetMapping("/captcha")
|
||||||
|
public R captcha(@Parameter(description = "类型:svg|base64") @RequestParam(defaultValue = "base64") String type,
|
||||||
|
@Parameter(description = "宽度") @RequestParam(defaultValue = "150") Integer width,
|
||||||
|
@Parameter(description = "高度") @RequestParam(defaultValue = "50") Integer height) {
|
||||||
|
return R.ok(baseSysLoginService.captcha(type, width, height));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "刷新token")
|
||||||
|
@GetMapping("/refreshToken")
|
||||||
|
public R refreshToken(String refreshToken) {
|
||||||
|
return R.ok(baseSysLoginService.refreshToken(refreshToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "文件上传")
|
||||||
|
@PostMapping(value = "/upload", consumes = { MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.ALL_VALUE })
|
||||||
|
public R upload(@RequestPart(value = "file", required = false) @Parameter(description = "文件") MultipartFile[] files,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
return R.ok(fileUploadStrategyFactory.upload(files, request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "文件上传模式")
|
||||||
|
@GetMapping("/uploadMode")
|
||||||
|
public R uploadMode() {
|
||||||
|
return R.ok(fileUploadStrategyFactory.getMode());
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
package com.cool.modules.base.controller.admin.sys;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.cool.core.annotation.CoolRestController;
|
||||||
|
import com.cool.core.base.BaseController;
|
||||||
|
import com.cool.core.request.R;
|
||||||
|
import com.cool.modules.base.entity.sys.BaseSysDepartmentEntity;
|
||||||
|
import com.cool.modules.base.service.sys.BaseSysDepartmentService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统部门
|
||||||
|
*/
|
||||||
|
@Tag(name = "系统部门", description = "系统部门")
|
||||||
|
@CoolRestController(api = { "add", "delete", "update", "list" })
|
||||||
|
public class AdminBaseSysDepartmentController
|
||||||
|
extends BaseController<BaseSysDepartmentService, BaseSysDepartmentEntity> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init(HttpServletRequest request, JSONObject requestParams) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "排序")
|
||||||
|
@PostMapping("/order")
|
||||||
|
public R order(@RequestBody List<BaseSysDepartmentEntity> list) {
|
||||||
|
this.service.order(list);
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
+57
@@ -0,0 +1,57 @@
|
|||||||
|
package com.cool.modules.base.controller.admin.sys;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.cool.core.annotation.CoolRestController;
|
||||||
|
import com.cool.core.base.BaseController;
|
||||||
|
import com.cool.core.request.R;
|
||||||
|
import com.cool.modules.base.entity.sys.BaseSysLogEntity;
|
||||||
|
import com.cool.modules.base.entity.sys.table.BaseSysLogEntityTableDef;
|
||||||
|
import com.cool.modules.base.entity.sys.table.BaseSysUserEntityTableDef;
|
||||||
|
import com.cool.modules.base.service.sys.BaseSysConfService;
|
||||||
|
import com.cool.modules.base.service.sys.BaseSysLogService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestAttribute;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统日志
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Tag(name = "系统日志", description = "系统日志")
|
||||||
|
@CoolRestController(api = {"page"})
|
||||||
|
public class AdminBaseSysLogController extends BaseController<BaseSysLogService, BaseSysLogEntity> {
|
||||||
|
|
||||||
|
private final BaseSysConfService baseSysConfService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init(HttpServletRequest request, JSONObject requestParams) {
|
||||||
|
setPageOption(
|
||||||
|
createOp()
|
||||||
|
.keyWordLikeFields(
|
||||||
|
BaseSysUserEntityTableDef.BASE_SYS_USER_ENTITY.NAME,
|
||||||
|
BaseSysLogEntityTableDef.BASE_SYS_LOG_ENTITY.PARAMS));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "清理日志")
|
||||||
|
@PostMapping("/clear")
|
||||||
|
public R clear() {
|
||||||
|
service.clear(true);
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "设置日志保存时间")
|
||||||
|
@PostMapping("/setKeep")
|
||||||
|
public R setKeep(@RequestAttribute JSONObject requestParams) {
|
||||||
|
baseSysConfService.updateValue("logKeep", requestParams.getStr("value"));
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获得日志报错时间")
|
||||||
|
@PostMapping("/getKeep")
|
||||||
|
public R getKeep() {
|
||||||
|
return R.ok(baseSysConfService.getValue("logKeep"));
|
||||||
|
}
|
||||||
|
}
|
||||||
+56
@@ -0,0 +1,56 @@
|
|||||||
|
package com.cool.modules.base.controller.admin.sys;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.cool.core.annotation.CoolRestController;
|
||||||
|
import com.cool.core.base.BaseController;
|
||||||
|
import com.cool.core.exception.CoolPreconditions;
|
||||||
|
import com.cool.core.request.R;
|
||||||
|
import com.cool.modules.base.entity.sys.BaseSysMenuEntity;
|
||||||
|
import com.cool.modules.base.service.sys.BaseSysMenuService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统菜单
|
||||||
|
*/
|
||||||
|
@Tag(name = "系统菜单", description = "系统菜单")
|
||||||
|
@CoolRestController(api = {"add", "delete", "update", "page", "list", "info"})
|
||||||
|
public class AdminBaseSysMenuController extends
|
||||||
|
BaseController<BaseSysMenuService, BaseSysMenuEntity> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init(HttpServletRequest request, JSONObject requestParams) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "创建代码", description = "创建代码")
|
||||||
|
@PostMapping("/create")
|
||||||
|
public R create(@RequestBody() Map<String, Object> params) {
|
||||||
|
CoolPreconditions.checkEmpty(params.get("module"), "module参数不能为空");
|
||||||
|
CoolPreconditions.checkEmpty(params.get("entity"), "entity参数不能为空");
|
||||||
|
CoolPreconditions.checkEmpty(params.get("controller"), "controller参数不能为空");
|
||||||
|
CoolPreconditions.checkEmpty(params.get("service"), "service参数不能为空");
|
||||||
|
CoolPreconditions.checkEmpty(params.get("service-impl"), "service-impl参数不能为空");
|
||||||
|
CoolPreconditions.checkEmpty(params.get("mapper"), "mapper参数不能为空");
|
||||||
|
CoolPreconditions.checkEmpty(params.get("fileName"), "fileName参数不能为空");
|
||||||
|
this.service.create(params);
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "导出", description = "导出")
|
||||||
|
@PostMapping("/export")
|
||||||
|
public R export(@RequestBody Map<String, Object> params) {
|
||||||
|
return R.ok(this.service.export(getIds(params)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "导入", description = "导入")
|
||||||
|
@PostMapping("/import")
|
||||||
|
public R importMenu(@RequestBody Map<String, List<BaseSysMenuEntity>> params) {
|
||||||
|
CoolPreconditions.checkEmpty(params.get("menus"), "参数不能为空");
|
||||||
|
return R.ok(this.service.importMenu(params.get("menus")));
|
||||||
|
}
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
package com.cool.modules.base.controller.admin.sys;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.cool.core.annotation.CoolRestController;
|
||||||
|
import com.cool.core.base.BaseController;
|
||||||
|
import com.cool.modules.base.entity.sys.BaseSysParamEntity;
|
||||||
|
import com.cool.modules.base.service.sys.BaseSysParamService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统参数配置
|
||||||
|
*/
|
||||||
|
@Tag(name = "系统参数配置", description = "系统参数配置")
|
||||||
|
@CoolRestController(api = { "add", "delete", "update", "page", "info" })
|
||||||
|
public class AdminBaseSysParamController extends BaseController<BaseSysParamService, BaseSysParamEntity> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init(HttpServletRequest request, JSONObject requestParams) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "根据键返回网页的参数值")
|
||||||
|
@GetMapping("/html")
|
||||||
|
public String html(String key) {
|
||||||
|
return service.htmlByKey(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
package com.cool.modules.base.controller.admin.sys;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.cool.core.annotation.CoolRestController;
|
||||||
|
import com.cool.core.base.BaseController;
|
||||||
|
import com.cool.modules.base.entity.sys.BaseSysRoleEntity;
|
||||||
|
import static com.cool.modules.base.entity.sys.table.BaseSysRoleEntityTableDef.BASE_SYS_ROLE_ENTITY;
|
||||||
|
import com.cool.modules.base.service.sys.BaseSysRoleService;
|
||||||
|
import com.mybatisflex.core.query.QueryWrapper;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
/**
|
||||||
|
* 系统角色
|
||||||
|
*/
|
||||||
|
@Tag(name = "系统角色", description = "系统角色")
|
||||||
|
@CoolRestController(api = { "add", "delete", "update", "page", "list", "info" })
|
||||||
|
public class AdminBaseSysRoleController extends BaseController<BaseSysRoleService, BaseSysRoleEntity> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init(HttpServletRequest request, JSONObject requestParams) {
|
||||||
|
JSONObject tokenInfo = requestParams.getJSONObject("tokenInfo");
|
||||||
|
boolean isAdmin = tokenInfo.getStr("username").equals("admin");
|
||||||
|
|
||||||
|
setPageOption(createOp().keyWordLikeFields(BASE_SYS_ROLE_ENTITY.NAME, BASE_SYS_ROLE_ENTITY.LABEL).queryWrapper(QueryWrapper.create().and(qw -> {
|
||||||
|
qw.eq(BASE_SYS_ROLE_ENTITY.USER_ID.getName(), tokenInfo.get("userId")).or(w -> {
|
||||||
|
w.in(BASE_SYS_ROLE_ENTITY.ID.getName(), tokenInfo.get("roleIds"));
|
||||||
|
});
|
||||||
|
}, !isAdmin).and(BASE_SYS_ROLE_ENTITY.LABEL.ne("admin"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
package com.cool.modules.base.controller.admin.sys;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.cool.core.annotation.CoolRestController;
|
||||||
|
import com.cool.core.base.BaseController;
|
||||||
|
import com.cool.core.request.R;
|
||||||
|
import com.cool.modules.base.entity.sys.BaseSysUserEntity;
|
||||||
|
import com.cool.modules.base.service.sys.BaseSysUserService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestAttribute;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统用户
|
||||||
|
*/
|
||||||
|
@Tag(name = "系统用户", description = "系统用户")
|
||||||
|
@CoolRestController(api = { "add", "delete", "update", "page", "info" })
|
||||||
|
public class AdminBaseSysUserController extends BaseController<BaseSysUserService, BaseSysUserEntity> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init(HttpServletRequest request, JSONObject requestParams) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "移动部门")
|
||||||
|
@PostMapping("/move")
|
||||||
|
public R move(@RequestAttribute JSONObject requestParams) {
|
||||||
|
service.move(requestParams.getLong("departmentId"), requestParams.get("userIds", Long[].class));
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.cool.modules.base.controller.app;
|
||||||
|
|
||||||
|
import com.cool.core.annotation.CoolRestController;
|
||||||
|
import com.cool.core.eps.CoolEps;
|
||||||
|
import com.cool.core.request.R;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* app通用接口
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Tag(name = "应用通用", description = "应用通用")
|
||||||
|
@CoolRestController
|
||||||
|
public class AppBaseCommController {
|
||||||
|
|
||||||
|
final private CoolEps coolEps;
|
||||||
|
|
||||||
|
@Operation(summary = "实体信息与路径", description = "系统所有的实体信息与路径,供前端自动生成代码与服务")
|
||||||
|
@GetMapping("/eps")
|
||||||
|
public R eps() {
|
||||||
|
return R.ok(coolEps.getApp());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.cool.modules.base.dto.sys;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "登录参数")
|
||||||
|
public class BaseSysLoginDto {
|
||||||
|
|
||||||
|
@Schema(description = "用户名")
|
||||||
|
@NotBlank
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@Schema(description = "密码")
|
||||||
|
@NotBlank
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
@Schema(description = "验证码ID")
|
||||||
|
@NotBlank
|
||||||
|
private String captchaId;
|
||||||
|
|
||||||
|
@Schema(description = "验证码")
|
||||||
|
@NotBlank
|
||||||
|
private String verifyCode;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.cool.modules.base.entity.sys;
|
||||||
|
|
||||||
|
import com.cool.core.base.BaseEntity;
|
||||||
|
import com.tangzc.autotable.annotation.enums.IndexTypeEnum;
|
||||||
|
|
||||||
|
import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine;
|
||||||
|
import com.mybatisflex.annotation.Table;
|
||||||
|
import com.tangzc.autotable.annotation.Index;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统配置
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Table(value = "base_sys_conf", comment = "系统配置表")
|
||||||
|
public class BaseSysConfEntity extends BaseEntity<BaseSysConfEntity> {
|
||||||
|
@Index(type = IndexTypeEnum.UNIQUE)
|
||||||
|
@ColumnDefine(comment = "配置键", notNull = true)
|
||||||
|
private String cKey;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "值", notNull = true, type = "text")
|
||||||
|
private String cValue;
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.cool.modules.base.entity.sys;
|
||||||
|
|
||||||
|
import com.cool.core.base.BaseEntity;
|
||||||
|
import com.mybatisflex.annotation.Column;
|
||||||
|
|
||||||
|
import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine;
|
||||||
|
import com.mybatisflex.annotation.Table;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统部门
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Table(value = "base_sys_department", comment = "系统部门")
|
||||||
|
public class BaseSysDepartmentEntity extends BaseEntity<BaseSysDepartmentEntity> {
|
||||||
|
@ColumnDefine(comment = "部门名称", notNull = true)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "上级部门ID", type = "bigint")
|
||||||
|
private Long parentId;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "排序", defaultValue = "0")
|
||||||
|
private Integer orderNum;
|
||||||
|
|
||||||
|
// 父菜单名称
|
||||||
|
@Column(ignore = true)
|
||||||
|
private String parentName;
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.cool.modules.base.entity.sys;
|
||||||
|
|
||||||
|
import com.cool.core.base.BaseEntity;
|
||||||
|
import com.mybatisflex.annotation.Column;
|
||||||
|
import com.mybatisflex.core.handler.Fastjson2TypeHandler;
|
||||||
|
import com.tangzc.autotable.annotation.Index;
|
||||||
|
import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine;
|
||||||
|
import com.mybatisflex.annotation.Table;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Table(value = "base_sys_log", comment = "系统日志表")
|
||||||
|
public class BaseSysLogEntity extends BaseEntity<BaseSysLogEntity> {
|
||||||
|
|
||||||
|
@Index
|
||||||
|
@ColumnDefine(comment = "用户ID", type = "bigint")
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "行为", length = 1000)
|
||||||
|
private String action;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "IP", length = 50)
|
||||||
|
private String ip;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "参数", type = "json")
|
||||||
|
@Column(typeHandler = Fastjson2TypeHandler.class)
|
||||||
|
private Object params;
|
||||||
|
|
||||||
|
// 用户名称
|
||||||
|
@Column(ignore = true)
|
||||||
|
private String name;
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package com.cool.modules.base.entity.sys;
|
||||||
|
|
||||||
|
import com.cool.core.base.BaseEntity;
|
||||||
|
import com.mybatisflex.annotation.Column;
|
||||||
|
import com.tangzc.autotable.annotation.Index;
|
||||||
|
|
||||||
|
import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine;
|
||||||
|
import com.mybatisflex.annotation.Table;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Table(value = "base_sys_menu", comment = "系统菜单表")
|
||||||
|
public class BaseSysMenuEntity extends BaseEntity<BaseSysMenuEntity> {
|
||||||
|
@Index
|
||||||
|
@ColumnDefine(comment = "父菜单ID", type = "bigint")
|
||||||
|
private Long parentId;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "菜单名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "权限")
|
||||||
|
private String perms;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "类型 0:目录 1:菜单 2:按钮", type = "tinyint", defaultValue = "0")
|
||||||
|
private Integer type;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "图标")
|
||||||
|
private String icon;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "排序", defaultValue = "0")
|
||||||
|
private Integer orderNum;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "菜单地址")
|
||||||
|
private String router;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "视图地址")
|
||||||
|
private String viewPath;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "路由缓存", defaultValue = "true")
|
||||||
|
private Boolean keepAlive;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "是否显示", defaultValue = "true")
|
||||||
|
private Boolean isShow;
|
||||||
|
|
||||||
|
@Column(ignore = true)
|
||||||
|
private String parentName;
|
||||||
|
|
||||||
|
@Column(ignore = true)
|
||||||
|
private List<BaseSysMenuEntity> childMenus;
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.cool.modules.base.entity.sys;
|
||||||
|
|
||||||
|
import com.cool.core.base.BaseEntity;
|
||||||
|
import com.tangzc.autotable.annotation.Index;
|
||||||
|
|
||||||
|
import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine;
|
||||||
|
import com.mybatisflex.annotation.Table;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Table(value = "base_sys_param", comment = "系统参数配置")
|
||||||
|
public class BaseSysParamEntity extends BaseEntity<BaseSysParamEntity> {
|
||||||
|
@Index
|
||||||
|
@ColumnDefine(comment = "键", notNull = true)
|
||||||
|
private String keyName;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "数据", type = "text")
|
||||||
|
private String data;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "数据类型 0:字符串 1:数组 2:键值对", defaultValue = "0", type = "tinyint")
|
||||||
|
private Integer dataType;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "备注")
|
||||||
|
private String remark;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.cool.modules.base.entity.sys;
|
||||||
|
|
||||||
|
import com.cool.core.base.BaseEntity;
|
||||||
|
|
||||||
|
import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine;
|
||||||
|
import com.mybatisflex.annotation.Table;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Table(value = "base_sys_role_department", comment = "系统角色部门")
|
||||||
|
public class BaseSysRoleDepartmentEntity extends BaseEntity<BaseSysRoleDepartmentEntity> {
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "角色ID", type = "bigint")
|
||||||
|
private Long roleId;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "部门ID", type = "bigint")
|
||||||
|
private Long departmentId;
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.cool.modules.base.entity.sys;
|
||||||
|
|
||||||
|
import com.cool.core.base.BaseEntity;
|
||||||
|
import com.mybatisflex.annotation.Column;
|
||||||
|
import com.mybatisflex.annotation.Table;
|
||||||
|
import com.mybatisflex.core.handler.Fastjson2TypeHandler;
|
||||||
|
import com.tangzc.autotable.annotation.Index;
|
||||||
|
import com.tangzc.autotable.annotation.enums.IndexTypeEnum;
|
||||||
|
import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Table(value = "base_sys_role", comment = "系统角色表")
|
||||||
|
public class BaseSysRoleEntity extends BaseEntity<BaseSysRoleEntity> {
|
||||||
|
|
||||||
|
@Index
|
||||||
|
@ColumnDefine(comment = "用户ID", notNull = true, type = "bigint")
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "名称", notNull = true)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Index(type = IndexTypeEnum.UNIQUE)
|
||||||
|
@ColumnDefine(comment = "角色标签", notNull = true)
|
||||||
|
private String label;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "备注")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "数据权限是否关联上下级", defaultValue = "1")
|
||||||
|
private Integer relevance;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "菜单权限", type = "json")
|
||||||
|
@Column(typeHandler = Fastjson2TypeHandler.class)
|
||||||
|
private List<Long> menuIdList;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "部门权限", type = "json")
|
||||||
|
@Column(typeHandler = Fastjson2TypeHandler.class)
|
||||||
|
private List<Long> departmentIdList;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.cool.modules.base.entity.sys;
|
||||||
|
|
||||||
|
import com.cool.core.base.BaseEntity;
|
||||||
|
|
||||||
|
import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine;
|
||||||
|
import com.mybatisflex.annotation.Table;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Table(value = "base_sys_role_menu", comment = "系统角色菜单表")
|
||||||
|
public class BaseSysRoleMenuEntity extends BaseEntity<BaseSysRoleMenuEntity> {
|
||||||
|
@ColumnDefine(comment = "菜单", type = "bigint")
|
||||||
|
private Long menuId;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "角色ID", type = "bigint")
|
||||||
|
private Long roleId;
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package com.cool.modules.base.entity.sys;
|
||||||
|
|
||||||
|
import com.cool.core.base.BaseEntity;
|
||||||
|
import com.mybatisflex.annotation.Column;
|
||||||
|
import com.tangzc.autotable.annotation.Index;
|
||||||
|
import com.tangzc.autotable.annotation.enums.IndexTypeEnum;
|
||||||
|
|
||||||
|
import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine;
|
||||||
|
import com.mybatisflex.annotation.Table;
|
||||||
|
import com.tangzc.autotable.annotation.Index;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Table(value = "base_sys_user", comment = "系统用户表")
|
||||||
|
public class BaseSysUserEntity extends BaseEntity<BaseSysUserEntity> {
|
||||||
|
@Index
|
||||||
|
@ColumnDefine(comment = "部门ID", type = "bigint")
|
||||||
|
private Long departmentId;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "姓名")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Index(type = IndexTypeEnum.UNIQUE)
|
||||||
|
@ColumnDefine(comment = "用户名", length = 100, notNull = true)
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "密码", notNull = true)
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "密码版本", defaultValue = "1")
|
||||||
|
private Integer passwordV;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "昵称", notNull = true)
|
||||||
|
private String nickName;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "头像")
|
||||||
|
private String headImg;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "手机号")
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "邮箱")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "备注")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "状态 0:禁用 1:启用", defaultValue = "1", type = "tinyint")
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
// 部门名称
|
||||||
|
@Column(ignore = true)
|
||||||
|
private String departmentName;
|
||||||
|
|
||||||
|
// 角色名称
|
||||||
|
@Column(ignore = true)
|
||||||
|
private String roleName;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "socketId")
|
||||||
|
private String socketId;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.cool.modules.base.entity.sys;
|
||||||
|
|
||||||
|
import com.cool.core.base.BaseEntity;
|
||||||
|
|
||||||
|
import com.tangzc.mybatisflex.autotable.annotation.ColumnDefine;
|
||||||
|
import com.mybatisflex.annotation.Table;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Table(value = "base_sys_user_role", comment = "系统用户角色表")
|
||||||
|
public class BaseSysUserRoleEntity extends BaseEntity<BaseSysUserRoleEntity> {
|
||||||
|
@ColumnDefine(comment = "用户ID", type = "bigint")
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
@ColumnDefine(comment = "角色ID", type = "bigint")
|
||||||
|
private Long roleId;
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.cool.modules.base.filter;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.cool.modules.base.service.sys.BaseSysLogService;
|
||||||
|
import jakarta.servlet.Filter;
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.ServletRequest;
|
||||||
|
import jakarta.servlet.ServletResponse;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import java.io.IOException;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Order(10)
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class BaseLogFilter implements Filter {
|
||||||
|
|
||||||
|
final private BaseSysLogService baseSysLogService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
|
||||||
|
FilterChain filterChain)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
// 记录日志
|
||||||
|
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||||
|
baseSysLogService.record(request, (JSONObject) request.getAttribute("requestParams"));
|
||||||
|
filterChain.doFilter(servletRequest, servletResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.cool.modules.base.mapper.sys;
|
||||||
|
|
||||||
|
import com.cool.modules.base.entity.sys.BaseSysConfEntity;
|
||||||
|
import com.mybatisflex.core.BaseMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统配置
|
||||||
|
*/
|
||||||
|
public interface BaseSysConfMapper extends BaseMapper<BaseSysConfEntity> {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.cool.modules.base.mapper.sys;
|
||||||
|
|
||||||
|
import com.mybatisflex.core.BaseMapper;
|
||||||
|
import com.cool.modules.base.entity.sys.BaseSysDepartmentEntity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统部门
|
||||||
|
*/
|
||||||
|
public interface BaseSysDepartmentMapper extends BaseMapper<BaseSysDepartmentEntity> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.cool.modules.base.mapper.sys;
|
||||||
|
|
||||||
|
import com.mybatisflex.core.BaseMapper;
|
||||||
|
import com.cool.modules.base.entity.sys.BaseSysLogEntity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统日志
|
||||||
|
*/
|
||||||
|
public interface BaseSysLogMapper extends BaseMapper<BaseSysLogEntity> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.cool.modules.base.mapper.sys;
|
||||||
|
|
||||||
|
import com.mybatisflex.core.BaseMapper;
|
||||||
|
import com.cool.modules.base.entity.sys.BaseSysMenuEntity;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统菜单
|
||||||
|
*/
|
||||||
|
public interface BaseSysMenuMapper extends BaseMapper<BaseSysMenuEntity> {
|
||||||
|
/**
|
||||||
|
* 根据角色ID获得所有菜单
|
||||||
|
*
|
||||||
|
* @param roleIds 角色ID集合
|
||||||
|
* @return SysMenuEntity
|
||||||
|
*/
|
||||||
|
List<BaseSysMenuEntity> getMenus(@Param("roleIds") Long[] roleIds);
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.cool.modules.base.mapper.sys;
|
||||||
|
|
||||||
|
import com.mybatisflex.core.BaseMapper;
|
||||||
|
import com.cool.modules.base.entity.sys.BaseSysParamEntity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统参数配置
|
||||||
|
*/
|
||||||
|
public interface BaseSysParamMapper extends BaseMapper<BaseSysParamEntity> {
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user