探花交友_第9章_小视频方案(新版)

news2024/10/1 23:34:20

探花交友_第9章_小视频方案(新版)

文章目录

  • 探花交友_第9章_小视频方案(新版)
  • 1. 我的访客
    • 1.1 需求分析
      • 1.1.1 功能说明
      • 1.1.2 数据库表
    • 1.2 记录访客数据
      • tanhua-model
      • tanhua-app-server
      • tanhua-dubbo-interface
      • tanhua-dubbo-mongo
    • 1.3 首页谁看过我
      • 需求分析
      • tanhua-model
      • tanhua-app-server
        • MovementController
        • MovementService
      • tanhua-dubbo-interface
      • tanhua-dubbo-mongo
  • 2. 小视频功能说明
  • 3. FastDFS
    • 3.1. FastDFS是什么?
    • 3.2. 工作原理
      • 3.2.1. 文件的上传
      • 3.2.2. 文件的下载
    • 3.3. FastDFS环境搭建
      • 3.3.1. 搭建服务
      • 3.3.2. 入门案例
        • 导入依赖
        • application.yml
        • 测试
  • 4. 发布小视频
    • 4.1 需求分析
      • 表结构
      • 操作步骤
      • 接口说明
    • 4.2. 发布视频
      • tanhua-model
      • tanhua-app-server
        • SmallVideoController
        • SmallVideosService
      • tanhua-dubbo-interface
      • tanhua-dubbo-mongo
      • 测试问题
  • 5. 小视频列表
    • 5.1 步骤分析
    • 5.2 代码实现
      • tanhua-model
      • tanhua-app-server
        • SmallVideoController
        • SmallVideosService
      • tanhua-dubbo-interface
      • tanhua-dubbo-mongo
      • 问题分析与解决
        • 测试问题
        • 解决方法
  • 6. SpringCache
    • 6.1. 重要概念
    • 6.2. 入门案例
      • 6.2.1 导入依赖
      • 6.2.2 开启缓存支持
      • 6.2.3 编写UserInfoService
      • 6.2.4 缓存@Cacheable
      • 6.2.5 清除@CacheEvict
    • 6.3. 视频列表缓存处理
    • 6.4. 发布视频清空缓存

  • 小视频功能说明
  • FastDFS环境搭建
  • 小视频的功能实现

1. 我的访客

1.1 需求分析

1.1.1 功能说明

在这里插入图片描述

首页我的功能卡片中,包含我的访客数据

  • 其他用户访问我的动态,视频,个人信息时,需要记录访客数据到数据库中
  • 首页端展示最新的访客用户,只显示5条。
  • 我的功能中,展示全部历史访客记录

1.1.2 数据库表

visitors集合

{
    "_id": ObjectId("60176f785098b26d1c3ff043"),
    "date": NumberLong("1612148600271"),
    "userId": NumberLong("99"),
    "visitorUserId": NumberLong("2"),
    "score": 72,
    "from": "首页",
    "visitDate": "20210101"
}

注意 : 记录访客数据, 在查询用户详情的接口中记录即可, 不需要额外的接口提供

1.2 记录访客数据

APP用户点击查看推荐用户详情之后需要记录访客信息 , 数据保存在MongoDB中 , 存储结构如下

tanhua-model

tanhua-model项目中定义Visitors对象

package com.tanhua.model.mongo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.core.mapping.Document;

@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; //得分
}

tanhua-app-server

项目中,用户对用户的所有访问信息都应该记录到数据库中,学习阶段仅以访问今日佳人详情接口为案例添加数据。

修改tanhua-app-server中的查看佳人详情功能,保存访客数据

在这里插入图片描述

tanhua-dubbo-interface

tanhua-dubbo-interface模块创建VisitorsApi接口, 定义保存访客数据的接口

public interface VisitorsApi {

    /**
     * 保存访客数据
     */
    String save(Visitors visitors);
}

tanhua-dubbo-mongo

tanhua-dubbo-mongo模块创建VisitorsApiImpl实现类, 实现保存访客数据的接口

注意 : 访客数据一天只记录一次

@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);
        }
    }
}

1.3 首页谁看过我

需求分析

接口文档 : http://118.25.197.221:3000/project/10/interface/api/77

在这里插入图片描述

tanhua-model

tanhua-model项目中定义封装数据的vo对象

package com.tanhua.model.vo;

import com.tanhua.model.domain.UserInfo;
import com.tanhua.model.mongo.Visitors;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.BeanUtils;

@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;
    }
}

tanhua-app-server

修改tanhua-app-server模块中MovementControllerMovementService

MovementController

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

MovementService

//首页-访客列表
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.queryMyVisitors(date,UserHolder.getUserId());
    if(CollUtil.isEmpty(list)) {
        return new ArrayList<>();
    }
    //3、提取用户的id
    List<Long> userIds = CollUtil.getFieldValues(list, "visitorUserId", Long.class);
    //4、查看用户详情
    Map<Long, UserInfo> map = userInfoApi.findByIds(userIds, 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;
}

tanhua-dubbo-interface

tanhua-dubbo-interface项目在VisitorsApi中定义查询访客列表的方法

/**
 * 查询我的访客数据,存在2种情况:
 * 1. 我没有看过我的访客数据,返回前5个访客信息
 * 2. 之前看过我的访客,从上一次查看的时间点往后查询5个访客数据
 * @param date 上一次查询时间
 * @return
 */
List<Visitors> queryMyVisitors(Long date, Long userId);

tanhua-dubbo-mongo

tanhua-dubbo-mongo项目中VisitorsApiImpl中实现查询访客列表的方法

//查询首页访客列表
public List<Visitors> queryMyVisitors(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);
}

2. 小视频功能说明

小视频功能类似于抖音. 快手小视频的应用,用户可以上传小视频进行分享,也可以浏览查看别人分享的视频,并且可以对视频评论和点赞操作。

  1. 视频发布(视频:容量大,视频存储到什么位置?)

  2. 查询视频列表(问题:数据库表)

  3. 关注视频作者

  4. 视频播放(客户端获取视频的URL地址,自动的播放)

效果:

在这里插入图片描述

查看详情:

在这里插入图片描述

评论:

在这里插入图片描述

点赞:

在这里插入图片描述

3. FastDFS

视频存储

  • 阿里云OSS(视频简单,贵!!!)
  • 自建存储系统

对于小视频的功能的开发,核心点就是:存储 + 推荐 + 加载速度 。

  • 对于存储而言,小视频的存储量以及容量都是非常巨大的
    • 所以我们选择自己搭建分布式存储系统 FastDFS进行存储
  • 对于推荐算法,我们将采用多种权重的计算方式进行计算
  • 对于加载速度,除了提升服务器带宽外可以通过CDN的方式进行加速,当然了这需要额外购买CDN服务

3.1. FastDFS是什么?

FastDFS是分布式文件系统。使用 FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传. 下载等服务。

3.2. 工作原理

FastDFS 架构包括 Tracker server 和 Storage server。客户端请求 Tracker server 进行文件上传. 下载,通过 Tracker server 调度最终由 Storage server 完成文件上传和下载。

Tracker server 作用是负载均衡和调度,通过 Tracker server 在文件上传时可以根据一些策略找到 Storage server 提供文件上传服务。可以将 tracker 称为追踪服务器或调度服务器。

Storage server 作用是文件存储,客户端上传的文件最终存储在 Storage 服务器上,Storage server 没有实现自己的文件系统而是利用操作系统的文件系统来管理文件。可以将storage称为存储服务器。

在这里插入图片描述

每个 tracker 节点地位平等。收集 Storage 集群的状态。

Storage 分为多个组,每个组之间保存的文件是不同的。每个组内部可以有多个成员,组成员内部保存的内容是一样的,组成员的地位是一致的,没有主从的概念。

3.2.1. 文件的上传

在这里插入图片描述

客户端上传文件后存储服务器将文件 ID 返回给客户端,此文件 ID 用于以后访问该文件的索引信息。文件索引信息包括:组名,虚拟磁盘路径,数据两级目录,文件名。

3.2.2. 文件的下载

在这里插入图片描述

客户端下载请求到Tracker服务,Tracker返回给客户端storage的信息,客户端根据这些信息进行请求storage获取到文件。

3.3. FastDFS环境搭建

​ 企业中搭建FastDFS是一个比较繁琐和复杂的过程(多个服务器之间的配合和配置等,专业的人员搭建),但是在学习阶段。由于所有的组件全部配置到linux虚拟机,已docker运行。所以linux的内存有要求(运行的过程中,可能会出现fastdfs的容器,启动之后自动关闭,表示虚拟机内存不足,适当的扩大内存),学习环境中使用一台调度服务器,一台存储服务器

3.3.1. 搭建服务

我们使用docker进行搭建。目前所有的组件全部以docker的形式配置

#进入目录
cd /root/docker-file/fastdfs/
#启动
docker-compose up -d
#查看容器
docker ps -a
#FastDFS占用虚拟机资源较多,如果启动时发现Tracker或者Storage没有正常启动,
#使用如下命令 重启即可
#docker-compose restart

FastDFS调度服务器地址:192.168.136.160:22122
FastDFS存储服务器地址:http://192.168.136.160:8888/

3.3.2. 入门案例

导入依赖

找到tanhua-app-server的pom文件,打开fastdfs的依赖如下

<dependency>
    <groupId>com.github.tobato</groupId>
    <artifactId>fastdfs-client</artifactId>
    <version>1.26.7</version>
    <exclusions>
        <exclusion>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
        </exclusion>
    </exclusions>
</dependency>

application.yml

找到tanhua-app-server的application.yml,添加FastDFS的配置

# 分布式文件系统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编写测试类,测试文件上传到FastDFS

package com.tanhua.server.test;

import com.github.tobato.fastdfs.domain.conn.FdfsWebServer;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.tanhua.server.TanhuaServerApplication;
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.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = TanhuaServerApplication.class)
public class TestFastDFS {

    //从调度服务器获取,一个目标存储服务器,上传
    @Autowired
    private FastFileStorageClient client;

    @Autowired
    private FdfsWebServer webServer;// 获取存储服务器的请求URL

    @Test
    public void testFileUpdate() throws FileNotFoundException {
 		//1. 指定文件
        File file = new File("D:\\1.jpg");
		//2. 文件上传
        StorePath path = client.uploadFile(new FileInputStream(file),
                file.length(), "jpg", null);
		//3. 拼接访问路径
        String url = webServer.getWebServerUrl() + path.getFullPath();
    }
}

存储服务器:

  • 在线的存储服务器:阿里云OSS
  • 自己搭建分布式的存储服务器:fastdfs

4. 发布小视频

4.1 需求分析

表结构

{
    "_id" : ObjectId("5fa60707ed0ad13fa89925cc"),
    "vid" : NumberLong(1),
    "userId" : NumberLong(1),
    "text" : "我就是我不一样的烟火~",
    "picUrl" : "https://tanhua-dev.oss-cn-zhangjiakou.aliyuncs.com/images/video/video_1.png",
    "videoUrl" : "https://tanhua-dev.oss-cn-zhangjiakou.aliyuncs.com/images/video/1576134125940400.mp4",
    "created" : NumberLong(1604716296066),
    "likeCount" : 0,
    "commentCount" : 0,
    "loveCount" : 0,
    "_class" : "com.tanhua.domain.mongo.Video"
}

在这里插入图片描述

操作步骤

在这里插入图片描述

接口说明

接口文档 : http://192.168.136.160:3000/project/19/interface/api/214

在这里插入图片描述

4.2. 发布视频

tanhua-model

tanhua-model项目中定义小视频的实体类Video

package com.tanhua.model.mongo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.core.mapping.Document;

@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; //喜欢数
}

tanhua-app-server

tanhua-app-server中的创建SmallVideoControllerSmallVideosService

SmallVideoController

创建控制器类, 定义控制方法接收客户端上传小视频请求

package com.tanhua.server.controller;

import com.tanhua.model.vo.PageResult;
import com.tanhua.server.service.SmallVideosService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

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

    @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);
    }
}

SmallVideosService

创建SmallVideosService编写代码完成小视频上传逻辑

package com.tanhua.server.service;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.github.tobato.fastdfs.domain.conn.FdfsWebServer;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.tanhua.autoconfig.template.OssTemplate;
import com.tanhua.dubbo.api.mongo.VideoApi;
import com.tanhua.model.mongo.Video;
import com.tanhua.server.interceptor.UserHolder;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

@Service
public class SmallVideosService {

    @Autowired
    private OssTemplate ossTemplate;

    @Autowired
    private FastFileStorageClient fastFileStorageClient;

    @Autowired
    private FdfsWebServer webServer;

    @DubboReference
    private VideoApi videoApi;

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 发布小视频
     */
    public void saveVideos(MultipartFile videoThumbnail, MultipartFile videoFile) throws         if(videoFile.isEmpty() || videoThumbnail.isEmpty()) {
            throw new BusinessException(ErrorResult.error());
        }
        //1、将视频上传到FastDFS,获取访问URL
        String filename = videoFile.getOriginalFilename();  // abc.mp4
        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 imageUrl = ossTemplate.upload(videoThumbnail.getOriginalFilename(), videoThumbnail.getInputStream());
        //3、构建Videos对象
        Video video = new Video();
        video.setUserId(UserHolder.getUserId());
        video.setPicUrl(imageUrl);
        video.setVideoUrl(videoUrl);
        video.setText("我就是我,不一样的烟火");
        //4、调用API保存数据
        String videoId = videoApi.save(video);
        if(StringUtils.isEmpty(videoId)) {
            throw new BusinessException(ErrorResult.error());
        }
	}
}

tanhua-dubbo-interface

tanhua-dubbo-interface项目中创建VideoApi接口 , 定义保存小视频的方法

package com.tanhua.dubbo.api.mongo;


import com.tanhua.model.mongo.Video;

public interface VideoApi {

    /**
     * 保存小视频
     */
    void save(Video video);
}

tanhua-dubbo-mongo

tanhua-dubbo-mongo项目中创建VideoApiImpl接口 , 实现保存小视频的方法

@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工程进行文件上传,默认支持最大的文件是1M。为了解决这个问题,需要在application.yml中配置文件限制

如果上传视频,会导致异常,是因为请求太大的缘故:

在这里插入图片描述

tanhua-server工程的application.yml中添加解析器,配置请求文件和请求体 , 设置文件大小

spring:
  servlet:
    multipart:
      max-file-size: 30MB
      max-request-size: 30MB

5. 小视频列表

接口文档 : http://118.25.197.221:3000/project/10/interface/api/227

在这里插入图片描述

5.1 步骤分析

小视频的列表查询的实现需要注意的是,如果有推荐视频,优先返回推荐视频,如果没有,按照时间倒序查询视频表。

在这里插入图片描述

5.2 代码实现

tanhua-model

tanhua-model项目中创建VideoVo封装接口返回数据

package com.tanhua.model.vo;

import com.tanhua.model.domain.UserInfo;
import com.tanhua.model.mongo.Video;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.BeanUtils;

import java.io.Serializable;

@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);
        //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;
    }
}

tanhua-app-server

tanhua-app-server中的SmallVideoControllerSmallVideosService编写方法完成查询功能

SmallVideoController

SmallVideoController定义方法接收请求

/**
* 视频列表
*/
@GetMapping
public ResponseEntity queryVideoList(@RequestParam(defaultValue = "1")  Integer page,
                                     @RequestParam(defaultValue = "10") Integer pagesize) {
    PageResult result = videosService.queryVideoList(page, pagesize);
    return ResponseEntity.ok(result);
}

SmallVideosService

SmallVideosService中实现分页查询小视频列表方法

public PageResult queryVideoList(Integer page, Integer pagesize) {

    //1、查询redis数据
    String redisKey = Constants.VIDEOS_RECOMMEND +UserHolder.getUserId();
    String redisValue = redisTemplate.opsForValue().get(redisKey);
    //2、判断redis数据是否存在,判断redis中数据是否满足本次分页条数
    List<Video> list = new ArrayList<>();
    int redisPages = 0;
    if(!StringUtils.isEmpty(redisValue)) {
        //3、如果redis数据存在,根据VID查询数据
        String[] values = redisValue.split(",");
        //判断当前页的起始条数是否小于数组总数
        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());
            //5、调用API根据PID数组查询动态数据
            list = videoApi.findMovementsByVids(vids);
        }
        redisPages = PageUtil.totalPage(values.length,pagesize);
    }
    //4、如果redis数据不存在,分页查询视频数据
    if(list.isEmpty()) {
        //page的计算规则,  传入的页码  -- redis查询的总页数
        list = videoApi.queryVideoList(page - redisPages, pagesize);  //page=1 ?
    }
    //5、提取视频列表中所有的用户id
    List<Long> userIds = CollUtil.getFieldValues(list, "userId", Long.class);
    //6、查询用户信息
    Map<Long, UserInfo> map = userInfoApi.findByIds(userIds, null);
    //7、构建返回值
    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-dubbo-interface

