微信公眾號發(fā)送關(guān)鍵字領(lǐng)取CDK

寫在前面

最近跟朋友搞了個網(wǎng)游公會,主體運作是《御龍在天》這款游戲,因為公會做了微信公眾號,所以提出這個需求:用戶在公眾號發(fā)送關(guān)鍵字,然后領(lǐng)取CDK,每個用于不可重復(fù)領(lǐng)取。多方資料查閱,發(fā)現(xiàn)這方面的資料很少,所以就獨立的自己研究開發(fā)了一下,不存在任何商業(yè)利益,純粹個人的愛好和貢獻(xiàn)。

原文地址(我的博客):http://zhuxinfeng.com/2019/08/12/wei-xin-gong-zhong-hao-fa-song-guan-jian-zi-ling-qu-cdk/#more

注冊訂閱號

前往微信公眾平臺注冊,這里就不多說了。點擊下面機票,go!

微信公眾平臺

微信公眾號開發(fā)文檔

開發(fā)前準(zhǔn)備

服務(wù)器:阿里云或者華為云都可以,配置不需要很高<br />
域名:已備案的域名,或者你的服務(wù)器沒有其它服務(wù),80端口給即將開發(fā)的程序用<br />
Nginx:這個東西沒什么好說的。
MySQL:數(shù)據(jù)庫,用來存儲CDK信息。

架構(gòu)設(shè)計

涉及技術(shù):Spring Boot、Spring Mvc、MyBatis、Druid、MySQL、Lombok、Dom4j、Xstream <br />
Spring Boot、Spring Mvc、MyBatis不需要多說,經(jīng)典的ssm架構(gòu);Druid是阿里爸爸的數(shù)據(jù)庫連接池;Lombok是用來簡化開發(fā)的框架,比如快速省略日志,無需生成Get/Set方法;Dom4j主要用來解析Xml消息;Xstream用來將消息轉(zhuǎn)換為Xml;

新建項目,創(chuàng)建數(shù)據(jù)表

初始化一個Spring Boot項目就不在這提了,不會的點擊下面機票,查看另外一篇文章;

初始化一個Spring Boot項目

創(chuàng)建數(shù)據(jù)表:

CREATE TABLE `cdk_info` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
  `cdk` varchar(50) NOT NULL DEFAULT '' COMMENT 'CDK',
  `type` varchar(50) DEFAULT '' COMMENT 'CDK類型',
  `open_id` varchar(50) DEFAULT '' COMMENT '微信的openid',
  `create_time` timestamp NULL DEFAULT NULL COMMENT '創(chuàng)建時間',
  `modify_time` timestamp NULL DEFAULT NULL COMMENT '修改時間',
  `dr` int(10) DEFAULT '1' COMMENT '刪除標(biāo)識: [1 有效; 0 刪除;]',
  PRIMARY KEY (`id`,`cdk`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COMMENT='CDK信息表';

Maven依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.3.2</version>
</dependency>

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.2</version>
</dependency>

<!-- alibaba的druid數(shù)據(jù)庫連接池 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.9</version>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.46</version>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.6</version>
    <scope>provided</scope>
</dependency>

<!-- 解析xml -->
<dependency>
    <groupId>dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>1.6</version>
</dependency>

<!-- 轉(zhuǎn)xml -->
<dependency>
    <groupId>com.thoughtworks.xstream</groupId>
    <artifactId>xstream</artifactId>
    <version>1.4</version>
</dependency>

創(chuàng)建微信消息實體類

@Getter
@Setter
@ToString
public class WeChatMessageBean implements Serializable {

    private static final long serialVersionUID = 1L;

    /** 開發(fā)者微信號 **/
    private String ToUserName;
    /** 發(fā)送方帳號(一個OpenID) **/
    private String FromUserName;
    /** 消息創(chuàng)建時間 (整型) **/
    private Long CreateTime;
    /** 消息類型,文本為text **/
    private String MsgType;
    /** 文本消息內(nèi)容 **/
    private String Content;
    /** 消息id,64位整型 **/
    private String MsgId;
}

創(chuàng)建XML與JavaBean轉(zhuǎn)換工具類

public class XmlConverterUtil implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 微信公眾號使用:XML轉(zhuǎn)換為Map
     * 使用dom4j進(jìn)行xml讀取
     *
     * @param request HTTP請求
     * @return 返回map
     * @date 2019/8/12 11:14
     */
    public static Map<String, String> xmlToMap(HttpServletRequest request) throws Exception {
        Map<String, String> map = new HashMap<>();
        SAXReader reader = new SAXReader();
        ServletInputStream inputStream = request.getInputStream();
        Document document = reader.read(inputStream);
        // 獲取跟節(jié)點
        Element rootElement = document.getRootElement();
        // 獲取根下所有節(jié)點
        List<Element> elements = rootElement.elements();
        for (Element element : elements) {
            map.put(element.getName(), element.getText());
        }
        // 關(guān)閉流
        inputStream.close();
        return map;
    }

    /**
     * 微信公眾號使用:對象轉(zhuǎn)換為XML
     *
     * @param weChatMessage 消息體
     * @return 返回字符串
     * @date 2019/8/12 11:18
     */
    public static String objectToXml(WeChatMessageBean weChatMessage){
        XStream xStream = new XStream();
        xStream.alias(Constants.MSG_TYPE,weChatMessage.getClass());
        return xStream.toXML(weChatMessage);
    }
}

