diff --git a/src/main/java/com/youlai/boot/file/service/impl/AliyunFileService.java b/src/main/java/com/youlai/boot/file/service/impl/AliyunFileService.java index 2688f0d..baba791 100644 --- a/src/main/java/com/youlai/boot/file/service/impl/AliyunFileService.java +++ b/src/main/java/com/youlai/boot/file/service/impl/AliyunFileService.java @@ -6,6 +6,8 @@ import cn.hutool.core.lang.Assert; import cn.hutool.core.util.IdUtil; import com.aliyun.oss.OSS; import com.aliyun.oss.OSSClientBuilder; +import com.aliyun.oss.model.DeleteObjectsRequest; +import com.aliyun.oss.model.DeleteObjectsResult; import com.aliyun.oss.model.ObjectMetadata; import com.aliyun.oss.model.PutObjectRequest; import com.youlai.boot.file.service.FileService; @@ -14,6 +16,7 @@ import jakarta.annotation.PostConstruct; import lombok.Data; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @@ -21,6 +24,7 @@ import org.springframework.web.multipart.MultipartFile; import java.io.InputStream; import java.time.LocalDateTime; +import java.util.List; /** * Aliyun 对象存储服务类 @@ -33,6 +37,7 @@ import java.time.LocalDateTime; @ConfigurationProperties(prefix = "oss.aliyun") @RequiredArgsConstructor @Data +@Slf4j public class AliyunFileService implements FileService { /** * 服务Endpoint @@ -109,4 +114,17 @@ public class AliyunFileService implements FileService { aliyunOssClient.deleteObject(bucketName, fileName); return true; } + + public void deleteMultiFile(List objectNames) { + + DeleteObjectsRequest deleteRequest = new DeleteObjectsRequest(bucketName).withKeys(objectNames); + DeleteObjectsResult deleteResult = aliyunOssClient.deleteObjects(deleteRequest); + + List deletedObjects = deleteResult.getDeletedObjects(); + + deletedObjects.forEach(key -> log.info(" - {}", key)); + + log.info("aliyun oss files delete successfully......."); + + } } diff --git a/src/main/java/com/youlai/boot/mini/controller/StrayAnimalController.java b/src/main/java/com/youlai/boot/mini/controller/StrayAnimalController.java index 3ff6dcc..78feb1f 100644 --- a/src/main/java/com/youlai/boot/mini/controller/StrayAnimalController.java +++ b/src/main/java/com/youlai/boot/mini/controller/StrayAnimalController.java @@ -6,6 +6,7 @@ import com.youlai.boot.common.enums.ActionTypeEnum; import com.youlai.boot.common.enums.LogModuleEnum; import com.youlai.boot.common.model.Option; import com.youlai.boot.common.result.Result; +import com.youlai.boot.mini.model.dto.DeleteStrayAnimalNoteMediaDTO; import com.youlai.boot.mini.model.form.StrayAnimalForm; import com.youlai.boot.mini.service.StrayAnimalService; import io.swagger.v3.oas.annotations.Operation; @@ -35,7 +36,7 @@ public class StrayAnimalController { private final StrayAnimalService strayAnimalService; @Operation(summary = "添加动物信息") - @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @PostMapping(value = "save", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @RepeatSubmit @Log(module = LogModuleEnum.STRAY_ANIMAL_INFO, value = ActionTypeEnum.INSERT) public Result saveStrayAnimal( @@ -49,13 +50,37 @@ public class StrayAnimalController { @Operation(summary = "编辑动物信息(不包含图片/视频媒体资源)") - @PutMapping(value = "/{uuid}") + @PostMapping(value = "/update/{animalUuid}") @Log(module = LogModuleEnum.STRAY_ANIMAL_INFO, value = ActionTypeEnum.UPDATE) public Result updateStrayAnimal( - @PathVariable String uuid, + @PathVariable String animalUuid, @Validated @RequestBody StrayAnimalForm formData ) { - strayAnimalService.updateStrayAnimal(uuid, formData); + strayAnimalService.updateStrayAnimal(animalUuid, formData); + return Result.success(); + } + + @Operation(summary = "编辑动物信息时,删除流浪动物信息媒体资源") + @PostMapping(value = "/update/deleteMediaSource") + @Log(module = LogModuleEnum.STRAY_ANIMAL_INFO, value = ActionTypeEnum.UPDATE) + public Result deleteMediaSource( + @RequestBody @Validated DeleteStrayAnimalNoteMediaDTO deleteStrayAnimalNoteMediaDTO + ){ + strayAnimalService.deleteMediaSource(deleteStrayAnimalNoteMediaDTO); + return Result.success(); + } + + + @Operation(summary = "编辑动物信息时,添加流浪动物信息媒体资源", description = "比如补充图片、补充视频") + @PostMapping(value = "update/saveMediaSource/{noteMediaUuid}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @RepeatSubmit + @Log(module = LogModuleEnum.STRAY_ANIMAL_INFO, value = ActionTypeEnum.UPDATE) + public Result saveMediaSource( + @PathVariable String noteMediaUuid, + @RequestPart(name = "images", required = false) List images, + @RequestPart(name = "videos", required = false) List videos + ) { + strayAnimalService.saveMediaSource(noteMediaUuid, images, videos); return Result.success(); } diff --git a/src/main/java/com/youlai/boot/mini/mapper/MiniStrayAnimalNoteMediaMapper.java b/src/main/java/com/youlai/boot/mini/mapper/MiniStrayAnimalNoteMediaMapper.java index 4f60da1..c326c67 100644 --- a/src/main/java/com/youlai/boot/mini/mapper/MiniStrayAnimalNoteMediaMapper.java +++ b/src/main/java/com/youlai/boot/mini/mapper/MiniStrayAnimalNoteMediaMapper.java @@ -3,6 +3,8 @@ package com.youlai.boot.mini.mapper; import com.youlai.boot.mini.model.entity.MiniStrayAnimalNoteMedia; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import java.util.Map; + /** * 流浪信息资源表 Mapper 接口 * @@ -11,4 +13,5 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; */ public interface MiniStrayAnimalNoteMediaMapper extends BaseMapper { + int getMediaCountByNoteIdAndType(Map param); } diff --git a/src/main/java/com/youlai/boot/mini/model/dto/DeleteStrayAnimalNoteMediaDTO.java b/src/main/java/com/youlai/boot/mini/model/dto/DeleteStrayAnimalNoteMediaDTO.java new file mode 100644 index 0000000..3010dbb --- /dev/null +++ b/src/main/java/com/youlai/boot/mini/model/dto/DeleteStrayAnimalNoteMediaDTO.java @@ -0,0 +1,36 @@ +package com.youlai.boot.mini.model.dto; + +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +@Data +public class DeleteStrayAnimalNoteMediaDTO { + + @NotEmpty(message = "媒体资源uuid不能为空") + @ArraySchema( + arraySchema = @Schema( + description = "流浪动物信息媒体资源uuid列表", + example = "[\"uuid1\",\"uuid2\"]", + requiredMode = Schema.RequiredMode.REQUIRED + ), + schema = @Schema( + description = "媒体资源uuid", + example = "uuid1" + ) + ) + private List<@NotBlank(message = "uuid不能为空") String> noteMediaUuidList; + + @NotBlank(message = "笔记uuid不能为空") + @Schema( + description = "流浪动物信息笔记uuid", + example = "2d7890db8a6e47016ccef2d679d5a2c8", + requiredMode = Schema.RequiredMode.REQUIRED + ) + private String noteUuid; +} diff --git a/src/main/java/com/youlai/boot/mini/model/entity/MiniStrayAnimalNoteMedia.java b/src/main/java/com/youlai/boot/mini/model/entity/MiniStrayAnimalNoteMedia.java index 9cd53ed..5760a35 100644 --- a/src/main/java/com/youlai/boot/mini/model/entity/MiniStrayAnimalNoteMedia.java +++ b/src/main/java/com/youlai/boot/mini/model/entity/MiniStrayAnimalNoteMedia.java @@ -62,7 +62,7 @@ public class MiniStrayAnimalNoteMedia implements Serializable { @TableField("sort_order") @Schema(description = "排序顺序") - private Integer sortOrder; + private Long sortOrder; @TableField("create_time") @Schema(description = "创建时间") diff --git a/src/main/java/com/youlai/boot/mini/service/StrayAnimalService.java b/src/main/java/com/youlai/boot/mini/service/StrayAnimalService.java index 718ce36..9c9f5c5 100644 --- a/src/main/java/com/youlai/boot/mini/service/StrayAnimalService.java +++ b/src/main/java/com/youlai/boot/mini/service/StrayAnimalService.java @@ -1,6 +1,7 @@ package com.youlai.boot.mini.service; import com.baomidou.mybatisplus.extension.service.IService; +import com.youlai.boot.mini.model.dto.DeleteStrayAnimalNoteMediaDTO; import com.youlai.boot.mini.model.entity.MiniStrayAnimal; import com.youlai.boot.mini.model.form.StrayAnimalForm; import jakarta.validation.Valid; @@ -12,5 +13,9 @@ public interface StrayAnimalService extends IService { String saveStrayAnimal(@Valid StrayAnimalForm formData, List images, List videos); - void updateStrayAnimal(String uuid, StrayAnimalForm formData); + void updateStrayAnimal(String animalUuid, StrayAnimalForm formData); + + void deleteMediaSource(DeleteStrayAnimalNoteMediaDTO deleteStrayAnimalNoteMediaDTO); + + void saveMediaSource(String noteMediaUuid, List images, List videos); } diff --git a/src/main/java/com/youlai/boot/mini/service/impl/StrayAnimalServiceImpl.java b/src/main/java/com/youlai/boot/mini/service/impl/StrayAnimalServiceImpl.java index e9bdc89..42195fe 100644 --- a/src/main/java/com/youlai/boot/mini/service/impl/StrayAnimalServiceImpl.java +++ b/src/main/java/com/youlai/boot/mini/service/impl/StrayAnimalServiceImpl.java @@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.youlai.boot.common.constant.CommonConstants; +import com.youlai.boot.common.exception.BusinessException; import com.youlai.boot.common.util.CoordinateTransformUtils; import com.youlai.boot.common.util.FileUtils; import com.youlai.boot.common.util.JavaVCUtils; @@ -18,6 +19,7 @@ import com.youlai.boot.mini.converter.MiniStrayAnimalConverter; import com.youlai.boot.mini.mapper.MiniStrayAnimalMapper; import com.youlai.boot.mini.mapper.MiniStrayAnimalNoteMapper; import com.youlai.boot.mini.mapper.MiniStrayAnimalNoteMediaMapper; +import com.youlai.boot.mini.model.dto.DeleteStrayAnimalNoteMediaDTO; import com.youlai.boot.mini.model.enums.AnimalNoteMediaTypeEnum; import com.youlai.boot.mini.model.entity.MiniStrayAnimal; import com.youlai.boot.mini.model.entity.MiniStrayAnimalNote; @@ -26,6 +28,7 @@ import com.youlai.boot.mini.model.form.StrayAnimalForm; import com.youlai.boot.mini.service.StrayAnimalService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.io.FilenameUtils; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.GeometryFactory; @@ -37,9 +40,8 @@ import org.springframework.web.multipart.MultipartFile; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; -import java.util.Date; -import java.util.List; -import java.util.UUID; +import java.text.MessageFormat; +import java.util.*; /** * 流浪动物业务实现类 @@ -71,20 +73,22 @@ public class StrayAnimalServiceImpl extends ServiceImpl images, List videos) { - int sortOrder = 0; + private void saveMediaFiles(MiniStrayAnimalNote note, List images, List videos, long currentTimestamp) { + long sortOrder = currentTimestamp; // 处理图片 if (images != null) { for (MultipartFile image : images) { try { String objectName = "animal_note/" + note.getId() + "/" - + note.getCreateTimestamp() + RandomNumberUtils.createRandomLowerLetterAndNumber(8) + + currentTimestamp + RandomNumberUtils.createRandomLowerLetterAndNumber(8) + "." + FilenameUtils.getExtension(image.getOriginalFilename()); String url = aliyunFileService.uploadFile(objectName, image.getInputStream()); @@ -98,8 +102,8 @@ public class StrayAnimalServiceImpl extends ServiceImpl images, List videos) { // 验证必填图片 - Assert.notEmpty(images, "需要上传图片"); + if (images == null || images.isEmpty()) { + throw new BusinessException("需要上传图片"); + } // 验证图片数量 - Assert.isTrue( - images.size() <= CommonConstants.STRAY_ANIMAL_IMAGE_NUM_LIMIT, - "最多只能上传" + CommonConstants.STRAY_ANIMAL_IMAGE_NUM_LIMIT + "张图片"); + if (images.size() > CommonConstants.STRAY_ANIMAL_IMAGE_NUM_LIMIT) { + throw new BusinessException( + "最多只能上传" + CommonConstants.STRAY_ANIMAL_IMAGE_NUM_LIMIT + "张图片" + ); + } + // 验证视频数量 - Assert.isTrue( - CollUtil.isEmpty(videos) || videos.size() <= CommonConstants.STRAY_ANIMAL_VIDEO_NUM_LIMIT, - "最多只能上传" + CommonConstants.STRAY_ANIMAL_VIDEO_NUM_LIMIT + "个视频"); + if (!CollUtil.isEmpty(videos) && videos.size() > CommonConstants.STRAY_ANIMAL_VIDEO_NUM_LIMIT) { + throw new BusinessException( + "最多只能上传" + CommonConstants.STRAY_ANIMAL_VIDEO_NUM_LIMIT + "个视频" + ); + } } @Override - public void updateStrayAnimal(String uuid, StrayAnimalForm formData) { + public void updateStrayAnimal(String animalUuid, StrayAnimalForm formData) { // 校验动物是否存在 MiniStrayAnimal animal = miniStrayAnimalMapper.selectOne( new LambdaQueryWrapper() - .eq(MiniStrayAnimal::getUuid, uuid) + .eq(MiniStrayAnimal::getUuid, animalUuid) .eq(MiniStrayAnimal::getDeleted, 0) ); - Assert.notNull(animal, "动物不存在"); + if (animal == null) { + throw new BusinessException("动物不存在"); + } + + if (1 == animal.getAuditStatus()) { + throw new BusinessException("审核中无法更改"); + } long currentTimestamp = System.currentTimeMillis(); @@ -235,6 +252,7 @@ public class StrayAnimalServiceImpl extends ServiceImpl noteMediaUuidList = deleteStrayAnimalNoteMediaDTO.getNoteMediaUuidList(); + + MiniStrayAnimalNote note = miniStrayAnimalNoteMapper.selectOne(new LambdaQueryWrapper() + .eq(MiniStrayAnimalNote::getUuid, deleteStrayAnimalNoteMediaDTO.getNoteUuid())); + if (note == null) { + throw new BusinessException("笔记不存在"); + } + + LambdaQueryWrapper queryWrapper = + new LambdaQueryWrapper() + .in(MiniStrayAnimalNoteMedia::getUuid, noteMediaUuidList) + .eq(MiniStrayAnimalNoteMedia::getNoteId, note.getId()) + .eq(MiniStrayAnimalNoteMedia::getDeleted, 0); + + List mediaList = + miniStrayAnimalNoteMediaMapper.selectList(queryWrapper); + + if (CollectionUtils.isNotEmpty(mediaList)) { + + // 删除源文件 + List storageKeyList = mediaList.stream() + .map(MiniStrayAnimalNoteMedia::getStorageKey) + .toList(); + aliyunFileService.deleteMultiFile(storageKeyList); + + long currentTimestamp = System.currentTimeMillis(); + + // 逻辑删除 + miniStrayAnimalNoteMediaMapper.update( + null, + new LambdaUpdateWrapper() + .in(MiniStrayAnimalNoteMedia::getUuid, noteMediaUuidList) + .eq(MiniStrayAnimalNoteMedia::getNoteId, note.getId()) + .set(MiniStrayAnimalNoteMedia::getDeleted, 1) + .set(MiniStrayAnimalNoteMedia::getUpdateTimestamp, currentTimestamp) + .set(MiniStrayAnimalNoteMedia::getUpdateTime, new Date(currentTimestamp)) + .set(MiniStrayAnimalNoteMedia::getUpdateBy, SecurityUtils.getUserId()) + ); + } + } + + @Override + public void saveMediaSource(String noteMediaUuid, List images, List videos) { + if ((images == null || images.isEmpty()) && (videos == null || videos.isEmpty())) { + throw new BusinessException("需要上传媒体资源"); + } + MiniStrayAnimalNote note = miniStrayAnimalNoteMapper.selectOne( + new LambdaQueryWrapper() + .eq(MiniStrayAnimalNote::getUuid, noteMediaUuid) + .eq(MiniStrayAnimalNote::getDeleted, 0) + ); + if (note == null) { + throw new BusinessException("笔记不存在"); + } + + Long animalId = note.getStrayAnimalId(); + MiniStrayAnimal miniStrayAnimal = miniStrayAnimalMapper.selectById(animalId); + if (miniStrayAnimal == null) { + throw new BusinessException("动物信息不存在"); + } + if (1 == miniStrayAnimal.getAuditStatus()){ + throw new BusinessException("审核中"); + } + + //校验数量有没有超出 + Map param = new HashMap<>(); + param.put("noteId", note.getId()); + + if (CollectionUtils.isNotEmpty(images)){ + param.put("mediaType", AnimalNoteMediaTypeEnum.IMAGE.name().toLowerCase()); + int imagesExist = miniStrayAnimalNoteMediaMapper.getMediaCountByNoteIdAndType(param); + if (imagesExist+images.size() > CommonConstants.STRAY_ANIMAL_IMAGE_NUM_LIMIT) { + throw new BusinessException("图片总数不能超过" + CommonConstants.STRAY_ANIMAL_IMAGE_NUM_LIMIT + "张"); + } + } + if (CollectionUtils.isNotEmpty(videos)){ + param.put("mediaType", AnimalNoteMediaTypeEnum.VIDEO.name().toLowerCase()); + int videosExist = miniStrayAnimalNoteMediaMapper.getMediaCountByNoteIdAndType(param); + if(videosExist+videos.size() > CommonConstants.STRAY_ANIMAL_VIDEO_NUM_LIMIT){ + throw new BusinessException("视频总数不能超过" + CommonConstants.STRAY_ANIMAL_VIDEO_NUM_LIMIT + "个"); + } + } + + saveMediaFiles(note, images, videos, System.currentTimeMillis()); + + } + } diff --git a/src/main/resources/mapper/mini/MiniStrayAnimalNoteMediaMapper.xml b/src/main/resources/mapper/mini/MiniStrayAnimalNoteMediaMapper.xml index 4ff3cf4..e7dbb62 100644 --- a/src/main/resources/mapper/mini/MiniStrayAnimalNoteMediaMapper.xml +++ b/src/main/resources/mapper/mini/MiniStrayAnimalNoteMediaMapper.xml @@ -5,5 +5,13 @@ +