Springboot实现《微信公众号自动提取资源系统》(三)【完结】接入数据库,查询数据库的资源、使用客服消息

微信公众号自动提取资源

案例目录
《微信公众号自动提取资源系统》
1、准备工作、项目搭建、服务器校验
2、接收公众号消息、指定回复内容、配置公众号自动回复
3、接入数据库,查询数据库的资源、使用客服消息

项目技术栈介绍
后端:Springboot+mybatisPlus
jdk:1.8以上
数据库:mysql

源码获取地址(以上前后端代码已经全部打包好了)

https://gitee.com/xuxiaofei1996/case-source-code.git

为了方便大家更好的学习,本平台经常分享一些完整的单个功能案例代码给大家去练习,如果本平台没有你要学习的功能案例,你可以联系小编,提供你的小需求给我,我安排我们这边的开发团队免费帮你完成你的案例。

接入数据库,查询数据库的资源

创建库和表

创建wx_remind_data数据库,然后创建resource表

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for resource
-- ----------------------------
DROP TABLE IF EXISTS `resource`;
CREATE TABLE `resource`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `code` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '资源提取码',
  `description` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '资源描述',
  `url` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '资源地址',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;


表结构如下




项目配置编写

引入mysql、mybatisPlus的依赖

pom.xml

<!--mybatisPlus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>


        <!--mysql连接-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

修改配置

application.properties

# DB Configuration:
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://xx.xx.xx.xx:3306/wx_remind_data?serverTimezone=GMT%2B8
useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=xxx

