乐优商城(四十九)评论微服务(二)

目录

一、需求分析

1.1 功能分析

1.2 表结构分析

二、服务搭建

2.1 服务搭建

2.1.1 创建module

2.1.2 添加依赖

2.1.3 编写配置

2.1.4 启动类

2.2 准备工作

2.2.1 创建实体类

2.2.2 创建数据访问接口

2.2.3 添加路由

2.3 功能实现

2.3.1 分页查询评论

2.3.2 发布评论

2.3.3 删除评论

2.3.4 修改评论

2.3.5 根据id查询评论

2.3.6 评论点赞

2.3.7 评论访问量增加

2.4 测试

2.4.1 增加100条评论数据

2.4.2 分页查询

2.4.2 根据id查询评论

2.4.3 根据id修改评论

2.4.4 根据id删除评论

2.4.5 评论点赞

2.4.6 评论的评论

三、补充

3.1 Feign

3.2 添加相关依赖

3.3 修改


一、需求分析

1.1 功能分析

采用SpringDataMongoDB框架实现评论微服务的持久层。

实现功能:

(1)基本CRUD API

(2)评论点赞

1.2 表结构分析

字段名称

字段含义

字段类型

备注

_id

ID

文本

 

orderid 订单id 文本  

spuid

商品id

文本

 

userid

用户id

文本

 

nickname

评论人昵称

文本

 

parentid

评论id

文本

父评论id,顶级评论为0

isparent

是否是顶级评论

布尔型

 

publishtime

评论日期

日期

 

visits

浏览量

整型

 

thumbup

点赞数

整型

 

images

评论中的图片

数组

 

content

评论内容

文本

 

comment

回复数

整型

 

iscomment

是否允许回复

布尔型

 

type

评价类型

整型

0:非常差

1:差

2:一般

3:好

4:非常好

-1:表示评论的回复

二、服务搭建

2.1 服务搭建

2.1.1 创建module

 

乐优商城(四十九)评论微服务(二)_第1张图片

子模块interface:

乐优商城(四十九)评论微服务(二)_第2张图片

 

子模块service:

乐优商城(四十九)评论微服务(二)_第3张图片

 

 

目录结构:

乐优商城(四十九)评论微服务(二)_第4张图片

2.1.2 添加依赖

在leyou-comments-service中添加依赖



    
        
        
        
    
    
        org.springframework.boot
        spring-boot-starter-parent
        2.0.4.RELEASE
         
    

    4.0.0

    com.leyou.comments
    leyou-comments-service


    
        
            org.springframework.boot
            spring-boot-starter-web
            2.0.4.RELEASE
        

        
            org.springframework.boot
            spring-boot-starter-data-mongodb
            2.1.0.RELEASE
        

        
            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-client
            2.0.2.RELEASE
        
        
        
            org.springframework.cloud
            spring-cloud-starter-config
            2.0.2.RELEASE
        

        
            org.springframework.cloud
            spring-cloud-bus
            2.0.0.RELEASE
        

        
            org.springframework.cloud
            spring-cloud-stream-binder-rabbit
            2.0.0.RELEASE
        

        
            org.springframework.boot
            spring-boot-starter-actuator
            2.0.0.RELEASE
        
        
            com.leyou.authentication
            leyou-authentication-common
            1.0.0-SNAPSHOT
            compile
        
        
            com.leyou.authentication
            leyou-authentication-common
            1.0.0-SNAPSHOT
            compile
        
        
            com.leyou.common
            leyou-common
            1.0.0-SNAPSHOT
            compile
        
        
            com.leyou.review
            leyou-review-interface
            1.0.0-SNAPSHOT
            compile
        
        
            com.leyou.comments
            leyou-comments-interface
            1.0.0-SNAPSHOT
            compile
        
    

在这里面需要说明一下,spring boot在整合mongodb的时候,其配置文件中的parent属性必须是spring boot的依赖:

乐优商城(四十九)评论微服务(二)_第5张图片

否则在继承MongoRepository时无法自动加载。这样就破坏了整个工程结构,这个坑怎么填?请大佬赐教!!!!!!!!!

2.1.3 编写配置

server:
  port: 8092
  tomcat:
    max-connections: 5000
