听书项目(一)

内容来源

尚硅谷知识星球,精选项目,只记录开发中的优点功能,并不涉及侵权问题,若有侵权联系作者删除。

项目背景

随着智能手机和高速互联网的普及,人们开始寻求更便捷的方式来获取信息和娱乐。有声书的出现使得人们可以在旅途中、跑步时、做家务时等各种场景下,以更加灵活的方式享受阅读。
在过去,有声书主要是由专业的演员朗读,制作成录音带或CD。但随着数字化媒体的发展,听书软件应运而生,为用户提供了更多选择,包括自助出版的有声书和多样化的内容。
意义:

  1. 便捷性:听书软件使得阅读不再局限于纸质书籍,用户可以通过手机等设备在任何时间、任何地点收听有声书,节省了携带实体书的麻烦。
  2. 多样化内容:听书软件提供了广泛的有声书选择,涵盖了各种类型的图书、小说、杂志、教育内容等。这样的多样性使得用户能够根据个人兴趣和需求选择内容。
  3. 阅读体验:通过专业的朗读演员和音效制作,听书软件可以提供更加生动、有趣的阅读体验,有助于吸引更多读者,尤其是那些不太喜欢阅读纸质书籍的人。
  4. 辅助功能:听书软件通常还具备一些辅助功能,如调整朗读速度、书签功能、字幕显示等,有助于提高可访问性,使得视力受损或其他障碍的用户也能轻松阅读。
  5. 支持作家和内容创作者:听书软件为作家和内容创作者提供了另一种传播作品的渠道,有助于扩大影响力和读者群。
  6. 学习工具:听书软件也可以用作学习工具,提供学术教材、外语学习材料等,帮助用户在学习过程中更好地理解和吸收知识。
    总的来说,听书软件的开发推动了阅读体验的数字化和个性化,为用户提供了更加便捷、多样化的阅读方式,也促进了作家和内容创作者的创作和传播。

项目技术栈

  • SpringBoot:简化Spring应用的初始搭建以及开发过程
  • SpringCloud:基于Spring Boot实现的云原生应用开发工具,SpringCloud使用的技术:(Spring Cloud Gateway、Spring Cloud Task和Spring Cloud Feign等)
  • SpringBoot+SpringCloudAlibaba(Nacos,Sentinel)+Cloud OpenFeign
  • MyBatis-Plus:持久层框架,也依赖mybatis
  • Redis:内存做缓存
  • Redisson:基于redis的Java驻内存数据网格 - 框架;操作redis的框架
  • MongoDB: 分布式文件存储的数据库
  • Kafka:消息中间件;大型分布式项目是标配;分布式事务最终一致性
  • ElasticSearch+Kibana+Logstash 全文检索服务器+可视化数据监控:检索
  • ThreadPoolExecutor+CompletableFuture:线程池来实现异步操作,提高效率
  • xxl-Job: 分布式定时任务调用中心
  • Knife4J/YAPI:Api接口文档工具
  • MinIO(私有化对象存储集群):分布式文件存储 类似于OSS(公有)
  • 微信支付:
  • MySQL:关系型数据库 {shardingSphere-jdbc 进行读写分离; 分库,分表}
  • Lombok: 实体类的中get/set 生成的jar包
  • natapp:内网穿透
  • Docker:容器化技术; 生产环境Redis(运维人员);快速搭建环境Docker run
  • Git:代码管理工具;git使用,拉代码、提交、推送、合并、冲突解决
    前端技术栈
  • UniApp
  • Vue3全家桶
  • TypeScript
  • Grace-UI
  • Uni-UI
  • uniapp-axios-adapter

专辑模块

设置专辑可以帮助用户匹配适合的声音集合进行收听。

功能
  1. 先获取到专辑分类
  2. 文件上传
  3. 保存专辑
实现

基础增删改查

优化点

使用CompletableFuture 再插入专辑属性的时候进行异步提交,缩短响应时间。
使用Kafka进行上架,下架提醒操作。
使用Minio进行储存封面。
使用MybatisPlus中的batch进行批量保存。

