HRM微服务项目day-04-课程上线下线、集成ElasticSearch

1. ES服务搭建

前台页面很多场景下,都需要用到搜索,ElasticSearch作为分布式全文搜索引擎,使用restful api对文档操作,我们要集成ES,把它作为一个独立的服务!使其他需要集成搜索功能的服务模块,只需要通过feign向ES服务发起调用即可!

①:创建es服务模块:

​ hrm-es-parent

​ hrm-es-common

​ hrm-es-feign

​ hrm-es-server-2050

②:集成注册中心

③:集成配置中心

④:集成swagger

⑤:集成zuul网关

⑥:zuul整合swagger

2. ES与SpringBoot的集成

  1. 导入jar包
        
            org.springframework.boot
            spring-boot-starter-data-elasticsearch
        
  1. 创建配置文件
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:1010/eureka/ #注册中心服务端的注册地址
  instance:
    prefer-ip-address: true #使用ip进行注册
    instance-id: es-server:2050  #服务注册到注册中心的id
server:
  port: 2050
#应用的名字
spring:
  application:
    name: es-server
  data:
    elasticsearch:
      cluster-name: elasticsearch #集群的名字
      cluster-nodes: 127.0.0.1:9300 #9200是图形界面端,9300代码端
ribbon: #ribbon超时
  ReadTimeout: 30000
  ConnectTimeout: 30000
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 40000      
  1. 创建一个类用来描述文档信息
