系统运维--项目中问题的自动化查询

来源:互联网 发布:大数据工程师做什么 编辑:程序博客网 时间:2024/06/05 17:37

最近比较闲,领导给定了个小任务,在项目里加一个帮助与反馈功能。

项目越做内容越多越杂,一线客服任务重,客服解决不了的问题转到开发部,也耽误时间,感觉这个功能是挺需要。

看了微信和支付宝的问题反馈功能,第一感觉是,他们为什么这个功能做的那么隐蔽。。。

拿微信为例吧,分析一下几个功能点,和我准备做的一些东西。

代码在最后。。。。。


--------------------------------------------分界线--------------------------------------------

<1>问题内容

做这么一个帮助与反馈,其实最麻烦的其实是去维护一个知识库,可能要长时间的记录,一线客服的反馈,开发测试人员自己发现的bug(开发一般不会自己找自己bug。。)

总之,要维护这么一个知识问答库。

按数据库每条问答是一条数据来算,除了问题、解答 还需要那些附件字段

我目前设计的表结构如下:

CREATE TABLE    xxxxxxxxx_table    (        QUESTIONID INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY,        SCENETYPEID VARCHAR(5),        QUESTIONTYPEID VARCHAR(5),        QUESTIONDESCRIPTION VARCHAR(120),        QUESTIONANSWER VARCHAR(1000),        IS_USED VARCHAR(1),        ADDTM TIMESTAMP    )

没错。。我们项目用的老掉牙的DB2.。。

说说为什么这么设计吧,

问题ID自增  不解释了

问题场景ID 问题类型ID  考虑到后面设计分组查找 所有有这个字段

问题描述  和  问题解答

是否使用   有些问题这个版本bug是存在,可能下个版本就修复,所有可以在修复后的次上线,把这个问题隐藏起来


<2>热点问题  及 全部问题

还是按微信的设计来,热点问题 全部问题的展示。

那热点有了问题ID这个模块就很简单了,可以使用配置文件去配置热点问题的ID,查询出问题描述 和 问题解答  去展示就行了。

全部问题  我自己理解不是展示库里全量的数据,而是分类查询的入库,这里就用到我数据库设计的两个字段  问题场景ID 问题类型ID。

拿我自己做的项目为例,场景分类 :S1开卡签约理财类  S2贷款面签类 S3其他类       类型分类:L1图片上传类 L2业务办理流程类 L3业务提交报错类 L4其他、设备类

大概可以覆盖全量的常见问题。

页面感觉可以设计成如下:


场景分类查询--------->>

开卡签约理财类--------->

贷款面签类--------------->

其他类 -------------------->

类型分类查询--------->>

图片上传类--------------->

业务办理流程类--------->

业务提交报错类 -------->

其他、设备类------------>

代码贴一下,就只贴逻辑部分  和sql部分的


查询问题描述

