亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

評(píng)論模塊優(yōu)化 - 數(shù)據(jù)表優(yōu)化、添加緩存及用 Feign 與用戶服務(wù)通信

iflove / 3102人閱讀

摘要:前段時(shí)間設(shè)計(jì)了系統(tǒng)的評(píng)論模塊,并寫了篇文章評(píng)論模塊后端數(shù)據(jù)庫(kù)設(shè)計(jì)及功能實(shí)現(xiàn)講解。下面就對(duì)評(píng)論模塊進(jìn)行優(yōu)化改造,首先更改表結(jié)構(gòu),合成一張表。評(píng)論表不存用戶頭像的話,需要從用戶服務(wù)獲取。用戶服務(wù)提供獲取頭像的接口,兩個(gè)服務(wù)間通過(guò)通信。

前段時(shí)間設(shè)計(jì)了系統(tǒng)的評(píng)論模塊,并寫了篇文章 評(píng)論模塊 - 后端數(shù)據(jù)庫(kù)設(shè)計(jì)及功能實(shí)現(xiàn) 講解。

大佬們?cè)谠u(píng)論區(qū)提出了些優(yōu)化建議,總結(jié)一下:

之前評(píng)論一共分了兩張表,一個(gè)評(píng)論主表,一個(gè)回復(fù)表。這兩張表的字段區(qū)別不大,在主表上加個(gè) pid 字段就可以不用回復(fù)表合成一張表了。

評(píng)論表中存了用戶頭像,會(huì)引發(fā)一些問(wèn)題。比如用戶換頭像時(shí)要把評(píng)論也一起更新不太合適,還可能出現(xiàn)兩條評(píng)論頭像不一致的情況。

的確數(shù)據(jù)庫(kù)設(shè)計(jì)的有問(wèn)題,感謝 wangbjun 和 JWang。

下面就對(duì)評(píng)論模塊進(jìn)行優(yōu)化改造,首先更改表結(jié)構(gòu),合成一張表。評(píng)論表不存用戶頭像的話,需要從用戶服務(wù)獲取。用戶服務(wù)提供獲取頭像的接口,兩個(gè)服務(wù)間通過(guò) Feign 通信。

這樣有個(gè)問(wèn)題,如果一個(gè)資源的評(píng)論比較多,每個(gè)評(píng)論都調(diào)用用戶服務(wù)查詢頭像還是有點(diǎn)慢,所以對(duì)評(píng)論查詢加個(gè) Redis 緩存。要是有新的評(píng)論,就把這個(gè)資源緩存的評(píng)論刪除,下次請(qǐng)求時(shí)重新讀數(shù)據(jù)庫(kù)并將最新的數(shù)據(jù)緩存到 Redis 中。

代碼出自開(kāi)源項(xiàng)目 coderiver,致力于打造全平臺(tái)型全棧精品開(kāi)源項(xiàng)目。  
項(xiàng)目地址:https://github.com/cachecats/...

本文將分四部分介紹

數(shù)據(jù)庫(kù)改造

用戶服務(wù)提供獲取頭像接口

評(píng)論服務(wù)用 Feign 訪問(wèn)用戶服務(wù)取頭像

使用 Redis 緩存數(shù)據(jù)

一、數(shù)據(jù)庫(kù)改造

數(shù)據(jù)庫(kù)表重新設(shè)計(jì)如下