開發(fā)服務(wù)器基本配置

登錄微信公眾平臺后,基本配置 -> 服務(wù)器配置 -> 修改配置

URL:配置一個api接口,用來接收服務(wù)器推送的消息,url自定義,一會項目中用的接口

例如:http://wx.dbnewyouth.com/api/v1/cdk/msg

Token:Token不是校驗用的access_token,是單獨校驗服務(wù)器的token,自定義即可,與服務(wù)器配置相同

EncodingAESKey:隨機生成即可,因為是個人開發(fā)使用,所以消息加解密是明文,這個用處不大。

消息加解密方式:選擇明文模式,個人開發(fā)完,就不去解密了。

服務(wù)器配置

接收公眾號推送的消息

主要思路:接收公眾號的消息推送 -> 解析xml消息體,識別消息類型 -> 處理text文本消息,其它消息直接返回success -> 獲取xml消息體中的content內(nèi)容,與設(shè)定的關(guān)鍵詞進(jìn)行對比,符合進(jìn)行下一步,不符合直接返回success -> 獲取FromUserName,其實就是openid,查詢數(shù)據(jù)庫中對應(yīng)類型的cdk是否領(lǐng)取過,領(lǐng)取過返回消息,沒領(lǐng)取過返回cdk消息 -> 封裝xml消息體,返回。

PS:需要注意,在返回xml消息體的時候,要將接收的消息體FromUserNameToUserName相反設(shè)置,即將接收方設(shè)置為用戶,發(fā)送方設(shè)置為開發(fā)者。

/**
  * 獲取cdk
  *
  * @param signature 簽名
  * @param timestamp 時間戳
  * @param nonce     隨機數(shù)
  * @param echostr   密文
  * @param request   微信公眾平臺的Http請求
  * @return 返回xml數(shù)據(jù)或接收成功(success)
  * @date 2019/8/12 11:26
  */