tanhua-dubbo-interface项目中的VideoApi中定义分页查询小视频列表的方法

//根据vid查询数据列表
List<Video> findMovementsByVids(List<Long> vids);

//分页查询数据列表
List<Video> queryVideoList(int page, Integer pagesize);

tanhua-dubbo-mongo

tanhua-dubbo-mongo项目中的VideoApiImpl中实现分页查询小视频列表的方法

/**
 * 根据vid查询小视频列表
 */
@Override
public List<Video> findMovementsByVids(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().limit(pagesize).skip((page -1) * pagesize)
        .with(Sort.by(Sort.Order.desc("created")));
    return mongoTemplate.find(query,Video.class);
}

问题分析与解决

测试问题

在运行测试时,及其容易出现空指针等异常。

在这里插入图片描述

解决方法

之所以出现这类问题“”或者空指针异常,是由于MongoDB中非关系数据库,不能自动约束检测表关系。我们检查Video数据库表得知。其中有几条数据的发布人是虚拟构造,在用户表中并不存在

在这里插入图片描述

解决思路很简单,删除错误数据即可

6. SpringCache

Spring Cache是Spring提供的通用缓存框架。它利用了AOP,实现了基于注解的缓存功能,使开发者不用关心底层使用了什么缓存框架,只需要简单地加一个注解,就能实现缓存功能了。用户使用Spring Cache,可以快速开发一个很不错的缓存功能。

6.1. 重要概念

名称解释
@Cacheable主要针对方法配置,能够根据方法的请求参数对其进行缓存
@CacheEvict清空缓存

6.2. 入门案例

6.2.1 导入依赖

导入SpringDataRedis的依赖,并在application.yml中配置 (略)

6.2.2 开启缓存支持

然后在启动类注解@EnableCaching开启缓存

