FAQ搜索平台搭建

最近尝试做了一个简易的搜索平台,下面一起回顾一下搭建过程。

技术栈:

springboot,MySQL5.1.73,mybatis

搭建思路分析

MySQL建表

对于问题进行分类,这里有faq_hotel,faq_bussiness几张表,结构都是相同的

 CREATE TABLE `faq_others` (
     26   `id` int(11) NOT NULL AUTO_INCREMENT,
     27   `question` varchar(100) DEFAULT NULL,
     28   `answer` varchar(6000) DEFAULT NULL,
     29   `sign` varchar(10) DEFAULT NULL,
     30   PRIMARY KEY (`id`)
     31 ) ENGINE=MyISAM AUTO_INCREMENT=190 DEFAULT CHARSET=utf8;

具体实现

开启springboot,mybatis和druid

具体可查看我的另外一篇文章。这里不再重复。

实体类

根据数据库的表结构,我们创建出FAQ的实体类

import lombok.Data;
import lombok.ToString;
/*
用lombok插件实现了getter,setter,tostring,提高代码简洁性
*/
@Data
@ToString
public class FAQ {
    private Integer id;
    private String answer;
    private String question;
    private String sign;
}

我们再创建一个FAQ Collection的类来保存搜索记录,以便后台管理人员数据管理,也可以方便用户快捷搜索。

import lombok.Data;
import lombok.ToString;

@Data
@ToString
public class FAQCollection {
    private Integer id;
    private Integer searchcount;
    private String question;

}

前端页面

既然是FAQ,用户可以自由输入问题进行查询,创建hello.html文件,这里我们给一个简单的搜索框即可,通过表单以POST的方式传给后台。

<div class="search-box">
    <form action="/list" method="post">
        <input name="question" class="form-control input-lg" type="text" placeholder="请输入问题"
               autofocus="autofocus"
               th:value="${inputtext}"/>
        <button class="btn btn-lg" type="submit">
            <i class="fa fa-search"></i>
        </button>
    </form>
</div>

简单的搜索框之外,我们还可以在首页显示热搜,即数据库里搜索靠前的关键词方便用户搜索。

<div class="row text-center">
    <div class="col-12">
        <div>热门关键字:
            <span id="keyword">

                    <span th:each="kw:${keywords}">
                    <a th:href="'/list?question='+${kw}" th:text="${kw}"></a>
                    </span>
                 </span>
        </div>
    </div>
</div>

这里的keywords即是从后台获取的排名靠前的关键词,

Controller层

接下来就是实现步骤的核心过程了,希望你能深吸一口气。点击查询时,访问路径为/list,我们来看看在controller里list实现了什么。