文件上传

MinIO介绍
MinIO 是一个基于 Apache License v3.0 开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。
MinIO 是一个非常轻量的服务,可以很简单的和其他应用的结合,类似 NodeJS, Redis 或者 MySQL。
https://docs.min.io/ 英文
特点
· 高性能:作为高性能对象存储,在标准硬件条件下它能达到55GB/s的读、35GB/s的写速率
· 可扩容:不同MinIO集群可以组成联邦,并形成一个全局的命名空间,并跨越多个数据中心
· 云原生:容器化、基于K8S的编排、多租户支持
· Amazon S3兼容:Minio使用Amazon S3 v2 / v4 API。可以使用Minio SDK,Minio Client,AWS SDK和AWS CLI访问Minio服务器。
· 可对接后端存储: 除了Minio自己的文件系统,还支持DAS、 JBODs、NAS、Google云存储和Azure Blob存储。
· SDK支持: 基于Minio轻量的特点,它得到类似Java、Python或Go等语言 的sdk支持
· Lambda计算: Minio服务器通过其兼容AWS SNS / SQS的事件通知服务触发Lambda功能。支持的目标是消息队列,如Kafka,NATS,AMQP,MQTT,Webhooks以及Elasticsearch,Redis,Postgres和MySQL等数据库。
· 有操作页面
· 功能简单: 这一设计原则让MinIO不容易出错、更快启动
· 支持纠删码:MinIO使用纠删码、Checksum来防止硬件错误和静默数据污染。在最高冗余度配置下,即使丢失N/2的磁盘也能恢复数据!
存储机制
Minio使用纠删码erasure code和校验和checksum。 即便丢失一半数量(N/2)的硬盘,仍然可以恢复数据。纠删码是一种恢复丢失和损坏数据的数学算法
上传代码:

    public Result fileUpload(MultipartFile file) throws Exception {if (file == null) {return Result.fail("文件为空");}
//        文件大小监测if ((file.getSize() / 1024 / 1024) > 100) {return Result.fail("文件大小超出限制");}//  声明一个url 地址String url = "";
//        读取nacos的配置文件MinioClient minioClient = MinioClient.builder().endpoint(minioConstantProperties.getEndpointUrl()).credentials(minioConstantProperties.getAccessKey(), minioConstantProperties.getSecreKey()).build();// 判断桶是否存在。boolean found =minioClient.bucketExists(BucketExistsArgs.builder().bucket(minioConstantProperties.getBucketName()).build());if (!found) {// 如果不存在,则创建minioClient.makeBucket(MakeBucketArgs.builder().bucket(minioConstantProperties.getBucketName()).build());} else {//  这个桶已经存在.System.out.println("Bucket " + minioConstantProperties.getBucketName() + " already exists.");}//  生成文件名。String fileName = UUID.randomUUID().toString().replaceAll("-", "") + "." + FilenameUtils.getExtension(file.getOriginalFilename());//  调用上传方法.minioClient.putObject(PutObjectArgs.builder().bucket(minioConstantProperties.getBucketName()).object(fileName).stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build());//  拼接urlurl = minioConstantProperties.getEndpointUrl() + "/" + minioConstantProperties.getBucketName() + "/" + fileName;System.out.println(url);//  返回图片地址return Result.ok(url);}

优化点:用户更换图片的时候,我们可以先插入新图片后,再删除旧的图片。
示例:

try {minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());String url = trackInfo1.getMediaUrl();url = url.substring(url.lastIndexOf('/') + 1);minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(url).build());
} catch (Exception e) {throw new RuntimeException("删除失败");
}

声音模块

给对应专辑添加声音。一对多关系
上传声音,这个位置依旧采用Minio进行储存,如果想要使用更专业的内容的话,可以使用腾讯云服务,具体操作如下:
需要开通腾讯云服务 https://cloud.tencent.com/
快速介入流程:云点播 快速入门-文档中心-腾讯云 (tencent.com)
微信扫码登录:
关注公众号:
搜索云点播:
微信认证:
实名认证:
立即开通服务:
右边:点击访问管理

