设计模式+聚合搜索

540 阅读3分钟

前言

聚合搜索平台终于写完了。5月5日-今天。中间麦粒肿停了两三天。中午一点左右看第四期视频,一打开给我干蒙好家伙四个半小时,咬咬牙看到七点左右,好在现在实现了所有功能,完结撒花。

总结

系统架构

image.png

初始化项目

前端

  1. Vue+ts
  2. Ant Design Vue
  3. axios

eg:如何创建vue项目不再过多赘述,注意如果是对前端不熟悉建议不要选择代码校验**

Vue引入ant-design和axios
npm install ant-design-vue --save
import axios from 'axios';
引入组件
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import Antd from "ant-design-vue";
import "ant-design-vue/dist/reset.css";
createApp(App).use(Antd).use(router).mount("#app");
注意这里是reset.css,官方文档里的引入实例是错误的。
单独抽取axios
import axios from "axios"

const instance = axios.create({
    baseURL: 'http://localhost:8101/api',
    timeout: 10000,
    headers: {},
});
    
//全局相应拦截器,取出响应的data
// 添加响应拦截器
instance.interceptors.response.use(function (response) {
    // 2xx 范围内的状态码都会触发该函数。
    // 对响应数据做点什么
    const data = response.data;
    if(data.code == 0) {
    return data.data;
    }
    console.error("request error",data);
    return data.data;

  }, function (error) {
    // 超出 2xx 范围的状态码都会触发该函数。
    // 对响应错误做点什么
    return Promise.reject(error);
  });
    export default instance;

后端

  1. springboot-init-master,开箱即食,香!、
  2. Elasticsearch
  3. Logstash
  4. JMeter压力测试
  5. 阿里开源的canul

数据接入

获取数据
  1. 实时抓取,直接调用url
  2. 解析html
处理数据
@Test  
void testFetchPicture() throws IOException {  
int current = 1;  
String url = "https://cn.bing.com/images/search?q=%e5%b0%8f%e9%bb%91%e5%ad%90&qpvt=%e5%b0%8f%e9%bb%91%e5%ad%90&form=IGRE&first=" + current;  
Document doc = Jsoup.connect(url).get();  
Elements newsHeadlines = doc.select(".iuscp.isv");  
List<Picture> pictures = new ArrayList<>();  
  
for (Element element : newsHeadlines) {  
//取图片地址(murl)  
String m = element.select(".iusc").get(0).attr("m");  
Map<String, Object> map = JSONUtil.toBean(m, Map.class);  
String murl = String.valueOf(map.get("murl"));  
// System.out.println(murl);  
//取标题  
String title = element.select(".inflnk").get(0).attr("aria-label");  
// System.out.println(title);  
Picture picture = new Picture();  
picture.setTitle(title);  
picture.setUrl(murl);  
pictures.add(picture);  
}  
}  
  
@Test  
void testFetchPassage() {  
// 获取数据  
String json = "{\"current\":1,\"pageSize\":8,\"sortField\":\"createTime\",\"sortOrder\":\"descend\",\"category\":\"文章\",\"reviewStatus\":1}";  
String result = HttpRequest.post("https://www.code-nav.cn/api/post/search/page/vo")  
.body(json)  
.execute().body();  
System.out.println(result);  
  
// json转对象  
Map<String, Object> map = JSONUtil.toBean(result, Map.class);  
JSONObject data = (JSONObject) map.get("data");  
JSONArray records = (JSONArray) data.get("records");  
List<Post> postList = new ArrayList<>();  
System.out.println(records);  
for (Object record : records) {  
JSONObject tempRecord = (JSONObject) record;  
Post post = new Post();  
post.setTitle(tempRecord.getStr("title"));  
post.setContent(tempRecord.getStr("content"));  
JSONArray tags = (JSONArray) tempRecord.get("tags");  
List<String> tagList = tags.toList(String.class);  
post.setTags(JSONUtil.toJsonStr(tagList));  
post.setUserId(1L);  
postList.add(post);  
  
}  
//数据入库  
boolean b = postService.saveBatch(postList);  
Assertions.assertTrue(b);  
System.out.println(postList);  
}  
  