@Controller
public class HelloController {
    @Autowired
    FAQService faqService;//稍后再说它,别急。
    @RequestMapping("hello")
    public String home(Model model) {
        model.addAttribute("question",true);
        List<String> keywords=getKeywords();
        model.addAttribute("keywords",keywords);
        return "hello";
    }
    @RequestMapping("/list")
    public String SearchAll(Model model,
                            @RequestParam ("question") String question,
                            HttpServletRequest request)
    {
        model.addAttribute("question",true);
        if(question==null){
            return "hello";
        }
        model.addAttribute("inputtext",question);
        List<String> keywords=getKeywords();
        model.addAttribute("keywords",keywords);
        List<FAQ> results=faqService.getResults(question);
        model.addAttribute("results",results);
        return "hello";
    }

hello是平台的访问路径,第一个跳入我们眼帘的是model.addAttribute("question",true);这个是方便后面的页面渲染的,你先记住它,稍后会有妙用。接下来就是他了List<String> keywords=getKeywords();,他实现了获取热搜关键词的功能,现在我们需要跟数据库打交道了哦,老思路,调用service,再到Mapper跟数据库交流。为方便调用就把getkeywords()的方法写在同一个controller下

public List<String> getKeywords(){
    return faqService.getKeywords();
    }

Service层:

public interface FAQService {
    public List<String> getKeywords();
    public List<FAQ> getResults(String question);
    public FAQ getAnswer(String sign,String question_id);
    public List<String> findAllTables();

}

Service实现类:

@Service
public class FAQServiceImpl implements FAQService {
    @Autowired
    FAQMapper faqMapper;
    @Override
    public List<String> getKeywords(){
    List<FAQCollection> collections=faqMapper.getKeywords();
    List<String> keywords=new ArrayList<>();
    for(int i=0;i<collections.size();i++){
        String question=collections.get(i).getQuestion();
        keywords.add(question);
    }
    return keywords;
    }
     @Override
    public List<FAQ> getResults(String question){
        List<FAQ> list = new ArrayList<>();
        for (String name : findAllTables()) {
            List<FAQ> temp = faqMapper.searchAll(name, question);
//            System.out.println(temp);
            for (FAQ faq : temp) {
                String key=faq.getQuestion().toLowerCase();
                faq.setQuestion(key.replace(question.toLowerCase(), Contants.PRE+question+Contants.SUFFIX));
                faq.setSign(name);


            }//faq赋值后没有进行任何操作,这一步意义何在?关键词标红
            list.addAll(temp);
        }
        return list;
    }
    @Override
    public FAQ getAnswer(String sign,String question_id){
    return faqMapper.findAnswerById(sign, question_id);
    }
    @Override
    public List<String> findAllTables() {

        return faqMapper.findAllTables();
    }
  }

Mapper层:

@Repository
@Mapper
public interface FAQMapper {

    @Select("show tables like '%faq%'")
    List<String> findAllTables();
    @Insert("insert into ratetest(rate) values (#{rate})")
    void insertRate(@Param("rate") String rate);
    @Select("select *from ratetest order by id DESC limit 1")
    @Results({
            @Result(property = "rate",column = "rate"),
            @Result(property = "time",column = "time")
    })
    List<Rate> getNewestRate();

    @Select("select *from search_question_collection order by search_count DESC limit 5")
    List<FAQCollection> getKeywords();

    @Select("select id,question from ${tableName} where question like '%${question}%'")
    List<FAQ> searchAll(@Param("tableName") String tableName, @Param("question") String question);

    @Select("SELECT question,answer FROM ${name} WHERE id=${id}")
    FAQ findAnswerById(@Param("name") String name, @Param("id") String id);
}

在拿到热搜词后,是时候在前端把他展现出来了。

<div class="row text-center">
    <div class="col-12">
        <div>热门关键字:
            <span id="keyword">

                    <span th:each="kw:${keywords}">
                    <a th:href="'/list?question='+${kw}" th:text="${kw}"></a>
                    </span>
                 </span>
        </div>
    </div>
</div>

解决获取热搜词后,最关键的还是返回问题的答案。List<FAQ> results=faqService.getResults(question);,来看看service做了什么操作。

    @Override
    public List<FAQ> getResults(String question){
        List<FAQ> list = new ArrayList<>();
        for (String name : findAllTables()) {
            List<FAQ> temp = faqMapper.searchAll(name, question);
//            System.out.println(temp);
            for (FAQ faq : temp) {
                String key=faq.getQuestion().toLowerCase();
                faq.setQuestion(key.replace(question.toLowerCase(), Contants.PRE+question+Contants.SUFFIX));
                faq.setSign(name);
            }//faq赋值后没有进行任何操作,这一步意义何在?关键词标红
            list.addAll(temp);
        }
        return list;
    }

findAllTables()是什么?他是找到数据库中含有faq的表,这些表含有了问题的答案。在service实现类里实现该方法。

@Override
    public List<String> findAllTables() {

        return faqMapper.findAllTables();
    }

Mapper层:

