谷粒随享1

内容回顾

1、JVM

2、JUC

  • 线程池

-- 自定义线程池 ThreadPoolExecutor,传递7个参数

-- 核心线程数规则 : IO密集型 2N +1 ,CPU密集型 N+1

-- 为什么不使用Executors工具类创建?而是自定义创建线程池

3、MySQL高级

  • 索引底层结构

  • SQL优化

-- 单表

-- 多表(关联查询)

今天内容

1、项目概述

总体概述

  • 谷粒随享,阅读类听书项目,类似于喜马拉雅

  • 我们项目运行在微信小程序中

技术栈

  • SpringBoot:简化Spring应用的初始搭建以及开发过程

  • SpringBoot+SpringCloudAlibaba(Nacos)+OpenFeign+Gateway

  • MyBatis-Plus:持久层框架,也依赖mybatis

  • Redis:内存做缓存

  • MongoDB: 分布式文件存储的数据库

  • Rabbitmq:消息中间件;大型分布式项目是标配;分布式事务最终一致性

  • ThreadPoolExecutor+CompletableFuture:线程池来实现异步操作,提高效率

  • Knife4J:Api接口文档工具

  • MinIO(私有化对象存储集群):分布式文件存储

  • Docker:容器化技术; 生产环境Redis(运维人员);快速搭建环境Docker run

  • Cannal:阿里开源增量订阅组件,数据增量同步

  • Seata:阿里开源分布式事务解决方案

  • Redisson:基于redis的Java驻内存数据网格 - 框架;分布式锁

  • ElasticSearch+Kibana+Logstash 全文检索服务器+可视化数据监控:检索

  • xxl-Job: 分布式定时任务调用中心

  • **支付平台:**微信支付

  • Git:多练习

2、搭建项目环境

搭建软件环境

  • 项目运行需要使用服务,比如mysql、rabbitmq等等

方式一:使用提供虚拟机

**第一步,**下载我提供ts.7z,解压

**第二步,**使用VMware打开解压虚拟机,进行使用

第三步, 因为我提供虚拟机把ip地址写固定的,设置VMware网络环境

  • 虚拟机ip: 192.168.200.130

  • 用户名和密码 : root / 123456

  • 设置VMware网络环境

  • 检查本地虚拟网卡ip

设置完成之后,使用连接工具,通过192.168.200.130连接虚拟机环境

虚拟机需要软件

  • mysql

  • nacos

  • redis

  • minio

  • rabbitmq

  • mongo

  • es

  • kibana

  • logstash

  • canal

我提供虚拟机里面,rabbitmq需要每次手动启动,docker start spzx_rabbitmq

其他服务自动启动

方式二:自己安装

搭建项目工程

  • 我这里提供工程结构,有基础代码,比如实体类,有controller、service、mapper,有依赖

  • 使用idea打开工程

第一步 到资料找到工程模板压缩文件,解压到目录里面

第二步 使用idea打开这个工程

第三步 设置idea的maven环境 和 检查jdk版本

设置好之后,等待依赖下载

3、专辑管理

添加专辑分析

  • 功能接口分析
  • 项目表结构

查询所有分类接口(★)

1、因为只是开发后端接口,前端已经开发完成了,后端接口路径和课件保持一致,返回数据和课件一致

2、三级分类返回数据格式:

复制代码
[
    {
        一级分类id:   1
        一级分类名称:音乐
        child: [
            {
                二级分类id:11
                二级分类名称:古典音乐
                child: [
                    {
                        三级分类id:111
                        三级分类名称:希腊古典音乐
                    }
                ]
            }
        ]
    },
    {
        一级分类id:   2
        一级分类名称:军事
    }
]

BaseCategoryApiController

复制代码
@Tag(name = "分类管理")
@RestController
@RequestMapping(value="/api/album/category")
@SuppressWarnings({"all"})
public class BaseCategoryApiController {
    
