对客户推送满意度打分评价
一、业务功能描述
聚合聊天页面中,员工与粉丝聊天过程中,后端(定时器)需时刻判断该聊天是否快结束,或者已经结束,然后给粉丝发送即将结束或者结束的消息(链接 - 打开后是评价页面接口),然后粉丝进行评价 - 提交,后端记录评价数据。也可聚合聊天页手动发送评价链接。
sql:
INSERT INTO sys_tf_menu(id, parent_id, menu_name, sort, type, menu_code, page_path, status, component_path) VALUES (1150500, 1110000, '服务评价配置', 40, '1', NULL, '/evaluateConfig', 'E', '../views/qiYun/evaluateConfig.vue');CREATE TABLE qw_evaluate (
id int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
user_id varchar(30) NOT NULL COMMENT '店员企微user_id',
cust_user_id varchar(30) NOT NULL COMMENT '粉丝企微user_id',
jy_id varchar(11) NULL COMMENT '集运人员的账号id',
score int(11) NULL COMMENT '评分',
content text NULL COMMENT '评价内容',
create_time datetime NULL COMMENT '创建时间',
update_time datetime NULL COMMENT '修改时间',
state varchar(2) NULL COMMENT '状态 E-可用;D-不可用',
PRIMARY KEY (id)
);
INSERT INTO ts_dict(code, value, name, type_code, sort, dict_desc, status) VALUES ('1', '1', '1分文案', 'EVALUATE_TEMPLATE', 1, '', 'E');
INSERT INTO ts_dict(code, value, name, type_code, sort, dict_desc, status) VALUES ('2', '2', '2分文案', 'EVALUATE_TEMPLATE', 2, '', 'E');
INSERT INTO ts_dict(code, value, name, type_code, sort, dict_desc, status) VALUES ('3', '3', '3分文案', 'EVALUATE_TEMPLATE', 3, '', 'E');
INSERT INTO ts_dict(code, value, name, type_code, sort, dict_desc, status) VALUES ('4', '4', '4分文案', 'EVALUATE_TEMPLATE', 4, '', 'E');
INSERT INTO ts_dict(code, value, name, type_code, sort, dict_desc, status) VALUES ('5', '5', '5分文案', 'EVALUATE_TEMPLATE', 5, '', 'E');
INSERT INTO ts_dict(code, value, name, type_code, sort, dict_desc, status) VALUES ('6', '6', '6分文案', 'EVALUATE_TEMPLATE', 6, '', 'E');
INSERT INTO ts_dict(code, value, name, type_code, sort, dict_desc, status) VALUES ('7', '7', '7分文案', 'EVALUATE_TEMPLATE', 7, '', 'E');
INSERT INTO ts_dict(code, value, name, type_code, sort, dict_desc, status) VALUES ('8', '8', '8分文案', 'EVALUATE_TEMPLATE', 8, '', 'E');
INSERT INTO ts_dict(code, value, name, type_code, sort, dict_desc, status) VALUES ('9', '9', '9分文案', 'EVALUATE_TEMPLATE', 9, '', 'E');
INSERT INTO ts_dict(code, value, name, type_code, sort, dict_desc, status) VALUES ('10', '10', '10分文案', 'EVALUATE_TEMPLATE', 10, '', 'E');
二、windows页面 - 评价链接内容配置页面
windows前端增加配置页面 - “服务评价配置” (/views/qiYun/evaluateConfig.vue)代码如下:
<template> <div> <div class="container"> <div class="item-box" style="margin-top: 10px;"> <div class="item-name">服务提示内容:</div> <el-input v-model="warnContent" :rows="3" type="textarea" placeholder="请输入内容" style="box-sizing: border-box;" /> </div> <div class="item-box" style="margin-top: 10px;"> <div class="item-name">服务评价内容:</div> <el-input v-model="evaluateContent" :rows="3" type="textarea" placeholder="请输入内容" style="box-sizing: border-box;" /> </div> <div class="item-box" style="margin-top: 10px;justify-content: space-around;"> <el-button v-if="this.$getAuth(this.$route.path, 1)" type="primary" @click="saveConfig()">保存</el-button> </div> </div> </div> </template> <script> import { ref, reactive } from "vue"; import {useRoute} from "vue-router"; import { ElMessage, ElMessageBox } from "element-plus"; import {FL_SERVICE} from "../../api/config"; import {REQ_SUCCESS_GET} from "../../assets/js/commonConfig"; import {sys_isEmpty} from "../../assets/js/commonUtil"; import {getErrMsg, messageError, messageErrorWord} from "../../utils/CommonUtils"; import * as myEnc from "../../assets/js/commonEncryption"; export default { name: "evaluateConfig", data(){ return{ warnContent:'', evaluateContent:'', } }, methods:{ saveConfig: function(){ if(sys_isEmpty(this.warnContent)){ messageErrorWord("服务提示内容未设置"); return; } if(sys_isEmpty(this.evaluateContent)){ messageErrorWord("服务评价内容未设置"); return; } this.$api.saveEvaluateConfig({ warnContent:myEnc.encryptByAES(this.warnContent), evaluateContent:myEnc.encryptByAES(this.evaluateContent), },res=>{ if(res.retCode == REQ_SUCCESS_GET){ ElMessage.success("保存成功") }else{ messageErrorWord('保存失败'); } },err=>{ return messageError(getErrMsg(err,'保存失败')) }) } }, mounted() { this.$api.queryEvaluateConfig({ },res=>{ if(res.retCode == REQ_SUCCESS_GET){ console.log(res) let obj = res.body; if(sys_isEmpty(obj)){ return } if(!sys_isEmpty(obj.warnContent)){ this.warnContent = obj.warnContent; } if(!sys_isEmpty(obj.evaluateContent)){ this.evaluateContent = obj.evaluateContent; } }else{ } },err=>{ return messageError(getErrMsg(err,'读取失败')) }) }, created(){ }, setup() { return { }; }, }; </script> <style scoped> .item-box{ width: 100%; display: flex; } .item-name{ width: 250px; height: 50px; line-height: 50px; } .item-date-box{ width: 100%; height: 50px; display: flex; align-items: center; /* justify-content: space-between; */ } </style>
apiIndex.js 中
//查询服务评价配置
export const queryEvaluateConfig = (p, successBack, errorBack) => req.doPost(FL_SERVICE + '/chatData/queryEvaluateConfig', '', p, successBack, errorBack)
//保存服务评价配置
export const saveEvaluateConfig = (p, successBack, errorBack) => req.doPost(FL_SERVICE + '/chatData/saveEvaluateConfig', '', p, successBack, errorBack)
//聚合聊天页 - 主动推送服务评价
export const activePushEvaluate = (p, successBack, errorBack) => req.doPost(FL_SERVICE + '/chatData/activePushEvaluate', '', p, successBack, errorBack)
saveEvaluateConfig,
queryEvaluateConfig,
activePushEvaluate,
evaluateConfig.vue 页面效果如下,注意:服务评价内容有带参数的