spring:
  application:
    name: comments-service
  data:
    mongodb:
      host: 192.168.19.121
      database: spitdb
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/leyou?characterEncoding=UTF-8&useSSL=false
    username: root
    password: 123456
    hikari:
      maximum-pool-size: 20
      minimum-idle: 10
  rabbitmq:
    virtual-host: /leyou
    username: /leyou
    password: leyou
    host: 192.168.19.121
  redis:
    host: 192.168.19.121
  jackson:
    default-property-inclusion: non_null # 配置json处理时忽略空值
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
    registry-fetch-interval-seconds: 5
  instance:
    instance-id: ${spring.application.name}:${server.port}
    prefer-ip-address: true  #当你获取host时,返回的不是主机名,而是ip
    ip-address: 127.0.0.1
    lease-expiration-duration-in-seconds: 10 #10秒不发送九过期
    lease-renewal-interval-in-seconds: 5 #每隔5秒发一次心跳
ribbon:
  ConnectTimeout: 4000 # 连接超时时间(ms)
  ReadTimeout: 2000 # 通信超时时间(ms)
  OkToRetryOnAllOperations: true # 是否对所有操作重试
  MaxAutoRetriesNextServer: 1 # 同一服务不同实例的重试次数
  MaxAutoRetries: 1 # 同一实例的重试次数

leyou:
  worker:
    workerId: 2
    dataCenterId: 2
  jwt:
    cookieName: LY_TOKEN
    pubKeyPath: G:\\tmp\\rsa\\rsa.pub # 公钥地址

2.1.4 启动类

package com.leyou.comments;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * @Author: 98050
 * @Time: 2018-11-29 15:41
 * @Feature:
 */
@SpringBootApplication
@EnableDiscoveryClient
public class LyCommentsApplication {
    public static void main(String[] args) {
        SpringApplication.run(LyCommentsApplication.class,args);
    }
}

2.2 准备工作

2.2.1 创建实体类

package com.leyou.comments.pojo;

import javax.persistence.Id;
import java.io.Serializable;
import java.util.Date;
import java.util.List;

/**
 * @Author: 98050
 * @Time: 2018-11-26 14:45
 * @Feature:
 */
public class Review implements Serializable {

    @Id
    private String _id;

    /**
     * 订单id
     */
    private String orderid;
    /**
     * 商品id
     */
    private String spuid;
    /**
     * 评论内容
     */
    private String content;
    /**
     * 评论时间
     */
    private Date publishtime;
    /**
     * 评论用户id
     */
    private String userid;
    /**
     * 评论用户昵称
     */
    private String nickname;
    /**
     * 评论的浏览量
     */
    private Integer visits;
    /**
     * 评论的点赞数
     */
    private Integer thumbup;
    /**
     * 评论中的图片
     */
    private List images;
    /**
     * 评论的回复数
     */
    private Integer comment;
    /**
     * 该评论是否可以被回复
     */
    private Boolean iscomment;
    /**
     * 该评论的上一级id
     */
    private String parentid;
    /**
     * 是否是顶级评论
     */
    private Boolean isparent;
    /**
     * 评论的类型
     */
    private Integer type;

    /**
     * json转换需要
     */
    public Review() {
    }

    public Review(String orderid,String spuid, String content, String userid, String nickname, List images, Boolean iscomment, String parentid, Boolean isparent, Integer type) {
        this.orderid = orderid;
        this.spuid = spuid;
        this.content = content;
        this.userid = userid;
        this.nickname = nickname;
        this.images = images;
        this.iscomment = iscomment;
        this.parentid = parentid;
        this.isparent = isparent;
        this.type = type;
    }
}

2.2.2 创建数据访问接口

package com.leyou.comments.dao;


import com.leyou.comments.pojo.Review;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.repository.MongoRepository;


/**
 * @Author: 98050
 * @Time: 2018-11-26 20:51
 * @Feature:
 */
public interface CommentDao extends MongoRepository {

}

2.2.3 添加路由

在网关中添加路由

乐优商城(四十九)评论微服务(二)_第6张图片

2.3 功能实现

2.3.1 分页查询评论

Controller

请求方式:GET

请求路径:/list

请求参数:商品id、当前页码、页面大小,封装一个请求参数对象:

返回结果:返回评论的分页信息

请求对象,每页大小20,固定参数两个:spuId、page。其中构造函数是用来接收前端传进来的数据,其余都在后端生成。

package com.leyou.comments.bo;

/**
 * @Author: 98050
 * @Time: 2018-11-26 21:40
 * @Feature:
 */
public class CommentRequestParam {
    /**
     * 商品id
     */
    private Long spuId;

    /**
     * 当前页码
     */
    private Integer page;

    /**
     * 每页大小,不从页面接收,而是固定大小
     */
    private static final Integer DEFAULT_SIZE = 20;

    /**
     * 默认页
     */
    private static final Integer DEFAULT_PAGE = 1;

    public Long getSpuId() {
        return spuId;
    }

    public void setSpuId(Long spuId) {
        this.spuId = spuId;
    }

    public Integer getPage() {
        if (page == null){
            return DEFAULT_PAGE;
        }
        /**
         * 获取页码时做一些校验,不能小于1
         */
        return Math.max(DEFAULT_PAGE,page);
    }

    public void setPage(Integer page) {
        this.page = page;
    }

    public Integer getDefaultSize() {
        return DEFAULT_SIZE;
    }

}
package com.leyou.comments.controller;

import com.leyou.auth.entity.UserInfo;
import com.leyou.comments.bo.CommentRequestParam;
import com.leyou.comments.interceptor.LoginInterceptor;
import com.leyou.comments.pojo.Review;
import com.leyou.comments.service.CommentService;
import com.leyou.common.pojo.PageResult;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.domain.Page;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

/**
 * @Author: 98050
 * @Time: 2018-11-26 21:30
 * @Feature:
 */
@RequestMapping
@RestController
public class CommentController {

    @Autowired
    private CommentService commentService;

    /**
     * 分页查询某一商品下的所有顶级评论
     * @param requestParam
     * @return
     */
    @GetMapping("list")
    public ResponseEntity findReviewBySpuId(@RequestBody CommentRequestParam requestParam){
        Page result = commentService.findReviewBySpuId(requestParam);
        if (result == null){
            return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
        }
        PageResult pageResult = new PageResult();
        pageResult.setTotal(result.getTotalElements());
        pageResult.setItems(result.getContent());
        pageResult.setTotalPage((long)result.getTotalPages());
        return ResponseEntity.ok(pageResult);
    }
}

Service

  • 接口
package com.leyou.comments.service;

import com.leyou.comments.bo.CommentRequestParam;
import com.leyou.comments.pojo.Review;
import org.springframework.data.domain.Page;


/**
 * @Author: 98050
 * @Time: 2018-11-26 15:40
 * @Feature:
 */
public interface CommentService {

    /**
     * 查询某一商品下的所有顶级评论
     * @param commentRequestParam
     * @return
     */
    Page findReviewBySpuId(CommentRequestParam commentRequestParam);

}
  • 实现
package com.leyou.comments.service.impl;
import com.leyou.comments.bo.CommentRequestParam;
import com.leyou.comments.dao.CommentDao;
import com.leyou.comments.pojo.Review;
import com.leyou.comments.service.CommentService;
import com.leyou.utils.IdWorker;
import com.mongodb.client.result.UpdateResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import org.springframework.stereotype.Service;

import java.util.Date;

/**
 * @Author: 98050
 * @Time: 2018-11-26 15:41
 * @Feature:
 */
@Service
public class CommentServiceImpl implements CommentService {

    @Autowired
    private CommentDao commentDao;

    @Autowired
    private MongoTemplate mongoTemplate;


    /**
     * 查询某一商品下的所有顶级评论
     * @param commentRequestParam
     * @return
     */
    @Override
    public Page findReviewBySpuId(CommentRequestParam commentRequestParam) {
        PageRequest pageRequest = PageRequest.of(commentRequestParam.getPage()-1, commentRequestParam.getDefaultSize());
        return this.commentDao.findReviewBySpuid(commentRequestParam.getSpuId()+"",pageRequest);
    }

   
}

Dao

在CommentDao中添加分页查询的方法:

public interface CommentDao extends MongoRepository {

    /**
     * 分页查询
     * @param spuId
     * @param pageable
     * @return
     */
    Page findReviewBySpuid(String spuId, Pageable pageable);
}

2.3.2 发布评论

Controller

请求方式:POST

请求路径:/

请求参数:review对象

返回结果:201

每个用户对每一个订单只能发表一次顶级评论,可以追评论。

    /**
     * 增加评论
     * @param review
     * @return
     */
    @PostMapping
    public ResponseEntity addReview(@RequestBody Review review){
        boolean result = this.commentService.add(review);
        if (!result){
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
        }
        return ResponseEntity.status(HttpStatus.CREATED).build();
    }

Service

添加评论有两种情况,第一种是为商品添加评论,第二种是评论的回复。在这里就体现出表结构设计的技巧,同理参考商品分类管理中的表结构设计。

这里面评论的id需要自己指定,所以要使用IdWorker,参照以前的模块,cv一下

/**
     * 新增
     * 注意:一个用户只能发表一个顶级评论,可以追评(在一个订单中)
     * @param review
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean add(Long orderId,Review review) {
        //1.检查用户是否在该商品下发表过顶级评论过
        if (review.getIsparent()) {
            Query query2 = new Query();
            query2.addCriteria(Criteria.where("userid").is(review.getUserid()));
            query2.addCriteria(Criteria.where("orderid").is(review.getOrderid()));
            query2.addCriteria(Criteria.where("spuid").is(review.getSpuid()));
            List old = this.mongoTemplate.find(query2, Review.class);
            if (old.size() > 0 && old.get(0).getIsparent()) {
                return false;
            }
        }
        //2.修改订单状态,6代表评价状态
        boolean result = this.orderClient.updateOrderStatus(orderId, 6).getBody();
        if (!result){
            return false;
        }
        //3.添加评论
        /**
         * 设置主键
         */
        review.set_id(idWorker.nextId() + "");
        review.setPublishtime(new Date());
        review.setComment(0);
        review.setThumbup(0);
        review.setVisits(0);
        if (review.getParentid() != null && !"".equals(review.getParentid())){
            //如果存在上级id,则上级评论数加1,将上级评论的isParent设置为true,浏览量加一
            Query query = new Query();
            query.addCriteria(Criteria.where("_id").is(review.getParentid()));
            Update update = new Update();
            update.inc("comment",1);
            update.set("isparent",true);
            update.inc("visits",1);
            this.mongoTemplate.updateFirst(query,update,"review");
        }
        commentDao.save(review);
        return true;
    }

2.3.3 删除评论

Controller

请求方式:DELETE

请求路径:/commentId/{id}

请求参数:评论id

返回结果:200

    /**
     * 根据评论id删除评论
     * @param id
     * @return
     */
    @DeleteMapping("/commentId/{id}")
    public ResponseEntity deleteReview(@PathVariable("id") String id){
        this.commentService.deleteById(id);
        return ResponseEntity.status(HttpStatus.OK).build();
    }

Service

    /**
     * 删除
     *
     * @param id
     */
    @Override
    public void deleteById(String id) {
        commentDao.deleteById(id);
    }

2.3.4 修改评论

Controller

请求方式:PUT

请求路径:/comment

请求参数:review对象

返回结果:200

    /**
     * 修改评论
     * @param review
     * @return
     */
    @PutMapping("/comment")
    public ResponseEntity updateReview(@RequestBody Review review){
        this.commentService.update(review);
        return ResponseEntity.status(HttpStatus.OK).build();
    }

Service

    /**
     * 修改
     *
     * @param review
     */
    @Override
    public void update(Review review) {
        commentDao.save(review);
    }

2.3.5 根据id查询评论

Controller

请求方式:GET

请求路径:/commentId/{id}

请求参数:评论id

返回结果:review对象

    /**
     * 根据评论id查询评论
     * @param id
     * @return
     */
    @GetMapping("/commentId/{id}")
    public ResponseEntity findReviewById(@PathVariable("id") String id){
        Review review = this.commentService.findOne(id);
        if (review == null){
            return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
        }
        return ResponseEntity.ok(review);
    }

Service

    @Override
    public Review findOne(String id) {
        //判断空
        Optional optional = commentDao.findById(id);
        return optional.orElse(null);
    }

2.3.6 评论点赞

Controller

请求方式:PUT

请求路径:thumb/{id}

请求参数:评论id

返回结果:true or false

点赞就是让字段thumbup加一,使用MongoTemplate类来实现对某列的操作,不用全部都查询出来,那样效率不高。使用自增函数inc实现thumbup加一。

使用redis控制不能重复点赞

   private final String THUMBUP_PREFIX = "thumbup";
    /**
     * 评论点赞
     * @param id
     * @return
     */
    @PutMapping("thumb/{id}")
    public ResponseEntity updateThumbup(@PathVariable String id){

        //1.首先判断当前用户是否点过赞
        UserInfo userInfo = LoginInterceptor.getLoginUser();
        String userId = userInfo.getId()+"";
        if (redisTemplate.opsForValue().get(THUMBUP_PREFIX + userId + "_" + id) != null){
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
        }
        boolean result = this.commentService.updateThumbup(id);
        if (!result){
           return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
        }
        redisTemplate.opsForValue().set(THUMBUP_PREFIX + userId + "_" + id,"1");
        return ResponseEntity.ok(result);
    }

Service

    /**
     * 评论点赞
     * @param id
     */
    @Override
    public boolean updateThumbup(String id) {
        Query query = new Query();
        query.addCriteria(Criteria.where("_id").is(id));
        Update update = new Update();
        update.inc("thumbup",1);
        UpdateResult result = this.mongoTemplate.updateFirst(query,update,"review");
        return result.isModifiedCountAvailable();
    }