实现类 云点播 Java SDK-开发指南-文档中心-腾讯云 (tencent.com) Java 语言实现声音上传功能API

云API生成密钥:https://cloud.tencent.com/document/product/1278/85305
在这里插入图片描述

访问密钥:

package com.atguigu.tingshu.album.service.impl;import com.atguigu.tingshu.album.config.VodConstantProperties;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.util.UploadFileUtil;
import com.qcloud.vod.VodUploadClient;
import com.qcloud.vod.model.VodUploadRequest;
import com.qcloud.vod.model.VodUploadResponse;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;import java.util.HashMap;
import java.util.Map;@Service
public class VodServiceImpl implements VodService {@Autowiredprivate VodConstantProperties vodConstantProperties;@SneakyThrows@Overridepublic Map<String, Object> uploadTrack(MultipartFile file) {//  声音上传临时目录:String tempPath = UploadFileUtil.uploadTempPath(vodConstantProperties.getTempPath(), file);//  创建上传声音客户端VodUploadClient client = new VodUploadClient(vodConstantProperties.getSecretId(), vodConstantProperties.getSecretKey());//  构建上传请求对象VodUploadRequest request = new VodUploadRequest();//  设置视频本地地址request.setMediaFilePath(tempPath);//  指定任务流//  request.setProcedure(vodConstantProperties.getProcedure());//  调用上传方法VodUploadResponse response = client.upload(vodConstantProperties.getRegion(), request);//  创建map 对象HashMap<String, Object> map = new HashMap<>();map.put("mediaFileId",response.getFileId());map.put("mediaUrl",response.getMediaUrl());//  返回map 数据return map;}
}

优化

语法优化

使用Mysql max函数 if 函数优化sql代码,使得sql代码更加高效易懂。
用法:
IF(条件, 真时返回值, 假时返回值)
sql使用:

 max(if(ts.stat_type = '0701', ts.stat_num, 0)) playStatNum

从数据表中获取 stat_num 字段的最大值,其中 stat_type 字段的值等于 ‘0701’。如果 stat_type 不等于 ‘0701’,则将 stat_num 的值视为 0。
max(if(ts.stat_type = ‘0701’, ts.stat_num, 0)) 是一个聚合函数,它会计算满足条件的 stat_num 的最大值。
如果某一行的 stat_type 等于 ‘0701’,则取其 stat_num 的值;否则取 0。然后遍历所有的stat_type找出最大的

避免问题
mysql索引失效

%数据% 数据量多的话,这样搜索会出现问题,这个时候我们就可以采用elstaic搜索引擎解决这个问题。

用户登录

微服中,每一个服务都是独立的,此时我们对于用户请求标识的储存需要进行一个新的设置
当用户在查询专辑列表的时候,就应该让用户登录,所以在此我们自定义一个注解来表示访问此功能时必须要登录!
思路:

  • 编写一个自定义注解:GuiGuLogin,使用这个注解去拦截,当用户未登录的时候,查看专辑或声音列表时,需要给前端发起一个提示信息。前端根据这个提示信息[ResultCodeEnum.LOGIN_AUTH],就能够跳转到登录页面!
  • 在网关中设置拦截器,服务后续请求中携带这个token或者其他内容调用下一个服务。

自定义注解

1.书写注解
package com.atguigu.tingshu.common.login;
import java.lang.annotation.*;@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface GuiGuLogin {/*** 是否必须要登录* @return*/boolean required() default true;
}
2.书写切面类

切面类 GuiGuLoginAspect 中要获取到请求对象HttpServletRequest,通过这个对象获取到用户登录时存储的token 数据,这样才能判断用户是否登录。
RequestContextHolder 类持有上下文的 Request容器。
主要是为了获取token值

//  获取请求对象
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
//  转化为ServletRequestAttributes
ServletRequestAttributes sra = (ServletRequestAttributes) requestAttributes;
//  获取到HttpServletRequest 对象
HttpServletRequest request = sra.getRequest();
//	获取到HttpServletResponse 对象
HttpServletResponse response = sra.getResponse();

request 和 response 如何与 当前进行挂钩的?看底层源码
首先分析 RequestContextHolder这个类,里面有两个ThreadLocal 保存当前线程下的request

public abstract class RequestContextHolder {// 得到存储进去的requestprivate static final ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal("Request attributes");//可被子线程继承的requestprivate static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder = new NamedInheritableThreadLocal("Request context");
}  

再看getRequestAttributes() 方法,相当于直接获取ThreadLocal里面的值,这样就保证了每一次获取到的Request是该请求的request.

@Nullable
public static RequestAttributes getRequestAttributes() {RequestAttributes attributes = (RequestAttributes)requestAttributesHolder.get();if (attributes == null) {attributes = (RequestAttributes)inheritableRequestAttributesHolder.get();}return attributes;
}

request和response等是什么时候设置进去的?
springMVC 核心类 DispatcherServlet 继承关系

