韵搜坊 -- 聚合接口优化(设计模式)

门面模式

帮助我们用户(客户端)去更轻松的实现功能,不需要关心门面背后的细节,当调用你接口的客户端觉得麻烦的时候,是不是就可以抽象成一个门面了?
后端
将controller层的业务代码抽象到manager/SearchFacade中
前端
IndexPage代码改造为加载单类数据

<template>
  <div class="index-page">
  <a-input-search
      v-model:value="searchText"
      placeholder="input search text"
      enter-button
      @search="onSearch"
    />
        <MyDivider/>
      <a-tabs v-model:activeKey="activeKey" @change="onTabChange">
    <a-tab-pane key="post" tab="文章" > <PostList :post-list="postList"/></a-tab-pane>
    <a-tab-pane key="picture" tab="图片" force-render><PictureList :picture-list="pictureList"/></a-tab-pane>
    
    <a-tab-pane key="user" tab="用户">
      <UserList :user-list="userList"/></a-tab-pane>
    
  </a-tabs>
  </div>
  
</template>

<script setup lang="ts">
import {ref, watchEffect} from 'vue';
import PostList from '@/components/PostList.vue';
import PictureList from '@/components/PictureList.vue';
import UserList from '@/components/UserList.vue';
import MyDivider from '@/components/MyDivider.vue';
import { useLink, useRoute, useRouter } from 'vue-router';
import myAxios from '@/plugins/myAxios';
import { message } from 'ant-design-vue';
//创建路由实例
const router = useRouter();
//获取路由所有信息
const route = useRoute();
const activeKey = route.params.category;
const initSearchParams = {
  type: activeKey,
  text:"",
  pageSize: 10,
  pageNum: 1,
};
const searchText = ref(route.query.text || "");
//把initSearchParams变为响应式对象
const searchParams = ref(initSearchParams);
const postList = ref([]);
const userList = ref([]);
const pictureList = ref([]);
//旧 :加载数据
const loadDataOld = (params: any) => {
  const pictureQuery = {
    ...params,
    searchText: params.text,
  };
  myAxios.post("/picture/search/page/vo",pictureQuery).then((res: any) => {
  pictureList.value = res.records;
});
  const postQuery = {
    ...params,
    searchText: params.text,
  };
  myAxios.post("/post/list/page/vo", postQuery).then((res: any) => {
  postList.value = res.records;
});
const userQuery = {
  ...params,
  userName: params.text,
}
myAxios.post("/user/list/page/vo", userQuery).then((res: any) => {
  userList.value = res.records;
});
};
//新 :加载聚合数据
const loadAllData = (params: any) => {
  const query = {
    ...params,
    searchText: params.text,
  };
  myAxios.post("/search/all",query).then((res: any) => {
  userList.value = res.userList;
  pictureList.value = res.pictureList;
  postList.value = res.postList;
});
};
//更新 :加载单类数据
const loadData = (params: any) => {
  const {type} = params;
  if(!type){
    message.error("类别为空");
    return;
  }
   const query = {
    ...params,
    searchText: params.text,
  };
  myAxios.post("/search/all",query).then((res: any) => {
  if(type === "post"){
  postList.value = res.postList;
  }else if(type === "user"){
  userList.value = res.userList;
  }else if(type === "picture"){
  pictureList.value = res.pictureList;
  }
});
};
//监视路由变化
watchEffect(() =>{
  searchParams.value = {
  ...initSearchParams,
  text: route.query.text,
  type: route.params.category,
  } as any;
  loadData(searchParams.value);
});
//用户每次搜索
loadData(initSearchParams);
const onSearch = (value: string) =>{
  //把搜索框里面的内容压到路由中
  router.push({
    query:{
      ...searchParams.value,
      text: value,
    },
  });
  loadData(searchParams.value);
};
//每次导航栏切换
const onTabChange = (key: string) => {
  router.push({
    path: `/${key}`,
    query: searchParams.value,
  });
};
</script>

适配器模式

  1. 定制统一的数据源接入范围
  2. 适配器模式的作用:通过转换,让两个系统能够对接

后端数据源统一接口

package com.yupi.springbootinit.dataSource;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.poi.ss.formula.functions.T;

/**
 * 数据源接口
 * (新接入的数据源必须实现)
 */
public interface DataSource<T> {

    Page<T> doSearch(String searchText, long pageNum, long pageSize);
}

图片适配器

