diff --git a/app/index/controller/WechatController.php b/app/index/controller/WechatController.php index d525078..c0a7983 100644 --- a/app/index/controller/WechatController.php +++ b/app/index/controller/WechatController.php @@ -56,6 +56,8 @@ class WechatController extends BaseController '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' => [ @@ -66,6 +68,17 @@ class WechatController extends BaseController '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'), ] ]; @@ -119,10 +132,12 @@ class WechatController extends BaseController 'nonce' => $nonce, 'echostr' => $echostr, 'url' => Request::url(true), - 'time' => date('Y-m-d H:i:s') + 'time' => date('Y-m-d H:i:s'), + 'ip' => Request::ip(), + 'headers' => getallheaders() ]; - Log::info('微信验证参数:', $debugInfo); + Log::info('微信验证参数:' . json_encode($debugInfo, JSON_UNESCAPED_UNICODE)); if (empty($signature) || empty($timestamp) || empty($nonce)) { Log::error('微信验证参数缺失', $debugInfo); @@ -150,7 +165,7 @@ class WechatController extends BaseController 'nonce' => $nonce ]; - Log::info('签名验证:', $verifyInfo); + Log::info('签名验证:' . json_encode($verifyInfo, JSON_UNESCAPED_UNICODE)); if ($tmpStr == $signature) { Log::info('微信签名验证成功'); @@ -179,7 +194,7 @@ class WechatController extends BaseController // 转换编码 $postStr = mb_convert_encoding($postStr, 'UTF-8', 'UTF-8,GBK,GB2312'); - Log::info('微信消息:' . $postStr); + Log::info('微信消息原始数据:' . $postStr); libxml_disable_entity_loader(true); $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA); @@ -199,90 +214,122 @@ class WechatController extends BaseController // 处理扫码事件和关注事件 if ($event == 'scan' || $event == 'subscribe') { - $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') - ]); - - // 创建票据保存目录 - $upload_dir = 'public/storage/uploads/ticket/'; - if (!is_dir($upload_dir)) { - mkdir($upload_dir, 0755, true); - Log::info('创建票据保存目录:' . $upload_dir); - } - - // 保存扫码数据 - $data = [ - 'openid' => $fromUsername, - 'scene_str' => $scene_str, - 'ticket' => $ticket, - 'scan_time' => time(), - 'event' => $event - ]; - - $content = json_encode($data, JSON_UNESCAPED_UNICODE); - $file = $upload_dir . $scene_str . "_" . $ticket . ".json"; - file_put_contents($file, $content); - Log::info('保存扫码数据到文件:' . $file); - - // 获取用户信息 try { - $accessToken = $this->getGZHAccessToken(); - $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); + $scene_str = trim((string) $postObj->EventKey); + // 如果是关注事件,需要去掉前缀 'qrscene_' + if ($event == 'subscribe' && strpos($scene_str, 'qrscene_') === 0) { + $scene_str = substr($scene_str, 8); + } - Log::info('获取用户信息结果:' . json_encode($userInfo, JSON_UNESCAPED_UNICODE)); - - if (isset($userInfo['openid'])) { - // 保存用户信息到数据库 - $user = User::where('openid', $fromUsername)->find(); - if (!$user) { - $user = new User; - $user->openid = $fromUsername; - $user->name = $userInfo['nickname'] ?? ''; - $user->avatar = $userInfo['headimgurl'] ?? ''; - $user->save(); - Log::info('创建新用户', ['user' => $user->toArray()]); - } else { - // 更新用户信息 - $user->name = $userInfo['nickname'] ?? $user->name; - $user->avatar = $userInfo['headimgurl'] ?? $user->avatar; - $user->save(); - Log::info('更新用户信息', ['user' => $user->toArray()]); + $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); - // 更新票据文件中的用户信息 - $data['user_info'] = [ - 'uid' => $user->uid, - 'name' => $user->name, - 'avatar' => $user->avatar, - 'openid' => $user->openid - ]; - file_put_contents($file, json_encode($data, JSON_UNESCAPED_UNICODE)); - Log::info('更新票据文件中的用户信息'); + $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 = User::where('openid', $fromUsername)->find(); + if (!$user) { + $user = new User; + $user->openid = $fromUsername; + $user->name = $userInfo['nickname'] ?? ''; + $user->avatar = $userInfo['headimgurl'] ?? ''; + $user->save(); + Log::info('创建新用户成功', ['user' => $user->toArray()]); + } else { + // 更新用户信息 + $user->name = $userInfo['nickname'] ?? $user->name; + $user->avatar = $userInfo['headimgurl'] ?? $user->avatar; + $user->save(); + Log::info('更新用户信息成功', ['user' => $user->toArray()]); + } + + // 更新票据文件 + $data['user_info'] = [ + 'uid' => $user->uid, + 'name' => $user->name, + 'avatar' => $user->avatar, + 'openid' => $user->openid + ]; + $data['login_status'] = 'success'; + + if (file_put_contents($file, json_encode($data, JSON_UNESCAPED_UNICODE)) === false) { + Log::error('更新票据文件失败:' . $file); + } else { + Log::info('更新票据文件成功'); + } + + // 设置登录session + session('user_id', $user->uid); + session('user_info', $data['user_info']); + session('openid', $user->openid); + + Log::info('用户登录成功', [ + 'uid' => $user->uid, + 'name' => $user->name, + 'openid' => $user->openid + ]); + } 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->getMessage()); Log::error('错误堆栈:' . $e->getTraceAsString()); } - - return 'success'; } } @@ -535,56 +582,56 @@ class WechatController extends BaseController $scene_str = Request::post('scene_str'); $ticket = Request::post('ticket'); - Log::info('检查登录状态', [ - 'scene_str' => $scene_str, - 'ticket' => $ticket, - 'time' => date('Y-m-d H:i:s') - ]); - if (empty($scene_str) || empty($ticket)) { - Log::warning('检查登录状态参数错误', [ - 'scene_str' => $scene_str, - 'ticket' => $ticket - ]); return json(['code' => 0, 'msg' => '参数错误']); } // 检查票据文件 $file = 'public/storage/uploads/ticket/' . $scene_str . "_" . $ticket . ".json"; - Log::info('检查票据文件:' . $file); - if (file_exists($file)) { - $loginData = json_decode(file_get_contents($file), true); - Log::info('票据文件内容:' . json_encode($loginData, JSON_UNESCAPED_UNICODE)); - - if ($loginData) { - // 检查是否包含用户信息 - if (isset($loginData['user_info'])) { - Log::info('用户已扫码登录', [ - 'user_info' => $loginData['user_info'] - ]); - - // 设置登录session - session('user_id', $loginData['user_info']['uid']); - session('user_info', $loginData['user_info']); - session('openid', $loginData['user_info']['openid']); - - // 删除临时文件 - unlink($file); - - return json(['code' => 1, 'msg' => '登录成功']); - } else { - Log::info('等待获取用户信息'); - return json(['code' => 0, 'msg' => '正在获取用户信息,请稍候...']); - } - } + if (!file_exists($file)) { + return json(['code' => 0, 'msg' => '等待扫码']); } - Log::info('等待扫码'); - 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['user_info'])) { + return json(['code' => 0, 'msg' => '正在获取用户信息,请稍候...']); + } + + // 检查登录状态 + if (!isset($loginData['login_status']) || $loginData['login_status'] !== 'success') { + return json(['code' => 0, 'msg' => '正在处理登录,请稍候...']); + } + + // 登录成功,设置session + session('user_id', $loginData['user_info']['uid']); + session('user_info', $loginData['user_info']); + session('openid', $loginData['user_info']['openid']); + + // 删除临时文件 + @unlink($file); + + // 返回用户信息 + return json([ + 'code' => 1, + 'msg' => '登录成功', + 'data' => [ + 'user_info' => $loginData['user_info'] + ] + ]); + } catch (\Exception $e) { Log::error('检查登录状态失败:' . $e->getMessage()); - Log::error('错误堆栈:' . $e->getTraceAsString()); return json(['code' => 0, 'msg' => '系统错误']); } } @@ -622,6 +669,8 @@ class WechatController extends BaseController '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' => [ @@ -632,6 +681,12 @@ class WechatController extends BaseController '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'] ?? '', ] ]; @@ -651,12 +706,17 @@ class WechatController extends BaseController $nonce = Request::get('nonce'); $echostr = Request::get('echostr'); - Log::info('微信测试接口验证参数:', [ + $verifyInfo = [ 'signature' => $signature, 'timestamp' => $timestamp, 'nonce' => $nonce, - 'echostr' => $echostr - ]); + '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'); diff --git a/app/service/VisitStatsService.php b/app/service/VisitStatsService.php index 599aca4..a51f08b 100644 --- a/app/service/VisitStatsService.php +++ b/app/service/VisitStatsService.php @@ -57,12 +57,16 @@ class VisitStatsService // 执行所有命令 $result = $pipe->exec(); - // 更新数据库统计 - $this->updateDailyStats($date, [ - 'total_visits' => $result[0], - 'daily_visits' => $result[1], - 'unique_visitors' => $this->getUniqueVisitors($date) - ]); + // 使用Redis锁控制更新频率,每5分钟更新一次数据库 + $lockKey = $this->prefix.'update_lock:'.$date; + if (!$this->redis->exists($lockKey)) { + $this->redis->setex($lockKey, 300, 1); // 5分钟锁 + $this->updateDailyStats($date, [ + 'total_visits' => $result[0], + 'daily_visits' => $result[1], + 'unique_visitors' => $this->getUniqueVisitors($date) + ]); + } return [ 'total' => $result[0], @@ -91,7 +95,7 @@ class VisitStatsService try { // 获取其他统计数据 $otherStats = [ - 'total_users' => Db::name('users')->count(), // 移除 delete_time 条件 + 'total_users' => Db::name('users')->count(), 'new_users' => Db::name('users')->whereDay('create_time', $date)->count(), 'total_articles' => Db::name('articles')->where('delete_time', null)->count(), 'daily_articles' => Db::name('articles')->whereDay('create_time', $date)->count(), @@ -101,8 +105,10 @@ class VisitStatsService 'resource_downloads' => Db::name('resources')->whereDay('update_time', $date)->sum('downloads') ]; - // 记录日志,方便调试 - Log::info('统计数据:' . json_encode($otherStats, JSON_UNESCAPED_UNICODE)); + // 只在调试模式下记录日志 + if (config('app.debug')) { + Log::info('统计数据:' . json_encode($otherStats, JSON_UNESCAPED_UNICODE)); + } // 合并统计数据 $stats = array_merge($stats, $otherStats);