  1. HttpServletBean 进行初始化工作
  2. FrameworkServlet 初始化 WebApplicationContext,并提供service方法预处理请求
  3. DispatcherServlet 具体分发处理.
    那么就可以在FrameworkServlet查看到该类重写了service(),doGet(),doPost()…等方法,这些实现里面都有一个预处理方法processRequest(request, response);,所以定位到了我们要找的位置
    查看processRequest(request, response);的实现,具体可以分为三步:
  4. 获取上一个请求的参数
  5. 重新建立新的参数
  6. 设置到XXXContextHolder
  7. 父类的service()处理请求
  8. 恢复request
  9. 发布

自定义切面类

思路,判断注解参数是必须登录吗,如果是的话,检查token是否为空,不为空进行数据读取,储存数据在AuthContextHolder(原理还是ThreadLocal储存)

@Aspect
@Component
public class GuiGuLoginAspect {@Autowiredprivate RedisTemplate redisTemplate;@SneakyThrows@Around("execution(* com.atguigu.tingshu.*.api.*.*(..)) && @annotation(guiGuLogin)")public Object loginAspect(ProceedingJoinPoint point,GuiGuLogin guiGuLogin){//  获取请求对象RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();//  转化为ServletRequestAttributesServletRequestAttributes sra = (ServletRequestAttributes) requestAttributes;//  获取到HttpServletRequestHttpServletRequest request = sra.getRequest();String token = request.getHeader("token");//  判断是否需要登录if (guiGuLogin.required()){//  必须要登录,token 为空是抛出异常if (StringUtils.isEmpty(token)){//  没有token 要抛出异常throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);}//  如果token 不为空,从缓存中获取信息.UserInfo userInfo = (UserInfo) this.redisTemplate.opsForValue().get(RedisConstant.USER_LOGIN_KEY_PREFIX + token);//  判断对象是否为空if (null == userInfo){//  抛出异常信息throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);}}//  不需要强制登录,但是,有可能需要用信息.if (!StringUtils.isEmpty(token)){//  如果token 不为空,从缓存中获取信息.UserInfo userInfo = (UserInfo) this.redisTemplate.opsForValue().get(RedisConstant.USER_LOGIN_KEY_PREFIX + token);if (null != userInfo){//  将用户信息存储到请求头中AuthContextHolder.setUserId(userInfo.getId());AuthContextHolder.setUsername(userInfo.getNickname());}}//  执行业务逻辑return point.proceed();}
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

部署DNS主从服务器

一。DNS主从服务器作用&#xff1a; DNS作为重要的互联网基础设施服务&#xff0c;保证DNS域名解析服务的正常运转至关重要&#xff0c;只有这样才能提供稳定、快速日不间断的域名查询服务 DNS 域名解析服务中&#xff0c;从服务器可以从主服务器上获取指定的区域数据文件&…

Java面向对象编程进阶(四)

Java面向对象编程进阶&#xff08;四&#xff09; 一、equals()方法的使用二、toString()方法的使用三、复习 一、equals()方法的使用 适用性&#xff1a;任何引用数据都可以使用。 自定义的类在没有重写Object中equals()方法的情况下&#xff0c;调用的就是Object类中声明的…

Docker搭建基于Nextcloud的个人云盘/私有云盘/个人相册/家庭NAS

安装配置Docker 官方安装文档&#xff1a;https://docs.docker.com/engine/install/ Docker常用命令&#xff1a;https://blog.csdn.net/qq_43003203/article/details/139532097?spm1001.2014.3001.5502 Docker镜像仓库配置方法和国内常用镜像仓库地址&#xff1a; 输入&a…

k8s 二进制部署安装(三)

目录 部署Calico Calico 工作原理 部署Calico 部署CoreDNS 负载均衡部署 部署dashboard 部署Calico 安装步骤来到 CNI 网络组件 &#xff0c;在&#xff08;二&#xff09;中我们安装了flannel&#xff0c;现在我们要尝试安装另一网络组件Calico calico 不使用隧道或NAT…

Spring Task—定时任务

Spring Task 是 Spring 提供的一种轻量级定时任务调度功能&#xff0c;内置在 Spring 框架中。与 Quartz 等重量级调度框架相比&#xff0c;Spring Task 使用简便&#xff0c;无需额外依赖&#xff0c;适合在简单的调度任务场景中使用。通过注解配置方式&#xff0c;开发者可以…

docker 安装 PostgreSQL

参考链接 https://hub.docker.com/_/postgres 安装 # 后台运行&#xff0c;镜像名称为 postgres # --name postgres 容器名称为 postgres # POSTGRES_PASSWORD 超级用户的密码&#xff0c;超级用户名默认为&#xff1a;postgres&#xff0c;可以使用 POSTGRES_USER 环境变量设…

Flink(一)

目录 架构处理有界与无界数据部署应用到任意地方运行任意规模应用利用内存性能 流应用流处理应用的基本组件流状态时间 应用场景事件驱动应用事件驱动应用的优势Flink如何支持事件驱动应用&#xff1f; 典型的事件驱动示例 数据分析应用流式分析应用的优势&#xff1f;Flink 如…

【数据结构和算法】三、动态规划原理讲解与实战演练

目录 1、什么是动态规划&#xff1f; 2、动态规划实战演练 2.1 力扣题之爬楼梯问题 &#xff08;1&#xff09;解题思路1: &#xff08;2&#xff09;解题思路2: &#xff08;3&#xff09;动态规划&#xff08;DP&#xff09;&#xff1a;解题思路 &#xff08;4&#x…

中天控股智慧园区项目

— 项目概况 — 项目名称&#xff1a;智慧园区项目 项目地点&#xff1a;云南省 合作单位&#xff1a;中天控股集团有限公司&#xff08;简称“中天控股”&#xff09; 汇匠源与中天控股集团有限公司&#xff08;简称“中天控股”&#xff09;曾在智慧园区项目展开合作&a…

前端自学资料(笔记八股)分享—CSS(4)

更多详情&#xff1a;爱米的前端小笔记&#xff08;csdn~xitujuejin~zhiHu~Baidu~小红shu&#xff09;同步更新&#xff0c;等你来看&#xff01;都是利用下班时间整理的&#xff0c;整理不易&#xff0c;大家多多&#x1f44d;&#x1f49b;➕&#x1f914;哦&#xff01;你们…

MySQL查看某个数据库里面每张表的字符集和字符排序集

字符集&#xff1a; 定义了MySQL中数据在硬盘上的存储方式。例如 utfmb3、utfmb4等。每个不同的字符集都拥有一个默认的字符排序集。 字符排序集&#xff1a; 定义了在数据库中进行字符串比较和排序的方式。 &#xff08;1&#xff09;比较字符串&#xff1a;确定两个字符串是否…

Git相关介绍

基本概念 关注&#xff08;watch&#xff09; 关注项目&#xff0c;当项目更新可以接收到通知 事物卡片&#xff08;Issue&#xff09; 发现代码BUG&#xff0c;但是目前没有成型代码&#xff0c;需要讨论时用 Git工作区域 工作区 添加、编辑、修改文件等动作 暂存区 …

坚持使用kimi搭建小程序2小时(04天/05天)

运用好kimi智能助手里面的存储小程序&#xff0c;{缺乏一个相对稳定的反馈体系&#xff0c;自己所挑选的稳定反馈体系就是编程!} 开源竞争&#xff1a; 当你无法彻底掌握一门技术的时候&#xff0c;就开源这门技术&#xff0c;培养出更多的技术依赖&#xff0c;让更多人完善你…

VMware虚拟机启动报错“此主机支持 Intel VT-x,但 Intel VT-x 处于禁用状态”

之前正常使用的VMware虚拟机&#xff0c;突然启动时报错&#xff1a;此主机支持 Intel VT-x&#xff0c;但 Intel VT-x 处于禁用状态&#xff0c;详细信息如下截图所示。   百度错误信息&#xff0c;根据参考文献1中的方案&#xff0c;进入BIOS设置启动VT-x。进入BIOS后&…

spyglass关于cdc检测的一处bug

最近在使用22版spyglass的cdc检测功能&#xff0c;发现struct_check的cdc检测实际时存在一些bug的。 构造如下电路&#xff0c;当qualifier和destination信号汇聚时&#xff0c;如果des信号完全将qualifier gate住&#xff0c;sg仍然会报ac_sync。当然此问题可以通过后续funct…

基础知识-因果分析-daythree-独立性检验-贝叶斯公式及应用

根据概率乘法公式有P(AB)P(B|A)P(A)变形为除法形式&#xff0c;则有 更一般地&#xff0c;假设事件的集合B1&#xff0c;B2&#xff0c;…&#xff0c;Bn构成样本空间的一个划分&#xff0c;则根据全概率公式有 将式(2.14)中的B替换为Bi&#xff0c;则有 再代入P(A)的全概率计算…

Openlayers高级交互(8/20):选取feature,平移feature

本示例介绍如何在vue+openlayers中使用Translate,选取feature,平移feature。选择的时候需要按住shift。Translate 功能通常是指在地图上平移某个矢量对象的位置。在 OpenLayers 中,可以通过修改矢量对象的几何位置来实现这一功能。 效果图 配置方式 1)查看基础设置:http…

即插即用篇 | YOLOv8 引入 空间自适应特征调制模块 SAFM

代码地址: https://github.com/sunny2109/SAFMN 论文地址:https://arxiv.org/pdf/2302.13800 虽然已经提出了许多图像超分辨率的解决方案,但它们通常与许多计算和内存限制的低功耗设备不兼容。本文通过提出一个简单而有效的深度网络来高效地解决图像超分辨率问题。具体来说,…

【Oracle实验】字段为空的,无法通过排除判断

Oracle相关文档&#xff0c;希望互相学习&#xff0c;共同进步 风123456789&#xff5e;-CSDN博客 1.场景描述 需求&#xff1a;查询不是某个机构的数据。 同事SQL&#xff1a;where substr(bank_code,1,9) not in(014009001)&#xff1b; 看SQL似乎没什么问题&#xff0c;分析…

练习LabVIEW第二十一题

学习目标&#xff1a; 刚学了LabVIEW&#xff0c;在网上找了些题&#xff0c;练习一下LabVIEW&#xff0c;有不对不好不足的地方欢迎指正&#xff01; 第二十一题&#xff1a; 用一个chart(波形图表)显示一个随机数&#xff0c;用前面板控件改变chart(波形图表)的大小和位置…