代码功能更新

This commit is contained in:
root
2025-05-18 07:42:27 +00:00
parent 7fc1d422a1
commit 4a90c41da2
2083 changed files with 695218 additions and 3 deletions
+51
View File
@@ -0,0 +1,51 @@
<?php
namespace laytp;
use laytp\traits\JsonReturn;
use think\App;
/**
* 控制器基础类
*/
abstract class BaseController
{
use JsonReturn;
/**
* Request实例
* @var \think\Request
*/
protected $request;
/**
* 应用实例
* @var \think\App
*/
protected $app;
/**
* 控制器中间件
* @var array
*/
protected $middleware = [];
/**
* 构造方法
* @access public
* @param App $app 应用对象
*/
public function __construct(App $app)
{
$this->app = $app;
$this->request = $this->app->request;
// 控制器初始化
$this->initialize();
}
// 初始化
protected function initialize()
{
}
}
+14
View File
@@ -0,0 +1,14 @@
<?php
declare (strict_types=1);
namespace laytp;
use laytp\traits\JsonReturn;
/**
* 中间件基类
*/
class BaseMiddleware
{
use JsonReturn;
}
+13
View File
@@ -0,0 +1,13 @@
<?php
/**
* 模型基类
*/
namespace laytp;
use think\Model;
class BaseModel extends Model
{
}
+13
View File
@@ -0,0 +1,13 @@
<?php
/**
* 资源基类
* 在控制器中,当数据查询完成后,需要对数据集某些数据进行格式化时,集成此基类
* 基类暂时是空的,目的是为了便于二次开发
*/
namespace laytp;
class Resource
{
}
+56
View File
@@ -0,0 +1,56 @@
<?php
namespace laytp\controller;
use app\middleware\api\Auth;
use app\middleware\api\CheckSign;
use app\service\api\AuthServiceFacade;
use app\service\api\CheckSignServiceFacade;
use laytp\BaseController;
use laytp\traits\JsonReturn;
/**
* Api应用控制器基类
* @package laytp\controller
*/
class Api extends BaseController
{
use JsonReturn;
/**
* 无需登录的方法,同时也就不需要鉴权了
* @var array
*/
protected $noNeedLogin = [];
protected $app;
/**
* 无需验证签名的方法
* @var array
*/
protected $noNeedCheckSign = [];
/**
* 中间件
* @var array
*/
protected $middleware = [
Auth::class,
CheckSign::class,
];
protected function initialize()
{
//将无需登录的方法名数组设置到权限服务中,以方便中间件获取
AuthServiceFacade::setNoNeedLogin($this->noNeedLogin);
//将无需验证签名的方法名数组设置到Api验证签名服务中,以方便中间件获取
CheckSignServiceFacade::setNoNeedCheckSign($this->noNeedCheckSign);
$this->_initialize();
}
// 初始化
protected function _initialize()
{
}
}
+162
View File
@@ -0,0 +1,162 @@
<?php
namespace laytp\controller;
use app\middleware\admin\ActionLog;
use app\middleware\admin\Auth;
use app\service\admin\AuthServiceFacade;
use laytp\BaseController;
/**
* 后台应用admin控制器基类
* @package laytp\controller
*/
class Backend extends BaseController
{
use \laytp\traits\Backend;
protected $noNeedLogin = [];//无需登录,也无需鉴权的方法名列表,支持*通配符定义所有方法
protected $noNeedAuth = [];//需要登录,但是无需鉴权的方法名列表,支持*通配符定义所有方法
protected $hasSoftDel = 0;//当前访问的模型是否有软删除功能
protected $orderRule = ['id' => 'desc'];//默认排序规则
/**
* 中间件
* @var array
*/
protected $middleware = [
Auth::class,
ActionLog::class,
];
/**
* 基类初始化方法
*/
protected function initialize()
{
//将无需登录的方法名数组设置到权限服务中
AuthServiceFacade::setNoNeedLogin($this->noNeedLogin);
//将无需鉴权的方法名数组设置到权限服务中
AuthServiceFacade::setNoNeedAuth($this->noNeedAuth);
$this->_initialize();
}
//子类初始化
protected function _initialize()
{
}
/**
* 生成查询条件
* post方式传递了search_param参数,就说明是进行筛选搜索
* search_param格式:
* search_param : {
* 数据库字段 : {
* value : 值,
* condition : 搜索条件
* }
* }
* @return array
*/
public function buildSearchParams()
{
$where = [];
$whereOr = [];
//传递了search_param就说明是进行搜索查询
$searchParam = $this->request->param('search_param');
if ($searchParam) {
foreach ($searchParam as $field => $valueCondition) {
if ($valueCondition['value'] !== '') {
$condition = trim(strtoupper($valueCondition['condition']));
switch ($condition) {
case '=':
$where[] = [$field, '=', $valueCondition['value']];
break;
case 'FIND_IN_SET':
$values = explode(',', $valueCondition['value']);
foreach ($values as $val) {
$whereOr[] = [$field, 'find in set', $val];
}
if ($whereOr) {
$where[] = function ($query) use ($whereOr) {
$query->whereOr($whereOr);
};
}
break;
case 'LIKE':
$where[] = [$field, 'like', '%' . $valueCondition['value'] . '%'];
break;
case 'IN':
$where[] = [$field, 'in', $valueCondition['value']];
break;
case 'BETWEEN':
$arrBetween = explode(' - ', $valueCondition['value']);
$where[] = [$field, 'between', $arrBetween[0] . ',' . $arrBetween[1]];
break;
case 'BETWEEN_STRTOTIME':
$arrBetween = explode(' - ', $valueCondition['value']);
$begin_time = strtotime($arrBetween[0]);
$end_time = strtotime($arrBetween[1]);
$where[] = [$field, 'between', $begin_time . ',' . $end_time];
break;
case '>':
$where[] = [$field, '>', $valueCondition['value']];
break;
case '>=':
$where[] = [$field, '>=', $valueCondition['value']];
break;
case '<':
$where[] = [$field, '<', $valueCondition['value']];
break;
case '<=':
$where[] = [$field, '<=', $valueCondition['value']];
break;
case '<>':
case '!=':
$where[] = [$field, '<>', $valueCondition['value']];
break;
}
}
}
}
return $where;
}
/**
* 生成排序条件
*/
public function buildOrder()
{
$order = $this->orderRule;
//传递了order_param字段,就说明是进行排序搜索
$orderParam = $this->request->param('order_param');
if ($orderParam && $orderParam['field'] && in_array(strtolower($orderParam['type']), ['asc', 'desc'])) {
if (isset($order[$orderParam['field']])) {
$order[$orderParam['field']] = $orderParam['type'];
} else {
$order = array_merge([$orderParam['field'] => $orderParam['type']], $order);
}
}
return $order;
}
/**
* 获取已选中项
*/
public function getSelectedData($data)
{
if (!$data) return [];
$selectedVal = $this->request->param('selected');
if (is_array($selectedVal) && $selectedVal) {
$selectedField = $this->request->param('selectedField', 'id');
$columns = array_column($data, $selectedField);
$selectedVal = array_filter(array_unique(array_diff($selectedVal, $columns)));
if ($selectedVal) {
$selectedData = $this->model->where($selectedField, 'in', $selectedVal)->select()->toArray();
$selectedData && $data = array_merge($data, $selectedData);
}
}
return $data;
}
}
+30
View File
@@ -0,0 +1,30 @@
<?php
namespace laytp\library;
/**
* 常用函数
* Class CommonFun
* @package laytp\library
*/
class CommonFun
{
/**
* 统一处理post数据
* @param $post
* @return mixed
*/
public static function filterPostData($post)
{
if (!$post) {
return [];
}
//处理数组
foreach ($post as $k => $v) {
if (is_array($v)) {
$post[$k] = implode(',', $v);
}
}
return $post;
}
}
+358
View File
@@ -0,0 +1,358 @@
<?php
namespace laytp\library;
use DateTime;
use DateTimeZone;
/**
* 日期时间处理类
*/
class Date
{
const YEAR = 31536000;
const MONTH = 2592000;
const WEEK = 604800;
const DAY = 86400;
const HOUR = 3600;
const MINUTE = 60;
/**
* 计算两个时区间相差的时长,单位为秒
* $seconds = self::offset('America/Chicago', 'GMT');
* [!!] A list of time zones that PHP supports can be found at
* <http://php.net/timezones>.
*
* @param $remote
* @param null $local
* @param null $now
* @return int
* @throws \Exception
*/
public static function offset($remote, $local = null, $now = null)
{
if ($local === null) {
// Use the default timezone
$local = date_default_timezone_get();
}
if (is_int($now)) {
// Convert the timestamp into a string
$now = date(DateTime::RFC2822, $now);
}
// Create timezone objects
$zone_remote = new DateTimeZone($remote);
$zone_local = new DateTimeZone($local);
// Create date objects from timezones
$time_remote = new DateTime($now, $zone_remote);
$time_local = new DateTime($now, $zone_local);
// Find the offset
$offset = $zone_remote->getOffset($time_remote) - $zone_local->getOffset($time_local);
return $offset;
}
/**
* 计算两个时间戳之间相差的时间
*
* $span = self::span(60, 182, 'minutes,seconds'); // array('minutes' => 2, 'seconds' => 2)
* $span = self::span(60, 182, 'minutes'); // 2
*
* @param int $remote timestamp to find the span of
* @param int $local timestamp to use as the baseline
* @param string $output formatting string
* @return string when only a single output is requested
* @return array associative list of all outputs requested
* @from https://github.com/kohana/ohanzee-helpers/blob/master/src/Date.php
*/
public static function span($remote, $local = null, $output = 'years,months,weeks,days,hours,minutes,seconds')
{
// Normalize output
$output = trim(strtolower((string)$output));
if (!$output) {
// Invalid output
return false;
}
// Array with the output formats
$output = preg_split('/[^a-z]+/', $output);
// Convert the list of outputs to an associative array
$output = array_combine($output, array_fill(0, count($output), 0));
// Make the output values into keys
extract(array_flip($output), EXTR_SKIP);
if ($local === null) {
// Calculate the span from the current time
$local = time();
}
// Calculate timespan (seconds)
$timespan = abs($remote - $local);
if (isset($output['years'])) {
$timespan -= self::YEAR * ($output['years'] = (int)floor($timespan / self::YEAR));
}
if (isset($output['months'])) {
$timespan -= self::MONTH * ($output['months'] = (int)floor($timespan / self::MONTH));
}
if (isset($output['weeks'])) {
$timespan -= self::WEEK * ($output['weeks'] = (int)floor($timespan / self::WEEK));
}
if (isset($output['days'])) {
$timespan -= self::DAY * ($output['days'] = (int)floor($timespan / self::DAY));
}
if (isset($output['hours'])) {
$timespan -= self::HOUR * ($output['hours'] = (int)floor($timespan / self::HOUR));
}
if (isset($output['minutes'])) {
$timespan -= self::MINUTE * ($output['minutes'] = (int)floor($timespan / self::MINUTE));
}
// Seconds ago, 1
if (isset($output['seconds'])) {
$output['seconds'] = $timespan;
}
if (count($output) === 1) {
// Only a single output was requested, return it
return array_pop($output);
}
// Return array
return $output;
}
/**
* 格式化 UNIX 时间戳为人易读的字符串
*
* @param int Unix 时间戳
* @param mixed $local 本地时间
*
* @return string 格式化的日期字符串
*/
public static function human($remote, $local = null)
{
$timediff = (is_null($local) || $local ? time() : $local) - $remote;
$chunks = [
[60 * 60 * 24 * 365, 'year'],
[60 * 60 * 24 * 30, 'month'],
[60 * 60 * 24 * 7, 'week'],
[60 * 60 * 24, 'day'],
[60 * 60, 'hour'],
[60, 'minute'],
[1, 'second'],
];
for ($i = 0, $j = count($chunks); $i < $j; $i++) {
$seconds = $chunks[$i][0];
$name = $chunks[$i][1];
if (($count = floor($timediff / $seconds)) != 0) {
break;
}
}
return __("%d {$name}%s ago", $count, ($count > 1 ? 's' : ''));
}
/**
* 获取一个基于时间偏移的Unix时间戳
*
* @param string $type 时间类型,默认为day,可选minute,hour,day,week,month,quarter,year
* @param int $offset 时间偏移量 默认为0,正数表示当前type之后,负数表示当前type之前
* @param string $position 时间的开始或结束,默认为begin,可选前(begin,start,first,front)end
* @param int $year 基准年,默认为null,即以当前年为基准
* @param int $month 基准月,默认为null,即以当前月为基准
* @param int $day 基准天,默认为null,即以当前天为基准
* @param int $hour 基准小时,默认为null,即以当前年小时基准
* @param int $minute 基准分钟,默认为null,即以当前分钟为基准
* @return int 处理后的Unix时间戳
*/
public static function unixtime($type = 'day', $offset = 0, $position = 'begin', $year = null, $month = null, $day = null, $hour = null, $minute = null)
{
$year = is_null($year) ? date('Y') : $year;
$month = is_null($month) ? date('m') : $month;
$day = is_null($day) ? date('d') : $day;
$hour = is_null($hour) ? date('H') : $hour;
$minute = is_null($minute) ? date('i') : $minute;
$position = in_array($position, ['begin', 'start', 'first', 'front']);
switch ($type) {
case 'minute':
$time = $position ? mktime($hour, $minute + $offset, 0, $month, $day, $year) : mktime($hour, $minute + $offset, 59, $month, $day, $year);
break;
case 'hour':
$time = $position ? mktime($hour + $offset, 0, 0, $month, $day, $year) : mktime($hour + $offset, 59, 59, $month, $day, $year);
break;
case 'day':
$time = $position ? mktime(0, 0, 0, $month, $day + $offset, $year) : mktime(23, 59, 59, $month, $day + $offset, $year);
break;
case 'week':
$time = $position ?
mktime(0, 0, 0, $month, $day - date("w", mktime(0, 0, 0, $month, $day, $year)) + 1 - 7 * (-$offset), $year) :
mktime(23, 59, 59, $month, $day - date("w", mktime(0, 0, 0, $month, $day, $year)) + 7 - 7 * (-$offset), $year);
break;
case 'month':
$time = $position ? mktime(0, 0, 0, $month + $offset, 1, $year) : mktime(23, 59, 59, $month + $offset, cal_days_in_month(CAL_GREGORIAN, $month + $offset, $year), $year);
break;
case 'quarter':
$time = $position ?
mktime(0, 0, 0, 1 + ((ceil(date('n', mktime(0, 0, 0, $month, $day, $year)) / 3) + $offset) - 1) * 3, 1, $year) :
mktime(23, 59, 59, (ceil(date('n', mktime(0, 0, 0, $month, $day, $year)) / 3) + $offset) * 3, cal_days_in_month(CAL_GREGORIAN, (ceil(date('n', mktime(0, 0, 0, $month, $day, $year)) / 3) + $offset) * 3, $year), $year);
break;
case 'year':
$time = $position ? mktime(0, 0, 0, 1, 1, $year + $offset) : mktime(23, 59, 59, 12, 31, $year + $offset);
break;
default:
$time = mktime($hour, $minute, 0, $month, $day, $year);
break;
}
return $time;
}
/**
* 获取某个日期属于一年中的第几周
* @param $date
* @return false|int|string
*/
public static function getWeekNum($date='')
{
$date = $date ? $date : date('Y-m-d');
$year = date('Y', strtotime($date));
$yearBegin = strtotime($year.'-1-1');
$month = intval(date('m', strtotime($date)));
if( date('W', $yearBegin) == 1 ){
return date('W', strtotime($date));
}else if($month == 1 && date('W', $yearBegin) > 50){
return 1;
}else{
return date('W', strtotime($date)) + 1;
}
}
/**
* 根据日期,获取周信息
* @param $now string 日期,举例: 2021-01-01
* @return array
*/
public static function getWeekInfo($now)
{
$str = [];
//$first =1 表示每周星期一为开始日期 0表示每周日为开始日期
$str['year'] = date('Y', strtotime($now));
$first = 1;
//当日在整年中的第几周
$str['week'] = date('W', strtotime($now));
//获取当前周的第几天 周日是 0 周一到周六是 1 - 6
$w = date('w', strtotime($now));
//获取本周开始日期,如果$w是0,则表示周日,减去 6 天
$weekStart = date('Y-m-d', strtotime("$now -" . ($w ? $w - $first : 6) . ' days'));
$str['week_start'] = $weekStart;
//本周结束日期
$weekEnd = date('Y-m-d', strtotime("$weekStart +6 days"));
$str['week_end'] = $weekEnd;
return $str;
}
/**
* 某去某年第几周的周信息
* @param $year
* @param int $week
* @return mixed
*/
public static function weekDay($year, $week=1){
$yearStart = mktime(0,0,0,1,1, $year);
$yearEnd = mktime(0,0,0,12,31, $year);
$start = $yearStart;//把第一天做为第一周的开始
$end = strtotime('+1 sunday', $yearStart);//把第一个周日作为第一周的结束
$lastStart = strtotime('-1 monday', $yearStart);//把最后一个周一作为最后一周的开始
$lastEnd = $yearEnd;//把最后一天作为最后一周的结束
$totalWeekNum = intval(date('W', $yearStart));
if($week == 1){
$weekday['begin'] = $start;//把第一天做为第一周的开始
$weekday['begin_date'] = date('Y-m-d', $start);//把第一天做为第一周的开始
$weekday['end'] = $end;//把第一个周日作为第一周的结束
$weekday['end_date'] = date('Y-m-d', $end);//把第一个周日作为第一周的结束
}else if($week == $totalWeekNum){
$weekday['begin'] = $lastStart;//把最后一个周一作为最后一周的开始
$weekday['begin_date'] = date('Y-m-d', $lastStart);//把第一天做为第一周的开始
$weekday['end'] = $lastEnd;//把第一个周日作为第一周的结束
$weekday['end_date'] = date('Y-m-d', $lastEnd);//把第一个周日作为第一周的结束
}else if($week > 1 && $week < $totalWeekNum){
$weekday['begin'] = strtotime('+' . ($week-1) . ' monday', $end);
$weekday['begin_date'] = date('Y-m-d', $weekday['begin']);
$weekday['end'] = strtotime('+' . ($week-1) . ' sunday', $end + 24 * 60 * 60);
$weekday['end_date'] = date('Y-m-d', $weekday['end']);
}else{
return false;
}
$weekday['week'] = $week;
$weekday['totalWeekNum'] = $totalWeekNum;
return $weekday;
}
/**
* 友好的时间显示
*
* @param int $sTime 待显示的时间
* @param string $type 类型. normal | mohu | full | ymd | other
* @param string $alt 已失效
* @return string
*/
public static function friendlyDate($sTime,$type = 'default',$alt = 'false') {
//sTime=源时间,cTime=当前时间,dTime=时间差
$cTime = time();
$dTime = $cTime - $sTime;
$dDay = intval(date("z",$cTime)) - intval(date("z",$sTime));
//$dDay = intval($dTime/3600/24);
$dYear = intval(date("Y",$cTime)) - intval(date("Y",$sTime));
//normal:n秒前,n分钟前,n小时前,日期
if($type=='normal'){
if($dTime == 0){
return '现在';
}elseif( $dTime < 60 ){
return $dTime."秒前";
}elseif( $dTime < 3600 ){
return intval($dTime/60)."分钟前";
//今天的数据.年份相同.日期相同.
}elseif( $dYear==0 && $dDay == 0 ){
//return intval($dTime/3600)."小时前";
return '今天'.date('H:i',$sTime);
}elseif($dYear==0){
return date("m月d日 H:i",$sTime);
}else{
return date("Y-m-d H:i",$sTime);
}
}elseif($type=='mohu'){
if( $dTime < 60 ){
return $dTime."秒前";
}elseif( $dTime < 3600 ){
return intval($dTime/60)."分钟前";
}elseif( $dTime >= 3600 && $dDay == 0 ){
return intval($dTime/3600)."小时前";
}elseif( $dDay > 0 && $dDay<=7 ){
return intval($dDay)."天前";
}elseif( $dDay > 7 && $dDay <= 30 ){
return intval($dDay/7) . '周前';
}elseif( $dDay > 30 ){
return intval($dDay/30) . '个月前';
}
//full: Y-m-d , H:i:s
}elseif($type=='full'){
return date("Y-m-d , H:i:s",$sTime);
}elseif($type=='ymd'){
return date("Y-m-d",$sTime);
}else{
if( $dTime < 60 ){
return $dTime."秒前";
}elseif( $dTime < 3600 ){
return intval($dTime/60)."分钟前";
}elseif( $dTime >= 3600 && $dDay == 0 ){
return intval($dTime/3600)."小时前";
}elseif($dYear==0){
return date("Y-m-d H:i:s",$sTime);
}else{
return date("Y-m-d H:i:s",$sTime);
}
}
}
}
+171
View File
@@ -0,0 +1,171 @@
<?php
namespace laytp\library;
use DateTime;
use DateTimeZone;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use think\facade\Config;
/**
* 文件夹和文件处理类
*/
class DirFile
{
/**
* 创建目录
* @param $path
* @param int $mode
* @return bool
*/
public static function createDir($path, $mode = 0777)
{
if (is_dir($path)) {
return true;
} else {
//如果目录不存在,则递归创建
if (mkdir($path, $mode, true)) {
return true;
} else {
return false;
}
}
}
/**
* 循环遍历目录下的文件和文件夹并输出
* @param $pathName
* @param array $filterDir
* @param array $filterFile 需要过滤的文件名,*表示所有文件不展示
* @param array $selected 设置是否已经选中
* @param array $output
* @return array|null
*/
public static function recurDir($pathName, $filterDir = [], $filterFile = [], $selected = [], $output = ['id'=>'id', 'fullName' => 'fullName', 'baseName' => 'baseName'])
{
//将结果保存在result变量中
$result = [];
$temp = [];
//判断传入的变量是否是目录
if (!is_dir($pathName) || !is_readable($pathName)) {
return null;
}
//取出目录中的文件和子目录名,使用scandir函数
$allFiles = scandir($pathName);
//遍历他们
foreach ($allFiles as $fileName) {
if (in_array($fileName, ['.', '..'])) {
continue;
}
//路径加文件名
if (mb_substr($pathName, -1, 1) == DS) {
$fullName = $pathName . $fileName;
} else {
$fullName = $pathName . DS . $fileName;
}
$baseName = basename($fileName);
//如果是目录的话就继续遍历这个目录
if (is_dir($fullName)) {
if (in_array($fullName, $filterDir)) {
continue;
}
//将这个目录中的文件信息存入到数组中
$res = [
$output['baseName'] => $baseName,
$output['fullName'] => $fullName,
'type' => 'dir',
'children' => self::recurDir($fullName, $filterDir, $filterFile, $selected, $output),
];
if (in_array($fullName, $selected)) {
$res['state'] = ['selected' => true];
} else {
$res['state'] = ['selected' => false];
}
$apiDirName = Config::get('apidirname');
if(isset($apiDirName[$fullName])){
$res['name'] = $apiDirName[$fullName];
}else{
$res['name'] = '';
}
$res['id'] = $fullName;
$result[] = $res;
} else {
if($filterFile != '*'){
if (in_array($fullName, $filterFile)) {
continue;
}
//如果是文件就先存入临时变量
//将这个目录中的文件信息存入到数组中
$tem = [$output['id'] => $fullName, $output['baseName'] => $baseName, $output['fullName'] => $fullName, 'type' => 'file', 'state' => ['selected' => false]];
if (in_array($fullName, $selected)) {
$tem['state'] = ['selected' => true];
} else {
$tem['state'] = ['selected' => false];
}
$temp[] = $tem;
}
}
}
//取出文件
if ($temp) {
foreach ($temp as $f) {
$result[] = $f;
}
}
return $result;
}
/**
* 删除文件夹
* @param string $dirname 目录
* @param bool $withself 是否删除自身
* @return boolean
*/
public static function rmDirs($dirname, $withself = true)
{
if (!is_dir($dirname)) {
return false;
}
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dirname, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($files as $fileinfo) {
$todo = ($fileinfo->isDir() ? 'rmdir' : 'unlink');
$todo($fileinfo->getRealPath());
}
if ($withself) {
@rmdir($dirname);
}
return true;
}
/**
* 复制文件夹
* @param string $source 源文件夹
* @param string $dest 目标文件夹
*/
public static function copyDirs($source, $dest)
{
if (!is_dir($dest)) {
mkdir($dest, 0755, true);
}
foreach (
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
) as $item
) {
if ($item->isDir()) {
$sontDir = $dest . DS . $iterator->getSubPathName();
if (!is_dir($sontDir)) {
mkdir($sontDir, 0755, true);
}
} else {
copy($item, $dest . DS . $iterator->getSubPathName());
}
}
}
}
+191
View File
@@ -0,0 +1,191 @@
<?php
namespace laytp\library;
use PHPMailer\PHPMailer\PHPMailer;
use think\facade\Config;
class Email
{
/**
* 单例对象
*/
protected static $instance;
/**
* phpmailer对象
*/
protected $mail = [];
/**
* 错误内容
*/
protected $_error = '';
/**
* 默认配置
*/
public $options = [
'charset' => 'utf-8', //编码格式
'debug' => 0, //调式模式
];
/**
* 初始化
* @access public
* @param array $options 参数
* @return Email
*/
public static function instance($options = [])
{
if (is_null(self::$instance)) {
self::$instance = new static($options);
}
return self::$instance;
}
/**
* 构造函数
* @param array $options
*/
public function __construct($options = [])
{
if ($config = Config::get('laytp.email')) {
$this->options = array_merge($this->options, $config);
}
$this->options = array_merge($this->options, $options);
$securArr = [1 => 'tls', 2 => 'ssl'];
$this->mail = new PHPMailer(true);
$this->mail->CharSet = $this->options['charset'];
$this->mail->SMTPDebug = false;
$this->mail->isSMTP();
$this->mail->SMTPAuth = true;
$this->mail->Host = $this->options['smtp_host'];
$this->mail->Username = $this->options['smtp_user'];
$this->mail->Password = $this->options['smtp_password'];
$this->mail->SMTPSecure = isset($securArr[$this->options['verify_type']]) ? $securArr[$this->options['verify_type']] : '';
$this->mail->Port = $this->options['smtp_port'];
//设置发件人
$this->from($this->options['from'], $this->options['from_name']);
}
/**
* 设置邮件主题
* @param string $subject
* @return $this
*/
public function subject($subject)
{
$this->options['subject'] = $subject;
return $this;
}
/**
* 设置发件人
* @param string $email
* @param string $name
* @return $this
*/
public function from($email, $name = '')
{
$this->options['from'] = $email;
$this->options['from_name'] = $name;
return $this;
}
/**
* 设置收件人
* @param string $email
* @param string $name
* @return $this
*/
public function to($email, $name = '')
{
$this->options['to'] = $email;
$this->options['to_name'] = $name;
return $this;
}
/**
* 设置邮件正文
* @param string $body
* @param boolean $ishtml
* @return $this
*/
public function message($body, $ishtml = true)
{
$this->options['body'] = $body;
$this->options['ishtml'] = $ishtml;
return $this;
}
/**
* 获取最后产生的错误
* @return string
*/
public function getError()
{
return $this->_error;
}
/**
* 设置错误
* @param string $error 信息信息
*/
protected function setError($error)
{
$this->_error = $error;
}
/**
* 发送邮件
* @return boolean
*/
public function send()
{
$result = false;
switch ($this->options['send_type']) {
case 'smtp':
//使用phpmailer发送
$this->mail->setFrom($this->options['from'], $this->options['from_name']);
$this->mail->addAddress($this->options['to'], $this->options['to_name']);
$this->mail->Subject = $this->options['subject'];
if ($this->options['ishtml']) {
$this->mail->msgHTML($this->options['body']);
} else {
$this->mail->Body = $this->options['body'];
}
try {
$result = $this->mail->send();
if ($result) {
return $result;
} else {
$this->setError($this->mail->ErrorInfo);
}
} catch (\phpmailerException $e) {
$this->setError($e->getMessage());
return false;
}
break;
case 'mail':
//使用mail方法发送邮件
$headers = 'MIME-Version: 1.0' . "\r\n";
$headers .= "Content-type: text/html; charset=" . $this->options['charset'] . "\r\n";
$headers .= "To: {$this->options['to_name']} <{$this->options['to']}>\r\n"; //收件人
$headers .= "From: {$this->options['from_name']} <{$this->options['from']}>\r\n"; //发件人
$result = mail($this->options['to'], $this->options['subject'], $this->options['body'], $headers);
$this->setError($result ? '' : error_get_last()['message']);
break;
default:
//邮件功能已关闭
$this->setError('邮件功能已关闭');
break;
}
return $result;
}
}
+206
View File
@@ -0,0 +1,206 @@
<?php
namespace laytp\library;
/**
* Http请求工具类
*/
class Http
{
/**
* 发送一个POST请求
* @param string $url 请求URL
* @param array $params 请求参数
* @param array $options 扩展参数
* @return mixed|string
*/
public static function post($url, $params = [], $options = [])
{
$req = self::sendRequest($url, $params, 'POST', $options);
return $req['ret'] ? $req['msg'] : '';
}
/**
* 发送一个GET请求
* @param string $url 请求URL
* @param array $params 请求参数
* @param array $options 扩展参数
* @return mixed|string
*/
public static function get($url, $params = [], $options = [])
{
$req = self::sendRequest($url, $params, 'GET', $options);
return $req['ret'] ? $req['msg'] : '';
}
/**
* CURL发送Request请求,含POST和REQUEST
* @param string $url 请求的链接
* @param mixed $params 传递的参数
* @param string $method 请求的方法
* @param mixed $options CURL的参数
* @return array
*/
public static function sendRequest($url, $params = [], $method = 'POST', $options = [])
{
$method = strtoupper($method);
$protocol = substr($url, 0, 5);
$query_string = is_array($params) ? http_build_query($params) : $params;
$ch = curl_init();
$defaults = [];
if ('GET' == $method) {
$geturl = $query_string ? $url . (stripos($url, "?") !== false ? "&" : "?") . $query_string : $url;
$defaults[CURLOPT_URL] = $geturl;
} else {
$defaults[CURLOPT_URL] = $url;
if ($method == 'POST') {
$defaults[CURLOPT_POST] = 1;
} else {
$defaults[CURLOPT_CUSTOMREQUEST] = $method;
}
$defaults[CURLOPT_POSTFIELDS] = $query_string;
}
$defaults[CURLOPT_HEADER] = false;
$defaults[CURLOPT_USERAGENT] = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.98 Safari/537.36";
$defaults[CURLOPT_FOLLOWLOCATION] = true;
$defaults[CURLOPT_RETURNTRANSFER] = true;
$defaults[CURLOPT_CONNECTTIMEOUT] = 3;
$defaults[CURLOPT_TIMEOUT] = 3;
// 方便外部设置CURLOPT_HTTPHEADER,做此修改
if(isset($options['CURLOPT_HTTPHEADER']) && is_array($options['CURLOPT_HTTPHEADER'])){
curl_setopt($ch, CURLOPT_HTTPHEADER, $options['CURLOPT_HTTPHEADER']);
unset($options['CURLOPT_HTTPHEADER']);
}
// disable 100-continue
// curl_setopt($ch, CURLOPT_HTTPHEADER, ['Expect:']);
if ('https' == $protocol) {
$defaults[CURLOPT_SSL_VERIFYPEER] = false;
$defaults[CURLOPT_SSL_VERIFYHOST] = false;
}
curl_setopt_array($ch, (array)$options + $defaults);
$ret = curl_exec($ch);
$err = curl_error($ch);
if (false === $ret || !empty($err)) {
$errno = curl_errno($ch);
$info = curl_getinfo($ch);
curl_close($ch);
return [
'ret' => false,
'errno' => $errno,
'msg' => $err,
'info' => $info,
];
}
curl_close($ch);
return [
'ret' => true,
'msg' => $ret,
];
}
/**
* 异步发送一个请求
* @param string $url 请求的链接
* @param mixed $params 请求的参数
* @param string $method 请求的方法
* @return boolean TRUE
*/
public static function sendAsyncRequest($url, $params = [], $method = 'POST')
{
$method = strtoupper($method);
$method = $method == 'POST' ? 'POST' : 'GET';
//构造传递的参数
if (is_array($params)) {
$post_params = [];
foreach ($params as $k => &$v) {
if (is_array($v)) {
$v = implode(',', $v);
}
$post_params[] = $k . '=' . urlencode($v);
}
$post_string = implode('&', $post_params);
} else {
$post_string = $params;
}
$parts = parse_url($url);
//构造查询的参数
if ($method == 'GET' && $post_string) {
$parts['query'] = isset($parts['query']) ? $parts['query'] . '&' . $post_string : $post_string;
$post_string = '';
}
$parts['query'] = isset($parts['query']) && $parts['query'] ? '?' . $parts['query'] : '';
//发送socket请求,获得连接句柄
$fp = fsockopen($parts['host'], isset($parts['port']) ? $parts['port'] : 80, $errno, $errstr, 3);
if (!$fp) {
return false;
}
//设置超时时间
stream_set_timeout($fp, 3);
$out = "{$method} {$parts['path']}{$parts['query']} HTTP/1.1\r\n";
$out .= "Host: {$parts['host']}\r\n";
$out .= "Content-Type: application/x-www-form-urlencoded\r\n";
$out .= "Content-Length: " . strlen($post_string) . "\r\n";
$out .= "Connection: Close\r\n\r\n";
if ($post_string !== '') {
$out .= $post_string;
}
fwrite($fp, $out);
//不用关心服务器返回结果
//echo fread($fp, 1024);
fclose($fp);
return true;
}
/**
* 发送文件到客户端
* @param string $file
* @param bool $delaftersend
* @param bool $exitaftersend
*/
public static function sendToBrowser($file, $delaftersend = true, $exitaftersend = true)
{
if (file_exists($file) && is_readable($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment;filename = ' . basename($file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check = 0, pre-check = 0');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
if ($delaftersend) {
unlink($file);
}
if ($exitaftersend) {
exit;
}
}
}
/**
* 获取远程https的图片保存到本地
* @param $imgUrl string 图片地址
* @param $saveToPath string 本地保存路径
*/
public static function saveImageFromHttps($imgUrl, $saveToPath){
$arrContextOptions = array(
"ssl"=>array(
"verify_peer"=>false,
"verify_peer_name"=>false,
),
);
$file_contents = file_get_contents($imgUrl, false, stream_context_create($arrContextOptions));
file_put_contents($saveToPath, $file_contents);
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace laytp\library;
/**
* Class Output
*/
class Output extends \think\console\Output
{
protected $message = [];
public function __construct($driver = 'console')
{
parent::__construct($driver);
}
protected function block($style, $message)
{
$this->message[] = $message;
}
public function getMessage()
{
return $this->message;
}
}
+183
View File
@@ -0,0 +1,183 @@
<?php
namespace laytp\library;
use think\exception\HttpException;
use think\facade\Config;
use think\facade\Middleware;
use think\facade\Request;
use think\Response;
use think\Route;
class PluginRoute extends Route
{
protected $middleware;
protected $actionName;
/**
* 插件路由
* 路由访问规则,http(s)://yourDomain/plugin/[插件名称]/[插件controller目录下的类名,多级目录以.号分割]/[方法名]/[参数列表]
* @param null $plugin
* @return mixed
*/
public function execute($plugin = null)
{
$this->middleware = Middleware::instance();
$this->request = Request::instance();
$url = $this->request->url();
$urlArr = array_filter(explode('/', $url));
$controller = isset($urlArr[3]) ? $urlArr[3] : 'Index';
$plugin = $plugin ? trim(strtolower($plugin)) : $plugin;
if (!defined('LT_PLUGIN')) {
define('LT_PLUGIN', $plugin);
}
$controller = $controller ? str_replace('.', '\\', trim($controller)) : 'Index';
$classAndAction = $this->getPluginClassAndAction($plugin, $controller);
$this->request->setController($controller)->setAction($classAndAction['action']);
$class = $classAndAction['class'];
$action = $classAndAction['action'];
$instance = app()->make($class, [], true);
$this->actionName = $action;
try {
$this->registerControllerMiddleware($instance);
} catch (\ReflectionException $e) {
throw new HttpException(500, $e->getMessage());
}
return $this->middleware->pipeline('controller')
->send($this->request)
->then(function () use ($instance) {
// 获取当前操作名
$suffix = Config::get('route.action_suffix');
$action = $this->actionName . $suffix;
if (is_callable([$instance, $action])) {
$vars = $this->request->param();
try {
$reflect = new \ReflectionMethod($instance, $action);
// 严格获取当前操作方法名
$actionName = $reflect->getName();
if ($suffix) {
$actionName = substr($actionName, 0, -strlen($suffix));
}
$this->request->setAction($actionName);
} catch (\Exception $e) {
$reflect = new \ReflectionMethod($instance, '__call');
$vars = [$action, $vars];
$this->request->setAction($action);
}
} else {
// 操作不存在
throw new HttpException(404, 'method not exists:' . get_class($instance) . '->' . $action . '()');
}
$data = $this->app->invokeReflectMethod($instance, $reflect, $vars);
return $this->autoResponse($data);
});
}
/**
* 得到插件完整类名
* @param $plugin
* @param $controller
* @return array
*/
public function getPluginClassAndAction($plugin, $controller)
{
$request = Request::instance();
$url = $request->url();
$urlArr = array_filter(explode('/', $url));
$controllerArr = explode("\\", $controller);
$controllerArr[count($controllerArr) - 1] = ucfirst($controllerArr[count($controllerArr) - 1]);
$controller = implode("\\", $controllerArr);
$class = 'plugin\\' . $plugin . '\\controller\\' . $controller;
if (!class_exists($class)) {
throw new HttpException(404, $class . '类不存在');
}
$class = 'plugin\\' . $plugin . '\\controller\\' . $controller;
if (isset($urlArr[4])) {
$action_param = explode('?', $urlArr[4]);
$action = $action_param[0];
} else {
$action = 'index';
}
$action = $action ? str_replace('.' . Config::get("route.url_html_suffix"), '', trim($action)) : 'index';
if (!method_exists($class, $action)) {
throw new HttpException(404, $class . '->' . $action . '()' . '方法不存在');
}
return ['class' => $class, 'action' => $action];
}
/**
* 使用反射机制注册控制器中间件
* @param $controller
* @throws \ReflectionException
*/
protected function registerControllerMiddleware($controller): void
{
$class = new \ReflectionClass($controller);
if ($class->hasProperty('middleware')) {
$reflectionProperty = $class->getProperty('middleware');
$reflectionProperty->setAccessible(true);
$middlewares = $reflectionProperty->getValue($controller);
foreach ($middlewares as $key => $val) {
if (!is_int($key)) {
if (isset($val['only']) && !in_array($this->request->action(true), array_map(function ($item) {
return strtolower($item);
}, is_string($val['only']) ? explode(",", $val['only']) : $val['only']))) {
continue;
} elseif (isset($val['except']) && in_array($this->request->action(true), array_map(function ($item) {
return strtolower($item);
}, is_string($val['except']) ? explode(',', $val['except']) : $val['except']))) {
continue;
} else {
$val = $key;
}
}
if (is_string($val) && strpos($val, ':')) {
$val = explode(':', $val);
if (count($val) > 1) {
$val = [$val[0], array_slice($val, 1)];
}
}
$this->middleware->controller($val);
}
}
}
protected function autoResponse($data): Response
{
if ($data instanceof Response) {
$response = $data;
} elseif (!is_null($data)) {
// 默认自动识别响应输出类型
$type = $this->request->isJson() ? 'json' : 'html';
$response = Response::create($data, $type);
} else {
$data = ob_get_clean();
$content = false === $data ? '' : $data;
$status = '' === $content && $this->request->isJson() ? 204 : 200;
$response = Response::create($content, 'html', $status);
}
return $response;
}
}
+167
View File
@@ -0,0 +1,167 @@
<?php
namespace laytp\library;
/**
* 随机生成类
*/
class Random
{
/**
* 生成数字和字母
*
* @param int $len 长度
* @return string
*/
public static function alnum($len = 6)
{
return self::build('alnum', $len);
}
/**
* 仅生成字符
*
* @param int $len 长度
* @return string
*/
public static function alpha($len = 6)
{
return self::build('alpha', $len);
}
/**
* 生成指定长度的随机数字
*
* @param int $len 长度
* @return string
*/
public static function numeric($len = 4)
{
return self::build('numeric', $len);
}
/**
* 数字和字母组合的随机字符串
*
* @param int $len 长度
* @return string
*/
public static function nozero($len = 4)
{
return self::build('nozero', $len);
}
/**
* 能用的随机数生成
* @param string $type 类型 alpha/alnum/numeric/nozero/unique/md5/encrypt/sha1
* @param int $len 长度
* @return string
*/
public static function build($type = 'alnum', $len = 8)
{
switch ($type) {
case 'alpha':
case 'alnum':
case 'numeric':
case 'nozero':
switch ($type) {
case 'alpha':
$pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
break;
case 'alnum':
$pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
break;
case 'numeric':
$pool = '0123456789';
break;
case 'nozero':
$pool = '123456789';
break;
}
return substr(str_shuffle(str_repeat($pool, ceil($len / strlen($pool)))), 0, $len);
case 'unique':
case 'md5':
return md5(uniqid(mt_rand()));
case 'encrypt':
case 'sha1':
return sha1(uniqid(mt_rand(), true));
}
}
/**
* 根据数组元素的概率获得键名
*
* @param array $ps array('p1'=>20, 'p2'=>30, 'p3'=>50);
* @param int $num 默认为1,即随机出来的数量
* @param bool $unique 默认为true,即当num>1时,随机出的数量是否唯一
* @return mixed 当num为1时返回键名,反之返回一维数组
*/
public static function lottery($ps, $num = 1, $unique = true)
{
if (!$ps) {
return $num == 1 ? '' : [];
}
if ($num >= count($ps) && $unique) {
$res = array_keys($ps);
return $num == 1 ? $res[0] : $res;
}
$max_exp = 0;
$res = [];
foreach ($ps as $key => $value) {
$value = substr($value, 0, stripos($value, ".") + 6);
$exp = strlen(strchr($value, '.')) - 1;
if ($exp > $max_exp) {
$max_exp = $exp;
}
}
$pow_exp = pow(10, $max_exp);
if ($pow_exp > 1) {
reset($ps);
foreach ($ps as $key => $value) {
$ps[$key] = $value * $pow_exp;
}
}
$pro_sum = array_sum($ps);
if ($pro_sum < 1) {
return $num == 1 ? '' : [];
}
for ($i = 0; $i < $num; $i++) {
$rand_num = mt_rand(1, $pro_sum);
reset($ps);
foreach ($ps as $key => $value) {
if ($rand_num <= $value) {
break;
} else {
$rand_num -= $value;
}
}
if ($num == 1) {
$res = $key;
break;
} else {
$res[$i] = $key;
}
if ($unique) {
$pro_sum -= $value;
unset($ps[$key]);
}
}
return $res;
}
/**
* 获取全球唯一标识
* @return string
*/
public static function uuid()
{
$chars = md5(uniqid(mt_rand(), true));
$uuid = substr ( $chars, 0, 8 ) . '-'
. substr ( $chars, 8, 4 ) . '-'
. substr ( $chars, 12, 4 ) . '-'
. substr ( $chars, 16, 4 ) . '-'
. substr ( $chars, 20, 12 );
return $uuid ;
}
}
+57
View File
@@ -0,0 +1,57 @@
<?php
namespace laytp\library;
use think\facade\Cache;
/**
* Redis锁
*/
class Redis
{
/**
* 得到一个redis锁,循环获取锁,直到获取到锁为止
* @param $name string 锁名称
* @param $ttl int 锁存在的时间,单位秒,默认60秒
* @return bool
*/
public static function getLock($name, $ttl=60)
{
set_time_limit(0);
$redis = Cache::store('redis')->handler();
while(true){
if($redis->set($name, 1, ['NX', 'EX'=>$ttl])){
break;
}
}
return true;
}
/**
* 得到一个redis锁,仅尝试获取一次
*
* @param $name string 锁名称
* @param $ttl int 锁存在的时间,单位秒,默认60秒
* @return bool
*/
public static function getOnceLock($name, $ttl=60)
{
set_time_limit(0);
$redis = Cache::store('redis')->handler();
if($redis->set($name, 1, ['NX', 'EX'=>$ttl])){
return true;
}
return false;
}
/**
* 删除一个redis锁
* @param $name string 锁名称
* @return bool
*/
public static function delLock($name)
{
$redis = Cache::store('redis')->handler();
$redis->del($name);
return true;
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
namespace laytp\library;
/**
* 字符串处理
*/
class Str
{
/**
* 下划线转驼峰
* @param $str
* @return string|string[]|null
*/
public static function underlineToCamel($str)
{
$str = preg_replace_callback('/([-_]+([a-z]{1}))/i', function ($matches) {
return strtoupper($matches[2]);
}, $str);
return $str;
}
// 生成密码
public static function createPassword($password)
{
return password_hash(md5($password), PASSWORD_DEFAULT);
}
// 检验密码
public static function checkPassword($password, $passwordHash)
{
return password_verify(md5($password), $passwordHash);
}
}
+159
View File
@@ -0,0 +1,159 @@
<?php
namespace laytp\library;
use laytp\library\token\Driver;
use think\facade\Config;
use think\facade\Log;
/**
* Token操作类
*/
class Token
{
/**
* @var array Token的实例
*/
public static $instance = [];
/**
* @var object 操作句柄
*/
public static $handler;
/**
* 连接Token驱动
* @access public
* @param array $options 配置数组
* @param bool|string $name Token连接标识 true 强制重新连接
* @return Driver
*/
public static function connect(array $options = [], $name = false)
{
$type = !empty($options['type']) ? $options['type'] : 'File';
if (false === $name) {
$name = md5(serialize($options));
}
if (true === $name || !isset(self::$instance[$name])) {
$class = false === strpos($type, '\\') ?
'laytp\\library\\token\\driver\\' . ucwords($type) :
$type;
// 记录初始化信息
Config::get('app.app_debug') && Log::record('[ TOKEN ] INIT ' . $type, 'info');
if (true === $name) {
return new $class($options);
}
self::$instance[$name] = new $class($options);
}
return self::$instance[$name];
}
/**
* 自动初始化Token
* @access public
* @param array $options 配置数组
* @return Driver
*/
public static function init(array $options = [])
{
if (is_null(self::$handler)) {
if (empty($options) && 'complex' == Config::get('token.type')) {
$default = Config::get('token.default');
// 获取默认Token配置,并连接
$options = Config::get('token.' . $default['type']) ?: $default;
} elseif (empty($options)) {
$options = Config::get('token');
}
self::$handler = self::connect($options);
}
return self::$handler;
}
/**
* 判断Token是否可用(check别名)
* @access public
* @param string $token Token标识
* @param int $userId 用户ID
* @return bool
*/
public static function has($token, $userId)
{
return self::check($token, $userId);
}
/**
* 判断Token是否可用
* @param string $token Token标识
* @param int $userId 用户ID
* @return bool
*/
public static function check($token, $userId)
{
return self::init()->check($token, $userId);
}
/**
* 读取Token
* @access public
* @param string $token Token标识
* @param mixed $default 默认值
* @return mixed
*/
public static function get($token, $default = false)
{
return self::init()->get($token, $default);
}
/**
* 写入Token
* @access public
* @param string $token Token标识
* @param mixed $userId 存储数据
* @param int|null $expire 有效时间 0为永久
* @return boolean
*/
public static function set($token, $userId, $expire = null)
{
return self::init()->set($token, $userId, $expire);
}
/**
* 删除Token(delete别名)
* @access public
* @param string $token Token标识
* @return boolean
*/
public static function rm($token)
{
return self::delete($token);
}
/**
* 删除Token
* @param string $token 标签名
* @return bool
*/
public static function delete($token)
{
return self::init()->delete($token);
}
/**
* 清除Token
* @access public
* @param int $userId Token标记
* @return boolean
*/
public static function clear($userId = null)
{
return self::init()->clear($userId);
}
}
+221
View File
@@ -0,0 +1,221 @@
<?php
namespace laytp\library;
/**
* 通用树型类 - laytp极速后台框架
* @Author: JunStar
* @Version: 1.0.0
* @Date: 2022-1-7 21:48:38
* @LastModified by: JunStar
* @LastModified time: 2022-1-8 21:45:22
*/
class Tree
{
protected static $instance;
/**
* 生成树型结构所需要的2维数组
* @var array
*/
public $map = [];
public $mapName = 'id';
public $pidName = 'pid';
public $childName = 'children';
/**
* 初始化
* @access public
* @return Tree
*/
public static function instance()
{
if (is_null(self::$instance)) {
self::$instance = new static();
}
return self::$instance;
}
/**
* 初始化方法
* 注意,参数一定要是数组,数据库select()->toArray();否则调用getRootTree会内存溢出
* @param array 2维数组,例如:
* array(
* 1 => array('id'=>'1','pid'=>0,'name'=>'一级栏目一'),
* 2 => array('id'=>'2','pid'=>0,'name'=>'一级栏目二'),
* 3 => array('id'=>'3','pid'=>1,'name'=>'二级栏目一'),
* 4 => array('id'=>'4','pid'=>1,'name'=>'二级栏目二'),
* 5 => array('id'=>'5','pid'=>2,'name'=>'二级栏目三'),
* 6 => array('id'=>'6','pid'=>3,'name'=>'三级栏目一'),
* 7 => array('id'=>'7','pid'=>3,'name'=>'三级栏目二')
* )
* @return $this
*/
public function init($arr = [])
{
$map = [];
//生成以id为key的map
foreach ($arr as &$it){
$map[$it['id']] = &$it;
}
$this->map = $map;
return $this;
}
/**
* 获取所有根树
* @return array
*/
public function getRootTrees()
{
$map = $this->map;
$res = [];
$rootTree = [];
foreach ($map as $id => &$item) {
// 获取出每一条数据的父id
$pid = &$item[$this->pidName];
// 如果在map中没有设置过当前$item的pid索引, 说明$item是根节点
if(!isset($map[$pid])){
//将根节点的item的引用保存到$res中
$res[] = &$item;
}else{
// 如果在map中有设置过当前$item的pid索引, 则将当前item加入到他父亲的叶子节点中
// 此处关键需要理解内存地址引用,修改了$pItem,其实就是修改了$map[$pid]的值
$pItem = &$map[$pid];
$pItem[$this->childName][] = &$item;
}
}
// 循环res,将pid不为0的过滤掉,剩下的就是所有的根树
if($res){
foreach($res as $k=>$value){
if($value[$this->pidName] == 0){
$rootTree[] = $value;
}
}
}
return $rootTree;
}
/**
* 获取所有树
*/
public function getTrees(){
$map = $this->map;
$res = [];
foreach ($map as $id => &$item) {
// 获取出每一条数据的父id
$pid = &$item[$this->pidName];
// 如果在map中没有设置过当前$item的pid索引, 说明$item是根节点
if(!isset($map[$pid])){
//将根节点的item的引用保存到$res中
$res[] = &$item;
}else{
// 如果在map中有设置过当前$item的pid索引, 则将当前item加入到他父亲的叶子节点中
// 此处关键需要理解内存地址引用,修改了$pItem,其实就是修改了$map[$pid]的值
$pItem = &$map[$pid];
$pItem[$this->childName][] = &$item;
}
}
return $res;
}
/**
* 获取某棵子树
* 思路:
* 1.先获取当前节点的所有父级节点
* 2.组合一个根树的map,每个key都是id值,getRootTree是自增的
* 3.使用所有父级节点,直接在根树的map中索引得到子树
*/
/**
* 获取某ID的所有子级ID
* @param $ids string|array 要查询子级的ID
* @param bool $withSelf 返回的结果是否包含$ids
* @return array
*/
public function getChildIds($ids, $withSelf=true){
$map = $this->map;
$res = [];//最终结果
if(!is_array($ids)){
$ids = explode(',', $ids);
$sourceIds = $ids;
}else{
$sourceIds = $ids;
}
$state = true;
while($state){
$otherIds = [];
foreach ($ids as $id) {
foreach ($map as $key => $value) {
if($value[$this->pidName] == $id){
$res[] = $value[$this->mapName];//找到我的下级立即添加到最终结果中
$otherIds[] = $value[$this->mapName];//将我的下级id保存起来用来下轮循环他的下级
}
}
}
if(!$otherIds){
$state = false;
}else{
$ids = $otherIds;//foreach中找到的我的下级集合,用来下次循环
}
}
if($withSelf){
foreach($sourceIds as $sId){
$res[] = intval($sId);
}
}
return $res;
}
/**
* 获取某ID的所有父级ID
* @param $ids string|array 要查询父级的ID
* @param bool $withSelf 返回的结果是否包含$id
* @return array
*/
public function getParentIds($ids, $withSelf=true){
$map = $this->map;
$res = [];//最终结果
if(!is_array($ids)){
$ids = explode(',', $ids);
$sourceIds = $ids;
}else{
$sourceIds = $ids;
}
$state = true;
while($state){
$otherIds = [];
foreach ($ids as $id) {
foreach ($map as $key => $value) {
if($value[$this->mapName] == $map[$id][$this->pidName]){
$res[] = $value[$this->mapName];//找到我的上级立即添加到最终结果中
$otherIds[] = $value[$this->mapName];//将我的上级id保存起来用来下次轮循环他的下级
}
}
}
if(!$otherIds){
$state = false;
}else{
$ids = $otherIds;//foreach中找到的我的上级集合,用来下次循环
}
}
if($withSelf){
foreach($sourceIds as $sId){
$res[] = intval($sId);
}
}
return $res;
}
}
+261
View File
@@ -0,0 +1,261 @@
<?php
namespace laytp\library;
use app\model\Files;
use app\service\ConfServiceFacade;
use laytp\traits\Error;
use think\facade\Config;
use think\facade\Env;
/**
* 上传文件域名前缀处理类
*/
class UploadDomain
{
use Error;
/**
* 检测上传的文件
* @param $fileName
* @param $fileSize
* @param $fileExt
* @param $fileMime
* @return bool
*/
public function check($fileName, $fileSize, $fileExt, $fileMime)
{
$allowSize = request()->param('size', ConfServiceFacade::get('system.upload.size'));
if (!$this->checkSize($fileSize, $allowSize)) {
return false;
}
$allowExt = request()->param('mime', ConfServiceFacade::get('system.upload.mime'));
if (!$this->checkExt($fileName, $fileExt, $allowExt)) {
return false;
}
if (!$this->checkMime($fileMime)) {
return false;
}
return true;
}
/**
* 检测上传文件大小
* @param $fileSize
* @param string $allowSize
* @return bool
*/
public function checkSize($fileSize, $allowSize = '')
{
$allowSize = str_replace(' ', '', $allowSize);
$allowUploadSizeConf = ConfServiceFacade::get('system.upload.size');
// 没有配置,也没有传入上传文件大小限制参数,即程序上不限制上传文件大小
if (!$allowUploadSizeConf && !$allowSize) {
return true;
}
$maxSize = $allowSize ? $allowSize : $allowUploadSizeConf;
preg_match('/(\d+)(\w+)/', $maxSize, $matches);
$type = strtolower($matches[2]);
$typeDict = ['b' => 0, 'k' => 1, 'kb' => 1, 'm' => 2, 'mb' => 2, 'gb' => 3, 'g' => 3];
$maxSize = (int)$maxSize * pow(1024, isset($typeDict[$type]) ? $typeDict[$type] : 0);
if ($fileSize > $maxSize) {
$this->setError('上传失败,文件大小超过 ' . $allowSize);
return false;
}
return true;
}
/**
* 检测上传文件后缀
* @param $fileName
* @param $fileExt
* @param string $allowExt
* @return bool
*/
public function checkExt($fileName, $fileExt, $allowExt = '')
{
$allowExtArr = explode(',', strtolower($allowExt));
//禁止上传PHP和HTML文件
if (in_array($fileExt, ['php', 'html', 'htm'])) {
$this->setError('上传失败,禁止上传php和html文件');
return false;
}
//验证文件后缀
$allowExtConf = ConfServiceFacade::get('system.upload.mime');
// 没有配置,也没有传入上传文件大小限制参数,即程序上不限制上传文件大小
if (!$allowExtConf && !$allowExt) {
$this->setError('上传失败,允许上传的文件后缀为空,请到系统配置 - 上传配置进行设置');
return false;
}
if ($allowExt !== '*' && (!in_array($fileExt, $allowExtArr))) {
$this->setError('上传失败,允许上传的文件后缀为' . $allowExt . ',实际上传文件[ ' . $fileName . ' ]的后缀为' . $fileExt);
return false;
}
return true;
}
/**
* 检测上传文件类型
* @param $fileMime
* @return bool
*/
public function checkMime($fileMime)
{
//禁止上传PHP和HTML文件
if (in_array($fileMime, ['text/x-php', 'text/html'])) {
$this->setError('上传失败,禁止上传php和html文件');
return false;
}
return true;
}
/**
* 编辑器内容,添加上传文件的域名前缀,一般提供给数据模型层getFiledAttr方法调用
* @param $string
* @param $uploadType
* @return string|string[]|null
*/
static public function addUploadDomain($string, $uploadType = 'local')
{
$defaultType = ConfServiceFacade::get('system.upload.defaultType', 'local');
if($uploadType == 'default') $uploadType = $defaultType;
$uploadDomain = self::getUploadDomain($uploadType, 'via');
//ueditor编辑器正则替换所有的图片、视频、音频
/* $string = preg_replace("/(<img .*?src=\")^(?!http)(.*?)(\".*?>+)/is", "\${1}{$uploadDomain}\${2}\${3}", $string);*/
preg_match_all("/(<img .*?src=\")(.*?)(\".*?>+)/is", $string, $matches);
if ($matches && isset($matches['2'])) {
foreach ($matches['2'] as $item) {
if (substr($item, 0, 4) != 'http') {
$string = str_replace($item, $uploadDomain . $item, $string);
}
}
}
$string = preg_replace("/(<video .*?src=\")(.*?)(\".*?>+)/is", "\${1}{$uploadDomain}\${2}\${3}", $string);
$string = preg_replace("/(<source .*?src=\")(.*?)(\".*?>+)/is", "\${1}{$uploadDomain}\${2}\${3}", $string);
//meditor编辑器正则替换所有的图片
$string = preg_replace("/(\!\[\]\()(.*?)(\))/is", "\${1}{$uploadDomain}\${2}\${3}", $string);
return $string;
}
/**
* 编辑器内容,删除上传文件的域名前缀,一般提供给控制器添加和编辑方法调用
* @param $string
* @param $uploadType
* @return string|string[]|null
*/
static public function delUploadDomain($string, $uploadType = 'local')
{
$defaultType = ConfServiceFacade::get('system.upload.defaultType', 'local');
if($uploadType == 'default') $uploadType = $defaultType;
$uploadDomain = addcslashes(self::getUploadDomain($uploadType, 'via'), '/');
//ueditor编辑器正则替换所有的图片、视频、音频
$string = preg_replace("/(<img .*?src=\"){$uploadDomain}(.*?)(\".*?>+)/is", "\${1}\${2}\${3}", $string);
$string = preg_replace("/(<video .*?src=\"){$uploadDomain}(.*?)(\".*?>+)/is", "\${1}\${2}\${3}", $string);
$string = preg_replace("/(<source .*?src=\"){$uploadDomain}(.*?)(\".*?>+)/is", "\${1}\${2}\${3}", $string);
//meditor编辑器正则替换所有的图片
$string = preg_replace("/(\!\[\]\(){$uploadDomain}(.*?)(\))/is", "\${1}\${2}\${3}", $string);
return $string;
}
/**
* 获取文件上传后访问的域名
* @param string $uploadType 上传方式
* @param string $viaServer 上传是否经过服务器
* @return mixed|string
*/
static public function getUploadDomain($uploadType = 'local', $viaServer = 'via')
{
$uploadDomain = '';
if ($uploadType === 'local') {
$uploadDomain = Env::get('domain.static', request()->domain() . '/static');
} else if ($uploadType === 'qiniu-kodo') {
$uploadDomain = ConfServiceFacade::get('plugin.qiniu_kodo.domain');
} else if ($uploadType === 'ali-oss') {
if ($viaServer === 'via') {
$uploadDomain = ConfServiceFacade::get('plugin.ali_oss.domain');
} else {
$uploadDomain = ConfServiceFacade::get('plugin.ali_oss_sts.domain');
}
}
return $uploadDomain;
}
// 设置上传方式为本地时的path,提供给当前类的singleAddUploadDomain方法调用
static public function setLocalPath($data)
{
$uploadDomain = self::getUploadDomain($data['upload_type']);
$uploadType = $data['upload_type'];
$value = $data['path'];
if($uploadType === 'local'){
if($uploadDomain){
$value = '/storage/' . $data['path'];
}else{
$value = '/static' . $value;
}
}
return $value;
}
/**
* 上传控件,单个,添加域名前缀,提供给Files模型层getPathAttr方法调用
* @param $data
* @return string
*/
static public function singleAddUploadDomain($data)
{
$uploadDomain = self::getUploadDomain($data['upload_type'], $data['via_server']);
$value = self::setLocalPath($data);
return $uploadDomain . '/' . ltrim($value, '/');
}
/**
* 上传控件,单个,保存path时,删除掉域名前缀,此方法仅FileController的add方法调用
* @param $data
* @return mixed
*/
static public function singleDelUploadDomain($data)
{
$uploadDomain = self::getUploadDomain($data['upload_type'], $data['via_server']);
return str_replace($uploadDomain, '', $data['path']);
}
/**
* 上传控件,多个,整理成前端需要的数据,以[, ]组合的字符串
* @param $fileIds
* @return array|boolean
*/
static public function multiJoin($fileIds)
{
try {
$files = Files::where('id', 'in', $fileIds)->select()->toArray();
$idArr = [];
$pathArr = [];
$filenameArr = [];
foreach ($files as $v) {
$idArr[] = $v['id'];
$pathArr[] = $v['path'];
$filenameArr[] = $v['name'];
}
return [
'id' => $fileIds,
'path' => join(', ', $pathArr),
'filename' => join(', ', $filenameArr)
];
} catch (\Exception $e) {
return false;
}
}
/**
* 获取默认的用户头像
* @return string
*/
public static function getDefaultAvatar()
{
$path = '/admin/images/avatar.jpg';
$uploadDomain = Env::get('domain.static', request()->domain() . '/static');
return $uploadDomain . $path;
}
}
+84
View File
@@ -0,0 +1,84 @@
<?php
namespace laytp\library\token;
use think\facade\Config;
/**
* Token基础类
*/
abstract class Driver
{
protected $handler = null;
protected $options = [];
/**
* 存储Token
* @param string $token Token
* @param int $user_id 会员ID
* @param int $expire 过期时长,0表示无限,单位秒
* @return bool
*/
abstract function set($token, $user_id, $expire = 0);
/**
* 获取Token内的信息
* @param string $token
* @return array
*/
abstract function get($token);
/**
* 判断Token是否可用
* @param string $token Token
* @param int $user_id 会员ID
* @return boolean
*/
abstract function check($token, $user_id);
/**
* 删除Token
* @param string $token
* @return boolean
*/
abstract function delete($token);
/**
* 删除指定用户的所有Token
* @param int $user_id
* @return boolean
*/
abstract function clear($user_id);
/**
* 返回句柄对象,可执行其它高级方法
*
* @access public
* @return object
*/
public function handler()
{
return $this->handler;
}
/**
* 获取加密后的Token
* @param string $token Token标识
* @return string
*/
protected function getEncryptedToken($token)
{
$tokenConfig = Config::get('token');
return hash_hmac($tokenConfig['hashalgo'], $token, $tokenConfig['key']);
}
/**
* 获取过期剩余时长
* @param $expiretime
* @return float|int|mixed
*/
protected function getExpiredIn($expiretime)
{
return $expiretime ? max(0, $expiretime - time()) : 365 * 86400;
}
}
+116
View File
@@ -0,0 +1,116 @@
<?php
namespace laytp\library\token\driver;
use laytp\library\token\Driver;
use think\facade\Db;
/**
* Token操作类
*/
class Mysql extends Driver
{
/**
* 默认配置
* @var array
*/
protected $options = [
'table' => 'user_token',
'expire' => 2592000,
'connection' => [],
];
/**
* 构造函数
* @param array $options 参数
* @access public
*/
public function __construct($options = [])
{
if (!empty($options)) {
$this->options = array_merge($this->options, $options);
}
if ($this->options['connection']) {
$this->handler = Db::connect($this->options['connection'])->name($this->options['table']);
} else {
$this->handler = Db::name($this->options['table']);
}
}
/**
* 存储Token
* @param string $token Token
* @param int $user_id 会员ID
* @param int $expire 过期时长,0表示无限,单位秒
* @return bool
*/
public function set($token, $user_id, $expire = null)
{
$expire_time = !is_null($expire) && $expire !== 0 ? time() + $expire : 0;
$token = $this->getEncryptedToken($token);
$this->handler->insert(['token' => $token, 'user_id' => $user_id, 'create_time' => time(), 'expire_time' => $expire_time]);
return TRUE;
}
/**
* 获取Token内的信息
* @param string $token
* @return array|null|\think\Model
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function get($token)
{
$data = $this->handler->where('token', $this->getEncryptedToken($token))->find();
if ($data) {
if (!$data['expire_time'] || $data['expire_time'] > time()) {
//返回未加密的token给客户端使用
$data['token'] = $token;
//返回剩余有效时间
$data['expires_in'] = $this->getExpiredIn($data['expire_time']);
return $data;
} else {
self::delete($token);
}
}
return [];
}
/**
* 判断Token是否可用
* @param string $token Token
* @param int $user_id 会员ID
* @return boolean
*/
public function check($token, $user_id)
{
$data = $this->get($token);
return $data && $data['user_id'] == $user_id ? true : false;
}
/**
* 删除Token
* @param string $token
* @return boolean
*/
public function delete($token)
{
$this->handler->where('token', $this->getEncryptedToken($token))->delete();
return true;
}
/**
* 删除指定用户的所有Token
* @param int $user_id
* @return boolean
*/
public function clear($user_id)
{
$this->handler->where('user_id', $user_id)->delete();
return true;
}
}
+168
View File
@@ -0,0 +1,168 @@
<?php
namespace laytp\library\token\driver;
use laytp\library\token\Driver;
use think\facade\Config;
/**
* Token操作类
*/
class Redis extends Driver
{
protected $options = [
'host' => '127.0.0.1',
'port' => 6379,
'password' => '',
'select' => 0,
'timeout' => 0,
'expire' => 0,
'persistent' => false,
'userprefix' => 'up:',
'tokenprefix' => 'tp:',
];
/**
* 构造函数
* @param array $options 缓存参数
* @throws \BadFunctionCallException
* @access public
*/
public function __construct($options = [])
{
if (!extension_loaded('redis')) {
throw new \BadFunctionCallException('not support: redis');
}
if (!empty($options)) {
$this->options = array_merge($this->options, $options);
}
$this->handler = new \Redis;
if ($this->options['persistent']) {
$this->handler->pconnect($this->options['host'], $this->options['port'], $this->options['timeout'], 'persistent_id_' . $this->options['select']);
} else {
$this->handler->connect($this->options['host'], $this->options['port'], $this->options['timeout']);
}
if ('' != $this->options['password']) {
$this->handler->auth($this->options['password']);
}
if (0 != $this->options['select']) {
$this->handler->select($this->options['select']);
}
}
/**
* 获取加密后的Token
* @param string $token Token标识
* @return string
*/
protected function getEncryptedToken($token)
{
$config = Config::get('token');
return $this->options['tokenprefix'] . hash_hmac($config['hashalgo'], $token, $config['key']);
}
/**
* 获取会员的key
* @param $user_id
* @return string
*/
protected function getUserKey($user_id)
{
return $this->options['userprefix'] . $user_id;
}
/**
* 存储Token
* @param string $token Token
* @param int $user_id 会员ID
* @param int $expire 过期时长,0表示无限,单位秒
* @return bool
*/
public function set($token, $user_id, $expire = 0)
{
if (is_null($expire)) {
$expire = $this->options['expire'];
}
if ($expire instanceof \DateTime) {
$expire = $expire->getTimestamp() - time();
}
$key = $this->getEncryptedToken($token);
if ($expire) {
$result = $this->handler->setex($key, $expire, $user_id);
} else {
$result = $this->handler->set($key, $user_id);
}
//写入会员关联的token
$this->handler->sAdd($this->getUserKey($user_id), $key);
return $result;
}
/**
* 获取Token内的信息
* @param string $token
* @return array
*/
public function get($token)
{
$key = $this->getEncryptedToken($token);
$value = $this->handler->get($key);
if (is_null($value) || false === $value) {
return [];
}
//获取有效期
$expire = $this->handler->ttl($key);
$expire = $expire < 0 ? 365 * 86400 : $expire;
$expire_time = time() + $expire;
//解决使用redis方式储存token时api接口Token刷新与检测因expires_in拼写错误报错的BUG
$result = ['token' => $token, 'user_id' => $value, 'expire_time' => $expire_time, 'expires_in' => $expire];
return $result;
}
/**
* 判断Token是否可用
* @param string $token Token
* @param int $user_id 会员ID
* @return boolean
*/
public function check($token, $user_id)
{
$data = self::get($token);
return $data && $data['user_id'] == $user_id ? true : false;
}
/**
* 删除Token
* @param string $token
* @return boolean
*/
public function delete($token)
{
$data = $this->get($token);
if ($data) {
$key = $this->getEncryptedToken($token);
$user_id = $data['user_id'];
$this->handler->del($key);
$this->handler->sRem($this->getUserKey($user_id), $key);
}
return true;
}
/**
* 删除指定用户的所有Token
* @param int $user_id
* @return boolean
*/
public function clear($user_id)
{
$keys = $this->handler->sMembers($this->getUserKey($user_id));
$this->handler->del($this->getUserKey($user_id));
$this->handler->del($keys);
return true;
}
}
+138
View File
@@ -0,0 +1,138 @@
<?php
namespace laytp\traits;
use laytp\library\CommonFun;
use think\facade\Config;
use think\facade\Db;
trait Backend
{
/**
* 列表
* @return mixed
*/
public function index()
{
$where = $this->buildSearchParams();
$order = $this->buildOrder();
$data = $this->model->where($where)->order($order);
$paging = $this->request->param('paging', false);
if ($paging) {
$limit = $this->request->param('limit', Config::get('paginate.limit'));
$data = $data->paginate($limit)->toArray();
$data['data'] = $this->getSelectedData($data['data']);
} else {
$data = $data->select()->toArray();
}
return $this->success('数据获取成功', $data);
}
//查看详情
public function info()
{
$id = $this->request->param('id');
$info = $this->model->find($id);
return $this->success('获取成功', $info);
}
//添加
public function add()
{
$post = CommonFun::filterPostData($this->request->post());
try {
if ($this->model->create($post)) {
return $this->success('添加成功', $post);
} else {
return $this->error('操作失败');
}
} catch (\Exception $e) {
return $this->exceptionError($e);
}
}
//编辑
public function edit()
{
$id = $this->request->param('id');
$info = $this->model->find($id);
if (!$info) {
return $this->error('ID参数错误');
}
$post = CommonFun::filterPostData($this->request->post());
foreach ($post as $k => $v) {
$info->$k = $v;
}
try {
$updateRes = $info->save();
if ($updateRes) {
return $this->success('编辑成功');
} else {
return $this->error('编辑失败');
}
} catch (\Exception $e) {
return $this->exceptionError($e);
}
}
//删除
public function del()
{
$ids = array_filter($this->request->post('ids'));
if (!$ids) {
return $this->error('参数ids不能为空');
}
try {
if ($this->model->destroy($ids)) {
return $this->success('数据删除成功');
} else {
return $this->error('数据删除失败');
}
} catch (\Exception $e) {
return $this->exceptionError($e);
}
}
//回收站
public function recycle()
{
$where = $this->buildSearchParams();
$order = $this->buildOrder();
$data = $this->model->onlyTrashed()->where($where)->order($order);
$limit = $this->request->param('limit', Config::get('paginate.limit'));
$data = $data->paginate($limit)->toArray();
$data['data'] = $this->getSelectedData($data['data']);
return $this->success('回收站数据获取成功', $data);
}
//还原
public function restore()
{
$where[] = ['id', 'in', $this->request->post('ids')];
if ($this->model->restore($where)) {
return $this->success('数据成功还原');
} else {
return $this->error('操作失败');
}
}
//真实删除
public function trueDel()
{
Db::startTrans();
try {
$ids = $this->request->post('ids');
$res = $this->model->onlyTrashed()->where('id', 'in', $ids)->select();
foreach ($res as $key => $item) {
$delRes = $item->force()->delete();
if (!$delRes) throw new \Exception('删除失败');
}
Db::commit();
return $this->success('数据已经彻底删除');
} catch (\Exception $e) {
Db::rollback();
return $this->error('数据库异常,操作失败');
}
}
}
+22
View File
@@ -0,0 +1,22 @@
<?php
/**
* 设置错误和获取错误公用方法定义,任何类都可以使用这两个方法
*/
namespace laytp\traits;
trait Error
{
protected $_error = null;
public function getError()
{
return $this->_error;
}
public function setError($error)
{
$this->_error = $error;
return false;
}
}
+111
View File
@@ -0,0 +1,111 @@
<?php
declare (strict_types=1);
namespace laytp\traits;
use app\model\api\Log;
/**
* 所有接口success和error返回数据格式定义
*/
trait JsonReturn
{
/**
* 操作成功返回数据
* @param string $msg
* @param null $data
* @return false|string|\think\response\Json
*/
public function success($msg = '', $data = null)
{
$result = [
'code' => 0,
'msg' => $msg,
'time' => time(),
'data' => $data ?? new \stdClass(),
];
//response返回的Content-Type:text/html; charset=utf-8,使用浏览器单独访问接口地址方便调试,
//但是注意在javascript进行ajax请求时,要设置dataType: "json",这样,javascript得到字符串后,会自动使用JSON.parse对字符串进行解析
// return json_encode($result,JSON_UNESCAPED_UNICODE);
//使用json方法进行返回的好处:
// 1.客户端在进行ajax请求时,不需要设置dataType: "json",依旧会认为得到的数据是json
// 2.APP_DEBUG = true,客户端得到的也仅仅是json部分数据,不会夹杂trace等debug代码
// return json($result);
//记录Api请求日志
if (app('http')->getName() === 'api') {
Log::create([
'rule' => request()->url(),
'request_body' => json_encode(request()->post(), JSON_UNESCAPED_UNICODE),
'request_header' => json_encode(request()->header(), JSON_UNESCAPED_UNICODE),
'ip' => request()->ip(),
'status_code' => 200,
'response_body' => json_encode($result, JSON_UNESCAPED_UNICODE),
'create_time' => date('Y-m-d H:i:s'),
]);
}
//最终做如下处理:在测试环境且传入的debug=1的参数,就返回html,否则返回json数据
//在测试环境可以单独用浏览器打开接口地址,传入debug=1的参数对接口进行调试
if (env('APP_DEBUG') && request()->param('debug')) {
return json_encode($result, JSON_UNESCAPED_UNICODE);
} else {
return json($result);
}
}
/**
* 操作失败返回数据
* @param string $msg
* @param int $code 错误码,为0表示没有错误,为1表示常规错误,前端仅需提示msg,其他值有具体含义,比如10401为未登录,前端需要跳转至登录界面
* @param array $data
* @return \think\response\Json
*/
public function error($msg = '', $code = 1, $data = null)
{
$result = [
'code' => $code,
'msg' => $msg,
'time' => time(),
'data' => $data ?? new \stdClass(),
];
//记录Api请求日志
if (app('http')->getName() === 'api') {
Log::create([
'rule' => request()->url(),
'request_body' => json_encode(request()->post(), JSON_UNESCAPED_UNICODE),
'request_header' => json_encode(request()->header(), JSON_UNESCAPED_UNICODE),
'ip' => request()->ip(),
'status_code' => 200,
'response_body' => json_encode($result, JSON_UNESCAPED_UNICODE),
'create_time' => date('Y-m-d H:i:s'),
]);
}
if (env('APP_DEBUG') && request()->param('debug')) {
return json_encode($result, JSON_UNESCAPED_UNICODE);
} else {
return json($result);
}
}
/**
* 捕获异常后返回数据
* @param $e \Exception
* @return \think\response\Json
*/
public function exceptionError($e){
$data = [];
if(env('APP_DEBUG')){
$data = [
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'trace' => $e->getTrace()
];
}
return $this->error($e->getMessage(), 1, $data);
}
}