业务背景
随着公司业务量激增,每天线上问题的反馈量很大,线上问题支持需要大量测试人员 24 小时在群里回复支持,这件事其中就会涉及到很多痛点:
1、线上问题涉及到很多多端的问题,多端的研发团队又是异地跨团队,问题反馈、沟通时间成本巨大。
2、测试人员手头工作量也很大,没法及时响应用户问题,导致投诉。
3、线上反馈问题 70% 为操作解释性问题,如果维护好对应的知识文档进行自动回复,可以解放很多测试人力。
4、公司通讯工具虽然接入了企业微信,但是大家都还是用个人微信建工作群和问题支持群,个人微信没有对外提供相应的 api 来进行自动群管理。
为了解决以上问题,领导考虑引入微信机器人自动回复一些操作解释性问题,既可以快速响应用户反馈的问题,又可以解放一部分测试人力出来。文章源自玩技e族-https://www.playezu.com/181309.html
解决方案
把线上问题群里全部拉入机器人的微信,实现以下功能:
1、每日定时几次发送各种常见问题以及对应操作指引,以及对应的问题需要 @ 的相应的负责人。
2、支持配置关键字及自动回复消息,一旦包含关键字就触发自动回复消息。
3、针对员工内部群,定时推送提醒工时、发日报等内容,并 @ 到对应的人。文章源自玩技e族-https://www.playezu.com/181309.html
具体实现
第一步:
服务器上安装可爱猫及对外 http 的插件及对应版本微信,电脑上微信版本设置禁止自动更新,并登录机器人微信。文章源自玩技e族-https://www.playezu.com/181309.html
可爱猫及微信安装包:
文章源自玩技e族-https://www.playezu.com/181309.html可爱猫上登录机器人微信:
文章源自玩技e族-https://www.playezu.com/181309.html安装 http 对外插件:
文章源自玩技e族-https://www.playezu.com/181309.html第二步:
配置消息回调地址:
第三步:
数据库相关表设计:文章源自玩技e族-https://www.playezu.com/181309.html
微信群组表:
微信自动消息配置表:
第四步:
开发对应的后端接口:
1、机器人所有微信群相关接口 (获取微信机器人所有群组)
**
* <p>
* 微信配置表 前端控制器
* </p>
*
* @author xxxx
* @since 2021-10-15
*/
@Slf4j
@RestController
@ApiSupport(order = 19)
@Api(tags = {"微信群组配置表相关接口"})
@RequestMapping("wxGroupConfig")
public class WxGroupConfigController {
@Autowired
private WxGroupConfigService wxGroupConfigService;
@ApiOperation("分页查询群组列表")
@ApiOperationSupport(order = 2)
@PostMapping("/queryPageWxGroup")
public PageableEntity<WxGroupConfig> queryPageWxGroup(@RequestBody PageRequest<String> request) {
IPage pager = MpToolkit.toPage(request);
String groupWxName = request.getData();
QueryWrapper<WxGroupConfig> wrapper = new QueryWrapper<>();
if (StringUtils.isNotBlank(groupWxName)){
wrapper.like("group_wxname",groupWxName);
}
IPage<WxGroupConfig> result = wxGroupConfigService.page(pager,wrapper);
return MpToolkit.build(result);
}
@ApiOperation(value = "不分页查询群组列表")
@ApiOperationSupport(order = 1)
@PostMapping("/queryWxGroupList")
public ResponseEntity<List<WxGroupConfig>> queryWxGroupList() {
return StatusCode.OK.build(wxGroupConfigService.list());
}
@ApiOperation(value = "获取微信机器人所有微信群组")
@ApiOperationSupport(order = 1)
@PostMapping("/getGroupList")
public ResponseEntity<Boolean> getGroupList() {
return StatusCode.OK.build(wxGroupConfigService.getGroupList());
}
}
服务实现类:
/**
* <p>
* 微信配置表 服务实现类
* </p>
*
* @author xxxx
* @since 2021-10-15
*/
@Slf4j
@Service
public class WxGroupConfigServiceImpl extends MybatisPlusServiceImpl<WxGroupConfigMapper, WxGroupConfig> implements WxGroupConfigService {
@Autowired
private WxMsgSendUtils wxMsgSendUtils;
@Autowired
private WxGroupConfigService wxGroupConfigService;
@Override
public boolean getGroupList() {
JSONObject json = wxMsgSendUtils.getGroupList();
if (null != json){
JSONArray jsonArray = json.getJSONArray("data");
for (int i = 0;i < jsonArray.size();i++){
JSONObject jsonObject = jsonArray.getJSONObject(i);
WxGroupConfig wxGroupConfig = new WxGroupConfig();
wxGroupConfig.setGroupWxid(jsonObject.getString("wxid"));
if (StringUtils.isNotBlank(jsonObject.getString("nickname"))){
wxGroupConfig.setGroupWxname(jsonObject.getString("nickname"));
}else {
wxGroupConfig.setGroupWxname("群聊");
}
try {
wxGroupConfigService.save(wxGroupConfig);
}catch (DuplicateKeyException e){
// log.info("已经初始化过该微信群组: {},{}" ,wxGroupConfig.getGroupWxid(),wxGroupConfig.getGroupWxname());
}
}
return true;
}
return false;
}
}
2、群自动回复消息相关接口
/**
* <p>
* 群自动回复消息配置表 前端控制器
* </p>
*
* @author xxxxx
* @since 2021-10-15
*/
@Slf4j
@RestController
@ApiSupport(order = 20)
@Api(tags = {"群自动回复消息配置表相关接口"})
@RequestMapping("wxAutomsgConfig")
public class WxAutomsgConfigController extends BaseController {
@Autowired
private WxAutomsgConfigService wxautomsgconfigService;
@Resource
private WxAutomsgConfigMapper wxAutomsgConfigMapper;
/**
* 服务器回调接口,用于接收微信机器人消息
*
* 可爱猫,消息上报
* @param request
* @return
*/
@ApiOperation(value = "监听可爱猫的消息")
@ApiOperationSupport(order = 1)
@PostMapping("/callback")
public void handleMessage(HttpServletRequest request) throws Exception{
RequestVo requestVo = new RequestVo();
requestVo.setType(request.getParameter("type"));
requestVo.setFrom_wxid(request.getParameter("from_wxid"));
requestVo.setFinal_from_wxid(request.getParameter("final_from_wxid"));
requestVo.setFrom_name(request.getParameter("from_name"));
requestVo.setFinal_from_name(request.getParameter("final_from_name"));
requestVo.setRobot_wxid(request.getParameter("robot_wxid"));
requestVo.setMsg(request.getParameter("msg"));
requestVo.setTime(request.getParameter("time"));
requestVo.setRid(request.getParameter("rid"));
// log.info("收到微信消息:{}", JSON.toJSONString(requestVo));
if (requestVo.getType().equals("200") && requestVo.getMsg().contains("@")&& !requestVo.getMsg().startsWith("<?xml")){
List<WxAutomsgConfig> wxAutomsgConfigs = wxAutomsgConfigMapper.queryByGroupWxId(requestVo.getFrom_wxid());
if (CollectionUtils.isNotEmpty(wxAutomsgConfigs)){
wxautomsgconfigService.handleGroupMsg(requestVo,wxAutomsgConfigs);
}
}
}
@ApiOperation("分页查询自动消息配置信息")
@ApiOperationSupport(order = 2)
@PostMapping("/queryPageWxAutomsg")
public PageableEntity<WxAutomsgConfigView> page(@RequestBody PageRequest<WxAutomsgConfigDto> request) {
IPage pager = MpToolkit.toPage(request);
WxAutomsgConfigDto wxAutomsgConfigDto = request.getData();
if (null == wxAutomsgConfigDto) {
return MpToolkit.build(pager);
}
Map<String,Object> mapParam = new HashMap<>();
mapParam.put("page",pager);
mapParam.put("groupId",wxAutomsgConfigDto.getGroupId());
mapParam.put("keyword",wxAutomsgConfigDto.getKeyword());
pager.setRecords(wxAutomsgConfigMapper.queryPageWxAutomsg(mapParam));
return MpToolkit.build(pager);
}
@ApiOperation("新增自动消息配置信息")
@ApiOperationSupport(order = 3)
@PostMapping("/addWxAutomsg")
public ResponseEntity<Boolean> add(@RequestBody WxAutomsgConfigDto wxAutomsgConfigDto) {
// 实体校验
ComplexResult cr = FluentValidator.checkAll().failOver()
.on(wxAutomsgConfigDto, new WxAutomsgConfigDtoValidator())
.doValidate()
.result(ResultCollectors.toComplex());
// 验证失败
if (!cr.isSuccess()) {
throw new FluentException(cr.getErrors());
}
return StatusCode.OK.build(wxautomsgconfigService.addWxAutomsgConfig(wxAutomsgConfigDto));
}
@ApiOperation("更新自动消息配置信息")
@ApiOperationSupport(order = 4)
@PostMapping("/updateWxAutomsg")
public ResponseEntity<Boolean> update(@RequestBody WxAutomsgConfigDto wxAutomsgConfigDto) {
// 实体校验
ComplexResult cr = FluentValidator.checkAll().failOver()
.on(wxAutomsgConfigDto, new WxAutomsgConfigDtoValidator())
.on(wxAutomsgConfigDto.getMsgId(),new ObjectValidator("msgId"))
.doValidate()
.result(ResultCollectors.toComplex());
// 验证失败
if (!cr.isSuccess()) {
throw new FluentException(cr.getErrors());
}
return StatusCode.OK.build(wxautomsgconfigService.editWxAutomsgConfig(wxAutomsgConfigDto));
}
@ApiOperation("删除自动消息配置信息")
@ApiOperationSupport(order = 5)
@GetMapping("/removeWxAutomsg")
public ResponseEntity<Boolean> remove(Long msgId) {
// ID校验
ComplexResult cr = FluentValidator.checkAll().failOver()
.on(msgId, new ObjectValidator("msgId"))
.doValidate()
.result(ResultCollectors.toComplex());
// 验证失败
if (!cr.isSuccess()) {
throw new FluentException(cr.getErrors());
}
return StatusCode.OK.build(wxautomsgconfigService.deleteWxAutomsgConfig(msgId));
}
}
服务实现类:
/**
* <p>
* 群自动回复消息配置表 服务实现类
* </p>
*
* @author xxxx
* @since 2021-10-15
*/
@Slf4j
@Service
public class WxAutomsgConfigServiceImpl extends MybatisPlusServiceImpl<WxAutomsgConfigMapper, WxAutomsgConfig> implements WxAutomsgConfigService {
@Autowired
private WxMsgSendUtils wxMsgSendUtils;
@Override
public synchronized void handleGroupMsg(RequestVo requestVo, List<WxAutomsgConfig> wxAutomsgConfigs) {
for (WxAutomsgConfig wxAutomsgConfig : wxAutomsgConfigs){
if (StringUtils.isNotBlank(wxAutomsgConfig.getAutoMsg())){
if (requestVo.getMsg().contains(wxAutomsgConfig.getKeyword())){
// log.info("自动回复消息");
wxMsgSendUtils.sendTextMsg(wxAutomsgConfig.getAutoMsg(),requestVo);
break;
}
}
}
}
@Override
public boolean addWxAutomsgConfig(WxAutomsgConfigDto wxAutomsgConfigDto) {
WxAutomsgConfig wxAutomsgConfig = WxAutomsgConfigDxo.INSTANCE.toEntity(wxAutomsgConfigDto);
UserContextUtils.addModel(wxAutomsgConfig);
wxAutomsgConfig.setMsgId(null);
try {
return this.save(wxAutomsgConfig);
} catch (DuplicateKeyException e) {
throw new BusinessException("微信群ID不能重复",e);
}
}
@Override
public boolean editWxAutomsgConfig(WxAutomsgConfigDto wxAutomsgConfigDto) {
WxAutomsgConfig dbWxAutomsgConfig = this.getById(wxAutomsgConfigDto.getMsgId());
if (null == dbWxAutomsgConfig) {
throw new BusinessException("要修改的自动消息配置不存在");
}
WxAutomsgConfig wxAutomsgConfig = WxAutomsgConfigDxo.INSTANCE.toEntity(wxAutomsgConfigDto);
//防止修改主键id值
wxAutomsgConfig.setMsgId(dbWxAutomsgConfig.getMsgId());
UserContextUtils.editModel(wxAutomsgConfig);
try {
return this.updateById(wxAutomsgConfig);
} catch (DuplicateKeyException e) {
throw new BusinessException("微信群ID不能重复", e);
}
}
@Override
public boolean deleteWxAutomsgConfig(Long msgId) {
QueryWrapper<WxAutomsgConfig> wrapper = new QueryWrapper<>();
wrapper.eq("is_delete", Constants.IS_DELETE_NO);
wrapper.eq("msg_id", msgId);
WxAutomsgConfig wxAutomsgConfig = this.getOne(wrapper);
if (null == wxAutomsgConfig) {
throw new BusinessException("要删除的数据不存在");
}
UserContextUtils.deleteModel(wxAutomsgConfig);
return this.updateById(wxAutomsgConfig);
}
}
第五步:
前端相关功能页面:
微信群组:
自动回复消息配置:
总结
虽然看起来一个很小的工具,却可以实在的提升一些效能,从中也提升了个人的解决问题的能力,另外代码自我感觉写得很烂,不大会发帖子,各位大佬轻喷。
未知地区 1F
钉钉机器人是不是可以试试,我记得群聊设置页面有 webhook学习一下!