后端代码上传

This commit is contained in:
huadengwu 2024-06-29 15:42:15 +08:00
parent dd699bdf65
commit fd1dfc932c
1827 changed files with 102764 additions and 0 deletions

View File

@ -0,0 +1,38 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

8
jjj_erp/.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

36
jjj_erp/.idea/compiler.xml generated Normal file
View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile default="true" name="Default" enabled="true" />
<profile name="Annotation profile for jjj-erp" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<processorPath useClasspath="false">
<entry name="$PROJECT_DIR$/../../../../../Maven/maven-repository/org/mapstruct/mapstruct-processor/1.3.1.Final/mapstruct-processor-1.3.1.Final.jar" />
<entry name="$PROJECT_DIR$/../../../../../Maven/maven-repository/com/jolira/hickory/1.0.0/hickory-1.0.0.jar" />
<entry name="$PROJECT_DIR$/../../../../../Maven/maven-repository/org/mapstruct/mapstruct/1.3.1.Final/mapstruct-1.3.1.Final.jar" />
<entry name="$PROJECT_DIR$/../../../../../Maven/maven-repository/junit/junit/4.12/junit-4.12.jar" />
<entry name="$PROJECT_DIR$/../../../../../Maven/maven-repository/org/assertj/assertj-core/3.11.1/assertj-core-3.11.1.jar" />
<entry name="$PROJECT_DIR$/../../../../../Maven/maven-repository/com/google/guava/guava/19.0/guava-19.0.jar" />
<entry name="$PROJECT_DIR$/../../../../../Maven/maven-repository/com/puppycrawl/tools/checkstyle/8.14/checkstyle-8.14.jar" />
<entry name="$PROJECT_DIR$/../../../../../Maven/maven-repository/javax/inject/javax.inject/1/javax.inject-1.jar" />
<entry name="$PROJECT_DIR$/../../../../../Maven/maven-repository/org/codehaus/plexus/plexus-container-default/1.6/plexus-container-default-1.6.jar" />
<entry name="$PROJECT_DIR$/../../../../../Maven/maven-repository/org/springframework/spring-test/4.0.3.RELEASE/spring-test-4.0.3.RELEASE.jar" />
<entry name="$PROJECT_DIR$/../../../../../Maven/maven-repository/org/springframework/spring-beans/4.0.3.RELEASE/spring-beans-4.0.3.RELEASE.jar" />
<entry name="$PROJECT_DIR$/../../../../../Maven/maven-repository/org/springframework/spring-context/4.0.3.RELEASE/spring-context-4.0.3.RELEASE.jar" />
<entry name="$PROJECT_DIR$/../../../../../Maven/maven-repository/joda-time/joda-time/2.9/joda-time-2.9.jar" />
<entry name="$PROJECT_DIR$/../../../../../Maven/maven-repository/org/projectlombok/lombok/1.18.12/lombok-1.18.12.jar" />
</processorPath>
<module name="jjj-saas" />
<module name="jjj-job" />
<module name="generator" />
<module name="jjj-admin" />
<module name="bootstrap" />
<module name="jjj-common" />
<module name="config" />
</profile>
</annotationProcessing>
</component>
</project>

13
jjj_erp/.idea/encodings.xml generated Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/bootstrap/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/config/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/generator/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/jjj-admin/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/jjj-common/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/jjj-job/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/jjj-saas/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
</component>
</project>

30
jjj_erp/.idea/jarRepositories.xml generated Normal file
View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="maven-central" />
<option name="url" value="http://maven.aliyun.com/nexus/content/repositories/central/" />
</remote-repository>
<remote-repository>
<option name="id" value="aliyun" />
<option name="name" value="aliyun-maven" />
<option name="url" value="http://maven.aliyun.com/nexus/content/groups/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="spring-milestones" />
<option name="name" value="Spring Milestones" />
<option name="url" value="https://maven.aliyun.com/repository/spring" />
</remote-repository>
</component>
</project>

26
jjj_erp/.idea/misc.xml generated Normal file
View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
<option value="$PROJECT_DIR$/jjj-saas/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK" />
<component name="SvnBranchConfigurationManager">
<option name="myConfigurationMap">
<map>
<entry key="$PROJECT_DIR$">
<value>
<SvnBranchConfiguration>
<option name="trunkUrl" value="svn://120.78.205.187/product_java/jjj_erp_java/jjj_erp" />
</SvnBranchConfiguration>
</value>
</entry>
</map>
</option>
</component>
</project>

124
jjj_erp/.idea/uiDesigner.xml generated Normal file
View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

6
jjj_erp/.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="svn" />
</component>
</project>

View File

@ -0,0 +1,53 @@
version: '1.0'
name: branch-pipeline
displayName: BranchPipeline
stages:
- stage:
name: compile
displayName: 编译
steps:
- step: build@maven
name: build_maven
displayName: Maven 构建
# 支持6、7、8、9、10、11六个版本
jdkVersion: 8
# 支持2.2.1、3.2.5、3.3.9、3.5.2、3.5.3、3.5.4、3.6.1、3.6.3八个版本
mavenVersion: 3.3.9
# 构建命令
commands:
- mvn -B clean package -Dmaven.test.skip=true
# 非必填字段开启后表示将构建产物暂存但不会上传到制品库中7天后自动清除
artifacts:
# 构建产物名字作为产物的唯一标识可向下传递支持自定义默认为BUILD_ARTIFACT。在下游可以通过${BUILD_ARTIFACT}方式引用来获取构建物地址
- name: BUILD_ARTIFACT
# 构建产物获取路径是指代码编译完毕之后构建物的所在路径如通常jar包在target目录下。当前目录为代码库根目录
path:
- ./target
- step: publish@general_artifacts
name: publish_general_artifacts
displayName: 上传制品
# 上游构建任务定义的产物名默认BUILD_ARTIFACT
dependArtifact: BUILD_ARTIFACT
# 上传到制品库时的制品命名默认output
artifactName: output
dependsOn: build_maven
- stage:
name: release
displayName: 发布
steps:
- step: publish@release_artifacts
name: publish_release_artifacts
displayName: '发布'
# 上游上传制品任务的产出
dependArtifact: output
# 发布制品版本号
version: '1.0.0.0'
# 是否开启版本号自增,默认开启
autoIncrement: true
triggers:
push:
branches:
exclude:
- master
include:
- .*

View File

@ -0,0 +1,51 @@
version: '1.0'
name: master-pipeline
displayName: MasterPipeline
stages:
- stage:
name: compile
displayName: 编译
steps:
- step: build@maven
name: build_maven
displayName: Maven 构建
# 支持6、7、8、9、10、11六个版本
jdkVersion: 8
# 支持2.2.1、3.2.5、3.3.9、3.5.2、3.5.3、3.5.4、3.6.1、3.6.3八个版本
mavenVersion: 3.3.9
# 构建命令
commands:
- mvn -B clean package -Dmaven.test.skip=true
# 非必填字段开启后表示将构建产物暂存但不会上传到制品库中7天后自动清除
artifacts:
# 构建产物名字作为产物的唯一标识可向下传递支持自定义默认为BUILD_ARTIFACT。在下游可以通过${BUILD_ARTIFACT}方式引用来获取构建物地址
- name: BUILD_ARTIFACT
# 构建产物获取路径是指代码编译完毕之后构建物的所在路径如通常jar包在target目录下。当前目录为代码库根目录
path:
- ./target
- step: publish@general_artifacts
name: publish_general_artifacts
displayName: 上传制品
# 上游构建任务定义的产物名默认BUILD_ARTIFACT
dependArtifact: BUILD_ARTIFACT
# 上传到制品库时的制品命名默认output
artifactName: output
dependsOn: build_maven
- stage:
name: release
displayName: 发布
steps:
- step: publish@release_artifacts
name: publish_release_artifacts
displayName: '发布'
# 上游上传制品任务的产出
dependArtifact: output
# 发布制品版本号
version: '1.0.0.0'
# 是否开启版本号自增,默认开启
autoIncrement: true
triggers:
push:
branches:
include:
- master

View File

@ -0,0 +1,40 @@
version: '1.0'
name: pr-pipeline
displayName: PRPipeline
stages:
- stage:
name: compile
displayName: 编译
steps:
- step: build@maven
name: build_maven
displayName: Maven 构建
# 支持6、7、8、9、10、11六个版本
jdkVersion: 8
# 支持2.2.1、3.2.5、3.3.9、3.5.2、3.5.3、3.5.4、3.6.1、3.6.3八个版本
mavenVersion: 3.3.9
# 构建命令
commands:
- mvn -B clean package -Dmaven.test.skip=true
# 非必填字段开启后表示将构建产物暂存但不会上传到制品库中7天后自动清除
artifacts:
# 构建产物名字作为产物的唯一标识可向下传递支持自定义默认为BUILD_ARTIFACT。在下游可以通过${BUILD_ARTIFACT}方式引用来获取构建物地址
- name: BUILD_ARTIFACT
# 构建产物获取路径是指代码编译完毕之后构建物的所在路径如通常jar包在target目录下。当前目录为代码库根目录
path:
- ./target
- step: publish@general_artifacts
name: publish_general_artifacts
displayName: 上传制品
# 上游构建任务定义的产物名默认BUILD_ARTIFACT
dependArtifact: BUILD_ARTIFACT
# 构建产物制品库默认default系统默认创建
artifactRepository: default
# 上传到制品库时的制品命名默认output
artifactName: output
dependsOn: build_maven
triggers:
pr:
branches:
include:
- master

View File

@ -0,0 +1 @@
# bootstrap 项目启动模块

54
jjj_erp/bootstrap/pom.xml Normal file
View File

@ -0,0 +1,54 @@
<?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>net.jjjerp</groupId>
<artifactId>parent</artifactId>
<version>1.0</version>
</parent>
<artifactId>bootstrap</artifactId>
<name>bootstrap</name>
<description>项目启动模块</description>
<dependencies>
<dependency>
<groupId>net.jjjerp</groupId>
<artifactId>jjj-common</artifactId>
</dependency>
<dependency>
<groupId>net.jjjerp</groupId>
<artifactId>jjj-saas</artifactId>
</dependency>
<dependency>
<groupId>net.jjjerp</groupId>
<artifactId>jjj-admin</artifactId>
</dependency>
<dependency>
<groupId>net.jjjerp</groupId>
<artifactId>jjj-job</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,40 @@
package net.jjjerp;
import net.jjjerp.config.JjjNameGenerator;
import net.jjjerp.framework.util.PrintApplicationInfo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* spring-boot-jjj 项目启动入口
*/
@EnableAsync
@EnableScheduling
@EnableTransactionManagement
@EnableConfigurationProperties
@ComponentScan(nameGenerator = JjjNameGenerator.class)
@ServletComponentScan
@MapperScan(basePackages = {"net.jjjerp.**.mapper"}, nameGenerator = JjjNameGenerator.class)
@SpringBootApplication(scanBasePackages = {"net.jjjerp"})
public class SpringBootJjjApplication {
public static void main(String[] args) {
// 启动spring-boot-jjj
ConfigurableApplicationContext context = SpringApplication.run(SpringBootJjjApplication.class, args);
// 打印项目信息
PrintApplicationInfo.print(context);
// 打印项目提示
PrintApplicationInfo.printTip(context);
}
}

