Commit c5866e47 by zhangxingmin

Merge remote-tracking branch 'origin/dev' into prod

parents a26c6db3 35a8bfc3
#!/bin/bash
# 源端(使用默认配置,无需凭证)
SRC_BUCKET="yd-ali-oss"
# 目标端(香港)
DST_BUCKET="csf-hk"
DST_ENDPOINT="oss-cn-hongkong.aliyuncs.com"
DST_ACCESS_KEY_ID="LTAI5tHS7WBNxN7CwKcWC1Jh"
DST_ACCESS_KEY_SECRET="GaJi5KHqcLOf2lBIFlPKBKDKCECUjo"
# 同步选项
UPDATE_FLAG="--update" # 只同步新增和修改过的文件
LOG_FILE="/root/oss_sync.log"
echo "$(date '+%Y-%m-%d %H:%M:%S') - Start sync" >> ${LOG_FILE}
ossutil sync oss://${SRC_BUCKET}/jpg/ oss://${DST_BUCKET}/pc/prod/ \
-e ${DST_ENDPOINT} -i ${DST_ACCESS_KEY_ID} -k ${DST_ACCESS_KEY_SECRET} \
${UPDATE_FLAG} >> ${LOG_FILE} 2>&1
echo "$(date '+%Y-%m-%d %H:%M:%S') - Sync finished" >> ${LOG_FILE}
\ No newline at end of file
......@@ -36,5 +36,5 @@ FROM base-builder
COPY target/yd-oss-api-1.0-SNAPSHOT-exec.jar /home/app/yd-oss-api.jar
# 执行命令启动jar,并设置JVM内存参数
ENTRYPOINT ["java", "-Xmx256m", "-Xms128m", "-jar", "/home/app/yd-oss-api.jar"]
ENTRYPOINT ["java", "-Xmx1024m", "-Xms512m", "-jar", "/home/app/yd-oss-api.jar"]
EXPOSE 9106
\ No newline at end of file
......@@ -35,8 +35,8 @@ public class ApiOssController implements ApiOssFeignClient {
* @return
*/
@Override
public Result<ApiUploadResponse> uploadFile(MultipartFile file) {
return apiOssService.uploadFile(file,"","","");
public Result<ApiUploadResponse> uploadFile(MultipartFile file,String projectBizId) {
return apiOssService.uploadFile(file,"","","",projectBizId);
}
/**
......
......@@ -43,7 +43,7 @@ public class ApiOssFileController implements ApiOssFileFeignClient {
* @return
*/
@Override
public Result del(String fileBizId) {
return apiOssFileService.del(fileBizId);
public Result del(String fileBizId,String projectBizId) {
return apiOssFileService.del(fileBizId,projectBizId);
}
}
package com.yd.oss.api.controller;
import com.yd.common.result.Result;
import com.yd.oss.api.service.ApiOssStsService;
import com.yd.oss.feign.client.ApiOssStsFeignClient;
import com.yd.oss.feign.request.ApiBatchSaveFilesRequest;
import com.yd.oss.feign.response.ApiBatchSaveFilesResponse;
import com.yd.oss.feign.response.ApiGetStsTokenResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* OSS-分片接口信息
*
* @author zxm
* @since 2025-07-31
*/
@RestController
@RequestMapping("/sts")
@Validated
public class ApiOssStsController implements ApiOssStsFeignClient {
@Autowired
private ApiOssStsService apiOssStsService;
/**
* 获取STS凭证
* @return
*/
public Result<ApiGetStsTokenResponse> getStsToken(String projectBizId){
return apiOssStsService.getStsToken(projectBizId);
}
/**
* 批量保存文件列表
* @return
*/
@Override
public Result<ApiBatchSaveFilesResponse> batchSaveFiles(ApiBatchSaveFilesRequest request) {
return apiOssStsService.batchSaveFiles(request);
}
}
\ No newline at end of file
......@@ -11,7 +11,7 @@ import java.util.List;
public interface ApiOssFileService {
Result<List<ApiOssFileListResponse>> list(ApiOssFileListRequest request);
Result del(String fileBizId);
Result del(String fileBizId,String projectBizId);
Result updateByMaterial(List<RelObjectMaterial> relObjectMaterialList,
List<ApiRelMaterialDto> materialDtoList);
......
......@@ -11,7 +11,7 @@ import javax.servlet.http.HttpServletResponse;
import java.util.List;
public interface ApiOssService {
Result<ApiUploadResponse> uploadFile(MultipartFile file, String bucket, String provider, String uploadUser);
Result<ApiUploadResponse> uploadFile(MultipartFile file, String bucket, String provider, String uploadUser,String projectBizId);
Result<ApiUploadResponse> uploadFileBody(MultipartFile file, ApiUploadFileRequest request);
......
package com.yd.oss.api.service;
import com.yd.common.result.Result;
import com.yd.oss.feign.request.ApiBatchSaveFilesRequest;
import com.yd.oss.feign.response.ApiBatchSaveFilesResponse;
import com.yd.oss.feign.response.ApiGetStsTokenResponse;
public interface ApiOssStsService {
Result<ApiGetStsTokenResponse> getStsToken(String projectBizId);
Result<ApiBatchSaveFilesResponse> batchSaveFiles(ApiBatchSaveFilesRequest request);
}
package com.yd.oss.api.service.impl;
import com.aliyun.oss.OSS;
import com.yd.common.exception.BusinessException;
import com.yd.common.result.Result;
import com.yd.oss.api.service.ApiExcelService;
......@@ -10,10 +11,9 @@ import com.yd.oss.feign.response.ApiOssExportAppointmentExcelResponse;
import com.yd.oss.feign.dto.ExportParam;
import com.yd.oss.feign.dto.ExportResult;
import com.yd.oss.feign.result.ImportResult;
import com.yd.oss.service.service.AppointmentExcelService;
import com.yd.oss.service.service.ExcelExportService;
import com.yd.oss.service.service.ExcelImportService;
import com.yd.oss.service.service.ExcelParserService;
import com.yd.oss.service.config.OssClientFactory;
import com.yd.oss.service.model.OssProvider;
import com.yd.oss.service.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
......@@ -41,6 +41,12 @@ public class ApiExcelServiceImpl implements ApiExcelService {
@Autowired
private ExcelImportService excelImportService;
@Autowired
private OssClientFactory ossClientFactory;
@Autowired
private IOssProviderService ossProviderService;
/**
* 导出excel-预约信息
* @param request
......@@ -49,9 +55,17 @@ public class ApiExcelServiceImpl implements ApiExcelService {
@Override
public Result<ApiOssExportAppointmentExcelResponse> exportAppointment(ApiOssExportAppointmentExcelRequest request) {
ApiOssExportAppointmentExcelResponse response = new ApiOssExportAppointmentExcelResponse();
// 根据项目ID获取服务商信息
OssProvider provider = ossProviderService.getProviderByProjectId(request.getProjectBizId());
if (provider == null) {
log.error("未找到项目对应的OSS服务商,projectBizId={}", request.getProjectBizId());
return Result.fail("未找到对应的OSS服务商配置");
}
OSS tempClient = ossClientFactory.createOssClient(provider);
String url = appointmentExcelService.exportAppointment(request.getData(),
request.getTemplateType(),
request.getAppointmentBizId());
request.getAppointmentBizId(),
request.getProjectBizId());
response.setUrl(url);
return Result.success(response);
}
......
package com.yd.oss.api.service.impl;
import com.yd.auth.core.dto.AuthUserDto;
import com.yd.auth.core.utils.SecurityUtil;
import com.yd.common.enums.CommonEnum;
import com.yd.common.enums.ResultCode;
import com.yd.common.exception.BusinessException;
......@@ -12,6 +14,7 @@ import com.yd.oss.service.dto.OssFileDto;
import com.yd.oss.service.model.OssFile;
import com.yd.oss.service.model.RelObjectMaterial;
import com.yd.oss.service.service.IOssFileService;
import com.yd.oss.service.service.OssService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -28,6 +31,9 @@ public class ApiOssFileServiceImpl implements ApiOssFileService {
@Autowired
private IOssFileService iOssFileService;
@Autowired
private OssService ossService;
/**
* 列表查询-文件元数据信息
* @param request
......@@ -45,10 +51,13 @@ public class ApiOssFileServiceImpl implements ApiOssFileService {
* @return
*/
@Override
public Result del(String fileBizId) {
Result<OssFile> result = checkOssFileIsExist(fileBizId);
OssFile ossFile = result.getData();
iOssFileService.removeById(ossFile.getId());
public Result del(String fileBizId, String projectBizId) {
// 获取当前操作人
AuthUserDto authUser = SecurityUtil.getCurrentLoginUser();
String operator = authUser != null ? authUser.getUsername() : "system";
// 调用OSS服务删除文件(包含OSS删除和数据库物理删除)
ossService.deleteFileByBizId(fileBizId, operator);
return Result.success();
}
......
package com.yd.oss.api.service.impl;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.yd.common.exception.BusinessException;
import com.yd.common.result.Result;
import com.yd.oss.api.service.ApiOssService;
import com.yd.oss.feign.request.ApiUploadFileRequest;
......@@ -42,17 +43,13 @@ public class ApiOssServiceImpl implements ApiOssService {
* @return
*/
@Override
public Result<ApiUploadResponse> uploadFile(MultipartFile file, String bucket, String provider, String uploadUser) {
public Result<ApiUploadResponse> uploadFile(MultipartFile file, String bucket, String provider, String uploadUser,String projectBizId) {
ApiUploadResponse response = new ApiUploadResponse();
try {
// 如果指定了提供商,则切换
if (StringUtils.isNotBlank(provider)) {
ossService.switchProvider(provider);
}
// 上传文件
OssUploadFileResDto ossUploadFileDto = ossService.uploadFile(file.getInputStream(), file.getOriginalFilename(),
bucket, uploadUser, FileUtil.getFileType(file.getOriginalFilename()));
bucket, uploadUser, FileUtil.getFileType(file.getOriginalFilename()),projectBizId);
// 获取上传结果(包含文件信息和访问URL)
UploadResult result = ossService.getUploadResult(ossUploadFileDto.getFileKey(), Duration.ofHours(1));
......
package com.yd.oss.api.service.impl;
import com.aliyun.oss.OSS;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.sts.model.v20150401.AssumeRoleRequest;
import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse;
import com.yd.auth.core.dto.AuthUserDto;
import com.yd.auth.core.utils.SecurityUtil;
import com.yd.common.enums.CommonEnum;
import com.yd.common.result.Result;
import com.yd.common.utils.RandomStringGenerator;
import com.yd.oss.api.service.ApiOssStsService;
import com.yd.oss.service.config.OssClientFactory;
import com.yd.oss.service.model.OssProvider;
import com.yd.oss.service.service.IOssProviderService;
import com.yd.oss.service.utils.OssUtils;
import com.yd.oss.feign.dto.ApiOssFileDto;
import com.yd.oss.feign.request.ApiBatchSaveFilesRequest;
import com.yd.oss.feign.response.ApiBatchSaveFilesResponse;
import com.yd.oss.feign.response.ApiGetStsTokenResponse;
import com.yd.oss.feign.response.ApiOssFileResponse;
import com.yd.oss.service.model.OssFile;
import com.yd.oss.service.service.IOssFileService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@Service
public class ApiOssStsServiceImpl implements ApiOssStsService {
@Value("${aliyun.oss.role-arn:acs:ram::1164861558430519:role/oss-upload-role}")
private String roleArn;
// 注入最大文件大小配置,默认100MB
@Value("${aliyun.oss.max-size:104857600}")
private Long maxSize;
@Autowired
private IOssProviderService ossProviderService;
@Autowired
private IOssFileService iOssFileService;
@Autowired
private OssUtils ossUtils;
@Autowired
private OssClientFactory ossClientFactory; // 如果需要执行OSS操作时使用
/**
* 获取STS临时凭证
* @param projectBizId 项目业务ID,用于确定使用的OSS服务商
* @return STS凭证响应
*/
@Override
public Result<ApiGetStsTokenResponse> getStsToken(String projectBizId) {
// 根据项目ID获取服务商信息
OssProvider provider = ossProviderService.getProviderByProjectId(projectBizId);
if (provider == null) {
log.error("未找到项目对应的OSS服务商,projectBizId={}", projectBizId);
return Result.fail("未找到对应的OSS服务商配置");
}
String region = provider.getRegion();
String accessKeyId = provider.getAccessKey();
String accessKeySecret = provider.getSecretKey();
String bucket = provider.getBucketName();
String endpoint = provider.getEndpoint();
// 检查bucket是否存在
// 如果需要检查,可以用ossClientFactory创建临时客户端执行操作,然后关闭
OSS tempClient = ossClientFactory.createOssClient(provider);
try {
if (!tempClient.doesBucketExist(bucket)) {
log.warn("当前服务商的桶 {} 不存在,请确认配置", bucket);
}
} finally {
tempClient.shutdown();
}
// 构建STS客户端
DefaultProfile profile = DefaultProfile.getProfile(region, accessKeyId, accessKeySecret);
IAcsClient client = new DefaultAcsClient(profile);
// 构建请求
AssumeRoleRequest request = new AssumeRoleRequest();
request.setRoleArn(roleArn);
request.setRoleSessionName("vue-upload-session");
request.setDurationSeconds(3600L);
try {
AssumeRoleResponse response = client.getAcsResponse(request);
if (response == null || response.getCredentials() == null) {
return Result.fail("获取STS凭证失败:返回凭证为空");
}
AssumeRoleResponse.Credentials credentials = response.getCredentials();
ApiGetStsTokenResponse tokenResponse = new ApiGetStsTokenResponse();
tokenResponse.setAccessKeyId(credentials.getAccessKeyId());
tokenResponse.setAccessKeySecret(credentials.getAccessKeySecret());
tokenResponse.setStsToken(credentials.getSecurityToken());
tokenResponse.setRegion(region);
tokenResponse.setBucket(bucket);
tokenResponse.setEndpoint(endpoint);
tokenResponse.setMaxSize(maxSize);
// 获取分片文件路径的前缀
String filePrefix = ossUtils.getShardFilePrefix(projectBizId, "");
tokenResponse.setFilePrefix(filePrefix);
return Result.success(tokenResponse);
} catch (ClientException e) {
log.error("获取STS凭证失败", e);
return Result.fail("获取STS凭证失败: " + e.getMessage());
}
}
/**
* 批量保存文件列表(仅保存元数据,不涉及OSS操作)
*
* @param request 批量保存请求
* @return 保存结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Result<ApiBatchSaveFilesResponse> batchSaveFiles(ApiBatchSaveFilesRequest request) {
// 根据 projectBizId 获取服务商信息(用于填充providerBizId和bucketName)
OssProvider provider = ossProviderService.getProviderByProjectId(request.getProjectBizId());
if (provider == null) {
log.error("批量保存文件时未找到服务商配置,projectBizId={}", request.getProjectBizId());
return Result.fail("未找到对应的OSS服务商配置");
}
String providerBizId = provider.getProviderBizId();
String bucketName = provider.getBucketName();
// 获取当前登录用户信息
AuthUserDto authUserDto = SecurityUtil.getCurrentLoginUser();
String userId = authUserDto.getUserBizId();
String userName = authUserDto.getUsername();
List<OssFile> saveOssFileList = new ArrayList<>();
List<ApiOssFileResponse> fileResponses = new ArrayList<>();
// 遍历处理文件列表
for (ApiOssFileDto fileDto : request.getApiOssFileDtoList()) {
try {
// 从完整 URL 中提取 fileKey(相对路径)
String fileUrl = fileDto.getFileUrl();
if (StringUtils.isBlank(fileUrl)) {
log.warn("文件URL为空,跳过:{}", fileDto);
continue;
}
URL url = new URL(fileUrl);
String path = url.getPath();
String fileKey = path.startsWith("/") ? path.substring(1) : path;
// 生成文件业务ID
String fileBizId = RandomStringGenerator.generateBizId16(CommonEnum.UID_TYPE_OSS_FILE.getCode());
// 获取文件类型(扩展名)
String originalName = fileDto.getFileName();
String fileType = FilenameUtils.getExtension(originalName);
if (StringUtils.isBlank(fileType)) {
fileType = FilenameUtils.getExtension(fileKey);
}
// 构建实体
OssFile file = new OssFile();
file.setTenantBizId(request.getTenantBizId());
file.setProjectBizId(request.getProjectBizId());
file.setFileBizId(fileBizId);
file.setObjectType(request.getObjectType());
file.setObjectTableName(request.getObjectTableName());
file.setObjectName(request.getObjectName());
file.setObjectBizId(request.getObjectBizId());
file.setFileKey(fileKey);
file.setOriginalName(originalName);
file.setFileSize(fileDto.getFileSize());
file.setFileType(fileType);
file.setProviderBizId(providerBizId);
file.setBucketName(bucketName);
file.setUploadTime(LocalDateTime.now());
file.setUploadUser(userId);
file.setCreatorId(userId);
file.setCreatorName(userName);
file.setUpdaterId(userId);
file.setCreateTime(LocalDateTime.now());
file.setUpdateTime(LocalDateTime.now());
file.setIsDeleted(0);
saveOssFileList.add(file);
// 构建响应对象(直接使用传入的完整URL)
ApiOssFileResponse ossFileResponse = new ApiOssFileResponse();
ossFileResponse.setFileBizId(file.getFileBizId());
ossFileResponse.setFileName(file.getOriginalName());
ossFileResponse.setFileSize(file.getFileSize());
ossFileResponse.setFileUrl(fileDto.getFileUrl()); // 设置完整URL
fileResponses.add(ossFileResponse);
} catch (MalformedURLException e) {
log.error("文件URL格式错误: {}", fileDto.getFileUrl(), e);
return Result.fail("文件URL格式错误: " + fileDto.getFileUrl());
} catch (Exception e) {
log.error("保存文件记录失败: {}", fileDto, e);
return Result.fail("保存文件记录失败: " + e.getMessage());
}
}
// 批量保存
if (CollectionUtils.isNotEmpty(saveOssFileList)) {
iOssFileService.saveOrUpdateBatch(saveOssFileList);
// 构建响应
ApiBatchSaveFilesResponse response = new ApiBatchSaveFilesResponse();
response.setFileResponses(fileResponses);
return Result.success(response);
}
return Result.success();
}
}
\ No newline at end of file
......@@ -84,7 +84,6 @@
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter:3.1.1" level="project" />
<orderEntry type="library" name="Maven: org.springframework.security:spring-security-rsa:1.0.10.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.bouncycastle:bcpkix-jdk15on:1.68" level="project" />
<orderEntry type="library" name="Maven: org.bouncycastle:bcprov-jdk15on:1.69" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.13.2" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.13.2" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.13.2" level="project" />
......@@ -135,8 +134,6 @@
<orderEntry type="library" name="Maven: org.springdoc:springdoc-openapi-webmvc-core:1.7.0" level="project" />
<orderEntry type="library" name="Maven: org.springdoc:springdoc-openapi-common:1.7.0" level="project" />
<orderEntry type="library" name="Maven: io.swagger.core.v3:swagger-core:2.2.9" level="project" />
<orderEntry type="library" name="Maven: jakarta.xml.bind:jakarta.xml.bind-api:2.3.3" level="project" />
<orderEntry type="library" name="Maven: jakarta.activation:jakarta.activation-api:1.2.2" level="project" />
<orderEntry type="library" name="Maven: io.swagger.core.v3:swagger-annotations:2.2.9" level="project" />
<orderEntry type="library" name="Maven: io.swagger.core.v3:swagger-models:2.2.9" level="project" />
<orderEntry type="library" name="Maven: org.webjars:swagger-ui:4.18.2" level="project" />
......@@ -196,22 +193,30 @@
<orderEntry type="library" name="Maven: io.github.openfeign:feign-slf4j:11.8" level="project" />
<orderEntry type="library" name="Maven: com.aliyun.oss:aliyun-sdk-oss:3.15.1" level="project" />
<orderEntry type="library" name="Maven: org.apache.httpcomponents:httpclient:4.5.13" level="project" />
<orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore:4.4.15" level="project" />
<orderEntry type="library" name="Maven: org.jdom:jdom2:2.0.6.1" level="project" />
<orderEntry type="library" name="Maven: org.codehaus.jettison:jettison:1.1" level="project" />
<orderEntry type="library" name="Maven: stax:stax-api:1.0.1" level="project" />
<orderEntry type="library" name="Maven: com.aliyun:aliyun-java-sdk-core:4.5.10" level="project" />
<orderEntry type="library" name="Maven: com.aliyun:aliyun-java-sdk-ram:3.1.0" level="project" />
<orderEntry type="library" name="Maven: com.aliyun:aliyun-java-sdk-kms:2.11.0" level="project" />
<orderEntry type="library" name="Maven: com.aliyun:aliyun-java-sdk-sts:3.0.0" level="project" />
<orderEntry type="library" name="Maven: com.aliyun:aliyun-java-sdk-core:4.6.0" level="project" />
<orderEntry type="library" name="Maven: com.google.code.gson:gson:2.8.9" level="project" />
<orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore:4.4.15" level="project" />
<orderEntry type="library" name="Maven: commons-logging:commons-logging:1.2" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.14.0" level="project" />
<orderEntry type="library" name="Maven: javax.xml.bind:jaxb-api:2.3.1" level="project" />
<orderEntry type="library" name="Maven: javax.activation:javax.activation-api:1.2.0" level="project" />
<orderEntry type="library" name="Maven: org.jacoco:org.jacoco.agent:runtime:0.8.5" level="project" />
<orderEntry type="library" name="Maven: org.glassfish.jaxb:jaxb-runtime:2.3.6" level="project" />
<orderEntry type="library" name="Maven: jakarta.xml.bind:jakarta.xml.bind-api:2.3.3" level="project" />
<orderEntry type="library" name="Maven: org.glassfish.jaxb:txw2:2.3.6" level="project" />
<orderEntry type="library" name="Maven: com.sun.istack:istack-commons-runtime:3.0.12" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: com.sun.activation:jakarta.activation:1.2.2" level="project" />
<orderEntry type="library" name="Maven: org.bouncycastle:bcprov-jdk15on:1.69" level="project" />
<orderEntry type="library" name="Maven: org.jacoco:org.jacoco.agent:runtime:0.8.7" level="project" />
<orderEntry type="library" name="Maven: org.ini4j:ini4j:0.5.4" level="project" />
<orderEntry type="library" name="Maven: io.opentracing:opentracing-api:0.33.0" level="project" />
<orderEntry type="library" name="Maven: io.opentracing:opentracing-util:0.33.0" level="project" />
<orderEntry type="library" name="Maven: io.opentracing:opentracing-noop:0.33.0" level="project" />
<orderEntry type="library" name="Maven: com.aliyun:aliyun-java-sdk-ram:3.1.0" level="project" />
<orderEntry type="library" name="Maven: com.aliyun:aliyun-java-sdk-kms:2.11.0" level="project" />
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-core:9.0.60" level="project" />
<orderEntry type="library" name="Maven: org.apache.tomcat:tomcat-annotations-api:9.0.60" level="project" />
<orderEntry type="library" name="Maven: com.documents4j:documents4j-local:1.1.12" level="project" />
......@@ -237,7 +242,6 @@
<orderEntry type="library" name="Maven: org.apache.xmlbeans:xmlbeans:5.1.1" level="project" />
<orderEntry type="library" name="Maven: cn.afterturn:easypoi-base:4.4.0" level="project" />
<orderEntry type="library" name="Maven: org.apache.poi:poi-ooxml-schemas:4.1.1" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.14.0" level="project" />
<orderEntry type="library" name="Maven: ognl:ognl:3.2.6" level="project" />
<orderEntry type="library" name="Maven: org.javassist:javassist:3.20.0-GA" level="project" />
<orderEntry type="library" name="Maven: javax.validation:validation-api:2.0.1.Final" level="project" />
......
......@@ -11,6 +11,7 @@ import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.constraints.NotBlank;
import java.util.List;
/**
......@@ -24,8 +25,9 @@ public interface ApiOssFeignClient {
* @param file 上传的文件
* @return
*/
@PostMapping("/upload")
Result<ApiUploadResponse> uploadFile(@RequestParam("file") MultipartFile file);
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
Result<ApiUploadResponse> uploadFile(@RequestPart("file") MultipartFile file,
@RequestParam(value = "projectBizId", required = false) String projectBizId);
/**
* 上传文件 - body入参方式(新,推荐)
......
......@@ -33,6 +33,7 @@ public interface ApiOssFileFeignClient {
* @return
*/
@DeleteMapping("/del")
Result del(@NotBlank(message = "文件元数据表唯一业务ID不能为空") @RequestParam(value = "fileBizId") String fileBizId);
Result del(@NotBlank(message = "文件元数据表唯一业务ID不能为空") @RequestParam(value = "fileBizId") String fileBizId,
@RequestParam("projectBizId") String projectBizId);
}
package com.yd.oss.feign.client;
import com.yd.common.result.Result;
import com.yd.oss.feign.fallback.ApiOssStsFeignFallbackFactory;
import com.yd.oss.feign.request.ApiBatchSaveFilesRequest;
import com.yd.oss.feign.response.ApiBatchSaveFilesResponse;
import com.yd.oss.feign.response.ApiGetStsTokenResponse;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
/**
* OSS-分片服务信息Feign客户端
*/
@FeignClient(name = "yd-oss-api", fallbackFactory = ApiOssStsFeignFallbackFactory.class)
public interface ApiOssStsFeignClient {
/**
* 获取STS凭证
* @param projectBizId
* @return
*/
@GetMapping("/sts-token")
Result<ApiGetStsTokenResponse> getStsToken(@RequestParam(value = "projectBizId") String projectBizId);
/**
* 批量保存文件列表
* @return
*/
@PostMapping("/batch/save/files")
Result<ApiBatchSaveFilesResponse> batchSaveFiles(@Validated @RequestBody ApiBatchSaveFilesRequest request);
}
package com.yd.oss.feign.dto;
import lombok.Data;
@Data
public class ApiOssFileDto {
/**
* 文件名(原始文件名)
*/
private String fileName;
/**
* 文件大小
*/
private Long fileSize;
/**
* 上传文件路径(完整路径)
*/
private String fileUrl;
}
......@@ -23,7 +23,7 @@ public class ApiOssFeignFallbackFactory implements FallbackFactory<ApiOssFeignCl
public ApiOssFeignClient create(Throwable cause) {
return new ApiOssFeignClient() {
@Override
public Result<ApiUploadResponse> uploadFile(MultipartFile file) {
public Result<ApiUploadResponse> uploadFile(MultipartFile file,String projectBizId) {
return null;
}
......
......@@ -24,7 +24,7 @@ public class ApiOssFileFeignFallbackFactory implements FallbackFactory<ApiOssFil
}
@Override
public Result del(String fileBizId) {
public Result del(String fileBizId,String projectBizId) {
return null;
}
};
......
package com.yd.oss.feign.fallback;
import com.yd.common.result.Result;
import com.yd.oss.feign.client.ApiOssStsFeignClient;
import com.yd.oss.feign.request.ApiBatchSaveFilesRequest;
import com.yd.oss.feign.response.ApiBatchSaveFilesResponse;
import com.yd.oss.feign.response.ApiGetStsTokenResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestParam;
/**
* OSS服务信息Feign降级处理
*/
@Slf4j
@Component
public class ApiOssStsFeignFallbackFactory implements FallbackFactory<ApiOssStsFeignClient> {
@Override
public ApiOssStsFeignClient create(Throwable cause) {
return new ApiOssStsFeignClient() {
@Override
public Result<ApiGetStsTokenResponse> getStsToken(String projectBizId) {
return null;
}
@Override
public Result<ApiBatchSaveFilesResponse> batchSaveFiles(ApiBatchSaveFilesRequest request) {
return null;
}
};
}
}
package com.yd.oss.feign.request;
import com.yd.oss.feign.dto.ApiOssFileDto;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import java.util.List;
@Data
public class ApiBatchSaveFilesRequest {
/**
* 所属租户ID(业务id)
*/
@NotBlank(message = "所属租户ID不能为空")
private String tenantBizId;
/**
* 所属项目ID(业务id)
*/
@NotBlank(message = "所属项目ID不能为空")
private String projectBizId;
/**
* 项目类型类型(默认pc)
*/
private String projectType = "pc";
/**
* 对象类型
*/
private String objectType;
/**
* 对象所属表名(预约表、新单跟进表等)
*/
private String objectTableName;
/**
* 对象名
*/
private String objectName;
/**
* 对象业务ID
*/
@NotBlank(message = "对象业务ID不能为空")
private String objectBizId;
/**
* 上传的文件对象列表
*/
@NotEmpty(message = "上传的文件对象列表不能为空")
private List<ApiOssFileDto> apiOssFileDtoList;
}
......@@ -23,4 +23,9 @@ public class ApiOssExportAppointmentExcelRequest {
* 预约信息主表唯一业务ID
*/
private String appointmentBizId;
/**
* 项目ID
*/
private String projectBizId;
}
......@@ -8,6 +8,11 @@ import javax.validation.constraints.NotBlank;
public class ApiUploadFileRequest {
/**
* 项目ID
*/
private String projectBizId;
/**
* 对象类型
*/
private String objectType;
......
package com.yd.oss.feign.response;
import lombok.Data;
import java.util.List;
@Data
public class ApiBatchSaveFilesResponse {
private List<ApiOssFileResponse> fileResponses;
}
package com.yd.oss.feign.response;
import lombok.Data;
@Data
public class ApiGetStsTokenResponse {
private String accessKeyId;
private String accessKeySecret;
private String stsToken;
private String region;
private String bucket;
private String endpoint;
private Long maxSize;
/**
* 文件前缀
*/
private String filePrefix;
}
package com.yd.oss.feign.response;
import lombok.Data;
@Data
public class ApiOssFileResponse {
/**
* 文件元数据表唯一业务ID(不传值)
*/
private String fileBizId;
/**
* 文件名(原始文件名)
*/
private String fileName;
/**
* 文件大小
*/
private Long fileSize;
/**
* 上传文件路径(完整路径)
*/
private String fileUrl;
}
......@@ -80,6 +80,18 @@
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
</dependency>
<!-- 阿里云 STS SDK -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-sts</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
......
//package com.yd.oss.service.config;
//
//import com.aliyun.oss.OSS;
//import com.yd.oss.service.service.OssService;
//
//import java.lang.reflect.InvocationHandler;
//import java.lang.reflect.Method;
//import java.lang.reflect.Proxy;
//
//public class DynamicOssClientProxy implements InvocationHandler {
// private final OssService ossService;
//
// public DynamicOssClientProxy(OssService ossService) {
// this.ossService = ossService;
// }
//
// public static OSS createProxy(OssService ossService) {
// return (OSS) Proxy.newProxyInstance(
// DynamicOssClientProxy.class.getClassLoader(),
// new Class[]{OSS.class},
// new DynamicOssClientProxy(ossService)
// );
// }
//
// @Override
// public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// // 每次方法调用都从 OssService 获取当前客户端
// OSS currentClient = ossService.getOssClient();
// return method.invoke(currentClient, args);
// }
//}
\ No newline at end of file
package com.yd.oss.service.config;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.yd.oss.service.model.OssProvider;
import com.yd.oss.service.service.IOssProviderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class OssClientFactory {
@Autowired
private IOssProviderService ossProviderService;
/**
* 根据服务商业务ID创建临时OSS客户端
* @param providerBizId 服务商业务ID
* @return OSS客户端(使用后必须调用shutdown())
*/
public OSS createOssClient(String providerBizId) {
OssProvider provider = ossProviderService.getProviderByBizId(providerBizId);
return new OSSClientBuilder().build(
provider.getEndpoint(),
provider.getAccessKey(),
provider.getSecretKey()
);
}
/**
* 根据项目ID创建临时OSS客户端
* @param projectBizId 项目业务ID
* @return OSS客户端(使用后必须调用shutdown())
*/
public OSS createOssClientByProject(String projectBizId) {
OssProvider provider = ossProviderService.getProviderByProjectId(projectBizId);
return new OSSClientBuilder().build(
provider.getEndpoint(),
provider.getAccessKey(),
provider.getSecretKey()
);
}
/**
* 直接根据OssProvider对象创建客户端(如果调用者已持有provider)
*/
public OSS createOssClient(OssProvider provider) {
return new OSSClientBuilder().build(
provider.getEndpoint(),
provider.getAccessKey(),
provider.getSecretKey()
);
}
}
\ No newline at end of file
......@@ -4,6 +4,7 @@ import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.yd.oss.service.model.OssProvider;
import com.yd.oss.service.service.IOssProviderService;
import com.yd.oss.service.service.OssService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
......@@ -48,6 +49,12 @@ public class OssConfig {
);
}
// @Bean
// public OSS ossClient(OssService ossService) {
// // 返回动态代理,每次调用都委托给 ossService.getOssClient()
// return DynamicOssClientProxy.createProxy(ossService);
// }
@Bean
public String defaultBucket() {
return defaultBucket;
......
......@@ -15,6 +15,11 @@ import lombok.NoArgsConstructor;
public class OssUploadFileReqDto {
/**
* 项目ID
*/
private String projectBizId;
/**
* 对象类型
*/
private String objectType;
......
......@@ -28,6 +28,18 @@ public class OssFile implements Serializable {
private Long id;
/**
* 所属租户ID(业务id)
*/
@TableField("tenant_biz_id")
private String tenantBizId;
/**
* 所属项目ID(业务id)
*/
@TableField("project_biz_id")
private String projectBizId;
/**
* 对象类型
*/
@TableField("object_type")
......
......@@ -34,6 +34,18 @@ public class OssProvider implements Serializable {
private String providerBizId;
/**
* 所属租户ID(业务id)
*/
@TableField("tenant_biz_id")
private String tenantBizId;
/**
* 所属项目ID(业务id)
*/
@TableField("project_biz_id")
private String projectBizId;
/**
* 服务商编码(业务编码)
*/
@TableField("code")
......
......@@ -6,5 +6,6 @@ public interface AppointmentExcelService {
String exportAppointment(Map<String, Object> data,
String templateType,
String appointmentBizId);
String appointmentBizId,
String projectBizId);
}
......@@ -21,9 +21,13 @@ public interface IOssProviderService extends IService<OssProvider> {
OssProvider getProviderById(Long id);
OssProvider getProviderByBizId(String providerBizId);
List<OssProvider> getActiveProviders();
boolean saveProvider(OssProvider provider);
boolean deleteProvider(Long id);
OssProvider getProviderByProjectId(String projectBizId);
}
package com.yd.oss.service.service;
import com.yd.oss.feign.request.ApiUploadFileRequest;
import com.aliyun.oss.OSS;
import com.yd.oss.service.dto.FileMetadata;
import com.yd.oss.service.dto.OssUploadFileReqDto;
import com.yd.oss.service.dto.OssUploadFileResDto;
......@@ -16,7 +16,7 @@ import java.time.Duration;
public interface OssService {
// 上传文件
OssUploadFileResDto uploadFile(InputStream inputStream, String fileName, String bucketName, String uploadUser, String type);
OssUploadFileResDto uploadFile(InputStream inputStream, String fileName, String bucketName, String uploadUser, String type,String projectBizId);
OssUploadFileResDto uploadFileBody(MultipartFile file, OssUploadFileReqDto reqDto);
......@@ -32,6 +32,8 @@ public interface OssService {
// 删除文件
boolean deleteFile(String fileKey, String bucketName, String operator);
boolean deleteFileByBizId(String fileBizId, String operator);
// 删除文件(使用默认存储桶)
boolean deleteFile(String fileKey, String operator);
......@@ -53,12 +55,17 @@ public interface OssService {
// 获取文件元数据(使用默认存储桶)
FileMetadata getFileMetadata(String fileKey);
void switchProviderByProjectId(String projectBizId);
// 切换OSS提供商
void switchProvider(String providerName);
// 切换OSS提供商(通过ID)
void switchProvider(Long providerId);
//获取当前使用的 OSS 客户端
OSS getOssClient();
// 获取当前使用的提供商
OssProvider getCurrentProvider();
......
package com.yd.oss.service.service.impl;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.CannedAccessControlList;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.ObjectMetadata;
......@@ -13,6 +12,7 @@ import com.yd.common.exception.BusinessException;
import com.yd.common.utils.IpUtil;
import com.yd.common.utils.RandomStringGenerator;
import com.yd.oss.feign.request.ApiUploadFileRequest;
import com.yd.oss.service.config.OssClientFactory;
import com.yd.oss.service.dto.FileMetadata;
import com.yd.oss.service.dto.OssUploadFileReqDto;
import com.yd.oss.service.dto.OssUploadFileResDto;
......@@ -24,6 +24,7 @@ import com.yd.oss.service.service.IOssFileService;
import com.yd.oss.service.service.IOssProviderService;
import com.yd.oss.service.service.OssService;
import com.yd.oss.service.utils.FileUtil;
import com.yd.oss.service.utils.OssUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -49,16 +50,15 @@ public class AliYunOssServiceImpl implements OssService {
private IOssFileService ossFileService;
@Autowired
private OSS ossClient; // 注入OSS客户端
private OssClientFactory ossClientFactory;
@Autowired
private String defaultBucket; // 注入默认存储桶
private OssUtils ossUtils;
@Autowired
private String defaultEndpoint; // 注入默认服务端点
@Autowired
private OssProvider currentProvider; // 注入当前提供商
// 默认提供商(用于没有传入项目ID或提供商ID的情况)
private OssProvider getDefaultProvider() {
return ossProviderService.getDefaultProvider();
}
/**
* 上传文件
......@@ -67,106 +67,25 @@ public class AliYunOssServiceImpl implements OssService {
* @param bucketName OSS桶名
* @param uploadUser 操作用户
* @param type oss文件夹分类目录类型,例:如果是.xls那就是excel/
* @param projectBizId 项目业务ID(用于确定OSS提供商)
* @return
*/
@Override
public OssUploadFileResDto uploadFile(InputStream inputStream, String fileName,
String bucketName, String uploadUser, String type) {
//获取Security上下文当前用户的登录信息
AuthUserDto authUserDto = SecurityUtil.getCurrentLoginUser();
OssUploadFileResDto ossUploadFileDto = new OssUploadFileResDto();
long startTime = System.currentTimeMillis(); // 记录开始时间
String operationResult = "success"; // 操作结果
String errorMessage = null; // 错误信息
OssFile ossFile = null; // 文件记录
String bucketName, String uploadUser, String type, String projectBizId) {
// 获取提供商
OssProvider provider = ossProviderService.getProviderByProjectId(projectBizId);
if (provider == null) {
throw new BusinessException("未找到项目对应的OSS提供商,projectBizId=" + projectBizId);
}
OSS ossClient = null;
try {
// 读取输入流到字节数组
byte[] fileData = FileUtil.readInputStreamToBytes(inputStream);
long fileSize = fileData.length;
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(fileData);
// 生成文件key
String fileKey = FileUtil.generateFileKey(fileName,type);
String actualBucket = StringUtils.isNotBlank(bucketName) ? bucketName : defaultBucket;
String fileType = FileUtil.getFileType(fileName);
// 创建上传请求
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(fileSize);
metadata.setContentType(FileUtil.getContentType(fileType));
// 设置公共读权限(如果需要)
// metadata.setObjectAcl(CannedAccessControlList.PublicRead);
PutObjectRequest putObjectRequest = new PutObjectRequest(
actualBucket, fileKey, byteArrayInputStream, metadata);
// 上传文件
ossClient.putObject(putObjectRequest);
// 创建文件记录
ossFile = new OssFile();
ossFile.setFileBizId(RandomStringGenerator.generateBizId16(CommonEnum.UID_TYPE_OSS_FILE.getCode()));
ossFile.setFileKey(fileKey);
ossFile.setOriginalName(fileName);
ossFile.setFileSize(fileSize);
ossFile.setFileType(fileType);
ossFile.setProviderBizId(currentProvider.getProviderBizId());
ossFile.setBucketName(actualBucket);
ossFile.setUploadUser(uploadUser);
ossFile.setCreatorId(authUserDto.getUserBizId());
ossFile.setCreatorName(authUserDto.getUsername());
// 保存文件元数据到数据库
ossFileService.saveFileMetadata(ossFile);
// 创建操作日志
OssOperationLog operationLog = new OssOperationLog();
operationLog.setFileBizId(ossFile.getFileBizId());
operationLog.setOperationType("upload");
operationLog.setOperationUser(uploadUser);
operationLog.setOperationResult(operationResult);
operationLog.setErrorMessage(errorMessage);
operationLog.setClientIp(IpUtil.getClientIp());
operationLog.setCostTime(System.currentTimeMillis() - startTime);
// 保存操作日志到数据库
ossFileService.saveOperationLog(operationLog);
log.info("文件上传成功: {} -> {}/{}", fileName, actualBucket, fileKey);
ossUploadFileDto.setFileBizId(ossFile.getFileBizId());
ossUploadFileDto.setFileKey(fileKey);
ossUploadFileDto.setOriginalName(fileName);
// 如果设置了公共读,可以直接拼接URL
String publicUrl = "https://" + actualBucket + "." + defaultEndpoint + "/" + fileKey;
ossUploadFileDto.setUrl(publicUrl);
return ossUploadFileDto;
} catch (Exception e) {
operationResult = "failure"; // 标记操作失败
errorMessage = e.getMessage(); // 记录错误信息
log.error("阿里云OSS上传文件失败: {}", fileName, e);
throw new RuntimeException("阿里云OSS上传文件失败: " + fileName, e);
ossClient = ossClientFactory.createOssClient(provider);
return doUploadFile(ossClient, provider, inputStream, fileName, bucketName, uploadUser, type, null, null, null, null, null, null);
} finally {
// 关闭资源
FileUtil.closeQuietly(inputStream);
// 记录失败日志
if ("failure".equals(operationResult)) {
OssOperationLog operationLog = new OssOperationLog();
if (ossFile != null) {
operationLog.setFileBizId(ossFile.getFileBizId());
} else {
operationLog.setFileBizId("-1"); // 使用无效文件ID
}
operationLog.setOperationType("upload");
operationLog.setOperationUser(uploadUser);
operationLog.setOperationResult(operationResult);
operationLog.setErrorMessage(errorMessage);
operationLog.setClientIp(IpUtil.getClientIp());
operationLog.setCostTime(System.currentTimeMillis() - startTime);
// 保存操作日志到数据库
ossFileService.saveOperationLog(operationLog);
if (ossClient != null) {
ossClient.shutdown();
}
}
}
......@@ -179,51 +98,88 @@ public class AliYunOssServiceImpl implements OssService {
*/
@Override
public OssUploadFileResDto uploadFileBody(MultipartFile file, OssUploadFileReqDto reqDto) {
//获取Security上下文当前用户的登录信息
AuthUserDto authUserDto = SecurityUtil.getCurrentLoginUser();
OssUploadFileResDto ossUploadFileDto = new OssUploadFileResDto();
// 记录开始时间
long startTime = System.currentTimeMillis();
// 操作结果
String operationResult = "success";
// 错误信息
String errorMessage = null;
// 文件记录
OssFile ossFile = null;
//文件输入流
InputStream inputStream = null;
//原始文件名
String fileName = file.getOriginalFilename();
//文件扩展名
String type = FileUtil.getFileType(file.getOriginalFilename());
// 获取提供商
OssProvider provider = ossProviderService.getProviderByProjectId(reqDto.getProjectBizId());
if (provider == null) {
throw new BusinessException("未找到项目对应的OSS提供商,projectBizId=" + reqDto.getProjectBizId());
}
OSS ossClient = null;
InputStream inputStream = null;
try {
inputStream = file.getInputStream();
ossClient = ossClientFactory.createOssClient(provider);
return doUploadFile(ossClient, provider, inputStream, file.getOriginalFilename(),
provider.getBucketName(), reqDto.getUploadUser(),
FileUtil.getFileType(file.getOriginalFilename()),
reqDto.getObjectBizId(), reqDto.getObjectName(),
reqDto.getObjectTableName(), reqDto.getObjectType(),
reqDto.getObjectBizId(), reqDto.getObjectName()); // 这里需要根据实际情况调整,原代码中存储了object信息
} catch (IOException e) {
e.printStackTrace();
throw new BusinessException("文件输入流解析异常:{}");
log.error("文件输入流解析异常", e);
throw new BusinessException("文件输入流解析异常");
} finally {
FileUtil.closeQuietly(inputStream);
if (ossClient != null) {
ossClient.shutdown();
}
}
}
/**
* 上传文件(使用默认存储桶)
* @param inputStream
* @param fileName
* @param uploadUser
* @return
*/
@Override
public String uploadFile(InputStream inputStream, String fileName, String uploadUser) {
// 使用默认提供商
OssProvider defaultProvider = getDefaultProvider();
OSS ossClient = null;
try {
ossClient = ossClientFactory.createOssClient(defaultProvider);
OssUploadFileResDto result = doUploadFile(ossClient, defaultProvider, inputStream, fileName,
defaultProvider.getBucketName(), uploadUser, null, null, null, null, null, null, null);
return result.getUrl();
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
/**
* 实际执行上传的逻辑(复用代码)
*/
private OssUploadFileResDto doUploadFile(OSS ossClient, OssProvider provider,
InputStream inputStream, String fileName,
String bucketName, String uploadUser, String type,
String objectBizId, String objectName,
String objectTableName, String objectType,
String creatorId, String creatorName) {
long startTime = System.currentTimeMillis();
String operationResult = "success";
String errorMessage = null;
OssFile ossFile = null;
OssUploadFileResDto ossUploadFileDto = new OssUploadFileResDto();
try {
// 读取输入流到字节数组
byte[] fileData = FileUtil.readInputStreamToBytes(inputStream);
long fileSize = fileData.length;
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(fileData);
// 生成文件key
String fileKey = FileUtil.generateFileKey(fileName,type);
//默认阿里云的桶名
String actualBucket = defaultBucket;
//获取文件扩展名
// 生成文件key(这里使用ossUtils的方法,需要传入projectBizId,但此时provider已知,可根据需要调整)
String fileKey = ossUtils.getFilePrefix(provider.getProviderBizId(), "", fileName, type);
String actualBucket = StringUtils.isNotBlank(bucketName) ? bucketName : provider.getBucketName();
String fileType = FileUtil.getFileType(fileName);
// 创建上传请求
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(fileSize);
metadata.setContentType(FileUtil.getContentType(fileType));
// 设置公共读权限(如果需要)
// metadata.setObjectAcl(CannedAccessControlList.PublicRead);
PutObjectRequest putObjectRequest = new PutObjectRequest(
actualBucket, fileKey, byteArrayInputStream, metadata);
......@@ -238,90 +194,67 @@ public class AliYunOssServiceImpl implements OssService {
ossFile.setOriginalName(fileName);
ossFile.setFileSize(fileSize);
ossFile.setFileType(fileType);
ossFile.setProviderBizId(currentProvider.getProviderBizId());
ossFile.setProviderBizId(provider.getProviderBizId());
ossFile.setBucketName(actualBucket);
ossFile.setUploadUser(reqDto.getUploadUser());
//存储对象信息
ossFile.setObjectBizId(reqDto.getObjectBizId());
ossFile.setObjectName(reqDto.getObjectName());
ossFile.setObjectTableName(reqDto.getObjectTableName());
ossFile.setObjectType(reqDto.getObjectType());
ossFile.setUploadUser(uploadUser);
ossFile.setObjectBizId(objectBizId);
ossFile.setObjectName(objectName);
ossFile.setObjectTableName(objectTableName);
ossFile.setObjectType(objectType);
// 获取当前登录用户信息(如果存在)
AuthUserDto authUserDto = SecurityUtil.getCurrentLoginUser();
if (authUserDto != null) {
ossFile.setCreatorId(authUserDto.getUserBizId());
ossFile.setCreatorName(authUserDto.getUsername());
// 保存文件元数据到数据库
} else {
ossFile.setCreatorId(creatorId);
ossFile.setCreatorName(creatorName);
}
ossFileService.saveFileMetadata(ossFile);
// 创建操作日志
// 记录成功操作日志
OssOperationLog operationLog = new OssOperationLog();
operationLog.setFileBizId(ossFile.getFileBizId());
operationLog.setOperationType("upload");
operationLog.setOperationUser(reqDto.getUploadUser());
operationLog.setOperationUser(uploadUser);
operationLog.setOperationResult(operationResult);
operationLog.setErrorMessage(errorMessage);
operationLog.setClientIp(IpUtil.getClientIp());
operationLog.setCostTime(System.currentTimeMillis() - startTime);
// 保存操作日志到数据库
ossFileService.saveOperationLog(operationLog);
log.info("文件上传成功: {} -> {}/{}", fileName, actualBucket, fileKey);
ossUploadFileDto.setFileBizId(ossFile.getFileBizId());
ossUploadFileDto.setFileKey(fileKey);
ossUploadFileDto.setOriginalName(fileName);
// 如果设置了公共读,可以直接拼接URL
String publicUrl = "https://" + actualBucket + "." + defaultEndpoint + "/" + fileKey;
// 构建访问URL(根据实际需要,可以是公共读或预签名URL)
String publicUrl = "https://" + actualBucket + "." + provider.getEndpoint() + "/" + fileKey;
ossUploadFileDto.setUrl(publicUrl);
ossUploadFileDto.setFileSize(fileSize);
ossUploadFileDto.setFileType(fileType);
return ossUploadFileDto;
} catch (Exception e) {
// 标记操作失败
operationResult = "failure";
// 记录错误信息
errorMessage = e.getMessage();
log.error("阿里云OSS上传文件失败: {}", fileName, e);
throw new RuntimeException("阿里云OSS上传文件失败: " + fileName, e);
log.error("OSS上传文件失败: {}", fileName, e);
throw new RuntimeException("OSS上传文件失败: " + fileName, e);
} finally {
// 关闭资源
FileUtil.closeQuietly(inputStream);
// 记录失败日志
// 记录失败日志(如果失败)
if ("failure".equals(operationResult)) {
OssOperationLog operationLog = new OssOperationLog();
if (ossFile != null) {
operationLog.setFileBizId(ossFile.getFileBizId());
} else {
// 使用无效文件ID
operationLog.setFileBizId("-1");
}
operationLog.setFileBizId(ossFile != null ? ossFile.getFileBizId() : "-1");
operationLog.setOperationType("upload");
operationLog.setOperationUser(reqDto.getUploadUser());
operationLog.setOperationUser(uploadUser);
operationLog.setOperationResult(operationResult);
operationLog.setErrorMessage(errorMessage);
operationLog.setClientIp(IpUtil.getClientIp());
operationLog.setCostTime(System.currentTimeMillis() - startTime);
// 保存操作日志到数据库
ossFileService.saveOperationLog(operationLog);
}
}
}
/**
* 上传文件(使用默认存储桶)
* @param inputStream
* @param fileName
* @param uploadUser
* @return
*/
@Override
public String uploadFile(InputStream inputStream, String fileName, String uploadUser) {
// 调用重载方法,使用默认存储桶
// return uploadFile(inputStream, fileName, defaultBucket, uploadUser);
return null;
}
/**
* 下载文件
* @param fileKey
* @param bucketName
......@@ -329,61 +262,46 @@ public class AliYunOssServiceImpl implements OssService {
*/
@Override
public InputStream downloadFile(String fileKey, String bucketName) {
long startTime = System.currentTimeMillis(); // 记录开始时间
String operationResult = "success"; // 操作结果
String errorMessage = null; // 错误信息
OssFile ossFile = null; // 文件记录
// 根据文件key获取提供商信息
OssFile ossFile = ossFileService.getFileByKey(fileKey);
if (ossFile == null) {
throw new BusinessException("文件不存在: " + fileKey);
}
OssProvider provider = ossProviderService.getProviderByBizId(ossFile.getProviderBizId());
if (provider == null) {
throw new BusinessException("提供商不存在: " + ossFile.getProviderBizId());
}
OSS ossClient = null;
try {
// 确定存储桶(如果未指定则使用默认存储桶)
String actualBucket = StringUtils.isNotBlank(bucketName) ? bucketName : defaultBucket;
ossClient = ossClientFactory.createOssClient(provider);
String actualBucket = StringUtils.isNotBlank(bucketName) ? bucketName : ossFile.getBucketName();
// 获取文件信息
ossFile = ossFileService.getFileByKey(fileKey);
// 从阿里云OSS下载文件
OSSObject ossObject = ossClient.getObject(actualBucket, fileKey);
InputStream inputStream = ossObject.getObjectContent();
// 创建操作日志
OssOperationLog operationLog = new OssOperationLog();
operationLog.setFileBizId(ossFile.getFileBizId());
operationLog.setOperationType("download");
operationLog.setOperationUser("system"); // 系统操作
operationLog.setOperationResult(operationResult);
operationLog.setErrorMessage(errorMessage);
operationLog.setClientIp(IpUtil.getClientIp());
operationLog.setCostTime(System.currentTimeMillis() - startTime);
// 保存操作日志到数据库
ossFileService.saveOperationLog(operationLog);
// 记录操作日志(成功)
recordOperationLog(ossFile.getFileBizId(), "download", "system", "success", null, System.currentTimeMillis());
log.info("文件下载成功: {}/{}", actualBucket, fileKey);
// 注意:返回的InputStream需要由调用者负责关闭,但OSS客户端必须在此方法内关闭?
// 不能关闭,因为流还未消费完。此处需要特殊处理:返回的流内部可能关联OSSClient,调用者使用后需主动关闭。
// 一个解决方案是使用包装流,在close时同时关闭OSSClient。但为简化,这里不处理,要求调用者显式关闭。
// 更好的做法是返回一个包装流,但需要改造。为了示例,我们保留原逻辑,但强调调用者需要关闭流并手动shutdown客户端。
// 由于客户端不能在这里关闭,我们不得不将客户端保留到流消费完。这会导致资源泄漏。
// 因此,建议将下载操作设计为直接返回字节数组或使用回调,或者使用预签名URL让客户端直接下载。
// 这里为了演示,我们返回流,但客户端无法在这里关闭,存在风险。
// 实际使用时,可考虑返回预签名URL或使用临时文件。
// 此处暂时返回流,并记录警告。
log.warn("返回原始InputStream,调用者必须确保在使用后关闭流,并注意OSSClient资源管理。");
return inputStream;
} catch (Exception e) {
operationResult = "failure"; // 标记操作失败
errorMessage = e.getMessage(); // 记录错误信息
log.error("阿里云OSS下载文件失败: {}", fileKey, e);
throw new RuntimeException("阿里云OSS下载文件失败: " + fileKey, e);
recordOperationLog(ossFile != null ? ossFile.getFileBizId() : "-1", "download", "system", "failure", e.getMessage(), System.currentTimeMillis());
log.error("OSS下载文件失败: {}", fileKey, e);
throw new RuntimeException("OSS下载文件失败: " + fileKey, e);
} finally {
// 记录失败日志
if ("failure".equals(operationResult)) {
OssOperationLog operationLog = new OssOperationLog();
if (ossFile != null) {
operationLog.setFileBizId(ossFile.getFileBizId());
} else {
operationLog.setFileBizId("-1"); // 使用无效文件ID
}
operationLog.setOperationType("download");
operationLog.setOperationUser("system");
operationLog.setOperationResult(operationResult);
operationLog.setErrorMessage(errorMessage);
operationLog.setClientIp(IpUtil.getClientIp());
operationLog.setCostTime(System.currentTimeMillis() - startTime);
// 保存操作日志到数据库
ossFileService.saveOperationLog(operationLog);
}
// 注意:不能在这里shutdown,因为InputStream可能还未消费完。但如果不shutdown,客户端会一直保持连接。
// 这是一个设计问题。实际生产环境建议使用预签名URL或临时文件下载。
}
}
......@@ -394,8 +312,7 @@ public class AliYunOssServiceImpl implements OssService {
*/
@Override
public InputStream downloadFile(String fileKey) {
// 调用重载方法,使用默认存储桶
return downloadFile(fileKey, defaultBucket);
return downloadFile(fileKey, null);
}
/**
......@@ -407,62 +324,66 @@ public class AliYunOssServiceImpl implements OssService {
*/
@Override
public boolean deleteFile(String fileKey, String bucketName, String operator) {
long startTime = System.currentTimeMillis(); // 记录开始时间
String operationResult = "success"; // 操作结果
String errorMessage = null; // 错误信息
OssFile ossFile = null; // 文件记录
OssFile ossFile = ossFileService.getFileByKey(fileKey);
if (ossFile == null) {
throw new BusinessException("文件不存在: " + fileKey);
}
OssProvider provider = ossProviderService.getProviderByBizId(ossFile.getProviderBizId());
if (provider == null) {
throw new BusinessException("提供商不存在: " + ossFile.getProviderBizId());
}
OSS ossClient = null;
long startTime = System.currentTimeMillis();
try {
// 确定存储桶(如果未指定则使用默认存储桶)
String actualBucket = StringUtils.isNotBlank(bucketName) ? bucketName : defaultBucket;
ossClient = ossClientFactory.createOssClient(provider);
String actualBucket = StringUtils.isNotBlank(bucketName) ? bucketName : ossFile.getBucketName();
// 获取文件信息
ossFile = ossFileService.getFileByKey(fileKey);
// 从阿里云OSS删除文件 TODO
ossClient.deleteObject(actualBucket, ossFile.getFileKey());
// 标记文件为已删除
ossFileService.markFileAsDeleted(fileKey);
// 创建操作日志
OssOperationLog operationLog = new OssOperationLog();
operationLog.setFileBizId(ossFile.getFileBizId());
operationLog.setOperationType("delete");
operationLog.setOperationUser(operator);
operationLog.setOperationResult(operationResult);
operationLog.setErrorMessage(errorMessage);
operationLog.setClientIp(IpUtil.getClientIp());
operationLog.setCostTime(System.currentTimeMillis() - startTime);
// 保存操作日志到数据库
ossFileService.saveOperationLog(operationLog);
recordOperationLog(ossFile.getFileBizId(), "delete", operator, "success", null, startTime);
log.info("文件删除成功: {}/{}", actualBucket, fileKey);
return true;
} catch (Exception e) {
operationResult = "failure"; // 标记操作失败
errorMessage = e.getMessage(); // 记录错误信息
log.error("阿里云OSS删除文件失败: {}", fileKey, e);
throw new RuntimeException("阿里云OSS删除文件失败: " + fileKey, e);
recordOperationLog(ossFile.getFileBizId(), "delete", operator, "failure", e.getMessage(), startTime);
log.error("OSS删除文件失败: {}", fileKey, e);
throw new RuntimeException("OSS删除文件失败: " + fileKey, e);
} finally {
// 记录失败日志
if ("failure".equals(operationResult)) {
OssOperationLog operationLog = new OssOperationLog();
if (ossFile != null) {
operationLog.setFileBizId(ossFile.getFileBizId());
} else {
operationLog.setFileBizId("-1"); // 使用无效文件ID
if (ossClient != null) {
ossClient.shutdown();
}
}
}
operationLog.setOperationType("delete");
operationLog.setOperationUser(operator);
operationLog.setOperationResult(operationResult);
operationLog.setErrorMessage(errorMessage);
operationLog.setClientIp(IpUtil.getClientIp());
operationLog.setCostTime(System.currentTimeMillis() - startTime);
// 保存操作日志到数据库
ossFileService.saveOperationLog(operationLog);
@Override
public boolean deleteFileByBizId(String fileBizId, String operator) {
OssFile ossFile = ossFileService.queryOne(fileBizId);
if (ossFile == null) {
throw new BusinessException("文件不存在: " + fileBizId);
}
OssProvider provider = ossProviderService.getProviderByBizId(ossFile.getProviderBizId());
if (provider == null) {
throw new BusinessException("提供商不存在: " + ossFile.getProviderBizId());
}
OSS ossClient = null;
long startTime = System.currentTimeMillis();
try {
ossClient = ossClientFactory.createOssClient(provider);
ossClient.deleteObject(ossFile.getBucketName(), ossFile.getFileKey());
ossFileService.removeById(ossFile.getId());
recordOperationLog(ossFile.getFileBizId(), "delete", operator, "success", null, startTime);
log.info("文件删除成功: {}/{}", ossFile.getBucketName(), ossFile.getFileKey());
return true;
} catch (Exception e) {
recordOperationLog(ossFile.getFileBizId(), "delete", operator, "failure", e.getMessage(), startTime);
log.error("删除文件失败: fileBizId={}", fileBizId, e);
throw new RuntimeException("删除文件失败: " + fileBizId, e);
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
......@@ -475,12 +396,11 @@ public class AliYunOssServiceImpl implements OssService {
*/
@Override
public boolean deleteFile(String fileKey, String operator) {
// 调用重载方法,使用默认存储桶
return deleteFile(fileKey, defaultBucket, operator);
return deleteFile(fileKey, null, operator);
}
/**
* 获取文件URL
* 获取文件URL(预签名)
* @param fileKey
* @param bucketName
* @param expiration
......@@ -488,18 +408,29 @@ public class AliYunOssServiceImpl implements OssService {
*/
@Override
public String getFileUrl(String fileKey, String bucketName, Duration expiration) {
try {
// 确定存储桶(如果未指定则使用默认存储桶)
String actualBucket = StringUtils.isNotBlank(bucketName) ? bucketName : defaultBucket;
OssFile ossFile = ossFileService.getFileByKey(fileKey);
if (ossFile == null) {
throw new BusinessException("文件不存在: " + fileKey);
}
OssProvider provider = ossProviderService.getProviderByBizId(ossFile.getProviderBizId());
if (provider == null) {
throw new BusinessException("提供商不存在: " + ossFile.getProviderBizId());
}
// 生成预签名URL(带过期时间)
OSS ossClient = null;
try {
ossClient = ossClientFactory.createOssClient(provider);
String actualBucket = StringUtils.isNotBlank(bucketName) ? bucketName : ossFile.getBucketName();
Date expirationDate = new Date(System.currentTimeMillis() + expiration.toMillis());
URL url = ossClient.generatePresignedUrl(actualBucket, fileKey, expirationDate);
return url.toString();
} catch (Exception e) {
log.error("阿里云OSS获取文件URL失败: {}", fileKey, e);
throw new RuntimeException("阿里云OSS获取文件URL失败: " + fileKey, e);
log.error("获取文件URL失败: {}", fileKey, e);
throw new RuntimeException("获取文件URL失败: " + fileKey, e);
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
......@@ -511,8 +442,7 @@ public class AliYunOssServiceImpl implements OssService {
*/
@Override
public String getFileUrl(String fileKey, Duration expiration) {
// 调用重载方法,使用默认存储桶
return getFileUrl(fileKey, defaultBucket, expiration);
return getFileUrl(fileKey, null, expiration);
}
/**
......@@ -523,15 +453,27 @@ public class AliYunOssServiceImpl implements OssService {
*/
@Override
public boolean doesFileExist(String fileKey, String bucketName) {
try {
// 确定存储桶(如果未指定则使用默认存储桶)
String actualBucket = StringUtils.isNotBlank(bucketName) ? bucketName : defaultBucket;
OssFile ossFile = ossFileService.getFileByKey(fileKey);
if (ossFile == null) {
return false;
}
OssProvider provider = ossProviderService.getProviderByBizId(ossFile.getProviderBizId());
if (provider == null) {
return false;
}
// 检查文件是否存在
OSS ossClient = null;
try {
ossClient = ossClientFactory.createOssClient(provider);
String actualBucket = StringUtils.isNotBlank(bucketName) ? bucketName : ossFile.getBucketName();
return ossClient.doesObjectExist(actualBucket, fileKey);
} catch (Exception e) {
log.error("阿里云OSS检查文件存在失败: {}", fileKey, e);
throw new RuntimeException("阿里云OSS检查文件存在失败: " + fileKey, e);
log.error("检查文件存在失败: {}", fileKey, e);
return false;
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
......@@ -542,8 +484,7 @@ public class AliYunOssServiceImpl implements OssService {
*/
@Override
public boolean doesFileExist(String fileKey) {
// 调用重载方法,使用默认存储桶
return doesFileExist(fileKey, defaultBucket);
return doesFileExist(fileKey, null);
}
/**
......@@ -554,25 +495,36 @@ public class AliYunOssServiceImpl implements OssService {
*/
@Override
public FileMetadata getFileMetadata(String fileKey, String bucketName) {
try {
// 确定存储桶(如果未指定则使用默认存储桶)
String actualBucket = StringUtils.isNotBlank(bucketName) ? bucketName : defaultBucket;
OssFile ossFile = ossFileService.getFileByKey(fileKey);
if (ossFile == null) {
throw new BusinessException("文件不存在: " + fileKey);
}
OssProvider provider = ossProviderService.getProviderByBizId(ossFile.getProviderBizId());
if (provider == null) {
throw new BusinessException("提供商不存在: " + ossFile.getProviderBizId());
}
// 获取文件元数据
OSS ossClient = null;
try {
ossClient = ossClientFactory.createOssClient(provider);
String actualBucket = StringUtils.isNotBlank(bucketName) ? bucketName : ossFile.getBucketName();
ObjectMetadata objectMetadata = ossClient.getObjectMetadata(actualBucket, fileKey);
// 转换为通用文件元数据格式
FileMetadata metadata = new FileMetadata();
metadata.setFileKey(fileKey);
metadata.setFileSize(objectMetadata.getContentLength());
metadata.setContentType(objectMetadata.getContentType());
// metadata.setLastModified(objectMetadata.getLastModified());
metadata.setEtag(objectMetadata.getETag());
// 如果有最后修改时间,可设置
// metadata.setLastModified(objectMetadata.getLastModified());
return metadata;
} catch (Exception e) {
log.error("阿里云OSS获取文件元数据失败: {}", fileKey, e);
throw new RuntimeException("阿里云OSS获取文件元数据失败: " + fileKey, e);
log.error("获取文件元数据失败: {}", fileKey, e);
throw new RuntimeException("获取文件元数据失败: " + fileKey, e);
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
......@@ -583,118 +535,77 @@ public class AliYunOssServiceImpl implements OssService {
*/
@Override
public FileMetadata getFileMetadata(String fileKey) {
// 调用重载方法,使用默认存储桶
return getFileMetadata(fileKey, defaultBucket);
return getFileMetadata(fileKey, null);
}
/**
* 切换OSS提供商
* @param providerName
* 切换OSS提供商-根据项目ID切换(已废弃,由每次操作动态决定,保留空实现)
*/
@Override
public void switchProvider(String providerName) {
try {
// 根据名称获取OSS提供商
OssProvider provider = ossProviderService.getProviderByName(providerName);
// 切换到新的提供商
switchToProvider(provider);
log.info("已切换到OSS提供商: {}", providerName);
} catch (Exception e) {
log.error("切换OSS提供商失败: {}", providerName, e);
throw new RuntimeException("切换OSS提供商失败: " + providerName, e);
}
public void switchProviderByProjectId(String projectBizId) {
// 不再需要
}
/**
* 切换OSS提供商(通过ID)
* @param providerId
* 切换OSS提供商(已废弃)
*/
@Override
public void switchProvider(Long providerId) {
try {
// 根据ID获取OSS提供商
OssProvider provider = ossProviderService.getProviderById(providerId);
// 切换到新的提供商
switchToProvider(provider);
log.info("已切换到OSS提供商ID: {}", providerId);
} catch (Exception e) {
log.error("切换OSS提供商失败: {}", providerId, e);
throw new RuntimeException("切换OSS提供商失败: " + providerId, e);
}
public void switchProvider(String providerName) {
// 不再需要
}
/**
* 切换到指定的OSS提供商
* @param provider
* 切换OSS提供商(已废弃)
*/
private void switchToProvider(OssProvider provider) {
// 关闭旧的客户端
if (this.ossClient != null) {
this.ossClient.shutdown();
@Override
public void switchProvider(Long providerId) {
// 不再需要
}
// 创建新的客户端
this.currentProvider = provider;
this.ossClient = new OSSClientBuilder().build(
provider.getEndpoint(),
provider.getAccessKey(),
provider.getSecretKey()
);
this.defaultBucket = provider.getBucketName();
/**
* 获取当前使用的 OSS 客户端(已废弃,返回null)
*/
@Override
public OSS getOssClient() {
return null;
}
/**
* 获取当前使用的提供商
* @return
* 获取当前使用的提供商(已废弃,返回默认提供商)
*/
@Override
public OssProvider getCurrentProvider() {
return currentProvider;
return getDefaultProvider();
}
/**
* 设置默认存储桶
* @param bucketName
* 设置默认存储桶(已废弃)
*/
@Override
public void setDefaultBucket(String bucketName) {
this.defaultBucket = bucketName;
log.info("已设置默认存储桶: {}", bucketName);
// 不再需要
}
/**
* 获取默认存储桶
* @return
* 获取默认存储桶(已废弃,返回默认提供商的桶)
*/
@Override
public String getDefaultBucket() {
return defaultBucket;
return getDefaultProvider().getBucketName();
}
/**
* 获取上传结果(包含文件信息和访问URL)
* @param fileKey
* @param expiration
* @return
* 获取上传结果
*/
@Override
public UploadResult getUploadResult(String fileKey, Duration expiration) {
try {
// 获取文件信息
OssFile file = ossFileService.getFileByKey(fileKey);
// 生成访问URL
if (file == null) {
throw new BusinessException("文件不存在: " + fileKey);
}
String accessUrl = getFileUrl(fileKey, expiration);
String url = accessUrl.split("\\?")[0];
// 使用 split 方法以 "?" 为分隔符分割字符串
String[] parts = accessUrl.split("\\?");
String url = parts[0]; // 取问号之前的部分
// 创建上传结果
UploadResult result = new UploadResult();
result.setFileKey(fileKey);
result.setAccessUrl(accessUrl);
......@@ -703,100 +614,57 @@ public class AliYunOssServiceImpl implements OssService {
result.setFileType(file.getFileType());
result.setOriginalName(file.getOriginalName());
result.setUploadTime(file.getUploadTime());
return result;
} catch (Exception e) {
log.error("获取上传结果失败: {}", fileKey, e);
throw new RuntimeException("获取上传结果失败: " + fileKey, e);
}
}
/**
* 创建虚拟文件记录(用于操作日志)
* @return
*/
private OssFile createDummyOssFile() {
OssFile file = new OssFile();
file.setId(-1L); // 使用无效ID
return file;
}
/**
* 上传字节数组到OSS并设置为公共读权限
* @param content 文件内容字节数组
* @param fileName 文件名
* @return 公共访问URL
* 上传字节数组并设置公共读(示例方法)
*/
public String upload(byte[] content, String fileName) {
OssProvider defaultProvider = getDefaultProvider();
OSS ossClient = null;
try {
// 生成唯一文件名
ossClient = ossClientFactory.createOssClient(defaultProvider);
String objectName = "appointment/excel/" + UUID.randomUUID() + "/" + fileName;
// 创建上传请求
PutObjectRequest putObjectRequest = new PutObjectRequest(
defaultBucket,
objectName,
new ByteArrayInputStream(content)
);
// 上传文件
defaultProvider.getBucketName(), objectName, new ByteArrayInputStream(content));
ossClient.putObject(putObjectRequest);
// 设置对象访问权限为公共读
ossClient.setObjectAcl(defaultBucket, objectName, CannedAccessControlList.PublicRead);
// 构造公共访问URL
String publicUrl = "https://" + defaultBucket + "." + defaultEndpoint + "/" + objectName;
return publicUrl;
} catch (Exception e) {
throw new RuntimeException("上传文件到OSS失败", e);
ossClient.setObjectAcl(defaultProvider.getBucketName(), objectName, CannedAccessControlList.PublicRead);
return "https://" + defaultProvider.getBucketName() + "." + defaultProvider.getEndpoint() + "/" + objectName;
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
/**
* 上传输入流到OSS并设置为公共读权限
* @param inputStream 文件输入流
* @param fileName 文件名
* @return 公共访问URL
* 上传输入流并设置公共读
*/
public String upload(InputStream inputStream, String fileName) {
OssProvider defaultProvider = getDefaultProvider();
OSS ossClient = null;
try {
// 生成唯一文件名
ossClient = ossClientFactory.createOssClient(defaultProvider);
String objectName = "appointment/excel/" + UUID.randomUUID() + "/" + fileName;
// 创建上传请求
PutObjectRequest putObjectRequest = new PutObjectRequest(
defaultBucket,
objectName,
inputStream
);
// 上传文件
defaultProvider.getBucketName(), objectName, inputStream);
ossClient.putObject(putObjectRequest);
// 设置对象访问权限为公共读
ossClient.setObjectAcl(defaultBucket, objectName, CannedAccessControlList.PublicRead);
// 构造公共访问URL
String publicUrl = "https://" + defaultBucket + "." + defaultEndpoint + "/" + objectName;
return publicUrl;
} catch (Exception e) {
throw new RuntimeException("上传文件到OSS失败", e);
ossClient.setObjectAcl(defaultProvider.getBucketName(), objectName, CannedAccessControlList.PublicRead);
return "https://" + defaultProvider.getBucketName() + "." + defaultProvider.getEndpoint() + "/" + objectName;
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
/**
* 批量上传文件
* @param files 文件列表
* @param reqDto 上传请求参数
* @return 批量上传响应
*/
@Override
public com.yd.oss.feign.response.ApiBatchUploadResponse batchUploadFiles(
java.util.List<MultipartFile> files, OssUploadFileReqDto reqDto) {
com.yd.oss.feign.response.ApiBatchUploadResponse response = new com.yd.oss.feign.response.ApiBatchUploadResponse();
java.util.List<com.yd.oss.feign.response.ApiUploadResponse> successFiles = new java.util.ArrayList<>();
java.util.List<com.yd.oss.feign.response.ApiBatchUploadResponse.FailedFile> failedFiles = new java.util.ArrayList<>();
......@@ -811,10 +679,7 @@ public class AliYunOssServiceImpl implements OssService {
for (MultipartFile file : files) {
try {
// 调用单文件上传方法
OssUploadFileResDto uploadResult = uploadFileBody(file, reqDto);
// 转换为ApiUploadResponse
com.yd.oss.feign.response.ApiUploadResponse uploadResponse = new com.yd.oss.feign.response.ApiUploadResponse();
uploadResponse.setFileBizId(uploadResult.getFileBizId());
uploadResponse.setFileKey(uploadResult.getFileKey());
......@@ -822,7 +687,6 @@ public class AliYunOssServiceImpl implements OssService {
uploadResponse.setUrl(uploadResult.getUrl());
uploadResponse.setFileSize(uploadResult.getFileSize());
uploadResponse.setFileType(uploadResult.getFileType());
successFiles.add(uploadResponse);
} catch (Exception e) {
log.error("批量上传文件失败: {}", file.getOriginalFilename(), e);
......@@ -838,9 +702,23 @@ public class AliYunOssServiceImpl implements OssService {
response.setFailedFiles(failedFiles);
response.setSuccessCount(successFiles.size());
response.setFailedCount(failedFiles.size());
log.info("批量上传完成: 成功{}个, 失败{}个", successFiles.size(), failedFiles.size());
return response;
}
/**
* 记录操作日志(公共方法)
*/
private void recordOperationLog(String fileBizId, String operationType, String operator,
String result, String errorMsg, long startTime) {
OssOperationLog operationLog = new OssOperationLog();
operationLog.setFileBizId(fileBizId);
operationLog.setOperationType(operationType);
operationLog.setOperationUser(operator);
operationLog.setOperationResult(result);
operationLog.setErrorMessage(errorMsg);
operationLog.setClientIp(IpUtil.getClientIp());
operationLog.setCostTime(System.currentTimeMillis() - startTime);
ossFileService.saveOperationLog(operationLog);
}
}
\ No newline at end of file
......@@ -40,7 +40,7 @@ public class AppointmentExcelServiceImpl implements AppointmentExcelService {
* @param fileName
* @return
*/
public String exportAndUploadToOss(Map<String, Object> data, String ossObjectKey, String fileName) {
public String exportAndUploadToOss(Map<String, Object> data, String ossObjectKey, String fileName,String projectBizId) {
File tempFile = null;
File processedFile = null;
......@@ -301,12 +301,13 @@ public class AppointmentExcelServiceImpl implements AppointmentExcelService {
@Override
public String exportAppointment(Map<String, Object> data,
String templateType,
String appointmentBizId) {
String appointmentBizId,
String projectBizId) {
// 获取模板信息
FileProdDto fileProdDto = iFileTemplateService.getFileProd("", templateType);
// 生成文件名
String fileName = "预约信息_" + appointmentBizId + "_" + System.currentTimeMillis() + ".xlsx";
// 导出并上传到OSS
return exportAndUploadToOss(data, fileProdDto.getFileKey(), fileName);
return exportAndUploadToOss(data, fileProdDto.getFileKey(), fileName,projectBizId);
}
}
......@@ -115,7 +115,8 @@ public class ExcelExportServiceImpl implements ExcelExportService {
fileName, // 生成的文件名
"", // 存储桶名称(空字符串表示使用默认存储桶)
"", // 上传用户标识(空字符串表示匿名或系统用户)
"excel" // 文件分类类型,对应OSS文件夹目录
"excel", // 文件分类类型,对应OSS文件夹目录
""
);
// 返回成功结果,包含文件URL和大小信息
......
package com.yd.oss.service.service.impl;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yd.common.exception.BusinessException;
import com.yd.oss.service.model.OssProvider;
import com.yd.oss.service.dao.OssProviderMapper;
......@@ -75,6 +77,14 @@ public class OssProviderServiceImpl extends ServiceImpl<OssProviderMapper, OssPr
return provider;
}
@Override
public OssProvider getProviderByBizId(String providerBizId) {
return baseMapper.selectOne(new LambdaQueryWrapper<OssProvider>()
.eq(OssProvider::getProviderBizId,providerBizId)
.last(" limit 1 ")
);
}
/**
* 获取所有激活的OSS提供商
* @return
......@@ -113,4 +123,24 @@ public class OssProviderServiceImpl extends ServiceImpl<OssProviderMapper, OssPr
public boolean deleteProvider(Long id) {
return ossProviderMapper.deleteById(id) > 0;
}
/**
* 根据项目ID查询对应OSS服务商
* @param projectBizId
* @return
*/
@Override
public OssProvider getProviderByProjectId(String projectBizId) {
if (StringUtils.isBlank(projectBizId)) {
//项目ID空值,查询默认的服务商
return baseMapper.selectOne(new LambdaQueryWrapper<OssProvider>()
.eq(OssProvider::getIsDefault,1)
.last(" limit 1 ")
);
}
return baseMapper.selectOne(new LambdaQueryWrapper<OssProvider>()
.eq(OssProvider::getProjectBizId,projectBizId)
.last(" limit 1 ")
);
}
}
package com.yd.oss.service.utils;
import com.yd.common.result.Result;
import com.yd.user.feign.client.sysproject.ApiSysProjectFeignClient;
import com.yd.user.feign.response.sysproject.ApiSysProjectDetailResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.core.env.Environment;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Component
public class OssUtils {
@Autowired
private Environment environment;
@Autowired
private ApiSysProjectFeignClient apiSysProjectFeignClient;
/**
* 获取通用文件路径的前缀
* @param projectBizId 项目ID
* @param env 环境变量(prod,test)如果不传系统获取
* @param originalFileName 原始文件名
* @param fileType(文件类型,文件扩展名)
* @return
*/
public String getFilePrefix(String projectBizId,String env,
String originalFileName,String fileType) {
String projectType = "pc";
if (StringUtils.isNotBlank(projectBizId)) {
//通过项目ID查询项目类型
Result<ApiSysProjectDetailResponse> result = apiSysProjectFeignClient.detail(projectBizId);
if (result.getData() != null) {
ApiSysProjectDetailResponse response = result.getData();
projectType = StringUtils.isNotBlank(response.getProjectType()) ? response.getProjectType() : "pc";
}
}
if (StringUtils.isBlank(env)) {
env = getActiveEnv();
}
return projectType + "/" + env + "/" + FileUtil.generateFileKey(originalFileName,fileType);
}
/**
* 获取分片文件路径的前缀
* @param projectBizId
* @param env
* @param env
* @return
*/
public String getShardFilePrefix(String projectBizId,String env) {
String projectType = "pc";
if (StringUtils.isNotBlank(projectBizId)) {
//通过项目ID查询项目类型
Result<ApiSysProjectDetailResponse> result = apiSysProjectFeignClient.detail(projectBizId);
if (result.getData() != null) {
ApiSysProjectDetailResponse response = result.getData();
projectType = StringUtils.isNotBlank(response.getProjectType()) ? response.getProjectType() : "pc";
}
}
if (StringUtils.isBlank(env)) {
env = getActiveEnv();
}
return projectType + "/" + env + "/large_file/uploads/" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd/"));
}
private String getActiveEnv() {
String[] activeProfiles = environment.getActiveProfiles();
return activeProfiles.length > 0 ? activeProfiles[0] : "prod";
}
}
......@@ -77,8 +77,6 @@
<orderEntry type="library" name="Maven: org.springdoc:springdoc-openapi-webmvc-core:1.7.0" level="project" />
<orderEntry type="library" name="Maven: org.springdoc:springdoc-openapi-common:1.7.0" level="project" />
<orderEntry type="library" name="Maven: io.swagger.core.v3:swagger-core:2.2.9" level="project" />
<orderEntry type="library" name="Maven: jakarta.xml.bind:jakarta.xml.bind-api:2.3.3" level="project" />
<orderEntry type="library" name="Maven: jakarta.activation:jakarta.activation-api:1.2.2" level="project" />
<orderEntry type="library" name="Maven: io.swagger.core.v3:swagger-annotations:2.2.9" level="project" />
<orderEntry type="library" name="Maven: io.swagger.core.v3:swagger-models:2.2.9" level="project" />
<orderEntry type="library" name="Maven: org.webjars:swagger-ui:4.18.2" level="project" />
......@@ -130,7 +128,6 @@
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter:3.1.1" level="project" />
<orderEntry type="library" name="Maven: org.springframework.security:spring-security-rsa:1.0.10.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.bouncycastle:bcpkix-jdk15on:1.68" level="project" />
<orderEntry type="library" name="Maven: org.bouncycastle:bcprov-jdk15on:1.69" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-loadbalancer:3.1.1" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-loadbalancer:3.1.1" level="project" />
<orderEntry type="library" name="Maven: io.projectreactor.addons:reactor-extra:3.4.7" level="project" />
......@@ -178,22 +175,30 @@
<orderEntry type="library" name="Maven: io.github.openfeign:feign-slf4j:11.8" level="project" />
<orderEntry type="library" name="Maven: com.aliyun.oss:aliyun-sdk-oss:3.15.1" level="project" />
<orderEntry type="library" name="Maven: org.apache.httpcomponents:httpclient:4.5.13" level="project" />
<orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore:4.4.15" level="project" />
<orderEntry type="library" name="Maven: org.jdom:jdom2:2.0.6.1" level="project" />
<orderEntry type="library" name="Maven: org.codehaus.jettison:jettison:1.1" level="project" />
<orderEntry type="library" name="Maven: stax:stax-api:1.0.1" level="project" />
<orderEntry type="library" name="Maven: com.aliyun:aliyun-java-sdk-core:4.5.10" level="project" />
<orderEntry type="library" name="Maven: com.aliyun:aliyun-java-sdk-ram:3.1.0" level="project" />
<orderEntry type="library" name="Maven: com.aliyun:aliyun-java-sdk-kms:2.11.0" level="project" />
<orderEntry type="library" name="Maven: com.aliyun:aliyun-java-sdk-sts:3.0.0" level="project" />
<orderEntry type="library" name="Maven: com.aliyun:aliyun-java-sdk-core:4.6.0" level="project" />
<orderEntry type="library" name="Maven: com.google.code.gson:gson:2.8.9" level="project" />
<orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore:4.4.15" level="project" />
<orderEntry type="library" name="Maven: commons-logging:commons-logging:1.2" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.14.0" level="project" />
<orderEntry type="library" name="Maven: javax.xml.bind:jaxb-api:2.3.1" level="project" />
<orderEntry type="library" name="Maven: javax.activation:javax.activation-api:1.2.0" level="project" />
<orderEntry type="library" name="Maven: org.jacoco:org.jacoco.agent:runtime:0.8.5" level="project" />
<orderEntry type="library" name="Maven: org.glassfish.jaxb:jaxb-runtime:2.3.6" level="project" />
<orderEntry type="library" name="Maven: jakarta.xml.bind:jakarta.xml.bind-api:2.3.3" level="project" />
<orderEntry type="library" name="Maven: org.glassfish.jaxb:txw2:2.3.6" level="project" />
<orderEntry type="library" name="Maven: com.sun.istack:istack-commons-runtime:3.0.12" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: com.sun.activation:jakarta.activation:1.2.2" level="project" />
<orderEntry type="library" name="Maven: org.bouncycastle:bcprov-jdk15on:1.69" level="project" />
<orderEntry type="library" name="Maven: org.jacoco:org.jacoco.agent:runtime:0.8.7" level="project" />
<orderEntry type="library" name="Maven: org.ini4j:ini4j:0.5.4" level="project" />
<orderEntry type="library" name="Maven: io.opentracing:opentracing-api:0.33.0" level="project" />
<orderEntry type="library" name="Maven: io.opentracing:opentracing-util:0.33.0" level="project" />
<orderEntry type="library" name="Maven: io.opentracing:opentracing-noop:0.33.0" level="project" />
<orderEntry type="library" name="Maven: com.aliyun:aliyun-java-sdk-ram:3.1.0" level="project" />
<orderEntry type="library" name="Maven: com.aliyun:aliyun-java-sdk-kms:2.11.0" level="project" />
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-core:9.0.60" level="project" />
<orderEntry type="library" name="Maven: org.apache.tomcat:tomcat-annotations-api:9.0.60" level="project" />
<orderEntry type="library" name="Maven: com.documents4j:documents4j-local:1.1.12" level="project" />
......@@ -227,7 +232,6 @@
<orderEntry type="library" name="Maven: org.apache.xmlbeans:xmlbeans:5.1.1" level="project" />
<orderEntry type="library" name="Maven: cn.afterturn:easypoi-base:4.4.0" level="project" />
<orderEntry type="library" name="Maven: org.apache.poi:poi-ooxml-schemas:4.1.1" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.14.0" level="project" />
<orderEntry type="library" name="Maven: ognl:ognl:3.2.6" level="project" />
<orderEntry type="library" name="Maven: org.javassist:javassist:3.20.0-GA" level="project" />
<orderEntry type="library" name="Maven: javax.validation:validation-api:2.0.1.Final" level="project" />
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment