HRM微服務(wù)項(xiàng)目day-04-課程上線下線拜银、集成ElasticSearch

1. ES服務(wù)搭建

前臺(tái)頁(yè)面很多場(chǎng)景下,都需要用到搜索遭垛,ElasticSearch作為分布式全文搜索引擎尼桶,使用restful api對(duì)文檔操作,我們要集成ES耻卡,把它作為一個(gè)獨(dú)立的服務(wù)疯汁!使其他需要集成搜索功能的服務(wù)模塊,只需要通過(guò)feign向ES服務(wù)發(fā)起調(diào)用即可卵酪!

①:創(chuàng)建es服務(wù)模塊:

? hrm-es-parent

? hrm-es-common

? hrm-es-feign

? hrm-es-server-2050

②:集成注冊(cè)中心

③:集成配置中心

④:集成swagger

⑤:集成zuul網(wǎng)關(guān)

⑥:zuul整合swagger

2. ES與SpringBoot的集成

  1. 導(dǎo)入jar包
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>
  1. 創(chuàng)建配置文件
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:1010/eureka/ #注冊(cè)中心服務(wù)端的注冊(cè)地址
  instance:
    prefer-ip-address: true #使用ip進(jìn)行注冊(cè)
    instance-id: es-server:2050  #服務(wù)注冊(cè)到注冊(cè)中心的id
server:
  port: 2050
#應(yīng)用的名字
spring:
  application:
    name: es-server
  data:
    elasticsearch:
      cluster-name: elasticsearch #集群的名字
      cluster-nodes: 127.0.0.1:9300 #9200是圖形界面端,9300代碼端
ribbon: #ribbon超時(shí)
  ReadTimeout: 30000
  ConnectTimeout: 30000
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 40000      
  1. 創(chuàng)建一個(gè)類用來(lái)描述文檔信息
