天天看點

SpringBoot交友APP項目實戰(詳細介紹+案例源碼) - 9.小視訊(SpringCache緩存)系列文章目錄一、訪客功能二、分布式存儲FastDFS三、視訊功能四、通用緩存SpringCache

有人相愛,有人跳海
SpringBoot交友APP項目實戰(詳細介紹+案例源碼) - 9.小視訊(SpringCache緩存)系列文章目錄一、訪客功能二、分布式存儲FastDFS三、視訊功能四、通用緩存SpringCache

系列文章目錄

1. 項目介紹及環境配置

2. 短信驗證碼登入

3. 使用者資訊

4. MongoDB

5. 推薦好友清單/MongoDB叢集/動态釋出與檢視

6. 圈子動态/圈子互動

7. 即時通訊(基于第三方API)

8. 附近的人(百度地圖APi)

9. 小視訊

10.網關配置

11.背景管理

文章目錄

  • 系列文章目錄
  • 一、訪客功能
    • 1. 需求分析
    • 2. 儲存訪客
      • ⑴. 需求
      • ⑵. 編碼實作
        • ①. 實體對象
        • ②. Service - 檢視佳人詳情
        • ③. Api
          • Ⅰ. Api
          • Ⅱ. 實作類
      • ⑶. 功能測試
    • 3. 誰看過我
      • ⑴. 需求
      • ⑵. 接口文檔
      • ⑶. 編碼實作
        • ①. vo對象
        • ②. Controller
        • ③. Service
        • ④. Api
        • ⑤. Api實作類
      • ⑷. 頁面效果
  • 二、分布式存儲FastDFS
    • 1. 内部結構
    • 2. 工作原理
    • 3. 服務搭建
    • 4. 測試案例
      • ⑴. 添加配置資訊
      • ⑵. 測試類
    • 5. 測試結果
  • 三、視訊功能
    • 1. 需求分析
    • 2. 釋出視訊
      • ⑴. 接口文檔
      • ⑵. 釋出流程
      • ⑶. 編碼實作
        • ①. yml配置檔案
        • ②. 實體類
        • ③. Controller
        • ④. Service
        • ⑤. Api
        • ⑥. Api實作類
      • ⑷. 頁面效果
    • 3. 視訊清單查詢
      • ⑴. 接口文檔
      • ⑵. 編碼實作
        • ①. vo對象
        • ②. Controller
        • ③. Service
        • ④. Api
        • ⑤. ApiImpl
      • ⑶. Postman
      • ⑷. 頁面效果
  • 四、通用緩存SpringCache
    • 1. 存在的問題
      • ⑴. 問題
      • ⑵. 問題分析
    • 2. SpringCache入門案例
      • ⑴. 概述
      • ⑵. 入門案例
        • ①. 引入依賴
        • ②. 開啟緩存
        • ③. 配置注解
        • ④. 測試類
      • ⑶. redis緩存
        • ①. 引入依賴
        • ②. 加入redis配置
        • ③. 配置注解
        • ④. 測試類
    • 3. 常用注解
    • 4. 使用SpringCache優化視訊清單
        • ①. 開啟緩存
        • ②. 配置注解
        • ③. 設定失效時間

一、訪客功能

1. 需求分析

  • 使用者在浏覽我的首頁時,需要記錄訪客資料,訪客在一天内每個使用者隻記錄一次
  • 首頁展示最新5條訪客記錄
  • 我的子產品,分頁展示所有的訪客記錄
    SpringBoot交友APP項目實戰(詳細介紹+案例源碼) - 9.小視訊(SpringCache緩存)系列文章目錄一、訪客功能二、分布式存儲FastDFS三、視訊功能四、通用緩存SpringCache

visirots(訪客記錄表):

SpringBoot交友APP項目實戰(詳細介紹+案例源碼) - 9.小視訊(SpringCache緩存)系列文章目錄一、訪客功能二、分布式存儲FastDFS三、視訊功能四、通用緩存SpringCache

2. 儲存訪客

⑴. 需求

  • 使用者在浏覽我的首頁時,需要記錄訪客資料
  • 訪客在一天内每個使用者隻記錄一次

⑵. 編碼實作

①. 實體對象

建立

tanhua-model/src/main/java/com/tanhua/model/mongo/Visitors.java

檔案:

@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "visitors")
public class Visitors implements java.io.Serializable{

    private static final long serialVersionUID = 2811682148052386573L;

    private ObjectId id;
    private Long userId; //我的id
    private Long visitorUserId; //來訪使用者id
    private String from; //來源,如首頁、圈子等
    private Long date; //來訪時間
    private String visitDate;//來訪日期
    private Double score; //得分

}
           

②. Service - 檢視佳人詳情

編輯

tanhua-app-server/src/main/java/com/tanhua/server/service/TanhuaService.java

檔案:

...

    // 檢視佳人詳情
    public TodayBest personalInfo(Long userId) {
        // 1. 根據使用者id,查詢使用者詳情
        UserInfo userInfo = userInfoApi.findById(userId);
        // 2. 根據操作人id, 和檢視的使用者id,查詢兩者的推薦資料(緣分值)
        RecommendUser user = recommendUserApi.queryByUserId(userId, UserHolder.getUserId());

        // 構造訪客資料,調用API儲存
        Visitors visitors = new Visitors();
        visitors.setUserId(userId);
        visitors.setVisitorUserId(UserHolder.getUserId());
        visitors.setFrom("首頁");
        visitors.setDate(System.currentTimeMillis());
        visitors.setVisitDate(new SimpleDateFormat("yyyyMMdd").format(new Date()));
        visitors.setScore(user.getScore());
        visitorsApi.save(visitors);

        // 3. 構造傳回值
        return TodayBest.init(userInfo, user);
    }

...
           

③. Api

Ⅰ. Api

建立

tanhua-dubbo/tanhua-dubbo-interface/src/main/java/com/tanhua/dubbo/api/VisitorsApi.java

檔案:

public interface VisitorsApi {

    // 儲存訪客資料
    void save(Visitors visitors);
}
           
Ⅱ. 實作類

建立

tanhua-dubbo/tanhua-dubbo-mongo/src/main/java/com/tanhua/dubbo/api/VisitorsApiImpl.java

檔案:

@DubboService
public class VisitorsApiImpl implements VisitorsApi{

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 儲存訪客資料
         * 對于同一個訪客,一天隻能儲存一次資料
     */
    @Override
    public void save(Visitors visitors) {
        // 1. 查詢訪客資料
        Query query = Query.query(Criteria.where("userId").is(visitors.getUserId())
                .and("visitorUserId").is(visitors.getVisitorUserId())
                .and("visitDate").is(visitors.getVisitDate()));
        // 2. 不存在,儲存
        if(!mongoTemplate.exists(query, Visitors.class)) {
            mongoTemplate.save(visitors);
        }
    }
}
           

⑶. 功能測試

SpringBoot交友APP項目實戰(詳細介紹+案例源碼) - 9.小視訊(SpringCache緩存)系列文章目錄一、訪客功能二、分布式存儲FastDFS三、視訊功能四、通用緩存SpringCache
SpringBoot交友APP項目實戰(詳細介紹+案例源碼) - 9.小視訊(SpringCache緩存)系列文章目錄一、訪客功能二、分布式存儲FastDFS三、視訊功能四、通用緩存SpringCache

3. 誰看過我

⑴. 需求

首頁查詢最新訪客清單,查詢資料時,如果使用者查詢過清單,就需要記錄這次查詢資料的時間,下次查詢時查詢大于等于該時間的資料。如果,使用者沒有記錄查詢時間,就查詢最近的5個來訪使用者。

SpringBoot交友APP項目實戰(詳細介紹+案例源碼) - 9.小視訊(SpringCache緩存)系列文章目錄一、訪客功能二、分布式存儲FastDFS三、視訊功能四、通用緩存SpringCache

⑵. 接口文檔

SpringBoot交友APP項目實戰(詳細介紹+案例源碼) - 9.小視訊(SpringCache緩存)系列文章目錄一、訪客功能二、分布式存儲FastDFS三、視訊功能四、通用緩存SpringCache

⑶. 編碼實作

①. vo對象

建立

tanhua-model/src/main/java/com/tanhua/model/vo/VisitorsVo.java

檔案:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class VisitorsVo {

    private Long id; //使用者id
    private String avatar;
    private String nickname;
    private String gender; //性别 man woman
    private Integer age;
    private String[] tags;
    private Long fateValue; //緣分值

    /**
     * 在vo對象中,補充一個工具方法,封裝轉化過程
     */
    public static VisitorsVo init(UserInfo userInfo, Visitors visitors) {
        VisitorsVo vo = new VisitorsVo();
        BeanUtils.copyProperties(userInfo,vo);
        if(userInfo.getTags() != null) {
            vo.setTags(userInfo.getTags().split(","));
        }
        vo.setFateValue(visitors.getScore().longValue());
        return vo;
    }
}
           

②. Controller

編輯

tanhua-app-server/src/main/java/com/tanhua/server/controller/MovementController.java

檔案:

@RestController
@RequestMapping("/movements")
public class MovementController {

    @Autowired
    private MovementService movementService;

    @Autowired
    private CommentsService commentsService;

    /**
     * 釋出動态
     * @return
     */
    @PostMapping
    public ResponseEntity movements(Movement movement, MultipartFile imageContent[]) throws IOException {
        movementService.publishMovement(movement, imageContent);
        return ResponseEntity.ok(null);
    }

    /**
     * 查詢我的動态
     */
    @GetMapping("/all")
    public ResponseEntity findByUserId(Long userId,
                                       @RequestParam(defaultValue = "1") Integer page,
                                       @RequestParam(defaultValue = "10") Integer pagesize) {
        PageResult pr = movementService.findByUserId(userId, page, pagesize);
        return ResponseEntity.ok(pr);
    }

    /**
     * 查詢好友動态
     */
    @GetMapping()
    public ResponseEntity movements( @RequestParam(defaultValue = "1") Integer page,
                                     @RequestParam(defaultValue = "10") Integer pagesize) {
        PageResult pr = movementService.findFriendMovements(page, pagesize);
        return ResponseEntity.ok(pr);
    }

    /**
     * 查詢推薦動态
     */
    @GetMapping("/recommend")
    public ResponseEntity recommend( @RequestParam(defaultValue = "1") Integer page,
                                     @RequestParam(defaultValue = "10") Integer pagesize) {
        PageResult pr = movementService.findRecommendMovements(page, pagesize);
        return ResponseEntity.ok(pr);
    }

    /**
     * 查詢單條動态
     */
    @GetMapping("/{id}")
    public ResponseEntity findById(@PathVariable("id") String movementId) {
        MovementsVo vo = movementService.findById(movementId);
        return ResponseEntity.ok(vo);
    }

    /**
     * 點贊
     */
    @GetMapping("/{id}/like")
    public ResponseEntity like(@PathVariable("id") String movementId) {
        Integer likeCount = commentsService.likeComment(movementId);
        return ResponseEntity.ok(likeCount);
    }

    /**
     * 取消點贊
     */
    @GetMapping("/{id}/dislike")
    public ResponseEntity dislike(@PathVariable("id") String movementId) {
        Integer likeCount = commentsService.dislikeComment(movementId);
        return ResponseEntity.ok(likeCount);
    }

    /**
     * 喜歡
     */
    @GetMapping("/{id}/love")
    public ResponseEntity love(@PathVariable("id") String movementId) {
        Integer likeCount = commentsService.loveComment(movementId);
        return ResponseEntity.ok(likeCount);
    }

    /**
     * 取消喜歡
     */
    @GetMapping("/{id}/unlove")
    public ResponseEntity unlove(@PathVariable("id") String movementId) {
        Integer likeCount = commentsService.disloveComment(movementId);
        return ResponseEntity.ok(likeCount);
    }

    /**
     * 誰看過我
     */
    @GetMapping("visitors")
    public ResponseEntity queryVisitorsList(){
        List<VisitorsVo> list = movementService.queryVisitorsList();
        return ResponseEntity.ok(list);
    }
}
           

③. Service

編輯

tanhua-app-server/src/main/java/com/tanhua/server/service/MovementService.java

檔案:

@Service
public class MovementService {

    @Autowired
    private OssTemplate ossTemplate;

    @DubboReference
    private MovementApi movementApi;

    @DubboReference
    private UserInfoApi userInfoApi;

     @Autowired
     private RedisTemplate<String, String> redisTemplate;

     @DubboReference
     private VisitorsApi visitorsApi;

