当前位置: 首页 > news >正文

(黑马点评) 五、探店达人系列功能实现

5.1 发布和查看探店笔记

5.1.1 发布探店笔记

        这块代码黑马已经完成了,在发布探店笔记界面,有两块内容是需要上传的。一是笔记内容,二是笔记配图。其中笔记配图部分黑马使用的是上传到本地前端服务器上面的。我我觉得可以将图片文件发布在阿里云的OSS存储也行,该功能等后续会完成。等黑马点评后端部分完成后、再去分析黑马写的前端代码。然后再将该功能实现。

文件上传功能实现

在系统常量中修改文件上传的地址(改成你自己的)

// 图片上传路径public static final String IMAGE_UPLOAD_DIR = "D:\\software\\hm-dianping-nginx\\nginx-1.18.0\\html\\hmdp\\imgs";
@Slf4j
@RestController
@RequestMapping("upload")
public class UploadController {@PostMapping("blog")public Result uploadImage(@RequestParam("file") MultipartFile image) {try {// 获取原始文件名称String originalFilename = image.getOriginalFilename();// 生成新文件名String fileName = createNewFileName(originalFilename);// 保存文件image.transferTo(new File(SystemConstants.IMAGE_UPLOAD_DIR, fileName));// 返回结果log.debug("文件上传成功,{}", fileName);return Result.ok(fileName);} catch (IOException e) {throw new RuntimeException("文件上传失败", e);}}private String createNewFileName(String originalFilename) {// 获取后缀String suffix = StrUtil.subAfter(originalFilename, ".", true);// 生成目录String name = UUID.randomUUID().toString();int hash = name.hashCode();int d1 = hash & 0xF;int d2 = (hash >> 4) & 0xF;// 判断目录是否存在File dir = new File(SystemConstants.IMAGE_UPLOAD_DIR, StrUtil.format("/blogs/{}/{}", d1, d2));if (!dir.exists()) {dir.mkdirs();}// 生成文件名return StrUtil.format("/blogs/{}/{}/{}.{}", d1, d2, name, suffix);}
笔记发布功能实现 
    @PostMappingpublic Result saveBlog(@RequestBody Blog blog) {// 获取登录用户UserDTO user = UserHolder.getUser();blog.setUserId(user.getId());// 保存探店博文blogService.save(blog);// 返回idreturn Result.ok(blog.getId());}
发布功能测试

5.1.2 查看探店笔记

        添加TableField注解表示这三个字段不是数据库中的字段。我们查看探店笔记时,需要将创作者的信息也显示出来,但是又不能显示过多暴露,只需要有头像、姓名之类的就好了。

/*** 用户图标*/@TableField(exist = false)private String icon;/*** 用户姓名*/@TableField(exist = false)private String name;
查看探店笔记代码实现
/*** 根据id查询探店笔记* @param id* @return*/@GetMapping("/{id}")public Result queryBlogById(@PathVariable("id") Long id) {return blogService.queryBlogById(id);}/*** 根据id查博客笔记* @param id* @return*/@Overridepublic Result queryBlogById(Long id) {//1. 查询blogBlog blog = getById(id);if(blog==null){return Result.fail("博客不存在");}//2.查用户queryBlogUser(blog);return Result.ok(blog);}/*** 复用方法封装* @param blog*/private void queryBlogUser(Blog blog){Long userId = blog.getUserId();User user = userService.getById(userId);blog.setName(user.getNickName());blog.setIcon(user.getIcon());}
查看功能测试

5.2 点赞功能实现

5.2.1 当前点赞功能存在的问题

不校验点赞用户信息,一个人可以无限刷赞

    /*** 修改点赞数量* @param id* @return*/@PutMapping("/like/{id}")public Result likeBlog(@PathVariable("id") Long id) {// 修改点赞数量// update tb_blog set liked liked + 1 where id = #{id}blogService.update().setSql("liked = liked + 1").eq("id", id).update();return Result.ok();}

5.2.2 完善点赞功能需求与实现步骤

需求:

1. 同一用户只能点赞一次,再次点击即取消点赞

2. 如果当前用户已经点赞,则点赞按钮高亮显示(isLike 告知前端即可) 

步骤:

1. 给Blog类添加一个isList字段,标记当前用户是否点赞

2. 修改点赞功能,利用Redis的set集合特性存储当前笔记的点赞用户id,用于判断该用户是否给笔记点过赞,为点过则赞 +1 ,否则赞-1

3. 在根据id查询Blog业务时,就判断当前登录用户有无点赞记录,赋值给isList字段.

4. 在分页查询Blog业务时,也去判断并赋值

5.2.3 点赞功能实现

添加一个isList字段

    /*** 是否点赞过了*/@TableField(exist = false)private Boolean isLike;

修改点赞功能

/*** 点赞* @param id* @return*/@Overridepublic Result likeBlog(Long id) {//1.判断当前用户有没点赞Long userId = UserHolder.getUser().getId();String key = RedisConstants.BLOG_LIKED_KEY + id;Boolean isMember =  stringRedisTemplate.opsForSet().isMember(key,userId.toString());if(BooleanUtil.isFalse(isMember)){//2.更新数据库信息 点赞+1boolean isSuccess =  update().setSql("liked = liked + 1").eq("id",id).update();//3. 保存用户到Redis的set集合if(isSuccess){stringRedisTemplate.opsForSet().add(key,userId.toString());}}else{//4. 点赞-1boolean isSuccess =  update().setSql("liked = liked - 1").eq("id",id).update();//5。 移除Redisif(isSuccess){stringRedisTemplate.opsForSet().remove(key,userId.toString());}}return Result.ok();}

添加判断功能

    /*** 查询点赞状态* @param blog*/private void isBlogLiked(Blog blog) {Long userId = UserHolder.getUser().getId();String key = RedisConstants.BLOG_LIKED_KEY + blog.getId();Boolean isMember =  stringRedisTemplate.opsForSet().isMember(key,userId.toString());blog.setIsLike(BooleanUtil.isTrue(isMember));}/*** 查多个* @param current* @return*/@Overridepublic Result queryHotBlog(Integer current) {//    根据用户查询Page<Blog> page = query().orderByDesc("liked").page(new Page<>(current, SystemConstants.MAX_PAGE_SIZE));// 获取当前页数据List<Blog> records = page.getRecords();// 查询用户records.forEach(blog -> {this.queryBlogUser(blog);// 查询点赞状态this.isBlogLiked(blog);});return Result.ok(records);}/*** 根据id查博客笔记* @param id* @return*/@Overridepublic Result queryBlogById(Long id) {//1. 查询blogBlog blog = getById(id);if(blog==null){return Result.fail("博客不存在");}//2.查用户queryBlogUser(blog);//3. 查询点赞状态isBlogLiked(blog);return Result.ok(blog);}

5.2.4 点赞功能测试

5.3 点赞排行榜功能实现

5.3.1 数据结构选型说明

需求是能排序且去重的排行榜功能,因此我们选用zset集合来实现这些需求

5.3.2 排行功能实现

修改点赞及点赞状态逻辑

/*** 查询点赞状态* @param blog*/private void isBlogLiked(Blog blog) {Long userId = UserHolder.getUser().getId();String key = RedisConstants.BLOG_LIKED_KEY + blog.getId();Double score =  stringRedisTemplate.opsForZSet().score(key,userId.toString());blog.setIsLike(score != null);}/*** 点赞* @param id* @return*/@Overridepublic Result likeBlog(Long id) {//1.判断当前用户有没点赞Long userId = UserHolder.getUser().getId();String key = RedisConstants.BLOG_LIKED_KEY + id;Double score =  stringRedisTemplate.opsForZSet().score(key,userId.toString());if(score == null){//2.更新数据库信息 点赞+1boolean isSuccess =  update().setSql("liked = liked + 1").eq("id",id).update();//3. 保存用户到Redis的set集合if(isSuccess){stringRedisTemplate.opsForZSet().add(key,userId.toString(),System.currentTimeMillis());}}else{//4. 点赞-1boolean isSuccess =  update().setSql("liked = liked - 1").eq("id",id).update();//5。 移除Redisif(isSuccess){stringRedisTemplate.opsForZSet().remove(key,userId.toString());}}return Result.ok();}

实现排行榜功能

ZRANGE 查询范围内的元素

/*** 查询点赞排行榜* @param id* @return*/@Overridepublic Result queryBlogLikes(Long id) {// 使用zrange查询TOP5Set<String> top5StrIds = stringRedisTemplate.opsForZSet().range(RedisConstants.BLOG_LIKED_KEY + id, 0, 4);if(top5StrIds==null || top5StrIds.isEmpty()){return Result.ok(Collections.emptyList());}List<Long> ids = top5StrIds.stream().map(Long::valueOf).collect(Collectors.toList());// 根据用户id查询数据库List<User> users = userService.listByIds(ids);// DTOList<UserDTO> userDTOList = users.stream().map(user -> BeanUtil.copyProperties(user, UserDTO.class)).collect(Collectors.toList());return Result.ok(userDTOList);}

5.3.3 排行功能测试

优化

发现查询点赞排行榜的顺序不正确——原因:数据库in使用时,无法保障顺序

添加ORDER BY FIELD 自定义顺序
 

 // 根据用户id查询数据库// 自定义sql查询List<User> users = userService.query().in("id", ids).last("ORDER BY FIELD(id," + StrUtil.join(",", ids) + ")").list();

  

5.4 查看用户笔记列表功能实现

在点击用户头像进入首页时,可以查询该用户的博客列表进行展示

博客列表
查看博客列表

/*** 根据用户id查询探店笔记列表* @param current* @param id* @return*/@GetMapping("/of/user")public Result queryBlogByUserid(@RequestParam(value = "current",defaultValue = "1") Integer current,@RequestParam("id") Long id) {// 根据用户查询Page<Blog> page = blogService.query().eq("user_id", id).page(new Page<>(current,SystemConstants.MAX_PAGE_SIZE));// 获取当前页数据List<Blog> records = page.getRecords();return Result.ok(records);}

相关文章:

(黑马点评) 五、探店达人系列功能实现

5.1 发布和查看探店笔记 5.1.1 发布探店笔记 这块代码黑马已经完成了&#xff0c;在发布探店笔记界面&#xff0c;有两块内容是需要上传的。一是笔记内容&#xff0c;二是笔记配图。其中笔记配图部分黑马使用的是上传到本地前端服务器上面的。我我觉得可以将图片文件发布在阿里…...

SQLiteDatabase insert or replace数据不生效

在Android开发中&#xff0c;如果您在SQLite数据库中更新了数据&#xff0c;但重启应用后更新的数据不再生效&#xff0c;那么可能的原因有&#xff1a; 更新操作没有正确执行&#xff0c;可能是由于SQL语句错误或者数据库没有正确打开。 更新操作在事务中没有被正确提交。 更…...

基于Python实现一个浪漫烟花秀

为了实现一个类似烟花秀的效果&#xff0c;我们可以通过复杂的粒子系统来模拟烟花的升起、绽放和下落效果。以下是一个示例&#xff0c;旨在创建更为动态和逼真的烟花秀效果。 示例代码 这个代码示例将使用 matplotlib 和 numpy&#xff0c;并实现更丰富的视觉效果&#xff1…...

电气自动化入门03:安全用电

视频链接&#xff1a;2.1 电工知识&#xff1a;触电原因与防触电措施_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1PJ41117PW/?p4&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 1.电流对人体的危害 电击&#xff1a;电流通过人体。 电伤&#xff1a;电流热效应…...

【深度学习】(2)--PyTorch框架认识

文章目录 PyTorch框架认识1. Tensor张量定义与特性创建方式 2. 下载数据集下载测试展现下载内容 3. 创建DataLoader&#xff08;数据加载器&#xff09;4. 选择处理器5. 神经网络模型构建模型 6. 训练数据训练集数据测试集数据 7. 提高模型学习率 总结 PyTorch框架认识 PyTorc…...

前端面试记录

js 1. 函数式编程 将计算过程视为一系列的函数调用,函数的输出完全由输入决定&#xff0c;不依赖于或改变程序的状态,使得函数式编程的代码更加可预测和易于理解。 函数式编程的三个核心概念&#xff1a;纯函数、高阶函数和柯里化。 高阶函数&#xff1a;函数可以作为参数传…...

裁员了,很严重,大家做好准备吧!

最近刷到这样一个故事&#xff1a; 一个网友在大厂当牛马接近10年&#xff0c;部门优秀员工&#xff0c;业绩一直很稳&#xff0c;没想到&#xff0c;今年公司引进AI降本增效&#xff0c;开始大幅裁员&#xff0c;有些部门一夜之间被连锅端&#xff01; 上个月果然轮到他了&a…...

uniapp组件uni-datetime-picker选择年月后在ios上日期不显示

uniapp组件uni-datetime-picker选择年月后在ios上日期不显示 操作步骤&#xff1a; ios 选择年月 预期结果&#xff1a; 日期变为选择年月的日期 实际结果&#xff1a; 日期不显示 bug描述&#xff1a; uni-datetime-picker 2.2.22 ios点击年月选择后日期不显示 解决方案 …...

01_快速入门

读取数据 import pandas as pd# df pd.read_excel(https://xxxx/xxx//xx.xslx) # 读取网络数据 # df pd.read_excel(rd:\data\xx.xslx) # 读取本地文件 # 如果是csv文件&#xff0c;用read_csv()函数 df pd.read_csv(seaborn/iris.csv)查看数据 df.head() # 前5条记录 d…...

数据结构之分文件编译学生管理

list.h #ifndef LIST_H_ #define LIST_H_ #define MAX 30 typedef struct {int id;//学号char name[20];//姓名char major[20];//专业int age;//年龄 }student,*Pstudent;typedef struct {student data[MAX];//储存学生信息的数组int len;//统计学生个数 }list,*Plist;Plist c…...

TypeScript入门 (二)控制语句

引言 大家好&#xff0c;我是GISer Liu&#x1f601;&#xff0c;一名热爱AI技术的GIS开发者。本系列文章是我跟随DataWhale 2024年9月学习赛的TypeScript学习总结文档。本文主要讲解TypeScript中控制语句的部分&#xff1b;希望通过我的知识点总结&#xff0c;能够帮助你更好地…...

MVP 最简可行产品

MVP&#xff08;最小可行产品&#xff09;是一种产品开发策略&#xff0c;其主要目的是用最少的时间和资源&#xff0c;开发一个包含最基本必要功能的产品。这样做的目的是能够以最小的成本进入市场&#xff0c;获取用户反馈&#xff0c;再根据反馈逐步优化产品。 MVP是什么 …...

数仓工具:datax

datax可以理解为sqoop的优化版&#xff0c; 速度比sqoop快 因为sqoop底层是map任务&#xff0c;而datax底层是基于内存 DataX 是一个异构数据源离线同步工具&#xff0c;致力于实现包括关系型数据库(MySQL、Oracle等)、HDFS、Hive、ODPS、HBase、FTP等各种异构数据源之间稳定…...

CSS传统布局方法(补充)——WEB开发系列37

开发技术不断演进&#xff0c;布局方式也经历了多个阶段的变革。从最初的基于表格布局到 CSS 的浮动布局&#xff0c;再到今天的弹性盒&#xff08;Flexbox&#xff09;与 CSS Grid 网格布局&#xff0c;每一种布局方式都有其独特的背景和解决特定问题的优势。 一、CSS Grid 出…...

【系统架构设计师】软件架构的风格(经典习题)

更多内容请见: 备考系统架构设计师-核心总结索引 文章目录 【第1题】【第2题】【第3~4题】【第5题】【第6题】【第7题】【第8题】【第9题】【第10题】【第11题】【第12题】【第13题】【第14题】【第15~16题】【第17题】【第18~19题】【第20~21题】【第22题】【第23题】【第24~…...

网页打开时,下载的文件fetcht类型?有什么作用?

‌fetch API‌是一种用于向服务器发送请求并获取响应的现代Web API。它支持获取各种类型的数据&#xff0c;包括文本、JSON、图像和文件等。fetch API的主要优势之一是支持流式传输和取消请求&#xff0c;这使得处理大型数据集和长时间运行的操作变得更加简单和可靠。此外&…...

作为HR,如何考察候选人的专业知识与技能

这是严肃的话题&#xff0c;如何考察候选人的专业知识和技能。HR招聘是一个让我们既爱又恨的过程。爱的是&#xff0c;我们有机会遇到各种各样的人才&#xff1b;恨的是&#xff0c;要从茫茫人海中找到那个“对的人”简直比找一根针在干草堆里还难。 本系列的文章&#xff0c;…...

阻止冒泡事件

每一div都有一个切换事件 div里包括【复制】事件&#xff0c; 点击【复制按钮】&#xff0c;会触发【切换事件】 因为冒泡 在 Vue 3 中&#xff0c;阻止 click 事件冒泡可以使用以下常规方法&#xff1a; 1 事件修饰符&#xff1a;Vue 3 中提供了多种事件修饰符&#xff0c…...

聊聊Netty对于内存方面的优化

写在文章开头 Netty通过巧妙的内存使用技巧尽可能节约内存空间,进而减少java中Full gc的STW的时间,由此间接的提升了程序的性能,本文也将直接从源码的角度分析一下Netty对于内存方面的使用技巧,希望对你有所启发。 Hi,我是 sharkChili ,是个不断在硬核技术上作死的 java…...

2024年轻人驯化AI指南

或许Python编程是答案 我为您精心准备了一份全面的Python学习大礼包&#xff0c;完全免费分享给每一位渴望成长、希望突破自我现状却略感迷茫的朋友。无论您是编程新手还是希望深化技能的开发者&#xff0c;都欢迎加入我们的学习之旅&#xff0c;共同交流进步&#xff01; &…...

揭秘量子比特态演化模拟:用现代C++20实现HHL算法,内存开销降低73%的关键技巧

第一章&#xff1a;量子比特态演化模拟的理论基础与工程挑战 量子比特态演化模拟是连接量子力学原理与可执行计算任务的核心桥梁。其理论根基植根于薛定谔方程的幺正演化描述&#xff1a;任意闭合量子系统的时间演化由哈密顿量 $H(t)$ 决定&#xff0c;满足 $|\psi(t)\rangle …...

全球工业不间断电源行业市场规模与增长预测

工业不间断电源&#xff08;简称工业UPS&#xff09;&#xff0c;专为严苛工业环境而设计,在复杂工业环境下为关键负荷提供高可靠性、高稳定性、强抗干扰能力的电力保护专。它的核心功能是在市电发生波动、短时断电或其他电力异常情况下&#xff0c;为关键设备提供持续、稳定的…...

OpCore-Simplify终极指南:15分钟完成黑苹果EFI配置的智能工具

OpCore-Simplify终极指南&#xff1a;15分钟完成黑苹果EFI配置的智能工具 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 黑苹果配置一直是技术爱好者…...

WSL2 子系统配置 SSH 并实现 VSCode 远程开发

1. 为什么要在WSL2中配置SSH服务&#xff1f; 作为一个长期使用WSL2进行开发的程序员&#xff0c;我发现直接通过终端操作WSL2虽然方便&#xff0c;但在某些场景下还是存在局限性。比如当需要同时管理多个项目时&#xff0c;终端窗口切换就显得不够高效&#xff1b;再比如团队协…...

3步搞定智能字幕下载:GetSubtitles让观影体验再升级

3步搞定智能字幕下载&#xff1a;GetSubtitles让观影体验再升级 【免费下载链接】GetSubtitles 一步下载匹配字幕 项目地址: https://gitcode.com/gh_mirrors/ge/GetSubtitles 您是否曾因找不到匹配的字幕而放弃观看一部精彩的外语影片&#xff1f;GetSubtitles作为一款…...

iperf3 Windows预编译二进制深度解析:专业网络性能测试技术实践

iperf3 Windows预编译二进制深度解析&#xff1a;专业网络性能测试技术实践 【免费下载链接】iperf3-win-builds iperf3 binaries for Windows. Benchmark your network limits. 项目地址: https://gitcode.com/gh_mirrors/ip/iperf3-win-builds iperf3-win-builds是针对…...

避免自激!AD8367用作AGC放大器时的PCB布局避坑指南与环路稳定性分析

避免自激&#xff01;AD8367用作AGC放大器时的PCB布局避坑指南与环路稳定性分析 在射频与中频电路设计中&#xff0c;AD8367作为一款高性能可变增益放大器(VGA)&#xff0c;因其宽增益范围(45dB)和集成平方律检波器特性&#xff0c;常被用于自动增益控制(AGC)系统。然而&#x…...

FFmpeg drawtext滤镜进阶:除了时间水印,你还能用它玩出什么花样?(动态文本+多位置叠加)

FFmpeg drawtext滤镜进阶&#xff1a;动态文本与多位置水印的创意实践 在视频处理领域&#xff0c;水印不仅是版权保护的标配工具&#xff0c;更是内容创作者展示品牌个性的画布。传统的时间戳水印早已无法满足专业用户的需求——想象一下&#xff0c;在直播流中实时显示股票行…...

WeChatMsg:数据自主权回归的创新方法

WeChatMsg&#xff1a;数据自主权回归的创新方法 【免费下载链接】WeChatMsg 提取微信聊天记录&#xff0c;将其导出成HTML、Word、CSV文档永久保存&#xff0c;对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/WeChatMsg 副标题…...

RK3588 Ethernet网络配置与调试实战指南

1. RK3588以太网配置基础入门 第一次拿到RK3588开发板时&#xff0c;很多开发者都会迫不及待地想测试网络功能。作为一款高性能处理器&#xff0c;RK3588通常配备双千兆以太网接口&#xff08;GMAC0和GMAC1&#xff09;&#xff0c;但在实际使用前需要正确配置才能正常工作。我…...