Commit 12c220d7 authored by 林洋洋's avatar 林洋洋

代码调整

parent 581a2e6e
...@@ -13,7 +13,7 @@ USE `pig`; ...@@ -13,7 +13,7 @@ USE `pig`;
-- ---------------------------- -- ----------------------------
DROP TABLE IF EXISTS `sys_dept`; DROP TABLE IF EXISTS `sys_dept`;
CREATE TABLE `sys_dept` ( CREATE TABLE `sys_dept` (
`dept_id` bigint NOT NULL COMMENT '部门ID', `dept_id` bigserial NOT NULL COMMENT '部门ID',
`name` varchar(50) DEFAULT NULL COMMENT '部门名称', `name` varchar(50) DEFAULT NULL COMMENT '部门名称',
`sort_order` int NOT NULL DEFAULT '0' COMMENT '排序', `sort_order` int NOT NULL DEFAULT '0' COMMENT '排序',
`create_by` varchar(64) NOT NULL DEFAULT ' ' COMMENT '创建人', `create_by` varchar(64) NOT NULL DEFAULT ' ' COMMENT '创建人',
......
...@@ -42,18 +42,6 @@ ...@@ -42,18 +42,6 @@
<artifactId>pig-upms-biz</artifactId> <artifactId>pig-upms-biz</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!--可选:代码生成模块-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-codegen</artifactId>
<version>${revision}</version>
</dependency>
<!--可选:定时任务模块-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-quartz</artifactId>
<version>${revision}</version>
</dependency>
<!--安全模块--> <!--安全模块-->
<dependency> <dependency>
<groupId>com.pig4cloud</groupId> <groupId>com.pig4cloud</groupId>
......
...@@ -3,13 +3,16 @@ spring: ...@@ -3,13 +3,16 @@ spring:
type: redis # 缓存类型 Redis type: redis # 缓存类型 Redis
data: data:
redis: redis:
host: 127.0.0.1 # Redis地址 database: 5
host: 81.70.183.25
port: 16379
password: '123qwe!@#'
# 数据库相关配置 # 数据库相关配置
datasource: datasource:
driver-class-name: com.mysql.cj.jdbc.Driver driver-class-name: org.postgresql.Driver
username: root username: postgres
password: root password: postgres123
url: jdbc:mysql://127.0.0.1:3306/pig?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&nullCatalogMeansCurrent=true url: jdbc:postgresql://81.70.183.25:25432/ask_data_ai_db
# 本地文件系统 # 本地文件系统
file: file:
......
server: server:
port: 9999 # 项目端口 port: 19999 # 项目端口
servlet: servlet:
context-path: /admin # 项目访问路径 context-path: /admin # 项目访问路径
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
<properties> <properties>
<!-- 项目版本号 --> <!-- 项目版本号 -->
<revision>3.9.0</revision> <revision>3.9.0</revision>
<postgresql.version>42.6.2</postgresql.version>
<spring-boot.version>3.5.0</spring-boot.version> <spring-boot.version>3.5.0</spring-boot.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source> <maven.compiler.source>17</maven.compiler.source>
...@@ -25,7 +26,6 @@ ...@@ -25,7 +26,6 @@
<springdoc.version>2.8.8</springdoc.version> <springdoc.version>2.8.8</springdoc.version>
<swagger.core.version>2.2.32</swagger.core.version> <swagger.core.version>2.2.32</swagger.core.version>
<mybatis-plus.version>3.5.12</mybatis-plus.version> <mybatis-plus.version>3.5.12</mybatis-plus.version>
<mysql.version>9.2.0</mysql.version>
<dynamic-ds.version>4.3.1</dynamic-ds.version> <dynamic-ds.version>4.3.1</dynamic-ds.version>
<seata.version>1.7.0</seata.version> <seata.version>1.7.0</seata.version>
<excel.version>3.4.2</excel.version> <excel.version>3.4.2</excel.version>
...@@ -99,9 +99,9 @@ ...@@ -99,9 +99,9 @@
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.mysql</groupId> <groupId>org.postgresql</groupId>
<artifactId>mysql-connector-j</artifactId> <artifactId>postgresql</artifactId>
<version>${mysql.version}</version> <version>${postgresql.version}</version>
</dependency> </dependency>
<!--springdoc --> <!--springdoc -->
<dependency> <dependency>
......
...@@ -38,5 +38,9 @@ ...@@ -38,5 +38,9 @@
<groupId>jakarta.servlet</groupId> <groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId> <artifactId>jakarta.servlet-api</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>
FROM registry.cn-hangzhou.aliyuncs.com/dockerhub_mirror/java:21-anolis
WORKDIR /pig-gateway
ARG JAR_FILE=target/pig-gateway.jar
COPY ${JAR_FILE} app.jar
EXPOSE 9999
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms128m -Xmx256m -Djava.security.egd=file:/dev/./urandom"
CMD sleep 60; java $JAVA_OPTS -jar app.jar
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig</artifactId>
<version>${revision}</version>
</parent>
<artifactId>pig-gateway</artifactId>
<packaging>jar</packaging>
<description>pig 服务网关,基于 spring cloud gateway</description>
<dependencies>
<!--gateway 网关依赖,内置webflux 依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway-server-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<!--注册中心客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--配置中心客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- LB 扩展 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!--caffeine 替换LB 默认缓存实现-->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<!-- 工具包依赖 -->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common-core</artifactId>
</dependency>
<!--接口文档-->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webflux-ui</artifactId>
</dependency>
<!--引入Knife4j的官方ui包-->
<dependency>
<groupId>io.springboot</groupId>
<artifactId>knife4j-openapi3-ui</artifactId>
<version>${knife4j.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-crypto</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
/*
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.pig4cloud.pig.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* 网关应用
*
* @author lengleng
* @date 2025/05/30
*/
@EnableDiscoveryClient
@SpringBootApplication
public class PigGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(PigGatewayApplication.class, args);
}
}
package com.pig4cloud.pig.gateway.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.pig4cloud.pig.gateway.filter.PigRequestGlobalFilter;
import com.pig4cloud.pig.gateway.handler.GlobalExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 网关配置类
*
* @author lengleng
* @date 2025/05/30
*/
@Configuration(proxyBeanMethods = false)
public class GatewayConfiguration {
/**
* 创建PigRequest全局过滤器
* @return PigRequest全局过滤器
*/
@Bean
public PigRequestGlobalFilter pigRequestGlobalFilter() {
return new PigRequestGlobalFilter();
}
/**
* 创建全局异常处理程序
* @param objectMapper 对象映射器
* @return 全局异常处理程序
*/
@Bean
public GlobalExceptionHandler globalExceptionHandler(ObjectMapper objectMapper) {
return new GlobalExceptionHandler(objectMapper);
}
}
/*
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.pig4cloud.pig.gateway.config;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;
import java.util.Objects;
/**
* 路由限流配置类
*
* @author lengleng
* @date 2019/2/1
*/
@Configuration(proxyBeanMethods = false)
public class RateLimiterConfiguration {
/**
* 创建基于远程地址的KeyResolver实例
* @return 根据请求的远程地址生成限流key的KeyResolver
* @see <a href=
* "https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-requestratelimiter-gatewayfilter-factory">Spring
* Cloud Gateway文档</a>
*/
@Bean
public KeyResolver remoteAddrKeyResolver() {
return exchange -> Mono
.just(Objects.requireNonNull(Objects.requireNonNull(exchange.getRequest().getRemoteAddress()))
.getAddress()
.getHostAddress());
}
}
package com.pig4cloud.pig.gateway.config;
import com.alibaba.nacos.client.naming.event.InstancesChangeEvent;
import com.alibaba.nacos.common.notify.Event;
import com.alibaba.nacos.common.notify.NotifyCenter;
import com.alibaba.nacos.common.notify.listener.Subscriber;
import com.alibaba.nacos.common.utils.StringUtils;
import lombok.RequiredArgsConstructor;
import org.springdoc.core.properties.AbstractSwaggerUiConfigProperties;
import org.springdoc.core.properties.SwaggerUiConfigProperties;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.annotation.Configuration;
import java.util.Set;
import java.util.stream.Collectors;
/**
* SpringDoc配置类,实现InitializingBean接口,用于Swagger 3.0文档展示
*
* @author lengleng
* @date 2025/05/30
*/
@RequiredArgsConstructor
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = "springdoc.api-docs.enabled", matchIfMissing = true)
public class SpringDocConfiguration implements InitializingBean {
private final SwaggerUiConfigProperties swaggerUiConfigProperties;
private final DiscoveryClient discoveryClient;
/**
* 在初始化后调用的方法,用于注册SwaggerDocRegister订阅器
*/
@Override
public void afterPropertiesSet() {
NotifyCenter.registerSubscriber(new SwaggerDocRegister(swaggerUiConfigProperties, discoveryClient));
}
}
/**
* Swagger文档注册器,用于处理服务实例变更事件并更新Swagger UI配置
*
* @author lengleng
* @date 2025/05/30
*/
@RequiredArgsConstructor
class SwaggerDocRegister extends Subscriber<InstancesChangeEvent> {
private final SwaggerUiConfigProperties swaggerUiConfigProperties;
private final DiscoveryClient discoveryClient;
/**
* 处理服务实例变更事件
* @param event 服务实例变更事件对象
*/
@Override
public void onEvent(InstancesChangeEvent event) {
Set<AbstractSwaggerUiConfigProperties.SwaggerUrl> swaggerUrlSet = discoveryClient.getServices()
.stream()
.flatMap(serviceId -> discoveryClient.getInstances(serviceId).stream())
.filter(instance -> StringUtils.isNotBlank(instance.getMetadata().get("spring-doc")))
.map(instance -> {
AbstractSwaggerUiConfigProperties.SwaggerUrl swaggerUrl = new AbstractSwaggerUiConfigProperties.SwaggerUrl();
swaggerUrl.setName(instance.getServiceId());
swaggerUrl.setUrl(String.format("/%s/v3/api-docs", instance.getMetadata().get("spring-doc")));
return swaggerUrl;
})
.collect(Collectors.toSet());
swaggerUiConfigProperties.setUrls(swaggerUrlSet);
}
/**
* 订阅类型方法,返回订阅的事件类型
* @return 订阅的事件类型
*/
@Override
public Class<? extends Event> subscribeType() {
return InstancesChangeEvent.class;
}
}
/*
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.pig4cloud.pig.gateway.filter;
import com.pig4cloud.pig.common.core.constant.CommonConstants;
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.Collections;
import java.util.stream.Collectors;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.addOriginalRequestUrl;
/**
* 全局拦截器,作用于所有微服务
* <p>
* 1. 清洗请求头中的from参数 2. 重写StripPrefix = 1,支持全局路由
*
* @author lengleng
* @date 2025/05/30
*/
public class PigRequestGlobalFilter implements GlobalFilter, Ordered {
/**
* 处理Web请求并(可选地)通过给定的网关过滤器链委托给下一个过滤器
* @param exchange 当前服务器交换对象
* @param chain 提供委托给下一个过滤器的方式
* @return {@code Mono<Void>} 表示请求处理完成
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1. 清洗请求头中from 参数
ServerHttpRequest request = exchange.getRequest().mutate().headers(httpHeaders -> {
httpHeaders.remove(SecurityConstants.FROM);
// 设置请求时间
httpHeaders.put(CommonConstants.REQUEST_START_TIME,
Collections.singletonList(String.valueOf(System.currentTimeMillis())));
}).build();
// 2. 重写StripPrefix
addOriginalRequestUrl(exchange, request.getURI());
String rawPath = request.getURI().getRawPath();
String newPath = "/" + Arrays.stream(StringUtils.tokenizeToStringArray(rawPath, "/"))
.skip(1L)
.collect(Collectors.joining("/"));
ServerHttpRequest newRequest = request.mutate().path(newPath).build();
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, newRequest.getURI());
return chain.filter(exchange.mutate().request(newRequest.mutate().build()).build());
}
@Override
public int getOrder() {
return 10;
}
}
/*
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.pig4cloud.pig.gateway.handler;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.pig4cloud.pig.common.core.util.R;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* 网关异常通用处理器,作用于WebFlux环境,优先级低于ResponseStatusExceptionHandler
*
* @author lengleng
* @date 2025/05/30
*/
@Slf4j
@Order(-1)
@RequiredArgsConstructor
public class GlobalExceptionHandler implements ErrorWebExceptionHandler {
/**
* 对象映射器,用于JSON序列化与反序列化
*/
private final ObjectMapper objectMapper;
/**
* @param exchange 服务器网络交换对象
* @param ex 抛出的异常
* @return Mono<Void> 异步处理结果
*/
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
ServerHttpResponse response = exchange.getResponse();
if (response.isCommitted()) {
return Mono.error(ex);
}
// header set
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
if (ex instanceof ResponseStatusException) {
response.setStatusCode(((ResponseStatusException) ex).getStatusCode());
}
return response.writeWith(Mono.fromSupplier(() -> {
DataBufferFactory bufferFactory = response.bufferFactory();
try {
log.debug("Error Spring Cloud Gateway : {} {}", exchange.getRequest().getPath(), ex.getMessage());
return bufferFactory.wrap(objectMapper.writeValueAsBytes(R.failed(ex.getMessage())));
}
catch (JsonProcessingException e) {
log.error("Error writing response", ex);
return bufferFactory.wrap(new byte[0]);
}
}));
}
}
server:
port: 9999
spring:
application:
name: @artifactId@
cloud:
nacos:
username: @nacos.username@
password: @nacos.password@
discovery:
server-addr: ${NACOS_HOST:127.0.0.1}:${NACOS_PORT:8848}
watch:
enabled: true
watch-delay: 1000
config:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
config:
import:
- optional:nacos:application-@profiles.active@.yml
- optional:nacos:${spring.application.name}-@profiles.active@.yml
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<configuration debug="false" scan="false">
<springProperty scop="context" name="spring.application.name" source="spring.application.name" defaultValue=""/>
<property name="log.path" value="logs/${spring.application.name}"/>
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<property name="FILE_LOG_PATTERN" value=":%d{yyyy-MM-dd HH:mm:ss.SSS} %5p %t %c{1}: %m%n"/>
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" class="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
class="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
class="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- Console log output -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- Log file debug output -->
<appender name="debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/debug.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/%d{yyyy-MM, aux}/debug.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- Log file error output -->
<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/%d{yyyy-MM}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
</appender>
<!--nacos 心跳 INFO 屏蔽-->
<logger name="com.alibaba.nacos" level="OFF">
<appender-ref ref="error"/>
</logger>
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
<root level="INFO">
<appender-ref ref="console"/>
<appender-ref ref="debug"/>
<appender-ref ref="error"/>
</root>
</configuration>
FROM registry.cn-hangzhou.aliyuncs.com/dockerhub_mirror/java:21-anolis
WORKDIR /pig-register
ARG JAR_FILE=target/pig-register.jar
COPY ${JAR_FILE} app.jar
EXPOSE 8848 9848 8080
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms128m -Xmx256m -Djava.security.egd=file:/dev/./urandom"
CMD sleep 30; java $JAVA_OPTS -jar app.jar
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 1999-2018 Alibaba Group Holding Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig</artifactId>
<version>${revision}</version>
</parent>
<artifactId>pig-register</artifactId>
<packaging>jar</packaging>
<name>pig-register</name>
<description>nacos 注册配置中心</description>
<properties>
<nacos.version>3.0.0</nacos.version>
</properties>
<dependencies>
<dependency>
<groupId>io.github.pig-mesh.nacos</groupId>
<artifactId>nacos-console</artifactId>
<version>${nacos.version}</version>
</dependency>
<dependency>
<groupId>io.github.pig-mesh.nacos</groupId>
<artifactId>nacos-server</artifactId>
<version>${nacos.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<excludes>
<exclude>**/*.woff</exclude>
<exclude>**/*.woff2</exclude>
<exclude>**/*.ttf</exclude>
<exclude>**/*.eot</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<includes>
<include>**/*.woff</include>
<include>**/*.woff2</include>
<include>**/*.ttf</include>
<include>**/*.eot</include>
</includes>
</resource>
</resources>
</build>
</project>
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.bootstrap;
import com.alibaba.nacos.NacosServerBasicApplication;
import com.alibaba.nacos.NacosServerWebApplication;
import com.alibaba.nacos.console.NacosConsole;
import com.alibaba.nacos.core.listener.startup.NacosStartUp;
import com.alibaba.nacos.core.listener.startup.NacosStartUpManager;
import com.alibaba.nacos.sys.env.Constants;
import com.alibaba.nacos.sys.env.DeploymentType;
import com.alibaba.nacos.sys.env.EnvUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import static org.springframework.boot.context.logging.LoggingApplicationListener.CONFIG_PROPERTY;
import static org.springframework.core.io.ResourceLoader.CLASSPATH_URL_PREFIX;
/**
* @author nacos
* <p>
* nacos console 源码运行,方便开发 生产从官网下载zip最新版集群配置运行
*/
@Slf4j
public class PigNacosApplication {
/**
* 独立模式系统属性名称
*/
private static String STANDALONE_MODE = "nacos.standalone";
public static void main(String[] args) {
System.setProperty(STANDALONE_MODE, "true");
System.setProperty(CONFIG_PROPERTY, CLASSPATH_URL_PREFIX + "logback-spring.xml");
String type = System.getProperty(Constants.NACOS_DEPLOYMENT_TYPE, Constants.NACOS_DEPLOYMENT_TYPE_MERGED);
DeploymentType deploymentType = DeploymentType.getType(type);
EnvUtil.setDeploymentType(deploymentType);
// Start Core Context
NacosStartUpManager.start(NacosStartUp.CORE_START_UP_PHASE);
ConfigurableApplicationContext coreContext = new SpringApplicationBuilder(NacosServerBasicApplication.class)
.web(WebApplicationType.NONE)
.run(args);
// Start Server Web Context
NacosStartUpManager.start(NacosStartUp.WEB_START_UP_PHASE);
ConfigurableApplicationContext serverWebContext = new SpringApplicationBuilder(NacosServerWebApplication.class)
.parent(coreContext)
.run(args);
// Start Console Context
NacosStartUpManager.start(NacosStartUp.CONSOLE_START_UP_PHASE);
ConfigurableApplicationContext consoleContext = new SpringApplicationBuilder(NacosConsole.class)
.parent(coreContext)
.run(args);
}
}
# Nacos \u63A7\u5236\u53F0\u7AEF\u53E3\uFF0C\u8BF7\u6CE8\u610F\u8BBF\u95EE\u7684 IP:8080
nacos.console.port=8080
# Nacos \u670D\u52A1\u7AEF\u4E3B\u7AEF\u53E3
nacos.server.main.port=8848
# \u6570\u636E\u5E93\u8BBE\u7F6E
spring.sql.init.platform=mysql
db.num=1
db.url.0=jdbc:mysql://${MYSQL_HOST:pig-mysql}:${MYSQL_PORT:3306}/${MYSQL_DB:pig_config}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true
db.user=root
db.password=root
# nacos \u4F1A\u81EA\u52A8\u88C5\u914D\u6570\u636E\u6E90\uFF0C\u6240\u4EE5\u6392\u9664 spring.datasource \u81EA\u52A8\u914D\u7F6E
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
#*************** \u76D1\u63A7\u76F8\u5173\u914D\u7F6E ***************#
# Prometheus \u76D1\u63A7\u914D\u7F6E
#management.endpoints.web.exposure.include=prometheus
# ElasticSearch \u76D1\u63A7\u914D\u7F6E - \u5DF2\u7981\u7528
management.elastic.metrics.export.enabled=false
# InfluxDB \u76D1\u63A7\u914D\u7F6E - \u5DF2\u7981\u7528
management.influx.metrics.export.enabled=false
#*************** \u914D\u7F6E\u4E2D\u5FC3\u76F8\u5173 ***************#
# Nacos \u914D\u7F6E\u63A8\u9001\u6700\u5927\u91CD\u8BD5\u6B21\u6570
nacos.config.push.maxRetryTime=50
#*************** \u547D\u540D\u670D\u52A1\u76F8\u5173 ***************#
# \u7A7A\u670D\u52A1\u81EA\u52A8\u6E05\u7406
nacos.naming.empty-service.auto-clean=true
nacos.naming.empty-service.clean.initial-delay-ms=50000
nacos.naming.empty-service.clean.period-time-ms=30000
#*************** Nacos Web \u76F8\u5173\u914D\u7F6E ***************#
# Nacos \u670D\u52A1\u7AEF\u4E0A\u4E0B\u6587\u8DEF\u5F84
nacos.server.contextPath=/nacos
#*************** \u65E5\u5FD7\u76F8\u5173\u914D\u7F6E ***************#
# \u5F00\u542F\u8BBF\u95EE\u65E5\u5FD7
server.tomcat.accesslog.enabled=true
# \u8BBF\u95EE\u65E5\u5FD7\u4FDD\u7559\u5929\u6570\u914D\u7F6E
server.tomcat.accesslog.max-days=30
# \u8BBF\u95EE\u65E5\u5FD7\u683C\u5F0F
server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{User-Agent}i %{Request-Source}i
# \u65E5\u5FD7\u57FA\u7840\u76EE\u5F55
server.tomcat.basedir=file:.
#*************** API \u9519\u8BEF\u5904\u7406 ***************#
# \u9519\u8BEF\u4FE1\u606F\u663E\u793A\u8BBE\u7F6E
server.error.include-message=ALWAYS
#*************** Nacos \u63A7\u5236\u53F0\u914D\u7F6E ***************#
# Nacos \u63A7\u5236\u53F0\u4E0A\u4E0B\u6587\u8DEF\u5F84
nacos.console.contextPath=
# Nacos \u63A7\u5236\u53F0\u8FDC\u7A0B\u670D\u52A1\u4E0A\u4E0B\u6587\u8DEF\u5F84
nacos.console.remote.server.context-path=/nacos
#*************** \u5B89\u5168\u76F8\u5173\u914D\u7F6E ***************#
# \u5B89\u5168\u5FFD\u7565 URL
nacos.security.ignore.urls=/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-ui/public/**,/v1/auth/**,/v1/console/health/**,/actuator/**,/v1/console/server/**
# \u8BA4\u8BC1\u7CFB\u7EDF\u7C7B\u578B
nacos.core.auth.system.type=nacos
# \u5F00\u542F\u8BA4\u8BC1
nacos.core.auth.enabled=true
# \u542F\u7528 API \u8BBF\u95EE\u6743\u9650
nacos.core.auth.admin.enabled=true
# \u542F\u7528\u63A7\u5236\u53F0 API \u8BA4\u8BC1
nacos.core.auth.console.enabled=true
# \u5F00\u542F\u8BA4\u8BC1\u7F13\u5B58
nacos.core.auth.caching.enabled=true
# \u670D\u52A1\u5668\u8EAB\u4EFD\u8BA4\u8BC1 (\u5F53 nacos.core.auth.enabled=true \u65F6\u751F\u6548)
nacos.core.auth.server.identity.key=VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMTIzNDU2Nzg=
nacos.core.auth.server.identity.value=VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMTIzNDU2Nzg=
# Nacos \u4EE4\u724C\u76F8\u5173\u914D\u7F6E
nacos.core.auth.plugin.nacos.token.cache.enable=false
nacos.core.auth.plugin.nacos.token.expire.seconds=18000
# \u8BA4\u8BC1\u5BC6\u94A5 (Base64 \u7F16\u7801)
nacos.core.auth.plugin.nacos.token.secret.key=VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMTIzNDU2Nzg=
#*************** Istio \u670D\u52A1\u7F51\u683C\u914D\u7F6E ***************#
# MCP \u670D\u52A1\u652F\u6301 - \u5DF2\u7981\u7528
nacos.istio.mcp.server.enabled=false
#*************** K8s \u76F8\u5173\u914D\u7F6E ***************#
# K8s \u540C\u6B65\u652F\u6301 - \u5DF2\u7981\u7528
nacos.k8s.sync.enabled=false
#*************** \u90E8\u7F72\u6A21\u5F0F\u914D\u7F6E ***************#
# \u90E8\u7F72\u6A21\u5F0F: 'merged' \u6DF7\u5408\u6A21\u5F0F, 'server' \u670D\u52A1\u7AEF\u6A21\u5F0F, 'console' \u63A7\u5236\u53F0\u6A21\u5F0F
nacos.deployment.type=merged
#*************** \u6A21\u7CCA\u5339\u914D\u914D\u7F6E ***************#
# \u914D\u7F6E\u4E2D\u5FC3\u6A21\u7CCA\u5339\u914D\u6700\u5927\u503C
nacos.config.fuzzy.watch.max.pattern.count=20
nacos.config.fuzzy.watch.max.pattern.match.config.count=500
# \u670D\u52A1\u53D1\u73B0\u6A21\u7CCA\u5339\u914D\u6700\u5927\u503C
nacos.naming.fuzzy.watch.max.pattern.count=20
nacos.naming.fuzzy.watch.max.pattern.match.service.count=500
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2018-2025, lengleng All rights reserved.
~
~ Redistribution and use in source and binary forms, with or without
~ modification, are permitted provided that the following conditions are met:
~
~ Redistributions of source code must retain the above copyright notice,
~ this list of conditions and the following disclaimer.
~ Redistributions in binary form must reproduce the above copyright
~ notice, this list of conditions and the following disclaimer in the
~ documentation and/or other materials provided with the distribution.
~ Neither the name of the pig4cloud.com developer nor the names of its
~ contributors may be used to endorse or promote products derived from
~ this software without specific prior written permission.
~ Author: lengleng (wangiegie@gmail.com)
-->
<!--
小技巧: 在根pom里面设置统一存放路径,统一管理方便维护
<properties>
<log-path>/Users/lengleng</log-path>
</properties>
1. 其他模块加日志输出,直接copy本文件放在resources 目录即可
2. 注意修改 <property name="${log-path}/log.path" value=""/> 的value模块
-->
<configuration debug="false" scan="false">
<property name="log.path" value="logs/${project.artifactId}"/>
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" class="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
class="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
class="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- Console log output -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- Log file debug output -->
<appender name="debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/debug.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/%d{yyyy-MM, aux}/debug.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
</encoder>
</appender>
<!-- Log file error output -->
<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/%d{yyyy-MM}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
</appender>
<logger name="org.activiti.engine.impl.db" level="DEBUG">
<appender-ref ref="debug"/>
</logger>
<!--nacos 心跳 INFO 屏蔽-->
<logger name="com.alibaba.nacos" level="OFF">
<appender-ref ref="error"/>
</logger>
<!--AJ 验证码INFO 屏蔽-->
<logger name="com.anji.captcha" level="OFF">
<appender-ref ref="error"/>
</logger>
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
<root level="INFO">
<appender-ref ref="console"/>
<appender-ref ref="debug"/>
<appender-ref ref="error"/>
</root>
</configuration>
...@@ -65,9 +65,10 @@ ...@@ -65,9 +65,10 @@
<groupId>com.baomidou</groupId> <groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId> <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
</dependency> </dependency>
<!-- PostgreSQL驱动 -->
<dependency> <dependency>
<groupId>com.mysql</groupId> <groupId>org.postgresql</groupId>
<artifactId>mysql-connector-j</artifactId> <artifactId>postgresql</artifactId>
</dependency> </dependency>
<!--注册中心客户端--> <!--注册中心客户端-->
<dependency> <dependency>
......
FROM registry.cn-hangzhou.aliyuncs.com/dockerhub_mirror/java:21-anolis
WORKDIR /pig-codegen
ARG JAR_FILE=target/pig-codegen.jar
COPY ${JAR_FILE} app.jar
EXPOSE 5002
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms128m -Xmx256m -Djava.security.egd=file:/dev/./urandom"
CMD sleep 60; java $JAVA_OPTS -jar app.jar
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-visual</artifactId>
<version>${revision}</version>
</parent>
<artifactId>pig-codegen</artifactId>
<packaging>jar</packaging>
<description>代码生成模块</description>
<properties>
<screw.version>0.0.6</screw.version>
<anyline.version>8.7.2-jdk17-20240808</anyline.version>
</properties>
<dependencies>
<!--注册中心客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--配置中心客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--数据操作-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common-mybatis</artifactId>
</dependency>
<!--动态数据源 数据操作-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common-datasource</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!--anyline-->
<dependency>
<groupId>org.anyline</groupId>
<artifactId>anyline-environment-spring-data-jdbc</artifactId>
<version>${anyline.version}</version>
</dependency>
<dependency>
<groupId>org.anyline</groupId>
<artifactId>anyline-data-jdbc-mysql</artifactId>
<version>${anyline.version}</version>
</dependency>
<!--common-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common-core</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-json</artifactId>
</dependency>
<!--swagger-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common-swagger</artifactId>
</dependency>
<!--安全模块-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common-xss</artifactId>
</dependency>
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common-security</artifactId>
</dependency>
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common-log</artifactId>
</dependency>
<!--代码生成模板引擎-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>${velocity.version}</version>
</dependency>
<dependency>
<groupId>org.apache.velocity.tools</groupId>
<artifactId>velocity-tools-generic</artifactId>
<version>${velocity.tool.version}</version>
</dependency>
<!--生成文档-->
<dependency>
<groupId>group.springframework.plugin</groupId>
<artifactId>screw-spring-boot-starter</artifactId>
<version>${screw.version}</version>
</dependency>
<!--web 模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--undertow容器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
</dependencies>
<profiles>
<profile>
<id>cloud</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<loaderImplementation>CLASSIC</loaderImplementation>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<configuration>
<skip>false</skip>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>boot</id>
</profile>
</profiles>
</project>
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen;
import com.pig4cloud.pig.common.datasource.annotation.EnableDynamicDataSource;
import com.pig4cloud.pig.common.feign.annotation.EnablePigFeignClients;
import com.pig4cloud.pig.common.security.annotation.EnablePigResourceServer;
import com.pig4cloud.pig.common.swagger.annotation.EnablePigDoc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* 代码生成模块应用启动类
*
* @author lengleng
* @date 2025/05/31
*/
@EnableDynamicDataSource
@EnablePigFeignClients
@EnablePigDoc("gen")
@EnableDiscoveryClient
@EnablePigResourceServer
@SpringBootApplication
public class PigCodeGenApplication {
public static void main(String[] args) {
SpringApplication.run(PigCodeGenApplication.class, args);
}
}
package com.pig4cloud.pig.codegen.config;
import cn.smallbun.screw.core.constant.DefaultConstants;
import lombok.Data;
import org.anyline.util.ConfigTable;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* 代码生成默认配置类
*
* @author lengleng
* @date 2025/05/31
*/
@Data
@Configuration(proxyBeanMethods = false)
@ConfigurationProperties(prefix = PigCodeGenDefaultProperties.PREFIX)
public class PigCodeGenDefaultProperties implements InitializingBean {
public static final String PREFIX = "codegen";
/**
* 是否开启在线更新
*/
private boolean autoCheckVersion = true;
/**
* 模板项目地址
*/
private String onlineUrl = DefaultConstants.CGTM_URL;
/**
* 生成代码的包名
*/
private String packageName = "com.pig4cloud.pig";
/**
* 生成代码的版本
*/
private String version = "1.0.0";
/**
* 生成代码的模块名
*/
private String moduleName = "admin";
/**
* 生成代码的后端路径
*/
private String backendPath = "pig";
/**
* 生成代码的前端路径
*/
private String frontendPath = "pig-ui";
/**
* 生成代码的作者
*/
private String author = "pig";
/**
* 生成代码的邮箱
*/
private String email = "sw@pigx.vip";
/**
* 表单布局(一列、两列)
*/
private Integer formLayout = 2;
/**
* 下载方式 (0 文件下载、1写入目录)
*/
private String generatorType = "0";
@Override
public void afterPropertiesSet() throws Exception {
ConfigTable.KEEP_ADAPTER = 0;
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.controller;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.smallbun.screw.boot.config.Screw;
import cn.smallbun.screw.boot.properties.ScrewProperties;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pig.codegen.entity.GenDatasourceConf;
import com.pig4cloud.pig.codegen.service.GenDatasourceConfService;
import com.pig4cloud.pig.common.core.util.R;
import com.pig4cloud.pig.common.core.util.SpringContextHolder;
import com.pig4cloud.pig.common.security.annotation.Inner;
import com.pig4cloud.pig.common.xss.core.XssCleanIgnore;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.*;
import javax.sql.DataSource;
/**
* 数据源管理控制器
*
* @author lengleng
* @date 2025/05/31
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/dsconf")
public class GenDsConfController {
private final GenDatasourceConfService datasourceConfService;
private final Screw screw;
/**
* 分页查询数据源配置
* @param page 分页参数对象
* @param datasourceConf 数据源配置查询条件
* @return 分页查询结果
*/
@GetMapping("/page")
public R getDsConfPage(Page page, GenDatasourceConf datasourceConf) {
return R.ok(datasourceConfService.page(page,
Wrappers.<GenDatasourceConf>lambdaQuery()
.like(StrUtil.isNotBlank(datasourceConf.getDsName()), GenDatasourceConf::getDsName,
datasourceConf.getDsName())));
}
/**
* 查询全部数据源列表
* @return 包含全部数据源列表的响应结果
*/
@GetMapping("/list")
@Inner(value = false)
public R listDsConfs() {
return R.ok(datasourceConfService.list());
}
/**
* 根据ID查询数据源表
* @param id 数据源ID
* @return 包含查询结果的响应对象
*/
@GetMapping("/{id}")
public R getDsConfById(@PathVariable("id") Long id) {
return R.ok(datasourceConfService.getById(id));
}
/**
* 新增数据源表
* @param datasourceConf 数据源配置信息
* @return 操作结果
*/
@PostMapping
@XssCleanIgnore
public R saveDsConf(@RequestBody GenDatasourceConf datasourceConf) {
return R.ok(datasourceConfService.saveDsByEnc(datasourceConf));
}
/**
* 修改数据源表
* @param conf 数据源表配置信息
* @return 操作结果
*/
@PutMapping
@XssCleanIgnore
public R updateDsConf(@RequestBody GenDatasourceConf conf) {
return R.ok(datasourceConfService.updateDsByEnc(conf));
}
/**
* 通过id数组删除数据源表
* @param ids 要删除的数据源id数组
* @return 包含操作结果的R对象
*/
@DeleteMapping
public R removeDsConfByIds(@RequestBody Long[] ids) {
return R.ok(datasourceConfService.removeByDsId(ids));
}
/**
* 生成指定数据源的数据库文档并输出到响应流
* @param dsName 数据源名称
* @param response HTTP响应对象
* @throws Exception 生成文档或IO操作过程中可能抛出的异常
*/
@SneakyThrows
@GetMapping("/doc")
public void generatorDoc(String dsName, HttpServletResponse response) {
// 设置指定的数据源
DynamicRoutingDataSource dynamicRoutingDataSource = SpringContextHolder.getBean(DynamicRoutingDataSource.class);
DynamicDataSourceContextHolder.push(dsName);
DataSource dataSource = dynamicRoutingDataSource.determineDataSource();
// 设置指定的目标表
ScrewProperties screwProperties = SpringContextHolder.getBean(ScrewProperties.class);
// 生成
byte[] data = screw.documentGeneration(dsName, dataSource, screwProperties).toByteArray();
response.reset();
response.addHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(data.length));
response.setContentType("application/octet-stream");
IoUtil.write(response.getOutputStream(), Boolean.FALSE, data);
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.controller;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pig.codegen.entity.GenFieldType;
import com.pig4cloud.pig.codegen.service.GenFieldTypeService;
import com.pig4cloud.pig.common.core.util.R;
import com.pig4cloud.pig.common.log.annotation.SysLog;
import com.pig4cloud.plugin.excel.annotation.ResponseExcel;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 列属性管理控制器
*
* @author lengleng
* @date 2025/05/31
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/fieldtype")
@Tag(description = "fieldtype", name = "列属性管理")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class GenFieldTypeController {
private final GenFieldTypeService fieldTypeService;
/**
* 分页查询字段类型
* @param page 分页对象
* @param fieldType 字段类型查询条件
* @return 分页查询结果
*/
@Operation(summary = "分页查询", description = "分页查询")
@GetMapping("/page")
public R getFieldTypePage(Page page, GenFieldType fieldType) {
return R.ok(fieldTypeService.page(page,
Wrappers.<GenFieldType>lambdaQuery()
.like(StrUtil.isNotBlank(fieldType.getColumnType()), GenFieldType::getColumnType,
fieldType.getColumnType())));
}
/**
* 查询列表
* @param fieldType 查询条件
* @return 包含查询结果的响应对象
*/
@Operation(summary = "查询列表", description = "查询列表")
@GetMapping("/list")
public R listFieldTypes(GenFieldType fieldType) {
return R.ok(fieldTypeService.list(Wrappers.query(fieldType)));
}
/**
* 通过id查询列属性
* @param id 列属性id
* @return 包含查询结果的响应对象
*/
@Operation(summary = "通过id查询", description = "通过id查询")
@GetMapping("/details/{id}")
public R getFieldTypeById(@PathVariable("id") Long id) {
return R.ok(fieldTypeService.getById(id));
}
/**
* 根据查询条件获取字段类型详情
* @param query 字段类型查询条件
* @return 包含查询结果的响应对象
*/
@GetMapping("/details")
public R getFieldTypeDetails(GenFieldType query) {
return R.ok(fieldTypeService.getOne(Wrappers.query(query), false));
}
/**
* 新增列属性
* @param fieldType 列属性对象
* @return 操作结果
*/
@Operation(summary = "新增列属性", description = "新增列属性")
@SysLog("新增列属性")
@PostMapping
public R saveFieldType(@RequestBody GenFieldType fieldType) {
return R.ok(fieldTypeService.save(fieldType));
}
/**
* 修改列属性
* @param fieldType 列属性对象
* @return 操作结果
*/
@Operation(summary = "修改列属性", description = "修改列属性")
@SysLog("修改列属性")
@PutMapping
public R updateFieldType(@RequestBody GenFieldType fieldType) {
return R.ok(fieldTypeService.updateById(fieldType));
}
/**
* 通过id批量删除列属性
* @param ids 要删除的列属性id数组
* @return 操作结果
*/
@Operation(summary = "通过id删除列属性", description = "通过id删除列属性")
@SysLog("通过id删除列属性")
@DeleteMapping
public R removeFieldTypeByIds(@RequestBody Long[] ids) {
return R.ok(fieldTypeService.removeBatchByIds(CollUtil.toList(ids)));
}
/**
* 导出excel表格
* @param fieldType 查询条件
* @return excel文件数据列表
*/
@ResponseExcel
@GetMapping("/export")
public List<GenFieldType> exportFieldTypes(GenFieldType fieldType) {
return fieldTypeService.list(Wrappers.query(fieldType));
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.controller;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pig.codegen.entity.GenGroupEntity;
import com.pig4cloud.pig.codegen.service.GenGroupService;
import com.pig4cloud.pig.codegen.util.vo.GroupVO;
import com.pig4cloud.pig.codegen.util.vo.TemplateGroupDTO;
import com.pig4cloud.pig.common.core.util.R;
import com.pig4cloud.pig.common.log.annotation.SysLog;
import com.pig4cloud.pig.common.security.annotation.HasPermission;
import com.pig4cloud.plugin.excel.annotation.ResponseExcel;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 模板分组管理控制器
*
* @author lengleng
* @date 2025/05/31
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/group")
@Tag(description = "group", name = "模板分组管理")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class GenGroupController {
private final GenGroupService genGroupService;
/**
* 分页查询模板分组
* @param page 分页对象
* @param genGroup 模板分组查询条件
* @return 分页查询结果
*/
@Operation(summary = "分页查询", description = "分页查询")
@GetMapping("/page")
@HasPermission("codegen_group_view")
public R getGroupPage(Page page, GenGroupEntity genGroup) {
LambdaQueryWrapper<GenGroupEntity> wrapper = Wrappers.<GenGroupEntity>lambdaQuery()
.like(genGroup.getId() != null, GenGroupEntity::getId, genGroup.getId())
.like(StrUtil.isNotEmpty(genGroup.getGroupName()), GenGroupEntity::getGroupName, genGroup.getGroupName());
return R.ok(genGroupService.page(page, wrapper));
}
/**
* 通过id查询模板分组
* @param id id
* @return R
*/
@Operation(summary = "通过id查询", description = "通过id查询")
@GetMapping("/{id}")
@HasPermission("codegen_group_view")
public R getGroupById(@PathVariable("id") Long id) {
return R.ok(genGroupService.getGroupVoById(id));
}
/**
* 新增模板分组
* @param genTemplateGroup 模板分组
* @return R
*/
@Operation(summary = "新增模板分组", description = "新增模板分组")
@SysLog("新增模板分组")
@PostMapping
@HasPermission("codegen_group_add")
public R saveGroup(@RequestBody TemplateGroupDTO genTemplateGroup) {
genGroupService.saveGenGroup(genTemplateGroup);
return R.ok();
}
/**
* 修改模板分组
* @param groupVo 模板分组
* @return R
*/
@Operation(summary = "修改模板分组", description = "修改模板分组")
@SysLog("修改模板分组")
@PutMapping
@HasPermission("codegen_group_edit")
public R updateGroup(@RequestBody GroupVO groupVo) {
genGroupService.updateGroupAndTemplateById(groupVo);
return R.ok();
}
/**
* 通过id删除模板分组
* @param ids id列表
* @return R
*/
@Operation(summary = "通过id删除模板分组", description = "通过id删除模板分组")
@SysLog("通过id删除模板分组")
@DeleteMapping
@HasPermission("codegen_group_del")
public R removeGroupByIds(@RequestBody Long[] ids) {
genGroupService.delGroupAndTemplate(ids);
return R.ok();
}
/**
* 导出excel 表格
* @param genGroup 查询条件
* @return excel 文件流
*/
@ResponseExcel
@GetMapping("/export")
@HasPermission("codegen_group_export")
public List<GenGroupEntity> exportGroups(GenGroupEntity genGroup) {
return genGroupService.list(Wrappers.query(genGroup));
}
/**
* 查询列表
* @return 包含列表数据的响应信息
*/
@GetMapping("/list")
@Operation(summary = "查询列表", description = "查询列表")
public R listGroups() {
List<GenGroupEntity> list = genGroupService
.list(Wrappers.<GenGroupEntity>lambdaQuery().orderByDesc(GenGroupEntity::getCreateTime));
return R.ok(list);
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.controller;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pig.codegen.entity.GenTable;
import com.pig4cloud.pig.codegen.entity.GenTableColumnEntity;
import com.pig4cloud.pig.codegen.service.GenTableColumnService;
import com.pig4cloud.pig.codegen.service.GenTableService;
import com.pig4cloud.pig.common.core.util.R;
import com.pig4cloud.pig.common.log.annotation.SysLog;
import com.pig4cloud.plugin.excel.annotation.ResponseExcel;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 代码表管理控制器
*
* @author lengleng
* @date 2025/05/31
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/table")
@Tag(description = "table", name = "代码表管理")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class GenTableController {
private final GenTableColumnService tableColumnService;
/**
* 表服务
*/
private final GenTableService tableService;
/**
* 分页查询
* @param page 分页对象
* @param table 列属性
* @return
*/
@Operation(summary = "分页查询", description = "分页查询")
@GetMapping("/page")
public R getTablePage(Page page, GenTable table) {
return R.ok(tableService.queryTablePage(page, table));
}
/**
* 通过id查询表信息(代码生成设置 + 表 + 字段设置)
* @param id id
* @return R
*/
@Operation(summary = "通过id查询", description = "通过id查询")
@GetMapping("/{id}")
public R getTableById(@PathVariable("id") Long id) {
return R.ok(tableService.getById(id));
}
/**
* 查询数据源所有表
* @param dsName 数据源名称
* @return 包含表列表的响应结果
*/
@GetMapping("/list/{dsName}")
public R listTables(@PathVariable("dsName") String dsName) {
return R.ok(tableService.queryTableList(dsName));
}
/**
* 获取表信息
* @param dsName 数据源
* @param tableName 表名称
*/
@GetMapping("/{dsName}/{tableName}")
public R<GenTable> getTable(@PathVariable("dsName") String dsName, @PathVariable String tableName) {
return R.ok(tableService.queryOrBuildTable(dsName, tableName));
}
/**
* 查询表DDL语句
* @param dsName 数据源
* @param tableName 表名称
*/
@GetMapping("/column/{dsName}/{tableName}")
public R getTableColumn(@PathVariable("dsName") String dsName, @PathVariable String tableName) throws Exception {
return R.ok(tableService.queryTableColumn(dsName, tableName));
}
/**
* 查询表DDL语句
* @param dsName 数据源
* @param tableName 表名称
*/
@GetMapping("/ddl/{dsName}/{tableName}")
public R getTableDdl(@PathVariable("dsName") String dsName, @PathVariable String tableName) throws Exception {
return R.ok(tableService.queryTableDdl(dsName, tableName));
}
/**
* 同步表信息
* @param dsName 数据源
* @param tableName 表名称
*/
@GetMapping("/sync/{dsName}/{tableName}")
public R<GenTable> syncTable(@PathVariable("dsName") String dsName, @PathVariable String tableName) {
// 表配置删除
tableService.remove(
Wrappers.<GenTable>lambdaQuery().eq(GenTable::getDsName, dsName).eq(GenTable::getTableName, tableName));
// 字段配置删除
tableColumnService.remove(Wrappers.<GenTableColumnEntity>lambdaQuery()
.eq(GenTableColumnEntity::getDsName, dsName)
.eq(GenTableColumnEntity::getTableName, tableName));
return R.ok(tableService.queryOrBuildTable(dsName, tableName));
}
/**
* 修改列属性
* @param table 列属性
* @return R
*/
@Operation(summary = "修改列属性", description = "修改列属性")
@SysLog("修改列属性")
@PutMapping
public R updateTable(@RequestBody GenTable table) {
return R.ok(tableService.updateById(table));
}
/**
* 修改表字段数据
* @param dsName 数据源
* @param tableName 表名称
* @param tableFieldList 字段列表
*/
@PutMapping("/field/{dsName}/{tableName}")
public R<String> updateTableField(@PathVariable("dsName") String dsName, @PathVariable String tableName,
@RequestBody List<GenTableColumnEntity> tableFieldList) {
tableColumnService.updateTableField(dsName, tableName, tableFieldList);
return R.ok();
}
/**
* 导出excel 表格
* @param table 查询条件
* @return excel 文件流
*/
@ResponseExcel
@GetMapping("/export")
public List<GenTable> exportTables(GenTable table) {
return tableService.list(Wrappers.query(table));
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.controller;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pig.codegen.entity.GenTemplateEntity;
import com.pig4cloud.pig.codegen.service.GenTemplateService;
import com.pig4cloud.pig.common.core.util.R;
import com.pig4cloud.pig.common.log.annotation.SysLog;
import com.pig4cloud.pig.common.security.annotation.HasPermission;
import com.pig4cloud.pig.common.xss.core.XssCleanIgnore;
import com.pig4cloud.plugin.excel.annotation.ResponseExcel;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 模板管理控制器
*
* @author lengleng
* @date 2025/05/31
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/template")
@Tag(description = "template", name = "模板管理")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class GenTemplateController {
private final GenTemplateService genTemplateService;
/**
* 分页查询模板信息
* @param page 分页参数对象
* @param genTemplate 模板查询条件
* @return 分页查询结果
*/
@Operation(summary = "分页查询", description = "分页查询")
@GetMapping("/page")
@HasPermission("codegen_template_view")
public R getTemplatePage(Page page, GenTemplateEntity genTemplate) {
LambdaQueryWrapper<GenTemplateEntity> wrapper = Wrappers.<GenTemplateEntity>lambdaQuery()
.like(genTemplate.getId() != null, GenTemplateEntity::getId, genTemplate.getId())
.like(StrUtil.isNotEmpty(genTemplate.getTemplateName()), GenTemplateEntity::getTemplateName,
genTemplate.getTemplateName());
return R.ok(genTemplateService.page(page, wrapper));
}
/**
* 查询全部模板
* @return
*/
@Operation(summary = "查询全部", description = "查询全部")
@GetMapping("/list")
@HasPermission("codegen_template_view")
public R listTemplates() {
return R.ok(genTemplateService
.list(Wrappers.<GenTemplateEntity>lambdaQuery().orderByDesc(GenTemplateEntity::getCreateTime)));
}
/**
* 通过id查询模板
* @param id id
* @return R
*/
@Operation(summary = "通过id查询", description = "通过id查询")
@GetMapping("/{id}")
@HasPermission("codegen_template_view")
public R getTemplateById(@PathVariable("id") Long id) {
return R.ok(genTemplateService.getById(id));
}
/**
* 新增模板
* @param genTemplate 模板
* @return R
*/
@XssCleanIgnore
@Operation(summary = "新增模板", description = "新增模板")
@SysLog("新增模板")
@PostMapping
@HasPermission("codegen_template_add")
public R saveTemplate(@RequestBody GenTemplateEntity genTemplate) {
return R.ok(genTemplateService.save(genTemplate));
}
/**
* 修改模板
* @param genTemplate 模板
* @return R
*/
@XssCleanIgnore
@Operation(summary = "修改模板", description = "修改模板")
@SysLog("修改模板")
@PutMapping
@HasPermission("codegen_template_edit")
public R updateTemplate(@RequestBody GenTemplateEntity genTemplate) {
return R.ok(genTemplateService.updateById(genTemplate));
}
/**
* 通过id删除模板
* @param ids id列表
* @return R
*/
@Operation(summary = "通过id删除模板", description = "通过id删除模板")
@SysLog("通过id删除模板")
@DeleteMapping
@HasPermission("codegen_template_del")
public R removeTemplateByIds(@RequestBody Long[] ids) {
return R.ok(genTemplateService.removeBatchByIds(CollUtil.toList(ids)));
}
/**
* 导出excel 表格
* @param genTemplate 查询条件
* @return excel 文件流
*/
@ResponseExcel
@GetMapping("/export")
@HasPermission("codegen_template_export")
public List<GenTemplateEntity> exportTemplates(GenTemplateEntity genTemplate) {
return genTemplateService.list(Wrappers.query(genTemplate));
}
/**
* 在线更新模板
* @return R
*/
@Operation(summary = "在线更新模板", description = "在线更新模板")
@GetMapping("/online")
@HasPermission("codegen_template_view")
public R online() {
return genTemplateService.onlineUpdate();
}
/**
* 检查版本
* @return {@link R }
*/
@Operation(summary = "在线检查模板", description = "在线检查模板")
@GetMapping("/checkVersion")
@HasPermission("codegen_template_view")
public R checkVersion() {
return genTemplateService.checkVersion();
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.controller;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pig.codegen.entity.GenTemplateGroupEntity;
import com.pig4cloud.pig.codegen.service.GenTemplateGroupService;
import com.pig4cloud.pig.common.core.util.R;
import com.pig4cloud.pig.common.log.annotation.SysLog;
import com.pig4cloud.pig.common.security.annotation.HasPermission;
import com.pig4cloud.plugin.excel.annotation.ResponseExcel;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 模板分组关联表
*
* @author PIG
* @date 2023-02-22 09:25:15
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/templateGroup")
@Tag(description = "templateGroup", name = "模板分组关联表管理")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class GenTemplateGroupController {
private final GenTemplateGroupService genTemplateGroupService;
/**
* 分页查询
* @param page 分页对象
* @param genTemplateGroup 模板分组关联表
* @return
*/
@Operation(summary = "分页查询", description = "分页查询")
@GetMapping("/page")
@HasPermission("codegen_templateGroup_view")
public R getTemplateGroupPage(Page page, GenTemplateGroupEntity genTemplateGroup) {
LambdaQueryWrapper<GenTemplateGroupEntity> wrapper = Wrappers.lambdaQuery();
return R.ok(genTemplateGroupService.page(page, wrapper));
}
/**
* 通过id查询模板分组关联表
* @param groupId id
* @return R
*/
@Operation(summary = "通过id查询", description = "通过id查询")
@GetMapping("/{groupId}")
@HasPermission("codegen_templateGroup_view")
public R getTemplateGroupById(@PathVariable("groupId") Long groupId) {
return R.ok(genTemplateGroupService.getById(groupId));
}
/**
* 新增模板分组关联表
* @param genTemplateGroup 模板分组关联表
* @return R
*/
@Operation(summary = "新增模板分组关联表", description = "新增模板分组关联表")
@SysLog("新增模板分组关联表")
@PostMapping
@HasPermission("codegen_templateGroup_add")
public R saveTemplateGroup(@RequestBody GenTemplateGroupEntity genTemplateGroup) {
return R.ok(genTemplateGroupService.save(genTemplateGroup));
}
/**
* 修改模板分组关联表
* @param genTemplateGroup 模板分组关联表
* @return R
*/
@Operation(summary = "修改模板分组关联表", description = "修改模板分组关联表")
@SysLog("修改模板分组关联表")
@PutMapping
@HasPermission("codegen_templateGroup_edit")
public R updateTemplateGroup(@RequestBody GenTemplateGroupEntity genTemplateGroup) {
return R.ok(genTemplateGroupService.updateById(genTemplateGroup));
}
/**
* 通过id删除模板分组关联表
* @param ids groupId列表
* @return R
*/
@Operation(summary = "通过id删除模板分组关联表", description = "通过id删除模板分组关联表")
@SysLog("通过id删除模板分组关联表")
@DeleteMapping
@HasPermission("codegen_templateGroup_del")
public R removeTemplateGroupByIds(@RequestBody Long[] ids) {
return R.ok(genTemplateGroupService.removeBatchByIds(CollUtil.toList(ids)));
}
/**
* 导出excel 表格
* @param genTemplateGroup 查询条件
* @return excel 文件流
*/
@ResponseExcel
@GetMapping("/export")
@HasPermission("codegen_templateGroup_export")
public List<GenTemplateGroupEntity> exportTemplateGroups(GenTemplateGroupEntity genTemplateGroup) {
return genTemplateGroupService.list(Wrappers.query(genTemplateGroup));
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.controller;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pig.codegen.service.GeneratorService;
import com.pig4cloud.pig.common.core.util.R;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.io.ByteArrayOutputStream;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipOutputStream;
/**
* 代码生成器控制器
*
* @author lengleng
* @date 2025/05/31
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/generator")
public class GeneratorController {
private final GeneratorService generatorService;
/**
* ZIP 下载生成代码
* @param tableIds 数据表ID
* @param response 流输出对象
*/
@SneakyThrows
@GetMapping("/download")
public void download(String tableIds, HttpServletResponse response) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ZipOutputStream zip = new ZipOutputStream(outputStream);
// 生成代码
for (String tableId : tableIds.split(StrUtil.COMMA)) {
generatorService.downloadCode(Long.parseLong(tableId), zip);
}
IoUtil.close(zip);
// zip压缩包数据
byte[] data = outputStream.toByteArray();
response.reset();
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment; filename=%s.zip", tableIds));
response.addHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(data.length));
response.setContentType("application/octet-stream; charset=UTF-8");
IoUtil.write(response.getOutputStream(), false, data);
}
/**
* 生成代码
* @param tableIds 表ID列表,多个ID用逗号分隔
* @return 操作结果
* @throws Exception 生成代码过程中可能抛出的异常
*/
@ResponseBody
@GetMapping("/code")
public R<String> code(String tableIds) throws Exception {
// 生成代码
for (String tableId : tableIds.split(StrUtil.COMMA)) {
generatorService.generatorCode(Long.valueOf(tableId));
}
return R.ok();
}
/**
* 预览代码
* @param tableId 表ID
* @return 代码预览结果列表
*/
@SneakyThrows
@GetMapping("/preview")
public List<Map<String, String>> preview(Long tableId) {
return generatorService.preview(tableId);
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.entity;
import lombok.Data;
/**
* @author lengleng
* @date 2018/07/29 列属性: https://blog.csdn.net/lkforce/article/details/79557482
*/
@Data
public class ColumnEntity {
/**
* 列表
*/
private String columnName;
/**
* 数据类型
*/
private String dataType;
/**
* JAVA 数据类型
*/
private String javaType;
/**
* 备注
*/
private String comments;
/**
* 驼峰属性
*/
private String caseAttrName;
/**
* 普通属性
*/
private String lowerAttrName;
/**
* 属性类型
*/
private String attrType;
/**
* 其他信息
*/
private String extra;
/**
* 字段类型
*/
private String columnType;
/**
* 是否可以为空
*/
private Boolean nullable;
/**
* 是否隐藏
*/
private Boolean hidden;
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.entity;
import lombok.Data;
/**
* @author lengleng
* @date 2018/8/2 生成配置
*/
@Data
public class GenConfig {
/**
* 数据源name
*/
private String dsName;
/**
* 包名
*/
private String packageName;
/**
* 作者
*/
private String author;
/**
* 模块名称
*/
private String moduleName;
/**
* 表前缀
*/
private String tablePrefix;
/**
* 表名称
*/
private String tableName;
/**
* 表备注
*/
private String comments;
/**
* 代码风格 0 - avue 1 - element 2 - uview
*/
private String style;
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 数据源表
*
* @author lengleng
* @date 2019-03-31 16:00:20
*/
@Data
@TableName("gen_datasource_conf")
@EqualsAndHashCode(callSuper = true)
public class GenDatasourceConf extends Model<GenDatasourceConf> {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(type = IdType.ASSIGN_ID)
private Long id;
/**
* 名称
*/
private String name;
/**
* 数据库类型
*/
private String dsType;
/**
* 配置类型 (0 主机形式 | 1 url形式)
*/
private Integer confType;
/**
* 主机地址
*/
private String host;
/**
* 端口
*/
private Integer port;
/**
* jdbc-url
*/
private String url;
/**
* 实例
*/
private String instance;
/**
* 数据库名称
*/
private String dsName;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 修改时间
*/
@TableField(fill = FieldFill.UPDATE)
private LocalDateTime updateTime;
/**
* 0-正常,1-删除
*/
@TableLogic
@TableField(fill = FieldFill.INSERT)
private String delFlag;
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 列属性
*
* @author pigx code generator
* @date 2023-02-06 20:16:01
*/
@Data
@TableName("gen_field_type")
@EqualsAndHashCode(callSuper = true)
@Schema(description = "列属性")
public class GenFieldType extends Model<GenFieldType> {
private static final long serialVersionUID = 1L;
/**
* id
*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "id")
private Long id;
/**
* 字段类型
*/
@Schema(description = "字段类型")
private String columnType;
/**
* 属性类型
*/
@Schema(description = "属性类型")
private String attrType;
/**
* 属性包名
*/
@Schema(description = "属性包名")
private String packageName;
/**
* 创建人
*/
@TableField(fill = FieldFill.INSERT)
@Schema(description = "创建人")
private String createBy;
/**
* 修改人
*/
@TableField(fill = FieldFill.UPDATE)
@Schema(description = "修改人")
private String updateBy;
/**
* 创建时间
*/
@Schema(description = "创建时间")
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 修改时间
*/
@Schema(description = "修改时间")
@TableField(fill = FieldFill.UPDATE)
private LocalDateTime updateTime;
/**
* 删除标识(0-正常,1-删除)
*/
@TableLogic
@TableField(fill = FieldFill.INSERT)
@Schema(description = "删除标记,1:已删除,0:正常")
private String delFlag;
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 模板分组
*
* @author PIG
* @date 2023-02-21 20:01:53
*/
@Data
@TableName("gen_group")
@EqualsAndHashCode(callSuper = true)
@Schema(description = "模板分组")
public class GenGroupEntity extends Model<GenGroupEntity> {
private static final long serialVersionUID = 1L;
/**
* id
*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "id")
private Long id;
/**
* 分组名称
*/
@Schema(description = "分组名称")
private String groupName;
/**
* 分组描述
*/
@Schema(description = "分组描述")
@TableField(fill = FieldFill.INSERT)
private String groupDesc;
/**
* 创建人
*/
@TableField(fill = FieldFill.INSERT)
@Schema(description = "创建人")
private String createBy;
/**
* 修改人
*/
@TableField(fill = FieldFill.UPDATE)
@Schema(description = "修改人")
private String updateBy;
/**
* 创建时间
*/
@Schema(description = "创建时间")
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 修改时间
*/
@Schema(description = "修改时间")
@TableField(fill = FieldFill.UPDATE)
private LocalDateTime updateTime;
/**
* 删除标识(0-正常,1-删除)
*/
@TableLogic
@TableField(fill = FieldFill.INSERT)
@Schema(description = "删除标记,1:已删除,0:正常")
private String delFlag;
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
import java.util.List;
/**
* 列属性
*
* @author pigx code generator
* @date 2023-02-06 20:34:55
*/
@Data
@TableName("gen_table")
@EqualsAndHashCode(callSuper = true)
@Schema(description = "列属性")
public class GenTable extends Model<GenTable> {
private static final long serialVersionUID = 1L;
/**
* id
*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "id")
private Long id;
/**
* 数据源名称
*/
@Schema(description = "数据源名称")
private String dsName;
/**
* 数据源类型
*/
@Schema(description = "数据源类型")
private String dbType;
/**
* 表名
*/
@Schema(description = "表名")
private String tableName;
/**
* 类名
*/
@Schema(description = "类名")
private String className;
/**
* 说明
*/
@Schema(description = "说明")
private String tableComment;
/**
* 作者
*/
@Schema(description = "作者")
private String author;
/**
* 邮箱
*/
@Schema(description = "邮箱")
private String email;
/**
* 项目包名
*/
@Schema(description = "项目包名")
private String packageName;
/**
* 项目版本号
*/
@Schema(description = "项目版本号")
private String version;
/**
* 生成方式 0:zip压缩包 1:自定义目录
*/
@Schema(description = "生成方式 0:zip压缩包 1:自定义目录")
private String generatorType;
/**
* 后端生成路径
*/
@Schema(description = "后端生成路径")
private String backendPath;
/**
* 前端生成路径
*/
@Schema(description = "前端生成路径")
private String frontendPath;
/**
* 模块名
*/
@Schema(description = "模块名")
private String moduleName;
/**
* 功能名
*/
@Schema(description = "功能名")
private String functionName;
/**
* 表单布局 1:一列 2:两列
*/
@Schema(description = "表单布局 1:一列 2:两列")
private Integer formLayout;
/**
* 基类ID
*/
@Schema(description = "基类ID")
private Long baseclassId;
/**
* 创建时间
*/
@Schema(description = "创建时间")
private LocalDateTime createTime;
/**
* 代码生成风格
*/
private Long style;
/**
* 子表名称
*/
private String childTableName;
/**
* 主表关联键
*/
private String mainField;
/**
* 子表关联键
*/
private String childField;
/**
* 字段列表
*/
@TableField(exist = false)
private List<GenTableColumnEntity> fieldList;
/**
* 子表字段列表
*/
@TableField(exist = false)
private List<GenTableColumnEntity> childFieldList;
/**
* 代码风格(模版分组信息)
*/
@TableField(exist = false)
private List<GenGroupEntity> groupList;
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author lengleng
* @date 2023-02-06
*
* 记录表字段的配置信息
*/
@Data
@TableName("gen_table_column")
@EqualsAndHashCode(callSuper = true)
public class GenTableColumnEntity extends Model<GenDatasourceConf> {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(type = IdType.ASSIGN_ID)
private Long id;
/**
* 数据源名
*/
private String dsName;
/**
* 表名称
*/
private String tableName;
/**
* 字段名称
*/
private String fieldName;
/**
* 排序
*/
private Integer sort;
/**
* 字段类型
*/
private String fieldType;
/**
* 字段说明
*/
private String fieldComment;
/**
* 属性名
*/
private String attrName;
/**
* 属性类型
*/
private String attrType;
/**
* 属性包名
*/
private String packageName;
/**
* 自动填充
*/
private String autoFill;
/**
* 主键 0:否 1:是
*/
private String primaryPk;
/**
* 基类字段 0:否 1:是
*/
private String baseField;
/**
* 表单项 0:否 1:是
*/
private String formItem;
/**
* 表单必填 0:否 1:是
*/
private String formRequired;
/**
* 表单类型
*/
private String formType;
/**
* 表单效验
*/
private String formValidator;
/**
* 列表项 0:否 1:是
*/
private String gridItem;
/**
* 列表排序 0:否 1:是
*/
private String gridSort;
/**
* 查询项 0:否 1:是
*/
private String queryItem;
/**
* 查询方式
*/
private String queryType;
/**
* 查询表单类型
*/
private String queryFormType;
/**
* 字段字典类型
*/
@TableField(updateStrategy = FieldStrategy.ALWAYS)
private String fieldDict;
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 模板
*
* @author PIG
* @date 2023-02-21 17:15:44
*/
@Data
@TableName("gen_template")
@EqualsAndHashCode(callSuper = true)
@Schema(description = "模板")
public class GenTemplateEntity extends Model<GenTemplateEntity> {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private Long id;
/**
* 模板名称
*/
@Schema(description = "模板名称")
private String templateName;
/**
* 模板路径
*/
@Schema(description = "模板路径")
private String generatorPath;
/**
* 模板描述
*/
@Schema(description = "模板描述")
private String templateDesc;
/**
* 模板代码
*/
@Schema(description = "模板代码")
private String templateCode;
/**
* 创建人
*/
@TableField(fill = FieldFill.INSERT)
@Schema(description = "创建人")
private String createBy;
/**
* 修改人
*/
@TableField(fill = FieldFill.UPDATE)
@Schema(description = "修改人")
private String updateBy;
/**
* 创建时间
*/
@Schema(description = "创建时间")
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 修改时间
*/
@Schema(description = "修改时间")
@TableField(fill = FieldFill.UPDATE)
private LocalDateTime updateTime;
/**
* 删除标识(0-正常,1-删除)
*/
@TableLogic
@TableField(fill = FieldFill.INSERT)
@Schema(description = "删除标记,1:已删除,0:正常")
private String delFlag;
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 模板分组关联表
*
* @author PIG
* @date 2023-02-22 09:25:15
*/
@Data
@TableName("gen_template_group")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@Schema(description = "模板分组关联表")
public class GenTemplateGroupEntity extends Model<GenTemplateGroupEntity> {
private static final long serialVersionUID = 1L;
/**
* 分组id
*/
@Schema(description = "分组id")
private Long groupId;
/**
* 模板id
*/
@Schema(description = "模板id")
private Long templateId;
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.entity;
import lombok.Data;
import java.util.List;
/**
* @author lengleng
* @date 2018/07/29 表属性: https://blog.csdn.net/lkforce/article/details/79557482
*/
@Data
public class TableEntity {
/**
* 名称
*/
private String tableName;
/**
* 备注
*/
private String comments;
/**
* 主键
*/
private ColumnEntity pk;
/**
* 列名
*/
private List<ColumnEntity> columns;
/**
* 驼峰类型
*/
private String caseClassName;
/**
* 普通类型
*/
private String lowerClassName;
/**
* 数据库类型 (用于根据数据库个性化)
*/
private String dbType;
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.pig4cloud.pig.codegen.entity.GenDatasourceConf;
import org.apache.ibatis.annotations.Mapper;
/**
* 数据源表 Mapper 接口
*
* @author lengleng
* @date 2019-03-31 16:00:20
*/
@Mapper
public interface GenDatasourceConfMapper extends BaseMapper<GenDatasourceConf> {
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.mapper;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.LinkedHashMap;
import java.util.List;
/**
* 动态查询Mapper接口
*
* @author lengleng
* @date 2025/05/31
*/
@Mapper
public interface GenDynamicMapper {
/**
* 动态SQL查询
* @param sq SQL查询语句
* @return 查询结果列表,每个结果以LinkedHashMap形式存储
*/
@InterceptorIgnore(tenantLine = "true")
List<LinkedHashMap<String, Object>> dynamicQuerySql(@Param("value") String sq);
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.pig4cloud.pig.codegen.entity.GenFieldType;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.Set;
/**
* 字段类型映射器接口:用于操作字段类型相关数据库操作
*
* @author lengleng
* @date 2025/05/31
*/
@Mapper
public interface GenFieldTypeMapper extends BaseMapper<GenFieldType> {
/**
* 根据tableId,获取包列表
* @param dsName 数据源名称
* @param tableName 表名称
* @return 返回包列表
*/
Set<String> getPackageByTableId(@Param("dsName") String dsName, @Param("tableName") String tableName);
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.pig4cloud.pig.codegen.entity.GenGroupEntity;
import com.pig4cloud.pig.codegen.util.vo.GroupVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* 模板分组 Mapper 接口
*
* @author lengleng
* @date 2025/05/31
*/
@Mapper
public interface GenGroupMapper extends BaseMapper<GenGroupEntity> {
/**
* 根据ID获取分组VO对象
* @param id 分组ID
* @return 分组VO对象
*/
GroupVO getGroupVoById(@Param("id") Long id);
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.pig4cloud.pig.codegen.entity.GenTableColumnEntity;
import org.apache.ibatis.annotations.Mapper;
/**
* 代码生成表列属性Mapper接口
*
* @author lengleng
* @date 2025/05/31
*/
@Mapper
public interface GenTableColumnMapper extends BaseMapper<GenTableColumnEntity> {
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.pig4cloud.pig.codegen.entity.GenTable;
import org.apache.ibatis.annotations.Mapper;
/**
* 代码生成表 Mapper 接口
*
* @author lengleng
* @date 2025/05/31
*/
@Mapper
public interface GenTableMapper extends BaseMapper<GenTable> {
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.pig4cloud.pig.codegen.entity.GenTemplateGroupEntity;
import org.apache.ibatis.annotations.Mapper;
/**
* 模板分组关联表 Mapper 接口
*
* @author lengleng
* @date 2025/05/31
*/
@Mapper
public interface GenTemplateGroupMapper extends BaseMapper<GenTemplateGroupEntity> {
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.pig4cloud.pig.codegen.entity.GenTemplateEntity;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 代码生成模板Mapper接口
*
* @author lengleng
* @date 2025/05/31
*/
@Mapper
public interface GenTemplateMapper extends BaseMapper<GenTemplateEntity> {
/**
* 根据模板组ID查询模板列表
* @param groupId 模板组ID
* @return 模板实体列表
*/
List<GenTemplateEntity> listTemplateById(Long groupId);
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pig.codegen.entity.GenDatasourceConf;
/**
* 数据源配置服务接口 提供数据源的增删改查及校验等功能
*
* @author lengleng
* @date 2025/05/31
*/
public interface GenDatasourceConfService extends IService<GenDatasourceConf> {
/**
* 保存数据源并加密
* @param genDatasourceConf 数据源配置信息
* @return 保存是否成功
*/
Boolean saveDsByEnc(GenDatasourceConf genDatasourceConf);
/**
* 更新数据源
* @param genDatasourceConf 数据源配置信息
* @return 更新是否成功
*/
Boolean updateDsByEnc(GenDatasourceConf genDatasourceConf);
/**
* 添加动态数据源
* @param datasourceConf 数据源配置信息
*/
void addDynamicDataSource(GenDatasourceConf datasourceConf);
/**
* 校验数据源配置是否有效
* @param datasourceConf 数据源配置信息
* @return true表示有效,false表示无效
*/
Boolean checkDataSource(GenDatasourceConf datasourceConf);
/**
* 通过数据源ID删除数据源
* @param dsIds 数据源ID数组
* @return 删除是否成功
*/
Boolean removeByDsId(Long[] dsIds);
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pig.codegen.entity.GenFieldType;
import java.util.Set;
/**
* 列属性服务接口
*
* @author lengleng
* @date 2025/05/31
*/
public interface GenFieldTypeService extends IService<GenFieldType> {
/**
* 根据tableId,获取包列表
* @param dsName 数据源名称
* @param tableName 表名称
* @return 返回包列表
*/
Set<String> getPackageByTableId(String dsName, String tableName);
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pig.codegen.entity.GenGroupEntity;
import com.pig4cloud.pig.codegen.util.vo.GroupVO;
import com.pig4cloud.pig.codegen.util.vo.TemplateGroupDTO;
/**
* 模板分组服务接口
*
* @author lengleng
* @date 2025/05/31
*/
public interface GenGroupService extends IService<GenGroupEntity> {
/**
* 保存生成模板组
* @param genTemplateGroup 模板组DTO对象
*/
void saveGenGroup(TemplateGroupDTO genTemplateGroup);
/**
* 删除分组极其关系
* @param ids
*/
void delGroupAndTemplate(Long[] ids);
/**
* 查询group数据
* @param id
*/
GroupVO getGroupVoById(Long id);
/**
* 更新group数据
* @param GroupVo
*/
void updateGroupAndTemplateById(GroupVO GroupVo);
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pig.codegen.entity.GenTableColumnEntity;
import java.util.List;
/**
* 代码生成表列服务接口
*
* @author lengleng
* @date 2025/05/31
*/
public interface GenTableColumnService extends IService<GenTableColumnEntity> {
/**
* 初始化字段列表
* @param tableFieldList 表字段实体列表
*/
void initFieldList(List<GenTableColumnEntity> tableFieldList);
/**
* 更新表字段信息
* @param dsName 数据源名称
* @param tableName 表名
* @param tableFieldList 表字段列表
*/
void updateTableField(String dsName, String tableName, List<GenTableColumnEntity> tableFieldList);
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pig.codegen.entity.GenTable;
import org.anyline.metadata.Table;
import java.util.List;
/**
* 代码生成表服务接口
*
* @author lengleng
* @date 2025/05/31
*/
public interface GenTableService extends IService<GenTable> {
/**
* 查询对应数据源的表
* @param page 分页信息
* @param table 查询条件
* @return 表
*/
IPage queryTablePage(Page<Table> page, GenTable table);
/**
* 查询表信息(列),然后插入到中间表中
* @param dsName 数据源
* @param tableName 表名
* @return GenTable
*/
GenTable queryOrBuildTable(String dsName, String tableName);
/**
* 查询表ddl 语句
* @param dsName 数据源名称
* @param tableName 表名称
* @return ddl 语句
* @throws Exception
*/
String queryTableDdl(String dsName, String tableName) throws Exception;
/**
* 查询数据源里面的全部表
* @param dsName 数据源名称
* @return table
*/
List<String> queryTableList(String dsName);
/**
* 查询表的全部字段
* @param dsName 数据源
* @param tableName 表名称
* @return column
*/
List<String> queryTableColumn(String dsName, String tableName);
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pig.codegen.entity.GenTemplateGroupEntity;
/**
* 模板分组关联服务接口
*
* @author lengleng
* @date 2025/05/31
*/
public interface GenTemplateGroupService extends IService<GenTemplateGroupEntity> {
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pig.codegen.entity.GenTemplateEntity;
import com.pig4cloud.pig.common.core.util.R;
/**
* 代码生成模板服务接口
*
* @author lengleng
* @date 2025/05/31
*/
public interface GenTemplateService extends IService<GenTemplateEntity> {
/**
* 检查版本信息
* @return 返回检查结果,包含版本信息
*/
R checkVersion();
/**
* 在线更新
* @return 更新结果
*/
R onlineUpdate();
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.service;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipOutputStream;
/**
* 代码生成服务接口
*
* @author lengleng
* @date 2025/05/31
*/
public interface GeneratorService {
/**
* 生成代码zip写出
* @param tableId 表
* @param zip 输出流
*/
void downloadCode(Long tableId, ZipOutputStream zip);
/**
* 预览代码
* @param tableId 表
* @return [{模板名称:渲染结果}]
*/
List<Map<String, String>> preview(Long tableId);
/**
* 目标目录写入渲染结果
* @param tableId 表
*/
void generatorCode(Long tableId);
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.creator.DataSourceCreator;
import com.baomidou.dynamic.datasource.creator.DataSourceProperty;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pig.codegen.entity.GenDatasourceConf;
import com.pig4cloud.pig.codegen.mapper.GenDatasourceConfMapper;
import com.pig4cloud.pig.codegen.service.GenDatasourceConfService;
import com.pig4cloud.pig.common.core.util.SpringContextHolder;
import com.pig4cloud.pig.common.datasource.enums.DsConfTypeEnum;
import com.pig4cloud.pig.common.datasource.enums.DsJdbcUrlEnum;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jasypt.encryption.StringEncryptor;
import org.springframework.stereotype.Service;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* 数据源配置服务实现类
*
* <p>
* 提供数据源的增删改查及校验功能,支持数据源密码加密存储
* </p>
*
* @author lengleng
* @date 2025/05/31
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class GenDatasourceConfServiceImpl extends ServiceImpl<GenDatasourceConfMapper, GenDatasourceConf>
implements GenDatasourceConfService {
private final StringEncryptor stringEncryptor;
private final DataSourceCreator hikariDataSourceCreator;
/**
* 保存数据源配置并进行加密处理
* @param conf 数据源配置信息
* @return 保存成功返回true,失败返回false
*/
@Override
public Boolean saveDsByEnc(GenDatasourceConf conf) {
// 校验配置合法性
if (!checkDataSource(conf)) {
return Boolean.FALSE;
}
// 添加动态数据源
addDynamicDataSource(conf);
// 更新数据库配置
conf.setPassword(stringEncryptor.encrypt(conf.getPassword()));
this.baseMapper.insert(conf);
return Boolean.TRUE;
}
/**
* 更新加密数据源
* @param conf 数据源配置信息
* @return 更新成功返回true,失败返回false
*/
@Override
public Boolean updateDsByEnc(GenDatasourceConf conf) {
if (!checkDataSource(conf)) {
return Boolean.FALSE;
}
// 先移除
DynamicRoutingDataSource dynamicRoutingDataSource = SpringContextHolder.getBean(DynamicRoutingDataSource.class);
dynamicRoutingDataSource.removeDataSource(baseMapper.selectById(conf.getId()).getName());
// 再添加
addDynamicDataSource(conf);
// 更新数据库配置
if (StrUtil.isNotBlank(conf.getPassword())) {
conf.setPassword(stringEncryptor.encrypt(conf.getPassword()));
}
this.baseMapper.updateById(conf);
return Boolean.TRUE;
}
/**
* 通过数据源ID删除数据源
* @param dsIds 数据源ID数组
* @return 删除是否成功
*/
@Override
public Boolean removeByDsId(Long[] dsIds) {
DynamicRoutingDataSource dynamicRoutingDataSource = SpringContextHolder.getBean(DynamicRoutingDataSource.class);
this.baseMapper.selectByIds(CollUtil.toList(dsIds))
.forEach(ds -> dynamicRoutingDataSource.removeDataSource(ds.getName()));
this.baseMapper.deleteByIds(CollUtil.toList(dsIds));
return Boolean.TRUE;
}
/**
* 添加动态数据源
* @param conf 数据源配置信息
*/
@Override
public void addDynamicDataSource(GenDatasourceConf conf) {
DataSourceProperty dataSourceProperty = new DataSourceProperty();
dataSourceProperty.setPoolName(conf.getName());
dataSourceProperty.setUrl(conf.getUrl());
dataSourceProperty.setUsername(conf.getUsername());
dataSourceProperty.setPassword(conf.getPassword());
DataSource dataSource = hikariDataSourceCreator.createDataSource(dataSourceProperty);
DynamicRoutingDataSource dynamicRoutingDataSource = SpringContextHolder.getBean(DynamicRoutingDataSource.class);
dynamicRoutingDataSource.addDataSource(dataSourceProperty.getPoolName(), dataSource);
}
/**
* 校验数据源配置是否有效
* @param conf 数据源配置信息
* @return 数据源配置是否有效,true表示有效
* @throws RuntimeException 数据库连接失败时抛出异常
*/
@Override
public Boolean checkDataSource(GenDatasourceConf conf) {
String url;
// JDBC 配置形式
if (DsConfTypeEnum.JDBC.getType().equals(conf.getConfType())) {
url = conf.getUrl();
}
else if (DsJdbcUrlEnum.MSSQL.getDbName().equals(conf.getDsType())) {
// 主机形式 sql server 特殊处理
DsJdbcUrlEnum urlEnum = DsJdbcUrlEnum.get(conf.getDsType());
url = String.format(urlEnum.getUrl(), conf.getHost(), conf.getPort(), conf.getDsName());
}
else {
DsJdbcUrlEnum urlEnum = DsJdbcUrlEnum.get(conf.getDsType());
url = String.format(urlEnum.getUrl(), conf.getHost(), conf.getPort(), conf.getDsName());
}
conf.setUrl(url);
try (Connection connection = DriverManager.getConnection(url, conf.getUsername(), conf.getPassword())) {
}
catch (SQLException e) {
log.error("数据源配置 {} , 获取链接失败", conf.getName(), e);
throw new RuntimeException("数据库配置错误,链接失败");
}
return Boolean.TRUE;
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pig.codegen.entity.GenFieldType;
import com.pig4cloud.pig.codegen.mapper.GenFieldTypeMapper;
import com.pig4cloud.pig.codegen.service.GenFieldTypeService;
import org.springframework.stereotype.Service;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 列属性
*
* @author pigx code generator
* @date 2023-02-06 20:16:01
*/
@Service
public class GenFieldTypeServiceImpl extends ServiceImpl<GenFieldTypeMapper, GenFieldType>
implements GenFieldTypeService {
/**
* 根据tableId,获取包列表
* @param dsName 数据源名称
* @param tableName 表名称
* @return 返回包列表
*/
@Override
public Set<String> getPackageByTableId(String dsName, String tableName) {
Set<String> importList = baseMapper.getPackageByTableId(dsName, tableName);
return importList.stream().filter(StrUtil::isNotBlank).collect(Collectors.toSet());
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pig.codegen.entity.GenGroupEntity;
import com.pig4cloud.pig.codegen.entity.GenTemplateGroupEntity;
import com.pig4cloud.pig.codegen.mapper.GenGroupMapper;
import com.pig4cloud.pig.codegen.service.GenGroupService;
import com.pig4cloud.pig.codegen.service.GenTemplateGroupService;
import com.pig4cloud.pig.codegen.util.vo.GroupVO;
import com.pig4cloud.pig.codegen.util.vo.TemplateGroupDTO;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.LinkedList;
import java.util.List;
/**
* 模板分组服务实现类
*
* @author lengleng
* @date 2025/05/31
*/
@Slf4j
@Service
@AllArgsConstructor
public class GenGroupServiceImpl extends ServiceImpl<GenGroupMapper, GenGroupEntity> implements GenGroupService {
private final GenTemplateGroupService genTemplateGroupService;
/**
* 保存模板分组信息
* @param genTemplateGroup 模板分组DTO对象,包含分组信息及关联模板ID列表
*/
@Override
public void saveGenGroup(TemplateGroupDTO genTemplateGroup) {
// 1.保存group
GenGroupEntity groupEntity = new GenGroupEntity();
BeanUtil.copyProperties(genTemplateGroup, groupEntity);
baseMapper.insert(groupEntity);
// 2.保存关系
List<GenTemplateGroupEntity> goals = new LinkedList<>();
for (Long TemplateId : genTemplateGroup.getTemplateId()) {
GenTemplateGroupEntity templateGroup = new GenTemplateGroupEntity();
templateGroup.setTemplateId(TemplateId).setGroupId(groupEntity.getId());
goals.add(templateGroup);
}
genTemplateGroupService.saveBatch(goals);
}
/**
* 按照分组ID数组删除分组及其关联模板
* @param ids 分组ID数组
*/
@Override
public void delGroupAndTemplate(Long[] ids) {
// 删除分组
this.removeBatchByIds(CollUtil.toList(ids));
// 删除关系
genTemplateGroupService.remove(Wrappers.<GenTemplateGroupEntity>lambdaQuery()
.in(GenTemplateGroupEntity::getGroupId, CollUtil.toList(ids)));
}
/**
* 根据ID查询组信息
* @param id 组ID
* @return 组信息视图对象
*/
@Override
public GroupVO getGroupVoById(Long id) {
return baseMapper.getGroupVoById(id);
}
/**
* 根据ID更新分组及其关联模板
* @param groupVo 分组VO对象,包含分组ID和模板ID列表
*/
@Override
public void updateGroupAndTemplateById(GroupVO groupVo) {
// 1.更新自身
GenGroupEntity groupEntity = new GenGroupEntity();
BeanUtil.copyProperties(groupVo, groupEntity);
this.updateById(groupEntity);
// 2.更新模板
// 2.1根据id删除之前的模板
genTemplateGroupService.remove(
Wrappers.<GenTemplateGroupEntity>lambdaQuery().eq(GenTemplateGroupEntity::getGroupId, groupVo.getId()));
// 2.2根据ids创建新的模板分组赋值
List<GenTemplateGroupEntity> goals = new LinkedList<>();
for (Long templateId : groupVo.getTemplateId()) {
goals.add(new GenTemplateGroupEntity().setGroupId(groupVo.getId()).setTemplateId(templateId));
}
genTemplateGroupService.saveBatch(goals);
}
}
package com.pig4cloud.pig.codegen.service.impl;
import cn.hutool.core.text.NamingCase;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pig.codegen.entity.GenFieldType;
import com.pig4cloud.pig.codegen.entity.GenTableColumnEntity;
import com.pig4cloud.pig.codegen.mapper.GenFieldTypeMapper;
import com.pig4cloud.pig.codegen.mapper.GenTableColumnMapper;
import com.pig4cloud.pig.codegen.service.GenTableColumnService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
/**
* 表字段信息管理服务实现类
*
* @author lengleng
* @date 2025/05/31
*/
@Service
@RequiredArgsConstructor
public class GenTableColumnServiceImpl extends ServiceImpl<GenTableColumnMapper, GenTableColumnEntity>
implements GenTableColumnService {
private final GenFieldTypeMapper fieldTypeMapper;
/**
* 初始化表单字段列表,主要是将数据库表中的字段转化为表单需要的字段数据格式,并为审计字段排序
* @param tableFieldList 表单字段列表
*/
public void initFieldList(List<GenTableColumnEntity> tableFieldList) {
// 字段类型、属性类型映射
List<GenFieldType> list = fieldTypeMapper.selectList(Wrappers.emptyWrapper());
Map<String, GenFieldType> fieldTypeMap = new LinkedHashMap<>(list.size());
list.forEach(
fieldTypeMapping -> fieldTypeMap.put(fieldTypeMapping.getColumnType().toLowerCase(), fieldTypeMapping));
// 索引计数器
AtomicInteger index = new AtomicInteger(0);
tableFieldList.forEach(field -> {
// 将字段名转化为驼峰格式
field.setAttrName(NamingCase.toCamelCase(field.getFieldName()));
// 获取字段对应的类型
GenFieldType fieldTypeMapping = fieldTypeMap.getOrDefault(field.getFieldType().toLowerCase(), null);
if (fieldTypeMapping == null) {
// 没找到对应的类型,则为Object类型
field.setAttrType("Object");
}
else {
field.setAttrType(fieldTypeMapping.getAttrType());
field.setPackageName(fieldTypeMapping.getPackageName());
}
// 设置查询类型和表单查询类型都为“=”
field.setQueryType("=");
field.setQueryFormType("text");
// 设置表单类型为文本框类型
field.setFormType("text");
// 保证审计字段最后显示
field.setSort(Objects.isNull(field.getSort()) ? index.getAndIncrement() : field.getSort());
});
}
/**
* 更新指定数据源和表名的表单字段信息
* @param dsName 数据源名称
* @param tableName 表名
* @param tableFieldList 表单字段列表
*/
@Override
public void updateTableField(String dsName, String tableName, List<GenTableColumnEntity> tableFieldList) {
AtomicInteger sort = new AtomicInteger();
this.updateBatchById(tableFieldList.stream().peek(field -> field.setSort(sort.getAndIncrement())).toList());
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.NamingCase;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pig.codegen.config.PigCodeGenDefaultProperties;
import com.pig4cloud.pig.codegen.entity.GenGroupEntity;
import com.pig4cloud.pig.codegen.entity.GenTable;
import com.pig4cloud.pig.codegen.entity.GenTableColumnEntity;
import com.pig4cloud.pig.codegen.mapper.GenTableMapper;
import com.pig4cloud.pig.codegen.service.GenGroupService;
import com.pig4cloud.pig.codegen.service.GenTableColumnService;
import com.pig4cloud.pig.codegen.service.GenTableService;
import com.pig4cloud.pig.codegen.util.AutoFillEnum;
import com.pig4cloud.pig.codegen.util.BoolFillEnum;
import com.pig4cloud.pig.codegen.util.CommonColumnFiledEnum;
import com.pig4cloud.pig.codegen.util.GenKit;
import lombok.RequiredArgsConstructor;
import org.anyline.metadata.Column;
import org.anyline.metadata.Database;
import org.anyline.metadata.Table;
import org.anyline.proxy.CacheProxy;
import org.anyline.proxy.ServiceProxy;
import org.anyline.service.AnylineService;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Objects;
/**
* 代码生成表服务实现类
*
* @author lengleng
* @date 2025/05/31
*/
@Service
@RequiredArgsConstructor
public class GenTableServiceImpl extends ServiceImpl<GenTableMapper, GenTable> implements GenTableService {
private final PigCodeGenDefaultProperties configurationProperties;
private final GenTableColumnService columnService;
private final GenGroupService genGroupService;
/**
* 查询表ddl 语句
* @param dsName 数据源名称
* @param tableName 表名称
* @return ddl 语句
* @throws Exception
*/
@Override
public String queryTableDdl(String dsName, String tableName) throws Exception {
// 手动切换数据源
DynamicDataSourceContextHolder.push(dsName);
Table table = ServiceProxy.metadata().table(tableName); // 获取表结构
table.execute(false);// 不执行SQL
ServiceProxy.ddl().create(table);
return table.getDdl();// 返回创建表的DDL
}
/**
* 查询表的全部字段
* @param dsName 数据源
* @param tableName 表名称
* @return column
*/
@Override
public List<String> queryTableColumn(String dsName, String tableName) {
// 手动切换数据源
DynamicDataSourceContextHolder.push(dsName);
CacheProxy.clear();
return ServiceProxy.metadata().columns(tableName).values().stream().map(Column::getName).toList();
}
/**
* 查询对应数据源的表
* @param page 分页信息
* @param table 查询条件
* @return 表
*/
@Override
public IPage queryTablePage(Page<Table> page, GenTable table) {
// 手动切换数据源
DynamicDataSourceContextHolder.push(table.getDsName());
CacheProxy.clear();
List<Table> tableList = ServiceProxy.metadata().tables().values().stream().filter(t -> {
if (StrUtil.isBlank(table.getTableName())) {
return true;
}
return StrUtil.containsIgnoreCase(t.getName(false), table.getTableName());
}).toList();
// 根据 page 进行分页
List<Table> records = CollUtil.page((int) page.getCurrent() - 1, (int) page.getSize(), tableList);
page.setTotal(tableList.size());
page.setRecords(records);
return page;
}
/**
* 查询数据源里面的全部表
* @param dsName 数据源名称
* @return table
*/
@Override
public List<String> queryTableList(String dsName) {
// 手动切换数据源
DynamicDataSourceContextHolder.push(dsName);
CacheProxy.clear();
return ServiceProxy.metadata().tables().values().stream().map(Table::getName).toList();
}
/**
* 查询表信息(列),然后插入到中间表中
* @param dsName 数据源
* @param tableName 表名
* @return GenTable
*/
@Override
public GenTable queryOrBuildTable(String dsName, String tableName) {
GenTable genTable = baseMapper.selectOne(
Wrappers.<GenTable>lambdaQuery().eq(GenTable::getTableName, tableName).eq(GenTable::getDsName, dsName));
// 如果 genTable 为空, 执行导入
if (Objects.isNull(genTable)) {
genTable = this.tableImport(dsName, tableName);
}
List<GenTableColumnEntity> fieldList = columnService.list(Wrappers.<GenTableColumnEntity>lambdaQuery()
.eq(GenTableColumnEntity::getDsName, dsName)
.eq(GenTableColumnEntity::getTableName, tableName)
.orderByAsc(GenTableColumnEntity::getSort));
genTable.setFieldList(fieldList);
// 查询模板分组信息
List<GenGroupEntity> groupEntities = genGroupService.list();
genTable.setGroupList(groupEntities);
return genTable;
}
/**
* 导入表结构并生成代码配置
* @param dsName 数据源名称
* @param tableName 表名
* @return 生成的表配置信息
* @Transactional 启用事务,遇到异常时回滚
*/
@Transactional(rollbackFor = Exception.class)
protected GenTable tableImport(String dsName, String tableName) {
// 手动切换数据源
DynamicDataSourceContextHolder.push(dsName);
// 查询表是否存在
GenTable table = new GenTable();
// 从数据库获取表信息
CacheProxy.clear();
AnylineService service = ServiceProxy.service();
Table tableMetadata = service.metadata().table(tableName);
Database database = service.metadata().database();
// 获取默认表配置信息 ()
table.setPackageName(configurationProperties.getPackageName());
table.setVersion(configurationProperties.getVersion());
table.setBackendPath(configurationProperties.getBackendPath());
table.setFrontendPath(configurationProperties.getFrontendPath());
table.setAuthor(configurationProperties.getAuthor());
table.setEmail(configurationProperties.getEmail());
table.setTableName(tableName);
table.setDsName(dsName);
table.setTableComment(tableMetadata.getComment());
table.setDbType(database.getDatabase().title());
table.setFormLayout(configurationProperties.getFormLayout());
table.setGeneratorType(configurationProperties.getGeneratorType());
table.setClassName(NamingCase.toPascalCase(tableName));
// 模块名称默认为 admin
table.setModuleName(configurationProperties.getModuleName());
table.setFunctionName(GenKit.getFunctionName(tableName));
table.setCreateTime(LocalDateTime.now());
// 使用默认数据源
DynamicDataSourceContextHolder.clear();
this.save(table);
// 获取原生字段数据
List<GenTableColumnEntity> tableFieldList = getGenTableColumnEntities(dsName, tableName, tableMetadata);
// 初始化字段数据
columnService.initFieldList(tableFieldList);
// 保存列数据
columnService.saveOrUpdateBatch(tableFieldList);
table.setFieldList(tableFieldList);
return table;
}
/**
* 获取表字段信息
* @param dsName 数据源信息
* @param tableName 表名称
* @param tableMetadata 表的元数据
* @return list
*/
private static @NotNull List<GenTableColumnEntity> getGenTableColumnEntities(String dsName, String tableName,
Table tableMetadata) {
List<GenTableColumnEntity> tableFieldList = new ArrayList<>();
LinkedHashMap<String, Column> columns = tableMetadata.getColumns();
columns.forEach((columnName, column) -> {
GenTableColumnEntity genTableColumnEntity = new GenTableColumnEntity();
genTableColumnEntity.setTableName(tableName);
genTableColumnEntity.setDsName(dsName);
genTableColumnEntity.setFieldName(column.getName());
genTableColumnEntity.setFieldComment(column.getComment());
genTableColumnEntity.setFieldType(column.getTypeName());
genTableColumnEntity.setPrimaryPk(
column.isPrimaryKey() == 1 ? BoolFillEnum.TRUE.getValue() : BoolFillEnum.FALSE.getValue());
genTableColumnEntity.setAutoFill(AutoFillEnum.DEFAULT.name());
genTableColumnEntity.setFormItem(BoolFillEnum.TRUE.getValue());
genTableColumnEntity.setGridItem(BoolFillEnum.TRUE.getValue());
// 审计字段处理
if (EnumUtil.contains(CommonColumnFiledEnum.class, column.getName())) {
CommonColumnFiledEnum commonColumnFiledEnum = CommonColumnFiledEnum.valueOf(column.getName());
genTableColumnEntity.setFormItem(commonColumnFiledEnum.getFormItem());
genTableColumnEntity.setGridItem(commonColumnFiledEnum.getGridItem());
genTableColumnEntity.setAutoFill(commonColumnFiledEnum.getAutoFill());
genTableColumnEntity.setSort(commonColumnFiledEnum.getSort());
}
tableFieldList.add(genTableColumnEntity);
});
return tableFieldList;
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pig.codegen.entity.GenTemplateGroupEntity;
import com.pig4cloud.pig.codegen.mapper.GenTemplateGroupMapper;
import com.pig4cloud.pig.codegen.service.GenTemplateGroupService;
import org.springframework.stereotype.Service;
/**
* 模板分组关联表服务实现类
*
* @author lengleng
* @date 2025/05/31
*/
@Service
public class GenTemplateGroupServiceImpl extends ServiceImpl<GenTemplateGroupMapper, GenTemplateGroupEntity>
implements GenTemplateGroupService {
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.service.impl;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.smallbun.screw.core.constant.DefaultConstants;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pig.codegen.config.PigCodeGenDefaultProperties;
import com.pig4cloud.pig.codegen.entity.GenGroupEntity;
import com.pig4cloud.pig.codegen.entity.GenTemplateEntity;
import com.pig4cloud.pig.codegen.entity.GenTemplateGroupEntity;
import com.pig4cloud.pig.codegen.mapper.GenGroupMapper;
import com.pig4cloud.pig.codegen.mapper.GenTemplateGroupMapper;
import com.pig4cloud.pig.codegen.mapper.GenTemplateMapper;
import com.pig4cloud.pig.codegen.service.GenTemplateService;
import com.pig4cloud.pig.codegen.util.vo.GenTemplateFileVO;
import com.pig4cloud.pig.common.core.exception.CheckedException;
import com.pig4cloud.pig.common.core.util.R;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 代码生成模板服务实现类
*
* @author lengleng
* @date 2025/05/31
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class GenTemplateServiceImpl extends ServiceImpl<GenTemplateMapper, GenTemplateEntity>
implements GenTemplateService {
private final GenTemplateGroupMapper genTemplateGroupMapper;
private final GenGroupMapper genGroupMapper;
private final PigCodeGenDefaultProperties defaultProperties;
/**
* 在线更新模板组
* @return 更新结果,包含成功或失败信息
* @throws Exception 事务执行过程中发生异常时抛出
*/
@Override
@Transactional(rollbackFor = Exception.class)
public R onlineUpdate() {
// 获取 config.json 和 version 文件
Map<String, Object> configAndVersion = getConfigAndVersion();
JSONObject configJsonObj = (JSONObject) configAndVersion.get("configJsonObj");
String versionFile = (String) configAndVersion.get("versionFile");
// 查询出全部的模板组名称
Set<String> cgtmConfigGroupNames = configJsonObj.keySet();
String cgtmConfigGroupName = cgtmConfigGroupNames.iterator().next();
// 根据模板组名称+version 查询是否存在,不存在则新增,存在跳过
boolean exists = genGroupMapper.exists(Wrappers.<GenGroupEntity>lambdaQuery()
.eq(GenGroupEntity::getGroupName, cgtmConfigGroupName + versionFile));
if (exists) {
return R.failed("已是最新版本,无需更新!");
}
// 插入新的模板组(名称 + VERSION), 再解析 config.json group 里面的所有模板
insertTemplateFiles(versionFile, configJsonObj, cgtmConfigGroupName);
return R.ok("更新成功,版本号:" + versionFile);
}
/**
* 检查版本
* @return 返回检查结果,包含版本是否存在信息
*/
public R checkVersion() {
// 关闭在线更新提示
if (!defaultProperties.isAutoCheckVersion()) {
return R.ok(true);
}
// 获取 config.json 和 version 文件
Map<String, Object> configAndVersion = getConfigAndVersion();
JSONObject configJsonObj = (JSONObject) configAndVersion.get("configJsonObj");
String versionFile = (String) configAndVersion.get("versionFile");
// 查询出全部的模板组名称
Set<String> cgtmConfigGroupNames = configJsonObj.keySet();
String cgtmConfigGroupName = cgtmConfigGroupNames.iterator().next();
// 根据模板组名称+version 查询是否存在,不存在则新增,存在跳过
boolean exists = genGroupMapper.exists(Wrappers.<GenGroupEntity>lambdaQuery()
.eq(GenGroupEntity::getGroupName, cgtmConfigGroupName + versionFile));
return R.ok(exists);
}
/**
* 获取配置和版本
* @return {@link Map }<{@link String }, {@link Object }>
*/
private Map<String, Object> getConfigAndVersion() {
// 获取 config.json 和 version 文件
String configFile = getCGTMFile("config.json");
String versionFile = getCGTMFile("VERSION");
// 解析 config.json
JSONObject configJsonObj = JSONUtil.parseObj(configFile);
// 将 configJsonObj 和 versionFile 放入 Map 中
Map<String, Object> configAndVersion = new HashMap<>();
configAndVersion.put("configJsonObj", configJsonObj);
configAndVersion.put("versionFile", versionFile);
return configAndVersion;
}
/**
* 插入模板文件
* @param version 版本
* @param configJsonObj config.json
* @param groupName 组名称
*/
private void insertTemplateFiles(String version, JSONObject configJsonObj, String groupName) {
// 创建新的 group
GenGroupEntity genGroupEntity = new GenGroupEntity();
genGroupEntity.setGroupName(groupName + version);
genGroupMapper.insert(genGroupEntity);
// 解析json配置文件
List<GenTemplateFileVO> templateFileVOList = configJsonObj.getBeanList(groupName, GenTemplateFileVO.class);
for (GenTemplateFileVO genTemplateFileVO : templateFileVOList) {
// 1. 获取模板文件
String templateFile = getCGTMFile(genTemplateFileVO.getTemplateFile());
// 2. 插入模板文件
GenTemplateEntity genTemplateEntity = new GenTemplateEntity();
genTemplateEntity.setTemplateName(genTemplateFileVO.getTemplateName() + version);
genTemplateEntity.setTemplateDesc(genTemplateFileVO.getTemplateName() + version);
genTemplateEntity.setTemplateCode(templateFile);
genTemplateEntity.setGeneratorPath(genTemplateFileVO.getGeneratorPath());
baseMapper.insert(genTemplateEntity);
// 3. 插入模板组关联
GenTemplateGroupEntity genTemplateGroupEntity = new GenTemplateGroupEntity();
genTemplateGroupEntity.setTemplateId(genTemplateEntity.getId());
genTemplateGroupEntity.setGroupId(genGroupEntity.getId());
genTemplateGroupMapper.insert(genTemplateGroupEntity);
}
}
/**
* 获取 cgtmfile
* @param fileName 文件名
* @return {@link String }
*/
private String getCGTMFile(String fileName) {
HttpResponse response = HttpRequest
.get(String.format("%s/CGTM/raw/master/%s", DefaultConstants.CGTM_URL, fileName))
.execute();
if (response.getStatus() == HttpStatus.HTTP_OK || StrUtil.isNotBlank(response.body())) {
return response.body();
}
else {
log.warn("在线更新模板失败:{} ,Http Code:{}", fileName, response.getStatus());
throw new CheckedException("在线更新模板失败,任务终止!");
}
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.codegen.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.NamingCase;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pig.codegen.config.PigCodeGenDefaultProperties;
import com.pig4cloud.pig.codegen.entity.GenTable;
import com.pig4cloud.pig.codegen.entity.GenTableColumnEntity;
import com.pig4cloud.pig.codegen.entity.GenTemplateEntity;
import com.pig4cloud.pig.codegen.service.*;
import com.pig4cloud.pig.codegen.util.VelocityKit;
import com.pig4cloud.pig.codegen.util.vo.GroupVO;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringBootVersion;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* 代码生成器服务实现类
*
* @author lengleng
* @date 2025/05/31
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class GeneratorServiceImpl implements GeneratorService {
private final PigCodeGenDefaultProperties configurationProperties;
private final GenTableColumnService columnService;
private final GenFieldTypeService fieldTypeService;
private final GenTableService tableService;
private final GenGroupService genGroupService;
/**
* 生成代码zip写出
* @param tableId 表
* @param zip 输出流
*/
@Override
@SneakyThrows
public void downloadCode(Long tableId, ZipOutputStream zip) {
// 数据模型
Map<String, Object> dataModel = getDataModel(tableId);
Long style = (Long) dataModel.get("style");
GroupVO groupVo = genGroupService.getGroupVoById(style);
List<GenTemplateEntity> templateList = groupVo.getTemplateList();
String frontendPath = configurationProperties.getFrontendPath();
String backendPath = configurationProperties.getBackendPath();
for (GenTemplateEntity template : templateList) {
String templateCode = template.getTemplateCode();
String generatorPath = template.getGeneratorPath();
dataModel.put("frontendPath", frontendPath);
dataModel.put("backendPath", backendPath);
String content = VelocityKit.renderStr(templateCode, dataModel);
String path = VelocityKit.renderStr(generatorPath, dataModel);
// 添加到zip
zip.putNextEntry(new ZipEntry(path));
IoUtil.writeUtf8(zip, false, content);
zip.flush();
zip.closeEntry();
}
}
/**
* 表达式优化的预览代码方法
* @param tableId 表
* @return [{模板名称:渲染结果}]
*/
@Override
@SneakyThrows
public List<Map<String, String>> preview(Long tableId) {
// 数据模型
Map<String, Object> dataModel = getDataModel(tableId);
Long style = (Long) dataModel.get("style");
// 获取模板列表,Lambda 表达式简化代码
List<GenTemplateEntity> templateList = genGroupService.getGroupVoById(style).getTemplateList();
String frontendPath = configurationProperties.getFrontendPath();
String backendPath = configurationProperties.getBackendPath();
return templateList.stream().map(template -> {
String templateCode = template.getTemplateCode();
String generatorPath = template.getGeneratorPath();
// 预览模式下, 使用相对路径展示
dataModel.put("frontendPath", frontendPath);
dataModel.put("backendPath", backendPath);
String content = VelocityKit.renderStr(templateCode, dataModel);
String path = VelocityKit.renderStr(generatorPath, dataModel);
// 使用 map 简化代码
return new HashMap<String, String>(4) {
{
put("code", content);
put("codePath", path);
}
};
}).collect(Collectors.toList());
}
/**
* 目标目录写入渲染结果方法
* @param tableId 表
*/
@Override
public void generatorCode(Long tableId) {
// 数据模型
Map<String, Object> dataModel = getDataModel(tableId);
Long style = (Long) dataModel.get("style");
// 获取模板列表,Lambda 表达式简化代码
List<GenTemplateEntity> templateList = genGroupService.getGroupVoById(style).getTemplateList();
templateList.forEach(template -> {
String templateCode = template.getTemplateCode();
String generatorPath = template.getGeneratorPath();
String content = VelocityKit.renderStr(templateCode, dataModel);
String path = VelocityKit.renderStr(generatorPath, dataModel);
FileUtil.writeUtf8String(content, path);
});
}
/**
* 通过 Lambda 表达式优化的获取数据模型方法
* @param tableId 表格 ID
* @return 数据模型 Map 对象
*/
private Map<String, Object> getDataModel(Long tableId) {
// 获取表格信息
GenTable table = tableService.getById(tableId);
// 获取字段列表
List<GenTableColumnEntity> fieldList = columnService.lambdaQuery()
.eq(GenTableColumnEntity::getDsName, table.getDsName())
.eq(GenTableColumnEntity::getTableName, table.getTableName())
.orderByAsc(GenTableColumnEntity::getSort)
.list();
table.setFieldList(fieldList);
// 创建数据模型对象
Map<String, Object> dataModel = new HashMap<>();
// 填充数据模型
dataModel.put("opensource", true);
dataModel.put("isSpringBoot3", isSpringBoot3());
dataModel.put("dbType", table.getDbType());
dataModel.put("package", table.getPackageName());
dataModel.put("packagePath", table.getPackageName().replace(".", "/"));
dataModel.put("version", table.getVersion());
dataModel.put("moduleName", table.getModuleName());
dataModel.put("ModuleName", StrUtil.upperFirst(table.getModuleName()));
dataModel.put("functionName", table.getFunctionName());
dataModel.put("FunctionName", StrUtil.upperFirst(table.getFunctionName()));
dataModel.put("formLayout", table.getFormLayout());
dataModel.put("style", table.getStyle());
dataModel.put("author", table.getAuthor());
dataModel.put("datetime", DateUtil.now());
dataModel.put("date", DateUtil.today());
setFieldTypeList(dataModel, table);
// 获取导入的包列表
Set<String> importList = fieldTypeService.getPackageByTableId(table.getDsName(), table.getTableName());
dataModel.put("importList", importList);
dataModel.put("tableName", table.getTableName());
dataModel.put("tableComment", table.getTableComment());
dataModel.put("className", StrUtil.lowerFirst(table.getClassName()));
dataModel.put("ClassName", table.getClassName());
dataModel.put("fieldList", table.getFieldList());
dataModel.put("backendPath", table.getBackendPath());
dataModel.put("frontendPath", table.getFrontendPath());
// 设置子表
String childTableName = table.getChildTableName();
if (StrUtil.isNotBlank(childTableName)) {
List<GenTableColumnEntity> childFieldList = columnService.lambdaQuery()
.eq(GenTableColumnEntity::getDsName, table.getDsName())
.eq(GenTableColumnEntity::getTableName, table.getChildTableName())
.list();
dataModel.put("childFieldList", childFieldList);
dataModel.put("childTableName", childTableName);
dataModel.put("mainField", NamingCase.toCamelCase(table.getMainField()));
dataModel.put("childField", NamingCase.toCamelCase(table.getChildField()));
dataModel.put("ChildClassName", NamingCase.toPascalCase(childTableName));
dataModel.put("childClassName", StrUtil.lowerFirst(NamingCase.toPascalCase(childTableName)));
// 设置是否是多租户模式 (判断字段列表中是否包含 tenant_id 字段)
childFieldList.stream()
.filter(genTableColumnEntity -> genTableColumnEntity.getFieldName().equals("tenant_id"))
.findFirst()
.ifPresent(columnEntity -> dataModel.put("isChildTenant", true));
}
// 设置是否是多租户模式 (判断字段列表中是否包含 tenant_id 字段)
table.getFieldList()
.stream()
.filter(genTableColumnEntity -> genTableColumnEntity.getFieldName().equals("tenant_id"))
.findFirst()
.ifPresent(columnEntity -> dataModel.put("isTenant", true));
return dataModel;
}
/**
* 判断当前是否是 SpringBoot3 版本
* @return true/fasle
*/
private boolean isSpringBoot3() {
return StrUtil.startWith(SpringBootVersion.getVersion(), "3");
}
/**
* 将表字段按照类型分组并存储到数据模型中
* @param dataModel 存储数据的 Map 对象
* @param table 表信息对象
*/
private void setFieldTypeList(Map<String, Object> dataModel, GenTable table) {
// 按字段类型分组,使用 Map 存储不同类型的字段列表
Map<Boolean, List<GenTableColumnEntity>> typeMap = table.getFieldList()
.stream()
.collect(Collectors.partitioningBy(columnEntity -> BooleanUtil.toBoolean(columnEntity.getPrimaryPk())));
// 从分组后的 Map 中获取不同类型的字段列表
List<GenTableColumnEntity> primaryList = typeMap.get(true);
List<GenTableColumnEntity> formList = typeMap.get(false)
.stream()
.filter(columnEntity -> BooleanUtil.toBoolean(columnEntity.getFormItem()))
.toList();
List<GenTableColumnEntity> gridList = typeMap.get(false)
.stream()
.filter(columnEntity -> BooleanUtil.toBoolean(columnEntity.getGridItem()))
.toList();
List<GenTableColumnEntity> queryList = typeMap.get(false)
.stream()
.filter(columnEntity -> BooleanUtil.toBoolean(columnEntity.getQueryItem()))
.toList();
if (CollUtil.isNotEmpty(primaryList)) {
dataModel.put("pk", primaryList.get(0));
}
dataModel.put("primaryList", primaryList);
dataModel.put("formList", formList);
dataModel.put("gridList", gridList);
dataModel.put("queryList", queryList);
}
}
package com.pig4cloud.pig.codegen.util;
/**
* 字段自动填充 枚举
*
* @author 阿沐 babamu@126.com
*/
public enum AutoFillEnum {
DEFAULT, INSERT, UPDATE, INSERT_UPDATE, CREATE;
}
package com.pig4cloud.pig.codegen.util;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* boolean 类型枚举
*
*/
@Getter
@RequiredArgsConstructor
public enum BoolFillEnum {
/**
* true
*/
TRUE("1"),
/**
* false
*/
FALSE("0");
private final String value;
}
package com.pig4cloud.pig.codegen.util;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author lengleng
* @date 2023/3/12
* <p>
* 通用字段的填充策略和显示策略
*/
@Getter
@AllArgsConstructor
public enum CommonColumnFiledEnum {
/**
* create_by 字段
*/
create_by("0", "0", AutoFillEnum.INSERT.name(), 100),
/**
* create_time 字段
*/
create_time("0", "0", AutoFillEnum.INSERT.name(), 101),
/**
* update_by 字段
*/
update_by("0", "0", AutoFillEnum.INSERT_UPDATE.name(), 102),
/**
* update_time 字段
*/
update_time("0", "0", AutoFillEnum.INSERT_UPDATE.name(), 103),
/**
* del_flag 字段
*/
del_flag("0", "0", AutoFillEnum.DEFAULT.name(), 104),
/**
* tenant_id 字段
*/
tenant_id("0", "0", AutoFillEnum.DEFAULT.name(), 105);
/**
* 表单是否默认显示 1/0
*/
private String formItem;
/**
* 表格是否默认显示 1/0
*/
private String gridItem;
/**
* 自动填充策略
*/
private String autoFill;
/**
* 排序值
*/
private Integer sort;
}
package com.pig4cloud.pig.codegen.util;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import java.util.HashSet;
import java.util.List;
/**
* 字典工具类:提供字典相关操作的工具方法
*
* @author lengleng
* @date 2025/05/31
*/
public class DictTool {
/**
* 将字段列表转换为带有双引号的逗号分隔的字符串
* @return 带有双引号的逗号分隔的字符串
*/
public static String quotation(List<String> fields) {
return CollUtil.join(new HashSet<>(fields), StrUtil.COMMA, s -> String.format("'%s'", s));
}
/**
* 将字段列表转换为逗号分隔的字符串
* @return 逗号分隔的字符串
*/
public static String format(List<String> fields) {
return CollUtil.join(new HashSet<>(fields), StrUtil.COMMA);
}
}
package com.pig4cloud.pig.codegen.util;
import cn.hutool.core.util.StrUtil;
import lombok.experimental.UtilityClass;
/**
* 代码生成工具类
*
* @author lengleng
* @date 2025/05/31
*/
@UtilityClass
public class GenKit {
/**
* 获取功能名 sys_a_b sysAb
* @param tableName 表名
* @return 功能名
*/
public String getFunctionName(String tableName) {
return StrUtil.toCamelCase(tableName);
}
/**
* 获取模块名称
* @param packageName 包名
* @return 功能名
*/
public String getModuleName(String packageName) {
return StrUtil.subAfter(packageName, ".", true);
}
}
package com.pig4cloud.pig.codegen.util;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 代码生成主题
*
* @author 冷冷
*/
@Getter
@AllArgsConstructor
public enum GeneratorStyleEnum {
VFORM_JSON(1L, "element-plus 风格"),
VFORM_FORM(2L, "uview 风格");
/**
* 对应模板ID
*/
private Long templateId;
/**
* 描述
*/
private String desc;
}
package com.pig4cloud.pig.codegen.util;
import cn.hutool.core.text.NamingCase;
/**
* 命名规则处理工具类,提供驼峰、下划线等命名格式转换功能
*
* @author lengleng
* @date 2025/05/31
*/
public class NamingCaseTool {
/**
* 根据字段名生成对应的get方法名
* @param in 字段名称
* @return 生成的get方法名
*/
public static String getProperty(String in) {
return String.format("get%s", NamingCase.toPascalCase(in));
}
/**
* 根据输入字符串生成setter方法名
* @param in 输入字符串
* @return 生成的setter方法名
*/
public static String setProperty(String in) {
return String.format("set%s", NamingCase.toPascalCase(in));
}
/**
* 将字符串转换为帕斯卡命名格式(首字母大写)
* @param in 输入字符串
* @return 首字母大写的字符串
*/
public static String pascalCase(String in) {
return String.format(NamingCase.toPascalCase(in));
}
}
package com.pig4cloud.pig.codegen.util;
import cn.hutool.core.util.CharsetUtil;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.tools.generic.DateTool;
import org.apache.velocity.tools.generic.MathTool;
import org.springframework.stereotype.Service;
import java.io.StringWriter;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
/**
* Velocity模板引擎工具类,提供模板渲染和字符串渲染功能
*
* @author lengleng
* @date 2025/05/31
*/
@Service
public class VelocityKit {
/**
* Velocity 模板渲染方法
* @param template 模板路径
* @param map 数据模型
* @return 渲染后的字符串结果
*/
public static String render(String template, Map<String, Object> map) {
// 设置velocity资源加载器
Properties prop = new Properties();
prop.put("resource.loader.file.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
Velocity.init(prop);
VelocityContext context = new VelocityContext(map);
// 函数库,使用 Lambda 表达式简化代码
Optional.of(new MathTool()).ifPresent(mt -> context.put("math", mt));
Optional.of(new DateTool()).ifPresent(dt -> context.put("dateTool", dt));
Optional.of(new DictTool()).ifPresent(dt -> context.put("dict", dt));
Optional.of(new NamingCaseTool()).ifPresent(nct -> context.put("str", nct));
// 渲染模板,使用 Lambda 表达式简化代码
StringWriter sw = new StringWriter();
Optional.ofNullable(Velocity.getTemplate(template, CharsetUtil.UTF_8)).ifPresent(tpl -> tpl.merge(context, sw));
return sw.toString();
}
/**
* 渲染文本
* @param str 待渲染的字符串
* @param dataModel 数据模型
* @return 渲染后的字符串
*/
public static String renderStr(String str, Map<String, Object> dataModel) {
// 设置velocity资源加载器
Velocity.init();
StringWriter stringWriter = new StringWriter();
VelocityContext context = new VelocityContext(dataModel);
// 函数库
context.put("math", new MathTool());
context.put("dateTool", new DateTool());
context.put("dict", new DictTool());
context.put("str", new NamingCaseTool());
Velocity.evaluate(context, stringWriter, "renderStr", str);
return stringWriter.toString();
}
}
package com.pig4cloud.pig.codegen.util.vo;
/*
* Copyright (c) 2018-2025, luolin All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: luolin (766488893@qq.com)
*/
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 自动创建表管理
*
* @author luolin
* @date 2022-09-23 21:56:11
*/
@Data
@Schema(description = "自动创建表管理")
public class GenCreateTableVO {
/**
* 主键ID
*/
@Schema(description = "主键ID")
private Long id;
/**
* 表名称
*/
@NotBlank(message = "表名称不能为空")
@Schema(description = "表名称")
private String tableName;
/**
* 表注释
*/
@NotBlank(message = "表注释不能为空")
@Schema(description = "表注释")
private String comments;
/**
* 数据源名称
*/
@NotBlank(message = "数据源名称不能为空")
@Schema(description = "数据源名称")
private String dsName;
/**
* 主键策略
*/
@NotBlank(message = "主键策略不能为空")
@Schema(description = "主键策略")
private String pkPolicy;
/**
* 创建人
*/
@Schema(description = "创建人")
private Long createUser;
/**
* 创建时间
*/
@Schema(description = "创建时间")
private LocalDateTime createTime;
/**
* 表字段信息
*/
@Schema(description = "表字段信息")
private String columnsInfo;
/**
* 字段信息
*/
@Schema(description = "字段信息")
private String columnInfo;
}
package com.pig4cloud.pig.codegen.util.vo;
import lombok.Data;
/**
* @author lengleng
* @date 2024/7/13
* <p>
* CGTM 文件路径
* <p>
* { "templateName": "Controller", "generatorPath":
* "${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java",
* "templateDesc": "后台Controller", "templateFile": "temps/Controller" },
*/
@Data
public class GenTemplateFileVO {
/**
* 模板名称
*/
private String templateName;
/**
* 路径
*/
private String generatorPath;
/**
* 模板 desc
*/
private String templateDesc;
/**
* 模板文件
*/
private String templateFile;
}
package com.pig4cloud.pig.codegen.util.vo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.pig4cloud.pig.codegen.entity.GenTemplateEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Data
public class GroupVO {
/**
* id
*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "id")
private Long id;
/**
* 分组名称
*/
@Schema(description = "分组名称")
private String groupName;
/**
* 分组描述
*/
@Schema(description = "分组描述")
private String groupDesc;
/**
* 模板ids
*/
@Schema(description = "拥有的模板列表")
private Long[] templateId;
/**
* 模板列表
*/
@Schema(description = "拥有的模板列表")
private List<GenTemplateEntity> templateList;
}
package com.pig4cloud.pig.codegen.util.vo;
import lombok.Data;
/**
* @author lengleng
* @date 2022/5/2
*/
@Data
public class SqlDto {
/**
* 数据源ID
*/
private String dsName;
/**
* sql脚本
*/
private String sql;
}
package com.pig4cloud.pig.codegen.util.vo;
import java.io.Serial;
import java.util.List;
import com.pig4cloud.pig.codegen.entity.GenGroupEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author weimeilayer@gmail.com ✨
* @date 💓💕 2025年5月30日 🐬🐇 💓💕
*/
@Data
@Schema(description = "模板传输对象")
@EqualsAndHashCode(callSuper = true)
public class TemplateGroupDTO extends GenGroupEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 模板id集合
*/
@Schema(description = "模板id集合")
private List<Long> templateId;
}
server:
port: 5002
spring:
application:
name: @artifactId@
cloud:
nacos:
username: @nacos.username@
password: @nacos.password@
discovery:
server-addr: ${NACOS_HOST:127.0.0.1}:${NACOS_PORT:8848}
config:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
config:
import:
- nacos:application-@profiles.active@.yml
- nacos:${spring.application.name}-@profiles.active@.yml
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2018-2025, lengleng All rights reserved.
~
~ Redistribution and use in source and binary forms, with or without
~ modification, are permitted provided that the following conditions are met:
~
~ Redistributions of source code must retain the above copyright notice,
~ this list of conditions and the following disclaimer.
~ Redistributions in binary form must reproduce the above copyright
~ notice, this list of conditions and the following disclaimer in the
~ documentation and/or other materials provided with the distribution.
~ Neither the name of the pig4cloud.com developer nor the names of its
~ contributors may be used to endorse or promote products derived from
~ this software without specific prior written permission.
~ Author: lengleng (wangiegie@gmail.com)
-->
<!--
小技巧: 在根pom里面设置统一存放路径,统一管理方便维护
<properties>
<log-path>/Users/lengleng</log-path>
</properties>
1. 其他模块加日志输出,直接copy本文件放在resources 目录即可
2. 注意修改 <property name="${log-path}/log.path" value=""/> 的value模块
-->
<configuration debug="false" scan="false">
<property name="log.path" value="logs/${project.artifactId}"/>
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<property name="FILE_LOG_PATTERN" value=":%d{yyyy-MM-dd HH:mm:ss.SSS} %5p %t %c{1}: %m%n"/>
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" class="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
class="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
class="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- Console log output -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- Log file debug output -->
<appender name="debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/debug.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/%d{yyyy-MM, aux}/debug.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- Log file error output -->
<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/%d{yyyy-MM}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
</appender>
<logger name="org.activiti.engine.impl.db" level="DEBUG">
<appender-ref ref="debug"/>
</logger>
<!--nacos 心跳 INFO 屏蔽-->
<logger name="com.alibaba.nacos" level="OFF">
<appender-ref ref="error"/>
</logger>
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
<root level="debug">
<appender-ref ref="console"/>
<appender-ref ref="debug"/>
<appender-ref ref="error"/>
</root>
</configuration>
<?xml version="1.0" encoding="UTF-8"?>
<!--
~
~ Copyright (c) 2018-2025, lengleng All rights reserved.
~
~ Redistribution and use in source and binary forms, with or without
~ modification, are permitted provided that the following conditions are met:
~
~ Redistributions of source code must retain the above copyright notice,
~ this list of conditions and the following disclaimer.
~ Redistributions in binary form must reproduce the above copyright
~ notice, this list of conditions and the following disclaimer in the
~ documentation and/or other materials provided with the distribution.
~ Neither the name of the pig4cloud.com developer nor the names of its
~ contributors may be used to endorse or promote products derived from
~ this software without specific prior written permission.
~ Author: lengleng (wangiegie@gmail.com)
~
-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pig4cloud.pig.codegen.mapper.GenFieldTypeMapper">
<resultMap id="fieldTypeMap" type="com.pig4cloud.pig.codegen.entity.GenFieldType">
<id property="id" column="id"/>
<result property="columnType" column="column_type"/>
<result property="attrType" column="attr_type"/>
<result property="packageName" column="package_name"/>
<result property="createTime" column="create_time"/>
</resultMap>
<select id="getPackageByTableId" resultType="String">
select t1.package_name
from gen_field_type t1,
gen_table_column t2
where t1.attr_type = t2.attr_type
and t2.ds_name = #{dsName} and t2.table_name = #{tableName}
</select>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pig4cloud.pig.codegen.mapper.GenGroupMapper">
<resultMap id="genGroupMap" type="com.pig4cloud.pig.codegen.util.vo.GroupVO">
<id property="id" column="group_id"/>
<result property="groupName" column="group_name"/>
<result property="groupDesc" column="group_desc"/>
<collection property="templateList" ofType="com.pig4cloud.pig.codegen.entity.GenTemplateEntity"
select="com.pig4cloud.pig.codegen.mapper.GenTemplateMapper.listTemplateById" column="group_id">
</collection>
</resultMap>
<select id="getGroupVoById" resultMap="genGroupMap">
SELECT
g.id as group_id ,
g.group_name ,
g.group_desc
FROM
gen_group g
WHERE g.id = #{id}
</select>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!--
~
~ Copyright (c) 2018-2025, lengleng All rights reserved.
~
~ Redistribution and use in source and binary forms, with or without
~ modification, are permitted provided that the following conditions are met:
~
~ Redistributions of source code must retain the above copyright notice,
~ this list of conditions and the following disclaimer.
~ Redistributions in binary form must reproduce the above copyright
~ notice, this list of conditions and the following disclaimer in the
~ documentation and/or other materials provided with the distribution.
~ Neither the name of the pig4cloud.com developer nor the names of its
~ contributors may be used to endorse or promote products derived from
~ this software without specific prior written permission.
~ Author: lengleng (wangiegie@gmail.com)
~
-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pig4cloud.pig.codegen.mapper.GenTableMapper">
<resultMap id="tableMap" type="com.pig4cloud.pig.codegen.entity.GenTable">
<id property="id" column="id"/>
<result property="tableName" column="table_name"/>
<result property="className" column="class_name"/>
<result property="tableComment" column="table_comment"/>
<result property="author" column="author"/>
<result property="email" column="email"/>
<result property="packageName" column="package_name"/>
<result property="version" column="version"/>
<result property="generatorType" column="generator_type"/>
<result property="backendPath" column="backend_path"/>
<result property="frontendPath" column="frontend_path"/>
<result property="moduleName" column="module_name"/>
<result property="functionName" column="function_name"/>
<result property="formLayout" column="form_layout"/>
<result property="baseclassId" column="baseclass_id"/>
<result property="createTime" column="create_time"/>
</resultMap>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pig4cloud.pig.codegen.mapper.GenTemplateGroupMapper">
<resultMap id="genTemplateGroupMap" type="com.pig4cloud.pig.codegen.entity.GenTemplateGroupEntity">
<id property="groupId" column="group_id"/>
<id property="templateId" column="template_id"/>
</resultMap>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pig4cloud.pig.codegen.mapper.GenTemplateMapper">
<resultMap id="genTemplateMap" type="com.pig4cloud.pig.codegen.entity.GenTemplateEntity">
<id property="id" column="id"/>
<result property="templateName" column="template_name"/>
<result property="generatorPath" column="generator_path"/>
<result property="templateDesc" column="template_desc"/>
<result property="templateCode" column="template_code"/>
</resultMap>
<select id="listTemplateById" resultType="com.pig4cloud.pig.codegen.entity.GenTemplateEntity">
SELECT
t.id as id,t.template_name,t.generator_path,t.template_desc,t.template_code
FROM gen_template t ,
gen_template_group tg
WHERE t.id = tg.template_id
AND t.del_flag = '0'
and tg.group_id = #{groupId}
</select>
</mapper>
FROM registry.cn-hangzhou.aliyuncs.com/dockerhub_mirror/java:21-anolis
WORKDIR /pig-monitor
ARG JAR_FILE=target/pig-monitor.jar
COPY ${JAR_FILE} app.jar
EXPOSE 5001
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms128m -Xmx256m -Djava.security.egd=file:/dev/./urandom"
CMD sleep 60; java $JAVA_OPTS -jar app.jar
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-visual</artifactId>
<version>${revision}</version>
</parent>
<artifactId>pig-monitor</artifactId>
<packaging>jar</packaging>
<description>pig 监控模块,基于 spring boot admin</description>
<dependencies>
<!--监控服务端-->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>${spring-boot-admin.version}</version>
</dependency>
<!--注册中心客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--配置中心客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--undertow容器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<!--web 模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
/*
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.pig4cloud.pig.monitor;
import de.codecentric.boot.admin.server.config.EnableAdminServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* 监控中心应用启动类
*
* @author lengleng
* @date 2018/06/21
*/
@EnableAdminServer
@EnableDiscoveryClient
@SpringBootApplication
public class PigMonitorApplication {
public static void main(String[] args) {
SpringApplication.run(PigMonitorApplication.class, args);
}
}
package com.pig4cloud.pig.monitor.config;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.WebUtils;
import java.io.IOException;
/**
* 自定义CSRF过滤器,用于处理CSRF令牌相关逻辑
*
* @author lengleng
* @date 2025/05/31
*/
public class CustomCsrfFilter extends OncePerRequestFilter {
public static final String CSRF_COOKIE_NAME = "XSRF-TOKEN";
/**
* 处理CSRF令牌的过滤器内部逻辑
* @param request HTTP请求
* @param response HTTP响应
* @param filterChain 过滤器链
* @throws ServletException 如果发生servlet相关异常
* @throws IOException 如果发生I/O异常
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, CSRF_COOKIE_NAME);
String token = csrf.getToken();
if (cookie == null || token != null && !token.equals(cookie.getValue())) {
cookie = new Cookie(CSRF_COOKIE_NAME, token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
}
/*
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.pig4cloud.pig.monitor.config;
import de.codecentric.boot.admin.server.config.AdminServerProperties;
import jakarta.servlet.DispatcherType;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import java.util.UUID;
/**
* 安全配置类:用于配置Spring Security相关设置
*
* @author lengleng
* @date 2025/05/31
*/
@Configuration(proxyBeanMethods = false)
public class SecuritySecureConfig {
private final AdminServerProperties adminServer;
private final SecurityProperties security;
/**
* 构造函数,初始化安全管理配置
* @param adminServer 管理服务器配置属性
* @param security 安全配置属性
*/
public SecuritySecureConfig(AdminServerProperties adminServer, SecurityProperties security) {
this.adminServer = adminServer;
this.security = security;
}
/**
* 配置Spring Security过滤器链
* @param http HttpSecurity对象,用于配置安全策略
* @return 配置好的SecurityFilterChain实例
* @throws Exception 配置过程中可能抛出的异常
*/
@Bean
protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setTargetUrlParameter("redirectTo");
successHandler.setDefaultTargetUrl(this.adminServer.path("/"));
http.authorizeHttpRequests((authorizeRequests) -> authorizeRequests //
.requestMatchers(PathPatternRequestMatcher.withDefaults().matcher(this.adminServer.path("/assets/**")))
.permitAll()
.requestMatchers(PathPatternRequestMatcher.withDefaults().matcher(this.adminServer.path("/actuator/info")))
.permitAll()
.requestMatchers(PathPatternRequestMatcher.withDefaults().matcher(adminServer.path("/actuator/health")))
.permitAll()
.requestMatchers(PathPatternRequestMatcher.withDefaults().matcher(this.adminServer.path("/login")))
.permitAll()
.dispatcherTypeMatchers(DispatcherType.ASYNC)
.permitAll() // https://github.com/spring-projects/spring-security/issues/11027
.anyRequest()
.authenticated())
.formLogin(
(formLogin) -> formLogin.loginPage(this.adminServer.path("/login")).successHandler(successHandler))
.logout((logout) -> logout.logoutUrl(this.adminServer.path("/logout")))
.httpBasic(Customizer.withDefaults());
http.addFilterAfter(new CustomCsrfFilter(), BasicAuthenticationFilter.class) // <5>
.csrf((csrf) -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler())
.ignoringRequestMatchers(
PathPatternRequestMatcher.withDefaults()
.matcher(HttpMethod.POST, this.adminServer.path("/instances")), // <6>
PathPatternRequestMatcher.withDefaults()
.matcher(HttpMethod.DELETE, this.adminServer.path("/instances/*")), // <6>
PathPatternRequestMatcher.withDefaults().matcher(this.adminServer.path("/actuator/**")) // <7>
));
http.rememberMe((rememberMe) -> rememberMe.key(UUID.randomUUID().toString()).tokenValiditySeconds(1209600));
return http.build();
}
/**
* 创建内存用户详情服务
* @param passwordEncoder 密码编码器
* @return 包含配置用户的InMemoryUserDetailsManager实例
*/
@Bean
public InMemoryUserDetailsManager userDetailsService(PasswordEncoder passwordEncoder) {
UserDetails user = User.withUsername(security.getUser().getName())
.password(passwordEncoder.encode(security.getUser().getPassword()))
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
/**
* 创建并返回一个BCrypt密码编码器实例
* @return BCryptPasswordEncoder实例
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
package com.pig4cloud.pig.monitor.converter;
import de.codecentric.boot.admin.server.cloud.discovery.DefaultServiceInstanceConverter;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
import java.util.stream.Collectors;
import static java.util.Collections.emptyMap;
/**
* Nacos 2.x 服务注册转换器,用于处理服务实例元数据转换
*
* @author lengleng
* @date 2025/05/31
*/
@Configuration(proxyBeanMethods = false)
public class NacosServiceInstanceConverter extends DefaultServiceInstanceConverter {
/**
* 获取服务实例的元数据
* @param instance 服务实例
* @return 过滤后的元数据映射,不包含空键或空值的条目
*/
@Override
protected Map<String, String> getMetadata(ServiceInstance instance) {
return (instance.getMetadata() != null) ? instance.getMetadata()
.entrySet()
.stream()
.filter((e) -> e.getKey() != null && e.getValue() != null)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)) : emptyMap();
}
}
server:
port: 5001
spring:
application:
name: @artifactId@
cloud:
nacos:
username: @nacos.username@
password: @nacos.password@
discovery:
server-addr: ${NACOS_HOST:127.0.0.1}:${NACOS_PORT:8848}
config:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
config:
import:
- nacos:application-@profiles.active@.yml
- nacos:${spring.application.name}-@profiles.active@.yml
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<configuration debug="false" scan="false">
<springProperty scop="context" name="spring.application.name" source="spring.application.name" defaultValue=""/>
<property name="log.path" value="logs/${spring.application.name}"/>
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<property name="FILE_LOG_PATTERN" value=":%d{yyyy-MM-dd HH:mm:ss.SSS} %5p %t %c{1}: %m%n"/>
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" class="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
class="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
class="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- Console log output -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- Log file debug output -->
<appender name="debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/debug.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/%d{yyyy-MM, aux}/debug.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- Log file error output -->
<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/%d{yyyy-MM}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
</appender>
<!--nacos 心跳 INFO 屏蔽-->
<logger name="com.alibaba.nacos" level="OFF">
<appender-ref ref="error"/>
</logger>
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
<root level="INFO">
<appender-ref ref="console"/>
<appender-ref ref="debug"/>
<appender-ref ref="error"/>
</root>
</configuration>
FROM registry.cn-hangzhou.aliyuncs.com/dockerhub_mirror/java:21-anolis
WORKDIR /pig-quartz
ARG JAR_FILE=target/pig-quartz.jar
COPY ${JAR_FILE} app.jar
EXPOSE 5007
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms128m -Xmx256m -Djava.security.egd=file:/dev/./urandom"
CMD sleep 60; java $JAVA_OPTS -jar app.jar
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-visual</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>pig-quartz</artifactId>
<packaging>jar</packaging>
<description>基于quartz后台定时任务模块</description>
<dependencies>
<!--注册中心客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--配置中心客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--日志处理-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common-log</artifactId>
</dependency>
<!--feign 处理-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common-feign</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common-mybatis</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
</dependency>
<!--数据库-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!--swagger-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common-swagger</artifactId>
</dependency>
<!--spring security 、oauth、jwt依赖-->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common-security</artifactId>
</dependency>
<!-- quartz 模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<!--web 模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--undertow容器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
</dependencies>
<profiles>
<profile>
<id>boot</id>
</profile>
<profile>
<id>cloud</id>
<activation>
<!-- 默认环境 -->
<activeByDefault>true</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
package com.pig4cloud.pig.daemon.quartz;
import com.pig4cloud.pig.common.feign.annotation.EnablePigFeignClients;
import com.pig4cloud.pig.common.security.annotation.EnablePigResourceServer;
import com.pig4cloud.pig.common.swagger.annotation.EnablePigDoc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* PigQuartz应用启动类
* <p>
* 集成定时任务、Feign客户端、资源服务及服务发现功能
*
* @author lengleng
* @author frwcloud
* @date 2025/05/31
*/
@EnablePigDoc("job")
@EnablePigFeignClients
@EnablePigResourceServer
@EnableDiscoveryClient
@SpringBootApplication
public class PigQuartzApplication {
public static void main(String[] args) {
SpringApplication.run(PigQuartzApplication.class, args);
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.config;
import org.quartz.JobKey;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
import org.springframework.util.Assert;
/**
* 自动装配能力的Bean任务工厂,继承自SpringBeanJobFactory,用于创建并自动装配Job实例
*
* @author lengleng
* @author 郑健楠
* @date 2025/05/31
*/
class AutowireCapableBeanJobFactory extends SpringBeanJobFactory {
private final AutowireCapableBeanFactory beanFactory;
AutowireCapableBeanJobFactory(AutowireCapableBeanFactory beanFactory) {
Assert.notNull(beanFactory, "Bean factory must not be null");
this.beanFactory = beanFactory;
}
/**
* 创建并初始化Job实例
* @param bundle 触发器触发包,包含Job相关信息
* @return 初始化后的Job实例
* @throws Exception 创建或初始化过程中可能抛出的异常
*/
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object jobInstance = super.createJobInstance(bundle);
this.beanFactory.autowireBean(jobInstance);
// 此处必须注入 beanName 不然sentinel 报错
JobKey jobKey = bundle.getTrigger().getJobKey();
String beanName = jobKey + jobKey.getName();
this.beanFactory.initializeBean(jobInstance, beanName);
return jobInstance;
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.config;
import com.pig4cloud.pig.daemon.quartz.constants.PigQuartzEnum;
import com.pig4cloud.pig.daemon.quartz.service.SysJobService;
import com.pig4cloud.pig.daemon.quartz.util.TaskUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Scheduler;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Configuration;
/**
* 初始化加载定时任务配置类
*
* @author lengleng
* @author 郑健楠
* @date 2025/05/31
*/
@Slf4j
@Configuration
@AllArgsConstructor
public class PigInitQuartzJob implements InitializingBean {
private final SysJobService sysJobService;
private final TaskUtil taskUtil;
private final Scheduler scheduler;
/**
* 在属性设置完成后执行,根据任务状态进行相应操作
* @throws Exception 执行过程中可能抛出的异常
*/
@Override
public void afterPropertiesSet() throws Exception {
sysJobService.list().forEach(sysjob -> {
if (PigQuartzEnum.JOB_STATUS_RELEASE.getType().equals(sysjob.getJobStatus())) {
taskUtil.removeJob(sysjob, scheduler);
}
else if (PigQuartzEnum.JOB_STATUS_RUNNING.getType().equals(sysjob.getJobStatus())) {
taskUtil.resumeJob(sysjob, scheduler);
}
else if (PigQuartzEnum.JOB_STATUS_NOT_RUNNING.getType().equals(sysjob.getJobStatus())) {
taskUtil.pauseJob(sysjob, scheduler);
}
else {
taskUtil.removeJob(sysjob, scheduler);
}
});
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.config;
import com.pig4cloud.pig.common.core.factory.YamlPropertySourceFactory;
import org.quartz.Calendar;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.quartz.QuartzProperties;
import org.springframework.boot.autoconfigure.quartz.SchedulerFactoryBeanCustomizer;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/**
* Quartz 定时任务配置类
*
* @author lengleng
* @author 郑健楠
* @date 2025/05/31
*/
@EnableAsync
@Configuration
@PropertySource(value = "classpath:quartz-config.yml", factory = YamlPropertySourceFactory.class)
@ConditionalOnClass({ Scheduler.class, SchedulerFactoryBean.class })
@EnableConfigurationProperties({ QuartzProperties.class })
public class PigQuartzConfig {
private final QuartzProperties properties;
private final List<SchedulerFactoryBeanCustomizer> customizers;
private final JobDetail[] jobDetails;
private final Map<String, Calendar> calendars;
private final Trigger[] triggers;
private final ApplicationContext applicationContext;
/**
* 构造函数,初始化PigQuartzConfig配置
* @param properties Quartz配置属性
* @param customizers SchedulerFactoryBean自定义器列表
* @param jobDetails JobDetail数组
* @param calendars 日历Map
* @param triggers 触发器数组
* @param applicationContext Spring应用上下文
*/
public PigQuartzConfig(QuartzProperties properties,
ObjectProvider<List<SchedulerFactoryBeanCustomizer>> customizers, ObjectProvider<JobDetail[]> jobDetails,
ObjectProvider<Map<String, Calendar>> calendars, ObjectProvider<Trigger[]> triggers,
ApplicationContext applicationContext) {
this.properties = properties;
this.customizers = customizers.getIfAvailable();
this.jobDetails = jobDetails.getIfAvailable();
this.calendars = calendars.getIfAvailable();
this.triggers = triggers.getIfAvailable();
this.applicationContext = applicationContext;
}
/**
* 创建并配置Quartz SchedulerFactoryBean
* @return 配置完成的SchedulerFactoryBean实例
*/
@Bean
@ConditionalOnMissingBean
public SchedulerFactoryBean quartzScheduler() {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean
.setJobFactory(new AutowireCapableBeanJobFactory(this.applicationContext.getAutowireCapableBeanFactory()));
if (!this.properties.getProperties().isEmpty()) {
schedulerFactoryBean.setQuartzProperties(this.asProperties(this.properties.getProperties()));
}
if (this.jobDetails != null && this.jobDetails.length > 0) {
schedulerFactoryBean.setJobDetails(this.jobDetails);
}
if (this.calendars != null && !this.calendars.isEmpty()) {
schedulerFactoryBean.setCalendars(this.calendars);
}
if (this.triggers != null && this.triggers.length > 0) {
schedulerFactoryBean.setTriggers(this.triggers);
}
this.customize(schedulerFactoryBean);
return schedulerFactoryBean;
}
/**
* 将Map转换为Properties对象
* @param source 源Map,键值对均为String类型
* @return 转换后的Properties对象
*/
private Properties asProperties(Map<String, String> source) {
Properties properties = new Properties();
properties.putAll(source);
return properties;
}
/**
* 自定义SchedulerFactoryBean
* @param schedulerFactoryBean 需要自定义的调度器工厂bean
*/
private void customize(SchedulerFactoryBean schedulerFactoryBean) {
if (this.customizers != null) {
for (SchedulerFactoryBeanCustomizer customizer : this.customizers) {
customizer.customize(schedulerFactoryBean);
}
}
}
/**
* 通过SchedulerFactoryBean获取Scheduler的实例
* @return
*/
@Bean
public Scheduler scheduler() {
return quartzScheduler().getScheduler();
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.config;
import org.springframework.boot.autoconfigure.quartz.SchedulerFactoryBeanCustomizer;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
/**
* PigQuartz 自定义配置类,用于配置 SchedulerFactoryBean
*
* @author lengleng
* @author 郑健楠
* @date 2025/05/31
*/
@Configuration
public class PigQuartzCustomizerConfig implements SchedulerFactoryBeanCustomizer {
/**
* 自定义SchedulerFactoryBean配置
* @param schedulerFactoryBean 调度器工厂bean
*/
@Override
public void customize(SchedulerFactoryBean schedulerFactoryBean) {
schedulerFactoryBean.setWaitForJobsToCompleteOnShutdown(true);
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.config;
import com.pig4cloud.pig.daemon.quartz.constants.PigQuartzEnum;
import com.pig4cloud.pig.daemon.quartz.entity.SysJob;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.springframework.beans.factory.annotation.Autowired;
/**
* 动态任务工厂:用于执行动态任务调度
*
* @author lengleng
* @author 郑健楠
* @date 2025/05/31
*/
@Slf4j
@DisallowConcurrentExecution
public class PigQuartzFactory implements Job {
/**
* 定时任务调用工厂
*/
@Autowired
private PigQuartzInvokeFactory pigxQuartzInvokeFactory;
/**
* 执行定时任务
* @param jobExecutionContext 任务执行上下文
* @throws Exception 执行过程中可能抛出的异常
*/
@Override
@SneakyThrows
public void execute(JobExecutionContext jobExecutionContext) {
SysJob sysJob = (SysJob) jobExecutionContext.getMergedJobDataMap()
.get(PigQuartzEnum.SCHEDULE_JOB_KEY.getType());
pigxQuartzInvokeFactory.init(sysJob, jobExecutionContext.getTrigger());
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.config;
import com.pig4cloud.pig.daemon.quartz.entity.SysJob;
import com.pig4cloud.pig.daemon.quartz.event.SysJobEvent;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Aspect;
import org.quartz.Trigger;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
/**
* 定时任务调用工厂类
* <p>
* 用于初始化并发布定时任务事件
*
* @author lengleng
* @author 郑健楠
* @date 2025/05/31
*/
@Slf4j
@Aspect
@Service
@AllArgsConstructor
public class PigQuartzInvokeFactory {
private final ApplicationEventPublisher publisher;
/**
* 初始化并发布定时任务事件
* @param sysJob 系统任务对象
* @param trigger 任务触发器
*/
@SneakyThrows
void init(SysJob sysJob, Trigger trigger) {
publisher.publishEvent(new SysJobEvent(sysJob, trigger));
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.constants;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 任务类型枚举
*
* @author lengleng
* @date 2019/03/14
*/
@Getter
@AllArgsConstructor
public enum JobTypeQuartzEnum {
/**
* 反射java类
*/
JAVA("1", "反射java类"),
/**
* spring bean 的方式
*/
SPRING_BEAN("2", "spring bean容器实例"),
/**
* rest 调用
*/
REST("3", "rest调用"),
/**
* jar
*/
JAR("4", "jar调用");
/**
* 类型
*/
private final String type;
/**
* 描述
*/
private final String description;
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.constants;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 定时任务枚举类
* <p>
* 定义定时任务相关的枚举常量,包括错失执行策略、任务状态等
*
* @author lengleng
* @author 郑健楠
* @date 2025/05/31
*/
@Getter
@AllArgsConstructor
public enum PigQuartzEnum {
/**
* 错失执行策略默认
*/
MISFIRE_DEFAULT("0", "默认"),
/**
* 错失执行策略-立即执行错失任务
*/
MISFIRE_IGNORE_MISFIRES("1", "立即执行错失任务"),
/**
* 错失执行策略-触发一次执行周期执行
*/
MISFIRE_FIRE_AND_PROCEED("2", "触发一次执行周期执行"),
/**
* 错失执行策略-不触发执行周期执行
*/
MISFIRE_DO_NOTHING("3", "不触发周期执行"),
/**
* 任务详细信息的key
*/
SCHEDULE_JOB_KEY("scheduleJob", "获取任务详细信息的key"),
/**
* JOB执行状态:0执行成功
*/
JOB_LOG_STATUS_SUCCESS("0", "执行成功"),
/**
* JOB执行状态:1执行失败
*/
JOB_LOG_STATUS_FAIL("1", "执行失败"),
/**
* JOB状态:1已发布
*/
JOB_STATUS_RELEASE("1", "已发布"),
/**
* JOB状态:2运行中
*/
JOB_STATUS_RUNNING("2", "运行中"),
/**
* JOB状态:3暂停
*/
JOB_STATUS_NOT_RUNNING("3", "暂停"),
/**
* JOB状态:4删除
*/
JOB_STATUS_DEL("4", "删除");
/**
* 类型
*/
private final String type;
/**
* 描述
*/
private final String description;
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.controller;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pig.common.core.util.R;
import com.pig4cloud.pig.common.log.annotation.SysLog;
import com.pig4cloud.pig.common.security.annotation.HasPermission;
import com.pig4cloud.pig.common.security.util.SecurityUtils;
import com.pig4cloud.pig.daemon.quartz.constants.PigQuartzEnum;
import com.pig4cloud.pig.daemon.quartz.entity.SysJob;
import com.pig4cloud.pig.daemon.quartz.entity.SysJobLog;
import com.pig4cloud.pig.daemon.quartz.service.SysJobLogService;
import com.pig4cloud.pig.daemon.quartz.service.SysJobService;
import com.pig4cloud.pig.daemon.quartz.util.TaskUtil;
import com.pig4cloud.plugin.excel.annotation.ResponseExcel;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 定时任务管理控制器
*
* @author lengleng
* @date 2025/05/31
*/
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/sys-job")
@Tag(description = "sys-job", name = "定时任务")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class SysJobController {
private final SysJobService sysJobService;
private final SysJobLogService sysJobLogService;
private final TaskUtil taskUtil;
private final Scheduler scheduler;
/**
* 定时任务分页查询
* @param page 分页对象
* @param sysJob 定时任务调度表
* @return R
*/
@GetMapping("/page")
@Operation(description = "分页定时业务查询")
public R getJobPage(Page page, SysJob sysJob) {
LambdaQueryWrapper<SysJob> wrapper = Wrappers.<SysJob>lambdaQuery()
.like(StrUtil.isNotBlank(sysJob.getJobName()), SysJob::getJobName, sysJob.getJobName())
.like(StrUtil.isNotBlank(sysJob.getJobGroup()), SysJob::getJobGroup, sysJob.getJobGroup())
.eq(StrUtil.isNotBlank(sysJob.getJobStatus()), SysJob::getJobStatus, sysJob.getJobGroup())
.eq(StrUtil.isNotBlank(sysJob.getJobExecuteStatus()), SysJob::getJobExecuteStatus,
sysJob.getJobExecuteStatus());
return R.ok(sysJobService.page(page, wrapper));
}
/**
* 通过id查询定时任务
* @param id id
* @return R
*/
@GetMapping("/{id}")
@Operation(description = "唯一标识查询定时任务")
public R getById(@PathVariable("id") Long id) {
return R.ok(sysJobService.getById(id));
}
/**
* 新增定时任务,默认新增状态为1已发布
* @param sysJob 定时任务调度表
* @return R
*/
@SysLog("新增定时任务")
@PostMapping
@HasPermission("job_sys_job_add")
@Operation(description = "新增定时任务")
public R saveJob(@RequestBody SysJob sysJob) {
long count = sysJobService.count(
Wrappers.query(SysJob.builder().jobName(sysJob.getJobName()).jobGroup(sysJob.getJobGroup()).build()));
if (count > 0) {
return R.failed("任务重复,请检查此组内是否已包含同名任务");
}
sysJob.setJobStatus(PigQuartzEnum.JOB_STATUS_RELEASE.getType());
sysJob.setCreateBy(SecurityUtils.getUser().getUsername());
return R.ok(sysJobService.save(sysJob));
}
/**
* 修改定时任务
* @param sysJob 定时任务调度表
* @return R
*/
@SysLog("修改定时任务")
@PutMapping
@HasPermission("job_sys_job_edit")
@Operation(description = "修改定时任务")
public R updateJob(@RequestBody SysJob sysJob) {
sysJob.setUpdateBy(SecurityUtils.getUser().getUsername());
SysJob querySysJob = this.sysJobService.getById(sysJob.getJobId());
if (PigQuartzEnum.JOB_STATUS_NOT_RUNNING.getType().equals(querySysJob.getJobStatus())) {
// 如修改暂停的需更新调度器
this.taskUtil.addOrUpateJob(sysJob, scheduler);
sysJobService.updateById(sysJob);
}
else if (PigQuartzEnum.JOB_STATUS_RELEASE.getType().equals(querySysJob.getJobStatus())) {
sysJobService.updateById(sysJob);
}
return R.ok();
}
/**
* 通过id删除定时任务
* @param id 定时任务唯一标识
* @return 操作结果
* @throws IllegalArgumentException 当任务未暂停时尝试删除会抛出异常
*/
@SysLog("删除定时任务")
@DeleteMapping("/{id}")
@HasPermission("job_sys_job_del")
@Operation(description = "唯一标识查询定时任务,暂停任务才能删除")
public R removeById(@PathVariable Long id) {
SysJob querySysJob = this.sysJobService.getById(id);
if (PigQuartzEnum.JOB_STATUS_NOT_RUNNING.getType().equals(querySysJob.getJobStatus())) {
this.taskUtil.removeJob(querySysJob, scheduler);
this.sysJobService.removeById(id);
}
else if (PigQuartzEnum.JOB_STATUS_RELEASE.getType().equals(querySysJob.getJobStatus())) {
this.sysJobService.removeById(id);
}
return R.ok();
}
/**
* 暂停全部定时任务
* @return R
*/
@SysLog("暂停全部定时任务")
@PostMapping("/shutdown-jobs")
@HasPermission("job_sys_job_shutdown_job")
@Operation(description = "暂停全部定时任务")
public R shutdownJobs() {
taskUtil.pauseJobs(scheduler);
long count = this.sysJobService.count(
new LambdaQueryWrapper<SysJob>().eq(SysJob::getJobStatus, PigQuartzEnum.JOB_STATUS_RUNNING.getType()));
if (count <= 0) {
return R.ok("无正在运行定时任务");
}
else {
// 更新定时任务状态条件,运行状态2更新为暂停状态3
this.sysJobService.update(
SysJob.builder().jobStatus(PigQuartzEnum.JOB_STATUS_NOT_RUNNING.getType()).build(),
new UpdateWrapper<SysJob>().lambda()
.eq(SysJob::getJobStatus, PigQuartzEnum.JOB_STATUS_RUNNING.getType()));
return R.ok("暂停成功");
}
}
/**
* 启动全部定时任务
* @return
*/
@SysLog("启动全部暂停的定时任务")
@PostMapping("/start-jobs")
@HasPermission("job_sys_job_start_job")
@Operation(description = "启动全部暂停的定时任务")
public R startJobs() {
// 更新定时任务状态条件,暂停状态3更新为运行状态2
this.sysJobService.update(SysJob.builder().jobStatus(PigQuartzEnum.JOB_STATUS_RUNNING.getType()).build(),
new UpdateWrapper<SysJob>().lambda()
.eq(SysJob::getJobStatus, PigQuartzEnum.JOB_STATUS_NOT_RUNNING.getType()));
taskUtil.startJobs(scheduler);
return R.ok();
}
/**
* 刷新全部定时任务 暂停和运行的添加到调度器其他状态从调度器移除
* @return R
*/
@SysLog("刷新全部定时任务")
@PostMapping("/refresh-jobs")
@HasPermission("job_sys_job_refresh_job")
@Operation(description = "刷新全部定时任务")
public R refreshJobs() {
sysJobService.list().forEach(sysjob -> {
if (PigQuartzEnum.JOB_STATUS_RUNNING.getType().equals(sysjob.getJobStatus())
|| PigQuartzEnum.JOB_STATUS_NOT_RUNNING.getType().equals(sysjob.getJobStatus())) {
taskUtil.addOrUpateJob(sysjob, scheduler);
}
else {
taskUtil.removeJob(sysjob, scheduler);
}
});
return R.ok();
}
/**
* 启动定时任务
* @param jobId 任务id
* @return R
*/
@SysLog("启动定时任务")
@PostMapping("/start-job/{id}")
@HasPermission("job_sys_job_start_job")
@Operation(description = "启动定时任务")
public R startJob(@PathVariable("id") Long jobId) throws SchedulerException {
SysJob querySysJob = this.sysJobService.getById(jobId);
if (querySysJob == null) {
return R.failed("无此定时任务,请确认");
}
// 如果定时任务不存在,强制状态为1已发布
if (!scheduler.checkExists(TaskUtil.getJobKey(querySysJob))) {
querySysJob.setJobStatus(PigQuartzEnum.JOB_STATUS_RELEASE.getType());
log.warn("定时任务不在quartz中,任务id:{},强制状态为已发布并加入调度器", jobId);
}
if (PigQuartzEnum.JOB_STATUS_RELEASE.getType().equals(querySysJob.getJobStatus())) {
taskUtil.addOrUpateJob(querySysJob, scheduler);
}
else {
taskUtil.resumeJob(querySysJob, scheduler);
}
// 更新定时任务状态为运行状态2
this.sysJobService
.updateById(SysJob.builder().jobId(jobId).jobStatus(PigQuartzEnum.JOB_STATUS_RUNNING.getType()).build());
return R.ok();
}
/**
* 启动定时任务
* @param jobId 任务id
* @return R
*/
@SysLog("立刻执行定时任务")
@PostMapping("/run-job/{id}")
@HasPermission("job_sys_job_run_job")
@Operation(description = "立刻执行定时任务")
public R runJob(@PathVariable("id") Long jobId) throws SchedulerException {
SysJob querySysJob = this.sysJobService.getById(jobId);
// 执行定时任务前判定任务是否在quartz中
if (!scheduler.checkExists(TaskUtil.getJobKey(querySysJob))) {
querySysJob.setJobStatus(PigQuartzEnum.JOB_STATUS_NOT_RUNNING.getType());
log.warn("立刻执行定时任务-定时任务不在quartz中,任务id:{},强制状态为暂停并加入调度器", jobId);
taskUtil.addOrUpateJob(querySysJob, scheduler);
}
return TaskUtil.runOnce(scheduler, querySysJob) ? R.ok() : R.failed();
}
/**
* 暂停定时任务
* @return
*/
@SysLog("暂停定时任务")
@PostMapping("/shutdown-job/{id}")
@HasPermission("job_sys_job_shutdown_job")
@Operation(description = "暂停定时任务")
public R shutdownJob(@PathVariable("id") Long id) {
SysJob querySysJob = this.sysJobService.getById(id);
// 更新定时任务状态条件,运行状态2更新为暂停状态3
this.sysJobService.updateById(SysJob.builder()
.jobId(querySysJob.getJobId())
.jobStatus(PigQuartzEnum.JOB_STATUS_NOT_RUNNING.getType())
.build());
taskUtil.pauseJob(querySysJob, scheduler);
return R.ok();
}
/**
* 分页查询定时执行日志
* @param page 分页参数
* @param sysJobLog 查询条件
* @return 分页结果
*/
@GetMapping("/job-log")
@Operation(description = "唯一标识查询定时执行日志")
public R getJobLogPage(Page page, SysJobLog sysJobLog) {
return R.ok(sysJobLogService.page(page, Wrappers.query(sysJobLog)));
}
/**
* 校验任务名称和任务组组合是否唯一
* @param jobName 任务名称
* @param jobGroup 任务组
* @return 校验结果,若已存在返回失败信息,否则返回成功
*/
@GetMapping("/is-valid-task-name")
@Operation(description = "检验任务名称和任务组联合是否唯一")
public R isValidTaskName(@RequestParam String jobName, @RequestParam String jobGroup) {
return this.sysJobService
.count(Wrappers.query(SysJob.builder().jobName(jobName).jobGroup(jobGroup).build())) > 0
? R.failed("任务重复,请检查此组内是否已包含同名任务") : R.ok();
}
/**
* 导出任务数据
* @param sysJob 查询条件对象
* @return 符合条件的任务列表
*/
@ResponseExcel
@GetMapping("/export")
@Operation(description = "导出任务")
public List<SysJob> exportJobs(SysJob sysJob) {
return sysJobService.list(Wrappers.query(sysJob));
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.controller;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pig.common.core.util.R;
import com.pig4cloud.pig.daemon.quartz.entity.SysJobLog;
import com.pig4cloud.pig.daemon.quartz.service.SysJobLogService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.*;
/**
* 定时任务日志控制器
*
* @author frwcloud
* @author lengleng
* @date 2025/05/31
*/
@RestController
@AllArgsConstructor
@RequestMapping("/sys-job-log")
@Tag(description = "sys-job-log", name = "定时任务日志")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class SysJobLogController {
private final SysJobLogService sysJobLogService;
/**
* 分页查询定时任务日志
* @param page 分页对象
* @param sysJobLog 查询条件对象
* @return 分页查询结果
*/
@GetMapping("/page")
@Operation(description = "分页定时任务日志查询")
public R getJobLogPage(Page page, SysJobLog sysJobLog) {
return R.ok(sysJobLogService.page(page, Wrappers.query(sysJobLog)));
}
/**
* 批量删除日志
* @param ids 要删除的日志ID数组
* @return 操作结果
*/
@DeleteMapping
@Operation(description = "批量删除日志")
public R removeBatchByIds(@RequestBody Long[] ids) {
return R.ok(sysJobLogService.removeBatchByIds(CollUtil.toList(ids)));
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.entity;
import java.io.Serial;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
/**
* 定时任务调度表
*
* @author frwcloud
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "定时任务")
@EqualsAndHashCode(callSuper = false)
public class SysJob extends Model<SysJob> {
@Serial
private static final long serialVersionUID = 1L;
/**
* 任务id
*/
@TableId(value = "job_id", type = IdType.ASSIGN_ID)
private Long jobId;
/**
* 任务名称
*/
private String jobName;
/**
* 任务组名
*/
private String jobGroup;
/**
* 组内执行顺利,值越大执行优先级越高,最大值9,最小值1
*/
private String jobOrder;
/**
* 1、java类;2、spring bean名称;3、rest调用;4、jar调用;9其他
*/
private String jobType;
/**
* job_type=3时,rest调用地址,仅支持rest get协议,需要增加String返回值,0成功,1失败;job_type=4时,jar路径;其它值为空
*/
private String executePath;
/**
* job_type=1时,类完整路径;job_type=2时,spring bean名称;其它值为空
*/
private String className;
/**
* 任务方法
*/
private String methodName;
/**
* 参数值
*/
private String methodParamsValue;
/**
* cron执行表达式
*/
private String cronExpression;
/**
* 错失执行策略(1错失周期立即执行 2错失周期执行一次 3下周期执行)
*/
private String misfirePolicy;
/**
* 1、多租户任务;2、非多租户任务
*/
private String jobTenantType;
/**
* 状态(0、未发布;1、已发布;2、运行中;3、暂停;4、删除;)
*/
private String jobStatus;
/**
* 状态(0正常 1异常)
*/
private String jobExecuteStatus;
/**
* 创建者
*/
@TableField(fill = FieldFill.INSERT)
private String createBy;
/**
* 更新者
*/
@TableField(fill = FieldFill.UPDATE)
private String updateBy;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 修改时间
*/
@TableField(fill = FieldFill.UPDATE)
private LocalDateTime updateTime;
/**
* 首次执行时间
*/
private LocalDateTime startTime;
/**
* 上次执行时间
*/
private LocalDateTime previousTime;
/**
* 下次执行时间
*/
private LocalDateTime nextTime;
/**
* 备注信息
*/
private String remark;
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.entity;
import java.io.Serial;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
/**
* 定时任务执行日志表
*
* @author frwcloud
* @date 2019-01-27 13:40:20
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
@Schema(description = "定时任务日志")
public class SysJobLog extends Model<SysJobLog> {
@Serial
private static final long serialVersionUID = 1L;
/**
* 任务日志ID
*/
@TableId(value = "job_log_id", type = IdType.ASSIGN_ID)
private Long jobLogId;
/**
* 任务id
*/
private Long jobId;
/**
* 任务名称
*/
private String jobName;
/**
* 任务组名
*/
private String jobGroup;
/**
* 组内执行顺利,值越大执行优先级越高,最大值9,最小值1
*/
private String jobOrder;
/**
* 1、java类;2、spring bean名称;3、rest调用;4、jar调用;9其他
*/
private String jobType;
/**
* job_type=3时,rest调用地址,仅支持post协议;job_type=4时,jar路径;其它值为空
*/
private String executePath;
/**
* job_type=1时,类完整路径;job_type=2时,spring bean名称;其它值为空
*/
private String className;
/**
* 任务方法
*/
private String methodName;
/**
* 参数值
*/
private String methodParamsValue;
/**
* cron执行表达式
*/
private String cronExpression;
/**
* 日志信息
*/
private String jobMessage;
/**
* 执行状态(0正常 1失败)
*/
private String jobLogStatus;
/**
* 执行时间
*/
private String executeTime;
/**
* 异常信息
*/
private String exceptionInfo;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.event;
import com.pig4cloud.pig.daemon.quartz.entity.SysJob;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.quartz.Trigger;
/**
* 系统任务事件类,用于封装定时任务及其触发器
*
* @author frwcloud
* @author lengleng
* @date 2025/05/31
*/
@Getter
@AllArgsConstructor
public class SysJobEvent {
private final SysJob sysJob;
private final Trigger trigger;
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.event;
import com.pig4cloud.pig.daemon.quartz.entity.SysJob;
import com.pig4cloud.pig.daemon.quartz.util.TaskInvokUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Trigger;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
/**
* 系统任务监听器:用于异步监听并处理定时任务事件
*
* @author frwcloud
* @author lengleng
* @date 2025/05/31
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class SysJobListener {
private final TaskInvokUtil taskInvokUtil;
@Async
@Order
@EventListener(SysJobEvent.class)
public void comSysJob(SysJobEvent event) {
SysJob sysJob = event.getSysJob();
Trigger trigger = event.getTrigger();
taskInvokUtil.invokMethod(sysJob, trigger);
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.event;
import com.pig4cloud.pig.daemon.quartz.entity.SysJobLog;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 定时任务日志多线程事件
*
* @author frwcloud
* @author lengleng
* @date 2025/05/31
*/
@Getter
@AllArgsConstructor
public class SysJobLogEvent {
private final SysJobLog sysJobLog;
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.event;
import com.pig4cloud.pig.daemon.quartz.entity.SysJobLog;
import com.pig4cloud.pig.daemon.quartz.service.SysJobLogService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
/**
* 系统任务日志监听器:用于异步监听并处理定时任务日志事件
*
* @author frwcloud
* @author lengleng
* @date 2025/05/31
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class SysJobLogListener {
private final SysJobLogService sysJobLogService;
/**
* 异步保存系统任务日志
* @param event 系统任务日志事件
*/
@Async
@Order
@EventListener(SysJobLogEvent.class)
public void saveSysJobLog(SysJobLogEvent event) {
SysJobLog sysJobLog = event.getSysJobLog();
sysJobLogService.save(sysJobLog);
log.info("执行定时任务日志");
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.exception;
/**
* 定时任务异常类
*
* @author lengleng
* @date 2025/05/31
*/
public class TaskException extends Exception {
/**
* 无参构造方法,创建一个TaskException实例
*/
public TaskException() {
super();
}
/**
* 构造方法,使用指定消息创建TaskException实例
* @param msg 异常信息
*/
public TaskException(String msg) {
super(msg);
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.pig4cloud.pig.daemon.quartz.entity.SysJobLog;
import org.apache.ibatis.annotations.Mapper;
/**
* 定时任务执行日志表 Mapper 接口
*
* @author lengleng
* @date 2025/05/31
*/
@Mapper
public interface SysJobLogMapper extends BaseMapper<SysJobLog> {
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.pig4cloud.pig.daemon.quartz.entity.SysJob;
import org.apache.ibatis.annotations.Mapper;
/**
* 定时任务调度表 Mapper 接口
*
* @author lengleng
* @date 2025/05/31
*/
@Mapper
public interface SysJobMapper extends BaseMapper<SysJob> {
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pig.daemon.quartz.entity.SysJobLog;
/**
* 定时任务执行日志服务接口
*
* @author lengleng
* @date 2025/05/31
*/
public interface SysJobLogService extends IService<SysJobLog> {
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pig.daemon.quartz.entity.SysJob;
/**
* 定时任务调度服务接口
*
* @author lengleng
* @date 2025/05/31
*/
public interface SysJobService extends IService<SysJob> {
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pig.daemon.quartz.entity.SysJobLog;
import com.pig4cloud.pig.daemon.quartz.mapper.SysJobLogMapper;
import com.pig4cloud.pig.daemon.quartz.service.SysJobLogService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 定时任务执行日志服务实现类
*
* @author lengleng
* @date 2025/05/31
*/
@Slf4j
@Service
@AllArgsConstructor
public class SysJobLogServiceImpl extends ServiceImpl<SysJobLogMapper, SysJobLog> implements SysJobLogService {
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pig.daemon.quartz.entity.SysJob;
import com.pig4cloud.pig.daemon.quartz.mapper.SysJobMapper;
import com.pig4cloud.pig.daemon.quartz.service.SysJobService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 定时任务调度服务实现类
*
* @author lengleng
* @date 2025/05/31
*/
@Slf4j
@Service
@AllArgsConstructor
public class SysJobServiceImpl extends ServiceImpl<SysJobMapper, SysJob> implements SysJobService {
}
package com.pig4cloud.pig.daemon.quartz.task;
import com.pig4cloud.pig.common.core.util.R;
import com.pig4cloud.pig.common.security.annotation.Inner;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
/**
* 用于测试REST风格调用的演示类
*
* @author lengleng
* @date 2025/05/31
*/
@Slf4j
@RestController
@RequestMapping("/inner-job")
public class RestTaskDemo {
/**
* REST风格调用定时任务的演示方法
* @param param 路径参数
* @return 统一响应结果
*/
@Inner(value = false)
@GetMapping("/{param}")
public R demoMethod(@PathVariable("param") String param) {
log.info("测试于:{},传入参数{}", LocalDateTime.now(), param);
return R.ok();
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.task;
import com.pig4cloud.pig.daemon.quartz.constants.PigQuartzEnum;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* Spring Bean任务演示类
*
* @author lengleng
* @author 郑健楠
* @date 2025/05/31
*/
@Slf4j
@Component("demo")
public class SpringBeanTaskDemo {
/**
* 演示方法,用于测试Spring Bean
* @param para 输入参数
* @return 返回任务日志状态成功类型
* @throws Exception 可能抛出的异常
*/
@SneakyThrows
public String demoMethod(String para) {
log.info("测试于:{},输入参数{}", LocalDateTime.now(), para);
return PigQuartzEnum.JOB_LOG_STATUS_SUCCESS.getType();
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.util;
import com.pig4cloud.pig.daemon.quartz.entity.SysJob;
import com.pig4cloud.pig.daemon.quartz.exception.TaskException;
/**
* 定时任务反射实现接口
*
* @author lengleng
* @date 2025/05/31
*/
public interface ITaskInvok {
/**
* 执行反射方法
* @param sysJob 任务配置类
* @throws TaskException 执行任务时可能抛出的异常
*/
void invokMethod(SysJob sysJob) throws TaskException;
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.util;
import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pig.daemon.quartz.entity.SysJob;
import com.pig4cloud.pig.daemon.quartz.exception.TaskException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 定时任务可执行jar反射实现类
*
* @author lengleng
* @date 2025/05/31
*/
@Slf4j
@Component("jarTaskInvok")
public class JarTaskInvok implements ITaskInvok {
/**
* 调用方法执行定时任务jar
* @param sysJob 定时任务信息
* @throws TaskException 执行任务时发生异常抛出
*/
@Override
public void invokMethod(SysJob sysJob) throws TaskException {
ProcessBuilder processBuilder = new ProcessBuilder();
File jar = new File(sysJob.getExecutePath());
processBuilder.directory(jar.getParentFile());
List<String> commands = new ArrayList<>();
commands.add("java");
commands.add("-jar");
commands.add(sysJob.getExecutePath());
if (StrUtil.isNotEmpty(sysJob.getMethodParamsValue())) {
commands.add(sysJob.getMethodParamsValue());
}
processBuilder.command(commands);
try {
processBuilder.start();
}
catch (IOException e) {
log.error("定时任务jar反射执行异常,执行任务:{}", sysJob.getExecutePath());
throw new TaskException("定时任务jar反射执行异常,执行任务:" + sysJob.getExecutePath());
}
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.util;
import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pig.daemon.quartz.constants.PigQuartzEnum;
import com.pig4cloud.pig.daemon.quartz.entity.SysJob;
import com.pig4cloud.pig.daemon.quartz.exception.TaskException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 基于Java反射实现的定时任务调用类
*
* @author lengleng
* @date 2025/05/31
*/
@Component("javaClassTaskInvok")
@Slf4j
public class JavaClassTaskInvok implements ITaskInvok {
/**
* 调用定时任务方法
* @param sysJob 定时任务信息
* @throws TaskException 执行任务过程中出现异常时抛出
*/
@Override
public void invokMethod(SysJob sysJob) throws TaskException {
Object obj;
Class clazz;
Method method;
Object returnValue;
try {
if (StrUtil.isNotEmpty(sysJob.getMethodParamsValue())) {
clazz = Class.forName(sysJob.getClassName());
obj = clazz.getDeclaredConstructor().newInstance();
method = clazz.getDeclaredMethod(sysJob.getMethodName(), String.class);
returnValue = method.invoke(obj, sysJob.getMethodParamsValue());
}
else {
clazz = Class.forName(sysJob.getClassName());
obj = clazz.getDeclaredConstructor().newInstance();
method = clazz.getDeclaredMethod(sysJob.getMethodName());
returnValue = method.invoke(obj);
}
if (StrUtil.isEmpty(returnValue.toString())
|| PigQuartzEnum.JOB_LOG_STATUS_FAIL.getType().equals(returnValue.toString())) {
log.error("定时任务javaClassTaskInvok异常,执行任务:{}", sysJob.getClassName());
throw new TaskException("定时任务javaClassTaskInvok业务执行失败,任务:" + sysJob.getClassName());
}
}
catch (ClassNotFoundException e) {
log.error("定时任务java反射类没有找到,执行任务:{}", sysJob.getClassName());
throw new TaskException("定时任务java反射类没有找到,执行任务:" + sysJob.getClassName());
}
catch (IllegalAccessException e) {
log.error("定时任务java反射类异常,执行任务:{}", sysJob.getClassName());
throw new TaskException("定时任务java反射类异常,执行任务:" + sysJob.getClassName());
}
catch (InstantiationException e) {
log.error("定时任务java反射类异常,执行任务:{}", sysJob.getClassName());
throw new TaskException("定时任务java反射类异常,执行任务:" + sysJob.getClassName());
}
catch (NoSuchMethodException e) {
log.error("定时任务java反射执行方法名异常,执行任务:{}", sysJob.getClassName());
throw new TaskException("定时任务java反射执行方法名异常,执行任务:" + sysJob.getClassName());
}
catch (InvocationTargetException e) {
log.error("定时任务java反射执行异常,执行任务:{}", sysJob.getClassName());
throw new TaskException("定时任务java反射执行异常,执行任务:" + sysJob.getClassName());
}
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.util;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import com.pig4cloud.pig.daemon.quartz.entity.SysJob;
import com.pig4cloud.pig.daemon.quartz.exception.TaskException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* REST定时任务反射实现类
*
* @author lengleng
* @date 2025/05/31
*/
@Slf4j
@AllArgsConstructor
@Component("restTaskInvok")
public class RestTaskInvok implements ITaskInvok {
/**
* 调用方法执行定时任务
* @param sysJob 定时任务信息
* @throws TaskException 任务执行失败时抛出异常
*/
@Override
public void invokMethod(SysJob sysJob) throws TaskException {
try {
HttpRequest request = HttpUtil.createGet(sysJob.getExecutePath());
request.execute();
}
catch (Exception e) {
log.error("定时任务restTaskInvok异常,执行任务:{}", sysJob.getExecutePath());
throw new TaskException("定时任务restTaskInvok业务执行失败,任务:" + sysJob.getExecutePath());
}
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.util;
import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pig.common.core.util.SpringContextHolder;
import com.pig4cloud.pig.daemon.quartz.constants.PigQuartzEnum;
import com.pig4cloud.pig.daemon.quartz.entity.SysJob;
import com.pig4cloud.pig.daemon.quartz.exception.TaskException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 基于Spring Bean的定时任务反射执行器
*
* @author lengleng
* @date 2025/05/31
*/
@Component("springBeanTaskInvok")
@Slf4j
public class SpringBeanTaskInvok implements ITaskInvok {
/**
* 调用定时任务方法
* @param sysJob 定时任务信息
* @throws TaskException 当任务执行失败或反射调用异常时抛出
*/
@Override
public void invokMethod(SysJob sysJob) throws TaskException {
Object target;
Method method;
Object returnValue;
// 通过Spring上下文去找 也有可能找不到
target = SpringContextHolder.getBean(sysJob.getClassName());
try {
if (StrUtil.isNotEmpty(sysJob.getMethodParamsValue())) {
method = target.getClass().getDeclaredMethod(sysJob.getMethodName(), String.class);
ReflectionUtils.makeAccessible(method);
returnValue = method.invoke(target, sysJob.getMethodParamsValue());
}
else {
method = target.getClass().getDeclaredMethod(sysJob.getMethodName());
ReflectionUtils.makeAccessible(method);
returnValue = method.invoke(target);
}
if (StrUtil.isEmpty(returnValue.toString())
|| PigQuartzEnum.JOB_LOG_STATUS_FAIL.getType().equals(returnValue.toString())) {
log.error("定时任务springBeanTaskInvok异常,执行任务:{}", sysJob.getClassName());
throw new TaskException("定时任务springBeanTaskInvok业务执行失败,任务:" + sysJob.getClassName());
}
}
catch (NoSuchMethodException e) {
log.error("定时任务spring bean反射异常方法未找到,执行任务:{}", sysJob.getClassName());
throw new TaskException("定时任务spring bean反射异常方法未找到,执行任务:" + sysJob.getClassName());
}
catch (IllegalAccessException e) {
log.error("定时任务spring bean反射异常,执行任务:{}", sysJob.getClassName());
throw new TaskException("定时任务spring bean反射异常,执行任务:" + sysJob.getClassName());
}
catch (InvocationTargetException e) {
log.error("定时任务spring bean反射执行异常,执行任务:{}", sysJob.getClassName());
throw new TaskException("定时任务spring bean反射执行异常,执行任务:" + sysJob.getClassName());
}
}
}
package com.pig4cloud.pig.daemon.quartz.util;
import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pig.common.core.util.SpringContextHolder;
import com.pig4cloud.pig.daemon.quartz.constants.JobTypeQuartzEnum;
import com.pig4cloud.pig.daemon.quartz.exception.TaskException;
import lombok.extern.slf4j.Slf4j;
/**
* 任务调用工厂类:根据任务类型获取对应的任务调用器
*
* @author lengleng
* @version 1.0
* @date 2025/05/31
*/
@Slf4j
public class TaskInvokFactory {
/**
* 根据任务类型获取对应的任务执行器
* @param jobType 任务类型
* @return 任务执行器实例
* @throws TaskException 当任务类型为空或不支持时抛出异常
*/
public static ITaskInvok getInvoker(String jobType) throws TaskException {
if (StrUtil.isBlank(jobType)) {
log.info("获取TaskInvok传递参数有误,jobType:{}", jobType);
throw new TaskException("");
}
ITaskInvok iTaskInvok = null;
if (JobTypeQuartzEnum.JAVA.getType().equals(jobType)) {
iTaskInvok = SpringContextHolder.getBean("javaClassTaskInvok");
}
else if (JobTypeQuartzEnum.SPRING_BEAN.getType().equals(jobType)) {
iTaskInvok = SpringContextHolder.getBean("springBeanTaskInvok");
}
else if (JobTypeQuartzEnum.REST.getType().equals(jobType)) {
iTaskInvok = SpringContextHolder.getBean("restTaskInvok");
}
else if (JobTypeQuartzEnum.JAR.getType().equals(jobType)) {
iTaskInvok = SpringContextHolder.getBean("jarTaskInvok");
}
else if (StrUtil.isBlank(jobType)) {
log.info("定时任务类型无对应反射方式,反射类型:{}", jobType);
throw new TaskException("");
}
return iTaskInvok;
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.util;
import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pig.daemon.quartz.constants.PigQuartzEnum;
import com.pig4cloud.pig.daemon.quartz.entity.SysJob;
import com.pig4cloud.pig.daemon.quartz.entity.SysJobLog;
import com.pig4cloud.pig.daemon.quartz.event.SysJobLogEvent;
import com.pig4cloud.pig.daemon.quartz.service.SysJobService;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.quartz.CronTrigger;
import org.quartz.Trigger;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import java.time.ZoneId;
import java.util.Date;
/**
* 定时任务反射工具类,用于执行和管理定时任务的反射调用
*
* @author lengleng
* @date 2025/05/31
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class TaskInvokUtil {
private final ApplicationEventPublisher publisher;
private final SysJobService sysJobService;
/**
* 执行定时任务方法
* @param sysJob 定时任务信息
* @param trigger 触发器
*/
@SneakyThrows
public void invokMethod(SysJob sysJob, Trigger trigger) {
// 执行开始时间
long startTime;
// 执行结束时间
long endTime;
// 获取执行开始时间
startTime = System.currentTimeMillis();
// 更新定时任务表内的状态、执行时间、上次执行时间、下次执行时间等信息
SysJob updateSysjob = new SysJob();
updateSysjob.setJobId(sysJob.getJobId());
// 日志
SysJobLog sysJobLog = new SysJobLog();
sysJobLog.setJobId(sysJob.getJobId());
sysJobLog.setJobName(sysJob.getJobName());
sysJobLog.setJobGroup(sysJob.getJobGroup());
sysJobLog.setJobOrder(sysJob.getJobOrder());
sysJobLog.setJobType(sysJob.getJobType());
sysJobLog.setExecutePath(sysJob.getExecutePath());
sysJobLog.setClassName(sysJob.getClassName());
sysJobLog.setMethodName(sysJob.getMethodName());
sysJobLog.setMethodParamsValue(sysJob.getMethodParamsValue());
sysJobLog.setCronExpression(sysJob.getCronExpression());
try {
// 执行任务
ITaskInvok iTaskInvok = TaskInvokFactory.getInvoker(sysJob.getJobType());
// 确保租户上下文有值,使得当前线程中的多租户特性生效。
iTaskInvok.invokMethod(sysJob);
// 记录成功状态
sysJobLog.setJobMessage(PigQuartzEnum.JOB_LOG_STATUS_SUCCESS.getDescription());
sysJobLog.setJobLogStatus(PigQuartzEnum.JOB_LOG_STATUS_SUCCESS.getType());
// 任务表信息更新
updateSysjob.setJobExecuteStatus(PigQuartzEnum.JOB_LOG_STATUS_SUCCESS.getType());
}
catch (Throwable e) {
log.error("定时任务执行失败,任务名称:{};任务组名:{},cron执行表达式:{},执行时间:{}", sysJob.getJobName(), sysJob.getJobGroup(),
sysJob.getCronExpression(), new Date());
// 记录失败状态
sysJobLog.setJobMessage(PigQuartzEnum.JOB_LOG_STATUS_FAIL.getDescription());
sysJobLog.setJobLogStatus(PigQuartzEnum.JOB_LOG_STATUS_FAIL.getType());
sysJobLog.setExceptionInfo(StrUtil.sub(e.getMessage(), 0, 2000));
// 任务表信息更新
updateSysjob.setJobExecuteStatus(PigQuartzEnum.JOB_LOG_STATUS_FAIL.getType());
}
finally {
// 记录执行时间 立刻执行使用的是simpleTeigger
if (trigger instanceof CronTrigger) {
updateSysjob
.setStartTime(trigger.getStartTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
updateSysjob.setPreviousTime(
trigger.getPreviousFireTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
updateSysjob.setNextTime(
trigger.getNextFireTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
}
// 记录执行时长
endTime = System.currentTimeMillis();
sysJobLog.setExecuteTime(String.valueOf(endTime - startTime));
publisher.publishEvent(new SysJobLogEvent(sysJobLog));
sysJobService.updateById(updateSysjob);
}
}
}
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pig.daemon.quartz.util;
import com.pig4cloud.pig.daemon.quartz.config.PigQuartzFactory;
import com.pig4cloud.pig.daemon.quartz.constants.PigQuartzEnum;
import com.pig4cloud.pig.daemon.quartz.entity.SysJob;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.stereotype.Component;
/**
* 定时任务工具类,提供定时任务的增删改查及状态管理功能
*
* @author lengleng
* @date 2025/05/31
*/
@Slf4j
@Component
public class TaskUtil {
/**
* 获取定时任务的唯一key
* @param sysjob 定时任务信息
* @return 定时任务的唯一key
*/
public static JobKey getJobKey(SysJob sysjob) {
return JobKey.jobKey(sysjob.getJobName(), sysjob.getJobGroup());
}
/**
* 获取定时任务触发器的唯一键
* @param sysjob 定时任务信息
* @return 定时任务触发器键
*/
public static TriggerKey getTriggerKey(SysJob sysjob) {
return TriggerKey.triggerKey(sysjob.getJobName(), sysjob.getJobGroup());
}
/**
* 添加或更新定时任务
* @param sysjob 任务信息
* @param scheduler 调度器
*/
public void addOrUpateJob(SysJob sysjob, Scheduler scheduler) {
CronTrigger trigger = null;
try {
JobKey jobKey = getJobKey(sysjob);
// 获得触发器
TriggerKey triggerKey = getTriggerKey(sysjob);
trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
// 判断触发器是否存在(如果存在说明之前运行过但是在当前被禁用了,如果不存在说明一次都没运行过)
if (trigger == null) {
// 新建一个工作任务 指定任务类型为串接进行的
JobDetail jobDetail = JobBuilder.newJob(PigQuartzFactory.class).withIdentity(jobKey).build();
// 将任务信息添加到任务信息中
jobDetail.getJobDataMap().put(PigQuartzEnum.SCHEDULE_JOB_KEY.getType(), sysjob);
// 将cron表达式进行转换
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(sysjob.getCronExpression());
cronScheduleBuilder = this.handleCronScheduleMisfirePolicy(sysjob, cronScheduleBuilder);
// 创建触发器并将cron表达式对象给塞入
trigger = TriggerBuilder.newTrigger()
.withIdentity(triggerKey)
.withSchedule(cronScheduleBuilder)
.build();
// 在调度器中将触发器和任务进行组合
scheduler.scheduleJob(jobDetail, trigger);
}
else {
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(sysjob.getCronExpression());
cronScheduleBuilder = this.handleCronScheduleMisfirePolicy(sysjob, cronScheduleBuilder);
// 按照新的规则进行
trigger = trigger.getTriggerBuilder()
.withIdentity(triggerKey)
.withSchedule(cronScheduleBuilder)
.build();
// 将任务信息更新到任务信息中
trigger.getJobDataMap().put(PigQuartzEnum.SCHEDULE_JOB_KEY.getType(), sysjob);
// 重启
scheduler.rescheduleJob(triggerKey, trigger);
}
// 如任务状态为暂停
if (sysjob.getJobStatus().equals(PigQuartzEnum.JOB_STATUS_NOT_RUNNING.getType())) {
this.pauseJob(sysjob, scheduler);
}
}
catch (SchedulerException e) {
log.error("添加或更新定时任务,失败信息:{}", e.getMessage());
}
}
/**
* 立即执行一次任务
* @param scheduler 调度器
* @param sysJob 任务信息
* @return 任务是否执行成功
*/
public static boolean runOnce(Scheduler scheduler, SysJob sysJob) {
try {
// 参数
JobDataMap dataMap = new JobDataMap();
dataMap.put(PigQuartzEnum.SCHEDULE_JOB_KEY.getType(), sysJob);
scheduler.triggerJob(getJobKey(sysJob), dataMap);
}
catch (SchedulerException e) {
log.error("立刻执行定时任务,失败信息:{}", e.getMessage());
return false;
}
return true;
}
/**
* 暂停定时任务
* @param sysjob 任务信息
* @param scheduler 任务调度器
*/
public void pauseJob(SysJob sysjob, Scheduler scheduler) {
try {
if (scheduler != null) {
scheduler.pauseJob(getJobKey(sysjob));
}
}
catch (SchedulerException e) {
log.error("暂停任务失败,失败信息:{}", e.getMessage());
}
}
/**
* 恢复定时任务
* @param sysjob 任务信息
* @param scheduler 任务调度器
*/
public void resumeJob(SysJob sysjob, Scheduler scheduler) {
try {
if (scheduler != null) {
scheduler.resumeJob(getJobKey(sysjob));
}
}
catch (SchedulerException e) {
log.error("恢复任务失败,失败信息:{}", e.getMessage());
}
}
/**
* 移除定时任务
* @param sysjob 定时任务信息
* @param scheduler 任务调度器
*/
public void removeJob(SysJob sysjob, Scheduler scheduler) {
try {
if (scheduler != null) {
// 停止触发器
scheduler.pauseTrigger(getTriggerKey(sysjob));
// 移除触发器
scheduler.unscheduleJob(getTriggerKey(sysjob));
// 删除任务
scheduler.deleteJob(getJobKey(sysjob));
}
}
catch (Exception e) {
log.error("移除定时任务失败,失败信息:{}", e.getMessage());
}
}
/**
* 启动所有运行中的定时任务
* @param scheduler 调度器实例
* @throws SchedulerException 当启动任务失败时抛出异常
*/
public void startJobs(Scheduler scheduler) {
try {
if (scheduler != null) {
scheduler.resumeAll();
}
}
catch (SchedulerException e) {
log.error("启动所有运行定时任务失败,失败信息:{}", e.getMessage());
}
}
/**
* 暂停所有运行中的定时任务
* @param scheduler 调度器实例
* @throws Exception 暂停任务过程中可能抛出的异常
*/
public void pauseJobs(Scheduler scheduler) {
try {
if (scheduler != null) {
scheduler.pauseAll();
}
}
catch (Exception e) {
log.error("暂停所有运行定时任务失败,失败信息:{}", e.getMessage());
}
}
/**
* 根据任务配置处理错失执行策略
* @param sysJob 任务信息
* @param cronScheduleBuilder 原始Cron调度构建器
* @return 处理后的Cron调度构建器
*/
private CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob sysJob,
CronScheduleBuilder cronScheduleBuilder) {
if (PigQuartzEnum.MISFIRE_DEFAULT.getType().equals(sysJob.getMisfirePolicy())) {
return cronScheduleBuilder;
}
else if (PigQuartzEnum.MISFIRE_IGNORE_MISFIRES.getType().equals(sysJob.getMisfirePolicy())) {
return cronScheduleBuilder.withMisfireHandlingInstructionIgnoreMisfires();
}
else if (PigQuartzEnum.MISFIRE_FIRE_AND_PROCEED.getType().equals(sysJob.getMisfirePolicy())) {
return cronScheduleBuilder.withMisfireHandlingInstructionFireAndProceed();
}
else if (PigQuartzEnum.MISFIRE_DO_NOTHING.getType().equals(sysJob.getMisfirePolicy())) {
return cronScheduleBuilder.withMisfireHandlingInstructionDoNothing();
}
else {
return cronScheduleBuilder;
}
}
/**
* 校验cron表达式是否合法
* @param cronExpression 待校验的cron表达式
* @return true表示合法,false表示不合法
*/
public boolean isValidCron(String cronExpression) {
return CronExpression.isValidExpression(cronExpression);
}
}
server:
port: 5007
spring:
application:
name: @artifactId@
cloud:
nacos:
username: @nacos.username@
password: @nacos.password@
discovery:
server-addr: ${NACOS_HOST:127.0.0.1}:${NACOS_PORT:8848}
config:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
config:
import:
- optional:nacos:application-@profiles.active@.yml
- optional:nacos:${spring.application.name}-@profiles.active@.yml
<?xml version="1.0" encoding="UTF-8"?>
<!--
小技巧: 在根pom里面设置统一存放路径,统一管理方便维护
<properties>
<log-path>/Users/lengleng</log-path>
</properties>
1. 其他模块加日志输出,直接copy本文件放在resources 目录即可
2. 注意修改 <property name="${log-path}/log.path" value=""/> 的value模块
-->
<configuration debug="false" scan="false">
<property name="log.path" value="logs/${project.artifactId}"/>
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<property name="FILE_LOG_PATTERN" value=":%d{yyyy-MM-dd HH:mm:ss.SSS} %5p %t %c{1}: %m%n"/>
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" class="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
class="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
class="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- Console log output -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- Log file debug output -->
<appender name="debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/debug.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/%d{yyyy-MM, aux}/debug.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- Log file error output -->
<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/%d{yyyy-MM}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
</appender>
<logger name="org.activiti.engine.impl.db" level="DEBUG">
<appender-ref ref="debug"/>
</logger>
<!--nacos 心跳 INFO 屏蔽-->
<logger name="com.alibaba.nacos" level="OFF">
<appender-ref ref="error"/>
</logger>
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
<root level="DEBUG">
<appender-ref ref="console"/>
<appender-ref ref="debug"/>
<appender-ref ref="error"/>
</root>
</configuration>
# 如下配置为使用数据库存储定时任务,属性不经常修改,所以不放在 application.yml 中统一管理
spring:
quartz:
properties:
org:
quartz:
scheduler:
instanceName: clusteredScheduler
instanceId: AUTO
jobStore:
class: org.springframework.scheduling.quartz.LocalDataSourceJobStore # 数据库存储
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate # 数据库代理
tablePrefix: QRTZ_ # 表前缀
isClustered: true # 集群
clusterCheckinInterval: 30000 # 集群检查间隔
useProperties: false
threadPool:
class: org.quartz.simpl.SimpleThreadPool # 线程池
threadCount: 50 # 线程数量
threadPriority: 5 # 线程优先级
threadsInheritContextClassLoaderOfInitializingThread: true # 是否继承类加载器
job-store-type: jdbc # 持久化到数据库
jdbc:
initialize-schema: never # 不初始化表结构
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig</artifactId>
<version>${revision}</version>
</parent>
<artifactId>pig-visual</artifactId>
<description>pig 图形化相关功能</description>
<packaging>pom</packaging>
<modules>
<module>pig-codegen</module>
<module>pig-monitor</module>
<module>pig-quartz</module>
</modules>
</project>
...@@ -104,12 +104,9 @@ ...@@ -104,12 +104,9 @@
</dependencies> </dependencies>
<modules> <modules>
<module>pig-register</module>
<module>pig-gateway</module>
<module>pig-auth</module> <module>pig-auth</module>
<module>pig-upms</module> <module>pig-upms</module>
<module>pig-common</module> <module>pig-common</module>
<module>pig-visual</module>
</modules> </modules>
<dependencyManagement> <dependencyManagement>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment