fix:课程搜索项目

上级 055c0541
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
<dependency> <dependency>
<groupId>org.elasticsearch.client</groupId> <groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId> <artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.3.0</version> <version>7.12.0</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>org.elasticsearch</groupId> <groupId>org.elasticsearch</groupId>
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
<dependency> <dependency>
<groupId>org.elasticsearch</groupId> <groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId> <artifactId>elasticsearch</artifactId>
<version>7.3.0</version> <version>7.12.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
...@@ -39,15 +39,30 @@ ...@@ -39,15 +39,30 @@
<scope>test</scope> <scope>test</scope>
<version>2.0.6.RELEASE</version> <version>2.0.6.RELEASE</version>
</dependency> </dependency>
<dependency>
<groupId>org.elasticsearch.plugin</groupId>
<artifactId>x-pack-sql-jdbc</artifactId>
<version>7.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
<version>1.16.10</version> <version>1.16.10</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.elasticsearch.plugin</groupId> <groupId>org.apache.commons</groupId>
<artifactId>x-pack-sql-jdbc</artifactId> <artifactId>commons-io</artifactId>
<version>7.3.0</version> <version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.1</version>
</dependency> </dependency>
</dependencies> </dependencies>
<repositories> <repositories>
......
...@@ -3,10 +3,9 @@ package com.kwan.shuyu; ...@@ -3,10 +3,9 @@ package com.kwan.shuyu;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication @SpringBootApplication
public class SearchApplication { public class SearchApplication {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(SearchApplication.class,args); SpringApplication.run(SearchApplication.class, args);
} }
} }
package com.kwan.shuyu.controller;
import com.kwan.shuyu.domain.CoursePub;
import com.kwan.shuyu.domain.CourseSearchParam;
import com.kwan.shuyu.domain.QueryResponseResult;
import com.kwan.shuyu.service.EsCourseServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/search/course")
public class EsCourseController {
@Autowired
private EsCourseServiceImpl esCourseServiceImpl;
@GetMapping(value = "/list/{page}/{size}")
public QueryResponseResult<CoursePub> list(@PathVariable("page") int page
, @PathVariable("size") int size, CourseSearchParam courseSearchParam) {
return esCourseServiceImpl.list(page, size, courseSearchParam);
}
}
\ No newline at end of file
package com.kwan.shuyu.domain;
import lombok.ToString;
/**
* @author : qinyingjie
* @version : 2.2.0
* @date : 2023/5/13 22:20
*/
@ToString
public enum CommonCode implements ResultCode {
INVALID_PARAM(false, 10003, "非法参数!"),
SUCCESS(true, 10000, "操作成功!"),
FAIL(false, 11111, "操作失败!"),
UNAUTHENTICATED(false, 10001, "此操作需要登陆系统!"),
UNAUTHORISE(false, 10002, "权限不足,无权操作!"),
SERVER_ERROR(false, 99999, "抱歉,系统繁忙,请稍后重试!");
/**
* 操作是否成功
*/
boolean success;
/**
* 操作代码
*/
int code;
/**
* 提示信息
*/
String message;
private CommonCode(boolean success, int code, String message) {
this.success = success;
this.code = code;
this.message = message;
}
@Override
public boolean success() {
return success;
}
@Override
public int code() {
return code;
}
@Override
public String message() {
return message;
}
}
package com.kwan.shuyu.domain;
import lombok.Data;
import lombok.ToString;
import java.io.Serializable;
import java.util.Date;
@Data
@ToString
public class CoursePub implements Serializable {
private static final long serialVersionUID = -916357110051689487L;
private String id;
private String name;
private String users;
private String mt;
private String st;
private String grade;
private String studymodel;
private String teachmode;
private String description;
/**
* 图片
*/
private String pic;
/**
* 时间戳
*/
private Date timestamp;
private String charge;
private String valid;
private String qq;
private Double price;
private Double price_old;
private String expires;
/**
* 课程计划
*/
private String teachplan;
/**
* 课程发布时间
*/
private String pubTime;
}
\ No newline at end of file
package com.kwan.shuyu.domain;
import lombok.Data;
import lombok.ToString;
@Data
@ToString
public class CourseSearchParam {
/**
* 关键字
*/
String keyword;
/**
* 一级分类
*/
String mt;
/**
* 二级分类
*/
String st;
/**
* 难度等级
*/
String grade;
/**
* 价格区间
*/
Float price_min;
Float price_max;
/**
* 排序字段
*/
String sort;
/**
* 过虑字段
*/
String filter;
}
\ No newline at end of file
package com.kwan.shuyu.domain;
import lombok.Data;
import lombok.ToString;
@Data
@ToString
public class QueryResponseResult<T> extends ResponseResult {
QueryResult<T> queryResult;
public QueryResponseResult(ResultCode resultCode, QueryResult queryResult) {
super(resultCode);
this.queryResult = queryResult;
}
}
\ No newline at end of file
package com.kwan.shuyu.domain;
import lombok.Data;
import lombok.ToString;
import java.util.List;
@Data
@ToString
public class QueryResult<T> {
/**
* 数据列表
*/
private List<T> list;
/**
* 数据总数
*/
private long total;
}
package com.kwan.shuyu.domain;
public interface Response {
public static final boolean SUCCESS = true;
public static final int SUCCESS_CODE = 10000;
}
package com.kwan.shuyu.domain;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@ToString
@NoArgsConstructor
public class ResponseResult implements Response {
/**
* 操作是否成功
*/
boolean success = SUCCESS;
/**
* 操作代码
*/
int code = SUCCESS_CODE;
/**
* 提示信息
*/
String message;
public ResponseResult(ResultCode resultCode) {
this.success = resultCode.success();
this.code = resultCode.code();
this.message = resultCode.message();
}
}
package com.kwan.shuyu.domain;
public interface ResultCode {
/**
* 操作是否成功,true为成功,false操作失败
*/
boolean success();
/**
* 操作代码
*/
int code();
/**
* 提示信息
*/
String message();
}
package com.kwan.shuyu.service;
import com.kwan.shuyu.domain.*;
import com.kwan.shuyu.service.impl.EsCourseService;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Service
public class EsCourseServiceImpl implements EsCourseService {
@Value("${kwan.course.source_field}")
private String source_field;
@Autowired
private RestHighLevelClient restHighLevelClient;
/**
* 课程搜索
*
* @param page
* @param size
* @param courseSearchParam
* @return
*/
@Override
public QueryResponseResult<CoursePub> list(int page, int size, CourseSearchParam courseSearchParam) {
if (courseSearchParam == null) {
courseSearchParam = new CourseSearchParam();
}
//1创建搜索请求对象
SearchRequest searchRequest = new SearchRequest("xc_course");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//过虑源字段
String[] source_field_array = source_field.split(",");
searchSourceBuilder.fetchSource(source_field_array, new String[]{});
//创建布尔查询对象
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//搜索条件
//根据关键字搜索
if (StringUtils.isNotEmpty(courseSearchParam.getKeyword())) {
MultiMatchQueryBuilder multiMatchQueryBuilder =
QueryBuilders
.multiMatchQuery(courseSearchParam.getKeyword(), "name", "description", "teachplan")
.minimumShouldMatch("70%")
.field("name", 10);
boolQueryBuilder.must(multiMatchQueryBuilder);
}
if (StringUtils.isNotEmpty(courseSearchParam.getMt())) {
//根据一级分类
boolQueryBuilder.filter(QueryBuilders.termQuery("mt", courseSearchParam.getMt()));
}
if (StringUtils.isNotEmpty(courseSearchParam.getSt())) {
//根据二级分类
boolQueryBuilder.filter(QueryBuilders.termQuery("st", courseSearchParam.getSt()));
}
if (StringUtils.isNotEmpty(courseSearchParam.getGrade())) {
//根据难度等级
boolQueryBuilder.filter(QueryBuilders.termQuery("grade", courseSearchParam.getGrade()));
}
//设置boolQueryBuilder到searchSourceBuilder
searchSourceBuilder.query(boolQueryBuilder);
//设置分页参数
if (page <= 0) {
page = 1;
}
if (size <= 0) {
size = 12;
}
//起始记录下标
int from = (page - 1) * size;
searchSourceBuilder.from(from);
searchSourceBuilder.size(size);
//设置高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.preTags("<font class='eslight'>");
highlightBuilder.postTags("</font>");
//设置高亮字段
highlightBuilder.fields().add(new HighlightBuilder.Field("name"));
searchSourceBuilder.highlighter(highlightBuilder);
searchRequest.source(searchSourceBuilder);
QueryResult<CoursePub> queryResult = new QueryResult();
List<CoursePub> list = new ArrayList<>();
try {
//2执行搜索
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
//3获取响应结果
SearchHits hits = searchResponse.getHits();
long totalHits = hits.getTotalHits().value;
//匹配的总记录数
queryResult.setTotal(totalHits);
SearchHit[] searchHits = hits.getHits();
for (SearchHit hit : searchHits) {
CoursePub coursePub = new CoursePub();
//源文档
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
//取出id
String id = (String) sourceAsMap.get("id");
coursePub.setId(id);
//取出name
String name = (String) sourceAsMap.get("name");
//取出高亮字段name
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
if (highlightFields != null) {
HighlightField highlightFieldName = highlightFields.get("name");
if (highlightFieldName != null) {
Text[] fragments = highlightFieldName.fragments();
StringBuffer stringBuffer = new StringBuffer();
for (Text text : fragments) {
stringBuffer.append(text);
}
name = stringBuffer.toString();
}
}
coursePub.setName(name);
//图片
String pic = (String) sourceAsMap.get("pic");
coursePub.setPic(pic);
//价格
Double price = null;
try {
if (sourceAsMap.get("price") != null) {
price = (Double) sourceAsMap.get("price");
}
} catch (Exception e) {
e.printStackTrace();
}
coursePub.setPrice(price);
//旧价格
Double price_old = null;
try {
if (sourceAsMap.get("price_old") != null) {
price_old = (Double) sourceAsMap.get("price_old");
}
} catch (Exception e) {
e.printStackTrace();
}
coursePub.setPrice_old(price_old);
//将coursePub对象放入list
list.add(coursePub);
}
} catch (IOException e) {
e.printStackTrace();
}
queryResult.setList(list);
QueryResponseResult<CoursePub> queryResponseResult = new QueryResponseResult<>(CommonCode.SUCCESS, queryResult);
return queryResponseResult;
}
}
\ No newline at end of file
package com.kwan.shuyu.service.impl;
import com.kwan.shuyu.domain.CoursePub;
import com.kwan.shuyu.domain.CourseSearchParam;
import com.kwan.shuyu.domain.QueryResponseResult;
public interface EsCourseService {
/**
* 查询课程
*
* @param page
* @param size
* @param courseSearchParam
* @return
*/
QueryResponseResult<CoursePub> list(int page, int size, CourseSearchParam courseSearchParam);
}
\ No newline at end of file
...@@ -7,3 +7,5 @@ spring: ...@@ -7,3 +7,5 @@ spring:
kwan: kwan:
elasticsearch: elasticsearch:
hostlist: 47.119.160.231:9200 #多个节点用逗号分隔 hostlist: 47.119.160.231:9200 #多个节点用逗号分隔
course: #课程字段
source_field: id,name,grade,mt,st,charge,valid,pic,qq,price,price_old,status,studymodel,teachmode,expires,pub_time,start_time,end_time
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<configuration> <configuration>
<!--定义日志文件的存储地址,使用绝对路径--> <!--定义日志文件的存储地址,使用绝对路径-->
<property name="LOG_HOME" value="/Users/qinyingjie/Downloads/"/> <property name="LOG_HOME" value="/Users/qinyingjie/Downloads/"/>
<!-- Console 输出设置 --> <!-- Console 输出设置 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder> <encoder>
...@@ -12,7 +10,6 @@ ...@@ -12,7 +10,6 @@
<charset>utf8</charset> <charset>utf8</charset>
</encoder> </encoder>
</appender> </appender>
<!-- 按照每天生成日志文件 --> <!-- 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
...@@ -23,7 +20,6 @@ ...@@ -23,7 +20,6 @@
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder> </encoder>
</appender> </appender>
<!-- 异步输出 --> <!-- 异步输出 -->
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender"> <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 --> <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
...@@ -33,8 +29,6 @@ ...@@ -33,8 +29,6 @@
<!-- 添加附加的appender,最多只能添加一个 --> <!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="FILE"/> <appender-ref ref="FILE"/>
</appender> </appender>
<logger name="org.apache.ibatis.cache.decorators.LoggingCache" level="DEBUG" additivity="false"> <logger name="org.apache.ibatis.cache.decorators.LoggingCache" level="DEBUG" additivity="false">
<appender-ref ref="CONSOLE"/> <appender-ref ref="CONSOLE"/>
</logger> </logger>
......
package es_06_log;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Random;
/**
* 测试ELK查看log
*
* @author : qinyingjie
* @version : 2.2.0
* @date : 2023/5/13 22:14
*/
@SpringBootTest
@RunWith(SpringRunner.class)
public class TestLog {
private static final Logger LOGGER = LoggerFactory.getLogger(TestLog.class);
@Test
public void testLog() {
Random random = new Random();
while (true) {
int userid = random.nextInt(10);
LOGGER.info("userId:{},send:{}", userid, "hello world.I am " + userid);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册