feat(qms): 新增QMS质量管理系统模块
- 创建QMS管理后台应用模块(nflg-qms-admin) - 添加序列号生成服务支持重试机制 - 集成分布式追踪和Loki日志收集 - 配置Nacos服务发现和SSO单点登录 - 添加部署测试工具类用于SIT环境发布 - 生成TSID唯一标识工具类 - 创建数据库操作仓库模块(nflg-wms-repository) - 更新父级项目配置添加QMS模块支持 - 修改WMS管理后台名称标识区分
This commit is contained in:
parent
4b9c5b540c
commit
d4dcc3c5dd
|
|
@ -0,0 +1 @@
|
|||
method.return[#response]=groovy: "com.nflg.wms.common.pojo.ApiResult<" + helper.resolveLink(it.doc("response")) +">"
|
||||
|
|
@ -0,0 +1,277 @@
|
|||
<?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.nflg</groupId>
|
||||
<artifactId>nflg-wms</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>nflg-qms-admin</artifactId>
|
||||
<name>服务-qms管理后台</name>
|
||||
<description>管理后台服务</description>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.nflg</groupId>
|
||||
<artifactId>nflg-wms-common</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.nflg</groupId>
|
||||
<artifactId>nflg-wms-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.biezhi</groupId>
|
||||
<artifactId>TinyPinyin</artifactId>
|
||||
<version>2.0.3.RELEASE</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-ldap</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.zxing</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.zxing</groupId>
|
||||
<artifactId>javase</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.11.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>tech.powerjob</groupId>
|
||||
<artifactId>powerjob-client</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>checker-qual</artifactId>
|
||||
<groupId>org.checkerframework</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!--生成pdf-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xhtmlrenderer</groupId>
|
||||
<artifactId>flying-saucer-pdf</artifactId>
|
||||
<version>9.13.3</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>bcprov-jdk14</artifactId>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<groupId>commons-io</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.itextpdf</groupId>
|
||||
<artifactId>itext7-core</artifactId>
|
||||
<version>7.2.6</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.itextpdf</groupId>
|
||||
<artifactId>font-asian</artifactId>
|
||||
<version>7.2.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-mongodb</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>de.flapdoodle.embed</groupId>
|
||||
<artifactId>de.flapdoodle.embed.mongo</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>cn.idev.excel</groupId>-->
|
||||
<!-- <artifactId>fastexcel</artifactId>-->
|
||||
<!-- <version>1.3.0</version>-->
|
||||
<!-- </dependency>-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>easyexcel</artifactId>
|
||||
<version>3.3.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.retry</groupId>
|
||||
<artifactId>spring-retry</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.loki4j</groupId>
|
||||
<artifactId>loki-logback-appender</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson-spring-boot-starter</artifactId>
|
||||
<version>3.52.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.mwiede</groupId>
|
||||
<artifactId>jsch</artifactId>
|
||||
<version>2.27.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-tracing-bridge-otel</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-exporter-otlp</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-sdk-extension-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.hypersistence</groupId>
|
||||
<artifactId>hypersistence-tsid</artifactId>
|
||||
<version>2.1.4</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-external-libs</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<!-- 指定源目录(项目根目录下的lib) -->
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${project.basedir}/lib</directory>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
<!-- 指定目标目录(与依赖库相同的目录) -->
|
||||
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
||||
<!-- 自动创建目标目录 -->
|
||||
<overwrite>true</overwrite>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-dependencies</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
||||
<excludeGroupIds>${project.groupId}</excludeGroupIds>
|
||||
<includeScope>runtime</includeScope>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<addClasspath>true</addClasspath>
|
||||
<classpathPrefix>lib/</classpathPrefix>
|
||||
<mainClass>com.nflg.qms.admin.AdminApplication</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactSet>
|
||||
<includes>
|
||||
<include>${project.groupId}:*</include>
|
||||
</includes>
|
||||
</artifactSet>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package com.nflg.qms.admin;
|
||||
|
||||
import cn.dev33.satoken.sso.SaSsoManager;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.retry.annotation.EnableRetry;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
@Slf4j
|
||||
@SpringBootApplication
|
||||
@ComponentScan(basePackages = {"com.nflg.qms.admin","com.nflg.wms.starter","com.nflg.wms.repository"})
|
||||
@EnableDiscoveryClient
|
||||
@EnableScheduling
|
||||
@EnableRetry
|
||||
public class AdminApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(AdminApplication.class, args);
|
||||
log.info("【QMS】服务已启动");
|
||||
log.info("---------------------- Sa-Token SSO 模式二 Client 端启动成功 ----------------------");
|
||||
log.info("配置信息:" + SaSsoManager.getClientConfig());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package com.nflg.qms.admin.service;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.nflg.wms.repository.entity.BasdeSerialNumber;
|
||||
import com.nflg.wms.repository.service.IBasdeSerialNumberService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Objects;
|
||||
|
||||
@Component
|
||||
public class BasdeSerialNumberControllerService {
|
||||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");
|
||||
private static final int MAX_RETRY = 5; // 最大重试次数
|
||||
|
||||
@Resource
|
||||
private IBasdeSerialNumberService basdeSerialNumberService;
|
||||
|
||||
@Retryable(
|
||||
maxAttempts = 5, // 最大重试次数(包括第一次调用)
|
||||
backoff = @Backoff(delay = 1000) // 重试间隔1秒
|
||||
)
|
||||
public String generateSerialNumber(Integer businessType) {
|
||||
String currentDate = LocalDate.now().format(DATE_FORMATTER);
|
||||
|
||||
BasdeSerialNumber serialNumber = basdeSerialNumberService.lambdaQuery()
|
||||
.eq(BasdeSerialNumber::getBusinessType, businessType)
|
||||
.one();
|
||||
|
||||
if (Objects.isNull(serialNumber)) {
|
||||
return null;
|
||||
}
|
||||
int nextSerial = 0;
|
||||
if (StrUtil.equals(currentDate, serialNumber.getCurrentDateStr())) {
|
||||
nextSerial = serialNumber.getMaxSerial();
|
||||
}
|
||||
nextSerial = nextSerial + 1;
|
||||
serialNumber.setMaxSerial(nextSerial);
|
||||
serialNumber.setCurrentDateStr(currentDate);
|
||||
basdeSerialNumberService.updateById(serialNumber);
|
||||
// 格式化为4位数字,不足补零
|
||||
return serialNumber.getBusinessPrefixNumber() + currentDate + String.format("%04d", nextSerial);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.nflg.qms.admin.util;
|
||||
|
||||
import io.hypersistence.tsid.TSID;
|
||||
|
||||
public class KeyUtil {
|
||||
|
||||
public static String next(){
|
||||
return TSID.Factory.getTsid().toString();
|
||||
}
|
||||
|
||||
public static String nextBase62(){
|
||||
return TSID.Factory.getTsid().encode(62);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
logging:
|
||||
loki:
|
||||
url: http://192.168.163.83:3100/loki/api/v1/push
|
||||
level:
|
||||
root: info
|
||||
com:
|
||||
nflg: trace
|
||||
alibaba:
|
||||
cloud:
|
||||
nacos: debug
|
||||
org:
|
||||
springframework: debug
|
||||
# sa-token配置
|
||||
sa-token:
|
||||
# SSO-相关配置
|
||||
sso-client:
|
||||
# SSO-Server 用户中心地址
|
||||
server-url: http://sa-sso-server.com:9000
|
||||
# LDAP
|
||||
#spring:
|
||||
# ldap:
|
||||
# urls:
|
||||
# - ldap://192.168.0.2:389
|
||||
# base: dc=nflg
|
||||
# username: commpub@nflg
|
||||
# password: Nflg2019#
|
||||
management:
|
||||
otlp:
|
||||
tracing:
|
||||
endpoint: http://192.168.163.83:4318/v1/traces
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
logging:
|
||||
loki:
|
||||
url: http://192.168.163.83:3100/loki/api/v1/push
|
||||
level:
|
||||
root: info
|
||||
com:
|
||||
nflg: debug
|
||||
alibaba:
|
||||
cloud:
|
||||
nacos: info
|
||||
org:
|
||||
springframework: info
|
||||
# sa-token配置
|
||||
sa-token:
|
||||
# SSO-相关配置
|
||||
sso-client:
|
||||
# SSO-Server 用户中心地址
|
||||
server-url: http://sa-sso-server.com:9000
|
||||
# LDAP
|
||||
#spring:
|
||||
# ldap:
|
||||
# urls:
|
||||
# - ldap://192.168.0.2:389
|
||||
# base: dc=nflg
|
||||
# username: commpub@nflg
|
||||
# password: Nflg2019#
|
||||
management:
|
||||
otlp:
|
||||
tracing:
|
||||
endpoint: http://192.168.163.83:4318/v1/traces
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
logging:
|
||||
loki:
|
||||
url: http://192.168.163.83:3100/loki/api/v1/push
|
||||
level:
|
||||
root: info
|
||||
com:
|
||||
nflg: trace
|
||||
alibaba:
|
||||
cloud:
|
||||
nacos: debug
|
||||
org:
|
||||
springframework: debug
|
||||
# sa-token配置
|
||||
sa-token:
|
||||
# SSO-相关配置
|
||||
sso-client:
|
||||
# SSO-Server 用户中心地址
|
||||
server-url: http://sa-sso-server.com:9000
|
||||
# LDAP
|
||||
#spring:
|
||||
# ldap:
|
||||
# urls:
|
||||
# - ldap://192.168.0.2:389
|
||||
# base: dc=nflg
|
||||
# username: commpub@nflg
|
||||
# password: Nflg2019#
|
||||
management:
|
||||
otlp:
|
||||
tracing:
|
||||
endpoint: http://192.168.163.83:4318/v1/traces
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
server:
|
||||
port: 8105
|
||||
tomcat:
|
||||
max-http-form-post-size: 200MB
|
||||
spring:
|
||||
main:
|
||||
allow-bean-definition-overriding: true
|
||||
application:
|
||||
name: qms-local
|
||||
profiles:
|
||||
active: dev
|
||||
config:
|
||||
import: nacos:shared.properties?group=${spring.profiles.active}&refreshEnabled=true
|
||||
cloud:
|
||||
nacos:
|
||||
config:
|
||||
server-addr: ${nacos.server-addr:192.168.163.83:8848}
|
||||
namespace: wms
|
||||
group: ${spring.profiles.active}
|
||||
discovery:
|
||||
server-addr: ${nacos.server-addr:192.168.163.83:8848}
|
||||
namespace: wms
|
||||
group: ${spring.profiles.active}
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 200MB
|
||||
max-request-size: 200MB
|
||||
logging:
|
||||
level:
|
||||
root: info
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: '*'
|
||||
enabled-by-default: on
|
||||
endpoint:
|
||||
health:
|
||||
show-details: always
|
||||
health:
|
||||
db:
|
||||
enabled: true
|
||||
redis:
|
||||
enabled: true
|
||||
tracing:
|
||||
enabled: true
|
||||
sampling:
|
||||
probability: 1.0
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<!-- 引入Spring Boot默认的基础配置 -->
|
||||
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
|
||||
|
||||
<Property name="logDir" value="./logs" />
|
||||
|
||||
<springProperty scope="context" name="LOKI_URL" source="logging.loki.url" defaultValue=""/>
|
||||
<springProperty scope="context" name="appName" source="spring.application.name" defaultValue="admin"/>
|
||||
<springProperty scope="context" name="profile" source="spring.profiles.active" defaultValue="dev"/>
|
||||
<define name="HOSTIP" class="com.nflg.wms.starter.definer.HostIpDefiner"/>
|
||||
|
||||
<!-- 控制台输出配置 -->
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] %-5level %logger{50} - %msg%n</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 文件日志配置 -->
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${logDir}/nflg-qms-admin.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<!-- 按天滚动日志并按大小拆分 -->
|
||||
<fileNamePattern>${logDir}/%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
||||
<!-- 单个日志文件最大大小 -->
|
||||
<maxFileSize>10MB</maxFileSize>
|
||||
<!-- 日志保留天数 -->
|
||||
<maxHistory>7</maxHistory>
|
||||
<!-- 所有日志文件的总大小上限 -->
|
||||
<totalSizeCap>1GB</totalSizeCap>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] %-5level %logger{50} - %msg%n</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="POWERJOB_WORKER_APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${logDir}/powerjob-worker.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<FileNamePattern>${logDir}/powerjob-worker.%d{yyyy-MM-dd}.log</FileNamePattern>
|
||||
<MaxHistory>7</MaxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
<append>true</append>
|
||||
</appender>
|
||||
|
||||
<appender name="LOKI" class="com.github.loki4j.logback.Loki4jAppender">
|
||||
<http>
|
||||
<url>${LOKI_URL}</url>
|
||||
</http>
|
||||
<labels>
|
||||
service_name = qms
|
||||
app = ${appName}
|
||||
profile = ${profile}
|
||||
host = ${HOSTIP}
|
||||
</labels>
|
||||
<structuredMetadata>
|
||||
traceId = %X{traceId}
|
||||
level = %level
|
||||
thread = %thread
|
||||
class = %logger
|
||||
</structuredMetadata>
|
||||
<batch>
|
||||
<staticLabels>true</staticLabels>
|
||||
<maxItems>5</maxItems>
|
||||
<timeoutMs>10000</timeoutMs>
|
||||
</batch>
|
||||
<message>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] %-5level %logger{50} - %msg %ex</pattern>
|
||||
</message>
|
||||
<verbose>false</verbose>
|
||||
</appender>
|
||||
<appender name="ASYNC_LOKI" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<appender-ref ref="LOKI" />
|
||||
<!-- 队列大小 -->
|
||||
<queueSize>1024</queueSize>
|
||||
<!-- 丢弃策略 -->
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
<!-- 关键:即使队列满也不阻塞 -->
|
||||
<neverBlock>true</neverBlock>
|
||||
<includeCallerData>false</includeCallerData>
|
||||
</appender>
|
||||
|
||||
<logger name="com.alibaba.nacos" level="warn" additivity="false">
|
||||
<!-- <appender-ref ref="FILE"/>-->
|
||||
<appender-ref ref="ASYNC_LOKI"/>
|
||||
</logger>
|
||||
|
||||
<logger name="tech.powerjob" level="INFO" additivity="false">
|
||||
<!-- <appender-ref ref="POWERJOB_WORKER_APPENDER" />-->
|
||||
<appender-ref ref="ASYNC_LOKI"/>
|
||||
</logger>
|
||||
|
||||
<!-- 默认配置 -->
|
||||
<root level="DEBUG">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
<!-- <appender-ref ref="FILE"/>-->
|
||||
<appender-ref ref="ASYNC_LOKI"/>
|
||||
</root>
|
||||
</configuration>
|
||||
|
|
@ -0,0 +1,332 @@
|
|||
import cn.hutool.core.util.StrUtil;
|
||||
import com.jcraft.jsch.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
public class DeploySitTest {
|
||||
|
||||
private static final String serviceName = "admin";
|
||||
private static final String localPath = System.getProperty("user.dir") + "//target//";
|
||||
private static final String remotePath = "/mnt/app/qms/" + serviceName + "/";
|
||||
private static final String jarName = "nflg-qms-" + serviceName + "-1.0.0-SNAPSHOT.jar";
|
||||
|
||||
@Test
|
||||
public void DeployToSit() throws Exception {
|
||||
SSHUtil sshUtil = new SSHUtil();
|
||||
sshUtil.connect("192.168.163.84", 22, "root", "CMP2025nf");
|
||||
//处理主jar包
|
||||
handleFile(sshUtil, localPath + jarName, remotePath + jarName);
|
||||
//处理字体目录
|
||||
// handleDir(sshUtil, localPath, remotePath, "fonts");
|
||||
//处理lib目录
|
||||
// handleDir(sshUtil, localPath, remotePath, "lib");
|
||||
//执行脚本启动服务
|
||||
sshUtil.exec("cd " + remotePath + " && ./restart.sh");
|
||||
sshUtil.disconnect();
|
||||
}
|
||||
|
||||
private void handleDir(SSHUtil sshUtil, String localPath, String remotePath, String dirName) throws Exception {
|
||||
printInfo("处理目录:" + dirName);
|
||||
List<Path> files = getFileList(localPath + "lib");
|
||||
for (Path file : files) {
|
||||
handleFile(sshUtil, file.toString(), remotePath + "lib/" + file.getFileName().toString());
|
||||
}
|
||||
printInfo("处理目录完成");
|
||||
}
|
||||
|
||||
public List<Path> getFileList(String folderPath) throws IOException {
|
||||
Path path = Paths.get(folderPath);
|
||||
if (Files.exists(path) && Files.isDirectory(path)) {
|
||||
List<Path> fileList = new ArrayList<>();
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
|
||||
for (Path entry : stream) {
|
||||
fileList.add(entry);
|
||||
}
|
||||
}
|
||||
return fileList;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void handleFile(SSHUtil sshUtil, String localPath, String remotePath) throws Exception {
|
||||
BasicFileAttributes localFileAttr = Files.readAttributes(Paths.get(localPath), BasicFileAttributes.class);
|
||||
printInfo("处理文件:{},大小:{}", localPath, getSize(localFileAttr.size()));
|
||||
if (sshUtil.fileExists(remotePath)) {
|
||||
if (!StrUtil.equals(getRemoteFileMD5(sshUtil, remotePath), getLocalFileMD5(localPath), true)) {
|
||||
printError("文件不一致,开始上传");
|
||||
sshUtil.uploadFile(localPath, remotePath);
|
||||
} else {
|
||||
printInfo("文件一致");
|
||||
}
|
||||
} else {
|
||||
printError("文件不存在,开始上传");
|
||||
sshUtil.uploadFile(localPath, remotePath);
|
||||
}
|
||||
printInfo("处理完成");
|
||||
}
|
||||
|
||||
private String getRemoteFileMD5(SSHUtil sshUtil, String remotePath) throws Exception {
|
||||
String md5 = StrUtil.subPre(sshUtil.execWithReturn("md5sum " + remotePath), 32);
|
||||
printInfo("远程文件MD5为" + md5);
|
||||
return md5;
|
||||
}
|
||||
|
||||
private String getLocalFileMD5(String localPath) throws Exception {
|
||||
String md5 = DigestUtils.md5Hex(new FileInputStream(localPath));
|
||||
printInfo("本地文件MD5为" + md5);
|
||||
return md5;
|
||||
}
|
||||
|
||||
private String getSize(long size) {
|
||||
long s = 1024;
|
||||
if (size < s) {
|
||||
return size + "B";
|
||||
}
|
||||
s = s * 1024;
|
||||
if (size < s) {
|
||||
return String.format("%.2f", size / 1.00 / 1024) + "KB";
|
||||
}
|
||||
s = s * 1024;
|
||||
if (size < s) {
|
||||
return String.format("%.2f", size / 1.00 / 1024 / 1024) + "MB";
|
||||
}
|
||||
s = s * 1024;
|
||||
return String.format("%.2f", size / 1.00 / 1024 / 1024 / 1024) + "GB";
|
||||
}
|
||||
|
||||
private static class SSHUtil {
|
||||
|
||||
private Session session;
|
||||
private ChannelSftp channelSftp;
|
||||
|
||||
/**
|
||||
* 建立SSH连接
|
||||
*/
|
||||
public void connect(String host, int port, String userName, String password) throws JSchException {
|
||||
printInfo("开始连接服务器,ip:{},port:{},userName:{},password:{}", host, port, userName, password);
|
||||
JSch jsch = new JSch();
|
||||
session = jsch.getSession(userName, host, port);
|
||||
session.setPassword(password);
|
||||
session.setConfig("StrictHostKeyChecking", "no");
|
||||
session.connect();
|
||||
printInfo("连接成功");
|
||||
|
||||
Channel channel = session.openChannel("sftp");
|
||||
channel.connect();
|
||||
channelSftp = (ChannelSftp) channel;
|
||||
}
|
||||
|
||||
public String execWithReturn(String command) throws Exception {
|
||||
printInfo("开始执行命令:{}", command);
|
||||
if (session == null || !session.isConnected()) {
|
||||
throw new IllegalStateException("SSH未连接");
|
||||
}
|
||||
ChannelExec channel = null;
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
// 创建执行命令的通道
|
||||
channel = (ChannelExec) session.openChannel("exec");
|
||||
channel.setCommand(command);
|
||||
// 获取输入流以读取命令输出
|
||||
InputStream in = channel.getInputStream();
|
||||
InputStream err = channel.getExtInputStream();
|
||||
channel.connect();
|
||||
reader = new BufferedReader(new InputStreamReader(in));
|
||||
String data = reader.readLine();
|
||||
// log.info("执行命令成功,返回数据:" + data);
|
||||
BufferedReader errReader = new BufferedReader(new InputStreamReader(err));
|
||||
String errData = errReader.readLine();
|
||||
// log.info("执行命令失败,返回数据:" + errData);
|
||||
if (StrUtil.isBlank(errData)) {
|
||||
return data;
|
||||
} else {
|
||||
errReader.close();
|
||||
throw new Exception("执行命令失败:" + errData);
|
||||
}
|
||||
} finally {
|
||||
if (channel != null) {
|
||||
channel.disconnect();
|
||||
}
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
printInfo("执行命令完毕");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行远程命令并返回输出
|
||||
*/
|
||||
public void exec(String command) {
|
||||
printInfo("开始执行命令:{}", command);
|
||||
if (session == null || !session.isConnected()) {
|
||||
throw new IllegalStateException("SSH未连接");
|
||||
}
|
||||
ChannelExec channel = null;
|
||||
SshResultCallback callback = new SshResultCallback() {
|
||||
@Override
|
||||
public void onOutput(String output) {
|
||||
printInfo(false, output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onErrorOutput(String errorOutput) {
|
||||
printError(false, errorOutput);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted(int exitStatus) {
|
||||
printInfo("输出完毕,退出状态:" + exitStatus);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
printError(false, e.getMessage());
|
||||
}
|
||||
};
|
||||
try {
|
||||
// 创建执行命令的通道
|
||||
channel = (ChannelExec) session.openChannel("exec");
|
||||
channel.setCommand(command);
|
||||
|
||||
// 获取输入流以读取命令输出
|
||||
InputStream in = channel.getInputStream();
|
||||
InputStream err = channel.getExtInputStream();
|
||||
|
||||
channel.connect();
|
||||
|
||||
// 读取命令输出
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
|
||||
BufferedReader errReader = new BufferedReader(new InputStreamReader(err));
|
||||
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
callback.onOutput(line);
|
||||
}
|
||||
|
||||
// 读取错误输出
|
||||
String errLine;
|
||||
while ((errLine = errReader.readLine()) != null) {
|
||||
callback.onErrorOutput(errLine);
|
||||
}
|
||||
|
||||
// 获取退出状态
|
||||
int exitStatus = channel.getExitStatus();
|
||||
callback.onCompleted(exitStatus);
|
||||
} catch (JSchException | IOException e) {
|
||||
callback.onError(e);
|
||||
} finally {
|
||||
if (channel != null) {
|
||||
channel.disconnect();
|
||||
}
|
||||
if (session != null) {
|
||||
session.disconnect();
|
||||
}
|
||||
printInfo("执行命令完毕");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查远程文件是否存在
|
||||
*/
|
||||
public boolean fileExists(String remotePath) throws SftpException {
|
||||
try {
|
||||
channelSftp.stat(remotePath);
|
||||
return true;
|
||||
} catch (SftpException e) {
|
||||
if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
|
||||
return false;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*/
|
||||
public void uploadFile(String localPath, String remotePath) throws SftpException {
|
||||
printInfo("开始上传本地文件{}到远程{}", localPath, remotePath);
|
||||
channelSftp.put(localPath, remotePath);
|
||||
printInfo("上传完成");
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭连接
|
||||
*/
|
||||
public void disconnect() {
|
||||
if (channelSftp != null) {
|
||||
channelSftp.disconnect();
|
||||
}
|
||||
if (session != null) {
|
||||
session.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface SshResultCallback {
|
||||
void onOutput(String output);
|
||||
|
||||
void onErrorOutput(String errorOutput);
|
||||
|
||||
void onCompleted(int exitStatus);
|
||||
|
||||
void onError(Exception e);
|
||||
}
|
||||
|
||||
private static void printError(String msg) {
|
||||
if (StrUtil.isNotBlank(msg)) {
|
||||
System.out.println(red + msg + reset);
|
||||
}
|
||||
}
|
||||
|
||||
private static void printError(boolean addTime, String msg) {
|
||||
if (addTime) {
|
||||
System.out.println(red + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + " " + msg + reset);
|
||||
} else {
|
||||
System.out.println(red + msg + reset);
|
||||
}
|
||||
}
|
||||
|
||||
private static void printInfo(String msg) {
|
||||
printInfo(true, msg);
|
||||
}
|
||||
|
||||
private static void printInfo(boolean addTime, String msg) {
|
||||
if (addTime) {
|
||||
System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + " " + msg);
|
||||
} else {
|
||||
System.out.println(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private static void printInfo(String format, Object... args) {
|
||||
printInfo(true, format, args);
|
||||
}
|
||||
|
||||
private static void printInfo(boolean addTime, String format, Object... args) {
|
||||
if (addTime) {
|
||||
System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + " " + StrUtil.format(format, args));
|
||||
} else {
|
||||
System.out.println(StrUtil.format(format, args));
|
||||
}
|
||||
}
|
||||
|
||||
public static final String red = "\u001B[31m";
|
||||
public static final String reset = "\u001B[0m";
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>nflg-wms-admin</artifactId>
|
||||
<name>服务-管理后台</name>
|
||||
<name>服务-wms管理后台</name>
|
||||
<description>管理后台服务</description>
|
||||
<packaging>jar</packaging>
|
||||
<dependencies>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ public class AdminApplication {
|
|||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(AdminApplication.class, args);
|
||||
log.info("服务已启动");
|
||||
log.info("【WMS】服务已启动");
|
||||
log.info("---------------------- Sa-Token SSO 模式二 Client 端启动成功 ----------------------");
|
||||
log.info("配置信息:" + SaSsoManager.getClientConfig());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>nflg-wms-repository</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<name>模块-db</name>
|
||||
<description>封装数据库操作</description>
|
||||
<packaging>jar</packaging>
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
package com.nflg.wms.shipment.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@Configuration
|
||||
public class RestTemplateConfig {
|
||||
|
||||
@Bean
|
||||
@Lazy
|
||||
public RestTemplate restTemplate() {
|
||||
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
|
||||
factory.setConnectTimeout(3_000);
|
||||
factory.setReadTimeout(10_000);
|
||||
return new RestTemplate(factory);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.nflg.wms.admin.config;
|
||||
package com.nflg.wms.starter.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
Loading…
Reference in New Issue