init
This commit is contained in:
76
.gitignore
vendored
Normal file
76
.gitignore
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
*.imi
|
||||
*.lst
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
.idea
|
||||
*.iml
|
||||
.settings/
|
||||
.project
|
||||
.classpath
|
||||
target/
|
||||
logs/
|
||||
|
||||
.DS_Store
|
||||
node_modules/
|
||||
dist/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
**/*.log
|
||||
|
||||
tests/**/coverage/
|
||||
tests/e2e/reports
|
||||
selenium-debug.log
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.local
|
||||
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
# Build and Release Folders
|
||||
bin-debug/
|
||||
bin-release/
|
||||
[Oo]bj/
|
||||
[Bb]in/
|
||||
|
||||
# Other files and folders
|
||||
.settings/
|
||||
|
||||
# Executables
|
||||
*.swf
|
||||
*.air
|
||||
*.ipa
|
||||
*.apk
|
||||
|
||||
# Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties`
|
||||
# should NOT be excluded as they contain compiler settings and other important
|
||||
# information for Eclipse / Flash Builder.
|
||||
36
README.en.md
Normal file
36
README.en.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# sfbx-cloud
|
||||
|
||||
#### Description
|
||||
四方保险
|
||||
|
||||
#### Software Architecture
|
||||
Software architecture description
|
||||
|
||||
#### Installation
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
#### Instructions
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
#### Contribution
|
||||
|
||||
1. Fork the repository
|
||||
2. Create Feat_xxx branch
|
||||
3. Commit your code
|
||||
4. Create Pull Request
|
||||
|
||||
|
||||
#### Gitee Feature
|
||||
|
||||
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
|
||||
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
|
||||
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
|
||||
4. The most valuable open source project [GVP](https://gitee.com/gvp)
|
||||
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
|
||||
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
|
||||
37
README.md
Normal file
37
README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# sfbx-cloud
|
||||
|
||||
#### 介绍
|
||||
四方保险
|
||||
|
||||
#### 软件架构
|
||||
软件架构说明
|
||||
|
||||
|
||||
#### 安装教程
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
#### 使用说明
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
#### 参与贡献
|
||||
|
||||
1. Fork 本仓库
|
||||
2. 新建 Feat_xxx 分支
|
||||
3. 提交代码
|
||||
4. 新建 Pull Request
|
||||
|
||||
|
||||
#### 特技
|
||||
|
||||
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
|
||||
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
|
||||
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
|
||||
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
|
||||
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
|
||||
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
|
||||
125
docker-compose.yml
Normal file
125
docker-compose.yml
Normal file
@@ -0,0 +1,125 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
#mysql数据库脚本
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
container_name: mysql
|
||||
restart: always
|
||||
ports:
|
||||
- 3306:3306
|
||||
volumes:
|
||||
- ./mysql/mydir:/mydir
|
||||
- ./mysql/data:/var/lib/mysql
|
||||
- ./mysql/conf/my.cnf:/etc/my.cnf
|
||||
- ./mysql/source:/docker-entrypoint-initdb.d/
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: pass
|
||||
networks:
|
||||
extnetwork:
|
||||
ipv4_address: 172.21.0.2
|
||||
#nacos服务脚本
|
||||
nacos:
|
||||
image: nacos/nacos-server:2.0.2
|
||||
container_name: nacos
|
||||
restart: always
|
||||
ports:
|
||||
- "8848:8848"
|
||||
environment:
|
||||
SPRING_DATASOURCE_PLATFORM: mysql #数据源平台 仅支持mysql或不保存empty
|
||||
MODE: standalone
|
||||
MYSQL_SERVICE_HOST: mysql
|
||||
MYSQL_SERVICE_DB_NAME: nacos
|
||||
MYSQL_SERVICE_PORT: 3306
|
||||
MYSQL_SERVICE_USER: root
|
||||
MYSQL_SERVICE_PASSWORD: pass
|
||||
NACOS_APPLICATION_PORT: 8848
|
||||
JVM_XMS: 512m
|
||||
JVM_MMS: 256m
|
||||
JVM_XMN: 128m
|
||||
networks:
|
||||
extnetwork:
|
||||
ipv4_address: 172.21.0.3
|
||||
depends_on:
|
||||
- mysql
|
||||
#seata服务脚本
|
||||
seata-server:
|
||||
image: seataio/seata-server:1.5.2
|
||||
container_name: seata-server
|
||||
restart: always
|
||||
ports:
|
||||
- "9200:9200"
|
||||
- "7091:7091"
|
||||
- "8091:8091"
|
||||
volumes:
|
||||
- ./seata-server:/seata-server/resources
|
||||
environment:
|
||||
SEATA_IP: 192.168.12.129
|
||||
SEATA_PORT: 9200
|
||||
networks:
|
||||
extnetwork:
|
||||
ipv4_address: 172.21.0.4
|
||||
depends_on:
|
||||
- nacos
|
||||
#rabbitmq脚本
|
||||
rabbitmq:
|
||||
image: rabbitmq:3.8.3-management
|
||||
container_name: rabbitmq
|
||||
restart: always
|
||||
ports:
|
||||
- 15672:15672
|
||||
- 5672:5672
|
||||
volumes:
|
||||
- ./rabbitmq/data:/var/lib/rabbitmq
|
||||
environment:
|
||||
RABBITMQ_DEFAULT_USER: admin
|
||||
RABBITMQ_DEFAULT_PASS: pass
|
||||
networks:
|
||||
extnetwork:
|
||||
ipv4_address: 172.21.0.5
|
||||
xxl-job:
|
||||
image: xuxueli/xxl-job-admin:2.1.2
|
||||
container_name: xxl-job-admin
|
||||
restart: always
|
||||
ports:
|
||||
- 8280:8080
|
||||
volumes:
|
||||
- ./xxl-job/data:/data/applogs
|
||||
environment:
|
||||
PARAMS: "--spring.datasource.url=jdbc:mysql://mysql:3306/xxl-job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai --spring.datasource.username=root --spring.datasource.password=pass"
|
||||
networks:
|
||||
extnetwork:
|
||||
ipv4_address: 172.21.0.9
|
||||
depends_on:
|
||||
- mysql
|
||||
redis:
|
||||
image: redis:5.0.0
|
||||
container_name: redis
|
||||
restart: always
|
||||
command: redis-server --requirepass pass
|
||||
ports:
|
||||
- 6379:6379
|
||||
volumes:
|
||||
- ./redis/data:/data
|
||||
networks:
|
||||
extnetwork:
|
||||
ipv4_address: 172.21.0.10
|
||||
influxdb:
|
||||
image: influxdb:1.8.0
|
||||
container_name: influxdb
|
||||
restart: always
|
||||
ports:
|
||||
- 9083:8083
|
||||
- 8086:8086
|
||||
- 8088:8088
|
||||
privileged: true
|
||||
volumes:
|
||||
- ./influxdb/data/influxdb:/var/lib/influxdb
|
||||
- ./influxdb/config/influxdb.conf:/etc/influxdb/influxdb.conf
|
||||
# docker容器内网地址
|
||||
networks:
|
||||
extnetwork:
|
||||
name: extnetwork
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 172.21.0.0/16
|
||||
473
pom.xml
Normal file
473
pom.xml
Normal file
@@ -0,0 +1,473 @@
|
||||
<?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>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>sfbx-cloud</artifactId>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
<modules>
|
||||
<!--数据字典模块-->
|
||||
<module>sfbx-dict</module>
|
||||
<!--基础骨架模块-->
|
||||
<module>sfbx-framework</module>
|
||||
<!--文件处理模块-->
|
||||
<module>sfbx-file</module>
|
||||
<!--埋点处理模块-->
|
||||
<module>sfbx-points</module>
|
||||
<!--交易处理模块-->
|
||||
<module>sfbx-trade</module>
|
||||
<!--权限处理模块-->
|
||||
<module>sfbx-security</module>
|
||||
<!--短信处理模块-->
|
||||
<module>sfbx-sms</module>
|
||||
<!--任务监听处理模块-->
|
||||
<module>sfbx-task</module>
|
||||
<!--保险业务模块-->
|
||||
<module>sfbx-insurance</module>
|
||||
<module>sfbx-gateway</module>
|
||||
<module>sfbx-apache-httpclient</module>
|
||||
<!--规则引擎模块-->
|
||||
<module>sfbx-rule</module>
|
||||
|
||||
</modules>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>sfbx-cloud</name>
|
||||
<url>http://www.example.com</url>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<!--easy.version-->
|
||||
<sfbx-cloud.version>2.0-SNAPSHOT</sfbx-cloud.version>
|
||||
<!--spring-cloud版本-->
|
||||
<spring-cloud.version>2021.0.6</spring-cloud.version>
|
||||
<!--spring-cloud-alibaba版本-->
|
||||
<spring-cloud-alibaba.version>2021.0.1.0</spring-cloud-alibaba.version>
|
||||
<!--spring-cloud-stream版本-->
|
||||
<stream-rabbit.version>3.1.0</stream-rabbit.version>
|
||||
<!--spring-boot版本-->
|
||||
<spring.boot.version>2.7.10</spring.boot.version>
|
||||
<!--mybatis-plus版本-->
|
||||
<mybatis-plus-boot-starter.version>3.4.0</mybatis-plus-boot-starter.version>
|
||||
<!--druid的springboot版本配置-->
|
||||
<druid-spring-boot-starter>1.1.20</druid-spring-boot-starter>
|
||||
<!--druid版本配置-->
|
||||
<druid.version>1.1.22</druid.version>
|
||||
<!--knife4j版本支持-->
|
||||
<knife4j.version>3.0.3</knife4j.version>
|
||||
<!--orika 拷贝工具 -->
|
||||
<orika-core.version>1.5.4</orika-core.version>
|
||||
<!--hutool工具 -->
|
||||
<hutool.version>5.8.10</hutool.version>
|
||||
<!--lang3-->
|
||||
<commons.lang3.version>3.8.1</commons.lang3.version>
|
||||
<!--kryo-->
|
||||
<kryo.version>4.0.2</kryo.version>
|
||||
<!--阿里easy支付-->
|
||||
<alipay.easysdk.version>2.2.0</alipay.easysdk.version>
|
||||
<!--阿里通用支付-->
|
||||
<alipay-sdk-java.version>4.38.61.ALL</alipay-sdk-java.version>
|
||||
<!--微信支付-->
|
||||
<wechatpay.version>0.4.8</wechatpay.version>
|
||||
<!--guava版本 -->
|
||||
<guava.version>23.0</guava.version>
|
||||
<!--fastjson版本-->
|
||||
<fastjson.version>1.2.73</fastjson.version>
|
||||
<!--sharding-jdbc版本-->
|
||||
<sharding-jdbc.version>4.1.1</sharding-jdbc.version>
|
||||
<!--redisson版本-->
|
||||
<redisson-spring-boot>3.11.2</redisson-spring-boot>
|
||||
<!--hessian协议支持-->
|
||||
<hessian.version>4.0.7</hessian.version>
|
||||
<!--xxl-job-->
|
||||
<xxl-job-core.version>2.1.2</xxl-job-core.version>
|
||||
<!--阿里当前线程,解决父子线程间参数传递问题-->
|
||||
<transmittable.version>2.12.2</transmittable.version>
|
||||
<!-- 七牛 sdk 版本 -->
|
||||
<qiniu.version>7.7.0</qiniu.version>
|
||||
<!-- mysql数据库版本 -->
|
||||
<mysql.version>8.0.19</mysql.version>
|
||||
</properties>
|
||||
|
||||
<!--声明jar-->
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<!--基础模块-工具包-->
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-commons</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<!--基础模块-redis支持-->
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-redis</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<!-- 外部数据源 -->
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-out-interface</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<!-- 数据字典 -->
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>dict-interface</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<!-- ai模块 -->
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-ai</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<!-- rule模块 -->
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-rule-base</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<!--基础模块-knife4j-web支持-->
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-knife4j-web</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>sfbx-apache-httpclient</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<!--基础模块-knife4j-gateway支持-->
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-knife4j-gateway</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<!--基础模块-gateway支持-->
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-gateway</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<!--基础模块-web支持-->
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-web</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<!--基础模块-feign支持-->
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-feign</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<!--基础模块-seata支持-->
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-seata</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<!--基础模块-rabbitmq支持-->
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-rabbitmq</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<!--基础模块-mybatis支持-->
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-mybatis-plus</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<!--基础模块-task-executor支持-->
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-task-executor</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<!--基础模块-xxl-job支持-->
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-xxl-job</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<!--基础模块-influxdb支持-->
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-influxdb</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<!--基础模块-权限接口支持-->
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>security-interface</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>rule-client</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<!--基础模块-文件接口支持-->
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>file-interface</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<!--基础模块-短信接口支持-->
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>sms-interface</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<!--基础模块-数据埋点支持-->
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>points-interface</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<!--基础模块-交易模块支持-->
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>trade-interface</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<!--保险模块-service支持-->
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>insurance-service</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<!--基础模块-保险模块支持-->
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>insurance-interface</artifactId>
|
||||
<version>${sfbx-cloud.version}</version>
|
||||
</dependency>
|
||||
<!---springcould主配置-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>${spring-cloud.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<!---spring-cloud-alibaba主配置-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
|
||||
<version>${spring-cloud-alibaba.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<!---springboot主配置-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<!-- sharding-jdbc -->
|
||||
<dependency>
|
||||
<groupId>org.apache.shardingsphere</groupId>
|
||||
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
|
||||
<version>${sharding-jdbc.version}</version>
|
||||
</dependency>
|
||||
<!-- 使用XA事务时,需要引入此模块 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.shardingsphere</groupId>
|
||||
<artifactId>sharding-transaction-xa-core</artifactId>
|
||||
<version>${sharding-jdbc.version}</version>
|
||||
</dependency>
|
||||
<!-- 使用BASE事务时,需要引入此模块 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.shardingsphere</groupId>
|
||||
<artifactId>sharding-transaction-base-seata-at</artifactId>
|
||||
<version>${sharding-jdbc.version}</version>
|
||||
</dependency>
|
||||
<!--工具包-->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>${hutool.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.zxing</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>3.3.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${commons.lang3.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>${fastjson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ma.glasnost.orika</groupId>
|
||||
<artifactId>orika-core</artifactId>
|
||||
<version>${orika-core.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>${guava.version}</version>
|
||||
</dependency>
|
||||
<!--knife4j版接口文档 访问/doc.html -->
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-spring-boot-starter</artifactId>
|
||||
<version>${knife4j.version}</version>
|
||||
</dependency>
|
||||
<!--Mysql支持-->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>${mysql.version}</version>
|
||||
</dependency>
|
||||
<!--druid的springboot配置-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid-spring-boot-starter</artifactId>
|
||||
<version>${druid-spring-boot-starter}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid</artifactId>
|
||||
<version>${druid.version}</version>
|
||||
</dependency>
|
||||
<!--springboot关于mybatis-plus-->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>${mybatis-plus-boot-starter.version}</version>
|
||||
</dependency>
|
||||
<!--代码生成器模板引擎 相关依赖-->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-generator</artifactId>
|
||||
<version>${mybatis-plus-boot-starter.version}</version>
|
||||
</dependency>
|
||||
<!-- 阿里easy支付 -->
|
||||
<dependency>
|
||||
<groupId>com.alipay.sdk</groupId>
|
||||
<artifactId>alipay-easysdk</artifactId>
|
||||
<version>${alipay.easysdk.version}</version>
|
||||
</dependency>
|
||||
<!-- 阿里通用版本支付- -->
|
||||
<dependency>
|
||||
<groupId>com.alipay.sdk</groupId>
|
||||
<artifactId>alipay-sdk-java</artifactId>
|
||||
<version>${alipay-sdk-java.version}</version>
|
||||
</dependency>
|
||||
<!-- 微信支付 -->
|
||||
<dependency>
|
||||
<groupId>com.github.wechatpay-apiv3</groupId>
|
||||
<artifactId>wechatpay-apache-httpclient</artifactId>
|
||||
<version>${wechatpay.version}</version>
|
||||
</dependency>
|
||||
<!--redis缓存客户端-->
|
||||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson-spring-boot-starter</artifactId>
|
||||
<version>${redisson-spring-boot}</version>
|
||||
</dependency>
|
||||
<!-- xxl-job-core -->
|
||||
<dependency>
|
||||
<groupId>com.xuxueli</groupId>
|
||||
<artifactId>xxl-job-core</artifactId>
|
||||
<version>${xxl-job-core.version}</version>
|
||||
</dependency>
|
||||
<!--使用spring-cloud-starter-stream对rabbitmq的支持-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
|
||||
<version>${stream-rabbit.version}</version>
|
||||
</dependency>
|
||||
<!--百度云短信-->
|
||||
<dependency>
|
||||
<groupId>com.baidubce</groupId>
|
||||
<artifactId>bce-java-sdk</artifactId>
|
||||
<version>0.10.119</version>
|
||||
</dependency>
|
||||
<!--阿里云短信-->
|
||||
<dependency>
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>dysmsapi20170525</artifactId>
|
||||
<version>2.0.24</version>
|
||||
</dependency>
|
||||
<!--腾讯云短信-->
|
||||
<dependency>
|
||||
<groupId>com.tencentcloudapi</groupId>
|
||||
<artifactId>tencentcloud-sdk-java</artifactId>
|
||||
<version>3.1.270</version>
|
||||
</dependency>
|
||||
<!-- 阿里当前线程,解决父子线程间参数传递问题 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>transmittable-thread-local</artifactId>
|
||||
<version>${transmittable.version}</version>
|
||||
</dependency>
|
||||
<!-- 七牛 sdk -->
|
||||
<dependency>
|
||||
<groupId>com.qiniu</groupId>
|
||||
<artifactId>qiniu-java-sdk</artifactId>
|
||||
<version>${qiniu.version}</version>
|
||||
</dependency>
|
||||
<!---spring-cloud-alibaba-oss配置-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alicloud-oss</artifactId>
|
||||
<version>2.2.0.RELEASE</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.aliyun.oss</groupId>
|
||||
<artifactId>aliyun-sdk-oss</artifactId>
|
||||
<version>3.10.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!--jdk插件-->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.1</version>
|
||||
<configuration>
|
||||
<source>11</source>
|
||||
<target>11</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!--springboot的打包插件-->
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>2.3.0.RELEASE</version>
|
||||
</plugin>
|
||||
|
||||
<!-- maven-surefire-plugin 测试包 -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.4.2</version>
|
||||
<configuration>
|
||||
<!-- 全局是否执行maven生命周期中的测试:是否跳过测试 -->
|
||||
<skipTests>true</skipTests>
|
||||
<!-- 解决测试中文乱码-->
|
||||
<forkMode>once</forkMode>
|
||||
<argLine>-Dfile.encoding=UTF-8</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
63
sfbx-apache-httpclient/pom.xml
Normal file
63
sfbx-apache-httpclient/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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>sfbx-cloud</artifactId>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<!--三方sdk接口封装-->
|
||||
<artifactId>sfbx-apache-httpclient</artifactId>
|
||||
<name>sfbx-apache-httpclient</name>
|
||||
<!-- FIXME change it to the project's website -->
|
||||
<url>http://www.example.com</url>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpmime</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.itheima.sfbx;
|
||||
|
||||
import com.itheima.sfbx.auth.*;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.execchain.ClientExecChain;
|
||||
|
||||
/**
|
||||
* @ClassName BoleeHttpClientBuilder.java
|
||||
* @Description 保乐三方请求HttpClient对象构建
|
||||
*/
|
||||
public class BoleeHttpClientBuilder extends HttpClientBuilder {
|
||||
|
||||
//系统相关属性
|
||||
private static final String OS = System.getProperty("os.name") + "/" + System.getProperty("os.version");
|
||||
private static final String VERSION = System.getProperty("java.version");
|
||||
|
||||
//凭证构建
|
||||
private Credentials credentials;
|
||||
|
||||
//校验者
|
||||
private Validator validator;
|
||||
|
||||
|
||||
public static BoleeHttpClientBuilder create() {
|
||||
return new BoleeHttpClientBuilder();
|
||||
}
|
||||
|
||||
private BoleeHttpClientBuilder() {
|
||||
String userAgent = String.format("sfbx-Apache-HttpClient/%s (%s) Java/%s", this.getClass().getPackage().getImplementationVersion(), OS, VERSION == null ? "Unknown" : VERSION);
|
||||
this.setUserAgent(userAgent);
|
||||
}
|
||||
|
||||
public BoleeHttpClientBuilder withCredentials(String appId,String privateKey) {
|
||||
this.credentials = new BoleeCredentials(appId, new PrivateKeySigner(privateKey));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public BoleeHttpClientBuilder withValidator(String publicKey) {
|
||||
this.validator = new BoleeValidator(new PublicKeyVerifier(publicKey));
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloseableHttpClient build() {
|
||||
if (this.credentials == null) {
|
||||
throw new IllegalArgumentException("缺少身份认证信息");
|
||||
} else if (this.validator == null) {
|
||||
throw new IllegalArgumentException("缺少签名验证信息");
|
||||
} else {
|
||||
return super.build();
|
||||
}
|
||||
}
|
||||
|
||||
protected ClientExecChain decorateProtocolExec(ClientExecChain requestExecutor) {
|
||||
return new DecorateClientExecChain(this.credentials, this.validator, requestExecutor);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.itheima.sfbx;
|
||||
|
||||
import org.apache.http.client.methods.HttpRequestWrapper;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @ClassName Credentials.java
|
||||
* @Description 认证凭证创建处理
|
||||
*/
|
||||
public interface Credentials {
|
||||
|
||||
/***
|
||||
* @description 创建请求认证凭证
|
||||
* @param request 请求对象
|
||||
* @return: java.lang.String 凭证字符串
|
||||
*/
|
||||
String createCredentials(HttpRequestWrapper request) throws IOException;
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.itheima.sfbx;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpException;
|
||||
import org.apache.http.HttpRequest;
|
||||
import org.apache.http.StatusLine;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
|
||||
import org.apache.http.client.methods.HttpExecutionAware;
|
||||
import org.apache.http.client.methods.HttpRequestWrapper;
|
||||
import org.apache.http.client.protocol.HttpClientContext;
|
||||
import org.apache.http.conn.routing.HttpRoute;
|
||||
import org.apache.http.entity.BufferedHttpEntity;
|
||||
import org.apache.http.entity.ByteArrayEntity;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.execchain.ClientExecChain;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static org.apache.http.HttpHeaders.AUTHORIZATION;
|
||||
import static org.apache.http.HttpStatus.SC_MULTIPLE_CHOICES;
|
||||
import static org.apache.http.HttpStatus.SC_OK;
|
||||
|
||||
/**
|
||||
* @ClassName SignatureExec.java
|
||||
* @Description 请求执行者
|
||||
*/
|
||||
@Slf4j
|
||||
public class DecorateClientExecChain implements ClientExecChain {
|
||||
|
||||
private final ClientExecChain mainExec;
|
||||
|
||||
private final Credentials credentials;
|
||||
|
||||
private final Validator validator;
|
||||
|
||||
protected DecorateClientExecChain(Credentials credentials, Validator validator, ClientExecChain mainExec) {
|
||||
this.credentials = credentials;
|
||||
this.validator = validator;
|
||||
this.mainExec = mainExec;
|
||||
}
|
||||
|
||||
protected void convertToRepeatableResponseEntity(CloseableHttpResponse response) throws IOException {
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (entity != null) {
|
||||
response.setEntity(new BufferedHttpEntity(entity));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CloseableHttpResponse execute(HttpRoute httpRoute, HttpRequestWrapper httpRequestWrapper,
|
||||
HttpClientContext httpClientContext, HttpExecutionAware httpExecutionAware) throws IOException, HttpException {
|
||||
//请求头添加认证令牌
|
||||
httpRequestWrapper.addHeader(AUTHORIZATION,credentials.createCredentials(httpRequestWrapper));
|
||||
//执行请求
|
||||
CloseableHttpResponse response = mainExec.execute(httpRoute, httpRequestWrapper, httpClientContext, httpExecutionAware);
|
||||
//对成功应答验签
|
||||
StatusLine statusLine = response.getStatusLine();
|
||||
if (statusLine.getStatusCode() >= SC_OK && statusLine.getStatusCode() < SC_MULTIPLE_CHOICES) {
|
||||
convertToRepeatableResponseEntity(response);
|
||||
if (!validator.validate(response)) {
|
||||
throw new HttpException("应答的签名验证失败");
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.itheima.sfbx;
|
||||
|
||||
import com.itheima.sfbx.dto.EncodeDTO;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface Encryptor {
|
||||
|
||||
|
||||
/***
|
||||
* @description 加密body体消息的方法
|
||||
* @param body 请求消息体
|
||||
* @return: boolean 校验结果
|
||||
*/
|
||||
boolean encode(EncodeDTO body) throws IOException;
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package com.itheima.sfbx;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.Mode;
|
||||
import cn.hutool.crypto.Padding;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.asymmetric.RSA;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.itheima.sfbx.auth.BoleeEncryptor;
|
||||
import com.itheima.sfbx.constants.BoleeSecurityConstant;
|
||||
import com.itheima.sfbx.dto.CredentialsDTO;
|
||||
import com.itheima.sfbx.dto.EncodeDTO;
|
||||
import com.itheima.sfbx.utils.SecurityUtil;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* RequestTemplate
|
||||
*
|
||||
* @author: wgl
|
||||
* @describe: 对外统一发送模板--组装各个模组
|
||||
* 加密模组
|
||||
* 解密模组组装
|
||||
* @date: 2022/12/28 10:10
|
||||
*/
|
||||
@Data
|
||||
public class RequestTemplate {
|
||||
|
||||
private String privateKey;
|
||||
|
||||
private String publicKey;
|
||||
|
||||
private String appId;
|
||||
|
||||
private URIBuilder uriBuilder;
|
||||
|
||||
private RequestConfig requestConfig;
|
||||
|
||||
@Builder
|
||||
public RequestTemplate(String privateKey, String publicKey, String appId, URIBuilder uriBuilder,RequestConfig requestConfig) {
|
||||
this.privateKey = privateKey;
|
||||
this.publicKey = publicKey;
|
||||
this.appId = appId;
|
||||
this.uriBuilder = uriBuilder;
|
||||
this.requestConfig = requestConfig;
|
||||
}
|
||||
|
||||
|
||||
public <T> T doRequest(Object params,Class<T> t) throws URISyntaxException, IOException {
|
||||
CloseableHttpClient httpclient = BoleeHttpClientBuilder.create()
|
||||
.withCredentials(appId, privateKey)
|
||||
.withValidator(publicKey)
|
||||
.build();
|
||||
//=============基础配置===================
|
||||
RequestConfig requestConfig = null;
|
||||
if(ObjectUtil.isNull(requestConfig)) {
|
||||
requestConfig = RequestConfig.custom()
|
||||
.setSocketTimeout(100000)
|
||||
.setConnectTimeout(100000)
|
||||
.build();
|
||||
}else{
|
||||
requestConfig = this.requestConfig;
|
||||
}
|
||||
try {
|
||||
URI url = uriBuilder.build();
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
httpPost.setConfig(requestConfig);
|
||||
EncodeDTO encodeDTO = new EncodeDTO();
|
||||
JSONObject body = JSONUtil.parseObj(params);
|
||||
encodeDTO.setBody(body.toString());
|
||||
if(new BoleeEncryptor(privateKey).encode(encodeDTO)){
|
||||
StringEntity stringEntity = new StringEntity(encodeDTO.getEncodeBody(), "utf-8");
|
||||
httpPost.setEntity(stringEntity);
|
||||
httpPost.addHeader(BoleeSecurityConstant.HEAD_NAME_BODY_KEY,encodeDTO.getHeadAESSecurityKey());
|
||||
httpPost.addHeader("Content-Type","application/json;charset=utf-8");
|
||||
CloseableHttpResponse apiRes = httpclient.execute(httpPost);
|
||||
//验签完毕后需要对数据进行解密
|
||||
HttpEntity entity = apiRes.getEntity();
|
||||
String content = EntityUtils.toString(entity);
|
||||
String responseJson = decryptRequestBody(content, apiRes.getFirstHeader(BoleeSecurityConstant.HEAD_NAME_BODY_KEY).getValue());
|
||||
if(StrUtil.isEmpty(responseJson)){
|
||||
throw new RuntimeException("参数解析为空");
|
||||
}
|
||||
return JSONUtil.toBean(responseJson, t);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密请求body体
|
||||
*
|
||||
* @param dody 返回数据对象
|
||||
* @param aesKeyRSA 获取请求头中加密后的AES密钥
|
||||
* @return
|
||||
*/
|
||||
private String decryptRequestBody(String dody, String aesKeyRSA) {
|
||||
try {
|
||||
//先使用rsa解密aes秘钥
|
||||
RSA rsa = SecureUtil.rsa(null, publicKey);
|
||||
String aesKey = SecurityUtil.decryptFromStringRSAPublicKey(rsa, aesKeyRSA);
|
||||
//使用aes进行解密
|
||||
// 使用解密后的AES密钥进行AES解密请求体数据
|
||||
String requestBody = SecurityUtil.decryptFromStringAES(dody, Mode.CBC, Padding.ZeroPadding,aesKey);
|
||||
return requestBody;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
// 解密失败,可以根据你的需求进行异常处理
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建请求参数
|
||||
* 1694953272
|
||||
* xHct36JgHbj6tXBx
|
||||
* Modified: {"msg":"ok","code":"100","data":null}
|
||||
*
|
||||
* @param authorization
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
private CredentialsDTO buildRequestParam(String authorization, CloseableHttpResponse response) {
|
||||
try {
|
||||
authorization = authorization.replaceAll("\"", "");
|
||||
String[] authorizations = authorization.split(",");
|
||||
Map<String, String> authMap = new HashMap<>();
|
||||
for (String authorizationIndex : authorizations) {
|
||||
String[] split = authorizationIndex.split("=");
|
||||
authMap.put(split[0], split[1].substring(0, split[1].length()));
|
||||
}
|
||||
CredentialsDTO credentialsDTO = new CredentialsDTO();
|
||||
credentialsDTO.setNonce(authMap.get("nonce_str"));
|
||||
credentialsDTO.setTimestamp(authMap.get("timestamp"));
|
||||
credentialsDTO.setSignature(authMap.get("signature"));
|
||||
// credentialsDTO.setSecurityBody(getPostData(req));
|
||||
return credentialsDTO;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.itheima.sfbx;
|
||||
|
||||
/**
|
||||
* @ClassName Signer.java
|
||||
* @Description 签名加密者
|
||||
*/
|
||||
public interface Signer {
|
||||
|
||||
String sign(byte[] message);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.itheima.sfbx;
|
||||
|
||||
import com.itheima.sfbx.notify.NotifyRequest;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @ClassName Validator.java
|
||||
* @Description 签名验证处理
|
||||
*/
|
||||
public interface Validator {
|
||||
|
||||
/***
|
||||
* @description 验证应答签名
|
||||
* @param response 响应结果
|
||||
* @return: boolean 校验结果
|
||||
*/
|
||||
boolean validate(CloseableHttpResponse response) throws IOException;
|
||||
|
||||
/***
|
||||
* @description 验证通知请求签名
|
||||
* @param notifyRequest 响应结果
|
||||
* @return: boolean 校验结果
|
||||
*/
|
||||
boolean validateNotify(NotifyRequest notifyRequest);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.itheima.sfbx;
|
||||
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.asymmetric.Sign;
|
||||
import cn.hutool.crypto.asymmetric.SignAlgorithm;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
/**
|
||||
* @ClassName Verifier.java
|
||||
* @Description 验签解密者
|
||||
*/
|
||||
public interface Verifier {
|
||||
|
||||
boolean verify(byte[] data,byte[] signature);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.itheima.sfbx.auth;
|
||||
|
||||
import com.itheima.sfbx.Credentials;
|
||||
import com.itheima.sfbx.Signer;
|
||||
import com.itheima.sfbx.utils.ClientUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.http.client.methods.HttpRequestWrapper;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @ClassName BoleeCredentials.java
|
||||
* @Description 认证凭证创建处理实现
|
||||
*/
|
||||
@Slf4j
|
||||
public class BoleeCredentials implements Credentials {
|
||||
|
||||
//应用id
|
||||
protected final String appId;
|
||||
|
||||
//签名者
|
||||
protected final Signer signer;
|
||||
|
||||
public BoleeCredentials(String appId, Signer signer) {
|
||||
this.appId = appId;
|
||||
this.signer = signer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createCredentials(HttpRequestWrapper request) throws IOException {
|
||||
//临时字符串
|
||||
String nonceStr = ClientUtils.generateNonceStr();
|
||||
//时间戳
|
||||
long timestamp = ClientUtils.generateTimestamp();
|
||||
//请求数据
|
||||
String message = ClientUtils.requestMessage(nonceStr, timestamp, request);
|
||||
log.debug("authorization data:{}", message);
|
||||
//生成签名字符串
|
||||
String signature = signer.sign(Base64.decodeBase64(message));
|
||||
//返回认证令牌对象
|
||||
String credentials = "appId=\"" + this.appId + "\","
|
||||
+ "nonce_str=\"" + nonceStr + "\","
|
||||
+ "timestamp=\"" + timestamp + "\","
|
||||
+ "signature=\"" + signature + "\"";
|
||||
log.debug("authorization credentials=[{}]", credentials);
|
||||
return credentials;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.itheima.sfbx.auth;
|
||||
|
||||
import cn.hutool.crypto.Mode;
|
||||
import cn.hutool.crypto.Padding;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.asymmetric.KeyType;
|
||||
import cn.hutool.crypto.asymmetric.RSA;
|
||||
import cn.hutool.crypto.symmetric.AES;
|
||||
import com.itheima.sfbx.Encryptor;
|
||||
import com.itheima.sfbx.constants.BoleeSecurityConstant;
|
||||
import com.itheima.sfbx.dto.EncodeDTO;
|
||||
import com.itheima.sfbx.utils.ClientUtils;
|
||||
import com.itheima.sfbx.utils.SecurityUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.http.client.methods.HttpRequestWrapper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* BoleeEncryptor
|
||||
*
|
||||
* @author: wgl
|
||||
* @describe: TODO
|
||||
* @date: 2022/12/28 10:10
|
||||
*/
|
||||
@Slf4j
|
||||
public class BoleeEncryptor implements Encryptor {
|
||||
|
||||
//私钥签名
|
||||
protected String privateKeyBase64;
|
||||
|
||||
|
||||
public BoleeEncryptor(String privateKey) {
|
||||
this.privateKeyBase64 = privateKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对数据进行加密
|
||||
* @param body 请求消息体
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public boolean encode(EncodeDTO body){
|
||||
//获取AES加密秘钥
|
||||
String bodySecurity = ClientUtils.generateNonceStr();
|
||||
//使用AES加密 body体里的数据
|
||||
String aesSecurity = SecurityUtil.encryptFromStringAES(body.getBody(), Mode.CBC, Padding.ZeroPadding,bodySecurity);
|
||||
body.setEncodeBody(aesSecurity);
|
||||
//使用RSA加密AES的对称加密秘钥
|
||||
RSA rsa = new RSA(privateKeyBase64, null);
|
||||
String securityToken = SecurityUtil.encryptFromStringRSAPrivateKey(rsa, bodySecurity);
|
||||
body.setHeadAESSecurityKey(securityToken);
|
||||
//将新生产的AES的body返回
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.itheima.sfbx.auth;
|
||||
|
||||
import com.itheima.sfbx.Validator;
|
||||
import com.itheima.sfbx.Verifier;
|
||||
import com.itheima.sfbx.notify.NotifyRequest;
|
||||
import com.itheima.sfbx.utils.ClientUtils;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* @ClassName BoleeValidator.java
|
||||
* @Description 签名验证处理实现
|
||||
*/
|
||||
public class BoleeValidator implements Validator {
|
||||
|
||||
protected final Verifier verifier;
|
||||
|
||||
public BoleeValidator(Verifier verifier) {
|
||||
this.verifier = verifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(CloseableHttpResponse response) throws IOException {
|
||||
//校验请求参数
|
||||
ClientUtils.validateParameters(response);
|
||||
//构建响应体信息
|
||||
String message = ClientUtils.responseMessage(response);
|
||||
//获得签名信息
|
||||
String signature = response.getFirstHeader("signature").getValue();
|
||||
//验证签名
|
||||
return verifier.verify(message.getBytes(StandardCharsets.UTF_8), Base64.decodeBase64(signature));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateNotify(NotifyRequest notifyRequest) {
|
||||
//校验请求参数
|
||||
ClientUtils.validateParametersNotify(notifyRequest);
|
||||
//构建响应体信息
|
||||
String message = ClientUtils.notifyMessage(notifyRequest);
|
||||
//获得签名信息
|
||||
String signature = notifyRequest.getSignature();
|
||||
//验证签名
|
||||
return verifier.verify(message.getBytes(StandardCharsets.UTF_8), Base64.decodeBase64(signature));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.itheima.sfbx.auth;
|
||||
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.asymmetric.Sign;
|
||||
import cn.hutool.crypto.asymmetric.SignAlgorithm;
|
||||
import com.itheima.sfbx.Signer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
/**
|
||||
* @ClassName PrivateKeySigner.java
|
||||
* @Description TODO
|
||||
*/
|
||||
@Slf4j
|
||||
public class PrivateKeySigner implements Signer {
|
||||
|
||||
protected final String privateKey;
|
||||
|
||||
public PrivateKeySigner(String privateKey) {
|
||||
this.privateKey = privateKey;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String sign(byte[] message) {
|
||||
Sign sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA,Base64.decodeBase64(privateKey),null);
|
||||
return Base64.encodeBase64String(sign.sign(message));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.itheima.sfbx.auth;
|
||||
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.asymmetric.Sign;
|
||||
import cn.hutool.crypto.asymmetric.SignAlgorithm;
|
||||
import com.itheima.sfbx.Verifier;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
/**
|
||||
* @ClassName PublicKeyVerifier.java
|
||||
* @Description 公钥验证
|
||||
*/
|
||||
public class PublicKeyVerifier implements Verifier {
|
||||
|
||||
protected final String publicKey;
|
||||
|
||||
public PublicKeyVerifier(String publicKey) {
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(byte[] data,byte[] signature){
|
||||
Sign sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA,null, Base64.decodeBase64(publicKey));
|
||||
return sign.verify(data,signature);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.itheima.sfbx.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* SecurityConfig
|
||||
*
|
||||
* @author: wgl
|
||||
* @describe: 客户端秘钥配置
|
||||
* @date: 2022/12/28 10:10
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "itheima.security")
|
||||
@Data
|
||||
public class SecurityConfig {
|
||||
|
||||
/**
|
||||
* 客户appId
|
||||
*/
|
||||
@Value("${itheima.security.client.appid}")
|
||||
private String appId;
|
||||
|
||||
/**
|
||||
* 客户端私钥
|
||||
*/
|
||||
@Value("${itheima.security.client.privateKey}")
|
||||
private String privateKey;
|
||||
|
||||
|
||||
/**
|
||||
* 服务端公钥
|
||||
*/
|
||||
@Value("${itheima.security.server.publicKey}")
|
||||
private String publicKey;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.itheima.sfbx.constants;
|
||||
|
||||
/**
|
||||
* BoleeSecurityConstant
|
||||
*
|
||||
* @author: wgl
|
||||
* @describe: 宝乐保险秘钥常量
|
||||
* @date: 2022/12/28 10:10
|
||||
*/
|
||||
public class BoleeSecurityConstant {
|
||||
|
||||
/**
|
||||
* 宝乐保险中body体中的信息
|
||||
*/
|
||||
public static String HEAD_NAME_BODY_KEY = "bolee_security_key";
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.itheima.sfbx.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* RequestParamDTO
|
||||
*
|
||||
* @author: wgl
|
||||
* @describe: TODO
|
||||
* @date: 2022/12/28 10:10
|
||||
*/
|
||||
@Data
|
||||
public class CredentialsDTO {
|
||||
|
||||
/**
|
||||
* 请求方式
|
||||
*/
|
||||
private String methodd;
|
||||
|
||||
/**
|
||||
* 接口路径
|
||||
*/
|
||||
private String uriPath;
|
||||
|
||||
/**
|
||||
* 时间戳
|
||||
*/
|
||||
private String timestamp;
|
||||
|
||||
/**
|
||||
* 签名数据
|
||||
*/
|
||||
private String signature;
|
||||
|
||||
/**
|
||||
* 密文数据
|
||||
*/
|
||||
private String securityBody;
|
||||
|
||||
/**
|
||||
* 随机字符串
|
||||
*/
|
||||
private String nonce;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 构建签名字符串
|
||||
* return request.getRequestLine().getMethod() + "\n"
|
||||
* + canonicalUrl + "\n"
|
||||
* + timestamp + "\n"
|
||||
* + nonce + "\n"
|
||||
* + body + "\n";
|
||||
* @return
|
||||
*/
|
||||
public String buildSignBody(){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(methodd + "\n");
|
||||
sb.append(uriPath + "\n");
|
||||
sb.append(timestamp + "\n");
|
||||
sb.append(nonce + "\n");
|
||||
sb.append(securityBody + "\n");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.itheima.sfbx.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* EncodeDTO
|
||||
*
|
||||
* @author: wgl
|
||||
* @describe: TODO
|
||||
* @date: 2022/12/28 10:10
|
||||
*/
|
||||
@Data
|
||||
public class EncodeDTO {
|
||||
|
||||
/**
|
||||
* 原始body参数
|
||||
*/
|
||||
private String body;
|
||||
|
||||
/**
|
||||
* 加密后的密文
|
||||
*/
|
||||
private String encodeBody;
|
||||
|
||||
/**
|
||||
* head中rsa加密后的aes密钥
|
||||
*/
|
||||
private String headAESSecurityKey;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.itheima.sfbx.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* ResponseDTO
|
||||
*
|
||||
* @author: wgl
|
||||
* @describe: 返回结果对象
|
||||
* @date: 2022/12/28 10:10
|
||||
*/
|
||||
@Data
|
||||
public class ResponseDTO {
|
||||
|
||||
private String msg;
|
||||
|
||||
private String code;
|
||||
|
||||
private Map data;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.itheima.sfbx.notify;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.ObjectReader;
|
||||
import com.itheima.sfbx.Validator;
|
||||
import com.itheima.sfbx.Verifier;
|
||||
import com.itheima.sfbx.auth.BoleeValidator;
|
||||
import com.itheima.sfbx.auth.PublicKeyVerifier;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @ClassName NotifiyHandler.java
|
||||
* @Description 通知验证及解析出来
|
||||
*/
|
||||
public class NotifiyHandler {
|
||||
|
||||
//校验者
|
||||
private Validator validator;
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public NotifiyHandler(String publicKey) {
|
||||
this.validator = new BoleeValidator(new PublicKeyVerifier(publicKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析通知请求结果
|
||||
* @param notifyRequest 微信支付通知请求
|
||||
* @return 微信支付通知报文解密结果@StreamListener(FileSink.FILE_INPUT)
|
||||
* public void onMessage(@Payload MqMessage message,
|
||||
* @Header(AmqpHeaders.CHANNEL) Channel channel,
|
||||
* @Header(AmqpHeaders.DELIVERY_TAG) Long deliveryTag) throws IOException {
|
||||
* String jsonConten = message.getcontent();
|
||||
* log.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message);
|
||||
* FileVO fileVO= JSONObject.parseObject(jsonConten,FileVO.class);
|
||||
* Boolean responseWrap = fileBusinessFeign.clearFileById(fileVO.getId());
|
||||
* channel.basicAck(deliveryTag,false);
|
||||
*
|
||||
* }
|
||||
*/
|
||||
public NotifyResponse parse(NotifyRequest notifyRequest) throws IOException {
|
||||
// 验签
|
||||
boolean validate = validator.validateNotify(notifyRequest);
|
||||
if (!validate){
|
||||
throw new RuntimeException("验证签名失败");
|
||||
}
|
||||
// 解析请求体
|
||||
return parseBody(notifyRequest.getBody());
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析请求体
|
||||
*
|
||||
* @param body 请求体
|
||||
* @return 解析结果
|
||||
* @throws IOException 解析body失败
|
||||
*/
|
||||
private NotifyResponse parseBody(String body) throws IOException {
|
||||
ObjectReader objectReader = objectMapper.reader();
|
||||
NotifyResponse notifyResponse = objectReader.readValue(body, NotifyResponse.class);
|
||||
return notifyResponse;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.itheima.sfbx.notify;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @ClassName NotificationRequest.java
|
||||
* @Description 通知请求对象
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class NotifyRequest {
|
||||
|
||||
//时间戳
|
||||
private String timestamp;
|
||||
//随机字符串
|
||||
private String nonce;
|
||||
//签名
|
||||
private String signature;
|
||||
//请求体
|
||||
private String body;
|
||||
|
||||
@Builder
|
||||
public NotifyRequest(String timestamp, String nonce, String signature, String body) {
|
||||
this.timestamp = timestamp;
|
||||
this.nonce = nonce;
|
||||
this.signature = signature;
|
||||
this.body = body;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package com.itheima.sfbx.notify;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* @ClassName NotifyResponse.java
|
||||
* @Description TODO
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class NotifyResponse {
|
||||
|
||||
@JsonProperty("id")
|
||||
private String id;
|
||||
@JsonProperty("create_time")
|
||||
private String createTime;
|
||||
@JsonProperty("event_type")
|
||||
private String eventType;
|
||||
@JsonProperty("resource_type")
|
||||
private String resourceType;
|
||||
@JsonProperty("summary")
|
||||
private String summary;
|
||||
@JsonProperty("")
|
||||
private Resource resource;
|
||||
private String decryptData;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Notification{" +
|
||||
"id='" + id + '\'' +
|
||||
", createTime='" + createTime + '\'' +
|
||||
", eventType='" + eventType + '\'' +
|
||||
", resourceType='" + resourceType + '\'' +
|
||||
", decryptData='" + decryptData + '\'' +
|
||||
", summary='" + summary + '\'' +
|
||||
", resource=" + resource +
|
||||
'}';
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
public String getEventType() {
|
||||
return eventType;
|
||||
}
|
||||
|
||||
public String getDecryptData() {
|
||||
return decryptData;
|
||||
}
|
||||
|
||||
public String getSummary() {
|
||||
return summary;
|
||||
}
|
||||
|
||||
public String getResourceType() {
|
||||
return resourceType;
|
||||
}
|
||||
|
||||
public Resource getResource() {
|
||||
return resource;
|
||||
}
|
||||
|
||||
public void setDecryptData(String decryptData) {
|
||||
this.decryptData = decryptData;
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class Resource {
|
||||
|
||||
@JsonProperty("algorithm")
|
||||
private String algorithm;
|
||||
@JsonProperty("ciphertext")
|
||||
private String ciphertext;
|
||||
@JsonProperty("associated_data")
|
||||
private String associatedData;
|
||||
@JsonProperty("nonce")
|
||||
private String nonce;
|
||||
@JsonProperty("original_type")
|
||||
private String originalType;
|
||||
|
||||
public String getAlgorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
public String getCiphertext() {
|
||||
return ciphertext;
|
||||
}
|
||||
|
||||
public String getAssociatedData() {
|
||||
return associatedData;
|
||||
}
|
||||
|
||||
public String getNonce() {
|
||||
return nonce;
|
||||
}
|
||||
|
||||
public String getOriginalType() {
|
||||
return originalType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Resource{" +
|
||||
"algorithm='" + algorithm + '\'' +
|
||||
", ciphertext='" + ciphertext + '\'' +
|
||||
", associatedData='" + associatedData + '\'' +
|
||||
", nonce='" + nonce + '\'' +
|
||||
", originalType='" + originalType + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package com.itheima.sfbx.utils;
|
||||
|
||||
import com.itheima.sfbx.notify.NotifyRequest;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpEntityEnclosingRequest;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpRequestWrapper;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.SecureRandom;
|
||||
import java.time.DateTimeException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* @ClassName ClientUtils.java
|
||||
* @Description 客户端工具类
|
||||
*/
|
||||
public class ClientUtils {
|
||||
|
||||
//随机字符串处理
|
||||
protected static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
//随机函数
|
||||
protected static final SecureRandom RANDOM = new SecureRandom();
|
||||
|
||||
/***
|
||||
* @description 时间戳创建
|
||||
* @return: long 返回时间戳
|
||||
*/
|
||||
public static long generateTimestamp() {
|
||||
|
||||
return System.currentTimeMillis() / 1000;
|
||||
}
|
||||
|
||||
/***
|
||||
* @description 随机字符串
|
||||
* @return: java.lang.String 字符串
|
||||
*/
|
||||
public static String generateNonceStr() {
|
||||
char[] nonceChars = new char[16];
|
||||
for (int index = 0; index < nonceChars.length; ++index) {
|
||||
nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
|
||||
}
|
||||
return new String(nonceChars);
|
||||
}
|
||||
|
||||
/***
|
||||
* @description 构建请求信息: 请求方式+请求路径+请求时间戳+随机字符串+请求体
|
||||
* @param nonce 随机字符串
|
||||
* @param timestamp 时间戳
|
||||
* @param request 请求对象
|
||||
* @return: java.lang.String 请求信息
|
||||
*/
|
||||
public static String requestMessage(String nonce, long timestamp, HttpRequestWrapper request) throws IOException {
|
||||
URI uri = request.getURI();
|
||||
String canonicalUrl = uri.getRawPath();
|
||||
if (uri.getQuery() != null) {
|
||||
canonicalUrl += "?" + uri.getRawQuery();
|
||||
}
|
||||
String body = EntityUtils.toString(((HttpEntityEnclosingRequest) request).getEntity(), StandardCharsets.UTF_8);
|
||||
return request.getRequestLine().getMethod() + "\n"
|
||||
+ canonicalUrl + "\n"
|
||||
+ timestamp + "\n"
|
||||
+ nonce + "\n"
|
||||
+ body + "\n";
|
||||
}
|
||||
|
||||
/***
|
||||
* @description 检测应答参数
|
||||
* @param response 应答对象
|
||||
*/
|
||||
public static final void validateParameters(CloseableHttpResponse response) {
|
||||
//返回头中存在的内容:签名字符串,随机字符串,时间戳
|
||||
String[] headers = {"signature","nonce_str","timestamp"};
|
||||
Header header = null;
|
||||
for (String headerName : headers) {
|
||||
header = response.getFirstHeader(headerName);
|
||||
if (header == null) {
|
||||
throw new RuntimeException("验证返回参数"+headerName+"不全");
|
||||
}
|
||||
}
|
||||
//获得最后时间戳,判断应答时间戳到当前时间超过5分钟则认为应答失败
|
||||
String timestampStr = header.getValue();
|
||||
try {
|
||||
Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestampStr));
|
||||
// 拒绝过期应答
|
||||
if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= 5) {
|
||||
throw new RuntimeException("应答超时");
|
||||
}
|
||||
} catch (DateTimeException | NumberFormatException e) {
|
||||
throw new RuntimeException("应答时间处理异常");
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* @description 构建应答信息
|
||||
* @param response 应答结果
|
||||
* @return: java.lang.String 应答信息
|
||||
*/
|
||||
public static String responseMessage(CloseableHttpResponse response) throws IOException {
|
||||
String timestamp = response.getFirstHeader("timestamp").getValue();
|
||||
String nonce = response.getFirstHeader("nonce_str").getValue();
|
||||
HttpEntity entity = response.getEntity();
|
||||
String body = (entity != null && entity.isRepeatable()) ? EntityUtils.toString(entity) : "";;
|
||||
return timestamp + "\n"
|
||||
+ nonce + "\n"
|
||||
+ body + "\n";
|
||||
}
|
||||
|
||||
/***
|
||||
* @description 构建notify信息
|
||||
* @param notifyRequest 推送请求结果
|
||||
* @return: java.lang.String 应答信息
|
||||
*/
|
||||
public static String notifyMessage(NotifyRequest notifyRequest) {
|
||||
String timestamp = notifyRequest.getTimestamp();
|
||||
String nonce = notifyRequest.getNonce();
|
||||
String body = notifyRequest.getBody();
|
||||
return timestamp + "\n"
|
||||
+ nonce + "\n"
|
||||
+ body + "\n";
|
||||
}
|
||||
|
||||
public static void validateParametersNotify(NotifyRequest notifyRequest) {
|
||||
//请求中必须存在的内容:签名字符串,随机字符串,时间戳
|
||||
String signature = notifyRequest.getSignature();
|
||||
if (signature==null){
|
||||
throw new RuntimeException("签名字符串为空");
|
||||
}
|
||||
String nonce = notifyRequest.getNonce();
|
||||
if (nonce==null){
|
||||
throw new RuntimeException("随机字符串为空");
|
||||
}
|
||||
String timestamp = notifyRequest.getTimestamp();
|
||||
if (timestamp==null){
|
||||
throw new RuntimeException("时间戳为空");
|
||||
}
|
||||
//获得最后时间戳,判断应答时间戳到当前时间超过5分钟则认为应答失败
|
||||
try {
|
||||
Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestamp));
|
||||
// 拒绝过期应答
|
||||
if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= 5) {
|
||||
throw new RuntimeException("应答超时");
|
||||
}
|
||||
} catch (DateTimeException | NumberFormatException e) {
|
||||
throw new RuntimeException("应答时间处理异常");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.itheima.sfbx.utils;
|
||||
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.Mode;
|
||||
import cn.hutool.crypto.Padding;
|
||||
import cn.hutool.crypto.asymmetric.KeyType;
|
||||
import cn.hutool.crypto.asymmetric.RSA;
|
||||
import cn.hutool.crypto.symmetric.AES;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* @ClassName SymmetricCryptoUtil.java
|
||||
* @Description TODO
|
||||
*/
|
||||
public class SecurityUtil {
|
||||
|
||||
/**
|
||||
* 16字节
|
||||
*/
|
||||
private static final String ENCODE_KEY = "1234567812345678";
|
||||
private static final String IV_KEY = "0000000000000000";
|
||||
|
||||
public static void main(String[] args) {
|
||||
String password = "zdm321123.";
|
||||
String encryptData = encryptFromStringAES(password, Mode.CBC, Padding.ZeroPadding);
|
||||
System.out.println("AES加密:" + encryptData);
|
||||
String decryptData = decryptFromStringAES(encryptData, Mode.CBC, Padding.ZeroPadding);
|
||||
System.out.println("AES解密:" + decryptData);
|
||||
RSA rsa = new RSA();
|
||||
encryptData = encryptFromStringRSA(rsa,password);
|
||||
System.out.println("RSA加密:"+encryptData);
|
||||
System.out.println("================用于网络传输===========");
|
||||
decryptData = decryptFromStringRSA(rsa,encryptData);
|
||||
//解密内容生成字符串
|
||||
System.out.println("RSA解密:" + decryptData);
|
||||
|
||||
}
|
||||
|
||||
public static String encryptFromStringAES(String data, Mode mode, Padding padding) {
|
||||
AES aes;
|
||||
if (Mode.CBC == mode) {
|
||||
aes = new AES(mode, padding,
|
||||
new SecretKeySpec(ENCODE_KEY.getBytes(), "AES"),
|
||||
new IvParameterSpec(IV_KEY.getBytes()));
|
||||
} else {
|
||||
aes = new AES(mode, padding,
|
||||
new SecretKeySpec(ENCODE_KEY.getBytes(), "AES"));
|
||||
}
|
||||
return aes.encryptBase64(data, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static String encryptFromStringAES(String data, Mode mode, Padding padding,String securityKey) {
|
||||
AES aes;
|
||||
if (Mode.CBC == mode) {
|
||||
aes = new AES(mode, padding,
|
||||
new SecretKeySpec(ENCODE_KEY.getBytes(), "AES"),
|
||||
new IvParameterSpec(securityKey.getBytes()));
|
||||
} else {
|
||||
aes = new AES(mode, padding,
|
||||
new SecretKeySpec(ENCODE_KEY.getBytes(), "AES"));
|
||||
}
|
||||
return aes.encryptBase64(data, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static String encryptFromStringRSA(RSA rsa,String password) {
|
||||
//byte[]内容加密
|
||||
byte[] encrypt = rsa.encrypt(StrUtil.bytes(password, CharsetUtil.CHARSET_UTF_8), KeyType.PublicKey);
|
||||
//加密后base64处理用于网络传输
|
||||
return Base64.encodeBase64String(encrypt);
|
||||
}
|
||||
|
||||
public static String encryptFromStringRSAPrivateKey(RSA rsa,String password) {
|
||||
//byte[]内容加密
|
||||
byte[] encrypt = rsa.encrypt(StrUtil.bytes(password, CharsetUtil.CHARSET_UTF_8), KeyType.PrivateKey);
|
||||
//加密后base64处理用于网络传输
|
||||
return Base64.encodeBase64String(encrypt);
|
||||
}
|
||||
|
||||
public static String decryptFromStringAES(String data, Mode mode, Padding padding) {
|
||||
AES aes;
|
||||
if (Mode.CBC == mode) {
|
||||
aes = new AES(mode, padding,
|
||||
new SecretKeySpec(ENCODE_KEY.getBytes(), "AES"),
|
||||
new IvParameterSpec(IV_KEY.getBytes()));
|
||||
} else {
|
||||
aes = new AES(mode, padding,
|
||||
new SecretKeySpec(ENCODE_KEY.getBytes(), "AES"));
|
||||
}
|
||||
byte[] decryptDataBase64 = aes.decrypt(data);
|
||||
return new String(decryptDataBase64, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static String decryptFromStringAES(String data, Mode mode, Padding padding,String aesKey) {
|
||||
AES aes;
|
||||
if (Mode.CBC == mode) {
|
||||
aes = new AES(mode, padding,
|
||||
new SecretKeySpec(ENCODE_KEY.getBytes(), "AES"),
|
||||
new IvParameterSpec(aesKey.getBytes()));
|
||||
} else {
|
||||
aes = new AES(mode, padding,
|
||||
new SecretKeySpec(ENCODE_KEY.getBytes(), "AES"));
|
||||
}
|
||||
byte[] decryptDataBase64 = aes.decrypt(data);
|
||||
return new String(decryptDataBase64, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static String decryptFromStringRSA(RSA rsa, String encryptData) {
|
||||
//对byte数组进行解密
|
||||
byte[] decrypt = rsa.decrypt(Base64.decodeBase64(encryptData), KeyType.PrivateKey);
|
||||
return new String(decrypt, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static String decryptFromStringRSAPublicKey(RSA rsa, String encryptData) {
|
||||
//对byte数组进行解密
|
||||
byte[] decrypt = rsa.decrypt(Base64.decodeBase64(encryptData), KeyType.PublicKey);
|
||||
return new String(decrypt, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.itheima.sfbx.config.SecurityConfig
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.itheima.sfbx;
|
||||
|
||||
import cn.hutool.crypto.asymmetric.RSA;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.itheima.sfbx.auth.BoleeEncryptor;
|
||||
import com.itheima.sfbx.auth.PrivateKeySigner;
|
||||
import com.itheima.sfbx.auth.PublicKeyVerifier;
|
||||
import com.itheima.sfbx.constants.BoleeSecurityConstant;
|
||||
import com.itheima.sfbx.dto.EncodeDTO;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @ClassName BoleeHttpClientTest.java
|
||||
* @Description TODO
|
||||
*/
|
||||
public class BoleeHttpClientTest {
|
||||
|
||||
private static String privateKeyBase64 = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAILdGdmrbwdn0tkD5I1RYB/whGEUsAOYprsy/F0cCdRFcUfNT6Q4BcErwcJAmdXPDuSYSbPsImKA0EbLN+pGh/wwlrYtBueItE4zGV2GdMNhAU3P6CquIAm5H58UFeDmVEUnE81laylXjaiaZduXBVTiLgTPVcpSxus70ZJQQirBAgMBAAECgYAg0sBHHn7MxrfWAunyoDSSDkvF5eB4JnO7hIBUAlJc0cYmElMlh3+6AfWpeXaccED2CVSDMnk1Z8XV2+b8dhBpTo3IHG+IQwZvz89bfMcwhhpRT9eXpC5YkG5UHlh11Ftm7byAo7s0HG/JynquxWZDRifWnvrA2bYE2sk6oN2zuQJBAMxlxcDDcSIdsZbXH7zTFJ6c0WEYlsXndRJhT1OfmxzwNitN+8PicbyJh6GfDcM+PR+13rqGZ4x+yGyKSjsp9XMCQQCj5tRLINjRUcNalPrJnvvUMhlJ0yrxsKReX81L1lwDXjbFpcMsy9eZddgdLjGXRgH7K9tYmLGZ6hflRGTcbrH7AkEAt/HDFOYOU1iLsKbrDgCcJt4T5CC/11ykVCU0wZn6ewGGjlRBBhksqDLQ19ePCC1jzrzas9wvJhYXAu81PKdXFwJAUftq4u1aJlFcgtmUG/efBUPN7GRozZ3Kib4nxTBCtBiTEwfX+Xc4r3UHlYj+mykUYptMSyONamxyaWZtgOkJswJBAMLjyUR61F4UPjoyGGg5G4eRTM3fJ1w7ulHYYxlEy5u2UAC/jBtd07wv030lsqPSA7abTdK+6iNM2v0gA7KODKk=";
|
||||
|
||||
private static String publicKeyBase64 = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCC3RnZq28HZ9LZA+SNUWAf8IRhFLADmKa7MvxdHAnURXFHzU+kOAXBK8HCQJnVzw7kmEmz7CJigNBGyzfqRof8MJa2LQbniLROMxldhnTDYQFNz+gqriAJuR+fFBXg5lRFJxPNZWspV42ommXblwVU4i4Ez1XKUsbrO9GSUEIqwQIDAQAB";
|
||||
|
||||
@Test
|
||||
public void testTemplate() throws URISyntaxException, IOException {
|
||||
URIBuilder urlBuilder = new URIBuilder()
|
||||
.setScheme("http")
|
||||
.setHost("localhost:8080")
|
||||
.setPath("/insure");
|
||||
RequestTemplate requestTemplate = RequestTemplate.builder()
|
||||
.appId("10001")
|
||||
.privateKey(privateKeyBase64)
|
||||
.publicKey(publicKeyBase64)
|
||||
.uriBuilder(urlBuilder)
|
||||
.build();
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("name","张三");
|
||||
params.put("age","18");
|
||||
params.put("sex","男");
|
||||
Map map = requestTemplate.doRequest(params, Map.class);
|
||||
System.out.println(map);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void BoleeHttpClientBuilderTest(){
|
||||
CloseableHttpClient httpclient = BoleeHttpClientBuilder.create()
|
||||
.withCredentials("10001", privateKeyBase64)
|
||||
.withValidator(publicKeyBase64)
|
||||
.build();
|
||||
//=============基础配置===================
|
||||
URIBuilder urlBuilder = new URIBuilder()
|
||||
.setScheme("http")
|
||||
.setHost("localhost:8080")
|
||||
.setPath("/insure");
|
||||
RequestConfig requestConfig = RequestConfig.custom()
|
||||
.setSocketTimeout(1000)
|
||||
.setConnectTimeout(1000)
|
||||
.build();
|
||||
try {
|
||||
URI url = urlBuilder.build();
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
httpPost.setConfig(requestConfig);
|
||||
EncodeDTO encodeDTO = new EncodeDTO();
|
||||
JSONObject body = JSONUtil.parseObj(new HashMap<>() {
|
||||
{
|
||||
put("age", 19);
|
||||
}
|
||||
});
|
||||
encodeDTO.setBody(body.toString());
|
||||
if(new BoleeEncryptor(privateKeyBase64).encode(encodeDTO)){
|
||||
StringEntity stringEntity = new StringEntity(encodeDTO.getEncodeBody(), "utf-8");
|
||||
httpPost.setEntity(stringEntity);
|
||||
httpPost.addHeader(BoleeSecurityConstant.HEAD_NAME_BODY_KEY,encodeDTO.getHeadAESSecurityKey());
|
||||
httpPost.addHeader("Content-Type","application/json;charset=utf-8");
|
||||
CloseableHttpResponse apiRes = httpclient.execute(httpPost);
|
||||
HttpEntity entity = apiRes.getEntity();
|
||||
String content = EntityUtils.toString(entity);
|
||||
System.out.println("接收到了返回信息:"+content);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
23
sfbx-dict/dict-interface/pom.xml
Normal file
23
sfbx-dict/dict-interface/pom.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?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.itheima.sfbx</groupId>
|
||||
<artifactId>sfbx-dict</artifactId>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<!--数字字典接口模块-->
|
||||
<artifactId>dict-interface</artifactId>
|
||||
<name>dict-interface</name>
|
||||
<!-- FIXME change it to the project's website -->
|
||||
<url>http://www.example.com</url>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-feign</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.itheima.sfbx.dict.config;
|
||||
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @ClassName DictFeignConfig.java
|
||||
* @Description feign的最优化配置
|
||||
*/
|
||||
@Configuration
|
||||
@EnableFeignClients(basePackages = "com.itheima.sfbx.dict")
|
||||
public class DictFeignConfig {
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.itheima.sfbx.dict.feign;
|
||||
|
||||
import com.itheima.sfbx.dict.hystrix.DataDictHystrix;
|
||||
import com.itheima.sfbx.framework.commons.dto.dict.DataDictVO;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassName DataDictFace.java
|
||||
* @Description:数字字典feign接口类
|
||||
*/
|
||||
@FeignClient(value = "dict-web",fallback = DataDictHystrix.class)
|
||||
public interface DataDictFeign {
|
||||
|
||||
/**
|
||||
* @Description 根据parentKey获取value
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("data-dict-feign/parent-key/{parentKey}")
|
||||
List<DataDictVO> findDataDictVOByParentKey(@PathVariable("parentKey") String parentKey);
|
||||
|
||||
/**
|
||||
* @Description 根据dataKey获取DataDictVO
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("data-dict-feign/data-key/{dataKey}")
|
||||
DataDictVO findDataDictVOByDataKey(@PathVariable("dataKey") String dataKey) ;
|
||||
|
||||
/**
|
||||
* @Description 根据dataKey获取DataDictVO集合
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("data-dict-feign/findValueByDataKeys")
|
||||
List<DataDictVO> findValueByDataKeys(@RequestBody ArrayList<String> dataKeys);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.itheima.sfbx.dict.feign;
|
||||
|
||||
import com.itheima.sfbx.framework.commons.dto.dict.PlacesVO;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassName PlacesFeign.java
|
||||
* @Description 地区feign服务
|
||||
*/
|
||||
@FeignClient(value = "dict-web")
|
||||
public interface PlacesFeign {
|
||||
|
||||
/***
|
||||
* @description 查询下级
|
||||
* @param parentId 父层Id
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("places-fegin/{parentId}")
|
||||
public List<PlacesVO> findPlacesVOListByParentId(@PathVariable("parentId") Long parentId);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.itheima.sfbx.dict.hystrix;
|
||||
|
||||
import com.itheima.sfbx.dict.feign.DataDictFeign;
|
||||
import com.itheima.sfbx.framework.commons.dto.dict.DataDictVO;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassName DataDictHystrix.java
|
||||
* @Description DataDictFeign的Hystrix处理
|
||||
*/
|
||||
@Component
|
||||
public class DataDictHystrix implements DataDictFeign {
|
||||
|
||||
|
||||
@Override
|
||||
public List<DataDictVO> findDataDictVOByParentKey(String parentKey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataDictVO findDataDictVOByDataKey(String dataKey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DataDictVO> findValueByDataKeys(ArrayList<String> dataKeys) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.itheima.sfbx.dict.hystrix;
|
||||
|
||||
import com.itheima.sfbx.dict.feign.PlacesFeign;
|
||||
import com.itheima.sfbx.framework.commons.dto.dict.PlacesVO;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassName PlacesHystrix.java
|
||||
* @Description PlacesFeign的Hystrix
|
||||
*/
|
||||
@Component
|
||||
public class PlacesHystrix implements PlacesFeign {
|
||||
|
||||
@Override
|
||||
public List<PlacesVO> findPlacesVOListByParentId(Long parentId) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
21
sfbx-dict/dict-web/Dockerfile
Normal file
21
sfbx-dict/dict-web/Dockerfile
Normal file
@@ -0,0 +1,21 @@
|
||||
FROM openjdk:11-jdk
|
||||
LABEL maintainer="研究院研发组 <research@itcast.cn>"
|
||||
|
||||
# 时区修改为东八区
|
||||
ENV TZ=Asia/Shanghai
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
WORKDIR /dict-web
|
||||
ARG PACKAGE_PATH=./target/dict-web.jar
|
||||
ADD ${PACKAGE_PATH:-./} dict-web.jar
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ENV JAVA_OPTS="\
|
||||
-server \
|
||||
-Xms256m \
|
||||
-Xmx512m \
|
||||
-XX:MetaspaceSize=256m \
|
||||
-XX:MaxMetaspaceSize=512m\
|
||||
-Dspring.profiles.active=test"
|
||||
ENTRYPOINT ["sh","-c","java -Djava.security.egd=file:/dev/./urandom -jar $JAVA_OPTS dict-web.jar"]
|
||||
97
sfbx-dict/dict-web/pom.xml
Normal file
97
sfbx-dict/dict-web/pom.xml
Normal file
@@ -0,0 +1,97 @@
|
||||
<?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.itheima.sfbx</groupId>
|
||||
<artifactId>sfbx-dict</artifactId>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<!--数字字典web模块-->
|
||||
<artifactId>dict-web</artifactId>
|
||||
|
||||
<name>dict-web</name>
|
||||
<!-- FIXME change it to the project's website -->
|
||||
<url>http://www.example.com</url>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-seata</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-mybatis-plus</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-task-executor</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-knife4j-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<includes>
|
||||
<include>**/*.yml</include>
|
||||
<include>**/*.properties</include>
|
||||
<include>**/*.xml</include>
|
||||
<include>**/*.yaml</include>
|
||||
<include>**/*.txt</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
<finalName>dict-web</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.itheima.sfbx.dict;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
/**
|
||||
* 后台管理中的 系统管理-数字字典
|
||||
*/
|
||||
@SpringBootApplication(scanBasePackages = {"com.itheima.sfbx"})
|
||||
public class DictWebStart {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(DictWebStart.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.itheima.sfbx.dict.feign;
|
||||
|
||||
import com.itheima.sfbx.dict.service.IDataDictService;
|
||||
import com.itheima.sfbx.framework.commons.dto.dict.DataDictVO;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassName DataDictController.java
|
||||
* @Description 数字字典controller
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("data-dict-feign")
|
||||
@Api(tags = "数字字典feign")
|
||||
public class DataDictFeignController {
|
||||
|
||||
@Autowired
|
||||
IDataDictService dataDictService;
|
||||
|
||||
/**
|
||||
* @Description 父项键查询
|
||||
* @return List<DataDictVO>
|
||||
*/
|
||||
@PostMapping("parent-key/{parentKey}")
|
||||
@ApiOperation(value = "父项键查询",notes = "父项键查询")
|
||||
@ApiImplicitParam(paramType = "path",name = "parentKey",value = "字典parentKey",example = "URGE_TYPE",dataType = "String")
|
||||
List<DataDictVO> findDataDictVOByParentKey(@PathVariable("parentKey") String parentKey) {
|
||||
return dataDictService.findDataDictVOByParentKey(parentKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Description 子项键查询
|
||||
* @return DataDictVO
|
||||
*/
|
||||
@PostMapping("data-key/{dataKey}")
|
||||
@ApiOperation(value = "子项键查询",notes = "子项键查询")
|
||||
@ApiImplicitParam(paramType = "path",name = "dataKey",value = "字典dataKey",example = "URGE_TYPE",dataType = "String")
|
||||
DataDictVO findDataDictVOByDataKey(@PathVariable("dataKey")String dataKey){
|
||||
return dataDictService.findDataDictVOByDataKey(dataKey);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.itheima.sfbx.dict.feign;
|
||||
|
||||
import com.itheima.sfbx.dict.service.IPlacesService;
|
||||
import com.itheima.sfbx.framework.commons.dto.dict.PlacesVO;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassName PlacesFeignController.java
|
||||
* @Description 地区controller
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("places-fgign")
|
||||
@Api(tags = "地区fegin")
|
||||
public class PlacesFeignController {
|
||||
|
||||
@Autowired
|
||||
IPlacesService placesService;
|
||||
|
||||
/**
|
||||
* @Description 保存字典数据
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("{parentId}")
|
||||
@ApiOperation(value = "地区下拉框",notes = "地区下拉框")
|
||||
@ApiImplicitParam(name = "parentId",value = "父层id",required = true,dataType = "Long")
|
||||
public List<PlacesVO> findPlacesVOListByParentId(@PathVariable("parentId") Long parentId) {
|
||||
return placesService.findPlacesVOListByParentId(parentId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.itheima.sfbx.dict.init;
|
||||
|
||||
import com.itheima.sfbx.dict.service.IDataDictService;
|
||||
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.Set;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
/**
|
||||
* @ClassName InitDataDict.java
|
||||
* @Description 热加载数字字典
|
||||
*/
|
||||
@Component
|
||||
public class InitDataDict {
|
||||
|
||||
@Autowired
|
||||
IDataDictService dataDictService;
|
||||
|
||||
@Async
|
||||
@PostConstruct
|
||||
public void initDataDict(){
|
||||
Timer timer = new Timer();
|
||||
timer.schedule(new InitTask(timer),10*1000);
|
||||
}
|
||||
|
||||
class InitTask extends TimerTask{
|
||||
|
||||
private Timer timer;
|
||||
|
||||
private InitTask(Timer timer) {
|
||||
this.timer= timer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
//所有ParentKey的set集合
|
||||
Set<String> parentKeyAll = dataDictService.findParentKeyAll();
|
||||
if (EmptyUtil.isNullOrEmpty(parentKeyAll)){
|
||||
return;
|
||||
}
|
||||
//初始化父亲目录下所有有效状态的数据
|
||||
parentKeyAll.forEach(n->{
|
||||
dataDictService.findDataDictVOByParentKey(n);
|
||||
});
|
||||
//所有dataKey的set集合
|
||||
Set<String> dataKeyAll = dataDictService.findDataKeyAll();
|
||||
if (EmptyUtil.isNullOrEmpty(parentKeyAll)){
|
||||
return;
|
||||
}
|
||||
//初始化datakey对应有效状态的数据
|
||||
dataKeyAll.forEach(n->{
|
||||
dataDictService.findDataDictVOByDataKey(n);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.itheima.sfbx.dict.init;
|
||||
|
||||
import com.itheima.sfbx.dict.service.IPlacesService;
|
||||
import com.itheima.sfbx.framework.commons.constant.basic.SuperConstant;
|
||||
import com.itheima.sfbx.framework.commons.dto.dict.PlacesVO;
|
||||
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
/**
|
||||
* @ClassName InitPlaces.java
|
||||
* @Description 热加载区域
|
||||
*/
|
||||
@Component
|
||||
public class InitPlaces {
|
||||
|
||||
@Autowired
|
||||
IPlacesService placesService;
|
||||
|
||||
@Async
|
||||
@PostConstruct
|
||||
public void initPlaces(){
|
||||
Timer timer = new Timer();
|
||||
timer.schedule(new InitTask(timer),10*1000);
|
||||
}
|
||||
|
||||
class InitTask extends TimerTask {
|
||||
|
||||
private Timer timer;
|
||||
|
||||
private InitTask(Timer timer) {
|
||||
this.timer= timer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
//初始化省列表
|
||||
List<PlacesVO> provinces = placesService.findPlacesVOListByParentId(SuperConstant.CHINA_CODE);
|
||||
if (EmptyUtil.isNullOrEmpty(provinces)){
|
||||
return;
|
||||
}
|
||||
//初始化市列表
|
||||
provinces.forEach(n->{
|
||||
List<PlacesVO> citys = placesService.findPlacesVOListByParentId(n.getId());
|
||||
if (EmptyUtil.isNullOrEmpty(citys)){
|
||||
return;
|
||||
}
|
||||
//初始化区列表
|
||||
citys.forEach(k->{
|
||||
placesService.findPlacesVOListByParentId(k.getId());
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.itheima.sfbx.dict.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.itheima.sfbx.dict.pojo.DataDict;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* @Description:数据字典表Mapper接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface DataDictMapper extends BaseMapper<DataDict> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.itheima.sfbx.dict.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.itheima.sfbx.dict.pojo.Places;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* @Description:地方表Mapper接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface PlacesMapper extends BaseMapper<Places> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.itheima.sfbx.dict.pojo;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.itheima.sfbx.framework.mybatisplus.basic.BasePojo;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @Description:数据字典表
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("tab_data_dict")
|
||||
@ApiModel(value="DataDict对象", description="数据字典表")
|
||||
public class DataDict extends BasePojo {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Builder
|
||||
public DataDict(Long id, String dataState, String parentKey, String dataKey, String dataValue, String discription) {
|
||||
super(id, dataState);
|
||||
this.parentKey = parentKey;
|
||||
this.dataKey = dataKey;
|
||||
this.dataValue = dataValue;
|
||||
this.discription = discription;
|
||||
}
|
||||
|
||||
@ApiModelProperty(value = "父key")
|
||||
private String parentKey;
|
||||
|
||||
@ApiModelProperty(value = "数据字典KEY")
|
||||
private String dataKey;
|
||||
|
||||
@ApiModelProperty(value = "值")
|
||||
private String dataValue;
|
||||
|
||||
@ApiModelProperty(value = "描述")
|
||||
private String discription;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.itheima.sfbx.dict.pojo;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.itheima.sfbx.framework.mybatisplus.basic.BasePojo;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @Description:地方表
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("tab_places")
|
||||
@ApiModel(value="Places对象", description="地方表")
|
||||
public class Places extends BasePojo {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Builder
|
||||
public Places(Long id, String dataState, Long parentId, String cityName) {
|
||||
super(id, dataState);
|
||||
this.parentId = parentId;
|
||||
this.cityName = cityName;
|
||||
}
|
||||
|
||||
@ApiModelProperty(value = "父ID")
|
||||
private Long parentId;
|
||||
|
||||
@ApiModelProperty(value = "名称")
|
||||
private String cityName;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.itheima.sfbx.dict.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.itheima.sfbx.dict.pojo.DataDict;
|
||||
import com.itheima.sfbx.framework.commons.dto.dict.DataDictVO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @Description:数据字典表 服务类
|
||||
*/
|
||||
public interface IDataDictService extends IService<DataDict> {
|
||||
|
||||
/***
|
||||
* @description 数据字典列表数据
|
||||
*
|
||||
* @param dataDictVO 查询条件
|
||||
* @param pageNum 页码
|
||||
* @param pageSize 每页条数
|
||||
* @return: Page<DataDictVO>
|
||||
*/
|
||||
Page<DataDictVO> findDataDictVOPage(DataDictVO dataDictVO, int pageNum, int pageSize);
|
||||
|
||||
/**
|
||||
* @Description 检测key是否已经存在
|
||||
* @return
|
||||
*/
|
||||
Boolean checkByDataKey(String dataKey);
|
||||
|
||||
/**
|
||||
* @Description 保存字典数据
|
||||
* @return
|
||||
*/
|
||||
DataDictVO saveDataDict(DataDictVO dataDictVO) ;
|
||||
|
||||
/**
|
||||
* @Description 修改字典数据
|
||||
* @return
|
||||
*/
|
||||
DataDictVO updateDataDict(DataDictVO dataDictVO);
|
||||
|
||||
/**
|
||||
* @Description 根据dataKey获取value
|
||||
* @return DataDictVO
|
||||
*/
|
||||
DataDictVO findDataDictVOByDataKey(String dataKey);
|
||||
|
||||
/**
|
||||
* @Description 获得所有不重复的ParentKey的set集合
|
||||
* @return Set<String>
|
||||
*/
|
||||
Set<String> findParentKeyAll();
|
||||
|
||||
/**
|
||||
* @Description 获取父key下的数据
|
||||
* @return List<DataDictVO>
|
||||
*/
|
||||
List<DataDictVO> findDataDictVOByParentKey(String parentKey);
|
||||
|
||||
/**
|
||||
* @Description 获得所有不重复的DataKey的set集合
|
||||
* @return Set<String>
|
||||
*/
|
||||
Set<String> findDataKeyAll();
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.itheima.sfbx.dict.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.itheima.sfbx.dict.pojo.Places;
|
||||
import com.itheima.sfbx.framework.commons.constant.dict.PlacesCacheConstant;
|
||||
import com.itheima.sfbx.framework.commons.dto.dict.PlacesVO;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Description:地方表 服务类
|
||||
*/
|
||||
public interface IPlacesService extends IService<Places> {
|
||||
|
||||
/***
|
||||
* @description 查询下级
|
||||
* @param parentId
|
||||
* @return: List<Places>
|
||||
*/
|
||||
List<PlacesVO> findPlacesVOListByParentId(Long parentId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
package com.itheima.sfbx.dict.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.itheima.sfbx.dict.mapper.DataDictMapper;
|
||||
import com.itheima.sfbx.dict.pojo.DataDict;
|
||||
import com.itheima.sfbx.dict.service.IDataDictService;
|
||||
import com.itheima.sfbx.framework.commons.constant.basic.SuperConstant;
|
||||
import com.itheima.sfbx.framework.commons.constant.dict.DataDictCacheConstant;
|
||||
import com.itheima.sfbx.framework.commons.dto.dict.DataDictVO;
|
||||
import com.itheima.sfbx.framework.commons.enums.dict.DataDictEnum;
|
||||
import com.itheima.sfbx.framework.commons.exception.ProjectException;
|
||||
import com.itheima.sfbx.framework.commons.utils.BeanConv;
|
||||
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
|
||||
import com.itheima.sfbx.framework.commons.utils.ExceptionsUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.CachePut;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.cache.annotation.Caching;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @Description:数据字典表 服务实现类
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class DataDictServiceImpl extends ServiceImpl<DataDictMapper, DataDict> implements IDataDictService {
|
||||
|
||||
/***
|
||||
* @description 构建多条件查询条件
|
||||
* @param dataDictVO 查询条件
|
||||
* @return 查询条件
|
||||
*/
|
||||
private QueryWrapper queryWrapper(DataDictVO dataDictVO){
|
||||
QueryWrapper<DataDict> queryWrapper = new QueryWrapper<>();
|
||||
//多条件查询
|
||||
if (!EmptyUtil.isNullOrEmpty(dataDictVO.getDiscription())){
|
||||
queryWrapper.lambda().like(DataDict::getDiscription,dataDictVO.getDiscription());
|
||||
}
|
||||
if (!EmptyUtil.isNullOrEmpty(dataDictVO.getParentKey())){
|
||||
queryWrapper.lambda().like(DataDict::getParentKey,dataDictVO.getParentKey());
|
||||
}
|
||||
if (!EmptyUtil.isNullOrEmpty(dataDictVO.getDataKey())){
|
||||
queryWrapper.lambda().like(DataDict::getDataKey,dataDictVO.getDataKey());
|
||||
}
|
||||
if (!EmptyUtil.isNullOrEmpty(dataDictVO.getDataValue())){
|
||||
queryWrapper.lambda().like(DataDict::getDataValue,dataDictVO.getDataValue());
|
||||
}
|
||||
if (!EmptyUtil.isNullOrEmpty(dataDictVO.getDataState())){
|
||||
queryWrapper.lambda().eq(DataDict::getDataState,dataDictVO.getDataState());
|
||||
}
|
||||
queryWrapper.lambda().orderByDesc(DataDict::getCreateTime).orderByAsc(DataDict::getDataKey);
|
||||
return queryWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = DataDictCacheConstant.PAGE,key ="#pageNum+'-'+#pageSize+'-'+#dataDictVO.hashCode()")
|
||||
public Page<DataDictVO> findDataDictVOPage(DataDictVO dataDictVO, int pageNum, int pageSize) {
|
||||
try {
|
||||
Page<DataDict> page = new Page<>(pageNum, pageSize);
|
||||
QueryWrapper<DataDict> queryWrapper = this.queryWrapper(dataDictVO);
|
||||
return BeanConv.toPage(page(page, queryWrapper),DataDictVO.class);
|
||||
}catch (Exception e){
|
||||
log.error("查询数据字典列表异常:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(DataDictEnum.PAGE_FAIL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean checkByDataKey(String dataKey) {
|
||||
try {
|
||||
QueryWrapper<DataDict> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.lambda().eq(DataDict::getDataKey,dataKey);
|
||||
return !EmptyUtil.isNullOrEmpty(getOne(queryWrapper));
|
||||
}catch (Exception e){
|
||||
log.error("检查数字字典重复性异常:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(DataDictEnum.CHECK_FALL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Caching(
|
||||
evict={@CacheEvict(value = DataDictCacheConstant.PAGE,allEntries = true),
|
||||
@CacheEvict(value = DataDictCacheConstant.PARENT_KEY,key = "#dataDictVO.parentKey")},
|
||||
put={@CachePut(value = DataDictCacheConstant.DATA_KEY,key = "#dataDictVO.dataKey")})
|
||||
@Transactional
|
||||
public DataDictVO saveDataDict(DataDictVO dataDictVO) {
|
||||
try {
|
||||
DataDict dataDict =BeanConv.toBean(dataDictVO,DataDict.class);
|
||||
boolean flag = save(dataDict);
|
||||
if (flag){
|
||||
return BeanConv.toBean(dataDict,DataDictVO.class);
|
||||
}else {
|
||||
log.error("数据字典保存异常!");
|
||||
throw new ProjectException(DataDictEnum.SAVE_FAIL);
|
||||
}
|
||||
}catch (Exception e){
|
||||
log.error("数据字典保存异常:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(DataDictEnum.SAVE_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Caching(
|
||||
evict={@CacheEvict(value = DataDictCacheConstant.PAGE,allEntries = true),
|
||||
@CacheEvict(value = DataDictCacheConstant.PARENT_KEY,key = "#dataDictVO.parentKey"),
|
||||
@CacheEvict(value = DataDictCacheConstant.DATA_KEY,key = "#dataDictVO.dataKey")})
|
||||
@Transactional
|
||||
public DataDictVO updateDataDict(DataDictVO dataDictVO) {
|
||||
try {
|
||||
DataDict dataDict = BeanConv.toBean(dataDictVO, DataDict.class);
|
||||
DataDict dataDictTemp = getById(dataDictVO.getId());
|
||||
dataDict.setCreateBy(dataDictTemp.getCreateBy());
|
||||
dataDict.setCreateTime(dataDictTemp.getCreateTime());
|
||||
boolean flag = updateById(dataDict);
|
||||
if (flag){
|
||||
return BeanConv.toBean(dataDict, DataDictVO.class);
|
||||
}else {
|
||||
log.error("修改数据字典列表异常!");
|
||||
throw new ProjectException(DataDictEnum.UPDATE_FAIL);
|
||||
}
|
||||
}catch (Exception e){
|
||||
log.error("修改数据字典列表异常:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(DataDictEnum.UPDATE_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> findParentKeyAll() {
|
||||
try {
|
||||
QueryWrapper<DataDict> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.lambda().eq(DataDict::getDataState, SuperConstant.DATA_STATE_0);
|
||||
List<DataDict> list = list(queryWrapper);
|
||||
return EmptyUtil.isNullOrEmpty(list)?null:list.stream().map(DataDict::getParentKey).collect(Collectors.toSet());
|
||||
}catch (Exception e){
|
||||
log.error("查询数据字典列表异常:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(DataDictEnum.FIND_PARENTKEY_ALL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = DataDictCacheConstant.PARENT_KEY,key = "#parentKey")
|
||||
public List<DataDictVO> findDataDictVOByParentKey(String parentKey) {
|
||||
try {
|
||||
QueryWrapper<DataDict> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.lambda().eq(DataDict::getParentKey,parentKey)
|
||||
.eq(DataDict::getDataState, SuperConstant.DATA_STATE_0);
|
||||
return BeanConv.toBeanList(list(queryWrapper),DataDictVO.class);
|
||||
}catch (Exception e){
|
||||
log.error("查询数据字典列表异常:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(DataDictEnum.FIND_DATADICTVO_PARENTKEY);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> findDataKeyAll() {
|
||||
try {
|
||||
QueryWrapper<DataDict> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.lambda().eq(DataDict::getDataState, SuperConstant.DATA_STATE_0);
|
||||
List<DataDict> list = list(queryWrapper);
|
||||
return EmptyUtil.isNullOrEmpty(list)?null:list.stream().map(DataDict::getDataKey).collect(Collectors.toSet());
|
||||
}catch (Exception e){
|
||||
log.error("查询数据字典列表异常:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(DataDictEnum.FIND_DATAKEY_ALL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = DataDictCacheConstant.DATA_KEY,key = "#dataKey")
|
||||
public DataDictVO findDataDictVOByDataKey(String dataKey) {
|
||||
try {
|
||||
QueryWrapper<DataDict> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.lambda().eq(DataDict::getDataKey,dataKey)
|
||||
.eq(DataDict::getDataState, SuperConstant.DATA_STATE_0);
|
||||
return BeanConv.toBean(getOne(queryWrapper),DataDictVO.class);
|
||||
}catch (Exception e){
|
||||
log.error("查询数据字典列表异常:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(DataDictEnum.FIND_DATADICTVO_DATAKEY);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.itheima.sfbx.dict.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.itheima.sfbx.dict.mapper.PlacesMapper;
|
||||
import com.itheima.sfbx.dict.pojo.Places;
|
||||
import com.itheima.sfbx.dict.service.IPlacesService;
|
||||
import com.itheima.sfbx.framework.commons.constant.dict.PlacesCacheConstant;
|
||||
import com.itheima.sfbx.framework.commons.dto.dict.PlacesVO;
|
||||
import com.itheima.sfbx.framework.commons.enums.dict.PlacesEnum;
|
||||
import com.itheima.sfbx.framework.commons.exception.ProjectException;
|
||||
import com.itheima.sfbx.framework.commons.utils.BeanConv;
|
||||
import com.itheima.sfbx.framework.commons.utils.ExceptionsUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Description:地方表 服务实现类
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class PlacesServiceImpl extends ServiceImpl<PlacesMapper, Places> implements IPlacesService {
|
||||
|
||||
@Override
|
||||
@Cacheable(value = PlacesCacheConstant.LIST,key = "#parentId")
|
||||
public List<PlacesVO> findPlacesVOListByParentId(Long parentId) {
|
||||
try {
|
||||
QueryWrapper<Places> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.lambda().eq(Places::getParentId,parentId);
|
||||
return BeanConv.toBeanList(list(queryWrapper),PlacesVO.class);
|
||||
}catch (Exception e){
|
||||
log.error("查询parentId下列表异常:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(PlacesEnum.LIST_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.itheima.sfbx.dict.web;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||
import com.itheima.sfbx.dict.service.IDataDictService;
|
||||
import com.itheima.sfbx.framework.commons.basic.ResponseResult;
|
||||
import com.itheima.sfbx.framework.commons.dto.dict.DataDictVO;
|
||||
import com.itheima.sfbx.framework.commons.utils.ResponseResultBuild;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassName DataDictController.java
|
||||
* @Description 数字字典controller
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("data-dict")
|
||||
@Api(tags = "数字字典")
|
||||
public class DataDictController {
|
||||
|
||||
@Autowired
|
||||
IDataDictService dataDictService;
|
||||
/***
|
||||
* @description 数据字典分页
|
||||
* @param dataDictVO 查询条件
|
||||
* @param pageNum 页码
|
||||
* @param pageSize 每页条数
|
||||
* @return: Page<DataDictVO>
|
||||
*/
|
||||
@PostMapping("page/{pageNum}/{pageSize}")
|
||||
@ApiOperation(value = "数据字典分页",notes = "数据字典分页")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "dataDictVO",value = "字典查询对象",required = false,dataType = "DataDictVO"),
|
||||
@ApiImplicitParam(paramType = "path",name = "pageNum",value = "页码",example = "1",dataType = "Integer"),
|
||||
@ApiImplicitParam(paramType = "path",name = "pageSize",value = "每页条数",example = "10",dataType = "Integer")
|
||||
})
|
||||
@ApiOperationSupport(includeParameters ={"dataDictVO.parentKey","","dataDictVO.dataKey",
|
||||
"dataDictVO.dataValue","dataDictVO.discription"} )
|
||||
ResponseResult<Page<DataDictVO>> findDataDictVOPage(
|
||||
@RequestBody DataDictVO dataDictVO,
|
||||
@PathVariable("pageNum") int pageNum,
|
||||
@PathVariable("pageSize") int pageSize) {
|
||||
Page<DataDictVO> dataDictVOPage = dataDictService.findDataDictVOPage(dataDictVO, pageNum, pageSize);
|
||||
return ResponseResultBuild.successBuild(dataDictVOPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Description 保存字典数据
|
||||
* @return
|
||||
*/
|
||||
@PostMapping
|
||||
@ApiOperation(value = "数字字典添加",notes = "数字字典添加")
|
||||
@ApiImplicitParam(name = "dataDictVO",value = "字典信息",required = true,dataType = "DataDictVO")
|
||||
@ApiOperationSupport(ignoreParameters ={"dataDictVO.id", "dataDictVO.updateTime",
|
||||
"dataDictVO.createBy","dataDictVO.updateBy", "dataDictVO.creator"} )
|
||||
public ResponseResult<DataDictVO> saveDataDict(@RequestBody DataDictVO dataDictVO) {
|
||||
DataDictVO dataDictVOResult = dataDictService.saveDataDict(dataDictVO);
|
||||
return ResponseResultBuild.successBuild(dataDictVOResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Description 修改字典数据
|
||||
* @return
|
||||
*/
|
||||
@PatchMapping
|
||||
@ApiOperation(value = "数字字典编辑",notes = "数字字典编辑")
|
||||
@ApiImplicitParam(name = "dataDictVO",value = "字典信息",required = true,dataType = "DataDictVO")
|
||||
@ApiOperationSupport(ignoreParameters ={"dataDictVO.updateTime", "dataDictVO.createBy",
|
||||
"dataDictVO.updateBy", "dataDictVO.creator"})
|
||||
public ResponseResult<DataDictVO> updateDataDict(@RequestBody DataDictVO dataDictVO) {
|
||||
DataDictVO dataDictVOResult = dataDictService.updateDataDict(dataDictVO);
|
||||
return ResponseResultBuild.successBuild(dataDictVOResult);
|
||||
}
|
||||
|
||||
@PostMapping("data-state")
|
||||
@ApiOperation(value = "数字字典状态编辑",notes = "数字字典状态编辑")
|
||||
@ApiImplicitParam(name = "dataDictVO",value = "字典信息",required = true,dataType = "DataDictVO")
|
||||
@ApiOperationSupport(ignoreParameters ={"dataDictVO.id", "dataDictVO.dataState"})
|
||||
ResponseResult<DataDictVO> updateDataDictEnableFlag(@RequestBody DataDictVO dataDictVO) {
|
||||
DataDictVO dataDictVOResult = dataDictService.updateDataDict(dataDictVO);
|
||||
return ResponseResultBuild.successBuild(dataDictVOResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Description 父项键查询
|
||||
* @return List<DataDictVO>
|
||||
*/
|
||||
@PostMapping("parent-key/{parentKey}")
|
||||
@ApiOperation(value = "父项键查询",notes = "父项键查询")
|
||||
@ApiImplicitParam(paramType = "path",name = "parentKey",value = "字典parentKey",example = "URGE_TYPE",dataType = "String")
|
||||
ResponseResult<List<DataDictVO>> findDataDictVOByParentKey(@PathVariable("parentKey") String parentKey) {
|
||||
return ResponseResultBuild.successBuild(dataDictService.findDataDictVOByParentKey(parentKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Description 子项键查询
|
||||
* @return DataDictVO
|
||||
*/
|
||||
@PostMapping("data-key/{dataKey}")
|
||||
@ApiOperation(value = "子项键查询",notes = "子项键查询")
|
||||
@ApiImplicitParam(paramType = "path",name = "dataKey",value = "字典dataKey",example = "URGE_TYPE",dataType = "String")
|
||||
ResponseResult<DataDictVO> findDataDictVOByDataKey(@PathVariable("dataKey")String dataKey){
|
||||
return ResponseResultBuild.successBuild(dataDictService.findDataDictVOByDataKey(dataKey));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.itheima.sfbx.dict.web;
|
||||
|
||||
import com.itheima.sfbx.dict.service.IPlacesService;
|
||||
import com.itheima.sfbx.framework.commons.basic.ResponseResult;
|
||||
import com.itheima.sfbx.framework.commons.dto.dict.PlacesVO;
|
||||
import com.itheima.sfbx.framework.commons.utils.ResponseResultBuild;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassName PlacesFeignController.java
|
||||
* @Description 地区controller
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("places")
|
||||
@Api(tags = "地区")
|
||||
public class PlacesController {
|
||||
|
||||
@Autowired
|
||||
IPlacesService placesService;
|
||||
|
||||
/**
|
||||
* @Description 保存字典数据
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("{parentId}")
|
||||
@ApiOperation(value = "地区下拉框",notes = "地区下拉框")
|
||||
@ApiImplicitParam(name = "parentId",value = "父层id",required = true,dataType = "Long")
|
||||
public ResponseResult<List<PlacesVO>> findPlacesVOListByParentId(@PathVariable("parentId") Long parentId) {
|
||||
List<PlacesVO> placesVOList = placesService.findPlacesVOListByParentId(parentId);
|
||||
return ResponseResultBuild.successBuild(placesVOList);
|
||||
}
|
||||
}
|
||||
10
sfbx-dict/dict-web/src/main/resources/banner.txt
Normal file
10
sfbx-dict/dict-web/src/main/resources/banner.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
_ __ __
|
||||
(_) /__________ ______/ /_
|
||||
/ / __/ ___/ __ `/ ___/ __/
|
||||
/ / /_/ /__/ /_/ (__ ) /_
|
||||
/_/\__/\___/\__,_/____/\__/
|
||||
:: Spring Boot :: (v-2.7.10)
|
||||
:: Spring Cloud :: (v-2021.0.6)
|
||||
:: Spring Cloud Alibaba :: (v-2021.0.1.0)
|
||||
:: sfbx Cloud :: (v-2.0-SNAPSHOT)
|
||||
:: 献给可爱的传智人 ::
|
||||
53
sfbx-dict/dict-web/src/main/resources/bootstrap-test.yml
Normal file
53
sfbx-dict/dict-web/src/main/resources/bootstrap-test.yml
Normal file
@@ -0,0 +1,53 @@
|
||||
#服务配置
|
||||
server:
|
||||
#端口
|
||||
port: 7071
|
||||
#服务编码
|
||||
tomcat:
|
||||
uri-encoding: UTF-8
|
||||
spring:
|
||||
# profiles:
|
||||
# active: test
|
||||
config:
|
||||
activate:
|
||||
on-profile:
|
||||
- test
|
||||
main:
|
||||
allow-circular-references: true
|
||||
allow-bean-definition-overriding: true
|
||||
mvc:
|
||||
pathmatch:
|
||||
matching-strategy: ant_path_matcher
|
||||
#应用配置
|
||||
application:
|
||||
#应用名称
|
||||
name: dict-web
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: ${NACOS_ADDRESS:nacos-service.yjy-public-sfbx-java.svc.cluster.local:20015} # nacos注册中心
|
||||
group: SEATA_GROUP
|
||||
service: ${spring.application.name}
|
||||
username: ${NACOS_USERNAME:nacos}
|
||||
password: ${NACOS_PASSWORD:PKsf*bxQ4;yP3a+}
|
||||
config:
|
||||
server-addr: ${NACOS_ADDRESS:nacos-service.yjy-public-sfbx-java.svc.cluster.local:20015} # nacos配置中心地址
|
||||
group: SEATA_GROUP
|
||||
file-extension: yml
|
||||
shared-configs: # 共享配置
|
||||
- data-id: shared-spring-seata.yml #配置文件名-DataId
|
||||
group: SEATA_GROUP
|
||||
refresh: false
|
||||
- data-id: shared-spring-task.yml #配置文件名-DataId
|
||||
group: SEATA_GROUP
|
||||
refresh: false
|
||||
- data-id: shared-redisson.yml #配置文件名-DataId
|
||||
group: SEATA_GROUP
|
||||
refresh: false
|
||||
- data-id: shared-mybatis-plus.yml #配置文件名-DataId
|
||||
group: SEATA_GROUP
|
||||
refresh: false
|
||||
username: ${NACOS_USERNAME:nacos}
|
||||
password: ${NACOS_PASSWORD:PKsf*bxQ4;yP3a+}
|
||||
logging:
|
||||
config: classpath:logback.xml
|
||||
45
sfbx-dict/dict-web/src/main/resources/bootstrap.yml
Normal file
45
sfbx-dict/dict-web/src/main/resources/bootstrap.yml
Normal file
@@ -0,0 +1,45 @@
|
||||
#服务配置
|
||||
server:
|
||||
#端口
|
||||
port: 7071
|
||||
#服务编码
|
||||
tomcat:
|
||||
uri-encoding: UTF-8
|
||||
spring:
|
||||
profiles:
|
||||
active: dev
|
||||
main:
|
||||
allow-circular-references: true
|
||||
allow-bean-definition-overriding: true
|
||||
mvc:
|
||||
pathmatch:
|
||||
matching-strategy: ant_path_matcher
|
||||
#应用配置
|
||||
application:
|
||||
#应用名称
|
||||
name: dict-web
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 192.168.12.129:8848 # nacos注册中心
|
||||
group: SEATA_GROUP
|
||||
service: ${spring.application.name}
|
||||
config:
|
||||
server-addr: 192.168.12.129:8848 # nacos配置中心地址
|
||||
group: SEATA_GROUP
|
||||
file-extension: yml
|
||||
shared-configs: # 共享配置
|
||||
- data-id: shared-spring-seata.yml #配置文件名-DataId
|
||||
group: SEATA_GROUP
|
||||
refresh: false
|
||||
- data-id: shared-spring-task.yml #配置文件名-DataId
|
||||
group: SEATA_GROUP
|
||||
refresh: false
|
||||
- data-id: shared-redisson.yml #配置文件名-DataId
|
||||
group: SEATA_GROUP
|
||||
refresh: false
|
||||
- data-id: shared-mybatis-plus.yml #配置文件名-DataId
|
||||
group: SEATA_GROUP
|
||||
refresh: false
|
||||
logging:
|
||||
config: classpath:logback.xml
|
||||
33
sfbx-dict/dict-web/src/main/resources/generrator.properties
Normal file
33
sfbx-dict/dict-web/src/main/resources/generrator.properties
Normal file
@@ -0,0 +1,33 @@
|
||||
#\u6570\u636E\u5E93\u5730\u5740
|
||||
url=jdbc:mysql://192.168.12.129:3306/restkeeper-dict?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&tinyInt1isBit=false
|
||||
#\u6570\u636E\u5E93\u8D26\u53F7
|
||||
userName=root
|
||||
#\u6570\u636E\u5E93\u5BC6\u7801
|
||||
password=pass
|
||||
#\u6B64\u5904\u4E3A\u672C\u9879\u76EEsrc\u6240\u5728\u8DEF\u5F84\uFF08\u4EE3\u7801\u751F\u6210\u5668\u8F93\u51FA\u8DEF\u5F84\uFF09
|
||||
serviceProjectPath=D:/easy-cloud/easy-dict/dict-web
|
||||
#\u8BBE\u7F6E\u4F5C\u8005
|
||||
author=Admin
|
||||
#\u81EA\u5B9A\u4E49\u5305\u8DEF\u5F84
|
||||
parent=com.itheima
|
||||
#\u88C5\u4EE3\u7801\u7684\u6587\u4EF6\u5939\u540D
|
||||
moduleName=project
|
||||
#\u8BBE\u7F6E\u8868\u524D\u7F00\uFF0C\u4E0D\u8BBE\u7F6E\u5219\u9ED8\u8BA4\u65E0\u524D\u7F00
|
||||
tablePrefix =tab_
|
||||
#\u6570\u636E\u5E93\u8868\u540D(\u6B64\u5904\u5207\u4E0D\u53EF\u4E3A\u7A7A\uFF0C\u5982\u679C\u4E3A\u7A7A\uFF0C\u5219\u9ED8\u8BA4\u8BFB\u53D6\u6570\u636E\u5E93\u7684\u6240\u6709\u8868\u540D)
|
||||
tableName=tab_data_dict,tab_places
|
||||
#pojo\u7684\u8D85\u7C7B
|
||||
SuperEntityClass = com.itheima.sfbx.framework.commons.basic.BasicPojo
|
||||
#pojo\u7684\u8D85\u7C7B\u516C\u7528\u5B57\u6BB5
|
||||
superEntityColumns = id,created_time,updated_time,sharding_id,enable_flag
|
||||
#\u751F\u6210\u7684\u5C42\u7EA7
|
||||
entity=true
|
||||
entity.ftl.path=/templates/entity.java
|
||||
mapper=true
|
||||
mapper.ftl.path=/templates/mapper.java
|
||||
service=false
|
||||
service.ftl.path=/templates/service.java
|
||||
serviceImp=false
|
||||
serviceImp.ftl.path=/templates/serviceImpl.java
|
||||
controller=false
|
||||
controller.ftl.path=/templates/controller.java
|
||||
53
sfbx-dict/dict-web/src/main/resources/logback.xml
Normal file
53
sfbx-dict/dict-web/src/main/resources/logback.xml
Normal file
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration debug="false">
|
||||
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径 -->
|
||||
<property name="LOG_HOME" value="/data/logs/dict-web" />
|
||||
<!-- 控制台输出 -->
|
||||
<appender name="STDOUT" 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}-%msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<!-- 按照每天生成日志文件 -->
|
||||
<appender name="FILE"
|
||||
class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!--日志文件输出的文件名 -->
|
||||
<FileNamePattern>${LOG_HOME}/dict-web-01.log.%d{yyyy-MM-dd}.log
|
||||
</FileNamePattern>
|
||||
<!--日志文件保留天数 -->
|
||||
<MaxHistory>30</MaxHistory>
|
||||
</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>
|
||||
</encoder>
|
||||
<!--日志文件最大的大小 -->
|
||||
<triggeringPolicy
|
||||
class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
|
||||
<MaxFileSize>10MB</MaxFileSize>
|
||||
</triggeringPolicy>
|
||||
</appender>
|
||||
<!-- show parameters for hibernate sql 专为 Hibernate 定制 -->
|
||||
<logger name="org.hibernate.type.descriptor.sql.BasicBinder"
|
||||
level="TRACE" />
|
||||
<logger name="org.hibernate.type.descriptor.sql.BasicExtractor"
|
||||
level="DEBUG" />
|
||||
<logger name="org.hibernate.SQL" level="DEBUG" />
|
||||
<logger name="org.hibernate.engine.QueryParameters" level="DEBUG" />
|
||||
<logger name="org.hibernate.engine.query.HQLQueryPlan" level="DEBUG" />
|
||||
|
||||
<!--myibatis log configure -->
|
||||
<logger name="com.apache.ibatis" level="TRACE" />
|
||||
<logger name="java.sql.Connection" level="DEBUG" />
|
||||
<logger name="java.sql.Statement" level="DEBUG" />
|
||||
<logger name="java.sql.PreparedStatement" level="DEBUG" />
|
||||
|
||||
<!-- 日志输出级别 -->
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
<appender-ref ref="FILE" />
|
||||
</root>
|
||||
<!--日志异步到数据库 -->
|
||||
</configuration>
|
||||
@@ -0,0 +1,34 @@
|
||||
package ${package.Controller};
|
||||
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
<#if restControllerStyle>
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
<#else>
|
||||
import org.springframework.stereotype.Controller;
|
||||
</#if>
|
||||
<#if superControllerClassPackage??>
|
||||
import ${superControllerClassPackage};
|
||||
</#if>
|
||||
|
||||
/**
|
||||
* @Description:${table.comment!} 前端控制器
|
||||
*/
|
||||
<#if restControllerStyle>
|
||||
@RestController
|
||||
<#else>
|
||||
@Controller
|
||||
</#if>
|
||||
@RequestMapping("<#if package.ModuleName??>/${package.ModuleName}</#if>/<#if controllerMappingHyphenStyle??>${controllerMappingHyphen}<#else>${table.entityPath}</#if>")
|
||||
<#if kotlin>
|
||||
class ${table.controllerName}<#if superControllerClass??> : ${superControllerClass}()</#if>
|
||||
<#else>
|
||||
<#if superControllerClass??>
|
||||
public class ${table.controllerName} extends ${superControllerClass} {
|
||||
<#else>
|
||||
public class ${table.controllerName} {
|
||||
</#if>
|
||||
|
||||
}
|
||||
</#if>
|
||||
156
sfbx-dict/dict-web/src/main/resources/templates/entity.java.ftl
Normal file
156
sfbx-dict/dict-web/src/main/resources/templates/entity.java.ftl
Normal file
@@ -0,0 +1,156 @@
|
||||
package ${package.Entity};
|
||||
|
||||
<#list table.importPackages as pkg>
|
||||
import ${pkg};
|
||||
</#list>
|
||||
<#if swagger2>
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
</#if>
|
||||
<#if entityLombokModel>
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
</#if>
|
||||
|
||||
/**
|
||||
* @Description:${table.comment!}
|
||||
*/
|
||||
<#if entityLombokModel>
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
<#if superEntityClass??>
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
<#else>
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
</#if>
|
||||
</#if>
|
||||
<#if table.convert>
|
||||
@TableName("${table.name}")
|
||||
</#if>
|
||||
<#if swagger2>
|
||||
@ApiModel(value="${entity}对象", description="${table.comment!}")
|
||||
</#if>
|
||||
<#if superEntityClass??>
|
||||
public class ${entity} extends ${superEntityClass}<#if activeRecord><${entity}></#if> {
|
||||
<#elseif activeRecord>
|
||||
public class ${entity} extends Model<${entity}> {
|
||||
<#else>
|
||||
public class ${entity} implements Serializable {
|
||||
</#if>
|
||||
|
||||
<#if entitySerialVersionUID>
|
||||
private static final long serialVersionUID = 1L;
|
||||
</#if>
|
||||
|
||||
@Builder
|
||||
public ${entity}(Long id,<#list table.fields as field>${field.propertyType} ${field.propertyName}<#if field_has_next>,</#if></#list>){
|
||||
super(id);
|
||||
<#list table.fields as field>
|
||||
this.${field.propertyName}=${field.propertyName};
|
||||
</#list>
|
||||
}
|
||||
<#-- ---------- BEGIN 字段循环遍历 ---------->
|
||||
<#list table.fields as field>
|
||||
<#if field.keyFlag>
|
||||
<#assign keyPropertyName="${field.propertyName}"/>
|
||||
</#if>
|
||||
|
||||
<#if field.comment!?length gt 0>
|
||||
<#if swagger2>
|
||||
@ApiModelProperty(value = "${field.comment}")
|
||||
<#else>
|
||||
/**
|
||||
* ${field.comment}
|
||||
*/
|
||||
</#if>
|
||||
</#if>
|
||||
<#if field.keyFlag>
|
||||
<#-- 主键 -->
|
||||
<#if field.keyIdentityFlag>
|
||||
@TableId(value = "${field.name}", type = IdType.AUTO)
|
||||
<#elseif idType??>
|
||||
@TableId(value = "${field.name}", type = IdType.${idType})
|
||||
<#elseif field.convert>
|
||||
@TableId("${field.name}")
|
||||
</#if>
|
||||
<#-- 普通字段 -->
|
||||
<#elseif field.fill??>
|
||||
<#-- ----- 存在字段填充设置 ----->
|
||||
<#if field.convert>
|
||||
@TableField(value = "${field.name}", fill = FieldFill.${field.fill})
|
||||
<#else>
|
||||
@TableField(fill = FieldFill.${field.fill})
|
||||
</#if>
|
||||
<#elseif field.convert>
|
||||
@TableField("${field.name}")
|
||||
</#if>
|
||||
<#-- 乐观锁注解 -->
|
||||
<#if (versionFieldName!"") == field.name>
|
||||
@Version
|
||||
</#if>
|
||||
<#-- 逻辑删除注解 -->
|
||||
<#if (logicDeleteFieldName!"") == field.name>
|
||||
@TableLogic
|
||||
</#if>
|
||||
private ${field.propertyType} ${field.propertyName};
|
||||
</#list>
|
||||
<#------------ END 字段循环遍历 ---------->
|
||||
|
||||
<#if !entityLombokModel>
|
||||
<#list table.fields as field>
|
||||
<#if field.propertyType == "boolean">
|
||||
<#assign getprefix="is"/>
|
||||
<#else>
|
||||
<#assign getprefix="get"/>
|
||||
</#if>
|
||||
public ${field.propertyType} ${getprefix}${field.capitalName}() {
|
||||
return ${field.propertyName};
|
||||
}
|
||||
|
||||
<#if entityBuilderModel>
|
||||
public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
|
||||
<#else>
|
||||
public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
|
||||
</#if>
|
||||
this.${field.propertyName} = ${field.propertyName};
|
||||
<#if entityBuilderModel>
|
||||
return this;
|
||||
</#if>
|
||||
}
|
||||
</#list>
|
||||
</#if>
|
||||
|
||||
<#if entityColumnConstant>
|
||||
<#list table.fields as field>
|
||||
public static final String ${field.name?upper_case} = "${field.name}";
|
||||
|
||||
</#list>
|
||||
</#if>
|
||||
<#if activeRecord>
|
||||
@Override
|
||||
protected Serializable pkVal() {
|
||||
<#if keyPropertyName??>
|
||||
return this.${keyPropertyName};
|
||||
<#else>
|
||||
return null;
|
||||
</#if>
|
||||
}
|
||||
|
||||
</#if>
|
||||
<#if !entityLombokModel>
|
||||
@Override
|
||||
public String toString() {
|
||||
return "${entity}{" +
|
||||
<#list table.fields as field>
|
||||
<#if field_index==0>
|
||||
"${field.propertyName}=" + ${field.propertyName} +
|
||||
<#else>
|
||||
", ${field.propertyName}=" + ${field.propertyName} +
|
||||
</#if>
|
||||
</#list>
|
||||
"}";
|
||||
}
|
||||
</#if>
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package ${package.Mapper};
|
||||
|
||||
import ${package.Entity}.${entity};
|
||||
import ${superMapperClassPackage};
|
||||
|
||||
/**
|
||||
* @Description:${table.comment!}Mapper接口
|
||||
*/
|
||||
<#if kotlin>
|
||||
interface ${table.mapperName} : ${superMapperClass}<${entity}>
|
||||
<#else>
|
||||
public interface ${table.mapperName} extends ${superMapperClass}<${entity}> {
|
||||
|
||||
}
|
||||
</#if>
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="${package.Mapper}.${table.mapperName}">
|
||||
|
||||
<#if enableCache>
|
||||
<!-- 开启二级缓存 -->
|
||||
<cache type="org.mybatis.caches.ehcache.LoggingEhcache"/>
|
||||
|
||||
</#if>
|
||||
<#if baseResultMap>
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="BaseResultMap" type="${package.Entity}.${entity}">
|
||||
<#list table.fields as field>
|
||||
<#if field.keyFlag><#--生成主键排在第一位-->
|
||||
<id column="${field.name}" property="${field.propertyName}" />
|
||||
</#if>
|
||||
</#list>
|
||||
<#list table.commonFields as field><#--生成公共字段 -->
|
||||
<result column="${field.name}" property="${field.propertyName}" />
|
||||
</#list>
|
||||
<#list table.fields as field>
|
||||
<#if !field.keyFlag><#--生成普通字段 -->
|
||||
<result column="${field.name}" property="${field.propertyName}" />
|
||||
</#if>
|
||||
</#list>
|
||||
</resultMap>
|
||||
|
||||
</#if>
|
||||
<#if baseColumnList>
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
<#list table.commonFields as field>
|
||||
${field.name},
|
||||
</#list>
|
||||
${table.fieldNames}
|
||||
</sql>
|
||||
|
||||
</#if>
|
||||
</mapper>
|
||||
@@ -0,0 +1,15 @@
|
||||
package ${package.Service};
|
||||
|
||||
import ${package.Entity}.${entity};
|
||||
import ${superServiceClassPackage};
|
||||
|
||||
/**
|
||||
* @Description:${table.comment!} 服务类
|
||||
*/
|
||||
<#if kotlin>
|
||||
interface ${table.serviceName} : ${superServiceClass}<${entity}>
|
||||
<#else>
|
||||
public interface ${table.serviceName} extends ${superServiceClass}<${entity}> {
|
||||
|
||||
}
|
||||
</#if>
|
||||
@@ -0,0 +1,21 @@
|
||||
package ${package.ServiceImpl};
|
||||
|
||||
import ${package.Entity}.${entity};
|
||||
import ${package.Mapper}.${table.mapperName};
|
||||
import ${package.Service}.${table.serviceName};
|
||||
import ${superServiceImplClassPackage};
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @Description:${table.comment!} 服务实现类
|
||||
*/
|
||||
@Service
|
||||
<#if kotlin>
|
||||
open class ${table.serviceImplName} : ${superServiceImplClass}<${table.mapperName}, ${entity}>(), ${table.serviceName} {
|
||||
|
||||
}
|
||||
<#else>
|
||||
public class ${table.serviceImplName} extends ${superServiceImplClass}<${table.mapperName}, ${entity}> implements ${table.serviceName} {
|
||||
|
||||
}
|
||||
</#if>
|
||||
22
sfbx-dict/pom.xml
Normal file
22
sfbx-dict/pom.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?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>
|
||||
<artifactId>sfbx-cloud</artifactId>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<!--数据字典模块-->
|
||||
<artifactId>sfbx-dict</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>sfbx-dict</name>
|
||||
<modules>
|
||||
<module>dict-interface</module>
|
||||
<module>dict-web</module>
|
||||
</modules>
|
||||
<!-- FIXME change it to the project's website -->
|
||||
<url>http://www.example.com</url>
|
||||
|
||||
</project>
|
||||
23
sfbx-file/file-interface/pom.xml
Normal file
23
sfbx-file/file-interface/pom.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?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.itheima.sfbx</groupId>
|
||||
<artifactId>sfbx-file</artifactId>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<!--文件处理接口模块-->
|
||||
<artifactId>file-interface</artifactId>
|
||||
<name>file-interface</name>
|
||||
<!-- FIXME change it to the project's website -->
|
||||
<url>http://www.example.com</url>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-feign</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.itheima.sfbx.file.config;
|
||||
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @ClassName DictFenginConfig.java
|
||||
* @Description feign的最优化配置
|
||||
*/
|
||||
@EnableFeignClients(basePackages = "com.itheima.sfbx.file.feign")
|
||||
@Configuration
|
||||
public class FileFeignConfig {
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.itheima.sfbx.file.feign;
|
||||
|
||||
import com.itheima.sfbx.file.hystrix.FileBusinessHystrix;
|
||||
import com.itheima.sfbx.framework.commons.dto.file.FileVO;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassName FileFeign.java
|
||||
* @Description 附件feign接口
|
||||
*/
|
||||
@FeignClient(value = "file-web",fallback = FileBusinessHystrix.class)
|
||||
public interface FileBusinessFeign {
|
||||
|
||||
/**
|
||||
* @Description 业务与文件绑定
|
||||
* @param fileVO 附件对象
|
||||
* @return com.itheima.travel.req.FileVO
|
||||
*/
|
||||
@PostMapping("file-feign/bind-file")
|
||||
List<FileVO> bindFile(@RequestBody FileVO fileVO);
|
||||
|
||||
/**
|
||||
* @Description 业务绑定多个附件
|
||||
* @param fileVOs 相同业务的多个附件对象
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("file-feign/bind-batch-file")
|
||||
List<FileVO> bindBatchFile(@RequestBody ArrayList<FileVO> fileVOs);
|
||||
|
||||
/**
|
||||
* @Description 移除业务原附件,并绑定新的附件到业务上
|
||||
* @param fileVO 附件对象
|
||||
* @return
|
||||
*/
|
||||
@PostMapping(value = "file-feign/replace-bind-file")
|
||||
Boolean replaceBindFile(@RequestBody FileVO fileVO);
|
||||
|
||||
/**
|
||||
* @Description 移除业务原附件,并批量绑定新的附件到业务上
|
||||
* @param fileVOs 附件对象
|
||||
* @return
|
||||
*/
|
||||
@PostMapping(value = "file-feign/replace-bind-batch-file")
|
||||
Boolean replaceBindBatchFile(@RequestBody ArrayList<FileVO> fileVOs);
|
||||
|
||||
|
||||
/**
|
||||
* @Description 按业务ID查询附件【对象传递参数方式】
|
||||
* @param businessIds 业务IDS
|
||||
* @return List<FileVO>
|
||||
*/
|
||||
@PostMapping("file-feign/find-in-business-ids")
|
||||
List<FileVO> findInBusinessIds(@RequestBody List<Long> businessIds);
|
||||
|
||||
|
||||
/**
|
||||
* @Description 删除业务相关附件
|
||||
* @param businessIds 业务IDS
|
||||
* @return Boolean
|
||||
*/
|
||||
@DeleteMapping("file-feign/delete-by-business-ids")
|
||||
Boolean deleteByBusinessIds(@RequestBody ArrayList<Long> businessIds);
|
||||
|
||||
/**
|
||||
* @Description 定时清理文件
|
||||
* @return
|
||||
*/
|
||||
@DeleteMapping("file-feign/clear-file")
|
||||
Boolean clearFile();
|
||||
|
||||
/**
|
||||
* @Description 延迟队列清理文件
|
||||
* @return
|
||||
*/
|
||||
@DeleteMapping("file-feign/clear-file-id/{id}")
|
||||
Boolean clearFileById(@PathVariable("id") Long id);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.itheima.sfbx.file.feign;
|
||||
|
||||
import com.itheima.sfbx.file.hystrix.FileBusinessHystrix;
|
||||
import com.itheima.sfbx.framework.commons.dto.file.FileVO;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
|
||||
/**
|
||||
* @ClassName FileFeign.java
|
||||
* @Description 文件下载feign接口
|
||||
*/
|
||||
@FeignClient(value = "file-web",fallback = FileBusinessHystrix.class)
|
||||
public interface FileDownLoadFeign {
|
||||
|
||||
/***
|
||||
* @description 文件下载-服务远程调用-base64方式返回
|
||||
* @param fileId 上传对象
|
||||
* @return: com.itheima.travel.req.FileVo
|
||||
*/
|
||||
@PostMapping("file-feign/down-load/{fileId}")
|
||||
FileVO downLoad(@PathVariable("fileId") Long fileId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.itheima.sfbx.file.hystrix;
|
||||
|
||||
import com.itheima.sfbx.framework.commons.dto.file.FileVO;
|
||||
import com.itheima.sfbx.file.feign.FileBusinessFeign;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassName FileHystrix.java
|
||||
* @Description FileFeign的Hystrix
|
||||
*/
|
||||
@Component
|
||||
public class FileBusinessHystrix implements FileBusinessFeign {
|
||||
|
||||
|
||||
@Override
|
||||
public List<FileVO> bindFile(FileVO fileVO) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FileVO> bindBatchFile(ArrayList<FileVO> fileVOs) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean replaceBindFile(FileVO fileVO) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean replaceBindBatchFile(ArrayList<FileVO> fileVOs) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FileVO> findInBusinessIds(List<Long> businessIds) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean deleteByBusinessIds(ArrayList<Long> businessIds) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean clearFile() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean clearFileById(Long id) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.itheima.sfbx.file.hystrix;
|
||||
|
||||
import com.itheima.sfbx.file.feign.FileDownLoadFeign;
|
||||
import com.itheima.sfbx.framework.commons.dto.file.FileVO;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @ClassName FileHystrix.java
|
||||
* @Description FileFeign的Hystrix
|
||||
*/
|
||||
@Component
|
||||
public class FileDownLoadHystrix implements FileDownLoadFeign {
|
||||
|
||||
|
||||
@Override
|
||||
public FileVO downLoad(Long fileId) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
21
sfbx-file/file-web/Dockerfile
Normal file
21
sfbx-file/file-web/Dockerfile
Normal file
@@ -0,0 +1,21 @@
|
||||
FROM openjdk:11-jdk
|
||||
LABEL maintainer="研究院研发组 <research@itcast.cn>"
|
||||
|
||||
# 时区修改为东八区
|
||||
ENV TZ=Asia/Shanghai
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
WORKDIR /file-web
|
||||
ARG PACKAGE_PATH=./target/file-web.jar
|
||||
ADD ${PACKAGE_PATH:-./} file-web.jar
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ENV JAVA_OPTS="\
|
||||
-server \
|
||||
-Xms256m \
|
||||
-Xmx512m \
|
||||
-XX:MetaspaceSize=256m \
|
||||
-XX:MaxMetaspaceSize=512m\
|
||||
-Dspring.profiles.active=test"
|
||||
ENTRYPOINT ["sh","-c","java -Djava.security.egd=file:/dev/./urandom -jar $JAVA_OPTS file-web.jar"]
|
||||
105
sfbx-file/file-web/pom.xml
Normal file
105
sfbx-file/file-web/pom.xml
Normal file
@@ -0,0 +1,105 @@
|
||||
<?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.itheima.sfbx</groupId>
|
||||
<artifactId>sfbx-file</artifactId>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<!--文件处理web模块-->
|
||||
<artifactId>file-web</artifactId>
|
||||
<name>file-web</name>
|
||||
<!-- FIXME change it to the project's website -->
|
||||
<url>http://www.example.com</url>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alicloud-oss</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.aliyun.oss</groupId>
|
||||
<artifactId>aliyun-sdk-oss</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-rabbitmq</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-seata</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-mybatis-plus</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-knife4j-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.qiniu</groupId>
|
||||
<artifactId>qiniu-java-sdk</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
<version>2.3.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<includes>
|
||||
<include>**/*.yml</include>
|
||||
<include>**/*.properties</include>
|
||||
<include>**/*.xml</include>
|
||||
<include>**/*.yaml</include>
|
||||
<include>**/*.txt</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
<finalName>file-web</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.itheima.sfbx.file;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
/**
|
||||
* 文件上传处理微服务
|
||||
*/
|
||||
@SpringBootApplication(scanBasePackages = "com.itheima.sfbx")
|
||||
public class FileWebStart {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(FileWebStart.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.itheima.sfbx.file.adapter;
|
||||
|
||||
import com.itheima.sfbx.file.pojo.File;
|
||||
import com.itheima.sfbx.framework.commons.dto.file.FilePartVO;
|
||||
import com.itheima.sfbx.framework.commons.dto.file.FileVO;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassName FileStorageAdapter.java
|
||||
* @Description 文件存储适配处理
|
||||
*/
|
||||
public interface FileStorageAdapter {
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
* @param fileVO {@link FileVO} 文件信息对象
|
||||
* @param inputStream 文件流
|
||||
* @return pathUrl 全路径
|
||||
*/
|
||||
String uploadFile(FileVO fileVO, InputStream inputStream);
|
||||
|
||||
/***
|
||||
* @description 分片上传-初始化分片请求
|
||||
* @param fileVO {@link FileVO} 文件信息对象
|
||||
* @return uploadId 文件上传id
|
||||
*/
|
||||
File initiateMultipartUpload(FileVO fileVO);
|
||||
|
||||
/***
|
||||
* @description 分片上传-上传每个分片文件
|
||||
* @param filePartVo {@link FilePartVO} 文件信息对象
|
||||
* @param inputStream 当前分片文件流
|
||||
* @return PartETag json字符串
|
||||
*/
|
||||
String uploadPart(FilePartVO filePartVo, InputStream inputStream);
|
||||
|
||||
|
||||
/***
|
||||
* @description 分片上传-合并所有上传文件
|
||||
* @param fileVO {@link File} 文件信息对象
|
||||
* @return 合并结果
|
||||
*/
|
||||
String completeMultipartUpload(FileVO fileVO);
|
||||
|
||||
/**
|
||||
* @Description 下载文件
|
||||
* @param storeFlag 存储源标识
|
||||
* @param bucketName 资源存储区域名称
|
||||
* @param pathUrl 资源文件路径地址(其中包含文件名称)
|
||||
* @return
|
||||
*/
|
||||
InputStream downloadFile(String storeFlag,String bucketName,String pathUrl) throws IOException;
|
||||
|
||||
/**
|
||||
* @Description 文件删除
|
||||
* @param pathUrl 全路径
|
||||
* @throws Exception
|
||||
*/
|
||||
void delete(String storeFlag,String bucketName,String pathUrl);
|
||||
|
||||
|
||||
/**
|
||||
* @Description 批量文件删除
|
||||
* @param pathUrls 全路径集合
|
||||
* @throws Exception
|
||||
*/
|
||||
void deleteBatch(String storeFlag,String bucketName,List<String> pathUrls);
|
||||
|
||||
/**
|
||||
* @Description 获取文件文本内容
|
||||
* @param pathUrl 全路径
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
String getFileContent(String storeFlag,String bucketName,String pathUrl) throws IOException;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.itheima.sfbx.file.adapter.impl;
|
||||
|
||||
import com.itheima.sfbx.file.adapter.FileStorageAdapter;
|
||||
import com.itheima.sfbx.file.handler.FileStorageHandler;
|
||||
import com.itheima.sfbx.framework.commons.constant.file.FileConstant;
|
||||
import com.itheima.sfbx.framework.commons.dto.file.FileVO;
|
||||
import com.itheima.sfbx.framework.commons.dto.file.FilePartVO;
|
||||
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
|
||||
import com.itheima.sfbx.framework.commons.utils.RegisterBeanHandler;
|
||||
import com.itheima.sfbx.file.pojo.File;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @ClassName FileStorageAdapterImpl.java
|
||||
* @Description 文件存储适配处理
|
||||
*/
|
||||
@Component
|
||||
public class FileStorageAdapterImpl implements FileStorageAdapter {
|
||||
|
||||
@Autowired
|
||||
RegisterBeanHandler registerBeanHandler;
|
||||
|
||||
private static Map<String,String> fileStorageHandlers =new HashMap<>();
|
||||
|
||||
static {
|
||||
fileStorageHandlers.put(FileConstant.ALIYUN_OSS,"ossFileStorageHandler");
|
||||
fileStorageHandlers.put(FileConstant.QINIU_KODO,"kodoFileStorageHandler");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadFile(FileVO fileVO, InputStream inputStream) {
|
||||
String fileStorageHandlerString = EmptyUtil.isNullOrEmpty(fileVO.getStoreFlag())?
|
||||
fileStorageHandlers.get(FileConstant.ALIYUN_OSS):fileStorageHandlers.get(fileVO.getStoreFlag());
|
||||
FileStorageHandler fileStorageHandler = registerBeanHandler.getBean(fileStorageHandlerString, FileStorageHandler.class);
|
||||
return fileStorageHandler.uploadFile(fileVO.getSuffix(), fileVO.getFileName(), fileVO.getBucketName(), fileVO.getAutoCatalog(), inputStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File initiateMultipartUpload(FileVO fileVO) {
|
||||
String fileStorageHandlerString = EmptyUtil.isNullOrEmpty(fileVO.getStoreFlag())?
|
||||
fileStorageHandlers.get(FileConstant.ALIYUN_OSS):fileStorageHandlers.get(fileVO.getStoreFlag());
|
||||
FileStorageHandler fileStorageHandler = registerBeanHandler.getBean(fileStorageHandlerString, FileStorageHandler.class);
|
||||
return fileStorageHandler.initiateMultipartUpload(fileVO.getSuffix(),fileVO.getFileName(),fileVO.getBucketName(),fileVO.getAutoCatalog());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadPart(FilePartVO filePartVo, InputStream inputStream) {
|
||||
String fileStorageHandlerString = EmptyUtil.isNullOrEmpty(filePartVo.getStoreFlag())?
|
||||
fileStorageHandlers.get(FileConstant.ALIYUN_OSS):fileStorageHandlers.get(filePartVo.getStoreFlag());
|
||||
FileStorageHandler fileStorageHandler = registerBeanHandler.getBean(fileStorageHandlerString, FileStorageHandler.class);
|
||||
return fileStorageHandler.uploadPart(filePartVo.getUploadId(),filePartVo.getFileName(),filePartVo.getPartNumber(),filePartVo.getPartSize(),filePartVo.getBucketName(),inputStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String completeMultipartUpload(FileVO fileVO) {
|
||||
String fileStorageHandlerString = EmptyUtil.isNullOrEmpty(fileVO.getStoreFlag())?
|
||||
fileStorageHandlers.get(FileConstant.ALIYUN_OSS):fileStorageHandlers.get(fileVO.getStoreFlag());
|
||||
FileStorageHandler fileStorageHandler = registerBeanHandler.getBean(fileStorageHandlerString, FileStorageHandler.class);
|
||||
return fileStorageHandler.completeMultipartUpload(fileVO.getUploadId(),fileVO.getPartETags(),fileVO.getFileName(),fileVO.getBucketName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream downloadFile(String storeFlag,String bucketName,String pathUrl) throws IOException {
|
||||
String fileStorageHandlerString = EmptyUtil.isNullOrEmpty(storeFlag)?
|
||||
fileStorageHandlers.get(FileConstant.ALIYUN_OSS):fileStorageHandlers.get(storeFlag);
|
||||
FileStorageHandler fileStorageHandler = registerBeanHandler.getBean(fileStorageHandlerString, FileStorageHandler.class);
|
||||
return fileStorageHandler.downloadFile(bucketName,pathUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String storeFlag,String bucketName,String pathUrl) {
|
||||
String fileStorageHandlerString = EmptyUtil.isNullOrEmpty(storeFlag)?
|
||||
fileStorageHandlers.get(FileConstant.ALIYUN_OSS):fileStorageHandlers.get(storeFlag);
|
||||
FileStorageHandler fileStorageHandler = registerBeanHandler.getBean(fileStorageHandlerString, FileStorageHandler.class);
|
||||
fileStorageHandler.delete(bucketName,pathUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteBatch(String storeFlag,String bucketName,List<String> pathUrls) {
|
||||
String fileStorageHandlerString = EmptyUtil.isNullOrEmpty(storeFlag)?
|
||||
fileStorageHandlers.get(FileConstant.ALIYUN_OSS):fileStorageHandlers.get(storeFlag);
|
||||
FileStorageHandler fileStorageHandler = registerBeanHandler.getBean(fileStorageHandlerString, FileStorageHandler.class);
|
||||
fileStorageHandler.deleteBatch(bucketName,pathUrls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileContent(String storeFlag,String bucketName,String pathUrl) throws IOException {
|
||||
String fileStorageHandlerString = EmptyUtil.isNullOrEmpty(storeFlag)?
|
||||
fileStorageHandlers.get(FileConstant.ALIYUN_OSS):fileStorageHandlers.get(storeFlag);
|
||||
FileStorageHandler fileStorageHandler = registerBeanHandler.getBean(fileStorageHandlerString, FileStorageHandler.class);
|
||||
return fileStorageHandler.getFileContent(bucketName,pathUrl);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.itheima.sfbx.file.binding;
|
||||
|
||||
import com.itheima.sfbx.framework.rabbitmq.source.FileSource;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
|
||||
/**
|
||||
* @ClassName Binding.java
|
||||
* @Description 绑定文件发送者声明
|
||||
*/
|
||||
@EnableBinding({FileSource.class})
|
||||
public class SourceBinding {
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.itheima.sfbx.file.fegin;
|
||||
|
||||
import com.itheima.sfbx.file.service.IFileService;
|
||||
import com.itheima.sfbx.framework.commons.dto.file.FileVO;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassName FileController.java
|
||||
* @Description 附件展示维护controller
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("file-feign")
|
||||
@Api(tags = "附件feign-controller")
|
||||
@Slf4j
|
||||
public class FileBusinessFeignController {
|
||||
|
||||
@Autowired
|
||||
IFileService fileService;
|
||||
|
||||
/**
|
||||
* @Description 业务绑定单个附件
|
||||
* @param fileVO 附件对象
|
||||
* @return
|
||||
*/
|
||||
@PostMapping(value = "bind-file")
|
||||
@ApiOperation(value = "业务绑定单文件",notes = "业务绑定单文件")
|
||||
@ApiImplicitParam(name = "fileVO",value = "附件对象",required = true,dataType = "FileVO")
|
||||
public FileVO bindFile(@RequestBody FileVO fileVO){
|
||||
return fileService.bindFile(fileVO);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Description 相同业务绑定多个附件
|
||||
* @param fileVOs 相同业务的多个附件对象
|
||||
* @return
|
||||
*/
|
||||
@PostMapping(value = "bind-batch-file")
|
||||
@ApiOperation(value = "业务绑定多文件",notes = "业务绑定多文件")
|
||||
@ApiImplicitParam(name = "fileVOs",value = "附件对象",required = true,dataType = "FileVO")
|
||||
public List<FileVO> bindBatchFile(@RequestBody ArrayList<FileVO> fileVOs){
|
||||
return fileService.bindBatchFile(fileVOs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Description 移除业务原图片,并绑定新的图片到业务上
|
||||
* @param fileVO 附件对象
|
||||
* @return
|
||||
*/
|
||||
@PostMapping(value = "replace-bind-file")
|
||||
@ApiOperation(value = "移除业务原图片,并绑定新的图片到业务上",notes = "移除业务原图片,并绑定新的图片到业务上")
|
||||
@ApiImplicitParam(name = "fileVO",value = "附件对象",required = true,dataType = "FileVO")
|
||||
public Boolean replaceBindFile(@RequestBody FileVO fileVO){
|
||||
return fileService.replaceBindFile(fileVO);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Description 批量移除业务原图片,并批量绑定新的图片到业务上
|
||||
* @param fileVOs 附件对象
|
||||
* @return
|
||||
*/
|
||||
@PostMapping(value = "replace-bind-batch-file")
|
||||
@ApiOperation(value = "移除业务原图片,并绑定新的图片到业务上",notes = "移除业务原图片,并绑定新的图片到业务上")
|
||||
@ApiImplicitParam(name = "fileVOs",value = "附件对象",required = true,dataType = "FileVO")
|
||||
public Boolean replaceBindBatchFile(@RequestBody ArrayList<FileVO> fileVOs){
|
||||
return fileService.replaceBindBatchFile(fileVOs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 按业务ID查询附件
|
||||
* @param businessIds 业务ids
|
||||
* @return java.util.List<com.itheima.travel.req.FileVO>
|
||||
*/
|
||||
@PostMapping("find-in-business-ids")
|
||||
@ApiOperation(value = "查询业务对应附件",notes = "查询业务对应附件")
|
||||
@ApiImplicitParam(name = "fileVO",value = "附件对象",required = true,dataType = "FileVO")
|
||||
public List<FileVO> findInBusinessIds(@RequestBody ArrayList<Long> businessIds) {
|
||||
return fileService.findInBusinessIds(businessIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Description 删除业务相关附件
|
||||
* @param businessIds 附件信息ids
|
||||
* @return
|
||||
*/
|
||||
@DeleteMapping("delete-by-business-ids")
|
||||
@ApiOperation(value = "删除业务对应附件",notes = "删除业务对应附件")
|
||||
@ApiImplicitParam(name = "fileVO",value = "附件对象",required = true,dataType = "FileVO")
|
||||
public Boolean deleteByBusinessIds(@RequestBody ArrayList<Long> businessIds) {
|
||||
return fileService.deleteInBusinessIds(businessIds);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @Description 定时清理文件
|
||||
* @return Boolean
|
||||
*/
|
||||
@DeleteMapping("clear-file")
|
||||
@ApiOperation(value = "删除业务对应附件",notes = "删除业务对应附件")
|
||||
public Boolean clearFile(){
|
||||
return fileService.clearFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* @Description 延迟清理文件
|
||||
* @return Boolean
|
||||
*/
|
||||
@DeleteMapping("clear-file-id/{id}")
|
||||
@ApiOperation(value = "删除业务对应附件",notes = "删除业务对应附件")
|
||||
@ApiImplicitParam(name = "id",value = "业务id",required = true,dataType = "String")
|
||||
public Boolean clearFileById(@PathVariable("id")String id){
|
||||
return fileService.clearFileById(id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.itheima.sfbx.file.fegin;
|
||||
|
||||
import com.itheima.sfbx.file.service.IFileService;
|
||||
import com.itheima.sfbx.framework.commons.dto.file.FileVO;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @ClassName FileUpLoadController.java
|
||||
* @Description 文件下载接口
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("file-feign")
|
||||
@Api(tags = "附件controller")
|
||||
@Slf4j
|
||||
public class FileDownLoadFeignController {
|
||||
|
||||
@Autowired
|
||||
IFileService fileService;
|
||||
|
||||
/***
|
||||
* @description 文件下载-简单下载-图片base64Image方式展示
|
||||
* @param fileId 上传对象
|
||||
* @return: com.itheima.travel.req.FileVo
|
||||
*/
|
||||
@PostMapping(value = "down-load/{fileId}")
|
||||
@ApiOperation(value = "文件下载",notes = "文件下载")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(paramType = "path", name = "fileId",value = "附件Id",dataType = "Long")
|
||||
})
|
||||
public FileVO downLoad(@PathVariable("fileId") Long fileId){
|
||||
FileVO fileVO = fileService.downLoad(fileId);
|
||||
return fileVO;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.itheima.sfbx.file.handler;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @ClassName AbsFileStorageHandler.java
|
||||
* @Description 文件存储处理抽象类
|
||||
*/
|
||||
public abstract class AbsFileStorageHandler {
|
||||
|
||||
public static Map<String,String> metaMimeTypeMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
metaMimeTypeMap.put(".docx","application/vnd.openxmlformats-officedocument.wordprocessingml.document");
|
||||
metaMimeTypeMap.put(".doc","application/msword");
|
||||
metaMimeTypeMap.put(".ppt","application/x-ppt");
|
||||
metaMimeTypeMap.put(".xls","application/vnd.ms-excel");
|
||||
metaMimeTypeMap.put(".xhtml","text/html");
|
||||
metaMimeTypeMap.put(".htm","text/html");
|
||||
metaMimeTypeMap.put(".html","text/html");
|
||||
metaMimeTypeMap.put(".jpe","image/jpg");
|
||||
metaMimeTypeMap.put(".jpeg","image/jpg");
|
||||
metaMimeTypeMap.put(".jpg","image/jpg");
|
||||
metaMimeTypeMap.put(".png","image/jpg");
|
||||
metaMimeTypeMap.put(".mp4","video/mp4");
|
||||
metaMimeTypeMap.put(".wmv","video/x-ms-wmv");
|
||||
metaMimeTypeMap.put(".pdf","application/pdf");
|
||||
metaMimeTypeMap.put(".mp3","audio/mp3");
|
||||
}
|
||||
|
||||
/***
|
||||
* @description 文件路径生成策略
|
||||
*
|
||||
* @param filename
|
||||
* @return
|
||||
* @return: java.lang.String
|
||||
*/
|
||||
public String builderOssPath(String filename) {
|
||||
String separator = "/";
|
||||
StringBuilder stringBuilder = new StringBuilder(50);
|
||||
LocalDate localDate = LocalDate.now();
|
||||
String yeat = String.valueOf(localDate.getYear());
|
||||
stringBuilder.append(yeat).append(separator);
|
||||
String moth = String.valueOf(localDate.getMonthValue());
|
||||
stringBuilder.append(moth).append(separator);
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE;
|
||||
String day = formatter.format(localDate);
|
||||
stringBuilder.append(day).append(separator);
|
||||
stringBuilder.append(filename);
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.itheima.sfbx.file.handler;
|
||||
|
||||
import com.itheima.sfbx.file.pojo.File;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassName FileStorageHandler.java
|
||||
* @Description 文件存储处理接口类
|
||||
*/
|
||||
public interface FileStorageHandler {
|
||||
|
||||
/**
|
||||
* @Description 文件上传
|
||||
* @param suffix 文件后缀
|
||||
* @param filename 文件名称-可以指定存储空间下的目录,例如:a/b/c/filename
|
||||
* @param bucketName 存储空间的名称
|
||||
* @param autoCatalog 是否自动生成文件存储目录,如果在filename指定了目录,此值设置为false
|
||||
* @param inputStream 文件流
|
||||
* @return pathUrl 全路径
|
||||
*/
|
||||
String uploadFile(String suffix, String filename,String bucketName,boolean autoCatalog, InputStream inputStream);
|
||||
|
||||
/***
|
||||
* @description 分片上传-初始化分片请求
|
||||
* @param suffix 文件后缀
|
||||
* @param filename 文件名称-可以指定存储空间下的目录,例如:a/b/c/filename
|
||||
* @param bucketName 存储空间的名称
|
||||
* @param autoCatalog 是否自动生成文件存储目录,如果在filename指定了目录,此值设置为false
|
||||
* @return uploadId 文件上传id
|
||||
*/
|
||||
File initiateMultipartUpload(String suffix, String filename, String bucketName, boolean autoCatalog);
|
||||
|
||||
/***
|
||||
* @description 分片上传-上传每个分片文件
|
||||
* @param upLoadId 文件上传id
|
||||
* @param filename 文件名称-可以指定存储空间下的目录,例如:a/b/c/filename
|
||||
* @param partNumber 当前分片
|
||||
* @param partSize 分片数
|
||||
* @param bucketName 存储空间的名称
|
||||
* @param inputStream 当前分片文件流
|
||||
* @return PartETag json字符串
|
||||
*/
|
||||
String uploadPart(String upLoadId,String filename,int partNumber,long partSize,String bucketName,InputStream inputStream);
|
||||
|
||||
|
||||
/***
|
||||
* @description 分片上传-合并所有上传文件
|
||||
*
|
||||
* @param upLoadId 文件上传id
|
||||
* @param partETags json字符串
|
||||
* @param filename 文件名称-可以指定存储空间下的目录,例如:a/b/c/filename
|
||||
* @param bucketName 存储空间的名称
|
||||
* @return 合并结果
|
||||
*/
|
||||
String completeMultipartUpload(String upLoadId,List<String> partETags,String filename,String bucketName);
|
||||
|
||||
/**
|
||||
* @Description 下载文件
|
||||
* @param bucketName 存储空间名称
|
||||
* @param pathUrl 全路径
|
||||
* @return
|
||||
*/
|
||||
InputStream downloadFile(String bucketName,String pathUrl) throws IOException;
|
||||
|
||||
/**
|
||||
* @Description 文件删除
|
||||
* @param bucketName 存储空间名称
|
||||
* @param pathUrl 全路径
|
||||
* @throws Exception
|
||||
*/
|
||||
void delete(String bucketName,String pathUrl);
|
||||
|
||||
/**
|
||||
* @Description 批量文件删除
|
||||
* @param bucketName 存储空间名称
|
||||
* @param pathUrls 全路径集合
|
||||
* @throws Exception
|
||||
*/
|
||||
void deleteBatch(String bucketName,List<String> pathUrls);
|
||||
|
||||
/**
|
||||
* @Description 获取文件文本内容
|
||||
* @param bucketName 存储空间名称
|
||||
* @param pathUrl 全路径
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
String getFileContent(String bucketName,String pathUrl) throws IOException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package com.itheima.sfbx.file.handler.aliyun.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.aliyun.oss.ClientException;
|
||||
import com.aliyun.oss.OSS;
|
||||
import com.aliyun.oss.OSSException;
|
||||
import com.aliyun.oss.internal.OSSConstants;
|
||||
import com.aliyun.oss.internal.OSSHeaders;
|
||||
import com.aliyun.oss.model.*;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.itheima.sfbx.framework.commons.enums.file.FileEnum;
|
||||
import com.itheima.sfbx.framework.commons.exception.ProjectException;
|
||||
import com.itheima.sfbx.file.handler.AbsFileStorageHandler;
|
||||
import com.itheima.sfbx.file.handler.FileStorageHandler;
|
||||
import com.itheima.sfbx.file.handler.aliyun.properties.OssAliyunConfigProperties;
|
||||
import com.itheima.sfbx.file.pojo.File;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StopWatch;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassName OssFileStorageHandlerImpl.java
|
||||
* @Description 阿里云文件上传
|
||||
*/
|
||||
@Slf4j
|
||||
@Service("ossFileStorageHandler")
|
||||
@EnableConfigurationProperties(OssAliyunConfigProperties.class)
|
||||
public class OssFileStorageHandlerImpl extends AbsFileStorageHandler implements FileStorageHandler {
|
||||
|
||||
@Autowired
|
||||
OSS ossClient;
|
||||
|
||||
@Autowired
|
||||
OssAliyunConfigProperties ossAliyunConfigProperties;
|
||||
/***
|
||||
* @description 文件元数据处理
|
||||
* @param prefix 文件后缀
|
||||
* @return
|
||||
*/
|
||||
public ObjectMetadata fileMetaHandler(String prefix){
|
||||
//元数据对象
|
||||
ObjectMetadata objectMeta = new ObjectMetadata();
|
||||
//文件字符集
|
||||
objectMeta.setContentEncoding("UTF-8");
|
||||
//文件类型匹配
|
||||
objectMeta.setContentType(metaMimeTypeMap.get(prefix.toLowerCase()));
|
||||
return objectMeta;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadFile(String suffix, String filename, String bucketName,boolean autoCatalog, InputStream inputStream) {
|
||||
// 是否自动生成存储路径并设置文件路径和名称(Key)
|
||||
String key = autoCatalog?builderOssPath(filename):filename;
|
||||
log.info("OSS文件上传开始:{}" ,key);
|
||||
try {
|
||||
//上传文件元数据处理
|
||||
ObjectMetadata objectMeta = fileMetaHandler(suffix);
|
||||
//文件上传请求对象
|
||||
PutObjectRequest request = new PutObjectRequest(bucketName, key, inputStream,objectMeta);
|
||||
//上传限流
|
||||
if (ossAliyunConfigProperties.getIslimitSpeed()){
|
||||
request.setTrafficLimit(ossAliyunConfigProperties.getUplimitSpeed());
|
||||
}
|
||||
//文件上传
|
||||
PutObjectResult result = ossClient.putObject(request);
|
||||
// 设置权限(公开读)
|
||||
ossClient.setBucketAcl(bucketName, CannedAccessControlList.PublicRead);
|
||||
} catch (OSSException oe) {
|
||||
log.error("OSS文件上传错误:{}", oe);
|
||||
throw new ProjectException(FileEnum.UPLOAD_FAIL);
|
||||
} catch (ClientException ce) {
|
||||
log.error("OSS文件上传客户端错误:{}",ce);
|
||||
throw new ProjectException(FileEnum.UPLOAD_FAIL);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File initiateMultipartUpload(String suffix, String filename, String bucketName, boolean autoCatalog) {
|
||||
// 是否自动生成存储路径并设置文件路径和名称(Key)
|
||||
String key = filename;
|
||||
InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, key);
|
||||
// 如果需要在初始化分片时设置请求头,请参考以下示例代码。
|
||||
ObjectMetadata metadata = fileMetaHandler(suffix);
|
||||
metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
|
||||
// 指定该Object的网页缓存行为。
|
||||
metadata.setCacheControl("no-cache");
|
||||
// 指定该Object被下载时的名称。
|
||||
metadata.setContentDisposition("inline;filename="+key);
|
||||
// 指定该Object的内容编码格式。
|
||||
metadata.setContentEncoding(OSSConstants.DEFAULT_CHARSET_NAME);
|
||||
//指定请求
|
||||
request.setObjectMetadata(metadata);
|
||||
// 初始化分片。
|
||||
InitiateMultipartUploadResult upresult = ossClient.initiateMultipartUpload(request);
|
||||
// 设置权限(公开读)
|
||||
ossClient.setBucketAcl(bucketName, CannedAccessControlList.PublicRead);
|
||||
// 返回uploadId,它是分片上传事件的唯一标识。您可以根据该uploadId发起相关的操作,例如取消分片上传、查询分片上传等。
|
||||
return File.builder().bucketName(bucketName).pathUrl(key).fileName(key).uploadId(upresult.getUploadId()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadPart(String upLoadId, String filename, int partNumber, long partSize, String bucketName, InputStream inputStream) {
|
||||
//封装分片上传请求
|
||||
UploadPartRequest uploadPartRequest = new UploadPartRequest();
|
||||
uploadPartRequest.setUploadId(upLoadId);
|
||||
//part大小 1-10000
|
||||
uploadPartRequest.setPartNumber(partNumber);
|
||||
uploadPartRequest.setPartSize(partSize);
|
||||
//文件上传的bucketName
|
||||
uploadPartRequest.setBucketName(bucketName);
|
||||
//分片文件
|
||||
uploadPartRequest.setInputStream(inputStream);
|
||||
uploadPartRequest.setKey(filename);
|
||||
// 每个分片不需要按顺序上传,甚至可以在不同客户端上传,OSS会按照分片号排序组成完整的文件。
|
||||
UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);
|
||||
log.info("{}文件第 {} 片上传成功,上传结果:{}", upLoadId, uploadPartRequest.getPartNumber(),JSON.toJSON(uploadPartResult));
|
||||
return JSONObject.toJSONString(uploadPartResult.getPartETag());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String completeMultipartUpload(String upLoadId, List<String> partETags, String filename, String bucketName) {
|
||||
StopWatch st = new StopWatch();
|
||||
st.start();
|
||||
//转换jsonarray为list
|
||||
List<PartETag> partETagList =Lists.newArrayList();
|
||||
partETags.forEach(n->{
|
||||
partETagList.add(JSONObject.parseObject(n,PartETag.class));
|
||||
});
|
||||
CompleteMultipartUploadRequest completeMultipartUploadRequest =
|
||||
new CompleteMultipartUploadRequest(bucketName, filename, upLoadId, partETagList);
|
||||
log.info("{}文件上传完成,开始合并,partList:{}", upLoadId, partETags);
|
||||
// 完成分片上传。
|
||||
CompleteMultipartUploadResult completeMultipartUploadResult = ossClient.completeMultipartUpload(completeMultipartUploadRequest);
|
||||
st.stop();
|
||||
log.info("{}文件上传完成,上传结果:{},耗时:{}", upLoadId, JSON.toJSON(completeMultipartUploadResult), st.getTotalTimeMillis());
|
||||
return completeMultipartUploadResult.getETag();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public InputStream downloadFile(String bucketName,String pathUrl) throws IOException {
|
||||
GetObjectRequest request = new GetObjectRequest(bucketName, pathUrl);
|
||||
//下载传限流
|
||||
if (ossAliyunConfigProperties.getIslimitSpeed()){
|
||||
request.setTrafficLimit(ossAliyunConfigProperties.getDownlimitSpeed());
|
||||
}
|
||||
//ossObject包含文件所在的存储空间名称、文件名称、文件元信息以及一个输入流。
|
||||
InputStream inputStream = ossClient.getObject(request).getObjectContent();
|
||||
return inputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String bucketName,String pathUrl) {
|
||||
// 删除Objects
|
||||
ossClient.deleteObject(bucketName,pathUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteBatch(String bucketName,List<String> pathUrls) {
|
||||
// 删除Objects
|
||||
ossClient.deleteObjects(new DeleteObjectsRequest(bucketName).withKeys(pathUrls));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileContent(String bucketName,String pathUrl) throws IOException {
|
||||
InputStream inputStream = downloadFile(bucketName,pathUrl);
|
||||
return new String(ByteStreams.toByteArray(inputStream));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.itheima.sfbx.file.handler.aliyun.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @Description 阿里云OSS上传配置类
|
||||
*/
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@ConfigurationProperties("spring.cloud.alicloud.oss")
|
||||
public class OssAliyunConfigProperties {
|
||||
|
||||
//区域
|
||||
String region;
|
||||
|
||||
//秘钥ID
|
||||
String accessKeyId;
|
||||
|
||||
//秘钥
|
||||
String accessKeySecret;
|
||||
|
||||
//角色
|
||||
String roleArn;
|
||||
|
||||
//桶名称
|
||||
private String bucketName ;
|
||||
|
||||
//访问终端域名地址
|
||||
private String endpoint;
|
||||
|
||||
//是否限流
|
||||
private Boolean islimitSpeed = true;
|
||||
|
||||
//上传限流
|
||||
private int uplimitSpeed = 100 * 1024 * 1024 * 2;
|
||||
|
||||
//下载限流
|
||||
private int downlimitSpeed = 100 * 1024 * 8;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.itheima.sfbx.file.handler.qiniu.config;
|
||||
|
||||
import com.itheima.sfbx.file.handler.qiniu.properties.QiniuProperties;
|
||||
import com.qiniu.storage.BucketManager;
|
||||
import com.qiniu.storage.Region;
|
||||
import com.qiniu.util.Auth;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Qiniu服务配置类
|
||||
* </p>
|
||||
* 声明创建Qiniu核心配置对象
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(QiniuProperties.class)
|
||||
public class QiniuConfig {
|
||||
|
||||
@Autowired
|
||||
private QiniuProperties qiniuConfigProperties;
|
||||
|
||||
private static Map<String, Region> regions =new HashMap<>();
|
||||
|
||||
static {
|
||||
regions.put(QiniuRegion.REGION_HUADONG,Region.huadong());
|
||||
regions.put(QiniuRegion.REGION_HUABEI,Region.huabei());
|
||||
regions.put(QiniuRegion.REGION_HUANAN,Region.huanan());
|
||||
regions.put(QiniuRegion.REGION_BEIMEI,Region.beimei());
|
||||
regions.put(QiniuRegion.REGION_DONGNANYA,Region.xinjiapo());
|
||||
}
|
||||
|
||||
@Bean("qiniuConfiguration")
|
||||
public com.qiniu.storage.Configuration qiniuConfiguration() {
|
||||
|
||||
// 设置存储区域
|
||||
String regionString = qiniuConfigProperties.getKodo().getRegion();
|
||||
|
||||
Region region = regions.get(regionString);
|
||||
|
||||
com.qiniu.storage.Configuration configuration = new com.qiniu.storage.Configuration(region);
|
||||
// 使用http协议
|
||||
configuration.useHttpsDomains = false;
|
||||
|
||||
//上传限流,超过后上传会自动转为分片上传
|
||||
configuration.resumableUploadAPIV2BlockSize = qiniuConfigProperties.getUplimitSpeed();
|
||||
|
||||
return configuration;
|
||||
}
|
||||
|
||||
@Bean("qiniuAuth")
|
||||
public Auth qiniuAuth() {
|
||||
Auth auth = Auth.create(qiniuConfigProperties.getAccessKey(),
|
||||
qiniuConfigProperties.getSecretKey());
|
||||
return auth;
|
||||
}
|
||||
|
||||
@Bean("bucketManager")
|
||||
public BucketManager bucketManager(@Qualifier("qiniuConfiguration") com.qiniu.storage.Configuration configuration,
|
||||
@Qualifier("qiniuAuth") Auth auth
|
||||
) {
|
||||
BucketManager bucketManager = new BucketManager(auth, configuration);
|
||||
return bucketManager;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 七牛服务区域标识
|
||||
* </p>
|
||||
* 此标识是由七牛的kodo对象存储中定义,参考 {@link Region}
|
||||
*/
|
||||
public interface QiniuRegion {
|
||||
|
||||
/**
|
||||
* z0 华东
|
||||
*/
|
||||
String REGION_HUADONG = "z0";
|
||||
/**
|
||||
* z1 华北
|
||||
*/
|
||||
String REGION_HUABEI = "z1";
|
||||
/**
|
||||
* z2 华南
|
||||
*/
|
||||
String REGION_HUANAN = "z2";
|
||||
/**
|
||||
* na0 北美
|
||||
*/
|
||||
String REGION_BEIMEI = "na0";
|
||||
/**
|
||||
* as0 东南亚
|
||||
*/
|
||||
String REGION_DONGNANYA = "as0";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package com.itheima.sfbx.file.handler.qiniu.impl;
|
||||
|
||||
import com.aliyun.oss.ClientException;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.gson.Gson;
|
||||
import com.itheima.sfbx.file.handler.qiniu.properties.QiniuProperties;
|
||||
import com.itheima.sfbx.framework.commons.enums.file.FileEnum;
|
||||
import com.itheima.sfbx.framework.commons.exception.ProjectException;
|
||||
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
|
||||
import com.itheima.sfbx.file.handler.AbsFileStorageHandler;
|
||||
import com.itheima.sfbx.file.handler.FileStorageHandler;
|
||||
import com.itheima.sfbx.file.pojo.File;
|
||||
import com.qiniu.common.QiniuException;
|
||||
import com.qiniu.http.Response;
|
||||
import com.qiniu.storage.BucketManager;
|
||||
import com.qiniu.storage.Configuration;
|
||||
import com.qiniu.storage.DownloadUrl;
|
||||
import com.qiniu.storage.UploadManager;
|
||||
import com.qiniu.storage.model.BatchStatus;
|
||||
import com.qiniu.storage.model.DefaultPutRet;
|
||||
import com.qiniu.util.Auth;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassName OssFileStorageHandlerImpl.java
|
||||
* @Description 七牛云文件上传
|
||||
*/
|
||||
@Slf4j
|
||||
@Service("kodoFileStorageHandler")
|
||||
public class KodoFileStorageHandlerImpl extends AbsFileStorageHandler implements FileStorageHandler {
|
||||
|
||||
@Autowired
|
||||
private Auth qiniuAuth;
|
||||
|
||||
@Autowired
|
||||
private Configuration qiniuConfiguration;
|
||||
|
||||
@Autowired
|
||||
private BucketManager bucketManager;
|
||||
|
||||
@Autowired
|
||||
QiniuProperties qiniuConfigProperties;
|
||||
|
||||
@Override
|
||||
public String uploadFile(String suffix, String filename, String bucketName, boolean autoCatalog, InputStream inputStream) {
|
||||
if (EmptyUtil.isNullOrEmpty(bucketName)){
|
||||
bucketName=qiniuConfigProperties.getKodo().getBucketName();
|
||||
}
|
||||
String pathUrl = null;
|
||||
// 是否自动生成存储路径并设置文件路径和名称(Key)
|
||||
String key = autoCatalog?builderOssPath(filename):filename;
|
||||
log.info("七牛Kodo文件上传开始:{}", key);
|
||||
try {
|
||||
String upToken = qiniuAuth.uploadToken(bucketName);
|
||||
String mimeType = metaMimeTypeMap.get(suffix);
|
||||
UploadManager uploadManager = new UploadManager(qiniuConfiguration);
|
||||
Response response = uploadManager.put(inputStream, key, upToken, null, mimeType);
|
||||
//解析上传成功的结果
|
||||
DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
|
||||
if (!(StringUtils.isEmpty(putRet.key))) {
|
||||
log.info("七牛Kodo文件上传成功:{}", putRet.key);
|
||||
pathUrl = putRet.key;
|
||||
}
|
||||
} catch (QiniuException oe) {
|
||||
log.error("七牛Kodo文件上传错误:{}", oe);
|
||||
throw new ProjectException(FileEnum.UPLOAD_FAIL);
|
||||
} catch (ClientException ce) {
|
||||
log.error("七牛Kodo文件上传客户端错误:{}", ce);
|
||||
throw new ProjectException(FileEnum.UPLOAD_FAIL);
|
||||
}
|
||||
return pathUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File initiateMultipartUpload(String suffix, String filename, String bucketName, boolean autoCatalog) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadPart(String uploadId, String filename, int partNumber, long partSize, String bucketName, InputStream inputStream) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String completeMultipartUpload(String uploadId, List<String> partETags, String filename, String bucketName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* 七牛获得文件的inputStream
|
||||
* 1.官方文档只有获得文件的URL路径地址的接口,没有直接获得Inputstream
|
||||
* 2.URL地址需要获得bucket所属于域名
|
||||
* 3.通过域名来获得文件的URL
|
||||
* 4.通过URL转为InputStream
|
||||
* */
|
||||
@Override
|
||||
public InputStream downloadFile(String bucketName, String pathUrl) throws IOException {
|
||||
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
// 默认获得指定Bucket第一个域名
|
||||
String[] domainList = bucketManager.domainList(bucketName);
|
||||
String domain = domainList[0];
|
||||
// 获得文件的路径并转为URL对象
|
||||
DownloadUrl downloadUrl = new DownloadUrl(domain, false, pathUrl);
|
||||
String buildURL = downloadUrl.buildURL();
|
||||
URL url = new URL(buildURL);
|
||||
// URL转为InputStream
|
||||
inputStream = url.openStream();
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("七牛Kodo获得文件输入流失败:{}", e);
|
||||
throw new ProjectException(FileEnum.UPLOAD_FAIL);
|
||||
}
|
||||
|
||||
return inputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String bucketName, String pathUrl) {
|
||||
try {
|
||||
bucketManager.delete(bucketName, pathUrl);
|
||||
} catch (QiniuException e) {
|
||||
//如果遇到异常,说明删除失败
|
||||
log.error("七牛Kodo获得文件输入流失败:{}", e);
|
||||
throw new ProjectException(FileEnum.DELETE_FAIL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteBatch(String bucketName, List<String> pathUrls) {
|
||||
|
||||
try {
|
||||
BucketManager.BatchOperations batchOperations = new BucketManager.BatchOperations();
|
||||
batchOperations.addDeleteOp(bucketName, pathUrls.toArray(new String[0]));
|
||||
Response response = bucketManager.batch(batchOperations);
|
||||
BatchStatus[] batchStatusList = response.jsonToObject(BatchStatus[].class);
|
||||
|
||||
for (int i = 0; i < pathUrls.size(); i++) {
|
||||
BatchStatus status = batchStatusList[i];
|
||||
String key = pathUrls.get(i);
|
||||
if (status.code != 200) {
|
||||
log.error(status.data.error);
|
||||
log.error("七牛Kodo批量删除文件失败:key 为 {}", key);
|
||||
throw new ProjectException(FileEnum.DELETE_FAIL);
|
||||
}
|
||||
}
|
||||
} catch (QiniuException e) {
|
||||
//如果遇到异常,说明删除失败
|
||||
log.error("七牛Kodo批量删除文件失败:{}", e);
|
||||
throw new ProjectException(FileEnum.DELETE_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileContent(String bucketName, String pathUrl) throws IOException {
|
||||
InputStream inputStream = downloadFile(bucketName, pathUrl);
|
||||
return new String(ByteStreams.toByteArray(inputStream));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.itheima.sfbx.file.handler.qiniu.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @Description 阿里云OSS上传配置类
|
||||
*/
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@ConfigurationProperties("spring.cloud.qiniu")
|
||||
public class QiniuProperties {
|
||||
|
||||
//访问key
|
||||
private String accessKey ;
|
||||
|
||||
//密钥key
|
||||
private String secretKey ;
|
||||
|
||||
//是否限流
|
||||
private Boolean islimitSpeed = true;
|
||||
|
||||
//上传限流,超过后上传会自动转为分片上传
|
||||
private int uplimitSpeed = 1024 * 1024 * 8;
|
||||
|
||||
|
||||
private KodoProperties kodo;
|
||||
|
||||
|
||||
public static class KodoProperties {
|
||||
|
||||
/**
|
||||
* 存款空间区域标识,参考 {@link com.qiniu.storage.Region} 中的region属性值
|
||||
*/
|
||||
private String region;
|
||||
|
||||
/**
|
||||
* 存储空间的名称
|
||||
*/
|
||||
private String bucketName;
|
||||
|
||||
/**
|
||||
* 存款空间访问域名
|
||||
*/
|
||||
private String endpoint;
|
||||
|
||||
public KodoProperties() {
|
||||
}
|
||||
|
||||
public KodoProperties(String region, String bucketName) {
|
||||
this.region = region;
|
||||
this.bucketName = bucketName;
|
||||
}
|
||||
public KodoProperties(String region, String bucketName, String endpoint) {
|
||||
this.region = region;
|
||||
this.bucketName = bucketName;
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
|
||||
public String getEndpoint() {
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
public void setEndpoint(String endpoint) {
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
|
||||
public String getRegion() {
|
||||
return region;
|
||||
}
|
||||
|
||||
public void setRegion(String region) {
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
public String getBucketName() {
|
||||
return bucketName;
|
||||
}
|
||||
|
||||
public void setBucketName(String bucketName) {
|
||||
this.bucketName = bucketName;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.itheima.sfbx.file.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.itheima.sfbx.file.pojo.File;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* @Description:附件Mapper接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface FileMapper extends BaseMapper<File> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.itheima.sfbx.file.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.itheima.sfbx.file.pojo.FilePart;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* @Description:Mapper接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface FilePartMapper extends BaseMapper<FilePart> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.itheima.sfbx.file.pojo;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.itheima.sfbx.framework.mybatisplus.basic.BasePojo;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @Description:附件
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("tab_file")
|
||||
@ApiModel(value="File对象", description="附件")
|
||||
public class File extends BasePojo {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Builder
|
||||
public File(Long id, String dataState, Long businessId, String businessType, String suffix, String fileName, String pathUrl, String storeFlag, String bucketName, String uploadId, String md5, String status, String companyNo) {
|
||||
super(id, dataState);
|
||||
this.businessId = businessId;
|
||||
this.businessType = businessType;
|
||||
this.suffix = suffix;
|
||||
this.fileName = fileName;
|
||||
this.pathUrl = pathUrl;
|
||||
this.storeFlag = storeFlag;
|
||||
this.bucketName = bucketName;
|
||||
this.uploadId = uploadId;
|
||||
this.md5 = md5;
|
||||
this.status = status;
|
||||
this.companyNo = companyNo;
|
||||
}
|
||||
|
||||
@ApiModelProperty(value = "业务ID")
|
||||
private Long businessId;
|
||||
|
||||
@ApiModelProperty(value = "业务类型")
|
||||
private String businessType;
|
||||
|
||||
@ApiModelProperty(value = "后缀名")
|
||||
private String suffix;
|
||||
|
||||
@ApiModelProperty(value = "文件名")
|
||||
private String fileName;
|
||||
|
||||
@ApiModelProperty(value = "访问路径")
|
||||
private String pathUrl;
|
||||
|
||||
@ApiModelProperty(value = "存储源标识,参考FileConstant")
|
||||
private String storeFlag;
|
||||
|
||||
@ApiModelProperty(value = "存储空间名称")
|
||||
private String bucketName;
|
||||
|
||||
@ApiModelProperty(value = "分片上传文件Id")
|
||||
private String uploadId;
|
||||
|
||||
@ApiModelProperty(value = "md5值")
|
||||
private String md5;
|
||||
|
||||
@ApiModelProperty(value = "状态:上传中【sending】,完成【succeed】,失败【failed】")
|
||||
private String status;
|
||||
|
||||
@ApiModelProperty(value = "企业号")
|
||||
private String companyNo;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.itheima.sfbx.file.pojo;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.itheima.sfbx.framework.mybatisplus.basic.BasePojo;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @Description:
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("tab_file_part")
|
||||
@ApiModel(value="FilePart对象", description="分片上传")
|
||||
public class FilePart extends BasePojo {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Builder
|
||||
public FilePart(Long id, String dataState, String uploadId, Integer partNumber, String uploadResult, String md5, String bucketName, String fileName, String storeFlag, String companyNo) {
|
||||
super(id, dataState);
|
||||
this.uploadId = uploadId;
|
||||
this.partNumber = partNumber;
|
||||
this.uploadResult = uploadResult;
|
||||
this.md5 = md5;
|
||||
this.bucketName = bucketName;
|
||||
this.fileName = fileName;
|
||||
this.storeFlag = storeFlag;
|
||||
this.companyNo = companyNo;
|
||||
}
|
||||
|
||||
@ApiModelProperty(value = "唯一上传id")
|
||||
private String uploadId;
|
||||
|
||||
@ApiModelProperty(value = "当前片数")
|
||||
private Integer partNumber;
|
||||
|
||||
@ApiModelProperty(value = "分片上传结果(json)")
|
||||
private String uploadResult;
|
||||
|
||||
@ApiModelProperty(value = "md5值")
|
||||
private String md5;
|
||||
|
||||
@ApiModelProperty(value = "存储空间名称")
|
||||
private String bucketName;
|
||||
|
||||
@ApiModelProperty(value = "文件名")
|
||||
private String fileName;
|
||||
|
||||
@ApiModelProperty(value = "存储源标识,参考FileConstant")
|
||||
private String storeFlag;
|
||||
|
||||
@ApiModelProperty(value = "企业号")
|
||||
private String companyNo;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.itheima.sfbx.file.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.itheima.sfbx.file.pojo.FilePart;
|
||||
import com.itheima.sfbx.framework.commons.dto.file.FilePartVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Description:服务类
|
||||
*/
|
||||
public interface IFilePartService extends IService<FilePart> {
|
||||
|
||||
/**
|
||||
* @Description 多条件查询分页列表
|
||||
* @param filePartVo 查询条件
|
||||
* @param pageNum 页码
|
||||
* @param pageSize 每页条数
|
||||
* @return Page<FilePart>
|
||||
*/
|
||||
Page<FilePart> findFilePartPage(FilePartVO filePartVo, int pageNum, int pageSize);
|
||||
|
||||
/**
|
||||
* @Description 创建
|
||||
* @param filePartVo 对象信息
|
||||
* @return FilePart
|
||||
*/
|
||||
FilePart createFilePart(FilePartVO filePartVo);
|
||||
|
||||
/**
|
||||
* @Description 修改
|
||||
* @param filePartVo 对象信息
|
||||
* @return Boolean
|
||||
*/
|
||||
Boolean updateFilePart(FilePartVO filePartVo);
|
||||
|
||||
/**
|
||||
* @Description 删除
|
||||
* @param checkedIds 选择中对象Ids
|
||||
* @return Boolean
|
||||
*/
|
||||
Boolean deleteFilePart(String[] checkedIds);
|
||||
|
||||
/**
|
||||
* @description 多条件查询列表
|
||||
* @param filePartVo 查询条件
|
||||
* @return: List<FilePart>
|
||||
*/
|
||||
List<FilePart> findFilePartList(FilePartVO filePartVo);
|
||||
|
||||
/**
|
||||
* @description 按upLoadId删除记录
|
||||
* @param upLoadId 上传ID
|
||||
* @return: List<FilePart>
|
||||
*/
|
||||
Boolean deleteFilePartByUpLoadId(String upLoadId);
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package com.itheima.sfbx.file.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.itheima.sfbx.framework.commons.dto.file.FilePartVO;
|
||||
import com.itheima.sfbx.framework.commons.dto.file.UploadMultipartFile;
|
||||
import com.itheima.sfbx.file.pojo.File;
|
||||
import com.itheima.sfbx.framework.commons.dto.file.FileVO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @Description:附件 服务类
|
||||
*/
|
||||
public interface IFileService extends IService<File> {
|
||||
|
||||
/**
|
||||
* @Description 按业务ID查询附件
|
||||
* @param businessId 附件对象业务Id
|
||||
* @return
|
||||
*/
|
||||
List<FileVO> findFileVoByBusinessId(Long businessId) ;
|
||||
|
||||
/**
|
||||
* @Description 附件列表
|
||||
* @param fileVO 查询条件
|
||||
* @return
|
||||
*/
|
||||
Page<FileVO> findFileVOPage(FileVO fileVO, int pageNum, int pageSize);
|
||||
|
||||
/***
|
||||
* @description 定时清理文件
|
||||
* @return
|
||||
*/
|
||||
List<FileVO> needClearFile();
|
||||
|
||||
/***
|
||||
* @description 延迟队列清理文件
|
||||
* @return
|
||||
*/
|
||||
FileVO needClearFileById(String id);
|
||||
|
||||
/**
|
||||
* @Description 业务绑定单个附件
|
||||
* @param fileVO 附件对象
|
||||
* @return
|
||||
*/
|
||||
FileVO bindFile(FileVO fileVO);
|
||||
|
||||
/**
|
||||
* @Description 相同业务绑定多个附件
|
||||
* @param fileVOs 相同业务的多个附件对象
|
||||
* @return
|
||||
*/
|
||||
List<FileVO> bindBatchFile(List<FileVO> fileVOs);
|
||||
|
||||
/**
|
||||
* @Description 移除业务原图片,并绑定新的图片到业务上
|
||||
* @param fileVO 附件对象
|
||||
* @return
|
||||
*/
|
||||
Boolean replaceBindFile(FileVO fileVO);
|
||||
|
||||
/**
|
||||
* @Description 移除业务原图片,并批量绑定新的图片到业务上
|
||||
* @param fileVOs 附件对象
|
||||
* @return
|
||||
*/
|
||||
Boolean replaceBindBatchFile(List<FileVO> fileVOs);
|
||||
|
||||
/**
|
||||
* @description 按业务ID查询附件
|
||||
* @param businessIds 业务ids
|
||||
* @return java.util.List<com.itheima.travel.req.FileVO>
|
||||
*/
|
||||
List<FileVO> findInBusinessIds(List<Long> businessIds);
|
||||
|
||||
/**
|
||||
* @Description 删除业务相关附件
|
||||
* @param businessIds 附件信息ids
|
||||
* @return
|
||||
*/
|
||||
Boolean deleteInBusinessIds(List<Long> businessIds);
|
||||
|
||||
/**
|
||||
* @Description 删除业务相关附件
|
||||
* @param ids 附件信息ids
|
||||
* @return
|
||||
*/
|
||||
Boolean deleteInIds(List<Long> ids);
|
||||
|
||||
/**
|
||||
* @Description 定时清理文件
|
||||
* @return Boolean
|
||||
*/
|
||||
Boolean clearFile();
|
||||
|
||||
/**
|
||||
* @Description 定时清理文件
|
||||
* @return Boolean
|
||||
*/
|
||||
Boolean clearFileById(String fileId);
|
||||
|
||||
/**
|
||||
* @Description 查询所有业务对应附件
|
||||
* @return Set<Long>
|
||||
*/
|
||||
Set<Long> findBusinessIdAll();
|
||||
|
||||
/***
|
||||
* @description 文件简单上传
|
||||
*
|
||||
* @param uploadMultipartFile
|
||||
* @param fileVO
|
||||
* @return FileVO
|
||||
*/
|
||||
FileVO upLoad(UploadMultipartFile uploadMultipartFile, FileVO fileVO);
|
||||
|
||||
/***
|
||||
* @description 初始化分片上传
|
||||
*
|
||||
* @param fileVO
|
||||
* @return FileVO
|
||||
*/
|
||||
FileVO initiateMultipartUpload(FileVO fileVO);
|
||||
|
||||
/***
|
||||
* @description 分片每个分片
|
||||
* @param uploadMultipartFile
|
||||
* @param filePartVo
|
||||
* @return String
|
||||
*/
|
||||
String uploadPart(UploadMultipartFile uploadMultipartFile, FilePartVO filePartVo);
|
||||
|
||||
/***
|
||||
* @description 合并所有分片
|
||||
* @param fileVO
|
||||
* @return String
|
||||
*/
|
||||
String completeMultipartUpload(FileVO fileVO);
|
||||
|
||||
/***
|
||||
* @description 文件下载
|
||||
* @param fileId
|
||||
* @return FileVO
|
||||
*/
|
||||
FileVO downLoad(Long fileId);
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.itheima.sfbx.file.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.itheima.sfbx.framework.commons.dto.file.FilePartVO;
|
||||
import com.itheima.sfbx.framework.commons.utils.BeanConv;
|
||||
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
|
||||
import com.itheima.sfbx.file.mapper.FilePartMapper;
|
||||
import com.itheima.sfbx.file.pojo.FilePart;
|
||||
import com.itheima.sfbx.file.service.IFilePartService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Description:服务实现类
|
||||
*/
|
||||
@Service
|
||||
public class FilePartServiceImpl extends ServiceImpl<FilePartMapper, FilePart> implements IFilePartService {
|
||||
|
||||
@Override
|
||||
public Page<FilePart> findFilePartPage(FilePartVO filePartVo, int pageNum, int pageSize) {
|
||||
//构建分页对象
|
||||
Page<FilePart> page = new Page<>(pageNum,pageSize);
|
||||
//构建查询条件
|
||||
QueryWrapper<FilePart> queryWrapper = new QueryWrapper<>();
|
||||
//构建多条件查询,代码生成后自己可自行调整
|
||||
//唯一上传id查询
|
||||
if (!EmptyUtil.isNullOrEmpty(filePartVo.getUploadId())) {
|
||||
queryWrapper.lambda().eq(FilePart::getUploadId,filePartVo.getUploadId());
|
||||
}
|
||||
//当前片数查询
|
||||
if (!EmptyUtil.isNullOrEmpty(filePartVo.getPartNumber())) {
|
||||
queryWrapper.lambda().eq(FilePart::getPartNumber,filePartVo.getPartNumber());
|
||||
}
|
||||
//分片上传结果(json)查询
|
||||
if (!EmptyUtil.isNullOrEmpty(filePartVo.getUploadResult())) {
|
||||
queryWrapper.lambda().eq(FilePart::getUploadResult,filePartVo.getUploadResult());
|
||||
}
|
||||
//状态查询
|
||||
if (!EmptyUtil.isNullOrEmpty(filePartVo.getDataState())) {
|
||||
queryWrapper.lambda().eq(FilePart::getDataState,filePartVo.getDataState());
|
||||
}
|
||||
//按创建时间降序
|
||||
queryWrapper.lambda().orderByDesc(FilePart::getCreateTime);
|
||||
//执行分页查询
|
||||
return page(page, queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilePart createFilePart(FilePartVO filePartVo) {
|
||||
//转换FilePartVO为FilePart
|
||||
FilePart filePart = BeanConv.toBean(filePartVo, FilePart.class);
|
||||
boolean flag = save(filePart);
|
||||
if (flag){
|
||||
return filePart;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean updateFilePart(FilePartVO filePartVo) {
|
||||
//转换FilePartVO为FilePart
|
||||
FilePart filePart = BeanConv.toBean(filePartVo, FilePart.class);
|
||||
return updateById(filePart);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean deleteFilePart(String[] checkedIds) {
|
||||
//转换数组为集合
|
||||
List<String> ids = Arrays.asList(checkedIds);
|
||||
List<Long> idsLong = new ArrayList<>();
|
||||
ids.forEach(n->{
|
||||
idsLong.add(Long.valueOf(n));
|
||||
});
|
||||
return removeByIds(idsLong);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FilePart> findFilePartList(FilePartVO filePartVo) {
|
||||
//构建查询条件
|
||||
QueryWrapper<FilePart> queryWrapper = new QueryWrapper<>();
|
||||
if (!EmptyUtil.isNullOrEmpty(filePartVo.getId())) {
|
||||
queryWrapper.lambda().eq(FilePart::getId,filePartVo.getId());
|
||||
}
|
||||
//唯一上传id查询
|
||||
if (!EmptyUtil.isNullOrEmpty(filePartVo.getUploadId())) {
|
||||
queryWrapper.lambda().eq(FilePart::getUploadId,filePartVo.getUploadId());
|
||||
}
|
||||
//当前片数查询
|
||||
if (!EmptyUtil.isNullOrEmpty(filePartVo.getPartNumber())) {
|
||||
queryWrapper.lambda().eq(FilePart::getPartNumber,filePartVo.getPartNumber());
|
||||
}
|
||||
//当前片数查询
|
||||
if (!EmptyUtil.isNullOrEmpty(filePartVo.getMd5())) {
|
||||
queryWrapper.lambda().eq(FilePart::getMd5,filePartVo.getMd5());
|
||||
}
|
||||
//状态查询
|
||||
if (!EmptyUtil.isNullOrEmpty(filePartVo.getDataState())) {
|
||||
queryWrapper.lambda().eq(FilePart::getDataState,filePartVo.getDataState());
|
||||
}
|
||||
return list(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean deleteFilePartByUpLoadId(String upLoadId) {
|
||||
UpdateWrapper<FilePart> updateWrapperp = new UpdateWrapper<>();
|
||||
updateWrapperp.lambda().eq(FilePart::getUploadId,upLoadId);
|
||||
return remove(updateWrapperp);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,593 @@
|
||||
package com.itheima.sfbx.file.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.aliyun.oss.model.UploadPartResult;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.itheima.sfbx.file.adapter.FileStorageAdapter;
|
||||
import com.itheima.sfbx.file.mapper.FileMapper;
|
||||
import com.itheima.sfbx.file.pojo.File;
|
||||
import com.itheima.sfbx.file.pojo.FilePart;
|
||||
import com.itheima.sfbx.file.service.IFilePartService;
|
||||
import com.itheima.sfbx.file.service.IFileService;
|
||||
import com.itheima.sfbx.file.utils.FileUrlContext;
|
||||
import com.itheima.sfbx.framework.commons.constant.basic.SuperConstant;
|
||||
import com.itheima.sfbx.framework.commons.constant.file.FileCacheConstant;
|
||||
import com.itheima.sfbx.framework.commons.constant.file.FileConstant;
|
||||
import com.itheima.sfbx.framework.commons.dto.file.FileVO;
|
||||
import com.itheima.sfbx.framework.commons.dto.file.FilePartVO;
|
||||
import com.itheima.sfbx.framework.commons.dto.file.UploadMultipartFile;
|
||||
import com.itheima.sfbx.framework.commons.enums.file.FileEnum;
|
||||
import com.itheima.sfbx.framework.commons.exception.ProjectException;
|
||||
import com.itheima.sfbx.framework.commons.utils.BeanConv;
|
||||
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
|
||||
import com.itheima.sfbx.framework.commons.utils.EncodesUtil;
|
||||
import com.itheima.sfbx.framework.commons.utils.ExceptionsUtil;
|
||||
import com.itheima.sfbx.framework.rabbitmq.pojo.MqMessage;
|
||||
import com.itheima.sfbx.framework.rabbitmq.source.FileSource;
|
||||
import lombok.Cleanup;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.redisson.api.RLock;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.CachePut;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.cache.annotation.Caching;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @Description:附件 服务实现类
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class FileServiceImpl extends ServiceImpl<FileMapper, File> implements IFileService {
|
||||
|
||||
@Value("${file-delay-time}")
|
||||
Integer fileDelayTime;
|
||||
|
||||
@Autowired
|
||||
private FileUrlContext fileUrlContext;
|
||||
|
||||
@Autowired
|
||||
FileStorageAdapter fileStorageAdapter;
|
||||
|
||||
@Autowired
|
||||
IdentifierGenerator identifierGenerator;
|
||||
|
||||
@Autowired
|
||||
FileSource fileSource;
|
||||
|
||||
@Autowired
|
||||
RedissonClient redissonClient;
|
||||
|
||||
@Autowired
|
||||
IFilePartService filePartService;
|
||||
|
||||
private QueryWrapper<File> queryWrapper(FileVO fileVO){
|
||||
QueryWrapper<File> queryWrapper = new QueryWrapper<>();
|
||||
if (!EmptyUtil.isNullOrEmpty(fileVO.getBusinessType())) {
|
||||
queryWrapper.lambda().eq(File::getBusinessType,fileVO.getBusinessType());
|
||||
}
|
||||
if (!EmptyUtil.isNullOrEmpty(fileVO.getFileName())) {
|
||||
queryWrapper.lambda().likeRight(File::getFileName,fileVO.getFileName());
|
||||
}
|
||||
if (!EmptyUtil.isNullOrEmpty(fileVO.getPathUrl())) {
|
||||
queryWrapper.lambda().likeRight(File::getPathUrl,fileVO.getPathUrl());
|
||||
}
|
||||
if (!EmptyUtil.isNullOrEmpty(fileVO.getDataState())) {
|
||||
queryWrapper.lambda().likeRight(File::getDataState,fileVO.getDataState());
|
||||
}
|
||||
if (!EmptyUtil.isNullOrEmpty(fileVO.getStatus())) {
|
||||
queryWrapper.lambda().likeRight(File::getStatus,fileVO.getStatus());
|
||||
}
|
||||
queryWrapper.lambda().orderByDesc(File::getCreateTime);
|
||||
return queryWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = FileCacheConstant.BUSINESS_KEY,key = "#businessId")
|
||||
public List<FileVO> findFileVoByBusinessId(Long businessId) {
|
||||
try {
|
||||
QueryWrapper<File> queryWrapper = new QueryWrapper();
|
||||
queryWrapper.lambda().eq(File::getBusinessId,businessId);
|
||||
List<File> files = list(queryWrapper);
|
||||
if (!EmptyUtil.isNullOrEmpty(files)){
|
||||
files.forEach(n->{
|
||||
String fileUrl = fileUrlContext.getFileUrl(n.getStoreFlag(), n.getPathUrl());
|
||||
n.setPathUrl(fileUrl);
|
||||
});
|
||||
}
|
||||
return BeanConv.toBeanList(files,FileVO.class);
|
||||
}catch (Exception e){
|
||||
log.error("查询业务对应附件:{}异常:{}", businessId,ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(FileEnum.SELECT_FILE_BUSINESSID_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = FileCacheConstant.PAGE,key ="#pageNum+'-'+#pageSize+'-'+#fileVO.hashCode()")
|
||||
public Page<FileVO> findFileVOPage(FileVO fileVO, int pageNum , int pageSize) {
|
||||
try {
|
||||
Page<File> page = new Page<>(pageNum,pageSize);
|
||||
QueryWrapper<File> queryWrapper = queryWrapper(fileVO);
|
||||
Page<FileVO> fileVOPage = BeanConv.toPage(page(page, queryWrapper), FileVO.class);
|
||||
if (!EmptyUtil.isNullOrEmpty(fileVOPage)&&!EmptyUtil.isNullOrEmpty(fileVOPage.getRecords())){
|
||||
fileVOPage.getRecords().forEach(n->{
|
||||
n.setPathUrl(fileUrlContext.getFileUrl(n.getStoreFlag(), n.getPathUrl()));
|
||||
});
|
||||
}
|
||||
return fileVOPage;
|
||||
}catch (Exception e){
|
||||
log.error("查询文件分页异常:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(FileEnum.PAGE_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FileVO> needClearFile() {
|
||||
try {
|
||||
QueryWrapper<File> queryWrapper = new QueryWrapper<>();
|
||||
LocalDateTime localDateTime = LocalDateTime.now().minusSeconds(fileDelayTime/1000);
|
||||
queryWrapper.lambda().isNull(File::getBusinessId).lt(File::getCreateTime,localDateTime);
|
||||
return BeanConv.toBeanList(list(queryWrapper), FileVO.class);
|
||||
}catch (Exception e){
|
||||
log.error("查询文件分页异常:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(FileEnum.SELECT_FILE_BUSINESSID_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVO needClearFileById(String id) {
|
||||
try {
|
||||
QueryWrapper<File> queryWrapper = new QueryWrapper<>();
|
||||
LocalDateTime localDateTime = LocalDateTime.now().minusSeconds(fileDelayTime/1000);
|
||||
queryWrapper.lambda().isNull(File::getBusinessId).lt(File::getCreateTime,localDateTime).eq(File::getId,id);
|
||||
return BeanConv.toBean(getOne(queryWrapper),FileVO.class);
|
||||
}catch (Exception e){
|
||||
log.error("查询文件分页异常:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(FileEnum.SELECT_FILE_BUSINESSID_FAIL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@Caching(evict = {@CacheEvict(value = FileCacheConstant.PAGE,allEntries = true),
|
||||
@CacheEvict(value = FileCacheConstant.BASIC,key = "#fileVO.id"),
|
||||
@CacheEvict(value = FileCacheConstant.BUSINESS_KEY,key = "#fileVO.businessId")})
|
||||
public FileVO bindFile(FileVO fileVO) {
|
||||
try {
|
||||
//修改file表中的businessId
|
||||
File file = BeanConv.toBean(fileVO, File.class);
|
||||
boolean flag = updateById(file);
|
||||
//构建完整返回对象
|
||||
if (flag){
|
||||
QueryWrapper<File> queryWrapper = new QueryWrapper();
|
||||
queryWrapper.lambda().eq(File::getBusinessId,fileVO.getBusinessId());
|
||||
File fileResult = getOne(queryWrapper);
|
||||
if (!EmptyUtil.isNullOrEmpty(file)){
|
||||
fileResult.setPathUrl(fileUrlContext.getFileUrl(fileResult.getStoreFlag(), fileResult.getPathUrl()));
|
||||
}
|
||||
return BeanConv.toBean(fileResult,FileVO.class);
|
||||
}
|
||||
return null;
|
||||
}catch (Exception e){
|
||||
log.error("查询文件分页异常:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(FileEnum.SELECT_FILE_BUSINESSID_FAIL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@Caching(evict = {@CacheEvict(value = FileCacheConstant.PAGE,allEntries = true),
|
||||
@CacheEvict(value = FileCacheConstant.BASIC,allEntries = true),
|
||||
@CacheEvict(value = FileCacheConstant.BUSINESS_KEY,key = "#fileVOs.get(0).getBusinessId()")})
|
||||
public List<FileVO> bindBatchFile(List<FileVO> fileVOs) {
|
||||
Long businessId = fileVOs.get(0).getBusinessId();
|
||||
if (EmptyUtil.isNullOrEmpty(businessId)) {
|
||||
throw new ProjectException(FileEnum.SELECT_BUSUBBESSID_FAIL);
|
||||
}
|
||||
try {
|
||||
//修改file表中的businessId
|
||||
updateBatchById(BeanConv.toBeanList(fileVOs, File.class));
|
||||
QueryWrapper<File> queryWrapper = new QueryWrapper();
|
||||
queryWrapper.lambda().eq(File::getBusinessId,businessId);
|
||||
List<File> files = list(queryWrapper);
|
||||
//构建完整返回对象
|
||||
if (!EmptyUtil.isNullOrEmpty(files)){
|
||||
files.forEach(n->{
|
||||
String fileUrl = fileUrlContext.getFileUrl(n.getStoreFlag(), n.getPathUrl());
|
||||
n.setPathUrl(fileUrl);
|
||||
});
|
||||
}
|
||||
return BeanConv.toBeanList(files,FileVO.class);
|
||||
} catch (Exception e) {
|
||||
log.error("绑定业务:{}异常:{}", fileVOs.toString(),ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(FileEnum.BIND_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Caching(evict = {@CacheEvict(value = FileCacheConstant.PAGE,allEntries = true),
|
||||
@CacheEvict(value = FileCacheConstant.BASIC,allEntries = true),
|
||||
@CacheEvict(value = FileCacheConstant.LIST,allEntries = true),
|
||||
@CacheEvict(value = FileCacheConstant.BUSINESS_KEY,key = "#fileVO.getBusinessId()")})
|
||||
public Boolean replaceBindFile(FileVO fileVO) {
|
||||
try {
|
||||
//删除老图片
|
||||
ArrayList<Long> ids = Lists.newArrayList();
|
||||
ids.add(fileVO.getId());
|
||||
deleteInIds(ids);
|
||||
//绑定新图片
|
||||
FileVO fileVOResult = bindFile(fileVO);
|
||||
return !EmptyUtil.isNullOrEmpty(fileVOResult);
|
||||
}catch (Exception e){
|
||||
log.error("查询文件分页异常:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(FileEnum.BIND_FAIL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Caching(evict = {@CacheEvict(value = FileCacheConstant.PAGE,allEntries = true),
|
||||
@CacheEvict(value = FileCacheConstant.BASIC,allEntries = true),
|
||||
@CacheEvict(value = FileCacheConstant.LIST,allEntries = true),
|
||||
@CacheEvict(value = FileCacheConstant.BUSINESS_KEY,key = "#fileVOs.get(0).getBusinessId()")})
|
||||
public Boolean replaceBindBatchFile(List<FileVO> fileVOs) {
|
||||
try {
|
||||
//查询当前业务图片
|
||||
QueryWrapper<File> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.lambda().eq(File::getBusinessId,fileVOs.get(0).getBusinessId());
|
||||
List<File> oldList = list(queryWrapper);
|
||||
List<Long> oldIds = oldList.stream().map(File::getId).collect(Collectors.toList());
|
||||
List<Long> newIds = fileVOs.stream().map(FileVO::getId).collect(Collectors.toList());
|
||||
//删除:老图片对新图片的差集
|
||||
List<Long> delIds = oldIds.stream().filter(n -> {
|
||||
return !newIds.contains(n);
|
||||
}).collect(Collectors.toList());
|
||||
if (!EmptyUtil.isNullOrEmpty(delIds)){
|
||||
deleteInIds(delIds);
|
||||
}
|
||||
//绑定新图片
|
||||
List<FileVO> newFiles = fileVOs.stream().filter(n -> {
|
||||
return !oldIds.contains(n.getId());
|
||||
}).collect(Collectors.toList());
|
||||
if (!EmptyUtil.isNullOrEmpty(newFiles)){
|
||||
List<FileVO> fileVOsResult = bindBatchFile(newFiles);
|
||||
return !EmptyUtil.isNullOrEmpty(fileVOsResult);
|
||||
}
|
||||
return true;
|
||||
}catch (Exception e){
|
||||
log.error("查询文件分页异常:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(FileEnum.BIND_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = FileCacheConstant.LIST,key ="#businessIds.hashCode()")
|
||||
public List<FileVO> findInBusinessIds(List<Long> businessIds) {
|
||||
try {
|
||||
QueryWrapper<File> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.lambda().in(File::getBusinessId,businessIds);
|
||||
List<FileVO> fileVOList = BeanConv.toBeanList(list(queryWrapper), FileVO.class);
|
||||
for (FileVO fileVO : fileVOList) {
|
||||
fileVO.setPathUrl(fileUrlContext.getFileUrl(fileVO.getStoreFlag(), fileVO.getPathUrl()));
|
||||
}
|
||||
return fileVOList;
|
||||
}catch (Exception e){
|
||||
log.error("查询文件分页异常:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(FileEnum.BIND_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Caching(evict = {@CacheEvict(value = FileCacheConstant.PAGE,allEntries = true),
|
||||
@CacheEvict(value = FileCacheConstant.BUSINESS_KEY,allEntries = true),
|
||||
@CacheEvict(value = FileCacheConstant.LIST,key ="#businessIds.hashCode()"),
|
||||
@CacheEvict(value = FileCacheConstant.BASIC,allEntries = true)})
|
||||
@Transactional
|
||||
public Boolean deleteInBusinessIds(List<Long> businessIds) {
|
||||
try {
|
||||
//删除数据库
|
||||
List<FileVO> files = findInBusinessIds(businessIds);
|
||||
UpdateWrapper<File> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.lambda().in(File::getBusinessId,businessIds);
|
||||
Boolean flag = remove(updateWrapper);
|
||||
if (!flag){
|
||||
throw new ProjectException(FileEnum.DELETE_FAIL);
|
||||
}
|
||||
//删除OSS中的图片
|
||||
if (!EmptyUtil.isNullOrEmpty(files)){
|
||||
List<String> getPathUrls = files.stream().map(FileVO::getPathUrl).collect(Collectors.toList());
|
||||
FileVO fileVO = files.get(0);
|
||||
String bucketName = fileVO.getBucketName();
|
||||
String storeFlag = fileVO.getStoreFlag();
|
||||
fileStorageAdapter.deleteBatch(storeFlag,bucketName,getPathUrls);
|
||||
}
|
||||
return flag;
|
||||
}catch (Exception e){
|
||||
log.error("删除业务对应附件:{}失败",businessIds);
|
||||
throw new ProjectException(FileEnum.DELETE_FILE_BUSINESSID_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Caching(evict = {@CacheEvict(value = FileCacheConstant.PAGE,allEntries = true),
|
||||
@CacheEvict(value = FileCacheConstant.BUSINESS_KEY,allEntries = true),
|
||||
@CacheEvict(value = FileCacheConstant.LIST,allEntries = true),
|
||||
@CacheEvict(value = FileCacheConstant.BASIC,allEntries = true)})
|
||||
public Boolean deleteInIds(List<Long> ids) {
|
||||
try {
|
||||
QueryWrapper<File> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.lambda().in(File::getId,ids);
|
||||
List<File> files = list(queryWrapper);
|
||||
boolean flag = removeByIds(ids);
|
||||
if (!flag){
|
||||
throw new ProjectException(FileEnum.DELETE_FAIL);
|
||||
}
|
||||
//移除对象存储数据
|
||||
for (File file : files) {
|
||||
fileStorageAdapter.delete(file.getStoreFlag(),file.getBucketName(),file.getPathUrl());
|
||||
}
|
||||
return flag;
|
||||
}catch (Exception e){
|
||||
log.error("删除文件异常:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(FileEnum.DELETE_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Caching(evict = {@CacheEvict(value = FileCacheConstant.PAGE,allEntries = true),
|
||||
@CacheEvict(value = FileCacheConstant.BUSINESS_KEY,allEntries = true),
|
||||
@CacheEvict(value = FileCacheConstant.BASIC,allEntries = true)})
|
||||
@Transactional
|
||||
public Boolean clearFile() {
|
||||
try {
|
||||
//查询需要清理的文件
|
||||
List<FileVO> fileList = needClearFile();
|
||||
if (EmptyUtil.isNullOrEmpty(fileList)){
|
||||
return true;
|
||||
}
|
||||
List<Long> fileListIds = fileList.stream().map(FileVO::getId).collect(Collectors.toList());
|
||||
//移除数据库信息
|
||||
Boolean flag = removeByIds(fileListIds);
|
||||
//移除对象存储数据
|
||||
for (FileVO fileVO : fileList) {
|
||||
fileStorageAdapter.delete(fileVO.getStoreFlag(),fileVO.getBucketName(),fileVO.getPathUrl());
|
||||
}
|
||||
return flag;
|
||||
}catch (Exception e){
|
||||
log.error("定时清理文件异常:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(FileEnum.CLEAR_FILE_TASK_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Caching(evict = {@CacheEvict(value = FileCacheConstant.PAGE,allEntries = true),
|
||||
@CacheEvict(value = FileCacheConstant.BASIC,key = "#id"),
|
||||
@CacheEvict(value = FileCacheConstant.BUSINESS_KEY,allEntries = true)})
|
||||
@Transactional
|
||||
public Boolean clearFileById(String id) {
|
||||
try {
|
||||
FileVO fileVO = needClearFileById(id);
|
||||
if (EmptyUtil.isNullOrEmpty(fileVO)){
|
||||
return true;
|
||||
}
|
||||
Boolean flag = removeById(fileVO.getId());
|
||||
if (!flag){
|
||||
throw new ProjectException(FileEnum.DELETE_FAIL);
|
||||
}
|
||||
//删除OSS中的图片
|
||||
if (!EmptyUtil.isNullOrEmpty(fileVO)){
|
||||
fileStorageAdapter.delete(fileVO.getStoreFlag(),fileVO.getBucketName(),fileVO.getPathUrl());
|
||||
}
|
||||
return flag;
|
||||
}catch (Exception e){
|
||||
log.error("定时清理文件异常:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(FileEnum.CLEAR_FILE_TASK_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> findBusinessIdAll() {
|
||||
try {
|
||||
QueryWrapper<File> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.lambda().eq(File::getDataState, SuperConstant.DATA_STATE_0).isNotNull(File::getBusinessId);
|
||||
List<File> list = list(queryWrapper);
|
||||
if (!EmptyUtil.isNullOrEmpty(list)){
|
||||
return list.stream().map(File::getBusinessId).collect(Collectors.toSet());
|
||||
}
|
||||
return null;
|
||||
}catch (Exception e){
|
||||
log.error("查询附件列表异常:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(FileEnum.SELECT_BUSUBBESSID_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Caching(
|
||||
evict = {
|
||||
@CacheEvict(value = FileCacheConstant.PAGE,allEntries = true),
|
||||
@CacheEvict(value = FileCacheConstant.BUSINESS_KEY,allEntries = true)},
|
||||
put={@CachePut(value =FileCacheConstant.BASIC,key = "#result.id")})
|
||||
@Transactional
|
||||
public FileVO upLoad(UploadMultipartFile multipartFile, FileVO fileVO) throws ProjectException {
|
||||
//获得文件ByteArrayInputStream
|
||||
ByteArrayInputStream byteArrayInputStream =new ByteArrayInputStream(multipartFile.getFileByte());
|
||||
try {
|
||||
//文件重命名
|
||||
String filename = identifierGenerator.nextId(fileVO)+"-"+multipartFile.getOriginalFilename();;
|
||||
fileVO.setFileName(filename);
|
||||
//文件后缀名
|
||||
String suffix = fileVO.getFileName().substring(fileVO.getFileName().lastIndexOf("."));
|
||||
fileVO.setSuffix(suffix);
|
||||
//调用简单上传
|
||||
String pathUrl = fileStorageAdapter.uploadFile(fileVO, byteArrayInputStream);
|
||||
//保存数据库
|
||||
File file = BeanConv.toBean(fileVO, File.class);
|
||||
file.setStatus(FileConstant.STATUS_SUCCEED);
|
||||
file.setPathUrl(pathUrl);
|
||||
boolean flag = save(file);
|
||||
if (!flag){
|
||||
throw new ProjectException(FileEnum.UPLOAD_FAIL);
|
||||
}
|
||||
//补全完整路径
|
||||
pathUrl = fileUrlContext.getFileUrl(fileVO.getStoreFlag(), pathUrl);
|
||||
fileVO.setId(file.getId());
|
||||
fileVO.setPathUrl(pathUrl);
|
||||
//发送延迟信息:上传如果超过10分钟不进行文件业务绑定则会被消息队列清空
|
||||
Long messageId = (Long) identifierGenerator.nextId(fileVO);
|
||||
MqMessage mqMessage = MqMessage.builder()
|
||||
.id(messageId)
|
||||
.title("file-message")
|
||||
.content(JSONObject.toJSONString(fileVO))
|
||||
.messageType("file-request")
|
||||
.produceTime(Timestamp.valueOf(LocalDateTime.now()))
|
||||
.sender("system")
|
||||
.build();
|
||||
Message<MqMessage> message = MessageBuilder.withPayload(mqMessage).setHeader("x-delay", fileDelayTime).build();
|
||||
fileSource.fileOutput().send(message);
|
||||
return fileVO;
|
||||
}catch (Exception e) {
|
||||
log.error("文件上传异常:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(FileEnum.UPLOAD_FAIL);
|
||||
}finally {
|
||||
if (byteArrayInputStream != null) {
|
||||
try {
|
||||
byteArrayInputStream.close();
|
||||
} catch (Exception e) {
|
||||
log.error("文件上传操作失败:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Caching(evict = {
|
||||
@CacheEvict(value = FileCacheConstant.PAGE,allEntries = true),
|
||||
@CacheEvict(value = FileCacheConstant.BUSINESS_KEY,allEntries = true)},
|
||||
put={@CachePut(value =FileCacheConstant.BASIC,key = "#result.id")})
|
||||
@Transactional
|
||||
public FileVO initiateMultipartUpload(FileVO fileVO) {
|
||||
try {
|
||||
//文件重命名
|
||||
String filename = identifierGenerator.nextId(fileVO)+"-"+fileVO.getFileName();
|
||||
fileVO.setFileName(filename);
|
||||
//文件后缀名
|
||||
String suffix = fileVO.getFileName().substring(fileVO.getFileName().lastIndexOf("."));
|
||||
fileVO.setSuffix(suffix);
|
||||
//分片上传-初始化
|
||||
File file = fileStorageAdapter.initiateMultipartUpload(fileVO);
|
||||
//保存数据库
|
||||
file.setBusinessType(fileVO.getBusinessType());
|
||||
file.setSuffix(suffix);
|
||||
file.setStoreFlag(fileVO.getStoreFlag());
|
||||
file.setMd5(fileVO.getMd5());
|
||||
file.setCompanyNo(fileVO.getCompanyNo());
|
||||
file.setStatus(FileConstant.STATUS_SENDING);
|
||||
boolean flag = save(file);
|
||||
if (!flag){
|
||||
throw new ProjectException(FileEnum.UPLOAD_FAIL);
|
||||
}
|
||||
//补全完整路径
|
||||
FileVO fileVOResult = BeanConv.toBean(file, FileVO.class);
|
||||
String pathUrl = fileUrlContext.getFileUrl(fileVOResult.getStoreFlag(), fileVOResult.getPathUrl());
|
||||
fileVOResult.setPathUrl(pathUrl);
|
||||
//发送队列信息:上传如果超过10分钟不进行文件业务绑定则会被消息队列清空
|
||||
Long messageId = (Long) identifierGenerator.nextId(fileVOResult);
|
||||
MqMessage mqMessage = MqMessage.builder()
|
||||
.id(messageId)
|
||||
.title("file-message")
|
||||
.content(JSONObject.toJSONString(fileVOResult))
|
||||
.messageType("file-request")
|
||||
.produceTime(Timestamp.valueOf(LocalDateTime.now()))
|
||||
.sender("system")
|
||||
.build();
|
||||
Message<MqMessage> message = MessageBuilder.withPayload(mqMessage).setHeader("x-delay", fileDelayTime).build();
|
||||
fileSource.fileOutput().send(message);
|
||||
return fileVOResult;
|
||||
} catch (Exception e) {
|
||||
log.error("文件上传初始化异常:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(FileEnum.INIT_UPLOAD_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public String uploadPart(UploadMultipartFile multipartFile, FilePartVO filePartVO) {
|
||||
try {
|
||||
//上传分片数据
|
||||
String partETagString = fileStorageAdapter.uploadPart(filePartVO, new ByteArrayInputStream(multipartFile.getFileByte()));
|
||||
//保存分片信息
|
||||
FilePart filePart = BeanConv.toBean(filePartVO, FilePart.class);
|
||||
filePart.setUploadResult(partETagString);
|
||||
filePartService.save(filePart);
|
||||
return partETagString;
|
||||
}catch (Exception e) {
|
||||
log.error("文件分片上传异常:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(FileEnum.UPLOAD_PART_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public String completeMultipartUpload(FileVO fileVO) {
|
||||
try {
|
||||
//移除分片记录
|
||||
Boolean flag = filePartService.deleteFilePartByUpLoadId(fileVO.getUploadId());
|
||||
if (!flag){
|
||||
throw new ProjectException(FileEnum.COMPLETE_PART_FAIL);
|
||||
}
|
||||
//修改文件记录状态
|
||||
fileVO.setStatus(FileConstant.STATUS_SUCCEED);
|
||||
flag = updateById(BeanConv.toBean(fileVO,File.class));
|
||||
if (!flag){
|
||||
throw new ProjectException(FileEnum.COMPLETE_PART_FAIL);
|
||||
}
|
||||
//合并结果
|
||||
return fileStorageAdapter.completeMultipartUpload(fileVO);
|
||||
} catch (Exception e) {
|
||||
log.error("文件分片上传异常:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(FileEnum.COMPLETE_PART_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = FileCacheConstant.BASIC,key = "#fileId")
|
||||
public FileVO downLoad(Long fileId) {
|
||||
try {
|
||||
File file = getById(fileId);
|
||||
InputStream inputStream = fileStorageAdapter
|
||||
.downloadFile(file.getStoreFlag(), file.getBucketName(), file.getPathUrl());
|
||||
byte[] bytes = IOUtils.toByteArray(inputStream);
|
||||
String base64Image = EncodesUtil.encodeBase64(bytes);
|
||||
FileVO fileVO = BeanConv.toBean(file, FileVO.class);
|
||||
fileVO.setBase64Image(base64Image);
|
||||
return fileVO;
|
||||
} catch (Exception e) {
|
||||
log.error("文件下载传异常:{}", ExceptionsUtil.getStackTraceAsString(e));
|
||||
throw new ProjectException(FileEnum.DOWNLOAD_FAIL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.itheima.sfbx.file.utils;
|
||||
|
||||
import com.itheima.sfbx.file.handler.aliyun.properties.OssAliyunConfigProperties;
|
||||
import com.itheima.sfbx.file.handler.qiniu.properties.QiniuProperties;
|
||||
import com.itheima.sfbx.framework.commons.constant.file.FileConstant;
|
||||
import com.itheima.sfbx.framework.commons.enums.file.FileEnum;
|
||||
import com.itheima.sfbx.framework.commons.exception.ProjectException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 获得资源文件完整路径地址上下文对象
|
||||
* 如果添加了新的对象存储资源,需要在此类中对 initMap 方法添加新的路径前缀
|
||||
* </p>
|
||||
*
|
||||
* @Description:
|
||||
*/
|
||||
@Component
|
||||
public class FileUrlContext {
|
||||
|
||||
@Autowired
|
||||
private OssAliyunConfigProperties ossAliyunConfigProperties;
|
||||
|
||||
@Autowired
|
||||
private QiniuProperties qiniuProperties;
|
||||
|
||||
private static Map<String,String> fileStoreUrlHandler =new HashMap<>();
|
||||
|
||||
@PostConstruct
|
||||
public void initMap() {
|
||||
// 初始化各个对象存储的文件访问路径地址
|
||||
String ossPrefixUrl = "https://" + ossAliyunConfigProperties.getBucketName() + "." +
|
||||
ossAliyunConfigProperties.getEndpoint() + "/";
|
||||
String kodoPrefixUrl = "http://" + qiniuProperties.getKodo().getEndpoint() + "/";
|
||||
|
||||
fileStoreUrlHandler.put(FileConstant.ALIYUN_OSS,ossPrefixUrl);
|
||||
fileStoreUrlHandler.put(FileConstant.QINIU_KODO,kodoPrefixUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得资源文件的访问地址
|
||||
* @param storeFlag String 对象存储标识
|
||||
* @param pathUrl String 相对路径
|
||||
* @return String 资源文件对应的完整路径地址
|
||||
*/
|
||||
public String getFileUrl(String storeFlag, String pathUrl) {
|
||||
String prefix = fileStoreUrlHandler.get(storeFlag);
|
||||
if (StringUtils.isEmpty(prefix)) {
|
||||
throw new ProjectException(FileEnum.FILE_PREFIX_NOT_FOUND);
|
||||
}
|
||||
return prefix + pathUrl;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.itheima.sfbx.file.web;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||
import com.itheima.sfbx.file.service.IFileService;
|
||||
import com.itheima.sfbx.framework.commons.basic.ResponseResult;
|
||||
import com.itheima.sfbx.framework.commons.dto.file.FileVO;
|
||||
import com.itheima.sfbx.framework.commons.utils.ResponseResultBuild;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassName FileController.java
|
||||
* @Description 附件展示维护controller
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("file")
|
||||
@Api(tags = "附件controller")
|
||||
@Slf4j
|
||||
public class FileBusinessController {
|
||||
|
||||
@Autowired
|
||||
IFileService fileService;
|
||||
|
||||
/***
|
||||
* @description 附件分页列表
|
||||
* @param fileVO 查询条件
|
||||
* @param pageNum 页码
|
||||
* @param pageSize 每页条数
|
||||
* @return: Page<FileVO>
|
||||
*/
|
||||
@PostMapping("page/{pageNum}/{pageSize}")
|
||||
@ApiOperation(value = "查询附件分页",notes = "查询附件分页")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "fileVO",value = "附件查询对象",required = false,dataType = "FileVO"),
|
||||
@ApiImplicitParam(paramType = "path",name = "pageNum",value = "页码",example = "1",dataType = "Integer"),
|
||||
@ApiImplicitParam(paramType = "path",name = "pageSize",value = "每页条数",example = "10",dataType = "Integer")
|
||||
})
|
||||
@ApiOperationSupport(includeParameters ={"fileVO.businessType","","fileVO.pathUrl",
|
||||
"fileVO.dataState","fileVO.status"} )
|
||||
public ResponseResult<Page<FileVO>> findFileVOPage(
|
||||
@RequestBody FileVO fileVO,
|
||||
@PathVariable("pageNum") int pageNum,
|
||||
@PathVariable("pageSize") int pageSize) {
|
||||
//查询附件分页信息
|
||||
Page<FileVO> fileVOPage = fileService.findFileVOPage(fileVO, pageNum, pageSize);
|
||||
return ResponseResultBuild.successBuild(fileVOPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Description 移除业务原图片,并批量绑定新的图片到业务上
|
||||
* @param fileVOs 附件对象
|
||||
* @return
|
||||
*/
|
||||
@PutMapping(value = "replace-bind-batch-file")
|
||||
@ApiOperation(value = "移除业务原图片,并绑定新的图片到业务上",notes = "移除业务原图片,并绑定新的图片到业务上")
|
||||
@ApiImplicitParam(name = "fileVOs",value = "附件对象",required = true,dataType = "FileVO")
|
||||
public ResponseResult<Boolean> replaceBindBatchFile(@RequestBody List<FileVO> fileVOs){
|
||||
Boolean flag = fileService.replaceBindBatchFile(fileVOs);
|
||||
return ResponseResultBuild.successBuild(flag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.itheima.sfbx.file.web;
|
||||
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||
import com.itheima.sfbx.file.service.IFileService;
|
||||
import com.itheima.sfbx.framework.commons.basic.ResponseResult;
|
||||
import com.itheima.sfbx.framework.commons.dto.file.FileVO;
|
||||
import com.itheima.sfbx.framework.commons.dto.file.FilePartVO;
|
||||
import com.itheima.sfbx.framework.commons.dto.file.UploadMultipartFile;
|
||||
import com.itheima.sfbx.framework.commons.utils.ResponseResultBuild;
|
||||
import com.itheima.sfbx.framework.commons.utils.SubjectContent;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @ClassName FileUpLoadController.java
|
||||
* @Description 文件上传接口
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("file")
|
||||
@Api(tags = "附件controller")
|
||||
@Slf4j
|
||||
public class FileUpLoadController {
|
||||
|
||||
@Autowired
|
||||
IFileService fileService;
|
||||
|
||||
/***
|
||||
* @description 文件上传-简单上传-前端直接调用
|
||||
* @param file 上传对象
|
||||
* @return: com.itheima.travel.req.FileVO
|
||||
*/
|
||||
@PostMapping(value = "up-load")
|
||||
@ApiOperation(value = "文件上传-简单上传",notes = "文件上传-简单上传")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(paramType = "form", name = "file", value = "文件对象", required = true, dataTypeClass = MultipartFile.class)
|
||||
})
|
||||
@ApiOperationSupport(includeParameters = {"fileVO.businessType","fileVO.bucketName","fileVO.storeFlag","fileVO.autoCatalog"})
|
||||
public ResponseResult<FileVO> upLoad(
|
||||
@RequestParam("file") MultipartFile file,
|
||||
FileVO fileVO) throws IOException {
|
||||
fileVO.setCompanyNo(SubjectContent.getCompanyNo());
|
||||
//构建文件上传对象
|
||||
UploadMultipartFile uploadMultipartFile = UploadMultipartFile
|
||||
.builder()
|
||||
.originalFilename(file.getOriginalFilename())
|
||||
.fileByte(IOUtils.toByteArray(file.getInputStream()))
|
||||
.build();
|
||||
//执行文件上传
|
||||
FileVO fileVOResult = fileService.upLoad(uploadMultipartFile, fileVO);
|
||||
return ResponseResultBuild.successBuild(fileVOResult);
|
||||
|
||||
}
|
||||
|
||||
@PostMapping(value = "initiate-multipart-up-load")
|
||||
@ApiOperation(value = "文件分片上传-初始化",notes = "文件分片上传-初始化")
|
||||
@ApiImplicitParam(name = "fileVO",value = "文件对象",required = true,dataType = "FileVO")
|
||||
public ResponseResult<FileVO> initiateMultipartUpload(
|
||||
@RequestBody FileVO fileVO){
|
||||
fileVO.setCompanyNo(SubjectContent.getCompanyNo());
|
||||
//初始化上传Id
|
||||
FileVO fileVOResult = fileService.initiateMultipartUpload(fileVO);
|
||||
return ResponseResultBuild.successBuild(fileVOResult);
|
||||
}
|
||||
|
||||
@PostMapping(value = "up-load-part")
|
||||
@ApiOperation(value = "文件分片上传-上传分片",notes = "文件分片上传-上传分片")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(paramType = "form", name = "file", value = "文件对象", required = true, dataTypeClass = MultipartFile.class)
|
||||
})
|
||||
public ResponseResult<String> uploadPart(
|
||||
@RequestParam("file") MultipartFile file,
|
||||
FilePartVO filePartVO)throws IOException {
|
||||
filePartVO.setCompanyNo(SubjectContent.getCompanyNo());
|
||||
//构建文件上次对象
|
||||
UploadMultipartFile uploadMultipartFile = UploadMultipartFile
|
||||
.builder()
|
||||
.originalFilename(file.getOriginalFilename())
|
||||
.fileByte(IOUtils.toByteArray(file.getInputStream()))
|
||||
.build();
|
||||
//上传分片返回partETagJson
|
||||
String partETagJson = fileService.uploadPart(uploadMultipartFile,filePartVO);
|
||||
return ResponseResultBuild.successBuild(partETagJson);
|
||||
}
|
||||
|
||||
@PostMapping(value = "complete-multipart-up-load")
|
||||
@ApiOperation(value = "文件分片上传-合并分片",notes = "文件分片上传-合并分片")
|
||||
@ApiImplicitParam(name = "fileVO",value = "文件对象",required = true,dataType = "FileVO")
|
||||
public ResponseResult<String> completeMultipartUpload(
|
||||
@RequestBody FileVO fileVO)throws IOException {
|
||||
//问上传分片返回partETagJson
|
||||
String eTagJson = fileService.completeMultipartUpload(fileVO);
|
||||
return ResponseResultBuild.successBuild(eTagJson);
|
||||
}
|
||||
|
||||
}
|
||||
10
sfbx-file/file-web/src/main/resources/banner.txt
Normal file
10
sfbx-file/file-web/src/main/resources/banner.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
_ __ __
|
||||
(_) /__________ ______/ /_
|
||||
/ / __/ ___/ __ `/ ___/ __/
|
||||
/ / /_/ /__/ /_/ (__ ) /_
|
||||
/_/\__/\___/\__,_/____/\__/
|
||||
:: Spring Boot :: (v-2.7.10)
|
||||
:: Spring Cloud :: (v-2021.0.6)
|
||||
:: Spring Cloud Alibaba :: (v-2021.0.1.0)
|
||||
:: sfbx Cloud :: (v-2.0-SNAPSHOT)
|
||||
:: 献给可爱的传智人 ::
|
||||
54
sfbx-file/file-web/src/main/resources/bootstrap-test.yml
Normal file
54
sfbx-file/file-web/src/main/resources/bootstrap-test.yml
Normal file
@@ -0,0 +1,54 @@
|
||||
#服务配置
|
||||
server:
|
||||
#端口
|
||||
port: 7075
|
||||
#服务编码
|
||||
tomcat:
|
||||
uri-encoding: UTF-8
|
||||
spring:
|
||||
config:
|
||||
activate:
|
||||
on-profile:
|
||||
- test
|
||||
main:
|
||||
allow-circular-references: true
|
||||
allow-bean-definition-overriding: true
|
||||
mvc:
|
||||
pathmatch:
|
||||
matching-strategy: ant_path_matcher
|
||||
#应用配置
|
||||
application:
|
||||
#应用名称
|
||||
name: file-web
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: ${NACOS_ADDRESS:nacos-service.yjy-public-sfbx-java.svc.cluster.local:20015} # nacos注册中心
|
||||
group: SEATA_GROUP
|
||||
service: ${spring.application.name}
|
||||
username: ${NACOS_USERNAME:nacos}
|
||||
password: ${NACOS_PASSWORD:PKsf*bxQ4;yP3a+}
|
||||
config:
|
||||
server-addr: ${NACOS_ADDRESS:nacos-service.yjy-public-sfbx-java.svc.cluster.local:20015} # nacos注册中心
|
||||
group: SEATA_GROUP
|
||||
file-extension: yml
|
||||
shared-configs: # 共享配置
|
||||
- data-id: shared-spring-seata.yml #配置文件名-DataId
|
||||
group: SEATA_GROUP
|
||||
refresh: false
|
||||
- data-id: shared-redisson.yml #配置文件名-DataId
|
||||
group: SEATA_GROUP
|
||||
refresh: false
|
||||
- data-id: shared-mybatis-plus.yml #配置文件名-DataId
|
||||
group: SEATA_GROUP
|
||||
refresh: false
|
||||
- data-id: shared-stream-rabbit-basic.yml #配置文件名-DataId
|
||||
group: SEATA_GROUP
|
||||
refresh: false
|
||||
- data-id: shared-stream-rabbit-source-file.yml #配置文件名-DataId
|
||||
group: SEATA_GROUP
|
||||
refresh: false
|
||||
username: ${NACOS_USERNAME:nacos}
|
||||
password: ${NACOS_PASSWORD:PKsf*bxQ4;yP3a+}
|
||||
logging:
|
||||
config: classpath:logback.xml
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user