//  以下是自己试着照猫画虎抓了一下同程旅游
@Test  
void testFetchInform() throws IOException {  
List<Picture> pictures = new ArrayList<>();  
int current = 1;  
String url = "https://www.ly.com/";  
Document doc = Jsoup.connect(url).get();  
System.out.println(doc);  
Elements newsHeadlines = doc.select(".slpnel");  
System.out.println(newsHeadlines);  
for (Element element : newsHeadlines) {  
String title = doc.select(".slpnel").get(0).attr("title");  
String purl = doc.select(".slpnel").get(0).attr("pic_box");  
Picture picture = new Picture();  
picture.setTitle(title);  
picture.setUrl(purl);  
pictures.add(picture);  
  
}
写入数据库

mybatispus批量插入

优化迭代

门面模式

我的理解,前端最少知道原则,只负责数据渲染,不做业务和逻辑上的处理

适配器模式

类似于转接口,例如轻薄本没有网线口,接一个转换口。

image.png

注册器模式

替代if-else


public SearchVO searchAll(@RequestBody SearchRequest searchRequest, HttpServletRequest request) {  
  
/**  
* 如果type为空则搜索所有数据  
* 不为空  
* 合法:查出对应数据  
* 非法:抛出异常  
*/  
  
String type = searchRequest.getType();  
SearchTypeEnum searchTypeEnum = SearchTypeEnum.getEnumByValue(type);  
ThrowUtils.throwIf(StringUtils.isBlank(type), ErrorCode.PARAMS_ERROR);  
String searchText = searchRequest.getSearchText();  
long current = searchRequest.getCurrent();  
long pageSize = searchRequest.getPageSize();  
  
if (searchTypeEnum == null) {  
  
CompletableFuture<Page<UserVO>> userTask = CompletableFuture.supplyAsync(() -> {  
UserQueryRequest userQueryRequest = new UserQueryRequest();  
userQueryRequest.setUserName(searchText);  
Page<UserVO> userVOPage = userDataSource.doSearch(searchText,current,pageSize);  
return userVOPage;  
});  
  
CompletableFuture<Page<PostVO>> postTask = CompletableFuture.supplyAsync(() -> {  
PostQueryRequest postQueryRequest = new PostQueryRequest();  
postQueryRequest.setSearchText(searchText);  
Page<PostVO> postVOPage = postDataSource.doSearch(searchText,current,pageSize);  
return postVOPage;  
});  
  
CompletableFuture<Page<Picture>> pictureTask = CompletableFuture.supplyAsync(() -> {  
Page<Picture> picturePage = pictureDataSource.doSearch(searchText, 1, 10);  
return picturePage;  
});  
  
CompletableFuture.allOf(userTask, postTask, pictureTask);  
  
Page<UserVO> userVOPage = null;  
try {  
userVOPage = userTask.get();  
Page<PostVO> postVOPage = postTask.get();  
Page<Picture> picturePage = pictureTask.get();  
  
  
SearchVO searchVO = new SearchVO();  
searchVO.setUserList(userVOPage.getRecords());  
searchVO.setPostList(postVOPage.getRecords());  
searchVO.setPictureList(picturePage.getRecords());  
return searchVO;  
} catch (InterruptedException e) {  
log.error("线程中断", e);  
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "线程中断");  
} catch (ExecutionException e) {  
log.error("查询异常", e);  
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "查询异常");  
}  
  
}else {  
  
  
SearchVO searchVO = new SearchVO();  
DataSource<?> dataSource = dataSourceRegistry.getDataSourceByType(type);  
Page<?> page = dataSource.doSearch(searchText,current,pageSize);  
searchVO.setDataList(page.getRecords());  
return searchVO;  
}  
  
  
/*------------------------此处再次优化,注册器模式---------------------------------*/  
  
/* SearchVO searchVO = new SearchVO();  
DataSource source = null;  
switch (searchTypeEnum) {  
case POST:  
source = postDataSource;  
break;  
case USER:  
source = userDataSource;  
break;  
case PICTURE:  
source = postDataSource;  
break;  
case VIDEO:  
break;  
default:  
}  
  
Page page = source.doSearch(searchText,current,pageSize);  
  
*/  
  
  
/*-----------------------------此处优化为上面----------------------------*/  
/* switch (searchTypeEnum) {  
case POST:  
PostQueryRequest postQueryRequest = new PostQueryRequest();  
postQueryRequest.setSearchText(searchText);  
Page<PostVO> postVOPage = postDataSource.doSearch(searchText,current,pageSize);  
searchVO.setPostList(postVOPage.getRecords());  
break;  
case USER:  
UserQueryRequest userQueryRequest = new UserQueryRequest();  
userQueryRequest.setUserName(searchText);  
Page<UserVO> userVOPage = userDataSource.doSearch(searchText,current,pageSize);  
searchVO.setUserList(userVOPage.getRecords());  
break;  
case PICTURE:  
Page<Picture> picturePage = pictureDataSource.doSearch(searchText, 1, 10);  
searchVO.setPictureList(picturePage.getRecords());  
break;  
  
default:  
}  
  
  
}*/  
  
}

ES接入

  1. 学习了ES基本的增删改查。
  2. Java客户端链接ES
  3. ES同步数据库,就目前而言我感觉用job更好理解一点

压力测试

到视频最后了注意力没那么集中,囫囵吞枣哈哈

心得体会

这个项目是我的第一个全栈项目,虽然前端页面只有几个组件,但那路由处理却不简单,期间我也碰到了很多问题,在前端耗费了很多时间,好在时间都是我暂停视频自己思考如何解决,然后继续,视频中也会解决。

后端学到了很多,以前写的都是能跑就行的原则,通过这次我有了迭代优化的意识,让自己的代码更优雅。尤其在将搜索帖子的PostDataSource的数据源从MySQL改为ES时,只需要改一个方法名,我深刻体会到之前做的优化多么高明!

在这之前,我喜欢看视频,但完结这个项目以后,我读文档的能力显著提升,我发现文档的内容更加精简,重点。目前正在跟着React官方文档入门。

成果展示

image.png

image.png

image.png