@SpringBootApplication
@EnableCaching  //开启缓存
public class DemoApplication{
 
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

6.2.3 编写UserInfoService

package com.tanhua.server.test;

import com.tanhua.domain.db.UserInfo;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class UserInfoService {

    //根据id查询
    public UserInfo queryById(Long userId) {
        //从数据库查询
        UserInfo user = new UserInfo();
        user.setId(userId);
        user.setNickname("ceshi");
        return user;
    }

    //根据id修改
    public void update(Long userId) {
        UserInfo user = new UserInfo();
        user.setId(userId);
        user.setNickname("itcast");
    }
}

6.2.4 缓存@Cacheable

@Cacheable注解会先查询是否已经有缓存,有会使用缓存,没有则会执行方法并缓存。

@Cacheable(value = "user",key = "#userId")
public UserInfo queryById(Long userId) {
    //从数据库查询
    UserInfo user = new UserInfo();
    user.setId(userId);
    user.setNickname("ceshi");
    return user;
}

此处的value是必需的,它指定了你的缓存存放在哪块命名空间。

6.2.5 清除@CacheEvict

@CachEvict 的作用 主要针对方法配置,能够根据一定的条件对缓存进行清空 。

//根据id修改
@CacheEvict(value = "user",key = "#userId")
public void update(Long userId) {
    //修改用户
    UserInfo user = new UserInfo();
    user.setId(userId);
    user.setNickname("itcast");
}

6.3. 视频列表缓存处理

tanhua-app-server修改VideoService,分页列表存入缓存,发布视频删除缓存

//查询视频列表
@Cacheable(
    value="videos",
    key = "T(com.tanhua.server.interceptor.UserHolder).getUserId()+'_'+#page+'_'+#pagesize")  //userid _ page_pagesize
public PageResult queryVideoList(Integer page, Integer pagesize) {
    .....
}

6.4. 发布视频清空缓存

/**
 * 发布小视频
 */
@CacheEvict(value="videos",allEntries = true)  //清空缓存
public void saveVideos(MultipartFile videoThumbnail, MultipartFile videoFile) {
   .....
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/95604.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小学生 C++画图 Go C 编程 第7课 奇异的花朵

第一课 GoC简介和演示 第一课 GoC简介和演示_ahwhjt的博客-CSDN博客_goc输入图形数量 第二课 了解编程环境 第二课 了解编程环境_ahwhjt的博客-CSDN博客_goc编程环境 第三课 基本绘图命令 第三课 基本绘图命令_ahwhjt的博客-CSDN博客_电脑编程的pen.lt 第四课 变量的引入 第…

重写 Nacos 服务发现:多个服务器如何跨命名空间,访问公共服务?

一、问题背景 在开发某个公共应用时&#xff0c;笔者发现该公共应用的数据是所有测试环境&#xff08;假设存在 dev/dev2/dev3&#xff09;通用的。 这就意味着只需部署一个应用&#xff0c;就能满足所有测试环境的需求&#xff1b;也意味着所有测试环境都需要调用该公共应用…

匆匆遭遇猿如意

刚刚收到一条消息&#xff0c;说有一个csdn的猿如意可以测试了&#xff0c;我就下载了一个&#xff0c;根据提示下载了&#xff0c;然后开始体验。 一、ChatGPT 谁让这个东西最近这么热呢&#xff0c;所以&#xff0c;我第一个就体验这个东东了&#xff0c;结果&#xff0c;结…

excel多条件预算:规划求解工具计算多产品最佳效益组合

江南皮革厂生产三种产品&#xff0c;皮鞋、皮手套、皮帽。三种产品需要原材料甲、乙、丙。近期&#xff0c;原材料供应有限制&#xff0c;生产工时也有限制。已知产品单件的用时、用料、利润&#xff0c;求如何组合产品利润最大。 一、加载规划求解工具 规划求解工具位于“数据…

CN域名隐私保护内测收费

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 据站长DNS Admin爆料&#xff0c;CNNIC向西部数码发布通知称&#xff0c;内测的CN域名WHOIS隐私保护服务即将暂停免费政策&#xff0c;继续使用将收取相应服务费用&#xff0c;价格为48元/个/年。 …

电脑重装系统后会闪屏是什么原因

​电脑的显示屏是我们日常使用电脑最常使用的硬件之一&#xff0c;可以说使用电脑必备的就是显示屏了。而显示屏在使用的过程中也会出现各种各样的问题&#xff0c;最近就有不少用户反应自己的电脑出现了黑屏闪烁的问题。 软件原因&#xff1a; 一、检查显示刷新率设置是否正确…

RTMP推流方案总结

由于项目需要 RTMP 推送 H264 数据&#xff0c;在网上查找了下相关的方案&#xff0c;总结一下。 RTMP协议简介 在总结之前&#xff0c;我们先简单介绍一下 RTMP 协议。 RTMP(Real Time Messaging Protocol) 实时消息传送协议是 Adobe Systems 公司为 Flash 播放器和服务器之间…

外卖订餐系统的设计与实现/点餐订餐系统

摘 要 随着外卖订餐在高校越来越普及&#xff0c;传统的电话订餐给顾客跟商家带来不方便,如何使订餐更快速&#xff0c;更方便已成为众多高校学生关注的问题了。本外卖订餐系统是针对高校商家进行具体的需求分析&#xff0c;采用JSP技术和采用SSM框架&#xff0c;MYSQL数据库…

QT制作窗口切换的小程序

QT制作窗口切换的小程序 前言&#xff1a;本次实验是在三个窗口之间自由切换&#xff0c;窗口中播放gif格式的动态图。 让我们先来看看使用到的主要的函数&#xff1a; 一、播放gif格式动态图的函数 QMovie *movie new QMovie("../form/1.gif"); // "../f…

软件测试人到30岁+,在岗位上工作如何破局?

最近一个学生也可以说是朋友&#xff0c;他遇到了一个让他困扰的职场难题&#xff0c;背景如下&#xff1a; 1&#xff09;他们公司准备搞 安全测试 了&#xff0c;现在有人员培训的计划&#xff0c;所以全组有学习安全测试课程的安排。 2&#xff09;他自己目前专职性能测试…

12月17日第壹简报,星期六,农历十一月廿四

12月17日第壹简报&#xff0c;星期六&#xff0c;农历十一月廿四1. 数字人民币试点再扩容&#xff1a;粤苏冀川4省全覆盖&#xff0c;新增济南、南宁、昆明等5座城市。2. 人民币兑美元中间价调降448点至6.9791&#xff0c;降幅创5月27日以来最大。3. 政府出面站台、鼓励居民团购…

DropBox系列-安卓DropBox介绍

前言&#xff1a; 作者本人负责公司的APM监控模块&#xff0c;因为工作的原因&#xff0c;对ANR&#xff0c;crash等流程研究的比较多&#xff0c;最近在打造APM监控平台的时候&#xff0c;顺带对DropBox的实现原理进行了一定的学习和研究&#xff0c;发现了一些妙用&#xff…

Dubbo 1 分布式系统中的相关概念 1.3 架构演进

Dubbo 【黑马程序员Dubbo快速入门&#xff0c;Java分布式框架dubbo教程】 1 分布式系统中的相关概念 文章目录Dubbo1 分布式系统中的相关概念1.3 架构演进1.3.1 架构演进1.3.2 架构演进 - 单体架构1.3.3 架构演进 - 垂直架构1.3.4 架构演进 - 分布式架构1.3.5 架构演进 - SOA…

SPDK块设备

SPDK视角每个App由多个子系统(subsystem)构成&#xff0c;同时每个子系统又包含多个模块(module)&#xff0c;子系统和模块的注入都是可插拔的&#xff0c;通过相关的宏定义声明集成到SPDK组件容器里(其中子系统的注入可通过声明SPDK_SUBSYSTEM_REGISTER&#xff0c;块设备模块…

5G小基站行业市场空间将持续释放 2024年或将迎来建设高峰期

5G小基站行业上游包括硬件资源供应商、软件资源供应商、配套资源供应商&#xff1b;中游主体包括5G小基站设备厂商、5G小基站解决方案服务商&#xff1b;下游则主要是大型写字楼、购物中心、机场等。 数据来源&#xff1a;中国5G小基站市场发展趋势分析与未来前景研究报告&…

弥漫的烟圈-Abaqus涡环仿真与空气大炮

今天简单地讨论一下这个有趣的流体现象-烟圈&#xff0c;并使用Abaqus欧拉分析对它的形成过程进行仿真&#xff0c;揭示其中的力学奥秘。 烟圈 喷气圈的海豚 在流体力学里面&#xff0c;烟圈和水下气圈有个共同的名字&#xff0c;叫做Vortex Ring&#xff0c;即涡环或环形涡流…

腾讯云服务器选购新手教程(新版流程超级详细)

腾讯云服务器选购新手教程(新版流程超级详细)&#xff0c;来详细说下腾讯云服务器购买流程图文详解及购买渠道说明。 腾讯云服务器购买流程 购买腾讯云服务器很简单&#xff0c;首先你需要注册一个腾讯云账号&#xff0c;使用微信或QQ注册即可&#xff0c;很简单。账号注册后&…

git clone 拉取远程仓库

1. git clone 拉取仓库 2. 以 HTTPS 方式拉取仓库 3. 以 SSH 方式拉取仓库 1. git clone 拉取仓库 拉取远程库的默认分支 git clone <repositories> 拉取远程库的指定分支 -b, --branch git clone -b <branch> <repositories> 将远程库拉取到指定目录 git c…

产品销量一直上不去,怎么办,试试这种模式?

裂变营销的本质是以存量带增量&#xff0c;让已有消费者帮你寻找潜在消费者&#xff0c;从而达到快速获客的目的。这种方法成本低、影响持久、效率高&#xff0c;已经成为传统企业转型不可或缺的重要战略&#xff1b;时至今日&#xff0c;许多传统企业&#xff0c;在引流方面碰…

【图像去噪】PM模型图像降噪【含Matlab源码 2107期】

⛄一、PM模型图像降噪简介 为了提高去除噪声和保留细节信息的算法的性能,Peroha等提出以热学中扩散方程式为基础的扩散算法即为PM模型。该模型主要是在经典各向异性扩散方dgi,j,t/dtdiv(d∇g)上提出将其中的扩散系数d用函数控制的扩散系数替代。PM模型为 其中,f(|∇gi,j,t|)是…