Your Name 2c713febc6 feat: 新增三个功能
1. 任务标题自动生成
   - 根据SPU信息+平台+品牌+任务子类型+需求类型自动组合生成标题
   - 添加watch监听相关字段变化自动更新标题

2. 预览图上传功能
   - 添加S3预览图上传API(自动压缩到200px宽度)
   - Task实体增加previewImage字段
   - 前端添加预览图上传组件
   - 任务卡片显示65x65像素预览图

3. 接单大厅按部门显示
   - 根据用户所属部门筛选可接单的任务类型
   - 只显示用户部门对应的任务类型的任务
2026-01-05 11:57:59 +08:00

230 lines
8.9 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.designhub.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.designhub.entity.FileUpload;
import com.designhub.entity.S3Config;
import com.designhub.mapper.FileUploadMapper;
import com.designhub.mapper.S3ConfigMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.S3Configuration;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URI;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
import net.coobird.thumbnailator.Thumbnails;
/**
* S3文件存储服务
*/
@Service
public class S3Service {
private static final Logger log = LoggerFactory.getLogger(S3Service.class);
@Autowired
private S3ConfigMapper s3ConfigMapper;
@Autowired
private FileUploadMapper fileUploadMapper;
/**
* 上传文件到S3
*
* @param file 文件
* @param uploaderId 上传者ID
* @param uploaderName 上传者姓名
* @param relatedType 关联类型
* @param relatedId 关联ID
* @return 文件URL
*/
public String uploadFile(MultipartFile file, Long uploaderId, String uploaderName,
String relatedType, Long relatedId) throws IOException {
// 获取激活的S3配置
S3Config config = s3ConfigMapper.getActiveConfig();
if (config == null) {
throw new RuntimeException("S3配置未激活,请先配置S3存储");
}
// 生成唯一文件名
String originalFilename = file.getOriginalFilename();
String fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
String uuid = UUID.randomUUID().toString().replace("-", "").substring(0, 8);
String storedName = timestamp + "_" + uuid + fileExtension;
// 构建文件路径
String filePath = config.getFilePath() + storedName;
try {
// 创建S3客户端
S3Client s3Client = createS3Client(config);
// 上传文件
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(config.getBucket())
.key(filePath)
.contentType(file.getContentType())
.build();
s3Client.putObject(putObjectRequest, RequestBody.fromInputStream(file.getInputStream(), file.getSize()));
// 生成文件URL
String fileUrl = config.getPublicDomain() + "/" + filePath;
// 保存文件上传记录
FileUpload fileUpload = new FileUpload();
fileUpload.setOriginalName(originalFilename);
fileUpload.setStoredName(storedName);
fileUpload.setFilePath(filePath);
fileUpload.setFileUrl(fileUrl);
fileUpload.setFileSize(file.getSize());
fileUpload.setFileType(file.getContentType());
fileUpload.setUploaderId(uploaderId);
fileUpload.setUploaderName(uploaderName);
fileUpload.setRelatedType(relatedType);
fileUpload.setRelatedId(relatedId);
fileUploadMapper.insert(fileUpload);
log.info("文件上传成功: {} -> {}", originalFilename, fileUrl);
return fileUrl;
} catch (Exception e) {
log.error("文件上传失败: {}", originalFilename, e);
throw new RuntimeException("文件上传失败: " + e.getMessage());
}
}
/**
* 创建S3客户端
*/
private S3Client createS3Client(S3Config config) {
AwsBasicCredentials credentials = AwsBasicCredentials.create(
config.getAccessKeyId(),
config.getSecretAccessKey()
);
S3Configuration s3Config = S3Configuration.builder()
.pathStyleAccessEnabled(config.getEnablePathStyle())
.build();
return S3Client.builder()
.region(Region.of(config.getRegion()))
.credentialsProvider(StaticCredentialsProvider.create(credentials))
.endpointOverride(URI.create(config.getEndpoint()))
.serviceConfiguration(s3Config)
.build();
}
/**
* 获取文件上传记录
*/
public FileUpload getFileUploadById(Long id) {
return fileUploadMapper.selectById(id);
}
/**
* 根据关联信息获取文件列表
*/
public java.util.List<FileUpload> getFilesByRelated(String relatedType, Long relatedId) {
LambdaQueryWrapper<FileUpload> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(FileUpload::getRelatedType, relatedType)
.eq(FileUpload::getRelatedId, relatedId)
.orderByDesc(FileUpload::getCreateTime);
return fileUploadMapper.selectList(wrapper);
}
/**
* 上传预览图(压缩到最大宽度200px,保持比例)
*
* @param file 图片文件
* @param uploaderId 上传者ID
* @param uploaderName 上传者姓名
* @return 压缩后图片的URL
*/
public String uploadPreviewImage(MultipartFile file, Long uploaderId, String uploaderName) throws IOException {
// 获取激活的S3配置
S3Config config = s3ConfigMapper.getActiveConfig();
if (config == null) {
throw new RuntimeException("S3配置未激活,请先配置S3存储");
}
// 验证是否为图片
String contentType = file.getContentType();
if (contentType == null || !contentType.startsWith("image/")) {
throw new RuntimeException("只能上传图片文件");
}
// 生成唯一文件名
String originalFilename = file.getOriginalFilename();
String fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
String uuid = UUID.randomUUID().toString().replace("-", "").substring(0, 8);
String storedName = "preview_" + timestamp + "_" + uuid + fileExtension;
// 构建文件路径(预览图专用目录)
String filePath = config.getFilePath() + "previews/" + storedName;
try {
// 压缩图片到最大宽度200px,保持比例
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Thumbnails.of(file.getInputStream())
.width(200) // 最大宽度200像素
.keepAspectRatio(true) // 保持比例
.outputQuality(0.85) // 图片质量
.toOutputStream(outputStream);
byte[] compressedBytes = outputStream.toByteArray();
// 创建S3客户端
S3Client s3Client = createS3Client(config);
// 上传压缩后的图片
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(config.getBucket())
.key(filePath)
.contentType(contentType)
.build();
s3Client.putObject(putObjectRequest,
RequestBody.fromInputStream(new ByteArrayInputStream(compressedBytes), compressedBytes.length));
// 生成文件URL
String fileUrl = config.getPublicDomain() + "/" + filePath;
// 保存文件上传记录
FileUpload fileUpload = new FileUpload();
fileUpload.setOriginalName(originalFilename);
fileUpload.setStoredName(storedName);
fileUpload.setFilePath(filePath);
fileUpload.setFileUrl(fileUrl);
fileUpload.setFileSize((long) compressedBytes.length);
fileUpload.setFileType(contentType);
fileUpload.setUploaderId(uploaderId);
fileUpload.setUploaderName(uploaderName);
fileUpload.setRelatedType("preview_image");
fileUploadMapper.insert(fileUpload);
log.info("预览图上传成功: {} -> {} (压缩后大小: {} bytes)", originalFilename, fileUrl, compressedBytes.length);
return fileUrl;
} catch (Exception e) {
log.error("预览图上传失败: {}", originalFilename, e);
throw new RuntimeException("预览图上传失败: " + e.getMessage());
}
}
}