View File

@ -0,0 +1,72 @@
package net.jjjerp.aop;
import lombok.extern.slf4j.Slf4j;
import net.jjjerp.framework.log.aop.BaseLogAop;
import net.jjjerp.framework.log.bean.OperationLogInfo;
import net.jjjerp.framework.log.bean.RequestInfo;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
/**
* Controller Aop
* 获取响应结果信息
*/
@Slf4j
@Aspect
@Component
@ConditionalOnProperty(value = {"spring-boot-jjj.aop.log.enable"}, matchIfMissing = true)
public class LogAop extends BaseLogAop {
/**
* 切点
*/
private static final String POINTCUT =
"execution(public * net.jjjerp..*.controller..*.*(..))";
@Around(POINTCUT)
@Override
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
return super.handle(joinPoint);
}
@AfterThrowing(pointcut = POINTCUT, throwing = "exception")
@Override
public void afterThrowing(JoinPoint joinPoint, Exception exception) {
super.handleAfterThrowing(exception);
}
@Override
protected void setRequestId(RequestInfo requestInfo) {
super.handleRequestId(requestInfo);
}
@Override
protected void getRequestInfo(RequestInfo requestInfo) {
// 处理请求参数日志
super.handleRequestInfo(requestInfo);
}
@Override
protected void getResponseResult(Object result) {
// 处理响应结果日志
super.handleResponseResult(result);
}
@Override
protected void finish(RequestInfo requestInfo, OperationLogInfo operationLogInfo, Object result, Exception exception) {
// 只记录shop端日志
if(requestInfo.getPath().startsWith("/api/admin/")){
// 异步保存操作日志
super.saveShopOperationLog(requestInfo, operationLogInfo, result, exception);
// 异步保存登录日志
super.saveShopLoginLog(requestInfo, operationLogInfo, result, exception);
}
}
}

View File

@ -0,0 +1,88 @@
package net.jjjerp.config;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import net.jjjerp.config.constant.DatePattern;
import net.jjjerp.framework.config.jackson.serializer.JacksonBigDecimalSerializer;
import net.jjjerp.framework.config.jackson.deserializer.JacksonDateDeserializer;
import net.jjjerp.framework.config.jackson.deserializer.JacksonDoubleDeserializer;
import net.jjjerp.framework.config.jackson.serializer.JacksonDateSerializer;
import net.jjjerp.framework.config.jackson.serializer.JacksonIntegerDeserializer;
import net.jjjerp.framework.core.xss.XssJacksonDeserializer;
import net.jjjerp.framework.core.xss.XssJacksonSerializer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
@Configuration
public class JacksonConfig implements WebMvcConfigurer {
@Value("${spring-boot-jjj.filter.xss.enable}")
private boolean enableXss;
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = jackson2HttpMessageConverter.getObjectMapper();
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
SimpleModule simpleModule = new SimpleModule();
// XSS序列化
if (enableXss){
simpleModule.addSerializer(String.class, new XssJacksonSerializer());
simpleModule.addDeserializer(String.class, new XssJacksonDeserializer());
}
// Date
simpleModule.addSerializer(Date.class, new JacksonDateSerializer());
simpleModule.addDeserializer(Date.class, new JacksonDateDeserializer());
simpleModule.addSerializer(BigDecimal.class, new JacksonBigDecimalSerializer());
simpleModule.addDeserializer(Integer.class, new JacksonIntegerDeserializer());
simpleModule.addDeserializer(Double.class, new JacksonDoubleDeserializer());
// jdk8日期序列化和反序列化设置
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DatePattern.YYYY_MM_DD_HH_MM_SS)));
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DatePattern.YYYY_MM_DD_HH_MM_SS)));
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DatePattern.YYYY_MM_DD)));
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DatePattern.YYYY_MM_DD)));
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DatePattern.HH_MM_SS)));
javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DatePattern.HH_MM_SS)));
objectMapper.registerModule(simpleModule).registerModule(javaTimeModule).registerModule(new ParameterNamesModule());
jackson2HttpMessageConverter.setObjectMapper(objectMapper);
//放到第一个
converters.add(0, jackson2HttpMessageConverter);
}
}

View File

@ -0,0 +1,18 @@
package net.jjjerp.config;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
/**
* @className: JjjNameGenerator
* @description: 解决不同包下相同类名冲突
*/
public class JjjNameGenerator extends AnnotationBeanNameGenerator {
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
//全限定类名
String beanName = definition.getBeanClassName();
return beanName;
}
}

View File

@ -0,0 +1,75 @@
package net.jjjerp.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import lombok.extern.slf4j.Slf4j;
import net.jjjerp.common.interceptor.JjjTenantLineInnerInterceptor;
import net.jjjerp.config.properties.SpringBootJjjProperties;
import net.jjjerp.framework.core.bean.RequestDetail;
import net.jjjerp.framework.core.util.RequestDetailThreadLocal;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.List;
/**
* MybatisPlus配置
*/
@Slf4j
@Configuration
public class MybatisPlusConfig {
@Value("${spring.profiles.active}")
private String env;
/**
* spring-boot-jjj配置属性
*/
@Autowired
private SpringBootJjjProperties springBootJjjProperties;
@Autowired
private RedisTemplate redisTemplate;
/**
* 新多租户插件配置,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存万一出现问题
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new JjjTenantLineInnerInterceptor(new TenantLineHandler() {
@Override
public Expression getTenantId() {
RequestDetail requestDetail = RequestDetailThreadLocal.getRequestDetail();
// 0为不带租户id
if(requestDetail == null){
return new LongValue(0);
}
return new LongValue(requestDetail.getAppId());
}
@Override
public String getTenantIdColumn() {
// 对应数据库租户标识的列名
return "app_id";
}
// 这是 default 方法,默认返回 false 表示所有表都需要拼多租户条件
@Override
public boolean ignoreTable(String tableName) {
List<String> tables = springBootJjjProperties.getIgnoreTables();
return tables.contains(tableName);
}
},env,redisTemplate));
// 如果用了分页插件注意先 add TenantLineInnerInterceptor add PaginationInnerInterceptor
// 用了分页插件必须设置 MybatisConfiguration#useDeprecatedExecutor = false
// 分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}

View File

@ -0,0 +1,68 @@
package net.jjjerp.config;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import java.time.Duration;
import java.util.*;
/**
* Redis Cache配置
*/
@Configuration
@EnableCaching
public class RedisCacheConfig extends CachingConfigurerSupport {
@Bean
@Override
public KeyGenerator keyGenerator() {
return (target, method, params) -> {
StringBuffer redisKey = new StringBuffer();
redisKey.append(target.getClass().getName()).append("-");
redisKey.append(method.getName());
if (params.length > 0) {
redisKey.append("-").append(Arrays.deepToString(params));
}
return redisKey.toString();
};
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
// 生成一个默认配置通过config对象即可对缓存进行自定义配置
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
// 设置缓存的默认过期时间也是使用Duration设置
// 过期时间5分钟
config = config.entryTtl(Duration.ofMinutes(5));
// 设置一个初始化的缓存空间set集合
Set<String> cacheNames = new HashSet<>();
cacheNames.add("my-redis-cache1");
cacheNames.add("my-redis-cache2");
// 对每个缓存空间应用不同的配置
Map<String, RedisCacheConfiguration> configMap = new HashMap<>(10);
configMap.put("my-redis-cache1", config);
configMap.put("my-redis-cache2", config.entryTtl(Duration.ofSeconds(120)));
// 使用自定义的缓存配置初始化一个cacheManager
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
// 注意这两句的调用顺序一定要先调用该方法设置初始化的缓存名再初始化相关的配置
.initialCacheNames(cacheNames)
.withInitialCacheConfigurations(configMap)
.build();
return cacheManager;
}
}

View File

@ -0,0 +1,41 @@
package net.jjjerp.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* Redis Template 配置
*/
@Configuration
public class RedisTemplateConfig {
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 自定义的string序列化器和fastjson序列化器
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// jackson 序列化器
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// kv 序列化
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setValueSerializer(jsonRedisSerializer);
// hash 序列化
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setHashValueSerializer(jsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}

View File

@ -0,0 +1,28 @@
package net.jjjerp.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory){
return new RestTemplate(factory);
}
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory(){
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
//单位为ms
factory.setReadTimeout(5000);
//单位为ms
factory.setConnectTimeout(5000);
return factory;
}
}

View File