    @Autowired
    private BaseCategoryService baseCategoryService;
​
    //查询所有分类
    @GetMapping("getBaseCategoryList")
    public Result getBaseCategoryList(){
       //调用service方法
       //List<Map> list = baseCategoryService.getBaseCategoryList();
​
       List<JSONObject> list = baseCategoryService.getBaseCategoryList();
​
       return Result.ok(list);
    }
}

service

复制代码
package com.atguigu.tingshu.album.service.impl;
​
import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.album.mapper.*;
import com.atguigu.tingshu.album.service.BaseCategoryService;
import com.atguigu.tingshu.model.album.BaseAttribute;
import com.atguigu.tingshu.model.album.BaseCategory1;
import com.atguigu.tingshu.model.album.BaseCategory2;
import com.atguigu.tingshu.model.album.BaseCategoryView;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
​
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
​
@Service
@SuppressWarnings({"all"})
public class BaseCategoryServiceImpl extends ServiceImpl<BaseCategory1Mapper, BaseCategory1> implements BaseCategoryService {
​
    @Autowired
    private BaseCategory1Mapper baseCategory1Mapper;
​
    @Autowired
    private BaseCategory2Mapper baseCategory2Mapper;
​
    @Autowired
    private BaseCategory3Mapper baseCategory3Mapper;
    
    @Autowired
    private BaseCategoryViewMapper baseCategoryViewMapper;
​
    @Autowired
    private BaseAttributeMapper baseAttributeMapper;
​
    //查询所有分类
    @Override
    public List<JSONObject> getBaseCategoryList() {
       //1 把视图里面所有分类查询出来,返回list集合
       List<BaseCategoryView> baseCategoryViewList =
             baseCategoryViewMapper.selectList(null);
​
       //2 创建list集合封装最终数据
       List<JSONObject> finalList = new ArrayList<>();
​
       //3 封装所有一级分类
       //把查询视图里面所有分类集合进行遍历,得到一级分类id,根据一级分类id进行分组
       //分组之后返回map集合
       // map的key:分组字段,一级分类id
       // map的value:每组里面所有数据集合
       Map<Long, List<BaseCategoryView>> map =
             baseCategoryViewList.stream()
                   .collect(Collectors
                         .groupingBy(BaseCategoryView::getCategory1Id));
       //for (Map.Entry<Long, List<BaseCategoryView>> entry : map.entrySet())
       //遍历map,
       map.forEach((k, v) -> {
          Long categoryId1 = k; //一级分类id
          List<BaseCategoryView> baseCategoryViewList1 = v; //每组一级分类组所有数据集合
​
          //封装一级分类
          JSONObject jsonObject1 = new JSONObject();
          jsonObject1.put("categoryId",categoryId1);
          jsonObject1.put("categoryName",baseCategoryViewList1.get(0).getCategory1Name());
​
          //封装二级分类
          //一级分类分组之后,每组里面集合,根据二级分类id分组
          //一级分类每组集合 v  == baseCategoryViewList1
          Map<Long, List<BaseCategoryView>> map1 =
                baseCategoryViewList1.stream()
                      .collect(Collectors
                            .groupingBy(BaseCategoryView::getCategory2Id));
​
          //创建list集合,用于每个一级分类所有二级分类集合
          List<JSONObject> categoryChild2 = new ArrayList<>();
​
          map1.forEach((k1,v1)->{
             Long categoryId2 = k1; //一级分类id
             List<BaseCategoryView> baseCategoryViewList2 = v1;
​
             JSONObject jsonObject2 = new JSONObject();
             jsonObject2.put("categoryId",categoryId2);
             jsonObject2.put("categoryName",
                   baseCategoryViewList2.get(0).getCategory2Name());
​
             // baseCategoryViewList2 = v1有二级下面所有三级分类
             // List<BaseCategoryView> --> List<JSONObject>
             List<JSONObject> categoryChild3 =
                   baseCategoryViewList2.stream().map(baseCategoryView -> {
                JSONObject jsonObject3 = new JSONObject();
                jsonObject3.put("categoryId", baseCategoryView.getCategory3Id());
                jsonObject3.put("categoryName", baseCategoryView.getCategory3Name());
                return jsonObject3;
             }).collect(Collectors.toList());
​
             //把三级集合放到每个二级分类对象里面
             jsonObject2.put("categoryChild",categoryChild3);
​
             // 将二级分类对象添加到集合中
             categoryChild2.add(jsonObject2);
          });
​
          //把二级分类集合放到每个一级里面
          jsonObject1.put("categoryChild",categoryChild2);
​
          //把所有一级分类对象放到最终集合里面
          finalList.add(jsonObject1);
       });
​
       return finalList;
    }
}

