mirror of https://github.com/elunez/eladmin
Merge branch 'master' into deploy
# Conflicts: # eladmin-system/src/main/resources/config/application.ymldeploy
commit
07055d5173
|
@ -24,7 +24,6 @@ import me.zhengjie.exception.BadRequestException;
|
||||||
import org.apache.poi.util.IOUtils;
|
import org.apache.poi.util.IOUtils;
|
||||||
import org.apache.poi.xssf.streaming.SXSSFSheet;
|
import org.apache.poi.xssf.streaming.SXSSFSheet;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.servlet.ServletOutputStream;
|
import javax.servlet.ServletOutputStream;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
@ -136,15 +135,15 @@ public class FileUtil extends cn.hutool.core.io.FileUtil {
|
||||||
String resultSize;
|
String resultSize;
|
||||||
if (size / GB >= 1) {
|
if (size / GB >= 1) {
|
||||||
//如果当前Byte的值大于等于1GB
|
//如果当前Byte的值大于等于1GB
|
||||||
resultSize = DF.format(size / (float) GB) + "GB ";
|
resultSize = DF.format(size / (float) GB) + "GB";
|
||||||
} else if (size / MB >= 1) {
|
} else if (size / MB >= 1) {
|
||||||
//如果当前Byte的值大于等于1MB
|
//如果当前Byte的值大于等于1MB
|
||||||
resultSize = DF.format(size / (float) MB) + "MB ";
|
resultSize = DF.format(size / (float) MB) + "MB";
|
||||||
} else if (size / KB >= 1) {
|
} else if (size / KB >= 1) {
|
||||||
//如果当前Byte的值大于等于1KB
|
//如果当前Byte的值大于等于1KB
|
||||||
resultSize = DF.format(size / (float) KB) + "KB ";
|
resultSize = DF.format(size / (float) KB) + "KB";
|
||||||
} else {
|
} else {
|
||||||
resultSize = size + "B ";
|
resultSize = size + "B";
|
||||||
}
|
}
|
||||||
return resultSize;
|
return resultSize;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package me.zhengjie.utils;
|
package me.zhengjie.utils;
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -234,7 +235,10 @@ public class RedisUtils {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (clazz.isInstance(value)) {
|
// 如果 value 不是目标类型,则尝试将其反序列化为 clazz 类型
|
||||||
|
if (!clazz.isInstance(value)) {
|
||||||
|
return JSON.parseObject(value.toString(), clazz);
|
||||||
|
} else if (clazz.isInstance(value)) {
|
||||||
return clazz.cast(value);
|
return clazz.cast(value);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -18,6 +18,7 @@ package me.zhengjie.service.impl;
|
||||||
import cn.hutool.core.lang.Dict;
|
import cn.hutool.core.lang.Dict;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.alibaba.fastjson2.JSONArray;
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import me.zhengjie.domain.SysLog;
|
import me.zhengjie.domain.SysLog;
|
||||||
|
@ -136,7 +137,14 @@ public class SysLogServiceImpl implements SysLogService {
|
||||||
// 将RequestBody注解修饰的参数作为请求参数
|
// 将RequestBody注解修饰的参数作为请求参数
|
||||||
RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
|
RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
|
||||||
if (requestBody != null) {
|
if (requestBody != null) {
|
||||||
params.putAll((JSONObject) JSON.toJSON(args[i]));
|
// [el-async-1] ERROR o.s.a.i.SimpleAsyncUncaughtExceptionHandler - Unexpected exception occurred invoking async method: public void me.zhengjie.service.impl.SysLogServiceImpl.save(java.lang.String,java.lang.String,java.lang.String,org.aspectj.lang.ProceedingJoinPoint,me.zhengjie.domain.SysLog)
|
||||||
|
// java.lang.ClassCastException: com.alibaba.fastjson2.JSONArray cannot be cast to com.alibaba.fastjson2.JSONObject
|
||||||
|
Object json = JSON.toJSON(args[i]);
|
||||||
|
if (json instanceof JSONArray) {
|
||||||
|
params.put("reqBodyList", json);
|
||||||
|
} else {
|
||||||
|
params.putAll((JSONObject) json);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
String key = parameters[i].getName();
|
String key = parameters[i].getName();
|
||||||
params.put(key, args[i]);
|
params.put(key, args[i]);
|
||||||
|
|
|
@ -22,6 +22,7 @@ import me.zhengjie.utils.SpringBeanHolder;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.boot.context.ApplicationPidFileWriter;
|
import org.springframework.boot.context.ApplicationPidFileWriter;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
|
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
|
@ -50,10 +51,11 @@ public class AppRun {
|
||||||
// 监控应用的PID,启动时可指定PID路径:--spring.pid.file=/home/eladmin/app.pid
|
// 监控应用的PID,启动时可指定PID路径:--spring.pid.file=/home/eladmin/app.pid
|
||||||
// 或者在 application.yml 添加文件路径,方便 kill,kill `cat /home/eladmin/app.pid`
|
// 或者在 application.yml 添加文件路径,方便 kill,kill `cat /home/eladmin/app.pid`
|
||||||
springApplication.addListeners(new ApplicationPidFileWriter());
|
springApplication.addListeners(new ApplicationPidFileWriter());
|
||||||
springApplication.run(args);
|
ConfigurableApplicationContext context = springApplication.run(args);
|
||||||
|
String port = context.getEnvironment().getProperty("server.port");
|
||||||
log.info("---------------------------------------------");
|
log.info("---------------------------------------------");
|
||||||
log.info("Local: {}", "http://localhost:8000");
|
log.info("Local: http://localhost:{}", port);
|
||||||
log.info("Swagger: {}", "http://localhost:8000/doc.html");
|
log.info("Swagger: http://localhost:{}/doc.html", port);
|
||||||
log.info("---------------------------------------------");
|
log.info("---------------------------------------------");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ public class AppServiceImpl implements AppService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AppDto findById(Long id) {
|
public AppDto findById(Long id) {
|
||||||
App app = appRepository.findById(id).orElseGet(App::new);
|
App app = appRepository.findById(id).orElseGet(App::new);
|
||||||
ValidationUtil.isNull(app.getId(),"App","id",id);
|
ValidationUtil.isNull(app.getId(),"App","id",id);
|
||||||
return appMapper.toDto(app);
|
return appMapper.toDto(app);
|
||||||
}
|
}
|
||||||
|
@ -64,6 +64,11 @@ public class AppServiceImpl implements AppService {
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void create(App resources) {
|
public void create(App resources) {
|
||||||
|
// 验证应用名称是否存在恶意攻击payload,https://github.com/elunez/eladmin/issues/873
|
||||||
|
String appName = resources.getName();
|
||||||
|
if (appName.contains(";") || appName.contains("|") || appName.contains("&")) {
|
||||||
|
throw new IllegalArgumentException("非法的应用名称,请勿包含[; | &]等特殊字符");
|
||||||
|
}
|
||||||
verification(resources);
|
verification(resources);
|
||||||
appRepository.save(resources);
|
appRepository.save(resources);
|
||||||
}
|
}
|
||||||
|
@ -71,6 +76,11 @@ public class AppServiceImpl implements AppService {
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void update(App resources) {
|
public void update(App resources) {
|
||||||
|
// 验证应用名称是否存在恶意攻击payload,https://github.com/elunez/eladmin/issues/873
|
||||||
|
String appName = resources.getName();
|
||||||
|
if (appName.contains(";") || appName.contains("|") || appName.contains("&")) {
|
||||||
|
throw new IllegalArgumentException("非法的应用名称,请勿包含[; | &]等特殊字符");
|
||||||
|
}
|
||||||
verification(resources);
|
verification(resources);
|
||||||
App app = appRepository.findById(resources.getId()).orElseGet(App::new);
|
App app = appRepository.findById(resources.getId()).orElseGet(App::new);
|
||||||
ValidationUtil.isNull(app.getId(),"App","id",resources.getId());
|
ValidationUtil.isNull(app.getId(),"App","id",resources.getId());
|
||||||
|
|
|
@ -263,9 +263,13 @@ public class DeployServiceImpl implements DeployService {
|
||||||
return "执行完毕";
|
return "执行完毕";
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkFile(ExecuteShellUtil executeShellUtil, AppDto appDTO) {
|
private boolean checkFile(ExecuteShellUtil executeShellUtil, AppDto app) {
|
||||||
String result = executeShellUtil.executeForResult("find " + appDTO.getDeployPath() + " -name " + appDTO.getName());
|
String deployPath = app.getDeployPath();
|
||||||
return result.indexOf(appDTO.getName())>0;
|
String appName = app.getName();
|
||||||
|
// 使用安全的命令执行方式,避免直接拼接字符串,https://github.com/elunez/eladmin/issues/873
|
||||||
|
String[] command = {"find", deployPath, "-name", appName};
|
||||||
|
String result = executeShellUtil.executeForResult(Arrays.toString(command));
|
||||||
|
return result.contains(appName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -42,8 +42,8 @@ import java.util.concurrent.TimeUnit;
|
||||||
@Component
|
@Component
|
||||||
public class TokenProvider implements InitializingBean {
|
public class TokenProvider implements InitializingBean {
|
||||||
|
|
||||||
|
private Key signingKey;
|
||||||
private JwtParser jwtParser;
|
private JwtParser jwtParser;
|
||||||
private JwtBuilder jwtBuilder;
|
|
||||||
private final RedisUtils redisUtils;
|
private final RedisUtils redisUtils;
|
||||||
private final SecurityProperties properties;
|
private final SecurityProperties properties;
|
||||||
public static final String AUTHORITIES_UUID_KEY = "uid";
|
public static final String AUTHORITIES_UUID_KEY = "uid";
|
||||||
|
@ -56,13 +56,13 @@ public class TokenProvider implements InitializingBean {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterPropertiesSet() {
|
public void afterPropertiesSet() {
|
||||||
|
// 解码Base64密钥并创建签名密钥
|
||||||
byte[] keyBytes = Decoders.BASE64.decode(properties.getBase64Secret());
|
byte[] keyBytes = Decoders.BASE64.decode(properties.getBase64Secret());
|
||||||
Key key = Keys.hmacShaKeyFor(keyBytes);
|
this.signingKey = Keys.hmacShaKeyFor(keyBytes);
|
||||||
|
// 初始化 JwtParser
|
||||||
jwtParser = Jwts.parserBuilder()
|
jwtParser = Jwts.parserBuilder()
|
||||||
.setSigningKey(key)
|
.setSigningKey(signingKey) // 使用预生成的签名密钥
|
||||||
.build();
|
.build();
|
||||||
jwtBuilder = Jwts.builder()
|
|
||||||
.signWith(key, SignatureAlgorithm.HS512);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -79,9 +79,14 @@ public class TokenProvider implements InitializingBean {
|
||||||
claims.put(AUTHORITIES_UID_KEY, user.getUser().getId());
|
claims.put(AUTHORITIES_UID_KEY, user.getUser().getId());
|
||||||
// 设置UUID,确保每次Token不一样
|
// 设置UUID,确保每次Token不一样
|
||||||
claims.put(AUTHORITIES_UUID_KEY, IdUtil.simpleUUID());
|
claims.put(AUTHORITIES_UUID_KEY, IdUtil.simpleUUID());
|
||||||
return jwtBuilder
|
// 直接调用 Jwts.builder() 创建新实例
|
||||||
|
return Jwts.builder()
|
||||||
|
// 设置自定义 Claims
|
||||||
.setClaims(claims)
|
.setClaims(claims)
|
||||||
|
// 设置主题
|
||||||
.setSubject(user.getUsername())
|
.setSubject(user.getUsername())
|
||||||
|
// 使用预生成的签名密钥和算法签名
|
||||||
|
.signWith(signingKey, SignatureAlgorithm.HS512)
|
||||||
.compact();
|
.compact();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -116,3 +116,22 @@ file:
|
||||||
# 文件大小 /M
|
# 文件大小 /M
|
||||||
maxSize: 1
|
maxSize: 1
|
||||||
avatarMaxSize: 5
|
avatarMaxSize: 5
|
||||||
|
|
||||||
|
# 亚马逊S3协议云存储配置
|
||||||
|
#支持七牛云,阿里云OSS,腾讯云COS,华为云OBS,移动云EOS等
|
||||||
|
amz:
|
||||||
|
s3:
|
||||||
|
# 地域
|
||||||
|
region: test
|
||||||
|
# 地域对应的 endpoint
|
||||||
|
endPoint: https://s3.test.com
|
||||||
|
# 访问的域名
|
||||||
|
domain: https://s3.test.com
|
||||||
|
# 账号的认证信息,或者子账号的认证信息
|
||||||
|
accessKey: 填写你的AccessKey
|
||||||
|
secretKey: 填写你的SecretKey
|
||||||
|
# 存储桶(Bucket)
|
||||||
|
defaultBucket: 填写你的存储桶名称
|
||||||
|
# 文件存储路径
|
||||||
|
timeformat: yyyy-MM
|
||||||
|
|
||||||
|
|
|
@ -127,3 +127,21 @@ file:
|
||||||
# 文件大小 /M
|
# 文件大小 /M
|
||||||
maxSize: 1
|
maxSize: 1
|
||||||
avatarMaxSize: 5
|
avatarMaxSize: 5
|
||||||
|
|
||||||
|
# 亚马逊S3协议云存储配置
|
||||||
|
#支持七牛云,阿里云OSS,腾讯云COS,华为云OBS,移动云EOS等
|
||||||
|
amz:
|
||||||
|
s3:
|
||||||
|
# 地域
|
||||||
|
region: test
|
||||||
|
# 地域对应的 endpoint
|
||||||
|
endPoint: https://s3.test.com
|
||||||
|
# 访问的域名
|
||||||
|
domain: https://s3.test.com
|
||||||
|
# 账号的认证信息,或者子账号的认证信息
|
||||||
|
accessKey: 填写你的AccessKey
|
||||||
|
secretKey: 填写你的SecretKey
|
||||||
|
# 存储桶(Bucket)
|
||||||
|
defaultBucket: 填写你的存储桶名称
|
||||||
|
# 文件存储路径
|
||||||
|
timeformat: yyyy-MM
|
|
@ -65,11 +65,6 @@ task:
|
||||||
# 队列容量
|
# 队列容量
|
||||||
queue-capacity: 30
|
queue-capacity: 30
|
||||||
|
|
||||||
#七牛云
|
|
||||||
qiniu:
|
|
||||||
# 文件大小 /M
|
|
||||||
max-size: 1
|
|
||||||
|
|
||||||
#邮箱验证码有效时间/秒
|
#邮箱验证码有效时间/秒
|
||||||
code:
|
code:
|
||||||
expiration: 300
|
expiration: 300
|
||||||
|
|
|
@ -44,5 +44,13 @@
|
||||||
<artifactId>alipay-sdk-java</artifactId>
|
<artifactId>alipay-sdk-java</artifactId>
|
||||||
<version>${alipay.version}</version>
|
<version>${alipay.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!--amazon s3 依赖-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>software.amazon.awssdk</groupId>
|
||||||
|
<artifactId>s3</artifactId>
|
||||||
|
<version>2.30.13</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
package me.zhengjie.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
|
||||||
|
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
|
||||||
|
import software.amazon.awssdk.regions.Region;
|
||||||
|
import software.amazon.awssdk.services.s3.S3Client;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Zheng Jie
|
||||||
|
* @date 2025-06-25
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "amz.s3")
|
||||||
|
public class AmzS3Config {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amazon S3 的区域配置,例如 "us-west-2"。
|
||||||
|
* 该区域决定了 S3 存储桶的地理位置。
|
||||||
|
*/
|
||||||
|
private String region;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amazon S3 的端点 URL
|
||||||
|
* 该端点用于访问 S3 服务。
|
||||||
|
*/
|
||||||
|
private String endPoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amazon S3 的域名
|
||||||
|
* 该域名用于构建访问 S3 服务的完整 URL。
|
||||||
|
*/
|
||||||
|
private String domain;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amazon S3 的访问密钥 ID,用于身份验证。
|
||||||
|
* 该密钥与 secretKey 一起使用来授权对 S3 服务的访问。
|
||||||
|
*/
|
||||||
|
private String accessKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amazon S3 的秘密访问密钥,用于身份验证。
|
||||||
|
* 该密钥与 accessKey 一起使用来授权对 S3 服务的访问。
|
||||||
|
*/
|
||||||
|
private String secretKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认的 S3 存储桶名称。
|
||||||
|
* 该存储桶用于存储上传的文件和数据。
|
||||||
|
*/
|
||||||
|
private String defaultBucket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传后存储的文件夹格式,默认为 "yyyy-MM"。
|
||||||
|
*/
|
||||||
|
private String timeformat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建并返回一个 AmazonS3 客户端实例。
|
||||||
|
* 使用当前配置类的 endPoint, region, accessKey 和 secretKey。
|
||||||
|
* 声明为 @Bean 后,Spring 会将其作为单例管理,并在需要时自动注入。
|
||||||
|
*
|
||||||
|
* @return 配置好的 AmazonS3 客户端实例
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public S3Client amazonS3Client() {
|
||||||
|
return S3Client.builder().region(Region.of(region))
|
||||||
|
.endpointOverride(URI.create(endPoint))
|
||||||
|
.credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(accessKey, secretKey)))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,69 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2019-2025 Zheng Jie
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package me.zhengjie.domain;
|
|
||||||
|
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
|
||||||
import lombok.Data;
|
|
||||||
import javax.persistence.*;
|
|
||||||
import javax.validation.constraints.NotBlank;
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 七牛云对象存储配置类
|
|
||||||
* @author Zheng Jie
|
|
||||||
* @date 2018-12-31
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@Entity
|
|
||||||
@Table(name = "tool_qiniu_config")
|
|
||||||
public class QiniuConfig implements Serializable {
|
|
||||||
|
|
||||||
@Id
|
|
||||||
@Column(name = "config_id")
|
|
||||||
@ApiModelProperty(value = "ID")
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
@NotBlank
|
|
||||||
@ApiModelProperty(value = "accessKey")
|
|
||||||
private String accessKey;
|
|
||||||
|
|
||||||
@NotBlank
|
|
||||||
@ApiModelProperty(value = "secretKey")
|
|
||||||
private String secretKey;
|
|
||||||
|
|
||||||
@NotBlank
|
|
||||||
@ApiModelProperty(value = "存储空间名称作为唯一的 Bucket 识别符")
|
|
||||||
private String bucket;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Zone表示与机房的对应关系
|
|
||||||
* 华东 Zone.zone0()
|
|
||||||
* 华北 Zone.zone1()
|
|
||||||
* 华南 Zone.zone2()
|
|
||||||
* 北美 Zone.zoneNa0()
|
|
||||||
* 东南亚 Zone.zoneAs0()
|
|
||||||
*/
|
|
||||||
@NotBlank
|
|
||||||
@ApiModelProperty(value = "Zone表示与机房的对应关系")
|
|
||||||
private String zone;
|
|
||||||
|
|
||||||
@NotBlank
|
|
||||||
@ApiModelProperty(value = "外链域名,可自定义,需在七牛云绑定")
|
|
||||||
private String host;
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "空间类型:公开/私有")
|
|
||||||
private String type = "公开";
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2019-2025 Zheng Jie
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package me.zhengjie.domain;
|
|
||||||
|
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
|
||||||
import lombok.Data;
|
|
||||||
import org.hibernate.annotations.UpdateTimestamp;
|
|
||||||
import javax.persistence.*;
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.sql.Timestamp;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 上传成功后,存储结果
|
|
||||||
* @author Zheng Jie
|
|
||||||
* @date 2018-12-31
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@Entity
|
|
||||||
@Table(name = "tool_qiniu_content")
|
|
||||||
public class QiniuContent implements Serializable {
|
|
||||||
|
|
||||||
@Id
|
|
||||||
@Column(name = "content_id")
|
|
||||||
@ApiModelProperty(value = "ID", hidden = true)
|
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
@Column(name = "name")
|
|
||||||
@ApiModelProperty(value = "文件名")
|
|
||||||
private String key;
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "空间名")
|
|
||||||
private String bucket;
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "大小")
|
|
||||||
private String size;
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "文件地址")
|
|
||||||
private String url;
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "文件类型")
|
|
||||||
private String suffix;
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "空间类型:公开/私有")
|
|
||||||
private String type = "公开";
|
|
||||||
|
|
||||||
@UpdateTimestamp
|
|
||||||
@ApiModelProperty(value = "创建或更新时间")
|
|
||||||
@Column(name = "update_time")
|
|
||||||
private Timestamp updateTime;
|
|
||||||
}
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019-2025 Zheng Jie
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package me.zhengjie.domain;
|
||||||
|
|
||||||
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
|
import cn.hutool.core.bean.copier.CopyOptions;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import me.zhengjie.base.BaseEntity;
|
||||||
|
import javax.persistence.*;
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description S3存储实体类
|
||||||
|
* @author Zheng Jie
|
||||||
|
* @date 2025-06-25
|
||||||
|
**/
|
||||||
|
@Data
|
||||||
|
@Entity
|
||||||
|
@Table(name = "tool_s3_storage")
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class S3Storage extends BaseEntity implements Serializable {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "storage_id")
|
||||||
|
@ApiModelProperty(value = "ID", hidden = true)
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
@ApiModelProperty(value = "文件名称")
|
||||||
|
private String fileName;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
@ApiModelProperty(value = "真实存储的名称")
|
||||||
|
private String fileRealName;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
@ApiModelProperty(value = "文件大小")
|
||||||
|
private String fileSize;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
@ApiModelProperty(value = "文件MIME 类型")
|
||||||
|
private String fileMimeType;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
@ApiModelProperty(value = "文件类型")
|
||||||
|
private String fileType;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
@ApiModelProperty(value = "文件路径")
|
||||||
|
private String filePath;
|
||||||
|
|
||||||
|
public void copy(S3Storage source){
|
||||||
|
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2019-2025 Zheng Jie
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package me.zhengjie.repository;
|
|
||||||
|
|
||||||
import me.zhengjie.domain.QiniuConfig;
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
|
||||||
import org.springframework.data.jpa.repository.Modifying;
|
|
||||||
import org.springframework.data.jpa.repository.Query;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Zheng Jie
|
|
||||||
* @date 2018-12-31
|
|
||||||
*/
|
|
||||||
public interface QiNiuConfigRepository extends JpaRepository<QiniuConfig,Long> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 编辑类型
|
|
||||||
* @param type /
|
|
||||||
*/
|
|
||||||
@Modifying
|
|
||||||
@Query(value = "update QiniuConfig set type = ?1")
|
|
||||||
void update(String type);
|
|
||||||
}
|
|
|
@ -15,20 +15,22 @@
|
||||||
*/
|
*/
|
||||||
package me.zhengjie.repository;
|
package me.zhengjie.repository;
|
||||||
|
|
||||||
import me.zhengjie.domain.QiniuContent;
|
import me.zhengjie.domain.S3Storage;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Zheng Jie
|
* @author Zheng Jie
|
||||||
* @date 2018-12-31
|
* @date 2025-06-25
|
||||||
*/
|
*/
|
||||||
public interface QiniuContentRepository extends JpaRepository<QiniuContent,Long>, JpaSpecificationExecutor<QiniuContent> {
|
public interface S3StorageRepository extends JpaRepository<S3Storage, Long>, JpaSpecificationExecutor<S3Storage> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据key查询
|
* 根据ID查询文件路径
|
||||||
* @param key 文件名
|
* @param id 文件ID
|
||||||
* @return QiniuContent
|
* @return 文件路径
|
||||||
*/
|
*/
|
||||||
QiniuContent findByKey(String key);
|
@Query(value = "SELECT file_path FROM s3_storage WHERE id = ?1", nativeQuery = true)
|
||||||
}
|
String selectFilePathById(Long id);
|
||||||
|
}
|
|
@ -1,122 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2019-2025 Zheng Jie
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package me.zhengjie.rest;
|
|
||||||
|
|
||||||
import io.swagger.annotations.Api;
|
|
||||||
import io.swagger.annotations.ApiOperation;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import me.zhengjie.annotation.Log;
|
|
||||||
import me.zhengjie.domain.QiniuConfig;
|
|
||||||
import me.zhengjie.domain.QiniuContent;
|
|
||||||
import me.zhengjie.service.dto.QiniuQueryCriteria;
|
|
||||||
import me.zhengjie.service.QiNiuService;
|
|
||||||
import me.zhengjie.utils.PageResult;
|
|
||||||
import org.springframework.data.domain.Pageable;
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.validation.annotation.Validated;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送邮件
|
|
||||||
* @author 郑杰
|
|
||||||
* @date 2018/09/28 6:55:53
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@RestController
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@RequestMapping("/api/qiNiuContent")
|
|
||||||
@Api(tags = "工具:七牛云存储管理")
|
|
||||||
public class QiniuController {
|
|
||||||
|
|
||||||
private final QiNiuService qiNiuService;
|
|
||||||
|
|
||||||
@GetMapping(value = "/config")
|
|
||||||
public ResponseEntity<QiniuConfig> queryQiNiuConfig(){
|
|
||||||
return new ResponseEntity<>(qiNiuService.find(), HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Log("配置七牛云存储")
|
|
||||||
@ApiOperation("配置七牛云存储")
|
|
||||||
@PutMapping(value = "/config")
|
|
||||||
public ResponseEntity<Object> updateQiNiuConfig(@Validated @RequestBody QiniuConfig qiniuConfig){
|
|
||||||
qiNiuService.config(qiniuConfig);
|
|
||||||
qiNiuService.update(qiniuConfig.getType());
|
|
||||||
return new ResponseEntity<>(HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOperation("导出数据")
|
|
||||||
@GetMapping(value = "/download")
|
|
||||||
public void exportQiNiu(HttpServletResponse response, QiniuQueryCriteria criteria) throws IOException {
|
|
||||||
qiNiuService.downloadList(qiNiuService.queryAll(criteria), response);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOperation("查询文件")
|
|
||||||
@GetMapping
|
|
||||||
public ResponseEntity<PageResult<QiniuContent>> queryQiNiu(QiniuQueryCriteria criteria, Pageable pageable){
|
|
||||||
return new ResponseEntity<>(qiNiuService.queryAll(criteria,pageable),HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOperation("上传文件")
|
|
||||||
@PostMapping
|
|
||||||
public ResponseEntity<Object> uploadQiNiu(@RequestParam MultipartFile file){
|
|
||||||
QiniuContent qiniuContent = qiNiuService.upload(file,qiNiuService.find());
|
|
||||||
Map<String,Object> map = new HashMap<>(3);
|
|
||||||
map.put("id",qiniuContent.getId());
|
|
||||||
map.put("errno",0);
|
|
||||||
map.put("data",new String[]{qiniuContent.getUrl()});
|
|
||||||
return new ResponseEntity<>(map,HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Log("同步七牛云数据")
|
|
||||||
@ApiOperation("同步七牛云数据")
|
|
||||||
@PostMapping(value = "/synchronize")
|
|
||||||
public ResponseEntity<Object> synchronizeQiNiu(){
|
|
||||||
qiNiuService.synchronize(qiNiuService.find());
|
|
||||||
return new ResponseEntity<>(HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Log("下载文件")
|
|
||||||
@ApiOperation("下载文件")
|
|
||||||
@GetMapping(value = "/download/{id}")
|
|
||||||
public ResponseEntity<Object> downloadQiNiu(@PathVariable Long id){
|
|
||||||
Map<String,Object> map = new HashMap<>(1);
|
|
||||||
map.put("url", qiNiuService.download(qiNiuService.findByContentId(id),qiNiuService.find()));
|
|
||||||
return new ResponseEntity<>(map,HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Log("删除文件")
|
|
||||||
@ApiOperation("删除文件")
|
|
||||||
@DeleteMapping(value = "/{id}")
|
|
||||||
public ResponseEntity<Object> deleteQiNiu(@PathVariable Long id){
|
|
||||||
qiNiuService.delete(qiNiuService.findByContentId(id),qiNiuService.find());
|
|
||||||
return new ResponseEntity<>(HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Log("删除多张图片")
|
|
||||||
@ApiOperation("删除多张图片")
|
|
||||||
@DeleteMapping
|
|
||||||
public ResponseEntity<Object> deleteAllQiNiu(@RequestBody Long[] ids) {
|
|
||||||
qiNiuService.deleteAll(ids, qiNiuService.find());
|
|
||||||
return new ResponseEntity<>(HttpStatus.OK);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019-2025 Zheng Jie
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package me.zhengjie.rest;
|
||||||
|
|
||||||
|
import io.swagger.annotations.Api;
|
||||||
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import me.zhengjie.annotation.Log;
|
||||||
|
import me.zhengjie.config.AmzS3Config;
|
||||||
|
import me.zhengjie.domain.S3Storage;
|
||||||
|
import me.zhengjie.service.S3StorageService;
|
||||||
|
import me.zhengjie.service.dto.S3StorageQueryCriteria;
|
||||||
|
import me.zhengjie.utils.PageResult;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* amz S3 协议云存储管理
|
||||||
|
* @author 郑杰
|
||||||
|
* @date 2025-06-25
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequestMapping("/api/s3Storage")
|
||||||
|
@Api(tags = "工具:S3协议云存储管理")
|
||||||
|
public class S3StorageController {
|
||||||
|
|
||||||
|
private final AmzS3Config amzS3Config;
|
||||||
|
private final S3StorageService s3StorageService;
|
||||||
|
|
||||||
|
@ApiOperation("导出数据")
|
||||||
|
@GetMapping(value = "/download")
|
||||||
|
@PreAuthorize("@el.check('storage:list')")
|
||||||
|
public void exportS3Storage(HttpServletResponse response, S3StorageQueryCriteria criteria) throws IOException {
|
||||||
|
s3StorageService.download(s3StorageService.queryAll(criteria), response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@ApiOperation("查询文件")
|
||||||
|
@PreAuthorize("@el.check('storage:list')")
|
||||||
|
public ResponseEntity<PageResult<S3Storage>> queryS3Storage(S3StorageQueryCriteria criteria, Pageable pageable){
|
||||||
|
return new ResponseEntity<>(s3StorageService.queryAll(criteria, pageable),HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@ApiOperation("上传文件")
|
||||||
|
public ResponseEntity<Object> uploadS3Storage(@RequestParam MultipartFile file){
|
||||||
|
S3Storage storage = s3StorageService.upload(file);
|
||||||
|
Map<String,Object> map = new HashMap<>(3);
|
||||||
|
map.put("id",storage.getId());
|
||||||
|
map.put("errno",0);
|
||||||
|
map.put("data",new String[]{amzS3Config.getDomain() + "/" + storage.getFilePath()});
|
||||||
|
return new ResponseEntity<>(map,HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Log("下载文件")
|
||||||
|
@ApiOperation("下载文件")
|
||||||
|
@GetMapping(value = "/download/{id}")
|
||||||
|
public ResponseEntity<Object> downloadS3Storage(@PathVariable Long id){
|
||||||
|
Map<String,Object> map = new HashMap<>(1);
|
||||||
|
S3Storage storage = s3StorageService.getById(id);
|
||||||
|
if (storage == null) {
|
||||||
|
map.put("message", "文件不存在或已被删除");
|
||||||
|
return new ResponseEntity<>(map, HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
// 仅适合公开文件访问,私有文件可以使用服务中的 privateDownload 方法
|
||||||
|
String url = amzS3Config.getDomain() + "/" + storage.getFilePath();
|
||||||
|
map.put("url", url);
|
||||||
|
return new ResponseEntity<>(map,HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Log("删除多个文件")
|
||||||
|
@DeleteMapping
|
||||||
|
@ApiOperation("删除多个文件")
|
||||||
|
@PreAuthorize("@el.check('storage:del')")
|
||||||
|
public ResponseEntity<Object> deleteAllS3Storage(@RequestBody List<Long> ids) {
|
||||||
|
s3StorageService.deleteAll(ids);
|
||||||
|
return new ResponseEntity<>(HttpStatus.OK);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,119 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2019-2025 Zheng Jie
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package me.zhengjie.service;
|
|
||||||
|
|
||||||
import me.zhengjie.domain.QiniuConfig;
|
|
||||||
import me.zhengjie.domain.QiniuContent;
|
|
||||||
import me.zhengjie.service.dto.QiniuQueryCriteria;
|
|
||||||
import me.zhengjie.utils.PageResult;
|
|
||||||
import org.springframework.data.domain.Pageable;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Zheng Jie
|
|
||||||
* @date 2018-12-31
|
|
||||||
*/
|
|
||||||
public interface QiNiuService {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查配置
|
|
||||||
* @return QiniuConfig
|
|
||||||
*/
|
|
||||||
QiniuConfig find();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 修改配置
|
|
||||||
* @param qiniuConfig 配置
|
|
||||||
* @return QiniuConfig
|
|
||||||
*/
|
|
||||||
QiniuConfig config(QiniuConfig qiniuConfig);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 分页查询
|
|
||||||
* @param criteria 条件
|
|
||||||
* @param pageable 分页参数
|
|
||||||
* @return /
|
|
||||||
*/
|
|
||||||
PageResult<QiniuContent> queryAll(QiniuQueryCriteria criteria, Pageable pageable);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询全部
|
|
||||||
* @param criteria 条件
|
|
||||||
* @return /
|
|
||||||
*/
|
|
||||||
List<QiniuContent> queryAll(QiniuQueryCriteria criteria);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 上传文件
|
|
||||||
* @param file 文件
|
|
||||||
* @param qiniuConfig 配置
|
|
||||||
* @return QiniuContent
|
|
||||||
*/
|
|
||||||
QiniuContent upload(MultipartFile file, QiniuConfig qiniuConfig);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询文件
|
|
||||||
* @param id 文件ID
|
|
||||||
* @return QiniuContent
|
|
||||||
*/
|
|
||||||
QiniuContent findByContentId(Long id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 下载文件
|
|
||||||
* @param content 文件信息
|
|
||||||
* @param config 配置
|
|
||||||
* @return String
|
|
||||||
*/
|
|
||||||
String download(QiniuContent content, QiniuConfig config);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除文件
|
|
||||||
* @param content 文件
|
|
||||||
* @param config 配置
|
|
||||||
*/
|
|
||||||
void delete(QiniuContent content, QiniuConfig config);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 同步数据
|
|
||||||
* @param config 配置
|
|
||||||
*/
|
|
||||||
void synchronize(QiniuConfig config);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除文件
|
|
||||||
* @param ids 文件ID数组
|
|
||||||
* @param config 配置
|
|
||||||
*/
|
|
||||||
void deleteAll(Long[] ids, QiniuConfig config);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新数据
|
|
||||||
* @param type 类型
|
|
||||||
*/
|
|
||||||
void update(String type);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 导出数据
|
|
||||||
* @param queryAll /
|
|
||||||
* @param response /
|
|
||||||
* @throws IOException /
|
|
||||||
*/
|
|
||||||
void downloadList(List<QiniuContent> queryAll, HttpServletResponse response) throws IOException;
|
|
||||||
}
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019-2025 Zheng Jie
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package me.zhengjie.service;
|
||||||
|
|
||||||
|
import me.zhengjie.domain.S3Storage;
|
||||||
|
import me.zhengjie.service.dto.S3StorageQueryCriteria;
|
||||||
|
import me.zhengjie.utils.PageResult;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 服务接口
|
||||||
|
* @author Zheng Jie
|
||||||
|
* @date 2025-06-25
|
||||||
|
**/
|
||||||
|
public interface S3StorageService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询数据分页
|
||||||
|
* @param criteria 条件
|
||||||
|
* @param pageable 分页参数
|
||||||
|
* @return PageResult
|
||||||
|
*/
|
||||||
|
PageResult<S3Storage> queryAll(S3StorageQueryCriteria criteria, Pageable pageable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询所有数据不分页
|
||||||
|
* @param criteria 条件参数
|
||||||
|
* @return List<S3StorageDto>
|
||||||
|
*/
|
||||||
|
List<S3Storage> queryAll(S3StorageQueryCriteria criteria);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 多选删除
|
||||||
|
* @param ids /
|
||||||
|
*/
|
||||||
|
void deleteAll(List<Long> ids);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出数据
|
||||||
|
* @param all 待导出的数据
|
||||||
|
* @param response /
|
||||||
|
* @throws IOException /
|
||||||
|
*/
|
||||||
|
void download(List<S3Storage> all, HttpServletResponse response) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 私有化下载,仅供参考,还有许多方式
|
||||||
|
* @param id 文件ID
|
||||||
|
*/
|
||||||
|
Map<String, String> privateDownload(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传文件
|
||||||
|
* @param file 上传的文件
|
||||||
|
* @return S3Storage 对象,包含文件存储信息
|
||||||
|
*/
|
||||||
|
S3Storage upload(MultipartFile file);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID获取文件信息
|
||||||
|
* @param id 文件ID
|
||||||
|
* @return S3Storage 对象,包含文件存储信息
|
||||||
|
*/
|
||||||
|
S3Storage getById(Long id);
|
||||||
|
}
|
|
@ -1,39 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2019-2025 Zheng Jie
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package me.zhengjie.service.dto;
|
|
||||||
|
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
|
||||||
import lombok.Data;
|
|
||||||
import me.zhengjie.annotation.Query;
|
|
||||||
|
|
||||||
import java.sql.Timestamp;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Zheng Jie
|
|
||||||
* @date 2019-6-4 09:54:37
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class QiniuQueryCriteria{
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "名称查询")
|
|
||||||
@Query(type = Query.Type.INNER_LIKE)
|
|
||||||
private String key;
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "创建时间")
|
|
||||||
@Query(type = Query.Type.BETWEEN)
|
|
||||||
private List<Timestamp> createTime;
|
|
||||||
}
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019-2025 Zheng Jie
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package me.zhengjie.service.dto;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import me.zhengjie.annotation.Query;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Zheng Jie
|
||||||
|
* @date 2025-06-25
|
||||||
|
**/
|
||||||
|
@Data
|
||||||
|
public class S3StorageQueryCriteria {
|
||||||
|
|
||||||
|
@Query(type = Query.Type.INNER_LIKE)
|
||||||
|
@ApiModelProperty(value = "文件名称")
|
||||||
|
private String fileName;
|
||||||
|
|
||||||
|
@Query(type = Query.Type.BETWEEN)
|
||||||
|
@ApiModelProperty(value = "创建时间")
|
||||||
|
private List<Timestamp> createTime;
|
||||||
|
|
||||||
|
}
|
|
@ -1,234 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2019-2025 Zheng Jie
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package me.zhengjie.service.impl;
|
|
||||||
|
|
||||||
import com.alibaba.fastjson2.JSON;
|
|
||||||
import com.qiniu.common.QiniuException;
|
|
||||||
import com.qiniu.http.Response;
|
|
||||||
import com.qiniu.storage.BucketManager;
|
|
||||||
import com.qiniu.storage.Configuration;
|
|
||||||
import com.qiniu.storage.UploadManager;
|
|
||||||
import com.qiniu.storage.model.DefaultPutRet;
|
|
||||||
import com.qiniu.storage.model.FileInfo;
|
|
||||||
import com.qiniu.util.Auth;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import me.zhengjie.domain.QiniuConfig;
|
|
||||||
import me.zhengjie.domain.QiniuContent;
|
|
||||||
import me.zhengjie.repository.QiniuContentRepository;
|
|
||||||
import me.zhengjie.service.dto.QiniuQueryCriteria;
|
|
||||||
import me.zhengjie.utils.*;
|
|
||||||
import me.zhengjie.exception.BadRequestException;
|
|
||||||
import me.zhengjie.repository.QiNiuConfigRepository;
|
|
||||||
import me.zhengjie.service.QiNiuService;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.cache.annotation.CacheConfig;
|
|
||||||
import org.springframework.cache.annotation.CachePut;
|
|
||||||
import org.springframework.cache.annotation.Cacheable;
|
|
||||||
import org.springframework.data.domain.Pageable;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Zheng Jie
|
|
||||||
* @date 2018-12-31
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@CacheConfig(cacheNames = "qiNiu")
|
|
||||||
public class QiNiuServiceImpl implements QiNiuService {
|
|
||||||
|
|
||||||
private final QiNiuConfigRepository qiNiuConfigRepository;
|
|
||||||
private final QiniuContentRepository qiniuContentRepository;
|
|
||||||
|
|
||||||
@Value("${qiniu.max-size}")
|
|
||||||
private Long maxSize;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Cacheable(key = "'config'")
|
|
||||||
public QiniuConfig find() {
|
|
||||||
Optional<QiniuConfig> qiniuConfig = qiNiuConfigRepository.findById(1L);
|
|
||||||
return qiniuConfig.orElseGet(QiniuConfig::new);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@CachePut(key = "'config'")
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
public QiniuConfig config(QiniuConfig qiniuConfig) {
|
|
||||||
qiniuConfig.setId(1L);
|
|
||||||
String http = "http://", https = "https://";
|
|
||||||
if (!(qiniuConfig.getHost().toLowerCase().startsWith(http)||qiniuConfig.getHost().toLowerCase().startsWith(https))) {
|
|
||||||
throw new BadRequestException("外链域名必须以http://或者https://开头");
|
|
||||||
}
|
|
||||||
return qiNiuConfigRepository.save(qiniuConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PageResult<QiniuContent> queryAll(QiniuQueryCriteria criteria, Pageable pageable){
|
|
||||||
return PageUtil.toPage(qiniuContentRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<QiniuContent> queryAll(QiniuQueryCriteria criteria) {
|
|
||||||
return qiniuContentRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
public QiniuContent upload(MultipartFile file, QiniuConfig qiniuConfig) {
|
|
||||||
FileUtil.checkSize(maxSize, file.getSize());
|
|
||||||
if(qiniuConfig.getId() == null){
|
|
||||||
throw new BadRequestException("请先添加相应配置,再操作");
|
|
||||||
}
|
|
||||||
// 构造一个带指定Zone对象的配置类
|
|
||||||
Configuration cfg = new Configuration(QiNiuUtil.getRegion(qiniuConfig.getZone()));
|
|
||||||
UploadManager uploadManager = new UploadManager(cfg);
|
|
||||||
Auth auth = Auth.create(qiniuConfig.getAccessKey(), qiniuConfig.getSecretKey());
|
|
||||||
String upToken = auth.uploadToken(qiniuConfig.getBucket());
|
|
||||||
try {
|
|
||||||
String key = file.getOriginalFilename();
|
|
||||||
if(qiniuContentRepository.findByKey(key) != null) {
|
|
||||||
key = QiNiuUtil.getKey(key);
|
|
||||||
}
|
|
||||||
Response response = uploadManager.put(file.getBytes(), key, upToken);
|
|
||||||
//解析上传成功的结果
|
|
||||||
|
|
||||||
DefaultPutRet putRet = JSON.parseObject(response.bodyString(), DefaultPutRet.class);
|
|
||||||
QiniuContent content = qiniuContentRepository.findByKey(FileUtil.getFileNameNoEx(putRet.key));
|
|
||||||
if(content == null){
|
|
||||||
//存入数据库
|
|
||||||
QiniuContent qiniuContent = new QiniuContent();
|
|
||||||
qiniuContent.setSuffix(FileUtil.getExtensionName(putRet.key));
|
|
||||||
qiniuContent.setBucket(qiniuConfig.getBucket());
|
|
||||||
qiniuContent.setType(qiniuConfig.getType());
|
|
||||||
qiniuContent.setKey(FileUtil.getFileNameNoEx(putRet.key));
|
|
||||||
qiniuContent.setUrl(qiniuConfig.getHost()+"/"+putRet.key);
|
|
||||||
qiniuContent.setSize(FileUtil.getSize(Integer.parseInt(String.valueOf(file.getSize()))));
|
|
||||||
return qiniuContentRepository.save(qiniuContent);
|
|
||||||
}
|
|
||||||
return content;
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new BadRequestException(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public QiniuContent findByContentId(Long id) {
|
|
||||||
QiniuContent qiniuContent = qiniuContentRepository.findById(id).orElseGet(QiniuContent::new);
|
|
||||||
ValidationUtil.isNull(qiniuContent.getId(),"QiniuContent", "id",id);
|
|
||||||
return qiniuContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String download(QiniuContent content,QiniuConfig config){
|
|
||||||
String finalUrl;
|
|
||||||
String type = "公开";
|
|
||||||
if(type.equals(content.getType())){
|
|
||||||
finalUrl = content.getUrl();
|
|
||||||
} else {
|
|
||||||
Auth auth = Auth.create(config.getAccessKey(), config.getSecretKey());
|
|
||||||
// 1小时,可以自定义链接过期时间
|
|
||||||
long expireInSeconds = 3600;
|
|
||||||
finalUrl = auth.privateDownloadUrl(content.getUrl(), expireInSeconds);
|
|
||||||
}
|
|
||||||
return finalUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
public void delete(QiniuContent content, QiniuConfig config) {
|
|
||||||
//构造一个带指定Zone对象的配置类
|
|
||||||
Configuration cfg = new Configuration(QiNiuUtil.getRegion(config.getZone()));
|
|
||||||
Auth auth = Auth.create(config.getAccessKey(), config.getSecretKey());
|
|
||||||
BucketManager bucketManager = new BucketManager(auth, cfg);
|
|
||||||
try {
|
|
||||||
bucketManager.delete(content.getBucket(), content.getKey() + "." + content.getSuffix());
|
|
||||||
qiniuContentRepository.delete(content);
|
|
||||||
} catch (QiniuException ex) {
|
|
||||||
qiniuContentRepository.delete(content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
public void synchronize(QiniuConfig config) {
|
|
||||||
if(config.getId() == null){
|
|
||||||
throw new BadRequestException("请先添加相应配置,再操作");
|
|
||||||
}
|
|
||||||
//构造一个带指定Zone对象的配置类
|
|
||||||
Configuration cfg = new Configuration(QiNiuUtil.getRegion(config.getZone()));
|
|
||||||
Auth auth = Auth.create(config.getAccessKey(), config.getSecretKey());
|
|
||||||
BucketManager bucketManager = new BucketManager(auth, cfg);
|
|
||||||
//文件名前缀
|
|
||||||
String prefix = "";
|
|
||||||
//每次迭代的长度限制,最大1000,推荐值 1000
|
|
||||||
int limit = 1000;
|
|
||||||
//指定目录分隔符,列出所有公共前缀(模拟列出目录效果)。缺省值为空字符串
|
|
||||||
String delimiter = "";
|
|
||||||
//列举空间文件列表
|
|
||||||
BucketManager.FileListIterator fileListIterator = bucketManager.createFileListIterator(config.getBucket(), prefix, limit, delimiter);
|
|
||||||
while (fileListIterator.hasNext()) {
|
|
||||||
//处理获取的file list结果
|
|
||||||
QiniuContent qiniuContent;
|
|
||||||
FileInfo[] items = fileListIterator.next();
|
|
||||||
for (FileInfo item : items) {
|
|
||||||
if(qiniuContentRepository.findByKey(FileUtil.getFileNameNoEx(item.key)) == null){
|
|
||||||
qiniuContent = new QiniuContent();
|
|
||||||
qiniuContent.setSize(FileUtil.getSize(Integer.parseInt(String.valueOf(item.fsize))));
|
|
||||||
qiniuContent.setSuffix(FileUtil.getExtensionName(item.key));
|
|
||||||
qiniuContent.setKey(FileUtil.getFileNameNoEx(item.key));
|
|
||||||
qiniuContent.setType(config.getType());
|
|
||||||
qiniuContent.setBucket(config.getBucket());
|
|
||||||
qiniuContent.setUrl(config.getHost()+"/"+item.key);
|
|
||||||
qiniuContentRepository.save(qiniuContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
public void deleteAll(Long[] ids, QiniuConfig config) {
|
|
||||||
for (Long id : ids) {
|
|
||||||
delete(findByContentId(id), config);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
public void update(String type) {
|
|
||||||
qiNiuConfigRepository.update(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void downloadList(List<QiniuContent> queryAll, HttpServletResponse response) throws IOException {
|
|
||||||
List<Map<String, Object>> list = new ArrayList<>();
|
|
||||||
for (QiniuContent content : queryAll) {
|
|
||||||
Map<String,Object> map = new LinkedHashMap<>();
|
|
||||||
map.put("文件名", content.getKey());
|
|
||||||
map.put("文件类型", content.getSuffix());
|
|
||||||
map.put("空间名称", content.getBucket());
|
|
||||||
map.put("文件大小", content.getSize());
|
|
||||||
map.put("空间类型", content.getType());
|
|
||||||
map.put("创建日期", content.getUpdateTime());
|
|
||||||
list.add(map);
|
|
||||||
}
|
|
||||||
FileUtil.downloadExcel(list, response);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,264 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019-2025 Zheng Jie
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package me.zhengjie.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.DateUtil;
|
||||||
|
import cn.hutool.core.util.IdUtil;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import me.zhengjie.config.AmzS3Config;
|
||||||
|
import me.zhengjie.domain.S3Storage;
|
||||||
|
import me.zhengjie.exception.BadRequestException;
|
||||||
|
import me.zhengjie.repository.S3StorageRepository;
|
||||||
|
import me.zhengjie.service.S3StorageService;
|
||||||
|
import me.zhengjie.service.dto.S3StorageQueryCriteria;
|
||||||
|
import me.zhengjie.utils.*;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import software.amazon.awssdk.core.ResponseInputStream;
|
||||||
|
import software.amazon.awssdk.core.sync.RequestBody;
|
||||||
|
import software.amazon.awssdk.core.waiters.WaiterResponse;
|
||||||
|
import software.amazon.awssdk.services.s3.S3Client;
|
||||||
|
import software.amazon.awssdk.services.s3.model.*;
|
||||||
|
import software.amazon.awssdk.services.s3.waiters.S3Waiter;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 服务实现
|
||||||
|
* @author Zheng Jie
|
||||||
|
* @date 2025-06-25
|
||||||
|
**/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class S3StorageServiceImpl implements S3StorageService {
|
||||||
|
|
||||||
|
private final S3Client s3Client;
|
||||||
|
private final AmzS3Config amzS3Config;
|
||||||
|
private final S3StorageRepository s3StorageRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public S3Storage getById(Long id) {
|
||||||
|
return s3StorageRepository.findById(id).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PageResult<S3Storage> queryAll(S3StorageQueryCriteria criteria, Pageable pageable){
|
||||||
|
Page<S3Storage> page = s3StorageRepository.findAll((root, criteriaQuery, criteriaBuilder)
|
||||||
|
-> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable);
|
||||||
|
return PageUtil.toPage(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<S3Storage> queryAll(S3StorageQueryCriteria criteria){
|
||||||
|
return s3StorageRepository.findAll((root, criteriaQuery, criteriaBuilder)
|
||||||
|
-> QueryHelp.getPredicate(root,criteria,criteriaBuilder));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void deleteAll(List<Long> ids) {
|
||||||
|
// 检查桶是否存在
|
||||||
|
String bucketName = amzS3Config.getDefaultBucket();
|
||||||
|
if (!bucketExists(bucketName)) {
|
||||||
|
throw new BadRequestException("存储桶不存在,请检查配置或权限。");
|
||||||
|
}
|
||||||
|
// 遍历 ID 列表,删除对应的文件和数据库记录
|
||||||
|
for (Long id : ids) {
|
||||||
|
String filePath = s3StorageRepository.selectFilePathById(id);
|
||||||
|
if (filePath == null) {
|
||||||
|
System.err.println("未找到 ID 为 " + id + " 的文件记录,无法删除。");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// 创建 DeleteObjectRequest,指定存储桶和文件键
|
||||||
|
DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder()
|
||||||
|
.bucket(bucketName)
|
||||||
|
.key(filePath)
|
||||||
|
.build();
|
||||||
|
// 调用 deleteObject 方法
|
||||||
|
s3Client.deleteObject(deleteObjectRequest);
|
||||||
|
// 删除数据库数据
|
||||||
|
s3StorageRepository.deleteById(id);
|
||||||
|
} catch (S3Exception e) {
|
||||||
|
// 处理 AWS 特定的异常
|
||||||
|
log.error("从 S3 删除文件时出错: {}", e.awsErrorDetails().errorMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public S3Storage upload(MultipartFile file) {
|
||||||
|
String bucketName = amzS3Config.getDefaultBucket();
|
||||||
|
// 检查存储桶是否存在
|
||||||
|
if (!bucketExists(bucketName)) {
|
||||||
|
log.warn("存储桶 {} 不存在,尝试创建...", bucketName);
|
||||||
|
if (createBucket(bucketName)){
|
||||||
|
log.info("存储桶 {} 创建成功。", bucketName);
|
||||||
|
} else {
|
||||||
|
throw new BadRequestException("存储桶创建失败,请检查配置或权限。");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 获取文件名
|
||||||
|
String originalName = file.getOriginalFilename();
|
||||||
|
if (StringUtils.isBlank(originalName)) {
|
||||||
|
throw new IllegalArgumentException("文件名不能为空");
|
||||||
|
}
|
||||||
|
// 生成存储路径和文件名
|
||||||
|
String folder = DateUtil.format(new Date(), amzS3Config.getTimeformat());
|
||||||
|
String fileName = IdUtil.simpleUUID() + "." + FileUtil.getExtensionName(originalName);
|
||||||
|
String filePath = folder + "/" + fileName;
|
||||||
|
// 构建上传请求
|
||||||
|
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
|
||||||
|
.bucket(amzS3Config.getDefaultBucket())
|
||||||
|
.key(filePath)
|
||||||
|
.build();
|
||||||
|
// 创建 S3Storage 实例
|
||||||
|
S3Storage s3Storage = new S3Storage();
|
||||||
|
try {
|
||||||
|
// 上传文件到 S3
|
||||||
|
s3Client.putObject(putObjectRequest, RequestBody.fromInputStream(file.getInputStream(), file.getSize()));
|
||||||
|
// 设置 S3Storage 属性
|
||||||
|
s3Storage.setFileMimeType(FileUtil.getMimeType(originalName));
|
||||||
|
s3Storage.setFileName(originalName);
|
||||||
|
s3Storage.setFileRealName(fileName);
|
||||||
|
s3Storage.setFileSize(FileUtil.getSize(file.getSize()));
|
||||||
|
s3Storage.setFileType(FileUtil.getExtensionName(originalName));
|
||||||
|
s3Storage.setFilePath(filePath);
|
||||||
|
// 保存入库
|
||||||
|
s3StorageRepository.save(s3Storage);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
// 设置地址
|
||||||
|
return s3Storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void download(List<S3Storage> all, HttpServletResponse response) throws IOException {
|
||||||
|
List<Map<String, Object>> list = new ArrayList<>();
|
||||||
|
for (S3Storage s3Storage : all) {
|
||||||
|
Map<String,Object> map = new LinkedHashMap<>();
|
||||||
|
map.put("文件名称", s3Storage.getFileName());
|
||||||
|
map.put("真实存储的名称", s3Storage.getFileRealName());
|
||||||
|
map.put("文件大小", s3Storage.getFileSize());
|
||||||
|
map.put("文件MIME 类型", s3Storage.getFileMimeType());
|
||||||
|
map.put("文件类型", s3Storage.getFileType());
|
||||||
|
map.put("文件路径", s3Storage.getFilePath());
|
||||||
|
map.put("创建者", s3Storage.getCreateBy());
|
||||||
|
map.put("更新者", s3Storage.getUpdateBy());
|
||||||
|
map.put("创建日期", s3Storage.getCreateTime());
|
||||||
|
map.put("更新时间", s3Storage.getUpdateTime());
|
||||||
|
list.add(map);
|
||||||
|
}
|
||||||
|
FileUtil.downloadExcel(list, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> privateDownload(Long id) {
|
||||||
|
S3Storage storage = s3StorageRepository.findById(id).orElse(null);
|
||||||
|
if (storage == null) {
|
||||||
|
throw new BadRequestException("文件不存在或已被删除");
|
||||||
|
}
|
||||||
|
// 创建 GetObjectRequest,指定存储桶和文件键
|
||||||
|
GetObjectRequest getObjectRequest = GetObjectRequest.builder()
|
||||||
|
.bucket(amzS3Config.getDefaultBucket())
|
||||||
|
.key(storage.getFilePath())
|
||||||
|
.build();
|
||||||
|
String base64Data;
|
||||||
|
// 使用 try-with-resources 确保流能被自动关闭
|
||||||
|
// s3Client.getObject() 返回一个 ResponseInputStream,它是一个包含S3对象数据的输入流
|
||||||
|
try (ResponseInputStream<GetObjectResponse> s3InputStream = s3Client.getObject(getObjectRequest)) {
|
||||||
|
// 使用 IOUtils.toByteArray 将输入流直接转换为字节数组
|
||||||
|
byte[] fileBytes = IOUtils.toByteArray(s3InputStream);
|
||||||
|
// 使用 Java 内置的 Base64 编码器将字节数组转换为 Base64 字符串
|
||||||
|
base64Data = Base64.getEncoder().encodeToString(fileBytes);
|
||||||
|
} catch (S3Exception e) {
|
||||||
|
// 处理 AWS 特定的异常
|
||||||
|
throw new BadRequestException("从 S3 下载文件时出错: " + e.awsErrorDetails().errorMessage());
|
||||||
|
} catch (IOException e) {
|
||||||
|
// 处理通用的 IO 异常 (IOUtils.toByteArray 可能会抛出)
|
||||||
|
throw new BadRequestException("读取 S3 输入流时出错: " + e.getMessage());
|
||||||
|
}
|
||||||
|
// 构造返回数据
|
||||||
|
Map<String, String> responseData = new HashMap<>();
|
||||||
|
// 文件名
|
||||||
|
responseData.put("fileName", storage.getFileName());
|
||||||
|
// 文件类型
|
||||||
|
responseData.put("fileMimeType", storage.getFileMimeType());
|
||||||
|
// 文件内容
|
||||||
|
responseData.put("base64Data", base64Data);
|
||||||
|
return responseData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查云存储桶是否存在
|
||||||
|
* @param bucketName 存储桶名称
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"all"})
|
||||||
|
private boolean bucketExists(String bucketName) {
|
||||||
|
try {
|
||||||
|
HeadBucketRequest headBucketRequest = HeadBucketRequest.builder()
|
||||||
|
.bucket(bucketName)
|
||||||
|
.build();
|
||||||
|
s3Client.headBucket(headBucketRequest);
|
||||||
|
return true;
|
||||||
|
} catch (S3Exception e) {
|
||||||
|
// 如果状态码是 404 (Not Found), 说明存储桶不存在
|
||||||
|
if (e.statusCode() == 404) {
|
||||||
|
log.error("存储桶 '{}' 不存在。", bucketName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 其他异常 (如 403 Forbidden) 说明存在问题,但不能断定它不存在
|
||||||
|
throw new BadRequestException("检查存储桶时出错: " + e.awsErrorDetails().errorMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建云存储桶
|
||||||
|
* @param bucketName 存储桶名称
|
||||||
|
*/
|
||||||
|
private boolean createBucket(String bucketName) {
|
||||||
|
try {
|
||||||
|
// 使用 S3Waiter 等待存储桶创建完成
|
||||||
|
S3Waiter s3Waiter = s3Client.waiter();
|
||||||
|
CreateBucketRequest bucketRequest = CreateBucketRequest.builder()
|
||||||
|
.bucket(bucketName)
|
||||||
|
.acl(BucketCannedACL.PRIVATE)
|
||||||
|
.build();
|
||||||
|
s3Client.createBucket(bucketRequest);
|
||||||
|
// 等待直到存储桶创建完成
|
||||||
|
HeadBucketRequest bucketRequestWait = HeadBucketRequest.builder()
|
||||||
|
.bucket(bucketName)
|
||||||
|
.build();
|
||||||
|
// 使用 WaiterResponse 等待存储桶存在
|
||||||
|
WaiterResponse<HeadBucketResponse> waiterResponse = s3Waiter.waitUntilBucketExists(bucketRequestWait);
|
||||||
|
waiterResponse.matched().response().ifPresent(response ->
|
||||||
|
log.info("存储桶 '{}' 创建成功,状态: {}", bucketName, response.sdkHttpResponse().statusCode())
|
||||||
|
);
|
||||||
|
} catch (BucketAlreadyOwnedByYouException e) {
|
||||||
|
log.warn("存储桶 '{}' 已经被您拥有,无需重复创建。", bucketName);
|
||||||
|
} catch (S3Exception e) {
|
||||||
|
throw new BadRequestException("创建存储桶时出错: " + e.awsErrorDetails().errorMessage());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,71 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2019-2025 Zheng Jie
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package me.zhengjie.utils;
|
|
||||||
|
|
||||||
import com.qiniu.storage.Region;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 七牛云存储工具类
|
|
||||||
* @author Zheng Jie
|
|
||||||
* @date 2018-12-31
|
|
||||||
*/
|
|
||||||
public class QiNiuUtil {
|
|
||||||
|
|
||||||
private static final String HUAD = "华东";
|
|
||||||
|
|
||||||
private static final String HUAB = "华北";
|
|
||||||
|
|
||||||
private static final String HUAN = "华南";
|
|
||||||
|
|
||||||
private static final String BEIM = "北美";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 得到机房的对应关系
|
|
||||||
* @param zone 机房名称
|
|
||||||
* @return Region
|
|
||||||
*/
|
|
||||||
public static Region getRegion(String zone){
|
|
||||||
|
|
||||||
if(HUAD.equals(zone)){
|
|
||||||
return Region.huadong();
|
|
||||||
} else if(HUAB.equals(zone)){
|
|
||||||
return Region.huabei();
|
|
||||||
} else if(HUAN.equals(zone)){
|
|
||||||
return Region.huanan();
|
|
||||||
} else if (BEIM.equals(zone)){
|
|
||||||
return Region.beimei();
|
|
||||||
// 否则就是东南亚
|
|
||||||
} else {
|
|
||||||
return Region.qvmHuadong();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 默认不指定key的情况下,以文件内容的hash值作为文件名
|
|
||||||
* @param file 文件名
|
|
||||||
* @return String
|
|
||||||
*/
|
|
||||||
public static String getKey(String file){
|
|
||||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
|
|
||||||
Date date = new Date();
|
|
||||||
return FileUtil.getFileNameNoEx(file) + "-" +
|
|
||||||
sdf.format(date) +
|
|
||||||
"." +
|
|
||||||
FileUtil.getExtensionName(file);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,16 +2,16 @@
|
||||||
Navicat Premium Dump SQL
|
Navicat Premium Dump SQL
|
||||||
|
|
||||||
Source Server : localhost
|
Source Server : localhost
|
||||||
Source Server Type : MySQL
|
Source Server Type : MariaDB
|
||||||
Source Server Version : 110206 (11.2.6-MariaDB)
|
Source Server Version : 110206 (11.2.6-MariaDB)
|
||||||
Source Host : localhost:3306
|
Source Host : localhost:3306
|
||||||
Source Schema : eladmin
|
Source Schema : eladmin
|
||||||
|
|
||||||
Target Server Type : MySQL
|
Target Server Type : MariaDB
|
||||||
Target Server Version : 110206 (11.2.6-MariaDB)
|
Target Server Version : 110206 (11.2.6-MariaDB)
|
||||||
File Encoding : 65001
|
File Encoding : 65001
|
||||||
|
|
||||||
Date: 15/01/2025 18:20:01
|
Date: 25/06/2025 15:56:51
|
||||||
*/
|
*/
|
||||||
|
|
||||||
SET NAMES utf8mb4;
|
SET NAMES utf8mb4;
|
||||||
|
@ -38,7 +38,7 @@ CREATE TABLE `code_column` (
|
||||||
`date_annotation` varchar(255) DEFAULT NULL COMMENT '日期注解',
|
`date_annotation` varchar(255) DEFAULT NULL COMMENT '日期注解',
|
||||||
PRIMARY KEY (`column_id`) USING BTREE,
|
PRIMARY KEY (`column_id`) USING BTREE,
|
||||||
KEY `idx_table_name` (`table_name`)
|
KEY `idx_table_name` (`table_name`)
|
||||||
) ENGINE=InnoDB AUTO_INCREMENT=259 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='代码生成字段信息存储';
|
) ENGINE=InnoDB AUTO_INCREMENT=266 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='代码生成字段信息存储';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of code_column
|
-- Records of code_column
|
||||||
|
@ -536,7 +536,7 @@ CREATE TABLE `sys_role` (
|
||||||
-- Records of sys_role
|
-- Records of sys_role
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
BEGIN;
|
BEGIN;
|
||||||
INSERT INTO `sys_role` (`role_id`, `name`, `level`, `description`, `data_scope`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (1, '超级管理员', 1, '-', '全部', NULL, 'admin', '2018-11-23 11:04:37', '2020-08-06 16:10:24');
|
INSERT INTO `sys_role` (`role_id`, `name`, `level`, `description`, `data_scope`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (1, '管理员', 1, '-', '全部', NULL, 'admin', '2018-11-23 11:04:37', '2025-01-21 14:53:13');
|
||||||
INSERT INTO `sys_role` (`role_id`, `name`, `level`, `description`, `data_scope`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (2, '普通用户', 2, '-', '本级', NULL, 'admin', '2018-11-23 13:09:06', '2020-09-05 10:45:12');
|
INSERT INTO `sys_role` (`role_id`, `name`, `level`, `description`, `data_scope`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (2, '普通用户', 2, '-', '本级', NULL, 'admin', '2018-11-23 13:09:06', '2020-09-05 10:45:12');
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
|
@ -705,8 +705,8 @@ CREATE TABLE `sys_user` (
|
||||||
-- Records of sys_user
|
-- Records of sys_user
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
BEGIN;
|
BEGIN;
|
||||||
INSERT INTO `sys_user` (`user_id`, `dept_id`, `username`, `nick_name`, `gender`, `phone`, `email`, `avatar_name`, `avatar_path`, `password`, `is_admin`, `enabled`, `create_by`, `update_by`, `pwd_reset_time`, `create_time`, `update_time`) VALUES (1, 2, 'admin', '管理员', '男', '18888888888', '201507802@qq.com', 'avatar-20250114101539224.png', '/Users/jie/Documents/work/me/admin/eladmin-mp/eladmin/~/avatar/avatar-20250114101539224.png', '$2a$10$Egp1/gvFlt7zhlXVfEFw4OfWQCGPw0ClmMcc6FjTnvXNRVf9zdMRa', b'1', b'1', NULL, 'admin', '2020-05-03 16:38:31', '2018-08-23 09:11:56', '2020-09-05 10:43:31');
|
INSERT INTO `sys_user` (`user_id`, `dept_id`, `username`, `nick_name`, `gender`, `phone`, `email`, `avatar_name`, `avatar_path`, `password`, `is_admin`, `enabled`, `create_by`, `update_by`, `pwd_reset_time`, `create_time`, `update_time`) VALUES (1, 2, 'admin', '管理员', '男', '18888888888', '201507802@qq.com', 'avatar-20250122102642222.png', '/Users/jie/Documents/work/private/eladmin/~/avatar/avatar-20250122102642222.png', '$2a$10$Egp1/gvFlt7zhlXVfEFw4OfWQCGPw0ClmMcc6FjTnvXNRVf9zdMRa', b'1', b'1', NULL, 'admin', '2020-05-03 16:38:31', '2018-08-23 09:11:56', '2025-01-22 10:26:42');
|
||||||
INSERT INTO `sys_user` (`user_id`, `dept_id`, `username`, `nick_name`, `gender`, `phone`, `email`, `avatar_name`, `avatar_path`, `password`, `is_admin`, `enabled`, `create_by`, `update_by`, `pwd_reset_time`, `create_time`, `update_time`) VALUES (2, 2, 'test', '测试', '男', '19999999999', '231@qq.com', NULL, NULL, '$2a$10$4XcyudOYTSz6fue6KFNMHeUQnCX5jbBQypLEnGk1PmekXt5c95JcK', b'0', b'1', 'admin', 'admin', NULL, '2020-05-05 11:15:49', '2020-09-05 10:43:38');
|
INSERT INTO `sys_user` (`user_id`, `dept_id`, `username`, `nick_name`, `gender`, `phone`, `email`, `avatar_name`, `avatar_path`, `password`, `is_admin`, `enabled`, `create_by`, `update_by`, `pwd_reset_time`, `create_time`, `update_time`) VALUES (2, 7, 'test', '测试', '男', '19999999999', '231@qq.com', NULL, NULL, '$2a$10$4XcyudOYTSz6fue6KFNMHeUQnCX5jbBQypLEnGk1PmekXt5c95JcK', b'0', b'1', 'admin', 'admin', NULL, '2020-05-05 11:15:49', '2025-01-21 14:53:04');
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
|
@ -820,47 +820,29 @@ BEGIN;
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Table structure for tool_qiniu_config
|
-- Table structure for tool_s3_storage
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
DROP TABLE IF EXISTS `tool_qiniu_config`;
|
DROP TABLE IF EXISTS `tool_s3_storage`;
|
||||||
CREATE TABLE `tool_qiniu_config` (
|
CREATE TABLE `tool_s3_storage` (
|
||||||
`config_id` bigint(20) NOT NULL COMMENT 'ID',
|
`storage_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||||
`access_key` text DEFAULT NULL COMMENT 'accessKey',
|
`file_name` varchar(255) NOT NULL COMMENT '文件名称',
|
||||||
`bucket` varchar(255) DEFAULT NULL COMMENT 'Bucket 识别符',
|
`file_real_name` varchar(255) NOT NULL COMMENT '真实存储的名称',
|
||||||
`host` varchar(255) NOT NULL COMMENT '外链域名',
|
`file_size` varchar(100) NOT NULL COMMENT '文件大小',
|
||||||
`secret_key` text DEFAULT NULL COMMENT 'secretKey',
|
`file_mime_type` varchar(50) NOT NULL COMMENT '文件MIME 类型',
|
||||||
`type` varchar(255) DEFAULT NULL COMMENT '空间类型',
|
`file_type` varchar(50) NOT NULL COMMENT '文件类型',
|
||||||
`zone` varchar(255) DEFAULT NULL COMMENT '机房',
|
`file_path` tinytext NOT NULL COMMENT '文件路径',
|
||||||
PRIMARY KEY (`config_id`) USING BTREE
|
`create_by` varchar(255) NOT NULL COMMENT '创建者',
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='七牛云配置';
|
`update_by` varchar(255) NOT NULL COMMENT '更新者',
|
||||||
|
`create_time` datetime NOT NULL COMMENT '创建日期',
|
||||||
|
`update_time` datetime NOT NULL COMMENT '更新时间',
|
||||||
|
PRIMARY KEY (`storage_id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='s3 协议对象存储';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of tool_qiniu_config
|
-- Records of tool_s3_storage
|
||||||
-- ----------------------------
|
|
||||||
BEGIN;
|
|
||||||
COMMIT;
|
|
||||||
|
|
||||||
-- ----------------------------
|
|
||||||
-- Table structure for tool_qiniu_content
|
|
||||||
-- ----------------------------
|
|
||||||
DROP TABLE IF EXISTS `tool_qiniu_content`;
|
|
||||||
CREATE TABLE `tool_qiniu_content` (
|
|
||||||
`content_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
|
||||||
`bucket` varchar(255) DEFAULT NULL COMMENT 'Bucket 识别符',
|
|
||||||
`name` varchar(180) DEFAULT NULL COMMENT '文件名称',
|
|
||||||
`size` varchar(255) DEFAULT NULL COMMENT '文件大小',
|
|
||||||
`type` varchar(255) DEFAULT NULL COMMENT '文件类型:私有或公开',
|
|
||||||
`url` varchar(255) DEFAULT NULL COMMENT '文件url',
|
|
||||||
`suffix` varchar(255) DEFAULT NULL COMMENT '文件后缀',
|
|
||||||
`update_time` datetime DEFAULT NULL COMMENT '上传或同步的时间',
|
|
||||||
PRIMARY KEY (`content_id`) USING BTREE,
|
|
||||||
UNIQUE KEY `uniq_name` (`name`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='七牛云文件存储';
|
|
||||||
|
|
||||||
-- ----------------------------
|
|
||||||
-- Records of tool_qiniu_content
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
BEGIN;
|
BEGIN;
|
||||||
|
INSERT INTO `tool_s3_storage` (`storage_id`, `file_name`, `file_real_name`, `file_size`, `file_mime_type`, `file_type`, `file_path`, `create_by`, `update_by`, `create_time`, `update_time`) VALUES (4, 'tx.jpg', '2ca1de24d8fa422eae4ede30e97c46d8.jpg', '29.67KB', 'image/jpeg', 'jpg', '2025-06/2ca1de24d8fa422eae4ede30e97c46d8.jpg', 'admin', 'admin', '2025-06-25 15:48:22', '2025-06-25 15:48:22');
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
SET FOREIGN_KEY_CHECKS = 1;
|
SET FOREIGN_KEY_CHECKS = 1;
|
||||||
|
|
Loading…
Reference in New Issue