    /**
     * 釋出動态
     */
    public void publishMovement(Movement movement, MultipartFile[] imageContent) throws IOException {
        // 1. 判斷釋出動态的内容是否存在
        if(StringUtils.isEmpty(movement.getTextContent())) {
            throw new BusinessException(ErrorResult.contentError());
        }

        // 2. 擷取目前登入的使用者id
        Long userId = UserHolder.getUserId();

        // 3. 将檔案内容上傳到阿裡雲OSS, 擷取請求位址
        List<String> medias = new ArrayList<>();
        for (MultipartFile multipartFile : imageContent) {
            // String upload = ossTemplate.upload(multipartFile.getOriginalFilename(), multipartFile.getInputStream());
            // !!! 阿裡雲OSS收費, 這裡暫時跳過
            String upload = "https://img0.baidu.com/it/u=1501084209,93021381&fm=253&fmt=auto&app=138&f=JPEG";
            medias.add(upload);
        }

        // 4. 将資料封裝到movement對象
        movement.setUserId(userId);
        movement.setMedias(medias);

         //5. 調用API完成動态釋出
        movementApi.publish(movement);
    }

    // 查詢我的動态

    public PageResult findByUserId(Long userId, Integer page, Integer pagesize) {
        // 1. 根據使用者id, 調用API查詢個人動态内容(PageResult -- Movement)
        PageResult pr = movementApi.findByUserId(userId, page, pagesize);

        // 2. 擷取PageResult中item清單對象
        List<Movement> items = (List<Movement>) pr.getItems();

        // 3. 非空判斷
        if(items == null) {
            return pr;
        }

        // 4. 循環資料清單
        UserInfo userInfo = userInfoApi.findById(userId);
        List<MovementsVo> vos = new ArrayList<>();
        for (Movement item : items) {
            // 5. 一個Movement建構一個VO對象
            MovementsVo vo = MovementsVo.init(userInfo, item);
            vos.add(vo);
        }

        // 6. 建構傳回值
        pr.setItems(vos);
        return pr;
    }

    // 查詢好友動态
    public PageResult findFriendMovements(Integer page, Integer pagesize) {
        // 1. 擷取目前使用者id
        Long userId = UserHolder.getUserId();

        // 2. 調用API查詢目前使用者好友釋出的動态清單
        List<Movement> list = movementApi.findFriendMovements(page, pagesize, userId);

        return getPageResult(page, pagesize, list);
    }

    // 公共方法
    private PageResult getPageResult(Integer page, Integer pagesize, List<Movement> list) {
        // 3. 判斷清單是否為空
        // if(list == null || list.isEmpty()) {
        if(CollUtil.isEmpty(list)) {
            return new PageResult();
       }

        // 4. 提取動态釋出人的id清單
        List<Long> userIds = CollUtil.getFieldValues(list, "userId", Long.class);

        // 5. 根據使用者id清單擷取使用者詳情
        Map<Long, UserInfo> map = userInfoApi.findByIds(userIds, null);

        // 6. 一個Movement構造一個vo對象
        List<MovementsVo> vos = new ArrayList<>();
        for (Movement movement : list) {
            UserInfo userInfo = map.get(movement.getUserId());
            if(userInfo != null) {
                MovementsVo vo = MovementsVo.init(userInfo, movement);

                // 添加點贊狀态,判斷hashKey是否存在
                String key = Constants.MOVEMENTS_INTERACT_KEY + movement.getId().toHexString();
                String hashKey = Constants.MOVEMENT_LIKE_HASHKEY + UserHolder.getUserId();
                if(redisTemplate.opsForHash().hasKey(key, hashKey)) {
                    vo.setHasLiked(1);
                }

                // 添加喜歡狀态,判斷hashKey是否存在
                String loveHashKey = Constants.MOVEMENT_LOVE_HASHKEY + UserHolder.getUserId();
                if(redisTemplate.opsForHash().hasKey(key, loveHashKey)) {
                    vo.setHasLoved(1);
                }

                vos.add(vo);
            }
        }

        // 7. 構造PageResult并傳回
        return new PageResult(page, pagesize, 0l, vos);
    }

    // 查詢推薦動态
    public PageResult findRecommendMovements(Integer page, Integer pagesize) {
        // 1. 從redis從擷取推薦資料
        String redisKey = Constants.MOVEMENTS_RECOMMEND + UserHolder.getUserId();
        String redisValue = redisTemplate.opsForValue().get(redisKey);

        // 2. 判斷推薦資料是否存在
        List<Movement> list = Collections.EMPTY_LIST;

        if(StringUtils.isEmpty(redisValue)) {
            // 3. 如果不存在, 調用API随機構造10條動态資料
            list = movementApi.randomMovements(pagesize);

        } else {
            // 4. 如果存在, 處理pid資料  "16,17,18,19,20,21,10015,10020,10040,10064,10092,10093,10099,10067"
            String[] values = redisValue.split(",");
            // 4.1 判斷目前頁的起始條數是否小于數組的總數
            if((page - 1) * pagesize < values.length) {
                List<Long> pids = Arrays.stream(values).skip((page - 1) * pagesize).limit(pagesize)
                        .map(e -> Long.valueOf(e))
                        .collect(Collectors.toList());

                // 5. 調用API根據PID數組查詢動态資料
                list = movementApi.findMovementByPids(pids);
            }
        }
        // 6. 調用公共方法構造傳回值
        return getPageResult(page, pagesize, list);
    }

    // 查詢單條動态
    public MovementsVo findById(String movementId) {
        // 1. 調用API查詢動态詳情
        Movement movement = movementApi.findById(movementId);

        // 2. 轉換vo對象
        if(movement != null) {
            UserInfo userInfo = userInfoApi.findById(movement.getUserId());
            return MovementsVo.init(userInfo, movement);
        } else {
            return null;
        }
    }

    // 首頁 - 訪客清單
    public List<VisitorsVo> queryVisitorsList() {
        // 1. 查詢通路時間
        String key = Constants.VISITORS_USER;
        String hashKey = String.valueOf(UserHolder.getUserId());
        String value = (String) redisTemplate.opsForHash().get(key, hashKey);
        Long date = StringUtils.isEmpty(value) ? null : Long.valueOf(value);
        // 2. 調用API查詢資料清單 List<Visitors>
        List<Visitors> list = visitorsApi.queryVisitorsList(date, UserHolder.getUserId());
        if(CollUtil.isEmpty(list)) {
            return new ArrayList<>();
        }
        // 3. 提取使用者id
        List<Long> visitorUserIds = CollUtil.getFieldValues(list, "visitorUserId", Long.class);
        // 4. 檢視使用者詳情
        Map<Long, UserInfo> map = userInfoApi.findByIds(visitorUserIds, null);
        // 5. 構造傳回
        List<VisitorsVo> vos = new ArrayList<>();
        for (Visitors visitors : list) {
            UserInfo userInfo = map.get(visitors.getVisitorUserId());
            if(userInfo != null) {
                VisitorsVo vo = VisitorsVo.init(userInfo, visitors);
                vos.add(vo);
            }
        }
        return vos;
    }
}
           