文件上传接口

FileUploadApiController

复制代码
@Tag(name = "上传管理接口")
@RestController
@RequestMapping("api/album")
public class FileUploadApiController {
    
    @Autowired
    private FileUploadService fileUploadService;
    
    //文件上传接口
    @Operation(summary = "文件上传")
    @PostMapping("/fileUpload")
    public Result fileUpload(MultipartFile file) {
        //  文件上传
        String url = fileUploadService.upload(file);
        //  返回数据
        return Result.ok(url);
    }
​
}

service

复制代码
package com.atguigu.tingshu.album.service.impl;
​
import com.atguigu.tingshu.album.config.MinioConstantProperties;
import com.atguigu.tingshu.album.service.FileUploadService;
import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.errors.MinioException;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
​
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;
​
@Service
public class FileUploadServiceImpl implements FileUploadService {
​
    @Autowired
    private MinioConstantProperties minioConstantProperties;
​
    //文件上传接口
    @Override
    public String upload(MultipartFile file) {
        //  声明一个url 地址
        String url = "";
        //  代码:
        try {
            // Create a minioClient with the MinIO server playground, its access key and secret key.
            //  ctrl+p
            MinioClient minioClient =
                    MinioClient.builder()
                            .endpoint(minioConstantProperties.getEndpointUrl())
                            .credentials(minioConstantProperties.getAccessKey(),
                                    minioConstantProperties.getSecreKey())
                            .build();
​
            // Make 'asiatrip' bucket if not exist.
            boolean found =
                    false;
            found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(minioConstantProperties.getBucketName()).build());
            if (!found) {
                // Make a new bucket called 'asiatrip'.
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(minioConstantProperties.getBucketName()).build());
            } else {
                System.out.println("Bucket " + minioConstantProperties.getBucketName() + " already exists.");
            }
​
            //  文件:
            //  new File().length();  数组长度:length  集合长度:size()  字符串长度: length();  ()--> 方法 没有(); 属性
            String fileName = UUID.randomUUID().toString().replaceAll("-", "") + "." + FilenameUtils.getExtension(file.getOriginalFilename());
            // Upload known sized input stream.
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(minioConstantProperties.getBucketName())
                            .object(fileName).stream(
                                    file.getInputStream(), file.getSize(), -1)
                            .contentType(file.getContentType())
                            .build());
            //  获取到上传之后的文件路径:
            //  https://www.image.com/ts/1213oiqwe.jpg;
            //  http://192.168.200.130:9000/tingshu/atguigu.jpg
            url = minioConstantProperties.getEndpointUrl()+"/"+minioConstantProperties.getBucketName()+"/"+fileName;
            System.out.println("url:\t"+url);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return url;
    }
}

4、搭建前端环境

  • 前端代码运行在微信小程序中

  • 测试时候,使用微信开发者工具进行测试

安装微信开发者工具

使用微信开发者工具导入前端项目

第一步 资料找到前端代码压缩文件,解压

第二步 打开微信开发者工具

  • 注册微信公众平台appid

1、注册正式号网站:小程序

2、注册测试号:微信公众平台