@Document(indexName = "hrm",type = "course")
public class CourseDoc {
    //文档的ID,同时也是数据的id
    @Id
    private Long id;
    //标题
    @Field(type =FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
    private String name;
    //适用人群
    @Field(type =FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
    private String users;
    //课程类型ID
    @Field(type = FieldType.Long)
    private Long courseTypeId;
    //等级名字
    //@Field(type = FieldType.Keyword)
    private String gradeName;
    //课程等级
    private Long gradeId;
    //机构id
    private Long tenantId;
    //机构名字
    @Field(type =FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
    private String tenantName;
    //开课时间
    private Date startTime;
    //结课时间
    private Date endTime;
    //封面
    private String pic;
    //免费、收费
    private String chargeName;
    //qq
    private String qq;
    //价格
    private Float price;
    //原价
    private Float priceOld;
    //课程介绍
    private String description;
    //上线时间
    private Date onlineDate = new Date();
    //浏览数
    private Integer viewCount;
    //购买数
    private Integer buyCount;
    ...

@Document(indexName = "hrm",type = "course"):描述文档信息,indexName:索引库,type:文档类型。
@Id:文档的ID,同时也是数据的id

@Field(type =FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word"):描述字段信息,FieldType.Text:做分词处理,FieldType.Keyword:不做分词处理,analyzer:使用什么分词器做分词,searchAnalyzer:使用什么分词器做查询!

  1. 创建一个接口继承ElasticsearchRepository,其中有对文档操作的所有方法!
@Repository
public interface CourseDocRepository extends ElasticsearchRepository {
}
  1. 使用SpringBoot的测试环境测试CRUD
@RunWith(SpringRunner.class)
@SpringBootTest
public class CourseDocTest {
    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;
    @Autowired
    private CourseDocRepository courseDocRepository;
    @Test
    public void test(){
        elasticsearchTemplate.createIndex(CourseDoc.class);
        elasticsearchTemplate.putMapping(CourseDoc.class);
    }
    @Test
    public void testAddDoc(){
        for(int i = 0; i< 20 ; i++){
            CourseDoc courseDoc = new CourseDoc();

            courseDoc.setId(Long.valueOf(1+i));
            if(i % 2 == 0){
                courseDoc.setName("Java从入门到超神");

            }else{
                courseDoc.setName("PHP从入门到放弃");
            }
            if(i % 3 == 0){
                courseDoc.setGradeName("神级");
            }else{
                courseDoc.setGradeName("低级");
            }
            courseDoc.setCourseTypeId(Long.valueOf(1+i));

            courseDocRepository.save(courseDoc);
        }


    }
    @Test
    public void testGetDoc(){
        Optional optional = courseDocRepository.findById(23l);
        if(optional!=null){
            CourseDoc courseDoc = optional.get();
            System.out.println(courseDoc);
        }
    }
    @Test
    public void testGetAll(){
        Iterator iterator = courseDocRepository.findAll().iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
    @Test
    public void testUpdata(){
        CourseDoc courseDoc = new CourseDoc();
        courseDoc.setId(23l);
        courseDoc.setName("JAVA");
        courseDocRepository.save(courseDoc);
    }
    @Test
    public void testDelete(){
        courseDocRepository.deleteById(23l);
    }

ElasticsearchTemplate是es的内置对象,可以用来创建索引库,和指定映射规则!

高级查询:

使用关键对象NativeSearchQueryBuilder构建查询规则!

    @Test
    public void testQuery(){
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        /**
         *     高级查询分页排序: DSL查询+DSL过滤
         *     // 需求:查询 name中包含 “Java”的课程,
         *     // 并且课程分类 CourseTypeId 在 1 - 10
         *     //课程等级 GradeName为“神级”
         *     //查询第2页,每页2条
         *     //按照id倒排序
         */
        //查询 name中包含 “Java”的课程
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        boolQueryBuilder.must(new MatchQueryBuilder("name", "java"));
        builder.withQuery(boolQueryBuilder);
       //并且课程分类 CourseTypeId 在 1 - 10
        boolQueryBuilder.filter(new RangeQueryBuilder("courseTypeId").gte(1).lte(20));
        //课程等级 GradeName为“神级”
        boolQueryBuilder.filter(new TermQueryBuilder("gradeName", "神级"));
        //按照id倒排序

        builder.withSort(new FieldSortBuilder("id").order(SortOrder.DESC));
        //查询第1页,每页5条
        builder.withPageable(PageRequest.of(0, 5));

        //执行查询
        Page docs = courseDocRepository.search(builder.build());
        System.out.println("总条数:"+docs.getTotalElements());
        System.out.println("查询的页数:"+docs.getTotalPages());
        docs.getContent().forEach(e-> System.out.println("======"+e));
        Iterator iterator = docs.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

3. ES与Feign的集成实现课程的上线下线

上线场景:

前台用户点击课程上线,请求到达控制器,控制器执行业务!

  • 根据传过来的课程id去数据库中查询出来
  • 判断是否是下线状态
  • 如果是下线状态通过feign调用es服务,将数据保存到es中
  • 修改数据库中课程的状态码

下线场景:

前台用户点击课程下线,请求到达控制器,控制器执行业务!

  • 根据传过来的课程id去数据库中查询出来
  • 判断是否是上线状态
  • 如果是上线状态通过feign调用es服务,将数据从es中删除
  • 修改数据库中课程的状态码

1:controller

    /**
     * 课程上线
     * @param id
     * @return
     */
    @PostMapping("/onLineCourse/{id}")
    public AjaxResult onLineCourse(@PathVariable Long id){
        try {
            courseService.onLineCourse(id);
            return AjaxResult.me();
        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.me().setSuccess(false).setMessage("课程上线失败!"+e.getMessage());
        }
    }

    /**
     *课程下线
     * @param
     * @return
     */
    @PostMapping("/offLineCourse/{id}")
    public AjaxResult offLineCourse(@PathVariable Long id){
        try {
            courseService.offLineCourse(id);
            return AjaxResult.me();
        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.me().setSuccess(false).setMessage("课程下线失败!"+e.getMessage());
        }
    }

2:es与feign的集成

①:导包

        
            org.springframework.cloud
            spring-cloud-starter-openfeign
        

②:创建feign的controller,接收请求的入口!

@RestController
@RequestMapping("/es")
public class EScontroller {
    @Autowired
    private CourseDocRepository courseDocRepository;
    @GetMapping("/get/{id}")
    public AjaxResult deleteById(@PathVariable("id") Long id){
        courseDocRepository.deleteById(id);
        return AjaxResult.me();
    }
    @PostMapping("/save")
    public AjaxResult save(@RequestBody CourseDoc courseDoc){
        courseDocRepository.save(courseDoc);
        return AjaxResult.me();
    }
}

③:创建feign的接口,分发请求到controller的具体方法

@FeignClient(value = "es-server" ,fallbackFactory = ESFeignFallbackFactory.class)
public interface ESFeignClient{
    @GetMapping("/es/get/{id}")
    AjaxResult deleteById(@PathVariable("id") Long id);
    @PostMapping("/es/save")
    AjaxResult save(@RequestBody CourseDoc courseDoc);
}

④:创建托底类,调用链发生异常后Hystrix返回托底数据!

@Component
public class ESFeignFallbackFactory implements FallbackFactory {
    @Override
    public ESFeignClient create(Throwable throwable) {
        return new ESFeignClient() {
            @Override
            public AjaxResult deleteById(Long id) {
                throwable.printStackTrace();
                return AjaxResult.me().setSuccess(false)
                        .setMessage("发生了一点小问题:["+throwable.getMessage()+"]");
            }

            @Override
            public AjaxResult save(CourseDoc courseDoc) {
                throwable.printStackTrace();
                return AjaxResult.me().setSuccess(false)
                        .setMessage("发生了一点小问题:["+throwable.getMessage()+"]");
            }
        };
    }
}

⑤:消费者微服务开启feign(谁调用谁开启),课程微服务开启feign

@SpringBootApplication
@MapperScan("com.hanfengyi.course.mapper")
@EnableTransactionManagement
@EnableFeignClients("com.hanfengyi.feign")
public class CourseService2020 {
    public static void main(String[] args) {
        SpringApplication.run(CourseService2020.class);
    }
}

@EnableFeignClients("com.hanfengyi.feign"):注意这里feign的包名要与提供者微服务的集成feign的包名要一致,如果不一致,要指定包名!

⑥:课程业务层执行的业务

@Service
public class CourseServiceImpl extends ServiceImpl implements ICourseService {
    @Autowired
    private CourseDetailMapper courseDetailMapper;
    @Autowired
    private CourseMarketMapper courseMarketMapper;
    @Autowired
    private ESFeignClient esFeignClient;
    @Override
    public void offLineCourse(Long id) {
        //1. 根据id查询课程
        Course course = baseMapper.selectById(id);
        //2. 判断课程状态是否是上线状态
        if(course!=null && course.getStatus()==1){
            //3. 删除es中的数据
            AjaxResult ajaxResult = esFeignClient.deleteById(id);
            //es中数据删除成功
            if(ajaxResult.isSuccess()){
                //4. 修改mysql中的课程中的课程状态为下线状态
                course.setStatus(Course.COURSE_OFFLINE);
                baseMapper.updateById(course);
            }
        }else{
            throw new RuntimeException("课程不存在或者课程已处于下线状态");
        }
    }

    @Override
    public void onLineCourse(Long id) {
        //1. 根据id查询课程
        Course course = baseMapper.selectById(id);
        //2. 判断课程状态是否是下线状态
        if(course!=null && course.getStatus()==0){
            //3. 封装CourseDoc对象
            CourseDoc courseDoc = new CourseDoc();
            //使用工具类拷贝属性
            BeanUtils.copyProperties(course, courseDoc);

            CourseDetail courseDetail = courseDetailMapper.selectById(course.getId());
            BeanUtils.copyProperties(courseDetail, courseDoc);

            CourseMarket courseMarket = courseMarketMapper.selectById(course.getId());
            courseDoc.setChargeName(courseMarket.getCharge().longValue()==1?"收费":"免费");
            BeanUtils.copyProperties(courseMarket, courseDoc);
            //4. 保存到es中
            AjaxResult ajaxResult = esFeignClient.save(courseDoc);
            //es中数据保存成功
            if(ajaxResult.isSuccess()){
                //5. 修改mysql中的课程中的课程状态为上线状态
                course.setStatus(Course.COURSE_ONLINE);
                baseMapper.updateById(course);
            }
        }else{
            throw new RuntimeException("课程不存在或者课程已处于上线状态");
        }


    }
}

4. zuul的饥饿加载配置

zuul:
  ignoredServices: '*' #忽略使用服务名访问
  ribbon:
    eager-load:
      enabled: true

5. ribbon的饥饿加载配置

ribbon:
  eager-load:
    enabled: true
    clients: es-server,redis-server,fastdfs-server #指定ribbon调用的服务名

你可能感兴趣的:(HRM微服务项目day-04-课程上线下线、集成ElasticSearch)