④. Api

編輯

tanhua-dubbo/tanhua-dubbo-interface/src/main/java/com/tanhua/dubbo/api/VisitorsApi.java

檔案:

public interface VisitorsApi {

    // 儲存訪客資料
    void save(Visitors visitors);

    // 首頁 - 查詢訪客清單
    List<Visitors> queryVisitorsList(Long date, Long userId);
}
           

⑤. Api實作類

編輯

tanhua-dubbo/tanhua-dubbo-mongo/src/main/java/com/tanhua/dubbo/api/VisitorsApiImpl.java

檔案:

@DubboService
public class VisitorsApiImpl implements VisitorsApi{

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 儲存訪客資料
         * 對于同一個訪客,一天隻能儲存一次資料
     */
    @Override
    public void save(Visitors visitors) {
        // 1. 查詢訪客資料
        Query query = Query.query(Criteria.where("userId").is(visitors.getUserId())
                .and("visitorUserId").is(visitors.getVisitorUserId())
                .and("visitDate").is(visitors.getVisitDate()));
        // 2. 不存在,儲存
        if(!mongoTemplate.exists(query, Visitors.class)) {
            mongoTemplate.save(visitors);
        }
    }

    // 首頁 - 查詢訪客清單
    public List<Visitors> queryVisitorsList(Long date, Long userId) {
        Criteria criteria = Criteria.where("userId").is(userId);
        if(date != null) {
            criteria.and("date").gt(date);
        }
        Query query = Query.query(criteria).limit(5).with(Sort.by(Sort.Order.desc("date")));
        return mongoTemplate.find(query, Visitors.class);
    }
}
           

⑷. 頁面效果

SpringBoot交友APP項目實戰(詳細介紹+案例源碼) - 9.小視訊(SpringCache緩存)系列文章目錄一、訪客功能二、分布式存儲FastDFS三、視訊功能四、通用緩存SpringCache

二、分布式存儲FastDFS

FastDFS是分布式檔案系統。使用 FastDFS很容易搭建一套高性能的檔案伺服器叢集提供檔案上傳、下載下傳等服務

1. 内部結構

  • Tracker server:
    • 配置叢集
    • Tracker server監控各個Storage server,排程存儲服務
  • Storage server:
    • Storage server(存儲伺服器),檔案最終存放的位置
    • 通過Group(組),拓展檔案存儲容量
    • 各個Group(組)中,通過叢集解決單點故障
SpringBoot交友APP項目實戰(詳細介紹+案例源碼) - 9.小視訊(SpringCache緩存)系列文章目錄一、訪客功能二、分布式存儲FastDFS三、視訊功能四、通用緩存SpringCache

2. 工作原理

上傳:

  • Storage Server 向Tracker Server, 彙報目前存儲節點的狀态資訊(包括磁盤剩餘空間、檔案同步狀況等統計資訊)
  • 用戶端程式連接配接Tracker Server發給上傳請求
  • Tracker Server計算可用的Storage Server 節點,傳回
  • 用戶端将檔案上傳到Storage Server,并擷取傳回的file_id(包括路徑資訊和檔案名稱)
  • 用戶端儲存請求位址
SpringBoot交友APP項目實戰(詳細介紹+案例源碼) - 9.小視訊(SpringCache緩存)系列文章目錄一、訪客功能二、分布式存儲FastDFS三、視訊功能四、通用緩存SpringCache

下載下傳:

  • 和檔案上傳類似
  • 檔案下載下傳使用頻率并不高,由于用戶端記錄的通路位址,直接拼接位址通路即可
SpringBoot交友APP項目實戰(詳細介紹+案例源碼) - 9.小視訊(SpringCache緩存)系列文章目錄一、訪客功能二、分布式存儲FastDFS三、視訊功能四、通用緩存SpringCache

3. 服務搭建

探花交友所需的第三方服務元件,已經以Docker-Compose準備好了。僅僅需要進入相關目錄,以指令形式啟動運作即可
#進入目錄
cd /root/docker-file/fastdfs/
#建立容器并啟動
docker-compose up –d
#檢視容器
docker ps -a
           
SpringBoot交友APP項目實戰(詳細介紹+案例源碼) - 9.小視訊(SpringCache緩存)系列文章目錄一、訪客功能二、分布式存儲FastDFS三、視訊功能四、通用緩存SpringCache

4. 測試案例

⑴. 添加配置資訊

編輯

tanhua-app-server/src/main/resources/application.yml

檔案:

...

# ===================================================================
# 分布式檔案系統FDFS配置
# ===================================================================
fdfs:
  so-timeout: 1500
  connect-timeout: 600
  #縮略圖生成參數
  thumb-image:
    width: 150
    height: 150
  #TrackerList參數,支援多個
  tracker-list: 192.168.136.160:22122
  web-server-url: http://192.168.136.160:8888/
           

⑵. 測試類

建立

tanhua-app-server/src/test/java/com/tanhua/test/FastDFSTest.java

檔案:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = AppServerApplication.class)
public class FastDFSTest {

    /**
     * 測試FastDFS的檔案上傳
     */

    // 用于檔案上傳或下載下傳
    @Autowired
    private FastFileStorageClient client;

    @Autowired
    private FdfsWebServer webServer;// 擷取存儲伺服器的請求URL

    @Test
    public void testUpload() throws FileNotFoundException {
 		//1、指定檔案
        File file = new File("D:\\Course\\HM\\img\\1.jpg");
		//2、檔案上傳
        StorePath path = client.uploadFile(new FileInputStream(file), file.length(), "jpg", null);
		//3、拼接通路路徑
        String fullPath = path.getFullPath();
        System.out.println(fullPath);
        String url = webServer.getWebServerUrl() + fullPath;
        System.out.println(url);
    }
}
           

5. 測試結果

