diff --git a/pom.xml b/pom.xml
index 14b2e0d..ad99baa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -74,6 +74,11 @@
caffeine
3.1.8
+
+
+ org.springframework.session
+ spring-session-data-redis
+
org.springframework.boot
spring-boot-configuration-processor
diff --git a/sql/create_table.sql b/sql/create_table.sql
index b1e5280..46aba32 100644
--- a/sql/create_table.sql
+++ b/sql/create_table.sql
@@ -61,4 +61,4 @@ CREATE INDEX idx_reviewStatus ON picture (review_status);
ALTER TABLE picture
-- 添加新列
ADD COLUMN original_url varchar(512) NULL COMMENT '原图 url',
- ADD COLUMN thumbnail_url varchar(512) NULL COMMENT '缩略图 url';
\ No newline at end of file
+ ADD COLUMN thumbnail_url varchar(512) NULL COMMENT '缩略图 url';
diff --git a/src/main/java/edu/whut/smilepicturebackend/controller/PictureController.java b/src/main/java/edu/whut/smilepicturebackend/controller/PictureController.java
index 8403ebd..4d22916 100644
--- a/src/main/java/edu/whut/smilepicturebackend/controller/PictureController.java
+++ b/src/main/java/edu/whut/smilepicturebackend/controller/PictureController.java
@@ -40,7 +40,8 @@ public class PictureController {
/**
- * 上传图片(可重新上传)
+ * 上传图片(可重新上传),前端选中图片就会调用该接口,无需前端点'创建'按钮!
+ *
*/
@PostMapping("/upload")
// @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
@@ -79,20 +80,7 @@ public class PictureController {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
User loginUser = userService.getLoginUser(request);
- Long id = deleteRequest.getId();
- //判断用户是否存在
- Picture oldPicture = pictureService.getById(id);
- ThrowUtils.throwIf(oldPicture == null, ErrorCode.NOT_FOUND_ERROR);
- // 仅本人或者管理员可删除
- if (!oldPicture.getUserId().equals(loginUser.getId())
- && !userService.isAdmin(loginUser)) {
- throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
- }
- // 操作数据库
- boolean result = pictureService.removeById(id);
- ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
- //清理图片资源
- pictureService.clearPictureFile(oldPicture);
+ pictureService.deletePicture(deleteRequest.getId(), loginUser);
return ResultUtils.success(true);
}
@@ -209,7 +197,7 @@ public class PictureController {
}
/**
- * 编辑图片(给用户使用)
+ * 编辑图片(给用户使用)或创建图片时,编辑标签、分类的时候
*/
@PostMapping("/edit")
public BaseResponse editPicture(@RequestBody PictureEditRequest pictureEditRequest, HttpServletRequest request) {
diff --git a/src/main/java/edu/whut/smilepicturebackend/manager/CosManager.java b/src/main/java/edu/whut/smilepicturebackend/manager/CosManager.java
index 27e4db3..b875ac8 100644
--- a/src/main/java/edu/whut/smilepicturebackend/manager/CosManager.java
+++ b/src/main/java/edu/whut/smilepicturebackend/manager/CosManager.java
@@ -1,15 +1,19 @@
package edu.whut.smilepicturebackend.manager;
import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.StrUtil;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.model.*;
import com.qcloud.cos.model.ciModel.persistence.PicOperations;
import edu.whut.smilepicturebackend.config.CosClientConfig;
import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.File;
import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
@@ -19,6 +23,7 @@ import java.util.List;
*/
@Component
@RequiredArgsConstructor
+@Slf4j
public class CosManager {
private final CosClientConfig cosClientConfig;
@@ -155,11 +160,45 @@ public class CosManager {
/**
- * 删除对象
- *
- * @param key 唯一键
+ * 删除对象 https://cloud.tencent.com/document/product/436/65939
+ * @param url
*/
- public void deleteObject(String key) {
- cosClient.deleteObject(cosClientConfig.getBucket(), key);
+ public void deleteIfNotBlank(String url) {
+ if (StrUtil.isBlank(url)) {
+ return;
+ }
+ try {
+ // 1. 解析 URL → 得到对象 key
+ String key = extractUploadPath(url);
+ if (StrUtil.isBlank(key)) {
+ log.warn("无法从 URL 解析出 COS 对象 key,跳过删除: {}", url);
+ return;
+ }
+ // 2. 调用 COS SDK 删除
+ log.info("清理图片中... {}", key);
+ cosClient.deleteObject(cosClientConfig.getBucket(), key);
+ } catch (IllegalArgumentException e) {
+ // URL 格式非法或解析失败时给出警告,不中断业务
+ log.warn("删除图片失败:URL 解析异常,url={}", url, e);
+ }
}
+
+ /**
+ * 从完整 COS URL 中解析出对象 key(即 uploadPath)
+ *
+ * @param originalUrl 形如 https://bucket.cos.xxx/my/prefix/file.png
+ * @return my/prefix/file.png
+ */
+ public static String extractUploadPath(String originalUrl) {
+ try {
+ // 1. 直接拿 URI 的 path 部分,例如 "//smile-picture/public/.../xxx.png"
+ String path = new URI(originalUrl).getPath();
+
+ // 2. 去掉所有前导斜杠,得到真正的 COS 对象 key
+ return path.replaceFirst("^/+", ""); // => smile-picture/public/.../xxx.png
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException("非法的 COS URL: " + originalUrl, e);
+ }
+ }
+
}
diff --git a/src/main/java/edu/whut/smilepicturebackend/manager/upload/PictureUploadTemplate.java b/src/main/java/edu/whut/smilepicturebackend/manager/upload/PictureUploadTemplate.java
index d373d1b..a508002 100644
--- a/src/main/java/edu/whut/smilepicturebackend/manager/upload/PictureUploadTemplate.java
+++ b/src/main/java/edu/whut/smilepicturebackend/manager/upload/PictureUploadTemplate.java
@@ -135,6 +135,7 @@ public abstract class PictureUploadTemplate {
uploadPictureResult.setPicColor(imageInfo.getAve());
// 设置缩略图地址
uploadPictureResult.setThumbnailUrl(cosClientConfig.getHost() + "/" + thumbnailCiObject.getKey());
+ //设置原图地址
uploadPictureResult.setOriginalUrl(cosClientConfig.getHost() + "/" + uploadPath);
// 返回可访问的地址
return uploadPictureResult;
diff --git a/src/main/java/edu/whut/smilepicturebackend/service/PictureService.java b/src/main/java/edu/whut/smilepicturebackend/service/PictureService.java
index fba9e72..75b427e 100644
--- a/src/main/java/edu/whut/smilepicturebackend/service/PictureService.java
+++ b/src/main/java/edu/whut/smilepicturebackend/service/PictureService.java
@@ -43,6 +43,14 @@ public interface PictureService extends IService {
*/
LambdaQueryWrapper getQueryWrapper(PictureQueryRequest pictureQueryRequest);
+ /**
+ * 删除图片
+ *
+ * @param pictureId
+ * @param loginUser
+ */
+ void deletePicture(long pictureId, User loginUser);
+
/**
* 获取图片包装类(单条)
*
diff --git a/src/main/java/edu/whut/smilepicturebackend/service/impl/PictureServiceImpl.java b/src/main/java/edu/whut/smilepicturebackend/service/impl/PictureServiceImpl.java
index fdd1bb4..fe4393f 100644
--- a/src/main/java/edu/whut/smilepicturebackend/service/impl/PictureServiceImpl.java
+++ b/src/main/java/edu/whut/smilepicturebackend/service/impl/PictureServiceImpl.java
@@ -40,6 +40,7 @@ import org.jsoup.select.Elements;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
@@ -84,19 +85,19 @@ public class PictureServiceImpl extends ServiceImpl
ThrowUtils.throwIf(introduction.length() > 800, ErrorCode.PARAMS_ERROR, "简介过长");
}
}
+
@Override
public PictureVO uploadPicture(Object inputSource, PictureUploadRequest pictureUploadRequest, User loginUser) {
// 校验参数
ThrowUtils.throwIf(loginUser == null, ErrorCode.NO_AUTH_ERROR);
- // 判断是新增还是删除
- Long pictureId = null;
- if (pictureUploadRequest != null) {
- pictureId = pictureUploadRequest.getId();
- }
+ // 判断是创建还是替换
+ Long pictureId = pictureUploadRequest == null ? null : pictureUploadRequest.getId();
+ Picture oldPicture = null;
+
// 如果是更新,判断图片是否存在
if (pictureId != null) {
- Picture oldPicture = this.getById(pictureId);
+ oldPicture = this.getById(pictureId);
ThrowUtils.throwIf(oldPicture == null, ErrorCode.NOT_FOUND_ERROR, "图片不存在");
// 仅本人或管理员可编辑图片
if (!oldPicture.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)) {
@@ -112,8 +113,9 @@ public class PictureServiceImpl extends ServiceImpl
if (inputSource instanceof String) {
pictureUploadTemplate = urlPictureUpload;
}
+ //上传到腾讯云COS上
UploadPictureResult uploadPictureResult = pictureUploadTemplate.uploadPicture(inputSource, uploadPathPrefix);
- // 构造要入库的图片信息
+ // 构造要入库的图片信息,将图片信息存入数据库中。
Picture picture = new Picture();
// 复制同名属性(url、name、picSize、picWidth、picHeight、picScale、picFormat)
BeanUtils.copyProperties(uploadPictureResult, picture);
@@ -135,8 +137,11 @@ public class PictureServiceImpl extends ServiceImpl
picture.setEditTime(new Date());
}
boolean result = this.saveOrUpdate(picture);
- //todo:如果是更新,清理旧的图片
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "图片上传失败,数据库操作失败");
+ //如果是更新,清理旧的图片
+ if (oldPicture != null) {
+ this.clearPictureFile(oldPicture);
+ }
return PictureVO.objToVo(picture);
}
@@ -190,6 +195,22 @@ public class PictureServiceImpl extends ServiceImpl
return qw;
}
+ @Override
+ public void deletePicture(long pictureId, User loginUser) {
+ Picture oldPicture = this.getById(pictureId);
+ ThrowUtils.throwIf(oldPicture == null, ErrorCode.NOT_FOUND_ERROR);
+ // 仅本人或者管理员可删除
+ if (!oldPicture.getUserId().equals(loginUser.getId())
+ && !userService.isAdmin(loginUser)) {
+ throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
+ }
+ // 操作数据库
+ boolean result = this.removeById(pictureId);
+ ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
+ //清理图片资源
+ this.clearPictureFile(oldPicture);
+ }
+
@Override
public PictureVO getPictureVO(Picture picture, HttpServletRequest request) {
// 对象转封装类
@@ -418,7 +439,7 @@ public class PictureServiceImpl extends ServiceImpl
@Async //异步执行
@Override
public void clearPictureFile(Picture oldPicture) {
- // 判断改图片是否被多条记录使用(图片秒传的情况可能一个cos地址对应多个数据库url)
+ // 判断该图片是否被多条记录使用(图片秒传的情况可能一个cos地址对应多个数据库url)
String pictureUrl = oldPicture.getUrl();
long count = this.lambdaQuery()
.eq(Picture::getUrl, pictureUrl)
@@ -428,18 +449,18 @@ public class PictureServiceImpl extends ServiceImpl
return;
}
// 删除图片
- cosManager.deleteObject(pictureUrl);
+ cosManager.deleteIfNotBlank(pictureUrl);
+
//删除原图
String originalUrl=oldPicture.getOriginalUrl();
- if (StrUtil.isNotBlank(originalUrl)) {
- cosManager.deleteObject(originalUrl);
- }
+ cosManager.deleteIfNotBlank(originalUrl);
+
// 删除缩略图
String thumbnailUrl = oldPicture.getThumbnailUrl();
- if (StrUtil.isNotBlank(thumbnailUrl)) {
- cosManager.deleteObject(thumbnailUrl);
- }
+ cosManager.deleteIfNotBlank(thumbnailUrl);
+
}
+
}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index cc67e40..2998472 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -2,6 +2,10 @@ server:
port: 8123
servlet:
context-path: /api
+ # cookie 30 天过期
+ session:
+ cookie:
+ max-age: 2592000
spring:
profiles:
@@ -21,6 +25,11 @@ spring:
port: 6379
password: 123456
timeout: 5000
+ # Session 配置
+ session:
+ store-type: redis
+ # session 30 天后过期,单位是秒
+ timeout: 2592000
servlet:
multipart:
max-file-size: 10MB