chore: 重写初始提交(清空历史,整理后全量提交)
This commit is contained in:
@@ -0,0 +1,253 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | YwxApp [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2026-2036 http://ywxapp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: ywxapp<admin@ywxapp.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace ywxapp\controller;
|
||||
|
||||
use think\App;
|
||||
use think\exception\ValidateException;
|
||||
use think\Validate;
|
||||
use ywxapp\library\Result;
|
||||
use think\facade\Lang;
|
||||
use think\facade\View;
|
||||
|
||||
/**
|
||||
* 控制器基础类
|
||||
*/
|
||||
abstract class BaseController
|
||||
{
|
||||
/**
|
||||
* 应用实例
|
||||
* @var \think\App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* Request实例
|
||||
* @var \think\Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* 应用模块名称
|
||||
* @var string
|
||||
*/
|
||||
protected $module = '';
|
||||
|
||||
/**
|
||||
* 是否批量验证
|
||||
* @var bool
|
||||
*/
|
||||
protected $batchValidate = false;
|
||||
|
||||
/**
|
||||
* 控制器中间件
|
||||
* @var array
|
||||
*/
|
||||
protected $middleware = [];
|
||||
|
||||
/**
|
||||
* 控制器模型
|
||||
* @var \think\Model
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 响应实例
|
||||
* @var \ywxapp\library\Result
|
||||
*/
|
||||
protected $result;
|
||||
|
||||
/**
|
||||
* 用户实例
|
||||
* @var \ywxapp\library\Auth
|
||||
*/
|
||||
protected $auth;
|
||||
|
||||
/**
|
||||
* Summary of needLogin
|
||||
* @var array
|
||||
*/
|
||||
protected $noNeedLogin = [];
|
||||
/**
|
||||
* Summary of needRight
|
||||
* @var array
|
||||
*/
|
||||
protected $noNeedVerify = [];
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @access public
|
||||
* @param App $app 应用对象
|
||||
*/
|
||||
public function __construct(App $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
// 安全的CORS配置(必须在 $this->app 赋值后再调用,使用容器 Env 而非全局 env() 助手)
|
||||
$this->setCorsHeaders();
|
||||
|
||||
$this->module = $app->http->getName();
|
||||
$this->result = $app->result;
|
||||
$this->request = $app->request;
|
||||
$this->auth = $app->auth;
|
||||
// 尝试用 token 还原登录态:有 token 就解析并初始化用户,没有则跳过(不报错、不影响后续)
|
||||
$this->auth->tryInitByToken();
|
||||
// 登录验证(需要登录的 action 未登录返回 401;已登录则校验权限)
|
||||
|
||||
// 加载当前控制器语言包
|
||||
$this->loadlang($this->request->controller());
|
||||
$this->_initialize();
|
||||
$this->assignSite();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置安全的CORS头部
|
||||
*/
|
||||
protected function setCorsHeaders(): void
|
||||
{
|
||||
// 从配置或环境变量获取允许的来源(使用容器 Env,避免依赖全局 env() 助手在插件/CLI 上下文未注册)
|
||||
$allowedOrigins = $this->app->env->get('CORS_ALLOWED_ORIGINS', 'http://localhost:3000,http://localhost:8080');
|
||||
$allowedOriginsArray = array_filter(array_map('trim', explode(',', $allowedOrigins)));
|
||||
|
||||
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
|
||||
|
||||
// 检查来源是否在白名单中
|
||||
if (in_array($origin, $allowedOriginsArray) || empty($origin)) {
|
||||
if (!empty($origin)) {
|
||||
header('Access-Control-Allow-Origin: ' . $origin);
|
||||
header('Access-Control-Allow-Credentials: true');
|
||||
}
|
||||
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With');
|
||||
header('Access-Control-Max-Age: 86400'); // 24小时预检缓存
|
||||
}
|
||||
|
||||
// 处理OPTIONS预检请求
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
http_response_code(200);
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向模板注入当前应用的基础路径信息。
|
||||
*
|
||||
* 关键:对外 URL 的模块段必须是 app_map 的「虚拟名」(admin/user/home),
|
||||
* 而非 http->getName() 返回的「真实目录名」(backend/member/frontend),
|
||||
* 否则拼接出的 URL 会与路由不匹配(重复 / 错位模块名)。
|
||||
* 虚拟名直接取自 request->root() 中解析出的那段。
|
||||
*/
|
||||
protected function assignSite(): void
|
||||
{
|
||||
$root = $this->app->request->root(); // 如 /admin、/user、默认应用为空
|
||||
$appRoot = ltrim($root, '/'); // 虚拟名:admin/user/home,默认应用为 ''
|
||||
// 部署根(去掉模块段):/sub/admin -> /sub;/admin -> ''(网站根目录)
|
||||
$deployRoot = preg_replace('#^/[^/]+#', '', $root);
|
||||
|
||||
View::assign('site', [
|
||||
'root' => $deployRoot === '' ? '' : rtrim($deployRoot, '/'),
|
||||
'module' => $appRoot, // 对外模块段(虚拟名,URL 用):admin/user/home
|
||||
'app' => $this->module, // 真实应用目录名(静态资源目录用):backend/member/frontend/home
|
||||
'controller' => $this->request->controller(),
|
||||
'action' => $this->request->action(),
|
||||
'devToken' => config('ywxapp.developer_token'),
|
||||
'devAddon' => config('ywxapp.addon_developer')
|
||||
]);
|
||||
|
||||
// 后端生成一个「带占位符的基准 URL」,供 JS(route.js) 做片段替换,
|
||||
// 从而与后端路由规则(app_map/别名/后缀)完全对齐。
|
||||
// 例如当前应用在 backend 时生成: /admin/{__CTRL__}/{__ACT__}
|
||||
// 注意:不可用 Route::buildUrl('{__CTRL__}/{__ACT__}') —— 它把占位符当真实
|
||||
// 路由解析,在某些路由配置下会误生成 /menu/{__CTRL__} 之类的异常路径。
|
||||
// 改为按 app_map 反查当前应用(真实目录名 $this->module)的虚拟前缀手动拼。
|
||||
$appMap = (array) config('app.app_map');
|
||||
$appPrefix = array_search($this->module, $appMap, true);
|
||||
if (! $appPrefix) {
|
||||
$appPrefix = $this->module; // 无映射时直接用真实目录名
|
||||
}
|
||||
$base = $deployRoot === '' ? '' : rtrim($deployRoot, '/');
|
||||
$routeBase = $base . '/' . $appPrefix . '/{__CTRL__}/{__ACT__}';
|
||||
View::assign('route_base', $routeBase);
|
||||
}
|
||||
|
||||
/**
|
||||
* 控制器初始化 _initialize
|
||||
* @return void
|
||||
*/
|
||||
protected function _initialize()
|
||||
{}
|
||||
|
||||
/**
|
||||
* 加载语言文件.
|
||||
*
|
||||
* @param string $name
|
||||
*/
|
||||
protected function loadlang($name = '')
|
||||
{
|
||||
$name = $name ?: $this->request->controller();
|
||||
if (strpos($name, '.')) {
|
||||
$_arr = explode('.', $name);
|
||||
if (count($_arr) == 2) {
|
||||
$path = $_arr[0] . '/' . strtolower($_arr[1]);
|
||||
} else {
|
||||
$path = strtolower($name);
|
||||
}
|
||||
} else {
|
||||
$path = strtolower($name);
|
||||
}
|
||||
Lang::load($this->app->getAppPath() . '/lang/' . Lang::getLangset() . '/' . $path . '.php');
|
||||
}
|
||||
/**
|
||||
* 验证数据
|
||||
* @access protected
|
||||
* @param array $data 数据
|
||||
* @param string|array $validate 验证器名或者验证规则数组
|
||||
* @param array $message 提示信息
|
||||
* @param bool $batch 是否批量验证
|
||||
* @return array|string|true
|
||||
* @throws ValidateException
|
||||
*/
|
||||
protected function validate(array $data, string | array $validate, array $message = [], bool $batch = false)
|
||||
{
|
||||
if (is_array($validate)) {
|
||||
$v = new Validate();
|
||||
$v->rule($validate);
|
||||
} else {
|
||||
if (strpos($validate, '.')) {
|
||||
[$validate, $scene] = explode('.', $validate);
|
||||
}
|
||||
$class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
|
||||
$v = new $class();
|
||||
if (! empty($scene)) {
|
||||
$v->scene($scene);
|
||||
}
|
||||
}
|
||||
$v->message($message);
|
||||
if ($batch || $this->batchValidate) {
|
||||
$v->batch(true);
|
||||
}
|
||||
return $v->failException(true)->check($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功响应(转发到 Result).
|
||||
*/
|
||||
public function success($data = null, $message = 'success', int $code = 0)
|
||||
{
|
||||
return $this->result->success($data, $message, $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败响应(转发到 Result).
|
||||
*/
|
||||
public function error($message = 'Error', int $code = 1, $data = null)
|
||||
{
|
||||
return $this->result->error($message, $code, $data);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user