简单的来讲,就是模仿抖音的后台 To simply said, this is the backend server of the app - TIK_TOC
聚合项目:
例子,在 Mapper 的Maven中,注入POJO的dependence
<dependencies>
<dependency>
<groupId>com.hptg.tik_toc</groupId>
<artifactId>Tik_Toc_Pojo</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
同样,在POJO的Maven中,注入Common的dependence
<dependencies>
<dependency>
<groupId>com.hptg.tik_toc</groupId>
<artifactId>Tik_Toc_Common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
同时,他们的parent都是最顶层的那个
<parent>
<artifactId>Tik_Toc</artifactId>
<groupId>com.hptg.tik_toc</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
同时,总文件项目project的parent是Spring Boot
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.hptg.tik_toc</groupId>
<artifactId>Tik_Toc</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>Tik_Toc_Common</module>
<module>Tik_Toc_Pojo</module>
<module>Tik_Toc_Mapper</module>
<module>Tik_Toc_Service</module>
<module>Tik_Toc_Api</module>
</modules>
<name>Tik_Toc Server</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starters</artifactId>
<version>1.5.18.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
</project>
-
开发Redis
- 按照视频把Redis搭建好
- 引入RedisOperator.java这个操作Redis的文件
-
我的个人信息接口的开发
-
用户头像上传的套路
-
新建/user/uploadFace Controller,这个Controller接受两个参数,一个是userId(url的Params中获取),另外一个是需要POST的文件(@RequestParam)
-
文件保存逻辑:两个常量,一个是保存在硬盘中的路径,另一个是保存到数据库中的绝对路径
String fileSpace = "/Users/hptg/Documents/Project/Spring/Tik_Toc/Resources"; String uploadPathDB = "/" + userId + "/face";
FileOutputStream 和InputStream分别对应输出到文件夹和客户端中输入进来的文件:
FileOutputStream fileOutputStream = null; InputStream inputStream = null;
将从客户端传来的文件保存到硬盘中:(这个outFile不是文件而是指定位置的文件夹)
fileOutputStream = new FileOutputStream(outFile); inputStream = files[0].getInputStream(); IOUtils.copy(inputStream, fileOutputStream);
-
将这个路径上传到数据库中(需要在Service中实现updateUserInfo的方法):
Users user = new Users(); user.setId(userId); user.setFaceImage(uploadPathDB); //创建新的user instance 只是为了查找出某个userId的user userService.updateUserInfo(user);
然后在updateUserInfo中(创建Example.Criteria,并用内部方法updateByExampleSelective更新):
Example userExample = new Example(Users.class); Example.Criteria criteria = userExample.createCriteria(); criteria.andEqualTo("id", user.getId()); usersMapper.updateByExampleSelective(user, userExample);
-
-
展示图片(先展示用户头像图片)
-
配置WebConfiguration,即配置Tomcat的虚拟目录并映射成web资源以达到图片的展示
@Configuration public class WebMvcConfig extends WebMvcConfigurerAdapter { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/**") .addResourceLocations("classpath:/META-INF/resources/").addResourceLocations("file:/Users/hptg/Documents/Project/Spring/Tik_Toc/Resources/"); }
此时就可以如下访问到图片:(用户名+face+图片名) http://localhost:8081/181219HBR49WA614/face/3236623630a84217bbdc81989a83dea6.png
-
程序中,直接使用上面那个地址来展示图片
-
-
查询用户信息(根据登陆的用户ID查询用户信息然后展示:包括昵称,头像图片等等)
@GetMapping("/query") public IMoocJSONResult query(String userId, String fanId) throws Exception { if (StringUtils.isBlank(userId)) { return IMoocJSONResult.errorMsg("用户id不能为空..."); } Users userInfo = userService.queryUserInfo(userId); UsersVO userVO = new UsersVO(); BeanUtils.copyProperties(userInfo, userVO); return IMoocJSONResult.ok(userVO); }
-
上传视频,文件的上传和存储逻辑上都和上传头像是一样的:
唯一注意的是,上传的格式是headers = "content-type=multipart/form-data"(Postman中选择form-data但不用设置header)
@PostMapping(value = "/upload", headers = "content-type=multipart/form-data") public IMoocJSONResult upload(String userId, String bgmId, double videoSeconds, int videoWidth, int videoHeight, String desc, @ApiParam(value = "短视频", required = true) MultipartFile file) throws Exception
-
videoService中saveVideo
videosMapper.insertSelective(video);
-
用ffmpeg截取视频的第一秒的图:
-
截取一个视频的封面(小程序中的封面图是自动获取的),然后上传和更新数据库:
@Transactional(propagation = Propagation.REQUIRED) @Override public void updateVideo(String videoId, String coverPath) { Videos video = new Videos(); video.setId(videoId); video.setCoverPath(coverPath); videosMapper.updateByPrimaryKeySelective(video); }
-
获取所有的Bgm列表(/bgm/list),这个没什么好讲的,建立一个controller然后用Service去query所有的bgm
@Transactional(propagation = Propagation.SUPPORTS) @Override public List<Bgm> queryBgmList() { return bgmMapper.selectAll(); }
同样获得某一个bgm直接:http://localhost:8081/BGM/TT.mp3, 因为之前已经设置过Tomcat的虚拟路径了
-
编写自定义的mapper:因为在视频展示页面,同时要获取两个东西,视频和用户信息同时展示,所以要自定义mapper,修改query语句来实现关联查询
-
新建自定义的VideoVO,匹配首页展示的数据(其实就是把User Pojo和Video Pojo的一些东西结合起来)
-
新建VideosMapperCustom
/** * @Description: 条件查询所有视频列表 */ public List<VideosVO> queryAllVideos(@Param("videoDesc") String videoDesc,@Param("userId") String userId); /** * @Description: 查询关注的视频 */ public List<VideosVO> queryMyFollowVideos(String userId); /** * @Description: 查询点赞视频 */ public List<VideosVO> queryMyLikeVideos(@Param("userId") String userId); /** * @Description: 对视频喜欢的数量进行累加 */ public void addVideoLikeCount(String videoId); /** * @Description: 对视频喜欢的数量进行累减 */ public void reduceVideoLikeCount(String videoId);
-
新建VideosMapperCustom.xml,这里进行SQL语句的修改
<select id="queryAllVideos" resultMap="BaseResultMap" parameterType="String"> select v.*,u.face_image as face_image,u.nickname as nickname from videos v left join users u on u.id = v.user_id where 1 = 1 // 这个就这样子了我也不知道为什么 <if test=" videoDesc != null and videoDesc != '' "> and v.video_desc like '%${videoDesc}%' </if> <if test=" userId != null and userId != '' "> and v.user_id = #{userId} </if> and v.status = 1 order by v.create_time desc </select>
-
分页查询,使用的是外部的一个PageHelper.java(com.github.pagehelper),自定义的一个PageResult(用于展示页面的信息封装)
-
查询视频的接口
-
-
实现HandlerInterceptor的接口(preHandle,postHandle,afterCompletion),return true or false
/** * 拦截请求,在controller调用之前 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception { ... } /** * 请求controller之后,渲染视图之前 */ @Override public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception { ... } /** * 请求controller之后,视图渲染之后 */ @Override public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception { ... }
-
对自定义的VideoCustomMapper改造:Java 文件新增addVideoLikeCount,xml文件新增相关sql语句
public void addVideoLikeCount(String videoId);
<update id="addVideoLikeCount" parameterType="String"> update videos set like_counts=like_counts+1 where id=#{videoId} </update>