对客户推送满意度打分评价

一、业务功能描述

  聚合聊天页面中,员工与粉丝聊天过程中,后端(定时器)需时刻判断该聊天是否快结束,或者已经结束,然后给粉丝发送即将结束或者结束的消息(链接 - 打开后是评价页面接口),然后粉丝进行评价 - 提交,后端记录评价数据。也可聚合聊天页手动发送评价链接。

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")

 

posted @ 2025-01-14 11:24  梦幻&浮云%  阅读(72)  评论(0)    收藏  举报