首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 微信公眾號開發 SDK 擴展實作:天氣查詢、翻譯、聊天機器人與自定義選單

     編輯:PHP 相關知識

PHP 微信公眾號開發 SDK 擴展實作:天氣查詢、翻譯、聊天機器人與自定義選單

基於官方微信公眾平台 PHP SDK 進行二次封裝,整合天氣查詢(K780 API)、有道翻譯、圖靈機器人聊天、自定義選單管理等實用功能。採用類別化設計,支援文本、事件等多種消息類型分發,適合快速構建功能型公眾號。

一、核心架構設計

WechatCallbackapiTest
├── valid()           // 簽名驗證(接入驗證)
├── responseMsg()     // 主入口:解析 XML → 分發處理器 → 輸出回覆
├── handleText()      // 文本消息路由:關鍵詞匹配 → 具體業務
├── handleEvent()     // 事件消息:關注/取消關注/選單點擊
├── handleWeather()   // 天氣查詢(K780 API)
├── handleTranslation() // 翻譯(有道 API)
├── handleChat()      // 聊天機器人(圖靈/小九 API)
├── createMenu/getMenu/deleteMenu // 自定義選單 CRUD
└── checkSignature()  // 簽名驗證工具方法

二、完整實作代碼

<?php
define('TOKEN', 'your_wechat_token');

$wechatObj = new WechatCallbackapiTest();
$wechatObj->responseMsg();

class WechatCallbackapiTest {
    /** 接入驗證 */
    public function valid() {
        $echoStr = $_GET['echostr'] ?? '';
        if ($this->checkSignature()) {
            echo $echoStr;
            exit;
        }
    }

    /** 主處理入口 */
    public function responseMsg() {
        $postStr = $GLOBALS['HTTP_RAW_POST_DATA'] ?? file_get_contents('php://input');
        if (empty($postStr)) {
            echo 'Error: Empty post data';
            exit;
        }

        $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
        $msgType = trim($postObj->MsgType);
        $resultStr = '';

        switch ($msgType) {
            case 'text':
                $resultStr = $this->handleText($postObj);
                break;
            case 'event':
                $resultStr = $this->handleEvent($postObj);
                break;
            default:
                $resultStr = $this->handleDefault($postObj);
        }
        echo $resultStr;
    }

    /** 文本消息路由 */
    private function handleText($obj) {
        $keyword = trim($obj->Content);

        if (preg_match('/^天氣\s*(.+)/u', $keyword, $m)) {
            $contentStr = $this->handleWeather($m[1]);
        } elseif (preg_match('/^翻譯\s*(.+)/u', $keyword, $m)) {
            $contentStr = $this->handleTranslation($m[1]);
        } else {
            $contentStr = $this->handleChat($keyword);
        }
        return $this->handleStr($obj, $contentStr);
    }

    /** 天氣查詢:格式「天氣 城市名」 */
    private function handleWeather($city) {
        $city = trim($city);
        if (!$city) return '請輸入:天氣 城市名(例:天氣 北京)';

        // 1. 獲取城市 ID 映射
        $cityListJson = @file_get_contents('https://api.k780.com:88/?app=weather.city&format=json');
        $cityList = json_decode($cityListJson, true)['result'] ?? [];
        $cityId = '';
        foreach ($cityList as $item) {
            if ($item['citynm'] === $city) {
                $cityId = $item['weaid'];
                break;
            }
        }

        if (!$cityId) return "找不到城市:$city";

        // 2. 查詢天氣
        $weatherUrl = "https://api.k780.com:88/?app=weather.today&weaid=$cityId&appkey=10638&sign=3736578f099375665f9f141a6326b757&format=json";
        $data = json_decode(@file_get_contents($weatherUrl), true);
        $r = $data['result'] ?? [];

        if (!$r) return '天氣服務暫時不可用';

        return "今天是:{$r['days']},{$r['week']},{$r['citynm']}天氣:{$r['weather']}\n" .
               "溫度:{$r['temperature']},{$r['wind_direction']} {$r['wind_power']}\n" .
               "最低:{$r['temp_low']}°C,最高:{$r['temp_high']}°C";
    }

    /** 翻譯:格式「翻譯 內容」 */
    private function handleTranslation($text) {
        $text = trim($text);
        if (!$text) return '請輸入:翻譯 內容';

        $url = 'https://fanyi.youdao.com/openapi.do?' . http_build_query([
            'keyfrom'  => 'your_keyfrom',
            'key'      => 'your_key',
            'type'     => 'data',
            'doctype'  => 'json',
            'version'  => '1.1',
            'q'        => urlencode($text),
        ]);

        $data = json_decode(@file_get_contents($url), true);
        switch ($data['errorCode'] ?? -1) {
            case 0:  return $data['translation'][0] ?? '翻譯結果為空';
            case 20: return '翻譯文本過長(>200字元)';
            case 30: return '無法進行有效翻譯';
            case 40: return '不支援的語言類型';
            case 50: return '無效的 API Key';
            default: return '翻譯服務異常';
        }
    }

