智能图库模块-图库分析(后端)

今天进行了图库分析模块后端部分的学习开发,对于图库分析,从字面上来看就是要分析当前的图库或者某个空间的资源使用情况,比如目前图库空间用了多少的资源、有哪些分类、哪些标签,图片的大小等,对于管理员来说,对所有方面进行分析是有利于管理员来把控系统的资源分配的。

用户对自己的空间进行分析和管理员来所有的空间进行分析本质上是一样的,区别在于图片的选择范围,转化为sql语句的话在where前面的select都是一样的,所以可以通过引入一些字段来进行区分:

java
复制代码
private void fillAnalyzeQueryWrapper(SpaceAnalyzeRequest spaceAnalyzeRequest, QueryWrapper<Picture> queryWrapper){ boolean queryAll = spaceAnalyzeRequest.isQueryAll(); if (queryAll)return; boolean queryPublic = spaceAnalyzeRequest.isQueryPublic(); if (queryPublic){ queryWrapper.isNull("spaceId"); return; } Long spaceId = spaceAnalyzeRequest.getSpaceId(); if (spaceId != null){ queryWrapper.eq("spaceId",spaceId); return; } throw new BusinessException(ErrorCode.PARAMS_ERROR,"未指定查询范围"); }

spaceAnalyzeRequest是一个基础的空间分析类,后面的多种分析请求类都会继承这个类,这个类的三个成员变量queryAllqueryPublicspaceId用于进行区分选择的范围,queryAll的优先级最高,sapceId仅在前面两个字段都为false的时候才生效,在后面写方法需要拼接查询条件时直接调用该方法即可。

分析空间也是需要权限的,而且后面的请求都需要校验,因此可以抽出来写成一个方法:

java
复制代码
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); } }

对于上面的分析请求,流程都是校验参数、校验权限(checkSpaceAnalyzeAuth)、补充查询条件(fillAnalyzeQueryWrapper,有的请求需要额外补充别的条件)、进行查询和封装返回结果,以分析空间使用数据为例:

java
复制代码
public SpaceUsageAnalyzeResponse getSpaceUsageAnalyze(SpaceUsageAnalyzeRequest spaceUsageAnalyzeRequest, User loginUser) { //校验参数 ThrowUtils.throwIf(spaceUsageAnalyzeRequest == null,ErrorCode.PARAMS_ERROR); //全空间或者公共图库 需要从Picture表中查询 if (spaceUsageAnalyzeRequest.isQueryAll() || spaceUsageAnalyzeRequest.isQueryPublic()){ //权限校验 ,仅管理员可以访问 checkSpaceAnalyzeAuth(spaceUsageAnalyzeRequest,loginUser); //统计图库的使用空间 QueryWrapper<Picture> queryWrapper = new QueryWrapper<>(); queryWrapper.select("picSize"); //补充查询范围 fillAnalyzeQueryWrapper(spaceUsageAnalyzeRequest,queryWrapper); //调用list()返回的是List<Picture>,存的是一个对象,当数据量大的时候会占用大量空间,需要转换为List<Long> //getBaseMapper().selectObjs 返回的其实是Long List<Object> pictureObjList = pictureService.getBaseMapper().selectObjs(queryWrapper); long usedSize = pictureObjList.stream().mapToLong(obj -> (Long) obj).sum(); 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; } }

上面的代码中,由于只需要获取图片的大小来计算使用情况,所以只select了图片大小这一列,并且在调用mapper方式时不用.list(queryWrapper)而是调用getBaseMapper().selectObjs(queryWrapper),这样查出来的列表就不会封装成Picture对象,在这里它其实就是Long,如果数据量很大并且封装的是Picture对象的话,就会占用大量存储和影响性能,通过上面的写法便能提高性能且节约了存储空间,是一个优化的细节,再通过pictureObjList.stream().mapToLong(obj -> (Long) obj).sum()来将Object转为Long之后进行计算得到图片大小之和,这是Java8的stream流特性,可以帮助我们提高开发的效率,这行代码不用stream的话便是要写一个for循环来求和,有了stream直接一行代码就可以搞定了,所以掌握stream的使用是有必要的,后面的请求在处理封装结果时也会用到stream流。

另外几个请求的的实现流程也是差不多的,只是不同请求在sql语句的拼接、mapper的调用和封装的细节上有区别,在不清楚怎么写wrapper的时候,可以在MySQL控制台中写sql语句来验证想法是否正确,然后自己或者通过ai写出相应的wrapper。mapper的调用上在获取空间图片分类分析中,select了不止一列: queryWrapper.select("category","count(*) as count","sum(picSize) as size").groupBy("category"); 在查询时就调用了getBaseMapper().selectMaps(queryWrapper)来得到一个Map对象而不是Object队列,然后对这个Map使用stream进行处理:

java
复制代码
List<SpaceCategoryAnalyzeResponse> list = pictureService.getBaseMapper().selectMaps(queryWrapper).stream() .map(result -> { String category = (String) result.get("category"); Long count = (Long) result.get("count"); Long size = ((BigDecimal) result.get("size")).longValue(); return new SpaceCategoryAnalyzeResponse(category, count, size); }) .collect(Collectors.toList());

在空间图片标签分析中,我们存在数据库的tags是以Json形式的字符串存进去的,为了得到每个标签的使用情况,我们在查出标签队列后,需要对这个队列进行扁平化处理,即把原本的["a","b"],["a","c"] 转化为 ["a","b","a","c"],而stream流中也确实有对应的方法来实现这个扁平化:

java
复制代码
//解析标签并统计 //扁平化 把多个数组的值合并成一个数组 Map<String, Long> tagCountMap = tagJsonList.stream() //["a","b"],["a","c"] => ["a","b","a","c"] .flatMap(tagJson -> JSONUtil.toList(tagJson, String.class).stream()) .collect(Collectors.groupingBy(tag -> tag, Collectors.counting()));

之后将这个map转为响应对象并按次数进行降序排序返回:

java
复制代码
return tagCountMap.entrySet().stream() .sorted((e1,e2) -> Long.compare(e2.getValue(),e1.getValue()))//降序 .map(entry -> new SpaceTagAnalyzeResponse(entry.getKey(),entry.getValue())) .collect(Collectors.toList());

后面的空间图片大小分析、用户上传行为分析和空间使用排行分析也跟前面这些请求一样的思路,这里就不再多作说明了。

0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
shark
作者分享
做了一个月的小程序,终于准备到即将交付的时候了,这段时间里可真是被微信小程序的各种限制折磨的不要不要的,突出一个为难开发者,有几次改代码改到了两点多才改完!小程序的后端技术栈和鱼皮的智能Bi项目采用了差不多的技术栈,通过注解+aop来对接口进行令牌桶限流,用rabbitmq来缓解调用Ai服务的压力,异步生成报告,对于订单采用了自定义线程池结合async+scheduled注解实现定时任务的异步执行,每分钟遍历删除库中的订单,还有些功能等真正交付后再做下总结。唉,不知道这个项目再加上智能图库项目能不能让我这种0相关工作经验的双非往届生有面试机会和找到工作呢,不想再次尝试付出与回报不成正比的滋味了😭😭
8
今天收到了甲方的第一笔付款💰,没想到毕业一年半居然能用自己专业技能接个活赚了点外快,还是有点小高兴的。 这两天的学习状态不太行,我好像总会在经历了持续一段时间的学习后会有两三天学不进的状态🤧,不知道大伙会不会有这种状态,虽然我很想克服这种状态,但心里就好像有东西堵在那影响着学不进去(不知道是不是焦虑),希望明天能恢复状态吧。 转眼到了3月份,接的活的时间也剩下半个月了,下半部分就要开发调用Ai接口的代码和微信支付(感觉好像挺麻烦?)的代码了,同时也需要开始准备看面试题了,想问下各位是怎么使用面试鸭来高效刷题的呢,这两天(可能跟我的状态有关)看面试题总是有种似记住但又没全记住的感觉,上次背八股还是22年秋招的时候,对于一些知识可能还有记得一些,但由于我是0开发经验社招,不知道重点要看哪些好,希望各位大佬能给小弟一点建议🙏
4
这几天没有打卡,不是摆烂了,而是前几天有个朋友推了个活给我问我说做不做,是做一个微信小程序,并且需要调用Ai的接口,他因为工作太忙没有空接这个活,而我本来正在学习Bi项目,打算学完之后写简历刷面试题投简历,所以一开始还在犹豫,觉得自己23届毕业,虽说大学自学了几年Java,但是0相关工作经验,有点没底气,不过后来我拉到了另一个同学来帮忙,那个同学在大厂实习过,有过企业开发的经验,他也乐意加入一起做项目,于是最后便接下了这个活(主要是有钱可以拿)。由于自己是前端苦手,所以也是一边问Ai一边改页面的布局,改到今天觉得差不多了,甲方也觉得ok,后面就准备和同学一起写后端的接口了,在这几天也对着项目需求跟同学讨论了许多功能和结构上的设计,也算是一种积累经验的过程了,希望这一个月的工期内能把项目做好上线,最好是能写到简历上吧,孩子真的很想要一份工作!!这一个月干的活希望不要是白忙活,不然我真的要道心破碎了😭😭😭
7
智能Bi项目-限流优化
7
智能BI项目-调用硅基流动的DeepSeek
19
下载 APP