三、手机端页面(粉丝打开的评价页面)
记得配一下阿里矢量图标库:参考( https://blog.csdn.net/qq_30255033/article/details/143208950?ops_request_misc=%257B%2522request%255Fid%2522%253A%252206d91f4159b7d6800d9389e583e34b7c%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=06d91f4159b7d6800d9389e583e34b7c&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-2-143208950-null-null.142^v101^pc_search_result_base7&utm_term=vue%E9%85%8D%E7%BD%AE%E9%98%BF%E9%87%8C%E7%9F%A2%E9%87%8F%E5%9B%BE%E6%A0%87svg%E6%80%8E%E4%B9%88%E7%94%A8&spm=1018.2226.3001.4187)
<template> <div> <div class="container"> <div class="logobox"> <img alt="logo" class="logo" src="./img/logo.png"> <h3 class="logoh1">用户至上 用心服务</h3> </div> <div class="con"> <img alt="" class="tx" src="./img/tx2.png"> <div class="xmh1" ><span style="margin-right: 10px" > {{ info.userName || '' }} </span> <svg class="icon" aria-hidden="true" v-for="(item, index) in startsLength" :key="index" > <use xlink:href="#icon-xingxing"></use> </svg> <!-- <div class="xmsp" v-if="info.serviceTitle">{{ info.serviceTitle || '' }}</div>--> </div> <div class="xmp1"> <img alt="" class="icon" src="./img/icon1.png"> <div class="xp1">好评率</div> {{ info.favorableRate || '--' }}% </div> </div> <div class="con2"> <div class="pingh1">您对客服的评价</div> <div class="relative"> <div class="flexbox"> <div v-for="(item,index) in items" :key="index" :class="{on:curIndex === index}" class="pli" @click="changeCurs(index)"> <div class="pxu">{{ item.number }}</div> <div class="pxp">{{ item.satisfaction }}</div> </div> </div> <div id="myshow1" class="mybq clearfix"> <p v-if="curIndex !== 0" class="pxp2">非常不满意</p> <p v-if="curIndex !== items.length - 1" class="pxp3">十分满意</p> </div> </div> <div v-if="curIndex != -1" id="myshow2" class="ybox"> <div v-for="(item,index) in evaluations" :key="index" :class="{on:item.checked}" class="ybli" @click="changeChecked(item)"> {{ item.text }} </div> </div> </div> <div class="con2 clearfix"> <img alt="" class="bianji" src="./img/bianji.png"> <textarea v-model="content" class="textarea" placeholder="写写您对客服的评价吧..."></textarea> </div> <button :disabled="btnDisabled" class="tjbtn" @click="submit">匿名提交</button> </div> </div> </template> <script> import {Toast} from "mint-ui"; export default { name: 'userEvaluation', data() { return { items: [ {number: 1, satisfaction: '非常不满意'}, {number: 2, satisfaction: '非常不满意'}, {number: 3, satisfaction: '不满意'}, {number: 4, satisfaction: '不满意'}, {number: 5, satisfaction: '一般'}, {number: 6, satisfaction: '一般'}, {number: 7, satisfaction: '满意'}, {number: 8, satisfaction: '满意'}, {number: 9, satisfaction: '十分满意'}, {number: 10, satisfaction: '十分满意'} ], curIndex: -1, content: '', evaluations: [ { text: '热情大方', checked: false }, { text: '解释清晰', checked: false }, { text: '办理快速', checked: false }, { text: '业务娴熟', checked: false }, { text: '仪容整洁', checked: false }, { text: '耐心细致', checked: false }, ], userId: '', custUserId: '', info: {}, startsLength:[] } }, computed: { btnDisabled() { return this.curIndex === -1 ; } }, mounted() { const {userId, custUserId} = this.$route.query || {} if (userId) this.userId = userId; if (custUserId) this.custUserId = custUserId; if (this.userId && this.custUserId) { this.fetchData(); } }, methods: { changeChecked(item) { item.checked = !item.checked; }, changeCurs(index) { this.curIndex = index; // return this.$api.getEvaluateTemplate({ score: index + 1 }, res => { if (res.retCode == this.$api.REQ_SUCCESS_GET) { const list = res.body.evaluateTemplate.split(','); this.evaluations = list.map(item => { return { text: item, checked: false } }) console.log(list) } else { Toast(res.retMsg); } }, err => { Toast(err.retMsg); // const list = "测试1,测试2,测试3".split(','); // this.evaluations = list.map(item => { // return { // text: item, // checked: false // } // }) }) }, // 提交评价 submit() { if (!this.userId || !this.custUserId) return Toast('请先登录'); const params = { "userId": this.userId, "custUserId": this.custUserId, "score": this.items[this.curIndex].number, "content": this.content + this.evaluations.filter(item => item.checked).map(item => item.text).join('、'), } this.$api.saveEvaluate({ ...params }, res => { if (res.retCode == this.$api.REQ_SUCCESS_GET) { if (res.body.evaluateCode === 1) { Toast('评价成功'); setTimeout(() => { this.$router.push({ path: '/evaluationResult', query: { info: JSON.stringify(this.info) } }) }, 1500) } else { Toast(res.body.evaluateMsg); } } else { Toast(res.retMsg); } }, err => { Toast(err.retMsg); }) }, // 获取评价信息 fetchData() { this.$api.getEvaluateInfo({ userId: this.userId, custUserId: this.custUserId, }, res => { if (res.retCode == this.$api.REQ_SUCCESS_GET) { this.info = res.body; // 字改成对应的星星,动态计算几颗星。 // 十分满意评价占比≥90% 五星 // 满意评价占比≥70% 四星 // 一般评价占比≥50% 三星 // 不满意评价占比≥30% 二星 // 非常不满意评价占比≥10% 一星 this.startsLength = [] if (this.info.favorableRate>=90){ this.startsLength = [1,2,3,4,5] }else if(this.info.favorableRate>=70){ this.startsLength = [1,2,3,4] }else if(this.info.favorableRate>=50){ this.startsLength = [1,2,3] }else if(this.info.favorableRate>=30){ this.startsLength = [1,2] }else if(this.info.favorableRate>=10){ this.startsLength = [1] } } else { Toast(res.retMsg); } }, err => { Toast(err.retMsg); }) } } } </script> <style scoped> @import "./css/common.css"; @import "./css/css.css"; .pli { border-right: 0; } .pli:last-child { border-right: 1px solid #ccc; } </style>
粉丝打开的评价页面是这样的:(手机端打开)

四、后端接口实现
控制层(ChatDataController.java)增加接口代码
/** * 获取服务提示/服务评价缓存内容 * @param req * @return */ @RequestMapping("/queryEvaluateConfig") @ResponseBody public Result queryEvaluateConfig(@RequestBody CommonBaseEvt req){ logger.log(MyLogUtil.LOG_INFO,"开始获取服务提示/服务评价缓存内容"); Result result = new Result(); try { result = qwChatService.queryEvaluateConfig(req); }catch (Exception e){ logger.log(MyLogUtil.LOG_ERROR,"获取服务提示/服务评价缓存内容异常",e); result.error("获取服务提示/服务评价缓存内容异常"); } return result; } /** * 设置服务提示/服务评价缓存内容 * @param params * @return */ @RequestMapping("/saveEvaluateConfig") @ResponseBody public Result saveEvaluateConfig(@RequestBody Map<String,Object> params){ logger.log(MyLogUtil.LOG_INFO,"开始设置服务提示/服务评价缓存内容"); Result result = new Result(); try { params = reqParamUtils.businessParamsDesEnc(params); result = qwChatService.saveEvaluateConfig(params); }catch (Exception e){ logger.log(MyLogUtil.LOG_ERROR,"设置服务提示/服务评价缓存内容异常",e); result.error("设置服务提示/服务评价缓存内容异常"); } return result; } /** * 聚合聊天页面 - 主动推送服务评价 */ @PostMapping("/activePushEvaluate") @ResponseBody public Result activePushEvaluate(@Valid @RequestBody ActivePushEvaluateReq req) { Result result = new Result(); try { req = reqParamUtils.businessParamsDesEnc(ActivePushEvaluateReq.class, req); logger.log(MyLogUtil.LOG_INFO,"主动推送服务评价"+ StringUtil.getJsonStr(req)); JSONArray sendMsgList = new JSONArray(); JSONObject jo = new JSONObject(); jo.put("sendId", req.getSendId()); jo.put("receiveId", req.getReceiveId()); jo.put("serviceCode", null); jo.put("clientId", req.getClientId()); jo.put("phone", req.getTrustMobile()); jo.put("isManagement", 4); sendMsgList.add(jo); List<QwTalkRecordVO> talkList = qwChatService.pushEvaluate(sendMsgList); if(talkList==null){ return result.error("未配置服务评价内容,发送失败!"); } return result.success(talkList); } catch (Exception e) { logger.log(MyLogUtil.LOG_ERROR,"服务提醒/服务评价异常", e); return result.error("服务提醒/服务评价异常:"+e.getMessage()); } } /** * 定时器触发 - 服务提醒/服务评价 */ @PostMapping("/pushEvaluate") @ResponseBody public Result pushEvaluate(@RequestBody JSONObject inJson) { Result result = new Result(); try { logger.log(MyLogUtil.LOG_INFO,"服务提醒/服务评价入参:"+ StringUtil.getJsonStr(inJson)); CallFuncInfoA callFuncInfoA = new CallFuncInfoA("ChatDataController", -1L, "pushEvaluate"); JSONArray sendMsgList = inJson.getJSONArray("sendMsgList"); List<QwTalkRecordVO> talkList = new ArrayList<>(); if(CollectionUtils.isNotEmpty(sendMsgList)){ talkList = qwChatService.pushEvaluate(sendMsgList); } return result.success(talkList); } catch (Exception e) { logger.log(MyLogUtil.LOG_ERROR,"服务提醒/服务评价异常", e); return result.error("服务提醒/服务评价异常:"+e.getMessage()); } } /** * 粉丝手机端 - 查询评价信息(粉丝链接打开的评价页面查询) * @return */ @GetMapping("/getEvaluateInfo") @ResponseBody public Result getEvaluateInfo(@Valid @ModelAttribute GetEvaluateInfoReq req){ logger.log(MyLogUtil.LOG_INFO,"开始查询评价信息内容"); Result result = new Result(); try { logger.log(MyLogUtil.LOG_INFO,"入参:"+ StringUtil.getJsonStr(req)); result = qwChatService.getEvaluateInfo(req); }catch (Exception e){ logger.log(MyLogUtil.LOG_ERROR,"查询评价信息异常",e); result.error("查询评价信息异常"); } return result; }
/**
* 根据分数查询评价文案模板
* @return
*/
@GetMapping("/getEvaluateTemplate")
@ResponseBody
public Result getEvaluateTemplate(@Valid @ModelAttribute GetEvaluateTemplateReq req){
logger.log(MyLogUtil.LOG_INFO,"开始根据分数查询评价文案模板");
Result result = new Result();
try {
logger.log(MyLogUtil.LOG_INFO,"入参:"+ StringUtil.getJsonStr(req));
result = qwChatService.getEvaluateTemplate(req);
}catch (Exception e){
logger.log(MyLogUtil.LOG_ERROR,"根据分数查询评价文案模板异常",e);
result.error("根据分数查询评价文案模板异常");
}
return result;
}
/** * 粉丝手机端 - 客户提交评价 * @return */ @RequestMapping("/saveEvaluate") @ResponseBody public Result saveEvaluate(@RequestBody SaveEvaluateReq req){ logger.log(MyLogUtil.LOG_INFO,"开始查询评价信息内容"); Result result = new Result(); try { logger.log(MyLogUtil.LOG_INFO,"入参:"+ StringUtil.getJsonStr(req)); result = qwChatService.saveEvaluate(req); }catch (Exception e){ logger.log(MyLogUtil.LOG_ERROR,"查询评价信息异常",e); result.error("查询评价信息异常"); } return result; }
实现层(QwChatServiceImpl.java)增加代码
/** * 获取服务提示/服务评价缓存内容 * @param req * @return */ @Override public Result queryEvaluateConfig(@RequestBody CommonBaseEvt req) { Result result = new Result(); // WE_WORK_CHAT_EVALUATE = "we_work_chat_evaluate" - (public static final String WE_WORK_CHAT_EVALUATE = "we_work_chat_evaluate";) String redisKey = RedisCacheKeysUtils.WE_WORK_CHAT_EVALUATE; SysTfUserInfo userInfo = userUtil.getCurUserInfo(req.getReqTicket()); if (userInfo == null) { return result.error("登录失效,请重新登录"); } JSONObject obj = cacheUtils.getJsonValue(redisKey); return result.success(obj); } /** * 设置服务提示/服务评价缓存内容 * @param params * @return * @throws Exception */ @Override public Result saveEvaluateConfig(@RequestBody Map<String, Object> params) throws Exception{ Result result = new Result(); String redisKey = RedisCacheKeysUtils.WE_WORK_CHAT_EVALUATE; SysTfUserInfo userInfo = userUtil.getCurUserInfo(params.get("reqTicket").toString()); if (userInfo == null) { return result.error("登录失效,请重新登录"); } String ak = ""; String ai = ""; if(params.get("reqTicket") !=null && !"".equals(params.get("reqTicket"))){ ak = userUtil.getUserAesKey(String.valueOf(params.get("reqTicket"))); ai = userUtil.getUserAesIv(String.valueOf(params.get("reqTicket"))); } if (params.get("warnContent") != null && params.get("evaluateContent") != null) { JSONObject obj = new JSONObject(); if(params.get("warnContent") !=null && !"".equals(params.get("warnContent"))){ String valueStr = new String(AesEncUtil.desEncrypt(UriUtils.decode(String.valueOf(params.get("warnContent")), "utf-8"), ak, ai)).trim(); obj.put("warnContent",valueStr); } if(params.get("evaluateContent") !=null && !"".equals(params.get("evaluateContent"))){ String valueStr = new String(AesEncUtil.desEncrypt(UriUtils.decode(String.valueOf(params.get("evaluateContent")), "utf-8"), ak, ai)).trim(); obj.put("evaluateContent",valueStr); } cacheUtils.setValue(redisKey, obj); } else { return result.error("未完成设置,关键信息缺失"); } return result.success("保存成功"); } //定义服务称号数组常量 private static final String[] serviceTitleArr = {"五星服务", "四星服务", "三星服务", "二星服务", "一星服务"};
@Override public List<QwTalkRecordVO> pushEvaluate(@RequestBody JSONArray sendMsgList) throws Exception { CallFuncInfoA callFuncInfoA = new CallFuncInfoA("WeWorkChatServiceImpl", -1L, "pushEvaluate"); Long st = System.currentTimeMillis(); logUtil.infoLog(callFuncInfoA,"开始执行服务提升/服务评价推送:"); String warnContent = null; String evaluateContent = null; JSONObject evaluateCache = cacheUtils.getJsonValue(RedisCacheKeysUtils.WE_WORK_CHAT_EVALUATE); if(evaluateCache!=null){ warnContent = evaluateCache.getString("warnContent"); evaluateContent = evaluateCache.getString("evaluateContent"); } if(StringUtils.isEmpty(warnContent) || StringUtils.isEmpty(evaluateContent)){ logUtil.infoLog(callFuncInfoA,"服务提升/服务评价缓存未设置!"); return null; } List<QwTalkRecordVO> talkList = new ArrayList<>(); // 处理发送逻辑 for (int i = 0; i < sendMsgList.size(); i++) { JSONObject jo = sendMsgList.getJSONObject(i); String sendId = jo.getString("sendId"); String receiveId = jo.getString("receiveId"); String serviceCode = jo.getString("serviceCode"); Integer clientId = jo.getInteger("clientId"); String phone = jo.getString("phone"); Integer isManagement = jo.getInteger("isManagement"); String content = warnContent; if(isManagement==4){ // 3-即将服务结束;4-服务结束 content = evaluateContent; } //加密店员/粉丝企微userid String sendIdStr= new String(AesEncUtil.encrypt(String.valueOf(sendId), desKey, desIv)).trim(); String receiveIdStr= new String(AesEncUtil.encrypt(String.valueOf(receiveId), desKey, desIv)).trim(); //对加密串编码处理并替换 content = content.replace("#{userId}", UriUtils.encode(String.valueOf(sendIdStr), "utf-8")); content = content.replace("#{custUserId}", UriUtils.encode(String.valueOf(receiveIdStr), "utf-8"));
// 这里需要改成rpa模式的发送消息接口!!!!!!!!!!!!!!!! QwTalkRecordVO vo = send(callFuncInfoA, serviceCode, clientId, isManagement, sendId, receiveId, content, phone); talkList.add(vo); } Long et = System.currentTimeMillis(); logUtil.infoLog(callFuncInfoA,"更新和新增外部联系人操作结束,耗时:" + (et - st)); return talkList; } /** * 查询评价信息 * @param req 店员userid、客户custUserId * @return 店员userid、客户custUserId、店员名称、好评率、客户今日是否评价 */ @Override public Result getEvaluateInfo(@RequestBody GetEvaluateInfoReq req) throws Exception { Result result = new Result(); Boolean haveEvaluate = false; String userId = AesEncUtil.desEncrypt(UriUtils.decode(req.getUserId(), "utf-8"), desKey, desIv).trim(); String custUserId = AesEncUtil.desEncrypt(UriUtils.decode(req.getCustUserId(), "utf-8"), desKey, desIv).trim(); logger.log(MyLogUtil.LOG_INFO,"解密后userId:"+userId+",custUserId:"+custUserId); //判断当天是否已评价 long evaluateCount = qwEvaluateMapper.todayEvaluateCount(userId, custUserId); if(evaluateCount>0){ haveEvaluate = true; } Object clerkName = null; String serviceTitle = null; BigDecimal favorableRate = null; //查询店员、评价信息 Map<String, Object> staffInfo = staffRelMapper.findStaffInfoByUserId(userId); if(staffInfo!=null){ clerkName = staffInfo.get("clerk_name"); QwEvaluate countQuery = new QwEvaluate(); countQuery.setState("E"); countQuery.setUserId(userId); long count = qwEvaluateMapper.count(countQuery); serviceTitle = serviceTitleArr[0];//没有评价时默认五星服务 // favorableRate = BigDecimal.valueOf(100.00);//没有评价时默认100%好评率 if(count>0){ Map<String, BigDecimal> scoreRatio = qwEvaluateMapper.getScoreRatio(userId); BigDecimal ratioA = scoreRatio.get("ratioA");//十分满意 BigDecimal ratioB = scoreRatio.get("ratioB");//满意 BigDecimal ratioC = scoreRatio.get("ratioC");//一般 BigDecimal ratioD = scoreRatio.get("ratioD");//不满意 BigDecimal ratioE = scoreRatio.get("ratioE");//非常不满意 favorableRate = scoreRatio.get("favorableRate"); //服务称号计算 serviceTitle = calServiceTitle(ratioA, ratioB, ratioC, ratioD, ratioE); } } //根据服务星级动态获取评价文案模板 // String evaluateTemplate = getEvaluateTemplate(serviceTitle); JSONObject jo = new JSONObject(); jo.put("haveEvaluate", haveEvaluate);//客户今日是否评价 jo.put("userId", req.getUserId()); jo.put("custUserId", req.getCustUserId()); jo.put("serviceTitle", serviceTitle);//服务称号 jo.put("favorableRate", favorableRate);//好评率 jo.put("userName", clerkName);//员工姓名 // jo.put("evaluateTemplate", evaluateTemplate);//评价文案模板 logger.log(MyLogUtil.LOG_INFO,"查询评价信息回参:"+StringUtil.getJsonStr(jo)); return result.success(jo); } /** * 计算服务称号 * 服务称号 "五星服务:十分满意评价占比≥90% * 四星服务:满意评价占比≥70% * 三星服务:一般评价占比≥50% * 二星服务:不满意评价占比≥30% * 一星服务:非常不满意评价占比≥10%" * 占比的计算方式:服务评价类型/评价总次数 * @param ratioA 十分满意占比 * @param ratioB 满意占比 * @param ratioC 一般占比 * @param ratioD 不满意占比 * @param ratioE 非常不满意占比 * @return 服务称号 */ public String calServiceTitle(BigDecimal ratioA, BigDecimal ratioB, BigDecimal ratioC, BigDecimal ratioD, BigDecimal ratioE){ if(ratioA.compareTo(new BigDecimal("0.9"))>=0){ return serviceTitleArr[0]; }else if(ratioB.compareTo(new BigDecimal("0.7"))>=0){ return serviceTitleArr[1]; }else if(ratioC.compareTo(new BigDecimal("0.5"))>=0){ return serviceTitleArr[2]; }else if(ratioD.compareTo(new BigDecimal("0.3"))>=0){ return serviceTitleArr[3]; }else if(ratioE.compareTo(new BigDecimal("0.1"))>=0){ return serviceTitleArr[4]; } return serviceTitleArr[4]; }
@Override
public Result getEvaluateTemplate(@RequestBody GetEvaluateTemplateReq req) throws Exception {
Result result = new Result();
String score = req.getScore();
String evaluateTemplate = null;
TsDict dictVo = dictMapper.getEvaluateTemplate(score);
if(dictVo!=null){
evaluateTemplate = dictVo.getDictDesc();
}
JSONObject jo = new JSONObject();
jo.put("score", score);//评价文案模板
jo.put("evaluateTemplate", evaluateTemplate);//评价文案模板
logger.log(MyLogUtil.LOG_INFO,"根据分数查询评价文案模板回参:"+StringUtil.getJsonStr(jo));
return result.success(jo);
}
/** * 客户评价 * 同一客户每日有效评价1次,超出评价无效; * 客户再次进入评价页面进行评价,提交时页面提示您已完成评价,24小时内不可以重新评价哦; * @param req 店员userid、客户custUserId、评价分数 * @return 评价编码(0-评价失败,1-评价成功,2-当天已评价),评价信息 */ @Override public Result saveEvaluate(@RequestBody SaveEvaluateReq req) throws Exception { Result result = new Result(); String userId = AesEncUtil.desEncrypt(UriUtils.decode(req.getUserId(), "utf-8"), desKey, desIv).trim(); String custUserId = AesEncUtil.desEncrypt(UriUtils.decode(req.getCustUserId(), "utf-8"), desKey, desIv).trim(); logger.log(MyLogUtil.LOG_INFO,"解密后userId:"+userId+",custUserId:"+custUserId); Integer evaluateCode = 0; String evaluateMsg = "评价失败"; Integer evaluateId = null; if(StringUtils.isBlank(userId) || StringUtils.isBlank(custUserId)){ return result.error("员工或客户信息为空!"); } //判断当天是否已评价 long evaluateCount = qwEvaluateMapper.todayEvaluateCount(userId, custUserId); if(evaluateCount>0){ evaluateCode = 2; evaluateMsg = "您已完成评价,24小时内不可以重新评价哦!"; }else { //评价 QwEvaluate qwEvaluate = new QwEvaluate(); qwEvaluate.setUserId(userId); qwEvaluate.setCustUserId(custUserId); qwEvaluate.setScore(req.getScore()); qwEvaluate.setContent(req.getContent()); qwEvaluate.setCreateTime(new Date()); qwEvaluate.setUpdateTime(new Date()); qwEvaluate.setState("E"); int insert = qwEvaluateMapper.insert(qwEvaluate); evaluateId = qwEvaluate.getId(); if(insert>0){ evaluateCode = 1; evaluateMsg = "评价成功"; } } JSONObject jo = new JSONObject(); jo.put("userId", req.getUserId()); jo.put("custUserId", req.getCustUserId()); jo.put("score", req.getScore()); jo.put("content", req.getContent()); jo.put("evaluateCode", evaluateCode); jo.put("evaluateMsg", evaluateMsg); logger.log(MyLogUtil.LOG_INFO,"客户评价回参:"+StringUtil.getJsonStr(jo)); CallFuncInfoA callFuncInfoA = new CallFuncInfoA("WeWorkChatService", -1L, "saveEvaluate"); StringBuffer sb =new StringBuffer(); sb.append("入参:"+StringUtil.getJsonStr(req)+";"); sb.append("出参:"+StringUtil.getJsonStr(jo)+";");return result.success(jo); } public QwTalkRecordVO send(CallFuncInfoA callFuncInfoA, String serviceCode, Integer clientId, Integer isManagement, String sendId, String receiveId, String content, String clerkPhone){ QwTalkRecordVO talkRecordVO = new QwTalkRecordVO(); talkRecordVO.setServiceCode(serviceCode); talkRecordVO.setClientId(clientId); talkRecordVO.setSendId(sendId); talkRecordVO.setReceiveId(receiveId); talkRecordVO.setContent(content); talkRecordVO.setIsRead("Y"); talkRecordVO.setMsgType("TXT"); talkRecordVO.setIsManagement(isManagement); talkRecordVO.setIsSync(0); talkRecordVO.setCreateTime(new Date()); talkRecordVO.setCreateTimeStr(getTimeString(talkRecordVO.getCreateTime().getTime())); String snowflakeId = SnowflakeIdUtils.getUUid(); talkRecordVO.setMsgUuid(snowflakeId); talkRecordVO.setPhone(clerkPhone); if (isConnectWindows != null && "N".equals(isConnectWindows)) { talkRecordVO.setSendState("W"); logUtil.infoLog(callFuncInfoA,"服务评价内容存数据库"); boolean addTalkResult = commonUtils.addTalkRecord(talkRecordVO); if (!addTalkResult) { logUtil.infoLog(callFuncInfoA,"服务评价内容存数据库失败"); } } else { String sendResult = robotUtils.sendText(talkRecordVO); boolean flag = false; if ("SUCCESS".equals(sendResult)) { talkRecordVO.setSendState("ING"); talkRecordVO.setErrorMsg("服务评价消息成功,调用发送消息接口成功."); logUtil.infoLog(callFuncInfoA,"服务评价消息成功,调用发送消息接口成功.:"+talkRecordVO.toString()); } else { talkRecordVO.setErrorMsg("服务评价消息失败,调用发送消息接口失败.-"+sendResult); talkRecordVO.setSendState("FAIL"); logUtil.infoLog(callFuncInfoA,"服务评价消息失败,调用发送消息接口失败.:"+talkRecordVO.toString()); } flag = commonUtils.addTalkRecord(talkRecordVO); StringBuffer sb =new StringBuffer(); sb.append("发送文本结果:"+sendResult+";"); sb.append("数据入库结果:"+flag+";"); sb.append("聊天消息:"+ StringUtil.getJsonStr(talkRecordVO)); } return talkRecordVO; } /** * 功能描述 客户列表仿微信时间显示工具 * * @param timestamp * @return java.lang.String * @author rzb * @date 2022/1/26 */ private String getTimeString(Long timestamp) { String result = ""; String[] weekNames = {"周日", "周一", "周二", "周三", "周四", "周五", "周六"}; String hourTimeFormat = "HH:mm"; String monthTimeFormat = "M月d日"; String yearTimeFormat = "yyyy年M月d日"; try { Calendar todayCalendar = Calendar.getInstance(); Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(timestamp); if (todayCalendar.get(Calendar.YEAR) == calendar.get(Calendar.YEAR)) {//当年 if (todayCalendar.get(Calendar.MONTH) == calendar.get(Calendar.MONTH)) {//当月 int temp = todayCalendar.get(Calendar.DAY_OF_MONTH) - calendar.get(Calendar.DAY_OF_MONTH); switch (temp) { case 0://今天 result = getTime(timestamp, hourTimeFormat); break; case 1://昨天 result = "昨天 " + getTime(timestamp, hourTimeFormat); break; case 2: case 3: case 4: case 5: case 6: int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); result = weekNames[dayOfWeek - 1] + " " + getTime(timestamp, hourTimeFormat); break; default: result = getTime(timestamp, monthTimeFormat); break; } } else { result = getTime(timestamp, monthTimeFormat); } } else { result = getTime(timestamp, yearTimeFormat); } return result; } catch (Exception e) { return ""; } } private String getTime(long time, String pattern) { Date date = new Date(time); return dateFormat(date, pattern); } private String dateFormat(Date date, String pattern) { SimpleDateFormat format = new SimpleDateFormat(pattern); return format.format(date); }
QwEvaluateMapper.xml中
<select id="todayEvaluateCount" resultType="java.lang.Long"> select count(1) from qw_evaluate where state='E' and DATE(create_time) = CURRENT_DATE and user_id=#{userId} and cust_user_id=#{custUserId} </select>
<!--统计总行数--> <select id="count" resultType="java.lang.Long"> select count(1) from qw_evaluate <where> <if test="id != null"> and id = #{id} </if> <if test="userId != null and userId != ''"> and user_id = #{userId} </if> <if test="custUserId != null and custUserId != ''"> and cust_user_id = #{custUserId} </if> <if test="score != null"> and score = #{score} </if> <if test="content != null and content != ''"> and content = #{content} </if> <if test="createTime != null"> and create_time = #{createTime} </if> <if test="updateTime != null"> and update_time = #{updateTime} </if> <if test="state != null and state != ''"> and state = #{state} </if> </where> </select>
<select id="getScoreRatio" resultType="java.util.Map"> select ROUND(t."A" * 100.0 / t."count" , 2) AS "ratioA", ROUND(t."B" * 100.0 / t."count" , 2) AS "ratioB", ROUND(t."C" * 100.0 / t."count" , 2) AS "ratioC", ROUND(t."D" * 100.0 / t."count" , 2) AS "ratioD", ROUND(t."E" * 100.0 / t."count" , 2) AS "ratioE", ( SELECT ROUND(SUM(CASE WHEN score BETWEEN 7 AND 10 THEN score ELSE 0 END)::numeric / SUM(score)::numeric * 100, 2) AS "favorableRate" FROM qw_evaluate WHERE state='E' and user_id = #{userId} ) AS "favorableRate" from ( select ( SELECT COUNT(*) FROM qw_evaluate WHERE state='E' and user_id = #{userId} and score BETWEEN 1 AND 2 ) AS "E", ( SELECT COUNT(*) FROM qw_evaluate WHERE state='E' and user_id = #{userId} and score BETWEEN 3 AND 4 ) AS "D", ( SELECT COUNT(*) FROM qw_evaluate WHERE state='E' and user_id = #{userId} and score BETWEEN 5 AND 6 ) AS "C", ( SELECT COUNT(*) FROM qw_evaluate WHERE state='E' and user_id = #{userId} and score BETWEEN 7 AND 8 ) AS "B", ( SELECT COUNT(*) FROM qw_evaluate WHERE state='E' and user_id = #{userId} and score BETWEEN 9 AND 10 ) AS "A", ( SELECT COUNT(*) FROM qw_evaluate WHERE state='E' and user_id = #{userId} ) AS "count" ) t </select>
<select id="todayEvaluateCount" resultType="java.lang.Long"> select count(1) from qw_evaluate where state='E' and DATE(create_time) = CURRENT_DATE and user_id=#{userId} and cust_user_id=#{custUserId} </select>
QwStaffRelMapper.xml中
<select id="findStaffInfoByUserId" resultType="java.util.Map" parameterType="java.lang.String"> SELECT * FROM qw_staff_rel WHERE user_id = #{sendId} and state='E' </select>
TsDictDao.xml中
<select id="getEvaluateTemplate" resultType="cn.com.fl.service.common.entity.PO.TsDict"> select * from ts_dict where status = 'E' and type_code = 'EVALUATE_TEMPLATE' and code = #{code} order by sort limit 1 </select>
RedisCacheKeysUtils.java(工具类)
/** * 企微聊天-服务评价缓存 */ public static final String WE_WORK_CHAT_EVALUATE = "we_work_chat_evaluate";
五、定时器工程代码(WeWorkChatEvaluateTask.java)
获取聊天记录信息,判断是否需要调 service 的 /pushEvaluate 接口
package cn.com.fl.task.impl.wework; import cn.com.fl.common.entity.common.task.SleepTask; import cn.com.fl.common.util.DateUtil; import cn.com.fl.ms.dao.fl.businessprocess.chatTimeout.TalkRecordMapper; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import frame.service.timertask.businessprocess.chatTimeout.entity.VO.QwTalkRecordVO; import frame.service.timertask.businessprocess.utils.HttpClientUtil; import org.apache.commons.collections4.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.*; import java.util.stream.Collectors; @Component("WeWorkChatEvaluateTask") public class WeWorkChatEvaluateTask extends SleepTask { @Value("${notify.host}") private String serviceUrl; @Autowired private HttpClientUtil httpClientUtil; @Resource private TalkRecordMapper talkRecordMapper; public WeWorkChatEvaluateTask() { super("WE_WORK_CHAT_EVALUATE_TASK", "客服服务评价"); } @Override public void run() { addRunRecordLog("---------- 客服服务评价 start ----------"); try { List<QwTalkRecordVO> clerkSendList = talkRecordMapper.getClerkSendList("30"); // 根据sendId和receiveId拼接成的字符串进行分组 Map<String, List<QwTalkRecordVO>> groupedMap = clerkSendList.stream() .collect(Collectors.groupingBy(vo -> vo.getSendId() + "-" + vo.getReceiveId())); //发送消息集合 JSONArray sendMsgList = new JSONArray(); for (Map.Entry<String, List<QwTalkRecordVO>> entry : groupedMap.entrySet()) { List<QwTalkRecordVO> list = entry.getValue(); String key = entry.getKey(); if(CollectionUtils.isEmpty(list)) continue; //校验list中是否存在已评价过的记录(isManagement=4) if(list.stream().anyMatch(vo -> vo.getIsManagement() == 4)){ continue; } //对比时间(1、客户5分钟内未发送消息,发送服务提示消息 2、发送后3分钟内客户依然未发送消息,发送评价消息) //获取服务提示消息(isManagement=3) QwTalkRecordVO qwTalkRecordVO = list.stream().filter(vo -> vo.getIsManagement() == 3).findFirst().orElse(null); if(qwTalkRecordVO==null){//未发送过服务提示消息 //获取list中企微发送时间最新的消息 qwTalkRecordVO = list.stream().max(Comparator.comparing(QwTalkRecordVO::getQwMsgTime)).orElse(null); if(qwTalkRecordVO!=null && qwTalkRecordVO.getQwMsgTime()!=null){ Date qwMsgTime = qwTalkRecordVO.getQwMsgTime(); //判断企微发送时间与当前时间是否超过指定时间,超过则加入集合 Calendar cal = Calendar.getInstance(); cal.add(Calendar.MINUTE, -5); // 减去分钟 Date threeMinutesAgo = cal.getTime(); if(qwMsgTime.compareTo(threeMinutesAgo) < 0){ JSONObject jo = new JSONObject(); jo.put("sendId", qwTalkRecordVO.getSendId()); jo.put("receiveId", qwTalkRecordVO.getReceiveId()); jo.put("serviceCode", qwTalkRecordVO.getServiceCode()); jo.put("clientId", qwTalkRecordVO.getClientId()); jo.put("phone", qwTalkRecordVO.getSendId()); jo.put("isManagement", 3); sendMsgList.add(jo); } } }else{//发送评价消息 Date qwMsgTime = qwTalkRecordVO.getQwMsgTime(); if(qwMsgTime!=null){ //判断企微发送时间与当前时间是否超过指定时间,超过则加入集合 Calendar cal = Calendar.getInstance(); cal.add(Calendar.MINUTE, -3); // 减去分钟 Date threeMinutesAgo = cal.getTime(); if(qwMsgTime.compareTo(threeMinutesAgo) < 0){ JSONObject jo = new JSONObject(); jo.put("sendId", qwTalkRecordVO.getSendId()); jo.put("receiveId", qwTalkRecordVO.getReceiveId()); jo.put("serviceCode", qwTalkRecordVO.getServiceCode()); jo.put("clientId", qwTalkRecordVO.getClientId()); jo.put("phone", qwTalkRecordVO.getSendId()); jo.put("isManagement", 4); sendMsgList.add(jo); } } } } if(CollectionUtils.isNotEmpty(sendMsgList) && sendMsgList.size()>0){ JSONObject inParam = new JSONObject(); inParam.put("sendMsgList", sendMsgList); String urlParam = serviceUrl+"/chatData/pushEvaluate"; String resultStr = httpClientUtil.httpClient(urlParam,inParam); addRunRecordLog("响应结果:" + resultStr); }else { addRunRecordLog("-------------------没有要发送的服务提示/服务评价消息-------------------------"); } } catch (Exception e) { e.printStackTrace(); addRunRecordLog("---------- 客服服务评价:" + e.getMessage()); } addRunRecordLog("---------- 客服服务评价 end "+ DateUtil.now2Str() +"----------"); } }
六、过滤器(InterceptorConfig.java)过滤下定时器和手机端调用的几个接口
.excludePathPatterns("/chatData/pushEvaluate")
.excludePathPatterns("/chatData/getEvaluateInfo")
.excludePathPatterns("/chatData/getEvaluateTemplate")
.excludePathPatterns("/chatData/saveEvaluate")

浙公网安备 33010602011771号