@ -0,0 +1,300 @@
package net.jjjerp.config;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import net.jjjerp.config.properties.JwtProperties;
import net.jjjerp.config.properties.ShiroPermissionProperties;
import net.jjjerp.config.properties.ShiroProperties;
import net.jjjerp.framework.shiro.cache.AdminLoginRedisService;
import net.jjjerp.framework.shiro.cache.ShopLoginRedisService;
import net.jjjerp.framework.shiro.exception.ShiroConfigException;
import net.jjjerp.framework.shiro.jwt.JwtCredentialsMatcher;
import net.jjjerp.framework.shiro.jwt.JwtFilter;
import net.jjjerp.framework.shiro.jwt.JwtRealm;
import net.jjjerp.framework.shiro.service.ShiroLoginService;
import net.jjjerp.framework.util.IniUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.Authenticator;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.pam.FirstSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.mgt.SessionStorageEvaluator;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSessionStorageEvaluator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import java.util.*;
/**
* Shiro配置
* https://shiro.apache.org/spring.html
* https://shiro.apache.org/spring-boot.html
**/
@Slf4j
@Configuration
@EnableConfigurationProperties({ShiroProperties.class})
@ConditionalOnProperty(value = {"spring-boot-jjj.shiro.enable"}, matchIfMissing = true)
public class ShiroConfig {
/**
* JWT过滤器名称
*/
private static final String JWT_FILTER_NAME = "jwtFilter";
/**
* Shiro过滤器名称
*/
private static final String SHIRO_FILTER_NAME = "shiroFilter";
/**
* anon
*/
private static final String ANON = "anon";
@Bean
public CredentialsMatcher credentialsMatcher() {
return new JwtCredentialsMatcher();
}
/**
* JWT数据源验证
*
* @return
*/
@Bean
public JwtRealm jwtRealm(AdminLoginRedisService adminLoginRedisService, ShopLoginRedisService shopLoginRedisService) {
JwtRealm jwtRealm = new JwtRealm(adminLoginRedisService, shopLoginRedisService);
jwtRealm.setCachingEnabled(false);
jwtRealm.setCredentialsMatcher(credentialsMatcher());
return jwtRealm;
}
@Bean
public SessionStorageEvaluator sessionStorageEvaluator() {
DefaultSessionStorageEvaluator sessionStorageEvaluator = new DefaultWebSessionStorageEvaluator();
sessionStorageEvaluator.setSessionStorageEnabled(false);
return sessionStorageEvaluator;
}
@Bean
public DefaultSubjectDAO subjectDAO() {
DefaultSubjectDAO defaultSubjectDAO = new DefaultSubjectDAO();
defaultSubjectDAO.setSessionStorageEvaluator(sessionStorageEvaluator());
return defaultSubjectDAO;
}
/**
* 安全管理器配置
*
* @return
*/
@Bean
public SecurityManager securityManager(AdminLoginRedisService adminLoginRedisService, ShopLoginRedisService shopLoginRedisService) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(jwtRealm(adminLoginRedisService, shopLoginRedisService));
securityManager.setSubjectDAO(subjectDAO());
SecurityUtils.setSecurityManager(securityManager);
return securityManager;
}
/**
* ShiroFilterFactoryBean配置
*
* @param securityManager
* @param adminLoginRedisService
* @param shiroProperties
* @param jwtProperties
* @return
*/
@Bean(SHIRO_FILTER_NAME)
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager,
ShiroLoginService shiroLoginService,
AdminLoginRedisService adminLoginRedisService,
ShopLoginRedisService shopLoginRedisService,
ShiroProperties shiroProperties,
JwtProperties jwtProperties) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, Filter> filterMap = getFilterMap(shiroLoginService, adminLoginRedisService, shopLoginRedisService, jwtProperties);
shiroFilterFactoryBean.setFilters(filterMap);
Map<String, String> filterChainMap = getFilterChainDefinitionMap(shiroProperties);
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);
return shiroFilterFactoryBean;
}
/**
* 获取filter map
*
* @return
*/
private Map<String, Filter> getFilterMap(ShiroLoginService shiroLoginService,
AdminLoginRedisService adminLoginRedisService,
ShopLoginRedisService shopLoginRedisService,
JwtProperties jwtProperties) {
Map<String, Filter> filterMap = new LinkedHashMap<>();
filterMap.put(JWT_FILTER_NAME, new JwtFilter(shiroLoginService, adminLoginRedisService, shopLoginRedisService, jwtProperties));
return filterMap;
}
/**
* Shiro路径权限配置
*
* @return
*/
private Map<String, String> getFilterChainDefinitionMap(ShiroProperties shiroProperties) {
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
// 获取排除的路径
List<String[]> anonList = shiroProperties.getAnon();
log.debug("anonList:{}", JSON.toJSONString(anonList));
if (CollectionUtils.isNotEmpty(anonList)) {
anonList.forEach(anonArray -> {
if (ArrayUtils.isNotEmpty(anonArray)) {
for (String anonPath : anonArray) {
filterChainDefinitionMap.put(anonPath, ANON);
}
}
});
}
// 获取ini格式配置
String definitions = shiroProperties.getFilterChainDefinitions();
if (StringUtils.isNotBlank(definitions)) {
Map<String, String> section = IniUtil.parseIni(definitions);
log.debug("definitions:{}", JSON.toJSONString(section));
for (Map.Entry<String, String> entry : section.entrySet()) {
filterChainDefinitionMap.put(entry.getKey(), entry.getValue());
}
}
// 获取自定义权限路径配置集合
List<ShiroPermissionProperties> permissionConfigs = shiroProperties.getPermission();
log.debug("permissionConfigs:{}", JSON.toJSONString(permissionConfigs));
if (CollectionUtils.isNotEmpty(permissionConfigs)) {
for (ShiroPermissionProperties permissionConfig : permissionConfigs) {
String url = permissionConfig.getUrl();
String[] urls = permissionConfig.getUrls();
String permission = permissionConfig.getPermission();
if (StringUtils.isBlank(url) && ArrayUtils.isEmpty(urls)) {
throw new ShiroConfigException("shiro permission config 路径配置不能为空");
}
if (StringUtils.isBlank(permission)) {
throw new ShiroConfigException("shiro permission config permission不能为空");
}
if (StringUtils.isNotBlank(url)) {
filterChainDefinitionMap.put(url, permission);
}
if (ArrayUtils.isNotEmpty(urls)) {
for (String string : urls) {
filterChainDefinitionMap.put(string, permission);
}
}
}
}
// 如果启用shiro则设置最后一个设置为JWTFilter否则全部路径放行
if (shiroProperties.isEnable()) {
filterChainDefinitionMap.put("/**", JWT_FILTER_NAME);
} else {
filterChainDefinitionMap.put("/**", ANON);
}
log.debug("filterChainMap:{}", JSON.toJSONString(filterChainDefinitionMap));
// 添加默认的filter
Map<String, String> newFilterChainDefinitionMap = addDefaultFilterDefinition(filterChainDefinitionMap);
return newFilterChainDefinitionMap;
}
/**
* 添加默认的filter权限过滤
*
* @param filterChainDefinitionMap
* @return
*/
private Map<String, String> addDefaultFilterDefinition(Map<String, String> filterChainDefinitionMap) {
if (MapUtils.isEmpty(filterChainDefinitionMap)) {
return filterChainDefinitionMap;
}
final Map<String, String> map = new LinkedHashMap<>();
for (Map.Entry<String, String> entry : filterChainDefinitionMap.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
String definition;
String[] strings = value.split(",");
List<String> list = new ArrayList<>();
list.addAll(Arrays.asList(strings));
definition = String.join(",", list);
map.put(key, definition);
}
return map;
}
/**
* ShiroFilter配置
*
* @return
*/
@Bean
public FilterRegistrationBean delegatingFilterProxy() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
DelegatingFilterProxy proxy = new DelegatingFilterProxy();
proxy.setTargetFilterLifecycle(true);
proxy.setTargetBeanName(SHIRO_FILTER_NAME);
filterRegistrationBean.setFilter(proxy);
filterRegistrationBean.setAsyncSupported(true);
filterRegistrationBean.setEnabled(true);
filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC);
return filterRegistrationBean;
}
@Bean
public Authenticator authenticator(AdminLoginRedisService adminLoginRedisService, ShopLoginRedisService shopLoginRedisService) {
ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
authenticator.setRealms(Arrays.asList(jwtRealm(adminLoginRedisService, shopLoginRedisService)));
authenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy());
return authenticator;
}
/**
* Enabling Shiro Annotations
*
* @return
*/
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}

View File

@ -0,0 +1,12 @@
package net.jjjerp.config;
import lombok.extern.slf4j.Slf4j;
/**
* spring-boot-jjj配置
*/
@Slf4j
public class SpringBootJjjConfig {
}

View File

@ -0,0 +1,50 @@
package net.jjjerp.config;
import net.jjjerp.config.properties.SpringBootJjjCorsProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
/**
* 跨域配置
*/
@Slf4j
@Configuration
@ConditionalOnProperty(value = {"spring-boot-jjj.cors.enable"}, matchIfMissing = true)
public class SpringBootJjjCorsConfig {
/**
* CORS跨域设置
*
* @return
*/
@Bean
public FilterRegistrationBean corsFilter(SpringBootJjjCorsProperties corsProperties) {
log.debug("corsProperties:{}", corsProperties);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
// 跨域配置
corsConfiguration.setAllowedOrigins(corsProperties.getAllowedOrigins());
corsConfiguration.setAllowedHeaders(corsProperties.getAllowedHeaders());
corsConfiguration.setAllowedMethods(corsProperties.getAllowedMethods());
corsConfiguration.setAllowCredentials(corsProperties.isAllowCredentials());
corsConfiguration.setExposedHeaders(corsProperties.getExposedHeaders());
corsConfiguration.setMaxAge(corsConfiguration.getMaxAge());
source.registerCorsConfiguration(corsProperties.getPath(), corsConfiguration);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
bean.setEnabled(corsProperties.isEnable());
return bean;
}
}

View File

@ -0,0 +1,145 @@
package net.jjjerp.config;
import com.alibaba.fastjson.JSON;
import net.jjjerp.config.properties.SpringBootJjjFilterProperties;
import net.jjjerp.config.properties.SpringBootJjjInterceptorProperties;
import net.jjjerp.config.properties.SpringBootJjjProperties;
import net.jjjerp.framework.core.filter.RequestDetailFilter;
import net.jjjerp.framework.core.interceptor.FrontInterceptor;
import net.jjjerp.framework.core.interceptor.PermissionInterceptor;
import net.jjjerp.framework.core.xss.XssFilter;
import net.jjjerp.framework.util.IniUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.PostConstruct;
import java.util.Map;
/**
* WebMvc配置
*/
@Slf4j
@Configuration
public class SpringBootJjjWebMvcConfig implements WebMvcConfigurer {
/**
* spring-boot-jjj配置属性
*/
@Autowired
private SpringBootJjjProperties springBootJjjProperties;
/**
* Filter配置
*/
private SpringBootJjjFilterProperties filterConfig;
/**
* 拦截器配置
*/
private SpringBootJjjInterceptorProperties interceptorConfig;
/**
* RequestDetailFilter配置
*
* @return
*/
@Bean
public FilterRegistrationBean requestDetailFilter() {
SpringBootJjjFilterProperties.FilterConfig requestFilterConfig = filterConfig.getRequest();
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new RequestDetailFilter());
filterRegistrationBean.setEnabled(requestFilterConfig.isEnable());
filterRegistrationBean.addUrlPatterns(requestFilterConfig.getUrlPatterns());
filterRegistrationBean.setOrder(requestFilterConfig.getOrder());
filterRegistrationBean.setAsyncSupported(requestFilterConfig.isAsync());
return filterRegistrationBean;
}
/**
* XssFilter配置
*
* @return
*/
@Bean
public FilterRegistrationBean xssFilter() {
SpringBootJjjFilterProperties.FilterConfig xssFilterConfig = filterConfig.getXss();
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new XssFilter());
filterRegistrationBean.setEnabled(xssFilterConfig.isEnable());
filterRegistrationBean.addUrlPatterns(xssFilterConfig.getUrlPatterns());
filterRegistrationBean.setOrder(xssFilterConfig.getOrder());
filterRegistrationBean.setAsyncSupported(xssFilterConfig.isAsync());
return filterRegistrationBean;
}
/**
* 自定义权限拦截器
*
* @return
*/
@Bean
public PermissionInterceptor permissionInterceptor() {
return new PermissionInterceptor();
}
/**
* 前端拦截器
*
* @return
*/
@Bean
public FrontInterceptor frontInterceptor() {
return new FrontInterceptor();
}
@PostConstruct
public void init() {
filterConfig = springBootJjjProperties.getFilter();
interceptorConfig = springBootJjjProperties.getInterceptor();
// 打印SpringBootJjjProperties配置信息
log.debug("SpringBootJjjProperties{}", JSON.toJSONString(springBootJjjProperties));
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 自定义权限拦截器注册
if (interceptorConfig.getPermission().isEnable()) {
registry.addInterceptor(permissionInterceptor())
.addPathPatterns(interceptorConfig.getPermission().getIncludePaths())
.excludePathPatterns(interceptorConfig.getPermission().getExcludePaths());
}
registry.addInterceptor(frontInterceptor())
.addPathPatterns("/front/**");
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 设置项目静态资源访问
String resourceHandlers = springBootJjjProperties.getResourceHandlers();
if (StringUtils.isNotBlank(resourceHandlers)) {
Map<String, String> map = IniUtil.parseIni(resourceHandlers);
for (Map.Entry<String, String> entry : map.entrySet()) {
String pathPatterns = entry.getKey();
String resourceLocations = entry.getValue();
registry.addResourceHandler(pathPatterns)
.addResourceLocations(resourceLocations);
}
}
// 设置上传文件访问路径
registry.addResourceHandler(springBootJjjProperties.getResourceAccessPatterns())
.addResourceLocations("file:" + springBootJjjProperties.getUploadPath() + "/");
}
}