2.3.7 评论访问量增加

这个功能和评论点赞差不多,但是具体怎么判断用户浏览过? 好吧,我也不知道,这个功能就当测试了。。。。。。。

Controller

请求方式:PUT

请求路径:/visit/{id}

请求参数:评论id

返回结果:200

    /**
     * 根据评论id访问量加1
     * @param id
     * @return
     */
    @PutMapping("visit/{id}")
    public ResponseEntity updateReviewVisit(@PathVariable("id") String id){
        boolean result = this.commentService.updateVisits(id);
        if (!result){
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
        }
        return ResponseEntity.status(HttpStatus.OK).build();
    }

Service

    /**
     * 访问量加一
     * @param id
     */
    @Override
    public boolean updateVisits(String id) {
        Query query = new Query();
        query.addCriteria(Criteria.where("_id").is(id));
        Update update = new Update();
        update.inc("visits",1);
        UpdateResult result = this.mongoTemplate.updateFirst(query,update,"review");
        return result.isModifiedCountAvailable();
    }

2.4 测试

2.4.1 增加100条评论数据

创建测试类:

import com.leyou.comments.LyCommentsApplication;
import com.leyou.comments.dao.CommentDao;
import com.leyou.comments.pojo.Review;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.ArrayList;
import java.util.List;

/**
 * @Author: 98050
 * @Time: 2018-12-09 20:37
 * @Feature:
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = LyCommentsApplication.class)
public class CommentsTest {

    @Autowired
    private CommentDao commentDao;

    /**
     * 为spuId为2的商品添加100条评顶级论数据
     */
    @Test
    public void LoadData(){
        for (int i = 0; i < 100; i++) {
            String spuId = "2";
            String content = "手机不错"+i;
            String userId = (35 + i) + "";
            String nickname = "username"+i;
            List images = new ArrayList<>();
            boolean iscomment = i % 2 == 0;
            String parentId = 0 + "";
            boolean isparent = true;
            int type = i % 5;
            Review review = new Review(spuId, content, userId, nickname, images, iscomment, parentId,isparent,type);
            commentDao.insert(review);
        }
    }
}

同一用户再次发布评论:

 乐优商城(四十九)评论微服务(二)_第7张图片

2.4.2 分页查询

乐优商城(四十九)评论微服务(二)_第8张图片

结果:

乐优商城(四十九)评论微服务(二)_第9张图片

2.4.2 根据id查询评论

乐优商城(四十九)评论微服务(二)_第10张图片

2.4.3 根据id修改评论

修改id为1071767094657556480的评论

乐优商城(四十九)评论微服务(二)_第11张图片

重新查询:

乐优商城(四十九)评论微服务(二)_第12张图片

2.4.4 根据id删除评论

删除id为1071767094657556480的评论:

 重新查询该评论:

2.4.5 评论点赞

乐优商城(四十九)评论微服务(二)_第13张图片

要添加cookie。

查询id为 1071767095416725504的评论:

乐优商城(四十九)评论微服务(二)_第14张图片

2.4.6 评论的评论

这个没什么特别,在前端进行数据封装就可以了:

乐优商城(四十九)评论微服务(二)_第15张图片

查看id为1071767095433502720的评论:

乐优商城(四十九)评论微服务(二)_第16张图片

三、补充

在添加评论的时候,还有一个需要做的工作就是修改订单的状态,为了测试方便,上面就没有给出,熟悉完mongodb后,再把这个坑填上。

3.1 Feign

在leyou-order微服务的接口中新增修改订单状态的方法:

乐优商城(四十九)评论微服务(二)_第17张图片

 在评论微服务中添加FeignClient:

package com.leyou.comments.client;

import com.leyou.order.api.OrderApi;
import org.springframework.cloud.openfeign.FeignClient;

/**
 * @Author: 98050
 * @Time: 2018-11-12 15:19
 * @Feature: 订单接口
 */
@FeignClient(value = "order-service")
public interface OrderClient extends OrderApi {
}

3.2 添加相关依赖

        
            com.leyou.order
            leyou-order-interface
            1.0.0-SNAPSHOT
            compile
        

        
            org.springframework.cloud
            spring-cloud-openfeign-core
            2.0.2.RELEASE
            compile
        

3.3 修改

乐优商城(四十九)评论微服务(二)_第18张图片

乐优商城(四十九)评论微服务(二)_第19张图片

 

注意:review对象中的orderid是后期加上去的,当时考虑不周,但是整体没有多大影响,在测试时添加100条数据的时候加上这个字段就可以了。

你可能感兴趣的:(乐优商城)