822 lines
35 KiB
PHP
822 lines
35 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\Users;
|
||
use app\index\model\AdminConfig;
|
||
|
||
class WechatController extends BaseController
|
||
{
|
||
/**
|
||
* 测试接口是否正常工作
|
||
*/
|
||
public function test()
|
||
{
|
||
try {
|
||
// 设置响应头
|
||
header('Content-Type: application/json; charset=utf-8');
|
||
|
||
$data = [
|
||
'time' => date('Y-m-d H:i:s'),
|
||
'status' => 'ok',
|
||
'server' => [
|
||
'REQUEST_METHOD' => $_SERVER['REQUEST_METHOD'] ?? '',
|
||
'REQUEST_URI' => $_SERVER['REQUEST_URI'] ?? '',
|
||
'HTTP_HOST' => $_SERVER['HTTP_HOST'] ?? '',
|
||
'REMOTE_ADDR' => $_SERVER['REMOTE_ADDR'] ?? '',
|
||
]
|
||
];
|
||
|
||
// 记录详细日志
|
||
Log::info('接口测试 - 请求信息:' . json_encode($data, JSON_UNESCAPED_UNICODE));
|
||
|
||
return json($data, JSON_UNESCAPED_UNICODE);
|
||
} catch (\Exception $e) {
|
||
$error = [
|
||
'error' => $e->getMessage(),
|
||
'time' => date('Y-m-d H:i:s')
|
||
];
|
||
Log::error('接口测试错误:' . json_encode($error, JSON_UNESCAPED_UNICODE));
|
||
return json($error, JSON_UNESCAPED_UNICODE);
|
||
}
|
||
}
|
||
|
||
public function index()
|
||
{
|
||
try {
|
||
// 设置响应头
|
||
header('Content-Type: text/html; charset=utf-8');
|
||
|
||
// 记录原始请求信息
|
||
$requestInfo = [
|
||
'time' => date('Y-m-d H:i:s'),
|
||
'method' => Request::method(),
|
||
'url' => Request::url(true),
|
||
'ip' => Request::ip(),
|
||
'real_ip' => Request::server('HTTP_X_REAL_IP'),
|
||
'forwarded_ip' => Request::server('HTTP_X_FORWARDED_FOR'),
|
||
'params' => Request::param(),
|
||
'headers' => getallheaders(),
|
||
'server' => [
|
||
'REQUEST_METHOD' => $_SERVER['REQUEST_METHOD'] ?? '',
|
||
'REQUEST_URI' => $_SERVER['REQUEST_URI'] ?? '',
|
||
'HTTP_HOST' => $_SERVER['HTTP_HOST'] ?? '',
|
||
'REMOTE_ADDR' => $_SERVER['REMOTE_ADDR'] ?? '',
|
||
'QUERY_STRING' => $_SERVER['QUERY_STRING'] ?? '',
|
||
'HTTP_USER_AGENT' => $_SERVER['HTTP_USER_AGENT'] ?? '',
|
||
'HTTP_REFERER' => $_SERVER['HTTP_REFERER'] ?? '',
|
||
'CONTENT_TYPE' => $_SERVER['CONTENT_TYPE'] ?? '',
|
||
'CONTENT_LENGTH' => $_SERVER['CONTENT_LENGTH'] ?? '',
|
||
'SERVER_NAME' => $_SERVER['SERVER_NAME'] ?? '',
|
||
'SERVER_ADDR' => $_SERVER['SERVER_ADDR'] ?? '',
|
||
'SERVER_PORT' => $_SERVER['SERVER_PORT'] ?? '',
|
||
'REQUEST_SCHEME' => $_SERVER['REQUEST_SCHEME'] ?? '',
|
||
],
|
||
'env' => [
|
||
'app_debug' => config('app.debug'),
|
||
'app_env' => config('app.env'),
|
||
'domain' => config('app.domain'),
|
||
]
|
||
];
|
||
|
||
Log::info('微信接口访问请求信息:' . json_encode($requestInfo, JSON_UNESCAPED_UNICODE));
|
||
|
||
// 获取原始数据
|
||
$rawData = file_get_contents("php://input");
|
||
if (!empty($rawData)) {
|
||
$rawData = mb_convert_encoding($rawData, 'UTF-8', 'UTF-8,GBK,GB2312');
|
||
Log::info('微信接口原始数据:' . $rawData);
|
||
}
|
||
|
||
// 检查请求方法
|
||
if (Request::method() == 'GET') {
|
||
Log::info('微信接口:GET请求,进行签名验证');
|
||
// 首次验证服务器地址的有效性
|
||
$this->checkSignature();
|
||
} elseif (Request::method() == 'POST') {
|
||
Log::info('微信接口:POST请求,处理消息');
|
||
// 接收消息并回复
|
||
$response = $this->receiveMessage();
|
||
// 直接输出回复的XML字符串
|
||
echo $response;
|
||
exit;
|
||
} else {
|
||
Log::error('微信接口:不支持的请求方法 - ' . Request::method());
|
||
echo '';
|
||
exit;
|
||
}
|
||
} catch (\Exception $e) {
|
||
Log::error('微信接口错误:' . $e->getMessage());
|
||
Log::error('错误堆栈:' . $e->getTraceAsString());
|
||
// 返回空字符串,避免微信服务器重试
|
||
echo '';
|
||
exit;
|
||
}
|
||
}
|
||
|
||
// 验证签名
|
||
protected function checkSignature()
|
||
{
|
||
try {
|
||
$signature = Request::get('signature');
|
||
$timestamp = Request::get('timestamp');
|
||
$nonce = Request::get('nonce');
|
||
$echostr = Request::get('echostr');
|
||
|
||
$debugInfo = [
|
||
'signature' => $signature,
|
||
'timestamp' => $timestamp,
|
||
'nonce' => $nonce,
|
||
'echostr' => $echostr,
|
||
'url' => Request::url(true),
|
||
'time' => date('Y-m-d H:i:s'),
|
||
'ip' => Request::ip(),
|
||
'headers' => getallheaders()
|
||
];
|
||
|
||
Log::info('微信验证参数:' . json_encode($debugInfo, JSON_UNESCAPED_UNICODE));
|
||
|
||
if (empty($signature) || empty($timestamp) || empty($nonce)) {
|
||
Log::error('微信验证参数缺失', $debugInfo);
|
||
exit;
|
||
}
|
||
|
||
$token = AdminConfig::where('config_name', 'wechat_token')->value('config_value');
|
||
if (empty($token)) {
|
||
Log::error('微信token未配置');
|
||
exit;
|
||
}
|
||
|
||
Log::info('微信token:' . $token);
|
||
|
||
$tmpArr = array($token, $timestamp, $nonce);
|
||
sort($tmpArr, SORT_STRING);
|
||
$tmpStr = implode($tmpArr);
|
||
$tmpStr = sha1($tmpStr);
|
||
|
||
$verifyInfo = [
|
||
'tmpStr' => $tmpStr,
|
||
'signature' => $signature,
|
||
'token' => $token,
|
||
'timestamp' => $timestamp,
|
||
'nonce' => $nonce
|
||
];
|
||
|
||
Log::info('签名验证:' . json_encode($verifyInfo, JSON_UNESCAPED_UNICODE));
|
||
|
||
if ($tmpStr == $signature) {
|
||
Log::info('微信签名验证成功');
|
||
echo $echostr;
|
||
exit;
|
||
} else {
|
||
Log::error('微信签名验证失败', $verifyInfo);
|
||
exit;
|
||
}
|
||
} catch (\Exception $e) {
|
||
Log::error('微信验证签名错误:' . $e->getMessage());
|
||
Log::error('错误堆栈:' . $e->getTraceAsString());
|
||
exit;
|
||
}
|
||
}
|
||
|
||
// 接收消息
|
||
protected function receiveMessage()
|
||
{
|
||
try {
|
||
$postStr = file_get_contents("php://input");
|
||
if (empty($postStr)) {
|
||
Log::error('微信消息:接收数据为空');
|
||
return '';
|
||
}
|
||
|
||
// 转换编码
|
||
$postStr = mb_convert_encoding($postStr, 'UTF-8', 'UTF-8,GBK,GB2312');
|
||
Log::info('微信消息原始数据:' . $postStr);
|
||
|
||
// 使用DOMDocument替代simplexml_load_string
|
||
$dom = new \DOMDocument();
|
||
$dom->loadXML($postStr, LIBXML_NOCDATA | LIBXML_NOBLANKS);
|
||
$postObj = simplexml_import_dom($dom);
|
||
|
||
if ($postObj === false) {
|
||
Log::error('微信消息:XML解析失败');
|
||
return '';
|
||
}
|
||
|
||
// 检查消息类型
|
||
$msgType = strtolower((string) $postObj->MsgType);
|
||
Log::info('微信消息类型:' . $msgType);
|
||
|
||
// 处理事件消息
|
||
if ($msgType == 'event') {
|
||
$event = strtolower((string) $postObj->Event);
|
||
Log::info('微信事件类型:' . $event);
|
||
|
||
// 处理扫码事件和关注事件
|
||
if ($event == 'scan' || $event == 'subscribe') {
|
||
try {
|
||
$scene_str = trim((string) $postObj->EventKey);
|
||
// 如果是关注事件,需要去掉前缀 'qrscene_'
|
||
if ($event == 'subscribe' && strpos($scene_str, 'qrscene_') === 0) {
|
||
$scene_str = substr($scene_str, 8);
|
||
}
|
||
|
||
$fromUsername = trim((string) $postObj->FromUserName);
|
||
$toUsername = trim((string) $postObj->ToUserName);
|
||
$ticket = (string) $postObj->Ticket;
|
||
|
||
Log::info('微信扫码/关注事件详情', [
|
||
'event' => $event,
|
||
'scene_str' => $scene_str,
|
||
'fromUsername' => $fromUsername,
|
||
'toUsername' => $toUsername,
|
||
'ticket' => $ticket,
|
||
'time' => date('Y-m-d H:i:s'),
|
||
'raw_data' => $postStr
|
||
]);
|
||
|
||
// 创建票据保存目录
|
||
$upload_dir = 'public/storage/uploads/ticket/';
|
||
if (!is_dir($upload_dir)) {
|
||
if (!mkdir($upload_dir, 0755, true)) {
|
||
Log::error('创建票据保存目录失败:' . $upload_dir);
|
||
return 'success';
|
||
}
|
||
Log::info('创建票据保存目录成功:' . $upload_dir);
|
||
}
|
||
|
||
// 保存扫码数据
|
||
$data = [
|
||
'openid' => $fromUsername,
|
||
'scene_str' => $scene_str,
|
||
'ticket' => $ticket,
|
||
'scan_time' => time(),
|
||
'event' => $event,
|
||
'raw_data' => $postStr
|
||
];
|
||
|
||
$content = json_encode($data, JSON_UNESCAPED_UNICODE);
|
||
$file = $upload_dir . $scene_str . "_" . $ticket . ".json";
|
||
|
||
if (file_put_contents($file, $content) === false) {
|
||
Log::error('保存扫码数据失败:' . $file);
|
||
return 'success';
|
||
}
|
||
Log::info('保存扫码数据成功:' . $file);
|
||
|
||
// 获取用户信息
|
||
try {
|
||
$accessToken = $this->getGZHAccessToken();
|
||
Log::info('获取access_token成功:' . $accessToken);
|
||
|
||
$url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token={$accessToken}&openid={$fromUsername}&lang=zh_CN";
|
||
$client = new Client(['verify' => false]);
|
||
$response = $client->get($url);
|
||
$userInfo = json_decode($response->getBody(), true);
|
||
|
||
Log::info('获取用户信息结果:' . json_encode($userInfo, JSON_UNESCAPED_UNICODE));
|
||
|
||
if (isset($userInfo['openid'])) {
|
||
// 保存用户信息到数据库
|
||
$user = Users::where('openid', $fromUsername)->find();
|
||
if (!$user) {
|
||
$user = new Users;
|
||
$user->openid = $fromUsername;
|
||
// 生成默认账号
|
||
$defaultAccount = 'wx_' . substr(md5($fromUsername), 0, 8);
|
||
$user->account = $defaultAccount;
|
||
$user->name = $defaultAccount; // 将默认账号同时设置为用户名
|
||
// 设置头像,确保有值
|
||
$user->avatar = !empty($userInfo['headimgurl']) ? $userInfo['headimgurl'] : '/static/images/avatar.png';
|
||
// 生成随机密码
|
||
$user->password = md5(uniqid() . rand(1000, 9999));
|
||
$user->create_time = time(); // 设置创建时间
|
||
$user->login_count = 1; // 首次登录,设置登录次数为1
|
||
if (!$user->save()) {
|
||
Log::error('创建用户失败:' . json_encode($user->getError(), JSON_UNESCAPED_UNICODE));
|
||
return 'success';
|
||
}
|
||
// 重新获取用户信息以确保获取到uid
|
||
$user = Users::where('openid', $fromUsername)->find();
|
||
if (!$user) {
|
||
Log::error('获取新创建的用户信息失败');
|
||
return 'success';
|
||
}
|
||
Log::info('创建新用户成功:' . json_encode($user->toArray(), JSON_UNESCAPED_UNICODE));
|
||
} else {
|
||
// 更新用户信息
|
||
$needUpdate = false;
|
||
|
||
// 只在用户名为空时设置
|
||
if (empty($user->name)) {
|
||
$user->name = $user->account;
|
||
$needUpdate = true;
|
||
}
|
||
|
||
// 只在头像为空或为默认头像时更新
|
||
if (empty($user->avatar) || $user->avatar === '/static/images/avatar.png') {
|
||
if (!empty($userInfo['headimgurl'])) {
|
||
$user->avatar = $userInfo['headimgurl'];
|
||
$needUpdate = true;
|
||
} elseif (empty($user->avatar)) {
|
||
$user->avatar = '/static/images/avatar.png';
|
||
$needUpdate = true;
|
||
}
|
||
}
|
||
|
||
// 增加登录次数
|
||
$user->login_count = $user->login_count + 1;
|
||
$needUpdate = true;
|
||
|
||
// 只在需要更新时才更新时间戳
|
||
if ($needUpdate) {
|
||
$user->update_time = time();
|
||
if (!$user->save()) {
|
||
Log::error('更新用户信息失败:' . json_encode($user->getError(), JSON_UNESCAPED_UNICODE));
|
||
return 'success';
|
||
}
|
||
Log::info('更新用户信息成功:' . json_encode($user->toArray(), JSON_UNESCAPED_UNICODE));
|
||
}
|
||
}
|
||
|
||
// 更新票据文件
|
||
$data = [
|
||
'uid' => (int)$user->uid, // 确保uid是整数
|
||
'name' => $user->name,
|
||
'avatar' => $user->avatar ?: '/static/images/avatar.png', // 确保avatar不为空
|
||
'openid' => $user->openid,
|
||
'user_account' => $user->account,
|
||
'user_password' => $user->password,
|
||
'expire_time' => time() + (7 * 24 * 3600), // 7天过期
|
||
'is_auto_login' => true,
|
||
'login_status' => 'success'
|
||
];
|
||
|
||
if (file_put_contents($file, json_encode($data, JSON_UNESCAPED_UNICODE)) === false) {
|
||
Log::error('更新票据文件失败:' . $file);
|
||
} else {
|
||
Log::info('更新票据文件成功:' . json_encode($data, JSON_UNESCAPED_UNICODE));
|
||
}
|
||
|
||
// 设置登录session
|
||
session('user_id', (int)$user->uid); // 确保session中的uid是整数
|
||
session('user_name', $user->name);
|
||
session('user_avatar', $user->avatar ?: '/static/images/avatar.png');
|
||
session('user_account', $user->account);
|
||
session('openid', $user->openid);
|
||
|
||
// 设置cookie
|
||
$expire = 7 * 24 * 3600; // 7天过期
|
||
cookie('user_account', $user->account, ['expire' => $expire]);
|
||
cookie('user_avatar', $user->avatar ?: '/static/images/avatar.png', ['expire' => $expire]);
|
||
cookie('user_name', $user->name, ['expire' => $expire]);
|
||
cookie('open_id', $user->openid, ['expire' => $expire]);
|
||
|
||
Log::info('用户登录成功:' . json_encode([
|
||
'uid' => (int)$user->uid,
|
||
'name' => $user->name,
|
||
'openid' => $user->openid,
|
||
'account' => $user->account,
|
||
'cookies' => [
|
||
'user_account' => $user->account,
|
||
'user_avatar' => $user->avatar,
|
||
'user_name' => $user->name,
|
||
'open_id' => $user->openid
|
||
]
|
||
], JSON_UNESCAPED_UNICODE));
|
||
} else {
|
||
Log::error('获取用户信息失败:' . json_encode($userInfo, JSON_UNESCAPED_UNICODE));
|
||
}
|
||
} catch (\Exception $e) {
|
||
Log::error('获取用户信息失败:' . $e->getMessage());
|
||
Log::error('错误堆栈:' . $e->getTraceAsString());
|
||
}
|
||
} catch (\Exception $e) {
|
||
Log::error('处理扫码事件失败:' . $e->getMessage());
|
||
Log::error('错误堆栈:' . $e->getTraceAsString());
|
||
}
|
||
}
|
||
}
|
||
|
||
return 'success';
|
||
} catch (\Exception $e) {
|
||
Log::error('处理微信消息错误:' . $e->getMessage());
|
||
Log::error('错误堆栈:' . $e->getTraceAsString());
|
||
return 'success';
|
||
}
|
||
}
|
||
|
||
// 发送小程序卡片消息的方法
|
||
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 发送请求,禁用SSL验证
|
||
$client = new Client([
|
||
'verify' => false
|
||
]);
|
||
$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;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取微信登录二维码
|
||
* @return \think\response\Json
|
||
*/
|
||
public function getLoginTicket()
|
||
{
|
||
try {
|
||
Log::info('开始获取微信登录二维码');
|
||
|
||
// 获取access_token
|
||
$access_token = $this->getGZHAccessToken();
|
||
Log::info('获取access_token成功:' . $access_token);
|
||
|
||
// 构建请求URL - 使用正确的接口生成临时二维码
|
||
$url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={$access_token}";
|
||
|
||
// 生成唯一场景值
|
||
$scene_str = md5(uniqid() . time());
|
||
Log::info('生成场景值:' . $scene_str);
|
||
|
||
// 准备请求数据 - 生成临时二维码,有效期5分钟
|
||
$postData = json_encode([
|
||
'expire_seconds' => 300, // 5分钟有效期
|
||
'action_name' => 'QR_STR_SCENE',
|
||
'action_info' => [
|
||
'scene' => [
|
||
'scene_str' => $scene_str
|
||
]
|
||
]
|
||
]);
|
||
|
||
// 发送请求获取ticket
|
||
$client = new Client(['verify' => false]);
|
||
$response = $client->post($url, [
|
||
'body' => $postData,
|
||
'headers' => [
|
||
'Content-Type' => 'application/json'
|
||
]
|
||
]);
|
||
|
||
$result = json_decode($response->getBody(), true);
|
||
Log::info('微信返回结果:' . json_encode($result, JSON_UNESCAPED_UNICODE));
|
||
|
||
if (isset($result['errcode']) && $result['errcode'] != 0) {
|
||
Log::error('获取二维码失败:' . $result['errmsg']);
|
||
return json(['code' => 1, 'msg' => '获取二维码失败:' . $result['errmsg']]);
|
||
}
|
||
|
||
// 使用ticket获取二维码图片URL
|
||
$ticket = urlencode($result['ticket']);
|
||
$qrcodeUrl = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket={$ticket}";
|
||
|
||
// 将场景值保存到缓存中,用于后续验证
|
||
Cache::set('wx_login_scene_' . $result['ticket'], [
|
||
'scene_str' => $scene_str,
|
||
'create_time' => time(),
|
||
'expire_time' => time() + 300
|
||
], 300);
|
||
|
||
Log::info('二维码生成成功', [
|
||
'ticket' => $result['ticket'],
|
||
'scene_str' => $scene_str,
|
||
'expire_time' => time() + 300
|
||
]);
|
||
|
||
return json([
|
||
'code' => 0,
|
||
'msg' => '获取二维码成功',
|
||
'data' => [
|
||
'ticket' => $result['ticket'],
|
||
'expire_seconds' => $result['expire_seconds'],
|
||
'url' => $qrcodeUrl,
|
||
'scene_str' => $scene_str
|
||
]
|
||
]);
|
||
|
||
} catch (\Exception $e) {
|
||
Log::error('获取微信登录二维码失败:' . $e->getMessage());
|
||
Log::error('错误堆栈:' . $e->getTraceAsString());
|
||
return json(['code' => 1, 'msg' => '获取二维码失败:' . $e->getMessage()]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 重新生成二维码
|
||
* @return \think\response\Json
|
||
*/
|
||
public function reGenerateQrcode()
|
||
{
|
||
try {
|
||
$scene_str = Request::post('scene_str');
|
||
if (empty($scene_str)) {
|
||
return json(['code' => 1, 'msg' => '参数错误']);
|
||
}
|
||
|
||
// 清除旧的缓存
|
||
Cache::delete('wx_login_scene_' . $scene_str);
|
||
|
||
// 获取access_token
|
||
$access_token = $this->getGZHAccessToken();
|
||
|
||
// 构建请求URL
|
||
$url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={$access_token}";
|
||
|
||
// 生成新的场景值
|
||
$new_scene_str = md5(uniqid() . time());
|
||
|
||
// 准备请求数据
|
||
$postData = json_encode([
|
||
'expire_seconds' => 300,
|
||
'action_name' => 'QR_STR_SCENE',
|
||
'action_info' => [
|
||
'scene' => [
|
||
'scene_str' => $new_scene_str
|
||
]
|
||
]
|
||
]);
|
||
|
||
// 发送请求获取ticket
|
||
$client = new Client(['verify' => false]);
|
||
$response = $client->post($url, [
|
||
'body' => $postData,
|
||
'headers' => [
|
||
'Content-Type' => 'application/json'
|
||
]
|
||
]);
|
||
|
||
$result = json_decode($response->getBody(), true);
|
||
|
||
if (isset($result['errcode']) && $result['errcode'] != 0) {
|
||
return json(['code' => 1, 'msg' => '获取二维码失败:' . $result['errmsg']]);
|
||
}
|
||
|
||
// 使用ticket获取二维码图片URL
|
||
$ticket = urlencode($result['ticket']);
|
||
$qrcodeUrl = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket={$ticket}";
|
||
|
||
// 将新的场景值保存到缓存中
|
||
Cache::set('wx_login_scene_' . $new_scene_str, [
|
||
'scene_str' => $new_scene_str,
|
||
'create_time' => time(),
|
||
'expire_time' => time() + 300
|
||
], 300);
|
||
|
||
return json([
|
||
'code' => 0,
|
||
'msg' => '获取二维码成功',
|
||
'data' => [
|
||
'ticket' => $result['ticket'],
|
||
'expire_seconds' => $result['expire_seconds'],
|
||
'url' => $qrcodeUrl,
|
||
'scene_str' => $new_scene_str
|
||
]
|
||
]);
|
||
|
||
} catch (\Exception $e) {
|
||
Log::error('重新生成二维码失败:' . $e->getMessage());
|
||
return json(['code' => 1, 'msg' => '重新生成二维码失败:' . $e->getMessage()]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查微信扫码登录状态
|
||
* @return \think\response\Json
|
||
*/
|
||
public function checkLoginStatus()
|
||
{
|
||
try {
|
||
$scene_str = Request::post('scene_str');
|
||
$ticket = Request::post('ticket');
|
||
|
||
if (empty($scene_str) || empty($ticket)) {
|
||
return json(['code' => 0, 'msg' => '参数错误']);
|
||
}
|
||
|
||
// 检查票据文件
|
||
$file = 'public/storage/uploads/ticket/' . $scene_str . "_" . $ticket . ".json";
|
||
|
||
if (!file_exists($file)) {
|
||
return json(['code' => 0, 'msg' => '等待扫码']);
|
||
}
|
||
|
||
$loginData = json_decode(file_get_contents($file), true);
|
||
if (!$loginData) {
|
||
return json(['code' => 0, 'msg' => '等待扫码']);
|
||
}
|
||
|
||
// 检查是否已经扫码
|
||
if (!isset($loginData['openid'])) {
|
||
return json(['code' => 0, 'msg' => '等待扫码']);
|
||
}
|
||
|
||
// 检查登录状态
|
||
if (!isset($loginData['login_status']) || $loginData['login_status'] !== 'success') {
|
||
return json(['code' => 0, 'msg' => '正在处理登录,请稍候...']);
|
||
}
|
||
|
||
// 登录成功,设置session
|
||
session('user_id', $loginData['uid']);
|
||
session('user_name', $loginData['name']);
|
||
session('user_avatar', $loginData['avatar']);
|
||
session('user_account', $loginData['user_account']);
|
||
session('openid', $loginData['openid']);
|
||
|
||
// 删除临时文件
|
||
@unlink($file);
|
||
|
||
// 返回用户信息
|
||
return json([
|
||
'code' => 1,
|
||
'msg' => '登录成功',
|
||
'data' => [
|
||
'uid' => $loginData['uid'],
|
||
'name' => $loginData['name'],
|
||
'avatar' => $loginData['avatar'],
|
||
'openid' => $loginData['openid'],
|
||
'user_account' => $loginData['user_account'],
|
||
'user_password' => $loginData['user_password'],
|
||
'expire_time' => $loginData['expire_time'],
|
||
'is_auto_login' => $loginData['is_auto_login']
|
||
]
|
||
]);
|
||
|
||
} catch (\Exception $e) {
|
||
Log::error('检查登录状态失败:' . $e->getMessage());
|
||
return json(['code' => 0, 'msg' => '系统错误']);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 根据openid获取用户信息
|
||
* @param string $openid
|
||
* @return array|null
|
||
*/
|
||
private function getUserInfoByOpenid($openid)
|
||
{
|
||
try {
|
||
// 这里需要根据你的用户表结构来实现
|
||
// 示例:从用户表中查询openid对应的用户信息
|
||
$user = Users::where('openid', $openid)->find();
|
||
if ($user) {
|
||
return $user->toArray();
|
||
}
|
||
return null;
|
||
} catch (\Exception $e) {
|
||
Log::error('获取用户信息失败:' . $e->getMessage());
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 测试微信服务器访问
|
||
*/
|
||
public function testWechat()
|
||
{
|
||
try {
|
||
// 记录所有请求信息
|
||
$requestInfo = [
|
||
'time' => date('Y-m-d H:i:s'),
|
||
'method' => Request::method(),
|
||
'url' => Request::url(true),
|
||
'ip' => Request::ip(),
|
||
'real_ip' => Request::server('HTTP_X_REAL_IP'),
|
||
'forwarded_ip' => Request::server('HTTP_X_FORWARDED_FOR'),
|
||
'params' => Request::param(),
|
||
'headers' => getallheaders(),
|
||
'server' => [
|
||
'REQUEST_METHOD' => $_SERVER['REQUEST_METHOD'] ?? '',
|
||
'REQUEST_URI' => $_SERVER['REQUEST_URI'] ?? '',
|
||
'HTTP_HOST' => $_SERVER['HTTP_HOST'] ?? '',
|
||
'REMOTE_ADDR' => $_SERVER['REMOTE_ADDR'] ?? '',
|
||
'QUERY_STRING' => $_SERVER['QUERY_STRING'] ?? '',
|
||
'HTTP_USER_AGENT' => $_SERVER['HTTP_USER_AGENT'] ?? '',
|
||
'HTTP_REFERER' => $_SERVER['HTTP_REFERER'] ?? '',
|
||
'CONTENT_TYPE' => $_SERVER['CONTENT_TYPE'] ?? '',
|
||
'CONTENT_LENGTH' => $_SERVER['CONTENT_LENGTH'] ?? '',
|
||
'SERVER_NAME' => $_SERVER['SERVER_NAME'] ?? '',
|
||
'SERVER_ADDR' => $_SERVER['SERVER_ADDR'] ?? '',
|
||
'SERVER_PORT' => $_SERVER['SERVER_PORT'] ?? '',
|
||
'REQUEST_SCHEME' => $_SERVER['REQUEST_SCHEME'] ?? '',
|
||
]
|
||
];
|
||
|
||
Log::info('微信测试接口访问:' . json_encode($requestInfo, JSON_UNESCAPED_UNICODE));
|
||
|
||
// 获取原始数据
|
||
$rawData = file_get_contents("php://input");
|
||
if (!empty($rawData)) {
|
||
$rawData = mb_convert_encoding($rawData, 'UTF-8', 'UTF-8,GBK,GB2312');
|
||
Log::info('微信测试接口原始数据:' . $rawData);
|
||
}
|
||
|
||
// 如果是GET请求,进行签名验证
|
||
if (Request::method() == 'GET') {
|
||
$signature = Request::get('signature');
|
||
$timestamp = Request::get('timestamp');
|
||
$nonce = Request::get('nonce');
|
||
$echostr = Request::get('echostr');
|
||
|
||
$verifyInfo = [
|
||
'signature' => $signature,
|
||
'timestamp' => $timestamp,
|
||
'nonce' => $nonce,
|
||
'echostr' => $echostr,
|
||
'time' => date('Y-m-d H:i:s'),
|
||
'ip' => Request::ip(),
|
||
'headers' => getallheaders()
|
||
];
|
||
|
||
Log::info('微信测试接口验证参数:' . json_encode($verifyInfo, JSON_UNESCAPED_UNICODE));
|
||
|
||
if (!empty($signature) && !empty($timestamp) && !empty($nonce)) {
|
||
$token = AdminConfig::where('config_name', 'wechat_token')->value('config_value');
|
||
if (empty($token)) {
|
||
Log::error('微信token未配置');
|
||
return 'token not configured';
|
||
}
|
||
|
||
$tmpArr = array($token, $timestamp, $nonce);
|
||
sort($tmpArr, SORT_STRING);
|
||
$tmpStr = implode($tmpArr);
|
||
$tmpStr = sha1($tmpStr);
|
||
|
||
if ($tmpStr == $signature) {
|
||
Log::info('微信测试接口验证成功');
|
||
return $echostr;
|
||
} else {
|
||
Log::error('微信测试接口验证失败');
|
||
return 'signature verification failed';
|
||
}
|
||
}
|
||
}
|
||
|
||
return 'success';
|
||
} catch (\Exception $e) {
|
||
Log::error('微信测试接口错误:' . $e->getMessage());
|
||
Log::error('错误堆栈:' . $e->getTraceAsString());
|
||
return 'error';
|
||
}
|
||
}
|
||
} |