# MybatisPlus
#指定mapper中xml文件的位置
mybatis-plus.mapper-locations  = classpath*:mapper/xml/*.xml
#mybatis中pojo的位置
mybatis-plus.type-aliases-package  = com.demo.model
#mybatis的主键策略
mybatis-plus.global-config.db-config.id-type =auto

#打印Sql
mybatis-plus.configuration.log-impl = org.apache.ibatis.logging.stdout.StdOutImpl

项目接口编写

ResourceController.java

@RestController
@Api(tags = {""})
@RequestMapping("/resource")
public class ResourceController {

    @Autowired
    private ResourceService resourceService;



    @RequestMapping(value = "/queryOne", method = RequestMethod.POST)
    @ApiOperation("获取资源")
    public ResultData<ResourceVO> queryOne(@RequestBody ResourceQueryDTO dto) {
        ResultData<ResourceVO> resultData = resourceService.queryOne(dto);
        return resultData;
    }


    @RequestMapping(value = "/add", method = RequestMethod.POST)
    @ApiOperation("新增")
    public ResultData<String> add(@RequestBody @Valid ResourceInsertDTO dto, BindingResult bindingResult) throws IOException {
        if(bindingResult.hasErrors()){
            return ResultData.error(bindingResult.getFieldError().getDefaultMessage());
        }
        return resourceService.add(dto);
    }

}

ResourceService.java

public interface ResourceService {


	// 获取列表
	ResultData<ResourceVO> queryOne(ResourceQueryDTO dto);

	// 新增
	ResultData<String> add(ResourceInsertDTO dto);


}

ResourceServiceImpl.java

@Service
public class ResourceServiceImpl extends ServiceImpl<ResourceMapper, Resource> implements ResourceService {


    @Override
    public ResultData<ResourceVO> queryOne(ResourceQueryDTO dto) {
        Resource resource = BeanUtils.copyProperties(dto, Resource.class);
        QueryWrapper<Resource> queryWrapper = new QueryWrapper<>(resource);
        Resource resultData = this.getOne(queryWrapper);
        return ResultData.success("查询成功",BeanUtils.copyProperties(resultData, ResourceVO.class));
    }


    @Override
    public ResultData<String> add(ResourceInsertDTO dto) {
        Boolean result = this.save(BeanUtils.copyProperties(dto,Resource.class));
        if (!result) {
            return ResultData.error("添加失败!");
        }
        return ResultData.success("添加成功!");
    }

}

接口测试

测试添加



测试查询

将查询加入代码中

根据用户发送的代码查询数据库中的数据进行回复

WxMessageController.java

    @RequestMapping(value="/urlR",method= RequestMethod.POST)
    public void index(@RequestBody String xml,HttpServletResponse resp) throws IOException  {
        //将接收到的xml消息转为map
        Map<String, Object> stringObjectMap = XmlUtil.xmlToMap(xml);
        //将map转为标准的message对象
        Message message = BeanUtil.fillBeanWithMap(stringObjectMap, new Message(), false);
        log.info("接收到微信发来的消息:{}",message);

        //返回的内容
        String resStr = "";

        if("event".equals(message.getMsgType())){
            resStr = "Hello!欢迎关注我。\n我是您的网盘资源获取小助手\n" +
                    "通过查看公众号往期文章来获取资源的提取码\n" +
                    "公众号置顶,避免错过重要消息哦!";
        }
        if(!StringUtils.isEmpty(message.getContent())){
            ResourceQueryDTO resourceQueryDTO = new ResourceQueryDTO();
            resourceQueryDTO.setCode(message.getContent());
            ResultData<ResourceVO> resultData = resourceService.queryOne(resourceQueryDTO);
            if(resultData.isFail()){
                resStr = "查询失败!";
            }else {
                if(StringUtils.isEmpty(resultData.getData().getUrl())){
                    resStr = "未查询到对应提取码的资源!";
                }else {
                    resStr = resultData.getData().getDescription()+"的百度网盘链接:"+resultData.getData().getUrl();
                }
            }
        }

        //构建返回的map
        Map<Object, Object> resMap = new HashMap<>();
        resMap.put("ToUserName",message.getFromUserName());
        resMap.put("FromUserName",message.getToUserName());
        resMap.put("CreateTime",System.currentTimeMillis());
        resMap.put("MsgType","text");
        resMap.put("Content",resStr);

        //因处理消息的时间可能在5秒之外,所以这里回复空串,使用客服消息接口回复消息即可
        resp.setContentType("text/xml;charset=UTF-8");
        resp.setCharacterEncoding("UTF-8");
        resp.getWriter().write(XmlUtil.mapToXmlStr(resMap,"xml"));

    }

最终测试

测试结果

客服消息

客服消息介绍

上面的功能已经基本实现,但是根据微信官方文档的要求,如果使用上文使用的被动回复的消息,在用户关注或发送消息时,只能回复一条消息。并且如果5秒内收不到回复,就会发起重试。所以,如果我们希望可以回复多条信息或者我们要处理的逻辑在5秒内无法保证处理完毕,这时候我们就需要使用客服消息接口。


客服消息接口需要单独获得,只有经过微信认证的用户才可以使用客服消息接口。具体微信认证的步骤这里不做详细赘述。

根据微信公众号官方文档的描述,在请求发送客服消息之前,我们首先需要获取accessToken

获取accessToken




这里分别填写上面的两个内容


application.properties

# 公众号配置(必填)
wx.appId = 你的appId
wx.secret = 你的secret

获取代码编写

KefuMessage.java

@Configuration
@Slf4j
public class KefuMessage {

    @Value("${wx.appId}")
    private String appid;

    @Value("${wx.secret}")
    private String secret;


    //获取accessToken
    public ResultData<String> getAccessToken(){
        String url = "https://api.weixin.qq.com/cgi-bin/token";
        Map<String,Object> param = new HashMap<>();
        param.put("grant_type","client_credential");
        param.put("appid",appid);
        param.put("secret",secret);

        String result = HttpUtil.get(url+"?"+HttpUtil.toParams(param));
        Map<Object, Object> map = JSONObject.parseObject(result, Map.class);
        String access_token = (String)map.get("access_token");
        if(StringUtils.isEmpty(access_token)){
            log.error("获取accessToken失败:{}",result);
            return ResultData.error(result);
        }
        log.info("获取到accessToken:{}",access_token);
        return ResultData.success("",access_token);
    }


}

获取accessToken测试

调用accessToken后,发现提示errcode:40164,也就是请求不允许的意思。这时,我们需要去配置白名单。在设置了AppSecret后,就可以配置白名单了,将你花生壳下面的地址和你电脑的本机地址都填进去,然后保存即可。

再次请求接口,正常获取accessToken。

添加过滤器

因为我们需要在关注公众号或给公众号发送消息时发送客服消息,那么我们就需要时时刻刻都有accessToken。所以我们将获取accessToken的方法加在过滤器中,每一次请求都会去尝试获取accessToken。accessToken有两个小时的有效期,并且一天只能调用20次,每调用一次,都会产生一个新的accessToken,所以我们需要在还有5分钟过期时再获取新的accessToken

引入Redis的依赖和配置

application.properties

#Redis的配置
spring.redis.host=xx.xx.xx.xx
spring.redis.port=6379
spring.redis.password=xxx
spring.redis.database=0

pom.xml

        <!--springboot-redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

TokenInterceptor.java

@Component
@Slf4j
public class TokenInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private KefuMessage kefuMessage;

    private static final String key ="wx:accessToken";


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String accessToken = redisUtil.get(key);
        if(StringUtils.isEmpty(accessToken)){
            ResultData<String> resultData = kefuMessage.getAccessToken();
            if(resultData.isSuccess()){
                redisUtil.setEx(key,resultData.getData(),2, TimeUnit.HOURS);
            }
            return true;
        }
        //5分钟的秒
        Long fiveMinute = 60*5L;
        //剩余过期时间的秒
        Long expire = redisUtil.getExpire(key);
        if(expire < fiveMinute){
            log.info("accessToken的过期时间不到5分钟,已刷新");
            redisUtil.setEx(key,kefuMessage.getAccessToken().getData(),2, TimeUnit.HOURS);
        }

        return true;
    }


}

WebConfig.java

@Configuration
public class WebConfig extends WebMvcConfigurationSupport {

    @Autowired
    private TokenInterceptor tokenInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(tokenInterceptor);
    }
}

添加发送客服消息的方法

KefuMessage.java

@Configuration
@Slf4j
public class KefuMessage {


    @Autowired
    private RedisUtil redisUtil;


    public ResultData<WxResult> sendKFMessage(KFMessageSendDto kfMessageSendDto) throws IOException, URISyntaxException
    {
        if(StringUtils.isEmpty(kfMessageSendDto.getContent())){
            return ResultData.error("发送的内容不可为空!");
        }
        if(StringUtils.isEmpty(kfMessageSendDto.getFromUserName())){
            return ResultData.error("发送人不可为空!");
        }
        if(StringUtils.isEmpty(kfMessageSendDto.getToUserName())){
            return ResultData.error("收信人不可为空!");
        }

        String url = "https://api.weixin.qq.com/cgi-bin/message/custom/send";
        Map<String, Object> params = new HashMap<>();
        params.put("touser",kfMessageSendDto.getToUserName());
        params.put("msgtype",kfMessageSendDto.getMsgType());
        params.put("text", MapUtil.of("content",kfMessageSendDto.getContent()));


        Map<String, String> urlParam = new HashMap<>();
        urlParam.put("access_token",redisUtil.get("wx:accessToken"));
        String result = HttpUtil.post(url+"?"+HttpUtil.toParams(urlParam),JSON.toJSONString(params));
        WxResult wxResult = JSONObject.parseObject(result, WxResult.class);
        if(wxResult.getErrcode() != 0){
            return ResultData.error(wxResult);
        }

        return ResultData.success();
    }
}


将获取消息的接口改造成如下,在获取到关注事件后,会发送两条客服消息
WxMessageController.java

    @RequestMapping(value="/urlR",method= RequestMethod.POST)
    public void index(@RequestBody String xml,HttpServletResponse resp) throws IOException, URISyntaxException {
        //将接收到的xml消息转为map
        Map<String, Object> stringObjectMap = XmlUtil.xmlToMap(xml);
        //将map转为标准的message对象
        Message message = BeanUtil.fillBeanWithMap(stringObjectMap, new Message(), false);
        log.info("接收到微信发来的消息:{}",message);

        //返回的内容
        String resStr = "";

        if("event".equals(message.getMsgType())){
            resStr = "Hello!欢迎关注我。\n我是您的网盘资源获取小助手\n" +
                    "通过查看公众号往期文章来获取资源的提取码\n" +
                    "公众号置顶,避免错过重要消息哦!";
            KFMessageSendDto kfMessageSendDto = new KFMessageSendDto();
            kfMessageSendDto.setToUserName(message.getFromUserName());
            kfMessageSendDto.setFromUserName(message.getToUserName());
            kfMessageSendDto.setContent(resStr);
            kefuMessage.sendKFMessage(kfMessageSendDto);

            kfMessageSendDto.setContent("第二条");
            kefuMessage.sendKFMessage(kfMessageSendDto);
            return;
        }
        if(!StringUtils.isEmpty(message.getContent())){
            ResourceQueryDTO resourceQueryDTO = new ResourceQueryDTO();
            resourceQueryDTO.setCode(message.getContent());
            ResultData<ResourceVO> resultData = resourceService.queryOne(resourceQueryDTO);
            if(resultData.isFail()){
                resStr = "查询失败!";
            }else {
                if(StringUtils.isEmpty(resultData.getData().getUrl())){
                    resStr = "未查询到对应提取码的资源!";
                }else {
                    resStr = resultData.getData().getDescription()+"的百度网盘链接:"+resultData.getData().getUrl();
                }
            }
        }

        //构建返回的map
        Map<Object, Object> resMap = new HashMap<>();
        resMap.put("ToUserName",message.getFromUserName());
        resMap.put("FromUserName",message.getToUserName());
        resMap.put("CreateTime",System.currentTimeMillis());
        resMap.put("MsgType","text");
        resMap.put("Content",resStr);

        //因处理消息的时间可能在5秒之外,所以这里回复空串,使用客服消息接口回复消息即可
        resp.setContentType("text/xml;charset=UTF-8");
        resp.setCharacterEncoding("UTF-8");
        resp.getWriter().write(XmlUtil.mapToXmlStr(resMap,"xml"));

    }

关注后的效果如下图,成功实现!

源码获取地址(以上前后端代码已经全部打包好了)

https://gitee.com/xuxiaofei1996/case-source-code.git

为了方便大家更好的学习,本平台经常分享一些完整的单个功能案例代码给大家去练习,如果本平台没有你要学习的功能案例,你可以联系小编,提供你的小需求给我,我安排我们这边的开发团队免费帮你完成你的案例。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

累人猿-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值