精华内容
下载资源
问答
  • 本功能已经完全实现,仿新浪博客下面的评论功能,并且实现了1楼到N楼的无限循环,引入了表情,并实现了分页功能,评分功能。支持头像显示,下载世界使用,里面css和sql文件都有,SQL文件导入数据库即可。我个人觉得值...
  • 文章同步:http://blog.csdn.net/wgyscsf/article/details/51510725。没有积分请留言
  • 简单实现了微信朋友圈评论功能,被点击的评论能够随着输入框高度的改变而改变位置!
  • Java+MySQL实现评论功能设计开发

    万次阅读 多人点赞 2018-08-13 09:39:52
    Java+MySQL实现评论功能设计开发 一、背景 项目初始版本上线,有时间写点东西记录一下项目中的心得体会,通过这个项目学习了很多,要写下来的有很多,先从评论功能开始吧。由于项目需要增加评论功能,之前并无此...

                                                    Java+MySQL实现评论功能设计开发

    一、背景

          项目初始版本上线,有时间写点东西记录一下项目中的心得体会,通过这个项目学习了很多,要写下来的有很多,先从评论功能开始吧。由于项目需要增加评论功能,之前并无此方面的经验,因此项目开始的一段时间都在寻思着如何进行评论功能的设计。上网搜索一波发现有很多优秀的第三方评论插件可以使用,本来准备直接采用的,但是心里始终有点疙瘩,可能是评论数据放在别人那里不放心的原因,或可能是想一探这些评论系统的究竟,因此最终决定自行设计开发这么一套评论功能。效果截图如下所示,采用的是MySQL数据库,编程语言用的Java。(更好的阅读体验,请移步我的个人博客)

    二、评论系统的概述:

           评论功能是用户表达对某一主题的想法的很好的一种方式,优秀的评论系统能很好地提高社区的活跃度。各大主流网站也都提供了相应的评论支持。比如:贴吧,新闻类门户网站(APP),UC浏览器等等。

          各大网站侧重点不同,对评论功能的要求就不一样,设计出来的评论系统自然就不一样。可能会有:①只可以进行评论,不可以回复,②既可以进行评论,也可以进行回复,然后在这个基础上可能会增加一些额外的功能,比如评论的折叠,审核,优选等。另外,一个良好的UI显示也是非常重要的,能给用户一个直观的视觉上的体验也是评论功能不可或缺的一个要素,毕竟用户都是具有很强的审美能力的,用户的使用体验决定了项目的需求。

          对于本项目,设计的是,既可以评论,也可以进行回复,评论和回复分开存储。在显示上,评论和回复显示的位置不同,回复相较于评论向右靠一些,这样,看起来比较舒适,当然也可以设置成其他的样式。评论会显示评论者头像,回复不会。

          评论的管理:后台系统应该具备基本的评论管理功能,比如:删除,折叠,优选,排序。这些功能的实现依赖于数据库表的设计,所以,在开始设计的时候,要想清楚自己的项目需要哪些功能。

    三、数据库表的设计:

         本评论功能采用评论和回复分离的方式进行存储,一共设计了两张表,一张用户评论表(comment),一张针对评论的回复表(comment_reply)。评论表主要保存对文章或者回答的评论,回复表保存对每一条评论的回复。

          评论表(comment)如下图:主要包括了:评论ID(作为回复表的主键),回答(文章)ID,评论者ID,评论内容,点赞数,评论时间,审核状态

         评论回复表(comment_reply)如下图:主要包括了:评论ID,用户ID,被回复人ID,回复内容,点赞数,回复时间。

          两张表通过comment_id联系起来(并没有设置主外键,主要是不想维护起来太麻烦),获取某一答案的评论及回复步骤:根据answer_id找到所有的评论,然后,遍历所有的评论,根据comment_id查询到所有的回复(评论者的基本信息,例如头像,名称等需要额外查询)。需要注意的是,在评论和回复数据较多的情况下做好分页处理。

    四、程序的实现: 

          采用Java语言进行编程的实现,使用的SSM框架。主要的功能代码如下所示(因为项目有通知功能,看的时候可以略过这部分,跟单纯的评论功能没有太大关系,但是一般要有通知,后面有时间会写站内通知的设计与开发博客):

       4.1 添加评论代码如下:(获取到评论相关的参数,然后进行向数据库表插入)

    public int addComment(Comment comment) {
    	try {
    		Answer commentAnswer = answerMapper.selectByPrimaryKey(comment.getAnswerId());
    		Long commentId = IDUtils.genItemId();//评论ID
    		Date createtime = new Date();
    		//1,填补comment对象的其他参数,进行插入
    		comment.setCommentId(commentId);
    		comment.setState(1);//状态: 0 待审核,1通过,2不通过
    		comment.setPraseCount(0);//一开始插入的点赞数设置为0
    		comment.setCreatetime(createtime);
    		comment.setUpdatetime(createtime);
    		commentMapper.insert(comment);//插入comment记录
    		//2,跟新Answer的相关一条数据,提示评论数+1
    		commentAnswer.setCommentNum((commentAnswer.getCommentNum()==null?0:commentAnswer.getCommentNum()) + 1);
    		answerMapper.updateByPrimaryKeySelective(commentAnswer);
    		//3,向提醒表插一条数据。这条评论是发给谁的,通知表里面的userId就是谁
    		if (comment.getUserId() != commentAnswer.getUserId()) { //自己评论自己不会有通知
    			Remind remind = new Remind();
    			remind.setRemindId(commentId);
    			remind.setUserId(commentAnswer.getUserId());
    			remind.setFromUserId(comment.getUserId());
    			//commentType:1评论回答,2评论别人的评论,3关注,4支持,5反对,6添加回答
    			remind.setRemindType(1);
    			//已读:0否,1是
    			remind.setReadStatus(0);//否
    			remind.setCreatetime(createtime);
    			//插入通知内容,以json的形式存储
    			RemindContent remindComment = new RemindContent();
    			remindComment.setContentId(commentAnswer.getAnswerId());
    			remind.setContent(JsonUtils.objectToJson(remindComment));//通知内容。回答问题的Id
    			remindMapper.insert(remind);
    		}
    		//返回1代表成功
    		return 1;
    	} catch (Exception e) {
    		e.printStackTrace();
    		return 2;
    	}
    }

     4.2 添加回复代码:(前台会传来评论的ID,然后,封装成回复对象进行插入,一个评论ID会对应很多回复)

    public int addCommentReply(CommentReply commentReply) {
        Long commentId = IDUtils.genItemId();//评论ID
        Date createtime = new Date();
    	commentReply.setPraseCount(0);
    	commentReply.setCreatetime(createtime);
    	int retVal = commentReplyMapper.insert(commentReply);
    	  
        //3,向提醒表插一条数据。这条评论是发给谁的,通知表里面的userId就是谁
    	if (commentReply.getReplyuserId() != commentReply.getUserId()) {
    	    Remind remind = new Remind();
    		remind.setRemindId(commentId);
    	    remind.setUserId(commentReply.getReplyuserId());
    		remind.setFromUserId(commentReply.getUserId());
    		//commentType:1评论回答,2评论别人的评论,3关注,4支持,5反对,6添加回答
    		remind.setRemindType(2);
    		//已读:0是,1否
    		remind.setReadStatus(1);
    		remind.setCreatetime(createtime);
    		remind.setContent(commentReply.getCommentId()+"");
    		remindMapper.insert(remind);
    	}
    	return retVal;
    }

     4.3获取某一回答的评论和回复(评论分页返回,但是回复没有分页,后面会优化,使用的是pagehelper插件):

    public PageBean<CommentStatus> listAnswerComments(Long answerId,Integer pageNum,Integer pageSize) {
    try {
    	CommentExample commentExample = new CommentExample();
    	commentExample.setOrderByClause("createtime DESC");
    	Criteria commentCriteria = commentExample.createCriteria();
    	commentCriteria.andAnswerIdEqualTo(answerId);
    	PageHelper.startPage(pageNum, pageSize);
    	List<Comment> commentList = commentMapper.selectByExampleWithBLOBs(commentExample);//获取具有分页结果的评论数据
    	List<CommentStatus> commentStatusList = new ArrayList<>();
    	for (Comment comment : commentList) {
    		CommentStatus commentStatus = new CommentStatus(); //评论返回的具体对象
    			
    		CommentReplyExample example = new CommentReplyExample();
    		com.pn.mini.model.CommentReplyExample.Criteria criteria = example.createCriteria();
    		criteria.andCommentIdEqualTo(comment.getCommentId());
    		List<CommentReply> commentReplyList = commentReplyMapper.selectByExample(example);
    		List<CommentReplyStatus> commentReplyStatusList = new ArrayList<>();
    		for (CommentReply commentReply2 : commentReplyList) {
    			UserBaseInfo commentUser = userBaseInfoMapper.selectByPrimaryKey(commentReply2.getUserId());
    			UserBaseInfo commentReplyUser = userBaseInfoMapper.selectByPrimaryKey(commentReply2.getReplyuserId());
    			CommentReplyStatus commentReplyStatus = new CommentReplyStatus();
    			commentReplyStatus.setCommentId(commentReply2.getCommentId());
    			commentReplyStatus.setContent(commentReply2.getContent());
    			commentReplyStatus.setCreatetime(commentReply2.getCreatetime());
    			commentReplyStatus.setPraseCount(commentReply2.getPraseCount());
    			commentReplyStatus.setReplyuserId(commentReply2.getReplyuserId());
    			commentReplyStatus.setReplyuserName(commentReplyUser.getUserName());
    			commentReplyStatus.setUserId(commentUser.getUserId());
    			commentReplyStatus.setUserName(commentUser.getUserName());
    			commentReplyStatusList.add(commentReplyStatus);
    		}
    		UserBaseInfo commentUserBaseInfo = userBaseInfoMapper.selectByPrimaryKey(comment.getUserId());
    		CommentIntegrate commentIntegrate = new CommentIntegrate();
    		commentIntegrate.setAnswerId(comment.getAnswerId());
    		commentIntegrate.setAvatar(commentUserBaseInfo.getAvatar());
    		commentIntegrate.setCommentId(comment.getCommentId());
    		commentIntegrate.setContent(comment.getContent());
    		commentIntegrate.setCreatetime(comment.getCreatetime());
    		commentIntegrate.setPraseCount(comment.getPraseCount());
    		commentIntegrate.setState(comment.getState());
    		commentIntegrate.setUpdatetime(comment.getUpdatetime());
    		commentIntegrate.setUserId(comment.getUserId());
    		commentIntegrate.setUserName(commentUserBaseInfo.getUserName());
    		//拼接一条评论的返回对象
    		commentStatus.setCommentIntegrate(commentIntegrate);
    		commentStatus.setCommentReplyStatusList(commentReplyStatusList);
    		commentStatusList.add(commentStatus);
    	}
    	PageBean<CommentStatus> recCommentItemBean = null;//接口返回的对象
    	PageInfo<Comment> pageInfo = new PageInfo<>(commentList);
    	recCommentItemBean = new PageBean<>(commentStatusList);
    	recCommentItemBean.setDataList(commentStatusList);
    	recCommentItemBean.setPageNum(pageInfo.getPageNum());
    	recCommentItemBean.setPages(pageInfo.getPages());
    	recCommentItemBean.setPageSize(pageInfo.getPageSize());
    	recCommentItemBean.setSize(pageInfo.getSize());		 
        recCommentItemBean.setTotal(pageInfo.getTotal());
    	return recCommentItemBean;
    			
    	} catch (Exception e) {
    		e.printStackTrace(); //出现异常返回null,controller根据此判断此处调用是否成功
    		return null;
    	}	
    }

     4.4  优化思考:

       ① 回复没有分页返回,回复数据量大的时候需要分页,在在获取回复的时候分页一下即可。

       ② 获取一条信息,需要再去查询用户表,获取用户的信息,这样就会导致获取一条回答的评论和回复需要查询N次数据表,思考的是增加冗余字段(用户名,用户头像),然后减少这方面的查询开销,当用户头像和名称更改的时候,同步更改这里面的数据,但是一般用户的这方面信息更改较少,总的来说,增加这个冗余字段还是能很大程度提高效率的。

      ③优化后的数据库表如下(忽略hot_value这样的字段,不同项目有不同需求):

     

    五:总结与反思(后续优化的方向):

           虽然评论功能开发完毕,在目前也可以正常的使用,待使用程序的用户的增加,流量的扩大后仍需要继续优化,不然在用户的使用体验上可能会很糟糕,尤其是当数据量大的时候,用户访问可能会感觉到有些慢。不足之处其一:在于获取评论的回复,每次读取数据的时候,需要遍历每一条评论,然后去查找这个评论下的所有回复,之后返回这些数据,这样就会造成获取一片文章的评论需要多次查找数据库,效率就会很低,下一步准备从数据库设计和程序实现两个方面去思考如何优化;其二在于:所有文章的评论都在一张表里面,评论的回复也都在一张表里面,这样就会导致表的条目很多,下一步优化的思路集中于分表操作,具体的实现还在思考中。。。。

         评论功能的设计还有很多需要优化的地方,欢迎对这方面有了解的小伙伴一起交流。

    展开全文
  • 评论功能实现

    千次阅读 2020-06-08 09:18:04
    本篇将博客主要是介绍如何实现文章的评论功能,已经对评论功能的优化处理。 1 评论功能分析 对于一条评论: 有可能是第一条对于文章的评论; 有可能是子评论(也就是回复); 评论中是否存在敏感词汇; 评论下方有...

    个人网站优化------博客文章评论模块

    摘要:
    本篇将博客主要是介绍如何实现文章的评论功能,已经对评论功能的优化处理。

    1 评论功能分析

    对于一条评论:

    1. 有可能是第一条对于文章的评论;
    2. 有可能是子评论(也就是回复);
    3. 评论中是否存在敏感词汇;
    4. 评论下方有评论,数据库表如何设计;
    5. 评论不能一直评论,需要进行条数控制;
      6. 评论需要管理员在后台进行管理;

    2 评论功能实现思路

    (1)数据库存储实现思路

    对于评论的存储我们可以使用Mysql数据库进行存储,因为评论内容较少.如果访问量较高的情况加我们可以使用MongoDB进行评论的存储.这里使用Mysql进行存储.创建数据库表t_comment用于存储评论数据库.创建单表t_comments用于存储comment的ID和comment的内容,并且使用t_comments用于在后台显示对comment可以对评论进行删除和查看操作。最重要的一点,t_comment表需要进行自关联。因为一条评论可能被回复。

    (2)评论存储业务逻辑实现思路

    在访客在前端进行评论的时候,在t_comment中进行存储,存储成功之后,在t_comments表中在进行一次存储,此次需要将t_comment中的评论ID以及评论内容进行存储(t_comments字段只有id name commentId)。

    (3)评论查看和删除业务实现

    查看很简单就是使用就是使用SpringDataJpa的查询所有就可以进行展示,删除评论需要先根据t_comments的id查询出t_comment的id将评论进行删除,然后将t_comments中的评论进行删除即可。

    3 后台实现逻辑代码

    评论存储逻辑代码

    @PostMapping("/comments")
        public String post(Comment comment, HttpSession session) {
            Long blogId = comment.getBlog().getId();
            /**
             * 根据blog查询一下评论条数,超过30条,停止写入数据库。
             */
            int number = commentService.getCommentsNumByblogid(blogId);
            if (number >= 30) {
                return "error/NumFull";
            }
    
            /**
             * 取出评论内容过滤脏字
             */
            try {
                //将文本中的敏感词读取出来存入List集合
                ClassPathResource classPathResource = new ClassPathResource("/static/txttemplate/maren.txt");
                List<String> list = FileUtil.readFile2List(classPathResource.getInputStream());
                //初始化词库
                Map sensitiveWordMap = sensitiveWordInit.initSensitiveWord(list);
                //初始化传入SensitiveUtil的敏感词库
                SensitiveUtil.sensitiveWordMap = sensitiveWordMap;
                String txtString = comment.getContent();
                boolean flag = SensitiveUtil.isContainsSensitiveWord(txtString, 2);
                if (flag) {
                    return "error/zang";
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            //没有脏字进行下面逻辑代码
    
            comment.setBlog(blogService.getBlog(blogId));
            User user = (User) session.getAttribute("user");
            if (user != null) {
                comment.setAvatar(user.getAvatar());
                comment.setAdminComment(true);
            } else {
                comment.setAvatar(avatar);
            }
            Comment comment1 = commentService.saveComment(comment);
    
            /**
             * 普通用户保存评论成功需要将评论内容在t_comments表中保存一份
             */
            Long commentId = comment1.getId();
            String name = comment.getContent();
            Comments comments = new Comments();
            comments.setCommentId(commentId);
            comments.setName(name);
            Comments commnetsNew = commentsService.saveComments(comments);
    
            return "redirect:/comments/" + blogId;
        }
    
    @Transactional
        @Override
        public Comment saveComment(Comment comment) {
            Long parentCommentId = comment.getParentComment().getId();
            if (parentCommentId != -1) {
                comment.setParentComment(commentRepository.findOne(parentCommentId));
            } else {
                comment.setParentComment(null);
            }
            comment.setCreateTime(new Date());
            return commentRepository.save(comment);
        }
    

    查看所有评论逻辑代码

    	/**
         * 查询所有用户的的评论
         */
        @GetMapping("/comments")
        public String comments(@PageableDefault(size = 10, sort = {"id"}, direction = Sort.Direction.DESC) Pageable pageable, Model model) {
            model.addAttribute("page", commentsService.listComments(pageable));
            return "admin/comments";
        }
    

    删除指定评论逻辑代码

    /**
     * 删除该评论:从t_comment中删除 、从t_comments表中也删除
     * 事务注解,其中一个数据库表删除失败就回滚
     */
    @Transactional
    @GetMapping("/comments/{id}/delete")
    public String delete(@PathVariable Long id, RedirectAttributes attributes) {
        /*先删除该条用户评论*/
        Comments comments = commentsService.findCommentsById(id);
        Long commentId = comments.getCommentId();
        commentService.delCommentById(commentId);
        /*再删除t_comments中的记录*/
        commentsService.delCommentsById(id);
        /*返回操作提示*/
        attributes.addFlashAttribute("message", "删除成功");
        return "redirect:/admin/comments";
    }
    

    4 总结

    此次评论管理模块还有很多不足之处需要继续改进;

    展开全文
  • 仿今日头条文章上拉看评论功能,今日头条的文章拉到下面时有个阻尼效果,再拉就进入了评论页面。就是这个功能
  • mongodb评论功能实现

    千次阅读 2020-09-12 23:03:22
    mongodb评论功能实现 对应的项目在我的下载里 一、mongodb 的优势与劣势 特点: MongoDB目前3大核心优势:『灵活模式』+ 『高可用性』 + 『可扩展性』,通过json文档来实现灵活模式,通过复制集来保证高...

    mongodb评论功能实现

    对应的项目在我的下载里

    一、mongodb 的优势与劣势

    特点:在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    MongoDB目前3大核心优势:『灵活模式』+ 『高可用性』 + 『可扩展性』,通过json文档来实现灵活模式,通过复制集来保证高可用,通过Sharded cluster来保证可扩展性。

    应用场景:

    1. 业务需要事务,使用mysql,因为mongodb不支持事务
    2. 数据量大,但是数据本身价值不大,使用mongodb
      ps:加载大量低价值的业务数据
    3. 数据是非结构化的,且数据量大,使用mongodb
    4. 业务未来走向不明确,使用mongodb,方便扩展

    优势:

    • 不存在SQL注入
    • 不需要提前创建表,可以直接写入数据
    • 字段数据格式自由 比如mysql中id 字段是数字,输入字符串会出错,mongodb不会
    • 可以处理json结构,并对其进行处理
      比如 字段a的值为
      {“a”:11,“b”:12,“c”:“abc”,“d”:[1,2,3]}
      你可以直接去读取或设置a字段的b值 a.b,读取a字段d数组的第二个值:a.d.1,可以去删除a字段的a数据$unset:{“a.a”:1},就会变成:
      {“b”:12,“c”:“abc”,“d”:[1,2,3]}
    • 可以使用upsert操作,修改的数据不存在的时候直接插入该数据
    • 查询和插入效率在没有索引的情况下远大于mysql

    缺点:

    • mongodb是nosql数据库,关系能力薄弱,不能使用join,union来联合查找
    • 事务能力薄弱
    • 效率存在波动性,不是很稳定

    MongoDB可以将模式设计划分为内嵌模式和 引用模式

    内嵌模式

    简单来讲,内嵌模式就是将关联数据,放在一个文档中。例如以下员工信息采用内嵌模式了而存储在了一个文档中:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OOSo6Eoo-1599922848721)(img\mongo01.png)]

    根据上面的描述可以看出,内嵌模型可以给应用程序提供很好的数据查询性能,因为基于内嵌模型,可以通过一次数据库操作得到所有相关的数据。同时,内嵌模型可以使数据更新操作变成一个原子写操作。然而,内嵌模型也可能引入一些问题,比如说文档会越来越大,这样就可能会影响数据库写操作的性能,还可能会产生数据碎片

    我们下面的demo就采用内嵌模型实现。

    引用模式

    引用模式是将数据存储在不同集合的文档中,而通过关系数据进行关联。例如,这里采用引用模式将员工信息存储在了3个文档中,基本信息一个文档,联系方式一个文档,登录权限放在了一个文档中。每个文档之前通过user_id来关联。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5P6Dw3Aj-1599922848724)(img\mongo02.png)]

    使用内嵌模型往往会带来数据的冗余,却可以提升数据查询的效率。但是,当应用程序基本上不通过内嵌模型查询,或者说查询效率的提升不足以弥补数据冗余带来的问题时,我们就应该考虑引用模型了。

    二、评论实现

    1.数据库设计

    (1)自由模式,无需提前声明、创建表结构,即不用先创建表、添加字段,然后才可以Insert数据。默认情况下MongoDB无需这样操作。

    (2)键值类型自由,MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。字段值可以包含其他文档,数组及文档数组。

    按照业务来慢慢添加,整个集合的存储文档格式就可以形成了。

    一般现在最常见的和最实用的都是二级评论,所以这里用二级评论举例

    一级评论, 文章id,用户id,用户评论内容,用户评论时间,评论回复

    其中回复即二级评论,

    包括 评论者id,评论者昵称,评论者评论内容,评论时间

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vP9eYbX3-1599922848727)(C:\Users\hasee\Desktop\mongodb实现评论\img\mongo03.png)]

    这里就遇到一个问题,二级评论可不可以被评论,如果不可以,显然不合理。

    所以这里仿照微博,在遇到二级评论的评论时,通过@确定回复的用户,内容全部和二级评论在同一级显示。

    因此我们设计出这样一个数据结构(此时二级评论和二级评论的回复是不同级的)

    {
        "_id" : ObjectId("597aa23add840cd4ce0681d1"),
        "comment_blog_id" : "ObjectId("597aa23add840213432rdsfsed1")",
        "comment_user_id" : "100001",
        "comment_user_name": "刘德宝",
        "comment_user_img": "图片地址",
        "comment_content" : "1号用户的评论",
        "create_time" : "2017-15-12 14:00",
        "comment_responses" : [ 
            {
            "_id" : ObjectId("597aa23add840cd4ce0681d1"),
                "response_user_id" : "1000002",
                "response_user_name" : "朱秀秀",
                "response_user_img" : "图片地址",
                "response_content" : [ 
                    "这是2号用户给刘德宝的评论", 
                ],
                "create_time" : "2017-15-12 14:00",
            }, 
            {
                "response_user_id" : "1000005",
                "response_user_name" : "小火龙",
                "response_user_img" : "图片地址",
                "response_content" : [ 
                     "这是5号用户给刘德宝的回复", 
                ],
                "create_time" : "2017-15-12 14:00",
                ]
            }
        ]
    }
    

    其中的评论和回复的id,昵称,和头像是方便查询显示使用,并且id可以用来做消息推送

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jVWQEmhf-1599922848730)(\img\mongo11.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mj8uDoGF-1599922848731)(\img\mongo10.png)]

    评论全部不存在。

    一级评论未存在,全空,最下方有一级评论回复框,可以进行评论产生comment

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RNRRrubS-1599922848735)(\img\mongo09.png)]

    一级评论存在,没有回复,点击回复,出现回复框,完成回复,即二级评论,可以回复,产生comment.response

    同理对二级评论的回复也是通过点击生成回复框,然后进行回复。然后判断是否是回复的一级评论来控制是否显示回复两个字即可。

    所以和二级评论实现是同一个逻辑,前台即可完成。

    2.spring整合mongodb

    整合部分最难的就是配置和熟悉对mongodb数据库的操作,其他的前台传参和逻辑编写大家都很熟悉。这里主要介绍一下mongodb操作的坑

    mongoRepository和mongoTemplate

    ①、Spring Data MongoDB 是 Spring 框架访问 MongoDB 数据库的分支,使用它可以非常方便地操作 MongoDB 数据库。

    Spring Data MongoDB 是 Spring Source 的一个子项目,旨在为关系型数据库、非关系型数据、Map-Reduce框架、云数据服务等等提供统一的数据访问API。

    Spring Data 提供了基于Repository的统一接口 MongoRepository 完成对象的 CRUD 操作以及查询方法、排序和分页方法等。

    使用 Spring Data 可以帮助我们快速构建项目,非常方便,Spring Data 在数据持久层已经写好了常用功能,我们只需要定义一个接口去继承 Spring Data 提供的接口,就可以实现对数据库的操作,也可以自定义接口方法,甚至这些自定义方法都不需要我们手动去实现,Reposity 会自动完成实现。

    实现方法:

    1.创建自定义接口 xxxRepository ,继承 MongoRepository,这样xxxRepository 就直接拥有了MongoRepository 中定义好的基本CRUD操作,同时可以完成方法扩展,只需要在xxxRepository 中按照规则声明抽象方法即可,MongoRepository 会自动完成方法实现,同时在类定义处添加@Repository 注解完成IoC注入。

    2.创建自定义接口 xxxService 以及实现类xxxServiceImpl,并通过自动装载将xxxRepository 注入xxxServiceImpl。

    3.创建 xxxController 实现相关业务方法。

    spring-data-mongodb其实有两种实现方式,一种是直接继承MongoRepository接口,dao层的实现,默认提供了CRUD的各种方法,几乎不用编写任何代码。另一种是通过MongoTemplate来操作数据库,这样需要自定义dao层接口,默认没有任何接口可以使用

    Criteria类:它封装所有的语句,以方法的形式进行查询。

    Query类:这是将语句进行封装或者添加排序之类的操作。(实现分页操作就在这里)

    mongoTemplate操作简介

    1.添加依赖

    <dependency>  
    <groupId>org.springframework.data</groupId> 
    <artifactId>spring-data-mongodb</artifactId>  
    <version>2.1.1.RELEASE</version>
    </dependency>
    

    添加spring和mongodb的依赖

    网上都说要加一个mongo的依赖类似于这种

    org.mongodb mongo-java-driver 2.13.0

    2.mongodb配置文件

    mongodb.properties

    #数据库名称
    mongo.dbname=sag
    #用户名
    mongo.username=
    #密码
    mongo.password=
    #主机
    mongo.host=127.0.0.1
    #端口
    mongo.port=27017
    #线程最大阻塞数
    mongo.connectionsPerHost=8
    #线程队列数mongo.threadsAllowedToBlockForConnectionMultiplier=4
    #连接超时时间,单位毫秒
    mongo.connectTimeout=1500
    #最大等待时间
    mongo.maxWaitTime=1500
    #自动连接
    mongo.autoConnectRetry=true
    #socket存活
    mongo.socketKeepAlive=true
    #socket超时时间
    mongo.socketTimeout=1500
    #读写分离
    mongo.slaveOk=true
    

    spring-mongodb.xml

    <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xmlns:context="http://www.springframework.org/schema/context"       xmlns:mongo="http://www.springframework.org/schema/data/mongo"       xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd">   
    
    
    <!--引入MongoDB连接文件-->  
    <context:property-placeholder location="classpath:mongodb.properties" ignore-unresolvable="true"/>   
    <!--连接MongoDB服务器-->   
    <!-- 除此之外还有一个mongo连接,标签为<mongo-mongo> 版本较低时使用 -->   
    <mongo:mongo-client id="mongo" host="${mongo.host}" port="${mongo.port}" >
    <mongo:client-options               
    connections-per-host="${mongo.connectionsPerHost}"
    threads-allowed-to-block-for-connection-multiplier=
    "${mongo.threadsAllowedToBlockForConnectionMultiplier}"
    connect-timeout="${mongo.connectTimeout}"
    max-wait-time="${mongo.maxWaitTime}"
    socket-keep-alive="${mongo.socketKeepAlive}"
    socket-timeout="${mongo.socketTimeout}"/> 
    </mongo:mongo-client>  
    
    <bean id="mappingContext"          			     class="org.springframework.data.mongodb.core.mapping.MongoMappingContext" /> 
    
    <!-- 去掉默认的_class属性 -->  
    <bean id="customMongoTypeMapper"          class="org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper">        <constructor-arg name="typeKey"><null/></constructor-arg>    
    </bean>   
    
    <bean id="mappingMongoConverter" class="org.springframework.data.mongodb.core.convert.MappingMongoConverter">
    <constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />       
    <constructor-arg name="mappingContext" ref="mappingContext" />    
    <property name="typeMapper" ref="customMongoTypeMapper" />
    </bean>   
    
     <!-- mongo的工厂,通过它来取得mongo实例,dbname为mongodb的数据库名,没有的话会自动创建 -->
    <mongo:db-factory id="mongoDbFactory"  dbname="${mongo.dbname}" mongo-ref="mongo"/> 
    
    <!-- 配置mongoTemplate -->
    <!-- mongodb的主要操作对象,所有对mongodb的增删改查的操作都是通过它完成-->   
    <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">     <constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
    <constructor-arg name="mongoConverter" ref="customMongoTypeMapper"></constructor-arg>
    </bean>
    </beans>
    

    在Spring框架中,对mongodb进行操作的是mongoTemplate对象。

    MongoTemplate是数据库和代码之间的接口,对数据库的操作都在它里面。

    注:MongoTemplate是线程安全的。

    在实现评论demo之前,我们先插入两个例子,感受一下mongotemplate

    这里我建了一个POJO, comment

    @Document(collection = "commenttest")
    public class Comment {  
    	//@id
        private String comment_id;         //评论id
        private String comment_blog_id;    //文章id
        private String comment_user_id;    //评论用户id
        private String comment_user_name;   //评论用户名
        private String comment_user_img;   //评论用户头像
        private String comment_content;    //评论内容
        private String comment_create_time;   //评论时间
        private String comment_responses;   //评论回复
    }
    

    然后相应的创建了一个CommentTestRepository类

    @Repository
    @Document(collection = "commenttest")
    public class CommentTestRepository {    
        @Autowired   
        MongoTemplate mongoTemplate;  
        public CommentTestRepository() {    
            ApplicationContext ac = new ClassPathXmlApplicationContext("spring/spring-mongodb.xml");        
            mongoTemplate = (MongoTemplate) ac.getBean("mongoTemplate");    
        }    
        public void insertComment(Comment comment) {       
            List<Comment> list = new ArrayList<Comment>();    
            list.add(comment);      
            mongoTemplate.insert(list,Comment.class);   
    }
    

    http 通过 controller 进来的 mongotemplate 对象才会注入 spring ,才能正常使用,如果是通过其他 controller 类来调用同层的 controller 里面的 mongotemplate ,则该对象是 null 值,不可使用 !而从 controller 调用 service 这种不同层的,则可以正常使用!

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mJoQEQ8O-1599922848736)(\img\mongo04.png)]

    插入成功后,可以看到两个字段与我们预期的不太一样,_ id和_ class

    _id:是系统自动生成的12字节唯一标识。满足分布式

    mongodb采用了一个称之为ObjectId的类型来做主键。ObjectId是一个12字节的 BSON 类型字符串。按照字节顺序,一次代表:

    4字节:UNIX时间戳
    3字节:表示运行MongoDB的机器
    2字节:表示生成此_id的进程
    3字节:由一个随机数开始的计数器生成的值

    https://blog.csdn.net/xiamizy/article/details/41521025

    不使用ObjectId方法:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NoDJwLHu-1599922848738)(\img\mongo05.png)]

    只需要把POJO实体类里的comment_id改名为id即可完成一种类似于覆盖的效果,达到了不使用objectId的目的。

    或者在想要标识为id的属性上加上注解@id

    _class:这个字段就是用来映射Pojo的,更具体的说,是为了方便处理Pojo中存在继承的情况,增加系统的扩展性的。去掉的方法就是配置Converter,默认映射为null

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nAT38MNK-1599922848739)(\img\mongo08.png)]

    假如到时候设计不好,字段不确定怎么办。

    个人建议是刚开始少一点,后面可以再加。

    映射的时候,pojo里有的属性,数据库里没有,依然可以映射

    数据库里有的字段,而pojo里没有对应的属性接受的话就无法完成映射了,

    ​ 比如我用户头像刚开始没加,只需要在对应的pojo类里,插入和查询逻辑里添加用户头像的属性和逻辑,就可以完成拓展,并不用像传统数据库再创建一个列,或者费心思再去创建几个冗余列。这就体现了mongodb的高拓展性。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mC3dN0ro-1599922848740)(\img\mongo06.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0Wj2EgQ2-1599922848740)(\img\mongo07.png)]

    评论要点:

    内嵌对象,比如comment对象里有一个response对象。那么mongotemplate映射到mongodb里的时候,对这个是怎么操作的呢。

    根据实际情况,一级评论完毕,回复是在一级评论之后才有的操作,这个之前,一级评论文档中是没有任何回复的。所以这里检查了一下comment里如果没有这个键,或者这个键为空,可不可以插入到mongodb中。答案是可以的。

    在测试的时候,尝试了两种方案。

    1.在设置comment实例的时候,把对象设置为null

    comment.setComment_responses(null);
    

    2.不设置该属性,直接插入

    结论:两者都不会插入该属性对应的数据。也不会存在这个属性对应的键值。不会自动映射进去

    到时候要往comment里插入回复的时候,直接使用mongotemplate.upsert语句就可以完成。

    如果查到一级评论没有任何回复,它就会创建response这个键,然后插入第一条记录

    如果一级评论已经有回复,它就会插入到这个键里,成为这个键下的一个文档。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZU38yJlO-1599922848742)(\img\mongo12.png)]

    mongoTemplate实现评论

    通过手写插入一些假数据之后,我们开始查询,插入要和实际业务逻辑相对应,这里demo就不做了,大体思路是一级评论直接插入,二级评论,前台传入一级评论的id然后根据id插入,同时记录用户名和用户头像。

    如果有,则前台显示有回复样式,没有的话,就是默认回复一级评论的二级评论。

    查询实现

    List<Comment> comments = mongoTemplate.find(query,Comment.class);
    List<JSONObject> comments = mongoTemplate.find(query,JSONObject.class,"testMax2");       
    

    query:查询条件,

    .class:需要查询的类型

    最后的字符串是需要查询的Collection,

    采用后面这个语句会遍历的把整个Collection查询出来,包括其下面的responses

    也可以不写,默认的是.class上注解的,但是这里采用了JSONObject.class这个类进行查询,我并没有对这个类进行注解,所以后面这个集合要写。

    整合springboot

    配置

    在spring官网添加mongodb即可。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x6xK6CGO-1599922848744)(C:\Users\hasee\Desktop\mongodb实现评论\img\mongo13.png)]

    先入为主的导入了Mybatis但没有引入Mysql,所以这里要引入

    <dependency>  
    <groupId>mysql</groupId> 
    <artifactId>mysql-connector-java</artifactId>
    </dependency>
    

    其余的配置文件在打包上传的包里。

    总结:

    1.为什么评论要使用mongodb而不是mysql?

    ​ mongodb舍弃了事务管理,但是mongodb的功能是海量数据的查询很快,读写效率高,一般用于简单的sql查询或日志类数据的写入操作。

    ​ 在评论的业务中,最多的就是查询评论和发表评论了。

    ​ BSON文档结构的存储形式,查询很方便,不需要关联查询。

    ​ 其次,评论这种数据价值并不是特别大,但是量却很大,所以可以交给mongodb来存储

    ​ 在这种不需要事务管理,也不需要关联查询的情况下,使用mongodb可以得到更快的响应速度,更快捷的开发,更少的数据库设计。

    2.为什么使用了内嵌模型而不是引用模型?

    一对很少 one-to-few 可以采用内嵌文档

    优点:不需要单独执行一条语句去获取内嵌的内容

    缺点:无法把这些内嵌文档当做单独的实体去访问

    适用场合:一对很少且不需要单独访问内嵌内容

    展开全文
  • 网页实现的评论功能的代码

    千次阅读 2020-10-17 01:19:46
    实现一个评论功能的页面并能通过验证码验证 效果展示1 效果展示2 效果展示3 效果展示4 内容 名字 描述 主页面展示 main.html 文章部分与评论部分整体 主页面css main.css 修饰整个页面整体部分 评论...

    用springboot实现一个具备评论功能的页面(从前端HTML到后端MySQL完整代码)

    关于该部分的说明:因为最近用springboot写了网站项目(目前部署到Linux上出现了不少问题),就从我写的项目中挑出实现评论功能这部分的代码进行展示,那么该部分就给新手们在练习springboot时提供了一个思路(通过左上角码龄看得出来我也是新手,哈哈)

    要实现评论功能
    第一:评论的哪篇文章得知道,什么时候评论的,评论的什么内容(我直接访问数据库了)
    第二:还得知道评论的人是谁,头像是什么(我下面给的是四个随机头像,6个随机网名)
    第三:评论内容字数内容是否要进行限制(我也只是写死了几个词汇进行对比)

    效果展示1

    评论功能1

    效果展示2

    效果展示2

    效果展示3

    评论功能3

    效果展示4

    效果展示4

    内容名字描述
    主页面展示main.html文章部分与评论部分整体
    主页面cssmain.css修饰整个页面整体部分
    评论功能整体CSScommentStyle.css确定评论区大小,所处的位置等
    评论后展示出来的CSSgetSQLcomment.css确定评论展示出来内容的位置,等
    写评论过程中的jscheck,js每提交一次评论随机产生一个头像及网名,以及生成提交时个时间
    验证码jscommentCheck.js生成四个0-9之间的数字的验证码

    页面main.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>CSDN.specialwu</title>
        <link type="text/css" rel="styleSheet" th:href="@{/css/main.css}" href="css/main.css" />
    	<link href="css/commentStyle.css" rel="stylesheet">
    	<link href="css/getSQLcomment.css" rel="stylesheet">
    </head>
    <body>
    <div class="containAll">
    <!--主体内容-->
    <div class="middle">
        <!-- 文章部分 -->
        <div class="middle-left">
            <!--点击后放大-->
            <div th:each="alltiltes:${articles}">
            <a class="big" th:attr="href='show?id='+${alltiltes.articleId}">
                <!--文本框:涉及文章图片、标题、等内容,这个部分不做主要阐述-->
                <table  cellspacing="0" cellpadding="0" height="148" width="930" class="artitle"  >
                    <tr class="title-picture">
                        <td rowspan="3" width="324" height="148">
                            <img src="images/mofei.jpg" width="324" height="148"/>
                        </td>
                        <td colspan="3" bgcolor="#e8e5ff">
                            <h1 th:text="${alltiltes.title}" th:name="title">ssss</h1>
                        </td>
                    </tr>
                    <tr align="center">
                        <td height="40" width="130" bgcolor="#99a7ff">
                            浏览:<span th:text="${alltiltes.clickCount}">10</span></td>	
                        </td>
    					<td bgcolor="#b5c9ff">
    						 评论:<span th:text="${alltiltes.commentNumber}">10</span></td>
    					<td width="260" bgcolor="#c6d1ff">
    						发表时间:<span th:text="${alltiltes.writeTime}" th:width="260">2020-10-05 17:45</span>
    					</td>
                    </tr>
                    <tr>
                        <td th:switch="${alltiltes.typeId}"bgcolor="#eff3ff"  th:name="articleId" colspan="3">
                            <span  th:case="1">类型1</span>
                            <span  th:case="2">类型2</span>
                            <span  th:case="3">类型3</span>
                        </td>
                    </tr>
                </table>
            </a>
            </div>
        </div>
        <!-- 评论部分 -->
            <div class="middle-right">
            <div class="commentBlock">
                <header><span>评论</span></header>
                <section>
                    <ul></ul>
                    <table th:each="comments:${articleComments}">
                        <tr>
                            <td colspan="2" align="center">
                                <span th:text="${comments.commentsTime}">2020-09-9</span>
                            </td>
                        </tr>
                        <tr height="20">
                            <td class="gettouxiang">
                                <img src="/comment/images/tou01.png" th:src="${comments.givePicture}" />
                            </td>
                            <td class="gettouScreenName" th:switch="${comments.giveScreenName}">
                            <!-- 在点击评论后产生0-5之间的数字并存到数据库,从数据库里拿出来后再进行对照,根据需要可进行修改-->
                                <span  th:case="0">这是一个网名</span>
                                <span  th:case="1">孤狼</span>
                                <span  th:case="2">似水流年</span>
                                <span  th:case="3">冰与火</span>
                                <span  th:case="4">皮卡丘的皮</span>
                                <span  th:case="5">永恒的心</span>
                            </td>
                        </tr>
                        <tr height="20">
                            <td colspan="2" class="getContent">
                                <h1 th:text="${comments.commentContent}">数据库来的评论</h1>
                            </td>
                        </tr>
                    </table>
                </section>
                <div class="post" align="center">
                    <form action="/manageComments/addComment" th:action="@{/manageComments/addComment}" method="post" th:object="${commentObject}" onsubmit="return trueFales()">
                        <input class="writeCommentContent"id="writeContent" placeholder="内容(1-50个字符)" type="text">
    
                        <input hidden id="writeContent1"  th:field="*{commentContent}" name="commentContent" type="text">
                        <input hidden th:text="${giveArticleId}" th:field="*{articleId}" name="articleId" type="text">
                        <input hidden  th:field="*{commentsTime}" id="getTime" name="commentsTime" type="text">
                        <input hidden  th:field="*{givePicture}" id="getPicture"name="givePicture" type="text">
                        <input hidden  th:field="*{giveScreenName}" id="getScreenName" name="giveScreenName" type="text">
    
                        <canvas id="canvas" width="150px" height="50px"></canvas><br>
                        <input id="givevalue" type="text" hidden>
                        <span><font color="aqua">点击刷新验证码</font></span><br>
                        <input id="checking" type="text">
                        <input type="submit" class="btn" value="发布">
                    </form>
                </div>
            </div>
    </div>
    </body>
    <script src="js/check.js"></script>
    <script src="js/commentCheck.js"></script>
    </html>
    

    页面CSS main.css

    *{
        margin: 0;
        padding: 0;
    }
    body{
        background-color: black;
    	height: 100%;
    	font-family:"宋体";
    	font-weight:normal;
    	font-style:normal;
    }
    /*中间部分*/
    .middle{
        width: 100%;
        margin-top: 0px;
    }
    /*中间左侧*/
    .middle-left{
        float: left;
        width: 75%;
        height: 100%;
    }
    /* 涉及标题等其他信息 */
    .artitle{
        margin-top: 20px;
        margin-left: 30px;
    	color: black;
        background-color: black;
    }
    /*点击后旋转360放大1.1倍*/
    .big table:hover{
        -moz-transform: rotate(360deg) scale(1.1);
        -webkit-transform: rotate(360deg) scale(1.1);
        -o-transform: rotate(360deg) scale(1.1);
        -ms-transform: rotate(360deg) scale(1.1);
        transform: rotate(360deg) scale(1.1);
    }
    /*中间右侧*/
    .middle-right{
        width: 25%;
        float: right;
    }
    

    评论部分整体的显示commentStyle.css

    /* 整个宽度 width */
    .commentBlock{
        margin: 0 auto;
        width: 300px;
        position: relative;font-family: "bodoni mt";
        color: white;
    }
    header{
        padding: 5px 0;
        border-bottom: 1px solid #cecece;
    }
    header span{
        display:inline-block;
        width: 220px;
        height: 50px; color: #fff;
        background: #009966;
        font-size: 18px;
        font-weight: bold;
        text-align: center;
        line-height: 50px;
        border-radius: 8px;
        cursor: pointer;
    }
    /* 评论前填写的部分 */
    .post{
        position: absolute;
        background: #ffffff;
        border: 1px #999999 solid;
        width: 300px;
        top:70px; padding: 10px;
        font-size: 14px;
        z-index: 999999;
        display: none;
    }
    /*修饰发布字体、内容这部分*/
    .post .writeCommentContent{
        width: 300px;
        height:30px;
        line-height: 30px;
        display: block;
        border: 1px #aaaaaa solid;
        margin-bottom: 10px;
    }
    /* “发布”字体 */
    .post .btn{
        width: 160px;
        height: 35px;
        color: #fff;
        background: #009966;
        border: none;
        font-size: 14px;
        font-weight: bold;
        text-align: center;
        line-height: 35px;
        border-radius: 8px;
        cursor: pointer;
    }
    
    .commentBlock section ul li{
        padding: 10px 0;
        border-bottom: 1px #999999 dashed;
        overflow: hidden;
    }
    /*头像栏*/
    .commentBlock section ul li div{
        float: left;
        width: 50px;
        margin-right: 10px;
    }
    /* 网名栏 */
    .bbs section ul li div span{
        float: left;
        width: 150px;
        margin-right: 10px;
        margin-top: 5px;
    }
    /*图片显示*/
    .commentBlock section ul li div img{
        border-radius:50%;
        width: 40px;
    }
    /*评论内容*/
    .commentBlock section ul li h1{
        float: left;
        width: 300px;
        font-size: 16px;
        line-height: 35px;
    }
    

    从数据库插入后评论内容的展示getSQLcomment.css

    .commentBlock section  table{padding: 5px 0; border-bottom: 1px #999999 dashed;
        overflow: hidden;font-family: "bodoni mt"}
    	
    	/* 图片栏 */
    .commentBlock .gettouxiang{float: left; width: 50px; margin-right: 10px;margin-top: 5px;}
    /* 网名栏 */
    .commentBlock .gettouScreenName{float: left; width: 150px; margin-right: 10px;margin-top: 5px;}
    /* 图片圆 */
    .commentBlock  img{ border-radius:50%; width: 40px;}
    /* 评论内容字体大小 */
    .commentBlock  h1{float: left; width: 300px; font-size: 16px; line-height: 25px;}
    

    评论相关commentCheck.js

    提交时,内容不能为空,验证码需正确,不能涉及敏感信息都满足了方可提交
    在处理提交内容时去掉评论内容两端的空格,方便存储,以及做个简单敏感信息验证,>实际生产中是通过敏感信息数据库来完成的
    为什么要自己生成网名,头像,为做到用户的快速评论,以及避免存储太多无关紧要信息,从而自己生成较为便利

    /**
     * Changed by specialwu on 2020/10/02
     */
    
    $(document).ready(function(){
        $(".commentBlock header span").click(function(){
    		/* 调取验证码获得数字赋值*/
    		drawPic();
            $(".commentBlock .post").show();
        });
    })
    function trueFales() {
    /*加载时图片无法显示得改成.png*/
    	/* 获取到验证码的值 */
    	var giveValue=document.getElementById("givevalue").value;
    	/* 输入的值与验证码的值进行对比 */
    	var inputValue=document.getElementById("checking").value;
    	/* 内容 */
    	var writeContent=document.getElementById("writeContent").value;
    	/* 此段大概提供个思路,也可以把一些敏感词存到数据库,如果库中有则不能存储 */
    	var guolv=new Array("SB","垃圾","神经","政治");
    	var trueFale=0;
    	for(var i=0; i<guolv.length;i++){
    		/* 去除空格 */
    		moveblack = writeContent.replace(/\s*/g,"");
    		/* 去除逗号 */
    		str = moveblack.replace(/,/g, "");
    		if(str==guolv[i]){
    			trueFale=1;
    		}
    		/* 此段希望每两个词为一组进行对比,以此过滤关键词*/
    		/* 			for(var j=0;j<writeContent.length-2;j++){
                            var theEnd=writeContent.substring(j,j+2);
                            alert(theEnd)
                            if(str==guolv[i]){
                                trueFale=1;
                            }
                        } */
    	}
    	if(trueFale==1){
    		alert("请勿涉及敏感信息");
    		return false;
    	}else if(writeContent==''){
    		alert("内容不能为空");
    		return false;
    	}else{
    		if(giveValue==inputValue){
    			alert("提交中")
    			var sNumber=Math.floor(Math.random()*6);  //0-5的随机数
    			/* 去除前后空格再显示 */
    			/* 去除空格 */
    			moveblack = writeContent.replace(/\s*/g,"");
    			/* 去除逗号 */
    			str = moveblack.replace(/,/g, "");
    			document.getElementById("writeContent1").value=str;
    
    			var myDate=new Date();
    			<!--提交后产生一个时间-->
    			var currentDate=myDate.getFullYear()+"-"+parseInt(myDate.getMonth()+1)+"-"+myDate.getDate()+" "+myDate.getHours()+":"+myDate.getMinutes();
    			document.getElementById("getTime").value=currentDate;
    			document.getElementById("getScreenName").value=sNumber;
    			if (sNumber==0){
    				<!-- 图片自己定义,同样存在数据库的只是地址,相当于一个引用,实际图片还是存储在项目中-->document.getElementById("getPicture").value="/comment/images/tou01.png";
    			}else if (sNumber==1){
    				document.getElementById("getPicture").value="/comment/images/tou02.png";
    			}else if (sNumber==2){
    				document.getElementById("getPicture").value="/comment/images/tou03.png";
    			}else if (sNumber==3){
    				document.getElementById("getPicture").value="/comment/images/tou04.png";
    			}else if (sNumber==4){
    				document.getElementById("getPicture").value="/comment/images/tou05.png";
    			}else {
    				document.getElementById("getPicture").value="/comment/images/tou06.png";
    			}
    			return true;
    		}else{
    			alert("验证码错误");
    			return false;
    		}
    	}
    }
    

    验证码check.js

    验证码的作用就是避免有人恶意点击,频繁访问数据库;

        //生成随机数
    function randomNum(min,max){
        return Math.floor(Math.random()*(max-min)+min);
    }
        //生成随机颜色RGB分量
    function randomColor(min,max){
        var _r = randomNum(min,max);
        var _g = randomNum(min,max);
        var _b = randomNum(min,max);
        return "rgb("+_r+","+_g+","+_b+")";
    }
    //先阻止画布默认点击发生的行为再执行drawPic()方法
    document.getElementById("canvas").onclick = function(e){
        e.preventDefault();
        drawPic();
    };
    function drawPic(){
        //获取到元素canvas
        var $canvas = document.getElementById("canvas");
        var _str = "0123456789";//设置随机数库
        var _picTxt = "";//随机数
        var _num = 4;//4个随机数字
        var _width = $canvas.width;
        var _height = $canvas.height;
        var ctx = $canvas.getContext("2d");//获取 context 对象
        ctx.textBaseline = "bottom";//文字上下对齐方式--底部对齐
        ctx.fillStyle = randomColor(180,240);//填充画布颜色
        ctx.fillRect(0,0,_width,_height);//填充矩形--画画
        for(var i=0; i<_num; i++){
            var x = (_width-10)/_num*i+10;
            var y = randomNum(_height/2,_height);
            var deg = randomNum(-45,45);
            var txt = _str[randomNum(0,_str.length)];
            _picTxt += txt;//获取一个随机数
            ctx.fillStyle = randomColor(10,100);//填充随机颜色
            ctx.font = randomNum(16,40)+"px SimHei";//设置随机数大小,字体为SimHei
            ctx.translate(x,y);//将当前xy坐标作为原始坐标
            ctx.rotate(deg*Math.PI/180);//旋转随机角度
            ctx.fillText(txt, 0,0);//绘制填色的文本
            ctx.rotate(-deg*Math.PI/180);
            ctx.translate(-x,-y);
        }
        for(var i=0; i<_num; i++){
            //定义笔触颜色
            ctx.strokeStyle = randomColor(90,180);
            ctx.beginPath();
            //随机划线--4条路径
            ctx.moveTo(randomNum(0,_width), randomNum(0,_height));
            ctx.lineTo(randomNum(0,_width), randomNum(0,_height));
            ctx.stroke();
        }
        for(var i=0; i<_num*10; i++){
            ctx.fillStyle = randomColor(0,255);
            ctx.beginPath();
            //随机画原,填充颜色
            ctx.arc(randomNum(0,_width),randomNum(0,_height), 1, 0, 2*Math.PI);
            ctx.fill();
        }
        //数字验证码的值传给一个隐藏的输入框,通过该框拿值再做对比
    	document.getElementById("givevalue").value=_picTxt;
        return _picTxt;//返回随机数字符串
    }
    drawPic();
    

    pojo

    public class Comments {
        /*分别代表评论内容编号、评论的时间、被评论的文章的id、评论的内容,
        随机产生头像的编号,随机产生网名的编号,标题,评论的类型*/
        //省略getter setter
        private Integer commentsId;
        private String commentsTime;
        private Integer articleId;
        private String commentContent;
        private String givePicture;
        private Integer giveScreenName;
        private String title;
        private Integer typeId;
        }
    

    Controller

    @Controller
    @RequestMapping("/article")
    public class ArticleController {
        @Autowired
        private ManageCommentsMapper manageCommentsMapper;
        /*展示评论内容*/
        @GetMapping("/show")
        public String show(Model model, Integer id, HttpSession session) {
            List<Comments> allComments = manageCommentsMapper.showComment(id);
            model.addAttribute("commentObject",new Comments());
            model.addAttribute("articleComments",allComments);
            return "main";
        }
     }
        /*写评论*/
        @PostMapping("/addComment")
        public String addComment(@ModelAttribute Comments comments,HttpSession session,String commentsTime,Integer articleId,String commentContent,String givePicture,Integer giveScreenName) {
            Integer giveArticleId = (Integer)session.getAttribute("giveArticleId");
            System.out.println("传来的id"+giveArticleId);
            Integer integer = manageCommentsMapper.addComment(commentsTime,giveArticleId, commentContent, givePicture, giveScreenName);
            return "redirect:/article/main";
        }
    

    dao

    /*前台显示某篇文章的评论*/
    @Repository
    @Mapper
    public interface ManageCommentsMapper {
    @Select("<script>select * from comments" +
            "<where>" +
            "       articleId = #{articleId}" +
            "</where>" +
            "</script>")
    List<Comments> showComment(Integer articleId);
    
    
    /*写评论*/
    @Insert("<script> insert into comments" +
            "(" +
            "            <if test=\"commentsTime != null\" >" +
            "                commentsTime," +
            "            </if>\n" +
            "            <if test=\"articleId != null\" >" +
            "                articleId," +
            "            </if>" +
            "            <if test=\"commentContent != null\" >" +
            "                commentContent," +
            "            </if>" +
            "            <if test=\"givePicture != null\" >" +
            "                givePicture," +
            "            </if>" +
            "            <if test=\"giveScreenName != null\" >" +
            "                giveScreenName" +
            "            </if>" +
            ")" +
            "        values" +
            "(" +
            "            <if test=\"commentsTime != null\" >" +
            "                #{commentsTime}," +
            "            </if>\n" +
            "            <if test=\"articleId != null\" >" +
            "                #{articleId}," +
            "            </if>" +
            "            <if test=\"commentContent != null\" >" +
            "                #{commentContent}," +
            "            </if>" +
            "            <if test=\"givePicture != null\" >" +
            "                #{givePicture}," +
            "            </if>" +
            "            <if test=\"giveScreenName != null\" >" +
            "                #{giveScreenName}" +
            "            </if>" +
            ")" +
            "</script>")
    Integer addComment(String commentsTime,Integer articleId,String commentContent,String givePicture,Integer giveScreenName);
    }
    

    Mysql

    它们的含义分别是:评论的id,评论的时间,被评论文章的id,评论的内容,给评论者的随机头像,给评论者的网名;

    CREATE TABLE `comments` (
      `commentsId` int(10) NOT NULL AUTO_INCREMENT,
      `commentsTime` varchar(100) NOT NULL,
      `articleId` int(10) DEFAULT NULL,
      `commentContent` varchar(200) DEFAULT NULL,
      `givePicture` varchar(100) DEFAULT NULL,
      `giveScreenName` int(10) DEFAULT NULL,
      PRIMARY KEY (`commentsId`)
    ) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8
    
    展开全文
  • springBoot+vue实现类QQ评论功能

    千次阅读 2018-10-08 18:47:33
    本文将讲如何使用一个评论功能。本文的评论功能,是我自己写的一个网站的中的评论,所以大家可以先看看效果,地址:http://www.toheart.xin(已被注销)。好了废话不说直接开讲。 使用技术介绍:后端使用了...
  • 回复和评论功能的实现

    千次阅读 2019-10-29 15:32:23
    帖子的回复和评论功能 有许多人在开发一些小项目的时候需要用到评论和回复功能,其实评论和回复的实现也特别简单,我做的一个项目需要用到帖子,所以我就自己写了个,以下是我自己做的帖子的评论和回复功能 * 我用的...
  • SpringBoot-实现评论功能(事务)

    千次阅读 2020-02-16 22:45:43
    实现评论功能 数据库的构建 创建评论数据表 create table tbl_comment ( id bigint auto_increment, parent_id bigint not null comment '父类id,父类可能是question,也可以同样是comment', type int not ...
  • 一小时带你手写评论功能——python+django+mysql+vue+element+axios 所需环境 前端:vue+element+axios 后端: python3.8 django3.1.4 mysql8.0 软件 (1)前端:vs code 需要插件: (2)后端:pycharm(我这里用的...
  • HTML实现评论功能

    千次阅读 2020-12-31 21:56:27
    <h3>留言版信息</h3> <ol id="olid"> <li id="tr1">昵称为“安妮”的用户说了“那个……你看见过我的小熊吗?...input id="txtName" type="text... 评论:<textarea id="txtCom"></t.
  • 小程序云开发教程七:贴子的详情及评论功能

    千次阅读 多人点赞 2018-10-08 12:06:19
    我们如果要实现评论功能, 那么需要什么参数呢? 参数如下 comment: 评论内容 username: 用户名 time: 评论时间 userId: 用户id id: 评论的贴子id avatar: 用户头像 先上代码, wxml: &amp;amp;...
  • 最近要写一个短视频直播小程序 记录一下评论功能的实现 首先接口返回部分 原先评论列表接口(第一种 返回一级分类。 第二种传一级分类id 返回二级分类数据) is_child_comment 是否展示更多评论 secondList 二级...
  • 发表评论功能的实现

    千次阅读 2018-12-10 13:16:42
    1、把文本框做双向数据绑定 2、为发表按钮绑定一个事件 3、校验评论内容是否为空,...+如果调用 getComments 方法重新刷新评论列表的话,可能只能得到 最后一页的评论,前几页的评论获取不到 +换一种思路:当...
  • 微信小程序 评论功能实现

    万次阅读 2018-09-06 21:29:56
    js: 默认展示历史评论评论后也刷新页面,连带此次评论内容一起展示。 var bindblur ; Page({ bindblur:function(e){ console.log('1111111:', e.detail.value) bindblur = e.detail.value; }, onLoad:...
  • springboot实现评论功能,分类展示

    千次阅读 2020-08-01 10:48:23
    和之前做其他功能的流程类似,新建comment实体类: @Entity @Table(name="t_comment") public class Comment { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String ...
  • Android评论功能

    千次阅读 2017-09-24 17:54:55
    Android评论功能最近有个评论功能的需求,便学习了下,做个demo练练手,话说这也开启了我自定义view学习的旅途了吧,ok,先看下效果图吧代码实现整个项目的结构图大家先看下,文尾附源码链接 布局文件xml这个主要是...
  • 评论回复功能demo

    2019-03-27 10:55:27
    通过JAVA结合js技术实现发表文章的评论回复功能,对评论和回复的功能可以删除,显示点赞数量。
  • 利用Vue+ElementUi实现评论功能-仿掘金

    万次阅读 多人点赞 2019-09-18 20:52:18
    这两天在用vue重构之前写的一个社区博客项目,之前评论的样式和效果都差强人意,所以仿照掘金重写了一个评论功能(但不是完全照搬模仿) 在写完这个功能后,由心觉得Vue真的非常好用。 话不多说,先上效果图 代码 ...
  • 用户评论功能

    千次阅读 2019-06-18 09:34:50
    在开发过程中,用户评论功能是一个很常见的功能,而我正好有开发这功能。用户评论功能的整体设计思路是这样的,用户在公告下进行评论,管理员能在后台查看所有用户的评论信息,用户与用户之间能够进行评论留言。根据...
  • uni-app实现点赞评论功能

    千次阅读 2019-11-25 14:45:31
    这篇文章主要介绍了uni-app实现点赞评论功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 模拟朋友圈实时点赞及评论功能 点赞思路...
  • hexo添加评论功能

    万次阅读 2018-01-12 20:46:39
    目前博客站点使用的评论功能,多说,网易云跟贴都已经下线。Disqus也被挡在墙外,友言貌似也不行。 可用的评论系统大概有:  - HyperComments:https://www.hypercomments.com (来自俄罗斯的评论系统,使用谷歌...
  • 基于云开发的小程序评论功能实现

    千次阅读 2019-06-09 22:19:32
    对于文章评论,新建数据库集合来保存评论内容及相关书籍,有以下数据库字段: var args={ cNickName: that.data.userInfo.nickName, cAvatarUrl: that.data.userInfo.avatarUrl, cOpenId: app.globalData....
  • SpringBoot博客系统之评论功能

    千次阅读 2020-02-05 14:12:32
    可以看到最后实现的样子是对文章直接发起的回复会在显示在第一级,之后对评论的回复都会显示在第二级(包括对回复的回复的回复) 把那个且
  • 在文章评论下面,还可以对一级评论进行评论,一级评论和二级评论的区别在于type字段的不同,一级评论为1,二级评论为2 后端设计 新建com.qianyucc.blog.model.dto.CommentDTO类,用于封装前端向后端传输的评论信息...
  • 如何在vue里面实现评论功能

    千次阅读 2020-04-30 18:39:34
    前几天做了文章的评论功能,现在用文字记录下来,希望帮助更多的人 //html <div class="bottom"> <div class="coomment" @click="show"> <i class="el-icon-chat-line-round"></i>评论 &...
  • Web网站评论功能数据库设计和开发

    万次阅读 2017-05-29 09:49:44
    本文主要分享了我在设计...现阶段评论做的最好的我想应该是网易新闻(app)里面的评论模块了,其“盖楼”的方式让人印象深刻,评论已经成为该app的核心功能之一了。市面上大部分app的评论模块设计的还是相对简单
  • 在Web系统中实现评论功能

    千次阅读 2019-09-11 16:03:45
    有一天,新Leader对我说:小伙子, 你来做一下这个备注(用户评论)模块吧。我一听,这不是在为难我胖虎吗,之前从来没做过啊。 但是转念一想,终于可以做点新东西了,心里还是有些小开心得。OK,在开始之前先先...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 323,786
精华内容 129,514
关键字:

评论功能