 @Select("show tables like '%faq%'")
    List<String> findAllTables();

在找到问题所在的几个表后,就可以带着用户输入的问题到一个个表里搜索相关数据了,也就是List<FAQ> temp = faqMapper.searchAll(name, question);

@Select("select id,question from ${tableName} where question like '%${question}%'")
    List<FAQ> searchAll(@Param("tableName") String tableName, @Param("question") String question);

找到的是问题的id和问题名称,细心的你应该发现了这时答案还没有拿到哦。定位到一个具体的问题答案时,需要问题id,表名,这时将拿到的结果赋值问题类型也就是表名,最后将搜索结果添加到list里。

for (FAQ faq : temp) {
                String key=faq.getQuestion().toLowerCase();
                faq.setQuestion(key.replace(question.toLowerCase(), Contants.PRE+question+Contants.SUFFIX));//关键词标红
                faq.setSign(name);
            }//关键词标红和记录表名
            list.addAll(temp);

至此,已经找到了相关的问题答案列表,将他们展现到前端。展现到前端时,像百度搜索结果一样都会标红问题关键词,来看看是怎么实现的。只需要一个Contants接口即可:

public interface Contants {

    String PRE = "<b class='text-danger'>";
    String SUFFIX = "</b>";

    SimpleDateFormat SIMPLE_DATE_FORMAT=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
<ol id="js-result" th:if="${question}">
            <li th:each="person:${results}">
                <div class="search-result">
                    <a class="h2" th:href="'/list/'+${person.sign}+'/'+${person.id}"
                       th:utext="${person.question}"></a>
                </div>
            </li>
        </ol>

还记得之前的model.addAttribute("question",true);吗,因为结果页面和搜索页面全部综合在了一个HTML文件上,question是作为显示什么页面的一个关键标志,当question为true时渲染出的是用户搜索得出的答案列表,当为false时展现的是单一问题的具体答案。用户在拿到答案列表后,下一个行为便是进入单一问题答案了。也就是

<a class="h2" th:href="'/list/'+${person.sign}+'/'+${person.id}"
                       th:utext="${person.question}"></a>

点击链接后再到Controller层进行处理用户请求:

@RequestMapping("/list/{sign}/{id}")
    public String getAnswer(Model model, @PathVariable String sign,@PathVariable String id){
        model.addAttribute("question",false);
        FAQ answer=faqService.getAnswer(sign,id);
        model.addAttribute("aer",answer);
        return "hello";
    }

还是一样的思路,service到mapper
Service实现类

@Override
    public FAQ getAnswer(String sign,String question_id){
    return faqMapper.findAnswerById(sign, question_id);
    }

Mapper:

@Select("SELECT question,answer FROM ${name} WHERE id=${id}")
    FAQ findAnswerById(@Param("name") String name, @Param("id") String id);

拿到的答案放入model里,最后返回HTML页面,接下来就是惊动人心的时刻了,经过漫长的追寻之旅,答案终于展现了在我们眼前:

<div th:if="${question}==false">
            <div class="text-primary">
                <p class="h3" th:text="${aer.question}"></p>
            </div>
            <div class="h6 animated fadeInRight text-left" style="line-height:30px">
                <p th:utext=" ${aer.answer}">
                </p>
            </div>
        </div>

流程走完了,还得给网页加一个异常处理,谁知道用户会输入什么网址呢?Springboot2.0集成了比较好的异常处理类ErrorController,只要简单的调用即可。

@Controller
public class GrobalException implements ErrorController {
    private static final String ERROR_PATH = "/error";

    @RequestMapping(value = ERROR_PATH)
    public String handleError() {
        return "404";
    }

    @Override
    public String getErrorPath() {
        return ERROR_PATH;
    }
}

404页面:

<div class="middle-box text-center animated fadeInDown">
    <h1>404</h1>
    <p class="h2 font-bold">
        页面未找到!
        <a href="/hello">返回首页>></a>
    </p>
</div>

咦,是不是感觉少了些什么,刚刚说的搜索行为记录呢,别急,在下一版本中,我们将记录用户的搜索行为和访问IP,顺利的话还会添加一个用户问题反馈功能,对答案的有用性进行评价。敬请期待!

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值