278 lines
8.7 KiB
PHP
278 lines
8.7 KiB
PHP
<?php
|
||
// +----------------------------------------------------------------------
|
||
// | YwxApp [ WE CAN DO IT JUST THINK ]
|
||
// +----------------------------------------------------------------------
|
||
// | Copyright (c) 2026-2036 http://ywxapp.cn All rights reserved.
|
||
// +----------------------------------------------------------------------
|
||
// | Author: ywxapp<admin@ywxapp.cn>
|
||
// +----------------------------------------------------------------------
|
||
|
||
namespace ywxapp\library;
|
||
|
||
use BcMath\Number;
|
||
use think\exception\HttpResponseException;
|
||
use think\facade\Request;
|
||
use think\Response;
|
||
|
||
|
||
class Result
|
||
{
|
||
/**
|
||
* Summary of StatusCode
|
||
* @var int
|
||
*/
|
||
protected $StatusCode = 200;
|
||
public $Message = null;
|
||
|
||
/**
|
||
* Summary of Headers
|
||
* @var array
|
||
*/
|
||
protected $Headers = [];
|
||
|
||
protected $AccessToken;
|
||
|
||
protected $AccessExpired;
|
||
|
||
protected $RefreshToken;
|
||
|
||
protected $RefreshExpired;
|
||
|
||
protected $Total = 0;
|
||
|
||
/**
|
||
* Summary of Type
|
||
* @var string
|
||
*/
|
||
protected $Type = 'json';
|
||
|
||
|
||
protected $isCout = false;
|
||
|
||
|
||
public function __construct()
|
||
{
|
||
if (! Request::isAjax()) {
|
||
$this->Type = 'html';
|
||
}
|
||
}
|
||
|
||
|
||
public function success($data = null, $message = 'success', int $code = 0)
|
||
{
|
||
$result = ['code' => 0, 'message' => $message, 'timestamp' => time()];
|
||
if ($data) {
|
||
$result['data'] = $data;
|
||
}
|
||
if ($this->Total > 0) {
|
||
$result['count'] = $this->Total;
|
||
}
|
||
if ($this->AccessToken != null) {
|
||
$result['access_token'] = $this->AccessToken;
|
||
$result['access_expired'] = $this->AccessExpired;
|
||
}
|
||
if ($this->RefreshToken != null) {
|
||
$result['refresh_token'] = $this->RefreshToken;
|
||
$result['refresh_expired'] = $this->RefreshExpired;
|
||
}
|
||
$response = '';
|
||
if ('html' == strtolower($this->Type)) {
|
||
\think\facade\View::config(['view_path' => root_path() . 'ywxapp/view/']);
|
||
\think\facade\View::layout(false);
|
||
$result = \think\facade\View::fetch('success.html', $result);
|
||
}
|
||
$response = Response::create($result, strtolower($this->Type), $this->StatusCode)->header($this->Headers);
|
||
$this->applyTokenCookies($response);
|
||
$response->send();
|
||
app()->http->end($response);
|
||
exit;
|
||
// throw new HttpResponseException($response);
|
||
}
|
||
|
||
|
||
public function error($message = 'Error', int $code = 1, $data = null)
|
||
{
|
||
$result = ['code' => $code, 'message' => $message, 'timestamp' => time()];
|
||
if ($data) {
|
||
$result['data'] = $data;
|
||
}
|
||
if ($this->AccessToken != null) {
|
||
$result['access_token'] = $this->AccessToken;
|
||
$result['access_expired'] = $this->AccessExpired;
|
||
}
|
||
if ($this->RefreshToken != null) {
|
||
$result['refresh_token'] = $this->RefreshToken;
|
||
$result['refresh_expired'] = $this->RefreshExpired;
|
||
}
|
||
$response = '';
|
||
// 若已被强制为 JSON(如插件 API 在 MultiApp 中间件里 setType('json')),
|
||
// 或请求本身是 ajax,则统一返回 JSON;否则按 HTML 渲染。
|
||
$type = ($this->Type === 'json') ? 'json' : (Request::isAjax() ? 'json' : 'html');
|
||
|
||
if ($this->StatusCode == 401) {
|
||
$result['url'] = url('login/index');
|
||
$result['code'] = $this->StatusCode;
|
||
if ('html' == strtolower($type)) {
|
||
\think\facade\View::config(['view_path' => root_path() . 'ywxapp' . DIRECTORY_SEPARATOR . 'view' . DIRECTORY_SEPARATOR]);
|
||
$result = \think\facade\View::fetch('401.html', $result);
|
||
}
|
||
$response = Response::create($result, strtolower($type), 401);
|
||
$this->applyTokenCookies($response);
|
||
} else {
|
||
if ('html' == strtolower($type)) {
|
||
\think\facade\View::config(['view_path' => root_path() . 'ywxapp' . DIRECTORY_SEPARATOR . 'view' . DIRECTORY_SEPARATOR]);
|
||
\think\facade\View::layout(false);
|
||
$result = \think\facade\View::fetch('error.html', $result);
|
||
}
|
||
$response = Response::create($result, strtolower($type), $this->StatusCode)->header($this->Headers);
|
||
$this->applyTokenCookies($response);
|
||
}
|
||
$response->send();
|
||
app()->http->end($response);
|
||
exit;
|
||
throw new HttpResponseException($response);
|
||
}
|
||
|
||
/**
|
||
* 返回封装后的 API 数据到客户端.
|
||
*
|
||
* @param mixed $data 要返回的数据
|
||
* @param int $code 返回的 code
|
||
* @param mixed $msg 提示信息
|
||
* @param string $type 返回数据格式
|
||
* @param array $header 发送的 Header 信息
|
||
*/
|
||
protected function result($data, $code = 0, $msg = '', $type = '', array $header = [])
|
||
{
|
||
$result = [
|
||
'code' => $code,
|
||
'msg' => $msg,
|
||
'time' => Request::server('REQUEST_TIME'),
|
||
'data' => $data,
|
||
];
|
||
$type = $type ?: $this->getResponseType();
|
||
$response = Response::create($result, $type)->header($header);
|
||
|
||
throw new HttpResponseException($response);
|
||
}
|
||
|
||
/**
|
||
* URL 重定向.
|
||
*
|
||
* @param string $url 跳转的 URL 表达式
|
||
* @param array|int $params 其它 URL 参数
|
||
* @param int $code http code
|
||
* @param array $with 隐式传参
|
||
*/
|
||
public function redirect($url, $code = 302, $params = [], $with = []): self
|
||
{
|
||
if (is_int($params)) {
|
||
$code = $params;
|
||
}
|
||
$response = \redirect($url);
|
||
$response->code($code)->with($with);
|
||
throw new HttpResponseException($response);
|
||
}
|
||
|
||
/**
|
||
* 设置Token
|
||
* @param string $token
|
||
* @return void
|
||
*/
|
||
public function setAccessToken($token, $expired = null): self
|
||
{
|
||
$this->AccessToken = $token;
|
||
$this->AccessExpired = $expired;
|
||
return app()->result;
|
||
}
|
||
|
||
/**
|
||
* 设置Token
|
||
* @param string $token
|
||
* @return void
|
||
*/
|
||
public function setRefreshToken($token, $expired = null): self
|
||
{
|
||
$this->RefreshToken = $token;
|
||
$this->RefreshExpired = $expired;
|
||
return $this;
|
||
}
|
||
|
||
public function setStatusCode(int $code = 200): self
|
||
{
|
||
$this->StatusCode = $code;
|
||
return $this;
|
||
}
|
||
|
||
|
||
public function setMessage(string $message = ''): self
|
||
{
|
||
$this->Message = $message;
|
||
return $this;
|
||
}
|
||
|
||
|
||
public function setHeaders(array $header = []): self
|
||
{
|
||
$this->Headers = $header;
|
||
return $this;
|
||
}
|
||
|
||
/**
|
||
* 强制设置响应类型(json/html)
|
||
* 插件 API 由小程序/APP 等客户端调用,请求不带 X-Requested-With,
|
||
* 在 MultiApp 中间件里统一 setType('json') 以确保返回 JSON。
|
||
* @param string $type
|
||
* @return self
|
||
*/
|
||
public function setType(string $type = 'json'): self
|
||
{
|
||
$this->Type = strtolower($type);
|
||
return app()->result;
|
||
}
|
||
|
||
/**
|
||
* 把当前持有的 access/refresh token 写入响应 Cookie(path=/,非 httponly)。
|
||
*
|
||
* 背景:登录/注册的 token 经 JwtService 存入本 Result 单例(setAccessToken/setRefreshToken),
|
||
* 但 persistTokens() 用的全局 Cookie 门面与 Response 持有的 Cookie 实例并非同一个,
|
||
* 仅靠门面 set 的 token 不会被 Response::send() 输出,导致整页跳转(菜单点击、iframe)
|
||
* 不带 token → 后端读不到 → 跳登录页。此处直接写到 Response 实例上,确保 100% 随响应输出。
|
||
*
|
||
* @param Response $response
|
||
* @return void
|
||
*/
|
||
protected function applyTokenCookies(Response $response): void
|
||
{
|
||
if ($this->AccessToken !== null && $this->AccessToken !== '') {
|
||
$expire = $this->AccessExpired ? ((int) $this->AccessExpired - time()) : 0;
|
||
$response->cookie('access_token', (string) $this->AccessToken, [
|
||
'expire' => $expire,
|
||
'path' => '/',
|
||
'httponly' => false,
|
||
]);
|
||
}
|
||
if ($this->RefreshToken !== null && $this->RefreshToken !== '') {
|
||
$expire = $this->RefreshExpired ? ((int) $this->RefreshExpired - time()) : 0;
|
||
$response->cookie('refresh_token', (string) $this->RefreshToken, [
|
||
'expire' => $expire,
|
||
'path' => '/',
|
||
'httponly' => false,
|
||
]);
|
||
}
|
||
}
|
||
|
||
|
||
public function setCount(int $total): self
|
||
{
|
||
$this->Total = $total;
|
||
return app()->result;
|
||
}
|
||
|
||
|
||
public static function instance($options = []): Result
|
||
{
|
||
return app()->result;
|
||
}
|
||
}
|