故事项新建调整,上传图像增加缩略图
This commit is contained in:
parent
75e61a1bf4
commit
7f3505ab2e
|
@ -13,4 +13,5 @@ public class CommonConstants {
|
||||||
|
|
||||||
public static final int DELETED = 1;
|
public static final int DELETED = 1;
|
||||||
public static final int NOT_DELETED = 0;
|
public static final int NOT_DELETED = 0;
|
||||||
|
public static final String LOW_RESOLUTION_PREFIX = "low_res_";
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,8 @@ public enum ResponseEnum {
|
||||||
GATEWAY_TIMEOUT(504, "网关超时"),
|
GATEWAY_TIMEOUT(504, "网关超时"),
|
||||||
|
|
||||||
// 操作错误
|
// 操作错误
|
||||||
SEARCH_ERROR(4001, "查询数据库错误");
|
SEARCH_ERROR(4001, "查询数据库错误"),
|
||||||
|
NOT_FOUND_ERROR(4002, "未找到该资源");
|
||||||
|
|
||||||
private final int code;
|
private final int code;
|
||||||
private final String message;
|
private final String message;
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
<?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="com.timeline.story.dao.CommonRelationMapper">
|
|
||||||
|
|
||||||
<insert id="insertImageStoryItemRelation">
|
|
||||||
INSERT INTO common_relation (rela_id, sub_rela_id, rela_type, user_id)
|
|
||||||
VALUES (#{imageInstanceId}, #{storyItemId}, 1, #{userId})
|
|
||||||
</insert>
|
|
||||||
|
|
||||||
<select id="getImagesByStoryItemId" resultType="string">
|
|
||||||
SELECT rela_id
|
|
||||||
FROM common_relation
|
|
||||||
WHERE sub_rela_id = #{storyItemId} AND rela_type = 1 AND is_delete = 0
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="getStoryItemsByImageInstanceId" resultType="string">
|
|
||||||
SELECT sub_rela_id
|
|
||||||
FROM common_relation
|
|
||||||
WHERE rela_id = #{imageInstanceId} AND rela_type = 1 AND is_delete = 0
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<update id="deleteImageStoryItemRelation">
|
|
||||||
UPDATE common_relation
|
|
||||||
SET is_delete = 1
|
|
||||||
WHERE rela_id = #{imageInstanceId} AND sub_rela_id = #{storyItemId}
|
|
||||||
</update>
|
|
||||||
|
|
||||||
<insert id="insertRelation">
|
|
||||||
INSERT INTO common_relation (rela_id, sub_rela_id, rela_type, user_id)
|
|
||||||
VALUES (#{relaId}, #{subRelaId}, #{relationType}, #{userId})
|
|
||||||
</insert>
|
|
||||||
</mapper>
|
|
|
@ -34,6 +34,11 @@
|
||||||
<artifactId>minio</artifactId>
|
<artifactId>minio</artifactId>
|
||||||
<version>8.5.2</version>
|
<version>8.5.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.coobird</groupId>
|
||||||
|
<artifactId>thumbnailator</artifactId>
|
||||||
|
<version>0.4.17</version>
|
||||||
|
</dependency>
|
||||||
<!--<dependency>
|
<!--<dependency>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package com.timeline.file.controller;
|
package com.timeline.file.controller;
|
||||||
|
|
||||||
import com.timeline.file.entity.ImageInfo;
|
|
||||||
import com.timeline.file.service.FileService;
|
import com.timeline.file.service.FileService;
|
||||||
import com.timeline.file.vo.ImageInfoVo;
|
import com.timeline.file.vo.ImageInfoVo;
|
||||||
import com.timeline.common.response.ResponseEntity;
|
import com.timeline.common.response.ResponseEntity;
|
||||||
|
@ -50,18 +49,22 @@ public class FileController {
|
||||||
}
|
}
|
||||||
@PostMapping("/upload-image")
|
@PostMapping("/upload-image")
|
||||||
public ResponseEntity<String> uploadCover(@RequestPart("image") MultipartFile image) throws Throwable {
|
public ResponseEntity<String> uploadCover(@RequestPart("image") MultipartFile image) throws Throwable {
|
||||||
String objectKey = fileService.uploadCover(image);
|
String objectKey = fileService.uploadImage(image);
|
||||||
return ResponseEntity.success(objectKey);
|
return ResponseEntity.success(objectKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = "/image/{coverInstanceId}", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
|
@RequestMapping(value = "/image/{instanceId}", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
|
||||||
public void downloadCover(@PathVariable String coverInstanceId, HttpServletResponse response) throws Throwable {
|
public void fetchImage(@PathVariable String instanceId, HttpServletResponse response) throws Throwable {
|
||||||
log.info("downloadCover");
|
InputStream inputStream = fileService.fetchImage(instanceId);
|
||||||
InputStream inputStream = fileService.downloadCover(coverInstanceId);
|
response.setContentType("image/jpeg");
|
||||||
|
IOUtils.copy(inputStream, response.getOutputStream());
|
||||||
|
}
|
||||||
|
@RequestMapping(value = "/image-low-res/{instanceId}", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
|
||||||
|
public void fetchImageLowRes(@PathVariable String instanceId, HttpServletResponse response) throws Throwable {
|
||||||
|
InputStream inputStream = fileService.fetchImageLowRes(instanceId);
|
||||||
response.setContentType("image/jpeg");
|
response.setContentType("image/jpeg");
|
||||||
IOUtils.copy(inputStream, response.getOutputStream());
|
IOUtils.copy(inputStream, response.getOutputStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传图片后绑定到某个 StoryItem
|
* 上传图片后绑定到某个 StoryItem
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -22,7 +22,10 @@ public interface FileService {
|
||||||
List<String> getStoryItemImages(String storyItemId);
|
List<String> getStoryItemImages(String storyItemId);
|
||||||
void removeImageFromStoryItem(String imageInstanceId, String storyItemId);
|
void removeImageFromStoryItem(String imageInstanceId, String storyItemId);
|
||||||
ArrayList<String> getAllImageUrls(List<String> images) throws Throwable;
|
ArrayList<String> getAllImageUrls(List<String> images) throws Throwable;
|
||||||
String uploadCover(MultipartFile cover) throws Throwable;
|
String uploadImage(MultipartFile cover) throws Throwable;
|
||||||
InputStream downloadCover(String coverKey) throws Throwable;
|
InputStream fetchImage(String coverKey) throws Throwable;
|
||||||
|
|
||||||
|
InputStream fetchImageLowRes(String instanceId) throws Throwable;
|
||||||
|
|
||||||
Map getImagesListByOwnerId(ImageInfoVo imageInfoVo);
|
Map getImagesListByOwnerId(ImageInfoVo imageInfoVo);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.timeline.file.service.impl;
|
||||||
|
|
||||||
import com.timeline.common.constants.CommonConstants;
|
import com.timeline.common.constants.CommonConstants;
|
||||||
import com.timeline.common.exception.CustomException;
|
import com.timeline.common.exception.CustomException;
|
||||||
|
import com.timeline.common.response.ResponseEnum;
|
||||||
import com.timeline.common.utils.CommonUtils;
|
import com.timeline.common.utils.CommonUtils;
|
||||||
import com.timeline.common.utils.PageUtils;
|
import com.timeline.common.utils.PageUtils;
|
||||||
import com.timeline.file.config.MinioConfig;
|
import com.timeline.file.config.MinioConfig;
|
||||||
|
@ -17,10 +18,13 @@ import io.minio.*;
|
||||||
import io.minio.errors.MinioException;
|
import io.minio.errors.MinioException;
|
||||||
import io.minio.http.Method;
|
import io.minio.http.Method;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.coobird.thumbnailator.Thumbnails;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -110,10 +114,17 @@ public class FileServiceImpl implements FileService {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// 不存在其他image_info使用则删除 MinIO 中的对象
|
// 不存在其他image_info使用则删除 MinIO 中的对象
|
||||||
|
log.info("删除 MinIO 中的对象:{}", imageInfo.getObjectKey());
|
||||||
minioClient.removeObject(RemoveObjectArgs.builder()
|
minioClient.removeObject(RemoveObjectArgs.builder()
|
||||||
.bucket(minioConfig.getBucketName())
|
.bucket(minioConfig.getBucketName())
|
||||||
.object(imageInfo.getObjectKey())
|
.object(imageInfo.getObjectKey())
|
||||||
.build());
|
.build());
|
||||||
|
// 删除低分辨率图像
|
||||||
|
log.info("删除 MinIO 中的低分辨率对象:{}", CommonConstants.LOW_RESOLUTION_PREFIX + imageInfo.getObjectKey());
|
||||||
|
minioClient.removeObject(RemoveObjectArgs.builder()
|
||||||
|
.bucket(minioConfig.getBucketName())
|
||||||
|
.object(CommonConstants.LOW_RESOLUTION_PREFIX + imageInfo.getObjectKey())
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
// 删除file_hash
|
// 删除file_hash
|
||||||
fileHashMapper.deleteFileHash(instanceId);
|
fileHashMapper.deleteFileHash(instanceId);
|
||||||
|
@ -165,10 +176,11 @@ public class FileServiceImpl implements FileService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String uploadCover(MultipartFile image) throws Throwable {
|
public String uploadImage(MultipartFile image) throws Throwable {
|
||||||
String suffix = Objects.requireNonNull(image.getOriginalFilename()).substring(image.getOriginalFilename().lastIndexOf("."));
|
String suffix = Objects.requireNonNull(image.getOriginalFilename()).substring(image.getOriginalFilename().lastIndexOf("."));
|
||||||
String hash = CommonUtils.calculateFileHash(image);
|
String hash = CommonUtils.calculateFileHash(image);
|
||||||
String objectKey = hash + suffix;
|
String objectKey = hash + suffix;
|
||||||
|
String lowResolutionObjectKey = CommonConstants.LOW_RESOLUTION_PREFIX + hash + suffix;
|
||||||
log.info("上传图片的ObjectKey值为:{}", objectKey);
|
log.info("上传图片的ObjectKey值为:{}", objectKey);
|
||||||
List<FileHash> hashByFileHash = fileHashMapper.getFileHashByFileHash(hash);
|
List<FileHash> hashByFileHash = fileHashMapper.getFileHashByFileHash(hash);
|
||||||
// 2. 保存元数据到 MySQL
|
// 2. 保存元数据到 MySQL
|
||||||
|
@ -190,6 +202,26 @@ public class FileServiceImpl implements FileService {
|
||||||
.stream(image.getInputStream(), image.getSize(), -1)
|
.stream(image.getInputStream(), image.getSize(), -1)
|
||||||
.contentType(image.getContentType())
|
.contentType(image.getContentType())
|
||||||
.build());
|
.build());
|
||||||
|
// 生成并上传低分辨率版本
|
||||||
|
try (InputStream inputStream = image.getInputStream()) {
|
||||||
|
ByteArrayOutputStream lowResOutputStream = new ByteArrayOutputStream();
|
||||||
|
Thumbnails.of(inputStream)
|
||||||
|
.size(300, 300) // 设置低分辨率版本大小
|
||||||
|
.outputQuality(0.7) // 设置压缩质量
|
||||||
|
.toOutputStream(lowResOutputStream);
|
||||||
|
|
||||||
|
ByteArrayInputStream lowResInputStream = new ByteArrayInputStream(lowResOutputStream.toByteArray());
|
||||||
|
minioClient.putObject(PutObjectArgs.builder()
|
||||||
|
.bucket(minioConfig.getBucketName())
|
||||||
|
.object(lowResolutionObjectKey)
|
||||||
|
.stream(lowResInputStream, lowResInputStream.available(), -1)
|
||||||
|
.contentType(image.getContentType())
|
||||||
|
.build());
|
||||||
|
|
||||||
|
log.info("低分辨率版本已生成并上传: {}", lowResolutionObjectKey);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("生成低分辨率版本失败", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fileHashMapper.insertFileHash(new FileHash(imageInfo.getInstanceId(), hash));
|
fileHashMapper.insertFileHash(new FileHash(imageInfo.getInstanceId(), hash));
|
||||||
imageInfoMapper.insert(imageInfo);
|
imageInfoMapper.insert(imageInfo);
|
||||||
|
@ -198,15 +230,40 @@ public class FileServiceImpl implements FileService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream downloadCover(String coverInstanceId) throws Throwable {
|
public InputStream fetchImage(String instanceId) throws Throwable {
|
||||||
log.info("获取");
|
String objectKey = imageInfoMapper.selectObjectKeyById(instanceId);
|
||||||
String objectKey = imageInfoMapper.selectObjectKeyById(coverInstanceId);
|
log.info("获取图像{}, objectKey:{}", instanceId, objectKey);
|
||||||
|
if (objectKey == null) {
|
||||||
|
throw new CustomException(ResponseEnum.NOT_FOUND_ERROR);
|
||||||
|
}
|
||||||
return minioClient.getObject(GetObjectArgs.builder()
|
return minioClient.getObject(GetObjectArgs.builder()
|
||||||
.bucket(minioConfig.getBucketName())
|
.bucket(minioConfig.getBucketName())
|
||||||
.object(objectKey)
|
.object(objectKey)
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
|
public InputStream fetchImageLowRes(String instanceId) throws Throwable {
|
||||||
|
String objectKey = imageInfoMapper.selectObjectKeyById(instanceId);
|
||||||
|
log.info("获取图像低分辨率版本{}, objectKey:{}", instanceId, objectKey);
|
||||||
|
if (objectKey == null) {
|
||||||
|
throw new CustomException(ResponseEnum.NOT_FOUND_ERROR);
|
||||||
|
}
|
||||||
|
String lowResObjectKey = CommonConstants.LOW_RESOLUTION_PREFIX + objectKey;
|
||||||
|
|
||||||
|
// 优先返回低分辨率版本,如果不存在则返回原图
|
||||||
|
if (doesObjectExist(lowResObjectKey)) {
|
||||||
|
return minioClient.getObject(GetObjectArgs.builder()
|
||||||
|
.bucket(minioConfig.getBucketName())
|
||||||
|
.object(lowResObjectKey)
|
||||||
|
.build());
|
||||||
|
} else {
|
||||||
|
log.warn("低分辨率版本不存在,返回原图: {}", objectKey);
|
||||||
|
return minioClient.getObject(GetObjectArgs.builder()
|
||||||
|
.bucket(minioConfig.getBucketName())
|
||||||
|
.object(objectKey)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
@Override
|
@Override
|
||||||
public Map getImagesListByOwnerId(ImageInfoVo imageInfoVo) {
|
public Map getImagesListByOwnerId(ImageInfoVo imageInfoVo) {
|
||||||
HashMap<String, String> map = new HashMap<>();
|
HashMap<String, String> map = new HashMap<>();
|
||||||
|
@ -215,4 +272,24 @@ public class FileServiceImpl implements FileService {
|
||||||
map, "list");
|
map, "list");
|
||||||
return resultMap;
|
return resultMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查对象是否存在
|
||||||
|
* @param objectKey 对象键
|
||||||
|
* @return true表示存在,false表示不存在
|
||||||
|
*/
|
||||||
|
private boolean doesObjectExist(String objectKey) {
|
||||||
|
try {
|
||||||
|
minioClient.statObject(StatObjectArgs.builder()
|
||||||
|
.bucket(minioConfig.getBucketName())
|
||||||
|
.object(objectKey)
|
||||||
|
.build());
|
||||||
|
return true;
|
||||||
|
} catch (MinioException e) {
|
||||||
|
return false;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("检查对象是否存在时出错: {}", objectKey, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSONObject;
|
||||||
import com.timeline.common.response.ResponseEntity;
|
import com.timeline.common.response.ResponseEntity;
|
||||||
import com.timeline.story.entity.StoryItem;
|
import com.timeline.story.entity.StoryItem;
|
||||||
import com.timeline.story.service.StoryItemService;
|
import com.timeline.story.service.StoryItemService;
|
||||||
|
import com.timeline.story.vo.StoryItemAddVo;
|
||||||
import com.timeline.story.vo.StoryItemVo;
|
import com.timeline.story.vo.StoryItemVo;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
@ -21,10 +22,10 @@ public class StoryItemController {
|
||||||
private StoryItemService storyItemService;
|
private StoryItemService storyItemService;
|
||||||
|
|
||||||
@PostMapping()
|
@PostMapping()
|
||||||
public ResponseEntity<String> createItem(@RequestParam("storyItem") String storyItemVoString, @RequestParam("cover") MultipartFile cover, @RequestParam("images") List<MultipartFile> images) {
|
public ResponseEntity<String> createItem(@RequestParam("storyItem") String storyItemVoString, @RequestParam(value = "images", required = false) List<MultipartFile> images) {
|
||||||
|
|
||||||
log.info("创建 StoryItem,{}", storyItemVoString);
|
log.info("创建 StoryItem,{}", storyItemVoString);
|
||||||
storyItemService.createItemWithCover(JSONObject.parseObject(storyItemVoString, StoryItemVo.class), cover, images);
|
storyItemService.createStoryItem(JSONObject.parseObject(storyItemVoString, StoryItemAddVo.class), images);
|
||||||
return ResponseEntity.success("StoryItem 创建成功");
|
return ResponseEntity.success("StoryItem 创建成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ import java.util.List;
|
||||||
public interface FileServiceClient {
|
public interface FileServiceClient {
|
||||||
|
|
||||||
@PostMapping(value = "/upload-image", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
@PostMapping(value = "/upload-image", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||||
ResponseEntity<String> uploadCover(@RequestPart("image") MultipartFile image);
|
ResponseEntity<String> uploadImage(@RequestPart("image") MultipartFile image);
|
||||||
|
|
||||||
@GetMapping("/download/cover/{coverKey}")
|
@GetMapping("/download/cover/{coverKey}")
|
||||||
InputStreamResource downloadCover(@PathVariable String coverKey);
|
InputStreamResource downloadCover(@PathVariable String coverKey);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.timeline.story.service;
|
package com.timeline.story.service;
|
||||||
|
|
||||||
import com.timeline.story.entity.StoryItem;
|
import com.timeline.story.entity.StoryItem;
|
||||||
|
import com.timeline.story.vo.StoryItemAddVo;
|
||||||
import com.timeline.story.vo.StoryItemVo;
|
import com.timeline.story.vo.StoryItemVo;
|
||||||
import com.timeline.story.vo.StoryItemWithCoverVo;
|
import com.timeline.story.vo.StoryItemWithCoverVo;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
@ -8,8 +9,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface StoryItemService {
|
public interface StoryItemService {
|
||||||
void createItem(StoryItemVo storyItemVo);
|
void createStoryItem(StoryItemAddVo storyItemVo, List<MultipartFile> images);
|
||||||
void createItemWithCover(StoryItemVo storyItemVo, MultipartFile cover, List<MultipartFile> images);
|
|
||||||
StoryItemWithCoverVo getStoryItemWithCover(String itemId);
|
StoryItemWithCoverVo getStoryItemWithCover(String itemId);
|
||||||
void updateItem(String itemId, String description, String location);
|
void updateItem(String itemId, String description, String location);
|
||||||
void deleteItem(String itemId);
|
void deleteItem(String itemId);
|
||||||
|
|
|
@ -10,6 +10,7 @@ import com.timeline.story.dao.StoryItemMapper;
|
||||||
import com.timeline.story.entity.StoryItem;
|
import com.timeline.story.entity.StoryItem;
|
||||||
import com.timeline.story.feign.FileServiceClient;
|
import com.timeline.story.feign.FileServiceClient;
|
||||||
import com.timeline.story.service.StoryItemService;
|
import com.timeline.story.service.StoryItemService;
|
||||||
|
import com.timeline.story.vo.StoryItemAddVo;
|
||||||
import com.timeline.story.vo.StoryItemVo;
|
import com.timeline.story.vo.StoryItemVo;
|
||||||
import com.timeline.story.vo.StoryItemWithCoverVo;
|
import com.timeline.story.vo.StoryItemWithCoverVo;
|
||||||
import com.timeline.common.utils.IdUtils;
|
import com.timeline.common.utils.IdUtils;
|
||||||
|
@ -33,35 +34,10 @@ public class StoryItemServiceImpl implements StoryItemService {
|
||||||
@Autowired
|
@Autowired
|
||||||
private CommonRelationMapper commonRelationMapper;
|
private CommonRelationMapper commonRelationMapper;
|
||||||
|
|
||||||
@Override
|
|
||||||
public void createItem(StoryItemVo storyItemVo) {
|
|
||||||
try {
|
|
||||||
StoryItem item = new StoryItem();
|
|
||||||
item.setInstanceId(IdUtils.randomUuidUpper());
|
|
||||||
item.setMasterItemId(storyItemVo.getMasterItemId());
|
|
||||||
item.setDescription(storyItemVo.getDescription());
|
|
||||||
item.setTitle(storyItemVo.getTitle());
|
|
||||||
item.setLocation(storyItemVo.getLocation());
|
|
||||||
item.setCreateId("createId");
|
|
||||||
item.setIsDelete(0);
|
|
||||||
item.setCreateTime(LocalDateTime.now());
|
|
||||||
item.setUpdateTime(LocalDateTime.now());
|
|
||||||
item.setStoryItemTime(storyItemVo.getStoryItemTime());
|
|
||||||
storyItemMapper.insert(item);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("创建 StoryItem 失败", e);
|
|
||||||
throw new RuntimeException("创建 StoryItem 失败");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createItemWithCover(StoryItemVo storyItemVo, MultipartFile cover, List<MultipartFile> images) {
|
public void createStoryItem(StoryItemAddVo storyItemVo, List<MultipartFile> images) {
|
||||||
try {
|
try {
|
||||||
// 1. 上传封面到 file 服务
|
|
||||||
ResponseEntity<String> coverResponse = fileServiceClient.uploadCover(cover);
|
|
||||||
String coverKey = coverResponse.getData();
|
|
||||||
log.info("上传成功,文件instanceId:{}", coverKey);
|
|
||||||
|
|
||||||
// 2. 创建 StoryItem 实体
|
// 2. 创建 StoryItem 实体
|
||||||
StoryItem item = new StoryItem();
|
StoryItem item = new StoryItem();
|
||||||
item.setInstanceId(IdUtils.randomUuidUpper());
|
item.setInstanceId(IdUtils.randomUuidUpper());
|
||||||
|
@ -72,21 +48,25 @@ public class StoryItemServiceImpl implements StoryItemService {
|
||||||
item.setLocation(storyItemVo.getLocation());
|
item.setLocation(storyItemVo.getLocation());
|
||||||
item.setCreateId("createId");
|
item.setCreateId("createId");
|
||||||
item.setStoryItemTime(storyItemVo.getStoryItemTime());
|
item.setStoryItemTime(storyItemVo.getStoryItemTime());
|
||||||
item.setIsDelete(0);
|
item.setIsDelete(CommonConstants.NOT_DELETED);
|
||||||
item.setCoverInstanceId(coverKey);
|
|
||||||
|
|
||||||
// 3. 上传所有图片并建立关联
|
|
||||||
for (MultipartFile image : images) {
|
|
||||||
ResponseEntity<String> response = fileServiceClient.uploadCover(image);
|
|
||||||
String key = response.getData();
|
|
||||||
log.info("上传成功,文件instanceId:{}", key);
|
|
||||||
// 4. 保存封面与 StoryItem 的关联关系
|
|
||||||
buildStoryItemImageRelation(item.getInstanceId(), key);
|
|
||||||
}
|
|
||||||
// 4. 记录封面与 StoryItem 的关联关系
|
|
||||||
buildStoryItemImageRelation(item.getInstanceId(), coverKey);
|
|
||||||
// 3. 插入到 story_item 表
|
|
||||||
storyItemMapper.insert(item);
|
storyItemMapper.insert(item);
|
||||||
|
if (storyItemVo.getRelatedImageInstanceIds() != null && !storyItemVo.getRelatedImageInstanceIds().isEmpty()) {
|
||||||
|
for (String imageInstanceId : storyItemVo.getRelatedImageInstanceIds()) {
|
||||||
|
log.info("关联现有图像 {} - {}", imageInstanceId, item.getInstanceId());
|
||||||
|
// 3. 建立 StoryItem 与图像关系
|
||||||
|
buildStoryItemImageRelation(item.getInstanceId(), imageInstanceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (images != null) {
|
||||||
|
log.info("上传 StoryItem 关联图像");
|
||||||
|
for (MultipartFile image : images) {
|
||||||
|
ResponseEntity<String> response = fileServiceClient.uploadImage(image);
|
||||||
|
String key = response.getData();
|
||||||
|
log.info("上传成功,文件instanceId:{}", key);
|
||||||
|
// 4. 建立图像与StoryItem 关系
|
||||||
|
buildStoryItemImageRelation(item.getInstanceId(), key);
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("创建 StoryItem 并上传封面失败", e);
|
log.error("创建 StoryItem 并上传封面失败", e);
|
||||||
throw new CustomException(ResponseEnum.INTERNAL_SERVER_ERROR, "上传封面失败");
|
throw new CustomException(ResponseEnum.INTERNAL_SERVER_ERROR, "上传封面失败");
|
||||||
|
@ -160,7 +140,7 @@ public class StoryItemServiceImpl implements StoryItemService {
|
||||||
return storyItemMapper.countByStoryId(instanceId);
|
return storyItemMapper.countByStoryId(instanceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildStoryItemImageRelation(String storyItemId, String imageIds) {
|
private void buildStoryItemImageRelation(String storyItemId, String imageIds) {
|
||||||
CommonRelationDTO relationDTO = new CommonRelationDTO();
|
CommonRelationDTO relationDTO = new CommonRelationDTO();
|
||||||
relationDTO.setRelaId(storyItemId);
|
relationDTO.setRelaId(storyItemId);
|
||||||
relationDTO.setSubRelaId(imageIds);
|
relationDTO.setSubRelaId(imageIds);
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
package com.timeline.story.vo;
|
package com.timeline.story.vo;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@Data
|
@Data
|
||||||
public class StoryItemAddVo extends StoryItemVo{
|
public class StoryItemAddVo extends StoryItemVo{
|
||||||
private MultipartFile cover;
|
private List<String> relatedImageInstanceIds;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
<mapper namespace="com.timeline.story.dao.StoryItemMapper">
|
<mapper namespace="com.timeline.story.dao.StoryItemMapper">
|
||||||
|
|
||||||
<insert id="insert">
|
<insert id="insert">
|
||||||
INSERT INTO story_item (instance_id, master_item_id, description, location, title, create_id, story_instance_id, is_delete, story_item_time, cover_instance_id)
|
INSERT INTO story_item (instance_id, master_item_id, description, location, title, create_id, story_instance_id, is_delete, story_item_time)
|
||||||
VALUES (#{instanceId}, #{masterItemId}, #{description}, #{location}, #{title},#{createId}, #{storyInstanceId}, #{isDelete}, #{storyItemTime}, #{coverInstanceId})
|
VALUES (#{instanceId}, #{masterItemId}, #{description}, #{location}, #{title},#{createId}, #{storyInstanceId}, #{isDelete}, #{storyItemTime})
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
<update id="update">
|
<update id="update">
|
||||||
|
|
Loading…
Reference in New Issue