@Document(indexName = "hrm",type = "course")
public class CourseDoc {
    //文檔的ID幌蚊,同時(shí)也是數(shù)據(jù)的id
    @Id
    private Long id;
    //標(biāo)題
    @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;
    //等級(jí)名字
    //@Field(type = FieldType.Keyword)
    private String gradeName;
    //課程等級(jí)
    private Long gradeId;
    //機(jī)構(gòu)id
    private Long tenantId;
    //機(jī)構(gòu)名字
    @Field(type =FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
    private String tenantName;
    //開(kāi)課時(shí)間
    private Date startTime;
    //結(jié)課時(shí)間
    private Date endTime;
    //封面
    private String pic;
    //免費(fèi)、收費(fèi)
    private String chargeName;
    //qq
    private String qq;
    //價(jià)格
    private Float price;
    //原價(jià)
    private Float priceOld;
    //課程介紹
    private String description;
    //上線時(shí)間
    private Date onlineDate = new Date();
    //瀏覽數(shù)
    private Integer viewCount;
    //購(gòu)買數(shù)
    private Integer buyCount;
    ...

@Document(indexName = "hrm",type = "course"):描述文檔信息,indexName:索引庫(kù),type:文檔類型卷仑。
@Id:文檔的ID窜锯,同時(shí)也是數(shù)據(jù)的id

@Field(type =FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word"):描述字段信息驾孔,F(xiàn)ieldType.Text:做分詞處理眉菱,F(xiàn)ieldType.Keyword:不做分詞處理华坦,analyzer:使用什么分詞器做分詞歹袁,searchAnalyzer:使用什么分詞器做查詢孟抗!

  1. 創(chuàng)建一個(gè)接口繼承ElasticsearchRepository,其中有對(duì)文檔操作的所有方法!
@Repository
public interface CourseDocRepository extends ElasticsearchRepository<CourseDoc,Long> {
}
  1. 使用SpringBoot的測(cè)試環(huán)境測(cè)試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("神級(jí)");
            }else{
                courseDoc.setGradeName("低級(jí)");
            }
            courseDoc.setCourseTypeId(Long.valueOf(1+i));

            courseDocRepository.save(courseDoc);
        }


    }
    @Test
    public void testGetDoc(){
        Optional<CourseDoc> optional = courseDocRepository.findById(23l);
        if(optional!=null){
            CourseDoc courseDoc = optional.get();
            System.out.println(courseDoc);
        }
    }
    @Test
    public void testGetAll(){
        Iterator<CourseDoc> 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的內(nèi)置對(duì)象,可以用來(lái)創(chuàng)建索引庫(kù),和指定映射規(guī)則!

高級(jí)查詢:

使用關(guān)鍵對(duì)象NativeSearchQueryBuilder構(gòu)建查詢規(guī)則!

    @Test
    public void testQuery(){
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        /**
         *     高級(jí)查詢分頁(yè)排序: DSL查詢+DSL過(guò)濾
         *     // 需求:查詢 name中包含 “Java”的課程缕陕,
         *     // 并且課程分類 CourseTypeId 在 1 - 10
         *     //課程等級(jí) GradeName為“神級(jí)”
         *     //查詢第2頁(yè)恶座,每頁(yè)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));
        //課程等級(jí) GradeName為“神級(jí)”
        boolQueryBuilder.filter(new TermQueryBuilder("gradeName", "神級(jí)"));
        //按照id倒排序

        builder.withSort(new FieldSortBuilder("id").order(SortOrder.DESC));
        //查詢第1頁(yè),每頁(yè)5條
        builder.withPageable(PageRequest.of(0, 5));

        //執(zhí)行查詢
        Page<CourseDoc> docs = courseDocRepository.search(builder.build());
        System.out.println("總條數(shù):"+docs.getTotalElements());
        System.out.println("查詢的頁(yè)數(shù):"+docs.getTotalPages());
        docs.getContent().forEach(e-> System.out.println("======"+e));
        Iterator<CourseDoc> iterator = docs.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

3. ES與Feign的集成實(shí)現(xiàn)課程的上線下線

上線場(chǎng)景:

前臺(tái)用戶點(diǎn)擊課程上線碴开,請(qǐng)求到達(dá)控制器巴碗,控制器執(zhí)行業(yè)務(wù)凹嘲!

  • 根據(jù)傳過(guò)來(lái)的課程id去數(shù)據(jù)庫(kù)中查詢出來(lái)
  • 判斷是否是下線狀態(tài)
  • 如果是下線狀態(tài)通過(guò)feign調(diào)用es服務(wù),將數(shù)據(jù)保存到es中
  • 修改數(shù)據(jù)庫(kù)中課程的狀態(tài)碼

下線場(chǎng)景:

前臺(tái)用戶點(diǎn)擊課程下線,請(qǐng)求到達(dá)控制器,控制器執(zhí)行業(yè)務(wù)!

  • 根據(jù)傳過(guò)來(lái)的課程id去數(shù)據(jù)庫(kù)中查詢出來(lái)
  • 判斷是否是上線狀態(tài)
  • 如果是上線狀態(tài)通過(guò)feign調(diào)用es服務(wù)抓督,將數(shù)據(jù)從es中刪除
  • 修改數(shù)據(jù)庫(kù)中課程的狀態(tài)碼

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的集成

①:導(dǎo)包

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

②:創(chuàng)建feign的controller桑驱,接收請(qǐng)求的入口!

@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();
    }
}

③:創(chuàng)建feign的接口,分發(fā)請(qǐng)求到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);
}

④:創(chuàng)建托底類谴分,調(diào)用鏈發(fā)生異常后Hystrix返回托底數(shù)據(jù)锈麸!

@Component
public class ESFeignFallbackFactory implements FallbackFactory<ESFeignClient> {
    @Override
    public ESFeignClient create(Throwable throwable) {
        return new ESFeignClient() {
            @Override
            public AjaxResult deleteById(Long id) {
                throwable.printStackTrace();
                return AjaxResult.me().setSuccess(false)
                        .setMessage("發(fā)生了一點(diǎn)小問(wèn)題:["+throwable.getMessage()+"]");
            }

            @Override
            public AjaxResult save(CourseDoc courseDoc) {
                throwable.printStackTrace();
                return AjaxResult.me().setSuccess(false)
                        .setMessage("發(fā)生了一點(diǎn)小問(wèn)題:["+throwable.getMessage()+"]");
            }
        };
    }
}

⑤:消費(fèi)者微服務(wù)開(kāi)啟feign(誰(shuí)調(diào)用誰(shuí)開(kāi)啟),課程微服務(wù)開(kāi)啟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的包名要與提供者微服務(wù)的集成feign的包名要一致牺蹄,如果不一致忘伞,要指定包名!

⑥:課程業(yè)務(wù)層執(zhí)行的業(yè)務(wù)

@Service
public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> implements ICourseService {
    @Autowired
    private CourseDetailMapper courseDetailMapper;
    @Autowired
    private CourseMarketMapper courseMarketMapper;
    @Autowired
    private ESFeignClient esFeignClient;
    @Override
    public void offLineCourse(Long id) {
        //1. 根據(jù)id查詢課程
        Course course = baseMapper.selectById(id);
        //2. 判斷課程狀態(tài)是否是上線狀態(tài)
        if(course!=null && course.getStatus()==1){
            //3. 刪除es中的數(shù)據(jù)
            AjaxResult ajaxResult = esFeignClient.deleteById(id);
            //es中數(shù)據(jù)刪除成功
            if(ajaxResult.isSuccess()){
                //4. 修改mysql中的課程中的課程狀態(tài)為下線狀態(tài)
                course.setStatus(Course.COURSE_OFFLINE);
                baseMapper.updateById(course);
            }
        }else{
            throw new RuntimeException("課程不存在或者課程已處于下線狀態(tài)");
        }
    }

    @Override
    public void onLineCourse(Long id) {
        //1. 根據(jù)id查詢課程
        Course course = baseMapper.selectById(id);
        //2. 判斷課程狀態(tài)是否是下線狀態(tài)
        if(course!=null && course.getStatus()==0){
            //3. 封裝CourseDoc對(duì)象
            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?"收費(fèi)":"免費(fèi)");
            BeanUtils.copyProperties(courseMarket, courseDoc);
            //4. 保存到es中
            AjaxResult ajaxResult = esFeignClient.save(courseDoc);
            //es中數(shù)據(jù)保存成功
            if(ajaxResult.isSuccess()){
                //5. 修改mysql中的課程中的課程狀態(tài)為上線狀態(tài)
                course.setStatus(Course.COURSE_ONLINE);
                baseMapper.updateById(course);
            }
        }else{
            throw new RuntimeException("課程不存在或者課程已處于上線狀態(tài)");
        }


    }
}

4. zuul的饑餓加載配置

zuul:
  ignoredServices: '*' #忽略使用服務(wù)名訪問(wèn)
  ribbon:
    eager-load:
      enabled: true

5. ribbon的饑餓加載配置

ribbon:
  eager-load:
    enabled: true
    clients: es-server,redis-server,fastdfs-server #指定ribbon調(diào)用的服務(wù)名
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末沙兰,一起剝皮案震驚了整個(gè)濱河市氓奈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鼎天,老刑警劉巖舀奶,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異斋射,居然都是意外死亡育勺,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門罗岖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)涧至,“玉大人,你說(shuō)我怎么就攤上這事桑包∧吓睿” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵哑了,是天一觀的道長(zhǎng)赘方。 經(jīng)常有香客問(wèn)我,道長(zhǎng)弱左,這世上最難降的妖魔是什么窄陡? 我笑而不...
    開(kāi)封第一講書人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮拆火,結(jié)果婚禮上泳梆,老公的妹妹穿的比我還像新娘。我一直安慰自己榜掌,他們只是感情好优妙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著憎账,像睡著了一般套硼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上胞皱,一...
    開(kāi)封第一講書人閱讀 51,598評(píng)論 1 305
  • 那天邪意,我揣著相機(jī)與錄音九妈,去河邊找鬼。 笑死雾鬼,一個(gè)胖子當(dāng)著我的面吹牛萌朱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播策菜,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼晶疼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了又憨?” 一聲冷哼從身側(cè)響起翠霍,我...
    開(kāi)封第一講書人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蠢莺,沒(méi)想到半個(gè)月后寒匙,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡躏将,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年锄弱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片祸憋。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡会宪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出夺衍,到底是詐尸還是另有隱情,我是刑警寧澤喜命,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布沟沙,位于F島的核電站,受9級(jí)特大地震影響壁榕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一雷则、第九天 我趴在偏房一處隱蔽的房頂上張望死嗦。 院中可真熱鬧,春花似錦牡辽、人聲如沸喳篇。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)麸澜。三九已至,卻和暖如春奏黑,著一層夾襖步出監(jiān)牢的瞬間炊邦,已是汗流浹背编矾。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留馁害,地道東北人窄俏。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像碘菜,于是被迫代替她去往敵國(guó)和親凹蜈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 1. 課程上下線業(yè)務(wù)1.1 業(yè)務(wù)描述1.2 技術(shù)方案1.2.1 技術(shù)方案一:數(shù)據(jù)庫(kù)狀態(tài)做判斷-->不可取1.2.2...
    程序員Darker閱讀 430評(píng)論 0 0
  • HRM項(xiàng)目Day02-課程類型菜單樹(shù)炉媒、集成redis緩存 1. 課程類型的菜單樹(shù)展示 domain @TableF...
    hanfy閱讀 140評(píng)論 0 0
  • 一. 環(huán)境搭建 二. 完成hrm-eureka-server-1010的搭建 三. 完成hrm-gateway-z...
    hanfy閱讀 239評(píng)論 0 0
  • 1. 項(xiàng)目功能架構(gòu)1.1 項(xiàng)目背景1.2 功能模塊1.3 項(xiàng)目原型 2. 項(xiàng)目技術(shù)架構(gòu)2.1 技術(shù)棧2.2 開(kāi)發(fā)步...
    程序員Darker閱讀 441評(píng)論 0 0
  • 1. 搭建頁(yè)面代理模塊1.1 步驟分析1.2 步驟實(shí)現(xiàn) 2. 發(fā)布頁(yè)面架構(gòu)圖2.1 頁(yè)面發(fā)布生產(chǎn)方2.1.1 頁(yè)面...
    程序員Darker閱讀 370評(píng)論 0 0