View File

@ -0,0 +1,255 @@
package net.jjjerp.config;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import net.jjjerp.config.properties.SwaggerProperties;
import net.jjjerp.framework.common.exception.SpringBootJjjConfigException;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Component;
import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
import springfox.documentation.RequestHandler;
import springfox.documentation.annotations.ApiIgnore;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin;
import springfox.documentation.spi.schema.contexts.ModelPropertyContext;
import springfox.documentation.spring.web.plugins.ApiSelectorBuilder;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.common.SwaggerPluginSupport;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static springfox.documentation.schema.Annotations.findPropertyAnnotation;
import static springfox.documentation.swagger.schema.ApiModelProperties.findApiModePropertyAnnotation;
/**
* Swagger2全局配置
*/
@Slf4j
@Configuration
@EnableSwagger2
@EnableKnife4j
@Import(BeanValidatorPluginsConfiguration.class)
@ConditionalOnProperty(value = {"knife4j.enable"}, matchIfMissing = true)
public class Swagger2Config {
@Autowired
private SwaggerProperties swaggerProperties;
/**
* 扫描多包时包路径的拆分符,分号
*/
private static final String SPLIT_COMMA = ",";
/**
* 扫描多包时包路径的拆分符,逗号
*/
private static final String SPLIT_SEMICOLON = ";";
/**
* Swagger忽略的参数类型
*/
private Class<?>[] ignoredParameterTypes = new Class[]{
ServletRequest.class,
ServletResponse.class,
HttpServletRequest.class,
HttpServletResponse.class,
HttpSession.class,
ApiIgnore.class
};
@Bean
public Docket createRestApi() {
// 获取需要扫描的包
String[] basePackages = getBasePackages();
ApiSelectorBuilder apiSelectorBuilder = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select();
// 如果扫描的包为空则默认扫描类上有@Api注解的类
if (ArrayUtils.isEmpty(basePackages)) {
apiSelectorBuilder.apis(RequestHandlerSelectors.withClassAnnotation(Api.class));
} else {
// 扫描指定的包
apiSelectorBuilder.apis(basePackage(basePackages));
}
Docket docket = apiSelectorBuilder.paths(PathSelectors.any())
.build()
.enable(swaggerProperties.isEnable())
.ignoredParameterTypes(ignoredParameterTypes)
.globalOperationParameters(getParameters());
return docket;
}
/**
* 获取apiInfo
*
* @return
*/
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title(swaggerProperties.getTitle())
.description(swaggerProperties.getDescription())
.termsOfServiceUrl(swaggerProperties.getUrl())
.contact(new Contact(swaggerProperties.getContactName(), swaggerProperties.getContactUrl(), swaggerProperties.getContactEmail()))
.version(swaggerProperties.getVersion())
.build();
}
/**
* 获取扫描的包
*
* @return
*/
public String[] getBasePackages() {
log.debug("swaggerProperties = " + swaggerProperties);
String basePackage = swaggerProperties.getBasePackage();
if (StringUtils.isBlank(basePackage)) {
throw new SpringBootJjjConfigException("Swagger basePackage不能为空");
}
String[] basePackages = null;
if (basePackage.contains(SPLIT_COMMA)) {
basePackages = basePackage.split(SPLIT_COMMA);
} else if (basePackage.contains(SPLIT_SEMICOLON)) {
basePackages = basePackage.split(SPLIT_SEMICOLON);
}
log.info("swagger scan basePackages:" + Arrays.toString(basePackages));
return basePackages;
}
/**
* 添加额外参数
*
* @return
*/
private List<Parameter> getParameters() {
// 获取自定义参数配置
List<SwaggerProperties.ParameterConfig> parameterConfig = swaggerProperties.getParameterConfig();
if (CollectionUtils.isEmpty(parameterConfig)) {
return null;
}
List<Parameter> parameters = new ArrayList<>();
parameterConfig.forEach(parameter -> {
// 设置自定义参数
parameters.add(new ParameterBuilder()
.name(parameter.getName())
.description(parameter.getDescription())
.modelRef(new ModelRef(parameter.getDataType()))
.parameterType(parameter.getType())
.required(parameter.isRequired())
.defaultValue(parameter.getDefaultValue())
.build());
});
return parameters;
}
public static Predicate<RequestHandler> basePackage(final String[] basePackages) {
return input -> declaringClass(input).transform(handlerPackage(basePackages)).or(true);
}
private static Function<Class<?>, Boolean> handlerPackage(final String[] basePackages) {
return input -> {
// 循环判断匹配
for (String strPackage : basePackages) {
boolean isMatch = input.getPackage().getName().startsWith(strPackage);
if (isMatch) {
return true;
}
}
return false;
};
}
@SuppressWarnings("deprecation")
private static Optional<? extends Class<?>> declaringClass(RequestHandler input) {
return Optional.fromNullable(input.declaringClass());
}
/**
* 按照类中字段顺序显示
*/
@Component
public static class ApiModelPropertyBuilderPlugin implements ModelPropertyBuilderPlugin {
@Override
public void apply(ModelPropertyContext context) {
try {
Optional<BeanPropertyDefinition> beanPropertyDefinitionOptional = context.getBeanPropertyDefinition();
Optional<ApiModelProperty> annotation = Optional.absent();
if (context.getAnnotatedElement().isPresent()) {
annotation = annotation.or(findApiModePropertyAnnotation(context.getAnnotatedElement().get()));
}
if (context.getBeanPropertyDefinition().isPresent()) {
annotation = annotation.or(findPropertyAnnotation(context.getBeanPropertyDefinition().get(), ApiModelProperty.class));
}
if (beanPropertyDefinitionOptional.isPresent()) {
BeanPropertyDefinition beanPropertyDefinition = beanPropertyDefinitionOptional.get();
if (annotation.isPresent() && annotation.get().position() != 0) {
return;
}
AnnotatedField annotatedField = beanPropertyDefinition.getField();
if (annotatedField == null) {
return;
}
Class<?> clazz = annotatedField.getDeclaringClass();
Field[] fields = clazz.getDeclaredFields();
// 获取当前字段对象
Field field = clazz.getDeclaredField(annotatedField.getName());
boolean required = false;
// 获取字段注解
NotNull notNull = field.getDeclaredAnnotation(NotNull.class);
NotBlank notBlank = field.getDeclaredAnnotation(NotBlank.class);
if (notNull != null || notBlank != null) {
required = true;
}
int position = ArrayUtils.indexOf(fields, field);
if (position != -1) {
context.getBuilder().position(position).required(required);
}
}
} catch (Exception exception) {
log.error("Swagger ApiModelProperty预处理异常", exception);
}
}
@Override
public boolean supports(DocumentationType delimiter) {
return SwaggerPluginSupport.pluginDoesApply(delimiter);
}
}
}

View File

