美文网首页微信公众号开发
微信公号快速开发(五)修复消息发送并整合JPA

微信公号快速开发(五)修复消息发送并整合JPA

作者: 永动的图灵机 | 来源:发表于2020-03-08 13:55 被阅读0次

    该文主要因为有网友提到,回复微信消息时总是提示服务故障。我自己也试了我的代码,确实出了问题,推测该原因是微信对消息文本的格式做了更新导致的,后面验证确实如此。

    问题修复

    回复消息失败原因

    返回xml文本格式不正确,在回复的field中,若要求有CData的形式,必须要以相应格式输出,且不能转义。

    image

    解决步骤

    自定义CData适配器

    public class CDataAdapter extends XmlAdapter<String, String> {
    
        @Override
        public String unmarshal(String v) throws Exception {
            return v;
        }
    
        @Override
        public String marshal(String v) throws Exception {
            return new StringBuilder("<![CDATA[").append(v).append("]]>").toString();
        }
    }
    

    在输出的实体类中添加该类型适配器注解

    注意,createTime不要求cdata

    @Data
    @XmlRootElement(name = "xml")
    @XmlAccessorType(XmlAccessType.FIELD)
    @Accessors(chain = true)
    public class TextReplyMsg{
    
        @XmlJavaTypeAdapter(CDataAdapter.class)
        @XmlElement(name = "ToUserName")
        private String toUserName;
    
        @XmlJavaTypeAdapter(CDataAdapter.class)
        @XmlElement(name = "FromUserName")
        private String fromUserName;
    
        @XmlElement(name = "CreateTime")
        private Long createTime;
    
        @XmlJavaTypeAdapter(CDataAdapter.class)
        @XmlElement(name = "MsgType")
        private final String msgType = MsgType.TEXT;
    
        @XmlJavaTypeAdapter(CDataAdapter.class)
        @XmlElement(name = "Content")
        private String content;
    
    }
    

    新增转义xml的方法

    public static String beanToXml(Object obj, java.lang.Class<?> clazz) throws JAXBException {
        String result = null;
        try {
            JAXBContext context = JAXBContext.newInstance(clazz);
            Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.setProperty(Marshaller.JAXB_ENCODING, "utf-8");
            // 不转义,根据需要添加
            marshaller.setProperty(CharacterEscapeHandler.class.getName(),
                                   (CharacterEscapeHandler)(chars, start, length, isAttVal, writer) -> writer.write(chars, start, length));
            StringWriter writer = new StringWriter();
            marshaller.marshal(obj, writer);
            result = writer.toString();
        } catch (JAXBException e) {
            e.printStackTrace();
            System.out.println("JAXBException happened.");
        }
        return result;
    }
    
    

    ok,后面只要将输出的实体类,转为xml输出即可

    其实在回复消息时,比如回复关键词,从数据库引入是比较合理的,便于更新维护,因此,以下借助这个需求,进行整合JPA的示例

    整合JPA

    准备

    引入JPA, MySql依赖

    <!--mysql-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!--jpa-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    

    新建数据库-服务微信公众号

    • 建库
    CREATE DATABASE IF NOT EXISTS flow_gzh DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;
    
    • 授权
    grant all privileges on *.* to 'root'@'%'  identified by '***' with grant option;
    FLUSH PRIVILEGES;
    

    配置数据源与jpa

    spring:  
      # mysql数据源配置
      datasource:
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://120.79.27.209:33306/flow_gzh?characterEncoding=utf8&useSSL=false
        username: root
        password: ENC(xQ/ZcWd0bfj9qaCd6qyNlA==)
      # jpa配置
      jpa:
        show-sql: true
        database: mysql
        hibernate:
          ddl-auto: update
          naming:
            physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
        properties.hibernate.dialect: org.hibernate.dialect.MySQL5Dialect
    

    利用JPA建表

    创建关系对象的消息实体类

    @Entity
    @Data
    @Table(name = "t_reply_message")
    public class ReplyMessage {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        private String keyword;
    
        private String text;
    
    }
    

    创建编写基于JPA的repository

    @Repository
    public interface ReplyMessageRepository extends JpaRepository<ReplyMessage,Long> {}
    

    编写测试类,运行时创建表格

    @SpringBootTest
    @RunWith(SpringRunner.class)
    @ActiveProfiles("dev")
    @ContextConfiguration(classes = {SpringDataJpaConfig.class, GzhApplication.class})
    public class GzhApplicationTests {
        private static final Logger logger = LoggerFactory.getLogger(GzhApplicationTests.class);
    
        @Autowired
        private ReplyMessageRepository replyMessageRepository;
    
        @Before
        public void create() {
            ReplyMessage replyMessage = new ReplyMessage();
            replyMessage.setKeyword("who");
            replyMessage.setText("王二狗");
            replyMessageRepository.save(replyMessage);
            assert replyMessage.getId() > 0 : "error";
        }
    
        @Test
        public void getData() {
            List<ReplyMessage> all = replyMessageRepository.findAll();
            assert all!=null:"table is null";
            all.forEach(System.out::println);
        }
    }
    

    启动成功后,即可看到数据库表已自动建立

    image

    如果启动测试失败,需要参看自己启动参数是否正确

    image

    重构回复消息方法

    以下以被动回复文本消息为例:

    Repository中新增关键词查询的方法

    @Repository
    public interface ReplyMessageRepository extends JpaRepository<ReplyMessage,Long> {
        ReplyMessage findByKeyword(String keyword);
    }
    

    如果Repository不能满足需求,建议使用@Query等注解自定义sql查询

    处理被动回复文本消息方法

    // 文本消息回复
    private String handleTextMsg(String toUser, String fromUser, Long createTime, String rcvContent)
        throws JAXBException {
        // 关键字回复,可使用properties或数据库
        ReplyMessage replyMessage = replyMessageRepository.findByKeyword(rcvContent);
        if (replyMessage != null && !replyMessage.getText().isEmpty()) {
            TextReplyMsg textReplyMsg = new TextReplyMsg().setToUserName(toUser).setFromUserName(fromUser)
                .setCreateTime(createTime).setContent(replyMessage.getText());
            return XmlConvertUtil.beanToXml(textReplyMsg, TextReplyMsg.class);
        }
    
        return null;
    }
    

    消息接口

    @PostMapping("/portal")
    public String handleMessage(@RequestBody RcvCommonMsg rcvCommonMsg) throws JAXBException {
        String replyMsg = messageService.handleMessage(rcvCommonMsg);
        // TODO 暂不支持直接bean注解输出
        String rcvMsg = XmlConvertUtil.beanToXml(rcvCommonMsg, RcvCommonMsg.class);
        logger.info("公众号接收消息:[{}}, 回复消息:[{}]",rcvMsg,replyMsg);
        return replyMsg;
    }
    
    • 注:如果返回null, 则默认公众号不返回消息

    测试通过:

    image

    详细过程,可参考源代码:https://github.com/chetwhy/cloud-flow

    相关文章

      网友评论

        本文标题:微信公号快速开发(五)修复消息发送并整合JPA

        本文链接:https://www.haomeiwen.com/subject/aapddhtx.html