where('group_key', 'wx') ->whereOr('key', 'like', 'wx.%') ->select() as $row) { self::$cfg[$row['key']] = $row['value']; } } catch (\Exception $e) { // 业务表尚未初始化时不阻断请求,按「未配置」处理 } } if ($key === null) { return self::$cfg; } return array_key_exists($key, self::$cfg) ? self::$cfg[$key] : $default; } /** 清除运行时配置缓存(后台保存配置后调用) */ public static function flush() { self::$cfg = null; self::$app = null; } /** 是否已配置 AppID / AppSecret */ public static function isConfigured() { return (string)self::config('wx.appid', '') !== '' && (string)self::config('wx.secret', '') !== ''; } /* ==================== SDK ==================== */ /** * 获取 EasyWeChat 小程序实例 * * @return \EasyWeChat\MiniProgram\Application * @throws \RuntimeException 未配置 AppID/AppSecret 时抛出 */ public static function app() { if (self::$app instanceof Application) { return self::$app; } if (!self::isConfigured()) { throw new \RuntimeException('微信小程序未配置,请先在后台「系统设置 → 系统配置 → 小程序配置」填写 AppID 与 AppSecret'); } $app = Factory::miniProgram([ 'app_id' => (string)self::config('wx.appid', ''), 'secret' => (string)self::config('wx.secret', ''), 'response_type' => 'array', 'http' => [ 'timeout' => 8.0, 'connect_timeout' => 5.0, ], ]); // access_token 缓存落盘到 runtime(SDK 默认用系统临时目录,易被清理且不便排查) if (class_exists('Symfony\Component\Cache\Psr16Cache') && class_exists('Symfony\Component\Cache\Adapter\FilesystemAdapter')) { $base = defined('CACHE_PATH') ? CACHE_PATH : RUNTIME_PATH . 'cache' . DIRECTORY_SEPARATOR; $dir = $base . 'wechat'; if (!is_dir($dir)) { @mkdir($dir, 0755, true); } $app['cache'] = new \Symfony\Component\Cache\Psr16Cache( new \Symfony\Component\Cache\Adapter\FilesystemAdapter('easywechat', 7200, $dir) ); } return self::$app = $app; } /* ==================== 登录 / 手机号 ==================== */ /** * code 换取登录态(jscode2session) * * @param string $code wx.login / uni.login 返回的 code * @return array{openid:string,session_key?:string,unionid?:string} * @throws \RuntimeException 微信侧返回错误时抛出(message 可直接展示给用户) */ public static function session($code) { $code = trim((string)$code); if ($code === '') { throw new \RuntimeException('缺少微信授权 code'); } $res = self::toArray(self::app()->auth->session($code)); if (empty($res['openid'])) { throw new \RuntimeException(self::errorMessage($res, '微信登录失败,请重试')); } return $res; } /** * code 换取 openid(登录场景的简写) * * @param string $code * @return string * @throws \RuntimeException */ public static function openid($code) { $res = self::session($code); return (string)$res['openid']; } /** * 手机号快速验证 code 换取真实手机号 * (