getMiniProgramOpenid($code); } else { // 公众号获取openid $openid = $this->getOfficialOpenid($code); } return [ 'openid' => $openid, 'type' => $type ]; } catch (\Exception $e) { throw new ApiException('获取openid失败:' . $e->getMessage()); } } /** * 绑定微信openid到人员 * @param array $data * @return array */ public function bindWechatOpenid(array $data) { $personnel_id = $this->request->memberId(); // 从token中获取当前登录的人员ID if (empty($personnel_id)) { throw new ApiException('用户未登录'); } // 查找人员记录 $personnel = SchoolPersonnel::where('id', $personnel_id)->find(); if (!$personnel) { throw new ApiException('人员信息不存在'); } $update_data = []; // 根据类型绑定不同的openid if ($data['type'] === 'miniprogram' && !empty($data['miniprogram_openid'])) { // 检查小程序openid是否已被其他用户绑定 $exists = SchoolPersonnel::where('wxminiopenid', $data['miniprogram_openid']) ->where('id', '<>', $personnel_id) ->find(); if ($exists) { throw new ApiException('该微信小程序已绑定其他账号'); } $update_data['wxminiopenid'] = $data['miniprogram_openid']; } elseif ($data['type'] === 'official' && !empty($data['official_openid'])) { // 检查公众号openid是否已被其他用户绑定 $exists = SchoolPersonnel::where('wxgzhopenid', $data['official_openid']) ->where('id', '<>', $personnel_id) ->find(); if ($exists) { throw new ApiException('该微信公众号已绑定其他账号'); } $update_data['wxgzhopenid'] = $data['official_openid']; } elseif ($data['type'] === 'both') { // 同时绑定两个openid if (!empty($data['miniprogram_openid'])) { $exists = SchoolPersonnel::where('wxminiopenid', $data['miniprogram_openid']) ->where('id', '<>', $personnel_id) ->find(); if ($exists) { throw new ApiException('该微信小程序已绑定其他账号'); } $update_data['wxminiopenid'] = $data['miniprogram_openid']; } if (!empty($data['official_openid'])) { $exists = SchoolPersonnel::where('wxgzhopenid', $data['official_openid']) ->where('id', '<>', $personnel_id) ->find(); if ($exists) { throw new ApiException('该微信公众号已绑定其他账号'); } $update_data['wxgzhopenid'] = $data['official_openid']; } } if (empty($update_data)) { throw new ApiException('没有可绑定的openid'); } // 更新人员微信信息 $personnel->save($update_data); return [ 'personnel_id' => $personnel_id, 'bind_data' => $update_data, 'message' => '绑定成功' ]; } /** * 微信公众号授权跳转 * @param string $redirect_uri 回调地址 * @param string $state 状态参数 * @return Response */ public function wechatAuthorize(string $redirect_uri, string $state) { if (empty($redirect_uri)) { throw new ApiException('回调地址不能为空'); } $state_data = json_decode($state, true); try { // 使用现有的微信授权服务 $core_wechat_service = new CoreWechatServeService(); $callback_url = $core_wechat_service->authorization($redirect_uri . '?state=' . urlencode($state).'&personnel_id='.$state_data['personnel_id'].'&from='.$state_data['from'], 'snsapi_userinfo'); // 直接跳转到微信授权页面 return redirect($callback_url); } catch (\Exception $e) { throw new ApiException('生成授权链接失败:' . $e->getMessage()); } } /** * 微信授权回调处理 * @param string $code 授权回调code * @param string $state 状态参数 * @return Response */ public function wechatCallback(string $code, string $state, string $personnel_id, string $from) { try { if (empty($code)) { return $this->buildCallbackPage('wechat_bind_cancel', '用户取消授权'); } // 解析state参数 $state_data = json_decode(urldecode($state), true); if (!$personnel_id) { return $this->buildCallbackPage('wechat_bind_error', '参数错误'); } // 获取公众号openid $official_openid = $this->getOfficialOpenid($code); // 绑定公众号openid $bind_result = $this->bindOfficialOpenid($personnel_id, $official_openid); if ($bind_result) { // 根据来源返回不同的页面 if ($from == 'h5') { // H5环境,返回简单的成功页面 return $this->buildH5SuccessPage('微信绑定成功!'); } else { // 小程序webview环境 return $this->buildCallbackPage('wechat_bind_success', '微信公众号绑定成功'); } } else { return $this->buildCallbackPage('wechat_bind_error', '绑定失败'); } } catch (\Exception $e) { return $this->buildCallbackPage('wechat_bind_error', '绑定失败:' . $e->getMessage()); } } /** * 获取绑定状态 * @return array */ public function getBindStatus() { $personnel_id = $this->request->memberId(); if (empty($personnel_id)) { throw new ApiException('用户未登录'); } $personnel = SchoolPersonnel::where('id', $personnel_id)->find(); if (!$personnel) { throw new ApiException('人员信息不存在'); } return [ 'personnel_id' => $personnel_id, 'miniprogram_bound' => !empty($personnel->wxminiopenid), 'official_bound' => !empty($personnel->wxgzhopenid), 'miniprogram_openid' => $personnel->wxminiopenid ?? '', 'official_openid' => $personnel->wxgzhopenid ?? '' ]; } /** * 获取小程序openid * @param string $code * @return string */ private function getMiniProgramOpenid(string $code) { // 获取小程序配置 $weapp_config = $this->getWeappConfig(); // 构建请求URL $url = "https://api.weixin.qq.com/sns/jscode2session?appid=" . $weapp_config['app_id'] . "&secret=" . $weapp_config['secret'] . "&js_code=" . $code . "&grant_type=authorization_code"; // 使用cURL发起HTTP请求,更稳定 $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 10); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); $response = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($response === false) { throw new ApiException('获取小程序openid失败:网络请求失败'); } if ($http_code !== 200) { throw new ApiException('获取小程序openid失败:HTTP状态码' . $http_code); } $result = json_decode($response, true); if (json_last_error() !== JSON_ERROR_NONE) { throw new ApiException('获取小程序openid失败:响应数据格式错误'); } if (isset($result['errcode']) && $result['errcode'] != 0) { throw new ApiException('获取小程序openid失败:' . ($result['errmsg'] ?? '未知错误') . '(错误码:' . $result['errcode'] . ')'); } if (empty($result['openid'])) { throw new ApiException('获取小程序openid失败:返回数据中无openid'); } return $result['openid']; } /** * 获取公众号openid * @param string $code * @return string */ private function getOfficialOpenid(string $code) { // 使用现有的微信服务获取用户信息 $core_wechat_service = new CoreWechatServeService(); $user = $core_wechat_service->userFromCode($code); if (!$user) { throw new ApiException('获取公众号openid失败'); } return $user->getId(); // openid } /** * 绑定公众号openid * @param int $personnel_id * @param string $official_openid * @return bool */ private function bindOfficialOpenid(int $personnel_id, string $official_openid) { // 检查公众号openid是否已被其他用户绑定 $exists = SchoolPersonnel::where('wxgzhopenid', $official_openid) ->where('id', '<>', $personnel_id) ->find(); if ($exists) { throw new ApiException('该微信公众号已绑定其他账号'); } // 更新人员微信公众号openid $personnel = SchoolPersonnel::where('id', $personnel_id)->find(); if (!$personnel) { throw new ApiException('人员信息不存在'); } $personnel->wxgzhopenid = $official_openid; return $personnel->save(); } /** * 构建回调页面 * @param string $type * @param string $message * @return Response */ private function buildCallbackPage(string $type, string $message) { $html = ' 微信绑定结果

' . $message . '

页面将在3秒后自动关闭

'; return Response::create($html, 'html'); } /** * 构建H5成功页面(用于微信浏览器直接访问) * @param string $message * @return Response */ private function buildH5SuccessPage(string $message) { $html = ' 微信绑定成功

' . $message . '

您已成功绑定微信公众号,现在可以享受更多便捷功能。

'; return Response::create($html, 'html'); } /** * 获取微信公众号配置 * @return array */ private function getWechatConfig() { $core_wechat_config = new CoreWechatConfigService(); return $core_wechat_config->getWechatConfig(); } /** * 获取小程序配置 * @return array */ private function getWeappConfig() { // 直接查询school_sys_config表(使用table避免重复前缀) $config = Db::table('school_sys_config') ->where('config_key', 'weapp') ->value('value'); if (empty($config)) { throw new ApiException('微信小程序未配置'); } $config_data = is_string($config) ? json_decode($config, true) : $config; if (empty($config_data['app_id']) || empty($config_data['app_secret'])) { throw new ApiException('微信小程序配置不完整,缺少app_id或app_secret'); } return [ 'app_id' => $config_data['app_id'] ?? '', 'secret' => $config_data['app_secret'] ?? '', 'token' => $config_data['token'] ?? '', 'aes_key' => $config_data['encoding_aes_key'] ?? '' ]; } }