// +---------------------------------------------------------------------- declare (strict_types=1); namespace app\store\controller; use app\common\crypto\Crypto; use app\store\model\store\User; use cores\BaseController; use app\store\service\Auth as AuthService; use app\common\service\store\User as StoreUserService; use app\common\exception\BaseException; use think\response\Json; use think\Session; use app\common\service\SybOil; /** * 商户后台控制器基类 * Class Controller * @package app\store\controller */ class Controller extends BaseController { // 商家登录信息 protected $store; // 当前商城ID protected $storeId; // 当前控制器名称 protected $controller = ''; // 当前方法名称 protected $action = ''; // 当前路由uri protected $routeUri = ''; // 当前路由:分组名称 protected $group = ''; // 登录验证白名单 protected $allowAllAction = [ 'passport/login', 'passport/logout', 'passport/detail', 'merchant/all', ]; /** * 强制验证当前访问的控制器方法method * 例: [ 'login' => 'POST' ] * @var array */ protected $methodRules = []; /** * 后台初始化 * @throws BaseException * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException */ public function initialize() { // 当前商城id $this->storeId = $this->getStoreId(); // 设置管理员登录信息 $this->setStoreInfo(); // 当前路由信息 $this->getRouteInfo(); // 验证登录状态 $this->checkLogin(); // 验证当前API权限 $this->checkPrivilege(); // 强制验证当前访问的控制器方法method $this->checkMethodRules(); } /** * 设置管理员登录信息 */ private function setStoreInfo() { $this->store = StoreUserService::getLoginInfo(); } /** * 验证当前路由权限 * @return bool * @throws BaseException * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException */ private function checkPrivilege(): bool { // 在开发模式下, 建议把此处代码暂时屏蔽, 开发完成后在超管后台新增菜单和api if (!AuthService::getInstance()->checkPrivilege('/' . $this->routeUri)) { throwError('很抱歉,没有当前api的访问权限 ' . $this->routeUri); } return true; } /** * 解析当前路由参数 (分组名称、控制器名称、方法名) */ protected function getRouteInfo() { // 控制器名称 $this->controller = uncamelize($this->request->controller()); // 方法名称 $this->action = $this->request->action(); // 控制器分组 (用于定义所属模块) $group = strstr($this->controller, '.', true); $this->group = $group !== false ? $group : $this->controller; // 当前uri $this->routeUri = "{$this->controller}/$this->action"; } /** * 验证登录状态 * @return bool * @throws BaseException */ private function checkLogin(): bool { // 验证当前请求是否在白名单 if (in_array($this->routeUri, $this->allowAllAction)) { return true; } // 验证登录状态 if (empty($this->store) || (int)$this->store['is_login'] !== 1) { throwError('请先登录后再访问', config('status.not_logged')); } return true; } /** * 获取当前store_id * @return int|null */ protected function getStoreId(): ?int { // app/store/common.php return getStoreId(); } /** * 返回封装后的 API 数据到客户端 * @param int|null $status 状态码 * @param string $message * @param array $data * @return Json */ protected function renderData(int $status = null, string $message = '', array $data = []): Json { is_null($status) && $status = config('status.success'); return json(compact('status', 'message', 'data')); } /** * 返回操作成功json * @param array|string $data * @param string $message * @return Json */ protected function renderSuccess($data = [], string $message = 'success'): Json { if (is_string($data)) { $message = $data; $data = []; } return $this->renderData(config('status.success'), $message, $data); } /** * 返回操作失败json * @param string $message * @param array $data * @return Json */ protected function renderError(string $message = 'error', array $data = []): Json { return $this->renderData(config('status.error'), $message, $data); } /** * 获取post数据 (数组) * @param $key * @return mixed */ protected function postData($key = null) { return $this->request->post(empty($key) ? '' : "{$key}/a"); } /** * 获取post数据 (数组) * @param string $key * @return mixed */ protected function postForm(string $key = 'form') { return $this->postData($key); } /** * 获取post数据 (数组) * @param $key * @return mixed */ protected function getData($key = null) { return $this->request->get(is_null($key) ? '' : $key); } /** * 强制验证当前访问的控制器方法method * @return bool * @throws BaseException */ private function checkMethodRules(): bool { if (!isset($this->methodRules[$this->action])) { return true; } $methodRule = $this->methodRules[$this->action]; $currentMethod = $this->request->method(); if (empty($methodRule)) { return true; } if (is_array($methodRule) && in_array($currentMethod, $methodRule)) { return true; } if (is_string($methodRule) && $methodRule == $currentMethod) { return true; } throwError('illegal request method'); return false; } public function mk_dir_store($dir, $mode = 0777) { if (is_dir($dir) || @mkdir($dir,$mode)) return true; if (!$this->mk_dir_store(dirname($dir),$mode)) return false; return @mkdir($dir,$mode); } /** 记录日志 * @param $data */ public function saveLogStore($data){ $SybOil = new SybOil(); $SybOil->saveLogStore($data); } public function sendRequest($company, $url, $businessParams = [], $appendHeaders = [], $file = null) { $crypto = new Crypto($company); // 签名并组合header,并加密数据 $this->saveLogStore(json_encode($businessParams,320)); $requestData = $crypto->prepareRequest($appendHeaders, $businessParams); $businessParams = $requestData['encryptData']; // 构建默认的公共参数头 $headers = []; foreach ($requestData['header'] as $k=>$val){ $headers[] = $k.': '.$val; } $this->saveLogStore(json_encode($requestData['header'],320)); $this->saveLogStore(json_encode($businessParams,320)); // 使用 cURL 发送请求 $ch = curl_init(); // 设置请求 URL curl_setopt($ch, CURLOPT_URL, $url); // 设置请求方式为 POST curl_setopt($ch, CURLOPT_POST, true); // 返回结果而不是输出 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 包含响应头在输出中 curl_setopt($ch, CURLOPT_HEADER, true); $headers[] = 'Charset: UTF-8'; if ($file) { // 如果有文件上传,设置 multipart/form-data 表单 $filePath = $file->getPathname(); $fileName = $file->getOriginalName(); $fileMime = $file->getMimeType(); $postFields = [ 'file' => new \CURLFile($filePath, $fileMime, $fileName), 'businessParams' => json_encode($businessParams, JSON_UNESCAPED_UNICODE), ]; curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields); } else { // 如果没有文件上传,直接发送业务参数的 JSON 数据 $postFields = json_encode($businessParams, JSON_UNESCAPED_UNICODE); $headers[] = 'Content-Type: application/json'; curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields); } curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); // 执行请求 $response = curl_exec($ch); $this->saveLogStore('接口返回值:'.$response); // 检查是否有错误 if (curl_errno($ch)) { $error_msg = curl_error($ch); curl_close($ch); return ['code' => '01', 'message' => $error_msg]; } // 获取响应头大小 $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); // 分离响应头和响应体 $headerString = substr($response, 0, $header_size); $bodyString = substr($response, $header_size); // 关闭 cURL 句柄 curl_close($ch); // 解析响应头为数组 $headerArray = []; foreach (explode("\r\n", $headerString) as $header) { if (trim($header) !== '' && strpos($header, ':') !== false) { list($key, $value) = explode(': ', $header, 2); $headerArray[strtolower(trim($key))] = trim($value); } } $res = $crypto->processResponse(['header'=>$headerArray,'encryptData'=>json_decode($bodyString, true)]); if(!$res){ return ['code' => '01', 'message' => $bodyString]; } // 返回结果 return $res; } }