SpringBoot交友APP項目實戰(詳細介紹+案例源碼) - 9.小視訊(SpringCache緩存)系列文章目錄一、訪客功能二、分布式存儲FastDFS三、視訊功能四、通用緩存SpringCache

三、視訊功能

1. 需求分析

小視訊功能類似于抖音、快手小視訊的應用,使用者可以上傳小視訊進行分享,也可以浏覽檢視别人分享的視訊,并且可以對視訊評論和點贊操作。

video(視訊記錄表):

SpringBoot交友APP項目實戰(詳細介紹+案例源碼) - 9.小視訊(SpringCache緩存)系列文章目錄一、訪客功能二、分布式存儲FastDFS三、視訊功能四、通用緩存SpringCache

2. 釋出視訊

⑴. 接口文檔

SpringBoot交友APP項目實戰(詳細介紹+案例源碼) - 9.小視訊(SpringCache緩存)系列文章目錄一、訪客功能二、分布式存儲FastDFS三、視訊功能四、通用緩存SpringCache

⑵. 釋出流程

  • 用戶端上傳視訊時,自動生成封面圖檔一并發送請求
  • 封面圖檔:上傳到阿裡雲OSS
  • 視訊:上傳到FastDFS
SpringBoot交友APP項目實戰(詳細介紹+案例源碼) - 9.小視訊(SpringCache緩存)系列文章目錄一、訪客功能二、分布式存儲FastDFS三、視訊功能四、通用緩存SpringCache
  • 使用者發通過用戶端APP上傳視訊到server服務
  • server服務上傳視訊到FastDFS檔案系統,上傳成功後傳回視訊的url位址
  • server服務上傳封面圖檔到阿裡雲OSS
  • server通過rpc的調用dubbo服務進行儲存小視訊資料

⑶. 編碼實作

①. yml配置檔案

編輯

tanhua-app-server/src/main/resources/application.yml

檔案:

...
Spring:
  servlet:
    multipart:
      max-file-size: 30MB
      max-request-size: 30MB
           

②. 實體類

建立

tanhua-model/src/main/java/com/tanhua/model/mongo/Video.java

檔案:

@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "video")
public class Video implements java.io.Serializable {

    private static final long serialVersionUID = -3136732836884933873L;

    private ObjectId id; //主鍵id
    private Long vid; //自動增長
    private Long created; //建立時間


    private Long userId;
    private String text; //文字
    private String picUrl; //視訊封面檔案,URL
    private String videoUrl; //視訊檔案,URL


    private Integer likeCount=0; //點贊數
    private Integer commentCount=0; //評論數
    private Integer loveCount=0; //喜歡數
}
           

③. Controller

建立

tanhua-app-server/src/main/java/com/tanhua/server/controller/SmallVideoController.java

檔案:

@RestController
@RequestMapping("/smallVideos")
public class SmallVideoController {

    @Autowired
    private SmallVideosService videosService;

    /**
     * 釋出視訊
     *  接口路徑:POST
     *  請求參數:
     *      videoThumbnail:封面圖
     *      videoFile:視訊檔案
     */
    @PostMapping
    public ResponseEntity saveVideos(MultipartFile videoThumbnail, MultipartFile videoFile) throws IOException {
        videosService.saveVideos(videoThumbnail,videoFile);
        return ResponseEntity.ok(null);
    }
}
           

④. Service

編輯

tanhua-app-server/src/main/java/com/tanhua/server/service/SmallVideosService.java

檔案:

@Service
public class SmallVideosService {

    @Autowired
    private FastFileStorageClient client;

    @Autowired
    private FdfsWebServer webServer;

    @Autowired
    private OssTemplate ossTemplate;

    @DubboReference
    private VideoApi videoApi;

    /**
     * 上傳視訊
     * @param videoThumbnail 視訊封面圖檔
     * @param videoFile 視訊檔案
     */
    public void saveVideos(MultipartFile videoThumbnail, MultipartFile videoFile) throws IOException {
        if(videoThumbnail.isEmpty() || videoFile.isEmpty()) {
            throw new BusinessException(ErrorResult.error());
        }
        // 1. 将視訊上傳到FastDFS,擷取通路url
        String filename = videoFile.getOriginalFilename();
        filename = filename.substring(filename.lastIndexOf(".") + 1);
        StorePath storePath = client.uploadFile(videoFile.getInputStream(), videoFile.getSize(), filename, null);
        String videoUrl = webServer.getWebServerUrl() + storePath.getFullPath();

        // 2. 将封面圖檔上傳到阿裡雲OSS,擷取通路的url
        // String upload = ossTemplate.upload(videoThumbnail.getOriginalFilename(), videoThumbnail.getInputStream());
        // // !!! 阿裡雲OSS收費, 這裡暫時跳過
        String upload = "https://img0.baidu.com/it/u=8672387,2873147723&fm=253&fmt=auto&app=138&f=JPEG";

        // 3. 建構Videos對象
        Video video = new Video();
        video.setUserId(UserHolder.getUserId());
        video.setVideoUrl(videoUrl);
        video.setPicUrl(upload);
        video.setText("我就是我, 是顔色不一樣的煙火");

        // 4. 調用API儲存資料
        String videoId = videoApi.save(video);
        if(StringUtils.isEmpty(videoId)) {
            throw new BusinessException(ErrorResult.error());
        }
    }
}
           

⑤. Api

建立

tanhua-dubbo/tanhua-dubbo-interface/src/main/java/com/tanhua/dubbo/api/VideoApi.java

檔案:

public interface VideoApi {

    // 儲存視訊
    String save(Video video);
}
           

⑥. Api實作類

建立

tanhua-dubbo/tanhua-dubbo-mongo/src/main/java/com/tanhua/dubbo/api/VideoApiImpl.java

檔案:

@DubboService
public class VideoApiImpl implements VideoApi{

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private IdWorker idWorker;

    // 儲存視訊
    @Override
    public String save(Video video) {
        // 1. 設定屬性
        video.setVid(idWorker.getNextId("video"));
        video.setCreated(System.currentTimeMillis());
        // 2. 調用方法儲存對象
        mongoTemplate.save(video);
        // 3. 傳回對象id
        return video.getId().toHexString();
    }
}
           

⑷. 頁面效果

SpringBoot交友APP項目實戰(詳細介紹+案例源碼) - 9.小視訊(SpringCache緩存)系列文章目錄一、訪客功能二、分布式存儲FastDFS三、視訊功能四、通用緩存SpringCache

3. 視訊清單查詢

