* @version : HUOSDK 8.0 */ namespace wap\common\controller; use huo\controller\agent\Agent; use huo\controller\common\HuoSession; use huolib\constant\CommonConst; use huolib\constant\FormatConst; use huolib\status\CommonStatus; use think\Config; use think\Controller; use think\Db; use think\exception\HttpResponseException; use think\exception\ValidateException; use think\Loader; use think\Log; use think\Response; use think\View; class V2BaseController extends Controller { // 语言 protected $lang = 'en'; /* 返回类型 */ protected $response_type = 'html'; protected $allowed_device_types = ['mobile', 'android', 'iphone', 'ipad', 'web', 'pc', 'mac', 'wxapp']; /** * @var \think\Request Request实例 */ protected $request; protected $agent_site; /* 渠道专属链接 */ // 验证失败是否抛出异常 protected $fail_exception = false; // 是否批量验证 protected $batch_validate = false; protected $rq_data = []; protected $sess_config = [ 'id' => '', 'prefix' => '', 'type' => '', 'expire' => CommonConst::CONST_DAY_SECONDS ]; /** * 前置操作方法列表 * * @var array $before_action_list * @access protected */ protected $before_action_list = []; // 初始化 private function _initLang() { $_lang = $this->request->header('HS-Lang/s', 'en-us'); $this->lang = $_lang; config('default_lang', $this->lang); } protected function _initialize() { if(!$this->request->isMobile()) { $this->redirect(WEBSITE); } $this->response_type = $this->request->param('format/s', FormatConst::FORMAT_HTML); if (FormatConst::FORMAT_HTML == $this->response_type) { $this->_initializeView(); $_site = $this->request->domain(); $_agent_id = (new Agent())->getAgentIdBySite($_site); if (empty($_agent_id)) { $_agent_id = $this->request->request('agent_id', 0); } if (empty($_agent_id)) { $_agent_id = (new HuoSession($this->mem_id))->getAgentId(); } else { (new HuoSession($this->mem_id))->setAgentId($_agent_id); } $siteInfo = cmf_get_site_info($_agent_id); View::share('site_info', $siteInfo); View::share('agent_id', $_agent_id); View::share('agent_site', $_site); } Config::set('default_return_type', $this->response_type); $this->_initLang(); $this->checkParam(); /* 记录请求数据 */ Log::write($this->request->getContent(), Log::LOG); Log::write($this->rq_data, Log::LOG); if ($this->request->isOptions()) { $this->success(); } } /** * 前置操作 * * @access protected * * @param string $method 前置操作方法名 * @param array $options 调用参数 ['only'=>[...]] 或者['except'=>[...]] */ protected function beforeAction($method, $options = []) { if (isset($options['only'])) { if (is_string($options['only'])) { $options['only'] = explode(',', $options['only']); } if (!in_array($this->request->action(), $options['only'])) { return; } } elseif (isset($options['except'])) { if (is_string($options['except'])) { $options['except'] = explode(',', $options['except']); } if (in_array($this->request->action(), $options['except'])) { return; } } call_user_func([$this, $method]); } /** * 返回数据 * * @param array $data */ protected function returnData($data = []) { $_msg = $data['msg']; $_code = $data['code']; $_data = $data['data']; if (CommonStatus::NO_ERROR != $_code) { $this->error($_msg, $_data, $_code); } $this->success($_msg, $_data, $_code); } /** * 操作错误跳转的快捷方法 * * @access protected * * @param mixed $msg 提示信息,若要指定错误码,可以传数组,格式为['code'=>您的错误码,'msg'=>'您的错误消息'] * @param mixed $data 返回的数据 * @param int $code * * @param array $header 发送的Header信息 * * @param null $url * @param int $wait * * @return void */ protected function error($msg = '', $data = '', $code = 400, array $header = [], $url = null, $wait = 3) { $type = $this->getResponseType(); if ('html' == $type) { parent::error($msg, $data, $code, $header, $url, $wait); } if (empty($data)) { $data = null; } $result = [ 'code' => $code, 'msg' => $msg, 'data' => $data, ]; $header['Access-Control-Allow-Origin'] = '*'; $header['Access-Control-Allow-Headers'] = 'X-Requested-With,Content-Type,HS-Device-Type,HS-Token,HS-Lang'; $header['Access-Control-Allow-Methods'] = 'GET,POST,PATCH,PUT,DELETE,OPTIONS'; $response = Response::create($result, $type)->header($header); throw new HttpResponseException($response); } /** * 获取当前的response 输出类型 * * @access protected * @return string */ protected function getResponseType() { return $this->response_type; } /** * 获取当前登录用户的id * * @return int */ public function getMemId() { if (empty($this->mem_id)) { $this->error(lang('NO_LOGIN'), '', 1002); } // $this->mem_id = rand(1, 10); return $this->mem_id; } /** * 设置验证失败后是否抛出异常 * * @access protected * * @param bool $fail 是否抛出异常 * * @return $this */ protected function validateFailException($fail = true) { $this->fail_exception = $fail; return $this; } /** * 验证数据 * * @access protected * * @param array $data 数据 * @param string|array $validate 验证器名或者验证规则数组 * @param array $message 提示信息 * @param bool $batch 是否批量验证 * @param mixed $callback 回调方法(闭包) * * @return array|string|true * @throws ValidateException */ protected function validate($data, $validate, $message = [], $batch = false, $callback = null) { if (is_array($validate)) { $_valid_class = Loader::validate(); $_valid_class->rule($validate); } else { if (strpos($validate, '.')) { // 支持场景 list($validate, $scene) = explode('.', $validate); } $_valid_class = Loader::validate($validate); if (!empty($scene)) { $_valid_class->scene($scene); } } // 是否批量验证 if ($batch || $this->batch_validate) { $_valid_class->batch(true); } if (is_array($message)) { $_valid_class->message($message); } if ($callback && is_callable($callback)) { call_user_func_array($callback, [$_valid_class, &$data]); } if (!$_valid_class->check($data)) { if ($this->fail_exception) { throw new ValidateException($_valid_class->getError()); } else { return $_valid_class->getError(); } } else { return true; } } /** * 操作成功跳转的快捷方法 * * @access protected * * @param mixed $msg 提示信息 * @param mixed $data 返回的数据 * @param array $header 发送的Header信息 * * @param int $code 返回码 * * @return void */ protected function success($msg = '', $data = '', $code = 200, array $header = [], $url = null, $wait = 3) { $type = $this->getResponseType(); if ('html' == $type) { parent::success($msg, $data, $code, $header, $url, $wait); } if (empty($data)) { $data = null; } $result = [ 'code' => $code, 'msg' => $msg, 'data' => $data, ]; $header['Access-Control-Allow-Origin'] = '*'; $header['Access-Control-Allow-Headers'] = 'X-Requested-With,Content-Type,HS-Device-Type,HS-Token,HS-Lang'; $header['Access-Control-Allow-Methods'] = 'GET,POST,PATCH,PUT,DELETE,OPTIONS'; $response = Response::create($result, $type)->header($header); throw new HttpResponseException($response); } protected function checkParam() { $_is_json = false; // $_params = $this->request->post(); $_params = $_REQUEST; if ($this->request->isPost()) { // $_params = $_POST; } $this->rq_data = $this->getParam($_params, $_is_json); if (empty($this->rq_data)) { $this->rq_data = []; } $this->checkTime(); } /** * 获取请求参数 * * @param mixed $param * @param bool $is_json * * @return bool|mixed|string */ public function getParam($param, $is_json = false) { if (empty($param)) { return ''; } if ($is_json) { $_param = json_decode($param, true); if (JSON_ERROR_NONE != json_last_error()) { return false; } } else { foreach ($param as $_k => $_v) { if (strpos($_k, '-')) { list($_k1, $_k2) = explode('-', $_k); if (is_array($_k2)) { return false; } $_param[$_k1][$_k2] = $_v; } else { $_param[$_k] = $_v; } } } if (empty($_param)) { return false; } return $_param; } /** * 校验请求事件 */ protected function checkTime() { $_ts = get_val($this->rq_data, 'ts', 0); if (abs($_ts - time()) < 5) { $this->error('time is error'); } } public function _initializeView() { $cmfThemePath = config('cmf_theme_path'); $cmfDefaultTheme = cmf_get_current_theme(); $themePath = "{$cmfThemePath}{$cmfDefaultTheme}"; $root = cmf_get_root(); //使cdn设置生效 $cdnSettings = cmf_get_option('cdn_settings'); if (empty($cdnSettings['cdn_static_root'])) { $viewReplaceStr = [ '__ROOT__' => $root, '__TMPL__' => "{$root}/{$themePath}", '__STATIC__' => "{$root}/static", '__WEB_ROOT__' => $root ]; } else { $cdnStaticRoot = rtrim($cdnSettings['cdn_static_root'], '/'); $viewReplaceStr = [ '__ROOT__' => $root, '__TMPL__' => "{$cdnStaticRoot}/{$themePath}", '__STATIC__' => "{$cdnStaticRoot}/static", '__WEB_ROOT__' => $cdnStaticRoot ]; } $viewReplaceStr = array_merge(config('view_replace_str'), $viewReplaceStr); config('template.view_base', "{$themePath}/"); config('view_replace_str', $viewReplaceStr); $themeErrorTmpl = "{$themePath}/error.html"; if (file_exists_case($themeErrorTmpl)) { config('dispatch_error_tmpl', $themeErrorTmpl); } $themeSuccessTmpl = "{$themePath}/success.html"; if (file_exists_case($themeSuccessTmpl)) { config('dispatch_success_tmpl', $themeSuccessTmpl); } } /** * 加载模板输出 * * @access protected * * @param string $template 模板文件名 * @param array $vars 模板输出变量 * @param array $replace 模板替换 * @param array $config 模板参数 * * @return mixed */ protected function fetch($template = '', $vars = [], $replace = [], $config = []) { $template = $this->parseTemplate($template); $more = $this->getThemeFileMore($template); $this->assign('theme_vars', $more['vars']); $this->assign('theme_widgets', $more['widgets']); return parent::fetch($template, $vars, $replace, $config); } /** * 自动定位模板文件 * * @access private * * @param string $template 模板文件规则 * * @return string */ private function parseTemplate($template) { // 分析模板文件规则 $request = $this->request; // 获取视图根目录 if (strpos($template, '@')) { // 跨模块调用 list($module, $template) = explode('@', $template); } $viewBase = config('template.view_base'); if ($viewBase) { // 基础视图目录 $module = isset($module) ? $module : $request->module(); $path = $viewBase.($module ? $module.DS : ''); } else { $path = isset($module) ? APP_PATH.$module.DS.'view'.DS : config('template.view_path'); } $depr = config('template.view_depr'); if (0 !== strpos($template, '/')) { $template = str_replace(['/', ':'], $depr, $template); $controller = cmf_parse_name($request->controller()); if ($controller) { if ('' == $template) { // 如果模板文件名为空 按照默认规则定位 $template = str_replace('.', DS, $controller).$depr.$request->action(); } elseif (false === strpos($template, $depr)) { $template = str_replace('.', DS, $controller).$depr.$template; } } } else { $template = str_replace(['/', ':'], $depr, substr($template, 1)); } return $path.ltrim($template, '/').'.'.ltrim(config('template.view_suffix'), '.'); } /** * 获取模板文件变量 * * @param string $file * @param string $theme * * @return array */ private function getThemeFileMore($file, $theme = "") { //TODO 增加缓存 $theme = empty($theme) ? cmf_get_current_theme() : $theme; $themePath = config('cmf_theme_path'); $file = str_replace('\\', '/', $file); $file = str_replace('//', '/', $file); $file = str_replace(['.html', '.php', $themePath.$theme."/"], '', $file); $files = Db::name('theme_file')->field('more')->where(['theme' => $theme])->where( function ($query) use ($file) { $query->where(['is_public' => 1])->whereOr(['file' => $file]); } )->select(); $vars = []; $widgets = []; foreach ($files as $file) { $oldMore = json_decode($file['more'], true); if (!empty($oldMore['vars'])) { foreach ($oldMore['vars'] as $varName => $var) { $vars[$varName] = $var['value']; } } if (!empty($oldMore['widgets'])) { foreach ($oldMore['widgets'] as $widgetName => $widget) { $widgetVars = []; if (!empty($widget['vars'])) { foreach ($widget['vars'] as $varName => $var) { $widgetVars[$varName] = $var['value']; } } $widget['vars'] = $widgetVars; $widgets[$widgetName] = $widget; } } } return ['vars' => $vars, 'widgets' => $widgets]; } }