feat:添加APP版本管理相关代码
This commit is contained in:
19
blade-service/blade-file/Dockerfile
Normal file
19
blade-service/blade-file/Dockerfile
Normal file
@@ -0,0 +1,19 @@
|
||||
FROM bladex/alpine-java:openjdk17_cn_slim
|
||||
ARG ACTIVE
|
||||
|
||||
LABEL maintainer="bladejava@qq.com"
|
||||
|
||||
# 设置时区为上海
|
||||
ENV TZ=Asia/Shanghai
|
||||
# 设置时区信息
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
COPY ./target/blade-file.jar /opt/app.jar
|
||||
|
||||
WORKDIR /opt
|
||||
|
||||
EXPOSE 8107
|
||||
|
||||
ENTRYPOINT ["java", "--add-opens", "java.base/java.lang=ALL-UNNAMED", "--add-opens", "java.base/java.lang.reflect=ALL-UNNAMED", "-Djava.security.egd=file:/dev/./urandom", "-XX:+HeapDumpOnOutOfMemoryError", "-XX:HeapDumpPath=/opt/logs", "-XX:ErrorFile=/opt/logs/hs_err_pid%p.log", "-Xlog:gc*:file=/opt/logs/gc.log:time,uptime,level,tags:filecount=5,filesize=50M", "-XX:MaxDirectMemorySize=512m", "-Xms1024m", "-Xmx4096m", "-jar", "app.jar"]
|
||||
|
||||
CMD ["--spring.profiles.active=${ACTIVE}","--logging.path=/opt/logs"]
|
||||
19
blade-service/blade-file/Dockerfile-rt
Normal file
19
blade-service/blade-file/Dockerfile-rt
Normal file
@@ -0,0 +1,19 @@
|
||||
#FROM 10.38.16.129/bladex/alpine-java:openjdk17_cn_slim
|
||||
#FROM registry.cn-hangzhou.aliyuncs.com/vic_person/alpine-java:openjdk17_cn_slim
|
||||
FROM 10.38.16.129:80/bladex/alpine-java:openjdk17_cn_slim
|
||||
ARG ACTIVE
|
||||
|
||||
LABEL maintainer="bladejava@qq.com"
|
||||
|
||||
RUN apk add --no-cache tzdata
|
||||
|
||||
# 设置时区为上海
|
||||
ENV TZ=Asia/Shanghai
|
||||
# 设置时区信息
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
COPY ./target/*.jar /opt/app.jar
|
||||
|
||||
ENTRYPOINT ["java", "--add-opens", "java.base/java.lang=ALL-UNNAMED", "--add-opens", "java.base/java.lang.reflect=ALL-UNNAMED", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/opt/app.jar"]
|
||||
|
||||
CMD ["--spring.profiles.active=${ACTIVE}"]
|
||||
19
blade-service/blade-file/Dockerfile-zh-dev
Normal file
19
blade-service/blade-file/Dockerfile-zh-dev
Normal file
@@ -0,0 +1,19 @@
|
||||
FROM bladex/alpine-java:openjdk17_cn_slim
|
||||
ARG ACTIVE
|
||||
|
||||
LABEL maintainer="bladejava@qq.com"
|
||||
|
||||
# 设置时区为上海
|
||||
ENV TZ=Asia/Shanghai
|
||||
# 设置时区信息
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
COPY ./target/blade-file.jar /opt/app.jar
|
||||
|
||||
WORKDIR /opt
|
||||
|
||||
EXPOSE 8107
|
||||
|
||||
ENTRYPOINT ["java", "--add-opens", "java.base/java.lang=ALL-UNNAMED", "--add-opens", "java.base/java.lang.reflect=ALL-UNNAMED", "-Djava.security.egd=file:/dev/./urandom", "-XX:+HeapDumpOnOutOfMemoryError", "-XX:HeapDumpPath=/opt/logs", "-XX:ErrorFile=/opt/logs/hs_err_pid%p.log", "-Xlog:gc*:file=/opt/logs/gc.log:time,uptime,level,tags:filecount=5,filesize=50M", "-XX:MaxDirectMemorySize=512m", "-Xms1024m", "-Xmx1024m", "-jar", "app.jar"]
|
||||
|
||||
CMD ["--spring.profiles.active=${ACTIVE}","--logging.path=/opt/logs"]
|
||||
139
blade-service/blade-file/pom.xml
Normal file
139
blade-service/blade-file/pom.xml
Normal file
@@ -0,0 +1,139 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>blade-service</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>blade-file</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<commons-compress.version>1.27.1</commons-compress.version>
|
||||
<xz.version>1.10</xz.version>
|
||||
|
||||
<!-- 其他依赖版本 -->
|
||||
<lombok.version>1.18.30</lombok.version>
|
||||
<org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
|
||||
<junrar.version>7.5.5</junrar.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-core-boot</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-starter-swagger</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-file-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-wps-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-ocr-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--zip解压缩-->
|
||||
<dependency>
|
||||
<groupId>net.lingala.zip4j</groupId>
|
||||
<artifactId>zip4j</artifactId>
|
||||
<version>1.3.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- 解压 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-compress</artifactId>
|
||||
<version>${commons-compress.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.tukaani</groupId>
|
||||
<artifactId>xz</artifactId>
|
||||
<version>${xz.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.junrar</groupId>
|
||||
<artifactId>junrar</artifactId>
|
||||
<version>${junrar.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-datasource-nacos</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct</artifactId>
|
||||
<version>${org.mapstruct.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>docker-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>${docker.fabric.skip}</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${maven.plugin.version}</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
<compilerArgs>
|
||||
<arg>-parameters</arg>
|
||||
</compilerArgs>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct-processor</artifactId>
|
||||
<version>${org.mapstruct.version}</version>
|
||||
</path>
|
||||
<!-- other annotation processors -->
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.file;
|
||||
|
||||
import org.springblade.core.cloud.client.BladeCloudApplication;
|
||||
import org.springblade.core.launch.BladeApplication;
|
||||
import org.springblade.core.launch.constant.AppConstant;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
|
||||
/**
|
||||
* Desk启动器
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@BladeCloudApplication
|
||||
@ComponentScan({"org.springblade.file", "org.springblade.**.feign"})
|
||||
public class FileApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
BladeApplication.run(AppConstant.APPLICATION_FILE_NAME, FileApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.springblade.file.config;
|
||||
|
||||
import com.obs.services.ObsClient;
|
||||
import org.springblade.common.utils.ObsUtil;
|
||||
import org.springblade.file.service.impl.ObsFileServiceImpl;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* 文件配置类
|
||||
* @author bfhuange
|
||||
* @date 2024/10/21
|
||||
*/
|
||||
@Import(ObsUtil.class)
|
||||
@Configuration
|
||||
public class FileConfig {
|
||||
|
||||
@Bean
|
||||
public ObsProperties obsProperties() {
|
||||
return new ObsProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ObsClient obsClient(ObsProperties properties) {
|
||||
return new ObsClient(properties.getAccessKeyId(), properties.getAccessKeySecret(), properties.getEndpoint());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ObsFileServiceImpl fileService(ObsProperties properties, ObsClient obsClient) {
|
||||
return new ObsFileServiceImpl(properties, obsClient);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package org.springblade.file.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* obs 配置
|
||||
* @author bfhuange
|
||||
* @date 2024/10/21
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "obs")
|
||||
public class ObsProperties {
|
||||
/**
|
||||
* OBS endpoint
|
||||
*/
|
||||
public String endpoint;
|
||||
/**
|
||||
* AK in the access key
|
||||
*/
|
||||
public String accessKeyId;
|
||||
/**
|
||||
* SK in the access key
|
||||
*/
|
||||
public String accessKeySecret;
|
||||
/**
|
||||
* 桶名
|
||||
*/
|
||||
public String bucketName;
|
||||
/**
|
||||
* 根目录
|
||||
*/
|
||||
public String rootDirectory;
|
||||
/**
|
||||
* 过期时间,默认1小时
|
||||
*/
|
||||
public Long expires = Duration.ofHours(1).toSeconds();
|
||||
/**
|
||||
* 本地临时下载文件目录
|
||||
*/
|
||||
public String tmpDownloadDir = "tmp" + File.separator + "download";
|
||||
/**
|
||||
* 本地临时解压文件目录
|
||||
*/
|
||||
public String tmpUnzipDir = "tmp" + File.separator + "unzip";
|
||||
/**
|
||||
* obs上压缩文件上传目录
|
||||
*/
|
||||
public String zipUploadDir = "part";
|
||||
/**
|
||||
* obs上解压后的文件上传目录
|
||||
*/
|
||||
public String unzipUploadDir = "unzip";
|
||||
/**
|
||||
* obs上设置了过期时间自动删除的上传目录
|
||||
*/
|
||||
public String tmpUploadDir = "tmp";
|
||||
/**
|
||||
* 下载进度间隔,默认 10m
|
||||
*/
|
||||
public Long downloadProgressInterval = 10 * 1024 * 1024L;
|
||||
/**
|
||||
* 上传进度间隔,默认 10m
|
||||
*/
|
||||
public Long uploadProgressInterval = 10 * 1024 * 1024L;
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package org.springblade.file.controller;
|
||||
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.file.pojo.dto.BusinessAttachmentAddDTO;
|
||||
import org.springblade.file.pojo.dto.BusinessAttachmentBatchAddDTO;
|
||||
import org.springblade.file.pojo.dto.BusinessAttachmentQueryDTO;
|
||||
import org.springblade.file.pojo.vo.BusinessAttachmentVO;
|
||||
import org.springblade.file.service.IBusinessAttachmentService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 业务附件信息表 控制器
|
||||
*
|
||||
* @author BladeX
|
||||
* @since 2024-09-04
|
||||
*/
|
||||
@Valid
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@RequestMapping("businessAttachment")
|
||||
@Tag(name = "业务附件信息表", description = "业务附件信息表接口")
|
||||
public class BusinessAttachmentController {
|
||||
|
||||
private final IBusinessAttachmentService businessAttachmentService;
|
||||
|
||||
/**
|
||||
* 业务附件信息表 新增
|
||||
*/
|
||||
@PostMapping("/batchSave")
|
||||
@ApiOperationSupport(order = 1)
|
||||
@Operation(summary = "批量新增", description = "批量新增")
|
||||
public R batchSave(@Validated @RequestBody List<BusinessAttachmentAddDTO> params) {
|
||||
return R.status(businessAttachmentService.saveAndRemove(params, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* 业务附件信息表 新增
|
||||
*/
|
||||
@PostMapping("/save")
|
||||
@ApiOperationSupport(order = 2)
|
||||
@Operation(summary = "新增", description = "新增")
|
||||
public R save(@Validated @RequestBody BusinessAttachmentBatchAddDTO param) {
|
||||
return R.status(businessAttachmentService.saveAndRemove(param));
|
||||
}
|
||||
|
||||
/**
|
||||
* 业务附件信息表 删除
|
||||
*/
|
||||
@PostMapping("/remove")
|
||||
@ApiOperationSupport(order = 3)
|
||||
@Operation(summary = "逻辑删除", description = "传入bizId")
|
||||
public R remove(@Parameter(description = "bizId", required = true) @RequestParam Long bizId) {
|
||||
return R.status(businessAttachmentService.removeByBizIds(List.of(bizId)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询业务附件信息列表
|
||||
*/
|
||||
@PostMapping("/queryList")
|
||||
@ApiOperationSupport(order = 4)
|
||||
@Operation(summary = "查询业务附件信息列表", description = "查询业务附件信息列表")
|
||||
public R<List<BusinessAttachmentVO>> queryList(@Validated @RequestBody BusinessAttachmentQueryDTO param) {
|
||||
return R.data(businessAttachmentService.queryList(param));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package org.springblade.file.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.log.annotation.ApiLog;
|
||||
import org.springblade.core.log.utils.AssertUtils;
|
||||
import org.springblade.core.mp.support.Condition;
|
||||
import org.springblade.core.mp.support.Query;
|
||||
import org.springblade.core.secure.annotation.PreAuth;
|
||||
import org.springblade.core.secure.utils.AuthUtil;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.file.pojo.dto.BusinessUnzipTaskQueryDTO;
|
||||
import org.springblade.file.pojo.dto.BusinessUnzipTaskUpdateDTO;
|
||||
import org.springblade.file.pojo.enums.UnzipTask;
|
||||
import org.springblade.file.pojo.vo.BusinessUnzipTaskListVO;
|
||||
import org.springblade.file.service.IBusinessUnzipTaskService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* @author bfhuange
|
||||
* @date 2024/10/28
|
||||
*/
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@RequestMapping("/businessUnzipTask")
|
||||
@Tag(name = "业务文件解压任务", description = "业务文件解压任务")
|
||||
public class BusinessUnzipTaskController {
|
||||
|
||||
private final IBusinessUnzipTaskService unzipTaskService;
|
||||
|
||||
/**
|
||||
* 文件任务列表
|
||||
* @param param
|
||||
* @param query
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "业务文件解压任务分页查询", description = "业务文件解压任务分页查询")
|
||||
@PreAuth("hasPermission('waybill:uploadTask:list')")
|
||||
public R<IPage<BusinessUnzipTaskListVO>> list(BusinessUnzipTaskQueryDTO param, Query query) {
|
||||
if (param == null) {
|
||||
param = new BusinessUnzipTaskQueryDTO();
|
||||
}
|
||||
// 只查文件任务类型
|
||||
param.setRelateType(UnzipTask.RelateType.FILE_TASK.getCode());
|
||||
if (param.isCurrentUser()) {
|
||||
// 只查自己的
|
||||
param.setCurrentUserId(AuthUtil.getUserId());
|
||||
}
|
||||
IPage<BusinessUnzipTaskListVO> pages = unzipTaskService.selectPage(Condition.getPage(query), param);
|
||||
return R.data(pages);
|
||||
}
|
||||
|
||||
/**
|
||||
* 业务文件解压任务更新(重新上传)
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
@ApiLog("上传任务-重新上传")
|
||||
@PostMapping("/update")
|
||||
@Operation(summary = "业务文件解压任务更新(重新上传)", description = "业务文件解压任务更新(重新上传)")
|
||||
@PreAuth("hasPermission('waybill:uploadTask:update')")
|
||||
public R update(@Validated @RequestBody BusinessUnzipTaskUpdateDTO param) {
|
||||
AssertUtils.notNull(param.getId(), "id不能为空");
|
||||
return R.status(unzipTaskService.updateReupload(param));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package org.springblade.file.controller;
|
||||
|
||||
import com.alibaba.csp.sentinel.annotation.SentinelResource;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.boot.ctrl.BladeController;
|
||||
import org.springblade.core.log.annotation.ApiLog;
|
||||
import org.springblade.core.tenant.annotation.NonDS;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.file.listener.FileEvent;
|
||||
import org.springblade.file.pojo.dto.CertificateAttachmentDTO;
|
||||
import org.springblade.file.pojo.dto.FileCertificateBatchRecognitionDTO;
|
||||
import org.springblade.file.pojo.entity.Attachment;
|
||||
import org.springblade.file.service.IAttachmentService;
|
||||
import org.springblade.file.service.IFileService;
|
||||
import org.springblade.file.service.IOCRConvertService;
|
||||
import org.springblade.thirdparty.ocr.pojo.dto.CertificateRecognitionDTO;
|
||||
import org.springblade.thirdparty.ocr.pojo.vo.TransportCertificateVO;
|
||||
import org.springblade.thirdparty.ocr.service.IOCRService;
|
||||
import org.springblade.thirdparty.wps.service.IWpsService;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @ClassName: fileUploadController
|
||||
* @Author: zhaowei
|
||||
* @Date: 2024/8/22
|
||||
*/
|
||||
@Valid
|
||||
@NonDS
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@RequestMapping("/file")
|
||||
@Tag(name = "文件相关管理", description = "文件相关管理")
|
||||
public class FileController extends BladeController {
|
||||
|
||||
private final IAttachmentService attachmentService;
|
||||
private final IFileService fileService;
|
||||
private final IWpsService wpsService;
|
||||
private final IOCRService ocrService;
|
||||
private final IOCRConvertService ocrConvertService;
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
@ApiLog("附件管理-批量上传")
|
||||
@Operation(summary = "OBS批量上传", description = "OBS批量上传,参数名:files")
|
||||
@PostMapping("/ossUpload")
|
||||
public R ossUpload(MultipartFile[] files, @RequestParam(value = "fileName", required = false) String fileName) {
|
||||
return attachmentService.batchOssUpload(files, fileName);
|
||||
}
|
||||
|
||||
@Operation(summary = "上传", description = "上传,参数名:file")
|
||||
@PostMapping("/upload")
|
||||
public R upload(MultipartFile file) {
|
||||
MultipartFile[] files = {file};
|
||||
R<List<Attachment>> result = attachmentService.batchOssUpload(files, null);
|
||||
List<Attachment> data = result.getData();
|
||||
if(data != null && !data.isEmpty())return R.data(data.get(0));
|
||||
return R.fail("上传失败");
|
||||
}
|
||||
|
||||
@Operation(summary = "获取附件,多个附件id用逗号分割", description = "获取附件,多个附件id用逗号分割")
|
||||
@GetMapping("/getAttachment")
|
||||
public R getAttachment(@RequestParam(value = "id") String id) {
|
||||
return R.data(attachmentService.getAttachment(id));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取文件url", description = "获取文件url,参数:objectKey")
|
||||
@GetMapping("/getFileUrl")
|
||||
public R getFileUrl(@RequestParam(value = "objectKey") String objectKey, @RequestParam(value = "attachmentName", required = false) String attachmentName) {
|
||||
if (StringUtil.isBlank(attachmentName)) {
|
||||
// 附件名为空,查询附件名
|
||||
Attachment attachment = attachmentService.getOne(Wrappers.<Attachment>lambdaQuery()
|
||||
.eq(Attachment::getObjectKey, objectKey)
|
||||
.last("limit 1")
|
||||
);
|
||||
attachmentName = Optional.ofNullable(attachment)
|
||||
.map(Attachment::getAttachmentName)
|
||||
.orElse(null);
|
||||
if (attachmentName != null) {
|
||||
// 附件名中如果有逗号,则替换为下划线
|
||||
attachmentName = attachmentName.replaceAll(",", "_");
|
||||
}
|
||||
}
|
||||
return R.data(fileService.getFileUrl(objectKey, attachmentName, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取wps文件预览url
|
||||
* @param attachmentId * @param attachmentName
|
||||
* @return
|
||||
*/
|
||||
@Operation(summary = "获取wps文件预览url", description = "获取wps文件预览url")
|
||||
@GetMapping("/getWpsFilePreviewUrl")
|
||||
public R getWpsFilePreviewUrl(@NotNull(message = "附件id不能为空") @RequestParam(value = "attachmentId", required = false) String attachmentId,
|
||||
@NotBlank(message = "附件名称不能为空") @RequestParam(value = "attachmentName", required = false) String attachmentName) {
|
||||
return R.data(wpsService.getFilePreviewUrl(attachmentId, attachmentName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取wps文件编辑url
|
||||
* @param attachmentId
|
||||
* @param attachmentName
|
||||
* @return
|
||||
*/
|
||||
@Operation(summary = "获取wps文件编辑url", description = "获取wps文件编辑url")
|
||||
@GetMapping("/getWpsFileEditUrl")
|
||||
public R getWpsFileEditUrl(@NotNull(message = "附件id不能为空") @RequestParam(value = "attachmentId", required = false) String attachmentId,
|
||||
@NotBlank(message = "附件名称不能为空") @RequestParam(value = "attachmentName", required = false) String attachmentName) {
|
||||
return R.data(wpsService.getWpsFileEditUrl(attachmentId, attachmentName));
|
||||
}
|
||||
|
||||
@Operation(summary = "批量获取文件url", description = "批量获取文件url,参数:objectKey数组")
|
||||
@PostMapping("/getFileUrls")
|
||||
public R getFileUrls(@RequestBody List<String> objectKeys) {
|
||||
return R.data(fileService.getFileUrls(objectKeys, null));
|
||||
}
|
||||
|
||||
@ApiLog("OCR识别-识别身份证")
|
||||
@Operation(summary = "识别身份证信息支持正反面", description = "参数:objectKey")
|
||||
@GetMapping("/recognitionIDCard")
|
||||
public R recognitionIDCard(@NotNull(message = "objectKey不能为空") @RequestParam(value = "objectKey", required = false) String objectKey) {
|
||||
String fileUrl = fileService.getFileUrl(objectKey, null);
|
||||
return R.data(ocrService.recognitionIDCard(List.of(fileUrl)));
|
||||
}
|
||||
|
||||
@SentinelResource("ocr:batchCards")
|
||||
@ApiLog("OCR识别-识别车辆运输凭证(不知道类型)")
|
||||
@Operation(summary = "识别车辆运输凭证,不知道类型", description = "参数:objectKeylist")
|
||||
@PostMapping("/recognitionTransportCertificates")
|
||||
public R<TransportCertificateVO> recognitionTransportCertificates(@RequestBody List<String> objectKeys, @RequestParam(value = "projectAbbreviation", required = false) String projectAbbreviation, @RequestParam(value = "code", required = false) String code) {
|
||||
return R.data(ocrConvertService.recognitionTransportCertificate(new FileCertificateBatchRecognitionDTO(objectKeys, projectAbbreviation, code, null)));
|
||||
}
|
||||
|
||||
@ApiLog("OCR识别-识别车辆运输凭证(知道类型)")
|
||||
@Operation(summary = "识别车辆运输凭证,知道类型", description = "参数:objectKeylist")
|
||||
@PostMapping("/recognitionTransportCertificatesByType")
|
||||
public R<TransportCertificateVO> recognitionTransportCertificatesByType(@RequestBody CertificateAttachmentDTO param, @RequestParam(value = "code", required = false) String code) {
|
||||
CertificateRecognitionDTO recognitionParam = new CertificateRecognitionDTO();
|
||||
recognitionParam.setPoundUrl(fileService.getFileUrl(param.getPoundObjectKey(), null));
|
||||
recognitionParam.setCarFrontUrl(fileService.getFileUrl(param.getCarFrontObjectKey(), null));
|
||||
recognitionParam.setDriverLicenseUrl(fileService.getFileUrl(param.getDriverLicenseObjectKey(), null));
|
||||
recognitionParam.setVehicleLicenseUrl(fileService.getFileUrl(param.getVehicleLicenseObjectKey(), null));
|
||||
recognitionParam.setName(param.getProjectAbbreviation());
|
||||
return R.data(ocrService.recognitionTransportCertificate(recognitionParam, code));
|
||||
}
|
||||
|
||||
@Operation(hidden = true)
|
||||
@PostMapping("/publishEvent")
|
||||
public R<Boolean> publishEvent(String code, Long id) {
|
||||
ApplicationEvent event = FileEvent.createEvent(code, id);
|
||||
if (event == null) {
|
||||
return R.fail("事件名称错误");
|
||||
}
|
||||
applicationContext.publishEvent(event);
|
||||
return R.status(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package org.springblade.file.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.boot.ctrl.BladeController;
|
||||
import org.springblade.core.log.annotation.ApiLog;
|
||||
import org.springblade.core.tenant.annotation.NonDS;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.file.pojo.dto.FileTaskCreateDTO;
|
||||
import org.springblade.file.pojo.dto.FileTaskUpdateDTO;
|
||||
import org.springblade.file.pojo.dto.IdsDTO;
|
||||
import org.springblade.file.pojo.vo.FileTaskUpdateVO;
|
||||
import org.springblade.file.service.IFileTaskService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* @ClassName: FileTaskController
|
||||
* @Author: zhaowei
|
||||
* @Date: 2024/8/28
|
||||
*/
|
||||
@Valid
|
||||
@NonDS
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@RequestMapping("/fileTask")
|
||||
@Tag(name = "文件任务", description = "文件任务")
|
||||
public class FileTaskController extends BladeController {
|
||||
|
||||
private final IFileTaskService fileTaskService;
|
||||
|
||||
@GetMapping("/queryFileTask")
|
||||
@Operation(summary = "根据文件的MD5值查询文件任务", description = "文件的MD5值检查分段,参数名:md5")
|
||||
public R<FileTaskUpdateVO> queryFileTask(@RequestParam(value = "md5") String md5) {
|
||||
FileTaskUpdateVO fileTask = fileTaskService.queryFileTask(null, md5);
|
||||
return R.data(fileTask);
|
||||
}
|
||||
|
||||
@ApiLog("上传任务-创建文件任务")
|
||||
@PostMapping("/createFileTask")
|
||||
@Operation(summary = "创建文件任务", description = "创建文件任务")
|
||||
public R<FileTaskUpdateVO> createFileTask(@RequestBody FileTaskCreateDTO param) {
|
||||
Long id = fileTaskService.createFileTask(param);
|
||||
return R.data(fileTaskService.queryFileTask(id, null));
|
||||
}
|
||||
|
||||
@ApiLog("上传任务-更新文件任务进度")
|
||||
@PostMapping("/updateFileTask")
|
||||
@Operation(summary = "更新文件任务进度", description = "更新文件任务进度")
|
||||
public R<FileTaskUpdateVO> updateFileTask(@RequestBody FileTaskUpdateDTO param) {
|
||||
return R.data(fileTaskService.updateFileTask(param));
|
||||
}
|
||||
|
||||
@ApiLog("上传任务-修改文件任务状态为失败")
|
||||
@PostMapping("/updateFailed")
|
||||
@Operation(summary = "更新文件任务为失败", description = "更新文件任务为失败")
|
||||
public R<FileTaskUpdateVO> updateFailed(@NotNull(message = "文件任务id不能为空") Long id) {
|
||||
return R.data(fileTaskService.updateFailed(id));
|
||||
}
|
||||
|
||||
@ApiLog("上传任务-暂停文件任务")
|
||||
@PostMapping("/updatePaused")
|
||||
@Operation(summary = "更新文件任务为暂停", description = "更新文件任务为暂停")
|
||||
public R<Boolean> updatePaused(@Validated @RequestBody IdsDTO param) {
|
||||
return R.status(fileTaskService.updatePaused(param.getIds()));
|
||||
}
|
||||
|
||||
@GetMapping("/getUploadUrl")
|
||||
@Parameters({
|
||||
@Parameter(name = "id", description = "文件任务id", in = ParameterIn.QUERY, schema = @Schema(type = "string")),
|
||||
@Parameter(name = "partNumber", description = "分段编号", in = ParameterIn.QUERY, schema = @Schema(type = "int"))
|
||||
})
|
||||
@Operation(summary = "根据文件任务id查询上传链接", description = "根据文件任务id查询上传链接")
|
||||
public R<String> getUploadUrl(Long id, Integer partNumber) {
|
||||
String uploadUrl = fileTaskService.getUploadUrl(id, partNumber);
|
||||
return R.data(uploadUrl);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.springblade.file.convert;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingConstants;
|
||||
import org.mapstruct.ReportingPolicy;
|
||||
import org.springblade.file.pojo.dto.BusinessAttachmentAddDTO;
|
||||
import org.springblade.file.pojo.dto.BusinessAttachmentBatchAddDTO;
|
||||
import org.springblade.file.pojo.entity.Attachment;
|
||||
import org.springblade.file.pojo.entity.BusinessAttachment;
|
||||
import org.springblade.file.pojo.vo.AttachmentDetailVO;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* @author bfhuange
|
||||
* @date 2024/9/4
|
||||
*/
|
||||
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, unmappedTargetPolicy = ReportingPolicy.IGNORE)
|
||||
public interface BusinessAttachmentConvert {
|
||||
BusinessAttachment dto2entity(BusinessAttachmentAddDTO dto);
|
||||
|
||||
List<BusinessAttachment> dtos2entities(List<BusinessAttachmentAddDTO> dtos);
|
||||
|
||||
AttachmentDetailVO entity2vo(Attachment entity);
|
||||
|
||||
default List<BusinessAttachment> dto2entities(BusinessAttachmentBatchAddDTO dto) {
|
||||
if (dto == null || dto.getDetails() == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
AtomicInteger sort = new AtomicInteger(1);
|
||||
return dto.getDetails().stream()
|
||||
.flatMap(detail -> detail.getAttachmentIds().stream().map(attachmentId -> {
|
||||
BusinessAttachment businessAttachment = new BusinessAttachment();
|
||||
businessAttachment.setAttachmentId(attachmentId);
|
||||
businessAttachment.setBizId(dto.getBizId());
|
||||
businessAttachment.setBizCode(detail.getBizCode());
|
||||
businessAttachment.setSort(sort.getAndIncrement());
|
||||
return businessAttachment;
|
||||
})).toList();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.springblade.file.convert;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingConstants;
|
||||
import org.mapstruct.ReportingPolicy;
|
||||
import org.springblade.file.pojo.dto.BusinessUnzipTaskAddDTO;
|
||||
import org.springblade.file.pojo.dto.FileTaskCreateDTO;
|
||||
import org.springblade.file.pojo.entity.BusinessUnzipTask;
|
||||
import org.springblade.file.pojo.entity.FileTask;
|
||||
import org.springblade.file.pojo.entity.FileTaskPart;
|
||||
import org.springblade.file.pojo.vo.FileTaskUpdateVO;
|
||||
import org.springblade.file.pojo.vo.FileTaskVO;
|
||||
import org.springblade.file.pojo.vo.PartEtagVO;
|
||||
|
||||
/**
|
||||
* @author bfhuange
|
||||
* @date 2024/10/22
|
||||
*/
|
||||
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, unmappedTargetPolicy = ReportingPolicy.IGNORE)
|
||||
public interface FileTaskConvert {
|
||||
|
||||
FileTaskVO entity2vo(FileTask entity);
|
||||
|
||||
FileTaskUpdateVO entity2updateVo(FileTask entity);
|
||||
|
||||
FileTask dto2entity(FileTaskCreateDTO dto);
|
||||
|
||||
PartEtagVO entity2vo(FileTaskPart entity);
|
||||
|
||||
BusinessUnzipTask dto2entity(BusinessUnzipTaskAddDTO dto);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.springblade.file.convert;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingConstants;
|
||||
import org.mapstruct.ReportingPolicy;
|
||||
import org.springblade.file.pojo.dto.FileCertificateBatchRecognitionDTO;
|
||||
import org.springblade.thirdparty.ocr.pojo.dto.CertificateBatchRecognitionDTO;
|
||||
|
||||
/**
|
||||
* @author bfhuange
|
||||
* @since 2025/3/13
|
||||
*/
|
||||
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, unmappedTargetPolicy = ReportingPolicy.IGNORE)
|
||||
public interface OCRConverter {
|
||||
|
||||
CertificateBatchRecognitionDTO dto2ocr(FileCertificateBatchRecognitionDTO dto);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.springblade.file.exception;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* 文件解压失败异常
|
||||
* @author bfhuange
|
||||
* @date 2024/11/11
|
||||
*/
|
||||
@Getter
|
||||
public class UnzipFailException extends RuntimeException{
|
||||
/**
|
||||
* 解压目录
|
||||
*/
|
||||
private final File unzipDir;
|
||||
|
||||
public UnzipFailException(Throwable cause, File unzipDir) {
|
||||
super(cause);
|
||||
this.unzipDir = unzipDir;
|
||||
}
|
||||
|
||||
public UnzipFailException(String message, File unzipDir) {
|
||||
super(message);
|
||||
this.unzipDir = unzipDir;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package org.springblade.file.feign;
|
||||
|
||||
import com.alibaba.csp.sentinel.annotation.SentinelResource;
|
||||
import io.swagger.v3.oas.annotations.Hidden;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.log.utils.AssertUtils;
|
||||
import org.springblade.core.tool.api.FR;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.file.config.ObsProperties;
|
||||
import org.springblade.file.pojo.dto.*;
|
||||
import org.springblade.file.pojo.entity.Attachment;
|
||||
import org.springblade.file.pojo.vo.*;
|
||||
import org.springblade.file.service.*;
|
||||
import org.springblade.file.util.ObjectKeyUtils;
|
||||
import org.springblade.thirdparty.ocr.pojo.vo.TransportCertificateVO;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author bfhuange
|
||||
* @date 2024/10/21
|
||||
*/
|
||||
@Slf4j
|
||||
@Valid
|
||||
@Hidden
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
public class FileClient implements IFileClient, IFileUploadClient {
|
||||
|
||||
private final IAttachmentService attachmentService;
|
||||
private final IBusinessAttachmentService businessAttachmentService;
|
||||
private final IBusinessUnzipTaskService businessUnzipTaskService;
|
||||
private final IOCRConvertService ocrConvertService;
|
||||
private final IFileService fileService;
|
||||
private final ObsProperties obsProperties;
|
||||
|
||||
@Override
|
||||
@GetMapping(ATTACHMENT)
|
||||
public FR<List<Attachment>> getAttachment(String id) {
|
||||
return FR.data(attachmentService.getAttachment(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FR<AttachmentDetailVO> getAttachmentDetail(Long id) {
|
||||
return FR.data(attachmentService.getDetail(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FR saveAndRemoveBusinessAttachment(@RequestBody @Validated BusinessAttachmentBatchAddDTO param) {
|
||||
return FR.status(businessAttachmentService.saveAndRemove(param));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FR batchSaveAndRemoveBusinessAttachment(@RequestBody List<@Valid BusinessAttachmentAddDTO> params, @RequestParam(value = "bizId", required = false) Long bizId) {
|
||||
return FR.status(businessAttachmentService.saveAndRemove(params, new BusinessAttachmentDeleteDTO(bizId)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FR batchSaveAndRemoveByParamBusinessAttachment(@RequestBody List<@Valid BusinessAttachmentAddDTO> params, @Valid BusinessAttachmentDeleteDTO deleteParam) {
|
||||
return FR.status(businessAttachmentService.saveAndRemove(params, deleteParam));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FR batchSaveAndRemoveBusinessAttachment(List<@Valid BusinessAttachmentAddDTO> params) {
|
||||
return FR.status(businessAttachmentService.saveAndRemove(params, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FR removeBusinessAttachment(@NotNull(message = "业务id不能为空") @RequestParam(value = "bizId", required = false) Long bizId) {
|
||||
businessAttachmentService.removeByBizIds(List.of(bizId));
|
||||
return FR.data(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FR<List<BusinessAttachmentVO>> getBusinessAttachments(@Validated @RequestBody BusinessAttachmentQueryDTO param) {
|
||||
return FR.data(businessAttachmentService.queryList(param));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FR saveBusinessUnzipTask(BusinessUnzipTaskAddDTO param) {
|
||||
return FR.status(businessUnzipTaskService.save(param));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FR updateBusinessUnzipTask(BusinessUnzipTaskUpdateDTO param) {
|
||||
AssertUtils.notNull(param.getBizId(), "业务id不能为空");
|
||||
return FR.status(businessUnzipTaskService.updateReupload(param));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FR cancelBusinessUnzipTask(@NotNull(message = "业务id不能为空") Long bizId) {
|
||||
return FR.status(businessUnzipTaskService.cancelBusinessUnzipTask(bizId, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FR removeBusinessUnzipTask(Long bizId) {
|
||||
return FR.status(businessUnzipTaskService.cancelBusinessUnzipTask(bizId, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FR<List<BusinessUnzipTaskListVO>> getBusinessUnzipTasksByBizIds(List<Long> bizIds) {
|
||||
return FR.data(businessUnzipTaskService.getBusinessUnzipTasksByBizIds(bizIds));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FR<List<BusinessUnzipDetailListVO>> queryBusinessUnzipDetail(@NotNull(message = "业务id不能为空") Long bizId) {
|
||||
return FR.data(businessUnzipTaskService.queryBusinessUnzipDetail(bizId));
|
||||
}
|
||||
|
||||
@SentinelResource("ocr:batchCards")
|
||||
@Override
|
||||
public FR<TransportCertificateVO> recognitionTransportCertificates(@Validated @RequestBody FileCertificateBatchRecognitionDTO param) {
|
||||
return FR.data(ocrConvertService.recognitionTransportCertificate(param));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FR<List<FileUrlVO>> getFileUrls(IdsDTO param) {
|
||||
List<FileUrlVO> list = attachmentService.listByIds(param.getIds()).stream()
|
||||
.map(attachment -> {
|
||||
String fileUrl = fileService.getFileUrl(attachment.getObjectKey(), attachment.getAttachmentName(), null);
|
||||
return new FileUrlVO(attachment.getId(), fileUrl);
|
||||
})
|
||||
.toList();
|
||||
return FR.data(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FR<List<Attachment>> ossUpload(MultipartFile[] files) {
|
||||
R<List<Attachment>> r = attachmentService.batchOssUpload(files, null);
|
||||
return R.isNotSuccess(r) ? FR.fail(r.getCode(), r.getMsg()) : FR.data(r.getData(), r.getMsg());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FR<TempFileUrlVO> ossUploadTemp(MultipartFile file, Integer expires) {
|
||||
if (expires == null) {
|
||||
// 默认1天过期
|
||||
expires = 1;
|
||||
}
|
||||
try {
|
||||
String objectKey = ObjectKeyUtils.generateObjectKey(obsProperties.getTmpUploadDir(), file.getOriginalFilename());
|
||||
String fileUrl = fileService.uploadTempFile(objectKey, file.getInputStream(), expires);
|
||||
return FR.data(new TempFileUrlVO(objectKey, fileUrl));
|
||||
} catch (Exception e) {
|
||||
log.error("上传临时文件异常", e);
|
||||
return FR.fail("上传临时文件异常");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.springblade.file.listener;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 文件事件枚举
|
||||
* @author bfhuange
|
||||
* @since 2024/11/22
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum FileEvent {
|
||||
/**
|
||||
* 上传完成事件
|
||||
*/
|
||||
UPLOADED(FileUploadedEvent::new),
|
||||
/**
|
||||
* 解压开始事件
|
||||
*/
|
||||
UNZIP_START(UnzipStartEvent::new),
|
||||
/**
|
||||
* 解压完成事件
|
||||
*/
|
||||
UNZIP_COMPLETED(UnzipCompletedEvent::new),
|
||||
/**
|
||||
* 解压失败事件
|
||||
*/
|
||||
UNZIP_FAILED(UnzipFailEvent::new),
|
||||
;
|
||||
/**
|
||||
* 事件函数
|
||||
*/
|
||||
private final Function<Long, ApplicationEvent> eventFunction;
|
||||
|
||||
/**
|
||||
* 根据编码创建事件
|
||||
* @param code
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
public static ApplicationEvent createEvent(String code, Long id) {
|
||||
if (code == null || code.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
return Stream.of(values())
|
||||
.filter(event -> event.name().equalsIgnoreCase(code))
|
||||
.findFirst()
|
||||
.map(event -> event.getEventFunction().apply(id))
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
package org.springblade.file.listener;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.log.utils.AssertUtils;
|
||||
import org.springblade.file.callback.UnzipCallback;
|
||||
import org.springblade.file.callback.UnzipEventType;
|
||||
import org.springblade.file.pojo.entity.BusinessUnzipTask;
|
||||
import org.springblade.file.pojo.enums.UnzipTask;
|
||||
import org.springblade.file.pojo.vo.FileVO;
|
||||
import org.springblade.file.service.IBusinessUnzipDetailService;
|
||||
import org.springblade.file.service.IBusinessUnzipTaskService;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.event.TransactionPhase;
|
||||
import org.springframework.transaction.event.TransactionalEventListener;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 文件监听器
|
||||
* @author bfhuange
|
||||
* @date 2024/11/8
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FileListener {
|
||||
|
||||
private final IBusinessUnzipTaskService taskService;
|
||||
private final IBusinessUnzipDetailService detailService;
|
||||
private final Map<String, UnzipCallback> callbackMap;
|
||||
|
||||
public FileListener(IBusinessUnzipTaskService taskService, IBusinessUnzipDetailService detailService, List<UnzipCallback> callbacks) {
|
||||
this.taskService = taskService;
|
||||
this.detailService = detailService;
|
||||
this.callbackMap = callbacks.stream()
|
||||
.collect(Collectors.toMap(UnzipCallback::getBizCode, Function.identity(), (a, b) -> {
|
||||
throw new ServiceException("重复的解压完成回调接口");
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件上传完成监听器
|
||||
* @param event
|
||||
*/
|
||||
@Async
|
||||
@TransactionalEventListener(value = FileUploadedEvent.class, phase = TransactionPhase.AFTER_COMMIT, fallbackExecution = true)
|
||||
public void fileUploaded(FileUploadedEvent event) {
|
||||
try {
|
||||
Long fileTaskId = event.getFileTaskId();
|
||||
log.info("文件上传任务完成事件 id:{}", fileTaskId);
|
||||
BusinessUnzipTask businessUnzipTask = taskService.getOne(Wrappers.<BusinessUnzipTask>lambdaQuery()
|
||||
.eq(BusinessUnzipTask::getRelateId, fileTaskId)
|
||||
.last("limit 1")
|
||||
);
|
||||
if (businessUnzipTask != null) {
|
||||
// 存在解压任务,更新任务为上传完成
|
||||
BusinessUnzipTask updateParam = new BusinessUnzipTask();
|
||||
updateParam.setId(businessUnzipTask.getId());
|
||||
updateParam.setStatus(UnzipTask.Status.UPLOADED.getCode());
|
||||
taskService.updateById(updateParam);
|
||||
// 解压开始
|
||||
this.unzipStart(new UnzipStartEvent(businessUnzipTask));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("文件上传任务完成事件 异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解压开始
|
||||
* @param event
|
||||
*/
|
||||
@Async
|
||||
@TransactionalEventListener(value = UnzipStartEvent.class, phase = TransactionPhase.AFTER_COMMIT, fallbackExecution = true)
|
||||
public void unzipStart(UnzipStartEvent event) {
|
||||
try {
|
||||
Long unzipTaskId = event.getUnzipTaskId();
|
||||
BusinessUnzipTask task = event.getTask();
|
||||
log.info("解压开始事件 id:{} 业务解压任务:{}", unzipTaskId, JSON.toJSONString(task));
|
||||
if (task == null) {
|
||||
// 手动触发时,只有id
|
||||
task = taskService.getById(unzipTaskId);
|
||||
}
|
||||
// 回调接口
|
||||
callback(task, UnzipEventType.START);
|
||||
// 解压并保存
|
||||
detailService.unzipAndSaveAsync(task.getId(), taskService.getFile(task));
|
||||
} catch (Exception e) {
|
||||
log.error("解压开始事件 异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解压完成监听器
|
||||
* @param event
|
||||
*/
|
||||
@Async
|
||||
@EventListener(UnzipCompletedEvent.class)
|
||||
public void unzipCompleted(UnzipCompletedEvent event) {
|
||||
try {
|
||||
Long unzipTaskId = event.getUnzipTaskId();
|
||||
log.info("解压完成事件 id:{}", unzipTaskId);
|
||||
// 查询解压任务
|
||||
BusinessUnzipTask unzipTask = taskService.getById(unzipTaskId);
|
||||
AssertUtils.notNull(unzipTask, "解压任务不存在");
|
||||
// 更新解压任务状态
|
||||
BusinessUnzipTask updateParam = new BusinessUnzipTask();
|
||||
updateParam.setId(unzipTaskId);
|
||||
updateParam.setStatus(UnzipTask.Status.FINISHED.getCode());
|
||||
taskService.updateById(updateParam);
|
||||
// 回调接口
|
||||
callback(unzipTask, UnzipEventType.SUCCESS);
|
||||
} catch (Exception e) {
|
||||
log.error("解压完成事件 异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解压失败监听器
|
||||
* @param event
|
||||
*/
|
||||
@Async
|
||||
@EventListener(UnzipFailEvent.class)
|
||||
public void unzipFail(UnzipFailEvent event) {
|
||||
try {
|
||||
Long unzipTaskId = event.getUnzipTaskId();
|
||||
log.info("解压失败事件 id:{}", unzipTaskId);
|
||||
// 查询解压任务
|
||||
BusinessUnzipTask unzipTask = taskService.getById(unzipTaskId);
|
||||
AssertUtils.notNull(unzipTask, "解压任务不存在");
|
||||
// 更新解压任务状态
|
||||
BusinessUnzipTask updateParam = new BusinessUnzipTask();
|
||||
updateParam.setId(unzipTaskId);
|
||||
updateParam.setStatus(UnzipTask.Status.UNZIP_FAIL.getCode());
|
||||
taskService.updateById(updateParam);
|
||||
// 回调接口
|
||||
callback(unzipTask, UnzipEventType.FAIL);
|
||||
} catch (Exception e) {
|
||||
log.error("解压失败事件 异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 回调接口
|
||||
* @param unzipTask
|
||||
* @param event
|
||||
*/
|
||||
private void callback(BusinessUnzipTask unzipTask, UnzipEventType event) {
|
||||
if (unzipTask == null) {
|
||||
log.warn("解压回调接口 解压任务为空");
|
||||
return;
|
||||
}
|
||||
UnzipCallback unzipCallback = callbackMap.get(unzipTask.getBizCode());
|
||||
if (unzipCallback == null) {
|
||||
log.error("未配置业务编码:{}对应的解压回调接口", unzipTask.getBizCode());
|
||||
return;
|
||||
}
|
||||
Boolean result = null;
|
||||
try {
|
||||
result = unzipCallback.callback(unzipTask.getBizId(), event.name());
|
||||
} catch (Exception e) {
|
||||
log.error("解压回调接口异常", e);
|
||||
}
|
||||
log.info("解压回调接口参数:{} 业务编码:{} 事件:{} 结果:{}", unzipTask.getBizId(), unzipTask.getBizCode(), event.name(), result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.springblade.file.listener;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
/**
|
||||
* @author bfhuange
|
||||
* @date 2024/11/8
|
||||
*/
|
||||
@Getter
|
||||
public class FileUploadedEvent extends ApplicationEvent {
|
||||
/**
|
||||
* 文件任务id
|
||||
*/
|
||||
private final Long fileTaskId;
|
||||
|
||||
public FileUploadedEvent(Long fileTaskId) {
|
||||
super(fileTaskId);
|
||||
this.fileTaskId = fileTaskId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.springblade.file.listener;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
/**
|
||||
* 解压完成事件
|
||||
* @author bfhuange
|
||||
* @date 2024/11/8
|
||||
*/
|
||||
@Getter
|
||||
public class UnzipCompletedEvent extends ApplicationEvent {
|
||||
/**
|
||||
* 解压任务id
|
||||
*/
|
||||
private final Long unzipTaskId;
|
||||
|
||||
public UnzipCompletedEvent(Long unzipTaskId) {
|
||||
super(unzipTaskId);
|
||||
this.unzipTaskId = unzipTaskId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.springblade.file.listener;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
/**
|
||||
* 解压失败事件
|
||||
* @author bfhuange
|
||||
* @date 2024/11/8
|
||||
*/
|
||||
@Getter
|
||||
public class UnzipFailEvent extends ApplicationEvent {
|
||||
/**
|
||||
* 解压任务id
|
||||
*/
|
||||
private final Long unzipTaskId;
|
||||
|
||||
public UnzipFailEvent(Long unzipTaskId) {
|
||||
super(unzipTaskId);
|
||||
this.unzipTaskId = unzipTaskId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.springblade.file.listener;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.springblade.file.pojo.entity.BusinessUnzipTask;
|
||||
import org.springblade.file.pojo.vo.FileVO;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
/**
|
||||
* 解压开始事件
|
||||
* @author bfhuange
|
||||
* @date 2024/11/8
|
||||
*/
|
||||
@Getter
|
||||
public class UnzipStartEvent extends ApplicationEvent {
|
||||
/**
|
||||
* 解压任务id
|
||||
*/
|
||||
private final Long unzipTaskId;
|
||||
/**
|
||||
* 解压任务
|
||||
*/
|
||||
private final BusinessUnzipTask task;
|
||||
|
||||
public UnzipStartEvent(Long unzipTaskId) {
|
||||
super(unzipTaskId);
|
||||
this.unzipTaskId = unzipTaskId;
|
||||
this.task = null;
|
||||
}
|
||||
|
||||
public UnzipStartEvent(BusinessUnzipTask task) {
|
||||
super(task);
|
||||
this.task = task;
|
||||
this.unzipTaskId = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.file.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.springblade.file.pojo.entity.Attachment;
|
||||
|
||||
/**
|
||||
* AttachmentMapper 接口
|
||||
*
|
||||
* @author zhaowei
|
||||
*/
|
||||
public interface AttachmentMapper extends BaseMapper<Attachment> {
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?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="org.springblade.file.mapper.AttachmentMapper">
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.springblade.file.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.springblade.file.pojo.dto.BusinessAttachmentQueryDTO;
|
||||
import org.springblade.file.pojo.entity.BusinessAttachment;
|
||||
import org.springblade.file.pojo.vo.BusinessAttachmentVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 业务附件信息表 Mapper 接口
|
||||
*
|
||||
* @author BladeX
|
||||
* @since 2024-09-04
|
||||
*/
|
||||
public interface BusinessAttachmentMapper extends BaseMapper<BusinessAttachment> {
|
||||
|
||||
/**
|
||||
* 查询业务附件列表
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
List<BusinessAttachmentVO> queryList(BusinessAttachmentQueryDTO param);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?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="org.springblade.file.mapper.BusinessAttachmentMapper">
|
||||
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="businessAttachmentResultMap" type="org.springblade.file.pojo.entity.BusinessAttachment">
|
||||
<result column="id" property="id"/>
|
||||
<result column="tenant_id" property="tenantId"/>
|
||||
<result column="biz_id" property="bizId"/>
|
||||
<result column="biz_code" property="bizCode"/>
|
||||
<result column="attachment_id" property="attachmentId"/>
|
||||
<result column="`desc`" property="desc"/>
|
||||
<result column="sort" property="sort"/>
|
||||
<result column="create_user" property="createUser"/>
|
||||
<result column="create_time" property="createTime"/>
|
||||
<result column="update_user" property="updateUser"/>
|
||||
<result column="update_time" property="updateTime"/>
|
||||
<result column="is_deleted" property="isDeleted"/>
|
||||
</resultMap>
|
||||
<select id="queryList" resultType="org.springblade.file.pojo.vo.BusinessAttachmentVO">
|
||||
select
|
||||
bba.id,
|
||||
bba.biz_id,
|
||||
bba.biz_code,
|
||||
bba.attachment_id,
|
||||
bba.`desc`,
|
||||
bba.sort,
|
||||
ba.dir,
|
||||
ba.attachment_name,
|
||||
ba.size,
|
||||
ba.object_key,
|
||||
ba.create_user uploadUserId,
|
||||
ba.create_user_name uploadUserName,
|
||||
ba.create_time,
|
||||
ba.create_user_name
|
||||
from blade_business_attachment bba
|
||||
left join blade_attachment ba on ba.id = bba.attachment_id
|
||||
where bba.is_deleted = 0 and ba.is_deleted = 0 and bba.biz_id in <foreach collection="bizIds" item="item" open="(" close=")" separator=",">#{item}</foreach>
|
||||
<if test="bizCode != null and bizCode != ''">
|
||||
and bba.biz_code = #{bizCode}
|
||||
</if>
|
||||
<if test="bizCodes != null and bizCodes.size() > 0">
|
||||
and bba.biz_code in <foreach collection="bizCodes" item="item" open="(" close=")" separator=",">#{item}</foreach>
|
||||
</if>
|
||||
order by bba.biz_id, bba.sort, bba.id
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.springblade.file.mapper;
|
||||
|
||||
import org.springblade.file.pojo.entity.BusinessUnzipDetail;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 业务解压明细表 Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author bfhuange
|
||||
* @since 2024-10-30
|
||||
*/
|
||||
public interface BusinessUnzipDetailMapper extends BaseMapper<BusinessUnzipDetail> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?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="org.springblade.file.mapper.BusinessUnzipDetailMapper">
|
||||
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="BaseResultMap" type="org.springblade.file.pojo.entity.BusinessUnzipDetail">
|
||||
<id column="id" property="id" />
|
||||
<result column="business_unzip_task_id" property="businessUnzipTaskId" />
|
||||
<result column="attachment_id" property="attachmentId" />
|
||||
<result column="create_user" property="createUser" />
|
||||
<result column="create_time" property="createTime" />
|
||||
<result column="update_user" property="updateUser" />
|
||||
<result column="update_time" property="updateTime" />
|
||||
<result column="is_deleted" property="isDeleted" />
|
||||
</resultMap>
|
||||
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
id, business_unzip_task_id, attachment_id, create_user, create_time, update_user, update_time, is_deleted
|
||||
</sql>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.springblade.file.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springblade.file.pojo.dto.BusinessUnzipTaskQueryDTO;
|
||||
import org.springblade.file.pojo.entity.BusinessUnzipTask;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.springblade.file.pojo.vo.BusinessUnzipDetailListVO;
|
||||
import org.springblade.file.pojo.vo.BusinessUnzipTaskListVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 业务解压任务表 Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author bfhuange
|
||||
* @since 2024-10-28
|
||||
*/
|
||||
public interface BusinessUnzipTaskMapper extends BaseMapper<BusinessUnzipTask> {
|
||||
|
||||
/**
|
||||
* 查询列表
|
||||
* @param page
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
List<BusinessUnzipTaskListVO> selectTaskList(IPage<BusinessUnzipTask> page, @Param("param") BusinessUnzipTaskQueryDTO param);
|
||||
|
||||
/**
|
||||
* 根据业务id查询解压明细
|
||||
* @param bizId
|
||||
* @return
|
||||
*/
|
||||
List<BusinessUnzipDetailListVO> queryBusinessUnzipDetail(@Param("bizId") Long bizId);
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?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="org.springblade.file.mapper.BusinessUnzipTaskMapper">
|
||||
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="BaseResultMap" type="org.springblade.file.pojo.entity.BusinessUnzipTask">
|
||||
<id column="id" property="id" />
|
||||
<result column="biz_id" property="bizId" />
|
||||
<result column="department_id" property="departmentId" />
|
||||
<result column="biz_code" property="bizCode" />
|
||||
<result column="doc_code" property="docCode" />
|
||||
<result column="relate_type" property="relateType" />
|
||||
<result column="relate_id" property="relateId" />
|
||||
<result column="status" property="status" />
|
||||
<result column="tenant_id" property="tenantId" />
|
||||
<result column="create_user" property="createUser" />
|
||||
<result column="create_time" property="createTime" />
|
||||
<result column="update_user" property="updateUser" />
|
||||
<result column="update_time" property="updateTime" />
|
||||
<result column="is_deleted" property="isDeleted" />
|
||||
</resultMap>
|
||||
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
id, biz_id, department_id, biz_code, doc_code, relate_type, relate_id, status, tenant_id, create_user, create_time, update_user, update_time, is_deleted
|
||||
</sql>
|
||||
<select id="selectTaskList" resultType="org.springblade.file.pojo.vo.BusinessUnzipTaskListVO">
|
||||
select
|
||||
but.id,
|
||||
but.biz_id,
|
||||
but.biz_code,
|
||||
but.doc_code,
|
||||
but.relate_type,
|
||||
but.relate_id,
|
||||
but.department_id,
|
||||
but.create_user b_create_user,
|
||||
ft.md5,
|
||||
ft.object_key,
|
||||
ft.attachment_name,
|
||||
ft.suffix,
|
||||
ft.size,
|
||||
ft.chunk_size,
|
||||
ft.chunk_total,
|
||||
ft.current_index,
|
||||
ft.upload_id,
|
||||
ft.status,
|
||||
ft.create_user,
|
||||
ft.create_user_name,
|
||||
ft.create_time,
|
||||
ft.update_user,
|
||||
ft.update_user_name,
|
||||
ft.update_time
|
||||
from blade_business_unzip_task but
|
||||
left join blade_file_task ft on ft.id = but.relate_id
|
||||
where but.is_deleted = 0 and ft.is_deleted = 0
|
||||
<if test="param.bizIds != null and param.bizIds.size() > 0">
|
||||
and but.biz_id in <foreach collection="param.bizIds" item="item" open="(" close=")" separator=",">#{item}</foreach>
|
||||
</if>
|
||||
<if test="param.docCode != null and param.docCode != ''">
|
||||
and but.doc_code like concat('%',#{param.docCode},'%')
|
||||
</if>
|
||||
<if test="param.relateType != null and param.relateType != ''">
|
||||
and but.relate_type = #{param.relateType}
|
||||
</if>
|
||||
<if test="param.currentUserId != null">
|
||||
and but.create_user = #{param.currentUserId}
|
||||
</if>
|
||||
<if test="param.attachmentName != null and param.attachmentName != ''">
|
||||
and ft.attachment_name like concat('%',#{param.attachmentName},'%')
|
||||
</if>
|
||||
<if test="param.status != null and param.status != ''">
|
||||
and ft.status = #{param.status}
|
||||
</if>
|
||||
order by Field(ft.status, 'uploading','failed', 'completed'), but.id desc
|
||||
</select>
|
||||
<select id="queryBusinessUnzipDetail" resultType="org.springblade.file.pojo.vo.BusinessUnzipDetailListVO">
|
||||
select
|
||||
bbud.id,
|
||||
bbud.attachment_id,
|
||||
ba.dir,
|
||||
ba.attachment_name,
|
||||
ba.size,
|
||||
ba.object_key,
|
||||
ba.create_user uploadUserId,
|
||||
ba.create_user_name uploadUserName,
|
||||
ba.create_time,
|
||||
ba.create_user_name
|
||||
from blade_business_unzip_task bbut
|
||||
left join blade_business_unzip_detail bbud on bbud.business_unzip_task_id = bbut.id
|
||||
left join blade_attachment ba on bbud.attachment_id = ba.id
|
||||
where bbut.is_deleted = 0 and bbud.is_deleted = 0 and ba.is_deleted = 0
|
||||
and bbut.biz_id = #{bizId}
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.file.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.springblade.file.pojo.entity.FileTask;
|
||||
|
||||
/**
|
||||
* FileTaskMapper 接口
|
||||
*
|
||||
* @author zhaowei
|
||||
*/
|
||||
public interface FileTaskMapper extends BaseMapper<FileTask> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?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="org.springblade.file.mapper.FileTaskMapper">
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.file.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.springblade.file.pojo.entity.FileTaskPart;
|
||||
|
||||
/**
|
||||
* FileUploadRecordMapper 接口
|
||||
*
|
||||
* @author zhaowei
|
||||
*/
|
||||
public interface FileTaskPartMapper extends BaseMapper<FileTaskPart> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?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="org.springblade.file.mapper.FileTaskPartMapper">
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.file.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.file.pojo.entity.Attachment;
|
||||
import org.springblade.file.pojo.vo.AttachmentDetailVO;
|
||||
import org.springblade.file.pojo.vo.FileVO;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 服务类
|
||||
*
|
||||
* @author zhaowei
|
||||
*/
|
||||
public interface IAttachmentService extends IService<Attachment> {
|
||||
|
||||
/**
|
||||
* 批量上传
|
||||
*
|
||||
* @param files
|
||||
* @param fileName 文件名,如果 files只有1个,且fileName不为空,设置文件名为 fileName
|
||||
* @return
|
||||
*/
|
||||
R<List<Attachment>> batchOssUpload(MultipartFile[] files, String fileName);
|
||||
|
||||
/**
|
||||
* 获取附件,多个附件id用逗号分割
|
||||
* @param id
|
||||
* @return List<Attachment>
|
||||
*/
|
||||
List<Attachment> getAttachment(String id);
|
||||
|
||||
/**
|
||||
* 批量上传解压缩文件并删除文件
|
||||
*
|
||||
* @param files
|
||||
* @param workDir 工作目录,取文件相对于工作目录的相对路径作为前缀
|
||||
* @return
|
||||
*/
|
||||
List<Attachment> batchOssUploadUnzipFilesAndDelete(List<File> files, File workDir);
|
||||
|
||||
/**
|
||||
* 新事务批量保存
|
||||
* @param list
|
||||
*/
|
||||
void batchSaveWithNewTransaction(List<Attachment> list);
|
||||
|
||||
/**
|
||||
* 获取附件详情
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
AttachmentDetailVO getDetail(Long id);
|
||||
|
||||
/**
|
||||
* 解压文件并上传
|
||||
* @param file
|
||||
* @return
|
||||
*/
|
||||
List<Attachment> unzipAndUpload(FileVO file);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package org.springblade.file.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.springblade.file.pojo.dto.BusinessAttachmentAddDTO;
|
||||
import org.springblade.file.pojo.dto.BusinessAttachmentBatchAddDTO;
|
||||
import org.springblade.file.pojo.dto.BusinessAttachmentDeleteDTO;
|
||||
import org.springblade.file.pojo.dto.BusinessAttachmentQueryDTO;
|
||||
import org.springblade.file.pojo.entity.BusinessAttachment;
|
||||
import org.springblade.file.pojo.vo.BusinessAttachmentVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 业务附件信息表 服务类
|
||||
*
|
||||
* @author BladeX
|
||||
* @since 2024-09-04
|
||||
*/
|
||||
public interface IBusinessAttachmentService extends IService<BusinessAttachment> {
|
||||
/**
|
||||
* 保存
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
boolean saveAndRemove(BusinessAttachmentBatchAddDTO param);
|
||||
|
||||
/**
|
||||
* 保存并删除
|
||||
*
|
||||
* @param params
|
||||
* @param deleteParam 业务id不为空,则先删除
|
||||
* @return
|
||||
*/
|
||||
boolean saveAndRemove(List<BusinessAttachmentAddDTO> params, BusinessAttachmentDeleteDTO deleteParam);
|
||||
|
||||
/**
|
||||
* 根据bizId列表删除
|
||||
*
|
||||
* @param bizIds
|
||||
* @return
|
||||
*/
|
||||
boolean removeByBizIds(List<Long> bizIds);
|
||||
|
||||
/**
|
||||
* 查询业务附件列表
|
||||
*
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
List<BusinessAttachmentVO> queryList(BusinessAttachmentQueryDTO param);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.springblade.file.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.springblade.file.pojo.entity.BusinessUnzipDetail;
|
||||
import org.springblade.file.pojo.vo.FileVO;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 业务解压明细表 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author bfhuange
|
||||
* @since 2024-10-30
|
||||
*/
|
||||
public interface IBusinessUnzipDetailService extends IService<BusinessUnzipDetail> {
|
||||
|
||||
/**
|
||||
* 异步解压并保存解压明细
|
||||
* @param unzipTaskId 解压任务id
|
||||
* @param file obs对象存储的key合md5值,md5有可能为空
|
||||
*/
|
||||
void unzipAndSaveAsync(Long unzipTaskId, FileVO file);
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package org.springblade.file.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.springblade.file.pojo.dto.BusinessUnzipTaskAddDTO;
|
||||
import org.springblade.file.pojo.dto.BusinessUnzipTaskQueryDTO;
|
||||
import org.springblade.file.pojo.dto.BusinessUnzipTaskUpdateDTO;
|
||||
import org.springblade.file.pojo.entity.BusinessUnzipTask;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.springblade.file.pojo.vo.BusinessUnzipDetailListVO;
|
||||
import org.springblade.file.pojo.vo.BusinessUnzipTaskListVO;
|
||||
import org.springblade.file.pojo.vo.FileVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 业务解压任务表 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author bfhuange
|
||||
* @since 2024-10-28
|
||||
*/
|
||||
public interface IBusinessUnzipTaskService extends IService<BusinessUnzipTask> {
|
||||
|
||||
/**
|
||||
* 保存业务解压任务
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
boolean save(BusinessUnzipTaskAddDTO param);
|
||||
|
||||
/**
|
||||
* 获取obs对象存储的key
|
||||
* @param task
|
||||
* @return
|
||||
*/
|
||||
String getObjectKey(BusinessUnzipTask task);
|
||||
|
||||
/**
|
||||
* 获取文件对象
|
||||
* @param task
|
||||
* @return
|
||||
*/
|
||||
FileVO getFile(BusinessUnzipTask task);
|
||||
|
||||
/**
|
||||
* 业务解压任务分页查询
|
||||
* @param page
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
IPage<BusinessUnzipTaskListVO> selectPage(IPage<BusinessUnzipTask> page, BusinessUnzipTaskQueryDTO param);
|
||||
|
||||
/**
|
||||
* 业务文件解压任务更新(重新上传)
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
boolean updateReupload(BusinessUnzipTaskUpdateDTO param);
|
||||
|
||||
/**
|
||||
* 根据业务id删除业务文件解压任务
|
||||
* @param bizId
|
||||
* @param cancelFile 是否取消文件,如果取消,上传完成后取消会报错
|
||||
* @return
|
||||
*/
|
||||
boolean cancelBusinessUnzipTask(Long bizId, boolean cancelFile);
|
||||
|
||||
/**
|
||||
* 根据业务id查询解压明细
|
||||
* @param bizId
|
||||
* @return
|
||||
*/
|
||||
List<BusinessUnzipDetailListVO> queryBusinessUnzipDetail(Long bizId);
|
||||
|
||||
/**
|
||||
* 根据业务id列表查询解压任务列表
|
||||
* @param bizIds
|
||||
* @return
|
||||
*/
|
||||
List<BusinessUnzipTaskListVO> getBusinessUnzipTasksByBizIds(List<Long> bizIds);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.springblade.file.service;
|
||||
|
||||
import org.springblade.file.pojo.vo.FileHandleVO;
|
||||
import org.springblade.file.pojo.vo.FileVO;
|
||||
|
||||
/**
|
||||
* 文件处理
|
||||
* @author bfhuange
|
||||
* @date 2024/10/22
|
||||
*/
|
||||
public interface IFileHandleService {
|
||||
|
||||
/**
|
||||
* 下载文件并解压
|
||||
*
|
||||
* @param file obs对象存储的key合md5,md5可能为空
|
||||
* @return
|
||||
*/
|
||||
FileHandleVO downloadAndUnzip(FileVO file);
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package org.springblade.file.service;
|
||||
|
||||
import org.springblade.file.pojo.vo.PartEtagVO;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 文件接口
|
||||
* @author bfhuange
|
||||
* @date 2024/10/21
|
||||
*/
|
||||
public interface IFileService {
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
* @param objectKey 文件key
|
||||
* @param is 输入流
|
||||
*/
|
||||
void uploadFile(String objectKey, InputStream is);
|
||||
|
||||
/**
|
||||
* 上传临时文件
|
||||
* @param objectKey 文件key
|
||||
* @param is 输入流
|
||||
* @param expires 过期时间,单位天,过期后自动删除
|
||||
*/
|
||||
String uploadTempFile(String objectKey, InputStream is, Integer expires);
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
* @param objectKey 文件key
|
||||
* @param file 文件
|
||||
*/
|
||||
void uploadFile(String objectKey, File file);
|
||||
|
||||
/**
|
||||
* 初始化分片上传
|
||||
*
|
||||
* @param objectKey 文件key
|
||||
* @return 上传任务id
|
||||
*/
|
||||
String initiateMultipartUpload(String objectKey);
|
||||
|
||||
/**
|
||||
* 分段上传
|
||||
* @param objectKey 文件key
|
||||
* @param uploadId 上传任务id
|
||||
* @param file 文件
|
||||
* @param partNumber 分段编号
|
||||
* @param offset 偏移位
|
||||
* @param partSize 分段大小
|
||||
* @return
|
||||
*/
|
||||
PartEtagVO uploadPart(String objectKey, String uploadId, File file, int partNumber, Long offset, Long partSize);
|
||||
|
||||
/**
|
||||
* 完成分段上传任务
|
||||
* @param objectKey 文件key
|
||||
* @param uploadId 上传任务id
|
||||
* @param partEtagVOS 要合并的分段
|
||||
*/
|
||||
void completeMultipartUpload(String objectKey, String uploadId, List<PartEtagVO> partEtagVOS);
|
||||
|
||||
/**
|
||||
* 取消分段上传任务
|
||||
* @param objectKey
|
||||
* @param uploadId
|
||||
*/
|
||||
void cancelMultipartUpload(String objectKey, String uploadId);
|
||||
|
||||
/**
|
||||
* 获取文件链接
|
||||
* @param objectKey 文件key
|
||||
* @param expires 过期时间,单位秒
|
||||
* @return
|
||||
*/
|
||||
String getFileUrl(String objectKey, Long expires);
|
||||
|
||||
/**
|
||||
* 获取文件链接
|
||||
* @param objectKey 文件key
|
||||
* @param fileName 文件名
|
||||
* @param expires 过期时间,单位秒
|
||||
* @return
|
||||
*/
|
||||
String getFileUrl(String objectKey, String fileName, Long expires);
|
||||
|
||||
/**
|
||||
* 获取文件上传链接
|
||||
*
|
||||
* @param objectKey 文件key
|
||||
* @param uploadId
|
||||
* @param partNumber
|
||||
* @param expires 过期时间,单位秒
|
||||
* @return
|
||||
*/
|
||||
String getUploadPartUrl(String objectKey, String uploadId, Integer partNumber, Long expires);
|
||||
|
||||
/**
|
||||
* 获取文件链接批量
|
||||
* @param objectKeys
|
||||
* @param expires 过期时间
|
||||
* @return
|
||||
*/
|
||||
List<String> getFileUrls(List<String> objectKeys, Long expires);
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
* @param objectKey
|
||||
* @param downloadFile 下载路径
|
||||
*/
|
||||
void downloadFile(String objectKey, String downloadFile);
|
||||
|
||||
/**
|
||||
* 获取文件md5
|
||||
* @param file
|
||||
* @return
|
||||
*/
|
||||
String getFileMd5(File file);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.file.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.springblade.file.pojo.entity.FileTaskPart;
|
||||
|
||||
/**
|
||||
* 服务类
|
||||
*
|
||||
* @author zhaowei
|
||||
*/
|
||||
public interface IFileTaskPartService extends IService<FileTaskPart> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.file.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.springblade.file.pojo.dto.FileTaskCreateDTO;
|
||||
import org.springblade.file.pojo.dto.FileTaskUpdateDTO;
|
||||
import org.springblade.file.pojo.entity.FileTask;
|
||||
import org.springblade.file.pojo.vo.FileTaskUpdateVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 服务类
|
||||
*
|
||||
* @author zhaowei
|
||||
*/
|
||||
public interface IFileTaskService extends IService<FileTask> {
|
||||
|
||||
/**
|
||||
* 根据文件md5值查询最新一条上传任务记录
|
||||
*
|
||||
* @param id
|
||||
* @param md5
|
||||
* @return
|
||||
*/
|
||||
FileTaskUpdateVO queryFileTask(Long id, String md5);
|
||||
|
||||
/**
|
||||
* 创建文件任务
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
Long createFileTask(FileTaskCreateDTO param);
|
||||
|
||||
/**
|
||||
* 更新文件任务进度
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
FileTaskUpdateVO updateFileTask(FileTaskUpdateDTO param);
|
||||
|
||||
/**
|
||||
* 根据文件任务id获取上传链接
|
||||
*
|
||||
* @param id
|
||||
* @param partNumber
|
||||
* @return
|
||||
*/
|
||||
String getUploadUrl(Long id, Integer partNumber);
|
||||
|
||||
/**
|
||||
* 取消文件任务
|
||||
* @param taskIds
|
||||
*/
|
||||
void cancelBatchByIds(List<Long> taskIds);
|
||||
|
||||
/**
|
||||
* 更新文件任务状态为失败
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
FileTaskUpdateVO updateFailed(Long id);
|
||||
|
||||
/**
|
||||
* 根据id列表更新文件任务状态为暂停
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
boolean updatePaused(List<Long> param);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.springblade.file.service;
|
||||
|
||||
import org.springblade.file.pojo.dto.FileCertificateBatchRecognitionDTO;
|
||||
import org.springblade.thirdparty.ocr.pojo.vo.TransportCertificateVO;
|
||||
|
||||
/**
|
||||
* ocr转换
|
||||
* @author bfhuange
|
||||
* @since 2025/3/13
|
||||
*/
|
||||
public interface IOCRConvertService {
|
||||
|
||||
/**
|
||||
* 凭证批量识别,不分类
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
TransportCertificateVO recognitionTransportCertificate(FileCertificateBatchRecognitionDTO param);
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.file.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.utils.CollectionUtil;
|
||||
import org.springblade.core.tool.utils.SpringUtil;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.file.config.ObsProperties;
|
||||
import org.springblade.file.convert.BusinessAttachmentConvert;
|
||||
import org.springblade.file.exception.UnzipFailException;
|
||||
import org.springblade.file.mapper.AttachmentMapper;
|
||||
import org.springblade.file.pojo.entity.Attachment;
|
||||
import org.springblade.file.pojo.vo.AttachmentDetailVO;
|
||||
import org.springblade.file.pojo.vo.FileHandleVO;
|
||||
import org.springblade.file.pojo.vo.FileVO;
|
||||
import org.springblade.file.service.IAttachmentService;
|
||||
import org.springblade.file.service.IFileHandleService;
|
||||
import org.springblade.file.service.IFileService;
|
||||
import org.springblade.file.util.ObjectKeyUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* 服务实现类
|
||||
* @author zhaowei
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class AttachmentServiceImpl extends ServiceImpl<AttachmentMapper, Attachment> implements IAttachmentService {
|
||||
|
||||
private final IFileService fileService;
|
||||
private final BusinessAttachmentConvert convert;
|
||||
private final ObsProperties obsProperties;
|
||||
private final IFileHandleService fileHandleService;
|
||||
private IAttachmentService self;
|
||||
|
||||
@Override
|
||||
public R<List<Attachment>> batchOssUpload(MultipartFile[] files, String overWriteFileName){
|
||||
try {
|
||||
List<Attachment> attachmentList = new ArrayList<>();
|
||||
// 文件数量为1个,且重写的文件名不为空,使用重写的文件名,给uniapp上传使用,uniapp上传的文件名不是原始文件名
|
||||
boolean useOverWriteFileName = files.length == 1 && StringUtil.isNotBlank(overWriteFileName);
|
||||
for(MultipartFile file : files){
|
||||
String fileName = useOverWriteFileName ? overWriteFileName : file.getOriginalFilename();
|
||||
String objectkey = ObjectKeyUtils.generateObjectKey(fileName);
|
||||
fileService.uploadFile(objectkey, file.getInputStream());
|
||||
// 插入附件数据
|
||||
Attachment attachment = new Attachment();
|
||||
Long id = IdWorker.getId();
|
||||
attachment.setId(id);
|
||||
attachment.setAttachmentName(fileName);
|
||||
attachment.setSize(file.getSize());
|
||||
attachment.setObjectKey(objectkey);
|
||||
attachmentList.add(attachment);
|
||||
}
|
||||
this.saveBatch(attachmentList);
|
||||
log.info("OBS上传 successfully");
|
||||
return R.data(attachmentList,"OBS上传 successfully");
|
||||
} catch (Exception e) {
|
||||
log.error("OBS上传 failed,Exception", e);
|
||||
}
|
||||
return R.fail("OBS上传 failed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Attachment> getAttachment(String id) {
|
||||
return this.list(new LambdaQueryWrapper<Attachment>()
|
||||
.in(Attachment::getId, Arrays.stream(id.split(",")).toList())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Attachment> batchOssUploadUnzipFilesAndDelete(List<File> files, File workDir) {
|
||||
if (CollectionUtil.isEmpty(files)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<Attachment> attachmentList = new ArrayList<>();
|
||||
AtomicInteger index = new AtomicInteger(1);
|
||||
try {
|
||||
log.info("上传文件总数:{}", files.size());
|
||||
for(File file : files){
|
||||
String fileName = file.getName();
|
||||
// 压缩文件所在压缩包父目录相对路径
|
||||
String dir = this.getDir(workDir, file);
|
||||
// 获取上传的目录前缀
|
||||
String prefixDir = getUploadPrefix();
|
||||
String objectkey = prefixDir + ObjectKeyUtils.generateObjectKey(fileName);
|
||||
fileService.uploadFile(objectkey, file);
|
||||
int completeFiles = index.getAndIncrement();
|
||||
if (completeFiles % 10 == 0) {
|
||||
log.info("上传文件总进度:{}/{}", completeFiles, files.size());
|
||||
}
|
||||
// 插入附件数据
|
||||
Attachment attachment = new Attachment();
|
||||
Long id = IdWorker.getId();
|
||||
attachment.setId(id);
|
||||
attachment.setDir(dir);
|
||||
attachment.setAttachmentName(fileName);
|
||||
attachment.setSize(file.length());
|
||||
attachment.setObjectKey(objectkey);
|
||||
attachmentList.add(attachment);
|
||||
}
|
||||
log.info("上传文件完成:{}", files.size());
|
||||
// 新事务批量保存
|
||||
this.getSelf().batchSaveWithNewTransaction(attachmentList);
|
||||
log.info("OBS上传 successfully");
|
||||
} catch (Exception e) {
|
||||
log.error("OBS上传失败", e);
|
||||
throw new ServiceException("OBS上传失败");
|
||||
}
|
||||
// 返回新增的附件列表
|
||||
return attachmentList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传文件目录前缀
|
||||
* @return
|
||||
*/
|
||||
private String getUploadPrefix() {
|
||||
// 上传路径前缀 = 配置的解压上传路径 + /
|
||||
return obsProperties.getUnzipUploadDir() + StringPool.SLASH;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取压缩文件所在压缩包父目录的相对路径,结尾不带/
|
||||
* @param workDir 工作目录,解压的根目录
|
||||
* @param file 解压后的文件
|
||||
* @return
|
||||
*/
|
||||
private String getDir(File workDir, File file) {
|
||||
if (workDir == null) {
|
||||
return StringPool.EMPTY;
|
||||
}
|
||||
Path workDirPath = workDir.toPath();
|
||||
Path filePath = file.toPath();
|
||||
return workDirPath.relativize(filePath).toString()
|
||||
// 去掉文件名以及文件所在父目录的后缀/或\
|
||||
.replace(File.separator + file.getName(), "")
|
||||
// 将文件分隔符替换为/,windows下是\,需要处理
|
||||
.replace(File.separator, StringPool.SLASH);
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
|
||||
@Override
|
||||
public void batchSaveWithNewTransaction(List<Attachment> list) {
|
||||
this.saveBatch(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttachmentDetailVO getDetail(Long id) {
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
Attachment attachment = this.getById(id);
|
||||
if (attachment == null) {
|
||||
return null;
|
||||
}
|
||||
AttachmentDetailVO detailVO = convert.entity2vo(attachment);
|
||||
// 查询下载链接
|
||||
String fileUrl = fileService.getFileUrl(attachment.getObjectKey(), null);
|
||||
detailVO.setUrl(fileUrl);
|
||||
return detailVO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Attachment> unzipAndUpload(FileVO file) {
|
||||
// 1. 下载并解压文件
|
||||
try {
|
||||
FileHandleVO fileHandleVO = fileHandleService.downloadAndUnzip(file);
|
||||
List<File> unzippedFiles = fileHandleVO.getUnzippedFiles();
|
||||
if (CollectionUtil.isEmpty(unzippedFiles)) {
|
||||
log.warn("下载解压文件为空 objectKey:{}", file.getObjectKey());
|
||||
throw new UnzipFailException("解压文件为空", fileHandleVO.getUnzippedDir());
|
||||
}
|
||||
File unzippedDir = fileHandleVO.getUnzippedDir();
|
||||
// 2. 上传文件
|
||||
long start = System.currentTimeMillis();
|
||||
try {
|
||||
List<Attachment> attachments = getSelf().batchOssUploadUnzipFilesAndDelete(unzippedFiles, unzippedDir);
|
||||
if (CollectionUtil.isEmpty(attachments)) {
|
||||
log.warn("上传解压文件为空 objectKey:{}", file.getObjectKey());
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 删除下载的压缩文件
|
||||
fileHandleVO.getDownloadFile().delete();
|
||||
// 删除解压后的目录
|
||||
FileUtils.deleteQuietly(unzippedDir);
|
||||
return attachments;
|
||||
} finally {
|
||||
long end = System.currentTimeMillis();
|
||||
log.info("文件:{} 解压后上传完成 耗时:{}", file.getObjectKey(), end - start);
|
||||
}
|
||||
} catch (UnzipFailException e) {
|
||||
// 文件解压失败,删除解压后的文件
|
||||
File unzipDir = e.getUnzipDir();
|
||||
// 删除解压后的目录
|
||||
FileUtils.deleteQuietly(unzipDir);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自己
|
||||
* @return
|
||||
*/
|
||||
private IAttachmentService getSelf() {
|
||||
if (self == null) {
|
||||
self = SpringUtil.getBean(IAttachmentService.class);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package org.springblade.file.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.secure.utils.AuthUtil;
|
||||
import org.springblade.core.tool.constant.BladeConstant;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.file.convert.BusinessAttachmentConvert;
|
||||
import org.springblade.file.mapper.BusinessAttachmentMapper;
|
||||
import org.springblade.file.pojo.dto.BusinessAttachmentAddDTO;
|
||||
import org.springblade.file.pojo.dto.BusinessAttachmentBatchAddDTO;
|
||||
import org.springblade.file.pojo.dto.BusinessAttachmentDeleteDTO;
|
||||
import org.springblade.file.pojo.dto.BusinessAttachmentQueryDTO;
|
||||
import org.springblade.file.pojo.entity.BusinessAttachment;
|
||||
import org.springblade.file.pojo.vo.BusinessAttachmentVO;
|
||||
import org.springblade.file.service.IBusinessAttachmentService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 业务附件信息表 服务实现类
|
||||
*
|
||||
* @author BladeX
|
||||
* @since 2024-09-04
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class BusinessAttachmentServiceImpl extends ServiceImpl<BusinessAttachmentMapper, BusinessAttachment> implements IBusinessAttachmentService {
|
||||
|
||||
private final BusinessAttachmentConvert convert;
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public boolean saveAndRemove(BusinessAttachmentBatchAddDTO param) {
|
||||
if (param == null) {
|
||||
log.warn("批量保存业务附件,参数为空");
|
||||
return false;
|
||||
}
|
||||
if (param.isRemove() && param.getBizId() != null) {
|
||||
// 先删除
|
||||
this.removeByBizIds(List.of(param.getBizId()));
|
||||
}
|
||||
List<BusinessAttachment> addList = convert.dto2entities(param);
|
||||
if (CollectionUtil.isNotEmpty(addList)) {
|
||||
return this.saveBatch(addList);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public boolean saveAndRemove(List<BusinessAttachmentAddDTO> params, BusinessAttachmentDeleteDTO deleteParam) {
|
||||
boolean hasBizIds = deleteParam != null && deleteParam.isDelete() && CollectionUtil.isNotEmpty(deleteParam.getBizIds());
|
||||
if (hasBizIds) {
|
||||
// bizIds不为空,先删除
|
||||
this.removeByBizIdsAndCodes(deleteParam.getBizIds(), deleteParam.getBizCodes());
|
||||
}
|
||||
if (CollectionUtil.isEmpty(params)) {
|
||||
log.warn("保存业务附件,参数为空");
|
||||
return true;
|
||||
}
|
||||
if (!hasBizIds && (deleteParam == null || deleteParam.isDelete())) {
|
||||
// bizIds为空,从参数提取bizIds,再删除
|
||||
// 删除参数为空,默认删除,删除参数中delete为true默认删除,false不删除
|
||||
List<Long> bizIds = params.stream()
|
||||
.map(BusinessAttachmentAddDTO::getBizId)
|
||||
.distinct()
|
||||
.toList();
|
||||
this.removeByBizIds(bizIds);
|
||||
}
|
||||
|
||||
List<BusinessAttachment> businessAttachments = convert.dtos2entities(params);
|
||||
|
||||
// 按照业务id分组排序
|
||||
Map<Long, List<BusinessAttachment>> bizIdAttachmentsMap = businessAttachments.stream()
|
||||
.collect(Collectors.groupingBy(BusinessAttachment::getBizId));
|
||||
String tenantId = AuthUtil.getTenantId();
|
||||
for (Map.Entry<Long, List<BusinessAttachment>> entry : bizIdAttachmentsMap.entrySet()) {
|
||||
int i = 1;
|
||||
for (BusinessAttachment businessAttachment : entry.getValue()) {
|
||||
businessAttachment.setId(IdWorker.getId());
|
||||
// 处理排序
|
||||
businessAttachment.setSort(i++);
|
||||
if (StringUtil.isBlank(tenantId)) {
|
||||
businessAttachment.setTenantId(BladeConstant.ADMIN_TENANT_ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.saveBatch(businessAttachments);
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public boolean removeByBizIds(List<Long> bizIds) {
|
||||
return this.removeByBizIdsAndCodes(bizIds, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BusinessAttachmentVO> queryList(BusinessAttachmentQueryDTO param) {
|
||||
return baseMapper.queryList(param);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除
|
||||
* @param bizIds 业务id列表,为空,不删除
|
||||
* @param bizCodes 业务编码列表,为空,删除所有
|
||||
* @return
|
||||
*/
|
||||
private boolean removeByBizIdsAndCodes(List<Long> bizIds, List<String> bizCodes) {
|
||||
if (CollectionUtil.isEmpty(bizIds)) {
|
||||
log.warn("删除附件,参数为空");
|
||||
return false;
|
||||
}
|
||||
List<Long> ids = this.list(Wrappers.<BusinessAttachment>lambdaQuery()
|
||||
.in(BusinessAttachment::getBizId, bizIds)
|
||||
// 业务编码列表不为空,添加业务编码查询条件
|
||||
.in(CollectionUtil.isNotEmpty(bizCodes), BusinessAttachment::getBizCode, bizCodes)
|
||||
).stream()
|
||||
.map(BusinessAttachment::getId)
|
||||
.toList();
|
||||
if (CollectionUtil.isNotEmpty(ids)) {
|
||||
return this.removeBatchByIds(ids);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package org.springblade.file.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.tool.utils.CollectionUtil;
|
||||
import org.springblade.file.exception.UnzipFailException;
|
||||
import org.springblade.file.listener.UnzipCompletedEvent;
|
||||
import org.springblade.file.listener.UnzipFailEvent;
|
||||
import org.springblade.file.mapper.BusinessUnzipDetailMapper;
|
||||
import org.springblade.file.pojo.entity.Attachment;
|
||||
import org.springblade.file.pojo.entity.BusinessUnzipDetail;
|
||||
import org.springblade.file.pojo.vo.FileVO;
|
||||
import org.springblade.file.service.IAttachmentService;
|
||||
import org.springblade.file.service.IBusinessUnzipDetailService;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author bfhuange
|
||||
* @date 2024/10/30
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Slf4j
|
||||
@Service
|
||||
public class BusinessUnzipDetailServiceImpl extends ServiceImpl<BusinessUnzipDetailMapper, BusinessUnzipDetail> implements IBusinessUnzipDetailService {
|
||||
|
||||
private final IAttachmentService attachmentService;
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
@Async
|
||||
@Override
|
||||
public void unzipAndSaveAsync(Long unzipTaskId, FileVO file) {
|
||||
log.info("解压文件并上传开始 unzipTaskId: {} file: {}", unzipTaskId, JSON.toJSONString(file));
|
||||
long start = System.currentTimeMillis();
|
||||
try {
|
||||
// 1. 上传文件并解压
|
||||
List<Attachment> attachments = attachmentService.unzipAndUpload(file);
|
||||
if (CollectionUtil.isEmpty(attachments)) {
|
||||
log.warn("上传解压文件为空 unzipTaskId:{} objectKey:{}", unzipTaskId, file.getObjectKey());
|
||||
return;
|
||||
}
|
||||
// 2. 保存压缩文件和解压文件关系
|
||||
List<BusinessUnzipDetail> addParams = attachments.stream()
|
||||
.map(attachment -> {
|
||||
BusinessUnzipDetail addParam = new BusinessUnzipDetail();
|
||||
addParam.setBusinessUnzipTaskId(unzipTaskId);
|
||||
addParam.setAttachmentId(attachment.getId());
|
||||
return addParam;
|
||||
}).toList();
|
||||
if (CollectionUtil.isNotEmpty(addParams)) {
|
||||
this.saveBatch(addParams);
|
||||
}
|
||||
// 3. 发布解压完成事件
|
||||
applicationContext.publishEvent(new UnzipCompletedEvent(unzipTaskId));
|
||||
} catch (UnzipFailException e) {
|
||||
log.error("解压文件异常", e);
|
||||
// 发布解压失败事件
|
||||
applicationContext.publishEvent(new UnzipFailEvent(unzipTaskId));
|
||||
} catch (Exception e) {
|
||||
log.error("解压文件并上传异常", e);
|
||||
} finally {
|
||||
long end = System.currentTimeMillis();
|
||||
log.info("解压文件并上传 完成 unzipTaskId: {} objectKey: {} 耗时:{}", unzipTaskId, file.getObjectKey(), end - start);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
package org.springblade.file.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.google.common.collect.Lists;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.common.constant.DictTypeEnum;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.log.utils.AssertUtils;
|
||||
import org.springblade.core.tool.utils.CollectionUtil;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.file.convert.FileTaskConvert;
|
||||
import org.springblade.file.listener.UnzipStartEvent;
|
||||
import org.springblade.file.pojo.dto.BusinessUnzipTaskAddDTO;
|
||||
import org.springblade.file.pojo.dto.BusinessUnzipTaskQueryDTO;
|
||||
import org.springblade.file.pojo.dto.BusinessUnzipTaskUpdateDTO;
|
||||
import org.springblade.file.pojo.entity.Attachment;
|
||||
import org.springblade.file.pojo.entity.BusinessUnzipTask;
|
||||
import org.springblade.file.mapper.BusinessUnzipTaskMapper;
|
||||
import org.springblade.file.pojo.entity.FileTask;
|
||||
import org.springblade.file.pojo.enums.FileTaskStatus;
|
||||
import org.springblade.file.pojo.enums.UnzipTask;
|
||||
import org.springblade.file.pojo.vo.BusinessUnzipDetailListVO;
|
||||
import org.springblade.file.pojo.vo.BusinessUnzipTaskListVO;
|
||||
import org.springblade.file.pojo.vo.FileVO;
|
||||
import org.springblade.file.service.IAttachmentService;
|
||||
import org.springblade.file.service.IBusinessUnzipDetailService;
|
||||
import org.springblade.file.service.IBusinessUnzipTaskService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springblade.file.service.IFileTaskService;
|
||||
import org.springblade.system.cache.DictCache;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 业务解压任务表 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author bfhuange
|
||||
* @since 2024-10-28
|
||||
*/
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
@Service
|
||||
public class BusinessUnzipTaskServiceImpl extends ServiceImpl<BusinessUnzipTaskMapper, BusinessUnzipTask> implements IBusinessUnzipTaskService {
|
||||
|
||||
private final FileTaskConvert convert;
|
||||
private final IFileTaskService fileTaskService;
|
||||
private final IBusinessUnzipDetailService detailService;
|
||||
private final IAttachmentService attachmentService;
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public boolean save(BusinessUnzipTaskAddDTO param) {
|
||||
BusinessUnzipTask addParam = convert.dto2entity(param);
|
||||
String status = getStatus(param.getRelateType(), param.getRelateId());
|
||||
addParam.setStatus(status);
|
||||
boolean result = this.save(addParam);
|
||||
if (result && UnzipTask.Status.UPLOADED.getCode().equals(status)) {
|
||||
// 解压任务状态是上传完成,发布解压开始事件
|
||||
applicationContext.publishEvent(new UnzipStartEvent(addParam));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getObjectKey(BusinessUnzipTask task) {
|
||||
return this.getFile(task).getObjectKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVO getFile(BusinessUnzipTask task) {
|
||||
AssertUtils.notNull(task, "文件任务不存在");
|
||||
String relateType = task.getRelateType();
|
||||
AssertUtils.notBlank(relateType, "文件任务关联类型不能为空");
|
||||
Long relateId = task.getRelateId();
|
||||
AssertUtils.notNull(relateId, "文件任务关联id不能为空");
|
||||
if (UnzipTask.RelateType.FILE_TASK.getCode().equals(relateType)) {
|
||||
// 文件任务关联类型
|
||||
FileTask fileTask = fileTaskService.getById(relateId);
|
||||
AssertUtils.notNull(fileTask, "文件任务不存在");
|
||||
return new FileVO(fileTask.getObjectKey(), fileTask.getMd5());
|
||||
}
|
||||
if (UnzipTask.RelateType.ATTACHMENT.getCode().equals(relateType)) {
|
||||
// 附件关联类型
|
||||
Attachment attachment = attachmentService.getById(relateId);
|
||||
AssertUtils.notNull(attachment, "附件不存在");
|
||||
// 附件暂时没有md5
|
||||
return new FileVO(attachment.getObjectKey(), null);
|
||||
}
|
||||
throw new ServiceException("文件任务关联类型错误");
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<BusinessUnzipTaskListVO> selectPage(IPage<BusinessUnzipTask> page, BusinessUnzipTaskQueryDTO param) {
|
||||
List<BusinessUnzipTaskListVO> list = baseMapper.selectTaskList(page, param);
|
||||
if (CollectionUtil.isNotEmpty(list)) {
|
||||
// 处理状态枚举值名称
|
||||
list.forEach(vo -> {
|
||||
String status = vo.getStatus();
|
||||
if (StringUtil.isNotBlank(status)) {
|
||||
vo.setStatusStr(DictCache.getValue(DictTypeEnum.FILE_TASK_STATUS.getType(), vo.getStatus()));
|
||||
}
|
||||
});
|
||||
}
|
||||
IPage<BusinessUnzipTaskListVO> newPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
|
||||
return newPage.setRecords(list);
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public boolean updateReupload(BusinessUnzipTaskUpdateDTO param) {
|
||||
log.info("修改业务解压任务重新上传参数:{}", JSON.toJSONString(param));
|
||||
if (param == null || (param.getId() == null && param.getBizId() == null)) {
|
||||
throw new ServiceException("参数不能为空");
|
||||
}
|
||||
// 校验业务解压任务
|
||||
BusinessUnzipTask unzipTask = param.getId() != null ? this.getById(param.getId()) : this.getOne(Wrappers.<BusinessUnzipTask>lambdaQuery()
|
||||
.eq(BusinessUnzipTask::getBizId, param.getBizId())
|
||||
.last("limit 1")
|
||||
);
|
||||
AssertUtils.notNull(unzipTask, "业务任务不存在");
|
||||
// 上传中或解压失败才可以重新上传
|
||||
AssertUtils.isTrue(UnzipTask.Status.UPLOADING.getCode().equals(unzipTask.getStatus())
|
||||
|| UnzipTask.Status.UNZIP_FAIL.getCode().equals(unzipTask.getStatus()), "只有上传中或解压失败的任务才可以重新上传");
|
||||
|
||||
if (UnzipTask.Status.UPLOADING.getCode().equals(unzipTask.getStatus())) {
|
||||
// 上传中的才需要取消上传任务,解压失败的无需取消
|
||||
try {
|
||||
// 取消文件任务
|
||||
fileTaskService.cancelBatchByIds(Lists.newArrayList(unzipTask.getRelateId()));
|
||||
} catch (Exception e) {
|
||||
log.error("取消上传异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新业务解压任务
|
||||
BusinessUnzipTask updateParam = new BusinessUnzipTask();
|
||||
updateParam.setId(unzipTask.getId());
|
||||
updateParam.setRelateId(param.getRelateId());
|
||||
String status = getStatus(param.getRelateType(), param.getRelateId());
|
||||
updateParam.setStatus(status);
|
||||
boolean result = this.updateById(updateParam);
|
||||
if (result && UnzipTask.Status.UPLOADED.getCode().equals(status)) {
|
||||
// 重新查询,防止解压开始事件中查询时事务未提交查询不到最新的数据
|
||||
unzipTask = this.getById(unzipTask.getId());
|
||||
// 解压任务状态是上传完成,发布解压开始事件
|
||||
applicationContext.publishEvent(new UnzipStartEvent(unzipTask));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public boolean cancelBusinessUnzipTask(Long bizId, boolean cancelFile) {
|
||||
List<BusinessUnzipTask> list = this.list(Wrappers.<BusinessUnzipTask>lambdaQuery()
|
||||
.eq(BusinessUnzipTask::getBizId, bizId)
|
||||
);
|
||||
if (CollectionUtil.isEmpty(list)) {
|
||||
return true;
|
||||
}
|
||||
if (cancelFile) {
|
||||
boolean hasUpload = list.stream()
|
||||
.anyMatch(task -> !UnzipTask.Status.UPLOADING.getCode().equals(task.getStatus()));
|
||||
AssertUtils.isFalse(hasUpload, "当前任务已上传完成,不能取消");
|
||||
// 取消文件任务
|
||||
List<Long> taskIds = list.stream()
|
||||
.filter(task -> UnzipTask.RelateType.FILE_TASK.getCode().equals(task.getRelateType()))
|
||||
.map(BusinessUnzipTask::getRelateId)
|
||||
.toList();
|
||||
fileTaskService.cancelBatchByIds(taskIds);
|
||||
}
|
||||
// 删除业务文件解压任务
|
||||
List<Long> unzipTaskIds = list.stream()
|
||||
.map(BusinessUnzipTask::getId)
|
||||
.toList();
|
||||
this.removeBatchByIds(unzipTaskIds);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BusinessUnzipDetailListVO> queryBusinessUnzipDetail(Long bizId) {
|
||||
return baseMapper.queryBusinessUnzipDetail(bizId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BusinessUnzipTaskListVO> getBusinessUnzipTasksByBizIds(List<Long> bizIds) {
|
||||
if (CollectionUtil.isEmpty(bizIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
BusinessUnzipTaskQueryDTO param = new BusinessUnzipTaskQueryDTO();
|
||||
param.setBizIds(bizIds);
|
||||
return baseMapper.selectTaskList(null, param);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取解压任务状态
|
||||
* @param relateType 关联类型
|
||||
* @param relateId 关联id
|
||||
* @return
|
||||
*/
|
||||
private String getStatus(String relateType, Long relateId) {
|
||||
if (UnzipTask.RelateType.ATTACHMENT.getCode().equals(relateType)) {
|
||||
// 关联类型是附件,状态直接就是上传完成
|
||||
return UnzipTask.Status.UPLOADED.getCode();
|
||||
}
|
||||
if (UnzipTask.RelateType.FILE_TASK.getCode().equals(relateType)) {
|
||||
// 关联类型是文件任务,查询文件任务状态
|
||||
FileTask fileTask = fileTaskService.getById(relateId);
|
||||
AssertUtils.notNull(fileTask, "文件任务不存在");
|
||||
if (FileTaskStatus.COMPLETED.getCode().equals(fileTask.getStatus())) {
|
||||
// 文件任务上传完成,解压任务设置为上传完成
|
||||
return UnzipTask.Status.UPLOADED.getCode();
|
||||
}
|
||||
// 否则解压任务设置为上传中状态
|
||||
return UnzipTask.Status.UPLOADING.getCode();
|
||||
}
|
||||
throw new ServiceException("关联类型错误");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package org.springblade.file.service.impl;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.file.config.ObsProperties;
|
||||
import org.springblade.file.exception.UnzipFailException;
|
||||
import org.springblade.file.pojo.vo.FileHandleVO;
|
||||
import org.springblade.file.pojo.vo.FileVO;
|
||||
import org.springblade.file.service.IFileHandleService;
|
||||
import org.springblade.file.service.IFileService;
|
||||
import org.springblade.file.util.UnzipUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author bfhuange
|
||||
* @date 2024/10/22
|
||||
*/
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
@Service
|
||||
public class FileHandleServiceImpl implements IFileHandleService {
|
||||
|
||||
private final IFileService fileService;
|
||||
private final ObsProperties obsProperties;
|
||||
|
||||
@Override
|
||||
public FileHandleVO downloadAndUnzip(FileVO file) {
|
||||
File downloadDir = new File(obsProperties.getTmpDownloadDir());
|
||||
if (!downloadDir.exists()) {
|
||||
downloadDir.mkdirs();
|
||||
}
|
||||
File downloadFile = new File(downloadDir, file.getObjectKey());
|
||||
String downloadFilePath = downloadFile.getAbsolutePath();
|
||||
File unzipDir = null;
|
||||
long unzipStart = System.currentTimeMillis();
|
||||
try {
|
||||
// 下载文件
|
||||
downloadFile(file, downloadFile, downloadFilePath);
|
||||
// 2. 解压文件目录
|
||||
unzipDir = new File(obsProperties.getTmpUnzipDir(), file.getObjectKey());
|
||||
if (!unzipDir.exists()) {
|
||||
unzipDir.mkdirs();
|
||||
}
|
||||
long start = System.currentTimeMillis();
|
||||
List<File> files = UnzipUtil.unzipOrUnrar(downloadFilePath, unzipDir);
|
||||
long end = System.currentTimeMillis();
|
||||
log.info("文件:{} 解压完成 共{}个 耗时:{}", file.getObjectKey(), files.size(), end - start);
|
||||
FileHandleVO fileHandleVO = new FileHandleVO();
|
||||
fileHandleVO.setDownloadFile(downloadFile);
|
||||
fileHandleVO.setUnzippedDir(unzipDir);
|
||||
fileHandleVO.setUnzippedFiles(files);
|
||||
return fileHandleVO;
|
||||
} catch (Exception e) {
|
||||
log.error("文件解压失败异常", e);
|
||||
throw new UnzipFailException(e, unzipDir);
|
||||
} finally {
|
||||
long unzipEnd = System.currentTimeMillis();
|
||||
log.info("文件:{} 下载并解压完成 耗时:{}", file.getObjectKey(), unzipEnd - unzipStart);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
*
|
||||
* @param file
|
||||
* @param downloadFile
|
||||
* @param downloadFilePath
|
||||
*/
|
||||
private void downloadFile(FileVO file, File downloadFile, String downloadFilePath) {
|
||||
if (downloadFile.exists() && file.getMd5() != null) {
|
||||
// 下载文件存在且md5不为空,可能是重新解压
|
||||
String fileMd5 = fileService.getFileMd5(downloadFile);
|
||||
if (StringUtil.equals(fileMd5, file.getMd5())) {
|
||||
log.info("文件存在且md5一致,无需重新下载 文件路径:{}", downloadFile.getAbsolutePath());
|
||||
return;
|
||||
}
|
||||
}
|
||||
long downloadStart = System.currentTimeMillis();
|
||||
try {
|
||||
// 1. 下载文件
|
||||
fileService.downloadFile(file.getObjectKey(), downloadFilePath);
|
||||
} finally {
|
||||
long downloadEnd = System.currentTimeMillis();
|
||||
log.info("文件:{} 下载完成 耗时:{}", file.getObjectKey(), downloadEnd - downloadStart);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.file.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.file.mapper.FileTaskPartMapper;
|
||||
import org.springblade.file.pojo.entity.FileTaskPart;
|
||||
import org.springblade.file.service.IFileTaskPartService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 服务实现类
|
||||
*
|
||||
* @author zhaowei
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public class FileTaskPartServiceImpl extends ServiceImpl<FileTaskPartMapper, FileTaskPart> implements IFileTaskPartService {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,360 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.file.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.log.utils.AssertUtils;
|
||||
import org.springblade.core.tool.utils.CollectionUtil;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.file.config.ObsProperties;
|
||||
import org.springblade.file.convert.FileTaskConvert;
|
||||
import org.springblade.file.listener.FileUploadedEvent;
|
||||
import org.springblade.file.mapper.BusinessUnzipTaskMapper;
|
||||
import org.springblade.file.mapper.FileTaskMapper;
|
||||
import org.springblade.file.pojo.dto.FileTaskCreateDTO;
|
||||
import org.springblade.file.pojo.dto.FileTaskUpdateDTO;
|
||||
import org.springblade.file.pojo.entity.BusinessUnzipTask;
|
||||
import org.springblade.file.pojo.entity.FileTask;
|
||||
import org.springblade.file.pojo.entity.FileTaskPart;
|
||||
import org.springblade.file.pojo.enums.FileTaskStatus;
|
||||
import org.springblade.file.pojo.vo.FileTaskUpdateVO;
|
||||
import org.springblade.file.pojo.vo.PartEtagVO;
|
||||
import org.springblade.file.service.IFileService;
|
||||
import org.springblade.file.service.IFileTaskPartService;
|
||||
import org.springblade.file.service.IFileTaskService;
|
||||
import org.springblade.file.util.ObjectKeyUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 服务实现类
|
||||
*
|
||||
* @author zhaowei
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public class FileTaskServiceImpl extends ServiceImpl<FileTaskMapper, FileTask> implements IFileTaskService {
|
||||
|
||||
private final FileTaskConvert convert;
|
||||
private final IFileTaskPartService partService;
|
||||
private final IFileService fileService;
|
||||
private final BusinessUnzipTaskMapper unzipTaskMapper;
|
||||
private final ObsProperties obsProperties;
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public FileTaskUpdateVO queryFileTask(Long id, String md5) {
|
||||
if (id == null && StringUtil.isBlank(md5)) {
|
||||
throw new ServiceException("id和md5不能都为空");
|
||||
}
|
||||
// 根据md5按创建时间倒序取一条
|
||||
FileTask fileTask = this.getOne(Wrappers.<FileTask>lambdaQuery()
|
||||
.eq(id != null, FileTask::getId, id)
|
||||
.eq(StringUtil.isNotBlank(md5), FileTask::getMd5, md5)
|
||||
.orderByDesc(FileTask::getCreateTime)
|
||||
.last("limit 1")
|
||||
);
|
||||
if (fileTask == null) {
|
||||
return null;
|
||||
}
|
||||
return getFileTaskUpdateVO(fileTask);
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public Long createFileTask(FileTaskCreateDTO param) {
|
||||
if (!param.isForce()) {
|
||||
// 不是强制创建新任务,文件任务存在就报错
|
||||
FileTask fileTask = this.getOne(Wrappers.<FileTask>lambdaQuery()
|
||||
.eq(FileTask::getMd5, param.getMd5())
|
||||
.last("limit 1")
|
||||
);
|
||||
AssertUtils.isNull(fileTask, "文件任务已存在");
|
||||
}
|
||||
|
||||
// 1. 初始化上传任务
|
||||
String fileName = param.getAttachmentName();
|
||||
String fileSuffix = fileName.substring(fileName.lastIndexOf("."));
|
||||
String objectKey = ObjectKeyUtils.generateObjectKey(obsProperties.getZipUploadDir(), fileName);
|
||||
String uploadId = fileService.initiateMultipartUpload(objectKey);
|
||||
|
||||
// 2. 保存文件任务
|
||||
FileTask addParam = convert.dto2entity(param);
|
||||
addParam.setId(IdWorker.getId());
|
||||
addParam.setObjectKey(objectKey);
|
||||
addParam.setSuffix(fileSuffix);
|
||||
// 当前位置
|
||||
addParam.setCurrentIndex(0);
|
||||
addParam.setUploadId(uploadId);
|
||||
// 正在上传
|
||||
addParam.setStatus(FileTaskStatus.UPLOADING.getCode());
|
||||
this.save(addParam);
|
||||
|
||||
return addParam.getId();
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public FileTaskUpdateVO updateFileTask(FileTaskUpdateDTO param) {
|
||||
// 校验文件任务
|
||||
FileTask fileTask = this.getById(param.getId());
|
||||
AssertUtils.notNull(fileTask, "文件任务不存在");
|
||||
AssertUtils.isTrue(!FileTaskStatus.COMPLETED.getCode().equals(fileTask.getStatus()), "文件任务已完成");
|
||||
|
||||
// 1. 新增分段
|
||||
addPart(param, fileTask);
|
||||
|
||||
// 是否最后一段
|
||||
boolean isLast = fileTask.getChunkTotal().equals(param.getPartNumber());
|
||||
// 2. 更新文件任务
|
||||
boolean update = updateTask(param, isLast);
|
||||
// 3. 处理上传结束
|
||||
handleFinished(isLast, update, fileTask);
|
||||
return this.getUpdateVO(param.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理结束
|
||||
* @param isLast
|
||||
* @param update
|
||||
* @param fileTask
|
||||
*/
|
||||
private void handleFinished(boolean isLast, boolean update, FileTask fileTask) {
|
||||
if (isLast && update) {
|
||||
// 最后一段并且更新成功,合并文件
|
||||
List<FileTaskPart> parts = partService.list(Wrappers.<FileTaskPart>lambdaQuery()
|
||||
.eq(FileTaskPart::getFileTaskId, fileTask.getId())
|
||||
.orderByAsc(FileTaskPart::getPartNumber)
|
||||
);
|
||||
|
||||
try {
|
||||
// 按分段号去重,相同分段号取第一个,如果分段号相同,etag不同,报错
|
||||
Map<Integer, FileTaskPart> numberTag = parts.stream()
|
||||
.collect(Collectors.toMap(FileTaskPart::getPartNumber, Function.identity(), (a, b) -> {
|
||||
if (StringUtil.equals(a.getEtag(), b.getEtag())) {
|
||||
return a;
|
||||
}
|
||||
log.error("etag不一致 id1:{} id2:{} etag1:{} etag2:{}", a.getId(), b.getId(), a.getEtag(), b.getEtag());
|
||||
throw new ServiceException("etag不一致");
|
||||
}));
|
||||
// 获取要保留的分段id
|
||||
List<Long> partId = numberTag.values().stream()
|
||||
.map(FileTaskPart::getId)
|
||||
.toList();
|
||||
// 去重
|
||||
parts = parts.stream()
|
||||
.filter(e -> partId.contains(e.getId()))
|
||||
.toList();
|
||||
List<PartEtagVO> partEtags = parts.stream()
|
||||
.map(convert::entity2vo)
|
||||
.toList();
|
||||
// 3. 合并文件
|
||||
fileService.completeMultipartUpload(fileTask.getObjectKey(), fileTask.getUploadId(), partEtags);
|
||||
// 4. 删除文件任务分段
|
||||
List<Long> partIds = parts.stream()
|
||||
.map(FileTaskPart::getId)
|
||||
.toList();
|
||||
partService.removeByIds(partIds);
|
||||
// 5. 发布上传完成事件
|
||||
applicationContext.publishEvent(new FileUploadedEvent(fileTask.getId()));
|
||||
} catch (Exception e) {
|
||||
log.error("文件分段上传合并文件异常", e);
|
||||
log.error("文件任务id:{} objectKey:{}", fileTask.getId(), fileTask.getObjectKey());
|
||||
// 更新文件任务状态为失败
|
||||
FileTask updateParam = new FileTask();
|
||||
updateParam.setId(fileTask.getId());
|
||||
updateParam.setStatus(FileTaskStatus.FAILED.getCode());
|
||||
this.updateById(updateParam);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增分段
|
||||
* @param param
|
||||
* @param fileTask
|
||||
*/
|
||||
private void addPart(FileTaskUpdateDTO param, FileTask fileTask) {
|
||||
// 校验分段
|
||||
FileTaskPart part = partService.getOne(Wrappers.<FileTaskPart>lambdaQuery()
|
||||
.eq(FileTaskPart::getFileTaskId, fileTask.getId())
|
||||
.eq(FileTaskPart::getPartNumber, param.getPartNumber())
|
||||
.last("limit 1")
|
||||
);
|
||||
if (part != null) {
|
||||
// 分段存在
|
||||
if (StringUtil.equals(part.getEtag(), param.getEtag())) {
|
||||
// 且etag和当前的etag相同,不做任何处理
|
||||
return;
|
||||
}
|
||||
// etag和当前的etag不同,报错
|
||||
throw new ServiceException("当前分段已存在");
|
||||
}
|
||||
|
||||
// 新增分段
|
||||
FileTaskPart addParam = new FileTaskPart();
|
||||
addParam.setId(IdWorker.getId());
|
||||
addParam.setFileTaskId(fileTask.getId());
|
||||
addParam.setPartNumber(param.getPartNumber());
|
||||
addParam.setEtag(param.getEtag());
|
||||
partService.save(addParam);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploadUrl(Long id, Integer partNumber) {
|
||||
FileTask fileTask = this.getById(id);
|
||||
AssertUtils.notNull(fileTask, "文件任务不存在");
|
||||
AssertUtils.isTrue(!FileTaskStatus.COMPLETED.getCode().equals(fileTask.getStatus()), "文件任务已完成");
|
||||
return fileService.getUploadPartUrl(fileTask.getObjectKey(), fileTask.getUploadId(), partNumber, null);
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void cancelBatchByIds(List<Long> taskIds) {
|
||||
// 查询任务
|
||||
List<FileTask> fileTasks = this.listByIds(taskIds);
|
||||
if (CollectionUtil.isEmpty(fileTasks)) {
|
||||
return;
|
||||
}
|
||||
// 校验状态
|
||||
fileTasks.forEach(task -> AssertUtils.isFalse(FileTaskStatus.COMPLETED.getCode().equals(task.getStatus()), "文件任务已上传完成"));
|
||||
// 调用obs取消上传任务
|
||||
try {
|
||||
for (FileTask fileTask : fileTasks) {
|
||||
fileService.cancelMultipartUpload(fileTask.getObjectKey(), fileTask.getUploadId());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("调用obs取消文件上传任务异常", e);
|
||||
throw new ServiceException("调用obs取消文件上传任务异常");
|
||||
}
|
||||
// 删除上传任务
|
||||
this.removeBatchByIds(taskIds);
|
||||
// 删除文件任务分片
|
||||
List<FileTaskPart> list = partService.list(Wrappers.<FileTaskPart>lambdaQuery()
|
||||
.in(FileTaskPart::getFileTaskId, taskIds)
|
||||
);
|
||||
if (CollectionUtil.isNotEmpty(list)) {
|
||||
List<Long> partIds = list.stream()
|
||||
.map(FileTaskPart::getId)
|
||||
.toList();
|
||||
partService.removeByIds(partIds);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileTaskUpdateVO updateFailed(Long id) {
|
||||
// 校验文件任务
|
||||
FileTask fileTask = this.getById(id);
|
||||
AssertUtils.notNull(fileTask, "文件任务不存在");
|
||||
AssertUtils.isTrue(!FileTaskStatus.COMPLETED.getCode().equals(fileTask.getStatus()), "文件任务已完成");
|
||||
// 更新文件任务状态为失败
|
||||
FileTask updateParam = new FileTask();
|
||||
updateParam.setId(id);
|
||||
updateParam.setStatus(FileTaskStatus.FAILED.getCode());
|
||||
this.updateById(updateParam);
|
||||
return this.getUpdateVO(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updatePaused(List<Long> ids) {
|
||||
if (CollectionUtil.isEmpty(ids)) {
|
||||
return false;
|
||||
}
|
||||
FileTask updateParam = new FileTask();
|
||||
// 修改状态为已暂停
|
||||
updateParam.setStatus(FileTaskStatus.PAUSED.getCode());
|
||||
return this.update(updateParam, Wrappers.<FileTask>lambdaUpdate()
|
||||
.in(FileTask::getId, ids)
|
||||
// 只更新正在上传状态的
|
||||
.eq(FileTask::getStatus, FileTaskStatus.UPLOADING.getCode())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询文件任务
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
private FileTaskUpdateVO getUpdateVO(Long id) {
|
||||
FileTask fileTask = this.getById(id);
|
||||
return getFileTaskUpdateVO(fileTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询文件任务
|
||||
* @param fileTask
|
||||
* @return
|
||||
*/
|
||||
private FileTaskUpdateVO getFileTaskUpdateVO(FileTask fileTask) {
|
||||
if (fileTask == null) {
|
||||
return null;
|
||||
}
|
||||
FileTaskUpdateVO updateVO = convert.entity2updateVo(fileTask);
|
||||
BusinessUnzipTask businessUnzipTask = unzipTaskMapper.selectOne(Wrappers.<BusinessUnzipTask>lambdaQuery()
|
||||
.eq(BusinessUnzipTask::getRelateId, fileTask.getId())
|
||||
.last("limit 1")
|
||||
);
|
||||
if (businessUnzipTask != null && businessUnzipTask.getDocCode() != null) {
|
||||
updateVO.setDocCode(businessUnzipTask.getDocCode());
|
||||
}
|
||||
return updateVO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新任务
|
||||
*
|
||||
* @param param
|
||||
* @param isLast 是否最后一段
|
||||
* @return
|
||||
*/
|
||||
private boolean updateTask(FileTaskUpdateDTO param, boolean isLast) {
|
||||
FileTask updateParam = new FileTask();
|
||||
updateParam.setCurrentIndex(param.getPartNumber());
|
||||
if (isLast) {
|
||||
// 最后一段,修改任务状态为完成
|
||||
updateParam.setStatus(FileTaskStatus.COMPLETED.getCode());
|
||||
} else {
|
||||
// 非最后一段,修改任务状态为上传中
|
||||
updateParam.setStatus(FileTaskStatus.UPLOADING.getCode());
|
||||
}
|
||||
return this.update(updateParam, Wrappers.<FileTask>lambdaUpdate()
|
||||
.eq(FileTask::getId, param.getId())
|
||||
.lt(FileTask::getCurrentIndex, param.getPartNumber())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.springblade.file.service.impl;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.log.utils.AssertUtils;
|
||||
import org.springblade.file.convert.OCRConverter;
|
||||
import org.springblade.file.pojo.dto.FileCertificateBatchRecognitionDTO;
|
||||
import org.springblade.file.service.IFileService;
|
||||
import org.springblade.file.service.IOCRConvertService;
|
||||
import org.springblade.thirdparty.ocr.pojo.dto.CertificateBatchRecognitionDTO;
|
||||
import org.springblade.thirdparty.ocr.pojo.vo.TransportCertificateVO;
|
||||
import org.springblade.thirdparty.ocr.service.IOCRService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author bfhuange
|
||||
* @since 2025/3/13
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class OCRConvertServiceImpl implements IOCRConvertService {
|
||||
|
||||
private final IFileService fileService;
|
||||
private final IOCRService ocrService;
|
||||
private final OCRConverter converter;
|
||||
|
||||
@Override
|
||||
public TransportCertificateVO recognitionTransportCertificate(FileCertificateBatchRecognitionDTO param) {
|
||||
AssertUtils.notNull(param, "参数不能为空");
|
||||
AssertUtils.notNull(param.getObjectKeys(), "图像obs key不能为空");
|
||||
CertificateBatchRecognitionDTO ocrParam = converter.dto2ocr(param);
|
||||
// 转换文件链接
|
||||
List<String> fileUrls = fileService.getFileUrls(param.getObjectKeys(), null);
|
||||
ocrParam.setUrls(fileUrls);
|
||||
return ocrService.recognitionTransportCertificate(ocrParam);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
package org.springblade.file.service.impl;
|
||||
|
||||
import cn.hutool.core.net.URLEncodeUtil;
|
||||
import com.obs.services.ObsClient;
|
||||
import com.obs.services.exception.ObsException;
|
||||
import com.obs.services.internal.Constants;
|
||||
import com.obs.services.internal.utils.ServiceUtils;
|
||||
import com.obs.services.model.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.tool.utils.CollectionUtil;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.file.config.ObsProperties;
|
||||
import org.springblade.file.pojo.vo.PartEtagVO;
|
||||
import org.springblade.file.service.IFileService;
|
||||
import org.springblade.file.service.support.DownloadProgressListener;
|
||||
import org.springblade.file.service.support.UploadProgressListener;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 华为obs对象存储
|
||||
* @author bfhuange
|
||||
* @date 2024/10/21
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class ObsFileServiceImpl implements IFileService {
|
||||
private final ObsProperties properties;
|
||||
private final ObsClient obsClient;
|
||||
|
||||
@Override
|
||||
public void uploadFile(String objectKey, InputStream is) {
|
||||
this.uploadFile(objectKey, null, is, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadTempFile(String objectKey, InputStream is, Integer expires) {
|
||||
// 上传文件
|
||||
this.uploadFile(objectKey, null, is, expires);
|
||||
// 生成下载链接
|
||||
return this.getFileUrl(objectKey, properties.getExpires());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uploadFile(String objectKey, File file) {
|
||||
this.uploadFile(objectKey, file, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String initiateMultipartUpload(String objectKey) {
|
||||
InitiateMultipartUploadRequest initiateRequest = new InitiateMultipartUploadRequest(properties.getBucketName(), objectKey);
|
||||
try {
|
||||
InitiateMultipartUploadResult initiateResult = obsClient.initiateMultipartUpload(initiateRequest);
|
||||
return initiateResult.getUploadId();
|
||||
} catch (Exception e) {
|
||||
log.error("初始化分段上传异常", e);
|
||||
throw new ServiceException("初始化分段上传异常");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PartEtagVO uploadPart(String objectKey, String uploadId, File file, int partNumber, Long offset, Long partSize) {
|
||||
UploadPartRequest uploadPartRequest = new UploadPartRequest();
|
||||
uploadPartRequest.setBucketName(properties.getBucketName());
|
||||
uploadPartRequest.setObjectKey(objectKey);
|
||||
uploadPartRequest.setUploadId(uploadId);
|
||||
uploadPartRequest.setPartNumber(partNumber);
|
||||
uploadPartRequest.setFile(file);
|
||||
if (offset != null) {
|
||||
uploadPartRequest.setOffset(offset);
|
||||
}
|
||||
if (partSize != null) {
|
||||
uploadPartRequest.setPartSize(partSize);
|
||||
}
|
||||
try {
|
||||
UploadPartResult uploadPartResult = obsClient.uploadPart(uploadPartRequest);
|
||||
return new PartEtagVO(uploadPartResult.getEtag(), uploadPartResult.getPartNumber());
|
||||
} catch (Exception e) {
|
||||
log.error("分段上传异常", e);
|
||||
throw new ServiceException("分段上传异常");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void completeMultipartUpload(String objectKey, String uploadId, List<PartEtagVO> partEtagVOS) {
|
||||
if (CollectionUtil.isEmpty(partEtagVOS)) {
|
||||
log.warn("完成分段上传,要合并的分段列表为空");
|
||||
return;
|
||||
}
|
||||
// 参数转换
|
||||
List<PartEtag> list = partEtagVOS.stream()
|
||||
.map(partEtagVO -> new PartEtag(partEtagVO.getEtag(), partEtagVO.getPartNumber()))
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
CompleteMultipartUploadRequest request =
|
||||
new CompleteMultipartUploadRequest(properties.getBucketName(), objectKey, uploadId, list);
|
||||
try {
|
||||
obsClient.completeMultipartUpload(request);
|
||||
} catch (Exception e) {
|
||||
log.error("完成分段上传异常", e);
|
||||
throw new ServiceException("完成分段上传异常");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelMultipartUpload(String objectKey, String uploadId) {
|
||||
AbortMultipartUploadRequest request = new AbortMultipartUploadRequest(properties.getBucketName(), objectKey, uploadId);
|
||||
try {
|
||||
obsClient.abortMultipartUpload(request);
|
||||
} catch (Exception e) {
|
||||
log.error("取消分段上传任务异常", e);
|
||||
throw new ServiceException("取消分段上传任务异常");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileUrl(String objectKey, Long expires) {
|
||||
return this.getFileUrl(objectKey, null, expires);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileUrl(String objectKey, String fileName, Long expires) {
|
||||
if (StringUtil.isBlank(objectKey)) {
|
||||
return null;
|
||||
}
|
||||
// 链接失效时间
|
||||
expires = Optional.ofNullable(expires)
|
||||
// 参数为空取配置
|
||||
.orElse(Optional.ofNullable(properties.getExpires())
|
||||
// 配置为空,默认1小时
|
||||
.orElse(Duration.ofHours(1).getSeconds()));
|
||||
TemporarySignatureRequest request = new TemporarySignatureRequest(HttpMethodEnum.GET, expires);
|
||||
request.setBucketName(properties.getBucketName());
|
||||
request.setObjectKey(objectKey);
|
||||
request.setRequestDate(new Date());
|
||||
if (StringUtil.isNotBlank(fileName)) {
|
||||
// 文件名不为空,修改响应的文件名
|
||||
Map<String, Object> queryParam = new HashMap<>();
|
||||
String encodeFileName = encode(fileName);
|
||||
queryParam.put(Constants.ObsRequestParams.RESPONSE_CONTENT_DISPOSITION, "attachment;filename*=UTF-8''" + encodeFileName);
|
||||
request.setQueryParams(queryParam);
|
||||
}
|
||||
try {
|
||||
TemporarySignatureResponse temporarySignature = obsClient.createTemporarySignature(request);
|
||||
return temporarySignature.getSignedUrl();
|
||||
} catch (Exception e) {
|
||||
log.error("获取文件链接异常", e);
|
||||
throw new ServiceException("获取文件链接异常");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码文件名
|
||||
* @param fileName
|
||||
* @return
|
||||
*/
|
||||
private String encode(String fileName) {
|
||||
int index = fileName.lastIndexOf(".");
|
||||
if (index < 0) {
|
||||
return URLEncodeUtil.encode(fileName);
|
||||
}
|
||||
// 文件名
|
||||
String prefix = fileName.substring(0, index);
|
||||
// 后缀
|
||||
String ext = fileName.substring(index);
|
||||
return URLEncodeUtil.encode(prefix) + ext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploadPartUrl(String objectKey, String uploadId, Integer partNumber, Long expires) {
|
||||
TemporarySignatureRequest request = new TemporarySignatureRequest();
|
||||
request.setBucketName(properties.getBucketName());
|
||||
request.setObjectKey(objectKey);
|
||||
request.setRequestDate(new Date());
|
||||
// 链接失效时间
|
||||
expires = Optional.ofNullable(expires)
|
||||
.orElse(properties.getExpires());
|
||||
if (expires != null) {
|
||||
request.setExpires(expires);
|
||||
}
|
||||
request.setMethod(HttpMethodEnum.PUT);
|
||||
request.setHeaders(Map.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE));
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put(Constants.ObsRequestParams.PART_NUMBER, String.valueOf(partNumber));
|
||||
params.put(Constants.ObsRequestParams.UPLOAD_ID, uploadId);
|
||||
request.setQueryParams(params);
|
||||
try {
|
||||
TemporarySignatureResponse temporarySignature = obsClient.createTemporarySignature(request);
|
||||
return temporarySignature.getSignedUrl();
|
||||
} catch (Exception e) {
|
||||
log.error("获取文件链接异常", e);
|
||||
throw new ServiceException("获取文件链接异常");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getFileUrls(List<String> objectKeys, Long expires) {
|
||||
if (CollectionUtil.isEmpty(objectKeys)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return objectKeys.stream()
|
||||
.map(objectKey -> this.getFileUrl(objectKey, expires))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void downloadFile(String objectKey, String downloadFile) {
|
||||
DownloadFileRequest request = new DownloadFileRequest(properties.getBucketName(), objectKey);
|
||||
request.setDownloadFile(downloadFile);
|
||||
// 开启断点续传模式
|
||||
request.setEnableCheckpoint(true);
|
||||
Long progressInterval = properties.getDownloadProgressInterval();
|
||||
// 设置下载进度日志
|
||||
request.setProgressListener(new DownloadProgressListener(objectKey, progressInterval));
|
||||
request.setProgressInterval(progressInterval);
|
||||
DownloadFileResult result = null;
|
||||
try {
|
||||
result = obsClient.downloadFile(request);
|
||||
} catch (ObsException e) {
|
||||
log.error("下载文件异常", e);
|
||||
log.error("下载文件异常 objectKey:{} responseCode:{} errorCode:{} errorMessage:{} errorRequestId:{} errorHostId:{}", objectKey, e.getResponseCode(), e.getErrorCode(), e.getErrorMessage(), e.getErrorRequestId(), e.getErrorHostId());
|
||||
throw new ServiceException("下载文件异常");
|
||||
} catch (Exception e) {
|
||||
log.error("下载文件异常", e);
|
||||
throw new ServiceException("下载文件异常");
|
||||
} finally {
|
||||
String etag = Optional.ofNullable(result)
|
||||
.map(DownloadFileResult::getObjectMetadata)
|
||||
.map(ObjectMetadata::getEtag)
|
||||
.orElse(null);
|
||||
log.info("下载文件结束 objectKey:{} etag:{}", objectKey, etag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
* @param objectKey
|
||||
* @param file
|
||||
* @param is
|
||||
* @param expires 过期时间,单位天,过期后自动删除
|
||||
*/
|
||||
private void uploadFile(String objectKey, File file, InputStream is, Integer expires) {
|
||||
if (file == null && is == null ) {
|
||||
throw new ServiceException("文件不存在");
|
||||
}
|
||||
PutObjectRequest request = new PutObjectRequest();
|
||||
request.setBucketName(properties.getBucketName());
|
||||
request.setObjectKey(objectKey);
|
||||
if (expires != null) {
|
||||
request.setExpires(expires);
|
||||
}
|
||||
if (is != null) {
|
||||
request.setInput(is);
|
||||
} else {
|
||||
request.setFile(file);
|
||||
// 文件不为空,设置md5
|
||||
String md5 = getFileMd5(file);
|
||||
ObjectMetadata metadata = new ObjectMetadata();
|
||||
metadata.setContentMd5(md5);
|
||||
request.setMetadata(metadata);
|
||||
}
|
||||
Long progressInterval = properties.getUploadProgressInterval();
|
||||
// 设置上传进度日志
|
||||
request.setProgressListener(new UploadProgressListener(objectKey, progressInterval));
|
||||
request.setProgressInterval(progressInterval);
|
||||
try {
|
||||
obsClient.putObject(request);
|
||||
} catch (Exception e) {
|
||||
log.error("上传文件异常", e);
|
||||
throw new ServiceException("上传文件异常");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileMd5(File file) {
|
||||
try {
|
||||
return ServiceUtils.toBase64(ServiceUtils.computeMD5Hash(new FileInputStream(file)));
|
||||
} catch (NoSuchAlgorithmException | IOException e) {
|
||||
log.error("获取文件md5异常", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.springblade.file.service.support;
|
||||
|
||||
import com.obs.services.model.ProgressListener;
|
||||
import com.obs.services.model.ProgressStatus;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 文件下载进度监听器
|
||||
* @author bfhuange
|
||||
* @date 2024/10/28
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Slf4j
|
||||
public class DownloadProgressListener implements ProgressListener {
|
||||
|
||||
/**
|
||||
* obs对象名称
|
||||
*/
|
||||
private final String objectKey;
|
||||
|
||||
/**
|
||||
* 进度间隔,如果新传输的大小不等于进度间隔,说明是结束了
|
||||
*/
|
||||
private final long progressInterval;
|
||||
|
||||
@Override
|
||||
public void progressChanged(ProgressStatus status) {
|
||||
log.info("文件下载进度 文件名:{} 是否结束:{} 总大小:{} 已下载大小:{} 百分比:{} 下载速度:{}", objectKey, progressInterval != status.getNewlyTransferredBytes(), status.getTotalBytes(), status.getTransferredBytes(), status.getTransferPercentage(), String.format("%.2f", status.getAverageSpeed()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.springblade.file.service.support;
|
||||
|
||||
import com.obs.services.model.ProgressListener;
|
||||
import com.obs.services.model.ProgressStatus;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 文件上传进度监听器
|
||||
* @author bfhuange
|
||||
* @date 2024/10/28
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Slf4j
|
||||
public class UploadProgressListener implements ProgressListener {
|
||||
|
||||
/**
|
||||
* obs对象名称
|
||||
*/
|
||||
private final String objectKey;
|
||||
|
||||
/**
|
||||
* 进度间隔,如果新传输的大小不等于进度间隔,说明是结束了
|
||||
*/
|
||||
private final long progressInterval;
|
||||
|
||||
@Override
|
||||
public void progressChanged(ProgressStatus status) {
|
||||
log.info("文件上传进度 文件名:{} 是否结束:{} 已上传大小:{} 上传速度:{}", objectKey, progressInterval != status.getNewlyTransferredBytes(), status.getTransferredBytes(), String.format("%.2f", status.getAverageSpeed()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package org.springblade.file.util;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
import org.springblade.core.secure.utils.AuthUtil;
|
||||
import org.springblade.core.tool.utils.RandomType;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
|
||||
/**
|
||||
* 对象存储key工具类
|
||||
* @author bfhuange
|
||||
* @date 2024/10/29
|
||||
*/
|
||||
public class ObjectKeyUtils {
|
||||
/**
|
||||
* object key 格式,前缀目录/ + id + 用户id后4位 + 后缀
|
||||
*/
|
||||
private static final String FORMAT = "%s%s%s%s";
|
||||
/**
|
||||
* 用户id后缀位数
|
||||
*/
|
||||
private static final int ID_SUFFIX_LENGTH = 4;
|
||||
|
||||
private ObjectKeyUtils() {}
|
||||
|
||||
/**
|
||||
* 生成 object key
|
||||
* @param dir 文件目录
|
||||
* @param fileName 文件名
|
||||
* @return
|
||||
*/
|
||||
public static String generateObjectKey(String dir, String fileName) {
|
||||
// 目录字符串
|
||||
String dirString = ObjectKeyUtils.getDirString(dir);
|
||||
// 文件名后缀
|
||||
String fileNameSuffix = ObjectKeyUtils.getFileNameSuffix(fileName);
|
||||
// 用户id后4位
|
||||
String userIdSuffix = ObjectKeyUtils.getUserIdSuffix();
|
||||
// 格式,目录 + id + 用户id后4位 + 后缀
|
||||
return String.format(FORMAT, dirString, IdWorker.getId(), userIdSuffix, fileNameSuffix);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 object key
|
||||
* @param fileName 文件名
|
||||
* @return
|
||||
*/
|
||||
public static String generateObjectKey(String fileName) {
|
||||
return ObjectKeyUtils.generateObjectKey(StringPool.EMPTY, fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户id后缀字符串
|
||||
* @return
|
||||
*/
|
||||
private static String getUserIdSuffix() {
|
||||
Long userId = AuthUtil.getUserId();
|
||||
if (userId > 0) {
|
||||
String idString = userId.toString();
|
||||
if (idString.length() >= ID_SUFFIX_LENGTH) {
|
||||
// 截取用户id后几位
|
||||
return idString.substring(idString.length() - ID_SUFFIX_LENGTH);
|
||||
}
|
||||
}
|
||||
// 未获取到用户id,取随机数字
|
||||
return StringUtil.random(ID_SUFFIX_LENGTH, RandomType.INT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取目录字符串
|
||||
* @param dir
|
||||
* @return
|
||||
*/
|
||||
private static String getDirString(String dir) {
|
||||
if (StringUtil.isBlank(dir)) {
|
||||
return StringPool.EMPTY;
|
||||
}
|
||||
if (dir.endsWith(StringPool.SLASH)) {
|
||||
return dir;
|
||||
}
|
||||
return dir + StringPool.SLASH;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件名后缀
|
||||
* @param fileName 文件名
|
||||
* @return
|
||||
*/
|
||||
private static String getFileNameSuffix(String fileName) {
|
||||
if (StringUtil.isBlank(fileName)) {
|
||||
return StringPool.EMPTY;
|
||||
}
|
||||
if (!fileName.contains(StringPool.DOT)) {
|
||||
return StringPool.EMPTY;
|
||||
}
|
||||
return fileName.substring(fileName.lastIndexOf(StringPool.DOT));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
package org.springblade.file.util;
|
||||
|
||||
import cn.hutool.core.io.CharsetDetector;
|
||||
import com.github.junrar.Archive;
|
||||
import com.github.junrar.rarfile.FileHeader;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.ArchiveException;
|
||||
import org.apache.commons.compress.archivers.ArchiveInputStream;
|
||||
import org.apache.commons.compress.archivers.ArchiveStreamFactory;
|
||||
import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.sevenz.SevenZFile;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
/**
|
||||
* 解压工具类
|
||||
* @author bfhuange
|
||||
* @since 2025/2/28
|
||||
*/
|
||||
@Slf4j
|
||||
public class UnzipUtil {
|
||||
/**
|
||||
* rar格式
|
||||
*/
|
||||
public static final String RAR = "rar";
|
||||
|
||||
/**
|
||||
* macos下垃圾目录,不处理
|
||||
*/
|
||||
private static final String MACOSX_DIR = "__MACOSX";
|
||||
|
||||
/**
|
||||
* 解压文件
|
||||
* @param zipFilePath 压缩文件路径
|
||||
* @param unzipDir 解压目录
|
||||
*/
|
||||
public static List<File> unzipOrUnrar(String zipFilePath, File unzipDir) {
|
||||
if (zipFilePath.toLowerCase().endsWith(RAR)) {
|
||||
// 解压rar
|
||||
return unrar(zipFilePath, unzipDir);
|
||||
} else if (zipFilePath.toLowerCase().endsWith(ArchiveStreamFactory.SEVEN_Z)) {
|
||||
// 解压7z
|
||||
return un7z(zipFilePath, unzipDir);
|
||||
}
|
||||
// 解压其余格式
|
||||
return unzip(zipFilePath, unzipDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解压zip格式
|
||||
* @param downloadFile
|
||||
* @param destDir
|
||||
*/
|
||||
public static List<File> un7z(String downloadFile, File destDir) {
|
||||
List<File> files = new ArrayList<>();
|
||||
try(
|
||||
SevenZFile sevenZFile = new SevenZFile.Builder()
|
||||
.setFile(new File(downloadFile))
|
||||
.get()
|
||||
) {
|
||||
SevenZArchiveEntry entry = sevenZFile.getNextEntry();
|
||||
while (entry != null) {
|
||||
log.debug("压缩文件条目:{}", entry.getName());
|
||||
File file = new File(destDir, entry.getName());
|
||||
if (!entry.isDirectory()) {
|
||||
// 不是目录才处理
|
||||
files.add(file);
|
||||
File parentFile = file.getParentFile();
|
||||
if (!parentFile.exists()) {
|
||||
parentFile.mkdirs();
|
||||
}
|
||||
try(
|
||||
OutputStream os = new FileOutputStream(file);
|
||||
InputStream is = sevenZFile.getInputStream(entry);
|
||||
) {
|
||||
IOUtils.copy(is, os);
|
||||
}
|
||||
}
|
||||
entry = sevenZFile.getNextEntry();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("解压文件异常", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解压rar格式
|
||||
* @param zipFilePath 压缩文件路径
|
||||
* @param unzipDir 解压文件目录
|
||||
*/
|
||||
public static List<File> unrar(String zipFilePath, File unzipDir) {
|
||||
List<File> files = new ArrayList<>();
|
||||
try (
|
||||
// 加载rar文件
|
||||
InputStream inputStream = Files.newInputStream(Paths.get(zipFilePath));
|
||||
Archive archive = new Archive(inputStream);
|
||||
) {
|
||||
List<FileHeader> fileHeaders = archive.getFileHeaders();
|
||||
if (fileHeaders == null) {
|
||||
throw new ServiceException("压缩文件为空");
|
||||
}
|
||||
int total = fileHeaders.size();
|
||||
for (int i = 0; i < fileHeaders.size(); i++) {
|
||||
FileHeader fileHeader = fileHeaders.get(i);
|
||||
log.debug("解压进度:{}/{} 文件:{}", i + 1, total, fileHeader.getFileName());
|
||||
// 解压文件
|
||||
File file = new File(unzipDir, fileHeader.getFileName());
|
||||
if (!fileHeader.isDirectory()) {
|
||||
// 不是目录才处理
|
||||
files.add(file);
|
||||
File parentFile = file.getParentFile();
|
||||
if (!parentFile.exists()) {
|
||||
parentFile.mkdirs();
|
||||
}
|
||||
InputStream is = archive.getInputStream(fileHeader);
|
||||
try (OutputStream os = new FileOutputStream(file)) {
|
||||
IOUtils.copy(is, os);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ServiceException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
log.error("解压文件异常", e);
|
||||
throw new ServiceException("解压文件异常");
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解压zip格式
|
||||
* @param zipFilePath 压缩文件路径
|
||||
* @param unzipDir 解压目录
|
||||
*/
|
||||
public static List<File> unzip(String zipFilePath, File unzipDir) {
|
||||
List<File> files = new ArrayList<>();
|
||||
ArchiveStreamFactory archiveStreamFactory = new ArchiveStreamFactory();
|
||||
try(
|
||||
FileInputStream is = new FileInputStream(zipFilePath);
|
||||
BufferedInputStream bis = new BufferedInputStream(is);
|
||||
ArchiveInputStream<? extends ArchiveEntry> ais = archiveStreamFactory.createArchiveInputStream(bis);
|
||||
) {
|
||||
ArchiveEntry entry;
|
||||
while ((entry = ais.getNextEntry()) != null) {
|
||||
// 获取文件名称
|
||||
String entryName = UnzipUtil.getEntryName(entry);
|
||||
log.debug("压缩文件条目:{}", entryName);
|
||||
if (entryName.contains(MACOSX_DIR)) {
|
||||
// macos下垃圾目录,跳过
|
||||
log.info("跳过macos下__MACOSX的文件:{}", entryName);
|
||||
continue;
|
||||
}
|
||||
// 压缩文件内文件夹分隔符是\,linux下不会识别为文件夹,会识别成文件名,处理下
|
||||
if (entryName.contains(StringPool.BACK_SLASH)) {
|
||||
entryName = entryName.replace(StringPool.BACK_SLASH, StringPool.SLASH);
|
||||
}
|
||||
if (!isDirectory(entryName)) {
|
||||
File file = new File(unzipDir, entryName);
|
||||
if (file.getName().startsWith(StringPool.DOT)) {
|
||||
// .开头的文件,是隐藏文件,跳过
|
||||
log.info("跳过.开头的文件:{}", entryName);
|
||||
continue;
|
||||
}
|
||||
// 不是目录才处理
|
||||
files.add(file);
|
||||
File parentFile = file.getParentFile();
|
||||
if (!parentFile.exists()) {
|
||||
parentFile.mkdirs();
|
||||
}
|
||||
try(OutputStream os = new FileOutputStream(file)) {
|
||||
IOUtils.copy(ais, os);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("解压文件异常", e);
|
||||
throw new RuntimeException(e);
|
||||
} catch (ArchiveException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件名称
|
||||
* @param entry
|
||||
* @return
|
||||
*/
|
||||
private static String getEntryName(ArchiveEntry entry) {
|
||||
if (entry instanceof ZipArchiveEntry zipArchiveEntry) {
|
||||
byte[] rawName = zipArchiveEntry.getRawName();
|
||||
// 检测文件名编码
|
||||
Charset charset = CharsetDetector.detect(new ByteArrayInputStream(rawName));
|
||||
return new String(rawName, charset);
|
||||
}
|
||||
return entry.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断压缩文件是否utf8编码
|
||||
* @param zipFilePath 压缩文件路径
|
||||
* @return
|
||||
*/
|
||||
public static boolean isUtf8(String zipFilePath) {
|
||||
try (
|
||||
FileInputStream fileInputStream = new FileInputStream(zipFilePath);
|
||||
ZipInputStream zipInputStream = new ZipInputStream(fileInputStream, StandardCharsets.UTF_8);
|
||||
) {
|
||||
while (zipInputStream.getNextEntry() != null) {
|
||||
|
||||
}
|
||||
return true;
|
||||
} catch (Exception ignore) {
|
||||
// gbk编码的用utf8编码读取会抛异常
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断压缩文件中条目是否文件夹
|
||||
* @param entryName
|
||||
* @return
|
||||
*/
|
||||
private static boolean isDirectory(String entryName) {
|
||||
if (entryName == null) {
|
||||
return false;
|
||||
}
|
||||
return entryName.endsWith(StringPool.SLASH) || entryName.endsWith(StringPool.BACK_SLASH);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#spring:
|
||||
# cloud:
|
||||
# nacos:
|
||||
# username: nacos
|
||||
# password: ${NACOS_PASSWORD:gr30wIs5%Hi7keQj}
|
||||
# server-addr: ${NACOS_ADDR:10.38.16.127:8848}
|
||||
@@ -0,0 +1,6 @@
|
||||
#spring:
|
||||
# cloud:
|
||||
# nacos:
|
||||
# username: nacos
|
||||
# password: rWrMrVTWyf%ekjuw
|
||||
# server-addr: ${NACOS_ADDR:192.168.0.242:8848}
|
||||
@@ -0,0 +1,8 @@
|
||||
#server:
|
||||
# port: 38107
|
||||
#spring:
|
||||
# cloud:
|
||||
# nacos:
|
||||
# username: nacos
|
||||
# password: gr30wIs5%Hi7keQj
|
||||
# server-addr: ${NACOS_ADDR:10.38.16.127:8848}
|
||||
41
blade-service/blade-file/src/main/resources/bootstrap.yml
Normal file
41
blade-service/blade-file/src/main/resources/bootstrap.yml
Normal file
@@ -0,0 +1,41 @@
|
||||
server:
|
||||
port: 8107
|
||||
|
||||
#数据源配置
|
||||
spring:
|
||||
datasource:
|
||||
url: ${blade.datasource.dev.url}
|
||||
username: ${blade.datasource.dev.username}
|
||||
password: ${blade.datasource.dev.password}
|
||||
|
||||
#spring:
|
||||
# application:
|
||||
# name: blade-file
|
||||
# cloud:
|
||||
# nacos:
|
||||
# discovery:
|
||||
# namespace: ${spring.profiles.active}
|
||||
# # 不注册到nacos
|
||||
# #registerEnabled: false
|
||||
# config:
|
||||
# # 文件后缀名
|
||||
# file-extension: yaml
|
||||
# namespace: ${spring.profiles.active}
|
||||
# shared-configs:
|
||||
# - data-id: blade.yaml
|
||||
# refresh: true
|
||||
# - data-id: blade-${spring.profiles.active}.yaml
|
||||
# refresh: true
|
||||
# - data-id: third-party-api.yaml
|
||||
# refresh: true
|
||||
# sentinel:
|
||||
# datasource:
|
||||
# flow:
|
||||
# nacos:
|
||||
# serverAddr: ${spring.cloud.nacos.server-addr}
|
||||
# username: ${spring.cloud.nacos.username}
|
||||
# password: ${spring.cloud.nacos.password}
|
||||
# dataId: sentinel-${spring.application.name}.json
|
||||
# groupId: DEFAULT_GROUP
|
||||
# namespace: ${spring.profiles.active}
|
||||
# ruleType: flow
|
||||
Reference in New Issue
Block a user