⑴. 接口文檔

SpringBoot交友APP項目實戰(詳細介紹+案例源碼) - 9.小視訊(SpringCache緩存)系列文章目錄一、訪客功能二、分布式存儲FastDFS三、視訊功能四、通用緩存SpringCache

⑵. 編碼實作

①. vo對象

建立

tanhua-model/src/main/java/com/tanhua/model/vo/VideoVo.java

檔案:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class VideoVo implements Serializable {


    private Long userId;
    private String avatar; //頭像
    private String nickname; //昵稱

    private String id;
    private String cover; //封面
    private String videoUrl; //視訊URL
    private String signature; //釋出視訊時,傳入的文字内容


    private Integer likeCount; //點贊數量
    private Integer hasLiked; //是否已贊(1是,0否)
    private Integer hasFocus; //是否關注 (1是,0否)
    private Integer commentCount; //評論數量

    public static VideoVo init(UserInfo userInfo, Video item) {
        VideoVo vo = new VideoVo();
        //copy使用者屬性
        BeanUtils.copyProperties(userInfo,vo);  //source,target
        //copy視訊屬性
        BeanUtils.copyProperties(item,vo);
        vo.setCover(item.getPicUrl());
        vo.setId(item.getId().toHexString());
        vo.setSignature(item.getText());
        vo.setHasFocus(0);
        vo.setHasLiked(0);
        return vo;
    }
}
           

②. Controller

編輯

tanhua-app-server/src/main/java/com/tanhua/server/controller/SmallVideoController.java

檔案:

@RestController
@RequestMapping("/smallVideos")
public class SmallVideoController {

    @Autowired
    private SmallVideosService videosService;

    /**
     * 釋出視訊
     *  接口路徑:POST
     *  請求參數:
     *      videoThumbnail:封面圖
     *      videoFile:視訊檔案
     */
    @PostMapping
    public ResponseEntity saveVideos(MultipartFile videoThumbnail, MultipartFile videoFile) throws IOException {
        videosService.saveVideos(videoThumbnail,videoFile);
        return ResponseEntity.ok(null);
    }

    /**
     * 視訊清單
     */
    @GetMapping
    public ResponseEntity queryVideoList(@RequestParam(defaultValue = "1")  Integer page,
                                         @RequestParam(defaultValue = "10") Integer pagesize) {
        PageResult result = videosService.queryVideoList(page, pagesize);
        return ResponseEntity.ok(result);
    }
}
           

③. Service

編輯

tanhua-app-server/src/main/java/com/tanhua/server/service/SmallVideosService.java

檔案:

@Service
public class SmallVideosService {

    @Autowired
    private FastFileStorageClient client;

    @Autowired
    private FdfsWebServer webServer;

    @Autowired
    private OssTemplate ossTemplate;

    @DubboReference
    private VideoApi videoApi;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @DubboReference
    private UserInfoApi userInfoApi;

    /**
     * 上傳視訊
     * @param videoThumbnail 視訊封面圖檔
     * @param videoFile 視訊檔案
     */
    public void saveVideos(MultipartFile videoThumbnail, MultipartFile videoFile) throws IOException {
        if(videoThumbnail.isEmpty() || videoFile.isEmpty()) {
            throw new BusinessException(ErrorResult.error());
        }
        // 1. 将視訊上傳到FastDFS,擷取通路url
        String filename = videoFile.getOriginalFilename();
        filename = filename.substring(filename.lastIndexOf(".") + 1);
        StorePath storePath = client.uploadFile(videoFile.getInputStream(), videoFile.getSize(), filename, null);
        String videoUrl = webServer.getWebServerUrl() + storePath.getFullPath();

        // 2. 将封面圖檔上傳到阿裡雲OSS,擷取通路的url
        // String upload = ossTemplate.upload(videoThumbnail.getOriginalFilename(), videoThumbnail.getInputStream());
        // // !!! 阿裡雲OSS收費, 這裡暫時跳過
        String upload = "https://img0.baidu.com/it/u=8672387,2873147723&fm=253&fmt=auto&app=138&f=JPEG";

        // 3. 建構Videos對象
        Video video = new Video();
        video.setUserId(UserHolder.getUserId());
        video.setVideoUrl(videoUrl);
        video.setPicUrl(upload);
        video.setText("我就是我, 是顔色不一樣的煙火");

        // 4. 調用API儲存資料
        String videoId = videoApi.save(video);
        if(StringUtils.isEmpty(videoId)) {
            throw new BusinessException(ErrorResult.error());
        }
    }

    // 查詢視訊清單
    public PageResult queryVideoList(Integer page, Integer pagesize) {
        // 1. 查詢redis資料
        String redisKey = Constants.VIDEOS_RECOMMEND + UserHolder.getUserId();
        String redisValue = redisTemplate.opsForValue().get(redisKey);

        // 2. 判斷redis資料是否存在
        List<Video> list = new ArrayList<>();
        int redisPages = 0;
        if(!StringUtils.isEmpty(redisValue)) {
            // 3. 如果redis資料存在,根據VID查詢資料
            String[] values = redisValue.split(",");
            // 4. 判斷redis中資料是否滿足本次分頁條數
            if((page - 1) * pagesize < values.length) {
                List<Long> vids = Arrays.stream(values).skip((page - 1) * pagesize).limit(pagesize)
                        .map(e -> Long.valueOf(e))
                        .collect(Collectors.toList());
                list = videoApi.findMovementsByPids(vids);
            }
            redisPages = PageUtil.totalPage(values.length, pagesize);
        }

        // 5. 如果redis資料不存在, 分頁查詢視訊資料
        if(list.isEmpty()) {
            // page的計算規則, 傳入的頁碼, --redis查詢的總頁數
            list = videoApi.queryVideoList(page - redisPages, pagesize);
        }

        // 6. 提取視訊清單中所有的使用者id
        List<Long> userIds = CollUtil.getFieldValues(list, "userId", Long.class);

        // 7. 查詢使用者資訊
        Map<Long, UserInfo> map = userInfoApi.findByIds(userIds, null);

        // 8. 建構傳回值
        List<VideoVo> vos = new ArrayList<>();
        for (Video video : list) {
            UserInfo info = map.get(video.getUserId());
            if(info != null) {
                VideoVo vo = VideoVo.init(info, video);
                vos.add(vo);
            }
        }
        return new PageResult(page, pagesize, 0l, vos);
    }
}
           

