微信公號快速開發(fā)(五)修復(fù)消息發(fā)送并整合JPA

該文主要因?yàn)橛芯W(wǎng)友提到,回復(fù)微信消息時(shí)總是提示服務(wù)故障。我自己也試了我的代碼,確實(shí)出了問題,推測該原因是微信對消息文本的格式做了更新導(dǎo)致的,后面驗(yàn)證確實(shí)如此。

問題修復(fù)

回復(fù)消息失敗原因

返回xml文本格式不正確,在回復(fù)的field中,若要求有CData的形式,必須要以相應(yīng)格式輸出,且不能轉(zhuǎn)義。

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();
    }
}

在輸出的實(shí)體類中添加該類型適配器注解

注意,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;

}

新增轉(zhuǎn)義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");
        // 不轉(zhuǎn)義,根據(jù)需要添加
        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,后面只要將輸出的實(shí)體類,轉(zhuǎn)為xml輸出即可

其實(shí)在回復(fù)消息時(shí),比如回復(fù)關(guān)鍵詞,從數(shù)據(jù)庫引入是比較合理的,便于更新維護(hù),因此,以下借助這個(gè)需求,進(jìn)行整合JPA的示例

整合JPA

準(zhǔn)備

引入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>

新建數(shù)據(jù)庫-服務(wù)微信公眾號

  • 建庫
CREATE DATABASE IF NOT EXISTS flow_gzh DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;
  • 授權(quán)
grant all privileges on *.* to 'root'@'%'  identified by '***' with grant option;
FLUSH PRIVILEGES;

配置數(shù)據(jù)源與jpa

spring:  
  # mysql數(shù)據(jù)源配置
  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建表

創(chuàng)建關(guān)系對象的消息實(shí)體類

@Entity
@Data
@Table(name = "t_reply_message")
public class ReplyMessage {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String keyword;

    private String text;

}

創(chuàng)建編寫基于JPA的repository

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

編寫測試類,運(yùn)行時(shí)創(chuàng)建表格

@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);
    }
}

啟動成功后,即可看到數(shù)據(jù)庫表已自動建立

image

如果啟動測試失敗,需要參看自己啟動參數(shù)是否正確

image

重構(gòu)回復(fù)消息方法

以下以被動回復(fù)文本消息為例:

Repository中新增關(guān)鍵詞查詢的方法

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

如果Repository不能滿足需求,建議使用@Query等注解自定義sql查詢

處理被動回復(fù)文本消息方法

// 文本消息回復(fù)
private String handleTextMsg(String toUser, String fromUser, Long createTime, String rcvContent)
    throws JAXBException {
    // 關(guān)鍵字回復(fù),可使用properties或數(shù)據(jù)庫
    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("公眾號接收消息:[{}}, 回復(fù)消息:[{}]",rcvMsg,replyMsg);
    return replyMsg;
}
  • 注:如果返回null, 則默認(rèn)公眾號不返回消息

測試通過:

image

詳細(xì)過程,可參考源代碼:https://github.com/chetwhy/cloud-flow

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容