//入参String questionId = busiCtx.get().getParam("questionId","");//問題ID  可以用:分割   此id为热门问题  可以在配置文件或前端传入String sceneTypeId = busiCtx.get().getParam("sceneTypeId","");//場景分類編碼String questionTypeId = busiCtx.get().getParam("questionTypeId","");//問題分類編碼String page = busiCtx.get().getParam("page","1");//页码String number = busiCtx.get().getParam("number","5");//条数int statrNo = (Integer.parseInt(page)-1)*Integer.parseInt(number)+1;int endNo = Integer.parseInt(page)*Integer.parseInt(number);logger.info("開始查詢問題描述...");List<Map<String,String>> list = new ArrayList<Map<String,String>>();try {try {if(!StringUtil.isNullOrEmpty(questionId) && StringUtil.isNullOrEmpty(sceneTypeId) && StringUtil.isNullOrEmpty(questionTypeId) ){logger.info("通过问题id查询:"+questionId);String[] idArr = questionId.split(":");for(int i=0;i<idArr.length;i++){Map<String,String> map = new HashMap<String,String>();map = AnswersAndQuestions.QueryQuestionsByQuestionId(idArr[i]);list.add(map);}}else if(StringUtil.isNullOrEmpty(questionId) && !StringUtil.isNullOrEmpty(sceneTypeId) && StringUtil.isNullOrEmpty(questionTypeId) ){logger.info("通过场景编码查询"+sceneTypeId);list = AnswersAndQuestions.QueryQuestionsBySceneTypeId(sceneTypeId);}else if(StringUtil.isNullOrEmpty(questionId) && StringUtil.isNullOrEmpty(sceneTypeId) && !StringUtil.isNullOrEmpty(questionTypeId) ){logger.info("通过问题类型编码查询"+questionTypeId);list = AnswersAndQuestions.QueryQuestionsByQuestionTypeId(questionTypeId);}else if(StringUtil.isNullOrEmpty(questionId) && StringUtil.isNullOrEmpty(sceneTypeId) && StringUtil.isNullOrEmpty(questionTypeId)){logger.info("通过全量问题。page:"+page+",number:"+number+",===="+statrNo+" to "+endNo);Map<String, String> map = new HashMap<String, String>();map.put("statrNo",String.valueOf(statrNo));map.put("endNo",String.valueOf(endNo));list = AnswersAndQuestions.QueryAllQuestions(map);}else{logger.error("入參檢查失敗!!!");rspCtx.get().addElement(AppConstants.STATUS).addText(AppConstants.FAIL);rspCtx.get().addElement(AppConstants.MSG).addText("入參檢查失敗!!!請聯係科技部開發人員!!!");return;}logger.info("開始查詢問題結束。list======"+list.toString());} catch (Exception e) {logger.info("開始查詢問題結束,查詢失敗!!!");rspCtx.get().addElement(AppConstants.STATUS).addText(AppConstants.FAIL);rspCtx.get().addElement(AppConstants.MSG).addText("服務調用失敗!!!請聯係科技部開發人員!!!");return;}if(!list.isEmpty()){Element table = rspCtx.get().addElement("questionMap");table.addAttribute(MBElement.TYPE, MBElement.LIST);String[] xpaths = { "questionId", "questionDescription"};String[] keys = { "QUESTIONID", "QUESTIONDESCRIPTION"};for (Map<String, String> parmMap : list){Element element = table.addElement("map");StringUtil.mapToXml(element, xpaths, keys, parmMap);logger.info("map===================" + parmMap);}rspCtx.get().addElement(AppConstants.MSG).addText("查询成功");rspCtx.get().addElement(AppConstants.STATUS).addText(AppConstants.AAAAAAA);}else{rspCtx.get().addElement(AppConstants.STATUS).addText(AppConstants.FAIL);rspCtx.get().addElement(AppConstants.MSG).addText("查询成功但返回结果为空!!!請聯係科技部開發人員!!!");}} catch (Exception e) {rspCtx.get().addElement(AppConstants.STATUS).addText(AppConstants.FAIL);rspCtx.get().addElement(AppConstants.MSG).addText("發生未知異常!!!請聯係科技部開發人員!!!");}
代码挺烂的。。。望理解。。。我会努力的。。。。