④. Api

編輯

tanhua-dubbo/tanhua-dubbo-interface/src/main/java/com/tanhua/dubbo/api/VideoApi.java

檔案:

public interface VideoApi {

    // 儲存視訊
    String save(Video video);

    // 根據vid查詢資料清單
    List<Video> findMovementsByPids(List<Long> vids);

    // 分頁查詢資料清單
    List<Video> queryVideoList(int page, Integer pagesize);
}
           

⑤. ApiImpl

編輯

tanhua-dubbo/tanhua-dubbo-mongo/src/main/java/com/tanhua/dubbo/api/VideoApiImpl.java

檔案:

@DubboService
public class VideoApiImpl implements VideoApi{

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private IdWorker idWorker;

    // 儲存視訊
    @Override
    public String save(Video video) {
        // 1. 設定屬性
        video.setVid(idWorker.getNextId("video"));
        video.setCreated(System.currentTimeMillis());
        // 2. 調用方法儲存對象
        mongoTemplate.save(video);
        // 3. 傳回對象id
        return video.getId().toHexString();
    }

    // 根據vid查詢資料清單
    @Override
    public List<Video> findMovementsByPids(List<Long> vids) {
        Query query = Query.query(Criteria.where("vid").in(vids));
        return mongoTemplate.find(query, Video.class);
    }

    // 分頁查詢資料清單
    @Override
    public List<Video> queryVideoList(int page, Integer pagesize) {
        Query query = new Query().skip((page - 1) * pagesize).limit(pagesize)
                .with(Sort.by(Sort.Order.desc("created")));
        return mongoTemplate.find(query, Video.class);
    }
}
           

⑶. Postman

SpringBoot交友APP項目實戰(詳細介紹+案例源碼) - 9.小視訊(SpringCache緩存)系列文章目錄一、訪客功能二、分布式存儲FastDFS三、視訊功能四、通用緩存SpringCache

⑷. 頁面效果

SpringBoot交友APP項目實戰(詳細介紹+案例源碼) - 9.小視訊(SpringCache緩存)系列文章目錄一、訪客功能二、分布式存儲FastDFS三、視訊功能四、通用緩存SpringCache

四、通用緩存SpringCache

1. 存在的問題

⑴. 問題

在項目中,我們通常會把高頻的查詢進行緩存。如資訊網站首頁的文章清單、電商網站首頁的商品清單、微網誌等社交媒體熱搜的文章等等,當大量的使用者發起查詢時,借助緩存提高查詢效率,同時減輕資料庫壓力。目前的緩存架構有很多:比如Redis、Memcached、Guava、Caffeine等等
SpringBoot交友APP項目實戰(詳細介紹+案例源碼) - 9.小視訊(SpringCache緩存)系列文章目錄一、訪客功能二、分布式存儲FastDFS三、視訊功能四、通用緩存SpringCache

以redis為例:

SpringBoot交友APP項目實戰(詳細介紹+案例源碼) - 9.小視訊(SpringCache緩存)系列文章目錄一、訪客功能二、分布式存儲FastDFS三、視訊功能四、通用緩存SpringCache

⑵. 問題分析

  • 使用SpringAOP動态增強
  • 自定義注解,進行緩存配置
  • 适配多種緩存
SpringBoot交友APP項目實戰(詳細介紹+案例源碼) - 9.小視訊(SpringCache緩存)系列文章目錄一、訪客功能二、分布式存儲FastDFS三、視訊功能四、通用緩存SpringCache

2. SpringCache入門案例

⑴. 概述

Spring Cache是Spring提供的通用緩存架構。它利用了AOP,實作了基于注解的緩存功能,使開發者不用關心底層使用了什麼緩存架構,隻需要簡單地加一個注解,就能實作緩存功能了。使用者使用Spring Cache,可以快速開發一個很不錯的緩存功能。

Gitee倉庫: https://gitee.com/yuan0_0/tanhua_spring_cache.git

SpringBoot交友APP項目實戰(詳細介紹+案例源碼) - 9.小視訊(SpringCache緩存)系列文章目錄一、訪客功能二、分布式存儲FastDFS三、視訊功能四、通用緩存SpringCache

⑵. 入門案例

①. 引入依賴

編輯

pom.xml

檔案:

<!--spring cache依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
           

②. 開啟緩存

編輯

src/main/java/com/itheima/cache/CachingApplication.java

檔案:

@SpringBootApplication
@EnableCaching // 開啟緩存
public class CachingApplication {

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

}
           

③. 配置注解

編輯

src/main/java/com/itheima/cache/service/UserService.java

檔案:

@CachePut(value="user"")
    public User findById(Long id) {
        return userDao.findById(id);
    }
           

④. 測試類

編輯

src/test/java/com/itheima/cache/test/UserServiceTest.java

檔案:

/**
     * 根據id查詢使用者
     */
    @Test
    public void testFindById() {
        for (int i = 0; i < 5; i++) {
            User user = userService.findById(1l);
            System.out.println(user);
        }
    }
           

⑶. redis緩存

①. 引入依賴

編輯

pom.xml

檔案:

<!--SpringDataRedis依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
           

②. 加入redis配置

編輯

src/main/resources/application.yml

檔案:

spring:
  redis:
    port: 6379
    host: 192.168.136.160
           

③. 配置注解

編輯

src/main/java/com/itheima/cache/service/UserService.java

檔案:

@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    // @CachePut(value="user"")
    /**
     * value:名稱空間(分組)
     * key: 支援springel
     * redis-key的命名規則:
     *      value + "::" + key
     */
    @CachePut(value="user" , key = "'test' + #id")
    public User findById(Long id) {
        return userDao.findById(id);
    }

    //@CacheEvict(value="user" , key = "'test' + #id")
    @Caching(
            evict = {
                    @CacheEvict(value="user" , key = "'test' + #id"),
                    @CacheEvict(value="user" , key = "#id")
            }
    )
    public void update(Long id) {
        userDao.update(id);
    }
}
           

④. 測試類

編輯

src/test/java/com/itheima/cache/test/UserServiceTest.java

檔案:

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {

    @Autowired
    private UserService userService;


    /**
     * 根據id查詢使用者
     */
    @Test
    public void testFindById() {
        for (int i = 0; i < 5; i++) {
            User user = userService.findById(1l);
            System.out.println(user);
        }
    }

    @Test
    public void testFindById2() {
        User user = userService.findById(2l);
        System.out.println(user);
    }

    //更新:更新資料庫,删除redis中的緩存資料
    @Test
    public void testUpdate() {
        userService.update(2l);
    }
}
           