CREATE TABLE `comments_info` (
  `id` varchar(32) NOT NULL COMMENT "評(píng)論主鍵id",
  `pid` varchar(32) DEFAULT "" COMMENT "父評(píng)論id",
  `owner_id` varchar(32) NOT NULL COMMENT "被評(píng)論的資源id,可以是人、項(xiàng)目、資源",
  `type` tinyint(1) NOT NULL COMMENT "評(píng)論類型:對(duì)人評(píng)論,對(duì)項(xiàng)目評(píng)論,對(duì)資源評(píng)論",
  `from_id` varchar(32) NOT NULL COMMENT "評(píng)論者id",
  `from_name` varchar(32) NOT NULL COMMENT "評(píng)論者名字",
  `to_id` varchar(32) DEFAULT "" COMMENT "被評(píng)論者id",
  `to_name` varchar(32) DEFAULT "" COMMENT "被評(píng)論者名字",
  `like_num` int(11) DEFAULT "0" COMMENT "點(diǎn)贊的數(shù)量",
  `content` varchar(512) DEFAULT NULL COMMENT "評(píng)論內(nèi)容",
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT "創(chuàng)建時(shí)間",
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT "修改時(shí)間",
  PRIMARY KEY (`id`),
  KEY `owner_id` (`owner_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT="評(píng)論表";

相比之前添加了父評(píng)論id pid ,去掉了用戶頭像。owner_id 是被評(píng)論的資源id,比如一個(gè)項(xiàng)目下的所有評(píng)論的 owner_id 都是一樣的,便于根據(jù)資源 id 查找該資源下的所有評(píng)論。

與數(shù)據(jù)表對(duì)應(yīng)的實(shí)體類 CommentsInfo

package com.solo.coderiver.comments.dataobject;

import lombok.Data;
import org.hibernate.annotations.DynamicUpdate;

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

/**
 * 評(píng)論表主表
 */
@Entity
@Data
@DynamicUpdate
public class CommentsInfo implements Serializable{

    private static final long serialVersionUID = -4568928073579442976L;

    //評(píng)論主鍵id
    @Id
    private String id;

    //該條評(píng)論的父評(píng)論id
    private String pid;

    //評(píng)論的資源id。標(biāo)記這條評(píng)論是屬于哪個(gè)資源的。資源可以是人、項(xiàng)目、設(shè)計(jì)資源
    private String ownerId;

    //評(píng)論類型。1用戶評(píng)論,2項(xiàng)目評(píng)論,3資源評(píng)論
    private Integer type;

    //評(píng)論者id
    private String fromId;

    //評(píng)論者名字
    private String fromName;

    //被評(píng)論者id
    private String toId;

    //被評(píng)論者名字
    private String toName;

    //獲得點(diǎn)贊的數(shù)量
    private Integer likeNum;

    //評(píng)論內(nèi)容
    private String content;

    //創(chuàng)建時(shí)間
    private Date createTime;

    //更新時(shí)間
    private Date updateTime;
}

數(shù)據(jù)傳輸對(duì)象 CommentsInfoDTO

在 DTO 對(duì)象中添加了用戶頭像,和子評(píng)論列表 children,因?yàn)榉到o前端要有層級(jí)嵌套。

package com.solo.coderiver.comments.dto;

import lombok.Data;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

@Data
public class CommentsInfoDTO implements Serializable {

    private static final long serialVersionUID = -6788130126931979110L;

    //評(píng)論主鍵id
    private String id;

    //該條評(píng)論的父評(píng)論id
    private String pid;

    //評(píng)論的資源id。標(biāo)記這條評(píng)論是屬于哪個(gè)資源的。資源可以是人、項(xiàng)目、設(shè)計(jì)資源
    private String ownerId;

    //評(píng)論類型。1用戶評(píng)論,2項(xiàng)目評(píng)論,3資源評(píng)論
    private Integer type;

    //評(píng)論者id
    private String fromId;

    //評(píng)論者名字
    private String fromName;

    //評(píng)論者頭像
    private String fromAvatar;

    //被評(píng)論者id
    private String toId;

    //被評(píng)論者名字
    private String toName;

    //被評(píng)論者頭像
    private String toAvatar;

    //獲得點(diǎn)贊的數(shù)量
    private Integer likeNum;

    //評(píng)論內(nèi)容
    private String content;

    //創(chuàng)建時(shí)間
    private Date createTime;

    //更新時(shí)間
    private Date updateTime;

    private List children;

}
二、用戶服務(wù)提供獲取頭像接口

為了方便理解先看一下項(xiàng)目的結(jié)構(gòu),本項(xiàng)目中所有的服務(wù)都是這種結(jié)構(gòu)

每個(gè)服務(wù)都分為三個(gè) Module,分別是 client , common , server

client :為其他服務(wù)提供數(shù)據(jù),F(xiàn)eign 的接口就寫在這層。

common :放 clientserver 公用的代碼,比如公用的對(duì)象、工具類。

server : 主要的邏輯代碼。

clientpom.xml 中引入 Feign 的依賴


    org.springframework.cloud
    spring-cloud-starter-openfeign
 

用戶服務(wù) user 需要對(duì)外暴露獲取用戶頭像的接口,以使評(píng)論服務(wù)通過(guò) Feign 調(diào)用。

user_service 項(xiàng)目的 server 下新建 ClientController , 提供獲取頭像的接口。

package com.solo.coderiver.user.controller;

import com.solo.coderiver.user.common.UserInfoForComments;
import com.solo.coderiver.user.dataobject.UserInfo;
import com.solo.coderiver.user.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * 對(duì)其他服務(wù)提供數(shù)據(jù)的 controller
 */
@RestController
@Slf4j
public class ClientController {

    @Autowired
    UserService userService;

    /**
     * 通過(guò) userId 獲取用戶頭像
     *
     * @param userId
     * @return
     */
    @GetMapping("/get-avatar")
    public UserInfoForComments getAvatarByUserId(@RequestParam("userId") String userId) {
        UserInfo info = userService.findById(userId);
        if (info == null){
            return null;
        }
        return new UserInfoForComments(info.getId(), info.getAvatar());
    }
}

然后在 client 定義 UserClient 接口

package com.solo.coderiver.user.client;

import com.solo.coderiver.user.common.UserInfoForComments;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(name = "user")
public interface UserClient {

    @GetMapping("/user/get-avatar")
    UserInfoForComments getAvatarByUserId(@RequestParam("userId") String userId);
}
三、評(píng)論服務(wù)用 Feign 訪問(wèn)用戶服務(wù)取頭像

在評(píng)論服務(wù)的 server 層的 pom.xml 里添加 Feign 依賴


    org.springframework.cloud
    spring-cloud-starter-openfeign
 

并在入口類添加注解 @EnableFeignClients(basePackages = "com.solo.coderiver.user.client") 注意到配置掃描包的全類名

package com.solo.coderiver.comments;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@SpringBootApplication
@EnableDiscoveryClient
@EnableSwagger2
@EnableFeignClients(basePackages = "com.solo.coderiver.user.client")
@EnableCaching
public class CommentsApplication {

    public static void main(String[] args) {
        SpringApplication.run(CommentsApplication.class, args);
    }
}

封裝 CommentsInfoService ,提供保存評(píng)論和獲取評(píng)論的接口

package com.solo.coderiver.comments.service;

import com.solo.coderiver.comments.dto.CommentsInfoDTO;

import java.util.List;

public interface CommentsInfoService {

    /**
     * 保存評(píng)論
     *
     * @param info
     * @return
     */
    CommentsInfoDTO save(CommentsInfoDTO info);

    /**
     * 根據(jù)被評(píng)論的資源id查詢?cè)u(píng)論列表
     *
     * @param ownerId
     * @return
     */
    List findByOwnerId(String ownerId);
}

CommentsInfoService 的實(shí)現(xiàn)類

package com.solo.coderiver.comments.service.impl;

import com.solo.coderiver.comments.converter.CommentsConverter;
import com.solo.coderiver.comments.dataobject.CommentsInfo;
import com.solo.coderiver.comments.dto.CommentsInfoDTO;
import com.solo.coderiver.comments.repository.CommentsInfoRepository;
import com.solo.coderiver.comments.service.CommentsInfoService;
import com.solo.coderiver.user.client.UserClient;
import com.solo.coderiver.user.common.UserInfoForComments;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class CommentsInfoServiceImpl implements CommentsInfoService {

    @Autowired
    CommentsInfoRepository repository;

    @Autowired
    UserClient userClient;

    @Override
    @CacheEvict(cacheNames = "comments", key = "#dto.ownerId")
    public CommentsInfoDTO save(CommentsInfoDTO dto) {
        CommentsInfo result = repository.save(CommentsConverter.DTO2Info(dto));
        return CommentsConverter.info2DTO(result);
    }

    @Override
    @Cacheable(cacheNames = "comments", key = "#ownerId")
    public List findByOwnerId(String ownerId) {
        List infoList = repository.findByOwnerId(ownerId);
        List list = CommentsConverter.infos2DTOList(infoList)
                .stream()
                .map(dto -> {
                    //從用戶服務(wù)取評(píng)論者頭像
                    UserInfoForComments fromUser = userClient.getAvatarByUserId(dto.getFromId());
                    if (fromUser != null) {
                        dto.setFromAvatar(fromUser.getAvatar());
                    }

                    //從用戶服務(wù)取被評(píng)論者頭像
                    String toId = dto.getToId();
                    if (!StringUtils.isEmpty(toId)) {
                        UserInfoForComments toUser = userClient.getAvatarByUserId(toId);
                        if (toUser != null) {
                            dto.setToAvatar(toUser.getAvatar());
                        }
                    }
                    return dto;
                }).collect(Collectors.toList());
        return sortData(list);
    }

    /**
     * 將無(wú)序的數(shù)據(jù)整理成有層級(jí)關(guān)系的數(shù)據(jù)
     *
     * @param dtos
     * @return
     */
    private List sortData(List dtos) {
        List list = new ArrayList<>();
        for (int i = 0; i < dtos.size(); i++) {
            CommentsInfoDTO dto1 = dtos.get(i);
            List children = new ArrayList<>();
            for (int j = 0; j < dtos.size(); j++) {
                CommentsInfoDTO dto2 = dtos.get(j);
                if (dto2.getPid() == null) {
                    continue;
                }
                if (dto1.getId().equals(dto2.getPid())) {
                    children.add(dto2);
                }
            }
            dto1.setChildren(children);
            //最外層的數(shù)據(jù)只添加 pid 為空的評(píng)論,其他評(píng)論在父評(píng)論的 children 下
            if (dto1.getPid() == null || StringUtils.isEmpty(dto1.getPid())) {
                list.add(dto1);
            }
        }
        return list;
    }
}

從數(shù)據(jù)庫(kù)取出來(lái)的評(píng)論是無(wú)序的,為了方便前端展示,需要對(duì)評(píng)論按層級(jí)排序,子評(píng)論在父評(píng)論的 children 字段中。

返回的數(shù)據(jù):

{
  "code": 0,
  "msg": "success",
  "data": [
    {
      "id": "1542338175424142145",
      "pid": null,
      "ownerId": "1541062468073593543",
      "type": 1,
      "fromId": "555555",
      "fromName": "張揚(yáng)",
      "fromAvatar": null,
      "toId": null,
      "toName": null,
      "toAvatar": null,
      "likeNum": 0,
      "content": "你好呀",
      "createTime": "2018-11-16T03:16:15.000+0000",
      "updateTime": "2018-11-16T03:16:15.000+0000",
      "children": []
    },
    {
      "id": "1542338522933315867",
      "pid": null,
      "ownerId": "1541062468073593543",
      "type": 1,
      "fromId": "555555",
      "fromName": "張揚(yáng)",
      "fromAvatar": null,
      "toId": null,
      "toName": null,
      "toAvatar": null,
      "likeNum": 0,
      "content": "你好呀嘿嘿",
      "createTime": "2018-11-16T03:22:03.000+0000",
      "updateTime": "2018-11-16T03:22:03.000+0000",
      "children": []
    },
    {
      "id": "abc123",
      "pid": null,
      "ownerId": "1541062468073593543",
      "type": 1,
      "fromId": "333333",
      "fromName": "王五",
      "fromAvatar": "http://avatar.png",
      "toId": null,
      "toName": null,
      "toAvatar": null,
      "likeNum": 3,
      "content": "這個(gè)小伙子不錯(cuò)",
      "createTime": "2018-11-15T06:06:10.000+0000",
      "updateTime": "2018-11-15T06:06:10.000+0000",
      "children": [
        {
          "id": "abc456",
          "pid": "abc123",
          "ownerId": "1541062468073593543",
          "type": 1,
          "fromId": "222222",
          "fromName": "李四",
          "fromAvatar": "http://222.png",
          "toId": "abc123",
          "toName": "王五",
          "toAvatar": null,
          "likeNum": 2,
          "content": "這個(gè)小伙子不錯(cuò)啊啊啊啊啊",
          "createTime": "2018-11-15T06:08:18.000+0000",
          "updateTime": "2018-11-15T06:36:47.000+0000",
          "children": []
        }
      ]
    }
  ]
}
四、使用 Redis 緩存數(shù)據(jù)

其實(shí)緩存已經(jīng)在上面的代碼中做過(guò)了,兩個(gè)方法上的

@Cacheable(cacheNames = "comments", key = "#ownerId")
@CacheEvict(cacheNames = "comments", key = "#dto.ownerId")

兩個(gè)注解就搞定了。第一次請(qǐng)求接口會(huì)走方法體

關(guān)于 Redis 的使用方法,我專門寫了篇文章介紹,就不在這里多說(shuō)了,需要的可以看看這篇文章:

Redis詳解 - SpringBoot整合Redis,RedisTemplate和注解兩種方式的使用

以上就是對(duì)評(píng)論模塊的優(yōu)化,歡迎大佬們提優(yōu)化建議~

代碼出自開(kāi)源項(xiàng)目 coderiver,致力于打造全平臺(tái)型全棧精品開(kāi)源項(xiàng)目。

coderiver 中文名 河碼,是一個(gè)為程序員和設(shè)計(jì)師提供項(xiàng)目協(xié)作的平臺(tái)。無(wú)論你是前端、后端、移動(dòng)端開(kāi)發(fā)人員,或是設(shè)計(jì)師、產(chǎn)品經(jīng)理,都可以在平臺(tái)上發(fā)布項(xiàng)目,與志同道合的小伙伴一起協(xié)作完成項(xiàng)目。

coderiver河碼 類似程序員客棧,但主要目的是方便各細(xì)分領(lǐng)域人才之間技術(shù)交流,共同成長(zhǎng),多人協(xié)作完成項(xiàng)目。暫不涉及金錢交易。

計(jì)劃做成包含 pc端(Vue、React)、移動(dòng)H5(Vue、React)、ReactNative混合開(kāi)發(fā)、Android原生、微信小程序、java后端的全平臺(tái)型全棧項(xiàng)目,歡迎關(guān)注。

項(xiàng)目地址:https://github.com/cachecats/...

您的鼓勵(lì)是我前行最大的動(dòng)力,歡迎點(diǎn)贊,歡迎送小星星? ~

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/72272.html

相關(guān)文章

  • 你想要的全平臺(tái)全棧開(kāi)源項(xiàng)目-Vue、React、小程序、安卓、ReactNative、java后端

    摘要:無(wú)論你是前端后端移動(dòng)端開(kāi)發(fā)人員,或是設(shè)計(jì)師產(chǎn)品經(jīng)理,都可以在平臺(tái)上發(fā)布項(xiàng)目,與志同道合的小伙伴一起協(xié)作完成項(xiàng)目。 全平臺(tái)全棧開(kāi)源項(xiàng)目 coderiver 今天終于開(kāi)始前后端聯(lián)調(diào)了~ 首先感謝大家的支持,coderiver 在 GitHub 上開(kāi)源兩周,獲得了 54 個(gè) Star,9 個(gè) Fork,5 個(gè) Watch。 這些鼓勵(lì)和認(rèn)可也更加堅(jiān)定了我繼續(xù)寫下去的決心~ 再次感謝各位大佬! ...

    Maxiye 評(píng)論0 收藏0
  • 2021 年最新基于 Spring Cloud 的微服務(wù)架構(gòu)分析

    摘要:是一個(gè)相對(duì)比較新的微服務(wù)框架,年才推出的版本雖然時(shí)間最短但是相比等框架提供的全套的分布式系統(tǒng)解決方案。提供線程池不同的服務(wù)走不同的線程池,實(shí)現(xiàn)了不同服務(wù)調(diào)用的隔離,避免了服務(wù)器雪崩的問(wèn)題。通過(guò)互相注冊(cè)的方式來(lái)進(jìn)行消息同步和保證高可用。 Spring Cloud 是一個(gè)相對(duì)比較新的微服務(wù)框架,...

    cikenerd 評(píng)論0 收藏0
  • 外行人都能看懂的SpringCloud,錯(cuò)過(guò)了血虧!

    摘要:集群系統(tǒng)中的單個(gè)計(jì)算機(jī)通常稱為節(jié)點(diǎn),通常通過(guò)局域網(wǎng)連接,但也有其它的可能連接方式。這樣就高興了,可以專心寫自己的,前端就專門交由小周負(fù)責(zé)了。于是,小周和就變成了協(xié)作開(kāi)發(fā)。都是為了項(xiàng)目正常運(yùn)行以及迭代。 一、前言 只有光頭才能變強(qiáng) 認(rèn)識(shí)我的朋友可能都知道我這陣子去實(shí)習(xí)啦,去的公司說(shuō)是用SpringCloud(但我覺(jué)得使用的力度并不大啊~~)... 所以,這篇主要來(lái)講講SpringClou...

    沈建明 評(píng)論0 收藏0
  • 外行人都能看懂的SpringCloud,錯(cuò)過(guò)了血虧!

    摘要:集群系統(tǒng)中的單個(gè)計(jì)算機(jī)通常稱為節(jié)點(diǎn),通常通過(guò)局域網(wǎng)連接,但也有其它的可能連接方式。這樣就高興了,可以專心寫自己的,前端就專門交由小周負(fù)責(zé)了。于是,小周和就變成了協(xié)作開(kāi)發(fā)。都是為了項(xiàng)目正常運(yùn)行以及迭代。 一、前言 只有光頭才能變強(qiáng) 認(rèn)識(shí)我的朋友可能都知道我這陣子去實(shí)習(xí)啦,去的公司說(shuō)是用SpringCloud(但我覺(jué)得使用的力度并不大啊~~)... 所以,這篇主要來(lái)講講SpringClou...

    enda 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<