initial commit
This commit is contained in:
14
.gitignore
vendored
Normal file
14
.gitignore
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
*.iml
|
||||
.idea/
|
||||
.svn/
|
||||
classes/
|
||||
generated-sources/
|
||||
generated-test-sources/
|
||||
maven-archiver/
|
||||
maven-status/
|
||||
test-classes/
|
||||
*1.0-SNAPSHOT.jar
|
||||
out
|
||||
rebel.xml
|
||||
*/target/
|
||||
/logs
|
||||
46
.gitlab-ci.yml
Normal file
46
.gitlab-ci.yml
Normal file
@@ -0,0 +1,46 @@
|
||||
stages:
|
||||
- build
|
||||
- package
|
||||
variables:
|
||||
PACKAGE_APP_NAME: rainyhon-factory-digital-example
|
||||
# 各个服务jar包路径
|
||||
ADMIN_JAR_ADDR: web-server/target/web-server.jar
|
||||
# 需要修改: 命名空间名称, 对应 Rancher 上的命名空间
|
||||
NAMESPACE: factory-digital
|
||||
|
||||
maven-build:
|
||||
stage: build
|
||||
tags:
|
||||
- java
|
||||
only:
|
||||
refs:
|
||||
- test
|
||||
- master
|
||||
- tags
|
||||
variables:
|
||||
# 打包环境变量配置
|
||||
MAVEN_OPTS: "-Dmaven.test.skip=true"
|
||||
MAVEN_CLI_OPTS: "--batch-mode -P test"
|
||||
script:
|
||||
- mvn $MAVEN_OPTS $MAVEN_CLI_OPTS package
|
||||
artifacts:
|
||||
# 发送文件到gitlab,在CI/CD界面可下载
|
||||
paths:
|
||||
- web-server/target/*.jar
|
||||
|
||||
docker-build:
|
||||
stage: package
|
||||
tags:
|
||||
- java
|
||||
only:
|
||||
refs:
|
||||
- test
|
||||
- master
|
||||
- tags
|
||||
variables:
|
||||
DOCKER_FACTORY_DIGITAL_APP_IMAGE_NAME: 192.168.1.242:8088/$NAMESPACE/$PACKAGE_APP_NAME
|
||||
script:
|
||||
- docker build --build-arg JAR_FILE=$ADMIN_JAR_ADDR --build-arg PUBLIC_KEY_ADDR=$PUBLIC_KEY_ADDR --build-arg ACTIVE=$CI_COMMIT_REF_NAME -t $DOCKER_FACTORY_DIGITAL_APP_IMAGE_NAME:$CI_COMMIT_SHORT_SHA -t $DOCKER_FACTORY_DIGITAL_APP_IMAGE_NAME:$CI_COMMIT_REF_NAME -t $DOCKER_FACTORY_DIGITAL_APP_IMAGE_NAME:latest .
|
||||
- docker push $DOCKER_FACTORY_DIGITAL_APP_IMAGE_NAME:$CI_COMMIT_SHORT_SHA
|
||||
- docker push $DOCKER_FACTORY_DIGITAL_APP_IMAGE_NAME:$CI_COMMIT_REF_NAME
|
||||
- docker push $DOCKER_FACTORY_DIGITAL_APP_IMAGE_NAME:latest
|
||||
17
Dockerfile
Normal file
17
Dockerfile
Normal file
@@ -0,0 +1,17 @@
|
||||
FROM swr.cn-south-1.myhuaweicloud.com/extend/openjdk:3u342-jdk
|
||||
|
||||
ENV TZ='Asia/Shanghai'
|
||||
ENV APP_FILE web-server.jar
|
||||
ENV APP_HOME /usr/app
|
||||
# Create the home directory for the new app user.
|
||||
RUN mkdir -p /usr/app
|
||||
RUN mkdir -p /usr/app/lib
|
||||
RUN mkdir -p /usr/app/resources
|
||||
|
||||
WORKDIR $APP_HOME
|
||||
COPY web-server/target/lib/* /usr/app/lib/
|
||||
COPY web-server/target/resources/* /usr/app/resources/
|
||||
COPY web-server/target/$APP_FILE /usr/app/
|
||||
|
||||
ENV JVM_OPTS=${JVM_OPTS}
|
||||
ENTRYPOINT java ${JVM_OPTS} -jar ./web-server.jar
|
||||
64
Jenkinsfile
vendored
Normal file
64
Jenkinsfile
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
|
||||
def backend_url = "http://192.168.101.221/iot/yelink-demo.git"
|
||||
def registry_url = "swr.cn-south-2.myhuaweicloud.com"
|
||||
def item = "yelink-demo"
|
||||
def project_name = "yelink_appstore_test"
|
||||
pipeline {
|
||||
agent any
|
||||
parameters {
|
||||
gitParameter branch: '', branchFilter: '.*', defaultValue: 'origin/master', description: '分支', name: 'BACKEND_BRANCH', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'GitParameterDefinition', useRepository: ''
|
||||
string defaultValue: '', description: 'docker镜像版本号', name: 'docker_tag', trim: false
|
||||
booleanParam(name: 'BUILD_DOCKER', defaultValue: true, description: 'Should we run build docker')
|
||||
}
|
||||
stages {
|
||||
stage('拉取后端代码') {
|
||||
steps {
|
||||
sh 'mkdir -p ./backend'
|
||||
dir('./backend'){
|
||||
sh 'rm -rf ./deploy'
|
||||
checkout([$class: 'GitSCM', branches: [[name: "${params.BACKEND_BRANCH}"]], extensions: [], userRemoteConfigs: [[credentialsId: 'weipeng', url: "$backend_url"]]])
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('后端代码编译') {
|
||||
when {
|
||||
expression { return params.BUILD_DOCKER }
|
||||
}
|
||||
steps {
|
||||
dir('./backend') {
|
||||
sh "mvn clean package -DskipTests"
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('构建docker镜像') {
|
||||
when {
|
||||
expression { return params.BUILD_DOCKER }
|
||||
}
|
||||
steps {
|
||||
dir('./backend') {
|
||||
script {
|
||||
sh "docker login -u cn-south-2@TQ0BQOAML1V5Y5HZ9ISP -p c6ec8441a31e8779899f060ee4a9146969b81f439ef5664cd2f5b3bd81cba962 swr.cn-south-2.myhuaweicloud.com"
|
||||
sh "docker build -t ${item}:latest ./"
|
||||
sh "docker tag ${item}:latest ${registry_url}/${project_name}/${item}:${docker_tag}"
|
||||
sh "docker push ${registry_url}/${project_name}/${item}:${docker_tag}"
|
||||
sh "docker rmi -f ${item}"
|
||||
sh "docker rmi -f ${registry_url}/${project_name}/${item}:${docker_tag}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('打包目录') {
|
||||
steps {
|
||||
dir('./backend/deploy') {
|
||||
script {
|
||||
sh 'echo "VERSION=${docker_tag}" > .env'
|
||||
sh "touch yelink-demo_${docker_tag}_${BUILD_NUMBER}_`date +%Y-%m-%d`"
|
||||
sh 'rm -f ../yelink-demo_*.zip'
|
||||
sh 'zip -r yelink-demo_${docker_tag}_${BUILD_NUMBER}_`date +%Y-%m-%d`.zip . -x ".git/*"'
|
||||
sh 'cp yelink-demo_${docker_tag}_${BUILD_NUMBER}_`date +%Y-%m-%d`.zip ../'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
92
README.md
Normal file
92
README.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# cd-xzt-app
|
||||
|
||||
成都鑫众泰改造-后端
|
||||
|
||||
## Getting started
|
||||
|
||||
To make it easy for you to get started with GitLab, here's a list of recommended next steps.
|
||||
|
||||
Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
|
||||
|
||||
## Add your files
|
||||
|
||||
- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
|
||||
- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
|
||||
|
||||
```
|
||||
cd existing_repo
|
||||
git remote add origin http://192.168.1.220/im/factory-digital/cd-xzt/cd-xzt-app.git
|
||||
git branch -M main
|
||||
git push -uf origin main
|
||||
```
|
||||
|
||||
## Integrate with your tools
|
||||
|
||||
- [ ] [Set up project integrations](http://192.168.1.220/im/factory-digital/cd-xzt/cd-xzt-app/-/settings/integrations)
|
||||
|
||||
## Collaborate with your team
|
||||
|
||||
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
|
||||
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
|
||||
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
|
||||
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
|
||||
- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
|
||||
|
||||
## Test and Deploy
|
||||
|
||||
Use the built-in continuous integration in GitLab.
|
||||
|
||||
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
|
||||
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
|
||||
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
|
||||
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
|
||||
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
|
||||
|
||||
***
|
||||
|
||||
# Editing this README
|
||||
|
||||
When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
|
||||
|
||||
## Suggestions for a good README
|
||||
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
|
||||
|
||||
## Name
|
||||
Choose a self-explaining name for your project.
|
||||
|
||||
## Description
|
||||
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
|
||||
|
||||
## Badges
|
||||
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
|
||||
|
||||
## Visuals
|
||||
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
|
||||
|
||||
## Installation
|
||||
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
|
||||
|
||||
## Usage
|
||||
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
|
||||
|
||||
## Support
|
||||
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
|
||||
|
||||
## Roadmap
|
||||
If you have ideas for releases in the future, it is a good idea to list them in the README.
|
||||
|
||||
## Contributing
|
||||
State if you are open to contributions and what your requirements are for accepting them.
|
||||
|
||||
For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
|
||||
|
||||
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
|
||||
|
||||
## Authors and acknowledgment
|
||||
Show your appreciation to those who have contributed to the project.
|
||||
|
||||
## License
|
||||
For open source projects, say how it is licensed.
|
||||
|
||||
## Project status
|
||||
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
|
||||
112
deploy/deploy.sh
Normal file
112
deploy/deploy.sh
Normal file
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env bash
|
||||
TARBALL_PATH=$PWD
|
||||
NIC=$(route -n | awk '{print $1,$8}' | grep "0.0.0.0" | awk '{print $2}')
|
||||
LOCAL_IP=$(ifconfig $NIC | grep inet | grep -v 127.0.0.1 | grep -v inet6 | awk '{print $2}' | tr -d "addr:")
|
||||
WORK_DIR=/opt/factory-digital-app
|
||||
CONFIG_DIR=/opt/config/factory-digital-app
|
||||
LOG_DIR=$(pwd)/deploy_logs/
|
||||
LOG_FILE="${LOG_DIR}/demo_$(date '+%Y%m%d').log"
|
||||
if [[ ! -d "${LOG_DIR}" ]]; then
|
||||
mkdir -p "${LOG_DIR}"
|
||||
touch "$LOG_FILE"
|
||||
fi
|
||||
# 定义颜色
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[0;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
function info() {
|
||||
local MSG=$1
|
||||
local COLOR=${2:-$NC} # 默认不使用颜色
|
||||
local DATE=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
# 打印到终端,使用指定的颜色
|
||||
echo -e "${COLOR}[${DATE}] ${MSG}${NC}"
|
||||
# 追加到日志文件,无颜色
|
||||
echo "[${DATE}] ${MSG}" >>"${LOG_FILE}"
|
||||
}
|
||||
|
||||
function prepare() {
|
||||
|
||||
info "[Preparing deploy ..............]" $YELLOW
|
||||
# 检查部署目录是否存在
|
||||
if [[ ! -d $WORK_DIR ]]; then
|
||||
mkdir -p $WORK_DIR
|
||||
else
|
||||
info "[$WORK_DIR] folder has existed! prepare check fail!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -d $CONFIG_DIR ]]; then
|
||||
mkdir -p $CONFIG_DIR
|
||||
fi
|
||||
|
||||
docker_process=$(docker ps | grep factory-digital-app| wc -l)
|
||||
if [[ $docker_process -gt 0 ]]; then
|
||||
info "factory-digital-appprocess has existed; prepare check fail!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
dos2unix $TARBALL_PATH/*
|
||||
cp $TARBALL_PATH/docker-compose.yml $WORK_DIR/
|
||||
cp $TARBALL_PATH/.env $WORK_DIR/
|
||||
cp $TARBALL_PATH/*.env $CONFIG_DIR/
|
||||
if [[ $LOCAL_IP == "172.16.0.9" || $LOCAL_IP == "172.16.0.8" ]]; then
|
||||
echo "[双机环境]"
|
||||
echo -e "\nKAFKACLOUD_URL=172.16.0.8:9092,172.16.0.9:9092\nMYSQL_URL=172.16.0.4\nREDIS_URL=172.16.0.7" >> $CONFIG_DIR/variables.env
|
||||
else
|
||||
echo "[单机环境]"
|
||||
echo -e "\nKAFKACLOUD_URL=172.16.0.3:9092nMYSQL_URL=172.16.0.3\nREDIS_URL=172.16.0.3" >> $CONFIG_DIR/variables.env
|
||||
sed -i 's/172.16.0.3/'$LOCAL_IP'/g' $CONFIG_DIR/variables.env
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
function deploy() {
|
||||
info "[Deploying factory-digital-app ..............]" $YELLOW
|
||||
cd $WORK_DIR && docker-compose up -d
|
||||
|
||||
}
|
||||
|
||||
function post_handle() {
|
||||
info "[post handle factory-digital-app ..............]" $YELLOW
|
||||
}
|
||||
function uninstall() {
|
||||
docker-compose down
|
||||
rm -rf $WORK_DIR
|
||||
rm -rf $CONFIG_DIR
|
||||
}
|
||||
function main() {
|
||||
case $1 in
|
||||
prepare)
|
||||
prepare
|
||||
;;
|
||||
deploy)
|
||||
deploy
|
||||
;;
|
||||
postHandle)
|
||||
post_handle
|
||||
;;
|
||||
all)
|
||||
prepare
|
||||
deploy
|
||||
post_handle
|
||||
;;
|
||||
local)
|
||||
docker login -u cn-south-2@TQ0BQOAML1V5Y5HZ9ISP -p c6ec8441a31e8779899f060ee4a9146969b81f439ef5664cd2f5b3bd81cba962 swr.cn-south-2.myhuaweicloud.com
|
||||
export IMAGE_URL=swr.cn-south-2.myhuaweicloud.com/yelink_appstore_test/
|
||||
prepare
|
||||
deploy
|
||||
post_handle
|
||||
;;
|
||||
uninstall)
|
||||
uninstall
|
||||
;;
|
||||
*)
|
||||
fatal "Unsupported params"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main $@
|
||||
12
deploy/docker-compose.yml
Normal file
12
deploy/docker-compose.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
version: '3'
|
||||
services:
|
||||
factory-digital-app:
|
||||
image: ${IMAGE_URL}factory-digital-app-swput3:${VERSION:-latest}
|
||||
container_name: factory-digital-app-swput3
|
||||
ports:
|
||||
- "38104:38104"
|
||||
restart: always
|
||||
volumes:
|
||||
- "/opt/data/factory-digital-app-swput3/logs/:/usr/app/logs/"
|
||||
env_file:
|
||||
- /opt/fallensakura/deploy/variables.env
|
||||
3
deploy/scripts/upgrade.sh
Normal file
3
deploy/scripts/upgrade.sh
Normal file
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
cd /opt/yelink-demo && docker-compose up -d
|
||||
1
deploy/services-info.yml
Normal file
1
deploy/services-info.yml
Normal file
@@ -0,0 +1 @@
|
||||
service: [ factory-digital-app ]
|
||||
13
deploy/variables.env
Normal file
13
deploy/variables.env
Normal file
@@ -0,0 +1,13 @@
|
||||
TZ=Asia/Shanghai
|
||||
MINIO_PREFIX_NAME=example
|
||||
MYSQL_URL=172.23.255.62
|
||||
MYSQL_USERNAME=root
|
||||
MYSQL_PASSWORD=yelink123
|
||||
REDIS_URL=172.23.255.62
|
||||
AUTH_URL=http://172.23.255.62:8411/auth
|
||||
MINIO_ENDPOINT=http://172.23.255.62:28301
|
||||
NACOS_URL=172.23.255.62:8001
|
||||
ACCESS_KEY=DJPE3QJ07OXPAFFJGORO
|
||||
ACCESS_KEY_SECRET=sFjISWRruGQjJLCZYiEwomqEpBCNiE3HBaYixZFl
|
||||
EDGE_GATEWAY_URL=https://172.23.255.62:8301
|
||||
VERSION=1.0.1
|
||||
168
pom.xml
Normal file
168
pom.xml
Normal file
@@ -0,0 +1,168 @@
|
||||
<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/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.yelink.example</groupId>
|
||||
<artifactId>rainyhon-xzt</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<modules>
|
||||
<module>web-server</module>
|
||||
<module>public-common</module>
|
||||
</modules>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<spring.boot.version>2.3.12.RELEASE</spring.boot.version>
|
||||
<spring.cloud.version>Hoxton.SR1</spring.cloud.version>
|
||||
<junit.version>4.12</junit.version>
|
||||
<log4j.version>2.17.1</log4j.version>
|
||||
<lombok.version>1.18.26</lombok.version>
|
||||
<mysql.version>8.0.32</mysql.version>
|
||||
<spring.nacos.version>2.2.7.RELEASE</spring.nacos.version>
|
||||
<alibaba.fastjson>1.2.83</alibaba.fastjson>
|
||||
<apache.commons.lang3>3.9</apache.commons.lang3>
|
||||
<google.gson.version>2.8.5</google.gson.version>
|
||||
<keycloak-adapter-version>15.0.1</keycloak-adapter-version>
|
||||
<redisson-version>3.18.0</redisson-version>
|
||||
<flyway-version>5.2.4</flyway-version>
|
||||
<swagger-core-version>1.5.24</swagger-core-version>
|
||||
<io.springfox.swagger.version>2.9.2</io.springfox.swagger.version>
|
||||
<minio.verson>8.5.8</minio.verson>
|
||||
<okhttp.verson>4.12.0</okhttp.verson>
|
||||
<validation.api.version>2.0.1.Final</validation.api.version>
|
||||
<hutool.version>4.6.10</hutool.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>${spring.cloud.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>${mysql.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
<version>${spring.nacos.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
<version>${spring.nacos.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>${log4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${apache.commons.lang3}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>${alibaba.fastjson}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>${google.gson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak.bom</groupId>
|
||||
<artifactId>keycloak-adapter-bom</artifactId>
|
||||
<version>${keycloak-adapter-version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>${swagger-core-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger2</artifactId>
|
||||
<version>${io.springfox.swagger.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger-ui</artifactId>
|
||||
<version>${io.springfox.swagger.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- redisson 依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson-spring-boot-starter</artifactId>
|
||||
<version>${redisson-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
<version>${flyway-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio</artifactId>
|
||||
<version>${minio.verson}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>${okhttp.verson}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>${hutool.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>2.3.11.RELEASE</version>
|
||||
<configuration>
|
||||
<fork>true</fork>
|
||||
<addResources>true</addResources>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
18
public-common/pom.xml
Normal file
18
public-common/pom.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<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/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.yelink.example</groupId>
|
||||
<artifactId>rainyhon-xzt</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>public-common</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<modules>
|
||||
<module>yelink-redis-starter</module>
|
||||
<module>yelink-security-starter</module>
|
||||
<module>yelink-minio-starter</module>
|
||||
<module>yelink-doc-starter</module>
|
||||
</modules>
|
||||
</project>
|
||||
39
public-common/yelink-doc-starter/pom.xml
Normal file
39
public-common/yelink-doc-starter/pom.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<?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">
|
||||
<parent>
|
||||
<groupId>com.yelink.example</groupId>
|
||||
<artifactId>public-common</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>yelink-doc-starter</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger-ui</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.yelink.doc.configure;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.yelink.doc.properties.DocProperties;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.service.*;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spi.service.contexts.SecurityContext;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* DocAutoconfigure.
|
||||
* @author cwp
|
||||
*/
|
||||
@Configuration
|
||||
@EnableSwagger2
|
||||
@EnableConfigurationProperties(DocProperties.class)
|
||||
@ConditionalOnProperty(value = "doc.enable", havingValue = "true", matchIfMissing = true)
|
||||
public class DocAutoconfigure {
|
||||
|
||||
private final DocProperties properties;
|
||||
|
||||
public DocAutoconfigure(DocProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(-1)
|
||||
public Docket groupRestApi() {
|
||||
return new Docket(DocumentationType.SWAGGER_2)
|
||||
.apiInfo(groupApiInfo())
|
||||
.select()
|
||||
.apis(RequestHandlerSelectors.basePackage(properties.getBasePackage()))
|
||||
.paths(PathSelectors.any())
|
||||
.build().securityContexts(Lists.newArrayList(securityContext())).securitySchemes(Lists.<SecurityScheme>newArrayList(apiKey()));
|
||||
}
|
||||
|
||||
private ApiInfo groupApiInfo() {
|
||||
String description = String.format("<div style='font-size:%spx;color:%s;'>%s</div>",
|
||||
properties.getDescriptionFontSize(), properties.getDescriptionColor(), properties.getDescription());
|
||||
|
||||
Contact contact = new Contact(properties.getName(), properties.getUrl(), properties.getEmail());
|
||||
|
||||
return new ApiInfoBuilder()
|
||||
.title(properties.getTitle())
|
||||
.description(description)
|
||||
.termsOfServiceUrl(properties.getTermsOfServiceUrl())
|
||||
.contact(contact)
|
||||
.license(properties.getLicense())
|
||||
.licenseUrl(properties.getLicenseUrl())
|
||||
.version(properties.getVersion())
|
||||
.build();
|
||||
}
|
||||
|
||||
private ApiKey apiKey() {
|
||||
return new ApiKey("BearerToken", "Authorization", "header");
|
||||
}
|
||||
|
||||
private SecurityContext securityContext() {
|
||||
return SecurityContext.builder()
|
||||
.securityReferences(defaultAuth())
|
||||
.forPaths(PathSelectors.regex("/.*"))
|
||||
.build();
|
||||
}
|
||||
|
||||
List<SecurityReference> defaultAuth() {
|
||||
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
|
||||
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
|
||||
authorizationScopes[0] = authorizationScope;
|
||||
return Lists.newArrayList(new SecurityReference("BearerToken", authorizationScopes));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.yelink.doc.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* DocProperties.
|
||||
*
|
||||
* @author cwp
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "doc")
|
||||
@Data
|
||||
public class DocProperties {
|
||||
|
||||
/**
|
||||
* 是否开启doc功能.
|
||||
*/
|
||||
private Boolean enable = true;
|
||||
|
||||
/**
|
||||
* 接口扫描路径,如Controller路径.
|
||||
*/
|
||||
private String basePackage;
|
||||
|
||||
/**
|
||||
* 文档标题.
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 文档描述.
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 文档描述颜色.
|
||||
*/
|
||||
private String descriptionColor = "#42b983";
|
||||
|
||||
/**
|
||||
* 文档描述字体大小.
|
||||
*/
|
||||
private String descriptionFontSize = "14";
|
||||
|
||||
/**
|
||||
* 服务url.
|
||||
*/
|
||||
private String termsOfServiceUrl;
|
||||
|
||||
/**
|
||||
* 联系方式:姓名.
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 联系方式:个人网站url.
|
||||
*/
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* 联系方式:邮箱.
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 协议.
|
||||
*/
|
||||
private String license;
|
||||
|
||||
/**
|
||||
* 协议地址.
|
||||
*/
|
||||
private String licenseUrl;
|
||||
|
||||
/**
|
||||
* 版本.
|
||||
*/
|
||||
private String version;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Auto Configure
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.yelink.doc.configure.DocAutoconfigure
|
||||
45
public-common/yelink-minio-starter/pom.xml
Normal file
45
public-common/yelink-minio-starter/pom.xml
Normal file
@@ -0,0 +1,45 @@
|
||||
<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/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.yelink.example</groupId>
|
||||
<artifactId>public-common</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>yelink-minio-starter</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.yelink.minio.configure;
|
||||
|
||||
import com.yelink.minio.properties.MinioConfig;
|
||||
import com.yelink.minio.service.MinioService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* MinioAutoconfigure.
|
||||
*
|
||||
* @author cwp
|
||||
* @date 2024-05-16 17:45
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties({MinioConfig.class})
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class MinioAutoconfigure {
|
||||
|
||||
@Bean
|
||||
public MinioService minioService(MinioConfig minioConfig) {
|
||||
return new MinioService(minioConfig);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.yelink.minio.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* MinioConfig.
|
||||
* @author cwp
|
||||
* @date 2024-05-16 17:19
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "minio")
|
||||
@Data
|
||||
public class MinioConfig {
|
||||
|
||||
private String endpoint;
|
||||
|
||||
private String accessKey;
|
||||
|
||||
private String secretKey;
|
||||
|
||||
private String bucket;
|
||||
|
||||
private String applicationName;
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
package com.yelink.minio.service;
|
||||
|
||||
import com.yelink.minio.properties.MinioConfig;
|
||||
import io.minio.CopyObjectArgs;
|
||||
import io.minio.CopySource;
|
||||
import io.minio.GetObjectArgs;
|
||||
import io.minio.MinioClient;
|
||||
import io.minio.PutObjectArgs;
|
||||
import io.minio.RemoveObjectArgs;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* MinioService.
|
||||
*
|
||||
* @author cwp
|
||||
* @date 2024-05-16 17:33
|
||||
*/
|
||||
@Slf4j
|
||||
public class MinioService {
|
||||
|
||||
private static final Map<String, String> MIME_TYPES = new HashMap<>();
|
||||
|
||||
static {
|
||||
MIME_TYPES.put("txt", "text/plain");
|
||||
MIME_TYPES.put("html", "text/html");
|
||||
MIME_TYPES.put("css", "text/css");
|
||||
MIME_TYPES.put("js", "application/javascript");
|
||||
MIME_TYPES.put("json", "application/json");
|
||||
MIME_TYPES.put("xml", "application/xml");
|
||||
MIME_TYPES.put("jpg", "image/jpeg");
|
||||
MIME_TYPES.put("jpeg", "image/jpeg");
|
||||
MIME_TYPES.put("png", "image/png");
|
||||
MIME_TYPES.put("gif", "image/gif");
|
||||
MIME_TYPES.put("bmp", "image/bmp");
|
||||
MIME_TYPES.put("pdf", "application/pdf");
|
||||
// 添加更多扩展名和 Content-Type 对应关系
|
||||
}
|
||||
|
||||
private final MinioConfig config;
|
||||
|
||||
private final SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmssSSS");
|
||||
|
||||
private MinioClient client;
|
||||
|
||||
public MinioService(MinioConfig config) {
|
||||
this.config = config;
|
||||
try {
|
||||
client = MinioClient.builder().endpoint(config.getEndpoint())
|
||||
.credentials(config.getAccessKey(), config.getSecretKey()).build();
|
||||
} catch (Exception ex) {
|
||||
log.error("Minio 初始化失败!Trace:", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文件扩展名获取 Content-Type 不使用MultipartFile.getContentType(),是因为它基于客户端传输的content_type,不一定准确.
|
||||
*
|
||||
* @param fileName 文件名,包含后缀
|
||||
* @return MIME 类型
|
||||
*/
|
||||
private String getContentType(String fileName) {
|
||||
String extension = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
|
||||
return MIME_TYPES.getOrDefault(extension, "application/octet-stream");
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件上传.
|
||||
*
|
||||
* @param file 文件
|
||||
* @param path minio 远程路径
|
||||
* @return 公网访问 url
|
||||
*/
|
||||
public String upload(MultipartFile file, String path) throws Exception {
|
||||
String contentType;
|
||||
if (ObjectUtils.isEmpty(file.getOriginalFilename())) {
|
||||
contentType = file.getContentType();
|
||||
} else {
|
||||
contentType = getContentType(file.getOriginalFilename());
|
||||
}
|
||||
return upload(file, path, contentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件上传.
|
||||
*
|
||||
* @param file 文件
|
||||
* @param path minio 远程路径
|
||||
* @param contentType 文件类型
|
||||
* @return 公网访问 url
|
||||
*/
|
||||
public String upload(MultipartFile file, String path, String contentType) throws Exception {
|
||||
String filename = genPath(path, getExtension(file));
|
||||
return upload(file.getInputStream(), filename, contentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件上传.
|
||||
*
|
||||
* @param content 文件内容
|
||||
* @param fileExtension 后缀名
|
||||
* @return 内网访问 url
|
||||
*/
|
||||
public String upload(String content, String fileExtension) throws Exception {
|
||||
// 转换字符串为输入流
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8));
|
||||
// 构建对象名称
|
||||
String objectName = config.getApplicationName() + "/" + format.format(new Date()) + "." + fileExtension;
|
||||
return upload(inputStream, objectName, "text/plain");
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件上传.
|
||||
*
|
||||
* @param inputStream inputStream
|
||||
* @param objectName 文件名
|
||||
* @param contentType 文件类型
|
||||
* @return 内网访问 url
|
||||
* @throws Exception e
|
||||
*/
|
||||
public String upload(InputStream inputStream, String objectName, String contentType) throws Exception {
|
||||
try {
|
||||
PutObjectArgs args = PutObjectArgs.builder().contentType(contentType).object(objectName)
|
||||
.bucket(config.getBucket()).stream(inputStream, inputStream.available(), -1).build();
|
||||
client.putObject(args);
|
||||
} finally {
|
||||
inputStream.close();
|
||||
}
|
||||
return config.getBucket() + "/" + objectName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据完整 URL 来移除 minio 服务器中的对象.
|
||||
*
|
||||
* @param url 例:<a href="http://127.0.0.1:9000/furniture/materials/20230315/7375e567-d771-46a7-bccf-bb48886ab3d2.png">...</a>
|
||||
*/
|
||||
public void removeObjectByUrl(String url) {
|
||||
removeObject(extractObjectName(url));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据对象名移除 minio 服务器中的对象.
|
||||
*
|
||||
* @param name 例:materials/20230315/7375e567-d771-46a7-bccf-bb48886ab3d2.png
|
||||
*/
|
||||
public void removeObject(String name) {
|
||||
try {
|
||||
RemoveObjectArgs args = RemoveObjectArgs.builder().bucket(config.getBucket()).object(name).build();
|
||||
client.removeObject(args);
|
||||
} catch (Exception e) {
|
||||
log.error("MinIO 文件删除错误", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象文件公网访问 url.
|
||||
*
|
||||
* @param filename 对象名称
|
||||
* @return 公网访问 url
|
||||
*/
|
||||
public String minioPublicUrl(String filename) {
|
||||
return config.getBucket() + "/" + filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成对象路径.
|
||||
*
|
||||
* @param basicPath 根路径
|
||||
* @param extension 文件扩展名
|
||||
* @return basicPath/yyyyMMdd/uuid.extension
|
||||
*/
|
||||
public String genPath(String basicPath, String extension) {
|
||||
return "iot/" + basicPath + extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件的扩展名.
|
||||
*
|
||||
* @param file 文件
|
||||
* @return 例:.jpg
|
||||
*/
|
||||
public String getExtension(MultipartFile file) {
|
||||
return getExtension(Objects.requireNonNull(file.getOriginalFilename()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件的扩展名.
|
||||
*
|
||||
* @param name 文件名称
|
||||
* @return 例:.jpg
|
||||
*/
|
||||
public String getExtension(String name) {
|
||||
return name.substring(name.lastIndexOf('.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件,用于受保护的 minio 服务器 此方法未测试.
|
||||
*/
|
||||
public String download(String object) {
|
||||
GetObjectArgs args = GetObjectArgs.builder().bucket(config.getBucket()).object(object).build();
|
||||
try {
|
||||
// 读取对象内容
|
||||
try (InputStream inputStream = client.getObject(args)) {
|
||||
// Read the input stream and print to the console till EOF.
|
||||
byte[] buf = new byte[16384];
|
||||
int bytesRead;
|
||||
StringBuilder content = new StringBuilder();
|
||||
while ((bytesRead = inputStream.read(buf, 0, buf.length)) >= 0) {
|
||||
String readContent = new String(buf, 0, bytesRead, StandardCharsets.UTF_8);
|
||||
content.append(readContent);
|
||||
}
|
||||
return content.toString();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
log.error("MinIO 文件下载错误:", ex);
|
||||
throw new RuntimeException(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据url下载文件 此方法未测试.
|
||||
*/
|
||||
public String downloadByUrl(String url) {
|
||||
return download(extractObjectName(url));
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取 url 中的 minio 对象路径.
|
||||
*
|
||||
* @param url 例:<a href="http://127.0.0.1:9000/furniture/materials/20230315/7375e567-d771-46a7-bccf-bb48886ab3d2.png">...</a>
|
||||
* @return 例:/materials/20230315/7375e567-d771-46a7-bccf-bb48886ab3d2.png
|
||||
*/
|
||||
public String extractObjectName(String url) {
|
||||
return url.substring(minioPublicUrl("").length());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将一个对象拷贝到指定路径.
|
||||
*
|
||||
* @param objectName minio 中需要拷贝的对象
|
||||
* @param path minio 要拷贝到的路径
|
||||
* @return 拷贝后路径的公网访问 url
|
||||
*/
|
||||
public String copy(String objectName, String path) {
|
||||
String extension = getExtension(objectName);
|
||||
String targetObject = genPath(path, extension);
|
||||
CopyObjectArgs args = CopyObjectArgs.builder().bucket(config.getBucket()).object(targetObject)
|
||||
.source(CopySource.builder().bucket(config.getBucket()).object(objectName).build()).build();
|
||||
try {
|
||||
client.copyObject(args);
|
||||
} catch (Exception ex) {
|
||||
log.error("minIO 文件拷贝失败:", ex);
|
||||
throw new RuntimeException(ex.getMessage());
|
||||
}
|
||||
return minioPublicUrl(targetObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 url 拷贝.
|
||||
*
|
||||
* @see MinioService#copy(String, String)
|
||||
*/
|
||||
public String copyByUrl(String url, String path) {
|
||||
return copy(extractObjectName(url), path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# Auto Configure
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.yelink.minio.configure.MinioAutoconfigure
|
||||
59
public-common/yelink-redis-starter/pom.xml
Normal file
59
public-common/yelink-redis-starter/pom.xml
Normal file
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.yelink.example</groupId>
|
||||
<artifactId>public-common</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>yelink-redis-starter</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.lettuce</groupId>
|
||||
<artifactId>lettuce-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.yelink.common.redis.annotation;
|
||||
|
||||
import com.yelink.common.redis.configure.RedisAutoConfigure;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
/**
|
||||
* EnableYtLettuceRedis.
|
||||
* @author cwp
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Import(RedisAutoConfigure.class)
|
||||
public @interface EnableYtLettuceRedis {
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.yelink.common.redis.configure;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import com.yelink.common.redis.service.RedisService;
|
||||
import io.micrometer.core.instrument.util.StringUtils;
|
||||
import org.redisson.Redisson;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.redisson.config.Config;
|
||||
import org.redisson.config.SingleServerConfig;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
|
||||
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.RedisOperations;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
/**
|
||||
* Lettuce Redis配置.
|
||||
*
|
||||
* @author cwp
|
||||
*/
|
||||
@Configuration
|
||||
public class RedisAutoConfigure {
|
||||
|
||||
@Bean(name = "redisTemplate")
|
||||
@ConditionalOnClass(RedisOperations.class)
|
||||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
|
||||
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||
template.setConnectionFactory(factory);
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||
mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
|
||||
mapper.registerModule(new JavaTimeModule());
|
||||
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
|
||||
jackson2JsonRedisSerializer.setObjectMapper(mapper);
|
||||
|
||||
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
|
||||
template.setKeySerializer(stringRedisSerializer);
|
||||
template.setHashKeySerializer(stringRedisSerializer);
|
||||
template.setValueSerializer(jackson2JsonRedisSerializer);
|
||||
template.setHashValueSerializer(jackson2JsonRedisSerializer);
|
||||
template.afterPropertiesSet();
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(name = "redisTemplate")
|
||||
public RedisService redisService(RedisTemplate<String, Object> redisTemplate) {
|
||||
return new RedisService(redisTemplate);
|
||||
}
|
||||
|
||||
@Bean(destroyMethod = "shutdown")
|
||||
public RedissonClient redissonSingle(RedisProperties redisProperties) {
|
||||
Config config = new Config();
|
||||
SingleServerConfig singleServerConfig = config.useSingleServer()
|
||||
.setAddress("redis://" + redisProperties.getHost() + ":" + redisProperties.getPort())
|
||||
.setDatabase(redisProperties.getDatabase())
|
||||
.setConnectionMinimumIdleSize(10)
|
||||
.setConnectionPoolSize(10);
|
||||
if (StringUtils.isNotEmpty(redisProperties.getPassword())) {
|
||||
singleServerConfig.setPassword(redisProperties.getPassword());
|
||||
}
|
||||
return Redisson.create(config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,561 @@
|
||||
package com.yelink.common.redis.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 定义常用的 Redis操作.
|
||||
*
|
||||
* @author cwp
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class RedisService {
|
||||
|
||||
private final RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
/**
|
||||
* 指定缓存失效时间.
|
||||
*
|
||||
* @param key 键
|
||||
* @param time 时间(秒)
|
||||
* @return Boolean
|
||||
*/
|
||||
public Boolean expire(String key, Long time) {
|
||||
try {
|
||||
if (time > 0) {
|
||||
redisTemplate.expire(key, time, TimeUnit.SECONDS);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key获取过期时间.
|
||||
*
|
||||
* @param key 键 不能为 null
|
||||
* @return 时间(秒) 返回 0代表为永久有效
|
||||
*/
|
||||
public Long getExpire(String key) {
|
||||
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断 key是否存在.
|
||||
*
|
||||
* @param key 键
|
||||
* @return true 存在 false不存在
|
||||
*/
|
||||
public Boolean hasKey(String key) {
|
||||
try {
|
||||
return redisTemplate.hasKey(key);
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存.
|
||||
*
|
||||
* @param key 可以传一个值 或多个
|
||||
*/
|
||||
public void del(String... key) {
|
||||
if (key != null && key.length > 0) {
|
||||
if (key.length == 1) {
|
||||
redisTemplate.delete(key[0]);
|
||||
} else {
|
||||
redisTemplate.delete(Arrays.asList(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 普通缓存获取.
|
||||
*
|
||||
* @param key 键
|
||||
* @return 值
|
||||
*/
|
||||
public Object get(String key) {
|
||||
return key == null ? null : redisTemplate.opsForValue().get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 普通缓存放入.
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return true成功 false失败
|
||||
*/
|
||||
public Boolean set(String key, Object value) {
|
||||
try {
|
||||
redisTemplate.opsForValue().set(key, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 普通缓存放入并设置时间.
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
|
||||
* @return true成功 false 失败
|
||||
*/
|
||||
public Boolean set(String key, Object value, Long time) {
|
||||
try {
|
||||
if (time > 0) {
|
||||
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
|
||||
} else {
|
||||
set(key, value);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递增.
|
||||
*
|
||||
* @param key 键
|
||||
* @param delta 要增加几(大于0)
|
||||
* @return Long
|
||||
*/
|
||||
public Long incr(String key, Long delta) {
|
||||
if (delta < 0) {
|
||||
throw new RuntimeException("递增因子必须大于0");
|
||||
}
|
||||
return redisTemplate.opsForValue().increment(key, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* 递减.
|
||||
*
|
||||
* @param key 键
|
||||
* @param delta 要减少几
|
||||
* @return Long
|
||||
*/
|
||||
public Long decr(String key, Long delta) {
|
||||
if (delta < 0) {
|
||||
throw new RuntimeException("递减因子必须大于0");
|
||||
}
|
||||
return redisTemplate.opsForValue().increment(key, -delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* HashGet.
|
||||
*
|
||||
* @param key 键 不能为 null
|
||||
* @param item 项 不能为 null
|
||||
* @return 值
|
||||
*/
|
||||
public Object hget(String key, String item) {
|
||||
return redisTemplate.opsForHash().get(key, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 hashKey对应的所有键值.
|
||||
*
|
||||
* @param key 键
|
||||
* @return 对应的多个键值
|
||||
*/
|
||||
public Map<Object, Object> hmget(String key) {
|
||||
return redisTemplate.opsForHash().entries(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* HashSet.
|
||||
*
|
||||
* @param key 键
|
||||
* @param map 对应多个键值
|
||||
* @return true 成功 false 失败
|
||||
*/
|
||||
public Boolean hmset(String key, Map<String, Object> map) {
|
||||
try {
|
||||
redisTemplate.opsForHash().putAll(key, map);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* HashSet 并设置时间.
|
||||
*
|
||||
* @param key 键
|
||||
* @param map 对应多个键值
|
||||
* @param time 时间(秒)
|
||||
* @return true成功 false失败
|
||||
*/
|
||||
public Boolean hmset(String key, Map<String, Object> map, Long time) {
|
||||
try {
|
||||
redisTemplate.opsForHash().putAll(key, map);
|
||||
if (time > 0) {
|
||||
expire(key, time);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向一张hash表中放入数据,如果不存在将创建.
|
||||
*
|
||||
* @param key 键
|
||||
* @param item 项
|
||||
* @param value 值
|
||||
* @return true 成功 false失败
|
||||
*/
|
||||
public Boolean hset(String key, String item, Object value) {
|
||||
try {
|
||||
redisTemplate.opsForHash().put(key, item, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向一张hash表中放入数据,如果不存在将创建.
|
||||
*
|
||||
* @param key 键
|
||||
* @param item 项
|
||||
* @param value 值
|
||||
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
|
||||
* @return true 成功 false失败
|
||||
*/
|
||||
public Boolean hset(String key, String item, Object value, Long time) {
|
||||
try {
|
||||
redisTemplate.opsForHash().put(key, item, value);
|
||||
if (time > 0) {
|
||||
expire(key, time);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除hash表中的值.
|
||||
*
|
||||
* @param key 键 不能为 null
|
||||
* @param item 项 可以使多个不能为 null
|
||||
*/
|
||||
public void hdel(String key, Object... item) {
|
||||
redisTemplate.opsForHash().delete(key, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断hash表中是否有该项的值.
|
||||
*
|
||||
* @param key 键 不能为 null
|
||||
* @param item 项 不能为 null
|
||||
* @return true 存在 false不存在
|
||||
*/
|
||||
public Boolean hHasKey(String key, String item) {
|
||||
return redisTemplate.opsForHash().hasKey(key, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* hash递增 如果不存在,就会创建一个 并把新增后的值返回.
|
||||
*
|
||||
* @param key 键
|
||||
* @param item 项
|
||||
* @param by 要增加几(大于0)
|
||||
* @return Double
|
||||
*/
|
||||
public Double hincr(String key, String item, Double by) {
|
||||
return redisTemplate.opsForHash().increment(key, item, by);
|
||||
}
|
||||
|
||||
/**
|
||||
* hash递减.
|
||||
*
|
||||
* @param key 键
|
||||
* @param item 项
|
||||
* @param by 要减少记(小于0)
|
||||
* @return Double
|
||||
*/
|
||||
public Double hdecr(String key, String item, Double by) {
|
||||
return redisTemplate.opsForHash().increment(key, item, -by);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 key获取 Set中的所有值.
|
||||
*
|
||||
* @param key 键
|
||||
* @return Set
|
||||
*/
|
||||
public Set<Object> sGet(String key) {
|
||||
try {
|
||||
return redisTemplate.opsForSet().members(key);
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据value从一个set中查询,是否存在.
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return true 存在 false不存在
|
||||
*/
|
||||
public Boolean sHasKey(String key, Object value) {
|
||||
try {
|
||||
return redisTemplate.opsForSet().isMember(key, value);
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数据放入set缓存.
|
||||
*
|
||||
* @param key 键
|
||||
* @param values 值 可以是多个
|
||||
* @return 成功个数
|
||||
*/
|
||||
public Long sSet(String key, Object... values) {
|
||||
try {
|
||||
return redisTemplate.opsForSet().add(key, values);
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将set数据放入缓存..
|
||||
*
|
||||
* @param key 键
|
||||
* @param time 时间(秒)
|
||||
* @param values 值 可以是多个
|
||||
* @return 成功个数
|
||||
*/
|
||||
public Long sSetAndTime(String key, Long time, Object... values) {
|
||||
try {
|
||||
Long count = redisTemplate.opsForSet().add(key, values);
|
||||
if (time > 0) {
|
||||
expire(key, time);
|
||||
}
|
||||
return count;
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取set缓存的长度.
|
||||
*
|
||||
* @param key 键
|
||||
* @return Long
|
||||
*/
|
||||
public Long sGetSetSize(String key) {
|
||||
try {
|
||||
return redisTemplate.opsForSet().size(key);
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除值为value的.
|
||||
*
|
||||
* @param key 键
|
||||
* @param values 值 可以是多个
|
||||
* @return 移除的个数
|
||||
*/
|
||||
public Long setRemove(String key, Object... values) {
|
||||
try {
|
||||
return redisTemplate.opsForSet().remove(key, values);
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取list缓存的内容.
|
||||
*
|
||||
* @param key 键
|
||||
* @param start 开始
|
||||
* @param end 结束 0 到 -1代表所有值
|
||||
* @return List
|
||||
*/
|
||||
public List<Object> lGet(String key, Long start, Long end) {
|
||||
try {
|
||||
return redisTemplate.opsForList().range(key, start, end);
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取list缓存的长度.
|
||||
*
|
||||
* @param key 键
|
||||
* @return Long
|
||||
*/
|
||||
public Long lGetListSize(String key) {
|
||||
try {
|
||||
return redisTemplate.opsForList().size(key);
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过索引 获取list中的值.
|
||||
*
|
||||
* @param key 键
|
||||
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推; index<0时,-1,表尾,-2倒数第二个元素,依次类推
|
||||
* @return Object
|
||||
*/
|
||||
public Object lGetIndex(String key, Long index) {
|
||||
try {
|
||||
return redisTemplate.opsForList().index(key, index);
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将list放入缓存.
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return Boolean
|
||||
*/
|
||||
public Boolean lSet(String key, Object value) {
|
||||
try {
|
||||
redisTemplate.opsForList().rightPush(key, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将list放入缓存.
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param time 时间(秒)
|
||||
* @return Boolean
|
||||
*/
|
||||
public Boolean lSet(String key, Object value, Long time) {
|
||||
try {
|
||||
redisTemplate.opsForList().rightPush(key, value);
|
||||
if (time > 0) {
|
||||
expire(key, time);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将list放入缓存.
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return Boolean
|
||||
*/
|
||||
public Boolean lSet(String key, List<Object> value) {
|
||||
try {
|
||||
redisTemplate.opsForList().rightPushAll(key, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将list放入缓存.
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param time 时间(秒)
|
||||
* @return Boolean
|
||||
*/
|
||||
public Boolean lSet(String key, List<Object> value, Long time) {
|
||||
try {
|
||||
redisTemplate.opsForList().rightPushAll(key, value);
|
||||
if (time > 0) {
|
||||
expire(key, time);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据索引修改list中的某条数据.
|
||||
*
|
||||
* @param key 键
|
||||
* @param index 索引
|
||||
* @param value 值
|
||||
* @return Boolean
|
||||
*/
|
||||
public Boolean lUpdateIndex(String key, Long index, Object value) {
|
||||
try {
|
||||
redisTemplate.opsForList().set(key, index, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除N个值为value.
|
||||
*
|
||||
* @param key 键
|
||||
* @param count 移除多少个
|
||||
* @param value 值
|
||||
* @return 移除的个数
|
||||
*/
|
||||
public Long lRemove(String key, Long count, Object value) {
|
||||
try {
|
||||
return redisTemplate.opsForList().remove(key, count, value);
|
||||
} catch (Exception e) {
|
||||
log.error("error", e);
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Auto Configure
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.yelink.common.redis.configure.RedisAutoConfigure
|
||||
63
public-common/yelink-security-starter/pom.xml
Normal file
63
public-common/yelink-security-starter/pom.xml
Normal file
@@ -0,0 +1,63 @@
|
||||
<?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">
|
||||
<parent>
|
||||
<groupId>com.yelink.example</groupId>
|
||||
<artifactId>public-common</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>yelink-security-starter</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-oauth2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-spring-security-adapter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.yelink.security.annotation;
|
||||
|
||||
import com.yelink.security.configure.AppResourceServerConfigure;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
/**
|
||||
* 启用该模块,需要再启动类增加@AppEnableResourceServer.
|
||||
* @author cwp
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Import(AppResourceServerConfigure.class)
|
||||
public @interface AppEnableResourceServer {
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.yelink.security.configure;
|
||||
|
||||
import com.yelink.security.expression.WebSecurityExpressionHandler;
|
||||
import com.yelink.security.handler.AccessDeniedHandler;
|
||||
import com.yelink.security.handler.AuthExceptionEntryPoint;
|
||||
import com.yelink.security.properties.EdgeSecurityProperties;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.keycloak.adapters.KeycloakConfigResolver;
|
||||
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
|
||||
import org.keycloak.adapters.springsecurity.KeycloakConfiguration;
|
||||
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
|
||||
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
|
||||
import org.springframework.security.core.session.SessionRegistry;
|
||||
import org.springframework.security.core.session.SessionRegistryImpl;
|
||||
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
|
||||
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
*/
|
||||
@KeycloakConfiguration
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
|
||||
@EnableConfigurationProperties({EdgeSecurityProperties.class})
|
||||
@EnableAutoConfiguration(exclude = UserDetailsServiceAutoConfiguration.class)
|
||||
public class AppResourceServerConfigure extends KeycloakWebSecurityConfigurerAdapter {
|
||||
|
||||
@Autowired
|
||||
private EdgeSecurityProperties properties;
|
||||
|
||||
|
||||
@Autowired
|
||||
public void configureGlobal(AuthenticationManagerBuilder auth) {
|
||||
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
|
||||
SimpleAuthorityMapper simpleAuthorityMapper = new SimpleAuthorityMapper();
|
||||
simpleAuthorityMapper.setPrefix("");
|
||||
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(simpleAuthorityMapper);
|
||||
auth.authenticationProvider(keycloakAuthenticationProvider);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
|
||||
return new RegisterSessionAuthenticationStrategy(buildSessionRegistry());
|
||||
}
|
||||
|
||||
@Bean
|
||||
protected SessionRegistry buildSessionRegistry() {
|
||||
return new SessionRegistryImpl();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public KeycloakConfigResolver KeycloakConfigResolver() {
|
||||
return new KeycloakSpringBootConfigResolver();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
super.configure(http);
|
||||
|
||||
// 若enable为false,则所有请求都允许访问
|
||||
if (!properties.getEnable()) {
|
||||
http.cors().and()
|
||||
.csrf().disable()
|
||||
.authorizeRequests()
|
||||
.anyRequest().permitAll();
|
||||
return;
|
||||
}
|
||||
|
||||
String[] anonUrls = StringUtils.splitByWholeSeparatorPreserveAllTokens(properties.getAnonUris(), ",");
|
||||
if (ArrayUtils.isEmpty(anonUrls)) {
|
||||
anonUrls = new String[]{};
|
||||
}
|
||||
http
|
||||
.cors().and()
|
||||
.csrf().disable()
|
||||
.antMatcher("/**").authorizeRequests()
|
||||
.antMatchers(anonUrls).permitAll()
|
||||
.antMatchers("/**").access("hasAkAuth()")
|
||||
.anyRequest().authenticated();
|
||||
|
||||
http.authorizeRequests().expressionHandler(new WebSecurityExpressionHandler());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "accessDeniedHandler")
|
||||
public AccessDeniedHandler accessDeniedHandler() {
|
||||
return new AccessDeniedHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "authenticationEntryPoint")
|
||||
public AuthExceptionEntryPoint authenticationEntryPoint() {
|
||||
return new AuthExceptionEntryPoint();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.yelink.security.expression;
|
||||
|
||||
import org.springframework.security.access.expression.SecurityExpressionOperations;
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
|
||||
|
||||
public class WebSecurityExpressionHandler extends DefaultWebSecurityExpressionHandler {
|
||||
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
|
||||
private String defaultRolePrefix = "ROLE_";
|
||||
|
||||
@Override
|
||||
protected SecurityExpressionOperations createSecurityExpressionRoot(
|
||||
Authentication authentication, FilterInvocation fi) {
|
||||
WebSecurityHeaderExpression root = new WebSecurityHeaderExpression(authentication, fi);
|
||||
root.setPermissionEvaluator(getPermissionEvaluator());
|
||||
root.setTrustResolver(trustResolver);
|
||||
root.setRoleHierarchy(getRoleHierarchy());
|
||||
root.setDefaultRolePrefix(this.defaultRolePrefix);
|
||||
return root;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.yelink.security.expression;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
import org.springframework.security.web.access.expression.WebSecurityExpressionRoot;
|
||||
import org.springframework.util.Base64Utils;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
*/
|
||||
public class WebSecurityHeaderExpression extends WebSecurityExpressionRoot {
|
||||
private Authentication a;
|
||||
|
||||
private static final String HEADER_ACCESS_KEY_ID = "AccessKeyId";
|
||||
private static final String HEADER_GATEWAY_TOKEN = "GatewayToken";
|
||||
private static final String GATEWAY_TOKEN_VALUE = "yt:gateway:123456";
|
||||
|
||||
public WebSecurityHeaderExpression(Authentication a, FilterInvocation fi) {
|
||||
super(a, fi);
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public boolean hasAkAuth() {
|
||||
boolean hasAk = request.getHeader(HEADER_ACCESS_KEY_ID) != null ||
|
||||
null != request.getHeader(HEADER_ACCESS_KEY_ID.toLowerCase());
|
||||
|
||||
boolean hasAccessGateway = false;
|
||||
String headerGatewayToken = request.getHeader(HEADER_GATEWAY_TOKEN);
|
||||
if (StringUtils.isNotEmpty(headerGatewayToken)) {
|
||||
String gatewayToken = new String(Base64Utils.encode(GATEWAY_TOKEN_VALUE.getBytes()));
|
||||
hasAccessGateway = StringUtils.equals(gatewayToken, headerGatewayToken);
|
||||
}
|
||||
|
||||
return hasAuth() || (hasAk && hasAccessGateway);
|
||||
}
|
||||
|
||||
private boolean hasAuth() {
|
||||
return !"anonymousUser".equals(a.getPrincipal()) && a.isAuthenticated();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.yelink.security.handler;
|
||||
|
||||
|
||||
import com.yelink.security.utils.ResponseUtil;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
*/
|
||||
public class AccessDeniedHandler implements org.springframework.security.web.access.AccessDeniedHandler {
|
||||
|
||||
@Override
|
||||
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
|
||||
|
||||
Map<String, Object> error = new HashMap<>();
|
||||
error.put("code", 10407);
|
||||
error.put("message", "没有权限访问该资源");
|
||||
|
||||
ResponseUtil.makeResponse(response, MediaType.APPLICATION_JSON_VALUE,
|
||||
HttpServletResponse.SC_FORBIDDEN, error);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.yelink.security.handler;
|
||||
|
||||
|
||||
import com.yelink.security.utils.ResponseUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
*/
|
||||
@Slf4j
|
||||
public class AuthExceptionEntryPoint implements AuthenticationEntryPoint {
|
||||
|
||||
@Override
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException authException) throws IOException {
|
||||
int status = HttpServletResponse.SC_UNAUTHORIZED;
|
||||
Map<String, Object> error = new HashMap<>();
|
||||
error.put("code", 10007);
|
||||
error.put("message", "无效的token");
|
||||
ResponseUtil.makeResponse(response, MediaType.APPLICATION_JSON_VALUE, status, error);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.yelink.security.properties;
|
||||
|
||||
import com.yelink.security.utils.EndpointConstant;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "app.security")
|
||||
@Data
|
||||
public class EdgeSecurityProperties {
|
||||
|
||||
/**
|
||||
* 是否开启安全配置
|
||||
*/
|
||||
private Boolean enable = true;
|
||||
/**
|
||||
* 配置需要认证的uri,默认为所有/**
|
||||
*/
|
||||
private String authUri = EndpointConstant.ALL;
|
||||
/**
|
||||
* 免认证资源路径,支持通配符
|
||||
* 多个值时使用逗号分隔
|
||||
*/
|
||||
private String anonUris;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.yelink.security.utils;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
*/
|
||||
public class EndpointConstant {
|
||||
public static final String ALL = "/**";
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.yelink.security.utils;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @date 2024-08-21 17:59
|
||||
*/
|
||||
public class ResponseUtil {
|
||||
/**
|
||||
* 设置响应
|
||||
*
|
||||
* @param response HttpServletResponse
|
||||
* @param contentType content-type
|
||||
* @param status http状态码
|
||||
* @param value 响应内容
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
public static void makeResponse(HttpServletResponse response, String contentType,
|
||||
int status, Object value) throws IOException {
|
||||
response.setContentType(contentType);
|
||||
response.setStatus(status);
|
||||
response.getOutputStream().write(JSON.toJSONString(value).getBytes());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Auto Configure
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.yelink.security.configure.AppResourceServerConfigure
|
||||
220
style/CheckStyle.xml
Normal file
220
style/CheckStyle.xml
Normal file
@@ -0,0 +1,220 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
~ Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<!DOCTYPE module PUBLIC "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
|
||||
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||
|
||||
<module name="Checker">
|
||||
<property name="charset" value="UTF-8"/>
|
||||
<property name="severity" value="error"/>
|
||||
<property name="fileExtensions" value="java, properties, xml"/>
|
||||
|
||||
<module name="FileTabCharacter">
|
||||
<property name="eachLine" value="true"/>
|
||||
</module>
|
||||
<module name="LineLength">
|
||||
<property name="fileExtensions" value="java"/>
|
||||
<property name="max" value="150"/>
|
||||
<property name="ignorePattern"
|
||||
value="^implements.*|^extends.*|^package.*|^import.*|a href|href|http://|https://|ftp://"/>
|
||||
</module>
|
||||
<module name="SuppressWarningsFilter"/>
|
||||
|
||||
<module name="TreeWalker">
|
||||
<module name="SuppressionCommentFilter"/>
|
||||
<module name="SuppressWarningsHolder" />
|
||||
|
||||
<!-- Name Checker -->
|
||||
<module name="OuterTypeFilename"/>
|
||||
<module name="PackageName">
|
||||
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Package name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="TypeName"/>
|
||||
<module name="MemberName"/>
|
||||
<module name="ParameterName"/>
|
||||
<module name="LambdaParameterName"/>
|
||||
<module name="CatchParameterName"/>
|
||||
<module name="LocalVariableName"/>
|
||||
<module name="ClassTypeParameterName"/>
|
||||
<module name="MethodTypeParameterName"/>
|
||||
<module name="InterfaceTypeParameterName"/>
|
||||
<module name="MethodName"/>
|
||||
<module name="ConstantName"/>
|
||||
<module name="StaticVariableName"/>
|
||||
<module name="AbbreviationAsWordInName">
|
||||
<property name="ignoreFinal" value="false"/>
|
||||
<property name="allowedAbbreviationLength" value="1"/>
|
||||
<property name="allowedAbbreviations" value="VO"/>
|
||||
</module>
|
||||
|
||||
<!-- Import Checker -->
|
||||
<module name="AvoidStarImport"/>
|
||||
<module name="UnusedImports"/>
|
||||
<module name="RedundantImport"/>
|
||||
|
||||
<!-- Block Checker -->
|
||||
<module name="EmptyBlock">
|
||||
<property name="option" value="TEXT"/>
|
||||
<property name="tokens"
|
||||
value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
|
||||
</module>
|
||||
<module name="EmptyCatchBlock">
|
||||
<property name="exceptionVariableName" value="expected|ignore(d)?"/>
|
||||
</module>
|
||||
<module name="LeftCurly"/>
|
||||
<module name="RightCurly"/>
|
||||
<module name="NeedBraces"/>
|
||||
|
||||
<!-- Javadoc Checker -->
|
||||
<module name="JavadocMethod">
|
||||
<property name="accessModifiers" value="public"/>
|
||||
<property name="allowMissingParamTags" value="true"/>
|
||||
<property name="allowMissingReturnTag" value="true"/>
|
||||
<property name="allowedAnnotations"
|
||||
value="Override, Test, Before, After, BeforeClass, AfterClass, Parameterized, Parameters, Bean"/>
|
||||
<property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF"/>
|
||||
</module>
|
||||
<module name="MissingJavadocMethod">
|
||||
<property name="scope" value="public"/>
|
||||
<property name="minLineCount" value="2"/>
|
||||
<property name="allowedAnnotations"
|
||||
value="Override, Test, Before, After, BeforeClass, AfterClass, Parameterized, Parameters, Bean"/>
|
||||
<property name="ignoreMethodNamesRegex" value="^set[A-Z].*|^get[A-Z].*|main"/>
|
||||
<property name="tokens" value="METHOD_DEF, ANNOTATION_FIELD_DEF"/>
|
||||
</module>
|
||||
<module name="SingleLineJavadoc">
|
||||
<property name="ignoreInlineTags" value="false"/>
|
||||
</module>
|
||||
<module name="InvalidJavadocPosition"/>
|
||||
<module name="SummaryJavadoc">
|
||||
<property name="forbiddenSummaryFragments"
|
||||
value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
|
||||
</module>
|
||||
<module name="JavadocParagraph"/>
|
||||
<module name="NonEmptyAtclauseDescription"/>
|
||||
|
||||
<!-- Coding Checker -->
|
||||
<module name="IllegalTokenText">
|
||||
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
|
||||
<property name="format"
|
||||
value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
|
||||
<property name="message"
|
||||
value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
|
||||
</module>
|
||||
<module name="OneStatementPerLine"/>
|
||||
<module name="MultipleVariableDeclarations"/>
|
||||
<module name="MissingSwitchDefault"/>
|
||||
<module name="FallThrough"/>
|
||||
<module name="NoFinalizer"/>
|
||||
<module name="OverloadMethodsDeclarationOrder"/>
|
||||
<module name="VariableDeclarationUsageDistance"/>
|
||||
<module name="AtclauseOrder">
|
||||
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
|
||||
</module>
|
||||
|
||||
<!-- Miscellaneous Checker -->
|
||||
<module name="AvoidEscapedUnicodeCharacters">
|
||||
<property name="allowEscapesForControlCharacters" value="true"/>
|
||||
<property name="allowByTailComment" value="true"/>
|
||||
<property name="allowNonPrintableEscapes" value="true"/>
|
||||
</module>
|
||||
<module name="Indentation">
|
||||
<property name="arrayInitIndent" value="8"/>
|
||||
<property name="lineWrappingIndentation" value="8"/>
|
||||
</module>
|
||||
<module name="CommentsIndentation">
|
||||
<property name="tokens" value="SINGLE_LINE_COMMENT, BLOCK_COMMENT_BEGIN"/>
|
||||
</module>
|
||||
<module name="ArrayTypeStyle"/>
|
||||
<module name="UpperEll"/>
|
||||
|
||||
<!-- Design Checker -->
|
||||
<module name="OneTopLevelClass"/>
|
||||
|
||||
<!-- Whitespace -->
|
||||
<module name="NoLineWrap"/>
|
||||
<module name="WhitespaceAfter"/>
|
||||
<module name="WhitespaceAround">
|
||||
<property name="allowEmptyConstructors" value="true"/>
|
||||
</module>
|
||||
<module name="EmptyLineSeparator">
|
||||
<property name="allowMultipleEmptyLines" value="false"/>
|
||||
<property name="allowMultipleEmptyLinesInsideClassMembers" value="false"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<property name="id" value="SeparatorWrapDot"/>
|
||||
<property name="tokens" value="DOT"/>
|
||||
<property name="option" value="nl"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<property name="id" value="SeparatorWrapComma"/>
|
||||
<property name="tokens" value="COMMA"/>
|
||||
<property name="option" value="EOL"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<property name="id" value="SeparatorWrapEllipsis"/>
|
||||
<property name="tokens" value="ELLIPSIS"/>
|
||||
<property name="option" value="EOL"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<property name="id" value="SeparatorWrapArrayDeclarator"/>
|
||||
<property name="tokens" value="ARRAY_DECLARATOR"/>
|
||||
<property name="option" value="EOL"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<property name="id" value="SeparatorWrapMethodRef"/>
|
||||
<property name="tokens" value="METHOD_REF"/>
|
||||
<property name="option" value="nl"/>
|
||||
</module>
|
||||
<module name="GenericWhitespace">
|
||||
<message key="ws.followed"
|
||||
value="GenericWhitespace ''{0}'' is followed by whitespace."/>
|
||||
<message key="ws.preceded"
|
||||
value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
|
||||
<message key="ws.illegalFollow"
|
||||
value="GenericWhitespace ''{0}'' should followed by whitespace."/>
|
||||
<message key="ws.notPreceded"
|
||||
value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
|
||||
</module>
|
||||
<module name="MethodParamPad"/>
|
||||
<module name="NoWhitespaceBefore"/>
|
||||
<module name="ParenPad"/>
|
||||
<module name="OperatorWrap">
|
||||
<property name="option" value="NL"/>
|
||||
<property name="tokens"
|
||||
value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR,
|
||||
LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF "/>
|
||||
</module>
|
||||
|
||||
<!-- Modifier Checker -->
|
||||
<module name="ModifierOrder"/>
|
||||
|
||||
<!-- Annotation Checker -->
|
||||
<module name="AnnotationLocation">
|
||||
<property name="id" value="AnnotationLocationMostCases"/>
|
||||
<property name="tokens"
|
||||
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/>
|
||||
</module>
|
||||
<module name="AnnotationLocation">
|
||||
<property name="id" value="AnnotationLocationVariables"/>
|
||||
<property name="tokens" value="VARIABLE_DEF"/>
|
||||
<property name="allowSamelineMultipleAnnotations" value="true"/>
|
||||
</module>
|
||||
</module>
|
||||
</module>
|
||||
576
style/code-style-for-idea.xml
Normal file
576
style/code-style-for-idea.xml
Normal file
@@ -0,0 +1,576 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<code_scheme name="NacoStyle">
|
||||
<option name="OTHER_INDENT_OPTIONS">
|
||||
<value>
|
||||
<option name="INDENT_SIZE" value="2"/>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4"/>
|
||||
<option name="TAB_SIZE" value="2"/>
|
||||
</value>
|
||||
</option>
|
||||
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="999"/>
|
||||
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="999"/>
|
||||
<option name="LINE_SEPARATOR" value="
"/>
|
||||
<AndroidXmlCodeStyleSettings>
|
||||
<option name="USE_CUSTOM_SETTINGS" value="true"/>
|
||||
<option name="LAYOUT_SETTINGS">
|
||||
<value>
|
||||
<option name="INSERT_BLANK_LINE_BEFORE_TAG" value="false"/>
|
||||
</value>
|
||||
</option>
|
||||
</AndroidXmlCodeStyleSettings>
|
||||
<JSCodeStyleSettings version="0">
|
||||
<option name="INDENT_CHAINED_CALLS" value="false"/>
|
||||
</JSCodeStyleSettings>
|
||||
<Objective-C>
|
||||
<option name="INDENT_NAMESPACE_MEMBERS" value="0"/>
|
||||
<option name="INDENT_C_STRUCT_MEMBERS" value="2"/>
|
||||
<option name="INDENT_CLASS_MEMBERS" value="2"/>
|
||||
<option name="INDENT_VISIBILITY_KEYWORDS" value="1"/>
|
||||
<option name="INDENT_INSIDE_CODE_BLOCK" value="2"/>
|
||||
<option name="KEEP_STRUCTURES_IN_ONE_LINE" value="true"/>
|
||||
<option name="FUNCTION_PARAMETERS_WRAP" value="5"/>
|
||||
<option name="FUNCTION_CALL_ARGUMENTS_WRAP" value="5"/>
|
||||
<option name="TEMPLATE_CALL_ARGUMENTS_WRAP" value="5"/>
|
||||
<option name="TEMPLATE_CALL_ARGUMENTS_ALIGN_MULTILINE" value="true"/>
|
||||
<option name="ALIGN_INIT_LIST_IN_COLUMNS" value="false"/>
|
||||
<option name="SPACE_BEFORE_SUPERCLASS_COLON" value="false"/>
|
||||
</Objective-C>
|
||||
<Objective-C-extensions>
|
||||
<option name="GENERATE_INSTANCE_VARIABLES_FOR_PROPERTIES" value="ASK"/>
|
||||
<option name="RELEASE_STYLE" value="IVAR"/>
|
||||
<option name="TYPE_QUALIFIERS_PLACEMENT" value="BEFORE"/>
|
||||
<file>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import"/>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro"/>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef"/>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum"/>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant"/>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global"/>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct"/>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl"/>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function"/>
|
||||
</file>
|
||||
<class>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property"/>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize"/>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod"/>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod"/>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod"/>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod"/>
|
||||
</class>
|
||||
<extensions>
|
||||
<pair source="cc" header="h"/>
|
||||
<pair source="c" header="h"/>
|
||||
</extensions>
|
||||
</Objective-C-extensions>
|
||||
<Python>
|
||||
<option name="USE_CONTINUATION_INDENT_FOR_ARGUMENTS" value="true"/>
|
||||
</Python>
|
||||
<TypeScriptCodeStyleSettings version="0">
|
||||
<option name="INDENT_CHAINED_CALLS" value="false"/>
|
||||
</TypeScriptCodeStyleSettings>
|
||||
<XML>
|
||||
<option name="XML_ALIGN_ATTRIBUTES" value="false"/>
|
||||
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true"/>
|
||||
</XML>
|
||||
<editorconfig>
|
||||
<option name="ENABLED" value="false"/>
|
||||
</editorconfig>
|
||||
<codeStyleSettings language="CSS">
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2"/>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4"/>
|
||||
<option name="TAB_SIZE" value="2"/>
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="ECMA Script Level 4">
|
||||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1"/>
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false"/>
|
||||
<option name="ALIGN_MULTILINE_FOR" value="false"/>
|
||||
<option name="CALL_PARAMETERS_WRAP" value="1"/>
|
||||
<option name="METHOD_PARAMETERS_WRAP" value="1"/>
|
||||
<option name="EXTENDS_LIST_WRAP" value="1"/>
|
||||
<option name="BINARY_OPERATION_WRAP" value="1"/>
|
||||
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true"/>
|
||||
<option name="TERNARY_OPERATION_WRAP" value="1"/>
|
||||
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true"/>
|
||||
<option name="FOR_STATEMENT_WRAP" value="1"/>
|
||||
<option name="ARRAY_INITIALIZER_WRAP" value="1"/>
|
||||
<option name="IF_BRACE_FORCE" value="3"/>
|
||||
<option name="DOWHILE_BRACE_FORCE" value="3"/>
|
||||
<option name="WHILE_BRACE_FORCE" value="3"/>
|
||||
<option name="FOR_BRACE_FORCE" value="3"/>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="HTML">
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2"/>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4"/>
|
||||
<option name="TAB_SIZE" value="2"/>
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true"/>
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JAVA">
|
||||
<option name="KEEP_LINE_BREAKS" value="false"/>
|
||||
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false"/>
|
||||
<option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false"/>
|
||||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1"/>
|
||||
<option name="BLANK_LINES_BEFORE_PACKAGE" value="1"/>
|
||||
<option name="BLANK_LINES_AROUND_FIELD" value="1"/>
|
||||
<option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1"/>
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false"/>
|
||||
<option name="ALIGN_MULTILINE_RESOURCES" value="false"/>
|
||||
<option name="ALIGN_MULTILINE_FOR" value="false"/>
|
||||
<option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true"/>
|
||||
<option name="CALL_PARAMETERS_WRAP" value="1"/>
|
||||
<option name="METHOD_PARAMETERS_WRAP" value="1"/>
|
||||
<option name="RESOURCE_LIST_WRAP" value="2"/>
|
||||
<option name="EXTENDS_KEYWORD_WRAP" value="1"/>
|
||||
<option name="THROWS_KEYWORD_WRAP" value="1"/>
|
||||
<option name="METHOD_CALL_CHAIN_WRAP" value="1"/>
|
||||
<option name="BINARY_OPERATION_WRAP" value="1"/>
|
||||
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true"/>
|
||||
<option name="TERNARY_OPERATION_WRAP" value="1"/>
|
||||
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true"/>
|
||||
<option name="FOR_STATEMENT_WRAP" value="1"/>
|
||||
<option name="ARRAY_INITIALIZER_WRAP" value="1"/>
|
||||
<option name="WRAP_COMMENTS" value="true"/>
|
||||
<option name="IF_BRACE_FORCE" value="3"/>
|
||||
<option name="DOWHILE_BRACE_FORCE" value="3"/>
|
||||
<option name="WHILE_BRACE_FORCE" value="3"/>
|
||||
<option name="FOR_BRACE_FORCE" value="3"/>
|
||||
<option name="ENUM_CONSTANTS_WRAP" value="2"/>
|
||||
<indentOptions>
|
||||
<option name="SMART_TABS" value="true"/>
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true"/>
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JSON">
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4"/>
|
||||
<option name="TAB_SIZE" value="2"/>
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true"/>
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JavaScript">
|
||||
<option name="RIGHT_MARGIN" value="80"/>
|
||||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1"/>
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false"/>
|
||||
<option name="ALIGN_MULTILINE_FOR" value="false"/>
|
||||
<option name="CALL_PARAMETERS_WRAP" value="1"/>
|
||||
<option name="METHOD_PARAMETERS_WRAP" value="1"/>
|
||||
<option name="BINARY_OPERATION_WRAP" value="1"/>
|
||||
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true"/>
|
||||
<option name="TERNARY_OPERATION_WRAP" value="1"/>
|
||||
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true"/>
|
||||
<option name="FOR_STATEMENT_WRAP" value="1"/>
|
||||
<option name="ARRAY_INITIALIZER_WRAP" value="1"/>
|
||||
<option name="IF_BRACE_FORCE" value="3"/>
|
||||
<option name="DOWHILE_BRACE_FORCE" value="3"/>
|
||||
<option name="WHILE_BRACE_FORCE" value="3"/>
|
||||
<option name="FOR_BRACE_FORCE" value="3"/>
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2"/>
|
||||
<option name="TAB_SIZE" value="2"/>
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="ObjectiveC">
|
||||
<option name="RIGHT_MARGIN" value="80"/>
|
||||
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1"/>
|
||||
<option name="BLANK_LINES_BEFORE_IMPORTS" value="0"/>
|
||||
<option name="BLANK_LINES_AFTER_IMPORTS" value="0"/>
|
||||
<option name="BLANK_LINES_AROUND_CLASS" value="0"/>
|
||||
<option name="BLANK_LINES_AROUND_METHOD" value="0"/>
|
||||
<option name="BLANK_LINES_AROUND_METHOD_IN_INTERFACE" value="0"/>
|
||||
<option name="ALIGN_MULTILINE_BINARY_OPERATION" value="false"/>
|
||||
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true"/>
|
||||
<option name="FOR_STATEMENT_WRAP" value="1"/>
|
||||
<option name="ASSIGNMENT_WRAP" value="1"/>
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2"/>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4"/>
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="PROTO">
|
||||
<option name="RIGHT_MARGIN" value="80"/>
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2"/>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2"/>
|
||||
<option name="TAB_SIZE" value="2"/>
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="Python">
|
||||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1"/>
|
||||
<option name="RIGHT_MARGIN" value="80"/>
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false"/>
|
||||
<option name="PARENT_SETTINGS_INSTALLED" value="true"/>
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2"/>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4"/>
|
||||
<option name="TAB_SIZE" value="2"/>
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="SASS">
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4"/>
|
||||
<option name="TAB_SIZE" value="2"/>
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="SCSS">
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4"/>
|
||||
<option name="TAB_SIZE" value="2"/>
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="TypeScript">
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2"/>
|
||||
<option name="TAB_SIZE" value="2"/>
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="XML">
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4"/>
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true"/>
|
||||
</indentOptions>
|
||||
<arrangement>
|
||||
<rules>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:android</NAME>
|
||||
<XML_ATTRIBUTE/>
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:.*</NAME>
|
||||
<XML_ATTRIBUTE/>
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:id</NAME>
|
||||
<XML_ATTRIBUTE/>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>style</NAME>
|
||||
<XML_ATTRIBUTE/>
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE/>
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:.*Style</NAME>
|
||||
<XML_ATTRIBUTE/>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:layout_width</NAME>
|
||||
<XML_ATTRIBUTE/>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:layout_height</NAME>
|
||||
<XML_ATTRIBUTE/>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:layout_weight</NAME>
|
||||
<XML_ATTRIBUTE/>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:layout_margin</NAME>
|
||||
<XML_ATTRIBUTE/>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:layout_marginTop</NAME>
|
||||
<XML_ATTRIBUTE/>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:layout_marginBottom</NAME>
|
||||
<XML_ATTRIBUTE/>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:layout_marginStart</NAME>
|
||||
<XML_ATTRIBUTE/>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:layout_marginEnd</NAME>
|
||||
<XML_ATTRIBUTE/>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:layout_marginLeft</NAME>
|
||||
<XML_ATTRIBUTE/>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:layout_marginRight</NAME>
|
||||
<XML_ATTRIBUTE/>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:layout_.*</NAME>
|
||||
<XML_ATTRIBUTE/>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:padding</NAME>
|
||||
<XML_ATTRIBUTE/>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:paddingTop</NAME>
|
||||
<XML_ATTRIBUTE/>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:paddingBottom</NAME>
|
||||
<XML_ATTRIBUTE/>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:paddingStart</NAME>
|
||||
<XML_ATTRIBUTE/>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:paddingEnd</NAME>
|
||||
<XML_ATTRIBUTE/>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:paddingLeft</NAME>
|
||||
<XML_ATTRIBUTE/>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:paddingRight</NAME>
|
||||
<XML_ATTRIBUTE/>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res-auto</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_NAMESPACE>http://schemas.android.com/tools</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
</rules>
|
||||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="protobuf">
|
||||
<option name="RIGHT_MARGIN" value="80"/>
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2"/>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2"/>
|
||||
<option name="TAB_SIZE" value="2"/>
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
31
style/codeStyle.md
Normal file
31
style/codeStyle.md
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
|
||||
## Code Style
|
||||
编码规范遵从于《阿里巴巴JAVA开发规约》,基于google style。
|
||||
|
||||
### Guidelines
|
||||
[Alibaba-Java-Coding-Guidelines](https://alibaba.github.io/Alibaba-Java-Coding-Guidelines/)
|
||||
|
||||
[阿里巴巴JAVA开发规约](https://github.com/alibaba/p3c/blob/master/%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4Java%E5%BC%80%E5%8F%91%E6%89%8B%E5%86%8C%EF%BC%88%E5%8D%8E%E5%B1%B1%E7%89%88%EF%BC%89.pdf)
|
||||
|
||||
|
||||
|
||||
## Code Style File
|
||||
|
||||
### Idea IDE
|
||||
|
||||
代码风格文件在源代码下的`style/code-style-for-idea.xml`文件中,开发者可以将其导入到Idea IDE中,并让IDE帮助您格式化代码。
|
||||
|
||||
#### Import Way/导入方式
|
||||
|
||||
```
|
||||
Preferences/Settings --> Editor --> Code Style --> Schema --> Import Schema --> IntelliJ IDEA code style XML
|
||||
```
|
||||
|
||||
#### checkstyle
|
||||
[checkstyle插件idea安装](https://plugins.jetbrains.com/plugin/1065-checkstyle-idea)
|
||||
|
||||
1. `Preferences/Settings --> Other Settings --> Checkstyle` 或者 `Preferences/Settings --> Tools --> Checkstyle`
|
||||
2. 在checkstyle插件中设置checkstyle版本至少为8.30,并将扫描作用域设置为`All resource(including tests)`
|
||||
3. 导入源代码下`style/CheckStyle.xml`文件到checkstyle插件。
|
||||
4. 用checkstyle插件扫描你修改的代码。
|
||||
203
web-server/pom.xml
Normal file
203
web-server/pom.xml
Normal file
@@ -0,0 +1,203 @@
|
||||
<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/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.yelink.example</groupId>
|
||||
<artifactId>rainyhon-xzt</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>web-server</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.yelink.example</groupId>
|
||||
<artifactId>yelink-redis-starter</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.yelink.example</groupId>
|
||||
<artifactId>yelink-doc-starter</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.yelink.example</groupId>
|
||||
<artifactId>yelink-security-starter</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.yelink.example</groupId>
|
||||
<artifactId>yelink-minio-starter</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.kafka</groupId>
|
||||
<artifactId>spring-kafka</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-core</artifactId>
|
||||
<version>1.19</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-generator-annprocess</artifactId>
|
||||
<version>1.19</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!--打包jar-->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<!--不打包资源文件,exclude的目录不是src下面的,是以编译结果classes为根目录计算-->
|
||||
<excludes>
|
||||
<exclude>*.properties</exclude>
|
||||
<exclude>*.yml</exclude>
|
||||
<exclude>*.xml</exclude>
|
||||
</excludes>
|
||||
<archive>
|
||||
<manifest>
|
||||
<addClasspath>true</addClasspath>
|
||||
<!--MANIFEST.MF 中 Class-Path 加入前缀-->
|
||||
<classpathPrefix>lib/</classpathPrefix>
|
||||
<!--jar包不包含唯一版本标识-->
|
||||
<useUniqueVersions>false</useUniqueVersions>
|
||||
<!--指定入口类-->
|
||||
<mainClass>com.rainyhon.swput3.web.WebServerApplication</mainClass>
|
||||
</manifest>
|
||||
<manifestEntries>
|
||||
<!--MANIFEST.MF 中 Class-Path 加入资源文件目录-->
|
||||
<Class-Path>./resources/</Class-Path>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
<outputDirectory>${project.build.directory}</outputDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!--拷贝依赖 copy-dependencies-->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-dependencies</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>
|
||||
${project.build.directory}/lib/
|
||||
</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!--拷贝资源文件 copy-resources-->
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-resources</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
<outputDirectory>${project.build.directory}/resources</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!--spring boot repackage,依赖 maven-jar-plugin 打包的jar包 重新打包成 spring boot 的jar包-->
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<!--重写包含依赖,包含不存在的依赖,jar里没有pom里的依赖-->
|
||||
<includes>
|
||||
<include>
|
||||
<groupId>null</groupId>
|
||||
<artifactId>null</artifactId>
|
||||
</include>
|
||||
</includes>
|
||||
<layout>ZIP</layout>
|
||||
<!--使用外部配置文件,jar包里没有资源文件-->
|
||||
<addResources>true</addResources>
|
||||
<outputDirectory>${project.build.directory}</outputDirectory>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<!--配置jar包特殊标识 配置后,保留原文件,生成新文件 *-run.jar -->
|
||||
<!--配置jar包特殊标识 不配置,原文件命名为 *.jar.original,生成新文件 *.jar -->
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>com.spotify</groupId>
|
||||
<artifactId>dockerfile-maven-plugin</artifactId>
|
||||
<version>1.3.6</version>
|
||||
<configuration>
|
||||
<repository>${project.artifactId}</repository>
|
||||
<buildArgs>
|
||||
<JAR_FILE>target/</JAR_FILE>
|
||||
</buildArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.rainyhon.swput3.web;
|
||||
|
||||
import com.yelink.security.annotation.AppEnableResourceServer;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @date 2024-08-21 15:31
|
||||
*/
|
||||
@AppEnableResourceServer
|
||||
@SpringBootApplication
|
||||
@EnableFeignClients(basePackages = "com.rainyhon.swput3.web.client")
|
||||
public class WebServerApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(WebServerApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.rainyhon.swput3.web.client;
|
||||
|
||||
import com.rainyhon.swput3.web.dto.Result;
|
||||
import com.rainyhon.swput3.web.openapi.openfeign.EdgeFeignConfig;
|
||||
import com.rainyhon.swput3.web.client.factory.DfsClientFallbackFactory;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
*/
|
||||
@FeignClient(
|
||||
name = "dfsClient",
|
||||
url = "${openapi.edge-gateway-url}",
|
||||
// url = "https://192.168.102.180:8301",
|
||||
path = "/openApi/dfs",
|
||||
fallbackFactory = DfsClientFallbackFactory.class,
|
||||
configuration = EdgeFeignConfig.class)
|
||||
public interface DfsClient {
|
||||
|
||||
/**
|
||||
* 查询设备的历史数据
|
||||
*
|
||||
* @param page 页数
|
||||
* @param size 每页大小
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/api/v1/open/sensors/records")
|
||||
Result querySensorsRecords(@RequestParam(value = "current") Integer page, @RequestParam(value = "size") Integer size);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.rainyhon.swput3.web.client.factory;
|
||||
|
||||
import com.rainyhon.swput3.web.client.DfsClient;
|
||||
import feign.hystrix.FallbackFactory;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @date 2024-08-22 15:28
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class DfsClientFallbackFactory implements FallbackFactory<DfsClient> {
|
||||
@Override
|
||||
public DfsClient create(Throwable throwable) {
|
||||
log.error("DFS服务调用失败:{}", throwable.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.rainyhon.swput3.web.controller;
|
||||
|
||||
import com.rainyhon.swput3.web.dto.Result;
|
||||
import com.rainyhon.swput3.web.dto.req.SaveDemoModelReq;
|
||||
import com.rainyhon.swput3.web.dto.req.SendKafkaMessageReq;
|
||||
import com.rainyhon.swput3.web.dto.req.UploadFileReq;
|
||||
import com.rainyhon.swput3.web.service.IDemoService;
|
||||
import com.rainyhon.swput3.web.utils.ResultUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @date 2024-08-21 15:41
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping()
|
||||
@RequiredArgsConstructor
|
||||
@Validated
|
||||
@Api(tags = "样例控制器")
|
||||
public class DemoController {
|
||||
|
||||
private final IDemoService demoService;
|
||||
|
||||
|
||||
@PostMapping("/kafka/send")
|
||||
@ApiOperation(
|
||||
value = "发送kafka消息"
|
||||
)
|
||||
public Result send(@Valid @RequestBody SendKafkaMessageReq req) {
|
||||
demoService.sendKafka(req);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
|
||||
@PostMapping("/mysql/save")
|
||||
@ApiOperation(
|
||||
value = "mysql保存数据样例"
|
||||
)
|
||||
public Result mysqlSaveDemo(@Valid @RequestBody SaveDemoModelReq req) {
|
||||
demoService.mysqlSaveDemo(req);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
|
||||
@PostMapping("/redis/save")
|
||||
@ApiOperation(
|
||||
value = "redis保存数据样例"
|
||||
)
|
||||
public Result redisSave(@Valid @RequestBody SaveDemoModelReq req) {
|
||||
demoService.redisSave(req);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
|
||||
@PostMapping("/redis/test/single")
|
||||
@ApiOperation(
|
||||
value = "redis压测"
|
||||
)
|
||||
public Result redisTestSingle(@RequestParam(value = "count") int count) {
|
||||
demoService.redistTestSingle(count);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
|
||||
@PostMapping("/redis/test/batch")
|
||||
@ApiOperation(
|
||||
value = "redis压测"
|
||||
)
|
||||
public Result redisTestBatch(@RequestParam(value = "count") int count) {
|
||||
demoService.redisTestBatch(count);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
@PostMapping("/minio/upload")
|
||||
@ApiOperation(
|
||||
value = "minio上传文件样例",
|
||||
notes = "返回的是minio文件的相对路径。\n" +
|
||||
"为了支持多网络访问,推荐用法是前端使用该相对路径地址请求,后端的nginx已经默认配置路由规则到文件系统服务。"
|
||||
)
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "file", value = "上传文件", paramType = "form", dataType = "__file", required = true),
|
||||
})
|
||||
public Result<String> minioUpload(@Valid UploadFileReq req) {
|
||||
return ResultUtil.successJsonStr(demoService.minioUpload(req));
|
||||
}
|
||||
|
||||
@PostMapping("/call/service/api")
|
||||
@ApiOperation(
|
||||
value = "调用其他服务接口样例",
|
||||
notes = "调用其他服务需要经过网关。\n" +
|
||||
"调用此接口如果携带了token的请求头,则采用token的认证方式调用其他服务,否则采用AK/SK的认证方式。"
|
||||
)
|
||||
public Result callServiceApi() {
|
||||
Result res = demoService.callServiceApi();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.rainyhon.swput3.web.controller;
|
||||
|
||||
import com.rainyhon.swput3.web.dto.Result;
|
||||
import com.rainyhon.swput3.web.dto.req.ProcessesTaskModelReq;
|
||||
import com.rainyhon.swput3.web.service.IProcessesTaskService;
|
||||
import com.rainyhon.swput3.web.utils.ResultUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* @author leim
|
||||
* @date 2024-08-21 15:41
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/task")
|
||||
@RequiredArgsConstructor
|
||||
@Validated
|
||||
@Api(tags = "工序任务")
|
||||
public class ProcessesTaskController {
|
||||
|
||||
private final IProcessesTaskService taskService;
|
||||
|
||||
@PostMapping("/add")
|
||||
@ApiOperation(value = "新增任务")
|
||||
public Result add(@Valid @RequestBody ProcessesTaskModelReq data) {
|
||||
taskService.add(data);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
|
||||
@PutMapping("/edit")
|
||||
@ApiOperation(value = "修改任务")
|
||||
public Result edit(@Valid @RequestBody ProcessesTaskModelReq data) {
|
||||
taskService.edit(data);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
|
||||
@DeleteMapping("/del")
|
||||
@ApiOperation(value = "删除任务")
|
||||
public Result del(Long taskId) {
|
||||
taskService.deletes(taskId);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
|
||||
@GetMapping("/detail")
|
||||
@ApiOperation(value = "详情任务")
|
||||
public Result detail(Long taskId) {
|
||||
return ResultUtil.success(taskService.detail(taskId));
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@ApiOperation(value = "任务列表")
|
||||
public Result list(@RequestParam(required = false) Long proId,
|
||||
@RequestParam(required = false) String taskName,
|
||||
@RequestParam(required = false) String productCode) {
|
||||
return ResultUtil.success(taskService.list(proId, taskName,productCode));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.rainyhon.swput3.web.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @date 2024-01-09 12:20
|
||||
*/
|
||||
@Data
|
||||
public class Result<T> {
|
||||
private Integer code;
|
||||
private String message;
|
||||
private T data;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.rainyhon.swput3.web.dto.req;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
/**
|
||||
* @author leim
|
||||
* @date 2025-06-26 17:08
|
||||
*/
|
||||
@Data
|
||||
public class ProcessesTaskModelReq {
|
||||
@ApiModelProperty(value = "产品编码")
|
||||
private String productCode;
|
||||
@ApiModelProperty(value = "任务id")
|
||||
private Long taskId;
|
||||
@ApiModelProperty(value = "任务名称")
|
||||
private String taskName;
|
||||
@ApiModelProperty(value = "任务内容")
|
||||
private String taskContent;
|
||||
@ApiModelProperty(value = "工序id")
|
||||
private Long proId;
|
||||
@ApiModelProperty(value = "备注")
|
||||
private String remark;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.rainyhon.swput3.web.dto.req;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
*/
|
||||
@Data
|
||||
public class SaveDemoModelReq {
|
||||
|
||||
@Pattern(
|
||||
regexp = "^[A-Za-z0-9][A-Za-z0-9_.]{1,18}[A-Za-z0-9]$",
|
||||
message = "用户名长度必须为3-20个字符,以字母或数字开头和结尾,可以包含字母、数字、下划线和句点"
|
||||
)
|
||||
@ApiModelProperty(value = "用户名称", notes = "用户名长度必须为3-20个字符,以字母或数字开头和结尾,可以包含字母、数字、下划线和句点")
|
||||
private String username;
|
||||
@ApiModelProperty(value = "用户地址")
|
||||
private String address;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.rainyhon.swput3.web.dto.req;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @date 2024-01-09 12:14
|
||||
*/
|
||||
@Data
|
||||
public class SendKafkaMessageReq {
|
||||
|
||||
@Pattern(regexp = "^[a-z0-9]+(_[a-z0-9]+)*(_v[0-9]+)?$", message = "主题名称必须以小写字母或数字开始,主题名称可以包含多个由下划线分隔的部分,每个部分由小写字母或数字组成。")
|
||||
@ApiModelProperty(value = "主题名称", notes = "主题名称必须以小写字母或数字开始,主题名称可以包含多个由下划线分隔的部分,每个部分由小写字母或数字组成。")
|
||||
private String topic;
|
||||
@ApiModelProperty(value = "主题主键")
|
||||
private String key;
|
||||
@ApiModelProperty(value = "消息内容")
|
||||
private String message;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.rainyhon.swput3.web.dto.req;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import lombok.Data;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @date 2024-08-22 11:51
|
||||
*/
|
||||
@Data
|
||||
public class UploadFileReq {
|
||||
@NotNull
|
||||
private MultipartFile file;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.rainyhon.swput3.web.handler;
|
||||
|
||||
|
||||
import com.rainyhon.swput3.web.dto.Result;
|
||||
import com.rainyhon.swput3.web.utils.ResultUtil;
|
||||
import com.rainyhon.swput3.web.handler.exception.BusinessException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
import java.nio.file.AccessDeniedException;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* @Description 全局异常处理
|
||||
* @auther cwp
|
||||
* @create 2019-07-15 13:35
|
||||
*/
|
||||
@Slf4j
|
||||
public class BaseExceptionHandler {
|
||||
|
||||
|
||||
@ExceptionHandler(value = Exception.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public void handleException(Exception e) {
|
||||
log.error("系统内部异常,异常信息", e);
|
||||
}
|
||||
|
||||
|
||||
@ExceptionHandler(value = BusinessException.class)
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
public Result handleFebsException(BusinessException e) {
|
||||
log.error("BusinessException {} ", e.getSnapshot());
|
||||
return ResultUtil.error(e.getCode(), e.getSnapshot());
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一处理请求参数校验(实体对象传参)
|
||||
*
|
||||
* @param e BindException
|
||||
* @return FebsResponse
|
||||
*/
|
||||
@ExceptionHandler(BindException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public Result handleBindException(BindException e) {
|
||||
StringBuilder message = new StringBuilder();
|
||||
List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
|
||||
for (FieldError error : fieldErrors) {
|
||||
message.append(error.getField()).append(error.getDefaultMessage()).append(",");
|
||||
}
|
||||
message = new StringBuilder(message.substring(0, message.length() - 1));
|
||||
return ResultUtil.error(40001, message.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一处理请求参数校验(json)
|
||||
*
|
||||
* @param e ConstraintViolationException
|
||||
* @return FebsResponse
|
||||
*/
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public Result handlerMethodArgumentNotValidException(MethodArgumentNotValidException e) {
|
||||
StringBuilder message = new StringBuilder();
|
||||
for (FieldError error : e.getBindingResult().getFieldErrors()) {
|
||||
message.append(error.getField()).append(error.getDefaultMessage()).append(",");
|
||||
}
|
||||
message = new StringBuilder(message.substring(0, message.length() - 1));
|
||||
log.error(message.toString(), e);
|
||||
return ResultUtil.error(40003, message.toString());
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = AccessDeniedException.class)
|
||||
@ResponseStatus(HttpStatus.FORBIDDEN)
|
||||
public Result handleAccessDeniedException() {
|
||||
return ResultUtil.error(10407, "没有权限访问该资源");
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = HttpMediaTypeNotSupportedException.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public Result handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) {
|
||||
String msg = "该方法不支持" + StringUtils.substringBetween(e.getMessage(), "'", "'") + "媒体类型";
|
||||
return ResultUtil.error(100801, msg);
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public Result handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
|
||||
String msg = "该方法不支持" + StringUtils.substringBetween(e.getMessage(), "'", "'") + "请求方法";
|
||||
return ResultUtil.error(100802, msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.rainyhon.swput3.web.handler;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
@Order(value = Ordered.HIGHEST_PRECEDENCE)
|
||||
@Slf4j
|
||||
public class GlobalExceptionHandler extends BaseExceptionHandler {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.rainyhon.swput3.web.handler.exception;
|
||||
|
||||
import org.slf4j.helpers.MessageFormatter;
|
||||
|
||||
|
||||
public class BusinessException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 9215327422367136022L;
|
||||
private int code;
|
||||
private String snapshot;
|
||||
|
||||
public BusinessException(ErrorCode errorCode) {
|
||||
super(errorCode.getMessage());
|
||||
this.code = errorCode.getCode();
|
||||
this.snapshot = errorCode.getMessage();
|
||||
}
|
||||
|
||||
public BusinessException(ErrorCode errorCode, String snapshotFormat, Object... argArray) {
|
||||
super(errorCode.getMessage());
|
||||
this.code = errorCode.getCode();
|
||||
this.snapshot = errorCode.getMessage() + MessageFormatter.arrayFormat(snapshotFormat, argArray).getMessage();
|
||||
}
|
||||
public BusinessException(int code, String message) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.snapshot = message;
|
||||
}
|
||||
public BusinessException(int code, String message, String snapshotFormat, Object... argArray) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.snapshot = MessageFormatter.arrayFormat(snapshotFormat, argArray).getMessage();
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getSnapshot() {
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.rainyhon.swput3.web.handler.exception;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @date 2024-03-14 11:39
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@ToString
|
||||
public enum ErrorCode {
|
||||
|
||||
/**
|
||||
* code 错误码
|
||||
* message 详细的错误信息
|
||||
*/
|
||||
KAFKA_SEND_FAIL(10000, "kafka消息发送异常"),
|
||||
MINIO_UPLOAD_FAIL(10001, "上传文件失败"),
|
||||
|
||||
END(99999,"END");
|
||||
|
||||
/**
|
||||
* 错误码
|
||||
*/
|
||||
private final int code;
|
||||
|
||||
/**
|
||||
* 详细的错误信息
|
||||
*/
|
||||
private final String message;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.rainyhon.swput3.web.kafka;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.kafka.clients.consumer.ConsumerRecord;
|
||||
import org.springframework.kafka.annotation.KafkaListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class KafkaListenerDemo {
|
||||
public static final String MIDDLE_PRIORITY_POST = "middle_priority_post";
|
||||
|
||||
@KafkaListener(topics = {MIDDLE_PRIORITY_POST})
|
||||
public void kafkaRealTimeMsgListener(ConsumerRecord<?, ?> record) {
|
||||
String msg = record.value().toString();
|
||||
log.info("接收到kafka消息 {}", msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.rainyhon.swput3.web.model;
|
||||
|
||||
import com.rainyhon.swput3.web.model.audit.DateAudit;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @date 2024-08-21 17:08
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "t_demo")
|
||||
@Data
|
||||
public class DemoModel extends DateAudit {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
private String username;
|
||||
private String address;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.rainyhon.swput3.web.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author leim
|
||||
* @date 2025-06-26 17:08
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "processes_task")
|
||||
@Data
|
||||
public class ProcessesTaskModel {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long taskId;
|
||||
|
||||
private String productCode;
|
||||
|
||||
private String taskName;
|
||||
|
||||
private String taskContent;
|
||||
|
||||
private Long proId;
|
||||
|
||||
private String remark;
|
||||
|
||||
private String createBy;
|
||||
|
||||
private String updateBy;
|
||||
|
||||
@CreatedDate
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date createTime;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date updateTime;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.rainyhon.swput3.web.model.audit;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.annotation.LastModifiedDate;
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
|
||||
import javax.persistence.EntityListeners;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
|
||||
@MappedSuperclass
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
@Data
|
||||
public abstract class DateAudit implements Serializable {
|
||||
|
||||
@CreatedDate
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Instant createdAt;
|
||||
|
||||
@LastModifiedDate
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Instant updatedAt;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.rainyhon.swput3.web.openapi;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
*/
|
||||
@Data
|
||||
public class OpenApiParams {
|
||||
public static final String SIGNATURE_KEY = "Signature";
|
||||
public static final String SIGNATURE_NONCE_KEY = "SignatureNonce";
|
||||
public static final String TIMESTAMP_KEY = "Timestamp";
|
||||
public static final String ACCESSKEY_ID_KEY = "AccessKeyId";
|
||||
|
||||
public static final String TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
|
||||
|
||||
private String signature;
|
||||
private String signatureMethod;
|
||||
private String signatureNonce;
|
||||
private Instant timestamp;
|
||||
private String accessKeyId;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.rainyhon.swput3.web.openapi.openfeign;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
*/
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "app.security")
|
||||
@Data
|
||||
public class AccessKeyProperties {
|
||||
@JsonProperty("access-key-id")
|
||||
private String accessKeyId;
|
||||
|
||||
@JsonProperty("access-key-secret")
|
||||
private String accessKeySecret;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.rainyhon.swput3.web.openapi.openfeign;
|
||||
|
||||
import com.rainyhon.swput3.web.utils.RequestInterceptorUtil;
|
||||
import feign.RequestInterceptor;
|
||||
import feign.RequestTemplate;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import com.rainyhon.swput3.web.utils.SecurityUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class AccessKeyRequestInterceptor implements RequestInterceptor {
|
||||
|
||||
private AccessKeyProperties properties;
|
||||
|
||||
@Override
|
||||
public void apply(RequestTemplate template) {
|
||||
try {
|
||||
String token = SecurityUtils.getCurrentTokenValue();
|
||||
if (StringUtils.isNotEmpty(token)) {
|
||||
template.header("Authorization", "Bearer " + token);
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// do nothing
|
||||
}
|
||||
Collection<String> authorization = template.headers().get("Authorization");
|
||||
if (ObjectUtils.isNotEmpty(authorization)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 生成ak请求头
|
||||
RequestInterceptorUtil.generateAccessKeyHeader(properties, template);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.rainyhon.swput3.web.openapi.openfeign;
|
||||
|
||||
import feign.Client;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
|
||||
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class EdgeFeignConfig {
|
||||
|
||||
private final AccessKeyProperties properties;
|
||||
|
||||
/**
|
||||
* 自定义feign拦截器
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public AccessKeyRequestInterceptor customFeignInterceptor() {
|
||||
return new AccessKeyRequestInterceptor(properties);
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {
|
||||
return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
|
||||
SpringClientFactory clientFactory) throws NoSuchAlgorithmException, KeyManagementException {
|
||||
SSLContext ctx = SSLContext.getInstance("SSL");
|
||||
X509TrustManager tm = new X509TrustManager() {
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] chain, String authType) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
//如果这里后续报空指针,就return new X509Certificate[0]
|
||||
return null;
|
||||
}
|
||||
};
|
||||
ctx.init(null, new TrustManager[]{tm}, null);
|
||||
return new LoadBalancerFeignClient(new Client.Default(ctx.getSocketFactory(),
|
||||
(hostname, session) -> true),
|
||||
cachingFactory, clientFactory);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.rainyhon.swput3.web.repository;
|
||||
|
||||
import com.rainyhon.swput3.web.model.DemoModel;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @date 2024-08-21 17:11
|
||||
*/
|
||||
@Repository
|
||||
public interface DemoModelRepository extends JpaRepository<DemoModel, Long> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.rainyhon.swput3.web.repository;
|
||||
|
||||
import com.rainyhon.swput3.web.model.ProcessesTaskModel;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* @author leim
|
||||
* @date 2024-08-21 17:11
|
||||
*/
|
||||
@Repository
|
||||
public interface ProcessesTaskRepository extends JpaRepository<ProcessesTaskModel, Long> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
package com.rainyhon.swput3.web.service;
|
||||
|
||||
import com.rainyhon.swput3.web.dto.Result;
|
||||
import com.rainyhon.swput3.web.dto.req.SaveDemoModelReq;
|
||||
import com.rainyhon.swput3.web.dto.req.SendKafkaMessageReq;
|
||||
import com.rainyhon.swput3.web.dto.req.UploadFileReq;
|
||||
import com.rainyhon.swput3.web.model.DemoModel;
|
||||
import com.yelink.common.redis.service.RedisService;
|
||||
import com.rainyhon.swput3.web.client.DfsClient;
|
||||
import com.rainyhon.swput3.web.handler.exception.BusinessException;
|
||||
import com.rainyhon.swput3.web.handler.exception.ErrorCode;
|
||||
import com.rainyhon.swput3.web.repository.DemoModelRepository;
|
||||
import com.rainyhon.swput3.web.utils.RetryTimerTask;
|
||||
import com.yelink.minio.service.MinioService;
|
||||
import io.netty.util.HashedWheelTimer;
|
||||
import io.netty.util.Timer;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.redisson.api.RLock;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.redis.core.RedisOperations;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.SessionCallback;
|
||||
import org.springframework.data.redis.core.ValueOperations;
|
||||
import org.springframework.kafka.core.KafkaTemplate;
|
||||
import org.springframework.kafka.support.SendResult;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @date 2024-08-21 17:30
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class DemoServiceImpl implements IDemoService {
|
||||
private Timer failTimer = null;
|
||||
|
||||
private final KafkaTemplate<String, String> kafkaTemplate;
|
||||
private final DemoModelRepository demoModelRepository;
|
||||
|
||||
private final RedisService redisService;
|
||||
private final RedissonClient redissonClient;
|
||||
private final MinioService minioService;
|
||||
private final DfsClient dfsClient;
|
||||
private final RedisTemplate<String, Object> redisTemplate;
|
||||
private static final String LOCK_KEY = "REDIS_LOCK";
|
||||
|
||||
@Override
|
||||
public void sendKafka(SendKafkaMessageReq req) {
|
||||
try {
|
||||
SendResult<String, String> result = kafkaTemplate.send(req.getTopic(), req.getKey(), req.getMessage()).get(5, TimeUnit.SECONDS);
|
||||
log.info(result.toString());
|
||||
} catch (ExecutionException | TimeoutException e) {
|
||||
throw new BusinessException(ErrorCode.KAFKA_SEND_FAIL);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("发送消息失败 InterruptedException", e);
|
||||
Thread.currentThread().interrupt();
|
||||
throw new BusinessException(ErrorCode.KAFKA_SEND_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mysqlSaveDemo(SaveDemoModelReq req) {
|
||||
DemoModel demoModel = new DemoModel();
|
||||
BeanUtils.copyProperties(req, demoModel);
|
||||
demoModelRepository.save(demoModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void redisSave(SaveDemoModelReq req) {
|
||||
RLock rLock = redissonClient.getLock(LOCK_KEY);
|
||||
try {
|
||||
boolean isLock = rLock.tryLock(10, TimeUnit.MINUTES);
|
||||
if (isLock) {
|
||||
try {
|
||||
doInvoke(req);
|
||||
} catch (Throwable e) {
|
||||
log.error("调用 doInvoke 方法失败,5s 后将进入后台的自动重试,异常信息: ", e);
|
||||
addFailed(() -> doInvoke(req));
|
||||
}
|
||||
} else {
|
||||
log.error("未拿到锁");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
log.error("InterruptedException", e);
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
rLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String minioUpload(UploadFileReq req) {
|
||||
|
||||
try {
|
||||
return minioService.upload(req.getFile(), UUID.randomUUID().toString());
|
||||
} catch (Exception e) {
|
||||
throw new BusinessException(ErrorCode.MINIO_UPLOAD_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result callServiceApi() {
|
||||
return dfsClient.querySensorsRecords(1, 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void redistTestSingle(int count) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
redisService.set("test" + i, i);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void redisTestBatch(int count) {
|
||||
redisTemplate.executePipelined(new SessionCallback<Object>() {
|
||||
@Override
|
||||
public <K, V> Object execute(RedisOperations<K, V> operations) throws DataAccessException {
|
||||
ValueOperations<String, Object> valueOperations = (ValueOperations<String, Object>) operations.opsForValue();
|
||||
for (int i = 0; i < count; i++) {
|
||||
valueOperations.set("batch-test" + i, i);
|
||||
}
|
||||
// 返回null即可,因为返回值会被管道的返回值覆盖,外层取不到这里的返回值
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addFailed(Runnable task) {
|
||||
getInstance();
|
||||
// 每30s重试一次
|
||||
RetryTimerTask retryTimerTask = new RetryTimerTask(task, 30, 3);
|
||||
try {
|
||||
// 5s 后执行第一次重试
|
||||
failTimer.newTimeout(retryTimerTask, 5, TimeUnit.SECONDS);
|
||||
} catch (Throwable e) {
|
||||
log.error("提交定时任务失败,exception: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void doInvoke(SaveDemoModelReq req) {
|
||||
Boolean res = redisService.set(req.getUsername(), req.getAddress());
|
||||
if (res) {
|
||||
log.info("redis数据保存成功");
|
||||
} else {
|
||||
log.error("redis数据保存失败");
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void getInstance() {
|
||||
if (failTimer == null) {
|
||||
failTimer = new HashedWheelTimer();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
List<String> authOrganizationIds = Arrays.asList("1", "2", "3","5");
|
||||
// List<Long> authOrganizationIds = Arrays.asList(1L, 2L, 3L);
|
||||
List<String> organizationIds = Arrays.asList("1", "2", "3","4");
|
||||
|
||||
boolean match = authOrganizationIds.stream().anyMatch(v -> !organizationIds.stream().anyMatch(v1 -> Objects.equals(v, v1)));
|
||||
authOrganizationIds.stream().anyMatch(v -> organizationIds.stream().anyMatch(v1 -> !Objects.equals(v, v1)));
|
||||
System.out.println("=====");
|
||||
System.out.println(match);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.rainyhon.swput3.web.service;
|
||||
|
||||
import com.rainyhon.swput3.web.dto.Result;
|
||||
import com.rainyhon.swput3.web.dto.req.SaveDemoModelReq;
|
||||
import com.rainyhon.swput3.web.dto.req.SendKafkaMessageReq;
|
||||
import com.rainyhon.swput3.web.dto.req.UploadFileReq;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @date 2024-08-21 17:30
|
||||
*/
|
||||
public interface IDemoService {
|
||||
/**
|
||||
* 发送kafka消息
|
||||
*
|
||||
* @param req
|
||||
*/
|
||||
void sendKafka(SendKafkaMessageReq req);
|
||||
|
||||
void mysqlSaveDemo(SaveDemoModelReq req);
|
||||
|
||||
void redisSave(SaveDemoModelReq req);
|
||||
|
||||
String minioUpload(UploadFileReq req);
|
||||
|
||||
Result callServiceApi();
|
||||
|
||||
void redistTestSingle(int count);
|
||||
|
||||
void redisTestBatch(int count);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.rainyhon.swput3.web.service;
|
||||
|
||||
import com.rainyhon.swput3.web.dto.req.ProcessesTaskModelReq;
|
||||
import com.rainyhon.swput3.web.model.ProcessesTaskModel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author leim
|
||||
* @date 2025-06-26 17:30
|
||||
*/
|
||||
public interface IProcessesTaskService {
|
||||
/**
|
||||
* 新增任务
|
||||
*
|
||||
* @param data
|
||||
* @return null
|
||||
* @Date 2025/6/26
|
||||
*/
|
||||
|
||||
void add(ProcessesTaskModelReq data);
|
||||
|
||||
/**
|
||||
* 修改任务
|
||||
*
|
||||
* @param data
|
||||
* @return null
|
||||
* @Date 2025/6/26
|
||||
*/
|
||||
|
||||
void edit(ProcessesTaskModelReq data);
|
||||
|
||||
/**
|
||||
* 删除任务
|
||||
*
|
||||
* @param taskIds
|
||||
* @return null
|
||||
* @Date 2025/6/26
|
||||
*/
|
||||
|
||||
void deletes(Long taskIds);
|
||||
|
||||
/**
|
||||
* 详情
|
||||
*
|
||||
* @param taskId
|
||||
* @return null
|
||||
* @Date 2025/6/26
|
||||
*/
|
||||
|
||||
ProcessesTaskModel detail(Long taskId);
|
||||
|
||||
/**
|
||||
* 列表
|
||||
*
|
||||
* @param proId
|
||||
* @param taskName
|
||||
* @param productCode 产品编号
|
||||
* @return null
|
||||
* @Date 2025/6/26
|
||||
*/
|
||||
|
||||
List<ProcessesTaskModel> list(Long proId, String taskName,String productCode);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.rainyhon.swput3.web.service;
|
||||
|
||||
import com.rainyhon.swput3.web.dto.req.ProcessesTaskModelReq;
|
||||
import com.rainyhon.swput3.web.model.ProcessesTaskModel;
|
||||
import com.rainyhon.swput3.web.repository.ProcessesTaskRepository;
|
||||
import com.rainyhon.swput3.web.utils.SecurityUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.data.domain.*;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author leim
|
||||
* @date 2025-06-26 17:30
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class ProcessesTaskServiceImpl implements IProcessesTaskService {
|
||||
|
||||
private final ProcessesTaskRepository taskRepository;
|
||||
|
||||
@Override
|
||||
public void add(ProcessesTaskModelReq data) {
|
||||
ProcessesTaskModel processesTaskModel = new ProcessesTaskModel();
|
||||
BeanUtils.copyProperties(data, processesTaskModel);
|
||||
processesTaskModel.setCreateBy(SecurityUtils.getUserName());
|
||||
processesTaskModel.setCreateTime(new Date());
|
||||
taskRepository.save(processesTaskModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void edit(ProcessesTaskModelReq data) {
|
||||
ProcessesTaskModel taskModel = taskRepository.findById(data.getTaskId()).orElseThrow(() -> new RuntimeException("任务数据不存在!"));
|
||||
taskModel.setTaskName(data.getTaskName());
|
||||
taskModel.setProductCode(data.getProductCode());
|
||||
taskModel.setTaskContent(data.getTaskContent());
|
||||
taskModel.setRemark(data.getRemark());
|
||||
taskModel.setProId(data.getProId());
|
||||
taskModel.setUpdateBy(SecurityUtils.getUserName());
|
||||
taskModel.setUpdateTime(new Date());
|
||||
// save方法在数据存在时更新
|
||||
taskRepository.save(taskModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deletes(Long taskId) {
|
||||
taskRepository.deleteById(taskId);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessesTaskModel detail(Long taskId) {
|
||||
Optional<ProcessesTaskModel> taskModel = taskRepository.findById(taskId);
|
||||
return taskModel.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProcessesTaskModel> list(Long proId, String taskName,String productCode) {
|
||||
ProcessesTaskModel probe = new ProcessesTaskModel();
|
||||
if (!ObjectUtils.isEmpty(taskName)) {
|
||||
probe.setTaskName(taskName);
|
||||
}
|
||||
if (!ObjectUtils.isEmpty(proId)) {
|
||||
probe.setProId(proId);
|
||||
}
|
||||
if(!ObjectUtils.isEmpty(productCode)){
|
||||
probe.setProductCode(productCode);
|
||||
}
|
||||
// 2. 创建匹配器 - 关键配置
|
||||
ExampleMatcher matcher = ExampleMatcher.matching()
|
||||
.withMatcher("taskName", match ->
|
||||
match.contains() // 使用包含匹配 (LIKE %value%)
|
||||
.ignoreCase() // 忽略大小写
|
||||
).withIgnoreNullValues(); // 忽略null值
|
||||
Example<ProcessesTaskModel> example = Example.of(probe, matcher);
|
||||
return taskRepository.findAll(example);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.rainyhon.swput3.web.utils;
|
||||
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import com.rainyhon.swput3.web.openapi.openfeign.AccessKeyProperties;
|
||||
import com.rainyhon.swput3.web.openapi.OpenApiParams;
|
||||
import feign.RequestTemplate;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
*/
|
||||
@Slf4j
|
||||
public class RequestInterceptorUtil {
|
||||
|
||||
public static void generateAccessKeyHeader(AccessKeyProperties properties, RequestTemplate template) {
|
||||
Map<String, String> queries = initQueryParams(template);
|
||||
|
||||
int randomInt = RandomUtil.randomInt(1000, 1000000);
|
||||
Map<String, String> headerMap = new HashMap<>();
|
||||
// 公共参数
|
||||
headerMap.put(OpenApiParams.ACCESSKEY_ID_KEY, properties.getAccessKeyId());
|
||||
headerMap.put(OpenApiParams.TIMESTAMP_KEY, getTimestamp());
|
||||
headerMap.put(OpenApiParams.SIGNATURE_NONCE_KEY, String.valueOf(randomInt));
|
||||
// 请求参数
|
||||
HashMap<String, String> map = new HashMap<>(headerMap);
|
||||
map.putAll(queries);
|
||||
String signature;
|
||||
try {
|
||||
signature = SignatureUtils.generate(template.method(), map, properties.getAccessKeySecret());
|
||||
headerMap.put(OpenApiParams.SIGNATURE_KEY, signature);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Map<String, Collection<String>> headers = toCollectionMap(headerMap);
|
||||
template.headers(headers);
|
||||
}
|
||||
|
||||
|
||||
public static String getTimestamp() {
|
||||
// 获取当前时间
|
||||
OffsetDateTime utcTime = OffsetDateTime.now(ZoneOffset.UTC);
|
||||
// 将时间格式化为指定格式的字符串
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(OpenApiParams.TIMESTAMP_FORMAT);
|
||||
return utcTime.format(formatter);
|
||||
}
|
||||
|
||||
public static Map<String, Collection<String>> toCollectionMap(Map<String, String> map) {
|
||||
Map<String, Collection<String>> result = new HashMap<>();
|
||||
for (Map.Entry<String, String> entry : map.entrySet()) {
|
||||
result.put(entry.getKey(), Collections.singletonList(entry.getValue()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Map<String, String> toMap(Map<String, Collection<String>> map) {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
for (Map.Entry<String, Collection<String>> entry : map.entrySet()) {
|
||||
result.put(entry.getKey(), entry.getValue().iterator().next());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Map<String, String> initQueryParams(RequestTemplate template) {
|
||||
Map<String, Collection<String>> queries = template.queries();
|
||||
Map<String, String> decodedQueryParams = new HashMap<>();
|
||||
|
||||
for (Map.Entry<String, Collection<String>> entry : queries.entrySet()) {
|
||||
String encodedKey = entry.getKey();
|
||||
for (String encodedValue : entry.getValue()) {
|
||||
String decodedKey = decodeQueryParam(encodedKey);
|
||||
String decodedValue = decodeQueryParam(encodedValue);
|
||||
decodedQueryParams.put(decodedKey, decodedValue);
|
||||
}
|
||||
}
|
||||
|
||||
return decodedQueryParams;
|
||||
}
|
||||
private static String decodeQueryParam(String value) {
|
||||
try {
|
||||
return URLDecoder.decode(value, "UTF-8");
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
log.warn("Could not decode query value [" + value + "] as 'UTF-8'. " +
|
||||
"Falling back on default encoding: " + ex.getMessage());
|
||||
return URLDecoder.decode(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.rainyhon.swput3.web.utils;
|
||||
|
||||
|
||||
import com.rainyhon.swput3.web.dto.Result;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @create 2020-04-15 17:21
|
||||
*/
|
||||
public class ResultUtil {
|
||||
public static Result success() {
|
||||
return success("");
|
||||
}
|
||||
|
||||
public static Result success(String msg) {
|
||||
Result result = new Result();
|
||||
result.setCode(0);
|
||||
result.setMessage(msg);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Result success(Object data) {
|
||||
Result result = new Result();
|
||||
result.setCode(0);
|
||||
result.setData(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Result successJsonStr(String data) {
|
||||
Result result = new Result();
|
||||
result.setCode(0);
|
||||
result.setData(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Result error(int code, String message) {
|
||||
return error(code, message, null);
|
||||
}
|
||||
|
||||
public static Result error(int code, String message, Object data) {
|
||||
Result result = new Result();
|
||||
result.setCode(code);
|
||||
result.setMessage(message);
|
||||
result.setData(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.rainyhon.swput3.web.utils;
|
||||
|
||||
import io.netty.util.Timeout;
|
||||
import io.netty.util.Timer;
|
||||
import io.netty.util.TimerTask;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @date 2024-01-24 10:56
|
||||
*/
|
||||
@Slf4j
|
||||
public class RetryTimerTask implements TimerTask {
|
||||
/**
|
||||
* 每隔几秒执行一次
|
||||
*/
|
||||
private final long tick;
|
||||
|
||||
/**
|
||||
* 最大重试次数
|
||||
*/
|
||||
private final int retries;
|
||||
|
||||
private int retryTimes = 0;
|
||||
|
||||
private final Runnable task;
|
||||
|
||||
public RetryTimerTask(Runnable task, long tick, int retries) {
|
||||
this.tick = tick;
|
||||
this.retries = retries;
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(Timeout timeout) {
|
||||
try {
|
||||
task.run();
|
||||
} catch (Throwable e) {
|
||||
if ((++retryTimes) >= retries) {
|
||||
// 重试次数超过了设置的值
|
||||
log.error("失败重试次数超过阈值: {},不再重试", retries);
|
||||
} else {
|
||||
log.error("重试失败,继续重试");
|
||||
rePut(timeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 timeout 拿到 timer 实例,重新提交一个定时任务
|
||||
* @param timeout
|
||||
*/
|
||||
private void rePut(Timeout timeout) {
|
||||
if (timeout == null) {
|
||||
return;
|
||||
}
|
||||
Timer timer = timeout.timer();
|
||||
if (timeout.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
timer.newTimeout(timeout.task(), tick, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.rainyhon.swput3.web.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.keycloak.adapters.springsecurity.account.SimpleKeycloakAccount;
|
||||
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@Slf4j
|
||||
public class SecurityUtils {
|
||||
|
||||
|
||||
private SecurityUtils() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token of the current user.
|
||||
*
|
||||
* @return the login of the current user organizationId.
|
||||
*/
|
||||
public static KeycloakAuthenticationToken getPrincipe() {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
return (KeycloakAuthenticationToken) authentication;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token of the current user Id.
|
||||
*
|
||||
* @return the login of the current user organizationId.
|
||||
*/
|
||||
public static String getUserId() {
|
||||
SimpleKeycloakAccount account = getAccount();
|
||||
return account.getKeycloakSecurityContext().getToken().getSubject();
|
||||
}
|
||||
|
||||
public static String getUserName() {
|
||||
SimpleKeycloakAccount account = getAccount();
|
||||
return account.getKeycloakSecurityContext().getToken().getPreferredUsername();
|
||||
}
|
||||
|
||||
|
||||
public static SimpleKeycloakAccount getAccount() {
|
||||
KeycloakAuthenticationToken principe = getPrincipe();
|
||||
return (SimpleKeycloakAccount) principe.getDetails();
|
||||
}
|
||||
|
||||
public static boolean isAdmin() {
|
||||
return getAccount().getRoles().contains("admin");
|
||||
}
|
||||
|
||||
|
||||
public static String getClient() {
|
||||
SimpleKeycloakAccount account = getAccount();
|
||||
String client = account.getKeycloakSecurityContext().getToken().getIssuedFor();
|
||||
return client;
|
||||
}
|
||||
|
||||
public static Set<String> getRealmRoles() {
|
||||
SimpleKeycloakAccount account = getAccount();
|
||||
Set<String> roles = account.getKeycloakSecurityContext().getToken().getRealmAccess().getRoles();
|
||||
return roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前令牌内容
|
||||
*
|
||||
* @return String 令牌内容
|
||||
*/
|
||||
public static String getCurrentTokenValue() {
|
||||
String token = null;
|
||||
try {
|
||||
token = getAccount().getKeycloakSecurityContext().getTokenString();
|
||||
} catch (Exception e) {
|
||||
log.warn("can not get OAuth2AuthenticationDetails;{}", e.getMessage());
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.rainyhon.swput3.web.utils;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
|
||||
/**
|
||||
* 服务端API签名
|
||||
*
|
||||
* @author cwp
|
||||
*/
|
||||
public class SignatureUtils {
|
||||
|
||||
private final static String CHARSET_UTF8 = "utf8";
|
||||
private final static String ALGORITHM = "HmacSHA1";
|
||||
private final static String SEPARATOR = "&";
|
||||
|
||||
public static Map<String, String> splitQueryString(String url)
|
||||
throws URISyntaxException, UnsupportedEncodingException {
|
||||
URI uri = new URI(url);
|
||||
String query = uri.getQuery();
|
||||
final String[] pairs = query.split("&");
|
||||
TreeMap<String, String> queryMap = new TreeMap<String, String>();
|
||||
for (String pair : pairs) {
|
||||
final int idx = pair.indexOf("=");
|
||||
final String key = idx > 0 ? pair.substring(0, idx) : pair;
|
||||
if (!queryMap.containsKey(key)) {
|
||||
queryMap.put(key, URLDecoder.decode(pair.substring(idx + 1), CHARSET_UTF8));
|
||||
}
|
||||
}
|
||||
return queryMap;
|
||||
}
|
||||
|
||||
public static String generate(String method, Map<String, String> parameter, String accessKeySecret)
|
||||
throws Exception {
|
||||
String signString = generateSignString(method, parameter);
|
||||
byte[] signBytes = hmacSHA1Signature(accessKeySecret + "&", signString);
|
||||
String signature = newStringByBase64(signBytes);
|
||||
if ("POST".equals(method)) {
|
||||
return signature;
|
||||
}
|
||||
return URLEncoder.encode(signature, "UTF-8");
|
||||
}
|
||||
|
||||
public static String generateSignString(String httpMethod, Map<String, String> parameter) throws IOException {
|
||||
TreeMap<String, String> sortParameter = new TreeMap<String, String>();
|
||||
sortParameter.putAll(parameter);
|
||||
String canonicalizedQueryString = UrlUtil.generateQueryString(sortParameter, true);
|
||||
if (null == httpMethod) {
|
||||
throw new RuntimeException("httpMethod can not be empty");
|
||||
}
|
||||
StringBuilder stringToSign = new StringBuilder();
|
||||
stringToSign.append(httpMethod).append(SEPARATOR);
|
||||
stringToSign.append(percentEncode("/")).append(SEPARATOR);
|
||||
stringToSign.append(percentEncode(canonicalizedQueryString));
|
||||
return stringToSign.toString();
|
||||
}
|
||||
|
||||
public static String percentEncode(String value) {
|
||||
try {
|
||||
return value == null ? null
|
||||
: URLEncoder.encode(value, CHARSET_UTF8).replace("+", "%20").replace("*", "%2A").replace("%7E",
|
||||
"~");
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static byte[] hmacSHA1Signature(String secret, String baseString) throws Exception {
|
||||
if (StringUtils.isEmpty(secret)) {
|
||||
throw new IOException("secret can not be empty");
|
||||
}
|
||||
if (StringUtils.isEmpty(baseString)) {
|
||||
return null;
|
||||
}
|
||||
Mac mac = Mac.getInstance(ALGORITHM);
|
||||
SecretKeySpec keySpec = new SecretKeySpec(secret.getBytes(CHARSET_UTF8), ALGORITHM);
|
||||
mac.init(keySpec);
|
||||
return mac.doFinal(baseString.getBytes(CHARSET_UTF8));
|
||||
}
|
||||
|
||||
public static String newStringByBase64(byte[] bytes) throws UnsupportedEncodingException {
|
||||
if (bytes == null || bytes.length == 0) {
|
||||
return null;
|
||||
}
|
||||
return new String(Base64.encodeBase64(bytes, false), CHARSET_UTF8);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.rainyhon.swput3.web.utils;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* URL处理类
|
||||
*
|
||||
* @author cwp
|
||||
*/
|
||||
public class UrlUtil {
|
||||
|
||||
private final static String CHARSET_UTF8 = "utf8";
|
||||
|
||||
public static String urlEncode(String url) {
|
||||
if (!StringUtils.isEmpty(url)) {
|
||||
try {
|
||||
url = URLEncoder.encode(url, "UTF-8");
|
||||
} catch (Exception e) {
|
||||
System.out.println("Url encode error:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
public static String generateQueryString(Map<String, String> params, boolean isEncodeKV) {
|
||||
StringBuilder canonicalizedQueryString = new StringBuilder();
|
||||
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||
if (isEncodeKV) {
|
||||
canonicalizedQueryString.append(percentEncode(entry.getKey())).append("=")
|
||||
.append(percentEncode(entry.getValue())).append("&");
|
||||
} else {
|
||||
canonicalizedQueryString.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
|
||||
}
|
||||
}
|
||||
if (canonicalizedQueryString.length() > 1) {
|
||||
canonicalizedQueryString.setLength(canonicalizedQueryString.length() - 1);
|
||||
}
|
||||
return canonicalizedQueryString.toString();
|
||||
}
|
||||
|
||||
public static String percentEncode(String value) {
|
||||
try {
|
||||
// 使用URLEncoder.encode编码后,将"+","*","%7E"做替换即满足API规定的编码规范。
|
||||
return value == null ? null
|
||||
: URLEncoder.encode(value, CHARSET_UTF8).replace("+", "%20").replace("*", "%2A").replace("%7E",
|
||||
"~");
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
123
web-server/src/main/resources/application.yml
Normal file
123
web-server/src/main/resources/application.yml
Normal file
@@ -0,0 +1,123 @@
|
||||
server:
|
||||
port: 38104
|
||||
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:mysql://${MYSQL_URL:172.23.255.62:3306}/rainyhon?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=GMT%2B8&useSSL=false
|
||||
username: ${MYSQL_USER:root}
|
||||
password: ${MYSQL_PASSWORD:yelink123}
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
hikari:
|
||||
max-lifetime: 600000
|
||||
flyway:
|
||||
enabled: true
|
||||
locations: classpath:db/migration
|
||||
out-of-order: true
|
||||
|
||||
redis:
|
||||
database: 12
|
||||
host: ${REDIS_URL:172.23.255.62}
|
||||
port: 6379
|
||||
jedis:
|
||||
pool:
|
||||
min-idle: 8
|
||||
max-idle: 500
|
||||
max-active: 2000
|
||||
max-wait: 10000
|
||||
timeout: 5000
|
||||
|
||||
main:
|
||||
allow-bean-definition-overriding: true
|
||||
jackson:
|
||||
date-format: yyyy-MM-dd HH:mm:ss
|
||||
time-zone: GMT+8
|
||||
default-property-inclusion: non_null
|
||||
jpa:
|
||||
open-in-view: true
|
||||
hibernate:
|
||||
ddl-auto:jpa: validate
|
||||
|
||||
kafka:
|
||||
listener:
|
||||
concurrency: 1
|
||||
missing-topics-fatal: false
|
||||
# 设置批量消费
|
||||
# type=batch
|
||||
bootstrap-servers: ${KAFKACLOUD_URL:172.23.255.62:9092}
|
||||
producer:
|
||||
retries: 3
|
||||
# 应答级别:多少个分区副本备份完成时向生产者发送ack确认(可选0、1、all/-1)
|
||||
acks: 1
|
||||
# 批量大小
|
||||
batch-size: 16384
|
||||
# 提交延时
|
||||
properties:
|
||||
linger:
|
||||
ms: 0
|
||||
# 当生产端积累的消息达到batch-size或接收到消息linger.ms后,生产者就会将消息提交给kafka
|
||||
# linger.ms为0表示每接收到一条消息就提交给kafka,这时候batch-size其实就没用了
|
||||
# 生产端缓冲区大小
|
||||
buffer-memory: 33554432
|
||||
# Kafka提供的序列化和反序列化类
|
||||
key-serializer: org.apache.kafka.common.serialization.StringSerializer
|
||||
value-serializer: org.apache.kafka.common.serialization.StringSerializer
|
||||
###########【初始化消费者配置】###########
|
||||
# 默认的消费组ID
|
||||
consumer:
|
||||
# 是否自动提交offset
|
||||
enable-auto-commit: true
|
||||
# 提交offset延时(接收到消息后多久提交offset)
|
||||
auto-commit-interval: 1000
|
||||
auto-offset-reset: latest
|
||||
properties:
|
||||
group.id: consumer-group-example
|
||||
# 消费会话超时时间(超过这个时间consumer没有发送心跳,就会触发rebalance操作)
|
||||
session:
|
||||
timeout.ms: 120000
|
||||
# 消费请求超时时间
|
||||
request:
|
||||
timeout.ms: 180000
|
||||
# Kafka提供的序列化和反序列化类
|
||||
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
|
||||
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
|
||||
# 批量消费每次最多消费多少条消息
|
||||
# max-poll-records=50
|
||||
|
||||
doc:
|
||||
enable: true
|
||||
title: ${spring.application.name}文档
|
||||
base-package: com.rainyhon.swput3.web.controller
|
||||
description: swput3 doc
|
||||
name: swput3
|
||||
email: swput3@example.com
|
||||
url: https://example.com
|
||||
version: 1.0-SNAPSHOT
|
||||
|
||||
app:
|
||||
security:
|
||||
enable: true
|
||||
anon-uris: /v2/api-docs-ext,/actuator/**,/swagger-ui.html,/webjars/**,/swagger-resources/**,/v2/api-docs/**,/call/service/api
|
||||
access-key-id: ${ACCESS_KEY:DJPE3QJ07OXPAFFJGORO}
|
||||
access-key-secret: ${ACCESS_KEY_SECRET:sFjISWRruGQjJLCZYiEwomqEpBCNiE3HBaYixZFl}
|
||||
|
||||
|
||||
keycloak:
|
||||
auth-server-url: ${AUTH_URL:http://172.23.255.62:8411/auth}
|
||||
realm: ${KEYCLOAK_REALM:edge}
|
||||
principal-attribute: preferred_username
|
||||
use-resource-role-mappings: false
|
||||
ssl-required: none
|
||||
enable-basic-auth: true
|
||||
resource: ${CLIENT_ID:iot}
|
||||
credentials:
|
||||
secret: ${CLIENT_SECRET:de512cc3-1208-4c3c-a389-8637774eee56}
|
||||
enabled: ${app.security.enable}
|
||||
minio:
|
||||
bucket: ${MINIO_BUCKET:oss-public}
|
||||
access-key: ${MINIO_ACCESS_KEY:miniouser}
|
||||
secret-key: ${MINIO_SECRET_KEY:yelink123}
|
||||
endpoint: ${MINIO_ENDPOINT:http://172.23.255.62:28301}
|
||||
application-name: ${MINIO_PREFIX_NAME:example}
|
||||
|
||||
openapi:
|
||||
edge-gateway-url: ${EDGE_GATEWAY_URL:https://172.23.255.62:8301}
|
||||
15
web-server/src/main/resources/bootstrap.yml
Normal file
15
web-server/src/main/resources/bootstrap.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
spring:
|
||||
application:
|
||||
name: factory-digital-app-swput3
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: ${NACOS_URL:172.23.255.62:8001}
|
||||
metadata:
|
||||
version: ${VERSION}
|
||||
ip: ${LOCAL_IP:172.23.226.250}
|
||||
port: ${server.port}
|
||||
|
||||
logging:
|
||||
level:
|
||||
com.alibaba.nacos: warn
|
||||
10
web-server/src/main/resources/db.migration/V1.0.0__init.sql
Normal file
10
web-server/src/main/resources/db.migration/V1.0.0__init.sql
Normal file
@@ -0,0 +1,10 @@
|
||||
create table t_demo
|
||||
(
|
||||
id int auto_increment,
|
||||
username varchar(64) not null,
|
||||
address varchar(128) null,
|
||||
created_at timestamp default CURRENT_TIMESTAMP null,
|
||||
updated_at timestamp default CURRENT_TIMESTAMP null,
|
||||
constraint t_demo_pk primary key (id)
|
||||
);
|
||||
|
||||
52
web-server/src/main/resources/logback.xml
Normal file
52
web-server/src/main/resources/logback.xml
Normal file
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration debug="false">
|
||||
|
||||
<!--设置存储路径变量-->
|
||||
<property name="LOG_HOME" value="./logs/"/>
|
||||
|
||||
<!-- ConsoleAppender:把日志输出到控制台 -->
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<!--设置输出格式-->
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} %line - %msg%n</pattern>
|
||||
<!--设置编码-->
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!--文件输出,时间窗口滚动-->
|
||||
<appender name="timeFileOutput" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
|
||||
<!--日志名,指定最新的文件名,其他文件名使用FileNamePattern -->
|
||||
<File>${LOG_HOME}/rainyhon-xzt.log</File>
|
||||
<append>true</append>
|
||||
<!--文件滚动模式-->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!--日志文件输出的文件名,可设置文件类型为gz,开启文件压缩-->
|
||||
<FileNamePattern>${LOG_HOME}/timeFile/rainyhon-xzt.%d{yyyy-MM-dd}.%i.gz</FileNamePattern>
|
||||
<!--日志文件保留天数-->
|
||||
<MaxHistory>30</MaxHistory>
|
||||
<!--按大小分割同一天的-->
|
||||
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<maxFileSize>100MB</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
<totalSizeCap>2048MB</totalSizeCap>
|
||||
</rollingPolicy>
|
||||
|
||||
<!--输出格式-->
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
|
||||
<!--设置编码-->
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
<!-- 控制台输出日志级别 -->
|
||||
<root level="info">
|
||||
<appender-ref ref="console"/>
|
||||
<appender-ref ref="timeFileOutput"/>
|
||||
</root>
|
||||
|
||||
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user