@ -0,0 +1,260 @@
package net.jjjerp.handler;
import com.alibaba.fastjson.JSON;
import com.auth0.jwt.exceptions.JWTDecodeException;
import lombok.extern.slf4j.Slf4j;
import net.jjjerp.framework.common.api.ApiCode;
import net.jjjerp.framework.common.api.ApiResult;
import net.jjjerp.framework.common.exception.BusinessException;
import net.jjjerp.framework.common.exception.DaoException;
import net.jjjerp.framework.common.exception.SpringBootJjjException;
import net.jjjerp.framework.core.bean.RequestDetail;
import net.jjjerp.framework.core.util.RequestDetailThreadLocal;
import net.jjjerp.admin.exception.SysLoginException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ControllerAdvice
@RestController
@Slf4j
public class GlobalExceptionHandler {
/**
* 非法参数验证异常
*
* @param ex
* @return
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(value = HttpStatus.OK)
public ApiResult<List<String>> handleMethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException ex) {
printRequestDetail();
BindingResult bindingResult = ex.getBindingResult();
List<String> list = new ArrayList<>();
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
for (FieldError fieldError : fieldErrors) {
list.add(fieldError.getDefaultMessage());
}
Collections.sort(list);
log.error(getApiCodeString(ApiCode.PARAMETER_EXCEPTION) + ":" + JSON.toJSONString(list));
return ApiResult.fail(ApiCode.PARAMETER_EXCEPTION, list);
}
/**
* 系统登录异常处理
*
* @param exception
* @return
*/
@ExceptionHandler(value = SysLoginException.class)
@ResponseStatus(HttpStatus.OK)
public ApiResult<Boolean> sysLoginExceptionHandler(SysLoginException exception) {
printRequestDetail();
printApiCodeException(ApiCode.LOGIN_EXCEPTION, exception);
return ApiResult.fail(ApiCode.LOGIN_EXCEPTION);
}
/**
* HTTP解析请求参数异常
*
* @param exception
* @return
*/
@ExceptionHandler(value = HttpMessageNotReadableException.class)
@ResponseStatus(HttpStatus.OK)
public ApiResult<Boolean> httpMessageNotReadableException(HttpMessageNotReadableException exception) {
printRequestDetail();
printApiCodeException(ApiCode.PARAMETER_EXCEPTION, exception);
return ApiResult.fail(ApiCode.PARAMETER_EXCEPTION);
}
/**
* HTTP
*
* @param exception
* @return
*/
@ExceptionHandler(value = HttpMediaTypeException.class)
@ResponseStatus(HttpStatus.OK)
public ApiResult<Boolean> httpMediaTypeException(HttpMediaTypeException exception) {
printRequestDetail();
printApiCodeException(ApiCode.HTTP_MEDIA_TYPE_EXCEPTION, exception);
return ApiResult.fail(ApiCode.HTTP_MEDIA_TYPE_EXCEPTION);
}
/**
* 自定义业务/数据异常处理
*
* @param exception
* @return
*/
@ExceptionHandler(value = {SpringBootJjjException.class})
@ResponseStatus(HttpStatus.OK)
public ApiResult<String> springBootJjjExceptionHandler(SpringBootJjjException exception) {
int errorCode;
boolean isLog = true;
if (exception instanceof BusinessException) {
if(exception.getErrorCode() != null){
errorCode = exception.getErrorCode();
}else{
errorCode = ApiCode.BUSINESS_EXCEPTION.getCode();
}
isLog = false;
} else if (exception instanceof DaoException) {
errorCode = ApiCode.DAO_EXCEPTION.getCode();
} else {
errorCode = ApiCode.SPRING_BOOT_JJJ_EXCEPTION.getCode();
}
// BusinessException不记录
if(isLog){
printRequestDetail();
log.error("springBootJjjException:", exception);
}
return ApiResult.fail(errorCode, exception.getMessage());
}
/**
* 登录授权异常处理
*
* @param exception
* @return
*/
@ExceptionHandler(value = AuthenticationException.class)
@ResponseStatus(HttpStatus.OK)
public ApiResult<String> authenticationExceptionHandler(AuthenticationException exception) {
printRequestDetail();
printApiCodeException(ApiCode.AUTHENTICATION_EXCEPTION, exception);
return ApiResult.fail(exception.getMessage());
}
/**
* 未认证异常处理
*
* @param exception
* @return
*/
@ExceptionHandler(value = UnauthenticatedException.class)
@ResponseStatus(HttpStatus.OK)
public ApiResult<Boolean> unauthenticatedExceptionHandler(UnauthenticatedException exception) {
printRequestDetail();
printApiCodeException(ApiCode.UNAUTHENTICATED_EXCEPTION, exception);
return ApiResult.fail(ApiCode.UNAUTHENTICATED_EXCEPTION);
}
/**
* 未授权异常处理
*
* @param exception
* @return
*/
@ExceptionHandler(value = UnauthorizedException.class)
@ResponseStatus(HttpStatus.OK)
public ApiResult<Boolean> unauthorizedExceptionHandler(UnauthorizedException exception) {
printRequestDetail();
printApiCodeException(ApiCode.UNAUTHORIZED_EXCEPTION, exception);
return ApiResult.fail(ApiCode.UNAUTHORIZED_EXCEPTION);
}
/**
* JWT Token解析异常
*
* @param exception
* @return
*/
@ExceptionHandler(value = JWTDecodeException.class)
@ResponseStatus(HttpStatus.OK)
public ApiResult<Boolean> jWTDecodeExceptionHandler(JWTDecodeException exception) {
printRequestDetail();
printApiCodeException(ApiCode.JWTDECODE_EXCEPTION, exception);
return ApiResult.fail(ApiCode.JWTDECODE_EXCEPTION);
}
/**
* 默认的异常处理
*
* @param exception
* @return
*/
@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
@ResponseStatus(HttpStatus.OK)
public ApiResult<String> httpRequestMethodNotSupportedExceptionHandler(Exception exception) {
printRequestDetail();
printApiCodeException(ApiCode.HTTP_REQUEST_METHOD_NOT_SUPPORTED_EXCEPTION, exception);
return ApiResult.fail(ApiCode.HTTP_REQUEST_METHOD_NOT_SUPPORTED_EXCEPTION.getCode(), exception.getMessage());
}
/**
* 默认的异常处理
*
* @param exception
* @return
*/
@ExceptionHandler(value = Exception.class)
@ResponseStatus(HttpStatus.OK)
public ApiResult<String> exceptionHandler(Exception exception) {
printRequestDetail();
printApiCodeException(ApiCode.SYSTEM_EXCEPTION, exception);
if(exception.getCause() != null && exception.getCause().getCause() != null){
return ApiResult.fail(0, exception.getCause().getCause().getMessage());
}else{
return ApiResult.fail(0, exception.getMessage());
}
}
/**
* 打印请求详情
*/
private void printRequestDetail() {
RequestDetail requestDetail = RequestDetailThreadLocal.getRequestDetail();
if (requestDetail != null) {
log.error("异常来源ip: {}, path: {}", requestDetail.getIp(), requestDetail.getPath());
}
}
/**
* 获取ApiCode格式化字符串
*
* @param apiCode
* @return
*/
private String getApiCodeString(ApiCode apiCode) {
if (apiCode != null) {
return String.format("errorCode: %s, errorMessage: %s", apiCode.getCode(), apiCode.getMessage());
}
return null;
}
/**
* 打印错误码及异常
*
* @param apiCode
* @param exception
*/
private void printApiCodeException(ApiCode apiCode, Exception exception) {
log.error(getApiCodeString(apiCode), exception);
}
}

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,5 @@
#Generated by Maven
#Sat Jun 29 14:55:11 CST 2024
version=1.0
groupId=net.jjjerp
artifactId=bootstrap

View File

@ -0,0 +1,16 @@
net\jjjerp\SpringBootJjjApplication.class
net\jjjerp\config\RestTemplateConfig.class
net\jjjerp\config\Swagger2Config$ApiModelPropertyBuilderPlugin.class
net\jjjerp\config\MybatisPlusConfig.class
net\jjjerp\config\JjjNameGenerator.class
net\jjjerp\aop\LogAop.class
net\jjjerp\config\Swagger2Config.class
net\jjjerp\config\SpringBootJjjWebMvcConfig.class
net\jjjerp\config\MybatisPlusConfig$1.class
net\jjjerp\config\RedisTemplateConfig.class
net\jjjerp\config\ShiroConfig.class
net\jjjerp\config\SpringBootJjjConfig.class
net\jjjerp\handler\GlobalExceptionHandler.class
net\jjjerp\config\JacksonConfig.class
net\jjjerp\config\RedisCacheConfig.class
net\jjjerp\config\SpringBootJjjCorsConfig.class

View File

@ -0,0 +1,14 @@
D:\IdeaProjects\jiujiujia\svn\jjj_erp_java\jjj_erp\bootstrap\src\main\java\net\jjjerp\config\MybatisPlusConfig.java
D:\IdeaProjects\jiujiujia\svn\jjj_erp_java\jjj_erp\bootstrap\src\main\java\net\jjjerp\config\SpringBootJjjConfig.java
D:\IdeaProjects\jiujiujia\svn\jjj_erp_java\jjj_erp\bootstrap\src\main\java\net\jjjerp\config\SpringBootJjjWebMvcConfig.java
D:\IdeaProjects\jiujiujia\svn\jjj_erp_java\jjj_erp\bootstrap\src\main\java\net\jjjerp\config\JjjNameGenerator.java
D:\IdeaProjects\jiujiujia\svn\jjj_erp_java\jjj_erp\bootstrap\src\main\java\net\jjjerp\handler\GlobalExceptionHandler.java
D:\IdeaProjects\jiujiujia\svn\jjj_erp_java\jjj_erp\bootstrap\src\main\java\net\jjjerp\config\ShiroConfig.java
D:\IdeaProjects\jiujiujia\svn\jjj_erp_java\jjj_erp\bootstrap\src\main\java\net\jjjerp\config\SpringBootJjjCorsConfig.java
D:\IdeaProjects\jiujiujia\svn\jjj_erp_java\jjj_erp\bootstrap\src\main\java\net\jjjerp\aop\LogAop.java
D:\IdeaProjects\jiujiujia\svn\jjj_erp_java\jjj_erp\bootstrap\src\main\java\net\jjjerp\config\RestTemplateConfig.java
D:\IdeaProjects\jiujiujia\svn\jjj_erp_java\jjj_erp\bootstrap\src\main\java\net\jjjerp\config\Swagger2Config.java
D:\IdeaProjects\jiujiujia\svn\jjj_erp_java\jjj_erp\bootstrap\src\main\java\net\jjjerp\config\JacksonConfig.java
D:\IdeaProjects\jiujiujia\svn\jjj_erp_java\jjj_erp\bootstrap\src\main\java\net\jjjerp\config\RedisCacheConfig.java
D:\IdeaProjects\jiujiujia\svn\jjj_erp_java\jjj_erp\bootstrap\src\main\java\net\jjjerp\SpringBootJjjApplication.java
D:\IdeaProjects\jiujiujia\svn\jjj_erp_java\jjj_erp\bootstrap\src\main\java\net\jjjerp\config\RedisTemplateConfig.java

1
jjj_erp/config/README.md Normal file
View File

@ -0,0 +1 @@
# config 项目配置模块

49
jjj_erp/config/pom.xml Normal file
View File

