203 lines
6.8 KiB
PHP
203 lines
6.8 KiB
PHP
<?php
|
||
namespace app\index\controller;
|
||
|
||
use think\facade\Request;
|
||
use think\facade\Log;
|
||
use think\facade\Cache;
|
||
use GuzzleHttp\Client;
|
||
|
||
use app\index\model\AdminConfig;
|
||
|
||
class WechatController extends BaseController
|
||
{
|
||
public function index()
|
||
{
|
||
// 检查请求方法
|
||
if (Request::method() == 'GET') {
|
||
// 首次验证服务器地址的有效性
|
||
$this->checkSignature();
|
||
} elseif (Request::method() == 'POST') {
|
||
// 接收消息并回复
|
||
$response = $this->receiveMessage();
|
||
// 直接输出回复的XML字符串
|
||
echo $response;
|
||
exit;
|
||
}
|
||
}
|
||
|
||
// 验证签名
|
||
protected function checkSignature()
|
||
{
|
||
$signature = Request::get('signature');
|
||
$timestamp = Request::get('timestamp');
|
||
$nonce = Request::get('nonce');
|
||
$echostr = Request::get('echostr');
|
||
$token = AdminConfig::where('config_name', 'wechat_token')->value('config_value');
|
||
$tmpArr = array($token, $timestamp, $nonce);
|
||
sort($tmpArr, SORT_STRING);
|
||
$tmpStr = implode($tmpArr);
|
||
$tmpStr = sha1($tmpStr);
|
||
|
||
if ($tmpStr == $signature) {
|
||
echo $echostr;
|
||
exit;
|
||
}
|
||
}
|
||
|
||
// 接收消息
|
||
protected function receiveMessage()
|
||
{
|
||
$postStr = file_get_contents("php://input");
|
||
if (empty($postStr)) {
|
||
return '';
|
||
}
|
||
|
||
libxml_disable_entity_loader(true);
|
||
$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
|
||
Log::write($postObj);
|
||
// 检查消息类型
|
||
$msgType = strtolower((string) $postObj->MsgType);
|
||
if ($msgType == 'text') {
|
||
// 提取消息内容
|
||
$fromUsername = (string) $postObj->FromUserName; // 微信用户openid
|
||
$toUsername = (string) $postObj->ToUserName; // 公众号id
|
||
$keyword = trim((string) $postObj->Content);
|
||
|
||
// 构造自动回复的文本消息
|
||
$time = time();
|
||
$textTpl = "<xml>
|
||
<ToUserName><![CDATA[%s]]></ToUserName>
|
||
<FromUserName><![CDATA[%s]]></FromUserName>
|
||
<CreateTime>%s</CreateTime>
|
||
<MsgType><![CDATA[text]]></MsgType>
|
||
<Content><![CDATA[%s]]></Content>
|
||
<FuncFlag>0</FuncFlag>
|
||
</xml>";
|
||
|
||
|
||
$responseContent = "消息标题";
|
||
$resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $responseContent);
|
||
// 假设已经确定要发送小程序卡片消息
|
||
$this->sendMiniProgramCard((string) $postObj->FromUserName);
|
||
|
||
return $resultStr;
|
||
}
|
||
// 如果需要处理其他类型的消息,可以在这里添加相应的逻辑
|
||
|
||
// 如果不是文本消息,可以返回一个空字符串或错误消息
|
||
return '';
|
||
}
|
||
|
||
// 发送小程序卡片消息的方法
|
||
private function sendMiniProgramCard($openid)
|
||
{
|
||
|
||
$accessToken = $this->getGZHAccessToken();
|
||
print_r($accessToken);
|
||
if (!$accessToken) {
|
||
// 处理获取access_token失败的情况
|
||
return;
|
||
}
|
||
$postData = json_encode([
|
||
'touser' => $openid, // 接收者(用户)的openid
|
||
'msgtype' => 'miniprogrampage',
|
||
'miniprogrampage' => [
|
||
'title' => '小程序标题',
|
||
'appid' => '小程序appid',
|
||
'pagepath' => '小程序路径',
|
||
'thumb_media_id' => '你的thumb_media_id'
|
||
]
|
||
], JSON_UNESCAPED_UNICODE);
|
||
$url = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token={$accessToken}";
|
||
$result = json_decode(file_get_contents($url, false, stream_context_create([
|
||
'http' => [
|
||
'method' => 'POST',
|
||
'header' => "Content-type: application/json\r\n",
|
||
'content' => $postData,
|
||
'timeout' => 60 // 超时时间(单位:s)
|
||
]
|
||
])), true);
|
||
// 处理返回结果
|
||
if ($result['errcode'] == 0) {
|
||
// 发送成功
|
||
} else {
|
||
// 发送失败
|
||
}
|
||
}
|
||
|
||
// 获取公众号Access token
|
||
public function getGZHAccessToken()
|
||
{
|
||
// 从数据库获取配置
|
||
$appid = AdminConfig::where('config_name', 'wechat_appid')->value('config_value');
|
||
$secret = AdminConfig::where('config_name', 'wechat_appsecret')->value('config_value');
|
||
|
||
if (empty($appid) || empty($secret)) {
|
||
throw new \Exception('微信配置信息未设置');
|
||
}
|
||
|
||
// 构建请求URL
|
||
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$appid}&secret={$secret}";
|
||
|
||
try {
|
||
// 使用 GuzzleHttp 发送请求
|
||
$client = new Client();
|
||
$response = $client->get($url);
|
||
$data = json_decode($response->getBody(), true);
|
||
$response = $client->get($url);
|
||
$data = json_decode($response->getBody(), true);
|
||
|
||
if (!isset($data['access_token'])) {
|
||
throw new \Exception("获取access_token失败: {$data['errmsg']}", $data['errcode'] ?? -1);
|
||
}
|
||
|
||
return $data['access_token'];
|
||
} catch (\Exception $e) {
|
||
Log::error('获取access_token失败:' . $e->getMessage());
|
||
throw $e;
|
||
}
|
||
}
|
||
|
||
//获取登录二维码的 ticket
|
||
public function getLoginTicket()
|
||
{
|
||
// 获取access_token
|
||
$access_token = $this->getGZHAccessToken();
|
||
|
||
// 构建请求URL
|
||
$url = "https://api.weixin.qq.com/cgi-bin/media/upload?access_token={$access_token}&type=image";
|
||
|
||
// 准备二维码图片文件
|
||
$file = request()->file('qrcode');
|
||
if (!$file) {
|
||
return json(['code' => 1, 'msg' => '请上传二维码图片']);
|
||
}
|
||
|
||
// 将文件转换为CURLFile对象
|
||
$file_path = $file->getPathname();
|
||
$file_name = $file->getOriginalName();
|
||
$file_type = $file->getMime();
|
||
|
||
$data = [
|
||
'media' => new \CURLFile($file_path, $file_type, $file_name)
|
||
];
|
||
|
||
// 发送请求获取ticket
|
||
$result = $this->curlPost($url, $data);
|
||
$response = json_decode($result, true);
|
||
|
||
if (isset($response['errcode']) && $response['errcode'] != 0) {
|
||
return json(['code' => 1, 'msg' => '获取ticket失败:' . $response['errmsg']]);
|
||
}
|
||
|
||
return json([
|
||
'code' => 0,
|
||
'msg' => '获取ticket成功',
|
||
'data' => [
|
||
'media_id' => $response['media_id'],
|
||
'created_at' => $response['created_at'],
|
||
'type' => $response['type']
|
||
]
|
||
]);
|
||
}
|
||
} |