3. 常用注解

  • @Cacheable:
    • 注解表示這個方法有了緩存的功能,方法的傳回值會被緩存下來,下一次調用該方法前,會去檢查是否緩存中已經有值,如果有就直接傳回,不調用方法。如果沒有,就調用方法,然後把結果緩存起來。
  • @CachePut:
    • 加了@CachePut注解的方法,會把方法的傳回值put到緩存裡面緩存起來,供其它地方使用。
  • @CacheEvit:
    • 使用了CacheEvict注解的方法,會清空指定緩存。
  • @Caching:
    • Java代碼中,同個方法,一個相同的注解隻能配置一次。如若操作多個緩存,可以使用@Caching

4. 使用SpringCache優化視訊清單

①. 開啟緩存

編輯

tanhua-app-server/src/main/java/com/tanhua/server/AppServerApplication.java

檔案:

//啟動類
// @SpringBootApplication
@SpringBootApplication(exclude = {
        MongoAutoConfiguration.class,
        MongoDataAutoConfiguration.class
}) //排除mongo的自動配置
@EnableCaching  //開啟緩存
public class AppServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(AppServerApplication.class,args);
    }
}
           

②. 配置注解

編輯

tanhua-app-server/src/main/java/com/tanhua/server/service/SmallVideosService.java

檔案:

@Service
public class SmallVideosService {

    @Autowired
    private FastFileStorageClient client;

    @Autowired
    private FdfsWebServer webServer;

    @Autowired
    private OssTemplate ossTemplate;

    @DubboReference
    private VideoApi videoApi;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @DubboReference
    private UserInfoApi userInfoApi;

    /**
     * 上傳視訊
     * @param videoThumbnail 視訊封面圖檔
     * @param videoFile 視訊檔案
     */
    @CacheEvict(value="videoList",allEntries = true) // 清空緩存
    public void saveVideos(MultipartFile videoThumbnail, MultipartFile videoFile) throws IOException {
        if(videoThumbnail.isEmpty() || videoFile.isEmpty()) {
            throw new BusinessException(ErrorResult.error());
        }
        // 1. 将視訊上傳到FastDFS,擷取通路url
        String filename = videoFile.getOriginalFilename();
        filename = filename.substring(filename.lastIndexOf(".") + 1);
        StorePath storePath = client.uploadFile(videoFile.getInputStream(), videoFile.getSize(), filename, null);
        String videoUrl = webServer.getWebServerUrl() + storePath.getFullPath();

        // 2. 将封面圖檔上傳到阿裡雲OSS,擷取通路的url
        // String upload = ossTemplate.upload(videoThumbnail.getOriginalFilename(), videoThumbnail.getInputStream());
        // // !!! 阿裡雲OSS收費, 這裡暫時跳過
        String upload = "https://img0.baidu.com/it/u=8672387,2873147723&fm=253&fmt=auto&app=138&f=JPEG";

        // 3. 建構Videos對象
        Video video = new Video();
        video.setUserId(UserHolder.getUserId());
        video.setVideoUrl(videoUrl);
        video.setPicUrl(upload);
        video.setText("我就是我, 是顔色不一樣的煙火");

        // 4. 調用API儲存資料
        String videoId = videoApi.save(video);
        if(StringUtils.isEmpty(videoId)) {
            throw new BusinessException(ErrorResult.error());
        }
    }

    // 查詢視訊清單
    @Cacheable(
            value="videos",
            key = "T(com.tanhua.server.interceptor.UserHolder).getUserId()+'_'+#page+'_'+#pagesize")  //userid _ page_pagesize
    public PageResult queryVideoList(Integer page, Integer pagesize) {
        // 1. 查詢redis資料
        String redisKey = Constants.VIDEOS_RECOMMEND + UserHolder.getUserId();
        String redisValue = redisTemplate.opsForValue().get(redisKey);

        // 2. 判斷redis資料是否存在
        List<Video> list = new ArrayList<>();
        int redisPages = 0;
        if(!StringUtils.isEmpty(redisValue)) {
            // 3. 如果redis資料存在,根據VID查詢資料
            String[] values = redisValue.split(",");
            // 4. 判斷redis中資料是否滿足本次分頁條數
            if((page - 1) * pagesize < values.length) {
                List<Long> vids = Arrays.stream(values).skip((page - 1) * pagesize).limit(pagesize)
                        .map(e -> Long.valueOf(e))
                        .collect(Collectors.toList());
                list = videoApi.findMovementsByPids(vids);
            }
            redisPages = PageUtil.totalPage(values.length, pagesize);
        }

        // 5. 如果redis資料不存在, 分頁查詢視訊資料
        if(list.isEmpty()) {
            // page的計算規則, 傳入的頁碼, --redis查詢的總頁數
            list = videoApi.queryVideoList(page - redisPages, pagesize);
        }

        // 6. 提取視訊清單中所有的使用者id
        List<Long> userIds = CollUtil.getFieldValues(list, "userId", Long.class);

        // 7. 查詢使用者資訊
        Map<Long, UserInfo> map = userInfoApi.findByIds(userIds, null);

        // 8. 建構傳回值
        List<VideoVo> vos = new ArrayList<>();
        for (Video video : list) {
            UserInfo info = map.get(video.getUserId());
            if(info != null) {
                VideoVo vo = VideoVo.init(info, video);
                vos.add(vo);
            }
        }
        return new PageResult(page, pagesize, 0l, vos);
    }
}
           

③. 設定失效時間

編輯

tanhua-app-server/src/main/java/com/tanhua/server/config/RedisCacheConfig.java

檔案:

@Configuration
public class RedisCacheConfig {

    //設定失效時間
    private static final Map<String, Duration> cacheMap;

    static {
        cacheMap = ImmutableMap.<String, Duration>builder().put("videos", Duration.ofSeconds(30L)).build();
    }

    //配置RedisCacheManagerBuilderCustomizer對象
    @Bean
    public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
        return (builder) -> {
            //根據不同的cachename設定不同的失效時間
            for (Map.Entry<String, Duration> entry : cacheMap.entrySet()) {
                builder.withCacheConfiguration(entry.getKey(),
                        RedisCacheConfiguration.defaultCacheConfig().entryTtl(entry.getValue()));
            }
        };
    }
}