    /** 聊天機器人:兜底回覆 */
    private function handleChat($keyword) {
        $curlPost = ['chat' => $keyword];
        $ch = curl_init('https://www.xiaojo.com/bot/chata.php');
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => $curlPost,
            CURLOPT_TIMEOUT        => 5,
        ]);
        $data = curl_exec($ch);
        curl_close($ch);

        if ($data) return $data;

        // API 失敗時的隨機回覆
        $fallbacks = [
            '小九今天累了,明天再陪你聊天吧',
            '小九睡覺喽~~',
            '呼呼~~呼呼~~',
            '你話好多啊,不跟你聊了',
            '我去喝杯咖啡,稍後回妳',
        ];
        return $fallbacks[array_rand($fallbacks)];
    }

    /** 事件消息處理 */
    private function handleEvent($obj) {
        $content = '';
        switch ((string)$obj->Event) {
            case 'subscribe':
                $content = 'Welcome! 感謝關注本公眾號~';
                break;
            case 'unsubscribe':
                $content = '感謝您一直以來的關注,再見!';
                break;
            case 'CLICK': // 選單點擊事件
                $content = '您點擊了選單:' . (string)$obj->EventKey;
                break;
            default:
                $content = '';
        }
        return $this->handleStr($obj, $content);
    }

    /** 預設回覆 */
    private function handleDefault($obj) {
        return $this->handleStr($obj, '收到非文字消息,暫不支援處理');
    }

    /** 組裝回覆 XML */
    private function handleStr($obj, $content, $flag = 0) {
        $tpl = "<xml>
    <ToUserName><![CDATA[%s]]></ToUserName>
    <FromUserName><![CDATA[%s]]></FromUserName>
    <CreateTime>%s</CreateTime>
    <MsgType><![CDATA[text]]></MsgType>
    <Content><![CDATA[%s]]></Content>
    <FuncFlag>%d</FuncFlag>
</xml>";
        return sprintf($tpl, $obj->FromUserName, $obj->ToUserName, time(), $content, $flag);
    }

    /** 簽名驗證 */
    private function checkSignature() {
        $signature = $_GET['signature'] ?? '';
        $timestamp = $_GET['timestamp'] ?? '';
        $nonce     = $_GET['nonce'] ?? '';
        $tmpArr = [TOKEN, $timestamp, $nonce];
        sort($tmpArr, SORT_STRING);
        return sha1(implode($tmpArr)) === $signature;
    }

    /* ===== 自定義選單管理 ===== */
    public function createMenu($menuData, $accessToken) {
        $url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=$accessToken";
        return $this->httpPostJson($url, $menuData);
    }

    public function getMenu($accessToken) {
        $url = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=$accessToken";
        return $this->httpGet($url);
    }

    public function deleteMenu($accessToken) {
        $url = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=$accessToken";
        return $this->httpGet($url);
    }

    private function httpPostJson($url, $data) {
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_CUSTOMREQUEST  => 'POST',
            CURLOPT_POSTFIELDS     => is_string($data) ? $data : json_encode($data, JSON_UNESCAPED_UNICODE),
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_HTTPHEADER     => ['Content-Type: application/json'],
        ]);
        $resp = curl_exec($ch);
        curl_close($ch);
        return $resp;
    }

    private function httpGet($url) {
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
        ]);
        $resp = curl_exec($ch);
        curl_close($ch);
        return $resp;
    }
}
?>

三、自定義選單配置範例

<?php
// 選單結構(按鈕類型:click/view/miniprogram 等)
$menuData = [
    'button' => [
        [
            'type' => 'click',
            'name' => '今日天氣',
            'key'  => 'WEATHER_TODAY',
        ],
        [
            'type' => 'view',
            'name' => '官網首頁',
            'url'  => 'https://www.example.com',
        ],
        [
            'name' => '更多服務',
            'sub_button' => [
                ['type' => 'click', 'name' => '在線客服', 'key' => 'CONTACT_SUPPORT'],
                ['type' => 'click', 'name' => '關於我們', 'key' => 'ABOUT_US'],
            ],
        ],
    ],
];

// 獲取 access_token(需定時刷新快取)
$accessToken = getWechatAccessToken(); // 自行實現

$wechat = new WechatCallbackapiTest();
echo $wechat->createMenu($menuData, $accessToken);
?>

四、生產環境優化建議

  1. Access Token 管理:使用 Redis 快取 access_token(有效期 7200 秒),提前 200 秒刷新,避免併發重複請求。
  2. 異步處理:耗時操作(外部 API 呼叫)可推入隊列,先回覆「收到,處理中...」,完成後發送客服消息。
  3. 日誌與監控:記錄所有請求/回覆 XML、外部 API 耗時、錯誤堆疊;設置告警(連續失敗、回覆超時)。
  4. 安全加固:啟用 CURLOPT_SSL_VERIFYPEER,配置 CA 憑證;驗證微信回調 IP 白名單。
  5. 多客服/模板消息:擴充 sendCustomMessage()、sendTemplateMessage() 方法,支援更豐富互動。

此 SDK 擴展提供了微信公眾號開發的常用功能骨架,開發者可根據業務需求繼續擴充:素材管理、用戶標籤、數據統計、小程序關聯等,構建完整的微信運營中台。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved