【项目】小帽学堂(十一②)

小帽学堂

7. 首页数据显示 - banner 接口

【项目】小帽学堂(十一②)_第1张图片

  • 新建banner微服务

    • 在service模块下创建子模块service-cms
    • 使用代码生成器生成banner代码
      【项目】小帽学堂(十一②)_第2张图片
    • 配置application.properties
    # 服务端口
    
    server.port=8004
    
    # 服务名
    
    spring.application.name=service-cms
    
    
    # mysql数据库连接
    
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    
    spring.datasource.url=jdbc:mysql://localhost:3306/school?serverTimezone=GMT%2B8
    
    spring.datasource.username=root
    
    spring.datasource.password=root
    
    
    #返回json的全局时间格式
    
    spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
    
    spring.jackson.time-zone=GMT+8
    
    
    #配置mapper xml文件的路径
    
    mybatis-plus.mapper-locations=classpath:com/alex/educms/mapper/xml/*.xml
    
    
    #mybatis日志
    
    mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
    
    • 创建启动类
    @SpringBootApplication
    @ComponentScan({"com.alex"}) //指定扫描位置
    @MapperScan("com.alex.educms.mapper")
    public class CmsApplication {
        public static void main(String[] args) {
            SpringApplication.run(CmsApplication.class, args);
        }
    }
    
  • 创建banner服务接口

    • 创建banner后台管理接口(banner后台分页查询、添加、修改、删除接口)
    @RestController
    @RequestMapping("/educms/banneradmin")
    @CrossOrigin
    public class BannerAdminController {
        @Autowired
        private CrmBannerService bannerService;
    
        // 1. 分页查询banner
        @GetMapping("pageBanner/{page}/{limit}")
        public R pageBanner(@PathVariable long page, @PathVariable long limit) {
            Page<CrmBanner> pageBanner = new Page<>(page, limit);
            bannerService.page(pageBanner,null);
            return R.ok().data("items", pageBanner.getRecords()).data("total", pageBanner.getTotal());
        }
    
        // 2. 添加banner
        @PostMapping("addBanner")
        public R addBanner(@RequestBody CrmBanner crmBanner) {
            bannerService.save(crmBanner);
            return R.ok();
        }
    
        // 3. 修改banner
        @PutMapping("update")
        public R updateById(@RequestBody CrmBanner banner) {
    
            bannerService.updateById(banner);
            return R.ok();
    
        }
    
        // 4. 删除banner
        @DeleteMapping("remove/{id}")
        public R remove(@PathVariable String id) {
            bannerService.removeById(id);
            return R.ok();
        }
    
        // 5. 获取banner
        @GetMapping("get/{id}")
        public R get(@PathVariable String id) {
            CrmBanner banner = bannerService.getById(id);
            return R.ok().data("item", banner);
        }
    }
    
    • 创建banner前台查询接口
    @RestController
    @RequestMapping("/educms/bannerfront")
    @CrossOrigin
    public class BannerFrontController {
        @Autowired
        private CrmBannerService bannerService;
    
        // 查询所有banner
        @GetMapping("getAllBanner")
        public R getAllBanner() {
            List<CrmBanner> list = bannerService.selectAllBanner();
            return R.ok().data("list", list);
        }
    }
    
    public interface CrmBannerService extends IService<CrmBanner> {
        List<CrmBanner> selectAllBanner();
    }
    
    @Service
    public class CrmBannerServiceImpl extends ServiceImpl<CrmBannerMapper, CrmBanner> implements CrmBannerService {
    
        @Override
        public List<CrmBanner> selectAllBanner() {
            List<CrmBanner> list = baseMapper.selectList(null);
            return list;
        }
    }
    

8. 热门课程和名师接口

【项目】小帽学堂(十一②)_第3张图片

@RestController
@RequestMapping("/eduservice/indexfront")
@CrossOrigin
public class IndexFrontController {

    @Autowired
    private EduCourseService courseService;

    @Autowired
    private EduTeacherService teacherService;
    // 查询前8条热门课程,查询前4个名师
    @GetMapping("index")
    public R index() {
        // 查询前8条热门课程
        QueryWrapper<EduCourse> wrapper = new QueryWrapper<>();
        wrapper.orderByDesc("id");
        wrapper.last("limit 8");
        List<EduCourse> eduList = courseService.list(wrapper);

        // 查询前4个名师
        QueryWrapper<EduTeacher> wrapperTeacher = new QueryWrapper<>();
        wrapperTeacher.orderByDesc("id");
        wrapperTeacher.last("limit 4");
        List<EduTeacher> teacherList = teacherService.list(wrapperTeacher);
        return R.ok().data("eduList",eduList).data("teacherList", teacherList);
    }
}

9. 首页数据显示 - banner 显示(前端)

【项目】小帽学堂(十一②)_第4张图片

  • 创建api文件夹,创建banner.js文件
// api\banner.js
import request from '@/utils/request'

export default {
  // 查询前两条banner数据
  getListBanner() {
    return request({
      url: `/educms/bannerfront/getAllBanner`,
      method: 'get'
    })
  }
}
// utils\request.js
import axios from 'axios'
// 创建axios实例
const service = axios.create({
  baseURL: 'http://localhost:9001', // api的base_url
  timeout: 20000 // 请求超时时间
})
export default service
  • 在首页面引入,调用实现
// pages\index.vue
 

<div v-swiper:mySwiper="swiperOption">

    <div class="swiper-wrapper">

        <div v-for="banner in bannerList" :key="banner.id" class="swiper-slide" style="background: #040B1B;">

            <a target="_blank" :href="banner.linkUrl">

                <img :src="banner.imageUrl" :alt="banner.title">

            a>

        div>

    div>

    <div class="swiper-pagination swiper-pagination-white">div>

    <div class="swiper-button-prev swiper-button-white" slot="button-prev">div>

    <div class="swiper-button-next swiper-button-white" slot="button-next">div>

div>


<script>
import banner from '@/api/banner'
export default {

  data () {
    return {
      bannerList: []
    }
  },
  created() {
    // 调用查询banner的方法
    this.getBannerList()
  },
  methods: {
    // 查询banner数据
    getBannerList() {
      banner.getListBanner()
        .then(response => {
          this.bannerList = response.data.data.list
        })
    }
  }
}
script>

10. 首页数据显示 - 热门课程和名师(前端)

// api\index.js
import request from '@/utils/request'

export default {
  // 查询热门课程和名师
  getIndexData() {
    return request({
      url: `/eduservice/indexfront/index`,
      method: 'get'
    })
  }
}
// pages\index.vue
<div id="aCoursesList">
      
      <div>
        <section class="container">
          <header class="comm-title">
            <h2 class="tac">
              <span class="c-333">热门课程span>
            h2>
          header>
          <div>
            <article class="comm-course-list">
              <ul class="of" id="bna">
                <li v-for="course in eduList" :key="course.id">
                  <div class="cc-l-wrap">
                    <section class="course-img">
                      <img
                        :src="course.cover"
                        class="img-responsive"
                        :alt="course.title"
                      >
                      <div class="cc-mask">
                        <a href="#" title="开始学习" class="comm-btn c-btn-1">开始学习a>
                      div>
                    section>
                    <h3 class="hLh30 txtOf mt10">
                      <a href="#" :title="course.title" class="course-title fsize18 c-333">{{course.title}}a>
                    h3>
                    <section class="mt10 hLh20 of">
                      <span class="fr jgTag bg-green" v-if="Number(course.price) === 0">
                        <i class="c-fff fsize12 f-fA">免费i>
                      span>
                      <span class="fl jgAttr c-ccc f-fA">
                        <i class="c-999 f-fA">9634人学习i>
                        |
                        <i class="c-999 f-fA">9634评论i>
                      span>
                    section>
                  div>
                li>
              ul>
              <div class="clear">div>
            article>
            <section class="tac pt20">
              <a href="#" title="全部课程" class="comm-btn c-btn-2">全部课程a>
            section>
          div>
        section>
      div>
      
      
      <div>
        <section class="container">
          <header class="comm-title">
            <h2 class="tac">
              <span class="c-333">名师大咖span>
            h2>
          header>
          <div>
            <article class="i-teacher-list">
              <ul class="of">
                <li v-for="teacher in teacherList" :key="teacher.id">
                  <section class="i-teach-wrap">
                    <div class="i-teach-pic">
                      <a href="/teacher/1" :title="teacher.name">
                        <img :alt="teacher.name" :src="teacher.avatar">
                      a>
                    div>
                    <div class="mt10 hLh30 txtOf tac">
                      <a href="/teacher/1" :title="teacher.name" class="fsize18 c-666">{{teacher.name}}a>
                    div>
                    <div class="hLh30 txtOf tac">
                      <span class="fsize14 c-999">{{teacher.career}}span>
                    div>
                    <div class="mt15 i-q-txt">
                      <p
                        class="c-999 f-fA"
                      >{{teacher.intro}}p>
                    div>
                  section>
                li>
              ul>
              <div class="clear">div>
            article>
            <section class="tac pt20">
              <a href="#" title="全部讲师" class="comm-btn c-btn-2">全部讲师a>
            section>
          div>
        section>
      div>
      
    div>

<script>
import banner from '@/api/banner'
import index from '@/api/index'
export default {

  data () {

    return {
      bannerList: [],
      eduList: [],
      teacherList: []
    }

  },
  created() {
    // 调用查询banner的方法
    this.getBannerList(),
    // 调用查询热门课程和名师的方法
    this.getHotCourseTeacher()
  },
  methods: {
    // 查询热门课程和名师
    getHotCourseTeacher() {
      index.getIndexData()
        .then(response => {
          this.eduList = response.data.data.eduList
          this.teacherList = response.data.data.teacherList
        })
    },
    // 查询banner数据
    getBannerList() {
      banner.getListBanner()
        .then(response => {
          this.bannerList = response.data.data.list
        })
    }
  }
}
script>

11. Redis 基本回顾

【项目】小帽学堂(十一②)_第5张图片

  • Redis是当前比较热门的NOSQL系统之一,它是一个开源的使用ANSI c语言编写的key-value存储系统(区别于MySQL的二维表格的形式存储。)。和Memcache类似,但很大程度补偿了Memcache的不足。和Memcache一样,Redis数据都是缓存在计算机内存中,不同的是,Memcache只能将数据缓存到内存中,无法自动定期写入硬盘,这就表示,一断电或重启,内存清空,数据丢失。所以Memcache的应用场景适用于缓存无需持久化的数据。而Redis不同的是它会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,实现数据的持久化。

  • Redis的特点:

    • Redis读取的速度是110000次/s,写的速度是81000次/s;
    • 原子 。Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
    • 支持多种数据结构:string(字符串);list(列表);hash(哈希),set(集合);zset(有序集合)
    • 持久化,集群部署
    • 支持过期时间,支持事务,消息订阅

12. 首页数据显示 - 添加 redis 缓存

  • 在common模块添加依赖
    • 由于redis缓存是公共应用,所以我们把依赖与配置添加到了common模块下面,在common模块pom.xml下添加以下依赖

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-data-redisartifactId>
dependency>

<dependency>
    <groupId>org.apache.commonsgroupId>
    <artifactId>commons-pool2artifactId>
    <version>2.6.0version>
dependency>
  • 在service-base模块添加redis配置类
package com.alex.servicebase;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

/**
 * @author :alex
 * @date :Created in 2022/2/27
 * @description :
 * @version: 1.0
 */