<select id="queryQuestionsByQuestionId" parameterClass="String"resultClass="java.util.HashMap"> <![CDATA[SELECT QUESTIONID,QUESTIONDESCRIPTION FROM ${PUB}.YDYY_ANSWER_QUESTION WHERE QUESTIONID=#questionId#]]></select><select id="queryQuestionsBySceneTypeId" parameterClass="String" resultClass="java.util.HashMap"> <![CDATA[SELECT QUESTIONID,QUESTIONDESCRIPTION FROM ${PUB}.YDYY_ANSWER_QUESTION WHERE SCENETYPEID=#sceneTypeId#]]></select><select id="queryQuestionsByQuestionTypeId" parameterClass="String"resultClass="java.util.HashMap"> <![CDATA[SELECT QUESTIONID,QUESTIONDESCRIPTION FROM ${PUB}.YDYY_ANSWER_QUESTION WHERE QUESTIONTYPEID=#questionTypeId#]]></select><select id="queryAllQuestions" parameterClass="java.util.HashMap"resultClass="java.util.HashMap"> <![CDATA[SELECT QUESTIONID,QUESTIONDESCRIPTION FROM ${PUB}.YDYY_ANSWER_QUESTION WHERE QUESTIONID>=#statrNo# and QUESTIONID<=#endNo#]]></select><select id="queryQuestionAnswerByQuestionId" parameterClass="String"resultClass="String"> <![CDATA[SELECT QUESTIONANSWER FROM ${PUB}.YDYY_ANSWER_QUESTION WHERE QUESTIONID=#questionId#]]></select><select id="questionFuzzyQuery" parameterClass="String"resultClass="java.util.HashMap"> <![CDATA[SELECT QUESTIONID,QUESTIONDESCRIPTION FROM ${PUB}.YDYY_ANSWER_QUESTION WHERE QUESTIONDESCRIPTION LIKE #fuzzyStr# OR QUESTIONANSWER LIKE #fuzzyStr#]]></select>

项目用的mybatis  ,自己去对应吧我就不删减了

说一下,为什么我查询都只查询  ID 和问题描述  不去查询问题答案。

两个原因:

1.还是因为涉及,如果当问题过多的时候,当点击二级目录的时候,把该目录下所有问题答案全查出来,挺费流量

2.目前涉及还全都是文字,如果想增加图片在里面,不是 不可以,还是考虑流量的问题。

所有就先只查询问题描述,不去查问题答案,反正有ID  去查答案不是分分钟。。

我这入参时录的ID目前还是从前端获取,改成读取配置文件也是一句话的是,只是忘了。。。

<3>快捷帮助

可以放每个功能的操作手册,这部分我不是很想开发。。

思路大概是用户一个连接,让客户端调我服务从后台取一个pdf展示

不知道这么做是不是最好,但是大概是这么一个思路


<4  O+  搜索>  这个 O+ 其实是个放大镜

这一部分其实  我目前能力真的做的一般。

先贴代码吧

//入参String fuzzyStr = new String(busiCtx.get().getParam("fuzzyStr","为什么 下载").getBytes("utf-8"));String[] strArr = fuzzyStr.split(" ");StringBuffer sb = null;for(int n=0;n<strArr.length;n++){sb = new StringBuffer(strArr[n]);for(int i=0;i<=sb.length();i+=2){sb.insert(i, '%');}strArr[n] = sb.toString();}List<Map<String, String>> list = new ArrayList<Map<String,String>>();try {try {for(int i=0;i<strArr.length;i++){if(!AnswersAndQuestions.QuestionFuzzyQuery(strArr[i]).isEmpty() && !StringUtil.isNullOrEmpty(strArr[i])){list.addAll(AnswersAndQuestions.QuestionFuzzyQuery(strArr[i]));}}} catch (Exception e) {logger.info("数据库操作失败,查詢失敗!!!");rspCtx.get().addElement(AppConstants.STATUS).addText(AppConstants.FAIL);rspCtx.get().addElement(AppConstants.MSG).addText("数据库操作失敗!!!請聯係科技部開發人員!!!");return;}if(!list.isEmpty()){//结果集合 去处重复元素HashSet<Map<String, String>> set = new HashSet<Map<String, String>>(list);List<Map<String, String>> resultList = new ArrayList<Map<String, String>>();resultList.addAll(set);Element table = rspCtx.get().addElement("questionMap");table.addAttribute(MBElement.TYPE, MBElement.LIST);String[] xpaths = { "questionId", "questionDescription"};String[] keys = { "QUESTIONID", "QUESTIONDESCRIPTION"};for (Map<String, String> parmMap : resultList){Element element = table.addElement("map");StringUtil.mapToXml(element, xpaths, keys, parmMap);logger.info("map===================" + parmMap);}rspCtx.get().addElement(AppConstants.MSG).addText("查询成功");rspCtx.get().addElement(AppConstants.STATUS).addText(AppConstants.AAAAAAA);}else{rspCtx.get().addElement(AppConstants.STATUS).addText(AppConstants.AAAAAAA);rspCtx.get().addElement(AppConstants.MSG).addText("查询成功但返回结果为空。");rspCtx.get().addElement("questionMap").addText("");}} catch (Exception e) {rspCtx.get().addElement(AppConstants.STATUS).addText(AppConstants.FAIL);rspCtx.get().addElement(AppConstants.MSG).addText("发生未知异常!!!请联系科技部开发人员!!!");}
只能叫关键词搜索吧,支持多个词输入 ,能匹配到的都给找出来。

比如我输入 "今天 不错"   就回去按空格拆分成 "今天" "不错",如果用户输入两个空格  其实也无所谓,反正后面有校验。

返回就去遍历   去查呗。

返回的结果做了个重复性校验。

最后展示通过关键词查询出的所有结果。



以上。

总结:

其实服务很少,也没难的点,只是记录我做这个功能的一个过程和思路。

真难得是去维护这样一个库。

其实想一下,这个库的维护其实可以写一个跑批,我传个文件到指定的静态资源服务器,让代码去跑,应该会用到quartz框架。这部分也是要去学习的。

再比如,我的这个分类是不是也可以做成动态的?如果以后增加一个类别,也可以很方便的去修改,前端可以不用动。

在牛逼的,感觉可以做一个对话框,用户输入一句,代码就自动回复。这个我去找过可有开源的代码,但是貌似没找到。

再比如,如果我在数据库,加一个字段存放图片url及图片名,如果这两个字段返回存在,前端自己去下载,并展示会不会更好。

都只是思路,学的东西还很多。

最近在看编译原理,把大学漏的知识要补回来。




天道酬勤,勤能补拙,拙哥最帅。






















<3>快捷帮助

可以放每个功能的操作手册,这部分我不是很想开发。。

思路大概是用户一个连接,让客户端调我服务从后台取一个pdf展示

不知道这么做是不是最好,但是大概是这么一个思路