package com.yupi.springbootinit.dataSource;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yupi.springbootinit.common.ErrorCode;
import com.yupi.springbootinit.exception.BusinessException;
import com.yupi.springbootinit.model.entity.Picture;
import com.yupi.springbootinit.service.PictureService;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 帖子服务实现
 *
 *
 *
 */
@Service
@Slf4j
public class PictureDataSource implements DataSource<Picture> {

    @Override
    public Page<Picture> doSearch(String searchText, long pageNum, long pageSize) {
        long current = (pageNum - 1) * pageSize;
        String url = String.format("https://cn.bing.com/images/search?q=%s&first=%s",searchText,current);
        Document doc = null;
        try {
            doc = Jsoup.connect(url).get();
        } catch (IOException e) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "数据抓取失败");
        }
        Elements elements = doc.select(".iuscp.isv"); //数组,每个元素是每一张图片
        List<Picture> pictures = new ArrayList<>();
        for (Element element : elements) {
            //取图片地址murl
            String m = element.select(".iusc").attr("m");
            Map<String, Object> map = JSONUtil.toBean(m, Map.class);
            String murl = (String) map.get("murl");
            //取标题
            String title = element.select(".inflnk").attr("aria-label");
            Picture picture = new Picture();
            picture.setTitle(title);
            picture.setUrl(murl);
            pictures.add(picture);
            if (pictures.size() >= pageSize)
                break;
        }
        Page<Picture> picturePage = new Page<>(pageNum,pageSize);
        picturePage.setRecords(pictures);
        return  picturePage;
    }
}

帖子适配器

package com.yupi.springbootinit.dataSource;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yupi.springbootinit.common.ErrorCode;
import com.yupi.springbootinit.constant.CommonConstant;
import com.yupi.springbootinit.exception.BusinessException;
import com.yupi.springbootinit.exception.ThrowUtils;
import com.yupi.springbootinit.mapper.PostFavourMapper;
import com.yupi.springbootinit.mapper.PostMapper;
import com.yupi.springbootinit.mapper.PostThumbMapper;
import com.yupi.springbootinit.model.dto.post.PostEsDTO;
import com.yupi.springbootinit.model.dto.post.PostQueryRequest;
import com.yupi.springbootinit.model.entity.Post;
import com.yupi.springbootinit.model.entity.PostFavour;
import com.yupi.springbootinit.model.entity.PostThumb;
import com.yupi.springbootinit.model.entity.User;
import com.yupi.springbootinit.model.vo.PostVO;
import com.yupi.springbootinit.model.vo.UserVO;
import com.yupi.springbootinit.service.PostService;
import com.yupi.springbootinit.service.UserService;
import com.yupi.springbootinit.utils.SqlUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 帖子服务实现
 */
@Service
@Slf4j
public class PostDataSource extends ServiceImpl<PostMapper, Post> implements DataSource<PostVO> {


    @Resource
    private PostService postService;

    @Override
    public Page<PostVO> doSearch(String searchText, long pageNum, long pageSize) {
        PostQueryRequest postQueryRequest = new PostQueryRequest();
        postQueryRequest.setSearchText(searchText);
        postQueryRequest.setCurrent(pageNum);
        postQueryRequest.setPageSize(pageSize);
        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
        Page<PostVO> postVOPage = postService.listPostVOByPage(postQueryRequest, request);
        return postVOPage;
    }
}

用户适配器

package com.yupi.springbootinit.dataSource;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yupi.springbootinit.common.ErrorCode;
import com.yupi.springbootinit.constant.CommonConstant;
import com.yupi.springbootinit.exception.BusinessException;
import com.yupi.springbootinit.mapper.UserMapper;
import com.yupi.springbootinit.model.dto.user.UserQueryRequest;
import com.yupi.springbootinit.model.entity.User;
import com.yupi.springbootinit.model.enums.UserRoleEnum;
import com.yupi.springbootinit.model.vo.LoginUserVO;
import com.yupi.springbootinit.model.vo.UserVO;
import com.yupi.springbootinit.service.UserService;
import com.yupi.springbootinit.utils.SqlUtils;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import javax.annotation.RegEx;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import static com.yupi.springbootinit.constant.UserConstant.USER_LOGIN_STATE;

/**
 * 用户服务实现
 *
 *
 *
 */
@Service
@Slf4j
public class UserDataSource extends ServiceImpl<UserMapper, User> implements DataSource {

    @Resource
    private UserService userService;

    @Override
    public Page doSearch(String searchText, long pageNum, long pageSize) {
        UserQueryRequest userQueryRequest = new UserQueryRequest();
        userQueryRequest.setUserName(searchText);
        Page<User> userPage = this.page(new Page<>(pageNum, pageSize),
                userService.getQueryWrapper(userQueryRequest));
        Page<UserVO> userVOPage = new Page<>(pageNum, pageSize, userPage.getTotal());
        List<UserVO> userVO = userService.getUserVO(userPage.getRecords());
        userVOPage.setRecords(userVO);
        return userVOPage;
    }
}

修改SearchFacade

@Component
@Slf4j
public class SearchFacade {
    @Resource
    private PictureDataSource pictureDataSource;


    @Resource
    private UserDataSource userDataSource;

    @Resource
    private PostDataSource postDataSource;

    public SearchVO searchAll(@RequestBody SearchRequest searchRequest, HttpServletRequest request) {
        String type = searchRequest.getType();
        ThrowUtils.throwIf(StringUtils.isBlank(type), ErrorCode.PARAMS_ERROR);
        SearchTypeEnum searchTypeEnum = SearchTypeEnum.getEnumByValue(type);
        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).join();
            try {
                Page<UserVO> userVOPage = userTask.get();
                Page<PostVO> postVOPage = postTask.get();
                Page<Picture> picturePage = pictureTask.get();
                SearchVO searchVO = new SearchVO();
                searchVO.setUserList(userVOPage.getRecords());
                searchVO.setPictureList(picturePage.getRecords());
                searchVO.setPostList(postVOPage.getRecords());
                return  searchVO;
            }catch (Exception e){
                log.error("查询异常",e);
                throw new BusinessException(ErrorCode.SYSTEM_ERROR,"查询异常");
            }
        }else{
            Map<String, DataSource> typeDataSourceMap = new HashMap(){{
             put(SearchTypeEnum.POST.getValue(),postDataSource);
             put(SearchTypeEnum.USER.getValue(),userDataSource);
             put(SearchTypeEnum.PICTURE.getValue(),pictureDataSource);
            }};
            SearchVO searchVO = new SearchVO();
            DataSource dataSource = typeDataSourceMap.get(type);
            Page page = dataSource.doSearch(searchText, current, pageSize);
            searchVO.setDataList(page.getRecords());
            return  searchVO;
        }
    }

}


注册器模式

(本质也是单例)
提前用一个map或者其他类型存储好后面需要调用的对象。
作用:代码量大幅度减少,可维护可扩展

注册器

@Component
public class DataSourceRegistry {

    @Resource
    private PictureDataSource pictureDataSource;


    @Resource
    private UserDataSource userDataSource;

    @Resource
    private PostDataSource postDataSource;

    private Map<String, DataSource> typeDataSourceMap;

    /**
     * @PostConstruct标识的方法会在Spring容器完成依赖注入后立即执行。
     * 没有这个初始化方法的话,依赖注入以后还没有执行typeDataSourceMap的赋值操作,就调用了getDataSourceByType方法,肯定是返回空的
     */
    @PostConstruct
    public void init() {
        typeDataSourceMap = new HashMap() {
            {
                put(SearchTypeEnum.POST.getValue(), postDataSource);
                put(SearchTypeEnum.USER.getValue(), userDataSource);
                put(SearchTypeEnum.PICTURE.getValue(), pictureDataSource);
            }
        };
    }

    public DataSource getDataSourceByType(String type) {
        if(typeDataSourceMap == null){
            return null;
        }
        return typeDataSourceMap.get(type);
    }
}

SearFacade类又精简了

@Component
@Slf4j
public class SearchFacade {
    @Resource
    private PictureDataSource pictureDataSource;


    @Resource
    private UserDataSource userDataSource;

    @Resource
    private PostDataSource postDataSource;

    @Resource
    private DataSourceRegistry dataSourceRegistry;

    public SearchVO searchAll(@RequestBody SearchRequest searchRequest, HttpServletRequest request) {
        String type = searchRequest.getType();
        ThrowUtils.throwIf(StringUtils.isBlank(type), ErrorCode.PARAMS_ERROR);
        SearchTypeEnum searchTypeEnum = SearchTypeEnum.getEnumByValue(type);
        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).join();
            try {
                Page<UserVO> userVOPage = userTask.get();
                Page<PostVO> postVOPage = postTask.get();
                Page<Picture> picturePage = pictureTask.get();
                SearchVO searchVO = new SearchVO();
                searchVO.setUserList(userVOPage.getRecords());
                searchVO.setPictureList(picturePage.getRecords());
                searchVO.setPostList(postVOPage.getRecords());
                return  searchVO;
            }catch (Exception 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;
        }
    }

}

  • 36
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值