@Configuration // 配置类
@EnableCaching // 开启缓存
public class RedisConfig extends CachingConfigurerSupport {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {

        RedisTemplate<String, Object> template = new RedisTemplate<>();

        RedisSerializer<String> redisSerializer = new StringRedisSerializer();

        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper om = new ObjectMapper();

        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

        jackson2JsonRedisSerializer.setObjectMapper(om);

        template.setConnectionFactory(factory);

        //key序列化方式

        template.setKeySerializer(redisSerializer);

        //value序列化

        template.setValueSerializer(jackson2JsonRedisSerializer);

        //value hashmap序列化

        template.setHashValueSerializer(jackson2JsonRedisSerializer);

        return template;

    }


    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {

        RedisSerializer<String> redisSerializer = new StringRedisSerializer();

        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        //解决查询缓存转换异常的问题

        ObjectMapper om = new ObjectMapper();

        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

        jackson2JsonRedisSerializer.setObjectMapper(om);

        // 配置序列化(解决乱码的问题),过期时间600秒

        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()

                .entryTtl(Duration.ofSeconds(600))

                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))

                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))

                .disableCachingNullValues();

        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)

                .cacheDefaults(config)

                .build();

        return cacheManager;
    }
}
  • 在接口中添加redis缓存
    • 由于首页数据变化不是很频繁,而且首页访问量相对较大,所以我们有必要把首页接口数据缓存到redis缓存中,减少数据库压力和提高访问速度。
    • 改造service-cms模块首页banner接口,首页课程与讲师接口类似
  • Spring Boot缓存注解

