From 00c871f41e3bad4bd5ce65f6f9439dcfee5fc9a6 Mon Sep 17 00:00:00 2001 From: zhangsan <646228430@qq.com> Date: Tue, 8 Apr 2025 19:51:38 +0800 Subject: [PATCH] =?UTF-8?q?4.8=20=E5=9B=BE=E5=BA=93=E7=A9=BA=E9=97=B4?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E5=88=86=E6=9E=90=EF=BC=9A=E7=A7=81=E4=BA=BA?= =?UTF-8?q?=E5=92=8C=E5=85=AC=E5=85=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SpaceAnalyzeController.java | 134 ++++++++ .../space/analyze/SpaceAnalyzeRequest.java | 30 ++ .../analyze/SpaceCategoryAnalyzeRequest.java | 13 + .../analyze/SpaceRankAnalyzeRequest.java | 19 ++ .../analyze/SpaceSizeAnalyzeRequest.java | 13 + .../space/analyze/SpaceTagAnalyzeRequest.java | 13 + .../analyze/SpaceUsageAnalyzeRequest.java | 13 + .../analyze/SpaceUserAnalyzeRequest.java | 22 ++ .../analyze/SpaceCategoryAnalyzeResponse.java | 33 ++ .../analyze/SpaceSizeAnalyzeResponse.java | 28 ++ .../analyze/SpaceTagAnalyzeResponse.java | 28 ++ .../analyze/SpaceUsageAnalyzeResponse.java | 44 +++ .../analyze/SpaceUserAnalyzeResponse.java | 28 ++ .../service/SpaceAnalyzeService.java | 70 ++++ .../service/impl/SpaceAnalyzeServiceImpl.java | 310 ++++++++++++++++++ 15 files changed, 798 insertions(+) create mode 100644 src/main/java/edu/whut/smilepicturebackend/controller/SpaceAnalyzeController.java create mode 100644 src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceAnalyzeRequest.java create mode 100644 src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceCategoryAnalyzeRequest.java create mode 100644 src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceRankAnalyzeRequest.java create mode 100644 src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceSizeAnalyzeRequest.java create mode 100644 src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceTagAnalyzeRequest.java create mode 100644 src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceUsageAnalyzeRequest.java create mode 100644 src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceUserAnalyzeRequest.java create mode 100644 src/main/java/edu/whut/smilepicturebackend/model/vo/space/analyze/SpaceCategoryAnalyzeResponse.java create mode 100644 src/main/java/edu/whut/smilepicturebackend/model/vo/space/analyze/SpaceSizeAnalyzeResponse.java create mode 100644 src/main/java/edu/whut/smilepicturebackend/model/vo/space/analyze/SpaceTagAnalyzeResponse.java create mode 100644 src/main/java/edu/whut/smilepicturebackend/model/vo/space/analyze/SpaceUsageAnalyzeResponse.java create mode 100644 src/main/java/edu/whut/smilepicturebackend/model/vo/space/analyze/SpaceUserAnalyzeResponse.java create mode 100644 src/main/java/edu/whut/smilepicturebackend/service/SpaceAnalyzeService.java create mode 100644 src/main/java/edu/whut/smilepicturebackend/service/impl/SpaceAnalyzeServiceImpl.java diff --git a/src/main/java/edu/whut/smilepicturebackend/controller/SpaceAnalyzeController.java b/src/main/java/edu/whut/smilepicturebackend/controller/SpaceAnalyzeController.java new file mode 100644 index 0000000..fbef838 --- /dev/null +++ b/src/main/java/edu/whut/smilepicturebackend/controller/SpaceAnalyzeController.java @@ -0,0 +1,134 @@ +package edu.whut.smilepicturebackend.controller; +import edu.whut.smilepicturebackend.common.BaseResponse; +import edu.whut.smilepicturebackend.common.ResultUtils; +import edu.whut.smilepicturebackend.exception.ErrorCode; +import edu.whut.smilepicturebackend.exception.ThrowUtils; +import edu.whut.smilepicturebackend.model.dto.space.analyze.*; +import edu.whut.smilepicturebackend.model.entity.Space; +import edu.whut.smilepicturebackend.model.entity.User; +import edu.whut.smilepicturebackend.model.vo.analyze.*; +import edu.whut.smilepicturebackend.model.vo.space.analyze.*; +import edu.whut.smilepicturebackend.service.SpaceAnalyzeService; +import edu.whut.smilepicturebackend.service.UserService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +/** + * @author 程序员鱼皮 编程导航原创项目 + */ +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("/space/analyze") +public class SpaceAnalyzeController { + + private final UserService userService; + + private final SpaceAnalyzeService spaceAnalyzeService; + + /** + * 获取空间的使用状态 + * + * @param spaceUsageAnalyzeRequest + * @param request + * @return + */ + @PostMapping("/usage") + public BaseResponse getSpaceUsageAnalyze( + @RequestBody SpaceUsageAnalyzeRequest spaceUsageAnalyzeRequest, + HttpServletRequest request) { + ThrowUtils.throwIf(spaceUsageAnalyzeRequest == null, ErrorCode.PARAMS_ERROR); + User loginUser = userService.getLoginUser(request); + SpaceUsageAnalyzeResponse spaceUsageAnalyze = spaceAnalyzeService.getSpaceUsageAnalyze(spaceUsageAnalyzeRequest, loginUser); + return ResultUtils.success(spaceUsageAnalyze); + } + + /** + * 获取空间图片分类分析 + * + * @param spaceCategoryAnalyzeRequest + * @param request + * @return + */ + @PostMapping("/category") + public BaseResponse> getSpaceCategoryAnalyze( + @RequestBody SpaceCategoryAnalyzeRequest spaceCategoryAnalyzeRequest, + HttpServletRequest request) { + ThrowUtils.throwIf(spaceCategoryAnalyzeRequest == null, ErrorCode.PARAMS_ERROR); + User loginUser = userService.getLoginUser(request); + List spaceCategoryAnalyze = spaceAnalyzeService.getSpaceCategoryAnalyze(spaceCategoryAnalyzeRequest, loginUser); + return ResultUtils.success(spaceCategoryAnalyze); + } + + /** + * 获取空间图片标签分析 + * + * @param spaceTagAnalyzeRequest + * @param request + * @return + */ + @PostMapping("/tag") + public BaseResponse> getSpaceTagAnalyze( + @RequestBody SpaceTagAnalyzeRequest spaceTagAnalyzeRequest, + HttpServletRequest request) { + ThrowUtils.throwIf(spaceTagAnalyzeRequest == null, ErrorCode.PARAMS_ERROR); + User loginUser = userService.getLoginUser(request); + List spaceTagAnalyze = spaceAnalyzeService.getSpaceTagAnalyze(spaceTagAnalyzeRequest, loginUser); + return ResultUtils.success(spaceTagAnalyze); + } + + /** + * 获取空间图片大小分析 + * + * @param spaceSizeAnalyzeRequest + * @param request + * @return + */ + @PostMapping("/size") + public BaseResponse> getSpaceSizeAnalyze(@RequestBody SpaceSizeAnalyzeRequest spaceSizeAnalyzeRequest, + HttpServletRequest request) { + ThrowUtils.throwIf(spaceSizeAnalyzeRequest == null, ErrorCode.PARAMS_ERROR); + User loginUser = userService.getLoginUser(request); + List resultList = spaceAnalyzeService.getSpaceSizeAnalyze(spaceSizeAnalyzeRequest, loginUser); + return ResultUtils.success(resultList); + } + + /** + * 获取空间用户行为分析 + * + * @param spaceUserAnalyzeRequest + * @param request + * @return + */ + @PostMapping("/user") + public BaseResponse> getSpaceUserAnalyze(@RequestBody SpaceUserAnalyzeRequest spaceUserAnalyzeRequest, + HttpServletRequest request) { + ThrowUtils.throwIf(spaceUserAnalyzeRequest == null, ErrorCode.PARAMS_ERROR); + User loginUser = userService.getLoginUser(request); + List resultList = spaceAnalyzeService.getSpaceUserAnalyze(spaceUserAnalyzeRequest, loginUser); + return ResultUtils.success(resultList); + } + + /** + * 获取空间使用排行分析 + * + * @param spaceRankAnalyzeRequest + * @param request + * @return + */ + @PostMapping("/rank") + public BaseResponse> getSpaceRankAnalyze(@RequestBody SpaceRankAnalyzeRequest spaceRankAnalyzeRequest, + HttpServletRequest request) { + ThrowUtils.throwIf(spaceRankAnalyzeRequest == null, ErrorCode.PARAMS_ERROR); + User loginUser = userService.getLoginUser(request); + List resultList = spaceAnalyzeService.getSpaceRankAnalyze(spaceRankAnalyzeRequest, loginUser); + return ResultUtils.success(resultList); + } +} + diff --git a/src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceAnalyzeRequest.java b/src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceAnalyzeRequest.java new file mode 100644 index 0000000..0f7c271 --- /dev/null +++ b/src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceAnalyzeRequest.java @@ -0,0 +1,30 @@ +package edu.whut.smilepicturebackend.model.dto.space.analyze; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 通用空间分析请求 + * queryAll ->查全空间,仅管理员 ; querypublic:查公共图库,仅管理员; spaceId,仅queryAll和querypublic都false时启用,查私人空间。 + */ +@Data +public class SpaceAnalyzeRequest implements Serializable { + + /** + * 空间 ID + */ + private Long spaceId; + + /** + * 是否查询公共图库 + */ + private boolean queryPublic; + + /** + * 全空间分析 + */ + private boolean queryAll; + + private static final long serialVersionUID = 1L; +} diff --git a/src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceCategoryAnalyzeRequest.java b/src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceCategoryAnalyzeRequest.java new file mode 100644 index 0000000..5ab2abe --- /dev/null +++ b/src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceCategoryAnalyzeRequest.java @@ -0,0 +1,13 @@ +package edu.whut.smilepicturebackend.model.dto.space.analyze; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 空间图片分类分析请求 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public class SpaceCategoryAnalyzeRequest extends SpaceAnalyzeRequest { + +} \ No newline at end of file diff --git a/src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceRankAnalyzeRequest.java b/src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceRankAnalyzeRequest.java new file mode 100644 index 0000000..2c2c065 --- /dev/null +++ b/src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceRankAnalyzeRequest.java @@ -0,0 +1,19 @@ +package edu.whut.smilepicturebackend.model.dto.space.analyze; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 空间使用排行分析请求(仅管理员) + */ +@Data +public class SpaceRankAnalyzeRequest implements Serializable { + + /** + * 排名前 N 的空间 + */ + private Integer topN = 10; + + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceSizeAnalyzeRequest.java b/src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceSizeAnalyzeRequest.java new file mode 100644 index 0000000..8c8bac0 --- /dev/null +++ b/src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceSizeAnalyzeRequest.java @@ -0,0 +1,13 @@ +package edu.whut.smilepicturebackend.model.dto.space.analyze; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 空间图片大小分析请求 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public class SpaceSizeAnalyzeRequest extends SpaceAnalyzeRequest { + +} \ No newline at end of file diff --git a/src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceTagAnalyzeRequest.java b/src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceTagAnalyzeRequest.java new file mode 100644 index 0000000..fa49368 --- /dev/null +++ b/src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceTagAnalyzeRequest.java @@ -0,0 +1,13 @@ +package edu.whut.smilepicturebackend.model.dto.space.analyze; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 空间图片标签分析请求 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public class SpaceTagAnalyzeRequest extends SpaceAnalyzeRequest { + +} \ No newline at end of file diff --git a/src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceUsageAnalyzeRequest.java b/src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceUsageAnalyzeRequest.java new file mode 100644 index 0000000..ce8cdb9 --- /dev/null +++ b/src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceUsageAnalyzeRequest.java @@ -0,0 +1,13 @@ +package edu.whut.smilepicturebackend.model.dto.space.analyze; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 空间资源使用分析请求封装类 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public class SpaceUsageAnalyzeRequest extends SpaceAnalyzeRequest { + +} diff --git a/src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceUserAnalyzeRequest.java b/src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceUserAnalyzeRequest.java new file mode 100644 index 0000000..70605c2 --- /dev/null +++ b/src/main/java/edu/whut/smilepicturebackend/model/dto/space/analyze/SpaceUserAnalyzeRequest.java @@ -0,0 +1,22 @@ +package edu.whut.smilepicturebackend.model.dto.space.analyze; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 空间用户上传行为分析请求 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public class SpaceUserAnalyzeRequest extends SpaceAnalyzeRequest { + + /** + * 用户 ID + */ + private Long userId; + + /** + * 时间维度:day / week / month + */ + private String timeDimension; +} \ No newline at end of file diff --git a/src/main/java/edu/whut/smilepicturebackend/model/vo/space/analyze/SpaceCategoryAnalyzeResponse.java b/src/main/java/edu/whut/smilepicturebackend/model/vo/space/analyze/SpaceCategoryAnalyzeResponse.java new file mode 100644 index 0000000..35089d1 --- /dev/null +++ b/src/main/java/edu/whut/smilepicturebackend/model/vo/space/analyze/SpaceCategoryAnalyzeResponse.java @@ -0,0 +1,33 @@ +package edu.whut.smilepicturebackend.model.vo.space.analyze; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 空间图片分类分析响应 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SpaceCategoryAnalyzeResponse implements Serializable { + + /** + * 图片分类 + */ + private String category; + + /** + * 图片数量 + */ + private Long count; + + /** + * 分类图片总大小 + */ + private Long totalSize; + + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/edu/whut/smilepicturebackend/model/vo/space/analyze/SpaceSizeAnalyzeResponse.java b/src/main/java/edu/whut/smilepicturebackend/model/vo/space/analyze/SpaceSizeAnalyzeResponse.java new file mode 100644 index 0000000..2012936 --- /dev/null +++ b/src/main/java/edu/whut/smilepicturebackend/model/vo/space/analyze/SpaceSizeAnalyzeResponse.java @@ -0,0 +1,28 @@ +package edu.whut.smilepicturebackend.model.vo.space.analyze; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 空间图片大小分析响应 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SpaceSizeAnalyzeResponse implements Serializable { + + /** + * 图片大小范围 + */ + private String sizeRange; + + /** + * 图片数量 + */ + private Long count; + + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/edu/whut/smilepicturebackend/model/vo/space/analyze/SpaceTagAnalyzeResponse.java b/src/main/java/edu/whut/smilepicturebackend/model/vo/space/analyze/SpaceTagAnalyzeResponse.java new file mode 100644 index 0000000..b88f481 --- /dev/null +++ b/src/main/java/edu/whut/smilepicturebackend/model/vo/space/analyze/SpaceTagAnalyzeResponse.java @@ -0,0 +1,28 @@ +package edu.whut.smilepicturebackend.model.vo.space.analyze; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 空间图片标签分析响应 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SpaceTagAnalyzeResponse implements Serializable { + + /** + * 标签名称 + */ + private String tag; + + /** + * 使用次数 + */ + private Long count; + + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/edu/whut/smilepicturebackend/model/vo/space/analyze/SpaceUsageAnalyzeResponse.java b/src/main/java/edu/whut/smilepicturebackend/model/vo/space/analyze/SpaceUsageAnalyzeResponse.java new file mode 100644 index 0000000..bc086ad --- /dev/null +++ b/src/main/java/edu/whut/smilepicturebackend/model/vo/space/analyze/SpaceUsageAnalyzeResponse.java @@ -0,0 +1,44 @@ +package edu.whut.smilepicturebackend.model.vo.space.analyze; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 空间资源使用分析响应类 + */ +@Data +public class SpaceUsageAnalyzeResponse implements Serializable { + + /** + * 已使用大小 + */ + private Long usedSize; + + /** + * 总大小 + */ + private Long maxSize; + + /** + * 空间使用比例 + */ + private Double sizeUsageRatio; + + /** + * 当前图片数量 + */ + private Long usedCount; + + /** + * 最大图片数量 + */ + private Long maxCount; + + /** + * 图片数量占比 + */ + private Double countUsageRatio; + + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/edu/whut/smilepicturebackend/model/vo/space/analyze/SpaceUserAnalyzeResponse.java b/src/main/java/edu/whut/smilepicturebackend/model/vo/space/analyze/SpaceUserAnalyzeResponse.java new file mode 100644 index 0000000..41b5a50 --- /dev/null +++ b/src/main/java/edu/whut/smilepicturebackend/model/vo/space/analyze/SpaceUserAnalyzeResponse.java @@ -0,0 +1,28 @@ +package edu.whut.smilepicturebackend.model.vo.space.analyze; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 空间用户上传行为分析响应 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SpaceUserAnalyzeResponse implements Serializable { + + /** + * 时间区间 + */ + private String period; + + /** + * 上传数量 + */ + private Long count; + + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/edu/whut/smilepicturebackend/service/SpaceAnalyzeService.java b/src/main/java/edu/whut/smilepicturebackend/service/SpaceAnalyzeService.java new file mode 100644 index 0000000..7e5fa9f --- /dev/null +++ b/src/main/java/edu/whut/smilepicturebackend/service/SpaceAnalyzeService.java @@ -0,0 +1,70 @@ +package edu.whut.smilepicturebackend.service; +import com.baomidou.mybatisplus.extension.service.IService; +import edu.whut.smilepicturebackend.model.dto.space.analyze.*; +import edu.whut.smilepicturebackend.model.entity.Space; +import edu.whut.smilepicturebackend.model.entity.User; +import edu.whut.smilepicturebackend.model.vo.analyze.*; +import edu.whut.smilepicturebackend.model.vo.space.analyze.*; + +import java.util.List; + +/** + * @author 李鱼皮 + * @createDate 2024-12-18 19:53:34 + */ +public interface SpaceAnalyzeService extends IService { + + /** + * 获取空间使用情况分析 + * + * @param spaceUsageAnalyzeRequest + * @param loginUser + * @return + */ + SpaceUsageAnalyzeResponse getSpaceUsageAnalyze(SpaceUsageAnalyzeRequest spaceUsageAnalyzeRequest, User loginUser); + + /** + * 获取空间图片分类分析 + * + * @param spaceCategoryAnalyzeRequest + * @param loginUser + * @return + */ + List getSpaceCategoryAnalyze(SpaceCategoryAnalyzeRequest spaceCategoryAnalyzeRequest, User loginUser); + + /** + * 获取空间图片标签分析 + * + * @param spaceTagAnalyzeRequest + * @param loginUser + * @return + */ + List getSpaceTagAnalyze(SpaceTagAnalyzeRequest spaceTagAnalyzeRequest, User loginUser); + + /** + * 获取空间图片大小分析 + * + * @param spaceSizeAnalyzeRequest + * @param loginUser + * @return + */ + List getSpaceSizeAnalyze(SpaceSizeAnalyzeRequest spaceSizeAnalyzeRequest, User loginUser); + + /** + * 获取空间用户上传行为分析 + * + * @param spaceUserAnalyzeRequest + * @param loginUser + * @return + */ + List getSpaceUserAnalyze(SpaceUserAnalyzeRequest spaceUserAnalyzeRequest, User loginUser); + + /** + * 空间使用排行分析(仅管理员) + * + * @param spaceRankAnalyzeRequest + * @param loginUser + * @return + */ + List getSpaceRankAnalyze(SpaceRankAnalyzeRequest spaceRankAnalyzeRequest, User loginUser); +} diff --git a/src/main/java/edu/whut/smilepicturebackend/service/impl/SpaceAnalyzeServiceImpl.java b/src/main/java/edu/whut/smilepicturebackend/service/impl/SpaceAnalyzeServiceImpl.java new file mode 100644 index 0000000..b6c5cf7 --- /dev/null +++ b/src/main/java/edu/whut/smilepicturebackend/service/impl/SpaceAnalyzeServiceImpl.java @@ -0,0 +1,310 @@ +package edu.whut.smilepicturebackend.service.impl; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + +import edu.whut.smilepicturebackend.exception.BusinessException; +import edu.whut.smilepicturebackend.exception.ErrorCode; +import edu.whut.smilepicturebackend.exception.ThrowUtils; +import edu.whut.smilepicturebackend.mapper.SpaceMapper; +import edu.whut.smilepicturebackend.model.dto.space.analyze.*; +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.vo.analyze.*; +import edu.whut.smilepicturebackend.model.vo.space.analyze.*; +import edu.whut.smilepicturebackend.service.PictureService; +import edu.whut.smilepicturebackend.service.SpaceAnalyzeService; +import edu.whut.smilepicturebackend.service.SpaceService; +import edu.whut.smilepicturebackend.service.UserService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author 李鱼皮 + * @createDate 2024-12-18 19:53:34 + */ +@Service +@RequiredArgsConstructor +public class SpaceAnalyzeServiceImpl extends ServiceImpl + implements SpaceAnalyzeService { + + private final UserService userService; + + private final SpaceService spaceService; + + private final PictureService pictureService; + + @Override + public SpaceUsageAnalyzeResponse getSpaceUsageAnalyze(SpaceUsageAnalyzeRequest spaceUsageAnalyzeRequest, User loginUser) { + // 校验参数 + // 全空间或公共图库,需要从 Picture 表查询 + if (spaceUsageAnalyzeRequest.isQueryAll() || spaceUsageAnalyzeRequest.isQueryPublic()) { + // 权限校验,仅管理员可以访问 + checkSpaceAnalyzeAuth(spaceUsageAnalyzeRequest, loginUser); + // 统计图库的使用空间 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.select("pic_size"); + // 补充查询范围 + fillAnalyzeQueryWrapper(spaceUsageAnalyzeRequest, queryWrapper); + List pictureObjList = pictureService.getBaseMapper().selectObjs(queryWrapper); //不用pictureService.list(),它会查完整的一条记录,而我们仅需一列 + long usedSize = 0L; + for (Object obj : pictureObjList) { + // 记得把 Object 强转成 Long,再拆箱加到累加器里 + usedSize += (Long) obj; + } + long usedCount = pictureObjList.size(); + // 封装返回结果 + SpaceUsageAnalyzeResponse spaceUsageAnalyzeResponse = new SpaceUsageAnalyzeResponse(); + spaceUsageAnalyzeResponse.setUsedSize(usedSize); + spaceUsageAnalyzeResponse.setUsedCount(usedCount); + // 公共图库(或者全部空间)无数量和容量限制、也没有比例 + spaceUsageAnalyzeResponse.setMaxSize(null); + spaceUsageAnalyzeResponse.setSizeUsageRatio(null); + spaceUsageAnalyzeResponse.setMaxCount(null); + spaceUsageAnalyzeResponse.setCountUsageRatio(null); + return spaceUsageAnalyzeResponse; + } else { + // 特定空间可以直接从 Space 表查询! + Long spaceId = spaceUsageAnalyzeRequest.getSpaceId(); + ThrowUtils.throwIf(spaceId == null || spaceId <= 0, ErrorCode.PARAMS_ERROR); + // 获取空间信息 + Space space = spaceService.getById(spaceId); + ThrowUtils.throwIf(space == null, ErrorCode.NOT_FOUND_ERROR, "空间不存在"); + // 权限校验,仅管理员可以访问 + checkSpaceAnalyzeAuth(spaceUsageAnalyzeRequest, loginUser); + // 封装返回结果 + SpaceUsageAnalyzeResponse spaceUsageAnalyzeResponse = new SpaceUsageAnalyzeResponse(); + spaceUsageAnalyzeResponse.setUsedSize(space.getTotalSize()); + spaceUsageAnalyzeResponse.setUsedCount(space.getTotalCount()); + spaceUsageAnalyzeResponse.setMaxSize(space.getMaxSize()); + spaceUsageAnalyzeResponse.setMaxCount(space.getMaxCount()); + // 计算比例 + double sizeUsageRatio = NumberUtil.round(space.getTotalSize() * 100.0 / space.getMaxSize(), 2).doubleValue(); + double countUsageRatio = NumberUtil.round(space.getTotalCount() * 100.0 / space.getMaxCount(), 2).doubleValue(); + spaceUsageAnalyzeResponse.setSizeUsageRatio(sizeUsageRatio); + spaceUsageAnalyzeResponse.setCountUsageRatio(countUsageRatio); + return spaceUsageAnalyzeResponse; + } + } + + @Override + public List getSpaceCategoryAnalyze(SpaceCategoryAnalyzeRequest spaceCategoryAnalyzeRequest, User loginUser) { + ThrowUtils.throwIf(spaceCategoryAnalyzeRequest == null, ErrorCode.PARAMS_ERROR); + // 检查权限 + checkSpaceAnalyzeAuth(spaceCategoryAnalyzeRequest, loginUser); + + // 构造查询条件 + QueryWrapper queryWrapper = new QueryWrapper<>(); + fillAnalyzeQueryWrapper(spaceCategoryAnalyzeRequest, queryWrapper); + + // 使用 MyBatis Plus 分组查询 + queryWrapper.select("category", "count(*) as count", "sum(pic_size) as total_size") + .groupBy("category"); + + // 查询并转换结果 + return pictureService.getBaseMapper().selectMaps(queryWrapper) + .stream() + .map(result -> { + String category = (String) result.get("category"); + Long count = ((Number) result.get("count")).longValue(); + Long totalSize = ((Number) result.get("total_size")).longValue(); + return new SpaceCategoryAnalyzeResponse(category, count, totalSize); + }) + .collect(Collectors.toList()); + } + + @Override + public List getSpaceTagAnalyze(SpaceTagAnalyzeRequest spaceTagAnalyzeRequest, User loginUser) { + ThrowUtils.throwIf(spaceTagAnalyzeRequest == null, ErrorCode.PARAMS_ERROR); + // 检查权限 + checkSpaceAnalyzeAuth(spaceTagAnalyzeRequest, loginUser); + + // 构造查询条件 + QueryWrapper queryWrapper = new QueryWrapper<>(); + fillAnalyzeQueryWrapper(spaceTagAnalyzeRequest, queryWrapper); + // 查询所有符合条件的标签 + queryWrapper.select("tags"); + List tagsJsonList = pictureService.getBaseMapper().selectObjs(queryWrapper) + .stream() + .filter(ObjUtil::isNotNull) + .map(Object::toString) + .collect(Collectors.toList()); + + // 解析标签并统计 + Map tagCountMap = tagsJsonList.stream() + // ["Java", "Python"], ["Java", "PHP"] => "Java", "Python", "Java", "PHP" + .flatMap(tagsJson -> JSONUtil.toList(tagsJson, String.class).stream()) + .collect(Collectors.groupingBy(tag -> tag, Collectors.counting())); + + // 转换为响应对象,按照使用次数进行排序 + return tagCountMap.entrySet().stream() + .sorted((e1, e2) -> Long.compare(e2.getValue(), e1.getValue())) // 降序排序 + .map(entry -> new SpaceTagAnalyzeResponse(entry.getKey(), entry.getValue())) + .collect(Collectors.toList()); + } + + @Override + public List getSpaceSizeAnalyze(SpaceSizeAnalyzeRequest spaceSizeAnalyzeRequest, User loginUser) { + ThrowUtils.throwIf(spaceSizeAnalyzeRequest == null, ErrorCode.PARAMS_ERROR); + // 检查权限 + checkSpaceAnalyzeAuth(spaceSizeAnalyzeRequest, loginUser); + + // 构造查询条件 + QueryWrapper queryWrapper = new QueryWrapper<>(); + fillAnalyzeQueryWrapper(spaceSizeAnalyzeRequest, queryWrapper); + + // 查询所有符合条件的图片大小 + queryWrapper.select("pic_size"); + // 100、120、1000 + List picSizeList = pictureService.getBaseMapper().selectObjs(queryWrapper) + .stream() + .filter(ObjUtil::isNotNull) + .map(size -> (Long) size) + .collect(Collectors.toList()); + + // 定义分段范围,注意使用有序的 Map + Map sizeRanges = new LinkedHashMap<>(); + sizeRanges.put("<100KB", picSizeList.stream().filter(size -> size < 100 * 1024).count()); + sizeRanges.put("100KB-500KB", picSizeList.stream().filter(size -> size >= 100 * 1024 && size < 500 * 1024).count()); + sizeRanges.put("500KB-1MB", picSizeList.stream().filter(size -> size >= 500 * 1024 && size < 1 * 1024 * 1024).count()); + sizeRanges.put(">1MB", picSizeList.stream().filter(size -> size >= 1 * 1024 * 1024).count()); + + // 转换为响应对象 + return sizeRanges.entrySet().stream() + .map(entry -> new SpaceSizeAnalyzeResponse(entry.getKey(), entry.getValue())) + .collect(Collectors.toList()); + } + + /** + * 根据前端传来的 时间维度(按天 / 周 / 月)来动态拼出不同的 SQL 聚合字段,然后再按照这个“时间段”字段去分组和排序,最终拿到每个周期(period)里上传的图片数量 + * @param spaceUserAnalyzeRequest + * @param loginUser + * @return + */ + @Override + public List getSpaceUserAnalyze(SpaceUserAnalyzeRequest spaceUserAnalyzeRequest, User loginUser) { + ThrowUtils.throwIf(spaceUserAnalyzeRequest == null, ErrorCode.PARAMS_ERROR); + // 检查权限 + checkSpaceAnalyzeAuth(spaceUserAnalyzeRequest, loginUser); + + // 构造查询条件 + QueryWrapper queryWrapper = new QueryWrapper<>(); + fillAnalyzeQueryWrapper(spaceUserAnalyzeRequest, queryWrapper); + // 补充用户 id 查询 + Long userId = spaceUserAnalyzeRequest.getUserId(); + queryWrapper.eq(ObjUtil.isNotNull(userId), "user_id", userId); + // 补充分析维度:每日、每周、每月 + /** + * eg: + * SELECT + * DATE_FORMAT(create_time, '%Y-%m') AS period, + * COUNT(*) AS count + * FROM picture + * … (其他 WHERE 条件) + * GROUP BY period + * ORDER BY period ASC; + */ + String timeDimension = spaceUserAnalyzeRequest.getTimeDimension(); + switch (timeDimension) { + case "day": + queryWrapper.select("DATE_FORMAT(create_time, '%Y-%m-%d') as period", "count(*) as count"); + break; + case "week": + queryWrapper.select("YEARWEEK(create_time) as period", "count(*) as count"); + break; + case "month": + queryWrapper.select("DATE_FORMAT(create_time, '%Y-%m') as period", "count(*) as count"); + break; + default: + throw new BusinessException(ErrorCode.PARAMS_ERROR, "不支持的时间维度"); + } + + // 分组排序 + queryWrapper.groupBy("period").orderByAsc("period"); + + // 查询并封装结果 + List> queryResult = pictureService.getBaseMapper().selectMaps(queryWrapper); + return queryResult + .stream() + .map(result -> { + String period = result.get("period").toString(); + Long count = ((Number) result.get("count")).longValue(); + return new SpaceUserAnalyzeResponse(period, count); + }) + .collect(Collectors.toList()); + } + + @Override + public List getSpaceRankAnalyze(SpaceRankAnalyzeRequest spaceRankAnalyzeRequest, User loginUser) { + ThrowUtils.throwIf(spaceRankAnalyzeRequest == null, ErrorCode.PARAMS_ERROR); + + // 检查权限,仅管理员可以查看 + ThrowUtils.throwIf(!userService.isAdmin(loginUser), ErrorCode.NO_AUTH_ERROR); + + // 构造查询条件 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.select("id", "space_name", "user_id", "total_size") + .orderByDesc("total_size") + .last("limit " + spaceRankAnalyzeRequest.getTopN()); // 取前 N 名 + + // 查询并封装结果 + return spaceService.list(queryWrapper); + } + + /** + * 公共方法:校验空间分析权限 + * + * @param spaceAnalyzeRequest + * @param loginUser + */ + private void checkSpaceAnalyzeAuth(SpaceAnalyzeRequest spaceAnalyzeRequest, User loginUser) { + boolean queryPublic = spaceAnalyzeRequest.isQueryPublic(); + boolean queryAll = spaceAnalyzeRequest.isQueryAll(); + // 全空间分析或者公共图库权限校验:仅管理员可访问 + if (queryAll || queryPublic) { + ThrowUtils.throwIf(!userService.isAdmin(loginUser), ErrorCode.NO_AUTH_ERROR); + } else { + // 分析特定空间,仅本人或管理员可以访问 + Long spaceId = spaceAnalyzeRequest.getSpaceId(); + ThrowUtils.throwIf(spaceId == null, ErrorCode.PARAMS_ERROR); + Space space = spaceService.getById(spaceId); + ThrowUtils.throwIf(space == null, ErrorCode.NOT_FOUND_ERROR, "空间不存在"); + spaceService.checkSpaceAuth(loginUser, space); + } + } + + /** + * 公共方法:根据请求对象封装查询条件 + * + * @param spaceAnalyzeRequest + * @param queryWrapper + */ + private void fillAnalyzeQueryWrapper(SpaceAnalyzeRequest spaceAnalyzeRequest, QueryWrapper queryWrapper) { + // 全空间分析 + boolean queryAll = spaceAnalyzeRequest.isQueryAll(); + if (queryAll) { + return; + } + // 公共图库 + boolean queryPublic = spaceAnalyzeRequest.isQueryPublic(); + if (queryPublic) { + queryWrapper.isNull("space_id"); + return; + } + // 分析特定空间 + Long spaceId = spaceAnalyzeRequest.getSpaceId(); + if (spaceId != null) { + queryWrapper.eq("space_id", spaceId); + return; + } + throw new BusinessException(ErrorCode.PARAMS_ERROR, "未指定查询范围"); + } +}