@RequestMapping("/msg")
public String msg(@Param("signature") String signature, @Param("timestamp") String timestamp,
    @Param("nonce") String nonce, @Param(value = "echostr") String echostr, HttpServletRequest request) {
// 校驗簽名和token
if (StringUtil.isNotEmpty(echostr)) {
    logger.info("正在校驗簽名等參數(shù)");
    if (WeixinUtil.checkSignature(signature, timestamp, nonce)) {
        return echostr;
    }
    return "";
} else {
    try {
        // 將request中的xml信息轉(zhuǎn)換為map
        Map<String, String> map = XmlConverterUtil.xmlToMap(request);
        // 消息類型
        String msgType = map.get(Constants.MSG_TYPE_KEY);
        // 發(fā)送方賬號openid
        String fromUserName = map.get(Constants.FROM_USER_NAME_KEY);
        // 開發(fā)者微信號
        String toUserName = map.get(Constants.TO_USER_NAME_KEY);
        // 用戶發(fā)送的消息
        String content = map.get(Constants.CONTENT_KEY);
        // 標(biāo)記數(shù)值
        String msgId = map.get(Constants.MSGID_KEY);
        logger.info("消息類型為:{} ,消息內(nèi)容為:{}", msgType, content);
        // 如果是文本消息則進(jìn)行如下處理
        if (msgType.equalsIgnoreCase(Constants.MSG_TYPE_VALUE)) {
            // 如果關(guān)鍵詞不是“公測CDK”或者“經(jīng)典CDK”,直接返回success
            if (!Constants.CDK_TYPE_GC_VALUE.equalsIgnoreCase(content) && !Constants.CDK_TYPE_JD_VALUE.equalsIgnoreCase(content)) {
                return Constants.DEFAULT_MSG;
            }
            // 通過content校驗出cdk類型,GC或者JD
            String cdkType = checkCdkType(content);
            // 獲取cdk
            String cdk = cdkInfoService.getCdk(fromUserName, cdkType);
            // 返回信息給用戶
            WeChatMessageBean weChatMessage = new WeChatMessageBean();
            weChatMessage.setContent(cdk);
            weChatMessage.setMsgType(msgType);
            weChatMessage.setToUserName(fromUserName);
            weChatMessage.setFromUserName(toUserName);
            weChatMessage.setCreateTime(System.currentTimeMillis());
            weChatMessage.setMsgId(msgId);
            // 將消息實體轉(zhuǎn)換為xml
            String xml = XmlConverterUtil.objectToXml(weChatMessage);
            logger.info("消息發(fā)送成功,時間:{}", getTimeStr());
            return xml;
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    logger.info("消息發(fā)送成功,時間:{}", getTimeStr());
    return Constants.DEFAULT_MSG;
}
}

實現(xiàn)類:

@Override
public String getCdk(String openId,String cdkType){
    // 參數(shù)校驗
    if(StringUtil.isBlank(openId)){
        return Constants.CDK_MSG_OPENID;
    }
    // 查詢是否領(lǐng)取過
    CdkInfoBean params = new CdkInfoBean();
    params.setOpenId(openId);
    params.setType(cdkType);
    CdkInfoBean bean = cdkInfoMapper.queryByOpenId(params);
    // 如果是空 則該用戶領(lǐng)取過CDK
    if(bean != null){
        return Constants.CDK_MSG_RECEIVED;
    }
    // 獲取一個新的CDK,如果拿不到,說明沒有cdk了。
    CdkInfoBean cdk = cdkInfoMapper.queryByAsc(cdkType);
    if(cdk == null){
        return Constants.CDK_MSG_NULL;
    }
    cdk.setOpenId(openId);
    // 更新為使用狀態(tài)
    cdkInfoMapper.update(cdk);
    // 返回cdk信息
    return Constants.CDK_MSG_RECEIVE + cdk.getCdk();
}

常量類:

public class Constants {

    /** 消息類型 **/
    public static final String MSG_TYPE = "xml";

    /** 微信公眾平臺配置的Token **/
    public static final String WECHAT_TOKEN = "xxxxxxxx";

    /** 發(fā)送消息的(open_id) **/
    public static final String FROM_USER_NAME_KEY ="FromUserName";
    
    /** 我方公眾號 **/
    public static final String TO_USER_NAME_KEY="ToUserName";
    
    /** 創(chuàng)建消息時間 **/
    public static final String CREATE_TIME_KEY="CreateTime";
    
    /** 創(chuàng)建消息時間 **/
    public static final String CONTENT_KEY="Content";
    
    /** 當(dāng)前消息類型 **/
    public static final String MSG_TYPE_KEY = "MsgType";
    public static final String MSG_TYPE_VALUE = "text";
    
    /** 當(dāng)前語音格式已知的有amr MP3 **/
    public static final String FORMAT_KEY = "Format";
    
    /** 消息ID **/
    public static final String MSGID_KEY = "MsgId";
    
    /** CDK的類型 **/
    public static final String CDK_TYPE_GC_KEY = "GC";
    public static final String CDK_TYPE_JD_KEY = "JD";

    /** 領(lǐng)取CDK的關(guān)鍵詞 **/
    public static final String CDK_TYPE_GC_VALUE = "公測CDK";
    public static final String CDK_TYPE_JD_VALUE = "經(jīng)典CDK";
    
    /** 重復(fù)領(lǐng)取 **/
    public static final String CDK_MSG_RECEIVED = "您已經(jīng)領(lǐng)取過CDK啦!";
    /** CDK前綴 **/
    public static final String CDK_MSG_RECEIVE = "感謝您關(guān)注Yy3191明月網(wǎng)游,CKD為:";
    /** CDK庫存不足 **/
    public static final String CDK_MSG_NULL = "很抱歉,CDK沒有啦,請去Yy3191聯(lián)系管理吧!";
    /** 領(lǐng)取出現(xiàn)異常 **/
    public static final String CDK_MSG_OPENID = "領(lǐng)取出現(xiàn)異常,請去Yy3191聯(lián)系管理吧!";

    /** 默認(rèn)回復(fù)消息,如果不是你想回復(fù)的消息,默認(rèn)回復(fù)success,防止公眾號5s重試 **/
    public static final String DEFAULT_MSG = "success";
}

實際效果測試

效果測試

源碼地址

點擊下方藍(lán)色飛機票,失效聯(lián)系我

公眾號發(fā)送關(guān)鍵字自動發(fā)放CDK源碼

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

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

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