(1)缓存@Cacheable
根据方法对其返回结果进行缓存,下次请求时,如果缓存存在,则直接读取缓存数据返回;如果缓存不存在,则执行方法,并把返回的结果存入缓存中。一般用在查询方法上。

查看源码,属性值如下:

属性/方法名 解释
value 缓存名,必填,它指定了你的缓存存放在哪块命名空间
cacheNames 与 value 差不多,二选一即可
key 可选属性,可以使用 SpEL 标签自定义缓存的key

(2)缓存@CachePut
使用该注解标志的方法,每次都会执行,并将结果存入指定的缓存中。其他方法可以直接从响应的缓存中读取缓存数据,而不需要再去查询数据库。一般用在新增方法上。

查看源码,属性值如下:

属性/方法名 解释
value 缓存名,必填,它指定了你的缓存存放在哪块命名空间
cacheNames 与 value 差不多,二选一即可
key 可选属性,可以使用 SpEL 标签自定义缓存的key

(3)缓存@CacheEvict
使用该注解标志的方法,会清空指定的缓存。一般用在更新或者删除方法上

查看源码,属性值如下:

属性/方法名 解释
value 缓存名,必填,它指定了你的缓存存放在哪块命名空间
cacheNames 与 value 差不多,二选一即可
key 可选属性,可以使用 SpEL 标签自定义缓存的key
allEntries 是否清空所有缓存,默认为 false。如果指定为 true,则方法调用后将立即清空所有的缓存
beforeInvocation 是否在方法执行前就清空,默认为 false。如果指定为 true,则在方法执行前就会清空缓存
  • 启动redis服务
    【项目】小帽学堂(十一②)_第6张图片
    【项目】小帽学堂(十一②)_第7张图片

  • 连接redis服务可能遇到的问题

    • 关闭liunx防火墙

    • 找到redis配置文件, 注释一行配置
      在这里插入图片描述

    • 如果出现下面错误提示
      在这里插入图片描述

    • 修改 protected-mode yes

      • 改为 protected-mode no
  • banner接口改造

    • 在service-cms模块配置文件添加redis配置
    spring.redis.host=192.168.44.132
    spring.redis.port=6379
    spring.redis.database= 0
    spring.redis.timeout=1800000
    
    
    spring.redis.lettuce.pool.max-active=20
    spring.redis.lettuce.pool.max-wait=-1
    
    #最大阻塞等待时间(负数表示没限制)
    spring.redis.lettuce.pool.max-idle=5
    spring.redis.lettuce.pool.min-idle=0
    
    • 修改CrmBannerServiceImpl,添加redis缓存注解
    @Service
    public class CrmBannerServiceImpl extends ServiceImpl<CrmBannerMapper, CrmBanner> implements CrmBannerService {
    
        // 查询所有banner
        @Cacheable(key = "'selectIndexList'", value = "banner")
        @Override
        public List<CrmBanner> selectAllBanner() {
            // 根据id进行降序排列,显示排列之后前两条记录
            QueryWrapper<CrmBanner> wrapper = new QueryWrapper<>();
            wrapper.orderByDesc("id");
            // last方法,拼接sql语句
            wrapper.last("limit 2");
            List<CrmBanner> list = baseMapper.selectList(wrapper);
            return list;
        }
    }
    
  • 在redis添加了key
    在这里插入图片描述

你可能感兴趣的:(项目,java)