diff --git a/src/main/java/edu/whut/smilepicturebackend/controller/PictureController.java b/src/main/java/edu/whut/smilepicturebackend/controller/PictureController.java index bf32794..2a46e92 100644 --- a/src/main/java/edu/whut/smilepicturebackend/controller/PictureController.java +++ b/src/main/java/edu/whut/smilepicturebackend/controller/PictureController.java @@ -11,11 +11,13 @@ import edu.whut.smilepicturebackend.exception.ErrorCode; import edu.whut.smilepicturebackend.exception.ThrowUtils; import edu.whut.smilepicturebackend.model.dto.picture.*; import edu.whut.smilepicturebackend.model.entity.Picture; +import edu.whut.smilepicturebackend.model.entity.Space; import edu.whut.smilepicturebackend.model.entity.User; import edu.whut.smilepicturebackend.model.enums.PictureReviewStatusEnum; import edu.whut.smilepicturebackend.model.vo.PictureTagCategory; import edu.whut.smilepicturebackend.model.vo.PictureVO; import edu.whut.smilepicturebackend.service.PictureService; +import edu.whut.smilepicturebackend.service.SpaceService; import edu.whut.smilepicturebackend.service.UserService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -36,6 +38,7 @@ import java.util.List; public class PictureController { private final UserService userService; private final PictureService pictureService; + private final SpaceService spaceService; @@ -157,6 +160,12 @@ public class PictureController { Picture picture = pictureService.getById(id); ThrowUtils.throwIf(picture == null, ErrorCode.NOT_FOUND_ERROR); ThrowUtils.throwIf(PictureReviewStatusEnum.PASS.getValue()!=picture.getReviewStatus(),ErrorCode.NOT_FOUND_ERROR); + // 空间权限校验 + Long spaceId = picture.getSpaceId(); + if (spaceId != null) { + User loginUser = userService.getLoginUser(request); + pictureService.checkPictureAuth(loginUser, picture); + } // 获取封装类 return ResultUtils.success(pictureService.getPictureVO(picture, request)); } @@ -185,8 +194,22 @@ public class PictureController { long size = pictureQueryRequest.getPageSize(); // 限制爬虫,一次不能请求超过20页 ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR); - // 普通用户默认只能看到审核通过的数据 - pictureQueryRequest.setReviewStatus(PictureReviewStatusEnum.PASS.getValue()); + // 空间权限校验 + Long spaceId = pictureQueryRequest.getSpaceId(); + if (spaceId == null) { + // 公开图库 + // 普通用户默认只能看到审核通过的数据 + pictureQueryRequest.setReviewStatus(PictureReviewStatusEnum.PASS.getValue()); + pictureQueryRequest.setNullSpaceId(true); + } else { + // 私有空间 + User loginUser = userService.getLoginUser(request); + Space space = spaceService.getById(spaceId); + ThrowUtils.throwIf(space == null, ErrorCode.NOT_FOUND_ERROR, "空间不存在"); + if (!loginUser.getId().equals(space.getUserId())) { + throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "没有空间权限"); + } + } // 查询数据库 Page picturePage = pictureService.page(new Page<>(current, size), pictureService.getQueryWrapper(pictureQueryRequest)); diff --git a/src/main/java/edu/whut/smilepicturebackend/controller/SpaceController.java b/src/main/java/edu/whut/smilepicturebackend/controller/SpaceController.java index 86ed450..7c0900b 100644 --- a/src/main/java/edu/whut/smilepicturebackend/controller/SpaceController.java +++ b/src/main/java/edu/whut/smilepicturebackend/controller/SpaceController.java @@ -187,4 +187,21 @@ public class SpaceController { return ResultUtils.success(true); } + /** + * 获取空间级别列表,便于前端展示 + * + * @return + */ + @GetMapping("/list/level") + public BaseResponse> listSpaceLevel() { + List spaceLevelList = Arrays.stream(SpaceLevelEnum.values()) + .map(spaceLevelEnum -> new SpaceLevel( + spaceLevelEnum.getValue(), + spaceLevelEnum.getText(), + spaceLevelEnum.getMaxCount(), + spaceLevelEnum.getMaxSize() + )) + .collect(Collectors.toList()); + return ResultUtils.success(spaceLevelList); + } } 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 c997e20..51af6c1 100644 --- a/src/main/java/edu/whut/smilepicturebackend/service/impl/PictureServiceImpl.java +++ b/src/main/java/edu/whut/smilepicturebackend/service/impl/PictureServiceImpl.java @@ -41,6 +41,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.support.TransactionTemplate; import org.springframework.util.DigestUtils; @@ -69,6 +70,7 @@ public class PictureServiceImpl extends ServiceImpl private final MyCacheManager cacheManager; private final CosManager cosManager; private final SpaceService spaceService; + private final TransactionTemplate transactionTemplate; @Override public void validPicture(Picture picture) { ThrowUtils.throwIf(picture == null, ErrorCode.PARAMS_ERROR); @@ -101,6 +103,13 @@ public class PictureServiceImpl extends ServiceImpl if (!loginUser.getId().equals(space.getUserId())) { throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "没有空间权限"); } + // 校验额度 + if (space.getTotalCount() >= space.getMaxCount()) { + throw new BusinessException(ErrorCode.OPERATION_ERROR, "空间条数不足"); + } + if (space.getTotalSize() >= space.getMaxSize()) { + throw new BusinessException(ErrorCode.OPERATION_ERROR, "空间大小不足"); + } } // 判断是创建还是替换 Long pictureId = pictureUploadRequest == null ? null : pictureUploadRequest.getId(); @@ -166,8 +175,28 @@ public class PictureServiceImpl extends ServiceImpl picture.setId(pictureId); picture.setEditTime(new Date()); } - boolean result = this.saveOrUpdate(picture); - ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "图片上传失败,数据库操作失败"); + // 计算差值,保证 oldPicture 为空时不抛异常 + long sizeDelta = picture.getPicSize() - (oldPicture == null ? 0 : oldPicture.getPicSize()); + long countDelta = (oldPicture == null ? 1 : 0); + // 开启事务,图片上传成功和修改额度一定要同时成功或失败 + Long finalSpaceId = spaceId; + transactionTemplate.execute(status -> { + // 插入数据 + boolean result = this.saveOrUpdate(picture); + ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "图片上传失败,数据库操作失败"); + if (finalSpaceId != null) { + // 更新空间的使用额度 + boolean update = spaceService.lambdaUpdate() + .eq(Space::getId, finalSpaceId) + // 更新 total_size + .apply(sizeDelta != 0, "total_size = total_size + {0}", sizeDelta) // 占位符安全绑定[1][2] + // 更新 total_count(只有新增才加 1) + .apply(countDelta != 0, "total_count = total_count + {0}", countDelta) + .update(); + ThrowUtils.throwIf(!update, ErrorCode.OPERATION_ERROR, "额度更新失败"); + } + return picture; + }); //如果是更新,清理旧的图片 if (oldPicture != null) { this.clearPictureFile(oldPicture); @@ -183,21 +212,23 @@ public class PictureServiceImpl extends ServiceImpl } // 精简版条件构造 - qw.eq(ObjUtil.isNotEmpty(req.getId()), Picture::getId, req.getId()) - .eq(ObjUtil.isNotEmpty(req.getUserId()), Picture::getUserId, req.getUserId()) - .like(StrUtil.isNotBlank(req.getName()), Picture::getName, req.getName()) + qw.eq(ObjUtil.isNotEmpty(req.getId()), Picture::getId, req.getId()) + .eq(ObjUtil.isNotEmpty(req.getUserId()), Picture::getUserId, req.getUserId()) + .eq(ObjUtil.isNotEmpty(req.getSpaceId()), Picture::getSpaceId, req.getSpaceId()) //指定 spaceId → 查该空间图片。 + .isNull(req.isNullSpaceId(), Picture::getSpaceId) //不传则查公共图库 + .like(StrUtil.isNotBlank(req.getName()), Picture::getName, req.getName()) .like(StrUtil.isNotBlank(req.getIntroduction()), Picture::getIntroduction, req.getIntroduction()) - .like(StrUtil.isNotBlank(req.getPicFormat()), Picture::getPicFormat, req.getPicFormat()) - .eq(ObjUtil.isNotEmpty(req.getReviewMessage()),Picture::getReviewMessage,req.getReviewMessage()) - .eq(StrUtil.isNotBlank(req.getCategory()), Picture::getCategory, req.getCategory()) - .eq(ObjUtil.isNotEmpty(req.getPicWidth()), Picture::getPicWidth, req.getPicWidth()) - .eq(ObjUtil.isNotEmpty(req.getPicHeight()), Picture::getPicHeight, req.getPicHeight()) - .eq(ObjUtil.isNotEmpty(req.getPicSize()), Picture::getPicSize, req.getPicSize()) - .eq(ObjUtil.isNotEmpty(req.getPicScale()), Picture::getPicScale, req.getPicScale()) - .eq(ObjUtil.isNotEmpty(req.getReviewStatus()),Picture::getReviewStatus,req.getReviewStatus()) - .eq(ObjUtil.isNotEmpty(req.getReviewerId()),Picture::getReviewerId,req.getReviewerId()) - .ge(ObjUtil.isNotEmpty(req.getStartEditTime()), Picture::getEditTime, req.getStartEditTime()) - .lt(ObjUtil.isNotEmpty(req.getEndEditTime()), Picture::getEditTime, req.getEndEditTime()); + .like(StrUtil.isNotBlank(req.getPicFormat()), Picture::getPicFormat, req.getPicFormat()) + .eq(ObjUtil.isNotEmpty(req.getReviewMessage()), Picture::getReviewMessage, req.getReviewMessage()) + .eq(StrUtil.isNotBlank(req.getCategory()), Picture::getCategory, req.getCategory()) + .eq(ObjUtil.isNotEmpty(req.getPicWidth()), Picture::getPicWidth, req.getPicWidth()) + .eq(ObjUtil.isNotEmpty(req.getPicHeight()), Picture::getPicHeight, req.getPicHeight()) + .eq(ObjUtil.isNotEmpty(req.getPicSize()), Picture::getPicSize, req.getPicSize()) + .eq(ObjUtil.isNotEmpty(req.getPicScale()), Picture::getPicScale, req.getPicScale()) + .eq(ObjUtil.isNotEmpty(req.getReviewStatus()), Picture::getReviewStatus, req.getReviewStatus()) + .eq(ObjUtil.isNotEmpty(req.getReviewerId()), Picture::getReviewerId, req.getReviewerId()) + .ge(ObjUtil.isNotEmpty(req.getStartEditTime()), Picture::getEditTime, req.getStartEditTime()) + .lt(ObjUtil.isNotEmpty(req.getEndEditTime()), Picture::getEditTime, req.getEndEditTime()); // 全字段模糊搜索 if (StrUtil.isNotBlank(req.getSearchText())) { @@ -236,9 +267,20 @@ public class PictureServiceImpl extends ServiceImpl } // 校验权限 checkPictureAuth(loginUser, oldPicture); - // 操作数据库 - boolean result = this.removeById(pictureId); - ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR); + // 开启事务 + transactionTemplate.execute(status -> { + // 操作数据库 + boolean result = this.removeById(pictureId); + ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR); + // 更新空间的使用额度,释放额度 + boolean update = spaceService.lambdaUpdate() + .eq(Space::getId, oldPicture.getSpaceId()) + .setSql("total_size = total_size - " + oldPicture.getPicSize()) + .setSql("total_count = total_count - 1") + .update(); + ThrowUtils.throwIf(!update, ErrorCode.OPERATION_ERROR, "额度更新失败"); + return true; + }); //清理图片资源 this.clearPictureFile(oldPicture); } @@ -260,7 +302,7 @@ public class PictureServiceImpl extends ServiceImpl ThrowUtils.throwIf(oldPicture == null, ErrorCode.NOT_FOUND_ERROR); // 校验权限 checkPictureAuth(loginUser, oldPicture); - // 补充审核参数,每次编辑图片都要重新过审 + // 补充审核 参数,每次编辑图片都要重新过审 this.fillReviewParams(picture, loginUser); // 操作数据库 boolean result = this.updateById(picture);