@ -0,0 +1,49 @@
<?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>
<dependencies>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>${hikari.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
</dependencies>
<parent>
<groupId>net.jjjerp</groupId>
<artifactId>parent</artifactId>
<version>1.0</version>
</parent>
<artifactId>config</artifactId>
<name>config</name>
<description>项目配置模块</description>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<excludes>
<exclude>config/application-*.yml</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources/config</directory>
<filtering>true</filtering>
<targetPath>config</targetPath>
<includes>
<include>application-${profileActive}.yml</include>
</includes>
</resource>
</resources>
</build>
</project>

View File

@ -0,0 +1,10 @@
package net.jjjerp.config.constant;
/**
* 可排序查询参数对象
*/
public interface CacheKey {
}

View File

@ -0,0 +1,153 @@
package net.jjjerp.config.constant;
/**
* 公共常量
*/
public interface CommonConstant {
/**
* 默认页码为1
*/
Long DEFAULT_PAGE_INDEX = 1L;
/**
* 默认页大小为10
*/
Long DEFAULT_PAGE_SIZE = 10L;
/**
* 分页总行数名称
*/
String PAGE_TOTAL_NAME = "total";
/**
* 分页数据列表名称
*/
String PAGE_RECORDS_NAME = "records";
/**
* 分页当前页码名称
*/
String PAGE_INDEX_NAME = "pageIndex";
/**
* 分页当前页大小名称
*/
String PAGE_SIZE_NAME = "pageSize";
/**
* 分页最后一页
*/
String PAGE_LAST_NAME = "lastPage";
/**
* 登录用户
*/
String LOGIN_SYS_USER = "loginSysUser";
/**
* 登录token
*/
String JWT_DEFAULT_TOKEN_NAME = "token";
/**
* JWT用户名
*/
String JWT_USERNAME = "username";
/**
* JWT刷新新token响应状态码
*/
int JWT_REFRESH_TOKEN_CODE = 460;
/**
* JWT刷新新token响应状态码
* Redis中不存在但jwt未过期不生成新的token返回361状态码
*/
int JWT_INVALID_TOKEN_CODE = 461;
/**
* JWT Token默认密钥
*/
String JWT_DEFAULT_SECRET = "888888";
/**
* JWT 默认过期时间3600L单位秒
*/
Long JWT_DEFAULT_EXPIRE_SECOND = 3600L;
/**
* 默认头像
*/
String DEFAULT_HEAD_URL = "";
/**
* 管理员角色名称
*/
String ADMIN_ROLE_NAME = "管理员";
String ADMIN_LOGIN = "adminLogin";
/**
* 验证码token
*/
String VERIFY_TOKEN = "verifyToken";
/**
* 图片
*/
String IMAGE = "image";
/**
* JPEG
*/
String JPEG = "JPEG";
/**
* base64前缀
*/
String BASE64_PREFIX = "data:image/png;base64,";
/**
* ..
*/
String SPOT_SPOT = "..";
/**
* ../
*/
String SPOT_SPOT_BACKSLASH = "../";
/**
* SpringBootAdmin登录信息
*/
String ADMIN_LOGIN_SESSION = "adminLoginSession";
/**
* 用户浏览器代理
*/
String USER_AGENT = "User-Agent";
/**
* 本机地址IP
*/
String LOCALHOST_IP = "127.0.0.1";
/**
* 本机地址名称
*/
String LOCALHOST_IP_NAME = "本机地址";
/**
* 局域网IP
*/
String LAN_IP = "192.168";
/**
* 局域网名称
*/
String LAN_IP_NAME = "局域网";
/**
* 忽略appId
*/
String NOT_WITH_App_Id = "notWithAppId";
}

View File

@ -0,0 +1,103 @@
package net.jjjerp.config.constant;
/**
* redis key 常量
**/
public interface CommonRedisKey {
// --------------------admin端登录信息开始----------------------
/**
* 登录用户token信息key
* login:token:tokenMd5
*/
String ADMIN_LOGIN_TOKEN = "erp.admin:login:token:%s";
/**
* 登录用户信息key
* login:user:username
*/
String ADMIN_LOGIN_USER = "erp.admin:login:user:%s";
/**
* 登录用户盐值信息key
* login:salt:username
*/
String ADMIN_LOGIN_SALT = "erp.admin:login:salt:%s";
/**
* 登录用户username token
* login:user:token:username:token
*/
String ADMIN_LOGIN_USER_TOKEN = "erp.admin:login:user:token:%s:%s";
/**
* 登录用户下的所有token
* login:user:token:username:*
*/
String ADMIN_LOGIN_USER_ALL_TOKEN = "erp.admin:login:user:token:%s:*";
// --------------------admin端登录信息结束----------------------
// --------------------shop端登录信息开始----------------------
/**
* 登录用户token信息key
* login:token:tokenMd5
*/
String SHOP_LOGIN_TOKEN = "erp.shop:login:token:%s";
/**
* 登录用户信息key
* login:user:username
*/
String SHOP_LOGIN_USER = "erp.shop:login:user:%s";
/**
* 登录用户盐值信息key
* login:salt:username
*/
String SHOP_LOGIN_SALT = "erp.shop:login:salt:%s";
/**
* 登录用户username token
* login:user:token:username:token
*/
String SHOP_LOGIN_USER_TOKEN = "erp.shop:login:user:token:%s:%s";
/**
* 登录用户下的所有token
* login:user:token:username:*
*/
String SHOP_LOGIN_USER_ALL_TOKEN = "erp.shop:login:user:token:%s:*";
// --------------------shop端登录信息结束----------------------
/**
* 地区缓存
*/
String REGION_DATA = "erp.region.data";
/**
* 设置缓存
*/
String SETTING_DATA = "erp.setting.data:%s:%s";
/**
* 所有设置缓存
*/
String SETTING_DATA_ALL = "erp.setting.data:%s:*";
/**
* 商品分类缓存
*/
String PRODUCT_CATEGORY_DATA = "erp.product.category.data:%s";
/**
* uat测试安全ip
*/
String UAT_IP = "uat.ip";
/**
* 所有设置缓存
*/
String TASK_DATA= "erp.task.data.%s:%s:%s:%s";
}

View File

@ -0,0 +1,39 @@
package net.jjjerp.config.constant;
/**
* 日期格式常量
*/
public interface DatePattern {
/**
* --
*/
String YYYY_MM_DD = "yyyy-MM-dd";
/**
* -- :
*/
String YYYY_MM_DD_HH_MM = "yyyy-MM-dd HH:mm";
/**
* -- ::
*/
String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
/**
* -- :::毫秒
*/
String YYYY_MM_DD_HH_MM_SS_S = "yyyy-MM-dd HH:mm:ss.S";
/**
* :
*/
String HH_MM = "HH:mm";
/**
* ::
*/
String HH_MM_SS = "HH:mm:ss";
/**
* :::毫秒
*/
String HH_MM_SS_S = "HH:mm:ss:S";
}

View File

@ -0,0 +1,73 @@
package net.jjjerp.config.properties;
import net.jjjerp.config.constant.CommonConstant;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* JWT属性配置
**/
@Data
@Component
@ConfigurationProperties(prefix = "spring-boot-jjj.jwt")
public class JwtProperties {
/**
* token名称,默认名称为token可自定义
*/
private String tokenName = CommonConstant.JWT_DEFAULT_TOKEN_NAME;
/**
* 密码
*/
private String secret = CommonConstant.JWT_DEFAULT_SECRET;
/**
* 签发人
*/
private String issuer;
/**
* 主题
*/
private String subject;
/**
* 签发的目标
*/
private String audience;
/**
* token失效时间,默认1小时60*60=3600
*/
private Long expireSecond = CommonConstant.JWT_DEFAULT_EXPIRE_SECOND;
/**
* 是否刷新token默认为true
*/
private boolean refreshToken = true;
/**
* 刷新token倒计时默认10分钟10*60=600
*/
private Integer refreshTokenCountdown;
/**
* redis校验jwt token是否存在
*/
private boolean redisCheck;
/**
* 单用户登录一个用户只能又一个有效的token
*/
private boolean singleLogin;
/**
* 是否进行盐值校验
*/
private boolean saltCheck;
}

View File

@ -0,0 +1,27 @@
package net.jjjerp.config.properties;
import lombok.Data;
/**
* Shiro权限配置映射类
**/
@Data
public class ShiroPermissionProperties {
/**
* 路径
*/
private String url;
/**
* 路径数组
*/
private String[] urls;
/**
* 权限
*/
private String permission;
}

View File

@ -0,0 +1,39 @@
package net.jjjerp.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import java.util.List;
/**
* Shiro配置映射类
**/
@Data
@ConfigurationProperties(prefix = "spring-boot-jjj.shiro")
public class ShiroProperties {
/**
* 是否启用
*/
private boolean enable;
/**
* 路径权限配置
*/
private String filterChainDefinitions;
/**
* 设置无需权限路径集合
*/
private List<String[]> anon;
/**
* 权限配置集合
*/
@NestedConfigurationProperty
private List<ShiroPermissionProperties> permission;
}

View File

@ -0,0 +1,162 @@
package net.jjjerp.config.properties;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.stereotype.Component;
import java.util.Set;
/**
* AOP配置属性
**/
@Data
@Component
@ConfigurationProperties(prefix = "spring-boot-jjj.aop")
public class SpringBootJjjAopProperties {
/**
* 请求路径Filter配置
*/
@NestedConfigurationProperty
private LogAopConfig log = new LogAopConfig();
/**
* 操作日志配置
*/
@NestedConfigurationProperty
private OperationLogConfig operationLog;
/**
* 登录日志配置
*/
private LoginLogConfig loginLog;
@Data
public static class AopConfig {
/**
* 是否启用
*/
private boolean enable;
}
@Data
@EqualsAndHashCode(callSuper = true)
public static class LogAopConfig extends AopConfig {
/**
* 是否启用requestId
*/
private boolean enableRequestId = true;
/**
* 日志输出类型print-type
*/
private LogPrintType logPrintType = LogPrintType.ORDER;
/**
* 请求ID生成类型
*/
private RequestIdType requestIdType = RequestIdType.IDWORK;
/**
* 请求日志在控制台是否格式化输出local环境建议开启服务器环境设置为false
*/
private boolean requestLogFormat = true;
/**
* 响应日志在控制台是否格式化输出local环境建议开启服务器环境设置为false
*/
private boolean responseLogFormat = true;
/**
* 排除路径
*/
private Set<String> excludePaths;
}
/**
* 操作日志配置
*/
@Data
public static class OperationLogConfig {
/**
* 是否启用
*/
private boolean enable = true;
/**
* 排除路径
*/
private Set<String> excludePaths;
}
/**
* 登录日志配置
*/
@Data
public static class LoginLogConfig {
/**
* 是否启用
*/
private boolean enable = true;
/**
* 登录路径
*/
private String loginPath = "/login";
/**
* 登出路径
*/
private String logoutPath = "/logout";
}
/**
* 日志打印类型
**/
public enum LogPrintType {
/**
* 不打印日志
*/
NONE,
/**
* 请求和响应日志按照执行顺序分开打印
*/
ORDER,
/**
* 方法执行结束时连续分开打印请求和响应日志
*/
LINE,
/**
* 方法执行结束时合并请求和响应日志同时打印
*/
MERGE;
}
/**
* 请求ID生成类型
**/
public enum RequestIdType {
/**
* 生成UUID无中横线
*/
UUID,
/**
* 生成数字
*/
IDWORK
}
}

View File

@ -0,0 +1,62 @@
package net.jjjerp.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.web.cors.CorsConfiguration;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* 跨域属性配置
**/
@Data
@Component
@ConfigurationProperties(prefix = "spring-boot-jjj.cors")
public class SpringBootJjjCorsProperties {
/**
* 是否启用跨域默认启用
*/
private boolean enable = true;
/**
* CORS过滤的路径默认/**
*/
private String path = "/**";
/**
* 允许访问的源
*/
private List<String> allowedOrigins = Collections.singletonList(CorsConfiguration.ALL);
/**
* 允许访问的请求头
*/
private List<String> allowedHeaders = Collections.singletonList(CorsConfiguration.ALL);
/**
* 是否允许发送cookie
*/
private boolean allowCredentials = true;
/**
* 允许访问的请求方式
*/
private List<String> allowedMethods = Collections.singletonList(CorsConfiguration.ALL);
/**
* 允许响应的头
*/
private List<String> exposedHeaders = Arrays.asList("token");
/**
* 该响应的有效时间默认为30分钟在有效时间内浏览器无须为同一请求再次发起预检请求
*/
private Long maxAge = 1800L;
}

View File

@ -0,0 +1,54 @@
package net.jjjerp.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.stereotype.Component;
/**
* Filter配置属性
**/
@Data
@Component
@ConfigurationProperties(prefix = "spring-boot-jjj.filter")
public class SpringBootJjjFilterProperties {
/**
* RequestDetail Filter配置
*/
@NestedConfigurationProperty
private FilterConfig request = new FilterConfig();
/**
* XSS Filter配置
*/
@NestedConfigurationProperty
private FilterConfig xss = new FilterConfig();
@Data
public static class FilterConfig {
/**
* 是否启用
*/
private boolean enable;
/**
* 过滤的路径
*/
private String[] urlPatterns;
/**
* 排序
*/
private int order;
/**
* 是否支持异步
*/
private boolean async;
}
}

View File

@ -0,0 +1,68 @@
package net.jjjerp.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.stereotype.Component;
/**
* spring-boot-jjj拦截器配置属性
*/
@Data
@Component
@ConfigurationProperties(prefix = "spring-boot-jjj.interceptor")
public class SpringBootJjjInterceptorProperties {
/**
* SpringBootAdmin权限拦截器
*/
private InterceptorConfig springbootadmin = new InterceptorConfig();
/**
* 自定义权限拦截器
*/
@NestedConfigurationProperty
private InterceptorConfig permission = new InterceptorConfig();
/**
* 资源拦截器
*/
@NestedConfigurationProperty
private InterceptorConfig resource = new InterceptorConfig();
/**
* 上传拦截器
*/
@NestedConfigurationProperty
private InterceptorConfig upload = new InterceptorConfig();
/**
* 下载拦截器
*/
@NestedConfigurationProperty
private InterceptorConfig download = new InterceptorConfig();
@Data
public static class InterceptorConfig {
/**
* 是否启用
*/
private boolean enable;
/**
* 包含的路径
*/
private String[] includePaths = new String[]{};
/**
* 排除路径
*/
private String[] excludePaths = new String[]{};
}
}

View File

@ -0,0 +1,149 @@
package net.jjjerp.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* spring-boot-jjj属性配置信息
*/
@Data
@Component
@ConfigurationProperties(prefix = "spring-boot-jjj")
public class SpringBootJjjProperties {
/**
* 当前版本号
*/
private String projectVersion;
/**
* 是否启用ansi控制台输出有颜色的字体local环境建议开启服务器环境设置为false
*/
private boolean enableAnsi;
/**
* 项目IP或域名地址
*/
private String serverIp;
/**
* 是否启用验证码
*/
private boolean enableVerifyCode;
/**
* 新建登录用户初始化盐值
*/
private String loginInitSalt;
/**
* 新建登录用户初始化密码
*/
private String loginInitPassword;
/**
* 新建用户初始化头像
*/
private String loginInitHead;
/**
* 实现BaseEnum接口的枚举包
*/
private String[] enumPackages;
/**
* 拦截器配置
*/
@NestedConfigurationProperty
private SpringBootJjjInterceptorProperties interceptor;
/**
* 过滤器配置
*/
@NestedConfigurationProperty
private SpringBootJjjFilterProperties filter;
/**
* 上传目录
*/
private String uploadPath;
/**
* 资源访问路径前端访问
*/
private String resourceAccessPath;
/**
* 资源访问路径后段配置资源映射/拦截器使用
*/
private String resourceAccessPatterns;
/**
* 资源访问全路径
*/
private String resourceAccessUrl;
/**
* 允许上传的文件后缀集合
*/
private List<String> allowUploadFileExtensions;
/**
* 允许下载的文件后缀集合
*/
private List<String> allowDownloadFileExtensions;
/**
* 租户忽略表
*/
private List<String> ignoreTables;
/**
* JWT配置
*/
@NestedConfigurationProperty
private JwtProperties jwt;
/**
* Shiro配置
*/
@NestedConfigurationProperty
private ShiroProperties shiro = new ShiroProperties();
/**
* 项目静态资源访问配置
*
* @see SpringBootJjjWebMvcConfig addResourceHandlers
*/
private String resourceHandlers;
/**
* 跨域配置
*/
@NestedConfigurationProperty
private SpringBootJjjCorsProperties cors = new SpringBootJjjCorsProperties();
/**
* Swagger路径
*/
private List<String> swaggerPaths;
/**
* 静态资源访问全路径
*/
//private String staticAccessUrl;
private static String staticAccessUrl;
public static String getStaticAccessUrl() {
return staticAccessUrl;
}
public void setStaticAccessUrl(String staticAccessUrl) {
SpringBootJjjProperties.staticAccessUrl = staticAccessUrl;
}
}

View File

@ -0,0 +1,31 @@
package net.jjjerp.config.properties;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
* 配置文件属性映射为静态属性
**/
@Slf4j
@Data
@Component
public class SpringBootJjjStaticProperties {
public static String INFO_PROJECT_VERSION = "";
@Value("${info.project-version}")
private String infoProjectVersion;
@PostConstruct
public void init() {
INFO_PROJECT_VERSION = this.infoProjectVersion;
log.debug("INFO_PROJECT_VERSION:" + INFO_PROJECT_VERSION);
}
}

View File

@ -0,0 +1,114 @@
package net.jjjerp.config.properties;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* Swagger配置属性
**/
@Data
@Component
@ConfigurationProperties(prefix = "spring-boot-jjj.swagger")
public class SwaggerProperties {
/**
* 是否启用Swagger
*/
private boolean enable;
/**
* 扫描的基本包
*/
@Value("${spring-boot-jjj.swagger.base.package}")
private String basePackage;
/**
* 联系人邮箱
*/
@Value("${spring-boot-jjj.swagger.contact.email}")
private String contactEmail;
/**
* 联系人名称
*/
@Value("${spring-boot-jjj.swagger.contact.name}")
private String contactName;
/**
* 联系人网址
*/
@Value("${spring-boot-jjj.swagger.contact.url}")
private String contactUrl;
/**
* 描述
*/
private String description;
/**
* 标题
*/
private String title;
/**
* 网址
*/
private String url;
/**
* 版本
*/
private String version;
/**
* 自定义参数配置
*/
@NestedConfigurationProperty
private List<ParameterConfig> parameterConfig;
/**
* 自定义参数配置
*/
@Data
public static class ParameterConfig {
/**
* 名称
*/
private String name;
/**
* 描述
*/
private String description;
/**
* 参数类型
* header, cookie, body, query
*/
private String type = "head";
/**
* 数据类型
*/
private String dataType = "String";
/**
* 是否必填
*/
private boolean required;
/**
* 默认值
*/
private String defaultValue;
}
}

View File

@ -0,0 +1,28 @@
spring-boot-jjj:
# 是否启用ansi控制台输出有颜色的字体dev环境建议开启服务器环境设置为false
enable-ansi: true
# 文件上传下载配置
upload-path: D:/product_java/jjj_erp_java/uploads
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/jjj_erp_java?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: root
# Redis配置
redis:
database: 0
host: 127.0.0.1
password: 123456
port: 6379
# logback.xml中有详细的日志配置
# 打印SQL语句和结果集本地开发环境可开启线上注释掉
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
logging:
file:
path: D:/java/logs/erp

View File

@ -0,0 +1,40 @@
server:
port: 8892
spring-boot-jjj:
# 是否启用ansi控制台输出有颜色的字体dev环境建议开启服务器环境设置为false
enable-ansi: false
# 当前环境服务IP地址/域名
server-ip: https://erp.jjjshop.net
# 文件上传下载配置
upload-path: /www/wwwroot/java/uploads
# AOP配置
aop:
# Aop日志配置
log:
# NONE不打印日志
# ORDER请求和响应日志按照执行顺序分开打印
# LINE方法执行结束时连续分开打印请求和响应日志
# MERGE方法执行结束时合并请求和响应日志同时打印
log-print-type: NONE
# logback.xml中有详细的日志配置
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/jjj_erp_java?autoReconnect=true&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: cLGfTNk3sBRmRBnw
# Redis配置
redis:
database: 0
host: localhost
password: 123456
port: 6379
# 打印SQL语句和结果集本地开发环境可开启线上注释掉
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
logging:
file:
path: /www/wwwroot/erp.jjjerp.net/logs

View File

@ -0,0 +1,42 @@
server:
port: 8892
spring-boot-jjj:
# 是否启用ansi控制台输出有颜色的字体dev环境建议开启服务器环境设置为false
enable-ansi: false
# 当前环境服务IP地址/域名
server-ip: http://150.158.154.41
# 文件上传下载配置
upload-path: /www/wwwroot/jjj_erp.com/uploads
# AOP配置
aop:
# Aop日志配置
log:
# NONE不打印日志
# ORDER请求和响应日志按照执行顺序分开打印
# LINE方法执行结束时连续分开打印请求和响应日志
# MERGE方法执行结束时合并请求和响应日志同时打印
log-print-type: NONE
# logback.xml中有详细的日志配置
logging:
path: /www/wwwroot/jjj_erp.com/logs
################################ spring config end #################################
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/jjj_erp?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
username: jjj_erp
password: 2STCfnf2WRSGfRn3
# Redis配置
redis:
database: 0
host: localhost
password: 123456
port: 6379
# 打印SQL语句和结果集本地开发环境可开启线上注释掉
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

View File

@ -0,0 +1,42 @@
server:
port: 8892
spring-boot-jjj:
# 是否启用ansi控制台输出有颜色的字体dev环境建议开启服务器环境设置为false
enable-ansi: false
# 当前环境服务IP地址/域名
server-ip: http://demo-erp.jjjerp.net
# 文件上传下载配置
upload-path: /www/wwwroot/demo-erp.jjjerp.net/uploads
# AOP配置
aop:
# Aop日志配置
log:
# NONE不打印日志
# ORDER请求和响应日志按照执行顺序分开打印
# LINE方法执行结束时连续分开打印请求和响应日志
# MERGE方法执行结束时合并请求和响应日志同时打印
log-print-type: NONE
# logback.xml中有详细的日志配置
logging:
path: /www/wwwroot/demo-erp.jjjerp.net/logs
################################ spring config end #################################
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/demo_erp?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
username: demo_erp
password: znJ5J3j7nzwA45cx
# Redis配置
redis:
database: 0
host: localhost
password: 123456
port: 6379
# 打印SQL语句和结果集本地开发环境可开启线上注释掉
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

View File

@ -0,0 +1,381 @@
# spring-boot-jjj Common Config
############################# 访问路径、端口tomcat start #############################
server:
port: 8892
servlet:
context-path: /api
encoding:
charset: UTF-8
enabled: true
force: true
tomcat:
threads:
max: 1000
min-spare: 30
uri-encoding: UTF-8
############################# 访问路径、端口tomcat end ###############################
################################ spring config start ###############################
spring:
application:
name: spring-boot-jjj
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
banner:
charset: UTF-8
location: classpath:config/banner.txt
servlet:
# 文件上传配置
multipart:
# 指定上传文件的临时目录
location: /opt/upload/tmp
# 单个文件最大值
max-file-size: 10MB
# 单个请求文件总计最大值
max-request-size: 100MB
mvc:
async:
request-timeout: 20000
# 当前项目maven激活环境例如dev/test/uat/prod对应pom.xml中profile设置值
---
spring:
profiles:
active: @profileActive@
# logback.xml中有详细的日志配置
logging:
config: classpath:config/logback.xml
# 方便Spring Boot Admin页面上实时查看日志
file:
name: logs/${spring.application.name}.log
path: ./logs
################################ spring config end #################################
############################## spring-boot-jjj start ##############################
spring-boot-jjj:
# 当前版本号
project-version: '@project.version@'
# 是否启用ansi控制台输出有颜色的字体
enable-ansi: true
# 服务器IP地址或域名
server-ip: http://127.0.0.1:${server.port}
# 是否启用验证码
enable-verify-code: false
# 实现BaseEnum接口的枚举包
enum-packages: net.jjjerp.framework.common.enums
# Swagger路径
swagger-paths: /swagger-ui.html,/docs,/doc.html,/swagger-resources/**,/webjars/**,/v2/api-docs,/csrf,/v2/api-docs-ext,/null/swagger-resources/**
# Filter配置
filter:
request:
enable: true
url-patterns: /*
order: 1
async: true
xss:
enable: true
url-patterns: /*
order: 2
async: true
# 拦截器配置
interceptor:
permission:
enable: false
include-paths: /**
resource:
enable: false
include-paths: ${spring-boot-jjj.resource-access-patterns}
upload:
enable: false
include-paths: /uploads/**
download:
enable: false
include-paths: /download/**
# AOP配置
aop:
# Aop日志配置
log:
# 是否启用
enable: true
# 是否启用requestId
enable-request-id: true
# requestId生成规则: UUID/IDWORK
request-id-type: IDWORK
# NONE不打印日志
# ORDER请求和响应日志按照执行顺序分开打印
# LINE方法执行结束时连续分开打印请求和响应日志
# MERGE方法执行结束时合并请求和响应日志同时打印
log-print-type: ORDER
# 请求日志在控制台是否格式化输出local环境建议开启服务器环境设置为false
request-log-format: true
# 响应日志在控制台是否格式化输出local环境建议开启服务器环境设置为false
response-log-format: true
# 排除的路径
exclude-paths: /,/csrf
# 操作日志配置
operation-log:
# 是否启用
enable: true
# 排除的路径
exclude-paths:
# 登录日志配置
login-log:
# 是否启用
enable: true
# 登录地址
login-path: /admin/passport/login
# 登出地址
logout-path: /admin/passport/logout
# 文件上传下载配置
# 上传路径配置
upload-path: /opt/resource/uploads/
# 资源访问路径
resource-access-path: /resource/
# 资源访问路径匹配:/resource/**
resource-access-patterns: ${spring-boot-jjj.resource-access-path}**
# 资源访问全路径前缀http://127.0.0.1:8888/resource/
resource-access-url: ${spring-boot-jjj.server-ip}${server.servlet.context-path}${spring-boot-jjj.resource-access-path}
# 静态资源访问目录
static-access-url: ${spring-boot-jjj.server-ip}${server.servlet.context-path}/static/
# 全局允许上传的类型
allow-upload-file-extensions: jpg,png,docx,xlsx,pptx,pdf
# 全局允许下载的类型
allow-download-file-extensions: jpg,png,docx,xlsx,pptx,pdf
# 租户忽略表
ignore_tables: jjjerp_region,jjjerp_message,jjjerp_message_field,jjjerp_material_property
############################ CORS start ############################
# CORS跨域配置默认允许跨域
cors:
# 是否启用跨域,默认启用
enable: true
# CORS过滤的路径默认/**
path: /**
# 允许访问的源
allowed-origins: '*'
# 允许访问的请求头
allowed-headers: '*'
# 是否允许发送cookie
allow-credentials: true
# 允许访问的请求方式
allowed-methods: OPTION,GET,POST
# 允许响应的头
exposed-headers: token
# 该响应的有效时间默认为30分钟在有效时间内浏览器无须为同一请求再次发起预检请求
max-age: 1800
############################ CORS end ##############################
########################## Resource start ##########################
# 静态资源访问配置
resource-handlers: |
/static/**=classpath:/static/
swagger-ui.html=classpath:/META-INF/resources/
/webjars/**=classpath:/META-INF/resources/webjars/
doc.html=classpath:/META-INF/resources/
########################## Resource end ############################
######################## Spring Shiro start ########################
shiro:
# 是否启用
enable: true
# 权限配置
anon:
# 排除静态资源
- /static/**,/templates/**
# 排除Swagger
- ${spring-boot-jjj.swagger-paths}
# 排除actuator
- /actuator/**
- # 排除首页
- /,/index.html
# front模块
- /front/**
# admin模块
- /saas/passport/login
# shop模块
- /admin/index/base,/admin/passport/login
# job模块
- /job/**
# 多行字符串权限配置
filter-chain-definitions: |
/resource/**=anon
/uploads/**=anon
/verificationCode/**=anon
/enum=anon
/getSysUserInfo=anon
######################## Spring Shiro end ##########################
############################ JWT start #############################
jwt:
# token请求头名称
token-name: token
# jwt密钥
secret: 888888
# 发行人
issuer: ${spring.application.name}
# 观众
audience: web
# 默认过期时间1小时单位
expire-second: 36000
# 是否刷新token
refresh-token: true
# 刷新token的时间间隔默认10分钟单位
refresh-token-countdown: 600
# redis校验jwt token是否存在,可选
redis-check: true
# true: 同一个账号只能是最后一次登录token有效false同一个账号可多次登录
single-login: false
# 盐值校验如果不加自定义盐值则使用secret校验
salt-check: true
############################ JWT end ###############################
############################### spring-boot-jjj end ###############################
############################### mybatis-plus start #################################
mybatis-plus:
# 启动时是否检查MyBatis XML文件是否存在
check-config-location: true
# 支持统配符 * 或者 ; 分割
typeEnumsPackage: net.jjjerp.*.enums
# MyBatis原生配置
configuration:
# 字段名称下划线转驼峰命名
map-underscore-to-camel-case: true
global-config:
db-config:
# 全局默认主键类型
id-type: auto
# 逻辑已删除值(默认为 1)
logic-delete-value: 1
# 逻辑未删除值(默认为 0)
logic-not-delete-value: 0
# 表前缀
table-prefix: jjjerp_
configuration-properties:
# 自定义sql中表名带前缀
prefix: jjjerp_
# mapper xml映射路径
mapper-locations: classpath*:mapper/**/*Mapper.xml
################################ mybatis-plus end ##################################
############################### HikariCP 数据源配置 start ################################
---
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
type: com.zaxxer.hikari.HikariDataSource
cachePrepStmts: true
prepStmtCacheSize: 300
prepStmtCacheSqlLimit: 2048
useServerPrepStmts: true
useLocalSessionState: true
rewriteBatchedStatements: true
cacheResultSetMetadata: true
cacheServerConfiguration: true
elideSetAutoCommits: true
maintainTimeStats: false
hikari:
minimum-idle: 10
maximum-pool-size: 20
idle-timeout: 10000
max-lifetime: 1800000
connection-timeout: 30000
############################### HikariCP 数据源配置 end ################################
############################### Redis 公共配置 start ###############################
---
spring:
redis:
timeout: 10s
lettuce:
pool:
max-active: 200
max-idle: 8
max-wait: 10s
min-idle: 2
shutdown-timeout: 3s
############################### Redis 公共配置 end ##################################
#################################### Swagger start #################################
---
spring-boot-jjj:
swagger:
# 是否启用
enable: true
base:
# 扫描的包,多个包使用逗号隔开
package: net.jjjerp
contact:
email: 279517889@qq.com
name: jjjerp
url: https://www.jjjshop.net
description:
title: ${spring.application.name} API Documents
version: ${project.version}
# 自定义参数配置可配置N个
parameter-config:
- name: ${spring-boot-jjj.jwt.token-name}
description: Token Request Header
# header, cookie, body, query
type: header
data-type: String
required: false
# 测试接口时自动填充token的值
default-value:
# knife4j配置
knife4j:
enable: ${spring-boot-jjj.swagger.enable}
basic:
enable: false
username: admin
password: admin
#################################### Swagger end ###################################
############################## Spring boot admin start ##############################
# 开启和暴露端点
management:
endpoint:
health:
show-details: ALWAYS
logfile:
#可在线查看日志
enabled: true
endpoints:
web:
exposure:
include: '*'
# 自定义项目信息Spring Boot Admin展示使用
info:
project-groupId: '@project.parent.groupId@'
project-name: ${spring.application.name}
project-finalName: '@boot.artifact.name@'
project-author: jjjerp
project-description: ${spring.application.name} project
project-sourceEncoding: '@project.build.sourceEncoding@'
project-spring-boot-version: '@spring-boot.version@'
project-mybatis-plus-version: '@mybatis-plus-boot-starter.version@'
project-version: '@project.version@'
project-website: 'https://www.jjjshop.net'
project-home: ${spring-boot-jjj.server-ip}${server.servlet.context-path}
project-swagger: ${spring-boot-jjj.server-ip}${server.servlet.context-path}/swagger-ui.html
project-knife4j: ${spring-boot-jjj.server-ip}${server.servlet.context-path}/doc.html
############################## Spring boot admin end ###############################

View File

@ -0,0 +1,13 @@
${AnsiColor.BLUE}
_ _ _ _
(_) | | | | | |
___ _ __ _ __ _ _ __ __ _ ______| |__ ___ ___ | |_ ______ _ __ | |_ _ ___
/ __| '_ \| '__| | '_ \ / _` |______| '_ \ / _ \ / _ \| __|______| '_ \| | | | / __|
\__ \ |_) | | | | | | | (_| | | |_) | (_) | (_) | |_ | |_) | | |_| \__ \
|___/ .__/|_| |_|_| |_|\__, | |_.__/ \___/ \___/ \__| | .__/|_|\__,_|___/
| | __/ | | |
|_| |___/ |_|
${AnsiColor.YELLOW} :: Spring Boot :: ${AnsiColor.WHITE}(v${spring-boot.version})
${AnsiColor.YELLOW} :: jjj-erp :: ${AnsiColor.WHITE}(v${project.version})
https://www.jjjshop.net

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="CONTEXT_NAME" value="spring-boot-jjj"/>
<springProperty name="LOG_PATH" source="logging.path" defaultValue="./logs" />
<property name="log.path" value="${LOG_PATH:-.}"/>
<property name="MAX_FILE_SIZE" value="10MB"/>
<property name="MAX_HISTORY" value="30"/>
<contextName>${CONTEXT_NAME}</contextName>
<!-- 彩色日志 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<!-- 控制台日志样式 -->
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr([%15.15t]){faint} [%X{requestId}] %clr(%-40.40logger{39}){cyan} [%L] %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!-- 文件日志样式 -->
<property name="FILE_LOG_PATTERN" value="${FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} [%t] [%X{requestId}] %-40.40logger{39} %L : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!-- 禁用logback自身日志输出 -->
<statusListener class="ch.qos.logback.core.status.NopStatusListener" />
<!-- 控制台 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- 运行日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
<file>${log.path}/spring-boot-jjj.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/spring-boot-jjj-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>${MAX_FILE_SIZE}</maxFileSize>
<maxHistory>${MAX_HISTORY}</maxHistory>
</rollingPolicy>
</appender>
<!-- 错误日志文件 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
<file>${log.path}/spring-boot-jjj-error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/spring-boot-jjj-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>${MAX_FILE_SIZE}</maxFileSize>
<maxHistory>${MAX_HISTORY}</maxHistory>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 异步写日志 -->
<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>1024</queueSize>
<appender-ref ref ="FILE"/>
</appender>
<appender name="ASYNC_ERROR_FILE" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>1024</queueSize>
<appender-ref ref ="ERROR_FILE"/>
</appender>
<!-- 不同环境的日志级别配置 -->
<springProfile name="dev">
<logger name="net.jjjerp" level="DEBUG"/>
</springProfile>
<!-- 解决SpringBootAdmin错误日志问题 -->
<logger name="org.apache.catalina.connector.CoyoteAdapter" level="OFF"/>
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="ASYNC_FILE" />
<appender-ref ref="ASYNC_ERROR_FILE" />
</root>
</configuration>

View File

@ -0,0 +1,4 @@
#
# 扩展mime-type
# 参考https://svn.apache.org/viewvc/httpd/httpd/trunk/docs/conf/mime.types?revision=1752884&view=co
md=text/markdown

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>404</title>
</head>
<body>
<h1>Not Found</h1>
</body>
</html>

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>500</title>
</head>
<body>
<h1>500</h1>
</body>
</html>

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>404</title>
</head>
<body>
<h1>Error</h1>
</body>
</html>

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Some files were not shown because too many files have changed in this diff Show More